basalt-ui 1.25.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 (462) hide show
  1. package/AGENTS.md +8 -6
  2. package/CHANGELOG.md +18 -0
  3. package/MIGRATING.md +546 -20
  4. package/README.md +45 -32
  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/settings-section.d.ts +6 -4
  134. package/dist/dashboard/settings-section.d.ts.map +1 -1
  135. package/dist/dashboard/settings-section.js +23 -16
  136. package/dist/dashboard/settings-section.module.css +10 -0
  137. package/dist/dashboard/settings-section.module.css.d.ts +1 -0
  138. package/dist/dashboard/settings-section.test.d.ts +2 -0
  139. package/dist/dashboard/settings-section.test.d.ts.map +1 -0
  140. package/dist/dashboard/stat-card.d.ts +44 -9
  141. package/dist/dashboard/stat-card.d.ts.map +1 -1
  142. package/dist/dashboard/stat-card.js +80 -48
  143. package/dist/dashboard/stat-card.module.css +82 -0
  144. package/dist/dashboard/stat-card.module.css.d.ts +11 -0
  145. package/dist/data/data-table.d.ts +37 -15
  146. package/dist/data/data-table.d.ts.map +1 -1
  147. package/dist/data/data-table.js +88 -58
  148. package/dist/guard/grace.test.d.ts +20 -0
  149. package/dist/guard/grace.test.d.ts.map +1 -0
  150. package/dist/guard/index.d.ts +66 -3
  151. package/dist/guard/index.d.ts.map +1 -1
  152. package/dist/guard/index.js +82 -7
  153. package/dist/guard/types.d.ts +3 -3
  154. package/dist/guard/types.d.ts.map +1 -1
  155. package/dist/index.d.ts +12 -2
  156. package/dist/index.d.ts.map +1 -1
  157. package/dist/index.js +13 -10
  158. package/dist/nav/types.d.ts +78 -2
  159. package/dist/nav/types.d.ts.map +1 -1
  160. package/dist/notifications/bell.d.ts +4 -1
  161. package/dist/notifications/bell.d.ts.map +1 -1
  162. package/dist/notifications/index.d.ts +1 -1
  163. package/dist/router-tanstack/field.d.ts +14 -0
  164. package/dist/router-tanstack/field.d.ts.map +1 -0
  165. package/dist/router-tanstack/field.js +4 -0
  166. package/dist/router-tanstack/index.d.ts +6 -2
  167. package/dist/router-tanstack/index.d.ts.map +1 -1
  168. package/dist/router-tanstack/index.js +4 -0
  169. package/dist/router-tanstack/multi-search-param-store.d.ts +4 -52
  170. package/dist/router-tanstack/multi-search-param-store.d.ts.map +1 -1
  171. package/dist/router-tanstack/multi-search-param-store.js +19 -27
  172. package/dist/router-tanstack/search-param-store.d.ts +3 -87
  173. package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
  174. package/dist/router-tanstack/search-param-store.js +16 -45
  175. package/dist/router-tanstack/search-store.d.ts +81 -0
  176. package/dist/router-tanstack/search-store.d.ts.map +1 -0
  177. package/dist/router-tanstack/search-store.js +187 -0
  178. package/dist/router-tanstack/search-store.router.test.d.ts +2 -0
  179. package/dist/router-tanstack/search-store.router.test.d.ts.map +1 -0
  180. package/dist/router-tanstack/search-store.test.d.ts +2 -0
  181. package/dist/router-tanstack/search-store.test.d.ts.map +1 -0
  182. package/dist/router-tanstack/use-nav.d.ts +0 -2
  183. package/dist/router-tanstack/use-nav.d.ts.map +1 -1
  184. package/dist/router-tanstack/use-nav.js +34 -12
  185. package/dist/router-tanstack/use-nav.test.d.ts +2 -0
  186. package/dist/router-tanstack/use-nav.test.d.ts.map +1 -0
  187. package/dist/section/index.d.ts +2 -0
  188. package/dist/section/index.d.ts.map +1 -0
  189. package/dist/section/index.js +4 -0
  190. package/dist/section/section.d.ts +24 -0
  191. package/dist/section/section.d.ts.map +1 -0
  192. package/dist/section/section.js +126 -0
  193. package/dist/section/section.module.css +104 -0
  194. package/dist/section/section.module.css.d.ts +11 -0
  195. package/dist/section/section.test.d.ts +2 -0
  196. package/dist/section/section.test.d.ts.map +1 -0
  197. package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
  198. package/dist/shell/app-breadcrumbs.js +18 -15
  199. package/dist/shell/app-header.module.css +77 -37
  200. package/dist/shell/app-header.module.css.d.ts +2 -1
  201. package/dist/shell/app-mobile-nav.d.ts +9 -2
  202. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  203. package/dist/shell/app-mobile-nav.js +98 -11
  204. package/dist/shell/app-mobile-nav.module.css +8 -0
  205. package/dist/shell/app-mobile-nav.module.css.d.ts +1 -0
  206. package/dist/shell/app-sidebar.d.ts +55 -28
  207. package/dist/shell/app-sidebar.d.ts.map +1 -1
  208. package/dist/shell/app-sidebar.js +116 -67
  209. package/dist/shell/app-sidebar.module.css +303 -20
  210. package/dist/shell/app-sidebar.module.css.d.ts +19 -1
  211. package/dist/shell/index.d.ts +55 -21
  212. package/dist/shell/index.d.ts.map +1 -1
  213. package/dist/shell/index.js +40 -18
  214. package/dist/shell/mobile-nav-model.d.ts +13 -2
  215. package/dist/shell/mobile-nav-model.d.ts.map +1 -1
  216. package/dist/shell/mobile-nav-model.js +15 -1
  217. package/dist/shell/nav-count-badge.d.ts +1 -1
  218. package/dist/shell/page-bar.d.ts +51 -0
  219. package/dist/shell/page-bar.d.ts.map +1 -0
  220. package/dist/shell/page-bar.js +154 -0
  221. package/dist/shell/page-bar.module.css +178 -0
  222. package/dist/shell/page-bar.module.css.d.ts +17 -0
  223. package/dist/shell/page-bar.test.d.ts +2 -0
  224. package/dist/shell/page-bar.test.d.ts.map +1 -0
  225. package/dist/shell/sidebar-block-model.d.ts +48 -0
  226. package/dist/shell/sidebar-block-model.d.ts.map +1 -0
  227. package/dist/shell/sidebar-block-model.js +39 -0
  228. package/dist/shell/sidebar-blocks.d.ts +32 -0
  229. package/dist/shell/sidebar-blocks.d.ts.map +1 -0
  230. package/dist/shell/sidebar-blocks.js +169 -0
  231. package/dist/shell/sidebar-search.d.ts +13 -1
  232. package/dist/shell/sidebar-search.d.ts.map +1 -1
  233. package/dist/shell/sidebar-search.js +34 -1
  234. package/dist/shell/sidebar-search.module.css +26 -0
  235. package/dist/shell/sidebar-search.module.css.d.ts +2 -0
  236. package/dist/state/fields.d.ts +392 -0
  237. package/dist/state/fields.d.ts.map +1 -0
  238. package/dist/state/fields.js +369 -0
  239. package/dist/state/fields.test.d.ts +2 -0
  240. package/dist/state/fields.test.d.ts.map +1 -0
  241. package/dist/state/persisted.d.ts +48 -0
  242. package/dist/state/persisted.d.ts.map +1 -0
  243. package/dist/state/persisted.js +105 -0
  244. package/dist/state.d.ts +11 -56
  245. package/dist/state.d.ts.map +1 -1
  246. package/dist/state.js +14 -120
  247. package/dist/styles.css +13 -0
  248. package/dist/surfaces.d.ts +106 -24
  249. package/dist/surfaces.d.ts.map +1 -1
  250. package/dist/surfaces.js +155 -19
  251. package/dist/theme/controls.module.css +16 -1
  252. package/dist/theme/ctl-theme.d.ts +32 -0
  253. package/dist/theme/ctl-theme.d.ts.map +1 -0
  254. package/dist/theme/ctl-theme.js +54 -0
  255. package/dist/theme/ctl-theme.test.d.ts +2 -0
  256. package/dist/theme/ctl-theme.test.d.ts.map +1 -0
  257. package/dist/theme/ctl-tier-coverage.test.d.ts +2 -0
  258. package/dist/theme/ctl-tier-coverage.test.d.ts.map +1 -0
  259. package/dist/theme/divider-law.test.d.ts +2 -0
  260. package/dist/theme/divider-law.test.d.ts.map +1 -0
  261. package/dist/theme/icon-slot.d.ts +44 -0
  262. package/dist/theme/icon-slot.d.ts.map +1 -0
  263. package/dist/theme/icon-slot.js +21 -0
  264. package/dist/theme/icon-slot.module.css +47 -0
  265. package/dist/theme/icon-slot.module.css.d.ts +8 -0
  266. package/dist/theme/icon-slot.test.d.ts +2 -0
  267. package/dist/theme/icon-slot.test.d.ts.map +1 -0
  268. package/dist/theme/index.d.ts +1 -0
  269. package/dist/theme/index.d.ts.map +1 -1
  270. package/dist/theme/index.js +82 -5
  271. package/dist/theme/layout-rhythm-css.test.d.ts +2 -0
  272. package/dist/theme/layout-rhythm-css.test.d.ts.map +1 -0
  273. package/dist/theme/segmented-control.module.css +20 -0
  274. package/dist/theme/shadow-surfaces.test.d.ts.map +1 -1
  275. package/dist/tokens/index.d.ts +12 -0
  276. package/dist/tokens/index.d.ts.map +1 -1
  277. package/dist/tokens/index.js +38 -8
  278. package/dist/tokens/palette.d.ts +97 -61
  279. package/dist/tokens/palette.d.ts.map +1 -1
  280. package/dist/tokens/palette.js +76 -38
  281. package/dist/tokens.css +10 -2
  282. package/dist/widget-header/delta-badge.d.ts +13 -0
  283. package/dist/widget-header/delta-badge.d.ts.map +1 -0
  284. package/dist/widget-header/delta-badge.js +29 -0
  285. package/dist/widget-header/delta-badge.test.d.ts +2 -0
  286. package/dist/widget-header/delta-badge.test.d.ts.map +1 -0
  287. package/dist/widget-header/index.d.ts +3 -0
  288. package/dist/widget-header/index.d.ts.map +1 -0
  289. package/dist/widget-header/index.js +6 -0
  290. package/dist/widget-header/no-mantine.test.d.ts +2 -0
  291. package/dist/widget-header/no-mantine.test.d.ts.map +1 -0
  292. package/dist/widget-header/widget-header.d.ts +37 -0
  293. package/dist/widget-header/widget-header.d.ts.map +1 -0
  294. package/dist/widget-header/widget-header.js +77 -0
  295. package/dist/widget-header/widget-header.module.css +335 -0
  296. package/dist/widget-header/widget-header.module.css.d.ts +24 -0
  297. package/dist/widget-header/widget-header.test.d.ts +2 -0
  298. package/dist/widget-header/widget-header.test.d.ts.map +1 -0
  299. package/llms.txt +30 -17
  300. package/package.json +14 -1
  301. package/src/charts/cursor/resolve.test.ts +29 -1
  302. package/src/charts/cursor/resolve.ts +38 -6
  303. package/src/charts/cursor/scope.tsx +6 -3
  304. package/src/charts/cursor/store.test.ts +19 -1
  305. package/src/charts/cursor/store.ts +10 -5
  306. package/src/charts/hooks/useChartCursor.ts +16 -8
  307. package/src/charts/index.ts +2 -2
  308. package/src/charts/primitives/CartesianChart.test.tsx +124 -0
  309. package/src/charts/primitives/ChartCard.test.tsx +66 -0
  310. package/src/charts/primitives/ChartCard.tsx +86 -146
  311. package/src/charts/sparklines/BarSparkline.tsx +43 -5
  312. package/src/cli/check-coverage.test.ts +297 -0
  313. package/src/cli/check-theme.test.ts +38 -14
  314. package/src/cli/index.ts +392 -16
  315. package/src/cli/init-sync.test.ts +97 -0
  316. package/src/connectivity/connectivity-indicator.tsx +16 -9
  317. package/src/content/index.ts +16 -3
  318. package/src/content/prose.module.css +7 -28
  319. package/src/content/prose.module.css.test.ts +8 -24
  320. package/src/controls/actions.module.css +62 -0
  321. package/src/controls/actions.module.css.d.ts +13 -0
  322. package/src/controls/actions.test.tsx +252 -0
  323. package/src/controls/actions.tsx +655 -0
  324. package/src/controls/actions.type-guard.test.ts +63 -0
  325. package/src/controls/compare-filter.tsx +64 -0
  326. package/src/controls/control-group.test.tsx +229 -0
  327. package/src/controls/control-group.tsx +63 -0
  328. package/src/controls/controls.module.css +542 -0
  329. package/src/controls/controls.module.css.d.ts +35 -0
  330. package/src/controls/controls.router.test.tsx +637 -0
  331. package/src/controls/enum-filter.tsx +159 -0
  332. package/src/controls/filter-context.tsx +153 -0
  333. package/src/controls/filter-pill.tsx +120 -0
  334. package/src/controls/filter-set.test.tsx +472 -0
  335. package/src/controls/filter-set.tsx +225 -0
  336. package/src/controls/filter-sheet.tsx +285 -0
  337. package/src/controls/glyphs.tsx +81 -0
  338. package/src/controls/index.ts +48 -0
  339. package/src/controls/multi-select-filter.tsx +140 -0
  340. package/src/controls/peer-boundary.test.ts +85 -0
  341. package/src/controls/props-contract.test.tsx +106 -0
  342. package/src/controls/range-filter.tsx +184 -0
  343. package/src/controls/search-filter.tsx +123 -0
  344. package/src/controls/select-filter.tsx +89 -0
  345. package/src/controls/sync-button.module.css +63 -0
  346. package/src/controls/sync-button.module.css.d.ts +11 -0
  347. package/src/controls/sync-button.test.tsx +183 -0
  348. package/src/controls/sync-button.tsx +197 -0
  349. package/src/controls/toggle-filter.tsx +60 -0
  350. package/src/controls/view-tabs.tsx +110 -0
  351. package/src/controls-dates/date-range-picker.tsx +42 -0
  352. package/src/controls-dates/index.ts +8 -0
  353. package/src/dashboard/delta-badge.tsx +4 -74
  354. package/src/dashboard/settings-section.module.css +10 -0
  355. package/src/dashboard/settings-section.module.css.d.ts +1 -0
  356. package/src/dashboard/settings-section.test.tsx +56 -0
  357. package/src/dashboard/settings-section.tsx +33 -30
  358. package/src/dashboard/stat-card.module.css +82 -0
  359. package/src/dashboard/stat-card.module.css.d.ts +11 -0
  360. package/src/dashboard/stat-card.test.tsx +67 -1
  361. package/src/dashboard/stat-card.tsx +209 -68
  362. package/src/data/data-table.test.tsx +96 -11
  363. package/src/data/data-table.tsx +220 -84
  364. package/src/guard/check-source.test.ts +126 -13
  365. package/src/guard/grace.test.ts +149 -0
  366. package/src/guard/index.ts +172 -29
  367. package/src/guard/types.ts +4 -2
  368. package/src/index.ts +46 -5
  369. package/src/nav/types.ts +84 -2
  370. package/src/notifications/bell.tsx +6 -3
  371. package/src/notifications/index.ts +1 -1
  372. package/src/router-tanstack/field.ts +31 -0
  373. package/src/router-tanstack/index.ts +24 -2
  374. package/src/router-tanstack/multi-search-param-store.test.ts +71 -3
  375. package/src/router-tanstack/multi-search-param-store.ts +46 -77
  376. package/src/router-tanstack/search-param-store.ts +42 -159
  377. package/src/router-tanstack/search-store.router.test.tsx +661 -0
  378. package/src/router-tanstack/search-store.test.ts +418 -0
  379. package/src/router-tanstack/search-store.ts +380 -0
  380. package/src/router-tanstack/use-nav.test.tsx +187 -0
  381. package/src/router-tanstack/use-nav.ts +72 -14
  382. package/src/section/index.ts +1 -0
  383. package/src/section/section.module.css +104 -0
  384. package/src/section/section.module.css.d.ts +11 -0
  385. package/src/section/section.test.tsx +204 -0
  386. package/src/section/section.tsx +220 -0
  387. package/src/shell/app-breadcrumbs.tsx +44 -31
  388. package/src/shell/app-header.module.css +77 -37
  389. package/src/shell/app-header.module.css.d.ts +2 -1
  390. package/src/shell/app-mobile-nav.module.css +8 -0
  391. package/src/shell/app-mobile-nav.module.css.d.ts +1 -0
  392. package/src/shell/app-mobile-nav.tsx +147 -8
  393. package/src/shell/app-sidebar-account.tsx +2 -2
  394. package/src/shell/app-sidebar.module.css +303 -20
  395. package/src/shell/app-sidebar.module.css.d.ts +19 -1
  396. package/src/shell/app-sidebar.test.tsx +456 -41
  397. package/src/shell/app-sidebar.tsx +276 -140
  398. package/src/shell/index.test.tsx +291 -5
  399. package/src/shell/index.tsx +137 -38
  400. package/src/shell/mobile-nav-model.test.ts +228 -7
  401. package/src/shell/mobile-nav-model.ts +53 -3
  402. package/src/shell/nav-count-badge.tsx +1 -1
  403. package/src/shell/page-bar.module.css +178 -0
  404. package/src/shell/page-bar.module.css.d.ts +17 -0
  405. package/src/shell/page-bar.test.tsx +316 -0
  406. package/src/shell/page-bar.tsx +315 -0
  407. package/src/shell/sidebar-block-model.ts +83 -0
  408. package/src/shell/sidebar-blocks.tsx +301 -0
  409. package/src/shell/sidebar-search.module.css +26 -0
  410. package/src/shell/sidebar-search.module.css.d.ts +2 -0
  411. package/src/shell/sidebar-search.tsx +71 -2
  412. package/src/state/fields.test.tsx +258 -0
  413. package/src/state/fields.ts +892 -0
  414. package/src/state/persisted.ts +211 -0
  415. package/src/state.ts +46 -240
  416. package/src/styles.css +13 -0
  417. package/src/surfaces.ts +203 -36
  418. package/src/theme/controls.module.css +16 -1
  419. package/src/theme/ctl-theme.test.tsx +275 -0
  420. package/src/theme/ctl-theme.tsx +138 -0
  421. package/src/theme/ctl-tier-coverage.test.ts +170 -0
  422. package/src/theme/density-relations.test.ts +106 -51
  423. package/src/theme/divider-law.test.ts +259 -0
  424. package/src/theme/icon-slot.module.css +47 -0
  425. package/src/theme/icon-slot.module.css.d.ts +8 -0
  426. package/src/theme/icon-slot.test.tsx +275 -0
  427. package/src/theme/icon-slot.tsx +59 -0
  428. package/src/theme/index.ts +165 -3
  429. package/src/theme/layout-rhythm-css.test.ts +72 -0
  430. package/src/theme/radius.test.ts +11 -1
  431. package/src/theme/segmented-control.module.css +20 -0
  432. package/src/theme/shadow-surfaces.test.ts +20 -7
  433. package/src/theme/spacing.test.ts +75 -27
  434. package/src/theme/use-basalt-spacing.ts +2 -2
  435. package/src/tokens/density.test.ts +7 -25
  436. package/src/tokens/index.ts +48 -8
  437. package/src/tokens/palette.ts +107 -73
  438. package/src/widget-header/delta-badge.test.tsx +60 -0
  439. package/src/widget-header/delta-badge.tsx +57 -0
  440. package/src/widget-header/index.ts +2 -0
  441. package/src/widget-header/no-mantine.test.ts +33 -0
  442. package/src/widget-header/widget-header.module.css +335 -0
  443. package/src/widget-header/widget-header.module.css.d.ts +24 -0
  444. package/src/widget-header/widget-header.test.tsx +187 -0
  445. package/src/widget-header/widget-header.tsx +168 -0
  446. package/agent/rules/basalt-agent.md +0 -452
  447. package/agent/rules/basalt-app.md +0 -246
  448. package/agent/rules/basalt-commands.md +0 -320
  449. package/agent/rules/basalt-content.md +0 -409
  450. package/agent/rules/basalt-data.md +0 -329
  451. package/agent/rules/basalt-forms.md +0 -186
  452. package/agent/rules/basalt-notifications.md +0 -264
  453. package/agent/rules/basalt-query.md +0 -187
  454. package/agent/rules/basalt-router.md +0 -336
  455. package/dist/content/article-filter-bar.d.ts +0 -40
  456. package/dist/content/article-filter-bar.d.ts.map +0 -1
  457. package/dist/content/article-filter-bar.js +0 -56
  458. package/dist/shell/page-header.d.ts +0 -13
  459. package/dist/shell/page-header.d.ts.map +0 -1
  460. package/dist/shell/page-header.js +0 -23
  461. package/src/content/article-filter-bar.tsx +0 -88
  462. package/src/shell/page-header.tsx +0 -35
@@ -1,5 +1,6 @@
1
1
  import type { GuardKind, GuardSeverity } from '../guard/index.js';
2
2
  import { RULE_NAMES, SKILL_NAMES } from '../surfaces.js';
3
+ import type { RuleName } from '../surfaces.js';
3
4
  /** Shape of the optional `"basalt"` key in a consumer's package.json. */
4
5
  export type BasaltConfig = {
5
6
  /** Source roots to scan. Default: `['src']`. Set explicitly for a monorepo layout. */
@@ -324,6 +325,8 @@ type Manifest = {
324
325
  */
325
326
  spacingScale?: Record<string, number>;
326
327
  };
328
+ /** A tracked dest inside a derived namespace that this basalt version no longer ships. */
329
+ export declare function retiredManagedDests(manifest: Manifest, files: readonly ManagedFile[]): readonly string[];
327
330
  /**
328
331
  * Normalize a managed unit's rendered bytes for the ledger comparison below — makes a delta that is
329
332
  * PURELY formatting or a version bump invisible to drift detection, while a real prose/content edit
@@ -368,11 +371,57 @@ type SyncOptions = {
368
371
  * `sync` does not get to make silently.
369
372
  */
370
373
  export declare function sync(opts?: SyncOptions, invocationCwd?: string): number;
374
+ /** The block's delimiters. Both are HTML comments, so the block renders as nothing in a viewer. */
375
+ export declare const COVERAGE_BLOCK_OPEN = "<!-- basalt:coverage -->";
376
+ export declare const COVERAGE_BLOCK_CLOSE = "<!-- /basalt:coverage -->";
371
377
  /**
372
- * Assert the 8 SURFACES invariants against the live SURFACES + GUARD_RULES + plugin.json.
373
- * Returns 0 when all pass; 1 when any fail (console.error each failure).
378
+ * What actually enforces one rule file's doctrine, as the file itself should state it.
374
379
  *
375
- * Eight assertions:
380
+ * Derived from SURFACES — the union over every doctrine surface carrying that `rule`, because
381
+ * `rule` is many-to-one (`data` covers `./data`, `./data/table`, `./data/virtual`; `mantine` covers
382
+ * `.` and `./connectivity`). Naming only one surface's coverage would understate the rule.
383
+ */
384
+ export declare function coverageFor(rule: RuleName): {
385
+ guardKinds: string[];
386
+ pluginRules: string[];
387
+ advisoryLaws: string[];
388
+ };
389
+ /**
390
+ * The generated block for one rule, verbatim — HTML comments only, one claim per line so a diff
391
+ * points at the claim that changed rather than at a reflowed paragraph.
392
+ *
393
+ * `not guarded` is printed even when empty, and that is the point: a rule file whose block says
394
+ * `not guarded: —` is claiming full coverage, which is a claim someone can check. A block that
395
+ * simply omitted the section would read as "nothing to declare" whether or not anyone had looked
396
+ * (D8 — see docs/CONTROLS-SPEC.md §6 "Honest coverage").
397
+ */
398
+ export declare function coverageBlock(rule: RuleName): string;
399
+ /** The block a rule file currently carries, or null when it has none yet. */
400
+ export declare function readCoverageBlock(text: string): string | null;
401
+ /**
402
+ * `text` with `block` in it — replacing the existing block, or inserted directly below the YAML
403
+ * frontmatter (the rule files' `paths:` header is load-bearing and must stay first).
404
+ */
405
+ export declare function applyCoverageBlock(text: string, block: string): string;
406
+ /**
407
+ * Reconcile every rule file's coverage block against SURFACES.
408
+ *
409
+ * `'write'` refreshes them; `'check'` is the CI gate. A rule file with NO block yet is REPORTED,
410
+ * never failed — the blocks are inserted by the agent-layer wave that rewrites those files, and a
411
+ * gate that failed before they exist would have to be landed disabled, which is how a gate stays
412
+ * disabled. A block that exists and DISAGREES is a hard failure: that is drift, and drift in a
413
+ * coverage claim is the defect the block was added for.
414
+ */
415
+ export declare function reconcileCoverageBlocks(pkgRoot: string, mode: 'write' | 'check'): {
416
+ failures: string[];
417
+ notes: string[];
418
+ };
419
+ /**
420
+ * Assert the 11 invariants against the live SURFACES + GUARD_RULES + the shipped agent layer, and
421
+ * (with `--write` / `--check`) reconcile the generated `<!-- basalt:coverage -->` header of every
422
+ * rule file against SURFACES. Returns 0 when all pass; 1 when any fail (console.error each failure).
423
+ *
424
+ * Eleven assertions:
376
425
  * 1. Every doctrine spec's guardKinds ⊆ keyof GUARD_RULES.
377
426
  * 2. Every doctrine rule (deduped) maps to agent/rules/basalt-{rule}.md on disk.
378
427
  * 3. Every doctrine skill (deduped) maps to agent/skills/{skill}/SKILL.md on disk.
@@ -386,12 +435,26 @@ export declare function sync(opts?: SyncOptions, invocationCwd?: string): number
386
435
  * the assertion's own comment below for why); basalt's own CI does
387
436
  * (`tests/surfaces-coverage.test.ts`).
388
437
  * 8. Every doctrine optionalPeers entry exists in peerDependencies AND peerDependenciesMeta.
438
+ * 9. Every registered oxlint plugin rule (PLUGIN_RULE_ID_LIST) maps to EXACTLY ONE surface's
439
+ * `pluginRules` — so a new rule cannot ship homeless, and one rule cannot be counted twice as
440
+ * coverage. Like assertion 7 this reads SURFACES only: whether the plugin really registers
441
+ * those ids is asserted against the plugin itself in `configs/oxlint-plugin.test.ts`, which a
442
+ * shipped CLI running from a consumer's node_modules cannot do.
443
+ * 10. Every shipped agent-layer file is inside its line budget, and the six rules are inside the
444
+ * whole-layer total (`AGENT_LINE_BUDGETS` / `AGENT_RULE_TOTAL_BUDGET`).
445
+ * 11. Every `GUARD_RULES` kind appears on at least one doctrine surface — assertion 9's twin for
446
+ * the text lane. Without it a kind could ship, be documented as a law in the spec, and be
447
+ * omitted from every generated coverage header, which is how two wave-6 kinds left
448
+ * `agent/rules/basalt-controls.md` printing "guard kinds — none" (D8). Unlike 9 this is
449
+ * one-directional: a kind may legitimately appear on SEVERAL surfaces (`raw-hex` is on
450
+ * `./charts` and `./tokens`), because a text scan is not partitioned by subpath the way a
451
+ * plugin rule id is.
389
452
  *
390
453
  * Tooling surfaces are exempt from assertions 1–3 by the discriminant.
391
454
  * Synthetic #-keys participate in assertions 1 and 2 but feed assertion 3 only
392
455
  * via the deduped skill union (no independent per-#-surface skill row).
393
456
  */
394
- export declare function checkCoverage(): number;
457
+ export declare function checkCoverage(flags?: readonly string[]): number;
395
458
  type DoctorResult = {
396
459
  /** Number of hard failures (exit non-zero). */
397
460
  hardFailures: number;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
package/dist/cli/index.js CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  mkdirSync,
6
6
  readdirSync,
7
7
  readFileSync,
8
+ rmdirSync,
8
9
  statSync,
9
10
  unlinkSync,
10
11
  writeFileSync
@@ -24,7 +25,13 @@ import {
24
25
  unmatchedExemptPatterns
25
26
  } from "../guard/index.js";
26
27
  import { evaluateGuardHook } from "../guard/guard-hook.js";
27
- import { RULE_NAMES, SKILL_NAMES, SURFACES, TOKEN_LAYER_BOUNDARY_SURFACES } from "../surfaces.js";
28
+ import {
29
+ PLUGIN_RULE_ID_LIST,
30
+ RULE_NAMES,
31
+ SKILL_NAMES,
32
+ SURFACES,
33
+ TOKEN_LAYER_BOUNDARY_SURFACES
34
+ } from "../surfaces.js";
28
35
  import { buildPaletteCss, deriveSpacing } from "../tokens/index.js";
29
36
  function exemptRulePaths(entry) {
30
37
  if (entry === void 0) return [];
@@ -890,6 +897,55 @@ function migrateLegacyOxfmt(cwd, pkgRoot, manifest) {
890
897
  const shipped = readSource(pkgRoot, "configs/oxfmt.json");
891
898
  if (shipped !== null && onDisk === shipped) unlinkSync(legacyAbs);
892
899
  }
900
+ const RETIREABLE_DEST_PATTERNS = [
901
+ /^\.claude\/rules\/basalt-[a-z0-9-]+\.md$/,
902
+ /^\.claude\/skills\/basalt-[a-z0-9-]+\/SKILL\.md$/
903
+ ];
904
+ function retiredManagedDests(manifest, files) {
905
+ const shipped = new Set(files.map((f) => f.dest));
906
+ return Object.keys(manifest.files).filter((dest) => !shipped.has(dest)).filter((dest) => RETIREABLE_DEST_PATTERNS.some((re) => re.test(dest))).toSorted();
907
+ }
908
+ function reportPrune(pruned) {
909
+ if (pruned.removed.length > 0) {
910
+ console.log(
911
+ `
912
+ Retired by this basalt-ui version (deleted \u2014 they no longer ship):
913
+ ` + pruned.removed.map((dest) => ` \xB7 ${dest}`).join("\n")
914
+ );
915
+ }
916
+ if (pruned.drifted.length === 0) return;
917
+ console.log(
918
+ `
919
+ Retired but locally edited, so left in place \u2014 your agent still loads them. Delete them, or run \`basalt-ui sync --force\`:
920
+ ` + pruned.drifted.map((dest) => ` \xB7 ${dest}`).join("\n")
921
+ );
922
+ }
923
+ function pruneRetiredManagedFiles(cwd, manifest, files, opts = {}) {
924
+ const result = { removed: [], drifted: [] };
925
+ for (const dest of retiredManagedDests(manifest, files)) {
926
+ const abs = resolve(cwd, dest);
927
+ const current = readIfExists(abs);
928
+ const recorded = manifest.files[dest];
929
+ const untouched = current === null || sha256(current) === recorded || sha256(normalizeForLedger(current)) === recorded;
930
+ if (!untouched && opts.force !== true) {
931
+ result.drifted.push(dest);
932
+ continue;
933
+ }
934
+ result.removed.push(dest);
935
+ if (opts.dryRun === true) continue;
936
+ delete manifest.files[dest];
937
+ if (current !== null) unlinkSync(abs);
938
+ removeEmptyParentDir(abs);
939
+ }
940
+ return result;
941
+ }
942
+ function removeEmptyParentDir(fileAbs) {
943
+ const dir = dirname(fileAbs);
944
+ try {
945
+ if (readdirSync(dir).length === 0) rmdirSync(dir);
946
+ } catch {
947
+ }
948
+ }
893
949
  const DESIGN_VERSION_LINE = /^(> Managed by basalt-ui) \(\d+\.\d+\.\d+\)\./m;
894
950
  function migrateDesignVersionLine(cwd) {
895
951
  const dest = resolve(cwd, "DESIGN.md");
@@ -1219,6 +1275,7 @@ function init(cwd = process.cwd(), scaffoldFlags = {}) {
1219
1275
  manifest.files[file.dest] = hash;
1220
1276
  writtenFiles.push(file.dest);
1221
1277
  }
1278
+ const pruned = pruneRetiredManagedFiles(cwd, manifest, files);
1222
1279
  const mergeLint = scaffoldFlags.mergeLint === true ? mergeOxlintExtends(cwd, ctx.vars.OXLINT_PRESET_PATH) : null;
1223
1280
  manifest.basaltVersion = ctx.vars.BASALT_VERSION;
1224
1281
  manifest.spacingScale = { ...deriveSpacing(0).scale };
@@ -1227,6 +1284,7 @@ function init(cwd = process.cwd(), scaffoldFlags = {}) {
1227
1284
  console.log(
1228
1285
  `basalt-ui init: ${writtenFiles.length} written, ${keptFiles.length} kept, manifest at ${MANIFEST_PATH}`
1229
1286
  );
1287
+ reportPrune(pruned);
1230
1288
  if (missingSources.length > 0) {
1231
1289
  console.log(
1232
1290
  `basalt-ui init: ${missingSources.length} shipped asset(s) not present, skipped: ${missingSources.join(", ")}`
@@ -1412,6 +1470,11 @@ function sync(opts = {}, invocationCwd = process.cwd()) {
1412
1470
  else if (tracked) recreated++;
1413
1471
  else created++;
1414
1472
  }
1473
+ const pruned = pruneRetiredManagedFiles(cwd, manifest, files, {
1474
+ dryRun: opts.check === true,
1475
+ force: opts.force === true
1476
+ });
1477
+ staleForCheck += pruned.removed.length + pruned.drifted.length;
1415
1478
  if (!placement.isPackageRepoRoot) {
1416
1479
  console.log(
1417
1480
  `basalt-ui sync: skipped lefthook.yml (this package is not the repo root \u2014 repo root detected at ${placement.repoRoot}) \u2014 lefthook only reads config at the repo root; extend node_modules/basalt-ui/configs/lefthook.yml from your root lefthook.yml instead.`
@@ -1447,6 +1510,12 @@ function sync(opts = {}, invocationCwd = process.cwd()) {
1447
1510
  console.error("basalt-ui sync --check: locally-drifted managed files:");
1448
1511
  for (const l of driftLines) console.error(l);
1449
1512
  }
1513
+ const retired = [...pruned.removed, ...pruned.drifted];
1514
+ if (retired.length > 0) {
1515
+ console.error(
1516
+ `basalt-ui sync --check: ${retired.length} retired rule/skill file(s) still present: ${retired.join(", ")} \u2014 run \`basalt-ui sync\`.`
1517
+ );
1518
+ }
1450
1519
  if (staleForCheck > 0) {
1451
1520
  console.error(`basalt-ui sync --check: ${staleForCheck} managed file(s) out of date.`);
1452
1521
  return 1;
@@ -1459,8 +1528,9 @@ function sync(opts = {}, invocationCwd = process.cwd()) {
1459
1528
  writeFileEnsuringDir(resolve(cwd, MANIFEST_PATH), `${JSON.stringify(manifest, null, 2)}
1460
1529
  `);
1461
1530
  console.log(
1462
- `basalt-ui sync: ${updated} updated, ${created} created, ${recreated} recreated, ${skippedDrift} skipped (drift).`
1531
+ `basalt-ui sync: ${updated} updated, ${created} created, ${recreated} recreated, ${skippedDrift} skipped (drift), ${pruned.removed.length} retired.`
1463
1532
  );
1533
+ reportPrune(pruned);
1464
1534
  if (driftLines.length > 0) {
1465
1535
  console.log("Locally-edited files were skipped (run with --force to overwrite):");
1466
1536
  for (const l of driftLines) console.log(l);
@@ -1472,9 +1542,98 @@ function sync(opts = {}, invocationCwd = process.cwd()) {
1472
1542
  }
1473
1543
  return 0;
1474
1544
  }
1475
- function checkCoverage() {
1545
+ const COVERAGE_BLOCK_OPEN = "<!-- basalt:coverage -->";
1546
+ const COVERAGE_BLOCK_CLOSE = "<!-- /basalt:coverage -->";
1547
+ const COVERAGE_BLOCK_RE = /<!-- basalt:coverage -->[\s\S]*?<!-- \/basalt:coverage -->/;
1548
+ function coverageFor(rule) {
1549
+ const specs = Object.values(SURFACES).filter(
1550
+ (s) => s.kind === "doctrine" && s.rule === rule
1551
+ );
1552
+ const unique = (values) => [...new Set(values)].toSorted();
1553
+ return {
1554
+ guardKinds: unique(specs.flatMap((s) => [...s.guardKinds])),
1555
+ pluginRules: unique(specs.flatMap((s) => [...s.pluginRules])),
1556
+ // NOT sorted, and not deduped across surfaces by content: an advisory law is a sentence, and
1557
+ // its order is the order the spec argues it in.
1558
+ advisoryLaws: specs.flatMap((s) => [...s.advisoryLaws ?? []])
1559
+ };
1560
+ }
1561
+ function coverageBlock(rule) {
1562
+ const { guardKinds, pluginRules, advisoryLaws } = coverageFor(rule);
1563
+ const backedBy = [
1564
+ guardKinds.length > 0 ? `guard kinds \u2014 ${guardKinds.join(", ")}` : "guard kinds \u2014 none",
1565
+ pluginRules.length > 0 ? `oxlint rules \u2014 ${pluginRules.map((id) => `basalt/${id}`).join(", ")}` : "oxlint rules \u2014 none"
1566
+ ].join(" \xB7 ");
1567
+ const lines = [
1568
+ COVERAGE_BLOCK_OPEN,
1569
+ "<!-- GENERATED from src/surfaces.ts \u2014 `basalt-ui check-coverage --write`. Do not hand-edit. -->",
1570
+ `<!-- backed by: ${backedBy} -->`
1571
+ ];
1572
+ if (advisoryLaws.length === 0) lines.push("<!-- not guarded: \u2014 -->");
1573
+ else for (const law of advisoryLaws) lines.push(`<!-- not guarded: ${law} -->`);
1574
+ lines.push(COVERAGE_BLOCK_CLOSE);
1575
+ return lines.join("\n");
1576
+ }
1577
+ function readCoverageBlock(text) {
1578
+ return COVERAGE_BLOCK_RE.exec(text)?.[0] ?? null;
1579
+ }
1580
+ function applyCoverageBlock(text, block) {
1581
+ if (COVERAGE_BLOCK_RE.test(text)) return text.replace(COVERAGE_BLOCK_RE, block);
1582
+ const frontmatter = /^---\n[\s\S]*?\n---\n/.exec(text);
1583
+ if (frontmatter === null) return `${block}
1584
+
1585
+ ${text}`;
1586
+ const end = frontmatter[0].length;
1587
+ return `${text.slice(0, end)}
1588
+ ${block}
1589
+ ${text.slice(end)}`;
1590
+ }
1591
+ function reconcileCoverageBlocks(pkgRoot, mode) {
1592
+ const failures = [];
1593
+ const notes = [];
1594
+ for (const rule of RULE_NAMES) {
1595
+ const rel = `agent/rules/basalt-${rule}.md`;
1596
+ const path = resolve(pkgRoot, rel);
1597
+ if (!existsSync(path)) continue;
1598
+ const text = readFileSync(path, "utf8");
1599
+ const current = readCoverageBlock(text);
1600
+ const expected = coverageBlock(rule);
1601
+ if (mode === "check") {
1602
+ if (current === null) notes.push(`${rel}: no <!-- basalt:coverage --> block yet`);
1603
+ else if (current !== expected) {
1604
+ failures.push(
1605
+ `${rel}: coverage block is out of sync with SURFACES (check-coverage --write)`
1606
+ );
1607
+ }
1608
+ continue;
1609
+ }
1610
+ if (current === expected) {
1611
+ notes.push(`${rel}: unchanged`);
1612
+ continue;
1613
+ }
1614
+ writeFileSync(path, applyCoverageBlock(text, expected), "utf8");
1615
+ notes.push(`${rel}: ${current === null ? "block inserted" : "block updated"}`);
1616
+ }
1617
+ return { failures, notes };
1618
+ }
1619
+ const AGENT_LINE_BUDGETS = {
1620
+ "agent/rules/basalt-tokens.md": 160,
1621
+ "agent/rules/basalt-mantine.md": 180,
1622
+ "agent/rules/basalt-charts.md": 140,
1623
+ "agent/rules/basalt-state.md": 160,
1624
+ "agent/rules/basalt-controls.md": 160,
1625
+ "agent/rules/basalt-batteries.md": 220,
1626
+ "agent/skills/basalt-app/SKILL.md": 100,
1627
+ "agent/skills/basalt-design/SKILL.md": 100,
1628
+ "agent/skills/basalt-charts/SKILL.md": 100,
1629
+ "agent/templates/CLAUDE-block.md.tpl": 40,
1630
+ "agent/templates/DESIGN.md.tpl": 45
1631
+ };
1632
+ const AGENT_RULE_TOTAL_BUDGET = 1050;
1633
+ function checkCoverage(flags = []) {
1476
1634
  const pkgRoot = packageRoot();
1477
1635
  const failures = [];
1636
+ const notes = [];
1478
1637
  const allSpecs = Object.entries(SURFACES);
1479
1638
  const doctrineSpecs = Object.values(SURFACES).filter(
1480
1639
  (s) => s.kind === "doctrine"
@@ -1573,8 +1732,59 @@ function checkCoverage() {
1573
1732
  }
1574
1733
  }
1575
1734
  }
1735
+ const surfacesPerPluginRule = /* @__PURE__ */ new Map();
1736
+ for (const [key, spec] of allSpecs) {
1737
+ if (spec.kind !== "doctrine") continue;
1738
+ for (const id of spec.pluginRules) {
1739
+ surfacesPerPluginRule.set(id, [...surfacesPerPluginRule.get(id) ?? [], key]);
1740
+ }
1741
+ }
1742
+ for (const id of PLUGIN_RULE_ID_LIST) {
1743
+ const owners = surfacesPerPluginRule.get(id) ?? [];
1744
+ if (owners.length === 1) continue;
1745
+ failures.push(
1746
+ owners.length === 0 ? `Plugin rule 'basalt/${id}' maps to no surface \u2014 add it to one surface's pluginRules` : `Plugin rule 'basalt/${id}' maps to ${owners.length} surfaces (${owners.join(", ")}) \u2014 exactly one`
1747
+ );
1748
+ }
1749
+ for (const [rel, budget] of Object.entries(AGENT_LINE_BUDGETS)) {
1750
+ const abs = resolve(pkgRoot, rel);
1751
+ if (!existsSync(abs)) continue;
1752
+ const lines = readFileSync(abs, "utf8").split("\n").length - 1;
1753
+ if (lines > budget) failures.push(`${rel}: ${lines} lines exceeds its ${budget}-line budget`);
1754
+ }
1755
+ const ruleTotal = RULE_NAMES.reduce((sum, rule) => {
1756
+ const abs = resolve(pkgRoot, `agent/rules/basalt-${rule}.md`);
1757
+ return existsSync(abs) ? sum + readFileSync(abs, "utf8").split("\n").length - 1 : sum;
1758
+ }, 0);
1759
+ if (ruleTotal > AGENT_RULE_TOTAL_BUDGET) {
1760
+ failures.push(
1761
+ `agent/rules/*.md: ${ruleTotal} lines total exceeds the ${AGENT_RULE_TOTAL_BUDGET}-line budget`
1762
+ );
1763
+ }
1764
+ const surfacedGuardKinds = /* @__PURE__ */ new Set();
1765
+ for (const spec of doctrineSpecs) {
1766
+ for (const kind of spec.guardKinds) surfacedGuardKinds.add(kind);
1767
+ }
1768
+ for (const kind of Object.keys(GUARD_RULES)) {
1769
+ if (surfacedGuardKinds.has(kind)) continue;
1770
+ failures.push(
1771
+ `Guard kind '${kind}' maps to no surface \u2014 add it to one surface's guardKinds so the generated coverage headers name it`
1772
+ );
1773
+ }
1774
+ const write = flags.includes("--write");
1775
+ const check = flags.includes("--check");
1776
+ if (write && check) {
1777
+ failures.push("--write and --check are alternatives; pass one");
1778
+ } else if (write || check) {
1779
+ const result = reconcileCoverageBlocks(pkgRoot, write ? "write" : "check");
1780
+ failures.push(...result.failures);
1781
+ notes.push(...result.notes);
1782
+ }
1783
+ for (const note of notes) {
1784
+ console.log(` ${note}`);
1785
+ }
1576
1786
  if (failures.length === 0) {
1577
- console.log("\u2713 check-coverage: all 8 assertions pass.");
1787
+ console.log("\u2713 check-coverage: all 11 assertions pass.");
1578
1788
  return 0;
1579
1789
  }
1580
1790
  console.error(`\u2716 check-coverage: ${failures.length} failure(s)`);
@@ -2425,7 +2635,7 @@ const COMMAND_FLAGS = {
2425
2635
  init: ["--with-router", "--with-query", "--merge-lint"],
2426
2636
  sync: ["--force", "--check", "--tokens-only", "--framework"],
2427
2637
  "check-theme": ["--audit-allows", "--tokens-only", "--framework"],
2428
- "check-coverage": [],
2638
+ "check-coverage": ["--write", "--check"],
2429
2639
  info: ["--json"],
2430
2640
  doctor: ["--tokens-only", "--framework"],
2431
2641
  "guard-hook": [],
@@ -2465,7 +2675,7 @@ function unknownFlag(cmd, flags) {
2465
2675
  }
2466
2676
  return null;
2467
2677
  }
2468
- const USAGE = "Usage: basalt-ui <--version | init [--with-router] [--with-query] [--merge-lint] |\n sync [--force] [--check] [--tokens-only|--framework] |\n check-theme [--audit-allows] | check-coverage | info [--json] |\n doctor [--tokens-only|--framework] | guard-hook | tokens:css | fonts:css | help>\n\ncheck-theme [--tokens-only|--framework] [--audit-allows]\n --audit-allows reports instead of scanning: every `theme-allow` annotation and every\n `basalt.exemptRules` entry, with what each one still suppresses \u2014 proved by re-running the\n guard with that one waiver neutralized, not by reading its text. Exits 1 on a waiver that\n suppresses nothing (dead), which is what makes it usable as a CI gate.\n\ntokens:css [--out <path>] [--check] [--selector-attribute <attr> | --selector-class <class>]\n [--light-class <class>] [--dark-value <v>] [--light-value <v>]\n [--default-scheme <dark|light|none>] [--media-fallback] [--only <core|all>]\n [--no-legacy-aliases]\n Emit the --vx-* stylesheet (stdout unless --out). Defaults reproduce basalt-ui/tokens.css.\n --selector-class emits `:root.dark` instead of an attribute selector (the Tailwind convention).\n --check writes nothing and exits 1 when --out differs from what would be emitted (a CI gate).\n --no-legacy-aliases drops the deprecated camelCase spellings (--vx-accentFill and friends),\n which are emitted by default as aliases of the canonical kebab-case names.\n\nfonts:css [--out <path>] [--check]\n Emit the shipped --basalt-font-* stacks as plain CSS \u2014 the typeface half of the token layer,\n otherwise reachable only by importing styles.css.\n\ndoctor [--tokens-only|--framework]\n Auto-detects a tokens-only consumer (no manifest + no @mantine/core) and checks only what\n applies. A check that cannot RUN is reported as SKIPPED and exits non-zero.\n\nsync [--force] [--check] [--tokens-only|--framework]\n Refreshes an EXISTING install; it never creates one (that is init). A tokens-only consumer\n has no scaffold to reconcile, so sync reports n/a and exits 0 there \u2014 as doctor does.\n\ncheck-theme / doctor / sync all resolve their project the same way: they honour BASALT_CWD, and\nrelocate to the single package below the cwd carrying a basalt config when invoked from a repo\nroot that has none \u2014 several carrying one is ambiguous and exits 1 in ALL THREE.\n--tokens-only and --framework are alternatives; passing both is an error.\n\nEvery subcommand accepts --help / -h to print this message and exit without running.\n--version / -v prints the resolved package version and exits 0 \u2014 the one command that proves\nWHICH basalt-ui ran, which a `bunx` cache can otherwise make a lie. An unknown command or an\nunrecognized flag exits 1 and names what it did not understand; neither is ever ignored.";
2678
+ const USAGE = "Usage: basalt-ui <--version | init [--with-router] [--with-query] [--merge-lint] |\n sync [--force] [--check] [--tokens-only|--framework] |\n check-theme [--audit-allows] | check-coverage [--write|--check] |\n info [--json] |\n doctor [--tokens-only|--framework] | guard-hook | tokens:css | fonts:css | help>\n\ncheck-theme [--tokens-only|--framework] [--audit-allows]\n --audit-allows reports instead of scanning: every `theme-allow` annotation and every\n `basalt.exemptRules` entry, with what each one still suppresses \u2014 proved by re-running the\n guard with that one waiver neutralized, not by reading its text. Exits 1 on a waiver that\n suppresses nothing (dead), which is what makes it usable as a CI gate.\n\ntokens:css [--out <path>] [--check] [--selector-attribute <attr> | --selector-class <class>]\n [--light-class <class>] [--dark-value <v>] [--light-value <v>]\n [--default-scheme <dark|light|none>] [--media-fallback] [--only <core|all>]\n [--no-legacy-aliases]\n Emit the --vx-* stylesheet (stdout unless --out). Defaults reproduce basalt-ui/tokens.css.\n --selector-class emits `:root.dark` instead of an attribute selector (the Tailwind convention).\n --check writes nothing and exits 1 when --out differs from what would be emitted (a CI gate).\n --no-legacy-aliases drops the deprecated camelCase spellings (--vx-accentFill and friends),\n which are emitted by default as aliases of the canonical kebab-case names.\n\nfonts:css [--out <path>] [--check]\n Emit the shipped --basalt-font-* stacks as plain CSS \u2014 the typeface half of the token layer,\n otherwise reachable only by importing styles.css.\n\ndoctor [--tokens-only|--framework]\n Auto-detects a tokens-only consumer (no manifest + no @mantine/core) and checks only what\n applies. A check that cannot RUN is reported as SKIPPED and exits non-zero.\n\nsync [--force] [--check] [--tokens-only|--framework]\n Refreshes an EXISTING install; it never creates one (that is init). A tokens-only consumer\n has no scaffold to reconcile, so sync reports n/a and exits 0 there \u2014 as doctor does.\n\ncheck-theme / doctor / sync all resolve their project the same way: they honour BASALT_CWD, and\nrelocate to the single package below the cwd carrying a basalt config when invoked from a repo\nroot that has none \u2014 several carrying one is ambiguous and exits 1 in ALL THREE.\n--tokens-only and --framework are alternatives; passing both is an error.\n\nEvery subcommand accepts --help / -h to print this message and exit without running.\n--version / -v prints the resolved package version and exits 0 \u2014 the one command that proves\nWHICH basalt-ui ran, which a `bunx` cache can otherwise make a lie. An unknown command or an\nunrecognized flag exits 1 and names what it did not understand; neither is ever ignored.";
2469
2679
  function run(argv, cwd = process.cwd()) {
2470
2680
  const [cmd, ...flags] = argv;
2471
2681
  if (cmd === "help" || cmd === "--help" || cmd === "-h" || flags.includes("--help") || flags.includes("-h")) {
@@ -2501,7 +2711,7 @@ function run(argv, cwd = process.cwd()) {
2501
2711
  case "check-theme":
2502
2712
  return checkTheme(cwd, flags);
2503
2713
  case "check-coverage":
2504
- return checkCoverage();
2714
+ return checkCoverage(flags);
2505
2715
  case "info":
2506
2716
  return info(flags);
2507
2717
  case "doctor":
@@ -2522,11 +2732,16 @@ function run(argv, cwd = process.cwd()) {
2522
2732
  }
2523
2733
  }
2524
2734
  export {
2735
+ COVERAGE_BLOCK_CLOSE,
2736
+ COVERAGE_BLOCK_OPEN,
2525
2737
  MANIFEST_PATH,
2526
2738
  RULE_NAMES,
2527
2739
  SKILL_NAMES,
2740
+ applyCoverageBlock,
2528
2741
  checkCoverage,
2529
2742
  checkTheme,
2743
+ coverageBlock,
2744
+ coverageFor,
2530
2745
  doctor,
2531
2746
  fontsCss,
2532
2747
  guardHook,
@@ -2535,6 +2750,9 @@ export {
2535
2750
  managedFiles,
2536
2751
  normalizeColorFunctions,
2537
2752
  normalizeForLedger,
2753
+ readCoverageBlock,
2754
+ reconcileCoverageBlocks,
2755
+ retiredManagedDests,
2538
2756
  run,
2539
2757
  sync,
2540
2758
  tokensCss
@@ -2,7 +2,10 @@
2
2
  * ConnectivityIndicator — clickable wifi icon that opens a per-signal detail popover.
3
3
  *
4
4
  * @example
5
- * <BasaltShell globalActions={<ConnectivityIndicator />} {...rest} />
5
+ * <BasaltShell
6
+ * globalActions={[{ key: 'connectivity', node: <ConnectivityIndicator />, mobile: 'bar' }]}
7
+ * {...rest}
8
+ * />
6
9
  */
7
10
  export declare function ConnectivityIndicator(): import("react").JSX.Element;
8
11
  //# sourceMappingURL=connectivity-indicator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"connectivity-indicator.d.ts","sourceRoot":"","sources":["../../src/connectivity/connectivity-indicator.tsx"],"names":[],"mappings":"AA0KA;;;;;GAKG;AACH,wBAAgB,qBAAqB,gCA8EpC"}
1
+ {"version":3,"file":"connectivity-indicator.d.ts","sourceRoot":"","sources":["../../src/connectivity/connectivity-indicator.tsx"],"names":[],"mappings":"AA6KA;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,gCA+EpC"}
@@ -149,7 +149,7 @@ function ConnectivityIndicator() {
149
149
  {
150
150
  variant: "transparent",
151
151
  color: ICON_COLOR[status],
152
- size: 28,
152
+ size: "ctl",
153
153
  onClick: toggle,
154
154
  onMouseEnter: scheduleOpen,
155
155
  onMouseLeave: scheduleClose,
@@ -7,7 +7,9 @@
7
7
  * remark-gfm + rehype-sanitize + remend, all optional peers) with its fence-renderer registry,
8
8
  * `MermaidDiagram` (beautiful-mermaid, optional peer), the MDX
9
9
  * component map, the docs-page frame (`ArticleLayout`), overview cards (`ArticleCard`/
10
- * `ArticleGrid`), and the contextual-help drawer (`GuideLink`/`GuideDrawer`). See
10
+ * `ArticleGrid`), the contextual-help drawer (`GuideLink`/`GuideDrawer`), and a re-export of the
11
+ * three `basalt-ui/controls` filters an article list needs (`FilterSet`/`ViewTabs`/
12
+ * `MultiSelectFilter`, which replaced the controlled `ArticleFilterBar`). See
11
13
  * `agent/rules/basalt-content.md` for the full doctrine + the content-collections recipe.
12
14
  */
13
15
  export { Prose } from './prose.js';
@@ -41,8 +43,8 @@ export { filterArticles, formatArticleDate, sortArticles } from './article-model
41
43
  export type { Article, ArticleFilterQuery, ArticleOrder } from './article-model.js';
42
44
  export { toArticleActions } from './article-actions.js';
43
45
  export type { ToArticleActionsOptions } from './article-actions.js';
44
- export { ArticleFilterBar } from './article-filter-bar.js';
45
- export type { ArticleFilterBarProps } from './article-filter-bar.js';
46
+ export { FilterSet, MultiSelectFilter, ViewTabs } from '../controls/index.js';
47
+ export type { FilterSetProps, MultiSelectFilterProps, ViewTabsOption, ViewTabsProps, } from '../controls/index.js';
46
48
  export { GuideDrawer, GuideLink } from './guide.js';
47
49
  export type { GuideDrawerProps, GuideLinkProps } from './guide.js';
48
50
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/content/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEvD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAElD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAE1D,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAA;AACvC,YAAY,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAE1D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAE9D,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAC9D,YAAY,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAEzC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAClD,YAAY,EACV,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,GACd,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AACxE,YAAY,EACV,0BAA0B,EAC1B,cAAc,EACd,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,YAAY,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAEpD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE1C,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAC1D,YAAY,EAAE,0BAA0B,EAAE,MAAM,OAAO,CAAA;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE/F,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACzD,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAExE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACjF,YAAY,EAAE,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEhF,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpD,YAAY,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAA;AAEhE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAEjE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAChD,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/content/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEvD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAElD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAE1D,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAA;AACvC,YAAY,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAE1D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAE9D,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAC9D,YAAY,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAEzC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAClD,YAAY,EACV,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,GACd,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AACxE,YAAY,EACV,0BAA0B,EAC1B,cAAc,EACd,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,YAAY,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAEpD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE1C,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAC1D,YAAY,EAAE,0BAA0B,EAAE,MAAM,OAAO,CAAA;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE/F,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACzD,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAExE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACjF,YAAY,EAAE,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEhF,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpD,YAAY,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAA;AAQhE,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACpE,YAAY,EACV,cAAc,EACd,sBAAsB,EACtB,cAAc,EACd,aAAa,GACd,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAChD,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA"}
@@ -14,25 +14,27 @@ import { ArticleLayout } from "./article-layout.js";
14
14
  import { ArticleCard, ArticleGrid } from "./article-card.js";
15
15
  import { filterArticles, formatArticleDate, sortArticles } from "./article-model.js";
16
16
  import { toArticleActions } from "./article-actions.js";
17
- import { ArticleFilterBar } from "./article-filter-bar.js";
17
+ import { FilterSet, MultiSelectFilter, ViewTabs } from "../controls/index.js";
18
18
  import { GuideDrawer, GuideLink } from "./guide.js";
19
19
  export {
20
20
  ArticleCard,
21
- ArticleFilterBar,
22
21
  ArticleGrid,
23
22
  ArticleLayout,
24
23
  BASALT_SANITIZE_SCHEMA,
25
24
  Callout,
26
25
  CodeBlock,
26
+ FilterSet,
27
27
  GuideDrawer,
28
28
  GuideLink,
29
29
  HeadingAnchor,
30
30
  Markdown,
31
31
  MermaidDiagram,
32
+ MultiSelectFilter,
32
33
  Prose,
33
34
  ReadingProgress,
34
35
  SlugTracker,
35
36
  TableOfContents,
37
+ ViewTabs,
36
38
  blockSplit,
37
39
  createMdxComponents,
38
40
  filterArticles,
@@ -32,37 +32,16 @@
32
32
  margin-bottom: 0;
33
33
  }
34
34
 
35
- /* Fixed-header clearance so an anchor-jumped/TOC-navigated heading isn't hidden under a sticky
36
- * header. RESPONSIVE (docs/CONTENT-SPEC.md §5): BasaltShell's AppShell header itself switches
37
- * height at the `sm` breakpoint (`shell/index.tsx`'s `header={{ height: { base, sm } }}`), so the
38
- * clearance must too — the default below is the DESKTOP value (`>= sm`, the common path); the
39
- * `max-width: 47.99375em` override below swaps in the MOBILE value. `47.99375em` is Mantine's own
40
- * complement of a `min-width: 48em` breakpoint (`em(px - 0.1)` — verified against the installed
41
- * `@mantine/core`'s `MantineClasses.mjs`, the same formula behind `visibleFrom`/`hiddenFrom`), NOT
42
- * an independently hand-picked value — a plain `47.99em` leaves a viewport band (`47.99em` to just
43
- * under `48em`) matched by NEITHER this rule nor AppShell's own `(min-width: 48em)` override.
44
- * `48em` is a STATIC assumption, not a value this CSS module can read live: it is Mantine's default
45
- * `theme.breakpoints.sm` (unmodified by `baseTheme`, `theme/index.ts`), and `shell/index.tsx` reads
46
- * that theme value directly for its own `min-width` media query — but a media query condition
47
- * cannot resolve a CSS custom property (`@media (min-width: var(--x))` is not valid CSS), so this
48
- * static module has no way to follow a consumer's `createBasaltTheme(overrides)` that overrides
49
- * `breakpoints.sm`. A consumer doing so desyncs this file's breakpoint from AppShell's actual
50
- * header-switch point — the failure mode is a heading that scrolls to a clearance tuned for the
51
- * WRONG header on the mismatched viewport band, not a total breakage. Both values also assume
52
- * BasaltShell's own AppShell header heights — a `./content` consumer rendering Prose standalone (no
53
- * BasaltShell) overrides `--vx-space-sticky-header-clearance`/`-mobile` itself. */
35
+ /* Sticky-header clearance for in-page anchors. One value on every viewport: since the
36
+ * controls waves the AppShell header is `appShellHeaderHeight` (48px) on phones too — the two-row
37
+ * mobile header and its `-mobile` clearance token are gone (docs/CONTROLS-SPEC.md §2.1, law C14).
38
+ * `--basalt-page-bar-h` (a sticky PageBar row 2, published by the shell) is added on top so a
39
+ * heading never lands under the filter row. A `./content` consumer rendering Prose standalone (no
40
+ * BasaltShell) overrides `--vx-space-sticky-header-clearance` itself. */
54
41
  .root h2,
55
42
  .root h3,
56
43
  .root h4 {
57
- scroll-margin-top: var(--vx-space-sticky-header-clearance);
58
- }
59
-
60
- @media (max-width: 47.99375em) {
61
- .root h2,
62
- .root h3,
63
- .root h4 {
64
- scroll-margin-top: var(--vx-space-sticky-header-clearance-mobile);
65
- }
44
+ scroll-margin-top: calc(var(--vx-space-sticky-header-clearance) + var(--basalt-page-bar-h, 0px));
66
45
  }
67
46
 
68
47
  /* Headings share family/weight/color across densities; size + margin differ below. */