@maxhealth.tech/prefab 0.1.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/CHANGELOG.md +51 -0
- package/LICENSE +21 -0
- package/README.md +340 -0
- package/dist/actions/client.d.ts +98 -0
- package/dist/actions/client.d.ts.map +1 -0
- package/dist/actions/client.js +188 -0
- package/dist/actions/client.js.map +1 -0
- package/dist/actions/index.d.ts +6 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/index.js +3 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/actions/mcp.d.ts +37 -0
- package/dist/actions/mcp.d.ts.map +1 -0
- package/dist/actions/mcp.js +64 -0
- package/dist/actions/mcp.js.map +1 -0
- package/dist/actions/types.d.ts +16 -0
- package/dist/actions/types.d.ts.map +1 -0
- package/dist/actions/types.js +8 -0
- package/dist/actions/types.js.map +1 -0
- package/dist/app.d.ts +65 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +120 -0
- package/dist/app.js.map +1 -0
- package/dist/auto/chart.d.ts +36 -0
- package/dist/auto/chart.d.ts.map +1 -0
- package/dist/auto/chart.js +50 -0
- package/dist/auto/chart.js.map +1 -0
- package/dist/auto/comparison.d.ts +32 -0
- package/dist/auto/comparison.d.ts.map +1 -0
- package/dist/auto/comparison.js +66 -0
- package/dist/auto/comparison.js.map +1 -0
- package/dist/auto/form.d.ts +50 -0
- package/dist/auto/form.d.ts.map +1 -0
- package/dist/auto/form.js +68 -0
- package/dist/auto/form.js.map +1 -0
- package/dist/auto/index.d.ts +84 -0
- package/dist/auto/index.d.ts.map +1 -0
- package/dist/auto/index.js +240 -0
- package/dist/auto/index.js.map +1 -0
- package/dist/auto/metrics.d.ts +44 -0
- package/dist/auto/metrics.d.ts.map +1 -0
- package/dist/auto/metrics.js +55 -0
- package/dist/auto/metrics.js.map +1 -0
- package/dist/auto/progress.d.ts +38 -0
- package/dist/auto/progress.d.ts.map +1 -0
- package/dist/auto/progress.js +72 -0
- package/dist/auto/progress.js.map +1 -0
- package/dist/auto/timeline.d.ts +41 -0
- package/dist/auto/timeline.d.ts.map +1 -0
- package/dist/auto/timeline.js +72 -0
- package/dist/auto/timeline.js.map +1 -0
- package/dist/components/alert/index.d.ts +14 -0
- package/dist/components/alert/index.d.ts.map +1 -0
- package/dist/components/alert/index.js +31 -0
- package/dist/components/alert/index.js.map +1 -0
- package/dist/components/card/index.d.ts +12 -0
- package/dist/components/card/index.d.ts.map +1 -0
- package/dist/components/card/index.js +33 -0
- package/dist/components/card/index.js.map +1 -0
- package/dist/components/charts/index.d.ts +57 -0
- package/dist/components/charts/index.d.ts.map +1 -0
- package/dist/components/charts/index.js +91 -0
- package/dist/components/charts/index.js.map +1 -0
- package/dist/components/control/index.d.ts +48 -0
- package/dist/components/control/index.d.ts.map +1 -0
- package/dist/components/control/index.js +47 -0
- package/dist/components/control/index.js.map +1 -0
- package/dist/components/data/index.d.ts +53 -0
- package/dist/components/data/index.d.ts.map +1 -0
- package/dist/components/data/index.js +88 -0
- package/dist/components/data/index.js.map +1 -0
- package/dist/components/form/index.d.ts +103 -0
- package/dist/components/form/index.d.ts.map +1 -0
- package/dist/components/form/index.js +247 -0
- package/dist/components/form/index.js.map +1 -0
- package/dist/components/interactive/index.d.ts +33 -0
- package/dist/components/interactive/index.d.ts.map +1 -0
- package/dist/components/interactive/index.js +59 -0
- package/dist/components/interactive/index.js.map +1 -0
- package/dist/components/layout/index.d.ts +61 -0
- package/dist/components/layout/index.d.ts.map +1 -0
- package/dist/components/layout/index.js +86 -0
- package/dist/components/layout/index.js.map +1 -0
- package/dist/components/media/index.d.ts +17 -0
- package/dist/components/media/index.d.ts.map +1 -0
- package/dist/components/media/index.js +41 -0
- package/dist/components/media/index.js.map +1 -0
- package/dist/components/table/index.d.ts +28 -0
- package/dist/components/table/index.d.ts.map +1 -0
- package/dist/components/table/index.js +56 -0
- package/dist/components/table/index.js.map +1 -0
- package/dist/components/typography/index.d.ts +34 -0
- package/dist/components/typography/index.d.ts.map +1 -0
- package/dist/components/typography/index.js +93 -0
- package/dist/components/typography/index.js.map +1 -0
- package/dist/core/component.d.ts +67 -0
- package/dist/core/component.d.ts.map +1 -0
- package/dist/core/component.js +122 -0
- package/dist/core/component.js.map +1 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/validate.d.ts +30 -0
- package/dist/core/validate.d.ts.map +1 -0
- package/dist/core/validate.js +178 -0
- package/dist/core/validate.js.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/display.d.ts +102 -0
- package/dist/mcp/display.d.ts.map +1 -0
- package/dist/mcp/display.js +124 -0
- package/dist/mcp/display.js.map +1 -0
- package/dist/mcp/index.d.ts +7 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +5 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/types.d.ts +35 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +8 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/renderer/actions.d.ts +36 -0
- package/dist/renderer/actions.d.ts.map +1 -0
- package/dist/renderer/actions.js +296 -0
- package/dist/renderer/actions.js.map +1 -0
- package/dist/renderer/app.d.ts +87 -0
- package/dist/renderer/app.d.ts.map +1 -0
- package/dist/renderer/app.js +166 -0
- package/dist/renderer/app.js.map +1 -0
- package/dist/renderer/bridge.d.ts +91 -0
- package/dist/renderer/bridge.d.ts.map +1 -0
- package/dist/renderer/bridge.js +302 -0
- package/dist/renderer/bridge.js.map +1 -0
- package/dist/renderer/components/alert.d.ts +5 -0
- package/dist/renderer/components/alert.d.ts.map +1 -0
- package/dist/renderer/components/alert.js +44 -0
- package/dist/renderer/components/alert.js.map +1 -0
- package/dist/renderer/components/card.d.ts +5 -0
- package/dist/renderer/components/card.d.ts.map +1 -0
- package/dist/renderer/components/card.js +38 -0
- package/dist/renderer/components/card.js.map +1 -0
- package/dist/renderer/components/charts.d.ts +8 -0
- package/dist/renderer/components/charts.d.ts.map +1 -0
- package/dist/renderer/components/charts.js +232 -0
- package/dist/renderer/components/charts.js.map +1 -0
- package/dist/renderer/components/control.d.ts +5 -0
- package/dist/renderer/components/control.d.ts.map +1 -0
- package/dist/renderer/components/control.js +140 -0
- package/dist/renderer/components/control.js.map +1 -0
- package/dist/renderer/components/data.d.ts +5 -0
- package/dist/renderer/components/data.d.ts.map +1 -0
- package/dist/renderer/components/data.js +268 -0
- package/dist/renderer/components/data.js.map +1 -0
- package/dist/renderer/components/form.d.ts +5 -0
- package/dist/renderer/components/form.d.ts.map +1 -0
- package/dist/renderer/components/form.js +523 -0
- package/dist/renderer/components/form.js.map +1 -0
- package/dist/renderer/components/index.d.ts +5 -0
- package/dist/renderer/components/index.d.ts.map +1 -0
- package/dist/renderer/components/index.js +32 -0
- package/dist/renderer/components/index.js.map +1 -0
- package/dist/renderer/components/interactive.d.ts +5 -0
- package/dist/renderer/components/interactive.d.ts.map +1 -0
- package/dist/renderer/components/interactive.js +324 -0
- package/dist/renderer/components/interactive.js.map +1 -0
- package/dist/renderer/components/layout.d.ts +5 -0
- package/dist/renderer/components/layout.d.ts.map +1 -0
- package/dist/renderer/components/layout.js +113 -0
- package/dist/renderer/components/layout.js.map +1 -0
- package/dist/renderer/components/media.d.ts +5 -0
- package/dist/renderer/components/media.d.ts.map +1 -0
- package/dist/renderer/components/media.js +132 -0
- package/dist/renderer/components/media.js.map +1 -0
- package/dist/renderer/components/table.d.ts +5 -0
- package/dist/renderer/components/table.d.ts.map +1 -0
- package/dist/renderer/components/table.js +101 -0
- package/dist/renderer/components/table.js.map +1 -0
- package/dist/renderer/components/typography.d.ts +5 -0
- package/dist/renderer/components/typography.d.ts.map +1 -0
- package/dist/renderer/components/typography.js +90 -0
- package/dist/renderer/components/typography.js.map +1 -0
- package/dist/renderer/engine.d.ts +53 -0
- package/dist/renderer/engine.d.ts.map +1 -0
- package/dist/renderer/engine.js +112 -0
- package/dist/renderer/engine.js.map +1 -0
- package/dist/renderer/index.d.ts +94 -0
- package/dist/renderer/index.d.ts.map +1 -0
- package/dist/renderer/index.js +215 -0
- package/dist/renderer/index.js.map +1 -0
- package/dist/renderer/rx.d.ts +37 -0
- package/dist/renderer/rx.d.ts.map +1 -0
- package/dist/renderer/rx.js +360 -0
- package/dist/renderer/rx.js.map +1 -0
- package/dist/renderer/state.d.ts +34 -0
- package/dist/renderer/state.d.ts.map +1 -0
- package/dist/renderer/state.js +132 -0
- package/dist/renderer/state.js.map +1 -0
- package/dist/renderer/theme.d.ts +13 -0
- package/dist/renderer/theme.d.ts.map +1 -0
- package/dist/renderer/theme.js +69 -0
- package/dist/renderer/theme.js.map +1 -0
- package/dist/renderer/transport.d.ts +22 -0
- package/dist/renderer/transport.d.ts.map +1 -0
- package/dist/renderer/transport.js +69 -0
- package/dist/renderer/transport.js.map +1 -0
- package/dist/renderer.min.js +83 -0
- package/dist/rx/index.d.ts +2 -0
- package/dist/rx/index.d.ts.map +1 -0
- package/dist/rx/index.js +2 -0
- package/dist/rx/index.js.map +1 -0
- package/dist/rx/rx.d.ts +81 -0
- package/dist/rx/rx.d.ts.map +1 -0
- package/dist/rx/rx.js +196 -0
- package/dist/rx/rx.js.map +1 -0
- package/package.json +84 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.1.0] — 2026-04-20
|
|
6
|
+
|
|
7
|
+
Initial release.
|
|
8
|
+
|
|
9
|
+
### Component Library
|
|
10
|
+
- Core classes: `Component`, `ContainerComponent`, `StatefulComponent`
|
|
11
|
+
- `PrefabApp` wrapper with `$prefab` v0.2 wire format and `toHTML()` self-contained page export
|
|
12
|
+
- `rx()` reactive expression builder (pipes, comparisons, ternary, built-in vars)
|
|
13
|
+
- 80+ components across layout, typography, card, data, form, chart, control, interactive, media, alert
|
|
14
|
+
- Table components: `Table`, `TableHead`, `TableBody`, `TableFooter`, `TableRow`, `TableHeader`, `TableCell`, `TableCaption`, `ExpandableRow`
|
|
15
|
+
- Form extensions: `Radio`, `RadioGroup`, `Combobox`, `ComboboxOption`, `Calendar`, `DatePicker`, `Field`, `ChoiceCard`, and more
|
|
16
|
+
- Chart extensions: `RadialChart`, `Histogram` (in addition to Bar, Line, Area, Pie, Radar, Scatter, Sparkline)
|
|
17
|
+
- Composition: `Define`, `Use`, `Slot` for template reuse
|
|
18
|
+
- Client actions: `SetState`, `ToggleState`, `AppendState`, `PopState`, `ShowToast`, `CloseOverlay`, `OpenLink`, `SetInterval`, `Fetch`, `OpenFilePicker`, `CallHandler`
|
|
19
|
+
- MCP actions: `CallTool`, `SendMessage`, `UpdateContext`, `RequestDisplayMode`
|
|
20
|
+
|
|
21
|
+
### MCP Display Helpers
|
|
22
|
+
- `display()`, `display_form()`, `display_update()`, `display_error()`
|
|
23
|
+
|
|
24
|
+
### Auto-Renderers
|
|
25
|
+
- `autoDetail`, `autoTable`, `autoChart`, `autoForm`, `autoComparison`, `autoMetrics`, `autoTimeline`, `autoProgress`
|
|
26
|
+
|
|
27
|
+
### Browser Renderer
|
|
28
|
+
- Vanilla DOM renderer — 55+ components, zero framework dependencies
|
|
29
|
+
- Reactive `Store` with get/set/merge/toggle/append
|
|
30
|
+
- Rx expression engine: ternary, logical, arithmetic, 15+ pipes, dot access, scoped variables
|
|
31
|
+
- Action dispatcher — 15 action types
|
|
32
|
+
- MCP transport — HTTP POST to `/mcp/tools/call`
|
|
33
|
+
- Theme engine — CSS custom properties from `theme` field (light/dark)
|
|
34
|
+
- Chart renderer — built-in SVG for Bar, Line, Area, Pie
|
|
35
|
+
- Mermaid integration — delegates to global `mermaid` if available
|
|
36
|
+
- IIFE bundle: `renderer.min.js` (54KB) for `<script>` tag usage, `window.prefab` global
|
|
37
|
+
|
|
38
|
+
### ext-apps Bridge
|
|
39
|
+
- `app()` one-call factory with PostMessage transport, host theme mapping, lifecycle hooks
|
|
40
|
+
- Capability negotiation, display mode requests, tool input/result/cancelled/partial events
|
|
41
|
+
- Auto-detect environment: iframe → PostMessage, standalone → HTTP transport
|
|
42
|
+
|
|
43
|
+
### Validation & Accessibility
|
|
44
|
+
- `validateWireFormat()` + `isValidWireFormat()` with detailed error reporting
|
|
45
|
+
- Stylesheet injection — renderer applies `stylesheets` field as `<style>` tags
|
|
46
|
+
- ARIA roles/attributes and keyboard navigation on all interactive components
|
|
47
|
+
|
|
48
|
+
### Infra
|
|
49
|
+
- GitHub Actions CI (test + build on push) and publish (npm on tag)
|
|
50
|
+
- 253 tests passing across 11 files (570 assertions)
|
|
51
|
+
- MIT license
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Max Health Inc
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +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
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side actions — executed by the prefab renderer without server roundtrip.
|
|
3
|
+
*/
|
|
4
|
+
import type { Action, ActionJSON } from './types.js';
|
|
5
|
+
export interface SetStateOpts {
|
|
6
|
+
onSuccess?: Action | Action[];
|
|
7
|
+
onError?: Action | Action[];
|
|
8
|
+
}
|
|
9
|
+
export declare class SetState implements Action {
|
|
10
|
+
readonly key: string;
|
|
11
|
+
readonly value: unknown;
|
|
12
|
+
private readonly opts?;
|
|
13
|
+
constructor(key: string, value: unknown, opts?: SetStateOpts | undefined);
|
|
14
|
+
toJSON(): ActionJSON;
|
|
15
|
+
}
|
|
16
|
+
export declare class ToggleState implements Action {
|
|
17
|
+
readonly key: string;
|
|
18
|
+
constructor(key: string);
|
|
19
|
+
toJSON(): ActionJSON;
|
|
20
|
+
}
|
|
21
|
+
export declare class AppendState implements Action {
|
|
22
|
+
readonly key: string;
|
|
23
|
+
readonly value: unknown;
|
|
24
|
+
readonly index?: number | undefined;
|
|
25
|
+
constructor(key: string, value: unknown, index?: number | undefined);
|
|
26
|
+
toJSON(): ActionJSON;
|
|
27
|
+
}
|
|
28
|
+
export declare class PopState implements Action {
|
|
29
|
+
readonly key: string;
|
|
30
|
+
readonly index: number | string;
|
|
31
|
+
constructor(key: string, index: number | string);
|
|
32
|
+
toJSON(): ActionJSON;
|
|
33
|
+
}
|
|
34
|
+
export type ToastVariant = 'default' | 'success' | 'error' | 'warning' | 'info';
|
|
35
|
+
export interface ShowToastOpts {
|
|
36
|
+
description?: string;
|
|
37
|
+
variant?: ToastVariant;
|
|
38
|
+
duration?: number;
|
|
39
|
+
}
|
|
40
|
+
export declare class ShowToast implements Action {
|
|
41
|
+
readonly message: string;
|
|
42
|
+
private readonly opts?;
|
|
43
|
+
constructor(message: string, opts?: ShowToastOpts | undefined);
|
|
44
|
+
toJSON(): ActionJSON;
|
|
45
|
+
}
|
|
46
|
+
export declare class CloseOverlay implements Action {
|
|
47
|
+
toJSON(): ActionJSON;
|
|
48
|
+
}
|
|
49
|
+
export declare class OpenLink implements Action {
|
|
50
|
+
readonly url: string;
|
|
51
|
+
readonly target: string;
|
|
52
|
+
constructor(url: string, target?: string);
|
|
53
|
+
toJSON(): ActionJSON;
|
|
54
|
+
}
|
|
55
|
+
export declare class SetInterval implements Action {
|
|
56
|
+
readonly intervalMs: number;
|
|
57
|
+
readonly onTick: Action | Action[];
|
|
58
|
+
constructor(intervalMs: number, onTick: Action | Action[]);
|
|
59
|
+
toJSON(): ActionJSON;
|
|
60
|
+
}
|
|
61
|
+
export interface FetchOpts {
|
|
62
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
63
|
+
headers?: Record<string, string>;
|
|
64
|
+
body?: unknown;
|
|
65
|
+
resultKey?: string;
|
|
66
|
+
onSuccess?: Action | Action[];
|
|
67
|
+
onError?: Action | Action[];
|
|
68
|
+
}
|
|
69
|
+
export declare class Fetch implements Action {
|
|
70
|
+
readonly url: string;
|
|
71
|
+
private readonly opts?;
|
|
72
|
+
constructor(url: string, opts?: FetchOpts | undefined);
|
|
73
|
+
toJSON(): ActionJSON;
|
|
74
|
+
}
|
|
75
|
+
export interface OpenFilePickerOpts {
|
|
76
|
+
accept?: string;
|
|
77
|
+
multiple?: boolean;
|
|
78
|
+
resultKey?: string;
|
|
79
|
+
onSuccess?: Action | Action[];
|
|
80
|
+
}
|
|
81
|
+
export declare class OpenFilePicker implements Action {
|
|
82
|
+
private readonly opts?;
|
|
83
|
+
constructor(opts?: OpenFilePickerOpts | undefined);
|
|
84
|
+
toJSON(): ActionJSON;
|
|
85
|
+
}
|
|
86
|
+
export interface CallHandlerOpts {
|
|
87
|
+
arguments?: Record<string, unknown>;
|
|
88
|
+
resultKey?: string;
|
|
89
|
+
onSuccess?: Action | Action[];
|
|
90
|
+
onError?: Action | Action[];
|
|
91
|
+
}
|
|
92
|
+
export declare class CallHandler implements Action {
|
|
93
|
+
readonly handler: string;
|
|
94
|
+
private readonly opts?;
|
|
95
|
+
constructor(handler: string, opts?: CallHandlerOpts | undefined);
|
|
96
|
+
toJSON(): ActionJSON;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/actions/client.ts"],"names":[],"mappings":"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"}
|