Drag a file out of the Files tab onto the host desktop
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
This commit is contained in:
@@ -79,6 +79,22 @@ docker exec stdout → tokio task → emit("terminal-output-{sessionId}") → li
|
||||
- **`components/projects/home/`** — **Project Home**, the main-area view for a project:
|
||||
Overview / Sessions / Automation / Config / Files. Per-project configuration lives here, not in
|
||||
modals — see "UI conventions" below.
|
||||
- **Files moves in both directions and neither direction uses HTML5 drag.** Dropping *into*
|
||||
the pane is Tauri's native `onDragDropEvent`, which is window-wide and therefore routed by
|
||||
a hit-test of the physical-pixel payload position against the pane's rect ÷
|
||||
`devicePixelRatio` — a hidden pane has a zero-size rect, which is what stops it and
|
||||
`TerminalView`'s listener both firing. Dragging *out* is pointer events into
|
||||
`tauri-plugin-drag`, for the same `dragDropEnabled` reason the tab strip is pointer-driven.
|
||||
- **A drag-out is a copy first and a drag second.** The OS can only drag a path that exists
|
||||
on the host, and these files are inside a container, so `stage_container_file_for_drag`
|
||||
materialises one into `<os-temp>/triple-c-drag-out/<session>/` (via the shared
|
||||
`fetch_container_file`, keeping the original filename, capped at the same 256 MiB as an
|
||||
upload) and `startDrag` is handed *that*. Two consequences worth keeping: the copy is an
|
||||
async gap inside a gesture that feels instantaneous, so the staged path is cached and the
|
||||
UI says "drag it again" when the pointer came up first; and the staging directory is
|
||||
cleared on exit **and** reaped at startup, because a drag-out quietly filling the host temp
|
||||
dir with whole files would be the disk problem this project just fixed, in a new place.
|
||||
"Save to host…" stays — `startDrag` is an enhancement and can fail per platform.
|
||||
- **`components/settings/`** — Host-level settings: Docker, AWS, Web Terminal, STT, shared auth
|
||||
- **`components/ui/`** — Shared primitives. **Use these; do not hand-roll replacements.**
|
||||
`Modal` (the only correct way to build a dialog — it supplies `role="dialog"`, `aria-modal`,
|
||||
|
||||
Reference in New Issue
Block a user