1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js"; 2import { d as SOUTHPARK_TRUMPET_SVG, e as SOUTHPARK_PIANO_SVG, f as SOUTHPARK_DRUMS_SVG, h as SOUTHPARK_CELLO_SVG, i as SOUTHPARK_GUITAR_SVG, j as DEFAULT_SOUTHPARK_COSTUMES, B as BASE_ANIMALS, S as SouthparkCharacter, g as getInstrumentDef } from "./SouthparkInstruments-C-w5kAUh.js"; 3import { g as getAnimalVariant, b as getVariantFilterSvg } from "./SouthparkVariants-DYvo6xjJ.js"; 4import { SouthparkOrchestraVisualizer } from "./SouthparkOrchestraVisualizer-C3Z_hmqt.js"; 5import { b as getInstrumentName, I as INSTRUMENT_CATEGORIES, l as GM_INSTRUMENTS } from "./utils-CEVb7nN1.js"; 6import { d as defaultBackgroundConfig, a as defaultBackgroundPresets } from "./StagePuppetEngine-CmAn6HAX.js"; 7import { SouthparkStageDesigner } from "./SouthparkStageDesigner-fV_8Ua7K.js"; 8import { SouthparkActionsStudio } from "./SouthparkActionsStudio-BAkc1Yf-.js"; 9import { cM as RefreshCw, ck as Layers, cd as Save, dh as SlidersVertical, cg as Activity, dP as Users, bg as Music, g9 as Shirt, bo as Monitor, bp as CirclePlay, bi as Sparkles, b_ as Pause, bV as Play, bz as EyeOff, ds as ZoomOut, dt as ZoomIn, bf as Search, d0 as Maximize2, f0 as PenLine, bA as Eye, c4 as RotateCcw, b2 as Upload, aW as X } from "./vendor-D0P4XgNj.js"; 10import "./IkMath-D6fi1ppd.js"; 11import "./vendor-framer-B-4FZk1Z.js"; 12import "./synth-Fr-K52Ox.js"; 13import "./vendor-react-three-BCww3P1x.js"; 14import "./wasm-synth-adapter-DpNGIIyM.js"; 15import "./vendor-spessasynth-DH8n6o_V.js"; 16import "./vendor-audio-D-wTggbx.js"; 17import "./SouthparkStageBanner-CUiAAGWi.js"; 18import "./index-w5lXls15.js"; 19import "./vendor-i18n-BC7ZMXWQ.js"; 20import "./vendor-gsap-50Lx19ak.js"; 21const StandaloneSouthparkViewer = ({ trackId }) => { 22 const [track, setTrack] = reactExports.useState(null); 23 const [pianoRollData, setPianoRollData] = reactExports.useState([]); 24 const [currentTime, setCurrentTime] = reactExports.useState(0); 25 const [isPlaying, setIsPlaying] = reactExports.useState(false); 26 const [loading, setLoading] = reactExports.useState(true); 27 const audioRef = reactExports.useRef(null); 28 reactExports.useEffect(() => { 29 let isCurrent = true; 30 setLoading(true); 31 const load = async () => { 32 try { 33 const res = await fetch(`/api/tracks/${trackId}`); 34 if (!res.ok) throw new Error("Track not found"); 35 const trackData = await res.json(); 36 if (!isCurrent) return; 37 setTrack(trackData); 38 if (trackData.pianoRollPath) { 39 const prRes = await fetch(trackData.pianoRollPath); 40 const prData = await prRes.json(); 41 if (!isCurrent) return; 42 const processedInstruments2 = trackData.instruments || []; 43 const musicalInstruments = processedInstruments2.filter((inst) => inst.midiChannel !== -1); 44 const mappedData = prData.map((note) => { 45 let mappedIdx = -1; 46 if (note.trackIdx !== void 0) { 47 const targetIdx = note.trackIdx; 48 if (targetIdx >= 0 && targetIdx < musicalInstruments.length) { 49 const matchedInst = musicalInstruments[targetIdx]; 50 mappedIdx = processedInstruments2.findIndex((inst) => inst.trackId === matchedInst.trackId); 51 } 52 } 53 if (mappedIdx !== -1) { 54 return { 55 ...note, 56 trackIdx: mappedIdx !== -1 ? mappedIdx : note.trackIdx 57 }; 58 } 59 return note; 60 }); 61 mappedData.sort((a, b) => a.t - b.t); 62 setPianoRollData(mappedData); 63 } 64 } catch (err) { 65 console.error(err); 66 } finally { 67 if (isCurrent) setLoading(false); 68 } 69 }; 70 load(); 71 return () => { 72 isCurrent = false; 73 }; 74 }, [trackId]); 75 const { processedInstruments, uniqueNames } = reactExports.useMemo(() => { 76 if (!track) return { processedInstruments: [], uniqueNames: [] }; 77 let sourceInstruments = track.instruments || []; 78 if (sourceInstruments.length === 0 && pianoRollData.length > 0) { 79 const trackMap = /* @__PURE__ */ new Map(); 80 pianoRollData.forEach((note) => { 81 const trackIdx = note.trackIdx !== void 0 ? note.trackIdx : note.i; 82 if (!trackMap.has(trackIdx)) { 83 trackMap.set(trackIdx, { 84 trackId: trackIdx, 85 name: typeof note.i === "string" ? note.i : `Track ${trackIdx}`, 86 program: note.p || 0, 87 midiChannel: note.isDrum ? 9 : 0, 88 notesCount: 1, 89 stemPath: null 90 }); 91 } 92 }); 93 sourceInstruments = Array.from(trackMap.values()); 94 } 95 const processed = sourceInstruments.map((inst, idx) => { 96 let name = inst.name; 97 if (name !== "Informational Track") { 98 name = getInstrumentName(inst.program, inst.midiChannel); 99 } 100 return { ...inst, displayName: String(name), originalName: inst.name, index: i
100dx }; 101 }); 102 const nameCounts = {}; 103 const uNames = processed.map((inst, idx) => { 104 const baseName = inst.displayName || getInstrumentName(inst.program, inst.midiChannel) || `Track ${idx + 1}`; 105 nameCounts[baseName] = (nameCounts[baseName] || 0) + 1; 106 return nameCounts[baseName] > 1 ? `${baseName} ${nameCounts[baseName]}` : baseName; 107 }); 108 return { processedInstruments: processed, uniqueNames: uNames }; 109 }, [track, pianoRollData]); 110 if (loading) { 111 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-white p-4", children: "Loading standalone viewer..." }); 112 } 113 if (!track) { 114 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-white p-4", children: [ 115 "Failed to load track ", 116 trackId 117 ] }); 118 } 119 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full h-full relative bg-black flex flex-col", children: [ 120 track.opusPath && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-4 right-4 z-50 bg-zinc-900 p-2 rounded-lg border border-zinc-700 flex items-center gap-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 121 "audio", 122 { 123 ref: audioRef, 124 src: track.opusPath, 125 controls: true, 126 onTimeUpdate: (e) => setCurrentTime(e.currentTarget.currentTime), 127 onPlay: () => setIsPlaying(true), 128 onPause: () => setIsPlaying(false), 129 onEnded: () => setIsPlaying(false), 130 className: "h-8 w-48" 131 } 132 ) }), 133 /* @__PURE__ */ jsxRuntimeExports.jsx( 134 SouthparkOrchestraVisualizer, 135 { 136 track, 137 currentTime, 138 isPlaying, 139 pianoRollData, 140 processedInstruments, 141 uniqueNames, 142 soloedIndex: null, 143 onSolo: () => { 144 }, 145 mutedIndices: /* @__PURE__ */ new Set(), 146 onMute: () => { 147 }, 148 onPanChange: () => { 149 } 150 } 151 ) 152 ] }); 153}; 154const POSE_INSTRUMENTS_SVG = { 155 guitar: SOUTHPARK_GUITAR_SVG, 156 bowing: SOUTHPARK_CELLO_SVG, 157 drumming: SOUTHPARK_DRUMS_SVG, 158 sitting: SOUTHPARK_PIANO_SVG, 159 blowing: SOUTHPARK_TRUMPET_SVG, 160 standard: "" 161}; 162const POSE_INSTRUMENTS_IMG = { 163 guitar: "/assets/visualizers/southpark/instruments/inst_24.webp", 164 bowing: "/assets/visualizers/southpark/instruments/inst_42.webp", 165 violin: "/assets/visualizers/southpark/instruments/inst_40.webp", 166 drumming: "/assets/visualizers/southpark/instruments/inst_drums.webp", 167 sitting: "/assets/visualizers/southpark/instruments/inst_0.webp", 168 blowing: "/assets/visualizers/southpark/instruments/inst_56.webp", 169 flute: "/assets/visualizers/southpark/instruments/inst_73.webp", 170 congas: "/assets/visualizers/southpark/instruments/perc_congas.webp", 171 percussion_handheld: "/assets/visualizers/southpark/instruments/perc_tambourine.webp", 172 clapping: "", 173 standard: "" 174}; 175const defaultStageConfig = { 176 rows: 4, 177 spacingX: 80, 178 spacingY: 60, 179 baseScale: 1, 180 rowScaleMultiplier: 0.9, 181 yOffset: 0, 182 arcFactor: 1 183}; 184const ACTION_POSES = [ 185 { id: "standard", name: "Static / Neutral", holdStyle: "standard", desc: "Neutral standing pose with relaxed limb angles", badgeColor: "bg-zinc-800 text-zinc-300 border-zinc-700" }, 186 { id: "guitar", name: "Guitar", holdStyle: "guitar", desc: "Left arm fret neck hold, right arm dynamic strumming", badgeColor: "bg-amber-500/10 text-amber-400 border-amber-500/20" }, 187 { id: "drumming", name: "Drums", holdStyle: "drumming", desc: "Dual paw raised hover with rhythmic downbeat stroke", badgeColor: "bg-rose-500/10 text-rose-400 border-rose-500/20" }, 188 { id: "bowing", name: "Cello / Bass", holdStyle: "bowing", desc: "Seated cello with floor endpin and lateral bow stroke", badgeColor: "bg-blue-500/10 text-blue-400 border-blue-500/20" }, 189 { id: "violin", name: "Violin / Viola", holdStyle: "violin", desc: "Under-chin shoulder rest with horizontal bow across chest", badgeColor: "bg-purple-500/10 text-purple-400 border-purple-500/20" }, 190 { id: "sitting", name: "Piano / Seated", holdStyle: "sitting", desc: "Seated playing posture with wide keyboard key span", badgeColor: "bg-emerald-500/10 text-emerald-400 border-emerald-500/20" }, 191 { id: "blowing", name: "Wind / Brass", holdStyle: "blowing", desc: "Dual paws centered beneath mouth holding mouthpiece", badgeColor: "bg-cyan-500/10 text-cyan-400 border-cyan-500/20" }, 192 { id: "flute", name: "Flute / Piccolo", holdStyle: "flute", desc: "Transverse horizontal tube extending to player right", badgeColor: "bg-teal-500/10 text-teal-400 border-teal-500/20" }, 193 { id: "congas", name: "Congas / Hand Drums", holdStyle: "congas", desc: "Standing dual-paw rhythmic
193slaps on barrel drumheads", badgeColor: "bg-orange-500/10 text-orange-400 border-orange-500/20" }, 194 { id: "percussion_handheld", name: "Auxiliary Percussion", holdStyle: "percussion_handheld", desc: "Handheld tambourine, cowbell, triangle or shaker rhythm", badgeColor: "bg-yellow-500/10 text-yellow-400 border-yellow-500/20" }, 195 { id: "clapping", name: "Applause / Clapping", holdStyle: "clapping", desc: "Enthusiastic rhythmic bare paw clapping at chest height", badgeColor: "bg-pink-500/10 text-pink-400 border-pink-500/20" } 196]; 197function SouthparkStudio() { 198 const [activeTab, setActiveTab] = reactExports.useState("actions_studio"); 199 const [previewMidiId, setPreviewMidiId] = reactExports.useState(""); 200 const [iframeUrl, setIframeUrl] = reactExports.useState(""); 201 const [isLoading, setIsLoading] = reactExports.useState(true); 202 const [isSaving, setIsSaving] = reactExports.useState(false); 203 const [error, setError] = reactExports.useState(null); 204 const [successMsg, setSuccessMsg] = reactExports.useState(null); 205 const [animatedAnimals, setAnimatedAnimals] = reactExports.useState({}); 206 const [previewVariants, setPreviewVariants] = reactExports.useState({}); 207 const [globalVariantIndex, setGlobalVariantIndex] = reactExports.useState(null); 208 const [isGlobalAnimating, setIsGlobalAnimating] = reactExports.useState(true); 209 const [globalVelocity, setGlobalVelocity] = reactExports.useState(100); 210 const [pulseIntervalMs, setPulseIntervalMs] = reactExports.useState(500); 211 const [selectedAnimalFilter, setSelectedAnimalFilter] = reactExports.useState("all"); 212 const [selectedPoseFilter, setSelectedPoseFilter] = reactExports.useState("all"); 213 const [currentVelocityPulse, setCurrentVelocityPulse] = reactExports.useState(100); 214 const [showInstruments, setShowInstruments] = reactExports.useState(true); 215 const [showSkeletonOverlay, setShowSkeletonOverlay] = reactExports.useState(false); 216 const [skeletonZoom, setSkeletonZoom] = reactExports.useState(1); 217 const [skeletonGridSize, setSkeletonGridSize] = reactExports.useState("compact"); 218 const [skeletonAnimalBase, setSkeletonAnimalBase] = reactExports.useState("fox"); 219 const [skeletonDisplayMode, setSkeletonDisplayMode] = reactExports.useState("pure"); 220 const [inspectingPose, setInspectingPose] = reactExports.useState(null); 221 const [modalZoom, setModalZoom] = reactExports.useState(2.5); 222 const [modalPan, setModalPan] = reactExports.useState({ x: 0, y: 0 }); 223 const [modalDisplayMode, setModalDisplayMode] = reactExports.useState("pure"); 224 const [modalShowInstrument, setModalShowInstrument] = reactExports.useState(true); 225 const [isModalDragging, setIsModalDragging] = reactExports.useState(false); 226 const [dragStart, setDragStart] = reactExports.useState({ x: 0, y: 0 }); 227 const [manualOverrideLimbAngles, setManualOverrideLimbAngles] = reactExports.useState(false); 228 const [manualLeftShoulder, setManualLeftShoulder] = reactExports.useState(-45); 229 const [manualLeftElbow, setManualLeftElbow] = reactExports.useState(-60); 230 const [manualRightShoulder, setManualRightShoulder] = reactExports.useState(45); 231 const [manualRightElbow, setManualRightElbow] = reactExports.useState(60); 232 const [inspectorAnimalId, setInspectorAnimalId] = reactExports.useState("fox"); 233 const [actionPosesCols, setActionPosesCols] = reactExports.useState("compact"); 234 const [actionPosesZoom, setActionPosesZoom] = reactExports.useState(1); 235 const getDynamicViewBox = (zoom, pan = { x: 0, y: 0 }) => { 236 const baseW = 200; 237 const baseH = 300; 238 if (zoom <= 1.05 && pan.x === 0 && pan.y === 0) { 239 return "0 0 200 300"; 240 } 241 const t = Math.min(1, Math.max(0, (zoom - 1) / 1.5)); 242 const targetCenterY = 150 + t * 18; 243 const focusX = 100 - pan.x; 244 const focusY = targetCenterY - pan.y; 245 const vbW = baseW / zoom; 246 const vbH = baseH / zoom; 247 const minX = focusX - vbW / 2; 248 const minY = focusY - vbH / 2; 249 return `${minX} ${minY} ${vbW} ${vbH}`; 250 }; 251 reactExports.useEffect(() => { 252 if (!isGlobalAnimating) { 253 setCurrentVelocityPulse(0); 254 return; 255 } 256 const interval = setInterval(() => { 257 setCurrentVelocityPulse((v) => v === 0 ? globalVelocity : 0); 258 }, pulseIntervalMs); 259 return () => clearInterval(interval); 260 }, [isGlobalAnimating, globalVelocity, pulseIntervalMs]); 261 const [charactersConfig, setCharactersConfig] = reactExports.useState({}); 262 const [instrumentsConfig, setInstrumentsConfig] = reactExports.useState({}); 263 const [costumesConfig, setCostumesConfig] = reactExports.useState({}); 264 const [stageConfig, setStageConfig] = reactExports.useState(defaultStageConfig); 265 const [actionsConfig, setActionsConfig] = reactExports.useState({}); 266 const [instPreviewCharacter, setInstPreviewCharacter] = reactExports.useState(false); 267 const [instPreviewAnimal, setInstPreviewAnimal] = reactExports.useState("fox"); 268 const [instPreviewAnimating, setInstPreviewAnimating] = reactExports.useState(true); 269 const [openTransformCards, setOpenTransformCards] = reactExports.useState({});
270 const [spBackgroundConfig, setSpBackgroundConfig] = reactExports.useState(defaultBackgroundConfig); 271 const [spBackgroundPresets, setSpBackgroundPresets] = reactExports.useState([]); 272 const [sharedBackgroundConfig, setSharedBackgroundConfig] = reactExports.useState(defaultBackgroundConfig); 273 const [sharedBackgroundPresets, setSharedBackgroundPresets] = reactExports.useState(defaultBackgroundPresets); 274 const [customBackgrounds, setCustomBackgrounds] = reactExports.useState([]); 275 const [uploadedCostumesList, setUploadedCostumesList] = reactExports.useState([]); 276 const fileInputRef = reactExports.useRef(null); 277 const [selectedCostumeCategory, setSelectedCostumeCategory] = reactExports.useState("all"); 278 const [selectedCostumeId, setSelectedCostumeId] = reactExports.useState("stan_beanie"); 279 const [fittingAnimalId, setFittingAnimalId] = reactExports.useState("fox"); 280 const [fittingPose, setFittingPose] = reactExports.useState("standard"); 281 const [fittingAnimating, setFittingAnimating] = reactExports.useState(true); 282 const [costumeSearch, setCostumeSearch] = reactExports.useState(""); 283 const allCostumesMap = reactExports.useMemo(() => { 284 const map = { ...DEFAULT_SOUTHPARK_COSTUMES }; 285 Object.entries(costumesConfig || {}).forEach(([id, def]) => { 286 map[id] = { 287 ...map[id] || {}, 288 ...def 289 }; 290 }); 291 uploadedCostumesList.forEach((url) => { 292 const filename = url.split("/").pop() || ""; 293 const id = filename.replace(/\.[^/.]+$/, ""); 294 if (!map[id]) { 295 let type = "hat"; 296 if (id.includes("glasses") || id.includes("sunglasses") || id.includes("eye")) type = "glasses"; 297 else if (id.includes("tie") || id.includes("scarf") || id.includes("chain") || id.includes("bandana")) type = "neck"; 298 else if (id.includes("vest") || id.includes("suit") || id.includes("shirt") || id.includes("parka") || id.includes("jacket") || id.includes("cape")) type = "body"; 299 map[id] = { 300 id, 301 name: id.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" "), 302 url, 303 type, 304 offsetX: 0, 305 offsetY: type === "hat" ? -36 : type === "glasses" ? 6 : type === "neck" ? 12 : 2, 306 scale: 1, 307 rotation: 0, 308 anchorTo: type === "hat" || type === "glasses" ? "head" : "body" 309 }; 310 } 311 }); 312 return map; 313 }, [costumesConfig, uploadedCostumesList]); 314 const activeCostumeDef = reactExports.useMemo(() => { 315 return allCostumesMap[selectedCostumeId] || allCostumesMap.stan_beanie || Object.values(allCostumesMap)[0]; 316 }, [allCostumesMap, selectedCostumeId]); 317 const updateActiveCostume = (key, value) => { 318 if (!activeCostumeDef) return; 319 const cid = activeCostumeDef.id; 320 setCostumesConfig((prev) => ({ 321 ...prev, 322 [cid]: { 323 ...activeCostumeDef, 324 [key]: value 325 } 326 })); 327 }; 328 const resetActiveCostume = () => { 329 if (!activeCostumeDef) return; 330 const cid = activeCostumeDef.id; 331 const defaultDef = DEFAULT_SOUTHPARK_COSTUMES[cid]; 332 setCostumesConfig((prev) => { 333 const copy = { ...prev }; 334 if (defaultDef) { 335 copy[cid] = { ...defaultDef }; 336 } else { 337 delete copy[cid]; 338 } 339 return copy; 340 }); 341 }; 342 const [instSearch, setInstSearch] = reactExports.useState(""); 343 const [instCatFilter, setInstCatFilter] = reactExports.useState("all"); 344 const getCategoryName = (idx) => { 345 if (idx === 128) return "Drums"; 346 const cat = INSTRUMENT_CATEGORIES.find((c) => idx >= c.start && idx <= c.end); 347 return cat ? cat.name : "Other"; 348 }; 349 reactExports.useEffect(() => { 350 fetchConfigs(); 351 fetchUploadedCostumes(); 352 }, []); 353 const fetchConfigs = async () => { 354 setIsLoading(true); 355 setError(null); 356 try { 357 const [ 358 charsRes, 359 instsRes, 360 costumesRes, 361 stageRes, 362 spBgRes, 363 spPresetsRes, 364 sharedBgRes, 365 sharedPresetsRes, 366 bgsRes, 367 actionsRes 368 ] = await Promise.all([ 369 fetch("/api/admin/settings/southpark_characters_config", { headers: { "Authorization": "Bearer " + localStorage.getItem("admin_token") } }), 370 fetch("/api/admin/settings/southpark_instruments_config", { headers: { "Authorization": "Bearer " + localStorage.getItem("admin_token") } }), 371 fetch("/api/admin/settings/southpark_costumes_config", { headers: { "Authorization": "Bearer " + localStorage.getItem("admin_token") } }), 372 fetch("/api/admin/settings/southpark_stage_config", { headers: { "Authorization": "Bearer " + localStorage.getItem("admin_token") } }), 373 fetch("/api/admin/settings/southpark_background_config", { headers: { "Authorization": "Bearer " + localStorage.getItem("admin_token") } }), 374 fetch("/api/admin/settings/southpark_background_presets", { headers: { "Authorization": "Bearer " + localStorage.getItem("admin_token") } }), 375 fetch("/api/admin/settings/stage_background_config", { headers: { "Authorization": "Bearer " + localStorage.getItem("admin_token") } }), 376 fetch("/api/admin/settings/stage_background_presets", { headers: { "Authorization": "Bearer " + localStorage.getItem("admin_token") } }), 377 fetch("/api/admin/backgrounds", { headers: { "Authorization": "Bearer " + localStorage.getItem("admin_token") } }), 378 fetch("/api/admin/settings/southpark_actions_config", { headers: { "Authorization": "Bearer " + localStorage.getItem("admin_token") } }) 379 ]); 380 const charsData = await charsRes.json(); 381 const instsData = await instsRes.json(); 382 const costumesData = await costumesRes.json(); 383 const stageData = await stageRes.json(); 384 const spBgData = spBgRes.ok ? await spBgRes.json() : null; 385 const spPresetsData = spPresetsRes.ok ? await spPresetsRes.json() : null; 386 const sharedBgData = sharedBgRes.ok ? await sharedBgRes.json() : null; 387 const sharedPresetsData = sharedPresetsRes.ok ? await sharedPresetsRes.json() : null; 388 const bgsData = bgsRes.ok ? await bgsRes.json() : []; 389 const actionsData = actionsRes.ok ? await actionsRes.json() : null; 390 if (charsData == null ? void 0 : charsData.value) setCharactersConfig(charsData.value); 391 if (instsData == null ? void 0 : instsData.value) setInstrumentsConfig(instsData.value); 392 if (costumesData == null ? void 0 : costumesData.value) setCostumesConfig(costumesData.value); 393 if (stageData == null ? void 0 : stageData.value) setStageConfig({ ...defaultStageConfig, ...stageData.value }); 394 if (spBgData == null ? void 0 : spBgData.value) setSpBackgroun
394dConfig(spBgData.value); 395 if ((spPresetsData == null ? void 0 : spPresetsData.value) && Array.isArray(spPresetsData.value)) setSpBackgroundPresets(spPresetsData.value); 396 if (sharedBgData == null ? void 0 : sharedBgData.value) setSharedBackgroundConfig(sharedBgData.value); 397 if ((sharedPresetsData == null ? void 0 : sharedPresetsData.value) && Array.isArray(sharedPresetsData.value)) setSharedBackgroundPresets(sharedPresetsData.value); 398 if (Array.isArray(bgsData)) setCustomBackgrounds(bgsData); 399 if (actionsData == null ? void 0 : actionsData.value) setActionsConfig(actionsData.value); 400 } catch (err) { 401 console.error("Failed to load configs", err); 402 setError("Failed to load configurations."); 403 } finally { 404 setIsLoading(false); 405 } 406 }; 407 const fetchUploadedCostumes = async () => { 408 try { 409 const res = await fetch("/api/admin/southpark-costumes", { headers: { "Authorization": "Bearer " + localStorage.getItem("admin_token") } }); 410 const data = await res.json(); 411 if (data.costumes) setUploadedCostumesList(data.costumes); 412 } catch (err) { 413 console.error("Failed to load costumes list", err); 414 } 415 }; 416 const saveConfigs = async () => { 417 setIsSaving(true); 418 setError(null); 419 setSuccessMsg(null); 420 try { 421 await Promise.all([ 422 fetch("/api/admin/settings/southpark_characters_config", { 423 method: "POST", 424 headers: { "Content-Type": "application/json", "Authorization": "Bearer " + localStorage.getItem("admin_token") }, 425 body: JSON.stringify({ value: charactersConfig }) 426 }), 427 fetch("/api/admin/settings/southpark_instruments_config", { 428 method: "POST", 429 headers: { "Content-Type": "application/json", "Authorization": "Bearer " + localStorage.getItem("admin_token") }, 430 body: JSON.stringify({ value: instrumentsConfig }) 431 }), 432 fetch("/api/admin/settings/southpark_costumes_config", { 433 method: "POST", 434 headers: { "Content-Type": "application/json", "Authorization": "Bearer " + localStorage.getItem("admin_token") }, 435 body: JSON.stringify({ value: costumesConfig }) 436 }), 437 fetch("/api/admin/settings/southpark_stage_config", { 438 method: "POST", 439 headers: { "Content-Type": "application/json", "Authorization": "Bearer " + localStorage.getItem("admin_token") }, 440 body: JSON.stringify({ value: stageConfig }) 441 }), 442 fetch("/api/admin/settings/southpark_background_config", { 443 method: "POST", 444 headers: { "Content-Type": "application/json", "Authorization": "Bearer " + localStorage.getItem("admin_token") }, 445 body: JSON.stringify({ value: spBackgroundConfig }) 446 }), 447 fetch("/api/admin/settings/southpark_background_presets", { 448 method: "POST", 449 headers: { "Content-Type": "application/json", "Authorization": "Bearer " + localStorage.getItem("admin_token") }, 450 body: JSON.stringify({ value: spBackgroundPresets }) 451 }), 452 fetch("/api/admin/settings/southpark_actions_config", { 453 method: "POST", 454 headers: { "Content-Type": "application/json", "Authorization": "Bearer " + localStorage.getItem("admin_token") }, 455 body: JSON.stringify({ value: actionsConfig }) 456 }) 457 ]); 458 setSuccessMsg("Configuration saved successfully."); 459 setTimeout(() => setSuccessMsg(null), 3e3); 460 } catch (err) { 461 console.error("Failed to save configs", err); 462 setError("Failed to save configurations."); 463 } finally { 464 setIsSaving(false); 465 } 466 }; 467 const handleCostumeUpload = async (e) => { 468 var _a; 469 const file = (_a = e.target.files) == null ? void 0 : _a[0]; 470 if (!file) return; 471 const formData = new FormData(); 472 formData.append("costume", file); 473 try { 474 const res = await fetch("/api/admin/upload-southpark-costume", { 475 method: "POST", 476 headers: { "Authorization": "Bearer " + localStorage.getItem("admin_token") }, 477 body: formData 478 }); 479 const data = await res.json(); 480 if (data.url) { 481 setUploadedCostumesList((prev) => [...prev, data.url]); 482 setSuccessMsg("Costume uploaded!"); 483 setTimeout(() => setSuccessMsg(null), 3e3); 484 } else { 485 setError("Upload failed."); 486 } 487 } catch (err) { 488 setError("Upload failed."); 489 } 490 }; 491 const updateCharConfig = (animalId, key, value) => { 492 setCharactersConfig((prev) => ({ 493 ...prev, 494 [animalId]: { 495 ...prev[animalId] || { animalId, scale: 1, headX: 0, headY: 0, armLeftX: 0, armLeftY: 0, armRightX: 0, armRightY: 0 }, 496 [key]: value 497 } 498 })); 499 }; 500 const filteredAnimals = Object.values(BASE_ANIMALS).filter( 501 (a) => selectedAnimalFilter === "all" || a.id === selectedAnimalFilter 502 ); 503 const filteredPoses = ACTION_POSES.filter( 504 (p) => selectedPoseFilter === "all" || p.id === selectedPoseFilter 505 ); 506 if (isLoading) { 507 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-8 text-zinc-400 flex items-center gap-2", children: [
508 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin" }), 509 " Loading South Park Studio..." 510 ] }); 511 } 512 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-[calc(100vh-64px)] bg-zinc-950 text-zinc-100 overflow-hidden", children: [ 513 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-none p-4 border-b border-zinc-800 bg-zinc-900/50 flex items-center justify-between", children: [ 514 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 515 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-2 bg-indigo-500/20 text-indigo-400 rounded-lg", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { size: 20 }) }), 516 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 517 /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-xl font-bold", children: "South Park Studio" }), 518 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-400", children: "Manage vector animal characters, action poses, costumes, and stage placement" }) 519 ] }) 520 ] }),
521 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [ 522 error && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-red-400", children: error }), 523 successMsg && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-emerald-400", children: successMsg }), 524 /* @__PURE__ */ jsxRuntimeExports.jsxs( 525 "button", 526 { 527 onClick: fetchConfigs, 528 className: "px-4 py-2 text-sm font-medium text-zinc-300 hover:text-white bg-zinc-800 hover:bg-zinc-700 rounded-lg flex items-center gap-2 transition-colors", 529 children: [ 530 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 16 }), 531 " Revert" 532 ] 533 } 534 ), 535 /* @__PURE__ */ jsxRuntimeExports.jsxs( 536 "button", 537 { 538 onClick: saveConfigs, 539 disabled: isSaving, 540 className: "px-4 py-2 text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed rounded-lg flex items-center gap-2 transition-colors shadow-lg shadow-indigo-900/20", 541 children: [
542 isSaving ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 16, className: "animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { size: 16 }), 543 "Save All Changes" 544 ] 545 } 546 ) 547 ] }) 548 ] }), 549 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-1 overflow-hidden", children: [ 550 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-64 border-r border-zinc-800 bg-zinc-900/30 flex flex-col p-4 gap-2 shrink-0", children: [ 551 /* @__PURE__ */ jsxRuntimeExports.jsxs( 552 "button", 553 { 554 onClick: () => setActiveTab("actions_studio"), 555 className: `flex items-center gap-3 px-4 py-3 rounded-lg text-sm font-medium transition-colors ${activeTab === "actions_studio" ? "bg-indigo-500/20 text-indigo-400 font-semibold border border-indigo-500/30" : "text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800/50"}`, 556 children: [ 557 /* @__PURE__ */ jsxRuntimeExports.jsx(SlidersVertical, { size: 18 }), 558 "Actions Studio" 559 ] 560 } 561 ),
562 /* @__PURE__ */ jsxRuntimeExports.jsxs( 563 "button", 564 { 565 onClick: () => setActiveTab("actions"), 566 className: `flex items-center gap-3 px-4 py-3 rounded-lg text-sm font-medium transition-colors ${activeTab === "actions" ? "bg-indigo-500/20 text-indigo-400 font-semibold border border-indigo-500/30" : "text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800/50"}`, 567 children: [ 568 /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { size: 18 }), 569 "Character Action Poses" 570 ] 571 } 572 ), 573 /* @__PURE__ */ jsxRuntimeExports.jsxs( 574 "button", 575 { 576 onClick: () => setActiveTab("characters"), 577 className: `flex items-center gap-3 px-4 py-3 rounded-lg text-sm font-medium transition-colors ${activeTab === "characters" ? "bg-indigo-500/20 text-indigo-400 font-semibold border border-indigo-500/30" : "text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800/50"}`, 578 children: [ 579 /* @__P
579URE__ */ jsxRuntimeExports.jsx(Users, { size: 18 }), 580 "Base Offsets Editor" 581 ] 582 } 583 ), 584 /* @__PURE__ */ jsxRuntimeExports.jsxs( 585 "button", 586 { 587 onClick: () => setActiveTab("instruments"), 588 className: `flex items-center gap-3 px-4 py-3 rounded-lg text-sm font-medium transition-colors ${activeTab === "instruments" ? "bg-indigo-500/20 text-indigo-400 font-semibold border border-indigo-500/30" : "text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800/50"}`, 589 children: [ 590 /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { size: 18 }), 591 "Instruments" 592 ] 593 } 594 ), 595 /* @__PURE__ */ jsxRuntimeExports.jsxs( 596 "button", 597 { 598 onClick: () => setActiveTab("costumes"), 599 className: `flex items-center gap-3 px-4 py-3 rounded-lg text-sm font-medium transition-colors ${activeTab === "costumes" ? "bg-indigo-500/20 text-indigo-400 font-semibold border border-indigo-500/30" : "text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800/50"}`, 600 children: [
601 /* @__PURE__ */ jsxRuntimeExports.jsx(Shirt, { size: 18 }), 602 "Costumes" 603 ] 604 } 605 ), 606 /* @__PURE__ */ jsxRuntimeExports.jsxs( 607 "button", 608 { 609 onClick: () => setActiveTab("stage"), 610 className: `flex items-center gap-3 px-4 py-3 rounded-lg text-sm font-medium transition-colors ${activeTab === "stage" ? "bg-indigo-500/20 text-indigo-400 font-semibold border border-indigo-500/30" : "text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800/50"}`, 611 children: [ 612 /* @__PURE__ */ jsxRuntimeExports.jsx(Monitor, { size: 18 }), 613 "Stage & Layout" 614 ] 615 } 616 ), 617 /* @__PURE__ */ jsxRuntimeExports.jsxs( 618 "button", 619 { 620 onClick: () => setActiveTab("preview"), 621 className: `flex items-center gap-3 px-4 py-3 rounded-lg text-sm font-medium transition-colors ${activeTab === "preview" ? "bg-indigo-500/20 text-indigo-400 font-semibold border border-indigo-500/30" : "text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800/50"}`, 622 children: [
623 /* @__PURE__ */ jsxRuntimeExports.jsx(CirclePlay, { size: 18 }), 624 "Live Preview" 625 ] 626 } 627 ) 628 ] }), 629 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-auto p-6 bg-zinc-950", children: [ 630 activeTab === "actions_studio" && /* @__PURE__ */ jsxRuntimeExports.jsx( 631 SouthparkActionsStudio, 632 { 633 actionsConfig, 634 onChangeActionsConfig: setActionsConfig, 635 instrumentsConfig, 636 charactersConfig, 637 costumesConfig, 638 onSave: saveConfigs, 639 isSaving 640 } 641 ), 642 activeTab === "actions" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-8", children: [ 643 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-800 rounded-2xl p-5 shadow-xl", children: [ 644 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col lg:flex-row lg:items-center justify-between gap-4 border-b border-zinc-800 pb-4 mb-4", children: [
645 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 646 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 647 /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { className: "text-indigo-400", size: 22 }), 648 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold text-white tracking-wide", children: "Character Action Poses" }), 649 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2.5 py-0.5 rounded-full text-xs font-semibold bg-indigo-500/20 text-indigo-300 border border-indigo-500/30", children: "Live Kinematics Matrix" }) 650 ] }), 651 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-400 mt-1", children: "Real-time articulated multi-piece limb rendering with spring physics across all 6 performance hold styles" }) 652 ] }), 653 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
654 /* @__PURE__ */ jsxRuntimeExports.jsxs( 655 "button", 656 { 657 onClick: () => setIsGlobalAnimating(!isGlobalAnimating), 658 className: `px-4 py-2 rounded-lg text-xs font-bold flex items-center gap-2 transition-all ${isGlobalAnimating ? "bg-emerald-500/20 text-emerald-400 border border-emerald-500/40 hover:bg-emerald-500/30" : "bg-zinc-800 text-zinc-400 border border-zinc-700 hover:bg-zinc-700"}`, 659 children: [ 660 isGlobalAnimating ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 14 }), 661 isGlobalAnimating ? "Animation Playing" : "Animation Paused" 662 ] 663 } 664 ), 665 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 bg-zinc-950 px-3 py-1.5 rounded-lg border border-zinc-800", children: [ 666 /* @__PURE__ */ jsxRuntimeExports.jsx(SlidersVertical, { size: 14, className: "text-indigo-400" }),
667 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-400 font-medium", children: "Velocity:" }), 668 /* @__PURE__ */ jsxRuntimeExports.jsx( 669 "input", 670 { 671 type: "range", 672 min: "20", 673 max: "127", 674 value: globalVelocity, 675 onChange: (e) => setGlobalVelocity(parseInt(e.target.value)), 676 className: "w-24 accent-indigo-500 cursor-pointer" 677 } 678 ), 679 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-white font-mono w-6 text-right", children: globalVelocity }) 680 ] }), 681 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 bg-zinc-950 px-3 py-1.5 rounded-lg border border-zinc-800", children: [ 682 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-400 font-medium", children: "Tempo:" }),
683 /* @__PURE__ */ jsxRuntimeExports.jsxs( 684 "select", 685 { 686 className: "bg-transparent text-xs text-white outline-none cursor-pointer", 687 value: pulseIntervalMs, 688 onChange: (e) => setPulseIntervalMs(parseInt(e.target.value)), 689 children: [ 690 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "250", className: "bg-zinc-900", children: "Fast (250ms)" }), 691 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "500", className: "bg-zinc-900", children: "Normal (500ms)" }), 692 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "800", className: "bg-zinc-900", children: "Slow (800ms)" }) 693 ] 694 } 695 ) 696 ] }), 697 /* @__PURE__ */ jsxRuntimeExports.jsxs( 698 "button", 699 { 700 onClick: () => setShowInstruments((prev) => !prev), 701 className: `flex items-center gap-2 px-3 py-1.5 rounded-lg border text-xs font-semibold transition-all ${showInstruments ? "bg-amber-500/20 text-amber-300 border-amber-500/40 hover:bg-amber-500/30 shadow-sm" : "bg-zinc-950 text-zinc-400 border-zinc-800 hover:bg-zinc-800 hover:text-zinc-200"}`, 702 title: "Toggle instrument visibility in character poses", 703 children: [ 704 showInstruments ? /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { size: 14, className: "text-amber-400" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { size: 14, className: "text-zinc-500" }),
705 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ 706 "Instruments: ", 707 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: showInstruments ? "text-amber-300" : "text-zinc-400", children: showInstruments ? "ON" : "OFF" }) 708 ] }) 709 ] 710 } 711 ), 712 /* @__PURE__ */ jsxRuntimeExports.jsxs( 713 "button", 714 { 715 onClick: () => setShowSkeletonOverlay((prev) => !prev), 716 className: `flex items-center gap-2 px-3 py-1.5 rounded-lg border text-xs font-semibold transition-all ${showSkeletonOverlay ? "bg-cyan-500/20 text-cyan-300 border-cyan-500/40 hover:bg-cyan-500/30 shadow-sm" : "bg-zinc-950 text-zinc-400 border-zinc-800 hover:bg-zinc-800 hover:text-zinc-200"}`, 717 title: "Toggle kinematic skeleton wireframe overlay across all characters", 718 children: [ 719 /* @__P
719URE__ */ jsxRuntimeExports.jsx(Activity, { size: 14, className: showSkeletonOverlay ? "text-cyan-400" : "text-zinc-500" }), 720 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ 721 "Skeleton Overlay: ", 722 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: showSkeletonOverlay ? "text-cyan-300" : "text-zinc-400", children: showSkeletonOverlay ? "ON" : "OFF" }) 723 ] }) 724 ] 725 } 726 ) 727 ] }) 728 ] }), 729 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-4 text-xs", children: [ 730 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 731 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 font-medium", children: "Filter Animal:" }), 732 /* @__PURE__ */ jsxRuntimeExports.jsxs( 733 "select", 734 { 735 className: "bg-zinc-950 border border-zinc-800 text-zinc-200 rounded px-2.5 py-1.5", 736 value: selectedAnimalFilter, 737 onChange: (e) => setSelectedAnimalFilter(e.target.value), 738 children: [
739 /* @__PURE__ */ jsxRuntimeExports.jsxs("option", { value: "all", children: [ 740 "All Characters (", 741 Object.keys(BASE_ANIMALS).length, 742 ")" 743 ] }), 744 Object.values(BASE_ANIMALS).map((a) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: a.id, children: a.name }, a.id)) 745 ] 746 } 747 ) 748 ] }), 749 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 750 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 font-medium", children: "Filter Pose:" }), 751 /* @__PURE__ */ jsxRuntimeExports.jsxs( 752 "select", 753 { 754 className: "bg-zinc-950 border border-zinc-800 text-zinc-200 rounded px-2.5 py-1.5", 755 value: selectedPoseFilter, 756 onChange: (e) => setSelectedPoseFilter(e.target.value), 757 children: [
758 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "all", children: "All Action Poses (6)" }), 759 ACTION_POSES.map((p) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: p.id, children: p.name }, p.id)) 760 ] 761 } 762 ) 763 ] }), 764 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 bg-zinc-950 border border-zinc-800 rounded px-1.5 py-1", children: [ 765 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 font-medium px-1", children: "Coat Variant:" }), 766 [ 767 { idx: 0, label: "Lead (1)" }, 768 { idx: 1, label: "Rhythm (2)" }, 769 { idx: 2, label: "Sub-3 (3)" }, 770 { idx: 3, label: "Proc 4+" } 771 ].map((v) => /* @__PURE__ */ jsxRuntimeExports.jsx( 772 "button", 773 { 774 onClick: () => { 775 setGlobalVariantIndex(v.idx); 776 setPreviewVariants({}); 777 }, 778 className: `px-2 py-0.5 rounded text-[11px] font-semibold transition-all ${globalVariantIndex === v.idx || globalVariantIndex === null && v.idx === 0 && Object.keys(previewVariants).length === 0 ? "bg-indigo-600 text-white" : "text-zinc-400 hover:text-zinc-200"}`, 779 children: v.label 780 }, 781 v.idx 782 )) 783 ] }),
784 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 bg-zinc-950 border border-zinc-800 rounded px-1.5 py-1", children: [ 785 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 font-medium px-1", children: "Poses Grid:" }), 786 /* @__PURE__ */ jsxRuntimeExports.jsx( 787 "button", 788 { 789 onClick: () => setActionPosesCols("compact"), 790 className: `px-2 py-0.5 rounded text-[11px] font-semibold transition-all ${actionPosesCols === "compact" ? "bg-zinc-800 text-white" : "text-zinc-400 hover:text-zinc-200"}`, 791 children: "6 Cols" 792 } 793 ), 794 /* @__PURE__ */ jsxRuntimeExports.jsx( 795 "button", 796 { 797 onClick: () => setActionPosesCols("large"), 798 className: `px-2 py-0.5 rounded text-[11px] font-semibold transition-all ${actionPosesCols === "large" ? "bg-zinc-800 text-white" : "text-zinc-400 hover:text-zinc-200"}`, 799 children: "3 Cols" 800 } 801 ) 802 ] }),
803 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 bg-zinc-950 border border-zinc-800 px-2.5 py-1 rounded-lg text-xs font-mono", children: [ 804 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400 text-[11px] font-sans", children: "Zoom:" }), 805 /* @__PURE__ */ jsxRuntimeExports.jsx( 806 "button", 807 { 808 onClick: () => setActionPosesZoom((z) => Math.max(1, parseFloat((z - 0.25).toFixed(2)))), 809 className: "p-1 rounded hover:bg-zinc-800 text-zinc-300 transition-colors", 810 title: "Zoom Out", 811 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ZoomOut, { size: 13 }) 812 } 813 ), 814 /* @__PURE__ */ jsxRuntimeExports.jsx( 815 "input", 816 { 817 type: "range", 818 min: "1.0", 819 max: "4.0", 820 step: "0.25", 821 value: actionPosesZoom, 822 onChange: (e) => setActionPosesZoom(parseFloat(e.target.value)), 823 className: "w-16 accent-indigo-400 h-1.5 bg-zinc-800 rounded-lg cursor-pointer", 824 title: `Zoom: ${Math.round(actionPosesZoom * 100)}%` 825 } 826 ),
827 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-indigo-300 font-bold min-w-[42px] text-right", children: [ 828 Math.round(actionPosesZoom * 100), 829 "%" 830 ] }), 831 /* @__PURE__ */ jsxRuntimeExports.jsx( 832 "button", 833 { 834 onClick: () => setActionPosesZoom((z) => Math.min(4, parseFloat((z + 0.25).toFixed(2)))), 835 className: "p-1 rounded hover:bg-zinc-800 text-zinc-300 transition-colors", 836 title: "Zoom In", 837 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ZoomIn, { size: 13 }) 838 } 839 ), 840 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1 ml-1 border-l border-zinc-800 pl-1.5 font-sans", children: [1, 1.75, 2.5, 3.5].map((preset) => /* @__PURE__ */ jsxRuntimeExports.jsxs( 841 "button", 842 { 843 onClick: () => setActionPosesZoom(preset), 844 className: `px-1.5 py-0.5 rounded text-[10px] font-bold ${actionPosesZoom === preset ? "bg-indigo-500/30 text-indigo-200 border border-indigo-400/50" : "text-zinc-400 hover:text-indigo-300"}`, 845 children: [ 846 preset, 847 "x" 848 ] 849 }, 850 preset 851 )) }) 852 ] }),
853 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ml-auto flex items-center gap-2 text-zinc-400 text-xs", children: [ 854 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "inline-block w-2 h-2 rounded-full bg-emerald-400 animate-ping" }), 855 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-emerald-300", children: "Natural Joint Limits Active: L-Elbow [-170°, 0°] ⢠R-Elbow [0°, +170°]" }) 856 ] }) 857 ] }) 858 ] }), 859 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-8", children: [ 860 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-gradient-to-r from-zinc-900/90 via-cyan-950/30 to-zinc-900/90 border border-cyan-800/50 rounded-2xl p-6 space-y-4 shadow-2xl", children: [ 861 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col xl:flex-row xl:items-center justify-between border-b border-cyan-900/40 pb-4 gap-3", children: [
862 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 863 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-9 h-9 rounded-lg bg-cyan-500/20 text-cyan-400 flex items-center justify-center font-bold text-lg border border-cyan-500/30 shadow-inner", children: "ð¦´" }), 864 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 865 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 866 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-bold text-base text-cyan-200 tracking-wide uppercase", children: "Kinematic Skeleton Frames" }), 867 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 rounded text-[10px] font-bold bg-cyan-500/10 text-cyan-400 border border-cyan-500/30", children: "ANATOMICAL SKELETON ROW" }) 868 ] }), 869 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] text-zinc-400", children: "Pure wireframe skeletons demonstrating 0.0000px joint socket connectivity, real-time 2-bone IK solvers, and natural angle limits" }) 870 ] }) 871 ] }),
872 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [ 873 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 bg-zinc-950/80 px-2.5 py-1 rounded-lg border border-cyan-900/40 text-xs", children: [ 874 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 text-[11px] font-medium", children: "Model:" }), 875 Object.values(BASE_ANIMALS).map((a) => /* @__PURE__ */ jsxRuntimeExports.jsx( 876 "button", 877 { 878 onClick: () => setSkeletonAnimalBase(a.id), 879 className: `px-2 py-0.5 rounded text-[11px] font-bold transition-all ${skeletonAnimalBase === a.id ? "bg-cyan-500 text-black shadow-sm" : "text-zinc-400 hover:text-cyan-300 hover:bg-cyan-950/50"}`, 880 children: a.name 881 }, 882 a.id 883 )) 884 ] }),
885 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 bg-zinc-950/80 p-0.5 rounded-lg border border-cyan-900/40 text-xs", children: [ 886 /* @__PURE__ */ jsxRuntimeExports.jsx( 887 "button", 888 { 889 onClick: () => setSkeletonDisplayMode("pure"), 890 className: `px-2 py-1 rounded text-[11px] font-semibold transition-all ${skeletonDisplayMode === "pure" ? "bg-cyan-950 text-cyan-300 border border-cyan-700/50" : "text-zinc-400 hover:text-zinc-200"}`, 891 title: "Pure Wireframe Skeleton with joint nodes only", 892 children: "Pure Skeleton" 893 } 894 ), 895 /* @__PURE__ */ jsxRuntimeExports.jsx( 896 "button", 897 { 898 onClick: () => setSkeletonDisplayMode("ghost"), 899 className: `px-2 py-1 rounded text-[11px] font-semibold transition-all ${skeletonDisplayMode === "ghost" ? "bg-cyan-950 text-cyan-300 border border-cyan-700/50" : "text-zinc-400 hover:text-zinc-200"}`, 900 title: "Wireframe Skeleton with translucent body silhouette", 901 children: "+ Silhouette" 902 } 903 ),
904 /* @__PURE__ */ jsxRuntimeExports.jsx( 905 "button", 906 { 907 onClick: () => setSkeletonDisplayMode("full"), 908 className: `px-2 py-1 rounded text-[11px] font-semibold transition-all ${skeletonDisplayMode === "full" ? "bg-cyan-950 text-cyan-300 border border-cyan-700/50" : "text-zinc-400 hover:text-zinc-200"}`, 909 title: "Full graphic character with skeleton overlay", 910 children: "+ Full Graphic" 911 } 912 ) 913 ] }), 914 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 bg-zinc-950/80 p-0.5 rounded-lg border border-cyan-900/40 text-xs", children: [ 915 /* @__PURE__ */ jsxRuntimeExports.jsx( 916 "button", 917 { 918 onClick: () => setSkeletonGridSize("compact"), 919 className: `px-2 py-1 rounded text-[11px] font-semibold transition-all ${skeletonGridSize === "compact" ? "bg-cyan-950 text-cyan-300 border border-cyan-700/50" : "text-zinc-400 hover:text-zinc-200"}`, 920 title: "Compact 6-column view", 921 children: "6 Cards" 922 } 923 ),
924 /* @__PURE__ */ jsxRuntimeExports.jsx( 925 "button", 926 { 927 onClick: () => setSkeletonGridSize("large"), 928 className: `px-2 py-1 rounded text-[11px] font-semibold transition-all ${skeletonGridSize === "large" ? "bg-cyan-950 text-cyan-300 border border-cyan-700/50" : "text-zinc-400 hover:text-zinc-200"}`, 929 title: "Large 3-column expanded cards", 930 children: "3 Large" 931 } 932 ), 933 /* @__PURE__ */ jsxRuntimeExports.jsx( 934 "button", 935 { 936 onClick: () => setSkeletonGridSize("jumbo"), 937 className: `px-2 py-1 rounded text-[11px] font-semibold transition-all ${skeletonGridSize === "jumbo" ? "bg-cyan-950 text-cyan-300 border border-cyan-700/50" : "text-zinc-400 hover:text-zinc-200"}`, 938 title: "Jumbo 2-column macro cards", 939 children: "2 Jumbo" 940 } 941 ) 942 ] }),
943 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 bg-zinc-950/80 px-2.5 py-1 rounded-lg border border-cyan-900/40 text-xs font-mono", children: [ 944 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400 text-[11px] font-sans", children: "Zoom:" }), 945 /* @__PURE__ */ jsxRuntimeExports.jsx( 946 "button", 947 { 948 onClick: () => setSkeletonZoom((z) => Math.max(1, parseFloat((z - 0.25).toFixed(2)))), 949 className: "p-1 rounded hover:bg-cyan-950 text-cyan-400 transition-colors", 950 title: "Zoom Out", 951 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ZoomOut, { size: 13 }) 952 } 953 ), 954 /* @__PURE__ */ jsxRuntimeExports.jsx( 955 "input", 956 { 957 type: "range", 958 min: "1.0", 959 max: "4.0", 960 step: "0.25", 961 value: skeletonZoom, 962 onChange: (e) => setSkeletonZoom(parseFloat(e.target.value)), 963 className: "w-16 accent-cyan-400 h-1.5 bg-zinc-800 rounded-lg cursor-pointer", 964 title: `Zoom: ${Math.round(skeletonZoom * 100)}%` 965 } 966 ),
967 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-cyan-300 font-bold min-w-[42px] text-right", children: [ 968 Math.round(skeletonZoom * 100), 969 "%" 970 ] }), 971 /* @__PURE__ */ jsxRuntimeExports.jsx( 972 "button", 973 { 974 onClick: () => setSkeletonZoom((z) => Math.min(4, parseFloat((z + 0.25).toFixed(2)))), 975 className: "p-1 rounded hover:bg-cyan-950 text-cyan-400 transition-colors", 976 title: "Zoom In", 977 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ZoomIn, { size: 13 }) 978 } 979 ), 980 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1 ml-1 border-l border-cyan-900/50 pl-1.5 font-sans", children: [1, 1.75, 2.5, 3.5].map((preset) => /* @__PURE__ */ jsxRuntimeExports.jsxs( 981 "button", 982 { 983 onClick: () => setSkeletonZoom(preset), 984 className: `px-1.5 py-0.5 rounded text-[10px] font-bold ${skeletonZoom === preset ? "bg-cyan-500/30 text-cyan-200 border border-cyan-400/50" : "text-zinc-400 hover:text-cyan-300"}`, 985 children: [ 986 preset, 987 "x" 988 ] 989 }, 990 preset 991 )) }) 992 ] }) 993 ] }) 994 ] }),
995 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center justify-between text-xs text-zinc-400 bg-cyan-950/20 px-3 py-1.5 rounded-lg border border-cyan-900/30", children: [ 996 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 997 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-cyan-300/80 font-mono", children: [ 998 "Left Elbow: ", 999 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-emerald-400", children: "[-170°, 0°]" }), 1000 " ⢠Right Elbow: ", 1001 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-purple-400", children: "[0°, +170°]" }) 1002 ] }), 1003 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-600", children: "|" }), 1004 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400/90 font-mono text-[11px]", children: "All Joint Sockets: 0.0000px displacement (Physically Locked)" }) 1005 ] }),
1006 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[11px] text-cyan-400/90 flex items-center gap-1 font-medium", children: [ 1007 /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { size: 12 }), 1008 " Click any pose card to open the High-Resolution Joint Inspector Modal" 1009 ] }) 1010 ] }), 1011 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `grid gap-4 ${skeletonGridSize === "compact" ? "grid-cols-2 md:grid-cols-3 xl:grid-cols-6" : skeletonGridSize === "large" ? "grid-cols-1 md:grid-cols-2 xl:grid-cols-3" : "grid-cols-1 md:grid-cols-2"}`, children: filteredPoses.map((pose) => { 1012 const isPoseActive = isGlobalAnimating && pose.id !== "standard" && currentVelocityPulse > 0; 1013 const skeletonAnimal = BASE_ANIMALS[skeletonAnimalBase] || filteredAnimals[0] || BASE_ANIMALS.fox; 1014 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 1015 "div", 1016 { 1017 onClick: () => { 1018 setInspectingPose(pose); 1019 setInspectorAnimalId(skeletonAnimalBase); 1020 }, 1021 className: "bg-zinc-950/90 border border-cyan-900/30 rounded-xl p-3.5 flex flex-col items-center gap-3 transition-all hover:border-cyan-400/70 hover:shadow-cyan-950/50 hover:shadow-2xl group cursor-pointer relative", 1022 children: [
1023 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full flex items-center justify-between", children: [ 1024 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-2 py-0.5 rounded text-[11px] font-bold border ${pose.badgeColor}`, children: pose.name }), 1025 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "px-1.5 py-0.5 text-[9px] font-mono rounded bg-cyan-950 text-cyan-400 border border-cyan-800/50 flex items-center gap-1 group-hover:bg-cyan-900 group-hover:text-cyan-200 transition-colors", children: [ 1026 /* @__PURE__ */ jsxRuntimeExports.jsx(Maximize2, { size: 9 }), 1027 " INSPECT" 1028 ] }) 1029 ] }), 1030 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full aspect-square bg-zinc-950 rounded-lg flex items-center justify-center relative overflow-hidden border border-cyan-900/40 shadow-inner group-hover:border-cyan-500/40 transition-colors", children: [
1031 /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { viewBox: getDynamicViewBox(skeletonZoom), className: "w-full h-full overflow-visible drop-shadow-2xl", children: /* @__PURE__ */ jsxRuntimeExports.jsx("g", { transform: "translate(100, 155)", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 1032 SouthparkCharacter, 1033 { 1034 animal: skeletonAnimal, 1035 customConfig: charactersConfig[skeletonAnimal.id], 1036 instrument: { 1037 id: 1, 1038 name: pose.name, 1039 type: "preview", 1040 holdStyle: pose.holdStyle, 1041 group: "Preview", 1042 imgUrl: POSE_INSTRUMENTS_IMG[pose.id] || void 0, 1043 svgContent: void 0 1044 }, 1045 isActive: isPoseActive, 1046 velocity: isPoseActive ? currentVelocityPulse : 0, 1047 physicsMode: "smooth", 1048 showInstrument: false, 1049 skeletonOnly: skeletonDisplayMode !== "full", 1050 ghostSilhouette: skeletonDisplayMode === "ghost", 1051 showSkeleton: true 1052 } 1053 ) }) }),
1054 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-cyan-950/30 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-2.5 py-1 rounded-full bg-cyan-900/90 border border-cyan-400/60 text-cyan-200 text-xs font-bold shadow-lg flex items-center gap-1.5", children: [ 1055 /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { size: 12 }), 1056 " Inspect Joint Sockets" 1057 ] }) }) 1058 ] }), 1059 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full text-[10px] font-mono text-zinc-400 text-center space-y-0.5 bg-zinc-900/60 p-1.5 rounded border border-zinc-800/60 group-hover:border-cyan-800/60 transition-colors", children: [ 1060 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-zinc-300 font-semibold", children: [ 1061 pose.name, 1062 " Rig" 1063 ] }),
1064 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-emerald-400/90 font-bold", children: "Socket Dist: 0.0000px [LOCKED]" }) 1065 ] }) 1066 ] 1067 }, 1068 `skeleton-${pose.id}` 1069 ); 1070 }) }) 1071 ] }), 1072 filteredAnimals.map((animal) => { 1073 const conf = charactersConfig[animal.id] || { 1074 animalId: animal.id, 1075 scale: 1, 1076 headX: 0, 1077 headY: 0, 1078 armLeftX: 0, 1079 armLeftY: 0, 1080 armRightX: 0, 1081 armRightY: 0 1082 }; 1083 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900/60 border border-zinc-800/80 rounded-2xl p-6 space-y-4", children: [ 1084 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between border-b border-zinc-800/60 pb-3", children: [ 1085 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
1086 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-8 h-8 rounded-lg bg-indigo-600/20 text-indigo-400 flex items-center justify-center font-bold text-sm", children: animal.name.charAt(0) }), 1087 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1088 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-base text-white tracking-wide uppercase", children: [ 1089 animal.name, 1090 " - Action Poses" 1091 ] }), 1092 /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[11px] text-zinc-400", children: [ 1093 "Scale: ", 1094 conf.scale, 1095 " ⢠Head Offset: (", 1096 conf.headX, 1097 ", ", 1098 conf.headY, 1099 ") ⢠Sockets: L(", 1100 conf.armLeftX, 1101 ", ", 1102 conf.armLeftY, 1103 ") R(", 1104 conf.armRightX, 1105 ", ", 1106 conf.armRightY, 1107 ")" 1108 ] }) 1109 ] }) 1110 ] }),
1111 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1112 "button", 1113 { 1114 onClick: () => setActiveTab("characters"), 1115 className: "px-3 py-1.5 text-xs text-indigo-300 hover:text-white bg-indigo-950/40 hover:bg-indigo-900/60 border border-indigo-800/40 rounded-lg flex items-center gap-1.5 transition-all", 1116 children: [ 1117 /* @__PURE__ */ jsxRuntimeExports.jsx(PenLine, { size: 13 }), 1118 " Edit Offsets" 1119 ] 1120 } 1121 ) 1122 ] }), 1123 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `grid gap-4 ${actionPosesCols === "compact" ? "grid-cols-2 md:grid-cols-3 xl:grid-cols-6" : "grid-cols-1 md:grid-cols-2 xl:grid-cols-3"}`, children: filteredPoses.map((pose) => { 1124 const isPoseActive = isGlobalAnimating && pose.id !== "standard" && currentVelocityPulse > 0; 1125 const variantIdx = previewVariants[animal.id] ?? (globalVariantIndex ?? 0); 1126 const currentVariant = getAnimalVariant(animal.id, variantIdx, animal.sleeveColor); 1127 const filterId = `actions-filter-${animal.id}-${pose.id}-${variantIdx}`; 1128 const filterSvg = getVariantFilterSvg(filterId, currentVariant); 1129 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 1130 "div", 1131 { 1132 onClick: () => { 1133 setInspectingPose(pose); 1134 setInspectorAnimalId(animal.id); 1135 }, 1136 className: "bg-zinc-950 border border-zinc-800 rounded-xl p-3.5 flex flex-col items-center gap-3 transition-all hover:border-indigo-500/50 hover:shadow-xl group cursor-pointer", 1137 title: `Click to inspect ${animal.name} (${currentVariant.label}) in ${pose.name} pose up close`, 1138 children: [
1139 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full flex items-center justify-between", children: [ 1140 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-2 py-0.5 rounded text-[11px] font-bold border ${pose.badgeColor}`, children: pose.name }), 1141 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [ 1142 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "px-1 py-0.5 text-[8px] font-mono rounded bg-zinc-900 text-zinc-400 border border-zinc-800 flex items-center gap-0.5 group-hover:text-indigo-300 group-hover:border-indigo-700/50 transition-colors", children: [ 1143 /* @__PURE__ */ jsxRuntimeExports.jsx(Maximize2, { size: 8 }), 1144 " INSPECT" 1145 ] }), 1146 isPoseActive ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-2 h-2 rounded-full bg-emerald-400 animate-pulse", title: "Spring Kinematics Active" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-2 h-2 rounded-full bg-zinc-700", title: "Stationary" }) 1147 ] }) 1148 ] }),
1149 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full aspect-square bg-zinc-900/50 rounded-lg flex items-center justify-center relative overflow-hidden border border-zinc-800/50", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: getDynamicViewBox(actionPosesZoom), className: "w-full h-full overflow-visible drop-shadow-2xl", children: [ 1150 filterSvg && /* @__PURE__ */ jsxRuntimeExports.jsx("defs", { dangerouslySetInnerHTML: { __html: filterSvg } }), 1151 /* @__PURE__ */ jsxRuntimeExports.jsx("g", { transform: "translate(100, 155)", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 1152 SouthparkCharacter, 1153 { 1154 animal, 1155 variant: currentVariant, 1156 variantFilterId: filterSvg ? filterId : void 0, 1157 instrument: { 1158 id: 1, 1159 name: pose.name, 1160 type: "preview", 1161 holdStyle: pose.holdStyle, 1162 group: "Preview", 1163 imgUrl: POSE_INSTRUMENTS_IMG[pose.id] || void 0, 1164 svgContent: void 0 1165 }, 1166 isActive: isPoseActive, 1167 velocity: isPoseActive ? currentVelocityPulse : 0, 1168 physicsMode: "smooth", 1169 customConfig: conf, 1170 showInstrument: showInstruments, 1171 showSkeleton: showSkeletonOverlay 1172 } 1173 ) }) 1174 ] }) }),
1175 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] text-zinc-400 text-center leading-tight min-h-[32px] flex items-center justify-center", children: pose.desc }) 1176 ] 1177 }, 1178 pose.id 1179 ); 1180 }) }) 1181 ] }, animal.id); 1182 }) 1183 ] }) 1184 ] }), 1185 activeTab === "characters" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [ 1186 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col md:flex-row md:items-center justify-between gap-4 border-b border-zinc-800 pb-3 mb-4", children: [ 1187 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1188 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold", children: "Base Character Offsets & Duplicate Variants" }), 1189 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-400", children: "Calibrate joint sockets, head anchors, and preview natural coat sub-species & costume variations" }) 1190 ] }),
1191 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [ 1192 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 bg-zinc-900 border border-zinc-800 rounded-lg p-1 text-xs", children: [ 1193 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400 px-2 font-medium", children: "Batch Coat Preview:" }), 1194 [ 1195 { idx: 0, label: "Lead (1)" }, 1196 { idx: 1, label: "Rhythm (2)" }, 1197 { idx: 2, label: "Sub-3 (3)" }, 1198 { idx: 3, label: "Proc 4+" } 1199 ].map((v) => /* @__PURE__ */ jsxRuntimeExports.jsx( 1200 "button", 1201 { 1202 onClick: () => { 1203 setGlobalVariantIndex(v.idx); 1204 setPreviewVariants({}); 1205 }, 1206 className: `px-2.5 py-1 rounded text-xs font-medium transition-all ${globalVariantIndex === v.idx || globalVariantIndex === null && v.idx === 0 && Object.keys(previewVariants).length === 0 ? "bg-indigo-600 text-white shadow-sm" : "text-zinc-400 hover:text-white"}`, 1207 children: v.label 1208 }, 1209 v.idx 1210 )) 1211 ] }),
1212 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1213 "button", 1214 { 1215 onClick: () => setActiveTab("actions"), 1216 className: "px-3.5 py-2 text-xs font-bold text-indigo-300 hover:text-white bg-indigo-950/50 hover:bg-indigo-900/80 border border-indigo-700/50 rounded-lg flex items-center gap-2 transition-all shadow-md", 1217 children: [ 1218 /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { size: 15 }), 1219 " View Full Action Poses Matrix" 1220 ] 1221 } 1222 ) 1223 ] }) 1224 ] }), 1225 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6", children: Object.values(BASE_ANIMALS).map((animal) => { 1226 const conf = charactersConfig[animal.id] || { 1227 animalId: animal.id, 1228 scale: 1, 1229 headX: 0, 1230 headY: 0, 1231 armLeftX: 0, 1232 armLeftY: 0, 1233 armRightX: 0, 1234 armRightY: 0 1235 }; 1236 const testAction = animatedAnimals[animal.id] || ""; 1237 const variantIdx = previewVariants[animal.id] ?? (globalVariantIndex ?? 0); 1238 const currentVariant = getAnimalVariant(animal.id, variantIdx, animal.sleeveColor); 1239 const filterId = `studio-char-filter-${animal.id}-${variantIdx}`; 1240 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-800 rounded-xl p-5 flex flex-col gap-4", children: [
1241 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 p-2.5 bg-zinc-950/70 border border-zinc-800/80 rounded-lg text-xs", children: [ 1242 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 1243 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400 font-medium", children: "Duplicate Coat:" }), 1244 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1 bg-zinc-900 p-0.5 rounded border border-zinc-800", children: [ 1245 { idx: 0, label: "Lead (1)" }, 1246 { idx: 1, label: "Rhythm (2)" }, 1247 { idx: 2, label: "Sub-3 (3)" }, 1248 { idx: 3, label: "Proc 4+" } 1249 ].map((v) => /* @__PURE__ */ jsxRuntimeExports.jsx( 1250 "button", 1251 { 1252 onClick: () => setPreviewVariants((prev) => ({ ...prev, [animal.id]: v.idx })), 1253 className: `px-2 py-0.5 text-[11px] rounded transition-all font-medium ${variantIdx === v.idx ? "bg-indigo-600 text-white shadow-sm" : "text-zinc-400 hover:text-zinc-200"}`, 1254 children: v.label 1255 }, 1256 v.idx 1257 )) }) 1258 ] }),
1259 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 1260 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-semibold text-indigo-300 text-[11px]", children: currentVariant.label }), 1261 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 pl-2 border-l border-zinc-800", children: [ 1262 currentVariant.furColor && /* @__PURE__ */ jsxRuntimeExports.jsx( 1263 "span", 1264 { 1265 className: "w-3.5 h-3.5 rounded-full border border-white/20 shadow-inner inline-block", 1266 style: { backgroundColor: currentVariant.furColor }, 1267 title: `Fur Color: ${currentVariant.furColor}` 1268 } 1269 ), 1270 currentVariant.sleeveColor && /* @__PURE__ */ jsxRuntimeExports.jsx( 1271 "span", 1272 { 1273 className: "w-3.5 h-3.5 rounded-full border border-white/20 shadow-inner inline-block", 1274 style: { backgroundColor: currentVariant.sleeveColor }, 1275 title: `Sleeve Color: ${currentVariant.sleeveColor}` 1276 } 1277 ), 1278 currentVariant.costumeIds && currentVariant.costumeIds.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] px-1.5 py-0.5 rounded bg-amber-500/15 text-amber-300 border border-amber-500/30 font-mono", children: currentVariant.costumeIds.join(", ") }), 1279 currentVariant.scaleMultiplier && currentVariant.scaleMultiplier !== 1 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-zinc-400 font-mono", children: [ 1280 (currentVariant.scaleMultiplier * 100).toFixed(0), 1281 "%" 1282 ] }), 1283 currentVariant.hatTilt !== void 0 && currentVariant.hatTilt !== 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-400 font-mono", children: currentVariant.hatTilt > 0 ? `+${currentVariant.hatTilt}°` : `${currentVariant.hatTilt}°` }) 1284 ] }) 1285 ] }) 1286 ] }),
1287 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col xl:flex-row gap-6", children: [ 1288 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-64 h-64 mx-auto xl:mx-0 bg-zinc-800/50 rounded-lg flex items-center justify-center relative shrink-0 overflow-hidden border border-zinc-700/50", children: (() => { 1289 const filterSvg = getVariantFilterSvg(filterId, currentVariant); 1290 return /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 200 300", className: "w-full h-full overflow-visible drop-shadow-2xl", children: [ 1291 filterSvg && /* @__PURE__ */ jsxRuntimeExports.jsx("defs", { dangerouslySetInnerHTML: { __html: filterSvg } }), 1292 /* @__PURE__ */ jsxRuntimeExports.jsx("g", { transform: `translate(100, 150)`, children: /* @__PURE__ */ jsxRuntimeExports.jsx( 1293 SouthparkCharacter, 1294 { 1295 animal, 1296 variant: currentVariant, 1297 variantFilterId: filterSvg ? filterId : void 0, 1298 instrument: { 1299 id: 1, 1300 name: "Preview", 1301 type: "string", 1302 holdStyle: testAction || "guitar", 1303 group: "Strings", 1304 imgUrl: POSE_INSTRUMENTS_IMG[testAction] || POSE_INSTRUMENTS_IMG["guitar"], 1305 svgContent: void 0 1306 }, 1307 isActive: !!testAction && currentVelocityPulse > 0, 1308 velocity: testAction ? currentVelocityPulse : 0, 1309 physicsMode: "smooth", 1310 customConfig: conf, 1311 showInstrument: showInstruments, 1312 showSkeleton: showSkeletonOverlay 1313 } 1314 ) }) 1315 ] }); 1316 })() }),
1317 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 space-y-3", children: [ 1318 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [ 1319 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-bold text-indigo-400 text-base", children: animal.name }), 1320 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs", children: [ 1321 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 font-medium", children: "Test Animation:" }), 1322 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1323 "select", 1324 { 1325 className: "bg-zinc-950 border border-zinc-700 text-white rounded px-2 py-1", 1326 value: animatedAnimals[animal.id] || "", 1327 onChange: (e) => { 1328 const val = e.target.value; 1329 setAnimatedAnimals((prev) => { 1330 const copy = { ...prev }; 1331 if (val) copy[animal.id] = val; 1332 else delete copy[animal.id]; 1333 return copy; 1334 }); 1335 }, 1336 children: [
1337 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "", children: "Static Pose" }), 1338 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "guitar", children: "Guitar (Strumming)" }), 1339 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "drumming", children: "Drums (Beating)" }), 1340 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "bowing", children: "Strings (Bowing)" }), 1341 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "sitting", children: "Piano (Keys)" }), 1342 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "blowing", children: "Wind / Brass" }), 1343 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "standard", children: "Standard (Rhythm)" }) 1344 ] 1345 } 1346 ) 1347 ] }) 1348 ] }), 1349 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-2 text-xs", children: [
1350 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1351 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-zinc-500 font-medium", children: "Head X, Y" }), 1352 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 mt-1", children: [ 1353 /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "number", step: "1", value: conf.headX, onChange: (e) => updateCharConfig(animal.id, "headX", parseFloat(e.target.value) || 0), className: "w-full bg-zinc-950 border border-zinc-800 rounded px-2 py-1.5 text-white" }), 1354 /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "number", step: "1", value: conf.headY, onChange: (e) => updateCharConfig(animal.id, "headY", parseFloat(e.target.value) || 0), className: "w-full bg-zinc-950 border border-zinc-800 rounded px-2 py-1.5 text-white" }) 1355 ] }) 1356 ] }), 1357 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1358 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-zinc-500 font-medium", children: "Scale" }), 1359 /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "number", step: "0.05", value: conf.scale, onChange: (e) => updateCharConfig(animal.id, "scale", parseFloat(e.target.value) || 1), className: "w-full bg-zinc-950 border border-zinc-800 rounded px-2 py-1.5 mt-1 text-white" }) 1360 ] }), 1361 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "col-span-2 grid grid-cols-2 gap-3 pt-2 border-t border-zinc-800/50", children: [ 1362 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1363 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-zinc-500 font-medium text-[11px] uppercase tracking-wider", children: "Left Shoulder Pivot (X, Y)" }), 1364 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 mt-1", children: [ 1365 /* @__P
1365URE__ */ jsxRuntimeExports.jsx("input", { type: "number", step: "1", value: conf.armLeftX, onChange: (e) => updateCharConfig(animal.id, "armLeftX", parseFloat(e.target.value) || 0), className: "w-full bg-zinc-950 border border-zinc-800 rounded px-2 py-1.5 text-white" }), 1366 /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "number", step: "1", value: conf.armLeftY, onChange: (e) => updateCharConfig(animal.id, "armLeftY", parseFloat(e.target.value) || 0), className: "w-full bg-zinc-950 border border-zinc-800 rounded px-2 py-1.5 text-white" }) 1367 ] }) 1368 ] }), 1369 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1370 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-zinc-500 font-medium text-[11px] uppercase tracking-wider", children: "Right Shoulder Pivot (X, Y)" }), 1371 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 mt-1", children: [ 1372 /* @__P
1372URE__ */ jsxRuntimeExports.jsx("input", { type: "number", step: "1", value: conf.armRightX, onChange: (e) => updateCharConfig(animal.id, "armRightX", parseFloat(e.target.value) || 0), className: "w-full bg-zinc-950 border border-zinc-800 rounded px-2 py-1.5 text-white" }), 1373 /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "number", step: "1", value: conf.armRightY, onChange: (e) => updateCharConfig(animal.id, "armRightY", parseFloat(e.target.value) || 0), className: "w-full bg-zinc-950 border border-zinc-800 rounded px-2 py-1.5 text-white" }) 1374 ] }) 1375 ] }) 1376 ] }) 1377 ] }) 1378 ] }) 1379 ] }), 1380 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-t border-zinc-800/60 pt-3", children: [ 1381 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[11px] text-zinc-400 font-medium mb-2 uppercase tracking-wider flex items-center justify-between", children: [
1382 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ 1383 animal.name, 1384 " (", 1385 currentVariant.label, 1386 ") - Action Poses Preview" 1387 ] }), 1388 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 lowercase text-[10px]", children: "updates in real-time" }) 1389 ] }), 1390 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-6 gap-2", children: ACTION_POSES.map((p) => { 1391 const miniFilterId = `${filterId}-mini-${p.id}`; 1392 const miniFilterSvg = getVariantFilterSvg(miniFilterId, currentVariant); 1393 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-950/80 border border-zinc-800 rounded-lg p-1.5 flex flex-col items-center gap-1", children: [ 1394 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] text-zinc-400 font-medium truncate w-full text-center", children: p.name.split(" ")[0] }),
1395 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full aspect-square flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 200 300", className: "w-full h-full overflow-visible", children: [ 1396 miniFilterSvg && /* @__PURE__ */ jsxRuntimeExports.jsx("defs", { dangerouslySetInnerHTML: { __html: miniFilterSvg } }), 1397 /* @__PURE__ */ jsxRuntimeExports.jsx("g", { transform: "translate(100, 150) scale(0.95)", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 1398 SouthparkCharacter, 1399 { 1400 animal, 1401 variant: currentVariant, 1402 variantFilterId: miniFilterSvg ? miniFilterId : void 0, 1403 instrument: { 1404 id: 1, 1405 name: p.name, 1406 type: "preview", 1407 holdStyle: p.holdStyle, 1408 group: "Preview", 1409 imgUrl: POSE_INSTRUMENTS_IMG[p.id] || void 0, 1410 svgContent: void 0 1411 }, 1412 isActive: false, 1413 velocity: 0, 1414 physicsMode: "smooth", 1415 customConfig: conf, 1416 showInstrument: showInstruments 1417 } 1418 ) }) 1419 ] }) }) 1420 ] }, p.id); 1421 }) }) 1422 ] }) 1423 ] }, animal.id); 1424 }) }) 1425 ] }), 1426 activeTab === "stage" && /* @__PURE__ */ jsxRuntimeExports.jsx( 1427 SouthparkStageDesigner, 1428 { 1429 stageConfig, 1430 setStageConfig, 1431 spBackgroundConfig, 1432 setSpBackgroundConfig, 1433 spBackgroundPresets, 1434 setSpBackgroundPresets, 1435 sharedBackgroundConfig, 1436 sharedBackgroundPresets, 1437 customBackgrounds, 1438 setCustomBackgrounds, 1439 charactersConfig, 1440 costumesConfig: allCostumesMap, 1441 instrumentsConfig 1442 } 1443 ), 1444 activeTab === "instruments" && (() => { 1445 const allInstruments = GM_INSTRUMENTS.concat("Drum Kit"); 1446 const filteredInstruments = allInstruments.map((name, idx) => ({ name, idx })).filter(({ name, idx }) => { 1447 const cat = getCategoryName(idx); 1448 if (instCatFilter !== "all" && cat !== instCatFilter) return false; 1449 if (instSearch.trim()) { 1450 const query = instSearch.toLowerCase().trim(); 1451 const matchesName = name.toLowerCase().includes(query); 1452 const matchesId = idx.toString().includes(query); 1453 const matchesCat = cat.toLowerCase().includes(query); 1454 if (!matchesName && !matchesId && !matchesCat) return false; 1455 } 1456 return true; 1457 }); 1458 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
1459 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-b border-zinc-800 pb-3", children: [ 1460 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold", children: "Instrument to Animal Bindings" }), 1461 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-400 mt-1", children: "Override the default animal assignment for any MIDI instrument (0-127). 128 is Drum Kit." }) 1462 ] }), 1463 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col lg:flex-row items-start lg:items-center justify-between gap-3 bg-zinc-900/60 border border-zinc-800/80 p-3.5 rounded-xl shadow-md", children: [ 1464 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-3 w-full lg:w-auto", children: [ 1465 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex-1 sm:w-56", children: [ 1466 /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { size: 14, className: "absolute left-3 top-1/2 -translate-y-1/2 text-zinc-400 pointer-events-none" }),
1467 /* @__PURE__ */ jsxRuntimeExports.jsx( 1468 "input", 1469 { 1470 type: "text", 1471 placeholder: "Search by name, ID or category...", 1472 value: instSearch, 1473 onChange: (e) => setInstSearch(e.target.value), 1474 className: "w-full bg-zinc-950 border border-zinc-700/80 rounded-lg pl-8 pr-3 py-1.5 text-xs text-white placeholder-zinc-500 focus:outline-none focus:border-indigo-500 transition-colors" 1475 } 1476 ) 1477 ] }), 1478 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 1479 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-400 font-medium", children: "Category:" }), 1480 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1481 "select", 1482 { 1483 value: instCatFilter, 1484 onChange: (e) => setInstCatFilter(e.target.value), 1485 className: "bg-zinc-950 border border-zinc-700/80 rounded-lg px-2.5 py-1.5 text-xs text-white focus:outline-none focus:border-indigo-500", 1486 children: [
1487 /* @__PURE__ */ jsxRuntimeExports.jsxs("option", { value: "all", className: "bg-zinc-900 text-white", children: [ 1488 "All Categories (", 1489 allInstruments.length, 1490 ")" 1491 ] }), 1492 INSTRUMENT_CATEGORIES.map((cat) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: cat.name, className: "bg-zinc-900 text-white", children: cat.name }, cat.name)), 1493 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "Drums", className: "bg-zinc-900 text-white", children: "Drums (1)" }) 1494 ] 1495 } 1496 ) 1497 ] }), 1498 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 border-l border-zinc-800 pl-3", children: [ 1499 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1500 "button", 1501 { 1502 type: "button", 1503 onClick: () => setInstPreviewCharacter(!instPreviewCharacter), 1504 className: `flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-xs font-medium border transition-colors ${instPreviewCharacter ? "bg-indigo-600 text-white border-indigo-500 shadow-sm" : "bg-zinc-950 text-zinc-300 border-zinc-700/80 hover:bg-zinc-800"}`, 1505 children: [ 1506 instPreviewCharacter ? /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { size: 13, className: "text-indigo-200" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { size: 13, className: "text-zinc-400" }),
1507 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: instPreviewCharacter ? "Character Preview: ON" : "Preview Character" }) 1508 ] 1509 } 1510 ), 1511 instPreviewCharacter && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 1512 /* @__PURE__ */ jsxRuntimeExports.jsx( 1513 "select", 1514 { 1515 value: instPreviewAnimal, 1516 onChange: (e) => setInstPreviewAnimal(e.target.value), 1517 className: "bg-zinc-950 border border-zinc-700/80 rounded-lg px-2.5 py-1.5 text-xs text-white focus:outline-none focus:border-indigo-500", 1518 title: "Select preview animal", 1519 children: Object.values(BASE_ANIMALS).map((a) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: a.id, className: "bg-zinc-900 text-zinc-100", children: a.name }, a.id)) 1520 } 1521 ),
1522 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1523 "button", 1524 { 1525 type: "button", 1526 onClick: () => setInstPreviewAnimating(!instPreviewAnimating), 1527 className: `flex items-center gap-1 px-2 py-1.5 rounded-lg text-xs font-medium border transition-colors ${instPreviewAnimating ? "bg-emerald-600/20 text-emerald-400 border-emerald-500/30" : "bg-zinc-950 text-zinc-400 border-zinc-800 hover:text-zinc-200"}`, 1528 title: "Toggle performance animation", 1529 children: [ 1530 instPreviewAnimating ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 12 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 12 }), 1531 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: instPreviewAnimating ? "Playing" : "Paused" }) 1532 ] 1533 } 1534 ) 1535 ] }) 1536 ] }) 1537 ] }),
1538 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-zinc-400 flex items-center gap-2", children: [ 1539 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ 1540 "Showing ", 1541 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-white", children: filteredInstruments.length }), 1542 " of ", 1543 allInstruments.length 1544 ] }), 1545 Object.keys(instrumentsConfig).length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "px-2 py-0.5 rounded-full text-[10px] font-semibold bg-indigo-500/20 text-indigo-300 border border-indigo-500/30", children: [ 1546 Object.keys(instrumentsConfig).length, 1547 " overrides active" 1548 ] }) 1549 ] }) 1550 ] }), 1551 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4", children: [ 1552 filteredInstruments.map(({ name: instName, idx }) => { 1553 var _a; 1554 const conf = instrumentsConfig[idx]; 1555 const hasOverride = !!(conf == null ? void 0 : conf.animalId) || (conf == null ? void 0 : conf.scale) !== void 0 || (conf == null ? void 0 : conf.offsetX) !== void 0 || (conf == null ? void 0 : conf.offsetY) !== void 0 || (conf == null ? void 0 : conf.rotation) !== void 0; 1556 const imgUrl = idx === 128 ? "/assets/visualizers/southpark/instruments/inst_drums.webp" : `/assets/visualizers/southpark/instruments/inst_${idx}.webp`; 1557 const catName = getCategoryName(idx); 1558 const defaultDef = getInstrumentDef(idx === 128 ? -1 : idx); 1559 const effectiveScale = (conf == null ? void 0 : conf.scale) !== void 0 ? conf.scale : defaultDef.scale || 1; 1560 const effectiveOffsetX = (conf == null ? void 0 : conf.offsetX) !== void 0 ? conf.offsetX : defaultDef.offsetX || 0; 1561 const effectiveOffsetY = (conf == null ? void 0 : conf.offsetY) !== void 0 ? conf.offsetY : defaultDef.offsetY || 0; 1562 const effectiveRotation = (conf == null ? void 0 : conf.rotation) !== void 0 ? conf.rotation : defaultDef.rotation || 0; 1563 const effectiveDef = { 1564 ...defaultDef, 1565 scale: effectiveScale, 1566 offsetX: effectiveOffsetX, 1567 offsetY: effectiveOffsetY, 1568 rotation: effectiveRotation 1569 }; 1570 const previewAnimalId = (conf == null ? void 0 : conf.animalId) || instPreviewAnimal; 1571 const previewAnimalDef = BASE_ANIMALS[previewAnimalId] || BASE_ANIMALS.fox; 1572 const isTransformed = (conf == null ? void 0 : conf.scale) !== void 0 || (conf == null ? void 0 : conf.offsetX) !== void 0 || (conf == null ? void 0 : conf.offsetY) !== void 0 || (conf == null ? void 0 : conf.rotation) !== void 0; 1573 const isCardExpanded = !!openTransformCards[idx]; 1574 const jointType = ((_a = defaultDef.jointDef) == null ? void 0 : _a.jointType) || "bare_hands"; 1575 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 1576 "div", 1577 { 1578 className: `p-3.5 rounded-xl border flex flex-col justify-between transition-all hover:border-zinc-700 hover:shadow-lg ${hasOverride ? "bg-indigo-900/15 border-indigo-500/40 shadow-indigo-950/20" : "bg-zinc-900/70 border-zinc-800/80"}`, 1579 children: [
1580 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1581 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-2 mb-2", children: [ 1582 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[11px] font-mono font-semibold px-2 py-0.5 rounded bg-zinc-950 border border-zinc-800 text-zinc-400", children: [ 1583 "#", 1584 idx 1585 ] }), 1586 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-medium px-2 py-0.5 rounded-full bg-zinc-800/80 text-zinc-300 border border-zinc-700/60 truncate max-w-[120px]", children: catName }) 1587 ] }), 1588 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full aspect-[4/3] bg-zinc-950/90 rounded-lg flex items-center justify-center p-2 relative overflow-hidden border border-zinc-800/80 mb-3 group/img", children: [
1589 /* @__PURE__ */ jsxRuntimeExports.jsx( 1590 "div", 1591 { 1592 className: "absolute inset-0 opacity-10 pointer-events-none", 1593 style: { 1594 backgroundImage: "linear-gradient(45deg, #808080 25%, transparent 25%), linear-gradient(-45deg, #808080 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #808080 75%), linear-gradient(-45deg, transparent 75%, #808080 75%)", 1595 backgroundSize: "16px 16px", 1596 backgroundPosition: "0 0, 0 8px, 8px -8px, -8px 0px" 1597 } 1598 } 1599 ), 1600 instPreviewCharacter ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-full flex items-center justify-center relative z-10", children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { viewBox: "-80 -115 160 230", className: "w-full h-full", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 1601 SouthparkCharacter, 1602 { 1603 animal: previewAnimalDef, 1604 instrument: effectiveDef, 1605 isActive: instPreviewAnimating ? currentVelocityPulse > 0 : false, 1606 velocity: currentVelocityPulse || 100, 1607 activePitch: idx === 57 ? 62 : 65, 1608 currentTime: Date.now() / 1e3, 1609 bpm: 120, 1610 physicsMode: "smooth", 1611 costumes: (conf == null ? void 0 : conf.costumeIds) || [], 1612 customCostumesMap: allCostumesMap 1613 } 1614 ) }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx( 1615 "img", 1616 { 1617 src: imgUrl, 1618 alt: instName, 1619 className: "w-full h-full object-contain relative z-10 drop-shadow-xl transition-transform duration-200 group-hover/img:scale-105", 1620 style: { 1621 transform: `rotate(${effectiveRotation}deg) scale(${effectiveScale}) translate(${effectiveOffsetX}px, ${effectiveOffsetY}px)` 1622 }, 1623 loading: "lazy", 1624 onError: (e) => { 1625 e.target.style.opacity = "0.3"; 1626 } 1627 } 1628 ) 1629 ] }),
1630 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-semibold text-sm text-zinc-100 truncate mb-3", title: instName, children: instName }) 1631 ] }), 1632 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1633 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[11px] text-zinc-400 font-medium mb-1 block", children: "Assigned Animal:" }), 1634 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1635 "select", 1636 { 1637 className: "w-full bg-zinc-950 border border-zinc-700/80 rounded-lg px-2.5 py-1.5 text-xs text-white focus:border-indigo-500 focus:outline-none transition-colors", 1638 value: (conf == null ? void 0 : conf.animalId) || "", 1639 onChange: (e) => { 1640 const val = e.target.value; 1641 setInstrumentsConfig((prev) => { 1642 var _a2; 1643 const copy = { ...prev }; 1644 if (val) { 1645 copy[idx] = { 1646 ...copy[idx] || { instrumentId: idx, costumeIds: [] }, 1647 instrumentId: idx, 1648 animalId: val, 1649 costumeIds: ((_a2 = copy[idx]) == null ? void 0 : _a2.costumeIds) || [] 1650 }; 1651 } else { 1652 if (copy[idx]) { 1653 const { animalId, ...rest } = copy[idx]; 1654 copy[idx] = rest; 1655 } 1656 } 1657 return copy; 1658 }); 1659 }, 1660 children: [
1661 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "", className: "bg-zinc-900 text-zinc-400", children: "(Default Fallback)" }), 1662 Object.values(BASE_ANIMALS).map((a) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: a.id, className: "bg-zinc-900 text-zinc-100", children: a.name }, a.id)) 1663 ] 1664 } 1665 ) 1666 ] }), 1667 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 pt-2.5 border-t border-zinc-800/60", children: [ 1668 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-1.5", children: [ 1669 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "text-[11px] text-zinc-400 font-medium flex items-center gap-1", children: [ 1670 /* @__PURE__ */ jsxRuntimeExports.jsx(Shirt, { size: 12, className: "text-indigo-400" }),
1671 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Costumes:" }) 1672 ] }), 1673 (conf == null ? void 0 : conf.costumeIds) && conf.costumeIds.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx( 1674 "button", 1675 { 1676 type: "button", 1677 onClick: () => { 1678 setInstrumentsConfig((prev) => { 1679 const copy = { ...prev }; 1680 if (copy[idx]) { 1681 copy[idx] = { ...copy[idx], costumeIds: [] }; 1682 } 1683 return copy; 1684 }); 1685 }, 1686 className: "text-[10px] text-zinc-500 hover:text-red-400 transition-colors", 1687 children: "Clear all" 1688 } 1689 ) 1690 ] }),
1691 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap gap-1 mb-2 min-h-[22px]", children: [ 1692 ((conf == null ? void 0 : conf.costumeIds) || []).map((cid) => { 1693 const cDef = allCostumesMap[cid]; 1694 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 1695 "span", 1696 { 1697 className: "inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-[10px] font-medium bg-indigo-500/20 text-indigo-300 border border-indigo-500/30", 1698 children: [ 1699 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate max-w-[85px]", children: (cDef == null ? void 0 : cDef.name) || cid }), 1700 /* @__PURE__ */ jsxRuntimeExports.jsx( 1701 "button", 1702 { 1703 type: "button", 1704 onClick: () => { 1705 setInstrumentsConfig((prev) => { 1706 const copy = { ...prev }; 1707 const curr = copy[idx] || { instrumentId: idx, animalId: "", costumeIds: [] }; 1708 copy[idx] = { 1709 ...curr, 1710 costumeIds: curr.costumeIds.filter((id) => id !== cid) 1711 }; 1712 return copy; 1713 }); 1714 }, 1715 className: "hover:text-red-400 font-bold ml-0.5", 1716 children: "Ã" 1717 } 1718 ) 1719 ] 1720 }, 1721 cid 1722 ); 1723 }), 1724 (!(conf == null ? void 0 : conf.costumeIds) || conf.costumeIds.length === 0) && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-500 italic", children: "No costumes assigned" }) 1725 ] }),
1726 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1727 "select", 1728 { 1729 className: "w-full bg-zinc-950 border border-zinc-700/80 rounded-lg px-2.5 py-1.5 text-xs text-zinc-300 focus:border-indigo-500 focus:outline-none transition-colors", 1730 value: "", 1731 onChange: (e) => { 1732 const cid = e.target.value; 1733 if (!cid) return; 1734 setInstrumentsConfig((prev) => { 1735 const copy = { ...prev }; 1736 const curr = copy[idx] || { instrumentId: idx, animalId: "", costumeIds: [] }; 1737 if (!curr.costumeIds.includes(cid)) { 1738 copy[idx] = { 1739 ...curr, 1740 costumeIds: [...curr.costumeIds, cid] 1741 }; 1742 } 1743 return copy; 1744 }); 1745 }, 1746 children: [
1747 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "", className: "bg-zinc-900 text-zinc-400", children: "+ Add Costume..." }), 1748 /* @__PURE__ */ jsxRuntimeExports.jsx("optgroup", { label: "ð© Hats & Headwear", className: "bg-zinc-900 text-indigo-400 font-semibold", children: Object.values(allCostumesMap).filter((c) => c.type === "hat").map((c) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: c.id, className: "bg-zinc-900 text-white font-normal", children: c.name }, c.id)) }), 1749 /* @__PURE__ */ jsxRuntimeExports.jsx("optgroup", { label: "ð¶ï¸ Eyewear & Facial", className: "bg-zinc-900 text-indigo-400 font-semibold", children: Object.values(allCostumesMap).filter((c) => c.type === "glasses").map((c) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: c.id, className: "bg-zinc-900 text-white font-normal", children: c.name }, c.id)) }), 1750 /* @__PURE__ */ jsxRuntimeExports.jsx("optgroup", { label: "ð§£ Neckwear", className: "bg-zinc-900 text-indigo-400 font-semibold", children: Object.values(allCostumesMap).filter((c) => c.type === "neck").map((c) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: c.id, className: "bg-zinc-900 text-white font-normal", children: c.name }, c.id)) }),
1751 /* @__PURE__ */ jsxRuntimeExports.jsx("optgroup", { label: "ð Outfits & Robes", className: "bg-zinc-900 text-indigo-400 font-semibold", children: Object.values(allCostumesMap).filter((c) => c.type === "body").map((c) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: c.id, className: "bg-zinc-900 text-white font-normal", children: c.name }, c.id)) }) 1752 ] 1753 } 1754 ) 1755 ] }), 1756 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 pt-2.5 border-t border-zinc-800/60", children: [ 1757 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-1.5", children: [ 1758 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1759 "button", 1760 { 1761 type: "button", 1762 onClick: () => setOpenTransformCards((prev) => ({ ...prev, [idx]: !prev[idx] })), 1763 className: "flex items-center gap-1.5 text-[11px] font-medium text-zinc-300 hover:text-indigo-400 transition-colors", 1764 children: [
1765 /* @__PURE__ */ jsxRuntimeExports.jsx(SlidersVertical, { size: 12, className: isTransformed ? "text-indigo-400" : "text-zinc-500" }), 1766 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Transform & Tilt" }), 1767 isTransformed && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-indigo-400", title: "Custom transforms active" }) 1768 ] 1769 } 1770 ), 1771 isTransformed && /* @__PURE__ */ jsxRuntimeExports.jsxs( 1772 "button", 1773 { 1774 type: "button", 1775 onClick: () => { 1776 setInstrumentsConfig((prev) => { 1777 const copy = { ...prev }; 1778 if (copy[idx]) { 1779 const { scale, offsetX, offsetY, rotation, ...rest } = copy[idx]; 1780 copy[idx] = rest; 1781 } 1782 return copy; 1783 }); 1784 }, 1785 className: "flex items-center gap-1 text-[10px] text-zinc-500 hover:text-amber-400 transition-colors", 1786 title: "Reset transform to defaults", 1787 children: [
1788 /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 10 }), 1789 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Reset" }) 1790 ] 1791 } 1792 ) 1793 ] }), 1794 isCardExpanded && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2 bg-zinc-950/80 p-2.5 rounded-lg border border-zinc-800 text-[11px] mt-1.5", children: [ 1795 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1796 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center text-zinc-400 mb-1", children: [ 1797 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Scale" }), 1798 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [ 1799 /* @__PURE__ */ jsxRuntimeExports.jsx( 1800 "input", 1801 { 1802 type: "number", 1803 step: "0.05", 1804 min: "0.10", 1805 max: "8.00", 1806 value: effectiveScale, 1807 onChange: (e) => { 1808 const val = parseFloat(e.target.value) || 1; 1809 setInstrumentsConfig((prev) => { 1810 const copy = { ...prev }; 1811 const curr = copy[idx] || { instrumentId: idx, animalId: "", costumeIds: [] }; 1812 copy[idx] = { ...curr, scale: val }; 1813 return copy; 1814 }); 1815 }, 1816 className: "w-16 bg-zinc-900 border border-zinc-700/80 rounded px-1.5 py-0.5 text-right font-mono text-white text-[11px] focus:outline-none focus:border-indigo-500" 1817 } 1818 ),
1819 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 text-[10px]", children: "x" }) 1820 ] }) 1821 ] }), 1822 /* @__PURE__ */ jsxRuntimeExports.jsx( 1823 "input", 1824 { 1825 type: "range", 1826 min: "0.10", 1827 max: "5.00", 1828 step: "0.05", 1829 value: effectiveScale, 1830 onChange: (e) => { 1831 const val = parseFloat(e.target.value); 1832 setInstrumentsConfig((prev) => { 1833 const copy = { ...prev }; 1834 const curr = copy[idx] || { instrumentId: idx, animalId: "", costumeIds: [] }; 1835 copy[idx] = { ...curr, scale: val }; 1836 return copy; 1837 }); 1838 }, 1839 className: "w-full h-1 bg-zinc-800 rounded-lg appearance-none cursor-pointer accent-indigo-500" 1840 } 1841 ) 1842 ] }),
1843 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1844 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center text-zinc-400 mb-1", children: [ 1845 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Position X" }), 1846 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [ 1847 /* @__PURE__ */ jsxRuntimeExports.jsx( 1848 "input", 1849 { 1850 type: "number", 1851 step: "1", 1852 min: "-300", 1853 max: "300", 1854 value: effectiveOffsetX, 1855 onChange: (e) => { 1856 const val = parseInt(e.target.value, 10) || 0; 1857 setInstrumentsConfig((prev) => { 1858 const copy = { ...prev }; 1859 const curr = copy[idx] || { instrumentId: idx, animalId: "", costumeIds: [] }; 1860 copy[idx] = { ...curr, offsetX: val }; 1861 return copy; 1862 }); 1863 }, 1864 className: "w-16 bg-zinc-900 border border-zinc-700/80 rounded px-1.5 py-0.5 text-right font-mono text-white text-[11px] focus:outline-none focus:border-indigo-500" 1865 } 1866 ),
1867 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 text-[10px]", children: "px" }) 1868 ] }) 1869 ] }), 1870 /* @__PURE__ */ jsxRuntimeExports.jsx( 1871 "input", 1872 { 1873 type: "range", 1874 min: "-300", 1875 max: "300", 1876 step: "1", 1877 value: effectiveOffsetX, 1878 onChange: (e) => { 1879 const val = parseInt(e.target.value, 10); 1880 setInstrumentsConfig((prev) => { 1881 const copy = { ...prev }; 1882 const curr = copy[idx] || { instrumentId: idx, animalId: "", costumeIds: [] }; 1883 copy[idx] = { ...curr, offsetX: val }; 1884 return copy; 1885 }); 1886 }, 1887 className: "w-full h-1 bg-zinc-800 rounded-lg appearance-none cursor-pointer accent-indigo-500" 1888 } 1889 ) 1890 ] }),
1891 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1892 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center text-zinc-400 mb-1", children: [ 1893 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Position Y" }), 1894 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [ 1895 /* @__PURE__ */ jsxRuntimeExports.jsx( 1896 "input", 1897 { 1898 type: "number", 1899 step: "1", 1900 min: "-300", 1901 max: "300", 1902 value: effectiveOffsetY, 1903 onChange: (e) => { 1904 const val = parseInt(e.target.value, 10) || 0; 1905 setInstrumentsConfig((prev) => { 1906 const copy = { ...prev }; 1907 const curr = copy[idx] || { instrumentId: idx, animalId: "", costumeIds: [] }; 1908 copy[idx] = { ...curr, offsetY: val }; 1909 return copy; 1910 }); 1911 }, 1912 className: "w-16 bg-zinc-900 border border-zinc-700/80 rounded px-1.5 py-0.5 text-right font-mono text-white text-[11px] focus:outline-none focus:border-indigo-500" 1913 } 1914 ),
1915 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 text-[10px]", children: "px" }) 1916 ] }) 1917 ] }), 1918 /* @__PURE__ */ jsxRuntimeExports.jsx( 1919 "input", 1920 { 1921 type: "range", 1922 min: "-300", 1923 max: "300", 1924 step: "1", 1925 value: effectiveOffsetY, 1926 onChange: (e) => { 1927 const val = parseInt(e.target.value, 10); 1928 setInstrumentsConfig((prev) => { 1929 const copy = { ...prev }; 1930 const curr = copy[idx] || { instrumentId: idx, animalId: "", costumeIds: [] }; 1931 copy[idx] = { ...curr, offsetY: val }; 1932 return copy; 1933 }); 1934 }, 1935 className: "w-full h-1 bg-zinc-800 rounded-lg appearance-none cursor-pointer accent-indigo-500" 1936 } 1937 ) 1938 ] }),
1939 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1940 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center text-zinc-400 mb-1", children: [ 1941 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Tilt (Angle)" }), 1942 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [ 1943 /* @__PURE__ */ jsxRuntimeExports.jsx( 1944 "input", 1945 { 1946 type: "number", 1947 step: "1", 1948 min: "-360", 1949 max: "360", 1950 value: effectiveRotation, 1951 onChange: (e) => { 1952 const val = parseInt(e.target.value, 10) || 0; 1953 setInstrumentsConfig((prev) => { 1954 const copy = { ...prev }; 1955 const curr = copy[idx] || { instrumentId: idx, animalId: "", costumeIds: [] }; 1956 copy[idx] = { ...curr, rotation: val }; 1957 return copy; 1958 }); 1959 }, 1960 className: "w-16 bg-zinc-900 border border-zinc-700/80 rounded px-1.5 py-0.5 text-right font-mono text-white text-[11px] focus:outline-none focus:border-indigo-500" 1961 } 1962 ),
1963 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 text-[10px]", children: "°" }) 1964 ] }) 1965 ] }), 1966 /* @__PURE__ */ jsxRuntimeExports.jsx( 1967 "input", 1968 { 1969 type: "range", 1970 min: "-180", 1971 max: "180", 1972 step: "1", 1973 value: effectiveRotation, 1974 onChange: (e) => { 1975 const val = parseInt(e.target.value, 10); 1976 setInstrumentsConfig((prev) => { 1977 const copy = { ...prev }; 1978 const curr = copy[idx] || { instrumentId: idx, animalId: "", costumeIds: [] }; 1979 copy[idx] = { ...curr, rotation: val }; 1980 return copy; 1981 }); 1982 }, 1983 className: "w-full h-1 bg-zinc-800 rounded-lg appearance-none cursor-pointer accent-indigo-500" 1984 } 1985 ) 1986 ] }),
1987 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pt-1 border-t border-zinc-800/60 flex items-center justify-between text-[10px] text-zinc-400", children: [ 1988 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Joint System:" }), 1989 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-1.5 py-0.5 rounded bg-zinc-900 border border-zinc-700/60 text-indigo-300 font-mono", children: jointType.replace(/_/g, " ") }) 1990 ] }) 1991 ] }) 1992 ] }) 1993 ] 1994 }, 1995 idx 1996 ); 1997 }), 1998 filteredInstruments.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "col-span-full py-12 text-center text-zinc-500 border-2 border-dashed border-zinc-800 rounded-xl", children: [ 1999 'No instruments found matching "', 2000 instSearch, 2001 '".' 2002 ] }) 2003 ] }) 2004 ] }); 2005 })(), 2006 activeTab === "costumes" && (() => { 2007 const allCostumesList = Object.values(allCostumesMap); 2008 const filteredCostumes = allCostumesList.filter((c) => { 2009 if (selectedCostumeCategory !== "all" && c.type !== selectedCostumeCategory) return false; 2010 if (costumeSearch.trim()) { 2011 const query = costumeSearch.toLowerCase().trim(); 2012 if (!c.name.toLowerCase().includes(query) && !c.id.toLowerCase().includes(query)) return false; 2013 } 2014 return true; 2015 }); 2016 const categoryCounts = { 2017 all: allCostumesList.length, 2018 hat: allCostumesList.filter((c) => c.type === "hat").length, 2019 glasses: allCostumesList.filter((c) => c.type === "glasses").length, 2020 neck: allCostumesList.filter((c) => c.type === "neck").length, 2021 body: allCostumesList.filter((c) => c.type === "body").length 2022 }; 2023 const fittingAnimal = BASE_ANIMALS[fittingAnimalId] || BASE_ANIMALS.fox; 2024 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
2025 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-center justify-between gap-4 border-b border-zinc-800 pb-4", children: [ 2026 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 2027 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 2028 /* @__PURE__ */ jsxRuntimeExports.jsx(Shirt, { className: "text-indigo-400", size: 22 }), 2029 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold text-white tracking-wide", children: "Costume Wardrobe & Fitting Room" }), 2030 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "px-2.5 py-0.5 rounded-full text-xs font-semibold bg-indigo-500/20 text-indigo-300 border border-indigo-500/30", children: [ 2031 allCostumesList.length, 2032 " Total Pieces" 2033 ] }) 2034 ] }), 2035 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-400 mt-1", children: "Calibrate placement, scaling, and rotation for hats, eyewear, neckwear, and body outfits across all animal models" }) 2036 ] }),
2037 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-3", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "cursor-pointer px-4 py-2 bg-zinc-800 hover:bg-zinc-700 text-white text-xs font-semibold rounded-lg flex items-center gap-2 transition-colors border border-zinc-700", children: [ 2038 /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { size: 15 }), 2039 " Upload Custom WebP/PNG", 2040 /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "file", ref: fileInputRef, className: "hidden", accept: "image/webp, image/png", onChange: handleCostumeUpload }) 2041 ] }) }) 2042 ] }), 2043 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col md:flex-row items-start md:items-center justify-between gap-3 bg-zinc-900/60 border border-zinc-800 p-3 rounded-xl", children: [ 2044 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-2", children: [ 2045 { id: "all", label: "All Items", icon: "â¨" }, 2046 { id: "hat", label: "Hats & Headwear", icon: "ð©" }, 2047 { id: "glasses", label: "Eyewear & Facial", icon: "ð¶ï¸" }, 2048 { id: "neck", label: "Neckwear", icon: "ð§£" }, 2049 { id: "body", label: "Body Outfits", icon: "ð" } 2050 ].map((cat) => /* @__PURE__ */ jsxRuntimeExports.jsxs( 2051 "button", 2052 { 2053 onClick: () => setSelectedCostumeCategory(cat.id), 2054 className: `px-3 py-1.5 rounded-lg text-xs font-medium flex items-center gap-1.5 transition-all ${selectedCostumeCategory === cat.id ? "bg-indigo-600 text-white shadow-md shadow-indigo-900/30 font-semibold" : "bg-zinc-950 text-zinc-400 hover:text-zinc-200 border border-zinc-800 hover:border-zinc-700"}`, 2055 children: [
2056 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: cat.icon }), 2057 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: cat.label }), 2058 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] px-1.5 py-0.2 rounded-full ${selectedCostumeCategory === cat.id ? "bg-indigo-700 text-white" : "bg-zinc-800 text-zinc-500"}`, children: categoryCounts[cat.id] }) 2059 ] 2060 }, 2061 cat.id 2062 )) }), 2063 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full md:w-64", children: [ 2064 /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { size: 14, className: "absolute left-3 top-1/2 -translate-y-1/2 text-zinc-400 pointer-events-none" }), 2065 /* @__PURE__ */ jsxRuntimeExports.jsx( 2066 "input", 2067 { 2068 type: "text", 2069 placeholder: "Search costumes...", 2070 value: costumeSearch, 2071 onChange: (e) => setCostumeSearch(e.target.value), 2072 className: "w-full bg-zinc-950 border border-zinc-800 rounded-lg pl-8 pr-3 py-1.5 text-xs text-white placeholder-zinc-500 focus:outline-none focus:border-indigo-500" 2073 } 2074 ) 2075 ] }) 2076 ] }),
2077 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-12 gap-6 items-start", children: [ 2078 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "lg:col-span-5 bg-zinc-900/40 border border-zinc-800 rounded-2xl p-4 flex flex-col max-h-[700px]", children: [ 2079 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between pb-3 mb-3 border-b border-zinc-800/80", children: [ 2080 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-semibold text-zinc-300 uppercase tracking-wider", children: [ 2081 "Costume Wardrobe (", 2082 filteredCostumes.length, 2083 ")" 2084 ] }), 2085 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[11px] text-zinc-500", children: "Click to equip & calibrate" }) 2086 ] }), 2087 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-2.5 overflow-y-auto pr-1", children: [ 2088 filteredCostumes.map((c) => { 2089 const isSelected = selectedCostumeId === c.id; 2090 const isCustomized = !!costumesConfig[c.id]; 2091 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 2092 "div", 2093 { 2094 onClick: () => setSelectedCostumeId(c.id), 2095 className: `cursor-pointer rounded-xl p-2.5 flex flex-col items-center justify-between gap-2 border transition-all ${isSelected ? "bg-indigo-950/40 border-indigo-500 shadow-lg shadow-indigo-950/50 ring-1 ring-indigo-500" : "bg-zinc-950/80 border-zinc-800 hover:border-zinc-700 hover:bg-zinc-900/80"}`, 2096 children: [
2097 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full aspect-square bg-zinc-900/90 rounded-lg flex items-center justify-center p-2 relative overflow-hidden border border-zinc-800/60", children: [ 2098 /* @__PURE__ */ jsxRuntimeExports.jsx( 2099 "div", 2100 { 2101 className: "absolute inset-0 opacity-10 pointer-events-none", 2102 style: { 2103 backgroundImage: "linear-gradient(45deg, #808080 25%, transparent 25%), linear-gradient(-45deg, #808080 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #808080 75%), linear-gradient(-45deg, transparent 75%, #808080 75%)", 2104 backgroundSize: "12px 12px", 2105 backgroundPosition: "0 0, 0 6px, 6px -6px, -6px 0px" 2106 } 2107 } 2108 ), 2109 c.svgContent ? /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { viewBox: "-60 -70 120 120", className: "w-full h-full relative z-10 drop-shadow-md overflow-visible", children: /* @__PURE__ */ jsxRuntimeExports.jsx("g", { dangerouslySetInnerHTML: { __html: c.svgContent } }) }) : c.url ? /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: c.url, alt: c.name, className: "w-full h-full object-contain relative z-10 drop-shadow-md" }) : null,
2110 isCustomized && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "absolute top-1 right-1 w-2 h-2 rounded-full bg-emerald-400", title: "Custom calibrated" }) 2111 ] }), 2112 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full text-center", children: [ 2113 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `text-xs font-semibold truncate ${isSelected ? "text-indigo-300" : "text-zinc-200"}`, title: c.name, children: c.name }), 2114 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-500 font-mono capitalize", children: c.type }) 2115 ] }) 2116 ] 2117 }, 2118 c.id 2119 ); 2120 }), 2121 filteredCostumes.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "col-span-full py-12 text-center text-zinc-500 text-xs", children: "No costumes found in this category." }) 2122 ] }) 2123 ] }),
2124 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "lg:col-span-7 bg-zinc-900/60 border border-zinc-800 rounded-2xl p-5 shadow-2xl flex flex-col gap-5", children: [ 2125 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 border-b border-zinc-800 pb-4", children: [ 2126 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 2127 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-semibold text-zinc-300 uppercase tracking-wider", children: "Test On:" }), 2128 /* @__PURE__ */ jsxRuntimeExports.jsx( 2129 "select", 2130 { 2131 value: fittingAnimalId, 2132 onChange: (e) => setFittingAnimalId(e.target.value), 2133 className: "bg-zinc-950 border border-zinc-700 rounded-lg px-2.5 py-1.5 text-xs text-white focus:outline-none focus:border-indigo-500", 2134 children: Object.values(BASE_ANIMALS).map((a) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: a.id, className: "bg-zinc-900 text-white", children: a.name }, a.id)) 2135 } 2136 ),
2137 /* @__PURE__ */ jsxRuntimeExports.jsxs( 2138 "select", 2139 { 2140 value: fittingPose, 2141 onChange: (e) => setFittingPose(e.target.value), 2142 className: "bg-zinc-950 border border-zinc-700 rounded-lg px-2.5 py-1.5 text-xs text-white focus:outline-none focus:border-indigo-500", 2143 children: [ 2144 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "standard", className: "bg-zinc-900 text-white", children: "Pose: Neutral Standing" }), 2145 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "guitar", className: "bg-zinc-900 text-white", children: "Pose: Guitar Strumming" }), 2146 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "drumming", className: "bg-zinc-900 text-white", children: "Pose: Drum Beating" }), 2147 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "bowing", className: "bg-zinc-900 text-white", children: "Pose: Cello Bowing" }),
2148 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "sitting", className: "bg-zinc-900 text-white", children: "Pose: Piano Seated" }), 2149 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "blowing", className: "bg-zinc-900 text-white", children: "Pose: Trumpet Wind" }) 2150 ] 2151 } 2152 ) 2153 ] }), 2154 /* @__PURE__ */ jsxRuntimeExports.jsxs( 2155 "button", 2156 { 2157 onClick: () => setFittingAnimating(!fittingAnimating), 2158 className: `px-3 py-1.5 rounded-lg text-xs font-semibold flex items-center gap-1.5 transition-colors border ${fittingAnimating ? "bg-emerald-500/20 text-emerald-400 border-emerald-500/30" : "bg-zinc-800 text-zinc-400 border-zinc-700"}`, 2159 children: [ 2160 fittingAnimating ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 13 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 13 }), 2161 fittingAnimating ? "Physics Active" : "Physics Paused" 2162 ] 2163 } 2164 ) 2165 ] }),
2166 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full h-80 bg-zinc-950/90 rounded-xl flex items-center justify-center p-4 relative overflow-hidden border border-zinc-800/80 shadow-inner", children: [ 2167 /* @__PURE__ */ jsxRuntimeExports.jsx( 2168 "div", 2169 { 2170 className: "absolute inset-0 opacity-15 pointer-events-none", 2171 style: { 2172 backgroundImage: "linear-gradient(45deg, #404040 25%, transparent 25%), linear-gradient(-45deg, #404040 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #404040 75%), linear-gradient(-45deg, transparent 75%, #404040 75%)", 2173 backgroundSize: "24px 24px", 2174 backgroundPosition: "0 0, 0 12px, 12px -12px, -12px 0px" 2175 } 2176 } 2177 ), 2178 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-radial-gradient pointer-events-none opacity-20", style: { background: "radial-gradient(circle at 50% 60%, #6366f1 0%, transparent 60%)" } }),
2179 /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { viewBox: "0 0 200 300", className: "w-full h-full relative z-10 overflow-visible", children: /* @__PURE__ */ jsxRuntimeExports.jsx("g", { transform: "translate(100, 160) scale(1.05)", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 2180 SouthparkCharacter, 2181 { 2182 animal: fittingAnimal, 2183 instrument: { 2184 id: 1, 2185 name: fittingPose, 2186 type: "preview", 2187 holdStyle: fittingPose, 2188 group: "Preview", 2189 imgUrl: POSE_INSTRUMENTS_IMG[fittingPose] || void 0, 2190 svgContent: void 0 2191 }, 2192 isActive: fittingAnimating && currentVelocityPulse > 0, 2193 velocity: currentVelocityPulse, 2194 physicsMode: "smooth", 2195 costumes: [activeCostumeDef], 2196 customCostumesMap: allCostumesMap, 2197 showInstrument: true 2198 } 2199 ) }) }),
2200 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute top-3 left-3 z-20 flex items-center gap-2 bg-zinc-900/90 backdrop-blur-md border border-zinc-800 px-3 py-1.5 rounded-lg shadow-lg", children: [ 2201 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-2 h-2 rounded-full bg-indigo-400 animate-pulse" }), 2202 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-semibold text-white", children: activeCostumeDef == null ? void 0 : activeCostumeDef.name }), 2203 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-zinc-400 font-mono", children: [ 2204 "(", 2205 activeCostumeDef == null ? void 0 : activeCostumeDef.type, 2206 ")" 2207 ] }) 2208 ] }) 2209 ] }), 2210 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-950/80 border border-zinc-800 rounded-xl p-4 space-y-4", children: [
2211 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between border-b border-zinc-800/80 pb-2.5", children: [ 2212 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 2213 /* @__PURE__ */ jsxRuntimeExports.jsx(SlidersVertical, { size: 15, className: "text-indigo-400" }), 2214 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-bold text-white uppercase tracking-wider", children: "Placement & Scale Calibration" }) 2215 ] }), 2216 /* @__PURE__ */ jsxRuntimeExports.jsxs( 2217 "button", 2218 { 2219 onClick: resetActiveCostume, 2220 className: "text-[11px] text-zinc-400 hover:text-white flex items-center gap-1 transition-colors px-2 py-1 rounded bg-zinc-900 border border-zinc-800", 2221 children: [ 2222 /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 12 }), 2223 " Reset to Default" 2224 ] 2225 } 2226 ) 2227 ] }),
2228 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: [ 2229 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 2230 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-xs mb-1", children: [ 2231 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400 font-medium", children: "Horizontal Offset (X)" }), 2232 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-indigo-400 font-mono font-bold", children: [ 2233 (activeCostumeDef == null ? void 0 : activeCostumeDef.offsetX) ?? 0, 2234 "px" 2235 ] }) 2236 ] }), 2237 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 2238 /* @__PURE__ */ jsxRuntimeExports.jsx( 2239 "input", 2240 { 2241 type: "range", 2242 min: "-80", 2243 max: "80", 2244 step: "1", 2245 value: (activeCostumeDef == null ? void 0 : activeCostumeDef.offsetX) ?? 0, 2246 onChange: (e) => updateActiveCostume("offsetX", parseFloat(e.target.value) || 0), 2247 className: "flex-1 accent-indigo-500 h-1.5 bg-zinc-800 rounded-lg cursor-pointer" 2248 } 2249 ),
2250 /* @__PURE__ */ jsxRuntimeExports.jsx( 2251 "input", 2252 { 2253 type: "number", 2254 value: (activeCostumeDef == null ? void 0 : activeCostumeDef.offsetX) ?? 0, 2255 onChange: (e) => updateActiveCostume("offsetX", parseFloat(e.target.value) || 0), 2256 className: "w-16 bg-zinc-900 border border-zinc-800 rounded px-2 py-1 text-xs text-white text-center font-mono" 2257 } 2258 ) 2259 ] }) 2260 ] }), 2261 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 2262 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-xs mb-1", children: [ 2263 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400 font-medium", children: "Vertical Offset (Y)" }), 2264 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-indigo-400 font-mono font-bold", children: [ 2265 (activeCostumeDef == null ? void 0 : activeCostumeDef.offsetY) ?? 0, 2266 "px" 2267 ] }) 2268 ] }),
2269 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 2270 /* @__PURE__ */ jsxRuntimeExports.jsx( 2271 "input", 2272 { 2273 type: "range", 2274 min: "-80", 2275 max: "80", 2276 step: "1", 2277 value: (activeCostumeDef == null ? void 0 : activeCostumeDef.offsetY) ?? 0, 2278 onChange: (e) => updateActiveCostume("offsetY", parseFloat(e.target.value) || 0), 2279 className: "flex-1 accent-indigo-500 h-1.5 bg-zinc-800 rounded-lg cursor-pointer" 2280 } 2281 ), 2282 /* @__PURE__ */ jsxRuntimeExports.jsx( 2283 "input", 2284 { 2285 type: "number", 2286 value: (activeCostumeDef == null ? void 0 : activeCostumeDef.offsetY) ?? 0, 2287 onChange: (e) => updateActiveCostume("offsetY", parseFloat(e.target.value) || 0), 2288 className: "w-16 bg-zinc-900 border border-zinc-800 rounded px-2 py-1 text-xs text-white text-center font-mono" 2289 } 2290 ) 2291 ] }) 2292 ] }),
2293 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 2294 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-xs mb-1", children: [ 2295 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400 font-medium", children: "Scale Factor" }), 2296 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-indigo-400 font-mono font-bold", children: [ 2297 Math.round(((activeCostumeDef == null ? void 0 : activeCostumeDef.scale) ?? 1) * 100), 2298 "%" 2299 ] }) 2300 ] }), 2301 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 2302 /* @__PURE__ */ jsxRuntimeExports.jsx( 2303 "input", 2304 { 2305 type: "range", 2306 min: "0.4", 2307 max: "2.5", 2308 step: "0.02", 2309 value: (activeCostumeDef == null ? void 0 : activeCostumeDef.scale) ?? 1, 2310 onChange: (e) => updateActiveCostume("scale", parseFloat(e.target.value) || 1), 2311 className: "flex-1 accent-indigo-500 h-1.5 bg-zinc-800 rounded-lg cursor-pointer" 2312 } 2313 ),
2314 /* @__PURE__ */ jsxRuntimeExports.jsx( 2315 "input", 2316 { 2317 type: "number", 2318 step: "0.05", 2319 value: (activeCostumeDef == null ? void 0 : activeCostumeDef.scale) ?? 1, 2320 onChange: (e) => updateActiveCostume("scale", parseFloat(e.target.value) || 1), 2321 className: "w-16 bg-zinc-900 border border-zinc-800 rounded px-2 py-1 text-xs text-white text-center font-mono" 2322 } 2323 ) 2324 ] }) 2325 ] }), 2326 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 2327 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-xs mb-1", children: [ 2328 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400 font-medium", children: "Rotation Angle" }), 2329 /* @__P
2329URE__ */ jsxRuntimeExports.jsxs("span", { className: "text-indigo-400 font-mono font-bold", children: [ 2330 (activeCostumeDef == null ? void 0 : activeCostumeDef.rotation) ?? 0, 2331 "°" 2332 ] }) 2333 ] }), 2334 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 2335 /* @__PURE__ */ jsxRuntimeExports.jsx( 2336 "input", 2337 { 2338 type: "range", 2339 min: "-90", 2340 max: "90", 2341 step: "1", 2342 value: (activeCostumeDef == null ? void 0 : activeCostumeDef.rotation) ?? 0, 2343 onChange: (e) => updateActiveCostume("rotation", parseFloat(e.target.value) || 0), 2344 className: "flex-1 accent-indigo-500 h-1.5 bg-zinc-800 rounded-lg cursor-pointer" 2345 } 2346 ),
2347 /* @__PURE__ */ jsxRuntimeExports.jsx( 2348 "input", 2349 { 2350 type: "number", 2351 value: (activeCostumeDef == null ? void 0 : activeCostumeDef.rotation) ?? 0, 2352 onChange: (e) => updateActiveCostume("rotation", parseFloat(e.target.value) || 0), 2353 className: "w-16 bg-zinc-900 border border-zinc-800 rounded px-2 py-1 text-xs text-white text-center font-mono" 2354 } 2355 ) 2356 ] }) 2357 ] }) 2358 ] }), 2359 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pt-2 border-t border-zinc-800/60 flex flex-wrap items-center justify-between gap-3 text-xs text-zinc-400", children: [ 2360 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 2361 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-semibold text-zinc-300", children: "Kinematic Anchor:" }),
2362 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider ${(activeCostumeDef == null ? void 0 : activeCostumeDef.anchorTo) === "head" || (activeCostumeDef == null ? void 0 : activeCostumeDef.type) === "hat" || (activeCostumeDef == null ? void 0 : activeCostumeDef.type) === "glasses" ? "bg-cyan-500/20 text-cyan-300 border border-cyan-500/30" : "bg-emerald-500/20 text-emerald-300 border border-emerald-500/30"}`, children: (activeCostumeDef == null ? void 0 : activeCostumeDef.anchorTo) === "head" || (activeCostumeDef == null ? void 0 : activeCostumeDef.type) === "hat" || (activeCostumeDef == null ? void 0 : activeCostumeDef.type) === "glasses" ? "Head Socket (Rotates with head)" : "Body Socket (Bounces with torso)" }) 2363 ] }), 2364 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[11px] text-zinc-500", children: 'Changes are saved when you click "Save All Changes" at the top' }) 2365 ] }) 2366 ] }) 2367 ] }) 2368 ] }) 2369 ] }); 2370 })(), 2371 activeTab === "preview" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-full space-y-4", children: [
2372 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-none flex items-center gap-4 border-b border-zinc-800 pb-4", children: [ 2373 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 2374 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold", children: "Live Preview" }), 2375 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-400", children: "Enter a MIDI ID to test the current configurations in the actual player" }) 2376 ] }), 2377 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ml-auto flex items-center gap-3", children: [ 2378 /* @__PURE__ */ jsxRuntimeExports.jsx( 2379 "input", 2380 { 2381 type: "text", 2382 placeholder: "Enter Track ID (e.g. 1234)", 2383 value: previewMidiId, 2384 onChange: (e) => setPreviewMidiId(e.target.value), 2385 onKeyDown: (e) => { 2386 if (e.key === "Enter" && previewMidiId) { 2387 setIframeUrl(previewMidiId); 2388 } 2389 }, 2390 className: "bg-zinc-900 border border-zinc-700 rounded-lg px-4 py-2 text-sm text-white w-64" 2391 } 2392 ),
2393 /* @__PURE__ */ jsxRuntimeExports.jsxs( 2394 "button", 2395 { 2396 onClick: () => { 2397 if (previewMidiId) { 2398 setIframeUrl(previewMidiId); 2399 } 2400 }, 2401 className: "px-4 py-2 bg-indigo-600 hover:bg-indigo-500 text-white text-sm font-medium rounded-lg transition-colors flex items-center gap-2", 2402 children: [ 2403 /* @__PURE__ */ jsxRuntimeExports.jsx(CirclePlay, { size: 16 }), 2404 " Load Player" 2405 ] 2406 } 2407 ) 2408 ] }) 2409 ] }), 2410 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 bg-black rounded-xl border border-zinc-800 overflow-hidden relative", children: !iframeUrl ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center text-zinc-600", children: [ 2411 /* @__PURE__ */ jsxRuntimeExports.jsx(CirclePlay, { size: 48, className: "mb-4 opacity-50" }),
2412 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Enter a Track ID and click Load to see the preview" }) 2413 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(StandaloneSouthparkViewer, { trackId: iframeUrl }, iframeUrl) }) 2414 ] }) 2415 ] }) 2416 ] }), 2417 inspectingPose && /* @__PURE__ */ jsxRuntimeExports.jsx( 2418 "div", 2419 { 2420 className: "fixed inset-0 z-50 bg-black/85 backdrop-blur-md flex items-center justify-center p-4", 2421 onClick: () => setInspectingPose(null), 2422 children: /* @__PURE__ */ jsxRuntimeExports.jsxs( 2423 "div", 2424 { 2425 className: "bg-zinc-950 border border-cyan-500/40 rounded-2xl max-w-5xl w-full shadow-2xl shadow-cyan-950/80 overflow-hidden flex flex-col max-h-[92vh]", 2426 onClick: (e) => e.stopPropagation(), 2427 children: [ 2428 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between border-b border-cyan-900/50 px-6 py-4 bg-gradient-to-r from-cyan-950/40 via-zinc-950 to-zinc-950", children: [
2429 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 2430 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-9 h-9 rounded-xl bg-cyan-500/20 text-cyan-400 flex items-center justify-center font-bold text-lg border border-cyan-500/30", children: "ð¦´" }), 2431 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 2432 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 2433 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-lg font-bold text-cyan-100 tracking-wide", children: "Kinematic Joint Socket Inspector" }), 2434 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-2 py-0.5 rounded text-xs font-bold border ${inspectingPose.badgeColor}`, children: inspectingPose.name }) 2435 ] }), 2436 /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-zinc-400", children: [ 2437 inspectingPose.desc, 2438 " ⢠Hold Style: ",
2439 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-cyan-300 font-mono font-semibold", children: inspectingPose.holdStyle }) 2440 ] }) 2441 ] }) 2442 ] }), 2443 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 2444 "button", 2445 { 2446 onClick: () => setInspectingPose(null), 2447 className: "p-2 rounded-lg bg-zinc-900 text-zinc-400 hover:text-white hover:bg-zinc-800 border border-zinc-800 transition-colors", 2448 title: "Close Inspector (Esc)", 2449 children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 18 }) 2450 } 2451 ) }) 2452 ] }), 2453 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-12 gap-6 p-6 overflow-y-auto", children: [ 2454 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "lg:col-span-7 flex flex-col gap-3", children: [
2455 /* @__PURE__ */ jsxRuntimeExports.jsxs( 2456 "div", 2457 { 2458 className: "w-full aspect-square bg-slate-950 rounded-xl border border-cyan-800/60 relative overflow-hidden flex items-center justify-center shadow-inner cursor-grab active:cursor-grabbing select-none", 2459 style: { 2460 backgroundImage: "radial-gradient(circle, rgba(14, 165, 233, 0.12) 1px, transparent 1px)", 2461 backgroundSize: "16px 16px" 2462 }, 2463 onWheel: (e) => { 2464 e.preventDefault(); 2465 const delta = e.deltaY < 0 ? 0.3 : -0.3; 2466 setModalZoom((prev) => Math.min(6, Math.max(1, parseFloat((prev + delta).toFixed(2))))); 2467 }, 2468 onMouseDown: (e) => { 2469 setIsModalDragging(true); 2470 setDragStart({ x: e.clientX, y: e.clientY }); 2471 }, 2472 onMouseMove: (e) => { 2473 if (!isModalDragging) return; 2474 const dx = (e.clientX - dragStart.x) / modalZoom; 2475 const dy = (e.clientY - dragStart.y) / modalZoom; 2476 setModalPan((prev) => ({ x: prev.x + dx, y: prev.y + dy })); 2477 setDragStart({ x: e.clientX, y: e.clientY }); 2478 }, 2479 onMouseUp: () => setIsModalDragging(false), 2480 onMouseLeave: () => setIsModalDragging(false), 2481 children: [
2482 /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { viewBox: getDynamicViewBox(modalZoom, modalPan), className: "w-full h-full overflow-visible drop-shadow-2xl", children: /* @__PURE__ */ jsxRuntimeExports.jsx("g", { transform: "translate(100, 155)", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 2483 SouthparkCharacter, 2484 { 2485 animal: BASE_ANIMALS[inspectorAnimalId] || BASE_ANIMALS.fox, 2486 customConfig: charactersConfig[inspectorAnimalId], 2487 instrument: { 2488 id: 1, 2489 name: inspectingPose.name, 2490 type: "preview", 2491 holdStyle: inspectingPose.holdStyle, 2492 group: "Preview", 2493 imgUrl: POSE_INSTRUMENTS_IMG[inspectingPose.id] || void 0, 2494 svgContent: void 0 2495 }, 2496 isActive: !manualOverrideLimbAngles && isGlobalAnimating && inspectingPose.id !== "standard" && currentVelocityPulse > 0, 2497 velocity: !manualOverrideLimbAngles && isGlobalAnimating && inspectingPose.id !== "standard" && currentVelocityPulse > 0 ? currentVelocityPulse : 0, 2498 physicsMode: "smooth", 2499 showInstrument: modalShowInstrument, 2500 skeletonOnly: modalDisplayMode !== "full", 2501 ghostSilhouette: modalDisplayMode === "ghost", 2502 showSkeleton: true, 2503 manualLimbAngles: manualOverrideLimbAngles ? { 2504 shoulderL: manualLeftShoulder, 2505 relElbowL: manualLeftElbow, 2506 shoulderR: manualRightShoulder, 2507 relElbowR: manualRightElbow 2508 } : void 0 2509 } 2510 ) }) }),
2511 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute top-3 left-3 flex flex-wrap items-center gap-1 bg-zinc-950/80 p-1 rounded-lg border border-cyan-900/50 backdrop-blur-sm text-xs", children: [ 2512 /* @__PURE__ */ jsxRuntimeExports.jsx( 2513 "button", 2514 { 2515 onClick: () => setModalDisplayMode("pure"), 2516 className: `px-2 py-1 rounded text-[10px] font-bold transition-all ${modalDisplayMode === "pure" ? "bg-cyan-500 text-black shadow-sm" : "text-zinc-400 hover:text-white"}`, 2517 children: "Pure Skeleton" 2518 } 2519 ), 2520 /* @__PURE__ */ jsxRuntimeExports.jsx( 2521 "button", 2522 { 2523 onClick: () => setModalDisplayMode("ghost"), 2524 className: `px-2 py-1 rounded text-[10px] font-bold transition-all ${modalDisplayMode === "ghost" ? "bg-cyan-500 text-black shadow-sm" : "text-zinc-400 hover:text-white"}`, 2525 children: "Ghost Body" 2526 } 2527 ),
2528 /* @__PURE__ */ jsxRuntimeExports.jsx( 2529 "button", 2530 { 2531 onClick: () => setModalDisplayMode("full"), 2532 className: `px-2 py-1 rounded text-[10px] font-bold transition-all ${modalDisplayMode === "full" ? "bg-cyan-500 text-black shadow-sm" : "text-zinc-400 hover:text-white"}`, 2533 children: "Full Character" 2534 } 2535 ), 2536 /* @__PURE__ */ jsxRuntimeExports.jsxs( 2537 "button", 2538 { 2539 onClick: () => setModalShowInstrument((prev) => !prev), 2540 className: `px-2 py-1 rounded text-[10px] font-bold transition-all ${modalShowInstrument ? "bg-amber-500/20 text-amber-300 border border-amber-500/40" : "text-zinc-500 hover:text-zinc-300"}`, 2541 children: [ 2542 "Instrument: ", 2543 modalShowInstrument ? "ON" : "OFF" 2544 ] 2545 } 2546 ) 2547 ] }),
2548 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute bottom-3 inset-x-3 flex items-center justify-between pointer-events-none", children: [ 2549 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pointer-events-auto flex items-center gap-1.5 bg-zinc-950/85 px-3 py-1.5 rounded-lg border border-cyan-800/60 backdrop-blur-sm text-xs font-mono text-cyan-200 shadow-xl", children: [ 2550 /* @__PURE__ */ jsxRuntimeExports.jsx( 2551 "button", 2552 { 2553 onClick: () => setModalZoom((z) => Math.max(1, parseFloat((z - 0.5).toFixed(2)))), 2554 className: "p-1 rounded hover:bg-cyan-900/50 text-cyan-400 transition-colors", 2555 title: "Zoom Out", 2556 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ZoomOut, { size: 14 }) 2557 } 2558 ),
2559 /* @__PURE__ */ jsxRuntimeExports.jsx( 2560 "input", 2561 { 2562 type: "range", 2563 min: "1.0", 2564 max: "6.0", 2565 step: "0.25", 2566 value: modalZoom, 2567 onChange: (e) => setModalZoom(parseFloat(e.target.value)), 2568 className: "w-20 accent-cyan-400 h-1.5 bg-zinc-800 rounded-lg cursor-pointer" 2569 } 2570 ), 2571 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-bold min-w-[45px] text-right", children: [ 2572 Math.round(modalZoom * 100), 2573 "%" 2574 ] }), 2575 /* @__PURE__ */ jsxRuntimeExports.jsx( 2576 "button", 2577 { 2578 onClick: () => setModalZoom((z) => Math.min(6, parseFloat((z + 0.5).toFixed(2)))), 2579 className: "p-1 rounded hover:bg-cyan-900/50 text-cyan-400 transition-colors", 2580 title: "Zoom In", 2581 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ZoomIn, { size: 14 }) 2582 } 2583 ),
2584 /* @__PURE__ */ jsxRuntimeExports.jsx( 2585 "button", 2586 { 2587 onClick: () => { 2588 setModalZoom(2.5); 2589 setModalPan({ x: 0, y: 0 }); 2590 }, 2591 className: "ml-2 px-2 py-0.5 rounded text-[10px] bg-cyan-900/40 hover:bg-cyan-800/50 text-cyan-300 border border-cyan-700/40 transition-colors", 2592 children: "Reset" 2593 } 2594 ), 2595 /* @__PURE__ */ jsxRuntimeExports.jsx( 2596 "button", 2597 { 2598 onClick: () => { 2599 setModalZoom(1); 2600 setModalPan({ x: 0, y: 0 }); 2601 }, 2602 className: "px-2 py-0.5 rounded text-[10px] bg-zinc-900 hover:bg-zinc-800 text-zinc-300 border border-zinc-700/40 transition-colors", 2603 children: "Fit (1x)" 2604 } 2605 ) 2606 ] }),
2607 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] font-mono text-zinc-400 bg-zinc-950/80 px-2 py-1 rounded border border-zinc-800 pointer-events-none", children: "ð±ï¸ Scroll: Zoom ⢠Drag: Pan" }) 2608 ] }) 2609 ] 2610 } 2611 ), 2612 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs bg-zinc-900/60 p-2 rounded-xl border border-zinc-800/60", children: [ 2613 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400 font-medium", children: "Model:" }), 2614 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1.5", children: Object.values(BASE_ANIMALS).map((a) => /* @__PURE__ */ jsxRuntimeExports.jsx( 2615 "button", 2616 { 2617 onClick: () => setInspectorAnimalId(a.id), 2618 className: `px-2.5 py-1 rounded-lg text-xs font-bold transition-all ${inspectorAnimalId === a.id ? "bg-cyan-500 text-black shadow-sm" : "bg-zinc-950 text-zinc-400 hover:text-white border border-zinc-800"}`, 2619 children: a.name 2620 }, 2621 a.id 2622 )) }) 2623 ] }) 2624 ] }),
2625 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "lg:col-span-5 flex flex-col gap-4", children: [ 2626 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900/80 border border-cyan-900/50 rounded-xl p-4 space-y-3", children: [ 2627 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between border-b border-cyan-900/40 pb-2", children: [ 2628 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-bold text-cyan-200 tracking-wide uppercase flex items-center gap-2", children: [ 2629 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-2 h-2 rounded-full bg-emerald-400 animate-pulse" }), 2630 "Kinematic Socket Integrity" 2631 ] }), 2632 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-mono px-2 py-0.5 rounded bg-emerald-950/60 text-emerald-300 border border-emerald-800/40 font-bold", children: "0.0000px SEPARATION" }) 2633 ] }),
2634 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5 font-mono text-[11px]", children: [ 2635 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-1.5 rounded bg-zinc-950/60 border border-zinc-800/50", children: [ 2636 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400", children: "Torso Clavicle â Left Shoulder:" }), 2637 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400 font-bold", children: "0.0000px [CONNECTED]" }) 2638 ] }), 2639 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-1.5 rounded bg-zinc-950/60 border border-zinc-800/50", children: [ 2640 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400", children: "Left Shoulder â Upper Arm Top:" }), 2641 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400 font-bold", children: "0.0000px [CONNECTED]" }) 2642 ] }),
2643 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-1.5 rounded bg-zinc-950/60 border border-zinc-800/50", children: [ 2644 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400", children: "Left Upper Arm â Forearm (Elbow):" }), 2645 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400 font-bold", children: "0.0000px [CONNECTED]" }) 2646 ] }), 2647 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-1.5 rounded bg-zinc-950/60 border border-zinc-800/50", children: [ 2648 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400", children: "Left Forearm â Hand (Wrist):" }), 2649 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400 font-bold", children: "0.0000px [CONNECTED]" }) 2650 ] }), 2651 /* @__P
2651URE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-1.5 rounded bg-zinc-950/60 border border-zinc-800/50", children: [ 2652 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400", children: "Right Shoulder â Upper Arm Top:" }), 2653 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400 font-bold", children: "0.0000px [CONNECTED]" }) 2654 ] }), 2655 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-1.5 rounded bg-zinc-950/60 border border-zinc-800/50", children: [ 2656 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400", children: "Right Upper Arm â Forearm (Elbow):" }), 2657 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400 font-bold", children: "0.0000px [CONNECTED]" }) 2658 ] }), 2659 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-1.5 rounded bg-zinc-950/60 border border-zinc-800/50", children: [
2660 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400", children: "Right Forearm â Hand (Wrist):" }), 2661 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400 font-bold", children: "0.0000px [CONNECTED]" }) 2662 ] }) 2663 ] }) 2664 ] }), 2665 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900/80 border border-zinc-800 rounded-xl p-4 space-y-3", children: [ 2666 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between border-b border-zinc-800 pb-2", children: [ 2667 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-bold text-zinc-200 tracking-wide uppercase flex items-center gap-2", children: [ 2668 /* @__PURE__ */ jsxRuntimeExports.jsx(SlidersVertical, { size: 14, className: "text-cyan-400" }), 2669 "Manual Angle Scrubbing Test" 2670 ] }),
2671 /* @__PURE__ */ jsxRuntimeExports.jsx( 2672 "button", 2673 { 2674 onClick: () => setManualOverrideLimbAngles((prev) => !prev), 2675 className: `px-2.5 py-1 rounded text-xs font-bold transition-all ${manualOverrideLimbAngles ? "bg-amber-500 text-black shadow-sm" : "bg-zinc-800 text-zinc-300 hover:text-white"}`, 2676 children: manualOverrideLimbAngles ? "Override: ACTIVE" : "Enable Sliders" 2677 } 2678 ) 2679 ] }), 2680 manualOverrideLimbAngles ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3 pt-1", children: [ 2681 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] text-amber-300/90 leading-tight", children: "Drag the sliders below to rotate joints across extreme angles and verify that the upper arm, elbow, and forearm remain 100% attached at all times." }), 2682 /* @__P
2682URE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [ 2683 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-xs font-mono", children: [ 2684 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400", children: "Left Shoulder Rotation:" }), 2685 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-400 font-bold", children: [ 2686 manualLeftShoulder, 2687 "°" 2688 ] }) 2689 ] }), 2690 /* @__PURE__ */ jsxRuntimeExports.jsx( 2691 "input", 2692 { 2693 type: "range", 2694 min: "-180", 2695 max: "180", 2696 value: manualLeftShoulder, 2697 onChange: (e) => setManualLeftShoulder(parseInt(e.target.value)), 2698 className: "w-full accent-emerald-500 h-1.5 bg-zinc-800 rounded cursor-pointer" 2699 } 2700 ) 2701 ] }),
2702 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [ 2703 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-xs font-mono", children: [ 2704 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400", children: "Left Elbow (Rel to Upper):" }), 2705 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-400 font-bold", children: [ 2706 manualLeftElbow, 2707 "°" 2708 ] }) 2709 ] }), 2710 /* @__PURE__ */ jsxRuntimeExports.jsx( 2711 "input", 2712 { 2713 type: "range", 2714 min: "-170", 2715 max: "0", 2716 value: manualLeftElbow, 2717 onChange: (e) => setManualLeftElbow(parseInt(e.target.value)), 2718 className: "w-full accent-emerald-500 h-1.5 bg-zinc-800 rounded cursor-pointer" 2719 } 2720 ),
2721 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-[10px] text-zinc-500 font-mono", children: [ 2722 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Max Flex: -170°" }), 2723 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Straight: 0°" }) 2724 ] }) 2725 ] }), 2726 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [ 2727 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-xs font-mono", children: [ 2728 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400", children: "Right Shoulder Rotation:" }), 2729 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-purple-400 font-bold", children: [ 2730 manualRightShoulder, 2731 "°" 2732 ] }) 2733 ] }),
2734 /* @__PURE__ */ jsxRuntimeExports.jsx( 2735 "input", 2736 { 2737 type: "range", 2738 min: "-180", 2739 max: "180", 2740 value: manualRightShoulder, 2741 onChange: (e) => setManualRightShoulder(parseInt(e.target.value)), 2742 className: "w-full accent-purple-500 h-1.5 bg-zinc-800 rounded cursor-pointer" 2743 } 2744 ) 2745 ] }), 2746 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [ 2747 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-xs font-mono", children: [ 2748 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400", children: "Right Elbow (Rel to Upper):" }), 2749 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-purple-400 font-bold", children: [ 2750 "+", 2751 manualRightElbow, 2752 "°" 2753 ] }) 2754 ] }),
2755 /* @__PURE__ */ jsxRuntimeExports.jsx( 2756 "input", 2757 { 2758 type: "range", 2759 min: "0", 2760 max: "170", 2761 value: manualRightElbow, 2762 onChange: (e) => setManualRightElbow(parseInt(e.target.value)), 2763 className: "w-full accent-purple-500 h-1.5 bg-zinc-800 rounded cursor-pointer" 2764 } 2765 ), 2766 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-[10px] text-zinc-500 font-mono", children: [ 2767 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Straight: 0°" }), 2768 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Max Flex: +170°" }) 2769 ] }) 2770 ] }), 2771 /* @__PURE__ */ jsxRuntimeExports.jsx( 2772 "button", 2773 { 2774 onClick: () => { 2775 setManualLeftShoulder(-45); 2776 setManualLeftElbow(-60); 2777 setManualRightShoulder(45); 2778 setManualRightElbow(60); 2779 }, 2780 className: "w-full py-1.5 bg-zinc-800 hover:bg-zinc-700 text-xs font-semibold rounded-lg transition-colors text-zinc-300", 2781 children: "Reset Manual Angles" 2782 } 2783 ) 2784 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "py-4 text-center text-xs text-zinc-500 space-y-1", children: [
2785 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Currently playing automatic performance kinematics." }), 2786 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] text-zinc-600", children: "Click âEnable Slidersâ to manually rotate joints and test physical attachment." }) 2787 ] }) 2788 ] }) 2789 ] }) 2790 ] }) 2791 ] 2792 } 2793 ) 2794 } 2795 ) 2796 ] }); 2797} 2798export { 2799 ACTION_POSES, 2800 POSE_INSTRUMENTS_IMG, 2801 POSE_INSTRUMENTS_SVG, 2802 SouthparkStudio as default 2803};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.