@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,632 @@
1
+ import {
2
+ defaultSplinePrimitive,
3
+ splineFrameScale,
4
+ splineProfileAt,
5
+ splineSampleTable,
6
+ } from "@dailies-studio/shot/doc";
7
+ import type { SplinePoint3D, SplinePrimitive } from "@dailies-studio/shot/doc";
8
+ import { Matrix4, Plane, Vector3 } from "three";
9
+ import type { Camera, Ray } from "three";
10
+ import type { ResolvedWorkPlane, SplinePointPart, WorkPlaneTarget } from "../bridgeTypes.js";
11
+ import type { LayerFrame, ScreenQuad } from "./project.js";
12
+ import { toScreen } from "./project.js";
13
+ import type { WorkingView } from "./workingView.js";
14
+ import { viewBasis } from "./workingView.js";
15
+
16
+ export interface ScreenSplinePoint {
17
+ id: string;
18
+ p: readonly [number, number];
19
+ in?: readonly [number, number];
20
+ out?: readonly [number, number];
21
+ selected: boolean;
22
+ }
23
+
24
+ export interface ScreenSplineStation {
25
+ at: readonly [number, number];
26
+ /** Canonical full-curve arc progress, suitable for `sampleSpline`. */
27
+ arcProgress: number;
28
+ /** Authored span index plus local span progress. */
29
+ curveT: number;
30
+ /**
31
+ * Conservative projected reach of the rendered profile around the
32
+ * centreline. It includes taper and profile offset. Guide-only splines and
33
+ * drafts keep this at zero because they render a centreline, not a loft.
34
+ */
35
+ profileRadiusPx?: number;
36
+ }
37
+
38
+ export interface ScreenSpline {
39
+ line: readonly (readonly [number, number])[];
40
+ stations: readonly ScreenSplineStation[];
41
+ points: readonly ScreenSplinePoint[];
42
+ cursor: readonly [number, number] | null;
43
+ closed: boolean;
44
+ closeHot: boolean;
45
+ editing: boolean;
46
+ /** Largest station reach, for bridge selection shapes. */
47
+ profileRadiusPx?: number;
48
+ }
49
+
50
+ export interface SplineControlHit {
51
+ pointId: string;
52
+ part: SplinePointPart;
53
+ }
54
+
55
+ export interface SplineSpanHit {
56
+ /** The authored anchor whose outgoing span was hit. */
57
+ spanIndex: number;
58
+ /** Local progress inside that authored span. */
59
+ spanProgress: number;
60
+ /** Canonical full-curve arc progress, suitable for `sampleSpline`. */
61
+ arcProgress: number;
62
+ }
63
+
64
+ const WORLD_XY: ResolvedWorkPlane = {
65
+ target: { kind: "xy" },
66
+ origin: [0, 0, 0],
67
+ xAxis: [1, 0, 0],
68
+ yAxis: [0, 1, 0],
69
+ normal: [0, 0, 1],
70
+ fallback: false,
71
+ };
72
+
73
+ /** Resolve an editor construction plane in schema coordinates. */
74
+ export function resolveWorkPlane(
75
+ target: WorkPlaneTarget,
76
+ workingView: WorkingView,
77
+ frames: readonly LayerFrame[] | null,
78
+ ): ResolvedWorkPlane {
79
+ switch (target.kind) {
80
+ case "xy":
81
+ return WORLD_XY;
82
+ case "xz":
83
+ return {
84
+ target,
85
+ origin: [0, 0, 0],
86
+ xAxis: [1, 0, 0],
87
+ yAxis: [0, 0, 1],
88
+ normal: [0, -1, 0],
89
+ fallback: false,
90
+ };
91
+ case "yz":
92
+ return {
93
+ target,
94
+ origin: [0, 0, 0],
95
+ xAxis: [0, 1, 0],
96
+ yAxis: [0, 0, 1],
97
+ normal: [1, 0, 0],
98
+ fallback: false,
99
+ };
100
+ case "view": {
101
+ const basis = viewBasis(workingView);
102
+ const x = worldVectorToDesign(basis.right);
103
+ // Plane y follows the display downward, opposite the camera's world up.
104
+ const y = worldVectorToDesign([-basis.up[0], -basis.up[1], -basis.up[2]]);
105
+ return planeRecord(
106
+ target,
107
+ worldPointToDesign(workingView.target),
108
+ x,
109
+ y,
110
+ false,
111
+ );
112
+ }
113
+ case "selection": {
114
+ const frame = frames?.find((candidate) => candidate.id === target.layerId);
115
+ if (frame === undefined) return { ...WORLD_XY, target, fallback: true };
116
+ const e = frame.matrixWorld.elements;
117
+ return planeRecord(
118
+ target,
119
+ [e[12]!, -e[13]!, -e[14]!],
120
+ [e[0]!, -e[1]!, -e[2]!],
121
+ [-e[4]!, e[5]!, e[6]!],
122
+ false,
123
+ );
124
+ }
125
+ }
126
+ }
127
+
128
+ /**
129
+ * The plane shown by the construction grid and used by the next anchor.
130
+ * Once a draft exists its plane wins by identity: orbiting a View plane or
131
+ * moving a Selection plane cannot rotate geometry already being authored.
132
+ */
133
+ export function splineWorkPlaneForDraft(
134
+ live: ResolvedWorkPlane,
135
+ frozen: ResolvedWorkPlane | null,
136
+ ): ResolvedWorkPlane {
137
+ return frozen ?? live;
138
+ }
139
+
140
+ /**
141
+ * Project the working-view focus onto a plane and snap the finite grid patch
142
+ * to that plane's own cell coordinates. The plane origin and basis remain the
143
+ * coordinate system, so panning moves the patch only by whole cells and the
144
+ * grid never appears to slide under an in-progress curve.
145
+ */
146
+ export function workPlaneGridCenter(
147
+ plane: ResolvedWorkPlane,
148
+ focus: readonly [number, number, number],
149
+ step: number,
150
+ ): [number, number, number] {
151
+ const safeStep = Number.isFinite(step) && step > 0 ? step : 1;
152
+ const delta: [number, number, number] = [
153
+ focus[0] - plane.origin[0],
154
+ focus[1] - plane.origin[1],
155
+ focus[2] - plane.origin[2],
156
+ ];
157
+ const u = Math.round(dot3(delta, plane.xAxis) / safeStep) * safeStep;
158
+ const v = Math.round(dot3(delta, plane.yAxis) / safeStep) * safeStep;
159
+ return [
160
+ plane.origin[0] + plane.xAxis[0] * u + plane.yAxis[0] * v,
161
+ plane.origin[1] + plane.xAxis[1] * u + plane.yAxis[1] * v,
162
+ plane.origin[2] + plane.xAxis[2] * u + plane.yAxis[2] * v,
163
+ ];
164
+ }
165
+
166
+ /** Intersect a stage ray with a resolved work plane and return design coords. */
167
+ export function pointOnWorkPlane(ray: Ray | null, plane: ResolvedWorkPlane): [number, number, number] | null {
168
+ if (ray === null) return null;
169
+ const normal = designVectorToWorld(plane.normal);
170
+ const origin = designPointToWorld(plane.origin);
171
+ const hit = ray.intersectPlane(new Plane().setFromNormalAndCoplanarPoint(normal, origin), new Vector3());
172
+ return hit === null || !finiteVector(hit) ? null : [cleanZero(hit.x), cleanZero(-hit.y), cleanZero(-hit.z)];
173
+ }
174
+
175
+ /** Project a world-design spline draft into the working pane. */
176
+ export function projectSplineDraft(
177
+ points: readonly SplinePoint3D[],
178
+ basis: "bezier" | "catmull-rom",
179
+ closed: boolean,
180
+ cursor: readonly [number, number, number] | null,
181
+ camera: Camera | null,
182
+ width: number,
183
+ height: number,
184
+ ): ScreenSpline | null {
185
+ if (points.length === 0 || camera === null || width <= 0 || height <= 0) return null;
186
+ const primitive = {
187
+ ...defaultSplinePrimitive([1, 1], points.map(copyPoint)),
188
+ basis,
189
+ closed,
190
+ };
191
+ const project = (point: readonly [number, number, number]): [number, number] =>
192
+ toScreen(designPointToWorld(point), camera, width, height);
193
+ const first = project(points[0]!.p);
194
+ const cursorScreen = cursor === null ? null : project(cursor);
195
+ const stations = splineSampleTable(primitive).stations.map((station) => ({
196
+ at: project(station.position),
197
+ arcProgress: station.arcProgress,
198
+ curveT: station.curveT,
199
+ profileRadiusPx: 0,
200
+ }));
201
+ return {
202
+ line: stations.map((station) => station.at),
203
+ stations,
204
+ points: points.map((point) => ({
205
+ id: point.id,
206
+ p: project(point.p),
207
+ ...(point.in === undefined ? {} : { in: project(point.in) }),
208
+ ...(point.out === undefined ? {} : { out: project(point.out) }),
209
+ selected: false,
210
+ })),
211
+ cursor: cursorScreen,
212
+ closed,
213
+ closeHot:
214
+ points.length >= 3 &&
215
+ cursorScreen !== null &&
216
+ Math.hypot(cursorScreen[0] - first[0], cursorScreen[1] - first[1]) <= 8,
217
+ editing: false,
218
+ profileRadiusPx: 0,
219
+ };
220
+ }
221
+
222
+ /** Project a committed spline's centreline and controls from layer-local space. */
223
+ export function projectSplineLayer(
224
+ spline: SplinePrimitive,
225
+ frame: LayerFrame,
226
+ camera: Camera | null,
227
+ width: number,
228
+ height: number,
229
+ selectedPointIds: readonly string[],
230
+ includeProfile = true,
231
+ ): ScreenSpline | null {
232
+ if (camera === null || width <= 0 || height <= 0) return null;
233
+ const scale = splineFrameScale(spline, frame.size);
234
+ const project = (point: readonly [number, number, number]): [number, number] =>
235
+ toScreen(splineLocalToWorld(point, scale, frame.matrixWorld), camera, width, height);
236
+ const selected = new Set(selectedPointIds);
237
+ let profileRadiusPx = 0;
238
+ const stations = splineSampleTable(spline).stations.map((station) => {
239
+ const world = splineLocalToWorld(station.position, scale, frame.matrixWorld);
240
+ const radius = includeProfile
241
+ ? projectedProfileRadius(
242
+ spline,
243
+ station.arcProgress,
244
+ scale[2],
245
+ frame.matrixWorld,
246
+ world,
247
+ camera,
248
+ width,
249
+ height,
250
+ )
251
+ : 0;
252
+ profileRadiusPx = Math.max(profileRadiusPx, radius);
253
+ return {
254
+ at: toScreen(world, camera, width, height),
255
+ arcProgress: station.arcProgress,
256
+ curveT: station.curveT,
257
+ profileRadiusPx: radius,
258
+ };
259
+ });
260
+ return {
261
+ line: stations.map((station) => station.at),
262
+ stations,
263
+ points: spline.points.map((point) => ({
264
+ id: point.id,
265
+ p: project(point.p),
266
+ ...(point.in === undefined ? {} : { in: project(point.in) }),
267
+ ...(point.out === undefined ? {} : { out: project(point.out) }),
268
+ selected: selected.has(point.id),
269
+ })),
270
+ cursor: null,
271
+ closed: spline.closed === true,
272
+ closeHot: false,
273
+ editing: true,
274
+ profileRadiusPx,
275
+ };
276
+ }
277
+
278
+ /** Nearest editable control, with handles ahead of anchors at overlaps. */
279
+ export function splineControlAt(
280
+ spline: ScreenSpline | null,
281
+ point: readonly [number, number],
282
+ ): SplineControlHit | null {
283
+ if (spline === null || !spline.editing) return null;
284
+ let bestHit: SplineControlHit | null = null;
285
+ let bestDistance = Number.POSITIVE_INFINITY;
286
+ const consider = (pointId: string, part: SplinePointPart, at: readonly [number, number], radius: number): void => {
287
+ const distance = Math.hypot(point[0] - at[0], point[1] - at[1]);
288
+ if (distance > radius || distance >= bestDistance) return;
289
+ bestDistance = distance;
290
+ bestHit = { pointId, part };
291
+ };
292
+ for (const control of spline.points) {
293
+ if (control.in !== undefined) consider(control.id, "in", control.in, 7);
294
+ if (control.out !== undefined) consider(control.id, "out", control.out, 7);
295
+ }
296
+ for (const control of spline.points) consider(control.id, "anchor", control.p, 9);
297
+ return bestHit;
298
+ }
299
+
300
+ /** Map a work-plane hit back into a spline layer's authored local coordinates. */
301
+ export function splineLocalPoint(
302
+ worldDesign: readonly [number, number, number],
303
+ spline: SplinePrimitive,
304
+ frame: LayerFrame,
305
+ ): [number, number, number] | null {
306
+ const inverse = new Matrix4().copy(frame.matrixWorld).invert();
307
+ const local = designPointToWorld(worldDesign).applyMatrix4(inverse);
308
+ const scale = splineFrameScale(spline, frame.size);
309
+ if (scale.some((value) => !Number.isFinite(value) || Math.abs(value) < 1e-9)) return null;
310
+ return [local.x / scale[0], -local.y / scale[1], -local.z / scale[2]];
311
+ }
312
+
313
+ export function splineWorldPoint(
314
+ local: readonly [number, number, number],
315
+ spline: SplinePrimitive,
316
+ frame: LayerFrame,
317
+ ): [number, number, number] {
318
+ const world = splineLocalToWorld(local, splineFrameScale(spline, frame.size), frame.matrixWorld);
319
+ return [world.x, -world.y, -world.z];
320
+ }
321
+
322
+ export function workPlaneThrough(
323
+ plane: ResolvedWorkPlane,
324
+ origin: readonly [number, number, number],
325
+ ): ResolvedWorkPlane {
326
+ return { ...plane, origin: [origin[0], origin[1], origin[2]] };
327
+ }
328
+
329
+ /** The nearest sampled centreline edge, carrying both authored and arc domains. */
330
+ export function nearestSplineSpan(
331
+ spline: ScreenSpline | null,
332
+ point: readonly [number, number],
333
+ radius = 8,
334
+ ): SplineSpanHit | null {
335
+ if (spline === null || spline.stations.length < 2) return null;
336
+ let bestIndex = -1;
337
+ let bestT = 0;
338
+ let bestDistance = Number.POSITIVE_INFINITY;
339
+ const slop = Math.max(0, radius);
340
+ for (let i = 0; i < spline.stations.length - 1; i += 1) {
341
+ const a = spline.stations[i]!;
342
+ const b = spline.stations[i + 1]!;
343
+ const nearest = pointSegmentNearest(point, a.at, b.at);
344
+ const reach = slop + Math.max(stationProfileRadius(a), stationProfileRadius(b));
345
+ if (nearest.distance <= reach && nearest.distance < bestDistance) {
346
+ bestDistance = nearest.distance;
347
+ bestIndex = i;
348
+ bestT = nearest.t;
349
+ }
350
+ }
351
+ if (bestIndex < 0) return null;
352
+ const a = spline.stations[bestIndex]!;
353
+ const b = spline.stations[bestIndex + 1]!;
354
+ const curveT = lerp(a.curveT, b.curveT, bestT);
355
+ const segmentCount = Math.max(1, Math.ceil(spline.stations[spline.stations.length - 1]!.curveT));
356
+ const spanIndex = Math.min(segmentCount - 1, Math.max(0, Math.floor(curveT)));
357
+ return {
358
+ spanIndex,
359
+ spanProgress: Math.max(0, Math.min(1, curveT - spanIndex)),
360
+ arcProgress: lerp(a.arcProgress, b.arcProgress, bestT),
361
+ };
362
+ }
363
+
364
+ /** Centreline hit used by StageTools instead of the mesh layer's loose box. */
365
+ export function splineScreenHit(
366
+ spline: ScreenSpline | null,
367
+ point: readonly [number, number],
368
+ radius = 8,
369
+ ): boolean {
370
+ return nearestSplineSpan(spline, point, radius) !== null;
371
+ }
372
+
373
+ /** Marquee intersection, including a band that crosses between sample points. */
374
+ export function splineScreenIntersectsRect(
375
+ spline: ScreenSpline | null,
376
+ rect: { x0: number; y0: number; x1: number; y1: number },
377
+ radius = 0,
378
+ ): boolean {
379
+ if (spline === null || spline.line.length === 0) return false;
380
+ const baseMinX = Math.min(rect.x0, rect.x1);
381
+ const baseMaxX = Math.max(rect.x0, rect.x1);
382
+ const baseMinY = Math.min(rect.y0, rect.y1);
383
+ const baseMaxY = Math.max(rect.y0, rect.y1);
384
+ const slop = Math.max(0, radius);
385
+ const inside = (point: readonly [number, number], reach: number): boolean =>
386
+ point[0] >= baseMinX - reach &&
387
+ point[0] <= baseMaxX + reach &&
388
+ point[1] >= baseMinY - reach &&
389
+ point[1] <= baseMaxY + reach;
390
+ if (
391
+ spline.line.some((point, index) =>
392
+ inside(point, slop + stationProfileRadius(spline.stations[index])),
393
+ )
394
+ ) {
395
+ return true;
396
+ }
397
+ for (let index = 0; index < spline.line.length - 1; index += 1) {
398
+ const a = spline.line[index]!;
399
+ const b = spline.line[index + 1]!;
400
+ const reach =
401
+ slop +
402
+ Math.max(
403
+ stationProfileRadius(spline.stations[index]),
404
+ stationProfileRadius(spline.stations[index + 1]),
405
+ );
406
+ const minX = baseMinX - reach;
407
+ const maxX = baseMaxX + reach;
408
+ const minY = baseMinY - reach;
409
+ const maxY = baseMaxY + reach;
410
+ if (
411
+ segmentsIntersect(a, b, [minX, minY], [maxX, minY]) ||
412
+ segmentsIntersect(a, b, [maxX, minY], [maxX, maxY]) ||
413
+ segmentsIntersect(a, b, [maxX, maxY], [minX, maxY]) ||
414
+ segmentsIntersect(a, b, [minX, maxY], [minX, minY])
415
+ ) {
416
+ return true;
417
+ }
418
+ }
419
+ return false;
420
+ }
421
+
422
+ /** Axis-aligned projected bounds for hover and non-edit selection furniture. */
423
+ export function splineScreenBounds(spline: ScreenSpline | null, padding = 0): ScreenQuad | null {
424
+ if (spline === null || spline.line.length === 0) return null;
425
+ let minX = Number.POSITIVE_INFINITY;
426
+ let minY = Number.POSITIVE_INFINITY;
427
+ let maxX = Number.NEGATIVE_INFINITY;
428
+ let maxY = Number.NEGATIVE_INFINITY;
429
+ for (const [index, point] of spline.line.entries()) {
430
+ const reach = Math.max(0, padding) + stationProfileRadius(spline.stations[index]);
431
+ minX = Math.min(minX, point[0] - reach);
432
+ minY = Math.min(minY, point[1] - reach);
433
+ maxX = Math.max(maxX, point[0] + reach);
434
+ maxY = Math.max(maxY, point[1] + reach);
435
+ }
436
+ return [minX, minY, maxX, minY, maxX, maxY, minX, maxY];
437
+ }
438
+
439
+ function stationProfileRadius(station: ScreenSplineStation | undefined): number {
440
+ const radius = station?.profileRadiusPx;
441
+ return typeof radius === "number" && Number.isFinite(radius) ? Math.max(0, radius) : 0;
442
+ }
443
+
444
+ /** A sphere around the centreline conservatively contains every profile kind. */
445
+ function projectedProfileRadius(
446
+ spline: SplinePrimitive,
447
+ progress: number,
448
+ frameScale: number,
449
+ matrixWorld: Matrix4,
450
+ worldCenter: Vector3,
451
+ camera: Camera,
452
+ width: number,
453
+ height: number,
454
+ ): number {
455
+ const profile = splineProfileAt(spline, progress);
456
+ const halfExtent =
457
+ profile.kind === "tube"
458
+ ? profile.radiusPx
459
+ : profile.kind === "ribbon"
460
+ ? profile.widthPx / 2
461
+ : Math.hypot(profile.widthPx / 2, profile.heightPx / 2);
462
+ const authoredReach = Math.hypot(profile.offset[0], profile.offset[1]) + halfExtent;
463
+ const worldReach = authoredReach * Math.max(0, frameScale) * affineScaleBound(matrixWorld);
464
+ return screenSphereRadius(worldCenter, worldReach, camera, width, height);
465
+ }
466
+
467
+ /** Upper bound for an affine transform, exact for orthogonal scale columns. */
468
+ function affineScaleBound(matrix: Matrix4): number {
469
+ const e = matrix.elements;
470
+ const columns = [
471
+ [e[0]!, e[1]!, e[2]!],
472
+ [e[4]!, e[5]!, e[6]!],
473
+ [e[8]!, e[9]!, e[10]!],
474
+ ] as const;
475
+ // The largest singular value squared is the largest eigenvalue of A^T A.
476
+ // Gershgorin's row-sum bound contains it, remains conservative under shear,
477
+ // and still returns exactly 1 for a rigid transform instead of sqrt(3).
478
+ let boundSquared = 0;
479
+ for (let row = 0; row < 3; row += 1) {
480
+ let rowSum = 0;
481
+ for (let column = 0; column < 3; column += 1) {
482
+ rowSum += Math.abs(dot3(columns[row]!, columns[column]!));
483
+ }
484
+ boundSquared = Math.max(boundSquared, rowSum);
485
+ }
486
+ return Math.sqrt(boundSquared);
487
+ }
488
+
489
+ /** Project a world-space sphere to one conservative screen-space radius. */
490
+ function screenSphereRadius(
491
+ center: Vector3,
492
+ radius: number,
493
+ camera: Camera,
494
+ width: number,
495
+ height: number,
496
+ ): number {
497
+ if (!(radius > 0) || !Number.isFinite(radius)) return 0;
498
+ const projection = camera.projectionMatrix.elements;
499
+ const scaleX = Math.abs(projection[0]!) * width / 2;
500
+ const scaleY = Math.abs(projection[5]!) * height / 2;
501
+ // Orthographic projection has w=1. A sphere stays an ellipse and its largest
502
+ // screen radius is exactly the larger camera-axis scale.
503
+ if (Math.abs(projection[11]!) < 1e-9) return radius * Math.max(scaleX, scaleY);
504
+
505
+ const cameraPoint = center.clone().applyMatrix4(camera.matrixWorldInverse);
506
+ const distance = -cameraPoint.z;
507
+ if (!(distance > radius + 1e-6)) return Math.hypot(width, height);
508
+ // Bound the perspective denominator by the sphere's nearest point and the
509
+ // numerator with one vector norm. This stays conservative off axis without
510
+ // turning an on-axis tube into the diagonal of a projected cube.
511
+ const focal = Math.max(scaleX, scaleY);
512
+ const offAxis = Math.hypot(scaleX * cameraPoint.x, scaleY * cameraPoint.y);
513
+ return radius * (distance * focal + offAxis) / (distance * (distance - radius));
514
+ }
515
+
516
+ function pointSegmentNearest(
517
+ point: readonly [number, number],
518
+ a: readonly [number, number],
519
+ b: readonly [number, number],
520
+ ): { distance: number; t: number } {
521
+ const dx = b[0] - a[0];
522
+ const dy = b[1] - a[1];
523
+ const length2 = dx * dx + dy * dy;
524
+ const t = length2 <= 1e-9 ? 0 : Math.max(0, Math.min(1, ((point[0] - a[0]) * dx + (point[1] - a[1]) * dy) / length2));
525
+ return { distance: Math.hypot(point[0] - (a[0] + t * dx), point[1] - (a[1] + t * dy)), t };
526
+ }
527
+
528
+ function segmentsIntersect(
529
+ a: readonly [number, number],
530
+ b: readonly [number, number],
531
+ c: readonly [number, number],
532
+ d: readonly [number, number],
533
+ ): boolean {
534
+ if (
535
+ Math.max(a[0], b[0]) < Math.min(c[0], d[0]) ||
536
+ Math.max(c[0], d[0]) < Math.min(a[0], b[0]) ||
537
+ Math.max(a[1], b[1]) < Math.min(c[1], d[1]) ||
538
+ Math.max(c[1], d[1]) < Math.min(a[1], b[1])
539
+ ) {
540
+ return false;
541
+ }
542
+ const cross2 = (p: readonly [number, number], q: readonly [number, number], r: readonly [number, number]): number =>
543
+ (q[0] - p[0]) * (r[1] - p[1]) - (q[1] - p[1]) * (r[0] - p[0]);
544
+ const abC = cross2(a, b, c);
545
+ const abD = cross2(a, b, d);
546
+ const cdA = cross2(c, d, a);
547
+ const cdB = cross2(c, d, b);
548
+ return abC * abD <= 0 && cdA * cdB <= 0;
549
+ }
550
+
551
+ function lerp(a: number, b: number, t: number): number {
552
+ return a + (b - a) * t;
553
+ }
554
+
555
+ function splineLocalToWorld(
556
+ point: readonly [number, number, number],
557
+ scale: readonly [number, number, number],
558
+ matrix: Matrix4,
559
+ ): Vector3 {
560
+ return new Vector3(point[0] * scale[0], -point[1] * scale[1], -point[2] * scale[2]).applyMatrix4(matrix);
561
+ }
562
+
563
+ function planeRecord(
564
+ target: WorkPlaneTarget,
565
+ origin: readonly [number, number, number],
566
+ xAxis: readonly [number, number, number],
567
+ yAxis: readonly [number, number, number],
568
+ fallback: boolean,
569
+ ): ResolvedWorkPlane {
570
+ const x = normalize(xAxis, [1, 0, 0]);
571
+ const yCandidate = normalize(yAxis, [0, 1, 0]);
572
+ const normal = normalize(cross(x, yCandidate), [0, 0, 1]);
573
+ const y = normalize(cross(normal, x), yCandidate);
574
+ return { target, origin: [...origin], xAxis: x, yAxis: y, normal, fallback };
575
+ }
576
+
577
+ function normalize(
578
+ value: readonly [number, number, number],
579
+ fallback: readonly [number, number, number],
580
+ ): [number, number, number] {
581
+ const length = Math.hypot(value[0], value[1], value[2]);
582
+ return length > 1e-9 && Number.isFinite(length)
583
+ ? [value[0] / length, value[1] / length, value[2] / length]
584
+ : [fallback[0], fallback[1], fallback[2]];
585
+ }
586
+
587
+ function cross(
588
+ a: readonly [number, number, number],
589
+ b: readonly [number, number, number],
590
+ ): [number, number, number] {
591
+ return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
592
+ }
593
+
594
+ function dot3(
595
+ a: readonly [number, number, number],
596
+ b: readonly [number, number, number],
597
+ ): number {
598
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
599
+ }
600
+
601
+ function designPointToWorld(point: readonly [number, number, number]): Vector3 {
602
+ return new Vector3(point[0], -point[1], -point[2]);
603
+ }
604
+
605
+ function worldPointToDesign(point: readonly [number, number, number]): [number, number, number] {
606
+ return [point[0], -point[1], -point[2]];
607
+ }
608
+
609
+ function designVectorToWorld(point: readonly [number, number, number]): Vector3 {
610
+ return new Vector3(point[0], -point[1], -point[2]).normalize();
611
+ }
612
+
613
+ function worldVectorToDesign(point: readonly [number, number, number]): [number, number, number] {
614
+ return normalize([point[0], -point[1], -point[2]], [1, 0, 0]);
615
+ }
616
+
617
+ function finiteVector(point: Vector3): boolean {
618
+ return Number.isFinite(point.x) && Number.isFinite(point.y) && Number.isFinite(point.z);
619
+ }
620
+
621
+ function cleanZero(value: number): number {
622
+ return Object.is(value, -0) ? 0 : value;
623
+ }
624
+
625
+ function copyPoint(point: SplinePoint3D): SplinePoint3D {
626
+ return {
627
+ id: point.id,
628
+ p: [...point.p],
629
+ ...(point.in === undefined ? {} : { in: [...point.in] }),
630
+ ...(point.out === undefined ? {} : { out: [...point.out] }),
631
+ };
632
+ }