@dailies-studio/shot-editor 0.1.0 → 0.2.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 (432) hide show
  1. package/dist/.vite/manifest.json +382 -0
  2. package/dist/assets/AgentPanel-CQejLbiX.js +2 -0
  3. package/dist/assets/AgentPanel-DJEphah0.css +1 -0
  4. package/dist/assets/AgentQuestionDialog-BqkD0kXF.js +1 -0
  5. package/dist/assets/CodeStudio-Dv4ihHpz.js +23 -0
  6. package/dist/assets/CommentsPanel-B4yhhT1T.js +1 -0
  7. package/dist/assets/ComponentLibraryRail-Ch_dsAru.js +1 -0
  8. package/dist/assets/ComponentStage-BRhRotyH.js +1 -0
  9. package/dist/assets/CurvePane-BjFVabKX.css +1 -0
  10. package/dist/assets/CurvePane-R22WzllJ.js +1 -0
  11. package/dist/assets/ExportDialog-CWLQWTmG.js +2 -0
  12. package/dist/assets/FontPicker-DWKaYL7F.js +1 -0
  13. package/dist/assets/FontPicker-v0IOIArM.css +1 -0
  14. package/dist/assets/GenerateDrawer-Bh3SqDKt.js +2 -0
  15. package/dist/assets/InsertDrawer-Ce1fod1L.js +1 -0
  16. package/dist/assets/LibraryBrowser-D9sEGz5w.js +2 -0
  17. package/dist/assets/MakeItYoursSection-CMO4319_.js +2 -0
  18. package/dist/assets/MaterialGraphCanvas-BFzl4wWR.js +23 -0
  19. package/dist/assets/MaterialGraphCanvas-BIUD59ik.css +1 -0
  20. package/dist/assets/MaterialWorkspace-CqTWqaRA.js +14 -0
  21. package/dist/assets/PreferencesDialog-CBf9_OoD.js +1 -0
  22. package/dist/assets/PresetsSection-CUEaoCVU.js +1 -0
  23. package/dist/assets/SoundHistory-BeS8EW1h.js +1 -0
  24. package/dist/assets/SoundMode-BmcZru3i.js +3 -0
  25. package/dist/assets/StockSounds-DdAUGRYK.js +1 -0
  26. package/dist/assets/TemplateFieldSection-CaeXpLFo.js +1 -0
  27. package/dist/assets/VersionHistoryPanel-CxG6XhNP.js +1 -0
  28. package/dist/assets/board-B88EPuLO.css +1 -0
  29. package/dist/assets/board-CL-he7SL.js +3 -0
  30. package/dist/assets/components-0oOSPN0l.js +1 -0
  31. package/dist/assets/googleFontRanges-gp32wJMo.js +1 -0
  32. package/dist/assets/googleFonts-BNon6M7X.js +1 -0
  33. package/dist/assets/iconsAndAliases-DL7gXP5j.js +1863 -0
  34. package/dist/assets/index-4F5NQmI8.css +1 -0
  35. package/dist/assets/index-BGHQGV5z.js +117 -0
  36. package/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  37. package/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  38. package/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  39. package/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  40. package/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  41. package/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  42. package/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  43. package/dist/assets/libraryModel-CpIwfFha.js +1 -0
  44. package/dist/assets/lucide-6iK64tTB.js +9319 -0
  45. package/dist/assets/motionStamp-CH-Hew4E.js +1 -0
  46. package/dist/assets/takeMeasure-Tdg0TxEi.js +1 -0
  47. package/dist/assets/webmcp-BsB9gxWC.js +2 -0
  48. package/dist/index.html +34 -3
  49. package/package.json +12 -9
  50. package/src/addFamilies.ts +66 -0
  51. package/src/api.ts +1631 -55
  52. package/src/app/App.tsx +977 -198
  53. package/src/app/lazyPanel.tsx +137 -0
  54. package/src/app/lazySurfaces.tsx +203 -0
  55. package/src/audio/index.tsx +41 -10
  56. package/src/audio/meter.ts +485 -0
  57. package/src/audio/meterMath.ts +273 -0
  58. package/src/audio/meterStore.ts +155 -0
  59. package/src/audio/offlineMix.ts +289 -0
  60. package/src/audio/preview.ts +928 -118
  61. package/src/bridge.ts +15 -5
  62. package/src/bridgeTypes.ts +1611 -36
  63. package/src/channels.ts +17 -32
  64. package/src/fonts/googleFonts.ts +4 -0
  65. package/src/hostApi.ts +174 -0
  66. package/src/icons.tsx +770 -0
  67. package/src/layerWords.ts +61 -0
  68. package/src/main.tsx +56 -4
  69. package/src/panels/ResizeHandle.tsx +33 -43
  70. package/src/panels/agent/AgentDots.tsx +36 -0
  71. package/src/panels/agent/AgentPanel.tsx +119 -43
  72. package/src/panels/agent/AgentQuestionDialog.tsx +202 -0
  73. package/src/panels/agent/agent.css +113 -97
  74. package/src/panels/agent/agentGate.ts +35 -0
  75. package/src/panels/code/CodeDefaultsPanel.tsx +345 -0
  76. package/src/panels/code/CodeStudio.tsx +373 -0
  77. package/src/panels/code/FileTree.tsx +265 -0
  78. package/src/panels/code/codeBufferGuard.ts +52 -0
  79. package/src/panels/code/codeParts.tsx +321 -0
  80. package/src/panels/comments/CommentComposer.tsx +239 -0
  81. package/src/panels/comments/CommentThread.tsx +346 -0
  82. package/src/panels/comments/CommentsPanel.tsx +336 -0
  83. package/src/panels/comments/InboxButton.tsx +131 -0
  84. package/src/panels/comments/MentionMenu.tsx +98 -0
  85. package/src/panels/comments/parts.tsx +171 -0
  86. package/src/panels/export/ExportDialog.tsx +919 -96
  87. package/src/panels/export/ExportScrim.tsx +57 -0
  88. package/src/panels/files/AssetsTab.tsx +656 -0
  89. package/src/panels/files/FilePanel.tsx +1466 -216
  90. package/src/panels/files/LayerMarks.tsx +40 -0
  91. package/src/panels/files/LeftColumn.tsx +37 -0
  92. package/src/panels/files/McpSection.tsx +690 -47
  93. package/src/panels/files/MissingAssetsNotice.tsx +60 -0
  94. package/src/panels/files/assetDrag.ts +79 -0
  95. package/src/panels/files/resourceParts.tsx +255 -0
  96. package/src/panels/fonts/FontPicker.tsx +762 -0
  97. package/src/panels/fonts/faceCache.ts +207 -0
  98. package/src/panels/fonts/fontPicker.css +271 -0
  99. package/src/panels/fonts/pickerModel.ts +265 -0
  100. package/src/panels/fonts/pickerRequest.ts +46 -0
  101. package/src/panels/header/AccountAvatar.tsx +76 -0
  102. package/src/panels/header/AgentChip.tsx +77 -0
  103. package/src/panels/header/AppMenu.tsx +213 -0
  104. package/src/panels/header/Header.tsx +564 -84
  105. package/src/panels/header/HistoryControls.tsx +31 -5
  106. package/src/panels/header/PresenceStack.tsx +329 -0
  107. package/src/panels/header/ProjectTitle.tsx +232 -0
  108. package/src/panels/header/ShareButton.tsx +59 -0
  109. package/src/panels/header/StatusBadges.tsx +358 -27
  110. package/src/panels/header/SyncDeletedDialog.tsx +88 -0
  111. package/src/panels/header/ZoomControls.tsx +46 -15
  112. package/src/panels/header/agentHandoff.ts +52 -0
  113. package/src/panels/header/leave.ts +26 -0
  114. package/src/panels/help/HelpSection.tsx +189 -163
  115. package/src/panels/insert/InsertDrawer.tsx +723 -0
  116. package/src/panels/insert/iconSet.ts +109 -0
  117. package/src/panels/inspector/ExposeDot.tsx +117 -0
  118. package/src/panels/inspector/FramingRows.tsx +314 -44
  119. package/src/panels/inspector/InspectorFooter.tsx +91 -0
  120. package/src/panels/inspector/InspectorHead.tsx +425 -0
  121. package/src/panels/inspector/InspectorPanel.tsx +408 -124
  122. package/src/panels/inspector/KeyframeDot.tsx +373 -0
  123. package/src/panels/inspector/KeyframeSection.tsx +257 -0
  124. package/src/panels/inspector/MarkerSection.tsx +55 -11
  125. package/src/panels/inspector/ParamRow.tsx +84 -53
  126. package/src/panels/inspector/RightColumn.tsx +152 -0
  127. package/src/panels/inspector/TimeSection.tsx +866 -0
  128. package/src/panels/inspector/animate/EaseControl.tsx +244 -0
  129. package/src/panels/inspector/animate/PresetsSection.tsx +135 -0
  130. package/src/panels/inspector/animate/ShotSection.tsx +562 -48
  131. package/src/panels/inspector/design/AlignRow.tsx +95 -31
  132. package/src/panels/inspector/design/AppearanceSection.tsx +118 -9
  133. package/src/panels/inspector/design/ClonerSection.tsx +323 -62
  134. package/src/panels/inspector/design/ComponentSection.tsx +50 -43
  135. package/src/panels/inspector/design/CornerSection.tsx +45 -14
  136. package/src/panels/inspector/design/DefinitionProps.tsx +315 -0
  137. package/src/panels/inspector/design/DefinitionSection.tsx +136 -0
  138. package/src/panels/inspector/design/DeviceSection.tsx +210 -0
  139. package/src/panels/inspector/design/FillSection.tsx +83 -51
  140. package/src/panels/inspector/design/FontField.tsx +196 -0
  141. package/src/panels/inspector/design/HtmlSection.tsx +23 -12
  142. package/src/panels/inspector/design/InstanceSection.tsx +373 -154
  143. package/src/panels/inspector/design/MaskSection.tsx +84 -0
  144. package/src/panels/inspector/design/MaterialSection.tsx +177 -0
  145. package/src/panels/inspector/design/MediaSection.tsx +273 -120
  146. package/src/panels/inspector/design/MeshSection.tsx +910 -82
  147. package/src/panels/inspector/design/PaintRows.tsx +550 -0
  148. package/src/panels/inspector/design/PlateStyleSection.tsx +177 -0
  149. package/src/panels/inspector/design/ShadowSection.tsx +221 -48
  150. package/src/panels/inspector/design/SplineFollowSection.tsx +235 -0
  151. package/src/panels/inspector/design/StrokeSection.tsx +228 -43
  152. package/src/panels/inspector/design/TemplateFieldSection.tsx +57 -0
  153. package/src/panels/inspector/design/TransformSection.tsx +244 -50
  154. package/src/panels/inspector/design/TypographySection.tsx +679 -110
  155. package/src/panels/inspector/layout.ts +295 -0
  156. package/src/panels/inspector/propValue.ts +199 -0
  157. package/src/panels/inspector/scene/CameraSection.tsx +232 -22
  158. package/src/panels/inspector/scene/EffectsSection.tsx +722 -71
  159. package/src/panels/inspector/scene/MakeItYoursSection.tsx +180 -0
  160. package/src/panels/inspector/scene/SceneDocSection.tsx +191 -0
  161. package/src/panels/inspector/scene/SceneSection.tsx +219 -44
  162. package/src/panels/inspector/scene/TransitionSection.tsx +523 -72
  163. package/src/panels/inspector/storyboard/StoryboardReferenceSection.tsx +593 -0
  164. package/src/panels/inspector/storyboard/VideoStill.tsx +120 -0
  165. package/src/panels/inspector/timeModel.ts +190 -0
  166. package/src/panels/inspector/useDocWrite.ts +438 -146
  167. package/src/panels/library/LibraryBrowser.tsx +435 -0
  168. package/src/panels/library/LibraryGrid.tsx +190 -0
  169. package/src/panels/library/LibraryPreviewPanel.tsx +349 -0
  170. package/src/panels/library/LibraryRail.tsx +84 -0
  171. package/src/panels/library/LibrarySoundPanel.tsx +293 -0
  172. package/src/panels/library/SoundHistory.tsx +245 -0
  173. package/src/panels/library/StockSounds.tsx +390 -0
  174. package/src/panels/library/libraryModel.ts +147 -0
  175. package/src/panels/library/stockSearch.ts +77 -0
  176. package/src/panels/maskMarks.tsx +117 -0
  177. package/src/panels/material/MaterialCommentNode.tsx +54 -0
  178. package/src/panels/material/MaterialGraphCanvas.tsx +861 -0
  179. package/src/panels/material/MaterialGraphMenu.tsx +45 -0
  180. package/src/panels/material/MaterialGroupNode.tsx +56 -0
  181. package/src/panels/material/MaterialLibrary.tsx +253 -0
  182. package/src/panels/material/MaterialNode.tsx +453 -0
  183. package/src/panels/material/MaterialNodePalette.tsx +349 -0
  184. package/src/panels/material/MaterialParameterControls.tsx +525 -0
  185. package/src/panels/material/MaterialRerouteNode.tsx +42 -0
  186. package/src/panels/material/MaterialWorkspace.tsx +501 -0
  187. package/src/panels/material/NodePreview.tsx +70 -0
  188. package/src/panels/material/flowModel.ts +441 -0
  189. package/src/panels/material/parameterValues.ts +231 -0
  190. package/src/panels/material/previewScheduler.ts +235 -0
  191. package/src/panels/material/reactFlow.css +6 -0
  192. package/src/panels/material/socketGlyph.ts +85 -0
  193. package/src/panels/material/viewportMemory.ts +58 -0
  194. package/src/panels/preferences/PreferencesDialog.tsx +215 -0
  195. package/src/panels/share/ShareComponentDialog.tsx +247 -0
  196. package/src/panels/stage/StageMenu.tsx +35 -5
  197. package/src/panels/stage/StagePanel.tsx +338 -23
  198. package/src/panels/stage/cellSlots.tsx +70 -0
  199. package/src/panels/storyboard/BoardCanvas.tsx +739 -0
  200. package/src/panels/storyboard/BoardCursors.tsx +158 -0
  201. package/src/panels/storyboard/BoardDropDialog.tsx +161 -0
  202. package/src/panels/storyboard/BoardPlan.tsx +42 -0
  203. package/src/panels/storyboard/ComponentSheet.tsx +187 -0
  204. package/src/panels/storyboard/ComponentStage.tsx +235 -0
  205. package/src/panels/storyboard/MiniSketch.tsx +173 -0
  206. package/src/panels/storyboard/PartPicker.tsx +64 -0
  207. package/src/panels/storyboard/PlanPanel.tsx +617 -0
  208. package/src/panels/storyboard/ReferencePreview.tsx +217 -0
  209. package/src/panels/storyboard/ShotBrief.tsx +360 -0
  210. package/src/panels/storyboard/ShotCard.tsx +251 -0
  211. package/src/panels/storyboard/StagePreview.tsx +348 -0
  212. package/src/panels/storyboard/StillThumbs.tsx +162 -0
  213. package/src/panels/storyboard/StoryboardView.tsx +79 -0
  214. package/src/panels/storyboard/board.css +1065 -0
  215. package/src/panels/storyboard/board.ts +12 -0
  216. package/src/panels/storyboard/boardClipboard.ts +111 -0
  217. package/src/panels/storyboard/boardOrder.ts +68 -0
  218. package/src/panels/storyboard/boardRows.tsx +425 -0
  219. package/src/panels/storyboard/liveCap.ts +90 -0
  220. package/src/panels/storyboard/plan.css +295 -0
  221. package/src/panels/storyboard/posterCache.ts +133 -0
  222. package/src/panels/storyboard/shotboard/ComponentPicker.tsx +373 -0
  223. package/src/panels/storyboard/shotboard/ImagePicker.tsx +145 -0
  224. package/src/panels/storyboard/shotboard/ShotBoard.tsx +292 -0
  225. package/src/panels/storyboard/shotboard/SketchCanvas.tsx +443 -0
  226. package/src/panels/storyboard/shotboard/SketchInspector.tsx +471 -0
  227. package/src/panels/storyboard/shotboard/SketchItem.tsx +240 -0
  228. package/src/panels/storyboard/shotboard/SketchLayers.tsx +130 -0
  229. package/src/panels/storyboard/shotboard/SketchPlay.tsx +108 -0
  230. package/src/panels/storyboard/shotboard/SketchStrip.tsx +178 -0
  231. package/src/panels/storyboard/shotboard/SketchToolFooter.tsx +99 -0
  232. package/src/panels/storyboard/shotboard/sketch.css +1392 -0
  233. package/src/panels/storyboard/shotboard/sketchModel.ts +219 -0
  234. package/src/panels/storyboard/storyboardDrop.ts +164 -0
  235. package/src/panels/studio/ComponentLibraryRail.tsx +346 -0
  236. package/src/panels/studio/ComponentSidePanel.tsx +355 -0
  237. package/src/panels/studio/ComponentStudio.tsx +243 -0
  238. package/src/panels/studio/ComponentsHome.tsx +318 -0
  239. package/src/panels/studio/ComponentsPreview.tsx +179 -0
  240. package/src/panels/studio/EveryUse.tsx +244 -0
  241. package/src/panels/studio/ThisUsePanel.tsx +679 -0
  242. package/src/panels/studio/componentParts.tsx +341 -0
  243. package/src/panels/studio/components.ts +6 -0
  244. package/src/panels/studio/newCodeComponent.ts +66 -0
  245. package/src/panels/timeline/AudioFxButton.tsx +290 -0
  246. package/src/panels/timeline/AudioLane.tsx +674 -85
  247. package/src/panels/timeline/BlockRow.tsx +154 -85
  248. package/src/panels/timeline/ChannelRow.tsx +378 -129
  249. package/src/panels/timeline/CurvePane.tsx +508 -0
  250. package/src/panels/timeline/Filmstrip.tsx +361 -0
  251. package/src/panels/timeline/GenerateDrawer.tsx +407 -0
  252. package/src/panels/timeline/KeyStretch.tsx +115 -0
  253. package/src/panels/timeline/LifespanRow.tsx +730 -47
  254. package/src/panels/timeline/MarkerLane.tsx +46 -8
  255. package/src/panels/timeline/PlayButton.tsx +91 -0
  256. package/src/panels/timeline/PropertyPicker.tsx +254 -0
  257. package/src/panels/timeline/ShotRow.tsx +400 -0
  258. package/src/panels/timeline/SoundMode.tsx +1406 -0
  259. package/src/panels/timeline/TimelineDock.tsx +2338 -272
  260. package/src/panels/timeline/TransportVolume.tsx +163 -0
  261. package/src/panels/timeline/beatRuler.ts +74 -0
  262. package/src/panels/timeline/curveModel.ts +500 -0
  263. package/src/panels/timeline/duckUnderVoice.ts +68 -0
  264. package/src/panels/timeline/keyframeMenu.ts +138 -0
  265. package/src/panels/timeline/labelWidth.ts +49 -0
  266. package/src/panels/timeline/marquee.ts +155 -0
  267. package/src/panels/timeline/model.ts +1860 -199
  268. package/src/panels/timeline/snap.ts +125 -43
  269. package/src/panels/timeline/staggerPreview.tsx +92 -0
  270. package/src/panels/timeline/takeMeasure.ts +72 -0
  271. package/src/panels/timeline/useLive.ts +48 -0
  272. package/src/panels/timeline/viewport.ts +234 -0
  273. package/src/panels/toolbar/BackChip.tsx +116 -0
  274. package/src/panels/toolbar/CameraViewBar.tsx +118 -49
  275. package/src/panels/toolbar/CanvasBar.tsx +117 -0
  276. package/src/panels/toolbar/InsertButton.tsx +38 -0
  277. package/src/panels/toolbar/IsolationTrail.tsx +156 -0
  278. package/src/panels/toolbar/PanelToggles.tsx +86 -0
  279. package/src/panels/toolbar/ShadingBar.tsx +119 -26
  280. package/src/panels/toolbar/ToolDockBar.tsx +329 -80
  281. package/src/panels/toolbar/ToolFooter.tsx +102 -0
  282. package/src/panels/toolbar/ViewModeBar.tsx +247 -38
  283. package/src/panels/toolbar/WorkPlaneBar.tsx +94 -0
  284. package/src/panels/toolbar/useAddRows.tsx +326 -0
  285. package/src/panels/toolbar/useDismiss.ts +122 -0
  286. package/src/panels/versions/VersionHistoryPanel.tsx +274 -0
  287. package/src/panels/versions/versionModel.ts +103 -0
  288. package/src/panels/virtualWindow.ts +271 -0
  289. package/src/shotTokens.css +151 -60
  290. package/src/stage/FrustumGizmo.tsx +504 -84
  291. package/src/stage/GridPlane.tsx +225 -16
  292. package/src/stage/NodePreviews.tsx +628 -0
  293. package/src/stage/PerfHud.tsx +437 -47
  294. package/src/stage/SplitRenderer.tsx +816 -169
  295. package/src/stage/StageApp.tsx +4274 -486
  296. package/src/stage/StageRootBoundary.tsx +114 -0
  297. package/src/stage/StageTools.tsx +651 -151
  298. package/src/stage/TransformGizmo.tsx +696 -0
  299. package/src/stage/adaptivePresentation.ts +145 -0
  300. package/src/stage/bridgeClient.ts +2522 -41
  301. package/src/stage/exportWorker.ts +9 -0
  302. package/src/stage/feed.ts +305 -14
  303. package/src/stage/fontPreview.ts +176 -0
  304. package/src/stage/frustumGeometry.ts +319 -1
  305. package/src/stage/furnitureTokens.ts +33 -0
  306. package/src/stage/gizmoMath.ts +508 -16
  307. package/src/stage/gpuTimer.ts +276 -0
  308. package/src/stage/instanceModules.ts +194 -33
  309. package/src/stage/interactions/move.ts +24 -5
  310. package/src/stage/isolation.ts +291 -33
  311. package/src/stage/jsSelfProfiler.ts +325 -0
  312. package/src/stage/main.tsx +116 -16
  313. package/src/stage/modalState.ts +222 -23
  314. package/src/stage/moduleEpoch.ts +60 -0
  315. package/src/stage/motionPathStage.ts +279 -0
  316. package/src/stage/motionPreview.ts +74 -0
  317. package/src/stage/overlays/Overlays.tsx +1112 -159
  318. package/src/stage/overlays/Rulers.tsx +495 -0
  319. package/src/stage/panes.ts +159 -10
  320. package/src/stage/project.ts +68 -3
  321. package/src/stage/renderScaleController.ts +730 -0
  322. package/src/stage/rulers.ts +129 -0
  323. package/src/stage/sceneExtent.ts +75 -0
  324. package/src/stage/shading.ts +20 -0
  325. package/src/stage/splineStage.ts +632 -0
  326. package/src/stage/stagePerformanceTelemetry.ts +717 -0
  327. package/src/stage/stageTheme.ts +36 -0
  328. package/src/stage/stageWarmup.ts +11 -0
  329. package/src/stage/textAutoSize.ts +124 -0
  330. package/src/stage/tier1Export.ts +641 -0
  331. package/src/stage/tokenColor.ts +14 -9
  332. package/src/stage/view.ts +56 -9
  333. package/src/stage/workingView.ts +277 -33
  334. package/src/state/accountPrefs.ts +261 -0
  335. package/src/state/agentActions.ts +173 -15
  336. package/src/state/agentActivity.ts +198 -0
  337. package/src/state/agentQuestion.ts +239 -0
  338. package/src/state/bootOpen.ts +42 -0
  339. package/src/state/cameraVerbs.ts +139 -0
  340. package/src/state/clientId.ts +6 -1
  341. package/src/state/comments.ts +1099 -0
  342. package/src/state/context.tsx +19 -1
  343. package/src/state/editorPrefs.ts +220 -0
  344. package/src/state/errorReports.ts +121 -0
  345. package/src/state/fieldWrite.ts +161 -0
  346. package/src/state/fontRecents.ts +70 -0
  347. package/src/state/fonts.ts +656 -0
  348. package/src/state/graphClipboard.ts +414 -0
  349. package/src/state/imageThumbs.ts +216 -0
  350. package/src/state/insert.ts +444 -67
  351. package/src/state/isolationTrail.ts +92 -0
  352. package/src/state/keyMoves.ts +134 -0
  353. package/src/state/keyframeClipboard.ts +177 -0
  354. package/src/state/keymap.ts +1153 -117
  355. package/src/state/layoutPrefs.ts +272 -0
  356. package/src/state/layouts.ts +205 -0
  357. package/src/state/measureMedia.ts +167 -0
  358. package/src/state/menu.ts +2341 -28
  359. package/src/state/motionStamp.ts +7 -0
  360. package/src/state/panelSize.ts +375 -5
  361. package/src/state/pendingChanges.ts +119 -0
  362. package/src/state/playHold.ts +78 -0
  363. package/src/state/playbackPreparation.ts +562 -0
  364. package/src/state/posterCapture.ts +242 -0
  365. package/src/state/presence.ts +254 -0
  366. package/src/state/projectComponents.ts +160 -0
  367. package/src/state/renderEvents.ts +298 -18
  368. package/src/state/runtimeErrors.ts +46 -0
  369. package/src/state/selection.ts +12 -0
  370. package/src/state/shortcuts.ts +1606 -0
  371. package/src/state/sketchGuide.ts +51 -0
  372. package/src/state/splineRefs.ts +87 -0
  373. package/src/state/stageHealth.ts +259 -0
  374. package/src/state/stageWatchdog.ts +47 -0
  375. package/src/state/stillStage.ts +330 -0
  376. package/src/state/stillThumbs.ts +474 -0
  377. package/src/state/store.ts +23591 -2982
  378. package/src/state/storyboardModel.ts +528 -0
  379. package/src/state/sync.ts +1257 -0
  380. package/src/state/syncCache.ts +159 -0
  381. package/src/state/triangles.ts +13 -0
  382. package/src/state/types.ts +1698 -55
  383. package/src/state/unsavedGuard.ts +49 -0
  384. package/src/state/viewMode.ts +200 -0
  385. package/src/state/webmcp.ts +1128 -0
  386. package/src/state/workspace.ts +1147 -0
  387. package/src/styles/assets-tab.css +304 -0
  388. package/src/styles/canvas.css +545 -0
  389. package/src/styles/comments.css +1435 -0
  390. package/src/styles/controls.css +162 -0
  391. package/src/styles/dialogs.css +390 -0
  392. package/src/styles/insert-drawer.css +586 -0
  393. package/src/styles/inspector-board.css +207 -0
  394. package/src/styles/inspector-object.css +486 -0
  395. package/src/styles/inspector-shot.css +252 -0
  396. package/src/styles/inspector.css +360 -0
  397. package/src/styles/left-column.css +1628 -0
  398. package/src/styles/library-preview.css +575 -0
  399. package/src/styles/library.css +512 -0
  400. package/src/styles/modes/code.css +569 -0
  401. package/src/styles/modes/component.css +1561 -0
  402. package/src/styles/modes/material.css +1201 -0
  403. package/src/styles/modes/storyboard.css +518 -0
  404. package/src/styles/share-component.css +49 -0
  405. package/src/styles/shell.css +653 -0
  406. package/src/styles/shot-brief.css +360 -0
  407. package/src/styles/states.css +76 -0
  408. package/src/styles/sync.css +380 -0
  409. package/src/styles/timeline-graph.css +384 -0
  410. package/src/styles/timeline-meters.css +89 -0
  411. package/src/styles/timeline-rows.css +1959 -0
  412. package/src/styles/timeline-sound.css +1003 -0
  413. package/src/styles/timeline.css +754 -0
  414. package/src/styles/tool-footer.css +525 -0
  415. package/src/styles/topbar.css +886 -0
  416. package/src/styles/versions.css +262 -0
  417. package/src/styles.css +72 -2921
  418. package/src/tools.ts +108 -23
  419. package/dist/assets/index-B7notge7.css +0 -1
  420. package/dist/assets/index-BBE6Ta9L.js +0 -52
  421. package/src/panels/files/icons.tsx +0 -118
  422. package/src/panels/header/DocSwitcher.tsx +0 -85
  423. package/src/panels/header/TransportBar.tsx +0 -86
  424. package/src/panels/inspector/animate/BlockTiming.tsx +0 -341
  425. package/src/panels/inspector/animate/DuringBlocks.tsx +0 -85
  426. package/src/panels/inspector/animate/InOutPresets.tsx +0 -57
  427. package/src/panels/inspector/animate/ResolvedChannels.tsx +0 -150
  428. package/src/panels/inspector/animate/useBlocks.ts +0 -43
  429. package/src/panels/material/MaterialPicker.tsx +0 -172
  430. package/src/panels/timeline/SceneStrip.tsx +0 -145
  431. package/src/panels/toolbar/AddMenu.tsx +0 -230
  432. package/src/stage/MoveGizmo.tsx +0 -411
@@ -12,7 +12,7 @@
12
12
  // It renders no conditional siblings, holds no error state and takes no
13
13
  // children, because it is the shell's backdrop: `App` mounts it as the
14
14
  // unconditional, unkeyed FIRST child of the shell root and the CSS pins it
15
- // `position: fixed; inset: 0` behind the floating panels. Anything conditional
15
+ // `position: fixed; inset: 0` behind the docked panels. Anything conditional
16
16
  // living in here would share a parent with the iframe, and the whole point of
17
17
  // the arrangement is that no shell state can reorder, reparent or remount it.
18
18
  //
@@ -25,25 +25,86 @@
25
25
  // The connect FAILURE therefore reports upward, into the shell's overlay strip,
26
26
  // rather than rendering a sibling strip here.
27
27
  //
28
+ // HOSTED, THE STAGE WAITS FOR THE CODE HOST (hostApi.ts). No Vite serves the
29
+ // project's modules there: a Service Worker does, from what the host compiled.
30
+ // So the iframe gets its src only once `__dailiesHost.ready` settles (the
31
+ // element itself is mounted from the start, so its identity never moves), and
32
+ // each stage load asks the host before connecting, which re-posts the module
33
+ // epoch and makes sure the Service Worker controls the new document. Locally
34
+ // there is no host and none of this runs.
35
+ //
28
36
  // Note main.tsx mounts under StrictMode, so this effect double-runs in dev:
29
37
  // connect, abort + attachStage(null), connect again. The abort path rejects
30
38
  // with "stage connect aborted" and is swallowed. The iframe ELEMENT is not
31
39
  // remounted by that — only the effect re-runs — and any future effect touching
32
40
  // the bridge must survive the same double-invoke.
33
- import { useEffect, useRef } from "react";
41
+ import { useEffect, useRef, useState } from "react";
42
+ import { useStudioTheme } from "@dailies-studio/ui/theme";
34
43
  import { useStore } from "../../state/context.js";
35
- import { connectStage } from "../../bridge.js";
44
+ import { connectStage, stageBridgeOf } from "../../bridge.js";
45
+ import { dailiesHost } from "../../hostApi.js";
46
+ import { chromePerfFlag } from "../../state/playbackPreparation.js";
47
+ import { bindBoardClipboard } from "../storyboard/boardClipboard.js";
48
+ import { PLAY_CONTROL } from "../timeline/PlayButton.js";
49
+
50
+ /** The author input a background preparing pass gives way to, in either
51
+ * window (`noteAuthorInput`). */
52
+ const AUTHOR_INPUT = ["pointerdown", "keydown", "wheel", "dragover"] as const;
36
53
 
37
- /** The stage URL, resolved ONCE at module load. Only the perf flag crosses the
38
- * boundary — never the chrome's whole search string: the chrome rewrites
39
- * ?shot/?scene via history.replaceState, and an iframe src interpolating live
40
- * location.search would RELOAD the stage (fresh WebGPU adapter, every raster
41
- * redrawn) on every document switch. */
42
- const STAGE_SRC =
43
- "/shot-stage.html" +
44
- (typeof window !== "undefined" && new URLSearchParams(window.location.search).get("perf") === "1"
45
- ? "?perf=1"
46
- : "");
54
+ /** Input aimed at the Play control itself: its click, after the press,
55
+ * upgrades a background pass, which must still be running then. */
56
+ function atPlayControl(event: Event): boolean {
57
+ const target = event.target as Partial<Element> | null;
58
+ return typeof target?.closest === "function" && target.closest(PLAY_CONTROL) !== null;
59
+ }
60
+ /** How far a pointer over the stage moves from where it was last heard
61
+ * before the move counts as input, in CSS px. */
62
+ const POINTER_TRAVEL_PX = 3;
63
+
64
+ /** Whether a drag over the stage can land: only a file does. An asset tile,
65
+ * a board card, a planned shot and a Library card land on the board, never
66
+ * here. Types only: `getData` is empty until the drop. */
67
+ export function stageDropLands(dt: Pick<DataTransfer, "types">): boolean {
68
+ return Array.from(dt.types).includes("Files");
69
+ }
70
+
71
+ /** The stage URL is frozen by StagePanel's ref on its FIRST render. Only the
72
+ * perf flag, the gate's two A/B arms (`warmup`, `rasterDensity`), the heavy
73
+ * lanes' render-scale arm (`renderScale`) and the initial resolved theme
74
+ * cross the boundary, never the
75
+ * chrome's whole search string: the chrome rewrites ?shot/?scene via
76
+ * history.replaceState, and an iframe src interpolating live location.search
77
+ * would RELOAD the stage (fresh GL context, every raster redrawn) on every
78
+ * document switch or appearance change. */
79
+ export function stageSrc(theme: "light" | "dark"): string {
80
+ const search = new URLSearchParams();
81
+ // ALLOWLISTED, never forwarded verbatim (`chromePerfFlag`, which the
82
+ // preparing pass's diagnostics read too): this value is interpolated into
83
+ // the stage's URL, so the set of things it may be is closed on purpose. "1"
84
+ // is the HUD, "log" is the HUD plus a console echo of every sample.
85
+ const perf = chromePerfFlag();
86
+ if (perf !== null) search.set("perf", perf);
87
+ const own = typeof window === "undefined" ? null : new URLSearchParams(window.location.search);
88
+ // Same closed set for the warm-up arm (`ShotRuntimeProps.warmup`): the
89
+ // playback gate's A/B seam, absent from the URL in normal use.
90
+ const warmup = own?.get("warmup") ?? null;
91
+ if (warmup === "off" || warmup === "mount") {
92
+ search.set("warmup", warmup);
93
+ }
94
+ // And the raster-density arm (the stage's `STAGE_RASTER_DENSITY`): `on`
95
+ // cuts the layer rasters at the zoom times the density, one value and no
96
+ // other.
97
+ if (own?.get("rasterDensity") === "on") search.set("rasterDensity", "on");
98
+ // And the render-scale arm (the stage's `STAGE_RENDER_SCALE`): `off` mounts
99
+ // no playback scale at all, and a step forces that scale on every played
100
+ // frame. The heavy proofs' seam, never set in normal use.
101
+ const renderScale = own?.get("renderScale") ?? null;
102
+ if (renderScale !== null && ["off", "1", "0.85", "0.7", "0.5"].includes(renderScale)) {
103
+ search.set("renderScale", renderScale);
104
+ }
105
+ search.set("theme", theme);
106
+ return `/shot-stage.html?${search.toString()}`;
107
+ }
47
108
 
48
109
  export function StagePanel({
49
110
  /** Report a stage connect failure to the shell's overlay strip (null clears). */
@@ -52,42 +113,296 @@ export function StagePanel({
52
113
  onConnectError: (message: string | null) => void;
53
114
  }) {
54
115
  const store = useStore();
116
+ const { resolved: theme } = useStudioTheme();
55
117
  const iframeRef = useRef<HTMLIFrameElement | null>(null);
118
+ const srcRef = useRef(stageSrc(theme));
119
+ // Read once: the boot script installs it before the chrome runs, or never.
120
+ const [host] = useState(dailiesHost);
121
+ const [hostSettled, setHostSettled] = useState(host === undefined);
56
122
 
123
+ useEffect(() => {
124
+ if (host === undefined) return;
125
+ let live = true;
126
+ let off: (() => void) | undefined;
127
+ host.ready.then(
128
+ () => {
129
+ if (live) setHostSettled(true);
130
+ },
131
+ (e: unknown) => {
132
+ if (!live) return;
133
+ // The stage still loads: a document with no project component draws,
134
+ // and each instance layer reports its own failure on top of this.
135
+ const message = `The hosted editor could not start its code host: ${e instanceof Error ? e.message : String(e)}`;
136
+ store.reportError(message);
137
+ // A later epoch means the host recovered: clear the error, unless
138
+ // something else has replaced it since.
139
+ off = host.subscribe(() => {
140
+ if (store.getState().error === message) store.reportError(null);
141
+ off?.();
142
+ off = undefined;
143
+ });
144
+ setHostSettled(true);
145
+ },
146
+ );
147
+ return () => {
148
+ live = false;
149
+ off?.();
150
+ };
151
+ }, [host, store]);
152
+
153
+ // The stage is a same-origin document, so editor furniture can follow a
154
+ // live appearance change by updating its namespaced root signal in place.
155
+ // Only furniture descendants consume it; authored content never receives a
156
+ // generic data-theme or changing root palette. The iframe URL stays frozen
157
+ // above, which keeps the document, bridge and GL context alive. Re-apply on
158
+ // load too because Vite HMR may replace the document without replacing this
159
+ // iframe element.
160
+ useEffect(() => {
161
+ const iframe = iframeRef.current;
162
+ if (!iframe) return;
163
+ const apply = (): void => {
164
+ try {
165
+ const root = iframe.contentDocument?.documentElement;
166
+ if (root !== undefined && root !== null) root.dataset.dailiesStudioTheme = theme;
167
+ } catch {
168
+ // The shipped stage is same-origin. If a dev proxy temporarily serves
169
+ // an error document from elsewhere, appearance is cosmetic and must
170
+ // not tear down the bridge while it recovers.
171
+ }
172
+ };
173
+ iframe.addEventListener("load", apply);
174
+ apply();
175
+ return () => iframe.removeEventListener("load", apply);
176
+ }, [theme]);
177
+
178
+ // ── DROPPING A FILE ON THE PICTURE ──────────────────────────────────────
179
+ //
180
+ // The gesture every tool in this class has, and the one an author reaches for
181
+ // first with a screen recording. Without it the browser's own default wins and
182
+ // the drop NAVIGATES THE STAGE IFRAME to the file, which tears down the
183
+ // document, the bridge and the GL context: the app goes blank and the only way
184
+ // back is a reload.
185
+ //
186
+ // Listened for on the stage's own document rather than on the `<iframe>`
187
+ // element, because a drop lands INSIDE the frame and never reaches the parent
188
+ // element's handlers. That is the same same-origin reach the theme effect
189
+ // above makes, and the same `try` for the same reason.
190
+ //
191
+ // AT THE DROP POINT (F52). The stage owns its coordinate space, so the drop's
192
+ // client point crosses once through `designPointAt` (bridge v38), the same
193
+ // conversion the draw gesture gives its corners. Off the frame pane (the
194
+ // camera pane over a Shot, an open definition's one pane inside it) it is
195
+ // null and the drop lands centred, what the Add menu's row does.
57
196
  useEffect(() => {
58
197
  const iframe = iframeRef.current;
59
198
  if (!iframe) return;
60
199
  const aborter = new AbortController();
200
+ const bind = (): void => {
201
+ try {
202
+ const doc = iframe.contentDocument;
203
+ if (!doc) return;
204
+ // `dragover` MUST be prevented or `drop` never fires at all: the spec's
205
+ // default for dragover is "this is not a drop target". Prevented always,
206
+ // so the browser never navigates the frame to a file; `dropEffect`
207
+ // "none" is what tells the author nothing lands here.
208
+ doc.addEventListener(
209
+ "dragover",
210
+ (e) => {
211
+ e.preventDefault();
212
+ const dt = (e as DragEvent).dataTransfer;
213
+ if (dt !== null && dt !== undefined && !stageDropLands(dt)) {
214
+ dt.dropEffect = "none";
215
+ }
216
+ },
217
+ { signal: aborter.signal },
218
+ );
219
+ doc.addEventListener(
220
+ "drop",
221
+ (e) => {
222
+ // Prevented FIRST and unconditionally, before any early return: the
223
+ // navigation this stops is destructive even for a file the store
224
+ // will refuse, and returning early without it is how a dropped .zip
225
+ // would still blank the stage.
226
+ e.preventDefault();
227
+ const dt = (e as DragEvent).dataTransfer;
228
+ const point = store.stage()?.designPointAt(e.clientX, e.clientY) ?? undefined;
229
+ const file = dt?.files?.[0];
230
+ if (file === undefined) return;
231
+ // Through the identical handshake the Frame tool and the Add menu
232
+ // use: park a click box at the point, then hand over the file. The
233
+ // kind is the SERVER's sniff, so an unsupported file is refused
234
+ // there with one message rather than guessed at here.
235
+ const at = point ?? store.insertOrigin();
236
+ store.stageHandlers.onDraw("image", [at, at]);
237
+ void store.completeImageDraw(file).catch((err: unknown) => {
238
+ store.reportError(err instanceof Error ? err.message : String(err));
239
+ });
240
+ },
241
+ { signal: aborter.signal },
242
+ );
243
+ // The board's ⌘C ⌘V on a frame layer: the pointer on the frame focuses
244
+ // this document, so the native clipboard events fire here, not in the
245
+ // chrome's.
246
+ bindBoardClipboard(doc, store, aborter.signal);
247
+ } catch {
248
+ // Same as the theme effect: a dev proxy serving a cross-origin error
249
+ // document must not tear anything down. The cost is that dropping does
250
+ // nothing until it recovers, which is the pre-existing behaviour.
251
+ }
252
+ };
253
+ iframe.addEventListener("load", bind, { signal: aborter.signal });
254
+ bind();
255
+ return () => aborter.abort();
256
+ }, [store]);
257
+
258
+ // ── THE AUTHOR AT WORK, IN BOTH WINDOWS ────────────────────────────────
259
+ //
260
+ // What a background preparing pass gives way to, and what its idle wait
261
+ // starts again on (`noteAuthorInput`): a press, a key, a wheel or a drag
262
+ // over, in the chrome or on the stage, heard in the capture phase so a
263
+ // handler that stops the event cannot hide it. On the stage a pointer that
264
+ // TRAVELS counts too, but not one at rest or jittering in place, which is
265
+ // the author's usual state over the picture: pausing on it would starve the
266
+ // pass. Input aimed at the Play control only restarts the wait: its click is
267
+ // the press of Play that upgrades the pass. The stage's listeners go on
268
+ // again after each load, since a reminted document has none.
269
+ useEffect(() => {
270
+ const iframe = iframeRef.current;
271
+ if (!iframe) return;
272
+ const aborter = new AbortController();
273
+ const options = { capture: true, passive: true, signal: aborter.signal };
274
+ const note = (event: Event): void => store.noteAuthorInput(!atPlayControl(event));
275
+ const listen = (target: Window): void => {
276
+ for (const type of AUTHOR_INPUT) target.addEventListener(type, note, options);
277
+ };
278
+ let travelledFrom: { x: number; y: number } | null = null;
279
+ const travel = (e: PointerEvent): void => {
280
+ const from = travelledFrom;
281
+ if (from !== null && Math.hypot(e.clientX - from.x, e.clientY - from.y) < POINTER_TRAVEL_PX) return;
282
+ travelledFrom = { x: e.clientX, y: e.clientY };
283
+ // The first move only says where the pointer is.
284
+ if (from !== null) store.noteAuthorInput();
285
+ };
286
+ listen(window);
287
+ const bindStage = (): void => {
288
+ try {
289
+ const stage = iframe.contentWindow;
290
+ if (stage === null) return;
291
+ travelledFrom = null;
292
+ listen(stage);
293
+ stage.addEventListener("pointermove", travel, options);
294
+ } catch {
295
+ // The drop effect's reason: a dev proxy's cross-origin error document
296
+ // must not tear anything down. Its input goes unheard meanwhile.
297
+ }
298
+ };
299
+ iframe.addEventListener("load", bindStage, { signal: aborter.signal });
300
+ bindStage();
301
+ return () => aborter.abort();
302
+ }, [store]);
303
+
304
+ // THE PANEL GOING withdraws the stage it expected, settled or not: the
305
+ // connect effect below has no cleanup before the host settles, so an
306
+ // unmount then would leave a queued Play waiting for a stage that is gone.
307
+ // Its own effect, so the settle flip never runs it.
308
+ useEffect(() => () => store.expectStage(false), [store]);
309
+
310
+ useEffect(() => {
311
+ const iframe = iframeRef.current;
312
+ if (!iframe) return;
313
+ // A STAGE IS ON ITS WAY FROM THE FIRST COMMIT, hosted too: the code host's
314
+ // boot comes before the stage's src, and a press of Play in it is queued
315
+ // like one during the stage's own boot. Before the return below, and with
316
+ // no cleanup on that branch, so the expectation holds across the settle
317
+ // flip: only an unmount or a failed connect withdraws it.
318
+ store.expectStage(true);
319
+ if (!hostSettled) return;
320
+ // ONE CONTROLLER PER CONNECT ATTEMPT: a new attempt (the next load) ends
321
+ // the one before it, so a local boot, which connects at once and again at
322
+ // the iframe's load, attaches once.
323
+ let attempt: AbortController | null = null;
61
324
  const connect = (): void => {
325
+ attempt?.abort();
326
+ const own = new AbortController();
327
+ attempt = own;
62
328
  onConnectError(null);
63
- void connectStage(iframe, store.stageHandlers, { signal: aborter.signal })
329
+ store.setStageConnectError(null);
330
+ // A RELOAD IS A NEW STAGE. This runs for the reminted document too, and
331
+ // the bridge from the old one is dead by then, so dropping it HERE is
332
+ // what clears `stageInfo` and `stageBusy`. Without it the chrome went on
333
+ // holding a stale "the stage is ready" beside a stage that had not come
334
+ // up, which is a lit play button over a canvas that has never drawn.
335
+ // Paired with the expectation below, a press of Play is queued across a
336
+ // remint exactly as it is across the first boot.
337
+ store.attachStage(null);
338
+ store.expectStage(true);
339
+ void connectStage(iframe, store.stageHandlers, { signal: own.signal })
64
340
  .then((bridge) => {
65
- if (!aborter.signal.aborted) store.attachStage(bridge);
341
+ if (own.signal.aborted) return;
342
+ store.attachStage(bridge);
343
+ // A RELOAD IS HEARD AS THE OLD DOCUMENT GOES, not only at the new
344
+ // one's load: the transport pauses (remembering its start) while
345
+ // the next stage boots, instead of running sound over a stage that
346
+ // is gone. A pagehide that keeps the page for the back/forward
347
+ // cache comes back with no load to reattach it, so it is ignored.
348
+ iframe.contentWindow?.addEventListener(
349
+ "pagehide",
350
+ (e: PageTransitionEvent) => {
351
+ if (e.persisted) return;
352
+ store.attachStage(null);
353
+ store.expectStage(true);
354
+ },
355
+ { signal: own.signal },
356
+ );
66
357
  })
67
358
  .catch((e) => {
68
- if (!aborter.signal.aborted) {
69
- onConnectError(e instanceof Error ? e.message : String(e));
359
+ if (!own.signal.aborted) {
360
+ // WITHDRAWN ON FAILURE, so a bridge version mismatch or the poll
361
+ // deadline calls a queued Play off instead of leaving it waiting
362
+ // for a stage that will never draw. The refusal is the overlay
363
+ // strip's, which says what actually went wrong.
364
+ store.expectStage(false);
365
+ const message = e instanceof Error ? e.message : String(e);
366
+ onConnectError(message);
367
+ // And for an agent, which cannot see the strip (stage health).
368
+ store.setStageConnectError(message);
70
369
  }
71
370
  });
72
371
  };
73
- iframe.addEventListener("load", connect);
74
- connect(); // the load event may already have fired
372
+ const onLoad = (): void => {
373
+ const stage = iframe.contentWindow;
374
+ // False: the host is reloading the stage to get it controlled.
375
+ if (host !== undefined && stage !== null && !host.stageLoaded(stage)) return;
376
+ // The document already attached (its bridge booted before its load
377
+ // event): nothing new loaded, so the stage is not dropped and taken
378
+ // again, and a queued Play is not mistaken for a reload's.
379
+ const attached = store.stage();
380
+ if (attached !== null && stageBridgeOf(iframe) === attached) return;
381
+ connect();
382
+ };
383
+ iframe.addEventListener("load", onLoad);
384
+ // Locally the load event may already have fired. Hosted, the src was set
385
+ // in the commit this effect follows, so the stage's load is still ahead,
386
+ // and connecting before it would push a document the host has not seen.
387
+ if (host === undefined) connect();
75
388
  return () => {
76
- aborter.abort();
77
- iframe.removeEventListener("load", connect);
389
+ attempt?.abort();
390
+ iframe.removeEventListener("load", onLoad);
78
391
  store.attachStage(null);
392
+ store.expectStage(false);
79
393
  // The error belonged to the connection being torn down; it is the shell's
80
394
  // state now, so it has to be cleared explicitly rather than dying with a
81
395
  // local useState.
82
396
  onConnectError(null);
397
+ store.setStageConnectError(null);
83
398
  };
84
- }, [store, onConnectError]);
399
+ }, [store, onConnectError, host, hostSettled]);
85
400
 
86
401
  return (
87
402
  <iframe
88
403
  ref={iframeRef}
89
404
  className="stage-frame"
90
- src={STAGE_SRC}
405
+ src={hostSettled ? srcRef.current : undefined}
91
406
  title="Stage"
92
407
  // The stage pins the cursor for the duration of a G / R / S transform,
93
408
  // and an iframe cannot without the delegation. Without it
@@ -0,0 +1,70 @@
1
+ // THE CELLS A MODE SHOWS ITS CONTENT IN.
2
+ //
3
+ // The four modes (the Storyboard, the material nodes, the Component Studio,
4
+ // Code Studio) used to cover the window. They now show their content in the
5
+ // docked grid's own cells: the left column, the canvas cell, the bottom row
6
+ // and the canvas bar. Each of those cells renders one SLOT, always, and a mode
7
+ // portals its parts into them.
8
+ //
9
+ // ── WHY PORTALS AND NOT FOUR COMPONENTS PER MODE ────────────────────────────
10
+ //
11
+ // A mode is one component with one state: the board's canvas API is shared by
12
+ // its rail and its canvas, the material graph's React Flow provider wraps the
13
+ // zoom its bar shows, the code studio's buffer feeds its editor and its save.
14
+ // Split into per-cell components, every one of those would have to move into
15
+ // the store or a new context. A portal keeps the mode one React subtree
16
+ // (context, refs and events all flow through a portal) and only moves where
17
+ // its pieces land in the DOM.
18
+ //
19
+ // The slots are rendered unconditionally, so a mode that opens finds them
20
+ // already registered and lands in the same commit, never a frame later.
21
+ import { createContext, useCallback, useContext, useMemo, useState } from "react";
22
+ import type { ReactNode } from "react";
23
+ import { createPortal } from "react-dom";
24
+
25
+ export type CellName = "left" | "centre" | "bottom" | "bar";
26
+
27
+ type Slots = Record<CellName, HTMLElement | null>;
28
+
29
+ interface CellSlotsValue {
30
+ slots: Slots;
31
+ register: (name: CellName, el: HTMLElement | null) => void;
32
+ }
33
+
34
+ const CellSlotsContext = createContext<CellSlotsValue | null>(null);
35
+
36
+ const EMPTY: Slots = { left: null, centre: null, bottom: null, bar: null };
37
+
38
+ /** Around the shell. Renders no element of its own, so the stage iframe's
39
+ * place in the DOM is untouched. */
40
+ export function CellSlotsProvider({ children }: { children: ReactNode }) {
41
+ const [slots, setSlots] = useState<Slots>(EMPTY);
42
+ const register = useCallback((name: CellName, el: HTMLElement | null) => {
43
+ setSlots((was) => (was[name] === el ? was : { ...was, [name]: el }));
44
+ }, []);
45
+ const value = useMemo(() => ({ slots, register }), [slots, register]);
46
+ return <CellSlotsContext.Provider value={value}>{children}</CellSlotsContext.Provider>;
47
+ }
48
+
49
+ /** A cell's slot: an empty element a mode portals into. */
50
+ export function CellSlot({ name, className }: { name: CellName; className: string }) {
51
+ const ctx = useContext(CellSlotsContext);
52
+ const register = ctx?.register;
53
+ const ref = useCallback(
54
+ (el: HTMLDivElement | null) => {
55
+ register?.(name, el);
56
+ },
57
+ [register, name],
58
+ );
59
+ return <div ref={ref} className={className} data-cell-slot={name} />;
60
+ }
61
+
62
+ /**
63
+ * A mode's part, shown in the named cell. Renders nothing until the cell's
64
+ * slot is on the page (outside the shell, as in a unit test that mounts a mode
65
+ * alone, there is no slot and nothing is shown).
66
+ */
67
+ export function InCell({ name, children }: { name: CellName; children: ReactNode }) {
68
+ const node = useContext(CellSlotsContext)?.slots[name] ?? null;
69
+ return node === null ? null : createPortal(children, node);
70
+ }