import type { Clip } from '$lib/stores/clips.svelte'; import { timeToX, type TimelineState } from './renderer'; const CLIP_LANE_Y = 90; const CLIP_LANE_HEIGHT = 30; const HANDLE_WIDTH = 6; export function drawClips( ctx: CanvasRenderingContext2D, state: TimelineState, clips: Clip[], selectedId: string | null, pendingInPoint: number | null ): void { for (const clip of clips) { const x1 = timeToX(clip.startTime, state); const x2 = timeToX(clip.endTime, state); const isSelected = clip.id === selectedId; ctx.fillStyle = clip.color + '66'; ctx.fillRect(x1, CLIP_LANE_Y, x2 - x1, CLIP_LANE_HEIGHT); ctx.strokeStyle = isSelected ? '#ffffff' : clip.color; ctx.lineWidth = isSelected ? 2 : 1; ctx.strokeRect(x1, CLIP_LANE_Y, x2 - x1, CLIP_LANE_HEIGHT); ctx.lineWidth = 1; ctx.fillStyle = isSelected ? '#ffffff' : clip.color; ctx.fillRect(x1 - HANDLE_WIDTH / 2, CLIP_LANE_Y, HANDLE_WIDTH, CLIP_LANE_HEIGHT); ctx.fillRect(x2 - HANDLE_WIDTH / 2, CLIP_LANE_Y, HANDLE_WIDTH, CLIP_LANE_HEIGHT); const labelWidth = x2 - x1; if (labelWidth > 40) { ctx.fillStyle = '#ffffff'; ctx.font = '11px -apple-system, sans-serif'; ctx.textAlign = 'center'; ctx.fillText( clip.label, (x1 + x2) / 2, CLIP_LANE_Y + CLIP_LANE_HEIGHT / 2 + 4, labelWidth - 10 ); } } if (pendingInPoint !== null) { const x = timeToX(pendingInPoint, state); ctx.strokeStyle = '#f9e2af'; ctx.lineWidth = 2; ctx.setLineDash([4, 4]); ctx.beginPath(); ctx.moveTo(x, CLIP_LANE_Y); ctx.lineTo(x, CLIP_LANE_Y + CLIP_LANE_HEIGHT); ctx.stroke(); ctx.setLineDash([]); ctx.lineWidth = 1; } } export interface ClipHitResult { clipId: string; edge: 'start' | 'end' | 'body'; } export function hitTestClip( x: number, y: number, state: TimelineState, clips: Clip[] ): ClipHitResult | null { if (y < CLIP_LANE_Y || y > CLIP_LANE_Y + CLIP_LANE_HEIGHT) { return null; } for (const clip of [...clips].reverse()) { const x1 = timeToX(clip.startTime, state); const x2 = timeToX(clip.endTime, state); if (Math.abs(x - x1) <= HANDLE_WIDTH) { return { clipId: clip.id, edge: 'start' }; } if (Math.abs(x - x2) <= HANDLE_WIDTH) { return { clipId: clip.id, edge: 'end' }; } if (x >= x1 && x <= x2) { return { clipId: clip.id, edge: 'body' }; } } return null; }