Five backend branches merged and the TypeScript still compiled, because
none of this is a type error: a field that arrives `undefined`, a variant
nothing emits any more, a prompt whose loop never closes. Six things.
**Orphaned volumes are destructive now, not safe.** `ReclaimTarget::
OrphanVolume` is gone from Rust; the object is a `DestructiveTarget::
OrphanVolume { name, project_id }` confirmed against the *volume's* name,
there being no project to name. The TS union still listed it under
`ReclaimTarget`, and — worse — `DiskProjectTable` keys destructive items
off `project_id`, which an orphan's never matches. So the item existed in
the plan and appeared nowhere on screen. `DiskSettings` now splits the
plan's destructive list and gives orphans their own section with a
per-volume `TypedConfirmModal`. The copy says what a
`triple-c-claude-config-*` volume actually is — a Claude login
credential, every plugin and skill, every transcript that project had —
and keeps the sentence explaining that "no matching project" is a lookup
against the project list and is never inferred from a project being
stopped or having no image, which is the inference that once flagged two
live projects.
`TypedConfirmModal` grew a `subject` prop: asking a user for "the exact
project name" of a volume that has no project is asking for a string that
does not exist.
**Snapshot and Total reconcile.** `ProjectDiskRow.snapshot_attributed_bytes`
is the single figure `snapshot_attribution()` exists to produce. The
column rendered `snapshot_above_base_bytes` and fell back to `—` while
the Total was `size - shared` regardless — and in that branch `size -
shared` is the whole 4.7 GB base image, charged per project and then
added again as a base-image row. One field, one rule. The one branch
where the figure *is* the whole image says so rather than passing itself
off as a share.
**The overwrite loop closes.** Traced end to end: a `FILE_EXISTS:`
refusal raises the prompt, Replace re-invokes with `overwrite: true`,
Skip advances, "…all" answers the rest without asking, and picker and
host-drop both reach `uploadFileToContainer` through `uploadPaths`. Two
gaps: a second batch's `askOverwrite` overwrote the first's resolver,
leaving that batch awaiting an answer no dialog could produce; and the
backend's written refusals — a hidden host folder, a path outside the
write roots — were passed as a toast `detail`, which `ToastHost` renders
as collapsed monospace behind a "Details" button, so the only sentence
that explained anything was the part nobody saw. `readableRefusal`
promotes it to the headline when a batch failed the same way.
**The browser pane's sandbox is pinned.** `allow-same-origin` must stay
(the proxy's gate reads `Origin`/`Referer`, and an opaque origin sends
`null`); every top-navigation grant and `allow-popups-to-escape-sandbox`
must stay absent, and the test names the offending token rather than
printing a set diff.
**`@tauri-apps/plugin-store` is gone** from `package.json` — its
capability grants were removed as a host-file-write primitive and nothing
in `app/src` imports it. The lockfile was updated with
`--package-lock-only`, deliberately: `node_modules` is a symlink shared
with other worktrees and a real install would have pulled it out from
under them.
Nothing under `src-tauri/` is touched. 663 frontend tests pass (was 635),
`tsc --noEmit` clean, `npm run build` green, `cargo test` 446 unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBq2rGum6GX7xXgsas1fDc
The Files tab could accept a drop but never produce one: getting a file
out meant "Save to host…" and a file picker. This adds the other
direction.
Two constraints shape it. `dragDropEnabled` is on — TerminalView needs
it, since the native drag-drop event is the only one carrying dropped
file paths — and it blocks HTML5 drag inside the webview, so `draggable`
plus `DataTransfer.setData("DownloadURL", …)` was never available. The
gesture is therefore pointer events into `tauri-plugin-drag`, the same
shape and the same reason as the tab strip's drag. And the file being
dragged does not exist on the host at all: it lives in a container, and
the OS can only drag a real host path.
So a drag-out is a copy first and a drag second.
`stage_container_file_for_drag` materialises the file into
`<os-temp>/triple-c-drag-out/<session>/<slot>/<name>` through the same
`fetch_container_file` the download and the viewer use, keeps the
original filename (a dropped `tmp1234` is not a file anyone wants), and
caps at the 256 MiB an upload already caps at, naming "Save to host…" in
the refusal. The path comes from Tauri's path API rather than `/tmp`,
because on Windows it is neither.
The staging directory has a lifecycle, because whole files accumulating
in the host temp dir would be the disk problem this project just fixed,
in a new place: cleared on exit inside the existing teardown (still
guarded on the main window), and reaped at startup for whatever a crash
left behind.
The copy is also an async gap in the middle of a gesture that feels
instantaneous, and the OS only adopts a drag while the button is still
down. Small files beat the pointer; large ones do not — so the staged
path is cached per entry (keyed on size and mtime, so an edited file
re-stages) and the pane says the copy is ready and to drag again, which
is an instruction rather than an apology because the retry is immediate.
A per-file slot keeps `a/notes.txt` and `b/notes.txt` from becoming the
same host path.
"Save to host…" stays exactly as it was. Drag-out is the enhancement;
a platform that refuses `startDrag` says so and points back at it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBq2rGum6GX7xXgsas1fDc
Two version problems, one of them mine.
**Previews claimed x.y.0.** The preview workflow hard-coded the patch
number, so every preview installer reported 0.3.0 whatever it contained,
while the real build computes the patch from tags. It now runs the same
computation, so a preview is labelled with the version the release it
previews would carry.
**A new minor line started at the wrong number.** `compute-version`'s
fallback for "no tag matches this line yet" counted every commit in the
repository — fine as a bootstrap, wrong the moment a minor version is
bumped: the first 0.4 build would have been 0.4.234. A line nobody has
tagged is a new line, and a new line starts at .0.
With those fixed, VERSION moves to 0.4 — tab reordering, the browser
pop-out, opening pages in the container's browser and the Playwright
install fix are more than a patch bump. The next release is v0.4.0;
today's HEAD would have been 0.3.90 on the old line.
`app/package.json`, `package-lock.json`, `tauri.conf.json` and
`Cargo.toml` follow to 0.4.0. CI patches all four per build, so they are
the dev-time defaults rather than the source of truth — but a local
`tauri dev` shows them, so they should not still say 0.3.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mac/Windows release builds failed the Tauri version-mismatch check:
tauri (2.11.0) vs @tauri-apps/api (2.10.1). The Linux fix only updated
the Rust lockfile; the npm lockfile was still at 2.10.x. Both lockfiles
now resolve to 2.11.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aligns the npm lockfile with the Cargo crate version to fix the Tauri
build version mismatch check.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The sidebar config panel content was overflowing its container width,
causing project names and directory paths to be clipped. Added min-w-0
and overflow-hidden to flex containers, and restructured folder path
config rows to stack vertically instead of cramming into one line.
The Windows taskbar was showing a black square because no default window
icon was set at runtime. Added default_window_icon() call in the Tauri
builder using the app's icon.png.
Also adds vitest test infrastructure with tests verifying both fixes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tauri v2 desktop app (React/TypeScript + Rust) for managing
containerized Claude Code environments. Includes Gitea Actions
workflow for building and pushing the sandbox container image,
and a BUILDING.md guide for manual app builds on Linux and Windows.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>