diff --git a/web/src/lib/components/timeline/StreamWithViewer.svelte b/web/src/lib/components/timeline/StreamWithViewer.svelte
new file mode 100644
index 0000000000..53730fc2e9
--- /dev/null
+++ b/web/src/lib/components/timeline/StreamWithViewer.svelte
@@ -0,0 +1,76 @@
+
+
+{@render children?.()}
+
+
+ {#if $showAssetViewer}
+ {@render assetViewer({ onViewerClose })}
+ {/if}
+
diff --git a/web/src/lib/components/timeline/Timeline.svelte b/web/src/lib/components/timeline/Timeline.svelte
index fab5b19f9e..07580489b3 100644
--- a/web/src/lib/components/timeline/Timeline.svelte
+++ b/web/src/lib/components/timeline/Timeline.svelte
@@ -1,25 +1,19 @@
- (await viewer?.scrollToAsset(asset)) ?? Promise.resolve(false)}
- {timelineManager}
- {assetInteraction}
- bind:isShowDeleteConfirmation
- {onEscape}
-/>
-
-
- {#snippet skeleton({ segment })}
-
- {/snippet}
- {#snippet segment({ segment, onScrollCompensationMonthInDOM })}
-
- {#snippet content({ onAssetOpen, onAssetSelect, onAssetHover })}
-
- {#snippet content({ onDayGroupSelect, onDayGroupAssetSelect })}
-
- {#snippet thumbnail({ asset, position, dayGroup, groupIndex })}
- {@const isAssetSelectionCandidate = assetInteraction.hasSelectionCandidate(asset.id)}
- {@const isAssetSelected =
- assetInteraction.hasSelectedAsset(asset.id) || timelineManager.albumAssets.has(asset.id)}
- {@const isAssetDisabled = timelineManager.albumAssets.has(asset.id)}
- onAssetOpen(asset)}
- onSelect={() => onDayGroupAssetSelect(dayGroup, asset)}
- onMouseEvent={(isMouseOver) => {
- if (isMouseOver) {
- onAssetHover(asset);
- } else {
- onAssetHover(null);
- }
- }}
- selected={isAssetSelected}
- selectionCandidate={isAssetSelectionCandidate}
- disabled={isAssetDisabled}
- thumbnailWidth={position.width}
- thumbnailHeight={position.height}
- />
- {/snippet}
-
- {/snippet}
-
- {/snippet}
-
- {/snippet}
-
-
-
- {#if $showAssetViewer}
+
+ {#snippet assetViewer({ onViewerClose })}
- {/if}
-
+ {/snippet}
+ (await viewer?.scrollToAsset(asset)) ?? Promise.resolve(false)}
+ {timelineManager}
+ {assetInteraction}
+ bind:isShowDeleteConfirmation
+ {onEscape}
+ />
+
+ {#snippet skeleton({ segment })}
+
+ {/snippet}
+ {#snippet segment({ segment, onScrollCompensationMonthInDOM })}
+
+ {#snippet content({ onAssetOpen, onAssetSelect, onAssetHover })}
+
+ {#snippet content({ onDayGroupSelect, onDayGroupAssetSelect })}
+
+ {#snippet thumbnail({ asset, position, dayGroup, groupIndex })}
+ {@const isAssetSelectionCandidate = assetInteraction.hasSelectionCandidate(asset.id)}
+ {@const isAssetSelected =
+ assetInteraction.hasSelectedAsset(asset.id) || timelineManager.albumAssets.has(asset.id)}
+ {@const isAssetDisabled = timelineManager.albumAssets.has(asset.id)}
+ onAssetOpen(asset)}
+ onSelect={() => onDayGroupAssetSelect(dayGroup, asset)}
+ onMouseEvent={(isMouseOver) => {
+ if (isMouseOver) {
+ onAssetHover(asset);
+ } else {
+ onAssetHover(null);
+ }
+ }}
+ selected={isAssetSelected}
+ selectionCandidate={isAssetSelectionCandidate}
+ disabled={isAssetDisabled}
+ thumbnailWidth={position.width}
+ thumbnailHeight={position.height}
+ />
+ {/snippet}
+
+ {/snippet}
+
+ {/snippet}
+
+ {/snippet}
+
+