@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
@@ -14,6 +14,7 @@
14
14
  // `world = (x, -y, -z)`. Labelling the arms with world axes instead would put
15
15
  // the letter Y on the arm that moves the layer the opposite way from the field
16
16
  // the author is watching.
17
+ import { borderDistance } from "./frustumGeometry.js";
17
18
  import { presetWorkingView, viewBasis } from "./workingView.js";
18
19
  import type { WorkingView, WorkingViewPreset } from "./workingView.js";
19
20
  import type { ModalState } from "./modalState.js";
@@ -46,20 +47,53 @@ export const DESIGN_AXES: readonly {
46
47
  { axis: "z", world: [0, 0, -1], preset: "back", presetNegative: "front" },
47
48
  ];
48
49
 
49
- /** What a press on one of the drag arms turned out to be. Plain arrays and a
50
- * letter, so the pointer lane can carry it without touching three — and so
50
+ /**
51
+ * Every handle the transform gizmo can be grabbed by, as ONE name.
52
+ *
53
+ * A single string rather than a `{kind, axis}` pair because every consumer
54
+ * wants it whole: the hover field compares it, the draw pass keys the active
55
+ * one off it, and the hit table is an ordered list of them. `screen` carries no
56
+ * axis at all — it is the view-plane disc — which is exactly the shape a pair
57
+ * would have had to fake with a null.
58
+ */
59
+ export type GizmoHandleId =
60
+ | `axis:${DesignAxis}`
61
+ | `plane:${DesignAxis}`
62
+ | `arc:${DesignAxis}`
63
+ | "screen";
64
+
65
+ /** The design axis a handle names, or null for the view-plane disc. A `plane:`
66
+ * or `arc:` handle names its own NORMAL, so `plane:z` is the pad the author
67
+ * drags in x and y and `arc:z` is the ring they turn IN that plane. */
68
+ export function gizmoAxisOf(handle: GizmoHandleId): DesignAxis | null {
69
+ if (handle === "screen") return null;
70
+ return handle.slice(handle.indexOf(":") + 1) as DesignAxis;
71
+ }
72
+
73
+ /** What a press on one of the gizmo's handles turned out to be. Plain arrays
74
+ * and a name, so the pointer lane can carry it without touching three — and so
51
75
  * the origin is a SNAPSHOT: the gizmo re-anchors every frame, and an axis
52
76
  * line that moved with the thing being dragged along it would chase its own
53
77
  * translation.
54
78
  *
55
79
  * `subject` is `modalTarget`'s own answer, carried rather than re-derived:
56
- * the arms are drawn at ONE anchor, and a drag that decided for itself
80
+ * the handles are drawn at ONE anchor, and a drag that decided for itself
57
81
  * whether that anchor was a layer or the camera could pick the other one
58
82
  * mid-gesture if the selection changed under it. */
59
83
  export interface GizmoGrab {
60
84
  subject: ModalState["target"];
61
- axis: DesignAxis;
85
+ handle: GizmoHandleId;
62
86
  origin: Vec3;
87
+ /**
88
+ * ONE world vector, meaning what the handle needs it to mean: an `axis:`
89
+ * arm's own direction, or the NORMAL of the plane a `plane:` pad and the
90
+ * `screen` disc unproject onto.
91
+ *
92
+ * Two fields would be one field always null — an arm solves a LINE
93
+ * (`axisParam`, so the grabbed arm stays under the cursor) and a pad solves a
94
+ * PLANE (`designDelta`), and neither solve has any use for the other's
95
+ * vector.
96
+ */
63
97
  direction: Vec3;
64
98
  }
65
99
 
@@ -105,6 +139,21 @@ export function framingArmDelta(
105
139
  return { dx: 0, dy: 0, dzoom: t0 / t };
106
140
  }
107
141
 
142
+ /**
143
+ * The camera's roll arc, as the framing delta a swept angle means.
144
+ *
145
+ * An arc measures an ANGLE where an arm measures travel, so it is its own
146
+ * function rather than a fourth axis of `framingArmDelta`. The sign is the
147
+ * layer arc's own: the drawn camera turns WITH the pointer around its ring, the
148
+ * way a layer does around its `arc:z` — the modal negates for the camera because
149
+ * it measures the picture in the camera pane, where a clockwise roll of the
150
+ * camera turns the picture the other way; this arc is drawn around the camera
151
+ * object, so no negation.
152
+ */
153
+ export function framingArcDelta(angleDeg: number): FramingDelta {
154
+ return { dx: 0, dy: 0, dzoom: 1, droll: Number.isFinite(angleDeg) ? angleDeg : 0 };
155
+ }
156
+
108
157
  function dot(a: Vec3, b: Vec3): number {
109
158
  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
110
159
  }
@@ -159,6 +208,61 @@ export function viewForBall(view: WorkingView, ball: NavBall): WorkingView {
159
208
  return presetWorkingView(view, ball.preset);
160
209
  }
161
210
 
211
+ /** Below this a middle-press has no direction to read: the author has not
212
+ * committed to a way yet, and picking an axis for them would be a guess. In
213
+ * screen px of accumulated travel. */
214
+ export const MIN_AUTO_CONSTRAIN_PX = 8;
215
+
216
+ /**
217
+ * Below this an axis is too FORESHORTENED to be what a screen drag meant.
218
+ *
219
+ * An axis pointing near the camera projects to almost nothing, so its screen
220
+ * direction is noise — every drag is "close to" it because it barely has a
221
+ * direction at all. `navBalls` keeps the foreshortening in the vector's length
222
+ * for exactly this reason (it is the nav gizmo's own depth cue), so the test is
223
+ * a length test.
224
+ */
225
+ const MIN_AXIS_PROJECTION = 0.2;
226
+
227
+ /**
228
+ * The design axis a screen drag was heading along, or null.
229
+ *
230
+ * Blender's middle-mouse AUTO-CONSTRAIN: the author drags a little way, taps
231
+ * the middle button, and the transform locks to whichever axis they were
232
+ * already going along. This is that choice, and it is `navBalls`' projection
233
+ * re-read — the same three dot products the corner gizmo draws itself from, so
234
+ * the axis the widget shows pointing that way IS the axis this picks.
235
+ *
236
+ * Null when the travel is too short to have a direction, or when the best axis
237
+ * is too foreshortened to have one either. Refusing beats locking to an axis
238
+ * the author did not mean, which costs an undo to get out of.
239
+ */
240
+ export function autoConstrainAxis(
241
+ view: WorkingView,
242
+ dx: number,
243
+ dy: number,
244
+ ): DesignAxis | null {
245
+ const length = Math.hypot(dx, dy);
246
+ if (!(length >= MIN_AUTO_CONSTRAIN_PX)) return null;
247
+ let best: DesignAxis | null = null;
248
+ let bestScore = 0;
249
+ for (const ball of navBalls(view)) {
250
+ // The POSITIVE end alone: an axis and its opposite are one line, and a drag
251
+ // "along x" is along x whichever way it points.
252
+ if (!ball.positive) continue;
253
+ const axisLength = Math.hypot(ball.x, ball.y);
254
+ if (axisLength < MIN_AXIS_PROJECTION) continue;
255
+ // |cos| between the drag and the axis, so either direction along the axis
256
+ // scores the same.
257
+ const score = Math.abs((ball.x * dx + ball.y * dy) / (axisLength * length));
258
+ if (score > bestScore) {
259
+ bestScore = score;
260
+ best = ball.axis;
261
+ }
262
+ }
263
+ return best;
264
+ }
265
+
162
266
  /**
163
267
  * How far along an axis line the closest point to a ray sits.
164
268
  *
@@ -199,12 +303,11 @@ const MIN_AXIS_DENOM = 1e-6;
199
303
  * SHIPPED DEFAULT view all three arms measured 20 to 27 — so every arm failed
200
304
  * and the gizmo answered nothing anywhere while still being painted.
201
305
  *
202
- * ── AND THE APEX CLEARANCE IS A SEPARATE QUESTION ───────────────────────
203
- *
204
- * Only on the CAMERA, where the anchor IS the frustum's apex and the apex's own
205
- * 14 px grab is tested AFTER this one. An arm whose root lands inside that
206
- * radius takes every press aimed at the dolly. A layer has no such neighbour,
207
- * so it does not pay for it.
306
+ * There used to be a second question here, an apex clearance for the camera's
307
+ * arms: the frustum's apex had its own 14 px grab tested AFTER the arms, and
308
+ * an arm rooted inside that radius took every press aimed at the dolly. The
309
+ * apex grab is gone (the Z arm IS the dolly, and the camera body's grab is
310
+ * tested after the arms too), so the length rule is the whole gate again.
208
311
  *
209
312
  * PURE, and here rather than inside `MoveGizmo`'s frame loop, because a gate
210
313
  * that decides whether a whole widget works has to be checkable without a GL
@@ -213,14 +316,403 @@ const MIN_AXIS_DENOM = 1e-6;
213
316
  export function armGrabbable(
214
317
  root: readonly [number, number],
215
318
  tip: readonly [number, number],
216
- anchor: readonly [number, number],
217
- subject: "layers" | "camera",
218
319
  minLengthPx: number,
219
- apexClearPx: number,
220
320
  ): boolean {
221
- if (Math.hypot(tip[0] - root[0], tip[1] - root[1]) < minLengthPx) return false;
222
- if (subject !== "camera") return true;
223
- return Math.hypot(root[0] - anchor[0], root[1] - anchor[1]) >= apexClearPx;
321
+ return Math.hypot(tip[0] - root[0], tip[1] - root[1]) >= minLengthPx;
322
+ }
323
+
324
+ /**
325
+ * The gizmo's shape, in CSS px, HELD CONSTANT as the author dollies.
326
+ *
327
+ * World-space sizes would be a gizmo that fills the pane up close and vanishes
328
+ * far out — and, because every hit test below is in screen px, one whose grab
329
+ * target changed size with the zoom.
330
+ *
331
+ * ── THE PROPORTIONS ARE DREI'S ──────────────────────────────────────────
332
+ *
333
+ * `arrowHead`, `padAt` and `pad` are `@react-three/drei` 10.7.7's PivotControls
334
+ * numbers, restated here as fractions of `arm` and rounded to whole pixels:
335
+ * `AxisArrow.js`'s `coneLength = 0.2`, `PlaneSlider.js`'s `pos1 * 1.7 = 1.7/7`
336
+ * and `length = 0.225`. MIT, same vendoring the player's `view.tsx` did with
337
+ * that release, and for the same reason — the shapes are tuned and the tuning
338
+ * is the part worth having. NONE OF DREI'S CODE IS USED: its shafts and arcs
339
+ * are `Line2`/`LineMaterial` (a GLSL `ShaderMaterial`), which is dead on the
340
+ * WebGPU backend and silently so; its hit-testing is r3f events, which this
341
+ * stage does not run; and its hover is React state under a `frameloop="never"`
342
+ * canvas, which cannot repaint. See docs and the plan for the full account.
343
+ */
344
+ export const GIZMO_PX = {
345
+ /** An arm's tip, measured from the anchor. */
346
+ arm: 90,
347
+ /**
348
+ * Where an arm STARTS.
349
+ *
350
+ * It has to clear the anchor by more than BOTH slops added together. On the
351
+ * camera the anchor is the eye, the centre of the camera body's own grab box
352
+ * (`BODY_GRAB_PX`, frustumGeometry.ts), which the pointer lane tests AFTER
353
+ * the arms: the arms win where they overlap it, and a start inside the box's
354
+ * slop would leave no press near the eye that reached the body at all.
355
+ *
356
+ * 30 > 14 + 14. It also keeps the three roots from piling into one ambiguous
357
+ * blob, which is why the layer gizmo wants it too.
358
+ */
359
+ armStart: 30,
360
+ /** Shaft radius. Thin enough to read as a line, thick enough to be a solid. */
361
+ shaft: 1.5,
362
+ /** Arrowhead length — drei's 0.2 of the arm. */
363
+ arrowHead: 18,
364
+ /** Arrowhead radius. */
365
+ arrowWidth: 5,
366
+ /** A plane pad's centre, on both of its two axes — drei's 1.7/7 of the arm. */
367
+ padAt: 22,
368
+ /** A plane pad's side — drei's 0.225 of the arm. */
369
+ pad: 20,
370
+ /**
371
+ * A rotate arc's radius — drei's 0.65 of the arm.
372
+ *
373
+ * INSIDE the arrowheads on purpose. A quarter arc between two axes crosses
374
+ * both of the arms that bound it, and the hit table ranks arms above arcs, so
375
+ * the two crossings belong to the arms and the 80% of the arc between them is
376
+ * the author's. That is drei's own layout and Blender's; a ring at the arm's
377
+ * full length would cross the cones instead, where the arm is widest.
378
+ */
379
+ arc: 58,
380
+ /**
381
+ * How close a press has to land to an ARC.
382
+ *
383
+ * Tighter than an arm's 14. An arc is a curve crossing three other handles'
384
+ * neighbourhoods, and every px of slop is a px it takes from one of them —
385
+ * whereas an arm is a straight run through empty space.
386
+ */
387
+ arcGrab: 9,
388
+ /** The view-plane disc's radius. Inside `padAt` less half a pad, so the disc
389
+ * and the three pads do not overlap and the hit order never has to arbitrate
390
+ * between them. */
391
+ screen: 11,
392
+ /** How close a press has to land to an ARM, in CSS px. The frustum's own 14 —
393
+ * a line is a line, and the author's finger is the same size in both. */
394
+ grab: 14,
395
+ } as const;
396
+
397
+ /**
398
+ * How long an arm's DRAWN segment must be, on screen, to answer a press.
399
+ *
400
+ * `armStart` and `arm` are world lengths taken at the anchor's depth and then
401
+ * projected, so what the author can actually hit is those lengths times the
402
+ * sine of the axis's angle to the view direction. An axis pointing at the
403
+ * camera collapses: at Numpad7 the design y arm draws about 2.6 px, and under
404
+ * an orthographic rig the design z arm draws a mathematical POINT on the
405
+ * anchor. A press within `grab` of that point would grab it, `axisParam` would
406
+ * divide by a zero denominator and answer null, and the press would open no
407
+ * scrub and do nothing at all — under a line that looked grabbable.
408
+ */
409
+ export const MIN_ARM_SCREEN_PX = GIZMO_PX.grab * 2;
410
+
411
+ /**
412
+ * The two design axes a plane pad SPANS, given the one it is normal to.
413
+ *
414
+ * By filter rather than by cross product, so the pair is stable and stated in
415
+ * the table's own order — which is what lets the drawn pad and its hit quad be
416
+ * built from the same two vectors, in the same order, and therefore be the same
417
+ * square. A cross product would answer a correct plane with an arbitrary
418
+ * in-plane rotation, and the mismatch would be invisible until an author
419
+ * pressed the corner of a pad and got the arm underneath it.
420
+ */
421
+ export function padAxes(axis: DesignAxis): readonly [Vec3, Vec3] {
422
+ const rest = DESIGN_AXES.filter((a) => a.axis !== axis);
423
+ return [rest[0]!.world, rest[1]!.world];
424
+ }
425
+
426
+ /** A point `distance` along a unit direction from an anchor. */
427
+ export function alongAxis(from: Vec3, direction: Vec3, distance: number): Vec3 {
428
+ return [
429
+ from[0] + direction[0] * distance,
430
+ from[1] + direction[1] * distance,
431
+ from[2] + direction[2] * distance,
432
+ ];
433
+ }
434
+
435
+ /**
436
+ * A plane pad's four world corners, wound consistently.
437
+ *
438
+ * `at` is the centre's offset along BOTH of its axes and `half` is half its
439
+ * side, both already in world units. Wound so `pointInQuad`'s sign test sees a
440
+ * convex quad however the projection flips it.
441
+ *
442
+ * ONE function for the drawing and the hit test. They had drifted apart at the
443
+ * first attempt: the mesh took its rotation from `setFromUnitVectors`, which
444
+ * lands the square at whatever in-plane angle the shortest rotation happens to
445
+ * give, while the hit quad was built from `padAxes`. Same plane, different
446
+ * square — a handle whose corners answer no press.
447
+ */
448
+ export function padCorners(centre: Vec3, axis: DesignAxis, at: number, half: number): Vec3[] {
449
+ const [u, v] = padAxes(axis);
450
+ return PAD_SIGNS.map(([su, sv]) => [
451
+ centre[0] + u[0] * (at + su * half) + v[0] * (at + sv * half),
452
+ centre[1] + u[1] * (at + su * half) + v[1] * (at + sv * half),
453
+ centre[2] + u[2] * (at + su * half) + v[2] * (at + sv * half),
454
+ ]);
455
+ }
456
+
457
+ /** A plane pad's world centre. */
458
+ export function padCentre(centre: Vec3, axis: DesignAxis, at: number): Vec3 {
459
+ const [u, v] = padAxes(axis);
460
+ return [
461
+ centre[0] + (u[0] + v[0]) * at,
462
+ centre[1] + (u[1] + v[1]) * at,
463
+ centre[2] + (u[2] + v[2]) * at,
464
+ ];
465
+ }
466
+
467
+ /** The four corners as signed half-widths on the pad's two axes, wound. */
468
+ const PAD_SIGNS: readonly (readonly [number, number])[] = [
469
+ [-1, -1],
470
+ [1, -1],
471
+ [1, 1],
472
+ [-1, 1],
473
+ ];
474
+
475
+ /**
476
+ * The shoelace area of a projected polygon, unsigned.
477
+ *
478
+ * `armGrabbable`'s claim in the units a quad has: a pad seen EDGE-ON projects
479
+ * to a sliver no press can land inside, and its plane is grazing enough that
480
+ * `designDelta` would answer a wild number if one did.
481
+ */
482
+ export function screenArea(corners: readonly (readonly [number, number])[]): number {
483
+ let sum = 0;
484
+ for (let i = 0; i < corners.length; i++) {
485
+ const [ax, ay] = corners[i]!;
486
+ const [bx, by] = corners[(i + 1) % corners.length]!;
487
+ sum += ax * by - bx * ay;
488
+ }
489
+ return Math.abs(sum) / 2;
490
+ }
491
+
492
+ /**
493
+ * The smallest projected pad, in square screen px, that can answer a press.
494
+ *
495
+ * A tenth of the pad drawn face-on (20 x 20 = 400) — well past the sliver and
496
+ * well short of anything a working pad reaches.
497
+ */
498
+ export const MIN_PAD_SCREEN_AREA = 40;
499
+
500
+ type Pt = readonly [number, number];
501
+
502
+ /**
503
+ * One thing on screen a press can land on, already projected.
504
+ *
505
+ * Screen geometry and not world, because that is where the question is asked:
506
+ * "is the cursor on this handle" is a question about pixels, and asking it in
507
+ * world units makes a grab target that shrinks as the author dollies out.
508
+ */
509
+ export interface GizmoTarget {
510
+ handle: GizmoHandleId;
511
+ origin: Vec3;
512
+ direction: Vec3;
513
+ shape:
514
+ /** An arm (two points) or an arc (many). `borderDistance` walks a CLOSED
515
+ * polygon, so a two-point path is that one segment measured twice — the
516
+ * point-to-segment distance — and an open arc is measured with one extra
517
+ * chord from its last point back to its first, which lies well outside
518
+ * the slop and therefore never wins. */
519
+ | { type: "path"; points: readonly Pt[]; slop: number }
520
+ | { type: "quad"; corners: readonly Pt[] }
521
+ | { type: "disc"; at: Pt; radius: number };
522
+ }
523
+
524
+ /**
525
+ * The FIRST target that contains a point, in the order it was handed.
526
+ *
527
+ * ── AN ORDER, NOT A NEAREST-WINS ────────────────────────────────────────
528
+ *
529
+ * With three arms and nothing else, "closest segment within 14px" was a
530
+ * complete answer. It stops being one the moment handles overlap: a plane pad
531
+ * SITS ON the two arms that bound it, and a nearest-wins test would give the
532
+ * pad's interior to whichever arm's centreline happened to be nearer, which is
533
+ * a handle that is drawn and never grabbed.
534
+ *
535
+ * So priority is declared — smallest and innermost first, which is the only
536
+ * order in which every handle stays reachable (the disc is inside the pads, the
537
+ * pads are inside the arms' span) — and each target carries its own exact
538
+ * shape rather than a distance. Ordering is the ARRAY's, so the one place it
539
+ * lives is where the targets are built, and it is checkable here without a GL
540
+ * context.
541
+ *
542
+ * The apex and the frustum's border are deliberately NOT in this list: they
543
+ * belong to `feed.frustumHit`, which `intentAt` ranks below `feed.grabAt` with
544
+ * a layer veto on the body. Folding them in here would rank them above both.
545
+ */
546
+ export function gizmoHitAt(
547
+ targets: readonly GizmoTarget[],
548
+ x: number,
549
+ y: number,
550
+ ): GizmoTarget | null {
551
+ for (const target of targets) {
552
+ const shape = target.shape;
553
+ if (shape.type === "path") {
554
+ if (borderDistance(x, y, shape.points) <= shape.slop) return target;
555
+ continue;
556
+ }
557
+ if (shape.type === "quad") {
558
+ if (pointInQuad(x, y, shape.corners)) return target;
559
+ continue;
560
+ }
561
+ if (Math.hypot(shape.at[0] - x, shape.at[1] - y) <= shape.radius) return target;
562
+ }
563
+ return null;
564
+ }
565
+
566
+ /**
567
+ * Is a point inside a projected convex polygon?
568
+ *
569
+ * The cross product of each edge with the point-to-vertex vector keeps one sign
570
+ * all the way round a convex polygon, and flips somewhere on a point outside
571
+ * it. Winding-agnostic (either sign is accepted, as long as it is consistent),
572
+ * because a plane pad seen from behind projects the other way round and it is
573
+ * still the same pad.
574
+ *
575
+ * A zero cross means the point is ON that edge, which counts as inside — the
576
+ * alternative is a one-pixel dead line down the middle of a handle.
577
+ */
578
+ export function pointInQuad(x: number, y: number, corners: readonly Pt[]): boolean {
579
+ if (corners.length < 3) return false;
580
+ let sign = 0;
581
+ for (let i = 0; i < corners.length; i++) {
582
+ const [ax, ay] = corners[i]!;
583
+ const [bx, by] = corners[(i + 1) % corners.length]!;
584
+ const cross = (bx - ax) * (y - ay) - (by - ay) * (x - ax);
585
+ if (cross === 0) continue;
586
+ const next = cross > 0 ? 1 : -1;
587
+ if (sign === 0) sign = next;
588
+ else if (sign !== next) return false;
589
+ }
590
+ return true;
591
+ }
592
+
593
+ /**
594
+ * A rotate arc's world points: a QUARTER turn in the plane normal to `axis`,
595
+ * running from that plane's first design axis round to its second.
596
+ *
597
+ * A quarter and not a full ring, which is drei's PivotControls layout and
598
+ * Blender's: three full rings at one radius make a sphere of lines over exactly
599
+ * the geometry the author is judging, and each one crosses the other two twice.
600
+ * A quarter arc per axis says the same thing — "turn about this" — inside one
601
+ * octant, and leaves the rest of the view clear.
602
+ *
603
+ * `segments` points, so an arc of 24 is 24 chords; the projected polyline IS
604
+ * the hit shape (`gizmoHitAt`), so the drawing and the grab are the same curve
605
+ * by construction rather than by two matching formulas.
606
+ */
607
+ /** Chords per quarter arc. Enough that the polyline reads as a curve at the
608
+ * radius it is drawn at, and that the hit test's own chords never sit further
609
+ * than a fraction of `arcGrab` inside the true circle. */
610
+ export const ARC_SEGMENTS = 24;
611
+
612
+ export function arcPoints(
613
+ centre: Vec3,
614
+ axis: DesignAxis,
615
+ radius: number,
616
+ segments: number,
617
+ ): Vec3[] {
618
+ const [u, v] = padAxes(axis);
619
+ const out: Vec3[] = [];
620
+ for (let i = 0; i <= segments; i++) {
621
+ const t = (i / segments) * (Math.PI / 2);
622
+ const cos = Math.cos(t) * radius;
623
+ const sin = Math.sin(t) * radius;
624
+ out.push([
625
+ centre[0] + u[0] * cos + v[0] * sin,
626
+ centre[1] + u[1] * cos + v[1] * sin,
627
+ centre[2] + u[2] * cos + v[2] * sin,
628
+ ]);
629
+ }
630
+ return out;
631
+ }
632
+
633
+ /**
634
+ * Where a point sits round a rotate arc, in radians, measured IN THE ARC'S OWN
635
+ * PLANE.
636
+ *
637
+ * ── WHY NOT A SCREEN-SPACE `atan2` ──────────────────────────────────────
638
+ *
639
+ * The camera-pane roll ring can use one, because that ring is drawn in screen
640
+ * space and the rotation it drives is about the view axis. A working-pane arc
641
+ * is a circle in the WORLD seen at an angle, so it projects to an ellipse: a
642
+ * screen angle runs fast across the ellipse's narrow axis and slow across its
643
+ * wide one, and the grabbed point slides out from under the cursor exactly
644
+ * where the author can see it doing so.
645
+ *
646
+ * Two dot products against the plane's own basis instead — the same solve
647
+ * `@react-three/drei` 10.7.7's `AxisRotator.calculateAngle` performs (MIT), and
648
+ * for the same reason.
649
+ */
650
+ export function ringAngle(centre: Vec3, axis: DesignAxis, point: Vec3): number {
651
+ const [u, v] = padAxes(axis);
652
+ const dx = point[0] - centre[0];
653
+ const dy = point[1] - centre[1];
654
+ const dz = point[2] - centre[2];
655
+ return Math.atan2(
656
+ dx * v[0] + dy * v[1] + dz * v[2],
657
+ dx * u[0] + dy * u[1] + dz * u[2],
658
+ );
659
+ }
660
+
661
+ /**
662
+ * The representative of `delta` (an angle that has been wrapped into a single
663
+ * turn) nearest `total`.
664
+ *
665
+ * ── WHAT THIS IS FOR, AND WHY IT IS NOT A DRIFT ─────────────────────────
666
+ *
667
+ * `ringAngle`'s difference lives in (-PI, PI], so a sweep past half a turn
668
+ * comes back as a rotation the other way and a sweep past a full turn comes
669
+ * back as zero — the author drags round and the layer springs back. Blender
670
+ * counts turns; so does this.
671
+ *
672
+ * The count is path-dependent, which is the ONLY path-dependent quantity a
673
+ * gesture in this editor carries, and it has to be: "how many times round" is
674
+ * not recoverable from the pointer's current position. What is emitted is still
675
+ * a TOTAL from the press, so the store's re-derive-from-base, one-undo-entry
676
+ * and exact-Escape properties all hold unchanged — see `advanceVirtual`, whose
677
+ * accumulation is path-dependent for the same reason and with the same result.
678
+ */
679
+ export function unwrapTurns(total: number, delta: number): number {
680
+ const turn = Math.PI * 2;
681
+ if (!Number.isFinite(total) || !Number.isFinite(delta)) return delta;
682
+ return delta + Math.round((total - delta) / turn) * turn;
683
+ }
684
+
685
+ /**
686
+ * The constraint axis, as the long screen segment Blender draws through the
687
+ * pivot while a transform is locked to it.
688
+ *
689
+ * ── WHY IT IS DONE IN SCREEN SPACE ──────────────────────────────────────
690
+ *
691
+ * A perspective projection maps a 3D line to a 2D line, so the image of the
692
+ * whole axis IS the screen line through the projected anchor with the projected
693
+ * direction. Extending two world points to ±infinity and projecting those
694
+ * instead means clipping them against the camera's near plane first, since a
695
+ * point behind the eye projects to a mirrored ghost — real work, for the same
696
+ * segment.
697
+ *
698
+ * `towards` is any second point on the axis, ahead of it on screen; only the
699
+ * direction is used. Null when the two project to the same pixel, which is an
700
+ * axis pointing at the camera — the arm is already refusing presses in that
701
+ * pose (`armGrabbable`), and a guide line with no direction would be a random
702
+ * one.
703
+ */
704
+ export function axisGuide(
705
+ at: Pt,
706
+ towards: Pt,
707
+ reach: number,
708
+ ): { a: [number, number]; b: [number, number] } | null {
709
+ const dx = towards[0] - at[0];
710
+ const dy = towards[1] - at[1];
711
+ const length = Math.hypot(dx, dy);
712
+ if (!(length > 1e-6) || !Number.isFinite(reach)) return null;
713
+ const ux = (dx / length) * reach;
714
+ const uy = (dy / length) * reach;
715
+ return { a: [at[0] - ux, at[1] - uy], b: [at[0] + ux, at[1] + uy] };
224
716
  }
225
717
 
226
718
  export function axisParam(