pmx-canvas 0.7.2 → 0.8.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/.github/extensions/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
- package/.github/extensions/pmx-canvas/extension.mjs +25 -172
- package/.github/extensions/pmx-canvas/mcp-bootstrap.ts +198 -0
- package/.github/extensions/pmx-canvas/runtime.d.mts +41 -0
- package/.github/extensions/pmx-canvas/runtime.json +3 -0
- package/.github/extensions/pmx-canvas/runtime.mjs +194 -0
- package/.github/extensions/pmx-canvas/start-mcp.mjs +51 -0
- package/.github/extensions/pmx-canvas/tools.json +2560 -0
- package/CHANGELOG.md +60 -0
- package/Readme.md +44 -16
- package/dist/canvas/fonts/OFL.txt +93 -0
- package/dist/canvas/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-mono-latin-ext-400-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-mono-latin-ext-500-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-ext-400-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-ext-500-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-ext-600-normal.woff2 +0 -0
- package/dist/canvas/global.css +844 -710
- package/dist/canvas/index.js +92 -90
- package/dist/canvas/mcp-app.html +7878 -0
- package/dist/canvas/mermaid-entry.js +1 -1
- package/dist/canvas/surface-theme.css +151 -46
- package/dist/json-render/index.css +1 -1
- package/dist/json-render/index.js +55 -55
- package/dist/types/cli/commands/record.d.ts +1 -1
- package/dist/types/client/canvas/ChoiceMenu.d.ts +13 -0
- package/dist/types/client/canvas/NodeContextMark.d.ts +24 -0
- package/dist/types/client/canvas/SessionReceipt.d.ts +3 -1
- package/dist/types/client/icons.d.ts +1 -2
- package/dist/types/client/mcp-app/index.d.ts +1 -0
- package/dist/types/client/nodes/McpAppNode.d.ts +2 -1
- package/dist/types/client/nodes/ax-node-actions.d.ts +2 -2
- package/dist/types/client/nodes/markdown-sanitize.d.ts +2 -0
- package/dist/types/client/nodes/use-workbench-asset.d.ts +2 -0
- package/dist/types/client/nodes/viewer-runtime-state.d.ts +1 -0
- package/dist/types/client/state/attention-store.d.ts +0 -5
- package/dist/types/client/state/canvas-store.d.ts +2 -0
- package/dist/types/client/state/context-status-store.d.ts +53 -0
- package/dist/types/client/state/intent-bridge.d.ts +4 -1
- package/dist/types/client/state/near-pin-store.d.ts +16 -0
- package/dist/types/client/state/presence-store.d.ts +5 -0
- package/dist/types/client/state/session-store.d.ts +33 -3
- package/dist/types/client/state/workbench-transport.d.ts +16 -0
- package/dist/types/client/theme/tokens.d.ts +1 -3
- package/dist/types/client/types.d.ts +9 -0
- package/dist/types/json-render/renderer/index.d.ts +2 -1
- package/dist/types/json-render/server.d.ts +3 -1
- package/dist/types/mcp/app-server.d.ts +3 -0
- package/dist/types/mcp/canvas-access.d.ts +1 -1
- package/dist/types/mcp/canvas-app.d.ts +4 -0
- package/dist/types/mcp/workbench-app.d.ts +4 -0
- package/dist/types/server/agent-presence.d.ts +1 -1
- package/dist/types/server/ax-state.d.ts +1 -1
- package/dist/types/server/canvas-db.d.ts +8 -0
- package/dist/types/server/canvas-geometry.d.ts +25 -0
- package/dist/types/server/canvas-operations.d.ts +1 -11
- package/dist/types/server/canvas-state.d.ts +25 -3
- package/dist/types/server/context-brief.d.ts +8 -1
- package/dist/types/server/context-reads.d.ts +30 -7
- package/dist/types/server/index.d.ts +6 -1
- package/dist/types/server/mcp-app-runtime.d.ts +1 -0
- package/dist/types/server/operations/http.d.ts +2 -0
- package/dist/types/server/operations/invoker.d.ts +4 -2
- package/dist/types/server/operations/ops/nodes.d.ts +0 -6
- package/dist/types/server/server.d.ts +1 -0
- package/dist/types/server/spatial-analysis.d.ts +2 -0
- package/dist/types/shared/agent-context.d.ts +22 -0
- package/dist/types/shared/agent-presence.d.ts +3 -10
- package/dist/types/shared/ax-surface-protocol.d.ts +2 -0
- package/dist/types/shared/canvas-app.d.ts +54 -0
- package/dist/types/shared/themes.d.ts +1 -1
- package/docs/RELEASE.md +7 -3
- package/docs/agent-plugin.md +123 -0
- package/docs/ax-host-adapter-contract.md +13 -2
- package/docs/ax-state-contract.md +40 -0
- package/docs/cli.md +7 -2
- package/docs/design/AgentContext.dc.html +186 -0
- package/docs/design/Before.dc.html +208 -0
- package/docs/design/BoardPins.dc.html +151 -0
- package/docs/design/Chrome.dc.html +208 -0
- package/docs/design/Context.dc.html +239 -0
- package/docs/design/Flows.dc.html +181 -0
- package/docs/design/Home.dc.html +209 -0
- package/docs/design/HomeGraph.dc.html +211 -0
- package/docs/design/HomeMap.dc.html +117 -0
- package/docs/design/HomeTeam.dc.html +176 -0
- package/docs/design/Light.dc.html +28 -0
- package/docs/design/Main.dc.html +280 -0
- package/docs/design/NearPin.dc.html +260 -0
- package/docs/design/Pane600.dc.html +120 -0
- package/docs/design/Relations.dc.html +297 -0
- package/docs/design/Themes.dc.html +121 -0
- package/docs/design/TokenSystem.dc.html +136 -0
- package/docs/design/Tokens.dc.html +163 -0
- package/docs/design/Types1.dc.html +177 -0
- package/docs/design/Types2.dc.html +226 -0
- package/docs/design/Types3.dc.html +195 -0
- package/docs/design/Uses.dc.html +152 -0
- package/docs/design/Vision.dc.html +116 -0
- package/docs/design/Zoom.dc.html +82 -0
- package/docs/design/canvas.json +274 -0
- package/docs/design.md +214 -0
- package/docs/environment.md +1 -1
- package/docs/http-api.md +80 -15
- package/docs/mcp.md +198 -3
- package/docs/node-types.md +34 -10
- package/docs/plans/plan-009-tech-debt-backlog.md +1 -1
- package/docs/plans/plan-015-design-foundation-wave-0.md +177 -0
- package/docs/plans/plan-016-context-made-visible-wave-1.md +148 -0
- package/docs/product-vision-2026-09.md +47 -7
- package/docs/screenshot.png +0 -0
- package/docs/sdk.md +1 -0
- package/package.json +10 -4
- package/plugins/pmx-canvas/LICENSE +21 -0
- package/plugins/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
- package/plugins/pmx-canvas/assets/preview.png +0 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/extension.mjs +930 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +3 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.mjs +194 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/steering-delivery.mjs +79 -0
- package/plugins/pmx-canvas/mcp.json +12 -0
- package/plugins/pmx-canvas/plugin.json +24 -0
- package/plugins/pmx-canvas/runtime.json +3 -0
- package/plugins/pmx-canvas/runtime.mjs +194 -0
- package/plugins/pmx-canvas/start-mcp.mjs +51 -0
- package/plugins/pmx-canvas/tools.json +2560 -0
- package/skills/pmx-canvas/SKILL.md +31 -16
- package/skills/pmx-canvas/evals/evals.json +69 -0
- package/skills/pmx-canvas/references/full-reference.md +14 -10
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +20 -8
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +76 -4
- package/skills/pmx-canvas-testing/SKILL.md +54 -4
- package/src/cli/commands/copilot.ts +5 -0
- package/src/cli/commands/view.ts +1 -1
- package/src/cli/index.ts +8 -3
- package/src/client/App.tsx +6 -1
- package/src/client/canvas/AgentPresenceLayer.tsx +3 -3
- package/src/client/canvas/AnnotationLayer.tsx +1 -1
- package/src/client/canvas/CanvasNode.tsx +208 -220
- package/src/client/canvas/CanvasViewport.tsx +6 -2
- package/src/client/canvas/ChoiceMenu.tsx +122 -0
- package/src/client/canvas/CommandBar.tsx +25 -23
- package/src/client/canvas/CommandPalette.tsx +3 -2
- package/src/client/canvas/ContextMenu.tsx +11 -0
- package/src/client/canvas/EdgeLayer.tsx +1 -1
- package/src/client/canvas/ExportDialog.tsx +33 -6
- package/src/client/canvas/FeedbackDialog.tsx +10 -8
- package/src/client/canvas/IntentLayer.tsx +2 -2
- package/src/client/canvas/Minimap.tsx +12 -7
- package/src/client/canvas/NodeContextMark.tsx +156 -0
- package/src/client/canvas/SessionPanel.tsx +4 -7
- package/src/client/canvas/SessionReceipt.tsx +114 -17
- package/src/client/canvas/ShortcutOverlay.tsx +6 -1
- package/src/client/canvas/SnapshotPanel.tsx +11 -2
- package/src/client/canvas/ToolRail.tsx +58 -108
- package/src/client/canvas/TopBar.tsx +16 -11
- package/src/client/icons.tsx +2 -12
- package/src/client/mcp-app/index.tsx +137 -0
- package/src/client/mcp-app/style.css +10 -0
- package/src/client/nodes/AttachmentNode.tsx +8 -1
- package/src/client/nodes/AxStepControls.tsx +1 -1
- package/src/client/nodes/ContextNode.tsx +9 -9
- package/src/client/nodes/ExtAppFrame.tsx +13 -8
- package/src/client/nodes/FileNode.tsx +8 -5
- package/src/client/nodes/ImageNode.tsx +4 -2
- package/src/client/nodes/InlineMarkdownEditor.tsx +2 -1
- package/src/client/nodes/LedgerNode.tsx +2 -2
- package/src/client/nodes/MarkdownNode.tsx +44 -9
- package/src/client/nodes/McpAppNode.tsx +22 -4
- package/src/client/nodes/PromptNode.tsx +5 -17
- package/src/client/nodes/ResponseNode.tsx +3 -15
- package/src/client/nodes/StatusNode.tsx +2 -2
- package/src/client/nodes/StatusSummary.tsx +3 -1
- package/src/client/nodes/TraceNode.tsx +6 -4
- package/src/client/nodes/WebpageNode.tsx +1 -1
- package/src/client/nodes/ax-node-actions.ts +2 -2
- package/src/client/nodes/iframe-document-url.ts +3 -2
- package/src/client/nodes/markdown-sanitize.ts +10 -0
- package/src/client/nodes/surface-url.ts +10 -0
- package/src/client/nodes/use-ax-surface-bridge.ts +4 -2
- package/src/client/nodes/use-surface-frame.ts +3 -1
- package/src/client/nodes/use-workbench-asset.ts +26 -0
- package/src/client/nodes/viewer-runtime-state.ts +78 -0
- package/src/client/state/attention-bridge.ts +0 -8
- package/src/client/state/attention-store.ts +0 -17
- package/src/client/state/canvas-store.ts +7 -2
- package/src/client/state/context-status-store.ts +66 -0
- package/src/client/state/human-store.ts +2 -0
- package/src/client/state/iframe-mode.ts +5 -0
- package/src/client/state/intent-bridge.ts +24 -6
- package/src/client/state/near-pin-store.ts +53 -0
- package/src/client/state/presence-store.ts +5 -10
- package/src/client/state/session-store.ts +106 -9
- package/src/client/state/sse-bridge.ts +26 -7
- package/src/client/state/theme-override.ts +1 -1
- package/src/client/state/workbench-transport.ts +36 -0
- package/src/client/theme/global.css +844 -710
- package/src/client/theme/surface-theme.css +151 -46
- package/src/client/theme/tokens.ts +7 -11
- package/src/client/types.ts +10 -0
- package/src/json-render/renderer/index.tsx +58 -38
- package/src/json-render/server.ts +79 -15
- package/src/mcp/app-server.ts +64 -0
- package/src/mcp/canvas-access.ts +14 -11
- package/src/mcp/canvas-app.ts +318 -0
- package/src/mcp/server.ts +1 -1
- package/src/mcp/workbench-app.ts +211 -0
- package/src/server/agent-presence.ts +45 -6
- package/src/server/ax-context.ts +1 -1
- package/src/server/ax-state.ts +1 -1
- package/src/server/board-export.ts +5 -2
- package/src/server/canvas-db.ts +46 -12
- package/src/server/canvas-geometry.ts +64 -0
- package/src/server/canvas-operations.ts +6 -24
- package/src/server/canvas-state.ts +105 -15
- package/src/server/canvas-validation.ts +19 -4
- package/src/server/context-brief.ts +39 -2
- package/src/server/context-reads.ts +86 -15
- package/src/server/demo-state.json +31 -31
- package/src/server/html-surface.ts +4 -5
- package/src/server/index.ts +19 -3
- package/src/server/mcp-app-runtime.ts +11 -0
- package/src/server/operations/composites.ts +4 -3
- package/src/server/operations/http.ts +4 -4
- package/src/server/operations/invoker.ts +13 -4
- package/src/server/operations/ops/ax-read.ts +1 -1
- package/src/server/operations/ops/ax-timeline.ts +37 -0
- package/src/server/operations/ops/boards.ts +1 -1
- package/src/server/operations/ops/canvas-wire.ts +3 -6
- package/src/server/operations/ops/intent.ts +31 -2
- package/src/server/operations/ops/json-render.ts +1 -1
- package/src/server/operations/ops/nodes.ts +8 -76
- package/src/server/operations/ops/query.ts +8 -2
- package/src/server/operations/ops/snapshots.ts +4 -1
- package/src/server/operations/ops/viewport.ts +29 -5
- package/src/server/operations/registry.ts +38 -4
- package/src/server/scope-fence.ts +55 -10
- package/src/server/server.ts +86 -6
- package/src/server/spatial-analysis.ts +2 -2
- package/src/server/workboard.ts +6 -1
- package/src/{server → shared}/agent-context.ts +2 -2
- package/src/shared/agent-presence.ts +3 -15
- package/src/shared/ax-surface-protocol.ts +3 -0
- package/src/shared/canvas-app.ts +62 -0
- package/src/shared/themes.ts +5 -5
- package/src/client/canvas/FocusFieldLayer.tsx +0 -116
- package/src/client/canvas/kind-colors.ts +0 -23
|
@@ -30,9 +30,18 @@
|
|
|
30
30
|
--c-accent: #4BBCFF;
|
|
31
31
|
--c-ok: #2fd07f;
|
|
32
32
|
--c-warn: #f4c542;
|
|
33
|
-
--c-warn-alt: #FFB300;
|
|
34
33
|
--c-danger: #ff6a7f;
|
|
35
|
-
|
|
34
|
+
/* ── Meaning (one hue per meaning in every theme; see docs/design.md) ── */
|
|
35
|
+
--c-pin: #4BBCFF;
|
|
36
|
+
--c-on-pin: #081524;
|
|
37
|
+
--c-agent: #B388FF;
|
|
38
|
+
--c-subagent: #E879F9;
|
|
39
|
+
--c-rel-supports: #5FCFC0;
|
|
40
|
+
--c-rel-contradicts: #E59B6B;
|
|
41
|
+
--c-rel-cites: #7C9CFF;
|
|
42
|
+
--c-rel-derived: #D5DEEA;
|
|
43
|
+
--c-rel-informs: #E58FB0;
|
|
44
|
+
--c-rel-related: #8ea3bd;
|
|
36
45
|
|
|
37
46
|
/* Common aliases authored HTML might use. */
|
|
38
47
|
--color-bg: #081524;
|
|
@@ -49,44 +58,74 @@
|
|
|
49
58
|
--color-warning: #f4c542;
|
|
50
59
|
--color-danger: #ff6a7f;
|
|
51
60
|
|
|
52
|
-
--font: "IBM Plex Sans", "SF Pro Text", "Avenir Next", system-ui, sans-serif;
|
|
53
|
-
--
|
|
61
|
+
--font-ui: "IBM Plex Sans", "SF Pro Text", "Avenir Next", system-ui, sans-serif;
|
|
62
|
+
--font-code: "IBM Plex Mono", "SF Mono", "Fira Code", monospace;
|
|
54
63
|
--font-sans: "IBM Plex Sans", "SF Pro Text", "Avenir Next", system-ui, sans-serif;
|
|
55
64
|
--font-mono: "IBM Plex Mono", "SF Mono", "Fira Code", monospace;
|
|
65
|
+
/* ── Scale (same in every theme; see docs/design.md) ───────── */
|
|
66
|
+
--fs-meta: 11px;
|
|
67
|
+
--fs-ui: 12px;
|
|
68
|
+
--fs-body: 13px;
|
|
69
|
+
--fs-title: 15px;
|
|
70
|
+
--fs-heading: 20px;
|
|
71
|
+
--space-1: 4px;
|
|
72
|
+
--space-2: 8px;
|
|
73
|
+
--space-3: 12px;
|
|
74
|
+
--space-4: 16px;
|
|
75
|
+
--space-6: 24px;
|
|
76
|
+
--space-8: 32px;
|
|
77
|
+
--r-control: 6px;
|
|
78
|
+
--r-node: 10px;
|
|
79
|
+
--r-overlay: 14px;
|
|
80
|
+
--r-pill: 999px;
|
|
81
|
+
--e-1: 0 1px 2px rgba(0, 0, 0, 0.4), 0 6px 20px rgba(0, 0, 0, 0.24);
|
|
82
|
+
--e-2: 0 10px 30px rgba(0, 0, 0, 0.6);
|
|
56
83
|
|
|
57
84
|
color-scheme: dark light;
|
|
58
85
|
}
|
|
59
86
|
|
|
60
|
-
:root[data-theme="
|
|
61
|
-
/* ── Core palette (
|
|
62
|
-
--c-bg: #
|
|
63
|
-
--c-panel: #
|
|
64
|
-
--c-panel-soft: #
|
|
65
|
-
--c-line: #
|
|
66
|
-
--c-text: #
|
|
87
|
+
:root[data-theme="daylight"] {
|
|
88
|
+
/* ── Core palette (Daylight · neutral paper + ink) ───────── */
|
|
89
|
+
--c-bg: #F6F7F9;
|
|
90
|
+
--c-panel: #FFFFFF;
|
|
91
|
+
--c-panel-soft: #F2F4F7;
|
|
92
|
+
--c-line: #DDE2E9;
|
|
93
|
+
--c-text: #0B1726;
|
|
67
94
|
--c-text-soft: #3d4d63;
|
|
68
|
-
--c-muted: #
|
|
69
|
-
--c-dim: #
|
|
95
|
+
--c-muted: #55657a;
|
|
96
|
+
--c-dim: #6f7d90;
|
|
70
97
|
--c-accent: #1A7ABF;
|
|
71
|
-
--c-ok: #
|
|
72
|
-
--c-warn: #
|
|
73
|
-
--c-
|
|
74
|
-
|
|
75
|
-
--c-
|
|
98
|
+
--c-ok: #1E7F51;
|
|
99
|
+
--c-warn: #94600A;
|
|
100
|
+
--c-danger: #B8344A;
|
|
101
|
+
/* ── Meaning (one hue per meaning in every theme; see docs/design.md) ── */
|
|
102
|
+
--c-pin: #1A7ABF;
|
|
103
|
+
--c-on-pin: #FFFFFF;
|
|
104
|
+
--c-agent: #7C4DDB;
|
|
105
|
+
--c-subagent: #A83BB0;
|
|
106
|
+
--c-rel-supports: #1E8C80;
|
|
107
|
+
--c-rel-contradicts: #B8572A;
|
|
108
|
+
--c-rel-cites: #4257C9;
|
|
109
|
+
--c-rel-derived: #4A5566;
|
|
110
|
+
--c-rel-informs: #B03F72;
|
|
111
|
+
--c-rel-related: #5c6b80;
|
|
112
|
+
/* Light themes: ink-tinted shadows (the drawn black ones are for dark surfaces). */
|
|
113
|
+
--e-1: 0 1px 2px rgba(8, 21, 36, 0.1), 0 6px 20px rgba(8, 21, 36, 0.08);
|
|
114
|
+
--e-2: 0 10px 30px rgba(8, 21, 36, 0.18);
|
|
76
115
|
|
|
77
|
-
--color-bg: #
|
|
78
|
-
--color-panel: #
|
|
79
|
-
--color-surface: #
|
|
80
|
-
--color-border: #
|
|
81
|
-
--color-text: #
|
|
82
|
-
--color-text-primary: #
|
|
116
|
+
--color-bg: #F6F7F9;
|
|
117
|
+
--color-panel: #FFFFFF;
|
|
118
|
+
--color-surface: #F2F4F7;
|
|
119
|
+
--color-border: #DDE2E9;
|
|
120
|
+
--color-text: #0B1726;
|
|
121
|
+
--color-text-primary: #0B1726;
|
|
83
122
|
--color-text-secondary: #3d4d63;
|
|
84
|
-
--color-text-muted: #
|
|
85
|
-
--color-text-dim: #
|
|
123
|
+
--color-text-muted: #55657a;
|
|
124
|
+
--color-text-dim: #6f7d90;
|
|
86
125
|
--color-accent: #1A7ABF;
|
|
87
|
-
--color-success: #
|
|
88
|
-
--color-warning: #
|
|
89
|
-
--color-danger: #
|
|
126
|
+
--color-success: #1E7F51;
|
|
127
|
+
--color-warning: #94600A;
|
|
128
|
+
--color-danger: #B8344A;
|
|
90
129
|
}
|
|
91
130
|
|
|
92
131
|
:root[data-theme="high-contrast"] {
|
|
@@ -102,9 +141,18 @@
|
|
|
102
141
|
--c-accent: #00ffff;
|
|
103
142
|
--c-ok: #00ff00;
|
|
104
143
|
--c-warn: #ffff00;
|
|
105
|
-
--c-warn-alt: #ffcc00;
|
|
106
144
|
--c-danger: #ff0000;
|
|
107
|
-
|
|
145
|
+
/* ── Meaning (one hue per meaning in every theme; see docs/design.md) ── */
|
|
146
|
+
--c-pin: #00ffff;
|
|
147
|
+
--c-on-pin: #000000;
|
|
148
|
+
--c-agent: #e040fb;
|
|
149
|
+
--c-subagent: #ff99ff;
|
|
150
|
+
--c-rel-supports: #00e5c0;
|
|
151
|
+
--c-rel-contradicts: #ff8a00;
|
|
152
|
+
--c-rel-cites: #8c9eff;
|
|
153
|
+
--c-rel-derived: #ffffff;
|
|
154
|
+
--c-rel-informs: #ff80ab;
|
|
155
|
+
--c-rel-related: #aaaaaa;
|
|
108
156
|
|
|
109
157
|
--color-bg: #000000;
|
|
110
158
|
--color-panel: #0a0a0a;
|
|
@@ -134,9 +182,18 @@
|
|
|
134
182
|
--c-accent: #8B96FF;
|
|
135
183
|
--c-ok: #34D399;
|
|
136
184
|
--c-warn: #F5C452;
|
|
137
|
-
--c-warn-alt: #FFB300;
|
|
138
185
|
--c-danger: #FF6B8A;
|
|
139
|
-
|
|
186
|
+
/* ── Meaning (one hue per meaning in every theme; see docs/design.md) ── */
|
|
187
|
+
--c-pin: #4BBCFF;
|
|
188
|
+
--c-on-pin: #081524;
|
|
189
|
+
--c-agent: #B388FF;
|
|
190
|
+
--c-subagent: #E879F9;
|
|
191
|
+
--c-rel-supports: #5FCFC0;
|
|
192
|
+
--c-rel-contradicts: #E59B6B;
|
|
193
|
+
--c-rel-cites: #7C9CFF;
|
|
194
|
+
--c-rel-derived: #D5DEEA;
|
|
195
|
+
--c-rel-informs: #E58FB0;
|
|
196
|
+
--c-rel-related: #8D96BC;
|
|
140
197
|
|
|
141
198
|
--color-bg: #0A0D1C;
|
|
142
199
|
--color-panel: #12172E;
|
|
@@ -165,10 +222,22 @@
|
|
|
165
222
|
--c-dim: #978870;
|
|
166
223
|
--c-accent: #B4632C;
|
|
167
224
|
--c-ok: #3E7D4F;
|
|
168
|
-
--c-warn: #
|
|
169
|
-
--c-warn-alt: #8F6A0D;
|
|
225
|
+
--c-warn: #8A5A00;
|
|
170
226
|
--c-danger: #B3402E;
|
|
171
|
-
|
|
227
|
+
/* ── Meaning (one hue per meaning in every theme; see docs/design.md) ── */
|
|
228
|
+
--c-pin: #1F6FA8;
|
|
229
|
+
--c-on-pin: #FFFFFF;
|
|
230
|
+
--c-agent: #7046A6;
|
|
231
|
+
--c-subagent: #A83BB0;
|
|
232
|
+
--c-rel-supports: #1E8C80;
|
|
233
|
+
--c-rel-contradicts: #A3461C;
|
|
234
|
+
--c-rel-cites: #3D50B0;
|
|
235
|
+
--c-rel-derived: #4E4433;
|
|
236
|
+
--c-rel-informs: #9C3A66;
|
|
237
|
+
--c-rel-related: #6B6A66;
|
|
238
|
+
/* Light themes: ink-tinted shadows (the drawn black ones are for dark surfaces). */
|
|
239
|
+
--e-1: 0 1px 2px rgba(8, 21, 36, 0.1), 0 6px 20px rgba(8, 21, 36, 0.08);
|
|
240
|
+
--e-2: 0 10px 30px rgba(8, 21, 36, 0.18);
|
|
172
241
|
|
|
173
242
|
--color-bg: #F2E7D5;
|
|
174
243
|
--color-panel: #EBDCC2;
|
|
@@ -181,7 +250,7 @@
|
|
|
181
250
|
--color-text-dim: #978870;
|
|
182
251
|
--color-accent: #B4632C;
|
|
183
252
|
--color-success: #3E7D4F;
|
|
184
|
-
--color-warning: #
|
|
253
|
+
--color-warning: #8A5A00;
|
|
185
254
|
--color-danger: #B3402E;
|
|
186
255
|
}
|
|
187
256
|
|
|
@@ -198,9 +267,18 @@
|
|
|
198
267
|
--c-accent: #7FC8DE;
|
|
199
268
|
--c-ok: #74C596;
|
|
200
269
|
--c-warn: #E8C476;
|
|
201
|
-
--c-warn-alt: #E0A93F;
|
|
202
270
|
--c-danger: #D9707C;
|
|
203
|
-
|
|
271
|
+
/* ── Meaning (one hue per meaning in every theme; see docs/design.md) ── */
|
|
272
|
+
--c-pin: #6EC1F5;
|
|
273
|
+
--c-on-pin: #081524;
|
|
274
|
+
--c-agent: #B388FF;
|
|
275
|
+
--c-subagent: #E879F9;
|
|
276
|
+
--c-rel-supports: #5FCFC0;
|
|
277
|
+
--c-rel-contradicts: #E59B6B;
|
|
278
|
+
--c-rel-cites: #7C9CFF;
|
|
279
|
+
--c-rel-derived: #D5DEEA;
|
|
280
|
+
--c-rel-informs: #E58FB0;
|
|
281
|
+
--c-rel-related: #939DB1;
|
|
204
282
|
|
|
205
283
|
--color-bg: #20242E;
|
|
206
284
|
--color-panel: #2A2F3C;
|
|
@@ -230,9 +308,18 @@
|
|
|
230
308
|
--c-accent: #F34E1C;
|
|
231
309
|
--c-ok: #4CC38A;
|
|
232
310
|
--c-warn: #F5B944;
|
|
233
|
-
--c-warn-alt: #FFB300;
|
|
234
311
|
--c-danger: #F0566A;
|
|
235
|
-
|
|
312
|
+
/* ── Meaning (one hue per meaning in every theme; see docs/design.md) ── */
|
|
313
|
+
--c-pin: #4BBCFF;
|
|
314
|
+
--c-on-pin: #081524;
|
|
315
|
+
--c-agent: #B388FF;
|
|
316
|
+
--c-subagent: #E879F9;
|
|
317
|
+
--c-rel-supports: #5FCFC0;
|
|
318
|
+
--c-rel-contradicts: #E59B6B;
|
|
319
|
+
--c-rel-cites: #7C9CFF;
|
|
320
|
+
--c-rel-derived: #D5DEEA;
|
|
321
|
+
--c-rel-informs: #E58FB0;
|
|
322
|
+
--c-rel-related: #96948D;
|
|
236
323
|
|
|
237
324
|
--color-bg: #0B0B0C;
|
|
238
325
|
--color-panel: #161618;
|
|
@@ -262,9 +349,18 @@
|
|
|
262
349
|
--c-accent: #5CCF8F;
|
|
263
350
|
--c-ok: #74D99F;
|
|
264
351
|
--c-warn: #E0B84F;
|
|
265
|
-
--c-warn-alt: #D9A421;
|
|
266
352
|
--c-danger: #E57373;
|
|
267
|
-
|
|
353
|
+
/* ── Meaning (one hue per meaning in every theme; see docs/design.md) ── */
|
|
354
|
+
--c-pin: #4BBCFF;
|
|
355
|
+
--c-on-pin: #081524;
|
|
356
|
+
--c-agent: #B388FF;
|
|
357
|
+
--c-subagent: #E879F9;
|
|
358
|
+
--c-rel-supports: #4FC9D9;
|
|
359
|
+
--c-rel-contradicts: #E59B6B;
|
|
360
|
+
--c-rel-cites: #7C9CFF;
|
|
361
|
+
--c-rel-derived: #D5DEEA;
|
|
362
|
+
--c-rel-informs: #E58FB0;
|
|
363
|
+
--c-rel-related: #8BA695;
|
|
268
364
|
|
|
269
365
|
--color-bg: #0C1712;
|
|
270
366
|
--color-panel: #14231B;
|
|
@@ -294,9 +390,18 @@
|
|
|
294
390
|
--c-accent: #DFDFC1;
|
|
295
391
|
--c-ok: #52C880;
|
|
296
392
|
--c-warn: #E4C465;
|
|
297
|
-
--c-warn-alt: #FFB300;
|
|
298
393
|
--c-danger: #D67670;
|
|
299
|
-
|
|
394
|
+
/* ── Meaning (one hue per meaning in every theme; see docs/design.md) ── */
|
|
395
|
+
--c-pin: #4BBCFF;
|
|
396
|
+
--c-on-pin: #081524;
|
|
397
|
+
--c-agent: #B388FF;
|
|
398
|
+
--c-subagent: #E879F9;
|
|
399
|
+
--c-rel-supports: #5FCFC0;
|
|
400
|
+
--c-rel-contradicts: #E59B6B;
|
|
401
|
+
--c-rel-cites: #7C9CFF;
|
|
402
|
+
--c-rel-derived: #D5DEEA;
|
|
403
|
+
--c-rel-informs: #E58FB0;
|
|
404
|
+
--c-rel-related: #9CA49C;
|
|
300
405
|
|
|
301
406
|
--color-bg: #0B0D0B;
|
|
302
407
|
--color-panel: #171A17;
|
|
@@ -319,7 +424,7 @@ body {
|
|
|
319
424
|
padding: 0;
|
|
320
425
|
background: var(--c-bg);
|
|
321
426
|
color: var(--c-text);
|
|
322
|
-
font-family: var(--font, system-ui, sans-serif);
|
|
427
|
+
font-family: var(--font-ui, system-ui, sans-serif);
|
|
323
428
|
font-size: 14px;
|
|
324
429
|
line-height: 1.5;
|
|
325
430
|
}
|
|
@@ -12,10 +12,8 @@ export interface CanvasTokens {
|
|
|
12
12
|
accent: string;
|
|
13
13
|
ok: string;
|
|
14
14
|
warn: string;
|
|
15
|
-
warnAlt: string;
|
|
16
15
|
danger: string;
|
|
17
|
-
|
|
18
|
-
thinking: string;
|
|
16
|
+
agent: string;
|
|
19
17
|
subagent: string;
|
|
20
18
|
font: string;
|
|
21
19
|
mono: string;
|
|
@@ -42,13 +40,11 @@ export function getCanvasTokens(): CanvasTokens {
|
|
|
42
40
|
accent: read('--c-accent'),
|
|
43
41
|
ok: read('--c-ok'),
|
|
44
42
|
warn: read('--c-warn'),
|
|
45
|
-
warnAlt: read('--c-warn-alt'),
|
|
46
43
|
danger: read('--c-danger'),
|
|
47
|
-
|
|
48
|
-
thinking: read('--c-thinking'),
|
|
44
|
+
agent: read('--c-agent'),
|
|
49
45
|
subagent: read('--c-subagent'),
|
|
50
|
-
font: read('--font'),
|
|
51
|
-
mono: read('--
|
|
46
|
+
font: read('--font-ui'),
|
|
47
|
+
mono: read('--font-code'),
|
|
52
48
|
};
|
|
53
49
|
return cached;
|
|
54
50
|
}
|
|
@@ -62,11 +58,11 @@ export function invalidateTokenCache(): void {
|
|
|
62
58
|
export const PHASE_COLORS: Record<string, string> = {
|
|
63
59
|
idle: 'var(--c-muted)',
|
|
64
60
|
running: 'var(--c-accent)',
|
|
65
|
-
planning: 'var(--c-
|
|
66
|
-
thinking: 'var(--c-
|
|
61
|
+
planning: 'var(--c-agent)',
|
|
62
|
+
thinking: 'var(--c-agent)',
|
|
67
63
|
drafting: 'var(--c-accent)',
|
|
68
64
|
tooling: 'var(--c-accent)',
|
|
69
65
|
review: 'var(--c-ok)',
|
|
70
66
|
'waiting-approval': 'var(--c-warn)',
|
|
71
|
-
waiting: 'var(--c-warn
|
|
67
|
+
waiting: 'var(--c-warn)',
|
|
72
68
|
};
|
package/src/client/types.ts
CHANGED
|
@@ -32,9 +32,19 @@ export interface CanvasNodeState {
|
|
|
32
32
|
zIndex: number;
|
|
33
33
|
collapsed: boolean;
|
|
34
34
|
pinned: boolean;
|
|
35
|
+
contentRevision?: number;
|
|
36
|
+
/** Who created / last edited the node's content (server attribution). */
|
|
37
|
+
createdBy?: NodeActor;
|
|
38
|
+
lastEditedBy?: NodeActor;
|
|
35
39
|
data: Record<string, unknown>;
|
|
36
40
|
}
|
|
37
41
|
|
|
42
|
+
export interface NodeActor {
|
|
43
|
+
actor: 'human' | 'agent' | 'system' | 'unknown';
|
|
44
|
+
source: string;
|
|
45
|
+
agentId?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
38
48
|
export interface CanvasEdge {
|
|
39
49
|
id: string;
|
|
40
50
|
from: string;
|
|
@@ -7,11 +7,14 @@
|
|
|
7
7
|
* from an inline global injected by the server-side viewer route.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type
|
|
11
|
-
import {
|
|
12
|
-
|
|
10
|
+
import { createStateStore, type Spec, type StateStore } from '@json-render/core';
|
|
11
|
+
import {
|
|
12
|
+
AX_SURFACE_EMIT_SOURCE,
|
|
13
|
+
HTML_SURFACE_PUSH_SOURCE,
|
|
14
|
+
JSON_VIEWER_STATE_SOURCE,
|
|
15
|
+
} from '../../shared/ax-surface-protocol.js';
|
|
13
16
|
import { createRoot } from 'react-dom/client';
|
|
14
|
-
import { defineRegistry, JSONUIProvider, Renderer
|
|
17
|
+
import { defineRegistry, JSONUIProvider, Renderer } from '@json-render/react';
|
|
15
18
|
import { shadcnComponents } from '@json-render/shadcn';
|
|
16
19
|
import { catalog } from '../catalog';
|
|
17
20
|
import { chartComponents } from '../charts/components';
|
|
@@ -106,29 +109,10 @@ declare global {
|
|
|
106
109
|
__PMX_CANVAS_JSON_RENDER_NODE_ID__?: string;
|
|
107
110
|
__PMX_CANVAS_AX_TOKEN__?: string;
|
|
108
111
|
__PMX_CANVAS_AX_STATE__?: unknown;
|
|
112
|
+
__PMX_CANVAS_UI_STATE_TOKEN__?: string;
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
115
|
|
|
112
|
-
// Read-side AX bridge for json-render: keeps the spec-bound `/ax` state live as
|
|
113
|
-
// the parent canvas pushes nonce-validated `ax-update` messages, so a declarative
|
|
114
|
-
// board ({ "$state": "/ax/workItems" }) reflects the work queue in real time.
|
|
115
|
-
function AxStateSync() {
|
|
116
|
-
const [, setAx] = useStateBinding<unknown>('ax');
|
|
117
|
-
useEffect(() => {
|
|
118
|
-
const token = window.__PMX_CANVAS_AX_TOKEN__;
|
|
119
|
-
if (!token) return undefined;
|
|
120
|
-
function onMessage(event: MessageEvent) {
|
|
121
|
-
const m = event.data as { source?: string; type?: string; token?: string; state?: unknown } | null;
|
|
122
|
-
if (!m || m.source !== HTML_SURFACE_PUSH_SOURCE || m.type !== 'ax-update' || m.token !== token) return;
|
|
123
|
-
window.__PMX_CANVAS_AX_STATE__ = m.state;
|
|
124
|
-
setAx(m.state);
|
|
125
|
-
}
|
|
126
|
-
window.addEventListener('message', onMessage);
|
|
127
|
-
return () => window.removeEventListener('message', onMessage);
|
|
128
|
-
}, [setAx]);
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
116
|
// AX interaction types a json-render spec can bind actions to. When an action
|
|
133
117
|
// named like one of these fires, we forward it to the parent canvas (which
|
|
134
118
|
// validates + submits through the capability-gated endpoint). Convention-based
|
|
@@ -188,7 +172,7 @@ function applyTheme(theme: unknown): void {
|
|
|
188
172
|
document.documentElement.style.colorScheme = theme === 'light' ? 'light' : 'dark';
|
|
189
173
|
}
|
|
190
174
|
|
|
191
|
-
function App() {
|
|
175
|
+
function App({ store }: { store: StateStore }) {
|
|
192
176
|
const spec = window.__PMX_CANVAS_JSON_RENDER_SPEC__;
|
|
193
177
|
|
|
194
178
|
if (!spec) {
|
|
@@ -208,11 +192,6 @@ function App() {
|
|
|
208
192
|
);
|
|
209
193
|
}
|
|
210
194
|
|
|
211
|
-
// Seed AX state under a reserved `/ax` key so specs can bind { "$state": "/ax/workItems" }.
|
|
212
|
-
const axState = window.__PMX_CANVAS_AX_STATE__;
|
|
213
|
-
const initialState =
|
|
214
|
-
axState !== undefined && axState !== null ? { ...(spec.state ?? {}), ax: axState } : (spec.state ?? undefined);
|
|
215
|
-
|
|
216
195
|
// Standalone "Open as site" tab (#65): fill the browser viewport instead of the
|
|
217
196
|
// in-canvas card height. The chart child flex-grows; useChartFrameHeight measures
|
|
218
197
|
// the full viewport in this mode. Embedded/expanded keep the padded min-height box.
|
|
@@ -229,13 +208,7 @@ function App() {
|
|
|
229
208
|
: { minHeight: '100vh', padding: 16, boxSizing: 'border-box' as const };
|
|
230
209
|
return (
|
|
231
210
|
<div style={containerStyle}>
|
|
232
|
-
<JSONUIProvider
|
|
233
|
-
registry={registry}
|
|
234
|
-
initialState={initialState}
|
|
235
|
-
directives={pmxCanvasDirectives}
|
|
236
|
-
handlers={buildAxHandlers()}
|
|
237
|
-
>
|
|
238
|
-
<AxStateSync />
|
|
211
|
+
<JSONUIProvider registry={registry} store={store} directives={pmxCanvasDirectives} handlers={buildAxHandlers()}>
|
|
239
212
|
<div style={isSite ? { flex: 1, minHeight: 0 } : undefined}>
|
|
240
213
|
<Renderer spec={spec} registry={registry} loading={false} />
|
|
241
214
|
</div>
|
|
@@ -248,5 +221,52 @@ function App() {
|
|
|
248
221
|
const root = document.getElementById('root');
|
|
249
222
|
if (root) {
|
|
250
223
|
syncPreferredTheme();
|
|
251
|
-
|
|
224
|
+
const stateToken = window.__PMX_CANVAS_UI_STATE_TOKEN__;
|
|
225
|
+
let store: StateStore | undefined;
|
|
226
|
+
const mount = (restored: Record<string, unknown> | null) => {
|
|
227
|
+
if (store) return;
|
|
228
|
+
const seed = restored ?? window.__PMX_CANVAS_JSON_RENDER_SPEC__?.state ?? {};
|
|
229
|
+
const ax = window.__PMX_CANVAS_AX_STATE__;
|
|
230
|
+
store = createStateStore(ax != null ? { ...seed, ax } : seed);
|
|
231
|
+
if (stateToken) {
|
|
232
|
+
// subscribe fires synchronously at the input/action write, not in a React
|
|
233
|
+
// effect that can be lost on unmount. Never mirror the server-owned /ax.
|
|
234
|
+
store.subscribe(() => {
|
|
235
|
+
const { ax: _ax, ...state } = store!.getSnapshot();
|
|
236
|
+
window.parent.postMessage(
|
|
237
|
+
{
|
|
238
|
+
source: JSON_VIEWER_STATE_SOURCE,
|
|
239
|
+
type: 'snapshot',
|
|
240
|
+
token: stateToken,
|
|
241
|
+
nodeId: window.__PMX_CANVAS_JSON_RENDER_NODE_ID__,
|
|
242
|
+
state,
|
|
243
|
+
},
|
|
244
|
+
'*',
|
|
245
|
+
);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
createRoot(root).render(<App store={store} />);
|
|
249
|
+
};
|
|
250
|
+
// Install before document load. The parent supplies a snapshot (or null) in
|
|
251
|
+
// onLoad; restore BEFORE mounting so watchers don't replay past user actions.
|
|
252
|
+
// Standalone viewers have no state token and render immediately.
|
|
253
|
+
window.addEventListener('message', (event: MessageEvent) => {
|
|
254
|
+
if (event.source !== window.parent) return;
|
|
255
|
+
const m = event.data as { source?: string; type?: string; token?: string; state?: unknown } | null;
|
|
256
|
+
if (!m) return;
|
|
257
|
+
if (m.source === JSON_VIEWER_STATE_SOURCE && m.type === 'restore' && stateToken && m.token === stateToken) {
|
|
258
|
+
if (m.state === null || (typeof m.state === 'object' && !Array.isArray(m.state)))
|
|
259
|
+
mount(m.state as Record<string, unknown> | null);
|
|
260
|
+
}
|
|
261
|
+
if (
|
|
262
|
+
m.source === HTML_SURFACE_PUSH_SOURCE &&
|
|
263
|
+
m.type === 'ax-update' &&
|
|
264
|
+
window.__PMX_CANVAS_AX_TOKEN__ &&
|
|
265
|
+
m.token === window.__PMX_CANVAS_AX_TOKEN__
|
|
266
|
+
) {
|
|
267
|
+
window.__PMX_CANVAS_AX_STATE__ = m.state;
|
|
268
|
+
store?.set('/ax', m.state);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
if (!stateToken) mount(null);
|
|
252
272
|
}
|
|
@@ -472,6 +472,73 @@ function normalizeSpec(spec: Record<string, unknown>): Record<string, unknown> {
|
|
|
472
472
|
return changed ? { ...spec, elements: normalizedElements } : spec;
|
|
473
473
|
}
|
|
474
474
|
|
|
475
|
+
/** shadcn 0.21 ignores these props unless the renderer supplies a two-way binding. */
|
|
476
|
+
const BINDING_ONLY_PROPS: Record<string, string> = {
|
|
477
|
+
Input: 'value',
|
|
478
|
+
Textarea: 'value',
|
|
479
|
+
Select: 'value',
|
|
480
|
+
Radio: 'value',
|
|
481
|
+
Slider: 'value',
|
|
482
|
+
ToggleGroup: 'value',
|
|
483
|
+
ButtonGroup: 'selected',
|
|
484
|
+
Tabs: 'value',
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
/** Element keys rendered once per item of a `repeat`; a single state path would be shared by every item. */
|
|
488
|
+
function collectRepeatedElementKeys(elements: Record<string, unknown>): Set<string> {
|
|
489
|
+
const repeated = new Set<string>();
|
|
490
|
+
const visit = (key: string): void => {
|
|
491
|
+
const element = asRecord(elements[key]);
|
|
492
|
+
if (!element) return;
|
|
493
|
+
const slots = Object.values(asRecord(element.slots) ?? {}).flat();
|
|
494
|
+
for (const child of [...(Array.isArray(element.children) ? element.children : []), ...slots]) {
|
|
495
|
+
if (typeof child === 'string' && !repeated.has(child)) {
|
|
496
|
+
repeated.add(child);
|
|
497
|
+
visit(child);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
for (const [key, element] of Object.entries(elements)) {
|
|
502
|
+
if (asRecord(element)?.repeat !== undefined) visit(key);
|
|
503
|
+
}
|
|
504
|
+
return repeated;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Rewrite a literal value on a binding-only form prop (e.g. Input `value: "nw1 6xe"`) into
|
|
509
|
+
* `{ $bindState: "/form/<element>" }` and seed that path in `state`, so the control shows the
|
|
510
|
+
* value instead of rendering empty. Controls inside a repeat, or a spec whose `state.form` is not
|
|
511
|
+
* an object, are left alone and still reported by `jsonRenderFormWarnings`.
|
|
512
|
+
*/
|
|
513
|
+
function bindLiteralFormValues(spec: Record<string, unknown>): Record<string, unknown> {
|
|
514
|
+
const elements = asRecord(spec.elements);
|
|
515
|
+
if (!elements) return spec;
|
|
516
|
+
if (spec.state !== undefined && !asRecord(spec.state)) return spec;
|
|
517
|
+
const state = asRecord(spec.state) ?? {};
|
|
518
|
+
if (state.form !== undefined && !asRecord(state.form)) return spec;
|
|
519
|
+
const form: Record<string, unknown> = { ...(asRecord(state.form) ?? {}) };
|
|
520
|
+
const repeated = collectRepeatedElementKeys(elements);
|
|
521
|
+
const nextElements: Record<string, unknown> = { ...elements };
|
|
522
|
+
let changed = false;
|
|
523
|
+
|
|
524
|
+
for (const [key, rawElement] of Object.entries(elements)) {
|
|
525
|
+
const element = asRecord(rawElement);
|
|
526
|
+
const prop = typeof element?.type === 'string' ? BINDING_ONLY_PROPS[element.type] : undefined;
|
|
527
|
+
const props = asRecord(element?.props);
|
|
528
|
+
if (!element || !prop || !props || repeated.has(key)) continue;
|
|
529
|
+
const value = props[prop];
|
|
530
|
+
if (value === undefined || asRecord(value)) continue;
|
|
531
|
+
const base = deriveElementName(key);
|
|
532
|
+
let field = base;
|
|
533
|
+
for (let n = 2; field in form; n++) field = `${base}-${n}`;
|
|
534
|
+
form[field] = value;
|
|
535
|
+
nextElements[key] = { ...element, props: { ...props, [prop]: { $bindState: `/form/${field}` } } };
|
|
536
|
+
changed = true;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return changed ? { ...spec, state: { ...state, form }, elements: nextElements } : spec;
|
|
540
|
+
}
|
|
541
|
+
|
|
475
542
|
function isBareJsonRenderElement(spec: Record<string, unknown>): boolean {
|
|
476
543
|
return typeof spec.type === 'string' && !('root' in spec) && !('elements' in spec);
|
|
477
544
|
}
|
|
@@ -528,7 +595,7 @@ export function normalizeAndValidateJsonRenderSpec(spec: unknown): JsonRenderSpe
|
|
|
528
595
|
}
|
|
529
596
|
}
|
|
530
597
|
|
|
531
|
-
const normalizedSpec = normalizeSpec(specRecord);
|
|
598
|
+
const normalizedSpec = bindLiteralFormValues(normalizeSpec(specRecord));
|
|
532
599
|
const validation = catalog.validate(normalizedSpec);
|
|
533
600
|
if (!validation.success || !validation.data) {
|
|
534
601
|
throw new Error(formatValidationError(validation.error));
|
|
@@ -542,30 +609,20 @@ export function normalizeAndValidateJsonRenderSpec(spec: unknown): JsonRenderSpe
|
|
|
542
609
|
return propsValidation.data as JsonRenderSpec;
|
|
543
610
|
}
|
|
544
611
|
|
|
545
|
-
/**
|
|
612
|
+
/** Binding-only form props that normalization could not bind (see bindLiteralFormValues). */
|
|
546
613
|
export function jsonRenderFormWarnings(spec: JsonRenderSpec): string[] {
|
|
547
|
-
const bindingOnlyProps: Record<string, string> = {
|
|
548
|
-
Input: 'value',
|
|
549
|
-
Textarea: 'value',
|
|
550
|
-
Select: 'value',
|
|
551
|
-
Radio: 'value',
|
|
552
|
-
Slider: 'value',
|
|
553
|
-
ToggleGroup: 'value',
|
|
554
|
-
ButtonGroup: 'selected',
|
|
555
|
-
Tabs: 'value',
|
|
556
|
-
};
|
|
557
614
|
const warnings: string[] = [];
|
|
558
615
|
for (const [key, rawElement] of Object.entries(spec.elements)) {
|
|
559
616
|
const element = asRecord(rawElement);
|
|
560
617
|
const type = typeof element?.type === 'string' ? element.type : '';
|
|
561
|
-
const prop =
|
|
618
|
+
const prop = BINDING_ONLY_PROPS[type];
|
|
562
619
|
if (!prop) continue;
|
|
563
620
|
const value = asRecord(element?.props)?.[prop];
|
|
564
621
|
if (value === undefined || value === null) continue;
|
|
565
622
|
const expression = asRecord(value);
|
|
566
623
|
if (hasString(expression?.$bindState) || hasString(expression?.$bindItem)) continue;
|
|
567
624
|
warnings.push(
|
|
568
|
-
`elements.${key}.props.${prop}: ${type} ignores an unbound ${prop}
|
|
625
|
+
`elements.${key}.props.${prop}: ${type} ignores an unbound ${prop}. PMX binds literal values automatically, but not $state or other expressions, controls inside a repeat, or specs whose state.form is not an object. Use { "$bindState": "/form/field" } and seed spec.state.form.field with the initial value; inside a repeat use $bindItem with seeded item data.${type === 'Tabs' ? ' For an uncontrolled initial tab, use defaultValue instead.' : ''}`,
|
|
569
626
|
);
|
|
570
627
|
}
|
|
571
628
|
return warnings;
|
|
@@ -979,6 +1036,8 @@ export async function buildJsonRenderViewerHtml(options: {
|
|
|
979
1036
|
nodeId?: string;
|
|
980
1037
|
axToken?: string;
|
|
981
1038
|
axState?: unknown;
|
|
1039
|
+
/** Independent nonce for tab-local runtime state across focus transitions. */
|
|
1040
|
+
stateToken?: string;
|
|
982
1041
|
/** Nonce for the content-height reporter so the node can grow to fit the chart. */
|
|
983
1042
|
frameToken?: string;
|
|
984
1043
|
/** When true, charts render at their natural (intrinsic) height instead of
|
|
@@ -995,9 +1054,14 @@ export async function buildJsonRenderViewerHtml(options: {
|
|
|
995
1054
|
...(options.theme ? [`window.__PMX_CANVAS_JSON_RENDER_THEME__ = ${JSON.stringify(options.theme)};`] : []),
|
|
996
1055
|
...(options.display ? [`window.__PMX_CANVAS_JSON_RENDER_DISPLAY__ = ${JSON.stringify(options.display)};`] : []),
|
|
997
1056
|
...(options.devtools ? ['window.__PMX_CANVAS_JSON_RENDER_DEVTOOLS__ = true;'] : []),
|
|
1057
|
+
...(options.nodeId
|
|
1058
|
+
? [`window.__PMX_CANVAS_JSON_RENDER_NODE_ID__ = ${JSON.stringify(sanitizeAxValue(options.nodeId))};`]
|
|
1059
|
+
: []),
|
|
1060
|
+
...(options.stateToken
|
|
1061
|
+
? [`window.__PMX_CANVAS_UI_STATE_TOKEN__ = ${JSON.stringify(sanitizeAxValue(options.stateToken))};`]
|
|
1062
|
+
: []),
|
|
998
1063
|
...(options.nodeId && options.axToken
|
|
999
1064
|
? [
|
|
1000
|
-
`window.__PMX_CANVAS_JSON_RENDER_NODE_ID__ = ${JSON.stringify(sanitizeAxValue(options.nodeId))};`,
|
|
1001
1065
|
`window.__PMX_CANVAS_AX_TOKEN__ = ${JSON.stringify(sanitizeAxValue(options.axToken))};`,
|
|
1002
1066
|
// Read-side AX state: seed for initial render + bound under /ax for specs.
|
|
1003
1067
|
`window.__PMX_CANVAS_AX_STATE__ = ${JSON.stringify(options.axState ?? null).replace(/</g, '\\u003c')};`,
|