Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion src/components/PresetSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ import { PRESETS } from "@/lib/presets";
import { EditRecipe } from "@/lib/types";
import { cn } from "@/lib/utils";

// 1. Define the shape of our saved custom presets
interface SavedPreset {
id: string;
label: string;
width: number;
height: number;
}

interface Props {
recipe: EditRecipe;
onChange: (patch: Partial<EditRecipe>) => void;
Expand Down Expand Up @@ -145,6 +153,47 @@ export default function PresetSelector({ recipe, onChange }: Props) {
})
)}

{/* Render dynamically saved custom presets */}
{savedPresets.map((preset) => {
const active = recipe.preset === preset.id;
return (
<button
type="button"
key={preset.id}
onClick={() => onChange({ preset: preset.id, customWidth: preset.width, customHeight: preset.height })}
title={`${preset.label} — ${preset.width}×${preset.height}`}
className={cn(
"group relative flex items-center gap-2.5 p-2.5 rounded-lg border text-left transition-all duration-150 cursor-pointer",
"hover:scale-[1.02] active:scale-[0.98]",
active
? "border-film-500 bg-film-50"
: "border-film-200 bg-[var(--surface)] hover:border-film-300 hover:bg-film-50/30"
)}
>
<RatioBox width={preset.width} height={preset.height} active={active} />
<div className="min-w-0 flex-1 pr-4">
<p className={cn(
"text-xs font-heading font-bold leading-tight truncate",
active ? "text-film-700" : "text-[var(--text)]"
)}>
{preset.label}
</p>
<p className="text-[10px] text-film-500 font-medium leading-tight mt-0.5 truncate">
Saved Custom
</p>
</div>
<div
onClick={(e) => handleDeletePreset(preset.id, e)}
className="absolute right-2 top-1/2 -translate-y-1/2 p-1 rounded-md opacity-0 group-hover:opacity-100 hover:bg-red-100 hover:text-red-600 text-[var(--muted)] transition-all"
title="Delete preset"
>
<X size={14} />
</div>
</button>
);
})}

{/* The Custom Trigger Button */}
<button
type="button"
title="Custom — Set your own dimensions"
Expand Down Expand Up @@ -185,6 +234,7 @@ export default function PresetSelector({ recipe, onChange }: Props) {
</button>
</div>

{/* The Custom Configuration Panel */}
{recipe.preset === "custom" && (
<div className="mt-2 flex items-center gap-4 rounded-lg border border-[var(--border)] bg-[var(--surface)] p-4 shadow-sm animate-fade-in">
<div className="flex-1">
Expand Down Expand Up @@ -229,6 +279,14 @@ export default function PresetSelector({ recipe, onChange }: Props) {
onChange={(e) => handleHeightChange(Number(e.target.value))}
className="w-full rounded-md border border-[var(--border)] bg-[var(--bg)] px-3 py-2 text-sm font-heading transition-all focus:outline-none focus:ring-2 focus:ring-film-400"
/>
<button
onClick={handleSavePreset}
disabled={!newPresetName.trim()}
className="flex items-center gap-1.5 px-3 py-1.5 bg-film-600 hover:bg-film-700 disabled:opacity-50 disabled:hover:bg-film-600 text-white text-xs font-bold font-heading uppercase tracking-wide rounded-md transition-colors"
>
<Save size={14} />
Save
</button>
</div>

<div className="hidden h-full flex-col justify-end sm:flex">
Expand All @@ -246,4 +304,4 @@ export default function PresetSelector({ recipe, onChange }: Props) {
)}
</div>
);
}
}
1 change: 1 addition & 0 deletions src/components/VideoEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export default function VideoEditor() {
}
}, [status]);


const isProcessing = status === "loading-engine" || status === "exporting";

const videoSrc = useMemo(
Expand Down
Loading