@dailies-studio/shot-editor 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (432) hide show
  1. package/dist/.vite/manifest.json +382 -0
  2. package/dist/assets/AgentPanel-CQejLbiX.js +2 -0
  3. package/dist/assets/AgentPanel-DJEphah0.css +1 -0
  4. package/dist/assets/AgentQuestionDialog-BqkD0kXF.js +1 -0
  5. package/dist/assets/CodeStudio-Dv4ihHpz.js +23 -0
  6. package/dist/assets/CommentsPanel-B4yhhT1T.js +1 -0
  7. package/dist/assets/ComponentLibraryRail-Ch_dsAru.js +1 -0
  8. package/dist/assets/ComponentStage-BRhRotyH.js +1 -0
  9. package/dist/assets/CurvePane-BjFVabKX.css +1 -0
  10. package/dist/assets/CurvePane-R22WzllJ.js +1 -0
  11. package/dist/assets/ExportDialog-CWLQWTmG.js +2 -0
  12. package/dist/assets/FontPicker-DWKaYL7F.js +1 -0
  13. package/dist/assets/FontPicker-v0IOIArM.css +1 -0
  14. package/dist/assets/GenerateDrawer-Bh3SqDKt.js +2 -0
  15. package/dist/assets/InsertDrawer-Ce1fod1L.js +1 -0
  16. package/dist/assets/LibraryBrowser-D9sEGz5w.js +2 -0
  17. package/dist/assets/MakeItYoursSection-CMO4319_.js +2 -0
  18. package/dist/assets/MaterialGraphCanvas-BFzl4wWR.js +23 -0
  19. package/dist/assets/MaterialGraphCanvas-BIUD59ik.css +1 -0
  20. package/dist/assets/MaterialWorkspace-CqTWqaRA.js +14 -0
  21. package/dist/assets/PreferencesDialog-CBf9_OoD.js +1 -0
  22. package/dist/assets/PresetsSection-CUEaoCVU.js +1 -0
  23. package/dist/assets/SoundHistory-BeS8EW1h.js +1 -0
  24. package/dist/assets/SoundMode-BmcZru3i.js +3 -0
  25. package/dist/assets/StockSounds-DdAUGRYK.js +1 -0
  26. package/dist/assets/TemplateFieldSection-CaeXpLFo.js +1 -0
  27. package/dist/assets/VersionHistoryPanel-CxG6XhNP.js +1 -0
  28. package/dist/assets/board-B88EPuLO.css +1 -0
  29. package/dist/assets/board-CL-he7SL.js +3 -0
  30. package/dist/assets/components-0oOSPN0l.js +1 -0
  31. package/dist/assets/googleFontRanges-gp32wJMo.js +1 -0
  32. package/dist/assets/googleFonts-BNon6M7X.js +1 -0
  33. package/dist/assets/iconsAndAliases-DL7gXP5j.js +1863 -0
  34. package/dist/assets/index-4F5NQmI8.css +1 -0
  35. package/dist/assets/index-BGHQGV5z.js +117 -0
  36. package/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  37. package/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  38. package/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  39. package/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  40. package/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  41. package/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  42. package/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  43. package/dist/assets/libraryModel-CpIwfFha.js +1 -0
  44. package/dist/assets/lucide-6iK64tTB.js +9319 -0
  45. package/dist/assets/motionStamp-CH-Hew4E.js +1 -0
  46. package/dist/assets/takeMeasure-Tdg0TxEi.js +1 -0
  47. package/dist/assets/webmcp-BsB9gxWC.js +2 -0
  48. package/dist/index.html +34 -3
  49. package/package.json +12 -9
  50. package/src/addFamilies.ts +66 -0
  51. package/src/api.ts +1631 -55
  52. package/src/app/App.tsx +977 -198
  53. package/src/app/lazyPanel.tsx +137 -0
  54. package/src/app/lazySurfaces.tsx +203 -0
  55. package/src/audio/index.tsx +41 -10
  56. package/src/audio/meter.ts +485 -0
  57. package/src/audio/meterMath.ts +273 -0
  58. package/src/audio/meterStore.ts +155 -0
  59. package/src/audio/offlineMix.ts +289 -0
  60. package/src/audio/preview.ts +928 -118
  61. package/src/bridge.ts +15 -5
  62. package/src/bridgeTypes.ts +1611 -36
  63. package/src/channels.ts +17 -32
  64. package/src/fonts/googleFonts.ts +4 -0
  65. package/src/hostApi.ts +174 -0
  66. package/src/icons.tsx +770 -0
  67. package/src/layerWords.ts +61 -0
  68. package/src/main.tsx +56 -4
  69. package/src/panels/ResizeHandle.tsx +33 -43
  70. package/src/panels/agent/AgentDots.tsx +36 -0
  71. package/src/panels/agent/AgentPanel.tsx +119 -43
  72. package/src/panels/agent/AgentQuestionDialog.tsx +202 -0
  73. package/src/panels/agent/agent.css +113 -97
  74. package/src/panels/agent/agentGate.ts +35 -0
  75. package/src/panels/code/CodeDefaultsPanel.tsx +345 -0
  76. package/src/panels/code/CodeStudio.tsx +373 -0
  77. package/src/panels/code/FileTree.tsx +265 -0
  78. package/src/panels/code/codeBufferGuard.ts +52 -0
  79. package/src/panels/code/codeParts.tsx +321 -0
  80. package/src/panels/comments/CommentComposer.tsx +239 -0
  81. package/src/panels/comments/CommentThread.tsx +346 -0
  82. package/src/panels/comments/CommentsPanel.tsx +336 -0
  83. package/src/panels/comments/InboxButton.tsx +131 -0
  84. package/src/panels/comments/MentionMenu.tsx +98 -0
  85. package/src/panels/comments/parts.tsx +171 -0
  86. package/src/panels/export/ExportDialog.tsx +919 -96
  87. package/src/panels/export/ExportScrim.tsx +57 -0
  88. package/src/panels/files/AssetsTab.tsx +656 -0
  89. package/src/panels/files/FilePanel.tsx +1466 -216
  90. package/src/panels/files/LayerMarks.tsx +40 -0
  91. package/src/panels/files/LeftColumn.tsx +37 -0
  92. package/src/panels/files/McpSection.tsx +690 -47
  93. package/src/panels/files/MissingAssetsNotice.tsx +60 -0
  94. package/src/panels/files/assetDrag.ts +79 -0
  95. package/src/panels/files/resourceParts.tsx +255 -0
  96. package/src/panels/fonts/FontPicker.tsx +762 -0
  97. package/src/panels/fonts/faceCache.ts +207 -0
  98. package/src/panels/fonts/fontPicker.css +271 -0
  99. package/src/panels/fonts/pickerModel.ts +265 -0
  100. package/src/panels/fonts/pickerRequest.ts +46 -0
  101. package/src/panels/header/AccountAvatar.tsx +76 -0
  102. package/src/panels/header/AgentChip.tsx +77 -0
  103. package/src/panels/header/AppMenu.tsx +213 -0
  104. package/src/panels/header/Header.tsx +564 -84
  105. package/src/panels/header/HistoryControls.tsx +31 -5
  106. package/src/panels/header/PresenceStack.tsx +329 -0
  107. package/src/panels/header/ProjectTitle.tsx +232 -0
  108. package/src/panels/header/ShareButton.tsx +59 -0
  109. package/src/panels/header/StatusBadges.tsx +358 -27
  110. package/src/panels/header/SyncDeletedDialog.tsx +88 -0
  111. package/src/panels/header/ZoomControls.tsx +46 -15
  112. package/src/panels/header/agentHandoff.ts +52 -0
  113. package/src/panels/header/leave.ts +26 -0
  114. package/src/panels/help/HelpSection.tsx +189 -163
  115. package/src/panels/insert/InsertDrawer.tsx +723 -0
  116. package/src/panels/insert/iconSet.ts +109 -0
  117. package/src/panels/inspector/ExposeDot.tsx +117 -0
  118. package/src/panels/inspector/FramingRows.tsx +314 -44
  119. package/src/panels/inspector/InspectorFooter.tsx +91 -0
  120. package/src/panels/inspector/InspectorHead.tsx +425 -0
  121. package/src/panels/inspector/InspectorPanel.tsx +408 -124
  122. package/src/panels/inspector/KeyframeDot.tsx +373 -0
  123. package/src/panels/inspector/KeyframeSection.tsx +257 -0
  124. package/src/panels/inspector/MarkerSection.tsx +55 -11
  125. package/src/panels/inspector/ParamRow.tsx +84 -53
  126. package/src/panels/inspector/RightColumn.tsx +152 -0
  127. package/src/panels/inspector/TimeSection.tsx +866 -0
  128. package/src/panels/inspector/animate/EaseControl.tsx +244 -0
  129. package/src/panels/inspector/animate/PresetsSection.tsx +135 -0
  130. package/src/panels/inspector/animate/ShotSection.tsx +562 -48
  131. package/src/panels/inspector/design/AlignRow.tsx +95 -31
  132. package/src/panels/inspector/design/AppearanceSection.tsx +118 -9
  133. package/src/panels/inspector/design/ClonerSection.tsx +323 -62
  134. package/src/panels/inspector/design/ComponentSection.tsx +50 -43
  135. package/src/panels/inspector/design/CornerSection.tsx +45 -14
  136. package/src/panels/inspector/design/DefinitionProps.tsx +315 -0
  137. package/src/panels/inspector/design/DefinitionSection.tsx +136 -0
  138. package/src/panels/inspector/design/DeviceSection.tsx +210 -0
  139. package/src/panels/inspector/design/FillSection.tsx +83 -51
  140. package/src/panels/inspector/design/FontField.tsx +196 -0
  141. package/src/panels/inspector/design/HtmlSection.tsx +23 -12
  142. package/src/panels/inspector/design/InstanceSection.tsx +373 -154
  143. package/src/panels/inspector/design/MaskSection.tsx +84 -0
  144. package/src/panels/inspector/design/MaterialSection.tsx +177 -0
  145. package/src/panels/inspector/design/MediaSection.tsx +273 -120
  146. package/src/panels/inspector/design/MeshSection.tsx +910 -82
  147. package/src/panels/inspector/design/PaintRows.tsx +550 -0
  148. package/src/panels/inspector/design/PlateStyleSection.tsx +177 -0
  149. package/src/panels/inspector/design/ShadowSection.tsx +221 -48
  150. package/src/panels/inspector/design/SplineFollowSection.tsx +235 -0
  151. package/src/panels/inspector/design/StrokeSection.tsx +228 -43
  152. package/src/panels/inspector/design/TemplateFieldSection.tsx +57 -0
  153. package/src/panels/inspector/design/TransformSection.tsx +244 -50
  154. package/src/panels/inspector/design/TypographySection.tsx +679 -110
  155. package/src/panels/inspector/layout.ts +295 -0
  156. package/src/panels/inspector/propValue.ts +199 -0
  157. package/src/panels/inspector/scene/CameraSection.tsx +232 -22
  158. package/src/panels/inspector/scene/EffectsSection.tsx +722 -71
  159. package/src/panels/inspector/scene/MakeItYoursSection.tsx +180 -0
  160. package/src/panels/inspector/scene/SceneDocSection.tsx +191 -0
  161. package/src/panels/inspector/scene/SceneSection.tsx +219 -44
  162. package/src/panels/inspector/scene/TransitionSection.tsx +523 -72
  163. package/src/panels/inspector/storyboard/StoryboardReferenceSection.tsx +593 -0
  164. package/src/panels/inspector/storyboard/VideoStill.tsx +120 -0
  165. package/src/panels/inspector/timeModel.ts +190 -0
  166. package/src/panels/inspector/useDocWrite.ts +438 -146
  167. package/src/panels/library/LibraryBrowser.tsx +435 -0
  168. package/src/panels/library/LibraryGrid.tsx +190 -0
  169. package/src/panels/library/LibraryPreviewPanel.tsx +349 -0
  170. package/src/panels/library/LibraryRail.tsx +84 -0
  171. package/src/panels/library/LibrarySoundPanel.tsx +293 -0
  172. package/src/panels/library/SoundHistory.tsx +245 -0
  173. package/src/panels/library/StockSounds.tsx +390 -0
  174. package/src/panels/library/libraryModel.ts +147 -0
  175. package/src/panels/library/stockSearch.ts +77 -0
  176. package/src/panels/maskMarks.tsx +117 -0
  177. package/src/panels/material/MaterialCommentNode.tsx +54 -0
  178. package/src/panels/material/MaterialGraphCanvas.tsx +861 -0
  179. package/src/panels/material/MaterialGraphMenu.tsx +45 -0
  180. package/src/panels/material/MaterialGroupNode.tsx +56 -0
  181. package/src/panels/material/MaterialLibrary.tsx +253 -0
  182. package/src/panels/material/MaterialNode.tsx +453 -0
  183. package/src/panels/material/MaterialNodePalette.tsx +349 -0
  184. package/src/panels/material/MaterialParameterControls.tsx +525 -0
  185. package/src/panels/material/MaterialRerouteNode.tsx +42 -0
  186. package/src/panels/material/MaterialWorkspace.tsx +501 -0
  187. package/src/panels/material/NodePreview.tsx +70 -0
  188. package/src/panels/material/flowModel.ts +441 -0
  189. package/src/panels/material/parameterValues.ts +231 -0
  190. package/src/panels/material/previewScheduler.ts +235 -0
  191. package/src/panels/material/reactFlow.css +6 -0
  192. package/src/panels/material/socketGlyph.ts +85 -0
  193. package/src/panels/material/viewportMemory.ts +58 -0
  194. package/src/panels/preferences/PreferencesDialog.tsx +215 -0
  195. package/src/panels/share/ShareComponentDialog.tsx +247 -0
  196. package/src/panels/stage/StageMenu.tsx +35 -5
  197. package/src/panels/stage/StagePanel.tsx +338 -23
  198. package/src/panels/stage/cellSlots.tsx +70 -0
  199. package/src/panels/storyboard/BoardCanvas.tsx +739 -0
  200. package/src/panels/storyboard/BoardCursors.tsx +158 -0
  201. package/src/panels/storyboard/BoardDropDialog.tsx +161 -0
  202. package/src/panels/storyboard/BoardPlan.tsx +42 -0
  203. package/src/panels/storyboard/ComponentSheet.tsx +187 -0
  204. package/src/panels/storyboard/ComponentStage.tsx +235 -0
  205. package/src/panels/storyboard/MiniSketch.tsx +173 -0
  206. package/src/panels/storyboard/PartPicker.tsx +64 -0
  207. package/src/panels/storyboard/PlanPanel.tsx +617 -0
  208. package/src/panels/storyboard/ReferencePreview.tsx +217 -0
  209. package/src/panels/storyboard/ShotBrief.tsx +360 -0
  210. package/src/panels/storyboard/ShotCard.tsx +251 -0
  211. package/src/panels/storyboard/StagePreview.tsx +348 -0
  212. package/src/panels/storyboard/StillThumbs.tsx +162 -0
  213. package/src/panels/storyboard/StoryboardView.tsx +79 -0
  214. package/src/panels/storyboard/board.css +1065 -0
  215. package/src/panels/storyboard/board.ts +12 -0
  216. package/src/panels/storyboard/boardClipboard.ts +111 -0
  217. package/src/panels/storyboard/boardOrder.ts +68 -0
  218. package/src/panels/storyboard/boardRows.tsx +425 -0
  219. package/src/panels/storyboard/liveCap.ts +90 -0
  220. package/src/panels/storyboard/plan.css +295 -0
  221. package/src/panels/storyboard/posterCache.ts +133 -0
  222. package/src/panels/storyboard/shotboard/ComponentPicker.tsx +373 -0
  223. package/src/panels/storyboard/shotboard/ImagePicker.tsx +145 -0
  224. package/src/panels/storyboard/shotboard/ShotBoard.tsx +292 -0
  225. package/src/panels/storyboard/shotboard/SketchCanvas.tsx +443 -0
  226. package/src/panels/storyboard/shotboard/SketchInspector.tsx +471 -0
  227. package/src/panels/storyboard/shotboard/SketchItem.tsx +240 -0
  228. package/src/panels/storyboard/shotboard/SketchLayers.tsx +130 -0
  229. package/src/panels/storyboard/shotboard/SketchPlay.tsx +108 -0
  230. package/src/panels/storyboard/shotboard/SketchStrip.tsx +178 -0
  231. package/src/panels/storyboard/shotboard/SketchToolFooter.tsx +99 -0
  232. package/src/panels/storyboard/shotboard/sketch.css +1392 -0
  233. package/src/panels/storyboard/shotboard/sketchModel.ts +219 -0
  234. package/src/panels/storyboard/storyboardDrop.ts +164 -0
  235. package/src/panels/studio/ComponentLibraryRail.tsx +346 -0
  236. package/src/panels/studio/ComponentSidePanel.tsx +355 -0
  237. package/src/panels/studio/ComponentStudio.tsx +243 -0
  238. package/src/panels/studio/ComponentsHome.tsx +318 -0
  239. package/src/panels/studio/ComponentsPreview.tsx +179 -0
  240. package/src/panels/studio/EveryUse.tsx +244 -0
  241. package/src/panels/studio/ThisUsePanel.tsx +679 -0
  242. package/src/panels/studio/componentParts.tsx +341 -0
  243. package/src/panels/studio/components.ts +6 -0
  244. package/src/panels/studio/newCodeComponent.ts +66 -0
  245. package/src/panels/timeline/AudioFxButton.tsx +290 -0
  246. package/src/panels/timeline/AudioLane.tsx +674 -85
  247. package/src/panels/timeline/BlockRow.tsx +154 -85
  248. package/src/panels/timeline/ChannelRow.tsx +378 -129
  249. package/src/panels/timeline/CurvePane.tsx +508 -0
  250. package/src/panels/timeline/Filmstrip.tsx +361 -0
  251. package/src/panels/timeline/GenerateDrawer.tsx +407 -0
  252. package/src/panels/timeline/KeyStretch.tsx +115 -0
  253. package/src/panels/timeline/LifespanRow.tsx +730 -47
  254. package/src/panels/timeline/MarkerLane.tsx +46 -8
  255. package/src/panels/timeline/PlayButton.tsx +91 -0
  256. package/src/panels/timeline/PropertyPicker.tsx +254 -0
  257. package/src/panels/timeline/ShotRow.tsx +400 -0
  258. package/src/panels/timeline/SoundMode.tsx +1406 -0
  259. package/src/panels/timeline/TimelineDock.tsx +2338 -272
  260. package/src/panels/timeline/TransportVolume.tsx +163 -0
  261. package/src/panels/timeline/beatRuler.ts +74 -0
  262. package/src/panels/timeline/curveModel.ts +500 -0
  263. package/src/panels/timeline/duckUnderVoice.ts +68 -0
  264. package/src/panels/timeline/keyframeMenu.ts +138 -0
  265. package/src/panels/timeline/labelWidth.ts +49 -0
  266. package/src/panels/timeline/marquee.ts +155 -0
  267. package/src/panels/timeline/model.ts +1860 -199
  268. package/src/panels/timeline/snap.ts +125 -43
  269. package/src/panels/timeline/staggerPreview.tsx +92 -0
  270. package/src/panels/timeline/takeMeasure.ts +72 -0
  271. package/src/panels/timeline/useLive.ts +48 -0
  272. package/src/panels/timeline/viewport.ts +234 -0
  273. package/src/panels/toolbar/BackChip.tsx +116 -0
  274. package/src/panels/toolbar/CameraViewBar.tsx +118 -49
  275. package/src/panels/toolbar/CanvasBar.tsx +117 -0
  276. package/src/panels/toolbar/InsertButton.tsx +38 -0
  277. package/src/panels/toolbar/IsolationTrail.tsx +156 -0
  278. package/src/panels/toolbar/PanelToggles.tsx +86 -0
  279. package/src/panels/toolbar/ShadingBar.tsx +119 -26
  280. package/src/panels/toolbar/ToolDockBar.tsx +329 -80
  281. package/src/panels/toolbar/ToolFooter.tsx +102 -0
  282. package/src/panels/toolbar/ViewModeBar.tsx +247 -38
  283. package/src/panels/toolbar/WorkPlaneBar.tsx +94 -0
  284. package/src/panels/toolbar/useAddRows.tsx +326 -0
  285. package/src/panels/toolbar/useDismiss.ts +122 -0
  286. package/src/panels/versions/VersionHistoryPanel.tsx +274 -0
  287. package/src/panels/versions/versionModel.ts +103 -0
  288. package/src/panels/virtualWindow.ts +271 -0
  289. package/src/shotTokens.css +151 -60
  290. package/src/stage/FrustumGizmo.tsx +504 -84
  291. package/src/stage/GridPlane.tsx +225 -16
  292. package/src/stage/NodePreviews.tsx +628 -0
  293. package/src/stage/PerfHud.tsx +437 -47
  294. package/src/stage/SplitRenderer.tsx +816 -169
  295. package/src/stage/StageApp.tsx +4274 -486
  296. package/src/stage/StageRootBoundary.tsx +114 -0
  297. package/src/stage/StageTools.tsx +651 -151
  298. package/src/stage/TransformGizmo.tsx +696 -0
  299. package/src/stage/adaptivePresentation.ts +145 -0
  300. package/src/stage/bridgeClient.ts +2522 -41
  301. package/src/stage/exportWorker.ts +9 -0
  302. package/src/stage/feed.ts +305 -14
  303. package/src/stage/fontPreview.ts +176 -0
  304. package/src/stage/frustumGeometry.ts +319 -1
  305. package/src/stage/furnitureTokens.ts +33 -0
  306. package/src/stage/gizmoMath.ts +508 -16
  307. package/src/stage/gpuTimer.ts +276 -0
  308. package/src/stage/instanceModules.ts +194 -33
  309. package/src/stage/interactions/move.ts +24 -5
  310. package/src/stage/isolation.ts +291 -33
  311. package/src/stage/jsSelfProfiler.ts +325 -0
  312. package/src/stage/main.tsx +116 -16
  313. package/src/stage/modalState.ts +222 -23
  314. package/src/stage/moduleEpoch.ts +60 -0
  315. package/src/stage/motionPathStage.ts +279 -0
  316. package/src/stage/motionPreview.ts +74 -0
  317. package/src/stage/overlays/Overlays.tsx +1112 -159
  318. package/src/stage/overlays/Rulers.tsx +495 -0
  319. package/src/stage/panes.ts +159 -10
  320. package/src/stage/project.ts +68 -3
  321. package/src/stage/renderScaleController.ts +730 -0
  322. package/src/stage/rulers.ts +129 -0
  323. package/src/stage/sceneExtent.ts +75 -0
  324. package/src/stage/shading.ts +20 -0
  325. package/src/stage/splineStage.ts +632 -0
  326. package/src/stage/stagePerformanceTelemetry.ts +717 -0
  327. package/src/stage/stageTheme.ts +36 -0
  328. package/src/stage/stageWarmup.ts +11 -0
  329. package/src/stage/textAutoSize.ts +124 -0
  330. package/src/stage/tier1Export.ts +641 -0
  331. package/src/stage/tokenColor.ts +14 -9
  332. package/src/stage/view.ts +56 -9
  333. package/src/stage/workingView.ts +277 -33
  334. package/src/state/accountPrefs.ts +261 -0
  335. package/src/state/agentActions.ts +173 -15
  336. package/src/state/agentActivity.ts +198 -0
  337. package/src/state/agentQuestion.ts +239 -0
  338. package/src/state/bootOpen.ts +42 -0
  339. package/src/state/cameraVerbs.ts +139 -0
  340. package/src/state/clientId.ts +6 -1
  341. package/src/state/comments.ts +1099 -0
  342. package/src/state/context.tsx +19 -1
  343. package/src/state/editorPrefs.ts +220 -0
  344. package/src/state/errorReports.ts +121 -0
  345. package/src/state/fieldWrite.ts +161 -0
  346. package/src/state/fontRecents.ts +70 -0
  347. package/src/state/fonts.ts +656 -0
  348. package/src/state/graphClipboard.ts +414 -0
  349. package/src/state/imageThumbs.ts +216 -0
  350. package/src/state/insert.ts +444 -67
  351. package/src/state/isolationTrail.ts +92 -0
  352. package/src/state/keyMoves.ts +134 -0
  353. package/src/state/keyframeClipboard.ts +177 -0
  354. package/src/state/keymap.ts +1153 -117
  355. package/src/state/layoutPrefs.ts +272 -0
  356. package/src/state/layouts.ts +205 -0
  357. package/src/state/measureMedia.ts +167 -0
  358. package/src/state/menu.ts +2341 -28
  359. package/src/state/motionStamp.ts +7 -0
  360. package/src/state/panelSize.ts +375 -5
  361. package/src/state/pendingChanges.ts +119 -0
  362. package/src/state/playHold.ts +78 -0
  363. package/src/state/playbackPreparation.ts +562 -0
  364. package/src/state/posterCapture.ts +242 -0
  365. package/src/state/presence.ts +254 -0
  366. package/src/state/projectComponents.ts +160 -0
  367. package/src/state/renderEvents.ts +298 -18
  368. package/src/state/runtimeErrors.ts +46 -0
  369. package/src/state/selection.ts +12 -0
  370. package/src/state/shortcuts.ts +1606 -0
  371. package/src/state/sketchGuide.ts +51 -0
  372. package/src/state/splineRefs.ts +87 -0
  373. package/src/state/stageHealth.ts +259 -0
  374. package/src/state/stageWatchdog.ts +47 -0
  375. package/src/state/stillStage.ts +330 -0
  376. package/src/state/stillThumbs.ts +474 -0
  377. package/src/state/store.ts +23591 -2982
  378. package/src/state/storyboardModel.ts +528 -0
  379. package/src/state/sync.ts +1257 -0
  380. package/src/state/syncCache.ts +159 -0
  381. package/src/state/triangles.ts +13 -0
  382. package/src/state/types.ts +1698 -55
  383. package/src/state/unsavedGuard.ts +49 -0
  384. package/src/state/viewMode.ts +200 -0
  385. package/src/state/webmcp.ts +1128 -0
  386. package/src/state/workspace.ts +1147 -0
  387. package/src/styles/assets-tab.css +304 -0
  388. package/src/styles/canvas.css +545 -0
  389. package/src/styles/comments.css +1435 -0
  390. package/src/styles/controls.css +162 -0
  391. package/src/styles/dialogs.css +390 -0
  392. package/src/styles/insert-drawer.css +586 -0
  393. package/src/styles/inspector-board.css +207 -0
  394. package/src/styles/inspector-object.css +486 -0
  395. package/src/styles/inspector-shot.css +252 -0
  396. package/src/styles/inspector.css +360 -0
  397. package/src/styles/left-column.css +1628 -0
  398. package/src/styles/library-preview.css +575 -0
  399. package/src/styles/library.css +512 -0
  400. package/src/styles/modes/code.css +569 -0
  401. package/src/styles/modes/component.css +1561 -0
  402. package/src/styles/modes/material.css +1201 -0
  403. package/src/styles/modes/storyboard.css +518 -0
  404. package/src/styles/share-component.css +49 -0
  405. package/src/styles/shell.css +653 -0
  406. package/src/styles/shot-brief.css +360 -0
  407. package/src/styles/states.css +76 -0
  408. package/src/styles/sync.css +380 -0
  409. package/src/styles/timeline-graph.css +384 -0
  410. package/src/styles/timeline-meters.css +89 -0
  411. package/src/styles/timeline-rows.css +1959 -0
  412. package/src/styles/timeline-sound.css +1003 -0
  413. package/src/styles/timeline.css +754 -0
  414. package/src/styles/tool-footer.css +525 -0
  415. package/src/styles/topbar.css +886 -0
  416. package/src/styles/versions.css +262 -0
  417. package/src/styles.css +72 -2921
  418. package/src/tools.ts +108 -23
  419. package/dist/assets/index-B7notge7.css +0 -1
  420. package/dist/assets/index-BBE6Ta9L.js +0 -52
  421. package/src/panels/files/icons.tsx +0 -118
  422. package/src/panels/header/DocSwitcher.tsx +0 -85
  423. package/src/panels/header/TransportBar.tsx +0 -86
  424. package/src/panels/inspector/animate/BlockTiming.tsx +0 -341
  425. package/src/panels/inspector/animate/DuringBlocks.tsx +0 -85
  426. package/src/panels/inspector/animate/InOutPresets.tsx +0 -57
  427. package/src/panels/inspector/animate/ResolvedChannels.tsx +0 -150
  428. package/src/panels/inspector/animate/useBlocks.ts +0 -43
  429. package/src/panels/material/MaterialPicker.tsx +0 -172
  430. package/src/panels/timeline/SceneStrip.tsx +0 -145
  431. package/src/panels/toolbar/AddMenu.tsx +0 -230
  432. package/src/stage/MoveGizmo.tsx +0 -411
@@ -0,0 +1,92 @@
1
+ // The isolation trail: which layers sit above the isolated one, whether
2
+ // anything inside it is drawing at the playhead, and whether it can take an
3
+ // insert. The stage header's breadcrumb (IsolationTrail.tsx) reads the first
4
+ // two. One walk, so the header and anything else that names the path cannot
5
+ // disagree about it.
6
+ import {
7
+ canHoldChildren,
8
+ layerAliveAt,
9
+ layerChildren,
10
+ parentOf,
11
+ sceneStart,
12
+ type Layer3D,
13
+ } from "@dailies-studio/shot/doc";
14
+ import { wordFor } from "../layerWords.js";
15
+ import type { ShotEditorState } from "./types.js";
16
+
17
+ type Lookup = { layersById: Record<string, Layer3D> };
18
+
19
+ /** The isolated layer and every ancestor above it, outermost first. The path
20
+ * the breadcrumb walks, and the reason entering a nested subtree is reversible
21
+ * one level at a time. Bounded by the layer count, so a corrupt parent cycle
22
+ * ends instead of hanging the chrome. */
23
+ export function crumbsFor(scene: Lookup, id: string): string[] {
24
+ const out: string[] = [];
25
+ const limit = Object.keys(scene.layersById).length;
26
+ let at: string | null = id;
27
+ for (let i = 0; at !== null && i <= limit; i += 1) {
28
+ out.push(at);
29
+ at = parentOf(scene as never, at);
30
+ }
31
+ return out.reverse();
32
+ }
33
+
34
+ /**
35
+ * True when the isolated subtree draws nothing at the playhead: the layer or
36
+ * an ancestor is outside its lifespan (or hidden), or every leaf under it is.
37
+ * The pane is then uniformly dimmed with nothing to click, and the breadcrumb
38
+ * says why instead of letting the mode read as broken.
39
+ *
40
+ * A selector returning a boolean, so a subscriber re-renders only when the
41
+ * answer flips, not on every playback frame. An empty container counts as on
42
+ * screen: there is nothing to be missing.
43
+ */
44
+ export function isolationOffscreen(s: ShotEditorState): boolean {
45
+ if (s.doc === null || s.isolation === null || s.editingDef !== null || s.sceneId === null) {
46
+ return false;
47
+ }
48
+ const scene = s.doc.scenesById[s.sceneId];
49
+ if (scene === undefined || scene.layersById[s.isolation] === undefined) return false;
50
+ const start = sceneStart(s.doc, s.sceneId);
51
+ if (start === null) return false;
52
+ const frame = s.frame - start;
53
+ if (frame < 0 || frame >= scene.durationFrames) return true;
54
+ const chain = crumbsFor(scene, s.isolation);
55
+ for (const id of chain.slice(0, -1)) {
56
+ const layer = scene.layersById[id];
57
+ if (layer !== undefined && !layerAliveAt(layer, frame)) return true;
58
+ }
59
+ const draws = (id: string): boolean => {
60
+ const layer = scene.layersById[id];
61
+ if (layer === undefined || !layerAliveAt(layer, frame)) return false;
62
+ const kids = layerChildren(layer);
63
+ return kids.length === 0 || kids.some(draws);
64
+ };
65
+ return !draws(s.isolation);
66
+ }
67
+
68
+ /**
69
+ * Why nothing can be added while `layer` is the isolation subject, or null
70
+ * when it can take children (or there is no subject).
71
+ *
72
+ * Everything outside the isolated subtree is dimmed and unpickable, so an
73
+ * insert that fell back to the scene root used to land where the author could
74
+ * neither see nor click it. A leaf subject refuses instead, with the reason.
75
+ * `insertContext` reports it, and the tool dock and the Use buttons render it
76
+ * as their disabled reason, so a refused click is never a surprise.
77
+ */
78
+ export function leafInsertRefusal(layer: Layer3D | undefined): string | null {
79
+ if (layer === undefined || canHoldChildren(layer)) return null;
80
+ const word = wordFor(layer);
81
+ const noun = word === word.toUpperCase() ? word : word.toLowerCase();
82
+ return `Can't add inside this ${noun}. Only groups, devices and cloners hold layers.`;
83
+ }
84
+
85
+ /** `leafInsertRefusal` for the isolation the stage is showing. A string or
86
+ * null, so a selector over it allocates nothing. */
87
+ export function isolationInsertRefusal(
88
+ s: Pick<ShotEditorState, "doc" | "sceneId" | "isolation" | "editingDef">,
89
+ ): string | null {
90
+ if (s.doc === null || s.isolation === null || s.editingDef !== null || s.sceneId === null) return null;
91
+ return leafInsertRefusal(s.doc.scenesById[s.sceneId]?.layersById[s.isolation]);
92
+ }
@@ -0,0 +1,134 @@
1
+ // MOVING SEVERAL KEYFRAMES AT ONCE, each by its own distance. The keyboard
2
+ // nudge moves a selection rigidly; the ⌥-drag stagger fans it out; the stretch
3
+ // bracket scales it about its far end. Each is one
4
+ // pure write over the document, so the store verb around each is only the
5
+ // bracket (an `edit` for the nudge, a scrub capture for the drag).
6
+ import { homeOf, moveLayerKeyframe, moveSceneKeyframe } from "@dailies-studio/shot/doc";
7
+ import { retimeLandings, retimeMinFactor } from "@dailies-studio/timeline";
8
+ import type { Doc3D } from "@dailies-studio/shot/doc";
9
+ import type { KeyframeSel } from "./types.js";
10
+
11
+ export interface KeyMove {
12
+ key: KeyframeSel;
13
+ to: number;
14
+ }
15
+
16
+ const trackOf = (k: KeyframeSel): string => `${k.containerId}|${k.layerId ?? ""}|${k.path}`;
17
+
18
+ /**
19
+ * Move every key in `moves` to its `to`, or refuse the whole set.
20
+ *
21
+ * REFUSED WHOLE ON ANY COLLISION: a key landing on a key that is not moving
22
+ * away, or two keys landing on one frame of one track, would replace a key with
23
+ * nothing on screen having said so. A frame below 0 refuses too.
24
+ *
25
+ * ORDER MATTERS INSIDE THE WRITE: a key may only land once the key that sat on
26
+ * its target has left, or the earlier one replaces the later. Each pass moves
27
+ * every key whose target is free, so a rigid nudge goes front to back in its own
28
+ * direction and a fan goes outward.
29
+ */
30
+ export function moveKeyRefs(doc: Doc3D, moves: readonly KeyMove[]): { doc: Doc3D; refusal: string | null } {
31
+ const live = moves.filter((m) => m.to !== m.key.frame);
32
+ if (live.length === 0) return { doc, refusal: null };
33
+ const leaving = new Set(live.map((m) => `${trackOf(m.key)}|${m.key.frame}`));
34
+ const landing = new Set<string>();
35
+ for (const { key, to } of live) {
36
+ if (to < 0) return { doc, refusal: "That would put a keyframe before frame 0." };
37
+ const at = `${trackOf(key)}|${to}`;
38
+ const tracks =
39
+ key.layerId === null
40
+ ? doc.scenesById[key.containerId]?.tracks
41
+ : homeOf(doc, key.containerId)?.layersById[key.layerId]?.tracks;
42
+ const occupied = tracks?.[key.path]?.keyframes.some((other) => other.frame === to) === true;
43
+ if ((occupied && !leaving.has(at)) || landing.has(at))
44
+ return { doc, refusal: "That would drop a keyframe onto another one. Nothing was moved." };
45
+ landing.add(at);
46
+ }
47
+ let next = doc;
48
+ let pending = live;
49
+ while (pending.length > 0) {
50
+ const sitting = new Set(pending.map((m) => `${trackOf(m.key)}|${m.key.frame}`));
51
+ const ready = pending.filter((m) => !sitting.has(`${trackOf(m.key)}|${m.to}`));
52
+ // A cycle (two keys swapping places) cannot be written one move at a time.
53
+ if (ready.length === 0) return { doc, refusal: "Those keyframes would swap places. Nothing was moved." };
54
+ for (const { key, to } of ready)
55
+ next =
56
+ key.layerId === null
57
+ ? moveSceneKeyframe(next, key.containerId, key.path, key.frame, to)
58
+ : moveLayerKeyframe(next, key.containerId, key.layerId, key.path, key.frame, to);
59
+ pending = pending.filter((m) => !ready.includes(m));
60
+ }
61
+ return { doc: next, refusal: null };
62
+ }
63
+
64
+ /**
65
+ * Where a stagger of `keys` by `step` sends each one.
66
+ *
67
+ * SEVERAL LAYERS: each layer's keys move together, so their spacing stays, and
68
+ * the layers are ranked by their earliest selected key. ONE LAYER: the keys
69
+ * keys are ranked by distinct frame, so keys sharing a frame move together.
70
+ * Rank i moves `i * step`.
71
+ */
72
+ export function keyStaggerMoves(keys: readonly KeyframeSel[], step: number): KeyMove[] {
73
+ const groups = new Map<string, KeyframeSel[]>();
74
+ for (const k of keys) {
75
+ const id = `${k.containerId}|${k.layerId ?? ""}`;
76
+ groups.set(id, [...(groups.get(id) ?? []), k]);
77
+ }
78
+ if (groups.size > 1) {
79
+ const earliest = (g: readonly KeyframeSel[]): number => Math.min(...g.map((k) => k.frame));
80
+ const ranked = [...groups.values()].sort((a, b) => earliest(a) - earliest(b));
81
+ return ranked.flatMap((g, i) => g.map((key) => ({ key, to: key.frame + i * step })));
82
+ }
83
+ // Rank distinct frames, not keys: keys on one frame (a pose across tracks)
84
+ // stay together.
85
+ const frames = [...new Set(keys.map((k) => k.frame))].sort((a, b) => a - b);
86
+ return [...keys]
87
+ .sort((a, b) => a.frame - b.frame)
88
+ .map((key) => ({ key, to: key.frame + frames.indexOf(key.frame) * step }));
89
+ }
90
+
91
+ /** What a stretch of the selection does: every key's move, and the factor it
92
+ * landed on (the chip's readout). */
93
+ export interface KeyStretch {
94
+ moves: KeyMove[];
95
+ factor: number;
96
+ }
97
+
98
+ /**
99
+ * Where a STRETCH of `keys` sends each one: the selection's frames scaled
100
+ * about its far end, so the edge grabbed moves by `deltaFrames` and the other
101
+ * stays put. Graph mode's retime bracket, over keys on any tracks and layers.
102
+ *
103
+ * In the dock's frames (a Shot's keys plus its start, `offsetOf`), so keys in
104
+ * two Shots stretch about one anchor. The factor stops where two keys of one
105
+ * track would round onto one frame (`retimeMinFactor`) and where a key would
106
+ * cross its container's frame 0; null when even that factor collides, or the
107
+ * selection spans one frame.
108
+ */
109
+ export function keyStretchMoves(
110
+ keys: readonly KeyframeSel[],
111
+ edge: "left" | "right",
112
+ deltaFrames: number,
113
+ offsetOf: (containerId: string) => number,
114
+ ): KeyStretch | null {
115
+ const placed = keys.map((key) => ({ key, at: key.frame + offsetOf(key.containerId) }));
116
+ if (placed.length < 2) return null;
117
+ const min = Math.min(...placed.map((p) => p.at));
118
+ const max = Math.max(...placed.map((p) => p.at));
119
+ if (min === max) return null;
120
+ const anchor = edge === "left" ? max : min;
121
+ const grab = edge === "left" ? min : max;
122
+ const byTrack = new Map<string, typeof placed>();
123
+ for (const p of placed) byTrack.set(trackOf(p.key), [...(byTrack.get(trackOf(p.key)) ?? []), p]);
124
+ const tracks = [...byTrack.values()];
125
+ let factor = Math.max(retimeMinFactor(tracks.map((t) => t.map((p) => p.at))), (grab + deltaFrames - anchor) / (grab - anchor));
126
+ // No key before its own container's frame 0: only keys below the anchor can.
127
+ for (const p of placed) {
128
+ if (p.at < anchor) factor = Math.min(factor, (anchor - offsetOf(p.key.containerId)) / (anchor - p.at));
129
+ }
130
+ const landed = retimeLandings(tracks.map((t) => t.map((p) => p.at)), anchor, factor);
131
+ if (landed === null) return null;
132
+ const moves = tracks.flatMap((t, i) => t.map((p, j) => ({ key: p.key, to: landed[i]![j]! - offsetOf(p.key.containerId) })));
133
+ return { moves, factor };
134
+ }
@@ -0,0 +1,177 @@
1
+ // The keyframe clipboard: a selection out, the same motion back in somewhere
2
+ // else.
3
+ //
4
+ // ── WHAT A CLIP IS, AND WHY IT IS RELATIVE ────────────────────────────────
5
+ //
6
+ // Copying keys and pasting them at the playhead is the single most common way
7
+ // an author reuses motion — the same punch on a second card, the same settle on
8
+ // a whole row of them. So a clip stores frames RELATIVE to the earliest key in
9
+ // the whole clip, never absolute ones: paste puts that earliest key on the
10
+ // playhead and everything else keeps its spacing, which is what makes a
11
+ // two-track clip (a position and an opacity that were meant to land together)
12
+ // arrive still in sync.
13
+ //
14
+ // The anchor is clip-wide rather than per track for exactly that reason. Per
15
+ // track, every track's first key would land on the playhead and a deliberate
16
+ // offset between two of them would be flattened out.
17
+ //
18
+ // ── IN MEMORY, NOT ON THE SYSTEM CLIPBOARD ────────────────────────────────
19
+ //
20
+ // `graphClipboard` encodes to text because a material graph is worth carrying
21
+ // between browser tabs. Keyframes are not: they are meaningless without the
22
+ // track kinds and paths they came from, and the realistic gesture is "copy this
23
+ // bounce onto that layer" inside one session. A store field is the whole
24
+ // transport. The codec is still split out here so the upgrade path is a
25
+ // `JSON.stringify` and a format tag, exactly as `MATERIAL_CLIP_FORMAT` does.
26
+ import type { Ease, TrackKind, Value, Vec2, Vec3 } from "@dailies-studio/engine";
27
+ import type { Doc3D } from "@dailies-studio/shot/doc";
28
+ import { homeOf } from "@dailies-studio/shot/doc";
29
+ import type { KeyframeSel } from "./types.js";
30
+
31
+ /** One track's worth of copied keys, with the path and kind they came from. */
32
+ export interface KeyframeClipTrack {
33
+ path: string;
34
+ /** The SOURCE track's kind. Paste refuses a target whose kind differs — see
35
+ * `pasteTargets`. */
36
+ kind: TrackKind;
37
+ /** The source track was a bezier path, so its keys' handles (offsets from
38
+ * the key's value, no rebasing needed) say exactly what the source said:
39
+ * an absent side is a cleared side. */
40
+ bezier?: true;
41
+ keys: readonly ClipKey[];
42
+ }
43
+
44
+ /** One copied key. The handles are present only when the source track is a
45
+ * bezier path and the key has them. */
46
+ export interface ClipKey {
47
+ relFrame: number;
48
+ value: Value;
49
+ easeOut: Ease;
50
+ spatialIn?: Vec2 | Vec3;
51
+ spatialOut?: Vec2 | Vec3;
52
+ }
53
+
54
+ /** A clip key without its frame: what is held per (track, frame). */
55
+ type HeldKey = Omit<ClipKey, "relFrame">;
56
+
57
+ export interface KeyframeClip {
58
+ version: 1;
59
+ tracks: readonly KeyframeClipTrack[];
60
+ }
61
+
62
+ /**
63
+ * Read a selection out of the document into a clip, or null when it holds
64
+ * nothing copyable.
65
+ *
66
+ * Grouped by (container, layer, path) because that is a TRACK, and a clip's
67
+ * unit is a track: two keys on `transform.position` are one row of the clip,
68
+ * and the same frame on `transform.opacity` is another.
69
+ */
70
+ export function copyKeyframes(doc: Doc3D, selection: readonly KeyframeSel[]): KeyframeClip | null {
71
+ const byTrack = new Map<string, { path: string; kind: TrackKind; bezier: boolean; frames: number[] }>();
72
+ const valueAt = new Map<string, HeldKey>();
73
+
74
+ for (const at of selection) {
75
+ const tracks =
76
+ at.layerId === null
77
+ ? doc.scenesById[at.containerId]?.tracks
78
+ : homeOf(doc, at.containerId)?.layersById[at.layerId]?.tracks;
79
+ const track = tracks?.[at.path];
80
+ const key = track?.keyframes.find((k) => k.frame === at.frame);
81
+ if (track === undefined || key === undefined) continue;
82
+ const id = `${at.containerId}|${at.layerId ?? ""}|${at.path}`;
83
+ const row = byTrack.get(id) ?? {
84
+ path: at.path,
85
+ kind: track.kind,
86
+ bezier: track.spatialInterp?.type === "bezier",
87
+ frames: [],
88
+ };
89
+ row.frames.push(at.frame);
90
+ byTrack.set(id, row);
91
+ valueAt.set(`${id}|${at.frame}`, {
92
+ value: key.value,
93
+ easeOut: key.easeOut,
94
+ ...(row.bezier && key.spatialIn !== undefined ? { spatialIn: [...key.spatialIn] as Vec2 | Vec3 } : {}),
95
+ ...(row.bezier && key.spatialOut !== undefined ? { spatialOut: [...key.spatialOut] as Vec2 | Vec3 } : {}),
96
+ });
97
+ }
98
+ if (byTrack.size === 0) return null;
99
+
100
+ // THE ANCHOR IS THE WHOLE CLIP'S EARLIEST FRAME — see the header.
101
+ let anchor = Number.POSITIVE_INFINITY;
102
+ for (const row of byTrack.values()) for (const f of row.frames) anchor = Math.min(anchor, f);
103
+
104
+ const tracks: KeyframeClipTrack[] = [];
105
+ for (const [id, row] of byTrack) {
106
+ const keys = row.frames
107
+ .slice()
108
+ .sort((a, b) => a - b)
109
+ .flatMap((frame) => {
110
+ const held = valueAt.get(`${id}|${frame}`);
111
+ return held === undefined ? [] : [{ relFrame: frame - anchor, ...held }];
112
+ });
113
+ if (keys.length > 0) tracks.push({ path: row.path, kind: row.kind, ...(row.bezier ? { bezier: true as const } : {}), keys });
114
+ }
115
+ return tracks.length === 0 ? null : { version: 1, tracks };
116
+ }
117
+
118
+ /** One track of a clip, aimed at a subject, with the frames it will write. */
119
+ export interface PasteTarget {
120
+ path: string;
121
+ /** Copied off a bezier path: see `KeyframeClipTrack.bezier`. */
122
+ bezier?: true;
123
+ keys: readonly ({ frame: number } & HeldKey)[];
124
+ }
125
+
126
+ /**
127
+ * Where a clip lands on `subject`, and what it cannot.
128
+ *
129
+ * ── KIND EQUALITY, THEN THE EXISTING GATES ───────────────────────────────
130
+ *
131
+ * A track's kind must match EXACTLY. Anything looser is a guess about what the
132
+ * author meant — a `vec3` position pasted onto a `vec2` size is arithmetic
133
+ * nobody asked for — and the value rules that police the rest (`colour must be
134
+ * #hex`, an enum's options, a number's range) already live in
135
+ * `trackValueRefusal`, which every write goes through. So this checks the one
136
+ * thing those cannot, and hands the rest over.
137
+ *
138
+ * A path the subject does not track yet is a target too, so a bounce copied
139
+ * from one card pastes onto a card that has never been animated. Whether the
140
+ * subject can hold that path is the trackable-path gate's question
141
+ * (`keyframeRefusal`, `sceneTrackRefusal`), which the write asks and the paste
142
+ * reports.
143
+ */
144
+ export function pasteTargets(
145
+ doc: Doc3D,
146
+ clip: KeyframeClip,
147
+ subject: { containerId: string; layerId: string | null },
148
+ atFrame: number,
149
+ ): { targets: PasteTarget[]; refusals: string[] } {
150
+ const tracks =
151
+ subject.layerId === null
152
+ ? doc.scenesById[subject.containerId]?.tracks
153
+ : homeOf(doc, subject.containerId)?.layersById[subject.layerId]?.tracks;
154
+ const targets: PasteTarget[] = [];
155
+ const refusals: string[] = [];
156
+
157
+ for (const row of clip.tracks) {
158
+ const existing = tracks?.[row.path];
159
+ if (existing !== undefined && existing.kind !== row.kind) {
160
+ refusals.push(
161
+ `"${row.path}" is a ${existing.kind} here and the copied keys are ${row.kind}, so they cannot land on it.`,
162
+ );
163
+ continue;
164
+ }
165
+ targets.push({
166
+ path: row.path,
167
+ ...(row.bezier === true ? { bezier: true as const } : {}),
168
+ // Integer frames, and never below zero: a clip anchored later than the
169
+ // playhead would otherwise ask for a negative frame the validator refuses.
170
+ keys: row.keys.map(({ relFrame, ...held }) => ({
171
+ frame: Math.max(0, Math.trunc(atFrame + relFrame)),
172
+ ...held,
173
+ })),
174
+ });
175
+ }
176
+ return { targets, refusals };
177
+ }