feat: timeline right-click context menu with mark/seek/delete actions
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,9 +1,14 @@
|
||||
<script lang="ts">
|
||||
import TimelineContextMenu from '$lib/components/TimelineContextMenu.svelte';
|
||||
import type { ContextMenuAction } from '$lib/components/TimelineContextMenu.svelte';
|
||||
import { session } from '$lib/stores/videoSession.svelte';
|
||||
import {
|
||||
clipStore,
|
||||
selectClip,
|
||||
updateClip,
|
||||
markInPoint,
|
||||
markOutPoint,
|
||||
removeClip,
|
||||
} from '$lib/stores/clips.svelte';
|
||||
import { drawTimeline, type TimelineState } from '$lib/timeline/renderer';
|
||||
import {
|
||||
@@ -37,6 +42,11 @@
|
||||
let minimapDragStartX = $state(0);
|
||||
let minimapDragStartVisibleStart = $state(0);
|
||||
let mouseX = $state<number | null>(null);
|
||||
let contextMenu = $state<{
|
||||
x: number;
|
||||
y: number;
|
||||
time: number;
|
||||
} | null>(null);
|
||||
|
||||
let isZoomed = $derived(timelineState.zoom > 1.01);
|
||||
|
||||
@@ -235,7 +245,49 @@
|
||||
}
|
||||
}
|
||||
|
||||
function handleContextMenu(e: MouseEvent) {
|
||||
e.preventDefault();
|
||||
const x = getCanvasX(e);
|
||||
const time = computeClickTime(x, timelineState, session.duration);
|
||||
seekTo(time);
|
||||
contextMenu = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
time,
|
||||
};
|
||||
}
|
||||
|
||||
function handleContextMenuAction(action: ContextMenuAction) {
|
||||
if (!contextMenu) return;
|
||||
switch (action.type) {
|
||||
case 'mark-in':
|
||||
markInPoint(contextMenu.time);
|
||||
break;
|
||||
case 'mark-out':
|
||||
markOutPoint(contextMenu.time);
|
||||
break;
|
||||
case 'go-to-clip-start': {
|
||||
const clip = clipStore.clips.find((c) => c.id === clipStore.selectedClipId);
|
||||
if (clip) seekTo(clip.startTime);
|
||||
break;
|
||||
}
|
||||
case 'go-to-clip-end': {
|
||||
const clip = clipStore.clips.find((c) => c.id === clipStore.selectedClipId);
|
||||
if (clip) seekTo(clip.endTime);
|
||||
break;
|
||||
}
|
||||
case 'delete-clip':
|
||||
if (clipStore.selectedClipId) removeClip(clipStore.selectedClipId);
|
||||
break;
|
||||
default: {
|
||||
const _exhaustive: never = action;
|
||||
void _exhaustive;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseDown(e: MouseEvent) {
|
||||
contextMenu = null;
|
||||
const x = getCanvasX(e);
|
||||
const y = getCanvasY(e);
|
||||
|
||||
@@ -298,6 +350,7 @@
|
||||
|
||||
function handleWheel(e: WheelEvent) {
|
||||
e.preventDefault();
|
||||
contextMenu = null;
|
||||
|
||||
if (e.deltaX !== 0) {
|
||||
const result = panBy(timelineState, e.deltaX, session.duration);
|
||||
@@ -395,10 +448,24 @@
|
||||
onmouseup={handleMouseUp}
|
||||
onmouseleave={handleMouseLeave}
|
||||
onwheel={handleWheel}
|
||||
oncontextmenu={handleContextMenu}
|
||||
></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if contextMenu}
|
||||
<TimelineContextMenu
|
||||
x={contextMenu.x}
|
||||
y={contextMenu.y}
|
||||
time={contextMenu.time}
|
||||
selectedClipId={clipStore.selectedClipId}
|
||||
onAction={handleContextMenuAction}
|
||||
onClose={() => {
|
||||
contextMenu = null;
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<svelte:window
|
||||
onmousemove={handleMinimapMouseMove}
|
||||
onmouseup={handleMinimapMouseUp}
|
||||
|
||||
Reference in New Issue
Block a user