diff --git a/web/src/lib/components/layout/CommandPalette.svelte b/web/src/lib/components/layout/CommandPalette.svelte
new file mode 100644
index 0000000..e078cbb
--- /dev/null
+++ b/web/src/lib/components/layout/CommandPalette.svelte
@@ -0,0 +1,200 @@
+
+
+
+ {
+ if (!o) closePalette();
+ }}
+>
+
+
+
+
+
+
+
+
+ No matches.
+
+
+
+
+ Go to
+
+
+ {#each SECTIONS as s (s.label)}
+
+
+ {s.label}
+
+ {/each}
+
+
+
+ {#if (heapsQuery.data ?? []).length > 0}
+
+
+ Heaps
+
+
+ {#each heapsQuery.data ?? [] as heap (heap.UID)}
+
+
+ {heap.Title}
+ {#if heap.PhotoCount}
+
+ {heap.PhotoCount}
+
+ {/if}
+
+ {/each}
+
+
+ {/if}
+
+ {#if folderEntries.length > 0}
+
+
+ Folders
+
+
+ {#each folderEntries as folder (folder.Path)}
+
+
+ {folder.Path}
+
+ {/each}
+
+
+ {/if}
+
+
+
+ Actions
+
+
+ {#each ACTIONS as a (a.label)}
+
+
+ {a.label}
+
+ {/each}
+
+
+
+
+
+
+
+
diff --git a/web/src/lib/stores/view.svelte.ts b/web/src/lib/stores/view.svelte.ts
index 28f919a..24a553a 100644
--- a/web/src/lib/stores/view.svelte.ts
+++ b/web/src/lib/stores/view.svelte.ts
@@ -83,6 +83,8 @@ export const view = $state<{
previewOpen: boolean;
/** Ephemeral: true while the keyboard-shortcuts overlay is open. */
shortcutsOpen: boolean;
+ /** Ephemeral: true while the ⌘K command palette is open. */
+ paletteOpen: boolean;
metadataSections: Record;
}>({
rightSidebarCollapsed: initial.rightSidebarCollapsed ?? false,
@@ -110,6 +112,7 @@ export const view = $state<{
tagsBrowserCollapsed: initial.tagsBrowserCollapsed ?? false,
previewOpen: false,
shortcutsOpen: false,
+ paletteOpen: false,
metadataSections:
initial.metadataSections && typeof initial.metadataSections === 'object'
? { ...initial.metadataSections }
@@ -175,6 +178,14 @@ export function closeShortcuts(): void {
view.shortcutsOpen = false;
}
+export function togglePalette(): void {
+ view.paletteOpen = !view.paletteOpen;
+}
+
+export function closePalette(): void {
+ view.paletteOpen = false;
+}
+
export function setThumbnailSize(size: ThumbnailSize): void {
view.thumbnailSize = size;
persist();
diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte
index b552a0f..aba87cc 100644
--- a/web/src/routes/+layout.svelte
+++ b/web/src/routes/+layout.svelte
@@ -21,6 +21,8 @@
import PreviewModal from '$lib/components/preview/PreviewModal.svelte';
import MoveToFolderDialog from '$lib/components/layout/MoveToFolderDialog.svelte';
import ShortcutsDialog from '$lib/components/layout/ShortcutsDialog.svelte';
+ import CommandPalette from '$lib/components/layout/CommandPalette.svelte';
+ import { togglePalette } from '$lib/stores/view.svelte';
let { children } = $props();
@@ -59,8 +61,18 @@
else stopIndexerWatch();
});
+ // ⌘K lives at the window level (not gridKeyNav) so the palette opens
+ // from any route and even while a form field holds focus.
+ function onGlobalKey(e: KeyboardEvent) {
+ if ((e.metaKey || e.ctrlKey) && (e.key === 'k' || e.key === 'K')) {
+ e.preventDefault();
+ togglePalette();
+ }
+ }
+
+
Mulimage
@@ -125,6 +137,8 @@
+
+
{:else}
{@render children?.()}
{/if}