pmx-canvas 0.11.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/extensions/pmx-canvas/extension.mjs +4 -1
- package/.github/extensions/pmx-canvas/runtime.json +1 -1
- package/.github/extensions/pmx-canvas/tools.json +440 -18
- package/CHANGELOG.md +51 -0
- package/dist/amp-plugin/pmx-canvas/LICENSE +21 -0
- package/dist/amp-plugin/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
- package/dist/amp-plugin/pmx-canvas/index.js +5445 -0
- package/dist/amp-plugin/pmx-canvas/runtime.json +3 -0
- package/dist/amp-plugin/pmx-canvas/shared-runtime.mjs +194 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/SKILL.md +267 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/evals.json +1168 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/jwt.ts +17 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/login.ts +12 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/middleware.ts +13 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/routes/auth.ts +13 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/investigation-board/src/handlers/users.ts +27 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/mcp.json +36 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/ax-html-control-surface.md +101 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/codex-app-adapter.md +151 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/excalidraw-diagram-authoring.md +150 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/full-reference.md +1839 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/github-copilot-app-adapter.md +223 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/html-primitives.md +139 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/importing-context.md +132 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/installing-pmx-canvas.md +301 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/tours-and-recording.md +117 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/workbench-workflows.md +908 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas-orchestration/SKILL.md +141 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas-testing/SKILL.md +266 -0
- package/dist/amp-plugin/pmx-canvas/start-mcp.mjs +14857 -0
- package/dist/amp-plugin/pmx-canvas/tools.json +10963 -0
- package/dist/canvas/chunks/{abnfDiagram-N423BO3Z-p8dtp6va.js → abnfDiagram-N423BO3Z-cwtsds5d.js} +1 -1
- package/dist/canvas/chunks/architecture-TIHT7OUA-x63cwejs.js +1 -0
- package/dist/canvas/chunks/{architectureDiagram-T3A2C74G-5jhmxrzf.js → architectureDiagram-T3A2C74G-b4xc9tpz.js} +3 -3
- package/dist/canvas/chunks/{blockDiagram-VBNYF7ZC-pw1fa9yb.js → blockDiagram-VBNYF7ZC-ahp3yxxs.js} +7 -7
- package/dist/canvas/chunks/{c4Diagram-5PPSVZJV-x07nq0tt.js → c4Diagram-5PPSVZJV-nappnay7.js} +4 -4
- package/dist/canvas/chunks/classDiagram-JCYQIIEL-k1j2xc1t.js +1 -0
- package/dist/canvas/chunks/classDiagram-v2-OCEON4UE-33x08xf2.js +1 -0
- package/dist/canvas/chunks/{cose-bilkent-JH36ORCC-nm39npq0.js → cose-bilkent-JH36ORCC-a428syda.js} +1 -1
- package/dist/canvas/chunks/cynefin-VYW2F7L2-8v6jxfz8.js +1 -0
- package/dist/canvas/chunks/{cynefinDiagram-MW4NZA55-vdxrwahz.js → cynefinDiagram-MW4NZA55-nspsqebj.js} +1 -1
- package/dist/canvas/chunks/{dagre-VZM6K2ZE-bqdm7wpd.js → dagre-VZM6K2ZE-8y6177fh.js} +2 -2
- package/dist/canvas/chunks/{diagram-7IWD3JNH-gs5gpr93.js → diagram-7IWD3JNH-vym028wn.js} +4 -4
- package/dist/canvas/chunks/{diagram-B4RE2ZJO-cgwjg47s.js → diagram-B4RE2ZJO-jddqb8sy.js} +1 -1
- package/dist/canvas/chunks/{diagram-LBJQPF4R-w3vahpav.js → diagram-LBJQPF4R-bjwbpkrn.js} +1 -1
- package/dist/canvas/chunks/{diagram-Q27KOJAE-42837z6h.js → diagram-Q27KOJAE-j6ph6hcx.js} +2 -2
- package/dist/canvas/chunks/{diagram-UB23O5K3-4fz83q59.js → diagram-UB23O5K3-p09w8jej.js} +1 -1
- package/dist/canvas/chunks/{ebnfDiagram-BXEA7PRR-zphtgegj.js → ebnfDiagram-BXEA7PRR-j975ycq5.js} +1 -1
- package/dist/canvas/chunks/{erDiagram-JOGREHBK-zvbbdxp4.js → erDiagram-JOGREHBK-sxp6hjn8.js} +5 -5
- package/dist/canvas/chunks/eventmodeling-45OFAUF4-xzfgn6xg.js +1 -0
- package/dist/canvas/chunks/flowDiagram-UKHOOZJN-2nsmkskb.js +1 -0
- package/dist/canvas/chunks/{ganttDiagram-PKOTCBZU-282jzycm.js → ganttDiagram-PKOTCBZU-4tv230y2.js} +2 -2
- package/dist/canvas/chunks/gitGraph-TEB2WS4Q-macbhand.js +1 -0
- package/dist/canvas/chunks/{gitGraphDiagram-DS77QQ5N-vqqy6p1b.js → gitGraphDiagram-DS77QQ5N-mqgmnw9g.js} +1 -1
- package/dist/canvas/chunks/{index-wgv7en21.js → index-037z5dz2.js} +2 -2
- package/dist/canvas/chunks/{index-w2mra17t.js → index-081n3har.js} +1 -1
- package/dist/canvas/chunks/{index-353m7b0p.js → index-5n03dmb9.js} +1 -1
- package/dist/canvas/chunks/{index-yw8h9vha.js → index-60pgzdqe.js} +1 -1
- package/dist/canvas/chunks/index-8g1vb57n.js +2 -0
- package/dist/canvas/chunks/{index-vv56h019.js → index-8rjes9r6.js} +1 -1
- package/dist/canvas/chunks/{index-nprm96xp.js → index-9022d1n9.js} +1 -1
- package/dist/canvas/chunks/{index-194m3hc3.js → index-9b02042t.js} +1 -1
- package/dist/canvas/chunks/{index-0rk4gp1g.js → index-ap4q4vez.js} +1 -1
- package/dist/canvas/chunks/{index-rh7nv68n.js → index-dyp2nhd3.js} +1 -1
- package/dist/canvas/chunks/index-frvta4tm.js +2 -0
- package/dist/canvas/chunks/{index-s4j678gj.js → index-h9ycj8n2.js} +1 -1
- package/dist/canvas/chunks/{index-fjtdggjn.js → index-mergmj97.js} +2 -2
- package/dist/canvas/chunks/index-nwsya6dj.js +301 -0
- package/dist/canvas/chunks/{index-3cy1bzwh.js → index-nz8qbd1p.js} +1 -1
- package/dist/canvas/chunks/{index-d3b3gqe9.js → index-qhfrazv3.js} +1 -1
- package/dist/canvas/chunks/{index-6qmsnxwy.js → index-v119nznk.js} +4 -4
- package/dist/canvas/chunks/{index-r2fyc9w1.js → index-vpt37m3d.js} +6 -6
- package/dist/canvas/chunks/{index-qj8fxwsf.js → index-y9ma6azy.js} +1 -1
- package/dist/canvas/chunks/{index-tb1wf525.js → index-zx0j3a2z.js} +26 -26
- package/dist/canvas/chunks/info-DKCQHKI2-a0ykzs9z.js +1 -0
- package/dist/canvas/chunks/{infoDiagram-6WML65LV-v1yvggqj.js → infoDiagram-6WML65LV-qv97qq6a.js} +2 -2
- package/dist/canvas/chunks/ishikawaDiagram-WSZJBQD7-p9306gh0.js +70 -0
- package/dist/canvas/chunks/{journeyDiagram-NVQOT4AX-zjcffveh.js → journeyDiagram-NVQOT4AX-sv67zats.js} +5 -5
- package/dist/canvas/chunks/{kanban-definition-27J2QSJJ-dakqtfp1.js → kanban-definition-27J2QSJJ-k2pfam6p.js} +1 -1
- package/dist/canvas/chunks/{mermaid.core-j2geswrd.js → mermaid.core-83r0fdce.js} +4 -4
- package/dist/canvas/chunks/{mindmap-definition-FAOFIHXS-142xhr3k.js → mindmap-definition-FAOFIHXS-vg2newm1.js} +1 -1
- package/dist/canvas/chunks/packet-7NZHBO7P-tqnabcdv.js +1 -0
- package/dist/canvas/chunks/{pegDiagram-VL7TDLO6-5wvk7vsd.js → pegDiagram-VL7TDLO6-k2h7zc5n.js} +1 -1
- package/dist/canvas/chunks/pie-RZYD4A2V-wke6gyhe.js +1 -0
- package/dist/canvas/chunks/{pieDiagram-7S7Q4E2Y-ztdhwycc.js → pieDiagram-7S7Q4E2Y-hg169p1v.js} +2 -2
- package/dist/canvas/chunks/{quadrantDiagram-CIZ2JOQS-c5p9gmg6.js → quadrantDiagram-CIZ2JOQS-bxtvsq0b.js} +5 -5
- package/dist/canvas/chunks/radar-I7S5WNFK-w7ka0drw.js +1 -0
- package/dist/canvas/chunks/railroad-3IZDKUUU-y3vg318x.js +1 -0
- package/dist/canvas/chunks/railroad-abnf-AHOZXSZD-6hqvcz3c.js +1 -0
- package/dist/canvas/chunks/railroad-ebnf-EBAXGLYW-vcae260m.js +1 -0
- package/dist/canvas/chunks/railroad-peg-LSFZ7HO6-0dbyme23.js +1 -0
- package/dist/canvas/chunks/{railroadDiagram-AXF67PYL-xkmqkjwf.js → railroadDiagram-AXF67PYL-njyhcp4p.js} +1 -1
- package/dist/canvas/chunks/{requirementDiagram-LRYGKXZP-9wm2dyaw.js → requirementDiagram-LRYGKXZP-2z6v09gm.js} +4 -4
- package/dist/canvas/chunks/{sankeyDiagram-W5VNT64P-2w19etgb.js → sankeyDiagram-W5VNT64P-15mzam0e.js} +1 -1
- package/dist/canvas/chunks/{sequenceDiagram-SI44F4Z6-0jn0vwmz.js → sequenceDiagram-SI44F4Z6-rbshyd2c.js} +12 -12
- package/dist/canvas/chunks/{sizeCapture-X5ZJPWSS-x75epwdq.js → sizeCapture-X5ZJPWSS-sg26xwts.js} +1 -1
- package/dist/canvas/chunks/{stateDiagram-OKZ733FA-schjpy6d.js → stateDiagram-OKZ733FA-6eahp98e.js} +1 -1
- package/dist/canvas/chunks/stateDiagram-v2-UEYNNEHI-tty0ggn5.js +1 -0
- package/dist/canvas/chunks/{swimlanes-SLNWSIFB-gkw4xce3.js → swimlanes-SLNWSIFB-ze5db10t.js} +1 -1
- package/dist/canvas/chunks/swimlanesDiagram-ULZ7WXOC-thze75gq.js +8 -0
- package/dist/canvas/chunks/{timeline-definition-Z64GVDOM-x7nkcy01.js → timeline-definition-Z64GVDOM-vnp7jyy8.js} +3 -3
- package/dist/canvas/chunks/treeView-QDETBFTQ-6ysffnm6.js +1 -0
- package/dist/canvas/chunks/treemap-6X3UGDF4-m3h9p066.js +1 -0
- package/dist/canvas/chunks/vennDiagram-T6HMQDX7-0qrnb7mj.js +34 -0
- package/dist/canvas/chunks/wardley-OPB4EBWU-dp37drwx.js +1 -0
- package/dist/canvas/chunks/{wardleyDiagram-T6FBY63Y-t1j52t9r.js → wardleyDiagram-T6FBY63Y-8nxg8wqt.js} +4 -4
- package/dist/canvas/chunks/{xychartDiagram-ELKLHX3M-38t16pme.js → xychartDiagram-ELKLHX3M-nj921e7s.js} +5 -5
- package/dist/canvas/global.css +94 -1
- package/dist/canvas/index.js +154 -145
- package/dist/canvas/mcp-app.html +600 -498
- package/dist/json-render/index.css +1 -1
- package/dist/types/client/canvas/BoardMoveDialog.d.ts +2 -0
- package/dist/types/client/canvas/EdgeEditor.d.ts +19 -0
- package/dist/types/client/canvas/GroupNavigation.d.ts +2 -0
- package/dist/types/client/canvas/TextPrompt.d.ts +4 -0
- package/dist/types/client/nodes/PromptNode.d.ts +1 -0
- package/dist/types/client/nodes/ViewerFrame.d.ts +1 -1
- package/dist/types/client/nodes/WorkCard.d.ts +7 -0
- package/dist/types/client/nodes/use-ax-surface-bridge.d.ts +1 -0
- package/dist/types/client/state/canvas-store.d.ts +15 -0
- package/dist/types/client/state/human-store.d.ts +2 -0
- package/dist/types/client/state/intent-bridge.d.ts +4 -2
- package/dist/types/client/state/relation-store.d.ts +11 -0
- package/dist/types/client/types.d.ts +1 -1
- package/dist/types/json-render/catalog.d.ts +2 -2
- package/dist/types/server/agent-presence.d.ts +1 -0
- package/dist/types/server/ax-interaction.d.ts +1 -0
- package/dist/types/server/ax-state-manager.d.ts +10 -2
- package/dist/types/server/board-local.d.ts +4 -0
- package/dist/types/server/board-move.d.ts +38 -0
- package/dist/types/server/canvas-db.d.ts +12 -2
- package/dist/types/server/canvas-operations.d.ts +5 -6
- package/dist/types/server/canvas-provenance.d.ts +1 -1
- package/dist/types/server/canvas-serialization.d.ts +1 -0
- package/dist/types/server/canvas-state.d.ts +84 -20
- package/dist/types/server/card-relocation.d.ts +6 -0
- package/dist/types/server/context-reads.d.ts +3 -1
- package/dist/types/server/human-presence.d.ts +1 -1
- package/dist/types/server/index.d.ts +16 -5
- package/dist/types/server/intent-registry.d.ts +8 -4
- package/dist/types/server/knowledge-lifecycle.d.ts +4 -2
- package/dist/types/server/mutation-history.d.ts +7 -5
- package/dist/types/server/operations/registry.d.ts +2 -0
- package/dist/types/server/trace-manager.d.ts +1 -0
- package/dist/types/shared/ax-kinds.d.ts +6 -0
- package/dist/types/shared/ax-surface-protocol.d.ts +8 -0
- package/dist/types/shared/human-presence.d.ts +1 -0
- package/dist/types/shared/lifecycle-status.d.ts +2 -0
- package/docs/RELEASE.md +21 -14
- package/docs/agent-plugin.md +22 -2
- package/docs/amp-plugin.md +270 -0
- package/docs/cli.md +42 -3
- package/docs/design/GuestName.dc.html +50 -0
- package/docs/design/PluginLogo.svg +7 -0
- package/docs/design/WorkCards.dc.html +39 -0
- package/docs/design/canvas.json +16 -0
- package/docs/design/plugin-logo.png +0 -0
- package/docs/design.md +111 -8
- package/docs/evals/agents-md-sections.md +38 -0
- package/docs/http-api.md +128 -10
- package/docs/mcp.md +61 -9
- package/docs/node-types.md +71 -1
- package/docs/plans/plan-009-tech-debt-backlog.md +1 -1
- package/docs/plans/plan-019-post-0.9-roadmap.md +44 -20
- package/docs/plans/plan-020-wave-5-work-planning.md +553 -0
- package/docs/product-vision-2026-09.md +33 -17
- package/docs/sdk.md +18 -0
- package/package.json +6 -3
- package/plugins/pmx-canvas/assets/preview.png +0 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/extension.mjs +4 -1
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
- package/plugins/pmx-canvas/plugin.json +2 -2
- package/plugins/pmx-canvas/runtime.json +1 -1
- package/plugins/pmx-canvas/tools.json +440 -18
- package/skills/data-analysis/SKILL.md +13 -26
- package/skills/frontend-design/LICENSE.txt +177 -0
- package/skills/frontend-design/SKILL.md +3 -0
- package/skills/pmx-canvas/SKILL.md +248 -842
- package/skills/pmx-canvas/evals/evals.json +110 -5
- package/skills/pmx-canvas/references/full-reference.md +142 -28
- package/skills/pmx-canvas/references/html-primitives.md +7 -0
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +61 -11
- package/skills/pmx-canvas/references/tours-and-recording.md +39 -6
- package/skills/pmx-canvas/references/workbench-workflows.md +908 -0
- package/skills/pmx-canvas-testing/SKILL.md +7 -5
- package/skills/published-consumer-e2e/SKILL.md +25 -8
- package/skills/published-consumer-e2e/scripts/run-published-consumer-e2e.sh +51 -75
- package/skills/web-artifacts-builder/LICENSE.txt +202 -0
- package/skills/web-artifacts-builder/SKILL.md +6 -0
- package/skills/web-artifacts-builder/scripts/bundle-artifact.sh +2 -2
- package/skills/web-artifacts-builder/scripts/init-artifact.sh +8 -8
- package/src/cli/commands/apps.ts +1 -0
- package/src/cli/commands/boards.ts +30 -0
- package/src/client/App.tsx +20 -4
- package/src/client/canvas/AttentionHistory.tsx +10 -2
- package/src/client/canvas/BoardMoveDialog.tsx +225 -0
- package/src/client/canvas/CanvasNode.tsx +45 -2
- package/src/client/canvas/CanvasViewport.tsx +38 -9
- package/src/client/canvas/ContextMenu.tsx +34 -12
- package/src/client/canvas/EdgeEditor.tsx +149 -0
- package/src/client/canvas/EdgeLayer.tsx +87 -16
- package/src/client/canvas/ExpandedNodeOverlay.tsx +4 -0
- package/src/client/canvas/GroupNavigation.tsx +85 -0
- package/src/client/canvas/RelationsPanel.tsx +9 -0
- package/src/client/canvas/SelectionBar.tsx +21 -0
- package/src/client/canvas/SessionPanel.tsx +35 -1
- package/src/client/canvas/TextPrompt.tsx +18 -1
- package/src/client/canvas/TopBar.tsx +30 -0
- package/src/client/canvas/canvas-area.ts +1 -1
- package/src/client/nodes/ExtAppFrame.tsx +26 -10
- package/src/client/nodes/MarkdownNode.tsx +6 -7
- package/src/client/nodes/PromptNode.tsx +32 -3
- package/src/client/nodes/StatusNode.tsx +12 -0
- package/src/client/nodes/StatusSummary.tsx +11 -0
- package/src/client/nodes/ViewerFrame.tsx +21 -10
- package/src/client/nodes/WorkCard.tsx +175 -0
- package/src/client/nodes/use-ax-surface-bridge.ts +18 -14
- package/src/client/state/attention-bridge.ts +5 -0
- package/src/client/state/attention-store.ts +1 -0
- package/src/client/state/canvas-store.ts +185 -15
- package/src/client/state/human-store.ts +22 -2
- package/src/client/state/intent-bridge.ts +19 -5
- package/src/client/state/relation-store.ts +127 -1
- package/src/client/theme/global.css +94 -1
- package/src/client/types.ts +1 -0
- package/src/mcp/server.ts +13 -3
- package/src/server/agent-presence.ts +43 -23
- package/src/server/ax-flow-loop.ts +6 -0
- package/src/server/ax-gate-ttl.ts +8 -1
- package/src/server/ax-interaction.ts +16 -10
- package/src/server/ax-state-manager.ts +17 -4
- package/src/server/ax-wait.ts +20 -6
- package/src/server/board-export.ts +19 -6
- package/src/server/board-local.ts +20 -0
- package/src/server/board-move.ts +297 -0
- package/src/server/canvas-db.ts +262 -160
- package/src/server/canvas-operations.ts +139 -109
- package/src/server/canvas-schema.ts +21 -0
- package/src/server/canvas-serialization.ts +2 -0
- package/src/server/canvas-state.ts +792 -159
- package/src/server/card-relocation.ts +53 -0
- package/src/server/code-graph.ts +4 -3
- package/src/server/context-reads.ts +83 -55
- package/src/server/demo-state.json +35 -15
- package/src/server/file-watcher.ts +30 -10
- package/src/server/human-presence.ts +18 -5
- package/src/server/index.ts +35 -15
- package/src/server/intent-registry.ts +43 -5
- package/src/server/knowledge-lifecycle.ts +32 -9
- package/src/server/lifecycle-status.ts +29 -1
- package/src/server/mcp-app-runtime.ts +26 -7
- package/src/server/mutation-history.ts +37 -8
- package/src/server/operations/composites.ts +6 -3
- package/src/server/operations/mcp-contracts.ts +15 -0
- package/src/server/operations/mcp.ts +36 -9
- package/src/server/operations/ops/annotation.ts +22 -1
- package/src/server/operations/ops/ax-await.ts +1 -0
- package/src/server/operations/ops/ax-timeline.ts +28 -2
- package/src/server/operations/ops/batch.ts +25 -11
- package/src/server/operations/ops/boards.ts +44 -3
- package/src/server/operations/ops/nodes.ts +70 -0
- package/src/server/operations/ops/query.ts +32 -3
- package/src/server/operations/ops/snapshots.ts +1 -0
- package/src/server/operations/registry.ts +49 -4
- package/src/server/server.ts +62 -16
- package/src/server/tour-control.ts +37 -12
- package/src/server/trace-manager.ts +4 -0
- package/src/server/web-artifacts/scripts/bundle-artifact.sh +2 -2
- package/src/server/web-artifacts/scripts/init-artifact.sh +8 -8
- package/src/shared/agent-context.ts +7 -1
- package/src/shared/auto-arrange.ts +5 -4
- package/src/shared/ax-kinds.ts +13 -0
- package/src/shared/ax-surface-protocol.ts +22 -0
- package/src/shared/human-presence.ts +1 -0
- package/src/shared/lifecycle-status.ts +2 -0
- package/dist/canvas/chunks/architecture-TIHT7OUA-9tffvpsv.js +0 -1
- package/dist/canvas/chunks/classDiagram-JCYQIIEL-nc6g5x1r.js +0 -1
- package/dist/canvas/chunks/classDiagram-v2-OCEON4UE-0qnbpwvh.js +0 -1
- package/dist/canvas/chunks/cynefin-VYW2F7L2-ys8p46ts.js +0 -1
- package/dist/canvas/chunks/eventmodeling-45OFAUF4-v9q6dn4c.js +0 -1
- package/dist/canvas/chunks/flowDiagram-UKHOOZJN-yrr5z8tc.js +0 -1
- package/dist/canvas/chunks/gitGraph-TEB2WS4Q-kdwb8m0a.js +0 -1
- package/dist/canvas/chunks/index-1r72hdzb.js +0 -301
- package/dist/canvas/chunks/index-g3pwnqeb.js +0 -2
- package/dist/canvas/chunks/index-rft2hcz5.js +0 -2
- package/dist/canvas/chunks/info-DKCQHKI2-wpzs6sz3.js +0 -1
- package/dist/canvas/chunks/ishikawaDiagram-WSZJBQD7-5hnq1a9p.js +0 -70
- package/dist/canvas/chunks/packet-7NZHBO7P-xn439f0s.js +0 -1
- package/dist/canvas/chunks/pie-RZYD4A2V-0kf16rwh.js +0 -1
- package/dist/canvas/chunks/radar-I7S5WNFK-0g2pz36t.js +0 -1
- package/dist/canvas/chunks/railroad-3IZDKUUU-fcaran6j.js +0 -1
- package/dist/canvas/chunks/railroad-abnf-AHOZXSZD-wgdya41q.js +0 -1
- package/dist/canvas/chunks/railroad-ebnf-EBAXGLYW-gy07a4ka.js +0 -1
- package/dist/canvas/chunks/railroad-peg-LSFZ7HO6-ewk6wtkh.js +0 -1
- package/dist/canvas/chunks/stateDiagram-v2-UEYNNEHI-k8sfp2e7.js +0 -1
- package/dist/canvas/chunks/swimlanesDiagram-ULZ7WXOC-nqysw67j.js +0 -8
- package/dist/canvas/chunks/treeView-QDETBFTQ-cesw6gp0.js +0 -1
- package/dist/canvas/chunks/treemap-6X3UGDF4-zdv4knp9.js +0 -1
- package/dist/canvas/chunks/vennDiagram-T6HMQDX7-x8dqn9yf.js +0 -34
- package/dist/canvas/chunks/wardley-OPB4EBWU-44pdddez.js +0 -1
package/src/server/canvas-db.ts
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
import { Database } from 'bun:sqlite';
|
|
9
9
|
import type { ActorAttribution } from './attribution.js';
|
|
10
10
|
import { CONTEXT_READS_SCHEMA_SQL } from './context-reads.js';
|
|
11
|
+
import { OperationError } from './operations/types.js';
|
|
12
|
+
import { boardMoveBoundary } from './card-relocation.js';
|
|
11
13
|
import { MIGRATED_SNAPSHOT_FOLDER, normalizeBoardFolder } from '../shared/boards.js';
|
|
12
14
|
import { wikiLinksFromData } from '../shared/memory-graph.js';
|
|
13
15
|
import { relationVocabularySchema, type RelationDefinition } from '../shared/relations.js';
|
|
@@ -56,6 +58,20 @@ export function normalizeCanvasTheme(value: unknown, fallback: CanvasTheme = 'ha
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
const SCHEMA_SQL = `
|
|
61
|
+
CREATE TABLE IF NOT EXISTS board_moves (
|
|
62
|
+
request_id TEXT PRIMARY KEY, fingerprint TEXT NOT NULL, result TEXT NOT NULL, created_at TEXT NOT NULL
|
|
63
|
+
);
|
|
64
|
+
CREATE TABLE IF NOT EXISTS board_move_boundaries (
|
|
65
|
+
board_id TEXT PRIMARY KEY, request_id TEXT NOT NULL
|
|
66
|
+
);
|
|
67
|
+
CREATE TABLE IF NOT EXISTS card_relocations (
|
|
68
|
+
sequence INTEGER PRIMARY KEY AUTOINCREMENT, request_id TEXT NOT NULL, node_id TEXT NOT NULL,
|
|
69
|
+
from_board TEXT NOT NULL, to_board TEXT NOT NULL,
|
|
70
|
+
review_after INTEGER NOT NULL, review_through INTEGER NOT NULL,
|
|
71
|
+
read_after INTEGER NOT NULL, read_through INTEGER NOT NULL
|
|
72
|
+
);
|
|
73
|
+
CREATE INDEX IF NOT EXISTS relocation_destination ON card_relocations(to_board, node_id, sequence);
|
|
74
|
+
CREATE INDEX IF NOT EXISTS relocation_source ON card_relocations(from_board, node_id, sequence);
|
|
59
75
|
CREATE TABLE IF NOT EXISTS node_creation_requests (
|
|
60
76
|
request_id TEXT PRIMARY KEY, board_id TEXT NOT NULL, fingerprint TEXT NOT NULL, node TEXT NOT NULL
|
|
61
77
|
);
|
|
@@ -357,6 +373,10 @@ function parsePersistedAxState(raw: string | null | undefined): PmxAxState {
|
|
|
357
373
|
// ── Persisted State Interface ───────────────────────────────────
|
|
358
374
|
|
|
359
375
|
export interface PersistedCanvasState {
|
|
376
|
+
/** Optimistic storage baseline, independent of content/read/review clocks. New boards start at zero. */
|
|
377
|
+
storageVersion?: number;
|
|
378
|
+
/** Snapshot restore boundary, not an authored content version. */
|
|
379
|
+
moveBoundary?: string | null;
|
|
360
380
|
tour?: Tour;
|
|
361
381
|
version: number;
|
|
362
382
|
theme?: CanvasTheme;
|
|
@@ -462,6 +482,19 @@ export function openCanvasDb(dbPath: string, options: OpenCanvasDbOptions = {}):
|
|
|
462
482
|
// worth the fsync.
|
|
463
483
|
db.exec('PRAGMA synchronous=FULL');
|
|
464
484
|
if (needsBoardsMigration(db)) migrateToBoards(db, dbPath, options.migratedBoardName ?? 'Board');
|
|
485
|
+
const empty = !db.query("SELECT 1 FROM sqlite_master WHERE type = 'table' LIMIT 1").get();
|
|
486
|
+
if (empty) {
|
|
487
|
+
// One durable commit for a fresh schema. Another initializer may win the
|
|
488
|
+
// lock first, so all schema checks still run inside the transaction.
|
|
489
|
+
db.transaction(() => initializeCanvasSchema(db)).immediate();
|
|
490
|
+
} else {
|
|
491
|
+
// Current databases must remain readable while another connection writes.
|
|
492
|
+
initializeCanvasSchema(db);
|
|
493
|
+
}
|
|
494
|
+
return db;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function initializeCanvasSchema(db: Database): void {
|
|
465
498
|
db.exec(SCHEMA_SQL);
|
|
466
499
|
db.exec(CONTEXT_READS_SCHEMA_SQL);
|
|
467
500
|
|
|
@@ -513,14 +546,34 @@ export function openCanvasDb(dbPath: string, options: OpenCanvasDbOptions = {}):
|
|
|
513
546
|
ensureColumn(db, 'snapshot_edges', 'revision', 'revision INTEGER NOT NULL DEFAULT 0');
|
|
514
547
|
ensureColumn(db, 'snapshot_edges', 'attribution', "attribution TEXT NOT NULL DEFAULT '{}'");
|
|
515
548
|
ensureColumn(db, 'document_imports', 'committed_node_ids', 'committed_node_ids TEXT');
|
|
549
|
+
ensureColumn(db, 'boards', 'storage_version', 'storage_version INTEGER NOT NULL DEFAULT 0');
|
|
550
|
+
// Cover direct writers as well as full-state saves. A relocation updates both
|
|
551
|
+
// board baselines; a stale source or destination payload must never recreate it.
|
|
552
|
+
for (const table of ['nodes', 'edges', 'annotations', 'context_pins', 'ax_state']) {
|
|
553
|
+
db.exec(`
|
|
554
|
+
CREATE TRIGGER IF NOT EXISTS ${table}_storage_insert AFTER INSERT ON ${table} BEGIN
|
|
555
|
+
UPDATE boards SET storage_version = storage_version + 1 WHERE id = NEW.board_id;
|
|
556
|
+
END;
|
|
557
|
+
CREATE TRIGGER IF NOT EXISTS ${table}_storage_delete AFTER DELETE ON ${table} BEGIN
|
|
558
|
+
UPDATE boards SET storage_version = storage_version + 1 WHERE id = OLD.board_id;
|
|
559
|
+
END;
|
|
560
|
+
CREATE TRIGGER IF NOT EXISTS ${table}_storage_update AFTER UPDATE ON ${table} BEGIN
|
|
561
|
+
UPDATE boards SET storage_version = storage_version + 1 WHERE id IN (OLD.board_id, NEW.board_id);
|
|
562
|
+
END;
|
|
563
|
+
`);
|
|
564
|
+
}
|
|
565
|
+
db.exec(`
|
|
566
|
+
CREATE TRIGGER IF NOT EXISTS boards_storage_view
|
|
567
|
+
AFTER UPDATE OF viewport_x, viewport_y, viewport_scale, tour ON boards BEGIN
|
|
568
|
+
UPDATE boards SET storage_version = storage_version + 1 WHERE id = NEW.id;
|
|
569
|
+
END;
|
|
570
|
+
`);
|
|
516
571
|
|
|
517
572
|
// Set schema version if not present
|
|
518
573
|
const row = db.query<{ value: string }, [string]>('SELECT value FROM meta WHERE key = ?').get('schema_version');
|
|
519
574
|
if (!row) {
|
|
520
575
|
db.run('INSERT INTO meta (key, value) VALUES (?, ?)', ['schema_version', String(SCHEMA_VERSION)]);
|
|
521
576
|
}
|
|
522
|
-
|
|
523
|
-
return db;
|
|
524
577
|
}
|
|
525
578
|
|
|
526
579
|
// ── Many boards migration (schema 1 → 2) ────────────────────────
|
|
@@ -762,9 +815,26 @@ export function readThemeFromDB(db: Database): CanvasTheme | undefined {
|
|
|
762
815
|
return value ? normalizeCanvasTheme(value) : undefined;
|
|
763
816
|
}
|
|
764
817
|
|
|
765
|
-
|
|
766
|
-
|
|
818
|
+
export function boardStorageVersion(db: Database, boardId: string): number {
|
|
819
|
+
const board = db
|
|
820
|
+
.query<{ storage_version: number }, [string]>('SELECT storage_version FROM boards WHERE id = ?')
|
|
821
|
+
.get(boardId);
|
|
822
|
+
if (!board) throw new OperationError(`Board "${boardId}" not found.`, 404);
|
|
823
|
+
return board.storage_version;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
export function assertBoardStorageVersion(db: Database, boardId: string, expected: number): void {
|
|
827
|
+
if (boardStorageVersion(db, boardId) !== expected)
|
|
828
|
+
throw new OperationError(
|
|
829
|
+
`Board "${boardId}" changed in another writer. Unsaved edits have been retained; reconcile them before saving.`,
|
|
830
|
+
409,
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/** Save one board, returning its new baseline. Publish the baseline only after the outer transaction commits. */
|
|
835
|
+
export function saveStateToDB(db: Database, boardId: string, state: PersistedCanvasState): number {
|
|
767
836
|
const transaction = db.transaction(() => {
|
|
837
|
+
assertBoardStorageVersion(db, boardId, state.storageVersion ?? 0);
|
|
768
838
|
// Rows are upserted only when a column actually differs, and rows that are
|
|
769
839
|
// gone are deleted by id. This used to be DELETE-all + INSERT-all, which
|
|
770
840
|
// rewrote every row on every debounced save: dragging one node rewrote the
|
|
@@ -920,157 +990,164 @@ export function saveStateToDB(db: Database, boardId: string, state: PersistedCan
|
|
|
920
990
|
WHERE value IS NOT excluded.value`,
|
|
921
991
|
[boardId, 'revision', JSON.stringify(state.revisionState ?? { revision: 0, floor: 0, tombstones: [] })],
|
|
922
992
|
);
|
|
993
|
+
return boardStorageVersion(db, boardId);
|
|
923
994
|
});
|
|
924
995
|
|
|
925
|
-
transaction();
|
|
996
|
+
return transaction.immediate();
|
|
926
997
|
}
|
|
927
998
|
|
|
928
999
|
/** Loads one board (the open board when `boardId` is omitted); null when there is no such board. */
|
|
929
1000
|
export function loadStateFromDB(db: Database, boardId?: string): PersistedCanvasState | null {
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
scale: board.viewport_scale || 1,
|
|
947
|
-
};
|
|
948
|
-
const theme = readThemeFromDB(db);
|
|
1001
|
+
return db.transaction(() => {
|
|
1002
|
+
const id = boardId ?? getActiveBoardIdFromDB(db);
|
|
1003
|
+
if (!id) return null;
|
|
1004
|
+
interface BoardRow {
|
|
1005
|
+
viewport_x: number;
|
|
1006
|
+
viewport_y: number;
|
|
1007
|
+
viewport_scale: number;
|
|
1008
|
+
tour: string;
|
|
1009
|
+
storage_version: number;
|
|
1010
|
+
}
|
|
1011
|
+
const board = db
|
|
1012
|
+
.query<BoardRow, [string]>(
|
|
1013
|
+
'SELECT viewport_x, viewport_y, viewport_scale, tour, storage_version FROM boards WHERE id = ?',
|
|
1014
|
+
)
|
|
1015
|
+
.get(id);
|
|
1016
|
+
if (!board) return null;
|
|
949
1017
|
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
pos_x: number;
|
|
955
|
-
pos_y: number;
|
|
956
|
-
width: number;
|
|
957
|
-
height: number;
|
|
958
|
-
z_index: number;
|
|
959
|
-
collapsed: number;
|
|
960
|
-
pinned: number;
|
|
961
|
-
data: string;
|
|
962
|
-
attribution: string;
|
|
963
|
-
content_revision: number;
|
|
964
|
-
links_revision: number;
|
|
965
|
-
}
|
|
966
|
-
const nodeRows = db.query<NodeRow, [string]>('SELECT * FROM nodes WHERE board_id = ?').all(id);
|
|
967
|
-
const nodes: CanvasNodeState[] = nodeRows.map((row) => {
|
|
968
|
-
const attribution = JSON.parse(row.attribution || '{}') as Partial<
|
|
969
|
-
Pick<CanvasNodeState, 'createdBy' | 'lastEditedBy' | 'contentChangedAt' | 'freshnessStartedAt'>
|
|
970
|
-
>;
|
|
971
|
-
return {
|
|
972
|
-
id: row.id,
|
|
973
|
-
type: row.type as CanvasNodeState['type'],
|
|
974
|
-
position: { x: row.pos_x, y: row.pos_y },
|
|
975
|
-
size: { width: row.width, height: row.height },
|
|
976
|
-
zIndex: row.z_index,
|
|
977
|
-
collapsed: row.collapsed === 1,
|
|
978
|
-
pinned: row.pinned === 1,
|
|
979
|
-
data: JSON.parse(row.data) as Record<string, unknown>,
|
|
980
|
-
createdBy: attribution.createdBy,
|
|
981
|
-
lastEditedBy: attribution.lastEditedBy,
|
|
982
|
-
contentChangedAt: attribution.contentChangedAt,
|
|
983
|
-
freshnessStartedAt: attribution.freshnessStartedAt,
|
|
984
|
-
contentRevision: row.content_revision,
|
|
985
|
-
linksRevision: row.links_revision,
|
|
1018
|
+
const viewport: ViewportState = {
|
|
1019
|
+
x: board.viewport_x,
|
|
1020
|
+
y: board.viewport_y,
|
|
1021
|
+
scale: board.viewport_scale || 1,
|
|
986
1022
|
};
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1023
|
+
const theme = readThemeFromDB(db);
|
|
1024
|
+
|
|
1025
|
+
// Load nodes
|
|
1026
|
+
interface NodeRow {
|
|
1027
|
+
id: string;
|
|
1028
|
+
type: string;
|
|
1029
|
+
pos_x: number;
|
|
1030
|
+
pos_y: number;
|
|
1031
|
+
width: number;
|
|
1032
|
+
height: number;
|
|
1033
|
+
z_index: number;
|
|
1034
|
+
collapsed: number;
|
|
1035
|
+
pinned: number;
|
|
1036
|
+
data: string;
|
|
1037
|
+
attribution: string;
|
|
1038
|
+
content_revision: number;
|
|
1039
|
+
links_revision: number;
|
|
1040
|
+
}
|
|
1041
|
+
const nodeRows = db.query<NodeRow, [string]>('SELECT * FROM nodes WHERE board_id = ?').all(id);
|
|
1042
|
+
const nodes: CanvasNodeState[] = nodeRows.map((row) => {
|
|
1043
|
+
const attribution = JSON.parse(row.attribution || '{}') as Partial<
|
|
1044
|
+
Pick<CanvasNodeState, 'createdBy' | 'lastEditedBy' | 'contentChangedAt' | 'freshnessStartedAt'>
|
|
1045
|
+
>;
|
|
1046
|
+
return {
|
|
1047
|
+
id: row.id,
|
|
1048
|
+
type: row.type as CanvasNodeState['type'],
|
|
1049
|
+
position: { x: row.pos_x, y: row.pos_y },
|
|
1050
|
+
size: { width: row.width, height: row.height },
|
|
1051
|
+
zIndex: row.z_index,
|
|
1052
|
+
collapsed: row.collapsed === 1,
|
|
1053
|
+
pinned: row.pinned === 1,
|
|
1054
|
+
data: JSON.parse(row.data) as Record<string, unknown>,
|
|
1055
|
+
createdBy: attribution.createdBy,
|
|
1056
|
+
lastEditedBy: attribution.lastEditedBy,
|
|
1057
|
+
contentChangedAt: attribution.contentChangedAt,
|
|
1058
|
+
freshnessStartedAt: attribution.freshnessStartedAt,
|
|
1059
|
+
contentRevision: row.content_revision,
|
|
1060
|
+
linksRevision: row.links_revision,
|
|
1061
|
+
};
|
|
1062
|
+
});
|
|
1063
|
+
|
|
1064
|
+
// Load edges
|
|
1065
|
+
interface EdgeRow {
|
|
1066
|
+
id: string;
|
|
1067
|
+
from_node: string;
|
|
1068
|
+
to_node: string;
|
|
1069
|
+
to_board_id: string | null;
|
|
1070
|
+
type: string;
|
|
1071
|
+
kind: string | null;
|
|
1072
|
+
reason: string | null;
|
|
1073
|
+
label: string | null;
|
|
1074
|
+
style: string | null;
|
|
1075
|
+
animated: number;
|
|
1076
|
+
revision: number;
|
|
1077
|
+
attribution: string;
|
|
1078
|
+
}
|
|
1079
|
+
const edgeRows = db.query<EdgeRow, [string]>('SELECT * FROM edges WHERE board_id = ?').all(id);
|
|
1080
|
+
const edges: CanvasEdge[] = edgeRows.map((row) => ({
|
|
1081
|
+
id: row.id,
|
|
1082
|
+
from: row.from_node,
|
|
1083
|
+
to: row.to_node,
|
|
1084
|
+
...(row.to_board_id ? { toBoardId: row.to_board_id } : {}),
|
|
1085
|
+
type: row.type as CanvasEdge['type'],
|
|
1086
|
+
...(row.kind ? { kind: row.kind } : {}),
|
|
1087
|
+
...(row.reason ? { reason: row.reason } : {}),
|
|
1088
|
+
...(row.label ? { label: row.label } : {}),
|
|
1089
|
+
...(row.style ? { style: row.style as CanvasEdge['style'] } : {}),
|
|
1090
|
+
...(row.animated ? { animated: true } : {}),
|
|
1091
|
+
...(row.revision ? { revision: row.revision } : {}),
|
|
1092
|
+
...(JSON.parse(row.attribution || '{}') as EdgeAttribution),
|
|
1093
|
+
}));
|
|
1094
|
+
|
|
1095
|
+
// Load annotations
|
|
1096
|
+
interface AnnotationRow {
|
|
1097
|
+
id: string;
|
|
1098
|
+
type: string;
|
|
1099
|
+
points: string;
|
|
1100
|
+
bounds: string;
|
|
1101
|
+
color: string;
|
|
1102
|
+
width: number;
|
|
1103
|
+
text: string | null;
|
|
1104
|
+
label: string | null;
|
|
1105
|
+
created_at: string;
|
|
1106
|
+
}
|
|
1107
|
+
const annotationRows = db.query<AnnotationRow, [string]>('SELECT * FROM annotations WHERE board_id = ?').all(id);
|
|
1108
|
+
const annotations: CanvasAnnotation[] = annotationRows.map((row) => ({
|
|
1109
|
+
id: row.id,
|
|
1110
|
+
type: row.type as CanvasAnnotation['type'],
|
|
1111
|
+
points: JSON.parse(row.points),
|
|
1112
|
+
bounds: JSON.parse(row.bounds),
|
|
1113
|
+
color: row.color,
|
|
1114
|
+
width: row.width,
|
|
1115
|
+
...(row.text ? { text: row.text } : {}),
|
|
1116
|
+
...(row.label ? { label: row.label } : {}),
|
|
1117
|
+
createdAt: row.created_at,
|
|
1118
|
+
}));
|
|
1119
|
+
|
|
1120
|
+
// Load context pins
|
|
1121
|
+
interface PinRow {
|
|
1122
|
+
node_id: string;
|
|
1123
|
+
meta: string | null;
|
|
1124
|
+
}
|
|
1125
|
+
const pinRows = db.query<PinRow, [string]>('SELECT node_id, meta FROM context_pins WHERE board_id = ?').all(id);
|
|
1126
|
+
const contextPins = pinRows.map((row) => row.node_id);
|
|
1127
|
+
const contextPinMeta = pinMetaFromRows(pinRows);
|
|
1053
1128
|
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1129
|
+
const axRow = db
|
|
1130
|
+
.query<{ value: string }, [string, string]>('SELECT value FROM ax_state WHERE board_id = ? AND key = ?')
|
|
1131
|
+
.get(id, 'state');
|
|
1057
1132
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1133
|
+
const revisionRaw = db
|
|
1134
|
+
.query<{ value: string }, [string, string]>('SELECT value FROM ax_state WHERE board_id = ? AND key = ?')
|
|
1135
|
+
.get(id, 'revision')?.value;
|
|
1136
|
+
return {
|
|
1137
|
+
storageVersion: board.storage_version,
|
|
1138
|
+
version: 1,
|
|
1139
|
+
theme,
|
|
1140
|
+
tour: readTour(board.tour),
|
|
1141
|
+
viewport,
|
|
1142
|
+
nodes,
|
|
1143
|
+
edges,
|
|
1144
|
+
annotations,
|
|
1145
|
+
contextPins,
|
|
1146
|
+
contextPinMeta,
|
|
1147
|
+
ax: parsePersistedAxState(axRow?.value),
|
|
1148
|
+
revisionState: revisionRaw ? (JSON.parse(revisionRaw) as PersistedRevisionState) : undefined,
|
|
1149
|
+
};
|
|
1150
|
+
})();
|
|
1074
1151
|
}
|
|
1075
1152
|
|
|
1076
1153
|
// ── Boards ──────────────────────────────────────────────────────
|
|
@@ -1387,6 +1464,7 @@ export function saveSnapshotToDB(
|
|
|
1387
1464
|
state: PersistedCanvasState,
|
|
1388
1465
|
): void {
|
|
1389
1466
|
const transaction = db.transaction(() => {
|
|
1467
|
+
assertBoardStorageVersion(db, boardId, state.storageVersion ?? 0);
|
|
1390
1468
|
// Insert snapshot metadata
|
|
1391
1469
|
db.run('INSERT INTO snapshots (id, board_id, name, created_at, node_count, edge_count) VALUES (?, ?, ?, ?, ?, ?)', [
|
|
1392
1470
|
snapshot.id,
|
|
@@ -1402,6 +1480,11 @@ export function saveSnapshotToDB(
|
|
|
1402
1480
|
'tour',
|
|
1403
1481
|
JSON.stringify(state.tour ?? null),
|
|
1404
1482
|
]);
|
|
1483
|
+
db.run('INSERT INTO snapshot_meta (snapshot_id, key, value) VALUES (?, ?, ?)', [
|
|
1484
|
+
snapshot.id,
|
|
1485
|
+
'move_boundary',
|
|
1486
|
+
boardMoveBoundary(db, boardId) ?? '',
|
|
1487
|
+
]);
|
|
1405
1488
|
// Insert snapshot viewport meta
|
|
1406
1489
|
db.run('INSERT INTO snapshot_meta (snapshot_id, key, value) VALUES (?, ?, ?)', [
|
|
1407
1490
|
snapshot.id,
|
|
@@ -1508,7 +1591,7 @@ export function saveSnapshotToDB(
|
|
|
1508
1591
|
]);
|
|
1509
1592
|
});
|
|
1510
1593
|
|
|
1511
|
-
transaction();
|
|
1594
|
+
transaction.immediate();
|
|
1512
1595
|
}
|
|
1513
1596
|
|
|
1514
1597
|
/** By id or name (most recent match) on `boardId`. */
|
|
@@ -1679,6 +1762,7 @@ export function loadSnapshotFromDB(
|
|
|
1679
1762
|
return {
|
|
1680
1763
|
snapshot,
|
|
1681
1764
|
state: {
|
|
1765
|
+
moveBoundary: metaMap.get('move_boundary') || null,
|
|
1682
1766
|
version: 1,
|
|
1683
1767
|
tour: readTour(metaMap.get('tour')),
|
|
1684
1768
|
viewport,
|
|
@@ -2019,28 +2103,37 @@ export function loadAxSteeringFromDB(
|
|
|
2019
2103
|
|
|
2020
2104
|
export function loadPendingAxSteeringFromDB(
|
|
2021
2105
|
db: Database,
|
|
2022
|
-
options: { consumer?: string; limit?: number } = {},
|
|
2106
|
+
options: { consumer?: string; limit?: number; addressedOnly?: boolean; offset?: number } = {},
|
|
2023
2107
|
): PmxAxSteeringMessage[] {
|
|
2108
|
+
if (options.addressedOnly && !options.consumer) return [];
|
|
2024
2109
|
// FIFO (oldest undelivered first); exclude the consumer's own steering (loop
|
|
2025
2110
|
// prevention) and any steering addressed to a DIFFERENT consumer (target scoping)
|
|
2026
2111
|
// in SQL so the LIMIT is applied AFTER both filters, not before.
|
|
2027
2112
|
const limit = clampTimelineLimit(options.limit);
|
|
2028
2113
|
const rows = options.consumer
|
|
2029
2114
|
? db
|
|
2030
|
-
.query<AxSteeringRow, [string, string, string, string, string, number]>(
|
|
2115
|
+
.query<AxSteeringRow, [string, string, string, string, string, number, number]>(
|
|
2031
2116
|
// A broadcast stays pending PER CONSUMER: it leaves this consumer's
|
|
2032
2117
|
// queue only once THIS consumer marked it (or an anonymous global ack
|
|
2033
2118
|
// set delivered).
|
|
2034
|
-
|
|
2119
|
+
`SELECT * FROM ax_steering s WHERE s.delivered = 0 AND (s.source IS NULL OR s.source != ?) AND (s.agent_id IS NULL OR s.agent_id != ?) AND (s.target IS NULL OR s.target = ?) ${options.addressedOnly ? 'AND s.target IS NOT NULL' : ''} AND (s.target IS NOT NULL OR s.created_at > ?) AND NOT EXISTS (SELECT 1 FROM ax_steering_deliveries d WHERE d.steering_id = s.id AND d.consumer = ?) ORDER BY s.seq ASC LIMIT ? OFFSET ?`,
|
|
2120
|
+
)
|
|
2121
|
+
.all(
|
|
2122
|
+
options.consumer,
|
|
2123
|
+
options.consumer,
|
|
2124
|
+
options.consumer,
|
|
2125
|
+
broadcastCutoffIso(),
|
|
2126
|
+
options.consumer,
|
|
2127
|
+
limit,
|
|
2128
|
+
options.offset ?? 0,
|
|
2035
2129
|
)
|
|
2036
|
-
.all(options.consumer, options.consumer, options.consumer, broadcastCutoffIso(), options.consumer, limit)
|
|
2037
2130
|
: db
|
|
2038
|
-
.query<AxSteeringRow, [string, number]>(
|
|
2131
|
+
.query<AxSteeringRow, [string, number, number]>(
|
|
2039
2132
|
// No consumer given: only broadcasts — addressed steering is claimable
|
|
2040
2133
|
// solely by the consumer it names.
|
|
2041
|
-
'SELECT * FROM ax_steering WHERE delivered = 0 AND target IS NULL AND created_at > ? ORDER BY seq ASC LIMIT ?',
|
|
2134
|
+
'SELECT * FROM ax_steering WHERE delivered = 0 AND target IS NULL AND created_at > ? ORDER BY seq ASC LIMIT ? OFFSET ?',
|
|
2042
2135
|
)
|
|
2043
|
-
.all(broadcastCutoffIso(), limit);
|
|
2136
|
+
.all(broadcastCutoffIso(), limit, options.offset ?? 0);
|
|
2044
2137
|
return rows.map(mapAxSteeringRow).filter((s): s is PmxAxSteeringMessage => s !== null);
|
|
2045
2138
|
}
|
|
2046
2139
|
|
|
@@ -2054,20 +2147,29 @@ export function loadPendingAxSteeringFromDB(
|
|
|
2054
2147
|
*/
|
|
2055
2148
|
export function loadNewestPendingAxSteeringFromDB(
|
|
2056
2149
|
db: Database,
|
|
2057
|
-
options: { consumer?: string; limit?: number } = {},
|
|
2150
|
+
options: { consumer?: string; limit?: number; addressedOnly?: boolean; offset?: number } = {},
|
|
2058
2151
|
): PmxAxSteeringMessage[] {
|
|
2152
|
+
if (options.addressedOnly && !options.consumer) return [];
|
|
2059
2153
|
const limit = clampTimelineLimit(options.limit);
|
|
2060
2154
|
const rows = options.consumer
|
|
2061
2155
|
? db
|
|
2062
|
-
.query<AxSteeringRow, [string, string, string, string, string, number]>(
|
|
2063
|
-
|
|
2156
|
+
.query<AxSteeringRow, [string, string, string, string, string, number, number]>(
|
|
2157
|
+
`SELECT * FROM ax_steering s WHERE s.delivered = 0 AND (s.source IS NULL OR s.source != ?) AND (s.agent_id IS NULL OR s.agent_id != ?) AND (s.target IS NULL OR s.target = ?) ${options.addressedOnly ? 'AND s.target IS NOT NULL' : ''} AND (s.target IS NOT NULL OR s.created_at > ?) AND NOT EXISTS (SELECT 1 FROM ax_steering_deliveries d WHERE d.steering_id = s.id AND d.consumer = ?) ORDER BY s.seq DESC LIMIT ? OFFSET ?`,
|
|
2158
|
+
)
|
|
2159
|
+
.all(
|
|
2160
|
+
options.consumer,
|
|
2161
|
+
options.consumer,
|
|
2162
|
+
options.consumer,
|
|
2163
|
+
broadcastCutoffIso(),
|
|
2164
|
+
options.consumer,
|
|
2165
|
+
limit,
|
|
2166
|
+
options.offset ?? 0,
|
|
2064
2167
|
)
|
|
2065
|
-
.all(options.consumer, options.consumer, options.consumer, broadcastCutoffIso(), options.consumer, limit)
|
|
2066
2168
|
: db
|
|
2067
|
-
.query<AxSteeringRow, [number]>(
|
|
2068
|
-
'SELECT * FROM ax_steering WHERE delivered = 0 AND target IS NULL ORDER BY seq DESC LIMIT ?',
|
|
2169
|
+
.query<AxSteeringRow, [number, number]>(
|
|
2170
|
+
'SELECT * FROM ax_steering WHERE delivered = 0 AND target IS NULL ORDER BY seq DESC LIMIT ? OFFSET ?',
|
|
2069
2171
|
)
|
|
2070
|
-
.all(limit);
|
|
2172
|
+
.all(limit, options.offset ?? 0);
|
|
2071
2173
|
return rows.map(mapAxSteeringRow).filter((s): s is PmxAxSteeringMessage => s !== null);
|
|
2072
2174
|
}
|
|
2073
2175
|
|