pmx-canvas 0.6.3 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/Readme.md +27 -6
- package/dist/canvas/global.css +90 -4
- package/dist/canvas/index.js +93 -91
- package/dist/canvas/mermaid-entry.js +258 -258
- package/dist/types/cli/agent.d.ts +1 -0
- package/dist/types/cli/commands/record.d.ts +12 -0
- package/dist/types/client/canvas/FeedbackDialog.d.ts +4 -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/McpAppNode.d.ts +24 -1
- package/dist/types/client/state/presentation.d.ts +3 -0
- package/dist/types/client/types.d.ts +1 -1
- package/dist/types/json-render/catalog.d.ts +2 -2
- package/dist/types/json-render/server.d.ts +2 -0
- package/dist/types/server/canvas-db.d.ts +2 -0
- package/dist/types/server/canvas-operations.d.ts +22 -0
- package/dist/types/server/canvas-schema.d.ts +1 -0
- package/dist/types/server/canvas-state.d.ts +6 -0
- package/dist/types/server/canvas-validation.d.ts +3 -0
- package/dist/types/server/html-surface.d.ts +1 -1
- package/dist/types/server/index.d.ts +4 -0
- package/dist/types/server/operations/ops/app.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 +3 -1
- package/dist/types/server/web-artifacts.d.ts +3 -0
- package/dist/types/shared/tour.d.ts +72 -0
- package/docs/cli.md +96 -2
- package/docs/http-api.md +79 -4
- package/docs/mcp.md +25 -1
- package/docs/node-types.md +123 -1
- package/docs/product-context-vision-2026-09.md +1 -1
- package/docs/product-vision-2026-09.md +135 -42
- package/docs/product-vision-review-2026-09.md +277 -0
- package/package.json +1 -1
- package/skills/pmx-canvas/SKILL.md +19 -4
- package/skills/pmx-canvas/evals/evals.json +46 -0
- package/skills/pmx-canvas/references/full-reference.md +1 -1
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +4 -4
- package/src/cli/agent.ts +5 -0
- package/src/cli/commands/nodes.ts +10 -0
- package/src/cli/commands/record.ts +218 -0
- package/src/cli/index.ts +2 -0
- package/src/client/App.tsx +7 -1
- package/src/client/canvas/AgentPresenceLayer.tsx +1 -1
- package/src/client/canvas/CanvasNode.tsx +8 -0
- package/src/client/canvas/FeedbackDialog.tsx +86 -0
- package/src/client/canvas/IntentLayer.tsx +9 -2
- package/src/client/canvas/Presentation.tsx +108 -0
- package/src/client/canvas/ToolRail.tsx +225 -189
- package/src/client/canvas/TopBar.tsx +28 -2
- package/src/client/canvas/use-node-drag.ts +8 -3
- package/src/client/mermaid-entry.ts +39 -0
- package/src/client/nodes/HtmlNode.tsx +12 -13
- package/src/client/nodes/McpAppNode.tsx +105 -13
- package/src/client/nodes/MermaidNode.tsx +3 -2
- package/src/client/state/intent-store.ts +5 -1
- package/src/client/state/presentation.ts +7 -0
- package/src/client/state/session-store.ts +3 -0
- package/src/client/state/sse-bridge.ts +3 -0
- package/src/client/theme/global.css +90 -4
- package/src/json-render/server.ts +29 -0
- package/src/server/agent-presence.ts +15 -9
- package/src/server/canvas-db.ts +15 -0
- package/src/server/canvas-operations.ts +115 -22
- package/src/server/canvas-schema.ts +10 -0
- package/src/server/canvas-state.ts +39 -4
- package/src/server/canvas-validation.ts +9 -1
- package/src/server/demo-state.json +11 -7
- package/src/server/html-primitives.ts +199 -91
- package/src/server/html-surface.ts +8 -3
- package/src/server/index.ts +12 -0
- package/src/server/operations/composites.ts +3 -1
- package/src/server/operations/ops/app.ts +10 -0
- package/src/server/operations/ops/batch.ts +12 -3
- package/src/server/operations/ops/groups.ts +14 -7
- package/src/server/operations/ops/json-render.ts +37 -5
- package/src/server/operations/ops/nodes.ts +42 -16
- package/src/server/operations/ops/viewport.ts +32 -0
- package/src/server/operations/registry.ts +6 -0
- package/src/server/scope-fence.ts +1 -1
- package/src/server/server.ts +9 -6
- package/src/server/web-artifacts.ts +9 -2
- package/src/server/workboard.ts +3 -3
- package/src/shared/tour.ts +87 -0
|
@@ -345,17 +345,19 @@
|
|
|
345
345
|
"collapsed": false,
|
|
346
346
|
"pinned": false,
|
|
347
347
|
"data": {
|
|
348
|
+
"path": "/workspace/reports/weekly-throughput.csv",
|
|
348
349
|
"title": "weekly-throughput.csv",
|
|
349
|
-
"
|
|
350
|
+
"byteSize": 197,
|
|
351
|
+
"updatedAt": "2026-08-01T09:00:00.000Z",
|
|
350
352
|
"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
353
|
"lineCount": 7,
|
|
352
354
|
"strictSize": true,
|
|
353
|
-
"path": "/workspace/reports/weekly-throughput.csv",
|
|
354
355
|
"provenance": {
|
|
355
356
|
"sourceKind": "workspace-file",
|
|
356
357
|
"sourceUri": "file:///workspace/reports/weekly-throughput.csv",
|
|
357
358
|
"refreshStrategy": "file-watch",
|
|
358
359
|
"snapshotContent": true,
|
|
360
|
+
"syncedAt": "2026-08-01T09:00:00.000Z",
|
|
359
361
|
"details": {
|
|
360
362
|
"path": "/workspace/reports/weekly-throughput.csv",
|
|
361
363
|
"nodeType": "file"
|
|
@@ -373,6 +375,7 @@
|
|
|
373
375
|
"sourceUri": "file:///workspace/reports/weekly-throughput.csv",
|
|
374
376
|
"refreshStrategy": "file-watch",
|
|
375
377
|
"snapshotContent": true,
|
|
378
|
+
"syncedAt": "2026-08-01T09:00:00.000Z",
|
|
376
379
|
"details": {
|
|
377
380
|
"path": "/workspace/reports/weekly-throughput.csv",
|
|
378
381
|
"nodeType": "file"
|
|
@@ -2975,7 +2978,7 @@
|
|
|
2975
2978
|
"collapsed": false,
|
|
2976
2979
|
"pinned": false,
|
|
2977
2980
|
"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>",
|
|
2981
|
+
"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
2982
|
"htmlPrimitive": "plan-timeline",
|
|
2980
2983
|
"primitiveData": {
|
|
2981
2984
|
"milestones": [
|
|
@@ -3782,7 +3785,7 @@
|
|
|
3782
3785
|
"collapsed": false,
|
|
3783
3786
|
"pinned": false,
|
|
3784
3787
|
"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>",
|
|
3788
|
+
"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
3789
|
"htmlPrimitive": "deck",
|
|
3787
3790
|
"primitiveData": {
|
|
3788
3791
|
"slides": [
|
|
@@ -3868,7 +3871,7 @@
|
|
|
3868
3871
|
"collapsed": false,
|
|
3869
3872
|
"pinned": false,
|
|
3870
3873
|
"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>",
|
|
3874
|
+
"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
3875
|
"htmlPrimitive": "presentation",
|
|
3873
3876
|
"primitiveData": {
|
|
3874
3877
|
"subtitle": "What shipped, what it costs, and what we do next.",
|
|
@@ -4579,7 +4582,7 @@
|
|
|
4579
4582
|
"collapsed": false,
|
|
4580
4583
|
"pinned": false,
|
|
4581
4584
|
"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>",
|
|
4585
|
+
"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
4586
|
"htmlPrimitive": "prompt-tuner",
|
|
4584
4587
|
"primitiveData": {
|
|
4585
4588
|
"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.",
|
|
@@ -5151,7 +5154,8 @@
|
|
|
5151
5154
|
"systemAppend": null,
|
|
5152
5155
|
"mode": null
|
|
5153
5156
|
},
|
|
5154
|
-
"scope": null
|
|
5157
|
+
"scope": null,
|
|
5158
|
+
"agentScopes": {}
|
|
5155
5159
|
}
|
|
5156
5160
|
}
|
|
5157
5161
|
}
|