@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
@@ -15,17 +15,41 @@
15
15
  // "the document moved underneath me", and only one of them would have that
16
16
  // refusal in it.
17
17
  //
18
- // EventSource reconnects on its own, and a missed frame costs a stale progress
19
- // number until the next one. For the document that is safe too: `doc-changed`
20
- // is a NOTIFICATION and `adoptExternalChange` re-reads from the server, so a
21
- // dropped frame costs a stale view until the next write, which the baseRev 409
22
- // protocol already makes safe.
18
+ // EventSource reconnects on its own. For the document a dropped frame is safe:
19
+ // `doc-changed` is a NOTIFICATION and `adoptExternalChange` re-reads from the
20
+ // server, so it costs a stale view until the next write, which the baseRev 409
21
+ // protocol already makes safe. For a render it was NOT: a dropped "done" or
22
+ // "failed" left the export lock held and the dialog on "Rendering" forever, and
23
+ // a server restart dropped the job itself. So every `hello` (the first frame of
24
+ // every connection, reconnects included) re-reads the render state; see
25
+ // `reconcileRender`.
26
+ import { renderState } from "../api.js";
27
+ import type { RenderJob, RenderState } from "../api.js";
28
+ import type { StageHealthRequest } from "@dailies-studio/shot/doc";
29
+ import { dailiesHost } from "../hostApi.js";
30
+ import { CLIENT_ID, CLIENT_ID_HEADER } from "./clientId.js";
31
+ import { setRuntimeErrorRevision } from "./runtimeErrors.js";
32
+ import { awaitStageHealth } from "./stageHealth.js";
23
33
  import type { ShotEditorStore } from "./store.js";
24
- import type { RenderJob } from "../api.js";
34
+ import { buffersOpen } from "./types.js";
35
+ import { parseAgentActivity } from "./agentActivity.js";
25
36
 
26
37
  /** The SSE event names; `docEvents.ts` is the other half of this contract. */
27
38
  const EVENT = "shot-render";
28
39
  const DOC_EVENT = "doc-changed";
40
+ const CONTRACT_EVENT = "contract-changed";
41
+ const FILES_EVENT = "files-changed";
42
+ const RELAY_EVENT = "relay-request";
43
+ /** Comment events (plan C4): `{v:1, projectId, threadId, stillId}` and
44
+ * `{v:1}`. Signals only: the comments store refetches. */
45
+ const COMMENTS_EVENT = "comments-changed";
46
+ const INBOX_EVENT = "inbox-changed";
47
+ /** An MCP agent's tool call started or ended (store mcpActivity.ts). */
48
+ const AGENT_EVENT = "agent-activity";
49
+ /** An agent's question to the person moved: `{v:1, project, id, status}`. */
50
+ const QUESTION_EVENT = "agent-question";
51
+ /** A project file's bytes changed: `{v:1, path, hash}`. */
52
+ const SOURCE_EVENT = "source-changed";
29
53
 
30
54
  /**
31
55
  * Subscribe until the returned function is called.
@@ -35,47 +59,303 @@ const DOC_EVENT = "doc-changed";
35
59
  * chrome from booting.
36
60
  */
37
61
  export function subscribeStudioEvents(store: ShotEditorStore): () => void {
62
+ // ── WHAT THE STAGE MADE OF A WRITE, FOR AN AGENT ─────────────────────
63
+ //
64
+ // An MCP write waits for an editor tab's stage to draw it and reports what
65
+ // the stage holds (state/stageHealth.ts). Hosted, the code host answers the
66
+ // workspace's relayed question and asks this chrome through the host seam.
67
+ // Locally there is no code host: this chrome's feed names it and asks for
68
+ // the dev server's one relayed question, `stage`, which it answers itself.
69
+ const host = dailiesHost();
70
+ setRuntimeErrorRevision(() => {
71
+ const now = store.getState();
72
+ return now.dirty ? null : now.rev;
73
+ });
74
+ host?.provideStageHealth((request) => awaitStageHealth(store, request));
75
+ // ── COMMENTS COME BACK ON FOCUS ─────────────────────────────────────────
76
+ //
77
+ // A hidden tab's EventSource may be throttled or dropped, and an access
78
+ // change made in the dashboard tab (Share and mention) sends no comment
79
+ // event. So a tab that comes back to the front re-reads both.
80
+ const refetchComments = (): void => {
81
+ if (typeof document !== "undefined" && document.visibilityState === "hidden") return;
82
+ void store.comments.refresh();
83
+ void store.comments.loadInbox();
84
+ };
85
+ const win = typeof window === "undefined" ? undefined : window;
86
+ win?.addEventListener("focus", refetchComments);
87
+ win?.document.addEventListener("visibilitychange", refetchComments);
88
+ const unlistenFocus = (): void => {
89
+ win?.removeEventListener("focus", refetchComments);
90
+ win?.document.removeEventListener("visibilitychange", refetchComments);
91
+ };
38
92
  const Source = (globalThis as { EventSource?: typeof EventSource }).EventSource;
39
- if (Source === undefined) return () => {};
40
- const source = new Source("/api/events");
93
+ if (Source === undefined) return unlistenFocus;
94
+ const source = new Source(host === undefined ? `/api/events?client=${encodeURIComponent(CLIENT_ID)}&relay=1` : "/api/events");
41
95
  const onMessage = (e: MessageEvent<string>): void => {
42
96
  const job = parseJob(e.data);
43
97
  // Only THIS document's renders. The bus is project-wide and a second chrome
44
98
  // may be exporting something else; showing its progress here would be a
45
- // progress bar for a file this window is not making.
46
- if (job !== null && job.name === store.getState().name) store.setRenderJob(job);
99
+ // progress bar for a file this window is not making. The job this window
100
+ // already holds is followed to its end even after it opened another shot:
101
+ // that job holds the export lock, and dropping its "done" held it for good.
102
+ if (job === null) return;
103
+ const now = store.getState();
104
+ if (job.name === now.name || job.id === now.renderJob?.id) store.setRenderJob(job);
47
105
  };
48
106
  const onDoc = (e: MessageEvent<string>): void => {
49
107
  const payload = parseDocChange(e.data);
50
- // The SHOT lane only. The bus carries motion, canvas and scene writes too,
51
- // and handing one of those to a shot store would ask it to reload a
52
- // document that is not in its store at all.
53
- if (payload !== null && payload.lane === "shot") store.adoptExternalChange(payload);
108
+ if (payload === null) return;
109
+ // The SHOT lane only. The bus carries other lanes' writes too, and handing
110
+ // one of those to a shot store would ask it to reload a document that is
111
+ // not in its store at all.
112
+ if (payload.lane === "shot") store.adoptExternalChange(payload);
113
+ };
114
+ // ── A COMPONENT'S SOURCE CHANGED ON DISK ────────────────────────────────
115
+ //
116
+ // The watcher re-extracts the contract and tells Vite, which reloads the
117
+ // stage. That covers the PICTURE. It never covered the chrome: the drift
118
+ // badge, the props panel and the open source buffer all stayed on what they
119
+ // had, so an edit made from outside this window — a second editor, or an
120
+ // agent through MCP — was invisible here until something happened to refetch.
121
+ const onContract = (e: MessageEvent<string>): void => {
122
+ const componentId = parseContractChange(e.data);
123
+ if (componentId === null) return;
124
+ store.adoptContractChange(componentId);
125
+ };
126
+ // ── A FILE APPEARED OR VANISHED ─────────────────────────────────────────
127
+ //
128
+ // The payload names nothing on purpose, so there is nothing to parse and
129
+ // nothing to get wrong: re-list and take the server's answer. Only while code
130
+ // buffers are open (Code Studio): a chrome editing a
131
+ // shot has no reason to fetch a file listing every time a build writes.
132
+ const onFiles = (): void => {
133
+ if (buffersOpen(store.getState())) void store.refreshProjectFiles();
134
+ };
135
+ const onComments = (e: MessageEvent<string>): void => {
136
+ store.comments.changed(parseJson(e.data) ?? {});
137
+ };
138
+ const onInbox = (): void => {
139
+ void store.comments.loadInbox();
140
+ };
141
+ const onAgent = (e: MessageEvent<string>): void => {
142
+ const event = parseAgentActivity(e.data);
143
+ if (event !== null) store.agentActivity.receive(event);
144
+ };
145
+ // The open Project's question only: re-read it, which opens the dialog for
146
+ // a new one and closes it for one answered elsewhere.
147
+ const onQuestion = (e: MessageEvent<string>): void => {
148
+ const name = store.getState().name;
149
+ if (name !== null && parseJson(e.data)?.["project"] === name) void store.agentQuestion.load(name);
150
+ };
151
+ // ── FOLLOWING AN AGENT'S WRITES ─────────────────────────────────────────
152
+ //
153
+ // `contract-changed` fires only when a contract moves, so a body-only write
154
+ // left an open buffer on the old text until its save 409'd. Each write now
155
+ // reaches the buffer holding that file, which is what Code Studio's follow
156
+ // mode shows.
157
+ const onSource = (e: MessageEvent<string>): void => {
158
+ const change = parseJson(e.data);
159
+ const path = change?.["path"];
160
+ const hash = change?.["hash"];
161
+ if (typeof path !== "string" || (hash !== null && typeof hash !== "string")) return;
162
+ void store.adoptSourceChange(path, hash);
163
+ };
164
+ const onHello = (): void => {
165
+ // A reconnect may have dropped comment events: read both again.
166
+ refetchComments();
167
+ const name = store.getState().name;
168
+ if (name === null) return;
169
+ // And the question: an ask sent while the feed was down is still open.
170
+ void store.agentQuestion.load(name);
171
+ const asked = store.getState().renderJob;
172
+ void renderState(name)
173
+ .then((state) => reconcileRender(store, name, state, asked))
174
+ .catch(() => {
175
+ // A server without the render lane, or one mid-restart: the next
176
+ // hello asks again.
177
+ });
178
+ };
179
+ const onRelay = (e: MessageEvent<string>): void => {
180
+ const ask = parseStageAsk(e.data);
181
+ if (ask === null) return;
182
+ const answer = (outcome: { ok: true; result: unknown } | { ok: false; error: string }): void => {
183
+ void fetch(`/api/relay/${encodeURIComponent(ask.id)}/result`, {
184
+ method: "POST",
185
+ headers: { "content-type": "application/json", [CLIENT_ID_HEADER]: CLIENT_ID },
186
+ body: JSON.stringify(outcome),
187
+ }).catch(() => {
188
+ // The dev server times the question out and says so.
189
+ });
190
+ };
191
+ if (ask.tool !== "stage") {
192
+ answer({ ok: false, error: `this editor answers "stage", not ${JSON.stringify(ask.tool)}: reload it, which picks up the newer build` });
193
+ return;
194
+ }
195
+ void awaitStageHealth(store, ask.input).then(
196
+ (report) => answer({ ok: true, result: report }),
197
+ (error: unknown) => answer({ ok: false, error: error instanceof Error ? error.message : String(error) }),
198
+ );
54
199
  };
200
+ source.addEventListener("hello", onHello);
55
201
  source.addEventListener(EVENT, onMessage as EventListener);
56
202
  source.addEventListener(DOC_EVENT, onDoc as EventListener);
203
+ source.addEventListener(CONTRACT_EVENT, onContract as EventListener);
204
+ source.addEventListener(FILES_EVENT, onFiles);
205
+ if (host === undefined) source.addEventListener(RELAY_EVENT, onRelay as EventListener);
206
+ source.addEventListener(COMMENTS_EVENT, onComments as EventListener);
207
+ source.addEventListener(INBOX_EVENT, onInbox);
208
+ source.addEventListener(AGENT_EVENT, onAgent as EventListener);
209
+ source.addEventListener(SOURCE_EVENT, onSource as EventListener);
210
+ source.addEventListener(QUESTION_EVENT, onQuestion as EventListener);
57
211
  return () => {
212
+ source.removeEventListener(QUESTION_EVENT, onQuestion as EventListener);
213
+ source.removeEventListener(AGENT_EVENT, onAgent as EventListener);
214
+ source.removeEventListener(SOURCE_EVENT, onSource as EventListener);
215
+ unlistenFocus();
216
+ source.removeEventListener(COMMENTS_EVENT, onComments as EventListener);
217
+ source.removeEventListener(INBOX_EVENT, onInbox);
218
+ source.removeEventListener("hello", onHello);
58
219
  source.removeEventListener(EVENT, onMessage as EventListener);
59
220
  source.removeEventListener(DOC_EVENT, onDoc as EventListener);
221
+ source.removeEventListener(CONTRACT_EVENT, onContract as EventListener);
222
+ source.removeEventListener(FILES_EVENT, onFiles);
223
+ source.removeEventListener(RELAY_EVENT, onRelay as EventListener);
60
224
  source.close();
61
225
  };
62
226
  }
63
227
 
228
+ /** The render reason a job the server no longer knows gets. */
229
+ export const RENDER_LOST =
230
+ "the studio server lost track of this render (it restarted, or its progress stopped reaching " +
231
+ "this window); check the output folder, or export again";
232
+
233
+ /**
234
+ * Adopt what the server says about THIS shot's render after a (re)connect.
235
+ *
236
+ * The live job wins, then the newest finished one, so a chrome that reloaded
237
+ * still shows the last result. A job this window thinks is running that the
238
+ * server has never heard of ended with the old server, and it is closed as
239
+ * failed rather than holding the export lock forever.
240
+ *
241
+ * `asked` is the job this window held when the read was sent. If that changed
242
+ * while the read was in flight (an Export click, a bus frame), the answer is
243
+ * older than what the window knows and is dropped: applying it could close a
244
+ * render that had just started.
245
+ */
246
+ export function reconcileRender(
247
+ store: ShotEditorStore,
248
+ name: string,
249
+ state: RenderState,
250
+ asked: RenderJob | null,
251
+ ): void {
252
+ const now = store.getState();
253
+ if (now.name !== name || now.renderJob !== asked) return;
254
+ const newest = state.job ?? state.recent.at(-1) ?? null;
255
+ if (newest !== null) {
256
+ store.setRenderJob(newest);
257
+ return;
258
+ }
259
+ // A job of ANOTHER shot (the author switched, renamed or duplicated while
260
+ // it ran). The route reports only this shot's renders, so its silence says
261
+ // nothing about that job: it is dropped from this window, never declared
262
+ // lost while the server may still be rendering it.
263
+ if (asked !== null && asked.name !== name) {
264
+ store.setRenderJob(null);
265
+ return;
266
+ }
267
+ if (asked !== null && asked.phase === "running") {
268
+ store.setRenderJob({ ...asked, phase: "failed", error: RENDER_LOST });
269
+ }
270
+ }
271
+
272
+ /** A relayed question the dev server asks this chrome (`relay-request`,
273
+ * `{v, id, tool, input}`): only `stage` is asked locally. */
274
+ function parseStageAsk(raw: string): { id: string; tool: string; input: StageHealthRequest } | null {
275
+ try {
276
+ const data: unknown = JSON.parse(raw);
277
+ if (data === null || typeof data !== "object") return null;
278
+ const p = data as { id?: unknown; tool?: unknown; input?: unknown };
279
+ if (typeof p.id !== "string" || typeof p.tool !== "string") return null;
280
+ const i = (typeof p.input === "object" && p.input !== null ? p.input : {}) as Record<string, unknown>;
281
+ const code = (typeof i["code"] === "object" && i["code"] !== null ? i["code"] : null) as Record<string, unknown> | null;
282
+ return {
283
+ id: p.id,
284
+ tool: p.tool,
285
+ input: {
286
+ ...(typeof i["name"] === "string" ? { name: i["name"] } : {}),
287
+ ...(typeof i["rev"] === "string" ? { rev: i["rev"] } : {}),
288
+ ...(code === null
289
+ ? {}
290
+ : {
291
+ code: {
292
+ ...(typeof code["epoch"] === "number" ? { epoch: code["epoch"] } : {}),
293
+ ...(typeof code["since"] === "number" ? { since: code["since"] } : {}),
294
+ },
295
+ }),
296
+ waitMs: typeof i["waitMs"] === "number" && Number.isFinite(i["waitMs"]) ? Math.max(0, i["waitMs"]) : 0,
297
+ },
298
+ };
299
+ } catch {
300
+ return null;
301
+ }
302
+ }
303
+
304
+ /** The one field this chrome acts on. `ContractChangedPayload` also carries the
305
+ * hashes, which are what the REFETCH is for. */
306
+ /** A comment frame's fields, or null for a malformed one. */
307
+ function parseJson(raw: string): Record<string, unknown> | null {
308
+ try {
309
+ const data: unknown = JSON.parse(raw);
310
+ return data !== null && typeof data === "object" ? (data as Record<string, unknown>) : null;
311
+ } catch {
312
+ return null;
313
+ }
314
+ }
315
+
316
+ function parseContractChange(raw: string): string | null {
317
+ try {
318
+ const data: unknown = JSON.parse(raw);
319
+ if (data === null || typeof data !== "object") return null;
320
+ const p = data as { componentId?: unknown };
321
+ return typeof p.componentId === "string" && p.componentId.length > 0 ? p.componentId : null;
322
+ } catch {
323
+ return null;
324
+ }
325
+ }
326
+
64
327
  /** The subset of `DocChangedPayload` this chrome acts on. A malformed frame is
65
328
  * dropped for the same reason a malformed job frame is. */
66
- function parseDocChange(
67
- raw: string,
68
- ): { lane: string; name?: string; origin?: string; clientId?: string } | null {
329
+ function parseDocChange(raw: string): {
330
+ lane: string;
331
+ name?: string;
332
+ origin?: string;
333
+ clientId?: string;
334
+ deleted?: true;
335
+ renamedFrom?: string;
336
+ renamedTo?: string;
337
+ } | null {
69
338
  try {
70
339
  const data: unknown = JSON.parse(raw);
71
340
  if (data === null || typeof data !== "object") return null;
72
- const p = data as { lane?: unknown; name?: unknown; origin?: unknown; clientId?: unknown };
341
+ const p = data as {
342
+ lane?: unknown;
343
+ name?: unknown;
344
+ origin?: unknown;
345
+ clientId?: unknown;
346
+ deleted?: unknown;
347
+ renamedFrom?: unknown;
348
+ renamedTo?: unknown;
349
+ };
73
350
  if (typeof p.lane !== "string") return null;
74
351
  return {
75
352
  lane: p.lane,
76
353
  ...(typeof p.name === "string" ? { name: p.name } : {}),
77
354
  ...(typeof p.origin === "string" ? { origin: p.origin } : {}),
78
355
  ...(typeof p.clientId === "string" ? { clientId: p.clientId } : {}),
356
+ ...(p.deleted === true ? { deleted: true as const } : {}),
357
+ ...(typeof p.renamedFrom === "string" ? { renamedFrom: p.renamedFrom } : {}),
358
+ ...(typeof p.renamedTo === "string" ? { renamedTo: p.renamedTo } : {}),
79
359
  };
80
360
  } catch {
81
361
  return null;
@@ -0,0 +1,46 @@
1
+ // The chrome realm's errors that no panel owns: a worklet processor that
2
+ // throws (the level meters' ran `process()` into a ReferenceError on every
3
+ // render quantum in the shipped chrome, and said so only in the console).
4
+ // Kept, deduplicated, for the stage health an agent asks for
5
+ // (state/stageHealth.ts), stamped with the saved revision on screen when each
6
+ // was last seen, so a write's check counts only what happened at the
7
+ // revision it wrote. One log per page, like the chrome's one store.
8
+ import { recordStageError, settleStageErrors } from "@dailies-studio/shot/doc";
9
+ import type { StageErrorSource, StageRuntimeError } from "@dailies-studio/shot/doc";
10
+
11
+ let errors: StageRuntimeError[] = [];
12
+ let revision: () => string | null = () => null;
13
+ const listeners = new Set<() => void>();
14
+
15
+ /** How the log reads the saved revision on screen (the store registers it). */
16
+ export function setRuntimeErrorRevision(read: () => string | null): void {
17
+ revision = read;
18
+ }
19
+
20
+ /** `standing`: the realm that threw is dead until it restarts (see
21
+ * `runtimeRealmRestarted`), so the error counts at every revision. */
22
+ export function recordRuntimeError(source: StageErrorSource, message: string, standing = false): void {
23
+ errors = recordStageError(errors, source, message, revision(), Date.now(), standing);
24
+ for (const listener of [...listeners]) listener();
25
+ }
26
+
27
+ /** `source`'s realm runs again (every dead processor was torn down): its
28
+ * errors are history. */
29
+ export function runtimeRealmRestarted(source: StageErrorSource): void {
30
+ errors = settleStageErrors(errors, source);
31
+ for (const listener of [...listeners]) listener();
32
+ }
33
+
34
+ export function runtimeErrors(): readonly StageRuntimeError[] {
35
+ return errors;
36
+ }
37
+
38
+ export function subscribeRuntimeErrors(listener: () => void): () => void {
39
+ listeners.add(listener);
40
+ return () => listeners.delete(listener);
41
+ }
42
+
43
+ /** Test seam: an empty log. */
44
+ export function clearRuntimeErrors(): void {
45
+ errors = [];
46
+ }
@@ -0,0 +1,12 @@
1
+ /** Selection membership is read by every visible outline row. Store selection
2
+ * arrays are immutable, so their identity is the exact invalidation key for a
3
+ * single shared Set rather than one linear `includes` scan per row. */
4
+ const MEMBERSHIP = new WeakMap<readonly string[], ReadonlySet<string>>();
5
+
6
+ export function selectionMembership(selection: readonly string[]): ReadonlySet<string> {
7
+ const found = MEMBERSHIP.get(selection);
8
+ if (found !== undefined) return found;
9
+ const next = new Set(selection);
10
+ MEMBERSHIP.set(selection, next);
11
+ return next;
12
+ }