zoom levels and selection

This commit is contained in:
Siddharth
2025-11-08 22:35:12 -07:00
parent 4cc1ae7a56
commit 43116a1bc3
4 changed files with 26 additions and 32 deletions
+14 -24
View File
@@ -2,12 +2,11 @@ import { cn } from "@/lib/utils";
import { Switch } from "@/components/ui/switch";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Button } from "@/components/ui/button";
import { useMemo, useState } from "react";
import { useState } from "react";
import Colorful from '@uiw/react-color-colorful';
import { hsvaToHex } from '@uiw/color-convert';
import { Trash2 } from "lucide-react";
import type { ZoomDepth } from "./types";
import { ZOOM_DEPTH_SCALES } from "./types";
const WALLPAPER_COUNT = 12;
const WALLPAPER_PATHS = Array.from({ length: WALLPAPER_COUNT }, (_, i) => `/wallpapers/wallpaper${i + 1}.jpg`);
@@ -35,10 +34,12 @@ interface SettingsPanelProps {
onZoomDelete?: (id: string) => void;
}
const ZOOM_DEPTH_OPTIONS: Array<{ depth: ZoomDepth; label: string; description: string }> = [
{ depth: 1, label: "Subtle", description: "Gentle focus" },
{ depth: 2, label: "Medium", description: "Balanced zoom" },
{ depth: 3, label: "Deep", description: "Bold spotlight" },
const ZOOM_DEPTH_OPTIONS: Array<{ depth: ZoomDepth; label: string }> = [
{ depth: 1, label: "1.25×" },
{ depth: 2, label: "1.5×" },
{ depth: 3, label: "1.8×" },
{ depth: 4, label: "2.2×" },
{ depth: 5, label: "3.5×" },
];
export function SettingsPanel({ selected, onWallpaperChange, selectedZoomDepth, onZoomDepthChange, selectedZoomId, onZoomDelete }: SettingsPanelProps) {
@@ -52,26 +53,19 @@ export function SettingsPanel({ selected, onWallpaperChange, selectedZoomDepth,
onZoomDelete(selectedZoomId);
}
};
const scaleLabels = useMemo(() => {
return ZOOM_DEPTH_OPTIONS.reduce<Record<ZoomDepth, string>>((acc, option) => {
const scale = ZOOM_DEPTH_SCALES[option.depth];
acc[option.depth] = `${scale.toFixed(2)}×`;
return acc;
}, { 1: "", 2: "", 3: "" });
}, []);
return (
<div className="flex-[3] min-w-0 bg-card border border-border rounded-xl p-8 flex flex-col shadow-sm">
<div className="mb-6">
<div className="flex items-center justify-between mb-3">
<span className="text-sm font-semibold text-slate-600">Zoom Region</span>
<span className="text-sm font-semibold text-slate-600">Zoom Level</span>
{zoomEnabled && selectedZoomDepth && (
<span className="text-xs uppercase tracking-wide text-slate-400">
Active · {scaleLabels[selectedZoomDepth]}
Active · {ZOOM_DEPTH_OPTIONS.find(o => o.depth === selectedZoomDepth)?.label}
</span>
)}
</div>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
<div className="grid grid-cols-5 gap-2">
{ZOOM_DEPTH_OPTIONS.map((option) => {
const isActive = selectedZoomDepth === option.depth;
return (
@@ -82,25 +76,21 @@ export function SettingsPanel({ selected, onWallpaperChange, selectedZoomDepth,
disabled={!zoomEnabled}
onClick={() => onZoomDepthChange?.(option.depth)}
className={cn(
"h-auto w-full rounded-xl border bg-muted/30 px-4 py-4 text-left shadow-sm transition-all",
"flex flex-col gap-2",
"h-auto w-full rounded-lg border bg-muted/30 px-2 py-2.5 text-center shadow-sm transition-all",
"flex flex-col items-center justify-center gap-0.5",
zoomEnabled ? "opacity-100" : "opacity-60",
isActive
? "border-primary/70 bg-primary/10 text-primary shadow-primary/20"
: "border-border/60 hover:border-primary/40 hover:bg-muted/60"
)}
>
<span className="text-sm font-semibold tracking-tight">{option.label}</span>
<span className="text-xs font-medium text-slate-500">
{scaleLabels[option.depth]}
</span>
<span className="text-xs text-slate-400 leading-snug">{option.description}</span>
<span className="text-xs font-semibold tracking-tight">{option.label}</span>
</Button>
);
})}
</div>
{!zoomEnabled && (
<p className="text-xs text-slate-400 mt-2">Select a zoom region in the timeline to adjust its depth.</p>
<p className="text-xs text-slate-400 mt-2">Select a zoom item in the timeline to adjust its depth.</p>
)}
{zoomEnabled && (
<Button
@@ -28,10 +28,12 @@ export default function Item({ id, span, rowId, children, isSelected = false, on
>
<div style={itemContentStyle}>
<div
className={cn(
"border border-indigo-400/40 rounded-lg shadow-sm w-full overflow-hidden flex items-center justify-center px-3 transition-all duration-150 cursor-grab active:cursor-grabbing group relative",
isSelected ? "bg-indigo-600 ring-2 ring-indigo-300 shadow-xl" : "bg-indigo-500 hover:bg-indigo-600"
)}
className={cn(
"border rounded-lg shadow-sm w-full overflow-hidden flex items-center justify-center px-3 transition-all duration-150 cursor-grab active:cursor-grabbing group relative",
isSelected
? "border-2 border-red-500 bg-indigo-600 shadow-xl"
: "border bg-indigo-500"
)}
style={{ height: 60 }}
onClick={(event) => {
event.stopPropagation();
@@ -375,7 +375,7 @@ export default function TimelineEditor({
}
const defaultDuration = Math.min(
Math.max(timelineScale.defaultItemDurationMs, safeMinDurationMs),
Math.max(3000, safeMinDurationMs),
totalMs,
);
+5 -3
View File
@@ -1,4 +1,4 @@
export type ZoomDepth = 1 | 2 | 3;
export type ZoomDepth = 1 | 2 | 3 | 4 | 5;
export interface ZoomFocus {
cx: number; // normalized horizontal center (0-1)
@@ -16,10 +16,12 @@ export interface ZoomRegion {
export const ZOOM_DEPTH_SCALES: Record<ZoomDepth, number> = {
1: 1.25,
2: 1.5,
3: 2.5,
3: 1.8,
4: 2.2,
5: 3.5,
};
export const DEFAULT_ZOOM_DEPTH: ZoomDepth = 2;
export const DEFAULT_ZOOM_DEPTH: ZoomDepth = 3;
export function clampFocusToDepth(focus: ZoomFocus, _depth: ZoomDepth): ZoomFocus {
return {