@vadimcomanescu/nadicode-design-system 5.0.0 → 5.0.2
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/.agents/skills/seed/SKILL.md +100 -24
- package/.agents/skills/seed/references/composition.md +37 -21
- package/.agents/skills/seed/references/glass-and-effects.md +7 -5
- package/.agents/skills/seed/references/responsive.md +22 -63
- package/.agents/skills/seed/references/state-machines.md +54 -46
- package/contracts/release-governance-baseline.json +0 -1
- package/dist/{TeamPage-BX3RMSZU.js → TeamPage-UO3V2JDK.js} +33 -32
- package/dist/catalog/catalog.d.ts +66 -4
- package/dist/catalog/catalog.js +9 -9
- package/dist/catalog/components.d.ts +1 -2
- package/dist/catalog/components.js +27 -27
- package/dist/catalog/definitions/blocks-agent.d.ts +0 -1
- package/dist/catalog/definitions/blocks-agent.js +1 -1
- package/dist/catalog/definitions/blocks-auth.d.ts +16 -0
- package/dist/catalog/definitions/blocks-auth.js +1 -1
- package/dist/catalog/definitions/blocks-content.d.ts +18 -0
- package/dist/catalog/definitions/blocks-content.js +1 -1
- package/dist/catalog/definitions/blocks-crud.d.ts +22 -2
- package/dist/catalog/definitions/blocks-crud.js +1 -1
- package/dist/catalog/definitions/blocks-data.d.ts +2 -0
- package/dist/catalog/definitions/blocks-data.js +1 -1
- package/dist/catalog/definitions/blocks-marketing.d.ts +8 -0
- package/dist/catalog/definitions/blocks-marketing.js +1 -1
- package/dist/catalog/definitions/chat.d.ts +0 -1
- package/dist/catalog/definitions/chat.js +1 -1
- package/dist/catalog/definitions/index.d.ts +66 -4
- package/dist/catalog/definitions/index.js +8 -8
- package/dist/catalog/primitives/chat.js +2 -2
- package/dist/catalog/primitives/index.js +3 -3
- package/dist/{chunk-C7LO3JEF.js → chunk-33UWVOQ6.js} +44 -12
- package/dist/{chunk-IHGZJS7N.js → chunk-3FJG5MAT.js} +8 -2
- package/dist/{chunk-UOUERAB4.js → chunk-3LROWCZE.js} +26 -8
- package/dist/{chunk-UIYX2EMT.js → chunk-5RBO2IMZ.js} +7 -2
- package/dist/{chunk-7SRNVMBY.js → chunk-7LYRUSWM.js} +8 -3
- package/dist/{chunk-ZND7AAWG.js → chunk-A4QNTJUR.js} +4 -2
- package/dist/{chunk-P7CTBRHO.js → chunk-AP5SGSN7.js} +15 -5
- package/dist/{chunk-5MLOZVOQ.js → chunk-B4373MDA.js} +9 -4
- package/dist/{chunk-KV726HMK.js → chunk-BOWKE7OE.js} +4 -4
- package/dist/{chunk-NVBF4OWT.js → chunk-BY6LWOHB.js} +7 -2
- package/dist/{chunk-4TK2PXMJ.js → chunk-CTVV6JS6.js} +1 -1
- package/dist/{chunk-6KRT337C.js → chunk-E4KUPYHE.js} +1 -1
- package/dist/{chunk-3TYPQWKD.js → chunk-FSU7ZM5V.js} +7 -2
- package/dist/{chunk-QLPU2IEO.js → chunk-FX3GYS5O.js} +27 -4
- package/dist/{chunk-OMI2LLXM.js → chunk-H3KTAHJP.js} +17 -3
- package/dist/{chunk-UGXI4PES.js → chunk-IW36SVOH.js} +17 -7
- package/dist/{chunk-5L2RFJZR.js → chunk-JDHD4L6N.js} +1 -1
- package/dist/{chunk-CAET62YQ.js → chunk-NBDUZA66.js} +11 -4
- package/dist/{chunk-TNDMBLYP.js → chunk-NDQO7AO6.js} +15 -3
- package/dist/{chunk-MT76BPYV.js → chunk-PQOL3E2V.js} +1 -1
- package/dist/{chunk-ZNF6S3DQ.js → chunk-QCFDSOTV.js} +9 -5
- package/dist/{chunk-ID3N3MHQ.js → chunk-R4SBK6Y5.js} +7 -2
- package/dist/{chunk-GNKNT52O.js → chunk-TTDKPZ75.js} +1 -1
- package/dist/{chunk-BAB6AHOM.js → chunk-U43JTK45.js} +1 -1
- package/dist/{chunk-WHYKEOUV.js → chunk-U5QLJHYN.js} +0 -2
- package/dist/{chunk-4ACR6NGQ.js → chunk-UAN2YEI5.js} +3 -7
- package/dist/{chunk-NA6ZPKIK.js → chunk-UCFR7GLW.js} +11 -17
- package/dist/{chunk-R4DPHCHJ.js → chunk-UMEBNHKR.js} +9 -6
- package/dist/{chunk-A44E44UV.js → chunk-UPMSE6PQ.js} +7 -2
- package/dist/{chunk-HMN4K4L6.js → chunk-VQVWFCHF.js} +7 -7
- package/dist/{chunk-LZ66ADSL.js → chunk-WGQHM56J.js} +25 -25
- package/dist/{chunk-JPNNAU26.js → chunk-XLN4NVKU.js} +16 -28
- package/dist/{chunk-CPHVCY3M.js → chunk-XUWKGDUY.js} +1 -1
- package/dist/{chunk-FAKTXE3F.js → chunk-YEAJLVGB.js} +8 -4
- package/dist/{chunk-56TNQ2P4.js → chunk-YMAEXGD2.js} +1 -1
- package/dist/{chunk-YNBDA24W.js → chunk-ZKA5X3E4.js} +12 -3
- package/dist/{chunk-VUNXVYOH.js → chunk-ZL6BPQNN.js} +19 -10
- package/dist/{chunk-XYYZRQES.js → chunk-ZU7MDRCI.js} +1 -1
- package/dist/components/blocks/AgentConversationBlock.d.ts +2 -5
- package/dist/components/blocks/AgentConversationBlock.js +2 -2
- package/dist/components/blocks/AgentWorkbenchBlock.d.ts +2 -5
- package/dist/components/blocks/AgentWorkbenchBlock.js +2 -2
- package/dist/components/blocks/ApiKeysBlock.d.ts +1 -1
- package/dist/components/blocks/ApiKeysBlock.js +1 -1
- package/dist/components/blocks/AuthLayout.d.ts +1 -1
- package/dist/components/blocks/AuthLayout.js +2 -2
- package/dist/components/blocks/CommandPaletteBlock.d.ts +1 -1
- package/dist/components/blocks/CommandPaletteBlock.js +1 -1
- package/dist/components/blocks/ContactBlock.d.ts +1 -1
- package/dist/components/blocks/ContactBlock.js +1 -1
- package/dist/components/blocks/CreateBlock.d.ts +1 -1
- package/dist/components/blocks/CreateBlock.js +1 -1
- package/dist/components/blocks/CrudListBlock.d.ts +1 -1
- package/dist/components/blocks/CrudListBlock.js +1 -1
- package/dist/components/blocks/DashboardBlock.js +1 -1
- package/dist/components/blocks/HeroBlock.d.ts +1 -25
- package/dist/components/blocks/HeroBlock.js +1 -1
- package/dist/components/blocks/InteractiveAreaChartBlock.d.ts +1 -1
- package/dist/components/blocks/InteractiveAreaChartBlock.js +1 -1
- package/dist/components/blocks/LoginBlock.d.ts +1 -1
- package/dist/components/blocks/LoginBlock.js +1 -1
- package/dist/components/blocks/NewsletterBlock.d.ts +3 -1
- package/dist/components/blocks/NewsletterBlock.js +1 -1
- package/dist/components/blocks/OTPBlock.d.ts +1 -1
- package/dist/components/blocks/OTPBlock.js +1 -1
- package/dist/components/blocks/OnboardingBlock.d.ts +1 -1
- package/dist/components/blocks/OnboardingBlock.js +1 -1
- package/dist/components/blocks/OnboardingFlowBlock.d.ts +1 -1
- package/dist/components/blocks/OnboardingFlowBlock.js +2 -2
- package/dist/components/blocks/PasswordRecoveryBlock.d.ts +1 -1
- package/dist/components/blocks/PasswordRecoveryBlock.js +1 -1
- package/dist/components/blocks/ResetPasswordBlock.d.ts +1 -1
- package/dist/components/blocks/ResetPasswordBlock.js +1 -1
- package/dist/components/blocks/TwoFactorSetupBlock.d.ts +1 -1
- package/dist/components/blocks/TwoFactorSetupBlock.js +2 -2
- package/dist/components/blocks/WizardBlock.d.ts +1 -1
- package/dist/components/blocks/WizardBlock.js +1 -1
- package/dist/components/blocks/user/InviteUserModal.d.ts +1 -18
- package/dist/components/blocks/user/InviteUserModal.js +1 -1
- package/dist/components/ui/ChatMessage.d.ts +2 -6
- package/dist/components/ui/ChatMessage.js +1 -1
- package/dist/components/ui/FormWizard.d.ts +1 -2
- package/dist/components/ui/FormWizard.js +1 -1
- package/dist/components/ui/KanbanBoard.d.ts +3 -3
- package/dist/components/ui/KanbanBoard.js +1 -1
- package/dist/index.js +0 -137
- package/dist/lib/json-render/app.js +1 -1
- package/dist/lib/json-render/catalog.d.ts +28 -0
- package/dist/lib/json-render/catalog.js +10 -10
- package/dist/lib/json-render/registry.js +10 -10
- package/dist/lib/json-render/showcase-spec.js +1 -1
- package/dist/lib-index.d.ts +6 -24
- package/package.json +1 -5
- package/dist/catalog/types.d.ts +0 -4
- package/dist/catalog/types.js +0 -1
|
@@ -34,14 +34,27 @@ Each entry provides: `props` (Zod schema), `description`, `slots`, `events`, `ex
|
|
|
34
34
|
import { seedComponents } from "@vadimcomanescu/nadicode-design-system/catalog/components"
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
Usage:
|
|
37
|
+
Usage — spec pattern (preferred):
|
|
38
38
|
```tsx
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
import { PageRenderer } from '@json-render/next'
|
|
40
|
+
import { registry } from '@/lib/registry'
|
|
41
|
+
|
|
42
|
+
// Pages are JSON specs rendered via PageRenderer
|
|
43
|
+
const spec: NextAppSpec = {
|
|
44
|
+
routes: {
|
|
45
|
+
'/login': {
|
|
46
|
+
page: {
|
|
47
|
+
root: 'shell',
|
|
48
|
+
elements: {
|
|
49
|
+
shell: { type: 'PageShell', props: { className: null }, children: ['login'] },
|
|
50
|
+
login: { type: 'LoginBlock', props: { type: 'login', error: null, email: null, password: null, fullName: null, showSocial: null, forgotPasswordHref: null, signUpHref: null, signInHref: null } },
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
<PageRenderer spec={spec} registry={registry} />
|
|
45
58
|
```
|
|
46
59
|
|
|
47
60
|
**Catalog blocks** are imported exclusively from `seedComponents`. Do not import them via individual subpath exports (ADR 0009). The ESLint rule `nadicode/require-catalog-import` enforces this at lint time.
|
|
@@ -105,6 +118,47 @@ The catalog is machine-readable, tested, accessible, and themed. Hand-built bloc
|
|
|
105
118
|
| Hardcoded hex in components | Map to semantic tokens | Brand colors via tokens only |
|
|
106
119
|
| Building a block from 3+ primitives without checking catalog | Query `seedComponentDefinitions` first, use catalog block | Catalog-First Rule |
|
|
107
120
|
| Reimplementing a catalog block locally | `const { BlockName } = seedComponents` | Prevents drift and wasted work |
|
|
121
|
+
| `emit('submit')` without `$bindState` | Wire form fields with `$bindState` + action params | Events are signals; data flows through state |
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Form Data Flow
|
|
126
|
+
|
|
127
|
+
`emit()` is a signal — it carries no data payload. Form field values travel through state bindings, not events.
|
|
128
|
+
|
|
129
|
+
### The pattern
|
|
130
|
+
|
|
131
|
+
Wire each field to a state path with `$bindState`. Reference those paths in the action `params` when the submit event fires.
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"type": "LoginBlock",
|
|
136
|
+
"props": {
|
|
137
|
+
"type": "login",
|
|
138
|
+
"email": { "$bindState": "/auth/email" },
|
|
139
|
+
"password": { "$bindState": "/auth/password" }
|
|
140
|
+
},
|
|
141
|
+
"on": {
|
|
142
|
+
"submit": {
|
|
143
|
+
"action": "login",
|
|
144
|
+
"params": {
|
|
145
|
+
"email": { "$state": "/auth/email" },
|
|
146
|
+
"password": { "$state": "/auth/password" }
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
When the user submits, `emit('submit')` fires. The runtime resolves `$state` references against the current state store and passes `{ email, password }` to the `login` action handler.
|
|
154
|
+
|
|
155
|
+
### Dual-mode guarantee
|
|
156
|
+
|
|
157
|
+
Blocks work both with and without bindings. Without `$bindState`, the block manages its own internal state and `emit('submit')` still fires — bindings just add the two-way state-sync layer that lets the spec read back the values.
|
|
158
|
+
|
|
159
|
+
### Binding path convention
|
|
160
|
+
|
|
161
|
+
Use `/form/fieldName` for generic forms and `/auth/fieldName` for authentication fields. The leading slash is required. Paths are scoped to the `StateProvider` wrapping the page.
|
|
108
162
|
|
|
109
163
|
---
|
|
110
164
|
|
|
@@ -146,17 +200,39 @@ Token source of truth: `src/lib/tokens.config.js` (authored), `src/index.css` (g
|
|
|
146
200
|
|
|
147
201
|
Use `Heading` for semantic headings. Use `Typography` for non-heading copy only.
|
|
148
202
|
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"page-title": {
|
|
206
|
+
"type": "Heading",
|
|
207
|
+
"props": { "level": 1, "size": null, "className": null },
|
|
208
|
+
"children": ["Page title"]
|
|
209
|
+
},
|
|
210
|
+
"section-title": {
|
|
211
|
+
"type": "Heading",
|
|
212
|
+
"props": { "level": 2, "size": "section", "className": null },
|
|
213
|
+
"children": ["Section title"]
|
|
214
|
+
},
|
|
215
|
+
"card-title": {
|
|
216
|
+
"type": "Heading",
|
|
217
|
+
"props": { "level": 3, "size": "label", "className": null },
|
|
218
|
+
"children": ["Card title"]
|
|
219
|
+
},
|
|
220
|
+
"body-text": {
|
|
221
|
+
"type": "Typography",
|
|
222
|
+
"props": { "variant": null, "className": null },
|
|
223
|
+
"children": ["Body copy"]
|
|
224
|
+
},
|
|
225
|
+
"meta-text": {
|
|
226
|
+
"type": "Typography",
|
|
227
|
+
"props": { "variant": "small", "className": null },
|
|
228
|
+
"children": ["Meta text"]
|
|
229
|
+
},
|
|
230
|
+
"muted-text": {
|
|
231
|
+
"type": "Typography",
|
|
232
|
+
"props": { "variant": "muted", "className": null },
|
|
233
|
+
"children": ["Muted text"]
|
|
234
|
+
}
|
|
235
|
+
}
|
|
160
236
|
```
|
|
161
237
|
|
|
162
238
|
**Heading props**
|
|
@@ -299,12 +375,12 @@ Do not copy DS catalog files into the app.
|
|
|
299
375
|
**Test helpers**
|
|
300
376
|
Consumer apps can import `SeedTestProvider` from `@vadimcomanescu/nadicode-design-system/test` when they need the package-owned test wrapper. The showcase app keeps its local `@/test/SeedTestProvider` wrapper for in-repo tests.
|
|
301
377
|
|
|
302
|
-
**LanguageSwitcher** (
|
|
303
|
-
|
|
378
|
+
**LanguageSwitcher** (catalog: `seedComponents.LanguageSwitcher`)
|
|
379
|
+
Consumed via the catalog. Schema props: `locales` (string[], default `['en','it']`), `value` (current locale), `className`. Fires `emit('localeChange')` for locale changes. Uses `useTranslations('components.languageSwitcher')` for labels. Without a `localeChange` handler, sets a `NEXT_LOCALE` cookie and calls `router.refresh()`.
|
|
304
380
|
|
|
305
|
-
**SettingsModal** (
|
|
306
|
-
Exports: `defaultSettingsTabs`, `SettingsTabDef` (interface)
|
|
307
|
-
|
|
381
|
+
**SettingsModal** (catalog: `seedComponents.SettingsModal`)
|
|
382
|
+
Consumed via the catalog. Exports: `defaultSettingsTabs`, `SettingsTabDef` (interface).
|
|
383
|
+
Schema props: `open`, `defaultTab`, `tabs` (SettingsTabDef[]). Fires `emit('openChange')` and `emit('renderContent')` for modal state and tab content. Uses `useTranslations('pages.settings')` for tab labels and headings. The default language tab renders `LanguageSwitcher` automatically.
|
|
308
384
|
|
|
309
385
|
---
|
|
310
386
|
|
|
@@ -322,7 +398,7 @@ Every page = **Shell > Sections > Components**.
|
|
|
322
398
|
|
|
323
399
|
| Intent | Shell | Components |
|
|
324
400
|
|--------|-------|------------|
|
|
325
|
-
| App (dashboard, settings, CRUD, analytics, agents) | `app-shell` | `Sidebar` + top bar + `
|
|
401
|
+
| App (dashboard, settings, CRUD, analytics, agents) | `app-shell` | `Sidebar` + top bar + `CommandPaletteBlock` |
|
|
326
402
|
| Marketing (landing, pricing, blog) | `marketing-shell` | `HeaderBlock` + `FooterBlock` |
|
|
327
403
|
| Auth | `auth-shell` | `AuthLayout` (split/centered) |
|
|
328
404
|
| Onboarding | `onboarding-shell` | Minimal header + progress bar |
|
|
@@ -26,7 +26,7 @@ Shell -- persistent frame (sidebar, header, footer)
|
|
|
26
26
|
|
|
27
27
|
| Intent | Shell | Components |
|
|
28
28
|
|--------|-------|------------|
|
|
29
|
-
| App pages (dashboard, settings, CRUD, analytics, agents) | `app-shell` | `Sidebar` + top bar + `
|
|
29
|
+
| App pages (dashboard, settings, CRUD, analytics, agents) | `app-shell` | `Sidebar` + top bar + `CommandPaletteBlock` |
|
|
30
30
|
| Marketing (landing, pricing, blog) | `marketing-shell` | `HeaderBlock` + `FooterBlock` |
|
|
31
31
|
| Auth (login, signup, reset) | `auth-shell` | `AuthLayout` (split-screen or centered) |
|
|
32
32
|
| Onboarding | `onboarding-shell` | Minimal header + progress bar, no nav |
|
|
@@ -110,26 +110,42 @@ Is it marketing / public?
|
|
|
110
110
|
|
|
111
111
|
### Section Template
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
113
|
+
App section — heading + action bar above content:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"component": "section",
|
|
118
|
+
"props": { "className": "py-6 md:py-8" },
|
|
119
|
+
"children": [
|
|
120
|
+
{
|
|
121
|
+
"component": "div",
|
|
122
|
+
"props": { "className": "flex items-center justify-between mb-4" },
|
|
123
|
+
"children": [
|
|
124
|
+
{ "component": "Heading", "props": { "level": 3, "size": "subsection", "children": "Section Title" } },
|
|
125
|
+
{ "component": "Button", "props": { "variant": "outline", "size": "sm", "children": "Action" } }
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Marketing section — centered heading + lead text above content:
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"component": "section",
|
|
137
|
+
"props": { "className": "py-16 md:py-24" },
|
|
138
|
+
"children": [
|
|
139
|
+
{
|
|
140
|
+
"component": "div",
|
|
141
|
+
"props": { "className": "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center" },
|
|
142
|
+
"children": [
|
|
143
|
+
{ "component": "Heading", "props": { "level": 2, "size": "section", "className": "mb-4", "children": "Section Title" } },
|
|
144
|
+
{ "component": "Typography", "props": { "variant": "lead", "className": "mb-12 text-text-secondary", "children": "Section description" } }
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
}
|
|
133
149
|
```
|
|
134
150
|
|
|
135
151
|
---
|
|
@@ -95,13 +95,15 @@
|
|
|
95
95
|
|
|
96
96
|
### AmbientGrid
|
|
97
97
|
|
|
98
|
-
Subtle CSS grid overlay for structural rhythm.
|
|
98
|
+
Subtle CSS grid overlay for structural rhythm. Cataloged — use via `seedComponents`.
|
|
99
99
|
|
|
100
100
|
```tsx
|
|
101
|
-
import {
|
|
102
|
-
<AmbientGrid
|
|
103
|
-
|
|
104
|
-
<AmbientGrid
|
|
101
|
+
import { seedComponents } from "@vadimcomanescu/nadicode-design-system/catalog/components"
|
|
102
|
+
<seedComponents.AmbientGrid props={{}} emit={() => {}} on={{}} />
|
|
103
|
+
// With custom cell size:
|
|
104
|
+
<seedComponents.AmbientGrid props={{ size: 32 }} emit={() => {}} on={{}} />
|
|
105
|
+
// Debug mode (0.15 opacity for alignment checking):
|
|
106
|
+
<seedComponents.AmbientGrid props={{ debug: true }} emit={() => {}} on={{}} />
|
|
105
107
|
```
|
|
106
108
|
|
|
107
109
|
### MeteorShower
|
|
@@ -99,78 +99,37 @@ Breakpoint contracts, mobile-first patterns, touch targets, and responsive rules
|
|
|
99
99
|
|
|
100
100
|
### Navigation Collapse
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
<main className="flex-1">
|
|
110
|
-
<header className="lg:hidden flex items-center p-4">
|
|
111
|
-
<SidebarTrigger />
|
|
112
|
-
<span className="ml-3 font-medium">Page Title</span>
|
|
113
|
-
</header>
|
|
114
|
-
{children}
|
|
115
|
-
</main>
|
|
116
|
-
</SidebarProvider>
|
|
117
|
-
```
|
|
102
|
+
Desktop shows the full sidebar; mobile hides it behind a sheet triggered by a hamburger button.
|
|
103
|
+
|
|
104
|
+
- Sidebar element: `className="hidden lg:flex"` (visible only at `lg:` and up).
|
|
105
|
+
- Mobile header: `className="lg:hidden"` — contains `SidebarTrigger` and the page title.
|
|
106
|
+
- The `SidebarProvider` wraps the layout; the sheet/drawer behavior is built into the `Sidebar` block.
|
|
107
|
+
|
|
108
|
+
CSS classes that drive this pattern: `hidden lg:flex` on the sidebar, `lg:hidden` on the mobile header bar.
|
|
118
109
|
|
|
119
110
|
### Table to Card List
|
|
120
111
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
<div className="sm:hidden space-y-3">
|
|
127
|
-
{data.map((item) => (
|
|
128
|
-
<Card key={item.id} className="p-4">
|
|
129
|
-
{/* Card representation of row */}
|
|
130
|
-
</Card>
|
|
131
|
-
))}
|
|
132
|
-
</div>
|
|
133
|
-
```
|
|
112
|
+
At `sm:` and up, render `DataTable`. Below `sm:`, render a card list with one `Card` per row.
|
|
113
|
+
|
|
114
|
+
- Table wrapper: `className="hidden sm:block"`
|
|
115
|
+
- Card list wrapper: `className="sm:hidden space-y-3"`
|
|
116
|
+
- Each card uses `className="p-4"` and mirrors the key fields of the table row.
|
|
134
117
|
|
|
135
118
|
### Tab Navigation Scroll
|
|
136
119
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
<TabsTrigger value="general">General</TabsTrigger>
|
|
142
|
-
<TabsTrigger value="security">Security</TabsTrigger>
|
|
143
|
-
<TabsTrigger value="billing">Billing</TabsTrigger>
|
|
144
|
-
</TabsList>
|
|
145
|
-
</div>
|
|
146
|
-
```
|
|
120
|
+
Settings and similar pages use horizontally scrollable tab lists on mobile so all tabs are reachable without wrapping.
|
|
121
|
+
|
|
122
|
+
- Outer wrapper: `className="overflow-x-auto -mx-4 px-4"` (negative margin bleeds to viewport edge, then re-pads).
|
|
123
|
+
- `TabsList`: `className="inline-flex w-auto min-w-full sm:w-full"` — `inline-flex w-auto` allows scroll; `min-w-full` ensures full width when content is narrow; `sm:w-full` snaps to full width on larger screens.
|
|
147
124
|
|
|
148
125
|
### Multi-Panel to Drawers
|
|
149
126
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
<main className="flex-1 flex flex-col">
|
|
157
|
-
<header className="xl:hidden flex items-center gap-2 p-3 border-b border-border">
|
|
158
|
-
<Sheet>
|
|
159
|
-
<SheetTrigger asChild>
|
|
160
|
-
<Button variant="ghost" size="icon"><UsersIcon size={16} /></Button>
|
|
161
|
-
</SheetTrigger>
|
|
162
|
-
<SheetContent side="left"><TeamPanel /></SheetContent>
|
|
163
|
-
</Sheet>
|
|
164
|
-
<span className="flex-1 text-center font-medium">Agent Chat</span>
|
|
165
|
-
</header>
|
|
166
|
-
<ConversationArea />
|
|
167
|
-
<Composer />
|
|
168
|
-
</main>
|
|
169
|
-
<aside className="hidden xl:block w-[320px] border-l border-border">
|
|
170
|
-
<WorkPanel />
|
|
171
|
-
</aside>
|
|
172
|
-
</div>
|
|
173
|
-
```
|
|
127
|
+
3-panel agent workbench: both side panels are visible at `xl:`, the left panel is visible at `lg:`, and on mobile both panels move into sheet drawers.
|
|
128
|
+
|
|
129
|
+
- Left panel (`aside`): `className="hidden xl:block w-[280px] border-r border-border"`.
|
|
130
|
+
- Right panel (`aside`): `className="hidden xl:block w-[320px] border-l border-border"`.
|
|
131
|
+
- Mobile header: `className="xl:hidden"` — contains `Sheet`/`SheetTrigger` buttons to open each panel as a drawer.
|
|
132
|
+
- Main area: `className="flex-1 flex flex-col"` with `h-dvh` on the outer container (never `h-screen`).
|
|
174
133
|
|
|
175
134
|
---
|
|
176
135
|
|
|
@@ -79,39 +79,33 @@ Expected wait time?
|
|
|
79
79
|
|
|
80
80
|
### Form Implementation Pattern
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
</FormControl>
|
|
102
|
-
<FormMessage /> {/* Validation error */}
|
|
103
|
-
</FormItem>
|
|
104
|
-
)}
|
|
105
|
-
/>
|
|
106
|
-
<Button type="submit" disabled={!isDirty || isSubmitting}>
|
|
107
|
-
{isSubmitting ? <Spinner /> : "Save"}
|
|
108
|
-
</Button>
|
|
109
|
-
</form>
|
|
110
|
-
</Form>
|
|
111
|
-
)
|
|
82
|
+
Forms are specified as JSON element trees. Use `$bindState` to wire form fields to block state, and emit `"submit"` when the form is submitted. The block handles `isDirty` / `isSubmitting` state internally via `useBoundProp`.
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"component": "FormBlock",
|
|
87
|
+
"props": {
|
|
88
|
+
"fields": [
|
|
89
|
+
{
|
|
90
|
+
"name": "name",
|
|
91
|
+
"label": "Name",
|
|
92
|
+
"type": "text",
|
|
93
|
+
"required": true
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
"submitLabel": "Save"
|
|
97
|
+
},
|
|
98
|
+
"on": {
|
|
99
|
+
"submit": "handleSave"
|
|
100
|
+
}
|
|
112
101
|
}
|
|
113
102
|
```
|
|
114
103
|
|
|
104
|
+
Key rules:
|
|
105
|
+
- Labels always stacked above their field (never inline).
|
|
106
|
+
- `FormMessage` (field-level validation error) renders below the field automatically.
|
|
107
|
+
- Submit button shows `Spinner` while `isSubmitting`; disabled when form is `pristine`.
|
|
108
|
+
|
|
115
109
|
---
|
|
116
110
|
|
|
117
111
|
## CRUD List States
|
|
@@ -213,22 +207,36 @@ function MyForm() {
|
|
|
213
207
|
|
|
214
208
|
### Streaming Implementation
|
|
215
209
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
210
|
+
Streaming chat UI is composed in JSON spec element trees. The block controls `isStreaming` state internally and exposes it through `$bindState`. Each message, tool call, and thinking indicator maps to a catalog component:
|
|
211
|
+
|
|
212
|
+
- `ChatMessage` — wraps a single turn; `props.role` is `"assistant"` or `"user"`.
|
|
213
|
+
- `ChatMessageContent` — renders streaming text; `props.isStreaming` shows the cursor while tokens arrive.
|
|
214
|
+
- `ChatToolCall` — shows a tool invocation; `props.state` is one of `"input-available"`, `"output-available"`, or `"output-error"`.
|
|
215
|
+
- `ChatThinkingMessage` — animated dots; `props.variant` is `"pill"` (block) or `"dots"` (inline).
|
|
216
|
+
|
|
217
|
+
Example spec fragment for a streaming assistant turn with a tool call:
|
|
218
|
+
|
|
219
|
+
```json
|
|
220
|
+
[
|
|
221
|
+
{
|
|
222
|
+
"component": "ChatMessage",
|
|
223
|
+
"props": { "role": "assistant" },
|
|
224
|
+
"children": [
|
|
225
|
+
{
|
|
226
|
+
"component": "ChatMessageContent",
|
|
227
|
+
"props": { "$bindState": "isStreaming", "text": "$bindState:streamingContent" }
|
|
228
|
+
}
|
|
229
|
+
]
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"component": "ChatToolCall",
|
|
233
|
+
"props": { "state": "input-available", "name": "search_files", "input": { "query": "utils.ts" } }
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"component": "ChatThinkingMessage",
|
|
237
|
+
"props": { "variant": "pill" }
|
|
238
|
+
}
|
|
239
|
+
]
|
|
232
240
|
```
|
|
233
241
|
|
|
234
242
|
---
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { seedComponents } from './chunk-
|
|
2
|
-
import './chunk-
|
|
1
|
+
import { seedComponents } from './chunk-WGQHM56J.js';
|
|
2
|
+
import './chunk-PQOL3E2V.js';
|
|
3
3
|
import './chunk-YGDO5KDY.js';
|
|
4
4
|
import './chunk-EJQ73FJ5.js';
|
|
5
5
|
import './chunk-4GLBFZVI.js';
|
|
@@ -11,24 +11,24 @@ import './chunk-5B3GLRX3.js';
|
|
|
11
11
|
import './chunk-GXUQFXT7.js';
|
|
12
12
|
import './chunk-65CUE2WL.js';
|
|
13
13
|
import './chunk-L55GVKLY.js';
|
|
14
|
-
import './chunk-
|
|
14
|
+
import './chunk-YMAEXGD2.js';
|
|
15
15
|
import './chunk-2NMP3J5R.js';
|
|
16
|
-
import './chunk-
|
|
16
|
+
import './chunk-XLN4NVKU.js';
|
|
17
17
|
import './chunk-A4TQNK7R.js';
|
|
18
18
|
import './chunk-PD6WW7E5.js';
|
|
19
19
|
import './chunk-XMGWLDNG.js';
|
|
20
20
|
import './chunk-PYRHNONA.js';
|
|
21
21
|
import './chunk-HF2HNDE7.js';
|
|
22
22
|
import './chunk-PTJPPKDR.js';
|
|
23
|
-
import './chunk-
|
|
23
|
+
import './chunk-QCFDSOTV.js';
|
|
24
24
|
import './chunk-HZTWLK7C.js';
|
|
25
|
-
import './chunk-
|
|
26
|
-
import './chunk-
|
|
27
|
-
import './chunk-
|
|
25
|
+
import './chunk-B4373MDA.js';
|
|
26
|
+
import './chunk-33UWVOQ6.js';
|
|
27
|
+
import './chunk-R4SBK6Y5.js';
|
|
28
28
|
import './chunk-PQBVNNEG.js';
|
|
29
29
|
import './chunk-B4YCI5NM.js';
|
|
30
30
|
import './chunk-L77KWJJB.js';
|
|
31
|
-
import './chunk-
|
|
31
|
+
import './chunk-ZKA5X3E4.js';
|
|
32
32
|
import './chunk-PJ7DVYWA.js';
|
|
33
33
|
import './chunk-HX3VXWNJ.js';
|
|
34
34
|
import './chunk-IYK2ABFE.js';
|
|
@@ -39,28 +39,28 @@ import './chunk-BZYMCJHW.js';
|
|
|
39
39
|
import './chunk-H466RJCI.js';
|
|
40
40
|
import './chunk-DT6DGTVW.js';
|
|
41
41
|
import './chunk-J5DRK4RF.js';
|
|
42
|
-
import './chunk-
|
|
42
|
+
import './chunk-3FJG5MAT.js';
|
|
43
43
|
import './chunk-STNVWBJH.js';
|
|
44
44
|
import { createAvatarDataUri } from './chunk-OWWQP3YW.js';
|
|
45
|
-
import './chunk-
|
|
45
|
+
import './chunk-FSU7ZM5V.js';
|
|
46
46
|
import './chunk-DARC2ACH.js';
|
|
47
47
|
import './chunk-4EHXVFQY.js';
|
|
48
|
-
import './chunk-
|
|
49
|
-
import './chunk-
|
|
48
|
+
import './chunk-7LYRUSWM.js';
|
|
49
|
+
import './chunk-BY6LWOHB.js';
|
|
50
50
|
import './chunk-DSNPOAE6.js';
|
|
51
51
|
import './chunk-JPA45CDE.js';
|
|
52
52
|
import './chunk-Q4CRHV5T.js';
|
|
53
53
|
import './chunk-KANK5FAG.js';
|
|
54
54
|
import './chunk-6NL36QN3.js';
|
|
55
55
|
import './chunk-CQ75K2DH.js';
|
|
56
|
-
import './chunk-
|
|
57
|
-
import './chunk-
|
|
56
|
+
import './chunk-UCFR7GLW.js';
|
|
57
|
+
import './chunk-UPMSE6PQ.js';
|
|
58
58
|
import './chunk-JVIRZNQ6.js';
|
|
59
59
|
import './chunk-GE4WUAAE.js';
|
|
60
|
-
import './chunk-
|
|
61
|
-
import './chunk-
|
|
62
|
-
import './chunk-
|
|
63
|
-
import './chunk-
|
|
60
|
+
import './chunk-H3KTAHJP.js';
|
|
61
|
+
import './chunk-NDQO7AO6.js';
|
|
62
|
+
import './chunk-AP5SGSN7.js';
|
|
63
|
+
import './chunk-XUWKGDUY.js';
|
|
64
64
|
import './chunk-D6MFOI3N.js';
|
|
65
65
|
import './chunk-7AUNUDHM.js';
|
|
66
66
|
import './chunk-4ZST7OY5.js';
|
|
@@ -73,10 +73,10 @@ import './chunk-IZ7A62GI.js';
|
|
|
73
73
|
import './chunk-BVXSAVKY.js';
|
|
74
74
|
import './chunk-GVOWGEGX.js';
|
|
75
75
|
import './chunk-KMTLCN6S.js';
|
|
76
|
-
import './chunk-
|
|
77
|
-
import './chunk-
|
|
78
|
-
import './chunk-
|
|
79
|
-
import './chunk-
|
|
76
|
+
import './chunk-U43JTK45.js';
|
|
77
|
+
import './chunk-5RBO2IMZ.js';
|
|
78
|
+
import './chunk-UMEBNHKR.js';
|
|
79
|
+
import './chunk-FX3GYS5O.js';
|
|
80
80
|
import './chunk-OMGVZWRM.js';
|
|
81
81
|
import './chunk-JVQQQS2M.js';
|
|
82
82
|
import './chunk-DGSNEGJP.js';
|
|
@@ -86,7 +86,7 @@ import './chunk-ANBJ2OLC.js';
|
|
|
86
86
|
import './chunk-LV4LBWCS.js';
|
|
87
87
|
import './chunk-7OOO22KB.js';
|
|
88
88
|
import './chunk-KWIIKHIB.js';
|
|
89
|
-
import './chunk-
|
|
89
|
+
import './chunk-TTDKPZ75.js';
|
|
90
90
|
import './chunk-STKNMEJA.js';
|
|
91
91
|
import './chunk-SIXPY62Q.js';
|
|
92
92
|
import './chunk-XZ3A33GP.js';
|
|
@@ -156,7 +156,7 @@ import './chunk-C4SNHMYC.js';
|
|
|
156
156
|
import './chunk-B5QL76GA.js';
|
|
157
157
|
import './chunk-KPO4PD6C.js';
|
|
158
158
|
import './chunk-LO3MDQSJ.js';
|
|
159
|
-
import './chunk-
|
|
159
|
+
import './chunk-BOWKE7OE.js';
|
|
160
160
|
import './chunk-B3BYBSF2.js';
|
|
161
161
|
import './chunk-FOFGPWFS.js';
|
|
162
162
|
import './chunk-VTAOHSPW.js';
|
|
@@ -179,7 +179,7 @@ import './chunk-RX5EUODB.js';
|
|
|
179
179
|
import './chunk-4GQ5EMWR.js';
|
|
180
180
|
import './chunk-TXRGQO5M.js';
|
|
181
181
|
import './chunk-UN2SJ42K.js';
|
|
182
|
-
import './chunk-
|
|
182
|
+
import './chunk-U5QLJHYN.js';
|
|
183
183
|
import './chunk-S2WSXZ7Y.js';
|
|
184
184
|
import './chunk-KXZP6XI2.js';
|
|
185
185
|
import './chunk-VJ5VD4UT.js';
|
|
@@ -208,7 +208,7 @@ import './chunk-MDAYDDTC.js';
|
|
|
208
208
|
import './chunk-TV6CJ5NI.js';
|
|
209
209
|
import './chunk-756Q7AC5.js';
|
|
210
210
|
import './chunk-MB75U2OV.js';
|
|
211
|
-
import './chunk-
|
|
211
|
+
import './chunk-UAN2YEI5.js';
|
|
212
212
|
import './chunk-SEJXMNMK.js';
|
|
213
213
|
import './chunk-VJIL7W55.js';
|
|
214
214
|
import './chunk-JDLCIPSC.js';
|
|
@@ -367,6 +367,9 @@ import './chunk-QYZT24TS.js';
|
|
|
367
367
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
368
368
|
|
|
369
369
|
var { InviteUserModal } = seedComponents;
|
|
370
|
+
var noop = () => {
|
|
371
|
+
};
|
|
372
|
+
var noopOn = () => ({ emit: noop, shouldPreventDefault: false, bound: false });
|
|
370
373
|
var initialMembers = [
|
|
371
374
|
{
|
|
372
375
|
id: "1",
|
|
@@ -411,11 +414,9 @@ function TeamPage() {
|
|
|
411
414
|
/* @__PURE__ */ jsx(
|
|
412
415
|
InviteUserModal,
|
|
413
416
|
{
|
|
414
|
-
props: {},
|
|
415
|
-
emit:
|
|
416
|
-
|
|
417
|
-
on: () => ({ emit: () => {
|
|
418
|
-
}, shouldPreventDefault: false, bound: false })
|
|
417
|
+
props: { email: null, role: null },
|
|
418
|
+
emit: noop,
|
|
419
|
+
on: noopOn
|
|
419
420
|
}
|
|
420
421
|
)
|
|
421
422
|
] }),
|