@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,525 @@
1
+ // A REUSABLE GRAPH'S EXPOSED CONTROLS, in the Material section of the mesh
2
+ // that wears it (MaterialSection.tsx): first the controls themselves, each a
3
+ // card editing its definition on the graph, then the per-mesh rows, which edit
4
+ // THIS mesh's value and keyframes.
5
+ //
6
+ // Both are `PropRow`s, the one row every ControlSpec surface renders, so a
7
+ // float with a range is the range field, a colour is the colour field, and a
8
+ // vector's axes are named boxes, exactly as a component's props are. Listed in
9
+ // the graph's own order (its declaration), never alphabetically, the rule
10
+ // every ControlSpec list follows.
11
+ import { useEffect, useMemo, useRef, useState } from "react";
12
+ import { IconClose, PropRow, useLocked } from "@dailies-studio/ui";
13
+ import type { PropRowReset, ScrubHandlers } from "@dailies-studio/ui";
14
+ import type { ControlSpec } from "@dailies-studio/contract";
15
+ import { valueEquals, type Value } from "@dailies-studio/engine";
16
+ import {
17
+ containerFrame,
18
+ homeOf,
19
+ isGraphMaterial,
20
+ isMaterialGraphValueForType,
21
+ materialBindingForSlot,
22
+ materialGraphParamPath,
23
+ materialGraphParameterValue,
24
+ resolveTracksAt,
25
+ } from "@dailies-studio/shot/doc";
26
+ import type {
27
+ MaterialGraph3D,
28
+ MaterialGraphParameter,
29
+ MaterialGraphValue,
30
+ } from "@dailies-studio/shot/doc";
31
+ import { useShotEditor, useStore } from "../../state/context.js";
32
+ import { writeMeshGraphOverride } from "../../state/store.js";
33
+ import { useExposeOffered } from "../inspector/ExposeDot.js";
34
+ import { KeyframeDot } from "../inspector/KeyframeDot.js";
35
+ import { isVectorSpec } from "../inspector/propValue.js";
36
+ import { useScrub } from "../inspector/useDocWrite.js";
37
+ import {
38
+ clampParameterValues,
39
+ valuesOutsideRange,
40
+ withParameterRange,
41
+ writeParameterDefault,
42
+ } from "./parameterValues.js";
43
+ import type { ParameterRange } from "./parameterValues.js";
44
+
45
+ export function MaterialParameterControls({
46
+ graph,
47
+ containerId,
48
+ layerId,
49
+ slot,
50
+ }: {
51
+ graph: MaterialGraph3D;
52
+ containerId: string;
53
+ layerId: string;
54
+ /** The binding slot `graph` is bound on (`governingMaterialSlot`): a
55
+ * submesh with its own graph, or null for the whole model. Values, keys and
56
+ * writes all address that binding. */
57
+ slot: number | null;
58
+ }) {
59
+ const parameters = Object.values(graph.parameters);
60
+ if (parameters.length === 0) {
61
+ return <p className="material-empty-copy">No exposed controls yet. Add one to animate a graph value per mesh.</p>;
62
+ }
63
+ return (
64
+ <div className="material-parameter-list">
65
+ {parameters.map((parameter) => (
66
+ <MaterialParameterRow
67
+ key={parameter.id}
68
+ graph={graph}
69
+ parameter={parameter}
70
+ containerId={containerId}
71
+ layerId={layerId}
72
+ slot={slot}
73
+ />
74
+ ))}
75
+ </div>
76
+ );
77
+ }
78
+
79
+ /** Definition-side lifecycle. These values belong to the reusable graph;
80
+ * MaterialParameterControls above edits this mesh's binding and keyframes. */
81
+ export function MaterialParameterDefinitions({ graph }: { graph: MaterialGraph3D }) {
82
+ const parameters = Object.values(graph.parameters);
83
+ if (parameters.length === 0) return null;
84
+ return (
85
+ <div className="material-parameter-definitions">
86
+ {parameters.map((parameter) => (
87
+ <ParameterDefinition key={parameter.id} graph={graph} parameter={parameter} />
88
+ ))}
89
+ </div>
90
+ );
91
+ }
92
+
93
+ /** A float parameter's range, as `ParameterRange` reads it. */
94
+ function rangeOf(parameter: MaterialGraphParameter): ParameterRange {
95
+ return {
96
+ ...(parameter.min === undefined ? {} : { min: parameter.min }),
97
+ ...(parameter.max === undefined ? {} : { max: parameter.max }),
98
+ };
99
+ }
100
+
101
+ /** A range in words, for the clamp offer: "0 to 1", "at least 0". */
102
+ function rangeWords(range: ParameterRange): string {
103
+ if (range.min !== undefined && range.max !== undefined) return `${range.min} to ${range.max}`;
104
+ return range.min !== undefined ? `at least ${range.min}` : `at most ${range.max ?? 0}`;
105
+ }
106
+
107
+ /** A number field's spec for one of the definition's own numbers (a bound, the
108
+ * step): always a plain field, never a slider, since these numbers have no
109
+ * scale of their own. */
110
+ function fieldSpec(key: string, label: string, limits: ParameterRange, step: number): ControlSpec {
111
+ return {
112
+ key,
113
+ propName: key,
114
+ label,
115
+ kind: "number",
116
+ animatable: false,
117
+ as: "field",
118
+ default: Number.NaN,
119
+ step,
120
+ ...limits,
121
+ } as ControlSpec;
122
+ }
123
+
124
+ /**
125
+ * A DRAG ON A NUMBER WITH NO PICTURE TO PREVIEW (a bound, the step): the field
126
+ * shows the dragged value live, and the release commits it ONCE, through the
127
+ * same commit a typed value takes, so a bound dragged past a mesh's value
128
+ * still asks the clamp question rather than writing into a refusal. A row's
129
+ * field always routes its drag through its scrub halves, so without these a
130
+ * drag would move nothing at all.
131
+ */
132
+ function useReleaseDrag(commit: (value: number) => void): { scrub: ScrubHandlers; live: number | null } {
133
+ const [live, setLive] = useState<number | null>(null);
134
+ const last = useRef<number | null>(null);
135
+ const latest = useRef(commit);
136
+ latest.current = commit;
137
+ return {
138
+ live,
139
+ scrub: {
140
+ start: () => {
141
+ last.current = null;
142
+ },
143
+ move: (raw) => {
144
+ if (typeof raw !== "number" || !Number.isFinite(raw)) return;
145
+ last.current = raw;
146
+ setLive(raw);
147
+ },
148
+ end: (keep) => {
149
+ const value = last.current;
150
+ last.current = null;
151
+ setLive(null);
152
+ if (keep && value !== null) latest.current(value);
153
+ },
154
+ },
155
+ };
156
+ }
157
+
158
+ /**
159
+ * ONE CONTROL'S DEFINITION: its label, its default, and for a float its range
160
+ * and step, in a card under the parameter's name.
161
+ *
162
+ * ── A RANGE THAT TELLS THE TRUTH ────────────────────────────────────────
163
+ *
164
+ * An absent bound shows EMPTY (the field's dash), never a 0 or a 1 the graph
165
+ * does not hold, and a set one carries a Clear in its box that opens it
166
+ * again. Minimum is held under Maximum and Maximum over Minimum by their own
167
+ * limits, so the pair cannot cross. A new bound moves the default inside it in
168
+ * the same step (the graph refuses a default outside its range), and when a
169
+ * mesh already holds a value or a keyframe outside it, the bound is not
170
+ * written blind into a refusal: the card says how many and offers to clamp
171
+ * them with it, in one undo step, or to keep the range as it was.
172
+ */
173
+ function ParameterDefinition({ graph, parameter }: { graph: MaterialGraph3D; parameter: MaterialGraphParameter }) {
174
+ const store = useStore();
175
+ const doc = useShotEditor((s) => s.doc);
176
+ // The card's own buttons write, so a viewer's locked panel disables them
177
+ // with every field in it.
178
+ const locked = useLocked();
179
+ const [pending, setPending] = useState<{ range: ParameterRange; count: number } | null>(null);
180
+ const float = parameter.type === "float";
181
+ const range = rangeOf(parameter);
182
+ const name = parameter.label;
183
+ // An offer is about the range it was asked against: once the bounds move
184
+ // under it (an undo, another tab), it is a question nobody is asking.
185
+ useEffect(() => setPending(null), [parameter.min, parameter.max]);
186
+
187
+ const patch = (
188
+ change: (found: MaterialGraphParameter) => MaterialGraphParameter,
189
+ alsoDoc?: Parameters<typeof store.updateProjectMaterial>[2],
190
+ ): void => {
191
+ store.updateProjectMaterial(
192
+ graph.id,
193
+ (current) => {
194
+ const found = current.parameters[parameter.id];
195
+ if (found === undefined) return current;
196
+ const next = change(found);
197
+ return next === found ? current : { ...current, parameters: { ...current.parameters, [parameter.id]: next } };
198
+ },
199
+ alsoDoc,
200
+ );
201
+ };
202
+
203
+ const applyRange = (next: ParameterRange, clamp: boolean): void => {
204
+ setPending(null);
205
+ patch(
206
+ (found) => withParameterRange(found, next),
207
+ clamp ? (d) => clampParameterValues(d, graph.id, parameter.id, next) : undefined,
208
+ );
209
+ };
210
+ const setBound = (which: "min" | "max", value: number | null): void => {
211
+ const next: ParameterRange = { ...range };
212
+ if (value === null) delete next[which];
213
+ else next[which] = value;
214
+ // Opening a bound leaves every value inside; narrowing one may not.
215
+ const outside = value === null || doc === null ? 0 : valuesOutsideRange(doc, graph.id, parameter.id, next);
216
+ if (outside > 0) setPending({ range: next, count: outside });
217
+ else applyRange(next, false);
218
+ };
219
+
220
+ // A drag on the default moves the graph's default while the pointer is down,
221
+ // one undo entry, through the narrow write a scrub may make. The bounds and
222
+ // the step commit on release (`useReleaseDrag`): they change no picture to
223
+ // preview, and a new bound has the clamp question to ask first.
224
+ const defaultScrub = useScrub((next, raw) => writeParameterDefault(next, graph.id, parameter.id, raw));
225
+ const defaultSpec = useMemo(() => controlSpec(parameter), [parameter]);
226
+ const setStep = (raw: unknown): void => {
227
+ if (typeof raw === "number" && Number.isFinite(raw) && raw > 0) {
228
+ patch((found) => (found.step === raw ? found : { ...found, step: raw }));
229
+ }
230
+ };
231
+ const minDrag = useReleaseDrag((value) => setBound("min", value));
232
+ const maxDrag = useReleaseDrag((value) => setBound("max", value));
233
+ const stepDrag = useReleaseDrag(setStep);
234
+
235
+ // A CLEAR GOES WITH THE NUMBER IT CLEARS, so it unmounts under the press,
236
+ // and the focus fell to <body>: no ring, nothing announced, and the next
237
+ // keys went to the window's keymap. It hands the focus to the field it sat
238
+ // in, which stays, as a PropRow's reset does.
239
+ const clear = (words: string, title: string, run: () => void) => (
240
+ <button
241
+ type="button"
242
+ className="insp-key"
243
+ aria-label={`Clear ${name} ${words}`}
244
+ title={title}
245
+ disabled={locked}
246
+ onClick={(e) => {
247
+ const field = e.currentTarget.closest(".numfield")?.querySelector<HTMLElement>(".numfield-input");
248
+ run();
249
+ field?.focus();
250
+ }}
251
+ >
252
+ <IconClose />
253
+ </button>
254
+ );
255
+ const clearStep = (): void =>
256
+ patch((found) => {
257
+ if (found.step === undefined) return found;
258
+ const { step: _step, ...rest } = found;
259
+ return rest;
260
+ });
261
+ // The label is typed, never dragged.
262
+ const noScrub: ScrubHandlers = { start: () => {}, move: () => {}, end: () => {} };
263
+ // A field's accessible name starts with the words its row shows ("Minimum,
264
+ // Roughness"), the house rule, so speech input can say what it sees, and the
265
+ // control's name tells the cards apart.
266
+ const named = (words: string): string => `${words}, ${name}`;
267
+
268
+ return (
269
+ <details className="material-parameter-definition">
270
+ <summary>
271
+ <span>{parameter.label}</span>
272
+ <small>{parameter.type}</small>
273
+ </summary>
274
+ <div className="material-parameter-definition-body">
275
+ <PropRow
276
+ spec={{ key: "label", propName: "label", label: "Label", kind: "string", animatable: false, default: "" } as ControlSpec}
277
+ value={parameter.label}
278
+ name={named("Label")}
279
+ commit={(raw) => {
280
+ if (typeof raw === "string") patch((found) => (found.label === raw.trim() ? found : { ...found, label: raw.trim() }));
281
+ }}
282
+ scrub={noScrub}
283
+ />
284
+ <PropRow
285
+ spec={defaultSpec}
286
+ label="Default"
287
+ name={named("Default")}
288
+ value={parameter.default}
289
+ // Graph colours are `#rrggbb`: the picker offers no alpha.
290
+ {...(parameter.type === "color" ? { opaqueColor: true } : {})}
291
+ commit={(raw) => {
292
+ if (isMaterialGraphValueForType(raw, parameter.type)) {
293
+ patch((found) => (valueEquals(found.default as Value, raw as Value) ? found : { ...found, default: raw }));
294
+ }
295
+ }}
296
+ scrub={defaultScrub}
297
+ />
298
+ {float ? (
299
+ <>
300
+ <PropRow
301
+ spec={fieldSpec("min", "Minimum", parameter.max === undefined ? {} : { max: parameter.max }, parameter.step ?? 0.01)}
302
+ name={named("Minimum")}
303
+ value={minDrag.live ?? parameter.min ?? Number.NaN}
304
+ commit={(raw) => {
305
+ if (typeof raw === "number" && Number.isFinite(raw)) setBound("min", raw);
306
+ }}
307
+ scrub={minDrag.scrub}
308
+ {...(parameter.min === undefined
309
+ ? {}
310
+ : { trailing: clear("minimum", "Clear the minimum: the control is open below", () => setBound("min", null)) })}
311
+ />
312
+ <PropRow
313
+ spec={fieldSpec("max", "Maximum", parameter.min === undefined ? {} : { min: parameter.min }, parameter.step ?? 0.01)}
314
+ name={named("Maximum")}
315
+ value={maxDrag.live ?? parameter.max ?? Number.NaN}
316
+ commit={(raw) => {
317
+ if (typeof raw === "number" && Number.isFinite(raw)) setBound("max", raw);
318
+ }}
319
+ scrub={maxDrag.scrub}
320
+ {...(parameter.max === undefined
321
+ ? {}
322
+ : { trailing: clear("maximum", "Clear the maximum: the control is open above", () => setBound("max", null)) })}
323
+ />
324
+ {pending === null ? null : (
325
+ <div className="insp-notice is-warn" role="status" data-testid="material-parameter-clamp">
326
+ <span>
327
+ {pending.count === 1
328
+ ? `A mesh holds a value outside ${rangeWords(pending.range)}.`
329
+ : `Meshes hold ${pending.count} values outside ${rangeWords(pending.range)}.`}
330
+ </span>
331
+ {/* The two answers on the house row of buttons: 28px house
332
+ buttons, 6px apart, sharing the row by their words. */}
333
+ <div className="insp-buttons material-parameter-clamp-actions">
334
+ <button
335
+ type="button"
336
+ className="btn"
337
+ data-testid="material-parameter-clamp-apply"
338
+ disabled={locked}
339
+ title="Set the range and move those values to its nearest end, in one undo step"
340
+ onClick={() => applyRange(pending.range, true)}
341
+ >
342
+ Clamp them
343
+ </button>
344
+ <button
345
+ type="button"
346
+ className="btn"
347
+ data-testid="material-parameter-clamp-cancel"
348
+ onClick={() => setPending(null)}
349
+ >
350
+ Keep the range
351
+ </button>
352
+ </div>
353
+ </div>
354
+ )}
355
+ <PropRow
356
+ spec={fieldSpec("step", "Step", { min: 0.0001 }, 0.01)}
357
+ name={named("Step")}
358
+ value={stepDrag.live ?? parameter.step ?? Number.NaN}
359
+ commit={setStep}
360
+ scrub={stepDrag.scrub}
361
+ {...(parameter.step === undefined
362
+ ? {}
363
+ : { trailing: clear("step", "Clear the step: the field moves by its range's own quantum", clearStep) })}
364
+ />
365
+ </>
366
+ ) : null}
367
+ <div className="insp-buttons material-delete-parameter">
368
+ <button
369
+ type="button"
370
+ className="btn is-danger"
371
+ disabled={locked}
372
+ onClick={() => store.deleteProjectMaterialParameter(graph.id, parameter.id)}
373
+ >
374
+ Delete control
375
+ </button>
376
+ </div>
377
+ </div>
378
+ </details>
379
+ );
380
+ }
381
+
382
+ /**
383
+ * One control's value ON THIS MESH: the row, its diamond, its reset.
384
+ *
385
+ * The value shown is the binding's AT THE FRAME (an animated control shows its
386
+ * sampled value); whether the mesh stores a value of its own, and whether a
387
+ * track drives it, are read off the AUTHORED binding.
388
+ */
389
+ function MaterialParameterRow({
390
+ graph,
391
+ parameter,
392
+ containerId,
393
+ layerId,
394
+ slot,
395
+ }: {
396
+ graph: MaterialGraph3D;
397
+ parameter: MaterialGraphParameter;
398
+ containerId: string;
399
+ layerId: string;
400
+ slot: number | null;
401
+ }) {
402
+ const store = useStore();
403
+ const doc = useShotEditor((state) => state.doc);
404
+ const frame = useShotEditor((state) => state.frame);
405
+ const defFrame = useShotEditor((state) => state.defFrame);
406
+ const editingDef = useShotEditor((state) => state.editingDef);
407
+ const layer = doc === null ? undefined : homeOf(doc, containerId)?.layersById[layerId];
408
+ const localFrame =
409
+ doc === null ? 0 : containerFrame(doc, containerId, editingDef === containerId ? defFrame : frame);
410
+ const effective =
411
+ doc === null || layer === undefined ? undefined : resolveTracksAt(layer, localFrame, doc.fps);
412
+ const binding = effective?.kind === "mesh" && isGraphMaterial(effective.mesh.material)
413
+ ? materialBindingForSlot(effective.mesh.material, slot)
414
+ : undefined;
415
+ const value = materialGraphParameterValue(graph, binding?.overrides, parameter.id) ?? parameter.default;
416
+ const authored =
417
+ layer?.kind === "mesh" && isGraphMaterial(layer.mesh.material)
418
+ ? materialBindingForSlot(layer.mesh.material, slot)
419
+ : undefined;
420
+ const override = authored?.overrides?.[parameter.id];
421
+ const path = materialGraphParamPath(parameter.id, slot);
422
+ const tracked = layer?.tracks?.[path] !== undefined;
423
+ const spec = controlSpec(parameter);
424
+ const vector = isVectorSpec(spec);
425
+ // The column draws a slot for whatever it is handed, even a pin that
426
+ // renders nothing, so the row asks before it hands one over.
427
+ const pinOffered = useExposeOffered(path);
428
+ const scrub = useScrub((base, raw) => {
429
+ const current = store.getState();
430
+ const next = graphValue(raw, parameter);
431
+ if (next === null) return base;
432
+ return writeMeshGraphOverride(
433
+ base,
434
+ containerId,
435
+ layerId,
436
+ parameter.id,
437
+ next,
438
+ containerFrame(base, containerId, current.editingDef === containerId ? current.defFrame : current.frame),
439
+ slot,
440
+ );
441
+ });
442
+
443
+ // THE RESET, in the label column, so the box and its diamond never move
444
+ // when it appears. Shown while this mesh holds a value of its own or a track
445
+ // drives the control, the rule a component's rows follow (InstanceSection,
446
+ // THIS USE): drawn on every row, it took a 16px lane and a gap from every
447
+ // untouched mesh's name, which then read "Roughn…" at the default width.
448
+ // Under a track it is refused with the way out: a reset would take the
449
+ // keyframes with it (clearing a tracked value reads to the router as
450
+ // "remove its animation").
451
+ const reset: PropRowReset | undefined =
452
+ override === undefined && !tracked
453
+ ? undefined
454
+ : {
455
+ onReset: () => store.setMeshGraphOverride(layerId, parameter.id, null, slot),
456
+ title: "Use the material's default on this mesh",
457
+ ...(tracked
458
+ ? {
459
+ refused: `${parameter.label} is animated on this mesh: its keyframes drive it. Remove them on the timeline to use the material's default.`,
460
+ }
461
+ : {}),
462
+ };
463
+
464
+ return (
465
+ <PropRow
466
+ spec={spec}
467
+ value={value}
468
+ commit={(raw) => {
469
+ const next = graphValue(raw, parameter);
470
+ if (next !== null) store.setMeshGraphOverride(layerId, parameter.id, next, slot);
471
+ }}
472
+ scrub={scrub}
473
+ // Every box of a vec2 or vec3 parameter carries its one track's
474
+ // diamond, naming the axes that key together. Its one expose pin sits
475
+ // in the label column instead, once per row (KeyframeDot's header):
476
+ // beside the first box's diamond it overflowed that third of the row.
477
+ // A one-box parameter keeps both in its box.
478
+ trailing={({ axes }) => <KeyframeDot path={path} label={parameter.label} pin={!vector} parts={axes} />}
479
+ {...(vector && pinOffered ? { actions: <KeyframeDot path={path} label={parameter.label} diamond={false} /> } : {})}
480
+ {...(reset === undefined ? {} : { reset })}
481
+ // THE BLUE DOT: this mesh holds its own value, not the material's.
482
+ changed={override !== undefined && !valueEquals(override as Value, parameter.default as Value)}
483
+ changedLabel="Changed on this mesh"
484
+ inherited={override === undefined && !tracked}
485
+ // A graph colour is `#rrggbb` (`isMaterialGraphValueForType`): the
486
+ // picker offers no alpha, so a drag cannot write a value it refuses.
487
+ {...(parameter.type === "color" ? { opaqueColor: true } : {})}
488
+ description={`Material parameter ${parameter.id}`}
489
+ rowProps={{ "data-material-parameter": parameter.id }}
490
+ />
491
+ );
492
+ }
493
+
494
+ function controlSpec(parameter: MaterialGraphParameter): ControlSpec {
495
+ const base = {
496
+ key: parameter.id,
497
+ propName: parameter.id,
498
+ label: parameter.label,
499
+ animatable: true as const,
500
+ };
501
+ if (parameter.type === "float") {
502
+ return {
503
+ ...base,
504
+ kind: "number",
505
+ default: parameter.default as number,
506
+ ...(parameter.min === undefined ? {} : { min: parameter.min }),
507
+ ...(parameter.max === undefined ? {} : { max: parameter.max }),
508
+ ...(parameter.step === undefined ? {} : { step: parameter.step }),
509
+ };
510
+ }
511
+ if (parameter.type === "color") return { ...base, kind: "color", default: parameter.default as string };
512
+ if (parameter.type === "vec2") {
513
+ return { ...base, kind: "vec2", default: parameter.default as [number, number], step: parameter.step ?? 0.01 };
514
+ }
515
+ return {
516
+ ...base,
517
+ kind: "vec3",
518
+ default: parameter.default as [number, number, number],
519
+ step: parameter.step ?? 0.01,
520
+ };
521
+ }
522
+
523
+ function graphValue(raw: unknown, parameter: MaterialGraphParameter): MaterialGraphValue | null {
524
+ return isMaterialGraphValueForType(raw, parameter.type) ? raw : null;
525
+ }
@@ -0,0 +1,42 @@
1
+ // A wire's elbow: one dot, one input, one output.
2
+ //
3
+ // The registry types it `passthrough`, so it is genuinely transparent — the
4
+ // compiler emits the upstream expression and the dot costs nothing. It exists
5
+ // because a graph with a wire crossing the whole canvas is a graph nobody can
6
+ // read, and dragging a card sideways to fix that changes the layout of the
7
+ // thing rather than the route of the wire.
8
+ //
9
+ // Drawn small on purpose. It is punctuation, not a card, and giving it a title
10
+ // bar would make an author look for a value in it.
11
+ import { memo } from "react";
12
+ import { Handle, Position } from "@xyflow/react";
13
+ import type { NodeProps } from "@xyflow/react";
14
+ import { socketGlyphClass, socketTitle } from "./socketGlyph.js";
15
+ import { useSocketDrop } from "./MaterialNode.js";
16
+ import type { MaterialFlowNode } from "./flowModel.js";
17
+
18
+ export const MaterialRerouteNode = memo(function MaterialRerouteNode({ data }: NodeProps<MaterialFlowNode>) {
19
+ const { node, connected, outputType } = data;
20
+ // The dot wears the type it is CARRYING once one is resolved, so a chain of
21
+ // reroutes still says what is flowing through it. Unresolved (nothing wired
22
+ // in yet) falls back to the generic dashed circle its spec declares.
23
+ const type = outputType === null || outputType === "texture" ? "numeric" : outputType;
24
+ const inDrop = useSocketDrop(node.id, "value", "target");
25
+ const outDrop = useSocketDrop(node.id, "value", "source");
26
+ return (
27
+ <div className="material-reroute" data-testid={`material-node-${node.id}`} title={socketTitle("Reroute", type)}>
28
+ <Handle
29
+ type="target"
30
+ position={Position.Left}
31
+ id="value"
32
+ className={socketGlyphClass(type, { connected: connected.has("in:value"), drop: inDrop })}
33
+ />
34
+ <Handle
35
+ type="source"
36
+ position={Position.Right}
37
+ id="value"
38
+ className={socketGlyphClass(type, { connected: connected.has("out:value"), drop: outDrop })}
39
+ />
40
+ </div>
41
+ );
42
+ });