pmx-canvas 0.1.0
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 +38 -0
- package/LICENSE +21 -0
- package/Readme.md +865 -0
- package/dist/canvas/global.css +3173 -0
- package/dist/canvas/index.js +183 -0
- package/dist/json-render/index.css +2 -0
- package/dist/json-render/index.js +389 -0
- package/dist/types/cli/agent.d.ts +13 -0
- package/dist/types/cli/index.d.ts +2 -0
- package/dist/types/cli/watch.d.ts +5 -0
- package/dist/types/client/App.d.ts +1 -0
- package/dist/types/client/canvas/AttentionHistory.d.ts +1 -0
- package/dist/types/client/canvas/AttentionToast.d.ts +1 -0
- package/dist/types/client/canvas/CanvasNode.d.ts +8 -0
- package/dist/types/client/canvas/CanvasViewport.d.ts +8 -0
- package/dist/types/client/canvas/CommandPalette.d.ts +4 -0
- package/dist/types/client/canvas/ContextMenu.d.ts +24 -0
- package/dist/types/client/canvas/ContextPinBar.d.ts +1 -0
- package/dist/types/client/canvas/ContextPinHud.d.ts +1 -0
- package/dist/types/client/canvas/DockedNode.d.ts +4 -0
- package/dist/types/client/canvas/EdgeLayer.d.ts +8 -0
- package/dist/types/client/canvas/ExpandedNodeOverlay.d.ts +1 -0
- package/dist/types/client/canvas/FocusFieldLayer.d.ts +1 -0
- package/dist/types/client/canvas/Minimap.d.ts +23 -0
- package/dist/types/client/canvas/SelectionBar.d.ts +1 -0
- package/dist/types/client/canvas/ShortcutOverlay.d.ts +3 -0
- package/dist/types/client/canvas/SnapshotPanel.d.ts +7 -0
- package/dist/types/client/canvas/snap-guides.d.ts +23 -0
- package/dist/types/client/canvas/use-node-drag.d.ts +15 -0
- package/dist/types/client/canvas/use-node-resize.d.ts +15 -0
- package/dist/types/client/canvas/use-pan-zoom.d.ts +16 -0
- package/dist/types/client/ext-app/bridge.d.ts +161 -0
- package/dist/types/client/icons.d.ts +70 -0
- package/dist/types/client/index.d.ts +1 -0
- package/dist/types/client/nodes/ContextNode.d.ts +34 -0
- package/dist/types/client/nodes/ExtAppFrame.d.ts +18 -0
- package/dist/types/client/nodes/FileNode.d.ts +5 -0
- package/dist/types/client/nodes/GroupNode.d.ts +6 -0
- package/dist/types/client/nodes/ImageNode.d.ts +10 -0
- package/dist/types/client/nodes/InlineFormatBar.d.ts +7 -0
- package/dist/types/client/nodes/InlineMarkdownEditor.d.ts +14 -0
- package/dist/types/client/nodes/LedgerNode.d.ts +4 -0
- package/dist/types/client/nodes/MarkdownNode.d.ts +6 -0
- package/dist/types/client/nodes/McpAppNode.d.ts +4 -0
- package/dist/types/client/nodes/MdFormatBar.d.ts +8 -0
- package/dist/types/client/nodes/PromptNode.d.ts +5 -0
- package/dist/types/client/nodes/ResponseNode.d.ts +5 -0
- package/dist/types/client/nodes/StatusNode.d.ts +4 -0
- package/dist/types/client/nodes/StatusSummary.d.ts +4 -0
- package/dist/types/client/nodes/TraceNode.d.ts +4 -0
- package/dist/types/client/nodes/WebpageNode.d.ts +5 -0
- package/dist/types/client/nodes/image-warnings.d.ts +6 -0
- package/dist/types/client/nodes/inline-editor-commands.d.ts +11 -0
- package/dist/types/client/nodes/md-format.d.ts +25 -0
- package/dist/types/client/state/attention-bridge.d.ts +3 -0
- package/dist/types/client/state/attention-store.d.ts +25 -0
- package/dist/types/client/state/canvas-store.d.ts +74 -0
- package/dist/types/client/state/intent-bridge.d.ts +158 -0
- package/dist/types/client/state/sse-bridge.d.ts +5 -0
- package/dist/types/client/theme/tokens.d.ts +27 -0
- package/dist/types/client/types.d.ts +40 -0
- package/dist/types/client/utils/ext-app-tool-result.d.ts +1 -0
- package/dist/types/client/utils/placement.d.ts +1 -0
- package/dist/types/client/utils/platform.d.ts +2 -0
- package/dist/types/json-render/catalog.d.ts +815 -0
- package/dist/types/json-render/charts/components.d.ts +54 -0
- package/dist/types/json-render/charts/definitions.d.ts +103 -0
- package/dist/types/json-render/charts/extra-components.d.ts +58 -0
- package/dist/types/json-render/charts/extra-definitions.d.ts +181 -0
- package/dist/types/json-render/renderer/index.d.ts +16 -0
- package/dist/types/json-render/schema.d.ts +46 -0
- package/dist/types/json-render/server.d.ts +55 -0
- package/dist/types/mcp/server.d.ts +22 -0
- package/dist/types/server/agent-context.d.ts +21 -0
- package/dist/types/server/artifact-paths.d.ts +3 -0
- package/dist/types/server/canvas-operations.d.ts +154 -0
- package/dist/types/server/canvas-provenance.d.ts +13 -0
- package/dist/types/server/canvas-schema.d.ts +49 -0
- package/dist/types/server/canvas-serialization.d.ts +25 -0
- package/dist/types/server/canvas-state.d.ts +174 -0
- package/dist/types/server/canvas-validation.d.ts +33 -0
- package/dist/types/server/chart-template.d.ts +29 -0
- package/dist/types/server/code-graph.d.ts +67 -0
- package/dist/types/server/context-cards.d.ts +24 -0
- package/dist/types/server/diagram-presets.d.ts +28 -0
- package/dist/types/server/ext-app-call-registry.d.ts +16 -0
- package/dist/types/server/ext-app-tool-result.d.ts +1 -0
- package/dist/types/server/file-watcher.d.ts +16 -0
- package/dist/types/server/index.d.ts +243 -0
- package/dist/types/server/mcp-app-candidate.d.ts +25 -0
- package/dist/types/server/mcp-app-host.d.ts +65 -0
- package/dist/types/server/mcp-app-runtime.d.ts +47 -0
- package/dist/types/server/mutation-history.d.ts +105 -0
- package/dist/types/server/placement.d.ts +37 -0
- package/dist/types/server/server.d.ts +103 -0
- package/dist/types/server/spatial-analysis.d.ts +87 -0
- package/dist/types/server/trace-manager.d.ts +48 -0
- package/dist/types/server/web-artifacts.d.ts +50 -0
- package/dist/types/server/webpage-node.d.ts +25 -0
- package/dist/types/shared/auto-arrange.d.ts +29 -0
- package/dist/types/shared/ext-app-tool-result.d.ts +9 -0
- package/dist/types/shared/placement.d.ts +26 -0
- package/dist/types/shared/semantic-attention.d.ts +97 -0
- package/package.json +109 -0
- package/skills/data-analysis/SKILL.md +324 -0
- package/skills/doc-coauthoring/SKILL.md +375 -0
- package/skills/frontend-design/SKILL.md +45 -0
- package/skills/json-render-codegen/SKILL.md +112 -0
- package/skills/json-render-core/SKILL.md +265 -0
- package/skills/json-render-ink/SKILL.md +273 -0
- package/skills/json-render-mcp/SKILL.md +132 -0
- package/skills/json-render-react/SKILL.md +264 -0
- package/skills/json-render-shadcn/SKILL.md +159 -0
- package/skills/playwright-cli/SKILL.md +67 -0
- package/skills/pmx-canvas/SKILL.md +668 -0
- package/skills/pmx-canvas/evals/evals.json +186 -0
- package/skills/pmx-canvas-testing/SKILL.md +78 -0
- package/skills/published-consumer-e2e/SKILL.md +43 -0
- package/skills/published-consumer-e2e/scripts/run-published-consumer-e2e.sh +241 -0
- package/skills/web-artifacts-builder/SKILL.md +80 -0
- package/skills/web-artifacts-builder/scripts/bundle-artifact.sh +167 -0
- package/skills/web-artifacts-builder/scripts/init-artifact.sh +425 -0
- package/skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
- package/skills/web-design-guidelines/SKILL.md +39 -0
- package/src/cli/agent.ts +2144 -0
- package/src/cli/index.ts +622 -0
- package/src/cli/watch.ts +88 -0
- package/src/client/App.tsx +507 -0
- package/src/client/canvas/AttentionHistory.tsx +81 -0
- package/src/client/canvas/AttentionToast.tsx +19 -0
- package/src/client/canvas/CanvasNode.tsx +363 -0
- package/src/client/canvas/CanvasViewport.tsx +590 -0
- package/src/client/canvas/CommandPalette.tsx +302 -0
- package/src/client/canvas/ContextMenu.tsx +601 -0
- package/src/client/canvas/ContextPinBar.tsx +25 -0
- package/src/client/canvas/ContextPinHud.tsx +22 -0
- package/src/client/canvas/DockedNode.tsx +66 -0
- package/src/client/canvas/EdgeLayer.tsx +280 -0
- package/src/client/canvas/ExpandedNodeOverlay.tsx +260 -0
- package/src/client/canvas/FocusFieldLayer.tsx +107 -0
- package/src/client/canvas/Minimap.tsx +301 -0
- package/src/client/canvas/SelectionBar.tsx +69 -0
- package/src/client/canvas/ShortcutOverlay.tsx +69 -0
- package/src/client/canvas/SnapshotPanel.tsx +236 -0
- package/src/client/canvas/snap-guides.ts +170 -0
- package/src/client/canvas/use-node-drag.ts +51 -0
- package/src/client/canvas/use-node-resize.ts +59 -0
- package/src/client/canvas/use-pan-zoom.ts +191 -0
- package/src/client/ext-app/bridge.ts +542 -0
- package/src/client/icons.tsx +424 -0
- package/src/client/index.tsx +7 -0
- package/src/client/nodes/ContextNode.tsx +412 -0
- package/src/client/nodes/ExtAppFrame.tsx +509 -0
- package/src/client/nodes/FileNode.tsx +256 -0
- package/src/client/nodes/GroupNode.tsx +39 -0
- package/src/client/nodes/ImageNode.tsx +160 -0
- package/src/client/nodes/InlineFormatBar.tsx +169 -0
- package/src/client/nodes/InlineMarkdownEditor.tsx +123 -0
- package/src/client/nodes/LedgerNode.tsx +37 -0
- package/src/client/nodes/MarkdownNode.tsx +359 -0
- package/src/client/nodes/McpAppNode.tsx +85 -0
- package/src/client/nodes/MdFormatBar.tsx +109 -0
- package/src/client/nodes/PromptNode.tsx +597 -0
- package/src/client/nodes/ResponseNode.tsx +153 -0
- package/src/client/nodes/StatusNode.tsx +84 -0
- package/src/client/nodes/StatusSummary.tsx +38 -0
- package/src/client/nodes/TraceNode.tsx +120 -0
- package/src/client/nodes/WebpageNode.tsx +288 -0
- package/src/client/nodes/image-warnings.ts +95 -0
- package/src/client/nodes/inline-editor-commands.ts +37 -0
- package/src/client/nodes/md-format.ts +206 -0
- package/src/client/state/attention-bridge.ts +328 -0
- package/src/client/state/attention-store.ts +73 -0
- package/src/client/state/canvas-store.ts +631 -0
- package/src/client/state/intent-bridge.ts +315 -0
- package/src/client/state/sse-bridge.ts +965 -0
- package/src/client/theme/global.css +3173 -0
- package/src/client/theme/tokens.ts +72 -0
- package/src/client/types-shims.d.ts +5 -0
- package/src/client/types.ts +81 -0
- package/src/client/utils/ext-app-tool-result.ts +4 -0
- package/src/client/utils/placement.ts +4 -0
- package/src/client/utils/platform.ts +2 -0
- package/src/json-render/catalog.ts +256 -0
- package/src/json-render/charts/components.tsx +198 -0
- package/src/json-render/charts/definitions.ts +81 -0
- package/src/json-render/charts/extra-components.tsx +267 -0
- package/src/json-render/charts/extra-definitions.ts +145 -0
- package/src/json-render/renderer/index.css +174 -0
- package/src/json-render/renderer/index.tsx +86 -0
- package/src/json-render/schema.ts +62 -0
- package/src/json-render/server.ts +597 -0
- package/src/mcp/server.ts +1377 -0
- package/src/server/agent-context.ts +242 -0
- package/src/server/artifact-paths.ts +17 -0
- package/src/server/canvas-operations.ts +1279 -0
- package/src/server/canvas-provenance.ts +243 -0
- package/src/server/canvas-schema.ts +432 -0
- package/src/server/canvas-serialization.ts +95 -0
- package/src/server/canvas-state.ts +1134 -0
- package/src/server/canvas-validation.ts +114 -0
- package/src/server/chart-template.ts +449 -0
- package/src/server/code-graph.ts +370 -0
- package/src/server/context-cards.ts +31 -0
- package/src/server/diagram-presets.ts +71 -0
- package/src/server/ext-app-call-registry.ts +77 -0
- package/src/server/ext-app-tool-result.ts +4 -0
- package/src/server/file-watcher.ts +121 -0
- package/src/server/index.ts +647 -0
- package/src/server/mcp-app-candidate.ts +174 -0
- package/src/server/mcp-app-host.ts +814 -0
- package/src/server/mcp-app-runtime.ts +459 -0
- package/src/server/mutation-history.ts +350 -0
- package/src/server/placement.ts +125 -0
- package/src/server/server.ts +3846 -0
- package/src/server/spatial-analysis.ts +356 -0
- package/src/server/trace-manager.ts +333 -0
- package/src/server/web-artifacts/scripts/bundle-artifact.sh +167 -0
- package/src/server/web-artifacts/scripts/init-artifact.sh +426 -0
- package/src/server/web-artifacts/scripts/shadcn-components.tar.gz +0 -0
- package/src/server/web-artifacts.ts +442 -0
- package/src/server/webpage-node.ts +328 -0
- package/src/shared/auto-arrange.ts +439 -0
- package/src/shared/ext-app-tool-result.ts +76 -0
- package/src/shared/placement.ts +81 -0
- package/src/shared/semantic-attention.ts +598 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: json-render-ink
|
|
3
|
+
description: Ink terminal renderer for json-render that turns JSON specs into interactive terminal UIs. Use when working with @json-render/ink, building terminal UIs from JSON, creating terminal component catalogs, or rendering AI-generated specs in the terminal.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @json-render/ink
|
|
7
|
+
|
|
8
|
+
Ink terminal renderer that converts JSON specs into interactive terminal component trees with standard components, data binding, visibility, actions, and dynamic props.
|
|
9
|
+
|
|
10
|
+
## Quick Start
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
import { defineCatalog } from "@json-render/core";
|
|
14
|
+
import { schema } from "@json-render/ink/schema";
|
|
15
|
+
import {
|
|
16
|
+
standardComponentDefinitions,
|
|
17
|
+
standardActionDefinitions,
|
|
18
|
+
} from "@json-render/ink/catalog";
|
|
19
|
+
import { defineRegistry, Renderer, type Components } from "@json-render/ink";
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
|
|
22
|
+
// Create catalog with standard + custom components
|
|
23
|
+
const catalog = defineCatalog(schema, {
|
|
24
|
+
components: {
|
|
25
|
+
...standardComponentDefinitions,
|
|
26
|
+
CustomWidget: {
|
|
27
|
+
props: z.object({ title: z.string() }),
|
|
28
|
+
slots: [],
|
|
29
|
+
description: "Custom widget",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
actions: standardActionDefinitions,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Register only custom components (standard ones are built-in)
|
|
36
|
+
const { registry } = defineRegistry(catalog, {
|
|
37
|
+
components: {
|
|
38
|
+
CustomWidget: ({ props }) => <Text>{props.title}</Text>,
|
|
39
|
+
} as Components<typeof catalog>,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Render
|
|
43
|
+
function App({ spec }) {
|
|
44
|
+
return (
|
|
45
|
+
<JSONUIProvider initialState={{}}>
|
|
46
|
+
<Renderer spec={spec} registry={registry} />
|
|
47
|
+
</JSONUIProvider>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Spec Structure (Flat Element Map)
|
|
53
|
+
|
|
54
|
+
The Ink schema uses a flat element map with a root key:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"root": "main",
|
|
59
|
+
"elements": {
|
|
60
|
+
"main": {
|
|
61
|
+
"type": "Box",
|
|
62
|
+
"props": { "flexDirection": "column", "padding": 1 },
|
|
63
|
+
"children": ["heading", "content"]
|
|
64
|
+
},
|
|
65
|
+
"heading": {
|
|
66
|
+
"type": "Heading",
|
|
67
|
+
"props": { "text": "Dashboard", "level": "h1" },
|
|
68
|
+
"children": []
|
|
69
|
+
},
|
|
70
|
+
"content": {
|
|
71
|
+
"type": "Text",
|
|
72
|
+
"props": { "text": "Hello from the terminal!" },
|
|
73
|
+
"children": []
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Standard Components
|
|
80
|
+
|
|
81
|
+
### Layout
|
|
82
|
+
- `Box` - Flexbox layout container (like a terminal `<div>`). Use for grouping, spacing, borders, alignment. Default flexDirection is row.
|
|
83
|
+
- `Text` - Text output with optional styling (color, bold, italic, etc.)
|
|
84
|
+
- `Newline` - Inserts blank lines. Must be inside a Box with flexDirection column.
|
|
85
|
+
- `Spacer` - Flexible empty space that expands along the main axis.
|
|
86
|
+
|
|
87
|
+
### Content
|
|
88
|
+
- `Heading` - Section heading (h1: bold+underlined, h2: bold, h3: bold+dimmed, h4: dimmed)
|
|
89
|
+
- `Divider` - Horizontal separator with optional centered title
|
|
90
|
+
- `Badge` - Colored inline label (variants: default, info, success, warning, error)
|
|
91
|
+
- `Spinner` - Animated loading spinner with optional label
|
|
92
|
+
- `ProgressBar` - Horizontal progress bar (0-1)
|
|
93
|
+
- `Sparkline` - Inline chart using Unicode block characters
|
|
94
|
+
- `BarChart` - Horizontal bar chart with labels and values
|
|
95
|
+
- `Table` - Tabular data with headers and rows
|
|
96
|
+
- `List` - Bulleted or numbered list
|
|
97
|
+
- `ListItem` - Structured list row with title, subtitle, leading/trailing text
|
|
98
|
+
- `Card` - Bordered container with optional title
|
|
99
|
+
- `KeyValue` - Key-value pair display
|
|
100
|
+
- `Link` - Clickable URL with optional label
|
|
101
|
+
- `StatusLine` - Status message with colored icon (info, success, warning, error)
|
|
102
|
+
- `Markdown` - Renders markdown text with terminal styling
|
|
103
|
+
|
|
104
|
+
### Interactive
|
|
105
|
+
- `TextInput` - Text input field (events: submit, change)
|
|
106
|
+
- `Select` - Selection menu with arrow key navigation (events: change)
|
|
107
|
+
- `MultiSelect` - Multi-selection with space to toggle (events: change, submit)
|
|
108
|
+
- `ConfirmInput` - Yes/No confirmation prompt (events: confirm, deny)
|
|
109
|
+
- `Tabs` - Tab bar navigation with left/right arrow keys (events: change)
|
|
110
|
+
|
|
111
|
+
## Visibility Conditions
|
|
112
|
+
|
|
113
|
+
Use `visible` on elements to show/hide based on state. Syntax: `{ "$state": "/path" }`, `{ "$state": "/path", "eq": value }`, `{ "$state": "/path", "not": true }`, `{ "$and": [cond1, cond2] }` for AND, `{ "$or": [cond1, cond2] }` for OR.
|
|
114
|
+
|
|
115
|
+
## Dynamic Prop Expressions
|
|
116
|
+
|
|
117
|
+
Any prop value can be a data-driven expression resolved at render time:
|
|
118
|
+
|
|
119
|
+
- **`{ "$state": "/state/key" }`** - reads from state model (one-way read)
|
|
120
|
+
- **`{ "$bindState": "/path" }`** - two-way binding: use on the natural value prop of form components
|
|
121
|
+
- **`{ "$bindItem": "field" }`** - two-way binding to a repeat item field
|
|
122
|
+
- **`{ "$cond": <condition>, "$then": <value>, "$else": <value> }`** - conditional value
|
|
123
|
+
- **`{ "$template": "Hello, ${/name}!" }`** - interpolates state values into strings
|
|
124
|
+
|
|
125
|
+
Components do not use a `statePath` prop for two-way binding. Use `{ "$bindState": "/path" }` on the natural value prop instead.
|
|
126
|
+
|
|
127
|
+
## Event System
|
|
128
|
+
|
|
129
|
+
Components use `emit` to fire named events. The element's `on` field maps events to action bindings:
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
CustomButton: ({ props, emit }) => (
|
|
133
|
+
<Box>
|
|
134
|
+
<Text>{props.label}</Text>
|
|
135
|
+
{/* emit("press") triggers the action bound in the spec's on.press */}
|
|
136
|
+
</Box>
|
|
137
|
+
),
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"type": "CustomButton",
|
|
143
|
+
"props": { "label": "Submit" },
|
|
144
|
+
"on": { "press": { "action": "submit" } },
|
|
145
|
+
"children": []
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Built-in Actions
|
|
150
|
+
|
|
151
|
+
`setState`, `pushState`, and `removeState` are built-in and handled automatically:
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{ "action": "setState", "params": { "statePath": "/activeTab", "value": "home" } }
|
|
155
|
+
{ "action": "pushState", "params": { "statePath": "/items", "value": { "text": "New" } } }
|
|
156
|
+
{ "action": "removeState", "params": { "statePath": "/items", "index": 0 } }
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Repeat (Dynamic Lists)
|
|
160
|
+
|
|
161
|
+
Use the `repeat` field on a container element to render items from a state array:
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"type": "Box",
|
|
166
|
+
"props": { "flexDirection": "column" },
|
|
167
|
+
"repeat": { "statePath": "/items", "key": "id" },
|
|
168
|
+
"children": ["item-row"]
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Inside repeated children, use `{ "$item": "field" }` to read from the current item and `{ "$index": true }` for the current index.
|
|
173
|
+
|
|
174
|
+
## Streaming
|
|
175
|
+
|
|
176
|
+
Use `useUIStream` to progressively render specs from JSONL patch streams:
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
import { useUIStream } from "@json-render/ink";
|
|
180
|
+
|
|
181
|
+
const { spec, send, isStreaming } = useUIStream({ api: "/api/generate" });
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Server-Side Prompt Generation
|
|
185
|
+
|
|
186
|
+
Use the `./server` export to generate AI system prompts from your catalog:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
import { catalog } from "./catalog";
|
|
190
|
+
|
|
191
|
+
const systemPrompt = catalog.prompt({ system: "You are a terminal assistant." });
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Providers
|
|
195
|
+
|
|
196
|
+
| Provider | Purpose |
|
|
197
|
+
|----------|---------|
|
|
198
|
+
| `StateProvider` | Share state across components (JSON Pointer paths). Accepts optional `store` prop for controlled mode. |
|
|
199
|
+
| `ActionProvider` | Handle actions dispatched via the event system |
|
|
200
|
+
| `VisibilityProvider` | Enable conditional rendering based on state |
|
|
201
|
+
| `ValidationProvider` | Form field validation |
|
|
202
|
+
| `FocusProvider` | Manage focus across interactive components |
|
|
203
|
+
| `JSONUIProvider` | Combined provider for all contexts |
|
|
204
|
+
|
|
205
|
+
### External Store (Controlled Mode)
|
|
206
|
+
|
|
207
|
+
Pass a `StateStore` to `StateProvider` (or `JSONUIProvider`) to use external state management:
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
import { createStateStore, type StateStore } from "@json-render/ink";
|
|
211
|
+
|
|
212
|
+
const store = createStateStore({ count: 0 });
|
|
213
|
+
|
|
214
|
+
<StateProvider store={store}>{children}</StateProvider>
|
|
215
|
+
|
|
216
|
+
store.set("/count", 1); // React re-renders automatically
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
When `store` is provided, `initialState` and `onStateChange` are ignored.
|
|
220
|
+
|
|
221
|
+
## createRenderer (Higher-Level API)
|
|
222
|
+
|
|
223
|
+
```tsx
|
|
224
|
+
import { createRenderer } from "@json-render/ink";
|
|
225
|
+
import { standardComponents } from "@json-render/ink";
|
|
226
|
+
import { catalog } from "./catalog";
|
|
227
|
+
|
|
228
|
+
const InkRenderer = createRenderer(catalog, {
|
|
229
|
+
...standardComponents,
|
|
230
|
+
// custom component overrides here
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// InkRenderer includes all providers (state, visibility, actions, focus)
|
|
234
|
+
render(
|
|
235
|
+
<InkRenderer spec={spec} state={{ activeTab: "overview" }} />
|
|
236
|
+
);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Key Exports
|
|
240
|
+
|
|
241
|
+
| Export | Purpose |
|
|
242
|
+
|--------|---------|
|
|
243
|
+
| `defineRegistry` | Create a type-safe component registry from a catalog |
|
|
244
|
+
| `Renderer` | Render a spec using a registry |
|
|
245
|
+
| `createRenderer` | Higher-level: creates a component with built-in providers |
|
|
246
|
+
| `JSONUIProvider` | Combined provider for all contexts |
|
|
247
|
+
| `schema` | Ink flat element map schema (includes built-in state actions) |
|
|
248
|
+
| `standardComponentDefinitions` | Catalog definitions for all standard components |
|
|
249
|
+
| `standardActionDefinitions` | Catalog definitions for standard actions |
|
|
250
|
+
| `standardComponents` | Pre-built component implementations |
|
|
251
|
+
| `useStateStore` | Access state context |
|
|
252
|
+
| `useStateValue` | Get single value from state |
|
|
253
|
+
| `useBoundProp` | Two-way binding for `$bindState`/`$bindItem` expressions |
|
|
254
|
+
| `useActions` | Access actions context |
|
|
255
|
+
| `useAction` | Get a single action dispatch function |
|
|
256
|
+
| `useOptionalValidation` | Non-throwing variant of useValidation |
|
|
257
|
+
| `useUIStream` | Stream specs from an API endpoint |
|
|
258
|
+
| `createStateStore` | Create a framework-agnostic in-memory `StateStore` |
|
|
259
|
+
| `StateStore` | Interface for plugging in external state management |
|
|
260
|
+
| `Components` | Typed component map (catalog-aware) |
|
|
261
|
+
| `Actions` | Typed action map (catalog-aware) |
|
|
262
|
+
| `ComponentContext` | Typed component context (catalog-aware) |
|
|
263
|
+
| `flatToTree` | Convert flat element map to tree structure |
|
|
264
|
+
|
|
265
|
+
## Terminal UI Design Guidelines
|
|
266
|
+
|
|
267
|
+
- Use Box for layout (flexDirection, padding, gap). Default flexDirection is row.
|
|
268
|
+
- Terminal width is ~80-120 columns. Prefer vertical layouts (flexDirection: column) for main structure.
|
|
269
|
+
- Use borderStyle on Box for visual grouping (single, double, round, bold).
|
|
270
|
+
- Use named terminal colors: red, green, yellow, blue, magenta, cyan, white, gray.
|
|
271
|
+
- Use Heading for section titles, Divider to separate sections, Badge for status, KeyValue for labeled data, Card for bordered groups.
|
|
272
|
+
- Use Tabs for multi-view UIs with visible conditions on child content.
|
|
273
|
+
- Use Sparkline for inline trends and BarChart for comparing values.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: json-render-mcp
|
|
3
|
+
description: MCP Apps integration for json-render. Use when building MCP servers that render interactive UIs in Claude, ChatGPT, Cursor, or VS Code, or when integrating json-render with the Model Context Protocol.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @json-render/mcp
|
|
7
|
+
|
|
8
|
+
MCP Apps integration that serves json-render UIs as interactive MCP Apps inside Claude, ChatGPT, Cursor, VS Code, and other MCP-capable clients.
|
|
9
|
+
|
|
10
|
+
In `pmx-canvas`, prefer the native `canvas_add_json_render_node` and `canvas_add_graph_node`
|
|
11
|
+
tools first. They store validated specs directly in canvas node state and render them through the
|
|
12
|
+
local `pmx-canvas` viewer route without needing a separate sidecar server.
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
### Server (Node.js)
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { createMcpApp } from "@json-render/mcp";
|
|
20
|
+
import { defineCatalog } from "@json-render/core";
|
|
21
|
+
import { schema } from "@json-render/react/schema";
|
|
22
|
+
import { shadcnComponentDefinitions } from "@json-render/shadcn/catalog";
|
|
23
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
24
|
+
import fs from "node:fs";
|
|
25
|
+
|
|
26
|
+
const catalog = defineCatalog(schema, {
|
|
27
|
+
components: { ...shadcnComponentDefinitions },
|
|
28
|
+
actions: {},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const server = createMcpApp({
|
|
32
|
+
name: "My App",
|
|
33
|
+
version: "1.0.0",
|
|
34
|
+
catalog,
|
|
35
|
+
html: fs.readFileSync("dist/index.html", "utf-8"),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await server.connect(new StdioServerTransport());
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Client (React, inside iframe)
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import { useJsonRenderApp } from "@json-render/mcp/app";
|
|
45
|
+
import { JSONUIProvider, Renderer } from "@json-render/react";
|
|
46
|
+
|
|
47
|
+
function McpAppView({ registry }) {
|
|
48
|
+
const { spec, loading, error } = useJsonRenderApp();
|
|
49
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
50
|
+
if (!spec) return <div>Waiting...</div>;
|
|
51
|
+
return (
|
|
52
|
+
<JSONUIProvider registry={registry} initialState={spec.state ?? {}}>
|
|
53
|
+
<Renderer spec={spec} registry={registry} loading={loading} />
|
|
54
|
+
</JSONUIProvider>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Architecture
|
|
60
|
+
|
|
61
|
+
1. `createMcpApp()` creates an `McpServer` that registers a `render-ui` tool and a `ui://` HTML resource
|
|
62
|
+
2. The tool description includes the catalog prompt so the LLM knows how to generate valid specs
|
|
63
|
+
3. The HTML resource is a Vite-bundled single-file React app with json-render renderers
|
|
64
|
+
4. Inside the iframe, `useJsonRenderApp()` connects to the host via `postMessage` and renders specs
|
|
65
|
+
|
|
66
|
+
## Server API
|
|
67
|
+
|
|
68
|
+
- `createMcpApp(options)` - main entry, creates a full MCP server
|
|
69
|
+
- `registerJsonRenderTool(server, options)` - register a json-render tool on an existing server
|
|
70
|
+
- `registerJsonRenderResource(server, options)` - register the UI resource
|
|
71
|
+
|
|
72
|
+
## Client API (`@json-render/mcp/app`)
|
|
73
|
+
|
|
74
|
+
- `useJsonRenderApp(options?)` - React hook, returns `{ spec, loading, connected, error, callServerTool }`
|
|
75
|
+
- `buildAppHtml(options)` - generate HTML from bundled JS/CSS
|
|
76
|
+
|
|
77
|
+
## Building the iframe HTML
|
|
78
|
+
|
|
79
|
+
Bundle the React app into a single self-contained HTML file using Vite + `vite-plugin-singlefile`:
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// vite.config.ts
|
|
83
|
+
import { defineConfig } from "vite";
|
|
84
|
+
import react from "@vitejs/plugin-react";
|
|
85
|
+
import { viteSingleFile } from "vite-plugin-singlefile";
|
|
86
|
+
|
|
87
|
+
export default defineConfig({
|
|
88
|
+
plugins: [react(), viteSingleFile()],
|
|
89
|
+
build: { outDir: "dist" },
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Client Configuration
|
|
94
|
+
|
|
95
|
+
### Cursor (`.cursor/mcp.json`)
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"mcpServers": {
|
|
100
|
+
"my-app": {
|
|
101
|
+
"command": "npx",
|
|
102
|
+
"args": ["tsx", "server.ts", "--stdio"]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Claude Desktop
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"mcpServers": {
|
|
113
|
+
"my-app": {
|
|
114
|
+
"command": "npx",
|
|
115
|
+
"args": ["tsx", "/path/to/server.ts", "--stdio"]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Dependencies
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# Server
|
|
125
|
+
npm install @json-render/mcp @json-render/core @modelcontextprotocol/sdk
|
|
126
|
+
|
|
127
|
+
# Client (iframe)
|
|
128
|
+
npm install @json-render/react @json-render/shadcn react react-dom
|
|
129
|
+
|
|
130
|
+
# Build tools
|
|
131
|
+
npm install -D vite @vitejs/plugin-react vite-plugin-singlefile
|
|
132
|
+
```
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: json-render-react
|
|
3
|
+
description: React renderer for json-render that turns JSON specs into React components. Use when working with @json-render/react, building React UIs from JSON, creating component catalogs, or rendering AI-generated specs.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @json-render/react
|
|
7
|
+
|
|
8
|
+
React renderer that converts JSON specs into React component trees.
|
|
9
|
+
|
|
10
|
+
## Quick Start
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
import { defineRegistry, Renderer } from "@json-render/react";
|
|
14
|
+
import { catalog } from "./catalog";
|
|
15
|
+
|
|
16
|
+
const { registry } = defineRegistry(catalog, {
|
|
17
|
+
components: {
|
|
18
|
+
Card: ({ props, children }) => <div>{props.title}{children}</div>,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
function App({ spec }) {
|
|
23
|
+
return <Renderer spec={spec} registry={registry} />;
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Creating a Catalog
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { defineCatalog } from "@json-render/core";
|
|
31
|
+
import { schema } from "@json-render/react/schema";
|
|
32
|
+
import { defineRegistry } from "@json-render/react";
|
|
33
|
+
import { z } from "zod";
|
|
34
|
+
|
|
35
|
+
// Create catalog with props schemas
|
|
36
|
+
export const catalog = defineCatalog(schema, {
|
|
37
|
+
components: {
|
|
38
|
+
Button: {
|
|
39
|
+
props: z.object({
|
|
40
|
+
label: z.string(),
|
|
41
|
+
variant: z.enum(["primary", "secondary"]).nullable(),
|
|
42
|
+
}),
|
|
43
|
+
description: "Clickable button",
|
|
44
|
+
},
|
|
45
|
+
Card: {
|
|
46
|
+
props: z.object({ title: z.string() }),
|
|
47
|
+
description: "Card container with title",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Define component implementations with type-safe props
|
|
53
|
+
const { registry } = defineRegistry(catalog, {
|
|
54
|
+
components: {
|
|
55
|
+
Button: ({ props }) => (
|
|
56
|
+
<button className={props.variant}>{props.label}</button>
|
|
57
|
+
),
|
|
58
|
+
Card: ({ props, children }) => (
|
|
59
|
+
<div className="card">
|
|
60
|
+
<h2>{props.title}</h2>
|
|
61
|
+
{children}
|
|
62
|
+
</div>
|
|
63
|
+
),
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Spec Structure (Element Tree)
|
|
69
|
+
|
|
70
|
+
The React schema uses an element tree format:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"root": {
|
|
75
|
+
"type": "Card",
|
|
76
|
+
"props": { "title": "Hello" },
|
|
77
|
+
"children": [
|
|
78
|
+
{ "type": "Button", "props": { "label": "Click me" } }
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Visibility Conditions
|
|
85
|
+
|
|
86
|
+
Use `visible` on elements to show/hide based on state. New syntax: `{ "$state": "/path" }`, `{ "$state": "/path", "eq": value }`, `{ "$state": "/path", "not": true }`, `{ "$and": [cond1, cond2] }` for AND, `{ "$or": [cond1, cond2] }` for OR. Helpers: `visibility.when("/path")`, `visibility.unless("/path")`, `visibility.eq("/path", val)`, `visibility.and(cond1, cond2)`, `visibility.or(cond1, cond2)`.
|
|
87
|
+
|
|
88
|
+
## Providers
|
|
89
|
+
|
|
90
|
+
| Provider | Purpose |
|
|
91
|
+
|----------|---------|
|
|
92
|
+
| `StateProvider` | Share state across components (JSON Pointer paths). Accepts optional `store` prop for controlled mode. |
|
|
93
|
+
| `ActionProvider` | Handle actions dispatched via the event system |
|
|
94
|
+
| `VisibilityProvider` | Enable conditional rendering based on state |
|
|
95
|
+
| `ValidationProvider` | Form field validation |
|
|
96
|
+
|
|
97
|
+
### External Store (Controlled Mode)
|
|
98
|
+
|
|
99
|
+
Pass a `StateStore` to `StateProvider` (or `JSONUIProvider` / `createRenderer`) to use external state management (Redux, Zustand, XState, etc.):
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
import { createStateStore, type StateStore } from "@json-render/react";
|
|
103
|
+
|
|
104
|
+
const store = createStateStore({ count: 0 });
|
|
105
|
+
|
|
106
|
+
<StateProvider store={store}>{children}</StateProvider>
|
|
107
|
+
|
|
108
|
+
// Mutate from anywhere — React re-renders automatically:
|
|
109
|
+
store.set("/count", 1);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
When `store` is provided, `initialState` and `onStateChange` are ignored.
|
|
113
|
+
|
|
114
|
+
## Dynamic Prop Expressions
|
|
115
|
+
|
|
116
|
+
Any prop value can be a data-driven expression resolved by the renderer before components receive props:
|
|
117
|
+
|
|
118
|
+
- **`{ "$state": "/state/key" }`** - reads from state model (one-way read)
|
|
119
|
+
- **`{ "$bindState": "/path" }`** - two-way binding: reads from state and enables write-back. Use on the natural value prop (value, checked, pressed, etc.) of form components.
|
|
120
|
+
- **`{ "$bindItem": "field" }`** - two-way binding to a repeat item field. Use inside repeat scopes.
|
|
121
|
+
- **`{ "$cond": <condition>, "$then": <value>, "$else": <value> }`** - conditional value
|
|
122
|
+
- **`{ "$template": "Hello, ${/name}!" }`** - interpolates state values into strings
|
|
123
|
+
- **`{ "$computed": "fn", "args": { ... } }`** - calls registered functions with resolved args
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"type": "Input",
|
|
128
|
+
"props": {
|
|
129
|
+
"value": { "$bindState": "/form/email" },
|
|
130
|
+
"placeholder": "Email"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Components do not use a `statePath` prop for two-way binding. Use `{ "$bindState": "/path" }` on the natural value prop instead.
|
|
136
|
+
|
|
137
|
+
Components receive already-resolved props. For two-way bound props, use the `useBoundProp` hook with the `bindings` map the renderer provides.
|
|
138
|
+
|
|
139
|
+
Register `$computed` functions via the `functions` prop on `JSONUIProvider` or `createRenderer`:
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
<JSONUIProvider
|
|
143
|
+
functions={{ fullName: (args) => `${args.first} ${args.last}` }}
|
|
144
|
+
>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Event System
|
|
148
|
+
|
|
149
|
+
Components use `emit` to fire named events, or `on()` to get an event handle with metadata. The element's `on` field maps events to action bindings:
|
|
150
|
+
|
|
151
|
+
```tsx
|
|
152
|
+
// Simple event firing
|
|
153
|
+
Button: ({ props, emit }) => (
|
|
154
|
+
<button onClick={() => emit("press")}>{props.label}</button>
|
|
155
|
+
),
|
|
156
|
+
|
|
157
|
+
// Event handle with metadata (e.g. preventDefault)
|
|
158
|
+
Link: ({ props, on }) => {
|
|
159
|
+
const click = on("click");
|
|
160
|
+
return (
|
|
161
|
+
<a href={props.href} onClick={(e) => {
|
|
162
|
+
if (click.shouldPreventDefault) e.preventDefault();
|
|
163
|
+
click.emit();
|
|
164
|
+
}}>{props.label}</a>
|
|
165
|
+
);
|
|
166
|
+
},
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"type": "Button",
|
|
172
|
+
"props": { "label": "Submit" },
|
|
173
|
+
"on": { "press": { "action": "submit" } }
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The `EventHandle` returned by `on()` has: `emit()`, `shouldPreventDefault` (boolean), and `bound` (boolean).
|
|
178
|
+
|
|
179
|
+
## State Watchers
|
|
180
|
+
|
|
181
|
+
Elements can declare a `watch` field (top-level, sibling of type/props/children) to trigger actions when state values change:
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"type": "Select",
|
|
186
|
+
"props": { "value": { "$bindState": "/form/country" }, "options": ["US", "Canada"] },
|
|
187
|
+
"watch": { "/form/country": { "action": "loadCities" } },
|
|
188
|
+
"children": []
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Built-in Actions
|
|
193
|
+
|
|
194
|
+
The `setState`, `pushState`, `removeState`, and `validateForm` actions are built into the React schema and handled automatically by `ActionProvider`. They are injected into AI prompts without needing to be declared in catalog `actions`:
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{ "action": "setState", "params": { "statePath": "/activeTab", "value": "home" } }
|
|
198
|
+
{ "action": "pushState", "params": { "statePath": "/items", "value": { "text": "New" } } }
|
|
199
|
+
{ "action": "removeState", "params": { "statePath": "/items", "index": 0 } }
|
|
200
|
+
{ "action": "validateForm", "params": { "statePath": "/formResult" } }
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`validateForm` validates all registered fields and writes `{ valid, errors }` to state.
|
|
204
|
+
|
|
205
|
+
Note: `statePath` in action params (e.g. `setState.statePath`) targets the mutation path. Two-way binding in component props uses `{ "$bindState": "/path" }` on the value prop, not `statePath`.
|
|
206
|
+
|
|
207
|
+
## useBoundProp
|
|
208
|
+
|
|
209
|
+
For form components that need two-way binding, use `useBoundProp` with the `bindings` map the renderer provides when a prop uses `{ "$bindState": "/path" }` or `{ "$bindItem": "field" }`:
|
|
210
|
+
|
|
211
|
+
```tsx
|
|
212
|
+
import { useBoundProp } from "@json-render/react";
|
|
213
|
+
|
|
214
|
+
Input: ({ element, bindings }) => {
|
|
215
|
+
const [value, setValue] = useBoundProp<string>(
|
|
216
|
+
element.props.value,
|
|
217
|
+
bindings?.value
|
|
218
|
+
);
|
|
219
|
+
return (
|
|
220
|
+
<input
|
|
221
|
+
value={value ?? ""}
|
|
222
|
+
onChange={(e) => setValue(e.target.value)}
|
|
223
|
+
/>
|
|
224
|
+
);
|
|
225
|
+
},
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
`useBoundProp(propValue, bindingPath)` returns `[value, setValue]`. The `value` is the resolved prop; `setValue` writes back to the bound state path (no-op if not bound).
|
|
229
|
+
|
|
230
|
+
## BaseComponentProps
|
|
231
|
+
|
|
232
|
+
For building reusable component libraries not tied to a specific catalog (e.g. `@json-render/shadcn`):
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
import type { BaseComponentProps } from "@json-render/react";
|
|
236
|
+
|
|
237
|
+
const Card = ({ props, children }: BaseComponentProps<{ title?: string }>) => (
|
|
238
|
+
<div>{props.title}{children}</div>
|
|
239
|
+
);
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## defineRegistry
|
|
243
|
+
|
|
244
|
+
`defineRegistry` conditionally requires the `actions` field only when the catalog declares actions. Catalogs with `actions: {}` can omit it.
|
|
245
|
+
|
|
246
|
+
## Key Exports
|
|
247
|
+
|
|
248
|
+
| Export | Purpose |
|
|
249
|
+
|--------|---------|
|
|
250
|
+
| `defineRegistry` | Create a type-safe component registry from a catalog |
|
|
251
|
+
| `Renderer` | Render a spec using a registry |
|
|
252
|
+
| `schema` | Element tree schema (includes built-in state actions: setState, pushState, removeState, validateForm) |
|
|
253
|
+
| `useStateStore` | Access state context |
|
|
254
|
+
| `useStateValue` | Get single value from state |
|
|
255
|
+
| `useBoundProp` | Two-way binding for `$bindState`/`$bindItem` expressions |
|
|
256
|
+
| `useActions` | Access actions context |
|
|
257
|
+
| `useAction` | Get a single action dispatch function |
|
|
258
|
+
| `useOptionalValidation` | Non-throwing variant of useValidation (returns null if no provider) |
|
|
259
|
+
| `useUIStream` | Stream specs from an API endpoint |
|
|
260
|
+
| `createStateStore` | Create a framework-agnostic in-memory `StateStore` |
|
|
261
|
+
| `StateStore` | Interface for plugging in external state management |
|
|
262
|
+
| `BaseComponentProps` | Catalog-agnostic base type for reusable component libraries |
|
|
263
|
+
| `EventHandle` | Event handle type (`emit`, `shouldPreventDefault`, `bound`) |
|
|
264
|
+
| `ComponentContext` | Typed component context (catalog-aware) |
|