Bunch of additions to the Workbench, some Gateway features - #3
Merged
Conversation
Also centralize the profile config path in discover.rs and fix the template-load test to mirror the empty-falls-back rule.
Also de-flake the listing test's sort order and add tests for racing-publisher dedupe and corrupt-sidecar tolerance.
Also watch tsconfig.json and package-lock.json in build.rs and drive a scripted chat round-trip in the UI smoke test.
Also test that a client disconnect mid-stream is taped with the partial content.
Also catch the socket open rejection deliberately and rename a test to match what it asserts.
Also move the recording-mic foreground onto an --on-danger variable.
Also test that a declined catalog refresh on reconnect pushes nothing.
Also log startup voice degradation verdicts and document the download stream's no-stall-timeout posture.
Voice updates set the textarea value programmatically, which fires no input event, so murm-ui's resizer never ran. The local resizeInput fallback pinned an explicit inline height that disabled the CSS field-sizing the app relies on, freezing the composer at the last pinned height for anything outside the voice path. Dispatch input after every voice-driven write so murm-ui's listener owns growth (and submit state), raise the composer cap to 40vh via the murm-ui bridge block, and cover the growth with a jsdom smoke assertion.
vinniefalco
temporarily deployed
to
github-pages
August 25, 2026 14:30 — with
GitHub Actions
Inactive
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.
The workbench now generates its own configuration on first launch, writing a default workbench.toml into the user's profile directory with interpolated gateway credentials and sensible defaults. The env-only configuration path was removed in favor of a single TOML-based flow where unset environment variables resolve to empty strings, an empty gateway URL falls back to the loopback address, and an empty API key simply omits the Authorization header. The app never exits on a missing config file - it creates one and keeps going.
The gateway gained a cache API for managing large model files. Three bearer-authenticated routes (POST, GET, DELETE on /v1/cache) let the workbench request model downloads through the gateway, which streams them with SSE progress events, verifies SHA-256 digests, and stages files through .part temporaries with sidecar metadata. The download machinery reuses the gateway's existing reqwest-based downloader with its HF token scoping and size caps, running inside spawn_blocking to keep the async executor clear. Concurrent requests for the same source are deduplicated under a lock, and the listing endpoint reads sidecars instead of re-hashing multi-gigabyte blobs.
The entire UI was rebuilt from vanilla JavaScript into a TypeScript pipeline bundled by esbuild and served through rust-embed, with murm-ui providing the chat renderer and dockview providing the panel infrastructure. The chat transport moved from server-sent events to a persistent, id-multiplexed WebSocket that carries both chat frames and observer status updates. An observer broadcast channel instruments every subsystem - startup phases, gateway traffic, voice capture, model downloads - and the status bar renders them in real time with a progress bar for downloads and an activity LED that glows amber while a model turn is processing and green when output tokens arrive. The status bar spans the full window width, uses CSS custom properties for complete skinning, and includes an always-visible REC badge that lights up red during voice recording. All interactive controls use a consistent glow-outline hover, and the gateway heartbeat automatically reconnects the socket with exponential backoff, resetting the status bar to "Reconnecting..." until the server pushes fresh state.
Voice transcription was redesigned around a progressive committed-plus-tentative pipeline. The fast interim model transcribes only the uncommitted audio past the last segment boundary, while the large final model crystallizes completed segments in the background and reports each one back to the session on a per-take callback channel. The client displays the concatenation of committed (append-only, never rewrites) and tentative (the volatile trailing decode) text, and on stop the final frame assembles committed plus only the tail's transcription - so stop latency scales with the tail, not the full take. A configurable vocabulary list biases both models via a glossary-formatted initial prompt, respecting whisper's 224-token cap. A display latch prevents the brief empty-text gap between segment closure and crystallization by holding the last non-empty frame until committed catches up.
Recording behaves like OS dictation: pressing the mic captures the cursor position, sets the textarea read-only, and splices transcribed text at the insertion point (replacing any selection). Multiple takes compose naturally since each captures a fresh cursor position. Pressing send mid-recording discards the take without waiting for the final pass, and the form container was moved into the flex flow so the scroll area shrinks as the composer grows instead of overlapping the chat history. The silence threshold for segment closure was raised to two seconds (matching the natural-pause crystallization feel of production dictation systems), the interim window was widened to fifteen seconds so text stays visible during continuous speech, and the interim interval was tightened to 500ms for smoother updates on GPU-accelerated hardware.