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.
Files changed (226) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/LICENSE +21 -0
  3. package/Readme.md +865 -0
  4. package/dist/canvas/global.css +3173 -0
  5. package/dist/canvas/index.js +183 -0
  6. package/dist/json-render/index.css +2 -0
  7. package/dist/json-render/index.js +389 -0
  8. package/dist/types/cli/agent.d.ts +13 -0
  9. package/dist/types/cli/index.d.ts +2 -0
  10. package/dist/types/cli/watch.d.ts +5 -0
  11. package/dist/types/client/App.d.ts +1 -0
  12. package/dist/types/client/canvas/AttentionHistory.d.ts +1 -0
  13. package/dist/types/client/canvas/AttentionToast.d.ts +1 -0
  14. package/dist/types/client/canvas/CanvasNode.d.ts +8 -0
  15. package/dist/types/client/canvas/CanvasViewport.d.ts +8 -0
  16. package/dist/types/client/canvas/CommandPalette.d.ts +4 -0
  17. package/dist/types/client/canvas/ContextMenu.d.ts +24 -0
  18. package/dist/types/client/canvas/ContextPinBar.d.ts +1 -0
  19. package/dist/types/client/canvas/ContextPinHud.d.ts +1 -0
  20. package/dist/types/client/canvas/DockedNode.d.ts +4 -0
  21. package/dist/types/client/canvas/EdgeLayer.d.ts +8 -0
  22. package/dist/types/client/canvas/ExpandedNodeOverlay.d.ts +1 -0
  23. package/dist/types/client/canvas/FocusFieldLayer.d.ts +1 -0
  24. package/dist/types/client/canvas/Minimap.d.ts +23 -0
  25. package/dist/types/client/canvas/SelectionBar.d.ts +1 -0
  26. package/dist/types/client/canvas/ShortcutOverlay.d.ts +3 -0
  27. package/dist/types/client/canvas/SnapshotPanel.d.ts +7 -0
  28. package/dist/types/client/canvas/snap-guides.d.ts +23 -0
  29. package/dist/types/client/canvas/use-node-drag.d.ts +15 -0
  30. package/dist/types/client/canvas/use-node-resize.d.ts +15 -0
  31. package/dist/types/client/canvas/use-pan-zoom.d.ts +16 -0
  32. package/dist/types/client/ext-app/bridge.d.ts +161 -0
  33. package/dist/types/client/icons.d.ts +70 -0
  34. package/dist/types/client/index.d.ts +1 -0
  35. package/dist/types/client/nodes/ContextNode.d.ts +34 -0
  36. package/dist/types/client/nodes/ExtAppFrame.d.ts +18 -0
  37. package/dist/types/client/nodes/FileNode.d.ts +5 -0
  38. package/dist/types/client/nodes/GroupNode.d.ts +6 -0
  39. package/dist/types/client/nodes/ImageNode.d.ts +10 -0
  40. package/dist/types/client/nodes/InlineFormatBar.d.ts +7 -0
  41. package/dist/types/client/nodes/InlineMarkdownEditor.d.ts +14 -0
  42. package/dist/types/client/nodes/LedgerNode.d.ts +4 -0
  43. package/dist/types/client/nodes/MarkdownNode.d.ts +6 -0
  44. package/dist/types/client/nodes/McpAppNode.d.ts +4 -0
  45. package/dist/types/client/nodes/MdFormatBar.d.ts +8 -0
  46. package/dist/types/client/nodes/PromptNode.d.ts +5 -0
  47. package/dist/types/client/nodes/ResponseNode.d.ts +5 -0
  48. package/dist/types/client/nodes/StatusNode.d.ts +4 -0
  49. package/dist/types/client/nodes/StatusSummary.d.ts +4 -0
  50. package/dist/types/client/nodes/TraceNode.d.ts +4 -0
  51. package/dist/types/client/nodes/WebpageNode.d.ts +5 -0
  52. package/dist/types/client/nodes/image-warnings.d.ts +6 -0
  53. package/dist/types/client/nodes/inline-editor-commands.d.ts +11 -0
  54. package/dist/types/client/nodes/md-format.d.ts +25 -0
  55. package/dist/types/client/state/attention-bridge.d.ts +3 -0
  56. package/dist/types/client/state/attention-store.d.ts +25 -0
  57. package/dist/types/client/state/canvas-store.d.ts +74 -0
  58. package/dist/types/client/state/intent-bridge.d.ts +158 -0
  59. package/dist/types/client/state/sse-bridge.d.ts +5 -0
  60. package/dist/types/client/theme/tokens.d.ts +27 -0
  61. package/dist/types/client/types.d.ts +40 -0
  62. package/dist/types/client/utils/ext-app-tool-result.d.ts +1 -0
  63. package/dist/types/client/utils/placement.d.ts +1 -0
  64. package/dist/types/client/utils/platform.d.ts +2 -0
  65. package/dist/types/json-render/catalog.d.ts +815 -0
  66. package/dist/types/json-render/charts/components.d.ts +54 -0
  67. package/dist/types/json-render/charts/definitions.d.ts +103 -0
  68. package/dist/types/json-render/charts/extra-components.d.ts +58 -0
  69. package/dist/types/json-render/charts/extra-definitions.d.ts +181 -0
  70. package/dist/types/json-render/renderer/index.d.ts +16 -0
  71. package/dist/types/json-render/schema.d.ts +46 -0
  72. package/dist/types/json-render/server.d.ts +55 -0
  73. package/dist/types/mcp/server.d.ts +22 -0
  74. package/dist/types/server/agent-context.d.ts +21 -0
  75. package/dist/types/server/artifact-paths.d.ts +3 -0
  76. package/dist/types/server/canvas-operations.d.ts +154 -0
  77. package/dist/types/server/canvas-provenance.d.ts +13 -0
  78. package/dist/types/server/canvas-schema.d.ts +49 -0
  79. package/dist/types/server/canvas-serialization.d.ts +25 -0
  80. package/dist/types/server/canvas-state.d.ts +174 -0
  81. package/dist/types/server/canvas-validation.d.ts +33 -0
  82. package/dist/types/server/chart-template.d.ts +29 -0
  83. package/dist/types/server/code-graph.d.ts +67 -0
  84. package/dist/types/server/context-cards.d.ts +24 -0
  85. package/dist/types/server/diagram-presets.d.ts +28 -0
  86. package/dist/types/server/ext-app-call-registry.d.ts +16 -0
  87. package/dist/types/server/ext-app-tool-result.d.ts +1 -0
  88. package/dist/types/server/file-watcher.d.ts +16 -0
  89. package/dist/types/server/index.d.ts +243 -0
  90. package/dist/types/server/mcp-app-candidate.d.ts +25 -0
  91. package/dist/types/server/mcp-app-host.d.ts +65 -0
  92. package/dist/types/server/mcp-app-runtime.d.ts +47 -0
  93. package/dist/types/server/mutation-history.d.ts +105 -0
  94. package/dist/types/server/placement.d.ts +37 -0
  95. package/dist/types/server/server.d.ts +103 -0
  96. package/dist/types/server/spatial-analysis.d.ts +87 -0
  97. package/dist/types/server/trace-manager.d.ts +48 -0
  98. package/dist/types/server/web-artifacts.d.ts +50 -0
  99. package/dist/types/server/webpage-node.d.ts +25 -0
  100. package/dist/types/shared/auto-arrange.d.ts +29 -0
  101. package/dist/types/shared/ext-app-tool-result.d.ts +9 -0
  102. package/dist/types/shared/placement.d.ts +26 -0
  103. package/dist/types/shared/semantic-attention.d.ts +97 -0
  104. package/package.json +109 -0
  105. package/skills/data-analysis/SKILL.md +324 -0
  106. package/skills/doc-coauthoring/SKILL.md +375 -0
  107. package/skills/frontend-design/SKILL.md +45 -0
  108. package/skills/json-render-codegen/SKILL.md +112 -0
  109. package/skills/json-render-core/SKILL.md +265 -0
  110. package/skills/json-render-ink/SKILL.md +273 -0
  111. package/skills/json-render-mcp/SKILL.md +132 -0
  112. package/skills/json-render-react/SKILL.md +264 -0
  113. package/skills/json-render-shadcn/SKILL.md +159 -0
  114. package/skills/playwright-cli/SKILL.md +67 -0
  115. package/skills/pmx-canvas/SKILL.md +668 -0
  116. package/skills/pmx-canvas/evals/evals.json +186 -0
  117. package/skills/pmx-canvas-testing/SKILL.md +78 -0
  118. package/skills/published-consumer-e2e/SKILL.md +43 -0
  119. package/skills/published-consumer-e2e/scripts/run-published-consumer-e2e.sh +241 -0
  120. package/skills/web-artifacts-builder/SKILL.md +80 -0
  121. package/skills/web-artifacts-builder/scripts/bundle-artifact.sh +167 -0
  122. package/skills/web-artifacts-builder/scripts/init-artifact.sh +425 -0
  123. package/skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
  124. package/skills/web-design-guidelines/SKILL.md +39 -0
  125. package/src/cli/agent.ts +2144 -0
  126. package/src/cli/index.ts +622 -0
  127. package/src/cli/watch.ts +88 -0
  128. package/src/client/App.tsx +507 -0
  129. package/src/client/canvas/AttentionHistory.tsx +81 -0
  130. package/src/client/canvas/AttentionToast.tsx +19 -0
  131. package/src/client/canvas/CanvasNode.tsx +363 -0
  132. package/src/client/canvas/CanvasViewport.tsx +590 -0
  133. package/src/client/canvas/CommandPalette.tsx +302 -0
  134. package/src/client/canvas/ContextMenu.tsx +601 -0
  135. package/src/client/canvas/ContextPinBar.tsx +25 -0
  136. package/src/client/canvas/ContextPinHud.tsx +22 -0
  137. package/src/client/canvas/DockedNode.tsx +66 -0
  138. package/src/client/canvas/EdgeLayer.tsx +280 -0
  139. package/src/client/canvas/ExpandedNodeOverlay.tsx +260 -0
  140. package/src/client/canvas/FocusFieldLayer.tsx +107 -0
  141. package/src/client/canvas/Minimap.tsx +301 -0
  142. package/src/client/canvas/SelectionBar.tsx +69 -0
  143. package/src/client/canvas/ShortcutOverlay.tsx +69 -0
  144. package/src/client/canvas/SnapshotPanel.tsx +236 -0
  145. package/src/client/canvas/snap-guides.ts +170 -0
  146. package/src/client/canvas/use-node-drag.ts +51 -0
  147. package/src/client/canvas/use-node-resize.ts +59 -0
  148. package/src/client/canvas/use-pan-zoom.ts +191 -0
  149. package/src/client/ext-app/bridge.ts +542 -0
  150. package/src/client/icons.tsx +424 -0
  151. package/src/client/index.tsx +7 -0
  152. package/src/client/nodes/ContextNode.tsx +412 -0
  153. package/src/client/nodes/ExtAppFrame.tsx +509 -0
  154. package/src/client/nodes/FileNode.tsx +256 -0
  155. package/src/client/nodes/GroupNode.tsx +39 -0
  156. package/src/client/nodes/ImageNode.tsx +160 -0
  157. package/src/client/nodes/InlineFormatBar.tsx +169 -0
  158. package/src/client/nodes/InlineMarkdownEditor.tsx +123 -0
  159. package/src/client/nodes/LedgerNode.tsx +37 -0
  160. package/src/client/nodes/MarkdownNode.tsx +359 -0
  161. package/src/client/nodes/McpAppNode.tsx +85 -0
  162. package/src/client/nodes/MdFormatBar.tsx +109 -0
  163. package/src/client/nodes/PromptNode.tsx +597 -0
  164. package/src/client/nodes/ResponseNode.tsx +153 -0
  165. package/src/client/nodes/StatusNode.tsx +84 -0
  166. package/src/client/nodes/StatusSummary.tsx +38 -0
  167. package/src/client/nodes/TraceNode.tsx +120 -0
  168. package/src/client/nodes/WebpageNode.tsx +288 -0
  169. package/src/client/nodes/image-warnings.ts +95 -0
  170. package/src/client/nodes/inline-editor-commands.ts +37 -0
  171. package/src/client/nodes/md-format.ts +206 -0
  172. package/src/client/state/attention-bridge.ts +328 -0
  173. package/src/client/state/attention-store.ts +73 -0
  174. package/src/client/state/canvas-store.ts +631 -0
  175. package/src/client/state/intent-bridge.ts +315 -0
  176. package/src/client/state/sse-bridge.ts +965 -0
  177. package/src/client/theme/global.css +3173 -0
  178. package/src/client/theme/tokens.ts +72 -0
  179. package/src/client/types-shims.d.ts +5 -0
  180. package/src/client/types.ts +81 -0
  181. package/src/client/utils/ext-app-tool-result.ts +4 -0
  182. package/src/client/utils/placement.ts +4 -0
  183. package/src/client/utils/platform.ts +2 -0
  184. package/src/json-render/catalog.ts +256 -0
  185. package/src/json-render/charts/components.tsx +198 -0
  186. package/src/json-render/charts/definitions.ts +81 -0
  187. package/src/json-render/charts/extra-components.tsx +267 -0
  188. package/src/json-render/charts/extra-definitions.ts +145 -0
  189. package/src/json-render/renderer/index.css +174 -0
  190. package/src/json-render/renderer/index.tsx +86 -0
  191. package/src/json-render/schema.ts +62 -0
  192. package/src/json-render/server.ts +597 -0
  193. package/src/mcp/server.ts +1377 -0
  194. package/src/server/agent-context.ts +242 -0
  195. package/src/server/artifact-paths.ts +17 -0
  196. package/src/server/canvas-operations.ts +1279 -0
  197. package/src/server/canvas-provenance.ts +243 -0
  198. package/src/server/canvas-schema.ts +432 -0
  199. package/src/server/canvas-serialization.ts +95 -0
  200. package/src/server/canvas-state.ts +1134 -0
  201. package/src/server/canvas-validation.ts +114 -0
  202. package/src/server/chart-template.ts +449 -0
  203. package/src/server/code-graph.ts +370 -0
  204. package/src/server/context-cards.ts +31 -0
  205. package/src/server/diagram-presets.ts +71 -0
  206. package/src/server/ext-app-call-registry.ts +77 -0
  207. package/src/server/ext-app-tool-result.ts +4 -0
  208. package/src/server/file-watcher.ts +121 -0
  209. package/src/server/index.ts +647 -0
  210. package/src/server/mcp-app-candidate.ts +174 -0
  211. package/src/server/mcp-app-host.ts +814 -0
  212. package/src/server/mcp-app-runtime.ts +459 -0
  213. package/src/server/mutation-history.ts +350 -0
  214. package/src/server/placement.ts +125 -0
  215. package/src/server/server.ts +3846 -0
  216. package/src/server/spatial-analysis.ts +356 -0
  217. package/src/server/trace-manager.ts +333 -0
  218. package/src/server/web-artifacts/scripts/bundle-artifact.sh +167 -0
  219. package/src/server/web-artifacts/scripts/init-artifact.sh +426 -0
  220. package/src/server/web-artifacts/scripts/shadcn-components.tar.gz +0 -0
  221. package/src/server/web-artifacts.ts +442 -0
  222. package/src/server/webpage-node.ts +328 -0
  223. package/src/shared/auto-arrange.ts +439 -0
  224. package/src/shared/ext-app-tool-result.ts +76 -0
  225. package/src/shared/placement.ts +81 -0
  226. 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) |