@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
@@ -1,118 +0,0 @@
1
- // The rail's icon set: hand-drawn 24-viewBox strokes at 1.5, tinted by the CSS
2
- // `color` of whatever wraps them. Still no icon dependency — but a family of
3
- // consistent strokes, where the old unicode glyphs never read as one voice.
4
- //
5
- // Sized by CSS (`.layer-kind svg`, `.layer-flag svg`), never by attributes, so
6
- // one rule owns the scale everywhere the set appears.
7
- import type { ReactElement, ReactNode } from "react";
8
-
9
- function icon(children: ReactNode): ReactElement {
10
- return (
11
- <svg
12
- viewBox="0 0 24 24"
13
- fill="none"
14
- stroke="currentColor"
15
- strokeWidth={1.5}
16
- strokeLinecap="round"
17
- strokeLinejoin="round"
18
- aria-hidden="true"
19
- >
20
- {children}
21
- </svg>
22
- );
23
- }
24
-
25
- /** One icon per layer kind — the same grammar Figma settled: a lone diamond is
26
- * an instance, four diamonds are a definition. */
27
- const KIND_ICONS: Record<string, ReactElement> = {
28
- text: icon(<path d="M6 6h12M12 6v13" />),
29
- image: icon(
30
- <>
31
- <rect x="3.5" y="5" width="17" height="14" rx="2" />
32
- <circle cx="9" cy="9.5" r="1.5" />
33
- <path d="m4.5 17 4-4 4 4 3-3 4 4" />
34
- </>,
35
- ),
36
- video: icon(
37
- <>
38
- <rect x="3.5" y="5" width="17" height="14" rx="2" />
39
- <path d="M10.5 9.5v5l4-2.5z" />
40
- </>,
41
- ),
42
- rect: icon(<rect x="4.5" y="6.5" width="15" height="11" rx="1.5" />),
43
- ellipse: icon(<circle cx="12" cy="12" r="7.5" />),
44
- line: icon(<path d="M5.5 18.5 18.5 5.5" />),
45
- arrow: icon(<path d="M6 18 18 6M9 6h9v9" />),
46
- path: icon(<path d="M4.5 19.5c7 0 8-15 15-15" />),
47
- // Angle brackets: the one glyph nobody has to be told means markup.
48
- html: icon(<path d="m9 8-4 4 4 4M15 8l4 4-4 4" />),
49
- group: icon(
50
- <>
51
- <rect x="4" y="4" width="12" height="12" rx="2" />
52
- <path d="M20 9v9a2 2 0 0 1-2 2H9" />
53
- </>,
54
- ),
55
- device: icon(
56
- <>
57
- <rect x="7" y="3.5" width="10" height="17" rx="2" />
58
- <path d="M10.5 17.5h3" />
59
- </>,
60
- ),
61
- mesh: icon(<path d="M12 3.5 19.5 8v8L12 20.5 4.5 16V8z" />),
62
- cloner: icon(
63
- <>
64
- <rect x="4.5" y="4.5" width="6" height="6" rx="1" />
65
- <rect x="13.5" y="4.5" width="6" height="6" rx="1" />
66
- <rect x="4.5" y="13.5" width="6" height="6" rx="1" />
67
- <rect x="13.5" y="13.5" width="6" height="6" rx="1" />
68
- </>,
69
- ),
70
- instance: icon(<path d="m12 4 8 8-8 8-8-8z" />),
71
- };
72
-
73
- const FALLBACK = icon(<path d="m12 6 6 6-6 6-6-6z" />);
74
-
75
- export function kindIcon(kind: string): ReactElement {
76
- return KIND_ICONS[kind] ?? FALLBACK;
77
- }
78
-
79
- /** A shot row: a clapperboard. */
80
- export const SCENE_ICON = icon(
81
- <>
82
- <rect x="3.5" y="5" width="17" height="14" rx="2" />
83
- <path d="M3.5 9.5h17M9 5.5l1.5 4M14 5.5l1.5 4" />
84
- </>,
85
- );
86
-
87
- /** A component definition: the four-diamond glyph. */
88
- export const COMPONENT_ICON = icon(
89
- <path d="M12 3.5 15 6.5 12 9.5 9 6.5zM17.5 9l3 3-3 3-3-3zM12 14.5l3 3-3 3-3-3zM6.5 9l3 3-3 3-3-3z" />,
90
- );
91
-
92
- export const EYE_ICON = icon(
93
- <>
94
- <path d="M3.5 12S6.5 6 12 6s8.5 6 8.5 6-3 6-8.5 6S3.5 12 3.5 12z" />
95
- <circle cx="12" cy="12" r="2.5" />
96
- </>,
97
- );
98
-
99
- export const EYE_OFF_ICON = icon(
100
- <>
101
- <path d="M3.5 12S6.5 6 12 6s8.5 6 8.5 6-3 6-8.5 6S3.5 12 3.5 12z" />
102
- <path d="m5 4.5 14 15" />
103
- </>,
104
- );
105
-
106
- export const LOCK_ICON = icon(
107
- <>
108
- <rect x="5.5" y="10.5" width="13" height="9" rx="2" />
109
- <path d="M8.5 10.5V8a3.5 3.5 0 0 1 7 0v2.5" />
110
- </>,
111
- );
112
-
113
- export const UNLOCK_ICON = icon(
114
- <>
115
- <rect x="5.5" y="10.5" width="13" height="9" rx="2" />
116
- <path d="M8.5 10.5V8a3.5 3.5 0 0 1 6.8-1.2" />
117
- </>,
118
- );
@@ -1,85 +0,0 @@
1
- // Which scene is open, and how to open another one.
2
- //
3
- // A `<select>` and not a custom popover, deliberately: it is the one control in
4
- // this chrome that has to work before anything else does (there is no document
5
- // yet), and the native element brings keyboard navigation, type-ahead and a
6
- // scrolling list that never leaves the viewport for free.
7
- import { useState } from "react";
8
- import { useShotEditor, useStore } from "../../state/context.js";
9
-
10
- export function DocSwitcher() {
11
- const store = useStore();
12
- const shots = useShotEditor((s) => s.shots);
13
- const name = useShotEditor((s) => s.name);
14
- const [creating, setCreating] = useState(false);
15
- const [draft, setDraft] = useState("");
16
-
17
- const create = (): void => {
18
- const trimmed = draft.trim();
19
- setCreating(false);
20
- setDraft("");
21
- if (trimmed.length > 0) void store.createShot(trimmed);
22
- };
23
-
24
- if (creating) {
25
- return (
26
- <form
27
- className="doc-switcher"
28
- onSubmit={(e) => {
29
- e.preventDefault();
30
- create();
31
- }}
32
- >
33
- <input
34
- className="doc-switcher-input"
35
- autoFocus
36
- value={draft}
37
- placeholder="scene name"
38
- aria-label="New scene name"
39
- onChange={(e) => setDraft(e.target.value)}
40
- onKeyDown={(e) => {
41
- if (e.key === "Escape") {
42
- e.stopPropagation();
43
- setCreating(false);
44
- setDraft("");
45
- }
46
- }}
47
- />
48
- </form>
49
- );
50
- }
51
-
52
- return (
53
- <div className="doc-switcher">
54
- <select
55
- className="doc-switcher-select"
56
- aria-label="Open scene"
57
- value={name ?? ""}
58
- onChange={(e) => {
59
- const next = e.target.value;
60
- // Closing DROPS the in-memory document, so the debounced autosave has
61
- // to land first — the same order `openShot` uses. `flush` rejects
62
- // when it could not write, which leaves the scene open with the
63
- // failure already on screen instead of discarding it.
64
- if (next === "") void store.flush().then(() => store.closeShot(), () => {});
65
- else void store.openShot(next);
66
- }}
67
- >
68
- <option value="">{shots.length === 0 ? "no scenes yet" : "choose a scene…"}</option>
69
- {shots.map((s) => (
70
- <option key={s.name} value={s.name}>
71
- {s.name}
72
- </option>
73
- ))}
74
- </select>
75
- <button
76
- type="button"
77
- className="doc-switcher-new"
78
- title="New scene"
79
- onClick={() => setCreating(true)}
80
- >
81
- +
82
- </button>
83
- </div>
84
- );
85
- }
@@ -1,86 +0,0 @@
1
- // Play, pause, and the playhead.
2
- //
3
- // ── THE ONE THING WORTH READING ──────────────────────────────────────────
4
- //
5
- // Playback is a rAF loop that converts WALL TIME into an INTEGER FRAME and
6
- // seeks to it. It never accumulates frames per tick.
7
- //
8
- // The difference matters: `frame++` per rAF makes playback speed a function of
9
- // the monitor's refresh rate (a 120Hz laptop plays a 30fps shot at double
10
- // speed) and makes a dropped frame permanently shift everything after it.
11
- // Deriving the frame from elapsed time instead means the preview plays at the
12
- // document's own rate on any display, drops frames under load rather than
13
- // slowing down, and — because the seek target is `round(elapsed * fps)` — puts
14
- // the same integer frame on screen that the export will render.
15
- import { useEffect, useRef } from "react";
16
- import { docDurationFrames } from "@dailies-studio/shot/doc";
17
- import { useShotEditor, useStore } from "../../state/context.js";
18
-
19
- /** The slider and the readout — the two things that show the frame — in their
20
- * own component, so the per-frame tick during playback re-renders them alone
21
- * and never the play button or the rAF loop's owner above. */
22
- function TransportScrub({ total, playing }: { total: number; playing: boolean }) {
23
- const store = useStore();
24
- const frame = useShotEditor((s) => s.frame);
25
- return (
26
- <>
27
- <input
28
- className="transport-scrub"
29
- type="range"
30
- min={0}
31
- max={Math.max(0, total - 1)}
32
- step={1}
33
- value={frame}
34
- aria-label="Playhead"
35
- onChange={(e) => {
36
- // Scrubbing pauses. A transport that kept playing under the pointer
37
- // would fight the drag on every rAF.
38
- if (playing) store.setPlaying(false);
39
- store.seek(Number(e.target.value));
40
- }}
41
- />
42
- <span className="transport-readout" data-testid="frame-readout">
43
- {frame} / {Math.max(0, total - 1)}
44
- </span>
45
- </>
46
- );
47
- }
48
-
49
- export function TransportBar() {
50
- const store = useStore();
51
- const playing = useShotEditor((s) => s.playing);
52
- const doc = useShotEditor((s) => s.doc);
53
- const total = doc === null ? 0 : docDurationFrames(doc);
54
- const fps = doc === null ? 30 : doc.fps.num / doc.fps.den;
55
-
56
- const startedAt = useRef(0);
57
- const startedFrame = useRef(0);
58
- useEffect(() => {
59
- if (!playing || doc === null || total <= 0) return;
60
- startedAt.current = performance.now();
61
- startedFrame.current = store.getState().frame;
62
- let raf = 0;
63
- const tick = (): void => {
64
- const elapsed = (performance.now() - startedAt.current) / 1000;
65
- const target = startedFrame.current + Math.round(elapsed * fps);
66
- store.seek(((target % total) + total) % total);
67
- raf = requestAnimationFrame(tick);
68
- };
69
- raf = requestAnimationFrame(tick);
70
- return () => cancelAnimationFrame(raf);
71
- }, [playing, doc, total, fps, store]);
72
-
73
- return (
74
- <div className="transport">
75
- <button
76
- type="button"
77
- className="transport-play"
78
- title={playing ? "Pause (Space)" : "Play (Space)"}
79
- onClick={() => store.setPlaying(!playing)}
80
- >
81
- {playing ? "❚❚" : "▶"}
82
- </button>
83
- <TransportScrub total={total} playing={playing} />
84
- </div>
85
- );
86
- }
@@ -1,341 +0,0 @@
1
- // The focused block's params, timing and easing.
2
- //
3
- // ── WHY THIS IS NOT `KeyframeEditor` ───────────────────────────────────
4
- //
5
- // `@dailies-studio/timeline`'s ease editor is a fine component and its INPUTS
6
- // are keyframe-shaped: `refs: KeyframeAddr[]` and a `TimelineGesture` whose
7
- // capture emits `{kind:"setEase", refs}`. There are no keyframes in this lane,
8
- // so every one of those would be a fabricated address. What DOES transfer is
9
- // everything pure: `EASE_PRESETS`, `matchPreset`, `SPRING_DEFAULT`, and the pad
10
- // geometry `padToPx`/`pxToPad`. Those are imported, not re-derived, so the two
11
- // chromes cannot disagree about what "Ease Out" means.
12
- //
13
- // ── THE SPRING WART, AND THE TAIL THAT ADMITS IT ───────────────────────
14
- //
15
- // A spring has an INTRINSIC duration in seconds. So dragging a spring block's
16
- // right edge changes where its window CLIPS, not how fast it settles — which is
17
- // correct physics and matches springs everywhere else in the engine, but is
18
- // invisible unless the UI says so. It says so twice: the duration field is
19
- // disabled with the reason attached, and the preview draws the part of the
20
- // settle that falls OUTSIDE the block window as a dashed tail. If the tail is
21
- // long, the block is cutting the spring off mid-flight, and the author can see
22
- // exactly that.
23
- import { useRef } from "react";
24
- import {
25
- blockProgressAt,
26
- motionPresets,
27
- presetsForSlot,
28
- MIN_ANIMATE_FRAMES,
29
- } from "@dailies-studio/shot/doc";
30
- import type { AnimBlock } from "@dailies-studio/shot/doc";
31
- import { EASE_PRESETS, SPRING_DEFAULT, easeSelection, padToPx, pxToPad } from "@dailies-studio/timeline";
32
- import type { PadGeom } from "@dailies-studio/timeline";
33
- import { InspectorRow, InspectorSection, NumberField, polylinePoints } from "@dailies-studio/ui";
34
- import type { Ease, SpringEase, Value } from "@dailies-studio/engine";
35
- import type { PointerEvent as ReactPointerEvent } from "react";
36
- import { useShotEditor, useStore } from "../../../state/context.js";
37
- import { ParamRow } from "../ParamRow.js";
38
- import { useFocusedBlock } from "./useBlocks.js";
39
-
40
- const PAD_GEOM: PadGeom = { width: 216, height: 132, padY: 30 };
41
- const PREVIEW_W = 216;
42
- const PREVIEW_H = 72;
43
- const PREVIEW_PAD = 8;
44
- const PREVIEW_SAMPLES = 96;
45
- /** How far past the block's own window the settle tail is drawn, as a multiple
46
- * of the window. Two windows is enough to show a spring that has not settled
47
- * and short enough that a settled one draws a flat stub. */
48
- const TAIL_FACTOR = 2;
49
-
50
- export function BlockTiming() {
51
- const store = useStore();
52
- // A STATE SLICE OR NULL, never a fresh fallback object: a selector that
53
- // mints `{num: 30, den: 1}` per call gives useSyncExternalStore a snapshot
54
- // that is never Object.is-stable, which is an infinite re-render the moment
55
- // this mounts with no document. Unreachable today only because the
56
- // inspector early-returns on a null doc — this keeps it unreachable.
57
- const fps = useShotEditor((s) => s.doc?.fps ?? null) ?? { num: 30, den: 1 };
58
- const ref = useFocusedBlock();
59
- if (ref === null) return null;
60
- const { layer, block } = ref;
61
- const preset = motionPresets[block.preset];
62
- const duration = Math.max(1, Math.trunc(block.durationFrames));
63
- const spring = block.ease?.type === "spring";
64
-
65
- return (
66
- <InspectorSection title="Timing" persistKey="shot:animate:timing">
67
- <InspectorRow>
68
- <NumberField
69
- value={block.frame}
70
- label="Start"
71
- ariaLabel="Block start"
72
- min={0}
73
- step={1}
74
- onCommit={(next) => store.setBlockTiming(layer.id, block.id, Math.max(0, next), duration)}
75
- />
76
- <NumberField
77
- value={duration}
78
- label="Frames"
79
- ariaLabel="Block frames"
80
- min={MIN_ANIMATE_FRAMES}
81
- step={1}
82
- // A spring's duration is set by its physics. The field still SHOWS the
83
- // window (it is real: it is where the block stops contributing), it
84
- // just refuses to pretend that changing it retimes the motion.
85
- disabled={spring}
86
- {...(spring ? { title: "A spring settles in its own time — edit stiffness and damping." } : {})}
87
- onCommit={(next) =>
88
- store.setBlockTiming(layer.id, block.id, block.frame, Math.max(MIN_ANIMATE_FRAMES, next))
89
- }
90
- />
91
- </InspectorRow>
92
-
93
- {preset !== undefined &&
94
- preset.params.map((spec) => (
95
- <ParamRow
96
- key={spec.name}
97
- spec={spec}
98
- value={paramValue(block, spec.name, spec.default)}
99
- onCommit={(next) => store.setBlockParams(layer.id, block.id, { [spec.name]: next })}
100
- />
101
- ))}
102
-
103
- <EaseControl
104
- {...(block.ease === undefined ? {} : { ease: block.ease })}
105
- onEase={(ease) => store.setBlockEase(layer.id, block.id, ease)}
106
- onScrub={(ease) => store.setBlockEase(layer.id, block.id, ease)}
107
- />
108
- <SettlePreview block={block} fps={fps} />
109
-
110
- {/* The preset itself is swappable in place, so "make that a slide instead"
111
- keeps the block's timing and easing rather than deleting and redoing. */}
112
- <InspectorRow label="Preset">
113
- <select
114
- className="select"
115
- aria-label="Block preset"
116
- value={block.preset}
117
- onChange={(e) => store.setBlockPreset(layer.id, block.id, e.target.value)}
118
- >
119
- {presetsForSlot(block.slot).map((p) => (
120
- <option key={p.id} value={p.id}>
121
- {p.label}
122
- </option>
123
- ))}
124
- </select>
125
- </InspectorRow>
126
- </InspectorSection>
127
- );
128
- }
129
-
130
- function paramValue(block: AnimBlock, name: string, fallback: Value): Value {
131
- const v = block.params[name];
132
- return v === undefined ? fallback : v;
133
- }
134
-
135
- // ---- easing ------------------------------------------------------------------
136
-
137
- /**
138
- * The ease row, plus whichever editor the chosen ease needs.
139
- *
140
- * Takes an `Ease` and not a block, because the CAMERA's shots carry one too and
141
- * `clipProgress01` reads it from both the same way. Two ease editors would be
142
- * two places for "Ease Out" to mean something slightly different, which is the
143
- * whole reason `EASE_PRESETS` is imported rather than re-declared.
144
- */
145
- export function EaseControl(props: {
146
- ease?: Ease;
147
- /** What the select is called. The subject differs between the two callers and
148
- * the accessible name has to say which one the author is looking at. */
149
- ariaLabel?: string;
150
- onEase(ease: Ease): void;
151
- onScrub(ease: Ease): void;
152
- }) {
153
- const ease = props.ease;
154
- // `easeSelection` takes a LIST because the shared editor is multi-select. One
155
- // block is a list of one, and reusing it keeps "Custom" meaning exactly what
156
- // it means in the other two chromes.
157
- const sel = easeSelection(ease === undefined ? [] : [ease]);
158
-
159
- return (
160
- <>
161
- <InspectorRow label="Ease">
162
- <select
163
- className="select"
164
- aria-label={props.ariaLabel ?? "Block ease"}
165
- value={sel.selectValue}
166
- onChange={(e) => {
167
- const preset = EASE_PRESETS.find((p) => p.id === e.target.value);
168
- if (preset !== undefined) props.onEase(preset.ease);
169
- }}
170
- >
171
- {EASE_PRESETS.map((p) => (
172
- <option key={p.id} value={p.id}>
173
- {p.label}
174
- </option>
175
- ))}
176
- {sel.selectValue === "custom" && (
177
- <option value="custom" disabled>
178
- Custom
179
- </option>
180
- )}
181
- {sel.selectValue === "mixed" && (
182
- <option value="mixed" disabled>
183
- —
184
- </option>
185
- )}
186
- </select>
187
- </InspectorRow>
188
- {sel.ease?.type === "bezier" && <BezierPad handles={sel.ease.handles} onEase={props.onScrub} />}
189
- {sel.ease?.type === "spring" && <SpringFields spring={sel.ease} onEase={props.onEase} />}
190
- </>
191
- );
192
- }
193
-
194
- function BezierPad(props: {
195
- handles: readonly [number, number, number, number];
196
- onEase(ease: Ease): void;
197
- }) {
198
- const svgRef = useRef<SVGSVGElement | null>(null);
199
- const drag = useRef<{ index: 0 | 1; base: [number, number, number, number] } | null>(null);
200
-
201
- const down =
202
- (index: 0 | 1) =>
203
- (e: ReactPointerEvent<SVGCircleElement>): void => {
204
- if (e.button !== 0) return;
205
- e.currentTarget.setPointerCapture(e.pointerId);
206
- const h = props.handles;
207
- drag.current = { index, base: [h[0], h[1], h[2], h[3]] };
208
- };
209
-
210
- const move = (e: ReactPointerEvent<SVGCircleElement>): void => {
211
- const d = drag.current;
212
- const svg = svgRef.current;
213
- if (d === null || svg === null) return;
214
- const rect = svg.getBoundingClientRect();
215
- const [x, y] = pxToPad(PAD_GEOM, e.clientX - rect.left, e.clientY - rect.top);
216
- // Rebuilt from the DRAG-START handles, so the untouched one cannot creep as
217
- // the live value it is being compared against changes underneath.
218
- const handles: [number, number, number, number] =
219
- d.index === 0 ? [x, y, d.base[2], d.base[3]] : [d.base[0], d.base[1], x, y];
220
- props.onEase({ type: "bezier", handles });
221
- };
222
-
223
- const up = (e: ReactPointerEvent<SVGCircleElement>): void => {
224
- if (drag.current === null) return;
225
- drag.current = null;
226
- e.currentTarget.releasePointerCapture(e.pointerId);
227
- };
228
-
229
- const h = props.handles;
230
- const [ax, ay] = padToPx(PAD_GEOM, 0, 0);
231
- const [bx, by] = padToPx(PAD_GEOM, 1, 1);
232
- const [x1, y1] = padToPx(PAD_GEOM, h[0], h[1]);
233
- const [x2, y2] = padToPx(PAD_GEOM, h[2], h[3]);
234
- const handle = (index: 0 | 1) => ({
235
- className: "insp-pad-handle",
236
- r: 4.5,
237
- onPointerDown: down(index),
238
- onPointerMove: move,
239
- onPointerUp: up,
240
- onPointerCancel: () => {
241
- drag.current = null;
242
- },
243
- });
244
-
245
- return (
246
- <svg ref={svgRef} className="insp-pad" width={PAD_GEOM.width} height={PAD_GEOM.height}>
247
- <rect
248
- className="insp-pad-box"
249
- x={0.5}
250
- y={PAD_GEOM.padY + 0.5}
251
- width={PAD_GEOM.width - 1}
252
- height={PAD_GEOM.height - 2 * PAD_GEOM.padY - 1}
253
- rx={3}
254
- />
255
- <path className="insp-pad-curve" d={`M ${ax} ${ay} C ${x1} ${y1} ${x2} ${y2} ${bx} ${by}`} />
256
- <line className="insp-pad-arm" x1={ax} y1={ay} x2={x1} y2={y1} />
257
- <line className="insp-pad-arm" x1={bx} y1={by} x2={x2} y2={y2} />
258
- <circle {...handle(0)} cx={x1} cy={y1} />
259
- <circle {...handle(1)} cx={x2} cy={y2} />
260
- </svg>
261
- );
262
- }
263
-
264
- const SPRING_FIELDS = [
265
- { key: "stiffness", label: "Stiffness", min: 1, max: 1000, step: 1 },
266
- { key: "damping", label: "Damping", min: 0, max: 200, step: 0.5 },
267
- { key: "mass", label: "Mass", min: 0.05, max: 20, step: 0.05 },
268
- ] as const;
269
-
270
- function SpringFields(props: { spring: SpringEase; onEase(ease: Ease): void }) {
271
- return (
272
- <InspectorRow>
273
- {SPRING_FIELDS.map((f) => (
274
- <NumberField
275
- key={f.key}
276
- value={props.spring[f.key]}
277
- label={f.label}
278
- ariaLabel={`Spring ${f.label.toLowerCase()}`}
279
- min={f.min}
280
- max={f.max}
281
- step={f.step}
282
- onCommit={(v) => props.onEase({ ...SPRING_DEFAULT, ...props.spring, [f.key]: v })}
283
- />
284
- ))}
285
- </InspectorRow>
286
- );
287
- }
288
-
289
- // ---- the preview, and the tail --------------------------------------------------
290
-
291
- function SettlePreview(props: { block: AnimBlock; fps: { num: number; den: number } }) {
292
- const { block, fps } = props;
293
- const duration = Math.max(1, Math.trunc(block.durationFrames));
294
- const spring = block.ease?.type === "spring";
295
- // Sampled through `blockProgressAt` — the SAME function the renderer folds
296
- // with. A preview drawn from a local re-implementation of the ease would
297
- // eventually disagree with the pixels, which is the one thing a preview must
298
- // never do.
299
- const to = block.frame + (spring ? duration * (1 + TAIL_FACTOR) : duration);
300
- const samples: Array<{ frame: number; value: number }> = [];
301
- for (let i = 0; i < PREVIEW_SAMPLES; i += 1) {
302
- const frame = block.frame + ((to - block.frame) * i) / (PREVIEW_SAMPLES - 1);
303
- samples.push({ frame, value: blockProgressAt(block, frame, fps) ?? 0 });
304
- }
305
- const line = polylinePoints(samples, PREVIEW_W, PREVIEW_H, PREVIEW_PAD);
306
- if (line === null) return null;
307
- // The window's right edge in preview px, so the tail is drawn from exactly
308
- // where the block stops contributing.
309
- const edgeX = spring ? (duration / (to - block.frame)) * PREVIEW_W : PREVIEW_W;
310
-
311
- return (
312
- <div className="insp-preview animate-preview" data-testid="block-preview">
313
- <svg width={PREVIEW_W} height={PREVIEW_H}>
314
- {/* One polyline, clipped twice: solid inside the window, dashed after.
315
- Two <polyline>s over the same points with opposite clips beats
316
- splitting the sample array, because the split point then cannot land
317
- between two samples and jitter as the duration changes. */}
318
- <defs>
319
- <clipPath id="blk-in">
320
- <rect x={0} y={0} width={edgeX} height={PREVIEW_H} />
321
- </clipPath>
322
- <clipPath id="blk-out">
323
- <rect x={edgeX} y={0} width={PREVIEW_W - edgeX} height={PREVIEW_H} />
324
- </clipPath>
325
- </defs>
326
- <polyline className={spring ? "is-spring" : ""} points={line.points} clipPath="url(#blk-in)" />
327
- {spring && (
328
- <>
329
- <polyline className="is-tail" points={line.points} clipPath="url(#blk-out)" />
330
- <line className="animate-preview-edge" x1={edgeX} y1={0} x2={edgeX} y2={PREVIEW_H} />
331
- </>
332
- )}
333
- </svg>
334
- {spring && (
335
- <span className="insp-dim animate-preview-note">
336
- dashed = after the block ends; the spring holds at its clipped value
337
- </span>
338
- )}
339
- </div>
340
- );
341
- }