@hyperframes/studio 0.8.51 → 0.8.53-desktop.0

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.
Files changed (160) hide show
  1. package/dist/assets/{hyperframes-player-Cn_PpHqP.js → hyperframes-player-PTEnAHhg.js} +1 -1
  2. package/dist/assets/{index-CPS3Cvj8.js → index--myXp8ui.js} +1 -1
  3. package/dist/assets/{index-Clrb11lj.js → index-BjslOS9g.js} +1 -1
  4. package/dist/assets/index-BnXZuIJD.css +1 -0
  5. package/dist/assets/index-CNyviK_l.js +390 -0
  6. package/dist/index.css +29 -0
  7. package/dist/index.css.map +1 -0
  8. package/dist/index.d.ts +808 -12
  9. package/dist/index.html +2 -2
  10. package/dist/index.js +8109 -7583
  11. package/dist/index.js.map +1 -1
  12. package/package.json +9 -7
  13. package/src/App.tsx +45 -50
  14. package/src/captions/components/CaptionOverlay.sender.test.tsx +52 -0
  15. package/src/captions/components/CaptionOverlay.tsx +8 -4
  16. package/src/components/EditorShell.selectionSync.test.tsx +9 -4
  17. package/src/components/EditorShell.tsx +56 -56
  18. package/src/components/RightPanelTabs.test.tsx +151 -0
  19. package/src/components/RightPanelTabs.tsx +59 -0
  20. package/src/components/StudioHeader.dom.test.tsx +181 -0
  21. package/src/components/StudioHeader.test.ts +0 -12
  22. package/src/components/StudioHeader.tsx +117 -160
  23. package/src/components/StudioLeftPanels.tsx +202 -0
  24. package/src/components/{StudioRightPanel.tsx → StudioRightPanels.tsx} +57 -194
  25. package/src/components/{StudioRightPanel.types.ts → StudioRightPanels.types.ts} +4 -3
  26. package/src/components/TimelineHistoryButtons.dom.test.tsx +117 -0
  27. package/src/components/TimelineHistoryButtons.tsx +54 -0
  28. package/src/components/TimelineToolbar.test.tsx +8 -0
  29. package/src/components/TimelineToolbar.tsx +3 -7
  30. package/src/components/dock/Dock.test.tsx +100 -18
  31. package/src/components/dock/Dock.tsx +32 -5
  32. package/src/components/dock/dock.css +5 -0
  33. package/src/components/dock/dockAccessibility.test.ts +166 -0
  34. package/src/components/dock/dockAccessibility.ts +98 -0
  35. package/src/components/dock/dockLayout.test.ts +134 -0
  36. package/src/components/dock/dockLayout.ts +53 -6
  37. package/src/components/dock/dockLayoutStore.test.ts +28 -0
  38. package/src/components/dock/dockLayoutStore.ts +21 -4
  39. package/src/components/dock/dockTestHarness.ts +11 -0
  40. package/src/components/editor/LayersPanel.test.ts +97 -1
  41. package/src/components/editor/LayersPanel.tsx +4 -2
  42. package/src/components/editor/PreviewGuides.tsx +1 -1
  43. package/src/components/editor/SnapToolbar.test.tsx +0 -3
  44. package/src/components/editor/TopologyLens.test.tsx +28 -0
  45. package/src/components/editor/TopologyLens.tsx +3 -1
  46. package/src/components/editor/useColorGradingController.test.ts +38 -0
  47. package/src/components/editor/useColorGradingController.ts +9 -1
  48. package/src/components/editor/useColorGradingPreviews.test.ts +76 -0
  49. package/src/components/editor/useColorGradingPreviews.ts +11 -1
  50. package/src/components/editor/usePreviewCompositionRect.test.tsx +95 -0
  51. package/src/components/editor/usePreviewCompositionRect.ts +21 -0
  52. package/src/components/nle/NLEContext.tsx +0 -43
  53. package/src/components/nle/NLEPreview.tsx +1 -1
  54. package/src/components/nle/PreviewPane.tsx +1 -1
  55. package/src/components/nle/TimelinePane.tsx +53 -67
  56. package/src/components/panels/SlideshowPanel.tsx +1 -1
  57. package/src/components/panels/VariablesPanel.tsx +1 -1
  58. package/src/components/pointerTargetSize.test.tsx +8 -0
  59. package/src/components/renders/RenderQueue.tsx +1 -1
  60. package/src/components/renders/RenderQueuePanel.tsx +1 -1
  61. package/src/components/sidebar/{LeftSidebar.masterComposition.test.tsx → CompositionsPanel.masterComposition.test.tsx} +6 -9
  62. package/src/components/sidebar/CompositionsPanel.tsx +15 -0
  63. package/src/components/sidebar/{LeftSidebar.lintBadge.test.tsx → SidebarLintButton.test.tsx} +6 -11
  64. package/src/components/sidebar/SidebarLintButton.tsx +50 -0
  65. package/src/components/timelineToolbarStyles.ts +5 -0
  66. package/src/components/ui/Button.test.tsx +222 -30
  67. package/src/components/ui/Button.tsx +88 -125
  68. package/src/components/ui/IconButton.tsx +38 -0
  69. package/src/components/ui/Input.tsx +115 -0
  70. package/src/components/ui/Menu.test.tsx +374 -0
  71. package/src/components/ui/Menu.tsx +213 -0
  72. package/src/components/ui/NumberField.tsx +122 -0
  73. package/src/components/ui/Popover.tsx +74 -0
  74. package/src/components/ui/Select.tsx +100 -0
  75. package/src/components/ui/Slider.tsx +153 -0
  76. package/src/components/ui/Tabs.test.tsx +98 -0
  77. package/src/components/ui/Tabs.tsx +71 -0
  78. package/src/components/ui/Toggle.tsx +63 -0
  79. package/src/components/ui/Tooltip.test.tsx +110 -0
  80. package/src/components/ui/Tooltip.tsx +38 -112
  81. package/src/components/ui/cn.test.ts +30 -0
  82. package/src/components/ui/cn.ts +25 -0
  83. package/src/components/ui/index.ts +29 -2
  84. package/src/components/ui/mountHost.testHelpers.ts +24 -0
  85. package/src/components/ui/valueControls.test.tsx +524 -0
  86. package/src/contexts/PanelLayoutContext.tsx +3 -57
  87. package/src/hooks/appHotkeysDispatch.ts +3 -4
  88. package/src/hooks/timelineEditPermission.ts +31 -0
  89. package/src/hooks/useAppHotkeys.previewForwarding.test.tsx +0 -2
  90. package/src/hooks/useAppHotkeys.test.ts +0 -1
  91. package/src/hooks/useAppHotkeys.textEditing.test.tsx +0 -3
  92. package/src/hooks/useAppHotkeys.ts +20 -83
  93. package/src/hooks/useCaptionDetection.sender.test.tsx +46 -0
  94. package/src/hooks/useCaptionDetection.ts +3 -1
  95. package/src/hooks/useDomEditPreviewSync.ts +4 -6
  96. package/src/hooks/useDomEditSession.ts +0 -7
  97. package/src/hooks/useDomEditWiring.ts +4 -9
  98. package/src/hooks/useEditHistoryActions.test.tsx +90 -0
  99. package/src/hooks/useEditHistoryActions.ts +106 -0
  100. package/src/hooks/useFileManager.ts +21 -123
  101. package/src/hooks/usePanelLayout.test.ts +108 -296
  102. package/src/hooks/usePanelLayout.ts +50 -255
  103. package/src/hooks/usePersistentEditHistory.ts +1 -1
  104. package/src/hooks/useProjectFileWriter.ts +150 -0
  105. package/src/hooks/useRightPanelIntents.test.ts +181 -0
  106. package/src/hooks/useRightPanelIntents.ts +63 -0
  107. package/src/hooks/useServerConnection.hashProject.test.tsx +94 -0
  108. package/src/hooks/useServerConnection.ts +43 -10
  109. package/src/hooks/useSlideshowTabState.test.ts +9 -31
  110. package/src/hooks/useSlideshowTabState.ts +6 -19
  111. package/src/hooks/useStudioContextValue.test.ts +6 -18
  112. package/src/hooks/useStudioContextValue.ts +5 -26
  113. package/src/hooks/useTimelineDeleteOps.test.tsx +111 -0
  114. package/src/hooks/useTimelineEditing.test.tsx +322 -3
  115. package/src/hooks/useTimelineEditing.ts +103 -16
  116. package/src/hooks/useTimelineEditingTypes.ts +3 -0
  117. package/src/hooks/useTrackPendingTimelineEdit.test.tsx +62 -0
  118. package/src/hooks/useTrackPendingTimelineEdit.ts +22 -0
  119. package/src/hostMount.test.tsx +222 -0
  120. package/src/index.ts +87 -0
  121. package/src/player/components/AutomationSelectionMenu.tsx +1 -1
  122. package/src/player/components/Timeline.tsx +1 -1
  123. package/src/player/components/TimelineCanvas.tsx +4 -4
  124. package/src/player/components/TimelineClip.tsx +0 -1
  125. package/src/player/components/TimelineTrackHeader.test.tsx +26 -5
  126. package/src/player/components/TimelineTrackHeader.tsx +1 -1
  127. package/src/player/components/timelineTheme.test.ts +4 -4
  128. package/src/player/components/timelineTheme.ts +30 -30
  129. package/src/player/lib/playbackShortcuts.ts +2 -0
  130. package/src/player/lib/timelinePerformanceDiagnostics.test.ts +3 -2
  131. package/src/player/lib/timelinePerformanceDiagnostics.ts +1 -1
  132. package/src/player/store/previewIframeStore.notify.test.tsx +75 -0
  133. package/src/player/store/previewIframeStore.test.tsx +29 -0
  134. package/src/player/store/previewIframeStore.ts +25 -0
  135. package/src/renderQueueExports.test.tsx +58 -0
  136. package/src/styles/classCandidates.test.ts +105 -0
  137. package/src/styles/classCandidates.ts +281 -0
  138. package/src/styles/hex-baseline.json +84 -0
  139. package/src/styles/hexRatchet.test.ts +201 -0
  140. package/src/styles/styleSources.test.ts +20 -0
  141. package/src/styles/styleSources.ts +47 -0
  142. package/src/styles/theme.css +57 -0
  143. package/src/styles/theme.test.ts +2 -15
  144. package/src/styles/tokenGate.test.ts +109 -0
  145. package/src/utils/sourcePatcher.ts +1 -1
  146. package/src/utils/studioHelpers.test.ts +18 -0
  147. package/src/utils/studioHelpers.ts +11 -8
  148. package/src/utils/studioUiPreferences.test.ts +10 -16
  149. package/src/utils/studioUiPreferences.ts +0 -16
  150. package/src/utils/typingTarget.ts +4 -0
  151. package/dist/assets/index-C05SNrvY.js +0 -360
  152. package/dist/assets/index-DUR9qzQM.css +0 -1
  153. package/src/components/PanelTabButton.tsx +0 -31
  154. package/src/components/StudioLeftSidebar.tsx +0 -207
  155. package/src/components/nle/TimelineResizeDivider.tsx +0 -101
  156. package/src/components/sidebar/LeftSidebar.storage.test.ts +0 -28
  157. package/src/components/sidebar/LeftSidebar.tsx +0 -348
  158. package/src/hooks/useInspectorSplitResize.ts +0 -51
  159. package/src/utils/fitPanels.test.ts +0 -164
  160. package/src/utils/fitPanels.ts +0 -151
package/dist/index.d.ts CHANGED
@@ -1,12 +1,281 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
1
  import * as react from 'react';
3
- import { ReactNode, RefObject } from 'react';
2
+ import { ButtonHTMLAttributes, ReactNode, ElementType, ComponentPropsWithoutRef, ReactElement, RefObject, MutableRefObject } from 'react';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import { Tabs as Tabs$1 } from '@base-ui/react/tabs';
5
+ import * as node_modules_cn_dist_types from 'node_modules/cn/dist/types';
6
+ import { ContextMenu as ContextMenu$1 } from '@base-ui/react/context-menu';
7
+ import { Menu as Menu$1 } from '@base-ui/react/menu';
8
+ import { Popover as Popover$1 } from '@base-ui/react/popover';
4
9
  import { RuntimeTimelineClipIdentity, HyperframePickerElementInfo } from '@hyperframes/core';
5
10
  import { GsapAnimation, PropertyGroupName } from '@hyperframes/core/gsap-parser';
6
11
  import * as zustand from 'zustand';
7
12
  import { StoreApi } from 'zustand';
8
13
  import { MusicBeatAnalysis } from '@hyperframes/core/beats';
9
14
  import { GsapAnimation as GsapAnimation$1, ArcPathSegment } from '@hyperframes/parsers/gsap-parser';
15
+ import { CanvasResolution } from '@hyperframes/parsers';
16
+ import { Composition } from '@hyperframes/sdk';
17
+
18
+ type ButtonVariant = "primary" | "secondary" | "danger" | "ghost";
19
+ type ButtonSize = "sm" | "md" | "lg";
20
+ /** Forces one interactive look for a gallery shot. CSS-only; see the header. */
21
+ type PreviewState = "hover" | "active" | "focus";
22
+ interface ButtonBaseProps extends ButtonHTMLAttributes<HTMLButtonElement> {
23
+ variant?: ButtonVariant;
24
+ size?: ButtonSize;
25
+ "data-preview-state"?: PreviewState;
26
+ }
27
+ interface ButtonProps extends ButtonBaseProps {
28
+ loading?: boolean;
29
+ icon?: ReactNode;
30
+ }
31
+ /**
32
+ * Shared by Button and IconButton. `disabled:` keeps pointer events alive so a
33
+ * wrapping Tooltip can still explain why the control is disabled.
34
+ */
35
+ declare const buttonBase: string;
36
+ /**
37
+ * One entry per variant. The `data-[preview-state=…]` half of each string
38
+ * repeats the `hover:` and `active:` half exactly; the test pairs them.
39
+ */
40
+ declare const buttonVariants: Record<ButtonVariant, string>;
41
+ declare const Button: react.ForwardRefExoticComponent<ButtonProps & react.RefAttributes<HTMLButtonElement>>;
42
+
43
+ interface IconButtonProps extends ButtonBaseProps {
44
+ icon: ReactNode;
45
+ "aria-label": string;
46
+ }
47
+ declare const IconButton: react.ForwardRefExoticComponent<IconButtonProps & react.RefAttributes<HTMLButtonElement>>;
48
+
49
+ /**
50
+ * Base UI lets `className` be a function of the part's state. Studio's parts
51
+ * merge a string with `cn`, so the string form is the one they accept.
52
+ */
53
+ type StyledProps$1<T extends ElementType> = Omit<ComponentPropsWithoutRef<T>, "className"> & {
54
+ className?: string;
55
+ };
56
+ /**
57
+ * Groups a tab list with its panels. Uncontrolled by default; pass `value` and
58
+ * `onValueChange` for a strip whose selection lives in a store.
59
+ */
60
+ declare function Tabs(props: ComponentPropsWithoutRef<typeof Tabs$1.Root>): react_jsx_runtime.JSX.Element;
61
+ /** The strip. Give it an `aria-label`: a tablist with no name is unlabelled. */
62
+ declare function TabsList({ className, ...props }: StyledProps$1<typeof Tabs$1.List>): react_jsx_runtime.JSX.Element;
63
+ interface TabProps extends StyledProps$1<typeof Tabs$1.Tab> {
64
+ value: string;
65
+ "data-preview-state"?: PreviewState;
66
+ }
67
+ /**
68
+ * aria-selected drives the selected look (Base UI sets it) so assistive tech
69
+ * and sighted users match. data-preview-state is for gallery shots only.
70
+ */
71
+ declare function Tab({ value, className, ...props }: TabProps): react_jsx_runtime.JSX.Element;
72
+ /** The panel for one tab. Base UI wires `aria-labelledby` back to its tab. */
73
+ declare function TabPanel({ className, ...props }: StyledProps$1<typeof Tabs$1.Panel>): react_jsx_runtime.JSX.Element;
74
+
75
+ interface TooltipProps {
76
+ label: string;
77
+ /** A single element, wrapped in a box-less span so a disabled one still gets hover. */
78
+ children: ReactElement<{
79
+ "aria-describedby"?: string;
80
+ }>;
81
+ /** Hover delay in ms. */
82
+ delay?: number;
83
+ side?: "top" | "bottom" | "left" | "right";
84
+ }
85
+ declare function Tooltip({ label, children, delay, side }: TooltipProps): react_jsx_runtime.JSX.Element;
86
+
87
+ /**
88
+ * Studio's tailwind-merge config: teaches it the two scales `theme.css`
89
+ * invents (`text-step-*`, `ctl`/`ctl-sm`/`ctl-lg`) so overrides replace, not stack.
90
+ */
91
+ declare const cn: node_modules_cn_dist_types.CnFunction;
92
+
93
+ /**
94
+ * Base UI lets `className` be a function of the part's state. Studio's parts
95
+ * merge a string with `cn`, so the string form is the one they accept.
96
+ */
97
+ type StyledProps<T extends ElementType> = Omit<ComponentPropsWithoutRef<T>, "className"> & {
98
+ className?: string;
99
+ };
100
+ /** Forces the settled open look for a gallery shot; CSS-only, open state stays controlled. */
101
+ type PopupPreviewState = "open";
102
+ /** Where the portal puts the popup. `null` keeps it inline, next to its trigger. */
103
+ type PortalContainer$1 = ComponentPropsWithoutRef<typeof Menu$1.Portal>["container"];
104
+ /**
105
+ * Chrome shared by every floating panel (`Popover` too); callers add their own shadow token.
106
+ * `data-starting-style`/`data-ending-style` are Base UI's transition attributes.
107
+ */
108
+ declare const popupSurface: string;
109
+ type MenuItemTone = "default" | "danger";
110
+ interface PositionedProps {
111
+ side?: "top" | "bottom" | "left" | "right";
112
+ align?: "start" | "center" | "end";
113
+ sideOffset?: number;
114
+ /** Portal target. Pass the shadow root when the trigger lives in one. */
115
+ container?: PortalContainer$1;
116
+ /** Names the popup for assistive tech. A menu with no name is unlabelled. */
117
+ "aria-label"?: string;
118
+ className?: string;
119
+ "data-preview-state"?: PopupPreviewState;
120
+ }
121
+ interface MenuProps extends PositionedProps, Omit<ComponentPropsWithoutRef<typeof Menu$1.Root>, "children"> {
122
+ /** A single element. It becomes the trigger; no wrapper is added around it. */
123
+ trigger: ReactElement;
124
+ /** The items: `MenuItem`, `MenuRadioGroup`, `MenuSeparator`. */
125
+ children: ReactNode;
126
+ }
127
+ /**
128
+ * A menu opened by its trigger. Uncontrolled by default; pass `open` and
129
+ * `onOpenChange` for a menu whose state lives in a store.
130
+ */
131
+ declare function Menu({ trigger, children, side, align, sideOffset, container, className, "aria-label": ariaLabel, "data-preview-state": previewState, ...root }: MenuProps): react_jsx_runtime.JSX.Element;
132
+ interface ContextMenuProps extends PositionedProps, Omit<ComponentPropsWithoutRef<typeof ContextMenu$1.Root>, "children"> {
133
+ /**
134
+ * The right-clickable area, as the caller's own element. Its children are
135
+ * kept: the trigger renders that element rather than wrapping it.
136
+ */
137
+ trigger: ReactElement;
138
+ children: ReactNode;
139
+ }
140
+ /** Opened by right click or long press at the pointer, so `side` and `align` are not offered. */
141
+ declare function ContextMenu({ trigger, children, container, className, "aria-label": ariaLabel, "data-preview-state": previewState, ...root }: Omit<ContextMenuProps, "side" | "align" | "sideOffset">): react_jsx_runtime.JSX.Element;
142
+ /** Keyboard shortcut hint, mono and tabular; exported for hints that are not a plain string. */
143
+ declare function MenuShortcut({ className, ...props }: StyledProps<"span">): react_jsx_runtime.JSX.Element;
144
+ interface MenuItemProps extends StyledProps<typeof Menu$1.Item> {
145
+ /** Rendered as a dim mono hint on the trailing edge. Decorative. */
146
+ shortcut?: string;
147
+ /** `danger` for a destructive action (Delete, Remove). */
148
+ tone?: MenuItemTone;
149
+ "data-preview-state"?: "hover";
150
+ }
151
+ /** One action. `disabled` items are skipped by the arrow keys, not just dimmed. */
152
+ declare function MenuItem({ shortcut, tone, className, children, ...props }: MenuItemProps): react_jsx_runtime.JSX.Element;
153
+ /** Single-choice group; selection is `aria-checked` on each item, which the dot indicator draws from. */
154
+ declare function MenuRadioGroup(props: ComponentPropsWithoutRef<typeof Menu$1.RadioGroup>): react_jsx_runtime.JSX.Element;
155
+ interface MenuRadioItemProps extends StyledProps<typeof Menu$1.RadioItem> {
156
+ "data-preview-state"?: "hover";
157
+ }
158
+ /** One choice in a `MenuRadioGroup`. The dot renders only while it is checked. */
159
+ declare function MenuRadioItem({ className, children, ...props }: MenuRadioItemProps): react_jsx_runtime.JSX.Element;
160
+ /** A hairline between two groups of items. */
161
+ declare function MenuSeparator({ className, ...props }: StyledProps<typeof Menu$1.Separator>): react_jsx_runtime.JSX.Element;
162
+
163
+ type PortalContainer = ComponentPropsWithoutRef<typeof Popover$1.Portal>["container"];
164
+ interface PopoverProps extends Omit<ComponentPropsWithoutRef<typeof Popover$1.Root>, "children"> {
165
+ /** A single element. It becomes the trigger; no wrapper is added around it. */
166
+ trigger: ReactElement;
167
+ /** Arbitrary content. The popover owns none of its keys. */
168
+ children: ReactNode;
169
+ side?: "top" | "bottom" | "left" | "right";
170
+ align?: "start" | "center" | "end";
171
+ sideOffset?: number;
172
+ /** Portal target. Pass the shadow root when the trigger lives in one. */
173
+ container?: PortalContainer;
174
+ /** Names the popup for assistive tech. */
175
+ "aria-label"?: string;
176
+ /**
177
+ * What to focus on open. Defaults to Base UI's behaviour (the first tabbable
178
+ * element), which is what a rename field wants.
179
+ */
180
+ initialFocus?: ComponentPropsWithoutRef<typeof Popover$1.Popup>["initialFocus"];
181
+ className?: string;
182
+ "data-preview-state"?: PopupPreviewState;
183
+ }
184
+ declare function Popover({ trigger, children, side, align, sideOffset, container, className, initialFocus, "aria-label": ariaLabel, "data-preview-state": previewState, ...root }: PopoverProps): react_jsx_runtime.JSX.Element;
185
+
186
+ /**
187
+ * Shared field look (also used by NumberField and Select). Each interactive state is written
188
+ * twice, real and `data-[preview-state=...]`; `valueControls.test.tsx` asserts the lists match.
189
+ */
190
+ declare const fieldBase: string;
191
+ /** The text itself, inside the box. The box owns the border and the ring. */
192
+ declare const fieldText: string;
193
+ interface InputProps extends Omit<ComponentPropsWithoutRef<"input">, "value" | "onChange" | "className"> {
194
+ /** The committed value. A draft lives inside until Enter or blur. */
195
+ value: string;
196
+ /** Called with the draft on Enter and on blur, only when it really changed. */
197
+ onCommit: (next: string) => void;
198
+ /** Called once per committed change, for design-input telemetry. */
199
+ onTrack?: () => void;
200
+ /** Marks the field invalid: red boundary and `aria-invalid`. */
201
+ invalid?: boolean;
202
+ className?: string;
203
+ "data-preview-state"?: PreviewState;
204
+ }
205
+ declare function Input({ value, onCommit, onTrack, invalid, disabled, className, "data-preview-state": previewState, ...props }: InputProps): react_jsx_runtime.JSX.Element;
206
+
207
+ interface NumberFieldProps {
208
+ /** Accessible name. The inspector's row label is the usual one. */
209
+ label: string;
210
+ /** The committed value. */
211
+ value: number;
212
+ /** Called when a commit boundary produces a value different from `value`. */
213
+ onCommit: (next: number) => void;
214
+ /** Called once per committed change, for design-input telemetry. */
215
+ onTrack?: () => void;
216
+ /** Rendered beside the number: "px", "%", "deg". Never part of the text. */
217
+ unit?: string;
218
+ min?: number;
219
+ max?: number;
220
+ step?: number;
221
+ disabled?: boolean;
222
+ className?: string;
223
+ "data-preview-state"?: PreviewState;
224
+ }
225
+ declare function NumberField({ label, value, onCommit, onTrack, unit, min, max, step, disabled, className, "data-preview-state": previewState, }: NumberFieldProps): react_jsx_runtime.JSX.Element;
226
+
227
+ interface SelectOption {
228
+ label: string;
229
+ value: string;
230
+ }
231
+ interface SelectProps {
232
+ /** Accessible name for the trigger. */
233
+ label: string;
234
+ value: string;
235
+ options: SelectOption[];
236
+ /** Called when a different option is chosen. */
237
+ onCommit: (next: string) => void;
238
+ /** Called once per committed change, for design-input telemetry. */
239
+ onTrack?: () => void;
240
+ disabled?: boolean;
241
+ className?: string;
242
+ "data-preview-state"?: PreviewState;
243
+ }
244
+ declare function Select({ label, value, options, onCommit, onTrack, disabled, className, "data-preview-state": previewState, }: SelectProps): react_jsx_runtime.JSX.Element;
245
+
246
+ interface SliderProps {
247
+ /** Accessible name for the thumb. The inspector's row label is the usual one. */
248
+ label: string;
249
+ /** The committed value. */
250
+ value: number;
251
+ min: number;
252
+ max: number;
253
+ step?: number;
254
+ /** Every intermediate value while dragging or stepping. Not a commit. */
255
+ onPreview?: (next: number) => void;
256
+ /** Called at a commit boundary when the value really changed. */
257
+ onCommit: (next: number) => void;
258
+ /** Called once per commit boundary, for design-input telemetry. */
259
+ onTrack?: () => void;
260
+ disabled?: boolean;
261
+ className?: string;
262
+ "data-preview-state"?: PreviewState;
263
+ }
264
+ declare function Slider({ label, value, min, max, step, onPreview, onCommit, onTrack, disabled, className, "data-preview-state": previewState, }: SliderProps): react_jsx_runtime.JSX.Element;
265
+
266
+ interface ToggleProps {
267
+ /** Accessible name. */
268
+ label: string;
269
+ checked: boolean;
270
+ /** Called on every flip. */
271
+ onCommit: (next: boolean) => void;
272
+ /** Called once per flip, for design-input telemetry. */
273
+ onTrack?: () => void;
274
+ disabled?: boolean;
275
+ className?: string;
276
+ "data-preview-state"?: PreviewState;
277
+ }
278
+ declare function Toggle({ label, checked, onCommit, onTrack, disabled, className, "data-preview-state": previewState, }: ToggleProps): react_jsx_runtime.JSX.Element;
10
279
 
11
280
  interface PlayerProps {
12
281
  projectId?: string;
@@ -587,10 +856,6 @@ interface BeatHistoryEntry {
587
856
  }
588
857
  declare const usePlayerStore: zustand.UseBoundStore<zustand.StoreApi<PlayerState>>;
589
858
 
590
- /**
591
- * Source Patcher — Maps visual property edits back to source HTML files.
592
- * Handles inline style updates, attribute changes, and text content.
593
- */
594
859
  interface PatchOperation {
595
860
  type: "inline-style" | "attribute" | "text-content" | "html-attribute" | "rich-text";
596
861
  property: string;
@@ -705,8 +970,179 @@ declare function buildPickerAgentPrompt({ selection, userInstruction, }: {
705
970
  }): string;
706
971
  declare function buildPickerAgentContextPreview(selection: HyperframePickerElementInfo | null): string;
707
972
 
973
+ type EditHistoryKind = "manual" | "motion" | "timeline" | "source";
974
+ interface EditHistoryFileSnapshot {
975
+ before: string;
976
+ after: string;
977
+ beforeHash: string;
978
+ afterHash: string;
979
+ }
980
+ interface EditHistoryEntry {
981
+ id: string;
982
+ projectId: string;
983
+ label: string;
984
+ kind: EditHistoryKind;
985
+ coalesceKey?: string;
986
+ /** Per-entry coalesce window override (ms). Falls back to the reducer default. */
987
+ coalesceMs?: number;
988
+ createdAt: number;
989
+ files: Record<string, EditHistoryFileSnapshot>;
990
+ }
991
+ interface EditHistoryState {
992
+ version: 1;
993
+ updatedAt: number;
994
+ undo: EditHistoryEntry[];
995
+ redo: EditHistoryEntry[];
996
+ }
997
+ interface BuildEditHistoryEntryInput {
998
+ id: string;
999
+ projectId: string;
1000
+ label: string;
1001
+ kind?: EditHistoryKind;
1002
+ coalesceKey?: string;
1003
+ coalesceMs?: number;
1004
+ now: number;
1005
+ files: Record<string, {
1006
+ before: string;
1007
+ after: string;
1008
+ }>;
1009
+ }
1010
+
1011
+ interface SdkSessionPublication {
1012
+ candidate: Composition;
1013
+ expectedSession: Composition;
1014
+ targetPath: string;
1015
+ }
1016
+ type SdkSessionPublicationResult = "published" | "rejected-active-target" | "rejected-inactive-target";
1017
+ type PublishSdkSession = (publication: SdkSessionPublication) => SdkSessionPublicationResult;
1018
+
1019
+ type TimelineLayerDropPlacement = {
1020
+ type: "onto";
1021
+ layerId: string;
1022
+ } | {
1023
+ type: "between";
1024
+ beforeLayerId: string;
1025
+ afterLayerId: string;
1026
+ } | {
1027
+ type: "above";
1028
+ layerId: string;
1029
+ } | {
1030
+ type: "below";
1031
+ layerId: string;
1032
+ };
1033
+ interface TimelineStackingZIndexChange {
1034
+ key: string;
1035
+ zIndex: number;
1036
+ domId?: string;
1037
+ selector?: string;
1038
+ selectorIndex?: number;
1039
+ sourceFile?: string;
1040
+ }
1041
+ interface TimelineStackingReorderIntent {
1042
+ contextKey: string;
1043
+ placement: TimelineLayerDropPlacement;
1044
+ zIndexChanges: TimelineStackingZIndexChange[];
1045
+ }
1046
+
708
1047
  type BlockedTimelineEditIntent = "move" | "resize-start" | "resize-end";
709
1048
 
1049
+ /**
1050
+ * Durability report for a patch-batches commit. `durable === false` means the
1051
+ * server could not locate at least one patch target on disk — the preview was
1052
+ * reloaded to reconverge, and dependent follow-up writes (the z→lane timeline
1053
+ * mirror) must be skipped. `allMatched` retains the underlying match detail.
1054
+ * `changed === false` means no source file was written: every patch was a
1055
+ * byte-identical no-op, or the atomic gesture was refused before any write.
1056
+ */
1057
+ interface DomEditPatchBatchesResult {
1058
+ durable: boolean;
1059
+ allMatched: boolean;
1060
+ changed: boolean;
1061
+ }
1062
+
1063
+ /** A host's verdict on one element: editable, or blocked with a reason to show. */
1064
+ type TimelineEditPermission = true | {
1065
+ blocked: true;
1066
+ reason: string;
1067
+ };
1068
+ type CanEditTimelineElement = (element: TimelineElement) => TimelineEditPermission;
1069
+
1070
+ interface RecordEditInput$1 {
1071
+ label: string;
1072
+ kind: EditHistoryKind;
1073
+ coalesceKey?: string;
1074
+ files: Record<string, {
1075
+ before: string;
1076
+ after: string;
1077
+ }>;
1078
+ }
1079
+ type TimelineZIndexReorderCommit = (entries: Array<{
1080
+ element: HTMLElement;
1081
+ zIndex: number;
1082
+ id?: string;
1083
+ selector?: string;
1084
+ selectorIndex?: number;
1085
+ sourceFile: string;
1086
+ key?: string;
1087
+ }>, coalesceKey?: string) => Promise<DomEditPatchBatchesResult | undefined | void>;
1088
+ interface UseTimelineEditingOptions {
1089
+ projectId: string | null;
1090
+ activeCompPath: string | null;
1091
+ timelineElements: TimelineElement[];
1092
+ showToast: (message: string, tone?: "error" | "info") => void;
1093
+ writeProjectFile: (path: string, content: string, expectedContent?: string) => Promise<void>;
1094
+ observeProjectFileVersion?: (path: string, version: string | null) => void;
1095
+ recordEdit: (input: RecordEditInput$1) => Promise<void>;
1096
+ reloadPreview: () => void;
1097
+ previewIframeRef: RefObject<HTMLIFrameElement | null>;
1098
+ pendingTimelineEditPathRef: MutableRefObject<Set<string>>;
1099
+ uploadProjectFiles: (files: Iterable<File>, dir?: string) => Promise<string[]>;
1100
+ isRecordingRef?: RefObject<boolean>;
1101
+ /** Stage 7 §3.2: SDK session for routing timing ops through setTiming. */
1102
+ sdkSession?: Composition | null;
1103
+ /** Publish a fully persisted candidate SDK session. */
1104
+ publishSdkSession?: PublishSdkSession;
1105
+ /** Resync the SDK session after a server-authoritative timeline write. */
1106
+ forceReloadSdkSession?: () => void;
1107
+ /** Reparse authored animations after a timing rewrite changes their positions. */
1108
+ invalidateGsapCache?: () => void;
1109
+ handleDomZIndexReorderCommitRef?: MutableRefObject<TimelineZIndexReorderCommit | null>;
1110
+ /** Refuses a hand edit at the persist boundary when set; absent = Studio unchanged. */
1111
+ canEdit?: CanEditTimelineElement;
1112
+ }
1113
+ /** What a timeline move commits: the new start and track, plus the z-index
1114
+ * reorder a vertical drag resolves to (absent for a pure horizontal move). */
1115
+ type TimelineMoveUpdates = Pick<TimelineElement, "start" | "track"> & {
1116
+ stackingReorder?: TimelineStackingReorderIntent | null;
1117
+ };
1118
+
1119
+ interface TimelineGroupMoveChange {
1120
+ element: TimelineElement;
1121
+ start: number;
1122
+ track?: number;
1123
+ }
1124
+ interface TimelineGroupResizeChange {
1125
+ element: TimelineElement;
1126
+ start: number;
1127
+ duration: number;
1128
+ playbackStart?: number;
1129
+ }
1130
+ interface TimelineGroupCommitOptions {
1131
+ beforeTiming?: Promise<void>;
1132
+ coalesceKey?: string;
1133
+ /** Per-entry undo coalesce window override (ms) — see EditHistoryEntry.coalesceMs. */
1134
+ coalesceMs?: number;
1135
+ /** Overrides the default "Move timeline clips" undo-history label. Coalescing
1136
+ * keeps the LAST entry's label (editHistory.ts), so a mechanical follow-up
1137
+ * move folded into another gesture's coalesceKey (e.g. the ripple after a
1138
+ * delete) should carry that gesture's own label, not its own. */
1139
+ label?: string;
1140
+ /** Skips this call's own generic failure toast (the error is still logged
1141
+ * to the console) — for a caller that shows its own more specific message
1142
+ * on the same failure, so the user isn't told about one action twice. */
1143
+ suppressFailureToast?: boolean;
1144
+ }
1145
+
710
1146
  type TimelineMoveOperation = "timing" | "lane-reorder" | "track-insert";
711
1147
 
712
1148
  interface TimelineKeyframeTarget {
@@ -991,10 +1427,8 @@ type RenderClipContent = (element: TimelineElement, style: {
991
1427
  label: string;
992
1428
  }) => ReactNode;
993
1429
  interface EditorShellProps extends TimelineEditCallbackDeps {
994
- /** Left sidebar (media/library), rendered in the top row. */
995
- left: ReactNode;
996
- /** Right panel (inspector/design) or null when collapsed, in the top row. */
997
- right: ReactNode;
1430
+ /** Dock.Panel elements for every panel except the built-in preview and timeline. */
1431
+ panels: ReactNode;
998
1432
  timelineToolbar: ReactNode;
999
1433
  renderClipContent: RenderClipContent;
1000
1434
  handleTimelineElementDelete: (element: TimelineElement) => Promise<void> | void;
@@ -1018,9 +1452,13 @@ interface EditorShellProps extends TimelineEditCallbackDeps {
1018
1452
  isGestureRecording?: boolean;
1019
1453
  recordingState?: GestureRecordingState;
1020
1454
  onToggleRecording?: () => void;
1455
+ /**
1456
+ * Host layer over the preview, positioned with `usePreviewCompositionRect`. Hidden in
1457
+ * fullscreen and during a block preview; below the selection overlay past z-index 10.
1458
+ */
1021
1459
  gestureOverlay?: ReactNode;
1022
1460
  }
1023
- declare function EditorShell({ left, right, timelineToolbar, renderClipContent, handleTimelineElementDelete, handleTimelineAssetDrop, handleTimelineBlockDrop, handleTimelineCompositionDrop, handlePreviewBlockDrop, handleTimelineFileDrop, handleTimelineElementMove, handleTimelineElementsMove, handleTimelineElementResize, handleTimelineGroupResize, handleToggleTrackHidden, setAudioGroupAttribute, handleGroupClips, setElementFxAttribute, handleBlockedTimelineEdit, handleTimelineElementSplit, handleRazorSplit, handleRazorSplitAll, onCopyClip, onPasteClip, onDuplicateClip, canPasteClip, setCompIdToSrc, setCompositionLoading, shouldShowMotionPath, shouldShowSelectedDomBounds, isGestureRecording, recordingState, onToggleRecording, blockPreview, gestureOverlay, }: EditorShellProps): react_jsx_runtime.JSX.Element;
1461
+ declare function EditorShell({ panels, timelineToolbar, renderClipContent, handleTimelineElementDelete, handleTimelineAssetDrop, handleTimelineBlockDrop, handleTimelineCompositionDrop, handlePreviewBlockDrop, handleTimelineFileDrop, handleTimelineElementMove, handleTimelineElementsMove, handleTimelineElementResize, handleTimelineGroupResize, handleToggleTrackHidden, setAudioGroupAttribute, handleGroupClips, setElementFxAttribute, handleBlockedTimelineEdit, handleTimelineElementSplit, handleRazorSplit, handleRazorSplitAll, onCopyClip, onPasteClip, onDuplicateClip, canPasteClip, setCompIdToSrc, setCompositionLoading, shouldShowMotionPath, shouldShowSelectedDomBounds, isGestureRecording, recordingState, onToggleRecording, blockPreview, gestureOverlay, }: EditorShellProps): react_jsx_runtime.JSX.Element;
1024
1462
 
1025
1463
  interface NLEPreviewProps {
1026
1464
  projectId: string;
@@ -1054,6 +1492,21 @@ interface CompositionBreadcrumbProps {
1054
1492
  }
1055
1493
  declare function CompositionBreadcrumb({ stack, onNavigate }: CompositionBreadcrumbProps): react_jsx_runtime.JSX.Element | null;
1056
1494
 
1495
+ interface DomEditCompositionRect {
1496
+ left: number;
1497
+ top: number;
1498
+ width: number;
1499
+ height: number;
1500
+ scaleX: number;
1501
+ scaleY: number;
1502
+ }
1503
+
1504
+ /**
1505
+ * Where the composition sits inside `overlayRef`, in overlay pixels, plus the scale from
1506
+ * composition units to pixels. Follows the live preview iframe across reloads.
1507
+ */
1508
+ declare function usePreviewCompositionRect(overlayRef: RefObject<HTMLDivElement | null>): DomEditCompositionRect;
1509
+
1057
1510
  interface SourceEditorProps {
1058
1511
  content: string;
1059
1512
  filePath?: string;
@@ -1273,6 +1726,117 @@ interface AskAgentModalProps {
1273
1726
  }
1274
1727
  declare function AskAgentModal({ selectionLabel, contextPreview, anchorPoint, onSubmit, onClose, }: AskAgentModalProps): react_jsx_runtime.JSX.Element;
1275
1728
 
1729
+ /**
1730
+ * What the dev server knows about this machine's FFmpeg, as reported by
1731
+ * `GET /api/environment/ffmpeg`. Studio asks when the Render panel opens so a
1732
+ * missing encoder is a prompt with an install command, not a failed export an
1733
+ * hour into the work.
1734
+ */
1735
+ interface FfmpegStatus {
1736
+ ok: boolean;
1737
+ /** Short headline, e.g. "FFmpeg not found". Absent when ok. */
1738
+ title?: string;
1739
+ /** Why it cannot run, in the server's words. Absent when ok. */
1740
+ detail?: string;
1741
+ /** Prose remediation, including the manual route where one exists. */
1742
+ hint?: string;
1743
+ /** A single pasteable install command, when this platform has one. */
1744
+ command?: string;
1745
+ }
1746
+
1747
+ interface RenderJob {
1748
+ id: string;
1749
+ status: "rendering" | "complete" | "failed" | "cancelled";
1750
+ progress: number;
1751
+ stage?: string;
1752
+ error?: string;
1753
+ filename: string;
1754
+ createdAt: number;
1755
+ durationMs?: number;
1756
+ }
1757
+ type ResolutionPreset = CanvasResolution;
1758
+ interface StartRenderOptions {
1759
+ fps?: number;
1760
+ quality?: "draft" | "standard" | "high";
1761
+ format?: "mp4" | "webm" | "mov";
1762
+ /** `"auto"` (default) renders at the composition's authored dimensions. */
1763
+ resolution?: ResolutionPreset | "auto";
1764
+ /**
1765
+ * Render a specific composition file. Omit it to render the composition the
1766
+ * user currently has open — only the sidebar's per-composition Render button
1767
+ * names one, because it renders a card the user is not looking at.
1768
+ */
1769
+ composition?: string;
1770
+ /**
1771
+ * Composition-variable overrides ({variableId: value}), forwarded to the
1772
+ * render route and injected as window.__hfVariables — the same channel
1773
+ * `hyperframes render --variables` uses.
1774
+ */
1775
+ variables?: Record<string, unknown>;
1776
+ }
1777
+ declare function useRenderQueue(projectId: string | null, activeCompPathRef: {
1778
+ current: string | null;
1779
+ }): {
1780
+ jobs: RenderJob[];
1781
+ isRendering: boolean;
1782
+ loadError: string | null;
1783
+ actionError: string | null;
1784
+ dismissActionError: () => void;
1785
+ reloadRenders: () => Promise<void>;
1786
+ deleteRender: (jobId: string) => Promise<void>;
1787
+ cancelRender: (jobId: string) => Promise<void>;
1788
+ clearCompleted: () => void;
1789
+ startRender: (options: unknown) => Promise<void>;
1790
+ ffmpeg: FfmpegStatus | null;
1791
+ ffmpegMissing: boolean;
1792
+ ffmpegChecking: boolean;
1793
+ recheckFfmpeg: () => void;
1794
+ };
1795
+
1796
+ interface CompositionDimensions {
1797
+ width: number;
1798
+ height: number;
1799
+ }
1800
+ type StartRenderHandler = (format: "mp4" | "webm" | "mov", quality: "draft" | "standard" | "high", resolution: ResolutionPreset | "auto", fps: 24 | 30 | 60) => void | Promise<void>;
1801
+ interface RenderQueueProps {
1802
+ jobs: RenderJob[];
1803
+ projectId: string;
1804
+ onDelete: (jobId: string) => void;
1805
+ onCancel?: (jobId: string) => void;
1806
+ onClearCompleted: () => void;
1807
+ onStartRender: StartRenderHandler;
1808
+ isRendering: boolean;
1809
+ /** History fetch failure (null when the last load succeeded). */
1810
+ loadError?: string | null;
1811
+ /** Retry a failed history load. */
1812
+ onRetryLoad?: () => void;
1813
+ /** Failure of a delete/cancel action, shown inline until dismissed. */
1814
+ actionError?: string | null;
1815
+ onDismissActionError?: () => void;
1816
+ /**
1817
+ * Authored dimensions of the active composition. Used to pick the
1818
+ * matching preset (landscape / portrait / square) when the user selects
1819
+ * a 1080p or 4K scale. `null` falls back to landscape (legacy default).
1820
+ */
1821
+ compositionDimensions?: CompositionDimensions | null;
1822
+ /**
1823
+ * Encoder availability, owned by useRenderQueue so the panel's Export button
1824
+ * and the header's agree. `null` means "no answer", not "missing".
1825
+ */
1826
+ ffmpeg: FfmpegStatus | null;
1827
+ ffmpegChecking: boolean;
1828
+ onRecheckFfmpeg: () => void;
1829
+ }
1830
+ declare const RenderQueue: react.NamedExoticComponent<RenderQueueProps>;
1831
+
1832
+ interface PersistedRenderSettings {
1833
+ format: "mp4" | "webm" | "mov";
1834
+ quality: "draft" | "standard" | "high";
1835
+ fps: 24 | 30 | 60;
1836
+ }
1837
+ declare function getPersistedRenderSettings(): PersistedRenderSettings;
1838
+ declare function persistRenderSettings(format: PersistedRenderSettings["format"], quality: PersistedRenderSettings["quality"], fps: PersistedRenderSettings["fps"]): void;
1839
+
1276
1840
  interface PickedElement {
1277
1841
  id: string | null;
1278
1842
  tagName: string;
@@ -1348,4 +1912,236 @@ declare function findElementBlock(html: string, elementId: string): {
1348
1912
  isSelfClosing: boolean;
1349
1913
  } | null;
1350
1914
 
1351
- export { type AgentModalAnchorPoint, type AgentPromptElementInfo, AskAgentModal, type AskAgentModalProps, CompositionBreadcrumb, type CompositionLevel, CompositionThumbnail, EditorShell, FileTree, NLEPreview, type PatchOperation, type PickedElement, Player, PlayerControls, PropertyPanel, SourceEditor, StudioApp, Timeline, type TimelineElement, VideoThumbnail, applyPatch, buildPickerAgentContextPreview, buildPickerAgentPrompt, findElementBlock, formatTime, liveTime, mergeStyleIntoTag, parseStyleString, resolveIframe, resolveSourceFile, useElementPicker, usePlayerStore, useTimelinePlayer };
1915
+ interface EditHistoryStorageAdapter {
1916
+ get(projectId: string): Promise<EditHistoryState | null>;
1917
+ set(projectId: string, state: EditHistoryState): Promise<void>;
1918
+ delete(projectId: string): Promise<void>;
1919
+ }
1920
+
1921
+ interface RecordEditInput {
1922
+ label: string;
1923
+ kind: EditHistoryKind;
1924
+ coalesceKey?: string;
1925
+ coalesceMs?: number;
1926
+ files: BuildEditHistoryEntryInput["files"];
1927
+ }
1928
+ interface ApplyCallbacks {
1929
+ readFile: (path: string) => Promise<string>;
1930
+ writeFile: (path: string, content: string) => Promise<void>;
1931
+ serialize?: <T>(paths: readonly string[], task: () => Promise<T>) => Promise<T>;
1932
+ }
1933
+ interface UsePersistentEditHistoryOptions {
1934
+ projectId: string | null;
1935
+ storage?: EditHistoryStorageAdapter;
1936
+ now?: () => number;
1937
+ }
1938
+ /**
1939
+ * Per-file content the restore just applied. `restored` is the bytes written to
1940
+ * disk (the undo/redo target); `previous` is what was on disk immediately before
1941
+ * (the current live preview state). The undo preview-sync diffs these to decide
1942
+ * whether the restore is soft-reloadable (attributes/style/GSAP-script only) or
1943
+ * needs a full iframe reload.
1944
+ */
1945
+ interface ApplyRestoredFile {
1946
+ previous: string;
1947
+ restored: string;
1948
+ }
1949
+ interface ApplyResult {
1950
+ ok: boolean;
1951
+ reason?: "empty" | "content-mismatch";
1952
+ label?: string;
1953
+ paths?: string[];
1954
+ files?: Record<string, ApplyRestoredFile>;
1955
+ }
1956
+ declare function usePersistentEditHistory(options: UsePersistentEditHistoryOptions): {
1957
+ recordEdit: (input: RecordEditInput) => Promise<void>;
1958
+ undo: (callbacks: ApplyCallbacks) => Promise<ApplyResult>;
1959
+ redo: (callbacks: ApplyCallbacks) => Promise<ApplyResult>;
1960
+ canUndo: boolean;
1961
+ canRedo: boolean;
1962
+ undoLabel: string;
1963
+ redoLabel: string;
1964
+ undoPaths: string[];
1965
+ redoPaths: string[];
1966
+ state: EditHistoryState;
1967
+ loaded: boolean;
1968
+ };
1969
+
1970
+ declare function useTimelineEditing({ projectId, activeCompPath, timelineElements, showToast, writeProjectFile, observeProjectFileVersion, recordEdit, reloadPreview, previewIframeRef, pendingTimelineEditPathRef, uploadProjectFiles, isRecordingRef, sdkSession, publishSdkSession, forceReloadSdkSession, invalidateGsapCache, handleDomZIndexReorderCommitRef, canEdit, }: UseTimelineEditingOptions): {
1971
+ handleTimelineElementMove: (element: TimelineElement, updates: TimelineMoveUpdates) => Promise<void>;
1972
+ handleTimelineElementResize: (element: TimelineElement, updates: Pick<TimelineElement, "start" | "duration" | "playbackStart">) => Promise<void>;
1973
+ handleToggleTrackHidden: (track: number, hidden: boolean, displayNumber?: number | null | undefined) => Promise<void>;
1974
+ handleToggleElementHidden: (elementKey: string | readonly string[], hidden: boolean) => Promise<void>;
1975
+ handleAutoGroupCarveSources: (clipIds: readonly string[], groupId: string, groupLabel?: string | undefined) => Promise<void>;
1976
+ setAudioGroupAttribute: {
1977
+ setQuiet: (groupId: string, attr: string, value: string | null, label: string) => Promise<void>;
1978
+ setLive: (groupId: string, attr: string, value: string | null) => void;
1979
+ };
1980
+ setElementFxAttribute: {
1981
+ setQuiet: (element: TimelineElement, attr: string, value: string | null, label: string) => Promise<void>;
1982
+ setLive: (element: TimelineElement, attr: string, value: string | null) => void;
1983
+ };
1984
+ handleTimelineElementDelete: (element: TimelineElement) => Promise<void>;
1985
+ handleTimelineElementsDelete: (selection: TimelineElement[]) => Promise<void>;
1986
+ handleTimelineElementSplit: (element: TimelineElement, splitTime: number) => Promise<void>;
1987
+ handleRazorSplit: (element: TimelineElement, splitTime: number) => Promise<void>;
1988
+ handleRazorSplitAll: (splitTime: number) => Promise<void>;
1989
+ handleTimelineAssetDrop: (assetPath: string, placement: TimelineDropPlacement, durationOverride?: number | undefined) => Promise<void>;
1990
+ handleTimelineFileDrop: (files: File[], placement?: TimelineDropPlacement | undefined) => Promise<void>;
1991
+ handleTimelineCompositionDrop: (sourcePath: string, placement: Pick<TimelineElement, "track" | "start">) => Promise<void>;
1992
+ handleBlockedTimelineEdit: (element: TimelineElement) => void;
1993
+ handleTimelineGroupMove: (changes: TimelineGroupMoveChange[], options?: TimelineGroupCommitOptions | undefined) => Promise<void>;
1994
+ handleTimelineGroupResize: (changes: TimelineGroupResizeChange[], options?: TimelineGroupCommitOptions | undefined) => Promise<void>;
1995
+ };
1996
+
1997
+ declare class StudioSaveHttpError extends Error {
1998
+ readonly statusCode: number;
1999
+ readonly alreadyToasted: boolean;
2000
+ constructor(message: string, statusCode: number, options?: {
2001
+ alreadyToasted?: boolean;
2002
+ });
2003
+ }
2004
+ declare class StudioFileConflictError extends StudioSaveHttpError {
2005
+ readonly filePath: string;
2006
+ readonly currentVersion: string | null;
2007
+ readonly currentContent: string | null;
2008
+ readonly attemptedContent: string;
2009
+ constructor(input: {
2010
+ filePath: string;
2011
+ currentVersion: string | null;
2012
+ currentContent: string | null;
2013
+ attemptedContent: string;
2014
+ });
2015
+ }
2016
+ type StudioSaveDrainResult<Failure = unknown> = {
2017
+ status: "clean";
2018
+ } | {
2019
+ status: "conflict";
2020
+ error: StudioFileConflictError;
2021
+ } | {
2022
+ status: "failed";
2023
+ error: Failure;
2024
+ };
2025
+
2026
+ type StudioPendingEditsDrainResult = StudioSaveDrainResult;
2027
+ declare function flushStudioPendingEdits(): Promise<StudioPendingEditsDrainResult>;
2028
+
2029
+ interface HistoryResult {
2030
+ ok: boolean;
2031
+ reason?: string;
2032
+ label?: string;
2033
+ paths?: string[];
2034
+ /** Per-file restored/previous content, used to soft-apply the preview. */
2035
+ files?: Record<string, {
2036
+ previous: string;
2037
+ restored: string;
2038
+ }>;
2039
+ }
2040
+ interface HistoryFileCallbacks {
2041
+ readFile: (path: string) => Promise<string>;
2042
+ writeFile: (path: string, content: string) => Promise<void>;
2043
+ serialize?: <T>(paths: readonly string[], task: () => Promise<T>) => Promise<T>;
2044
+ }
2045
+ interface EditHistoryHandle {
2046
+ undo: (cb: HistoryFileCallbacks) => Promise<HistoryResult>;
2047
+ redo: (cb: HistoryFileCallbacks) => Promise<HistoryResult>;
2048
+ state: {
2049
+ undo: ReadonlyArray<{
2050
+ createdAt: number;
2051
+ }>;
2052
+ redo: ReadonlyArray<{
2053
+ createdAt: number;
2054
+ }>;
2055
+ };
2056
+ }
2057
+ interface UseEditHistoryActionsOptions {
2058
+ editHistory: Pick<EditHistoryHandle, "undo" | "redo">;
2059
+ readOptionalProjectFile: (path: string) => Promise<string>;
2060
+ readProjectFile: (path: string) => Promise<string>;
2061
+ writeProjectFile: (path: string, content: string) => Promise<void>;
2062
+ showToast: (message: string, tone?: "error" | "info") => void;
2063
+ syncHistoryPreviewAfterApply: (restore: Pick<HistoryResult, "paths" | "files">) => Promise<void>;
2064
+ waitForPendingDomEditSaves: () => Promise<void>;
2065
+ onAfterUndoRedo?: () => void;
2066
+ /** Active composition path — decides whether undo/redo must resync the SDK session. */
2067
+ activeCompPath?: string | null;
2068
+ /** Reloads the SDK session after a revert of the active comp, past the self-write suppress window. */
2069
+ forceReloadSdkSession?: () => void;
2070
+ }
2071
+ /** Applies one persisted file-history step: the single owner of undo/redo over project files. */
2072
+ declare function useEditHistoryActions({ editHistory, readOptionalProjectFile, readProjectFile, writeProjectFile, showToast, syncHistoryPreviewAfterApply, waitForPendingDomEditSaves, onAfterUndoRedo, activeCompPath, forceReloadSdkSession, }: UseEditHistoryActionsOptions): {
2073
+ undo: () => Promise<void>;
2074
+ redo: () => Promise<void>;
2075
+ };
2076
+
2077
+ interface UseProjectFileWriterOptions {
2078
+ projectId: string | null;
2079
+ }
2080
+ declare function useProjectFileWriter({ projectId }: UseProjectFileWriterOptions): {
2081
+ fileVersions: Map<string, string | null>;
2082
+ readProjectFile: (path: string) => Promise<string>;
2083
+ writeProjectFile: (path: string, content: string, expectedContent?: string) => Promise<void>;
2084
+ readOptionalProjectFile: (path: string) => Promise<string>;
2085
+ observeProjectFileVersion: (path: string, version: string | null) => void;
2086
+ };
2087
+
2088
+ type ExternalFileChangeBlockedState = {
2089
+ status: "conflict";
2090
+ generation: number;
2091
+ error: StudioFileConflictError;
2092
+ payload: unknown;
2093
+ } | {
2094
+ status: "failed";
2095
+ generation: number;
2096
+ path: string;
2097
+ error: unknown;
2098
+ payload: unknown;
2099
+ studioContent: string | null;
2100
+ recovered: boolean;
2101
+ };
2102
+ interface ExternalFileChangeCoordinatorHandle {
2103
+ blocked: ExternalFileChangeBlockedState | null;
2104
+ retry: () => Promise<void>;
2105
+ useExternalFile: () => Promise<void>;
2106
+ keepStudioFile: () => Promise<void>;
2107
+ }
2108
+
2109
+ declare function ExternalFileConflictBanner({ coordinator, }: {
2110
+ coordinator: ExternalFileChangeCoordinatorHandle;
2111
+ }): react_jsx_runtime.JSX.Element | null;
2112
+
2113
+ interface TimelinePaneProps {
2114
+ /** Slot rendered above the timeline tracks (toolbar with split, delete, zoom) */
2115
+ timelineToolbar?: ReactNode;
2116
+ /** Slot rendered below the timeline tracks */
2117
+ timelineFooter?: ReactNode;
2118
+ /** Slot for a host's own overlay spanning the whole timeline area (e.g. a draft/ghost
2119
+ * clip), positioned absolutely the same way PreviewPane's previewOverlay is. */
2120
+ timelineOverlay?: ReactNode;
2121
+ /** Custom clip content renderer for timeline (thumbnails, waveforms, etc.) */
2122
+ renderClipContent?: (element: TimelineElement, style: {
2123
+ clip: string;
2124
+ label: string;
2125
+ }) => ReactNode;
2126
+ onFileDrop?: (files: File[], placement?: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
2127
+ onDeleteElement?: (element: TimelineElement) => Promise<void> | void;
2128
+ onAssetDrop?: (assetPath: string, placement: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
2129
+ onBlockDrop?: (blockName: string, placement: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
2130
+ onCompositionDrop?: (sourcePath: string, placement: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
2131
+ onBlockedEditAttempt?: (element: TimelineElement, intent: BlockedTimelineEditIntent) => void;
2132
+ onSelectTimelineElement?: (element: TimelineElement | null) => void;
2133
+ /** Copy/paste/duplicate act on the store's own selection, not a passed
2134
+ * element, so unlike onDeleteElement they need no composition-basis wrapper. */
2135
+ onCopyClip?: () => boolean;
2136
+ onPasteClip?: () => Promise<void>;
2137
+ onDuplicateClip?: () => Promise<boolean>;
2138
+ canPasteClip?: () => boolean;
2139
+ }
2140
+ declare function TimelinePane({ timelineToolbar, timelineFooter, timelineOverlay, renderClipContent, onFileDrop, onDeleteElement, onAssetDrop, onBlockDrop, onCompositionDrop, onBlockedEditAttempt, onSelectTimelineElement, onCopyClip, onPasteClip, onDuplicateClip, canPasteClip, }: TimelinePaneProps): react_jsx_runtime.JSX.Element;
2141
+
2142
+ declare function TimelineEditProvider({ value, children, }: {
2143
+ value: TimelineEditCallbacks;
2144
+ children: ReactNode;
2145
+ }): react_jsx_runtime.JSX.Element;
2146
+
2147
+ export { type AgentModalAnchorPoint, type AgentPromptElementInfo, AskAgentModal, type AskAgentModalProps, type BlockedTimelineEditIntent, Button, type ButtonSize, type ButtonVariant, type CanEditTimelineElement, CompositionBreadcrumb, type CompositionDimensions, type CompositionLevel, CompositionThumbnail, ContextMenu, type EditHistoryHandle, EditorShell, type EditorShellProps, type ExternalFileChangeBlockedState, type ExternalFileChangeCoordinatorHandle, ExternalFileConflictBanner, type FfmpegStatus, FileTree, IconButton, Input, type InputProps, Menu, MenuItem, type MenuItemTone, MenuRadioGroup, MenuRadioItem, MenuSeparator, MenuShortcut, NLEPreview, NumberField, type NumberFieldProps, type PatchOperation, type PersistedRenderSettings, type PickedElement, Player, PlayerControls, Popover, type PopupPreviewState, type DomEditCompositionRect as PreviewCompositionRect, type PreviewState, PropertyPanel, type RenderJob, RenderQueue, type RenderQueueProps, type ResolutionPreset, Select, type SelectOption, type SelectProps, Slider, type SliderProps, SourceEditor, type StartRenderOptions, StudioApp, StudioFileConflictError, type StudioPendingEditsDrainResult, Tab, TabPanel, Tabs, TabsList, Timeline, type TimelineEditCallbacks, type TimelineEditPermission, TimelineEditProvider, type TimelineElement, TimelinePane, type TimelinePaneProps, Toggle, type ToggleProps, Tooltip, type UseEditHistoryActionsOptions, type UsePersistentEditHistoryOptions, type UseProjectFileWriterOptions, type UseTimelineEditingOptions, VideoThumbnail, applyPatch, buildPickerAgentContextPreview, buildPickerAgentPrompt, buttonBase, buttonVariants, cn, fieldBase, fieldText, findElementBlock, flushStudioPendingEdits, formatTime, getPersistedRenderSettings, liveTime, mergeStyleIntoTag, parseStyleString, persistRenderSettings, popupSurface, resolveIframe, resolveSourceFile, useEditHistoryActions, useElementPicker, usePersistentEditHistory, usePlayerStore, usePreviewCompositionRect, useProjectFileWriter, useRenderQueue, useTimelineEditing, useTimelinePlayer };