@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,235 @@
1
+ // The node thumbnails, and the four rules that keep them from costing more than
2
+ // the graph they describe.
3
+ //
4
+ // A preview is a real GPU readback: the stage renders each asked-for node's
5
+ // output to an atlas cell and reads it back, and a readback is a pipeline
6
+ // stall. So the chrome, which is the half that knows what the author can
7
+ // actually see, does the deciding:
8
+ //
9
+ // VIEWPORT-VISIBLE ONLY. `visiblePreviewNodes` is the filter, and it is a
10
+ // pure function of (nodes, viewport, canvas box) so it can be checked
11
+ // without mounting anything. A 200-node graph zoomed to one corner asks for
12
+ // the four cards on screen.
13
+ //
14
+ // ONE REQUEST IN FLIGHT. A pan is thirty viewport events; thirty overlapping
15
+ // readbacks would queue thirty stalls behind each other and the last answer
16
+ // would arrive seconds after the pan stopped. The newest ask waits, and the
17
+ // ones it overtook are simply dropped — nobody wants the intermediate
18
+ // frames of a pan.
19
+ //
20
+ // DEBOUNCED, so a keystroke in a Float field is one batch after the typing
21
+ // stops rather than one per character.
22
+ //
23
+ // KEYED BY THE PIXEL SIGNATURE, and a batch whose `semanticSignature` is no
24
+ // longer current is THROWN AWAY rather than painted. The stage answers
25
+ // asynchronously; by the time a batch lands the author may have rewired the
26
+ // node it describes, and a thumbnail that is quietly one edit stale is
27
+ // worse than no thumbnail at all — it is a wrong answer with a picture.
28
+ //
29
+ // And the bitmaps are OWNED here: an `ImageBitmap` holds GPU-side memory until
30
+ // `close()` is called, so every replacement closes what it replaced and
31
+ // `dispose()` closes the lot. Leaking them is invisible until a long session
32
+ // runs the tab out of memory.
33
+ import { MAX_NODE_PREVIEWS } from "../../bridgeTypes.js";
34
+ import type { NodePreviewBatch, NodePreviewRequest } from "../../bridgeTypes.js";
35
+
36
+ /** A card's own box in GRAPH units. Only used to decide "is any of this on
37
+ * screen", so it is deliberately generous rather than measured: a card that is
38
+ * half a width off the edge is about to be scrolled into view. */
39
+ const CARD = { width: 220, height: 150 } as const;
40
+
41
+ /** How far outside the canvas still counts as visible, in screen px. One card's
42
+ * worth, so a small pan does not re-ask for what it just uncovered. */
43
+ const VIEWPORT_PAD = 240;
44
+
45
+ /** How long the edits have to stop before a batch goes out. Long enough to
46
+ * swallow a typed number, short enough that a committed edit feels answered. */
47
+ export const PREVIEW_DEBOUNCE_MS = 120;
48
+
49
+ /** What one node contributes to the visibility test. A structural subset of
50
+ * `MaterialGraphNode`, so the caller passes graph nodes straight in. */
51
+ export interface PreviewCandidate {
52
+ id: string;
53
+ position: readonly [number, number];
54
+ preview?: true | undefined;
55
+ }
56
+
57
+ /**
58
+ * The preview-enabled nodes that are actually on screen, newest-first order
59
+ * preserved from the input.
60
+ *
61
+ * Clamped to `MAX_NODE_PREVIEWS` here rather than left to the stage, because
62
+ * the CHROME is the half that can choose WHICH ones to drop — and dropping the
63
+ * tail of a screen-order list is a scroll position, which is what the bridge's
64
+ * own clamp comment says it means.
65
+ */
66
+ export function visiblePreviewNodes(
67
+ nodes: readonly PreviewCandidate[],
68
+ viewport: { x: number; y: number; zoom: number },
69
+ box: { width: number; height: number },
70
+ ): string[] {
71
+ const zoom = Number.isFinite(viewport.zoom) && viewport.zoom > 0 ? viewport.zoom : 1;
72
+ const x = Number.isFinite(viewport.x) ? viewport.x : 0;
73
+ const y = Number.isFinite(viewport.y) ? viewport.y : 0;
74
+ const out: string[] = [];
75
+ for (const node of nodes) {
76
+ if (node.preview !== true) continue;
77
+ const left = node.position[0] * zoom + x;
78
+ const top = node.position[1] * zoom + y;
79
+ const right = left + CARD.width * zoom;
80
+ const bottom = top + CARD.height * zoom;
81
+ if (right < -VIEWPORT_PAD || left > box.width + VIEWPORT_PAD) continue;
82
+ if (bottom < -VIEWPORT_PAD || top > box.height + VIEWPORT_PAD) continue;
83
+ out.push(node.id);
84
+ if (out.length >= MAX_NODE_PREVIEWS) break;
85
+ }
86
+ return out;
87
+ }
88
+
89
+ /** What the canvas knows and the scheduler needs. `pixelSignature` decides
90
+ * whether an ask is NEW; `semanticSignature` decides whether an ANSWER is
91
+ * still current. Two different questions, so two different signatures. */
92
+ export interface PreviewAsk {
93
+ materialId: string;
94
+ layerId: string;
95
+ slot: number | null;
96
+ nodeIds: readonly string[];
97
+ size: number;
98
+ pixelSignature: string;
99
+ semanticSignature: string;
100
+ }
101
+
102
+ /** A thumbnail, or the stage's sentence about why there is not one. */
103
+ export type PreviewEntry = ImageBitmap | string;
104
+
105
+ export interface PreviewScheduler {
106
+ /** The newest truth. Null means "nothing wants previews" — inside a subgraph,
107
+ * with no graph open, or while the stage has no preview target — and it
108
+ * releases everything held. */
109
+ ask(next: PreviewAsk | null): void;
110
+ dispose(): void;
111
+ }
112
+
113
+ export interface PreviewSchedulerDeps {
114
+ request(request: NodePreviewRequest): Promise<NodePreviewBatch | null>;
115
+ /** Called with a fresh map whenever the held set changes. */
116
+ onChange(entries: ReadonlyMap<string, PreviewEntry>): void;
117
+ debounceMs?: number;
118
+ }
119
+
120
+ export function createPreviewScheduler(deps: PreviewSchedulerDeps): PreviewScheduler {
121
+ const held = new Map<string, PreviewEntry>();
122
+ const debounce = deps.debounceMs ?? PREVIEW_DEBOUNCE_MS;
123
+ let timer: ReturnType<typeof setTimeout> | null = null;
124
+ let inFlight = false;
125
+ let pending: PreviewAsk | null = null;
126
+ /** The ask the held bitmaps answer. A repeat of it is a no-op. */
127
+ let satisfied: string | null = null;
128
+ let disposed = false;
129
+
130
+ const keyOf = (ask: PreviewAsk): string =>
131
+ `${ask.materialId}|${ask.slot ?? "-"}|${ask.size}|${ask.pixelSignature}|${[...ask.nodeIds].sort().join(",")}`;
132
+
133
+ const release = (entry: PreviewEntry | undefined): void => {
134
+ // A string is a message and owns nothing. Only a bitmap has to be closed,
135
+ // and `close` is guarded because jsdom's ImageBitmap stand-ins have none.
136
+ if (entry === undefined || typeof entry === "string") return;
137
+ if (typeof entry.close === "function") entry.close();
138
+ };
139
+
140
+ const clearHeld = (): void => {
141
+ if (held.size === 0) return;
142
+ for (const entry of held.values()) release(entry);
143
+ held.clear();
144
+ deps.onChange(new Map(held));
145
+ };
146
+
147
+ const land = (ask: PreviewAsk, batch: NodePreviewBatch): void => {
148
+ // STALE ANSWERS ARE DROPPED WHOLE. A batch is one render of one graph; half
149
+ // of it cannot be current.
150
+ if (batch.materialId !== ask.materialId) return;
151
+ if (batch.semanticSignature !== ask.semanticSignature) return;
152
+ const wanted = new Set(ask.nodeIds);
153
+ for (const [id, entry] of held) {
154
+ if (wanted.has(id)) continue;
155
+ release(entry);
156
+ held.delete(id);
157
+ }
158
+ for (const result of batch.results) {
159
+ if (!wanted.has(result.nodeId)) continue;
160
+ release(held.get(result.nodeId));
161
+ held.set(result.nodeId, result.bitmap ?? result.message);
162
+ }
163
+ satisfied = keyOf(ask);
164
+ deps.onChange(new Map(held));
165
+ };
166
+
167
+ const run = (ask: PreviewAsk): void => {
168
+ inFlight = true;
169
+ const request: NodePreviewRequest = {
170
+ materialId: ask.materialId,
171
+ layerId: ask.layerId,
172
+ slot: ask.slot,
173
+ nodeIds: ask.nodeIds,
174
+ size: ask.size,
175
+ };
176
+ void deps
177
+ .request(request)
178
+ .then((batch) => {
179
+ if (disposed || batch === null) return;
180
+ land(ask, batch);
181
+ })
182
+ .catch(() => {
183
+ // The stage refused or went away. Silent: the cards keep whatever they
184
+ // had, and the next committed edit asks again.
185
+ })
186
+ .finally(() => {
187
+ inFlight = false;
188
+ if (disposed) return;
189
+ // THE OVERTAKEN ASKS ARE GONE, not queued. `pending` only ever holds
190
+ // the newest, so a pan that produced thirty of them costs two requests.
191
+ const next = pending;
192
+ pending = null;
193
+ if (next !== null && keyOf(next) !== satisfied) run(next);
194
+ });
195
+ };
196
+
197
+ return {
198
+ ask(next) {
199
+ if (disposed) return;
200
+ if (timer !== null) {
201
+ clearTimeout(timer);
202
+ timer = null;
203
+ }
204
+ if (next === null || next.nodeIds.length === 0) {
205
+ pending = null;
206
+ satisfied = null;
207
+ clearHeld();
208
+ return;
209
+ }
210
+ if (keyOf(next) === satisfied) {
211
+ pending = null;
212
+ return;
213
+ }
214
+ pending = next;
215
+ timer = setTimeout(() => {
216
+ timer = null;
217
+ const ask = pending;
218
+ if (ask === null) return;
219
+ // Held while one is out. The settle path above picks `pending` back up,
220
+ // which is what makes "one in flight" true without a queue.
221
+ if (inFlight) return;
222
+ pending = null;
223
+ run(ask);
224
+ }, debounce);
225
+ },
226
+ dispose() {
227
+ disposed = true;
228
+ if (timer !== null) clearTimeout(timer);
229
+ timer = null;
230
+ pending = null;
231
+ for (const entry of held.values()) release(entry);
232
+ held.clear();
233
+ },
234
+ };
235
+ }
@@ -0,0 +1,6 @@
1
+ /* React Flow's own stylesheet, in the `vendor` cascade layer under every
2
+ house rule, as the /scene index had it. Imported by MaterialGraphCanvas
3
+ alone, so it ships in the material canvas's chunk and only the material
4
+ nodes pay for it. The index's first statement fixes the layer order before
5
+ this can arrive, so the layer keeps its rank. */
6
+ @import "@xyflow/react/dist/style.css" layer(vendor);
@@ -0,0 +1,85 @@
1
+ // THE SOCKET VOCABULARY: shape carries the type, and there is exactly one ink.
2
+ //
3
+ // ── WHY NOT A COLOUR PER TYPE ────────────────────────────────────────────
4
+ //
5
+ // Every shipped node editor colours its sockets, and this one may not. The
6
+ // house design system is monochrome plus ONE blue, and the blue is signal:
7
+ // selection, focus, the armed thing. Nine socket types would be nine hues that
8
+ // are none of them a studio colour: an unbounded second palette.
9
+ //
10
+ // So the TYPE is a shape and the STATE is the ink:
11
+ //
12
+ // --text-dim at rest
13
+ // --text when a wire lands on it
14
+ // --accent ringed, while a live drag could land here
15
+ // opacity .45 while a live drag could NOT
16
+ //
17
+ // A reader learns nine shapes once; they then read identically in both themes,
18
+ // at every zoom, and for an author who cannot tell red from green.
19
+ //
20
+ // ── AND WHY THE TABLE IS A TYPED `Record` ────────────────────────────────
21
+ //
22
+ // `Record<MaterialGraphSocketType, …>` and not a partial map with a fallback: a
23
+ // tenth socket type added to the registry must be a compile error here rather
24
+ // than a handle that silently draws as a circle and lies about what it carries.
25
+ import type { MaterialGraphSocketType } from "@dailies-studio/shot/doc";
26
+
27
+ export interface MaterialSocketGlyph {
28
+ /** The class that draws the shape. Every one of these resolves in
29
+ * `styles.css` and paints from `--text*` / `--border*` / `--accent*` only —
30
+ * `materialSocketGlyph.test.ts` holds both halves of that. */
31
+ shape: string;
32
+ /** What the shape means, said in words for the handle's tooltip. */
33
+ label: string;
34
+ }
35
+
36
+ /**
37
+ * The nine shapes, chosen so that width reads as width: a float is the smallest
38
+ * closed shape, a vec2 is a diamond, a vec3 a square, a vec4 a rounded square.
39
+ * The two hollow rings are the two things that are not plain numbers (a colour
40
+ * and a texture handle), and the two broken outlines are the two that are not a
41
+ * concrete value (an integer's discrete steps, a generic's "whatever arrives").
42
+ */
43
+ export const SOCKET_GLYPHS: Record<MaterialGraphSocketType, MaterialSocketGlyph> = {
44
+ float: { shape: "material-socket-circle", label: "Float" },
45
+ int: { shape: "material-socket-circle-dotted", label: "Integer" },
46
+ bool: { shape: "material-socket-triangle", label: "Boolean" },
47
+ vec2: { shape: "material-socket-diamond", label: "Vector 2" },
48
+ vec3: { shape: "material-socket-square", label: "Vector 3" },
49
+ vec4: { shape: "material-socket-square-round", label: "Vector 4" },
50
+ color: { shape: "material-socket-ring", label: "Colour" },
51
+ texture: { shape: "material-socket-ring-double", label: "Texture" },
52
+ numeric: { shape: "material-socket-circle-dashed", label: "Any number" },
53
+ };
54
+
55
+ /**
56
+ * What a LIVE drag says about this handle.
57
+ *
58
+ * `"idle"` is the resting state and the only one there is when no wire is in
59
+ * hand. The other two are the answer `canConnect` already gives — shown rather
60
+ * than discovered by dropping and being refused.
61
+ */
62
+ export type MaterialSocketDrop = "idle" | "ok" | "blocked";
63
+
64
+ /** The whole class list for one handle, in one place, so the card renderers
65
+ * cannot each spell the state modifiers slightly differently. */
66
+ export function socketGlyphClass(
67
+ type: MaterialGraphSocketType,
68
+ state: { connected: boolean; drop: MaterialSocketDrop },
69
+ ): string {
70
+ const glyph = SOCKET_GLYPHS[type];
71
+ return [
72
+ "material-socket",
73
+ glyph.shape,
74
+ state.connected ? "is-connected" : "",
75
+ state.drop === "ok" ? "is-droppable" : "",
76
+ state.drop === "blocked" ? "is-blocked" : "",
77
+ ]
78
+ .filter((part) => part !== "")
79
+ .join(" ");
80
+ }
81
+
82
+ /** The handle's tooltip: what it is called, and what it carries. */
83
+ export function socketTitle(label: string, type: MaterialGraphSocketType): string {
84
+ return `${label} · ${SOCKET_GLYPHS[type].label}`;
85
+ }
@@ -0,0 +1,58 @@
1
+ // Where the author last left each graph, per scope.
2
+ //
3
+ // ── WHY A MODULE MAP AND NOT THE DOCUMENT ───────────────────────────────
4
+ //
5
+ // A pan and a zoom are the same class of thing as `materialSnapToGrid`: they
6
+ // describe where a person is looking, not what the material is. Writing them to
7
+ // `Doc3D` would make scrolling the canvas a document edit — an undo entry, an
8
+ // autosave, and a diff in every agent's next read of the file — for a gesture
9
+ // that changes no pixel of the render.
10
+ //
11
+ // ── AND WHY NOT COMPONENT STATE ─────────────────────────────────────────
12
+ //
13
+ // The canvas UNMOUNTS whenever the author flips to the TSL view, enters a
14
+ // subgraph, or changes slot. Component state dies with it, so every one of
15
+ // those cost a `fitView` back to the origin — the author lost their place for
16
+ // pressing a button and coming back. A module Map outlives the component and
17
+ // dies with the tab, which is exactly the lifetime a "where was I" belongs to.
18
+ //
19
+ // Keyed by material AND scope trail, because a function body is its own canvas
20
+ // with its own layout; sharing one viewport across the two would land the
21
+ // author somewhere arbitrary on every entry.
22
+
23
+ export interface GraphViewport {
24
+ x: number;
25
+ y: number;
26
+ zoom: number;
27
+ }
28
+
29
+ /** `materialId + "/" + path.join("/")`. The main graph's trail is empty, so its
30
+ * key ends in a trailing slash — deliberate, since it keeps the main scope and
31
+ * a function called "" (which cannot exist) from ever colliding. */
32
+ export function materialViewportKey(materialId: string, path: readonly string[]): string {
33
+ return `${materialId}/${path.join("/")}`;
34
+ }
35
+
36
+ // ponytail: unbounded, one entry per (material, scope) an author visits. A
37
+ // session reaches dozens; a cap would be eviction policy for nothing.
38
+ const REMEMBERED = new Map<string, GraphViewport>();
39
+
40
+ export function rememberMaterialViewport(key: string, viewport: GraphViewport): void {
41
+ // Garbage in a viewport is not hypothetical: React Flow reports NaN for a
42
+ // moment when its container is measured at zero, which is every mount under
43
+ // jsdom and a real one behind a `display: none` panel. Storing it would make
44
+ // the NEXT entry restore to nowhere with no way back but the frame key.
45
+ if (!Number.isFinite(viewport.x) || !Number.isFinite(viewport.y)) return;
46
+ if (!Number.isFinite(viewport.zoom) || viewport.zoom <= 0) return;
47
+ REMEMBERED.set(key, { x: viewport.x, y: viewport.y, zoom: viewport.zoom });
48
+ }
49
+
50
+ export function recallMaterialViewport(key: string): GraphViewport | null {
51
+ return REMEMBERED.get(key) ?? null;
52
+ }
53
+
54
+ /** For the suites, which must not inherit one another's pan. Never called by
55
+ * the app: a session's memory has no reason to be thrown away. */
56
+ export function clearMaterialViewports(): void {
57
+ REMEMBERED.clear();
58
+ }
@@ -0,0 +1,215 @@
1
+ // The Preferences dialog (gap 5, Paper "PreferencesDialog / dark").
2
+ //
3
+ // Opened by the app menu row, ⌘, and the `prefs.open` WebMCP verb, all of
4
+ // which set `prefsOpen`; mounted once, in AppMenu. Every control applies at
5
+ // once through the setter the rest of the chrome already uses (the View menu
6
+ // rows, ⇧R), so there is no Save: Done only closes.
7
+ //
8
+ // It is chrome state, never `edit`: a viewer can open it and change any row.
9
+ import type { ReactNode } from "react";
10
+ import { Dialog, SegmentedControl, Toggle } from "@dailies-studio/ui";
11
+ import { useStudioTheme } from "@dailies-studio/ui/theme";
12
+ import type { StudioThemePreference } from "@dailies-studio/ui/theme";
13
+ import { isPlaybackResolution } from "../../bridgeTypes.js";
14
+ import type { PlaybackResolution } from "../../bridgeTypes.js";
15
+ import { useShotEditor, useStore } from "../../state/context.js";
16
+ import { isCloudWorkspace } from "../../state/posterCapture.js";
17
+ import { shortcutFor } from "../../state/shortcuts.js";
18
+
19
+ const THEMES: { value: StudioThemePreference; label: string }[] = [
20
+ { value: "system", label: "System" },
21
+ { value: "light", label: "Light" },
22
+ { value: "dark", label: "Dark" },
23
+ ];
24
+
25
+ const PLAYBACK_RESOLUTION_OPTIONS: { value: PlaybackResolution; label: string }[] = [
26
+ { value: "auto", label: "Auto" },
27
+ { value: "full", label: "Full" },
28
+ { value: "fast", label: "Fast" },
29
+ ];
30
+
31
+ function Section(props: { label: string; children: ReactNode }) {
32
+ return (
33
+ <section className="prefs-section" aria-label={props.label}>
34
+ <div className="prefs-section-label">{props.label}</div>
35
+ {props.children}
36
+ </section>
37
+ );
38
+ }
39
+
40
+ /** One row: the label (and a hint under it) on the left, the control right.
41
+ * The control takes the label as its accessible name. */
42
+ function Row(props: { label: string; hint?: string; children: ReactNode }) {
43
+ return (
44
+ <div className="prefs-row">
45
+ <div className="prefs-row-text">
46
+ <span className="prefs-row-label">{props.label}</span>
47
+ {props.hint !== undefined && <span className="prefs-row-hint">{props.hint}</span>}
48
+ </div>
49
+ <div className="prefs-row-control">{props.children}</div>
50
+ </div>
51
+ );
52
+ }
53
+
54
+ export function PreferencesDialog() {
55
+ const store = useStore();
56
+ const open = useShotEditor((s) => s.prefsOpen);
57
+ const { preference, setPreference } = useStudioTheme();
58
+ const rulers = useShotEditor((s) => s.rulers);
59
+ const showGrid = useShotEditor((s) => s.showGrid);
60
+ const smartGuides = useShotEditor((s) => s.smartGuides);
61
+ const motionPaths = useShotEditor((s) => s.motionPaths);
62
+ const toolbarPosition = useShotEditor((s) => s.toolbarPosition);
63
+ const playbackResolution = useShotEditor((s) => s.playbackResolution);
64
+ const blockLabels = useShotEditor((s) => s.blockLabels);
65
+ const selectMovesPlayhead = useShotEditor((s) => s.selectMovesPlayhead);
66
+ const fieldLabels = useShotEditor((s) => s.fieldLabels);
67
+ const showFpsBadge = useShotEditor((s) => s.showFpsBadge);
68
+ const showBackendBadge = useShotEditor((s) => s.showBackendBadge);
69
+ const close = (): void => store.setPrefsOpen(false);
70
+ const rulersKey = shortcutFor("view.rulers");
71
+
72
+ return (
73
+ <Dialog
74
+ open={open}
75
+ size="wide"
76
+ className="prefs-dialog"
77
+ onClose={close}
78
+ title={
79
+ <span className="prefs-title">
80
+ Preferences
81
+ <kbd className="prefs-kbd">{shortcutFor("prefs.open")}</kbd>
82
+ </span>
83
+ }
84
+ actions={
85
+ <>
86
+ <span className="prefs-footnote" data-testid="prefs-footnote">
87
+ {isCloudWorkspace() ? "Saved to your account. This browser keeps a copy." : "Saved in this browser."}
88
+ </span>
89
+ <button type="button" className="btn is-primary" onClick={close}>
90
+ Done
91
+ </button>
92
+ </>
93
+ }
94
+ >
95
+ <div className="prefs-body" data-testid="prefs-dialog">
96
+ <Section label="Appearance">
97
+ <Row label="Theme">
98
+ <SegmentedControl
99
+ ariaLabel="Theme"
100
+ options={THEMES}
101
+ value={preference}
102
+ onChange={(v) => setPreference(v as StudioThemePreference)}
103
+ />
104
+ </Row>
105
+ </Section>
106
+ <Section label="Canvas">
107
+ <Row label="Rulers" hint={`On the camera view and inside a component.${rulersKey === undefined ? "" : ` ${rulersKey}`}`}>
108
+ <Toggle ariaLabel="Rulers" checked={rulers} onChange={(on) => store.setEditorPref("rulers", on)} />
109
+ </Row>
110
+ <Row label="Grid">
111
+ <Toggle
112
+ ariaLabel="Grid"
113
+ checked={showGrid}
114
+ onChange={(on) => {
115
+ if (on !== store.getState().showGrid) store.toggleGrid();
116
+ }}
117
+ />
118
+ </Row>
119
+ <Row label="Smart guides" hint="Ctrl turns snapping off while you drag">
120
+ <Toggle
121
+ ariaLabel="Smart guides"
122
+ checked={smartGuides}
123
+ onChange={(on) => store.setEditorPref("smartGuides", on)}
124
+ />
125
+ </Row>
126
+ <Row label="Motion paths" hint="Path of the selected layer's keys">
127
+ <Toggle
128
+ ariaLabel="Motion paths"
129
+ checked={motionPaths}
130
+ onChange={(on) => store.setEditorPref("motionPaths", on)}
131
+ />
132
+ </Row>
133
+ <Row label="Toolbar position">
134
+ <SegmentedControl
135
+ ariaLabel="Toolbar position"
136
+ options={[
137
+ { value: "bottom", label: "Bottom" },
138
+ { value: "top", label: "Top" },
139
+ ]}
140
+ value={toolbarPosition}
141
+ onChange={(v) => store.setEditorPref("toolbarPosition", v === "top" ? "top" : "bottom")}
142
+ />
143
+ </Row>
144
+ <Row
145
+ label="Playback resolution"
146
+ hint="Auto lowers it while playing only when the GPU falls behind. Paused frames and exports stay full."
147
+ >
148
+ <SegmentedControl
149
+ ariaLabel="Playback resolution"
150
+ options={PLAYBACK_RESOLUTION_OPTIONS}
151
+ value={playbackResolution}
152
+ onChange={(v) => store.setEditorPref("playbackResolution", isPlaybackResolution(v) ? v : "auto")}
153
+ />
154
+ </Row>
155
+ </Section>
156
+ <Section label="Timeline">
157
+ <Row label="Camera move labels">
158
+ <SegmentedControl
159
+ ariaLabel="Camera move labels"
160
+ options={[
161
+ { value: "fit", label: "When they fit" },
162
+ { value: "off", label: "Off" },
163
+ ]}
164
+ value={blockLabels}
165
+ onChange={(v) => store.setEditorPref("blockLabels", v === "off" ? "off" : "fit")}
166
+ />
167
+ </Row>
168
+ <Row label="Move playhead to selection">
169
+ <Toggle
170
+ ariaLabel="Move playhead to selection"
171
+ checked={selectMovesPlayhead}
172
+ onChange={(on) => store.setSelectMovesPlayhead(on)}
173
+ />
174
+ </Row>
175
+ </Section>
176
+ <Section label="Inspector">
177
+ <Row label="Field labels" hint="Compact shows a glyph. Named shows the word.">
178
+ <SegmentedControl
179
+ ariaLabel="Field labels"
180
+ options={[
181
+ { value: "compact", label: "Compact" },
182
+ { value: "named", label: "Named" },
183
+ ]}
184
+ value={fieldLabels}
185
+ onChange={(v) => store.setEditorPref("fieldLabels", v === "named" ? "named" : "compact")}
186
+ />
187
+ </Row>
188
+ </Section>
189
+ <Section label="Status">
190
+ <Row label="FPS badge">
191
+ <Toggle ariaLabel="FPS badge" checked={showFpsBadge} onChange={(on) => store.setFpsBadge(on)} />
192
+ </Row>
193
+ <Row label="Renderer badge">
194
+ <Toggle
195
+ ariaLabel="Renderer badge"
196
+ checked={showBackendBadge}
197
+ onChange={(on) => store.setBackendBadge(on)}
198
+ />
199
+ </Row>
200
+ </Section>
201
+ <Section label="Layout">
202
+ <Row label="Panel sizes and docks">
203
+ <button
204
+ type="button"
205
+ className="btn"
206
+ onClick={() => store.resetLayout(window.innerWidth, window.innerHeight)}
207
+ >
208
+ Reset panel layout
209
+ </button>
210
+ </Row>
211
+ </Section>
212
+ </div>
213
+ </Dialog>
214
+ );
215
+ }