diff --git a/PLAN.md b/PLAN.md index dd090b2..8d50470 100644 --- a/PLAN.md +++ b/PLAN.md @@ -683,6 +683,30 @@ something long in one, switch away and back, refresh — it's all still there. - *Not done, on purpose:* no cap on the cache and no clear button (not asked; it only holds what was looked at); no tonemapping for HDR sources (a 10-bit HEVC frame comes out flat); no preview in the viewer for a video (it still says "binary, download it"). +- **Move… is a folder picker** — **shipped 2026-10-11** (v0.1.0-204, `MovePicker` in + `Files.tsx`, `internal/files/move.go`, `POST /api/files/move`). Asked for as: instead of + a path prompt, "an interactive file explorer, where I can go back/forward, etc and then + click some button, and it will move the file/folder to where I was". The picker opens in + the item's own folder and walks the machine the way the tab does — back and forward + through where it has been, up, breadcrumbs, a path box, folders only, `.hidden` + following the tab's toggle, **+ folder** to make the destination on the spot — and + **Move here** puts the item in the folder being looked at. It says why it can't when it + can't, with the button greyed: already here, a folder into itself, a name clash in that + folder (read from the listing before anything is tried). The row being moved shows in + its own folder, greyed. Rename stays a name prompt: it is a different question. + - **Across filesystems it copies, then removes.** `os.Rename` is EXDEV between + filesystems, and on `grid` every dataset is one — `/tank/media` to `/tank/files` was a + "invalid cross-device link" error before this, and the picker would have made that + move easy to attempt. `/api/files/move` renames when it can (204) and otherwise runs + `hamlab fs move` as a task (202 with the task; the tab shows its output and reloads + when it ends). The copy is built under a hidden name beside its destination + (`..hamlab-moving`), renamed into place once complete, and only then is the + original removed — a failure part-way removes the partial copy and leaves the original + as it was, and a cancel (SIGTERM to the helper) does the same. Modes, mtimes and + symlinks are kept; owners too when the backend is root. Devices, pipes and sockets are + refused outright rather than half-copied. Progress every 500 files or 512 MiB. + Verified on `grid` ext4 → ZFS with a tree holding a 0600 file, a symlink and an old + mtime, all intact on the other side. - **Docker read-only view** — `docker ps`/logs; dockhand stays the source of truth. - **Notifications** — push on service failure / SMART warning / UPS event. - **Terminal extras** — search, copy-mode, font/theme, split panes, upload via