basalt-ui 1.24.0 → 1.26.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 (467) hide show
  1. package/AGENTS.md +8 -6
  2. package/CHANGELOG.md +25 -0
  3. package/MIGRATING.md +547 -21
  4. package/README.md +46 -33
  5. package/agent/rules/basalt-batteries.md +205 -0
  6. package/agent/rules/basalt-charts.md +125 -555
  7. package/agent/rules/basalt-controls.md +155 -0
  8. package/agent/rules/basalt-mantine.md +146 -439
  9. package/agent/rules/basalt-state.md +113 -96
  10. package/agent/rules/basalt-tokens.md +126 -288
  11. package/agent/skills/basalt-app/SKILL.md +70 -195
  12. package/agent/skills/basalt-charts/SKILL.md +54 -234
  13. package/agent/skills/basalt-design/SKILL.md +56 -248
  14. package/agent/templates/CLAUDE-block.md.tpl +31 -40
  15. package/agent/templates/DESIGN.md.tpl +22 -51
  16. package/configs/oxlint-plugin.js +1275 -31
  17. package/configs/oxlint-plugin.test.ts +947 -11
  18. package/configs/oxlint.json +11 -3
  19. package/dist/charts/cursor/resolve.d.ts +18 -0
  20. package/dist/charts/cursor/resolve.d.ts.map +1 -1
  21. package/dist/charts/cursor/resolve.js +15 -6
  22. package/dist/charts/cursor/scope.d.ts +6 -3
  23. package/dist/charts/cursor/scope.d.ts.map +1 -1
  24. package/dist/charts/cursor/store.d.ts +5 -1
  25. package/dist/charts/cursor/store.d.ts.map +1 -1
  26. package/dist/charts/cursor/store.js +4 -4
  27. package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
  28. package/dist/charts/hooks/useChartCursor.js +9 -8
  29. package/dist/charts/index.d.ts +2 -2
  30. package/dist/charts/index.d.ts.map +1 -1
  31. package/dist/charts/primitives/ChartCard.d.ts +34 -9
  32. package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
  33. package/dist/charts/primitives/ChartCard.js +29 -114
  34. package/dist/charts/primitives/ChartCard.test.d.ts +2 -0
  35. package/dist/charts/primitives/ChartCard.test.d.ts.map +1 -0
  36. package/dist/charts/sparklines/BarSparkline.d.ts +26 -2
  37. package/dist/charts/sparklines/BarSparkline.d.ts.map +1 -1
  38. package/dist/charts/sparklines/BarSparkline.js +18 -4
  39. package/dist/cli/check-coverage.test.d.ts +2 -0
  40. package/dist/cli/check-coverage.test.d.ts.map +1 -0
  41. package/dist/cli/index.d.ts +67 -4
  42. package/dist/cli/index.d.ts.map +1 -1
  43. package/dist/cli/index.js +225 -7
  44. package/dist/connectivity/connectivity-indicator.d.ts +4 -1
  45. package/dist/connectivity/connectivity-indicator.d.ts.map +1 -1
  46. package/dist/connectivity/connectivity-indicator.js +1 -1
  47. package/dist/content/index.d.ts +5 -3
  48. package/dist/content/index.d.ts.map +1 -1
  49. package/dist/content/index.js +4 -2
  50. package/dist/content/prose.module.css +7 -28
  51. package/dist/controls/actions.d.ts +169 -0
  52. package/dist/controls/actions.d.ts.map +1 -0
  53. package/dist/controls/actions.js +287 -0
  54. package/dist/controls/actions.module.css +62 -0
  55. package/dist/controls/actions.module.css.d.ts +13 -0
  56. package/dist/controls/actions.test.d.ts +2 -0
  57. package/dist/controls/actions.test.d.ts.map +1 -0
  58. package/dist/controls/actions.type-guard.test.d.ts +2 -0
  59. package/dist/controls/actions.type-guard.test.d.ts.map +1 -0
  60. package/dist/controls/compare-filter.d.ts +41 -0
  61. package/dist/controls/compare-filter.d.ts.map +1 -0
  62. package/dist/controls/compare-filter.js +23 -0
  63. package/dist/controls/control-group.d.ts +19 -0
  64. package/dist/controls/control-group.d.ts.map +1 -0
  65. package/dist/controls/control-group.js +20 -0
  66. package/dist/controls/control-group.test.d.ts +2 -0
  67. package/dist/controls/control-group.test.d.ts.map +1 -0
  68. package/dist/controls/controls.module.css +542 -0
  69. package/dist/controls/controls.module.css.d.ts +35 -0
  70. package/dist/controls/controls.router.test.d.ts +2 -0
  71. package/dist/controls/controls.router.test.d.ts.map +1 -0
  72. package/dist/controls/enum-filter.d.ts +30 -0
  73. package/dist/controls/enum-filter.d.ts.map +1 -0
  74. package/dist/controls/enum-filter.js +91 -0
  75. package/dist/controls/filter-context.d.ts +55 -0
  76. package/dist/controls/filter-context.d.ts.map +1 -0
  77. package/dist/controls/filter-context.js +69 -0
  78. package/dist/controls/filter-pill.d.ts +28 -0
  79. package/dist/controls/filter-pill.d.ts.map +1 -0
  80. package/dist/controls/filter-pill.js +70 -0
  81. package/dist/controls/filter-set.d.ts +13 -0
  82. package/dist/controls/filter-set.d.ts.map +1 -0
  83. package/dist/controls/filter-set.js +125 -0
  84. package/dist/controls/filter-set.test.d.ts +2 -0
  85. package/dist/controls/filter-set.test.d.ts.map +1 -0
  86. package/dist/controls/filter-sheet.d.ts +117 -0
  87. package/dist/controls/filter-sheet.d.ts.map +1 -0
  88. package/dist/controls/filter-sheet.js +146 -0
  89. package/dist/controls/glyphs.d.ts +27 -0
  90. package/dist/controls/glyphs.d.ts.map +1 -0
  91. package/dist/controls/glyphs.js +49 -0
  92. package/dist/controls/index.d.ts +19 -0
  93. package/dist/controls/index.d.ts.map +1 -0
  94. package/dist/controls/index.js +44 -0
  95. package/dist/controls/multi-select-filter.d.ts +23 -0
  96. package/dist/controls/multi-select-filter.d.ts.map +1 -0
  97. package/dist/controls/multi-select-filter.js +89 -0
  98. package/dist/controls/peer-boundary.test.d.ts +2 -0
  99. package/dist/controls/peer-boundary.test.d.ts.map +1 -0
  100. package/dist/controls/props-contract.test.d.ts +2 -0
  101. package/dist/controls/props-contract.test.d.ts.map +1 -0
  102. package/dist/controls/range-filter.d.ts +29 -0
  103. package/dist/controls/range-filter.d.ts.map +1 -0
  104. package/dist/controls/range-filter.js +120 -0
  105. package/dist/controls/search-filter.d.ts +10 -0
  106. package/dist/controls/search-filter.d.ts.map +1 -0
  107. package/dist/controls/search-filter.js +81 -0
  108. package/dist/controls/select-filter.d.ts +74 -0
  109. package/dist/controls/select-filter.d.ts.map +1 -0
  110. package/dist/controls/select-filter.js +10 -0
  111. package/dist/controls/sync-button.d.ts +38 -0
  112. package/dist/controls/sync-button.d.ts.map +1 -0
  113. package/dist/controls/sync-button.js +119 -0
  114. package/dist/controls/sync-button.module.css +63 -0
  115. package/dist/controls/sync-button.module.css.d.ts +11 -0
  116. package/dist/controls/sync-button.test.d.ts +2 -0
  117. package/dist/controls/sync-button.test.d.ts.map +1 -0
  118. package/dist/controls/toggle-filter.d.ts +9 -0
  119. package/dist/controls/toggle-filter.d.ts.map +1 -0
  120. package/dist/controls/toggle-filter.js +42 -0
  121. package/dist/controls/view-tabs.d.ts +23 -0
  122. package/dist/controls/view-tabs.d.ts.map +1 -0
  123. package/dist/controls/view-tabs.js +59 -0
  124. package/dist/controls-dates/date-range-picker.d.ts +4 -0
  125. package/dist/controls-dates/date-range-picker.d.ts.map +1 -0
  126. package/dist/controls-dates/date-range-picker.js +18 -0
  127. package/dist/controls-dates/index.d.ts +9 -0
  128. package/dist/controls-dates/index.d.ts.map +1 -0
  129. package/dist/controls-dates/index.js +4 -0
  130. package/dist/dashboard/delta-badge.d.ts +6 -12
  131. package/dist/dashboard/delta-badge.d.ts.map +1 -1
  132. package/dist/dashboard/delta-badge.js +1 -50
  133. package/dist/dashboard/query-state.d.ts +1 -1
  134. package/dist/dashboard/settings-section.d.ts +6 -4
  135. package/dist/dashboard/settings-section.d.ts.map +1 -1
  136. package/dist/dashboard/settings-section.js +23 -16
  137. package/dist/dashboard/settings-section.module.css +10 -0
  138. package/dist/dashboard/settings-section.module.css.d.ts +1 -0
  139. package/dist/dashboard/settings-section.test.d.ts +2 -0
  140. package/dist/dashboard/settings-section.test.d.ts.map +1 -0
  141. package/dist/dashboard/stat-card.d.ts +44 -9
  142. package/dist/dashboard/stat-card.d.ts.map +1 -1
  143. package/dist/dashboard/stat-card.js +80 -48
  144. package/dist/dashboard/stat-card.module.css +82 -0
  145. package/dist/dashboard/stat-card.module.css.d.ts +11 -0
  146. package/dist/data/data-table.d.ts +102 -20
  147. package/dist/data/data-table.d.ts.map +1 -1
  148. package/dist/data/data-table.js +166 -65
  149. package/dist/data/table.d.ts +1 -1
  150. package/dist/data/table.d.ts.map +1 -1
  151. package/dist/guard/grace.test.d.ts +20 -0
  152. package/dist/guard/grace.test.d.ts.map +1 -0
  153. package/dist/guard/index.d.ts +66 -3
  154. package/dist/guard/index.d.ts.map +1 -1
  155. package/dist/guard/index.js +82 -7
  156. package/dist/guard/types.d.ts +3 -3
  157. package/dist/guard/types.d.ts.map +1 -1
  158. package/dist/index.d.ts +12 -2
  159. package/dist/index.d.ts.map +1 -1
  160. package/dist/index.js +13 -10
  161. package/dist/nav/types.d.ts +78 -2
  162. package/dist/nav/types.d.ts.map +1 -1
  163. package/dist/notifications/bell.d.ts +4 -1
  164. package/dist/notifications/bell.d.ts.map +1 -1
  165. package/dist/notifications/index.d.ts +1 -1
  166. package/dist/router-tanstack/field.d.ts +14 -0
  167. package/dist/router-tanstack/field.d.ts.map +1 -0
  168. package/dist/router-tanstack/field.js +4 -0
  169. package/dist/router-tanstack/index.d.ts +6 -2
  170. package/dist/router-tanstack/index.d.ts.map +1 -1
  171. package/dist/router-tanstack/index.js +4 -0
  172. package/dist/router-tanstack/multi-search-param-store.d.ts +4 -52
  173. package/dist/router-tanstack/multi-search-param-store.d.ts.map +1 -1
  174. package/dist/router-tanstack/multi-search-param-store.js +19 -27
  175. package/dist/router-tanstack/search-param-store.d.ts +3 -87
  176. package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
  177. package/dist/router-tanstack/search-param-store.js +16 -45
  178. package/dist/router-tanstack/search-store.d.ts +81 -0
  179. package/dist/router-tanstack/search-store.d.ts.map +1 -0
  180. package/dist/router-tanstack/search-store.js +187 -0
  181. package/dist/router-tanstack/search-store.router.test.d.ts +2 -0
  182. package/dist/router-tanstack/search-store.router.test.d.ts.map +1 -0
  183. package/dist/router-tanstack/search-store.test.d.ts +2 -0
  184. package/dist/router-tanstack/search-store.test.d.ts.map +1 -0
  185. package/dist/router-tanstack/use-nav.d.ts +0 -2
  186. package/dist/router-tanstack/use-nav.d.ts.map +1 -1
  187. package/dist/router-tanstack/use-nav.js +34 -12
  188. package/dist/router-tanstack/use-nav.test.d.ts +2 -0
  189. package/dist/router-tanstack/use-nav.test.d.ts.map +1 -0
  190. package/dist/section/index.d.ts +2 -0
  191. package/dist/section/index.d.ts.map +1 -0
  192. package/dist/section/index.js +4 -0
  193. package/dist/section/section.d.ts +24 -0
  194. package/dist/section/section.d.ts.map +1 -0
  195. package/dist/section/section.js +126 -0
  196. package/dist/section/section.module.css +104 -0
  197. package/dist/section/section.module.css.d.ts +11 -0
  198. package/dist/section/section.test.d.ts +2 -0
  199. package/dist/section/section.test.d.ts.map +1 -0
  200. package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
  201. package/dist/shell/app-breadcrumbs.js +18 -15
  202. package/dist/shell/app-header.module.css +77 -37
  203. package/dist/shell/app-header.module.css.d.ts +2 -1
  204. package/dist/shell/app-mobile-nav.d.ts +9 -2
  205. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  206. package/dist/shell/app-mobile-nav.js +98 -11
  207. package/dist/shell/app-mobile-nav.module.css +8 -0
  208. package/dist/shell/app-mobile-nav.module.css.d.ts +1 -0
  209. package/dist/shell/app-sidebar.d.ts +55 -28
  210. package/dist/shell/app-sidebar.d.ts.map +1 -1
  211. package/dist/shell/app-sidebar.js +116 -67
  212. package/dist/shell/app-sidebar.module.css +303 -20
  213. package/dist/shell/app-sidebar.module.css.d.ts +19 -1
  214. package/dist/shell/index.d.ts +55 -21
  215. package/dist/shell/index.d.ts.map +1 -1
  216. package/dist/shell/index.js +40 -18
  217. package/dist/shell/mobile-nav-model.d.ts +13 -2
  218. package/dist/shell/mobile-nav-model.d.ts.map +1 -1
  219. package/dist/shell/mobile-nav-model.js +15 -1
  220. package/dist/shell/nav-count-badge.d.ts +1 -1
  221. package/dist/shell/page-bar.d.ts +51 -0
  222. package/dist/shell/page-bar.d.ts.map +1 -0
  223. package/dist/shell/page-bar.js +154 -0
  224. package/dist/shell/page-bar.module.css +178 -0
  225. package/dist/shell/page-bar.module.css.d.ts +17 -0
  226. package/dist/shell/page-bar.test.d.ts +2 -0
  227. package/dist/shell/page-bar.test.d.ts.map +1 -0
  228. package/dist/shell/sidebar-block-model.d.ts +48 -0
  229. package/dist/shell/sidebar-block-model.d.ts.map +1 -0
  230. package/dist/shell/sidebar-block-model.js +39 -0
  231. package/dist/shell/sidebar-blocks.d.ts +32 -0
  232. package/dist/shell/sidebar-blocks.d.ts.map +1 -0
  233. package/dist/shell/sidebar-blocks.js +169 -0
  234. package/dist/shell/sidebar-search.d.ts +13 -1
  235. package/dist/shell/sidebar-search.d.ts.map +1 -1
  236. package/dist/shell/sidebar-search.js +34 -1
  237. package/dist/shell/sidebar-search.module.css +26 -0
  238. package/dist/shell/sidebar-search.module.css.d.ts +2 -0
  239. package/dist/state/fields.d.ts +392 -0
  240. package/dist/state/fields.d.ts.map +1 -0
  241. package/dist/state/fields.js +369 -0
  242. package/dist/state/fields.test.d.ts +2 -0
  243. package/dist/state/fields.test.d.ts.map +1 -0
  244. package/dist/state/persisted.d.ts +48 -0
  245. package/dist/state/persisted.d.ts.map +1 -0
  246. package/dist/state/persisted.js +105 -0
  247. package/dist/state.d.ts +11 -56
  248. package/dist/state.d.ts.map +1 -1
  249. package/dist/state.js +14 -120
  250. package/dist/styles.css +13 -0
  251. package/dist/surfaces.d.ts +106 -24
  252. package/dist/surfaces.d.ts.map +1 -1
  253. package/dist/surfaces.js +155 -19
  254. package/dist/theme/controls.module.css +16 -1
  255. package/dist/theme/ctl-theme.d.ts +32 -0
  256. package/dist/theme/ctl-theme.d.ts.map +1 -0
  257. package/dist/theme/ctl-theme.js +54 -0
  258. package/dist/theme/ctl-theme.test.d.ts +2 -0
  259. package/dist/theme/ctl-theme.test.d.ts.map +1 -0
  260. package/dist/theme/ctl-tier-coverage.test.d.ts +2 -0
  261. package/dist/theme/ctl-tier-coverage.test.d.ts.map +1 -0
  262. package/dist/theme/divider-law.test.d.ts +2 -0
  263. package/dist/theme/divider-law.test.d.ts.map +1 -0
  264. package/dist/theme/icon-slot.d.ts +44 -0
  265. package/dist/theme/icon-slot.d.ts.map +1 -0
  266. package/dist/theme/icon-slot.js +21 -0
  267. package/dist/theme/icon-slot.module.css +47 -0
  268. package/dist/theme/icon-slot.module.css.d.ts +8 -0
  269. package/dist/theme/icon-slot.test.d.ts +2 -0
  270. package/dist/theme/icon-slot.test.d.ts.map +1 -0
  271. package/dist/theme/index.d.ts +1 -0
  272. package/dist/theme/index.d.ts.map +1 -1
  273. package/dist/theme/index.js +82 -5
  274. package/dist/theme/layout-rhythm-css.test.d.ts +2 -0
  275. package/dist/theme/layout-rhythm-css.test.d.ts.map +1 -0
  276. package/dist/theme/segmented-control.module.css +20 -0
  277. package/dist/theme/shadow-surfaces.test.d.ts.map +1 -1
  278. package/dist/tokens/index.d.ts +12 -0
  279. package/dist/tokens/index.d.ts.map +1 -1
  280. package/dist/tokens/index.js +38 -8
  281. package/dist/tokens/palette.d.ts +97 -61
  282. package/dist/tokens/palette.d.ts.map +1 -1
  283. package/dist/tokens/palette.js +76 -38
  284. package/dist/tokens.css +10 -2
  285. package/dist/widget-header/delta-badge.d.ts +13 -0
  286. package/dist/widget-header/delta-badge.d.ts.map +1 -0
  287. package/dist/widget-header/delta-badge.js +29 -0
  288. package/dist/widget-header/delta-badge.test.d.ts +2 -0
  289. package/dist/widget-header/delta-badge.test.d.ts.map +1 -0
  290. package/dist/widget-header/index.d.ts +3 -0
  291. package/dist/widget-header/index.d.ts.map +1 -0
  292. package/dist/widget-header/index.js +6 -0
  293. package/dist/widget-header/no-mantine.test.d.ts +2 -0
  294. package/dist/widget-header/no-mantine.test.d.ts.map +1 -0
  295. package/dist/widget-header/widget-header.d.ts +37 -0
  296. package/dist/widget-header/widget-header.d.ts.map +1 -0
  297. package/dist/widget-header/widget-header.js +77 -0
  298. package/dist/widget-header/widget-header.module.css +335 -0
  299. package/dist/widget-header/widget-header.module.css.d.ts +24 -0
  300. package/dist/widget-header/widget-header.test.d.ts +2 -0
  301. package/dist/widget-header/widget-header.test.d.ts.map +1 -0
  302. package/llms.txt +30 -17
  303. package/package.json +14 -1
  304. package/src/charts/cursor/resolve.test.ts +29 -1
  305. package/src/charts/cursor/resolve.ts +38 -6
  306. package/src/charts/cursor/scope.tsx +6 -3
  307. package/src/charts/cursor/store.test.ts +19 -1
  308. package/src/charts/cursor/store.ts +10 -5
  309. package/src/charts/hooks/useChartCursor.ts +16 -8
  310. package/src/charts/index.ts +2 -2
  311. package/src/charts/primitives/CartesianChart.test.tsx +124 -0
  312. package/src/charts/primitives/ChartCard.test.tsx +66 -0
  313. package/src/charts/primitives/ChartCard.tsx +86 -146
  314. package/src/charts/sparklines/BarSparkline.tsx +43 -5
  315. package/src/cli/check-coverage.test.ts +297 -0
  316. package/src/cli/check-theme.test.ts +38 -14
  317. package/src/cli/index.ts +392 -16
  318. package/src/cli/init-sync.test.ts +97 -0
  319. package/src/connectivity/connectivity-indicator.tsx +16 -9
  320. package/src/content/index.ts +16 -3
  321. package/src/content/prose.module.css +7 -28
  322. package/src/content/prose.module.css.test.ts +8 -24
  323. package/src/controls/actions.module.css +62 -0
  324. package/src/controls/actions.module.css.d.ts +13 -0
  325. package/src/controls/actions.test.tsx +252 -0
  326. package/src/controls/actions.tsx +655 -0
  327. package/src/controls/actions.type-guard.test.ts +63 -0
  328. package/src/controls/compare-filter.tsx +64 -0
  329. package/src/controls/control-group.test.tsx +229 -0
  330. package/src/controls/control-group.tsx +63 -0
  331. package/src/controls/controls.module.css +542 -0
  332. package/src/controls/controls.module.css.d.ts +35 -0
  333. package/src/controls/controls.router.test.tsx +637 -0
  334. package/src/controls/enum-filter.tsx +159 -0
  335. package/src/controls/filter-context.tsx +153 -0
  336. package/src/controls/filter-pill.tsx +120 -0
  337. package/src/controls/filter-set.test.tsx +472 -0
  338. package/src/controls/filter-set.tsx +225 -0
  339. package/src/controls/filter-sheet.tsx +285 -0
  340. package/src/controls/glyphs.tsx +81 -0
  341. package/src/controls/index.ts +48 -0
  342. package/src/controls/multi-select-filter.tsx +140 -0
  343. package/src/controls/peer-boundary.test.ts +85 -0
  344. package/src/controls/props-contract.test.tsx +106 -0
  345. package/src/controls/range-filter.tsx +184 -0
  346. package/src/controls/search-filter.tsx +123 -0
  347. package/src/controls/select-filter.tsx +89 -0
  348. package/src/controls/sync-button.module.css +63 -0
  349. package/src/controls/sync-button.module.css.d.ts +11 -0
  350. package/src/controls/sync-button.test.tsx +183 -0
  351. package/src/controls/sync-button.tsx +197 -0
  352. package/src/controls/toggle-filter.tsx +60 -0
  353. package/src/controls/view-tabs.tsx +110 -0
  354. package/src/controls-dates/date-range-picker.tsx +42 -0
  355. package/src/controls-dates/index.ts +8 -0
  356. package/src/dashboard/delta-badge.tsx +4 -74
  357. package/src/dashboard/query-state.tsx +1 -1
  358. package/src/dashboard/settings-section.module.css +10 -0
  359. package/src/dashboard/settings-section.module.css.d.ts +1 -0
  360. package/src/dashboard/settings-section.test.tsx +56 -0
  361. package/src/dashboard/settings-section.tsx +33 -30
  362. package/src/dashboard/stat-card.module.css +82 -0
  363. package/src/dashboard/stat-card.module.css.d.ts +11 -0
  364. package/src/dashboard/stat-card.test.tsx +67 -1
  365. package/src/dashboard/stat-card.tsx +209 -68
  366. package/src/data/data-table.test.tsx +282 -6
  367. package/src/data/data-table.tsx +429 -94
  368. package/src/data/table.ts +2 -0
  369. package/src/guard/check-source.test.ts +126 -13
  370. package/src/guard/grace.test.ts +149 -0
  371. package/src/guard/index.ts +172 -29
  372. package/src/guard/types.ts +4 -2
  373. package/src/index.ts +46 -5
  374. package/src/nav/types.ts +84 -2
  375. package/src/notifications/bell.tsx +6 -3
  376. package/src/notifications/index.ts +1 -1
  377. package/src/router-tanstack/field.ts +31 -0
  378. package/src/router-tanstack/index.ts +24 -2
  379. package/src/router-tanstack/multi-search-param-store.test.ts +71 -3
  380. package/src/router-tanstack/multi-search-param-store.ts +46 -77
  381. package/src/router-tanstack/search-param-store.ts +42 -159
  382. package/src/router-tanstack/search-store.router.test.tsx +661 -0
  383. package/src/router-tanstack/search-store.test.ts +418 -0
  384. package/src/router-tanstack/search-store.ts +380 -0
  385. package/src/router-tanstack/use-nav.test.tsx +187 -0
  386. package/src/router-tanstack/use-nav.ts +72 -14
  387. package/src/section/index.ts +1 -0
  388. package/src/section/section.module.css +104 -0
  389. package/src/section/section.module.css.d.ts +11 -0
  390. package/src/section/section.test.tsx +204 -0
  391. package/src/section/section.tsx +220 -0
  392. package/src/shell/app-breadcrumbs.tsx +44 -31
  393. package/src/shell/app-header.module.css +77 -37
  394. package/src/shell/app-header.module.css.d.ts +2 -1
  395. package/src/shell/app-mobile-nav.module.css +8 -0
  396. package/src/shell/app-mobile-nav.module.css.d.ts +1 -0
  397. package/src/shell/app-mobile-nav.tsx +147 -8
  398. package/src/shell/app-sidebar-account.tsx +2 -2
  399. package/src/shell/app-sidebar.module.css +303 -20
  400. package/src/shell/app-sidebar.module.css.d.ts +19 -1
  401. package/src/shell/app-sidebar.test.tsx +456 -41
  402. package/src/shell/app-sidebar.tsx +276 -140
  403. package/src/shell/index.test.tsx +291 -5
  404. package/src/shell/index.tsx +137 -38
  405. package/src/shell/mobile-nav-model.test.ts +228 -7
  406. package/src/shell/mobile-nav-model.ts +53 -3
  407. package/src/shell/nav-count-badge.tsx +1 -1
  408. package/src/shell/page-bar.module.css +178 -0
  409. package/src/shell/page-bar.module.css.d.ts +17 -0
  410. package/src/shell/page-bar.test.tsx +316 -0
  411. package/src/shell/page-bar.tsx +315 -0
  412. package/src/shell/sidebar-block-model.ts +83 -0
  413. package/src/shell/sidebar-blocks.tsx +301 -0
  414. package/src/shell/sidebar-search.module.css +26 -0
  415. package/src/shell/sidebar-search.module.css.d.ts +2 -0
  416. package/src/shell/sidebar-search.tsx +71 -2
  417. package/src/state/fields.test.tsx +258 -0
  418. package/src/state/fields.ts +892 -0
  419. package/src/state/persisted.ts +211 -0
  420. package/src/state.ts +46 -240
  421. package/src/styles.css +13 -0
  422. package/src/surfaces.ts +203 -36
  423. package/src/theme/controls.module.css +16 -1
  424. package/src/theme/ctl-theme.test.tsx +275 -0
  425. package/src/theme/ctl-theme.tsx +138 -0
  426. package/src/theme/ctl-tier-coverage.test.ts +170 -0
  427. package/src/theme/density-relations.test.ts +106 -51
  428. package/src/theme/divider-law.test.ts +259 -0
  429. package/src/theme/icon-slot.module.css +47 -0
  430. package/src/theme/icon-slot.module.css.d.ts +8 -0
  431. package/src/theme/icon-slot.test.tsx +275 -0
  432. package/src/theme/icon-slot.tsx +59 -0
  433. package/src/theme/index.ts +165 -3
  434. package/src/theme/layout-rhythm-css.test.ts +72 -0
  435. package/src/theme/radius.test.ts +11 -1
  436. package/src/theme/segmented-control.module.css +20 -0
  437. package/src/theme/shadow-surfaces.test.ts +20 -7
  438. package/src/theme/spacing.test.ts +75 -27
  439. package/src/theme/use-basalt-spacing.ts +2 -2
  440. package/src/tokens/density.test.ts +7 -25
  441. package/src/tokens/index.ts +48 -8
  442. package/src/tokens/palette.ts +107 -73
  443. package/src/widget-header/delta-badge.test.tsx +60 -0
  444. package/src/widget-header/delta-badge.tsx +57 -0
  445. package/src/widget-header/index.ts +2 -0
  446. package/src/widget-header/no-mantine.test.ts +33 -0
  447. package/src/widget-header/widget-header.module.css +335 -0
  448. package/src/widget-header/widget-header.module.css.d.ts +24 -0
  449. package/src/widget-header/widget-header.test.tsx +187 -0
  450. package/src/widget-header/widget-header.tsx +168 -0
  451. package/agent/rules/basalt-agent.md +0 -452
  452. package/agent/rules/basalt-app.md +0 -246
  453. package/agent/rules/basalt-commands.md +0 -320
  454. package/agent/rules/basalt-content.md +0 -409
  455. package/agent/rules/basalt-data.md +0 -269
  456. package/agent/rules/basalt-forms.md +0 -186
  457. package/agent/rules/basalt-notifications.md +0 -264
  458. package/agent/rules/basalt-query.md +0 -187
  459. package/agent/rules/basalt-router.md +0 -336
  460. package/dist/content/article-filter-bar.d.ts +0 -40
  461. package/dist/content/article-filter-bar.d.ts.map +0 -1
  462. package/dist/content/article-filter-bar.js +0 -56
  463. package/dist/shell/page-header.d.ts +0 -13
  464. package/dist/shell/page-header.d.ts.map +0 -1
  465. package/dist/shell/page-header.js +0 -23
  466. package/src/content/article-filter-bar.tsx +0 -88
  467. package/src/shell/page-header.tsx +0 -35
@@ -40,30 +40,63 @@
40
40
 
41
41
  /* Inactive rows: muted text + faint icon. Active rows keep the theme's ink text (via `--nl-color`)
42
42
  * untouched — this selector only ever matches when neither `data-active` nor `aria-current="page"`
43
- * is present. */
44
- .link:not([data-active]):not([aria-current='page']) {
43
+ * is present. `[data-ancestor]` is excluded too: it gets its own ink/accent treatment below and
44
+ * must not fall through to muted/faint here. */
45
+ .link:not([data-active]):not([aria-current='page']):not([data-ancestor]) {
45
46
  color: var(--vx-muted) !important;
46
47
  }
47
48
 
48
- .link:not([data-active]):not([aria-current='page'])
49
+ .link:not([data-active]):not([aria-current='page']):not([data-ancestor])
49
50
  :global(.mantine-NavLink-section[data-position='left']) {
50
51
  color: var(--vx-faint);
51
52
  }
52
53
 
53
54
  /* Hover — ink-6% tint + ink text, for inactive rows only (an active row already carries the
54
- * theme's stronger ink-9% tint and keeps its own hover via `--nl-hover`). */
55
+ * theme's stronger ink-9% tint and keeps its own hover via `--nl-hover`; an ancestor row gets its
56
+ * own hover rule below so its accent icon survives the pointer). */
55
57
  @media (hover: hover) {
56
- .link:not([data-active]):not([aria-current='page']):hover {
58
+ .link:not([data-active]):not([aria-current='page']):not([data-ancestor]):hover {
57
59
  background-color: color-mix(in srgb, var(--vx-ink) 6%, transparent) !important;
58
60
  color: var(--vx-ink) !important;
59
61
  }
60
62
 
61
- .link:not([data-active]):not([aria-current='page']):hover
63
+ .link:not([data-active]):not([aria-current='page']):not([data-ancestor]):hover
62
64
  :global(.mantine-NavLink-section[data-position='left']) {
63
65
  color: var(--vx-ink);
64
66
  }
65
67
  }
66
68
 
69
+ /*
70
+ * Ancestor row (`SidebarItem.ancestor` — a parent on the path to the active row, never active
71
+ * itself, never `aria-current`): must read visibly WEAKER than active and visibly STRONGER than a
72
+ * hovered inactive row, since all three now share the same 6%/9% ink-tint family. Active carries a
73
+ * persistent `--nl-bg` ink-9% fill + weight 600 (theme `nav-link.module.css`); a hovered inactive
74
+ * row carries a transient ink-6% fill. An ancestor therefore gets NO fill at rest (unlike active)
75
+ * and NO weight change (still 400, unlike active's 600) — only the ink label + accent icon that an
76
+ * active row also carries, so it reads as "on the path" without claiming to BE the destination.
77
+ */
78
+ .link[data-ancestor] {
79
+ color: var(--vx-ink) !important;
80
+ }
81
+
82
+ .link[data-ancestor] :global(.mantine-NavLink-section[data-position='left']) {
83
+ color: var(--vx-accent);
84
+ }
85
+
86
+ /* Hover still adds the shared ink-6% tint (same as any inactive row), but must not let the plain
87
+ * inactive hover rule above flatten the icon back to ink — that rule is excluded via
88
+ * `:not([data-ancestor])`, and this one re-asserts the accent icon on top of the shared tint. */
89
+ @media (hover: hover) {
90
+ .link[data-ancestor]:hover {
91
+ background-color: color-mix(in srgb, var(--vx-ink) 6%, transparent) !important;
92
+ color: var(--vx-ink) !important;
93
+ }
94
+
95
+ .link[data-ancestor]:hover :global(.mantine-NavLink-section[data-position='left']) {
96
+ color: var(--vx-accent);
97
+ }
98
+ }
99
+
67
100
  /*
68
101
  * Icon-only ghost buttons in shell chrome (collapse toggle, mobile close) — docs/DESIGN-SPEC.md
69
102
  * §5: transparent, faint icon, hover ink-6% + ink, radius 6. `!important` for the same reason as
@@ -113,6 +146,22 @@
113
146
  font-stretch: 88%;
114
147
  }
115
148
 
149
+ /* Workspace switcher (`brand.menu`) — the brand name plus a chevron, as a row-shaped trigger. The
150
+ * chevron is faint until hover so the brand still reads as the brand, not as a control. */
151
+ .brandButton {
152
+ display: flex;
153
+ align-items: center;
154
+ gap: var(--vx-space-sidebar-section-label-gap);
155
+ min-width: 0;
156
+ padding: 0;
157
+ color: var(--vx-faint);
158
+ cursor: pointer;
159
+ }
160
+
161
+ .brandButton:hover {
162
+ color: var(--vx-ink);
163
+ }
164
+
116
165
  /*
117
166
  * Nav scroll region — a Mantine ScrollArea, whose overlay scrollbar is absolutely positioned inside
118
167
  * the (position: relative; overflow: hidden) root, so it floats over the rows instead of reserving
@@ -151,7 +200,7 @@
151
200
 
152
201
  /*
153
202
  * Footer settings row — IDENTICAL geometry to a nav row (padding 6px 10px, radius 6, gap 10,
154
- * 13px): muted text + faint icon at rest, ink-6% tint + ink on hover, exactly like an inactive
203
+ * 15px): muted text + faint icon at rest, ink-6% tint + ink on hover, exactly like an inactive
155
204
  * nav item, so the footer never reads as a separate control family.
156
205
  */
157
206
  .footerBtn {
@@ -211,7 +260,7 @@
211
260
  }
212
261
 
213
262
  /*
214
- * Identity initials block (docs/DESIGN-SPEC.md §5): 28px, radius 7, ink-10% bg, mono 10.5px
263
+ * Identity initials block (docs/DESIGN-SPEC.md §5): 28px, radius 7, ink-10% bg, mono micro (11px)
215
264
  * weight 500, muted — replaces the generic person glyph for the authenticated state.
216
265
  */
217
266
  .avatar {
@@ -236,7 +285,7 @@
236
285
  color: var(--vx-ink);
237
286
  }
238
287
 
239
- /* Meta line under the name (email / "FREE · MEMBER") — mono 9.5px, uppercase, faint, tracked
288
+ /* Meta line under the name (email / "FREE · MEMBER") — mono micro (11px), uppercase, faint, tracked
240
289
  * 0.03em. */
241
290
  .accountMeta {
242
291
  font-family: var(--basalt-font-mono);
@@ -293,6 +342,214 @@
293
342
  color: var(--vx-ink);
294
343
  }
295
344
 
345
+ /*
346
+ * ── Sidebar blocks (docs/CONTROLS-SPEC.md §2.3) ────────────────────────────────────────────────
347
+ *
348
+ * Same spacing-only separation law as the nav sections above: no rules, no bands, no fills. A
349
+ * block's rows are 32px (`--vx-space-sidebar-block-row-height`) — one step under a nav row, since
350
+ * a block row is reference material rather than a destination.
351
+ */
352
+ .block {
353
+ min-width: 0;
354
+ }
355
+
356
+ .blockHeader {
357
+ display: flex;
358
+ align-items: center;
359
+ gap: var(--vx-space-nav-icon-gap);
360
+ width: 100%;
361
+ padding: 0 var(--vx-space-row-inset-x);
362
+ margin-bottom: var(--vx-space-sidebar-section-label-gap);
363
+ }
364
+
365
+ /* The header's label takes the free width so a count badge / chevron sits hard right. */
366
+ .blockHeader > div:first-of-type {
367
+ flex: 1;
368
+ min-width: 0;
369
+ text-align: left;
370
+ }
371
+
372
+ /* Collapsible variant — text-only hover, exactly like `.sectionHeader`; never a band. */
373
+ .blockHeaderButton {
374
+ color: var(--vx-faint);
375
+ cursor: pointer;
376
+ }
377
+
378
+ .blockHeaderButton:hover .sectionLabel {
379
+ color: var(--vx-ink);
380
+ }
381
+
382
+ .blockIcon {
383
+ position: relative;
384
+ display: flex;
385
+ flex: none;
386
+ align-items: center;
387
+ color: var(--vx-faint);
388
+ }
389
+
390
+ /*
391
+ * Rail dot — the collapsed rail's whole representation of a `count`. Hidden in the expanded
392
+ * sidebar, where the count renders as a real `NavCountBadge` instead: a dot and a number saying
393
+ * the same thing twice is noise, and the rail has no room for the number.
394
+ */
395
+ .blockDot {
396
+ position: absolute;
397
+ top: -2px;
398
+ inset-inline-end: -3px;
399
+ display: none;
400
+ width: 7px;
401
+ height: 7px;
402
+ border-radius: 999px;
403
+ background-color: var(--vx-accent);
404
+ box-shadow: 0 0 0 2px var(--mantine-color-body);
405
+ }
406
+
407
+ .blockItems {
408
+ display: flex;
409
+ flex-direction: column;
410
+ }
411
+
412
+ /* Block row — 32px, small text, muted; the same ink-6% hover every other sidebar row uses. */
413
+ .blockRow {
414
+ display: flex;
415
+ align-items: center;
416
+ gap: var(--vx-space-nav-icon-gap);
417
+ min-height: var(--vx-space-sidebar-block-row-height);
418
+ padding: 0 var(--vx-space-row-inset-x) !important;
419
+ border-radius: var(--vx-radius-ctrl) !important;
420
+ font-size: var(--vx-text-sm);
421
+ color: var(--vx-muted);
422
+ }
423
+
424
+ .blockRow :global(.mantine-NavLink-label) {
425
+ font-size: var(--vx-text-sm);
426
+ }
427
+
428
+ .blockRow :global(.mantine-NavLink-section[data-position='left']) {
429
+ margin-inline-end: 0;
430
+ color: var(--vx-faint);
431
+ }
432
+
433
+ @media (hover: hover) {
434
+ .blockRow:not(.blockRowStatic):hover {
435
+ background-color: color-mix(in srgb, var(--vx-ink) 6%, transparent) !important;
436
+ color: var(--vx-ink) !important;
437
+ }
438
+ }
439
+
440
+ /* A non-interactive row (the "Recents" shape) keeps the geometry and drops every affordance. */
441
+ .blockRowStatic {
442
+ cursor: default;
443
+ }
444
+
445
+ .blockRowLabel {
446
+ flex: 1;
447
+ min-width: 0;
448
+ overflow: hidden;
449
+ text-overflow: ellipsis;
450
+ white-space: nowrap;
451
+ }
452
+
453
+ .blockRowMeta {
454
+ flex: none;
455
+ font-size: var(--vx-text-micro);
456
+ color: var(--vx-faint);
457
+ }
458
+
459
+ /* Status dot standing in for a missing row icon — reads the per-scheme `--vx-status-*` solid. */
460
+ .blockToneDot {
461
+ flex: none;
462
+ width: 6px;
463
+ height: 6px;
464
+ border-radius: 999px;
465
+ background-color: var(--vx-status-neutral);
466
+ }
467
+
468
+ .blockToneDot[data-tone='good'] {
469
+ background-color: var(--vx-status-good);
470
+ }
471
+
472
+ .blockToneDot[data-tone='warn'] {
473
+ background-color: var(--vx-status-warn);
474
+ }
475
+
476
+ .blockToneDot[data-tone='bad'] {
477
+ background-color: var(--vx-status-bad);
478
+ }
479
+
480
+ /* "Show more" — a text affordance, not a button surface. */
481
+ .blockMore {
482
+ min-height: var(--vx-space-sidebar-block-row-height);
483
+ padding: 0 var(--vx-space-row-inset-x);
484
+ text-align: left;
485
+ font-size: var(--vx-text-sm);
486
+ color: var(--vx-faint);
487
+ }
488
+
489
+ .blockMore:hover {
490
+ color: var(--vx-ink);
491
+ }
492
+
493
+ /* Progress block — label + `n of m` over a thin track. */
494
+ .blockProgress {
495
+ display: block;
496
+ width: 100%;
497
+ padding: var(--vx-space-row-inset-y) var(--vx-space-row-inset-x);
498
+ border-radius: var(--vx-radius-ctrl);
499
+ text-align: left;
500
+ }
501
+
502
+ .blockProgress:hover {
503
+ background-color: color-mix(in srgb, var(--vx-ink) 6%, transparent);
504
+ }
505
+
506
+ .blockProgressHead {
507
+ display: flex;
508
+ align-items: center;
509
+ justify-content: space-between;
510
+ gap: var(--vx-space-nav-icon-gap);
511
+ margin-bottom: var(--vx-space-sidebar-section-label-gap);
512
+ }
513
+
514
+ /* The rail's progress mark, rendered on the settings row (never inside the block). */
515
+ .blockRing {
516
+ display: flex;
517
+ flex: none;
518
+ align-items: center;
519
+ }
520
+
521
+ /*
522
+ * Flat footer links — `settingsMenuItems` at three or fewer, rendered as rows instead of a gear
523
+ * menu (see `AppSidebar`'s JSDoc for the threshold). Each row IS a `.footerBtn`, so the two
524
+ * renderings are the same geometry.
525
+ */
526
+ .footerLinks {
527
+ display: flex;
528
+ flex-direction: column;
529
+ }
530
+
531
+ /* Fixed 18px icon column for a flat footer row — an icon-less row keeps the label aligned with
532
+ * its neighbours instead of sliding left. */
533
+ .footerIconSlot {
534
+ display: flex;
535
+ flex: none;
536
+ align-items: center;
537
+ justify-content: center;
538
+ width: 18px;
539
+ color: var(--vx-faint);
540
+ }
541
+
542
+ .footerBtn:hover .footerIconSlot {
543
+ color: var(--vx-ink);
544
+ }
545
+
546
+ .footerVersion {
547
+ padding: var(--vx-space-row-inset-y) var(--vx-space-row-inset-x);
548
+ font-family: var(--basalt-font-mono);
549
+ font-size: var(--vx-text-micro);
550
+ color: var(--vx-faint);
551
+ }
552
+
296
553
  /*
297
554
  * Inline child items — indent under the parent with the ONE allowed sidebar line: a 1px
298
555
  * `--vx-divider` left guide on the WRAPPER (not per row, so hover tints don't break the line).
@@ -350,9 +607,9 @@
350
607
  }
351
608
 
352
609
  /*
353
- * Icon-rail (collapsed) — desktop only. Gated behind min-width: sm (48em) so the mobile drawer keeps
354
- * full labels even when the persisted collapse flag is on. Hide the brand text, section labels and
355
- * nav labels; center the remaining icons.
610
+ * Icon-rail (collapsed) — desktop only. Gated behind min-width: sm (48em) so the sidebar doesn't
611
+ * exist at all below that breakpoint (see `MobileNav`) regardless of the persisted collapse flag.
612
+ * Hide the brand text, section labels and nav labels; center the remaining icons.
356
613
  */
357
614
  @media (min-width: 48em) {
358
615
  .root[data-collapsed] .brand {
@@ -421,14 +678,40 @@
421
678
  display: none;
422
679
  }
423
680
 
424
- /* `navExtra` has no sensible icon-rail representation (arbitrary consumer content, not a nav
425
- * row) — hide it in the rail, same treatment as `.childList`. It stays mounted (never a JS
426
- * conditional on `collapsed`): `collapsed` is one value shared by the desktop rail AND the
427
- * mobile drawer (`AppShell.Navbar`'s `collapsed: { mobile: !mobileOpened }` only ever un-collapses
428
- * the mobile side), and the rail-vs-drawer split lives ENTIRELY in this media query, never in JS —
429
- * a JS gate on `collapsed` would also drop `navExtra` from the mobile drawer, which opens at full
430
- * width regardless of the persisted rail state. */
431
- .root[data-collapsed] .navExtra {
681
+ /*
682
+ * Block rail projection (`sidebarBlockRail`). `hidden` and `ring` blocks leave the rail entirely
683
+ * — a `custom` block is arbitrary consumer content with no icon-column representation (the
684
+ * treatment `navExtra` had), and a progress block's rail form is the ring on the settings row
685
+ * below, not a squashed track. A `dot` block keeps its icon and nothing else.
686
+ *
687
+ * All of it is CSS, never a JS conditional on `collapsed`: the navbar is permanently
688
+ * `collapsed: { mobile: true }` (there is no mobile drawer — see `MobileNav`), so `collapsed`
689
+ * means only the desktop expanded-vs-rail state, and this media query is what applies it.
690
+ */
691
+ .root[data-collapsed] .block[data-rail='hidden'],
692
+ .root[data-collapsed] .block[data-rail='ring'] {
693
+ display: none;
694
+ }
695
+
696
+ .root[data-collapsed] .block[data-rail='dot'] .blockHeader {
697
+ justify-content: center;
698
+ padding: 0;
699
+ margin-bottom: 0;
700
+ }
701
+
702
+ .root[data-collapsed] .block[data-rail='dot'] .blockHeader > *:not(.blockIcon) {
703
+ display: none;
704
+ }
705
+
706
+ .root[data-collapsed] .block[data-rail='dot'] .blockItems {
707
+ display: none;
708
+ }
709
+
710
+ .root[data-collapsed] .blockDot {
711
+ display: block;
712
+ }
713
+
714
+ .root[data-collapsed] .footerVersion {
432
715
  display: none;
433
716
  }
434
717
  }
@@ -10,6 +10,7 @@ declare const classes: {
10
10
  readonly brand: string
11
11
  readonly brandLead: string
12
12
  readonly brandName: string
13
+ readonly brandButton: string
13
14
  readonly searchSlot: string
14
15
  readonly navScroll: string
15
16
  readonly navViewport: string
@@ -27,7 +28,24 @@ declare const classes: {
27
28
  readonly sectionHeader: string
28
29
  readonly subnavDropdown: string
29
30
  readonly childList: string
30
- readonly navExtra: string
31
31
  readonly ghostIcon: string
32
+ readonly block: string
33
+ readonly blockHeader: string
34
+ readonly blockHeaderButton: string
35
+ readonly blockIcon: string
36
+ readonly blockDot: string
37
+ readonly blockItems: string
38
+ readonly blockRow: string
39
+ readonly blockRowStatic: string
40
+ readonly blockRowLabel: string
41
+ readonly blockRowMeta: string
42
+ readonly blockToneDot: string
43
+ readonly blockMore: string
44
+ readonly blockProgress: string
45
+ readonly blockProgressHead: string
46
+ readonly blockRing: string
47
+ readonly footerLinks: string
48
+ readonly footerIconSlot: string
49
+ readonly footerVersion: string
32
50
  }
33
51
  export default classes
@@ -1,22 +1,23 @@
1
1
  import type { MouseEvent, ReactNode } from 'react';
2
- import type { BasaltAccountProps } from './account-types.js';
3
- import type { SidebarSearchConfig } from './sidebar-search.js';
4
- import type { MobileNavConfig, SidebarSection } from '../nav/types.js';
2
+ import type { GlobalAction } from '../controls/actions.js';
3
+ import type { AccountMenuItem, BasaltAccountProps } from './account-types.js';
4
+ import type { SidebarSearchActions, SidebarSearchConfig } from './sidebar-search.js';
5
+ import type { MobileNavConfig, SidebarBlock, SidebarSection } from '../nav/types.js';
5
6
  export { AppSidebar, type AppSidebarProps } from './app-sidebar.js';
6
7
  export { NavCountBadge } from './nav-count-badge.js';
7
- export { SidebarSearch, type SidebarSearchConfig } from './sidebar-search.js';
8
+ export { SidebarSearch, type SidebarSearchConfig, type SidebarSearchActions, } from './sidebar-search.js';
8
9
  export { SidebarAccount } from './app-sidebar-account.js';
9
10
  export type { AccountBadgeTone, BasaltIdentity, BasaltRole, BasaltPlan, AccountMenuItem, BasaltAccountState, BasaltAccountActions, BasaltAccountProps, } from './account-types.js';
10
11
  export { MobileNav, type MobileNavProps } from './app-mobile-nav.js';
11
12
  export { projectMobileNav, type ProjectMobileNavOptions, MOBILE_MAX_TABS_DEFAULT, MOBILE_MENU_MAX_DEFAULT, MOBILE_MORE_KEY, } from './mobile-nav-model.js';
12
13
  export { AppBreadcrumbs } from './app-breadcrumbs.js';
13
- export { PageHeaderProvider, PageActions, PageActionsOutlet } from './page-header.js';
14
+ export { PageBar, type PageBarProps } from './page-bar.js';
14
15
  /**
15
16
  * The shared nav vocabulary lives in `src/nav/types.ts` so the headless router bridge can `import
16
17
  * type` it without reaching into the Mantine layer. Re-exported here so `SidebarItem` /
17
18
  * `SidebarSection` keep resolving straight from `basalt-ui`, exactly as before.
18
19
  */
19
- export type { NavAnchor, NavAnchorProps, NavMobilePlacement, NavSectionMobile, SidebarItem, SidebarSection, MobileNavConfig, MobileNavGroup, MobileNavModel, MobileNavSlot, MobileNavSurface, } from '../nav/types.js';
20
+ export type { NavAnchor, NavAnchorProps, NavMobilePlacement, NavSectionMobile, SidebarItem, SidebarSection, SidebarBlock, SidebarBlockItem, SidebarBlockTone, SidebarListBlock, SidebarProgressBlock, SidebarCustomBlock, MobileNavConfig, MobileNavGroup, MobileNavModel, MobileNavSlot, MobileNavSurface, } from '../nav/types.js';
20
21
  /** Brand identity shown in the sidebar header (logo + name). */
21
22
  export type BrandConfig = {
22
23
  /** Display name (e.g. "Argo"). */
@@ -36,8 +37,13 @@ export type SettingsMenuItem = {
36
37
  onClick?: (e: MouseEvent) => void;
37
38
  };
38
39
  export type BasaltShellProps = {
39
- /** Brand identity for the sidebar header. */
40
- brand: BrandConfig;
40
+ /**
41
+ * Brand identity for the sidebar header. Supplying `menu` turns the brand row into a `Name ▾`
42
+ * workspace switcher — the rows are `AccountMenuItem`s, the shape the account menu already uses.
43
+ */
44
+ brand: BrandConfig & {
45
+ menu?: AccountMenuItem[];
46
+ };
41
47
  /** Grouped nav sections — the ONE definition, rendered as the sidebar and projected to the bar. */
42
48
  sections: SidebarSection[];
43
49
  /**
@@ -45,28 +51,56 @@ export type BasaltShellProps = {
45
51
  * configured still produces a working bar (see `projectMobileNav`'s zero-config fallback).
46
52
  */
47
53
  mobileNav?: MobileNavConfig;
48
- /** Persistent, shell-owned top-bar slot (timer, refresh, notifications). */
49
- globalActions?: ReactNode;
50
54
  /**
51
- * DESKTOP ONLY. Arbitrary content appended after `sections` inside the sidebar's nav scroll
52
- * region (a tree, a filter panel, a project list, …), for anything a set of `SidebarItem`s can't
53
- * express. Hidden on the collapsed desktop rail; the sidebar itself no longer exists below the
54
- * `sm` breakpoint, so this slot has no mobile representation — put mobile-reachable extras in
55
- * `mobileNav.moreExtra` instead.
55
+ * Persistent, shell-owned header actions (timer, notifications, a global `SyncButton`) — DECLARED
56
+ * DATA, not a `ReactNode` slot, so basalt owns the mobile projection: the first two ride the bar,
57
+ * the rest fold into the header's single kebab (`mobile: 'bar' | 'more' | 'hidden'`, see
58
+ * `GlobalAction`). That kebab is the SAME one `PageBar`'s ROW 1 opens — a route whose bar has
59
+ * row-1 actions (or `filtersEnd`) lends it, any other route gets it from the shell. An
60
+ * `ActionGroup` a consumer mounts in some other home never inherits these rows.
61
+ */
62
+ globalActions?: GlobalAction[];
63
+ /**
64
+ * Sidebar blocks — DECLARED DATA (law C13, `docs/CONTROLS-SPEC.md` §2.3), which replaced both
65
+ * `sidebarNavExtra` and `mobileNav.moreExtra`. `placement: 'nav'` (the default for `list` and
66
+ * `custom`) appends after the nav sections inside the scroll region; `'bottom'` pins above the
67
+ * settings footer.
68
+ *
69
+ * Because they are data, basalt owns the projections the two `ReactNode` slots could not express:
70
+ * a `list` with a `count` becomes a dot on its icon in the collapsed rail, a `progress` block
71
+ * becomes a ring on the settings row, and a block with `mobile: 'more'` becomes one More-sheet
72
+ * row opening a nested sheet of its items. A `kind: 'custom'` block is desktop-only, exactly as
73
+ * `sidebarNavExtra` was.
74
+ */
75
+ sidebarBlocks?: SidebarBlock[];
76
+ /**
77
+ * Entries appended to the sidebar settings footer — flat link rows at three or fewer, one gear
78
+ * menu at four or more (`AppSidebarProps.settingsMenuItems` documents the threshold).
56
79
  */
57
- sidebarNavExtra?: ReactNode;
58
- /** Entries appended to the sidebar settings menu. */
59
80
  settingsMenuItems?: SettingsMenuItem[];
60
81
  /**
61
- * Optional account row rendered above the settings menu in the sidebar footer (see
82
+ * Forces the footer form instead of taking the count rule above: `'flat'` always renders link
83
+ * rows, `'menu'` always renders the gear dropdown. `'auto'` (the default) is today's ≤3 → flat
84
+ * behaviour. Reach for it when the rows are CONTROLS rather than destinations — three of those
85
+ * (a theme radio group, a devtools switch) read as a widget pile in the footer even though the
86
+ * count says flat. Full rationale on `AppSidebarProps.settingsMenu`.
87
+ *
88
+ * @default 'auto'
89
+ */
90
+ settingsMenu?: 'auto' | 'flat' | 'menu';
91
+ /**
92
+ * Optional account row rendered below the settings menu in the sidebar footer (see
62
93
  * `SidebarAccount` / `BasaltAccountProps`). Omitting it reproduces today's footer unchanged.
63
94
  */
64
95
  account?: BasaltAccountProps;
65
96
  /**
66
97
  * Optional search field below the brand in the sidebar (fixed, above the nav scroll). Supply
67
- * `onOpen`, e.g. `() => openSpotlight()` from basalt-ui/commands.
98
+ * `onOpen`, e.g. `() => openSpotlight()` from basalt-ui/commands. `actions` adds one or two
99
+ * icon-only buttons to the right of that row (a tuple by type — see `SidebarSearchActions`).
68
100
  */
69
- search?: SidebarSearchConfig;
101
+ search?: SidebarSearchConfig & {
102
+ actions?: SidebarSearchActions;
103
+ };
70
104
  /**
71
105
  * localStorage key for the persisted sidebar-collapsed flag. Ignored when `collapsed` is set.
72
106
  *
@@ -92,5 +126,5 @@ export type BasaltShellProps = {
92
126
  /** Page content. */
93
127
  children?: ReactNode;
94
128
  };
95
- export declare function BasaltShell({ brand, sections, mobileNav, globalActions, sidebarNavExtra, settingsMenuItems, storageKey, collapsed: collapsedProp, onCollapsedChange, account, search, children, }: BasaltShellProps): import("react").JSX.Element;
129
+ export declare function BasaltShell({ brand, sections, mobileNav, globalActions, sidebarBlocks, settingsMenuItems, settingsMenu, storageKey, collapsed: collapsedProp, onCollapsedChange, account, search, children, }: BasaltShellProps): import("react").JSX.Element;
96
130
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shell/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shell/index.tsx"],"names":[],"mappings":"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"}