@nocoo/basalt 2.1.8 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (352) hide show
  1. package/README.md +2 -0
  2. package/ai/COMPATIBILITY.md +11 -1
  3. package/ai/DESIGN.md +342 -0
  4. package/ai/INTEGRATION.md +239 -152
  5. package/ai/RECIPES.md +96 -26
  6. package/ai/USAGE.md +5 -5
  7. package/ai/registry.json +2883 -275
  8. package/dist/assets/brand-mark.js.map +1 -1
  9. package/dist/charts/area.js.map +1 -1
  10. package/dist/charts/bar.js.map +1 -1
  11. package/dist/charts/bullet.js.map +1 -1
  12. package/dist/charts/charts.js.map +1 -1
  13. package/dist/charts/config.d.ts +11 -11
  14. package/dist/charts/config.js +4 -4
  15. package/dist/charts/config.js.map +1 -1
  16. package/dist/charts/custom-chart.js.map +1 -1
  17. package/dist/charts/date-navigation.js +10 -10
  18. package/dist/charts/date-navigation.js.map +1 -1
  19. package/dist/charts/donut.js +1 -1
  20. package/dist/charts/donut.js.map +1 -1
  21. package/dist/charts/frame.d.ts +1 -1
  22. package/dist/charts/frame.js +4 -4
  23. package/dist/charts/frame.js.map +1 -1
  24. package/dist/charts/funnel.js.map +1 -1
  25. package/dist/charts/gauge.js +5 -5
  26. package/dist/charts/gauge.js.map +1 -1
  27. package/dist/charts/grouped-bar.js.map +1 -1
  28. package/dist/charts/heatmap-calendar.js +11 -11
  29. package/dist/charts/heatmap-calendar.js.map +1 -1
  30. package/dist/charts/heatmap-matrix.js +8 -8
  31. package/dist/charts/heatmap-matrix.js.map +1 -1
  32. package/dist/charts/item-list.js +1 -1
  33. package/dist/charts/item-list.js.map +1 -1
  34. package/dist/charts/legend.js +19 -19
  35. package/dist/charts/legend.js.map +1 -1
  36. package/dist/charts/line.js.map +1 -1
  37. package/dist/charts/palette.js +2 -2
  38. package/dist/charts/palette.js.map +1 -1
  39. package/dist/charts/radar.js.map +1 -1
  40. package/dist/charts/sample.js.map +1 -1
  41. package/dist/charts/sankey.js.map +1 -1
  42. package/dist/charts/series.js.map +1 -1
  43. package/dist/charts/slot-bar.js +2 -2
  44. package/dist/charts/slot-bar.js.map +1 -1
  45. package/dist/charts/sparkline.js +38 -23
  46. package/dist/charts/sparkline.js.map +1 -1
  47. package/dist/charts/stacked-bar.js.map +1 -1
  48. package/dist/charts/stat-card.d.ts +2 -2
  49. package/dist/charts/stat-card.js +61 -58
  50. package/dist/charts/stat-card.js.map +1 -1
  51. package/dist/charts/timeline.js +18 -9
  52. package/dist/charts/timeline.js.map +1 -1
  53. package/dist/charts/timeseries.js.map +1 -1
  54. package/dist/charts/tooltip.js +16 -16
  55. package/dist/charts/tooltip.js.map +1 -1
  56. package/dist/components/accordion.js +1 -1
  57. package/dist/components/accordion.js.map +1 -1
  58. package/dist/components/alert-dialog.js +4 -4
  59. package/dist/components/alert-dialog.js.map +1 -1
  60. package/dist/components/app-header.d.ts +5 -1
  61. package/dist/components/app-header.js +19 -11
  62. package/dist/components/app-header.js.map +1 -1
  63. package/dist/components/app-shell.d.ts +4 -1
  64. package/dist/components/app-shell.js +8 -5
  65. package/dist/components/app-shell.js.map +1 -1
  66. package/dist/components/approval-card.d.ts +13 -0
  67. package/dist/components/approval-card.js +161 -0
  68. package/dist/components/approval-card.js.map +1 -0
  69. package/dist/components/autocomplete.js.map +1 -1
  70. package/dist/components/avatar.d.ts +12 -0
  71. package/dist/components/avatar.js +39 -18
  72. package/dist/components/avatar.js.map +1 -1
  73. package/dist/components/badge.js +8 -8
  74. package/dist/components/badge.js.map +1 -1
  75. package/dist/components/banner.d.ts +2 -2
  76. package/dist/components/banner.js +9 -8
  77. package/dist/components/banner.js.map +1 -1
  78. package/dist/components/basalt-mark.js +1 -1
  79. package/dist/components/basalt-mark.js.map +1 -1
  80. package/dist/components/battery-meter.js +5 -5
  81. package/dist/components/battery-meter.js.map +1 -1
  82. package/dist/components/breadcrumbs.js +6 -6
  83. package/dist/components/breadcrumbs.js.map +1 -1
  84. package/dist/components/button.js +10 -10
  85. package/dist/components/button.js.map +1 -1
  86. package/dist/components/chat-bubble.js +4 -3
  87. package/dist/components/chat-bubble.js.map +1 -1
  88. package/dist/components/chat-composer.d.ts +21 -3
  89. package/dist/components/chat-composer.js +126 -60
  90. package/dist/components/chat-composer.js.map +1 -1
  91. package/dist/components/chat-header.js +6 -6
  92. package/dist/components/chat-header.js.map +1 -1
  93. package/dist/components/chat-inbox.js +34 -30
  94. package/dist/components/chat-inbox.js.map +1 -1
  95. package/dist/components/chat-markdown.d.ts +8 -0
  96. package/dist/components/chat-markdown.js +122 -0
  97. package/dist/components/chat-markdown.js.map +1 -0
  98. package/dist/components/chat-message.d.ts +26 -0
  99. package/dist/components/chat-message.js +145 -0
  100. package/dist/components/chat-message.js.map +1 -0
  101. package/dist/components/checkbox.js +11 -11
  102. package/dist/components/checkbox.js.map +1 -1
  103. package/dist/components/clipboard-text.js +3 -3
  104. package/dist/components/clipboard-text.js.map +1 -1
  105. package/dist/components/code.d.ts +22 -20
  106. package/dist/components/code.js +94 -32
  107. package/dist/components/code.js.map +1 -1
  108. package/dist/components/collapsible.js +8 -4
  109. package/dist/components/collapsible.js.map +1 -1
  110. package/dist/components/combobox.js.map +1 -1
  111. package/dist/components/command-palette.js +73 -47
  112. package/dist/components/command-palette.js.map +1 -1
  113. package/dist/components/confirm-dialog.d.ts +4 -1
  114. package/dist/components/confirm-dialog.js +20 -18
  115. package/dist/components/confirm-dialog.js.map +1 -1
  116. package/dist/components/context-cards.d.ts +15 -0
  117. package/dist/components/context-cards.js +148 -0
  118. package/dist/components/context-cards.js.map +1 -0
  119. package/dist/components/context-menu.d.ts +1 -1
  120. package/dist/components/context-menu.js +18 -14
  121. package/dist/components/context-menu.js.map +1 -1
  122. package/dist/components/data-table.d.ts +8 -2
  123. package/dist/components/data-table.js +161 -106
  124. package/dist/components/data-table.js.map +1 -1
  125. package/dist/components/date-picker.js +13 -13
  126. package/dist/components/date-picker.js.map +1 -1
  127. package/dist/components/delete-resource.js +1 -1
  128. package/dist/components/delete-resource.js.map +1 -1
  129. package/dist/components/description-list.js +3 -3
  130. package/dist/components/description-list.js.map +1 -1
  131. package/dist/components/dialog.d.ts +2 -2
  132. package/dist/components/dialog.js +8 -8
  133. package/dist/components/dialog.js.map +1 -1
  134. package/dist/components/diff-table.d.ts +15 -0
  135. package/dist/components/diff-table.js +146 -0
  136. package/dist/components/diff-table.js.map +1 -0
  137. package/dist/components/dock.js +2 -2
  138. package/dist/components/dock.js.map +1 -1
  139. package/dist/components/dropdown-menu.js +23 -18
  140. package/dist/components/dropdown-menu.js.map +1 -1
  141. package/dist/components/editable-nav-item.js +63 -58
  142. package/dist/components/editable-nav-item.js.map +1 -1
  143. package/dist/components/empty.js +6 -6
  144. package/dist/components/empty.js.map +1 -1
  145. package/dist/components/fab.js +1 -1
  146. package/dist/components/fab.js.map +1 -1
  147. package/dist/components/field.js +3 -3
  148. package/dist/components/field.js.map +1 -1
  149. package/dist/components/file-dropzone.js +7 -7
  150. package/dist/components/file-dropzone.js.map +1 -1
  151. package/dist/components/filter-bar.js +6 -6
  152. package/dist/components/filter-bar.js.map +1 -1
  153. package/dist/components/flow.js +4 -3
  154. package/dist/components/flow.js.map +1 -1
  155. package/dist/components/grid.d.ts +3 -1
  156. package/dist/components/grid.js +15 -8
  157. package/dist/components/grid.js.map +1 -1
  158. package/dist/components/hover-card.js +1 -1
  159. package/dist/components/hover-card.js.map +1 -1
  160. package/dist/components/icon-picker.d.ts +8 -2
  161. package/dist/components/icon-picker.js +75 -47
  162. package/dist/components/icon-picker.js.map +1 -1
  163. package/dist/components/inline-editable.js +6 -6
  164. package/dist/components/inline-editable.js.map +1 -1
  165. package/dist/components/input-area.js +3 -3
  166. package/dist/components/input-area.js.map +1 -1
  167. package/dist/components/input-group.js +5 -5
  168. package/dist/components/input-group.js.map +1 -1
  169. package/dist/components/input.js +4 -4
  170. package/dist/components/input.js.map +1 -1
  171. package/dist/components/label.js +3 -3
  172. package/dist/components/label.js.map +1 -1
  173. package/dist/components/layer-card.d.ts +9 -4
  174. package/dist/components/layer-card.js +72 -67
  175. package/dist/components/layer-card.js.map +1 -1
  176. package/dist/components/link.js.map +1 -1
  177. package/dist/components/loader.d.ts +16 -10
  178. package/dist/components/loader.js +46 -53
  179. package/dist/components/loader.js.map +1 -1
  180. package/dist/components/loading-screen.js +3 -3
  181. package/dist/components/loading-screen.js.map +1 -1
  182. package/dist/components/menu-bar.js +32 -27
  183. package/dist/components/menu-bar.js.map +1 -1
  184. package/dist/components/meter.d.ts +5 -5
  185. package/dist/components/meter.js +25 -12
  186. package/dist/components/meter.js.map +1 -1
  187. package/dist/components/multi-select.js +66 -62
  188. package/dist/components/multi-select.js.map +1 -1
  189. package/dist/components/navigation-menu.d.ts +2 -2
  190. package/dist/components/navigation-menu.js +22 -8
  191. package/dist/components/navigation-menu.js.map +1 -1
  192. package/dist/components/overlay.js +2 -2
  193. package/dist/components/overlay.js.map +1 -1
  194. package/dist/components/page-header.d.ts +3 -1
  195. package/dist/components/page-header.js +12 -12
  196. package/dist/components/page-header.js.map +1 -1
  197. package/dist/components/pagination.js +3 -3
  198. package/dist/components/pagination.js.map +1 -1
  199. package/dist/components/popover.js +18 -23
  200. package/dist/components/popover.js.map +1 -1
  201. package/dist/components/prompt-bar.d.ts +16 -0
  202. package/dist/components/prompt-bar.js +49 -0
  203. package/dist/components/prompt-bar.js.map +1 -0
  204. package/dist/components/radio.js +9 -9
  205. package/dist/components/radio.js.map +1 -1
  206. package/dist/components/recommendation-card.d.ts +13 -0
  207. package/dist/components/recommendation-card.js +123 -0
  208. package/dist/components/recommendation-card.js.map +1 -0
  209. package/dist/components/resource-list.js +1 -1
  210. package/dist/components/resource-list.js.map +1 -1
  211. package/dist/components/responsive-master-detail.js +7 -7
  212. package/dist/components/responsive-master-detail.js.map +1 -1
  213. package/dist/components/scroll-area.js +3 -3
  214. package/dist/components/scroll-area.js.map +1 -1
  215. package/dist/components/section-rule.d.ts +5 -3
  216. package/dist/components/section-rule.js +12 -12
  217. package/dist/components/section-rule.js.map +1 -1
  218. package/dist/components/segment-control.js +3 -3
  219. package/dist/components/segment-control.js.map +1 -1
  220. package/dist/components/select.js +47 -32
  221. package/dist/components/select.js.map +1 -1
  222. package/dist/components/sensitive-input.js +17 -19
  223. package/dist/components/sensitive-input.js.map +1 -1
  224. package/dist/components/separator.js.map +1 -1
  225. package/dist/components/sheet.d.ts +4 -4
  226. package/dist/components/sheet.js +12 -10
  227. package/dist/components/sheet.js.map +1 -1
  228. package/dist/components/sidebar.d.ts +3 -1
  229. package/dist/components/sidebar.js +150 -138
  230. package/dist/components/sidebar.js.map +1 -1
  231. package/dist/components/skeleton-line.d.ts +1 -1
  232. package/dist/components/skeleton-line.js +2 -2
  233. package/dist/components/skeleton-line.js.map +1 -1
  234. package/dist/components/slider.js +2 -2
  235. package/dist/components/slider.js.map +1 -1
  236. package/dist/components/stat-strip.js +5 -4
  237. package/dist/components/stat-strip.js.map +1 -1
  238. package/dist/components/switch.js +6 -6
  239. package/dist/components/switch.js.map +1 -1
  240. package/dist/components/table-of-contents.js +5 -5
  241. package/dist/components/table-of-contents.js.map +1 -1
  242. package/dist/components/table-pager.js +2 -2
  243. package/dist/components/table-pager.js.map +1 -1
  244. package/dist/components/table.js +4 -4
  245. package/dist/components/table.js.map +1 -1
  246. package/dist/components/tabs.js +7 -6
  247. package/dist/components/tabs.js.map +1 -1
  248. package/dist/components/tag-badge.d.ts +10 -10
  249. package/dist/components/tag-badge.js +11 -11
  250. package/dist/components/tag-badge.js.map +1 -1
  251. package/dist/components/tag-color-picker.d.ts +8 -1
  252. package/dist/components/tag-color-picker.js +33 -25
  253. package/dist/components/tag-color-picker.js.map +1 -1
  254. package/dist/components/text.js +5 -5
  255. package/dist/components/text.js.map +1 -1
  256. package/dist/components/theme-toggle.js +1 -1
  257. package/dist/components/theme-toggle.js.map +1 -1
  258. package/dist/components/thinking.d.ts +16 -0
  259. package/dist/components/thinking.js +85 -0
  260. package/dist/components/thinking.js.map +1 -0
  261. package/dist/components/toast.js +25 -20
  262. package/dist/components/toast.js.map +1 -1
  263. package/dist/components/toggle-group.js +6 -5
  264. package/dist/components/toggle-group.js.map +1 -1
  265. package/dist/components/toggle.js +6 -6
  266. package/dist/components/toggle.js.map +1 -1
  267. package/dist/components/tool-chips.d.ts +12 -0
  268. package/dist/components/tool-chips.js +138 -0
  269. package/dist/components/tool-chips.js.map +1 -0
  270. package/dist/components/toolbar.js +3 -3
  271. package/dist/components/toolbar.js.map +1 -1
  272. package/dist/components/tooltip.js +1 -1
  273. package/dist/components/tooltip.js.map +1 -1
  274. package/dist/components/typeahead-field.js +59 -56
  275. package/dist/components/typeahead-field.js.map +1 -1
  276. package/dist/components/upload-queue.js +15 -14
  277. package/dist/components/upload-queue.js.map +1 -1
  278. package/dist/models/agent-feedback.d.ts +44 -0
  279. package/dist/models/agent-feedback.js +28 -0
  280. package/dist/models/agent-feedback.js.map +1 -0
  281. package/dist/models/avatar.d.ts +2 -0
  282. package/dist/models/avatar.js +15 -0
  283. package/dist/models/avatar.js.map +1 -0
  284. package/dist/models/code.d.ts +6 -0
  285. package/dist/models/code.js +24 -0
  286. package/dist/models/code.js.map +1 -0
  287. package/dist/models/context-cards.d.ts +13 -0
  288. package/dist/models/context-cards.js +17 -0
  289. package/dist/models/context-cards.js.map +1 -0
  290. package/dist/models/diff-table.d.ts +12 -0
  291. package/dist/models/diff-table.js +9 -0
  292. package/dist/models/diff-table.js.map +1 -0
  293. package/dist/models/recommendation.d.ts +26 -0
  294. package/dist/models/recommendation.js +23 -0
  295. package/dist/models/recommendation.js.map +1 -0
  296. package/dist/providers/accent.js +1 -1
  297. package/dist/providers/accent.js.map +1 -1
  298. package/dist/providers/link.js.map +1 -1
  299. package/dist/providers/theme.js +1 -1
  300. package/dist/providers/theme.js.map +1 -1
  301. package/dist/styles/base.css +128 -0
  302. package/dist/styles/standalone.css +1994 -937
  303. package/dist/styles/tailwind.css +179 -13
  304. package/dist/styles/tokens.css +679 -20
  305. package/dist/utils/cn.js +25 -4
  306. package/dist/utils/cn.js.map +1 -1
  307. package/dist/utils/control-surface.d.ts +1 -1
  308. package/dist/utils/control-surface.js +1 -1
  309. package/dist/utils/control-surface.js.map +1 -1
  310. package/dist/utils/navigation.d.ts +3 -0
  311. package/dist/utils/navigation.js +8 -0
  312. package/dist/utils/navigation.js.map +1 -0
  313. package/dist/utils/preference-store.js +1 -1
  314. package/dist/utils/preference-store.js.map +1 -1
  315. package/dist/utils/selection-indicator.d.ts +1 -1
  316. package/dist/utils/selection-indicator.js +49 -72
  317. package/dist/utils/selection-indicator.js.map +1 -1
  318. package/dist/utils/use-checkable-group.js.map +1 -1
  319. package/dist/utils/use-hover-highlight.d.ts +4 -0
  320. package/dist/utils/use-hover-highlight.js +80 -0
  321. package/dist/utils/use-hover-highlight.js.map +1 -0
  322. package/dist/viewmodels/use-approval-card.d.ts +20 -0
  323. package/dist/viewmodels/use-approval-card.js +83 -0
  324. package/dist/viewmodels/use-approval-card.js.map +1 -0
  325. package/dist/viewmodels/use-chat-composer.d.ts +16 -0
  326. package/dist/viewmodels/use-chat-composer.js +34 -0
  327. package/dist/viewmodels/use-chat-composer.js.map +1 -0
  328. package/dist/viewmodels/use-chat-message.d.ts +16 -0
  329. package/dist/viewmodels/use-chat-message.js +26 -0
  330. package/dist/viewmodels/use-chat-message.js.map +1 -0
  331. package/dist/viewmodels/use-code-block.d.ts +8 -0
  332. package/dist/viewmodels/use-code-block.js +38 -0
  333. package/dist/viewmodels/use-code-block.js.map +1 -0
  334. package/dist/viewmodels/use-context-cards.d.ts +17 -0
  335. package/dist/viewmodels/use-context-cards.js +18 -0
  336. package/dist/viewmodels/use-context-cards.js.map +1 -0
  337. package/dist/viewmodels/use-diff-table.d.ts +16 -0
  338. package/dist/viewmodels/use-diff-table.js +42 -0
  339. package/dist/viewmodels/use-diff-table.js.map +1 -0
  340. package/dist/viewmodels/use-loader.d.ts +4 -0
  341. package/dist/viewmodels/use-loader.js +22 -0
  342. package/dist/viewmodels/use-loader.js.map +1 -0
  343. package/dist/viewmodels/use-recommendation-card.d.ts +17 -0
  344. package/dist/viewmodels/use-recommendation-card.js +41 -0
  345. package/dist/viewmodels/use-recommendation-card.js.map +1 -0
  346. package/dist/viewmodels/use-thinking.d.ts +13 -0
  347. package/dist/viewmodels/use-thinking.js +18 -0
  348. package/dist/viewmodels/use-thinking.js.map +1 -0
  349. package/dist/viewmodels/use-tool-chips.d.ts +10 -0
  350. package/dist/viewmodels/use-tool-chips.js +20 -0
  351. package/dist/viewmodels/use-tool-chips.js.map +1 -0
  352. package/package.json +3 -1
package/ai/RECIPES.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Application recipes
2
2
 
3
- These complete modules, included in v2.1.0, use only public Basalt imports and React.
3
+ These complete modules use only public Basalt imports and React. AppFrame, Login and Resources are available since v2.1.0; MobileLayout requires v2.2.0.
4
4
 
5
5
  Load exactly one CSS mode at the application entry: import `@nocoo/basalt/styles/standalone` for a build without Tailwind, or configure the Tailwind source scan and import `@nocoo/basalt/styles/tailwind` as described in INTEGRATION.md. In Next, import CSS in the root layout and import these modules through a `use client` boundary. Mount ThemeProvider once per application; the standalone recipes include their own root provider. Root pages should set body margin to zero.
6
6
 
@@ -16,7 +16,7 @@ import { AppMain, AppShell, AppSkipLink } from "@nocoo/basalt/components/app-she
16
16
  import { Button } from "@nocoo/basalt/components/button";
17
17
  import { DialogDescription, DialogTitle } from "@nocoo/basalt/components/dialog";
18
18
  import { PageHeader } from "@nocoo/basalt/components/page-header";
19
- import { Sidebar, SidebarHeader, SidebarItem, SidebarNav, SidebarProvider } from "@nocoo/basalt/components/sidebar";
19
+ import { ContentIsland, Sidebar, SidebarHeader, SidebarItem, SidebarNav, SidebarProvider } from "@nocoo/basalt/components/sidebar";
20
20
  import { ThemeToggle } from "@nocoo/basalt/components/theme-toggle";
21
21
  import { ThemeProvider } from "@nocoo/basalt/providers/theme";
22
22
  import { useEffect, useState } from "react";
@@ -34,9 +34,10 @@ export default function AppFrameRecipe() {
34
34
  }, []);
35
35
  // Replace this local state change with your router's navigation adapter.
36
36
  function navigate(next: string) { setPage(next); if (compact) setCollapsed(true); }
37
- return <ThemeProvider><div style={{ position: "fixed", inset: 0 }}>
37
+ return <ThemeProvider>
38
38
  <SidebarProvider collapsed={collapsed} onCollapsedChange={setCollapsed} overlay={compact}>
39
- <AppShell>
39
+ {/* One bounded viewport; the island below owns its own scrolling. */}
40
+ <AppShell layout="workspace" className="h-dvh">
40
41
  <AppSkipLink href="#recipe-main">Skip to content</AppSkipLink>
41
42
  <Sidebar>
42
43
  {compact && <><DialogTitle className="sr-only">Workspace navigation</DialogTitle><DialogDescription className="sr-only">Choose an application section.</DialogDescription></>}
@@ -46,15 +47,15 @@ export default function AppFrameRecipe() {
46
47
  </SidebarNav>
47
48
  </Sidebar>
48
49
  <AppMain id="recipe-main" tabIndex={-1}>
49
- <AppHeader leading={<Button size="sm" variant="ghost" aria-label="Toggle navigation" onClick={() => setCollapsed(!collapsed)}>Menu</Button>} actions={<ThemeToggle aria-label="Change theme" />} />
50
- <div style={{ overflow: "auto", flex: 1, padding: 24 }}>
50
+ <AppHeader leading={<Button size="sm" variant="ghost" aria-label="Toggle navigation" onClick={(event) => { event.currentTarget.focus(); setCollapsed(!collapsed); }}>Menu</Button>} actions={<ThemeToggle aria-label="Change theme" />} />
51
+ <ContentIsland className="space-y-basalt-layout-lg">
51
52
  <PageHeader title={page} description="A reusable application frame with responsive navigation." />
52
- <p role="status" className="mt-4 text-sm text-basalt-muted-foreground">Opened {page}. Your application supplies routes and page content.</p>
53
- </div>
53
+ <p role="status" className="text-basalt-sm text-basalt-muted-foreground">Opened {page}. Your application supplies routes and page content.</p>
54
+ </ContentIsland>
54
55
  </AppMain>
55
56
  </AppShell>
56
57
  </SidebarProvider>
57
- </div></ThemeProvider>;
58
+ </ThemeProvider>;
58
59
  }
59
60
  ```
60
61
 
@@ -79,7 +80,7 @@ export function LoginForm({ authenticate, onSuccess }: {
79
80
  const [error, setError] = useState("");
80
81
  const active = useRef<AbortController | null>(null);
81
82
  useEffect(() => () => active.current?.abort(), []);
82
- return <form aria-label="Sign in" className="space-y-4" onSubmit={async (event) => {
83
+ return <form aria-label="Sign in" className="space-y-basalt-space-lg" onSubmit={async (event) => {
83
84
  event.preventDefault();
84
85
  if (active.current) return;
85
86
  const form = new FormData(event.currentTarget);
@@ -98,21 +99,37 @@ export function LoginForm({ authenticate, onSuccess }: {
98
99
  }}>
99
100
  <Field label="Email"><Input name="email" type="email" autoComplete="username" required /></Field>
100
101
  <Field label="Password"><SensitiveInput name="password" autoComplete="current-password" revealLabel="Show password" hideLabel="Hide password" required /></Field>
101
- {error && <p role="alert" className="text-sm text-basalt-destructive">{error}</p>}
102
+ {error && <p role="alert" className="text-basalt-sm text-basalt-destructive">{error}</p>}
102
103
  <Button className="w-full" type="submit" loading={pending}>Sign in</Button>
103
104
  </form>;
104
105
  }
105
106
 
107
+ // The documented visitor badge: a full-viewport root, a primary strip, the body,
108
+ // and a status footer. It never enters AppShell, and the body grows with the form.
106
109
  export default function LoginRecipe() {
107
110
  const [account, setAccount] = useState<string | null>(null);
108
- return <ThemeProvider><main style={{ boxSizing: "border-box", minHeight: "100dvh", display: "grid", placeItems: "center", padding: 16 }}>
109
- <div className="rounded-basalt-lg border border-basalt-border bg-basalt-card p-6 space-y-4" style={{ boxSizing: "border-box", width: "100%", maxWidth: 380 }}>
110
- <h1 className="text-xl font-semibold">Welcome to Atlas</h1>
111
- {account ? <><p role="status">Signed in as {account}</p><Button onClick={() => setAccount(null)}>Sign out</Button></> :
112
- <><p className="text-sm text-basalt-muted-foreground">Local sign-in demo. Use password demo-pass.</p>
113
- <LoginForm authenticate={async ({ password }) => { if (password !== "demo-pass") throw new Error("Incorrect demo password."); }} onSuccess={setAccount} /></>}
114
- </div>
115
- </main></ThemeProvider>;
111
+ return <ThemeProvider>
112
+ <main className="basalt-ui flex min-h-dvh items-center justify-center bg-basalt-background p-basalt-space-lg">
113
+ <div data-basalt-surface-root="" className="flex w-72 max-w-full flex-col overflow-hidden rounded-basalt-lg bg-basalt-card ring-1 ring-basalt-border">
114
+ <div className="flex items-center justify-between gap-basalt-space-lg bg-basalt-primary px-basalt-space-lg py-basalt-space-lg">
115
+ <span className="text-basalt-base font-semibold text-basalt-primary-foreground">Atlas</span>
116
+ <span className="text-basalt-xs font-medium uppercase tracking-widest text-basalt-primary-foreground/80">Visitor</span>
117
+ </div>
118
+ <div className="flex flex-1 flex-col gap-basalt-space-lg px-basalt-space-lg py-basalt-space-lg">
119
+ <div className="space-y-basalt-space-sm">
120
+ <h1 className="text-basalt-xl font-semibold text-basalt-foreground">Welcome to Atlas</h1>
121
+ <p className="text-basalt-sm text-basalt-muted-foreground">Local sign-in demo. Use password demo-pass.</p>
122
+ </div>
123
+ {account ? <><p role="status" className="text-basalt-sm text-basalt-foreground">Signed in as {account}</p>
124
+ <Button variant="outline" onClick={() => setAccount(null)}>Sign out</Button></> :
125
+ <LoginForm authenticate={async ({ password }) => { if (password !== "demo-pass") throw new Error("Incorrect demo password."); }} onSuccess={setAccount} />}
126
+ </div>
127
+ <div className="border-t border-basalt-border px-basalt-space-lg py-basalt-space-lg">
128
+ <p className="text-basalt-xs text-basalt-muted-foreground">Secure sign-in. Sessions stay with your application.</p>
129
+ </div>
130
+ </div>
131
+ </main>
132
+ </ThemeProvider>;
116
133
  }
117
134
  ```
118
135
 
@@ -121,11 +138,13 @@ export default function LoginRecipe() {
121
138
  Search, an error with retry, formatted status cells, and a confirmation flow use page-owned state. The first local deletion fails so the retry path can be tested. For remote pagination use the existing DataTable manual-state adapter.
122
139
 
123
140
  ```tsx compile:recipe-resources
141
+ import { AppMain, AppShell, AppSkipLink } from "@nocoo/basalt/components/app-shell";
124
142
  import { Button } from "@nocoo/basalt/components/button";
125
143
  import { DataTable, type DataTableColumn } from "@nocoo/basalt/components/data-table";
126
144
  import { DeleteResource } from "@nocoo/basalt/components/delete-resource";
127
145
  import { Input } from "@nocoo/basalt/components/input";
128
146
  import { ResourceList } from "@nocoo/basalt/components/resource-list";
147
+ import { ContentIsland } from "@nocoo/basalt/components/sidebar";
129
148
  import { TagBadge } from "@nocoo/basalt/components/tag-badge";
130
149
  import { ThemeProvider } from "@nocoo/basalt/providers/theme";
131
150
  import { useRef, useState } from "react";
@@ -150,12 +169,63 @@ export default function ResourcesRecipe() {
150
169
  setRows((current) => current.filter((item) => item.id !== row.id)); setMessage("Deleted " + row.name);
151
170
  }} /> },
152
171
  ];
153
- return <ThemeProvider><main style={{ padding: 24, maxWidth: 960, margin: "auto" }}>
154
- <ResourceList title="Projects" description="A complete resource-page composition with local service adapters." data={[]}
155
- toolbar={<div className="flex flex-wrap gap-3"><Input aria-label="Search projects" placeholder="Search projects…" value={query} onChange={(event) => setQuery(event.target.value)} /><Button variant="outline" onClick={() => { setRows(initial); setQuery(""); setMessage(""); }}>Reset projects</Button></div>}
156
- footer={<p role="status" className="text-sm text-basalt-muted-foreground">{message || (filtered.length + " projects")}</p>}>
157
- <DataTable aria-label="Projects" data={filtered} columns={columns} error={error ? "The project service is unavailable." : undefined} onRetry={() => setError(false)} />
158
- </ResourceList>
159
- </main></ThemeProvider>;
172
+ // A document page: the shell grows with content and the island owns the desktop inset.
173
+ return <ThemeProvider><AppShell layout="document">
174
+ <AppSkipLink href="#recipe-resources">Skip to content</AppSkipLink>
175
+ <AppMain id="recipe-resources" tabIndex={-1}>
176
+ <ContentIsland>
177
+ <ResourceList title="Projects" description="A complete resource-page composition with local service adapters." data={[]}
178
+ toolbar={<div className="flex flex-wrap gap-basalt-content-gap"><Input aria-label="Search projects" placeholder="Search projects…" value={query} onChange={(event) => setQuery(event.target.value)} /><Button variant="outline" onClick={() => { setRows(initial); setQuery(""); setMessage(""); }}>Reset projects</Button></div>}
179
+ footer={<p role="status" className="text-basalt-sm text-basalt-muted-foreground">{message || (filtered.length + " projects")}</p>}>
180
+ <DataTable aria-label="Projects" data={filtered} columns={columns} error={error ? "The project service is unavailable." : undefined} onRetry={() => setError(false)} />
181
+ </ResourceList>
182
+ </ContentIsland>
183
+ </AppMain>
184
+ </AppShell></ThemeProvider>;
185
+ }
186
+ ```
187
+
188
+ ## Mobile layouts
189
+
190
+ Use `responsive` for mobile root scrolling and desktop island scrolling; `document` keeps root scrolling at every width, while `workspace` remains bounded everywhere. Select this per layout, not by user-agent. Place the compact sticky header inside the edge-to-edge island for a single immersive row. Keep a normal PageHeader in a desktop-only wrapper when it adds context. The outer wrapper must not retain mobile `px-2 pb-2`; the island cannot remove its parent's spacing.
191
+
192
+ This complete recipe compiles from the installed package. Supply content and actions from the application; there are no routing, article, account or network assumptions. The consumer owns a zero-margin body and viewport metadata (`width=device-width, initial-scale=1, viewport-fit=cover`). Do not lock body scrolling or add height/overflow constraints to intermediate wrappers. The shell handles safe areas; the sticky header paints the notch gap without charging the initial top inset twice. Bottom safe area follows the last content, not a fixed footer strip.
193
+
194
+ ```tsx compile:recipe-mobile-layout
195
+ import { AppHeader } from "@nocoo/basalt/components/app-header";
196
+ import { AppMain, AppShell, AppSkipLink } from "@nocoo/basalt/components/app-shell";
197
+ import { Button } from "@nocoo/basalt/components/button";
198
+ import { Popover, PopoverContent, PopoverTrigger } from "@nocoo/basalt/components/popover";
199
+ import { ContentIsland } from "@nocoo/basalt/components/sidebar";
200
+ import { ThemeProvider } from "@nocoo/basalt/providers/theme";
201
+
202
+ export default function MobileLayoutRecipe() {
203
+ return <ThemeProvider><AppShell layout="responsive">
204
+ <AppSkipLink>Skip to content</AppSkipLink>
205
+ <AppMain>
206
+ <ContentIsland mobileSurface="edge-to-edge">
207
+ <AppHeader sticky density="compact" title="Field notes" actions={
208
+ <Popover><PopoverTrigger asChild><Button variant="ghost">Options</Button></PopoverTrigger>
209
+ <PopoverContent aria-label="Options"><p>Application-owned reading preferences.</p></PopoverContent>
210
+ </Popover>
211
+ } />
212
+ <article className="mx-auto max-w-[65ch] space-y-basalt-space-lg p-basalt-layout text-basalt-base leading-basalt-relaxed">
213
+ {Array.from({ length: 24 }, (_, index) => <section key={index}>
214
+ <h2>Observation {index + 1}</h2>
215
+ <p>The shell leaves the document in charge on a narrow screen. Content grows naturally, the header stays reachable, and the final paragraph remains above the trailing safe area.</p>
216
+ </section>)}
217
+ <p>End of notes.</p>
218
+ </article>
219
+ </ContentIsland>
220
+ </AppMain>
221
+ </AppShell></ThemeProvider>;
160
222
  }
161
223
  ```
224
+
225
+ ### List/detail and form boundaries
226
+
227
+ - Full-page examples: [reader](https://basaltui.com/examples/reader), [list/detail](https://basaltui.com/examples/list-detail), [workspace/form](https://basaltui.com/examples/workspace). Open them outside another AppShell; do not nest viewport shells.
228
+ - Use `ResponsiveMasterDetail` inside a `ContentIsland` with `className="md:overflow-hidden"`, and give the composition `className="md:h-full"`. Its desktop panes scroll independently; below 768px only the visible pane participates in document layout. The application owns selection. Built-in back navigation restores focus to the opening item, with a region fallback if it no longer exists.
229
+ - Keep `mobileSurface="inset"` for regular forms. Use native labels, appropriate input types and at least 44px controls; 16px input text avoids the common small-text zoom problem without disabling user zoom. Browser scrolling brings focused fields into view; never reposition the viewport with innerHeight.
230
+ - Modal `Sheet`/overlay `Sidebar` owns focus trapping, Escape, focus restoration and background scroll locking. Do not add a second manual body lock. An expanded Sidebar inside a Sheet stays bounded and owns the portal's safe-area padding once.
231
+ - Automated Chromium/WebKit evidence covers geometry, document/pane scroll owners, resize, focus and overlays. Real iPhone Safari toolbar collapse, notch behavior during toolbar transitions, and software keyboard reachability require a physical-device check; desktop WebKit is not that proof.
package/ai/USAGE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Basalt AI & Machine Usage Guide
2
2
 
3
- This document describes how AI code assistants, automated agents, and developers can consume `@nocoo/basalt` (v2.1.8).
3
+ This document describes how AI code assistants, automated agents, and developers can consume `@nocoo/basalt` (v3.0.0).
4
4
 
5
5
  ---
6
6
 
@@ -12,9 +12,9 @@ The complete, machine-readable component registry and API surface is published w
12
12
  Document anchors formatted as `ai/registry.json#<slug>` reference specific catalog entries within the `catalogEntries` array by matching the `slug` property (e.g. `ai/registry.json#button` matches the entry where `slug === "button"`).
13
13
 
14
14
  It contains:
15
- - **122 Exported Modules** (1 root barrel, components, charts, providers)
16
- - **111 Ready Catalog Entries** with full API definitions
17
- - **741 Public Symbols** (395 runtime values, 346 TypeScript types)
15
+ - **131 Exported Modules** (1 root barrel, components, charts, providers)
16
+ - **120 Ready Catalog Entries** with full API definitions
17
+ - **776 Public Symbols** (405 runtime values, 371 TypeScript types)
18
18
  - **Detailed Component API**: Props, types, defaults, descriptions, and function callable signatures (including all `toast()` variants)
19
19
  - **Exact Peer Dependency Closures**: Identifies optional peers required per subpath (e.g., Recharts)
20
20
 
@@ -94,6 +94,6 @@ export function App() {
94
94
 
95
95
  ## 6. Application recipes and palette preferences
96
96
 
97
- Read [RECIPES.md](RECIPES.md) for complete AppFrame, Login, and Resources modules. Each module is compiled and exercised from the installed tarball in standalone, Tailwind, and Next.js consumers. These recipes keep authentication, routing, data and network requests in the application.
97
+ Read [RECIPES.md](RECIPES.md) for complete AppFrame, Login, Resources, and MobileLayout modules. Each module is compiled and exercised from the installed tarball in standalone, Tailwind, and Next.js consumers. These recipes keep authentication, routing, data and network requests in the application.
98
98
 
99
99
  Read [INTEGRATION.md#accent-provider](INTEGRATION.md#accent-provider) for the twelve control accents, optional `paletteOverrides`, and the fixed five-color chart cycle matching the classic candy swatches with its existing gray retained. Check [COMPATIBILITY.md](COMPATIBILITY.md) before migrating old color indices or stored IDs. The catalog at https://basaltui.com/palette provides an application-owned editor with localStorage persistence.