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
45 lines
1.1 KiB
JSON
45 lines
1.1 KiB
JSON
{
|
|
"name": "triple-c",
|
|
"private": true,
|
|
"version": "0.4.0",
|
|
"type": "module",
|
|
"scripts": {
|
|
"dev": "vite",
|
|
"build": "tsc && vite build",
|
|
"preview": "vite preview",
|
|
"tauri": "tauri",
|
|
"test": "vitest run",
|
|
"test:watch": "vitest"
|
|
},
|
|
"dependencies": {
|
|
"@crabnebula/tauri-plugin-drag": "^2.1.0",
|
|
"@tauri-apps/api": "^2",
|
|
"@tauri-apps/plugin-dialog": "^2.7.0",
|
|
"@tauri-apps/plugin-opener": "^2.5.3",
|
|
"@tauri-apps/plugin-store": "^2",
|
|
"@xterm/addon-fit": "^0.10",
|
|
"@xterm/addon-web-links": "^0.12.0",
|
|
"@xterm/addon-webgl": "^0.18",
|
|
"@xterm/xterm": "^5",
|
|
"react": "^19.0.0",
|
|
"react-dom": "^19.0.0",
|
|
"zustand": "^5"
|
|
},
|
|
"devDependencies": {
|
|
"@tailwindcss/vite": "^4",
|
|
"@tauri-apps/cli": "^2",
|
|
"@testing-library/jest-dom": "^6.9.1",
|
|
"@testing-library/react": "^16.3.2",
|
|
"@types/react": "^19.0.0",
|
|
"@types/react-dom": "^19.0.0",
|
|
"@vitejs/plugin-react": "^4",
|
|
"autoprefixer": "^10",
|
|
"jsdom": "^28.1.0",
|
|
"postcss": "^8",
|
|
"tailwindcss": "^4",
|
|
"typescript": "^5.7",
|
|
"vite": "^6",
|
|
"vitest": "^4.0.18"
|
|
}
|
|
}
|