@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,242 @@
1
+ // The dashboard's debounced poster attempt after a save.
2
+ //
3
+ // ── WHY THE EDITOR AND NOT THE SERVER ────────────────────────────────────
4
+ //
5
+ // The hosted lane has no server renderer, so the stage is the only thing that
6
+ // can draw a poster. It captures the poster frame exactly as a Tier-1 export
7
+ // captures a frame (export framing, then the in-draw snapshot), because the
8
+ // GL framebuffer is unpreserved and a late read of it can be black.
9
+ //
10
+ // The trade is named: for that one capture the stage shows export framing,
11
+ // the same swap an in-browser export makes. So the poster never holds the
12
+ // stage against the author: the stage answers null while they are playing,
13
+ // exporting, preparing Play or inside a component, and anything they start on
14
+ // it mid-capture (Play, an export, a scrub, an edit, a press) aborts the
15
+ // capture, which also answers null (`capturePoster`, bridgeClient.ts).
16
+ //
17
+ // Locally this stays inert: the CLI renders every card's poster itself
18
+ // through its pinned Chromium (`posterStore.ts`), so an upload from here would
19
+ // be a second writer for the same picture.
20
+ //
21
+ // ── A POSTER IS OWED UNTIL IT LANDS ──────────────────────────────────────
22
+ //
23
+ // A save makes the open Project's poster owed. Null means "not now", so the
24
+ // owed poster is asked for again a settle later, for as long as that Project
25
+ // stays open; a stage whose capture failed for a document answers null for it
26
+ // at once until the document changes, so asking again costs nothing. A hidden
27
+ // tab presents nothing, so the ask waits for the tab to come back. And the
28
+ // menu's ways out of the editor run an owed poster before they leave
29
+ // (`flushPoster`), because the save that just landed is usually the last one.
30
+ //
31
+ // ── EVERY FAILURE IS SILENT ──────────────────────────────────────────────
32
+ //
33
+ // A thumbnail is decoration. Nothing in here may surface an error or delay a
34
+ // save: the fallback is a gradient the dashboard draws anyway.
35
+ import type { ShotStageBridge } from "../bridgeTypes.js";
36
+
37
+ /**
38
+ * Exactly what this module reads from the store, and nothing else.
39
+ *
40
+ * Structural rather than `Pick<ShotEditorStore, …>` on purpose: the state has
41
+ * ~58 fields, and depending on the whole shape would mean a test double has to
42
+ * fabricate all of them to stand in for the two that matter.
43
+ */
44
+ export interface PosterStore {
45
+ getState(): { saveState: string; name: string | null };
46
+ /** The COLD lane: a save and a name are never a transport tick. */
47
+ subscribeCold(listener: () => void): () => void;
48
+ stage(): ShotStageBridge | null;
49
+ }
50
+
51
+ /** Wide enough to look sharp on a card, small enough that a JPEG of it is
52
+ * tens of KB. The Worker caps the upload at 512KB regardless. */
53
+ const POSTER_WIDTH = 640;
54
+
55
+ /**
56
+ * How long after a save to attempt the poster request, and how long to wait
57
+ * before asking again when the stage answered "not now".
58
+ *
59
+ * Long enough that a burst of autosaves during an edit produces ONE request.
60
+ * This delay is only debounce policy. Pixel readiness belongs to the stage's
61
+ * exact presentation latch and must never be inferred from elapsed time.
62
+ */
63
+ const SETTLE_MS = 10_000;
64
+
65
+ /** The most a way out of the editor waits for an owed poster: a warm capture
66
+ * and its upload fit well inside it. A cold one is abandoned with the page
67
+ * rather than holding the author at the door. */
68
+ const POSTER_FLUSH_MS = 5_000;
69
+
70
+ /**
71
+ * Only in the hosted lane.
72
+ *
73
+ * The editor host stamps this marker into `/scene` (browser-host's
74
+ * `stampHostBoot`, which the Worker's `editorPage` calls). Locally the CLI
75
+ * renders every card's poster itself and has no route to accept one, so the
76
+ * whole feature stays inert rather than 404ing on every save.
77
+ */
78
+ export function isCloudWorkspace(doc: Document = document): boolean {
79
+ return doc.querySelector('meta[name="dailies-workspace"][content="cloud"]') !== null;
80
+ }
81
+
82
+ export interface PosterCaptureDeps {
83
+ store: PosterStore;
84
+ /** Injected so tests can drive the timing without a real clock. */
85
+ setTimeout?: (fn: () => void, ms: number) => unknown;
86
+ clearTimeout?: (handle: unknown) => void;
87
+ fetch?: typeof fetch;
88
+ enabled?: boolean;
89
+ /** Whether the page is hidden: nothing presents there, so no capture can. */
90
+ hidden?: () => boolean;
91
+ /** Subscribe to the page's visibility changing; returns the unsubscribe. */
92
+ onVisibilityChange?: (listener: () => void) => () => void;
93
+ }
94
+
95
+ /** The running capture's flush, for `flushPoster`. One editor per page. */
96
+ let flushRunning: (() => Promise<void>) | null = null;
97
+
98
+ /**
99
+ * Run an owed poster now and wait for its upload, for a page about to leave
100
+ * the editor. Resolves (never rejects) within `POSTER_FLUSH_MS` whatever the
101
+ * stage does, and at once when nothing is owed or the feature is inert.
102
+ */
103
+ export function flushPoster(): Promise<void> {
104
+ return flushRunning?.() ?? Promise.resolve();
105
+ }
106
+
107
+ /**
108
+ * Watch for saves and upload a poster after things settle. Returns the
109
+ * unsubscribe.
110
+ */
111
+ export function startPosterCapture(deps: PosterCaptureDeps): () => void {
112
+ const enabled = deps.enabled ?? isCloudWorkspace();
113
+ if (!enabled) return () => {};
114
+
115
+ const later = deps.setTimeout ?? ((fn, ms) => globalThis.setTimeout(fn, ms));
116
+ const cancel = deps.clearTimeout ?? ((h) => globalThis.clearTimeout(h as never));
117
+ const send = deps.fetch ?? globalThis.fetch.bind(globalThis);
118
+ // "hidden" only: jsdom and prerendered pages report other states, and a
119
+ // poster that waited for "visible" there would never be asked for.
120
+ const hidden = deps.hidden ?? (() => typeof document !== "undefined" && document.visibilityState === "hidden");
121
+ const onVisibilityChange =
122
+ deps.onVisibilityChange ??
123
+ ((listener: () => void) => {
124
+ if (typeof document === "undefined") return () => {};
125
+ document.addEventListener("visibilitychange", listener);
126
+ return () => document.removeEventListener("visibilitychange", listener);
127
+ });
128
+
129
+ let timer: unknown = null;
130
+ let lastSaveState = deps.store.getState().saveState;
131
+ /** The Project whose poster is owed, or null. */
132
+ let owed: string | null = null;
133
+ /** Counts saves, so a capture that finishes after a newer save does not
134
+ * settle the debt that save made. */
135
+ let saves = 0;
136
+ let inFlight: Promise<void> | null = null;
137
+
138
+ function arm(): void {
139
+ if (timer !== null) cancel(timer);
140
+ timer = later(() => {
141
+ timer = null;
142
+ void attempt();
143
+ }, SETTLE_MS);
144
+ }
145
+
146
+ function attempt(): Promise<void> {
147
+ if (inFlight !== null) return inFlight;
148
+ const name = owed;
149
+ if (name === null) return Promise.resolve();
150
+ // Another Project opened: its own saves owe its own poster.
151
+ if (deps.store.getState().name !== name) {
152
+ owed = null;
153
+ return Promise.resolve();
154
+ }
155
+ // Asked again when the tab comes back.
156
+ if (hidden()) return Promise.resolve();
157
+ inFlight = capture(name, saves).finally(() => {
158
+ inFlight = null;
159
+ });
160
+ return inFlight;
161
+ }
162
+
163
+ async function capture(name: string, served: number): Promise<void> {
164
+ const bridge = deps.store.stage();
165
+ // A stage still booting: ask again later.
166
+ if (bridge === null) {
167
+ arm();
168
+ return;
169
+ }
170
+ // A v12 stage under a v13 chrome cannot answer. The connect-time version
171
+ // gate should have caught it; this keeps a stale build from throwing on
172
+ // every save if it ever does not.
173
+ if (typeof bridge.capturePoster !== "function") {
174
+ owed = null;
175
+ return;
176
+ }
177
+ const blob = await bridge.capturePoster(POSTER_WIDTH).catch(() => null);
178
+ // The stage draws the OPEN project. Another one opened during the
179
+ // capture would put its picture on this card.
180
+ if (deps.store.getState().name !== name) {
181
+ if (owed === name) owed = null;
182
+ return;
183
+ }
184
+ if (blob === null) {
185
+ // Not now (playing, exporting, a capture that stepped aside): owed
186
+ // still, and asked again once things settle.
187
+ if (owed === name) arm();
188
+ return;
189
+ }
190
+ if (saves === served) owed = null;
191
+ try {
192
+ await send(`/api/posters/shot/${encodeURIComponent(name)}`, {
193
+ method: "PUT",
194
+ headers: { "content-type": "image/jpeg" },
195
+ body: blob,
196
+ });
197
+ } catch {
198
+ // Offline, or the route is not there. The card keeps its existing poster
199
+ // when it has one, otherwise it keeps the deterministic gradient.
200
+ }
201
+ }
202
+
203
+ const unsubscribe = deps.store.subscribeCold(() => {
204
+ const { saveState, name } = deps.store.getState();
205
+ const justSaved = saveState === "saved" && lastSaveState !== "saved";
206
+ lastSaveState = saveState;
207
+ if (!justSaved || name === null) return;
208
+ owed = name;
209
+ saves += 1;
210
+ arm();
211
+ });
212
+
213
+ const unwatchVisibility = onVisibilityChange(() => {
214
+ if (owed !== null && timer === null && inFlight === null && !hidden()) arm();
215
+ });
216
+
217
+ const flush = async (): Promise<void> => {
218
+ if (owed === null && inFlight === null) return;
219
+ if (timer !== null) {
220
+ cancel(timer);
221
+ timer = null;
222
+ }
223
+ let giveUp: unknown = null;
224
+ await Promise.race([
225
+ attempt(),
226
+ new Promise<void>((resolve) => {
227
+ giveUp = later(resolve, POSTER_FLUSH_MS);
228
+ }),
229
+ ]);
230
+ cancel(giveUp);
231
+ };
232
+ flushRunning = flush;
233
+
234
+ return () => {
235
+ if (timer !== null) cancel(timer);
236
+ timer = null;
237
+ owed = null;
238
+ unsubscribe();
239
+ unwatchVisibility();
240
+ if (flushRunning === flush) flushRunning = null;
241
+ };
242
+ }
@@ -0,0 +1,254 @@
1
+ // WHO ELSE IS HERE (collaboration lane R7, Paper page 21).
2
+ //
3
+ // A store of its own, like the comments': presence never enters undo, the
4
+ // save or the document. It lives on the live sync socket (state/sync.ts hands
5
+ // over the session's `peers`, `peer` and `peer-left`) and is gone with it, so
6
+ // there is presence only while live sync is on.
7
+ //
8
+ // ── ONE PERSON, ONE FACE ────────────────────────────────────────────────────
9
+ //
10
+ // The session tells tabs. Here they are grouped by user: one person with three
11
+ // tabs is one face, and their selection, Shot and playhead are the ones of the
12
+ // tab they touched last (the newest `at`). This tab's own user is left out,
13
+ // with its other tabs: the CLI's tabs are all user "local", so the CLI shows
14
+ // no faces at all.
15
+ //
16
+ // ── WHAT THIS TAB SAYS ──────────────────────────────────────────────────────
17
+ //
18
+ // `update` takes where this tab is (the store's selection, Shot and playhead;
19
+ // the board's cursor) and says it: a new selection or Shot at once, a moving
20
+ // playhead or cursor at most every `PRESENCE_MS` (15 Hz), with the last one
21
+ // always sent.
22
+ import { PRESENCE_SEL_MAX } from "@dailies-studio/shot/doc";
23
+ import type { SyncCursor, SyncPeerPresence, SyncPresence, SyncPresenceMessage } from "@dailies-studio/shot/doc";
24
+
25
+ /** A moving playhead or cursor is said at most this often (15 Hz). */
26
+ export const PRESENCE_MS = 67;
27
+
28
+ /** The peer palette (page 21): eight colours, none of them the selection's
29
+ * blue, the warning amber, the error coral or the Agent's mint. */
30
+ export const PEER_COLORS = [
31
+ "#8A6CFF",
32
+ "#FF7A3D",
33
+ "#2EC4B6",
34
+ "#D86CFF",
35
+ "#A8D84A",
36
+ "#FF7AC6",
37
+ "#5AD2F0",
38
+ "#E8D44D",
39
+ ] as const;
40
+
41
+ /** The Agent's colour, only ever the Agent's. */
42
+ export const AGENT_COLOR = "#34D399";
43
+
44
+ /** A person's peer colour, stable for their user id. */
45
+ export function peerColor(userId: string): string {
46
+ let hash = 0;
47
+ for (let i = 0; i < userId.length; i++) hash = (hash * 31 + userId.charCodeAt(i)) | 0;
48
+ return PEER_COLORS[Math.abs(hash) % PEER_COLORS.length]!;
49
+ }
50
+
51
+ export interface PresenceName {
52
+ name: string;
53
+ avatarUrl?: string | null;
54
+ }
55
+
56
+ /** One face: a person, however many tabs they have open. */
57
+ export interface PresencePerson {
58
+ user: string;
59
+ name: string;
60
+ color: string;
61
+ avatarUrl: string | null;
62
+ /** "edit" when any of their tabs may edit. */
63
+ role: "edit" | "view";
64
+ tabs: number;
65
+ /** From the tab they touched last. */
66
+ sel: readonly string[];
67
+ scene: string | null;
68
+ frame: number | null;
69
+ cursor: SyncCursor | null;
70
+ }
71
+
72
+ export interface PresenceState {
73
+ /** The session told us who is here (joined, and the socket is up). */
74
+ live: boolean;
75
+ /** This tab's user, from the session. */
76
+ self: string | null;
77
+ /** Every other tab, as the session told them. */
78
+ tabs: readonly SyncPeerPresence[];
79
+ /** The faces, in the order they arrived. */
80
+ people: readonly PresencePerson[];
81
+ names: Readonly<Record<string, PresenceName>>;
82
+ /** The user this tab follows, or null. */
83
+ following: string | null;
84
+ /** Why a follow ended on its own ("Maya left the Shot"), until dismissed. */
85
+ notice: string | null;
86
+ }
87
+
88
+ /** This tab's own place. `null` clears a field. */
89
+ export interface PresenceLocal {
90
+ sel?: readonly string[];
91
+ scene?: string | null;
92
+ frame?: number | null;
93
+ cursor?: SyncCursor | null;
94
+ }
95
+
96
+ export interface PresenceStore {
97
+ getState(): PresenceState;
98
+ subscribe(listener: () => void): () => void;
99
+ /** Where presence goes out (the sync client's `presence`), or null with no
100
+ * live sync. */
101
+ attach(send: ((p: SyncPresence) => void) | null): void;
102
+ /** A session message, or null when the socket went. */
103
+ receive(msg: SyncPresenceMessage | null): void;
104
+ /** Names learnt (the Project's people route, comment authors). */
105
+ learn(names: Readonly<Record<string, PresenceName>>): void;
106
+ update(local: PresenceLocal): void;
107
+ /** Follow a person, or stop (`null`). `notice` says why it stopped. */
108
+ follow(user: string | null, notice?: string): void;
109
+ dismissNotice(): void;
110
+ }
111
+
112
+ const EMPTY: PresenceState = {
113
+ live: false,
114
+ self: null,
115
+ tabs: [],
116
+ people: [],
117
+ names: {},
118
+ following: null,
119
+ notice: null,
120
+ };
121
+
122
+ /** The faces for a list of tabs: grouped by user, without `self`. */
123
+ export function groupPeople(
124
+ tabs: readonly SyncPeerPresence[],
125
+ self: string | null,
126
+ names: Readonly<Record<string, PresenceName>>,
127
+ ): PresencePerson[] {
128
+ const by = new Map<string, PresencePerson & { at: number }>();
129
+ for (const t of tabs) {
130
+ if (t.user === self) continue;
131
+ const seen = by.get(t.user);
132
+ const where = { at: t.at, sel: t.sel ?? [], scene: t.scene ?? null, frame: t.frame ?? null, cursor: t.cursor ?? null };
133
+ if (seen === undefined) {
134
+ by.set(t.user, {
135
+ user: t.user,
136
+ name: names[t.user]?.name ?? "Someone",
137
+ color: peerColor(t.user),
138
+ avatarUrl: names[t.user]?.avatarUrl ?? null,
139
+ role: t.role,
140
+ tabs: 1,
141
+ ...where,
142
+ });
143
+ continue;
144
+ }
145
+ seen.tabs++;
146
+ if (t.role === "edit") seen.role = "edit";
147
+ if (t.at >= seen.at) Object.assign(seen, where);
148
+ }
149
+ return [...by.values()].map(({ at: _at, ...person }) => person);
150
+ }
151
+
152
+ const sameIds = (a: readonly string[] | undefined, b: readonly string[] | undefined): boolean =>
153
+ (a ?? []).length === (b ?? []).length && (a ?? []).every((id, i) => id === b![i]);
154
+
155
+ export function createPresenceStore(deps: { now?: () => number } = {}): PresenceStore {
156
+ const now = deps.now ?? (() => Date.now());
157
+ let state = EMPTY;
158
+ const listeners = new Set<() => void>();
159
+ let send: ((p: SyncPresence) => void) | null = null;
160
+ /** This tab's place, as last said and as now. */
161
+ let mine: SyncPresence = {};
162
+ let said: SyncPresence | null = null;
163
+ let saidAt = -Infinity;
164
+ let timer: ReturnType<typeof setTimeout> | null = null;
165
+
166
+ function set(patch: Partial<PresenceState>): void {
167
+ const before = state.people;
168
+ state = { ...state, ...patch };
169
+ if (patch.tabs !== undefined || patch.self !== undefined || patch.names !== undefined) {
170
+ state = { ...state, people: groupPeople(state.tabs, state.self, state.names) };
171
+ }
172
+ // A follow ends when its person leaves.
173
+ if (state.following !== null && !state.people.some((p) => p.user === state.following)) {
174
+ const gone = before.find((p) => p.user === state.following)?.name ?? "They";
175
+ state = { ...state, following: null, notice: `${gone} left. Follow ended.` };
176
+ }
177
+ for (const listener of listeners) listener();
178
+ }
179
+
180
+ function flush(): void {
181
+ if (timer !== null) clearTimeout(timer);
182
+ timer = null;
183
+ said = mine;
184
+ saidAt = now();
185
+ send?.(mine);
186
+ }
187
+
188
+ return {
189
+ getState: () => state,
190
+ subscribe(listener) {
191
+ listeners.add(listener);
192
+ return () => listeners.delete(listener);
193
+ },
194
+ attach(next) {
195
+ send = next;
196
+ said = null;
197
+ if (next !== null) flush();
198
+ else if (timer !== null) {
199
+ clearTimeout(timer);
200
+ timer = null;
201
+ }
202
+ },
203
+ receive(msg) {
204
+ if (msg === null) {
205
+ if (state.live || state.tabs.length > 0) set({ live: false, tabs: [], following: null });
206
+ return;
207
+ }
208
+ if (msg.t === "peers") set({ live: true, self: msg.user, tabs: msg.peers });
209
+ else if (msg.t === "peer") {
210
+ const at = state.tabs.findIndex((t) => t.id === msg.peer.id);
211
+ set({ tabs: at < 0 ? [...state.tabs, msg.peer] : state.tabs.map((t, i) => (i === at ? msg.peer : t)) });
212
+ } else set({ tabs: state.tabs.filter((t) => t.id !== msg.id) });
213
+ },
214
+ learn(names) {
215
+ set({ names: { ...state.names, ...names } });
216
+ },
217
+ update(local) {
218
+ const next: SyncPresence = { ...mine };
219
+ for (const key of ["sel", "scene", "frame", "cursor"] as const) {
220
+ if (!(key in local)) continue;
221
+ const v = local[key];
222
+ if (v === null || v === undefined || (key === "sel" && (v as readonly string[]).length === 0)) delete next[key];
223
+ else if (key === "sel") next.sel = (v as readonly string[]).slice(0, PRESENCE_SEL_MAX);
224
+ else (next as Record<string, unknown>)[key] = v;
225
+ }
226
+ const prev = mine;
227
+ mine = next;
228
+ if (send === null) return;
229
+ if (
230
+ sameIds(prev.sel, next.sel) &&
231
+ prev.scene === next.scene &&
232
+ prev.frame === next.frame &&
233
+ prev.cursor?.x === next.cursor?.x &&
234
+ prev.cursor?.y === next.cursor?.y &&
235
+ prev.cursor?.space === next.cursor?.space &&
236
+ prev.cursor?.where === next.cursor?.where
237
+ ) {
238
+ return;
239
+ }
240
+ // A new selection or Shot, or a cursor leaving, goes at once.
241
+ const urgent =
242
+ said === null || !sameIds(said.sel, next.sel) || said.scene !== next.scene || (said.cursor !== undefined && next.cursor === undefined);
243
+ const wait = saidAt + PRESENCE_MS - now();
244
+ if (urgent || wait <= 0) return flush();
245
+ timer ??= setTimeout(flush, wait);
246
+ },
247
+ follow(user, notice) {
248
+ set({ following: user, notice: notice ?? null });
249
+ },
250
+ dismissNotice() {
251
+ if (state.notice !== null) set({ notice: null });
252
+ },
253
+ };
254
+ }
@@ -0,0 +1,160 @@
1
+ // WHICH COMPONENTS BELONG TO THIS PROJECT.
2
+ //
3
+ // Nothing on the wire answers this. A workspace is scanned whole (`src/**/*.tsx`
4
+ // from the root) and a registry row carries `id`, `rel`, `name`, `exportName`,
5
+ // `host` and the studio-versus-user `source`. There is no project id, no owner
6
+ // and no document name on it. In a workspace holding forty films that means one
7
+ // film's board offered the other thirty-nine films' components, plus the four
8
+ // foreign workspaces a marketplace claim copies into `src/market/`.
9
+ //
10
+ // So the group is DERIVED, from the facts the document itself holds:
11
+ //
12
+ // 1. the components it already places or cites (exact, and the seed),
13
+ // 2. the code files made for it (`componentFiles`, exact, and never a seed),
14
+ // and
15
+ // 3. the folders the placed and cited sit in (convention, and what makes a
16
+ // component written for this film appear without being placed first).
17
+ //
18
+ // A file made for this Project never folds its folder in: agents pick the
19
+ // folder (`src/components/` is a common one), so a member's folder is shared
20
+ // with other Projects' members far more often than a placed component's is.
21
+ // And with `homes` (the workspace's index of which Project lists or uses each
22
+ // file) a folder-mate another Project holds is left out of the fold too.
23
+ //
24
+ // The fold is the half that needs guarding. `src/Foo.tsx` sits at the source
25
+ // root, so folding on its directory would hand back the whole tree; and one film
26
+ // placed inside another is a real thing, so folding on the generated directory
27
+ // would hand back every film. Both are excluded by name, and this Project's own
28
+ // generated film is added back by an exact match on `doc.name`.
29
+ //
30
+ // A Project that has used nothing gets an EMPTY group, deliberately: the rail's
31
+ // search reaches the whole registry, and answering "everything" would be the
32
+ // same wrong list this module exists to stop.
33
+ import { GENERATED_SHOTS_DIR, componentFilesOf, usedInstanceComponentsOf } from "@dailies-studio/shot/doc";
34
+ import type { Doc3D, UsedInstanceComponent } from "@dailies-studio/shot/doc";
35
+ import { isProjectComponent } from "@dailies-studio/ui";
36
+ import type { ComponentFileHome, ComponentRow } from "../api.js";
37
+ import { storyboardComponentsOf } from "./storyboardModel.js";
38
+
39
+ /**
40
+ * The registry components a document PLACES plus the ones its storyboard
41
+ * CITES, so "components are shared by both views" is one list. A board-only
42
+ * entry has no layers: its row is a name and nothing to select.
43
+ *
44
+ * Lifted out of `FilePanel` so the files panel and the storyboard rail cannot
45
+ * answer "what does this Project use" two different ways.
46
+ */
47
+ export function usedComponentsOf(doc: Doc3D): UsedInstanceComponent[] {
48
+ const placed = usedInstanceComponentsOf(doc);
49
+ const seen = new Set(placed.map((used) => used.component));
50
+ const extra = storyboardComponentsOf(doc)
51
+ .filter((row) => !seen.has(row.component))
52
+ .map((row): UsedInstanceComponent => ({ component: row.component, layerIds: [] }));
53
+ return extra.length === 0 ? placed : [...placed, ...extra];
54
+ }
55
+
56
+ /** The directory part of a posix path, or "" for a bare file name. */
57
+ function dirOf(rel: string): string {
58
+ const cut = rel.lastIndexOf("/");
59
+ return cut === -1 ? "" : rel.slice(0, cut);
60
+ }
61
+
62
+ /**
63
+ * Folders the fold may never claim.
64
+ *
65
+ * The source root, because a component at the top of `src/` would otherwise
66
+ * fold in every project file in the workspace. The generated directory,
67
+ * because a film placed inside another film is an ordinary instance and would
68
+ * otherwise fold in every other film. Both are single directories rather than
69
+ * a prefix test: `src/cero` is fine, `src` is not.
70
+ */
71
+ function foldable(dir: string): boolean {
72
+ return dir !== "" && dir !== "src" && dir !== GENERATED_SHOTS_DIR;
73
+ }
74
+
75
+ export interface ProjectComponents {
76
+ /** This Project's own: what it uses, the files made for it, the folders of
77
+ * what it uses, and its generated film. */
78
+ project: ComponentRow[];
79
+ /** Shipped builtins and vendored shared components. */
80
+ library: ComponentRow[];
81
+ /** Other Projects' generated films, which are build products, not source. */
82
+ films: ComponentRow[];
83
+ /** Every row, in registry order — what the rail's search runs over. */
84
+ all: ComponentRow[];
85
+ }
86
+
87
+ const EMPTY: ProjectComponents = { project: [], library: [], films: [], all: [] };
88
+
89
+ /**
90
+ * Split the registry three ways for one open document.
91
+ *
92
+ * Order is the registry's own throughout, so a row never moves because of
93
+ * which group it landed in. With no document open every group is empty rather
94
+ * than a copy of the registry: "no Project" is not "every component".
95
+ */
96
+ export function projectComponents(
97
+ doc: Doc3D | null,
98
+ library: readonly ComponentRow[],
99
+ /** The workspace's `homes` (`componentHomes`), when read: a folder-mate
100
+ * another Project lists or uses is that Project's, not this one's. */
101
+ homes: readonly ComponentFileHome[] | null = null,
102
+ ): ProjectComponents {
103
+ if (doc === null) return { ...EMPTY, all: [...library] };
104
+
105
+ const used = new Set(usedComponentsOf(doc).map((row) => row.component));
106
+ const members = new Set(componentFilesOf(doc).filter((path) => !path.startsWith(`${GENERATED_SHOTS_DIR}/`)));
107
+ const elsewhere = new Set((homes ?? []).filter((h) => h.projectId !== doc.id).map((h) => h.path));
108
+ const byId = new Map(library.map((row) => [row.id, row]));
109
+
110
+ // Only a PROJECT row's folder folds. A builtin's `rel` is a package
111
+ // specifier (`@dailies-studio/particles`), which has no directory to speak of.
112
+ const folders = new Set<string>();
113
+ for (const id of used) {
114
+ const row = byId.get(id);
115
+ if (row === undefined || !isProjectComponent(row)) continue;
116
+ const dir = dirOf(row.rel);
117
+ if (foldable(dir)) folders.add(dir);
118
+ }
119
+
120
+ const ownFilm = `${GENERATED_SHOTS_DIR}/${doc.name}.tsx`;
121
+ const inFolder = (rel: string): boolean => {
122
+ for (const dir of folders) if (rel.startsWith(`${dir}/`)) return true;
123
+ return false;
124
+ };
125
+
126
+ const out: ProjectComponents = { project: [], library: [], films: [], all: [...library] };
127
+ for (const row of library) {
128
+ if (!isProjectComponent(row)) {
129
+ out.library.push(row);
130
+ } else if (
131
+ used.has(row.id) ||
132
+ members.has(row.rel) ||
133
+ row.rel === ownFilm ||
134
+ (inFolder(row.rel) && !elsewhere.has(row.rel))
135
+ ) {
136
+ out.project.push(row);
137
+ } else if (row.rel.startsWith(`${GENERATED_SHOTS_DIR}/`)) {
138
+ out.films.push(row);
139
+ } else {
140
+ // A project component of some OTHER film. Reachable through the rail's
141
+ // search and nowhere else, which is the whole point.
142
+ }
143
+ }
144
+ return out;
145
+ }
146
+
147
+ /**
148
+ * The folder a row sits in, for the badge the rail puts on a search hit from
149
+ * outside this Project. A builtin has none, and says so with its package name.
150
+ */
151
+ export function componentFolderOf(row: ComponentRow): string {
152
+ return isProjectComponent(row) ? (dirOf(row.rel) || row.rel) : row.rel;
153
+ }
154
+
155
+ /** Case-insensitive match over the two strings a person would type. */
156
+ export function matchesComponent(row: ComponentRow, query: string): boolean {
157
+ const q = query.trim().toLowerCase();
158
+ if (q === "") return true;
159
+ return row.name.toLowerCase().includes(q) || row.id.toLowerCase().includes(q);
160
+ }