@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
@@ -16,22 +16,20 @@
16
16
  // the pull feels the same size at every zoom. A threshold in frames would get
17
17
  // stronger as the author zoomed out until a whole scene was one sticky point;
18
18
  // the store's layout snapping is divided by the view zoom for the same reason.
19
- import { sceneWindows } from "@dailies-studio/shot/doc";
19
+ import { beatFrames, masterAudioClips, sceneWindows } from "@dailies-studio/shot/doc";
20
20
  import type { Doc3D } from "@dailies-studio/shot/doc";
21
21
  import { lifespanOf } from "./model.js";
22
22
 
23
23
  /** How close a moving edge has to come, in SCREEN px. */
24
24
  export const TIMELINE_SNAP_PX = 6;
25
25
 
26
- export type SnapKind = "playhead" | "marker" | "scene" | "block" | "lifespan" | "audio";
26
+ export type SnapKind = "playhead" | "marker" | "scene" | "lifespan" | "audio" | "beat";
27
27
 
28
28
  export interface SnapTarget {
29
29
  /** MASTER frame. Every target in one space, because a gesture that crosses a
30
- * scene boundary has to compare a scene-2 block edge with a scene-1 one. */
30
+ * scene boundary has to compare a scene-2 lifespan edge with a scene-1 one. */
31
31
  frame: number;
32
32
  kind: SnapKind;
33
- /** What the guide would say if the UI ever labelled it. */
34
- label: string;
35
33
  }
36
34
 
37
35
  export interface SnapHit {
@@ -56,28 +54,64 @@ export interface SnapScope {
56
54
  *
57
55
  * Without it every drag is pinned at zero travel: the bar under the pointer
58
56
  * is always exactly on its own edge, so it wins every comparison and the
59
- * gesture never leaves the ground.
60
- *
61
- * `blockId` narrows that to the ONE bar. A lifespan drag moves the layer's bar
62
- * and every block on it, so the whole layer really is in motion and none of its
63
- * edges is a target. A block drag moves exactly one bar: its siblings and the
64
- * layer's own lifespan stay put, and butting a During block against the In
65
- * block's end — or onto the lifespan in, which is the pairing the lifespan row
66
- * exists to make visible — is the most natural alignment in the lane. Excluding
67
- * them buys nothing and disables it.
57
+ * gesture never leaves the ground. A lifespan drag moves the layer's bar and
58
+ * every key on it, so the whole layer is in motion and none of its edges is a
59
+ * target.
68
60
  */
69
61
  exclude?: {
70
62
  sceneId?: string;
71
63
  layerId?: string;
72
- blockId?: string;
73
- /** The audio clip a gesture is moving. Document-level, so it carries no
74
- * scene: `doc.audio` is timed on MASTER frames. */
64
+ /**
65
+ * EVERY bar a gesture is moving an edge of, for the single-bar reason above
66
+ * read once per bar: a body drag over a selection of four clips has four
67
+ * bars sitting exactly on their own edges, and any one of them wins every
68
+ * comparison and pins the whole gesture at zero travel.
69
+ *
70
+ * A ROLL is the other list, and it is a PAIR rather than a selection: the
71
+ * seam is one layer's `out` and the next layer's `in` at the same frame, so
72
+ * the partner's edge sits exactly on the frame under the pointer and pins
73
+ * the drag just as surely as the pressed bar's own edge would. Naming both
74
+ * sides is what makes a seam drag able to leave the ground.
75
+ *
76
+ * Carries no scene, on the same ground `audioClipId` and `markerId` state:
77
+ * a layer id is document-unique (`usedIdsOf` mints against every home at
78
+ * once), so the id identifies the bar without one.
79
+ */
80
+ layerIds?: readonly string[];
81
+ /** The audio clip a gesture is moving. Carries no scene even when a shot
82
+ * owns the clip: the id is document-unique across both homes (`usedIdsOf`
83
+ * mints against all of them), so it identifies the clip on its own. */
75
84
  audioClipId?: string;
76
85
  /** The marker a gesture is moving. Same reason as `audioClipId`, and it is
77
86
  * not optional politeness: a marker sits exactly on its own target, so
78
87
  * without this a marker drag is pinned at zero travel. */
79
88
  markerId?: string;
89
+ /** A shot's out trim RIPPLES: every scene, lifespan and shot-owned
90
+ * audio edge at or past this master frame moves with the trim, so none of
91
+ * them is a target. The playhead, the markers and the Project's audio stay
92
+ * put. */
93
+ rippleFromFrame?: number;
80
94
  };
95
+ /**
96
+ * WHERE THE MOVING EDGE MAY LEGALLY LAND, in master frames.
97
+ *
98
+ * Applied AT COLLECTION TIME, so an illegal target is simply not on the list.
99
+ * That is one code path rather than two: a trim bounded by `legalTrimRange`
100
+ * and a seam bounded by `legalRollRange` can only ever snap to a frame their
101
+ * op accepts, so an edge never appears to stick to a guide and then slip off
102
+ * it when the op clamps the same gesture back inside the range.
103
+ *
104
+ * An EMPTY range filters everything out, which is the right answer and needs
105
+ * no extra branch: `LegalRange` marks an empty intersection by carrying a
106
+ * `min` above its `max`, and no frame is both. An edge with nowhere legal to
107
+ * go has nothing legal to snap to.
108
+ *
109
+ * Omitted means unbounded above; frame 0 remains the floor either way.
110
+ */
111
+ legal?: { min: number; max: number };
112
+ /** Offer the beat grid's frames too (gap 8), when the document has a
113
+ * `tempo`. The dock's per-tab toggle (`snapBeats`). */
114
+ beats?: boolean;
81
115
  }
82
116
 
83
117
  /**
@@ -89,53 +123,78 @@ export interface SnapScope {
89
123
  *
90
124
  * Deduped by frame, first writer wins, which makes the push order below a
91
125
  * PRIORITY order: the playhead and the markers are what the author put there on
92
- * purpose, so a guide at a frame that is also a scene boundary names the marker.
126
+ * purpose, so a hit on a frame that is also a scene boundary comes back as the
127
+ * marker.
93
128
  */
94
129
  export function collectSnapTargets(doc: Doc3D, opts: SnapScope): SnapTarget[] {
95
130
  const out: SnapTarget[] = [];
96
131
  const seen = new Set<number>();
97
- const push = (frame: number, kind: SnapKind, label: string): void => {
132
+ // Frame 0 is the floor whether or not a legal range was given, so the two
133
+ // bounds fold into one pair and `push` keeps one comparison per side.
134
+ const lo = Math.max(0, opts.legal?.min ?? 0);
135
+ const hi = opts.legal?.max ?? Infinity;
136
+ const ripple = opts.exclude?.rippleFromFrame ?? Infinity;
137
+ /** `rides`: the edge moves under a ripple trim (`exclude.rippleFromFrame`). */
138
+ const push = (frame: number, kind: SnapKind, rides = true): void => {
98
139
  const at = Math.round(frame);
99
- if (at < 0 || seen.has(at)) return;
140
+ if (at < lo || at > hi || seen.has(at) || (rides && at >= ripple)) return;
100
141
  seen.add(at);
101
- out.push({ frame: at, kind, label });
142
+ out.push({ frame: at, kind });
102
143
  };
144
+ // The bars a multi-bar drag is moving. Built once: `exclude.layerIds` is read
145
+ // inside the layer loop, and a list scan per layer per scene is the only part
146
+ // of this walk that would grow with the selection.
147
+ const moving = new Set(opts.exclude?.layerIds ?? []);
103
148
 
104
- push(opts.playhead, "playhead", "playhead");
149
+ push(opts.playhead, "playhead", false);
105
150
  for (const marker of doc.markers ?? []) {
106
151
  if (marker.id === opts.exclude?.markerId) continue;
107
- push(marker.frame, "marker", marker.label ?? "marker");
152
+ push(marker.frame, "marker", false);
108
153
  }
109
154
 
110
- // Audio edges are MASTER frames already, so they need no scene offset — and
111
- // they come before the scene loop because landing a block on the start of the
112
- // music is the alignment this lane is for.
113
- for (const clip of Object.values(doc.audio)) {
155
+ // Audio edges, THROUGH THE FLATTEN so a clip its shot owns is a target too —
156
+ // its scene-local frames arrive here already shifted onto the master base
157
+ // every other target on this list is in. They come before the scene loop
158
+ // because landing a layer on the start of the music is the alignment this
159
+ // lane is for.
160
+ for (const { clip, layerId, sceneId } of masterAudioClips(doc)) {
114
161
  if (clip.id === opts.exclude?.audioClipId) continue;
115
- push(clip.frame, "audio", "audio in");
116
- push(clip.frame + clip.durationFrames, "audio", "audio out");
162
+ // A RECORDING'S OWN SOUND IS THE OTHER HALF OF THE BAR BEING DRAGGED, and
163
+ // without this line it defeats the whole exclusion below. The flatten
164
+ // places `video.sound` at `max(lifetime.in, offsetFrames)` for the rest of
165
+ // the window, so a video layer contributes an "audio" target on the very
166
+ // lifespan edge the layer loop is careful to withhold: a clip drag then had
167
+ // a target at distance zero from its own anchor and was pinned at zero
168
+ // travel for the first frame of pointer travel.
169
+ if (layerId !== undefined && (moving.has(layerId) || layerId === opts.exclude?.layerId)) continue;
170
+ push(clip.frame, "audio", sceneId !== null);
171
+ push(clip.frame + clip.durationFrames, "audio", sceneId !== null);
117
172
  }
118
173
 
119
174
  for (const window of sceneWindows(doc)) {
120
175
  const scene = doc.scenesById[window.id];
121
176
  if (scene === undefined) continue;
122
- push(window.start, "scene", `${scene.name} start`);
123
- push(window.end, "scene", `${scene.name} end`);
177
+ push(window.start, "scene");
178
+ push(window.end, "scene");
124
179
  for (const layer of Object.values(scene.layersById)) {
125
- const moving =
126
- opts.exclude?.sceneId === window.id && opts.exclude.layerId === layer.id ? opts.exclude : undefined;
127
- // The whole layer only when the whole layer moves — see SnapScope.exclude.
128
- if (moving !== undefined && moving.blockId === undefined) continue;
129
- const life = lifespanOf(layer, scene);
130
- push(window.start + life.in, "lifespan", `${layer.label} in`);
131
- push(window.start + life.out, "lifespan", `${layer.label} out`);
132
- for (const block of layer.blocks) {
133
- if (block.id === moving?.blockId) continue;
134
- push(window.start + block.frame, "block", `${layer.label} ${block.preset}`);
135
- push(window.start + block.frame + block.durationFrames, "block", `${layer.label} ${block.preset} end`);
136
- }
180
+ // A BAR THIS GESTURE CARRIES offers neither of its edges: the whole
181
+ // layer is in motion (see SnapScope.exclude).
182
+ if (moving.has(layer.id)) continue;
183
+ if (opts.exclude?.sceneId === window.id && opts.exclude.layerId === layer.id) continue;
184
+ const life = lifespanOf(layer, scene.durationFrames);
185
+ push(window.start + life.in, "lifespan");
186
+ push(window.start + life.out, "lifespan");
137
187
  }
138
188
  }
189
+ // BEATS LAST, the lowest priority: a beat is a grid nobody placed on this
190
+ // frame, so a marker, a cut or a clip edge on the same frame wins it. The
191
+ // grid runs to the furthest edge already listed plus a bar, so a clip can
192
+ // still be dragged out past the film's end onto a beat.
193
+ if (opts.beats === true && doc.tempo !== undefined) {
194
+ const last = out.reduce((max, t) => Math.max(max, t.frame), 0);
195
+ const bar = Math.ceil((60 * doc.fps.num * (doc.tempo.beatsPerBar ?? 4)) / (doc.fps.den * doc.tempo.bpm));
196
+ for (const beat of beatFrames(doc.tempo, doc.fps, lo, Math.min(hi + 1, last + bar + 1))) push(beat.frame, "beat", false);
197
+ }
139
198
  return out;
140
199
  }
141
200
 
@@ -162,6 +221,16 @@ export function snapFrame(
162
221
  return best === null ? { frame: rounded, target: null } : { frame: best.frame, target: best };
163
222
  }
164
223
 
224
+ // ── ONE ANCHOR PER GESTURE ───────────────────────────────────────────────────
225
+ //
226
+ // There is no multi-edge pairing here, and a `snapDelta` that offered one was
227
+ // deleted rather than wired: nothing called it, and the rows do not work that
228
+ // way. A body drag picks ONE anchor at the press (`LifespanRow`: "WHAT THE
229
+ // POINTER IS CARRYING is what snaps") and moves the rest by the delta that
230
+ // anchor came back with, which is why `SnapPress` below is a one-candidate
231
+ // closure. Wanting several edges in play is a real change to that type and to
232
+ // every row that presses, not a missing call.
233
+
165
234
  /**
166
235
  * What a row is handed at the press: give it the frame you want and it gives
167
236
  * back the frame you get, having told the dock where to draw the guide.
@@ -173,6 +242,19 @@ export function snapFrame(
173
242
  export type SnapPress = (
174
243
  exclude: SnapScope["exclude"],
175
244
  suppressed: boolean,
245
+ /**
246
+ * WHERE THIS GESTURE MAY LEGALLY LAND, in MASTER frames — the row's own range
247
+ * already shifted onto the base every target is collected in.
248
+ *
249
+ * A third argument rather than something the dock could derive, because only
250
+ * the row knows which bound it is under: a clip's `in` edge is bounded by its
251
+ * footage and its own `out`, a seam by both sides' footage at once, and the
252
+ * dock sees a candidate frame and nothing else. Omitted by every gesture with
253
+ * no bound, which is every gesture but a clip's two edges and its seam. See
254
+ * `SnapScope.legal` for why it filters at collection rather than clamping
255
+ * after.
256
+ */
257
+ legal?: SnapScope["legal"],
176
258
  ) => (candidateMasterFrame: number) => number;
177
259
 
178
260
  /**
@@ -0,0 +1,92 @@
1
+ // THE STAGGER'S ON-SCREEN HALF (Paper page 23, "8 · Stagger").
2
+ //
3
+ // A stagger moves several rows at once, and each row draws its own ghost, so the
4
+ // press-time picture has to reach rows the pressed one does not own. It is
5
+ // transient pointer state, not editor state: nothing is saved, undone or synced,
6
+ // so it lives here rather than in the store.
7
+ //
8
+ // ponytail: one module-level slot, because there is one pointer. Two editors
9
+ // dragging at once in one page would share it; scope it per store if that ever
10
+ // becomes a real case.
11
+ import { useSyncExternalStore } from "react";
12
+ import { frameToPx } from "@dailies-studio/timeline";
13
+ import { formatCombo } from "../../state/shortcuts.js";
14
+
15
+ export interface StaggerPreview {
16
+ /** Each moving bar's `in` at the press, in LANE frames (drawing clock). */
17
+ from: Readonly<Record<string, number>>;
18
+ /** The bar (or the key row) the pointer holds: it carries the readout. */
19
+ grabbed: string;
20
+ /** The step in frames, not the total: what the author is choosing. */
21
+ step: number;
22
+ /** Where a key row's readout sits, in lane pixels. A bar row places its own,
23
+ * beside the bar. */
24
+ anchor?: number;
25
+ }
26
+
27
+ let preview: StaggerPreview | null = null;
28
+ const listeners = new Set<() => void>();
29
+
30
+ export function setStaggerPreview(next: StaggerPreview | null): void {
31
+ preview = next;
32
+ for (const listener of listeners) listener();
33
+ }
34
+
35
+ function subscribe(listener: () => void): () => void {
36
+ listeners.add(listener);
37
+ return () => listeners.delete(listener);
38
+ }
39
+
40
+ /** This row's press-time `in`, or null when it is not in a running stagger.
41
+ * A primitive, so only the rows whose answer changed re-render. */
42
+ export function useStaggerGhost(id: string): number | null {
43
+ return useSyncExternalStore(subscribe, () => preview?.from[id] ?? null);
44
+ }
45
+
46
+ /** The running stagger when `id` holds its readout, else null. The slot's own
47
+ * object, so the snapshot is stable between two sets. */
48
+ export function useStaggerReadout(id: string): StaggerPreview | null {
49
+ return useSyncExternalStore(subscribe, () => (preview?.grabbed === id ? preview : null));
50
+ }
51
+
52
+ /** "Stagger +20f": the sign always shows, so a step left reads as one. */
53
+ export function staggerLabel(step: number): string {
54
+ return `Stagger ${step < 0 ? "−" : "+"}${Math.abs(step)}f`;
55
+ }
56
+
57
+ /** The dashed box where a bar started, and the accent rule as long as its shift. */
58
+ export function StaggerGhost({
59
+ fromFrame,
60
+ width,
61
+ shift,
62
+ ppf,
63
+ }: {
64
+ fromFrame: number;
65
+ width: number;
66
+ shift: number;
67
+ ppf: number;
68
+ }) {
69
+ const left = frameToPx(fromFrame, ppf);
70
+ const travel = frameToPx(Math.abs(shift), ppf);
71
+ return (
72
+ <span className="stagger-ghost" data-stagger-ghost="" style={{ left, width }} aria-hidden="true">
73
+ {shift === 0 ? null : (
74
+ // Leftward from the ghost's left edge for a step back, rightward otherwise.
75
+ <span
76
+ className="stagger-shift"
77
+ style={shift < 0 ? { right: "100%", width: travel } : { left: 0, width: travel }}
78
+ />
79
+ )}
80
+ </span>
81
+ );
82
+ }
83
+
84
+ /** The floating readout beside the grabbed bar. */
85
+ export function StaggerChip({ step, left }: { step: number; left: number }) {
86
+ return (
87
+ <span className="stagger-chip" data-testid="stagger-readout" role="status" style={{ left }}>
88
+ <span className="stagger-chip-step">{staggerLabel(step)}</span>
89
+ <span className="stagger-chip-key">{formatCombo("⌥")}</span>
90
+ </span>
91
+ );
92
+ }
@@ -0,0 +1,72 @@
1
+ // Measuring a generated take in this tab, for the Generate drawer's choice
2
+ // between two: decode it, then the shared measuring functions over its PCM
3
+ // (`@dailies-studio/shot/measure`), into the facts `rankSfxTakes` reads.
4
+ //
5
+ // A CHUNK OF ITS OWN: the drawer imports this module with `import()` when two
6
+ // takes arrive, and the three functions are imported here BY NAME. A dynamic
7
+ // import of the measure entry itself keeps every export it has, the probe's
8
+ // lazy mediabunny among them, which put a 75 kB chunk in the build for code
9
+ // this never runs.
10
+ //
11
+ // It ADVISES a choice and nothing more: the decode is the browser's, whose
12
+ // resampler the spec leaves open, so these numbers are close to a sidecar's,
13
+ // not equal to it, and nothing stored or exported reads them. A take that
14
+ // cannot be measured here is shown unranked, and the drawer says to listen.
15
+ import { rankSfxTakes, takeFacts, toneCheck } from "@dailies-studio/shot/doc";
16
+ import type { TakeFacts } from "@dailies-studio/shot/doc";
17
+ import type { GenerationTake } from "../../api.js";
18
+ import { measureLoudness, rmsEnvelopeDb, spectralBalance } from "@dailies-studio/shot/measure";
19
+
20
+ const RATE = 48_000;
21
+
22
+ /**
23
+ * The takes as the drawer shows them: the one that measures better first,
24
+ * the reasons, and the tone check on it. A take this browser cannot measure
25
+ * leaves both unranked, with a word to listen instead.
26
+ */
27
+ export async function judgeTakes(
28
+ takes: readonly GenerationTake[],
29
+ requestedSeconds: number,
30
+ ): Promise<{ order: GenerationTake[]; reasons: string[]; tone: string | null }> {
31
+ const facts = await Promise.all(takes.map((take) => measureTake(take.url, requestedSeconds)));
32
+ if (facts.some((f) => f === null)) {
33
+ return { order: [...takes], reasons: takes.length > 1 ? ["These takes could not be measured in this browser, so listen to both."] : [], tone: null };
34
+ }
35
+ const measured = facts as TakeFacts[];
36
+ if (takes.length < 2) return { order: [...takes], reasons: [], tone: measured[0] === undefined ? null : toneCheck(measured[0]) };
37
+ const ranked = rankSfxTakes(measured);
38
+ const pick = takes[ranked.pick]!;
39
+ return { order: [pick, ...takes.filter((take) => take !== pick)], reasons: ranked.reasons, tone: toneCheck(measured[ranked.pick]!) };
40
+ }
41
+
42
+ /** The take at `url` as the facts a ranking reads, or null when this browser
43
+ * cannot decode it. `requestedSeconds` is the length that was asked for. */
44
+ export async function measureTake(url: string, requestedSeconds: number): Promise<TakeFacts | null> {
45
+ try {
46
+ const Context = globalThis.OfflineAudioContext;
47
+ if (Context === undefined) return null;
48
+ const bytes = await (await fetch(url)).arrayBuffer();
49
+ const buffer = await new Context(2, 1, RATE).decodeAudioData(bytes);
50
+ const left = buffer.getChannelData(0);
51
+ const right = buffer.numberOfChannels > 1 ? buffer.getChannelData(1) : left;
52
+ const frames = buffer.length;
53
+ const samples = new Int16Array(frames * 2);
54
+ for (let i = 0; i < frames; i++) {
55
+ samples[i * 2] = Math.max(-32768, Math.min(32767, Math.round(left[i]! * 32768)));
56
+ samples[i * 2 + 1] = Math.max(-32768, Math.min(32767, Math.round(right[i]! * 32768)));
57
+ }
58
+ const wav = { sampleRate: buffer.sampleRate, channels: 2, frames, samples };
59
+ return takeFacts(
60
+ {
61
+ rmsDb: rmsEnvelopeDb(wav),
62
+ hopSamples: 960,
63
+ sampleRate: buffer.sampleRate,
64
+ loudness: measureLoudness(wav),
65
+ spectral: spectralBalance(wav),
66
+ },
67
+ requestedSeconds,
68
+ );
69
+ } catch {
70
+ return null;
71
+ }
72
+ }
@@ -0,0 +1,48 @@
1
+ // IS THE PLAYHEAD INSIDE THIS BAR RIGHT NOW.
2
+ //
3
+ // The timeline's one live state, and the reason `--bar-live` exists: a bar is
4
+ // green while the clock is in it and the hueless ink wash otherwise, so the
5
+ // strip says what is playing without the author tracing the playhead down forty
6
+ // rows to see which bars it crosses.
7
+ //
8
+ // ── WHY THIS IS A BOOLEAN SUBSCRIPTION AND NOT A FRAME ONE ─────────────────
9
+ //
10
+ // `useSyncExternalStore` notifies a component only when the SELECTED value
11
+ // changes, so selecting the predicate rather than the frame means a bar
12
+ // re-renders exactly twice per playhead pass-through — once on the way in and
13
+ // once on the way out — instead of once per playback frame. Forty bars on
14
+ // screen at 30fps is 80 renders over the length of a shot rather than 1200 a
15
+ // second, and the lane holding them never re-renders at all.
16
+ //
17
+ // `MarkerLane` found this shape first (`s.frame === marker.frame`); this is the
18
+ // same trick over a half-open range. The rule it protects is `TimelineDock`'s
19
+ // own header: THE FRAME STAYS OUT OF THAT COMPONENT. A dock root subscribed to
20
+ // the frame re-renders every scene group, camera row, track row and block bar
21
+ // thirty times a second, which is the performance hole this lane has already
22
+ // paid for once.
23
+ //
24
+ // The hot channel is right here and nowhere higher: `useShotEditorHot` is the
25
+ // one that fires on a frame-only `set`, and its own contract is that it lives
26
+ // "at the smallest leaf that must repaint every tick".
27
+ import { useShotEditorHot } from "../../state/context.js";
28
+ import { laneFrame } from "./model.js";
29
+
30
+ /**
31
+ * @param startLane The bar's left edge in LANE frames — the same number its
32
+ * `left` is drawn from, which is what keeps the highlight and
33
+ * the pixels from disagreeing about where the bar is.
34
+ * @param endLane Its right edge, HALF-OPEN: a bar that ends at frame 60 is
35
+ * not live at 60, because the next one starts there and two
36
+ * adjacent bars must never both be lit.
37
+ * @param origin The lane's re-zero, as `laneFrame` means it (a shot's
38
+ * master start, or 0 in the scene and definition contexts).
39
+ * `laneFrame` picks the master clock or the definition's own,
40
+ * so a bar in a component reads `defFrame` with no extra
41
+ * argument here.
42
+ */
43
+ export function useLive(startLane: number, endLane: number, origin: number): boolean {
44
+ return useShotEditorHot((s) => {
45
+ const frame = laneFrame(s, origin);
46
+ return frame >= startLane && frame < endLane;
47
+ });
48
+ }