feat(move): "move to folder" for grid selections, folders, and m shortcut

Extends the heap-only "move to folder" action to grid single/bulk
selections, sidebar folders, and an `m` keyboard shortcut — all through
one shared dialog driven by a moveDialog store.

Backend (sidecar):
- Extract the heap move/copy + reindex loop into a reusable movePhotoFiles
  helper plus resolveMoveTarget
- POST /photos/move: move/copy an arbitrary UID list into a folder
- POST /folders/:rel/move: reparent a folder dir (whole subtree) under a
  new parent, guarding against moving into itself/a descendant

Frontend:
- moveDialog store + generalized MoveToFolderDialog (heap | photos | folder
  subjects); mounted once in +layout.svelte. Replaces HeapConvertDialog
- movePhotosToFolder / moveFolder service fns
- Entry points: BulkActionBar button, gridKeyNav `m`, FolderTree kebab,
  heap kebab — all call openMove()

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-18 00:10:19 +02:00
parent 5be6fd9047
commit b2b6060872
12 changed files with 725 additions and 383 deletions

View File

@@ -19,6 +19,7 @@
import LeftSidebar from '$lib/components/layout/LeftSidebar.svelte';
import AnimatedMule from '$lib/components/mule/AnimatedMule.svelte';
import PreviewModal from '$lib/components/preview/PreviewModal.svelte';
import MoveToFolderDialog from '$lib/components/layout/MoveToFolderDialog.svelte';
let { children } = $props();
@@ -117,6 +118,10 @@
helper (called by the timeline / PhotoGrid dblclick paths and
by gridKeyNav's Space handler). -->
<PreviewModal />
<!-- Single shared move-to-folder dialog, driven by the moveDialog
store. Opened from the heap/folder kebabs, the BulkActionBar
button, and the `m` shortcut — all through openMove(). -->
<MoveToFolderDialog />
{:else}
{@render children?.()}
{/if}