basalt-ui 1.28.0 → 1.29.1

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 (457) hide show
  1. package/AGENTS.md +12 -19
  2. package/CHANGELOG.md +23 -0
  3. package/MIGRATING.md +171 -7
  4. package/README.md +66 -105
  5. package/agent/rules/basalt-batteries.md +60 -183
  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/agent/templates/query-client.ts.tpl +1 -1
  14. package/configs/oxlint-plugin.js +84 -254
  15. package/configs/oxlint-plugin.test.ts +23 -266
  16. package/configs/oxlint.json +0 -2
  17. package/dist/agent/ai-sdk-transport.d.ts.map +1 -1
  18. package/dist/agent/ai-sdk-transport.js +7 -4
  19. package/dist/agent/coalesce.js +1 -1
  20. package/dist/agent/merge.js +1 -1
  21. package/dist/agent/transport.d.ts +13 -2
  22. package/dist/agent/transport.d.ts.map +1 -1
  23. package/dist/agent/use-agent-thread-runs.d.ts +12 -1
  24. package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
  25. package/dist/agent/use-agent-thread-runs.js +20 -6
  26. package/dist/agent-chat/index.d.ts +1 -1
  27. package/dist/agent-chat/index.d.ts.map +1 -1
  28. package/dist/agent-chat/relative-time.js +1 -1
  29. package/dist/agent-chat/thread-detail-panel.js +1 -1
  30. package/dist/agent-chat/thread-feed-row.d.ts +32 -7
  31. package/dist/agent-chat/thread-feed-row.d.ts.map +1 -1
  32. package/dist/agent-chat/thread-feed-row.js +121 -86
  33. package/dist/agent-chat/thread-feed.d.ts +11 -1
  34. package/dist/agent-chat/thread-feed.d.ts.map +1 -1
  35. package/dist/agent-chat/thread-feed.js +10 -6
  36. package/dist/agent-chat/thread-message.js +1 -1
  37. package/dist/agent-chat/virtualize.d.ts +39 -0
  38. package/dist/agent-chat/virtualize.d.ts.map +1 -1
  39. package/dist/agent-chat/virtualize.js +15 -0
  40. package/dist/charts/index.d.ts +3 -3
  41. package/dist/charts/index.d.ts.map +1 -1
  42. package/dist/charts/index.js +8 -4
  43. package/dist/charts/kinds/BandStrip.js +1 -1
  44. package/dist/charts/kinds/Bars.d.ts +1 -3
  45. package/dist/charts/kinds/Bars.d.ts.map +1 -1
  46. package/dist/charts/kinds/Bars.js +1 -1
  47. package/dist/charts/kinds/DualPanel.d.ts +3 -1
  48. package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
  49. package/dist/charts/kinds/DualPanel.js +1 -1
  50. package/dist/charts/kinds/StackedArea.js +1 -1
  51. package/dist/charts/kinds/ZonedLine.d.ts +1 -3
  52. package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
  53. package/dist/charts/primitives/Axes.d.ts +21 -2
  54. package/dist/charts/primitives/Axes.d.ts.map +1 -1
  55. package/dist/charts/primitives/Axes.js +34 -2
  56. package/dist/charts/primitives/ChartCard.d.ts +20 -4
  57. package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
  58. package/dist/charts/primitives/ChartCard.js +62 -33
  59. package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
  60. package/dist/charts/primitives/ChartFrame.js +8 -1
  61. package/dist/charts/primitives/ChartPending.d.ts +12 -6
  62. package/dist/charts/primitives/ChartPending.d.ts.map +1 -1
  63. package/dist/charts/primitives/ChartPending.js +1 -1
  64. package/dist/charts/utils/format.d.ts +5 -92
  65. package/dist/charts/utils/format.d.ts.map +1 -1
  66. package/dist/charts/utils/format.js +10 -80
  67. package/dist/cli/audit-allows.d.ts +24 -0
  68. package/dist/cli/audit-allows.d.ts.map +1 -0
  69. package/dist/cli/audit-allows.js +240 -0
  70. package/dist/cli/check-theme.d.ts +10 -0
  71. package/dist/cli/check-theme.d.ts.map +1 -0
  72. package/dist/cli/check-theme.js +139 -0
  73. package/dist/cli/doctor.d.ts +47 -0
  74. package/dist/cli/doctor.d.ts.map +1 -0
  75. package/dist/cli/doctor.js +197 -0
  76. package/dist/cli/guard-hook.d.ts +9 -0
  77. package/dist/cli/guard-hook.d.ts.map +1 -0
  78. package/dist/cli/guard-hook.js +93 -0
  79. package/dist/cli/help.d.ts +9 -0
  80. package/dist/cli/help.d.ts.map +1 -0
  81. package/dist/cli/help.js +53 -0
  82. package/dist/cli/index.d.ts +360 -270
  83. package/dist/cli/index.d.ts.map +1 -1
  84. package/dist/cli/index.js +85 -1901
  85. package/dist/cli/init.d.ts +4 -0
  86. package/dist/cli/init.d.ts.map +1 -0
  87. package/dist/cli/init.js +165 -0
  88. package/dist/cli/sync.d.ts +43 -0
  89. package/dist/cli/sync.d.ts.map +1 -0
  90. package/dist/cli/sync.js +206 -0
  91. package/dist/cli/tokens-css.d.ts +51 -0
  92. package/dist/cli/tokens-css.d.ts.map +1 -0
  93. package/dist/cli/tokens-css.js +165 -0
  94. package/dist/commands/define-commands.js +1 -1
  95. package/dist/commands/define-overlays.js +1 -1
  96. package/dist/commands/index.d.ts +2 -1
  97. package/dist/commands/index.d.ts.map +1 -1
  98. package/dist/commands/index.js +6 -0
  99. package/dist/commands/overlays-mount.d.ts +7 -9
  100. package/dist/commands/overlays-mount.d.ts.map +1 -1
  101. package/dist/commands/shell-bridge.d.ts +57 -0
  102. package/dist/commands/shell-bridge.d.ts.map +1 -0
  103. package/dist/commands/shell-bridge.js +32 -0
  104. package/dist/common/errors.d.ts +35 -0
  105. package/dist/common/errors.d.ts.map +1 -1
  106. package/dist/common/errors.js +29 -1
  107. package/dist/common/index.d.ts +2 -0
  108. package/dist/common/index.d.ts.map +1 -1
  109. package/dist/common/index.js +6 -0
  110. package/dist/common/is-dev.d.ts.map +1 -0
  111. package/dist/{motion/index.d.ts → common/motion.d.ts} +1 -1
  112. package/dist/common/motion.d.ts.map +1 -0
  113. package/dist/common/props.d.ts +1 -1
  114. package/dist/common/props.d.ts.map +1 -1
  115. package/dist/common/validate.js +1 -1
  116. package/dist/content/markdown.d.ts +3 -4
  117. package/dist/content/markdown.d.ts.map +1 -1
  118. package/dist/content/markdown.js +1 -4
  119. package/dist/controls/actions.d.ts +1 -1
  120. package/dist/controls/actions.d.ts.map +1 -1
  121. package/dist/controls/panel-row.js +1 -1
  122. package/dist/controls/range-filter.js +1 -1
  123. package/dist/dashboard/delta-badge.d.ts +18 -10
  124. package/dist/dashboard/delta-badge.d.ts.map +1 -1
  125. package/dist/dashboard/delta-badge.js +33 -1
  126. package/dist/dashboard/empty-state.d.ts +1 -4
  127. package/dist/dashboard/empty-state.d.ts.map +1 -1
  128. package/dist/dashboard/empty-state.js +1 -2
  129. package/dist/dashboard/index.d.ts +4 -2
  130. package/dist/dashboard/index.d.ts.map +1 -1
  131. package/dist/dashboard/index.js +7 -1
  132. package/dist/dashboard/query-state.d.ts +4 -13
  133. package/dist/dashboard/query-state.d.ts.map +1 -1
  134. package/dist/dashboard/query-state.js +4 -7
  135. package/dist/{section → dashboard}/section.d.ts +2 -2
  136. package/dist/{section → dashboard}/section.d.ts.map +1 -1
  137. package/dist/{section → dashboard}/section.js +2 -2
  138. package/dist/dashboard/settings-section.d.ts +1 -1
  139. package/dist/dashboard/settings-section.d.ts.map +1 -1
  140. package/dist/dashboard/settings-section.js +1 -1
  141. package/dist/dashboard/stat-card.d.ts +1 -1
  142. package/dist/dashboard/stat-card.d.ts.map +1 -1
  143. package/dist/dashboard/stat-card.js +1 -1
  144. package/dist/dashboard/widget-header.d.ts.map +1 -0
  145. package/dist/data/data-table.js +2 -2
  146. package/dist/data/table.d.ts +1 -1
  147. package/dist/data/table.d.ts.map +1 -1
  148. package/dist/data/table.js +2 -16
  149. package/dist/data/virtual.d.ts +3 -3
  150. package/dist/dev-dock.d.ts +26 -0
  151. package/dist/dev-dock.d.ts.map +1 -0
  152. package/dist/dev-dock.js +85 -0
  153. package/dist/format/date.d.ts +62 -0
  154. package/dist/format/date.d.ts.map +1 -0
  155. package/dist/format/date.js +106 -0
  156. package/dist/format/index.d.ts +39 -0
  157. package/dist/format/index.d.ts.map +1 -0
  158. package/dist/format/index.js +54 -0
  159. package/dist/format/measure.d.ts +15 -0
  160. package/dist/format/measure.d.ts.map +1 -0
  161. package/dist/format/measure.js +16 -0
  162. package/dist/format/number.d.ts +69 -0
  163. package/dist/format/number.d.ts.map +1 -0
  164. package/dist/format/number.js +62 -0
  165. package/dist/format/shared.d.ts +27 -0
  166. package/dist/format/shared.d.ts.map +1 -0
  167. package/dist/format/shared.js +29 -0
  168. package/dist/forms/field.d.ts +4 -20
  169. package/dist/forms/field.d.ts.map +1 -1
  170. package/dist/forms/field.js +0 -4
  171. package/dist/forms/form-layout.d.ts +1 -1
  172. package/dist/forms/form-layout.d.ts.map +1 -1
  173. package/dist/forms/form-layout.js +1 -1
  174. package/dist/forms/index.d.ts +1 -1
  175. package/dist/forms/index.d.ts.map +1 -1
  176. package/dist/forms/index.js +1 -2
  177. package/dist/forms/use-form-submit.d.ts +1 -1
  178. package/dist/forms/use-form-submit.js +1 -1
  179. package/dist/guard/index.d.ts +2 -9
  180. package/dist/guard/index.d.ts.map +1 -1
  181. package/dist/guard/index.js +53 -32
  182. package/dist/guard/types.d.ts +8 -10
  183. package/dist/guard/types.d.ts.map +1 -1
  184. package/dist/index.d.ts +16 -11
  185. package/dist/index.d.ts.map +1 -1
  186. package/dist/index.js +23 -29
  187. package/dist/notifications/index.d.ts +5 -5
  188. package/dist/notifications/index.d.ts.map +1 -1
  189. package/dist/notifications/index.js +0 -2
  190. package/dist/notifications/mount-guard.d.ts.map +1 -1
  191. package/dist/notifications/mount-guard.js +2 -2
  192. package/dist/provider/connectivity-indicator.d.ts.map +1 -0
  193. package/dist/{connectivity → provider}/connectivity-provider.d.ts +1 -1
  194. package/dist/provider/connectivity-provider.d.ts.map +1 -0
  195. package/dist/{connectivity/types.d.ts → provider/connectivity-types.d.ts} +1 -1
  196. package/dist/provider/connectivity-types.d.ts.map +1 -0
  197. package/dist/provider/error-fallback.module.css +21 -0
  198. package/dist/provider/error-fallback.module.css.d.ts +9 -0
  199. package/dist/provider/index.d.ts +20 -36
  200. package/dist/provider/index.d.ts.map +1 -1
  201. package/dist/provider/index.js +42 -32
  202. package/dist/provider/lab-theme.js +1 -1
  203. package/dist/provider/use-connectivity.d.ts +3 -0
  204. package/dist/provider/use-connectivity.d.ts.map +1 -0
  205. package/dist/query-client.d.ts +67 -0
  206. package/dist/query-client.d.ts.map +1 -0
  207. package/dist/query-client.js +35 -0
  208. package/dist/{query/devtools.d.ts → query-devtools.d.ts} +2 -2
  209. package/dist/query-devtools.d.ts.map +1 -0
  210. package/dist/register.d.ts +17 -3
  211. package/dist/register.d.ts.map +1 -1
  212. package/dist/router-tanstack/index.d.ts +4 -7
  213. package/dist/router-tanstack/index.d.ts.map +1 -1
  214. package/dist/router-tanstack/index.js +1 -9
  215. package/dist/router-tanstack/nav.d.ts +1 -1
  216. package/dist/router-tanstack/nav.d.ts.map +1 -1
  217. package/dist/router-tanstack/search-store.d.ts +3 -6
  218. package/dist/router-tanstack/search-store.d.ts.map +1 -1
  219. package/dist/router-tanstack/use-nav.d.ts +1 -1
  220. package/dist/router-tanstack/use-nav.d.ts.map +1 -1
  221. package/dist/shell/app-breadcrumbs.d.ts +1 -1
  222. package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
  223. package/dist/shell/app-main.module.css +11 -0
  224. package/dist/shell/app-mobile-nav.d.ts +1 -1
  225. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  226. package/dist/shell/app-mobile-nav.js +24 -2
  227. package/dist/shell/app-sidebar.d.ts +1 -1
  228. package/dist/shell/app-sidebar.d.ts.map +1 -1
  229. package/dist/shell/app-sidebar.js +31 -2
  230. package/dist/shell/app-sidebar.module.css +10 -0
  231. package/dist/shell/index.d.ts +10 -4
  232. package/dist/shell/index.d.ts.map +1 -1
  233. package/dist/shell/index.js +13 -17
  234. package/dist/shell/mobile-nav-model.d.ts +1 -1
  235. package/dist/shell/mobile-nav-model.d.ts.map +1 -1
  236. package/dist/{nav/types.d.ts → shell/nav-types.d.ts} +1 -1
  237. package/dist/shell/nav-types.d.ts.map +1 -0
  238. package/dist/shell/page-aside.d.ts.map +1 -1
  239. package/dist/shell/page-aside.js +2 -29
  240. package/dist/shell/page-title.d.ts +14 -0
  241. package/dist/shell/page-title.d.ts.map +1 -0
  242. package/dist/shell/page-title.js +25 -0
  243. package/dist/shell/page-title.module.css +33 -0
  244. package/dist/shell/page-title.module.css.d.ts +11 -0
  245. package/dist/shell/sidebar-block-model.d.ts +1 -1
  246. package/dist/shell/sidebar-block-model.d.ts.map +1 -1
  247. package/dist/shell/sidebar-blocks.d.ts +1 -1
  248. package/dist/shell/sidebar-blocks.d.ts.map +1 -1
  249. package/dist/shell/use-breakpoint.d.ts +11 -0
  250. package/dist/shell/use-breakpoint.d.ts.map +1 -0
  251. package/dist/shell/use-breakpoint.js +33 -0
  252. package/dist/state/fields.d.ts +3 -19
  253. package/dist/state/fields.d.ts.map +1 -1
  254. package/dist/state/fields.js +6 -16
  255. package/dist/styles.css +36 -0
  256. package/dist/surfaces.d.ts +18 -41
  257. package/dist/surfaces.d.ts.map +1 -1
  258. package/dist/surfaces.js +48 -60
  259. package/dist/theme/index.d.ts.map +1 -1
  260. package/dist/{theme-toggle/index.d.ts → theme/theme-toggle.d.ts} +1 -1
  261. package/dist/theme/theme-toggle.d.ts.map +1 -0
  262. package/dist/{theme-toggle/index.js → theme/theme-toggle.js} +1 -1
  263. package/dist/tokens/index.d.ts +1 -0
  264. package/dist/tokens/index.d.ts.map +1 -1
  265. package/dist/tokens/index.js +10 -0
  266. package/dist/tokens/palette.d.ts +12 -0
  267. package/dist/tokens/palette.d.ts.map +1 -1
  268. package/dist/tokens/palette.js +13 -1
  269. package/dist/tokens.css +1 -0
  270. package/llms.txt +9 -24
  271. package/package.json +12 -16
  272. package/src/agent/ai-sdk-transport.ts +11 -4
  273. package/src/agent/coalesce.ts +1 -1
  274. package/src/agent/merge.ts +1 -1
  275. package/src/agent/transport.ts +15 -2
  276. package/src/agent/use-agent-thread-runs.ts +65 -15
  277. package/src/agent-chat/index.ts +6 -4
  278. package/src/agent-chat/relative-time.ts +1 -1
  279. package/src/agent-chat/thread-detail-panel.tsx +1 -1
  280. package/src/agent-chat/thread-feed-row.tsx +105 -30
  281. package/src/agent-chat/thread-feed.tsx +51 -25
  282. package/src/agent-chat/thread-message.tsx +1 -1
  283. package/src/agent-chat/virtualize.ts +48 -0
  284. package/src/charts/index.ts +7 -10
  285. package/src/charts/kinds/BandStrip.tsx +1 -1
  286. package/src/charts/kinds/Bars.tsx +2 -5
  287. package/src/charts/kinds/DualPanel.tsx +4 -3
  288. package/src/charts/kinds/StackedArea.tsx +1 -1
  289. package/src/charts/kinds/ZonedLine.tsx +1 -4
  290. package/src/charts/primitives/Axes.tsx +49 -0
  291. package/src/charts/primitives/ChartCard.tsx +49 -6
  292. package/src/charts/primitives/ChartFrame.tsx +5 -1
  293. package/src/charts/primitives/ChartPending.tsx +14 -8
  294. package/src/charts/utils/format.ts +17 -186
  295. package/src/cli/audit-allows.ts +371 -0
  296. package/src/cli/check-theme.ts +197 -0
  297. package/src/cli/doctor.ts +331 -0
  298. package/src/cli/guard-hook.ts +115 -0
  299. package/src/cli/help.ts +101 -0
  300. package/src/cli/index.ts +269 -3439
  301. package/src/cli/init.ts +247 -0
  302. package/src/cli/sync.ts +324 -0
  303. package/src/cli/tokens-css.ts +252 -0
  304. package/src/commands/define-commands.ts +1 -1
  305. package/src/commands/define-overlays.ts +1 -1
  306. package/src/commands/index.ts +10 -1
  307. package/src/commands/overlays-mount.tsx +8 -10
  308. package/src/commands/shell-bridge.ts +86 -0
  309. package/src/common/errors.ts +70 -0
  310. package/src/common/index.ts +2 -0
  311. package/src/common/props.ts +1 -1
  312. package/src/common/validate.ts +1 -1
  313. package/src/content/markdown.tsx +4 -8
  314. package/src/controls/actions.tsx +1 -1
  315. package/src/controls/panel-row.tsx +1 -1
  316. package/src/controls/range-filter.tsx +1 -1
  317. package/src/dashboard/delta-badge.tsx +74 -7
  318. package/src/dashboard/empty-state.tsx +2 -6
  319. package/src/dashboard/index.ts +11 -2
  320. package/src/dashboard/query-state.tsx +5 -18
  321. package/src/{section → dashboard}/section.tsx +4 -4
  322. package/src/dashboard/settings-section.tsx +2 -2
  323. package/src/dashboard/stat-card.tsx +2 -2
  324. package/src/data/data-table.tsx +3 -3
  325. package/src/data/table.ts +6 -25
  326. package/src/data/virtual-list.tsx +1 -1
  327. package/src/data/virtual.ts +3 -3
  328. package/src/dev-dock.tsx +162 -0
  329. package/src/format/date.ts +184 -0
  330. package/src/format/index.ts +69 -0
  331. package/src/format/measure.ts +31 -0
  332. package/src/format/number.ts +129 -0
  333. package/src/format/shared.ts +63 -0
  334. package/src/forms/field.ts +4 -24
  335. package/src/forms/form-layout.tsx +2 -2
  336. package/src/forms/index.ts +1 -2
  337. package/src/forms/use-form-submit.ts +2 -2
  338. package/src/guard/index.ts +80 -69
  339. package/src/guard/types.ts +7 -10
  340. package/src/index.ts +38 -29
  341. package/src/notifications/index.ts +5 -7
  342. package/src/notifications/mount-guard.ts +7 -9
  343. package/src/{connectivity → provider}/connectivity-indicator.tsx +1 -1
  344. package/src/{connectivity → provider}/connectivity-provider.tsx +1 -1
  345. package/src/provider/error-fallback.module.css +21 -0
  346. package/src/provider/error-fallback.module.css.d.ts +9 -0
  347. package/src/provider/index.tsx +71 -71
  348. package/src/provider/lab-theme.ts +1 -1
  349. package/src/{connectivity → provider}/use-connectivity.ts +3 -3
  350. package/src/query-client.ts +105 -0
  351. package/src/{query/devtools.tsx → query-devtools.tsx} +5 -4
  352. package/src/register.ts +20 -4
  353. package/src/router-tanstack/index.ts +4 -15
  354. package/src/router-tanstack/nav.ts +1 -1
  355. package/src/router-tanstack/search-store.ts +3 -4
  356. package/src/router-tanstack/use-nav.ts +1 -1
  357. package/src/shell/app-breadcrumbs.tsx +1 -1
  358. package/src/shell/app-main.module.css +11 -0
  359. package/src/shell/app-mobile-nav.tsx +28 -1
  360. package/src/shell/app-sidebar.module.css +10 -0
  361. package/src/shell/app-sidebar.tsx +31 -2
  362. package/src/shell/index.tsx +23 -28
  363. package/src/shell/mobile-nav-model.ts +1 -1
  364. package/src/shell/page-aside.tsx +2 -52
  365. package/src/shell/page-title.module.css +33 -0
  366. package/src/shell/page-title.module.css.d.ts +11 -0
  367. package/src/shell/page-title.tsx +57 -0
  368. package/src/shell/sidebar-block-model.ts +1 -1
  369. package/src/shell/sidebar-blocks.tsx +1 -1
  370. package/src/shell/use-breakpoint.ts +73 -0
  371. package/src/state/fields.ts +7 -43
  372. package/src/styles.css +36 -0
  373. package/src/surfaces.ts +47 -61
  374. package/src/theme/index.ts +7 -0
  375. package/src/{theme-toggle/index.tsx → theme/theme-toggle.tsx} +1 -1
  376. package/src/tokens/index.ts +10 -0
  377. package/src/tokens/palette.ts +12 -0
  378. package/dist/connectivity/connectivity-indicator.d.ts.map +0 -1
  379. package/dist/connectivity/connectivity-provider.d.ts.map +0 -1
  380. package/dist/connectivity/index.d.ts +0 -14
  381. package/dist/connectivity/index.d.ts.map +0 -1
  382. package/dist/connectivity/index.js +0 -8
  383. package/dist/connectivity/types.d.ts.map +0 -1
  384. package/dist/connectivity/use-connectivity.d.ts +0 -3
  385. package/dist/connectivity/use-connectivity.d.ts.map +0 -1
  386. package/dist/data/index.d.ts +0 -38
  387. package/dist/data/index.d.ts.map +0 -1
  388. package/dist/data/index.js +0 -24
  389. package/dist/motion/index.d.ts.map +0 -1
  390. package/dist/nav/types.d.ts.map +0 -1
  391. package/dist/notifications/overlay.d.ts +0 -20
  392. package/dist/notifications/overlay.d.ts.map +0 -1
  393. package/dist/notifications/overlay.js +0 -14
  394. package/dist/query/devtools.d.ts.map +0 -1
  395. package/dist/query/error-message.d.ts +0 -41
  396. package/dist/query/error-message.d.ts.map +0 -1
  397. package/dist/query/error-message.js +0 -30
  398. package/dist/query/index.d.ts +0 -47
  399. package/dist/query/index.d.ts.map +0 -1
  400. package/dist/query/index.js +0 -56
  401. package/dist/router-tanstack/field.d.ts +0 -14
  402. package/dist/router-tanstack/field.d.ts.map +0 -1
  403. package/dist/router-tanstack/field.js +0 -4
  404. package/dist/router-tanstack/multi-search-param-store.d.ts +0 -43
  405. package/dist/router-tanstack/multi-search-param-store.d.ts.map +0 -1
  406. package/dist/router-tanstack/multi-search-param-store.js +0 -30
  407. package/dist/router-tanstack/search-param-store.d.ts +0 -42
  408. package/dist/router-tanstack/search-param-store.d.ts.map +0 -1
  409. package/dist/router-tanstack/search-param-store.js +0 -25
  410. package/dist/section/index.d.ts +0 -2
  411. package/dist/section/index.d.ts.map +0 -1
  412. package/dist/section/index.js +0 -4
  413. package/dist/theme-toggle/index.d.ts.map +0 -1
  414. package/dist/utils/is-dev.d.ts.map +0 -1
  415. package/dist/widget-header/delta-badge.d.ts +0 -19
  416. package/dist/widget-header/delta-badge.d.ts.map +0 -1
  417. package/dist/widget-header/delta-badge.js +0 -36
  418. package/dist/widget-header/index.d.ts +0 -3
  419. package/dist/widget-header/index.d.ts.map +0 -1
  420. package/dist/widget-header/index.js +0 -8
  421. package/dist/widget-header/widget-header.d.ts.map +0 -1
  422. package/src/connectivity/index.ts +0 -19
  423. package/src/data/index.ts +0 -69
  424. package/src/notifications/overlay.tsx +0 -66
  425. package/src/query/error-message.ts +0 -70
  426. package/src/query/index.ts +0 -97
  427. package/src/router-tanstack/field.ts +0 -36
  428. package/src/router-tanstack/multi-search-param-store.ts +0 -106
  429. package/src/router-tanstack/search-param-store.ts +0 -100
  430. package/src/section/index.ts +0 -1
  431. package/src/widget-header/delta-badge.tsx +0 -77
  432. package/src/widget-header/index.ts +0 -10
  433. /package/dist/{utils → common}/is-dev.d.ts +0 -0
  434. /package/dist/{utils → common}/is-dev.js +0 -0
  435. /package/dist/{motion/index.js → common/motion.js} +0 -0
  436. /package/dist/{section → dashboard}/section.module.css +0 -0
  437. /package/dist/{section → dashboard}/section.module.css.d.ts +0 -0
  438. /package/dist/{widget-header → dashboard}/widget-header.d.ts +0 -0
  439. /package/dist/{widget-header → dashboard}/widget-header.js +0 -0
  440. /package/dist/{widget-header → dashboard}/widget-header.module.css +0 -0
  441. /package/dist/{widget-header → dashboard}/widget-header.module.css.d.ts +0 -0
  442. /package/dist/{connectivity → provider}/connectivity-indicator.d.ts +0 -0
  443. /package/dist/{connectivity → provider}/connectivity-indicator.js +0 -0
  444. /package/dist/{connectivity → provider}/connectivity-provider.js +0 -0
  445. /package/dist/{connectivity/types.js → provider/connectivity-types.js} +0 -0
  446. /package/dist/{connectivity → provider}/use-connectivity.js +0 -0
  447. /package/dist/{query/devtools.js → query-devtools.js} +0 -0
  448. /package/dist/{nav/types.js → shell/nav-types.js} +0 -0
  449. /package/src/{utils → common}/is-dev.ts +0 -0
  450. /package/src/{motion/index.ts → common/motion.ts} +0 -0
  451. /package/src/{section → dashboard}/section.module.css +0 -0
  452. /package/src/{section → dashboard}/section.module.css.d.ts +0 -0
  453. /package/src/{widget-header → dashboard}/widget-header.module.css +0 -0
  454. /package/src/{widget-header → dashboard}/widget-header.module.css.d.ts +0 -0
  455. /package/src/{widget-header → dashboard}/widget-header.tsx +0 -0
  456. /package/src/{connectivity/types.ts → provider/connectivity-types.ts} +0 -0
  457. /package/src/{nav/types.ts → shell/nav-types.ts} +0 -0
@@ -0,0 +1,252 @@
1
+ /**
2
+ * `tokens:css` / `fonts:css` — split out of `src/cli/index.ts` (C2) so the dispatcher file holds
3
+ * routing only. Imports its shared plumbing (`readFrameworkVersion`, `packageRoot`, `readSource`,
4
+ * `readIfExists`) back from `./index`, this package's shared CLI helpers.
5
+ */
6
+ import { mkdirSync, writeFileSync } from 'node:fs'
7
+ import { dirname, isAbsolute, relative, resolve } from 'node:path'
8
+
9
+ import { GENERATED_HEADER_LINE } from '../guard'
10
+ import { buildPaletteCss } from '../tokens'
11
+ import { packageRoot, readFrameworkVersion, readIfExists, readSource } from './index'
12
+
13
+ /** Read the value that follows a `--flag` in an argv slice; `undefined` when absent or terminal. */
14
+ function flagValue(flags: string[], name: string): string | undefined {
15
+ const i = flags.indexOf(name)
16
+ if (i === -1) return undefined
17
+ const value = flags[i + 1]
18
+ return value === undefined || value.startsWith('--') ? undefined : value
19
+ }
20
+
21
+ /**
22
+ * The generated-file header: the canonical marker line (imported from the guard, which owns it),
23
+ * then the version + invocation that produced the file.
24
+ *
25
+ * Both lines are load-bearing, not decoration — `checkSource` only exempts a stylesheet whose line
26
+ * 1 is the marker verbatim AND whose line 2 parses as this provenance line. Reword either half and
27
+ * basalt starts reporting its own output again (116 findings in rollhook's token sheet).
28
+ */
29
+ function generatedHeader(version: string, command: string, flags: string[]): string {
30
+ const shown = flags.filter((f) => f !== '--check')
31
+ const invocation = shown.length === 0 ? command : `${command} ${shown.join(' ')}`
32
+ return `${GENERATED_HEADER_LINE}\n/* basalt-ui ${version} — \`basalt-ui ${invocation}\` */\n`
33
+ }
34
+
35
+ /**
36
+ * Space- and number-normalize the argument list of a legacy color function so the emitted file
37
+ * survives a normal repo's formatter.
38
+ *
39
+ * This is FORMATTING ONLY and deliberately the CLI's single deviation from "print exactly what
40
+ * `buildPaletteCss` returned": `rgba(255,255,255,0.6)` and `rgba(255, 255, 255, 0.6)` are the same
41
+ * colour, the shipped palette emits the first form on the dark side and the second on the light
42
+ * side, and a consumer committing the output ate a lint-ignore entry for the difference. The token
43
+ * VALUES are untouched — nothing here can change what basalt's tokens are.
44
+ *
45
+ * Trailing zeros go the same way and for the same reason: prettier rewrites `0.10` → `0.1`, so the
46
+ * two `rgba(28, 25, 23, 0.10)` alphas in the light shadow tokens were the whole reason the one
47
+ * framework-free consumer still could not lint its committed sheet — `--fix` and `tokens:css
48
+ * --check` disagreed forever, and the lint-ignore entry survived two releases. Only a bare decimal
49
+ * literal is touched (never a `%`, a `var()` or a unit), and `0.10`/`0.1` are the same number.
50
+ *
51
+ * Exported for tests, so the expectation can be computed rather than restated.
52
+ */
53
+ export function normalizeColorFunctions(css: string): string {
54
+ return css.replace(/\b(rgba?|hsla?)\(([^()]*)\)/g, (_match, fn: string, args: string) => {
55
+ const parts = args.split(',').map((part) => normalizeCssNumber(part.trim()))
56
+ return `${fn}(${parts.join(', ')})`
57
+ })
58
+ }
59
+
60
+ /** `0.10` → `0.1`, `1.0` → `1`; anything that is not a bare decimal literal is returned verbatim. */
61
+ function normalizeCssNumber(arg: string): string {
62
+ if (!/^\d+\.\d+$/.test(arg)) return arg
63
+ return arg.replace(/\.?0+$/, '')
64
+ }
65
+
66
+ /** A scheme-class emission uses this sentinel attribute, then rewrites it to a class selector. */
67
+ const SCHEME_CLASS_SENTINEL = 'data-basalt-scheme-class'
68
+
69
+ /**
70
+ * Write (or drift-check) a generated stylesheet. `--check` makes no writes and exits 1 when the
71
+ * file on disk differs from what would be emitted — the CI gate for a committed `tokens:css`
72
+ * artifact, mirroring `sync --check`. Without `--out` the content goes to stdout unchanged.
73
+ */
74
+ function emitGeneratedCss(content: string, flags: string[], cwd: string, command: string): number {
75
+ const out = flagValue(flags, '--out')
76
+ const check = flags.includes('--check')
77
+ if (out === undefined) {
78
+ if (check) {
79
+ console.error(
80
+ `${command}: --check needs --out <path> — there is nothing to compare stdout to.`,
81
+ )
82
+ return 1
83
+ }
84
+ process.stdout.write(content)
85
+ return 0
86
+ }
87
+ const target = isAbsolute(out) ? out : resolve(cwd, out)
88
+ const shown = relative(cwd, target) || target
89
+ if (check) {
90
+ const onDisk = readIfExists(target)
91
+ if (onDisk === null) {
92
+ console.error(
93
+ `✖ ${command} --check: ${shown} does not exist — run without --check to write it.`,
94
+ )
95
+ return 1
96
+ }
97
+ if (onDisk === content) {
98
+ console.log(`✓ ${command} --check: ${shown} is up to date.`)
99
+ return 0
100
+ }
101
+ const diskLines = onDisk.split('\n')
102
+ const wantLines = content.split('\n')
103
+ console.error(
104
+ `✖ ${command} --check: ${shown} differs from what \`basalt-ui ${command}\` emits today ` +
105
+ `(on disk ${diskLines.length} lines, emitted ${wantLines.length}) — re-run the same ` +
106
+ 'command without --check and commit the result. A basalt-ui release moves the ' +
107
+ '`@generated` header line, so this is byte-exact — expect a no-op commit on every upgrade ' +
108
+ 'that ships a committed generated sheet.',
109
+ )
110
+ return 1
111
+ }
112
+ mkdirSync(dirname(target), { recursive: true })
113
+ writeFileSync(target, content)
114
+ console.log(`wrote ${content.split('\n').length} lines → ${shown}`)
115
+ return 0
116
+ }
117
+
118
+ /**
119
+ * tokens:css — emit the `--vx-*` stylesheet, optionally retargeted.
120
+ *
121
+ * The escape hatch from installing anything: a static site runs this once (`bunx basalt-ui
122
+ * tokens:css --selector-attribute data-theme --out src/tokens.css`) and consumes basalt's token
123
+ * system as plain CSS, with no package in its dependency tree at all. `basalt-ui/tokens.css` is the
124
+ * same artifact for a consumer that does install; this command exists for the ones that shouldn't
125
+ * have to just to change a selector.
126
+ *
127
+ * It parses flags and calls `buildPaletteCss` for the token VALUES — the CLI and the API must not
128
+ * be able to disagree about what basalt's tokens are. What it adds is strictly file framing for an
129
+ * artifact a consumer COMMITS: the `@generated` header (the guard's skip marker), a trailing
130
+ * newline, and `rgba()` argument spacing. All three were reported by the one framework-free
131
+ * consumer as the reasons the output could not simply be committed.
132
+ */
133
+ export function tokensCss(flags: string[], cwd: string = process.cwd()): number {
134
+ const attribute = flagValue(flags, '--selector-attribute')
135
+ const schemeClass = flagValue(flags, '--selector-class')
136
+ const lightClass = flagValue(flags, '--light-class')
137
+ const darkValue = flagValue(flags, '--dark-value')
138
+ const lightValue = flagValue(flags, '--light-value')
139
+ const defaultScheme = flagValue(flags, '--default-scheme')
140
+
141
+ const only = flagValue(flags, '--only')
142
+
143
+ if (defaultScheme !== undefined && !['dark', 'light', 'none'].includes(defaultScheme)) {
144
+ console.error(
145
+ `tokens:css: --default-scheme must be dark, light or none (got '${defaultScheme}')`,
146
+ )
147
+ return 1
148
+ }
149
+ if (only !== undefined && !['core', 'all'].includes(only)) {
150
+ console.error(`tokens:css: --only must be core or all (got '${only}')`)
151
+ return 1
152
+ }
153
+ if (schemeClass !== undefined && attribute !== undefined) {
154
+ console.error(
155
+ 'tokens:css: --selector-class and --selector-attribute are alternatives — pass one.',
156
+ )
157
+ return 1
158
+ }
159
+ if (schemeClass !== undefined && /[^\w-]/.test(schemeClass)) {
160
+ console.error(
161
+ `tokens:css: --selector-class must be a plain CSS class name (got '${schemeClass}')`,
162
+ )
163
+ return 1
164
+ }
165
+ if (lightClass !== undefined && /[^\w-]/.test(lightClass)) {
166
+ console.error(`tokens:css: --light-class must be a plain CSS class name (got '${lightClass}')`)
167
+ return 1
168
+ }
169
+
170
+ // A class selector is Tailwind's universal dark convention (`<html class="dark">`) and was
171
+ // reachable before only by parking dark on the bare `:root` via --default-scheme. `buildPaletteCss`
172
+ // emits attribute selectors, so the class form is produced by emitting against a sentinel
173
+ // attribute this CLI chose itself and rewriting exactly those selectors — a rewrite of strings
174
+ // the command generated deterministically, never of a token value.
175
+ const resolvedDarkValue = schemeClass !== undefined ? schemeClass : darkValue
176
+ const resolvedLightValue = schemeClass !== undefined ? (lightClass ?? 'light') : lightValue
177
+ const resolvedAttribute = schemeClass !== undefined ? SCHEME_CLASS_SENTINEL : attribute
178
+
179
+ const scheme = {
180
+ ...(resolvedAttribute === undefined ? {} : { attribute: resolvedAttribute }),
181
+ ...(resolvedDarkValue === undefined ? {} : { darkValue: resolvedDarkValue }),
182
+ ...(resolvedLightValue === undefined ? {} : { lightValue: resolvedLightValue }),
183
+ }
184
+
185
+ let css = buildPaletteCss({
186
+ ...(Object.keys(scheme).length === 0 ? {} : { scheme }),
187
+ ...(defaultScheme === undefined
188
+ ? {}
189
+ : { defaultScheme: defaultScheme as 'dark' | 'light' | 'none' }),
190
+ ...(flags.includes('--media-fallback') ? { mediaFallback: true } : {}),
191
+ ...(only === undefined ? {} : { only: only as 'core' | 'all' }),
192
+ // The tokens-only consumer is exactly the one who writes these names by hand, so they are also
193
+ // the one who wants the deprecated camelCase aliases gone — and this command is their only
194
+ // entry point. Without the flag the CLI and the API could disagree about what basalt's tokens
195
+ // are, which is the one thing this command is documented never to allow.
196
+ ...(flags.includes('--no-legacy-aliases') ? { legacyAliases: false } : {}),
197
+ })
198
+
199
+ if (schemeClass !== undefined) {
200
+ css = css.replace(
201
+ new RegExp(`\\[${SCHEME_CLASS_SENTINEL}='([\\w-]+)'\\]`, 'g'),
202
+ (_match, value: string) => `.${value}`,
203
+ )
204
+ }
205
+
206
+ const version = readFrameworkVersion(packageRoot())
207
+ const content = `${generatedHeader(version, 'tokens:css', flags)}${normalizeColorFunctions(css)}\n`
208
+ return emitGeneratedCss(content, flags, cwd, 'tokens:css')
209
+ }
210
+
211
+ /** Every `--basalt-font-*` declaration in the shipped stylesheet, in source order. */
212
+ function readShippedFontDecls(pkgRoot: string): { name: string; value: string }[] {
213
+ const css = readSource(pkgRoot, 'dist/styles.css') ?? readSource(pkgRoot, 'src/styles.css')
214
+ if (css === null) return []
215
+ const decls: { name: string; value: string }[] = []
216
+ for (const match of css.matchAll(/(--basalt-font[\w-]*)\s*:\s*([^;]+);/g)) {
217
+ decls.push({
218
+ name: match[1] as string,
219
+ value: (match[2] as string).replace(/\s+/g, ' ').trim(),
220
+ })
221
+ }
222
+ return decls
223
+ }
224
+
225
+ /**
226
+ * fonts:css — emit the shipped `--basalt-font-*` stacks as plain CSS.
227
+ *
228
+ * The typeface half of the framework-free route, which had no supported path at all: `tokens.css`
229
+ * emits no font vars, `styles.css` is the one place the defaults live and a framework-free consumer
230
+ * is told not to import it, and `buildFontsCss` returns `''` unless the caller already knows the
231
+ * stacks. The one non-Mantine consumer lost its font identity entirely and hardcoded the stacks —
232
+ * which the theme guard then flagged as `raw-font-family`.
233
+ *
234
+ * The declarations are READ from the shipped stylesheet rather than restated here, so this command
235
+ * and `basalt-ui/styles.css` can never name different typefaces. Note the fonts themselves are
236
+ * `@fontsource-variable/*` packages: without them the stacks fall through to their own system
237
+ * fallbacks, which is the intended framework-free behaviour.
238
+ */
239
+ export function fontsCss(flags: string[], cwd: string = process.cwd()): number {
240
+ const pkgRoot = packageRoot()
241
+ const decls = readShippedFontDecls(pkgRoot)
242
+ if (decls.length === 0) {
243
+ console.error(
244
+ 'fonts:css: could not read the shipped font stacks from styles.css — this is a packaging ' +
245
+ 'bug in basalt-ui, not a config error.',
246
+ )
247
+ return 1
248
+ }
249
+ const body = `:root {\n${decls.map((d) => ` ${d.name}: ${d.value};`).join('\n')}\n}\n`
250
+ const header = generatedHeader(readFrameworkVersion(pkgRoot), 'fonts:css', flags)
251
+ return emitGeneratedCss(`${header}${body}`, flags, cwd, 'fonts:css')
252
+ }
@@ -24,7 +24,7 @@
24
24
  */
25
25
  import type { ReactNode } from 'react'
26
26
  import type { Slot } from '../register'
27
- import { isDev } from '../utils/is-dev'
27
+ import { isDev } from '../common/is-dev'
28
28
 
29
29
  // ── Context ───────────────────────────────────────────────────────────────────
30
30
 
@@ -29,7 +29,7 @@ import type { ReactNode } from 'react'
29
29
  import { useEffect } from 'react'
30
30
  import { missingLayer } from '../common/errors'
31
31
  import type { Slot } from '../register'
32
- import { isDev } from '../utils/is-dev'
32
+ import { isDev } from '../common/is-dev'
33
33
 
34
34
  // ── Lazy @mantine/modals resolution ───────────────────────────────────────────
35
35
  // @mantine/modals is an OPTIONAL peer — never imported at module evaluation time, so importing
@@ -19,7 +19,7 @@
19
19
  * interface BasaltRegister { commands: typeof COMMANDS }
20
20
  * }
21
21
  *
22
- * // main.tsx — mount composable overlays (replaces <BasaltNotifications />):
22
+ * // main.tsx — mount composable overlays:
23
23
  * import { BasaltOverlays } from 'basalt-ui/commands'
24
24
  * import '@mantine/spotlight/styles.layer.css'
25
25
  * <BasaltProvider><BasaltOverlays><App /></BasaltOverlays></BasaltProvider>
@@ -79,6 +79,15 @@ export {
79
79
  type HotkeyBinding,
80
80
  } from './projectors'
81
81
 
82
+ // ── Imperative shell handles (setColorScheme/toggleSidebar) ──────────────────
83
+ export {
84
+ setColorScheme,
85
+ toggleSidebar,
86
+ type ColorScheme,
87
+ type ColorSchemeSetter,
88
+ type SidebarToggle,
89
+ } from './shell-bridge'
90
+
82
91
  // ── Shortcut format utils ─────────────────────────────────────────────────────
83
92
  export { parseShortcut, detectMac, formatShortcutDisplay } from './shortcut-format'
84
93
 
@@ -2,10 +2,10 @@
2
2
  * overlays-mount — composable overlay mount for basalt-ui apps.
3
3
  *
4
4
  * `BasaltOverlays` bundles ModalsProvider, Spotlight, and Notifications into a single mount point.
5
- * Put it inside BasaltProvider, before the router. It replaces the standalone
6
- * `<BasaltNotifications />` from `basalt-ui/notifications` — do NOT mount both
7
- * `<BasaltOverlays notifications />` and `<BasaltNotifications />` in the same tree (double-mount
8
- * of `<Notifications />`).
5
+ * Put it inside BasaltProvider, before the router. It is the ONLY notifications mount — the
6
+ * standalone `basalt-ui/notifications` overlay it superseded is gone (C1 consolidation); mounting
7
+ * a second, hand-rolled `<Notifications />` alongside `<BasaltOverlays notifications />` still
8
+ * double-mounts the overlay and is still wrong for the same reason.
9
9
  *
10
10
  * Every layer — ModalsProvider included — mounts as a SIBLING of `children`, never as a wrapper:
11
11
  * see the comment in the component body (R1). `@mantine/modals`' imperative API (`modals.open*`,
@@ -33,8 +33,8 @@
33
33
  * code. The re-exported `openSpotlight` / `closeSpotlight` helpers delegate to this store.
34
34
  *
35
35
  * @example
36
- * // main.tsx — replace <BasaltNotifications /> with <BasaltOverlays>. Use the layered bundle —
37
- * // the unlayered one outranks basalt's `@layer basalt` styles regardless of specificity:
36
+ * // main.tsx — mount <BasaltOverlays>. Use the layered bundle — the unlayered one outranks
37
+ * // basalt's `@layer basalt` styles regardless of specificity:
38
38
  * import { BasaltOverlays } from 'basalt-ui/commands'
39
39
  * import '@mantine/spotlight/styles.layer.css'
40
40
  *
@@ -244,7 +244,7 @@ Object.assign(LazyNotifications, { displayName: 'LazyNotifications' })
244
244
  // ── NotificationsLayer (runs the shared duplicate-mount guard, F15) ───────────
245
245
 
246
246
  /** Wraps `LazyNotifications` so `useNotificationsMountGuard` only fires while this layer is
247
- * actually enabled — a sibling of `BasaltNotifications` importing the SAME shared counter. */
247
+ * actually enabled — the shared counter guards against any second `<Notifications />` mount. */
248
248
  function NotificationsLayer({
249
249
  notificationsProps,
250
250
  }: {
@@ -278,9 +278,7 @@ function HotkeysMount() {
278
278
  * `children`. All three layers are enabled by default; pass `false` to disable. A disabled layer's
279
279
  * optional peer is never imported.
280
280
  *
281
- * Mount exactly ONE BasaltOverlays per app. Do NOT combine with a standalone
282
- * `<BasaltNotifications />` from `basalt-ui/notifications` — that would double-mount
283
- * `<Notifications />`.
281
+ * Mount exactly ONE BasaltOverlays per app — a second instance double-mounts `<Notifications />`.
284
282
  *
285
283
  * @example
286
284
  * <BasaltProvider>
@@ -0,0 +1,86 @@
1
+ /**
2
+ * `setColorScheme`/`toggleSidebar` — imperative shell handles, wired automatically.
3
+ *
4
+ * Command bodies run OUTSIDE the React tree (a Spotlight action, a hotkey), so a `run: () =>
5
+ * ...` has no hook access to `useMantineColorScheme()` or `BasaltShell`'s collapse state. Every
6
+ * consumer of `defineCommands` therefore hand-rolled the SAME "register a live setter ref, read
7
+ * it later" bridge per action — argo's own `lib/color-scheme-bridge.ts` / `lib/sidebar-bridge.ts`
8
+ * are the seed for this file, right down to the reason each is its OWN leaf module: `commands.tsx`
9
+ * imports the router, which imports the generated route tree, which imports every route file
10
+ * including the one that owns the shell — so a command file importing anything back out of the
11
+ * shell closes a real circular module dependency (surfaces as TS's "implicitly has type any…
12
+ * referenced in its own initializer"). A leaf with ZERO local imports is what both sides can
13
+ * depend on without re-closing that cycle (`register.ts`'s `Slot` doc has the type-level half of
14
+ * the same story for `commands`↔`overlays`).
15
+ *
16
+ * basalt registers BOTH setters itself — `BasaltProvider` calls {@link registerColorSchemeSetter}
17
+ * from inside `MantineProvider` (`provider/index.tsx`'s `BasaltBridge`), `BasaltShell` calls
18
+ * {@link registerSidebarToggle} (`shell/index.tsx`'s `ShellFrame`) — so a consumer's own
19
+ * `commands.tsx` needs no `__root.tsx` wiring at all, unlike argo's hand-rolled version: mount
20
+ * `BasaltProvider`/`BasaltShell` once, and `setColorScheme`/`toggleSidebar` just work.
21
+ *
22
+ * Last-registered wins, same "last call wins" rule `defineCommands`/`defineOverlays`'s own runtime
23
+ * stash documents — two overlapping mounts (a route swap) are normal, not a race to guard against.
24
+ *
25
+ * @example
26
+ * // commands.ts
27
+ * import { defineCommands, setColorScheme, toggleSidebar } from 'basalt-ui/commands'
28
+ * export const COMMANDS = defineCommands({
29
+ * 'theme:dark': { label: 'Dark theme', run: () => setColorScheme('dark') },
30
+ * 'ui:toggle-sidebar': { label: 'Toggle sidebar', shortcut: 'Mod+B', run: () => toggleSidebar() },
31
+ * })
32
+ */
33
+
34
+ export type ColorScheme = 'light' | 'dark' | 'auto'
35
+ export type ColorSchemeSetter = (scheme: ColorScheme) => void
36
+ export type SidebarToggle = () => void
37
+
38
+ let colorSchemeSetterRef: ColorSchemeSetter | null = null
39
+ let sidebarToggleRef: SidebarToggle | null = null
40
+
41
+ /** @internal called by `BasaltProvider`. Not part of the public API surface. */
42
+ export function registerColorSchemeSetter(setter: ColorSchemeSetter | null): void {
43
+ colorSchemeSetterRef = setter
44
+ }
45
+
46
+ /** @internal called by `BasaltShell`. Not part of the public API surface. */
47
+ export function registerSidebarToggle(toggle: SidebarToggle | null): void {
48
+ sidebarToggleRef = toggle
49
+ }
50
+
51
+ /**
52
+ * Set the color scheme from outside the React tree. A no-op (with a dev-only console warning)
53
+ * when no `BasaltProvider` is mounted yet — the same "not wired" shape `runCommand`/
54
+ * `overlays.open` already use for an unregistered id.
55
+ *
56
+ * @example
57
+ * setColorScheme('dark')
58
+ */
59
+ export function setColorScheme(scheme: ColorScheme): void {
60
+ if (colorSchemeSetterRef === null) {
61
+ if (process.env['NODE_ENV'] !== 'production') {
62
+ // eslint-disable-next-line no-console -- dev-only diagnostic, mirrors runCommand's own
63
+ console.warn('[basalt] setColorScheme: no BasaltProvider is mounted yet')
64
+ }
65
+ return
66
+ }
67
+ colorSchemeSetterRef(scheme)
68
+ }
69
+
70
+ /**
71
+ * Toggle `BasaltShell`'s desktop sidebar collapse from outside the React tree. A no-op (with a
72
+ * dev-only console warning) when no `BasaltShell` is mounted yet.
73
+ *
74
+ * @example
75
+ * toggleSidebar()
76
+ */
77
+ export function toggleSidebar(): void {
78
+ if (sidebarToggleRef === null) {
79
+ if (process.env['NODE_ENV'] !== 'production') {
80
+ // eslint-disable-next-line no-console -- dev-only diagnostic, mirrors runCommand's own
81
+ console.warn('[basalt] toggleSidebar: no BasaltShell is mounted yet')
82
+ }
83
+ return
84
+ }
85
+ sidebarToggleRef()
86
+ }
@@ -71,3 +71,73 @@ export function duplicateMount(component: string): string {
71
71
  'more than one instance is mounted at once — mount exactly one, at the app root.',
72
72
  )
73
73
  }
74
+
75
+ // ── Transport-agnostic error decoding ────────────────────────────────────────────────────────────
76
+ //
77
+ // Deliberately in this module, with NO `@tanstack/react-query` import — `dashboard/query-state.tsx`
78
+ // renders server messages and must not drag the query peer into the root barrel's graph to do it.
79
+
80
+ /**
81
+ * The HTTP status behind a thrown envelope (Eden Treaty's `{ status, value }`, a `Response`-shaped
82
+ * throw), when there is one. Use it to BRANCH — 404 → "not found" copy — rather than to build a
83
+ * message; `toErrorMessage` already folds the status into its text when the body says nothing.
84
+ *
85
+ * @example
86
+ * if (errorStatus(err) === 404) return <EmptyState title="Gone" />
87
+ */
88
+ export function errorStatus(err: unknown): number | undefined {
89
+ if (err === null || typeof err !== 'object') return undefined
90
+ const status = (err as { status?: unknown }).status
91
+ return typeof status === 'number' ? status : undefined
92
+ }
93
+
94
+ /** Strings that technically decoded but tell a reader nothing. `JSON.stringify` produces most. */
95
+ const UNUSABLE_MESSAGES = new Set(['', '{}', '[]', 'null', 'undefined', '[object Object]'])
96
+
97
+ function decodeError(err: unknown): string {
98
+ if (err instanceof Error) return err.message
99
+ if (typeof err === 'string') return err
100
+ if (typeof err === 'object' && err !== null) {
101
+ const obj = err as Record<string, unknown>
102
+ if (typeof obj['message'] === 'string') return obj['message']
103
+ // Eden-style { value: ... } or { status, value } envelopes — unwrap one level.
104
+ if ('value' in obj) return decodeError(obj['value'])
105
+ }
106
+ try {
107
+ return JSON.stringify(err) ?? ''
108
+ } catch {
109
+ return String(err)
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Extract a human-readable message from an unknown thrown value (e.g. the raw Eden envelope
115
+ * thrown by `unwrap`). Removes the 8-line three-branch boilerplate consumers duplicate.
116
+ *
117
+ * Resolution order:
118
+ * 1. `Error` instance → err.message
119
+ * 2. `{ message: string }` → message (Eden / Elysia error shape)
120
+ * 3. `{ value: ... }` → recursively (Eden nested value envelope)
121
+ * 4. plain string → itself
122
+ * 5. anything else → `JSON.stringify`
123
+ *
124
+ * **If that yields nothing a human can read** (`''`, `'{}'`, `'[]'`, `'null'`, `'undefined'`,
125
+ * `'[object Object]'` — the shapes `JSON.stringify` produces for an opaque envelope) the `fallback`
126
+ * is returned instead, with `(HTTP <status>)` appended when the envelope carries one. Without that
127
+ * guard the caller renders a literal `{}` at the user, or — for `toErrorMessage(undefined)` —
128
+ * the actual value `undefined`, since `JSON.stringify(undefined)` is not a string at all.
129
+ *
130
+ * @example
131
+ * try {
132
+ * await unwrap(api.resource.post({ body }))
133
+ * } catch (e) {
134
+ * toast(toErrorMessage(e, 'Could not create share')) // → "slug already in use"
135
+ * // → "Could not create share (HTTP 502)"
136
+ * }
137
+ */
138
+ export function toErrorMessage(err: unknown, fallback = 'The request failed.'): string {
139
+ const decoded = decodeError(err).trim()
140
+ if (!UNUSABLE_MESSAGES.has(decoded)) return decoded
141
+ const status = errorStatus(err)
142
+ return status === undefined ? fallback : `${fallback} (HTTP ${status})`
143
+ }
@@ -19,3 +19,5 @@ export {
19
19
  } from './errors'
20
20
  export { useValidateProps, assertRequiredProps, resetValidatedProps } from './validate'
21
21
  export { scrollParentOf, SCROLLPORT_ATTRIBUTE } from './scroll-parent'
22
+ export { isDev } from './is-dev'
23
+ export { MOTION_DURATION, MOTION_EASE_STANDARD, MOTION_SPRING } from './motion'
@@ -60,7 +60,7 @@ export type ToneWithNeutral = Tone | 'neutral'
60
60
  * twice — `WidgetHeaderTier` stays the name on the public barrel; `Tier` is what `common`-aware
61
61
  * code spells. Type-only, so this file emits no runtime edge into the Mantine-coupled half.
62
62
  */
63
- export type { WidgetHeaderTier as Tier } from '../widget-header/widget-header'
63
+ export type { WidgetHeaderTier as Tier } from '../dashboard/widget-header'
64
64
 
65
65
  /**
66
66
  * The class joiner. Replaces the `[a, b].filter(Boolean).join(' ')` spelled out in seven files
@@ -7,7 +7,7 @@
7
7
  * mean warns once in dev and renders on (`useValidateProps`).
8
8
  */
9
9
  import { useEffect } from 'react'
10
- import { isDev } from '../utils/is-dev'
10
+ import { isDev } from './is-dev'
11
11
  import { requiredProp } from './errors'
12
12
 
13
13
  /** A check returns the message(s) it has, or nothing. `null`/`undefined` entries are ignored. */
@@ -101,10 +101,9 @@ export type MarkdownProps = {
101
101
  * default to same-origin `['/']`; `'trusted'` (your own authored content) keeps
102
102
  * `['https://', '/']`. An explicit `allowedImagePrefixes` always wins over both.
103
103
  *
104
- * Default: `'untrusted'` when `streaming` is set, `'trusted'` otherwise. That default only
105
- * preserves the pre-1.12 fail-safe — it is NOT the policy. Settledness has nothing to do with
106
- * trust: a FINISHED agent message is still model-generated, so any surface rendering agent text
107
- * must pin `contentTrust="untrusted"` explicitly rather than lean on `streaming`.
104
+ * Default: `'trusted'`. Settledness has nothing to do with trust: a FINISHED agent message is
105
+ * still model-generated, so any surface rendering agent text must pin `contentTrust="untrusted"`
106
+ * explicitly rather than lean on `streaming`.
108
107
  */
109
108
  readonly contentTrust?: MarkdownContentTrust
110
109
  /**
@@ -618,10 +617,7 @@ export function Markdown({
618
617
  density = 'article',
619
618
  measure,
620
619
  streaming = false,
621
- // `streaming` as the trust DEFAULT is backward compatibility only (pre-1.12 the image policy was
622
- // read straight off `streaming`, so dropping it would silently widen every existing streaming
623
- // call site). It is a floor, never the policy — see the prop's JSDoc.
624
- contentTrust = streaming ? 'untrusted' : 'trusted',
620
+ contentTrust = 'trusted',
625
621
  components,
626
622
  fenceRenderers,
627
623
  sanitizeSchema,
@@ -16,7 +16,7 @@ import { ActionIcon, Button, Group, Menu } from '@mantine/core'
16
16
  import { createContext, isValidElement, useContext, useEffect } from 'react'
17
17
  import type { ReactNode } from 'react'
18
18
  import type { BasaltProps } from '../common/props'
19
- import type { NavAnchor } from '../nav/types'
19
+ import type { NavAnchor } from '../shell/nav-types'
20
20
  import type { AnyNavLink } from '../router-tanstack/nav'
21
21
  import { IconSlot } from '../theme/icon-slot'
22
22
  import { ControlGroup } from './control-group'
@@ -28,7 +28,7 @@ import type { ReactNode, RefObject } from 'react'
28
28
  import { cx } from '../common/props'
29
29
  import type { BasaltProps } from '../common/props'
30
30
  import { CtlSlot } from '../theme'
31
- import { InfoGlyph } from '../widget-header/widget-header'
31
+ import { InfoGlyph } from '../dashboard/widget-header'
32
32
  import classes from './panel-row.module.css'
33
33
 
34
34
  /**
@@ -32,7 +32,7 @@ import { useEffect, useRef, useState } from 'react'
32
32
  import type { ComponentType, ReactNode } from 'react'
33
33
  import type { BasaltProps } from '../common/props'
34
34
  import { assertRequiredProps } from '../common/validate'
35
- import { isDev } from '../utils/is-dev'
35
+ import { isDev } from '../common/is-dev'
36
36
  import type { FieldHandle, RangeField, RangeValue } from '../state'
37
37
  import classes from './controls.module.css'
38
38
  import { useFilterRegistration, useFilterSurface } from './filter-context'