pmx-canvas 0.6.4 → 0.7.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/extension.mjs +67 -3
- package/CHANGELOG.md +73 -0
- package/Readme.md +35 -5
- package/dist/canvas/global.css +498 -28
- package/dist/canvas/index.js +110 -95
- package/dist/canvas/mermaid-entry.js +258 -258
- package/dist/types/cli/agent.d.ts +4 -0
- package/dist/types/cli/commands/backup.d.ts +1 -0
- package/dist/types/cli/commands/boards.d.ts +1 -0
- package/dist/types/cli/commands/export.d.ts +1 -0
- package/dist/types/cli/commands/record.d.ts +12 -0
- package/dist/types/client/canvas/BoardCopyDialog.d.ts +5 -0
- package/dist/types/client/canvas/BoardFolderDialog.d.ts +7 -0
- package/dist/types/client/canvas/BoardLinkDialog.d.ts +3 -0
- package/dist/types/client/canvas/BoardSwitcher.d.ts +8 -0
- package/dist/types/client/canvas/ExportDialog.d.ts +7 -0
- package/dist/types/client/canvas/HomeView.d.ts +9 -0
- package/dist/types/client/canvas/Presentation.d.ts +13 -0
- package/dist/types/client/canvas/use-node-drag.d.ts +2 -1
- package/dist/types/client/nodes/AttachmentNode.d.ts +6 -0
- package/dist/types/client/nodes/BoardNode.d.ts +5 -0
- package/dist/types/client/nodes/McpAppNode.d.ts +24 -1
- package/dist/types/client/state/boards-store.d.ts +79 -0
- package/dist/types/client/state/canvas-store.d.ts +2 -0
- package/dist/types/client/state/intent-bridge.d.ts +1 -0
- package/dist/types/client/state/presentation.d.ts +3 -0
- package/dist/types/client/state/session-store.d.ts +7 -0
- package/dist/types/client/types.d.ts +2 -2
- package/dist/types/json-render/catalog.d.ts +3 -3
- package/dist/types/json-render/charts/definitions.d.ts +1 -1
- package/dist/types/json-render/server.d.ts +8 -0
- package/dist/types/mcp/canvas-access.d.ts +10 -3
- package/dist/types/mcp/context-uri-template.d.ts +6 -0
- package/dist/types/server/agent-presence.d.ts +1 -1
- package/dist/types/server/attribution.d.ts +11 -0
- package/dist/types/server/ax-context.d.ts +3 -1
- package/dist/types/server/ax-interaction.d.ts +2 -2
- package/dist/types/server/ax-state.d.ts +13 -3
- package/dist/types/server/backup.d.ts +36 -0
- package/dist/types/server/board-export.d.ts +45 -0
- package/dist/types/server/canvas-db.d.ts +65 -9
- package/dist/types/server/canvas-operations.d.ts +45 -0
- package/dist/types/server/canvas-provenance.d.ts +1 -1
- package/dist/types/server/canvas-schema.d.ts +1 -0
- package/dist/types/server/canvas-state.d.ts +146 -3
- package/dist/types/server/canvas-validation.d.ts +3 -0
- package/dist/types/server/context-brief.d.ts +111 -0
- package/dist/types/server/context-reads.d.ts +55 -0
- package/dist/types/server/document-import.d.ts +66 -0
- package/dist/types/server/html-surface.d.ts +1 -1
- package/dist/types/server/index.d.ts +107 -4
- package/dist/types/server/mutation-history.d.ts +1 -0
- package/dist/types/server/operations/invoker.d.ts +2 -0
- package/dist/types/server/operations/mcp.d.ts +7 -1
- package/dist/types/server/operations/ops/app.d.ts +2 -0
- package/dist/types/server/operations/ops/ax-shared.d.ts +2 -2
- package/dist/types/server/operations/ops/backup.d.ts +2 -0
- package/dist/types/server/operations/ops/boards.d.ts +10 -0
- package/dist/types/server/operations/ops/export.d.ts +2 -0
- package/dist/types/server/operations/ops/imports.d.ts +2 -0
- package/dist/types/server/operations/ops/json-render.d.ts +2 -0
- package/dist/types/server/operations/ops/nodes.d.ts +4 -2
- package/dist/types/server/operations/ops/query.d.ts +2 -0
- package/dist/types/server/operations/registry.d.ts +9 -1
- package/dist/types/server/operations/types.d.ts +1 -1
- package/dist/types/server/server.d.ts +1 -0
- package/dist/types/server/trace-manager.d.ts +7 -0
- package/dist/types/server/web-artifacts.d.ts +3 -0
- package/dist/types/server/webpage-node.d.ts +22 -1
- package/dist/types/server/workbench-auth.d.ts +3 -0
- package/dist/types/shared/boards.d.ts +6 -0
- package/dist/types/shared/tour.d.ts +72 -0
- package/docs/RELEASE.md +5 -0
- package/docs/cli.md +157 -0
- package/docs/http-api.md +277 -6
- package/docs/mcp.md +96 -7
- package/docs/node-types.md +106 -0
- package/docs/plans/plan-010-attributed-ask-answers.md +39 -0
- package/docs/plans/plan-011-context-read-instrumentation.md +54 -0
- package/docs/plans/plan-012-many-boards-stage-1.md +98 -0
- package/docs/plans/plan-013-static-board-export.md +47 -0
- package/docs/plans/plan-014-connected-board-memory.md +140 -0
- package/docs/product-vision-2026-09.md +118 -18
- package/docs/sdk.md +50 -0
- package/package.json +1 -1
- package/skills/pmx-canvas/SKILL.md +62 -5
- package/skills/pmx-canvas/references/full-reference.md +44 -8
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +3 -0
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +4 -4
- package/skills/pmx-canvas-orchestration/SKILL.md +5 -0
- package/skills/pmx-canvas-testing/SKILL.md +4 -0
- package/src/cli/agent.ts +9 -0
- package/src/cli/commands/ax.ts +13 -0
- package/src/cli/commands/backup.ts +66 -0
- package/src/cli/commands/boards.ts +71 -0
- package/src/cli/commands/export.ts +30 -0
- package/src/cli/commands/nodes.ts +14 -1
- package/src/cli/commands/pins.ts +6 -2
- package/src/cli/commands/pump.ts +3 -0
- package/src/cli/commands/query.ts +33 -7
- package/src/cli/commands/record.ts +218 -0
- package/src/cli/commands/view.ts +7 -5
- package/src/cli/index.ts +21 -9
- package/src/client/App.tsx +28 -4
- package/src/client/canvas/AgentPresenceLayer.tsx +1 -1
- package/src/client/canvas/BoardCopyDialog.tsx +153 -0
- package/src/client/canvas/BoardFolderDialog.tsx +120 -0
- package/src/client/canvas/BoardLinkDialog.tsx +93 -0
- package/src/client/canvas/BoardSwitcher.tsx +149 -0
- package/src/client/canvas/CanvasNode.tsx +8 -0
- package/src/client/canvas/CanvasViewport.tsx +3 -0
- package/src/client/canvas/CommandPalette.tsx +107 -4
- package/src/client/canvas/ContextMenu.tsx +4 -0
- package/src/client/canvas/ExpandedNodeOverlay.tsx +3 -0
- package/src/client/canvas/ExportDialog.tsx +197 -0
- package/src/client/canvas/HomeView.tsx +277 -0
- package/src/client/canvas/IntentLayer.tsx +9 -2
- package/src/client/canvas/Presentation.tsx +108 -0
- package/src/client/canvas/SessionPanel.tsx +28 -1
- package/src/client/canvas/ToolRail.tsx +38 -3
- package/src/client/canvas/TopBar.tsx +49 -7
- package/src/client/canvas/import-files.ts +19 -1
- package/src/client/canvas/kind-colors.ts +1 -0
- package/src/client/canvas/use-node-drag.ts +8 -3
- package/src/client/icons.tsx +2 -0
- package/src/client/mermaid-entry.ts +39 -0
- package/src/client/nodes/AttachmentNode.tsx +148 -0
- package/src/client/nodes/BoardNode.tsx +35 -0
- package/src/client/nodes/FileNode.tsx +6 -0
- package/src/client/nodes/HtmlNode.tsx +12 -13
- package/src/client/nodes/MarkdownNode.tsx +47 -19
- package/src/client/nodes/McpAppNode.tsx +105 -13
- package/src/client/nodes/MermaidNode.tsx +3 -2
- package/src/client/nodes/ax-node-actions.ts +2 -5
- package/src/client/nodes/use-ax-surface-bridge.ts +1 -1
- package/src/client/state/boards-store.ts +167 -0
- package/src/client/state/canvas-store.ts +22 -0
- package/src/client/state/intent-bridge.ts +30 -2
- package/src/client/state/intent-store.ts +5 -1
- package/src/client/state/node-factory.ts +1 -0
- package/src/client/state/presentation.ts +7 -0
- package/src/client/state/session-store.ts +8 -0
- package/src/client/state/sse-bridge.ts +52 -5
- package/src/client/theme/global.css +498 -28
- package/src/client/types.ts +2 -0
- package/src/json-render/server.ts +45 -9
- package/src/mcp/canvas-access.ts +59 -22
- package/src/mcp/context-uri-template.ts +28 -0
- package/src/mcp/server.ts +122 -15
- package/src/server/agent-context.ts +116 -29
- package/src/server/agent-presence.ts +15 -9
- package/src/server/attribution.ts +29 -0
- package/src/server/ax-context.ts +4 -2
- package/src/server/ax-interaction.ts +1 -0
- package/src/server/ax-state-manager.ts +72 -3
- package/src/server/ax-state.ts +41 -0
- package/src/server/backup.ts +178 -0
- package/src/server/board-export.ts +686 -0
- package/src/server/canvas-db.ts +612 -105
- package/src/server/canvas-operations.ts +183 -31
- package/src/server/canvas-provenance.ts +1 -0
- package/src/server/canvas-schema.ts +22 -0
- package/src/server/canvas-state.ts +812 -48
- package/src/server/canvas-validation.ts +9 -1
- package/src/server/context-brief.ts +372 -0
- package/src/server/context-reads.ts +238 -0
- package/src/server/demo-state.json +670 -9
- package/src/server/document-import.ts +223 -0
- package/src/server/html-primitives.ts +199 -91
- package/src/server/html-surface.ts +8 -3
- package/src/server/index.ts +243 -5
- package/src/server/mutation-history.ts +10 -13
- package/src/server/operations/composites.ts +29 -5
- package/src/server/operations/http.ts +9 -3
- package/src/server/operations/index.ts +8 -0
- package/src/server/operations/invoker.ts +14 -3
- package/src/server/operations/mcp.ts +50 -11
- package/src/server/operations/ops/app.ts +15 -0
- package/src/server/operations/ops/ax-read.ts +10 -6
- package/src/server/operations/ops/ax-timeline.ts +75 -0
- package/src/server/operations/ops/backup.ts +134 -0
- package/src/server/operations/ops/batch.ts +20 -4
- package/src/server/operations/ops/boards.ts +351 -0
- package/src/server/operations/ops/export.ts +74 -0
- package/src/server/operations/ops/ext-app.ts +4 -0
- package/src/server/operations/ops/groups.ts +14 -7
- package/src/server/operations/ops/imports.ts +220 -0
- package/src/server/operations/ops/json-render.ts +37 -5
- package/src/server/operations/ops/nodes.ts +99 -21
- package/src/server/operations/ops/query.ts +206 -9
- package/src/server/operations/ops/viewport.ts +32 -0
- package/src/server/operations/registry.ts +129 -31
- package/src/server/operations/types.ts +1 -1
- package/src/server/scope-fence.ts +1 -1
- package/src/server/server.ts +155 -8
- package/src/server/spatial-analysis.ts +9 -17
- package/src/server/trace-manager.ts +19 -0
- package/src/server/web-artifacts.ts +11 -3
- package/src/server/webpage-node.ts +173 -13
- package/src/server/workbench-auth.ts +11 -0
- package/src/server/workboard.ts +3 -3
- package/src/shared/boards.ts +18 -0
- package/src/shared/node-sizes.ts +1 -0
- package/src/shared/tour.ts +87 -0
|
@@ -24,6 +24,15 @@
|
|
|
24
24
|
"content": "What the agent is doing, what context it holds, what it decided, and the tool calls behind it.",
|
|
25
25
|
"strictSize": true
|
|
26
26
|
},
|
|
27
|
+
"createdBy": {
|
|
28
|
+
"actor": "agent",
|
|
29
|
+
"source": "api"
|
|
30
|
+
},
|
|
31
|
+
"lastEditedBy": {
|
|
32
|
+
"actor": "agent",
|
|
33
|
+
"source": "api"
|
|
34
|
+
},
|
|
35
|
+
"contentRevision": 1,
|
|
27
36
|
"kind": "markdown",
|
|
28
37
|
"title": "① Start here",
|
|
29
38
|
"content": "What the agent is doing, what context it holds, what it decided, and the tool calls behind it.",
|
|
@@ -51,6 +60,15 @@
|
|
|
51
60
|
"content": "# PMX Canvas\n\nAn infinite spatial canvas that agents drive over **MCP**, **HTTP** or the **Bun SDK** — and that humans steer by pinning nodes.\n\nThis board is a live catalogue. Bands ① to ④ and ⑧ run down the left, ⑤ to ⑦ down the right.\n\n| Band | Shows |\n| --- | --- |\n| ① Start here | status · context · ledger · trace |\n| ② Files & review | file · CSV table · diff · image · webpage |\n| ③ Diagrams & surfaces | mermaid · raw HTML · MCP app |\n| ④ Charts | all 12 graph types |\n| ⑤ Structured UI | json-render specs |\n| ⑥ Grouping | one group frame |\n| ⑦ HTML primitives | all 21 generated surfaces |\n| ⑧ Agent at work | a live AX flow, mid-task |\n\n> Pin a node in the browser and the agent reads it back from `canvas://pinned-context`. That is the whole collaboration loop.",
|
|
52
61
|
"strictSize": true
|
|
53
62
|
},
|
|
63
|
+
"createdBy": {
|
|
64
|
+
"actor": "agent",
|
|
65
|
+
"source": "api"
|
|
66
|
+
},
|
|
67
|
+
"lastEditedBy": {
|
|
68
|
+
"actor": "agent",
|
|
69
|
+
"source": "api"
|
|
70
|
+
},
|
|
71
|
+
"contentRevision": 2,
|
|
54
72
|
"kind": "markdown",
|
|
55
73
|
"title": "PMX Canvas — guided tour",
|
|
56
74
|
"content": "# PMX Canvas\n\nAn infinite spatial canvas that agents drive over **MCP**, **HTTP** or the **Bun SDK** — and that humans steer by pinning nodes.\n\nThis board is a live catalogue. Bands ① to ④ and ⑧ run down the left, ⑤ to ⑦ down the right.\n\n| Band | Shows |\n| --- | --- |\n| ① Start here | status · context · ledger · trace |\n| ② Files & review | file · CSV table · diff · image · webpage |\n| ③ Diagrams & surfaces | mermaid · raw HTML · MCP app |\n| ④ Charts | all 12 graph types |\n| ⑤ Structured UI | json-render specs |\n| ⑥ Grouping | one group frame |\n| ⑦ HTML primitives | all 21 generated surfaces |\n| ⑧ Agent at work | a live AX flow, mid-task |\n\n> Pin a node in the browser and the agent reads it back from `canvas://pinned-context`. That is the whole collaboration loop.",
|
|
@@ -59,6 +77,42 @@
|
|
|
59
77
|
"surfaceUrl": null,
|
|
60
78
|
"provenance": null
|
|
61
79
|
},
|
|
80
|
+
{
|
|
81
|
+
"id": "board-link",
|
|
82
|
+
"type": "board",
|
|
83
|
+
"position": {
|
|
84
|
+
"x": -368,
|
|
85
|
+
"y": 200
|
|
86
|
+
},
|
|
87
|
+
"size": {
|
|
88
|
+
"width": 320,
|
|
89
|
+
"height": 160
|
|
90
|
+
},
|
|
91
|
+
"zIndex": 1,
|
|
92
|
+
"collapsed": false,
|
|
93
|
+
"pinned": false,
|
|
94
|
+
"data": {
|
|
95
|
+
"boardId": "board-demo-related",
|
|
96
|
+
"title": "Related research",
|
|
97
|
+
"strictSize": true
|
|
98
|
+
},
|
|
99
|
+
"createdBy": {
|
|
100
|
+
"actor": "agent",
|
|
101
|
+
"source": "api"
|
|
102
|
+
},
|
|
103
|
+
"lastEditedBy": {
|
|
104
|
+
"actor": "agent",
|
|
105
|
+
"source": "api"
|
|
106
|
+
},
|
|
107
|
+
"contentRevision": 3,
|
|
108
|
+
"kind": "board",
|
|
109
|
+
"title": "Related research",
|
|
110
|
+
"content": null,
|
|
111
|
+
"path": null,
|
|
112
|
+
"url": null,
|
|
113
|
+
"surfaceUrl": null,
|
|
114
|
+
"provenance": null
|
|
115
|
+
},
|
|
62
116
|
{
|
|
63
117
|
"id": "status",
|
|
64
118
|
"type": "status",
|
|
@@ -86,6 +140,15 @@
|
|
|
86
140
|
"title": "Agent",
|
|
87
141
|
"strictSize": true
|
|
88
142
|
},
|
|
143
|
+
"createdBy": {
|
|
144
|
+
"actor": "agent",
|
|
145
|
+
"source": "api"
|
|
146
|
+
},
|
|
147
|
+
"lastEditedBy": {
|
|
148
|
+
"actor": "agent",
|
|
149
|
+
"source": "api"
|
|
150
|
+
},
|
|
151
|
+
"contentRevision": 4,
|
|
89
152
|
"kind": "status",
|
|
90
153
|
"title": "Agent",
|
|
91
154
|
"content": null,
|
|
@@ -117,6 +180,15 @@
|
|
|
117
180
|
"title": "canvas_query",
|
|
118
181
|
"strictSize": true
|
|
119
182
|
},
|
|
183
|
+
"createdBy": {
|
|
184
|
+
"actor": "agent",
|
|
185
|
+
"source": "api"
|
|
186
|
+
},
|
|
187
|
+
"lastEditedBy": {
|
|
188
|
+
"actor": "agent",
|
|
189
|
+
"source": "api"
|
|
190
|
+
},
|
|
191
|
+
"contentRevision": 5,
|
|
120
192
|
"kind": "trace",
|
|
121
193
|
"title": "canvas_query",
|
|
122
194
|
"content": null,
|
|
@@ -148,6 +220,15 @@
|
|
|
148
220
|
"title": "edit_file",
|
|
149
221
|
"strictSize": true
|
|
150
222
|
},
|
|
223
|
+
"createdBy": {
|
|
224
|
+
"actor": "agent",
|
|
225
|
+
"source": "api"
|
|
226
|
+
},
|
|
227
|
+
"lastEditedBy": {
|
|
228
|
+
"actor": "agent",
|
|
229
|
+
"source": "api"
|
|
230
|
+
},
|
|
231
|
+
"contentRevision": 6,
|
|
151
232
|
"kind": "trace",
|
|
152
233
|
"title": "edit_file",
|
|
153
234
|
"content": null,
|
|
@@ -208,6 +289,15 @@
|
|
|
208
289
|
"title": "Working context",
|
|
209
290
|
"strictSize": true
|
|
210
291
|
},
|
|
292
|
+
"createdBy": {
|
|
293
|
+
"actor": "agent",
|
|
294
|
+
"source": "api"
|
|
295
|
+
},
|
|
296
|
+
"lastEditedBy": {
|
|
297
|
+
"actor": "agent",
|
|
298
|
+
"source": "api"
|
|
299
|
+
},
|
|
300
|
+
"contentRevision": 7,
|
|
211
301
|
"kind": "context",
|
|
212
302
|
"title": "Working context",
|
|
213
303
|
"content": null,
|
|
@@ -239,6 +329,15 @@
|
|
|
239
329
|
"content": "09:12 Ship 0.4.7 behind the existing smoke gate\n09:26 Keep demo board offline-only — no live sessions\n10:03 strictSize on every demo node so layout is stable\n10:41 Drop the OKR board; catalogue every node type instead\n11:15 Charts band shows all 12 graph types, Tufte row last\n11:48 Pin the intro, the context card and the diff under review\n13:20 Bands get section labels; only one group frame survives",
|
|
240
330
|
"strictSize": true
|
|
241
331
|
},
|
|
332
|
+
"createdBy": {
|
|
333
|
+
"actor": "agent",
|
|
334
|
+
"source": "api"
|
|
335
|
+
},
|
|
336
|
+
"lastEditedBy": {
|
|
337
|
+
"actor": "agent",
|
|
338
|
+
"source": "api"
|
|
339
|
+
},
|
|
340
|
+
"contentRevision": 8,
|
|
242
341
|
"kind": "ledger",
|
|
243
342
|
"title": "Decision ledger",
|
|
244
343
|
"content": "09:12 Ship 0.4.7 behind the existing smoke gate\n09:26 Keep demo board offline-only — no live sessions\n10:03 strictSize on every demo node so layout is stable\n10:41 Drop the OKR board; catalogue every node type instead\n11:15 Charts band shows all 12 graph types, Tufte row last\n11:48 Pin the intro, the context card and the diff under review\n13:20 Bands get section labels; only one group frame survives",
|
|
@@ -266,6 +365,15 @@
|
|
|
266
365
|
"content": "Inline file text, delimited data as a table, unified diffs, images, and cached webpage snapshots.",
|
|
267
366
|
"strictSize": true
|
|
268
367
|
},
|
|
368
|
+
"createdBy": {
|
|
369
|
+
"actor": "agent",
|
|
370
|
+
"source": "api"
|
|
371
|
+
},
|
|
372
|
+
"lastEditedBy": {
|
|
373
|
+
"actor": "agent",
|
|
374
|
+
"source": "api"
|
|
375
|
+
},
|
|
376
|
+
"contentRevision": 9,
|
|
269
377
|
"kind": "markdown",
|
|
270
378
|
"title": "② Files & review",
|
|
271
379
|
"content": "Inline file text, delimited data as a table, unified diffs, images, and cached webpage snapshots.",
|
|
@@ -295,6 +403,15 @@
|
|
|
295
403
|
"lineCount": 20,
|
|
296
404
|
"strictSize": true
|
|
297
405
|
},
|
|
406
|
+
"createdBy": {
|
|
407
|
+
"actor": "agent",
|
|
408
|
+
"source": "api"
|
|
409
|
+
},
|
|
410
|
+
"lastEditedBy": {
|
|
411
|
+
"actor": "agent",
|
|
412
|
+
"source": "api"
|
|
413
|
+
},
|
|
414
|
+
"contentRevision": 10,
|
|
298
415
|
"kind": "file",
|
|
299
416
|
"title": "node-sizes.ts",
|
|
300
417
|
"content": "import { canvasState } from './canvas-state.js';\nimport { nodeMinSize } from '../shared/node-sizes.js';\n\n/** Clamp an agent-supplied frame up to the per-type readability floor. */\nexport function clampNodeFrame(type: string, width: number, height: number) {\n const min = nodeMinSize(type);\n if (!min) return { width, height };\n return {\n width: Math.max(width, min.width),\n height: Math.max(height, min.height),\n };\n}\n\nexport function nodeCountByType(): Record<string, number> {\n const counts: Record<string, number> = {};\n for (const node of canvasState.getLayout().nodes) {\n counts[node.type] = (counts[node.type] ?? 0) + 1;\n }\n return counts;\n}",
|
|
@@ -322,6 +439,15 @@
|
|
|
322
439
|
"content": "diff --git a/src/shared/node-sizes.ts b/src/shared/node-sizes.ts\n--- a/src/shared/node-sizes.ts\n+++ b/src/shared/node-sizes.ts\n@@ -20,6 +20,8 @@ export const NODE_MIN_SIZES: Record<string, { width: number; height: number }> =\n markdown: { width: 360, height: 180 },\n context: { width: 360, height: 180 },\n file: { width: 360, height: 200 },\n+ diff: { width: 420, height: 240 },\n+ mermaid: { width: 360, height: 240 },\n status: { width: 280, height: 120 },\n\ndiff --git a/src/client/canvas/auto-fit.ts b/src/client/canvas/auto-fit.ts\n--- a/src/client/canvas/auto-fit.ts\n+++ b/src/client/canvas/auto-fit.ts\n@@ -63,7 +63,8 @@ export function computeAutoFitHeight(node: CanvasNodeState, contentHeight: numbe\n const fitted = Math.min(contentHeight + AUTO_FIT_TITLEBAR_HEIGHT, AUTO_FIT_MAX_HEIGHT);\n- return fitted;\n+ const min = nodeMinSize(node.type);\n+ return min ? Math.max(fitted, min.height) : fitted;\n }",
|
|
323
440
|
"strictSize": true
|
|
324
441
|
},
|
|
442
|
+
"createdBy": {
|
|
443
|
+
"actor": "agent",
|
|
444
|
+
"source": "api"
|
|
445
|
+
},
|
|
446
|
+
"lastEditedBy": {
|
|
447
|
+
"actor": "agent",
|
|
448
|
+
"source": "api"
|
|
449
|
+
},
|
|
450
|
+
"contentRevision": 11,
|
|
325
451
|
"kind": "diff",
|
|
326
452
|
"title": "Readability floor — 2 files",
|
|
327
453
|
"content": "diff --git a/src/shared/node-sizes.ts b/src/shared/node-sizes.ts\n--- a/src/shared/node-sizes.ts\n+++ b/src/shared/node-sizes.ts\n@@ -20,6 +20,8 @@ export const NODE_MIN_SIZES: Record<string, { width: number; height: number }> =\n markdown: { width: 360, height: 180 },\n context: { width: 360, height: 180 },\n file: { width: 360, height: 200 },\n+ diff: { width: 420, height: 240 },\n+ mermaid: { width: 360, height: 240 },\n status: { width: 280, height: 120 },\n\ndiff --git a/src/client/canvas/auto-fit.ts b/src/client/canvas/auto-fit.ts\n--- a/src/client/canvas/auto-fit.ts\n+++ b/src/client/canvas/auto-fit.ts\n@@ -63,7 +63,8 @@ export function computeAutoFitHeight(node: CanvasNodeState, contentHeight: numbe\n const fitted = Math.min(contentHeight + AUTO_FIT_TITLEBAR_HEIGHT, AUTO_FIT_MAX_HEIGHT);\n- return fitted;\n+ const min = nodeMinSize(node.type);\n+ return min ? Math.max(fitted, min.height) : fitted;\n }",
|
|
@@ -345,23 +471,34 @@
|
|
|
345
471
|
"collapsed": false,
|
|
346
472
|
"pinned": false,
|
|
347
473
|
"data": {
|
|
474
|
+
"path": "/workspace/reports/weekly-throughput.csv",
|
|
348
475
|
"title": "weekly-throughput.csv",
|
|
349
|
-
"
|
|
476
|
+
"byteSize": 197,
|
|
477
|
+
"updatedAt": "2026-08-01T09:00:00.000Z",
|
|
350
478
|
"fileContent": "week,merged_prs,review_hours,p95_ci_minutes,flaky_reruns\n2026-W23,41,18.5,11.2,6\n2026-W24,38,21.0,12.8,9\n2026-W25,52,16.4,10.1,4\n2026-W26,47,15.2,9.6,3\n2026-W27,55,14.8,9.1,2\n2026-W28,61,13.9,8.4,2",
|
|
351
479
|
"lineCount": 7,
|
|
352
480
|
"strictSize": true,
|
|
353
|
-
"path": "/workspace/reports/weekly-throughput.csv",
|
|
354
481
|
"provenance": {
|
|
355
482
|
"sourceKind": "workspace-file",
|
|
356
483
|
"sourceUri": "file:///workspace/reports/weekly-throughput.csv",
|
|
357
484
|
"refreshStrategy": "file-watch",
|
|
358
485
|
"snapshotContent": true,
|
|
486
|
+
"syncedAt": "2026-08-01T09:00:00.000Z",
|
|
359
487
|
"details": {
|
|
360
488
|
"path": "/workspace/reports/weekly-throughput.csv",
|
|
361
489
|
"nodeType": "file"
|
|
362
490
|
}
|
|
363
491
|
}
|
|
364
492
|
},
|
|
493
|
+
"createdBy": {
|
|
494
|
+
"actor": "agent",
|
|
495
|
+
"source": "api"
|
|
496
|
+
},
|
|
497
|
+
"lastEditedBy": {
|
|
498
|
+
"actor": "agent",
|
|
499
|
+
"source": "api"
|
|
500
|
+
},
|
|
501
|
+
"contentRevision": 12,
|
|
365
502
|
"kind": "file",
|
|
366
503
|
"title": "weekly-throughput.csv",
|
|
367
504
|
"content": "week,merged_prs,review_hours,p95_ci_minutes,flaky_reruns\n2026-W23,41,18.5,11.2,6\n2026-W24,38,21.0,12.8,9\n2026-W25,52,16.4,10.1,4\n2026-W26,47,15.2,9.6,3\n2026-W27,55,14.8,9.1,2\n2026-W28,61,13.9,8.4,2",
|
|
@@ -373,6 +510,7 @@
|
|
|
373
510
|
"sourceUri": "file:///workspace/reports/weekly-throughput.csv",
|
|
374
511
|
"refreshStrategy": "file-watch",
|
|
375
512
|
"snapshotContent": true,
|
|
513
|
+
"syncedAt": "2026-08-01T09:00:00.000Z",
|
|
376
514
|
"details": {
|
|
377
515
|
"path": "/workspace/reports/weekly-throughput.csv",
|
|
378
516
|
"nodeType": "file"
|
|
@@ -399,6 +537,15 @@
|
|
|
399
537
|
"title": "Canvas data flow",
|
|
400
538
|
"strictSize": true
|
|
401
539
|
},
|
|
540
|
+
"createdBy": {
|
|
541
|
+
"actor": "agent",
|
|
542
|
+
"source": "api"
|
|
543
|
+
},
|
|
544
|
+
"lastEditedBy": {
|
|
545
|
+
"actor": "agent",
|
|
546
|
+
"source": "api"
|
|
547
|
+
},
|
|
548
|
+
"contentRevision": 13,
|
|
402
549
|
"kind": "image",
|
|
403
550
|
"title": "Canvas data flow",
|
|
404
551
|
"content": "data:image/svg+xml;utf8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%20480%20300'%20width%3D'480'%20height%3D'300'%3E%3Crect%20width%3D'480'%20height%3D'300'%20rx%3D'16'%20fill%3D'%230f1420'%2F%3E%3Ctext%20x%3D'24'%20y%3D'40'%20fill%3D'%23e6ecff'%20font-family%3D'ui-sans-serif%2Csystem-ui'%20font-size%3D'17'%20font-weight%3D'600'%3ECanvas%20data%20flow%3C%2Ftext%3E%3Crect%20x%3D'24'%20y%3D'70'%20width%3D'128'%20height%3D'62'%20rx%3D'10'%20fill%3D'%2316203a'%20stroke%3D'%234a9eff'%2F%3E%3Ctext%20x%3D'88'%20y%3D'106'%20fill%3D'%239ec5ff'%20font-family%3D'ui-sans-serif%2Csystem-ui'%20font-size%3D'13'%20text-anchor%3D'middle'%3EAgent%20(MCP)%3C%2Ftext%3E%3Crect%20x%3D'176'%20y%3D'70'%20width%3D'128'%20height%3D'62'%20rx%3D'10'%20fill%3D'%2316203a'%20stroke%3D'%237c5cff'%2F%3E%3Ctext%20x%3D'240'%20y%3D'100'%20fill%3D'%23c3b4ff'%20font-family%3D'ui-sans-serif%2Csystem-ui'%20font-size%3D'13'%20text-anchor%3D'middle'%3ECanvas%20state%3C%2Ftext%3E%3Ctext%20x%3D'240'%20y%3D'118'%20fill%3D'%237b86a8'%20font-family%3D'ui-sans-serif%2Csystem-ui'%20font-size%3D'11'%20text-anchor%3D'middle'%3Esingle%20source%20of%20truth%3C%2Ftext%3E%3Crect%20x%3D'328'%20y%3D'70'%20width%3D'128'%20height%3D'62'%20rx%3D'10'%20fill%3D'%2316203a'%20stroke%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'392'%20y%3D'106'%20fill%3D'%238ee5ad'%20font-family%3D'ui-sans-serif%2Csystem-ui'%20font-size%3D'13'%20text-anchor%3D'middle'%3EBrowser%3C%2Ftext%3E%3Cpath%20d%3D'M152%20101%20L172%20101'%20stroke%3D'%234a9eff'%20stroke-width%3D'2'%2F%3E%3Cpath%20d%3D'M304%20101%20L324%20101'%20stroke%3D'%2322c55e'%20stroke-width%3D'2'%2F%3E%3Cpath%20d%3D'M392%20140%20L392%20196%20L88%20196%20L88%20140'%20fill%3D'none'%20stroke%3D'%23eab308'%20stroke-width%3D'2'%20stroke-dasharray%3D'5%205'%2F%3E%3Ctext%20x%3D'240'%20y%3D'188'%20fill%3D'%23eab308'%20font-family%3D'ui-sans-serif%2Csystem-ui'%20font-size%3D'12'%20text-anchor%3D'middle'%3Ehuman%20pins%20nodes%20-%26gt%3B%20canvas%3A%2F%2Fpinned-context%3C%2Ftext%3E%3Crect%20x%3D'24'%20y%3D'222'%20width%3D'432'%20height%3D'52'%20rx%3D'10'%20fill%3D'%23111827'%20stroke%3D'%23243049'%2F%3E%3Ctext%20x%3D'240'%20y%3D'253'%20fill%3D'%237b86a8'%20font-family%3D'ui-monospace%2Cmonospace'%20font-size%3D'12'%20text-anchor%3D'middle'%3Estate%20survives%20refresh%20%C2%B7%20SQLite%20at%20.pmx-canvas%2Fcanvas.db%3C%2Ftext%3E%3C%2Fsvg%3E",
|
|
@@ -450,6 +597,15 @@
|
|
|
450
597
|
"frameBlocked": false,
|
|
451
598
|
"frameBlockedReason": ""
|
|
452
599
|
},
|
|
600
|
+
"createdBy": {
|
|
601
|
+
"actor": "agent",
|
|
602
|
+
"source": "api"
|
|
603
|
+
},
|
|
604
|
+
"lastEditedBy": {
|
|
605
|
+
"actor": "agent",
|
|
606
|
+
"source": "api"
|
|
607
|
+
},
|
|
608
|
+
"contentRevision": 16,
|
|
453
609
|
"kind": "webpage",
|
|
454
610
|
"title": "Model Context Protocol — Resources",
|
|
455
611
|
"content": "https://modelcontextprotocol.io/docs/concepts/resources",
|
|
@@ -488,6 +644,15 @@
|
|
|
488
644
|
"content": "Mermaid renders from plain text; HTML and MCP App nodes are sandboxed iframes the agent authors.",
|
|
489
645
|
"strictSize": true
|
|
490
646
|
},
|
|
647
|
+
"createdBy": {
|
|
648
|
+
"actor": "agent",
|
|
649
|
+
"source": "api"
|
|
650
|
+
},
|
|
651
|
+
"lastEditedBy": {
|
|
652
|
+
"actor": "agent",
|
|
653
|
+
"source": "api"
|
|
654
|
+
},
|
|
655
|
+
"contentRevision": 17,
|
|
491
656
|
"kind": "markdown",
|
|
492
657
|
"title": "③ Diagrams & surfaces",
|
|
493
658
|
"content": "Mermaid renders from plain text; HTML and MCP App nodes are sandboxed iframes the agent authors.",
|
|
@@ -515,6 +680,15 @@
|
|
|
515
680
|
"content": "flowchart LR\n A[Agent] -->|MCP tool call| B(Canvas server)\n B --> C{{SSE stream}}\n C --> D[Browser canvas]\n D -->|human pins a node| B\n B --> E[(canvas.db)]",
|
|
516
681
|
"strictSize": true
|
|
517
682
|
},
|
|
683
|
+
"createdBy": {
|
|
684
|
+
"actor": "agent",
|
|
685
|
+
"source": "api"
|
|
686
|
+
},
|
|
687
|
+
"lastEditedBy": {
|
|
688
|
+
"actor": "agent",
|
|
689
|
+
"source": "api"
|
|
690
|
+
},
|
|
691
|
+
"contentRevision": 18,
|
|
518
692
|
"kind": "mermaid",
|
|
519
693
|
"title": "Flowchart — request path",
|
|
520
694
|
"content": "flowchart LR\n A[Agent] -->|MCP tool call| B(Canvas server)\n B --> C{{SSE stream}}\n C --> D[Browser canvas]\n D -->|human pins a node| B\n B --> E[(canvas.db)]",
|
|
@@ -542,6 +716,15 @@
|
|
|
542
716
|
"content": "sequenceDiagram\n participant H as Human\n participant C as Canvas\n participant A as Agent\n H->>C: pin 3 nodes\n C-->>A: resources/updated\n A->>C: read canvas://pinned-context\n A->>C: canvas_node add (summary)\n C-->>H: SSE layout update",
|
|
543
717
|
"strictSize": true
|
|
544
718
|
},
|
|
719
|
+
"createdBy": {
|
|
720
|
+
"actor": "agent",
|
|
721
|
+
"source": "api"
|
|
722
|
+
},
|
|
723
|
+
"lastEditedBy": {
|
|
724
|
+
"actor": "agent",
|
|
725
|
+
"source": "api"
|
|
726
|
+
},
|
|
727
|
+
"contentRevision": 19,
|
|
545
728
|
"kind": "mermaid",
|
|
546
729
|
"title": "Sequence — the pin loop",
|
|
547
730
|
"content": "sequenceDiagram\n participant H as Human\n participant C as Canvas\n participant A as Agent\n H->>C: pin 3 nodes\n C-->>A: resources/updated\n A->>C: read canvas://pinned-context\n A->>C: canvas_node add (summary)\n C-->>H: SSE layout update",
|
|
@@ -569,6 +752,15 @@
|
|
|
569
752
|
"content": "stateDiagram-v2\n [*] --> pending\n pending --> approved: human approves\n pending --> rejected: human rejects\n pending --> expired: timeout\n approved --> [*]\n rejected --> [*]\n expired --> [*]",
|
|
570
753
|
"strictSize": true
|
|
571
754
|
},
|
|
755
|
+
"createdBy": {
|
|
756
|
+
"actor": "agent",
|
|
757
|
+
"source": "api"
|
|
758
|
+
},
|
|
759
|
+
"lastEditedBy": {
|
|
760
|
+
"actor": "agent",
|
|
761
|
+
"source": "api"
|
|
762
|
+
},
|
|
763
|
+
"contentRevision": 20,
|
|
572
764
|
"kind": "mermaid",
|
|
573
765
|
"title": "State — approval gate",
|
|
574
766
|
"content": "stateDiagram-v2\n [*] --> pending\n pending --> approved: human approves\n pending --> rejected: human rejects\n pending --> expired: timeout\n approved --> [*]\n rejected --> [*]\n expired --> [*]",
|
|
@@ -599,6 +791,15 @@
|
|
|
599
791
|
"contentSummary": "Release gates — 0.4.7 Raw HTML in a sandboxed iframe. No scripts, no network, no build step. typecheck passing unit tests 612 passed web-canvas e2e running bundle size +4.1% vs 0.4.6 pack dry-run clean",
|
|
600
792
|
"agentSummary": "Five release gates with pass/running/warning status for 0.4.7.\nRelease gates — 0.4.7 Raw HTML in a sandboxed iframe. No scripts, no network, no build step. typecheck passing unit tests 612 passed web-canvas e2e running bundle size +4.1% vs 0.4.6 pack dry-run clean"
|
|
601
793
|
},
|
|
794
|
+
"createdBy": {
|
|
795
|
+
"actor": "agent",
|
|
796
|
+
"source": "api"
|
|
797
|
+
},
|
|
798
|
+
"lastEditedBy": {
|
|
799
|
+
"actor": "agent",
|
|
800
|
+
"source": "api"
|
|
801
|
+
},
|
|
802
|
+
"contentRevision": 21,
|
|
602
803
|
"kind": "html",
|
|
603
804
|
"title": "Release gates",
|
|
604
805
|
"content": "Five release gates with pass/running/warning status for 0.4.7.\nRelease gates — 0.4.7 Raw HTML in a sandboxed iframe. No scripts, no network, no build step. typecheck passing unit tests 612 passed web-canvas e2e running bundle size +4.1% vs 0.4.6 pack dry-run clean",
|
|
@@ -640,6 +841,15 @@
|
|
|
640
841
|
}
|
|
641
842
|
}
|
|
642
843
|
},
|
|
844
|
+
"createdBy": {
|
|
845
|
+
"actor": "agent",
|
|
846
|
+
"source": "api"
|
|
847
|
+
},
|
|
848
|
+
"lastEditedBy": {
|
|
849
|
+
"actor": "agent",
|
|
850
|
+
"source": "api"
|
|
851
|
+
},
|
|
852
|
+
"contentRevision": 22,
|
|
643
853
|
"kind": "external-app",
|
|
644
854
|
"title": "Board inspector",
|
|
645
855
|
"content": null,
|
|
@@ -677,6 +887,15 @@
|
|
|
677
887
|
"content": "Every canonical graph type, then the Tufte primitives in the compact bottom row.",
|
|
678
888
|
"strictSize": true
|
|
679
889
|
},
|
|
890
|
+
"createdBy": {
|
|
891
|
+
"actor": "agent",
|
|
892
|
+
"source": "api"
|
|
893
|
+
},
|
|
894
|
+
"lastEditedBy": {
|
|
895
|
+
"actor": "agent",
|
|
896
|
+
"source": "api"
|
|
897
|
+
},
|
|
898
|
+
"contentRevision": 23,
|
|
680
899
|
"kind": "markdown",
|
|
681
900
|
"title": "④ Charts",
|
|
682
901
|
"content": "Every canonical graph type, then the Tufte primitives in the compact bottom row.",
|
|
@@ -796,6 +1015,15 @@
|
|
|
796
1015
|
},
|
|
797
1016
|
"strictSize": true
|
|
798
1017
|
},
|
|
1018
|
+
"createdBy": {
|
|
1019
|
+
"actor": "agent",
|
|
1020
|
+
"source": "api"
|
|
1021
|
+
},
|
|
1022
|
+
"lastEditedBy": {
|
|
1023
|
+
"actor": "agent",
|
|
1024
|
+
"source": "api"
|
|
1025
|
+
},
|
|
1026
|
+
"contentRevision": 24,
|
|
799
1027
|
"kind": "graph",
|
|
800
1028
|
"title": "line — p95 latency",
|
|
801
1029
|
"content": null,
|
|
@@ -919,6 +1147,15 @@
|
|
|
919
1147
|
},
|
|
920
1148
|
"strictSize": true
|
|
921
1149
|
},
|
|
1150
|
+
"createdBy": {
|
|
1151
|
+
"actor": "agent",
|
|
1152
|
+
"source": "api"
|
|
1153
|
+
},
|
|
1154
|
+
"lastEditedBy": {
|
|
1155
|
+
"actor": "agent",
|
|
1156
|
+
"source": "api"
|
|
1157
|
+
},
|
|
1158
|
+
"contentRevision": 25,
|
|
922
1159
|
"kind": "graph",
|
|
923
1160
|
"title": "bar — merged PRs per week",
|
|
924
1161
|
"content": null,
|
|
@@ -1030,6 +1267,15 @@
|
|
|
1030
1267
|
},
|
|
1031
1268
|
"strictSize": true
|
|
1032
1269
|
},
|
|
1270
|
+
"createdBy": {
|
|
1271
|
+
"actor": "agent",
|
|
1272
|
+
"source": "api"
|
|
1273
|
+
},
|
|
1274
|
+
"lastEditedBy": {
|
|
1275
|
+
"actor": "agent",
|
|
1276
|
+
"source": "api"
|
|
1277
|
+
},
|
|
1278
|
+
"contentRevision": 26,
|
|
1033
1279
|
"kind": "graph",
|
|
1034
1280
|
"title": "area — canvas nodes created",
|
|
1035
1281
|
"content": null,
|
|
@@ -1135,6 +1381,15 @@
|
|
|
1135
1381
|
},
|
|
1136
1382
|
"strictSize": true
|
|
1137
1383
|
},
|
|
1384
|
+
"createdBy": {
|
|
1385
|
+
"actor": "agent",
|
|
1386
|
+
"source": "api"
|
|
1387
|
+
},
|
|
1388
|
+
"lastEditedBy": {
|
|
1389
|
+
"actor": "agent",
|
|
1390
|
+
"source": "api"
|
|
1391
|
+
},
|
|
1392
|
+
"contentRevision": 27,
|
|
1138
1393
|
"kind": "graph",
|
|
1139
1394
|
"title": "pie — node types on real boards",
|
|
1140
1395
|
"content": null,
|
|
@@ -1263,6 +1518,15 @@
|
|
|
1263
1518
|
},
|
|
1264
1519
|
"strictSize": true
|
|
1265
1520
|
},
|
|
1521
|
+
"createdBy": {
|
|
1522
|
+
"actor": "agent",
|
|
1523
|
+
"source": "api"
|
|
1524
|
+
},
|
|
1525
|
+
"lastEditedBy": {
|
|
1526
|
+
"actor": "agent",
|
|
1527
|
+
"source": "api"
|
|
1528
|
+
},
|
|
1529
|
+
"contentRevision": 28,
|
|
1266
1530
|
"kind": "graph",
|
|
1267
1531
|
"title": "stacked-bar — CI minutes by stage",
|
|
1268
1532
|
"content": null,
|
|
@@ -1377,6 +1641,15 @@
|
|
|
1377
1641
|
},
|
|
1378
1642
|
"strictSize": true
|
|
1379
1643
|
},
|
|
1644
|
+
"createdBy": {
|
|
1645
|
+
"actor": "agent",
|
|
1646
|
+
"source": "api"
|
|
1647
|
+
},
|
|
1648
|
+
"lastEditedBy": {
|
|
1649
|
+
"actor": "agent",
|
|
1650
|
+
"source": "api"
|
|
1651
|
+
},
|
|
1652
|
+
"contentRevision": 29,
|
|
1380
1653
|
"kind": "graph",
|
|
1381
1654
|
"title": "composed — throughput vs review load",
|
|
1382
1655
|
"content": null,
|
|
@@ -1510,6 +1783,15 @@
|
|
|
1510
1783
|
},
|
|
1511
1784
|
"strictSize": true
|
|
1512
1785
|
},
|
|
1786
|
+
"createdBy": {
|
|
1787
|
+
"actor": "agent",
|
|
1788
|
+
"source": "api"
|
|
1789
|
+
},
|
|
1790
|
+
"lastEditedBy": {
|
|
1791
|
+
"actor": "agent",
|
|
1792
|
+
"source": "api"
|
|
1793
|
+
},
|
|
1794
|
+
"contentRevision": 30,
|
|
1513
1795
|
"kind": "graph",
|
|
1514
1796
|
"title": "scatter — PR size vs review time",
|
|
1515
1797
|
"content": null,
|
|
@@ -1638,6 +1920,15 @@
|
|
|
1638
1920
|
},
|
|
1639
1921
|
"strictSize": true
|
|
1640
1922
|
},
|
|
1923
|
+
"createdBy": {
|
|
1924
|
+
"actor": "agent",
|
|
1925
|
+
"source": "api"
|
|
1926
|
+
},
|
|
1927
|
+
"lastEditedBy": {
|
|
1928
|
+
"actor": "agent",
|
|
1929
|
+
"source": "api"
|
|
1930
|
+
},
|
|
1931
|
+
"contentRevision": 31,
|
|
1641
1932
|
"kind": "graph",
|
|
1642
1933
|
"title": "radar — surface coverage",
|
|
1643
1934
|
"content": null,
|
|
@@ -1779,6 +2070,15 @@
|
|
|
1779
2070
|
},
|
|
1780
2071
|
"strictSize": true
|
|
1781
2072
|
},
|
|
2073
|
+
"createdBy": {
|
|
2074
|
+
"actor": "agent",
|
|
2075
|
+
"source": "api"
|
|
2076
|
+
},
|
|
2077
|
+
"lastEditedBy": {
|
|
2078
|
+
"actor": "agent",
|
|
2079
|
+
"source": "api"
|
|
2080
|
+
},
|
|
2081
|
+
"contentRevision": 32,
|
|
1782
2082
|
"kind": "graph",
|
|
1783
2083
|
"title": "sparkline — build time",
|
|
1784
2084
|
"content": null,
|
|
@@ -1884,6 +2184,15 @@
|
|
|
1884
2184
|
},
|
|
1885
2185
|
"strictSize": true
|
|
1886
2186
|
},
|
|
2187
|
+
"createdBy": {
|
|
2188
|
+
"actor": "agent",
|
|
2189
|
+
"source": "api"
|
|
2190
|
+
},
|
|
2191
|
+
"lastEditedBy": {
|
|
2192
|
+
"actor": "agent",
|
|
2193
|
+
"source": "api"
|
|
2194
|
+
},
|
|
2195
|
+
"contentRevision": 33,
|
|
1887
2196
|
"kind": "graph",
|
|
1888
2197
|
"title": "dot-plot — test time by suite",
|
|
1889
2198
|
"content": null,
|
|
@@ -2019,6 +2328,15 @@
|
|
|
2019
2328
|
},
|
|
2020
2329
|
"strictSize": true
|
|
2021
2330
|
},
|
|
2331
|
+
"createdBy": {
|
|
2332
|
+
"actor": "agent",
|
|
2333
|
+
"source": "api"
|
|
2334
|
+
},
|
|
2335
|
+
"lastEditedBy": {
|
|
2336
|
+
"actor": "agent",
|
|
2337
|
+
"source": "api"
|
|
2338
|
+
},
|
|
2339
|
+
"contentRevision": 34,
|
|
2022
2340
|
"kind": "graph",
|
|
2023
2341
|
"title": "bullet — release KPIs vs target",
|
|
2024
2342
|
"content": null,
|
|
@@ -2138,6 +2456,15 @@
|
|
|
2138
2456
|
},
|
|
2139
2457
|
"strictSize": true
|
|
2140
2458
|
},
|
|
2459
|
+
"createdBy": {
|
|
2460
|
+
"actor": "agent",
|
|
2461
|
+
"source": "api"
|
|
2462
|
+
},
|
|
2463
|
+
"lastEditedBy": {
|
|
2464
|
+
"actor": "agent",
|
|
2465
|
+
"source": "api"
|
|
2466
|
+
},
|
|
2467
|
+
"contentRevision": 35,
|
|
2141
2468
|
"kind": "graph",
|
|
2142
2469
|
"title": "slopegraph — coverage 0.4.6 → 0.4.7",
|
|
2143
2470
|
"content": null,
|
|
@@ -2165,6 +2492,15 @@
|
|
|
2165
2492
|
"content": "json-render nodes: an agent posts a validated component spec and the canvas renders real UI.",
|
|
2166
2493
|
"strictSize": true
|
|
2167
2494
|
},
|
|
2495
|
+
"createdBy": {
|
|
2496
|
+
"actor": "agent",
|
|
2497
|
+
"source": "api"
|
|
2498
|
+
},
|
|
2499
|
+
"lastEditedBy": {
|
|
2500
|
+
"actor": "agent",
|
|
2501
|
+
"source": "api"
|
|
2502
|
+
},
|
|
2503
|
+
"contentRevision": 36,
|
|
2168
2504
|
"kind": "markdown",
|
|
2169
2505
|
"title": "⑤ Structured UI",
|
|
2170
2506
|
"content": "json-render nodes: an agent posts a validated component spec and the canvas renders real UI.",
|
|
@@ -2364,6 +2700,15 @@
|
|
|
2364
2700
|
"viewerType": "json-render",
|
|
2365
2701
|
"strictSize": true
|
|
2366
2702
|
},
|
|
2703
|
+
"createdBy": {
|
|
2704
|
+
"actor": "agent",
|
|
2705
|
+
"source": "api"
|
|
2706
|
+
},
|
|
2707
|
+
"lastEditedBy": {
|
|
2708
|
+
"actor": "agent",
|
|
2709
|
+
"source": "api"
|
|
2710
|
+
},
|
|
2711
|
+
"contentRevision": 37,
|
|
2367
2712
|
"kind": "json-render",
|
|
2368
2713
|
"title": "Release dashboard",
|
|
2369
2714
|
"content": null,
|
|
@@ -2540,6 +2885,15 @@
|
|
|
2540
2885
|
"viewerType": "json-render",
|
|
2541
2886
|
"strictSize": true
|
|
2542
2887
|
},
|
|
2888
|
+
"createdBy": {
|
|
2889
|
+
"actor": "agent",
|
|
2890
|
+
"source": "api"
|
|
2891
|
+
},
|
|
2892
|
+
"lastEditedBy": {
|
|
2893
|
+
"actor": "agent",
|
|
2894
|
+
"source": "api"
|
|
2895
|
+
},
|
|
2896
|
+
"contentRevision": 38,
|
|
2543
2897
|
"kind": "json-render",
|
|
2544
2898
|
"title": "Blocker intake form",
|
|
2545
2899
|
"content": null,
|
|
@@ -2699,6 +3053,15 @@
|
|
|
2699
3053
|
"viewerType": "json-render",
|
|
2700
3054
|
"strictSize": true
|
|
2701
3055
|
},
|
|
3056
|
+
"createdBy": {
|
|
3057
|
+
"actor": "agent",
|
|
3058
|
+
"source": "api"
|
|
3059
|
+
},
|
|
3060
|
+
"lastEditedBy": {
|
|
3061
|
+
"actor": "agent",
|
|
3062
|
+
"source": "api"
|
|
3063
|
+
},
|
|
3064
|
+
"contentRevision": 39,
|
|
2702
3065
|
"kind": "json-render",
|
|
2703
3066
|
"title": "Usage panel",
|
|
2704
3067
|
"content": null,
|
|
@@ -2726,6 +3089,15 @@
|
|
|
2726
3089
|
"content": "A group says \"these belong together\". One frame is enough — bands carry a board’s structure.",
|
|
2727
3090
|
"strictSize": true
|
|
2728
3091
|
},
|
|
3092
|
+
"createdBy": {
|
|
3093
|
+
"actor": "agent",
|
|
3094
|
+
"source": "api"
|
|
3095
|
+
},
|
|
3096
|
+
"lastEditedBy": {
|
|
3097
|
+
"actor": "agent",
|
|
3098
|
+
"source": "api"
|
|
3099
|
+
},
|
|
3100
|
+
"contentRevision": 40,
|
|
2729
3101
|
"kind": "markdown",
|
|
2730
3102
|
"title": "⑥ Grouping",
|
|
2731
3103
|
"content": "A group says \"these belong together\". One frame is enough — bands carry a board’s structure.",
|
|
@@ -2754,6 +3126,15 @@
|
|
|
2754
3126
|
"strictSize": true,
|
|
2755
3127
|
"parentGroup": "group"
|
|
2756
3128
|
},
|
|
3129
|
+
"createdBy": {
|
|
3130
|
+
"actor": "agent",
|
|
3131
|
+
"source": "api"
|
|
3132
|
+
},
|
|
3133
|
+
"lastEditedBy": {
|
|
3134
|
+
"actor": "agent",
|
|
3135
|
+
"source": "api"
|
|
3136
|
+
},
|
|
3137
|
+
"contentRevision": 41,
|
|
2757
3138
|
"kind": "markdown",
|
|
2758
3139
|
"title": "Sprint 41 — canvas polish",
|
|
2759
3140
|
"content": "Three nodes, one frame. Moving the frame moves the children; ungrouping leaves them exactly where they are.\n\n- Group children keep their own positions by default\n- `childLayout` opts into auto-packing\n- The frame is stored as `data.children` on the group",
|
|
@@ -2785,6 +3166,15 @@
|
|
|
2785
3166
|
"strictSize": true,
|
|
2786
3167
|
"parentGroup": "group"
|
|
2787
3168
|
},
|
|
3169
|
+
"createdBy": {
|
|
3170
|
+
"actor": "agent",
|
|
3171
|
+
"source": "api"
|
|
3172
|
+
},
|
|
3173
|
+
"lastEditedBy": {
|
|
3174
|
+
"actor": "agent",
|
|
3175
|
+
"source": "api"
|
|
3176
|
+
},
|
|
3177
|
+
"contentRevision": 42,
|
|
2788
3178
|
"kind": "status",
|
|
2789
3179
|
"title": "Sprint state",
|
|
2790
3180
|
"content": null,
|
|
@@ -2817,6 +3207,15 @@
|
|
|
2817
3207
|
"strictSize": true,
|
|
2818
3208
|
"parentGroup": "group"
|
|
2819
3209
|
},
|
|
3210
|
+
"createdBy": {
|
|
3211
|
+
"actor": "agent",
|
|
3212
|
+
"source": "api"
|
|
3213
|
+
},
|
|
3214
|
+
"lastEditedBy": {
|
|
3215
|
+
"actor": "agent",
|
|
3216
|
+
"source": "api"
|
|
3217
|
+
},
|
|
3218
|
+
"contentRevision": 43,
|
|
2820
3219
|
"kind": "trace",
|
|
2821
3220
|
"title": "canvas_group",
|
|
2822
3221
|
"content": null,
|
|
@@ -2849,6 +3248,15 @@
|
|
|
2849
3248
|
"frameMode": "manual",
|
|
2850
3249
|
"color": "#7c5cff"
|
|
2851
3250
|
},
|
|
3251
|
+
"createdBy": {
|
|
3252
|
+
"actor": "agent",
|
|
3253
|
+
"source": "api"
|
|
3254
|
+
},
|
|
3255
|
+
"lastEditedBy": {
|
|
3256
|
+
"actor": "agent",
|
|
3257
|
+
"source": "api"
|
|
3258
|
+
},
|
|
3259
|
+
"contentRevision": 44,
|
|
2852
3260
|
"kind": "group",
|
|
2853
3261
|
"title": "Sprint 41",
|
|
2854
3262
|
"content": null,
|
|
@@ -2876,6 +3284,15 @@
|
|
|
2876
3284
|
"content": "All 21 generated communication surfaces — self-contained sandboxed documents, no build step.",
|
|
2877
3285
|
"strictSize": true
|
|
2878
3286
|
},
|
|
3287
|
+
"createdBy": {
|
|
3288
|
+
"actor": "agent",
|
|
3289
|
+
"source": "api"
|
|
3290
|
+
},
|
|
3291
|
+
"lastEditedBy": {
|
|
3292
|
+
"actor": "agent",
|
|
3293
|
+
"source": "api"
|
|
3294
|
+
},
|
|
3295
|
+
"contentRevision": 45,
|
|
2879
3296
|
"kind": "markdown",
|
|
2880
3297
|
"title": "⑦ HTML primitives",
|
|
2881
3298
|
"content": "All 21 generated communication surfaces — self-contained sandboxed documents, no build step.",
|
|
@@ -2952,6 +3369,15 @@
|
|
|
2952
3369
|
"contentSummary": "How should the demo board be generated? PMX HTML primitive / choice-grid How should the demo board be generated? Side-by-side options with tradeoffs, pros, cons, and code or evidence snippets. Copy JSON Copy prompt Choice 1 Hand-written fixture Edit demo-state.json directly. Tradeoff: Fastest to start, impossible to keep correct. Pros No tooling Full control of every byte Cons Data shapes drift from the server Generated HTML surfaces cannot be authored by hand // 1100 lines of JSON nobody dares touch Choice 2 Generate through the HTTP API A committed script replays the real create path. Tradeoff: Needs a throwaway server during generation. Pros Shapes correct by construction Regenerates after schema changes Asserts its own invariants Cons One more script to maintain await api(\"POST\", \"/api/canvas/node\", { type: \"mermaid\", ... }) Choice 3 Snapshot a live board Curate by hand in the bro...",
|
|
2953
3370
|
"agentSummary": "Side-by-side options with tradeoffs, pros, cons, and code or evidence snippets."
|
|
2954
3371
|
},
|
|
3372
|
+
"createdBy": {
|
|
3373
|
+
"actor": "agent",
|
|
3374
|
+
"source": "api"
|
|
3375
|
+
},
|
|
3376
|
+
"lastEditedBy": {
|
|
3377
|
+
"actor": "agent",
|
|
3378
|
+
"source": "api"
|
|
3379
|
+
},
|
|
3380
|
+
"contentRevision": 46,
|
|
2955
3381
|
"kind": "html",
|
|
2956
3382
|
"title": "How should the demo board be generated?",
|
|
2957
3383
|
"content": "Side-by-side options with tradeoffs, pros, cons, and code or evidence snippets.",
|
|
@@ -2975,7 +3401,7 @@
|
|
|
2975
3401
|
"collapsed": false,
|
|
2976
3402
|
"pinned": false,
|
|
2977
3403
|
"data": {
|
|
2978
|
-
"html": "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Demo board rebuild</title>\n<style>\n :root { color-scheme: dark light; }\n * { box-sizing: border-box; }\n body { margin: 0; padding: 22px; background: radial-gradient(circle at top left, color-mix(in srgb, var(--color-accent, #4a9eff) 18%, transparent), transparent 34rem), var(--color-bg, #0b0f14); color: var(--color-text, #e8edf2); font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif); }\n .shell { max-width: 1160px; margin: 0 auto; }\n header.hero { display: grid; grid-template-columns: 1fr auto; gap: 16px; align-items: start; margin-bottom: 20px; }\n .kicker { color: var(--color-accent, #4a9eff); text-transform: uppercase; letter-spacing: .16em; font-size: 11px; font-weight: 800; }\n h1 { margin: 5px 0 8px; font-size: clamp(28px, 4vw, 46px); line-height: .95; letter-spacing: -.04em; }\n h2 { margin: 0 0 12px; font-size: 19px; letter-spacing: -.02em; }\n h3 { margin: 0 0 8px; font-size: 15px; }\n p { color: var(--color-text-secondary, #aeb8c2); margin: 0 0 12px; }\n .actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }\n button { border: 1px solid var(--color-border, #263241); background: var(--color-panel, #111821); color: var(--color-text, #e8edf2); border-radius: 999px; padding: 8px 12px; font: inherit; cursor: pointer; }\n button:hover { border-color: var(--color-accent, #4a9eff); transform: translateY(-1px); }\n table { width: 100%; border-collapse: collapse; color: var(--color-text-secondary, #aeb8c2); font-size: 13px; }\n th, td { text-align: left; border-bottom: 1px solid var(--color-border, #263241); padding: 9px 8px; vertical-align: top; }\n th { color: var(--color-text, #e8edf2); font-size: 11px; text-transform: uppercase; letter-spacing: .08em; }\n .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(235px, 1fr)); gap: 14px; }\n .two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; }\n .three { display: grid; grid-template-columns: 1.1fr 1fr .9fr; gap: 14px; align-items: start; }\n .card, details, .panel { border: 1px solid var(--color-border, #263241); background: color-mix(in srgb, var(--color-panel, #111821) 88%, transparent); border-radius: 18px; padding: 16px; box-shadow: 0 18px 50px rgba(0, 0, 0, .18); }\n .card.emphasis { border-color: color-mix(in srgb, var(--color-accent, #4a9eff) 55%, var(--color-border, #263241)); }\n .sticky { position: sticky; top: 14px; }\n .metric { min-height: 104px; display: grid; align-content: space-between; }\n .metric strong { font-size: clamp(24px, 4vw, 42px); letter-spacing: -.04em; }\n .muted { color: var(--color-text-secondary, #aeb8c2); }\n .small { font-size: 12px; color: var(--color-text-muted, #7e8a97); }\n ul { padding-left: 18px; margin: 8px 0 0; color: var(--color-text-secondary, #aeb8c2); }\n li + li { margin-top: 5px; }\n pre { white-space: pre-wrap; overflow: auto; margin: 10px 0 0; padding: 12px; border-radius: 12px; background: #05070a; border: 1px solid color-mix(in srgb, var(--color-border, #263241) 75%, black); color: #d7eadb; font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: 12px; line-height: 1.45; }\n .badge { display: inline-flex; align-items: center; border-radius: 999px; padding: 3px 8px; font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; }\n .badge.ok { color: #062b18; background: var(--color-success, #22c55e); }\n .badge.warn { color: #2b1a02; background: var(--color-warning, #eab308); }\n .badge.danger { color: white; background: var(--color-danger, #ef4444); }\n .badge.info { color: #051a33; background: var(--color-accent, #4a9eff); }\n .timeline { position: relative; display: grid; gap: 12px; }\n .timeline .step { display: grid; grid-template-columns: 34px 1fr; gap: 12px; align-items: start; }\n .dot { width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; background: var(--color-accent, #4a9eff); color: #06111f; font-weight: 900; }\n .flow { display: flex; gap: 10px; align-items: stretch; overflow-x: auto; padding-bottom: 4px; }\n .flow-node { min-width: 180px; border-radius: 16px; border: 1px solid var(--color-border, #263241); padding: 14px; background: var(--color-surface, #17202b); }\n .arrow { align-self: center; color: var(--color-accent, #4a9eff); font-weight: 900; }\n .swatches { display: flex; gap: 6px; flex-wrap: wrap; margin: 8px 0; }\n .swatch { width: 44px; height: 44px; border-radius: 13px; border: 1px solid rgba(255,255,255,.22); }\n textarea, input[type=\"text\"] { width: 100%; border: 1px solid var(--color-border, #263241); border-radius: 14px; padding: 12px; background: var(--color-bg, #0b0f14); color: var(--color-text, #e8edf2); font: inherit; }\n textarea { min-height: 220px; resize: vertical; font-family: var(--font-mono, ui-monospace, monospace); }\n .columns { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; align-items: start; }\n .column { min-height: 220px; border: 1px dashed var(--color-border, #263241); border-radius: 18px; padding: 12px; background: color-mix(in srgb, var(--color-panel, #111821) 70%, transparent); }\n .ticket { cursor: grab; margin: 10px 0; }\n .ticket:active { cursor: grabbing; }\n .slide { min-height: 390px; display: none; align-content: center; }\n .slide.active { display: grid; }\n .slide h2 { font-size: clamp(30px, 5vw, 58px); line-height: .95; }\n .preview { white-space: pre-wrap; min-height: 220px; }\n .figure svg { width: 100%; min-height: 220px; border-radius: 14px; background: color-mix(in srgb, var(--color-bg, #0b0f14) 86%, white); border: 1px solid var(--color-border, #263241); }\n @media (max-width: 760px) { body { padding: 14px; } header.hero, .two, .three { grid-template-columns: 1fr; } .actions { justify-content: flex-start; } .sticky { position: static; } }\n</style>\n</head>\n<body>\n<div class=\"shell\">\n <header class=\"hero\">\n <div><div class=\"kicker\">PMX HTML primitive / plan-timeline</div><h1>Demo board rebuild</h1><p>Implementation plan with milestones, data flow, code checkpoints, and risks.</p></div>\n <div class=\"actions\"><button type=\"button\" data-copy-json>Copy JSON</button><button type=\"button\" data-copy-prompt>Copy prompt</button></div>\n </header>\n <section class=\"two\"><div class=\"panel\"><h2>Milestones</h2><div class=\"timeline\">\n <div class=\"step\"><div class=\"dot\">1</div><div class=\"card\"><h3>Inventory the surfaces <span class=\"badge ok\">done</span></h3><p>Every node type, graph type and primitive kind.</p></div></div>\n <div class=\"step\"><div class=\"dot\">2</div><div class=\"card\"><h3>Write the generator <span class=\"badge ok\">done</span></h3><p>Create everything through the real HTTP API.</p></div></div>\n <div class=\"step\"><div class=\"dot\">3</div><div class=\"card\"><h3>Lay out the bands <span class=\"badge info\">active</span></h3><p>Spatial bands with section labels, generous gutters.</p></div></div>\n <div class=\"step\"><div class=\"dot\">4</div><div class=\"card\"><h3>Rewrite the fixture test <span class=\"badge info\">todo</span></h3><p>Keep the structural invariants, add coverage assertions.</p></div></div>\n <div class=\"step\"><div class=\"dot\">5</div><div class=\"card\"><h3>Visual pass <span class=\"badge info\">todo</span></h3><p>Screenshot each band and fix anything blank or clipped.</p></div></div></div></div>\n <div class=\"panel\"><h2>Data Flow</h2><div class=\"flow\"><div class=\"flow-node\"><h3>generator</h3><p>HTTP create</p><strong>temp server</strong></div><div class=\"arrow\">-></div><div class=\"flow-node\"><h3>temp server</h3><p>export + stable ids</p><strong>demo-state.json</strong></div><div class=\"arrow\">-></div><div class=\"flow-node\"><h3>demo-state.json</h3><p>--demo</p><strong>seedDemoCanvas</strong></div></div><h2 style=\"margin-top:18px\">Risks</h2><div class=\"card\"><strong>Browser auto-fit reflows the board on first open</strong><p>Create every node with strictSize.</p></div><div class=\"card\"><strong>Fixture depends on files or network at render time</strong><p>Inline file text, data-URI image, cached webpage snapshot.</p></div><div class=\"card\"><strong>Server ids churn on every regeneration</strong><p>Rewrite ids to stable demo-* keys during export.</p></div></div></section>\n <section class=\"panel\" style=\"margin-top:14px\"><h2>Code Checkpoints</h2><h3>Regenerate</h3><pre><code>bun run scripts/generate-demo-board.ts</code></pre><h3>Verify</h3><pre><code>bun test tests/unit/demo.test.ts</code></pre></section>\n</div>\n<script type=\"application/json\" id=\"pmx-data\">{\n \"milestones\": [\n {\n \"title\": \"Inventory the surfaces\",\n \"detail\": \"Every node type, graph type and primitive kind.\",\n \"status\": \"done\"\n },\n {\n \"title\": \"Write the generator\",\n \"detail\": \"Create everything through the real HTTP API.\",\n \"status\": \"done\"\n },\n {\n \"title\": \"Lay out the bands\",\n \"detail\": \"Spatial bands with section labels, generous gutters.\",\n \"status\": \"active\"\n },\n {\n \"title\": \"Rewrite the fixture test\",\n \"detail\": \"Keep the structural invariants, add coverage assertions.\",\n \"status\": \"todo\"\n },\n {\n \"title\": \"Visual pass\",\n \"detail\": \"Screenshot each band and fix anything blank or clipped.\",\n \"status\": \"todo\"\n }\n ],\n \"flow\": [\n {\n \"from\": \"generator\",\n \"to\": \"temp server\",\n \"label\": \"HTTP create\"\n },\n {\n \"from\": \"temp server\",\n \"to\": \"demo-state.json\",\n \"label\": \"export + stable ids\"\n },\n {\n \"from\": \"demo-state.json\",\n \"to\": \"seedDemoCanvas\",\n \"label\": \"--demo\"\n }\n ],\n \"risks\": [\n {\n \"risk\": \"Browser auto-fit reflows the board on first open\",\n \"mitigation\": \"Create every node with strictSize.\"\n },\n {\n \"risk\": \"Fixture depends on files or network at render time\",\n \"mitigation\": \"Inline file text, data-URI image, cached webpage snapshot.\"\n },\n {\n \"risk\": \"Server ids churn on every regeneration\",\n \"mitigation\": \"Rewrite ids to stable demo-* keys during export.\"\n }\n ],\n \"snippets\": [\n {\n \"label\": \"Regenerate\",\n \"code\": \"bun run scripts/generate-demo-board.ts\"\n },\n {\n \"label\": \"Verify\",\n \"code\": \"bun test tests/unit/demo.test.ts\"\n }\n ]\n}</script>\n<script>\nconst PMX_DATA = JSON.parse(document.getElementById('pmx-data').textContent);\nfunction fallbackCopy(text) {\n const el = document.createElement('textarea');\n el.value = text;\n el.setAttribute('readonly', '');\n el.style.position = 'fixed';\n el.style.left = '-9999px';\n document.body.appendChild(el);\n el.select();\n document.execCommand('copy');\n el.remove();\n}\nasync function copyText(text) {\n if (navigator.clipboard && window.isSecureContext) await navigator.clipboard.writeText(text);\n else fallbackCopy(text);\n}\ndocument.querySelector('[data-copy-json]')?.addEventListener('click', () => {\n const payload = typeof window.__pmxGetCopyJson === 'function' ? window.__pmxGetCopyJson() : PMX_DATA;\n copyText(JSON.stringify(payload, null, 2));\n});\ndocument.querySelector('[data-copy-prompt]')?.addEventListener('click', () => copyText('Use this PMX Canvas HTML primitive output as context:\\n\\n' + JSON.stringify(PMX_DATA, null, 2)));\n\n</script>\n</body>\n</html>",
|
|
3404
|
+
"html": "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Demo board rebuild</title>\n<style>\n :root { color-scheme: dark light; }\n * { box-sizing: border-box; }\n body { margin: 0; padding: 22px; background: radial-gradient(circle at top left, color-mix(in srgb, var(--color-accent, #4a9eff) 18%, transparent), transparent 34rem), var(--color-bg, #0b0f14); color: var(--color-text, #e8edf2); font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif); }\n .shell { max-width: 1160px; margin: 0 auto; }\n header.hero { display: grid; grid-template-columns: 1fr auto; gap: 16px; align-items: start; margin-bottom: 20px; }\n .kicker { color: var(--color-accent, #4a9eff); text-transform: uppercase; letter-spacing: .16em; font-size: 11px; font-weight: 800; }\n h1 { margin: 5px 0 8px; font-size: clamp(28px, 4vw, 46px); line-height: .95; letter-spacing: -.04em; }\n h2 { margin: 0 0 12px; font-size: 19px; letter-spacing: -.02em; }\n h3 { margin: 0 0 8px; font-size: 15px; }\n p { color: var(--color-text-secondary, #aeb8c2); margin: 0 0 12px; }\n .actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }\n button { border: 1px solid var(--color-border, #263241); background: var(--color-panel, #111821); color: var(--color-text, #e8edf2); border-radius: 999px; padding: 8px 12px; font: inherit; cursor: pointer; }\n button:hover { border-color: var(--color-accent, #4a9eff); transform: translateY(-1px); }\n table { width: 100%; border-collapse: collapse; color: var(--color-text-secondary, #aeb8c2); font-size: 13px; }\n th, td { text-align: left; border-bottom: 1px solid var(--color-border, #263241); padding: 9px 8px; vertical-align: top; }\n th { color: var(--color-text, #e8edf2); font-size: 11px; text-transform: uppercase; letter-spacing: .08em; }\n .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(235px, 1fr)); gap: 14px; }\n .two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; }\n .three { display: grid; grid-template-columns: 1.1fr 1fr .9fr; gap: 14px; align-items: start; }\n .card, details, .panel { border: 1px solid var(--color-border, #263241); background: color-mix(in srgb, var(--color-panel, #111821) 88%, transparent); border-radius: 18px; padding: 16px; box-shadow: 0 18px 50px rgba(0, 0, 0, .18); }\n .card.emphasis { border-color: color-mix(in srgb, var(--color-accent, #4a9eff) 55%, var(--color-border, #263241)); }\n .sticky { position: sticky; top: 14px; }\n .metric { min-height: 104px; display: grid; align-content: space-between; }\n .metric strong { font-size: clamp(24px, 4vw, 42px); letter-spacing: -.04em; }\n .muted { color: var(--color-text-secondary, #aeb8c2); }\n .small { font-size: 12px; color: var(--color-text-muted, #7e8a97); }\n ul { padding-left: 18px; margin: 8px 0 0; color: var(--color-text-secondary, #aeb8c2); }\n li + li { margin-top: 5px; }\n pre { white-space: pre-wrap; overflow: auto; margin: 10px 0 0; padding: 12px; border-radius: 12px; background: #05070a; border: 1px solid color-mix(in srgb, var(--color-border, #263241) 75%, black); color: #d7eadb; font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: 12px; line-height: 1.45; }\n .badge { display: inline-flex; align-items: center; border-radius: 999px; padding: 3px 8px; font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; }\n .badge.ok { color: #062b18; background: var(--color-success, #22c55e); }\n .badge.warn { color: #2b1a02; background: var(--color-warning, #eab308); }\n .badge.danger { color: white; background: var(--color-danger, #ef4444); }\n .badge.info { color: #051a33; background: var(--color-accent, #4a9eff); }\n .timeline { position: relative; display: grid; gap: 12px; }\n .timeline .step { display: grid; grid-template-columns: 34px 1fr; gap: 12px; align-items: start; }\n .dot { width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; background: var(--color-accent, #4a9eff); color: #06111f; font-weight: 900; }\n .flow { display: flex; gap: 10px; align-items: stretch; overflow-x: auto; padding-bottom: 4px; }\n .flow-node { min-width: 180px; border-radius: 16px; border: 1px solid var(--color-border, #263241); padding: 14px; background: var(--color-surface, #17202b); }\n .arrow { align-self: center; color: var(--color-accent, #4a9eff); font-weight: 900; }\n .swatches { display: flex; gap: 6px; flex-wrap: wrap; margin: 8px 0; }\n .swatch { width: 44px; height: 44px; border-radius: 13px; border: 1px solid rgba(255,255,255,.22); }\n textarea, input[type=\"text\"] { width: 100%; border: 1px solid var(--color-border, #263241); border-radius: 14px; padding: 12px; background: var(--color-bg, #0b0f14); color: var(--color-text, #e8edf2); font: inherit; }\n textarea { min-height: 220px; resize: vertical; font-family: var(--font-mono, ui-monospace, monospace); }\n .columns { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; align-items: start; }\n .column { min-height: 220px; border: 1px dashed var(--color-border, #263241); border-radius: 18px; padding: 12px; background: color-mix(in srgb, var(--color-panel, #111821) 70%, transparent); }\n .ticket { cursor: grab; margin: 10px 0; }\n .ticket:active { cursor: grabbing; }\n .slide { min-height: 390px; display: none; align-content: center; }\n .slide.active { display: grid; }\n .slide h2 { font-size: clamp(30px, 5vw, 58px); line-height: .95; }\n .preview { white-space: pre-wrap; min-height: 220px; }\n .figure svg { width: 100%; min-height: 220px; border-radius: 14px; background: color-mix(in srgb, var(--color-bg, #0b0f14) 86%, white); border: 1px solid var(--color-border, #263241); }\n @media (max-width: 760px) { body { padding: 14px; } header.hero, .two, .three { grid-template-columns: 1fr; } .actions { justify-content: flex-start; } .sticky { position: static; } }\n</style>\n</head>\n<body>\n<div class=\"shell\">\n <header class=\"hero\">\n <div><div class=\"kicker\">PMX HTML primitive / plan-timeline</div><h1>Demo board rebuild</h1><p>Implementation plan with milestones, data flow, code checkpoints, and risks.</p></div>\n <div class=\"actions\"><button type=\"button\" data-copy-json>Copy JSON</button><button type=\"button\" data-copy-prompt>Copy prompt</button></div>\n </header>\n <section class=\"two\"><div class=\"panel\"><h2>Milestones</h2><div class=\"timeline\">\n <div class=\"step\"><div class=\"dot\">1</div><div class=\"card\"><h3>Inventory the surfaces <span class=\"badge ok\">done</span></h3><p>Every node type, graph type and primitive kind.</p></div></div>\n <div class=\"step\"><div class=\"dot\">2</div><div class=\"card\"><h3>Write the generator <span class=\"badge ok\">done</span></h3><p>Create everything through the real HTTP API.</p></div></div>\n <div class=\"step\"><div class=\"dot\">3</div><div class=\"card\"><h3>Lay out the bands <span class=\"badge info\">active</span></h3><p>Spatial bands with section labels, generous gutters.</p></div></div>\n <div class=\"step\"><div class=\"dot\">4</div><div class=\"card\"><h3>Rewrite the fixture test <span class=\"badge info\">todo</span></h3><p>Keep the structural invariants, add coverage assertions.</p></div></div>\n <div class=\"step\"><div class=\"dot\">5</div><div class=\"card\"><h3>Visual pass <span class=\"badge info\">todo</span></h3><p>Screenshot each band and fix anything blank or clipped.</p></div></div></div></div><div class=\"panel\"><h2>Data Flow</h2><div class=\"flow\"><div class=\"flow-node\"><h3>generator</h3><p>HTTP create</p><strong>temp server</strong></div><div class=\"arrow\">-></div><div class=\"flow-node\"><h3>temp server</h3><p>export + stable ids</p><strong>demo-state.json</strong></div><div class=\"arrow\">-></div><div class=\"flow-node\"><h3>demo-state.json</h3><p>--demo</p><strong>seedDemoCanvas</strong></div></div><h2 style=\"margin-top:18px\">Risks</h2><div class=\"card\"><strong>Browser auto-fit reflows the board on first open</strong><p>Create every node with strictSize.</p></div><div class=\"card\"><strong>Fixture depends on files or network at render time</strong><p>Inline file text, data-URI image, cached webpage snapshot.</p></div><div class=\"card\"><strong>Server ids churn on every regeneration</strong><p>Rewrite ids to stable demo-* keys during export.</p></div></div></section>\n <section class=\"panel\" style=\"margin-top:14px\"><h2>Code Checkpoints</h2><h3>Regenerate</h3><pre><code>bun run scripts/generate-demo-board.ts</code></pre><h3>Verify</h3><pre><code>bun test tests/unit/demo.test.ts</code></pre></section>\n</div>\n<script type=\"application/json\" id=\"pmx-data\">{\n \"milestones\": [\n {\n \"title\": \"Inventory the surfaces\",\n \"detail\": \"Every node type, graph type and primitive kind.\",\n \"status\": \"done\"\n },\n {\n \"title\": \"Write the generator\",\n \"detail\": \"Create everything through the real HTTP API.\",\n \"status\": \"done\"\n },\n {\n \"title\": \"Lay out the bands\",\n \"detail\": \"Spatial bands with section labels, generous gutters.\",\n \"status\": \"active\"\n },\n {\n \"title\": \"Rewrite the fixture test\",\n \"detail\": \"Keep the structural invariants, add coverage assertions.\",\n \"status\": \"todo\"\n },\n {\n \"title\": \"Visual pass\",\n \"detail\": \"Screenshot each band and fix anything blank or clipped.\",\n \"status\": \"todo\"\n }\n ],\n \"flow\": [\n {\n \"from\": \"generator\",\n \"to\": \"temp server\",\n \"label\": \"HTTP create\"\n },\n {\n \"from\": \"temp server\",\n \"to\": \"demo-state.json\",\n \"label\": \"export + stable ids\"\n },\n {\n \"from\": \"demo-state.json\",\n \"to\": \"seedDemoCanvas\",\n \"label\": \"--demo\"\n }\n ],\n \"risks\": [\n {\n \"risk\": \"Browser auto-fit reflows the board on first open\",\n \"mitigation\": \"Create every node with strictSize.\"\n },\n {\n \"risk\": \"Fixture depends on files or network at render time\",\n \"mitigation\": \"Inline file text, data-URI image, cached webpage snapshot.\"\n },\n {\n \"risk\": \"Server ids churn on every regeneration\",\n \"mitigation\": \"Rewrite ids to stable demo-* keys during export.\"\n }\n ],\n \"snippets\": [\n {\n \"label\": \"Regenerate\",\n \"code\": \"bun run scripts/generate-demo-board.ts\"\n },\n {\n \"label\": \"Verify\",\n \"code\": \"bun test tests/unit/demo.test.ts\"\n }\n ]\n}</script>\n<script>\nconst PMX_DATA = JSON.parse(document.getElementById('pmx-data').textContent);\nfunction fallbackCopy(text) {\n const el = document.createElement('textarea');\n el.value = text;\n el.setAttribute('readonly', '');\n el.style.position = 'fixed';\n el.style.left = '-9999px';\n document.body.appendChild(el);\n el.select();\n document.execCommand('copy');\n el.remove();\n}\nasync function copyText(text) {\n if (navigator.clipboard && window.isSecureContext) await navigator.clipboard.writeText(text);\n else fallbackCopy(text);\n}\ndocument.querySelector('[data-copy-json]')?.addEventListener('click', () => {\n const payload = typeof window.__pmxGetCopyJson === 'function' ? window.__pmxGetCopyJson() : PMX_DATA;\n copyText(JSON.stringify(payload, null, 2));\n});\ndocument.querySelector('[data-copy-prompt]')?.addEventListener('click', () => copyText('Use this PMX Canvas HTML primitive output as context:\\n\\n' + JSON.stringify(PMX_DATA, null, 2)));\n\n</script>\n</body>\n</html>",
|
|
2979
3405
|
"htmlPrimitive": "plan-timeline",
|
|
2980
3406
|
"primitiveData": {
|
|
2981
3407
|
"milestones": [
|
|
@@ -3053,6 +3479,15 @@
|
|
|
3053
3479
|
"contentSummary": "Demo board rebuild PMX HTML primitive / plan-timeline Demo board rebuild Implementation plan with milestones, data flow, code checkpoints, and risks. Copy JSON Copy prompt Milestones 1 Inventory the surfaces done Every node type, graph type and primitive kind. 2 Write the generator done Create everything through the real HTTP API. 3 Lay out the bands active Spatial bands with section labels, generous gutters. 4 Rewrite the fixture test todo Keep the structural invariants, add coverage assertions. 5 Visual pass todo Screenshot each band and fix anything blank or clipped. Data Flow generator HTTP create temp server -> temp server export + stable ids demo-state.json -> demo-state.json --demo seedDemoCanvas Risks Browser auto-fit reflows the board on first open Create every node with strictSize. Fixture depends on files or network at render time Inline file text, data-URI image, cached we...",
|
|
3054
3480
|
"agentSummary": "Implementation plan with milestones, data flow, code checkpoints, and risks."
|
|
3055
3481
|
},
|
|
3482
|
+
"createdBy": {
|
|
3483
|
+
"actor": "agent",
|
|
3484
|
+
"source": "api"
|
|
3485
|
+
},
|
|
3486
|
+
"lastEditedBy": {
|
|
3487
|
+
"actor": "agent",
|
|
3488
|
+
"source": "api"
|
|
3489
|
+
},
|
|
3490
|
+
"contentRevision": 47,
|
|
3056
3491
|
"kind": "html",
|
|
3057
3492
|
"title": "Demo board rebuild",
|
|
3058
3493
|
"content": "Implementation plan with milestones, data flow, code checkpoints, and risks.",
|
|
@@ -3123,6 +3558,15 @@
|
|
|
3123
3558
|
"node-sizes"
|
|
3124
3559
|
]
|
|
3125
3560
|
},
|
|
3561
|
+
"createdBy": {
|
|
3562
|
+
"actor": "agent",
|
|
3563
|
+
"source": "api"
|
|
3564
|
+
},
|
|
3565
|
+
"lastEditedBy": {
|
|
3566
|
+
"actor": "agent",
|
|
3567
|
+
"source": "api"
|
|
3568
|
+
},
|
|
3569
|
+
"contentRevision": 48,
|
|
3126
3570
|
"kind": "html",
|
|
3127
3571
|
"title": "Review — auto-fit floor",
|
|
3128
3572
|
"content": "Annotated PR/code review sheet with severity-colored findings and diff excerpts.",
|
|
@@ -3202,6 +3646,15 @@
|
|
|
3202
3646
|
"contentSummary": "PR — replace the demo board PMX HTML primitive / pr-writeup PR — replace the demo board Reviewer-ready PR narrative with motivation, before/after, file tour, test plan, and rollout notes. Copy JSON Copy prompt Summary Replaces the OKR demo board with a generated catalogue of every public canvas surface. The old board used three node types and was dominated by group frames, so it showed almost nothing the product does. Before 19 nodes 3 node types 3 large group frames hand-edited JSON After 69 nodes across 8 labelled bands all 15 public node types all 12 graph types and all 21 HTML primitives generated by a committed script File Tour scripts/generate-demo-board.ts The generator, and the only place the board is authored. Focus: Band layout and offline-safety rules. src/server/demo-state.json Generated output — never hand-edited. Focus: Stable demo-* ids. tests/unit/demo.test.ts Structur...",
|
|
3203
3647
|
"agentSummary": "Reviewer-ready PR narrative with motivation, before/after, file tour, test plan, and rollout notes."
|
|
3204
3648
|
},
|
|
3649
|
+
"createdBy": {
|
|
3650
|
+
"actor": "agent",
|
|
3651
|
+
"source": "api"
|
|
3652
|
+
},
|
|
3653
|
+
"lastEditedBy": {
|
|
3654
|
+
"actor": "agent",
|
|
3655
|
+
"source": "api"
|
|
3656
|
+
},
|
|
3657
|
+
"contentRevision": 49,
|
|
3205
3658
|
"kind": "html",
|
|
3206
3659
|
"title": "PR — replace the demo board",
|
|
3207
3660
|
"content": "Reviewer-ready PR narrative with motivation, before/after, file tour, test plan, and rollout notes.",
|
|
@@ -3305,6 +3758,15 @@
|
|
|
3305
3758
|
"contentSummary": "Canvas server map PMX HTML primitive / system-map Canvas server map Module or architecture map with boxes, relationships, entry points, and hot paths. Copy JSON Copy prompt Entry Points src/mcp/server.ts src/server/server.ts src/cli/index.ts entry MCP server stdio transport, 22 tools and 14 resources. entry HTTP API REST + SSE on Bun.serve. core Operation registry One definition drives HTTP, MCP and the SDK. core CanvasStateManager Singleton source of truth; every mutation goes through it. store SQLite .pmx-canvas/canvas.db — nodes, pins, snapshots, blobs. view Preact client Renders layout; state survives refresh. Relationships mcp tool call ops + http route ops + ops mutate state + state debounced save db + state SSE client",
|
|
3306
3759
|
"agentSummary": "Module or architecture map with boxes, relationships, entry points, and hot paths."
|
|
3307
3760
|
},
|
|
3761
|
+
"createdBy": {
|
|
3762
|
+
"actor": "agent",
|
|
3763
|
+
"source": "api"
|
|
3764
|
+
},
|
|
3765
|
+
"lastEditedBy": {
|
|
3766
|
+
"actor": "agent",
|
|
3767
|
+
"source": "api"
|
|
3768
|
+
},
|
|
3769
|
+
"contentRevision": 50,
|
|
3308
3770
|
"kind": "html",
|
|
3309
3771
|
"title": "Canvas server map",
|
|
3310
3772
|
"content": "Module or architecture map with boxes, relationships, entry points, and hot paths.",
|
|
@@ -3425,6 +3887,15 @@
|
|
|
3425
3887
|
"node-sizes"
|
|
3426
3888
|
]
|
|
3427
3889
|
},
|
|
3890
|
+
"createdBy": {
|
|
3891
|
+
"actor": "agent",
|
|
3892
|
+
"source": "api"
|
|
3893
|
+
},
|
|
3894
|
+
"lastEditedBy": {
|
|
3895
|
+
"actor": "agent",
|
|
3896
|
+
"source": "api"
|
|
3897
|
+
},
|
|
3898
|
+
"contentRevision": 51,
|
|
3428
3899
|
"kind": "html",
|
|
3429
3900
|
"title": "Walkthrough — creating a node",
|
|
3430
3901
|
"content": "Guided code-path map with modules, ordered file steps, snippets, key files, and gotchas.",
|
|
@@ -3519,6 +3990,15 @@
|
|
|
3519
3990
|
"contentSummary": "Board typography directions PMX HTML primitive / design-sheet Board typography directions Visual design directions, tokens, swatches, type samples, and rationale. Copy JSON Copy prompt Instrument dense, technical, high contrast Reads like a console. Good for status, trace and ledger nodes where density beats warmth. Editorial calm, generous leading Better for long markdown and explainers; loses signal on dashboards. Blueprint cool, structural, muted Diagram-first. Chart ink stays readable against the panel at low zoom. Tokens --c-bg #0d1220 --c-panel #131a2b --c-line #243049 --c-accent #4a9eff --c-ok #22c55e --c-warn #eab308",
|
|
3520
3991
|
"agentSummary": "Visual design directions, tokens, swatches, type samples, and rationale."
|
|
3521
3992
|
},
|
|
3993
|
+
"createdBy": {
|
|
3994
|
+
"actor": "agent",
|
|
3995
|
+
"source": "api"
|
|
3996
|
+
},
|
|
3997
|
+
"lastEditedBy": {
|
|
3998
|
+
"actor": "agent",
|
|
3999
|
+
"source": "api"
|
|
4000
|
+
},
|
|
4001
|
+
"contentRevision": 52,
|
|
3522
4002
|
"kind": "html",
|
|
3523
4003
|
"title": "Board typography directions",
|
|
3524
4004
|
"content": "Visual design directions, tokens, swatches, type samples, and rationale.",
|
|
@@ -3593,6 +4073,15 @@
|
|
|
3593
4073
|
"node-sizes"
|
|
3594
4074
|
]
|
|
3595
4075
|
},
|
|
4076
|
+
"createdBy": {
|
|
4077
|
+
"actor": "agent",
|
|
4078
|
+
"source": "api"
|
|
4079
|
+
},
|
|
4080
|
+
"lastEditedBy": {
|
|
4081
|
+
"actor": "agent",
|
|
4082
|
+
"source": "api"
|
|
4083
|
+
},
|
|
4084
|
+
"contentRevision": 53,
|
|
3596
4085
|
"kind": "html",
|
|
3597
4086
|
"title": "Node titlebar states",
|
|
3598
4087
|
"content": "Contact sheet for component variants, states, sizes, and accessibility notes.",
|
|
@@ -3682,6 +4171,15 @@
|
|
|
3682
4171
|
"contentSummary": "Node expand transition PMX HTML primitive / interaction-prototype Node expand transition Throwaway interaction or motion sandbox with a live stage, controls, annotations, and copyable config. Copy JSON Copy prompt Stage Tune the expand-to-overlay transition before wiring it into CanvasNode. Rest Node sits in the band at canvas scale. -> Lift Node scales toward the overlay frame; siblings dim. -> Overlay Full-size surface with the same renderer, expanded props. Select a screen to inspect it. Controls duration Duration: 240 ms overshoot Overshoot: 6 % dim Backdrop dim: 62 % Notes Same renderer The overlay must not fork the node component, or surfaces drift. No viewport jump Expanding never moves the underlying canvas. Questions Does overshoot help or just feel noisy at 0.4 zoom? Should the backdrop dim scale with node count? transition: transform 240ms cubic-bezier(0.2, 0.8, 0.2, 1);",
|
|
3683
4172
|
"agentSummary": "Throwaway interaction or motion sandbox with a live stage, controls, annotations, and copyable config."
|
|
3684
4173
|
},
|
|
4174
|
+
"createdBy": {
|
|
4175
|
+
"actor": "agent",
|
|
4176
|
+
"source": "api"
|
|
4177
|
+
},
|
|
4178
|
+
"lastEditedBy": {
|
|
4179
|
+
"actor": "agent",
|
|
4180
|
+
"source": "api"
|
|
4181
|
+
},
|
|
4182
|
+
"contentRevision": 54,
|
|
3685
4183
|
"kind": "html",
|
|
3686
4184
|
"title": "Node expand transition",
|
|
3687
4185
|
"content": "Throwaway interaction or motion sandbox with a live stage, controls, annotations, and copyable config.",
|
|
@@ -3759,6 +4257,15 @@
|
|
|
3759
4257
|
"contentSummary": "Release pipeline PMX HTML primitive / flowchart Release pipeline Clickable flowchart for pipelines, user journeys, process diagrams, and failure paths. Copy JSON Copy prompt Pre-flight ok 61s -> Build ok 44s -> Version bump ok 8s -> Tag and publish active — -> Smoke todo — Pre-flight typecheck, lint, unit and client suites. Failure Paths Smoke: missing file in package Add it to the files array and publish a patch. Tag and publish: registry 403 Re-auth, then republish the same tag.",
|
|
3760
4258
|
"agentSummary": "Clickable flowchart for pipelines, user journeys, process diagrams, and failure paths."
|
|
3761
4259
|
},
|
|
4260
|
+
"createdBy": {
|
|
4261
|
+
"actor": "agent",
|
|
4262
|
+
"source": "api"
|
|
4263
|
+
},
|
|
4264
|
+
"lastEditedBy": {
|
|
4265
|
+
"actor": "agent",
|
|
4266
|
+
"source": "api"
|
|
4267
|
+
},
|
|
4268
|
+
"contentRevision": 55,
|
|
3762
4269
|
"kind": "html",
|
|
3763
4270
|
"title": "Release pipeline",
|
|
3764
4271
|
"content": "Clickable flowchart for pipelines, user journeys, process diagrams, and failure paths.",
|
|
@@ -3782,7 +4289,7 @@
|
|
|
3782
4289
|
"collapsed": false,
|
|
3783
4290
|
"pinned": false,
|
|
3784
4291
|
"data": {
|
|
3785
|
-
"html": "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Why a spatial canvas</title>\n<style>\n :root { color-scheme: dark light; }\n * { box-sizing: border-box; }\n body { margin: 0; padding: 22px; background: radial-gradient(circle at top left, color-mix(in srgb, var(--color-accent, #4a9eff) 18%, transparent), transparent 34rem), var(--color-bg, #0b0f14); color: var(--color-text, #e8edf2); font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif); }\n .shell { max-width: 1160px; margin: 0 auto; }\n header.hero { display: grid; grid-template-columns: 1fr auto; gap: 16px; align-items: start; margin-bottom: 20px; }\n .kicker { color: var(--color-accent, #4a9eff); text-transform: uppercase; letter-spacing: .16em; font-size: 11px; font-weight: 800; }\n h1 { margin: 5px 0 8px; font-size: clamp(28px, 4vw, 46px); line-height: .95; letter-spacing: -.04em; }\n h2 { margin: 0 0 12px; font-size: 19px; letter-spacing: -.02em; }\n h3 { margin: 0 0 8px; font-size: 15px; }\n p { color: var(--color-text-secondary, #aeb8c2); margin: 0 0 12px; }\n .actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }\n button { border: 1px solid var(--color-border, #263241); background: var(--color-panel, #111821); color: var(--color-text, #e8edf2); border-radius: 999px; padding: 8px 12px; font: inherit; cursor: pointer; }\n button:hover { border-color: var(--color-accent, #4a9eff); transform: translateY(-1px); }\n table { width: 100%; border-collapse: collapse; color: var(--color-text-secondary, #aeb8c2); font-size: 13px; }\n th, td { text-align: left; border-bottom: 1px solid var(--color-border, #263241); padding: 9px 8px; vertical-align: top; }\n th { color: var(--color-text, #e8edf2); font-size: 11px; text-transform: uppercase; letter-spacing: .08em; }\n .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(235px, 1fr)); gap: 14px; }\n .two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; }\n .three { display: grid; grid-template-columns: 1.1fr 1fr .9fr; gap: 14px; align-items: start; }\n .card, details, .panel { border: 1px solid var(--color-border, #263241); background: color-mix(in srgb, var(--color-panel, #111821) 88%, transparent); border-radius: 18px; padding: 16px; box-shadow: 0 18px 50px rgba(0, 0, 0, .18); }\n .card.emphasis { border-color: color-mix(in srgb, var(--color-accent, #4a9eff) 55%, var(--color-border, #263241)); }\n .sticky { position: sticky; top: 14px; }\n .metric { min-height: 104px; display: grid; align-content: space-between; }\n .metric strong { font-size: clamp(24px, 4vw, 42px); letter-spacing: -.04em; }\n .muted { color: var(--color-text-secondary, #aeb8c2); }\n .small { font-size: 12px; color: var(--color-text-muted, #7e8a97); }\n ul { padding-left: 18px; margin: 8px 0 0; color: var(--color-text-secondary, #aeb8c2); }\n li + li { margin-top: 5px; }\n pre { white-space: pre-wrap; overflow: auto; margin: 10px 0 0; padding: 12px; border-radius: 12px; background: #05070a; border: 1px solid color-mix(in srgb, var(--color-border, #263241) 75%, black); color: #d7eadb; font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: 12px; line-height: 1.45; }\n .badge { display: inline-flex; align-items: center; border-radius: 999px; padding: 3px 8px; font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; }\n .badge.ok { color: #062b18; background: var(--color-success, #22c55e); }\n .badge.warn { color: #2b1a02; background: var(--color-warning, #eab308); }\n .badge.danger { color: white; background: var(--color-danger, #ef4444); }\n .badge.info { color: #051a33; background: var(--color-accent, #4a9eff); }\n .timeline { position: relative; display: grid; gap: 12px; }\n .timeline .step { display: grid; grid-template-columns: 34px 1fr; gap: 12px; align-items: start; }\n .dot { width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; background: var(--color-accent, #4a9eff); color: #06111f; font-weight: 900; }\n .flow { display: flex; gap: 10px; align-items: stretch; overflow-x: auto; padding-bottom: 4px; }\n .flow-node { min-width: 180px; border-radius: 16px; border: 1px solid var(--color-border, #263241); padding: 14px; background: var(--color-surface, #17202b); }\n .arrow { align-self: center; color: var(--color-accent, #4a9eff); font-weight: 900; }\n .swatches { display: flex; gap: 6px; flex-wrap: wrap; margin: 8px 0; }\n .swatch { width: 44px; height: 44px; border-radius: 13px; border: 1px solid rgba(255,255,255,.22); }\n textarea, input[type=\"text\"] { width: 100%; border: 1px solid var(--color-border, #263241); border-radius: 14px; padding: 12px; background: var(--color-bg, #0b0f14); color: var(--color-text, #e8edf2); font: inherit; }\n textarea { min-height: 220px; resize: vertical; font-family: var(--font-mono, ui-monospace, monospace); }\n .columns { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; align-items: start; }\n .column { min-height: 220px; border: 1px dashed var(--color-border, #263241); border-radius: 18px; padding: 12px; background: color-mix(in srgb, var(--color-panel, #111821) 70%, transparent); }\n .ticket { cursor: grab; margin: 10px 0; }\n .ticket:active { cursor: grabbing; }\n .slide { min-height: 390px; display: none; align-content: center; }\n .slide.active { display: grid; }\n .slide h2 { font-size: clamp(30px, 5vw, 58px); line-height: .95; }\n .preview { white-space: pre-wrap; min-height: 220px; }\n .figure svg { width: 100%; min-height: 220px; border-radius: 14px; background: color-mix(in srgb, var(--color-bg, #0b0f14) 86%, white); border: 1px solid var(--color-border, #263241); }\n @media (max-width: 760px) { body { padding: 14px; } header.hero, .two, .three { grid-template-columns: 1fr; } .actions { justify-content: flex-start; } .sticky { position: static; } }\n</style>\n</head>\n<body>\n<div class=\"shell\">\n <header class=\"hero\">\n <div><div class=\"kicker\">PMX HTML primitive / deck</div><h1>Why a spatial canvas</h1><p>Arrow-key HTML deck with sections, speaker notes, and copyable JSON payload.</p></div>\n <div class=\"actions\"><button type=\"button\" data-copy-json>Copy JSON</button><button type=\"button\" data-copy-prompt>Copy prompt</button></div>\n </header>\n <section class=\"panel\"><div class=\"small\"><span id=\"slide-count\">1</span> / 3 - use left/right arrows</div><article class=\"slide active\" data-slide=\"0\"><div><div class=\"kicker\">01</div><h2>Chat is a queue, work is a graph</h2><p></p><ul><li>A transcript only remembers order</li><li>Real work has neighbours, not turns</li><li>Space is free context</li></ul><p class=\"small\">Open on the problem, not the product.</p></div></article><article class=\"slide \" data-slide=\"1\"><div><div class=\"kicker\">02</div><h2>The canvas is the agent’s working memory</h2><p></p><ul><li>Nodes persist across sessions</li><li>Position carries meaning</li><li>The agent can read its own board back</li></ul><p class=\"small\"></p></div></article><article class=\"slide \" data-slide=\"2\"><div><div class=\"kicker\">03</div><h2>Pinning is the steering wheel</h2><p></p><ul><li>Human pins what matters</li><li>Agent reads canvas://pinned-context</li><li>No prompt engineering required</li></ul><p class=\"small\">Demo the pin loop live if there is time.</p></div></article></section>\n</div>\n<script type=\"application/json\" id=\"pmx-data\">{\n \"slides\": [\n {\n \"title\": \"Chat is a queue, work is a graph\",\n \"kicker\": \"01\",\n \"bullets\": [\n \"A transcript only remembers order\",\n \"Real work has neighbours, not turns\",\n \"Space is free context\"\n ],\n \"note\": \"Open on the problem, not the product.\"\n },\n {\n \"title\": \"The canvas is the agent’s working memory\",\n \"kicker\": \"02\",\n \"bullets\": [\n \"Nodes persist across sessions\",\n \"Position carries meaning\",\n \"The agent can read its own board back\"\n ]\n },\n {\n \"title\": \"Pinning is the steering wheel\",\n \"kicker\": \"03\",\n \"bullets\": [\n \"Human pins what matters\",\n \"Agent reads canvas://pinned-context\",\n \"No prompt engineering required\"\n ],\n \"note\": \"Demo the pin loop live if there is time.\"\n }\n ],\n \"presentation\": true,\n \"slideCount\": 3,\n \"slideTitles\": [\n \"Chat is a queue, work is a graph\",\n \"The canvas is the agent’s working memory\",\n \"Pinning is the steering wheel\"\n ],\n \"speakerNotes\": [\n \"Open on the problem, not the product.\",\n \"Demo the pin loop live if there is time.\"\n ]\n}</script>\n<script>\nconst PMX_DATA = JSON.parse(document.getElementById('pmx-data').textContent);\nfunction fallbackCopy(text) {\n const el = document.createElement('textarea');\n el.value = text;\n el.setAttribute('readonly', '');\n el.style.position = 'fixed';\n el.style.left = '-9999px';\n document.body.appendChild(el);\n el.select();\n document.execCommand('copy');\n el.remove();\n}\nasync function copyText(text) {\n if (navigator.clipboard && window.isSecureContext) await navigator.clipboard.writeText(text);\n else fallbackCopy(text);\n}\ndocument.querySelector('[data-copy-json]')?.addEventListener('click', () => {\n const payload = typeof window.__pmxGetCopyJson === 'function' ? window.__pmxGetCopyJson() : PMX_DATA;\n copyText(JSON.stringify(payload, null, 2));\n});\ndocument.querySelector('[data-copy-prompt]')?.addEventListener('click', () => copyText('Use this PMX Canvas HTML primitive output as context:\\n\\n' + JSON.stringify(PMX_DATA, null, 2)));\n\nlet currentSlide = 0;\nconst slides = Array.from(document.querySelectorAll('[data-slide]'));\nfunction showSlide(index) {\n currentSlide = Math.max(0, Math.min(slides.length - 1, index));\n slides.forEach((slide, i) => slide.classList.toggle('active', i === currentSlide));\n document.getElementById('slide-count').textContent = String(currentSlide + 1);\n}\ndocument.addEventListener('keydown', (event) => {\n if (event.key === 'ArrowRight' || event.key === 'PageDown' || event.key === ' ') showSlide(currentSlide + 1);\n if (event.key === 'ArrowLeft' || event.key === 'PageUp') showSlide(currentSlide - 1);\n});\n</script>\n</body>\n</html>",
|
|
4292
|
+
"html": "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Why a spatial canvas</title>\n<style>\n :root { color-scheme: dark light; }\n * { box-sizing: border-box; }\n body { margin: 0; padding: 22px; background: radial-gradient(circle at top left, color-mix(in srgb, var(--color-accent, #4a9eff) 18%, transparent), transparent 34rem), var(--color-bg, #0b0f14); color: var(--color-text, #e8edf2); font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif); }\n .shell { max-width: 1160px; margin: 0 auto; }\n header.hero { display: grid; grid-template-columns: 1fr auto; gap: 16px; align-items: start; margin-bottom: 20px; }\n .kicker { color: var(--color-accent, #4a9eff); text-transform: uppercase; letter-spacing: .16em; font-size: 11px; font-weight: 800; }\n h1 { margin: 5px 0 8px; font-size: clamp(28px, 4vw, 46px); line-height: .95; letter-spacing: -.04em; }\n h2 { margin: 0 0 12px; font-size: 19px; letter-spacing: -.02em; }\n h3 { margin: 0 0 8px; font-size: 15px; }\n p { color: var(--color-text-secondary, #aeb8c2); margin: 0 0 12px; }\n .actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }\n button { border: 1px solid var(--color-border, #263241); background: var(--color-panel, #111821); color: var(--color-text, #e8edf2); border-radius: 999px; padding: 8px 12px; font: inherit; cursor: pointer; }\n button:hover { border-color: var(--color-accent, #4a9eff); transform: translateY(-1px); }\n table { width: 100%; border-collapse: collapse; color: var(--color-text-secondary, #aeb8c2); font-size: 13px; }\n th, td { text-align: left; border-bottom: 1px solid var(--color-border, #263241); padding: 9px 8px; vertical-align: top; }\n th { color: var(--color-text, #e8edf2); font-size: 11px; text-transform: uppercase; letter-spacing: .08em; }\n .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(235px, 1fr)); gap: 14px; }\n .two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; }\n .three { display: grid; grid-template-columns: 1.1fr 1fr .9fr; gap: 14px; align-items: start; }\n .card, details, .panel { border: 1px solid var(--color-border, #263241); background: color-mix(in srgb, var(--color-panel, #111821) 88%, transparent); border-radius: 18px; padding: 16px; box-shadow: 0 18px 50px rgba(0, 0, 0, .18); }\n .card.emphasis { border-color: color-mix(in srgb, var(--color-accent, #4a9eff) 55%, var(--color-border, #263241)); }\n .sticky { position: sticky; top: 14px; }\n .metric { min-height: 104px; display: grid; align-content: space-between; }\n .metric strong { font-size: clamp(24px, 4vw, 42px); letter-spacing: -.04em; }\n .muted { color: var(--color-text-secondary, #aeb8c2); }\n .small { font-size: 12px; color: var(--color-text-muted, #7e8a97); }\n ul { padding-left: 18px; margin: 8px 0 0; color: var(--color-text-secondary, #aeb8c2); }\n li + li { margin-top: 5px; }\n pre { white-space: pre-wrap; overflow: auto; margin: 10px 0 0; padding: 12px; border-radius: 12px; background: #05070a; border: 1px solid color-mix(in srgb, var(--color-border, #263241) 75%, black); color: #d7eadb; font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: 12px; line-height: 1.45; }\n .badge { display: inline-flex; align-items: center; border-radius: 999px; padding: 3px 8px; font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; }\n .badge.ok { color: #062b18; background: var(--color-success, #22c55e); }\n .badge.warn { color: #2b1a02; background: var(--color-warning, #eab308); }\n .badge.danger { color: white; background: var(--color-danger, #ef4444); }\n .badge.info { color: #051a33; background: var(--color-accent, #4a9eff); }\n .timeline { position: relative; display: grid; gap: 12px; }\n .timeline .step { display: grid; grid-template-columns: 34px 1fr; gap: 12px; align-items: start; }\n .dot { width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; background: var(--color-accent, #4a9eff); color: #06111f; font-weight: 900; }\n .flow { display: flex; gap: 10px; align-items: stretch; overflow-x: auto; padding-bottom: 4px; }\n .flow-node { min-width: 180px; border-radius: 16px; border: 1px solid var(--color-border, #263241); padding: 14px; background: var(--color-surface, #17202b); }\n .arrow { align-self: center; color: var(--color-accent, #4a9eff); font-weight: 900; }\n .swatches { display: flex; gap: 6px; flex-wrap: wrap; margin: 8px 0; }\n .swatch { width: 44px; height: 44px; border-radius: 13px; border: 1px solid rgba(255,255,255,.22); }\n textarea, input[type=\"text\"] { width: 100%; border: 1px solid var(--color-border, #263241); border-radius: 14px; padding: 12px; background: var(--color-bg, #0b0f14); color: var(--color-text, #e8edf2); font: inherit; }\n textarea { min-height: 220px; resize: vertical; font-family: var(--font-mono, ui-monospace, monospace); }\n .columns { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; align-items: start; }\n .column { min-height: 220px; border: 1px dashed var(--color-border, #263241); border-radius: 18px; padding: 12px; background: color-mix(in srgb, var(--color-panel, #111821) 70%, transparent); }\n .ticket { cursor: grab; margin: 10px 0; }\n .ticket:active { cursor: grabbing; }\n .slide { min-height: 390px; display: none; align-content: center; }\n .slide.active { display: grid; }\n .slide h2 { font-size: clamp(30px, 5vw, 58px); line-height: .95; }\n .preview { white-space: pre-wrap; min-height: 220px; }\n .figure svg { width: 100%; min-height: 220px; border-radius: 14px; background: color-mix(in srgb, var(--color-bg, #0b0f14) 86%, white); border: 1px solid var(--color-border, #263241); }\n @media (max-width: 760px) { body { padding: 14px; } header.hero, .two, .three { grid-template-columns: 1fr; } .actions { justify-content: flex-start; } .sticky { position: static; } }\n</style>\n</head>\n<body>\n<div class=\"shell\">\n <header class=\"hero\">\n <div><div class=\"kicker\">PMX HTML primitive / deck</div><h1>Why a spatial canvas</h1><p>Arrow-key HTML deck with sections, speaker notes, and copyable JSON payload.</p></div>\n <div class=\"actions\"><button type=\"button\" data-copy-json>Copy JSON</button><button type=\"button\" data-copy-prompt>Copy prompt</button></div>\n </header>\n <section class=\"panel\"><div class=\"small\"><span id=\"slide-count\">1</span> / 3 - use left/right arrows</div><article class=\"slide active\" data-slide=\"0\"><div><div class=\"kicker\">01</div><h2>Chat is a queue, work is a graph</h2><p></p><ul><li>A transcript only remembers order</li><li>Real work has neighbours, not turns</li><li>Space is free context</li></ul><p class=\"small\">Open on the problem, not the product.</p></div></article><article class=\"slide \" data-slide=\"1\"><div><div class=\"kicker\">02</div><h2>The canvas is the agent’s working memory</h2><p></p><ul><li>Nodes persist across sessions</li><li>Position carries meaning</li><li>The agent can read its own board back</li></ul><p class=\"small\"></p></div></article><article class=\"slide \" data-slide=\"2\"><div><div class=\"kicker\">03</div><h2>Pinning is the steering wheel</h2><p></p><ul><li>Human pins what matters</li><li>Agent reads canvas://pinned-context</li><li>No prompt engineering required</li></ul><p class=\"small\">Demo the pin loop live if there is time.</p></div></article></section>\n</div>\n<script type=\"application/json\" id=\"pmx-data\">{\n \"slides\": [\n {\n \"title\": \"Chat is a queue, work is a graph\",\n \"kicker\": \"01\",\n \"bullets\": [\n \"A transcript only remembers order\",\n \"Real work has neighbours, not turns\",\n \"Space is free context\"\n ],\n \"note\": \"Open on the problem, not the product.\"\n },\n {\n \"title\": \"The canvas is the agent’s working memory\",\n \"kicker\": \"02\",\n \"bullets\": [\n \"Nodes persist across sessions\",\n \"Position carries meaning\",\n \"The agent can read its own board back\"\n ]\n },\n {\n \"title\": \"Pinning is the steering wheel\",\n \"kicker\": \"03\",\n \"bullets\": [\n \"Human pins what matters\",\n \"Agent reads canvas://pinned-context\",\n \"No prompt engineering required\"\n ],\n \"note\": \"Demo the pin loop live if there is time.\"\n }\n ],\n \"presentation\": true,\n \"slideCount\": 3,\n \"slideTitles\": [\n \"Chat is a queue, work is a graph\",\n \"The canvas is the agent’s working memory\",\n \"Pinning is the steering wheel\"\n ],\n \"speakerNotes\": [\n \"Open on the problem, not the product.\",\n \"Demo the pin loop live if there is time.\"\n ]\n}</script>\n<script>\nconst PMX_DATA = JSON.parse(document.getElementById('pmx-data').textContent);\nfunction fallbackCopy(text) {\n const el = document.createElement('textarea');\n el.value = text;\n el.setAttribute('readonly', '');\n el.style.position = 'fixed';\n el.style.left = '-9999px';\n document.body.appendChild(el);\n el.select();\n document.execCommand('copy');\n el.remove();\n}\nasync function copyText(text) {\n if (navigator.clipboard && window.isSecureContext) await navigator.clipboard.writeText(text);\n else fallbackCopy(text);\n}\ndocument.querySelector('[data-copy-json]')?.addEventListener('click', () => {\n const payload = typeof window.__pmxGetCopyJson === 'function' ? window.__pmxGetCopyJson() : PMX_DATA;\n copyText(JSON.stringify(payload, null, 2));\n});\ndocument.querySelector('[data-copy-prompt]')?.addEventListener('click', () => copyText('Use this PMX Canvas HTML primitive output as context:\\n\\n' + JSON.stringify(PMX_DATA, null, 2)));\n\nlet currentSlide = 0;\nconst slides = Array.from(document.querySelectorAll('[data-slide]'));\nfunction showSlide(index) {\n if (!slides.length) return;\n currentSlide = Math.max(0, Math.min(slides.length - 1, index));\n slides.forEach((slide, i) => slide.classList.toggle('active', i === currentSlide));\n document.getElementById('slide-count').textContent = String(currentSlide + 1);\n}\ndocument.addEventListener('keydown', (event) => {\n if (event.key === 'ArrowRight' || event.key === 'PageDown' || event.key === ' ') showSlide(currentSlide + 1);\n if (event.key === 'ArrowLeft' || event.key === 'PageUp') showSlide(currentSlide - 1);\n});\n</script>\n</body>\n</html>",
|
|
3786
4293
|
"htmlPrimitive": "deck",
|
|
3787
4294
|
"primitiveData": {
|
|
3788
4295
|
"slides": [
|
|
@@ -3845,6 +4352,15 @@
|
|
|
3845
4352
|
"contentSummary": "Why a spatial canvas PMX HTML primitive / deck Why a spatial canvas Arrow-key HTML deck with sections, speaker notes, and copyable JSON payload. Copy JSON Copy prompt 1 / 3 - use left/right arrows 01 Chat is a queue, work is a graph A transcript only remembers order Real work has neighbours, not turns Space is free context Open on the problem, not the product. 02 The canvas is the agent’s working memory Nodes persist across sessions Position carries meaning The agent can read its own board back 03 Pinning is the steering wheel Human pins what matters Agent reads canvas://pinned-context No prompt engineering required Demo the pin loop live if there is time.",
|
|
3846
4353
|
"agentSummary": "Arrow-key HTML deck with sections, speaker notes, and copyable JSON payload. Slides: Chat is a queue, work is a graph, The canvas is the agent’s working memory, Pinning is the steering wheel."
|
|
3847
4354
|
},
|
|
4355
|
+
"createdBy": {
|
|
4356
|
+
"actor": "agent",
|
|
4357
|
+
"source": "api"
|
|
4358
|
+
},
|
|
4359
|
+
"lastEditedBy": {
|
|
4360
|
+
"actor": "agent",
|
|
4361
|
+
"source": "api"
|
|
4362
|
+
},
|
|
4363
|
+
"contentRevision": 56,
|
|
3848
4364
|
"kind": "html",
|
|
3849
4365
|
"title": "Why a spatial canvas",
|
|
3850
4366
|
"content": "Arrow-key HTML deck with sections, speaker notes, and copyable JSON payload. Slides: Chat is a queue, work is a graph, The canvas is the agent’s working memory, Pinning is the steering wheel.",
|
|
@@ -3868,7 +4384,7 @@
|
|
|
3868
4384
|
"collapsed": false,
|
|
3869
4385
|
"pinned": false,
|
|
3870
4386
|
"data": {
|
|
3871
|
-
"html": "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>PMX Canvas 0.4.7 briefing</title>\n<style>\n :root { color-scheme: dark; --deck-accent: #4BBCFF; --deck-bg: #081524; --deck-panel: #0f1d31; --deck-surface: #10213a; --deck-border: #1b2c44; --deck-text: #e6eef7; --deck-text-secondary: #c7d3ea; --deck-text-muted: #8ea3bd; }\n * { box-sizing: border-box; }\n html, body { width: 100%; height: 100%; overflow: hidden; }\n body { margin: 0; padding: 0; background: var(--deck-bg); color: var(--deck-text); font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif); }\n .deck { height: 100vh; min-height: 0; display: grid; grid-template-rows: auto minmax(0, 1fr) auto; background: radial-gradient(circle at 10% 10%, color-mix(in srgb, var(--deck-accent) 32%, transparent), transparent 28rem), linear-gradient(135deg, color-mix(in srgb, var(--deck-panel) 88%, black), var(--deck-bg)); }\n .topbar, .bottombar { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: clamp(12px, 2.5vmin, 24px) clamp(18px, 4vw, 48px); color: var(--deck-text-secondary); }\n .brand { display: grid; gap: 2px; }\n .brand p { margin: 0; }\n .eyebrow { color: var(--deck-accent); font-size: 11px; font-weight: 900; letter-spacing: .18em; text-transform: uppercase; }\n .title { max-width: 70vw; overflow: hidden; color: var(--deck-text); font-size: clamp(16px, 2vw, 24px); font-weight: 850; letter-spacing: -.03em; text-overflow: ellipsis; white-space: nowrap; }\n .controls { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }\n button { border: 1px solid color-mix(in srgb, var(--deck-border) 82%, white); background: color-mix(in srgb, var(--deck-panel) 82%, transparent); color: var(--deck-text); border-radius: 999px; padding: 8px 12px; font: 700 12px/1 var(--font-sans, ui-sans-serif, system-ui, sans-serif); cursor: pointer; }\n button:hover, button.active { border-color: var(--deck-accent); color: var(--deck-text); background: color-mix(in srgb, var(--deck-accent) 18%, var(--deck-panel)); }\n .slides { min-height: 0; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; scrollbar-gutter: stable; }\n .slide { display: none; min-height: 100%; align-content: center; gap: clamp(16px, 3vmin, 28px); padding: clamp(24px, 5vmin, 64px) clamp(28px, 6vw, 92px); }\n .slide.active { display: grid; }\n .slide-grid { display: grid; gap: clamp(24px, 5vw, 72px); align-items: center; }\n .slide-grid.with-metrics { grid-template-columns: minmax(0, 1.15fr) minmax(260px, .85fr); }\n .slide-grid.without-metrics { grid-template-columns: minmax(0, 1fr); }\n .slide-copy { max-width: min(1120px, 100%); }\n .kicker { color: var(--deck-accent); font-size: clamp(12px, 1.8vw, 18px); font-weight: 950; letter-spacing: .18em; text-transform: uppercase; }\n h2 { margin: 10px 0 18px; max-width: 16ch; font-size: clamp(40px, 8vmin, 104px); line-height: .9; letter-spacing: -.07em; }\n .lede { max-width: 900px; margin: 0 0 20px; color: var(--deck-text-secondary); font-size: clamp(18px, 3vmin, 32px); line-height: 1.14; letter-spacing: -.03em; }\n ul { display: grid; gap: 12px; max-width: 780px; margin: 0; padding: 0; list-style: none; }\n li { position: relative; padding-left: 30px; color: var(--deck-text-secondary); font-size: clamp(17px, 2.2vmin, 25px); line-height: 1.22; }\n li::before { content: ''; position: absolute; left: 0; top: .42em; width: 12px; height: 12px; border-radius: 50%; background: var(--deck-accent); box-shadow: 0 0 24px color-mix(in srgb, var(--deck-accent) 60%, transparent); }\n .metrics { display: grid; gap: 14px; }\n .metric { border: 1px solid color-mix(in srgb, var(--deck-accent) 42%, var(--deck-border)); border-radius: 28px; padding: 22px; background: color-mix(in srgb, var(--deck-panel) 78%, transparent); box-shadow: 0 24px 70px rgba(0,0,0,.26); }\n .metric span { color: var(--deck-text-muted); font-size: 11px; font-weight: 900; letter-spacing: .14em; text-transform: uppercase; }\n .metric strong { display: block; margin-top: 8px; font-size: clamp(34px, 6vw, 78px); line-height: .9; letter-spacing: -.06em; }\n .metric p { margin: 10px 0 0; color: var(--deck-text-secondary); }\n .speaker-note { max-width: min(1120px, 100%); border-left: 4px solid var(--deck-accent); padding: 10px 14px; color: var(--deck-text-muted); background: color-mix(in srgb, var(--deck-panel) 76%, transparent); border-radius: 14px; }\n .speaker-note span { display: block; margin-bottom: 2px; color: var(--deck-accent); font-size: 10px; font-weight: 900; letter-spacing: .12em; text-transform: uppercase; }\n .dots { display: flex; gap: 7px; align-items: center; }\n .dot { width: 30px; height: 7px; border: 0; border-radius: 999px; padding: 0; background: color-mix(in srgb, var(--deck-text) 24%, transparent); }\n .dot.active { width: 54px; background: var(--deck-accent); }\n .hint { font-size: 12px; color: var(--deck-text-muted); }\n html[data-pmx-presentation-mode=\"present\"] .hint { display: none; }\n .progress { height: 3px; width: 180px; overflow: hidden; border-radius: 999px; background: color-mix(in srgb, var(--deck-text) 18%, transparent); }\n .progress span { display: block; height: 100%; width: 0; background: var(--deck-accent); transition: width .2s ease; }\n @media (max-width: 820px) { .slide-grid { grid-template-columns: 1fr; } .metrics { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); } h2 { max-width: none; font-size: clamp(42px, 14vw, 78px); } .lede, li { font-size: 21px; } .topbar { align-items: flex-start; flex-direction: column; } .title { max-width: 100%; } }\n</style>\n</head>\n<body>\n<main class=\"deck\">\n <header class=\"topbar\">\n <div class=\"brand\"><div class=\"eyebrow\">PMX presentation</div><div class=\"title\">PMX Canvas 0.4.7 briefing</div><p>What shipped, what it costs, and what we do next.</p></div>\n </header>\n <section class=\"slides\"><article class=\"slide active\" data-slide=\"0\">\n <div class=\"slide-grid with-metrics\">\n <div class=\"slide-copy\">\n <div class=\"kicker\">01</div>\n <h2>Where we are</h2>\n <p class=\"lede\">The canvas is the shared surface between the agent and the human.</p>\n <ul><li>15 node types</li><li>22 MCP tools</li><li>14 MCP resources</li></ul>\n </div>\n <div class=\"metrics\"><div class=\"metric\"><span>Node types</span><strong>15</strong><p>all catalogued on the demo board</p></div><div class=\"metric\"><span>Graph types</span><strong>12</strong><p>canonical plus Tufte primitives</p></div><div class=\"metric\"><span>HTML primitives</span><strong>21</strong><p>generated, self-contained surfaces</p></div></div>\n </div>\n <aside class=\"speaker-note\"><span>Speaker note</span>Lead with the surface count — it is the clearest proxy for reach.</aside>\n </article><article class=\"slide \" data-slide=\"1\">\n <div class=\"slide-grid without-metrics\">\n <div class=\"slide-copy\">\n <div class=\"kicker\">02</div>\n <h2>What changed in 0.4.7</h2>\n \n <ul><li>The demo board is now a generated catalogue</li><li>Readability floors are shared by server and client</li><li>Delimited files render as tables</li></ul>\n </div>\n \n </div>\n <aside class=\"speaker-note\"><span>Speaker note</span>Keep this slide to three lines; detail lives in the changelog.</aside>\n </article><article class=\"slide \" data-slide=\"2\">\n <div class=\"slide-grid without-metrics\">\n <div class=\"slide-copy\">\n <div class=\"kicker\">03</div>\n <h2>Next</h2>\n <p class=\"lede\">Make the board the first thing a new user sees, not the last.</p>\n <ul><li>Ship --demo in the quickstart</li><li>Record a 60-second tour</li><li>Add a per-band deep link</li></ul>\n </div>\n \n </div>\n \n </article></section>\n <footer class=\"bottombar\">\n <div class=\"dots\"><button class=\"dot active\" type=\"button\" data-dot=\"0\" aria-label=\"Go to slide 1\"></button><button class=\"dot \" type=\"button\" data-dot=\"1\" aria-label=\"Go to slide 2\"></button><button class=\"dot \" type=\"button\" data-dot=\"2\" aria-label=\"Go to slide 3\"></button></div>\n <div class=\"hint\"><span id=\"slide-current\">1</span> / 3 - Arrow keys, Space, Page Up/Down</div>\n <div class=\"progress\" aria-hidden=\"true\"><span id=\"slide-progress\"></span></div>\n </footer>\n</main>\n<script type=\"application/json\" id=\"pmx-data\">{\n \"subtitle\": \"What shipped, what it costs, and what we do next.\",\n \"theme\": \"midnight\",\n \"slides\": [\n {\n \"title\": \"Where we are\",\n \"kicker\": \"01\",\n \"body\": \"The canvas is the shared surface between the agent and the human.\",\n \"bullets\": [\n \"15 node types\",\n \"22 MCP tools\",\n \"14 MCP resources\"\n ],\n \"metrics\": [\n {\n \"label\": \"Node types\",\n \"value\": \"15\",\n \"detail\": \"all catalogued on the demo board\"\n },\n {\n \"label\": \"Graph types\",\n \"value\": \"12\",\n \"detail\": \"canonical plus Tufte primitives\"\n },\n {\n \"label\": \"HTML primitives\",\n \"value\": \"21\",\n \"detail\": \"generated, self-contained surfaces\"\n }\n ],\n \"note\": \"Lead with the surface count — it is the clearest proxy for reach.\"\n },\n {\n \"title\": \"What changed in 0.4.7\",\n \"kicker\": \"02\",\n \"bullets\": [\n \"The demo board is now a generated catalogue\",\n \"Readability floors are shared by server and client\",\n \"Delimited files render as tables\"\n ],\n \"note\": \"Keep this slide to three lines; detail lives in the changelog.\"\n },\n {\n \"title\": \"Next\",\n \"kicker\": \"03\",\n \"body\": \"Make the board the first thing a new user sees, not the last.\",\n \"bullets\": [\n \"Ship --demo in the quickstart\",\n \"Record a 60-second tour\",\n \"Add a per-band deep link\"\n ]\n }\n ],\n \"presentation\": true,\n \"slideCount\": 3,\n \"slideTitles\": [\n \"Where we are\",\n \"What changed in 0.4.7\",\n \"Next\"\n ],\n \"speakerNotes\": [\n \"Lead with the surface count — it is the clearest proxy for reach.\",\n \"Keep this slide to three lines; detail lives in the changelog.\"\n ],\n \"presentationTheme\": \"midnight\"\n}</script>\n<script>\nlet currentSlide = 0;\nconst slides = Array.from(document.querySelectorAll('[data-slide]'));\nconst dots = Array.from(document.querySelectorAll('[data-dot]'));\nfunction showSlide(index) {\n currentSlide = Math.max(0, Math.min(slides.length - 1, index));\n slides.forEach((slide, i) => slide.classList.toggle('active', i === currentSlide));\n dots.forEach((dot, i) => dot.classList.toggle('active', i === currentSlide));\n document.getElementById('slide-current').textContent = String(currentSlide + 1);\n document.getElementById('slide-progress').style.width = String(((currentSlide + 1) / slides.length) * 100) + '%';\n}\ndots.forEach((dot) => dot.addEventListener('click', () => showSlide(Number(dot.getAttribute('data-dot')))));\nfunction handlePresentationKey(key) {\n if (key === 'ArrowRight' || key === 'PageDown' || key === ' ') { showSlide(currentSlide + 1); return true; }\n if (key === 'ArrowLeft' || key === 'PageUp') { showSlide(currentSlide - 1); return true; }\n if (key === 'Home') { showSlide(0); return true; }\n if (key === 'End') { showSlide(slides.length - 1); return true; }\n return false;\n}\nwindow.PMX_CANVAS_PRESENTATION_HANDLE_KEY = handlePresentationKey;\ndocument.addEventListener('pmx-presentation-key', (event) => {\n if (!event.detail || typeof event.detail.key !== 'string') return;\n handlePresentationKey(event.detail.key);\n});\ndocument.addEventListener('keydown', (event) => {\n const tag = event.target && event.target.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;\n if (handlePresentationKey(event.key)) event.preventDefault();\n});\nshowSlide(0);\n</script>\n</body>\n</html>",
|
|
4387
|
+
"html": "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>PMX Canvas 0.4.7 briefing</title>\n<style>\n :root { color-scheme: dark; --deck-accent: #4BBCFF; --deck-bg: #081524; --deck-panel: #0f1d31; --deck-surface: #10213a; --deck-border: #1b2c44; --deck-text: #e6eef7; --deck-text-secondary: #c7d3ea; --deck-text-muted: #8ea3bd; }\n * { box-sizing: border-box; }\n html, body { width: 100%; height: 100%; overflow: hidden; }\n body { margin: 0; padding: 0; background: var(--deck-bg); color: var(--deck-text); font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif); }\n .deck { height: 100vh; min-height: 0; display: grid; grid-template-rows: auto minmax(0, 1fr) auto; background: radial-gradient(circle at 10% 10%, color-mix(in srgb, var(--deck-accent) 32%, transparent), transparent 28rem), linear-gradient(135deg, color-mix(in srgb, var(--deck-panel) 88%, black), var(--deck-bg)); }\n .topbar, .bottombar { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: clamp(12px, 2.5vmin, 24px) clamp(18px, 4vw, 48px); color: var(--deck-text-secondary); }\n .brand { display: grid; gap: 2px; }\n .brand p { margin: 0; }\n .eyebrow { color: var(--deck-accent); font-size: 11px; font-weight: 900; letter-spacing: .18em; text-transform: uppercase; }\n .title { max-width: 70vw; overflow: hidden; color: var(--deck-text); font-size: clamp(16px, 2vw, 24px); font-weight: 850; letter-spacing: -.03em; text-overflow: ellipsis; white-space: nowrap; }\n .controls { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }\n button { border: 1px solid color-mix(in srgb, var(--deck-border) 82%, white); background: color-mix(in srgb, var(--deck-panel) 82%, transparent); color: var(--deck-text); border-radius: 999px; padding: 8px 12px; font: 700 12px/1 var(--font-sans, ui-sans-serif, system-ui, sans-serif); cursor: pointer; }\n button:hover, button.active { border-color: var(--deck-accent); color: var(--deck-text); background: color-mix(in srgb, var(--deck-accent) 18%, var(--deck-panel)); }\n .slides { min-height: 0; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; scrollbar-gutter: stable; }\n .slide { display: none; min-height: 100%; align-content: center; gap: clamp(16px, 3vmin, 28px); padding: clamp(24px, 5vmin, 64px) clamp(28px, 6vw, 92px); }\n .slide.active { display: grid; }\n .slide-grid { display: grid; gap: clamp(24px, 5vw, 72px); align-items: center; }\n .slide-grid.with-metrics { grid-template-columns: minmax(0, 1.15fr) minmax(260px, .85fr); }\n .slide-grid.without-metrics { grid-template-columns: minmax(0, 1fr); }\n .slide-copy { max-width: min(1120px, 100%); }\n .kicker { color: var(--deck-accent); font-size: clamp(12px, 1.8vw, 18px); font-weight: 950; letter-spacing: .18em; text-transform: uppercase; }\n h2 { margin: 10px 0 18px; max-width: 16ch; font-size: clamp(40px, 8vmin, 104px); line-height: .9; letter-spacing: -.07em; }\n .lede { max-width: 900px; margin: 0 0 20px; color: var(--deck-text-secondary); font-size: clamp(18px, 3vmin, 32px); line-height: 1.14; letter-spacing: -.03em; }\n ul { display: grid; gap: 12px; max-width: 780px; margin: 0; padding: 0; list-style: none; }\n li { position: relative; padding-left: 30px; color: var(--deck-text-secondary); font-size: clamp(17px, 2.2vmin, 25px); line-height: 1.22; }\n li::before { content: ''; position: absolute; left: 0; top: .42em; width: 12px; height: 12px; border-radius: 50%; background: var(--deck-accent); box-shadow: 0 0 24px color-mix(in srgb, var(--deck-accent) 60%, transparent); }\n .metrics { display: grid; gap: 14px; }\n .metric { border: 1px solid color-mix(in srgb, var(--deck-accent) 42%, var(--deck-border)); border-radius: 28px; padding: 22px; background: color-mix(in srgb, var(--deck-panel) 78%, transparent); box-shadow: 0 24px 70px rgba(0,0,0,.26); }\n .metric span { color: var(--deck-text-muted); font-size: 11px; font-weight: 900; letter-spacing: .14em; text-transform: uppercase; }\n .metric strong { display: block; margin-top: 8px; font-size: clamp(34px, 6vw, 78px); line-height: .9; letter-spacing: -.06em; }\n .metric p { margin: 10px 0 0; color: var(--deck-text-secondary); }\n .speaker-note { max-width: min(1120px, 100%); border-left: 4px solid var(--deck-accent); padding: 10px 14px; color: var(--deck-text-muted); background: color-mix(in srgb, var(--deck-panel) 76%, transparent); border-radius: 14px; }\n .speaker-note span { display: block; margin-bottom: 2px; color: var(--deck-accent); font-size: 10px; font-weight: 900; letter-spacing: .12em; text-transform: uppercase; }\n .dots { display: flex; gap: 7px; align-items: center; }\n .dot { width: 30px; height: 7px; border: 0; border-radius: 999px; padding: 0; background: color-mix(in srgb, var(--deck-text) 24%, transparent); }\n .dot.active { width: 54px; background: var(--deck-accent); }\n .hint { font-size: 12px; color: var(--deck-text-muted); }\n html[data-pmx-presentation-mode=\"present\"] .hint { display: none; }\n .progress { height: 3px; width: 180px; overflow: hidden; border-radius: 999px; background: color-mix(in srgb, var(--deck-text) 18%, transparent); }\n .progress span { display: block; height: 100%; width: 0; background: var(--deck-accent); transition: width .2s ease; }\n @media (max-width: 820px) { .slide-grid { grid-template-columns: 1fr; } .metrics { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); } h2 { max-width: none; font-size: clamp(42px, 14vw, 78px); } .lede, li { font-size: 21px; } .topbar { align-items: flex-start; flex-direction: column; } .title { max-width: 100%; } }\n</style>\n</head>\n<body>\n<main class=\"deck\">\n <header class=\"topbar\">\n <div class=\"brand\"><div class=\"eyebrow\">PMX presentation</div><div class=\"title\">PMX Canvas 0.4.7 briefing</div><p>What shipped, what it costs, and what we do next.</p></div>\n </header>\n <section class=\"slides\"><article class=\"slide active\" data-slide=\"0\">\n <div class=\"slide-grid with-metrics\">\n <div class=\"slide-copy\">\n <div class=\"kicker\">01</div>\n <h2>Where we are</h2>\n <p class=\"lede\">The canvas is the shared surface between the agent and the human.</p>\n <ul><li>15 node types</li><li>22 MCP tools</li><li>14 MCP resources</li></ul>\n </div>\n <div class=\"metrics\"><div class=\"metric\"><span>Node types</span><strong>15</strong><p>all catalogued on the demo board</p></div><div class=\"metric\"><span>Graph types</span><strong>12</strong><p>canonical plus Tufte primitives</p></div><div class=\"metric\"><span>HTML primitives</span><strong>21</strong><p>generated, self-contained surfaces</p></div></div>\n </div>\n <aside class=\"speaker-note\"><span>Speaker note</span>Lead with the surface count — it is the clearest proxy for reach.</aside>\n </article><article class=\"slide \" data-slide=\"1\">\n <div class=\"slide-grid without-metrics\">\n <div class=\"slide-copy\">\n <div class=\"kicker\">02</div>\n <h2>What changed in 0.4.7</h2>\n \n <ul><li>The demo board is now a generated catalogue</li><li>Readability floors are shared by server and client</li><li>Delimited files render as tables</li></ul>\n </div>\n \n </div>\n <aside class=\"speaker-note\"><span>Speaker note</span>Keep this slide to three lines; detail lives in the changelog.</aside>\n </article><article class=\"slide \" data-slide=\"2\">\n <div class=\"slide-grid without-metrics\">\n <div class=\"slide-copy\">\n <div class=\"kicker\">03</div>\n <h2>Next</h2>\n <p class=\"lede\">Make the board the first thing a new user sees, not the last.</p>\n <ul><li>Ship --demo in the quickstart</li><li>Record a 60-second tour</li><li>Add a per-band deep link</li></ul>\n </div>\n \n </div>\n \n </article></section>\n <footer class=\"bottombar\">\n <div class=\"dots\"><button class=\"dot active\" type=\"button\" data-dot=\"0\" aria-label=\"Go to slide 1\"></button><button class=\"dot \" type=\"button\" data-dot=\"1\" aria-label=\"Go to slide 2\"></button><button class=\"dot \" type=\"button\" data-dot=\"2\" aria-label=\"Go to slide 3\"></button></div>\n <div class=\"hint\"><span id=\"slide-current\">1</span> / 3 - Arrow keys, Space, Page Up/Down</div>\n <div class=\"progress\" aria-hidden=\"true\"><span id=\"slide-progress\"></span></div>\n </footer>\n</main>\n<script type=\"application/json\" id=\"pmx-data\">{\n \"subtitle\": \"What shipped, what it costs, and what we do next.\",\n \"theme\": \"midnight\",\n \"slides\": [\n {\n \"title\": \"Where we are\",\n \"kicker\": \"01\",\n \"body\": \"The canvas is the shared surface between the agent and the human.\",\n \"bullets\": [\n \"15 node types\",\n \"22 MCP tools\",\n \"14 MCP resources\"\n ],\n \"metrics\": [\n {\n \"label\": \"Node types\",\n \"value\": \"15\",\n \"detail\": \"all catalogued on the demo board\"\n },\n {\n \"label\": \"Graph types\",\n \"value\": \"12\",\n \"detail\": \"canonical plus Tufte primitives\"\n },\n {\n \"label\": \"HTML primitives\",\n \"value\": \"21\",\n \"detail\": \"generated, self-contained surfaces\"\n }\n ],\n \"note\": \"Lead with the surface count — it is the clearest proxy for reach.\"\n },\n {\n \"title\": \"What changed in 0.4.7\",\n \"kicker\": \"02\",\n \"bullets\": [\n \"The demo board is now a generated catalogue\",\n \"Readability floors are shared by server and client\",\n \"Delimited files render as tables\"\n ],\n \"note\": \"Keep this slide to three lines; detail lives in the changelog.\"\n },\n {\n \"title\": \"Next\",\n \"kicker\": \"03\",\n \"body\": \"Make the board the first thing a new user sees, not the last.\",\n \"bullets\": [\n \"Ship --demo in the quickstart\",\n \"Record a 60-second tour\",\n \"Add a per-band deep link\"\n ]\n }\n ],\n \"presentation\": true,\n \"slideCount\": 3,\n \"slideTitles\": [\n \"Where we are\",\n \"What changed in 0.4.7\",\n \"Next\"\n ],\n \"speakerNotes\": [\n \"Lead with the surface count — it is the clearest proxy for reach.\",\n \"Keep this slide to three lines; detail lives in the changelog.\"\n ],\n \"presentationTheme\": \"midnight\"\n}</script>\n<script>\nlet currentSlide = 0;\nconst slides = Array.from(document.querySelectorAll('[data-slide]'));\nconst dots = Array.from(document.querySelectorAll('[data-dot]'));\nfunction showSlide(index) {\n if (!slides.length) return;\n currentSlide = Math.max(0, Math.min(slides.length - 1, index));\n slides.forEach((slide, i) => slide.classList.toggle('active', i === currentSlide));\n dots.forEach((dot, i) => dot.classList.toggle('active', i === currentSlide));\n document.getElementById('slide-current').textContent = String(currentSlide + 1);\n document.getElementById('slide-progress').style.width = String(((currentSlide + 1) / slides.length) * 100) + '%';\n}\ndots.forEach((dot) => dot.addEventListener('click', () => showSlide(Number(dot.getAttribute('data-dot')))));\nfunction handlePresentationKey(key) {\n if (key === 'ArrowRight' || key === 'PageDown' || key === ' ') { showSlide(currentSlide + 1); return true; }\n if (key === 'ArrowLeft' || key === 'PageUp') { showSlide(currentSlide - 1); return true; }\n if (key === 'Home') { showSlide(0); return true; }\n if (key === 'End') { showSlide(slides.length - 1); return true; }\n return false;\n}\nwindow.PMX_CANVAS_PRESENTATION_HANDLE_KEY = handlePresentationKey;\ndocument.addEventListener('pmx-presentation-key', (event) => {\n if (!event.detail || typeof event.detail.key !== 'string') return;\n handlePresentationKey(event.detail.key);\n});\ndocument.addEventListener('keydown', (event) => {\n const tag = event.target && event.target.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;\n if (handlePresentationKey(event.key)) event.preventDefault();\n});\nshowSlide(0);\n</script>\n</body>\n</html>",
|
|
3872
4388
|
"htmlPrimitive": "presentation",
|
|
3873
4389
|
"primitiveData": {
|
|
3874
4390
|
"subtitle": "What shipped, what it costs, and what we do next.",
|
|
@@ -3954,6 +4470,15 @@
|
|
|
3954
4470
|
"contentSummary": "PMX Canvas 0.4.7 briefing PMX presentation PMX Canvas 0.4.7 briefing What shipped, what it costs, and what we do next. 01 Where we are The canvas is the shared surface between the agent and the human. 15 node types 22 MCP tools 14 MCP resources Node types 15 all catalogued on the demo board Graph types 12 canonical plus Tufte primitives HTML primitives 21 generated, self-contained surfaces Speaker note Lead with the surface count — it is the clearest proxy for reach. 02 What changed in 0.4.7 The demo board is now a generated catalogue Readability floors are shared by server and client Delimited files render as tables Speaker note Keep this slide to three lines; detail lives in the changelog. 03 Next Make the board the first thing a new user sees, not the last. Ship --demo in the quickstart Record a 60-second tour Add a per-band deep link 1 / 3 - Arrow keys, Space, Page Up/Down",
|
|
3955
4471
|
"agentSummary": "PowerPoint-style fullscreen-ready HTML presentation with slide navigation, progress, speaker notes, and presentation metadata. Slides: Where we are, What changed in 0.4.7, Next."
|
|
3956
4472
|
},
|
|
4473
|
+
"createdBy": {
|
|
4474
|
+
"actor": "agent",
|
|
4475
|
+
"source": "api"
|
|
4476
|
+
},
|
|
4477
|
+
"lastEditedBy": {
|
|
4478
|
+
"actor": "agent",
|
|
4479
|
+
"source": "api"
|
|
4480
|
+
},
|
|
4481
|
+
"contentRevision": 57,
|
|
3957
4482
|
"kind": "html",
|
|
3958
4483
|
"title": "PMX Canvas 0.4.7 briefing",
|
|
3959
4484
|
"content": "PowerPoint-style fullscreen-ready HTML presentation with slide navigation, progress, speaker notes, and presentation metadata. Slides: Where we are, What changed in 0.4.7, Next.",
|
|
@@ -4110,6 +4635,15 @@
|
|
|
4110
4635
|
"contentSummary": "Figures — the collaboration loop PMX HTML primitive / illustration-set Figures — the collaboration loop Inline SVG figure sheet with captions and per-figure SVG copy/export controls. Copy JSON Copy prompt The pin loop Human Canvas Agent pins → canvas://pinned-context The human curates space; the agent reads that curation as context. Copy SVG One state, many surfaces MCP HTTP SDK Operation registry MCP, HTTP and the SDK are three doors into the same operation registry. Copy SVG",
|
|
4111
4636
|
"agentSummary": "Inline SVG figure sheet with captions and per-figure SVG copy/export controls."
|
|
4112
4637
|
},
|
|
4638
|
+
"createdBy": {
|
|
4639
|
+
"actor": "agent",
|
|
4640
|
+
"source": "api"
|
|
4641
|
+
},
|
|
4642
|
+
"lastEditedBy": {
|
|
4643
|
+
"actor": "agent",
|
|
4644
|
+
"source": "api"
|
|
4645
|
+
},
|
|
4646
|
+
"contentRevision": 58,
|
|
4113
4647
|
"kind": "html",
|
|
4114
4648
|
"title": "Figures — the collaboration loop",
|
|
4115
4649
|
"content": "Inline SVG figure sheet with captions and per-figure SVG copy/export controls.",
|
|
@@ -4207,6 +4741,15 @@
|
|
|
4207
4741
|
"node-b"
|
|
4208
4742
|
]
|
|
4209
4743
|
},
|
|
4744
|
+
"createdBy": {
|
|
4745
|
+
"actor": "agent",
|
|
4746
|
+
"source": "api"
|
|
4747
|
+
},
|
|
4748
|
+
"lastEditedBy": {
|
|
4749
|
+
"actor": "agent",
|
|
4750
|
+
"source": "api"
|
|
4751
|
+
},
|
|
4752
|
+
"contentRevision": 59,
|
|
4210
4753
|
"kind": "html",
|
|
4211
4754
|
"title": "How context pins work",
|
|
4212
4755
|
"content": "Readable explainer with TLDR, collapsible steps, annotated snippets, FAQ, and glossary.",
|
|
@@ -4319,6 +4862,15 @@
|
|
|
4319
4862
|
"mcp-app-1"
|
|
4320
4863
|
]
|
|
4321
4864
|
},
|
|
4865
|
+
"createdBy": {
|
|
4866
|
+
"actor": "agent",
|
|
4867
|
+
"source": "api"
|
|
4868
|
+
},
|
|
4869
|
+
"lastEditedBy": {
|
|
4870
|
+
"actor": "agent",
|
|
4871
|
+
"source": "api"
|
|
4872
|
+
},
|
|
4873
|
+
"contentRevision": 60,
|
|
4322
4874
|
"kind": "html",
|
|
4323
4875
|
"title": "Blank ext-app tiles on WebKit",
|
|
4324
4876
|
"content": "Post-incident report with impact metrics, minute-by-minute timeline, root cause, log excerpts, and action checklist.",
|
|
@@ -4396,6 +4948,15 @@
|
|
|
4396
4948
|
"contentSummary": "Weekly canvas status PMX HTML primitive / status-report Weekly canvas status Skimmable report with metrics, shipped/slipped lists, blockers, and next actions. Copy JSON Copy prompt Gates 4/5 green warn Unit tests 612 passing ok Coverage 71% lines ok Open bugs 7 warn Bundle +4.1% warn Shipped Delimited files render as tables instead of raw text Shared readability floor between server and client Generated demo board covering every public surface Slipped Per-band deep links Snapshot diff in the browser Risks Bundle growth is trending up three weeks running e2e suite is the slowest gate and the flakiest Next Cut the bundle back under the 0.4.5 baseline Record the 60-second tour off the new demo board",
|
|
4397
4949
|
"agentSummary": "Skimmable report with metrics, shipped/slipped lists, blockers, and next actions."
|
|
4398
4950
|
},
|
|
4951
|
+
"createdBy": {
|
|
4952
|
+
"actor": "agent",
|
|
4953
|
+
"source": "api"
|
|
4954
|
+
},
|
|
4955
|
+
"lastEditedBy": {
|
|
4956
|
+
"actor": "agent",
|
|
4957
|
+
"source": "api"
|
|
4958
|
+
},
|
|
4959
|
+
"contentRevision": 61,
|
|
4399
4960
|
"kind": "html",
|
|
4400
4961
|
"title": "Weekly canvas status",
|
|
4401
4962
|
"content": "Skimmable report with metrics, shipped/slipped lists, blockers, and next actions.",
|
|
@@ -4473,6 +5034,15 @@
|
|
|
4473
5034
|
"contentSummary": "Post-0.4.7 triage PMX HTML primitive / triage-board Post-0.4.7 triage Draggable Now/Next/Later/Cut board with copy-as-markdown export. Copy JSON Copy prompt Now Bundle back under baseline Client bundle is +4.1% over 0.4.6. Trending the wrong way three weeks running. Stabilise the e2e gate Two reruns per week on average. Slowest and flakiest gate blocks every release. Next Per-band deep links Open the board focused on one band. Makes the demo board usable as documentation. 60-second tour recording Screen capture driven off --demo. Cheapest onboarding win available. Later Snapshot diff in the browser Today it is MCP/HTTP only. Useful, not blocking. Cut Theme editor UI Edit CSS variables from the toolbar. Nine built-in themes already cover the need. Drag cards between columns, then copy JSON or markdown. Copy markdown",
|
|
4474
5035
|
"agentSummary": "Draggable Now/Next/Later/Cut board with copy-as-markdown export."
|
|
4475
5036
|
},
|
|
5037
|
+
"createdBy": {
|
|
5038
|
+
"actor": "agent",
|
|
5039
|
+
"source": "api"
|
|
5040
|
+
},
|
|
5041
|
+
"lastEditedBy": {
|
|
5042
|
+
"actor": "agent",
|
|
5043
|
+
"source": "api"
|
|
5044
|
+
},
|
|
5045
|
+
"contentRevision": 62,
|
|
4476
5046
|
"kind": "html",
|
|
4477
5047
|
"title": "Post-0.4.7 triage",
|
|
4478
5048
|
"content": "Draggable Now/Next/Later/Cut board with copy-as-markdown export.",
|
|
@@ -4556,6 +5126,15 @@
|
|
|
4556
5126
|
"node-type"
|
|
4557
5127
|
]
|
|
4558
5128
|
},
|
|
5129
|
+
"createdBy": {
|
|
5130
|
+
"actor": "agent",
|
|
5131
|
+
"source": "api"
|
|
5132
|
+
},
|
|
5133
|
+
"lastEditedBy": {
|
|
5134
|
+
"actor": "agent",
|
|
5135
|
+
"source": "api"
|
|
5136
|
+
},
|
|
5137
|
+
"contentRevision": 63,
|
|
4559
5138
|
"kind": "html",
|
|
4560
5139
|
"title": "Canvas feature flags",
|
|
4561
5140
|
"content": "Form-like editor for flags or structured config with dependency warnings and copy-diff export.",
|
|
@@ -4579,7 +5158,7 @@
|
|
|
4579
5158
|
"collapsed": false,
|
|
4580
5159
|
"pinned": false,
|
|
4581
5160
|
"data": {
|
|
4582
|
-
"html": "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Board summary prompt</title>\n<style>\n :root { color-scheme: dark light; }\n * { box-sizing: border-box; }\n body { margin: 0; padding: 22px; background: radial-gradient(circle at top left, color-mix(in srgb, var(--color-accent, #4a9eff) 18%, transparent), transparent 34rem), var(--color-bg, #0b0f14); color: var(--color-text, #e8edf2); font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif); }\n .shell { max-width: 1160px; margin: 0 auto; }\n header.hero { display: grid; grid-template-columns: 1fr auto; gap: 16px; align-items: start; margin-bottom: 20px; }\n .kicker { color: var(--color-accent, #4a9eff); text-transform: uppercase; letter-spacing: .16em; font-size: 11px; font-weight: 800; }\n h1 { margin: 5px 0 8px; font-size: clamp(28px, 4vw, 46px); line-height: .95; letter-spacing: -.04em; }\n h2 { margin: 0 0 12px; font-size: 19px; letter-spacing: -.02em; }\n h3 { margin: 0 0 8px; font-size: 15px; }\n p { color: var(--color-text-secondary, #aeb8c2); margin: 0 0 12px; }\n .actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }\n button { border: 1px solid var(--color-border, #263241); background: var(--color-panel, #111821); color: var(--color-text, #e8edf2); border-radius: 999px; padding: 8px 12px; font: inherit; cursor: pointer; }\n button:hover { border-color: var(--color-accent, #4a9eff); transform: translateY(-1px); }\n table { width: 100%; border-collapse: collapse; color: var(--color-text-secondary, #aeb8c2); font-size: 13px; }\n th, td { text-align: left; border-bottom: 1px solid var(--color-border, #263241); padding: 9px 8px; vertical-align: top; }\n th { color: var(--color-text, #e8edf2); font-size: 11px; text-transform: uppercase; letter-spacing: .08em; }\n .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(235px, 1fr)); gap: 14px; }\n .two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; }\n .three { display: grid; grid-template-columns: 1.1fr 1fr .9fr; gap: 14px; align-items: start; }\n .card, details, .panel { border: 1px solid var(--color-border, #263241); background: color-mix(in srgb, var(--color-panel, #111821) 88%, transparent); border-radius: 18px; padding: 16px; box-shadow: 0 18px 50px rgba(0, 0, 0, .18); }\n .card.emphasis { border-color: color-mix(in srgb, var(--color-accent, #4a9eff) 55%, var(--color-border, #263241)); }\n .sticky { position: sticky; top: 14px; }\n .metric { min-height: 104px; display: grid; align-content: space-between; }\n .metric strong { font-size: clamp(24px, 4vw, 42px); letter-spacing: -.04em; }\n .muted { color: var(--color-text-secondary, #aeb8c2); }\n .small { font-size: 12px; color: var(--color-text-muted, #7e8a97); }\n ul { padding-left: 18px; margin: 8px 0 0; color: var(--color-text-secondary, #aeb8c2); }\n li + li { margin-top: 5px; }\n pre { white-space: pre-wrap; overflow: auto; margin: 10px 0 0; padding: 12px; border-radius: 12px; background: #05070a; border: 1px solid color-mix(in srgb, var(--color-border, #263241) 75%, black); color: #d7eadb; font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: 12px; line-height: 1.45; }\n .badge { display: inline-flex; align-items: center; border-radius: 999px; padding: 3px 8px; font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; }\n .badge.ok { color: #062b18; background: var(--color-success, #22c55e); }\n .badge.warn { color: #2b1a02; background: var(--color-warning, #eab308); }\n .badge.danger { color: white; background: var(--color-danger, #ef4444); }\n .badge.info { color: #051a33; background: var(--color-accent, #4a9eff); }\n .timeline { position: relative; display: grid; gap: 12px; }\n .timeline .step { display: grid; grid-template-columns: 34px 1fr; gap: 12px; align-items: start; }\n .dot { width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; background: var(--color-accent, #4a9eff); color: #06111f; font-weight: 900; }\n .flow { display: flex; gap: 10px; align-items: stretch; overflow-x: auto; padding-bottom: 4px; }\n .flow-node { min-width: 180px; border-radius: 16px; border: 1px solid var(--color-border, #263241); padding: 14px; background: var(--color-surface, #17202b); }\n .arrow { align-self: center; color: var(--color-accent, #4a9eff); font-weight: 900; }\n .swatches { display: flex; gap: 6px; flex-wrap: wrap; margin: 8px 0; }\n .swatch { width: 44px; height: 44px; border-radius: 13px; border: 1px solid rgba(255,255,255,.22); }\n textarea, input[type=\"text\"] { width: 100%; border: 1px solid var(--color-border, #263241); border-radius: 14px; padding: 12px; background: var(--color-bg, #0b0f14); color: var(--color-text, #e8edf2); font: inherit; }\n textarea { min-height: 220px; resize: vertical; font-family: var(--font-mono, ui-monospace, monospace); }\n .columns { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; align-items: start; }\n .column { min-height: 220px; border: 1px dashed var(--color-border, #263241); border-radius: 18px; padding: 12px; background: color-mix(in srgb, var(--color-panel, #111821) 70%, transparent); }\n .ticket { cursor: grab; margin: 10px 0; }\n .ticket:active { cursor: grabbing; }\n .slide { min-height: 390px; display: none; align-content: center; }\n .slide.active { display: grid; }\n .slide h2 { font-size: clamp(30px, 5vw, 58px); line-height: .95; }\n .preview { white-space: pre-wrap; min-height: 220px; }\n .figure svg { width: 100%; min-height: 220px; border-radius: 14px; background: color-mix(in srgb, var(--color-bg, #0b0f14) 86%, white); border: 1px solid var(--color-border, #263241); }\n @media (max-width: 760px) { body { padding: 14px; } header.hero, .two, .three { grid-template-columns: 1fr; } .actions { justify-content: flex-start; } .sticky { position: static; } }\n</style>\n</head>\n<body>\n<div class=\"shell\">\n <header class=\"hero\">\n <div><div class=\"kicker\">PMX HTML primitive / prompt-tuner</div><h1>Board summary prompt</h1><p>Side-by-side prompt/template editor with live variable previews and copy export.</p></div>\n <div class=\"actions\"><button type=\"button\" data-copy-json>Copy JSON</button><button type=\"button\" data-copy-prompt>Copy prompt</button></div>\n </header>\n <section class=\"two\"><div class=\"panel\"><h2>Template</h2><textarea id=\"template\">You are looking at a PMX Canvas board for {{project}}.\n\nSummarise it for a {{audience}} in {{sentences}} sentences. Lead with what the board is FOR, then what changed most recently. Ignore layout and node ids.\n\nPinned nodes are the human’s curation — weight them above everything else.</textarea><p class=\"small\"><span id=\"char-count\">0</span> characters</p></div><div class=\"panel\"><h2>Live Samples</h2><div id=\"previews\"></div><button type=\"button\" data-copy-template>Copy template</button></div></section>\n</div>\n<script type=\"application/json\" id=\"pmx-data\">{\n \"template\": \"You are looking at a PMX Canvas board for {{project}}.\\n\\nSummarise it for a {{audience}} in {{sentences}} sentences. Lead with what the board is FOR, then what changed most recently. Ignore layout and node ids.\\n\\nPinned nodes are the human’s curation — weight them above everything else.\",\n \"samples\": [\n {\n \"name\": \"Standup\",\n \"variables\": {\n \"project\": \"pmx-canvas\",\n \"audience\": \"teammate joining the standup\",\n \"sentences\": \"two\"\n }\n },\n {\n \"name\": \"Exec review\",\n \"variables\": {\n \"project\": \"the 0.4.7 release\",\n \"audience\": \"director who has not seen the tool\",\n \"sentences\": \"three\"\n }\n },\n {\n \"name\": \"Handoff\",\n \"variables\": {\n \"project\": \"the demo board rebuild\",\n \"audience\": \"agent picking the work up cold\",\n \"sentences\": \"four\"\n }\n }\n ]\n}</script>\n<script>\nconst PMX_DATA = JSON.parse(document.getElementById('pmx-data').textContent);\nfunction fallbackCopy(text) {\n const el = document.createElement('textarea');\n el.value = text;\n el.setAttribute('readonly', '');\n el.style.position = 'fixed';\n el.style.left = '-9999px';\n document.body.appendChild(el);\n el.select();\n document.execCommand('copy');\n el.remove();\n}\nasync function copyText(text) {\n if (navigator.clipboard && window.isSecureContext) await navigator.clipboard.writeText(text);\n else fallbackCopy(text);\n}\ndocument.querySelector('[data-copy-json]')?.addEventListener('click', () => {\n const payload = typeof window.__pmxGetCopyJson === 'function' ? window.__pmxGetCopyJson() : PMX_DATA;\n copyText(JSON.stringify(payload, null, 2));\n});\ndocument.querySelector('[data-copy-prompt]')?.addEventListener('click', () => copyText('Use this PMX Canvas HTML primitive output as context:\\n\\n' + JSON.stringify(PMX_DATA, null, 2)));\n\nconst templateEl = document.getElementById('template');\nconst previewsEl = document.getElementById('previews');\nconst samples = PMX_DATA.samples || [];\nfunction fill(template, vars) {\n return template.replace(/{{\\s*([\\w.-]+)\\s*}}/g, (_, key) => vars?.[key] ?? '{{' + key + '}}');\n}\nfunction renderPreviews() {\n const value = templateEl.value;\n document.getElementById('char-count').textContent = String(value.length);\n previewsEl.innerHTML = samples.map((sample) => '<div class=\"card\"><h3>' + (sample.name || 'Sample') + '</h3><div class=\"preview\">' + fill(value, sample.variables || {}).replace(/[&<>]/g, (c) => ({'&':'&','<':'<','>':'>'}[c])) + '</div></div>').join('');\n}\ntemplateEl.addEventListener('input', renderPreviews);\nwindow.__pmxGetCopyJson = () => ({ ...PMX_DATA, template: templateEl.value });\ndocument.querySelector('[data-copy-template]')?.addEventListener('click', () => copyText(templateEl.value));\nrenderPreviews();\n</script>\n</body>\n</html>",
|
|
5161
|
+
"html": "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Board summary prompt</title>\n<style>\n :root { color-scheme: dark light; }\n * { box-sizing: border-box; }\n body { margin: 0; padding: 22px; background: radial-gradient(circle at top left, color-mix(in srgb, var(--color-accent, #4a9eff) 18%, transparent), transparent 34rem), var(--color-bg, #0b0f14); color: var(--color-text, #e8edf2); font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif); }\n .shell { max-width: 1160px; margin: 0 auto; }\n header.hero { display: grid; grid-template-columns: 1fr auto; gap: 16px; align-items: start; margin-bottom: 20px; }\n .kicker { color: var(--color-accent, #4a9eff); text-transform: uppercase; letter-spacing: .16em; font-size: 11px; font-weight: 800; }\n h1 { margin: 5px 0 8px; font-size: clamp(28px, 4vw, 46px); line-height: .95; letter-spacing: -.04em; }\n h2 { margin: 0 0 12px; font-size: 19px; letter-spacing: -.02em; }\n h3 { margin: 0 0 8px; font-size: 15px; }\n p { color: var(--color-text-secondary, #aeb8c2); margin: 0 0 12px; }\n .actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }\n button { border: 1px solid var(--color-border, #263241); background: var(--color-panel, #111821); color: var(--color-text, #e8edf2); border-radius: 999px; padding: 8px 12px; font: inherit; cursor: pointer; }\n button:hover { border-color: var(--color-accent, #4a9eff); transform: translateY(-1px); }\n table { width: 100%; border-collapse: collapse; color: var(--color-text-secondary, #aeb8c2); font-size: 13px; }\n th, td { text-align: left; border-bottom: 1px solid var(--color-border, #263241); padding: 9px 8px; vertical-align: top; }\n th { color: var(--color-text, #e8edf2); font-size: 11px; text-transform: uppercase; letter-spacing: .08em; }\n .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(235px, 1fr)); gap: 14px; }\n .two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; }\n .three { display: grid; grid-template-columns: 1.1fr 1fr .9fr; gap: 14px; align-items: start; }\n .card, details, .panel { border: 1px solid var(--color-border, #263241); background: color-mix(in srgb, var(--color-panel, #111821) 88%, transparent); border-radius: 18px; padding: 16px; box-shadow: 0 18px 50px rgba(0, 0, 0, .18); }\n .card.emphasis { border-color: color-mix(in srgb, var(--color-accent, #4a9eff) 55%, var(--color-border, #263241)); }\n .sticky { position: sticky; top: 14px; }\n .metric { min-height: 104px; display: grid; align-content: space-between; }\n .metric strong { font-size: clamp(24px, 4vw, 42px); letter-spacing: -.04em; }\n .muted { color: var(--color-text-secondary, #aeb8c2); }\n .small { font-size: 12px; color: var(--color-text-muted, #7e8a97); }\n ul { padding-left: 18px; margin: 8px 0 0; color: var(--color-text-secondary, #aeb8c2); }\n li + li { margin-top: 5px; }\n pre { white-space: pre-wrap; overflow: auto; margin: 10px 0 0; padding: 12px; border-radius: 12px; background: #05070a; border: 1px solid color-mix(in srgb, var(--color-border, #263241) 75%, black); color: #d7eadb; font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: 12px; line-height: 1.45; }\n .badge { display: inline-flex; align-items: center; border-radius: 999px; padding: 3px 8px; font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; }\n .badge.ok { color: #062b18; background: var(--color-success, #22c55e); }\n .badge.warn { color: #2b1a02; background: var(--color-warning, #eab308); }\n .badge.danger { color: white; background: var(--color-danger, #ef4444); }\n .badge.info { color: #051a33; background: var(--color-accent, #4a9eff); }\n .timeline { position: relative; display: grid; gap: 12px; }\n .timeline .step { display: grid; grid-template-columns: 34px 1fr; gap: 12px; align-items: start; }\n .dot { width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; background: var(--color-accent, #4a9eff); color: #06111f; font-weight: 900; }\n .flow { display: flex; gap: 10px; align-items: stretch; overflow-x: auto; padding-bottom: 4px; }\n .flow-node { min-width: 180px; border-radius: 16px; border: 1px solid var(--color-border, #263241); padding: 14px; background: var(--color-surface, #17202b); }\n .arrow { align-self: center; color: var(--color-accent, #4a9eff); font-weight: 900; }\n .swatches { display: flex; gap: 6px; flex-wrap: wrap; margin: 8px 0; }\n .swatch { width: 44px; height: 44px; border-radius: 13px; border: 1px solid rgba(255,255,255,.22); }\n textarea, input[type=\"text\"] { width: 100%; border: 1px solid var(--color-border, #263241); border-radius: 14px; padding: 12px; background: var(--color-bg, #0b0f14); color: var(--color-text, #e8edf2); font: inherit; }\n textarea { min-height: 220px; resize: vertical; font-family: var(--font-mono, ui-monospace, monospace); }\n .columns { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; align-items: start; }\n .column { min-height: 220px; border: 1px dashed var(--color-border, #263241); border-radius: 18px; padding: 12px; background: color-mix(in srgb, var(--color-panel, #111821) 70%, transparent); }\n .ticket { cursor: grab; margin: 10px 0; }\n .ticket:active { cursor: grabbing; }\n .slide { min-height: 390px; display: none; align-content: center; }\n .slide.active { display: grid; }\n .slide h2 { font-size: clamp(30px, 5vw, 58px); line-height: .95; }\n .preview { white-space: pre-wrap; min-height: 220px; }\n .figure svg { width: 100%; min-height: 220px; border-radius: 14px; background: color-mix(in srgb, var(--color-bg, #0b0f14) 86%, white); border: 1px solid var(--color-border, #263241); }\n @media (max-width: 760px) { body { padding: 14px; } header.hero, .two, .three { grid-template-columns: 1fr; } .actions { justify-content: flex-start; } .sticky { position: static; } }\n</style>\n</head>\n<body>\n<div class=\"shell\">\n <header class=\"hero\">\n <div><div class=\"kicker\">PMX HTML primitive / prompt-tuner</div><h1>Board summary prompt</h1><p>Side-by-side prompt/template editor with live variable previews and copy export.</p></div>\n <div class=\"actions\"><button type=\"button\" data-copy-json>Copy JSON</button><button type=\"button\" data-copy-prompt>Copy prompt</button></div>\n </header>\n <section class=\"two\"><div class=\"panel\"><h2>Template</h2><textarea id=\"template\">You are looking at a PMX Canvas board for {{project}}.\n\nSummarise it for a {{audience}} in {{sentences}} sentences. Lead with what the board is FOR, then what changed most recently. Ignore layout and node ids.\n\nPinned nodes are the human’s curation — weight them above everything else.</textarea><p class=\"small\"><span id=\"char-count\">0</span> characters</p></div><div class=\"panel\"><h2>Live Samples</h2><div id=\"previews\"></div><button type=\"button\" data-copy-template>Copy template</button></div></section>\n</div>\n<script type=\"application/json\" id=\"pmx-data\">{\n \"template\": \"You are looking at a PMX Canvas board for {{project}}.\\n\\nSummarise it for a {{audience}} in {{sentences}} sentences. Lead with what the board is FOR, then what changed most recently. Ignore layout and node ids.\\n\\nPinned nodes are the human’s curation — weight them above everything else.\",\n \"samples\": [\n {\n \"name\": \"Standup\",\n \"variables\": {\n \"project\": \"pmx-canvas\",\n \"audience\": \"teammate joining the standup\",\n \"sentences\": \"two\"\n }\n },\n {\n \"name\": \"Exec review\",\n \"variables\": {\n \"project\": \"the 0.4.7 release\",\n \"audience\": \"director who has not seen the tool\",\n \"sentences\": \"three\"\n }\n },\n {\n \"name\": \"Handoff\",\n \"variables\": {\n \"project\": \"the demo board rebuild\",\n \"audience\": \"agent picking the work up cold\",\n \"sentences\": \"four\"\n }\n }\n ]\n}</script>\n<script>\nconst PMX_DATA = JSON.parse(document.getElementById('pmx-data').textContent);\nfunction fallbackCopy(text) {\n const el = document.createElement('textarea');\n el.value = text;\n el.setAttribute('readonly', '');\n el.style.position = 'fixed';\n el.style.left = '-9999px';\n document.body.appendChild(el);\n el.select();\n document.execCommand('copy');\n el.remove();\n}\nasync function copyText(text) {\n if (navigator.clipboard && window.isSecureContext) await navigator.clipboard.writeText(text);\n else fallbackCopy(text);\n}\ndocument.querySelector('[data-copy-json]')?.addEventListener('click', () => {\n const payload = typeof window.__pmxGetCopyJson === 'function' ? window.__pmxGetCopyJson() : PMX_DATA;\n copyText(JSON.stringify(payload, null, 2));\n});\ndocument.querySelector('[data-copy-prompt]')?.addEventListener('click', () => copyText('Use this PMX Canvas HTML primitive output as context:\\n\\n' + JSON.stringify(PMX_DATA, null, 2)));\n\nconst templateEl = document.getElementById('template');\nconst previewsEl = document.getElementById('previews');\nconst samples = PMX_DATA.samples || [];\nfunction fill(template, vars) {\n return template.replace(/{{\\s*([\\w.-]+)\\s*}}/g, (_, key) => vars?.[key] ?? '{{' + key + '}}');\n}\nfunction renderPreviews() {\n if (!templateEl) return;\n const value = templateEl.value;\n document.getElementById('char-count').textContent = String(value.length);\n if (!previewsEl) return;\n previewsEl.innerHTML = samples.map((sample) => '<div class=\"card\"><h3>' + (sample.name || 'Sample') + '</h3><div class=\"preview\">' + fill(value, sample.variables || {}).replace(/[&<>]/g, (c) => ({'&':'&','<':'<','>':'>'}[c])) + '</div></div>').join('');\n}\ntemplateEl?.addEventListener('input', renderPreviews);\nwindow.__pmxGetCopyJson = () => ({ ...PMX_DATA, template: templateEl?.value ?? '' });\ndocument.querySelector('[data-copy-template]')?.addEventListener('click', () => copyText(templateEl.value));\nrenderPreviews();\n</script>\n</body>\n</html>",
|
|
4583
5162
|
"htmlPrimitive": "prompt-tuner",
|
|
4584
5163
|
"primitiveData": {
|
|
4585
5164
|
"template": "You are looking at a PMX Canvas board for {{project}}.\n\nSummarise it for a {{audience}} in {{sentences}} sentences. Lead with what the board is FOR, then what changed most recently. Ignore layout and node ids.\n\nPinned nodes are the human’s curation — weight them above everything else.",
|
|
@@ -4616,6 +5195,15 @@
|
|
|
4616
5195
|
"contentSummary": "Board summary prompt PMX HTML primitive / prompt-tuner Board summary prompt Side-by-side prompt/template editor with live variable previews and copy export. Copy JSON Copy prompt Template You are looking at a PMX Canvas board for {{project}}. Summarise it for a {{audience}} in {{sentences}} sentences. Lead with what the board is FOR, then what changed most recently. Ignore layout and node ids. Pinned nodes are the human’s curation — weight them above everything else. 0 characters Live Samples Copy template",
|
|
4617
5196
|
"agentSummary": "Side-by-side prompt/template editor with live variable previews and copy export."
|
|
4618
5197
|
},
|
|
5198
|
+
"createdBy": {
|
|
5199
|
+
"actor": "agent",
|
|
5200
|
+
"source": "api"
|
|
5201
|
+
},
|
|
5202
|
+
"lastEditedBy": {
|
|
5203
|
+
"actor": "agent",
|
|
5204
|
+
"source": "api"
|
|
5205
|
+
},
|
|
5206
|
+
"contentRevision": 64,
|
|
4619
5207
|
"kind": "html",
|
|
4620
5208
|
"title": "Board summary prompt",
|
|
4621
5209
|
"content": "Side-by-side prompt/template editor with live variable previews and copy export.",
|
|
@@ -4660,6 +5248,15 @@
|
|
|
4660
5248
|
"contentSummary": "Agent board PMX HTML primitive / ax-board Agent board Live agent work board: hand the agent tasks, watch status, steer, and run bounded task loops. Copy JSON Copy prompt AX bridge unavailable This board is not running as an AX-enabled canvas node, so its controls are inert. Give the agent a task Tasks queued here land in the canvas AX work queue, so the agent picks them up without another prompt. Add task or run it as a bounded loop: max runs Start loop Steer Queue steering message Queued for the agent's next turn — steering is recorded on the timeline, it does not wake a running agent. Loop run 0 of 0 Each run is its own work item. When you mark the current run done , the surface opens the next run and steers again — until the cap or Stop. Stop loop Loop control lives in this panel, so reloading it stops the loop — the work items stay as the durable record. Work",
|
|
4661
5249
|
"agentSummary": "Live agent work board: hand the agent tasks, watch status, steer, and run bounded task loops."
|
|
4662
5250
|
},
|
|
5251
|
+
"createdBy": {
|
|
5252
|
+
"actor": "agent",
|
|
5253
|
+
"source": "api"
|
|
5254
|
+
},
|
|
5255
|
+
"lastEditedBy": {
|
|
5256
|
+
"actor": "agent",
|
|
5257
|
+
"source": "api"
|
|
5258
|
+
},
|
|
5259
|
+
"contentRevision": 65,
|
|
4663
5260
|
"kind": "html",
|
|
4664
5261
|
"title": "Agent board",
|
|
4665
5262
|
"content": "Live agent work board: hand the agent tasks, watch status, steer, and run bounded task loops.",
|
|
@@ -4730,11 +5327,20 @@
|
|
|
4730
5327
|
"axflow-primitive-ax-flow-step-4"
|
|
4731
5328
|
],
|
|
4732
5329
|
"contentSummary": "Release flow PMX HTML primitive / ax-flow Release flow Live task-flow control panel: a step diagram with an explicit loop-back, per-step status, and a one-click materialize onto the board. Copy JSON Copy prompt AX bridge unavailable This flow is not running as an AX-enabled canvas node, so its controls are inert. Release flow Each step is a canvas AX work item. Materialize drops the same flow onto the board as real nodes. loops back to step 1 · max 3 runs Run the flow Queue every step A step is queued once — after that its status control advances it (todo → in-progress → done). or run the whole flow as a bounded loop: max runs Start loop Steer Queue steering message Queued for the agent's next turn — steering is recorded on the timeline, it does not wake a running agent. Loop run 0 of 0 Each run is its own work item. When you mark the current run done , the surface opens the next run...",
|
|
4733
|
-
"agentSummary": "Live task-flow control panel: a step diagram with an explicit loop-back, per-step status, and a one-click materialize onto the board."
|
|
5330
|
+
"agentSummary": "HTML primitive: ax-flow\nLive task-flow control panel: a step diagram with an explicit loop-back, per-step status, and a one-click materialize onto the board.\nRelease flow PMX HTML primitive / ax-flow Release flow Live task-flow control panel: a step diagram with an explicit loop-back, per-step status, and a one-click materialize onto the board. Copy JSON Copy prompt AX bridge unavailable This flow is not running as an AX-enabled canvas node, so its controls are inert. Release flow Each step is a canvas AX work item. Materialize drops the same flow onto the board as real nodes. loops back to step 1 · max 3 runs Run the flow Queue every step A step is queued once — after that its status control advances it (todo → in-progress → done). or run the whole flow as a bounded loop: max runs Start loop Steer Queue steering message Queued for the agent's next turn — steering is recorded on the timeline, it does not wake a running agent. Loop run 0 of 0 Each run is its own work item. When you mark the current run done , the surface opens the next run..."
|
|
5331
|
+
},
|
|
5332
|
+
"createdBy": {
|
|
5333
|
+
"actor": "agent",
|
|
5334
|
+
"source": "api"
|
|
5335
|
+
},
|
|
5336
|
+
"lastEditedBy": {
|
|
5337
|
+
"actor": "agent",
|
|
5338
|
+
"source": "api"
|
|
4734
5339
|
},
|
|
5340
|
+
"contentRevision": 77,
|
|
4735
5341
|
"kind": "html",
|
|
4736
5342
|
"title": "Release flow",
|
|
4737
|
-
"content": "Live task-flow control panel: a step diagram with an explicit loop-back, per-step status, and a one-click materialize onto the board.",
|
|
5343
|
+
"content": "HTML primitive: ax-flow\nLive task-flow control panel: a step diagram with an explicit loop-back, per-step status, and a one-click materialize onto the board.\nRelease flow PMX HTML primitive / ax-flow Release flow Live task-flow control panel: a step diagram with an explicit loop-back, per-step status, and a one-click materialize onto the board. Copy JSON Copy prompt AX bridge unavailable This flow is not running as an AX-enabled canvas node, so its controls are inert. Release flow Each step is a canvas AX work item. Materialize drops the same flow onto the board as real nodes. loops back to step 1 · max 3 runs Run the flow Queue every step A step is queued once — after that its status control advances it (todo → in-progress → done). or run the whole flow as a bounded loop: max runs Start loop Steer Queue steering message Queued for the agent's next turn — steering is recorded on the timeline, it does not wake a running agent. Loop run 0 of 0 Each run is its own work item. When you mark the current run done , the surface opens the next run...",
|
|
4738
5344
|
"path": null,
|
|
4739
5345
|
"url": null,
|
|
4740
5346
|
"surfaceUrl": "/api/canvas/surface/primitive-ax-flow",
|
|
@@ -4759,6 +5365,15 @@
|
|
|
4759
5365
|
"content": "A task caught mid-flight: one work item per step, status chips on the nodes, controls that steer the agent.",
|
|
4760
5366
|
"strictSize": true
|
|
4761
5367
|
},
|
|
5368
|
+
"createdBy": {
|
|
5369
|
+
"actor": "agent",
|
|
5370
|
+
"source": "api"
|
|
5371
|
+
},
|
|
5372
|
+
"lastEditedBy": {
|
|
5373
|
+
"actor": "agent",
|
|
5374
|
+
"source": "api"
|
|
5375
|
+
},
|
|
5376
|
+
"contentRevision": 67,
|
|
4762
5377
|
"kind": "markdown",
|
|
4763
5378
|
"title": "⑧ Agent at work",
|
|
4764
5379
|
"content": "A task caught mid-flight: one work item per step, status chips on the nodes, controls that steer the agent.",
|
|
@@ -4831,6 +5446,15 @@
|
|
|
4831
5446
|
},
|
|
4832
5447
|
"strictSize": true
|
|
4833
5448
|
},
|
|
5449
|
+
"createdBy": {
|
|
5450
|
+
"actor": "agent",
|
|
5451
|
+
"source": "api"
|
|
5452
|
+
},
|
|
5453
|
+
"lastEditedBy": {
|
|
5454
|
+
"actor": "agent",
|
|
5455
|
+
"source": "api"
|
|
5456
|
+
},
|
|
5457
|
+
"contentRevision": 78,
|
|
4834
5458
|
"kind": "markdown",
|
|
4835
5459
|
"title": "1. Reproduce",
|
|
4836
5460
|
"content": "Ran the publish gate 20× in a loop. Fails 3 in 20, always on the registry probe.",
|
|
@@ -4868,6 +5492,15 @@
|
|
|
4868
5492
|
},
|
|
4869
5493
|
"strictSize": true
|
|
4870
5494
|
},
|
|
5495
|
+
"createdBy": {
|
|
5496
|
+
"actor": "agent",
|
|
5497
|
+
"source": "api"
|
|
5498
|
+
},
|
|
5499
|
+
"lastEditedBy": {
|
|
5500
|
+
"actor": "agent",
|
|
5501
|
+
"source": "api"
|
|
5502
|
+
},
|
|
5503
|
+
"contentRevision": 79,
|
|
4871
5504
|
"kind": "markdown",
|
|
4872
5505
|
"title": "2. Fix",
|
|
4873
5506
|
"content": "Retry the probe with backoff instead of failing the gate on the first timeout.",
|
|
@@ -4905,6 +5538,15 @@
|
|
|
4905
5538
|
},
|
|
4906
5539
|
"strictSize": true
|
|
4907
5540
|
},
|
|
5541
|
+
"createdBy": {
|
|
5542
|
+
"actor": "agent",
|
|
5543
|
+
"source": "api"
|
|
5544
|
+
},
|
|
5545
|
+
"lastEditedBy": {
|
|
5546
|
+
"actor": "agent",
|
|
5547
|
+
"source": "api"
|
|
5548
|
+
},
|
|
5549
|
+
"contentRevision": 80,
|
|
4908
5550
|
"kind": "markdown",
|
|
4909
5551
|
"title": "3. Verify",
|
|
4910
5552
|
"content": "Re-run the same 20× loop and require 20 green.",
|
|
@@ -4942,6 +5584,15 @@
|
|
|
4942
5584
|
},
|
|
4943
5585
|
"strictSize": true
|
|
4944
5586
|
},
|
|
5587
|
+
"createdBy": {
|
|
5588
|
+
"actor": "agent",
|
|
5589
|
+
"source": "api"
|
|
5590
|
+
},
|
|
5591
|
+
"lastEditedBy": {
|
|
5592
|
+
"actor": "agent",
|
|
5593
|
+
"source": "api"
|
|
5594
|
+
},
|
|
5595
|
+
"contentRevision": 81,
|
|
4945
5596
|
"kind": "markdown",
|
|
4946
5597
|
"title": "4. Ship",
|
|
4947
5598
|
"content": "Land the patch, then watch the next three releases.",
|
|
@@ -4969,6 +5620,15 @@
|
|
|
4969
5620
|
"content": "Each step is a real **AX work item**, so the chip on a step node is the\nagent’s live status — it moves as the agent works.\n\nThe buttons in each step footer steer it: **Start**, **Done** and\n**Blocked** write that step’s work item, which the agent reads back from\n`canvas://ax-work`.\n\nStep 2 is pinned, so `canvas://pinned-context` hands the agent the\nstep it is actually on.",
|
|
4970
5621
|
"strictSize": true
|
|
4971
5622
|
},
|
|
5623
|
+
"createdBy": {
|
|
5624
|
+
"actor": "agent",
|
|
5625
|
+
"source": "api"
|
|
5626
|
+
},
|
|
5627
|
+
"lastEditedBy": {
|
|
5628
|
+
"actor": "agent",
|
|
5629
|
+
"source": "api"
|
|
5630
|
+
},
|
|
5631
|
+
"contentRevision": 82,
|
|
4972
5632
|
"kind": "markdown",
|
|
4973
5633
|
"title": "How to read this band",
|
|
4974
5634
|
"content": "Each step is a real **AX work item**, so the chip on a step node is the\nagent’s live status — it moves as the agent works.\n\nThe buttons in each step footer steer it: **Start**, **Done** and\n**Blocked** write that step’s work item, which the agent reads back from\n`canvas://ax-work`.\n\nStep 2 is pinned, so `canvas://pinned-context` hands the agent the\nstep it is actually on.",
|
|
@@ -5151,7 +5811,8 @@
|
|
|
5151
5811
|
"systemAppend": null,
|
|
5152
5812
|
"mode": null
|
|
5153
5813
|
},
|
|
5154
|
-
"scope": null
|
|
5814
|
+
"scope": null,
|
|
5815
|
+
"agentScopes": {}
|
|
5155
5816
|
}
|
|
5156
5817
|
}
|
|
5157
5818
|
}
|