@dailies-studio/shot-editor 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (432) hide show
  1. package/dist/.vite/manifest.json +382 -0
  2. package/dist/assets/AgentPanel-CQejLbiX.js +2 -0
  3. package/dist/assets/AgentPanel-DJEphah0.css +1 -0
  4. package/dist/assets/AgentQuestionDialog-BqkD0kXF.js +1 -0
  5. package/dist/assets/CodeStudio-Dv4ihHpz.js +23 -0
  6. package/dist/assets/CommentsPanel-B4yhhT1T.js +1 -0
  7. package/dist/assets/ComponentLibraryRail-Ch_dsAru.js +1 -0
  8. package/dist/assets/ComponentStage-BRhRotyH.js +1 -0
  9. package/dist/assets/CurvePane-BjFVabKX.css +1 -0
  10. package/dist/assets/CurvePane-R22WzllJ.js +1 -0
  11. package/dist/assets/ExportDialog-CWLQWTmG.js +2 -0
  12. package/dist/assets/FontPicker-DWKaYL7F.js +1 -0
  13. package/dist/assets/FontPicker-v0IOIArM.css +1 -0
  14. package/dist/assets/GenerateDrawer-Bh3SqDKt.js +2 -0
  15. package/dist/assets/InsertDrawer-Ce1fod1L.js +1 -0
  16. package/dist/assets/LibraryBrowser-D9sEGz5w.js +2 -0
  17. package/dist/assets/MakeItYoursSection-CMO4319_.js +2 -0
  18. package/dist/assets/MaterialGraphCanvas-BFzl4wWR.js +23 -0
  19. package/dist/assets/MaterialGraphCanvas-BIUD59ik.css +1 -0
  20. package/dist/assets/MaterialWorkspace-CqTWqaRA.js +14 -0
  21. package/dist/assets/PreferencesDialog-CBf9_OoD.js +1 -0
  22. package/dist/assets/PresetsSection-CUEaoCVU.js +1 -0
  23. package/dist/assets/SoundHistory-BeS8EW1h.js +1 -0
  24. package/dist/assets/SoundMode-BmcZru3i.js +3 -0
  25. package/dist/assets/StockSounds-DdAUGRYK.js +1 -0
  26. package/dist/assets/TemplateFieldSection-CaeXpLFo.js +1 -0
  27. package/dist/assets/VersionHistoryPanel-CxG6XhNP.js +1 -0
  28. package/dist/assets/board-B88EPuLO.css +1 -0
  29. package/dist/assets/board-CL-he7SL.js +3 -0
  30. package/dist/assets/components-0oOSPN0l.js +1 -0
  31. package/dist/assets/googleFontRanges-gp32wJMo.js +1 -0
  32. package/dist/assets/googleFonts-BNon6M7X.js +1 -0
  33. package/dist/assets/iconsAndAliases-DL7gXP5j.js +1863 -0
  34. package/dist/assets/index-4F5NQmI8.css +1 -0
  35. package/dist/assets/index-BGHQGV5z.js +117 -0
  36. package/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  37. package/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  38. package/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  39. package/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  40. package/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  41. package/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  42. package/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  43. package/dist/assets/libraryModel-CpIwfFha.js +1 -0
  44. package/dist/assets/lucide-6iK64tTB.js +9319 -0
  45. package/dist/assets/motionStamp-CH-Hew4E.js +1 -0
  46. package/dist/assets/takeMeasure-Tdg0TxEi.js +1 -0
  47. package/dist/assets/webmcp-BsB9gxWC.js +2 -0
  48. package/dist/index.html +34 -3
  49. package/package.json +12 -9
  50. package/src/addFamilies.ts +66 -0
  51. package/src/api.ts +1631 -55
  52. package/src/app/App.tsx +977 -198
  53. package/src/app/lazyPanel.tsx +137 -0
  54. package/src/app/lazySurfaces.tsx +203 -0
  55. package/src/audio/index.tsx +41 -10
  56. package/src/audio/meter.ts +485 -0
  57. package/src/audio/meterMath.ts +273 -0
  58. package/src/audio/meterStore.ts +155 -0
  59. package/src/audio/offlineMix.ts +289 -0
  60. package/src/audio/preview.ts +928 -118
  61. package/src/bridge.ts +15 -5
  62. package/src/bridgeTypes.ts +1611 -36
  63. package/src/channels.ts +17 -32
  64. package/src/fonts/googleFonts.ts +4 -0
  65. package/src/hostApi.ts +174 -0
  66. package/src/icons.tsx +770 -0
  67. package/src/layerWords.ts +61 -0
  68. package/src/main.tsx +56 -4
  69. package/src/panels/ResizeHandle.tsx +33 -43
  70. package/src/panels/agent/AgentDots.tsx +36 -0
  71. package/src/panels/agent/AgentPanel.tsx +119 -43
  72. package/src/panels/agent/AgentQuestionDialog.tsx +202 -0
  73. package/src/panels/agent/agent.css +113 -97
  74. package/src/panels/agent/agentGate.ts +35 -0
  75. package/src/panels/code/CodeDefaultsPanel.tsx +345 -0
  76. package/src/panels/code/CodeStudio.tsx +373 -0
  77. package/src/panels/code/FileTree.tsx +265 -0
  78. package/src/panels/code/codeBufferGuard.ts +52 -0
  79. package/src/panels/code/codeParts.tsx +321 -0
  80. package/src/panels/comments/CommentComposer.tsx +239 -0
  81. package/src/panels/comments/CommentThread.tsx +346 -0
  82. package/src/panels/comments/CommentsPanel.tsx +336 -0
  83. package/src/panels/comments/InboxButton.tsx +131 -0
  84. package/src/panels/comments/MentionMenu.tsx +98 -0
  85. package/src/panels/comments/parts.tsx +171 -0
  86. package/src/panels/export/ExportDialog.tsx +919 -96
  87. package/src/panels/export/ExportScrim.tsx +57 -0
  88. package/src/panels/files/AssetsTab.tsx +656 -0
  89. package/src/panels/files/FilePanel.tsx +1466 -216
  90. package/src/panels/files/LayerMarks.tsx +40 -0
  91. package/src/panels/files/LeftColumn.tsx +37 -0
  92. package/src/panels/files/McpSection.tsx +690 -47
  93. package/src/panels/files/MissingAssetsNotice.tsx +60 -0
  94. package/src/panels/files/assetDrag.ts +79 -0
  95. package/src/panels/files/resourceParts.tsx +255 -0
  96. package/src/panels/fonts/FontPicker.tsx +762 -0
  97. package/src/panels/fonts/faceCache.ts +207 -0
  98. package/src/panels/fonts/fontPicker.css +271 -0
  99. package/src/panels/fonts/pickerModel.ts +265 -0
  100. package/src/panels/fonts/pickerRequest.ts +46 -0
  101. package/src/panels/header/AccountAvatar.tsx +76 -0
  102. package/src/panels/header/AgentChip.tsx +77 -0
  103. package/src/panels/header/AppMenu.tsx +213 -0
  104. package/src/panels/header/Header.tsx +564 -84
  105. package/src/panels/header/HistoryControls.tsx +31 -5
  106. package/src/panels/header/PresenceStack.tsx +329 -0
  107. package/src/panels/header/ProjectTitle.tsx +232 -0
  108. package/src/panels/header/ShareButton.tsx +59 -0
  109. package/src/panels/header/StatusBadges.tsx +358 -27
  110. package/src/panels/header/SyncDeletedDialog.tsx +88 -0
  111. package/src/panels/header/ZoomControls.tsx +46 -15
  112. package/src/panels/header/agentHandoff.ts +52 -0
  113. package/src/panels/header/leave.ts +26 -0
  114. package/src/panels/help/HelpSection.tsx +189 -163
  115. package/src/panels/insert/InsertDrawer.tsx +723 -0
  116. package/src/panels/insert/iconSet.ts +109 -0
  117. package/src/panels/inspector/ExposeDot.tsx +117 -0
  118. package/src/panels/inspector/FramingRows.tsx +314 -44
  119. package/src/panels/inspector/InspectorFooter.tsx +91 -0
  120. package/src/panels/inspector/InspectorHead.tsx +425 -0
  121. package/src/panels/inspector/InspectorPanel.tsx +408 -124
  122. package/src/panels/inspector/KeyframeDot.tsx +373 -0
  123. package/src/panels/inspector/KeyframeSection.tsx +257 -0
  124. package/src/panels/inspector/MarkerSection.tsx +55 -11
  125. package/src/panels/inspector/ParamRow.tsx +84 -53
  126. package/src/panels/inspector/RightColumn.tsx +152 -0
  127. package/src/panels/inspector/TimeSection.tsx +866 -0
  128. package/src/panels/inspector/animate/EaseControl.tsx +244 -0
  129. package/src/panels/inspector/animate/PresetsSection.tsx +135 -0
  130. package/src/panels/inspector/animate/ShotSection.tsx +562 -48
  131. package/src/panels/inspector/design/AlignRow.tsx +95 -31
  132. package/src/panels/inspector/design/AppearanceSection.tsx +118 -9
  133. package/src/panels/inspector/design/ClonerSection.tsx +323 -62
  134. package/src/panels/inspector/design/ComponentSection.tsx +50 -43
  135. package/src/panels/inspector/design/CornerSection.tsx +45 -14
  136. package/src/panels/inspector/design/DefinitionProps.tsx +315 -0
  137. package/src/panels/inspector/design/DefinitionSection.tsx +136 -0
  138. package/src/panels/inspector/design/DeviceSection.tsx +210 -0
  139. package/src/panels/inspector/design/FillSection.tsx +83 -51
  140. package/src/panels/inspector/design/FontField.tsx +196 -0
  141. package/src/panels/inspector/design/HtmlSection.tsx +23 -12
  142. package/src/panels/inspector/design/InstanceSection.tsx +373 -154
  143. package/src/panels/inspector/design/MaskSection.tsx +84 -0
  144. package/src/panels/inspector/design/MaterialSection.tsx +177 -0
  145. package/src/panels/inspector/design/MediaSection.tsx +273 -120
  146. package/src/panels/inspector/design/MeshSection.tsx +910 -82
  147. package/src/panels/inspector/design/PaintRows.tsx +550 -0
  148. package/src/panels/inspector/design/PlateStyleSection.tsx +177 -0
  149. package/src/panels/inspector/design/ShadowSection.tsx +221 -48
  150. package/src/panels/inspector/design/SplineFollowSection.tsx +235 -0
  151. package/src/panels/inspector/design/StrokeSection.tsx +228 -43
  152. package/src/panels/inspector/design/TemplateFieldSection.tsx +57 -0
  153. package/src/panels/inspector/design/TransformSection.tsx +244 -50
  154. package/src/panels/inspector/design/TypographySection.tsx +679 -110
  155. package/src/panels/inspector/layout.ts +295 -0
  156. package/src/panels/inspector/propValue.ts +199 -0
  157. package/src/panels/inspector/scene/CameraSection.tsx +232 -22
  158. package/src/panels/inspector/scene/EffectsSection.tsx +722 -71
  159. package/src/panels/inspector/scene/MakeItYoursSection.tsx +180 -0
  160. package/src/panels/inspector/scene/SceneDocSection.tsx +191 -0
  161. package/src/panels/inspector/scene/SceneSection.tsx +219 -44
  162. package/src/panels/inspector/scene/TransitionSection.tsx +523 -72
  163. package/src/panels/inspector/storyboard/StoryboardReferenceSection.tsx +593 -0
  164. package/src/panels/inspector/storyboard/VideoStill.tsx +120 -0
  165. package/src/panels/inspector/timeModel.ts +190 -0
  166. package/src/panels/inspector/useDocWrite.ts +438 -146
  167. package/src/panels/library/LibraryBrowser.tsx +435 -0
  168. package/src/panels/library/LibraryGrid.tsx +190 -0
  169. package/src/panels/library/LibraryPreviewPanel.tsx +349 -0
  170. package/src/panels/library/LibraryRail.tsx +84 -0
  171. package/src/panels/library/LibrarySoundPanel.tsx +293 -0
  172. package/src/panels/library/SoundHistory.tsx +245 -0
  173. package/src/panels/library/StockSounds.tsx +390 -0
  174. package/src/panels/library/libraryModel.ts +147 -0
  175. package/src/panels/library/stockSearch.ts +77 -0
  176. package/src/panels/maskMarks.tsx +117 -0
  177. package/src/panels/material/MaterialCommentNode.tsx +54 -0
  178. package/src/panels/material/MaterialGraphCanvas.tsx +861 -0
  179. package/src/panels/material/MaterialGraphMenu.tsx +45 -0
  180. package/src/panels/material/MaterialGroupNode.tsx +56 -0
  181. package/src/panels/material/MaterialLibrary.tsx +253 -0
  182. package/src/panels/material/MaterialNode.tsx +453 -0
  183. package/src/panels/material/MaterialNodePalette.tsx +349 -0
  184. package/src/panels/material/MaterialParameterControls.tsx +525 -0
  185. package/src/panels/material/MaterialRerouteNode.tsx +42 -0
  186. package/src/panels/material/MaterialWorkspace.tsx +501 -0
  187. package/src/panels/material/NodePreview.tsx +70 -0
  188. package/src/panels/material/flowModel.ts +441 -0
  189. package/src/panels/material/parameterValues.ts +231 -0
  190. package/src/panels/material/previewScheduler.ts +235 -0
  191. package/src/panels/material/reactFlow.css +6 -0
  192. package/src/panels/material/socketGlyph.ts +85 -0
  193. package/src/panels/material/viewportMemory.ts +58 -0
  194. package/src/panels/preferences/PreferencesDialog.tsx +215 -0
  195. package/src/panels/share/ShareComponentDialog.tsx +247 -0
  196. package/src/panels/stage/StageMenu.tsx +35 -5
  197. package/src/panels/stage/StagePanel.tsx +338 -23
  198. package/src/panels/stage/cellSlots.tsx +70 -0
  199. package/src/panels/storyboard/BoardCanvas.tsx +739 -0
  200. package/src/panels/storyboard/BoardCursors.tsx +158 -0
  201. package/src/panels/storyboard/BoardDropDialog.tsx +161 -0
  202. package/src/panels/storyboard/BoardPlan.tsx +42 -0
  203. package/src/panels/storyboard/ComponentSheet.tsx +187 -0
  204. package/src/panels/storyboard/ComponentStage.tsx +235 -0
  205. package/src/panels/storyboard/MiniSketch.tsx +173 -0
  206. package/src/panels/storyboard/PartPicker.tsx +64 -0
  207. package/src/panels/storyboard/PlanPanel.tsx +617 -0
  208. package/src/panels/storyboard/ReferencePreview.tsx +217 -0
  209. package/src/panels/storyboard/ShotBrief.tsx +360 -0
  210. package/src/panels/storyboard/ShotCard.tsx +251 -0
  211. package/src/panels/storyboard/StagePreview.tsx +348 -0
  212. package/src/panels/storyboard/StillThumbs.tsx +162 -0
  213. package/src/panels/storyboard/StoryboardView.tsx +79 -0
  214. package/src/panels/storyboard/board.css +1065 -0
  215. package/src/panels/storyboard/board.ts +12 -0
  216. package/src/panels/storyboard/boardClipboard.ts +111 -0
  217. package/src/panels/storyboard/boardOrder.ts +68 -0
  218. package/src/panels/storyboard/boardRows.tsx +425 -0
  219. package/src/panels/storyboard/liveCap.ts +90 -0
  220. package/src/panels/storyboard/plan.css +295 -0
  221. package/src/panels/storyboard/posterCache.ts +133 -0
  222. package/src/panels/storyboard/shotboard/ComponentPicker.tsx +373 -0
  223. package/src/panels/storyboard/shotboard/ImagePicker.tsx +145 -0
  224. package/src/panels/storyboard/shotboard/ShotBoard.tsx +292 -0
  225. package/src/panels/storyboard/shotboard/SketchCanvas.tsx +443 -0
  226. package/src/panels/storyboard/shotboard/SketchInspector.tsx +471 -0
  227. package/src/panels/storyboard/shotboard/SketchItem.tsx +240 -0
  228. package/src/panels/storyboard/shotboard/SketchLayers.tsx +130 -0
  229. package/src/panels/storyboard/shotboard/SketchPlay.tsx +108 -0
  230. package/src/panels/storyboard/shotboard/SketchStrip.tsx +178 -0
  231. package/src/panels/storyboard/shotboard/SketchToolFooter.tsx +99 -0
  232. package/src/panels/storyboard/shotboard/sketch.css +1392 -0
  233. package/src/panels/storyboard/shotboard/sketchModel.ts +219 -0
  234. package/src/panels/storyboard/storyboardDrop.ts +164 -0
  235. package/src/panels/studio/ComponentLibraryRail.tsx +346 -0
  236. package/src/panels/studio/ComponentSidePanel.tsx +355 -0
  237. package/src/panels/studio/ComponentStudio.tsx +243 -0
  238. package/src/panels/studio/ComponentsHome.tsx +318 -0
  239. package/src/panels/studio/ComponentsPreview.tsx +179 -0
  240. package/src/panels/studio/EveryUse.tsx +244 -0
  241. package/src/panels/studio/ThisUsePanel.tsx +679 -0
  242. package/src/panels/studio/componentParts.tsx +341 -0
  243. package/src/panels/studio/components.ts +6 -0
  244. package/src/panels/studio/newCodeComponent.ts +66 -0
  245. package/src/panels/timeline/AudioFxButton.tsx +290 -0
  246. package/src/panels/timeline/AudioLane.tsx +674 -85
  247. package/src/panels/timeline/BlockRow.tsx +154 -85
  248. package/src/panels/timeline/ChannelRow.tsx +378 -129
  249. package/src/panels/timeline/CurvePane.tsx +508 -0
  250. package/src/panels/timeline/Filmstrip.tsx +361 -0
  251. package/src/panels/timeline/GenerateDrawer.tsx +407 -0
  252. package/src/panels/timeline/KeyStretch.tsx +115 -0
  253. package/src/panels/timeline/LifespanRow.tsx +730 -47
  254. package/src/panels/timeline/MarkerLane.tsx +46 -8
  255. package/src/panels/timeline/PlayButton.tsx +91 -0
  256. package/src/panels/timeline/PropertyPicker.tsx +254 -0
  257. package/src/panels/timeline/ShotRow.tsx +400 -0
  258. package/src/panels/timeline/SoundMode.tsx +1406 -0
  259. package/src/panels/timeline/TimelineDock.tsx +2338 -272
  260. package/src/panels/timeline/TransportVolume.tsx +163 -0
  261. package/src/panels/timeline/beatRuler.ts +74 -0
  262. package/src/panels/timeline/curveModel.ts +500 -0
  263. package/src/panels/timeline/duckUnderVoice.ts +68 -0
  264. package/src/panels/timeline/keyframeMenu.ts +138 -0
  265. package/src/panels/timeline/labelWidth.ts +49 -0
  266. package/src/panels/timeline/marquee.ts +155 -0
  267. package/src/panels/timeline/model.ts +1860 -199
  268. package/src/panels/timeline/snap.ts +125 -43
  269. package/src/panels/timeline/staggerPreview.tsx +92 -0
  270. package/src/panels/timeline/takeMeasure.ts +72 -0
  271. package/src/panels/timeline/useLive.ts +48 -0
  272. package/src/panels/timeline/viewport.ts +234 -0
  273. package/src/panels/toolbar/BackChip.tsx +116 -0
  274. package/src/panels/toolbar/CameraViewBar.tsx +118 -49
  275. package/src/panels/toolbar/CanvasBar.tsx +117 -0
  276. package/src/panels/toolbar/InsertButton.tsx +38 -0
  277. package/src/panels/toolbar/IsolationTrail.tsx +156 -0
  278. package/src/panels/toolbar/PanelToggles.tsx +86 -0
  279. package/src/panels/toolbar/ShadingBar.tsx +119 -26
  280. package/src/panels/toolbar/ToolDockBar.tsx +329 -80
  281. package/src/panels/toolbar/ToolFooter.tsx +102 -0
  282. package/src/panels/toolbar/ViewModeBar.tsx +247 -38
  283. package/src/panels/toolbar/WorkPlaneBar.tsx +94 -0
  284. package/src/panels/toolbar/useAddRows.tsx +326 -0
  285. package/src/panels/toolbar/useDismiss.ts +122 -0
  286. package/src/panels/versions/VersionHistoryPanel.tsx +274 -0
  287. package/src/panels/versions/versionModel.ts +103 -0
  288. package/src/panels/virtualWindow.ts +271 -0
  289. package/src/shotTokens.css +151 -60
  290. package/src/stage/FrustumGizmo.tsx +504 -84
  291. package/src/stage/GridPlane.tsx +225 -16
  292. package/src/stage/NodePreviews.tsx +628 -0
  293. package/src/stage/PerfHud.tsx +437 -47
  294. package/src/stage/SplitRenderer.tsx +816 -169
  295. package/src/stage/StageApp.tsx +4274 -486
  296. package/src/stage/StageRootBoundary.tsx +114 -0
  297. package/src/stage/StageTools.tsx +651 -151
  298. package/src/stage/TransformGizmo.tsx +696 -0
  299. package/src/stage/adaptivePresentation.ts +145 -0
  300. package/src/stage/bridgeClient.ts +2522 -41
  301. package/src/stage/exportWorker.ts +9 -0
  302. package/src/stage/feed.ts +305 -14
  303. package/src/stage/fontPreview.ts +176 -0
  304. package/src/stage/frustumGeometry.ts +319 -1
  305. package/src/stage/furnitureTokens.ts +33 -0
  306. package/src/stage/gizmoMath.ts +508 -16
  307. package/src/stage/gpuTimer.ts +276 -0
  308. package/src/stage/instanceModules.ts +194 -33
  309. package/src/stage/interactions/move.ts +24 -5
  310. package/src/stage/isolation.ts +291 -33
  311. package/src/stage/jsSelfProfiler.ts +325 -0
  312. package/src/stage/main.tsx +116 -16
  313. package/src/stage/modalState.ts +222 -23
  314. package/src/stage/moduleEpoch.ts +60 -0
  315. package/src/stage/motionPathStage.ts +279 -0
  316. package/src/stage/motionPreview.ts +74 -0
  317. package/src/stage/overlays/Overlays.tsx +1112 -159
  318. package/src/stage/overlays/Rulers.tsx +495 -0
  319. package/src/stage/panes.ts +159 -10
  320. package/src/stage/project.ts +68 -3
  321. package/src/stage/renderScaleController.ts +730 -0
  322. package/src/stage/rulers.ts +129 -0
  323. package/src/stage/sceneExtent.ts +75 -0
  324. package/src/stage/shading.ts +20 -0
  325. package/src/stage/splineStage.ts +632 -0
  326. package/src/stage/stagePerformanceTelemetry.ts +717 -0
  327. package/src/stage/stageTheme.ts +36 -0
  328. package/src/stage/stageWarmup.ts +11 -0
  329. package/src/stage/textAutoSize.ts +124 -0
  330. package/src/stage/tier1Export.ts +641 -0
  331. package/src/stage/tokenColor.ts +14 -9
  332. package/src/stage/view.ts +56 -9
  333. package/src/stage/workingView.ts +277 -33
  334. package/src/state/accountPrefs.ts +261 -0
  335. package/src/state/agentActions.ts +173 -15
  336. package/src/state/agentActivity.ts +198 -0
  337. package/src/state/agentQuestion.ts +239 -0
  338. package/src/state/bootOpen.ts +42 -0
  339. package/src/state/cameraVerbs.ts +139 -0
  340. package/src/state/clientId.ts +6 -1
  341. package/src/state/comments.ts +1099 -0
  342. package/src/state/context.tsx +19 -1
  343. package/src/state/editorPrefs.ts +220 -0
  344. package/src/state/errorReports.ts +121 -0
  345. package/src/state/fieldWrite.ts +161 -0
  346. package/src/state/fontRecents.ts +70 -0
  347. package/src/state/fonts.ts +656 -0
  348. package/src/state/graphClipboard.ts +414 -0
  349. package/src/state/imageThumbs.ts +216 -0
  350. package/src/state/insert.ts +444 -67
  351. package/src/state/isolationTrail.ts +92 -0
  352. package/src/state/keyMoves.ts +134 -0
  353. package/src/state/keyframeClipboard.ts +177 -0
  354. package/src/state/keymap.ts +1153 -117
  355. package/src/state/layoutPrefs.ts +272 -0
  356. package/src/state/layouts.ts +205 -0
  357. package/src/state/measureMedia.ts +167 -0
  358. package/src/state/menu.ts +2341 -28
  359. package/src/state/motionStamp.ts +7 -0
  360. package/src/state/panelSize.ts +375 -5
  361. package/src/state/pendingChanges.ts +119 -0
  362. package/src/state/playHold.ts +78 -0
  363. package/src/state/playbackPreparation.ts +562 -0
  364. package/src/state/posterCapture.ts +242 -0
  365. package/src/state/presence.ts +254 -0
  366. package/src/state/projectComponents.ts +160 -0
  367. package/src/state/renderEvents.ts +298 -18
  368. package/src/state/runtimeErrors.ts +46 -0
  369. package/src/state/selection.ts +12 -0
  370. package/src/state/shortcuts.ts +1606 -0
  371. package/src/state/sketchGuide.ts +51 -0
  372. package/src/state/splineRefs.ts +87 -0
  373. package/src/state/stageHealth.ts +259 -0
  374. package/src/state/stageWatchdog.ts +47 -0
  375. package/src/state/stillStage.ts +330 -0
  376. package/src/state/stillThumbs.ts +474 -0
  377. package/src/state/store.ts +23591 -2982
  378. package/src/state/storyboardModel.ts +528 -0
  379. package/src/state/sync.ts +1257 -0
  380. package/src/state/syncCache.ts +159 -0
  381. package/src/state/triangles.ts +13 -0
  382. package/src/state/types.ts +1698 -55
  383. package/src/state/unsavedGuard.ts +49 -0
  384. package/src/state/viewMode.ts +200 -0
  385. package/src/state/webmcp.ts +1128 -0
  386. package/src/state/workspace.ts +1147 -0
  387. package/src/styles/assets-tab.css +304 -0
  388. package/src/styles/canvas.css +545 -0
  389. package/src/styles/comments.css +1435 -0
  390. package/src/styles/controls.css +162 -0
  391. package/src/styles/dialogs.css +390 -0
  392. package/src/styles/insert-drawer.css +586 -0
  393. package/src/styles/inspector-board.css +207 -0
  394. package/src/styles/inspector-object.css +486 -0
  395. package/src/styles/inspector-shot.css +252 -0
  396. package/src/styles/inspector.css +360 -0
  397. package/src/styles/left-column.css +1628 -0
  398. package/src/styles/library-preview.css +575 -0
  399. package/src/styles/library.css +512 -0
  400. package/src/styles/modes/code.css +569 -0
  401. package/src/styles/modes/component.css +1561 -0
  402. package/src/styles/modes/material.css +1201 -0
  403. package/src/styles/modes/storyboard.css +518 -0
  404. package/src/styles/share-component.css +49 -0
  405. package/src/styles/shell.css +653 -0
  406. package/src/styles/shot-brief.css +360 -0
  407. package/src/styles/states.css +76 -0
  408. package/src/styles/sync.css +380 -0
  409. package/src/styles/timeline-graph.css +384 -0
  410. package/src/styles/timeline-meters.css +89 -0
  411. package/src/styles/timeline-rows.css +1959 -0
  412. package/src/styles/timeline-sound.css +1003 -0
  413. package/src/styles/timeline.css +754 -0
  414. package/src/styles/tool-footer.css +525 -0
  415. package/src/styles/topbar.css +886 -0
  416. package/src/styles/versions.css +262 -0
  417. package/src/styles.css +72 -2921
  418. package/src/tools.ts +108 -23
  419. package/dist/assets/index-B7notge7.css +0 -1
  420. package/dist/assets/index-BBE6Ta9L.js +0 -52
  421. package/src/panels/files/icons.tsx +0 -118
  422. package/src/panels/header/DocSwitcher.tsx +0 -85
  423. package/src/panels/header/TransportBar.tsx +0 -86
  424. package/src/panels/inspector/animate/BlockTiming.tsx +0 -341
  425. package/src/panels/inspector/animate/DuringBlocks.tsx +0 -85
  426. package/src/panels/inspector/animate/InOutPresets.tsx +0 -57
  427. package/src/panels/inspector/animate/ResolvedChannels.tsx +0 -150
  428. package/src/panels/inspector/animate/useBlocks.ts +0 -43
  429. package/src/panels/material/MaterialPicker.tsx +0 -172
  430. package/src/panels/timeline/SceneStrip.tsx +0 -145
  431. package/src/panels/toolbar/AddMenu.tsx +0 -230
  432. package/src/stage/MoveGizmo.tsx +0 -411
@@ -1,49 +1,380 @@
1
- // What the chrome knows and the author cannot see: save state, codegen refusal,
2
- // and which GL backend actually came up.
1
+ // What the chrome knows and the author cannot see, in two places.
3
2
  //
4
- // The backend badge is not decoration, but read it for what it is: the shot
5
- // lane PINS WebGL (`SHOT_STAGE_BACKEND`, packages/shot/src/stageBackend.ts) so
6
- // the editor previews on the same backend the export renders on — "webgl" here
7
- // is the pin working, not a fallback event. The badge earns its keep on the
8
- // day something changes that pin: a backend the export lane cannot reproduce
9
- // is the first thing to check when an effect looks wrong in one place and
10
- // right in the other.
11
- import { useShotEditor } from "../../state/context.js";
12
-
13
- export function StatusBadges() {
3
+ // `SaveBadges` sit in the top bar: save state and a codegen refusal, which are
4
+ // facts about the DOCUMENT. `StageBadges` sit in the canvas bar: the frame rate
5
+ // and the GL backend, which are facts about the PICTURE under that bar.
6
+ //
7
+ // The backend chip was once removed under "a badge is an event, never a
8
+ // setting": the shot lane pinned preview and export to one backend
9
+ // (`SHOT_STAGE_BACKEND`), so the chip said "webgl" in every session that has
10
+ // ever run. That pin now covers the EXPORT only — the preview takes the
11
+ // WebGPU-first default and lands on whichever backend the host browser has
12
+ // (packages/shot/src/stageBackend.ts) — so the word varies per session again
13
+ // and is worth a chip. `stageInfo.backend` is the stage's own onReady report,
14
+ // read off the live renderer after three's silent fallback has or has not
15
+ // happened, which is exactly what an import-time constant cannot tell you.
16
+ //
17
+ // THE FPS CHIP IS AN EVENT TOO, and playback is the event: it appears when the
18
+ // transport starts and goes when it stops, so it never becomes a permanent
19
+ // number decorating an idle bar. What it reports is what the STAGE presented,
20
+ // measured stage-side over one-second windows and sent across the bridge — not
21
+ // the chrome's own seek rate, which is what the transport asked for and reads
22
+ // ~60 while the picture runs at 18, because the stage coalesces requests and
23
+ // drops publications exactly when the two come apart. While the playback
24
+ // render scale holds the picture below the display's density (bridge v52), the
25
+ // chip says by how much ("24 fps · 85%"), and its title says why.
26
+ //
27
+ // A BROKEN PIECE IS AN EVENT THAT STANDS. When the stage isolates a layer, a
28
+ // background, a post stack or a transition that threw (bridge v34), the
29
+ // danger chip says how many, first in the row so it is the last to wrap away,
30
+ // and its title names each one and what it threw. It is not on the View >
31
+ // Status badges switch: a picture with a hole in it is not a preference.
32
+ //
33
+ // A LIVE-SYNC PROJECT SWAPS THE SAVE CHIP FOR THE SYNC CHIP (R5, Paper page
34
+ // 21 "Sync status"): one quiet line that only speaks up when work is not on
35
+ // the server yet. Saved and Syncing stay grey; Offline turns warn and counts
36
+ // what waits on this device. It opens a popover naming every state, with
37
+ // "Try now" on the reconnect row. A full disk adds `SyncQuotaBanner`.
38
+ import { useEffect, useRef, useState } from "react";
39
+ import type { ReactNode } from "react";
40
+ import { Popover } from "@dailies-studio/ui";
41
+ import { describePieceFailure, pieceLayerLabel } from "@dailies-studio/shot/doc";
42
+ import type { PlaybackResolution, StagePerformanceReport } from "../../bridgeTypes.js";
43
+ import { CHECK_ICON } from "../../icons.js";
44
+ import { useShotEditor, useShotEditorHot, useStore } from "../../state/context.js";
45
+ import type { SyncStatus } from "../../state/sync.js";
46
+ import { SYNC_HOLD_REASON } from "../../state/workspace.js";
47
+
48
+ /** The document's state: a codegen refusal and the save chip. Never on the
49
+ * View > Status badges switch, see `showFpsBadge` in types.ts. */
50
+ export function SaveBadges() {
14
51
  const saveState = useShotEditor((s) => s.saveState);
15
52
  const dirty = useShotEditor((s) => s.dirty);
16
53
  const emitWarning = useShotEditor((s) => s.emitWarning);
17
- const stageInfo = useShotEditor((s) => s.stageInfo);
18
54
  const name = useShotEditor((s) => s.name);
55
+ const sync = useShotEditor((s) => s.syncStatus);
56
+ const save = saveChip(saveState, dirty);
19
57
 
20
58
  return (
21
59
  <div className="status-badges">
22
- {stageInfo === null ? null : (
23
- <span className="badge badge-quiet" title={`${stageInfo.width}x${stageInfo.height}`}>
24
- {stageInfo.backend}
25
- </span>
26
- )}
27
60
  {emitWarning === null ? null : (
28
- <span className="badge badge-warn" title={emitWarning}>
61
+ <span className="status-chip status-chip-warn" title={emitWarning}>
29
62
  codegen
30
63
  </span>
31
64
  )}
32
- {name === null ? null : (
33
- <span className={`badge badge-${saveState}`} title={saveLabel(saveState, dirty)}>
34
- {saveLabel(saveState, dirty)}
65
+ {name === null ? null : sync !== null ? (
66
+ <SyncChip status={sync} />
67
+ ) : save === null ? null : (
68
+ <span className={`status-chip status-chip-${save.tone}`} title={save.text}>
69
+ {/* Saved wears Paper's check: the quiet state, read at a glance. */}
70
+ {save.tone === "saved" ? CHECK_ICON : null}
71
+ {save.text}
35
72
  </span>
36
73
  )}
37
74
  </div>
38
75
  );
39
76
  }
40
77
 
41
- function saveLabel(saveState: string, dirty: boolean): string {
42
- if (saveState === "error") return "save failed";
43
- if (saveState === "saving") return "saving…";
78
+ const changes = (n: number): string => (n === 1 ? "1 change" : `${n} changes`);
79
+
80
+ function glyph(size: 14 | 16, children: ReactNode): ReactNode {
81
+ return (
82
+ <svg
83
+ viewBox={`0 0 ${size} ${size}`}
84
+ fill="none"
85
+ stroke="currentColor"
86
+ strokeWidth={1.4}
87
+ strokeLinecap="round"
88
+ strokeLinejoin="round"
89
+ aria-hidden="true"
90
+ >
91
+ {children}
92
+ </svg>
93
+ );
94
+ }
95
+ const SAVED_ICON = glyph(14, <path d="M3 7.4L5.8 10L11 4.5" />);
96
+ const CLOUD_OFF_ICON = glyph(
97
+ 14,
98
+ <>
99
+ <path d="M4 11h6.2a2.6 2.6 0 0 0 .4-5.17A3.6 3.6 0 0 0 3.7 5.2 2.9 2.9 0 0 0 4 11Z" />
100
+ <path d="M2 2l10 10" />
101
+ </>,
102
+ );
103
+ const SYNCING_ICON = glyph(
104
+ 14,
105
+ <>
106
+ <circle cx="7" cy="7" r="4.8" className="sync-ring" />
107
+ <path d="M7 2.2a4.8 4.8 0 0 1 4.8 4.8" className="sync-arc" />
108
+ </>,
109
+ );
110
+ const RECONNECT_ICON = glyph(
111
+ 14,
112
+ <>
113
+ <path d="M11.5 6.5A4.6 4.6 0 0 0 3.2 4.4M2.5 7.5a4.6 4.6 0 0 0 8.3 2.1" />
114
+ <path d="M3 2v2.6h2.6M11 12V9.4H8.4" />
115
+ </>,
116
+ );
117
+ const CHEVRON = (
118
+ <svg viewBox="0 0 10 10" fill="none" stroke="currentColor" strokeWidth={1.3} strokeLinecap="round" aria-hidden="true" className="sync-chevron">
119
+ <path d="M2.5 4l2.5 2.5L7.5 4" />
120
+ </svg>
121
+ );
122
+ const WARN_ICON = glyph(
123
+ 16,
124
+ <>
125
+ <path d="M8 2.2L14.2 13H1.8L8 2.2Z" />
126
+ <path d="M8 6.5v3" />
127
+ <circle cx="8" cy="11.2" r="0.8" fill="currentColor" stroke="none" />
128
+ </>,
129
+ );
130
+
131
+ type SyncState = "saved" | "syncing" | "offline" | "reconnecting" | "held";
132
+
133
+ /** The one state the sync chip says. */
134
+ export function syncState(s: SyncStatus): SyncState {
135
+ if (s.hold !== null) return "held";
136
+ if (!s.connected) return s.waiting > 0 ? "offline" : "reconnecting";
137
+ return s.waiting > 0 ? "syncing" : "saved";
138
+ }
139
+
140
+ /** Whole seconds until `at`, ticking each second while `live`. */
141
+ function useSecondsUntil(at: number | null, live: boolean): number | null {
142
+ const [now, setNow] = useState(() => Date.now());
143
+ useEffect(() => {
144
+ if (at === null || !live) return;
145
+ setNow(Date.now());
146
+ const id = setInterval(() => setNow(Date.now()), 1000);
147
+ return () => clearInterval(id);
148
+ }, [at, live]);
149
+ return at === null ? null : Math.max(0, Math.ceil((at - now) / 1000));
150
+ }
151
+
152
+ function SyncChip({ status }: { status: SyncStatus }) {
153
+ const store = useStore();
154
+ const anchor = useRef<HTMLButtonElement>(null);
155
+ const [open, setOpen] = useState(false);
156
+ const seconds = useSecondsUntil(status.retryAt, open);
157
+ const state = syncState(status);
158
+ const { waiting, hold } = status;
159
+
160
+ const chip: Record<SyncState, { tone: string; icon: ReactNode; text: string }> = {
161
+ saved: { tone: "saved", icon: SAVED_ICON, text: "saved" },
162
+ syncing: { tone: "saved", icon: SYNCING_ICON, text: "syncing…" },
163
+ offline: { tone: "offline", icon: CLOUD_OFF_ICON, text: `Offline, ${changes(waiting)} saved on this device` },
164
+ reconnecting: { tone: "saved", icon: RECONNECT_ICON, text: "reconnecting…" },
165
+ held: { tone: "error", icon: null, text: hold === "quota" ? "not stored" : "read only" },
166
+ };
167
+ const rows: Array<{ id: SyncState; icon: ReactNode; title: ReactNode; text: string }> = [
168
+ { id: "saved", icon: SAVED_ICON, title: "Saved", text: "Every change is on the server." },
169
+ {
170
+ id: "syncing",
171
+ icon: SYNCING_ICON,
172
+ title: "Syncing",
173
+ text:
174
+ state === "syncing"
175
+ ? `Sending ${changes(waiting)}. Peers see them as they land.`
176
+ : "Changes send as you make them. Peers see them as they land.",
177
+ },
178
+ {
179
+ id: "offline",
180
+ icon: CLOUD_OFF_ICON,
181
+ title: "Offline",
182
+ text: "Keep working. They send when you are back online. Closing the tab is safe.",
183
+ },
184
+ {
185
+ id: "reconnecting",
186
+ icon: RECONNECT_ICON,
187
+ title: (
188
+ <>
189
+ <span>{seconds === null ? "Reconnecting" : `Reconnecting in ${seconds} s`}</span>
190
+ {status.connected || hold !== null ? null : (
191
+ <button type="button" className="sync-try" data-testid="sync-try-now" onClick={() => store.syncRetryNow()}>
192
+ Try now
193
+ </button>
194
+ )}
195
+ </>
196
+ ),
197
+ text: "Retries wait longer each time, with a little jitter.",
198
+ },
199
+ ];
200
+ const c = chip[state];
201
+
202
+ return (
203
+ <>
204
+ <button
205
+ ref={anchor}
206
+ type="button"
207
+ className={`status-chip status-chip-${c.tone} sync-chip`}
208
+ data-testid="sync-chip"
209
+ data-state={state}
210
+ aria-expanded={open}
211
+ title={hold === null ? "Sync status" : SYNC_HOLD_REASON[hold]}
212
+ onClick={() => setOpen((v) => !v)}
213
+ >
214
+ {c.icon}
215
+ {c.text}
216
+ {state === "offline" ? CHEVRON : null}
217
+ </button>
218
+ <Popover open={open} anchor={anchor} onClose={() => setOpen(false)} align="right" className="sync-pop">
219
+ <div className="sync-panel" data-testid="sync-panel">
220
+ <div className="sync-head">
221
+ <span className="sync-title">Sync</span>
222
+ <span className="sync-device">this device · {status.tabs === 1 ? "1 tab" : `${status.tabs} tabs`}</span>
223
+ </div>
224
+ {hold === null ? null : (
225
+ <div className="sync-row is-current is-held">
226
+ <span className="sync-icon">{WARN_ICON}</span>
227
+ <span className="sync-body">
228
+ <span className="sync-row-title">{hold === "quota" ? "Not stored" : "Read only"}</span>
229
+ <span className="sync-row-text">{SYNC_HOLD_REASON[hold]}</span>
230
+ </span>
231
+ </div>
232
+ )}
233
+ {rows.map((row) => (
234
+ <div key={row.id} className={`sync-row${row.id === state ? " is-current" : ""}`} data-row={row.id}>
235
+ <span className="sync-icon">{row.icon}</span>
236
+ <span className="sync-body">
237
+ <span className="sync-row-title">{row.title}</span>
238
+ <span className="sync-row-text">{row.text}</span>
239
+ </span>
240
+ </div>
241
+ ))}
242
+ </div>
243
+ </Popover>
244
+ </>
245
+ );
246
+ }
247
+
248
+ /** "Edits cannot be stored on this device" (Paper page 21): editing is held
249
+ * until the browser takes the waiting changes again. Sticky, no dismiss. */
250
+ export function SyncQuotaBanner() {
251
+ const store = useStore();
252
+ const status = useShotEditor((s) => s.syncStatus);
253
+ const [busy, setBusy] = useState(false);
254
+ if (status?.hold !== "quota") return null;
255
+ const retry = (prune: boolean): void => {
256
+ setBusy(true);
257
+ void store.syncRetryStorage(prune).finally(() => setBusy(false));
258
+ };
259
+ const n = status.waiting;
260
+ return (
261
+ <div className="sync-banner" role="alert" data-testid="sync-quota">
262
+ <span className="sync-banner-icon">{WARN_ICON}</span>
263
+ <span className="sync-banner-body">
264
+ <span className="sync-banner-title">Edits cannot be stored on this device</span>
265
+ <span className="sync-banner-text">
266
+ The browser is out of space. Editing is paused so nothing is lost.
267
+ {n > 0 ? ` Your ${changes(n)} saved ${n === 1 ? "is" : "are"} safe.` : ""}
268
+ </span>
269
+ </span>
270
+ <button type="button" className="sync-banner-free" disabled={busy} onClick={() => retry(true)}>
271
+ Free up space
272
+ </button>
273
+ <button type="button" className="sync-banner-retry" disabled={busy} onClick={() => retry(false)}>
274
+ Try again
275
+ </button>
276
+ </div>
277
+ );
278
+ }
279
+
280
+ /** The picture's state: the presented rate while playing, and the backend.
281
+ * Both answer to View > Status badges. */
282
+ export function StageBadges() {
283
+ const backend = useShotEditor((s) => s.stageInfo?.backend ?? null);
284
+ const playing = useShotEditor((s) => s.playing);
285
+ // Hot: a report written while the transport plays wakes no cold reader.
286
+ const perf = useShotEditorHot((s) => s.stagePerf);
287
+ const showFps = useShotEditor((s) => s.showFpsBadge);
288
+ const resolution = useShotEditor((s) => s.playbackResolution);
289
+ const showBackend = useShotEditor((s) => s.showBackendBadge);
290
+ const failures = useShotEditor((s) => s.stagePieceFailures);
291
+ const renderErrors = useShotEditor((s) => s.stageRenderErrors);
292
+ const doc = useShotEditor((s) => s.doc);
293
+ // ONE chip for everything the stage could not draw: an isolated piece and a
294
+ // render fallback (an undecodable html layer, a font, a texture) are the
295
+ // same news to the author, and the same refusal at export.
296
+ const broken = failures.length + renderErrors.length;
297
+
298
+ return (
299
+ <div className="stage-badges" data-testid="stage-badges">
300
+ {broken === 0 ? null : (
301
+ <span
302
+ className="status-chip status-chip-error"
303
+ data-testid="piece-failures-badge"
304
+ role="status"
305
+ title={[
306
+ ...failures.map((failure) => describePieceFailure(failure, pieceLayerLabel(doc, failure))),
307
+ ...renderErrors,
308
+ ].join("\n")}
309
+ >
310
+ {broken === 1 ? "1 broken piece" : `${broken} broken pieces`}
311
+ </span>
312
+ )}
313
+ {showFps && playing && perf !== null ? (
314
+ <span
315
+ className="status-chip status-chip-quiet status-chip-fps"
316
+ data-testid="fps-badge"
317
+ data-render-scale={perf.renderScale}
318
+ title={fpsTitle(perf, resolution)}
319
+ >
320
+ {Math.round(perf.presentedFps)} fps
321
+ {perf.renderScale < 1 ? ` · ${Math.round(perf.renderScale * 100)}%` : null}
322
+ </span>
323
+ ) : null}
324
+ {backend === null || !showBackend ? null : (
325
+ <span
326
+ className="status-chip status-chip-quiet"
327
+ data-testid="backend-badge"
328
+ title={`preview backend: ${backend}. Exports always render on webgl.`}
329
+ >
330
+ {backend}
331
+ </span>
332
+ )}
333
+ </div>
334
+ );
335
+ }
336
+
337
+ /** The fps chip's title: the presented rate, the GPU's time when the stage
338
+ * measured it, and while the picture renders below the display's density,
339
+ * how far and why (bridge v52). */
340
+ export function fpsTitle(perf: StagePerformanceReport, resolution: PlaybackResolution): string {
341
+ const rate =
342
+ `Presented ${Math.round(perf.presentedFps)} of ${Math.round(perf.expectedFps)} expected frames ` +
343
+ `in the last second, ${perf.droppedPublications} dropped.`;
344
+ const gpu = perf.gpuMs === null ? "" : ` The GPU took ${perf.gpuMs.toFixed(1)} ms a frame (p95).`;
345
+ if (!(perf.renderScale < 1)) return rate + gpu;
346
+ const why = resolution === "fast" ? "Playback resolution is Fast" : "the GPU could not keep up";
347
+ return (
348
+ `${rate}${gpu} Rendering at ${Math.round(perf.renderScale * 100)}% of the display's resolution while ` +
349
+ `playing, because ${why}. Paused frames and exports are always full resolution.`
350
+ );
351
+ }
352
+
353
+ /** The chip's word AND its tone, or NULL when there is nothing to say.
354
+ *
355
+ * ONE FUNCTION FOR BOTH, because they disagreed. The word has always come
356
+ * from `dirty` first and the class was written straight off `saveState`, so
357
+ * the ordinary state after a first autosave — `saveState: "saved"` with a
358
+ * pending edit — rendered the word "unsaved" wearing `.status-chip-saved`. That
359
+ * cost nothing while `.status-chip-saved` had no rule; the moment it got the quiet
360
+ * treatment it deserves, the one chip an author must not miss became the
361
+ * faintest thing on the bar. The tone now falls out of the same branch that
362
+ * picks the word, so the two cannot drift again.
363
+ *
364
+ * Null is a state the chip used to spell "—": a freshly opened, untouched
365
+ * document sat behind a bordered pill containing an em-dash, which is a
366
+ * control-shaped object that reads as information and carries none. Silence
367
+ * is the honest rendering of "saved, and nothing has happened since" — the
368
+ * chip returns the moment there is a word for it, which is what makes its
369
+ * appearance mean something. */
370
+ type SaveChip = { tone: "error" | "saving" | "idle" | "saved"; text: string };
371
+
372
+ function saveChip(saveState: string, dirty: boolean): SaveChip | null {
373
+ if (saveState === "error") return { tone: "error", text: "save failed" };
374
+ if (saveState === "saving") return { tone: "saving", text: "saving…" };
44
375
  // "saved" plus pending edits is not saved — the badge follows the DOCUMENT,
45
376
  // not the last request, or an autosave debounce reads as "everything is on
46
377
  // disk" for the whole 400ms an author might close the tab in.
47
- if (dirty) return "unsaved";
48
- return saveState === "saved" ? "saved" : "—";
378
+ if (dirty) return { tone: "idle", text: "unsaved" };
379
+ return saveState === "saved" ? { tone: "saved", text: "saved" } : null;
49
380
  }
@@ -0,0 +1,88 @@
1
+ // A Shot deleted under live sync while this tab had changes the server never
2
+ // got (R10, Paper page 21 "Renamed or deleted"). The editor is read only
3
+ // behind it, and the two ways on are the only ways out: "Restore" brings the
4
+ // Shot back and its session replays the changes, "Save as copy" makes them a
5
+ // new Shot. With nothing pending there is no dialog: the Shot closes and the
6
+ // error row says who deleted it.
7
+ import type { ReactNode } from "react";
8
+ import { Dialog, initialsOf } from "@dailies-studio/ui";
9
+ import { useShotEditor, useStore } from "../../state/context.js";
10
+
11
+ const plural = (n: number, one: string): string => `${n} ${one}${n === 1 ? "" : "s"}`;
12
+
13
+ export function SyncDeletedDialog(): ReactNode {
14
+ const store = useStore();
15
+ const d = useShotEditor((s) => s.syncDeleted);
16
+ if (d === null) return null;
17
+ const changes = plural(d.count, "change");
18
+ return (
19
+ <Dialog
20
+ open
21
+ className="sync-deleted"
22
+ dismissOnOverlay={false}
23
+ dismissOnEscape={false}
24
+ onClose={() => {}}
25
+ title={
26
+ <>
27
+ {d.by === null ? null : (
28
+ <span className="sync-face is-lg" style={{ background: d.color ?? undefined }} aria-hidden="true">
29
+ {initialsOf(d.by)}
30
+ </span>
31
+ )}
32
+ <span>{d.by === null ? `"${d.title}" was deleted` : `${d.by} deleted "${d.title}"`}</span>
33
+ </>
34
+ }
35
+ actions={
36
+ <>
37
+ <button
38
+ type="button"
39
+ className="sync-deleted-btn"
40
+ disabled={d.busy || d.trashId === null}
41
+ onClick={() => void store.saveDeletedAsCopy()}
42
+ >
43
+ Save as copy
44
+ </button>
45
+ <button
46
+ type="button"
47
+ className="sync-deleted-btn is-primary"
48
+ disabled={d.busy || d.trashId === null}
49
+ onClick={() => void store.restoreDeleted()}
50
+ >
51
+ Restore
52
+ </button>
53
+ </>
54
+ }
55
+ >
56
+ <p className="sync-deleted-text">
57
+ It is in Trash now. You have {changes} that never reached the server. Keep them by restoring the Shot, or by
58
+ saving them as a new copy.
59
+ </p>
60
+ <div className="sync-deleted-pending" data-testid="sync-deleted-pending">
61
+ <div className="sync-deleted-label">Your {d.count} pending {d.count === 1 ? "change" : "changes"}</div>
62
+ <pre>{d.lines.join("\n")}</pre>
63
+ </div>
64
+ {d.error === null ? null : (
65
+ <p className="dialog-error" role="alert">
66
+ {d.error}
67
+ </p>
68
+ )}
69
+ </Dialog>
70
+ );
71
+ }
72
+
73
+ /** "Renamed to X by Maya", beside the title until it clears itself. */
74
+ export function SyncRenamedNote(): ReactNode {
75
+ const note = useShotEditor((s) => s.syncRenamed);
76
+ if (note === null) return null;
77
+ return (
78
+ <span className="sync-renamed" role="status" data-testid="sync-renamed">
79
+ {note.by === null ? null : (
80
+ <span className="sync-face" style={{ background: note.color ?? undefined }} aria-hidden="true">
81
+ {initialsOf(note.by)}
82
+ </span>
83
+ )}
84
+ Renamed to {note.to}
85
+ {note.by === null ? "" : ` by ${note.by}`}
86
+ </span>
87
+ );
88
+ }
@@ -1,13 +1,27 @@
1
+ import { IconMinus, IconPlus } from "@dailies-studio/ui";
1
2
  import { useShotEditor, useStore } from "../../state/context.js";
3
+ import { layoutSpec } from "../../state/layouts.js";
4
+ import { shortcutHint } from "../../state/shortcuts.js";
2
5
 
3
- /** Zoom readout, plus fit. Routed by the ACTIVE pane, exactly as ⌘+/⌘−/⇧Z
4
- * are: in a split the buttons address the pane the author last touched, and
5
- * in working mode they dolly the working view instead of silently writing a
6
- * camera-pane zoom nobody can see. The readout is a percentage only for the
7
- * camera pane — "100%" has no meaning for an orbit camera, so the working
8
- * pane shows which view the controls are pointed at instead. The stage OWNS
9
- * the views (it knows the pane sizes), so "fit" asks the stage and mirrors
10
- * what it answers rather than computing a second opinion here. */
6
+ /** Zoom readout, plus fit, at the right end of the canvas bar (CanvasBar.tsx).
7
+ * In the canvas bar and not the top bar because it acts on the canvas, and
8
+ * because `]`, `.` and Video hide columns but never that bar.
9
+ *
10
+ * Routed by the ACTIVE pane, exactly as ⌘+/⌘− and the
11
+ * fit key are: in a split the buttons address the pane the author last
12
+ * touched, and in working mode they dolly the working view instead of
13
+ * silently writing a camera-pane zoom nobody can see. The readout is a
14
+ * percentage only for the camera pane — "100%" has no meaning for an orbit
15
+ * camera, so the working pane shows which view the controls are pointed at
16
+ * instead. The stage OWNS the views (it knows the pane sizes), so "fit" asks
17
+ * the stage and mirrors what it answers rather than computing a second
18
+ * opinion here.
19
+ *
20
+ * THE HINT COMES OFF THE TABLE, NOT OFF A LITERAL. It said ⇧Z for a fit that
21
+ * moved to Home, which is the exact drift shortcuts.ts's header cites as the
22
+ * reason that table exists — and it went on saying it after the table landed
23
+ * because nothing here read from it. `shortcutFor` is the same lookup the menu
24
+ * accelerators use, so a rebind moves this tooltip too. */
11
25
  export function ZoomControls() {
12
26
  const store = useStore();
13
27
  // The ZOOM alone, never the whole view object: `onViewChange` fires per
@@ -16,22 +30,39 @@ export function ZoomControls() {
16
30
  const zoom = useShotEditor((s) => s.view.zoom);
17
31
  const working = useShotEditor((s) => s.activePane === "working");
18
32
  const open = useShotEditor((s) => s.name !== null);
33
+ // THE STAGE'S ZOOM ONLY. It reports `state.view.zoom`, the stage camera's,
34
+ // and drives `zoomActivePane`, so it lives in the stage workspace's canvas
35
+ // bar (CanvasBar renders it nowhere else). A mode brings its own zoom, about
36
+ // its own canvas: two percentages about two things would never agree.
19
37
  if (!open) return null;
38
+ const fitHint = shortcutHint("view.fit");
20
39
  return (
21
40
  <div className="zoom-controls">
22
- <button type="button" title="Zoom out" onClick={() => store.zoomActivePane(-1)}>
23
- −
41
+ <button
42
+ type="button"
43
+ className="btn is-icon"
44
+ aria-label="Zoom out"
45
+ title={`Zoom out${shortcutHint("view.zoomOut")}`}
46
+ onClick={() => store.zoomActivePane(-1)}
47
+ >
48
+ <IconMinus />
24
49
  </button>
25
50
  <button
26
51
  type="button"
27
- className="zoom-readout"
28
- title={working ? "Fit the working view (⇧Z)" : "Fit (⇧Z)"}
52
+ className="btn zoom-readout"
53
+ title={working ? `Fit the working view${fitHint}` : `Fit${fitHint}`}
29
54
  onClick={() => store.fitActivePane()}
30
55
  >
31
- {working ? "3D" : `${Math.round(zoom * 100)}%`}
56
+ {working ? layoutSpec("working").label : `${Math.round(zoom * 100)}%`}
32
57
  </button>
33
- <button type="button" title="Zoom in" onClick={() => store.zoomActivePane(1)}>
34
- +
58
+ <button
59
+ type="button"
60
+ className="btn is-icon"
61
+ aria-label="Zoom in"
62
+ title={`Zoom in${shortcutHint("view.zoomIn")}`}
63
+ onClick={() => store.zoomActivePane(1)}
64
+ >
65
+ <IconPlus />
35
66
  </button>
36
67
  </div>
37
68
  );
@@ -0,0 +1,52 @@
1
+ // The prompt the Agent chip copies for an outside agent (Claude Code over the
2
+ // dailies MCP server): what "this" means right now, as ids the MCP tools take.
3
+ //
4
+ // Built from `buildShotAgentContext`, the in-app agent's own context, so the
5
+ // two agents are told the same thing about the same moment: the storage name,
6
+ // the entered definition, the Shot, the frame (def-local inside a definition),
7
+ // the selected layer and, on the board, the planned shot, the selected
8
+ // reference and the board subject (a still, a gap or a cut), and on a shot
9
+ // board the open sketch and its selected item. The
10
+ // document itself is never pasted: the agent reads it fresh through the tools.
11
+ import { buildShotAgentContext } from "../../state/agentActions.js";
12
+ import type { ShotEditorState } from "../../state/types.js";
13
+
14
+ interface ShotContext {
15
+ docName?: string;
16
+ editingDef?: string;
17
+ frame?: number;
18
+ sceneId?: string;
19
+ selectedLayerId?: string;
20
+ storyboardOpen?: boolean;
21
+ selectedPartId?: string;
22
+ selectedReferenceId?: string;
23
+ boardSubject?: string;
24
+ sketchId?: string;
25
+ selectedSketchItemId?: string;
26
+ }
27
+
28
+ /** Null with no Project open: there is nothing for the agent to target. */
29
+ export function agentHandoffPrompt(s: ShotEditorState): string | null {
30
+ const { shot } = buildShotAgentContext(s) as { shot?: ShotContext };
31
+ if (shot?.docName === undefined) return null;
32
+ const title = s.doc?.title ?? shot.docName;
33
+ const lines = [
34
+ `Use the dailies MCP server to work on the Dailies Project "${title}" (storage name: ${shot.docName}).`,
35
+ ];
36
+ if (shot.editingDef !== undefined) lines.push(`I am inside the component definition ${shot.editingDef}.`);
37
+ if (shot.sceneId !== undefined) lines.push(`Shot: ${shot.sceneId}`);
38
+ if (shot.frame !== undefined) {
39
+ lines.push(`Frame: ${shot.frame}${shot.editingDef !== undefined ? " (on the definition's own clock)" : ""}`);
40
+ }
41
+ if (shot.selectedLayerId !== undefined) lines.push(`Selected layer: ${shot.selectedLayerId}`);
42
+ if (shot.storyboardOpen === true) {
43
+ lines.push("The Storyboard is open: read get_storyboard before changing Shots.");
44
+ if (shot.selectedPartId !== undefined) lines.push(`Planned shot: ${shot.selectedPartId}`);
45
+ if (shot.selectedReferenceId !== undefined) lines.push(`Selected reference: ${shot.selectedReferenceId}`);
46
+ if (shot.boardSubject !== undefined) lines.push(`Board subject: ${shot.boardSubject}`);
47
+ if (shot.sketchId !== undefined) lines.push(`Sketch: ${shot.sketchId} (screenshot_shot sketch shows it)`);
48
+ if (shot.selectedSketchItemId !== undefined) lines.push(`Selected sketch item: ${shot.selectedSketchItemId}`);
49
+ }
50
+ lines.push("", "Task: ");
51
+ return lines.join("\n");
52
+ }
@@ -0,0 +1,26 @@
1
+ import { flushPoster } from "../../state/posterCapture.js";
2
+ import type { ShotEditorStore } from "../../state/store.js";
3
+
4
+ /**
5
+ * Leave the editor for `href`: FLUSH FIRST, AND ONLY LEAVE IF IT LANDED.
6
+ *
7
+ * Every way out (the app menu's account rows, the `Projects` crumb, the
8
+ * avatar) leaves the page, and the autosave debounce is measured in seconds,
9
+ * so the last edit is routinely still in flight when one is picked.
10
+ *
11
+ * This was `.finally` once, which navigates whether the flush RESOLVED or
12
+ * REJECTED, so a save that failed took the edit with it. A rejection leaves the
13
+ * author exactly where they were, with the document still dirty and the
14
+ * header's save badge still saying so, which is the whole explanation of why
15
+ * the click appeared to do nothing.
16
+ *
17
+ * Then the POSTER the save just made owed (hosted only; bounded, and it never
18
+ * rejects): its timer would otherwise die with the page, and the card for the
19
+ * work just done would keep an older picture.
20
+ */
21
+ export function leaveFor(store: Pick<ShotEditorStore, "flush">, href: string): void {
22
+ void store.flush().then(
23
+ () => flushPoster().then(() => window.location.assign(href)),
24
+ () => {},
25
+ );
26
+ }