feat: drop Tauri shell, ship axum-only web app#545
Merged
Conversation
Pass 1 of desktop-app removal (see docs/plans/2026-04-23-remove-desktop-app-design.md). Reimplements the two native-only features that blocked dropping the Tauri shell: - Screenshot: html2canvas renders the DOM; new /api/save_screenshot route decodes the base64 data URL and writes a PNG to the temp dir, keeping the existing file-path contract that downstream Claude Code CLI depends on. - Clipboard image: DOM drop handler + navigator.clipboard.write replaces the Tauri drag-drop event + copy_image_file_to_clipboard. Tauri branches are kept (gated on isTauri) so the desktop target still works through Pass 1; Pass 2 will delete them. Cropping is dropped per design decision — html2canvas captures the full DOM. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pass 2 of desktop-app removal. The web frontend no longer references
Tauri at all:
- backend.ts: drop isTauri, delete all Tauri branches in command,
listen, listenAsync, openUrl — fetch/WebSocket only.
- App.svelte: captureScreenshot always uses captureScreenshotPath;
updateWindowTitle always uses document.title; no more window plugin.
- Terminal.svelte: drop Tauri drag-drop listener; the DOM drop handler
is now unconditional. isImageFile helper removed (callers gone).
- clipboard.ts: clipboardHasImage now uses navigator.clipboard.read()
instead of the Tauri clipboard plugin.
- package.json: remove @tauri-apps/{api,plugin-clipboard-manager,
plugin-opener,cli} and the tauri script; description updated.
- Test harness: vitest-setup no longer mocks isTauri; test files
updated to mock document.title and $lib/backend openUrl instead of
Tauri plugin mocks.
The src-tauri/ Rust crate is still buildable (Pass 3 deletes it).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pass 3 of desktop-app removal. The Tauri target is gone; the axum server is now the sole backend. Structural changes: - src-tauri/ → server/ (directory rename) - src-tauri/src/bin/server.rs → server/src/main.rs (promoted from optional --features server binary to the default bin) - Crate renamed: the-controller → the-controller-server - Dropped the `server` Cargo feature; axum + tower-http are now unconditional deps. - Dropped tauri, tauri-build, tauri-plugin-opener, tauri-plugin-clipboard-manager, raw-window-handle, rfd. Deleted files: - src-tauri/src/lib.rs (Tauri run() entry point) - src-tauri/src/main.rs (Tauri launcher) - src-tauri/build.rs, tauri.conf.json, capabilities/, icons/ - src-tauri/src/commands/media.rs (screenshot + clipboard moved to browser) - 59 #[tauri::command] wrappers across commands.rs, commands/kanban.rs - Tauri emitter (kept NoopEmitter + WsBroadcastEmitter) Refactored to take Arc<AppState> instead of tauri::AppHandle: - auto_worker::AutoWorkerScheduler::start - maintainer::MaintainerScheduler::start - status_socket::start_listener + handle_connection + handle_cleanup - tauri::async_runtime::spawn → tokio::spawn main.rs now starts the background schedulers on boot (cli_install, skills sync, status_socket listener, maintainer + auto_worker) — previously lived in Tauri's setup() callback. Tests: added thin test-only shims in commands.rs tests module for the 6 deleted wrappers referenced by existing tests (list_projects, create_project, load_project, scaffold_project, get_auto_worker_queue, cancel_secure_env_request, submit_secure_env_value). state_from_ref becomes an identity fn returning &AppState instead of the tauri::State transmute hack. Dev harness: - dev.sh: boots axum+vite pair instead of tauri dev - playwright.config.ts: cd server && cargo run --bin the-controller-server - e2e/eval.sh: same - CI workflow: server/ workspaces, dropped tauri system-dep installs - pre-commit hook: cd server instead of cd src-tauri - vite.config.ts: TAURI_DEV_HOST → DEV_HOST Verification: pnpm test 300/300, cargo test 310/310, cargo clippy -D warnings clean, cargo fmt clean, pnpm check 0 errors, pnpm build OK. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sweep docs/domain-knowledge.md after the Tauri removal: src-tauri/ paths become server/, lib.rs references become main.rs, the "Synchronous Commands Block the Webview" section is rewritten for axum (spawn_blocking on the tokio reactor) with the Tauri origin kept as a historical note. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Before: SKIP_VARS lines (PWD, _, SHLVL, OLDPWD) emitted by `/usr/bin/env` were never matched by the `is_valid_env_name && !SKIP_VARS` guard, so they fell through to the multi-line continuation branch and got appended to the previous entry's value. A child process launched with `PORT=3001` would end up seeing `PORT="3001\n_=/usr/bin/env\nPWD=…"` after inherit_shell_env ran, because the skipped `_=…` line was folded into the PORT value. Fix: when a line parses as a valid KEY=VAL, flush the current accumulator unconditionally. If KEY is in SKIP_VARS, don't record it (and don't start a new accumulator) — but also don't treat the line as a continuation. Added a regression test that reproduces the PORT/_=/usr/bin/env scenario. This was latent in the Tauri build too (GUI launches inherit launchd env which doesn't normally contain PORT) but became load-bearing now that the axum server reads PORT for binding. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cosmetic cleanup after the Tauri removal: - server/src/maintainer.rs: stopword list "tauri" → "server"; fixture paths and fingerprint string updated to match. - server/src/commands.rs: drop stale "Tauri command wrapper" rationale from create_session_impl and stage_session_core docstrings. - server/src/status_socket.rs: rephrase the lock-ordering note to reference axum command handlers, not Tauri commands. - server/tests/integration.rs: rename "Tauri command level" comment to point at the *_impl request-handler functions. - e2e/specs/chat-mode.spec.ts: drop the "future Tauri-native runner" comment and remove the test.fixme that was waiting on the daemon route — /api/read_daemon_token landed in commit 4a699ae. The only remaining `tauri` mention is a comment in commands.rs tests documenting why the test shims exist (they replace the deleted #[tauri::command] wrappers). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The tests module had a set of pass-through wrappers (scaffold_project, create_project, load_project, list_projects, get_auto_worker_queue, cancel_secure_env_request, submit_secure_env_value) plus a state_from_ref unsafe transmute that all existed only to preserve the old #[tauri::command] call shape. With Tauri gone, the shims add no value — they just hide which function is actually under test. Replace every shim call site with a direct call to the underlying function (scaffold_project_impl, secure_env::cancel_secure_env_request, storage.list_projects, etc.) and delete the shims + state_from_ref. The last `tauri` reference in the active codebase is gone. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Removes the Tauri desktop target. The axum HTTP server is now the sole backend and the Svelte frontend talks to it via
fetch+ WebSocket.Plan:
docs/plans/2026-04-23-remove-desktop-app-design.md.Pass 1 — browser-side native features (
324fbe9)html2canvas+ newPOST /api/save_screenshotroute writes a temp PNG and returns the path Claude Code expects.navigator.clipboard.write([new ClipboardItem(...)])replaces the Tauri clipboard plugin for the terminal drag-drop image flow.src/lib/native.ts. Tauri branches kept temporarily so the desktop target still booted during this pass.html2canvascaptures the DOM only).Pass 2 — strip
@tauri-apps/*from the frontend (df76579)src/lib/backend.tsis fetch/WebSocket only;isTaurideleted.App.sveltealways usesdocument.titlefor the window title.Terminal.sveltedrag-drop is now an unconditional DOMdrophandler.clipboard.tsusesnavigator.clipboard.read()for image-presence checks.package.json:@tauri-apps/{api,plugin-clipboard-manager,plugin-opener,cli}and thetauriscript removed.Pass 3 — delete the Rust Tauri layer (
707419d)src-tauri/→server/;src-tauri/src/bin/server.rs→server/src/main.rs.the-controller→the-controller-server.tauri,tauri-build,tauri-plugin-*,raw-window-handle,rfddropped fromCargo.toml.axum+tower-httpare now unconditional (no moreserverfeature gate).lib.rsreduced to module declarations; the old Taurirun()entry plusmedia.rsdeleted.auto_worker::AutoWorkerScheduler::start,maintainer::MaintainerScheduler::start,status_socket::start_listenernow takeArc<AppState>instead oftauri::AppHandle.tauri::async_runtime::spawn→tokio::spawn.main.rsboots the schedulers +cli_install+skills::sync_skills+status_socketlistener that the Taurisetup()callback used to run.#[tauri::command]wrappers + themedia.rsmodule deleted; the*_implfunctions stay and are whatmain.rsand tests call.dev.shboots the axum + vite pair;playwright.config.ts,e2e/eval.sh, CI workflow, and pre-commit hook all updated to point atserver/.Pass 4 — docs + cleanup (
91b0baf,dd9ab58,73c7047,1925604)docs/domain-knowledge.mdrewritten for axum (spawn_blockingstory now framed against tokio rather than the Tauri main thread; Tauri origin kept as historical note).shell_env::parse_env_outputfix:SKIP_VARSlines (PWD,_,SHLVL,OLDPWD) were being folded into the previous variable's value, soPORT=3001ended up as\"3001\\nPWD=…\"and the server failed to bind. Regression test added.\"tauri\"→\"server\"; fixture paths and fingerprint string follow.scaffold_project,create_project,load_project,list_projects,get_auto_worker_queue,cancel_secure_env_request,submit_secure_env_value) and the unsafestate_from_reftransmute helper deleted; tests now call*_implandcrate::secure_env::*directly.taurireferences removed fromcommands.rs/status_socket.rs/integration.rs/chat-mode.spec.ts.Test plan
pnpm test— 300/300 passedcd server && cargo test -- --test-threads=1— 311/311 passedcd server && cargo clippy --all-targets -- -D warnings— cleancd server && cargo fmt --check— cleanpnpm check— 0 errors (16 pre-existing a11y warnings)pnpm build— bundle builds cleanly./e2e/eval.sh \$PWD e2e/specs/smoke.spec.ts— passed./e2e/eval.sh \$PWD— 20 passed, 1 skipped, 1 pre-existing failure (merge-codex.spec.tsdoes not expand the seeded project before locating.session-label; identical spec onmain)POST /api/save_screenshotwith a 1×1 PNG data URL returns a valid PNG at<tmp>/the-controller-screenshot.png; three error cases return clean 400s.html2canvasresolves,captureScreenshotPath()round-trips through/api/save_screenshot, andnavigator.clipboard.write([new ClipboardItem({...})])works under Chromium with clipboard permissions.Notes
.dmg/.appdistribution; users run./dev.sh(or any other process manager) and openhttp://localhost:1420.taurimentions are insidedocs/plans/*.mddesign docs (historical, intentionally untouched).🤖 Generated with Claude Code