Hold back the Disk panel and OS drag-out from the ship branch
This is a scope reduction, not an abandonment. Both subsystems are
preserved in full on `hold/disk-and-dragout` and are intended to come
back once they have been hardened separately. Nothing here is a
judgement that the features are unwanted — three successive
audit-and-fix cycles each closed a critical defect in these two areas
and each opened a new one, so the rest of the round ships now and these
two get their own cycle rather than holding it up.
Removed: the Disk settings panel and its whole reclaim / destroy /
compaction surface — `DiskSettings`, `DiskProjectTable`, `useDiskUsage`,
`docker/disk.rs`, `disk_tests.rs`, the disk commands in
`docker_commands.rs`, and their `generate_handler!` entries. Dropping
the IPC entries is the point: a UI-only removal would have left five
commands callable by a compromised webview, one of them a verified
arbitrary-DELETE primitive. `sweep_orphaned_snapshots`'s *command* goes
with them (the panel was its only caller); the sweep itself stays.
Removed: OS drag-out from the Files tab — `stage_container_file_for_drag`
and its host staging lifecycle, the pointer gesture and `dragPreview`,
`stageForDrag` / `isStagedHostPath`, the `tauri-plugin-drag` and
`@crabnebula/tauri-plugin-drag` dependencies, and the
`drag:allow-start-drag` capability grant, which could not be scoped.
The capability test's expected list is updated; its `*:default` and
`store:*` assertions are untouched.
Kept, deliberately: drag-and-drop *into* the app (Files pane and
terminal) and "Save to host…", which is now the only route out of a
container. The prevention work is untouched — the pre-commit scrub and
`SNAPSHOT_SCRUB_PATHS`, capped container logs, the `triple-c.base` /
`triple-c.managed` labels, `sweep_orphaned_snapshots` and the startup
housekeeping, the migration pin/probe reapers, scheduler log pruning,
`formatBytes.ts`, and `project_lock.rs` in full with every acquisition
site outside `disk.rs`.
Entanglements, resolved rather than deleted blind:
* `container.rs`'s `a_compaction_runs_this_module_s_scrub_script_byte_for_byte`
pinned the compaction Dockerfile against `snapshot_scrub_script()`.
Dropped — it existed only for compaction. `snapshot_scrub_script` and
its containment tests are untouched.
* `lib.rs`'s startup reap of `:compacting` tags and `triple-c-compact-*`
containers is dropped: nothing on this branch creates them.
* `project_lock`'s `Compaction` / `CacheClear` variants and
`any_held_excluding`, `migration_commands::is_migrating`, and
`formatBytes{Delta,Ceiling}` lose their last production caller but are
kept and still tested, annotated with why.
* `projects_store::corrupt_since` and `migration_store::peek_ownerless_since`
were read only by the disk survey and are removed. The corrupt-load
marker and `.bak` are still written.
Verified: `npm run test` 611 passing, `npx tsc --noEmit` clean,
`npm run build` green; `cargo test` 419 passed / 2 ignored,
`cargo build` 0 warnings. Every test removed belongs to a removed
feature — no kept-behaviour test was weakened or deleted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBq2rGum6GX7xXgsas1fDc
This commit is contained in:
@@ -32,15 +32,6 @@ function baseName(path: string): string {
|
||||
return parts[parts.length - 1] || path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Host paths compare on separators, not on case: the OS hands a dropped path
|
||||
* back in whatever form its file dialog produced, and on Windows that is not
|
||||
* reliably the form `stage_container_file_for_drag` returned.
|
||||
*/
|
||||
function normaliseHostPath(path: string): string {
|
||||
return path.replace(/\\/g, "/").replace(/\/+$/, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* ## Where failures are reported
|
||||
*
|
||||
@@ -51,7 +42,7 @@ function normaliseHostPath(path: string): string {
|
||||
* no rows, and it is not transient — it stands until the directory lists.
|
||||
*
|
||||
* Every **transient operation** failure — upload, rename, create folder,
|
||||
* save-to-host, drag staging — goes to `ToastHost` instead. Those used to land
|
||||
* save-to-host — goes to `ToastHost` instead. Those used to land
|
||||
* in the same inline `error` div, which is the first child of the *scrolling*
|
||||
* list: three hundred rows down, a refused rename produced no visible change
|
||||
* at all, just a rename box that stayed open for no stated reason. Worse, the
|
||||
@@ -91,7 +82,7 @@ export function useFileManager(projectId: string) {
|
||||
/**
|
||||
* A slow listing can land after a newer one and set both the rows and the
|
||||
* breadcrumb back to a directory the user already left. Same generation
|
||||
* guard `useDiskUsage` and `useContainerMigration` use: every async write
|
||||
* guard `useContainerMigration` uses: every async write
|
||||
* checks it is still the newest before it lands.
|
||||
*/
|
||||
const navGeneration = useRef(0);
|
||||
@@ -320,63 +311,6 @@ export function useFileManager(projectId: string) {
|
||||
[projectId, navigate, startWork, report, askOverwrite],
|
||||
);
|
||||
|
||||
/**
|
||||
* Host paths already copied out this session, keyed by the entry they came
|
||||
* from. Size and mtime are in the key, so an entry that changed since the
|
||||
* last listing re-stages rather than dragging a stale copy.
|
||||
*/
|
||||
const stagedRef = useRef(new Map<string, string>());
|
||||
/**
|
||||
* The same paths the other way round, as a set.
|
||||
*
|
||||
* A drag-out released back inside the app arrives as an ordinary host drop
|
||||
* carrying the staged copy's path, and uploading that would write the app's
|
||||
* own temp copy over the container file it came from — which is worse than a
|
||||
* no-op, because the key above is built from the *last listing*, so a file an
|
||||
* agent rewrote since then would be replaced by a minutes-old snapshot. This
|
||||
* set is what makes the "is this ours?" test exact instead of a guess at the
|
||||
* temp directory's name.
|
||||
*/
|
||||
const stagedHostPathsRef = useRef(new Set<string>());
|
||||
|
||||
/** True when `path` is a copy this pane staged for a drag-out. */
|
||||
const isStagedHostPath = useCallback(
|
||||
(path: string) => stagedHostPathsRef.current.has(normaliseHostPath(path)),
|
||||
[],
|
||||
);
|
||||
|
||||
/**
|
||||
* Copy an entry onto the host so the OS can drag it, and return the absolute
|
||||
* host path — or `null`, having reported why, if it could not be staged.
|
||||
*
|
||||
* `cached` is what the caller needs to tell a gesture that will feel
|
||||
* instantaneous from one that has a whole-file copy in front of it: the copy
|
||||
* is the slow half of a drag-out, and the OS only picks a drag up while the
|
||||
* button is still down.
|
||||
*/
|
||||
const stageForDrag = useCallback(
|
||||
async (entry: FileEntry): Promise<{ hostPath: string; cached: boolean } | null> => {
|
||||
const key = `${entry.path}|${entry.size}|${entry.modified}`;
|
||||
const cached = stagedRef.current.get(key);
|
||||
if (cached) return { hostPath: cached, cached: true };
|
||||
|
||||
startWork(`Preparing "${entry.name}"…`);
|
||||
try {
|
||||
const hostPath = await commands.stageContainerFileForDrag(projectId, entry.path);
|
||||
stagedRef.current.set(key, hostPath);
|
||||
stagedHostPathsRef.current.add(normaliseHostPath(hostPath));
|
||||
setCompleted(`"${entry.name}" is ready to drag.`);
|
||||
return { hostPath, cached: false };
|
||||
} catch (e) {
|
||||
report(`Could not prepare "${entry.name}" for dragging`, e);
|
||||
return null;
|
||||
} finally {
|
||||
setBusy(null);
|
||||
}
|
||||
},
|
||||
[projectId, startWork, report],
|
||||
);
|
||||
|
||||
const uploadFile = useCallback(async () => {
|
||||
try {
|
||||
const selected = await openDialog({ multiple: true, directory: false });
|
||||
@@ -447,8 +381,6 @@ export function useFileManager(projectId: string) {
|
||||
downloadFile,
|
||||
uploadFile,
|
||||
uploadPaths,
|
||||
stageForDrag,
|
||||
isStagedHostPath,
|
||||
renameEntry,
|
||||
createFolder,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user