@nocoo/basalt 3.2.1 → 3.2.4
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.
- package/README.md +25 -19
- package/ai/COMPATIBILITY.md +41 -36
- package/ai/DESIGN.md +53 -47
- package/ai/INTEGRATION.md +814 -820
- package/ai/RECIPES.md +334 -135
- package/ai/USAGE.md +2 -2
- package/ai/registry.json +384 -27
- package/ai/sources.json +2 -2
- package/dist/assets/brand-mark.js +1 -2
- package/dist/charts/area.js +1 -2
- package/dist/charts/bar.js +1 -2
- package/dist/charts/bullet.js +1 -2
- package/dist/charts/chart-colors.js +1 -2
- package/dist/charts/charts.js +1 -2
- package/dist/charts/config.js +1 -2
- package/dist/charts/custom-chart.js +1 -2
- package/dist/charts/date-navigation.js +1 -2
- package/dist/charts/donut.js +1 -2
- package/dist/charts/frame.js +1 -2
- package/dist/charts/frame.js.map +1 -1
- package/dist/charts/funnel.js +1 -2
- package/dist/charts/gauge.js +1 -2
- package/dist/charts/grouped-bar.js +1 -2
- package/dist/charts/heatmap-calendar.js +3 -7
- package/dist/charts/heatmap-calendar.js.map +1 -1
- package/dist/charts/heatmap-matrix.js +1 -2
- package/dist/charts/item-list.js +1 -2
- package/dist/charts/legend.js +1 -2
- package/dist/charts/line.js +1 -2
- package/dist/charts/palette.js +1 -2
- package/dist/charts/radar.js +1 -2
- package/dist/charts/sample.js +1 -2
- package/dist/charts/sankey.js +1 -2
- package/dist/charts/series.js +1 -2
- package/dist/charts/slot-bar.js +1 -2
- package/dist/charts/sparkline.js +1 -2
- package/dist/charts/stacked-bar.js +1 -2
- package/dist/charts/stat-card.js +1 -2
- package/dist/charts/timeline.js +1 -2
- package/dist/charts/timeseries.js +1 -2
- package/dist/charts/tooltip.js +31 -27
- package/dist/charts/tooltip.js.map +1 -1
- package/dist/components/accordion.js +1 -2
- package/dist/components/alert-dialog.js +1 -2
- package/dist/components/app-header.js +1 -2
- package/dist/components/app-shell.js +1 -2
- package/dist/components/approval-card.js +1 -2
- package/dist/components/autocomplete.js +1 -2
- package/dist/components/avatar.js +1 -2
- package/dist/components/badge.js +1 -2
- package/dist/components/banner.js +1 -2
- package/dist/components/basalt-mark.js +1 -2
- package/dist/components/battery-meter.js +1 -2
- package/dist/components/breadcrumbs.js +1 -2
- package/dist/components/button.d.ts +1 -1
- package/dist/components/button.js +3 -3
- package/dist/components/button.js.map +1 -1
- package/dist/components/chat-bubble.js +1 -2
- package/dist/components/chat-composer.js +1 -2
- package/dist/components/chat-header.js +1 -2
- package/dist/components/chat-inbox.js +1 -2
- package/dist/components/chat-markdown.js +1 -2
- package/dist/components/chat-message.js +1 -2
- package/dist/components/checkbox.js +1 -2
- package/dist/components/clipboard-text.js +1 -2
- package/dist/components/code.js +1 -2
- package/dist/components/collapsible.js +1 -2
- package/dist/components/collapsible.js.map +1 -1
- package/dist/components/combobox.js +1 -2
- package/dist/components/command-palette.js +1 -2
- package/dist/components/confirm-dialog.js +3 -12
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/context-cards.js +1 -2
- package/dist/components/context-menu.js +1 -2
- package/dist/components/data-table.js +2 -7
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.js +7 -18
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/delete-resource.js +1 -2
- package/dist/components/description-list.js +1 -2
- package/dist/components/dialog.js +1 -2
- package/dist/components/diff-table.js +1 -2
- package/dist/components/dock.js +1 -2
- package/dist/components/dock.js.map +1 -1
- package/dist/components/dropdown-menu.js +1 -2
- package/dist/components/editable-nav-item.js +1 -2
- package/dist/components/empty.js +1 -2
- package/dist/components/fab.js +1 -2
- package/dist/components/field.js +1 -2
- package/dist/components/file-dropzone.js +1 -2
- package/dist/components/filter-bar.js +1 -2
- package/dist/components/flow.js +1 -2
- package/dist/components/grid.js +1 -2
- package/dist/components/hover-card.js +1 -2
- package/dist/components/icon-picker.js +1 -2
- package/dist/components/inline-editable.js +1 -2
- package/dist/components/input-area.js +1 -2
- package/dist/components/input-group.js +2 -3
- package/dist/components/input-group.js.map +1 -1
- package/dist/components/input.js +1 -2
- package/dist/components/label.js +1 -2
- package/dist/components/layer-card.js +1 -2
- package/dist/components/link.js +1 -2
- package/dist/components/loader.js +1 -2
- package/dist/components/loading-screen.js +1 -2
- package/dist/components/menu-bar.js +1 -2
- package/dist/components/meter.js +1 -2
- package/dist/components/multi-select.js +1 -2
- package/dist/components/navigation-menu.js +1 -2
- package/dist/components/overlay.js +1 -2
- package/dist/components/page-header.js +1 -2
- package/dist/components/pagination.js +1 -2
- package/dist/components/popover.js +1 -2
- package/dist/components/prompt-bar.js +1 -2
- package/dist/components/radio.js +2 -7
- package/dist/components/radio.js.map +1 -1
- package/dist/components/recommendation-card.js +1 -2
- package/dist/components/resource-list.js +1 -2
- package/dist/components/responsive-master-detail.js +1 -2
- package/dist/components/scroll-area.js +1 -2
- package/dist/components/section-rule.js +1 -2
- package/dist/components/segment-control.js +1 -2
- package/dist/components/select.js +1 -2
- package/dist/components/sensitive-input.js +1 -2
- package/dist/components/separator.js +1 -2
- package/dist/components/sheet.js +1 -2
- package/dist/components/sidebar.d.ts +53 -19
- package/dist/components/sidebar.js +201 -100
- package/dist/components/sidebar.js.map +1 -1
- package/dist/components/skeleton-line.js +1 -2
- package/dist/components/slider.js +1 -2
- package/dist/components/stat-strip.js +1 -2
- package/dist/components/switch.js +1 -2
- package/dist/components/table-of-contents.js +1 -2
- package/dist/components/table-pager.js +1 -2
- package/dist/components/table.js +1 -2
- package/dist/components/tabs.js +1 -2
- package/dist/components/tag-badge.js +1 -2
- package/dist/components/tag-color-picker.js +1 -2
- package/dist/components/text.js +1 -2
- package/dist/components/theme-toggle.js +1 -2
- package/dist/components/thinking.js +1 -2
- package/dist/components/toast.js +1 -2
- package/dist/components/toggle-group.js +1 -2
- package/dist/components/toggle.js +1 -2
- package/dist/components/tool-chips.js +1 -2
- package/dist/components/toolbar.js +1 -2
- package/dist/components/tooltip.js +1 -2
- package/dist/components/typeahead-field.js +6 -21
- package/dist/components/typeahead-field.js.map +1 -1
- package/dist/components/upload-queue.js +1 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +11 -11
- package/dist/models/agent-feedback.js +1 -2
- package/dist/models/avatar.js +1 -2
- package/dist/models/code.js +1 -2
- package/dist/models/context-cards.js +1 -2
- package/dist/models/diff-table.js +1 -2
- package/dist/models/recommendation.js +1 -2
- package/dist/providers/accent.js +2 -7
- package/dist/providers/accent.js.map +1 -1
- package/dist/providers/link.js +1 -2
- package/dist/providers/theme.js +2 -7
- package/dist/providers/theme.js.map +1 -1
- package/dist/styles/standalone.css +55 -11
- package/dist/styles/tailwind.css +2 -0
- package/dist/styles/tokens.css +51 -6
- package/dist/utils/cn.js +1 -2
- package/dist/utils/control-surface.js +1 -2
- package/dist/utils/navigation.d.ts +1 -1
- package/dist/utils/navigation.js +2 -3
- package/dist/utils/navigation.js.map +1 -1
- package/dist/utils/preference-store.js +1 -2
- package/dist/utils/selection-indicator.js +1 -2
- package/dist/utils/use-checkable-group.js +1 -2
- package/dist/utils/use-hover-highlight.js +2 -3
- package/dist/utils/use-hover-highlight.js.map +1 -1
- package/dist/viewmodels/use-approval-card.js +11 -13
- package/dist/viewmodels/use-approval-card.js.map +1 -1
- package/dist/viewmodels/use-chat-composer.js +1 -2
- package/dist/viewmodels/use-chat-message.js +1 -2
- package/dist/viewmodels/use-code-block.js +1 -2
- package/dist/viewmodels/use-code-block.js.map +1 -1
- package/dist/viewmodels/use-context-cards.js +1 -2
- package/dist/viewmodels/use-diff-table.js +1 -2
- package/dist/viewmodels/use-diff-table.js.map +1 -1
- package/dist/viewmodels/use-loader.js +1 -2
- package/dist/viewmodels/use-recommendation-card.js +1 -2
- package/dist/viewmodels/use-recommendation-card.js.map +1 -1
- package/dist/viewmodels/use-thinking.js +1 -2
- package/dist/viewmodels/use-tool-chips.js +1 -2
- package/package.json +1 -1
package/ai/RECIPES.md
CHANGED
|
@@ -16,46 +16,107 @@ 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 {
|
|
19
|
+
import {
|
|
20
|
+
ContentIsland,
|
|
21
|
+
Sidebar,
|
|
22
|
+
SidebarBrand,
|
|
23
|
+
SidebarHeader,
|
|
24
|
+
SidebarItem,
|
|
25
|
+
SidebarNav,
|
|
26
|
+
SidebarProvider,
|
|
27
|
+
SidebarToggle,
|
|
28
|
+
} from "@nocoo/basalt/components/sidebar";
|
|
20
29
|
import { ThemeToggle } from "@nocoo/basalt/components/theme-toggle";
|
|
21
30
|
import { ThemeProvider } from "@nocoo/basalt/providers/theme";
|
|
22
31
|
import { useEffect, useState } from "react";
|
|
23
32
|
|
|
24
33
|
export default function AppFrameRecipe() {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
34
|
+
const [page, setPage] = useState("Projects");
|
|
35
|
+
const [compact, setCompact] = useState(false);
|
|
36
|
+
const [collapsed, setCollapsed] = useState(true);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const media = matchMedia("(max-width: 767px)");
|
|
39
|
+
const update = () => {
|
|
40
|
+
setCompact(media.matches);
|
|
41
|
+
setCollapsed(media.matches);
|
|
42
|
+
};
|
|
43
|
+
update();
|
|
44
|
+
media.addEventListener("change", update);
|
|
45
|
+
return () => media.removeEventListener("change", update);
|
|
46
|
+
}, []);
|
|
47
|
+
// Replace this local state change with your router's navigation adapter.
|
|
48
|
+
function navigate(next: string) {
|
|
49
|
+
setPage(next);
|
|
50
|
+
if (compact) setCollapsed(true);
|
|
51
|
+
}
|
|
52
|
+
return (
|
|
53
|
+
<ThemeProvider>
|
|
54
|
+
<SidebarProvider collapsed={collapsed} onCollapsedChange={setCollapsed} overlay={compact}>
|
|
55
|
+
{/* One bounded viewport; the island below owns its own scrolling. */}
|
|
56
|
+
<AppShell layout="workspace" className="h-dvh">
|
|
57
|
+
<AppSkipLink href="#recipe-main">Skip to content</AppSkipLink>
|
|
58
|
+
<Sidebar>
|
|
59
|
+
{compact && (
|
|
60
|
+
<>
|
|
61
|
+
<DialogTitle className="sr-only">Workspace navigation</DialogTitle>
|
|
62
|
+
<DialogDescription className="sr-only">
|
|
63
|
+
Choose an application section.
|
|
64
|
+
</DialogDescription>
|
|
65
|
+
</>
|
|
66
|
+
)}
|
|
67
|
+
<SidebarHeader>
|
|
68
|
+
<SidebarBrand
|
|
69
|
+
logo={
|
|
70
|
+
<span
|
|
71
|
+
className="flex size-basalt-sidebar-brand-slot items-center justify-center"
|
|
72
|
+
aria-hidden="true"
|
|
73
|
+
>
|
|
74
|
+
A
|
|
75
|
+
</span>
|
|
76
|
+
}
|
|
77
|
+
name="Atlas workspace"
|
|
78
|
+
action={<SidebarToggle />}
|
|
79
|
+
/>
|
|
80
|
+
</SidebarHeader>
|
|
81
|
+
<SidebarNav aria-label="Workspace navigation">
|
|
82
|
+
{["Projects", "Members", "Activity"].map((name) => (
|
|
83
|
+
<SidebarItem key={name} active={page === name} onClick={() => navigate(name)}>
|
|
84
|
+
{name}
|
|
85
|
+
</SidebarItem>
|
|
86
|
+
))}
|
|
87
|
+
</SidebarNav>
|
|
88
|
+
</Sidebar>
|
|
89
|
+
<AppMain id="recipe-main" tabIndex={-1}>
|
|
90
|
+
<AppHeader
|
|
91
|
+
leading={
|
|
92
|
+
<Button
|
|
93
|
+
size="sm"
|
|
94
|
+
variant="ghost"
|
|
95
|
+
aria-label="Toggle navigation"
|
|
96
|
+
onClick={(event) => {
|
|
97
|
+
event.currentTarget.focus();
|
|
98
|
+
setCollapsed(!collapsed);
|
|
99
|
+
}}
|
|
100
|
+
>
|
|
101
|
+
Menu
|
|
102
|
+
</Button>
|
|
103
|
+
}
|
|
104
|
+
actions={<ThemeToggle aria-label="Change theme" />}
|
|
105
|
+
/>
|
|
106
|
+
<ContentIsland className="space-y-basalt-layout-lg">
|
|
107
|
+
<PageHeader
|
|
108
|
+
title={page}
|
|
109
|
+
description="A reusable application frame with responsive navigation."
|
|
110
|
+
/>
|
|
111
|
+
<p role="status" className="text-basalt-sm text-basalt-muted-foreground">
|
|
112
|
+
Opened {page}. Your application supplies routes and page content.
|
|
113
|
+
</p>
|
|
114
|
+
</ContentIsland>
|
|
115
|
+
</AppMain>
|
|
116
|
+
</AppShell>
|
|
117
|
+
</SidebarProvider>
|
|
118
|
+
</ThemeProvider>
|
|
119
|
+
);
|
|
59
120
|
}
|
|
60
121
|
```
|
|
61
122
|
|
|
@@ -72,64 +133,124 @@ import { ThemeProvider } from "@nocoo/basalt/providers/theme";
|
|
|
72
133
|
import { useEffect, useRef, useState } from "react";
|
|
73
134
|
|
|
74
135
|
type Credentials = { email: string; password: string };
|
|
75
|
-
export function LoginForm({
|
|
76
|
-
|
|
77
|
-
|
|
136
|
+
export function LoginForm({
|
|
137
|
+
authenticate,
|
|
138
|
+
onSuccess,
|
|
139
|
+
}: {
|
|
140
|
+
authenticate: (credentials: Credentials, signal: AbortSignal) => Promise<void>;
|
|
141
|
+
onSuccess: (email: string) => void;
|
|
78
142
|
}) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
143
|
+
const [pending, setPending] = useState(false);
|
|
144
|
+
const [error, setError] = useState("");
|
|
145
|
+
const active = useRef<AbortController | null>(null);
|
|
146
|
+
useEffect(() => () => active.current?.abort(), []);
|
|
147
|
+
return (
|
|
148
|
+
<form
|
|
149
|
+
aria-label="Sign in"
|
|
150
|
+
className="space-y-basalt-space-lg"
|
|
151
|
+
onSubmit={async (event) => {
|
|
152
|
+
event.preventDefault();
|
|
153
|
+
if (active.current) return;
|
|
154
|
+
const form = new FormData(event.currentTarget);
|
|
155
|
+
const email = String(form.get("email") ?? "");
|
|
156
|
+
const password = String(form.get("password") ?? "");
|
|
157
|
+
const controller = new AbortController();
|
|
158
|
+
active.current = controller;
|
|
159
|
+
setPending(true);
|
|
160
|
+
setError("");
|
|
161
|
+
try {
|
|
162
|
+
await authenticate({ email, password }, controller.signal);
|
|
163
|
+
if (!controller.signal.aborted) onSuccess(email);
|
|
164
|
+
} catch (cause) {
|
|
165
|
+
if (!controller.signal.aborted)
|
|
166
|
+
setError(cause instanceof Error ? cause.message : "Sign in failed. Try again.");
|
|
167
|
+
} finally {
|
|
168
|
+
if (!controller.signal.aborted) {
|
|
169
|
+
active.current = null;
|
|
170
|
+
setPending(false);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}}
|
|
174
|
+
>
|
|
175
|
+
<Field label="Email">
|
|
176
|
+
<Input name="email" type="email" autoComplete="username" required />
|
|
177
|
+
</Field>
|
|
178
|
+
<Field label="Password">
|
|
179
|
+
<SensitiveInput
|
|
180
|
+
name="password"
|
|
181
|
+
autoComplete="current-password"
|
|
182
|
+
revealLabel="Show password"
|
|
183
|
+
hideLabel="Hide password"
|
|
184
|
+
required
|
|
185
|
+
/>
|
|
186
|
+
</Field>
|
|
187
|
+
{error && (
|
|
188
|
+
<p role="alert" className="text-basalt-sm text-basalt-destructive">
|
|
189
|
+
{error}
|
|
190
|
+
</p>
|
|
191
|
+
)}
|
|
192
|
+
<Button className="w-full" type="submit" loading={pending}>
|
|
193
|
+
Sign in
|
|
194
|
+
</Button>
|
|
195
|
+
</form>
|
|
196
|
+
);
|
|
105
197
|
}
|
|
106
198
|
|
|
107
199
|
// The documented visitor badge: a full-viewport root, a primary strip, the body,
|
|
108
200
|
// and a status footer. It never enters AppShell, and the body grows with the form.
|
|
109
201
|
export default function LoginRecipe() {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
202
|
+
const [account, setAccount] = useState<string | null>(null);
|
|
203
|
+
return (
|
|
204
|
+
<ThemeProvider>
|
|
205
|
+
<main className="basalt-ui flex min-h-dvh items-center justify-center bg-basalt-background p-basalt-space-lg">
|
|
206
|
+
<div
|
|
207
|
+
data-basalt-surface-root=""
|
|
208
|
+
className="flex w-72 max-w-full flex-col overflow-hidden rounded-basalt-lg bg-basalt-card ring-1 ring-basalt-border"
|
|
209
|
+
>
|
|
210
|
+
<div className="flex items-center justify-between gap-basalt-space-lg bg-basalt-primary px-basalt-space-lg py-basalt-space-lg">
|
|
211
|
+
<span className="text-basalt-base font-semibold text-basalt-primary-foreground">
|
|
212
|
+
Atlas
|
|
213
|
+
</span>
|
|
214
|
+
<span className="text-basalt-xs font-medium uppercase tracking-widest text-basalt-primary-foreground/80">
|
|
215
|
+
Visitor
|
|
216
|
+
</span>
|
|
217
|
+
</div>
|
|
218
|
+
<div className="flex flex-1 flex-col gap-basalt-space-lg px-basalt-space-lg py-basalt-space-lg">
|
|
219
|
+
<div className="space-y-basalt-space-sm">
|
|
220
|
+
<h1 className="text-basalt-xl font-semibold text-basalt-foreground">
|
|
221
|
+
Welcome to Atlas
|
|
222
|
+
</h1>
|
|
223
|
+
<p className="text-basalt-sm text-basalt-muted-foreground">
|
|
224
|
+
Local sign-in demo. Use password demo-pass.
|
|
225
|
+
</p>
|
|
226
|
+
</div>
|
|
227
|
+
{account ? (
|
|
228
|
+
<>
|
|
229
|
+
<p role="status" className="text-basalt-sm text-basalt-foreground">
|
|
230
|
+
Signed in as {account}
|
|
231
|
+
</p>
|
|
232
|
+
<Button variant="outline" onClick={() => setAccount(null)}>
|
|
233
|
+
Sign out
|
|
234
|
+
</Button>
|
|
235
|
+
</>
|
|
236
|
+
) : (
|
|
237
|
+
<LoginForm
|
|
238
|
+
authenticate={async ({ password }) => {
|
|
239
|
+
if (password !== "demo-pass") throw new Error("Incorrect demo password.");
|
|
240
|
+
}}
|
|
241
|
+
onSuccess={setAccount}
|
|
242
|
+
/>
|
|
243
|
+
)}
|
|
244
|
+
</div>
|
|
245
|
+
<div className="border-t border-basalt-border px-basalt-space-lg py-basalt-space-lg">
|
|
246
|
+
<p className="text-basalt-xs text-basalt-muted-foreground">
|
|
247
|
+
Secure sign-in. Sessions stay with your application.
|
|
248
|
+
</p>
|
|
249
|
+
</div>
|
|
250
|
+
</div>
|
|
251
|
+
</main>
|
|
252
|
+
</ThemeProvider>
|
|
253
|
+
);
|
|
133
254
|
}
|
|
134
255
|
```
|
|
135
256
|
|
|
@@ -151,37 +272,95 @@ import { useRef, useState } from "react";
|
|
|
151
272
|
|
|
152
273
|
type Project = { id: string; name: string; status: "Active" | "Paused" };
|
|
153
274
|
const initial: Project[] = [
|
|
154
|
-
|
|
155
|
-
|
|
275
|
+
{ id: "atlas", name: "Atlas", status: "Active" },
|
|
276
|
+
{ id: "boreal", name: "Boreal", status: "Paused" },
|
|
277
|
+
{ id: "cedar", name: "Cedar", status: "Active" },
|
|
156
278
|
];
|
|
157
279
|
export default function ResourcesRecipe() {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
280
|
+
const [rows, setRows] = useState(initial);
|
|
281
|
+
const [query, setQuery] = useState("");
|
|
282
|
+
const [error, setError] = useState(true);
|
|
283
|
+
const [message, setMessage] = useState("");
|
|
284
|
+
const failDeletion = useRef(true);
|
|
285
|
+
const filtered = rows.filter((row) => row.name.toLowerCase().includes(query.toLowerCase()));
|
|
286
|
+
const columns: DataTableColumn<Project>[] = [
|
|
287
|
+
{ id: "name", header: "Project", accessor: (row) => row.name },
|
|
288
|
+
{
|
|
289
|
+
id: "status",
|
|
290
|
+
header: "Status",
|
|
291
|
+
accessor: (row) => (
|
|
292
|
+
<TagBadge name={row.status} color={row.status === "Active" ? "success" : "warning"} />
|
|
293
|
+
),
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
id: "actions",
|
|
297
|
+
header: "Actions",
|
|
298
|
+
sortable: false,
|
|
299
|
+
accessor: (row) => (
|
|
300
|
+
<DeleteResource
|
|
301
|
+
name={row.name}
|
|
302
|
+
onDelete={async () => {
|
|
303
|
+
if (failDeletion.current) {
|
|
304
|
+
failDeletion.current = false;
|
|
305
|
+
throw new Error("Temporary conflict. Retry this deletion.");
|
|
306
|
+
}
|
|
307
|
+
setRows((current) => current.filter((item) => item.id !== row.id));
|
|
308
|
+
setMessage("Deleted " + row.name);
|
|
309
|
+
}}
|
|
310
|
+
/>
|
|
311
|
+
),
|
|
312
|
+
},
|
|
313
|
+
];
|
|
314
|
+
// A document page: the shell grows with content and the island owns the desktop inset.
|
|
315
|
+
return (
|
|
316
|
+
<ThemeProvider>
|
|
317
|
+
<AppShell layout="document">
|
|
318
|
+
<AppSkipLink href="#recipe-resources">Skip to content</AppSkipLink>
|
|
319
|
+
<AppMain id="recipe-resources" tabIndex={-1}>
|
|
320
|
+
<ContentIsland>
|
|
321
|
+
<ResourceList
|
|
322
|
+
title="Projects"
|
|
323
|
+
description="A complete resource-page composition with local service adapters."
|
|
324
|
+
data={[]}
|
|
325
|
+
toolbar={
|
|
326
|
+
<div className="flex flex-wrap gap-basalt-content-gap">
|
|
327
|
+
<Input
|
|
328
|
+
aria-label="Search projects"
|
|
329
|
+
placeholder="Search projects…"
|
|
330
|
+
value={query}
|
|
331
|
+
onChange={(event) => setQuery(event.target.value)}
|
|
332
|
+
/>
|
|
333
|
+
<Button
|
|
334
|
+
variant="outline"
|
|
335
|
+
onClick={() => {
|
|
336
|
+
setRows(initial);
|
|
337
|
+
setQuery("");
|
|
338
|
+
setMessage("");
|
|
339
|
+
}}
|
|
340
|
+
>
|
|
341
|
+
Reset projects
|
|
342
|
+
</Button>
|
|
343
|
+
</div>
|
|
344
|
+
}
|
|
345
|
+
footer={
|
|
346
|
+
<p role="status" className="text-basalt-sm text-basalt-muted-foreground">
|
|
347
|
+
{message || filtered.length + " projects"}
|
|
348
|
+
</p>
|
|
349
|
+
}
|
|
350
|
+
>
|
|
351
|
+
<DataTable
|
|
352
|
+
aria-label="Projects"
|
|
353
|
+
data={filtered}
|
|
354
|
+
columns={columns}
|
|
355
|
+
error={error ? "The project service is unavailable." : undefined}
|
|
356
|
+
onRetry={() => setError(false)}
|
|
357
|
+
/>
|
|
358
|
+
</ResourceList>
|
|
359
|
+
</ContentIsland>
|
|
360
|
+
</AppMain>
|
|
361
|
+
</AppShell>
|
|
362
|
+
</ThemeProvider>
|
|
363
|
+
);
|
|
185
364
|
}
|
|
186
365
|
```
|
|
187
366
|
|
|
@@ -200,25 +379,45 @@ import { ContentIsland } from "@nocoo/basalt/components/sidebar";
|
|
|
200
379
|
import { ThemeProvider } from "@nocoo/basalt/providers/theme";
|
|
201
380
|
|
|
202
381
|
export default function MobileLayoutRecipe() {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
382
|
+
return (
|
|
383
|
+
<ThemeProvider>
|
|
384
|
+
<AppShell layout="responsive">
|
|
385
|
+
<AppSkipLink>Skip to content</AppSkipLink>
|
|
386
|
+
<AppMain>
|
|
387
|
+
<ContentIsland mobileSurface="edge-to-edge">
|
|
388
|
+
<AppHeader
|
|
389
|
+
sticky
|
|
390
|
+
density="compact"
|
|
391
|
+
title="Field notes"
|
|
392
|
+
actions={
|
|
393
|
+
<Popover>
|
|
394
|
+
<PopoverTrigger asChild>
|
|
395
|
+
<Button variant="ghost">Options</Button>
|
|
396
|
+
</PopoverTrigger>
|
|
397
|
+
<PopoverContent aria-label="Options">
|
|
398
|
+
<p>Application-owned reading preferences.</p>
|
|
399
|
+
</PopoverContent>
|
|
400
|
+
</Popover>
|
|
401
|
+
}
|
|
402
|
+
/>
|
|
403
|
+
<article className="mx-auto max-w-[65ch] space-y-basalt-space-lg p-basalt-layout text-basalt-base leading-basalt-relaxed">
|
|
404
|
+
{Array.from({ length: 24 }, (_, index) => (
|
|
405
|
+
<section key={index}>
|
|
406
|
+
<h2>Observation {index + 1}</h2>
|
|
407
|
+
<p>
|
|
408
|
+
The shell leaves the document in charge on a narrow screen. Content grows
|
|
409
|
+
naturally, the header stays reachable, and the final paragraph remains above the
|
|
410
|
+
trailing safe area.
|
|
411
|
+
</p>
|
|
412
|
+
</section>
|
|
413
|
+
))}
|
|
414
|
+
<p>End of notes.</p>
|
|
415
|
+
</article>
|
|
416
|
+
</ContentIsland>
|
|
417
|
+
</AppMain>
|
|
418
|
+
</AppShell>
|
|
419
|
+
</ThemeProvider>
|
|
420
|
+
);
|
|
222
421
|
}
|
|
223
422
|
```
|
|
224
423
|
|
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` (v3.2.
|
|
3
|
+
This document describes how AI code assistants, automated agents, and developers can consume `@nocoo/basalt` (v3.2.4).
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -14,7 +14,7 @@ Document anchors formatted as `ai/registry.json#<slug>` reference specific catal
|
|
|
14
14
|
It contains:
|
|
15
15
|
- **131 Exported Modules** (1 root barrel, components, charts, providers)
|
|
16
16
|
- **120 Ready Catalog Entries** with full API definitions
|
|
17
|
-
- **
|
|
17
|
+
- **787 Public Symbols** (410 runtime values, 377 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
|
|