@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
@@ -0,0 +1,528 @@
1
+ // The storyboard's pure editor model: what each card draws as, the board's
2
+ // cards by planned shot, and its rows by Shot. The document's own derived
3
+ // views (windows, status, the brief) live in `@dailies-studio/shot/doc`'s
4
+ // storyboard.ts and are reused here.
5
+ //
6
+ // Pure so the store, the board and the menus ask one implementation and the
7
+ // tests can pin it without mounting anything.
8
+ import { videoFrameSeconds, referencesByPart, visibleStills, gapsOf, sceneWindows, cutPlays, seamWindow, partOfScene, partFramesOf, sketchesOf, boardSlots } from "@dailies-studio/shot/doc";
9
+ import type { Doc3D, ReferenceItem3D, Scene3D, SceneWindow, ShotAsset, StillGap, StoryboardPart3D, VisibleStill } from "@dailies-studio/shot/doc";
10
+ import { framesToSeconds } from "@dailies-studio/engine";
11
+ import { resolveGlPresetSpec } from "@dailies-studio/transitions";
12
+ import type { LibraryItem } from "../api.js";
13
+ import { formatDuration } from "../panels/timeline/model.js";
14
+
15
+ // A part's colour and the part a Shot's first plan write makes live in the
16
+ // data plane, so a new Project's scaffold makes Shot 1's part the same way.
17
+ export { PART_PALETTE, paletteColor, pinPartFor } from "@dailies-studio/shot/doc";
18
+
19
+ /** `library` is a shipped shot, material, blueprint or image pack cited by
20
+ * the board. A shipped COMPONENT never draws as one: dropping it installs it,
21
+ * and the card is an ordinary `component`. */
22
+ export type CardKind = "image" | "video" | "note" | "component" | "library" | "text" | "transition" | "sound" | "native" | "link";
23
+
24
+ /** Share the runtime's midpoint seek so a boundary cannot decode the previous
25
+ * picture. A partial final frame uses the midpoint of its available interval;
26
+ * a frame starting beyond the source is refused, never relabelled. */
27
+ export function storyboardVideoSeconds(frame: number, fps: Doc3D["fps"], duration: number): number | null {
28
+ if (!Number.isFinite(duration) || duration <= 0) return null;
29
+ const start = (frame * fps.den) / fps.num;
30
+ if (start >= duration) return null;
31
+ return Math.min(videoFrameSeconds(frame, fps), start + (duration - start) / 2);
32
+ }
33
+
34
+ /** The components the board cites, for the rail's used-components list. */
35
+ export function storyboardComponentsOf(doc: Doc3D): Array<{ component: string; referenceIds: string[] }> {
36
+ const byComponent = new Map<string, string[]>();
37
+ for (const item of doc.resources?.references ?? []) {
38
+ if (item.type !== "component" || item.component === undefined) continue;
39
+ const ids = byComponent.get(item.component) ?? [];
40
+ ids.push(item.id);
41
+ byComponent.set(item.component, ids);
42
+ }
43
+ return [...byComponent.entries()].map(([component, referenceIds]) => ({ component, referenceIds }));
44
+ }
45
+
46
+ export function isPartId(id: string): boolean {
47
+ return id.startsWith("part_");
48
+ }
49
+
50
+ export function isReferenceId(id: string): boolean {
51
+ return id.startsWith("ref_");
52
+ }
53
+
54
+ /** A stored still's id (v37). The computed default has none. */
55
+ export function isStillId(id: string): boolean {
56
+ return id.startsWith("stl_");
57
+ }
58
+
59
+ /** A gap's derived id, `gap_<from still>_<to still>` (`gapsOf`). Never
60
+ * stored, so no document verb may ever be handed one. */
61
+ export function isGapId(id: string): boolean {
62
+ return id.startsWith("gap_");
63
+ }
64
+
65
+ /** A cut's derived id, `cut_<outgoing Shot>`. Never stored. */
66
+ export function isCutId(id: string): boolean {
67
+ return id.startsWith("cut_");
68
+ }
69
+
70
+ export function cutIdOf(sceneId: string): string {
71
+ return `cut_${sceneId}`;
72
+ }
73
+
74
+ /** An id the board selects that names no stored row: a gap or a cut. Delete,
75
+ * Duplicate and Rename strip these before any edit. */
76
+ export function isDerivedBoardId(id: string): boolean {
77
+ return isGapId(id) || isCutId(id);
78
+ }
79
+
80
+ /** The card kind a reference draws as. An element card (v33) is its own. */
81
+ export function cardKindOf(item: ReferenceItem3D, assets: Doc3D["assets"]): CardKind {
82
+ if (item.type !== "asset") return item.type;
83
+ return assets[item.asset]?.kind === "video" ? "video" : "image";
84
+ }
85
+
86
+ /** An element card in the brief's own words: `text "Ship faster"`,
87
+ * `sound sfx`, `native device phone`. One line, for a card body that has
88
+ * no picture of its own. */
89
+ export function elementSummary(
90
+ item: Extract<ReferenceItem3D, { type: "text" | "transition" | "sound" | "native" }>,
91
+ ): string {
92
+ switch (item.type) {
93
+ case "text":
94
+ return `"${item.copy}"`;
95
+ case "transition":
96
+ return item.kind.replace(/^gl\//, "") + (item.durationFrames === undefined ? "" : ` · ${item.durationFrames} frames`);
97
+ case "sound":
98
+ return item.role + (item.asset === undefined ? " · no file yet" : "") + (item.hitMs ? ` · hit ${item.hitMs} ms` : "");
99
+ case "native":
100
+ return item.kind + (item.variant === undefined ? "" : ` · ${item.variant}`);
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Case-insensitive match over what a person would type for a shipped item:
106
+ * its name, its id, a tag or a vibe. `matchesComponent`'s rule, widened to
107
+ * the two lists the catalog row carries, because "premium" or "typography"
108
+ * is how the library skill tells an agent to choose and an author searches
109
+ * the same way.
110
+ *
111
+ * AND OVER THE PROSE THE CARD PRINTS. The library browser draws `description`,
112
+ * `useFor` and `avoidFor` on every card, and a search that cannot find the
113
+ * words on the card in front of you is a search that looks broken. Those three
114
+ * plus `category` are exactly what `get_library_item`'s own matcher already
115
+ * reads (packages/cli/src/mcpLibraryTools.ts), so widening here makes the
116
+ * rail, the browser and the agent answer one question one way instead of the
117
+ * three spellings this repo had.
118
+ */
119
+ export function matchesLibraryItem(row: LibraryItem, query: string): boolean {
120
+ const q = query.trim().toLowerCase();
121
+ if (q === "") return true;
122
+ const haystack = [
123
+ row.id,
124
+ row.name,
125
+ row.category ?? "",
126
+ row.useFor ?? "",
127
+ row.avoidFor ?? "",
128
+ row.description ?? "",
129
+ ...row.tags,
130
+ ...(row.vibe ?? []),
131
+ ]
132
+ .join(" ")
133
+ .toLowerCase();
134
+ return haystack.includes(q);
135
+ }
136
+
137
+ /** A link pin's address as a person reads it: the host, without `www.`. The
138
+ * validator admits only `https:` addresses, so a parse failure is a document
139
+ * that skipped the gate, and it reads as the raw text. Links are never
140
+ * fetched. */
141
+ export function linkHost(url: string): string {
142
+ try {
143
+ return new URL(url).hostname.replace(/^www[.]/, "");
144
+ } catch {
145
+ return url;
146
+ }
147
+ }
148
+
149
+ /** A stored name without its content hash: `1a2b3c4d-logo.png` reads `logo.png`. */
150
+ export function storedName(path: string): string {
151
+ return path.replace(/^[0-9a-f]{8}-/u, "");
152
+ }
153
+
154
+ /** Why a registry component on a host other than three is not placed: an
155
+ * instance layer only renders a three component. */
156
+ export function instanceHostRefusal(host: string): string {
157
+ return `A "${host}" component cannot be an instance layer: carry its look with text, image or html layers.`;
158
+ }
159
+
160
+ // ── A v2: the board as rows BY SHOT ───────────────────────────────────────────
161
+ //
162
+ // The frame and the strip read the film Shot by Shot, in playing order: each
163
+ // Shot with its planned shot (when one links it), its master window, its
164
+ // stills, the gaps between them and the cut into the next Shot. Derived once,
165
+ // pure, so the subject, the menu rows and the tests read one answer.
166
+
167
+ export interface BoardShotCut {
168
+ /** `cut_<this Shot>`. */
169
+ id: string;
170
+ /** The next Shot in playing order. */
171
+ incoming: string;
172
+ /** Whether a transition blends here (`cutPlays`). */
173
+ plays: boolean;
174
+ }
175
+
176
+ export interface BoardShot {
177
+ sceneId: string;
178
+ scene: Scene3D;
179
+ /** The planned shot that links this Shot, or null. */
180
+ part: StoryboardPart3D | null;
181
+ /** Master frames, `end` exclusive. */
182
+ window: SceneWindow;
183
+ stills: VisibleStill[];
184
+ gaps: StillGap[];
185
+ /** The cut into the next Shot; null on the last. */
186
+ cut: BoardShotCut | null;
187
+ }
188
+
189
+ export function boardShots(doc: Doc3D): BoardShot[] {
190
+ const parts = doc.storyboard?.parts ?? [];
191
+ const windows = sceneWindows(doc);
192
+ return windows.map((window, at): BoardShot => {
193
+ const scene = doc.scenesById[window.id]!;
194
+ const stills = visibleStills(scene);
195
+ const next = windows[at + 1];
196
+ return {
197
+ sceneId: window.id,
198
+ scene,
199
+ part: parts.find((p) => p.scene === window.id) ?? null,
200
+ window,
201
+ stills,
202
+ gaps: gapsOf(stills),
203
+ cut: next === undefined ? null : { id: cutIdOf(window.id), incoming: next.id, plays: cutPlays(doc, window.id, next.id) },
204
+ };
205
+ });
206
+ }
207
+
208
+ /** One entry of the strip: a built Shot, or a planned shot
209
+ * with no Shot yet, drawn where the plan puts it. */
210
+ export type StoryEntry =
211
+ | {
212
+ kind: "shot";
213
+ shot: BoardShot;
214
+ /** The planned shot's label when one links it, else the Shot's name. */
215
+ name: string;
216
+ /** The chip right after this card: the cut out of this Shot, where its
217
+ * transition is saved. Null on the last. A placeholder after the chip
218
+ * does not take it: the cut still plays into the next built Shot. */
219
+ cut: { id: string; label: string } | null;
220
+ }
221
+ | { kind: "placeholder"; part: StoryboardPart3D };
222
+
223
+ /**
224
+ * THE FILM AS THE BOARD READS IT: every Shot in playing order, and each
225
+ * planned shot that has no Shot where Make shot would put it, in shot's
226
+ * `boardSlots` order, the one `screenshot_shot { sketches: "all" }` draws in
227
+ * too. Pure, so the shot row and the tests read one order.
228
+ */
229
+ export function storyEntries(doc: Doc3D): StoryEntry[] {
230
+ const shots = new Map(boardShots(doc).map((shot) => [shot.sceneId, shot]));
231
+ return boardSlots(doc).map((slot): StoryEntry => {
232
+ if (slot.sceneId === null) return { kind: "placeholder", part: slot.part };
233
+ // Non-null: both read `sceneWindows`, one entry per Shot.
234
+ const shot = shots.get(slot.sceneId)!;
235
+ const cut = shot.cut === null ? null : { id: shot.cut.id, label: cutLabel(doc, shot) };
236
+ return { kind: "shot", shot, name: shot.part?.label ?? shot.scene.name, cut };
237
+ });
238
+ }
239
+
240
+ /** A cut chip's words: the transition's preset and its played length where
241
+ * one blends (`cutPlays`), "Cut" where none does. */
242
+ export function cutLabel(doc: Doc3D, shot: BoardShot): string {
243
+ const transition = shot.scene.transition;
244
+ if (shot.cut === null || !shot.cut.plays || transition === undefined) return "Cut";
245
+ const name = resolveGlPresetSpec(transition.kind)?.label ?? transition.kind;
246
+ const frames = seamWindow(doc, shot.sceneId)?.durationFrames ?? transition.durationFrames;
247
+ return `${name} ${formatDuration(framesToSeconds(frames, doc.fps))}`;
248
+ }
249
+
250
+ /** What the board's one subject is: a still, a gap or a cut, each in its
251
+ * Shot. Derived from the selection, never stored, so an edit that removes
252
+ * the thing it names leaves nothing stale to clean up. */
253
+ export type BoardSubject =
254
+ | { kind: "still"; id: string; sceneId: string; still: VisibleStill }
255
+ | { kind: "gap"; id: string; sceneId: string; gap: StillGap }
256
+ | { kind: "cut"; id: string; sceneId: string; cut: BoardShotCut };
257
+
258
+ /** The subject an id names in this document, or null when it names none (an
259
+ * unknown id, a still a trim hid, a cut out of the last Shot). */
260
+ export function boardSubjectAt(doc: Doc3D, id: string): BoardSubject | null {
261
+ if (!isStillId(id) && !isGapId(id) && !isCutId(id)) return null;
262
+ for (const shot of boardShots(doc)) {
263
+ if (isStillId(id)) {
264
+ const still = shot.stills.find((s) => s.id === id);
265
+ if (still !== undefined) return { kind: "still", id, sceneId: shot.sceneId, still };
266
+ } else if (isGapId(id)) {
267
+ const gap = shot.gaps.find((g) => g.id === id);
268
+ if (gap !== undefined) return { kind: "gap", id, sceneId: shot.sceneId, gap };
269
+ } else if (shot.cut?.id === id) {
270
+ return { kind: "cut", id, sceneId: shot.sceneId, cut: shot.cut };
271
+ }
272
+ }
273
+ return null;
274
+ }
275
+
276
+ /** The board's subject: the selection's one id, when it names a still, a gap
277
+ * or a cut. A multi-selection has none. */
278
+ export function boardSubjectOf(doc: Doc3D | null, selection: readonly string[]): BoardSubject | null {
279
+ if (doc === null || selection.length !== 1) return null;
280
+ return boardSubjectAt(doc, selection[0]!);
281
+ }
282
+
283
+ /** A Shot's name on the board: its planned shot's label (`partOfScene`),
284
+ * else the Shot's own name. What "Between 02 and 03" says. */
285
+ export function shotNameOf(doc: Doc3D, sceneId: string): string {
286
+ return partOfScene(doc, sceneId)?.label ?? doc.scenesById[sceneId]?.name ?? sceneId;
287
+ }
288
+
289
+ /** A Shot's two-digit number as the strip counts it ("02"): its place in
290
+ * `storyEntries`, placeholders included, so "Between 02 and 03" and the
291
+ * Notes head name the cards the author sees. Null for a Shot the strip does not draw. */
292
+ export function shotNumberOf(doc: Doc3D, sceneId: string): string | null {
293
+ const at = storyEntries(doc).findIndex((e) => e.kind === "shot" && e.shot.sceneId === sceneId);
294
+ return at < 0 ? null : String(at + 1).padStart(2, "0");
295
+ }
296
+
297
+ /** A still's name: its label, else "Frame N" by its place in the Shot's
298
+ * stills. What a move's "from → to" head says. */
299
+ export function stillNameOf(stills: readonly VisibleStill[], frame: number): string {
300
+ const at = stills.findIndex((s) => s.frame === frame);
301
+ const label = at < 0 ? "" : (stills[at]!.label?.trim() ?? "");
302
+ return label !== "" ? label : `Frame ${at < 0 ? stills.length + 1 : at + 1}`;
303
+ }
304
+
305
+ /**
306
+ * THE MOVE SECTION'S AGENT ASK ("Follow the motion notes", ON): restage the
307
+ * gap's keys of Shot `sceneId` over `[from, to]` (Shot-local) by its planned
308
+ * shot's numbered How it moves notes. Prefilled, never sent: the author reads
309
+ * it and presses send. The rules are what the section reads back: keys
310
+ * outside the gap are flagged, and a track whose ends sit on the stills reads
311
+ * as following no notes.
312
+ */
313
+ export function movePrompt(doc: Doc3D, sceneId: string, from: number, to: number): string {
314
+ const scene = doc.scenesById[sceneId];
315
+ const beats = partOfScene(doc, sceneId)?.motion ?? [];
316
+ const lines = [
317
+ `Restage the move in Shot ${sceneId}${scene === undefined ? "" : ` ("${scene.name}")`} between frames ${from} and ${to} so it follows the motion notes.`,
318
+ "",
319
+ "How it moves:",
320
+ ...(beats.length === 0
321
+ ? ["(no notes yet: stagger the moving layers in a readable order)"]
322
+ : beats.map((beat, at) => {
323
+ const pin = beat.layer === undefined ? "" : ` (layer ${beat.layer}${beat.path === undefined ? "" : `, track ${beat.path}`})`;
324
+ return `${at + 1}. ${beat.text}${pin}`;
325
+ })),
326
+ "",
327
+ "Rules:",
328
+ `- Every key you write sits inside frames ${from} to ${to}, on whole frames of the Shot's own clock.`,
329
+ "- Give every key an explicit easeOut.",
330
+ "- The last key of each track holds its value.",
331
+ `- When a track has keys before frame ${from}, keep a key at frame ${from}.`,
332
+ "- Write it all in one shot_batch.",
333
+ ];
334
+ return lines.join("\n");
335
+ }
336
+
337
+ /** "Match Sketch 1 at 0.00s. Animate toward Sketch 2 by 1.60s.": what a
338
+ * part's sketches ask of the build, each by its Shot-local time when it has
339
+ * one. Empty for a part with no sketches. */
340
+ export function sketchTaskOf(doc: Doc3D, part: StoryboardPart3D): string {
341
+ const sketches = part.sketches ?? [];
342
+ const at = (frame: number | undefined, word: string): string =>
343
+ frame === undefined ? "" : ` ${word} ${formatDuration(framesToSeconds(frame, doc.fps))}`;
344
+ if (sketches.length === 0) return "";
345
+ const first = `Match Sketch 1${at(sketches[0]!.frame, "at")}.`;
346
+ const later = sketches.slice(1).map((sketch, i) => `Sketch ${i + 2}${at(sketch.frame, "by")}`);
347
+ return later.length === 0 ? first : `${first} Animate toward ${later.join(", then ")}.`;
348
+ }
349
+
350
+ /**
351
+ * "BUILD PLANNED SHOTS" (S1): ask the agent to build these planned shots in
352
+ * the Studio, by their stored ids, each with what the board says about it.
353
+ * Prefilled like `movePrompt`. The board marks the parts Building while the
354
+ * run is out (`boardBuilding`, session state, never saved). One part is the
355
+ * shot board's "Ask agent": it leads with the task for that shot alone.
356
+ */
357
+ export function buildPlannedPrompt(doc: Doc3D, partIds: readonly string[]): string {
358
+ const parts = (doc.storyboard?.parts ?? []).filter((p) => partIds.includes(p.id));
359
+ const one = parts.length === 1 ? parts[0]! : null;
360
+ const lines =
361
+ one === null ? [`Build these ${parts.length} planned shots from the storyboard in the Studio, in order:`] : [];
362
+ for (const part of parts) {
363
+ // A part that links a Shot is built INTO it: add_scene refuses its part
364
+ // (`already-planned`). A link to a Shot that is gone reads as none. A
365
+ // linked part names both lengths: the Shot's is the one the board shows.
366
+ const shot = part.scene === undefined ? undefined : doc.scenesById[part.scene];
367
+ const lengthOf = (frames: number): string => formatDuration(framesToSeconds(frames, doc.fps));
368
+ const length =
369
+ shot === undefined
370
+ ? lengthOf(part.durationFrames)
371
+ : `planned ${lengthOf(part.durationFrames)}, its Shot runs ${lengthOf(shot.durationFrames)}`;
372
+ const task = sketchTaskOf(doc, part);
373
+ const title = `${partTitleOf(doc, part.id) ?? part.label} (#${part.id}), ${length}.${task === "" ? "" : ` ${task}`}`;
374
+ lines.push(one === null ? `- ${title}` : `Task: build ${title}`);
375
+ if (part.notes !== undefined) lines.push(` What we see: ${part.notes.trim().replace(/\s+/g, " ")}`);
376
+ if (part.voice !== undefined) lines.push(` Voice: “${part.voice}”`);
377
+ const sketches = part.sketches ?? [];
378
+ if (sketches.length > 0) {
379
+ lines.push(
380
+ ` Sketches: ${sketches.map((sketch, i) => `${sketch.id} (Sketch ${i + 1})`).join(", ")}. ` +
381
+ "Look at one with screenshot_shot and its sketch id.",
382
+ );
383
+ }
384
+ lines.push(
385
+ shot === undefined
386
+ ? ` No Shot yet: make it with add_scene and part "#${part.id}".`
387
+ : ` Its Shot: ${shot.name} (#${part.scene}). Build into it, never add_scene for it.`,
388
+ );
389
+ if (part.built !== undefined) {
390
+ lines.push(` Already marked built at frame ${part.built.frame} of its Shot: change it only if asked.`);
391
+ }
392
+ }
393
+ lines.push(
394
+ "",
395
+ "Read the whole board first with get_storyboard: the Style cards guide every shot, a card pinned to a part guides only that one, and each sketch is printed as layout text in layer units.",
396
+ parts.some((part) => (part.sketches?.length ?? 0) > 0)
397
+ ? "Match Sketch 1's positions and sizes, animate toward the later sketches by their times, and keep the Style. Then check your frames with screenshot_shot."
398
+ : "Keep the Style. Then check your frames with screenshot_shot.",
399
+ "A part with no Shot gets one from add_scene carrying its part ({ op: \"add_scene\", part: \"#part_id\" }): that links it and places it in the plan's order, in one write.",
400
+ "Never add_scene for a part that already has a Shot. Build each Shot in one shot_batch.",
401
+ );
402
+ return lines.join("\n");
403
+ }
404
+
405
+ /** A shot card's status (D5): Built once "Mark shot built" ran, Building while
406
+ * this session's agent run is out for it, else Planned. Never stored. */
407
+ export type ShotStatus = "planned" | "building" | "built";
408
+
409
+ export function shotStatusOf(part: StoryboardPart3D | null, building: readonly { part: string }[]): ShotStatus {
410
+ if (part === null) return "planned";
411
+ if (part.built !== undefined) return "built";
412
+ return building.some((b) => b.part === part.id) ? "building" : "planned";
413
+ }
414
+
415
+ export const SHOT_STATUS_LABEL: Record<ShotStatus, string> = { planned: "Planned", building: "Building", built: "Built" };
416
+
417
+ /** A shot card's length in frames (decision 1, agent experience plan): its
418
+ * linked Shot's (`partFramesOf`, the length every agent text promises), else
419
+ * the plan's. "Mark shot built" sets the picture and the Built status, never
420
+ * the length. A Shot no part links has only its own. */
421
+ export function shotLengthOf(doc: Doc3D, part: StoryboardPart3D | null, scene: Scene3D | null): number {
422
+ return part === null ? (scene?.durationFrames ?? 0) : partFramesOf(doc, part);
423
+ }
424
+
425
+ /** The board's Style row: the cards no planned shot holds, in rank order. */
426
+ export function styleItemsOf(doc: Doc3D): ReferenceItem3D[] {
427
+ return referencesByPart(doc).global.references;
428
+ }
429
+
430
+ /** The first image card Sketch 1 draws (v44), the card picture until the
431
+ * board draws a sketch itself. A migrated part picture is exactly this. */
432
+ export function sketchPictureOf(doc: Doc3D, part: StoryboardPart3D): ShotAsset | undefined {
433
+ for (const item of part.sketches?.[0]?.items ?? []) {
434
+ if (item.kind !== "ref") continue;
435
+ const card = doc.resources?.references?.find((r) => r.id === item.ref);
436
+ const asset = card?.type === "asset" ? doc.assets[card.asset] : undefined;
437
+ if (asset?.kind === "image") return asset;
438
+ }
439
+ return undefined;
440
+ }
441
+
442
+ /** A planned shot as the board's cards name it ("03 Make it move"): its
443
+ * place in `storyEntries`, then its label. Null for a part that is gone. */
444
+ export function partTitleOf(doc: Doc3D, partId: string): string | null {
445
+ const part = doc.storyboard?.parts.find((p) => p.id === partId);
446
+ if (part === undefined) return null;
447
+ const at = storyEntries(doc).findIndex((e) => (e.kind === "shot" ? e.shot.part?.id : e.part.id) === partId);
448
+ return at < 0 ? part.label : `${String(at + 1).padStart(2, "0")} ${part.label}`;
449
+ }
450
+
451
+ /** What the drop dialog's scope writes. */
452
+ export type DropScope = "style" | "shot" | "keep";
453
+
454
+ /**
455
+ * THE DROP DIALOG'S "AGENT READS" LINE, in `storyboardBrief`'s own words:
456
+ * cards dropped with one note print as "Together (N cards). Note: …" under
457
+ * Global or under their planned shot. Keep makes no card, so the brief says
458
+ * nothing about it.
459
+ */
460
+ export function dropBriefLine(count: number, note: string, scope: DropScope, partLabel: string | null): string {
461
+ if (scope === "keep") return "Agent reads nothing yet. Kept files wait in Assets until a card uses them.";
462
+ const where = scope === "style" ? "Global, the whole reel" : (partLabel ?? "the shot you pick");
463
+ const text = note.trim().replace(/\s+/g, " ");
464
+ const cards = count === 1 ? "1 card" : `Together (${count} cards)`;
465
+ return `Agent reads, under ${where}: ${cards}.${text === "" ? " No note yet: say why so it knows what to take." : ` Note: ${text}`}`;
466
+ }
467
+
468
+ /** The planned shot the board is adding to: the open shot board's
469
+ * (`storyboardPlan`), else null, which is Style. Where a board Add, a paste
470
+ * and Select all land. The board enters no Shot, so the plan is the only
471
+ * thing that can name one. */
472
+ export function boardTargetOf(s: { doc: Doc3D | null; storyboardPlan: string | null }): string | null {
473
+ if (s.doc === null || s.storyboardPlan === null) return null;
474
+ return s.doc.storyboard?.parts.some((p) => p.id === s.storyboardPlan) === true ? s.storyboardPlan : null;
475
+ }
476
+
477
+ /** The shot board's view state, as `sketchViewIn` revalidates it. */
478
+ export interface SketchView {
479
+ storyboardPlan: string | null;
480
+ storyboardSketch: string | null;
481
+ sketchSelection: readonly string[];
482
+ sketchPlaying: boolean;
483
+ sketchGuide: { partId: string; sketchId: string } | null;
484
+ }
485
+
486
+ /**
487
+ * THE SHOT BOARD'S VIEW AFTER A WRITE: only the keys that must change, so a
488
+ * write that touched nothing here patches nothing. A delete, an undo or an
489
+ * agent write can take the plan, its sketch or an item away:
490
+ *
491
+ * - a plan whose part is gone closes, with everything under it;
492
+ * - a sketch that is gone gives way to the plan's first, and a plan that
493
+ * gains its first sketch (an agent's `set_sketch`) shows it;
494
+ * - the selection keeps only items of the sketch on screen;
495
+ * - Play stops with no sketch to play;
496
+ * - the Studio guide goes with its sketch.
497
+ */
498
+ export function sketchViewIn(doc: Doc3D, s: SketchView): Partial<SketchView> {
499
+ const out: Partial<SketchView> = {};
500
+ const part = s.storyboardPlan === null ? undefined : doc.storyboard?.parts.find((p) => p.id === s.storyboardPlan);
501
+ if (s.storyboardPlan !== null && part === undefined) out.storyboardPlan = null;
502
+ const sketches = part === undefined ? [] : sketchesOf(doc, part.id);
503
+ const sketch = sketches.find((k) => k.id === s.storyboardSketch) ?? sketches[0];
504
+ const sketchId = sketch?.id ?? null;
505
+ if (sketchId !== s.storyboardSketch) out.storyboardSketch = sketchId;
506
+ const items = new Set(sketch?.items.map((item) => item.id) ?? []);
507
+ const kept = s.sketchSelection.filter((id) => items.has(id));
508
+ if (kept.length !== s.sketchSelection.length) out.sketchSelection = kept;
509
+ if (s.sketchPlaying && sketch === undefined) out.sketchPlaying = false;
510
+ const guide = s.sketchGuide;
511
+ if (guide !== null && !sketchesOf(doc, guide.partId).some((k) => k.id === guide.sketchId)) out.sketchGuide = null;
512
+ return out;
513
+ }
514
+
515
+ /**
516
+ * A NEW SHOT'S DEFAULT NAME: "Shot N" from the count of Shots and planned
517
+ * shots, stepped past any name a Shot or a planned shot already has, so two
518
+ * cards never read the same.
519
+ */
520
+ export function nextShotName(doc: Doc3D): string {
521
+ const parts = doc.storyboard?.parts ?? [];
522
+ const taken = new Set<string>(parts.map((p) => p.label));
523
+ for (const id of doc.scenes) taken.add(doc.scenesById[id]?.name ?? "");
524
+ for (let n = Math.max(doc.scenes.length, parts.length) + 1; ; n += 1) {
525
+ if (!taken.has(`Shot ${n}`)) return `Shot ${n}`;
526
+ }
527
+ }
528
+