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
@@ -23,10 +23,11 @@ import { AnimatePresence, motion } from 'motion/react'
23
23
  import type { JSX, ReactNode } from 'react'
24
24
  import { useEffect, useState } from 'react'
25
25
  import type { AgentThread } from '../agent'
26
+ import { BasaltStickToBottom } from '../agent'
26
27
  import { cx } from '../common/props'
27
28
  import type { BasaltProps } from '../common/props'
28
29
  import { assertRequiredProps } from '../common/validate'
29
- import { MOTION_SPRING } from '../motion'
30
+ import { MOTION_SPRING } from '../common/motion'
30
31
  import type { ComposerSubmit } from './composer'
31
32
  import { ThreadFeedRow } from './thread-feed-row'
32
33
  import { ThreadOutcomeCard } from './thread-outcome-card'
@@ -69,6 +70,16 @@ export type ThreadFeedProps = BasaltProps & {
69
70
  * (`onStop`/`liveParts`/`liveStatus`) the built-in `'inline'` row doesn't expose; see `variant`
70
71
  * and `onSend`. */
71
72
  readonly renderRow?: (thread: AgentThread) => ReactNode
73
+ /**
74
+ * Scroll anchor. `'start'` (default) is today's top-anchored `ScrollArea` feed, unchanged.
75
+ * `'end'` anchors the feed's growth to the BOTTOM instead — a `BasaltStickToBottom` scroll node
76
+ * (auto-follows the live turn, same container `ThreadDetailPanel` uses) rather than `ScrollArea`,
77
+ * with the row stack `justify`-ed to the end and grown to the full height so a short feed still
78
+ * sits at the bottom rather than floating at the top. This is what a bottom-anchored chat feed
79
+ * (argo's hermes-chat S15 — oldest→newest top→bottom, pinned last, `renderRow`-driven) needs
80
+ * instead of hand-rolling its own scroll shell around `renderRow`.
81
+ */
82
+ readonly anchor?: 'start' | 'end'
72
83
  }
73
84
 
74
85
  function defaultRow(
@@ -116,6 +127,7 @@ export function ThreadFeed({
116
127
  variant = 'outcome',
117
128
  onSend,
118
129
  renderRow,
130
+ anchor = 'start',
119
131
  className,
120
132
  style,
121
133
  }: ThreadFeedProps): JSX.Element {
@@ -153,36 +165,50 @@ export function ThreadFeed({
153
165
  ? renderRow(thread)
154
166
  : defaultRow(thread, variant, activeId, collapsedId, handleInlineToggle, onSelect, onSend)
155
167
 
156
- if (reduceMotion) {
168
+ // B5: `anchor="end"` grows the stack to the full scroll height and pins its content to the
169
+ // bottom, so a feed with only a couple of threads still sits flush against the composer instead
170
+ // of floating at the top of an otherwise-empty ScrollArea.
171
+ const stackAnchorProps = anchor === 'end' ? { justify: 'flex-end' as const, mih: '100%' } : {}
172
+
173
+ const list = reduceMotion ? (
174
+ <Stack gap="sm" p="sm" {...stackAnchorProps}>
175
+ {threads.map((thread) => (
176
+ <Box key={thread.id}>{rowFor(thread)}</Box>
177
+ ))}
178
+ </Stack>
179
+ ) : (
180
+ <Stack gap="sm" p="sm" {...stackAnchorProps}>
181
+ <AnimatePresence mode="popLayout" initial={false}>
182
+ {threads.map((thread) => (
183
+ <motion.div
184
+ key={thread.id}
185
+ layout
186
+ initial={{ opacity: 0, y: -8 }}
187
+ animate={{ opacity: 1, y: 0 }}
188
+ exit={{ opacity: 0, scale: 0.97 }}
189
+ transition={MOTION_SPRING}
190
+ >
191
+ {rowFor(thread)}
192
+ </motion.div>
193
+ ))}
194
+ </AnimatePresence>
195
+ </Stack>
196
+ )
197
+
198
+ // `anchor="end"`: a `BasaltStickToBottom` scroll node instead of `ScrollArea` — it owns an
199
+ // initial scroll-to-end on mount and auto-follows appended content (the live turn), which a
200
+ // plain `ScrollArea` has no notion of.
201
+ if (anchor === 'end') {
157
202
  return (
158
- <ScrollArea className={cx(className)} style={{ height: '100%', ...style }}>
159
- <Stack gap="sm" p="sm">
160
- {threads.map((thread) => (
161
- <Box key={thread.id}>{rowFor(thread)}</Box>
162
- ))}
163
- </Stack>
164
- </ScrollArea>
203
+ <BasaltStickToBottom className={cx(className)} style={{ height: '100%', ...style }}>
204
+ {list}
205
+ </BasaltStickToBottom>
165
206
  )
166
207
  }
167
208
 
168
209
  return (
169
210
  <ScrollArea className={cx(className)} style={{ height: '100%', ...style }}>
170
- <Stack gap="sm" p="sm">
171
- <AnimatePresence mode="popLayout" initial={false}>
172
- {threads.map((thread) => (
173
- <motion.div
174
- key={thread.id}
175
- layout
176
- initial={{ opacity: 0, y: -8 }}
177
- animate={{ opacity: 1, y: 0 }}
178
- exit={{ opacity: 0, scale: 0.97 }}
179
- transition={MOTION_SPRING}
180
- >
181
- {rowFor(thread)}
182
- </motion.div>
183
- ))}
184
- </AnimatePresence>
185
- </Stack>
211
+ {list}
186
212
  </ScrollArea>
187
213
  )
188
214
  }
@@ -60,7 +60,7 @@ import { formatRelativeTime } from './relative-time'
60
60
  import { ToolChip } from './tool-chip'
61
61
  import { resolveVirtualize } from './virtualize'
62
62
  import type { VirtualizeOptions, VirtualizeProps } from './virtualize'
63
- import { isDev } from '../utils/is-dev'
63
+ import { isDev } from '../common/is-dev'
64
64
 
65
65
  /** The mono, uppercase, letter-spaced micro-label idiom (docs/DESIGN-SPEC.md §3) — shared by the
66
66
  * transcript's role labels and the reasoning/tool-call headers below. */
@@ -112,6 +112,54 @@ export type ResolvedVirtualize = {
112
112
  readonly height: number | string
113
113
  }
114
114
 
115
+ /**
116
+ * `ThreadFeedRow`'s OWN height contract (B3) — a strict superset of {@link VirtualizeProps} that
117
+ * additionally allows `height` WITHOUT `virtualize`. This is deliberately NOT a widening of
118
+ * `VirtualizeProps` itself (see `virtualize.type-guard.test.ts`, which pins that union rejecting
119
+ * `{ height }` with no `virtualize`): the two `height` modes mean different things and route to
120
+ * different DOM. `virtualize: true|VirtualizeOptions` + `height` still hands the row's transcript a
121
+ * fixed-size scroll container `@tanstack/react-virtual` owns and measures (unchanged, forwarded to
122
+ * `ThreadTranscript` via `resolveVirtualize`). `height` ALONE — no `virtualize` — wraps the row's
123
+ * TRANSCRIPT in a fixed-height `BasaltStickToBottom` (the same auto-follow-the-live-turn container
124
+ * `ThreadDetailPanel` already uses), leaving the composer outside/below it: a bounded, scrolling
125
+ * row for a thread the caller does not want to virtualize (argo's hermes-chat S7 — a 480px
126
+ * transcript body with inner scroll, no `@tanstack/react-virtual` peer installed).
127
+ */
128
+ export type RowHeightProps =
129
+ | { readonly virtualize?: false; readonly height?: number | string }
130
+ | { readonly virtualize: true | VirtualizeOptions; readonly height: number | string }
131
+
132
+ /** The resolved shape of {@link RowHeightProps} — exactly one of the three row body layouts. */
133
+ export type ResolvedRowHeight =
134
+ | { readonly kind: 'virtualized'; readonly virtualize: ResolvedVirtualize }
135
+ | { readonly kind: 'bounded'; readonly height: number | string }
136
+ | { readonly kind: 'content-sized' }
137
+
138
+ /**
139
+ * The single narrowing point `ThreadFeedRow` resolves {@link RowHeightProps} through — mirrors
140
+ * {@link resolveVirtualize}'s role for `VirtualizeProps`, extended with the `'bounded'` branch B3
141
+ * adds. Runtime-checked rather than routed through `resolveVirtualize` itself: `RowHeightProps`'
142
+ * inactive branch relaxes `height` from `never` to `number | string | undefined`, which is NOT
143
+ * structurally assignable to `VirtualizeProps` (by design — see this type's own doc), so the two
144
+ * resolvers stay independent rather than one calling the other with a cast.
145
+ */
146
+ export function resolveRowHeight(props: RowHeightProps): ResolvedRowHeight {
147
+ const { virtualize, height } = props
148
+ if (virtualize === true || (typeof virtualize === 'object' && virtualize !== null)) {
149
+ // The union guarantees `height` is present on this branch — see resolveVirtualize's own doc
150
+ // for why the pairing is re-stated here instead of narrowed automatically.
151
+ return {
152
+ kind: 'virtualized',
153
+ virtualize: {
154
+ options: virtualize === true ? {} : virtualize,
155
+ height: height as number | string,
156
+ },
157
+ }
158
+ }
159
+ if (height !== undefined) return { kind: 'bounded', height }
160
+ return { kind: 'content-sized' }
161
+ }
162
+
115
163
  /**
116
164
  * Narrows a `VirtualizeProps` pair once, centrally: `null` when virtualization is off, otherwise
117
165
  * the options object (normalizing the bare `virtualize: true` shorthand to `{}`) alongside the
@@ -101,7 +101,12 @@ export {
101
101
  TooltipRow,
102
102
  TooltipBody,
103
103
  } from './primitives/ChartTooltip'
104
- export { AxisBottomDate, AxisLeftNumeric, AxisRightNumeric } from './primitives/Axes'
104
+ export {
105
+ AxisBottomDate,
106
+ AxisBottomNumeric,
107
+ AxisLeftNumeric,
108
+ AxisRightNumeric,
109
+ } from './primitives/Axes'
105
110
  export { HoverOverlay } from './primitives/HoverOverlay'
106
111
  export { ZoneRects, type ZoneSpec } from './primitives/ZoneRects'
107
112
  export { XZoneRects, type XZoneSpec } from './primitives/XZoneRects'
@@ -139,19 +144,11 @@ export { measureText, maxTextWidth } from './utils/measure-text'
139
144
  export {
140
145
  ZonedLine,
141
146
  type ZonedLineProps,
142
- type ZonedLineZone,
143
147
  type ZonedLineThreshold,
144
148
  type ZonedLineRefLine,
145
149
  } from './kinds/ZonedLine'
146
150
 
147
- export {
148
- Bars,
149
- type BarsProps,
150
- type BarsBar,
151
- type BarsLine,
152
- type BarsZone,
153
- type BarsRefLine,
154
- } from './kinds/Bars'
151
+ export { Bars, type BarsProps, type BarsBar, type BarsLine, type BarsRefLine } from './kinds/Bars'
155
152
 
156
153
  export { StackedArea, type StackedAreaProps } from './kinds/StackedArea'
157
154
  export { Donut, type DonutProps, type DonutDatum } from './kinds/Donut'
@@ -22,7 +22,7 @@ import { HoverOverlay } from '../primitives/HoverOverlay'
22
22
  import type { ChartState } from '../primitives/ChartPending'
23
23
  import type { ChartLegendConfig, SeriesStyle } from '../series'
24
24
  import { fmtAxisDate } from '../utils/format'
25
- import { isDev } from '../../utils/is-dev'
25
+ import { isDev } from '../../common/is-dev'
26
26
 
27
27
  /**
28
28
  * A state a strip can draw. Extends `SeriesStyle` rather than `ChartSeries` because a band has no
@@ -12,7 +12,7 @@ import type { ChartState } from '../primitives/ChartPending'
12
12
  import { curveFor, LINE_OVERLAY_STROKE_WIDTH } from '../series'
13
13
  import type { ChartLegendConfig, ChartSeries, SeriesCurve } from '../series'
14
14
  import { padAutoLower } from '../utils/domain'
15
- import { isDev } from '../../utils/is-dev'
15
+ import { isDev } from '../../common/is-dev'
16
16
 
17
17
  /**
18
18
  * `T = unknown` is load-bearing, not a shortcut: it keeps a consumer's existing
@@ -55,9 +55,6 @@ export type BarsLine<T = unknown> = {
55
55
  curve?: SeriesCurve
56
56
  }
57
57
 
58
- /** @deprecated Use ZoneSpec from primitives/ZoneRects. Kept as an alias. */
59
- export type BarsZone = ZoneSpec
60
-
61
58
  export type BarsRefLine = {
62
59
  value: number
63
60
  color: string
@@ -84,7 +81,7 @@ export type BarsProps<T> = BasaltProps & {
84
81
  lines?: BarsLine<T>[]
85
82
 
86
83
  /** Horizontal value-range overlays (target zones, optimal bands). */
87
- zones?: BarsZone[]
84
+ zones?: ZoneSpec[]
88
85
  /** Dashed/solid horizontal reference lines. */
89
86
  refLines?: BarsRefLine[]
90
87
 
@@ -49,8 +49,9 @@ export type DualPanelProps<T> = BasaltProps & {
49
49
  series: ChartSeries<T>[]
50
50
  /** Top-pane y-domain. Default 'auto' (computed from all top lines, padded). */
51
51
  topYDomain?: [number, number] | 'auto'
52
- /** Shade the band between two top lines (by key), filled on both sides. */
53
- fillBetween?: { from: string; to: string; fill: string }
52
+ /** Shade the band between two top lines (by key). `aboveFill` colors the side where `to` runs
53
+ * above `from` (e.g. a "bad" red) — omitted, both sides share `fill` (today's behaviour). */
54
+ fillBetween?: { from: string; to: string; fill: string; aboveFill?: string }
54
55
  /** Horizontal reference lines on the top pane. Solid by default; set dashed: true for a dashed line. */
55
56
  topRefLines?: { value: number; color: string; dashed?: boolean }[]
56
57
  /** Horizontal value-range bands on the top pane. */
@@ -512,7 +513,7 @@ function DualPanelPlot<T>(props: DualPanelPlotProps<T>) {
512
513
  clipBelowTo={topH}
513
514
  curve={fillCurve}
514
515
  belowAreaProps={{ fill: fillBetween.fill }}
515
- aboveAreaProps={{ fill: fillBetween.fill }}
516
+ aboveAreaProps={{ fill: fillBetween.aboveFill ?? fillBetween.fill }}
516
517
  />
517
518
  )}
518
519
 
@@ -8,7 +8,7 @@ import type { AxisConfig, PlotContext } from '../primitives/CartesianChart'
8
8
  import type { ChartState } from '../primitives/ChartPending'
9
9
  import { curveFor } from '../series'
10
10
  import type { ChartLegendConfig, ChartSeries } from '../series'
11
- import { isDev } from '../../utils/is-dev'
11
+ import { isDev } from '../../common/is-dev'
12
12
 
13
13
  export type StackedAreaProps<T> = BasaltProps & {
14
14
  data: T[]
@@ -14,9 +14,6 @@ import { curveFor, definedOn, LINE_OVERLAY_STROKE_WIDTH, toPlotPoint } from '../
14
14
  import type { ChartLegendConfig, ChartSeries } from '../series'
15
15
  import { VX } from '../../tokens'
16
16
 
17
- /** @deprecated Use ZoneSpec from primitives/ZoneRects. Kept as an alias for back-compat. */
18
- export type ZonedLineZone = ZoneSpec
19
-
20
17
  /** Semi-transparent fill above (or below) a threshold value, tracking the line. */
21
18
  export type ZonedLineThreshold = {
22
19
  value: number
@@ -46,7 +43,7 @@ export type ZonedLineProps<T> = BasaltProps & {
46
43
  /** Y-axis. Collapses the old `yDomain` / `yAutoMaxFloor` / `yAutoMinCeil` / `yAutoPad` /
47
44
  * `numTicksY` / `formatYTick` prop soup into one object. */
48
45
  y?: AxisConfig<T>
49
- zones?: ZonedLineZone[]
46
+ zones?: ZoneSpec[]
50
47
  /** Vertical x-range overlays (time windows), rendered behind the line. Bounds are `getX`
51
48
  * domain keys. */
52
49
  xZones?: XZoneSpec[]
@@ -20,10 +20,13 @@ export function AxisLeftNumeric({
20
20
  scale,
21
21
  numTicks = 5,
22
22
  tickFormat,
23
+ tickValues,
23
24
  }: {
24
25
  scale: AxisScale
25
26
  numTicks?: number
26
27
  tickFormat?: TickFormatter<number>
28
+ /** Exact tick positions (e.g. a compass axis at 0/90/180/270). Overrides `numTicks`. */
29
+ tickValues?: readonly number[]
27
30
  }) {
28
31
  const { axisFont } = useChartTierMetrics()
29
32
  return (
@@ -31,6 +34,7 @@ export function AxisLeftNumeric({
31
34
  scale={scale}
32
35
  numTicks={numTicks}
33
36
  {...(tickFormat !== undefined && { tickFormat })}
37
+ {...(tickValues !== undefined && { tickValues: [...tickValues] })}
34
38
  tickLabelProps={{
35
39
  fill: VX.faint,
36
40
  fontFamily: TICK_FONT_FAMILY,
@@ -49,12 +53,15 @@ export function AxisRightNumeric({
49
53
  left,
50
54
  numTicks = 5,
51
55
  tickFormat,
56
+ tickValues,
52
57
  }: {
53
58
  scale: AxisScale
54
59
  /** Left offset inside the Group (typically xMax). Required since AxisRight needs positioning. */
55
60
  left: number
56
61
  numTicks?: number
57
62
  tickFormat?: TickFormatter<number>
63
+ /** Exact tick positions (e.g. a compass axis at 0/90/180/270). Overrides `numTicks`. */
64
+ tickValues?: readonly number[]
58
65
  }) {
59
66
  const { axisFont } = useChartTierMetrics()
60
67
  return (
@@ -63,6 +70,7 @@ export function AxisRightNumeric({
63
70
  scale={scale}
64
71
  numTicks={numTicks}
65
72
  {...(tickFormat !== undefined && { tickFormat })}
73
+ {...(tickValues !== undefined && { tickValues: [...tickValues] })}
66
74
  tickLabelProps={{
67
75
  fill: VX.faint,
68
76
  fontFamily: TICK_FONT_FAMILY,
@@ -75,6 +83,47 @@ export function AxisRightNumeric({
75
83
  )
76
84
  }
77
85
 
86
+ /**
87
+ * Themed bottom numeric axis — the numeric twin of {@link AxisLeftNumeric}, for a plot whose x is
88
+ * a continuous number rather than a date/category (e.g. `sky-panorama.tsx`'s azimuth 0–360°,
89
+ * `docs/CHARTS-SPEC.md` issue #52). Exists so a bespoke continuous-x plot — declared exempt from
90
+ * `CartesianChart` by a `theme-allow-file hand-rolled-plot` waiver, since the point-scale x axis
91
+ * that primitive builds cannot represent one — stops re-implementing tick text/color/font by hand.
92
+ */
93
+ export function AxisBottomNumeric({
94
+ scale,
95
+ top,
96
+ numTicks = 5,
97
+ tickFormat,
98
+ tickValues,
99
+ }: {
100
+ scale: AxisScale
101
+ top: number
102
+ numTicks?: number
103
+ tickFormat?: TickFormatter<number>
104
+ /** Exact tick positions (e.g. a compass axis at 0/90/180/270). Overrides `numTicks`. */
105
+ tickValues?: readonly number[]
106
+ }) {
107
+ const { axisFont } = useChartTierMetrics()
108
+ return (
109
+ <AxisBottom
110
+ top={top}
111
+ scale={scale}
112
+ numTicks={numTicks}
113
+ {...(tickFormat !== undefined && { tickFormat })}
114
+ {...(tickValues !== undefined && { tickValues: [...tickValues] })}
115
+ tickLabelProps={{
116
+ fill: VX.faint,
117
+ fontFamily: TICK_FONT_FAMILY,
118
+ fontSize: axisFont,
119
+ textAnchor: 'middle',
120
+ }}
121
+ stroke={VX.surface.border}
122
+ tickStroke={VX.surface.border}
123
+ />
124
+ )
125
+ }
126
+
78
127
  /**
79
128
  * Nudge a rotated tick label back onto its tick. Both are the d3 idiom for the angle: a 45° label
80
129
  * hangs from the tick's lower-left, a 90° one is centred on the tick's vertical line.
@@ -3,7 +3,7 @@
3
3
  * tier="widget"` title row (icon/info/value/delta/count/actions) plus an optional subtitle, over a
4
4
  * clipped chart body. Do not wrap visx charts in bare divs.
5
5
  *
6
- * `WidgetHeader` lives in `src/widget-header/` and is itself Mantine-free, so composing it here
6
+ * `WidgetHeader` lives in `src/dashboard/` and is itself Mantine-free, so composing it here
7
7
  * keeps `./charts` resolving with no `@mantine/*` installed (`check-dist-layering.mjs`,
8
8
  * `basalt/token-layer-boundary`). The `actions` slot cannot mount `CtlSlot` (Mantine-coupled) for
9
9
  * the same reason — it carries only `data-basalt-tier="widget"`; a basalt control placed there
@@ -16,8 +16,10 @@ import type { CSSProperties, ReactNode } from 'react'
16
16
  import { cx } from '../../common/props'
17
17
  import type { BasaltProps, SlotStylesProps } from '../../common/props'
18
18
  import { VX } from '../../tokens'
19
- import { WidgetHeader } from '../../widget-header'
20
- import type { DeltaPolarity } from '../../widget-header'
19
+ import { WidgetHeader } from '../../dashboard/widget-header'
20
+ import type { DeltaPolarity } from '../../dashboard/delta-badge'
21
+ import { ChartEmpty, ChartError, ChartPending, resolveChartState } from './ChartPending'
22
+ import type { ChartState } from './ChartPending'
21
23
 
22
24
  // Surfaces resolve per theme via CSS vars, so the styles are static (no useMemo/isDark).
23
25
  // Depth = `shadow-card` (a whisper shadow + a 1px ring baked into the same value), never a
@@ -99,7 +101,22 @@ export type ChartCardProps = BasaltProps &
99
101
  count?: number
100
102
  /** Right-aligned slot — carries `data-basalt-tier="widget"` (no `CtlSlot`; see module doc). */
101
103
  actions?: ReactNode
102
- children: ReactNode
104
+ /**
105
+ * Any resolved state (pending → error → empty, same precedence `resolveChartState` gives
106
+ * every chart kind) REPLACES `children` with its placeholder at `placeholderHeight` — the
107
+ * header still renders unchanged, so a card's title never reflows through the pending → drawn
108
+ * transition. `state={{}}` (or an all-falsy state) renders `children`, same as omitting the
109
+ * prop.
110
+ */
111
+ state?: ChartState
112
+ /** Body height while a placeholder shows. Default 240 (`ChartFrame`'s own default height). */
113
+ placeholderHeight?: number
114
+ /** Affordance under a pending/empty/error label — a retry button, a "clear filters" link.
115
+ * Forwarded to whichever placeholder resolves. */
116
+ stateAction?: ReactNode
117
+ /** Optional so a title-less pending card can BE a route's `<Suspense>` fallback
118
+ * (`<ChartCard state={{ pending: true }} placeholderHeight={320} />`, no `children` at all). */
119
+ children?: ReactNode
103
120
  }
104
121
 
105
122
  export function ChartCard({
@@ -113,11 +130,15 @@ export function ChartCard({
113
130
  deltaPolarity,
114
131
  count,
115
132
  actions,
133
+ state,
134
+ placeholderHeight = 240,
135
+ stateAction,
116
136
  className,
117
137
  classNames,
118
138
  style,
119
139
  children,
120
140
  }: ChartCardProps) {
141
+ const resolvedState = resolveChartState({ ...(state !== undefined && { state }) })
121
142
  const hasHeader =
122
143
  title !== undefined ||
123
144
  info !== undefined ||
@@ -127,7 +148,11 @@ export function ChartCard({
127
148
  count !== undefined
128
149
 
129
150
  return (
130
- <div className={cx(classNames?.root, className)} style={{ ...cardStyle, ...style }}>
151
+ <div
152
+ className={cx(classNames?.root, className)}
153
+ style={{ ...cardStyle, ...style }}
154
+ {...(resolvedState === 'pending' && { 'aria-busy': 'true' })}
155
+ >
131
156
  {hasHeader && (
132
157
  <div
133
158
  {...(classNames?.header !== undefined && { className: classNames.header })}
@@ -154,7 +179,25 @@ export function ChartCard({
154
179
  {...(classNames?.body !== undefined && { className: classNames.body })}
155
180
  style={bodyClipStyle}
156
181
  >
157
- {children}
182
+ {resolvedState === 'pending' ? (
183
+ <ChartPending width="100%" height={placeholderHeight} />
184
+ ) : resolvedState === 'error' ? (
185
+ <ChartError
186
+ width="100%"
187
+ height={placeholderHeight}
188
+ error={state?.error}
189
+ action={stateAction}
190
+ />
191
+ ) : resolvedState === 'empty' ? (
192
+ <ChartEmpty
193
+ width="100%"
194
+ height={placeholderHeight}
195
+ {...(typeof state?.empty === 'string' && { label: state.empty })}
196
+ action={stateAction}
197
+ />
198
+ ) : (
199
+ children
200
+ )}
158
201
  </div>
159
202
  </div>
160
203
  )
@@ -283,7 +283,11 @@ export function ChartFrame({
283
283
  ) : resolvedState === 'error' ? (
284
284
  <ChartError width={plot.width} height={plot.height} error={state?.error} />
285
285
  ) : resolvedState === 'empty' ? (
286
- <ChartEmpty width={plot.width} height={plot.height} />
286
+ <ChartEmpty
287
+ width={plot.width}
288
+ height={plot.height}
289
+ {...(typeof state?.empty === 'string' && { label: state.empty })}
290
+ />
287
291
  ) : (
288
292
  children({ ...plot, hidden })
289
293
  ))}
@@ -3,12 +3,14 @@ import type { BasaltProps } from '../../common/props'
3
3
  import { VX } from '../../tokens'
4
4
 
5
5
  export type ChartCenterProps = BasaltProps & {
6
- width: number
6
+ /** A block body (a title-less pending `ChartCard`, a non-`ChartFrame` body) has no measured
7
+ * plot width to reserve — `'100%'` fills its container instead of a pixel figure. */
8
+ width: number | string
7
9
  height: number
8
10
  children: ReactNode
9
11
  }
10
12
 
11
- const centerStyle = (width: number, height: number): CSSProperties => ({
13
+ const centerStyle = (width: number | string, height: number): CSSProperties => ({
12
14
  width,
13
15
  height,
14
16
  display: 'flex',
@@ -40,7 +42,7 @@ export function ChartCenter({
40
42
  }
41
43
 
42
44
  export type ChartPendingProps = BasaltProps & {
43
- width: number
45
+ width: number | string
44
46
  height: number
45
47
  /** Default `'Loading…'`. */
46
48
  label?: string
@@ -113,8 +115,12 @@ export type ChartState = {
113
115
  pending?: boolean
114
116
  /** Truthy = the query failed. Any thrown value; an `Error`'s `message` is used as the label. */
115
117
  error?: unknown
116
- /** The query resolved and there is genuinely nothing to plot. */
117
- empty?: boolean
118
+ /**
119
+ * The query resolved and there is genuinely nothing to plot. `true`, or the empty label
120
+ * itself — a string is truthy AND the copy `ChartEmpty` paints, so a consumer with its own
121
+ * "No data" wording never needs a ternary around the placeholder.
122
+ */
123
+ empty?: boolean | string
118
124
  }
119
125
 
120
126
  /** Which placeholder {@link ChartState} resolves to, or `null` to draw the chart. */
@@ -132,12 +138,12 @@ export function resolveChartState(input: {
132
138
  const { state, isPending } = input
133
139
  if (isPending === true || state?.pending === true) return 'pending'
134
140
  if (state?.error !== undefined && state.error !== null && state.error !== false) return 'error'
135
- if (state?.empty === true) return 'empty'
141
+ if (state?.empty) return 'empty'
136
142
  return null
137
143
  }
138
144
 
139
145
  export type ChartEmptyProps = BasaltProps & {
140
- width: number
146
+ width: number | string
141
147
  height: number
142
148
  /** Default `'No data'`. */
143
149
  label?: string
@@ -188,7 +194,7 @@ export function ChartEmpty({
188
194
  }
189
195
 
190
196
  export type ChartErrorProps = BasaltProps & {
191
- width: number
197
+ width: number | string
192
198
  height: number
193
199
  /** Overrides the text outright. Omitted, an `Error`'s own `message` is used, else
194
200
  * `'Could not load chart'` — a failure never borrows the empty state's copy. */