diff --git a/src/components/PreviewOverlay.svelte b/src/components/PreviewOverlay.svelte
index 1038800..ed931ec 100644
--- a/src/components/PreviewOverlay.svelte
+++ b/src/components/PreviewOverlay.svelte
@@ -436,6 +436,43 @@
await previewCommands.playFullTrack(async (item) => starter(item));
await closePreview();
}
+
+ // "Open album" goes to the record the PREVIEWED TRACK declares, which on a
+ // top-tracks session is often a single or a compilation rather than the
+ // artist's studio album — that is the record the metadata line above is
+ // already naming, and the id comes from the provider rather than a title
+ // match, so the two can never disagree.
+ //
+ // An album session is already standing on that record, so the action is
+ // offered only when it would actually take you somewhere.
+ const albumTarget = $derived(
+ activeItem?.actions.openAlbum && activeItem.albumId ? activeItem.albumId : null,
+ );
+ const atOwnAlbum = $derived(
+ albumTarget != null && manifest?.context.contextKey === `album:${albumTarget}`,
+ );
+ const canOpenAlbum = $derived(
+ Boolean(previewUi.openAlbum) && albumTarget != null && !atOwnAlbum,
+ );
+ const openAlbumTitle = $derived(
+ atOwnAlbum
+ ? "You're already on this album"
+ : albumTarget == null
+ ? "No album to open for this track"
+ : "Open album",
+ );
+
+ async function onOpenAlbum() {
+ if (!canOpenAlbum) return;
+ // Both are read BEFORE the close, which nulls the callback and drops
+ // the snapshot the active item came from.
+ const go = previewUi.openAlbum;
+ const item = activeItem;
+ // Close first: the session holds the audio focus, and navigating under
+ // an open dialog would land the new page behind the scrim.
+ await closePreview();
+ go(item);
+ }
@@ -570,7 +607,13 @@
-