@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,471 @@
1
+ // THE SHOT BOARD'S ITEM INSPECTOR (Paper SB3, SB5): the right column while
2
+ // items on the sketch are selected (`boardRightPanel` answers "sketch").
3
+ //
4
+ // THE STUDIO INSPECTOR'S LOOK: static sections (`InspectorSection`) of rows
5
+ // (`InspectorRow`) with each label inside its pill, and Position as the
6
+ // Transform section's grid of fields with their names inside (X, Y, W, H).
7
+ //
8
+ // image its picture, Fit and Replace, then its card (Use, Importance:
9
+ // the card-use block every board panel shares, and the note),
10
+ // then Position
11
+ // component Open in Components, its props on the card (`ThisUsePanel`, so a
12
+ // tweak rides on `card.props`, live while it plays), its card
13
+ // (Frame, Use, Importance, the note), Position; while playing,
14
+ // what the stage holds
15
+ // text its words, role and alignment; box: label and tone; arrow:
16
+ // label
17
+ // several align and distribute (`ALIGN_MODES`' words, `alignPatches`)
18
+ //
19
+ // THE ITEM'S OWN FIELDS write through the store's sketch verbs, which lock
20
+ // the layout while it plays (X/Y/W/H show it read-only then); the card's
21
+ // fields through `updateReference`. Every write is one undo step, and a drag
22
+ // on X, Y, W or H is one too, drawn on the paper as it moves
23
+ // (`beginSketchDrag`). A viewer reads it all with the writes off
24
+ // (`LockedControls`).
25
+ import { useEffect, useRef, useState } from "react";
26
+ import type { ReactNode } from "react";
27
+ import { ALIGN_LABELS, MIN_DISTRIBUTE, DISTRIBUTE_MODES, SKETCH_LABEL_MAX, SKETCH_TEXT_MAX, addReference, findSketchItem, sketchCardOf, updateSketchItem } from "@dailies-studio/shot/doc";
28
+ import type { AlignMode, Doc3D, ReferenceItem3D, SketchItem3D, SketchItemPatch, SketchRef3D } from "@dailies-studio/shot/doc";
29
+ import { InspectorRow, InspectorSection, LockedControls, NumberField, PropertyGrid, SegmentedControl, TextField, useLocked } from "@dailies-studio/ui";
30
+ import { COMPONENT_ICON, OPEN_ELSEWHERE_ICON, SPARKLES_ICON, kindIcon } from "../../../icons.js";
31
+ import { useShotEditor, useStore } from "../../../state/context.js";
32
+ import { useImageThumb } from "../../../state/imageThumbs.js";
33
+ import { SKETCH_PLAYING } from "../../../state/store.js";
34
+ import { BUILTIN_NO_MASTER, cardUseKey, isBuiltinComponent } from "../../../state/workspace.js";
35
+ import { ThisUsePanel } from "../../studio/ThisUsePanel.js";
36
+ import { CardNoteRow, CardUseRows } from "../boardRows.js";
37
+ import { assetUrl } from "../ReferencePreview.js";
38
+ import { ImagePicker } from "./ImagePicker.js";
39
+ import type { ImagePick } from "./ImagePicker.js";
40
+ import { alignPatches, componentNameOf, fileNameOf, itemKindOf, itemNameOf } from "./sketchModel.js";
41
+
42
+ /** The align and distribute glyphs, AlignRow's own 24-grid strokes. */
43
+ const ALIGN_PATHS = {
44
+ left: "M5 3V21M8 7H20V10H8M8 14H16V17H8",
45
+ hcenter: "M12 3V21M5 7H19V10H5M8 14H16V17H8",
46
+ right: "M19 3V21M4 7H16V10H4M8 14H16V17H8",
47
+ top: "M3 5H21M7 8V20H10V8M14 8V16H17V8",
48
+ vcenter: "M3 12H21M7 5V19H10V5M14 8V16H17V8",
49
+ bottom: "M3 19H21M7 4V16H10V4M14 8V16H17V8",
50
+ hdistribute: "M3 4V20M21 4V20M7 8V16M12 5V19M17 8V16",
51
+ vdistribute: "M4 3H20M4 21H20M8 7H16M5 12H19M8 17H16",
52
+ } as const satisfies Record<AlignMode, string>;
53
+ const ALIGN_ORDER: readonly AlignMode[] = ["left", "hcenter", "right", "hdistribute", "top", "vcenter", "bottom", "vdistribute"];
54
+
55
+ export function SketchInspector() {
56
+ const doc = useShotEditor((s) => s.doc);
57
+ const selection = useShotEditor((s) => s.sketchSelection);
58
+ if (doc === null) return null;
59
+ const items = selection.flatMap((id) => {
60
+ const at = findSketchItem(doc, id);
61
+ return at === null ? [] : [at.item];
62
+ });
63
+ if (items.length === 0) return null;
64
+ if (items.length > 1) {
65
+ return (
66
+ <Shell icon={kindIcon("rect")} title={`${items.length} items`}>
67
+ <AlignSection doc={doc} items={items} />
68
+ </Shell>
69
+ );
70
+ }
71
+ const item = items[0]!;
72
+ switch (item.kind) {
73
+ case "text":
74
+ return <TextInspector doc={doc} item={item} />;
75
+ case "box":
76
+ return <BoxInspector doc={doc} item={item} />;
77
+ case "arrow":
78
+ return <ArrowInspector doc={doc} item={item} />;
79
+ case "ref": {
80
+ const card = sketchCardOf(doc, item);
81
+ if (card?.type === "component") return <ComponentInspector doc={doc} item={item} card={card} />;
82
+ return <PictureInspector doc={doc} item={item} card={card?.type === "asset" ? card : undefined} />;
83
+ }
84
+ }
85
+ }
86
+
87
+ // ── the frame of every inspector ─────────────────────────────────────────
88
+
89
+ function Shell(props: { icon: ReactNode; title: string; badge?: string; children: ReactNode }) {
90
+ const readOnly = useShotEditor((s) => s.readOnly);
91
+ const playing = useShotEditor((s) => s.sketchPlaying);
92
+ return (
93
+ <section className="sketch-inspector" aria-label={props.title} data-testid="sketch-inspector">
94
+ <header className="panel-head sketch-inspector-head">
95
+ <span className="sketch-inspector-icon" aria-hidden="true">
96
+ {props.icon}
97
+ </span>
98
+ <h3 className="sketch-inspector-title">{props.title}</h3>
99
+ {props.badge === undefined ? null : <span className="badge">{props.badge}</span>}
100
+ </header>
101
+ {playing ? (
102
+ <div className="sketch-inspector-playing" data-testid="sketch-inspector-playing">
103
+ <span className="sketch-live-dot" aria-hidden="true" />
104
+ <span className="sketch-inspector-playing-word">Playing in one stage</span>
105
+ <span className="sketch-inspector-meta">1 context</span>
106
+ </div>
107
+ ) : null}
108
+ <LockedControls value={readOnly}>
109
+ <div className="sketch-inspector-body">{props.children}</div>
110
+ </LockedControls>
111
+ <footer className="panel-foot is-note">
112
+ {SPARKLES_ICON}
113
+ The agent reads this item as layout text.
114
+ </footer>
115
+ </section>
116
+ );
117
+ }
118
+
119
+ /** One of the inspector's groups: a static section, the Studio's. */
120
+ function Group(props: { title: string; children: ReactNode }) {
121
+ return (
122
+ <InspectorSection title={props.title} collapsible={false}>
123
+ {props.children}
124
+ </InspectorSection>
125
+ );
126
+ }
127
+
128
+ /**
129
+ * X, Y, W and H in layer units (design px), the Transform section's grid:
130
+ * each name inside its field. Read-only while it plays. A drag is the store's
131
+ * sketch drag, one undo step, the item moving on the paper as it goes; a
132
+ * typed value lands on the whole pixel.
133
+ *
134
+ * Each cell is a row (`InspectorRow`), as every Studio grid's is: the
135
+ * inspector's type scale (12px values, 11px names and units) is the row's,
136
+ * and a bare field in the grid kept the field's own 13px beside the panel's
137
+ * 12px rows.
138
+ */
139
+ function Position({ item }: { item: Exclude<SketchItem3D, { kind: "arrow" }> }) {
140
+ const store = useStore();
141
+ const playing = useShotEditor((s) => s.sketchPlaying);
142
+ const [x, y] = item.position;
143
+ const [w, h] = item.size;
144
+ const field = (label: string, name: string, value: number, patchOf: (v: number) => SketchItemPatch, min?: number) => (
145
+ <InspectorRow>
146
+ <NumberField
147
+ label={label}
148
+ ariaLabel={name}
149
+ value={value}
150
+ unit="px"
151
+ readOnly={playing}
152
+ {...(min === undefined ? {} : { min })}
153
+ onCommit={(next) => store.updateSketchItem(item.id, patchOf(Math.round(next)))}
154
+ onScrubStart={() => store.beginSketchDrag()}
155
+ onScrub={(next) => store.dragSketchItems({ [item.id]: patchOf(Math.round(next)) })}
156
+ onScrubEnd={(commit) => store.endSketchDrag(commit)}
157
+ />
158
+ </InspectorRow>
159
+ );
160
+ return (
161
+ <Group title="Position">
162
+ <PropertyGrid>
163
+ {field("X", "Position X", x, (v) => ({ position: [v, y] }))}
164
+ {field("Y", "Position Y", y, (v) => ({ position: [x, v] }))}
165
+ {field("W", "Width", w, (v) => ({ size: [v, h] }), 1)}
166
+ {field("H", "Height", h, (v) => ({ size: [w, v] }), 1)}
167
+ </PropertyGrid>
168
+ {playing ? <p className="insp-note">{SKETCH_PLAYING}</p> : null}
169
+ </Group>
170
+ );
171
+ }
172
+
173
+ /** The card a ref item draws: what it is for, how hard it binds, and the
174
+ * note, the rows every board panel shows for a card. */
175
+ function CardGroup({ card }: { card: Extract<ReferenceItem3D, { type: "asset" | "component" }> }) {
176
+ return (
177
+ <Group title="Card">
178
+ <CardUseRows card={card} />
179
+ <CardNoteRow card={card} />
180
+ </Group>
181
+ );
182
+ }
183
+
184
+ // ── a picture ────────────────────────────────────────────────────────────
185
+
186
+ function PictureInspector({ doc, item, card }: { doc: Doc3D; item: SketchRef3D; card: Extract<ReferenceItem3D, { type: "asset" }> | undefined }) {
187
+ const store = useStore();
188
+ const playing = useShotEditor((s) => s.sketchPlaying);
189
+ // Replace writes, so a viewer's panel takes it with the fields. Read off
190
+ // the store: this component renders the Shell, so the Shell's
191
+ // `LockedControls` sits below it.
192
+ const readOnly = useShotEditor((s) => s.readOnly);
193
+ const [replacing, setReplacing] = useState(false);
194
+ const replaceButton = useRef<HTMLButtonElement>(null);
195
+ const asset = card === undefined ? undefined : doc.assets[card.asset];
196
+ const image = asset?.kind === "image" ? asset : undefined;
197
+ const thumb = useImageThumb(image === undefined ? "" : assetUrl(image.path), image?.hash ?? "", 272);
198
+ const kind = itemKindOf(doc, item);
199
+
200
+ // REPLACE points the item at another picture, in one edit: the plan's card
201
+ // for that file, made when it has none, or the Style card picked.
202
+ const replace = (pick: ImagePick): void => {
203
+ const plan = store.getState().storyboardPlan;
204
+ store.edit((base) => {
205
+ let next = base;
206
+ let ref = pick.kind === "card" ? pick.card : undefined;
207
+ if (pick.kind === "asset") {
208
+ const file = base.assets[pick.asset];
209
+ if (file === undefined || plan === null) return base;
210
+ ref = base.resources?.references?.find((r) => r.type === "asset" && r.asset === pick.asset && r.part === plan)?.id;
211
+ if (ref === undefined) {
212
+ const before = new Set((base.resources?.references ?? []).map((r) => r.id));
213
+ next = addReference(base, {}, { type: "asset", label: fileNameOf(file.path), asset: pick.asset, use: "asset", part: plan }, "pin");
214
+ ref = next.resources?.references?.find((r) => !before.has(r.id))?.id;
215
+ }
216
+ }
217
+ return ref === undefined ? base : updateSketchItem(next, item.id, { ref });
218
+ });
219
+ };
220
+
221
+ return (
222
+ <Shell icon={kindIcon(kind === "Video" ? "video" : "image")} title={itemNameOf(doc, item)} badge={kind.toLowerCase()}>
223
+ <Group title="Picture">
224
+ {image !== undefined ? (
225
+ <div className="sketch-preview" style={{ aspectRatio: `${item.size[0]} / ${item.size[1]}` }}>
226
+ {thumb === null ? null : <img src={thumb} alt="" draggable={false} style={{ objectFit: item.fit ?? "cover" }} />}
227
+ </div>
228
+ ) : card === undefined ? (
229
+ <p className="insp-note">This card is gone from the Project. Replace it, or delete the item.</p>
230
+ ) : null}
231
+ <InspectorRow label="Fit">
232
+ <SegmentedControl
233
+ ariaLabel="Fit"
234
+ value={item.fit ?? "cover"}
235
+ options={[
236
+ { value: "cover", label: "Fill" },
237
+ { value: "contain", label: "Fit" },
238
+ ]}
239
+ onChange={(value) => store.updateSketchItem(item.id, { fit: value === "contain" ? "contain" : null })}
240
+ />
241
+ </InspectorRow>
242
+ {/* The house row of buttons: one 28px house button across the row. */}
243
+ <div className="insp-buttons">
244
+ <button ref={replaceButton} type="button" className="btn" disabled={playing || readOnly} onClick={() => setReplacing(true)}>
245
+ Replace…
246
+ </button>
247
+ </div>
248
+ </Group>
249
+ {card === undefined ? null : <CardGroup card={card} />}
250
+ <Position item={item} />
251
+ <ImagePicker open={replacing} anchor={replaceButton} title="Replace the picture" onPick={replace} onClose={() => setReplacing(false)} />
252
+ </Shell>
253
+ );
254
+ }
255
+
256
+ // ── a component ──────────────────────────────────────────────────────────
257
+
258
+ function ComponentInspector({ doc, item, card }: { doc: Doc3D; item: SketchRef3D; card: Extract<ReferenceItem3D, { type: "component" }> }) {
259
+ const store = useStore();
260
+ const playing = useShotEditor((s) => s.sketchPlaying);
261
+ const componentId = card.def !== undefined && doc.components?.[card.def] !== undefined ? null : (card.component ?? null);
262
+ // A built-in has no source to open in Components.
263
+ const builtin = useShotEditor((s) => isBuiltinComponent(s.library, componentId ?? undefined));
264
+ // The props rows read the contract.
265
+ useEffect(() => {
266
+ if (componentId !== null) void store.loadContract(componentId);
267
+ }, [componentId, store]);
268
+ const open = (): void => {
269
+ // Leaving for Components closes the board and keeps this shot board, so
270
+ // the way back lands here (`returnTarget`, `storyboardPlan`).
271
+ if (componentId === null && card.def !== undefined) void store.selectComponent({ kind: "studio", def: card.def });
272
+ else if (componentId !== null) void store.selectComponent({ kind: "code", module: componentId });
273
+ };
274
+ return (
275
+ <Shell icon={COMPONENT_ICON} title={componentNameOf(doc, card)} badge="component">
276
+ <Group title="Component">
277
+ {/* A door on the house row of buttons: one 28px house button across
278
+ the row, its words and glyph together. */}
279
+ <div className="insp-buttons">
280
+ <button
281
+ type="button"
282
+ className="btn sketch-door"
283
+ data-testid="sketch-open-component"
284
+ disabled={builtin}
285
+ title={builtin ? BUILTIN_NO_MASTER : undefined}
286
+ onClick={open}
287
+ >
288
+ Open in Components
289
+ {OPEN_ELSEWHERE_ICON}
290
+ </button>
291
+ </div>
292
+ {playing ? <p className="insp-note">Edits show live while playing.</p> : null}
293
+ </Group>
294
+ <ThisUsePanel useKey={cardUseKey(card.id)} propsLabel="Props" />
295
+ <CardGroup card={card} />
296
+ <Position item={item} />
297
+ {playing ? <InThisStage doc={doc} /> : null}
298
+ </Shell>
299
+ );
300
+ }
301
+
302
+ /** SB5's "In this stage": what moves, what holds, and what is overlay. */
303
+ function InThisStage({ doc }: { doc: Doc3D }) {
304
+ const sketchId = useShotEditor((s) => s.storyboardSketch);
305
+ const items = doc.storyboard?.parts.flatMap((p) => p.sketches ?? []).find((s) => s.id === sketchId)?.items ?? [];
306
+ const components = items.flatMap((item) => {
307
+ if (item.kind !== "ref") return [];
308
+ const card = sketchCardOf(doc, item);
309
+ return card?.type === "component" ? [componentNameOf(doc, card)] : [];
310
+ });
311
+ const rows: { name: string; badge: string; moving: boolean }[] = [
312
+ ...[...new Set(components)].map((name) => ({ name, badge: "Moving", moving: true })),
313
+ { name: "Image, text, box", badge: "Holding", moving: false },
314
+ { name: "Arrow, box label", badge: "Overlay", moving: false },
315
+ ];
316
+ return (
317
+ <Group title="In this stage">
318
+ <ul className="sketch-stage-list">
319
+ {rows.map((row) => (
320
+ <li key={row.name} className={`sketch-stage-row${row.moving ? " is-moving" : ""}`}>
321
+ <span className="sketch-stage-dot" aria-hidden="true" />
322
+ <span className="sketch-stage-name">{row.name}</span>
323
+ <span className={`badge${row.moving ? " is-success" : ""}`}>{row.badge}</span>
324
+ </li>
325
+ ))}
326
+ </ul>
327
+ </Group>
328
+ );
329
+ }
330
+
331
+ // ── text, box, arrow ─────────────────────────────────────────────────────
332
+
333
+ function TextInspector({ doc, item }: { doc: Doc3D; item: Extract<SketchItem3D, { kind: "text" }> }) {
334
+ const store = useStore();
335
+ return (
336
+ <Shell icon={kindIcon("text")} title={itemNameOf(doc, item)} badge="text">
337
+ <Group title="Text">
338
+ <InspectorRow label="Content" align="start">
339
+ <TextField
340
+ multiline
341
+ value={item.text}
342
+ ariaLabel="Content"
343
+ onCommit={(text) => {
344
+ // A text item is never stored empty: an emptied field keeps the words.
345
+ const next = text.trim().slice(0, SKETCH_TEXT_MAX).trim();
346
+ if (next !== "" && next !== item.text) store.updateSketchItem(item.id, { text: next });
347
+ }}
348
+ />
349
+ </InspectorRow>
350
+ <InspectorRow label="Role">
351
+ <SegmentedControl
352
+ ariaLabel="Role"
353
+ value={item.role}
354
+ options={[
355
+ { value: "title", label: "Title" },
356
+ { value: "body", label: "Body" },
357
+ { value: "caption", label: "Caption" },
358
+ ]}
359
+ onChange={(value) => {
360
+ if (value === "title" || value === "body" || value === "caption") store.updateSketchItem(item.id, { role: value });
361
+ }}
362
+ />
363
+ </InspectorRow>
364
+ <InspectorRow label="Align">
365
+ <SegmentedControl
366
+ ariaLabel="Align"
367
+ value={item.align ?? "start"}
368
+ options={[
369
+ { value: "start", label: "Start" },
370
+ { value: "center", label: "Centre" },
371
+ { value: "end", label: "End" },
372
+ ]}
373
+ onChange={(value) => store.updateSketchItem(item.id, { align: value === "center" || value === "end" ? value : null })}
374
+ />
375
+ </InspectorRow>
376
+ </Group>
377
+ <Position item={item} />
378
+ </Shell>
379
+ );
380
+ }
381
+
382
+ function LabelRow({ itemId, label }: { itemId: string; label: string | undefined }) {
383
+ const store = useStore();
384
+ return (
385
+ <InspectorRow label="Label">
386
+ <TextField
387
+ value={label ?? ""}
388
+ placeholder="What this stands for"
389
+ ariaLabel="Label"
390
+ onCommit={(text) => {
391
+ const next = text.trim().slice(0, SKETCH_LABEL_MAX).trim();
392
+ if (next !== (label ?? "")) store.updateSketchItem(itemId, { label: next === "" ? null : next });
393
+ }}
394
+ />
395
+ </InspectorRow>
396
+ );
397
+ }
398
+
399
+ function BoxInspector({ doc, item }: { doc: Doc3D; item: Extract<SketchItem3D, { kind: "box" }> }) {
400
+ const store = useStore();
401
+ return (
402
+ <Shell icon={kindIcon("rect")} title={itemNameOf(doc, item)} badge="box">
403
+ <Group title="Box">
404
+ <LabelRow itemId={item.id} label={item.label} />
405
+ <InspectorRow label="Tone">
406
+ <SegmentedControl
407
+ ariaLabel="Tone"
408
+ value={item.tone ?? "light"}
409
+ options={[
410
+ { value: "light", label: "Light" },
411
+ { value: "dark", label: "Dark" },
412
+ { value: "accent", label: "Accent" },
413
+ ]}
414
+ onChange={(value) => store.updateSketchItem(item.id, { tone: value === "dark" || value === "accent" ? value : null })}
415
+ />
416
+ </InspectorRow>
417
+ </Group>
418
+ <Position item={item} />
419
+ </Shell>
420
+ );
421
+ }
422
+
423
+ function ArrowInspector({ doc, item }: { doc: Doc3D; item: Extract<SketchItem3D, { kind: "arrow" }> }) {
424
+ return (
425
+ <Shell icon={kindIcon("arrow")} title={itemNameOf(doc, item)} badge="arrow">
426
+ <Group title="Arrow">
427
+ <LabelRow itemId={item.id} label={item.label} />
428
+ <p className="insp-note">An arrow stays a note: the Studio never makes it a layer.</p>
429
+ </Group>
430
+ </Shell>
431
+ );
432
+ }
433
+
434
+ // ── several ──────────────────────────────────────────────────────────────
435
+
436
+ function AlignSection({ doc, items }: { doc: Doc3D; items: readonly SketchItem3D[] }) {
437
+ const store = useStore();
438
+ const playing = useShotEditor((s) => s.sketchPlaying);
439
+ // The buttons write, so a viewer's locked panel takes them with the fields.
440
+ const locked = useLocked();
441
+ const run = (mode: AlignMode): void => {
442
+ const patches = alignPatches(items, mode, doc.size);
443
+ if (Object.keys(patches).length === 0) return;
444
+ store.edit((base) => Object.entries(patches).reduce((next, [id, patch]) => updateSketchItem(next, id, patch), base));
445
+ };
446
+ return (
447
+ <Group title="Align">
448
+ <div className="sketch-align" role="group" aria-label="Align and distribute" data-testid="sketch-align">
449
+ {ALIGN_ORDER.map((mode) => {
450
+ const needsThree = DISTRIBUTE_MODES.has(mode) && items.length < MIN_DISTRIBUTE;
451
+ const label = ALIGN_LABELS[mode];
452
+ return (
453
+ <button
454
+ key={mode}
455
+ type="button"
456
+ className="align-button"
457
+ aria-label={label}
458
+ title={playing ? SKETCH_PLAYING : needsThree ? `${label}: needs ${MIN_DISTRIBUTE} or more items` : label}
459
+ disabled={needsThree || playing || locked}
460
+ onClick={() => run(mode)}
461
+ >
462
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
463
+ <path d={ALIGN_PATHS[mode]} />
464
+ </svg>
465
+ </button>
466
+ );
467
+ })}
468
+ </div>
469
+ </Group>
470
+ );
471
+ }