tighten the MCP server, fix the UX blockers, add a notification center and site analytics - #39
Merged
Merged
Conversation
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.
Toasts vanish in four seconds. That is fine for "Clip copied" and useless for "the speech model could not be downloaded: <reason>" — the notice you actually needed to read, and there was nowhere to read it afterwards. Every toast now also writes to a notification log (src/lib/notifications.svelte.ts). Components import `toast` from there instead of from svelte-sonner — a drop-in wrapper, so no call site changed; a toast that isn't recorded is one that can be lost, so the swap is whole-app. The title bar's bell opens the log (All / Unread / Problems, per-row read toggle, mark all read, clear) and badges the unread count, red when something unread actually failed. Errors and warnings also linger longer on screen than sonner's default. The log is deliberately not replayable: a toast's "Undo" action is dropped rather than kept, since an hour later it would undo whatever the newest revision is, not the edit the notice was about. And a log only helps if the notice is raised at all, so the failure paths that used to reject into nothing now report: a speech model download that fails, an asset the import queue could not analyze, and the media bin's two `void ui.runAnalysis(...)` calls.
Cookieless, self-hosted at a.orellbuehler.ch, so no consent banner. Gated on hugo.IsProduction — `hugo server` runs the development environment, and local page views are not traffic.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Five commits that had piled up locally on
main, pushed together.d5389bbtighten the MCP server53ad658close the gaps the MCP review left open4367c93fix the seven blockers from the UX reviewd4997a2add a notification center so a toast can be read after it is gone029eb81add umami analytics to the siteThe last one is the only change to
site/: a[params.analytics]block inhugo.tomland a deferred script tag inbaseof.html, guarded byhugo.IsProductionsohugo serverdoes not log local page views. Umami iscookieless and self-hosted, so there is no consent banner to add.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Qi7Fe5GC1JgRsok7HLVP7o