cursedbelt 5.40.0 → 5.45.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 (499) hide show
  1. package/dist/react/Toast.d.ts +12 -2
  2. package/dist/react/Toast.d.ts.map +1 -1
  3. package/dist/react/Toast.js +32 -3
  4. package/dist/react/Toast.js.map +1 -1
  5. package/dist/react/analytics/MetricDrilldown.js +1 -1
  6. package/dist/react/charts/AreaChart.d.ts.map +1 -1
  7. package/dist/react/charts/AreaChart.js +5 -5
  8. package/dist/react/charts/AreaChart.js.map +1 -1
  9. package/dist/react/charts/BarChart.d.ts.map +1 -1
  10. package/dist/react/charts/BarChart.js +3 -3
  11. package/dist/react/charts/BarChart.js.map +1 -1
  12. package/dist/react/charts/ChartCard.d.ts +53 -0
  13. package/dist/react/charts/ChartCard.d.ts.map +1 -0
  14. package/dist/react/charts/ChartCard.js +46 -0
  15. package/dist/react/charts/ChartCard.js.map +1 -0
  16. package/dist/react/charts/ChartFrame.d.ts +12 -5
  17. package/dist/react/charts/ChartFrame.d.ts.map +1 -1
  18. package/dist/react/charts/ChartFrame.js +12 -5
  19. package/dist/react/charts/ChartFrame.js.map +1 -1
  20. package/dist/react/charts/ChartLegend.js +1 -1
  21. package/dist/react/charts/ChartLegend.js.map +1 -1
  22. package/dist/react/charts/HeatmapChart.js +3 -3
  23. package/dist/react/charts/HeatmapChart.js.map +1 -1
  24. package/dist/react/charts/IntervalTimeline.js +1 -1
  25. package/dist/react/charts/IntervalTimeline.js.map +1 -1
  26. package/dist/react/charts/LineChart.d.ts.map +1 -1
  27. package/dist/react/charts/LineChart.js +3 -3
  28. package/dist/react/charts/LineChart.js.map +1 -1
  29. package/dist/react/charts/TimeSeriesChart.d.ts.map +1 -1
  30. package/dist/react/charts/TimeSeriesChart.js +6 -6
  31. package/dist/react/charts/TimeSeriesChart.js.map +1 -1
  32. package/dist/react/charts/chartBox.d.ts +46 -0
  33. package/dist/react/charts/chartBox.d.ts.map +1 -0
  34. package/dist/react/charts/chartBox.js +45 -0
  35. package/dist/react/charts/chartBox.js.map +1 -0
  36. package/dist/react/charts/index.d.ts +2 -0
  37. package/dist/react/charts/index.d.ts.map +1 -1
  38. package/dist/react/charts/index.js +2 -0
  39. package/dist/react/charts/index.js.map +1 -1
  40. package/dist/react/charts/shared.d.ts +13 -2
  41. package/dist/react/charts/shared.d.ts.map +1 -1
  42. package/dist/react/charts/shared.js +14 -3
  43. package/dist/react/charts/shared.js.map +1 -1
  44. package/dist/react/chat/Bubble.js +4 -4
  45. package/dist/react/chat/Bubble.js.map +1 -1
  46. package/dist/react/chat/MessageScroller.js +1 -1
  47. package/dist/react/chat/MessageScroller.js.map +1 -1
  48. package/dist/react/components/ActionBar.d.ts.map +1 -1
  49. package/dist/react/components/ActionBar.js +63 -1
  50. package/dist/react/components/ActionBar.js.map +1 -1
  51. package/dist/react/components/AddItemsMenu.js +1 -1
  52. package/dist/react/components/AddItemsMenu.js.map +1 -1
  53. package/dist/react/components/Alert.d.ts +16 -2
  54. package/dist/react/components/Alert.d.ts.map +1 -1
  55. package/dist/react/components/Alert.js +35 -2
  56. package/dist/react/components/Alert.js.map +1 -1
  57. package/dist/react/components/AppShell.d.ts +55 -33
  58. package/dist/react/components/AppShell.d.ts.map +1 -1
  59. package/dist/react/components/AppShell.js +121 -31
  60. package/dist/react/components/AppShell.js.map +1 -1
  61. package/dist/react/components/AsyncPanel.d.ts +21 -3
  62. package/dist/react/components/AsyncPanel.d.ts.map +1 -1
  63. package/dist/react/components/AsyncPanel.js +49 -5
  64. package/dist/react/components/AsyncPanel.js.map +1 -1
  65. package/dist/react/components/BannerLane.d.ts +32 -0
  66. package/dist/react/components/BannerLane.d.ts.map +1 -0
  67. package/dist/react/components/BannerLane.js +41 -0
  68. package/dist/react/components/BannerLane.js.map +1 -0
  69. package/dist/react/components/Button.d.ts.map +1 -1
  70. package/dist/react/components/Button.js +2 -1
  71. package/dist/react/components/Button.js.map +1 -1
  72. package/dist/react/components/ChipFilterStrip.js +1 -1
  73. package/dist/react/components/ChipFilterStrip.js.map +1 -1
  74. package/dist/react/components/ComboboxField.d.ts.map +1 -1
  75. package/dist/react/components/ComboboxField.js +14 -4
  76. package/dist/react/components/ComboboxField.js.map +1 -1
  77. package/dist/react/components/CommentThread.js +2 -2
  78. package/dist/react/components/CommentThread.js.map +1 -1
  79. package/dist/react/components/ConfirmDialog.js +1 -1
  80. package/dist/react/components/ConfirmDialog.js.map +1 -1
  81. package/dist/react/components/DataTable/DataTable.d.ts +18 -2
  82. package/dist/react/components/DataTable/DataTable.d.ts.map +1 -1
  83. package/dist/react/components/DataTable/DataTable.js +51 -12
  84. package/dist/react/components/DataTable/DataTable.js.map +1 -1
  85. package/dist/react/components/DataTable/Pagination.js +1 -1
  86. package/dist/react/components/DataTable/Pagination.js.map +1 -1
  87. package/dist/react/components/DataTable/RowActions.d.ts +7 -1
  88. package/dist/react/components/DataTable/RowActions.d.ts.map +1 -1
  89. package/dist/react/components/DataTable/RowActions.js +10 -3
  90. package/dist/react/components/DataTable/RowActions.js.map +1 -1
  91. package/dist/react/components/DataTable/TableFilterBuilder.js +1 -1
  92. package/dist/react/components/DataTable/TableFilterBuilder.js.map +1 -1
  93. package/dist/react/components/DataTable/cells/CodeCell.js +1 -1
  94. package/dist/react/components/DataTable/cells/CodeCell.js.map +1 -1
  95. package/dist/react/components/DataTable/cells/LinkCell.js +1 -1
  96. package/dist/react/components/DataTable/cells/LinkCell.js.map +1 -1
  97. package/dist/react/components/DataTable/index.d.ts +1 -1
  98. package/dist/react/components/DataTable/index.d.ts.map +1 -1
  99. package/dist/react/components/DataTable/index.js.map +1 -1
  100. package/dist/react/components/DataTable/types.d.ts +20 -0
  101. package/dist/react/components/DataTable/types.d.ts.map +1 -1
  102. package/dist/react/components/DataTable/types.js +22 -0
  103. package/dist/react/components/DataTable/types.js.map +1 -1
  104. package/dist/react/components/DateTimeField.d.ts.map +1 -1
  105. package/dist/react/components/DateTimeField.js +4 -1
  106. package/dist/react/components/DateTimeField.js.map +1 -1
  107. package/dist/react/components/DeleteButton.js +2 -0
  108. package/dist/react/components/DeleteButton.js.map +1 -1
  109. package/dist/react/components/Drawer.js +3 -3
  110. package/dist/react/components/Drawer.js.map +1 -1
  111. package/dist/react/components/DropdownMenu.d.ts +16 -0
  112. package/dist/react/components/DropdownMenu.d.ts.map +1 -1
  113. package/dist/react/components/DropdownMenu.js +22 -6
  114. package/dist/react/components/DropdownMenu.js.map +1 -1
  115. package/dist/react/components/EmptyState.d.ts +10 -2
  116. package/dist/react/components/EmptyState.d.ts.map +1 -1
  117. package/dist/react/components/EmptyState.js +10 -5
  118. package/dist/react/components/EmptyState.js.map +1 -1
  119. package/dist/react/components/EventTimeline.js +1 -1
  120. package/dist/react/components/EventTimeline.js.map +1 -1
  121. package/dist/react/components/FileDropZone.js +1 -1
  122. package/dist/react/components/FileDropZone.js.map +1 -1
  123. package/dist/react/components/Grid.d.ts +9 -1
  124. package/dist/react/components/Grid.d.ts.map +1 -1
  125. package/dist/react/components/Grid.js +20 -2
  126. package/dist/react/components/Grid.js.map +1 -1
  127. package/dist/react/components/Guide.d.ts +31 -0
  128. package/dist/react/components/Guide.d.ts.map +1 -0
  129. package/dist/react/components/Guide.js +18 -0
  130. package/dist/react/components/Guide.js.map +1 -0
  131. package/dist/react/components/InfoHintImpl.d.ts +0 -13
  132. package/dist/react/components/InfoHintImpl.d.ts.map +1 -1
  133. package/dist/react/components/InfoHintImpl.js +17 -1
  134. package/dist/react/components/InfoHintImpl.js.map +1 -1
  135. package/dist/react/components/InlineEditableText.js +1 -1
  136. package/dist/react/components/InlineEditableText.js.map +1 -1
  137. package/dist/react/components/IsolatedFrame.js +1 -1
  138. package/dist/react/components/IsolatedFrame.js.map +1 -1
  139. package/dist/react/components/ListToolbar.d.ts.map +1 -1
  140. package/dist/react/components/ListToolbar.js +2 -2
  141. package/dist/react/components/ListToolbar.js.map +1 -1
  142. package/dist/react/components/LogViewer.d.ts +26 -2
  143. package/dist/react/components/LogViewer.d.ts.map +1 -1
  144. package/dist/react/components/LogViewer.js +41 -26
  145. package/dist/react/components/LogViewer.js.map +1 -1
  146. package/dist/react/components/Modal.d.ts +5 -1
  147. package/dist/react/components/Modal.d.ts.map +1 -1
  148. package/dist/react/components/Modal.js +18 -8
  149. package/dist/react/components/Modal.js.map +1 -1
  150. package/dist/react/components/Page.d.ts +51 -7
  151. package/dist/react/components/Page.d.ts.map +1 -1
  152. package/dist/react/components/Page.js +53 -8
  153. package/dist/react/components/Page.js.map +1 -1
  154. package/dist/react/components/PageGuideButton.d.ts +11 -0
  155. package/dist/react/components/PageGuideButton.d.ts.map +1 -0
  156. package/dist/react/components/PageGuideButton.js +13 -0
  157. package/dist/react/components/PageGuideButton.js.map +1 -0
  158. package/dist/react/components/PanelSeparator.js +1 -1
  159. package/dist/react/components/PanelSeparator.js.map +1 -1
  160. package/dist/react/components/Popover.d.ts.map +1 -1
  161. package/dist/react/components/Popover.js +2 -1
  162. package/dist/react/components/Popover.js.map +1 -1
  163. package/dist/react/components/PromptDialog.js +1 -1
  164. package/dist/react/components/PromptDialog.js.map +1 -1
  165. package/dist/react/components/ScrollArea.d.ts +9 -1
  166. package/dist/react/components/ScrollArea.d.ts.map +1 -1
  167. package/dist/react/components/ScrollArea.js +9 -3
  168. package/dist/react/components/ScrollArea.js.map +1 -1
  169. package/dist/react/components/Section.d.ts +5 -2
  170. package/dist/react/components/Section.d.ts.map +1 -1
  171. package/dist/react/components/Section.js +5 -2
  172. package/dist/react/components/Section.js.map +1 -1
  173. package/dist/react/components/Skeleton.d.ts +19 -1
  174. package/dist/react/components/Skeleton.d.ts.map +1 -1
  175. package/dist/react/components/Skeleton.js +24 -2
  176. package/dist/react/components/Skeleton.js.map +1 -1
  177. package/dist/react/components/StatStrip.js +1 -1
  178. package/dist/react/components/StatStrip.js.map +1 -1
  179. package/dist/react/components/StatTile.d.ts +30 -3
  180. package/dist/react/components/StatTile.d.ts.map +1 -1
  181. package/dist/react/components/StatTile.js +35 -9
  182. package/dist/react/components/StatTile.js.map +1 -1
  183. package/dist/react/components/StatTileGrid.d.ts +10 -1
  184. package/dist/react/components/StatTileGrid.d.ts.map +1 -1
  185. package/dist/react/components/StatTileGrid.js +50 -33
  186. package/dist/react/components/StatTileGrid.js.map +1 -1
  187. package/dist/react/components/StatusPill.d.ts.map +1 -1
  188. package/dist/react/components/StatusPill.js +26 -1
  189. package/dist/react/components/StatusPill.js.map +1 -1
  190. package/dist/react/components/Surface.d.ts +1 -1
  191. package/dist/react/components/Surface.d.ts.map +1 -1
  192. package/dist/react/components/Surface.js +20 -11
  193. package/dist/react/components/Surface.js.map +1 -1
  194. package/dist/react/components/SwatchPicker.d.ts.map +1 -1
  195. package/dist/react/components/SwatchPicker.js +5 -1
  196. package/dist/react/components/SwatchPicker.js.map +1 -1
  197. package/dist/react/components/Switch.js +1 -1
  198. package/dist/react/components/Switch.js.map +1 -1
  199. package/dist/react/components/WidgetHeader.js +1 -1
  200. package/dist/react/components/WidgetHeader.js.map +1 -1
  201. package/dist/react/components/calendar/CalendarLegend.js +1 -1
  202. package/dist/react/components/calendar/CalendarLegend.js.map +1 -1
  203. package/dist/react/components/calendar/DayAgenda.js +1 -1
  204. package/dist/react/components/calendar/DayAgenda.js.map +1 -1
  205. package/dist/react/components/calendar/EventChip.js +1 -1
  206. package/dist/react/components/calendar/EventChip.js.map +1 -1
  207. package/dist/react/components/calendar/MonthGrid.js +1 -1
  208. package/dist/react/components/calendar/MonthGrid.js.map +1 -1
  209. package/dist/react/components/calendar/WeekView.js +2 -2
  210. package/dist/react/components/calendar/WeekView.js.map +1 -1
  211. package/dist/react/components/cardFrame.d.ts +16 -0
  212. package/dist/react/components/cardFrame.d.ts.map +1 -0
  213. package/dist/react/components/cardFrame.js +24 -0
  214. package/dist/react/components/cardFrame.js.map +1 -0
  215. package/dist/react/components/dialog.d.ts.map +1 -1
  216. package/dist/react/components/dialog.js +2 -1
  217. package/dist/react/components/dialog.js.map +1 -1
  218. package/dist/react/dashboard-grid/WidgetFrame.js +2 -2
  219. package/dist/react/dashboard-grid/WidgetFrame.js.map +1 -1
  220. package/dist/react/emoji/EmojiPicker.js +1 -1
  221. package/dist/react/emoji/EmojiPicker.js.map +1 -1
  222. package/dist/react/file-tree/FileTree.d.ts +8 -1
  223. package/dist/react/file-tree/FileTree.d.ts.map +1 -1
  224. package/dist/react/file-tree/FileTree.js +29 -4
  225. package/dist/react/file-tree/FileTree.js.map +1 -1
  226. package/dist/react/filter-rail/FilterRail.js +1 -1
  227. package/dist/react/filter-rail/FilterRail.js.map +1 -1
  228. package/dist/react/icons.js +1 -1
  229. package/dist/react/icons.js.map +1 -1
  230. package/dist/react/index.d.ts +8 -5
  231. package/dist/react/index.d.ts.map +1 -1
  232. package/dist/react/index.js +5 -2
  233. package/dist/react/index.js.map +1 -1
  234. package/dist/react/lib/menuSections.d.ts +8 -0
  235. package/dist/react/lib/menuSections.d.ts.map +1 -0
  236. package/dist/react/lib/menuSections.js +37 -0
  237. package/dist/react/lib/menuSections.js.map +1 -0
  238. package/dist/react/markdown/Markdown.js +2 -2
  239. package/dist/react/markdown/Markdown.js.map +1 -1
  240. package/dist/react/media/HoverScrubVideoThumb.js +1 -1
  241. package/dist/react/media/HoverScrubVideoThumb.js.map +1 -1
  242. package/dist/react/media/MaskPainter.js +1 -1
  243. package/dist/react/media/MediaUploadControl.js +1 -1
  244. package/dist/react/media/MediaUploadControl.js.map +1 -1
  245. package/dist/react/media/VideoTrimmer.js +1 -1
  246. package/dist/react/media/VideoTrimmer.js.map +1 -1
  247. package/dist/react/media/denseGallery.css +5 -1
  248. package/dist/react/namespace/NamespaceTargetCapsule.js +1 -1
  249. package/dist/react/namespace/NamespaceTargetCapsule.js.map +1 -1
  250. package/dist/react/nav/BreadcrumbTrail.js +1 -1
  251. package/dist/react/nav/BreadcrumbTrail.js.map +1 -1
  252. package/dist/react/nav/CommandBar.d.ts +7 -1
  253. package/dist/react/nav/CommandBar.d.ts.map +1 -1
  254. package/dist/react/nav/CommandBar.js +5 -3
  255. package/dist/react/nav/CommandBar.js.map +1 -1
  256. package/dist/react/nav/PlaceBar.d.ts.map +1 -1
  257. package/dist/react/nav/PlaceBar.js +7 -9
  258. package/dist/react/nav/PlaceBar.js.map +1 -1
  259. package/dist/react/nav/SideNav.d.ts +12 -2
  260. package/dist/react/nav/SideNav.d.ts.map +1 -1
  261. package/dist/react/nav/SideNav.js +42 -22
  262. package/dist/react/nav/SideNav.js.map +1 -1
  263. package/dist/react/nav/TopBar.d.ts +65 -31
  264. package/dist/react/nav/TopBar.d.ts.map +1 -1
  265. package/dist/react/nav/TopBar.js +59 -24
  266. package/dist/react/nav/TopBar.js.map +1 -1
  267. package/dist/react/nav/index.d.ts +1 -1
  268. package/dist/react/nav/index.d.ts.map +1 -1
  269. package/dist/react/nav/index.js +1 -1
  270. package/dist/react/nav/index.js.map +1 -1
  271. package/dist/react/nav/navShared.d.ts +10 -1
  272. package/dist/react/nav/navShared.d.ts.map +1 -1
  273. package/dist/react/nav/navShared.js +23 -0
  274. package/dist/react/nav/navShared.js.map +1 -1
  275. package/dist/react/notifications/NotificationFeed.js +2 -2
  276. package/dist/react/notifications/NotificationFeed.js.map +1 -1
  277. package/dist/react/palette/Command.d.ts.map +1 -1
  278. package/dist/react/palette/Command.js +1 -1
  279. package/dist/react/palette/Command.js.map +1 -1
  280. package/dist/react/palette/CommandPalette.d.ts +13 -1
  281. package/dist/react/palette/CommandPalette.d.ts.map +1 -1
  282. package/dist/react/palette/CommandPalette.js +44 -8
  283. package/dist/react/palette/CommandPalette.js.map +1 -1
  284. package/dist/react/palette/commandSources.d.ts +57 -0
  285. package/dist/react/palette/commandSources.d.ts.map +1 -0
  286. package/dist/react/palette/commandSources.js +129 -0
  287. package/dist/react/palette/commandSources.js.map +1 -0
  288. package/dist/react/palette/index.d.ts +1 -0
  289. package/dist/react/palette/index.d.ts.map +1 -1
  290. package/dist/react/palette/index.js +1 -0
  291. package/dist/react/palette/index.js.map +1 -1
  292. package/dist/react/rich-text/RichTextEditor.js +1 -1
  293. package/dist/react/rich-text/RichTextEditor.js.map +1 -1
  294. package/dist/react/rich-text/RichTextToolbar.js +1 -1
  295. package/dist/react/rich-text/RichTextToolbar.js.map +1 -1
  296. package/dist/scripts/guardrailsEnforce.d.ts.map +1 -1
  297. package/dist/styles-areas/analytics.css +16 -16
  298. package/dist/styles-areas/auth.css +3 -3
  299. package/dist/styles-areas/calendar.css +6 -6
  300. package/dist/styles-areas/chart-table.css +1 -1
  301. package/dist/styles-areas/charts.css +3 -3
  302. package/dist/styles-areas/chat.css +7 -6
  303. package/dist/styles-areas/companion-link.css +2 -2
  304. package/dist/styles-areas/core.css +98 -89
  305. package/dist/styles-areas/dashboard-grid.css +4 -4
  306. package/dist/styles-areas/data-table.css +16 -16
  307. package/dist/styles-areas/disk-usage.css +5 -5
  308. package/dist/styles-areas/emoji.css +2 -2
  309. package/dist/styles-areas/fields.css +13 -14
  310. package/dist/styles-areas/filter-rail.css +4 -4
  311. package/dist/styles-areas/folder-tree.css +3 -3
  312. package/dist/styles-areas/markdown.css +2 -1
  313. package/dist/styles-areas/master-detail.css +3 -3
  314. package/dist/styles-areas/media-gallery.css +8 -8
  315. package/dist/styles-areas/media.css +12 -13
  316. package/dist/styles-areas/nav.css +28 -14
  317. package/dist/styles-areas/notifications.css +4 -4
  318. package/dist/styles-areas/overlays.css +4 -4
  319. package/dist/styles-areas/palette.css +4 -3
  320. package/dist/styles-areas/rich-text.css +3 -3
  321. package/dist/styles-areas/sharing.css +4 -3
  322. package/dist/styles-areas/stats.css +9 -8
  323. package/dist/styles-areas/test-report.css +3 -3
  324. package/dist/styles-areas/wizard.css +6 -5
  325. package/dist/styles-areas/workbench.css +10 -9
  326. package/dist/styles-areas/workbook-viewer.css +2 -2
  327. package/dist/styles-utilities.css +272 -187
  328. package/dist/testing/designAudit.d.ts.map +1 -1
  329. package/dist/testing/designAudit.js +4 -0
  330. package/dist/testing/designAudit.js.map +1 -1
  331. package/dist/testing/designBaseline.d.ts.map +1 -1
  332. package/dist/testing/designBaseline.js +4 -0
  333. package/dist/testing/designBaseline.js.map +1 -1
  334. package/dist/theme.css +103 -2
  335. package/package.json +3 -2
  336. package/scripts/guardrails-baseline.json +2 -1
  337. package/scripts/guardrailsEnforce.spec.ts +64 -0
  338. package/scripts/guardrailsEnforce.ts +65 -8
  339. package/scripts/styleAreas.ts +1 -0
  340. package/src/nodeTypesDeclared.spec.ts +47 -0
  341. package/src/react/Toast.spec.tsx +43 -0
  342. package/src/react/Toast.tsx +45 -2
  343. package/src/react/analytics/MetricDrilldown.tsx +1 -1
  344. package/src/react/charts/AreaChart.tsx +9 -8
  345. package/src/react/charts/BarChart.tsx +11 -10
  346. package/src/react/charts/ChartCard.tsx +146 -0
  347. package/src/react/charts/ChartFrame.tsx +28 -10
  348. package/src/react/charts/ChartLegend.tsx +1 -1
  349. package/src/react/charts/HeatmapChart.tsx +5 -5
  350. package/src/react/charts/IntervalTimeline.tsx +2 -2
  351. package/src/react/charts/LineChart.tsx +9 -8
  352. package/src/react/charts/TimeSeriesChart.tsx +11 -10
  353. package/src/react/charts/chartBox.spec.ts +40 -0
  354. package/src/react/charts/chartBox.ts +75 -0
  355. package/src/react/charts/index.ts +2 -0
  356. package/src/react/charts/shared.ts +16 -3
  357. package/src/react/chat/Bubble.tsx +5 -5
  358. package/src/react/chat/MessageScroller.tsx +1 -1
  359. package/src/react/components/ActionBar.spec.tsx +90 -0
  360. package/src/react/components/ActionBar.tsx +82 -2
  361. package/src/react/components/AddItemsMenu.tsx +1 -1
  362. package/src/react/components/Alert.spec.tsx +32 -2
  363. package/src/react/components/Alert.tsx +47 -3
  364. package/src/react/components/AppShell.spec.tsx +199 -13
  365. package/src/react/components/AppShell.tsx +235 -92
  366. package/src/react/components/AsyncPanel.spec.tsx +7 -1
  367. package/src/react/components/AsyncPanel.tsx +85 -5
  368. package/src/react/components/BannerLane.tsx +128 -0
  369. package/src/react/components/Button.tsx +2 -0
  370. package/src/react/components/ChipFilterStrip.tsx +1 -1
  371. package/src/react/components/ComboboxField.tsx +20 -3
  372. package/src/react/components/CommentThread.tsx +2 -2
  373. package/src/react/components/ConfirmDialog.tsx +1 -1
  374. package/src/react/components/DataTable/DataTable.tsx +80 -8
  375. package/src/react/components/DataTable/Pagination.tsx +1 -1
  376. package/src/react/components/DataTable/RowActions.tsx +55 -2
  377. package/src/react/components/DataTable/TableFilterBuilder.tsx +1 -1
  378. package/src/react/components/DataTable/cells/CodeCell.tsx +1 -1
  379. package/src/react/components/DataTable/cells/LinkCell.tsx +1 -1
  380. package/src/react/components/DataTable/index.ts +1 -0
  381. package/src/react/components/DataTable/numericColumn.spec.ts +19 -0
  382. package/src/react/components/DataTable/types.ts +36 -0
  383. package/src/react/components/DateTimeField.tsx +3 -1
  384. package/src/react/components/DeleteButton.tsx +3 -0
  385. package/src/react/components/Drawer.tsx +3 -3
  386. package/src/react/components/DropdownMenu.spec.tsx +55 -0
  387. package/src/react/components/DropdownMenu.tsx +73 -20
  388. package/src/react/components/EmptyState.tsx +26 -7
  389. package/src/react/components/EventTimeline.tsx +1 -1
  390. package/src/react/components/Field.width.spec.tsx +11 -0
  391. package/src/react/components/FileDropZone.tsx +1 -1
  392. package/src/react/components/Grid.spec.tsx +44 -0
  393. package/src/react/components/Grid.tsx +41 -11
  394. package/src/react/components/Guide.tsx +77 -0
  395. package/src/react/components/InfoHint.spec.tsx +21 -0
  396. package/src/react/components/InfoHintImpl.tsx +22 -1
  397. package/src/react/components/InlineEditableText.tsx +1 -1
  398. package/src/react/components/IsolatedFrame.tsx +1 -1
  399. package/src/react/components/ListToolbar.spec.tsx +12 -0
  400. package/src/react/components/ListToolbar.tsx +8 -5
  401. package/src/react/components/LogViewer.tsx +118 -18
  402. package/src/react/components/Modal.spec.tsx +17 -2
  403. package/src/react/components/Modal.tsx +25 -10
  404. package/src/react/components/MultiSelectField.spec.tsx +6 -0
  405. package/src/react/components/Page.spec.tsx +89 -5
  406. package/src/react/components/Page.tsx +100 -16
  407. package/src/react/components/PageGuideButton.tsx +42 -0
  408. package/src/react/components/PanelSeparator.tsx +1 -1
  409. package/src/react/components/Popover.tsx +3 -1
  410. package/src/react/components/PromptDialog.tsx +1 -1
  411. package/src/react/components/ScrollArea.spec.tsx +20 -0
  412. package/src/react/components/ScrollArea.tsx +19 -0
  413. package/src/react/components/Section.spec.tsx +82 -1
  414. package/src/react/components/Section.tsx +10 -4
  415. package/src/react/components/Skeleton.tsx +50 -2
  416. package/src/react/components/StatStrip.tsx +1 -1
  417. package/src/react/components/StatTile.tsx +84 -11
  418. package/src/react/components/StatTileGrid.spec.tsx +6 -1
  419. package/src/react/components/StatTileGrid.tsx +25 -0
  420. package/src/react/components/StatusPill.tsx +26 -1
  421. package/src/react/components/Surface.tsx +22 -14
  422. package/src/react/components/SwatchPicker.tsx +4 -0
  423. package/src/react/components/Switch.tsx +1 -1
  424. package/src/react/components/WidgetHeader.tsx +1 -1
  425. package/src/react/components/calendar/CalendarLegend.tsx +1 -1
  426. package/src/react/components/calendar/DayAgenda.tsx +1 -1
  427. package/src/react/components/calendar/EventChip.tsx +1 -1
  428. package/src/react/components/calendar/MonthGrid.tsx +1 -1
  429. package/src/react/components/calendar/WeekView.tsx +2 -2
  430. package/src/react/components/cardFrame.ts +28 -0
  431. package/src/react/components/dataDisplay.spec.tsx +176 -0
  432. package/src/react/components/dialog.tsx +4 -1
  433. package/src/react/dashboard-grid/WidgetFrame.tsx +2 -2
  434. package/src/react/emoji/EmojiPicker.tsx +1 -1
  435. package/src/react/file-tree/FileTree.spec.tsx +49 -0
  436. package/src/react/file-tree/FileTree.tsx +37 -4
  437. package/src/react/filter-rail/FilterRail.tsx +1 -1
  438. package/src/react/icons.tsx +1 -1
  439. package/src/react/index.ts +28 -3
  440. package/src/react/lib/menuSections.ts +33 -0
  441. package/src/react/markdown/Markdown.tsx +2 -2
  442. package/src/react/media/HoverScrubVideoThumb.tsx +2 -2
  443. package/src/react/media/MaskPainter.tsx +1 -1
  444. package/src/react/media/MediaUploadControl.tsx +1 -1
  445. package/src/react/media/VideoTrimmer.tsx +1 -1
  446. package/src/react/media/denseGallery.css +5 -1
  447. package/src/react/namespace/NamespaceTargetCapsule.tsx +1 -1
  448. package/src/react/nav/BreadcrumbTrail.tsx +2 -2
  449. package/src/react/nav/CommandBar.tsx +14 -3
  450. package/src/react/nav/PlaceBar.tsx +18 -8
  451. package/src/react/nav/SideNav.tsx +99 -17
  452. package/src/react/nav/TopBar.spec.tsx +78 -21
  453. package/src/react/nav/TopBar.tsx +270 -98
  454. package/src/react/nav/index.ts +8 -1
  455. package/src/react/nav/nav.spec.tsx +46 -2
  456. package/src/react/nav/navShared.tsx +23 -2
  457. package/src/react/notifications/NotificationFeed.tsx +2 -2
  458. package/src/react/palette/Command.tsx +1 -1
  459. package/src/react/palette/CommandPalette.spec.tsx +31 -0
  460. package/src/react/palette/CommandPalette.tsx +108 -11
  461. package/src/react/palette/commandSources.tsx +186 -0
  462. package/src/react/palette/index.ts +7 -0
  463. package/src/react/rich-text/RichTextEditor.tsx +1 -1
  464. package/src/react/rich-text/RichTextToolbar.tsx +1 -1
  465. package/src/styles-areas/analytics.css +16 -16
  466. package/src/styles-areas/auth.css +3 -3
  467. package/src/styles-areas/calendar.css +6 -6
  468. package/src/styles-areas/chart-table.css +1 -1
  469. package/src/styles-areas/charts.css +3 -3
  470. package/src/styles-areas/chat.css +7 -6
  471. package/src/styles-areas/companion-link.css +2 -2
  472. package/src/styles-areas/core.css +98 -89
  473. package/src/styles-areas/dashboard-grid.css +4 -4
  474. package/src/styles-areas/data-table.css +16 -16
  475. package/src/styles-areas/disk-usage.css +5 -5
  476. package/src/styles-areas/emoji.css +2 -2
  477. package/src/styles-areas/fields.css +13 -14
  478. package/src/styles-areas/filter-rail.css +4 -4
  479. package/src/styles-areas/folder-tree.css +3 -3
  480. package/src/styles-areas/markdown.css +2 -1
  481. package/src/styles-areas/master-detail.css +3 -3
  482. package/src/styles-areas/media-gallery.css +8 -8
  483. package/src/styles-areas/media.css +12 -13
  484. package/src/styles-areas/nav.css +28 -14
  485. package/src/styles-areas/notifications.css +4 -4
  486. package/src/styles-areas/overlays.css +4 -4
  487. package/src/styles-areas/palette.css +4 -3
  488. package/src/styles-areas/rich-text.css +3 -3
  489. package/src/styles-areas/sharing.css +4 -3
  490. package/src/styles-areas/stats.css +9 -8
  491. package/src/styles-areas/test-report.css +3 -3
  492. package/src/styles-areas/wizard.css +6 -5
  493. package/src/styles-areas/workbench.css +10 -9
  494. package/src/styles-areas/workbook-viewer.css +2 -2
  495. package/src/styles-utilities.css +272 -187
  496. package/src/testing/designAudit.ts +3 -0
  497. package/src/testing/designBaseline.ts +4 -0
  498. package/src/theme.css +103 -2
  499. package/src/themeDarkScopeAliases.spec.ts +65 -0
@@ -1,12 +1,24 @@
1
- import type { ElementType, ReactNode, Ref } from 'react';
2
- import type { GateFn, NavigationModel, NavPreferences } from 'cursedbelt-core/navigation';
1
+ import { type ElementType, type ReactNode, type Ref, useLayoutEffect, useRef } from 'react';
2
+ import {
3
+ allowAllGate,
4
+ deriveSideNavDomains,
5
+ type GateFn,
6
+ type NavigationModel,
7
+ type NavPreferences,
8
+ } from 'cursedbelt-core/navigation';
3
9
  import { useKeyboardInset } from '../hooks/useKeyboardInset.js';
4
10
  import { cn } from '../lib/cn.js';
11
+ import { devWarnOnce } from '../lib/devWarn.js';
5
12
  import { CommandBar } from '../nav/CommandBar.js';
6
- import type { IconResolver, NavBadgeTone } from '../nav/navShared.js';
13
+ import { type IconResolver, isDomainActive, type NavBadgeTone, renderIcon, routeClick } from '../nav/navShared.js';
7
14
  import { PlaceBar } from '../nav/PlaceBar.js';
8
15
  import { SideNav } from '../nav/SideNav.js';
16
+ import { TopBarPlaces } from '../nav/TopBar.js';
17
+ import { CommandPalette } from '../palette/CommandPalette.js';
18
+ import { CommandSourceProvider, useCommandSources } from '../palette/commandSources.js';
19
+ import { useCommandPalette } from '../palette/useCommandPalette.js';
9
20
  import { AmbientBackdrop, type AmbientBackdropProps } from './AmbientBackdrop.js';
21
+ import { BannerLane, type ShellBanner } from './BannerLane.js';
10
22
  import { ScrollArea, type ScrollAreaSpace } from './ScrollArea.js';
11
23
 
12
24
  export interface AppShellProps {
@@ -14,11 +26,15 @@ export interface AppShellProps {
14
26
  children: ReactNode;
15
27
 
16
28
  /**
17
- * The navigation model — the single `navigationModel` registry generalized to the
18
- * UX north-star's **small fixed place-set** (Today/Library/People/Calendar/Play +
19
- * a role-gated Operate). The shell renders it as the slim desktop **place rail**
20
- * (`SideNav`, md+) and the mobile capture-forward **`PlaceBar`** (below md) — never
21
- * a 256px hub list and never a hamburger drawer. Omit for chromeless pages.
29
+ * The navigation model — the one registry every nav surface reads. The app passes it and
30
+ * the SHELL picks the pattern from the number of places (NAV-3):
31
+ *
32
+ * | places | ≥1024px | 768–1023px | <768px |
33
+ * |---|---|---|---|
34
+ * | 2–5 | labeled `TopBar` places in the header's center | same | `PlaceBar`, every place |
35
+ * | 6+ | `SideNav` with labeled sections, 240px | the 64px icon rail | `PlaceBar`, top 4 + More |
36
+ *
37
+ * Six or more places mount the ⌘K `CommandPalette` too (NAV-9). Omit for chromeless pages.
22
38
  */
23
39
  nav?: NavigationModel;
24
40
 
@@ -50,9 +66,34 @@ export interface AppShellProps {
50
66
  /** Brand slot — top-left of the command spine (logo / app name). */
51
67
  navHeader?: ReactNode;
52
68
 
53
- /** Slot at the bottom of the desktop place rail (theme toggle, secondary links). */
69
+ /**
70
+ * The sidebar's footer (NAV-13): the version, `BuildFreshness` and a Settings link, set in
71
+ * 12px muted type. Only a 6+-place shell has a sidebar; a 2–5-place app keeps these in
72
+ * Settings → About.
73
+ */
54
74
  navFooter?: ReactNode;
55
75
 
76
+ /**
77
+ * App-wide conditions — a sign-in broke, the Mac is asleep, the app is offline — as the ONE
78
+ * banner lane between the header and the page (NAV-10). One line each; two or more collapse
79
+ * into "N problems · View". Never a card inside the page.
80
+ */
81
+ banners?: readonly ShellBanner[];
82
+
83
+ /**
84
+ * The header's height (NAV-1): `dense` 48px for a tool (station, desk, binary-server),
85
+ * `roomy` 64px for media and consumer apps. Unset, it reads `--header-h` (64px; 48px
86
+ * inside `.cb-dense`).
87
+ */
88
+ density?: 'roomy' | 'dense';
89
+
90
+ /**
91
+ * Mount the shell's own ⌘K `CommandPalette` — every place preloaded, plus whatever pages
92
+ * contribute through `useCommandSource`. `'auto'` (default) mounts it for 6+ places
93
+ * (NAV-9). Ignored when the app renders its own through `palette`.
94
+ */
95
+ commandPalette?: boolean | 'auto';
96
+
56
97
  /**
57
98
  * Universal capture (UX north-star §8.2) — wired to the command spine's capture
58
99
  * hero (desktop) and the `PlaceBar`'s raised center FAB (mobile). Capture is a
@@ -94,7 +135,7 @@ export interface AppShellProps {
94
135
  */
95
136
  palette?: ReactNode;
96
137
 
97
- /** Max places in the mobile `PlaceBar` thumb zone (rest reachable via command). Default 5. */
138
+ /** Max places in the mobile `PlaceBar` thumb zone; past it, `max - 1` places + More. Default 5. */
98
139
  placeBarMax?: number;
99
140
 
100
141
  /** PlaceBar caption policy (`'active'` | `'all'`). Pass `'all'` when place names
@@ -127,52 +168,58 @@ export interface AppShellProps {
127
168
  className?: string;
128
169
  }
129
170
 
130
- // The desktop place rail is a *small spatial floor* (a handful of places), not the old
131
- // 256px hub list — slimmer + quiet, with the command spine carrying the second way to move.
132
- const RAIL_WIDTH = 'w-60'; // 240px
171
+ /** NAV-3's line: up to this many places ride a labeled top bar; past it, a sectioned sidebar. */
172
+ const TOP_BAR_MAX_PLACES = 5;
173
+ /** Past this many places, a sidebar with no sections is a wall (NAV-3: ≤7 items a section). */
174
+ const UNSECTIONED_MAX_PLACES = 7;
133
175
 
134
176
  /**
135
- * THE single application shell. One per app — reconciled to the UX north-star
136
- * (`cursedalchemy/UX_NORTH_STAR.md`, §§6–7, 10; reconciliation #311 + `SHELL.md`).
177
+ * THE single application shell. One per app — the frame every page sits in
178
+ * (`docs/design/shell-and-navigation.md`, NAV-1…NAV-14). The app passes ONE navigation
179
+ * model and the shell decides how to draw it, so no app can pick the wrong pattern:
137
180
  *
138
- * **What changed, and why** (the old form was kept by inertia and failed the §12
139
- * anti-anchoring gate, not preserved as optimal):
140
- * - The fixed **256px hub-list sidebar** → a slim, quiet **place rail** (the small
141
- * fixed place-set is a defended spatial anchor; the *hub-list form* is not).
142
- * - The **hamburger → `translate-x` drawer** → the capture-forward **`PlaceBar`**: the
143
- * place-set in the thumb zone around a raised center capture FAB (mobile is
144
- * capture-forward, not tab-forward — §7).
145
- * - The buried, invisible ⌘K palette + no capture → the first-class **`CommandBar`
146
- * spine**: a visible command/search field (the co-equal second way to move) + a
147
- * capture hero (universal capture, §8.2).
148
- * - The avatar-in-nav → **account chrome** behind the avatar in the spine (not a door).
181
+ * - **2–5 places** → their labels in the header's center zone (NAV-2: destinations have
182
+ * words from 768px up), every place in the `PlaceBar` on a phone.
183
+ * - **6+ places** → a 240px `SideNav` in labeled sections at ≥1024px, the 64px icon rail
184
+ * at 768–1023px, the `PlaceBar` with the top 4 + More on a phone, and a ⌘K
185
+ * `CommandPalette` preloaded with every place (NAV-9).
149
186
  *
150
- * What is *kept* (proven optimal, §11): the single `navigationModel` registry + its
151
- * coverage test (anti-drift substrate), per-user place prefs, badges, and the
152
- * keyboard-safe single `ScrollArea`. Capabilities preserved; form reimagined.
187
+ * Kept from the shell before it (owner teardowns 2026-07-15, the UX north-star): the single
188
+ * `navigationModel` registry and its coverage test, per-user place prefs, badges, universal
189
+ * capture (the spine's hero on desktop, the `PlaceBar` FAB on a phone), and the
190
+ * keyboard-safe single `ScrollArea`. No hamburger drawer, ever.
153
191
  *
154
192
  * Layout model:
155
193
  * ```
156
194
  * <div h-dvh flex flex-col overflow-hidden> ← dvh excludes the mobile URL bar
157
- * <header /> ← brand + CommandBar spine + account
158
- * <div flex min-h-0 grow> ← rail + content row
159
- * <aside hidden md:flex w-60> ← slim place rail (SideNav, md+ only)
195
+ * <header h-[--header-h]> ← left: brand · center: places or ⌘K field · right: account
196
+ * <BannerLane /> ← NAV-10, only when there is a banner
197
+ * <div flex min-h-0 grow> ← sidebar + content row
198
+ * <aside md:flex lg:hidden w-16 /> ← 6+ places: the icon rail
199
+ * <aside lg:flex w-60 /> ← 6+ places: the sectioned SideNav
160
200
  * <main><ScrollArea>{children}</ScrollArea></main> ← THE ONLY page scroll container
161
201
  * </div>
162
202
  * <div pb-[safe-area]> ← bottomDock + mobile PlaceBar
163
- * {bottomDock}
164
- * <PlaceBar md:hidden /> ← capture-forward bottom bar
165
- * </div>
166
203
  * </div>
167
204
  * ```
168
205
  *
169
206
  * Keyboard model: `useKeyboardInset` (installed exactly once, here) writes
170
207
  * `--keyboard-inset` onto `<html>`; `ScrollArea` reads it via CSS `calc()`.
171
208
  */
172
- export function AppShell({
209
+ export function AppShell(props: AppShellProps) {
210
+ // The provider sits ABOVE the shell, so a page anywhere in `children` can contribute to the
211
+ // palette the shell mounts, and the shell can read what they contributed.
212
+ return (
213
+ <CommandSourceProvider>
214
+ <Shell {...props} />
215
+ </CommandSourceProvider>
216
+ );
217
+ }
218
+
219
+ function Shell({
173
220
  children,
174
221
  nav,
175
- navGate,
222
+ navGate = allowAllGate,
176
223
  activeHref,
177
224
  onNavigate,
178
225
  navLinkComponent,
@@ -183,6 +230,9 @@ export function AppShell({
183
230
  navBadgeTone,
184
231
  navHeader,
185
232
  navFooter,
233
+ banners,
234
+ density,
235
+ commandPalette = 'auto',
186
236
  onCapture,
187
237
  captureLabel,
188
238
  onOpenCommand,
@@ -201,16 +251,68 @@ export function AppShell({
201
251
  }: AppShellProps) {
202
252
  useKeyboardInset();
203
253
 
204
- const showSpine = Boolean(navHeader || account || onOpenCommand || onCapture || commandBarExtra);
254
+ const places = nav ? deriveSideNavDomains(nav, navGate) : [];
255
+ const pattern: 'none' | 'top' | 'side' = !nav ? 'none' : places.length <= TOP_BAR_MAX_PLACES ? 'top' : 'side';
256
+ if (pattern === 'side' && places.length > UNSECTIONED_MAX_PLACES && !places.some((d) => d.section)) {
257
+ devWarnOnce(
258
+ 'AppShell:unsectioned',
259
+ `cursedbelt: <AppShell> was given ${places.length} places and no sections. NAV-3 wants 2–5 labeled sections ` +
260
+ 'of ≤7 places each — give each NavDomain a `section` ("Operate", "Insight", "Limits", "Records").',
261
+ );
262
+ }
263
+
264
+ // NAV-9: the shell's own palette, unless the app renders one.
265
+ const ownPalette = !palette && Boolean(nav) && (commandPalette === 'auto' ? pattern === 'side' : commandPalette);
266
+ const paletteControls = useCommandPalette({ enabled: ownPalette });
267
+ const sources = useCommandSources();
268
+ const openCommand = onOpenCommand ?? (ownPalette ? paletteControls.toggle : undefined);
269
+ const navigate = (path: string) => {
270
+ if (onNavigate) onNavigate(path);
271
+ else window.location.assign(path);
272
+ };
273
+
274
+ const showSpine = Boolean(nav || navHeader || account || openCommand || onCapture || commandBarExtra);
205
275
  const showBottom = Boolean(bottomDock || nav);
276
+ const bottomChromeRef = usePublishedBottomChrome(showBottom);
206
277
  // Chrome that lets the field through. The rails/spine keep their own directional
207
278
  // border either way — only the FILL changes, so the layout is byte-identical and
208
279
  // an app can flip `ambient` on and off without anything reflowing.
209
280
  const chrome = ambient ? 'cb-glass-chrome' : 'bg-background';
210
281
 
282
+ const commandBar = (
283
+ <CommandBar
284
+ // The search field is capped and centered: a 2000px-wide input reads as a form, not a
285
+ // header (Linear and GitHub cap theirs the same way).
286
+ className={pattern === 'top' ? 'shrink-0' : 'min-w-0 max-w-2xl flex-1'}
287
+ compact={pattern === 'top'}
288
+ onCapture={onCapture}
289
+ captureLabel={captureLabel}
290
+ // On mobile the PlaceBar FAB owns capture — don't duplicate it in the spine.
291
+ captureDesktopOnly={Boolean(nav)}
292
+ onOpenCommand={openCommand}
293
+ commandPlaceholder={commandLabel}
294
+ commandHint={commandHint}
295
+ extra={commandBarExtra}
296
+ />
297
+ );
298
+
299
+ const sideNavProps = {
300
+ model: nav as NavigationModel,
301
+ gate: navGate,
302
+ activeHref,
303
+ onNavigate,
304
+ linkComponent: navLinkComponent,
305
+ resolveIcon,
306
+ prefs: navPrefs,
307
+ badges: navBadges,
308
+ badgeTone: navBadgeTone,
309
+ footer: navFooter,
310
+ };
311
+
211
312
  return (
212
313
  <div
213
314
  data-cb-appshell=''
315
+ data-cb-nav-pattern={pattern}
214
316
  className={cn(
215
317
  'flex h-dvh flex-col overflow-hidden bg-background text-foreground',
216
318
  // `isolation: isolate` is load-bearing, not decoration: the blobs blend with
@@ -228,86 +330,89 @@ export function AppShell({
228
330
  <AmbientBackdrop position='absolute' {...(ambient === true ? {} : ambient)} />
229
331
  ) : null}
230
332
 
231
- {/* ── Top spine: brand + command/capture + account ───────────────────── */}
333
+ {/* ── Header: three zones on one midline (NAV-1) ─────────────────────── */}
232
334
  {showSpine && (
233
335
  <header
234
336
  data-cb-appheader=''
337
+ data-cb-density={density}
235
338
  className={cn(
236
- 'relative z-30 flex shrink-0 items-center gap-3 border-b border-border px-3 py-2',
339
+ 'relative z-30 flex shrink-0 items-center gap-3 border-b border-border px-3 md:px-6',
340
+ density === 'dense'
341
+ ? 'h-[var(--header-h-dense)]'
342
+ : density === 'roomy'
343
+ ? 'h-[var(--header-h-roomy)]'
344
+ : 'h-[var(--header-h)]',
237
345
  ambient && chrome,
238
346
  )}
239
347
  >
240
- {navHeader && <div className='shrink-0'>{navHeader}</div>}
241
- <CommandBar
242
- className='min-w-0 flex-1'
243
- onCapture={onCapture}
244
- captureLabel={captureLabel}
245
- // On mobile the PlaceBar FAB owns capture — don't duplicate it in the spine.
246
- captureDesktopOnly={Boolean(nav)}
247
- onOpenCommand={onOpenCommand}
248
- commandPlaceholder={commandLabel}
249
- commandHint={commandHint}
250
- extra={commandBarExtra}
251
- />
252
- {account && (
253
- <div data-cb-account='' className='shrink-0'>
254
- {account}
348
+ <div data-cb-header-zone='left' className='flex shrink-0 items-center'>
349
+ {navHeader}
350
+ </div>
351
+ <div data-cb-header-zone='center' className='flex h-full min-w-0 flex-1 items-center justify-center'>
352
+ {pattern === 'top' ? (
353
+ <TopBarPlaces
354
+ // Below 768px the PlaceBar carries every place (NAV-14), so the header doesn't.
355
+ phonePlaces='none'
356
+ linkComponent={navLinkComponent}
357
+ items={places.map((d) => ({
358
+ id: d.id,
359
+ label: d.label,
360
+ icon: renderIcon(resolveIcon, d.icon),
361
+ href: d.path,
362
+ active: isDomainActive(d, activeHref),
363
+ badge: navBadges?.[d.id],
364
+ badgeTone: navBadgeTone,
365
+ unavailable: d.hostLimit && d.hostLimit.navigable !== true ? d.hostLimit.reason : undefined,
366
+ linkProps: navLinkComponent ? undefined : { onClick: routeClick(d.path, onNavigate) },
367
+ }))}
368
+ />
369
+ ) : (
370
+ commandBar
371
+ )}
372
+ </div>
373
+ {/* The right zone is locked: it never shrinks (NAV-1). */}
374
+ {(pattern === 'top' || account) && (
375
+ <div data-cb-header-zone='right' className='flex shrink-0 items-center gap-2'>
376
+ {pattern === 'top' ? commandBar : null}
377
+ {account && (
378
+ <div data-cb-account='' className='shrink-0'>
379
+ {account}
380
+ </div>
381
+ )}
255
382
  </div>
256
383
  )}
257
384
  </header>
258
385
  )}
259
386
 
260
- {/* ── Body row (slim place rail + content) ───────────────────────────── */}
387
+ {/* ── The one banner lane (NAV-10) ──────────────────────────────────── */}
388
+ {banners && banners.length > 0 ? <BannerLane banners={banners} className={cn(ambient && chrome)} /> : null}
389
+
390
+ {/* ── Body row (sidebar + content) ───────────────────────────────────── */}
261
391
  <div className='relative flex min-h-0 grow overflow-hidden'>
262
- {/* Desktop place rail. Tablet (md→lg) gets the ICON rail — a full-width
263
- 240px rail on an ~820px screen squeezes the workspace into a cramped
264
- column (owner tablet teardown 2026-07-15 §4); the full labeled rail
265
- returns at lg. Two CSS-switched renders (SideNav's `collapsed` is a
266
- prop, not a media query) — the rail is cheap to render twice. */}
267
- {nav && (
392
+ {/* 6+ places: the labeled, sectioned sidebar at lg (240px), the icon rail at md–lg
393
+ (64px — a 240px rail on an ~820px screen squeezes the workspace, owner tablet
394
+ teardown 2026-07-15 §4). Two CSS-switched renders: SideNav's `collapsed` is a
395
+ prop, not a media query, and the rail is cheap to render twice. */}
396
+ {pattern === 'side' && (
268
397
  <>
269
398
  <aside
270
399
  data-cb-nav-rail=''
400
+ data-cb-nav-collapsed=''
271
401
  className={cn(
272
- 'hidden w-16 border-r border-border md:flex md:shrink-0 md:flex-col lg:hidden',
402
+ 'hidden w-[var(--rail-w-collapsed)] border-r border-border md:flex md:shrink-0 md:flex-col lg:hidden',
273
403
  chrome,
274
404
  )}
275
405
  >
276
- <SideNav
277
- model={nav}
278
- gate={navGate}
279
- activeHref={activeHref}
280
- onNavigate={onNavigate}
281
- linkComponent={navLinkComponent}
282
- resolveIcon={resolveIcon}
283
- prefs={navPrefs}
284
- collapsed
285
- badges={navBadges}
286
- badgeTone={navBadgeTone}
287
- footer={navFooter}
288
- />
406
+ <SideNav {...sideNavProps} collapsed />
289
407
  </aside>
290
408
  <aside
291
409
  data-cb-nav-rail=''
292
410
  className={cn(
293
- 'hidden border-r border-border lg:flex lg:shrink-0 lg:flex-col',
411
+ 'hidden w-[var(--rail-w)] border-r border-border lg:flex lg:shrink-0 lg:flex-col',
294
412
  chrome,
295
- RAIL_WIDTH,
296
413
  )}
297
414
  >
298
- <SideNav
299
- model={nav}
300
- gate={navGate}
301
- activeHref={activeHref}
302
- onNavigate={onNavigate}
303
- linkComponent={navLinkComponent}
304
- resolveIcon={resolveIcon}
305
- prefs={navPrefs}
306
- onPrefsChange={onNavPrefsChange}
307
- badges={navBadges}
308
- badgeTone={navBadgeTone}
309
- footer={navFooter}
310
- />
415
+ <SideNav {...sideNavProps} onPrefsChange={onNavPrefsChange} />
311
416
  </aside>
312
417
  </>
313
418
  )}
@@ -325,7 +430,7 @@ export function AppShell({
325
430
 
326
431
  {/* ── Bottom: dock + mobile PlaceBar — the ONLY safe-area owner. ──────── */}
327
432
  {showBottom && (
328
- <div className='shrink-0 pb-[env(safe-area-inset-bottom,0px)]'>
433
+ <div ref={bottomChromeRef} className='shrink-0 pb-[env(safe-area-inset-bottom,0px)]'>
329
434
  {bottomDock && <div className='border-t border-border'>{bottomDock}</div>}
330
435
  {nav && (
331
436
  <PlaceBar
@@ -349,6 +454,44 @@ export function AppShell({
349
454
 
350
455
  {/* The command palette portals its own Dialog; rendered here so one shell owns one. */}
351
456
  {palette}
457
+ {ownPalette && nav ? (
458
+ <CommandPalette
459
+ model={nav}
460
+ gate={navGate}
461
+ open={paletteControls.open}
462
+ onOpenChange={paletteControls.setOpen}
463
+ onNavigate={navigate}
464
+ resolveIcon={resolveIcon}
465
+ currentPath={activeHref}
466
+ places
467
+ sources={sources}
468
+ />
469
+ ) : null}
352
470
  </div>
353
471
  );
354
472
  }
473
+
474
+ /**
475
+ * Publishes the bottom chrome's height (dock + `PlaceBar` + safe area) as
476
+ * `--cb-bottom-chrome-h` on the root, so what floats over the page — the `Toaster`'s stack
477
+ * (OVL-9) — rises above the navigation instead of covering it. A `PlaceBar` hidden at `md` up
478
+ * measures 0, so the desktop stack drops back to the corner by itself.
479
+ */
480
+ function usePublishedBottomChrome(shown: boolean) {
481
+ const ref = useRef<HTMLDivElement | null>(null);
482
+ // biome-ignore lint/correctness/useExhaustiveDependencies: `shown` mounts or drops the element the ref points at.
483
+ useLayoutEffect(() => {
484
+ const el = ref.current;
485
+ const root = document.documentElement;
486
+ if (!el) return;
487
+ const publish = () => root.style.setProperty('--cb-bottom-chrome-h', `${el.offsetHeight}px`);
488
+ publish();
489
+ const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(publish);
490
+ observer?.observe(el);
491
+ return () => {
492
+ observer?.disconnect();
493
+ root.style.removeProperty('--cb-bottom-chrome-h');
494
+ };
495
+ }, [shown]);
496
+ return ref;
497
+ }
@@ -1,6 +1,6 @@
1
1
  import { afterEach, describe, expect, test } from 'bun:test';
2
2
  import { cleanup, render } from '@testing-library/react';
3
- import { AsyncPanel } from './AsyncPanel.js';
3
+ import { AsyncPanel, ageLabel } from './AsyncPanel.js';
4
4
 
5
5
  afterEach(cleanup);
6
6
 
@@ -65,4 +65,10 @@ describe('AsyncPanel', () => {
65
65
  expect(getByText('No volumes reported')).toBeDefined();
66
66
  expect(getByText('The collector has not run.')).toBeDefined();
67
67
  });
68
+
69
+ test('ageLabel reads in the largest whole unit', () => {
70
+ expect(ageLabel(4_000)).toBe('just now');
71
+ expect(ageLabel(12_000)).toBe('12s ago');
72
+ expect(ageLabel(5 * 60_000)).toBe('5m ago');
73
+ });
68
74
  });
@@ -1,7 +1,7 @@
1
- import type { ReactNode } from 'react';
1
+ import { type ReactNode, useEffect, useRef, useState } from 'react';
2
2
  import { Alert } from './Alert.js';
3
3
  import { EmptyState } from './EmptyState.js';
4
- import { SkeletonList } from './Skeleton.js';
4
+ import { ContentSkeleton, type SkeletonVariant } from './Skeleton.js';
5
5
 
6
6
  export interface AsyncPanelProps {
7
7
  loading?: boolean;
@@ -12,8 +12,23 @@ export interface AsyncPanelProps {
12
12
  /** Copy for the empty state. */
13
13
  emptyTitle?: ReactNode;
14
14
  emptyDescription?: ReactNode;
15
- /** Rows of skeleton to show while loading. Default 3. */
15
+ /** Rows (or tiles) of skeleton to show while loading. Default 3. */
16
16
  skeletonRows?: number;
17
+ /**
18
+ * The SHAPE of the first-load skeleton (STA-1): `rows` for a list or table, `tiles` for a
19
+ * stat strip, `chart` for a chart frame at the chart's own height. Default `rows`.
20
+ */
21
+ skeleton?: SkeletonVariant;
22
+ /**
23
+ * A refetch is in flight while the panel already shows data (STA-2). The data stays and a
24
+ * quiet "Updating…" replaces the freshness stamp — the panel never blanks to a skeleton on
25
+ * every poll. `loading` after the first content behaves the same way on its own.
26
+ */
27
+ refreshing?: boolean;
28
+ /** When the shown data was fetched — rendered as "Updated 12s ago" (STA-2, STA-8). */
29
+ updatedAt?: number | Date | null;
30
+ /** Past this age the stamp turns amber and says so ("No update for 5m", STA-8). */
31
+ staleAfterMs?: number;
17
32
  /**
18
33
  * Names the failing section in its error message ("AWS cost is unavailable").
19
34
  * Without it the reader gets "Something went wrong" on a page of twelve
@@ -25,6 +40,54 @@ export interface AsyncPanelProps {
25
40
  children: ReactNode;
26
41
  }
27
42
 
43
+ /** "just now", "12s ago", "3m ago", "2h ago", "4d ago" — the freshness stamp's wording. */
44
+ export function ageLabel(ms: number): string {
45
+ if (!Number.isFinite(ms) || ms < 5_000) return 'just now';
46
+ const s = Math.floor(ms / 1000);
47
+ if (s < 60) return `${s}s ago`;
48
+ const m = Math.floor(s / 60);
49
+ if (m < 60) return `${m}m ago`;
50
+ const h = Math.floor(m / 60);
51
+ if (h < 48) return `${h}h ago`;
52
+ return `${Math.floor(h / 24)}d ago`;
53
+ }
54
+
55
+ /** Re-render on a slow tick so an age keeps reading true without a refetch. */
56
+ function useNow(active: boolean, everyMs = 10_000): number {
57
+ const [now, setNow] = useState(() => Date.now());
58
+ useEffect(() => {
59
+ if (!active) return;
60
+ const id = setInterval(() => setNow(Date.now()), everyMs);
61
+ return () => clearInterval(id);
62
+ }, [active, everyMs]);
63
+ return now;
64
+ }
65
+
66
+ function Freshness({
67
+ refreshing,
68
+ updatedAt,
69
+ staleAfterMs,
70
+ }: {
71
+ refreshing: boolean;
72
+ updatedAt: number | Date | null | undefined;
73
+ staleAfterMs: number | undefined;
74
+ }) {
75
+ const at = updatedAt == null ? null : typeof updatedAt === 'number' ? updatedAt : updatedAt.getTime();
76
+ const now = useNow(at != null);
77
+ if (!refreshing && at == null) return null;
78
+ const age = at == null ? 0 : Math.max(0, now - at);
79
+ const stale = !refreshing && staleAfterMs != null && at != null && age > staleAfterMs;
80
+ return (
81
+ <p
82
+ data-cb-freshness={refreshing ? 'updating' : stale ? 'stale' : 'fresh'}
83
+ aria-live='polite'
84
+ className={stale ? 'text-right text-xs tabular-nums text-warning' : 'text-right text-xs tabular-nums text-muted-foreground'}
85
+ >
86
+ {refreshing ? 'Updating…' : stale ? `No update for ${ageLabel(age).replace(' ago', '')}` : `Updated ${ageLabel(age)}`}
87
+ </p>
88
+ );
89
+ }
90
+
28
91
  const messageOf = (error: unknown): string => {
29
92
  if (error instanceof Error) return error.message;
30
93
  if (typeof error === 'string') return error;
@@ -54,11 +117,20 @@ export function AsyncPanel({
54
117
  emptyTitle = 'Nothing to show',
55
118
  emptyDescription,
56
119
  skeletonRows = 3,
120
+ skeleton = 'rows',
121
+ refreshing = false,
122
+ updatedAt,
123
+ staleAfterMs,
57
124
  label,
58
125
  onRetryAction,
59
126
  children,
60
127
  }: AsyncPanelProps) {
61
- if (loading) return <SkeletonList rows={skeletonRows} />;
128
+ // STA-2: once the panel has shown its content, a later `loading` is a REFRESH — the content
129
+ // stays on screen. Only the first load draws the skeleton.
130
+ const shownContent = useRef(false);
131
+ if (loading && !shownContent.current) {
132
+ return <ContentSkeleton variant={skeleton} count={skeletonRows} />;
133
+ }
62
134
  if (error) {
63
135
  return (
64
136
  <Alert tone='warning' {...(onRetryAction ? { action: onRetryAction } : {})}>
@@ -76,5 +148,13 @@ export function AsyncPanel({
76
148
  />
77
149
  );
78
150
  }
79
- return <>{children}</>;
151
+ shownContent.current = true;
152
+ const busy = refreshing || loading === true;
153
+ if (!busy && updatedAt == null) return <>{children}</>;
154
+ return (
155
+ <div className='flex min-w-0 flex-col gap-2' aria-busy={busy || undefined}>
156
+ <Freshness refreshing={busy} updatedAt={updatedAt} staleAfterMs={staleAfterMs} />
157
+ {children}
158
+ </div>
159
+ );
80
160
  }