basalt-ui 1.28.0 → 1.29.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 (455) hide show
  1. package/AGENTS.md +11 -18
  2. package/CHANGELOG.md +13 -0
  3. package/MIGRATING.md +151 -7
  4. package/README.md +23 -66
  5. package/agent/rules/basalt-batteries.md +59 -182
  6. package/agent/rules/basalt-charts.md +56 -75
  7. package/agent/rules/basalt-controls.md +38 -69
  8. package/agent/rules/basalt-mantine.md +58 -103
  9. package/agent/rules/basalt-state.md +42 -60
  10. package/agent/rules/basalt-tokens.md +46 -67
  11. package/agent/skills/basalt-app/SKILL.md +2 -0
  12. package/agent/templates/CLAUDE-block.md.tpl +14 -14
  13. package/configs/oxlint-plugin.js +70 -167
  14. package/configs/oxlint-plugin.test.ts +20 -186
  15. package/configs/oxlint.json +0 -1
  16. package/dist/agent/ai-sdk-transport.d.ts.map +1 -1
  17. package/dist/agent/ai-sdk-transport.js +7 -4
  18. package/dist/agent/coalesce.js +1 -1
  19. package/dist/agent/merge.js +1 -1
  20. package/dist/agent/transport.d.ts +13 -2
  21. package/dist/agent/transport.d.ts.map +1 -1
  22. package/dist/agent/use-agent-thread-runs.d.ts +12 -1
  23. package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
  24. package/dist/agent/use-agent-thread-runs.js +20 -6
  25. package/dist/agent-chat/index.d.ts +1 -1
  26. package/dist/agent-chat/index.d.ts.map +1 -1
  27. package/dist/agent-chat/relative-time.js +1 -1
  28. package/dist/agent-chat/thread-detail-panel.js +1 -1
  29. package/dist/agent-chat/thread-feed-row.d.ts +32 -7
  30. package/dist/agent-chat/thread-feed-row.d.ts.map +1 -1
  31. package/dist/agent-chat/thread-feed-row.js +121 -86
  32. package/dist/agent-chat/thread-feed.d.ts +11 -1
  33. package/dist/agent-chat/thread-feed.d.ts.map +1 -1
  34. package/dist/agent-chat/thread-feed.js +10 -6
  35. package/dist/agent-chat/thread-message.js +1 -1
  36. package/dist/agent-chat/virtualize.d.ts +39 -0
  37. package/dist/agent-chat/virtualize.d.ts.map +1 -1
  38. package/dist/agent-chat/virtualize.js +15 -0
  39. package/dist/charts/index.d.ts +3 -3
  40. package/dist/charts/index.d.ts.map +1 -1
  41. package/dist/charts/index.js +8 -4
  42. package/dist/charts/kinds/BandStrip.js +1 -1
  43. package/dist/charts/kinds/Bars.d.ts +1 -3
  44. package/dist/charts/kinds/Bars.d.ts.map +1 -1
  45. package/dist/charts/kinds/Bars.js +1 -1
  46. package/dist/charts/kinds/DualPanel.d.ts +3 -1
  47. package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
  48. package/dist/charts/kinds/DualPanel.js +1 -1
  49. package/dist/charts/kinds/StackedArea.js +1 -1
  50. package/dist/charts/kinds/ZonedLine.d.ts +1 -3
  51. package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
  52. package/dist/charts/primitives/Axes.d.ts +21 -2
  53. package/dist/charts/primitives/Axes.d.ts.map +1 -1
  54. package/dist/charts/primitives/Axes.js +34 -2
  55. package/dist/charts/primitives/ChartCard.d.ts +20 -4
  56. package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
  57. package/dist/charts/primitives/ChartCard.js +62 -33
  58. package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
  59. package/dist/charts/primitives/ChartFrame.js +8 -1
  60. package/dist/charts/primitives/ChartPending.d.ts +12 -6
  61. package/dist/charts/primitives/ChartPending.d.ts.map +1 -1
  62. package/dist/charts/primitives/ChartPending.js +1 -1
  63. package/dist/charts/utils/format.d.ts +5 -92
  64. package/dist/charts/utils/format.d.ts.map +1 -1
  65. package/dist/charts/utils/format.js +10 -80
  66. package/dist/cli/audit-allows.d.ts +24 -0
  67. package/dist/cli/audit-allows.d.ts.map +1 -0
  68. package/dist/cli/audit-allows.js +240 -0
  69. package/dist/cli/check-theme.d.ts +10 -0
  70. package/dist/cli/check-theme.d.ts.map +1 -0
  71. package/dist/cli/check-theme.js +139 -0
  72. package/dist/cli/doctor.d.ts +47 -0
  73. package/dist/cli/doctor.d.ts.map +1 -0
  74. package/dist/cli/doctor.js +197 -0
  75. package/dist/cli/guard-hook.d.ts +9 -0
  76. package/dist/cli/guard-hook.d.ts.map +1 -0
  77. package/dist/cli/guard-hook.js +93 -0
  78. package/dist/cli/help.d.ts +9 -0
  79. package/dist/cli/help.d.ts.map +1 -0
  80. package/dist/cli/help.js +53 -0
  81. package/dist/cli/index.d.ts +360 -270
  82. package/dist/cli/index.d.ts.map +1 -1
  83. package/dist/cli/index.js +85 -1901
  84. package/dist/cli/init.d.ts +4 -0
  85. package/dist/cli/init.d.ts.map +1 -0
  86. package/dist/cli/init.js +165 -0
  87. package/dist/cli/sync.d.ts +43 -0
  88. package/dist/cli/sync.d.ts.map +1 -0
  89. package/dist/cli/sync.js +206 -0
  90. package/dist/cli/tokens-css.d.ts +51 -0
  91. package/dist/cli/tokens-css.d.ts.map +1 -0
  92. package/dist/cli/tokens-css.js +165 -0
  93. package/dist/commands/define-commands.js +1 -1
  94. package/dist/commands/define-overlays.js +1 -1
  95. package/dist/commands/index.d.ts +2 -1
  96. package/dist/commands/index.d.ts.map +1 -1
  97. package/dist/commands/index.js +6 -0
  98. package/dist/commands/overlays-mount.d.ts +7 -9
  99. package/dist/commands/overlays-mount.d.ts.map +1 -1
  100. package/dist/commands/shell-bridge.d.ts +57 -0
  101. package/dist/commands/shell-bridge.d.ts.map +1 -0
  102. package/dist/commands/shell-bridge.js +32 -0
  103. package/dist/common/errors.d.ts +35 -0
  104. package/dist/common/errors.d.ts.map +1 -1
  105. package/dist/common/errors.js +29 -1
  106. package/dist/common/index.d.ts +2 -0
  107. package/dist/common/index.d.ts.map +1 -1
  108. package/dist/common/index.js +6 -0
  109. package/dist/common/is-dev.d.ts.map +1 -0
  110. package/dist/{motion/index.d.ts → common/motion.d.ts} +1 -1
  111. package/dist/common/motion.d.ts.map +1 -0
  112. package/dist/common/props.d.ts +1 -1
  113. package/dist/common/props.d.ts.map +1 -1
  114. package/dist/common/validate.js +1 -1
  115. package/dist/content/markdown.d.ts +3 -4
  116. package/dist/content/markdown.d.ts.map +1 -1
  117. package/dist/content/markdown.js +1 -4
  118. package/dist/controls/actions.d.ts +1 -1
  119. package/dist/controls/actions.d.ts.map +1 -1
  120. package/dist/controls/panel-row.js +1 -1
  121. package/dist/controls/range-filter.js +1 -1
  122. package/dist/dashboard/delta-badge.d.ts +18 -10
  123. package/dist/dashboard/delta-badge.d.ts.map +1 -1
  124. package/dist/dashboard/delta-badge.js +33 -1
  125. package/dist/dashboard/empty-state.d.ts +1 -4
  126. package/dist/dashboard/empty-state.d.ts.map +1 -1
  127. package/dist/dashboard/empty-state.js +1 -2
  128. package/dist/dashboard/index.d.ts +4 -2
  129. package/dist/dashboard/index.d.ts.map +1 -1
  130. package/dist/dashboard/index.js +7 -1
  131. package/dist/dashboard/query-state.d.ts +4 -13
  132. package/dist/dashboard/query-state.d.ts.map +1 -1
  133. package/dist/dashboard/query-state.js +4 -7
  134. package/dist/{section → dashboard}/section.d.ts +2 -2
  135. package/dist/{section → dashboard}/section.d.ts.map +1 -1
  136. package/dist/{section → dashboard}/section.js +2 -2
  137. package/dist/dashboard/settings-section.d.ts +1 -1
  138. package/dist/dashboard/settings-section.d.ts.map +1 -1
  139. package/dist/dashboard/settings-section.js +1 -1
  140. package/dist/dashboard/stat-card.d.ts +1 -1
  141. package/dist/dashboard/stat-card.d.ts.map +1 -1
  142. package/dist/dashboard/stat-card.js +1 -1
  143. package/dist/dashboard/widget-header.d.ts.map +1 -0
  144. package/dist/data/data-table.js +2 -2
  145. package/dist/data/table.d.ts +1 -1
  146. package/dist/data/table.d.ts.map +1 -1
  147. package/dist/data/table.js +2 -16
  148. package/dist/data/virtual.d.ts +3 -3
  149. package/dist/dev-dock.d.ts +28 -0
  150. package/dist/dev-dock.d.ts.map +1 -0
  151. package/dist/dev-dock.js +85 -0
  152. package/dist/format/date.d.ts +62 -0
  153. package/dist/format/date.d.ts.map +1 -0
  154. package/dist/format/date.js +106 -0
  155. package/dist/format/index.d.ts +39 -0
  156. package/dist/format/index.d.ts.map +1 -0
  157. package/dist/format/index.js +54 -0
  158. package/dist/format/measure.d.ts +15 -0
  159. package/dist/format/measure.d.ts.map +1 -0
  160. package/dist/format/measure.js +16 -0
  161. package/dist/format/number.d.ts +69 -0
  162. package/dist/format/number.d.ts.map +1 -0
  163. package/dist/format/number.js +62 -0
  164. package/dist/format/shared.d.ts +27 -0
  165. package/dist/format/shared.d.ts.map +1 -0
  166. package/dist/format/shared.js +29 -0
  167. package/dist/forms/field.d.ts +4 -20
  168. package/dist/forms/field.d.ts.map +1 -1
  169. package/dist/forms/field.js +0 -4
  170. package/dist/forms/form-layout.d.ts +1 -1
  171. package/dist/forms/form-layout.d.ts.map +1 -1
  172. package/dist/forms/form-layout.js +1 -1
  173. package/dist/forms/index.d.ts +1 -1
  174. package/dist/forms/index.d.ts.map +1 -1
  175. package/dist/forms/index.js +1 -2
  176. package/dist/forms/use-form-submit.js +1 -1
  177. package/dist/guard/index.d.ts +2 -9
  178. package/dist/guard/index.d.ts.map +1 -1
  179. package/dist/guard/index.js +16 -26
  180. package/dist/guard/types.d.ts +8 -10
  181. package/dist/guard/types.d.ts.map +1 -1
  182. package/dist/index.d.ts +16 -11
  183. package/dist/index.d.ts.map +1 -1
  184. package/dist/index.js +23 -29
  185. package/dist/notifications/index.d.ts +5 -5
  186. package/dist/notifications/index.d.ts.map +1 -1
  187. package/dist/notifications/index.js +0 -2
  188. package/dist/notifications/mount-guard.d.ts.map +1 -1
  189. package/dist/notifications/mount-guard.js +2 -2
  190. package/dist/provider/connectivity-indicator.d.ts.map +1 -0
  191. package/dist/{connectivity → provider}/connectivity-provider.d.ts +1 -1
  192. package/dist/provider/connectivity-provider.d.ts.map +1 -0
  193. package/dist/{connectivity/types.d.ts → provider/connectivity-types.d.ts} +1 -1
  194. package/dist/provider/connectivity-types.d.ts.map +1 -0
  195. package/dist/provider/error-fallback.module.css +21 -0
  196. package/dist/provider/error-fallback.module.css.d.ts +9 -0
  197. package/dist/provider/index.d.ts +20 -36
  198. package/dist/provider/index.d.ts.map +1 -1
  199. package/dist/provider/index.js +42 -32
  200. package/dist/provider/lab-theme.js +1 -1
  201. package/dist/provider/use-connectivity.d.ts +3 -0
  202. package/dist/provider/use-connectivity.d.ts.map +1 -0
  203. package/dist/query-client.d.ts +63 -0
  204. package/dist/query-client.d.ts.map +1 -0
  205. package/dist/query-client.js +33 -0
  206. package/dist/{query/devtools.d.ts → query-devtools.d.ts} +2 -2
  207. package/dist/query-devtools.d.ts.map +1 -0
  208. package/dist/register.d.ts +17 -3
  209. package/dist/register.d.ts.map +1 -1
  210. package/dist/router-tanstack/index.d.ts +4 -7
  211. package/dist/router-tanstack/index.d.ts.map +1 -1
  212. package/dist/router-tanstack/index.js +1 -9
  213. package/dist/router-tanstack/nav.d.ts +1 -1
  214. package/dist/router-tanstack/nav.d.ts.map +1 -1
  215. package/dist/router-tanstack/search-store.d.ts +3 -6
  216. package/dist/router-tanstack/search-store.d.ts.map +1 -1
  217. package/dist/router-tanstack/use-nav.d.ts +1 -1
  218. package/dist/router-tanstack/use-nav.d.ts.map +1 -1
  219. package/dist/shell/app-breadcrumbs.d.ts +1 -1
  220. package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
  221. package/dist/shell/app-main.module.css +11 -0
  222. package/dist/shell/app-mobile-nav.d.ts +1 -1
  223. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  224. package/dist/shell/app-mobile-nav.js +24 -2
  225. package/dist/shell/app-sidebar.d.ts +1 -1
  226. package/dist/shell/app-sidebar.d.ts.map +1 -1
  227. package/dist/shell/app-sidebar.js +31 -2
  228. package/dist/shell/app-sidebar.module.css +10 -0
  229. package/dist/shell/index.d.ts +10 -4
  230. package/dist/shell/index.d.ts.map +1 -1
  231. package/dist/shell/index.js +13 -17
  232. package/dist/shell/mobile-nav-model.d.ts +1 -1
  233. package/dist/shell/mobile-nav-model.d.ts.map +1 -1
  234. package/dist/{nav/types.d.ts → shell/nav-types.d.ts} +1 -1
  235. package/dist/shell/nav-types.d.ts.map +1 -0
  236. package/dist/shell/page-aside.d.ts.map +1 -1
  237. package/dist/shell/page-aside.js +2 -29
  238. package/dist/shell/page-title.d.ts +14 -0
  239. package/dist/shell/page-title.d.ts.map +1 -0
  240. package/dist/shell/page-title.js +25 -0
  241. package/dist/shell/page-title.module.css +33 -0
  242. package/dist/shell/page-title.module.css.d.ts +11 -0
  243. package/dist/shell/sidebar-block-model.d.ts +1 -1
  244. package/dist/shell/sidebar-block-model.d.ts.map +1 -1
  245. package/dist/shell/sidebar-blocks.d.ts +1 -1
  246. package/dist/shell/sidebar-blocks.d.ts.map +1 -1
  247. package/dist/shell/use-breakpoint.d.ts +11 -0
  248. package/dist/shell/use-breakpoint.d.ts.map +1 -0
  249. package/dist/shell/use-breakpoint.js +33 -0
  250. package/dist/state/fields.d.ts +3 -19
  251. package/dist/state/fields.d.ts.map +1 -1
  252. package/dist/state/fields.js +6 -16
  253. package/dist/styles.css +36 -0
  254. package/dist/surfaces.d.ts +18 -41
  255. package/dist/surfaces.d.ts.map +1 -1
  256. package/dist/surfaces.js +46 -59
  257. package/dist/theme/index.d.ts.map +1 -1
  258. package/dist/{theme-toggle/index.d.ts → theme/theme-toggle.d.ts} +1 -1
  259. package/dist/theme/theme-toggle.d.ts.map +1 -0
  260. package/dist/{theme-toggle/index.js → theme/theme-toggle.js} +1 -1
  261. package/dist/tokens/index.d.ts +1 -0
  262. package/dist/tokens/index.d.ts.map +1 -1
  263. package/dist/tokens/index.js +10 -0
  264. package/dist/tokens/palette.d.ts +12 -0
  265. package/dist/tokens/palette.d.ts.map +1 -1
  266. package/dist/tokens/palette.js +13 -1
  267. package/dist/tokens.css +1 -0
  268. package/llms.txt +9 -24
  269. package/package.json +12 -16
  270. package/src/agent/ai-sdk-transport.ts +11 -4
  271. package/src/agent/coalesce.ts +1 -1
  272. package/src/agent/merge.ts +1 -1
  273. package/src/agent/transport.ts +15 -2
  274. package/src/agent/use-agent-thread-runs.ts +65 -15
  275. package/src/agent-chat/index.ts +6 -4
  276. package/src/agent-chat/relative-time.ts +1 -1
  277. package/src/agent-chat/thread-detail-panel.tsx +1 -1
  278. package/src/agent-chat/thread-feed-row.tsx +105 -30
  279. package/src/agent-chat/thread-feed.tsx +51 -25
  280. package/src/agent-chat/thread-message.tsx +1 -1
  281. package/src/agent-chat/virtualize.ts +48 -0
  282. package/src/charts/index.ts +7 -10
  283. package/src/charts/kinds/BandStrip.tsx +1 -1
  284. package/src/charts/kinds/Bars.tsx +2 -5
  285. package/src/charts/kinds/DualPanel.tsx +4 -3
  286. package/src/charts/kinds/StackedArea.tsx +1 -1
  287. package/src/charts/kinds/ZonedLine.tsx +1 -4
  288. package/src/charts/primitives/Axes.tsx +49 -0
  289. package/src/charts/primitives/ChartCard.tsx +49 -6
  290. package/src/charts/primitives/ChartFrame.tsx +5 -1
  291. package/src/charts/primitives/ChartPending.tsx +14 -8
  292. package/src/charts/utils/format.ts +17 -186
  293. package/src/cli/audit-allows.ts +371 -0
  294. package/src/cli/check-theme.ts +197 -0
  295. package/src/cli/doctor.ts +331 -0
  296. package/src/cli/guard-hook.ts +115 -0
  297. package/src/cli/help.ts +101 -0
  298. package/src/cli/index.ts +269 -3439
  299. package/src/cli/init.ts +247 -0
  300. package/src/cli/sync.ts +324 -0
  301. package/src/cli/tokens-css.ts +252 -0
  302. package/src/commands/define-commands.ts +1 -1
  303. package/src/commands/define-overlays.ts +1 -1
  304. package/src/commands/index.ts +10 -1
  305. package/src/commands/overlays-mount.tsx +8 -10
  306. package/src/commands/shell-bridge.ts +86 -0
  307. package/src/common/errors.ts +70 -0
  308. package/src/common/index.ts +2 -0
  309. package/src/common/props.ts +1 -1
  310. package/src/common/validate.ts +1 -1
  311. package/src/content/markdown.tsx +4 -8
  312. package/src/controls/actions.tsx +1 -1
  313. package/src/controls/panel-row.tsx +1 -1
  314. package/src/controls/range-filter.tsx +1 -1
  315. package/src/dashboard/delta-badge.tsx +74 -7
  316. package/src/dashboard/empty-state.tsx +2 -6
  317. package/src/dashboard/index.ts +11 -2
  318. package/src/dashboard/query-state.tsx +5 -18
  319. package/src/{section → dashboard}/section.tsx +4 -4
  320. package/src/dashboard/settings-section.tsx +2 -2
  321. package/src/dashboard/stat-card.tsx +2 -2
  322. package/src/data/data-table.tsx +3 -3
  323. package/src/data/table.ts +6 -25
  324. package/src/data/virtual-list.tsx +1 -1
  325. package/src/data/virtual.ts +3 -3
  326. package/src/dev-dock.tsx +159 -0
  327. package/src/format/date.ts +184 -0
  328. package/src/format/index.ts +69 -0
  329. package/src/format/measure.ts +31 -0
  330. package/src/format/number.ts +129 -0
  331. package/src/format/shared.ts +63 -0
  332. package/src/forms/field.ts +4 -24
  333. package/src/forms/form-layout.tsx +2 -2
  334. package/src/forms/index.ts +1 -2
  335. package/src/forms/use-form-submit.ts +1 -1
  336. package/src/guard/index.ts +30 -56
  337. package/src/guard/types.ts +7 -10
  338. package/src/index.ts +38 -29
  339. package/src/notifications/index.ts +5 -7
  340. package/src/notifications/mount-guard.ts +7 -9
  341. package/src/{connectivity → provider}/connectivity-indicator.tsx +1 -1
  342. package/src/{connectivity → provider}/connectivity-provider.tsx +1 -1
  343. package/src/provider/error-fallback.module.css +21 -0
  344. package/src/provider/error-fallback.module.css.d.ts +9 -0
  345. package/src/provider/index.tsx +71 -71
  346. package/src/provider/lab-theme.ts +1 -1
  347. package/src/{connectivity → provider}/use-connectivity.ts +3 -3
  348. package/src/query-client.ts +100 -0
  349. package/src/{query/devtools.tsx → query-devtools.tsx} +5 -4
  350. package/src/register.ts +20 -4
  351. package/src/router-tanstack/index.ts +4 -15
  352. package/src/router-tanstack/nav.ts +1 -1
  353. package/src/router-tanstack/search-store.ts +3 -4
  354. package/src/router-tanstack/use-nav.ts +1 -1
  355. package/src/shell/app-breadcrumbs.tsx +1 -1
  356. package/src/shell/app-main.module.css +11 -0
  357. package/src/shell/app-mobile-nav.tsx +28 -1
  358. package/src/shell/app-sidebar.module.css +10 -0
  359. package/src/shell/app-sidebar.tsx +31 -2
  360. package/src/shell/index.tsx +23 -28
  361. package/src/shell/mobile-nav-model.ts +1 -1
  362. package/src/shell/page-aside.tsx +2 -52
  363. package/src/shell/page-title.module.css +33 -0
  364. package/src/shell/page-title.module.css.d.ts +11 -0
  365. package/src/shell/page-title.tsx +57 -0
  366. package/src/shell/sidebar-block-model.ts +1 -1
  367. package/src/shell/sidebar-blocks.tsx +1 -1
  368. package/src/shell/use-breakpoint.ts +73 -0
  369. package/src/state/fields.ts +7 -43
  370. package/src/styles.css +36 -0
  371. package/src/surfaces.ts +45 -60
  372. package/src/theme/index.ts +7 -0
  373. package/src/{theme-toggle/index.tsx → theme/theme-toggle.tsx} +1 -1
  374. package/src/tokens/index.ts +10 -0
  375. package/src/tokens/palette.ts +12 -0
  376. package/dist/connectivity/connectivity-indicator.d.ts.map +0 -1
  377. package/dist/connectivity/connectivity-provider.d.ts.map +0 -1
  378. package/dist/connectivity/index.d.ts +0 -14
  379. package/dist/connectivity/index.d.ts.map +0 -1
  380. package/dist/connectivity/index.js +0 -8
  381. package/dist/connectivity/types.d.ts.map +0 -1
  382. package/dist/connectivity/use-connectivity.d.ts +0 -3
  383. package/dist/connectivity/use-connectivity.d.ts.map +0 -1
  384. package/dist/data/index.d.ts +0 -38
  385. package/dist/data/index.d.ts.map +0 -1
  386. package/dist/data/index.js +0 -24
  387. package/dist/motion/index.d.ts.map +0 -1
  388. package/dist/nav/types.d.ts.map +0 -1
  389. package/dist/notifications/overlay.d.ts +0 -20
  390. package/dist/notifications/overlay.d.ts.map +0 -1
  391. package/dist/notifications/overlay.js +0 -14
  392. package/dist/query/devtools.d.ts.map +0 -1
  393. package/dist/query/error-message.d.ts +0 -41
  394. package/dist/query/error-message.d.ts.map +0 -1
  395. package/dist/query/error-message.js +0 -30
  396. package/dist/query/index.d.ts +0 -47
  397. package/dist/query/index.d.ts.map +0 -1
  398. package/dist/query/index.js +0 -56
  399. package/dist/router-tanstack/field.d.ts +0 -14
  400. package/dist/router-tanstack/field.d.ts.map +0 -1
  401. package/dist/router-tanstack/field.js +0 -4
  402. package/dist/router-tanstack/multi-search-param-store.d.ts +0 -43
  403. package/dist/router-tanstack/multi-search-param-store.d.ts.map +0 -1
  404. package/dist/router-tanstack/multi-search-param-store.js +0 -30
  405. package/dist/router-tanstack/search-param-store.d.ts +0 -42
  406. package/dist/router-tanstack/search-param-store.d.ts.map +0 -1
  407. package/dist/router-tanstack/search-param-store.js +0 -25
  408. package/dist/section/index.d.ts +0 -2
  409. package/dist/section/index.d.ts.map +0 -1
  410. package/dist/section/index.js +0 -4
  411. package/dist/theme-toggle/index.d.ts.map +0 -1
  412. package/dist/utils/is-dev.d.ts.map +0 -1
  413. package/dist/widget-header/delta-badge.d.ts +0 -19
  414. package/dist/widget-header/delta-badge.d.ts.map +0 -1
  415. package/dist/widget-header/delta-badge.js +0 -36
  416. package/dist/widget-header/index.d.ts +0 -3
  417. package/dist/widget-header/index.d.ts.map +0 -1
  418. package/dist/widget-header/index.js +0 -8
  419. package/dist/widget-header/widget-header.d.ts.map +0 -1
  420. package/src/connectivity/index.ts +0 -19
  421. package/src/data/index.ts +0 -69
  422. package/src/notifications/overlay.tsx +0 -66
  423. package/src/query/error-message.ts +0 -70
  424. package/src/query/index.ts +0 -97
  425. package/src/router-tanstack/field.ts +0 -36
  426. package/src/router-tanstack/multi-search-param-store.ts +0 -106
  427. package/src/router-tanstack/search-param-store.ts +0 -100
  428. package/src/section/index.ts +0 -1
  429. package/src/widget-header/delta-badge.tsx +0 -77
  430. package/src/widget-header/index.ts +0 -10
  431. /package/dist/{utils → common}/is-dev.d.ts +0 -0
  432. /package/dist/{utils → common}/is-dev.js +0 -0
  433. /package/dist/{motion/index.js → common/motion.js} +0 -0
  434. /package/dist/{section → dashboard}/section.module.css +0 -0
  435. /package/dist/{section → dashboard}/section.module.css.d.ts +0 -0
  436. /package/dist/{widget-header → dashboard}/widget-header.d.ts +0 -0
  437. /package/dist/{widget-header → dashboard}/widget-header.js +0 -0
  438. /package/dist/{widget-header → dashboard}/widget-header.module.css +0 -0
  439. /package/dist/{widget-header → dashboard}/widget-header.module.css.d.ts +0 -0
  440. /package/dist/{connectivity → provider}/connectivity-indicator.d.ts +0 -0
  441. /package/dist/{connectivity → provider}/connectivity-indicator.js +0 -0
  442. /package/dist/{connectivity → provider}/connectivity-provider.js +0 -0
  443. /package/dist/{connectivity/types.js → provider/connectivity-types.js} +0 -0
  444. /package/dist/{connectivity → provider}/use-connectivity.js +0 -0
  445. /package/dist/{query/devtools.js → query-devtools.js} +0 -0
  446. /package/dist/{nav/types.js → shell/nav-types.js} +0 -0
  447. /package/src/{utils → common}/is-dev.ts +0 -0
  448. /package/src/{motion/index.ts → common/motion.ts} +0 -0
  449. /package/src/{section → dashboard}/section.module.css +0 -0
  450. /package/src/{section → dashboard}/section.module.css.d.ts +0 -0
  451. /package/src/{widget-header → dashboard}/widget-header.module.css +0 -0
  452. /package/src/{widget-header → dashboard}/widget-header.module.css.d.ts +0 -0
  453. /package/src/{widget-header → dashboard}/widget-header.tsx +0 -0
  454. /package/src/{connectivity/types.ts → provider/connectivity-types.ts} +0 -0
  455. /package/src/{nav/types.ts → shell/nav-types.ts} +0 -0
@@ -9,212 +9,89 @@ paths:
9
9
  ---
10
10
 
11
11
  <!-- basalt:coverage -->
12
- <!-- GENERATED from src/surfaces.ts — `basalt-ui check-coverage --write`. Do not hand-edit. -->
12
+ <!-- GENERATED from src/surfaces.ts — `bun scripts/check-coverage.ts --write`. Do not hand-edit. -->
13
13
  <!-- backed by: guard kinds — none · oxlint rules — basalt/agent-no-raw-usechat, basalt/agent-resume-guard, basalt/ai-sdk-major, basalt/forms-field-key, basalt/query-dual-import, basalt/query-fn-unwrap -->
14
14
  <!-- not guarded: — -->
15
15
  <!-- /basalt:coverage -->
16
16
 
17
17
  # Basalt Batteries — the adapter surfaces
18
18
 
19
- Each battery is a subpath with **one** doctrine: the shipped seam is the seam, and re-rolling it is
20
- the failure this rule exists to prevent. Every peer named below is OPTIONAL — install it before
21
- importing the battery. The API is in the shipped types, the JSDoc and `llms.txt` at the install
22
- directory; what follows is only what a reader gets wrong without being told.
23
-
24
- ## `./query` — TanStack Query
25
-
26
- `@tanstack/react-query` is a REQUIRED peer (`BasaltProvider` needs it at build time). Import the
27
- hooks from `basalt-ui/query`, never dual-import from `@tanstack/react-query`.
28
-
29
- - **One factory per resource**, keyed `[resource, action, ...params]`, built with `queryOptions`;
30
- components import the factory, never a raw string key.
31
- - **`queryFn` wraps the call in `unwrap()`** — it throws on the error branch AND on a `null` data
32
- with no error (a 204, a silent transport failure). Both are caller bugs, so both must surface.
33
- - **Render a query with `QueryState`, don't write the switch.** Rendering only the empty branch is
34
- the bug it retires: a consumer's library showed `No images` on a 500 because nobody wrote the other
35
- three branches. Precedence is error-without-data → empty → loading → children, plus a cached-data
36
- banner when a refresh fails over existing data. `LoadingState`/`ErrorState` ship beside it for a
37
- page that must place the branches in different DOM positions.
38
- - **`QueryState` takes `useQuery`, not `useSuspenseQuery`** — a suspense read never hands you an
39
- undefined `data` or an error branch, so there is nothing to decide. Suspense + an error boundary
40
- and `QueryState` are the two ways to render a query; pick ONE per read. Inside a
41
- loader-prefetched route, `useSuspenseQuery` + `ensureQueryData` is the pair.
42
- - **`query` is a structural subset checked at runtime** — a composed or hand-rolled result passes
43
- with no cast, and a missing `isError` throws naming the field rather than rendering a false claim.
44
- - Decode an unknown error with `toErrorMessage(err, fallback?)` / `errorStatus(err)`; invalidate by
45
- resource PREFIX after a mutation; `BasaltQueryDevtools` is lazy and production-excluded, so import
46
- it unconditionally and never the raw devtools package.
19
+ Each battery is a subpath with **one** doctrine: the shipped seam is the seam, re-rolling it is the
20
+ failure this rule prevents. Every peer is OPTIONAL — install before importing. Full API: the
21
+ shipped types, JSDoc and `llms.txt` at the install directory — this file is only what a reader gets
22
+ wrong without being told.
23
+
24
+ **Not guarded, honestly**: `forms`, `notifications`, `commands`, `data` and `content` carry zero
25
+ guard kinds or oxlint rules — the `not guarded: —` line above is real, not aspirational. Nothing
26
+ fails a build if you re-roll one of these seams by hand; follow the doctrine below on trust.
27
+
28
+ **Deprecation, as a consumer sees it.** A `basalt/deprecated-export` warn means the export still
29
+ works but leaves in a stated `removeIn` minor (autofix renames the import, keeps your local
30
+ binding). The replacement and removal minor are always in `MIGRATING.md` § Unreleased the moment
31
+ the deprecation ships — that file, not the warning text, is where you confirm what to migrate to.
32
+ Maintainer mechanics: package `CLAUDE.md` § Deprecation lifecycle.
33
+
34
+ ## Query — `createBasaltQueryClient`/`unwrap` (root barrel, C1: `./query` dropped)
35
+
36
+ `unwrap()` throws on the error branch AND on a `null` data with no error — both are caller bugs.
37
+ Render with `QueryState`, never a hand-written switch (precedence: error-without-data → empty →
38
+ loading → children). One factory per resource via `queryOptions`, never a raw string key.
47
39
 
48
40
  ## Eden Treaty — three silent `any`s (stated once)
49
41
 
50
- Stated here because they reach both `./query`'s `unwrap` and `./agent`'s transport. Three
51
- patterns silently degrade Treaty inference to `any`, with no TypeScript error at the point of
52
- breakage: a route that is not method-chained back onto the app instance; client and server packages
53
- whose tsconfig path aliases do not resolve identically; and a `t.Object`/`t.Union` **response schema
54
- on a streaming route** (Eden #231, still open). For a stream: no response schema, an explicit
55
- `: AsyncGenerator<AgentPart>` return annotation, and validation at yield time via `parseAgentPart`.
56
- Check these three first whenever Eden types regress.
42
+ Reaches both `unwrap` and `./agent`'s transport: an un-chained route, mismatched tsconfig path
43
+ aliases, and a response schema on a streaming route (Eden #231) all silently degrade inference to
44
+ `any`. Check these three first when Eden types regress.
57
45
 
58
46
  ## `./forms` — `@mantine/form`
59
47
 
60
- - **`useBasaltForm` is the entry**: `mode: 'uncontrolled'` by default (no per-keystroke re-render)
61
- and `schemaResolver(schema, { sync: true })` when a `schema` is passed. Type a schema param as
62
- `StandardSchemaV1` from `basalt-ui`, never `ZodSchema` — Valibot, Zod 4, ArkType and Effect all
63
- satisfy it. `validate` stays off its options: a cross-field rule drops to a raw `useForm`, an
64
- async one to `useFormSubmit`'s `validateAsync`.
65
- - **A field is TWO calls on one line**, never one object — a spread `key` is a React 19 warning:
66
- `<TextInput key={fieldKey(form, 'x')} {...inputProps(form, 'x')} />`. Dropping it is worse: the
67
- field keeps its old text through `form.reset()` and nothing type-checks differently, so
68
- **`basalt/forms-field-key`** guards it and autofixes it in. `field` is `@deprecated` (it
69
- collides with `basalt-ui/state`'s) and NOT an alias — it still bundles `key`.
70
- - **`FormRow` is law C1's form row** (label left, control right, label above below `sm`) inside a
71
- **`FormSection`**, clusters in a **`FormGroup`**, **`FormActions`** at the end. `SettingsRow` is
72
- the settings-page variant — pick by page, never fork a third.
73
- - **`useFormSubmit` owns the submit**, not a hand-rolled `try`/`catch`: it decodes the throw into
74
- the form-level `submitError`, routes a `{ fieldErrors }` envelope back onto the fields as inline
75
- errors and focuses the first. Hand its `isSubmitting` to **`FormStateProvider`** and the layout
76
- disables itself — never thread `disabled` per input; a custom control reads **`useFormState`**.
77
- - **`useFieldArray` is the list field**; `key(index)` is positional — reorder by an item id.
78
- - **`FormErrorSummary` goes at the TOP of the form** so assistive tech lands on it after a failed
79
- submit. It renders `null` on a clean form, so include it unconditionally.
80
- - **`useFormDraft` persists through `createPersistedState`** — `autosave: true` owns its own
81
- subscription, `clearDraft()` on success, keep `key` stable (changing it loses the draft), bump
82
- `version` on a shape change.
48
+ `useBasaltForm` (`mode: 'uncontrolled'`, `StandardSchemaV1` never `ZodSchema`) +
49
+ `useFormSubmit`/`useFieldArray`/`FormErrorSummary`/`useFormDraft`. A field is two calls, never one
50
+ object (`basalt/forms-field-key` guards + autofixes it) — dropping `key` keeps stale text through
51
+ `form.reset()`.
83
52
 
84
53
  ## `./notifications` — `@mantine/notifications`
85
54
 
86
- - **`notify({ intent, message })` is the surface**, plus the four `notifySuccess/Error/Warning/Info`
87
- wrappers and `notifyPromise` for an async operation. Intent — not a color — is what maps to the
88
- Mantine color, the correct aria `role` (polite vs assertive) and the per-intent `autoClose`, so
89
- **never pass a raw color instead of an intent**.
90
- - **`defineNotifications` + `emit` is the typed registry**: augment `BasaltRegister.notifications`
91
- once and unknown kinds become tsc errors. `defineNotification` only TYPES a single spec — only
92
- `defineNotifications(map)` registers, and `emit` before that registration is a runtime no-op.
93
- - **A spec carries no `title`/`icon`** by design; those stay per-call so a varying title is not
94
- forced into the registry.
95
- - **Colors beyond the four intents live OUTSIDE the registry** — call Mantine's raw
96
- `notifications.show({ color })` directly, and accept the stated cost: a raw toast is invisible to
97
- the persisted history, the bell and the center, because only `notify`/`notifyPromise` record.
98
- - **`notifyUndo` is the undo window**, not a toast plus your own `setTimeout`: one number is both
99
- the `Undo` affordance and the commit delay, `onExpire` is where the optimistic mutation commits,
100
- `onUndo` reverts, and exactly one of the two ever runs.
101
- - History is a module-level persisted ring buffer; `useNotificationHistory` reads it, and
102
- `NotificationBell` (built for the shell's `globalActions`) opens a `NotificationCenter`, which also
103
- mounts standalone in any Drawer or panel.
104
- - The overlay MOUNT is basalt-mantine.md's — one place, one statement.
55
+ `notify({ intent, message })` — intent, never a raw color, drives the Mantine color AND the aria
56
+ `role`. `defineNotifications`/`emit` is the typed registry; only persisted-history calls
57
+ (`notify`/`notifyPromise`) reach the bell/center. `notifyUndo` owns the undo-window contract.
105
58
 
106
59
  ## `./commands` — the command bus
107
60
 
108
- - **`defineCommands` + `runCommand`, `defineOverlays` + `overlays.open`** — augment
109
- `BasaltRegister` once per registry and unknown ids are tsc errors. The augmentation is type-only
110
- and erased, so both factories stash their map at module scope: call the factory once, at app boot,
111
- before anything runs a command.
112
- - **A TS circular-inference footgun**: `overlays.open(...)` inside a `run` in the same module that
113
- augments `BasaltRegister.commands` can make the map infer as `any`. Annotate that `run`'s return
114
- type explicitly, or split overlay-opening commands into their own file.
115
- - **Projections, not parallel lists**: `toSpotlightActions()` (respecting each command's `when()`),
116
- `toShortcutList()` / `ShortcutsHelp` for display, `toHotkeyBindings()` / `useCommandHotkeys()` for
117
- the live keys. Never hand-author a second list of the same commands.
118
- - Reach for `openSpotlight`/`closeSpotlight` from `basalt-ui/commands`, not Mantine's global
119
- `spotlight` — `BasaltOverlays` mounts against basalt's own store.
120
- - **A confirm dialog is not an overlay you define**: `overlays.confirm` / `overlays.confirmDelete`
121
- need no registry entry and resolve to the answer. `defineOverlays` is for an overlay with a
122
- payload and a body of its own.
123
- - **Imperative overlay vs route**: `overlays.open` for an ephemeral confirm or quick edit; a route
124
- for anything that must be shareable, refreshable or back-button-addressable.
125
-
126
- ## `./data` — TanStack Table + Virtual
127
-
128
- - **Read the props before hand-rolling anything**: title/icon/subtitle + count, an `actions`
129
- toolbar slot, `facets`, global search, client- or server-side pagination, column pinning, sticky
130
- header, `maxHeight`/`minWidth`, per-column `meta.align` and `meta.numeral` are all there.
131
- `meta.align` is a `ColumnMeta` augmentation, so a typo is a tsc error and a bad VALUE throws naming
132
- the column — declare alignment once instead of on both the `th` and the `td`.
133
- - **Adopt it for ownership, not for a line count.** One consumer's port ran 29–38 lines LONGER:
134
- accessor blocks cost more than bespoke `<Table.Td>` rows. What it buys is the `type="native"`
135
- scroll footgun, alignment stated once, and sorting/filtering/pagination no longer being yours.
136
- A table with no sorting, no pagination, no filter and no alignment to declare is a layout grid —
137
- adopting the component there buys nothing.
138
- - **`manualPagination` imposes a contract on every other prop**: `data` becomes one server page
139
- while the bar still claims `of N`, so sorting (`manualSorting` or `enableSorting={false}`),
140
- filtering (`manualFiltering`) and the count (`rowCount`) must each be resolved explicitly.
141
- Unresolved, it throws in dev naming every breach and degrades to the honest table in production.
142
- Read the throw; don't work around it.
143
- - **The escape hatch is the same subpath**: `useReactTable`/`flexRender`/the row models are
144
- re-exported, so a bespoke table needs no direct `@tanstack/react-table` import — and it scrolls
145
- through the identical `Table.ScrollContainer type="native"` node.
146
- - **`BasaltVirtualList` above a few hundred rows**; below that a plain `Stack`/`Table` is cheaper.
147
- It sets `useFlushSync: false` for React 19 itself — do not override it.
61
+ `defineCommands`/`runCommand`, `defineOverlays`/`overlays.open` — call each factory once, at app
62
+ boot. Project via `toSpotlightActions`/`toShortcutList`/`toHotkeyBindings`, never a hand-authored
63
+ second list. `overlays.confirm`/`confirmDelete` need no registry entry.
64
+
65
+ ## `./data/table` + `./data/virtual` — TanStack Table + Virtual (C1: no bare `./data`)
66
+
67
+ Read the props before hand-rolling anything (facets, pagination, `meta.align`/`meta.numeral`).
68
+ `manualPagination` imposes a contract on sorting/filtering/count — unresolved, it throws in dev.
69
+ `BasaltVirtualList` above a few hundred rows only.
148
70
 
149
71
  ## `./content` — prose, markdown, MDX
150
72
 
151
- - **Three entries, one typography layer**: `Prose` (JSX children), `Markdown` (a string — CMS,
152
- files, streamed model output), `mdxComponents` (an MDX pipeline's component map). `Markdown` wraps
153
- its own `Prose`; MDX output does not, so wrap it or use `ArticleLayout`.
154
- - **`streaming` is a RENDERING mode, not a trust claim.** `contentTrust` is the independent security
155
- input and the sole input to the image-origin allowlist: **any surface rendering agent or model
156
- output must pin `contentTrust="untrusted"`** — an auto-fetched image is the classic
157
- prompt-injection exfiltration channel. Link/script hardening is always on, streaming or not, and
158
- `sanitizeSchema` only ADDS to the default schema.
159
- - **Every optional peer degrades, except one.** No `shiki` (or an unknown language) renders a plain
160
- mono block; no `beautiful-mermaid` leaves the fence as code; `remend`'s streaming repair and the
161
- markdown renderer resolve lazily. Six mermaid diagram types are covered — anything else is a
162
- consumer escape hatch outside `Markdown`.
163
- - **`ArticleLayout` is the docs frame** (meta header, sticky scroll-spy TOC, prev/next); every
164
- linking content component takes `renderLink` as the router bridge and defaults to a plain anchor.
165
- - **The Article model is data + pure operators**, generic over the taxonomy — the app declares which
166
- categories and tags exist (`satisfies readonly Article<Cat, Tag>[]`, deliberately no factory).
167
- Filter state is store fields, and the filter UI is `FilterSet`/`ViewTabs`/`MultiSelectFilter`,
168
- re-exported here so a content-only consumer has one import path (basalt-controls.md).
169
- - Build-time content tooling (`content-collections`) is a RECIPE, not a dependency; its vite plugin
170
- must run FIRST because other transforms resolve its virtual module.
73
+ `Prose`/`Markdown`/`mdxComponents` share one typography layer. `streaming` is a rendering mode, not
74
+ a trust claim — any surface rendering agent/model output pins `contentTrust="untrusted"`. Every
75
+ optional peer degrades gracefully except the sanitizer, which is always on.
171
76
 
172
77
  ## `./agent` + `./agent-chat` — streaming chat
173
78
 
174
- - **`./agent` is headless and Mantine-free; the chrome is `./agent-chat`.** `./agent` ships no
175
- markdown renderer at all (`agent → content` is lint-blocked), so `PartList` takes a
176
- consumer-supplied `components.text`; basalt's own is `./content`'s `Markdown`.
177
- - **Every switch over `AgentPart` ends with `default: return assertNever(part)`** — that is what
178
- makes a new variant a compile error instead of a silently dropped part.
179
- - **`aiSdkTransport` is the recommended default** (optional peer `ai`, resolved lazily on first
180
- stream); `edenTransport` is the zero-extra-dependency alternative. Neither is deprecated. The
181
- transport is an injected seam, so a mock is just an object with a `stream` generator.
182
- - **`useAgentStream` runs ONE turn; `useAgentThreadRuns` runs N keyed by thread id.** Reach for the
183
- latter for the many-short-chats shape, with `createThreadsStore` as its persisted registry.
184
- - **basalt ships the outcome-resolver SEAM, never an LLM call.** `heuristicOutcome` is a demo
185
- fallback; production derives `{ title, summary, status }` from the finished run. The outcome shape
186
- is deliberately DIFFERENT from the transcript so raw prompts and thinking cannot leak into a feed.
187
- - **Stream resumption is a client contract, not a backend**: a `StartPart` carries the token, the
188
- transport implements `resume`, and a mount-time reconnect is attempted before an orphaned thread
189
- falls back to interrupted. A transport that does neither behaves exactly as before.
190
- - **Three lint guards protect this discipline** — an unguarded `resume`, a raw `useChat`, and an `ai`
191
- major mismatch — and they honour `basalt-agent-allow`, never `theme-allow`. `doctor`'s
192
- `ai-major-parity` is the cross-package counterpart, because a lint run only ever sees one
193
- package's manifest.
79
+ `./agent` is headless/Mantine-free; `./agent-chat` is the chrome. Every `AgentPart` switch ends
80
+ `default: return assertNever(part)`. `useAgentStream` runs one turn, `useAgentThreadRuns` runs N by
81
+ thread id. Stream resumption reconnects a MOUNT (page reload mid-stream), it never continues a
82
+ click — `retry()`/`regenerate()` always start a fresh stream from the cached input.
83
+
84
+ **Why the three lint guards exist** (a consumer's build fails on one of these; all honour
85
+ `basalt-agent-allow`, never `theme-allow`): `basalt/agent-resume-guard` — an unguarded
86
+ `resumeStream()`/`useChat({ resume: true })` re-fires under StrictMode with no dedup, use
87
+ `useAgentThreadRuns` instead. `basalt/agent-no-raw-usechat` — bypassing the basalt hooks loses the
88
+ unmount-abort and supersede guards. `basalt/ai-sdk-major` (+ `doctor`'s `ai-major-parity`) — a v5
89
+ stream parsed by a v7 reader throws; nothing else pins the `ai` major across packages.
194
90
 
195
91
  ## App bootstrapping — `./vite`, CSS order, `init`
196
92
 
197
- - **`basaltViteConfig` is config-only and always will be; `basaltAppPlugin` is the plugin half** —
198
- spread it into your own `plugins` array. Keeping plugins out of the preset is why adopting it never
199
- injects one you did not ask for. Position is free (`vite-plugin-pwa` self-orders by `enforce`); the
200
- one real caveat is a plugin emitting files from its own `closeBundle` after the PWA plugin's.
201
- - **CSS layer order is load-bearing**: every `@mantine/*/styles.layer.css` first, then
202
- `basalt-ui/styles.css`. An unlayered Mantine import outranks `@layer basalt` regardless of
203
- specificity.
204
- - **Head, manifest and theme colors come from the token palette** — never hand-compute a
205
- `theme-color` hex or an anti-FOUC background, and set `colorScheme` to whatever the app passes as
206
- `defaultColorScheme` or you ship a flash of the wrong surface. Icons are bring-your-own: either
207
- the six default filenames under `public/`, or an `icons` array naming what you actually have (an
208
- entry reaches the head only when it names a `rel`; every entry becomes a manifest icon). The
209
- service worker is opt-in and degrades to a warning without its peer.
210
- - **`init` on an existing app is a lint-debt event, not a no-op** — the shipped preset turns on
211
- whole oxlint plugins the repo was never linted against, so previously-clean code lands with real
212
- findings. Triage them; do not blanket-disable a plugin. It also patches `basalt.roots` (everything
213
- derives from it) and a `lint:basalt` script, and it KEEPS an existing `.oxlintrc.json`, which
214
- leaves the whole basalt lint half off until you extend the preset.
215
- - **The lefthook preset overrides YOU on a colliding key** — a `run:`/`glob:` under one of its
216
- command names is discarded silently, while `env:`, `exclude:`, `skip:` and your own command names
217
- merge; `BASALT_BIN` is the sanctioned seam. A broken `extends` target is a hard fail: lefthook
218
- merges a missing target into ZERO commands and still exits 0.
219
- - **Then run `doctor` and read it to the last line.** `SKIPPED` is a third outcome that exits
220
- non-zero on its own — a check that could not run is not a check that passed.
93
+ `basaltViteConfig` is config-only; spread `basaltAppPlugin` into your own `plugins`. CSS layer
94
+ order is load-bearing: every `@mantine/*/styles.layer.css` first, then `basalt-ui/styles.css` — an
95
+ unlayered Mantine import outranks `@layer basalt` regardless of specificity. `init` on an existing
96
+ app is a lint-debt event, not a no-op — triage the new findings, don't blanket-disable a plugin.
97
+ Run `doctor` after and read it to the last line (`SKIPPED` exits non-zero on its own).
@@ -6,8 +6,8 @@ paths:
6
6
  ---
7
7
 
8
8
  <!-- basalt:coverage -->
9
- <!-- GENERATED from src/surfaces.ts — `basalt-ui check-coverage --write`. Do not hand-edit. -->
10
- <!-- backed by: guard kinds — chart-missing-aria-label, raw-color-fn, raw-hex, raw-visx-axis, unframed-chart · oxlint rules — basalt/chart-in-raw-surface, basalt/chart-legend-literal, basalt/hand-rolled-plot, basalt/visx-boundary, basalt/visx-tooltip -->
9
+ <!-- GENERATED from src/surfaces.ts — `bun scripts/check-coverage.ts --write`. Do not hand-edit. -->
10
+ <!-- backed by: guard kinds — chart-missing-aria-label, raw-color-fn, raw-hex, raw-visx-axis · oxlint rules — basalt/chart-in-raw-surface, basalt/chart-legend-literal, basalt/hand-rolled-plot, basalt/visx-boundary, basalt/visx-tooltip -->
11
11
  <!-- not guarded: — -->
12
12
  <!-- /basalt:coverage -->
13
13
 
@@ -26,21 +26,18 @@ the hover/keyboard overlay and the derived tooltip. The caller supplies `series`
26
26
  render prop that draws **only marks**.
27
27
 
28
28
  Reaching past it for an axis, a tooltip or a margin means the chart has drifted. `basalt/hand-rolled-plot`
29
- enforces this per NODE: a chart-assembly primitive rendered in a file that does not compose
30
- `CartesianChart` is a finding, waived one node at a time with `theme-allow hand-rolled-plot — <why>`.
31
-
32
- **Five shipped shapes are the declared exceptions**, by shape, not by preference: `DualPanel` (two
33
- panes), `MirroredBars` (two bar panes on one baseline), `BandStrip` (no y dimension at all), `Donut`
34
- (radial) and `Heatmap` (a matrix). They compose `ChartFrame` + `useChartCursor` + `autoMargin` +
35
- `ChartTooltipFloat` directly. The first three carry a `theme-allow-file hand-rolled-plot — <why>`
36
- declaration; `Donut` and `Heatmap` render no assembly primitive, so nothing fires and there is
37
- nothing to waive. A genuinely non-single-plot bespoke shape declares itself the same way — file
38
- scope, spelled, with a reason. The module that DEFINES `CartesianChart` is exempt definitionally: a
39
- rule saying "compose X" cannot fire inside X.
40
-
41
- **A documented limit:** the rule keys on those visx primitives, so a chart drawn with DOM nodes or a
42
- hand-assembled `<svg>` is structurally invisible to it. Compose the primitive because it is the
43
- contract, not because lint will catch you.
29
+ enforces this per NODE, waived one node at a time with `theme-allow hand-rolled-plot — <why>`.
30
+
31
+ **Five shipped shapes are the declared exceptions**, by shape, not preference: `DualPanel` (two
32
+ panes), `MirroredBars` (two bar panes, one baseline), `BandStrip` (no y dimension), `Donut`
33
+ (radial), `Heatmap` (matrix). They compose `ChartFrame` + `useChartCursor` + `autoMargin` +
34
+ `ChartTooltipFloat` directly. The first three carry `theme-allow-file hand-rolled-plot — <why>`;
35
+ `Donut`/`Heatmap` render no assembly primitive, so nothing fires. A bespoke non-single-plot shape
36
+ declares itself the same way. `CartesianChart`'s own defining module is exempt definitionally.
37
+
38
+ **A documented limit:** the rule keys on visx primitives, so a DOM- or hand-`<svg>`-drawn chart is
39
+ structurally invisible to it. Compose the primitive because it's the contract, not because lint
40
+ catches you.
44
41
 
45
42
  ## `series` is the single source of truth
46
43
 
@@ -57,74 +54,58 @@ minus whatever the legend has toggled off, and the legend toggles by default at
57
54
 
58
55
  ## Boundaries (three independent rules, no escape hatch)
59
56
 
60
- - **`@visx/*` only inside a `charts/` path segment** (`basalt/visx-boundary`, shipped AND repo-local).
61
- Need a raw visx primitive? Import it from `basalt-ui/charts`, which re-exports the curated set, and
62
- keep your bespoke chart file under a `charts/` directory.
57
+ - **`@visx/*` only inside a `charts/` path segment** (`basalt/visx-boundary`, shipped AND
58
+ repo-local) — import a raw visx primitive from `basalt-ui/charts`'s curated re-export instead.
63
59
  - **`@visx/tooltip` is banned everywhere**, charts included (`basalt/visx-tooltip`) — use
64
- `ChartTooltipFloat` + `TooltipHeader`/`TooltipRow`/`TooltipBody`. It portals to `document.body`, so
65
- it can be authored anywhere including inside an `<svg>`; a plain `<div>` there mounts in the SVG
66
- namespace, typechecks, lints, throws nothing, and never paints.
67
- - **basalt's own `charts/` and `tokens/` import zero `@mantine/*`** (`basalt/token-layer-boundary`,
68
- repo-local by design). That is a framework invariant, not an obligation on your app's own
69
- `charts/`-named directory: it keeps the token layer upstream of Mantine and lets
70
- `basalt-ui/charts` + `basalt-ui/tokens` resolve with no Mantine installed.
71
- - Because Mantine layout primitives are unreachable inside a chart file, `inline-display` and
72
- `raw-html-layout` deliberately do NOT fire there — the finding would be unactionable. Raw `<div>`
73
- is fine in a chart file; `VX.*` tokens are still mandatory. Reach for `ChartCenter` to center.
74
- - **Maps are not charts.** No map library is a `@visx/*` package, basalt ships no map kind, and the
75
- boundary says nothing about one — build it wherever the rest of the app's non-chart UI lives, and
76
- style its chrome with `--vx-*` so it matches.
60
+ `ChartTooltipFloat` + `TooltipHeader`/`TooltipRow`/`TooltipBody`. A plain `<div>` inside an
61
+ `<svg>` mounts in the SVG namespace, typechecks, lints, and never paints.
62
+ - **basalt's own `charts/`+`tokens/` import zero `@mantine/*`** (`basalt/token-layer-boundary`,
63
+ repo-local — a framework invariant, not an obligation on your app's own `charts/`-named dir). It
64
+ keeps the token layer upstream of Mantine and lets both subpaths resolve with no Mantine
65
+ installed.
66
+ - Mantine layout primitives are unreachable in a chart file, so `inline-display`/`raw-html-layout`
67
+ deliberately don't fire there — raw `<div>` is fine, `VX.*` tokens still mandatory
68
+ (`ChartCenter` to center).
69
+ - **Maps are not charts** — no map library is `@visx/*`, basalt ships no map kind; build one where
70
+ the rest of the app's non-chart UI lives, styled with `--vx-*`.
77
71
 
78
72
  ## Color, scheme and margins
79
73
 
80
- - **Never a raw hex in a chart file**, and never `localStorage.getItem('theme')`. Theme reactivity is
81
- **pure CSS**: the `--vx-*` variables are redeclared per scheme, so toggling restyles every chart
82
- with no React re-render. Don't branch on the color scheme in JS.
74
+ - **Never a raw hex in a chart file**, never `localStorage.getItem('theme')`. Theme reactivity is
75
+ **pure CSS**: `--vx-*` redeclares per scheme, so toggling restyles every chart with no React
76
+ re-render.
83
77
  - **Series color is consumer domain data** — one guard-exempt file per app, `defineSeries` →
84
- `seriesTokens`/`groupTokens` → `buildPaletteCss` (basalt-tokens.md). A hue keeps its identity and
85
- shifts shade across schemes, which is why every entry is a `{ light, dark }` pair.
86
- - **Margins are MEASURED, not hand-picked.** Every configured axis's tick labels run through
87
- `autoMargin`/`measureText` before layout, so `VX.margin` is a floor and a wide label widens its own
88
- gutter. Passing `y2` is what makes a chart dual-axis — the right margin widens by measurement, with
89
- no manual nudge. `margin={{ side: n }}` is the per-side override, applied last.
90
- - **Derive constants from `VX.margin`** rather than copying a number into three chart files — those
91
- are three silent-drift sites the moment the density scale is retuned.
92
- - **Never fake an empty dataset for a pending query.** "Nothing to draw" is three states, and
93
- `data ?? []` collapses "not asked yet" into "measured and absent", which is a positive claim about
94
- data nobody fetched. Pass `isPending`; every kind forwards it to one pending renderer that reserves
95
- the plot's footprint and draws nothing that could be mistaken for a measurement.
96
- - **`ChartFrame`'s container is `role="group"`, never `role="img"`.** Every descendant of a
97
- `role="img"` element is presentational, which erases the hover overlay's `role="slider"` from the
98
- accessibility tree — silently, and jsdom does not implement the pruning, so no `getByRole` test can
99
- catch the regression. Do not "simplify" it back.
78
+ `seriesTokens`/`groupTokens` → `buildPaletteCss` (basalt-tokens.md), each entry a `{light,dark}`
79
+ pair.
80
+ - **Margins are MEASURED, not hand-picked** — every axis's tick labels run through
81
+ `autoMargin`/`measureText` before layout, so `VX.margin` is a floor. Passing `y2` is what makes a
82
+ chart dual-axis; `margin={{ side: n }}` overrides last. Derive constants from `VX.margin` rather
83
+ than copying a number into three files.
84
+ - **Never fake an empty dataset for a pending query** — `data ?? []` collapses "not asked yet" into
85
+ "measured and absent". Pass `isPending`; every kind forwards it to one pending renderer.
86
+ - **`ChartFrame`'s container is `role="group"`, never `role="img"`** — every descendant of a
87
+ `role="img"` element is presentational, which would erase the hover overlay's `role="slider"`
88
+ from the accessibility tree silently (jsdom doesn't implement the pruning, so no test catches it).
100
89
 
101
90
  ## Adding a chart
102
91
 
103
- 1. **Fits a shipped kind?** Use it. The kinds are declarative — `data`, `chartId`, `getX`, `series`,
104
- one `AxisConfig` per axis, zones/thresholds/refLines as plain arrays.
105
- 2. **Second instance of a shape with no kind?** Extract a kind and migrate both call sites (Rule of
106
- Three: not on the first, not past the third). **Prove a new kind by porting real call sites** and
107
- shipping the "still cannot express" list with it — a demo page is written against the API that
108
- already exists.
109
- 3. **Genuinely unique?** Stay bespoke, composing `CartesianChart` (or `ChartFrame`) directly in the
92
+ 1. **Fits a shipped kind?** Use it — declarative props (`data`, `chartId`, `getX`, `series`, one
93
+ `AxisConfig` per axis, zones/thresholds/refLines as plain arrays).
94
+ 2. **Second instance of a shape with no kind?** Extract a kind (Rule of Three), migrating both call
95
+ sites; prove it by porting real call sites, ship the "still cannot express" list with it.
96
+ 3. **Genuinely unique?** Stay bespoke, composing `CartesianChart`/`ChartFrame` directly in the
110
97
  page's chart file, not in a shared kinds directory.
111
98
 
112
- Sparklines are the one exemption from the composition contract (no legend, no tooltip, no
113
- `CartesianChart`) — they still read `VX.*`.
114
-
115
- **Anti-pattern:** a single `<Chart type="…" config={…} />` that switches by kind. Prefer N small
116
- kinds, and if a chart doesn't fit the primitives, **add a kind — don't loosen the primitives.**
99
+ Sparklines are the one exemption (no legend, no tooltip, no `CartesianChart`) — still `VX.*` only.
100
+ **Anti-pattern:** a single `<Chart type="…" config={…} />` switching by kind — add a kind, don't
101
+ loosen the primitives.
117
102
 
118
103
  ## The x axis is categorical, and the API does not say so
119
104
 
120
- `CartesianChart` builds x as a point scale over domain KEYS: N points are N evenly spaced positions
121
- whatever the values behind them. Correct and invisible for a regular grid; wrong for an event-shaped
122
- series, where two consequences are silent — equal spacing means a shared crosshair marks the right
123
- point at a different screen x than a regularly-sampled sibling, and two events at the same instant
124
- collapse onto one position with one of them dropped. `getX` returning a date string reads like a time
125
- axis and is not one. A chart whose x is a measured quantity needs a file declaration today.
126
-
127
- The cursor is likewise **shared page-wide by default** with no provider, resolved domain-aware
128
- (nearest parsed date/number within a chart's own step, not string equality) so a chart that folds its
129
- domain still tracks a sibling's hover. `ChartCursorScope` ISOLATES a subtree — it opts out of
130
- sharing, never into it.
105
+ `CartesianChart` builds x as a point scale over domain KEYS — N evenly spaced positions whatever
106
+ the values behind them. Wrong for an event-shaped series: a shared crosshair marks the wrong screen
107
+ x against a regularly-sampled sibling, and two events at the same instant collapse onto one
108
+ position. `getX` returning a date string reads like a time axis and is not one.
109
+
110
+ The cursor is **shared page-wide by default**, resolved domain-aware (nearest parsed date/number
111
+ within a chart's own step). `ChartCursorScope` ISOLATES a subtree — opts out of sharing, never in.