feat: bunx biome format --write .

This commit is contained in:
Simon Orzel
2025-07-17 03:00:38 +02:00
parent 1505c1b12f
commit 167f366c6d
11 changed files with 1038 additions and 1049 deletions
@@ -20,11 +20,11 @@ export function useTimelineActionHandlers() {
// Create optimized lookup maps for O(1) access instead of O(n) find operations // Create optimized lookup maps for O(1) access instead of O(n) find operations
const trackMap = useMemo(() => { const trackMap = useMemo(() => {
const map = new Map(); const map = new Map();
tracks.forEach(track => { tracks.forEach((track) => {
map.set(track.id, track); map.set(track.id, track);
// Also create element lookup for this track // Also create element lookup for this track
const elementMap = new Map(); const elementMap = new Map();
track.elements.forEach(element => { track.elements.forEach((element) => {
elementMap.set(element.id, element); elementMap.set(element.id, element);
}); });
map.set(`${track.id}_elements`, elementMap); map.set(`${track.id}_elements`, elementMap);
@@ -1,6 +1,9 @@
import { useCallback } from "react"; import { useCallback } from "react";
import { useProjectStore } from "@/stores/project-store"; import { useProjectStore } from "@/stores/project-store";
import { TIMELINE_CONSTANTS, snapTimeToFrame } from "@/constants/timeline-constants"; import {
TIMELINE_CONSTANTS,
snapTimeToFrame,
} from "@/constants/timeline-constants";
export interface TimelineContentClickProps { export interface TimelineContentClickProps {
duration: number; duration: number;
@@ -56,7 +59,7 @@ export function useTimelineContentClick({
// Determine if we're clicking in ruler or tracks area // Determine if we're clicking in ruler or tracks area
const isRulerClick = (e.target as HTMLElement).closest( const isRulerClick = (e.target as HTMLElement).closest(
"[data-ruler-area]" "[data-ruler-area]",
); );
let mouseX: number; let mouseX: number;
@@ -65,7 +68,7 @@ export function useTimelineContentClick({
if (isRulerClick) { if (isRulerClick) {
// Calculate based on ruler position // Calculate based on ruler position
const rulerContent = rulerScrollRef.current?.querySelector( const rulerContent = rulerScrollRef.current?.querySelector(
"[data-radix-scroll-area-viewport]" "[data-radix-scroll-area-viewport]",
) as HTMLElement; ) as HTMLElement;
if (!rulerContent) return; if (!rulerContent) return;
const rect = rulerContent.getBoundingClientRect(); const rect = rulerContent.getBoundingClientRect();
@@ -74,7 +77,7 @@ export function useTimelineContentClick({
} else { } else {
// Calculate based on tracks content position // Calculate based on tracks content position
const tracksContent = tracksScrollRef.current?.querySelector( const tracksContent = tracksScrollRef.current?.querySelector(
"[data-radix-scroll-area-viewport]" "[data-radix-scroll-area-viewport]",
) as HTMLElement; ) as HTMLElement;
if (!tracksContent) return; if (!tracksContent) return;
const rect = tracksContent.getBoundingClientRect(); const rect = tracksContent.getBoundingClientRect();
@@ -87,8 +90,8 @@ export function useTimelineContentClick({
Math.min( Math.min(
duration, duration,
(mouseX + scrollLeft) / (mouseX + scrollLeft) /
(TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel) (TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel),
) ),
); );
// Use frame snapping for timeline clicking // Use frame snapping for timeline clicking
@@ -108,7 +111,7 @@ export function useTimelineContentClick({
justFinishedSelecting, justFinishedSelecting,
playheadRef, playheadRef,
activeProject?.fps, activeProject?.fps,
] ],
); );
return { handleTimelineContentClick }; return { handleTimelineContentClick };
@@ -56,9 +56,13 @@ export function TimelineContent({
showSnapIndicator, showSnapIndicator,
}: TimelineContentProps) { }: TimelineContentProps) {
return ( return (
<div className="relative min-w-max min-h-max" style={{ minWidth: `${dynamicTimelineWidth}px` }}> <div
<div className={`h-full grid grid-cols-[192px_1fr] ${tracks.length > 0 ? `grid-rows-[20px_repeat(${tracks.length},minmax(0,max-content))]` : 'grid-rows-[20px_1fr]'}`}> className="relative min-w-max min-h-max"
style={{ minWidth: `${dynamicTimelineWidth}px` }}
>
<div
className={`h-full grid grid-cols-[192px_1fr] ${tracks.length > 0 ? `grid-rows-[20px_repeat(${tracks.length},minmax(0,max-content))]` : "grid-rows-[20px_1fr]"}`}
>
{/* Top-Left Corner (Empty space above track labels) */} {/* Top-Left Corner (Empty space above track labels) */}
<div className="sticky top-0 left-0 border-inset bg-card/[0.99]"></div> <div className="sticky top-0 left-0 border-inset bg-card/[0.99]"></div>
@@ -66,7 +66,7 @@ export function useTimelineDragHandlers({
// Ignore timeline element drags - they're handled by track-specific handlers // Ignore timeline element drags - they're handled by track-specific handlers
const hasTimelineElement = e.dataTransfer.types.includes( const hasTimelineElement = e.dataTransfer.types.includes(
"application/x-timeline-element" "application/x-timeline-element",
); );
if (hasTimelineElement) { if (hasTimelineElement) {
return; return;
@@ -83,7 +83,7 @@ export function useTimelineDragHandlers({
} else { } else {
// Handle media items // Handle media items
const mediaItem = mediaItems.find( const mediaItem = mediaItems.find(
(item: any) => item.id === dragData.id (item: any) => item.id === dragData.id,
); );
if (!mediaItem) { if (!mediaItem) {
toast.error("Media item not found"); toast.error("Media item not found");
@@ -108,14 +108,15 @@ export function useTimelineDragHandlers({
try { try {
const processedItems = await processMediaFiles( const processedItems = await processMediaFiles(
e.dataTransfer.files, e.dataTransfer.files,
(p) => setProgress(p) (p) => setProgress(p),
); );
for (const processedItem of processedItems) { for (const processedItem of processedItems) {
await addMediaItem(activeProject.id, processedItem); await addMediaItem(activeProject.id, processedItem);
const currentMediaItems = useMediaStore.getState().mediaItems; const currentMediaItems = useMediaStore.getState().mediaItems;
const addedItem = currentMediaItems.find( const addedItem = currentMediaItems.find(
(item) => (item) =>
item.name === processedItem.name && item.url === processedItem.url item.name === processedItem.name &&
item.url === processedItem.url,
); );
if (addedItem) { if (addedItem) {
useTimelineStore.getState().addMediaToNewTrack(addedItem); useTimelineStore.getState().addMediaToNewTrack(addedItem);
@@ -56,7 +56,8 @@ export function TimelinePlayhead({
// Get dynamic track labels width, fallback to 192px (ml-48) if no tracks or no ref // Get dynamic track labels width, fallback to 192px (ml-48) if no tracks or no ref
const trackLabelsWidth = 192; // Fixed width from grid layout const trackLabelsWidth = 192; // Fixed width from grid layout
const leftPosition = playheadPosition * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel; const leftPosition =
playheadPosition * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel;
return ( return (
<> <>
@@ -77,8 +77,10 @@ export function TimelineRuler({
const time = i * interval; const time = i * interval;
if (time > duration) break; if (time > duration) break;
const isMainMarker = time % (interval >= 1 ? Math.max(1, interval) : 1) === 0; const isMainMarker =
const leftPosition = time * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel; time % (interval >= 1 ? Math.max(1, interval) : 1) === 0;
const leftPosition =
time * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel;
markers.push( markers.push(
<div <div
@@ -99,7 +101,7 @@ export function TimelineRuler({
> >
{formatTime(time, interval)} {formatTime(time, interval)}
</span> </span>
</div> </div>,
); );
} }
@@ -15,10 +15,10 @@ export function useTimelineScrollSync({
// --- Horizontal scroll synchronization between ruler and tracks --- // --- Horizontal scroll synchronization between ruler and tracks ---
useEffect(() => { useEffect(() => {
const rulerViewport = rulerScrollRef.current?.querySelector( const rulerViewport = rulerScrollRef.current?.querySelector(
"[data-radix-scroll-area-viewport]" "[data-radix-scroll-area-viewport]",
) as HTMLElement; ) as HTMLElement;
const tracksViewport = tracksScrollRef.current?.querySelector( const tracksViewport = tracksScrollRef.current?.querySelector(
"[data-radix-scroll-area-viewport]" "[data-radix-scroll-area-viewport]",
) as HTMLElement; ) as HTMLElement;
if (!rulerViewport || !tracksViewport) return; if (!rulerViewport || !tracksViewport) return;
@@ -44,8 +44,12 @@ export function useTimelineScrollSync({
isUpdatingRef.current = false; isUpdatingRef.current = false;
}; };
rulerViewport.addEventListener("scroll", handleRulerScroll, { passive: true }); rulerViewport.addEventListener("scroll", handleRulerScroll, {
tracksViewport.addEventListener("scroll", handleTracksScroll, { passive: true }); passive: true,
});
tracksViewport.addEventListener("scroll", handleTracksScroll, {
passive: true,
});
return () => { return () => {
rulerViewport.removeEventListener("scroll", handleRulerScroll); rulerViewport.removeEventListener("scroll", handleRulerScroll);
@@ -105,20 +105,14 @@ export function TimelineToolbar({
Add Test Clip Add Test Clip
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>Add a test clip to try playback</TooltipContent>
Add a test clip to try playback
</TooltipContent>
</Tooltip> </Tooltip>
</> </>
)} )}
<div className="w-px h-6 bg-border mx-1" /> <div className="w-px h-6 bg-border mx-1" />
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button variant="text" size="icon" onClick={handleSplitSelected}>
variant="text"
size="icon"
onClick={handleSplitSelected}
>
<Scissors className="h-4 w-4" /> <Scissors className="h-4 w-4" />
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
@@ -150,11 +144,7 @@ export function TimelineToolbar({
</Tooltip> </Tooltip>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button variant="text" size="icon" onClick={handleSeparateAudio}>
variant="text"
size="icon"
onClick={handleSeparateAudio}
>
<SplitSquareHorizontal className="h-4 w-4" /> <SplitSquareHorizontal className="h-4 w-4" />
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
@@ -174,11 +164,7 @@ export function TimelineToolbar({
</Tooltip> </Tooltip>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button variant="text" size="icon" onClick={handleFreezeSelected}>
variant="text"
size="icon"
onClick={handleFreezeSelected}
>
<Snowflake className="h-4 w-4" /> <Snowflake className="h-4 w-4" />
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
@@ -186,11 +172,7 @@ export function TimelineToolbar({
</Tooltip> </Tooltip>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button variant="text" size="icon" onClick={handleDeleteSelected}>
variant="text"
size="icon"
onClick={handleDeleteSelected}
>
<Trash2 className="h-4 w-4" /> <Trash2 className="h-4 w-4" />
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
@@ -42,9 +42,7 @@ export function TimelineTracksArea({
<TrackIcon track={track} /> <TrackIcon track={track} />
</div> </div>
{track.muted && ( {track.muted && (
<span className="text-xs text-red-500 font-semibold"> <span className="text-xs text-red-500 font-semibold">Muted</span>
Muted
</span>
)} )}
</div> </div>
@@ -55,11 +53,7 @@ export function TimelineTracksArea({
className=" h-full" className=" h-full"
onClick={(e) => { onClick={(e) => {
// If clicking empty area (not on a element), deselect all elements // If clicking empty area (not on a element), deselect all elements
if ( if (!(e.target as HTMLElement).closest(".timeline-element")) {
!(e.target as HTMLElement).closest(
".timeline-element"
)
) {
clearSelectedElements(); clearSelectedElements();
} }
}} }}
@@ -72,21 +66,17 @@ export function TimelineTracksArea({
</div> </div>
</ContextMenuTrigger> </ContextMenuTrigger>
<ContextMenuContent> <ContextMenuContent>
<ContextMenuItem <ContextMenuItem onClick={() => toggleTrackMute(track.id)}>
onClick={() => toggleTrackMute(track.id)}
>
{track.muted ? "Unmute Track" : "Mute Track"} {track.muted ? "Unmute Track" : "Mute Track"}
</ContextMenuItem> </ContextMenuItem>
<ContextMenuItem> <ContextMenuItem>Track settings (soon)</ContextMenuItem>
Track settings (soon)
</ContextMenuItem>
</ContextMenuContent> </ContextMenuContent>
</ContextMenu> </ContextMenu>
</Fragment> </Fragment>
))} ))}
{/* Add Track Button - spans full width */} {/* Add Track Button - spans full width */}
<div <div
onClick={() => addTrack('media')} onClick={() => addTrack("media")}
className="col-span-1 sticky left-0 w-full flex items-center border-b border-muted bg-card/[0.99] hover:bg-card/50 transition-colors cursor-pointer z-[89]" className="col-span-1 sticky left-0 w-full flex items-center border-b border-muted bg-card/[0.99] hover:bg-card/50 transition-colors cursor-pointer z-[89]"
style={{ height: `${getTrackHeight("media")}px` }} style={{ height: `${getTrackHeight("media")}px` }}
> >
@@ -19,7 +19,9 @@ export function useTimelineWheelHandler({
handleWheel(e as any); handleWheel(e as any);
}; };
timelineContainer.addEventListener("wheel", handleWheelCapture, { passive: false }); timelineContainer.addEventListener("wheel", handleWheelCapture, {
passive: false,
});
return () => { return () => {
timelineContainer.removeEventListener("wheel", handleWheelCapture); timelineContainer.removeEventListener("wheel", handleWheelCapture);