Skip to content

fix the seven blockers from the UX review - #40

Merged
3 commits merged into
mainfrom
fix/ux-review-blockers
Aug 30, 2026
Merged

fix the seven blockers from the UX review#40
3 commits merged into
mainfrom
fix/ux-review-blockers

Conversation

@OrellBuehler

@OrellBuehler OrellBuehler commented Aug 30, 2026

Copy link
Copy Markdown
Owner

Each of these was the UI saying or doing something that wasn't true.

# Blocker Fix
1 Inspector only mounted with a clip selected, so titles and captions were unreachable Always mounted (it already had an empty state), toggled from the toolbar via ui.inspectorOpen
2 Transition badge read crossfade ? 'Crossfade' : 'Dip to black' — 9 of 11 kinds mislabelled Use transitionLabel(), which already existed unused
3 "Drop media to start" — nothing listened for Tauri's drag-drop event onDragDropEventisMediaPath filter (same extensions the picker uses) → editor.importPaths, plus a drop overlay
4 Export dialog defaulted to Web 1080p, so a 9:16 project opened its export already landscape and the readiness panel warned about the frame just chosen initialExport() opens on the project frame — the preset that renders it when one matches, else the default preset with its resolution cleared
5 Agent panel showed a green "live" dot regardless agent_status reports when an agent last spoke to the endpoint; the panel judges from the age
6 Import ran a full analysis per asset with no cancel Analysis is cancellable end to end; the status bar names the step, says what is queued, and stops it
7 editor.error / editor.loading set and never rendered Dismissible banner under the toolbar; loading in the status bar

Notes for review

Agent presence (#5). A streamable-HTTP client holds no connection between calls, so there is no socket to report as open. mcp::LAST_AGENT_ACTIVITY is stamped in lock_agent — the one choke point every agent-side project access goes through — and in get_info, since initialize is the moment an agent is known to be there. The panel polls agent_status every 5s and shows connected / away / nothing yet.

Cancellation (#6) is real but not instantaneous. CancelFn sits beside the ProgressFn:

  • checked between the four ffmpeg passes, which are single whole-file runs with no hook to poll inside, so a stop lands at the next step boundary;
  • polled about once a second inside the ffmpeg whisper run (off -stats_period 1), which is the step that actually runs for minutes — the child is killed;
  • polled per chunk during the model download, keeping the .part file so a retry resumes rather than re-fetching 148 MB.

A cancelled pass caches nothing: a half-analyzed asset would read as analyzed and its missing transcript as "no speech". The Stop tooltip says exactly what it does.

API change. Transcriber::transcribe is now a 3-arg trait method. analyze_with_progress / analyze_asset_media_with_progress keep their signatures and delegate with a no-op cancel, so no existing caller moved.

Checks

cargo test --workspace --no-default-features 297 pass · cargo clippy clean · cargo fmt --check clean · cargo check builds with the ffmpeg default features and with --features whisper · bun run check 0 errors · bun test 66 pass.

The two MCP commits underneath (53ad658, d5389bb) were already unpushed on main and come along with the branch.

🤖 Generated with Claude Code

https://claude.ai/code/session_01X1y7fErQC6TsYqCw8mXnFv

Fixes, from a read of the whole tool surface:

- snap_to_beats returned `timeline()` where every other read uses
  `working_timeline()`, so inside a task it handed back the user's
  untouched cut next to a `cuts_aligned` count of moves that were not
  in it.
- timeline_summary promised "any per-track gaps" and returned none.
  It now reports them, head gap included — a hole is black picture,
  which an agent never sees for itself.
- download_speech_model filled the cache but never selected the model,
  so naming one and then analyzing still transcribed with the old one.
  Add set_speech_model, the write side of transcription_status.
- core_err mapped everything to internal_error; a stale id or an
  out-of-range value is the caller's mistake, and invalid_params is
  what tells a model to fix its arguments instead of giving up.
- Clamp the sizes a model picks out of a schema description
  (waveform buckets, frame widths), the way skim_asset already does.

Optimizations:

- Build the tool router once. #[tool_handler]'s default router
  expression is re-evaluated by call_tool, list_tools and get_tool, so
  every request rebuilt all 85 routes — ~250us of release-build work
  per tool call for an identical result.
- platform_check resolved the cut summary twice (two timeline
  deserializes, two asset queries) for one answer.
- Release the project lock before emitting `project-changed`. The event
  makes the GUI re-fetch and that re-fetch takes the same lock, so the
  notify belonged outside. An `edit()` helper does the whole shape once
  and takes the lock/mutate/notify boilerplate out of 61 tools.

- export now reports progress and can be cancelled. A render runs for
  minutes; an agent could neither tell a slow export from a hung one
  nor abandon settings it already knew were wrong. Both ride the
  protocol — the client's `progressToken` and the cancellation token
  rmcp trips on `notifications/cancelled` — and a cancelled render
  deletes its half-written file from inside the blocking job, since
  cancelling the request can drop the handler future.

- Add import_asset, so an agent can load media rather than only
  rearranging what it was handed. It deliberately does not stage: a
  file on disk is not an edit to the user's cut.

- set_mask no longer swallows a failed timeline read. `.ok()` made a
  read that failed indistinguishable from "this clip has no mask yet",
  and silently reset every field the caller did not name.

- set_speech_model emits `speech-model-changed`, which the webview
  listens for. The GUI reads transcription status only at launch, so a
  model an agent picked stayed invisible in the picker until restart.
Each one was the UI saying or doing something that wasn't true.

- The Inspector was gated on a clip being selected, so its Text overlays
  section — which belongs to the timeline, not to any clip — was
  unreachable until you clicked one. It is now always mounted (it already
  had an empty state) and toggled from the toolbar, so closing it is also
  how you get the width back.
- The timeline's transition badge read `crossfade ? 'Crossfade' : 'Dip to
  black'`, mislabelling nine of eleven kinds. `transitionLabel()` already
  existed, unused.
- "Drop media to start" now does: `+page.svelte` listens for Tauri's
  drag-drop event, filters by the same extension list the picker uses and
  runs the same import.
- The export dialog opened on Web 1080p, so a 9:16 project opened its
  export already landscape and the readiness panel warned about the frame
  the user had just chosen. It now opens on the project frame.
- The agent panel showed a green "live" dot whether or not anything was
  connected. A streamable-HTTP client holds no connection between calls,
  so `agent_status` reports when an agent last spoke to the endpoint
  (stamped in `lock_agent` and in `get_info`) and the panel judges from
  the age: connected / away / nothing yet.
- Import ran a full analysis per asset with no way out — a model download
  and then minutes of inference each. Analysis is now cancellable end to
  end: `CancelFn` beside the `ProgressFn`, checked between steps, polled
  about once a second inside the ffmpeg whisper run, and per chunk during
  the model download (keeping the `.part` file so a retry resumes). The
  status bar names the step, says what is queued behind it, and stops it.
- `editor.error` was recorded and never rendered, so a `.kerf` that would
  not open opened as silence. It is a dismissible banner now, and
  `editor.loading` shows in the status bar.
@OrellBuehler OrellBuehler changed the title Fix/ux review blockers fix the seven blockers from the UX review Aug 30, 2026
@OrellBuehler OrellBuehler closed this pull request by merging all changes into main in bec8523 Aug 30, 2026
@OrellBuehler
OrellBuehler deleted the fix/ux-review-blockers branch August 30, 2026 20:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant