Skip to content

Commit 1fb58ea

Browse files
committed
refactor: improve UI consistency across panels
1 parent 19aa88b commit 1fb58ea

7 files changed

Lines changed: 218 additions & 186 deletions

File tree

app/components/editor/ExportPanel.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useState, useCallback } from "react";
2-
import { Download, X } from "lucide-react";
2+
import { Download } from "lucide-react";
33
import { Button } from "~/components/ui/button";
44
import { Label } from "~/components/ui/label";
55
import { Switch } from "~/components/ui/switch";
@@ -31,7 +31,6 @@ interface ExportPanelProps {
3131
getPixelsPerSecond: () => number,
3232
options?: RenderOptions,
3333
) => void;
34-
onClose: () => void;
3534
}
3635

3736
type QualityPreset = "web" | "balanced" | "high" | "lossless";
@@ -56,7 +55,6 @@ export function ExportPanel({
5655
getTimelineData,
5756
getPixelsPerSecond,
5857
handleRenderVideo,
59-
onClose,
6058
}: ExportPanelProps) {
6159
const [codec, setCodec] = useState<"h264" | "h265" | "vp9">("h264");
6260
const [quality, setQuality] = useState<QualityPreset>("balanced");
@@ -84,13 +82,8 @@ export function ExportPanel({
8482
return (
8583
<div className="h-full flex flex-col bg-background text-foreground">
8684
{/* Header */}
87-
<div className="flex items-center justify-between px-3 py-2 border-b border-border/50 shrink-0">
85+
<div className="flex items-center px-3 py-2 border-b border-border/50 shrink-0">
8886
<span className="text-xs font-semibold">Export</span>
89-
<button
90-
onClick={onClose}
91-
className="text-muted-foreground hover:text-foreground transition-colors">
92-
<X className="h-3.5 w-3.5" />
93-
</button>
9487
</div>
9588

9689
<div className="flex-1 overflow-y-auto px-3 py-3 space-y-4 text-xs">

app/components/editor/InspectorPanel.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from "react";
2-
import { Volume2, VolumeX, X, MousePointerClick } from "lucide-react";
2+
import { Volume2, VolumeX, MousePointerClick } from "lucide-react";
33
import { Separator } from "~/components/ui/separator";
44
import { type ScrubberState } from "~/components/timeline/types";
55

@@ -8,7 +8,6 @@ interface InspectorPanelProps {
88
getAllScrubbers: () => ScrubberState[];
99
pixelsPerSecond: number;
1010
onUpdate: (s: ScrubberState) => void;
11-
onClose: () => void;
1211
}
1312

1413
const SPEED_OPTIONS = [0.25, 0.5, 1, 1.5, 2, 4];
@@ -21,19 +20,16 @@ const MEDIA_TYPE_LABELS: Record<string, string> = {
2120
groupped_scrubber: "Group",
2221
};
2322

24-
export function InspectorPanel({ selectedScrubberIds, getAllScrubbers, pixelsPerSecond, onUpdate, onClose }: InspectorPanelProps) {
23+
export function InspectorPanel({ selectedScrubberIds, getAllScrubbers, pixelsPerSecond, onUpdate }: InspectorPanelProps) {
2524
const scrubber = selectedScrubberIds.length === 1
2625
? getAllScrubbers().find((s) => s.id === selectedScrubberIds[0]) ?? null
2726
: null;
2827

2928
return (
30-
<div className="h-full flex flex-col bg-background text-foreground">
29+
<div data-no-deselect="true" className="h-full flex flex-col bg-background text-foreground">
3130
{/* Header */}
32-
<div className="flex items-center justify-between px-3 py-2 border-b border-border/50 shrink-0">
31+
<div className="flex items-center px-3 py-2 border-b border-border/50 shrink-0">
3332
<span className="text-xs font-semibold">Inspector</span>
34-
<button onClick={onClose} className="text-muted-foreground hover:text-foreground transition-colors">
35-
<X className="h-3.5 w-3.5" />
36-
</button>
3733
</div>
3834

3935
{!scrubber ? (

app/components/media/TextEditor.tsx

Lines changed: 145 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { Button } from "~/components/ui/button";
44
import { Input } from "~/components/ui/input";
55
import { Label } from "~/components/ui/label";
66
import { Badge } from "~/components/ui/badge";
7-
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
87
import { Separator } from "~/components/ui/separator";
98
import { AlignLeft, AlignCenter, AlignRight, Bold, ChevronDown, Type, Plus } from "lucide-react";
109
import {
@@ -53,161 +52,162 @@ export default function TextEditor() {
5352
};
5453

5554
return (
56-
<div className="h-full flex flex-col bg-background">
57-
<div className="flex-1 overflow-y-auto p-3">
58-
<Card className="border-border/50">
59-
<CardHeader className="pb-3">
60-
<div className="flex items-center gap-2">
61-
<Type className="h-4 w-4 text-primary" />
62-
<CardTitle className="text-sm">Text Properties</CardTitle>
63-
</div>
64-
</CardHeader>
65-
<CardContent className="space-y-4">
66-
{/* Text Content */}
67-
<div className="space-y-2">
68-
<Label className="text-xs font-medium">Content</Label>
69-
<textarea
70-
value={textContent}
71-
onChange={(e) => setTextContent(e.target.value)}
72-
className="w-full h-20 p-3 text-sm bg-muted/50 border border-border rounded-md text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary resize-none"
73-
placeholder="Enter your text..."
55+
<div className="h-full flex flex-col bg-background text-foreground">
56+
{/* Header */}
57+
<div className="flex items-center gap-2 px-3 py-2 border-b border-border/50 shrink-0">
58+
<Type className="h-3.5 w-3.5 text-muted-foreground" />
59+
<span className="text-xs font-semibold">Text Properties</span>
60+
</div>
61+
62+
<div className="flex-1 overflow-y-auto px-3 py-3 space-y-4 text-xs">
63+
{/* Text Content */}
64+
<div className="space-y-1.5">
65+
<div className="text-[10px] font-medium text-muted-foreground uppercase tracking-wider">Content</div>
66+
<textarea
67+
value={textContent}
68+
onChange={(e) => setTextContent(e.target.value)}
69+
className="w-full h-20 p-2 text-xs bg-muted/30 border border-border/50 rounded-md text-foreground placeholder-muted-foreground focus:outline-none focus:border-primary resize-none"
70+
placeholder="Enter your text..."
71+
/>
72+
</div>
73+
74+
<Separator />
75+
76+
{/* Font Size & Family Row */}
77+
<div className="space-y-1.5">
78+
<div className="text-[10px] font-medium text-muted-foreground uppercase tracking-wider">Typography</div>
79+
<div className="grid grid-cols-2 gap-2">
80+
<div className="space-y-1">
81+
<Label className="text-[10px] text-muted-foreground">Size</Label>
82+
<Input
83+
type="number"
84+
min="8"
85+
max="200"
86+
value={fontSize}
87+
onChange={(e) => setFontSize(parseInt(e.target.value) || 48)}
88+
className="h-6 text-xs px-2"
7489
/>
7590
</div>
76-
77-
{/* Font Size & Family Row */}
78-
<div className="grid grid-cols-2 gap-3">
79-
<div className="space-y-2">
80-
<Label className="text-xs font-medium">Size</Label>
81-
<Input
82-
type="number"
83-
min="8"
84-
max="200"
85-
value={fontSize}
86-
onChange={(e) => setFontSize(parseInt(e.target.value) || 48)}
87-
className="h-8 text-sm"
88-
/>
89-
</div>
90-
<div className="space-y-2">
91-
<Label className="text-xs font-medium">Font</Label>
92-
<DropdownMenu>
93-
<DropdownMenuTrigger asChild>
94-
<Button
95-
variant="ghost"
96-
className="w-full h-8 px-2 text-sm bg-muted/50 border border-border rounded-md text-foreground justify-between hover:bg-muted/70"
97-
style={{ fontFamily: fontFamily }}
98-
aria-label="Select font">
99-
<span className="truncate">
100-
{availableFonts.find((f) => f.value === fontFamily)?.label || fontFamily}
101-
</span>
102-
<ChevronDown className="h-3.5 w-3.5 ml-2 opacity-70" />
103-
</Button>
104-
</DropdownMenuTrigger>
105-
<DropdownMenuContent className="rounded-md p-1 min-w-[12rem]">
106-
{availableFonts.map((font) => (
107-
<DropdownMenuItem
108-
key={font.label}
109-
onSelect={() => setFontFamily(font.value)}
110-
className="cursor-pointer"
111-
style={{ fontFamily: font.value }}>
112-
{font.label}
113-
</DropdownMenuItem>
114-
))}
115-
</DropdownMenuContent>
116-
</DropdownMenu>
117-
</div>
91+
<div className="space-y-1">
92+
<Label className="text-[10px] text-muted-foreground">Font</Label>
93+
<DropdownMenu>
94+
<DropdownMenuTrigger asChild>
95+
<Button
96+
variant="ghost"
97+
className="w-full h-6 px-2 text-xs bg-muted/30 border border-border/50 rounded text-foreground justify-between hover:bg-muted/50"
98+
style={{ fontFamily: fontFamily }}
99+
aria-label="Select font">
100+
<span className="truncate text-[10px]">
101+
{availableFonts.find((f) => f.value === fontFamily)?.label || fontFamily}
102+
</span>
103+
<ChevronDown className="h-3 w-3 ml-1 opacity-60 shrink-0" />
104+
</Button>
105+
</DropdownMenuTrigger>
106+
<DropdownMenuContent className="rounded-md p-1 min-w-[10rem]">
107+
{availableFonts.map((font) => (
108+
<DropdownMenuItem
109+
key={font.label}
110+
onSelect={() => setFontFamily(font.value)}
111+
className="cursor-pointer text-xs"
112+
style={{ fontFamily: font.value }}>
113+
{font.label}
114+
</DropdownMenuItem>
115+
))}
116+
</DropdownMenuContent>
117+
</DropdownMenu>
118118
</div>
119+
</div>
120+
</div>
119121

120-
<Separator className="my-4" />
121-
122-
{/* Style Controls */}
123-
<div className="space-y-3">
124-
<Label className="text-xs font-medium">Style</Label>
125-
126-
{/* Text Alignment */}
127-
<div className="space-y-2">
128-
<Label className="text-xs text-muted-foreground">Alignment</Label>
129-
<div className="flex rounded-md border border-border overflow-hidden">
130-
{(
131-
[
132-
{ value: "left", icon: AlignLeft, label: "Left" },
133-
{ value: "center", icon: AlignCenter, label: "Center" },
134-
{ value: "right", icon: AlignRight, label: "Right" },
135-
] as const
136-
).map(({ value, icon: Icon, label }) => (
137-
<Button
138-
key={value}
139-
variant={textAlign === value ? "default" : "ghost"}
140-
size="sm"
141-
onClick={() => setTextAlign(value)}
142-
className="flex-1 h-8 rounded-none border-0"
143-
title={label}>
144-
<Icon className="h-3.5 w-3.5" />
145-
</Button>
146-
))}
147-
</div>
148-
</div>
122+
<Separator />
123+
124+
{/* Style Controls */}
125+
<div className="space-y-2">
126+
<div className="text-[10px] font-medium text-muted-foreground uppercase tracking-wider">Style</div>
127+
128+
<div className="space-y-1">
129+
<div className="text-[10px] text-muted-foreground">Alignment</div>
130+
<div className="flex rounded border border-border/50 overflow-hidden">
131+
{(
132+
[
133+
{ value: "left", icon: AlignLeft, label: "Left" },
134+
{ value: "center", icon: AlignCenter, label: "Center" },
135+
{ value: "right", icon: AlignRight, label: "Right" },
136+
] as const
137+
).map(({ value, icon: Icon, label }) => (
138+
<Button
139+
key={value}
140+
variant={textAlign === value ? "default" : "ghost"}
141+
size="sm"
142+
onClick={() => setTextAlign(value)}
143+
className="flex-1 h-7 rounded-none border-0"
144+
title={label}>
145+
<Icon className="h-3 w-3" />
146+
</Button>
147+
))}
148+
</div>
149+
</div>
149150

150-
{/* Font Weight & Color */}
151-
<div className="grid grid-cols-2 gap-3">
152-
<div className="space-y-2">
153-
<Label className="text-xs text-muted-foreground">Weight</Label>
154-
<div className="flex rounded-md border border-border overflow-hidden">
155-
{(["normal", "bold"] as const).map((weight) => (
156-
<Button
157-
key={weight}
158-
variant={fontWeight === weight ? "default" : "ghost"}
159-
size="sm"
160-
onClick={() => setFontWeight(weight)}
161-
className="flex-1 h-8 rounded-none border-0 text-xs"
162-
title={weight}>
163-
{weight === "normal" ? "Normal" : <Bold className="h-3.5 w-3.5" />}
164-
</Button>
165-
))}
166-
</div>
167-
</div>
168-
169-
<div className="space-y-2">
170-
<Label className="text-xs text-muted-foreground">Color</Label>
171-
<div className="flex items-center gap-2">
172-
<input
173-
type="color"
174-
value={color}
175-
onChange={(e) => setColor(e.target.value)}
176-
className="w-full h-8 bg-muted/50 border border-border rounded-md cursor-pointer"
177-
/>
178-
<Badge variant="outline" className="text-xs font-mono">
179-
{color.toUpperCase()}
180-
</Badge>
181-
</div>
182-
</div>
151+
<div className="grid grid-cols-2 gap-2">
152+
<div className="space-y-1">
153+
<div className="text-[10px] text-muted-foreground">Weight</div>
154+
<div className="flex rounded border border-border/50 overflow-hidden">
155+
{(["normal", "bold"] as const).map((weight) => (
156+
<Button
157+
key={weight}
158+
variant={fontWeight === weight ? "default" : "ghost"}
159+
size="sm"
160+
onClick={() => setFontWeight(weight)}
161+
className="flex-1 h-7 rounded-none border-0 text-[10px]"
162+
title={weight}>
163+
{weight === "normal" ? "Normal" : <Bold className="h-3 w-3" />}
164+
</Button>
165+
))}
183166
</div>
184167
</div>
185168

186-
<Separator className="my-4" />
187-
188-
{/* Preview */}
189-
<div className="space-y-2">
190-
<Label className="text-xs font-medium">Preview</Label>
191-
<div
192-
className="w-full h-20 bg-muted/30 border border-border rounded-md flex items-center justify-center p-3"
193-
style={{
194-
textAlign: textAlign,
195-
fontSize: `${Math.min(fontSize * 0.3, 18)}px`,
196-
fontFamily: fontFamily,
197-
fontWeight: fontWeight,
198-
color: color,
199-
}}>
200-
{textContent || "Sample text"}
169+
<div className="space-y-1">
170+
<div className="text-[10px] text-muted-foreground">Color</div>
171+
<div className="flex items-center gap-1.5">
172+
<input
173+
type="color"
174+
value={color}
175+
onChange={(e) => setColor(e.target.value)}
176+
className="flex-1 h-7 bg-muted/30 border border-border/50 rounded cursor-pointer"
177+
/>
178+
<Badge variant="outline" className="text-[9px] font-mono px-1 shrink-0">
179+
{color.toUpperCase()}
180+
</Badge>
201181
</div>
202182
</div>
183+
</div>
184+
</div>
185+
186+
<Separator />
187+
188+
{/* Preview */}
189+
<div className="space-y-1.5">
190+
<div className="text-[10px] font-medium text-muted-foreground uppercase tracking-wider">Preview</div>
191+
<div
192+
className="w-full h-16 bg-muted/30 border border-border/50 rounded flex items-center justify-center p-2 overflow-hidden"
193+
style={{
194+
textAlign: textAlign,
195+
fontSize: `${Math.min(fontSize * 0.3, 16)}px`,
196+
fontFamily: fontFamily,
197+
fontWeight: fontWeight,
198+
color: color,
199+
}}>
200+
{textContent || "Sample text"}
201+
</div>
202+
</div>
203+
</div>
203204

204-
{/* Add Button */}
205-
<Button onClick={handleAddText} disabled={!textContent.trim()} className="w-full h-9" size="sm">
206-
<Plus className="h-3.5 w-3.5 mr-2" />
207-
Add Text to Timeline
208-
</Button>
209-
</CardContent>
210-
</Card>
205+
{/* Add Button */}
206+
<div className="px-3 py-3 border-t border-border/50 shrink-0">
207+
<Button onClick={handleAddText} disabled={!textContent.trim()} className="w-full h-8 text-xs" size="sm">
208+
<Plus className="h-3.5 w-3.5 mr-1.5" />
209+
Add Text to Timeline
210+
</Button>
211211
</div>
212212
</div>
213213
);

0 commit comments

Comments
 (0)