@maxhealth.tech/prefab 0.1.2 → 0.1.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/CHANGELOG.md +51 -51
- package/LICENSE +21 -21
- package/README.md +340 -340
- package/dist/app.js +19 -19
- package/dist/core/validate.js +4 -2
- package/dist/core/validate.js.map +1 -1
- package/dist/renderer/components/charts.js +7 -2
- package/dist/renderer/components/charts.js.map +1 -1
- package/dist/renderer/components/data.js +1 -1
- package/dist/renderer/components/data.js.map +1 -1
- package/dist/renderer/components/form.js +54 -6
- package/dist/renderer/components/form.js.map +1 -1
- package/dist/renderer/components/layout.js +1 -1
- package/dist/renderer/components/layout.js.map +1 -1
- package/dist/renderer/rx.js +4 -4
- package/dist/renderer/rx.js.map +1 -1
- package/dist/renderer.min.js +21 -21
- package/package.json +88 -84
package/README.md
CHANGED
|
@@ -1,340 +1,340 @@
|
|
|
1
|
-
# prefab
|
|
2
|
-
|
|
3
|
-
[](https://github.com/Max-Health-Inc/prefab-ui/actions/workflows/ci.yml)
|
|
4
|
-
[](https://www.npmjs.com/package/@maxhealth.tech/prefab)
|
|
5
|
-
[](https://www.typescriptlang.org/)
|
|
6
|
-
[](https://opensource.org/licenses/MIT)
|
|
7
|
-
|
|
8
|
-
TypeScript declarative UI component library for MCP apps. Wire-compatible with PrefectHQ's Python [prefab-ui](https://github.com/PrefectHQ/prefab) — **same `$prefab` JSON, two languages**.
|
|
9
|
-
|
|
10
|
-
Write MCP servers in **TypeScript/Bun** and generate the exact same wire format that Python servers produce. Render the output in **any web app** with the included vanilla DOM renderer. Full circle: server-side DSL → JSON → browser UI.
|
|
11
|
-
|
|
12
|
-
- **55+ components** — layout, form, data, charts, media, interactive, control flow
|
|
13
|
-
- **Reactive state** — `rx()` expressions, `SetState`/`ToggleState`/`AppendState` actions
|
|
14
|
-
- **MCP-native** — `display()`, `display_form()`, `CallTool`, `SendMessage` built in
|
|
15
|
-
- **Browser renderer** — zero dependencies, vanilla DOM (optional separate import)
|
|
16
|
-
- **ext-apps bridge** — `app()` factory with PostMessage transport, host theme, lifecycle hooks
|
|
17
|
-
- **Auto-renderers** — `autoTable()`, `autoChart()`, `autoForm()`, `autoMetrics()` and more
|
|
18
|
-
|
|
19
|
-
## Works Everywhere
|
|
20
|
-
|
|
21
|
-
The renderer is **vanilla DOM** — no framework dependency. Drop it into any web app:
|
|
22
|
-
|
|
23
|
-
- **React** — mount into a `ref` div
|
|
24
|
-
- **Vue / Svelte / Angular** — same, it's just DOM
|
|
25
|
-
- **Plain HTML** — single `<script>` tag
|
|
26
|
-
- **Electron / Tauri** — desktop apps with web views
|
|
27
|
-
- **Any iframe** — ext-apps, embedded widgets, sandboxed UIs
|
|
28
|
-
|
|
29
|
-
Any app that connects to MCP servers can render `$prefab` tool output as rich interactive UI — tables, charts, forms, badges — with zero custom code.
|
|
30
|
-
|
|
31
|
-
## Install
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
npm install @maxhealth.tech/prefab
|
|
35
|
-
# or
|
|
36
|
-
bun add @maxhealth.tech/prefab
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Quick Start
|
|
40
|
-
|
|
41
|
-
### Server-side (MCP tool handler)
|
|
42
|
-
|
|
43
|
-
```ts
|
|
44
|
-
import { display, autoTable, H1, Column } from '@maxhealth.tech/prefab'
|
|
45
|
-
|
|
46
|
-
async function listUsers(args: any) {
|
|
47
|
-
const users = await db.query('SELECT * FROM users')
|
|
48
|
-
return display(
|
|
49
|
-
Column({ children: [
|
|
50
|
-
H1('Users'),
|
|
51
|
-
autoTable(users),
|
|
52
|
-
]}),
|
|
53
|
-
{ title: 'User List' }
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Client-side (browser ext-app)
|
|
59
|
-
|
|
60
|
-
```html
|
|
61
|
-
<script src="https://cdn.jsdelivr.net/npm/@maxhealth.tech/prefab/dist/renderer.min.js"></script>
|
|
62
|
-
<script>
|
|
63
|
-
const ui = await prefab.app();
|
|
64
|
-
|
|
65
|
-
ui.onToolInput((args) => {
|
|
66
|
-
// Render wire-format JSON received from the MCP host
|
|
67
|
-
ui.mount('#root', args);
|
|
68
|
-
});
|
|
69
|
-
</script>
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Components
|
|
73
|
-
|
|
74
|
-
### Layout
|
|
75
|
-
`Column`, `Row`, `Grid`, `GridItem`, `Container`, `Div`, `Span`, `Dashboard`, `DashboardItem`, `Pages`, `Page`
|
|
76
|
-
|
|
77
|
-
### Typography
|
|
78
|
-
`Heading`, `H1`–`H4`, `Text`, `P`, `Lead`, `Large`, `Small`, `Muted`, `BlockQuote`, `Label`, `Link`, `Code`, `Markdown`, `Kbd`
|
|
79
|
-
|
|
80
|
-
### Card
|
|
81
|
-
`Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`
|
|
82
|
-
|
|
83
|
-
### Data Display
|
|
84
|
-
`DataTable`, `col`, `Badge`, `Dot`, `Metric`, `Ring`, `Progress`, `Separator`, `Loader`, `Icon`
|
|
85
|
-
|
|
86
|
-
### Table
|
|
87
|
-
`Table`, `TableHead`, `TableBody`, `TableFooter`, `TableRow`, `TableHeader`, `TableCell`, `TableCaption`, `ExpandableRow`
|
|
88
|
-
|
|
89
|
-
### Form
|
|
90
|
-
`Form`, `Input`, `Textarea`, `Button`, `ButtonGroup`, `Select`, `SelectOption`, `SelectGroup`, `SelectLabel`, `SelectSeparator`, `Checkbox`, `Switch`, `Slider`, `Radio`, `RadioGroup`, `Combobox`, `ComboboxOption`, `ComboboxGroup`, `ComboboxLabel`, `ComboboxSeparator`, `Calendar`, `DatePicker`, `Field`, `FieldTitle`, `FieldDescription`, `FieldContent`, `FieldError`, `ChoiceCard`
|
|
91
|
-
|
|
92
|
-
### Interactive
|
|
93
|
-
`Tabs`, `Tab`, `Accordion`, `AccordionItem`, `Dialog`, `Popover`, `Tooltip`, `HoverCard`, `Carousel`
|
|
94
|
-
|
|
95
|
-
### Charts
|
|
96
|
-
`BarChart`, `LineChart`, `AreaChart`, `PieChart`, `RadarChart`, `ScatterChart`, `Sparkline`, `RadialChart`, `Histogram`
|
|
97
|
-
|
|
98
|
-
### Media
|
|
99
|
-
`Image`, `Audio`, `Video`, `Embed`, `Svg`, `DropZone`, `Mermaid`
|
|
100
|
-
|
|
101
|
-
### Alert
|
|
102
|
-
`Alert`, `AlertTitle`, `AlertDescription`
|
|
103
|
-
|
|
104
|
-
### Control Flow
|
|
105
|
-
`ForEach`, `If`, `Elif`, `Else`, `Define`, `Use`, `Slot`
|
|
106
|
-
|
|
107
|
-
## Reactive Expressions
|
|
108
|
-
|
|
109
|
-
Use `rx()` to create reactive expressions that update when state changes:
|
|
110
|
-
|
|
111
|
-
```ts
|
|
112
|
-
import { rx, STATE } from '@maxhealth.tech/prefab'
|
|
113
|
-
|
|
114
|
-
// Simple state reference
|
|
115
|
-
Text(rx('count')) // → "{{ count }}"
|
|
116
|
-
|
|
117
|
-
// Arithmetic
|
|
118
|
-
Text(rx('count').add(1)) // → "{{ count + 1 }}"
|
|
119
|
-
|
|
120
|
-
// Dot-path access
|
|
121
|
-
Text(rx('user').dot('name')) // → "{{ user.name }}"
|
|
122
|
-
|
|
123
|
-
// Direct template string
|
|
124
|
-
Text('Hello, {{ user.name }}!') // interpolated at render time
|
|
125
|
-
|
|
126
|
-
// Ternary
|
|
127
|
-
Badge(rx('status').eq('active').then('Online', 'Offline'))
|
|
128
|
-
|
|
129
|
-
// Pipes (filters)
|
|
130
|
-
Text(rx('amount').currency()) // → "{{ amount | currency }}"
|
|
131
|
-
Text(rx('items').length()) // → "{{ items | length }}"
|
|
132
|
-
Text(rx('name').upper().truncate(20)) // → "{{ name | upper | truncate:20 }}"
|
|
133
|
-
|
|
134
|
-
// STATE proxy (single-level shorthand: STATE.key → rx('key'))
|
|
135
|
-
Text(STATE.count) // → "{{ count }}"
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
**Built-in pipes:** `upper`, `lower`, `capitalize`, `truncate`, `currency`, `number`, `percent`, `date`, `time`, `datetime`, `length`, `default`, `json`, `keys`, `values`, `first`, `last`
|
|
139
|
-
|
|
140
|
-
## Actions
|
|
141
|
-
|
|
142
|
-
Actions are triggered by user interactions (`onClick`, `onChange`, `onSubmit`) or lifecycle events (`onMount`):
|
|
143
|
-
|
|
144
|
-
```ts
|
|
145
|
-
import { SetState, ToggleState, CallTool, ShowToast, OpenLink, rx } from '@maxhealth.tech/prefab'
|
|
146
|
-
|
|
147
|
-
// Client-side state mutation
|
|
148
|
-
Button('Increment', { onClick: new SetState('count', rx('count').add(1)) })
|
|
149
|
-
|
|
150
|
-
// Toggle boolean
|
|
151
|
-
Button('Toggle', { onClick: new ToggleState('expanded') })
|
|
152
|
-
|
|
153
|
-
// MCP tool call
|
|
154
|
-
Button('Refresh', { onClick: new CallTool('get_data', { arguments: { id: '{{ selectedId }}' } }) })
|
|
155
|
-
|
|
156
|
-
// Toast notification
|
|
157
|
-
Button('Save', { onClick: new ShowToast('Saved!', { variant: 'success' }) })
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
**Client actions:** `SetState`, `ToggleState`, `AppendState`, `PopState`, `ShowToast`, `CloseOverlay`, `OpenLink`, `SetInterval`, `Fetch`, `OpenFilePicker`, `CallHandler`
|
|
161
|
-
|
|
162
|
-
**MCP actions:** `CallTool`, `SendMessage`, `UpdateContext`, `RequestDisplayMode`
|
|
163
|
-
|
|
164
|
-
## Auto-Renderers
|
|
165
|
-
|
|
166
|
-
Generate complete UIs from raw data — no manual component wiring:
|
|
167
|
-
|
|
168
|
-
```ts
|
|
169
|
-
import { autoTable, autoChart, autoForm, autoMetrics } from '@maxhealth.tech/prefab'
|
|
170
|
-
|
|
171
|
-
// Table from array of objects
|
|
172
|
-
autoTable(users, { title: 'Users', search: true })
|
|
173
|
-
|
|
174
|
-
// Chart from data + series definitions
|
|
175
|
-
autoChart(
|
|
176
|
-
salesData,
|
|
177
|
-
[{ dataKey: 'revenue', label: 'Revenue', color: '#3b82f6' }],
|
|
178
|
-
{ title: 'Revenue', chartType: 'bar', xAxis: 'month' },
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
// Form that submits to an MCP tool
|
|
182
|
-
autoForm(
|
|
183
|
-
[
|
|
184
|
-
{ name: 'email', type: 'email', required: true },
|
|
185
|
-
{ name: 'name', label: 'Full Name', required: true },
|
|
186
|
-
],
|
|
187
|
-
'create_user',
|
|
188
|
-
{ title: 'New User', submitLabel: 'Create' },
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
// KPI metric cards
|
|
192
|
-
autoMetrics([
|
|
193
|
-
{ label: 'Revenue', value: '$42K', delta: '+12%', trend: 'up', trendSentiment: 'positive' },
|
|
194
|
-
{ label: 'Users', value: '3,420', delta: '+5%', trend: 'up', trendSentiment: 'positive' },
|
|
195
|
-
])
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
**Auto-renderers:** `autoDetail`, `autoTable`, `autoChart`, `autoForm`, `autoComparison`, `autoMetrics`, `autoTimeline`, `autoProgress`
|
|
199
|
-
|
|
200
|
-
## MCP Display Helpers
|
|
201
|
-
|
|
202
|
-
Return UIs from MCP tool handlers:
|
|
203
|
-
|
|
204
|
-
```ts
|
|
205
|
-
import { display, display_form, display_update, display_error } from '@maxhealth.tech/prefab'
|
|
206
|
-
import { Column, H1 } from '@maxhealth.tech/prefab'
|
|
207
|
-
|
|
208
|
-
// Full UI
|
|
209
|
-
return display(Column({ children: [H1('Dashboard'), autoMetrics(kpis)] }), { title: 'Dashboard' })
|
|
210
|
-
|
|
211
|
-
// Form that submits back to a tool (fields, toolName, options)
|
|
212
|
-
return display_form(
|
|
213
|
-
[
|
|
214
|
-
{ name: 'name', type: 'text', required: true },
|
|
215
|
-
{ name: 'email', type: 'email' },
|
|
216
|
-
],
|
|
217
|
-
'update_user',
|
|
218
|
-
{ title: 'Edit User' },
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
// Partial state update (no full re-render)
|
|
222
|
-
return display_update({ count: 42, status: 'complete' })
|
|
223
|
-
|
|
224
|
-
// Error display
|
|
225
|
-
return display_error('User not found', { code: 404 })
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
## Browser Renderer
|
|
229
|
-
|
|
230
|
-
The renderer is a 54KB IIFE bundle with zero external dependencies:
|
|
231
|
-
|
|
232
|
-
```html
|
|
233
|
-
<script src="renderer.min.js"></script>
|
|
234
|
-
<script>
|
|
235
|
-
// Mount from wire format data
|
|
236
|
-
const app = PrefabRenderer.mount(document.getElementById('root'), wireData);
|
|
237
|
-
|
|
238
|
-
// Or auto-mount from embedded data
|
|
239
|
-
// (set window.__PREFAB_DATA__ before loading the script)
|
|
240
|
-
</script>
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
### ext-apps Bridge
|
|
244
|
-
|
|
245
|
-
For MCP ext-apps running in iframes:
|
|
246
|
-
|
|
247
|
-
```js
|
|
248
|
-
const ui = await prefab.app();
|
|
249
|
-
|
|
250
|
-
// Receive tool input from host
|
|
251
|
-
ui.onToolInput((args) => {
|
|
252
|
-
ui.render('#root', buildUI(args));
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
// Call tools on the host
|
|
256
|
-
const result = await ui.callTool('get_data', { query: 'active users' });
|
|
257
|
-
|
|
258
|
-
// Request display mode change
|
|
259
|
-
ui.requestMode('fullscreen');
|
|
260
|
-
|
|
261
|
-
// Access host context
|
|
262
|
-
console.log(ui.host); // { name, version, ... }
|
|
263
|
-
console.log(ui.capabilities); // { toast, clipboard, ... }
|
|
264
|
-
console.log(ui.theme); // host CSS variables
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
## Wire Format
|
|
268
|
-
|
|
269
|
-
All UIs serialize to the `$prefab` wire format (JSON):
|
|
270
|
-
|
|
271
|
-
```json
|
|
272
|
-
{
|
|
273
|
-
"$prefab": { "version": "0.2" },
|
|
274
|
-
"view": {
|
|
275
|
-
"type": "Column",
|
|
276
|
-
"children": [
|
|
277
|
-
{ "type": "H1", "content": "Hello" },
|
|
278
|
-
{ "type": "Text", "content": "{{ message }}" }
|
|
279
|
-
]
|
|
280
|
-
},
|
|
281
|
-
"state": {
|
|
282
|
-
"message": "Welcome to prefab"
|
|
283
|
-
},
|
|
284
|
-
"theme": {
|
|
285
|
-
"light": { "primary": "#3b82f6" },
|
|
286
|
-
"dark": { "primary": "#60a5fa" }
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
### Wire Format Fields
|
|
292
|
-
|
|
293
|
-
| Field | Type | Description |
|
|
294
|
-
|---|---|---|
|
|
295
|
-
| `$prefab` | `{ version: string }` | Format identifier and version |
|
|
296
|
-
| `view` | `ComponentJSON` | Root component tree |
|
|
297
|
-
| `state` | `Record<string, unknown>` | Initial reactive state |
|
|
298
|
-
| `theme` | `{ light?, dark? }` | CSS custom property overrides |
|
|
299
|
-
| `defs` | `Record<string, ComponentJSON>` | Reusable component templates |
|
|
300
|
-
| `keyBindings` | `Record<string, ActionJSON>` | Keyboard shortcut → action mappings |
|
|
301
|
-
|
|
302
|
-
### Component JSON Shape
|
|
303
|
-
|
|
304
|
-
```json
|
|
305
|
-
{
|
|
306
|
-
"type": "Button",
|
|
307
|
-
"content": "Click me",
|
|
308
|
-
"variant": "default",
|
|
309
|
-
"onClick": {
|
|
310
|
-
"action": "setState",
|
|
311
|
-
"key": "count",
|
|
312
|
-
"value": "{{ count + 1 }}"
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
## Subpath Exports
|
|
318
|
-
|
|
319
|
-
```ts
|
|
320
|
-
import { ... } from '@maxhealth.tech/prefab' // Everything
|
|
321
|
-
import { ... } from '@maxhealth.tech/prefab/actions' // Actions only
|
|
322
|
-
import { ... } from '@maxhealth.tech/prefab/rx' // Rx expressions only
|
|
323
|
-
import { ... } from '@maxhealth.tech/prefab/charts' // Chart components only
|
|
324
|
-
import { ... } from '@maxhealth.tech/prefab/mcp' // MCP display helpers
|
|
325
|
-
import { ... } from '@maxhealth.tech/prefab/renderer' // Browser renderer
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
## Development
|
|
329
|
-
|
|
330
|
-
```bash
|
|
331
|
-
bun install # Install dependencies
|
|
332
|
-
bun test # Run tests (362 passing)
|
|
333
|
-
bun run build # TypeScript compile + IIFE bundle
|
|
334
|
-
bun run lint # ESLint
|
|
335
|
-
bun run typecheck # Type check without emitting
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
## License
|
|
339
|
-
|
|
340
|
-
MIT
|
|
1
|
+
# prefab
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Max-Health-Inc/prefab-ui/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@maxhealth.tech/prefab)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
TypeScript declarative UI component library for MCP apps. Wire-compatible with PrefectHQ's Python [prefab-ui](https://github.com/PrefectHQ/prefab) — **same `$prefab` JSON, two languages**.
|
|
9
|
+
|
|
10
|
+
Write MCP servers in **TypeScript/Bun** and generate the exact same wire format that Python servers produce. Render the output in **any web app** with the included vanilla DOM renderer. Full circle: server-side DSL → JSON → browser UI.
|
|
11
|
+
|
|
12
|
+
- **55+ components** — layout, form, data, charts, media, interactive, control flow
|
|
13
|
+
- **Reactive state** — `rx()` expressions, `SetState`/`ToggleState`/`AppendState` actions
|
|
14
|
+
- **MCP-native** — `display()`, `display_form()`, `CallTool`, `SendMessage` built in
|
|
15
|
+
- **Browser renderer** — zero dependencies, vanilla DOM (optional separate import)
|
|
16
|
+
- **ext-apps bridge** — `app()` factory with PostMessage transport, host theme, lifecycle hooks
|
|
17
|
+
- **Auto-renderers** — `autoTable()`, `autoChart()`, `autoForm()`, `autoMetrics()` and more
|
|
18
|
+
|
|
19
|
+
## Works Everywhere
|
|
20
|
+
|
|
21
|
+
The renderer is **vanilla DOM** — no framework dependency. Drop it into any web app:
|
|
22
|
+
|
|
23
|
+
- **React** — mount into a `ref` div
|
|
24
|
+
- **Vue / Svelte / Angular** — same, it's just DOM
|
|
25
|
+
- **Plain HTML** — single `<script>` tag
|
|
26
|
+
- **Electron / Tauri** — desktop apps with web views
|
|
27
|
+
- **Any iframe** — ext-apps, embedded widgets, sandboxed UIs
|
|
28
|
+
|
|
29
|
+
Any app that connects to MCP servers can render `$prefab` tool output as rich interactive UI — tables, charts, forms, badges — with zero custom code.
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install @maxhealth.tech/prefab
|
|
35
|
+
# or
|
|
36
|
+
bun add @maxhealth.tech/prefab
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
### Server-side (MCP tool handler)
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { display, autoTable, H1, Column } from '@maxhealth.tech/prefab'
|
|
45
|
+
|
|
46
|
+
async function listUsers(args: any) {
|
|
47
|
+
const users = await db.query('SELECT * FROM users')
|
|
48
|
+
return display(
|
|
49
|
+
Column({ children: [
|
|
50
|
+
H1('Users'),
|
|
51
|
+
autoTable(users),
|
|
52
|
+
]}),
|
|
53
|
+
{ title: 'User List' }
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Client-side (browser ext-app)
|
|
59
|
+
|
|
60
|
+
```html
|
|
61
|
+
<script src="https://cdn.jsdelivr.net/npm/@maxhealth.tech/prefab/dist/renderer.min.js"></script>
|
|
62
|
+
<script>
|
|
63
|
+
const ui = await prefab.app();
|
|
64
|
+
|
|
65
|
+
ui.onToolInput((args) => {
|
|
66
|
+
// Render wire-format JSON received from the MCP host
|
|
67
|
+
ui.mount('#root', args);
|
|
68
|
+
});
|
|
69
|
+
</script>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Components
|
|
73
|
+
|
|
74
|
+
### Layout
|
|
75
|
+
`Column`, `Row`, `Grid`, `GridItem`, `Container`, `Div`, `Span`, `Dashboard`, `DashboardItem`, `Pages`, `Page`
|
|
76
|
+
|
|
77
|
+
### Typography
|
|
78
|
+
`Heading`, `H1`–`H4`, `Text`, `P`, `Lead`, `Large`, `Small`, `Muted`, `BlockQuote`, `Label`, `Link`, `Code`, `Markdown`, `Kbd`
|
|
79
|
+
|
|
80
|
+
### Card
|
|
81
|
+
`Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`
|
|
82
|
+
|
|
83
|
+
### Data Display
|
|
84
|
+
`DataTable`, `col`, `Badge`, `Dot`, `Metric`, `Ring`, `Progress`, `Separator`, `Loader`, `Icon`
|
|
85
|
+
|
|
86
|
+
### Table
|
|
87
|
+
`Table`, `TableHead`, `TableBody`, `TableFooter`, `TableRow`, `TableHeader`, `TableCell`, `TableCaption`, `ExpandableRow`
|
|
88
|
+
|
|
89
|
+
### Form
|
|
90
|
+
`Form`, `Input`, `Textarea`, `Button`, `ButtonGroup`, `Select`, `SelectOption`, `SelectGroup`, `SelectLabel`, `SelectSeparator`, `Checkbox`, `Switch`, `Slider`, `Radio`, `RadioGroup`, `Combobox`, `ComboboxOption`, `ComboboxGroup`, `ComboboxLabel`, `ComboboxSeparator`, `Calendar`, `DatePicker`, `Field`, `FieldTitle`, `FieldDescription`, `FieldContent`, `FieldError`, `ChoiceCard`
|
|
91
|
+
|
|
92
|
+
### Interactive
|
|
93
|
+
`Tabs`, `Tab`, `Accordion`, `AccordionItem`, `Dialog`, `Popover`, `Tooltip`, `HoverCard`, `Carousel`
|
|
94
|
+
|
|
95
|
+
### Charts
|
|
96
|
+
`BarChart`, `LineChart`, `AreaChart`, `PieChart`, `RadarChart`, `ScatterChart`, `Sparkline`, `RadialChart`, `Histogram`
|
|
97
|
+
|
|
98
|
+
### Media
|
|
99
|
+
`Image`, `Audio`, `Video`, `Embed`, `Svg`, `DropZone`, `Mermaid`
|
|
100
|
+
|
|
101
|
+
### Alert
|
|
102
|
+
`Alert`, `AlertTitle`, `AlertDescription`
|
|
103
|
+
|
|
104
|
+
### Control Flow
|
|
105
|
+
`ForEach`, `If`, `Elif`, `Else`, `Define`, `Use`, `Slot`
|
|
106
|
+
|
|
107
|
+
## Reactive Expressions
|
|
108
|
+
|
|
109
|
+
Use `rx()` to create reactive expressions that update when state changes:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { rx, STATE } from '@maxhealth.tech/prefab'
|
|
113
|
+
|
|
114
|
+
// Simple state reference
|
|
115
|
+
Text(rx('count')) // → "{{ count }}"
|
|
116
|
+
|
|
117
|
+
// Arithmetic
|
|
118
|
+
Text(rx('count').add(1)) // → "{{ count + 1 }}"
|
|
119
|
+
|
|
120
|
+
// Dot-path access
|
|
121
|
+
Text(rx('user').dot('name')) // → "{{ user.name }}"
|
|
122
|
+
|
|
123
|
+
// Direct template string
|
|
124
|
+
Text('Hello, {{ user.name }}!') // interpolated at render time
|
|
125
|
+
|
|
126
|
+
// Ternary
|
|
127
|
+
Badge(rx('status').eq('active').then('Online', 'Offline'))
|
|
128
|
+
|
|
129
|
+
// Pipes (filters)
|
|
130
|
+
Text(rx('amount').currency()) // → "{{ amount | currency }}"
|
|
131
|
+
Text(rx('items').length()) // → "{{ items | length }}"
|
|
132
|
+
Text(rx('name').upper().truncate(20)) // → "{{ name | upper | truncate:20 }}"
|
|
133
|
+
|
|
134
|
+
// STATE proxy (single-level shorthand: STATE.key → rx('key'))
|
|
135
|
+
Text(STATE.count) // → "{{ count }}"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Built-in pipes:** `upper`, `lower`, `capitalize`, `truncate`, `currency`, `number`, `percent`, `date`, `time`, `datetime`, `length`, `default`, `json`, `keys`, `values`, `first`, `last`
|
|
139
|
+
|
|
140
|
+
## Actions
|
|
141
|
+
|
|
142
|
+
Actions are triggered by user interactions (`onClick`, `onChange`, `onSubmit`) or lifecycle events (`onMount`):
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { SetState, ToggleState, CallTool, ShowToast, OpenLink, rx } from '@maxhealth.tech/prefab'
|
|
146
|
+
|
|
147
|
+
// Client-side state mutation
|
|
148
|
+
Button('Increment', { onClick: new SetState('count', rx('count').add(1)) })
|
|
149
|
+
|
|
150
|
+
// Toggle boolean
|
|
151
|
+
Button('Toggle', { onClick: new ToggleState('expanded') })
|
|
152
|
+
|
|
153
|
+
// MCP tool call
|
|
154
|
+
Button('Refresh', { onClick: new CallTool('get_data', { arguments: { id: '{{ selectedId }}' } }) })
|
|
155
|
+
|
|
156
|
+
// Toast notification
|
|
157
|
+
Button('Save', { onClick: new ShowToast('Saved!', { variant: 'success' }) })
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Client actions:** `SetState`, `ToggleState`, `AppendState`, `PopState`, `ShowToast`, `CloseOverlay`, `OpenLink`, `SetInterval`, `Fetch`, `OpenFilePicker`, `CallHandler`
|
|
161
|
+
|
|
162
|
+
**MCP actions:** `CallTool`, `SendMessage`, `UpdateContext`, `RequestDisplayMode`
|
|
163
|
+
|
|
164
|
+
## Auto-Renderers
|
|
165
|
+
|
|
166
|
+
Generate complete UIs from raw data — no manual component wiring:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { autoTable, autoChart, autoForm, autoMetrics } from '@maxhealth.tech/prefab'
|
|
170
|
+
|
|
171
|
+
// Table from array of objects
|
|
172
|
+
autoTable(users, { title: 'Users', search: true })
|
|
173
|
+
|
|
174
|
+
// Chart from data + series definitions
|
|
175
|
+
autoChart(
|
|
176
|
+
salesData,
|
|
177
|
+
[{ dataKey: 'revenue', label: 'Revenue', color: '#3b82f6' }],
|
|
178
|
+
{ title: 'Revenue', chartType: 'bar', xAxis: 'month' },
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
// Form that submits to an MCP tool
|
|
182
|
+
autoForm(
|
|
183
|
+
[
|
|
184
|
+
{ name: 'email', type: 'email', required: true },
|
|
185
|
+
{ name: 'name', label: 'Full Name', required: true },
|
|
186
|
+
],
|
|
187
|
+
'create_user',
|
|
188
|
+
{ title: 'New User', submitLabel: 'Create' },
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
// KPI metric cards
|
|
192
|
+
autoMetrics([
|
|
193
|
+
{ label: 'Revenue', value: '$42K', delta: '+12%', trend: 'up', trendSentiment: 'positive' },
|
|
194
|
+
{ label: 'Users', value: '3,420', delta: '+5%', trend: 'up', trendSentiment: 'positive' },
|
|
195
|
+
])
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Auto-renderers:** `autoDetail`, `autoTable`, `autoChart`, `autoForm`, `autoComparison`, `autoMetrics`, `autoTimeline`, `autoProgress`
|
|
199
|
+
|
|
200
|
+
## MCP Display Helpers
|
|
201
|
+
|
|
202
|
+
Return UIs from MCP tool handlers:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
import { display, display_form, display_update, display_error } from '@maxhealth.tech/prefab'
|
|
206
|
+
import { Column, H1 } from '@maxhealth.tech/prefab'
|
|
207
|
+
|
|
208
|
+
// Full UI
|
|
209
|
+
return display(Column({ children: [H1('Dashboard'), autoMetrics(kpis)] }), { title: 'Dashboard' })
|
|
210
|
+
|
|
211
|
+
// Form that submits back to a tool (fields, toolName, options)
|
|
212
|
+
return display_form(
|
|
213
|
+
[
|
|
214
|
+
{ name: 'name', type: 'text', required: true },
|
|
215
|
+
{ name: 'email', type: 'email' },
|
|
216
|
+
],
|
|
217
|
+
'update_user',
|
|
218
|
+
{ title: 'Edit User' },
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
// Partial state update (no full re-render)
|
|
222
|
+
return display_update({ count: 42, status: 'complete' })
|
|
223
|
+
|
|
224
|
+
// Error display
|
|
225
|
+
return display_error('User not found', { code: 404 })
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Browser Renderer
|
|
229
|
+
|
|
230
|
+
The renderer is a 54KB IIFE bundle with zero external dependencies:
|
|
231
|
+
|
|
232
|
+
```html
|
|
233
|
+
<script src="renderer.min.js"></script>
|
|
234
|
+
<script>
|
|
235
|
+
// Mount from wire format data
|
|
236
|
+
const app = PrefabRenderer.mount(document.getElementById('root'), wireData);
|
|
237
|
+
|
|
238
|
+
// Or auto-mount from embedded data
|
|
239
|
+
// (set window.__PREFAB_DATA__ before loading the script)
|
|
240
|
+
</script>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### ext-apps Bridge
|
|
244
|
+
|
|
245
|
+
For MCP ext-apps running in iframes:
|
|
246
|
+
|
|
247
|
+
```js
|
|
248
|
+
const ui = await prefab.app();
|
|
249
|
+
|
|
250
|
+
// Receive tool input from host
|
|
251
|
+
ui.onToolInput((args) => {
|
|
252
|
+
ui.render('#root', buildUI(args));
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Call tools on the host
|
|
256
|
+
const result = await ui.callTool('get_data', { query: 'active users' });
|
|
257
|
+
|
|
258
|
+
// Request display mode change
|
|
259
|
+
ui.requestMode('fullscreen');
|
|
260
|
+
|
|
261
|
+
// Access host context
|
|
262
|
+
console.log(ui.host); // { name, version, ... }
|
|
263
|
+
console.log(ui.capabilities); // { toast, clipboard, ... }
|
|
264
|
+
console.log(ui.theme); // host CSS variables
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Wire Format
|
|
268
|
+
|
|
269
|
+
All UIs serialize to the `$prefab` wire format (JSON):
|
|
270
|
+
|
|
271
|
+
```json
|
|
272
|
+
{
|
|
273
|
+
"$prefab": { "version": "0.2" },
|
|
274
|
+
"view": {
|
|
275
|
+
"type": "Column",
|
|
276
|
+
"children": [
|
|
277
|
+
{ "type": "H1", "content": "Hello" },
|
|
278
|
+
{ "type": "Text", "content": "{{ message }}" }
|
|
279
|
+
]
|
|
280
|
+
},
|
|
281
|
+
"state": {
|
|
282
|
+
"message": "Welcome to prefab"
|
|
283
|
+
},
|
|
284
|
+
"theme": {
|
|
285
|
+
"light": { "primary": "#3b82f6" },
|
|
286
|
+
"dark": { "primary": "#60a5fa" }
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Wire Format Fields
|
|
292
|
+
|
|
293
|
+
| Field | Type | Description |
|
|
294
|
+
|---|---|---|
|
|
295
|
+
| `$prefab` | `{ version: string }` | Format identifier and version |
|
|
296
|
+
| `view` | `ComponentJSON` | Root component tree |
|
|
297
|
+
| `state` | `Record<string, unknown>` | Initial reactive state |
|
|
298
|
+
| `theme` | `{ light?, dark? }` | CSS custom property overrides |
|
|
299
|
+
| `defs` | `Record<string, ComponentJSON>` | Reusable component templates |
|
|
300
|
+
| `keyBindings` | `Record<string, ActionJSON>` | Keyboard shortcut → action mappings |
|
|
301
|
+
|
|
302
|
+
### Component JSON Shape
|
|
303
|
+
|
|
304
|
+
```json
|
|
305
|
+
{
|
|
306
|
+
"type": "Button",
|
|
307
|
+
"content": "Click me",
|
|
308
|
+
"variant": "default",
|
|
309
|
+
"onClick": {
|
|
310
|
+
"action": "setState",
|
|
311
|
+
"key": "count",
|
|
312
|
+
"value": "{{ count + 1 }}"
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Subpath Exports
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
import { ... } from '@maxhealth.tech/prefab' // Everything
|
|
321
|
+
import { ... } from '@maxhealth.tech/prefab/actions' // Actions only
|
|
322
|
+
import { ... } from '@maxhealth.tech/prefab/rx' // Rx expressions only
|
|
323
|
+
import { ... } from '@maxhealth.tech/prefab/charts' // Chart components only
|
|
324
|
+
import { ... } from '@maxhealth.tech/prefab/mcp' // MCP display helpers
|
|
325
|
+
import { ... } from '@maxhealth.tech/prefab/renderer' // Browser renderer
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Development
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
bun install # Install dependencies
|
|
332
|
+
bun test # Run tests (362 passing)
|
|
333
|
+
bun run build # TypeScript compile + IIFE bundle
|
|
334
|
+
bun run lint # ESLint
|
|
335
|
+
bun run typecheck # Type check without emitting
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## License
|
|
339
|
+
|
|
340
|
+
MIT
|