import { useRef } from "react"; import { X } from "lucide-react"; import { useStore } from "../store"; import { MG_THEME_LIST, MG_ACCENTS } from "../remotion/mg/templates"; import { MG_BACKGROUNDS, MG_ANIMATIONS, MG_FONTS, MG_FONTS_AR } from "../remotion/mg/catalog.mjs"; import { AccentControls } from "./SceneInspector"; // Bulk styling for a multi-selection of scenes. Mirrors the SceneInspector look controls, // but every control writes the chosen property onto ALL selected scenes at once (one undo step // per change) via applyLook({ : value }, selectedIds). export function BulkStylePanel() { const selectedIds = useStore((s) => s.selectedIds); const clearSelection = useStore((s) => s.clearSelection); const applyLook = useStore((s) => s.applyLook); const pushHistory = useStore((s) => s.pushHistory); const updateMotion = useStore((s) => s.updateMotion); const motion = useStore((s) => s.motion); // stable ref; filter in render (a selector that .filter()s returns a NEW array each call → infinite re-render loop) const lang = useStore((s) => s.lang); // only template scenes are styleable; keep store order const scenes = motion.filter((m) => m.template && selectedIds.includes(m.id)); const n = scenes.length; // live color drag → snapshot once on pointerdown, then live-write to every selected scene (no per-event history) const armed = useRef(false); const accentArm = () => { armed.current = true; }; const accentLive = (c: string) => { if (armed.current) { pushHistory(); armed.current = false; } for (const m of scenes) updateMotion(m.id, { accent: c }); }; // a property is "active" only when every selected scene shares the same value (else: mixed) const shared = (get: (m: (typeof scenes)[number]) => string | undefined, dflt: string): string | null => { if (!n) return dflt; const first = get(scenes[0]) || dflt; return scenes.every((m) => (get(m) || dflt) === first) ? first : null; }; const theme = shared((m) => m.theme, "dark"); const accent = shared((m) => m.accent, MG_ACCENTS[0]); const bg = shared((m) => m.bg, "mesh"); const anim = shared((m) => m.anim, "rise"); const font = shared((m) => m.font, "editorial"); const set = (patch: Record) => applyLook(patch, selectedIds); const eq = (a: string | null, b: string) => a != null && a.toLowerCase() === b.toLowerCase(); return (
Styling {n} scene{n === 1 ? "" : "s"} Bulk
Changes apply to all {n} selected scenes.
{/* theme */}
Theme
{MG_THEME_LIST.map((t) => (
{/* accent — presets + custom color, identical to the inspector */}
Accent {accent == null && · mixed}
set({ accent: c })} onArm={accentArm} onLive={accentLive} />
{/* background */}
Background
{MG_BACKGROUNDS.map((bgo) => (
{/* animation */}
Animation
{MG_ANIMATIONS.map((a) => ( ))}
{/* font */} {(() => { const ar = lang === "ar"; const fonts = ar ? MG_FONTS_AR : MG_FONTS; return (
Font{ar && عربي}
{fonts.map((f) => ( ))}
); })()}
); }