@hyperframes/studio 0.8.126 → 0.8.128
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{hyperframes-player-BbbN-wWN.js → hyperframes-player-CKCx0CFA.js} +1 -1
- package/dist/assets/{index-Cll7uTNy.js → index-CF31Pq1E.js} +1 -1
- package/dist/assets/{index-B8kU2xXl.js → index-ClLzyuwb.js} +172 -172
- package/dist/assets/{index-nvLF29ZH.js → index-CwS08gWW.js} +1 -1
- package/dist/{chunk-3F47EFCT.js → chunk-7UQWVN52.js} +17 -10
- package/dist/chunk-7UQWVN52.js.map +1 -0
- package/dist/{chunk-246VB2G3.js → chunk-DRVWEPO5.js} +35 -7
- package/dist/chunk-DRVWEPO5.js.map +1 -0
- package/dist/{chunk-5EKWJZLM.js → chunk-TDWGXBUT.js} +1 -1
- package/dist/chunk-TDWGXBUT.js.map +1 -0
- package/dist/{domEditingLayers-OL7GLSS4.js → domEditingLayers-IEVLO54Z.js} +3 -3
- package/dist/index.d.ts +8 -9
- package/dist/index.html +1 -1
- package/dist/index.js +2184 -1745
- package/dist/index.js.map +1 -1
- package/dist/player.js +2 -2
- package/package.json +8 -8
- package/src/components/TimelineToolbar.tsx +6 -7
- package/src/components/editor/MotionPathOverlay.layerPress.test.tsx +42 -0
- package/src/components/editor/MotionPathOverlay.tsx +20 -26
- package/src/components/editor/commitPreflightHandles.test.tsx +32 -0
- package/src/components/editor/domEditingLayers.test.ts +45 -18
- package/src/components/editor/domEditingLayers.ts +11 -9
- package/src/components/editor/domEditingTypes.ts +1 -0
- package/src/components/editor/keyframeRetime.ts +11 -4
- package/src/components/editor/motionPathCommit.nodeDrop.test.ts +212 -0
- package/src/components/editor/motionPathCommit.ts +58 -0
- package/src/components/editor/motionPathGeometry.test.ts +13 -0
- package/src/components/editor/motionPathGeometry.ts +4 -2
- package/src/components/editor/probeSourceElement.ts +17 -1
- package/src/components/nle/useCanvasZOrderTimelineMirror.test.tsx +4 -14
- package/src/components/nle/useCanvasZOrderTimelineMirror.ts +0 -7
- package/src/components/nle/useTimelineEditCallbacks.test.tsx +27 -42
- package/src/components/nle/useTimelineEditCallbacks.ts +3 -2
- package/src/hooks/gsapDragCommit.ts +41 -6
- package/src/hooks/gsapDragPositionCommit.ts +90 -17
- package/src/hooks/gsapEditOutcome.ts +27 -2
- package/src/hooks/gsapKeyframeCacheHelpers.test.ts +50 -0
- package/src/hooks/gsapKeyframeCacheHelpers.ts +21 -2
- package/src/hooks/gsapMutationClient.ts +35 -7
- package/src/hooks/gsapOvershootEase.test.ts +67 -0
- package/src/hooks/gsapParsedTween.test-helpers.ts +28 -1
- package/src/hooks/gsapParsedTween.ts +27 -7
- package/src/hooks/gsapPositionDetection.ts +5 -2
- package/src/hooks/gsapRuntimeBridge.test.ts +35 -0
- package/src/hooks/gsapRuntimeBridge.ts +7 -1
- package/src/hooks/gsapRuntimeKeyframes.test.ts +80 -6
- package/src/hooks/gsapRuntimeKeyframes.ts +20 -60
- package/src/hooks/gsapRuntimeMotionPath.ts +47 -0
- package/src/hooks/gsapRuntimeTweenIndex.ts +53 -0
- package/src/hooks/gsapShared.test.ts +49 -35
- package/src/hooks/gsapShared.ts +35 -33
- package/src/hooks/gsapValueAtPlayhead.realGsap.test.ts +198 -6
- package/src/hooks/gsapValueAtPlayhead.test.ts +142 -27
- package/src/hooks/gsapValueAtPlayhead.ts +49 -10
- package/src/hooks/gsapWholePropertyOffsetCommit.test.ts +72 -1
- package/src/hooks/gsapWholePropertyOffsetCommit.ts +6 -1
- package/src/hooks/timelineEditingHelpers.test.ts +21 -0
- package/src/hooks/timelineEditingHelpers.ts +40 -14
- package/src/hooks/timelineLinkEditPlan.ts +6 -4
- package/src/hooks/timelineMoveAdapter.ts +3 -5
- package/src/hooks/timelineTimingSync.test.ts +83 -125
- package/src/hooks/timelineTimingSync.ts +62 -42
- package/src/hooks/useAnimatedPropertyCommit.ts +6 -6
- package/src/hooks/useBlockedTimelineEditToast.ts +3 -12
- package/src/hooks/useCommitPreflightCapabilities.ts +44 -9
- package/src/hooks/useDomEditPreviewSync.ts +8 -2
- package/src/hooks/useDomSelection.test.ts +27 -0
- package/src/hooks/useDomSelection.ts +12 -13
- package/src/hooks/useDomSelectionTypes.ts +1 -0
- package/src/hooks/useEditHistoryActions.test.tsx +54 -3
- package/src/hooks/useEditHistoryActions.ts +9 -6
- package/src/hooks/useEnableKeyframes.test.ts +20 -15
- package/src/hooks/useEnableKeyframes.ts +54 -71
- package/src/hooks/useGestureCommit.ts +2 -1
- package/src/hooks/useGsapAwareEditing.groupPlan.test.tsx +25 -0
- package/src/hooks/useGsapTweenCache.ts +2 -17
- package/src/hooks/usePersistentEditHistory.test.ts +45 -0
- package/src/hooks/usePersistentEditHistory.ts +13 -2
- package/src/hooks/usePreviewPersistence.history.test.tsx +83 -0
- package/src/hooks/usePreviewPersistence.ts +26 -4
- package/src/hooks/useTimelineClipTimingEditing.ts +343 -0
- package/src/hooks/useTimelineDeleteOps.test.tsx +20 -1
- package/src/hooks/useTimelineDeleteOps.ts +5 -13
- package/src/hooks/useTimelineEditing.test.tsx +63 -4
- package/src/hooks/useTimelineEditing.ts +21 -295
- package/src/hooks/useTimelineGroupEditing.test.tsx +91 -0
- package/src/hooks/useTimelineGroupEditing.ts +15 -38
- package/src/player/components/Timeline.test.ts +12 -4
- package/src/player/components/TimelineClip.tsx +3 -0
- package/src/player/components/TimelineCompactDiamonds.tsx +4 -0
- package/src/player/components/TimelineLanes.test.tsx +61 -5
- package/src/player/components/TimelineLanes.tsx +20 -28
- package/src/player/components/TimelinePropertyLanes.tsx +10 -1
- package/src/player/components/timelineCallbacks.ts +1 -4
- package/src/player/components/timelineClipDragCommit.test.ts +31 -0
- package/src/player/components/timelineClipDragCommit.ts +39 -28
- package/src/player/components/timelineClipDragGestureLifecycle.ts +1 -1
- package/src/player/components/timelineClipGestureHandlers.test.ts +6 -34
- package/src/player/components/timelineClipGestureHandlers.ts +2 -20
- package/src/player/components/timelineEditing.ts +1 -3
- package/src/player/components/timelineGroupResizeCommit.ts +10 -9
- package/src/player/components/timelineLinkSelection.ts +1 -35
- package/src/player/components/timelineOptimisticRevision.ts +7 -4
- package/src/player/components/trackHeaderLaneState.ts +9 -2
- package/src/player/components/trackHeaderLaneValues.test.ts +89 -0
- package/src/player/components/trackHeaderLaneValues.ts +54 -13
- package/src/player/components/useTimelineClipDrag.keyboard.test.tsx +4 -6
- package/src/player/components/useTimelineClipDrag.resize.test.tsx +4 -7
- package/src/player/components/useTimelineClipDrag.ts +0 -3
- package/src/player/components/useTimelineKeyframeHandlers.ts +17 -1
- package/src/player/store/batchElementUpdates.test.ts +43 -0
- package/src/player/store/batchElementUpdates.ts +9 -0
- package/src/player/store/elementPatchQueue.ts +35 -0
- package/src/player/store/playerStore.ts +5 -6
- package/src/utils/domEditHelpers.ts +5 -0
- package/src/utils/globalTimeCompiler.test.ts +0 -4
- package/src/utils/globalTimeCompiler.ts +6 -4
- package/src/utils/gsapKeyframeEases.ts +79 -0
- package/src/utils/sameData.ts +9 -0
- package/src/utils/studioFileHistory.ts +1 -0
- package/src/utils/studioPendingEdits.ts +15 -1
- package/dist/chunk-246VB2G3.js.map +0 -1
- package/dist/chunk-3F47EFCT.js.map +0 -1
- package/dist/chunk-5EKWJZLM.js.map +0 -1
- /package/dist/{domEditingLayers-OL7GLSS4.js.map → domEditingLayers-IEVLO54Z.js.map} +0 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Structural equality for plain data; DOM nodes (from any realm) compare by identity. */
|
|
2
|
+
export function sameData(a: unknown, b: unknown): boolean {
|
|
3
|
+
if (Object.is(a, b)) return true;
|
|
4
|
+
if (!a || !b || typeof a !== "object" || typeof b !== "object") return false;
|
|
5
|
+
if ("nodeType" in a || "nodeType" in b || Array.isArray(a) !== Array.isArray(b)) return false;
|
|
6
|
+
const keys = Object.keys(a);
|
|
7
|
+
if (keys.length !== Object.keys(b).length) return false;
|
|
8
|
+
return keys.every((key) => sameData((a as never)[key], (b as never)[key]));
|
|
9
|
+
}
|
|
@@ -81,6 +81,7 @@ export async function saveServerRewriteWithHistory(input: {
|
|
|
81
81
|
path: string;
|
|
82
82
|
label: string;
|
|
83
83
|
coalesceKey?: string;
|
|
84
|
+
coalesceMs?: number;
|
|
84
85
|
writeFile: ProjectFileWriter;
|
|
85
86
|
recordEdit: (entry: RecordEditInput) => Promise<void>;
|
|
86
87
|
rewrite: (original: string) => Promise<{ disk: string; after?: string } | null>;
|
|
@@ -16,6 +16,7 @@ interface PendingEdit {
|
|
|
16
16
|
landed: () => Promise<boolean>;
|
|
17
17
|
redraws: Array<() => void>;
|
|
18
18
|
showAgain: (() => void) | null;
|
|
19
|
+
claimsAtBegin: number;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export interface StudioEditInFlight {
|
|
@@ -27,6 +28,11 @@ export interface StudioEditInFlight {
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
const pendingEdits = new Map<Promise<unknown>, PendingEdit>();
|
|
31
|
+
let historyClaims: () => number = () => 0;
|
|
32
|
+
|
|
33
|
+
export function setStudioPendingEditClaimClock(read: (() => number) | null): void {
|
|
34
|
+
historyClaims = read ?? (() => 0);
|
|
35
|
+
}
|
|
30
36
|
const NOT_SAVED = () => Promise.resolve(false);
|
|
31
37
|
let adopting: StudioEditInFlight | null = null;
|
|
32
38
|
|
|
@@ -87,7 +93,13 @@ export function trackStudioPendingEdit(
|
|
|
87
93
|
if (!result) return undefined;
|
|
88
94
|
const promise = Promise.resolve(result);
|
|
89
95
|
if (adopting) return promise;
|
|
90
|
-
pendingEdits.set(promise, {
|
|
96
|
+
pendingEdits.set(promise, {
|
|
97
|
+
revert: null,
|
|
98
|
+
landed: NOT_SAVED,
|
|
99
|
+
redraws: [],
|
|
100
|
+
showAgain: null,
|
|
101
|
+
claimsAtBegin: historyClaims(),
|
|
102
|
+
});
|
|
91
103
|
promise.then(
|
|
92
104
|
() => pendingEdits.delete(promise),
|
|
93
105
|
() => pendingEdits.delete(promise),
|
|
@@ -162,6 +174,7 @@ export function beginStudioPendingEdit(revert: StudioEditRevert | null) {
|
|
|
162
174
|
export function paintBackNewestStudioPendingEdit(): {
|
|
163
175
|
showAgain: () => void;
|
|
164
176
|
landed: () => Promise<boolean>;
|
|
177
|
+
claimsAtBegin: number;
|
|
165
178
|
} | null {
|
|
166
179
|
const newest = [...pendingEdits.values()].at(-1);
|
|
167
180
|
const revert = newest?.revert;
|
|
@@ -174,6 +187,7 @@ export function paintBackNewestStudioPendingEdit(): {
|
|
|
174
187
|
for (const redraw of newest.redraws.splice(0)) redraw();
|
|
175
188
|
},
|
|
176
189
|
landed: newest.landed,
|
|
190
|
+
claimsAtBegin: newest.claimsAtBegin,
|
|
177
191
|
};
|
|
178
192
|
}
|
|
179
193
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/player/lib/time.ts","../src/player/store/liveTime.ts","../src/player/store/playerStore.ts","../src/player/store/playerStoreDevHandle.ts","../src/player/store/playerStoreSelection.ts","../src/components/dock/panelRegistry.ts","../src/components/dock/dockLayoutSchema.ts","../src/utils/studioUiPreferences.ts","../src/player/components/timelineZoom.ts","../src/player/store/keyframeSlice.ts","../src/player/store/automationSelectionSlice.ts","../src/player/store/editingModeSlice.ts","../src/player/store/timelineFocusState.ts","../src/player/lib/thumbnailPolicy.ts","../src/player/store/thumbnailSlice.ts","../src/player/store/readinessSlice.ts","../src/player/store/rangeSelectionSlice.ts","../src/player/store/timelineResetState.ts","../src/player/components/Player.tsx","../src/hooks/useMountEffect.ts","../src/hooks/previewVariablesStore.ts"],"sourcesContent":["export const STUDIO_PREVIEW_FPS = 30;\n\nexport const clampToDuration = (time: number, duration: number) =>\n Math.max(0, duration > 0 ? Math.min(duration, time) : time);\n\nexport function formatTime(time: number): string {\n if (!Number.isFinite(time) || time < 0) return \"00:00\";\n const mins = Math.floor(time / 60);\n const secs = Math.floor(time % 60);\n // Zero-pad minutes as well as seconds so every readout is a stable-width\n // MM:SS (e.g. \"00:44\", not \"0:44\"). Minutes past 99 keep their extra digits\n // (\"120:00\"); an hours-style H:MM:SS grouping is applied by callers that need\n // it (see formatTimelineTickLabel), never here.\n return `${mins.toString().padStart(2, \"0\")}:${secs.toString().padStart(2, \"0\")}`;\n}\n\nexport function secondsToFrame(time: number, fps = STUDIO_PREVIEW_FPS): number {\n if (!Number.isFinite(time) || time <= 0) return 0;\n if (!Number.isFinite(fps) || fps <= 0) return 0;\n return Math.round(time * fps);\n}\n\nexport function frameToSeconds(frame: number, fps = STUDIO_PREVIEW_FPS): number {\n if (!Number.isFinite(frame) || frame <= 0) return 0;\n if (!Number.isFinite(fps) || fps <= 0) return 0;\n return frame / fps;\n}\n\nexport function stepFrameTime(time: number, deltaFrames: number, fps = STUDIO_PREVIEW_FPS): number {\n const currentFrame = secondsToFrame(time, fps);\n const nextFrame = Math.max(0, currentFrame + deltaFrames);\n return frameToSeconds(nextFrame, fps);\n}\n\nexport function formatFrameTime(time: number, duration: number, fps = STUDIO_PREVIEW_FPS): string {\n const currentFrame = secondsToFrame(time, fps);\n const totalFrames = secondsToFrame(duration, fps);\n return `${currentFrame}f / ${totalFrames}f`;\n}\n","// ponytail: Playback RAF updates the playhead/time display without per-frame React renders.\ntype TimeListener = (time: number) => void;\nconst timeListeners = new Set<TimeListener>();\nlet latestTime = 0;\nlet seekCount = 0;\n\nexport const liveTime = {\n notify: (time: number) => {\n latestTime = time;\n timeListeners.forEach((listener) => listener(time));\n },\n notifySeek: (time: number) => {\n seekCount += 1;\n liveTime.notify(time);\n },\n seekCount: () => seekCount,\n latest: () => latestTime,\n subscribe: (listener: TimeListener) => {\n timeListeners.add(listener);\n return () => timeListeners.delete(listener);\n },\n};\n","import { create } from \"zustand\";\nimport { attachPlayerStoreDevHandle } from \"./playerStoreDevHandle\";\nimport { nextSelectionSet, revealTargetsSelection } from \"./playerStoreSelection\";\nimport type { MusicBeatAnalysis } from \"@hyperframes/core/beats\";\nimport type { BeatEditState } from \"../../utils/beatEditing\";\nimport type { ClipManifestClip } from \"../lib/playbackTypes\";\nimport {\n readStudioUiPreferences,\n writeStudioUiPreferences,\n type TimelineTimeDisplayMode,\n} from \"../../utils/studioUiPreferences\";\nimport { clampTimelineZoomPercent, computePinnedZoomPercent } from \"../components/timelineZoom\";\nimport { createKeyframeSlice, type KeyframeSlice } from \"./keyframeSlice\";\nimport {\n createAutomationSelectionSlice,\n type AutomationSelectionSlice,\n} from \"./automationSelectionSlice\";\nimport { createEditingModeSlice, type EditingModeSlice } from \"./editingModeSlice\";\nimport { createTimelineFocusRequest, type TimelineFocusRequest } from \"./timelineFocusState\";\nimport { createThumbnailSlice, type ThumbnailSlice } from \"./thumbnailSlice\";\nimport { createPlaybackReadinessSlice } from \"./readinessSlice\";\nimport { createRangeSelectionSlice, type RangeSelectionSlice } from \"./rangeSelectionSlice\";\nimport { createTimelineResetState } from \"./timelineResetState\";\nexport type { KeyframeCacheEntry } from \"./keyframeSlice\";\nexport { liveTime } from \"./liveTime\";\nimport { liveTime } from \"./liveTime\";\nexport { createTimelineResetState };\n\nimport type {\n TimelineElement,\n TimelineElementPatch,\n SubCompositionHostState,\n} from \"./timelineElement\";\n\nexport type { TimelineElement, SubCompositionHostState };\nexport type ZoomMode = \"fit\" | \"manual\";\ntype TimelineTool = \"select\" | \"razor\";\n\nexport interface SelectElementOptions {\n preserveSet?: boolean;\n}\n\nfunction resolveElementSelection(\n ids: Iterable<string>,\n anchor?: string | null,\n): { selectedElementIds: Set<string>; selectedElementId: string | null } {\n const selectedElementIds = new Set(ids);\n if (selectedElementIds.size === 0) {\n return { selectedElementIds, selectedElementId: null };\n }\n if (anchor && selectedElementIds.has(anchor)) {\n return { selectedElementIds, selectedElementId: anchor };\n }\n return {\n selectedElementIds,\n selectedElementId: selectedElementIds.values().next().value ?? null,\n };\n}\n\ntype PlayerStoreSlices = KeyframeSlice &\n AutomationSelectionSlice &\n ThumbnailSlice &\n EditingModeSlice &\n ReturnType<typeof createPlaybackReadinessSlice> &\n RangeSelectionSlice;\ninterface PlayerState extends PlayerStoreSlices {\n isPlaying: boolean;\n currentTime: number;\n duration: number;\n /** Increments exactly once when the Studio switches to a different project. */\n timelineSessionEpoch: number;\n /** Project owning the current timeline session; null outside a project-scoped reset. */\n timelineProjectId: string | null;\n /** True while a beat dot is being dragged — hides the playhead guideline. */\n beatDragging: boolean;\n elements: TimelineElement[];\n selectedElementId: string | null;\n playbackRate: number;\n audioMuted: boolean;\n audioVolume: number;\n loopEnabled: boolean;\n /** Timeline zoom: 'fit' auto-scales to viewport, 'manual' uses manualZoomPercent */\n zoomMode: ZoomMode;\n /** Timeline zoom percent relative to the fit width when in manual mode */\n manualZoomPercent: number;\n userZoomCount: number;\n /**\n * Bumped on every live z-index edit (handleDomZIndexReorderCommit apply AND\n * rollback). Flashless z commits (skipReload) never reload the iframe or\n * bump refreshKey, so DOM-derived views (the Layers panel's z-sorted tree)\n * subscribe to this to re-read the live DOM while playback is paused.\n */\n zEditVersion: number;\n /** Work-area in-point (seconds). When set, loop starts here and A jumps here. */\n inPoint: number | null;\n /** Work-area out-point (seconds). When set, loop ends here and E jumps here. */\n outPoint: number | null;\n\n activeTool: TimelineTool;\n setActiveTool: (tool: TimelineTool) => void;\n selectLeftward: () => void;\n selectRightward: () => void;\n\n /** Tween-relative percentage of the last-clicked keyframe diamond. Operations\n * (drag, resize, rotate) target this instead of recomputing from playhead. */\n activeKeyframePct: number | null;\n setActiveKeyframePct: (pct: number | null) => void;\n\n /** Multi-select: additional selected elements beyond selectedElementId. */\n selectedElementIds: Set<string>;\n clearSelectedElementIds: () => void;\n /** Replace the whole multi-selection at once (marquee live updates). */\n setSelectedElementIds: (ids: Set<string>) => void;\n /** Timeline magnet toggle — when false, clip drags/trims/drops never snap. */\n timelineSnapEnabled: boolean;\n setTimelineSnapEnabled: (enabled: boolean) => void;\n /** Keeps the main track gapless on delete; distinct from the magnet above. */\n rippleEditEnabled: boolean;\n setRippleEditEnabled: (enabled: boolean) => void;\n /** Transport + ruler readout: timecode (\"time\") or frame number (\"frame\"). */\n timeDisplayMode: TimelineTimeDisplayMode;\n setTimeDisplayMode: (mode: TimelineTimeDisplayMode) => void;\n /** Pin the timeline zoom to its current scale before a duration change, so\n * it stops rescaling every clip. No-op once already pinned. */\n pinTimelineZoom: (currentPixelsPerSecond: number, fitPixelsPerSecond: number) => void;\n /** The timeline's live pixels-per-second + fit basis, published by <Timeline>. */\n timelinePps: number;\n timelineFitPps: number;\n setTimelineScale: (pps: number, fitPps: number) => void;\n setSelection: (ids: Iterable<string>, anchor?: string | null) => void;\n addSelectedElementId: (id: string) => void;\n toggleSelectedElementId: (id: string) => void;\n clearSelection: () => void;\n\n setIsPlaying: (playing: boolean) => void;\n setCurrentTime: (time: number) => void;\n setDuration: (duration: number) => void;\n setPlaybackRate: (rate: number) => void;\n setAudioMuted: (muted: boolean) => void;\n setAudioVolume: (volume: number) => void;\n setLoopEnabled: (enabled: boolean) => void;\n setBeatDragging: (dragging: boolean) => void;\n setElements: (elements: TimelineElement[]) => void;\n setSelectedElementId: (id: string | null, options?: SelectElementOptions) => void;\n /** Move the selection anchor within an active multi-selection without collapsing it. */\n setSelectionAnchor: (id: string | null) => void;\n updateElement: (elementId: string, updates: TimelineElementPatch) => void;\n setZoomMode: (mode: ZoomMode) => void;\n setManualZoomPercent: (percent: number) => void;\n bumpZEditVersion: () => void;\n setInPoint: (time: number | null) => void;\n setOutPoint: (time: number | null) => void;\n /** Owns the hard project boundary; repeated calls for one project are no-ops. */\n beginTimelineSession: (projectId: string) => void;\n /** Clears project data without creating a new hard-project session. */\n reset: () => void;\n\n /** Request a seek from outside the player loop (e.g. Layers panel);\n * useTimelinePlayer subscribes and calls adapter.seek() + liveTime.notify(). */\n requestedSeekTime: number | null;\n requestSeek: (time: number) => void;\n clearSeekRequest: () => void;\n\n /** Request the transport start or stop from outside the player loop: the FX\n * rack starts playback to audition a preset (silent while paused) and\n * restores the playhead on leave, without costing the author their place.\n * A nonce, not a bare boolean, so two hovers in a row both register. */\n playbackRequest: { playing: boolean; returnTo: number | null; nonce: number } | null;\n requestPlayback: (playing: boolean, returnTo?: number | null) => void;\n clearPlaybackRequest: () => void;\n\n /**\n * Request the timeline to scroll a clip into view (e.g. clicking an\n * already-added asset card in the sidebar). Consumed and cleared by\n * useTimelineRevealClip. The nonce makes repeat requests for the same\n * clip observable so a second click re-reveals after the user scrolls away.\n */\n clipRevealRequest: { elementId: string; nonce: number } | null;\n requestClipReveal: (elementId: string) => void;\n clearClipRevealRequest: () => void;\n\n timelineFocus: TimelineFocusRequest | null;\n timelineFocusNonce: number;\n requestTimelineFocus: (id: string) => void;\n clearTimelineFocus: (nonce: number) => void;\n\n lintFindingsByElement: Map<string, { count: number; messages: string[] }>;\n setLintFindingsByElement: (map: Map<string, { count: number; messages: string[] }>) => void;\n\n beatAnalysis: MusicBeatAnalysis | null;\n setBeatAnalysis: (analysis: MusicBeatAnalysis | null) => void;\n\n /** User edits (add/move/delete) layered over the detected beat grid. */\n beatEdits: BeatEditState | null;\n setBeatEdits: (edits: BeatEditState | null) => void;\n /** Undo/redo stacks for beat edits (in-memory, session-only). */\n beatUndo: BeatHistoryEntry[];\n beatRedo: BeatHistoryEntry[];\n commitBeatEdits: (next: BeatEditState | null, label: string) => void;\n undoBeatEdits: () => string | null;\n redoBeatEdits: () => string | null;\n resetBeatHistory: () => void;\n beatPersist: (() => void) | null;\n setBeatPersist: (fn: (() => void) | null) => void;\n\n clipManifest: ClipManifestClip[] | null;\n setClipManifest: (clips: ClipManifestClip[] | null) => void;\n clipParentMap: Map<string, string>;\n setClipParentMap: (map: Map<string, string>) => void;\n /**\n * Sub-composition DOM descendants (groups + their children) that have no\n * `data-start`, so they're absent from the clip manifest/tree. Collected\n * studio-side from the live preview so the timeline can expand a sub-comp row\n * to show its DOM-only children. Keeps the manifest lean (timed clips only).\n */\n domClipChildren: DomClipChild[];\n setDomClipChildren: (children: DomClipChild[]) => void;\n /**\n * Host-element state for every id'd element inside a sub-composition, keyed by\n * dom id. Collected from the live preview because it is the only place that\n * sees it: these elements are filtered out of `elements` before the flat store\n * is built, and the clip manifest carries timing, not attributes.\n */\n subCompositionHostState: Map<string, SubCompositionHostState>;\n setSubCompositionHostState: (state: Map<string, SubCompositionHostState>) => void;\n}\n\n/** A sub-comp DOM-only timeline child (no data-start) and its nesting context. */\nexport interface DomClipChild {\n id: string;\n parentId: string;\n /** The manifest sub-comp host clip id this descendant ultimately lives under. */\n hostId: string;\n label: string;\n stackingContextId: string;\n /**\n * The child's audio-group state, read off its live element during the DOM\n * walk — the only place that sees it. A sub-composition can declare a group\n * and its members entirely within itself, and those members never reach the\n * flat store, so an expanded child has no twin to inherit membership from.\n */\n audioGroup?: string;\n audioGroupLabel?: string;\n audioGroupVolume?: number;\n audioGroupHidden?: boolean;\n audioGroupFxChain?: string;\n /** The group element's `data-automation`, mirrored the same way. */\n audioGroupAutomation?: string;\n}\n\ninterface BeatHistoryEntry {\n restore: BeatEditState | null;\n at: number;\n label: string;\n}\n\n/** Selects like the marquee: the primary first, so its resets run, then the whole set. */\nfunction selectAroundPlayhead(\n state: PlayerState,\n keep: (el: TimelineElement, playhead: number) => boolean,\n): void {\n const playhead = state.isPlaying ? liveTime.latest() : state.currentTime;\n const ids = state.elements.filter((el) => keep(el, playhead)).map((el) => el.key ?? el.id);\n state.setSelectedElementId(ids[0] ?? null);\n state.setSelectedElementIds(new Set(ids));\n}\n\nexport const usePlayerStore = create<PlayerState>((set, get) => ({\n isPlaying: false,\n currentTime: 0,\n duration: 0,\n timelineSessionEpoch: 0,\n timelineProjectId: null,\n beatDragging: false,\n elements: [],\n selectedElementId: null,\n playbackRate: readStudioUiPreferences().playbackRate ?? 1,\n audioMuted: readStudioUiPreferences().audioMuted ?? false,\n audioVolume: readStudioUiPreferences().audioVolume ?? 1,\n loopEnabled: false,\n zoomMode: \"fit\",\n manualZoomPercent: 100,\n userZoomCount: 0,\n zEditVersion: 0,\n timelinePps: 100,\n timelineFitPps: 100,\n inPoint: null,\n outPoint: null,\n\n activeTool: \"select\",\n setActiveTool: (tool) => set({ activeTool: tool }),\n selectLeftward: () => selectAroundPlayhead(get(), (el, t) => el.start < t),\n selectRightward: () => selectAroundPlayhead(get(), (el, t) => el.start + el.duration > t),\n\n ...createKeyframeSlice(set, () => ({\n timelineProjectId: get().timelineProjectId,\n timelineSessionEpoch: get().timelineSessionEpoch,\n })),\n ...createThumbnailSlice(set),\n\n ...createAutomationSelectionSlice(set),\n ...createEditingModeSlice(set),\n ...createRangeSelectionSlice(),\n ...createPlaybackReadinessSlice(set),\n\n activeKeyframePct: null,\n setActiveKeyframePct: (pct) => set({ activeKeyframePct: pct }),\n\n selectedElementIds: new Set<string>(),\n setSelection: (ids, anchor) => set(resolveElementSelection(ids, anchor)),\n addSelectedElementId: (id: string) =>\n set((s) => {\n const next = new Set(s.selectedElementIds);\n next.add(id);\n return resolveElementSelection(next, s.selectedElementId);\n }),\n toggleSelectedElementId: (id: string) =>\n set((s) => {\n const next = new Set(s.selectedElementIds);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return resolveElementSelection(next, s.selectedElementId);\n }),\n clearSelection: () => set({ selectedElementId: null, selectedElementIds: new Set() }),\n\n requestedSeekTime: null,\n requestSeek: (time) => set({ requestedSeekTime: time }),\n clearSeekRequest: () => set({ requestedSeekTime: null }),\n\n playbackRequest: null,\n requestPlayback: (playing, returnTo = null) =>\n set((s) => ({\n playbackRequest: { playing, returnTo, nonce: (s.playbackRequest?.nonce ?? 0) + 1 },\n })),\n clearPlaybackRequest: () => set({ playbackRequest: null }),\n\n clipRevealRequest: null,\n requestClipReveal: (elementId) =>\n set((s) => ({\n clipRevealRequest: { elementId, nonce: (s.clipRevealRequest?.nonce ?? 0) + 1 },\n })),\n clearClipRevealRequest: () => set({ clipRevealRequest: null }),\n\n timelineFocus: null,\n timelineFocusNonce: 0,\n requestTimelineFocus: (id) =>\n set((s) => {\n const nonce = s.timelineFocusNonce + 1;\n return {\n timelineFocusNonce: nonce,\n timelineFocus: createTimelineFocusRequest(\n id,\n s.timelineProjectId,\n s.timelineSessionEpoch,\n nonce,\n ),\n };\n }),\n clearTimelineFocus: (nonce) =>\n set((s) => (s.timelineFocus?.nonce === nonce ? { timelineFocus: null } : s)),\n\n lintFindingsByElement: new Map(),\n setLintFindingsByElement: (map) => set({ lintFindingsByElement: map }),\n\n beatAnalysis: null,\n setBeatAnalysis: (analysis) => set({ beatAnalysis: analysis }),\n\n beatEdits: null,\n setBeatEdits: (edits) => set({ beatEdits: edits }),\n\n beatUndo: [],\n beatRedo: [],\n beatPersist: null,\n setBeatPersist: (fn) => set({ beatPersist: fn }),\n commitBeatEdits: (next, label) => {\n set((s) => ({\n beatEdits: next,\n beatUndo: [...s.beatUndo, { restore: s.beatEdits, at: Date.now(), label }],\n beatRedo: [],\n }));\n get().beatPersist?.();\n },\n undoBeatEdits: () => {\n const s = get();\n const entry = s.beatUndo[s.beatUndo.length - 1];\n if (!entry) return null;\n set({\n beatEdits: entry.restore,\n beatUndo: s.beatUndo.slice(0, -1),\n beatRedo: [...s.beatRedo, { restore: s.beatEdits, at: entry.at, label: entry.label }],\n });\n get().beatPersist?.();\n return entry.label;\n },\n resetBeatHistory: () => set({ beatUndo: [], beatRedo: [] }),\n redoBeatEdits: () => {\n const s = get();\n const entry = s.beatRedo[s.beatRedo.length - 1];\n if (!entry) return null;\n set({\n beatEdits: entry.restore,\n beatRedo: s.beatRedo.slice(0, -1),\n beatUndo: [...s.beatUndo, { restore: s.beatEdits, at: entry.at, label: entry.label }],\n });\n get().beatPersist?.();\n return entry.label;\n },\n\n clipManifest: null,\n setClipManifest: (clips) => set({ clipManifest: clips }),\n clipParentMap: new Map(),\n setClipParentMap: (map) => set({ clipParentMap: map }),\n domClipChildren: [],\n setDomClipChildren: (children) => set({ domClipChildren: children }),\n subCompositionHostState: new Map(),\n setSubCompositionHostState: (state) => set({ subCompositionHostState: state }),\n\n setIsPlaying: (playing) => {\n if (get().isPlaying === playing) return;\n set({ isPlaying: playing });\n },\n setPlaybackRate: (rate) => {\n writeStudioUiPreferences({ playbackRate: rate });\n set({ playbackRate: rate });\n },\n setAudioMuted: (muted) => {\n writeStudioUiPreferences({ audioMuted: muted });\n set({ audioMuted: muted });\n },\n setAudioVolume: (volume) => {\n const nextVolume = Number.isFinite(volume) ? Math.max(0, Math.min(1, volume)) : 1;\n writeStudioUiPreferences({ audioVolume: nextVolume });\n set({ audioVolume: nextVolume });\n },\n setLoopEnabled: (enabled) => set({ loopEnabled: enabled }),\n setZoomMode: (mode) => set({ zoomMode: mode }),\n clearSelectedElementIds: () => set({ selectedElementIds: new Set() }),\n setSelectedElementIds: (ids: Set<string>) => set({ selectedElementIds: new Set(ids) }),\n timelineSnapEnabled: readStudioUiPreferences().timelineSnapEnabled ?? true,\n setTimelineSnapEnabled: (enabled) => {\n writeStudioUiPreferences({ timelineSnapEnabled: enabled });\n set({ timelineSnapEnabled: enabled });\n },\n rippleEditEnabled: readStudioUiPreferences().rippleEditEnabled ?? true, // default on\n setRippleEditEnabled: (enabled) => {\n writeStudioUiPreferences({ rippleEditEnabled: enabled });\n set({ rippleEditEnabled: enabled });\n },\n timeDisplayMode: readStudioUiPreferences().timeDisplayMode ?? \"time\",\n setTimeDisplayMode: (mode) => {\n writeStudioUiPreferences({ timeDisplayMode: mode });\n set({ timeDisplayMode: mode });\n },\n pinTimelineZoom: (currentPixelsPerSecond, fitPixelsPerSecond) =>\n set((s) => {\n // Already pinned (or the user manually zoomed) — never clobber that.\n if (s.zoomMode === \"manual\") return {};\n const percent = computePinnedZoomPercent(currentPixelsPerSecond, fitPixelsPerSecond);\n writeStudioUiPreferences({\n timelineZoomMode: \"manual\",\n timelineManualZoomPercent: percent,\n });\n return { zoomMode: \"manual\", manualZoomPercent: percent };\n }),\n setTimelineScale: (pps, fitPps) => {\n const state = get();\n if (state.timelinePps === pps && state.timelineFitPps === fitPps) return;\n set({ timelinePps: pps, timelineFitPps: fitPps });\n },\n setInPoint: (time) =>\n set((state) => {\n const t = time !== null && Number.isFinite(time) ? time : null;\n return {\n inPoint: t,\n outPoint:\n t !== null && state.outPoint !== null && t >= state.outPoint ? null : state.outPoint,\n // Setting a work-area marker implies the user wants playback bounded by it.\n // Auto-enable loop so the playhead respects the marker instead of running past.\n loopEnabled: t !== null ? true : state.loopEnabled,\n };\n }),\n setOutPoint: (time) =>\n set((state) => {\n const t = time !== null && Number.isFinite(time) ? time : null;\n return {\n outPoint: t,\n inPoint: t !== null && state.inPoint !== null && t <= state.inPoint ? null : state.inPoint,\n loopEnabled: t !== null ? true : state.loopEnabled,\n };\n }),\n setManualZoomPercent: (percent) =>\n set((state) => ({\n manualZoomPercent: clampTimelineZoomPercent(percent, state.timelineFitPps),\n })),\n bumpZEditVersion: () => set((state) => ({ zEditVersion: state.zEditVersion + 1 })),\n setCurrentTime: (time) => set({ currentTime: Number.isFinite(time) ? time : 0 }),\n setDuration: (duration) => set({ duration: Number.isFinite(duration) ? duration : 0 }),\n setBeatDragging: (dragging) => set({ beatDragging: dragging }),\n setElements: (elements) => set({ elements }),\n // A genuine single selection: always collapse the set to just this element. User\n // intent (timeline click, preview click via applyDomSelection) flows here; DOM sync\n // echoes that must preserve a group go through setSelectionAnchor instead.\n setSelectedElementId: (id, options) =>\n set((s) => {\n const selectedElementIds = nextSelectionSet(s.selectedElementIds, id, options?.preserveSet);\n // Selecting a different element drops any active keyframe selection — otherwise\n // a stale activeKeyframePct from a prior diamond click would force the next drag\n // to \"modify\" a keyframe on the new element. A diamond click sets the pct AFTER\n // calling setSelectedElementId, so this never clobbers a genuine keyframe select.\n // A reveal request survives the selection it is FOR. `openClipFxRack`\n // raises the request and then selects the clip asynchronously, so the\n // selection lands afterwards and used to clear the very request that\n // caused it — the panel then read null and the section never opened.\n // Any OTHER selection still drops it: a request aimed elsewhere is stale.\n //\n // Compared across the ID-SPACE BOUNDARY, which is why this needs saying:\n // a request carries the BARE dom id (`runtimeAudioId`, because the panel\n // and the runtime speak that), while this store's ids are\n // `sourceFile#domId`. A direct `===` was silently never true — the exact\n // shape of failure the id-space split produces.\n const revealSurvives = revealTargetsSelection(s.revealedAudioFxTarget, id);\n return id !== s.selectedElementId\n ? {\n selectedElementId: id,\n selectedElementIds,\n activeKeyframePct: null,\n motionPathArmed: false,\n focusedEaseSegment: null,\n ...(revealSurvives ? {} : { revealedAudioFxTarget: null }),\n }\n : { selectedElementId: id, selectedElementIds };\n }),\n // Move the anchor within an active multi-selection WITHOUT collapsing it — used by\n // DOM->store sync echoes while a group gesture re-patches the preview. A non-member\n // id is treated as a genuine new single selection.\n setSelectionAnchor: (id) =>\n set((s) => {\n if (id != null && s.selectedElementIds.size > 1 && s.selectedElementIds.has(id)) {\n return {\n selectedElementId: id,\n focusedEaseSegment: id === s.selectedElementId ? s.focusedEaseSegment : null,\n };\n }\n return {\n selectedElementId: id,\n selectedElementIds: id ? new Set([id]) : new Set<string>(),\n focusedEaseSegment: id === s.selectedElementId ? s.focusedEaseSegment : null,\n };\n }),\n updateElement: (elementId, updates) =>\n set((state) => ({\n elements: state.elements.map((el) =>\n (el.key ?? el.id) === elementId ? { ...el, ...updates } : el,\n ),\n })),\n // UI preferences intentionally survive reset. So do timelineSessionEpoch and\n // focusedEaseRequestNonce: the epoch advances only when project identity\n // changes, while a monotonic nonce prevents collisions with stale consumers.\n beginTimelineSession: (projectId) =>\n set((state) => {\n if (state.timelineProjectId === projectId) return state;\n return {\n ...createTimelineResetState(),\n timelineSessionEpoch: state.timelineSessionEpoch + 1,\n timelineProjectId: projectId,\n };\n }),\n reset: () => set(createTimelineResetState()),\n}));\n\nattachPlayerStoreDevHandle(usePlayerStore);\n\nexport function isPreviewBooted(projectId: string): boolean {\n const { previewBooted, timelineProjectId } = usePlayerStore.getState();\n return previewBooted && timelineProjectId === projectId;\n}\n\n/** True once projectId's live preview has booted, false once another project replaces it.\n * Open-time work the first frame does not need (server parses, lint) waits on it. */\nexport function whenPreviewBooted(projectId: string): Promise<boolean> {\n const openedFrom = usePlayerStore.getState().timelineProjectId;\n let seen = false;\n const settle = (state: PlayerState): boolean | null => {\n if (state.timelineProjectId === projectId) {\n seen = true;\n return state.previewBooted ? true : null;\n }\n return seen || state.timelineProjectId !== openedFrom ? false : null;\n };\n return new Promise((resolve) => {\n const now = settle(usePlayerStore.getState());\n if (now !== null) return resolve(now);\n const stop = usePlayerStore.subscribe((state) => {\n const result = settle(state);\n if (result === null) return;\n stop();\n resolve(result);\n });\n });\n}\n","/**\n * A console handle on the live store, dev builds only.\n *\n * Split out of `playerStore.ts` to keep it under the studio's 600-line cap. The\n * dev check is its own function because `import.meta.env` is absent under\n * Turbopack and other non-Vite bundlers, where reading it throws.\n */\n\nfunction isDevBuild(): boolean {\n try {\n return import.meta.env.DEV === true;\n } catch {\n return false;\n }\n}\n\n/** Expose `store` as `window.__playerStore` for dumping live Studio state\n * during bug-bash reproduction. No-op outside a dev build or a browser. */\nexport function attachPlayerStoreDevHandle(store: unknown): void {\n if (!isDevBuild() || typeof window === \"undefined\") return;\n (window as unknown as { __playerStore?: unknown }).__playerStore = store;\n}\n","/**\n * Selection-set arithmetic for the player store, and the reveal request's\n * cross-id-space match.\n *\n * Its own module so `playerStore.ts` stays under the studio's 600-line cap.\n */\n\nimport { splitTimelineElementKey } from \"../lib/timelineElementHelpers\";\n\n/**\n * The id set a selection change leaves behind.\n *\n * `preserveSet` means \"keep the multi-selection if this id is already in it\" —\n * a DOM→store echo re-announcing a member must not collapse the set — and\n * anything else is a genuine single selection.\n */\nexport function nextSelectionSet(\n current: ReadonlySet<string>,\n id: string | null,\n preserveSet: boolean | undefined,\n): Set<string> {\n if (preserveSet) return id && current.has(id) ? new Set(current) : new Set<string>();\n return id ? new Set([id]) : new Set<string>();\n}\n\n/**\n * Is a pending reveal request aimed at the element being selected?\n *\n * Compared across the ID-SPACE BOUNDARY, which is why it is a named function:\n * a request carries the BARE dom id (`runtimeAudioId` — the panel and the\n * runtime speak that), while the store's ids are `sourceFile#domId`. A direct\n * `===` between the two is silently never true, which is the exact shape of\n * failure the split produces and how the reveal came to be dead.\n */\nexport function revealTargetsSelection(\n request: { elementKey: string } | null,\n id: string | null,\n): boolean {\n if (!request || id === null) return false;\n return request.elementKey === splitTimelineElementKey(id).domId;\n}\n","import type { Direction } from \"dockview-react\";\n\nexport const PANEL_IDS = [\n \"preview\",\n \"timeline\",\n \"compositions\",\n \"assets\",\n \"code\",\n \"catalog\",\n \"design\",\n \"layers\",\n \"renders\",\n \"variables\",\n \"slideshow\",\n] as const;\n\nexport type PanelId = (typeof PANEL_IDS)[number];\nexport type PanelZone = \"left\" | \"center\" | \"right\";\n\nexport interface PanelDefinition {\n title: string;\n zone: PanelZone;\n /** Where Window > <panel> puts it when it has no saved place: next to `near`. */\n reopen: { near: PanelId; direction: Direction };\n /** Content stays mounted while its tab is hidden (the preview iframe must not reload). */\n keepMounted?: true;\n}\n\nexport const PANEL_DEFINITIONS = {\n preview: {\n title: \"Preview\",\n zone: \"center\",\n reopen: { near: \"timeline\", direction: \"above\" },\n keepMounted: true,\n },\n timeline: {\n title: \"Timeline\",\n zone: \"center\",\n reopen: { near: \"preview\", direction: \"below\" },\n keepMounted: true,\n },\n compositions: {\n title: \"Compositions\",\n zone: \"left\",\n reopen: { near: \"preview\", direction: \"left\" },\n },\n assets: { title: \"Assets\", zone: \"left\", reopen: { near: \"compositions\", direction: \"within\" } },\n code: { title: \"Code\", zone: \"left\", reopen: { near: \"compositions\", direction: \"within\" } },\n catalog: {\n title: \"Catalog\",\n zone: \"left\",\n reopen: { near: \"compositions\", direction: \"within\" },\n },\n design: { title: \"Design\", zone: \"right\", reopen: { near: \"preview\", direction: \"right\" } },\n layers: { title: \"Layers\", zone: \"right\", reopen: { near: \"design\", direction: \"within\" } },\n renders: { title: \"Renders\", zone: \"right\", reopen: { near: \"design\", direction: \"within\" } },\n variables: { title: \"Variables\", zone: \"right\", reopen: { near: \"design\", direction: \"within\" } },\n slideshow: { title: \"Slideshow\", zone: \"right\", reopen: { near: \"design\", direction: \"within\" } },\n} as const satisfies Record<PanelId, PanelDefinition>;\n\nexport function isPanelId(value: unknown): value is PanelId {\n return typeof value === \"string\" && (PANEL_IDS as readonly string[]).includes(value);\n}\n\nexport function panelsInZone(zone: PanelZone): PanelId[] {\n return PANEL_IDS.filter((id) => PANEL_DEFINITIONS[id].zone === zone);\n}\n","import type { SerializedDockview } from \"dockview-react\";\nimport { PANEL_IDS, isPanelId, type PanelId } from \"./panelRegistry\";\n\nexport const DOCK_PANEL_COMPONENT = \"panel\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction collectLeafViews(node: unknown, into: unknown[]): boolean {\n if (!isRecord(node)) return false;\n if (node.type === \"leaf\") {\n if (!isRecord(node.data) || !Array.isArray(node.data.views)) return false;\n into.push(...node.data.views);\n return true;\n }\n if (node.type === \"branch\") {\n return Array.isArray(node.data) && node.data.every((child) => collectLeafViews(child, into));\n }\n return false;\n}\n\nfunction hasGridShape(grid: Record<string, unknown>): boolean {\n const oriented = grid.orientation === \"HORIZONTAL\" || grid.orientation === \"VERTICAL\";\n return oriented && typeof grid.width === \"number\" && typeof grid.height === \"number\";\n}\n\nfunction panelsAreRegistered(\n panels: Record<string, unknown>,\n allowed: readonly PanelId[],\n): boolean {\n const ids = Object.keys(panels);\n return (\n ids.length > 0 &&\n ids.every((id) => {\n const state = panels[id];\n return (\n isPanelId(id) &&\n allowed.includes(id) &&\n isRecord(state) &&\n state.id === id &&\n state.contentComponent === DOCK_PANEL_COMPONENT\n );\n })\n );\n}\n\nfunction collectFloatingViews(floating: unknown, into: unknown[]): boolean {\n if (floating === undefined) return true;\n if (!Array.isArray(floating)) return false;\n return floating.every((group) => {\n if (!isRecord(group) || !isRecord(group.data) || !Array.isArray(group.data.views)) return false;\n into.push(...group.data.views);\n return true;\n });\n}\n\n/** Parses a stored layout at the trust boundary; null (stale or foreign shape) means use the default preset. */\nexport function parseDockLayout(\n value: unknown,\n allowed: readonly PanelId[] = PANEL_IDS,\n): SerializedDockview | null {\n if (!isRecord(value) || !isRecord(value.grid) || !isRecord(value.panels)) return null;\n const { grid, panels } = value;\n if (!hasGridShape(grid) || !panelsAreRegistered(panels, allowed)) return null;\n\n const views: unknown[] = [];\n if (!collectLeafViews(grid.root, views)) return null;\n if (!collectFloatingViews(value.floatingGroups, views)) return null;\n const everyViewIsAPanel = views.every((id) => typeof id === \"string\" && id in panels);\n const everyPanelIsPlaced = Object.keys(panels).every((id) => views.includes(id));\n return everyViewIsAPanel && everyPanelIsPlaced ? (value as unknown as SerializedDockview) : null;\n}\n","import type { SerializedDockview } from \"dockview-react\";\nimport { parseDockLayout } from \"../components/dock/dockLayoutSchema\";\n\nexport type TimelineTimeDisplayMode = \"time\" | \"frame\";\nexport type StudioTheme = \"light\" | \"dark\";\n\nexport interface StudioUiPreferences {\n timelineVisible?: boolean;\n playbackRate?: number;\n audioMuted?: boolean;\n audioVolume?: number;\n thumbnailMode?: \"adaptive\" | \"hidden\";\n recentBlocks?: string[];\n snapEnabled?: boolean;\n gridVisible?: boolean;\n rulerVisible?: boolean;\n safeMarginsVisible?: boolean;\n gridSpacing?: number;\n snapToGrid?: boolean;\n /** Timeline magnet: snap clip drags/trims/drops to playhead, clip edges, and beats. */\n timelineSnapEnabled?: boolean;\n /** Audio level meters at the timeline's right edge; hidden unless enabled here. */\n audioMetersVisible?: boolean;\n /** Keeps the main track gapless: deleting a clip closes the gap. Distinct\n * from `timelineSnapEnabled` (\"Magnet\", drag/trim snapping). */\n rippleEditEnabled?: boolean;\n /** Transport + ruler readout mode: timecode or frame number. */\n timeDisplayMode?: TimelineTimeDisplayMode;\n /**\n * Timeline zoom mode. Persisted so a zoom PINNED on the first edit survives the\n * post-edit iframe reload — otherwise the store reset to \"fit\" and the duration\n * change rescaled every clip (the blink-fix's rescale symptom).\n */\n timelineZoomMode?: \"fit\" | \"manual\";\n /** Manual timeline zoom percent, paired with `timelineZoomMode: \"manual\"`. */\n timelineManualZoomPercent?: number;\n /**\n * Expose Studio's editing capabilities to an agentic browser as WebMCP tools.\n * Absent means on: the browser still gates every actual call behind its own\n * permission prompt, so \"registered\" is not \"reachable without consent\".\n * Changes take effect on the next Studio reload because registration is\n * intentionally scoped to one mount.\n */\n agentToolsEnabled?: boolean;\n theme?: StudioTheme;\n /** The dock's serialized panel tree; parsed by `parseDockLayout` on read. */\n dockLayout?: SerializedDockview;\n linkedSelectionEnabled?: boolean;\n syncIndicatorsVisible?: boolean;\n}\n\nconst STUDIO_UI_PREFERENCES_KEY = \"hf-studio-ui-preferences\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction getBrowserStorage(): Storage | null {\n if (typeof window === \"undefined\") return null;\n try {\n return window.localStorage;\n } catch {\n return null;\n }\n}\n\nfunction storageKeyFor(projectId: string | null, key: string): string {\n return projectId ? `${key}:${projectId}` : key;\n}\n\n// fallow-ignore-next-line complexity\nfunction readStorage(storage: Storage | null, key: string): StudioUiPreferences {\n if (!storage) return {};\n try {\n const raw = storage.getItem(key);\n if (!raw) return {};\n const parsed: unknown = JSON.parse(raw);\n if (!isRecord(parsed)) return {};\n\n const preferences: StudioUiPreferences = {};\n if (typeof parsed.timelineVisible === \"boolean\") {\n preferences.timelineVisible = parsed.timelineVisible;\n }\n if (typeof parsed.playbackRate === \"number\" && Number.isFinite(parsed.playbackRate)) {\n preferences.playbackRate = parsed.playbackRate;\n }\n if (typeof parsed.audioMuted === \"boolean\") {\n preferences.audioMuted = parsed.audioMuted;\n }\n if (\n typeof parsed.audioVolume === \"number\" &&\n Number.isFinite(parsed.audioVolume) &&\n parsed.audioVolume >= 0 &&\n parsed.audioVolume <= 1\n ) {\n preferences.audioVolume = parsed.audioVolume;\n }\n if (parsed.thumbnailMode === \"adaptive\" || parsed.thumbnailMode === \"hidden\") {\n preferences.thumbnailMode = parsed.thumbnailMode;\n } else if (typeof parsed.thumbnailsEnabled === \"boolean\") {\n preferences.thumbnailMode = parsed.thumbnailsEnabled ? \"adaptive\" : \"hidden\";\n }\n if (Array.isArray(parsed.recentBlocks)) {\n preferences.recentBlocks = parsed.recentBlocks.filter(\n (v: unknown): v is string => typeof v === \"string\",\n );\n }\n if (typeof parsed.snapEnabled === \"boolean\") {\n preferences.snapEnabled = parsed.snapEnabled;\n }\n if (typeof parsed.gridVisible === \"boolean\") {\n preferences.gridVisible = parsed.gridVisible;\n }\n if (typeof parsed.rulerVisible === \"boolean\") {\n preferences.rulerVisible = parsed.rulerVisible;\n }\n if (typeof parsed.safeMarginsVisible === \"boolean\") {\n preferences.safeMarginsVisible = parsed.safeMarginsVisible;\n }\n if (typeof parsed.gridSpacing === \"number\" && Number.isFinite(parsed.gridSpacing)) {\n preferences.gridSpacing = parsed.gridSpacing;\n }\n if (typeof parsed.snapToGrid === \"boolean\") {\n preferences.snapToGrid = parsed.snapToGrid;\n }\n if (typeof parsed.timelineSnapEnabled === \"boolean\") {\n preferences.timelineSnapEnabled = parsed.timelineSnapEnabled;\n }\n if (typeof parsed.audioMetersVisible === \"boolean\") {\n preferences.audioMetersVisible = parsed.audioMetersVisible;\n }\n if (typeof parsed.rippleEditEnabled === \"boolean\") {\n preferences.rippleEditEnabled = parsed.rippleEditEnabled;\n }\n if (parsed.theme === \"light\" || parsed.theme === \"dark\") preferences.theme = parsed.theme;\n if (parsed.timeDisplayMode === \"time\" || parsed.timeDisplayMode === \"frame\") {\n preferences.timeDisplayMode = parsed.timeDisplayMode;\n }\n if (parsed.timelineZoomMode === \"fit\" || parsed.timelineZoomMode === \"manual\") {\n preferences.timelineZoomMode = parsed.timelineZoomMode;\n }\n if (\n typeof parsed.timelineManualZoomPercent === \"number\" &&\n Number.isFinite(parsed.timelineManualZoomPercent)\n ) {\n preferences.timelineManualZoomPercent = parsed.timelineManualZoomPercent;\n }\n if (typeof parsed.agentToolsEnabled === \"boolean\") {\n preferences.agentToolsEnabled = parsed.agentToolsEnabled;\n }\n if (typeof parsed.linkedSelectionEnabled === \"boolean\") {\n preferences.linkedSelectionEnabled = parsed.linkedSelectionEnabled;\n }\n if (typeof parsed.syncIndicatorsVisible === \"boolean\") {\n preferences.syncIndicatorsVisible = parsed.syncIndicatorsVisible;\n }\n const dockLayout = parseDockLayout(parsed.dockLayout);\n if (dockLayout) preferences.dockLayout = dockLayout;\n return preferences;\n } catch {\n return {};\n }\n}\n\n/** `projectId` opts a caller into a per-project entry (falls back once to the\n * shared entry so a project's first read isn't blank). Defaults to `null`:\n * most callers read once at mount, never on a live project switch. */\nexport function readStudioUiPreferences(\n storage: Storage | null = getBrowserStorage(),\n projectId: string | null = null,\n key: string = STUDIO_UI_PREFERENCES_KEY,\n): StudioUiPreferences {\n const scoped = readStorage(storage, storageKeyFor(projectId, key));\n if (!projectId || Object.keys(scoped).length > 0) return scoped;\n return readStorage(storage, key);\n}\n\nexport function writeStudioUiPreferences(\n patch: StudioUiPreferences,\n storage: Storage | null = getBrowserStorage(),\n projectId: string | null = null,\n key: string = STUDIO_UI_PREFERENCES_KEY,\n) {\n if (!storage) return;\n try {\n const next = {\n ...readStudioUiPreferences(storage, projectId, key),\n ...patch,\n };\n storage.setItem(storageKeyFor(projectId, key), JSON.stringify(next));\n } catch {\n /* localStorage may be unavailable or full */\n }\n}\n","import type { ZoomMode } from \"../store/playerStore\";\nimport { STUDIO_PREVIEW_FPS } from \"../lib/time\";\n\nexport const MIN_TIMELINE_ZOOM_PERCENT = 10;\nconst MAX_TIMELINE_FRAME_WIDTH_PX = 48;\n// CapCut-strength steps: one button press / pinch gesture moves the zoom\n// meaningfully (user feedback, twice-doubled: 1.25×/0.8× + 0.0035 felt like\n// \"zooming several times to get anywhere\", then 1.5× + 0.007 still too soft).\n// Kept reciprocal (2 × 0.5 = 1) so in+out round-trips.\nconst ZOOM_OUT_FACTOR = 0.5;\nconst ZOOM_IN_FACTOR = 2;\nconst PINCH_ZOOM_SENSITIVITY = 0.014;\n\nexport function getMaxTimelineZoomPercent(fitPixelsPerSecond: number): number {\n if (!Number.isFinite(fitPixelsPerSecond) || fitPixelsPerSecond <= 0) return 100;\n const frameLevelPixelsPerSecond = STUDIO_PREVIEW_FPS * MAX_TIMELINE_FRAME_WIDTH_PX;\n return Math.max(100, Math.round((frameLevelPixelsPerSecond / fitPixelsPerSecond) * 100));\n}\n\nexport function clampTimelineZoomPercent(percent: number, fitPixelsPerSecond: number): number {\n if (!Number.isFinite(percent)) return 100;\n const maxZoomPercent = getMaxTimelineZoomPercent(fitPixelsPerSecond);\n return Math.max(MIN_TIMELINE_ZOOM_PERCENT, Math.min(maxZoomPercent, Math.round(percent)));\n}\n\nexport function getTimelineZoomPercent(\n zoomMode: ZoomMode,\n manualZoomPercent: number,\n fitPixelsPerSecond: number,\n): number {\n return zoomMode === \"fit\" ? 100 : clampTimelineZoomPercent(manualZoomPercent, fitPixelsPerSecond);\n}\n\n/**\n * The manual-zoom percent that, applied to `fitPixelsPerSecond`, reproduces the\n * CURRENT on-screen pixels-per-second exactly. Used to PIN the timeline zoom on\n * the first edit so a duration change (which recomputes fit-pps) no longer\n * rescales every clip: we switch `zoomMode` to \"manual\" with this percent, so\n * `getTimelinePixelsPerSecond` keeps returning today's pps regardless of the new\n * fit basis.\n *\n * Since `pps = fitPps * (percent / 100)` in manual mode, and while fitting\n * `pps === fitPps`, the pinned percent is `currentPps / fitPps * 100`. Clamped to\n * the manual-zoom range so the pin can't land outside the slider's bounds; falls\n * back to 100 (a no-op pin at the current fit) when either input is unusable.\n */\nexport function computePinnedZoomPercent(\n currentPixelsPerSecond: number,\n fitPixelsPerSecond: number,\n): number {\n if (\n !Number.isFinite(currentPixelsPerSecond) ||\n currentPixelsPerSecond <= 0 ||\n !Number.isFinite(fitPixelsPerSecond) ||\n fitPixelsPerSecond <= 0\n ) {\n return 100;\n }\n return clampTimelineZoomPercent(\n (currentPixelsPerSecond / fitPixelsPerSecond) * 100,\n fitPixelsPerSecond,\n );\n}\n\nexport function getTimelinePixelsPerSecond(\n fitPixelsPerSecond: number,\n zoomMode: ZoomMode,\n manualZoomPercent: number,\n): number {\n if (!Number.isFinite(fitPixelsPerSecond) || fitPixelsPerSecond <= 0) return 100;\n const zoomPercent = getTimelineZoomPercent(zoomMode, manualZoomPercent, fitPixelsPerSecond);\n return zoomMode === \"fit\" ? fitPixelsPerSecond : fitPixelsPerSecond * (zoomPercent / 100);\n}\n\nexport function getNextTimelineZoomPercent(\n direction: \"in\" | \"out\",\n zoomMode: ZoomMode,\n manualZoomPercent: number,\n fitPixelsPerSecond: number,\n): number {\n const current = getTimelineZoomPercent(zoomMode, manualZoomPercent, fitPixelsPerSecond);\n const next = direction === \"in\" ? current * ZOOM_IN_FACTOR : current * ZOOM_OUT_FACTOR;\n return clampTimelineZoomPercent(next, fitPixelsPerSecond);\n}\n\nexport function getPinchTimelineZoomPercent(\n deltaY: number,\n zoomMode: ZoomMode,\n manualZoomPercent: number,\n fitPixelsPerSecond: number,\n): number {\n const current = getTimelineZoomPercent(zoomMode, manualZoomPercent, fitPixelsPerSecond);\n if (!Number.isFinite(deltaY) || deltaY === 0) return current;\n return clampTimelineZoomPercent(\n current * Math.exp(-deltaY * PINCH_ZOOM_SENSITIVITY),\n fitPixelsPerSecond,\n );\n}\n\nconst LOG_MIN = Math.log(MIN_TIMELINE_ZOOM_PERCENT);\n\n/**\n * Maps the frame-level zoom range to a slider position (0–100) using a log scale.\n * Linear would compress the useful low end into a tiny sliver of the slider.\n */\nexport function timelineZoomPercentToSlider(percent: number, fitPixelsPerSecond: number): number {\n const clamped = clampTimelineZoomPercent(percent, fitPixelsPerSecond);\n const logMax = Math.log(getMaxTimelineZoomPercent(fitPixelsPerSecond));\n return ((Math.log(clamped) - LOG_MIN) / (logMax - LOG_MIN)) * 100;\n}\n\n/**\n * Maps a slider position (0–100) to the frame-level zoom range using a log scale.\n * Inverse of `timelineZoomPercentToSlider`.\n */\nexport function timelineSliderToZoomPercent(slider: number, fitPixelsPerSecond: number): number {\n const clampedSlider = Math.max(0, Math.min(100, slider));\n const logMax = Math.log(getMaxTimelineZoomPercent(fitPixelsPerSecond));\n const logValue = LOG_MIN + (clampedSlider / 100) * (logMax - LOG_MIN);\n return clampTimelineZoomPercent(Math.exp(logValue), fitPixelsPerSecond);\n}\n","import type { GsapAnimation } from \"@hyperframes/core/gsap-parser\";\nimport type { StoreApi } from \"zustand\";\nimport type { AnimationKeyframeTarget } from \"../../hooks/gsapTweenSynth\";\n\n/** Minimal keyframe cache types — mirrors GsapKeyframesData without pulling in Node-only gsap-parser. */\nexport interface KeyframeCacheEntry {\n format: string;\n keyframes: Array<{\n percentage: number;\n /** Original tween-relative percentage (server mutations need this, not the clip-relative `percentage`). */\n tweenPercentage?: number;\n /** Which property group the source tween belongs to (position, scale, rotation, visual, etc.). */\n propertyGroup?: string;\n /** Source tween id — lets the inline clip-row ease button target a specific segment. */\n animationId?: string;\n properties: Record<string, number | string>;\n ease?: string;\n /** Source animation/keyframe targets that collide at this clip percentage. */\n collidingAnimationTargets?: AnimationKeyframeTarget[];\n }>;\n ease?: string;\n easeEach?: string;\n}\n\nexport interface FocusedEaseSegment {\n animationId: string;\n collidingAnimationTargets?: AnimationKeyframeTarget[];\n tweenPercentage: number;\n elementId: string;\n projectId: string | null;\n sessionEpoch: number;\n nonce: number;\n}\n\ntype FocusedEaseSegmentTarget = Omit<FocusedEaseSegment, \"projectId\" | \"sessionEpoch\" | \"nonce\">;\n\n/**\n * A request to reveal one automated parameter in the audio FX rack.\n *\n * `elementKey` is the timeline element whose rack it belongs to — a group's own\n * id for a group lane, the clip's key otherwise — so the panel can refuse a\n * request aimed at something it is not showing.\n */\nexport interface RevealedAudioFxTarget {\n elementKey: string;\n /** The lane's own `fx.<node>.<param>` / `volume` target. */\n automationTarget: string;\n projectId: string | null;\n sessionEpoch: number;\n nonce: number;\n}\n\nexport type RevealedAudioFxTargetRequest = Omit<\n RevealedAudioFxTarget,\n \"projectId\" | \"sessionEpoch\" | \"nonce\"\n>;\n\n/** Whether a reveal request still belongs to what is on screen. */\nexport function isRevealedAudioFxRequestCurrent(\n request: RevealedAudioFxTarget,\n state: TimelineSessionIdentity,\n): boolean {\n return (\n request.projectId === state.timelineProjectId &&\n request.sessionEpoch === state.timelineSessionEpoch\n );\n}\n\ninterface TimelineSessionIdentity {\n timelineProjectId: string | null;\n timelineSessionEpoch: number;\n}\n\nexport function isFocusedEaseRequestCurrent(\n request: FocusedEaseSegment,\n state: TimelineSessionIdentity & { selectedElementId: string | null },\n): boolean {\n return (\n request.projectId === state.timelineProjectId &&\n request.sessionEpoch === state.timelineSessionEpoch &&\n request.elementId === state.selectedElementId\n );\n}\n\nexport interface KeyframeSlice {\n /** Selected collapsed (`element:pct`) or expanded (`element:group:animation:clipPct`) diamonds. */\n selectedKeyframes: Set<string>;\n toggleSelectedKeyframe: (key: string) => void;\n clearSelectedKeyframes: () => void;\n\n /** Clips whose keyframe property lanes are expanded in the timeline. */\n expandedClipIds: Set<string>;\n toggleClipExpanded: (id: string) => void;\n setClipExpanded: (id: string, expanded: boolean) => void;\n /** Union-expand clips (keyframed clips are expanded by default on load). */\n expandClips: (ids: readonly string[]) => void;\n\n /**\n * Groups whose member rows the caret has HIDDEN (structural, not lanes).\n *\n * Inverted deliberately. As an expanded-set, \"not in the set\" could not tell\n * never-touched from deliberately-collapsed, so every group defaulted to\n * collapsed — and since nothing seeds the set on create, grouping three\n * tracks made all three vanish behind a header the user had not yet learned\n * to open. Groups are expanded until someone closes one.\n */\n collapsedGroupIds: Set<string>;\n toggleGroupExpanded: (id: string) => void;\n\n /** Rows (clip id or group id) whose automation-lane rows the `∿` button opened. */\n expandedLaneOwnerIds: Set<string>;\n toggleLaneOwnerExpanded: (id: string) => void;\n\n /**\n * Project/session/element-scoped request. Its nonce is monotonic across store\n * resets so a stale consumer can never collide with a later request.\n */\n focusedEaseSegment: FocusedEaseSegment | null;\n focusedEaseRequestNonce: number;\n setFocusedEaseSegment: (target: FocusedEaseSegmentTarget) => void;\n clearFocusedEaseSegment: (nonce: number) => void;\n\n /**\n * \"Show me this automated parameter in the rack\" — raised by clicking an\n * automation lane's label in the timeline, consumed by the property panel.\n *\n * Session-stamped and nonce-guarded exactly like `focusedEaseSegment`: a\n * request outlives the click, so one made against a different project or\n * before a reload must not reopen a rack on whatever is mounted later.\n */\n revealedAudioFxTarget: RevealedAudioFxTarget | null;\n revealedAudioFxNonce: number;\n setRevealedAudioFxTarget: (target: RevealedAudioFxTargetRequest) => void;\n clearRevealedAudioFxTarget: (nonce: number) => void;\n\n /** Keyframe data per element id, populated from parsed GSAP animations. */\n keyframeCache: Map<string, KeyframeCacheEntry>;\n /** Unmerged source tweens per element; expanded property lanes read this, never keyframeCache. */\n gsapAnimations: Map<string, GsapAnimation[]>;\n setGsapAnimations: (elementId: string, animations: GsapAnimation[] | undefined) => void;\n setKeyframeCache: (elementId: string, data: KeyframeCacheEntry | undefined) => void;\n}\n\nexport function createKeyframeSlice(\n set: StoreApi<KeyframeSlice>[\"setState\"],\n getTimelineSessionIdentity: () => TimelineSessionIdentity,\n): KeyframeSlice {\n return {\n selectedKeyframes: new Set(),\n toggleSelectedKeyframe: (key) =>\n set((state) => {\n const next = new Set(state.selectedKeyframes);\n if (next.has(key)) next.delete(key);\n else next.add(key);\n return { selectedKeyframes: next };\n }),\n clearSelectedKeyframes: () => set({ selectedKeyframes: new Set() }),\n\n expandedClipIds: new Set(),\n toggleClipExpanded: (id) =>\n set((state) => {\n const next = new Set(state.expandedClipIds);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return { expandedClipIds: next };\n }),\n setClipExpanded: (id, expanded) =>\n set((state) => {\n if (state.expandedClipIds.has(id) === expanded) return state;\n const next = new Set(state.expandedClipIds);\n if (expanded) next.add(id);\n else next.delete(id);\n return { expandedClipIds: next };\n }),\n expandClips: (ids) =>\n set((state) => {\n if (ids.every((id) => state.expandedClipIds.has(id))) return state;\n const next = new Set(state.expandedClipIds);\n for (const id of ids) next.add(id);\n return { expandedClipIds: next };\n }),\n\n collapsedGroupIds: new Set(),\n toggleGroupExpanded: (id) =>\n set((state) => {\n const next = new Set(state.collapsedGroupIds);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return { collapsedGroupIds: next };\n }),\n\n expandedLaneOwnerIds: new Set(),\n toggleLaneOwnerExpanded: (id) =>\n set((state) => {\n const next = new Set(state.expandedLaneOwnerIds);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return { expandedLaneOwnerIds: next };\n }),\n\n focusedEaseSegment: null,\n focusedEaseRequestNonce: 0,\n setFocusedEaseSegment: (target) =>\n set((state) => {\n const nonce = state.focusedEaseRequestNonce + 1;\n const { timelineProjectId, timelineSessionEpoch } = getTimelineSessionIdentity();\n return {\n focusedEaseRequestNonce: nonce,\n focusedEaseSegment: {\n ...target,\n projectId: timelineProjectId,\n sessionEpoch: timelineSessionEpoch,\n nonce,\n },\n };\n }),\n clearFocusedEaseSegment: (nonce) =>\n set((state) =>\n state.focusedEaseSegment?.nonce === nonce ? { focusedEaseSegment: null } : state,\n ),\n\n revealedAudioFxTarget: null,\n revealedAudioFxNonce: 0,\n setRevealedAudioFxTarget: (target) =>\n set((state) => {\n const nonce = state.revealedAudioFxNonce + 1;\n const { timelineProjectId, timelineSessionEpoch } = getTimelineSessionIdentity();\n return {\n revealedAudioFxNonce: nonce,\n revealedAudioFxTarget: {\n ...target,\n projectId: timelineProjectId,\n sessionEpoch: timelineSessionEpoch,\n nonce,\n },\n };\n }),\n clearRevealedAudioFxTarget: (nonce) =>\n set((state) =>\n state.revealedAudioFxTarget?.nonce === nonce ? { revealedAudioFxTarget: null } : state,\n ),\n\n keyframeCache: new Map(),\n setKeyframeCache: (elementId, data) =>\n set((state) => {\n // A write that changes nothing must not emit a new Map: the cache has\n // several hot writers (per-element effect, file populate, post-commit\n // updater, delete) and every no-op re-rendered every subscriber.\n if (\n data ? state.keyframeCache.get(elementId) === data : !state.keyframeCache.has(elementId)\n )\n return state;\n const next = new Map(state.keyframeCache);\n if (data) next.set(elementId, data);\n else next.delete(elementId);\n return { keyframeCache: next };\n }),\n gsapAnimations: new Map(),\n setGsapAnimations: (elementId, animations) =>\n set((state) => {\n if (\n animations\n ? state.gsapAnimations.get(elementId) === animations\n : !state.gsapAnimations.has(elementId)\n )\n return state;\n const next = new Map(state.gsapAnimations);\n if (animations) next.set(elementId, animations);\n else next.delete(elementId);\n return { gsapAnimations: next };\n }),\n };\n}\n","/**\n * The active selection box on one automation lane.\n *\n * A store slice, not lane-local state, for the same reason keyframe selection\n * is one: Delete/copy/paste handlers and the shape menu live outside the lane\n * component and need to read it. Ephemeral by construction — nothing\n * serializes store state, and the selection must never survive into a render.\n */\nimport type { StoreApi } from \"zustand\";\n\nexport interface AutomationSelection {\n /** TimelineElement key (key ?? id) of the clip that owns the lane. */\n elementKey: string;\n /** Lane target: \"volume\" or \"fx.<nodeId>.<param>\". */\n target: string;\n /** Clip-local seconds; always t0 <= t1 (ordered on write). */\n t0: number;\n t1: number;\n /**\n * The box's value bounds, in the parameter's own units; always v0 <= v1.\n *\n * What makes the selection a box rather than a time span: a point is caught\n * only if it falls inside both axes. Span operations — copy, paste, shape\n * insert, simplify — still read t0/t1 alone, because they act on the envelope\n * over a stretch of time rather than on a set of breakpoints.\n */\n v0: number;\n v1: number;\n}\n\nexport interface AutomationSelectionSlice {\n automationSelection: AutomationSelection | null;\n setAutomationSelection: (sel: AutomationSelection) => void;\n clearAutomationSelection: () => void;\n}\n\nexport function createAutomationSelectionSlice(\n set: StoreApi<AutomationSelectionSlice>[\"setState\"],\n): AutomationSelectionSlice {\n return {\n automationSelection: null,\n setAutomationSelection: (sel) =>\n set({\n automationSelection: {\n ...sel,\n t0: Math.min(sel.t0, sel.t1),\n t1: Math.max(sel.t0, sel.t1),\n v0: Math.min(sel.v0, sel.v1),\n v1: Math.max(sel.v0, sel.v1),\n },\n }),\n clearAutomationSelection: () => set({ automationSelection: null }),\n };\n}\n","/**\n * The two editing-mode toggles the timeline toolbar owns.\n *\n * A store slice rather than toolbar-local state because both are read far from\n * where they are set: the motion-path arm is consumed by the canvas click\n * handler and published by MotionPathOverlay, and the auto-keyframe flag\n * decides what a drag/resize/rotate does inside the edit commit path.\n *\n * `motionPathArmed` is also cleared by playerStore on selection change — arming\n * survives neither a new element nor a project switch, so those resets stay\n * with the state they clear alongside.\n */\nimport type { StoreApi } from \"zustand\";\n\nexport interface EditingModeSlice {\n /** Motion-path \"set destination\" mode. Armed from the preview toolbar\n * (replaces the old double-click-on-canvas UX); while armed, one canvas\n * click places the new path's destination. */\n motionPathArmed: boolean;\n setMotionPathArmed: (armed: boolean) => void;\n /** Published by MotionPathOverlay so the toolbar shows the button only when\n * the selected element can actually take a path. */\n motionPathCreateAvailable: boolean;\n setMotionPathCreateAvailable: (available: boolean) => void;\n /** Global toggle for the \"Add keyframe\" diamond in the timeline toolbar\n * (#1808). When false, a manual drag/resize/rotate edit on an element that\n * already has a live tween shifts every keyframe by the edit's delta\n * (preserving the animation's shape) instead of inserting/updating a\n * keyframe at the playhead. */\n autoKeyframeEnabled: boolean;\n setAutoKeyframeEnabled: (enabled: boolean) => void;\n}\n\nexport function createEditingModeSlice(\n set: StoreApi<EditingModeSlice>[\"setState\"],\n): EditingModeSlice {\n return {\n motionPathArmed: false,\n setMotionPathArmed: (armed) => set({ motionPathArmed: armed }),\n motionPathCreateAvailable: false,\n setMotionPathCreateAvailable: (available) => set({ motionPathCreateAvailable: available }),\n autoKeyframeEnabled: true,\n setAutoKeyframeEnabled: (enabled) => set({ autoKeyframeEnabled: enabled }),\n };\n}\n","export interface TimelineFocusRequest {\n id: string;\n projectId: string | null;\n sessionEpoch: number;\n nonce: number;\n}\n\nexport function createTimelineFocusRequest(\n id: string,\n projectId: string | null,\n sessionEpoch: number,\n nonce: number,\n): TimelineFocusRequest {\n return { id, projectId, sessionEpoch, nonce };\n}\n","export type ThumbnailMode = \"adaptive\" | \"hidden\";\nexport type ThumbnailRuntimePolicy = \"follow-preference\" | \"force-hidden\" | \"legacy-default\";\n\n// \"Adaptive\" currently means the scheduler pauses rich work while scrolling.\n// The mode name leaves room for finer-grained runtime heuristics later.\n\nconst rawPolicy = import.meta.env?.VITE_STUDIO_TIMELINE_THUMBNAIL_POLICY;\n\nconst studioThumbnailRuntimePolicy: ThumbnailRuntimePolicy =\n rawPolicy === \"force-hidden\" || rawPolicy === \"legacy-default\" ? rawPolicy : \"follow-preference\";\n\nexport function defaultThumbnailMode(\n storedMode: ThumbnailMode | undefined,\n policy: ThumbnailRuntimePolicy = studioThumbnailRuntimePolicy,\n): ThumbnailMode {\n return storedMode ?? (policy === \"legacy-default\" ? \"hidden\" : \"adaptive\");\n}\n\nexport function effectiveThumbnailMode(\n preferredMode: ThumbnailMode,\n policy: ThumbnailRuntimePolicy = studioThumbnailRuntimePolicy,\n): ThumbnailMode {\n return policy === \"force-hidden\" ? \"hidden\" : preferredMode;\n}\n","import type { StoreApi } from \"zustand\";\nimport { readStudioUiPreferences, writeStudioUiPreferences } from \"../../utils/studioUiPreferences\";\nimport { defaultThumbnailMode, type ThumbnailMode } from \"../lib/thumbnailPolicy\";\n\n/** Revision that moves every composition's thumbnails at once. */\nconst EVERY_COMPOSITION = \"*\";\n\n/** Mounts may be written `./scene.html`; the server reports `scene.html`. */\nfunction revisionKey(path: string): string {\n return (path.split(/[?#]/)[0] ?? \"\").replace(/\\\\/g, \"/\").replace(/^\\.?\\//, \"\");\n}\n\nexport interface ThumbnailSlice {\n thumbnailMode: ThumbnailMode;\n /** Monotonic identity, per composition path, for persisted content shown by mounted thumbnails. */\n thumbnailRevisions: Readonly<Record<string, number>>;\n setThumbnailMode: (mode: ThumbnailMode) => void;\n /** `null` moves every composition, e.g. after a root or asset change. */\n bumpThumbnailRevisions: (compositions: readonly string[] | null) => void;\n}\n\nexport function thumbnailRevisionOf(\n revisions: Readonly<Record<string, number>>,\n compositionPath: string,\n): number {\n return (revisions[EVERY_COMPOSITION] ?? 0) + (revisions[revisionKey(compositionPath)] ?? 0);\n}\n\nexport function createThumbnailSlice(set: StoreApi<ThumbnailSlice>[\"setState\"]): ThumbnailSlice {\n return {\n thumbnailMode: defaultThumbnailMode(readStudioUiPreferences().thumbnailMode),\n thumbnailRevisions: {},\n setThumbnailMode: (mode) => {\n writeStudioUiPreferences({ thumbnailMode: mode });\n set({ thumbnailMode: mode });\n },\n bumpThumbnailRevisions: (compositions) =>\n set(({ thumbnailRevisions }) => {\n const next = { ...thumbnailRevisions };\n for (const path of compositions?.map(revisionKey) ?? [EVERY_COMPOSITION]) {\n next[path] = (next[path] ?? 0) + 1;\n }\n return { thumbnailRevisions: next };\n }),\n };\n}\n","/** Gates `timelineReady` on the composition's declared readiness inputs\n * (media, compute, and the paint-and-idle default) instead of just a known\n * duration. The generation counter is module-scope, not store state: it\n * guards an in-flight settlement, not something a component reads, so\n * bumping it shouldn't trigger a render. */\nimport type { StoreApi } from \"zustand\";\nimport { settleCompositionReadiness } from \"@hyperframes/core/composition-readiness\";\n\nexport interface PlaybackReadinessSlice {\n timelineReady: boolean;\n /** Latched when the project's first preview shows its first frame (or fails), kept through edit\n * reloads, so work that must not compete with the boot waits for it once. */\n previewBooted: boolean;\n setTimelineReady: (ready: boolean) => void;\n markPreviewBooted: () => void;\n /** Sets timelineReady once doc's readiness inputs settle, or immediately\n * if doc is null. A wait a later call supersedes never wins the race.\n * Waits for doc's load step first: it pauses and rewinds the preview, so a\n * Play enabled before it would be undone. */\n requestTimelineReady: (doc: Document | null) => void;\n /** Called by the preview's load step for the document it ran on. */\n markPreviewLoadStep: (doc: Document) => void;\n}\n\nlet timelineReadyGeneration = 0;\nconst loadStepDocs = new WeakSet<Document>();\nlet awaitingLoadStep: { doc: Document; generation: number } | null = null;\n\n/** For a full timeline reset: bumps the generation so any requestTimelineReady\n * wait in flight can never resolve into what replaced it. */\nexport function resetPlaybackReadinessState(): Pick<\n PlaybackReadinessSlice,\n \"timelineReady\" | \"previewBooted\"\n> {\n timelineReadyGeneration++;\n return { timelineReady: false, previewBooted: false };\n}\n\nexport function createPlaybackReadinessSlice(\n set: StoreApi<PlaybackReadinessSlice>[\"setState\"],\n): PlaybackReadinessSlice {\n const settle = (doc: Document, generation: number) =>\n settleCompositionReadiness(doc, () => {\n if (generation === timelineReadyGeneration) set({ timelineReady: true });\n });\n return {\n timelineReady: false,\n previewBooted: false,\n markPreviewBooted: () => set({ previewBooted: true }),\n setTimelineReady: (ready) => {\n timelineReadyGeneration++;\n set({ timelineReady: ready });\n },\n requestTimelineReady: (doc) => {\n const generation = ++timelineReadyGeneration;\n if (!doc) return set({ timelineReady: true });\n if (!loadStepDocs.has(doc)) {\n awaitingLoadStep = { doc, generation };\n return;\n }\n settle(doc, generation);\n },\n markPreviewLoadStep: (doc) => {\n loadStepDocs.add(doc);\n if (awaitingLoadStep?.doc !== doc) return;\n const { generation } = awaitingLoadStep;\n awaitingLoadStep = null;\n settle(doc, generation);\n },\n };\n}\n","export interface TimelineTimeRange {\n t0: number;\n t1: number;\n}\n\nexport interface RangeSelectionSlice {\n /** Shift-drag ruler range in seconds, normalized so t0 <= t1. */\n rangeSelection: TimelineTimeRange | null;\n}\n\nexport function createRangeSelectionSlice(): RangeSelectionSlice {\n return { rangeSelection: null };\n}\n","import type { GsapAnimation } from \"@hyperframes/core/gsap-parser\";\nimport type { KeyframeCacheEntry } from \"./keyframeSlice\";\nimport { resetPlaybackReadinessState } from \"./readinessSlice\";\nimport type { SubCompositionHostState } from \"./timelineElement\";\n\nexport function createTimelineResetState() {\n return {\n isPlaying: false,\n currentTime: 0,\n duration: 0,\n ...resetPlaybackReadinessState(),\n beatDragging: false,\n elements: [],\n selectedElementId: null,\n zEditVersion: 0,\n inPoint: null,\n outPoint: null,\n rangeSelection: null,\n activeTool: \"select\" as const,\n activeKeyframePct: null,\n motionPathArmed: false,\n motionPathCreateAvailable: false,\n selectedKeyframes: new Set<string>(),\n // Ephemeral like every other selection here. A range surviving a project\n // switch can match a same-keyed clip in the new project and redirect a\n // paste through `sel.elementKey === paste.elementKey` to a stale t0.\n automationSelection: null,\n expandedClipIds: new Set<string>(),\n // Per-composition: ids from comp A match nothing in B, silencing all of it.\n collapsedGroupIds: new Set<string>(),\n expandedLaneOwnerIds: new Set<string>(),\n focusedEaseSegment: null,\n revealedAudioFxTarget: null,\n selectedElementIds: new Set<string>(),\n requestedSeekTime: null,\n lintFindingsByElement: new Map<string, { count: number; messages: string[] }>(),\n timelineFocus: null,\n keyframeCache: new Map<string, KeyframeCacheEntry>(),\n gsapAnimations: new Map<string, GsapAnimation[]>(),\n beatAnalysis: null,\n beatEdits: null,\n beatUndo: [],\n beatRedo: [],\n beatPersist: null,\n clipManifest: null,\n clipParentMap: new Map<string, string>(),\n domClipChildren: [],\n subCompositionHostState: new Map<string, SubCompositionHostState>(),\n };\n}\n","import { buildProjectApiPath } from \"../../utils/projectRouting\";\nimport { forwardRef, useEffect, useRef, useState } from \"react\";\nimport { isLottieAnimationLoaded } from \"@hyperframes/core/runtime/lottie-readiness\";\nimport { useMountEffect } from \"../../hooks/useMountEffect\";\nimport { applyPreviewVariablesToUrl } from \"../../hooks/previewVariablesStore\";\nimport { HyperframesLoader } from \"../../components/ui\";\nimport { usePlayerStore } from \"../store/playerStore\";\n// Importing \"@hyperframes/player\" registers a class extending HTMLElement at\n// module load, which throws under SSR, hence the dynamic import behind a\n// `typeof window` guard. Kicking it here rather than in the mount effect puts\n// the chunk request in flight before the shell's first layout. Clearing the memo\n// on rejection stops one failure poisoning every later mount; the browser's\n// module map still caches a failed fetch, so recovery is a page reload.\nlet playerModule: Promise<unknown> | null = null;\n\nexport function loadPlayerModule(): Promise<unknown> {\n playerModule ??= import(\"@hyperframes/player\").catch((err: unknown) => {\n playerModule = null;\n throw err;\n });\n return playerModule;\n}\n\nif (typeof window !== \"undefined\") void loadPlayerModule().catch(() => {});\n\ninterface PlayerProps {\n projectId?: string;\n directUrl?: string;\n onLoad: () => void;\n /** Fires once the loaded document is painted and every loader (shader, assets) has cleared. */\n onReadyToShowChange?: (ready: boolean) => void;\n onPreviewError?: (message: string) => void;\n onCompositionLoadingChange?: (loading: boolean) => void;\n onPainted?: (details: { iframe: HTMLIFrameElement; startedAt: number; loadId: number }) => void;\n portrait?: boolean;\n style?: React.CSSProperties;\n suppressLoadingOverlay?: boolean;\n}\n\ninterface HyperframesPlayerElement extends HTMLElement {\n iframeElement: HTMLIFrameElement;\n ready?: boolean;\n painted?: boolean;\n}\n\nconst MEDIA_HAVE_FUTURE_DATA = 3;\nconst MEDIA_NETWORK_NO_SOURCE = 3;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction getShaderTransitionLoading(event: Event): boolean | null {\n if (!(event instanceof CustomEvent)) return null;\n const detail: unknown = event.detail;\n if (!isRecord(detail)) return null;\n const state = detail.state;\n if (!isRecord(state)) return null;\n return state.loading === true && state.ready !== true;\n}\n\nconst COMPOSITION_LOADING_OVERLAY_DELAY_MS = 400;\nconst PREVIEW_BOOT_DEADLINE_MS = 5000;\nconst DEFAULT_PREVIEW_ERROR = \"The composition preview did not become ready.\";\n\nexport function shouldShowCompositionLoadingOverlay(compositionLoading: boolean): boolean {\n return compositionLoading;\n}\n\nexport function readPreviewErrorMessage(event: Event): string {\n if (!(event instanceof CustomEvent) || !isRecord(event.detail)) return DEFAULT_PREVIEW_ERROR;\n return typeof event.detail.message === \"string\" && event.detail.message.trim()\n ? event.detail.message\n : DEFAULT_PREVIEW_ERROR;\n}\n\nfunction enableInteractiveIframe(player: HyperframesPlayerElement): void {\n const root = player.shadowRoot;\n if (!root) return;\n\n const container = root.querySelector<HTMLElement>(\".hfp-container\");\n const iframe = root.querySelector<HTMLIFrameElement>(\".hfp-iframe\");\n\n container?.style.setProperty(\"pointer-events\", \"auto\");\n iframe?.style.setProperty(\"pointer-events\", \"auto\");\n}\n\nfunction isPreviewMediaElement(el: Element): el is HTMLMediaElement {\n const tagName = el.tagName.toLowerCase();\n return tagName === \"video\" || tagName === \"audio\";\n}\n\n// Assets are considered ready when every `<video>`/`<audio>` has enough data\n// to play through without buffering, and every registered Lottie animation has\n// finished loading.\n//\n// Returns whichever value was returned last on cross-origin / transient DOM\n// races so a brief access failure (e.g. an iframe that just swapped src)\n// doesn't flicker the overlay state — we keep showing whatever was most\n// recently true.\nexport function hasUnloadedAssets(iframe: HTMLIFrameElement, lastResult: boolean): boolean {\n try {\n const win = iframe.contentWindow as unknown as (Window & { __hfLottie?: unknown[] }) | null;\n const doc = iframe.contentDocument;\n if (!win || !doc) return lastResult;\n\n for (const el of doc.querySelectorAll(\"video, audio\")) {\n if (\n isPreviewMediaElement(el) &&\n !el.error &&\n el.networkState !== MEDIA_NETWORK_NO_SOURCE &&\n el.readyState < MEDIA_HAVE_FUTURE_DATA\n ) {\n return true;\n }\n }\n\n const lotties = win.__hfLottie;\n if (lotties?.length) {\n for (const anim of lotties) {\n if (!isLottieAnimationLoaded(anim)) return true;\n }\n }\n\n return false;\n } catch {\n return lastResult;\n }\n}\n\n/**\n * Renders a composition preview using the <hyperframes-player> web component.\n *\n * The web component handles iframe scaling, dimension detection, and\n * ResizeObserver internally. This wrapper bridges its inner iframe to the\n * forwarded ref so useTimelinePlayer can access it for clip manifest parsing,\n * timeline probing, and DOM inspection.\n */\nexport const Player = forwardRef<HTMLIFrameElement, PlayerProps>(\n (\n {\n projectId,\n directUrl,\n onLoad,\n onReadyToShowChange,\n onPreviewError,\n onCompositionLoadingChange,\n onPainted,\n portrait,\n style,\n suppressLoadingOverlay,\n },\n ref,\n ) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const loadCountRef = useRef(0);\n const assetPollRef = useRef<ReturnType<typeof setInterval> | null>(null);\n const assetFadeRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const retryPreviewRef = useRef<(() => void) | null>(null);\n const loadStartedAtRef = useRef(0);\n const loadIdRef = useRef(0);\n const retryCountRef = useRef(0);\n // Read at call time: this element outlives its first props (a shadow preview is\n // promoted in place), so mount-time closures would go stale.\n const onLoadRef = useRef(onLoad);\n onLoadRef.current = onLoad;\n const onReadyToShowChangeRef = useRef(onReadyToShowChange);\n onReadyToShowChangeRef.current = onReadyToShowChange;\n const onPreviewErrorRef = useRef(onPreviewError);\n onPreviewErrorRef.current = onPreviewError;\n const [loaded, setLoaded] = useState(false);\n const [assetsLoading, setAssetsLoading] = useState(false);\n const [assetOverlayVisible, setAssetOverlayVisible] = useState(false);\n const [assetOverlayFading, setAssetOverlayFading] = useState(false);\n const [assetWaitLong, setAssetWaitLong] = useState(false);\n const [shaderTransitionLoading, setShaderTransitionLoading] = useState(false);\n const [compositionLoading, setCompositionLoading] = useState(true);\n const [painted, setPainted] = useState(false);\n const [compositionOverlayDeferred, setCompositionOverlayDeferred] = useState(true);\n const [previewError, setPreviewError] = useState<string | null>(null);\n\n // eslint-disable-next-line no-restricted-syntax\n useEffect(() => {\n if (!compositionLoading) {\n setCompositionOverlayDeferred(true);\n return;\n }\n const timer = setTimeout(\n () => setCompositionOverlayDeferred(false),\n COMPOSITION_LOADING_OVERLAY_DELAY_MS,\n );\n return () => clearTimeout(timer);\n }, [compositionLoading]);\n\n useMountEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const previewSource =\n directUrl || (projectId ? buildProjectApiPath(projectId, \"/preview\") : null);\n if (!previewSource) return;\n\n let canceled = false;\n let cleanup: (() => void) | undefined;\n\n void loadPlayerModule().then(() => {\n if (canceled) return;\n\n // Create the web component imperatively to avoid JSX custom-element typing.\n const player = document.createElement(\"hyperframes-player\") as HyperframesPlayerElement;\n const srcUrl = new URL(previewSource, window.location.origin);\n applyPreviewVariablesToUrl(srcUrl);\n const src = srcUrl.pathname + srcUrl.search;\n const retryPreview = () => {\n retryCountRef.current += 1;\n const retryUrl = new URL(src, window.location.origin);\n retryUrl.searchParams.set(\"_hfStudioRetry\", String(retryCountRef.current));\n setPreviewError(null);\n setCompositionLoading(true);\n loadStartedAtRef.current = performance.now();\n loadIdRef.current += 1;\n player.setAttribute(\"src\", retryUrl.pathname + retryUrl.search);\n };\n retryPreviewRef.current = retryPreview;\n const iframe = player.iframeElement;\n const preventToggle = (e: Event) => e.stopImmediatePropagation();\n const handleShaderTransitionState = (event: Event) => {\n const loading = getShaderTransitionLoading(event);\n if (loading !== null) setShaderTransitionLoading(loading);\n };\n const handleReady = () => {\n setPreviewError(null);\n setCompositionLoading(false);\n };\n const handlePainted = () => {\n setPainted(true);\n onPainted?.({ iframe, startedAt: loadStartedAtRef.current, loadId: loadIdRef.current });\n };\n const handleError = (event: Event) => {\n const message = readPreviewErrorMessage(event);\n onPreviewErrorRef.current?.(message);\n setPreviewError(message);\n setCompositionLoading(false);\n };\n const handleLoad = () => {\n loadCountRef.current++;\n setLoaded(true);\n setPreviewError(null);\n setShaderTransitionLoading(false);\n setCompositionLoading(!player.ready);\n setPainted(Boolean(player.painted));\n // Reveal animation on reload (hot-reload, composition switch)\n if (loadCountRef.current > 1) {\n container.classList.remove(\"preview-revealing\");\n void container.offsetWidth;\n container.classList.add(\"preview-revealing\");\n const onEnd = () => container.classList.remove(\"preview-revealing\");\n container.addEventListener(\"animationend\", onEnd, { once: true });\n }\n onLoadRef.current();\n\n // Show a loading overlay until every `<video>`/`<audio>` and Lottie\n // asset is ready. Without this users can click play before audio has\n // buffered — the runtime is resilient (queued play() resolves once\n // data arrives), but the overlay communicates why the first frame\n // or first audio beat may lag.\n //\n // Skip the overlay on subsequent loads (content refreshes via\n // refreshPlayer). The browser has already cached the assets from\n // the first load, so they resolve near-instantly and the overlay\n // just creates a disruptive flash.\n //\n // Poll with a 10 s safety cap (100 ticks × 100 ms). If the cap\n // trips we hide the overlay so the UI doesn't appear stuck forever,\n // but we log a debug warning so the case is diagnosable — a long\n // cold video or a broken asset can legitimately exceed 10 s on a\n // slow network.\n if (assetPollRef.current) clearInterval(assetPollRef.current);\n const isContentRefresh = loadCountRef.current > 1;\n let lastUnloaded = isContentRefresh ? false : hasUnloadedAssets(iframe, false);\n if (lastUnloaded) {\n setAssetsLoading(true);\n let attempts = 0;\n assetPollRef.current = setInterval(() => {\n attempts += 1;\n lastUnloaded = hasUnloadedAssets(iframe, lastUnloaded);\n if (!lastUnloaded || attempts > 100) {\n if (lastUnloaded && attempts > 100) {\n console.debug(\n \"[studio] asset readiness poll hit the 10s cap — continuing with unloaded assets\",\n );\n }\n if (assetPollRef.current) clearInterval(assetPollRef.current);\n assetPollRef.current = null;\n setAssetsLoading(false);\n }\n }, 100);\n } else {\n setAssetsLoading(false);\n }\n };\n\n // Attach lifecycle listeners before assigning src or connecting the\n // custom element. A warm local iframe can otherwise finish before\n // Studio observes its load and never initialize the timeline.\n iframe.addEventListener(\"load\", handleLoad);\n player.addEventListener(\"click\", preventToggle, { capture: true });\n player.addEventListener(\"shadertransitionstate\", handleShaderTransitionState);\n player.addEventListener(\"ready\", handleReady);\n player.addEventListener(\"painted\", handlePainted);\n player.addEventListener(\"error\", handleError);\n\n // Bridge the inner iframe to the forwarded ref for useTimelinePlayer.\n if (typeof ref === \"function\") {\n ref(iframe);\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLIFrameElement | null>).current = iframe;\n }\n\n player.setAttribute(\"shader-capture-scale\", \"1\");\n player.setAttribute(\"shader-loading\", \"player\");\n player.setAttribute(\"low-power-idle\", \"\");\n player.setAttribute(\"width\", String(portrait ? 1080 : 1920));\n player.setAttribute(\"height\", String(portrait ? 1920 : 1080));\n player.style.width = \"100%\";\n player.style.height = \"100%\";\n player.style.display = \"block\";\n player.style.background = \"transparent\";\n loadStartedAtRef.current = performance.now();\n loadIdRef.current += 1;\n player.setAttribute(\"src\", src);\n container.appendChild(player);\n\n // Inject pasteboard shadow: let the shadow around the canvas bleed\n // into the surrounding pasteboard area (overflow: visible on the container)\n // and add a subtle outline + drop-shadow so the canvas boundary reads\n // against the gray pasteboard, consistent with professional editors.\n if (player.shadowRoot) {\n const pasteboardStyle = document.createElement(\"style\");\n pasteboardStyle.textContent =\n \".hfp-container{overflow:visible}\" +\n \".hfp-iframe{box-shadow:0 0 0 1px rgba(255,255,255,0.08),0 4px 32px rgba(0,0,0,.7)}\";\n player.shadowRoot.appendChild(pasteboardStyle);\n }\n\n enableInteractiveIframe(player);\n\n cleanup = () => {\n iframe.removeEventListener(\"load\", handleLoad);\n player.removeEventListener(\"click\", preventToggle, { capture: true });\n player.removeEventListener(\"shadertransitionstate\", handleShaderTransitionState);\n player.removeEventListener(\"ready\", handleReady);\n player.removeEventListener(\"painted\", handlePainted);\n player.removeEventListener(\"error\", handleError);\n if (assetPollRef.current) clearInterval(assetPollRef.current);\n assetPollRef.current = null;\n // `remove()` rather than `container.removeChild(player)`: by the time\n // this cleanup runs the element may already be detached — React can\n // re-render the container, a crossfade refresh can swap it, or a\n // translation/extension can reparent it. `removeChild` then throws\n // NotFoundError, which the error boundary turns into a full-screen\n // \"Something went wrong\". `remove()` is a no-op when already detached.\n player.remove();\n if (retryPreviewRef.current === retryPreview) retryPreviewRef.current = null;\n // Clear the forwarded ref only if it still points to THIS iframe.\n // During crossfade refreshes the retiring Player unmounts after the\n // new Player has already assigned its iframe to the same ref — blindly\n // nulling it would break seeking in the new Player.\n // Callback refs are skipped — we can't read back the current value to\n // guard against clobbering a newer assignment. The mutable-ref branch\n // (the only path used today) is guarded by identity check.\n if (typeof ref === \"function\") {\n // no-op: can't safely guard callback refs\n } else if (ref) {\n const mutableRef = ref as React.MutableRefObject<HTMLIFrameElement | null>;\n if (mutableRef.current === iframe) {\n mutableRef.current = null;\n }\n }\n };\n });\n\n return () => {\n canceled = true;\n cleanup?.();\n };\n });\n\n // Surface a \"Continue anyway\" escape hatch once the asset wait drags on.\n // eslint-disable-next-line no-restricted-syntax\n useEffect(() => {\n if (!assetsLoading) {\n setAssetWaitLong(false);\n return;\n }\n const timer = setTimeout(() => setAssetWaitLong(true), 3000);\n return () => clearTimeout(timer);\n }, [assetsLoading]);\n\n useEffect(() => {\n if (assetFadeRef.current) {\n clearTimeout(assetFadeRef.current);\n assetFadeRef.current = null;\n }\n\n if (assetsLoading) {\n setAssetOverlayVisible(true);\n setAssetOverlayFading(false);\n return;\n }\n\n setAssetOverlayFading(true);\n assetFadeRef.current = setTimeout(() => {\n setAssetOverlayVisible(false);\n setAssetOverlayFading(false);\n assetFadeRef.current = null;\n }, 240);\n\n return () => {\n if (assetFadeRef.current) {\n clearTimeout(assetFadeRef.current);\n assetFadeRef.current = null;\n }\n };\n }, [assetsLoading]);\n\n const handleContinueAnyway = () => {\n if (assetPollRef.current) {\n clearInterval(assetPollRef.current);\n assetPollRef.current = null;\n }\n setAssetsLoading(false);\n };\n\n const firstFrameShown =\n loaded && painted && !compositionLoading && !shaderTransitionLoading && !previewError;\n const readyToShow = firstFrameShown && !assetsLoading;\n // Work waiting on the boot starts at the first frame, while media still buffers.\n useEffect(() => {\n if (firstFrameShown) usePlayerStore.getState().markPreviewBooted();\n }, [firstFrameShown]);\n // `painted` means the player's own loader has finished fading; two frames of grace on top.\n useEffect(() => {\n if (!readyToShow) {\n onReadyToShowChangeRef.current?.(false);\n return;\n }\n let second = 0;\n const first = requestAnimationFrame(() => {\n second = requestAnimationFrame(() => onReadyToShowChangeRef.current?.(true));\n });\n return () => {\n cancelAnimationFrame(first);\n cancelAnimationFrame(second);\n };\n }, [readyToShow]);\n\n useEffect(() => {\n if (previewError) usePlayerStore.getState().markPreviewBooted();\n }, [previewError]);\n\n useEffect(() => {\n const timer = setTimeout(\n () => usePlayerStore.getState().markPreviewBooted(),\n PREVIEW_BOOT_DEADLINE_MS,\n );\n return () => clearTimeout(timer);\n }, [projectId]);\n\n const showCompositionOverlay =\n !suppressLoadingOverlay &&\n !compositionOverlayDeferred &&\n shouldShowCompositionLoadingOverlay(compositionLoading);\n const showAssetOverlay =\n assetOverlayVisible && !shaderTransitionLoading && !showCompositionOverlay && !previewError;\n\n useEffect(() => {\n onCompositionLoadingChange?.(showCompositionOverlay || showAssetOverlay);\n }, [onCompositionLoadingChange, showCompositionOverlay, showAssetOverlay]);\n\n return (\n <div\n className=\"relative w-full h-full max-w-full max-h-full overflow-hidden flex items-center justify-center\"\n style={style}\n >\n <div ref={containerRef} className=\"w-full h-full\" />\n {showCompositionOverlay && (\n <div\n className=\"absolute inset-0 bg-black flex items-center justify-center z-30 select-none\"\n data-hyperframes-ignore=\"\"\n data-testid=\"composition-loading-overlay\"\n draggable={false}\n onDragStart={(event) => event.preventDefault()}\n onMouseDown={(event) => event.preventDefault()}\n onPointerDown={(event) => event.preventDefault()}\n >\n <HyperframesLoader\n title=\"Loading composition\"\n detail=\"Preparing the Studio preview.\"\n size={56}\n />\n </div>\n )}\n {showAssetOverlay && (\n <div\n className=\"absolute inset-0 bg-black flex items-center justify-center z-20 select-none\"\n data-hyperframes-ignore=\"\"\n draggable={false}\n style={{\n opacity: assetOverlayFading ? 0 : 1,\n pointerEvents: assetOverlayFading ? \"none\" : \"auto\",\n transition: \"opacity 180ms ease-in\",\n }}\n onDragStart={(event) => event.preventDefault()}\n onMouseDown={(event) => event.preventDefault()}\n >\n <div className=\"flex flex-col items-center gap-3\">\n <HyperframesLoader\n title=\"Preparing preview assets\"\n detail=\"Waiting for media and motion assets before playback starts.\"\n size={56}\n />\n {assetWaitLong && (\n <button\n type=\"button\"\n onClick={handleContinueAnyway}\n className=\"px-3 py-1.5 text-[11px] rounded-md border border-neutral-700 text-neutral-300 hover:border-neutral-500 hover:bg-neutral-800 transition-colors\"\n >\n Continue anyway\n </button>\n )}\n </div>\n </div>\n )}\n {previewError && (\n <div\n className=\"absolute inset-0 z-40 flex items-center justify-center bg-black/90 px-6 text-center\"\n data-hyperframes-ignore=\"\"\n data-testid=\"composition-preview-error\"\n >\n <div className=\"max-w-sm\">\n <p className=\"text-sm font-semibold text-white\">Preview failed to load</p>\n <p className=\"mt-1 text-xs text-neutral-400\">{previewError}</p>\n <button\n type=\"button\"\n className=\"mt-4 rounded-md bg-white px-3 py-1.5 text-xs font-semibold text-black transition-colors hover:bg-neutral-200\"\n onClick={() => retryPreviewRef.current?.()}\n >\n Retry preview\n </button>\n </div>\n </div>\n )}\n </div>\n );\n },\n);\n\nPlayer.displayName = \"Player\";\n","import { useEffect } from \"react\";\n\n/**\n * Run an effect exactly once on mount (and optional cleanup on unmount).\n * This is the ONLY sanctioned way to call useEffect in this codebase.\n *\n * If you need to react to prop/state changes, use one of:\n * - Derived state (compute inline, no hook needed)\n * - Event handlers (onClick, onChange, etc.)\n * - `key` prop to force remount\n * - Data-fetching library (useQuery, useSWR)\n *\n * @see https://react.dev/learn/you-might-not-need-an-effect\n */\nexport function useMountEffect(effect: () => void | (() => void)) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(effect, []);\n}\n","import { create } from \"zustand\";\n\n/**\n * Ephemeral composition-variable overrides for the preview iframe.\n *\n * Values here are NEVER persisted to the composition — they ride the preview\n * URL as `?variables=<json>` (see the studio-server preview routes), which the\n * server injects as `window.__hfVariables` exactly like render-time injection,\n * so what the user previews is what `hyperframes render --variables` produces.\n * `null` means \"preview with declared defaults\".\n */\ninterface PreviewVariablesState {\n values: Record<string, unknown> | null;\n setValues: (values: Record<string, unknown> | null) => void;\n}\n\nexport const usePreviewVariablesStore = create<PreviewVariablesState>((set) => ({\n values: null,\n setValues: (values) => set({ values: values && Object.keys(values).length > 0 ? values : null }),\n}));\n\n/**\n * Apply the current preview-variable overrides to a preview URL (both the\n * Player's initial mount and refreshPlayer's soft reload route through this,\n * so a hard remount can't silently drop the active overrides).\n */\nexport function applyPreviewVariablesToUrl(url: URL): void {\n const values = usePreviewVariablesStore.getState().values;\n if (values) {\n url.searchParams.set(\"variables\", JSON.stringify(values));\n } else {\n url.searchParams.delete(\"variables\");\n }\n}\n"],"mappings":";;;;;;;;;AAAO,IAAM,qBAAqB;AAE3B,IAAM,kBAAkB,CAAC,MAAc,aAC5C,KAAK,IAAI,GAAG,WAAW,IAAI,KAAK,IAAI,UAAU,IAAI,IAAI,IAAI;AAErD,SAAS,WAAW,MAAsB;AAC/C,MAAI,CAAC,OAAO,SAAS,IAAI,KAAK,OAAO,EAAG,QAAO;AAC/C,QAAM,OAAO,KAAK,MAAM,OAAO,EAAE;AACjC,QAAM,OAAO,KAAK,MAAM,OAAO,EAAE;AAKjC,SAAO,GAAG,KAAK,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,KAAK,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC;AAChF;AAEO,SAAS,eAAe,MAAc,MAAM,oBAA4B;AAC7E,MAAI,CAAC,OAAO,SAAS,IAAI,KAAK,QAAQ,EAAG,QAAO;AAChD,MAAI,CAAC,OAAO,SAAS,GAAG,KAAK,OAAO,EAAG,QAAO;AAC9C,SAAO,KAAK,MAAM,OAAO,GAAG;AAC9B;AAEO,SAAS,eAAe,OAAe,MAAM,oBAA4B;AAC9E,MAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,EAAG,QAAO;AAClD,MAAI,CAAC,OAAO,SAAS,GAAG,KAAK,OAAO,EAAG,QAAO;AAC9C,SAAO,QAAQ;AACjB;AAEO,SAAS,cAAc,MAAc,aAAqB,MAAM,oBAA4B;AACjG,QAAM,eAAe,eAAe,MAAM,GAAG;AAC7C,QAAM,YAAY,KAAK,IAAI,GAAG,eAAe,WAAW;AACxD,SAAO,eAAe,WAAW,GAAG;AACtC;AAEO,SAAS,gBAAgB,MAAc,UAAkB,MAAM,oBAA4B;AAChG,QAAM,eAAe,eAAe,MAAM,GAAG;AAC7C,QAAM,cAAc,eAAe,UAAU,GAAG;AAChD,SAAO,GAAG,YAAY,OAAO,WAAW;AAC1C;;;ACpCA,IAAM,gBAAgB,oBAAI,IAAkB;AAC5C,IAAI,aAAa;AACjB,IAAI,YAAY;AAET,IAAM,WAAW;AAAA,EACtB,QAAQ,CAAC,SAAiB;AACxB,iBAAa;AACb,kBAAc,QAAQ,CAAC,aAAa,SAAS,IAAI,CAAC;AAAA,EACpD;AAAA,EACA,YAAY,CAAC,SAAiB;AAC5B,iBAAa;AACb,aAAS,OAAO,IAAI;AAAA,EACtB;AAAA,EACA,WAAW,MAAM;AAAA,EACjB,QAAQ,MAAM;AAAA,EACd,WAAW,CAAC,aAA2B;AACrC,kBAAc,IAAI,QAAQ;AAC1B,WAAO,MAAM,cAAc,OAAO,QAAQ;AAAA,EAC5C;AACF;;;ACrBA,SAAS,cAAc;;;ACQvB,SAAS,aAAsB;AAC7B,MAAI;AACF,WAAO,YAAY,IAAI,QAAQ;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAIO,SAAS,2BAA2B,OAAsB;AAC/D,MAAI,CAAC,WAAW,KAAK,OAAO,WAAW,YAAa;AACpD,EAAC,OAAkD,gBAAgB;AACrE;;;ACLO,SAAS,iBACd,SACA,IACA,aACa;AACb,MAAI,YAAa,QAAO,MAAM,QAAQ,IAAI,EAAE,IAAI,IAAI,IAAI,OAAO,IAAI,oBAAI,IAAY;AACnF,SAAO,KAAK,oBAAI,IAAI,CAAC,EAAE,CAAC,IAAI,oBAAI,IAAY;AAC9C;AAWO,SAAS,uBACd,SACA,IACS;AACT,MAAI,CAAC,WAAW,OAAO,KAAM,QAAO;AACpC,SAAO,QAAQ,eAAe,wBAAwB,EAAE,EAAE;AAC5D;;;ACtCO,IAAM,YAAY;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAcO,IAAM,oBAAoB;AAAA,EAC/B,SAAS;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ,EAAE,MAAM,YAAY,WAAW,QAAQ;AAAA,IAC/C,aAAa;AAAA,EACf;AAAA,EACA,UAAU;AAAA,IACR,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ,EAAE,MAAM,WAAW,WAAW,QAAQ;AAAA,IAC9C,aAAa;AAAA,EACf;AAAA,EACA,cAAc;AAAA,IACZ,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ,EAAE,MAAM,WAAW,WAAW,OAAO;AAAA,EAC/C;AAAA,EACA,QAAQ,EAAE,OAAO,UAAU,MAAM,QAAQ,QAAQ,EAAE,MAAM,gBAAgB,WAAW,SAAS,EAAE;AAAA,EAC/F,MAAM,EAAE,OAAO,QAAQ,MAAM,QAAQ,QAAQ,EAAE,MAAM,gBAAgB,WAAW,SAAS,EAAE;AAAA,EAC3F,SAAS;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ,EAAE,MAAM,gBAAgB,WAAW,SAAS;AAAA,EACtD;AAAA,EACA,QAAQ,EAAE,OAAO,UAAU,MAAM,SAAS,QAAQ,EAAE,MAAM,WAAW,WAAW,QAAQ,EAAE;AAAA,EAC1F,QAAQ,EAAE,OAAO,UAAU,MAAM,SAAS,QAAQ,EAAE,MAAM,UAAU,WAAW,SAAS,EAAE;AAAA,EAC1F,SAAS,EAAE,OAAO,WAAW,MAAM,SAAS,QAAQ,EAAE,MAAM,UAAU,WAAW,SAAS,EAAE;AAAA,EAC5F,WAAW,EAAE,OAAO,aAAa,MAAM,SAAS,QAAQ,EAAE,MAAM,UAAU,WAAW,SAAS,EAAE;AAAA,EAChG,WAAW,EAAE,OAAO,aAAa,MAAM,SAAS,QAAQ,EAAE,MAAM,UAAU,WAAW,SAAS,EAAE;AAClG;AAEO,SAAS,UAAU,OAAkC;AAC1D,SAAO,OAAO,UAAU,YAAa,UAAgC,SAAS,KAAK;AACrF;AAEO,SAAS,aAAa,MAA4B;AACvD,SAAO,UAAU,OAAO,CAAC,OAAO,kBAAkB,EAAE,EAAE,SAAS,IAAI;AACrE;;;AC/DO,IAAM,uBAAuB;AAEpC,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,iBAAiB,MAAe,MAA0B;AACjE,MAAI,CAAC,SAAS,IAAI,EAAG,QAAO;AAC5B,MAAI,KAAK,SAAS,QAAQ;AACxB,QAAI,CAAC,SAAS,KAAK,IAAI,KAAK,CAAC,MAAM,QAAQ,KAAK,KAAK,KAAK,EAAG,QAAO;AACpE,SAAK,KAAK,GAAG,KAAK,KAAK,KAAK;AAC5B,WAAO;AAAA,EACT;AACA,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO,MAAM,QAAQ,KAAK,IAAI,KAAK,KAAK,KAAK,MAAM,CAAC,UAAU,iBAAiB,OAAO,IAAI,CAAC;AAAA,EAC7F;AACA,SAAO;AACT;AAEA,SAAS,aAAa,MAAwC;AAC5D,QAAM,WAAW,KAAK,gBAAgB,gBAAgB,KAAK,gBAAgB;AAC3E,SAAO,YAAY,OAAO,KAAK,UAAU,YAAY,OAAO,KAAK,WAAW;AAC9E;AAEA,SAAS,oBACP,QACA,SACS;AACT,QAAM,MAAM,OAAO,KAAK,MAAM;AAC9B,SACE,IAAI,SAAS,KACb,IAAI,MAAM,CAAC,OAAO;AAChB,UAAM,QAAQ,OAAO,EAAE;AACvB,WACE,UAAU,EAAE,KACZ,QAAQ,SAAS,EAAE,KACnB,SAAS,KAAK,KACd,MAAM,OAAO,MACb,MAAM,qBAAqB;AAAA,EAE/B,CAAC;AAEL;AAEA,SAAS,qBAAqB,UAAmB,MAA0B;AACzE,MAAI,aAAa,OAAW,QAAO;AACnC,MAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG,QAAO;AACrC,SAAO,SAAS,MAAM,CAAC,UAAU;AAC/B,QAAI,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS,MAAM,IAAI,KAAK,CAAC,MAAM,QAAQ,MAAM,KAAK,KAAK,EAAG,QAAO;AAC1F,SAAK,KAAK,GAAG,MAAM,KAAK,KAAK;AAC7B,WAAO;AAAA,EACT,CAAC;AACH;AAGO,SAAS,gBACd,OACA,UAA8B,WACH;AAC3B,MAAI,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS,MAAM,IAAI,KAAK,CAAC,SAAS,MAAM,MAAM,EAAG,QAAO;AACjF,QAAM,EAAE,MAAM,OAAO,IAAI;AACzB,MAAI,CAAC,aAAa,IAAI,KAAK,CAAC,oBAAoB,QAAQ,OAAO,EAAG,QAAO;AAEzE,QAAM,QAAmB,CAAC;AAC1B,MAAI,CAAC,iBAAiB,KAAK,MAAM,KAAK,EAAG,QAAO;AAChD,MAAI,CAAC,qBAAqB,MAAM,gBAAgB,KAAK,EAAG,QAAO;AAC/D,QAAM,oBAAoB,MAAM,MAAM,CAAC,OAAO,OAAO,OAAO,YAAY,MAAM,MAAM;AACpF,QAAM,qBAAqB,OAAO,KAAK,MAAM,EAAE,MAAM,CAAC,OAAO,MAAM,SAAS,EAAE,CAAC;AAC/E,SAAO,qBAAqB,qBAAsB,QAA0C;AAC9F;;;ACrBA,IAAM,4BAA4B;AAElC,SAASA,UAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,oBAAoC;AAC3C,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI;AACF,WAAO,OAAO;AAAA,EAChB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,cAAc,WAA0B,KAAqB;AACpE,SAAO,YAAY,GAAG,GAAG,IAAI,SAAS,KAAK;AAC7C;AAGA,SAAS,YAAY,SAAyB,KAAkC;AAC9E,MAAI,CAAC,QAAS,QAAO,CAAC;AACtB,MAAI;AACF,UAAM,MAAM,QAAQ,QAAQ,GAAG;AAC/B,QAAI,CAAC,IAAK,QAAO,CAAC;AAClB,UAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,QAAI,CAACA,UAAS,MAAM,EAAG,QAAO,CAAC;AAE/B,UAAM,cAAmC,CAAC;AAC1C,QAAI,OAAO,OAAO,oBAAoB,WAAW;AAC/C,kBAAY,kBAAkB,OAAO;AAAA,IACvC;AACA,QAAI,OAAO,OAAO,iBAAiB,YAAY,OAAO,SAAS,OAAO,YAAY,GAAG;AACnF,kBAAY,eAAe,OAAO;AAAA,IACpC;AACA,QAAI,OAAO,OAAO,eAAe,WAAW;AAC1C,kBAAY,aAAa,OAAO;AAAA,IAClC;AACA,QACE,OAAO,OAAO,gBAAgB,YAC9B,OAAO,SAAS,OAAO,WAAW,KAClC,OAAO,eAAe,KACtB,OAAO,eAAe,GACtB;AACA,kBAAY,cAAc,OAAO;AAAA,IACnC;AACA,QAAI,OAAO,kBAAkB,cAAc,OAAO,kBAAkB,UAAU;AAC5E,kBAAY,gBAAgB,OAAO;AAAA,IACrC,WAAW,OAAO,OAAO,sBAAsB,WAAW;AACxD,kBAAY,gBAAgB,OAAO,oBAAoB,aAAa;AAAA,IACtE;AACA,QAAI,MAAM,QAAQ,OAAO,YAAY,GAAG;AACtC,kBAAY,eAAe,OAAO,aAAa;AAAA,QAC7C,CAAC,MAA4B,OAAO,MAAM;AAAA,MAC5C;AAAA,IACF;AACA,QAAI,OAAO,OAAO,gBAAgB,WAAW;AAC3C,kBAAY,cAAc,OAAO;AAAA,IACnC;AACA,QAAI,OAAO,OAAO,gBAAgB,WAAW;AAC3C,kBAAY,cAAc,OAAO;AAAA,IACnC;AACA,QAAI,OAAO,OAAO,iBAAiB,WAAW;AAC5C,kBAAY,eAAe,OAAO;AAAA,IACpC;AACA,QAAI,OAAO,OAAO,uBAAuB,WAAW;AAClD,kBAAY,qBAAqB,OAAO;AAAA,IAC1C;AACA,QAAI,OAAO,OAAO,gBAAgB,YAAY,OAAO,SAAS,OAAO,WAAW,GAAG;AACjF,kBAAY,cAAc,OAAO;AAAA,IACnC;AACA,QAAI,OAAO,OAAO,eAAe,WAAW;AAC1C,kBAAY,aAAa,OAAO;AAAA,IAClC;AACA,QAAI,OAAO,OAAO,wBAAwB,WAAW;AACnD,kBAAY,sBAAsB,OAAO;AAAA,IAC3C;AACA,QAAI,OAAO,OAAO,uBAAuB,WAAW;AAClD,kBAAY,qBAAqB,OAAO;AAAA,IAC1C;AACA,QAAI,OAAO,OAAO,sBAAsB,WAAW;AACjD,kBAAY,oBAAoB,OAAO;AAAA,IACzC;AACA,QAAI,OAAO,UAAU,WAAW,OAAO,UAAU,OAAQ,aAAY,QAAQ,OAAO;AACpF,QAAI,OAAO,oBAAoB,UAAU,OAAO,oBAAoB,SAAS;AAC3E,kBAAY,kBAAkB,OAAO;AAAA,IACvC;AACA,QAAI,OAAO,qBAAqB,SAAS,OAAO,qBAAqB,UAAU;AAC7E,kBAAY,mBAAmB,OAAO;AAAA,IACxC;AACA,QACE,OAAO,OAAO,8BAA8B,YAC5C,OAAO,SAAS,OAAO,yBAAyB,GAChD;AACA,kBAAY,4BAA4B,OAAO;AAAA,IACjD;AACA,QAAI,OAAO,OAAO,sBAAsB,WAAW;AACjD,kBAAY,oBAAoB,OAAO;AAAA,IACzC;AACA,QAAI,OAAO,OAAO,2BAA2B,WAAW;AACtD,kBAAY,yBAAyB,OAAO;AAAA,IAC9C;AACA,QAAI,OAAO,OAAO,0BAA0B,WAAW;AACrD,kBAAY,wBAAwB,OAAO;AAAA,IAC7C;AACA,UAAM,aAAa,gBAAgB,OAAO,UAAU;AACpD,QAAI,WAAY,aAAY,aAAa;AACzC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAKO,SAAS,wBACd,UAA0B,kBAAkB,GAC5C,YAA2B,MAC3B,MAAc,2BACO;AACrB,QAAM,SAAS,YAAY,SAAS,cAAc,WAAW,GAAG,CAAC;AACjE,MAAI,CAAC,aAAa,OAAO,KAAK,MAAM,EAAE,SAAS,EAAG,QAAO;AACzD,SAAO,YAAY,SAAS,GAAG;AACjC;AAEO,SAAS,yBACd,OACA,UAA0B,kBAAkB,GAC5C,YAA2B,MAC3B,MAAc,2BACd;AACA,MAAI,CAAC,QAAS;AACd,MAAI;AACF,UAAM,OAAO;AAAA,MACX,GAAG,wBAAwB,SAAS,WAAW,GAAG;AAAA,MAClD,GAAG;AAAA,IACL;AACA,YAAQ,QAAQ,cAAc,WAAW,GAAG,GAAG,KAAK,UAAU,IAAI,CAAC;AAAA,EACrE,QAAQ;AAAA,EAER;AACF;;;AC9LO,IAAM,4BAA4B;AACzC,IAAM,8BAA8B;AAKpC,IAAM,kBAAkB;AACxB,IAAM,iBAAiB;AACvB,IAAM,yBAAyB;AAExB,SAAS,0BAA0B,oBAAoC;AAC5E,MAAI,CAAC,OAAO,SAAS,kBAAkB,KAAK,sBAAsB,EAAG,QAAO;AAC5E,QAAM,4BAA4B,qBAAqB;AACvD,SAAO,KAAK,IAAI,KAAK,KAAK,MAAO,4BAA4B,qBAAsB,GAAG,CAAC;AACzF;AAEO,SAAS,yBAAyB,SAAiB,oBAAoC;AAC5F,MAAI,CAAC,OAAO,SAAS,OAAO,EAAG,QAAO;AACtC,QAAM,iBAAiB,0BAA0B,kBAAkB;AACnE,SAAO,KAAK,IAAI,2BAA2B,KAAK,IAAI,gBAAgB,KAAK,MAAM,OAAO,CAAC,CAAC;AAC1F;AAEO,SAAS,uBACd,UACA,mBACA,oBACQ;AACR,SAAO,aAAa,QAAQ,MAAM,yBAAyB,mBAAmB,kBAAkB;AAClG;AAeO,SAAS,yBACd,wBACA,oBACQ;AACR,MACE,CAAC,OAAO,SAAS,sBAAsB,KACvC,0BAA0B,KAC1B,CAAC,OAAO,SAAS,kBAAkB,KACnC,sBAAsB,GACtB;AACA,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACJ,yBAAyB,qBAAsB;AAAA,IAChD;AAAA,EACF;AACF;AAEO,SAAS,2BACd,oBACA,UACA,mBACQ;AACR,MAAI,CAAC,OAAO,SAAS,kBAAkB,KAAK,sBAAsB,EAAG,QAAO;AAC5E,QAAM,cAAc,uBAAuB,UAAU,mBAAmB,kBAAkB;AAC1F,SAAO,aAAa,QAAQ,qBAAqB,sBAAsB,cAAc;AACvF;AAEO,SAAS,2BACd,WACA,UACA,mBACA,oBACQ;AACR,QAAM,UAAU,uBAAuB,UAAU,mBAAmB,kBAAkB;AACtF,QAAM,OAAO,cAAc,OAAO,UAAU,iBAAiB,UAAU;AACvE,SAAO,yBAAyB,MAAM,kBAAkB;AAC1D;AAEO,SAAS,4BACd,QACA,UACA,mBACA,oBACQ;AACR,QAAM,UAAU,uBAAuB,UAAU,mBAAmB,kBAAkB;AACtF,MAAI,CAAC,OAAO,SAAS,MAAM,KAAK,WAAW,EAAG,QAAO;AACrD,SAAO;AAAA,IACL,UAAU,KAAK,IAAI,CAAC,SAAS,sBAAsB;AAAA,IACnD;AAAA,EACF;AACF;AAEA,IAAM,UAAU,KAAK,IAAI,yBAAyB;AAM3C,SAAS,4BAA4B,SAAiB,oBAAoC;AAC/F,QAAM,UAAU,yBAAyB,SAAS,kBAAkB;AACpE,QAAM,SAAS,KAAK,IAAI,0BAA0B,kBAAkB,CAAC;AACrE,UAAS,KAAK,IAAI,OAAO,IAAI,YAAY,SAAS,WAAY;AAChE;AAMO,SAAS,4BAA4B,QAAgB,oBAAoC;AAC9F,QAAM,gBAAgB,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,MAAM,CAAC;AACvD,QAAM,SAAS,KAAK,IAAI,0BAA0B,kBAAkB,CAAC;AACrE,QAAM,WAAW,UAAW,gBAAgB,OAAQ,SAAS;AAC7D,SAAO,yBAAyB,KAAK,IAAI,QAAQ,GAAG,kBAAkB;AACxE;;;AC9DO,SAAS,gCACd,SACA,OACS;AACT,SACE,QAAQ,cAAc,MAAM,qBAC5B,QAAQ,iBAAiB,MAAM;AAEnC;AAOO,SAAS,4BACd,SACA,OACS;AACT,SACE,QAAQ,cAAc,MAAM,qBAC5B,QAAQ,iBAAiB,MAAM,wBAC/B,QAAQ,cAAc,MAAM;AAEhC;AA6DO,SAAS,oBACd,KACA,4BACe;AACf,SAAO;AAAA,IACL,mBAAmB,oBAAI,IAAI;AAAA,IAC3B,wBAAwB,CAAC,QACvB,IAAI,CAAC,UAAU;AACb,YAAM,OAAO,IAAI,IAAI,MAAM,iBAAiB;AAC5C,UAAI,KAAK,IAAI,GAAG,EAAG,MAAK,OAAO,GAAG;AAAA,UAC7B,MAAK,IAAI,GAAG;AACjB,aAAO,EAAE,mBAAmB,KAAK;AAAA,IACnC,CAAC;AAAA,IACH,wBAAwB,MAAM,IAAI,EAAE,mBAAmB,oBAAI,IAAI,EAAE,CAAC;AAAA,IAElE,iBAAiB,oBAAI,IAAI;AAAA,IACzB,oBAAoB,CAAC,OACnB,IAAI,CAAC,UAAU;AACb,YAAM,OAAO,IAAI,IAAI,MAAM,eAAe;AAC1C,UAAI,KAAK,IAAI,EAAE,EAAG,MAAK,OAAO,EAAE;AAAA,UAC3B,MAAK,IAAI,EAAE;AAChB,aAAO,EAAE,iBAAiB,KAAK;AAAA,IACjC,CAAC;AAAA,IACH,iBAAiB,CAAC,IAAI,aACpB,IAAI,CAAC,UAAU;AACb,UAAI,MAAM,gBAAgB,IAAI,EAAE,MAAM,SAAU,QAAO;AACvD,YAAM,OAAO,IAAI,IAAI,MAAM,eAAe;AAC1C,UAAI,SAAU,MAAK,IAAI,EAAE;AAAA,UACpB,MAAK,OAAO,EAAE;AACnB,aAAO,EAAE,iBAAiB,KAAK;AAAA,IACjC,CAAC;AAAA,IACH,aAAa,CAAC,QACZ,IAAI,CAAC,UAAU;AACb,UAAI,IAAI,MAAM,CAAC,OAAO,MAAM,gBAAgB,IAAI,EAAE,CAAC,EAAG,QAAO;AAC7D,YAAM,OAAO,IAAI,IAAI,MAAM,eAAe;AAC1C,iBAAW,MAAM,IAAK,MAAK,IAAI,EAAE;AACjC,aAAO,EAAE,iBAAiB,KAAK;AAAA,IACjC,CAAC;AAAA,IAEH,mBAAmB,oBAAI,IAAI;AAAA,IAC3B,qBAAqB,CAAC,OACpB,IAAI,CAAC,UAAU;AACb,YAAM,OAAO,IAAI,IAAI,MAAM,iBAAiB;AAC5C,UAAI,KAAK,IAAI,EAAE,EAAG,MAAK,OAAO,EAAE;AAAA,UAC3B,MAAK,IAAI,EAAE;AAChB,aAAO,EAAE,mBAAmB,KAAK;AAAA,IACnC,CAAC;AAAA,IAEH,sBAAsB,oBAAI,IAAI;AAAA,IAC9B,yBAAyB,CAAC,OACxB,IAAI,CAAC,UAAU;AACb,YAAM,OAAO,IAAI,IAAI,MAAM,oBAAoB;AAC/C,UAAI,KAAK,IAAI,EAAE,EAAG,MAAK,OAAO,EAAE;AAAA,UAC3B,MAAK,IAAI,EAAE;AAChB,aAAO,EAAE,sBAAsB,KAAK;AAAA,IACtC,CAAC;AAAA,IAEH,oBAAoB;AAAA,IACpB,yBAAyB;AAAA,IACzB,uBAAuB,CAAC,WACtB,IAAI,CAAC,UAAU;AACb,YAAM,QAAQ,MAAM,0BAA0B;AAC9C,YAAM,EAAE,mBAAmB,qBAAqB,IAAI,2BAA2B;AAC/E,aAAO;AAAA,QACL,yBAAyB;AAAA,QACzB,oBAAoB;AAAA,UAClB,GAAG;AAAA,UACH,WAAW;AAAA,UACX,cAAc;AAAA,UACd;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,IACH,yBAAyB,CAAC,UACxB;AAAA,MAAI,CAAC,UACH,MAAM,oBAAoB,UAAU,QAAQ,EAAE,oBAAoB,KAAK,IAAI;AAAA,IAC7E;AAAA,IAEF,uBAAuB;AAAA,IACvB,sBAAsB;AAAA,IACtB,0BAA0B,CAAC,WACzB,IAAI,CAAC,UAAU;AACb,YAAM,QAAQ,MAAM,uBAAuB;AAC3C,YAAM,EAAE,mBAAmB,qBAAqB,IAAI,2BAA2B;AAC/E,aAAO;AAAA,QACL,sBAAsB;AAAA,QACtB,uBAAuB;AAAA,UACrB,GAAG;AAAA,UACH,WAAW;AAAA,UACX,cAAc;AAAA,UACd;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,IACH,4BAA4B,CAAC,UAC3B;AAAA,MAAI,CAAC,UACH,MAAM,uBAAuB,UAAU,QAAQ,EAAE,uBAAuB,KAAK,IAAI;AAAA,IACnF;AAAA,IAEF,eAAe,oBAAI,IAAI;AAAA,IACvB,kBAAkB,CAAC,WAAW,SAC5B,IAAI,CAAC,UAAU;AAIb,UACE,OAAO,MAAM,cAAc,IAAI,SAAS,MAAM,OAAO,CAAC,MAAM,cAAc,IAAI,SAAS;AAEvF,eAAO;AACT,YAAM,OAAO,IAAI,IAAI,MAAM,aAAa;AACxC,UAAI,KAAM,MAAK,IAAI,WAAW,IAAI;AAAA,UAC7B,MAAK,OAAO,SAAS;AAC1B,aAAO,EAAE,eAAe,KAAK;AAAA,IAC/B,CAAC;AAAA,IACH,gBAAgB,oBAAI,IAAI;AAAA,IACxB,mBAAmB,CAAC,WAAW,eAC7B,IAAI,CAAC,UAAU;AACb,UACE,aACI,MAAM,eAAe,IAAI,SAAS,MAAM,aACxC,CAAC,MAAM,eAAe,IAAI,SAAS;AAEvC,eAAO;AACT,YAAM,OAAO,IAAI,IAAI,MAAM,cAAc;AACzC,UAAI,WAAY,MAAK,IAAI,WAAW,UAAU;AAAA,UACzC,MAAK,OAAO,SAAS;AAC1B,aAAO,EAAE,gBAAgB,KAAK;AAAA,IAChC,CAAC;AAAA,EACL;AACF;;;AC5OO,SAAS,+BACd,KAC0B;AAC1B,SAAO;AAAA,IACL,qBAAqB;AAAA,IACrB,wBAAwB,CAAC,QACvB,IAAI;AAAA,MACF,qBAAqB;AAAA,QACnB,GAAG;AAAA,QACH,IAAI,KAAK,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA,QAC3B,IAAI,KAAK,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA,QAC3B,IAAI,KAAK,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA,QAC3B,IAAI,KAAK,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA,MAC7B;AAAA,IACF,CAAC;AAAA,IACH,0BAA0B,MAAM,IAAI,EAAE,qBAAqB,KAAK,CAAC;AAAA,EACnE;AACF;;;ACpBO,SAAS,uBACd,KACkB;AAClB,SAAO;AAAA,IACL,iBAAiB;AAAA,IACjB,oBAAoB,CAAC,UAAU,IAAI,EAAE,iBAAiB,MAAM,CAAC;AAAA,IAC7D,2BAA2B;AAAA,IAC3B,8BAA8B,CAAC,cAAc,IAAI,EAAE,2BAA2B,UAAU,CAAC;AAAA,IACzF,qBAAqB;AAAA,IACrB,wBAAwB,CAAC,YAAY,IAAI,EAAE,qBAAqB,QAAQ,CAAC;AAAA,EAC3E;AACF;;;ACrCO,SAAS,2BACd,IACA,WACA,cACA,OACsB;AACtB,SAAO,EAAE,IAAI,WAAW,cAAc,MAAM;AAC9C;;;ACRA,IAAM,YAAY,YAAY,KAAK;AAEnC,IAAM,+BACJ,cAAc,kBAAkB,cAAc,mBAAmB,YAAY;AAExE,SAAS,qBACd,YACA,SAAiC,8BAClB;AACf,SAAO,eAAe,WAAW,mBAAmB,WAAW;AACjE;AAEO,SAAS,uBACd,eACA,SAAiC,8BAClB;AACf,SAAO,WAAW,iBAAiB,WAAW;AAChD;;;AClBA,IAAM,oBAAoB;AAG1B,SAAS,YAAY,MAAsB;AACzC,UAAQ,KAAK,MAAM,MAAM,EAAE,CAAC,KAAK,IAAI,QAAQ,OAAO,GAAG,EAAE,QAAQ,UAAU,EAAE;AAC/E;AAWO,SAAS,oBACd,WACA,iBACQ;AACR,UAAQ,UAAU,iBAAiB,KAAK,MAAM,UAAU,YAAY,eAAe,CAAC,KAAK;AAC3F;AAEO,SAAS,qBAAqB,KAA2D;AAC9F,SAAO;AAAA,IACL,eAAe,qBAAqB,wBAAwB,EAAE,aAAa;AAAA,IAC3E,oBAAoB,CAAC;AAAA,IACrB,kBAAkB,CAAC,SAAS;AAC1B,+BAAyB,EAAE,eAAe,KAAK,CAAC;AAChD,UAAI,EAAE,eAAe,KAAK,CAAC;AAAA,IAC7B;AAAA,IACA,wBAAwB,CAAC,iBACvB,IAAI,CAAC,EAAE,mBAAmB,MAAM;AAC9B,YAAM,OAAO,EAAE,GAAG,mBAAmB;AACrC,iBAAW,QAAQ,cAAc,IAAI,WAAW,KAAK,CAAC,iBAAiB,GAAG;AACxE,aAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK;AAAA,MACnC;AACA,aAAO,EAAE,oBAAoB,KAAK;AAAA,IACpC,CAAC;AAAA,EACL;AACF;;;ACvCA,SAAS,kCAAkC;AAkB3C,IAAI,0BAA0B;AAC9B,IAAM,eAAe,oBAAI,QAAkB;AAC3C,IAAI,mBAAiE;AAI9D,SAAS,8BAGd;AACA;AACA,SAAO,EAAE,eAAe,OAAO,eAAe,MAAM;AACtD;AAEO,SAAS,6BACd,KACwB;AACxB,QAAM,SAAS,CAAC,KAAe,eAC7B,2BAA2B,KAAK,MAAM;AACpC,QAAI,eAAe,wBAAyB,KAAI,EAAE,eAAe,KAAK,CAAC;AAAA,EACzE,CAAC;AACH,SAAO;AAAA,IACL,eAAe;AAAA,IACf,eAAe;AAAA,IACf,mBAAmB,MAAM,IAAI,EAAE,eAAe,KAAK,CAAC;AAAA,IACpD,kBAAkB,CAAC,UAAU;AAC3B;AACA,UAAI,EAAE,eAAe,MAAM,CAAC;AAAA,IAC9B;AAAA,IACA,sBAAsB,CAAC,QAAQ;AAC7B,YAAM,aAAa,EAAE;AACrB,UAAI,CAAC,IAAK,QAAO,IAAI,EAAE,eAAe,KAAK,CAAC;AAC5C,UAAI,CAAC,aAAa,IAAI,GAAG,GAAG;AAC1B,2BAAmB,EAAE,KAAK,WAAW;AACrC;AAAA,MACF;AACA,aAAO,KAAK,UAAU;AAAA,IACxB;AAAA,IACA,qBAAqB,CAAC,QAAQ;AAC5B,mBAAa,IAAI,GAAG;AACpB,UAAI,kBAAkB,QAAQ,IAAK;AACnC,YAAM,EAAE,WAAW,IAAI;AACvB,yBAAmB;AACnB,aAAO,KAAK,UAAU;AAAA,IACxB;AAAA,EACF;AACF;;;AC5DO,SAAS,4BAAiD;AAC/D,SAAO,EAAE,gBAAgB,KAAK;AAChC;;;ACPO,SAAS,2BAA2B;AACzC,SAAO;AAAA,IACL,WAAW;AAAA,IACX,aAAa;AAAA,IACb,UAAU;AAAA,IACV,GAAG,4BAA4B;AAAA,IAC/B,cAAc;AAAA,IACd,UAAU,CAAC;AAAA,IACX,mBAAmB;AAAA,IACnB,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,mBAAmB;AAAA,IACnB,iBAAiB;AAAA,IACjB,2BAA2B;AAAA,IAC3B,mBAAmB,oBAAI,IAAY;AAAA;AAAA;AAAA;AAAA,IAInC,qBAAqB;AAAA,IACrB,iBAAiB,oBAAI,IAAY;AAAA;AAAA,IAEjC,mBAAmB,oBAAI,IAAY;AAAA,IACnC,sBAAsB,oBAAI,IAAY;AAAA,IACtC,oBAAoB;AAAA,IACpB,uBAAuB;AAAA,IACvB,oBAAoB,oBAAI,IAAY;AAAA,IACpC,mBAAmB;AAAA,IACnB,uBAAuB,oBAAI,IAAmD;AAAA,IAC9E,eAAe;AAAA,IACf,eAAe,oBAAI,IAAgC;AAAA,IACnD,gBAAgB,oBAAI,IAA6B;AAAA,IACjD,cAAc;AAAA,IACd,WAAW;AAAA,IACX,UAAU,CAAC;AAAA,IACX,UAAU,CAAC;AAAA,IACX,aAAa;AAAA,IACb,cAAc;AAAA,IACd,eAAe,oBAAI,IAAoB;AAAA,IACvC,iBAAiB,CAAC;AAAA,IAClB,yBAAyB,oBAAI,IAAqC;AAAA,EACpE;AACF;;;AfPA,SAAS,wBACP,KACA,QACuE;AACvE,QAAM,qBAAqB,IAAI,IAAI,GAAG;AACtC,MAAI,mBAAmB,SAAS,GAAG;AACjC,WAAO,EAAE,oBAAoB,mBAAmB,KAAK;AAAA,EACvD;AACA,MAAI,UAAU,mBAAmB,IAAI,MAAM,GAAG;AAC5C,WAAO,EAAE,oBAAoB,mBAAmB,OAAO;AAAA,EACzD;AACA,SAAO;AAAA,IACL;AAAA,IACA,mBAAmB,mBAAmB,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACjE;AACF;AAwMA,SAAS,qBACP,OACA,MACM;AACN,QAAM,WAAW,MAAM,YAAY,SAAS,OAAO,IAAI,MAAM;AAC7D,QAAM,MAAM,MAAM,SAAS,OAAO,CAAC,OAAO,KAAK,IAAI,QAAQ,CAAC,EAAE,IAAI,CAAC,OAAO,GAAG,OAAO,GAAG,EAAE;AACzF,QAAM,qBAAqB,IAAI,CAAC,KAAK,IAAI;AACzC,QAAM,sBAAsB,IAAI,IAAI,GAAG,CAAC;AAC1C;AAEO,IAAM,iBAAiB,OAAoB,CAAC,KAAK,SAAS;AAAA,EAC/D,WAAW;AAAA,EACX,aAAa;AAAA,EACb,UAAU;AAAA,EACV,sBAAsB;AAAA,EACtB,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,UAAU,CAAC;AAAA,EACX,mBAAmB;AAAA,EACnB,cAAc,wBAAwB,EAAE,gBAAgB;AAAA,EACxD,YAAY,wBAAwB,EAAE,cAAc;AAAA,EACpD,aAAa,wBAAwB,EAAE,eAAe;AAAA,EACtD,aAAa;AAAA,EACb,UAAU;AAAA,EACV,mBAAmB;AAAA,EACnB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,UAAU;AAAA,EAEV,YAAY;AAAA,EACZ,eAAe,CAAC,SAAS,IAAI,EAAE,YAAY,KAAK,CAAC;AAAA,EACjD,gBAAgB,MAAM,qBAAqB,IAAI,GAAG,CAAC,IAAI,MAAM,GAAG,QAAQ,CAAC;AAAA,EACzE,iBAAiB,MAAM,qBAAqB,IAAI,GAAG,CAAC,IAAI,MAAM,GAAG,QAAQ,GAAG,WAAW,CAAC;AAAA,EAExF,GAAG,oBAAoB,KAAK,OAAO;AAAA,IACjC,mBAAmB,IAAI,EAAE;AAAA,IACzB,sBAAsB,IAAI,EAAE;AAAA,EAC9B,EAAE;AAAA,EACF,GAAG,qBAAqB,GAAG;AAAA,EAE3B,GAAG,+BAA+B,GAAG;AAAA,EACrC,GAAG,uBAAuB,GAAG;AAAA,EAC7B,GAAG,0BAA0B;AAAA,EAC7B,GAAG,6BAA6B,GAAG;AAAA,EAEnC,mBAAmB;AAAA,EACnB,sBAAsB,CAAC,QAAQ,IAAI,EAAE,mBAAmB,IAAI,CAAC;AAAA,EAE7D,oBAAoB,oBAAI,IAAY;AAAA,EACpC,cAAc,CAAC,KAAK,WAAW,IAAI,wBAAwB,KAAK,MAAM,CAAC;AAAA,EACvE,sBAAsB,CAAC,OACrB,IAAI,CAAC,MAAM;AACT,UAAM,OAAO,IAAI,IAAI,EAAE,kBAAkB;AACzC,SAAK,IAAI,EAAE;AACX,WAAO,wBAAwB,MAAM,EAAE,iBAAiB;AAAA,EAC1D,CAAC;AAAA,EACH,yBAAyB,CAAC,OACxB,IAAI,CAAC,MAAM;AACT,UAAM,OAAO,IAAI,IAAI,EAAE,kBAAkB;AACzC,QAAI,KAAK,IAAI,EAAE,EAAG,MAAK,OAAO,EAAE;AAAA,QAC3B,MAAK,IAAI,EAAE;AAChB,WAAO,wBAAwB,MAAM,EAAE,iBAAiB;AAAA,EAC1D,CAAC;AAAA,EACH,gBAAgB,MAAM,IAAI,EAAE,mBAAmB,MAAM,oBAAoB,oBAAI,IAAI,EAAE,CAAC;AAAA,EAEpF,mBAAmB;AAAA,EACnB,aAAa,CAAC,SAAS,IAAI,EAAE,mBAAmB,KAAK,CAAC;AAAA,EACtD,kBAAkB,MAAM,IAAI,EAAE,mBAAmB,KAAK,CAAC;AAAA,EAEvD,iBAAiB;AAAA,EACjB,iBAAiB,CAAC,SAAS,WAAW,SACpC,IAAI,CAAC,OAAO;AAAA,IACV,iBAAiB,EAAE,SAAS,UAAU,QAAQ,EAAE,iBAAiB,SAAS,KAAK,EAAE;AAAA,EACnF,EAAE;AAAA,EACJ,sBAAsB,MAAM,IAAI,EAAE,iBAAiB,KAAK,CAAC;AAAA,EAEzD,mBAAmB;AAAA,EACnB,mBAAmB,CAAC,cAClB,IAAI,CAAC,OAAO;AAAA,IACV,mBAAmB,EAAE,WAAW,QAAQ,EAAE,mBAAmB,SAAS,KAAK,EAAE;AAAA,EAC/E,EAAE;AAAA,EACJ,wBAAwB,MAAM,IAAI,EAAE,mBAAmB,KAAK,CAAC;AAAA,EAE7D,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,sBAAsB,CAAC,OACrB,IAAI,CAAC,MAAM;AACT,UAAM,QAAQ,EAAE,qBAAqB;AACrC,WAAO;AAAA,MACL,oBAAoB;AAAA,MACpB,eAAe;AAAA,QACb;AAAA,QACA,EAAE;AAAA,QACF,EAAE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAAA,EACH,oBAAoB,CAAC,UACnB,IAAI,CAAC,MAAO,EAAE,eAAe,UAAU,QAAQ,EAAE,eAAe,KAAK,IAAI,CAAE;AAAA,EAE7E,uBAAuB,oBAAI,IAAI;AAAA,EAC/B,0BAA0B,CAAC,QAAQ,IAAI,EAAE,uBAAuB,IAAI,CAAC;AAAA,EAErE,cAAc;AAAA,EACd,iBAAiB,CAAC,aAAa,IAAI,EAAE,cAAc,SAAS,CAAC;AAAA,EAE7D,WAAW;AAAA,EACX,cAAc,CAAC,UAAU,IAAI,EAAE,WAAW,MAAM,CAAC;AAAA,EAEjD,UAAU,CAAC;AAAA,EACX,UAAU,CAAC;AAAA,EACX,aAAa;AAAA,EACb,gBAAgB,CAAC,OAAO,IAAI,EAAE,aAAa,GAAG,CAAC;AAAA,EAC/C,iBAAiB,CAAC,MAAM,UAAU;AAChC,QAAI,CAAC,OAAO;AAAA,MACV,WAAW;AAAA,MACX,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,IAAI,KAAK,IAAI,GAAG,MAAM,CAAC;AAAA,MACzE,UAAU,CAAC;AAAA,IACb,EAAE;AACF,QAAI,EAAE,cAAc;AAAA,EACtB;AAAA,EACA,eAAe,MAAM;AACnB,UAAM,IAAI,IAAI;AACd,UAAM,QAAQ,EAAE,SAAS,EAAE,SAAS,SAAS,CAAC;AAC9C,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI;AAAA,MACF,WAAW,MAAM;AAAA,MACjB,UAAU,EAAE,SAAS,MAAM,GAAG,EAAE;AAAA,MAChC,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,IAAI,MAAM,IAAI,OAAO,MAAM,MAAM,CAAC;AAAA,IACtF,CAAC;AACD,QAAI,EAAE,cAAc;AACpB,WAAO,MAAM;AAAA,EACf;AAAA,EACA,kBAAkB,MAAM,IAAI,EAAE,UAAU,CAAC,GAAG,UAAU,CAAC,EAAE,CAAC;AAAA,EAC1D,eAAe,MAAM;AACnB,UAAM,IAAI,IAAI;AACd,UAAM,QAAQ,EAAE,SAAS,EAAE,SAAS,SAAS,CAAC;AAC9C,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI;AAAA,MACF,WAAW,MAAM;AAAA,MACjB,UAAU,EAAE,SAAS,MAAM,GAAG,EAAE;AAAA,MAChC,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,IAAI,MAAM,IAAI,OAAO,MAAM,MAAM,CAAC;AAAA,IACtF,CAAC;AACD,QAAI,EAAE,cAAc;AACpB,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,cAAc;AAAA,EACd,iBAAiB,CAAC,UAAU,IAAI,EAAE,cAAc,MAAM,CAAC;AAAA,EACvD,eAAe,oBAAI,IAAI;AAAA,EACvB,kBAAkB,CAAC,QAAQ,IAAI,EAAE,eAAe,IAAI,CAAC;AAAA,EACrD,iBAAiB,CAAC;AAAA,EAClB,oBAAoB,CAAC,aAAa,IAAI,EAAE,iBAAiB,SAAS,CAAC;AAAA,EACnE,yBAAyB,oBAAI,IAAI;AAAA,EACjC,4BAA4B,CAAC,UAAU,IAAI,EAAE,yBAAyB,MAAM,CAAC;AAAA,EAE7E,cAAc,CAAC,YAAY;AACzB,QAAI,IAAI,EAAE,cAAc,QAAS;AACjC,QAAI,EAAE,WAAW,QAAQ,CAAC;AAAA,EAC5B;AAAA,EACA,iBAAiB,CAAC,SAAS;AACzB,6BAAyB,EAAE,cAAc,KAAK,CAAC;AAC/C,QAAI,EAAE,cAAc,KAAK,CAAC;AAAA,EAC5B;AAAA,EACA,eAAe,CAAC,UAAU;AACxB,6BAAyB,EAAE,YAAY,MAAM,CAAC;AAC9C,QAAI,EAAE,YAAY,MAAM,CAAC;AAAA,EAC3B;AAAA,EACA,gBAAgB,CAAC,WAAW;AAC1B,UAAM,aAAa,OAAO,SAAS,MAAM,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,MAAM,CAAC,IAAI;AAChF,6BAAyB,EAAE,aAAa,WAAW,CAAC;AACpD,QAAI,EAAE,aAAa,WAAW,CAAC;AAAA,EACjC;AAAA,EACA,gBAAgB,CAAC,YAAY,IAAI,EAAE,aAAa,QAAQ,CAAC;AAAA,EACzD,aAAa,CAAC,SAAS,IAAI,EAAE,UAAU,KAAK,CAAC;AAAA,EAC7C,yBAAyB,MAAM,IAAI,EAAE,oBAAoB,oBAAI,IAAI,EAAE,CAAC;AAAA,EACpE,uBAAuB,CAAC,QAAqB,IAAI,EAAE,oBAAoB,IAAI,IAAI,GAAG,EAAE,CAAC;AAAA,EACrF,qBAAqB,wBAAwB,EAAE,uBAAuB;AAAA,EACtE,wBAAwB,CAAC,YAAY;AACnC,6BAAyB,EAAE,qBAAqB,QAAQ,CAAC;AACzD,QAAI,EAAE,qBAAqB,QAAQ,CAAC;AAAA,EACtC;AAAA,EACA,mBAAmB,wBAAwB,EAAE,qBAAqB;AAAA;AAAA,EAClE,sBAAsB,CAAC,YAAY;AACjC,6BAAyB,EAAE,mBAAmB,QAAQ,CAAC;AACvD,QAAI,EAAE,mBAAmB,QAAQ,CAAC;AAAA,EACpC;AAAA,EACA,iBAAiB,wBAAwB,EAAE,mBAAmB;AAAA,EAC9D,oBAAoB,CAAC,SAAS;AAC5B,6BAAyB,EAAE,iBAAiB,KAAK,CAAC;AAClD,QAAI,EAAE,iBAAiB,KAAK,CAAC;AAAA,EAC/B;AAAA,EACA,iBAAiB,CAAC,wBAAwB,uBACxC,IAAI,CAAC,MAAM;AAET,QAAI,EAAE,aAAa,SAAU,QAAO,CAAC;AACrC,UAAM,UAAU,yBAAyB,wBAAwB,kBAAkB;AACnF,6BAAyB;AAAA,MACvB,kBAAkB;AAAA,MAClB,2BAA2B;AAAA,IAC7B,CAAC;AACD,WAAO,EAAE,UAAU,UAAU,mBAAmB,QAAQ;AAAA,EAC1D,CAAC;AAAA,EACH,kBAAkB,CAAC,KAAK,WAAW;AACjC,UAAM,QAAQ,IAAI;AAClB,QAAI,MAAM,gBAAgB,OAAO,MAAM,mBAAmB,OAAQ;AAClE,QAAI,EAAE,aAAa,KAAK,gBAAgB,OAAO,CAAC;AAAA,EAClD;AAAA,EACA,YAAY,CAAC,SACX,IAAI,CAAC,UAAU;AACb,UAAM,IAAI,SAAS,QAAQ,OAAO,SAAS,IAAI,IAAI,OAAO;AAC1D,WAAO;AAAA,MACL,SAAS;AAAA,MACT,UACE,MAAM,QAAQ,MAAM,aAAa,QAAQ,KAAK,MAAM,WAAW,OAAO,MAAM;AAAA;AAAA;AAAA,MAG9E,aAAa,MAAM,OAAO,OAAO,MAAM;AAAA,IACzC;AAAA,EACF,CAAC;AAAA,EACH,aAAa,CAAC,SACZ,IAAI,CAAC,UAAU;AACb,UAAM,IAAI,SAAS,QAAQ,OAAO,SAAS,IAAI,IAAI,OAAO;AAC1D,WAAO;AAAA,MACL,UAAU;AAAA,MACV,SAAS,MAAM,QAAQ,MAAM,YAAY,QAAQ,KAAK,MAAM,UAAU,OAAO,MAAM;AAAA,MACnF,aAAa,MAAM,OAAO,OAAO,MAAM;AAAA,IACzC;AAAA,EACF,CAAC;AAAA,EACH,sBAAsB,CAAC,YACrB,IAAI,CAAC,WAAW;AAAA,IACd,mBAAmB,yBAAyB,SAAS,MAAM,cAAc;AAAA,EAC3E,EAAE;AAAA,EACJ,kBAAkB,MAAM,IAAI,CAAC,WAAW,EAAE,cAAc,MAAM,eAAe,EAAE,EAAE;AAAA,EACjF,gBAAgB,CAAC,SAAS,IAAI,EAAE,aAAa,OAAO,SAAS,IAAI,IAAI,OAAO,EAAE,CAAC;AAAA,EAC/E,aAAa,CAAC,aAAa,IAAI,EAAE,UAAU,OAAO,SAAS,QAAQ,IAAI,WAAW,EAAE,CAAC;AAAA,EACrF,iBAAiB,CAAC,aAAa,IAAI,EAAE,cAAc,SAAS,CAAC;AAAA,EAC7D,aAAa,CAAC,aAAa,IAAI,EAAE,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA,EAI3C,sBAAsB,CAAC,IAAI,YACzB,IAAI,CAAC,MAAM;AACT,UAAM,qBAAqB,iBAAiB,EAAE,oBAAoB,IAAI,SAAS,WAAW;AAgB1F,UAAM,iBAAiB,uBAAuB,EAAE,uBAAuB,EAAE;AACzE,WAAO,OAAO,EAAE,oBACZ;AAAA,MACE,mBAAmB;AAAA,MACnB;AAAA,MACA,mBAAmB;AAAA,MACnB,iBAAiB;AAAA,MACjB,oBAAoB;AAAA,MACpB,GAAI,iBAAiB,CAAC,IAAI,EAAE,uBAAuB,KAAK;AAAA,IAC1D,IACA,EAAE,mBAAmB,IAAI,mBAAmB;AAAA,EAClD,CAAC;AAAA;AAAA;AAAA;AAAA,EAIH,oBAAoB,CAAC,OACnB,IAAI,CAAC,MAAM;AACT,QAAI,MAAM,QAAQ,EAAE,mBAAmB,OAAO,KAAK,EAAE,mBAAmB,IAAI,EAAE,GAAG;AAC/E,aAAO;AAAA,QACL,mBAAmB;AAAA,QACnB,oBAAoB,OAAO,EAAE,oBAAoB,EAAE,qBAAqB;AAAA,MAC1E;AAAA,IACF;AACA,WAAO;AAAA,MACL,mBAAmB;AAAA,MACnB,oBAAoB,KAAK,oBAAI,IAAI,CAAC,EAAE,CAAC,IAAI,oBAAI,IAAY;AAAA,MACzD,oBAAoB,OAAO,EAAE,oBAAoB,EAAE,qBAAqB;AAAA,IAC1E;AAAA,EACF,CAAC;AAAA,EACH,eAAe,CAAC,WAAW,YACzB,IAAI,CAAC,WAAW;AAAA,IACd,UAAU,MAAM,SAAS;AAAA,MAAI,CAAC,QAC3B,GAAG,OAAO,GAAG,QAAQ,YAAY,EAAE,GAAG,IAAI,GAAG,QAAQ,IAAI;AAAA,IAC5D;AAAA,EACF,EAAE;AAAA;AAAA;AAAA;AAAA,EAIJ,sBAAsB,CAAC,cACrB,IAAI,CAAC,UAAU;AACb,QAAI,MAAM,sBAAsB,UAAW,QAAO;AAClD,WAAO;AAAA,MACL,GAAG,yBAAyB;AAAA,MAC5B,sBAAsB,MAAM,uBAAuB;AAAA,MACnD,mBAAmB;AAAA,IACrB;AAAA,EACF,CAAC;AAAA,EACH,OAAO,MAAM,IAAI,yBAAyB,CAAC;AAC7C,EAAE;AAEF,2BAA2B,cAAc;AAElC,SAAS,gBAAgB,WAA4B;AAC1D,QAAM,EAAE,eAAe,kBAAkB,IAAI,eAAe,SAAS;AACrE,SAAO,iBAAiB,sBAAsB;AAChD;AAIO,SAAS,kBAAkB,WAAqC;AACrE,QAAM,aAAa,eAAe,SAAS,EAAE;AAC7C,MAAI,OAAO;AACX,QAAM,SAAS,CAAC,UAAuC;AACrD,QAAI,MAAM,sBAAsB,WAAW;AACzC,aAAO;AACP,aAAO,MAAM,gBAAgB,OAAO;AAAA,IACtC;AACA,WAAO,QAAQ,MAAM,sBAAsB,aAAa,QAAQ;AAAA,EAClE;AACA,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,MAAM,OAAO,eAAe,SAAS,CAAC;AAC5C,QAAI,QAAQ,KAAM,QAAO,QAAQ,GAAG;AACpC,UAAM,OAAO,eAAe,UAAU,CAAC,UAAU;AAC/C,YAAM,SAAS,OAAO,KAAK;AAC3B,UAAI,WAAW,KAAM;AACrB,WAAK;AACL,cAAQ,MAAM;AAAA,IAChB,CAAC;AAAA,EACH,CAAC;AACH;;;AgBtlBA,SAAS,YAAY,aAAAC,YAAW,QAAQ,gBAAgB;AACxD,SAAS,+BAA+B;;;ACFxC,SAAS,iBAAiB;AAcnB,SAAS,eAAe,QAAmC;AAEhE,YAAU,QAAQ,CAAC,CAAC;AACtB;;;ACjBA,SAAS,UAAAC,eAAc;AAgBhB,IAAM,2BAA2BA,QAA8B,CAAC,SAAS;AAAA,EAC9E,QAAQ;AAAA,EACR,WAAW,CAAC,WAAW,IAAI,EAAE,QAAQ,UAAU,OAAO,KAAK,MAAM,EAAE,SAAS,IAAI,SAAS,KAAK,CAAC;AACjG,EAAE;AAOK,SAAS,2BAA2B,KAAgB;AACzD,QAAM,SAAS,yBAAyB,SAAS,EAAE;AACnD,MAAI,QAAQ;AACV,QAAI,aAAa,IAAI,aAAa,KAAK,UAAU,MAAM,CAAC;AAAA,EAC1D,OAAO;AACL,QAAI,aAAa,OAAO,WAAW;AAAA,EACrC;AACF;;;AFocQ,cA+BI,YA/BJ;AAxdR,IAAI,eAAwC;AAErC,SAAS,mBAAqC;AACnD,mBAAiB,OAAO,qBAAqB,EAAE,MAAM,CAAC,QAAiB;AACrE,mBAAe;AACf,UAAM;AAAA,EACR,CAAC;AACD,SAAO;AACT;AAEA,IAAI,OAAO,WAAW,YAAa,MAAK,iBAAiB,EAAE,MAAM,MAAM;AAAC,CAAC;AAsBzE,IAAM,yBAAyB;AAC/B,IAAM,0BAA0B;AAEhC,SAASC,UAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,2BAA2B,OAA8B;AAChE,MAAI,EAAE,iBAAiB,aAAc,QAAO;AAC5C,QAAM,SAAkB,MAAM;AAC9B,MAAI,CAACA,UAAS,MAAM,EAAG,QAAO;AAC9B,QAAM,QAAQ,OAAO;AACrB,MAAI,CAACA,UAAS,KAAK,EAAG,QAAO;AAC7B,SAAO,MAAM,YAAY,QAAQ,MAAM,UAAU;AACnD;AAEA,IAAM,uCAAuC;AAC7C,IAAM,2BAA2B;AACjC,IAAM,wBAAwB;AAEvB,SAAS,oCAAoC,oBAAsC;AACxF,SAAO;AACT;AAEO,SAAS,wBAAwB,OAAsB;AAC5D,MAAI,EAAE,iBAAiB,gBAAgB,CAACA,UAAS,MAAM,MAAM,EAAG,QAAO;AACvE,SAAO,OAAO,MAAM,OAAO,YAAY,YAAY,MAAM,OAAO,QAAQ,KAAK,IACzE,MAAM,OAAO,UACb;AACN;AAEA,SAAS,wBAAwB,QAAwC;AACvE,QAAM,OAAO,OAAO;AACpB,MAAI,CAAC,KAAM;AAEX,QAAM,YAAY,KAAK,cAA2B,gBAAgB;AAClE,QAAM,SAAS,KAAK,cAAiC,aAAa;AAElE,aAAW,MAAM,YAAY,kBAAkB,MAAM;AACrD,UAAQ,MAAM,YAAY,kBAAkB,MAAM;AACpD;AAEA,SAAS,sBAAsB,IAAqC;AAClE,QAAM,UAAU,GAAG,QAAQ,YAAY;AACvC,SAAO,YAAY,WAAW,YAAY;AAC5C;AAUO,SAAS,kBAAkB,QAA2B,YAA8B;AACzF,MAAI;AACF,UAAM,MAAM,OAAO;AACnB,UAAM,MAAM,OAAO;AACnB,QAAI,CAAC,OAAO,CAAC,IAAK,QAAO;AAEzB,eAAW,MAAM,IAAI,iBAAiB,cAAc,GAAG;AACrD,UACE,sBAAsB,EAAE,KACxB,CAAC,GAAG,SACJ,GAAG,iBAAiB,2BACpB,GAAG,aAAa,wBAChB;AACA,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,UAAU,IAAI;AACpB,QAAI,SAAS,QAAQ;AACnB,iBAAW,QAAQ,SAAS;AAC1B,YAAI,CAAC,wBAAwB,IAAI,EAAG,QAAO;AAAA,MAC7C;AAAA,IACF;AAEA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAUO,IAAM,SAAS;AAAA,EACpB,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GACA,QACG;AACH,UAAM,eAAe,OAAuB,IAAI;AAChD,UAAM,eAAe,OAAO,CAAC;AAC7B,UAAM,eAAe,OAA8C,IAAI;AACvE,UAAM,eAAe,OAA6C,IAAI;AACtE,UAAM,kBAAkB,OAA4B,IAAI;AACxD,UAAM,mBAAmB,OAAO,CAAC;AACjC,UAAM,YAAY,OAAO,CAAC;AAC1B,UAAM,gBAAgB,OAAO,CAAC;AAG9B,UAAM,YAAY,OAAO,MAAM;AAC/B,cAAU,UAAU;AACpB,UAAM,yBAAyB,OAAO,mBAAmB;AACzD,2BAAuB,UAAU;AACjC,UAAM,oBAAoB,OAAO,cAAc;AAC/C,sBAAkB,UAAU;AAC5B,UAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAC1C,UAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,KAAK;AACxD,UAAM,CAAC,qBAAqB,sBAAsB,IAAI,SAAS,KAAK;AACpE,UAAM,CAAC,oBAAoB,qBAAqB,IAAI,SAAS,KAAK;AAClE,UAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,KAAK;AACxD,UAAM,CAAC,yBAAyB,0BAA0B,IAAI,SAAS,KAAK;AAC5E,UAAM,CAAC,oBAAoB,qBAAqB,IAAI,SAAS,IAAI;AACjE,UAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAC5C,UAAM,CAAC,4BAA4B,6BAA6B,IAAI,SAAS,IAAI;AACjF,UAAM,CAAC,cAAc,eAAe,IAAI,SAAwB,IAAI;AAGpE,IAAAC,WAAU,MAAM;AACd,UAAI,CAAC,oBAAoB;AACvB,sCAA8B,IAAI;AAClC;AAAA,MACF;AACA,YAAM,QAAQ;AAAA,QACZ,MAAM,8BAA8B,KAAK;AAAA,QACzC;AAAA,MACF;AACA,aAAO,MAAM,aAAa,KAAK;AAAA,IACjC,GAAG,CAAC,kBAAkB,CAAC;AAEvB,mBAAe,MAAM;AACnB,YAAM,YAAY,aAAa;AAC/B,UAAI,CAAC,UAAW;AAEhB,YAAM,gBACJ,cAAc,YAAY,oBAAoB,WAAW,UAAU,IAAI;AACzE,UAAI,CAAC,cAAe;AAEpB,UAAI,WAAW;AACf,UAAI;AAEJ,WAAK,iBAAiB,EAAE,KAAK,MAAM;AACjC,YAAI,SAAU;AAGd,cAAM,SAAS,SAAS,cAAc,oBAAoB;AAC1D,cAAM,SAAS,IAAI,IAAI,eAAe,OAAO,SAAS,MAAM;AAC5D,mCAA2B,MAAM;AACjC,cAAM,MAAM,OAAO,WAAW,OAAO;AACrC,cAAM,eAAe,MAAM;AACzB,wBAAc,WAAW;AACzB,gBAAM,WAAW,IAAI,IAAI,KAAK,OAAO,SAAS,MAAM;AACpD,mBAAS,aAAa,IAAI,kBAAkB,OAAO,cAAc,OAAO,CAAC;AACzE,0BAAgB,IAAI;AACpB,gCAAsB,IAAI;AAC1B,2BAAiB,UAAU,YAAY,IAAI;AAC3C,oBAAU,WAAW;AACrB,iBAAO,aAAa,OAAO,SAAS,WAAW,SAAS,MAAM;AAAA,QAChE;AACA,wBAAgB,UAAU;AAC1B,cAAM,SAAS,OAAO;AACtB,cAAM,gBAAgB,CAAC,MAAa,EAAE,yBAAyB;AAC/D,cAAM,8BAA8B,CAAC,UAAiB;AACpD,gBAAM,UAAU,2BAA2B,KAAK;AAChD,cAAI,YAAY,KAAM,4BAA2B,OAAO;AAAA,QAC1D;AACA,cAAM,cAAc,MAAM;AACxB,0BAAgB,IAAI;AACpB,gCAAsB,KAAK;AAAA,QAC7B;AACA,cAAM,gBAAgB,MAAM;AAC1B,qBAAW,IAAI;AACf,sBAAY,EAAE,QAAQ,WAAW,iBAAiB,SAAS,QAAQ,UAAU,QAAQ,CAAC;AAAA,QACxF;AACA,cAAM,cAAc,CAAC,UAAiB;AACpC,gBAAM,UAAU,wBAAwB,KAAK;AAC7C,4BAAkB,UAAU,OAAO;AACnC,0BAAgB,OAAO;AACvB,gCAAsB,KAAK;AAAA,QAC7B;AACA,cAAM,aAAa,MAAM;AACvB,uBAAa;AACb,oBAAU,IAAI;AACd,0BAAgB,IAAI;AACpB,qCAA2B,KAAK;AAChC,gCAAsB,CAAC,OAAO,KAAK;AACnC,qBAAW,QAAQ,OAAO,OAAO,CAAC;AAElC,cAAI,aAAa,UAAU,GAAG;AAC5B,sBAAU,UAAU,OAAO,mBAAmB;AAC9C,iBAAK,UAAU;AACf,sBAAU,UAAU,IAAI,mBAAmB;AAC3C,kBAAM,QAAQ,MAAM,UAAU,UAAU,OAAO,mBAAmB;AAClE,sBAAU,iBAAiB,gBAAgB,OAAO,EAAE,MAAM,KAAK,CAAC;AAAA,UAClE;AACA,oBAAU,QAAQ;AAkBlB,cAAI,aAAa,QAAS,eAAc,aAAa,OAAO;AAC5D,gBAAM,mBAAmB,aAAa,UAAU;AAChD,cAAI,eAAe,mBAAmB,QAAQ,kBAAkB,QAAQ,KAAK;AAC7E,cAAI,cAAc;AAChB,6BAAiB,IAAI;AACrB,gBAAI,WAAW;AACf,yBAAa,UAAU,YAAY,MAAM;AACvC,0BAAY;AACZ,6BAAe,kBAAkB,QAAQ,YAAY;AACrD,kBAAI,CAAC,gBAAgB,WAAW,KAAK;AACnC,oBAAI,gBAAgB,WAAW,KAAK;AAClC,0BAAQ;AAAA,oBACN;AAAA,kBACF;AAAA,gBACF;AACA,oBAAI,aAAa,QAAS,eAAc,aAAa,OAAO;AAC5D,6BAAa,UAAU;AACvB,iCAAiB,KAAK;AAAA,cACxB;AAAA,YACF,GAAG,GAAG;AAAA,UACR,OAAO;AACL,6BAAiB,KAAK;AAAA,UACxB;AAAA,QACF;AAKA,eAAO,iBAAiB,QAAQ,UAAU;AAC1C,eAAO,iBAAiB,SAAS,eAAe,EAAE,SAAS,KAAK,CAAC;AACjE,eAAO,iBAAiB,yBAAyB,2BAA2B;AAC5E,eAAO,iBAAiB,SAAS,WAAW;AAC5C,eAAO,iBAAiB,WAAW,aAAa;AAChD,eAAO,iBAAiB,SAAS,WAAW;AAG5C,YAAI,OAAO,QAAQ,YAAY;AAC7B,cAAI,MAAM;AAAA,QACZ,WAAW,KAAK;AACd,UAAC,IAAyD,UAAU;AAAA,QACtE;AAEA,eAAO,aAAa,wBAAwB,GAAG;AAC/C,eAAO,aAAa,kBAAkB,QAAQ;AAC9C,eAAO,aAAa,kBAAkB,EAAE;AACxC,eAAO,aAAa,SAAS,OAAO,WAAW,OAAO,IAAI,CAAC;AAC3D,eAAO,aAAa,UAAU,OAAO,WAAW,OAAO,IAAI,CAAC;AAC5D,eAAO,MAAM,QAAQ;AACrB,eAAO,MAAM,SAAS;AACtB,eAAO,MAAM,UAAU;AACvB,eAAO,MAAM,aAAa;AAC1B,yBAAiB,UAAU,YAAY,IAAI;AAC3C,kBAAU,WAAW;AACrB,eAAO,aAAa,OAAO,GAAG;AAC9B,kBAAU,YAAY,MAAM;AAM5B,YAAI,OAAO,YAAY;AACrB,gBAAM,kBAAkB,SAAS,cAAc,OAAO;AACtD,0BAAgB,cACd;AAEF,iBAAO,WAAW,YAAY,eAAe;AAAA,QAC/C;AAEA,gCAAwB,MAAM;AAE9B,kBAAU,MAAM;AACd,iBAAO,oBAAoB,QAAQ,UAAU;AAC7C,iBAAO,oBAAoB,SAAS,eAAe,EAAE,SAAS,KAAK,CAAC;AACpE,iBAAO,oBAAoB,yBAAyB,2BAA2B;AAC/E,iBAAO,oBAAoB,SAAS,WAAW;AAC/C,iBAAO,oBAAoB,WAAW,aAAa;AACnD,iBAAO,oBAAoB,SAAS,WAAW;AAC/C,cAAI,aAAa,QAAS,eAAc,aAAa,OAAO;AAC5D,uBAAa,UAAU;AAOvB,iBAAO,OAAO;AACd,cAAI,gBAAgB,YAAY,aAAc,iBAAgB,UAAU;AAQxE,cAAI,OAAO,QAAQ,YAAY;AAAA,UAE/B,WAAW,KAAK;AACd,kBAAM,aAAa;AACnB,gBAAI,WAAW,YAAY,QAAQ;AACjC,yBAAW,UAAU;AAAA,YACvB;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAED,aAAO,MAAM;AACX,mBAAW;AACX,kBAAU;AAAA,MACZ;AAAA,IACF,CAAC;AAID,IAAAA,WAAU,MAAM;AACd,UAAI,CAAC,eAAe;AAClB,yBAAiB,KAAK;AACtB;AAAA,MACF;AACA,YAAM,QAAQ,WAAW,MAAM,iBAAiB,IAAI,GAAG,GAAI;AAC3D,aAAO,MAAM,aAAa,KAAK;AAAA,IACjC,GAAG,CAAC,aAAa,CAAC;AAElB,IAAAA,WAAU,MAAM;AACd,UAAI,aAAa,SAAS;AACxB,qBAAa,aAAa,OAAO;AACjC,qBAAa,UAAU;AAAA,MACzB;AAEA,UAAI,eAAe;AACjB,+BAAuB,IAAI;AAC3B,8BAAsB,KAAK;AAC3B;AAAA,MACF;AAEA,4BAAsB,IAAI;AAC1B,mBAAa,UAAU,WAAW,MAAM;AACtC,+BAAuB,KAAK;AAC5B,8BAAsB,KAAK;AAC3B,qBAAa,UAAU;AAAA,MACzB,GAAG,GAAG;AAEN,aAAO,MAAM;AACX,YAAI,aAAa,SAAS;AACxB,uBAAa,aAAa,OAAO;AACjC,uBAAa,UAAU;AAAA,QACzB;AAAA,MACF;AAAA,IACF,GAAG,CAAC,aAAa,CAAC;AAElB,UAAM,uBAAuB,MAAM;AACjC,UAAI,aAAa,SAAS;AACxB,sBAAc,aAAa,OAAO;AAClC,qBAAa,UAAU;AAAA,MACzB;AACA,uBAAiB,KAAK;AAAA,IACxB;AAEA,UAAM,kBACJ,UAAU,WAAW,CAAC,sBAAsB,CAAC,2BAA2B,CAAC;AAC3E,UAAM,cAAc,mBAAmB,CAAC;AAExC,IAAAA,WAAU,MAAM;AACd,UAAI,gBAAiB,gBAAe,SAAS,EAAE,kBAAkB;AAAA,IACnE,GAAG,CAAC,eAAe,CAAC;AAEpB,IAAAA,WAAU,MAAM;AACd,UAAI,CAAC,aAAa;AAChB,+BAAuB,UAAU,KAAK;AACtC;AAAA,MACF;AACA,UAAI,SAAS;AACb,YAAM,QAAQ,sBAAsB,MAAM;AACxC,iBAAS,sBAAsB,MAAM,uBAAuB,UAAU,IAAI,CAAC;AAAA,MAC7E,CAAC;AACD,aAAO,MAAM;AACX,6BAAqB,KAAK;AAC1B,6BAAqB,MAAM;AAAA,MAC7B;AAAA,IACF,GAAG,CAAC,WAAW,CAAC;AAEhB,IAAAA,WAAU,MAAM;AACd,UAAI,aAAc,gBAAe,SAAS,EAAE,kBAAkB;AAAA,IAChE,GAAG,CAAC,YAAY,CAAC;AAEjB,IAAAA,WAAU,MAAM;AACd,YAAM,QAAQ;AAAA,QACZ,MAAM,eAAe,SAAS,EAAE,kBAAkB;AAAA,QAClD;AAAA,MACF;AACA,aAAO,MAAM,aAAa,KAAK;AAAA,IACjC,GAAG,CAAC,SAAS,CAAC;AAEd,UAAM,yBACJ,CAAC,0BACD,CAAC,8BACD,oCAAoC,kBAAkB;AACxD,UAAM,mBACJ,uBAAuB,CAAC,2BAA2B,CAAC,0BAA0B,CAAC;AAEjF,IAAAA,WAAU,MAAM;AACd,mCAA6B,0BAA0B,gBAAgB;AAAA,IACzE,GAAG,CAAC,4BAA4B,wBAAwB,gBAAgB,CAAC;AAEzE,WACE;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV;AAAA,QAEA;AAAA,8BAAC,SAAI,KAAK,cAAc,WAAU,iBAAgB;AAAA,UACjD,0BACC;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,2BAAwB;AAAA,cACxB,eAAY;AAAA,cACZ,WAAW;AAAA,cACX,aAAa,CAAC,UAAU,MAAM,eAAe;AAAA,cAC7C,aAAa,CAAC,UAAU,MAAM,eAAe;AAAA,cAC7C,eAAe,CAAC,UAAU,MAAM,eAAe;AAAA,cAE/C;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAM;AAAA,kBACN,QAAO;AAAA,kBACP,MAAM;AAAA;AAAA,cACR;AAAA;AAAA,UACF;AAAA,UAED,oBACC;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,2BAAwB;AAAA,cACxB,WAAW;AAAA,cACX,OAAO;AAAA,gBACL,SAAS,qBAAqB,IAAI;AAAA,gBAClC,eAAe,qBAAqB,SAAS;AAAA,gBAC7C,YAAY;AAAA,cACd;AAAA,cACA,aAAa,CAAC,UAAU,MAAM,eAAe;AAAA,cAC7C,aAAa,CAAC,UAAU,MAAM,eAAe;AAAA,cAE7C,+BAAC,SAAI,WAAU,oCACb;AAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,OAAM;AAAA,oBACN,QAAO;AAAA,oBACP,MAAM;AAAA;AAAA,gBACR;AAAA,gBACC,iBACC;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS;AAAA,oBACT,WAAU;AAAA,oBACX;AAAA;AAAA,gBAED;AAAA,iBAEJ;AAAA;AAAA,UACF;AAAA,UAED,gBACC;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,2BAAwB;AAAA,cACxB,eAAY;AAAA,cAEZ,+BAAC,SAAI,WAAU,YACb;AAAA,oCAAC,OAAE,WAAU,oCAAmC,oCAAsB;AAAA,gBACtE,oBAAC,OAAE,WAAU,iCAAiC,wBAAa;AAAA,gBAC3D;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,WAAU;AAAA,oBACV,SAAS,MAAM,gBAAgB,UAAU;AAAA,oBAC1C;AAAA;AAAA,gBAED;AAAA,iBACF;AAAA;AAAA,UACF;AAAA;AAAA;AAAA,IAEJ;AAAA,EAEJ;AACF;AAEA,OAAO,cAAc;","names":["isRecord","useEffect","create","isRecord","useEffect"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/editor/manualEditingAvailability.ts","../src/utils/sdkCutoverPolicy.ts","../src/utils/studioApiFetch.ts","../src/components/editor/probeSourceElement.ts","../src/components/editor/domEditingLayers.ts","../src/components/editor/domEditingGroups.ts","../src/components/editor/domEditingElement.ts","../src/components/editor/domEditingRootLayer.ts","../src/components/editor/domEditLayerWalkCache.ts"],"sourcesContent":["export type StudioFeatureFlagEnv = Record<string, boolean | string | undefined>;\n\nconst TRUTHY_ENV_VALUES = new Set([\"1\", \"true\", \"yes\", \"on\", \"enabled\"]);\nconst FALSY_ENV_VALUES = new Set([\"0\", \"false\", \"no\", \"off\", \"disabled\"]);\n\nexport function resolveStudioBooleanEnvFlag(\n env: StudioFeatureFlagEnv,\n names: string[],\n fallback: boolean,\n): boolean {\n for (const name of names) {\n const value = env[name];\n if (typeof value === \"boolean\") return value;\n if (typeof value !== \"string\") continue;\n\n const normalized = value.trim().toLowerCase();\n if (!normalized) continue;\n if (TRUTHY_ENV_VALUES.has(normalized)) return true;\n if (FALSY_ENV_VALUES.has(normalized)) return false;\n }\n\n return fallback;\n}\n\n// `import.meta.env` is a Vite-only extension. In non-Vite ESM hosts\n// (Next.js / Turbopack, Node, jest in some configs) it's undefined,\n// and downstream `env[name]` reads would crash. Fall back to `{}` so\n// every flag resolves to its declared default outside Vite. Direct\n// property access keeps Vite's compile-time transform happy.\n//\n// When the studio is served as a pre-built SPA by the embedded Hono server,\n// `import.meta.env` values were baked at build time. The server injects\n// `window.__HF_STUDIO_ENV__` with any `VITE_STUDIO_*` env vars from the\n// user's shell, so runtime overrides take precedence over baked defaults.\nconst runtimeEnv =\n typeof window !== \"undefined\"\n ? ((window as Window & { __HF_STUDIO_ENV__?: StudioFeatureFlagEnv }).__HF_STUDIO_ENV__ ?? {})\n : {};\nconst env = { ...(import.meta.env ?? {}), ...runtimeEnv } as StudioFeatureFlagEnv;\n\n// Stage 7 SDK cutover — routes Studio edits through SDK dispatch instead of the\n// server patch/mutation routes. Default TRUE as of the flip release (spec:\n// 2026-09-17-studio-sdk-cutover-flip-design.md). Kill switch:\n// VITE_STUDIO_SDK_CUTOVER_ENABLED=false (works at runtime via __HF_STUDIO_ENV__).\nexport const STUDIO_SDK_CUTOVER_ENABLED = resolveStudioBooleanEnvFlag(\n env,\n [\"VITE_STUDIO_SDK_CUTOVER_ENABLED\"],\n true,\n);\n\n/**\n * Per-family selection. Unset = every family (the master alone enables all).\n * Set `VITE_STUDIO_SDK_CUTOVER_FAMILIES=dom,timing` to RESTRICT to a subset.\n */\nexport const STUDIO_SDK_CUTOVER_FAMILIES = resolveEnabledSdkFamilies(\n env,\n STUDIO_SDK_CUTOVER_ENABLED,\n);\n\n// Resolver-parity tripwire (telemetry-only, decoupled from cutover).\n// Runs the SDK resolver alongside any edit and emits sdk_resolver_shadow on\n// divergence. Default true; disable via VITE_STUDIO_SDK_RESOLVER_SHADOW_ENABLED=false.\n// Soak gate: retire once zero element_not_found divergences over a clean window.\nexport const STUDIO_SDK_RESOLVER_SHADOW_ENABLED = resolveStudioBooleanEnvFlag(\n env,\n [\"VITE_STUDIO_SDK_RESOLVER_SHADOW_ENABLED\"],\n true,\n);\n\n// Studio inspector redesign (\"Ledger, flat\" — design_handoff_studio_inspector):\n// flat identity header/footer/groups. Default true as of v0.7.59+ bug-fix pass\n// (right-aligned values, Stroke select-only, promote-badge overlap, Layout/\n// Style section gating); disable via VITE_STUDIO_FLAT_INSPECTOR_ENABLED=false\n// to fall back to the legacy panel.\nexport const STUDIO_FLAT_INSPECTOR_ENABLED = resolveStudioBooleanEnvFlag(\n env,\n [\"VITE_STUDIO_ENABLE_FLAT_INSPECTOR\", \"VITE_STUDIO_FLAT_INSPECTOR_ENABLED\"],\n true,\n);\n\n// A host whose mounted API authenticates with same-origin cookies sets this; studioApiFetch then sends them, and its\n// requests share the preview media's sockets again.\nexport const STUDIO_API_SAME_ORIGIN_CREDENTIALS = resolveStudioBooleanEnvFlag(\n env,\n [\"VITE_STUDIO_API_SAME_ORIGIN_CREDENTIALS\"],\n false,\n);\n\nimport { resolveEnabledSdkFamilies } from \"../../utils/sdkCutoverPolicy\";\n","export const STUDIO_SDK_OPERATION_FAMILIES = [\n \"dom\",\n \"timing\",\n \"gsap-animation\",\n \"gsap-keyframe\",\n \"lifecycle\",\n] as const;\n\nexport type StudioSdkOperationFamily = (typeof STUDIO_SDK_OPERATION_FAMILIES)[number];\n\ninterface StudioSdkFamilyStatus {\n owner: string;\n deadline: string;\n parityEvidence: string;\n graduated: boolean;\n}\n\nconst STUDIO_SDK_FAMILY_STATUS = {\n dom: {\n owner: \"studio-foundations\",\n deadline: \"2026-09-30\",\n parityEvidence: \"sdkCutoverParity.test.ts\",\n graduated: false,\n },\n timing: {\n owner: \"studio-foundations\",\n deadline: \"2026-10-15\",\n parityEvidence: \"sdkCutover.test.ts timing corpus\",\n graduated: false,\n },\n \"gsap-animation\": {\n owner: \"studio-foundations\",\n deadline: \"2026-10-31\",\n parityEvidence: \"sdkCutover.test.ts GSAP animation corpus\",\n graduated: false,\n },\n \"gsap-keyframe\": {\n owner: \"studio-foundations\",\n deadline: \"2026-11-15\",\n parityEvidence: \"sdkCutover.test.ts GSAP keyframe corpus\",\n graduated: false,\n },\n lifecycle: {\n owner: \"studio-foundations\",\n deadline: \"2026-11-30\",\n parityEvidence: \"sdkCutover.test.ts lifecycle corpus\",\n graduated: false,\n },\n} as const satisfies Record<StudioSdkOperationFamily, StudioSdkFamilyStatus>;\n\nexport function resolveEnabledSdkFamilies(\n env: Record<string, boolean | string | undefined>,\n masterEnabled: boolean,\n): ReadonlySet<StudioSdkOperationFamily> {\n if (!masterEnabled) return new Set();\n const raw = env[\"VITE_STUDIO_SDK_CUTOVER_FAMILIES\"];\n // Unset/blank = every family. The list exists to RESTRICT (kill switch per family).\n if (typeof raw !== \"string\" || raw.trim() === \"\") return new Set(STUDIO_SDK_OPERATION_FAMILIES);\n const requested = raw\n .split(\",\")\n .map((family) => family.trim())\n .filter(Boolean);\n const known = new Set<string>(STUDIO_SDK_OPERATION_FAMILIES);\n const unknown = requested.filter((family) => !known.has(family));\n if (unknown.length > 0) {\n throw new Error(`Unknown Studio SDK cutover families: ${unknown.join(\", \")}`);\n }\n return new Set(requested as StudioSdkOperationFamily[]);\n}\n\nexport function isSdkFamilyEnabled(\n masterEnabled: boolean,\n configured: ReadonlySet<StudioSdkOperationFamily> | undefined,\n family: StudioSdkOperationFamily,\n): boolean {\n // `undefined` preserves compatibility for isolated tests/embedders that mock\n // only the historical master flag. Production always supplies an explicit set.\n return masterEnabled && (configured?.has(family) ?? true);\n}\n\nexport function renderStudioSdkCutoverReport(): string {\n const rows = STUDIO_SDK_OPERATION_FAMILIES.map((family) => {\n const status = STUDIO_SDK_FAMILY_STATUS[family];\n return `| ${family} | ${status.owner} | ${status.deadline} | ${status.parityEvidence} | ${status.graduated ? \"yes\" : \"no\"} |`;\n });\n return [\n \"# Studio SDK cutover report\",\n \"\",\n \"Default: SDK persist ON for every family. Kill switches:\",\n \" `VITE_STUDIO_SDK_CUTOVER_ENABLED=false` (all families → server path)\",\n \" `VITE_STUDIO_SDK_CUTOVER_FAMILIES=dom,timing` (only listed families → SDK; rest → server)\",\n \"\",\n \"A family graduates only after zero unexplained resolver/serialization divergences over its agreed corpus and soak window.\",\n \"\",\n \"| Family | Owner | Deadline | Parity evidence | Graduated |\",\n \"| --- | --- | --- | --- | --- |\",\n ...rows,\n \"\",\n ].join(\"\\n\");\n}\n","import { STUDIO_API_SAME_ORIGIN_CREDENTIALS } from \"../components/editor/manualEditingAvailability\";\n\nconst LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"[::1]\"]);\n\nfunction studioApiCredentials(): RequestCredentials {\n if (STUDIO_API_SAME_ORIGIN_CREDENTIALS) return \"same-origin\";\n const host = globalThis.location?.hostname;\n return !host || LOOPBACK_HOSTS.has(host) ? \"omit\" : \"same-origin\";\n}\n\nexport function studioApiFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n return globalThis.fetch(input, { ...init, credentials: studioApiCredentials() });\n}\n","import { buildProjectApiPath } from \"../../utils/projectRouting\";\nimport { studioApiFetch } from \"../../utils/studioApiFetch\";\n\ntype ProbeTarget = { id?: string; hfId?: string; selector?: string; selectorIndex?: number };\n\ninterface PendingProbe {\n target: ProbeTarget;\n resolve: (exists: boolean) => void;\n}\n\nconst pending = new Map<string, PendingProbe[]>();\n\nasync function sendBatch(projectId: string, sourceFile: string, probes: PendingProbe[]) {\n let exists: boolean[] = [];\n try {\n const response = await studioApiFetch(\n buildProjectApiPath(\n projectId,\n `/file-mutations/probe-elements/${encodeURIComponent(sourceFile)}`,\n ),\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ targets: probes.map((probe) => probe.target) }),\n },\n );\n if (response.ok) {\n const data = await response.json();\n if (Array.isArray(data?.exists)) exists = data.exists;\n }\n } catch {}\n probes.forEach((probe, index) => probe.resolve(exists[index] !== false));\n}\n\nexport function probeSourceElement(\n projectId: string,\n sourceFile: string,\n target: ProbeTarget,\n): Promise<boolean> {\n return new Promise((resolve) => {\n const key = `${projectId}\\0${sourceFile}`;\n let probes = pending.get(key);\n if (!probes) {\n const batch: PendingProbe[] = [];\n probes = batch;\n pending.set(key, batch);\n setTimeout(() => {\n pending.delete(key);\n void sendBatch(projectId, sourceFile, batch);\n }, 0);\n }\n probes.push({ target, resolve });\n });\n}\n","import { probeSourceElement } from \"./probeSourceElement\";\nimport { isAudibleVideoNode } from \"../../player/lib/timelineElementHelpers\";\nimport type { PatchOperation } from \"../../utils/sourcePatcher\";\nimport {\n resolveEditingAffordances,\n resolveEditingSections,\n type EditableElementFacts,\n} from \"@hyperframes/core/editing\";\nimport { groupScopedLayerRoots, resolveGroupCapture } from \"./domEditingGroups\";\nimport type {\n DomEditCapabilities,\n DomEditContextOptions,\n DomEditLayerItem,\n DomEditSelection,\n DomEditTextField,\n} from \"./domEditingTypes\";\nimport {\n buildElementLabel,\n buildStableSelector,\n findClosestByAttribute,\n getCuratedComputedStyles,\n getDataAttributes,\n getInlineStyles,\n getSelectorIndex,\n getSourceFileForElement,\n isEditableTextLeaf,\n isHtmlElement,\n} from \"./domEditingDom\";\nimport {\n findElementForSelection,\n getSelectionCandidate,\n isDomLayerElement,\n} from \"./domEditingElement\";\nimport { isCompositionRootLayer } from \"./domEditingRootLayer\";\nimport { withSelectorIndexPass } from \"../../utils/sourceScopedSelectorIndex\";\nimport { type DomEditLayerWalkCache, readDomEditLayerWalkEntry } from \"./domEditLayerWalkCache\";\n\nfunction sameTagChildIndex(el: HTMLElement): number {\n let index = 0;\n let sibling = el.previousElementSibling;\n while (sibling) {\n if (sibling.tagName === el.tagName) index += 1;\n sibling = sibling.previousElementSibling;\n }\n return index;\n}\n\nfunction getTextFieldLabel(\n _tagName: string,\n index: number,\n total: number,\n source: \"self\" | \"child\",\n): string {\n if (source === \"self\" || total === 1) return \"Content\";\n return `Text ${index + 1}`;\n}\n\nfunction buildTextField(\n el: HTMLElement,\n index: number,\n total: number,\n source: \"self\" | \"child\",\n sourceChildIndex?: number,\n): DomEditTextField {\n const tagName = el.tagName.toLowerCase();\n const key = el.getAttribute(\"data-hf-text-key\") ?? `${source}:${index}:${tagName}`;\n return {\n key,\n label: getTextFieldLabel(tagName, index, total, source),\n value: el.textContent ?? \"\",\n tagName,\n attributes: Array.from(el.attributes)\n .filter((attribute) => attribute.name !== \"style\")\n .map((attribute) => ({\n name: attribute.name,\n value: attribute.value,\n })),\n inlineStyles: getInlineStyles(el),\n computedStyles: getCuratedComputedStyles(el),\n source,\n ...(sourceChildIndex == null ? {} : { sourceChildIndex }),\n };\n}\n\n// fallow-ignore-next-line complexity\nexport function collectDomEditTextFields(el: HTMLElement): DomEditTextField[] {\n const childElements = Array.from(el.children).filter(isHtmlElement).filter(isEditableTextLeaf);\n\n if (childElements.length > 0) {\n const hasMixedContent = Array.from(el.childNodes).some(\n (node) => node.nodeType === 3 && node.textContent?.trim(),\n );\n\n if (hasMixedContent) {\n const fields: DomEditTextField[] = [];\n let childIdx = 0;\n for (const node of el.childNodes) {\n if (node.nodeType === 3) {\n const text = node.textContent ?? \"\";\n if (!text.trim()) continue;\n fields.push({\n key: `text-node:${childIdx}`,\n label: `Text ${childIdx + 1}`,\n value: text,\n tagName: \"#text\",\n attributes: [],\n inlineStyles: {},\n computedStyles: {},\n source: \"text-node\",\n });\n childIdx++;\n } else if (isHtmlElement(node) && isEditableTextLeaf(node)) {\n fields.push(\n buildTextField(node, childIdx, childElements.length, \"child\", sameTagChildIndex(node)),\n );\n childIdx++;\n }\n }\n return fields;\n }\n\n return childElements.map((child, index) =>\n buildTextField(child, index, childElements.length, \"child\", sameTagChildIndex(child)),\n );\n }\n\n if (isEditableTextLeaf(el)) {\n return [buildTextField(el, 0, 1, \"self\")];\n }\n\n return [];\n}\n\nfunction escapeHtmlText(value: string): string {\n return value.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nfunction serializeTextFieldStyle(field: DomEditTextField): string {\n const entries = Object.entries(field.inlineStyles).filter(([, value]) => Boolean(value));\n if (entries.length === 0) return \"\";\n return entries.map(([key, value]) => `${key}: ${value}`).join(\"; \");\n}\n\nexport function serializeDomEditTextFields(fields: DomEditTextField[]): string {\n return fields\n .filter((field) => field.source === \"child\" || field.source === \"text-node\")\n .map((field) => {\n if (field.source === \"text-node\") {\n return escapeHtmlText(field.value);\n }\n const attrs = [\n ...field.attributes.filter((attribute) => attribute.name !== \"data-hf-text-key\"),\n { name: \"data-hf-text-key\", value: field.key },\n ]\n .map((attribute) => ` ${attribute.name}=\"${attribute.value.replace(/\"/g, \""\")}\"`)\n .join(\"\");\n const style = serializeTextFieldStyle(field);\n const styleAttr = style ? ` style=\"${style.replace(/\"/g, \""\")}\"` : \"\";\n return `<${field.tagName}${attrs}${styleAttr}>${escapeHtmlText(field.value)}</${field.tagName}>`;\n })\n .join(\"\");\n}\n\nexport function buildDefaultDomEditTextField(base?: Partial<DomEditTextField>): DomEditTextField {\n return {\n key: `child:new:${Date.now()}`,\n label: \"Text\",\n value: \"New text\",\n tagName: \"span\",\n attributes: [],\n inlineStyles: {\n \"font-family\": base?.computedStyles?.[\"font-family\"] ?? \"inherit\",\n \"font-size\": base?.computedStyles?.[\"font-size\"] ?? \"16px\",\n \"font-weight\": base?.computedStyles?.[\"font-weight\"] ?? \"400\",\n color: base?.computedStyles?.color ?? \"inherit\",\n },\n computedStyles: {},\n source: \"child\",\n };\n}\n\nexport interface DomEditChildLocator {\n childSelector: string;\n childIndex: number;\n}\n\nexport function buildTextFieldChildLocator(\n fields: DomEditTextField[],\n fieldKey: string,\n): DomEditChildLocator | null {\n const field = fields.find((candidate) => candidate.key === fieldKey);\n if (!field || field.source !== \"child\") return null;\n // sourceChildIndex is only absent for a synthetic field that was never read\n // back from the live DOM (e.g. one built by buildDefaultDomEditTextField).\n // Guessing its position by counting same-tag \"child\" fields elsewhere in\n // the array is unreliable and can silently locate the wrong element — fail\n // closed instead so the caller falls back to the unsupported-structure path.\n if (field.sourceChildIndex == null) return null;\n\n return {\n childSelector: `:scope > ${field.tagName}`,\n childIndex: field.sourceChildIndex,\n };\n}\n\nfunction capabilityFacts(geometry: {\n hasStableTarget: boolean;\n tag: string;\n inlineStyles: Record<string, string>;\n computedStyles: Record<string, string>;\n isCompositionHost: boolean;\n isCompositionRoot: boolean;\n isInsideLockedComposition: boolean;\n isMasterView: boolean;\n existsInSource: boolean;\n}): EditableElementFacts {\n return {\n ...geometry,\n hasEditableText: false,\n hasTimingStart: false,\n animationCount: 0,\n };\n}\n\n/**\n * Build core EditableElementFacts from a fully-resolved DomEditSelection.\n * `animationCount` is supplied by the caller because live GSAP tweens arrive on\n * a separate channel (the PropertyPanel `gsapAnimations` prop), not on the\n * selection — `selection.gsapAnimations` is never populated.\n */\nexport function domEditSelectionToFacts(\n selection: DomEditSelection,\n animationCount = selection.gsapAnimations?.length ?? 0,\n): EditableElementFacts {\n return {\n hasStableTarget: Boolean(selection.selector || selection.hfId),\n tag: selection.tagName,\n inlineStyles: selection.inlineStyles,\n computedStyles: selection.computedStyles,\n isCompositionHost: selection.isCompositionHost,\n isCompositionRoot: false,\n isInsideLockedComposition: selection.isInsideLockedComposition,\n isMasterView: false,\n existsInSource: true,\n hasEditableText: selection.textFields.length > 0,\n hasTimingStart: selection.dataAttributes.start != null,\n animationCount,\n hasAudio: isAudibleVideoNode(selection.element),\n };\n}\n\n/**\n * Resolve DOM edit capabilities for a given element.\n * Thin wrapper over core resolveEditingAffordances — kept for backward\n * compatibility (tests and the barrel import this signature directly).\n */\nexport function resolveDomEditCapabilities(args: {\n selector?: string;\n hfId?: string;\n tagName?: string;\n inlineStyles: Record<string, string>;\n computedStyles: Record<string, string>;\n isCompositionHost: boolean;\n isCompositionRoot?: boolean;\n isInsideLockedComposition: boolean;\n isMasterView: boolean;\n existsInSource?: boolean;\n}): DomEditCapabilities {\n return resolveEditingAffordances(\n capabilityFacts({\n hasStableTarget: Boolean(args.selector || args.hfId),\n tag: (args.tagName ?? \"div\").toLowerCase(),\n inlineStyles: args.inlineStyles,\n computedStyles: args.computedStyles,\n isCompositionHost: args.isCompositionHost,\n isCompositionRoot: args.isCompositionRoot ?? false,\n isInsideLockedComposition: args.isInsideLockedComposition ?? false,\n isMasterView: args.isMasterView,\n existsInSource: args.existsInSource ?? true,\n }),\n ).capabilities;\n}\n\n// fallow-ignore-next-line complexity\nexport async function resolveDomEditSelection(\n startEl: HTMLElement | null,\n options: DomEditContextOptions & {\n projectId?: string | null;\n skipSourceProbe?: boolean;\n exactTarget?: boolean;\n },\n): Promise<DomEditSelection | null> {\n if (!startEl) return null;\n const doc = startEl.ownerDocument;\n\n let capture = options.exactTarget\n ? ({ kind: \"unit\", element: startEl } as const)\n : resolveGroupCapture(startEl, options.activeGroupElement ?? null);\n if (capture.kind === \"out-of-scope\") {\n // Drill-in is non-sticky: clicking/hovering OUTSIDE the drilled-into group\n // exits it and resolves the target normally, rather than selecting nothing\n // (which felt like \"can't select anything\" once you'd drilled in).\n capture = resolveGroupCapture(startEl, null);\n }\n let current: HTMLElement | null =\n capture.kind === \"unit\" ? capture.element : getSelectionCandidate(startEl, options);\n while (current && current !== doc.body && current !== doc.documentElement) {\n const selector = buildStableSelector(current);\n const hfId = readHfId(current);\n if (!selector && !hfId) {\n current = current.parentElement;\n continue;\n }\n\n const { sourceFile, compositionPath } = getSourceFileForElement(\n current,\n options.activeCompositionPath,\n );\n const selectorIndex = selector\n ? getSelectorIndex(doc, current, selector, sourceFile, options.activeCompositionPath)\n : undefined;\n const compositionSrc =\n current.getAttribute(\"data-composition-src\") ??\n current.getAttribute(\"data-composition-file\") ??\n undefined;\n const inlineStyles = getInlineStyles(current);\n const computedStyles = getCuratedComputedStyles(current);\n const isCompositionRoot =\n (current.hasAttribute(\"data-composition-id\") && !compositionSrc) ||\n isCompositionRootLayer(current, doc, computedStyles);\n const textFields = collectDomEditTextFields(current);\n const isInsideLocked = Boolean(findClosestByAttribute(current, [\"data-timeline-locked\"]));\n let existsInSource: boolean | undefined;\n if (!options.skipSourceProbe && options.projectId && (current.id || selector || hfId)) {\n const probeTarget: { id?: string; hfId?: string; selector?: string; selectorIndex?: number } =\n {};\n if (current.id) probeTarget.id = current.id;\n if (hfId) probeTarget.hfId = hfId;\n if (selector) probeTarget.selector = selector;\n if (selectorIndex != null) probeTarget.selectorIndex = selectorIndex;\n existsInSource = await probeSourceElement(options.projectId, sourceFile, probeTarget);\n }\n const capabilities = resolveEditingAffordances(\n capabilityFacts({\n hasStableTarget: Boolean(selector || hfId),\n tag: current.tagName.toLowerCase(),\n inlineStyles,\n computedStyles,\n isCompositionHost: Boolean(compositionSrc),\n isCompositionRoot,\n isInsideLockedComposition: isInsideLocked,\n isMasterView: options.isMasterView,\n existsInSource: existsInSource ?? true,\n }),\n ).capabilities;\n const rect = current.getBoundingClientRect();\n\n return {\n element: current,\n id: current.id || undefined,\n hfId,\n selector,\n selectorIndex,\n sourceFile,\n compositionPath,\n compositionSrc,\n isCompositionHost: Boolean(compositionSrc),\n isInsideLockedComposition: isInsideLocked,\n label: buildElementLabel(current),\n tagName: current.tagName.toLowerCase(),\n boundingBox: {\n x: rect.left,\n y: rect.top,\n width: rect.width,\n height: rect.height,\n },\n textContent: current.textContent?.trim() || null,\n dataAttributes: getDataAttributes(current),\n inlineStyles,\n computedStyles,\n textFields,\n capabilities,\n };\n }\n\n return null;\n}\n\nexport async function refreshDomEditSelection(\n selection: DomEditSelection,\n activeCompositionPath: string | null,\n): Promise<DomEditSelection | null> {\n const doc = selection.element.ownerDocument;\n const nextElement = findElementForSelection(doc, selection, activeCompositionPath);\n return nextElement\n ? resolveDomEditSelection(nextElement, {\n activeCompositionPath,\n isMasterView: !activeCompositionPath || activeCompositionPath === \"index.html\",\n })\n : null;\n}\n\n// ─── Layer items ─────────────────────────────────────────────────────────────\n\nexport function getDomEditLayerKey(\n target: Pick<DomEditSelection, \"id\" | \"selector\" | \"selectorIndex\" | \"sourceFile\">,\n): string {\n const selectorIndex = target.selectorIndex ?? 0;\n return `${target.sourceFile}:${target.id ?? target.selector ?? \"layer\"}:${selectorIndex}`;\n}\n\nexport function countDomEditChildLayers(\n root: HTMLElement | null | undefined,\n options: DomEditContextOptions,\n maxCount = 99,\n): number {\n if (!root) return 0;\n\n let count = 0;\n const visit = (el: HTMLElement) => {\n for (const child of Array.from(el.children)) {\n if (!isHtmlElement(child)) continue;\n if (isDomLayerElement(child)) {\n count += 1;\n if (count >= maxCount) return;\n }\n visit(child);\n if (count >= maxCount) return;\n }\n };\n\n visit(root);\n return count;\n}\n\n// Every editable element under `root`, in document order. `maxItems` is a\n// caller's rendering budget, not a property of the document: hit-testing\n// callers (marquee, off-canvas indicators) must see all of it, and sharing a\n// truncated list left everything past the cut unselectable however far you drag.\nexport function collectDomEditLayerItems(\n root: HTMLElement | null | undefined,\n options: DomEditContextOptions,\n maxItems = Number.POSITIVE_INFINITY,\n cache?: DomEditLayerWalkCache,\n): DomEditLayerItem[] {\n if (!root) return [];\n cache?.beginWalk(options.activeCompositionPath);\n\n const items: DomEditLayerItem[] = [];\n // fallow-ignore-next-line complexity\n const visit = (el: HTMLElement, depth: number) => {\n if (items.length >= maxItems) return;\n\n const entry = readDomEditLayerWalkEntry(el, options.activeCompositionPath, cache);\n if (entry) {\n const { target } = entry;\n items.push({\n key: getDomEditLayerKey(target),\n element: el,\n label: entry.label,\n tagName: el.tagName.toLowerCase(),\n depth,\n childCount: entry.childCount,\n id: target.id ?? undefined,\n hfId: target.hfId ?? undefined,\n selector: target.selector ?? undefined,\n selectorIndex: target.selectorIndex,\n sourceFile: target.sourceFile,\n });\n }\n\n const nextDepth = entry ? depth + 1 : depth;\n for (const child of Array.from(el.children)) {\n if (!isHtmlElement(child)) continue;\n visit(child, nextDepth);\n if (items.length >= maxItems) return;\n }\n };\n\n // Every item resolves its selector's occurrence index, and unshared that is a\n // whole-document query per element — quadratic once a composition repeats a\n // card or tile class. The walk is one synchronous read of a document it does\n // not mutate, so one index per selector serves the whole of it. The pass lives\n // here rather than in each caller because this function owns the loop.\n withSelectorIndexPass(root.ownerDocument, () => {\n // Drilled into a group → show only its members; otherwise the whole tree.\n for (const el of groupScopedLayerRoots(root, options.activeGroupElement ?? null)) visit(el, 0);\n });\n return items;\n}\n\nexport function liveLayerElement(\n layer: DomEditLayerItem,\n doc: Document | null | undefined,\n activeCompositionPath: string | null,\n): HTMLElement {\n if (layer.element.isConnected || !doc) return layer.element;\n return findElementForSelection(doc, layer, activeCompositionPath) ?? layer.element;\n}\n\n// ─── Patch operations ────────────────────────────────────────────────────────\n\nexport function buildDomEditStylePatchOperation(\n property: string,\n value: string | null,\n childLocator?: DomEditChildLocator,\n): PatchOperation {\n return {\n type: \"inline-style\",\n property,\n value,\n ...childLocator,\n };\n}\n\nexport function buildDomEditTextPatchOperation(\n value: string,\n childLocator?: DomEditChildLocator,\n): PatchOperation {\n return { type: \"text-content\", property: \"text\", value, ...childLocator };\n}\n\n/** Replace an element's contents with markup, for a change no per-child operation\n * can express (a text layer added, removed or reordered). Sanitized at both ends. */\nexport function buildDomEditRichTextPatchOperation(value: string): PatchOperation {\n return { type: \"rich-text\", property: \"\", value };\n}\n\n// ─── Non-editable reason ─────────────────────────────────────────────────────\n\nfunction hasSupportedDirectEdit(capabilities: DomEditCapabilities): boolean {\n return (\n capabilities.canEditStyles ||\n capabilities.canMove ||\n capabilities.canResize ||\n capabilities.canApplyManualOffset ||\n capabilities.canApplyManualSize ||\n capabilities.canApplyManualRotation\n );\n}\n\nexport function getDomEditNonEditableReason(\n element: HTMLElement,\n selection: DomEditSelection | null,\n): string | null {\n if (!selection) {\n return \"No stable source target\";\n }\n\n if (selection.element !== element) {\n return selection.isCompositionHost\n ? \"Nested composition boundary\"\n : `Selection resolves to ${selection.label}`;\n }\n\n if (!hasSupportedDirectEdit(selection.capabilities)) {\n return selection.capabilities.reasonIfDisabled ?? \"No supported direct edits\";\n }\n\n return null;\n}\n\nexport function getDomEditTargetKey(\n selection: Pick<DomEditSelection, \"id\" | \"hfId\" | \"selector\" | \"selectorIndex\" | \"sourceFile\">,\n): string {\n return [\n selection.sourceFile || \"index.html\",\n selection.hfId ?? \"\",\n selection.id ?? \"\",\n selection.selector ?? \"\",\n selection.selectorIndex ?? \"\",\n ].join(\"|\");\n}\n\nexport function isTextEditableSelection(selection: DomEditSelection): boolean {\n return resolveEditingSections(domEditSelectionToFacts(selection)).text;\n}\n\n// buildElementAgentPrompt is in domEditingAgentPrompt.ts\n\nexport function readHfId(element: Element): string | undefined {\n return element.getAttribute(\"data-hf-id\")?.trim() || undefined;\n}\n\nexport function buildDomEditPatchTarget(\n selection: Pick<DomEditSelection, \"id\" | \"hfId\" | \"selector\" | \"selectorIndex\">,\n): { id?: string | null; hfId?: string; selector?: string; selectorIndex?: number } {\n return {\n id: selection.id,\n hfId: selection.hfId,\n selector: selection.selector,\n selectorIndex: selection.selectorIndex,\n };\n}\n","import { isHtmlElement } from \"./domEditingDom\";\n\n// `data-hf-group` selection semantics: a group wrapper is selected as one unit\n// until the user drills into it; once drilled in, clicks resolve to its children\n// (or to the next nested group inside it). One level of drill-in at a time keeps\n// nested groups navigable.\n\nexport type GroupCapture =\n | { kind: \"unit\"; element: HTMLElement } // select this group wrapper as one unit\n | { kind: \"child\" } // resolve the clicked element normally\n | { kind: \"out-of-scope\" }; // clicked outside the drilled-into group → select nothing\n\n// Layer-tree roots: the drilled-into group's element children, else the doc root.\nexport function groupScopedLayerRoots(\n root: HTMLElement,\n activeGroupElement: HTMLElement | null,\n): HTMLElement[] {\n const els = activeGroupElement?.isConnected ? Array.from(activeGroupElement.children) : [root];\n return els.filter(isHtmlElement);\n}\n\nexport function resolveGroupCapture(\n startEl: HTMLElement,\n activeGroupElement: HTMLElement | null,\n): GroupCapture {\n const groups: HTMLElement[] = [];\n for (let n: HTMLElement | null = startEl; n; n = n.parentElement) {\n if (n.hasAttribute(\"data-hf-group\")) groups.push(n);\n }\n const result = ((): GroupCapture => {\n if (!activeGroupElement) {\n const outermost = groups[groups.length - 1];\n return outermost ? { kind: \"unit\", element: outermost } : { kind: \"child\" };\n }\n const idx = groups.indexOf(activeGroupElement);\n if (idx === -1) return { kind: \"out-of-scope\" };\n const nestedInside = groups[idx - 1];\n return nestedInside ? { kind: \"unit\", element: nestedInside } : { kind: \"child\" };\n })();\n return result;\n}\n","/**\n * Element visibility, visual scoring, layer patch targets, element finders,\n * and the `findElementForSelection` / `findElementForTimelineElement` lookups.\n */\nimport type {\n DomEditContextOptions,\n DomEditSelection,\n DomEditViewport,\n TimelineElementDomTarget,\n TimelineElementDomTargetOptions,\n} from \"./domEditingTypes\";\nimport {\n buildStableSelector,\n escapeCssString,\n getSelectorIndex,\n getSourceFileForElement,\n isHtmlElement,\n isElementVisibleThroughAncestors,\n normalizeTimelineCompositionSource,\n querySelectorAllSafely,\n} from \"./domEditingDom\";\n\n// ─── Visibility ──────────────────────────────────────────────────────────────\n\nexport function isElementComputedVisible(\n el: HTMLElement,\n memo?: Map<HTMLElement, boolean>,\n): boolean {\n return isElementVisibleThroughAncestors(el, memo);\n}\n\nconst VISUAL_LEAF_TAGS = new Set([\"img\", \"video\", \"canvas\", \"svg\", \"audio\"]);\n\n// fallow-ignore-next-line complexity\nfunction hasVisualPresence(el: HTMLElement): boolean {\n const win = el.ownerDocument.defaultView;\n if (!win) return false;\n const cs = win.getComputedStyle(el);\n if (cs.backgroundImage !== \"none\") return true;\n if (\n cs.backgroundColor &&\n cs.backgroundColor !== \"transparent\" &&\n cs.backgroundColor !== \"rgba(0, 0, 0, 0)\"\n )\n return true;\n if (cs.borderWidth && parseFloat(cs.borderWidth) > 0 && cs.borderStyle !== \"none\") return true;\n if (cs.boxShadow && cs.boxShadow !== \"none\") return true;\n return false;\n}\n\nfunction isEmptyVisualContainer(el: HTMLElement): boolean {\n const tag = el.tagName.toLowerCase();\n if (VISUAL_LEAF_TAGS.has(tag)) return false;\n if (hasVisualPresence(el)) return false;\n\n const { children } = el;\n if (children.length === 0) {\n return (el.textContent ?? \"\").trim().length === 0;\n }\n\n for (let i = 0; i < children.length; i += 1) {\n const child = children[i];\n if (!isHtmlElement(child)) continue;\n if (VISUAL_LEAF_TAGS.has(child.tagName.toLowerCase())) return false;\n if (isElementComputedVisible(child)) return false;\n }\n\n return true;\n}\n\nfunction hasRenderedBox(el: HTMLElement): boolean {\n const rect = el.getBoundingClientRect();\n if (rect.width <= 1 || rect.height <= 1) return false;\n if (!isElementComputedVisible(el)) return false;\n if (isEmptyVisualContainer(el)) return false;\n return true;\n}\n\n// ─── Visual scoring ──────────────────────────────────────────────────────────\n\n// ─── Layer patch target ──────────────────────────────────────────────────────\n\nconst DOM_LAYER_IGNORED_TAGS = new Set([\n \"base\",\n \"br\",\n \"canvas\",\n \"link\",\n \"meta\",\n \"script\",\n \"source\",\n \"style\",\n \"template\",\n \"track\",\n \"wbr\",\n]);\n\n/**\n * Is the element rendered at all? The half of `getDomLayerPatchTarget` that a\n * plain style write can flip, and the only half that reads computed style.\n * Exported so a caller memoizing a layer walk can give it its own (short)\n * cache lifetime — see `domEditLayerWalkCache`.\n */\nexport function isInspectableLayerElement(el: HTMLElement): boolean {\n const tagName = el.tagName.toLowerCase();\n if (DOM_LAYER_IGNORED_TAGS.has(tagName)) return false;\n\n const computed = el.ownerDocument.defaultView?.getComputedStyle(el);\n if (computed?.display === \"none\" || computed?.visibility === \"hidden\") return false;\n\n return true;\n}\n\n/**\n * How the element is ADDRESSED — its selector, that selector's occurrence index\n * within the source file, and the file itself. Independent of whether the\n * element currently renders, so it survives every style write and moves only\n * when an identity attribute changes or the document's population of elements\n * sharing the selector does.\n */\n/**\n * The selector an element is addressed by, or undefined when it cannot be a\n * layer at all. Sole owner of that rule: `isDomLayerElement` answers the same\n * question without paying for the occurrence index, and a second copy of the\n * conditions would go stale the first time one of them changed.\n */\nfunction resolveLayerSelector(el: HTMLElement): string | undefined {\n if (el.hasAttribute(\"data-composition-id\")) return undefined;\n return buildStableSelector(el);\n}\n\nexport function resolveDomLayerIdentity(\n el: HTMLElement,\n activeCompositionPath: string | null,\n): Pick<DomEditSelection, \"id\" | \"hfId\" | \"selector\" | \"selectorIndex\" | \"sourceFile\"> | null {\n const selector = resolveLayerSelector(el);\n if (!selector) return null;\n\n const { sourceFile } = getSourceFileForElement(el, activeCompositionPath);\n return {\n id: el.id || undefined,\n hfId: el.getAttribute(\"data-hf-id\") || undefined,\n selector,\n selectorIndex: getSelectorIndex(\n el.ownerDocument,\n el,\n selector,\n sourceFile,\n activeCompositionPath,\n ),\n sourceFile,\n };\n}\n\nexport function getDomLayerPatchTarget(\n el: HTMLElement,\n activeCompositionPath: string | null,\n): Pick<DomEditSelection, \"id\" | \"hfId\" | \"selector\" | \"selectorIndex\" | \"sourceFile\"> | null {\n return isInspectableLayerElement(el) ? resolveDomLayerIdentity(el, activeCompositionPath) : null;\n}\n\n/**\n * Is `el` a layer at all? Exactly the condition under which\n * `getDomLayerPatchTarget` returns non-null, for callers that want the yes/no\n * and throw the target away.\n *\n * Worth its own function because the target carries the selector's OCCURRENCE\n * INDEX, and resolving that is a whole-document query — which the yes/no does\n * not depend on. Counting an element's layer children asked for one per child.\n */\nexport function isDomLayerElement(el: HTMLElement): boolean {\n return isInspectableLayerElement(el) && resolveLayerSelector(el) !== undefined;\n}\n\n// ─── Clip ancestor / selection candidate ─────────────────────────────────────\n\nfunction getPreferredClipAncestor(startEl: HTMLElement): HTMLElement | null {\n let current: HTMLElement | null = startEl;\n while (current) {\n if (current.classList.contains(\"clip\")) {\n const isCompositionHost =\n current.hasAttribute(\"data-composition-src\") ||\n current.hasAttribute(\"data-composition-file\");\n if (!isCompositionHost || current === startEl) return current;\n }\n current = current.parentElement;\n }\n return null;\n}\n\nexport function getSelectionCandidate(\n startEl: HTMLElement,\n options: DomEditContextOptions,\n): HTMLElement {\n if (options.preferClipAncestor) {\n const clipAncestor = getPreferredClipAncestor(startEl);\n if (clipAncestor) {\n return clipAncestor;\n }\n }\n\n return startEl;\n}\n\n// ─── Visual target resolution ─────────────────────────────────────────────────\n\nexport function resolveVisualDomEditSelectionTarget(\n elementsFromPoint: Iterable<Element | null | undefined>,\n options: Pick<DomEditContextOptions, \"activeCompositionPath\">,\n): HTMLElement | null {\n const candidates = resolveAllVisualDomEditTargets(elementsFromPoint, options);\n return candidates[0] ?? null;\n}\n\n/**\n * Returns all independently-selectable elements at the given point, in paint\n * order (topmost first). Used for click-cycling through stacked layers.\n *\n * Each entry in the returned array is an independent \"layer\" — an element\n * that is not an ancestor of an earlier entry. This gives one result per\n * z-stacked element rather than one per DOM node.\n */\nexport function resolveAllVisualDomEditTargets(\n elementsFromPoint: Iterable<Element | null | undefined>,\n options: Pick<DomEditContextOptions, \"activeCompositionPath\">,\n): HTMLElement[] {\n const raw: HTMLElement[] = [];\n\n for (const entry of elementsFromPoint) {\n if (!isHtmlElement(entry)) continue;\n if (hasRenderedBox(entry) && getDomLayerPatchTarget(entry, options.activeCompositionPath)) {\n raw.push(entry);\n }\n }\n\n if (raw.length === 0) return [];\n\n // First pass: for each contiguous ancestor-descendant run, keep only the\n // deepest (most specific) element, matching the original single-pick logic.\n const layers: HTMLElement[] = [];\n let best = raw[0];\n for (let i = 1; i < raw.length; i++) {\n const el = raw[i];\n if (best.contains(el)) {\n best = el; // go deeper in this subtree\n } else {\n layers.push(best);\n best = el;\n }\n }\n layers.push(best);\n\n return layers;\n}\n\n// ─── Raster detection ────────────────────────────────────────────────────────\n\nfunction hasRasterBackground(selection: Pick<DomEditSelection, \"computedStyles\">): boolean {\n const backgroundImage = selection.computedStyles[\"background-image\"]?.trim();\n return Boolean(backgroundImage && backgroundImage !== \"none\");\n}\n\nexport function isLargeRasterDomEditSelection(\n selection: Pick<DomEditSelection, \"boundingBox\" | \"computedStyles\" | \"tagName\">,\n viewport?: DomEditViewport | null,\n): boolean {\n const tagName = selection.tagName.toLowerCase();\n const isRasterLike = tagName === \"img\" || hasRasterBackground(selection);\n if (!isRasterLike) return false;\n\n const { width, height } = selection.boundingBox;\n if (width <= 1 || height <= 1) return false;\n if (!viewport || viewport.width <= 1 || viewport.height <= 1) {\n return width >= 960 && height >= 540;\n }\n\n const areaRatio = (width * height) / (viewport.width * viewport.height);\n const widthRatio = width / viewport.width;\n const heightRatio = height / viewport.height;\n return areaRatio >= 0.4 || (widthRatio >= 0.7 && heightRatio >= 0.5);\n}\n\n// ─── Element finders ──────────────────────────────────────────────────────────\n\ntype FindElementSelection = Pick<DomEditSelection, \"id\" | \"hfId\" | \"selector\" | \"selectorIndex\"> & {\n sourceFile?: string;\n};\n\nexport function findElementForSelection(\n doc: Document,\n selection: FindElementSelection,\n activeCompositionPath: string | null = null,\n): HTMLElement | null {\n const sourceMatches = (candidate: Element): candidate is HTMLElement =>\n isHtmlElement(candidate) &&\n (!selection.sourceFile ||\n getSourceFileForElement(candidate, activeCompositionPath).sourceFile ===\n selection.sourceFile);\n const findAll = (selector: string): HTMLElement[] =>\n querySelectorAllSafely(doc, selector).filter(sourceMatches);\n\n if (selection.hfId) {\n const byHfId = findAll(`[data-hf-id=\"${escapeCssString(selection.hfId)}\"]`)[0];\n if (byHfId) return byHfId;\n }\n\n if (selection.id) {\n // Flattened sub-compositions can repeat authored ids. getElementById returns\n // only the first document match, so filter every id match by source first.\n const byId = findAll(`[id=\"${escapeCssString(selection.id)}\"]`)[0];\n if (byId) return byId;\n }\n\n if (!selection.selector) return null;\n return findAll(selection.selector)[selection.selectorIndex ?? 0] ?? null;\n}\n\n// The selected node while mounted, else its copy in the selection's own file (ids repeat across files), else any copy.\nexport function findPreviewNode(\n doc: Document | null | undefined,\n el: DomEditSelection,\n): Element | null {\n if (!doc) return null;\n if (el.element?.isConnected && el.element.ownerDocument === doc) return el.element;\n return (\n findElementForSelection(doc, el) ??\n findElementForSelection(doc, { ...el, sourceFile: undefined })\n );\n}\n\n// fallow-ignore-next-line complexity\nexport function findElementForTimelineElement(\n doc: Document,\n element: TimelineElementDomTarget,\n options: TimelineElementDomTargetOptions,\n): HTMLElement | null {\n const elementId = typeof element.id === \"string\" ? element.id : \"\";\n const compositionSource =\n normalizeTimelineCompositionSource(element.compositionSrc) ??\n options.compIdToSrc?.get(elementId);\n const sourceFile =\n compositionSource ??\n normalizeTimelineCompositionSource(element.sourceFile) ??\n options.activeCompositionPath ??\n \"index.html\";\n const escapedElementId = escapeCssString(elementId);\n const escapedCompositionSource = compositionSource ? escapeCssString(compositionSource) : null;\n const selector =\n element.selector ??\n (compositionSource\n ? `[data-composition-src=\"${escapedCompositionSource}\"],[data-composition-file=\"${escapedCompositionSource}\"],[data-composition-id=\"${escapedElementId}\"]`\n : escapedElementId\n ? `[data-composition-id=\"${escapedElementId}\"]`\n : undefined);\n\n if (selector || element.domId) {\n const targetElement = findElementForSelection(\n doc,\n {\n id: element.domId ?? undefined,\n selector,\n selectorIndex: element.selectorIndex,\n sourceFile,\n },\n options.activeCompositionPath,\n );\n if (targetElement) return targetElement;\n }\n\n const hasExplicitDomTarget = Boolean(element.domId || element.selector || compositionSource);\n if (options.isMasterView || hasExplicitDomTarget || !options.activeCompositionPath) {\n return null;\n }\n\n const root = doc.querySelector(\"[data-composition-id]\");\n if (!isHtmlElement(root)) return null;\n return getSourceFileForElement(root, options.activeCompositionPath).sourceFile === sourceFile\n ? root\n : null;\n}\n\n// ─── Layer children ───────────────────────────────────────────────────────────\n\n/** `el`'s direct children that are layers. No longer takes the context options:\n * layer-ness does not depend on the active composition path. */\nexport function getDirectLayerChildren(el: HTMLElement): HTMLElement[] {\n return Array.from(el.children).filter(\n (child): child is HTMLElement => isHtmlElement(child) && isDomLayerElement(child),\n );\n}\n","import { parsePx } from \"./domEditingDom\";\n\nconst COMPOSITION_ROOT_LAYER_EPSILON_PX = 1;\n\nfunction readPositiveDimension(value: string | null): number | null {\n if (!value) return null;\n const parsed = Number.parseFloat(value);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : null;\n}\n\nfunction approximatelyEqual(a: number, b: number) {\n return Math.abs(a - b) <= COMPOSITION_ROOT_LAYER_EPSILON_PX;\n}\n\nfunction getCompositionRootBounds(doc: Document) {\n const root =\n doc.querySelector<HTMLElement>(\"[data-composition-id]\") ?? doc.documentElement ?? null;\n const rootWidth = readPositiveDimension(root?.getAttribute(\"data-width\") ?? null);\n const rootHeight = readPositiveDimension(root?.getAttribute(\"data-height\") ?? null);\n if (!root || !rootWidth || !rootHeight) return null;\n return { rect: root.getBoundingClientRect(), width: rootWidth, height: rootHeight };\n}\n\nfunction getRenderedLayerSize(element: HTMLElement, computedStyles: Record<string, string>) {\n const rect = element.getBoundingClientRect();\n const width = rect.width || parsePx(computedStyles.width);\n const height = rect.height || parsePx(computedStyles.height);\n return width && height ? { width, height } : null;\n}\n\nfunction matchesCompositionRootBounds(\n elementRect: DOMRect,\n elementSize: { width: number; height: number },\n rootBounds: { rect: DOMRect; width: number; height: number },\n) {\n return (\n approximatelyEqual(elementRect.left, rootBounds.rect.left) &&\n approximatelyEqual(elementRect.top, rootBounds.rect.top) &&\n approximatelyEqual(elementSize.width, rootBounds.width) &&\n approximatelyEqual(elementSize.height, rootBounds.height)\n );\n}\n\nfunction isExplicitFullBleedLayer(computedStyles: Record<string, string>) {\n return computedStyles.position === \"absolute\" || computedStyles.position === \"fixed\";\n}\n\nexport function isCompositionRootLayer(\n element: HTMLElement,\n doc: Document,\n computedStyles: Record<string, string>,\n) {\n if (element.parentElement !== doc.body) return false;\n if (element.hasAttribute(\"data-hf-allow-root-edit\")) return false;\n if (isExplicitFullBleedLayer(computedStyles)) return false;\n\n const rootBounds = getCompositionRootBounds(doc);\n const elementSize = getRenderedLayerSize(element, computedStyles);\n return Boolean(\n rootBounds &&\n elementSize &&\n matchesCompositionRootBounds(element.getBoundingClientRect(), elementSize, rootBounds),\n );\n}\n","/**\n * Incremental memory for `collectDomEditLayerItems`.\n *\n * The layer walk is re-run from scratch every time a MutationObserver says the\n * preview changed, and the observer's loudest source is inline `style` — which\n * is exactly what animation writes. So a paused-looking editor re-derived every\n * element's patch target, label and child count several times a second, and each\n * derivation costs two `getComputedStyle` reads (its own plus one per direct\n * child), two ancestor walks for the source file, and a `textContent` read whose\n * cost is the element's whole subtree.\n *\n * None of that depends on the traversal — only on the element. So the traversal\n * stays live (it is pointer-chasing, and `depth` genuinely is positional) and the\n * per-element half is memoized, with the mutation records deciding what to drop.\n *\n * WHAT MAKES A CACHED ENTRY WRONG, and how each is caught:\n *\n * - the element's own attributes (`style` -> computed display, `class`/`id`/\n * `data-hf-group` -> selector and label) -> attribute record on it\n * - an ancestor's `visibility`, the ONLY inherited input to the\n * display/visibility gate (`display:none` on an ancestor does not change a\n * descendant's own computed `display`) -> attribute record on the\n * ancestor, whose subtree is\n * dropped only when the\n * style declares visibility\n * - a direct child appearing or disappearing from the layer list, which moves\n * the parent's `childCount` -> the parent is dropped\n * alongside every element\n * - descendant text, which moves an ancestor's label -> characterData record,\n * ancestors dropped\n * - selector occurrence indices, which shift for EVERY element sharing a\n * selector when one element's identity changes -> whole cache dropped\n * - nodes added or removed, which shift indices, order, labels and counts at\n * once and cannot be attributed to one element -> whole cache dropped\n * - a different active composition path, or a new composition-id -> source-file\n * map, both of which re-scope every source file -> checked per walk\n *\n * The bias is deliberate and matches the duration cache in the runtime: a\n * redundant recomputation is a missed optimisation, a missed one is a wrong\n * layer tree.\n */\n\nimport { buildElementLabel, getCompositionSourceMapRevision, isHtmlElement } from \"./domEditingDom\";\nimport {\n getDirectLayerChildren,\n isInspectableLayerElement,\n resolveDomLayerIdentity,\n} from \"./domEditingElement\";\nimport type { DomEditSelection } from \"./domEditingTypes\";\n\n/** How an element is addressed. Survives style writes; see `readIdentity`. */\nexport type DomEditLayerIdentity = Pick<\n DomEditSelection,\n \"id\" | \"hfId\" | \"selector\" | \"selectorIndex\" | \"sourceFile\"\n>;\n\n/** Whether the element renders, and how many of its direct children are layers.\n * Both are computed style reads, and both a style write can flip. Filled\n * lazily: `childCount` is only asked for once an element is known to be one. */\ninterface PresenceMemo {\n inspectable?: boolean;\n childCount?: number;\n}\n\ninterface IdentityMemo {\n identity?: DomEditLayerIdentity | null;\n label?: string;\n}\n\n/**\n * Attributes that decide an element's SELECTOR, and therefore the occurrence\n * index of every other element sharing that selector. One of these changing\n * renumbers elements the record does not mention, so the only sound response is\n * to drop everything. `class` is here because `buildStableSelector` falls back\n * to the preferred class.\n */\nconst SELECTOR_IDENTITY_ATTRIBUTES = new Set([\n \"id\",\n \"class\",\n \"data-hf-group\",\n \"data-composition-id\",\n \"data-composition-file\",\n \"data-composition-src\",\n \"data-hf-original-composition-id\",\n]);\n\n/**\n * The observer configuration the cache's invalidation is written against.\n * Exported so the refresh loop and the tests cannot drift from it.\n *\n * No `attributeFilter`: an unlisted attribute is one the cache would never learn\n * about, and serving a stale entry is worse than the extra records.\n */\nexport const DOM_EDIT_LAYER_OBSERVER_INIT: MutationObserverInit = {\n attributes: true,\n characterData: true,\n childList: true,\n subtree: true,\n};\n\n/**\n * The inline `visibility` each element last presented. Deliberately NOT cleared\n * by `invalidateAll`: it records what the DOM said, not a value derived from it.\n */\nconst lastInlineVisibility = new WeakMap<HTMLElement, string>();\n\n/**\n * Did this style write move the element's inline `visibility`?\n *\n * `visibility` is inherited, so changing it changes the layer-list membership of\n * the element's whole subtree, and only then is the subtree worth dropping.\n *\n * The comparison is on the PARSED value, remembered per element. Testing the\n * attribute TEXT for \"visibility\" looks equivalent and is not: the standard\n * GSAP fade (`autoAlpha`) writes `visibility: visible` once and leaves it in the\n * attribute for the rest of the clip, so a text test stays true for every\n * transform tick afterwards. That is a whole-subtree walk per animation frame,\n * in the observer callback rather than behind the rebuild throttle, on exactly\n * the elements a composition animates most.\n *\n * An element seen for the first time reports changed, which costs one drop and\n * is the safe answer: its entries were derived before this write.\n */\nfunction inlineVisibilityChanged(el: HTMLElement): boolean {\n const next = el.style.visibility;\n if (lastInlineVisibility.get(el) === next) return false;\n lastInlineVisibility.set(el, next);\n return true;\n}\n\nexport interface DomEditLayerWalkCache {\n /**\n * Start a walk. Drops everything when the scoping inputs the entries were\n * built under no longer hold. Call once per `collectDomEditLayerItems`.\n */\n beginWalk(activeCompositionPath: string | null): void;\n /**\n * Does `el` render? Cheap to invalidate and invalidated often: any attribute\n * write on the element, on its parent, or a `visibility` write on an ancestor\n * drops it, because all three can flip the answer.\n */\n readInspectable(el: HTMLElement, compute: () => boolean): boolean;\n /** How many of `el`'s direct children are layers. Same lifetime as\n * `readInspectable`: it is a sum over their answers. */\n readChildCount(el: HTMLElement, compute: () => number): number;\n /**\n * `el`'s selector, occurrence index and source file. A SEPARATE, much longer\n * lifetime than presence: a style write cannot renumber a selector, so this\n * survives one, and it is the only part of the walk that queries the\n * document. That split is what makes an ordinary animation frame cost no\n * document queries at all.\n */\n readIdentity(\n el: HTMLElement,\n compute: () => DomEditLayerIdentity | null,\n ): DomEditLayerIdentity | null;\n /** `el`'s display label. Shares the identity lifetime, plus descendant text. */\n readLabel(el: HTMLElement, compute: () => string): string;\n /** Apply mutation records. Safe to call with an empty array. */\n ingest(records: MutationRecord[]): void;\n /** Forget every entry — the fallback for anything not attributable. */\n invalidateAll(): void;\n}\n\nexport function createDomEditLayerWalkCache(): DomEditLayerWalkCache {\n let presence = new WeakMap<HTMLElement, PresenceMemo>();\n let identities = new WeakMap<HTMLElement, IdentityMemo>();\n let scope: string | null = null;\n\n const invalidateAll = () => {\n presence = new WeakMap();\n identities = new WeakMap();\n };\n\n const drop = (el: HTMLElement | null) => {\n if (el) presence.delete(el);\n };\n\n const dropSubtree = (el: HTMLElement) => {\n for (const descendant of el.querySelectorAll(\"*\")) {\n if (isHtmlElement(descendant)) presence.delete(descendant);\n }\n };\n\n const ingestOne = (record: MutationRecord): boolean => {\n // Structural change: order, occurrence indices, labels and child counts all\n // move at once, and the record names the parent rather than everything\n // affected. This is the documented full-rebuild fallback.\n if (record.type === \"childList\") return false;\n\n if (record.type === \"characterData\") {\n // `buildElementLabel` falls back to `textContent`, so edited text changes\n // the label of every ancestor that contains it.\n for (\n let ancestor = record.target.parentElement;\n ancestor;\n ancestor = ancestor.parentElement\n ) {\n identities.delete(ancestor);\n }\n return true;\n }\n\n const target = record.target;\n if (!isHtmlElement(target)) return true;\n if (record.attributeName && SELECTOR_IDENTITY_ATTRIBUTES.has(record.attributeName))\n return false;\n\n drop(target);\n // The parent counts its layer children, and this element may have just\n // joined or left that count.\n drop(target.parentElement);\n if (record.attributeName === \"style\" && inlineVisibilityChanged(target)) dropSubtree(target);\n return true;\n };\n\n return {\n beginWalk(activeCompositionPath) {\n const nextScope = `${activeCompositionPath ?? \"\"}|${getCompositionSourceMapRevision()}`;\n if (nextScope === scope) return;\n scope = nextScope;\n invalidateAll();\n },\n readInspectable(el, compute) {\n const memo = presence.get(el) ?? {};\n if (memo.inspectable === undefined) {\n memo.inspectable = compute();\n presence.set(el, memo);\n }\n return memo.inspectable;\n },\n readChildCount(el, compute) {\n const memo = presence.get(el) ?? {};\n if (memo.childCount === undefined) {\n memo.childCount = compute();\n presence.set(el, memo);\n }\n return memo.childCount;\n },\n readIdentity(el, compute) {\n const memo = identities.get(el) ?? {};\n if (memo.identity === undefined) {\n memo.identity = compute();\n identities.set(el, memo);\n }\n return memo.identity;\n },\n readLabel(el, compute) {\n const memo = identities.get(el) ?? {};\n if (memo.label === undefined) {\n memo.label = compute();\n identities.set(el, memo);\n }\n return memo.label;\n },\n ingest(records) {\n for (const record of records) {\n if (!ingestOne(record)) {\n invalidateAll();\n return;\n }\n }\n },\n invalidateAll,\n };\n}\n\n/** One element's contribution to a layer walk. `depth` is absent on purpose: it\n * belongs to the traversal, not to the element. */\nexport interface DomEditLayerWalkEntry {\n target: DomEditLayerIdentity;\n label: string;\n childCount: number;\n}\n\n/**\n * Everything `collectDomEditLayerItems` needs about ONE element, served from\n * `cache` where it is still valid. Null when the element is not a layer.\n *\n * The four reads go through the cache separately rather than as one record\n * because they do not go stale together: a style write flips whether an element\n * renders without touching how it is addressed. Without a cache this is exactly\n * the original per-element derivation, which is what every other caller of the\n * walk still gets.\n *\n * Lives here rather than in the walk so the two halves of the memoization —\n * what is stored and what is read — sit in one file.\n */\nexport function readDomEditLayerWalkEntry(\n el: HTMLElement,\n activeCompositionPath: string | null,\n cache?: DomEditLayerWalkCache,\n): DomEditLayerWalkEntry | null {\n const inspectable = cache\n ? cache.readInspectable(el, () => isInspectableLayerElement(el))\n : isInspectableLayerElement(el);\n if (!inspectable) return null;\n\n const identity = () => resolveDomLayerIdentity(el, activeCompositionPath);\n const target = cache ? cache.readIdentity(el, identity) : identity();\n if (!target) return null;\n\n const label = () => buildElementLabel(el);\n const childCount = () => getDirectLayerChildren(el).length;\n return {\n target,\n label: cache ? cache.readLabel(el, label) : label(),\n childCount: cache ? cache.readChildCount(el, childCount) : childCount(),\n };\n}\n\n/**\n * Apply the records the observer has taken in but not yet delivered.\n *\n * Records arrive in a microtask, so an edit made earlier in THIS task would\n * otherwise be read back against entries that predate it. Taking them suppresses\n * the observer's own callback for them, which is equivalent here: that callback\n * only ingests and marks a rebuild owed, and a caller draining is rebuilding\n * regardless. A caller with no observer yet has nothing pending.\n */\nexport function drainPendingLayerMutations(\n observer: MutationObserver | null,\n cache: DomEditLayerWalkCache,\n): void {\n if (!observer) return;\n cache.ingest(observer.takeRecords());\n}\n"],"mappings":"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","names":["env","env"]}
|