@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,261 @@
1
+ // The editor preferences on the ACCOUNT (gap 5), hosted only.
2
+ //
3
+ // The hosted editor runs on a per-workspace origin, so its localStorage is per
4
+ // workspace and never shared with the dashboard. The account keeps one copy
5
+ // (`users.editor_prefs`, answered on `/auth/me` and written by
6
+ // `PUT /auth/prefs`), so a choice follows the person across workspaces and
7
+ // devices, and a theme picked in dashboard Settings reaches the editor.
8
+ //
9
+ // localStorage stays the boot cache: the store is born from it
10
+ // (`readEditorPrefs`), so the first paint never waits on the network, and a
11
+ // local install or an offline start behaves exactly as before.
12
+ //
13
+ // ONE CLOCK. The only stamp compared is the server's `updatedAt`, against the
14
+ // server stamp this browser last adopted or got back from a save
15
+ // (`SyncState.syncedAt`). A client clock running ahead or behind changes
16
+ // nothing.
17
+ //
18
+ // FIELD BY FIELD. A local change marks its field dirty (kept in localStorage,
19
+ // so a save that never landed is retried at the next boot), and a save sends
20
+ // ONLY the dirty fields. The server's `json_patch` merge then keeps whatever
21
+ // another surface wrote to the rest (the dashboard writes only `theme`).
22
+ //
23
+ // THE MERGE, once per page load:
24
+ // - the account's stamp differs from `syncedAt`: something else wrote since,
25
+ // so adopt every field it holds that is not dirty here;
26
+ // - dirty fields are then sent; an account that has never saved gets this
27
+ // browser's whole copy, if this browser ever saved one.
28
+ // After that, every change is one debounced PUT of the dirty fields, after
29
+ // `SAVE_DEBOUNCE_MS` of quiet: the Preferences dialog is a row of switches,
30
+ // and a burst of clicks is one write.
31
+ import { useEffect, useRef } from "react";
32
+ import { useStudioTheme } from "@dailies-studio/ui/theme";
33
+ import type { StudioThemePreference } from "@dailies-studio/ui/theme";
34
+ import { useStore } from "./context.js";
35
+ import { EDITOR_PREF_VALID, hasStoredEditorPrefs } from "./editorPrefs.js";
36
+ import { isCloudWorkspace } from "./posterCapture.js";
37
+ import type { ShotEditorStore } from "./store.js";
38
+
39
+ /** Quiet time before a change is saved to the account. */
40
+ export const SAVE_DEBOUNCE_MS = 800;
41
+
42
+ /** The sync bookkeeping, beside `EDITOR_PREFS_KEY`. */
43
+ export const ACCOUNT_SYNC_KEY = "dailies.editor.sync";
44
+
45
+ /** The fields the account keeps, as the server's closed schema names them
46
+ * (@dailies-studio/db editorPrefs.ts `EDITOR_PREF_FIELDS`). */
47
+ export interface AccountPrefs {
48
+ theme: StudioThemePreference;
49
+ showGrid: boolean;
50
+ rulers: boolean;
51
+ smartGuides: boolean;
52
+ motionPaths: boolean;
53
+ toolbarPosition: "bottom" | "top";
54
+ blockLabels: "fit" | "off";
55
+ selectMovesPlayhead: boolean;
56
+ fieldLabels: "compact" | "named";
57
+ showFpsBadge: boolean;
58
+ showBackendBadge: boolean;
59
+ }
60
+
61
+ type Field = keyof AccountPrefs;
62
+ type StoreField = Exclude<Field, "theme">;
63
+ const STORE_FIELDS: readonly StoreField[] = [
64
+ "showGrid",
65
+ "rulers",
66
+ "smartGuides",
67
+ "motionPaths",
68
+ "toolbarPosition",
69
+ "blockLabels",
70
+ "selectMovesPlayhead",
71
+ "fieldLabels",
72
+ "showFpsBadge",
73
+ "showBackendBadge",
74
+ ];
75
+ const FIELDS: readonly Field[] = ["theme", ...STORE_FIELDS];
76
+
77
+ const isTheme = (v: unknown): v is StudioThemePreference => v === "system" || v === "light" || v === "dark";
78
+ const isValid = (field: Field, v: unknown): boolean =>
79
+ field === "theme" ? isTheme(v) : EDITOR_PREF_VALID[field](v);
80
+
81
+ /** The account copy's fields, as the store and the theme hold them now. */
82
+ export function accountPrefsOf(store: ShotEditorStore, theme: StudioThemePreference): AccountPrefs {
83
+ const s = store.getState();
84
+ const out = { theme } as AccountPrefs;
85
+ for (const field of STORE_FIELDS) (out as unknown as Record<string, unknown>)[field] = s[field];
86
+ return out;
87
+ }
88
+
89
+ /** One store field through the setter the chrome already uses. */
90
+ function setStoreField(store: ShotEditorStore, field: StoreField, value: unknown): void {
91
+ switch (field) {
92
+ case "showGrid":
93
+ if (value !== store.getState().showGrid) store.toggleGrid();
94
+ return;
95
+ case "showFpsBadge":
96
+ return store.setFpsBadge(value as boolean);
97
+ case "showBackendBadge":
98
+ return store.setBackendBadge(value as boolean);
99
+ case "selectMovesPlayhead":
100
+ return store.setSelectMovesPlayhead(value as boolean);
101
+ default:
102
+ store.setEditorPref(field, value as never);
103
+ }
104
+ }
105
+
106
+ /** What this browser knows about the account copy. */
107
+ interface SyncState {
108
+ /** The server's `updatedAt` last adopted or answered to a save, 0 for never. */
109
+ syncedAt: number;
110
+ /** Fields changed here and not yet saved. */
111
+ dirty: Field[];
112
+ }
113
+
114
+ function readSync(): SyncState {
115
+ try {
116
+ const parsed: unknown = JSON.parse(localStorage.getItem(ACCOUNT_SYNC_KEY) ?? "null");
117
+ const o = (typeof parsed === "object" && parsed !== null ? parsed : {}) as Record<string, unknown>;
118
+ const syncedAt = typeof o["syncedAt"] === "number" && Number.isFinite(o["syncedAt"]) ? o["syncedAt"] : 0;
119
+ const dirty = Array.isArray(o["dirty"]) ? FIELDS.filter((f) => (o["dirty"] as unknown[]).includes(f)) : [];
120
+ return { syncedAt, dirty };
121
+ } catch {
122
+ return { syncedAt: 0, dirty: [] };
123
+ }
124
+ }
125
+
126
+ function writeSync(sync: SyncState): void {
127
+ try {
128
+ localStorage.setItem(ACCOUNT_SYNC_KEY, JSON.stringify(sync));
129
+ } catch {
130
+ /* storage disabled: the session still syncs, it just forgets between loads */
131
+ }
132
+ }
133
+
134
+ type ServerPrefs = Partial<Record<Field, unknown>> & { updatedAt?: unknown };
135
+
136
+ /** Mounted once, in App: the boot merge, then a debounced save per change. */
137
+ export function useAccountPrefs(): void {
138
+ const store = useStore();
139
+ const { preference, setPreference } = useStudioTheme();
140
+ // The theme as the sync knows it. The store subscription runs outside
141
+ // React, so it reads the theme here, not from a render.
142
+ const theme = useRef(preference);
143
+ // The change hook, set while hosted.
144
+ const changed = useRef<(() => void) | null>(null);
145
+
146
+ useEffect(() => {
147
+ if (!isCloudWorkspace()) return;
148
+ let live = true;
149
+ let ready = false;
150
+ let timer: ReturnType<typeof setTimeout> | null = null;
151
+ const sync = readSync();
152
+ const dirty = new Set<Field>(sync.dirty);
153
+ const persist = () => writeSync({ syncedAt: sync.syncedAt, dirty: [...dirty] });
154
+ // The value each field had when last sent, adopted or loaded. A field
155
+ // that moves off it is a local change.
156
+ const baseline = accountPrefsOf(store, theme.current);
157
+
158
+ /** Adopt the account's fields, except those `skip` names (changed here
159
+ * since). The baseline moves FIRST, so the adoption is not a change. */
160
+ const adopt = (saved: ServerPrefs, skip: ReadonlySet<Field>) => {
161
+ for (const field of FIELDS) {
162
+ const value = saved[field];
163
+ if (skip.has(field) || !isValid(field, value)) continue;
164
+ (baseline as unknown as Record<string, unknown>)[field] = value;
165
+ if (field === "theme") {
166
+ theme.current = value as StudioThemePreference;
167
+ setPreference(value as StudioThemePreference);
168
+ } else setStoreField(store, field, value);
169
+ }
170
+ };
171
+
172
+ const save = () => {
173
+ if (!live || dirty.size === 0) return;
174
+ const sent = accountPrefsOf(store, theme.current);
175
+ const body: Partial<AccountPrefs> = {};
176
+ for (const field of dirty) (body as Record<string, unknown>)[field] = sent[field];
177
+ // A failed save costs only the sync: the dirty marks stay in
178
+ // localStorage and the next change or boot sends them again.
179
+ void fetch("/auth/prefs", {
180
+ method: "PUT",
181
+ credentials: "same-origin",
182
+ headers: { "content-type": "application/json" },
183
+ body: JSON.stringify(body),
184
+ })
185
+ .then((res) => (res.ok ? res.json() : null))
186
+ .then((answer: { editorPrefs?: ServerPrefs | null } | null) => {
187
+ const saved = answer?.editorPrefs;
188
+ if (!live || saved == null || typeof saved.updatedAt !== "number") return;
189
+ const now = accountPrefsOf(store, theme.current);
190
+ // Clean only what is still what was sent: a change made while the
191
+ // request flew stays dirty for the next save.
192
+ for (const field of Object.keys(body) as Field[]) if (now[field] === sent[field]) dirty.delete(field);
193
+ sync.syncedAt = saved.updatedAt;
194
+ persist();
195
+ // The answer is the whole merged copy: take what another surface
196
+ // wrote meanwhile, but never over a field just sent or still dirty.
197
+ adopt(saved, new Set<Field>([...dirty, ...(Object.keys(body) as Field[])]));
198
+ })
199
+ .catch(() => {});
200
+ };
201
+
202
+ const schedule = () => {
203
+ if (timer !== null) clearTimeout(timer);
204
+ timer = setTimeout(() => {
205
+ timer = null;
206
+ save();
207
+ }, SAVE_DEBOUNCE_MS);
208
+ };
209
+
210
+ changed.current = () => {
211
+ const now = accountPrefsOf(store, theme.current);
212
+ let moved = false;
213
+ for (const field of FIELDS) {
214
+ if (now[field] === baseline[field]) continue;
215
+ (baseline as unknown as Record<string, unknown>)[field] = now[field];
216
+ dirty.add(field);
217
+ moved = true;
218
+ }
219
+ if (!moved) return;
220
+ persist();
221
+ if (ready) schedule();
222
+ };
223
+ const unsubscribe = store.subscribeCold(() => changed.current?.());
224
+
225
+ void fetch("/auth/me", { credentials: "same-origin" })
226
+ .then((res) => (res.ok ? res.json() : null))
227
+ .then((me: { signedIn?: boolean; editorPrefs?: ServerPrefs | null } | null) => {
228
+ if (!live || me?.signedIn !== true) return;
229
+ const saved = me.editorPrefs ?? null;
230
+ if (saved !== null && typeof saved.updatedAt === "number") {
231
+ if (saved.updatedAt !== sync.syncedAt) {
232
+ adopt(saved, dirty);
233
+ sync.syncedAt = saved.updatedAt;
234
+ persist();
235
+ }
236
+ } else if (hasStoredEditorPrefs()) {
237
+ // An account that never saved: seed it with this browser's copy.
238
+ for (const field of FIELDS) dirty.add(field);
239
+ persist();
240
+ }
241
+ ready = true;
242
+ save();
243
+ })
244
+ .catch(() => {});
245
+
246
+ return () => {
247
+ live = false;
248
+ unsubscribe();
249
+ changed.current = null;
250
+ if (timer !== null) clearTimeout(timer);
251
+ };
252
+ }, [store, setPreference]);
253
+
254
+ // A theme change: the same path as a store field. Before the hosted effect
255
+ // runs (or on a local install), only the ref moves.
256
+ useEffect(() => {
257
+ if (theme.current === preference) return;
258
+ theme.current = preference;
259
+ changed.current?.();
260
+ }, [preference]);
261
+ }
@@ -14,8 +14,11 @@
14
14
  import { AgentApiError, applyAgentEvent, createItemIds } from "@dailies-studio/agent-client";
15
15
  import type { AgentEvent, AgentProposalItem, TranscriptSink } from "@dailies-studio/agent-client";
16
16
  import { ApiError, agent } from "../api.js";
17
+ import { boardSubjectOf, isPartId } from "./storyboardModel.js";
17
18
  import type { ShotEditorStore } from "./store.js";
18
19
  import type { ShotEditorState } from "./types.js";
20
+ import { authoringFrame } from "./viewMode.js";
21
+ import { readOnlyReason } from "./workspace.js";
19
22
 
20
23
  function message(e: unknown): string {
21
24
  return e instanceof Error ? e.message : String(e);
@@ -29,6 +32,18 @@ function isConflict(e: unknown): boolean {
29
32
  }
30
33
 
31
34
  const itemId = createItemIds("sh");
35
+ const activeTurns = new WeakMap<ShotEditorStore, object>();
36
+
37
+ /** The Project each agent session was asked from (its doc id and display
38
+ * title), by session id, per store. A card stays pending across a Project
39
+ * switch, so a component file the agent made is made for the Project that
40
+ * asked, never for whichever one is open when the card is applied. */
41
+ const askedFrom = new WeakMap<ShotEditorStore, Map<string, { docId: string; title: string }>>();
42
+ /** How many asks `askedFrom` remembers, newest kept.
43
+ * ponytail: a create card from an older ask links nothing and says where it
44
+ * lists, as a card no ask of this page made does. Drop an entry once its
45
+ * session's cards are all applied if that ever bites. */
46
+ export const ASKED_FROM_MAX = 32;
32
47
 
33
48
  export async function refreshAgentCredentials(store: ShotEditorStore): Promise<void> {
34
49
  try {
@@ -57,7 +72,8 @@ export async function clearAgentKey(store: ShotEditorStore): Promise<void> {
57
72
 
58
73
  /**
59
74
  * What "this" means in the shot chrome: which surface is asking, which shot is
60
- * open, which scene and frame the author is looking at.
75
+ * open, which definition the author has entered, and which scene and frame they
76
+ * are looking at.
61
77
  *
62
78
  * `surface: "shot"` is load-bearing, not decoration — it is what makes the
63
79
  * server append the shot addendum to the system prompt AND compose the shot
@@ -72,16 +88,64 @@ export function buildShotAgentContext(s: ShotEditorState): unknown {
72
88
  // The playhead and the open scene, unconditionally. Every layer in this lane
73
89
  // renders AT a frame, so "which moment am I looking at" is always a real
74
90
  // statement about what the author means by "this".
75
- shot.frame = s.frame;
76
- if (s.sceneId !== null) shot.sceneId = s.sceneId;
77
- const primary = s.selection[0];
78
- if (primary !== undefined) shot.selectedLayerId = primary;
91
+ //
92
+ // AND THE FRAME FOLLOWS THE CONTEXT, because the context decides what a frame
93
+ // MEANS. Inside a definition the chrome's clock is `defFrame` — def-local,
94
+ // starting at 0 — and `frame` is master time, which nothing on screen is
95
+ // showing. Sending the master frame there would have the agent read one moment
96
+ // while the author is looking at another, with no way for either to notice.
97
+ if (s.editingDef !== null) shot.editingDef = s.editingDef;
98
+ shot.frame = authoringFrame(s);
99
+ // The board, when it is what the author is looking at: the flag tells the
100
+ // model to read get_storyboard before touching scenes, and the selected
101
+ // card or board subject (a still, a gap or a cut) is "this" in the same
102
+ // sense the selected layer is. The board shows no layers and enters no
103
+ // Shot, so it sends no layer; the part is the open shot board's plan, else
104
+ // the one shot card selected, and the scene is that plan's Shot.
105
+ if (s.storyboardOpen) {
106
+ shot.storyboardOpen = true;
107
+ const reference = s.storyboardSelection.find((id) => id.startsWith("ref_"));
108
+ if (reference !== undefined) shot.selectedReferenceId = reference;
109
+ const subject = boardSubjectOf(s.doc, s.storyboardSelection);
110
+ if (subject !== null) shot.boardSubject = subject.id;
111
+ const only = s.storyboardSelection.length === 1 ? s.storyboardSelection[0]! : null;
112
+ const partId = s.storyboardPlan ?? (only !== null && isPartId(only) ? only : null);
113
+ const part = partId === null ? undefined : s.doc?.storyboard?.parts.find((p) => p.id === partId);
114
+ if (part !== undefined) {
115
+ shot.selectedPartId = part.id;
116
+ if (part.scene !== undefined && s.doc?.scenes.includes(part.scene) === true) shot.sceneId = part.scene;
117
+ // On the shot board, the sketch on screen and its first selected item.
118
+ const sketch = s.storyboardPlan === part.id ? part.sketches?.find((k) => k.id === s.storyboardSketch) : undefined;
119
+ if (sketch !== undefined) {
120
+ shot.sketchId = sketch.id;
121
+ const item = s.sketchSelection.find((id) => sketch.items.some((i) => i.id === id));
122
+ if (item !== undefined) shot.selectedSketchItemId = item;
123
+ }
124
+ }
125
+ } else {
126
+ if (s.sceneId !== null) shot.sceneId = s.sceneId;
127
+ const primary = s.selection[0];
128
+ if (primary !== undefined) shot.selectedLayerId = primary;
129
+ }
79
130
  return { surface: "shot", ...(Object.keys(shot).length > 0 ? { shot } : {}) };
80
131
  }
81
132
 
82
133
  /** Every key `buildShotAgentContext` may put under `context.shot`. The server
83
134
  * rejects anything else, so this list and the server's are one contract. */
84
- export const SHOT_CONTEXT_KEYS = ["docName", "componentName", "frame", "sceneId", "selectedLayerId"];
135
+ export const SHOT_CONTEXT_KEYS = [
136
+ "docName",
137
+ "componentName",
138
+ "frame",
139
+ "sceneId",
140
+ "editingDef",
141
+ "selectedLayerId",
142
+ "storyboardOpen",
143
+ "selectedReferenceId",
144
+ "selectedPartId",
145
+ "boardSubject",
146
+ "sketchId",
147
+ "selectedSketchItemId",
148
+ ];
85
149
 
86
150
  /** The store, seen as the shared reducer's three transcript verbs. */
87
151
  function sinkFor(store: ShotEditorStore): TranscriptSink {
@@ -97,23 +161,77 @@ function sinkFor(store: ShotEditorStore): TranscriptSink {
97
161
  export async function askAgent(store: ShotEditorStore, prompt: string): Promise<void> {
98
162
  const s = store.getState();
99
163
  if (s.agentRunning || prompt.trim() === "") return;
164
+ const turn = {};
165
+ const userId = itemId();
166
+ activeTurns.set(store, turn);
100
167
  store.setAgentError(null);
101
- store.appendAgentItem({ id: itemId(), kind: "user", text: prompt });
168
+ store.appendAgentItem({ id: userId, kind: "user", text: prompt });
102
169
  store.setAgentRunning(true);
170
+ let valid = true;
171
+ let sessionId: string | null = null;
172
+ let cancelling: Promise<void> | null = null;
173
+ const ownsTurn = (): boolean => activeTurns.get(store) === turn;
174
+ const cancelObsolete = (): Promise<void> => {
175
+ if (sessionId === null) return Promise.resolve();
176
+ cancelling ??= agent.cancel(sessionId).catch(() => {});
177
+ return cancelling;
178
+ };
179
+ const stillCurrent = (): boolean => {
180
+ const current = store.getState();
181
+ return valid && ownsTurn() && current.name === s.name && current.doc?.id === s.doc?.id &&
182
+ current.agentItems.some((item) => item.id === userId);
183
+ };
184
+ // Latch any Project departure, even A -> B -> A between two awaited
185
+ // responses. Clearing the originating transcript also ends its turn.
186
+ // Release this turn's running flag immediately so the new Project can ask;
187
+ // its eventual finally must not stop a newer request. Cold: a departure
188
+ // and the transcript are never a transport tick.
189
+ const unsubscribe = store.subscribeCold(() => {
190
+ if (!valid || stillCurrent()) return;
191
+ valid = false;
192
+ if (ownsTurn()) {
193
+ activeTurns.delete(store);
194
+ store.setAgentRunning(false);
195
+ }
196
+ void cancelObsolete();
197
+ });
103
198
  try {
104
- const { sessionId } = await agent.createSession({
199
+ await store.flush();
200
+ if (!stillCurrent()) return;
201
+ if (store.getState().dirty)
202
+ throw new Error("The Project changed while saving. Let it finish saving, then ask the agent again.");
203
+ const created = await agent.createSession({
105
204
  prompt,
106
205
  ...(s.agentModel !== null ? { model: s.agentModel } : {}),
107
206
  context: buildShotAgentContext(s),
108
207
  });
109
- store.setAgentSession(sessionId);
110
- await agent.events(sessionId, (event: AgentEvent) => {
111
- applyAgentEvent(sinkFor(store), sessionId, event, itemId);
208
+ sessionId = created.sessionId;
209
+ if (s.doc !== null) {
210
+ const asked = askedFrom.get(store) ?? new Map<string, { docId: string; title: string }>();
211
+ asked.set(sessionId, { docId: s.doc.id, title: s.doc.title ?? s.name ?? s.doc.name });
212
+ // A Map keeps insertion order, so its first key is the oldest ask.
213
+ const oldest = asked.keys().next();
214
+ if (asked.size > ASKED_FROM_MAX && oldest.done !== true) asked.delete(oldest.value);
215
+ askedFrom.set(store, asked);
216
+ }
217
+ if (!stillCurrent()) {
218
+ await cancelObsolete();
219
+ return;
220
+ }
221
+ const currentSession = sessionId;
222
+ store.setAgentSession(currentSession);
223
+ await agent.events(currentSession, (event: AgentEvent) => {
224
+ if (!stillCurrent()) return;
225
+ applyAgentEvent(sinkFor(store), currentSession, event, itemId);
112
226
  });
113
227
  } catch (e) {
114
- store.setAgentError(message(e));
228
+ if (stillCurrent()) store.setAgentError(message(e));
115
229
  } finally {
116
- store.setAgentRunning(false);
230
+ unsubscribe();
231
+ if (ownsTurn()) {
232
+ activeTurns.delete(store);
233
+ store.setAgentRunning(false);
234
+ }
117
235
  }
118
236
  }
119
237
 
@@ -127,6 +245,29 @@ export async function cancelAgent(store: ShotEditorStore): Promise<void> {
127
245
  }
128
246
  }
129
247
 
248
+ /**
249
+ * A component file the agent created is MADE FOR THE PROJECT THAT ASKED, as
250
+ * New › Code makes one for the Project it was pressed in: listed under This
251
+ * project even before a shot uses it. Only while that Project is the one
252
+ * open, read after every await of the apply: otherwise nothing is linked and
253
+ * the author is told where to tick it. A `.ts` helper is not a component
254
+ * file, so it is never linked. Nothing here throws (`setComponentFile`
255
+ * answers its refusals).
256
+ */
257
+ function linkMadeFile(store: ShotEditorStore, sessionId: string, rel: string): void {
258
+ const asked = askedFrom.get(store)?.get(sessionId);
259
+ if (asked === undefined) {
260
+ store.reportError(`${rel} lists under Your library until someone ticks Made for this project in the Project it is for.`);
261
+ return;
262
+ }
263
+ if (store.getState().doc?.id !== asked.docId) {
264
+ store.reportError(`${rel} was made for ${asked.title}: open it there and tick Made for this project.`);
265
+ return;
266
+ }
267
+ const out = store.setComponentFile(rel, true);
268
+ if (!out.ok) store.reportError(`${rel} shows under Your library. ${out.reason}`);
269
+ }
270
+
130
271
  function proposalOf(store: ShotEditorStore, id: string): AgentProposalItem | undefined {
131
272
  return store
132
273
  .getState()
@@ -154,6 +295,12 @@ function proposalOf(store: ShotEditorStore, id: string): AgentProposalItem | und
154
295
  export async function applyAgentProposal(store: ShotEditorStore, proposalId: string): Promise<void> {
155
296
  const item = proposalOf(store, proposalId);
156
297
  if (item === undefined || item.status !== "pending") return;
298
+ // An apply writes the document on the server. The card stays pending, so an
299
+ // editor who opens the Project later can still apply it.
300
+ if (store.getState().readOnly) {
301
+ store.reportError(readOnlyReason(store.getState()));
302
+ return;
303
+ }
157
304
  store.patchAgentProposal(proposalId, { status: "applying", error: null });
158
305
  // THE AUTHOR'S OWN EDITS GO FIRST. Apply is a button in the same window as
159
306
  // the canvas, so "drag a layer, then click Apply" is an ordinary gesture —
@@ -182,6 +329,18 @@ export async function applyAgentProposal(store: ShotEditorStore, proposalId: str
182
329
  return;
183
330
  }
184
331
 
332
+ // A FILE APPLY UNDER AN OPEN BUFFER: the bus says "files changed", which
333
+ // marks a clean buffer for nothing, so the editor would go on showing the
334
+ // text from before the apply and a later save would write it back. A clean
335
+ // buffer takes the disk; a dirty one cannot exist here (`askAgent` refuses
336
+ // while the file is dirty) and, if typed into since, keeps its 409 path.
337
+ if (item.proposalKind === "edit" || item.proposalKind === "create") {
338
+ const rel = item.path.replace(/\\/g, "/").replace(/^\.\//, "");
339
+ const buffer = store.getState().codeFiles[rel];
340
+ if (buffer !== undefined && buffer.content === buffer.savedContent) await store.reloadCodeFile(rel);
341
+ if (item.proposalKind === "create" && /\.[jt]sx$/.test(rel)) linkMadeFile(store, item.sessionId, rel);
342
+ return;
343
+ }
185
344
  if (item.proposalKind !== "shot") return;
186
345
  if (store.getState().name !== item.docName && item.baseRev === null) {
187
346
  // A CREATE names a shot this chrome does not have open — by definition, it
@@ -198,6 +357,5 @@ export async function applyAgentProposal(store: ShotEditorStore, proposalId: str
198
357
  }
199
358
  }
200
359
  // …but an EDIT of some other shot never yanks the author out of the document
201
- // they are working in. It landed on disk; the switcher will show it.
202
- void store.refreshShots();
360
+ // they are working in. It landed on disk, and Project Home lists it.
203
361
  }