@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,38 @@
1
+ // THE DOCK'S + : it opens and closes the Insert drawer (InsertDrawer.tsx),
2
+ // which lists every insert a drag cannot express (a file, a registry choice,
3
+ // an operation on the selection) beside the icons, shaders and backgrounds.
4
+ //
5
+ // It also hosts the media tool's file claim. The media tool draws its box on
6
+ // the stage and only then needs a file, so its geometry parks in the store
7
+ // (`imagePick`) and a mounted `useAddRows` with the claim opens the dialog for
8
+ // it. The dock is mounted whenever the media tool can be armed, so the claim
9
+ // lives here and not in the drawer, which is often closed.
10
+ //
11
+ // A VIEWER sees no +: there is no insert for them to make. The claim's input
12
+ // stays mounted, since it costs nothing and a read-only flip mid-pick must not
13
+ // strand the dialog's answer.
14
+ import { useShotEditor, useStore } from "../../state/context.js";
15
+ import { ADD_ICON } from "../../icons.js";
16
+ import { ToolButton } from "./ToolDockBar.js";
17
+ import { useAddRows } from "./useAddRows.js";
18
+
19
+ export function InsertButton() {
20
+ const store = useStore();
21
+ const inserting = useShotEditor((s) => s.insertDrawer !== null);
22
+ const viewer = useShotEditor((s) => s.readOnly);
23
+ const { busy, fileInput } = useAddRows({ wantLibrary: false });
24
+ return (
25
+ <>
26
+ {viewer ? null : (
27
+ <ToolButton
28
+ armed={inserting}
29
+ refusal={busy ? "Waiting for the last file to finish importing" : null}
30
+ title="Insert: shapes, icons, shaders and backgrounds"
31
+ icon={ADD_ICON}
32
+ onClick={() => store.toggleInsertDrawer()}
33
+ />
34
+ )}
35
+ {fileInput}
36
+ </>
37
+ );
38
+ }
@@ -0,0 +1,156 @@
1
+ // Where the author is while isolated, over the canvas they are looking at.
2
+ //
3
+ // It replaces the stage's old "INSIDE X. ESC TO LEAVE" strip (a 22px accent
4
+ // bar across the top of the pane) and the canvas bar's one-level back chip.
5
+ // One trail says the mode, the path, and every way out:
6
+ //
7
+ // Shot › … › Group › [icon] line Not on screen Done
8
+ //
9
+ // - The shot crumb and Done leave isolation entirely (`exitIsolationAll`).
10
+ // - An ancestor crumb climbs to that level with the store's one-level verb,
11
+ // once per level. `enterIsolation(ancestor)` only looks like it: it selects
12
+ // the ancestor instead of the group just left.
13
+ // - The current crumb does nothing on click: it is where the author is.
14
+ // - Past two ancestors, all but the nearest fold into a `…` menu, so the bar
15
+ // keeps its width at any depth.
16
+ //
17
+ // Done names no key on purpose: Escape deselects first and climbs one level,
18
+ // while Done leaves every level at once. See BackChip.tsx for the same call.
19
+ import { useContextMenu } from "@dailies-studio/ui";
20
+ import { useRef } from "react";
21
+ import { CHEVRON_RIGHT_ICON, iconFor, isComponentGlyph } from "../../icons.js";
22
+ import { useShotEditor, useShotEditorHot, useStore } from "../../state/context.js";
23
+ import { crumbsFor, isolationOffscreen } from "../../state/isolationTrail.js";
24
+
25
+ /** Ancestors shown inline before the rest fold into the `…` menu. */
26
+ const INLINE_ANCESTORS = 2;
27
+
28
+ export function IsolationTrail() {
29
+ const store = useStore();
30
+ // The doc reference moves on edits only, never on playback frames, so this
31
+ // re-renders when a label or the tree changes and not 60 times a second.
32
+ const doc = useShotEditor((s) => s.doc);
33
+ const sceneId = useShotEditor((s) => s.sceneId);
34
+ const isolation = useShotEditor((s) => (s.editingDef === null ? s.isolation : null));
35
+ const { open, menu } = useContextMenu();
36
+ const moreRef = useRef<HTMLButtonElement | null>(null);
37
+
38
+ const scene = doc === null || sceneId === null ? undefined : doc.scenesById[sceneId];
39
+ const layer = isolation === null ? undefined : scene?.layersById[isolation];
40
+ if (scene === undefined || layer === undefined || isolation === null) return null;
41
+
42
+ const trail = crumbsFor(scene, isolation);
43
+ const ancestors = trail.slice(0, -1);
44
+ const folded = ancestors.length > INLINE_ANCESTORS ? ancestors.slice(0, -1) : [];
45
+ const inline = folded.length > 0 ? ancestors.slice(-1) : ancestors;
46
+ const labelOf = (id: string): string => scene.layersById[id]?.label ?? id;
47
+ /** Up to `id`, one level per call, the way Escape climbs. */
48
+ const climbTo = (id: string): void => {
49
+ const levels = trail.length - 1 - trail.indexOf(id);
50
+ for (let n = 0; n < levels; n += 1) store.exitIsolation();
51
+ };
52
+ const showFolded = (): void => {
53
+ const el = moreRef.current;
54
+ if (el === null) return;
55
+ const r = el.getBoundingClientRect();
56
+ open(
57
+ { x: r.left, y: r.bottom + 6 },
58
+ folded.map((id) => ({
59
+ kind: "action" as const,
60
+ id: `isolation.crumb.${id}`,
61
+ label: labelOf(id),
62
+ onSelect: () => climbTo(id),
63
+ })),
64
+ );
65
+ };
66
+ const sep = (
67
+ <span className="iso-trail-sep" aria-hidden="true">
68
+ {CHEVRON_RIGHT_ICON}
69
+ </span>
70
+ );
71
+
72
+ return (
73
+ <>
74
+ <nav className="iso-trail" aria-label="Isolation path" data-testid="isolation-trail">
75
+ <button
76
+ type="button"
77
+ className="iso-crumb"
78
+ data-testid="trail-shot"
79
+ title={`Back to ${scene.name}`}
80
+ onClick={() => store.exitIsolationAll()}
81
+ >
82
+ {scene.name}
83
+ </button>
84
+ {folded.length === 0 ? null : (
85
+ <span className="iso-trail-step">
86
+ {sep}
87
+ <button
88
+ ref={moreRef}
89
+ type="button"
90
+ className={`iso-crumb is-more${menu !== null ? " is-open" : ""}`}
91
+ aria-haspopup="menu"
92
+ aria-expanded={menu !== null}
93
+ aria-label={`${folded.length} more levels`}
94
+ title={folded.map(labelOf).join(" › ")}
95
+ data-testid="trail-more"
96
+ onClick={showFolded}
97
+ >
98
+ …
99
+ </button>
100
+ {menu}
101
+ </span>
102
+ )}
103
+ {inline.map((id) => (
104
+ <span key={id} className="iso-trail-step">
105
+ {sep}
106
+ <button
107
+ type="button"
108
+ className="iso-crumb"
109
+ title={`Back to ${labelOf(id)}`}
110
+ onClick={() => climbTo(id)}
111
+ >
112
+ {labelOf(id)}
113
+ </button>
114
+ </span>
115
+ ))}
116
+ {sep}
117
+ <span className="iso-crumb is-current" aria-current="location" data-testid="trail-current">
118
+ <span
119
+ className={`iso-crumb-icon${isComponentGlyph(layer) ? " is-component" : ""}`}
120
+ aria-hidden="true"
121
+ >
122
+ {iconFor(layer)}
123
+ </span>
124
+ <span className="iso-crumb-label">{layer.label}</span>
125
+ </span>
126
+ <OffscreenChip />
127
+ </nav>
128
+ <button
129
+ type="button"
130
+ className="btn is-ghost iso-done"
131
+ data-testid="isolation-done"
132
+ title={`Leave isolation, back to ${scene.name}`}
133
+ onClick={() => store.exitIsolationAll()}
134
+ >
135
+ Done
136
+ </button>
137
+ </>
138
+ );
139
+ }
140
+
141
+ /** The playhead moves on the clock tick, which `useShotEditor` is deaf to, so
142
+ * this leaf alone subscribes hot. The selector is a boolean: it re-renders
143
+ * only when the answer flips, never per frame. */
144
+ function OffscreenChip() {
145
+ const offscreen = useShotEditorHot(isolationOffscreen);
146
+ if (!offscreen) return null;
147
+ return (
148
+ <span
149
+ className="status-chip status-chip-quiet iso-offscreen"
150
+ data-testid="trail-offscreen"
151
+ title="Nothing in this layer is drawing at the playhead"
152
+ >
153
+ Not on screen
154
+ </span>
155
+ );
156
+ }
@@ -0,0 +1,86 @@
1
+ // Show and hide the three panels, from the canvas bar.
2
+ //
3
+ // ON THE CANVAS BAR, not on the panels themselves: a control that lives on the
4
+ // thing it hides disappears with it. The canvas cell never collapses, so the
5
+ // bar is the one place that can always bring a panel back.
6
+ //
7
+ // ONE TRIGGER, NOT THREE BUTTONS (Paper 27, "7 · Canvas bar"). Three glyph
8
+ // toggles cost the bar ~92px it needed for the plane and shading controls,
9
+ // and their state was a fill the eye had to decode. The menu says it in words
10
+ // with a check per panel, plus "Every panel", and shows each row's key, read
11
+ // off the shortcut table like every other accelerator in the chrome.
12
+ //
13
+ // The rows are built at OPEN time from `getState()` (StageMenu's rule), so a
14
+ // key pressed while the menu is closed is reflected the next time it opens.
15
+ import { useContextMenu, type MenuEntry } from "@dailies-studio/ui";
16
+ import { useRef } from "react";
17
+ import { CHEVRON_DOWN_ICON, PANELS_ICON } from "../../icons.js";
18
+ import { useShotEditor, useStore } from "../../state/context.js";
19
+ import { shortcutFor, shortcutHint } from "../../state/shortcuts.js";
20
+
21
+ export function PanelToggles() {
22
+ const store = useStore();
23
+ const docOpen = useShotEditor((s) => s.name !== null);
24
+ const { open, menu } = useContextMenu();
25
+ const buttonRef = useRef<HTMLButtonElement | null>(null);
26
+ // DERIVED from `menu`, never mirrored: see AppMenu.
27
+ const isOpen = menu !== null;
28
+ if (!docOpen) return null;
29
+
30
+ const row = (menuId: string, label: string, on: boolean, run: () => void): MenuEntry => ({
31
+ kind: "action",
32
+ id: menuId,
33
+ label,
34
+ checked: on,
35
+ shortcut: shortcutFor(menuId),
36
+ onSelect: run,
37
+ });
38
+
39
+ const show = (): void => {
40
+ const el = buttonRef.current;
41
+ if (el === null) return;
42
+ const s = store.getState();
43
+ const r = el.getBoundingClientRect();
44
+ open(
45
+ { x: r.left, y: r.bottom + 6 },
46
+ [
47
+ { kind: "separator", heading: "Panels" },
48
+ row("view.panel.rail", "Layers", !s.railCollapsed, () => store.setRailCollapsed(!s.railCollapsed)),
49
+ row("view.panel.dock", "Timeline", !s.dockCollapsed, () => store.setDockCollapsed(!s.dockCollapsed)),
50
+ row("view.panel.inspector", "Inspector", !s.inspectorCollapsed, () =>
51
+ store.setInspectorCollapsed(!s.inspectorCollapsed),
52
+ ),
53
+ { kind: "separator" },
54
+ row(
55
+ "view.panel.all",
56
+ "Every panel",
57
+ !s.railCollapsed && !s.dockCollapsed && !s.inspectorCollapsed,
58
+ () => store.togglePanels(),
59
+ ),
60
+ ],
61
+ { lanes: "always" },
62
+ );
63
+ };
64
+
65
+ return (
66
+ <div className="panel-toggles">
67
+ <button
68
+ ref={buttonRef}
69
+ type="button"
70
+ className={`tool-button panel-menu-trigger${isOpen ? " is-open" : ""}`}
71
+ aria-haspopup="menu"
72
+ aria-expanded={isOpen}
73
+ aria-label="Panels"
74
+ title={`Panels${shortcutHint("view.panel.all")}`}
75
+ data-testid="panel-menu-trigger"
76
+ onClick={show}
77
+ >
78
+ {PANELS_ICON}
79
+ <span className="panel-menu-caret" aria-hidden="true">
80
+ {CHEVRON_DOWN_ICON}
81
+ </span>
82
+ </button>
83
+ {menu}
84
+ </div>
85
+ );
86
+ }
@@ -1,42 +1,135 @@
1
1
  // How the WORKING pane draws: solid clay, wireframe, real materials, or the
2
2
  // graded render from the working camera.
3
3
  //
4
- // A floating sibling like ViewModeBar, and its structural twin: a table of
5
- // modes so glyph, label and cycle order live beside each other. It renders
6
- // NOTHING when the layout is camera-only — the camera pane is always the
7
- // graded render, so with no working pane on screen there is nothing this bar
8
- // addresses and an armed button over an invisible pane would be a lie.
4
+ // A table of GLYPHS, and nothing else: the words and the cycle order come from
5
+ // `stage/shading.ts`, which is where the App menu reads them too. It renders
6
+ // NOTHING when the layout is camera-only — the camera pane is always the graded
7
+ // render, so with no working pane on screen there is nothing this bar addresses
8
+ // and an armed button over an invisible pane would be a lie. It sits in the
9
+ // canvas bar's "3D" group (CanvasBar.tsx), which names the pane.
10
+ //
11
+ // ── ONE BUTTON, NOT FOUR, AND ITS OWN CLASS ─────────────────────────────
12
+ //
13
+ // This used to be four 36px glyph buttons wearing `view-mode-bar`, the
14
+ // view-mode segmented control's own class, borrowed for its layout and
15
+ // carrying that control's track and surface with it. It also cost 160px of a
16
+ // bar that does not wrap.
17
+ //
18
+ // So: one trigger showing what the pane is doing now, and the other three a
19
+ // press away. A mode picker IS one control reporting one value, and four
20
+ // mutually exclusive glyphs only read as four choices to someone who already
21
+ // knows the vocabulary. Every mode keeps its own title text, so "Z cycles"
22
+ // stays discoverable on the rows where the modes are.
23
+ //
24
+ // ── THE MENU PORTALS, IT DOES NOT FLY OUT ───────────────────────────────
25
+ //
26
+ // The reason used to be that `.control-strip` carried `overflow: clip` and
27
+ // would clip an absolutely-positioned child. IT NO LONGER DOES — that rule is
28
+ // gone, because it was clipping the dock's own shapes flyout and the whole Add
29
+ // menu into invisible, unclickable boxes. So `.tool-flyout` would work here.
30
+ //
31
+ // It still portals, and the reason is now the one that was always the better
32
+ // one: `.tool-flyout` is CSS and nothing else — it opens where its anchor is,
33
+ // stays there, and closes only from inside itself (its trigger again, or one
34
+ // of its own rows). Nothing outside it can dismiss it. This menu
35
+ // sits mid-strip over the stage, so it needs the behaviours `Popover` brings
36
+ // and the flyout has none of: it closes on an outside pointerdown and on
37
+ // Escape; it flips above the trigger when the window is short; and it clamps
38
+ // its LEFT EDGE on-screen. Not on blur — `Popover.tsx`'s own header says
39
+ // "outside pointerdown / Escape / blur" and the file registers two listeners,
40
+ // neither of them blur; and the clamp is horizontal only, which is what the
41
+ // flip is for. A menu left open under the author's next click on
42
+ // the picture is the failure the dock's flyout still has and this one does not.
43
+ import { useRef, useState } from "react";
44
+ import { Popover } from "@dailies-studio/ui";
9
45
  import { useShotEditor, useStore } from "../../state/context.js";
10
- import { SHADING_MODES } from "../../stage/shading.js";
46
+ import { inDefinition, shownLayoutMode } from "../../state/viewMode.js";
47
+ // `shadingLabel` and not four labels of our own: this bar hand-wrote the same
48
+ // words the App menu prints, so renaming a mode moved one and left the other.
49
+ import { SHADING_MODES, shadingLabel } from "../../stage/shading.js";
11
50
  import type { ShadingMode } from "../../bridgeTypes.js";
12
51
 
13
- const SHADING_GLYPHS: Readonly<Record<ShadingMode, { glyph: string; label: string }>> = {
14
- solid: { glyph: "◯", label: "Solid" },
15
- wireframe: { glyph: "◇", label: "Wireframe" },
16
- material: { glyph: "◑", label: "Material" },
17
- rendered: { glyph: "✦", label: "Rendered" },
52
+ /** The face for each mode. The drawing is this file's; the word is not. */
53
+ const SHADING_GLYPHS: Readonly<Record<ShadingMode, string>> = {
54
+ solid: "◯",
55
+ wireframe: "◇",
56
+ material: "◑",
57
+ rendered: "✦",
18
58
  };
19
59
 
60
+ /** One hover string for a mode, worn by the trigger and by that mode's row. */
61
+ function titleFor(mode: ShadingMode): string {
62
+ return `${shadingLabel(mode)}: working pane (Z cycles)`;
63
+ }
64
+
20
65
  export function ShadingBar() {
21
66
  const store = useStore();
22
67
  const shading = useShotEditor((s) => s.shadingMode);
23
- const layoutMode = useShotEditor((s) => s.stageLayout.mode);
68
+ // ON SCREEN, not chosen: see CameraViewBar.
69
+ const layoutMode = useShotEditor(shownLayoutMode);
70
+ // A definition always draws the working pane alone (StageApp forces it),
71
+ // whatever layout the shot left stashed. WorkPlaneBar keeps the same rule.
72
+ const inDef = useShotEditor(inDefinition);
24
73
  const open = useShotEditor((s) => s.name !== null);
25
- if (!open || layoutMode === "camera") return null;
74
+ const anchor = useRef<HTMLButtonElement>(null);
75
+ const [menuOpen, setMenuOpen] = useState(false);
76
+ if (!open || (layoutMode === "camera" && !inDef)) {
77
+ // ── AND THE GATE CLOSES THE MENU ──────────────────────────────────
78
+ //
79
+ // Returning null is not an unmount: React keeps this component and its
80
+ // state, so a menu left open when the layout went camera-only came back
81
+ // OPEN the next time the working pane did — a popover over the picture
82
+ // that nobody pressed, reporting `aria-expanded="true"` nobody set.
83
+ // Setting state during this component's own render is React's documented
84
+ // answer to "reset state when an input changes": it re-renders before
85
+ // anything is committed, so there is no extra paint and no effect.
86
+ if (menuOpen) setMenuOpen(false);
87
+ return null;
88
+ }
26
89
  return (
27
- <div className="view-mode-bar" role="group" aria-label="Working-pane shading" data-testid="shading-bar">
28
- {SHADING_MODES.map((mode) => (
29
- <button
30
- key={mode}
31
- type="button"
32
- className={`tool-button${shading === mode ? " is-armed" : ""}`}
33
- aria-pressed={shading === mode}
34
- title={`${SHADING_GLYPHS[mode].label} — working pane (Z cycles)`}
35
- onClick={() => store.setShadingMode(mode)}
36
- >
37
- {SHADING_GLYPHS[mode].glyph}
38
- </button>
39
- ))}
90
+ <div className="shading-bar" role="group" aria-label="Working-pane shading" data-testid="shading-bar">
91
+ <button
92
+ ref={anchor}
93
+ type="button"
94
+ className="tool-button has-caret"
95
+ aria-haspopup="menu"
96
+ aria-expanded={menuOpen}
97
+ // THE NAME CARRIES THE VALUE. A bare glyph has no accessible name at
98
+ // all, and `title` alone is the last resort the accname spec reaches
99
+ // for — the one control here whose face changes with its state is the
100
+ // one that must say so out loud.
101
+ aria-label={`Shading: ${shadingLabel(shading)}`}
102
+ title={titleFor(shading)}
103
+ onClick={() => setMenuOpen((v) => !v)}
104
+ >
105
+ {SHADING_GLYPHS[shading]}
106
+ <span className="tool-caret" aria-hidden="true" />
107
+ </button>
108
+ <Popover open={menuOpen} anchor={anchor} onClose={() => setMenuOpen(false)}>
109
+ {/* `menuitemradio` and not `menuitem`: the four rows ARE the whole
110
+ menu, so the `role="menu"` element is the group the radio semantics
111
+ need — which is what the old bar's four `aria-pressed` buttons
112
+ said, kept rather than dropped on the way into a menu. */}
113
+ <div className="shading-menu" role="menu" data-testid="shading-menu">
114
+ {SHADING_MODES.map((mode) => (
115
+ <button
116
+ key={mode}
117
+ type="button"
118
+ role="menuitemradio"
119
+ aria-checked={shading === mode}
120
+ className={`tool-button${shading === mode ? " is-armed" : ""}`}
121
+ title={titleFor(mode)}
122
+ onClick={() => {
123
+ setMenuOpen(false);
124
+ store.setShadingMode(mode);
125
+ }}
126
+ >
127
+ <span aria-hidden="true">{SHADING_GLYPHS[mode]}</span>
128
+ {shadingLabel(mode)}
129
+ </button>
130
+ ))}
131
+ </div>
132
+ </Popover>
40
133
  </div>
41
134
  );
42
135
  }