@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,593 @@
1
+ // One selected card, LEADING WITH WHAT IT IS AND WHERE IT SITS IN THE PLAN:
2
+ // the picture, its lane, when that plays, and, for a requirement, whether the
3
+ // video has it (the status, Place and the Done tick, `RequirementRows`). Then
4
+ // what the card names, kind by kind: a text card's copy, a transition's kind
5
+ // and length, a sound's role, file and hit, a native card's kind and variant.
6
+ // Then what it is for and how hard it binds (`CardUseRows`, the block every
7
+ // board panel shares), and the note. What is left is one fold down, under
8
+ // More, and More shows only when the card has something there.
9
+ //
10
+ // NEVER A COMPONENT CARD. One component card selected is the component sheet's
11
+ // (`boardRightPanel`), which carries its stage, its props, its Frame, Use and
12
+ // Importance; the branches this section kept for one were unreachable in the
13
+ // product and are gone.
14
+ //
15
+ // THE NAME ROW IS GONE. `InspectorHead` renames a reference in place, which is
16
+ // the design system's stated rule: a panel names its subject before it lists
17
+ // its properties, so restating the name as a form row two lines below its own
18
+ // heading is the panel saying one thing twice.
19
+ //
20
+ // THE PREVIEW LEADS, through the same `ReferencePreview` the rail's tiles
21
+ // render. One component, so the tile and the lead cannot drift into two
22
+ // answers about one card. A video's lead is its still (`VideoStill`), which
23
+ // owns the box and says under it when its frame is past the recording's end.
24
+ //
25
+ // EVERY READ-ONLY VALUE is a `ReadoutRow`, the house readout, and every
26
+ // number a person drags has its scrub half (`useScrub`), so the board's other
27
+ // panels see the value move with the pointer. The rows and their buttons are
28
+ // the house's (widgets.css), so the sections need no scope of their own.
29
+ import { useEffect, useRef, useState } from "react";
30
+ import {
31
+ DEVICE_PRESET_IDS,
32
+ MESH_PRIMITIVE_KINDS,
33
+ NATIVE_LAYER_KINDS,
34
+ SOUND_ROLES,
35
+ partWindowsOf,
36
+ updateReference,
37
+ } from "@dailies-studio/shot/doc";
38
+ import type { Doc3D, ReferenceItem3D } from "@dailies-studio/shot/doc";
39
+ import { framesToSeconds } from "@dailies-studio/engine";
40
+ import { glPresetSpecs } from "@dailies-studio/transitions";
41
+ import { Combobox, FieldLabels, InspectorRow, InspectorSection, NumberField, SegmentedControl, TextField, useLocked } from "@dailies-studio/ui";
42
+ import { useShotEditor, useStore } from "../../../state/context.js";
43
+ import { isReferenceId } from "../../../state/storyboardModel.js";
44
+ import { CardNoteRow, CardUseRows, ReadoutRow, RequirementRows } from "../../storyboard/boardRows.js";
45
+ import { PartPicker } from "../../storyboard/PartPicker.js";
46
+ import { ReferencePreview } from "../../storyboard/ReferencePreview.js";
47
+ import { AUDIO_ACCEPT_ATTRIBUTE } from "../../storyboard/storyboardDrop.js";
48
+ import { VideoStill } from "./VideoStill.js";
49
+ import { formatDuration, timecode } from "../../timeline/model.js";
50
+ import { useScrub } from "../useDocWrite.js";
51
+
52
+ /** A typed or dragged whole number at or above `floor`, or null for a value
53
+ * that is no number. What every number row here writes. */
54
+ function wholeAtLeast(raw: unknown, floor: number): number | null {
55
+ return typeof raw === "number" && Number.isFinite(raw) ? Math.max(floor, Math.round(raw)) : null;
56
+ }
57
+
58
+ /** The last frame a video card can name: the recording's probed length in
59
+ * the Project's frames, the frame that starts before its end. Undefined when
60
+ * nobody has probed it, and the still says so instead if the frame is past
61
+ * it (`storyboardVideoSeconds`). */
62
+ export function videoFrameMax(seconds: number | undefined, fps: Doc3D["fps"]): number | undefined {
63
+ if (seconds === undefined || !Number.isFinite(seconds) || seconds <= 0) return undefined;
64
+ return Math.max(0, Math.ceil((seconds * fps.num) / fps.den) - 1);
65
+ }
66
+
67
+ export function StoryboardReferenceSection() {
68
+ const store = useStore();
69
+ const open = useShotEditor((s) => s.storyboardOpen);
70
+ const selection = useShotEditor((s) => s.storyboardSelection);
71
+ const doc = useShotEditor((s) => s.doc);
72
+ const moreOpen = useShotEditor((s) => s.referenceMoreOpen);
73
+ const id = open && selection.length === 1 && isReferenceId(selection[0]!) ? selection[0]! : null;
74
+ const item = id === null ? undefined : doc?.resources?.references?.find((r) => r.id === id);
75
+ const libraryId = item?.type === "library" ? item.library : null;
76
+ // A LIBRARY CARD'S TWO SOURCES: the catalog row (is it in this build, what
77
+ // is it for, how long) and the item detail (its README, its ops). Both are
78
+ // selected by stable identity, so neither re-renders this panel on an
79
+ // unrelated store write.
80
+ const catalogLoaded = useShotEditor((s) => s.libraryCatalogLoaded);
81
+ const shipped = useShotEditor((s) => (libraryId === null ? undefined : s.libraryCatalog.find((row) => row.id === libraryId)));
82
+ const detail = useShotEditor((s) => (libraryId === null ? undefined : s.libraryItems[libraryId]));
83
+ // THE THIRD ANSWER TO "IS IT IN THIS BUILD". The registry omits an item it
84
+ // refused from the catalog, so without this row an item this build ships
85
+ // BROKEN reads as one it never shipped, and the notice below would offer a
86
+ // confident wrong diagnosis of a local fault the server already named.
87
+ const refused = useShotEditor((s) =>
88
+ libraryId === null ? undefined : s.libraryCatalogProblems.find((problem) => problem.id === libraryId),
89
+ );
90
+ // ONE VERB AT A TIME. A claim copies a whole Project and an import uploads a
91
+ // pack's files, neither is instant, and nothing else here disables the row:
92
+ // a second press on a claim mints a SECOND Project the generation fence then
93
+ // discards without a word. `StoryboardAddBar.run`'s shape.
94
+ const [busy, setBusy] = useState(false);
95
+ const run = (work: Promise<void>): void => {
96
+ setBusy(true);
97
+ void work.finally(() => setBusy(false));
98
+ };
99
+ // A VIEWER'S PANEL LOCKS EVERY VERB THAT WRITES. `LockedControls` reaches
100
+ // the widgets; these are plain buttons, so they read the lock themselves.
101
+ const locked = useLocked();
102
+
103
+ // HOOKS BEFORE THE EARLY RETURNS, the marker section's rule.
104
+ // The catalog first, because it answers whether this build ships the item
105
+ // at all; the detail only for a row the catalog has, since a 404 for an id
106
+ // the missing notice already explains would be noise in the error strip.
107
+ useEffect(() => {
108
+ if (libraryId === null) return;
109
+ if (!catalogLoaded) {
110
+ void store.refreshLibraryCatalog();
111
+ return;
112
+ }
113
+ if (shipped !== undefined) void store.loadLibraryItem(libraryId);
114
+ }, [libraryId, catalogLoaded, shipped, store]);
115
+ // The video's Frame, dragged: the still follows the pointer.
116
+ const frameScrub = useScrub((d, raw) => {
117
+ const frame = wholeAtLeast(raw, 0);
118
+ return id === null || frame === null ? d : updateReference(d, id, { frame });
119
+ });
120
+
121
+ if (doc === null || id === null || item === undefined) return null;
122
+ const parts = doc.storyboard?.parts ?? [];
123
+ const asset = item.type === "asset" ? doc.assets[item.asset] : undefined;
124
+ const isVideo = asset?.kind === "video";
125
+
126
+ // WHEN THE ATTACHED PART PLAYS. Derived from the prefix sum, never stored,
127
+ // so it cannot drift from a divider drag on the bar.
128
+ const window = item.part === undefined ? undefined : partWindowsOf(parts).find((w) => w.id === item.part);
129
+ // The loaded catalog has no row at this card's id. Only a LOADED catalog
130
+ // may say so: before it answers, "missing" would be a claim nobody made.
131
+ const libraryMissing = item.type === "library" && catalogLoaded && shipped === undefined;
132
+ // WHICH KIND THE PANEL ACTS ON. A card's `kind` is validated against the
133
+ // enum and never against the id it names, so `shot` can sit over a shipped
134
+ // blueprint. The registry owns the answer, exactly as the agent brief has
135
+ // it (`storyboard.ts`): dispatching on the document offered "Claim as
136
+ // Project" for a blueprint, which the server refuses with a 400, and left
137
+ // "Copy ops" disabled forever with "Waiting for the item's ops" for a
138
+ // material or a shot cited as `blueprint`. The document's kind is the
139
+ // fallback for a card whose row has not arrived, and the disagreement is
140
+ // SAID rather than silently resolved: the card is what an author fixes.
141
+ const libraryKind = item.type !== "library" ? undefined : (shipped?.kind ?? item.kind);
142
+ const kindDisagrees = item.type === "library" && shipped?.kind !== undefined && shipped.kind !== item.kind;
143
+ // A blueprint is copied by hand: its ops, with `$name` placeholders the
144
+ // author substitutes before applying them. The clipboard is the door, and
145
+ // its absence is said rather than swallowed.
146
+ const copyOps = (): void => {
147
+ const ops = detail?.blueprint?.ops;
148
+ if (ops === undefined) return;
149
+ const clipboard = typeof navigator === "undefined" ? undefined : navigator.clipboard;
150
+ if (clipboard === undefined) {
151
+ store.reportError("The clipboard is not available here, so the ops could not be copied.");
152
+ return;
153
+ }
154
+ void clipboard
155
+ .writeText(JSON.stringify(ops, null, 2))
156
+ .catch((e: unknown) => store.reportError(e instanceof Error ? e.message : String(e)));
157
+ };
158
+ // WHAT IS UNDER MORE: a video's Frame, where a still came from, a library
159
+ // item's docs. A card with none of them has no fold at all: an empty More
160
+ // opened onto nothing.
161
+ const more = (item.type === "asset" && (isVideo || item.source !== undefined)) || item.type === "library";
162
+ const frame = item.type === "asset" ? (item.frame ?? 0) : 0;
163
+ const frameMax = videoFrameMax(asset?.media?.durationSeconds, doc.fps);
164
+
165
+ return (
166
+ <>
167
+ <InspectorSection title="Reference" persistKey="shot:storyboard-reference" collapsible={false}>
168
+ {/* THE PICTURE FIRST. What a card IS, before any property of it. */}
169
+ {item.type === "asset" && isVideo && asset !== undefined ? (
170
+ <VideoStill id={id} assetId={item.asset} path={asset.path} frame={frame} fps={doc.fps} label={item.label} />
171
+ ) : (
172
+ <div className="insp-reference-lead">
173
+ <ReferencePreview
174
+ item={item}
175
+ {...(asset === undefined ? {} : { asset })}
176
+ fps={doc.fps}
177
+ {...(shipped?.thumb === undefined ? {} : { libraryThumb: shipped.thumb })}
178
+ {...(libraryKind === undefined ? {} : { libraryKind })}
179
+ {...(libraryMissing ? { libraryMissing } : {})}
180
+ />
181
+ </div>
182
+ )}
183
+ <InspectorRow label="Lane">
184
+ <PartPicker
185
+ parts={parts}
186
+ value={item.part ?? null}
187
+ noneLabel="Global"
188
+ ariaLabel="Lane"
189
+ onChange={(part) => store.updateReference(id, { part })}
190
+ />
191
+ </InspectorRow>
192
+ {window === undefined ? null : (
193
+ <ReadoutRow label="Plays">
194
+ {timecode(window.start, doc.fps)} to {timecode(window.end, doc.fps)}
195
+ </ReadoutRow>
196
+ )}
197
+ <RequirementRows doc={doc} item={item} />
198
+ <ElementRows doc={doc} item={item} />
199
+ {/* The two halves the old single Priority control conflated, in the
200
+ block every board panel draws. */}
201
+ <CardUseRows card={item} />
202
+ {/* WHAT THE CARD NAMES, for a library pick: the item, and the two
203
+ lines the library skill tells an agent to choose by. Read from the
204
+ catalog row, never from the document, which carries only the id,
205
+ the version and the kind. */}
206
+ {item.type === "library" ? (
207
+ <>
208
+ <ReadoutRow label="Item" title={`${item.library} v${item.version}`}>
209
+ {libraryKind} · {item.library} v{item.version}
210
+ </ReadoutRow>
211
+ {/* THE CARD IS WRONG ABOUT ITS OWN ITEM, said where the author is
212
+ looking at it and in the brief's own words, so the panel and
213
+ the brief cannot give one card two answers. The verb below
214
+ follows the item, which is the only one the server accepts. */}
215
+ {kindDisagrees ? (
216
+ <div className="insp-notice is-warn">
217
+ This build ships a {libraryKind}, but this card says {item.kind}. The action below follows the item; rewrite
218
+ the card to agree with it.
219
+ </div>
220
+ ) : null}
221
+ {libraryMissing ? (
222
+ <div className="insp-notice is-warn">
223
+ {refused === undefined
224
+ ? "Not in this build's library. The card keeps its id and version; another build may ship it."
225
+ : // The registry read it and REFUSED it, which is a local
226
+ // fault with a stated fix, not an item another build has.
227
+ `This build ships the item but its library refused to read it. ${refused.error}`}
228
+ </div>
229
+ ) : null}
230
+ {shipped?.useFor === undefined ? null : (
231
+ <ReadoutRow label="Use for" prose>
232
+ {shipped.useFor}
233
+ </ReadoutRow>
234
+ )}
235
+ {shipped?.avoidFor === undefined ? null : (
236
+ <ReadoutRow label="Avoid for" prose>
237
+ {shipped.avoidFor}
238
+ </ReadoutRow>
239
+ )}
240
+ {shipped?.lengthFrames === undefined ? null : (
241
+ <ReadoutRow label="Length">
242
+ {formatDuration(framesToSeconds(shipped.lengthFrames, doc.fps))} · {shipped.lengthFrames}f
243
+ </ReadoutRow>
244
+ )}
245
+ </>
246
+ ) : null}
247
+ {item.type === "note" ? (
248
+ <InspectorRow label="Text" align="start">
249
+ <TextField
250
+ multiline
251
+ value={item.text}
252
+ ariaLabel="Note text"
253
+ onCommit={(text) => {
254
+ const trimmed = text.trim();
255
+ if (trimmed.length > 0 && trimmed !== item.text) store.updateReference(id, { text: trimmed });
256
+ }}
257
+ />
258
+ </InspectorRow>
259
+ ) : (
260
+ <CardNoteRow card={item} />
261
+ )}
262
+ <InspectorRow>
263
+ {/* THE ONE VERB A LIBRARY PICK HAS, beside Remove, by kind: a shot
264
+ becomes a Project, a material or an image pack lands in THIS
265
+ one, a blueprint is copied by hand. Disabled with the reason in
266
+ `title` while the item is missing or its ops have not arrived.
267
+ BY THE REGISTRY'S KIND (`libraryKind`), never the document's: an
268
+ item whose kind the card gets wrong would otherwise offer the
269
+ verb its server route refuses. An id the registry knows as a
270
+ `component` has no verb here at all: dropping one installs it
271
+ and the card becomes a `component` reference. */}
272
+ {item.type === "library" && libraryId !== null ? (
273
+ libraryKind === "shot" ? (
274
+ <button
275
+ type="button"
276
+ className="btn"
277
+ disabled={libraryMissing || busy || locked}
278
+ title={
279
+ libraryMissing
280
+ ? "This build does not ship the item"
281
+ : busy
282
+ ? "Copying the Project…"
283
+ : "Copy this shot into a new Project and open it"
284
+ }
285
+ onClick={() => run(store.claimLibraryShot(libraryId))}
286
+ >
287
+ Claim as Project
288
+ </button>
289
+ ) : libraryKind === "material" ? (
290
+ <button
291
+ type="button"
292
+ className="btn"
293
+ disabled={libraryMissing || busy || locked}
294
+ title={
295
+ libraryMissing
296
+ ? "This build does not ship the item"
297
+ : busy
298
+ ? "Writing into the Project…"
299
+ : "Add this preset to the Project's materials under a fresh id"
300
+ }
301
+ onClick={() => run(store.importLibraryIntoProject(libraryId, "material"))}
302
+ >
303
+ Import into this Project
304
+ </button>
305
+ ) : libraryKind === "images" ? (
306
+ <button
307
+ type="button"
308
+ className="btn"
309
+ disabled={libraryMissing || busy || locked}
310
+ title={
311
+ libraryMissing
312
+ ? "This build does not ship the item"
313
+ : busy
314
+ ? "Writing into the Project…"
315
+ : "Register every file in the pack as a Project asset"
316
+ }
317
+ onClick={() => run(store.importLibraryIntoProject(libraryId, "images"))}
318
+ >
319
+ Add to Project assets
320
+ </button>
321
+ ) : libraryKind === "blueprint" ? (
322
+ <button
323
+ type="button"
324
+ className="btn"
325
+ disabled={detail?.blueprint === undefined}
326
+ title={
327
+ libraryMissing
328
+ ? "This build does not ship the item"
329
+ : detail?.blueprint === undefined
330
+ ? "Waiting for the item's ops"
331
+ : "Copy the ops as JSON; substitute your layer ids for its placeholders, then apply them"
332
+ }
333
+ onClick={copyOps}
334
+ >
335
+ Copy ops
336
+ </button>
337
+ ) : null
338
+ ) : null}
339
+ <button type="button" className="btn is-danger" disabled={locked} onClick={() => store.deleteStoryboardItems([id])}>
340
+ Delete
341
+ </button>
342
+ </InspectorRow>
343
+ </InspectorSection>
344
+ {/* ── MORE ──────────────────────────────────────────────────────
345
+ COLLAPSED BY DEFAULT, held by the `referenceMoreOpen` view pref so
346
+ an author who works in these rows keeps them open. Everything in
347
+ here is real and none of it is what this panel is opened to
348
+ decide.
349
+
350
+ DELETE STAYS OUT OF THE FOLD, above this. A destructive verb behind
351
+ a disclosure is a verb an author cannot find. */}
352
+ {more ? (
353
+ <InspectorSection
354
+ title="More"
355
+ collapsed={!moreOpen}
356
+ onCollapsedChange={(collapsed) => store.setReferenceMoreOpen(!collapsed)}
357
+ >
358
+ {item.type === "asset" && isVideo ? (
359
+ <>
360
+ <InspectorRow>
361
+ <FieldLabels value="compact">
362
+ <NumberField
363
+ value={frame}
364
+ label="Frame"
365
+ ariaLabel="Frame, video"
366
+ min={0}
367
+ {...(frameMax === undefined ? {} : { max: frameMax })}
368
+ step={1}
369
+ onCommit={(next) => {
370
+ const at = wholeAtLeast(next, 0);
371
+ if (at !== null) store.updateReference(id, { frame: at });
372
+ }}
373
+ onScrubStart={frameScrub.start}
374
+ onScrub={frameScrub.move}
375
+ onScrubEnd={frameScrub.end}
376
+ />
377
+ </FieldLabels>
378
+ </InspectorRow>
379
+ <ReadoutRow label="Timecode">{timecode(frame, doc.fps)}</ReadoutRow>
380
+ </>
381
+ ) : null}
382
+ {item.type === "asset" && item.source !== undefined ? (
383
+ <ReadoutRow label="Source" prose>
384
+ {item.source.asset !== undefined
385
+ ? `Still from a video at frame ${item.source.frame}`
386
+ : `Captured from this Project at frame ${item.source.frame}`}
387
+ </ReadoutRow>
388
+ ) : null}
389
+ {/* THE README, AS PLAIN TEXT. A `<pre>` and not rendered markdown:
390
+ the docs are the agent's reading material, quoted here so an
391
+ author can see what the model will be told, and a second
392
+ markdown renderer in this chrome would be a second answer about
393
+ how that text looks. */}
394
+ {item.type === "library" ? (
395
+ detail === undefined ? (
396
+ <ReadoutRow label="Docs" prose>
397
+ {libraryMissing ? "No docs: this build does not ship the item." : "Loading the item's docs…"}
398
+ </ReadoutRow>
399
+ ) : (
400
+ <InspectorRow label="Docs" align="start" className="insp-readout">
401
+ <pre className="insp-docs">{detail.docs}</pre>
402
+ </InspectorRow>
403
+ )
404
+ ) : null}
405
+ </InspectorSection>
406
+ ) : null}
407
+ </>
408
+ );
409
+ }
410
+
411
+ const titleCase = (text: string): string => text.charAt(0).toUpperCase() + text.slice(1);
412
+
413
+ /**
414
+ * WHAT AN ELEMENT CARD NAMES, kind by kind. Every control writes through
415
+ * `updateReference`, whose patch keys are the schema's (`PATCH_KEYS`), and
416
+ * writes null for a quiet default so an untouched card carries no keys. A
417
+ * value chosen again is the same document there, so a re-pick writes nothing.
418
+ */
419
+ function ElementRows({ doc, item }: { doc: Doc3D; item: ReferenceItem3D }) {
420
+ const store = useStore();
421
+ const id = item.id;
422
+ const fileRef = useRef<HTMLInputElement | null>(null);
423
+ const [busy, setBusy] = useState(false);
424
+ const locked = useLocked();
425
+ // The drag halves of the two number rows below, by key.
426
+ const lengthScrub = useScrub((d, raw) => {
427
+ const durationFrames = wholeAtLeast(raw, 1);
428
+ return durationFrames === null ? d : updateReference(d, id, { durationFrames });
429
+ });
430
+ const hitScrub = useScrub((d, raw) => {
431
+ const ms = wholeAtLeast(raw, 0);
432
+ return ms === null ? d : updateReference(d, id, { hitMs: ms === 0 ? null : ms });
433
+ });
434
+ switch (item.type) {
435
+ case "text":
436
+ return (
437
+ <InspectorRow label="Copy" align="start">
438
+ <TextField
439
+ multiline
440
+ value={item.copy}
441
+ ariaLabel="Copy"
442
+ onCommit={(copy) => {
443
+ const next = copy.trim();
444
+ if (next.length > 0 && next !== item.copy) store.updateReference(id, { copy: next });
445
+ }}
446
+ />
447
+ </InspectorRow>
448
+ );
449
+ case "transition": {
450
+ const specs = Object.values(glPresetSpecs);
451
+ const known = specs.some((spec) => spec.id === item.kind);
452
+ return (
453
+ <>
454
+ <InspectorRow label="Kind">
455
+ <Combobox
456
+ options={[
457
+ ...specs.map((spec) => ({ value: spec.id, label: spec.label })),
458
+ ...(known ? [] : [{ value: item.kind, label: `${item.kind} (unknown)` }]),
459
+ ]}
460
+ value={item.kind}
461
+ ariaLabel="Transition"
462
+ onChange={(kind) => store.updateReference(id, { kind })}
463
+ />
464
+ </InspectorRow>
465
+ <InspectorRow>
466
+ <FieldLabels value="compact">
467
+ <NumberField
468
+ // An absent length is the preset's own, so the field shows the
469
+ // dash rather than a 0 its own minimum refuses.
470
+ value={item.durationFrames ?? Number.NaN}
471
+ label="Length"
472
+ ariaLabel="Length, transition frames"
473
+ min={1}
474
+ step={1}
475
+ unit="f"
476
+ onCommit={(frames) => {
477
+ const durationFrames = wholeAtLeast(frames, 1);
478
+ if (durationFrames !== null) store.updateReference(id, { durationFrames });
479
+ }}
480
+ onScrubStart={lengthScrub.start}
481
+ onScrub={lengthScrub.move}
482
+ onScrubEnd={lengthScrub.end}
483
+ />
484
+ </FieldLabels>
485
+ </InspectorRow>
486
+ </>
487
+ );
488
+ }
489
+ case "sound": {
490
+ const asset = item.asset === undefined ? undefined : doc.assets[item.asset];
491
+ const pick = (file: File): void => {
492
+ setBusy(true);
493
+ void store.setSoundFile(id, file).finally(() => setBusy(false));
494
+ };
495
+ return (
496
+ <>
497
+ <InspectorRow label="Role">
498
+ <SegmentedControl
499
+ options={SOUND_ROLES.map((value) => ({ value, label: value === "sfx" ? "Effect" : titleCase(value) }))}
500
+ value={item.role}
501
+ ariaLabel="Role"
502
+ onChange={(role) => store.updateReference(id, { role: role as typeof item.role })}
503
+ />
504
+ </InspectorRow>
505
+ <InspectorRow label="File" className="insp-file-row">
506
+ <span className="insp-dim is-text" title={asset?.path}>
507
+ {item.asset === undefined ? "No file yet" : asset === undefined ? "File missing" : asset.path.replace(/^[0-9a-f]{8}-/u, "")}
508
+ </span>
509
+ <button type="button" className="btn" disabled={busy || locked} onClick={() => fileRef.current?.click()}>
510
+ {item.asset === undefined ? "Choose file…" : "Replace…"}
511
+ </button>
512
+ {item.asset === undefined ? null : (
513
+ <button
514
+ type="button"
515
+ className="btn"
516
+ disabled={locked}
517
+ title="Back to a named need: the card keeps its role and loses its file and hit"
518
+ onClick={() => store.updateReference(id, { asset: null })}
519
+ >
520
+ Clear
521
+ </button>
522
+ )}
523
+ <input
524
+ ref={fileRef}
525
+ type="file"
526
+ hidden
527
+ accept={AUDIO_ACCEPT_ATTRIBUTE}
528
+ onChange={(event) => {
529
+ const file = event.currentTarget.files?.[0];
530
+ event.currentTarget.value = "";
531
+ if (file !== undefined) pick(file);
532
+ }}
533
+ />
534
+ </InspectorRow>
535
+ {item.asset === undefined ? null : (
536
+ <InspectorRow>
537
+ <FieldLabels value="compact">
538
+ <NumberField
539
+ value={item.hitMs ?? 0}
540
+ label="Hit"
541
+ ariaLabel="Hit, where the sound lands in milliseconds"
542
+ min={0}
543
+ step={10}
544
+ unit="ms"
545
+ onCommit={(ms) => {
546
+ const next = wholeAtLeast(ms, 0);
547
+ if (next !== null) store.updateReference(id, { hitMs: next === 0 ? null : next });
548
+ }}
549
+ onScrubStart={hitScrub.start}
550
+ onScrub={hitScrub.move}
551
+ onScrubEnd={hitScrub.end}
552
+ />
553
+ </FieldLabels>
554
+ </InspectorRow>
555
+ )}
556
+ </>
557
+ );
558
+ }
559
+ case "native": {
560
+ const variants =
561
+ item.kind === "mesh" ? MESH_PRIMITIVE_KINDS : item.kind === "device" ? DEVICE_PRESET_IDS : null;
562
+ return (
563
+ <>
564
+ <InspectorRow label="Kind">
565
+ <Combobox
566
+ options={NATIVE_LAYER_KINDS.map((kind) => ({ value: kind, label: kind === "html" ? "Html snippet" : titleCase(kind) }))}
567
+ value={item.kind}
568
+ ariaLabel="Layer kind"
569
+ // A variant belongs to ONE kind, so a kind change drops it, and
570
+ // only a change: the kind chosen again keeps its Shape or Device.
571
+ onChange={(kind) => {
572
+ if (kind !== item.kind) store.updateReference(id, { kind, variant: null });
573
+ }}
574
+ />
575
+ </InspectorRow>
576
+ {variants === null ? null : (
577
+ <InspectorRow label={item.kind === "mesh" ? "Shape" : "Device"}>
578
+ <Combobox
579
+ options={[{ value: "", label: "Default" }, ...variants.map((value) => ({ value, label: titleCase(value) }))]}
580
+ value={item.variant ?? ""}
581
+ ariaLabel="Variant"
582
+ placeholder="Default"
583
+ onChange={(variant) => store.updateReference(id, { variant: variant === "" ? null : variant })}
584
+ />
585
+ </InspectorRow>
586
+ )}
587
+ </>
588
+ );
589
+ }
590
+ default:
591
+ return null;
592
+ }
593
+ }