shipping too hard

This commit is contained in:
Maze Winther
2025-07-08 00:07:22 +02:00
parent 9d2fd50fbc
commit d750d7f41d
4 changed files with 103 additions and 69 deletions

View File

@ -8,7 +8,7 @@ import {
ResizableHandle, ResizableHandle,
} from "../../../components/ui/resizable"; } from "../../../components/ui/resizable";
import { MediaPanel } from "../../../components/editor/media-panel"; import { MediaPanel } from "../../../components/editor/media-panel";
// import { PropertiesPanel } from "../../components/editor/properties-panel"; import { PropertiesPanel } from "../../../components/editor/properties-panel";
import { Timeline } from "../../../components/editor/timeline"; import { Timeline } from "../../../components/editor/timeline";
import { PreviewPanel } from "../../../components/editor/preview-panel"; import { PreviewPanel } from "../../../components/editor/preview-panel";
import { EditorHeader } from "@/components/editor-header"; import { EditorHeader } from "@/components/editor-header";
@ -27,6 +27,8 @@ export default function Editor() {
setPreviewPanel, setPreviewPanel,
setMainContent, setMainContent,
setTimeline, setTimeline,
propertiesPanel,
setPropertiesPanel,
} = usePanelStore(); } = usePanelStore();
const { activeProject, loadProject, createNewProject } = useProjectStore(); const { activeProject, loadProject, createNewProject } = useProjectStore();
@ -94,8 +96,8 @@ export default function Editor() {
<ResizableHandle withHandle /> <ResizableHandle withHandle />
{/* Properties Panel - Hidden for now but ready */}
{/* <ResizablePanel <ResizablePanel
defaultSize={propertiesPanel} defaultSize={propertiesPanel}
minSize={15} minSize={15}
maxSize={40} maxSize={40}
@ -103,7 +105,7 @@ export default function Editor() {
className="min-w-0" className="min-w-0"
> >
<PropertiesPanel /> <PropertiesPanel />
</ResizablePanel> */} </ResizablePanel>
</ResizablePanelGroup> </ResizablePanelGroup>
</ResizablePanel> </ResizablePanel>

View File

@ -173,8 +173,7 @@ export function TimelineElement({
const renderElementContent = () => { const renderElementContent = () => {
if (element.type === "text") { if (element.type === "text") {
return ( return (
<div className="w-full h-full flex items-center justify-center px-2"> <div className="w-full h-full flex items-center justify-center">
<Type className="h-4 w-4 mr-2 text-purple-400 flex-shrink-0" />
<span className="text-xs text-foreground/80 truncate"> <span className="text-xs text-foreground/80 truncate">
{element.content} {element.content}
</span> </span>
@ -195,6 +194,7 @@ export function TimelineElement({
if (mediaItem.type === "image") { if (mediaItem.type === "image") {
return ( return (
<div className="w-full h-full flex items-center justify-center"> <div className="w-full h-full flex items-center justify-center">
<div className="bg-[#004D52] py-3 w-full h-full">
<img <img
src={mediaItem.url} src={mediaItem.url}
alt={mediaItem.name} alt={mediaItem.name}
@ -202,6 +202,7 @@ export function TimelineElement({
draggable={false} draggable={false}
/> />
</div> </div>
</div>
); );
} }
@ -276,7 +277,7 @@ export function TimelineElement({
onElementMouseDown && onElementMouseDown(e, element) onElementMouseDown && onElementMouseDown(e, element)
} }
> >
<div className="absolute inset-1 flex items-center p-1"> <div className="absolute inset-0 flex items-center h-full">
{renderElementContent()} {renderElementContent()}
</div> </div>

View File

@ -46,6 +46,9 @@ import { TimelineTrackContent } from "./timeline-track";
import type { DragData } from "@/types/timeline"; import type { DragData } from "@/types/timeline";
import { import {
getTrackLabelColor, getTrackLabelColor,
getTrackHeight,
getCumulativeHeightBefore,
getTotalTracksHeight,
TIMELINE_CONSTANTS, TIMELINE_CONSTANTS,
} from "@/lib/timeline-constants"; } from "@/lib/timeline-constants";
@ -242,9 +245,9 @@ export function Timeline() {
track.elements.forEach((element) => { track.elements.forEach((element) => {
const clipLeft = const clipLeft =
element.startTime * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel; element.startTime * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel;
const clipTop = trackIdx * TIMELINE_CONSTANTS.TRACK_HEIGHT; const clipTop = getCumulativeHeightBefore(tracks, trackIdx);
const clipBottom = clipTop + TIMELINE_CONSTANTS.TRACK_HEIGHT; const clipBottom = clipTop + getTrackHeight(track.type);
const clipRight = clipLeft + TIMELINE_CONSTANTS.TRACK_HEIGHT; const clipRight = clipLeft + getTrackHeight(track.type);
if ( if (
bx1 < clipRight && bx1 < clipRight &&
bx2 > clipLeft && bx2 > clipLeft &&
@ -1021,7 +1024,8 @@ export function Timeline() {
{tracks.map((track) => ( {tracks.map((track) => (
<div <div
key={track.id} key={track.id}
className="h-[60px] flex items-center px-3 border-b border-muted/30 bg-background group" className="flex items-center px-3 border-b border-muted/30 bg-background group"
style={{ height: `${getTrackHeight(track.type)}px` }}
> >
<div className="flex items-center flex-1 min-w-0"> <div className="flex items-center flex-1 min-w-0">
<div <div
@ -1048,7 +1052,7 @@ export function Timeline() {
<div <div
className="relative flex-1" className="relative flex-1"
style={{ style={{
height: `${Math.max(200, Math.min(800, tracks.length * TIMELINE_CONSTANTS.TRACK_HEIGHT))}px`, height: `${Math.max(200, Math.min(800, getTotalTracksHeight(tracks)))}px`,
width: `${dynamicTimelineWidth}px`, width: `${dynamicTimelineWidth}px`,
}} }}
onClick={handleTimelineClick} onClick={handleTimelineClick}
@ -1064,8 +1068,8 @@ export function Timeline() {
<div <div
className="absolute left-0 right-0 border-b border-muted/30" className="absolute left-0 right-0 border-b border-muted/30"
style={{ style={{
top: `${index * TIMELINE_CONSTANTS.TRACK_HEIGHT}px`, top: `${getCumulativeHeightBefore(tracks, index)}px`,
height: `${TIMELINE_CONSTANTS.TRACK_HEIGHT}px`, height: `${getTrackHeight(track.type)}px`,
}} }}
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
@ -1098,7 +1102,7 @@ export function Timeline() {
className="absolute top-0 w-0.5 bg-red-500 pointer-events-auto z-50 cursor-col" className="absolute top-0 w-0.5 bg-red-500 pointer-events-auto z-50 cursor-col"
style={{ style={{
left: `${playheadPosition * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel}px`, left: `${playheadPosition * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel}px`,
height: `${tracks.length * TIMELINE_CONSTANTS.TRACK_HEIGHT}px`, height: `${getTotalTracksHeight(tracks)}px`,
}} }}
onMouseDown={handlePlayheadMouseDown} onMouseDown={handlePlayheadMouseDown}
/> />

View File

@ -1,32 +1,30 @@
import type { TrackType } from "@/types/timeline"; import type { TrackType } from "@/types/timeline";
// Track color definitions // Track color definitions
export const TRACK_COLORS = { export const TRACK_COLORS: Record<
TrackType,
{ solid: string; background: string; border: string }
> = {
media: { media: {
solid: "bg-blue-500", solid: "bg-blue-500",
background: "bg-blue-500/20", background: "bg-blue-500/20",
border: "border-blue-500/30", border: "border-blue-500/30",
}, },
text: { text: {
solid: "bg-purple-500", solid: "bg-[#9C4937]",
background: "bg-purple-500/20", background: "bg-[#9C4937]",
border: "border-purple-500/30", border: "border-[#9C4937]/40",
}, },
audio: { audio: {
solid: "bg-green-500", solid: "bg-green-500",
background: "bg-green-500/20", background: "bg-green-500/20",
border: "border-green-500/30", border: "border-green-500/30",
}, },
default: {
solid: "bg-gray-500",
background: "bg-gray-500/20",
border: "border-gray-500/30",
},
} as const; } as const;
// Utility functions // Utility functions
export function getTrackColors(type: TrackType) { export function getTrackColors(type: TrackType) {
return TRACK_COLORS[type] || TRACK_COLORS.default; return TRACK_COLORS[type];
} }
export function getTrackElementClasses(type: TrackType) { export function getTrackElementClasses(type: TrackType) {
@ -38,11 +36,40 @@ export function getTrackLabelColor(type: TrackType) {
return getTrackColors(type).solid; return getTrackColors(type).solid;
} }
// Track height definitions
export const TRACK_HEIGHTS: Record<TrackType, number> = {
media: 65,
text: 25,
audio: 50,
} as const;
// Utility function for track heights
export function getTrackHeight(type: TrackType): number {
return TRACK_HEIGHTS[type];
}
// Calculate cumulative height up to (but not including) a track index
export function getCumulativeHeightBefore(
tracks: Array<{ type: TrackType }>,
trackIndex: number
): number {
return tracks
.slice(0, trackIndex)
.reduce((sum, track) => sum + getTrackHeight(track.type), 0);
}
// Calculate total height of all tracks
export function getTotalTracksHeight(
tracks: Array<{ type: TrackType }>
): number {
return tracks.reduce((sum, track) => sum + getTrackHeight(track.type), 0);
}
// Other timeline constants // Other timeline constants
export const TIMELINE_CONSTANTS = { export const TIMELINE_CONSTANTS = {
ELEMENT_MIN_WIDTH: 80, ELEMENT_MIN_WIDTH: 80,
PIXELS_PER_SECOND: 50, PIXELS_PER_SECOND: 50,
TRACK_HEIGHT: 60, TRACK_HEIGHT: 60, // Default fallback
DEFAULT_TEXT_DURATION: 5, DEFAULT_TEXT_DURATION: 5,
ZOOM_LEVELS: [0.25, 0.5, 1, 1.5, 2, 3, 4], ZOOM_LEVELS: [0.25, 0.5, 1, 1.5, 2, 3, 4],
} as const; } as const;