@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,349 @@
1
+ // The Library's right column (the selected item, larger, its action and Pin) and
2
+ // the centre column's EXPANDED PREVIEW, which replaces the catalog while it is
3
+ // open. A sound pack's column is `LibrarySoundPanel`: a pack is a list of
4
+ // files to audition one at a time, not one thing to look at.
5
+ //
6
+ // THE PROPS ARE THE CONTRACT. The browser owns selection, the expanded flag
7
+ // and the one-add-at-a-time rule (a double click on a card adds too, so the
8
+ // guard and the add's error cannot live here); this panel renders the
9
+ // selected row and asks.
10
+ //
11
+ // PREVIEWING NEVER INSTALLS. A shipped component draws live only once it is
12
+ // in this Project's registry (`installedAs` names a row of `s.library`): the
13
+ // stage and the harness can only load a registry id, and installing as a side
14
+ // effect of LOOKING would write files into the author's source tree. Before
15
+ // the Add, the expanded preview is the item's thumb and a sentence that says
16
+ // so. The live picture is the inspector's: a second shot stage
17
+ // (`ComponentStage`, framed as a Shot frames it). Components are React Three
18
+ // Fiber, so a dom host shows its thumb and says it is not supported.
19
+ //
20
+ // A SHOT'S ACTION IS ITS OWN PROJECT. A shot has no layer to place, so its
21
+ // primary is "Use as new Project" (`claimLibraryShot`, the pinned card's Claim
22
+ // as Project): the shot copied into a Project of its own and opened. Pin to
23
+ // board stays the second action, as it is a component's.
24
+ //
25
+ // PLAY AND STOP, NOT A SCRUBBER. `ComponentStage` loops on its own rAF clock
26
+ // and exposes no seek and no position, so the transport is the one control it
27
+ // can honour: play loops, stop holds the first frame (a card with
28
+ // `frame: 0`). A scrubber would claim a position nothing reports.
29
+ import { useEffect, useMemo, useRef, useState } from "react";
30
+ import type { ReactNode } from "react";
31
+ import type { Value } from "@dailies-studio/engine";
32
+ import type { ReferenceItem3D } from "@dailies-studio/shot/doc";
33
+ import type { LibraryItem } from "../../api.js";
34
+ import { ADD_ICON, CHEVRON_DOWN_ICON, CHEVRON_LEFT_ICON, COLLAPSE_ICON, FIT_ICON, LIBRARY_CATEGORY_ICONS, PLAY_ICON, STOP_ICON } from "../../icons.js";
35
+ import { useShotEditor, useStore } from "../../state/context.js";
36
+ import { placeInShotRefusal } from "../../state/store.js";
37
+ import { instanceHostRefusal } from "../../state/storyboardModel.js";
38
+ import { ComponentStage } from "../storyboard/ComponentStage.js";
39
+ import { formatDuration } from "../timeline/model.js";
40
+ import { DOM_NOT_SUPPORTED } from "../../state/workspace.js";
41
+ import { displayNameOf, kindOf } from "./libraryModel.js";
42
+ import { LibrarySoundPanel } from "./LibrarySoundPanel.js";
43
+
44
+ type ComponentCard = Extract<ReferenceItem3D, { type: "component" }>;
45
+
46
+ const NO_PROPS: Record<string, Value> = {};
47
+
48
+ /** One item of a kind, for the metadata row and "About this …". */
49
+ const KIND_NOUN: Record<ReturnType<typeof kindOf>, string> = {
50
+ component: "Component",
51
+ blueprint: "Blueprint",
52
+ material: "Material",
53
+ shot: "Shot",
54
+ images: "Image pack",
55
+ sounds: "Sound pack",
56
+ };
57
+
58
+ /** Only a component has motion to open larger: every other kind is a still. */
59
+ export const canExpand = (item: LibraryItem): boolean => kindOf(item) === "component";
60
+
61
+ export interface LibraryPreviewPanelProps {
62
+ /** The selected catalog row, or null before the author picks one. */
63
+ item: LibraryItem | null;
64
+ /** Where a PIN lands, as printed: "Project storyboard", the whole film. */
65
+ destination: string;
66
+ /** Cards on this Project's board that cite `item` (0 when none, or no item). */
67
+ onBoard: number;
68
+ /** False with no Project open: the catalog still browses, nothing adds. */
69
+ canAdd: boolean;
70
+ /** An add is in flight somewhere on the surface; every Add waits for it. */
71
+ adding: boolean;
72
+ /** ADD (D2): place a component in the open Shot, selected. It installs
73
+ * first. Only a component has the button. */
74
+ onAdd: () => void;
75
+ /** USE AS NEW PROJECT: copy a shot into a new Project and open it. Only a
76
+ * shot has the button. */
77
+ onClaim: () => void;
78
+ /** PIN TO INSPIRATION: the item as a board card for the agent. The one
79
+ * action of a material, an image pack or a blueprint, and the second of a
80
+ * component and a shot. Not for a sound pack, which pins one sound at a
81
+ * time. */
82
+ onPin: () => void;
83
+ /** Why the last add of THIS item failed, printed under its button. The
84
+ * shell's error strip is under the dialog, so without this a refused add
85
+ * would only re-enable the button. */
86
+ error: string | null;
87
+ /** The centre column is showing this item's expanded preview. */
88
+ expanded: boolean;
89
+ onExpand: (open: boolean) => void;
90
+ }
91
+
92
+ export function LibraryPreviewPanel(props: LibraryPreviewPanelProps) {
93
+ const { item, destination, onBoard, canAdd, adding, onAdd, onClaim, onPin, error, expanded, onExpand } = props;
94
+ const [more, setMore] = useState(false);
95
+ // Why Add would refuse (no Shot open, a definition open), and the Shot it
96
+ // lands in: strings, so these selectors allocate nothing.
97
+ const shotRefusal = useShotEditor((s) => placeInShotRefusal(s));
98
+ // The board enters no Shot: one the Studio left open behind it is not where
99
+ // an Add lands (Add is refused there, `placeInShotRefusal`).
100
+ const shotName = useShotEditor((s) =>
101
+ s.storyboardOpen || s.enteredScene === null ? null : (s.doc?.scenesById[s.enteredScene]?.name ?? null),
102
+ );
103
+ const boardOpen = useShotEditor((s) => s.storyboardOpen);
104
+ // FOCUS COMES BACK HERE ON COLLAPSE. The expanded view's two closes unmount
105
+ // with it, so focus falls to <body>; put it on the button that opens the
106
+ // view again. Only when it fell: a rail pick also collapses, and the rail
107
+ // row keeps its focus.
108
+ const expandRef = useRef<HTMLButtonElement>(null);
109
+ const wasExpanded = useRef(expanded);
110
+ useEffect(() => {
111
+ const collapsed = wasExpanded.current && !expanded;
112
+ wasExpanded.current = expanded;
113
+ if (collapsed && (document.activeElement === null || document.activeElement === document.body)) expandRef.current?.focus();
114
+ }, [expanded]);
115
+ if (item === null) {
116
+ return (
117
+ <aside className="library-preview" aria-label="Preview">
118
+ <p className="library-preview-empty">Click an item to preview it.</p>
119
+ </aside>
120
+ );
121
+ }
122
+ const kind = kindOf(item);
123
+ const name = displayNameOf(item);
124
+ // Only a three host can be a layer, so a dom component's Add says why up front.
125
+ const placeRefusal = item.host === "three" ? shotRefusal : instanceHostRefusal(item.host);
126
+ if (kind === "sounds") {
127
+ return (
128
+ <LibrarySoundPanel
129
+ key={item.id}
130
+ item={item}
131
+ canAdd={canAdd}
132
+ adding={adding}
133
+ />
134
+ );
135
+ }
136
+ const opens = canExpand(item);
137
+ // The kinds whose primary is their own button, so Pin is the outlined second.
138
+ const primary = kind === "component" || kind === "shot";
139
+ const where = kind === "component" ? (shotName ?? "no Shot open") : kind === "shot" ? "a new Project" : destination;
140
+ return (
141
+ <aside className="library-preview" aria-label={`Preview: ${name}`} data-testid="library-preview">
142
+ <header className="library-preview-head">
143
+ <h2>{expanded ? `About this ${KIND_NOUN[kind].toLowerCase()}` : "Preview"}</h2>
144
+ {opens && !expanded ? (
145
+ <button ref={expandRef} type="button" className="btn is-ghost is-icon" aria-label="Open a larger preview" title="Open a larger preview" onClick={() => onExpand(true)}>
146
+ {FIT_ICON}
147
+ </button>
148
+ ) : null}
149
+ </header>
150
+ {expanded ? null : (
151
+ <div className="library-preview-thumb">
152
+ <LibraryArt item={item} />
153
+ {opens ? (
154
+ <button type="button" className="library-preview-play" aria-label={`Play ${name}`} title="Play it larger" onClick={() => onExpand(true)}>
155
+ {PLAY_ICON}
156
+ </button>
157
+ ) : null}
158
+ </div>
159
+ )}
160
+ <div className="library-preview-purpose">
161
+ <h3 className="library-preview-name">{name}</h3>
162
+ {item.description === undefined ? null : <p className="library-preview-note">{item.description}</p>}
163
+ </div>
164
+ <div className="library-preview-add">
165
+ <p className="library-preview-destination">
166
+ <span data-testid="library-destination">Destination: {where}</span>
167
+ {onBoard > 0 ? <span data-testid="library-preview-on-board">On the board ×{onBoard}</span> : null}
168
+ </p>
169
+ {kind === "component" ? (
170
+ <button
171
+ type="button"
172
+ className="btn is-primary is-lg"
173
+ data-testid="library-add"
174
+ disabled={!canAdd || adding}
175
+ aria-disabled={placeRefusal !== null || undefined}
176
+ title={
177
+ !canAdd
178
+ ? "Open a Project to add library items."
179
+ : (placeRefusal ?? `Install into this Project and place it in ${shotName ?? "the open Shot"}. Undo removes the layer; the installed files stay.`)
180
+ }
181
+ onClick={() => {
182
+ if (placeRefusal === null) onAdd();
183
+ }}
184
+ >
185
+ {ADD_ICON}
186
+ {adding ? "Adding…" : "Add to shot"}
187
+ </button>
188
+ ) : null}
189
+ {kind === "shot" ? (
190
+ <button
191
+ type="button"
192
+ className="btn is-primary is-lg"
193
+ data-testid="library-claim"
194
+ disabled={!canAdd || adding}
195
+ title={canAdd ? "Copy this shot into a new Project of its own and open it. This Project stays as it is." : "Open a Project to add library items."}
196
+ onClick={onClaim}
197
+ >
198
+ {ADD_ICON}
199
+ Use as new Project
200
+ </button>
201
+ ) : null}
202
+ <button
203
+ type="button"
204
+ className={primary ? "btn is-outline is-lg" : "btn is-primary is-lg"}
205
+ data-testid="library-pin"
206
+ disabled={!canAdd || adding}
207
+ title={canAdd ? `Pin a card to ${destination}. The agent reads it when it builds.${boardOpen ? "" : " Opens the storyboard."}` : "Open a Project to add library items."}
208
+ onClick={onPin}
209
+ >
210
+ {primary ? null : ADD_ICON}
211
+ Pin to board
212
+ </button>
213
+ {error === null ? null : (
214
+ <p className="library-preview-error" role="alert" data-testid="library-add-error">
215
+ {error}
216
+ </p>
217
+ )}
218
+ <p className="library-preview-hint">
219
+ {kind === "component"
220
+ ? "Add places it in the open Shot. Pin saves it for later."
221
+ : kind === "shot"
222
+ ? "Use as new Project copies it into a Project of its own and opens it. Pin saves it on this board for the agent."
223
+ : "This has no layer of its own. Pin it so the agent can use it."}
224
+ </p>
225
+ </div>
226
+ {item.useFor === undefined ? null : (
227
+ <section className="library-preview-usage" aria-label="Good for">
228
+ <h3>Good for</h3>
229
+ <p>{item.useFor}</p>
230
+ </section>
231
+ )}
232
+ {item.avoidFor === undefined ? null : (
233
+ <div className="library-preview-more">
234
+ <button type="button" aria-expanded={more} onClick={() => setMore((v) => !v)}>
235
+ More usage guidance
236
+ {CHEVRON_DOWN_ICON}
237
+ </button>
238
+ {more ? <p>Not for: {item.avoidFor}</p> : null}
239
+ </div>
240
+ )}
241
+ <footer className="library-preview-meta">
242
+ <span>
243
+ {KIND_NOUN[kind]}
244
+ {kind === "component" ? ` · ${item.host === "three" ? "3D" : "2D"}` : ""}
245
+ </span>
246
+ {item.version === undefined ? null : <span>v{item.version}</span>}
247
+ </footer>
248
+ </aside>
249
+ );
250
+ }
251
+
252
+ /** The item's thumb, or its category glyph on the well when it ships none. */
253
+ function LibraryArt({ item }: { item: LibraryItem }) {
254
+ return item.thumb === undefined ? (
255
+ <span className="library-preview-placeholder" aria-hidden="true">
256
+ {LIBRARY_CATEGORY_ICONS[kindOf(item)]}
257
+ </span>
258
+ ) : (
259
+ <img className="library-preview-art" src={item.thumb} alt="" />
260
+ );
261
+ }
262
+
263
+ /**
264
+ * The centre column's larger preview of one component: the way back, the
265
+ * live picture (or the thumb and why), and play / stop. The browser mounts it
266
+ * in place of the catalog, so the rail and the right column stay put.
267
+ */
268
+ // ponytail: Escape closes the whole Library, not just this view (the Dialog
269
+ // owns the key); give the browser a keydown guard if authors expect a step back.
270
+ export function LibraryExpandedPreview({ item, backTo, onClose }: { item: LibraryItem; backTo: string; onClose: () => void }) {
271
+ const store = useStore();
272
+ const name = displayNameOf(item);
273
+ const doc = useShotEditor((s) => s.doc);
274
+ const registryId = item.installedAs ?? null;
275
+ const row = useShotEditor((s) => (registryId === null ? undefined : s.library.find((r) => r.id === registryId)));
276
+ const contract = useShotEditor((s) => (registryId === null ? undefined : s.contracts[registryId]));
277
+ const [playing, setPlaying] = useState(true);
278
+ // The button that opened this view unmounted with the right column's thumb
279
+ // and head, so focus starts on the way back, not on <body>.
280
+ const backRef = useRef<HTMLButtonElement>(null);
281
+ useEffect(() => backRef.current?.focus(), []);
282
+ useEffect(() => {
283
+ if (registryId !== null && row?.host === "three") void store.loadContract(registryId);
284
+ }, [registryId, row?.host, store]);
285
+ // A STABLE CARD per play state: `ComponentStage` reloads its document when
286
+ // the card object changes, so a fresh literal per render would reload the
287
+ // stage on every store write.
288
+ const card = useMemo<ComponentCard | null>(
289
+ () =>
290
+ registryId === null
291
+ ? null
292
+ : { id: "ref_library_preview", type: "component", component: registryId, label: name, props: NO_PROPS, ...(playing ? {} : { frame: 0 }) },
293
+ [registryId, name, playing],
294
+ );
295
+ const live = doc !== null && card !== null && row?.host === "three";
296
+
297
+ let picture: ReactNode;
298
+ if (live && row !== undefined) {
299
+ picture = <ComponentStage doc={doc} item={card} contract={contract} availability={row.contractAvailability} lengthFrames={item.lengthFrames} />;
300
+ } else {
301
+ picture = (
302
+ <div className="library-expanded-still">
303
+ <LibraryArt item={item} />
304
+ <p>
305
+ {doc === null
306
+ ? "Open a Project to play this live."
307
+ : row?.host === "dom"
308
+ ? DOM_NOT_SUPPORTED
309
+ : "Add or pin it to play it live here. Previewing never installs it."}
310
+ </p>
311
+ </div>
312
+ );
313
+ }
314
+ const fps = doc === null ? 0 : doc.fps.num / doc.fps.den;
315
+
316
+ return (
317
+ <section className="library-expanded" aria-label={`Preview: ${name}`} data-testid="library-expanded">
318
+ <header className="library-expanded-head">
319
+ <div>
320
+ <button ref={backRef} type="button" className="library-expanded-back" onClick={onClose}>
321
+ {CHEVRON_LEFT_ICON}
322
+ Back to {backTo}
323
+ </button>
324
+ <h2>{name}</h2>
325
+ </div>
326
+ <button type="button" className="btn is-outline is-icon is-lg" aria-label="Close the larger preview" title="Close the larger preview" onClick={onClose}>
327
+ {COLLAPSE_ICON}
328
+ </button>
329
+ </header>
330
+ <div className="library-expanded-stage">{picture}</div>
331
+ {live ? (
332
+ <div className="library-expanded-transport">
333
+ <button
334
+ type="button"
335
+ className="library-preview-play"
336
+ aria-label={playing ? `Stop ${name}` : `Play ${name}`}
337
+ onClick={() => setPlaying((v) => !v)}
338
+ >
339
+ {playing ? STOP_ICON : PLAY_ICON}
340
+ </button>
341
+ {item.lengthFrames === undefined || !(fps > 0) ? null : (
342
+ <span className="library-expanded-length">Loops every {formatDuration(item.lengthFrames / fps)}</span>
343
+ )}
344
+ </div>
345
+ ) : null}
346
+ </section>
347
+ );
348
+ }
349
+
@@ -0,0 +1,84 @@
1
+ // The Library's left column: the way back, the categories, and the Project an
2
+ // Add lands in.
3
+ //
4
+ // THE BACK ROW IS THE CLOSE. The library has no Done button any more: the
5
+ // author came from somewhere, and the row names it ("Back to <Shot>", or the
6
+ // board, or the Project) so leaving says where it lands. Escape does the same
7
+ // through `Dialog`.
8
+ //
9
+ // EACH ROW IS A BUTTON WITH `aria-pressed`, not a tab: the rows filter one
10
+ // catalog column and own no panel of their own, so a tablist would promise a
11
+ // tabpanel per row that does not exist.
12
+ import type { ReactNode } from "react";
13
+ import { CHEVRON_LEFT_ICON, LIBRARY_CATEGORY_ICONS } from "../../icons.js";
14
+ import { CATEGORY_LABEL, LIBRARY_CATEGORIES, plural } from "./libraryModel.js";
15
+ import type { LibraryCategory } from "./libraryModel.js";
16
+
17
+ export function LibraryRail({
18
+ category,
19
+ counts,
20
+ onCategory,
21
+ backLabel,
22
+ onBack,
23
+ project,
24
+ onBoard,
25
+ }: {
26
+ category: LibraryCategory;
27
+ counts: ReadonlyMap<LibraryCategory, number>;
28
+ onCategory: (category: LibraryCategory) => void;
29
+ /** "Back to Fold reel", "Back to storyboard", "Back to <Project>". */
30
+ backLabel: string;
31
+ onBack: () => void;
32
+ /** The open Project's display name, or null with none open. */
33
+ project: string | null;
34
+ /** Shipped items with at least one card on this Project's board. */
35
+ onBoard: number;
36
+ }) {
37
+ let foot: ReactNode;
38
+ if (project === null) {
39
+ foot = (
40
+ <>
41
+ <span className="library-rail-project">No Project open</span>
42
+ <span>Open a Project to add library items.</span>
43
+ </>
44
+ );
45
+ } else {
46
+ foot = (
47
+ <>
48
+ <span className="library-rail-project">Project · {project}</span>
49
+ <span data-testid="library-on-board">{plural(onBoard, "library item")} on the storyboard</span>
50
+ </>
51
+ );
52
+ }
53
+ return (
54
+ <nav className="library-rail" aria-label="Library categories">
55
+ <button type="button" className="library-back" data-testid="library-back" onClick={onBack}>
56
+ {CHEVRON_LEFT_ICON}
57
+ <span>{backLabel}</span>
58
+ </button>
59
+ {/* The dialog's own title carries the accessible name "Library"; this
60
+ is its visible face, so the repeat is hidden from a reader. */}
61
+ <div className="library-rail-title">
62
+ <span aria-hidden="true">Library</span>
63
+ <span className="library-rail-sub">Pieces for your next shot</span>
64
+ </div>
65
+ <div className="library-rail-rows">
66
+ {LIBRARY_CATEGORIES.map((c) => (
67
+ <button
68
+ key={c}
69
+ type="button"
70
+ className="library-rail-row"
71
+ data-category={c}
72
+ aria-pressed={c === category}
73
+ onClick={() => onCategory(c)}
74
+ >
75
+ {LIBRARY_CATEGORY_ICONS[c]}
76
+ <span className="library-rail-label">{CATEGORY_LABEL[c]}</span>
77
+ {counts.has(c) ? <span className="library-rail-count">{counts.get(c)}</span> : null}
78
+ </button>
79
+ ))}
80
+ </div>
81
+ <div className="library-rail-foot">{foot}</div>
82
+ </nav>
83
+ );
84
+ }