diff --git a/src/App.svelte b/src/App.svelte
index e23ebba..f400c3b 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -62,6 +62,9 @@
// Caption state for PlayerControls
let captionsEnabled = $state(true);
+ // Pending timeline zoom for cut-clip feature
+ let pendingTimelineZoom = $state<{ start: number; end: number } | null>(null);
+
const CONTROLS_HIDE_DELAY = 2500;
const TOOLBAR_PROXIMITY = 50;
@@ -191,6 +194,7 @@
const range = computeQuickClipRange(session.currentTime, session.duration, position);
if (!range) return;
addClip(range.start, range.end);
+ pendingTimelineZoom = { start: range.start, end: range.end };
setAppMode('clipper');
}
@@ -462,7 +466,10 @@
}}
>
-
+ { pendingTimelineZoom = null; }}
+ />
diff --git a/src/lib/components/Timeline.svelte b/src/lib/components/Timeline.svelte
index 43d210e..591745e 100644
--- a/src/lib/components/Timeline.svelte
+++ b/src/lib/components/Timeline.svelte
@@ -22,6 +22,14 @@
type ClipDragTarget = { clipId: string; edge: 'start' | 'end' };
+ let {
+ zoomRegion,
+ onZoomApplied,
+ }: {
+ zoomRegion?: { start: number; end: number } | null;
+ onZoomApplied?: () => void;
+ } = $props();
+
let canvas = $state(null);
let minimapCanvas = $state(null);
let containerEl = $state(null);
@@ -189,6 +197,19 @@
}
});
+ $effect(() => {
+ if (zoomRegion && session.duration > 0) {
+ const clipLen = zoomRegion.end - zoomRegion.start;
+ const pad = Math.max(clipLen * 0.2, 2);
+ const vs = Math.max(0, zoomRegion.start - pad);
+ const ve = Math.min(session.duration, zoomRegion.end + pad);
+ timelineState.visibleStart = vs;
+ timelineState.visibleEnd = ve;
+ timelineState.zoom = session.duration / (ve - vs);
+ onZoomApplied?.();
+ }
+ });
+
$effect(() => {
if (!containerEl) return;
const observer = new ResizeObserver((entries) => {