@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,355 @@
1
+ // THE COMPONENTS RIGHT PANEL'S OWN PARTS (C1, C2): the three ways out (Add to
2
+ // storyboard, Share, Use in a shot) and where the component lists (Made for
3
+ // this project, In your library) over the props, History in place of the
4
+ // props while it is open, and the footer that says whose it is and which
5
+ // version is live. InspectorPanel puts them around its definition and code
6
+ // branches while Components is the workspace; InspectorFooter shows the footer.
7
+ import { useEffect, useRef, useState } from "react";
8
+ import { Avatar } from "@dailies-studio/ui";
9
+ import { componentFilesOf, instanceModuleOf } from "@dailies-studio/shot/doc";
10
+ import type { Doc3D } from "@dailies-studio/shot/doc";
11
+ import type { WorkspaceComponentVersion, WorkspaceComponentVersionInfo } from "../../api.js";
12
+ import { useShotEditor, useStore } from "../../state/context.js";
13
+ import { shotNumberOf } from "../../state/storyboardModel.js";
14
+ import type { ComponentsSelection } from "../../state/types.js";
15
+ import { componentsSubject, libraryRuleOf, readOnlyReason } from "../../state/workspace.js";
16
+ import { shortAgo } from "../comments/parts.js";
17
+ import { useSignedIn } from "../header/AccountAvatar.js";
18
+ import { ShareComponentDialog } from "../share/ShareComponentDialog.js";
19
+ import { useSubjectRow, useSubjectVersions } from "./componentParts.js";
20
+
21
+ /** One option per Shot, numbered as the board numbers it. */
22
+ function shotOptions(doc: Doc3D): { id: string; label: string }[] {
23
+ return doc.scenes.flatMap((id, i) => {
24
+ const scene = doc.scenesById[id];
25
+ if (scene === undefined) return [];
26
+ const number = shotNumberOf(doc, id) ?? String(i + 1).padStart(2, "0");
27
+ return [{ id, label: `${number} ${scene.name}` }];
28
+ });
29
+ }
30
+
31
+ /** The actions block: Add to storyboard (as Style; the board pins it to a
32
+ * Shot from there), Share, Use in a shot, and where it lists. */
33
+ export function ComponentActions() {
34
+ const store = useStore();
35
+ const doc = useShotEditor((s) => s.doc);
36
+ const readOnly = useShotEditor((s) => s.readOnly);
37
+ const codeComponent = useShotEditor((s) => s.codeComponent);
38
+ const editingDef = useShotEditor((s) => s.editingDef);
39
+ const [added, setAdded] = useState<string | null>(null);
40
+ if (doc === null) return null;
41
+ const subject = componentsSubject({ codeComponent, editingDef });
42
+ const shots = shotOptions(doc);
43
+ const add = async (): Promise<void> => {
44
+ if (await store.addComponentToStoryboard(null)) setAdded("On the board as Style.");
45
+ };
46
+ return (
47
+ <div className="components-actions" data-testid="components-actions">
48
+ <ShareComponentDialog />
49
+ <div className="components-actions-row">
50
+ <button
51
+ type="button"
52
+ className="components-add-board"
53
+ data-testid="components-add-board"
54
+ disabled={readOnly}
55
+ title="Put this component on the Storyboard"
56
+ onClick={() => void add()}
57
+ >
58
+ Add to storyboard
59
+ </button>
60
+ <button
61
+ type="button"
62
+ className="components-share-button"
63
+ data-testid="components-share"
64
+ title="Share this component"
65
+ onClick={() => store.openShareComponent()}
66
+ >
67
+ Share
68
+ </button>
69
+ </div>
70
+ <label className="components-field">
71
+ <span className="components-field-label">Use in a shot</span>
72
+ <select
73
+ className="select"
74
+ data-testid="components-use-in-shot"
75
+ value=""
76
+ disabled={readOnly || shots.length === 0}
77
+ onChange={(e) => {
78
+ const id = e.currentTarget.value;
79
+ if (id !== "") void store.placeComponentInShot(id);
80
+ }}
81
+ >
82
+ <option value="">{shots.length === 0 ? "No shots yet" : "Pick a shot"}</option>
83
+ {shots.map((s) => (
84
+ <option key={s.id} value={s.id}>
85
+ {s.label}
86
+ </option>
87
+ ))}
88
+ </select>
89
+ </label>
90
+ {subject === null ? null : <ComponentMembership doc={doc} sel={subject} />}
91
+ {added === null ? null : (
92
+ <p className="components-actions-note" role="status" data-testid="components-added">
93
+ {added}
94
+ </p>
95
+ )}
96
+ </div>
97
+ );
98
+ }
99
+
100
+ /** Every Project in this workspace lists a component the library holds. */
101
+ const IN_LIBRARY_NOTE = "Every Project in this workspace lists it under Your library.";
102
+
103
+ /**
104
+ * Where the component lists: "Made for this project" (code only: its file is
105
+ * in `componentFiles`, so it lists under This project with no shot using it)
106
+ * and "In your library" (its workspace row's `library` flag). Two rows and
107
+ * ONE note: the reason the library box is ticked by rule, else what a tick
108
+ * means. A box that cannot change is `aria-disabled`, still pressable, and a
109
+ * press says why in the error line: a viewer's refusal, the rule, or that the
110
+ * lists are still loading.
111
+ */
112
+ function ComponentMembership(props: { doc: Doc3D; sel: ComponentsSelection }) {
113
+ const store = useStore();
114
+ const { doc, sel } = props;
115
+ const viewer = useShotEditor((s) => (s.readOnly ? readOnlyReason(s) : null));
116
+ const library = useShotEditor((s) => s.library);
117
+ const workspace = useShotEditor((s) => s.workspaceComponents);
118
+ const homes = useShotEditor((s) => s.componentHomes);
119
+ const row = useSubjectRow();
120
+ // A definition's Edit tab shows its layers where the rail would be, so
121
+ // nothing else may have read the lists yet.
122
+ useEffect(() => {
123
+ const s = store.getState();
124
+ if (s.workspaceComponents === null || s.componentHomes === null) void store.refreshWorkspaceComponents();
125
+ }, [store]);
126
+ const rel = sel.kind === "code" ? instanceModuleOf(sel.module) : null;
127
+ const made = rel !== null && componentFilesOf(doc).includes(rel);
128
+ const reading = workspace === null || homes === null;
129
+ const rule = workspace === null || homes === null ? null : libraryRuleOf(doc, sel, library, workspace, homes);
130
+ const inLibrary = !reading && (rule !== null || row?.library === true);
131
+ const libraryRefusal = viewer ?? (reading ? "Your library is still loading." : rule);
132
+ const note = reading
133
+ ? "Reading…"
134
+ : (rule ?? (inLibrary ? IN_LIBRARY_NOTE : rel !== null ? "Listed here even when no shot uses it." : null));
135
+ return (
136
+ <div className="components-membership" data-testid="components-membership">
137
+ {rel === null ? null : (
138
+ <label className="components-membership-row">
139
+ <input
140
+ type="checkbox"
141
+ className="checkbox"
142
+ data-testid="components-made-here"
143
+ checked={made}
144
+ aria-disabled={viewer === null ? undefined : true}
145
+ onChange={() => {
146
+ if (viewer !== null) {
147
+ store.reportError(viewer);
148
+ return;
149
+ }
150
+ const out = store.setComponentFile(rel, !made);
151
+ if (!out.ok) store.reportError(out.reason);
152
+ }}
153
+ />
154
+ <span>Made for this project</span>
155
+ </label>
156
+ )}
157
+ <label className="components-membership-row">
158
+ <input
159
+ type="checkbox"
160
+ className="checkbox"
161
+ data-testid="components-in-library"
162
+ checked={inLibrary}
163
+ aria-disabled={libraryRefusal === null ? undefined : true}
164
+ onChange={() => {
165
+ if (libraryRefusal !== null) {
166
+ store.reportError(libraryRefusal);
167
+ return;
168
+ }
169
+ void store.setComponentInLibrary(sel, !inLibrary);
170
+ }}
171
+ />
172
+ <span>In your library</span>
173
+ </label>
174
+ {note === null ? null : (
175
+ <p className="insp-note" data-testid="components-membership-note">
176
+ {note}
177
+ </p>
178
+ )}
179
+ </div>
180
+ );
181
+ }
182
+
183
+ /** History, in the props' place: every recorded version, newest first, with
184
+ * a read-only view of one and Restore. */
185
+ export function ComponentHistory() {
186
+ const store = useStore();
187
+ const viewing = useShotEditor((s) => s.componentHistory?.viewing ?? null);
188
+ const readOnly = useShotEditor((s) => s.readOnly);
189
+ const row = useSubjectRow();
190
+ const versions = useSubjectVersions();
191
+ // History is one component's: picking another closes it.
192
+ const subjectKey = useShotEditor((s) => `${s.codeComponent ?? ""}|${s.editingDef ?? ""}`);
193
+ const openedOn = useRef(subjectKey);
194
+ useEffect(() => {
195
+ if (subjectKey !== openedOn.current) store.closeComponentHistory();
196
+ }, [store, subjectKey]);
197
+ return (
198
+ <section className="components-history" data-testid="components-history" aria-label="History">
199
+ <div className="components-history-head">
200
+ {viewing === null ? (
201
+ <h3 className="components-history-title">History</h3>
202
+ ) : (
203
+ <button
204
+ type="button"
205
+ className="components-history-back"
206
+ data-testid="components-history-back"
207
+ onClick={() => void store.viewComponentVersion(null)}
208
+ >
209
+ All versions
210
+ </button>
211
+ )}
212
+ <button
213
+ type="button"
214
+ className="components-history-done"
215
+ data-testid="components-history-close"
216
+ onClick={() => store.closeComponentHistory()}
217
+ >
218
+ Done
219
+ </button>
220
+ </div>
221
+ {viewing !== null ? (
222
+ <VersionView version={viewing} current={row?.version} readOnly={readOnly} />
223
+ ) : row === undefined ? (
224
+ <p className="insp-empty" data-testid="components-history-empty">
225
+ No versions yet. A version is recorded when the component builds or when you leave Edit.
226
+ </p>
227
+ ) : versions === undefined ? (
228
+ <p className="insp-empty">Reading the versions…</p>
229
+ ) : (
230
+ <ol className="components-history-list" data-testid="components-history-list">
231
+ {versions.map((v) => (
232
+ <VersionRow key={v.version} info={v} current={v.version === row.version} readOnly={readOnly} />
233
+ ))}
234
+ </ol>
235
+ )}
236
+ </section>
237
+ );
238
+ }
239
+
240
+ function VersionRow(props: { info: WorkspaceComponentVersionInfo; current: boolean; readOnly: boolean }) {
241
+ const store = useStore();
242
+ const v = props.info;
243
+ return (
244
+ <li className={`components-history-row${props.current ? " is-current" : ""}`} data-testid="components-history-row">
245
+ <span className="components-history-version">v{v.version}</span>
246
+ <span className="components-history-when" title={new Date(v.createdAt).toLocaleString()}>
247
+ {props.current ? "live" : shortAgo(v.createdAt)}
248
+ </span>
249
+ <button
250
+ type="button"
251
+ className="components-history-verb"
252
+ data-testid="components-history-view"
253
+ onClick={() => void store.viewComponentVersion(v.version)}
254
+ >
255
+ View
256
+ </button>
257
+ <button
258
+ type="button"
259
+ className="components-history-verb"
260
+ data-testid="components-history-restore"
261
+ disabled={props.current || props.readOnly}
262
+ title={props.current ? "This is the live version" : `Make v${v.version} the live version`}
263
+ onClick={() => void store.restoreComponentVersion(v.version)}
264
+ >
265
+ Restore
266
+ </button>
267
+ </li>
268
+ );
269
+ }
270
+
271
+ /** One version, read-only: a code version's files, a Studio version's props. */
272
+ function VersionView(props: { version: WorkspaceComponentVersion; current: number | undefined; readOnly: boolean }) {
273
+ const store = useStore();
274
+ const v = props.version;
275
+ const payload = v.payload;
276
+ return (
277
+ <div className="components-history-view" data-testid="components-history-viewing">
278
+ <div className="components-history-row is-head">
279
+ <span className="components-history-version">v{v.version}</span>
280
+ <span className="components-history-when">{v.name}</span>
281
+ <button
282
+ type="button"
283
+ className="components-history-verb"
284
+ data-testid="components-history-restore"
285
+ disabled={v.version === props.current || props.readOnly}
286
+ onClick={() => void store.restoreComponentVersion(v.version)}
287
+ >
288
+ Restore
289
+ </button>
290
+ </div>
291
+ {payload.kind === "code" ? (
292
+ payload.files.map((f) => (
293
+ <figure key={f.path} className="components-history-file">
294
+ <figcaption className="components-history-path">{f.path}</figcaption>
295
+ <pre className="components-history-code" data-testid="components-history-code">
296
+ {f.content}
297
+ </pre>
298
+ </figure>
299
+ ))
300
+ ) : (
301
+ <p className="components-history-props" data-testid="components-history-props">
302
+ {v.propKeys.length === 0 ? "No props." : `Props: ${v.propKeys.join(", ")}`}
303
+ </p>
304
+ )}
305
+ </div>
306
+ );
307
+ }
308
+
309
+ /** "Made by you · private · vN": who made it, who can see it, and which
310
+ * version is live. A code component adds "saved" once nothing is unsaved. */
311
+ export function ComponentFooter() {
312
+ const store = useStore();
313
+ const codeComponent = useShotEditor((s) => s.codeComponent);
314
+ const editingDef = useShotEditor((s) => s.editingDef);
315
+ const subject = componentsSubject({ codeComponent, editingDef });
316
+ const dirty = useShotEditor((s) => Object.values(s.codeFiles).some((f) => f.content !== f.savedContent));
317
+ const readOnly = useShotEditor((s) => s.readOnly);
318
+ const row = useSubjectRow();
319
+ const me = useSignedIn();
320
+ if (subject === null) return null;
321
+ const meta = ["private", ...(row === undefined ? [] : [`v${row.version}`]), ...(subject.kind === "code" && !dirty ? ["saved"] : [])];
322
+ return (
323
+ <footer className="inspector-footer components-footer" data-testid="components-footer">
324
+ <Avatar url={null} label={me?.name ?? ""} className="components-footer-avatar" />
325
+ <span className="components-footer-who">Made by you</span>
326
+ <span className="components-footer-meta" data-testid="components-footer-meta">
327
+ {meta.join(" · ")}
328
+ </span>
329
+ {/* A code component has Remix and History in its status footer. */}
330
+ {subject.kind === "studio" ? (
331
+ <span className="components-footer-verbs">
332
+ <button
333
+ type="button"
334
+ className="code-status-verb"
335
+ data-testid="studio-remix"
336
+ disabled={readOnly}
337
+ title="Make your own copy of this component. The original stays as it was."
338
+ onClick={() => void store.remixComponent()}
339
+ >
340
+ Remix
341
+ </button>
342
+ <button
343
+ type="button"
344
+ className="code-status-verb"
345
+ data-testid="studio-history"
346
+ title="Every recorded version of this component"
347
+ onClick={() => void store.openComponentHistory()}
348
+ >
349
+ History
350
+ </button>
351
+ </span>
352
+ ) : null}
353
+ </footer>
354
+ );
355
+ }
@@ -0,0 +1,243 @@
1
+ // THE COMPONENT STUDIO: Components' Edit tab for a Studio-made definition
2
+ // (mock C6), the definition standalone in the docked frame.
3
+ //
4
+ // A definition is not a property of the shot it happens to sit in. It is a
5
+ // module: it has its own canvas, its own clock, its own named actions and its
6
+ // own published surface, and every one of those is a thing an author works on
7
+ // for a while rather than glances at. So it gets the canvas, and the frame
8
+ // around the canvas says what it is:
9
+ //
10
+ // the LEFT COLUMN how it is built: the Files panel's LAYERS outline,
11
+ // scoped to the definition, under "All components"
12
+ // (FilePanel.tsx; `.is-component-edit` in component.css
13
+ // keeps it in place of the mode slot)
14
+ // the CANVAS it, standalone, through a HOLE onto the stage, with the
15
+ // tool footer under it (`toolFooterShown`)
16
+ // the CANVAS BAR the way back, its name (the rename), Preview | Edit,
17
+ // and the zoom over its render
18
+ // the RIGHT COLUMN the inspector: the definition's published surface
19
+ // (`DefinitionSection`) with nothing selected, a selected
20
+ // layer's sections otherwise
21
+ // the BOTTOM ROW its own clock: the timeline's definition fork (def-local
22
+ // ruler, Actions rows) and the dock's one transport
23
+ //
24
+ // ── ONE SUBTREE, PORTALLED INTO THE CELLS ────────────────────────────────
25
+ //
26
+ // App mounts this inside its PanelBoundary as before; it renders nothing into
27
+ // the shell itself. Each part goes into its cell's slot through `InCell`
28
+ // (panels/stage/cellSlots.tsx), so the rename prompt and the zoom stay one
29
+ // React subtree. It portals only on its own tab, so no other mode can be
30
+ // filling the same cells.
31
+ //
32
+ // ── WHY THE CANVAS IS A HOLE ─────────────────────────────────────────────
33
+ //
34
+ // The stage iframe behind the shell renders the open definition standalone
35
+ // (no scene, no scene camera, no post), and `measureInset` hands it the hole's
36
+ // rect to fit into. So what is on screen is the real component under the real
37
+ // renderer, which is the only way "preview === export" can hold for a thing
38
+ // the author is about to place in fifteen shots.
39
+ import { useEffect } from "react";
40
+ import { IconMinus, IconPlus, SegmentedControl, usePrompt } from "@dailies-studio/ui";
41
+ import { useShotEditor, useStore } from "../../state/context.js";
42
+ import type { ComponentsTab } from "../../state/types.js";
43
+ import { componentEditOpen } from "../../state/workspace.js";
44
+ import { InCell } from "../stage/cellSlots.js";
45
+ import { ComponentCrumb, ComponentsBack } from "./componentParts.js";
46
+
47
+ /** No buffers to lose on this tab: the way back never asks. */
48
+ const NO_GUARD = (then: () => void): void => then();
49
+
50
+ /** One press of the zoom. Multiplicative, because the control is a scale: a
51
+ * fixed ±0.25 step is a quarter of the picture near 1x and a whole doubling
52
+ * near 0.25x. The store owns the [0.25, 4] clamp. */
53
+ const ZOOM_STEP = 1.25;
54
+
55
+ export function ComponentStudio() {
56
+ const store = useStore();
57
+ const doc = useShotEditor((s) => s.doc);
58
+ const editingDef = useShotEditor((s) => s.editingDef);
59
+ // THE WORKSPACE decides, not `editingDef` alone: a mode that outranks this
60
+ // one (`workspaceOf`'s order) owns the cells while it is open. The Edit tab
61
+ // of Components only: Preview is ComponentsPreview's.
62
+ const own = useShotEditor(componentEditOpen);
63
+ const { prompt, dialog } = usePrompt();
64
+
65
+ const def = editingDef === null ? undefined : doc?.components?.[editingDef];
66
+ if (doc === null || def === undefined || !own) return null;
67
+
68
+ return (
69
+ <>
70
+ <InCell name="centre">
71
+ {/* THE COMPONENT ITSELF, and it is a hole rather than a component:
72
+ `.stage-window` paints nothing and takes no pointer, so every press
73
+ over the subject reaches the stage. The stage's one pane is the
74
+ definition's frame there: the tools draw on it, a drag moves a
75
+ layer or marquees, middle-drag and Alt+left drag orbit, and the
76
+ wheel pans and dollies. `aria-hidden` because a screen reader has
77
+ nothing to read in a GL surface this document does not own. */}
78
+ <div
79
+ className="stage-window component-studio-hole"
80
+ aria-hidden="true"
81
+ data-testid="studio-preview-hole"
82
+ />
83
+ </InCell>
84
+
85
+ <InCell name="bar">
86
+ <div
87
+ className="component-studio-bar"
88
+ role="group"
89
+ aria-label={`Component ${def.name}`}
90
+ data-testid="component-studio"
91
+ >
92
+ {dialog}
93
+ {/* THE WAY BACK: to the Shot this came from, else to All
94
+ components, the verb the outline's back row runs too. */}
95
+ <ComponentsBack testId="studio-back" guard={NO_GUARD} />
96
+ {/* THE NAME IS THE RENAME. One dialog for the whole chrome
97
+ (`usePrompt`, the app menu's), never a second inline field with its
98
+ own commit rules: the inspector's Name row is the other spelling of
99
+ this one call and both land in `renameComponent`. */}
100
+ <ComponentCrumb
101
+ sel={{ kind: "studio", def: def.id }}
102
+ name={
103
+ <button
104
+ type="button"
105
+ className="component-studio-name"
106
+ data-testid="studio-rename"
107
+ title="Rename this component"
108
+ onClick={() => {
109
+ void prompt({
110
+ title: "Rename component",
111
+ label: "Name",
112
+ initialValue: def.name,
113
+ }).then((name) => {
114
+ if (name !== null) store.renameComponent(def.id, name);
115
+ });
116
+ }}
117
+ >
118
+ {def.name}
119
+ </button>
120
+ }
121
+ />
122
+ <div className="component-studio-tools">
123
+ <ComponentsTabSwitch />
124
+ <StudioZoom />
125
+ </div>
126
+ </div>
127
+ </InCell>
128
+ </>
129
+ );
130
+ }
131
+
132
+ const TAB_TEST_ID: Record<ComponentsTab, string> = {
133
+ preview: "components-tab-preview",
134
+ edit: "components-tab-edit",
135
+ };
136
+
137
+ /** Set by a swap made from a focused segment, taken by the switch that mounts
138
+ * next: the swap unmounts this bar and mounts the other tab's, so focus has
139
+ * to be handed across. */
140
+ let refocusTab = false;
141
+
142
+ /**
143
+ * Components' Preview | Edit, in the canvas bar of both tabs: Preview is the
144
+ * component alone on the stage (ComponentsPreview.tsx), Edit its studio (this
145
+ * file for a definition, CodeStudio.tsx for code). The subject, its clock and
146
+ * its buffers stay across the swap (`setComponentsTab`).
147
+ */
148
+ export function ComponentsTabSwitch() {
149
+ const store = useStore();
150
+ const tab = useShotEditor((s) => s.componentsTab);
151
+ useEffect(() => {
152
+ if (!refocusTab) return;
153
+ refocusTab = false;
154
+ document.querySelector<HTMLElement>(`[data-testid="${TAB_TEST_ID[tab]}"]`)?.focus();
155
+ // On mount only: this instance is the one the swap brought up.
156
+ }, []);
157
+ return (
158
+ <SegmentedControl
159
+ ariaLabel="Components view"
160
+ className="is-bar components-tab-switch"
161
+ value={tab}
162
+ options={[
163
+ { value: "preview", label: "Preview", title: "The component alone on the stage", testId: TAB_TEST_ID.preview },
164
+ { value: "edit", label: "Edit", title: "Edit the component", testId: TAB_TEST_ID.edit },
165
+ ]}
166
+ onChange={(next) => {
167
+ if (next === tab) return;
168
+ const focused = document.activeElement;
169
+ refocusTab = focused instanceof HTMLElement && Object.values(TAB_TEST_ID).includes(focused.dataset.testid ?? "");
170
+ store.setComponentsTab(next === "edit" ? "edit" : "preview");
171
+ }}
172
+ />
173
+ );
174
+ }
175
+
176
+ /**
177
+ * How big the definition is drawn, plus the way back to the derived camera.
178
+ * Code Studio's canvas bar renders this same cluster for its component.
179
+ *
180
+ * The zoom is CHROME state pushed to the stage, which divides the definition's
181
+ * frame by it before fitting, so it scales the subject inside the hole rather
182
+ * than moving a camera the author may have taken. The stage bar's zoom look
183
+ * (`.zoom-controls`), because it is the same control over a different subject.
184
+ *
185
+ * Recenter appears exactly when it is true: once the author has taken the
186
+ * camera, by an orbit (middle-drag or Alt+left drag, and over a code
187
+ * component a plain drag too) or by a wheel pan or dolly, it is theirs and
188
+ * nothing re-derives it.
189
+ */
190
+ export function StudioZoom() {
191
+ const store = useStore();
192
+ const zoom = useShotEditor((s) => s.studioZoom);
193
+ const orbited = useShotEditor((s) => s.isolationOrbit);
194
+ return (
195
+ <>
196
+ {orbited ? (
197
+ <button
198
+ type="button"
199
+ className="btn"
200
+ data-testid="studio-refit"
201
+ title="Frame the component head-on again (Home)"
202
+ onClick={() => store.fitWorkingView()}
203
+ >
204
+ Recenter
205
+ </button>
206
+ ) : null}
207
+ <div className="zoom-controls" role="group" aria-label="Zoom">
208
+ <button
209
+ type="button"
210
+ className="btn is-icon"
211
+ data-testid="studio-zoom-out"
212
+ aria-label="Zoom out"
213
+ title="Zoom out"
214
+ onClick={() => store.setStudioZoom(zoom / ZOOM_STEP)}
215
+ >
216
+ <IconMinus />
217
+ </button>
218
+ {/* The readout doubles as the reset, which is where every editor puts
219
+ it, and it is the only way back to 1x that does not need repeated
220
+ presses to land exactly there. */}
221
+ <button
222
+ type="button"
223
+ className="btn zoom-readout"
224
+ data-testid="studio-zoom-level"
225
+ title="Reset the zoom"
226
+ onClick={() => store.setStudioZoom(1)}
227
+ >
228
+ {Math.round(zoom * 100)}%
229
+ </button>
230
+ <button
231
+ type="button"
232
+ className="btn is-icon"
233
+ data-testid="studio-zoom-in"
234
+ aria-label="Zoom in"
235
+ title="Zoom in"
236
+ onClick={() => store.setStudioZoom(zoom * ZOOM_STEP)}
237
+ >
238
+ <IconPlus />
239
+ </button>
240
+ </div>
241
+ </>
242
+ );
243
+ }