@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.
Files changed (217) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/LICENSE +21 -0
  3. package/README.md +340 -0
  4. package/dist/actions/client.d.ts +98 -0
  5. package/dist/actions/client.d.ts.map +1 -0
  6. package/dist/actions/client.js +188 -0
  7. package/dist/actions/client.js.map +1 -0
  8. package/dist/actions/index.d.ts +6 -0
  9. package/dist/actions/index.d.ts.map +1 -0
  10. package/dist/actions/index.js +3 -0
  11. package/dist/actions/index.js.map +1 -0
  12. package/dist/actions/mcp.d.ts +37 -0
  13. package/dist/actions/mcp.d.ts.map +1 -0
  14. package/dist/actions/mcp.js +64 -0
  15. package/dist/actions/mcp.js.map +1 -0
  16. package/dist/actions/types.d.ts +16 -0
  17. package/dist/actions/types.d.ts.map +1 -0
  18. package/dist/actions/types.js +8 -0
  19. package/dist/actions/types.js.map +1 -0
  20. package/dist/app.d.ts +65 -0
  21. package/dist/app.d.ts.map +1 -0
  22. package/dist/app.js +120 -0
  23. package/dist/app.js.map +1 -0
  24. package/dist/auto/chart.d.ts +36 -0
  25. package/dist/auto/chart.d.ts.map +1 -0
  26. package/dist/auto/chart.js +50 -0
  27. package/dist/auto/chart.js.map +1 -0
  28. package/dist/auto/comparison.d.ts +32 -0
  29. package/dist/auto/comparison.d.ts.map +1 -0
  30. package/dist/auto/comparison.js +66 -0
  31. package/dist/auto/comparison.js.map +1 -0
  32. package/dist/auto/form.d.ts +50 -0
  33. package/dist/auto/form.d.ts.map +1 -0
  34. package/dist/auto/form.js +68 -0
  35. package/dist/auto/form.js.map +1 -0
  36. package/dist/auto/index.d.ts +84 -0
  37. package/dist/auto/index.d.ts.map +1 -0
  38. package/dist/auto/index.js +240 -0
  39. package/dist/auto/index.js.map +1 -0
  40. package/dist/auto/metrics.d.ts +44 -0
  41. package/dist/auto/metrics.d.ts.map +1 -0
  42. package/dist/auto/metrics.js +55 -0
  43. package/dist/auto/metrics.js.map +1 -0
  44. package/dist/auto/progress.d.ts +38 -0
  45. package/dist/auto/progress.d.ts.map +1 -0
  46. package/dist/auto/progress.js +72 -0
  47. package/dist/auto/progress.js.map +1 -0
  48. package/dist/auto/timeline.d.ts +41 -0
  49. package/dist/auto/timeline.d.ts.map +1 -0
  50. package/dist/auto/timeline.js +72 -0
  51. package/dist/auto/timeline.js.map +1 -0
  52. package/dist/components/alert/index.d.ts +14 -0
  53. package/dist/components/alert/index.d.ts.map +1 -0
  54. package/dist/components/alert/index.js +31 -0
  55. package/dist/components/alert/index.js.map +1 -0
  56. package/dist/components/card/index.d.ts +12 -0
  57. package/dist/components/card/index.d.ts.map +1 -0
  58. package/dist/components/card/index.js +33 -0
  59. package/dist/components/card/index.js.map +1 -0
  60. package/dist/components/charts/index.d.ts +57 -0
  61. package/dist/components/charts/index.d.ts.map +1 -0
  62. package/dist/components/charts/index.js +91 -0
  63. package/dist/components/charts/index.js.map +1 -0
  64. package/dist/components/control/index.d.ts +48 -0
  65. package/dist/components/control/index.d.ts.map +1 -0
  66. package/dist/components/control/index.js +47 -0
  67. package/dist/components/control/index.js.map +1 -0
  68. package/dist/components/data/index.d.ts +53 -0
  69. package/dist/components/data/index.d.ts.map +1 -0
  70. package/dist/components/data/index.js +88 -0
  71. package/dist/components/data/index.js.map +1 -0
  72. package/dist/components/form/index.d.ts +103 -0
  73. package/dist/components/form/index.d.ts.map +1 -0
  74. package/dist/components/form/index.js +247 -0
  75. package/dist/components/form/index.js.map +1 -0
  76. package/dist/components/interactive/index.d.ts +33 -0
  77. package/dist/components/interactive/index.d.ts.map +1 -0
  78. package/dist/components/interactive/index.js +59 -0
  79. package/dist/components/interactive/index.js.map +1 -0
  80. package/dist/components/layout/index.d.ts +61 -0
  81. package/dist/components/layout/index.d.ts.map +1 -0
  82. package/dist/components/layout/index.js +86 -0
  83. package/dist/components/layout/index.js.map +1 -0
  84. package/dist/components/media/index.d.ts +17 -0
  85. package/dist/components/media/index.d.ts.map +1 -0
  86. package/dist/components/media/index.js +41 -0
  87. package/dist/components/media/index.js.map +1 -0
  88. package/dist/components/table/index.d.ts +28 -0
  89. package/dist/components/table/index.d.ts.map +1 -0
  90. package/dist/components/table/index.js +56 -0
  91. package/dist/components/table/index.js.map +1 -0
  92. package/dist/components/typography/index.d.ts +34 -0
  93. package/dist/components/typography/index.d.ts.map +1 -0
  94. package/dist/components/typography/index.js +93 -0
  95. package/dist/components/typography/index.js.map +1 -0
  96. package/dist/core/component.d.ts +67 -0
  97. package/dist/core/component.d.ts.map +1 -0
  98. package/dist/core/component.js +122 -0
  99. package/dist/core/component.js.map +1 -0
  100. package/dist/core/index.d.ts +5 -0
  101. package/dist/core/index.d.ts.map +1 -0
  102. package/dist/core/index.js +3 -0
  103. package/dist/core/index.js.map +1 -0
  104. package/dist/core/validate.d.ts +30 -0
  105. package/dist/core/validate.d.ts.map +1 -0
  106. package/dist/core/validate.js +178 -0
  107. package/dist/core/validate.js.map +1 -0
  108. package/dist/index.d.ts +48 -0
  109. package/dist/index.d.ts.map +1 -0
  110. package/dist/index.js +48 -0
  111. package/dist/index.js.map +1 -0
  112. package/dist/mcp/display.d.ts +102 -0
  113. package/dist/mcp/display.d.ts.map +1 -0
  114. package/dist/mcp/display.js +124 -0
  115. package/dist/mcp/display.js.map +1 -0
  116. package/dist/mcp/index.d.ts +7 -0
  117. package/dist/mcp/index.d.ts.map +1 -0
  118. package/dist/mcp/index.js +5 -0
  119. package/dist/mcp/index.js.map +1 -0
  120. package/dist/mcp/types.d.ts +35 -0
  121. package/dist/mcp/types.d.ts.map +1 -0
  122. package/dist/mcp/types.js +8 -0
  123. package/dist/mcp/types.js.map +1 -0
  124. package/dist/renderer/actions.d.ts +36 -0
  125. package/dist/renderer/actions.d.ts.map +1 -0
  126. package/dist/renderer/actions.js +296 -0
  127. package/dist/renderer/actions.js.map +1 -0
  128. package/dist/renderer/app.d.ts +87 -0
  129. package/dist/renderer/app.d.ts.map +1 -0
  130. package/dist/renderer/app.js +166 -0
  131. package/dist/renderer/app.js.map +1 -0
  132. package/dist/renderer/bridge.d.ts +91 -0
  133. package/dist/renderer/bridge.d.ts.map +1 -0
  134. package/dist/renderer/bridge.js +302 -0
  135. package/dist/renderer/bridge.js.map +1 -0
  136. package/dist/renderer/components/alert.d.ts +5 -0
  137. package/dist/renderer/components/alert.d.ts.map +1 -0
  138. package/dist/renderer/components/alert.js +44 -0
  139. package/dist/renderer/components/alert.js.map +1 -0
  140. package/dist/renderer/components/card.d.ts +5 -0
  141. package/dist/renderer/components/card.d.ts.map +1 -0
  142. package/dist/renderer/components/card.js +38 -0
  143. package/dist/renderer/components/card.js.map +1 -0
  144. package/dist/renderer/components/charts.d.ts +8 -0
  145. package/dist/renderer/components/charts.d.ts.map +1 -0
  146. package/dist/renderer/components/charts.js +232 -0
  147. package/dist/renderer/components/charts.js.map +1 -0
  148. package/dist/renderer/components/control.d.ts +5 -0
  149. package/dist/renderer/components/control.d.ts.map +1 -0
  150. package/dist/renderer/components/control.js +140 -0
  151. package/dist/renderer/components/control.js.map +1 -0
  152. package/dist/renderer/components/data.d.ts +5 -0
  153. package/dist/renderer/components/data.d.ts.map +1 -0
  154. package/dist/renderer/components/data.js +268 -0
  155. package/dist/renderer/components/data.js.map +1 -0
  156. package/dist/renderer/components/form.d.ts +5 -0
  157. package/dist/renderer/components/form.d.ts.map +1 -0
  158. package/dist/renderer/components/form.js +523 -0
  159. package/dist/renderer/components/form.js.map +1 -0
  160. package/dist/renderer/components/index.d.ts +5 -0
  161. package/dist/renderer/components/index.d.ts.map +1 -0
  162. package/dist/renderer/components/index.js +32 -0
  163. package/dist/renderer/components/index.js.map +1 -0
  164. package/dist/renderer/components/interactive.d.ts +5 -0
  165. package/dist/renderer/components/interactive.d.ts.map +1 -0
  166. package/dist/renderer/components/interactive.js +324 -0
  167. package/dist/renderer/components/interactive.js.map +1 -0
  168. package/dist/renderer/components/layout.d.ts +5 -0
  169. package/dist/renderer/components/layout.d.ts.map +1 -0
  170. package/dist/renderer/components/layout.js +113 -0
  171. package/dist/renderer/components/layout.js.map +1 -0
  172. package/dist/renderer/components/media.d.ts +5 -0
  173. package/dist/renderer/components/media.d.ts.map +1 -0
  174. package/dist/renderer/components/media.js +132 -0
  175. package/dist/renderer/components/media.js.map +1 -0
  176. package/dist/renderer/components/table.d.ts +5 -0
  177. package/dist/renderer/components/table.d.ts.map +1 -0
  178. package/dist/renderer/components/table.js +101 -0
  179. package/dist/renderer/components/table.js.map +1 -0
  180. package/dist/renderer/components/typography.d.ts +5 -0
  181. package/dist/renderer/components/typography.d.ts.map +1 -0
  182. package/dist/renderer/components/typography.js +90 -0
  183. package/dist/renderer/components/typography.js.map +1 -0
  184. package/dist/renderer/engine.d.ts +53 -0
  185. package/dist/renderer/engine.d.ts.map +1 -0
  186. package/dist/renderer/engine.js +112 -0
  187. package/dist/renderer/engine.js.map +1 -0
  188. package/dist/renderer/index.d.ts +94 -0
  189. package/dist/renderer/index.d.ts.map +1 -0
  190. package/dist/renderer/index.js +215 -0
  191. package/dist/renderer/index.js.map +1 -0
  192. package/dist/renderer/rx.d.ts +37 -0
  193. package/dist/renderer/rx.d.ts.map +1 -0
  194. package/dist/renderer/rx.js +360 -0
  195. package/dist/renderer/rx.js.map +1 -0
  196. package/dist/renderer/state.d.ts +34 -0
  197. package/dist/renderer/state.d.ts.map +1 -0
  198. package/dist/renderer/state.js +132 -0
  199. package/dist/renderer/state.js.map +1 -0
  200. package/dist/renderer/theme.d.ts +13 -0
  201. package/dist/renderer/theme.d.ts.map +1 -0
  202. package/dist/renderer/theme.js +69 -0
  203. package/dist/renderer/theme.js.map +1 -0
  204. package/dist/renderer/transport.d.ts +22 -0
  205. package/dist/renderer/transport.d.ts.map +1 -0
  206. package/dist/renderer/transport.js +69 -0
  207. package/dist/renderer/transport.js.map +1 -0
  208. package/dist/renderer.min.js +83 -0
  209. package/dist/rx/index.d.ts +2 -0
  210. package/dist/rx/index.d.ts.map +1 -0
  211. package/dist/rx/index.js +2 -0
  212. package/dist/rx/index.js.map +1 -0
  213. package/dist/rx/rx.d.ts +81 -0
  214. package/dist/rx/rx.d.ts.map +1 -0
  215. package/dist/rx/rx.js +196 -0
  216. package/dist/rx/rx.js.map +1 -0
  217. 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
+ [![CI](https://github.com/Max-Health-Inc/prefab-ui/actions/workflows/ci.yml/badge.svg)](https://github.com/Max-Health-Inc/prefab-ui/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/@maxhealth.tech/prefab)](https://www.npmjs.com/package/@maxhealth.tech/prefab)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-6.0-blue?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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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"}