Move files and folders inside the UI: drag & drop + explicit move affordance #179

Closed
opened 2026-08-10 00:50:25 +00:00 by Cordy · 1 comment
Owner

Dogfood note (Nikola): drag and drop folders from within the Cairn UI to move them into folders, plus a general possibility to move files and folders up and down the tree.

What exists: the backend has everything needed (POST /api/v1/rename moves any path; the row menu has a "Move…" dialog with a typed target path). What's missing is the direct-manipulation UX users expect from every other file product.

Scope:

  1. Drag & drop within the listing — drag a row onto a folder row → move into it; drag onto a breadcrumb segment → move up to that level. Multi-select drag once selection exists (today rows are single-target: keep v1 single-row and note multi-select as its own follow-up).
  2. Care points: distinguish internal row drags from the existing external file-drop upload zone (both use the drop events — the current upload handler must ignore internal drags); read-only spaces and held paths refuse with the visible error (#172's pattern); admins vs scoped users need no special casing since /rename goes through the scoped store.
  3. Keyboard/menu parity stays: the Move dialog remains for accessibility, and could get a folder-picker instead of a typed path while we're in there.
Dogfood note (Nikola): drag and drop folders from within the Cairn UI to move them into folders, plus a general possibility to move files and folders up and down the tree. **What exists:** the backend has everything needed (`POST /api/v1/rename` moves any path; the row menu has a "Move…" dialog with a typed target path). What's missing is the direct-manipulation UX users expect from every other file product. **Scope:** 1. **Drag & drop within the listing** — drag a row onto a folder row → move into it; drag onto a breadcrumb segment → move up to that level. Multi-select drag once selection exists (today rows are single-target: keep v1 single-row and note multi-select as its own follow-up). 2. Care points: distinguish *internal* row drags from the existing *external* file-drop upload zone (both use the drop events — the current upload handler must ignore internal drags); read-only spaces and held paths refuse with the visible error (#172's pattern); admins vs scoped users need no special casing since `/rename` goes through the scoped store. 3. Keyboard/menu parity stays: the Move dialog remains for accessibility, and could get a folder-picker instead of a typed path while we're in there.
Cordy closed this issue 2026-08-12 00:04:25 +00:00
Author
Owner

Shipped in v0.6.12 (PR #199), live on the dogfood (version=v0.6.12, pod 1/1).

Drag & drop within the listing (scope 1):

  • Drag a file/folder row onto a folder row → moves it inside.
  • Drag a row onto a breadcrumb segment → moves it up to that level.
  • Rows are draggable; folder rows and every breadcrumb are drop targets (wireDropTarget). A drag source dims (opacity) and valid targets highlight.

Care points (scope 2) — all handled:

  • Internal row drags carry a private MIME type application/x-cairn-move. The external OS-file upload drop zone arms only on dataTransfer "Files", so it ignores internal drags; the internal drop handler stopPropagations so it never reaches the document upload handler. The two drop paths are fully separated.
  • Moves reuse the scoped POST /api/v1/rename, so read-only spaces and held paths refuse server-side and surface via the say() banner (#172 pattern). Admins go through the same scoped store — no special-casing (consistent with #177).
  • Guards: never drop onto itself, never a folder into its own subtree, no-op when already in the target folder.

Keyboard/menu parity (scope 3): the Move dialog + folder picker (#185) stay. The folder-picker-instead-of-typed-path note in scope 3 already landed in #185.

Follow-up (not in this change): multi-select drag — deferred until row selection exists, as the issue anticipated. Closing this; will open a separate issue for multi-select drag when selection lands.

Shipped in **v0.6.12** (PR #199), live on the dogfood (`version=v0.6.12`, pod 1/1). **Drag & drop within the listing (scope 1):** - Drag a file/folder row onto a **folder row** → moves it inside. - Drag a row onto a **breadcrumb segment** → moves it up to that level. - Rows are `draggable`; folder rows and every breadcrumb are drop targets (`wireDropTarget`). A drag source dims (`opacity`) and valid targets highlight. **Care points (scope 2) — all handled:** - Internal row drags carry a private MIME type `application/x-cairn-move`. The external OS-file upload drop zone arms only on `dataTransfer` "Files", so it ignores internal drags; the internal drop handler `stopPropagation`s so it never reaches the document upload handler. The two drop paths are fully separated. - Moves reuse the scoped `POST /api/v1/rename`, so read-only spaces and held paths refuse server-side and surface via the `say()` banner (#172 pattern). Admins go through the same scoped store — no special-casing (consistent with #177). - Guards: never drop onto itself, never a folder into its own subtree, no-op when already in the target folder. **Keyboard/menu parity (scope 3):** the Move dialog + folder picker (#185) stay. The folder-picker-instead-of-typed-path note in scope 3 already landed in #185. **Follow-up (not in this change):** multi-select drag — deferred until row selection exists, as the issue anticipated. Closing this; will open a separate issue for multi-select drag when selection lands.
Sign in to join this conversation.
No labels
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/Cairn#179
No description provided.