pmx-canvas 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/extensions/pmx-canvas/runtime.json +1 -1
- package/.github/extensions/pmx-canvas/tools.json +246 -108
- package/CHANGELOG.md +32 -0
- package/dist/canvas/chunks/abnfDiagram-N423BO3Z-p8dtp6va.js +1 -0
- package/dist/canvas/chunks/architecture-TIHT7OUA-9tffvpsv.js +1 -0
- package/dist/canvas/chunks/architectureDiagram-T3A2C74G-5jhmxrzf.js +36 -0
- package/dist/canvas/chunks/blockDiagram-VBNYF7ZC-pw1fa9yb.js +132 -0
- package/dist/canvas/chunks/c4Diagram-5PPSVZJV-x07nq0tt.js +10 -0
- package/dist/canvas/chunks/classDiagram-JCYQIIEL-nc6g5x1r.js +1 -0
- package/dist/canvas/chunks/classDiagram-v2-OCEON4UE-0qnbpwvh.js +1 -0
- package/dist/canvas/chunks/cose-bilkent-JH36ORCC-nm39npq0.js +1 -0
- package/dist/canvas/chunks/cynefin-VYW2F7L2-ys8p46ts.js +1 -0
- package/dist/canvas/chunks/cynefinDiagram-MW4NZA55-vdxrwahz.js +62 -0
- package/dist/canvas/chunks/dagre-VZM6K2ZE-bqdm7wpd.js +4 -0
- package/dist/canvas/chunks/diagram-7IWD3JNH-gs5gpr93.js +30 -0
- package/dist/canvas/chunks/diagram-B4RE2ZJO-cgwjg47s.js +3 -0
- package/dist/canvas/chunks/diagram-LBJQPF4R-w3vahpav.js +24 -0
- package/dist/canvas/chunks/diagram-Q27KOJAE-42837z6h.js +24 -0
- package/dist/canvas/chunks/diagram-UB23O5K3-4fz83q59.js +41 -0
- package/dist/canvas/chunks/ebnfDiagram-BXEA7PRR-zphtgegj.js +1 -0
- package/dist/canvas/chunks/erDiagram-JOGREHBK-zvbbdxp4.js +85 -0
- package/dist/canvas/chunks/eventmodeling-45OFAUF4-v9q6dn4c.js +1 -0
- package/dist/canvas/chunks/flowDiagram-UKHOOZJN-yrr5z8tc.js +1 -0
- package/dist/canvas/chunks/ganttDiagram-PKOTCBZU-282jzycm.js +292 -0
- package/dist/canvas/chunks/gitGraph-TEB2WS4Q-kdwb8m0a.js +1 -0
- package/dist/canvas/chunks/gitGraphDiagram-DS77QQ5N-vqqy6p1b.js +106 -0
- package/dist/canvas/chunks/index-0rk4gp1g.js +2 -0
- package/dist/canvas/chunks/index-194m3hc3.js +2 -0
- package/dist/canvas/chunks/index-1r72hdzb.js +301 -0
- package/dist/canvas/chunks/index-353m7b0p.js +2 -0
- package/dist/canvas/chunks/index-3cy1bzwh.js +2 -0
- package/dist/canvas/chunks/index-5tv8v1hk.js +1 -0
- package/dist/canvas/chunks/index-6ceya556.js +332 -0
- package/dist/canvas/chunks/index-6qmsnxwy.js +4 -0
- package/dist/canvas/chunks/index-d3b3gqe9.js +2 -0
- package/dist/canvas/chunks/index-fjtdggjn.js +2 -0
- package/dist/canvas/chunks/index-g3pwnqeb.js +2 -0
- package/dist/canvas/chunks/index-gf4e0et4.js +2 -0
- package/dist/canvas/chunks/index-hj99nz1f.js +1 -0
- package/dist/canvas/chunks/index-nprm96xp.js +16 -0
- package/dist/canvas/chunks/index-qj8fxwsf.js +2 -0
- package/dist/canvas/chunks/index-r2fyc9w1.js +89 -0
- package/dist/canvas/chunks/index-rft2hcz5.js +2 -0
- package/dist/canvas/chunks/index-rh7nv68n.js +2 -0
- package/dist/canvas/chunks/index-s4j678gj.js +2 -0
- package/dist/canvas/chunks/index-tb1wf525.js +179 -0
- package/dist/canvas/chunks/index-vv56h019.js +2 -0
- package/dist/canvas/chunks/index-w2mra17t.js +207 -0
- package/dist/canvas/chunks/index-wgv7en21.js +232 -0
- package/dist/canvas/chunks/index-yw8h9vha.js +157 -0
- package/dist/canvas/chunks/info-DKCQHKI2-wpzs6sz3.js +1 -0
- package/dist/canvas/chunks/infoDiagram-6WML65LV-v1yvggqj.js +2 -0
- package/dist/canvas/chunks/ishikawaDiagram-WSZJBQD7-5hnq1a9p.js +70 -0
- package/dist/canvas/chunks/journeyDiagram-NVQOT4AX-zjcffveh.js +139 -0
- package/dist/canvas/chunks/kanban-definition-27J2QSJJ-dakqtfp1.js +89 -0
- package/dist/canvas/chunks/katex-dmg8k4zm.js +257 -0
- package/dist/canvas/chunks/mermaid.core-j2geswrd.js +12 -0
- package/dist/canvas/chunks/mindmap-definition-FAOFIHXS-142xhr3k.js +96 -0
- package/dist/canvas/chunks/packet-7NZHBO7P-xn439f0s.js +1 -0
- package/dist/canvas/chunks/pegDiagram-VL7TDLO6-5wvk7vsd.js +1 -0
- package/dist/canvas/chunks/pie-RZYD4A2V-0kf16rwh.js +1 -0
- package/dist/canvas/chunks/pieDiagram-7S7Q4E2Y-ztdhwycc.js +39 -0
- package/dist/canvas/chunks/quadrantDiagram-CIZ2JOQS-c5p9gmg6.js +7 -0
- package/dist/canvas/chunks/radar-I7S5WNFK-0g2pz36t.js +1 -0
- package/dist/canvas/chunks/railroad-3IZDKUUU-fcaran6j.js +1 -0
- package/dist/canvas/chunks/railroad-abnf-AHOZXSZD-wgdya41q.js +1 -0
- package/dist/canvas/chunks/railroad-ebnf-EBAXGLYW-gy07a4ka.js +1 -0
- package/dist/canvas/chunks/railroad-peg-LSFZ7HO6-ewk6wtkh.js +1 -0
- package/dist/canvas/chunks/railroadDiagram-AXF67PYL-xkmqkjwf.js +1 -0
- package/dist/canvas/chunks/requirementDiagram-LRYGKXZP-9wm2dyaw.js +84 -0
- package/dist/canvas/chunks/sankeyDiagram-W5VNT64P-2w19etgb.js +40 -0
- package/dist/canvas/chunks/sequenceDiagram-SI44F4Z6-0jn0vwmz.js +162 -0
- package/dist/canvas/chunks/sizeCapture-X5ZJPWSS-x75epwdq.js +1 -0
- package/dist/canvas/chunks/stateDiagram-OKZ733FA-schjpy6d.js +1 -0
- package/dist/canvas/chunks/stateDiagram-v2-UEYNNEHI-k8sfp2e7.js +1 -0
- package/dist/canvas/chunks/swimlanes-SLNWSIFB-gkw4xce3.js +1 -0
- package/dist/canvas/chunks/swimlanesDiagram-ULZ7WXOC-nqysw67j.js +8 -0
- package/dist/canvas/chunks/timeline-definition-Z64GVDOM-x7nkcy01.js +120 -0
- package/dist/canvas/chunks/treeView-QDETBFTQ-cesw6gp0.js +1 -0
- package/dist/canvas/chunks/treemap-6X3UGDF4-zdv4knp9.js +1 -0
- package/dist/canvas/chunks/vennDiagram-T6HMQDX7-x8dqn9yf.js +34 -0
- package/dist/canvas/chunks/wardley-OPB4EBWU-44pdddez.js +1 -0
- package/dist/canvas/chunks/wardleyDiagram-T6FBY63Y-t1j52t9r.js +78 -0
- package/dist/canvas/chunks/xychartDiagram-ELKLHX3M-38t16pme.js +7 -0
- package/dist/canvas/global.css +389 -229
- package/dist/canvas/index.js +141 -211
- package/dist/canvas/mcp-app.html +4205 -554
- package/dist/canvas/mermaid-entry.js +338 -338
- package/dist/canvas/surface-theme.css +6 -6
- package/dist/json-render/index.css +1 -1
- package/dist/json-render/index.js +49 -49
- package/dist/types/client/canvas/BarHint.d.ts +5 -3
- package/dist/types/client/canvas/CanvasNode.d.ts +0 -11
- package/dist/types/client/canvas/EdgeLayer.d.ts +1 -2
- package/dist/types/client/canvas/auto-fit.d.ts +4 -5
- package/dist/types/client/mermaid-entry.d.ts +1 -2
- package/dist/types/client/mermaid-render.d.ts +1 -0
- package/dist/types/client/nodes/BoardNode.d.ts +1 -1
- package/dist/types/client/nodes/ExtAppFrame.d.ts +7 -90
- package/dist/types/client/nodes/McpAppNode.d.ts +1 -25
- package/dist/types/client/nodes/MermaidNode.d.ts +1 -8
- package/dist/types/client/nodes/ViewerFrame.d.ts +29 -0
- package/dist/types/client/nodes/ViewerHost.d.ts +6 -0
- package/dist/types/client/nodes/iframe-document-url.d.ts +3 -9
- package/dist/types/client/nodes/primitive-copy.d.ts +5 -0
- package/dist/types/client/nodes/use-iframe-content-height.d.ts +1 -1
- package/dist/types/client/nodes/use-surface-frame.d.ts +4 -6
- package/dist/types/client/nodes/viewer-runtime-state.d.ts +2 -1
- package/dist/types/client/state/canvas-store.d.ts +3 -1
- package/dist/types/client/state/intent-bridge.d.ts +1 -0
- package/dist/types/client/state/presentation.d.ts +17 -6
- package/dist/types/json-render/server.d.ts +2 -1
- package/dist/types/server/canvas-state.d.ts +2 -0
- package/dist/types/server/index.d.ts +10 -5
- package/dist/types/server/operations/ops/boards.d.ts +0 -1
- package/dist/types/server/tour-control.d.ts +13 -3
- package/dist/types/shared/ax-surface-protocol.d.ts +0 -9
- package/dist/types/shared/tour.d.ts +8 -0
- package/docs/design/Chrome.dc.html +32 -6
- package/docs/design/Types1.dc.html +3 -2
- package/docs/design/Types2.dc.html +1 -1
- package/docs/design/Types3.dc.html +1 -1
- package/docs/design/Zoom.dc.html +37 -60
- package/docs/design/canvas.json +1 -1
- package/docs/design.md +169 -4
- package/docs/http-api.md +51 -1
- package/docs/mcp.md +8 -0
- package/docs/node-types.md +38 -3
- package/docs/plans/plan-009-tech-debt-backlog.md +2 -2
- package/docs/plans/plan-019-post-0.9-roadmap.md +283 -11
- package/docs/product-vision-2026-09.md +9 -0
- package/package.json +2 -2
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
- package/plugins/pmx-canvas/plugin.json +1 -1
- package/plugins/pmx-canvas/runtime.json +1 -1
- package/plugins/pmx-canvas/tools.json +246 -108
- package/skills/pmx-canvas/SKILL.md +11 -18
- package/skills/pmx-canvas/references/full-reference.md +9 -4
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
- package/skills/published-consumer-e2e/scripts/run-published-consumer-e2e.sh +3 -5
- package/src/client/canvas/BarHint.tsx +6 -3
- package/src/client/canvas/CanvasNode.tsx +34 -47
- package/src/client/canvas/CanvasViewport.tsx +6 -7
- package/src/client/canvas/ContextMenu.tsx +63 -3
- package/src/client/canvas/EdgeLayer.tsx +365 -169
- package/src/client/canvas/ExpandedNodeOverlay.tsx +1 -2
- package/src/client/canvas/Presentation.tsx +53 -3
- package/src/client/canvas/TopBar.tsx +24 -9
- package/src/client/canvas/auto-fit.ts +9 -11
- package/src/client/canvas/canvas-area.ts +1 -1
- package/src/client/canvas/use-focus-trap.ts +6 -1
- package/src/client/mermaid-entry.ts +6 -20
- package/src/client/mermaid-render.ts +39 -0
- package/src/client/nodes/AxStepControls.tsx +45 -69
- package/src/client/nodes/BoardNode.tsx +65 -9
- package/src/client/nodes/ContextNode.tsx +88 -396
- package/src/client/nodes/ExtAppFrame.tsx +283 -1462
- package/src/client/nodes/FileNode.tsx +92 -81
- package/src/client/nodes/HtmlNode.tsx +8 -36
- package/src/client/nodes/LedgerNode.tsx +31 -17
- package/src/client/nodes/MarkdownNode.tsx +8 -17
- package/src/client/nodes/McpAppNode.tsx +48 -154
- package/src/client/nodes/MermaidNode.tsx +80 -69
- package/src/client/nodes/PromptNode.tsx +12 -14
- package/src/client/nodes/ResponseNode.tsx +8 -6
- package/src/client/nodes/StatusNode.tsx +22 -7
- package/src/client/nodes/StatusSummary.tsx +5 -3
- package/src/client/nodes/TraceNode.tsx +8 -8
- package/src/client/nodes/ViewerFrame.tsx +262 -0
- package/src/client/nodes/ViewerHost.tsx +41 -0
- package/src/client/nodes/WebpageNode.tsx +22 -24
- package/src/client/nodes/iframe-document-url.ts +20 -27
- package/src/client/nodes/primitive-copy.ts +97 -0
- package/src/client/nodes/use-iframe-content-height.ts +34 -10
- package/src/client/nodes/use-surface-frame.ts +22 -11
- package/src/client/nodes/viewer-runtime-state.ts +41 -35
- package/src/client/state/canvas-store.ts +18 -12
- package/src/client/state/intent-bridge.ts +1 -0
- package/src/client/state/presentation.ts +135 -15
- package/src/client/state/sse-bridge.ts +18 -19
- package/src/client/theme/global.css +389 -229
- package/src/client/theme/surface-theme.css +6 -6
- package/src/json-render/charts/tufte-components.tsx +44 -2
- package/src/json-render/renderer/index.css +62 -2
- package/src/json-render/renderer/index.tsx +31 -7
- package/src/json-render/server.ts +3 -2
- package/src/server/board-export.ts +35 -24
- package/src/server/canvas-geometry.ts +1 -0
- package/src/server/canvas-state.ts +5 -0
- package/src/server/demo-state.json +106 -100
- package/src/server/html-primitives.ts +3 -9
- package/src/server/html-surface.ts +8 -0
- package/src/server/index.ts +19 -9
- package/src/server/operations/mcp-contracts.ts +17 -2
- package/src/server/operations/ops/boards.ts +1 -3
- package/src/server/operations/ops/viewport.ts +11 -9
- package/src/server/server.ts +7 -59
- package/src/server/tour-control.ts +66 -6
- package/src/shared/ax-surface-protocol.ts +0 -11
- package/src/shared/content-height-reporter.ts +4 -3
- package/src/shared/tour.ts +8 -0
|
@@ -812,13 +812,12 @@ function page(input: {
|
|
|
812
812
|
* { box-sizing: border-box; }
|
|
813
813
|
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); }
|
|
814
814
|
.shell { max-width: 1160px; margin: 0 auto; }
|
|
815
|
-
header.hero {
|
|
815
|
+
header.hero { margin-bottom: 20px; }
|
|
816
816
|
.kicker { color: var(--color-accent, #4a9eff); text-transform: uppercase; letter-spacing: .16em; font-size: 11px; font-weight: 800; }
|
|
817
817
|
h1 { margin: 5px 0 8px; font-size: clamp(28px, 4vw, 46px); line-height: .95; letter-spacing: -.04em; }
|
|
818
818
|
h2 { margin: 0 0 12px; font-size: 19px; letter-spacing: -.02em; }
|
|
819
819
|
h3 { margin: 0 0 8px; font-size: 15px; }
|
|
820
820
|
p { color: var(--color-text-secondary, #aeb8c2); margin: 0 0 12px; }
|
|
821
|
-
.actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
|
|
822
821
|
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; }
|
|
823
822
|
button:hover { border-color: var(--color-accent, #4a9eff); transform: translateY(-1px); }
|
|
824
823
|
table { width: 100%; border-collapse: collapse; color: var(--color-text-secondary, #aeb8c2); font-size: 13px; }
|
|
@@ -861,20 +860,20 @@ function page(input: {
|
|
|
861
860
|
.slide h2 { font-size: clamp(30px, 5vw, 58px); line-height: .95; }
|
|
862
861
|
.preview { white-space: pre-wrap; min-height: 220px; }
|
|
863
862
|
.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); }
|
|
864
|
-
@media (max-width: 760px) { body { padding: 14px; }
|
|
863
|
+
@media (max-width: 760px) { body { padding: 14px; } .two, .three { grid-template-columns: 1fr; } .sticky { position: static; } }
|
|
865
864
|
</style>
|
|
866
865
|
</head>
|
|
867
866
|
<body>
|
|
868
867
|
<div class="shell">
|
|
869
868
|
<header class="hero">
|
|
870
869
|
<div><div class="kicker">PMX HTML primitive / ${escapeHtml(input.kind)}</div><h1>${escapeHtml(input.title)}</h1><p>${escapeHtml(input.summary)}</p></div>
|
|
871
|
-
<div class="actions"><button type="button" data-copy-json>Copy JSON</button><button type="button" data-copy-prompt>Copy prompt</button></div>
|
|
872
870
|
</header>
|
|
873
871
|
${input.body}
|
|
874
872
|
</div>
|
|
875
873
|
<script type="application/json" id="pmx-data">${safeJson(input.data)}</script>
|
|
876
874
|
<script>
|
|
877
875
|
const PMX_DATA = JSON.parse(document.getElementById('pmx-data').textContent);
|
|
876
|
+
window.__pmxGetCopyJson = () => PMX_DATA;
|
|
878
877
|
function fallbackCopy(text) {
|
|
879
878
|
const el = document.createElement('textarea');
|
|
880
879
|
el.value = text;
|
|
@@ -890,11 +889,6 @@ async function copyText(text) {
|
|
|
890
889
|
if (navigator.clipboard && window.isSecureContext) await navigator.clipboard.writeText(text);
|
|
891
890
|
else fallbackCopy(text);
|
|
892
891
|
}
|
|
893
|
-
document.querySelector('[data-copy-json]')?.addEventListener('click', () => {
|
|
894
|
-
const payload = typeof window.__pmxGetCopyJson === 'function' ? window.__pmxGetCopyJson() : PMX_DATA;
|
|
895
|
-
copyText(JSON.stringify(payload, null, 2));
|
|
896
|
-
});
|
|
897
|
-
document.querySelector('[data-copy-prompt]')?.addEventListener('click', () => copyText('Use this PMX Canvas HTML primitive output as context:\\n\\n' + JSON.stringify(PMX_DATA, null, 2)));
|
|
898
892
|
${input.script ?? ''}
|
|
899
893
|
</script>
|
|
900
894
|
</body>
|
|
@@ -61,6 +61,14 @@ const PMX_CANVAS_THEME_TOKEN = ${token};
|
|
|
61
61
|
window.addEventListener('message', (event) => {
|
|
62
62
|
const message = event.data;
|
|
63
63
|
if (event.source !== window.parent || !message || message.source !== '${HTML_SURFACE_PUSH_SOURCE}' || message.token !== PMX_CANVAS_THEME_TOKEN) return;
|
|
64
|
+
if (message.type === 'copy-json' && PMX_CANVAS_THEME_TOKEN && typeof message.requestId === 'string') {
|
|
65
|
+
let json;
|
|
66
|
+
try {
|
|
67
|
+
if (typeof window.__pmxGetCopyJson === 'function') json = JSON.stringify(window.__pmxGetCopyJson(), null, 2);
|
|
68
|
+
} catch {}
|
|
69
|
+
window.parent.postMessage({ source: '${HTML_SURFACE_PUSH_SOURCE}', type: 'copy-json-result', token: PMX_CANVAS_THEME_TOKEN, requestId: message.requestId, json }, '*');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
64
72
|
if (message.type === 'check-ready') {
|
|
65
73
|
const reply = (type) => window.parent.postMessage({ source: '${HTML_SURFACE_PUSH_SOURCE}', type, token: PMX_CANVAS_THEME_TOKEN }, '*');
|
|
66
74
|
reply('mounted');
|
package/src/server/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
-
import type { Camera, Tour, TourStop } from '../shared/tour.js';
|
|
2
|
+
import type { Camera, Tour, TourPresenter, TourStop } from '../shared/tour.js';
|
|
3
3
|
import {
|
|
4
4
|
currentTour,
|
|
5
5
|
exitTour,
|
|
@@ -8,9 +8,10 @@ import {
|
|
|
8
8
|
type TourState,
|
|
9
9
|
type TourStep,
|
|
10
10
|
type TourStepResult,
|
|
11
|
+
type TourSuperseded,
|
|
11
12
|
} from './tour-control.js';
|
|
12
|
-
export type { Tour, TourStop } from '../shared/tour.js';
|
|
13
|
-
export type { TourState, TourStep, TourStepResult } from './tour-control.js';
|
|
13
|
+
export type { Tour, TourPresenter, TourStop } from '../shared/tour.js';
|
|
14
|
+
export type { TourState, TourStep, TourStepResult, TourSuperseded } from './tour-control.js';
|
|
14
15
|
import { agentPresence } from './agent-presence.js';
|
|
15
16
|
import { checkFenceTarget, type FenceTarget } from './scope-fence.js';
|
|
16
17
|
import type { AgentPhase, AgentPresence, AgentPresenceSnapshot } from '../shared/agent-presence.js';
|
|
@@ -631,16 +632,25 @@ export class PmxCanvas extends EventEmitter {
|
|
|
631
632
|
}
|
|
632
633
|
|
|
633
634
|
/** Present a tour stop (index, `next` or `previous`) in the workbench. Not recorded in undo history. */
|
|
634
|
-
goToTourStop(step: TourStep, options?: { present?: boolean }): TourStepResult
|
|
635
|
-
|
|
636
|
-
|
|
635
|
+
goToTourStop(step: TourStep, options?: { present?: boolean; presenter?: undefined }): TourStepResult;
|
|
636
|
+
goToTourStop(
|
|
637
|
+
step: TourStep,
|
|
638
|
+
options: { present?: boolean; presenter?: TourPresenter },
|
|
639
|
+
): TourStepResult | TourSuperseded;
|
|
640
|
+
goToTourStop(
|
|
641
|
+
step: TourStep,
|
|
642
|
+
options?: { present?: boolean; presenter?: TourPresenter },
|
|
643
|
+
): TourStepResult | TourSuperseded {
|
|
644
|
+
const result = goToTourStop(step, options?.presenter);
|
|
645
|
+
if (!('superseded' in result))
|
|
646
|
+
emitPrimaryWorkbenchEvent('canvas-tour-step', { ...result, present: options?.present !== false });
|
|
637
647
|
return result;
|
|
638
648
|
}
|
|
639
649
|
|
|
640
650
|
/** Leave presentation in every viewer. */
|
|
641
|
-
exitTour(): void {
|
|
642
|
-
exitTour();
|
|
643
|
-
emitPrimaryWorkbenchEvent('canvas-tour-exit', {});
|
|
651
|
+
exitTour(presenter?: TourPresenter): void {
|
|
652
|
+
const result = exitTour(presenter);
|
|
653
|
+
if (!('superseded' in result)) emitPrimaryWorkbenchEvent('canvas-tour-exit', { ...result });
|
|
644
654
|
}
|
|
645
655
|
|
|
646
656
|
/** Ease the camera to a node, world rect or viewport. Not recorded in undo history. */
|
|
@@ -11,6 +11,7 @@ import { tourSchema, tourStopSchema } from '../../shared/tour.js';
|
|
|
11
11
|
const id = z.string().min(1);
|
|
12
12
|
const count = z.number().int().nonnegative();
|
|
13
13
|
const ok = z.looseObject({ ok: z.boolean() });
|
|
14
|
+
const tourSuperseded = z.object({ ok: z.literal(true), superseded: z.literal(true) });
|
|
14
15
|
const identified = z.looseObject({ id });
|
|
15
16
|
const message = z.strictObject({ message: z.string() });
|
|
16
17
|
const node = z.looseObject({
|
|
@@ -123,8 +124,22 @@ const payloads: Record<string, ZodType> = {
|
|
|
123
124
|
'canvas.redo': history,
|
|
124
125
|
'tour.get': z.looseObject({ tour: tourSchema, derived: z.boolean(), position: count.nullable() }),
|
|
125
126
|
'tour.set': ok,
|
|
126
|
-
'tour.go': z.
|
|
127
|
-
|
|
127
|
+
'tour.go': z.union([
|
|
128
|
+
z.looseObject({
|
|
129
|
+
ok: z.boolean(),
|
|
130
|
+
catalogEpoch: z.string(),
|
|
131
|
+
boardGeneration: count,
|
|
132
|
+
sequence: count,
|
|
133
|
+
index: count,
|
|
134
|
+
total: count,
|
|
135
|
+
stop: tourStopSchema,
|
|
136
|
+
}),
|
|
137
|
+
tourSuperseded,
|
|
138
|
+
]),
|
|
139
|
+
'tour.exit': z.union([
|
|
140
|
+
z.looseObject({ ok: z.boolean(), catalogEpoch: z.string(), boardGeneration: count, sequence: count }),
|
|
141
|
+
tourSuperseded,
|
|
142
|
+
]),
|
|
128
143
|
'camera.move': z.looseObject({ ok: z.boolean(), stop: tourStopSchema, viewport }),
|
|
129
144
|
arrange: z.looseObject({ ok: z.boolean(), layout: z.enum(['grid', 'column', 'flow']) }),
|
|
130
145
|
'node.focus': z.looseObject({ ok: z.boolean(), focused: id, panned: z.boolean() }),
|
|
@@ -12,11 +12,10 @@
|
|
|
12
12
|
*
|
|
13
13
|
* This module must never import server.ts or index.ts.
|
|
14
14
|
*/
|
|
15
|
-
import { randomUUID } from 'node:crypto';
|
|
16
15
|
import { z } from 'zod';
|
|
17
16
|
import { normalizeBoardFolder } from '../../../shared/boards.js';
|
|
18
17
|
import { relationVocabularySchema } from '../../../shared/relations.js';
|
|
19
|
-
import { canvasState, type CanvasLayout } from '../../canvas-state.js';
|
|
18
|
+
import { boardCatalogEpoch, canvasState, type CanvasLayout } from '../../canvas-state.js';
|
|
20
19
|
import { openCanvasBoard } from '../../canvas-operations.js';
|
|
21
20
|
import { summarizeNodeForAgentContext } from '../../../shared/agent-context.js';
|
|
22
21
|
import { BRIEF_SUMMARY_LENGTH } from '../../context-brief.js';
|
|
@@ -25,7 +24,6 @@ import { defineOperation, OperationError, type Operation, type OperationContext
|
|
|
25
24
|
|
|
26
25
|
/** The most cards a Home thumbnail draws (the largest first); the rest are noise at that size. */
|
|
27
26
|
const PREVIEW_CARDS = 24;
|
|
28
|
-
export const boardCatalogEpoch = randomUUID();
|
|
29
27
|
let catalogRevision = 0;
|
|
30
28
|
let reloadRevision = 0;
|
|
31
29
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* This module must never import server.ts or index.ts.
|
|
14
14
|
*/
|
|
15
15
|
import { z } from 'zod';
|
|
16
|
-
import { tourSchema, rectSchema, cameraSchema, tourStopSchema } from '../../../shared/tour.js';
|
|
16
|
+
import { tourSchema, rectSchema, cameraSchema, tourStopSchema, tourPresenterSchema } from '../../../shared/tour.js';
|
|
17
17
|
import { canvasState } from '../../canvas-state.js';
|
|
18
18
|
import {
|
|
19
19
|
arrangeCanvasNodes,
|
|
@@ -339,6 +339,7 @@ const tourGetOperation = defineOperation({
|
|
|
339
339
|
const tourGoShape = {
|
|
340
340
|
stop: z.number().int().nonnegative().optional().describe('Zero-based stop index to go to'),
|
|
341
341
|
step: z.enum(['next', 'previous']).optional().describe('Step relative to the current stop (clamped at the ends)'),
|
|
342
|
+
presenter: tourPresenterSchema.optional().describe('Document intent order and expected board activation'),
|
|
342
343
|
present: z
|
|
343
344
|
.boolean()
|
|
344
345
|
.optional()
|
|
@@ -360,23 +361,24 @@ const tourGoOperation = defineOperation({
|
|
|
360
361
|
if ((input.stop === undefined) === (input.step === undefined)) {
|
|
361
362
|
throw new OperationError('Give exactly one of stop (index) or step ("next" | "previous").');
|
|
362
363
|
}
|
|
363
|
-
const result = goToTourStop(input.stop ?? (input.step as 'next' | 'previous'));
|
|
364
|
-
ctx.emit('canvas-tour-step', { ...result, present: input.present !== false });
|
|
364
|
+
const result = goToTourStop(input.stop ?? (input.step as 'next' | 'previous'), input.presenter);
|
|
365
|
+
if (!('superseded' in result)) ctx.emit('canvas-tour-step', { ...result, present: input.present !== false });
|
|
365
366
|
return { ok: true, ...result };
|
|
366
367
|
},
|
|
367
368
|
});
|
|
368
369
|
|
|
370
|
+
const tourExitShape = { presenter: tourGoShape.presenter };
|
|
369
371
|
const tourExitOperation = defineOperation({
|
|
370
372
|
name: 'tour.exit',
|
|
371
373
|
mutates: false,
|
|
372
|
-
input: z.object(
|
|
373
|
-
inputShape:
|
|
374
|
+
input: z.object(tourExitShape),
|
|
375
|
+
inputShape: tourExitShape,
|
|
374
376
|
http: { method: 'POST', path: '/api/canvas/tour/exit' },
|
|
375
377
|
mcp: { toolName: 'canvas_tour_exit', description: 'Leave presentation in every viewer.' },
|
|
376
|
-
handler: (
|
|
377
|
-
exitTour();
|
|
378
|
-
ctx.emit('canvas-tour-exit', {});
|
|
379
|
-
return { ok: true };
|
|
378
|
+
handler: (input, ctx) => {
|
|
379
|
+
const result = exitTour(input.presenter);
|
|
380
|
+
if (!('superseded' in result)) ctx.emit('canvas-tour-exit', { ...result });
|
|
381
|
+
return { ok: true, ...result };
|
|
380
382
|
},
|
|
381
383
|
});
|
|
382
384
|
|
package/src/server/server.ts
CHANGED
|
@@ -47,8 +47,8 @@ import { basename, extname, isAbsolute, join, relative, resolve, sep } from 'nod
|
|
|
47
47
|
import * as marked from 'marked';
|
|
48
48
|
import { renderWikiMarkdown, resolveWikiLinks, wikiReferences } from './wiki-links.js';
|
|
49
49
|
import { wikiLinksFromData } from '../shared/memory-graph.js';
|
|
50
|
-
import { type CanvasNodeState, IMAGE_MIME_MAP, canvasState } from './canvas-state.js';
|
|
51
|
-
import {
|
|
50
|
+
import { type CanvasNodeState, IMAGE_MIME_MAP, boardCatalogEpoch, canvasState } from './canvas-state.js';
|
|
51
|
+
import { boardsPayload } from './operations/ops/boards.js';
|
|
52
52
|
import {
|
|
53
53
|
buildAxBridge,
|
|
54
54
|
buildAxStateBridge,
|
|
@@ -72,7 +72,7 @@ import { buildAgentContextPreamble } from '../shared/agent-context.js';
|
|
|
72
72
|
import { buildCanvasAxSurfaceSnapshot } from './ax-context.js';
|
|
73
73
|
import { resolveNodeAxCapabilities } from './ax-interaction.js';
|
|
74
74
|
import { normalizeCanvasTheme, type CanvasTheme } from './canvas-db.js';
|
|
75
|
-
import {
|
|
75
|
+
import { isCanvasTheme } from '../shared/themes.js';
|
|
76
76
|
import { canOpenNodeAsSurface } from '../shared/surface.js';
|
|
77
77
|
import { validateLocalImageFile } from './image-source.js';
|
|
78
78
|
import {
|
|
@@ -1504,44 +1504,6 @@ function handleIframeProbe(): Response {
|
|
|
1504
1504
|
);
|
|
1505
1505
|
}
|
|
1506
1506
|
|
|
1507
|
-
// ── Ext-app recovery diagnostics (0.3.2 report Finding N) ──────
|
|
1508
|
-
//
|
|
1509
|
-
// The WebKit black-tile recovery trail is otherwise trapped inside host panels
|
|
1510
|
-
// without devtools (the Copilot WKWebView). Clients mirror their bounded
|
|
1511
|
-
// recovery log here; agents/testers read it back over HTTP. In-memory only,
|
|
1512
|
-
// diagnostics-grade: no persistence, no snapshots.
|
|
1513
|
-
const EXT_APP_RECOVERY_LOG_MAX = 500;
|
|
1514
|
-
const EXT_APP_RECOVERY_FIELD_MAX = 300;
|
|
1515
|
-
const extAppRecoveryEntries: Array<Record<string, unknown>> = [];
|
|
1516
|
-
|
|
1517
|
-
/**
|
|
1518
|
-
* Clamp a reported entry to the known trail shape ({ t, nodeId, event,
|
|
1519
|
-
* visibility }) with bounded string lengths. The count trim below caps the
|
|
1520
|
-
* number of entries; this caps bytes per entry, so the in-memory log stays
|
|
1521
|
-
* diagnostics-sized no matter what a client posts.
|
|
1522
|
-
*/
|
|
1523
|
-
function clampExtAppRecoveryEntry(entry: Record<string, unknown>): Record<string, unknown> {
|
|
1524
|
-
const clamped: Record<string, unknown> = {};
|
|
1525
|
-
if (typeof entry.t === 'number' && Number.isFinite(entry.t)) clamped.t = entry.t;
|
|
1526
|
-
for (const key of ['nodeId', 'event', 'visibility'] as const) {
|
|
1527
|
-
const value = entry[key];
|
|
1528
|
-
if (typeof value === 'string') clamped[key] = value.slice(0, EXT_APP_RECOVERY_FIELD_MAX);
|
|
1529
|
-
}
|
|
1530
|
-
return clamped;
|
|
1531
|
-
}
|
|
1532
|
-
|
|
1533
|
-
function handleExtAppRecoveryReport(entries: unknown): Response {
|
|
1534
|
-
if (!Array.isArray(entries)) return responseJson({ ok: false, error: 'entries must be an array' }, 400);
|
|
1535
|
-
for (const entry of entries.slice(0, 100)) {
|
|
1536
|
-
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) continue;
|
|
1537
|
-
extAppRecoveryEntries.push(clampExtAppRecoveryEntry(entry as Record<string, unknown>));
|
|
1538
|
-
}
|
|
1539
|
-
if (extAppRecoveryEntries.length > EXT_APP_RECOVERY_LOG_MAX) {
|
|
1540
|
-
extAppRecoveryEntries.splice(0, extAppRecoveryEntries.length - EXT_APP_RECOVERY_LOG_MAX);
|
|
1541
|
-
}
|
|
1542
|
-
return responseJson({ ok: true, total: extAppRecoveryEntries.length });
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1545
1507
|
// ── Node surfaces ("Open as site") ─────────────────────────────
|
|
1546
1508
|
//
|
|
1547
1509
|
// One stable, node-addressable URL — /api/canvas/surface/:nodeId — that serves
|
|
@@ -1841,14 +1803,7 @@ async function handleJsonRenderView(url: URL): Promise<Response> {
|
|
|
1841
1803
|
);
|
|
1842
1804
|
|
|
1843
1805
|
const themeValue = url.searchParams.get('theme');
|
|
1844
|
-
|
|
1845
|
-
// canvas themes (midnight, sepia, …) collapse to their dark/light scheme.
|
|
1846
|
-
const theme =
|
|
1847
|
-
themeValue === 'dark' || themeValue === 'light' || themeValue === 'high-contrast'
|
|
1848
|
-
? themeValue
|
|
1849
|
-
: isCanvasTheme(themeValue)
|
|
1850
|
-
? canvasThemeScheme(themeValue)
|
|
1851
|
-
: undefined;
|
|
1806
|
+
const theme = themeValue === 'dark' || themeValue === 'light' || isCanvasTheme(themeValue) ? themeValue : 'harbor';
|
|
1852
1807
|
const title = (node.data.title as string) || node.id;
|
|
1853
1808
|
// Devtools panel is double-gated: the operator must opt in via the env flag
|
|
1854
1809
|
// AND the request must carry ?devtools=1. Off by default in all normal runs.
|
|
@@ -2274,6 +2229,9 @@ function buildWorkbenchConnectSnapshot(
|
|
|
2274
2229
|
timestamp: new Date().toISOString(),
|
|
2275
2230
|
},
|
|
2276
2231
|
});
|
|
2232
|
+
// Establish ownership in this ordered stream before layouts and live tour
|
|
2233
|
+
// commands. The separate catalog HTTP response may arrive after either.
|
|
2234
|
+
events.push({ event: 'boards-changed', payload: boardsPayload() });
|
|
2277
2235
|
if (primaryWorkbenchPath) {
|
|
2278
2236
|
events.push({
|
|
2279
2237
|
event: 'workbench-open',
|
|
@@ -3571,16 +3529,6 @@ export function startCanvasServer(options: CanvasServerOptions = {}): string | n
|
|
|
3571
3529
|
return handleJsonRenderView(url);
|
|
3572
3530
|
}
|
|
3573
3531
|
|
|
3574
|
-
if (url.pathname === '/api/canvas/debug/ext-app-recovery' && req.method === 'POST') {
|
|
3575
|
-
const body = await readJson(req);
|
|
3576
|
-
if (body === null) return malformedJsonResponse();
|
|
3577
|
-
return handleExtAppRecoveryReport(body.entries);
|
|
3578
|
-
}
|
|
3579
|
-
|
|
3580
|
-
if (url.pathname === '/api/canvas/debug/ext-app-recovery' && req.method === 'GET') {
|
|
3581
|
-
return responseJson({ ok: true, entries: extAppRecoveryEntries });
|
|
3582
|
-
}
|
|
3583
|
-
|
|
3584
3532
|
if (url.pathname === '/api/canvas/frame-documents' && req.method === 'POST') {
|
|
3585
3533
|
return handleCreateFrameDocument(req);
|
|
3586
3534
|
}
|
|
@@ -8,19 +8,38 @@
|
|
|
8
8
|
*
|
|
9
9
|
* This module must never import server.ts or index.ts.
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
12
|
-
|
|
11
|
+
import {
|
|
12
|
+
derivedTour,
|
|
13
|
+
resolveStop,
|
|
14
|
+
tourPresenterSchema,
|
|
15
|
+
tourStopSchema,
|
|
16
|
+
type Camera,
|
|
17
|
+
type Tour,
|
|
18
|
+
type TourPresenter,
|
|
19
|
+
type TourStop,
|
|
20
|
+
} from '../shared/tour.js';
|
|
21
|
+
import { boardCatalogEpoch, canvasState } from './canvas-state.js';
|
|
13
22
|
import { getClientViewportSize } from './canvas-operations.js';
|
|
14
23
|
import { OperationError } from './operations/types.js';
|
|
15
24
|
|
|
16
25
|
export type TourStep = number | 'next' | 'previous';
|
|
17
26
|
|
|
18
|
-
export interface
|
|
27
|
+
export interface TourDelivery {
|
|
28
|
+
catalogEpoch: string;
|
|
29
|
+
boardGeneration: number;
|
|
30
|
+
sequence: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface TourStepResult extends TourDelivery {
|
|
19
34
|
index: number;
|
|
20
35
|
total: number;
|
|
21
36
|
stop: TourStop;
|
|
22
37
|
}
|
|
23
38
|
|
|
39
|
+
export interface TourSuperseded {
|
|
40
|
+
superseded: true;
|
|
41
|
+
}
|
|
42
|
+
|
|
24
43
|
export interface TourState {
|
|
25
44
|
tour: Tour;
|
|
26
45
|
derived: boolean;
|
|
@@ -28,6 +47,35 @@ export interface TourState {
|
|
|
28
47
|
}
|
|
29
48
|
|
|
30
49
|
let cursor: { boardGeneration: number; tour: string; index: number } | null = null;
|
|
50
|
+
// One order for both HTTP acknowledgements and event delivery in this server run.
|
|
51
|
+
let sequence = 0;
|
|
52
|
+
let presenterGeneration = -1;
|
|
53
|
+
const presenterIntents = new Map<string, number>();
|
|
54
|
+
|
|
55
|
+
function claimPresenter(presenter: TourPresenter | undefined): boolean {
|
|
56
|
+
if (presenter === undefined) return true;
|
|
57
|
+
const { clientId, intent, catalogEpoch, boardGeneration } = tourPresenterSchema.parse(presenter);
|
|
58
|
+
if (catalogEpoch !== boardCatalogEpoch || boardGeneration !== canvasState.boardGeneration) return false;
|
|
59
|
+
if (presenterGeneration !== boardGeneration) {
|
|
60
|
+
presenterIntents.clear();
|
|
61
|
+
presenterGeneration = boardGeneration;
|
|
62
|
+
}
|
|
63
|
+
const previous = presenterIntents.get(clientId);
|
|
64
|
+
if (previous !== undefined && intent <= previous) return false;
|
|
65
|
+
// Never evict an identity: that would make its delayed requests valid again.
|
|
66
|
+
if (previous === undefined && presenterIntents.size >= 4096)
|
|
67
|
+
throw new OperationError(
|
|
68
|
+
'Too many presentation documents on this board activation. Reopen the board to continue.',
|
|
69
|
+
409,
|
|
70
|
+
);
|
|
71
|
+
// Even a later invalid stop supersedes older commands. Claim before resolving it.
|
|
72
|
+
presenterIntents.set(clientId, intent);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function nextDelivery(): TourDelivery {
|
|
77
|
+
return { catalogEpoch: boardCatalogEpoch, boardGeneration: canvasState.boardGeneration, sequence: ++sequence };
|
|
78
|
+
}
|
|
31
79
|
|
|
32
80
|
export function currentTour(): TourState {
|
|
33
81
|
const saved = canvasState.getTour();
|
|
@@ -44,7 +92,10 @@ function tourPosition(tour: Tour): number | null {
|
|
|
44
92
|
}
|
|
45
93
|
|
|
46
94
|
/** Move the shared cursor. `next`/`previous` clamp at the ends; an explicit index must exist. */
|
|
47
|
-
export function goToTourStop(step: TourStep): TourStepResult
|
|
95
|
+
export function goToTourStop(step: TourStep): TourStepResult;
|
|
96
|
+
export function goToTourStop(step: TourStep, presenter: TourPresenter | undefined): TourStepResult | TourSuperseded;
|
|
97
|
+
export function goToTourStop(step: TourStep, presenter?: TourPresenter): TourStepResult | TourSuperseded {
|
|
98
|
+
if (!claimPresenter(presenter)) return { superseded: true };
|
|
48
99
|
const { tour, position: at } = currentTour();
|
|
49
100
|
const { stops } = tour;
|
|
50
101
|
if (stops.length === 0) {
|
|
@@ -56,11 +107,20 @@ export function goToTourStop(step: TourStep): TourStepResult {
|
|
|
56
107
|
else if (Number.isInteger(step) && step >= 0 && step < stops.length) index = step;
|
|
57
108
|
else throw new OperationError(`Tour stop ${step} does not exist (stops 0–${stops.length - 1}).`, 400);
|
|
58
109
|
cursor = { boardGeneration: canvasState.boardGeneration, tour: JSON.stringify(tour), index };
|
|
59
|
-
return {
|
|
110
|
+
return {
|
|
111
|
+
...nextDelivery(),
|
|
112
|
+
index,
|
|
113
|
+
total: stops.length,
|
|
114
|
+
stop: stops[index] as TourStop,
|
|
115
|
+
};
|
|
60
116
|
}
|
|
61
117
|
|
|
62
|
-
export function exitTour():
|
|
118
|
+
export function exitTour(): TourDelivery;
|
|
119
|
+
export function exitTour(presenter: TourPresenter | undefined): TourDelivery | TourSuperseded;
|
|
120
|
+
export function exitTour(presenter?: TourPresenter): TourDelivery | TourSuperseded {
|
|
121
|
+
if (!claimPresenter(presenter)) return { superseded: true };
|
|
63
122
|
cursor = null;
|
|
123
|
+
return nextDelivery();
|
|
64
124
|
}
|
|
65
125
|
|
|
66
126
|
/**
|
|
@@ -20,14 +20,3 @@ export const HTML_SURFACE_PUSH_SOURCE = 'pmx-canvas-html-node';
|
|
|
20
20
|
|
|
21
21
|
/** Tab-local JSON viewer state handoff, independent of AX capabilities. */
|
|
22
22
|
export const JSON_VIEWER_STATE_SOURCE = 'pmx-canvas-json-state';
|
|
23
|
-
|
|
24
|
-
/** Ext-app boot beacon: the iframe's scripts executed (WebKit watchdog liveness). */
|
|
25
|
-
export const EXT_APP_BOOT_BEACON_SOURCE = 'pmx-canvas-ext-app-alive';
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Parent→ext-app paint probe (Finding N): the parent asks the app document to
|
|
29
|
-
* answer with a double-rAF `paint-tick` beacon. A frame whose rendering
|
|
30
|
-
* pipeline is live answers within a frame or two; silence past the timeout is
|
|
31
|
-
* the paint-fail signal that drives the recovery ladder.
|
|
32
|
-
*/
|
|
33
|
-
export const EXT_APP_PAINT_PROBE_SOURCE = 'pmx-canvas-ext-app-paint-probe';
|
|
@@ -22,11 +22,12 @@ export function sanitizeFrameToken(token: string): string {
|
|
|
22
22
|
export function contentHeightReporterSource(frameToken: string): string {
|
|
23
23
|
const token = JSON.stringify(sanitizeFrameToken(frameToken));
|
|
24
24
|
return (
|
|
25
|
-
`(function(){var T=${token};var last=0,timer=null;` +
|
|
25
|
+
`(function(){var T=${token};var last=0,lastViewport=0,timer=null;` +
|
|
26
26
|
`function m(){var d=document.documentElement;return Math.max(d?d.scrollHeight:0,document.body?document.body.scrollHeight:0);}` +
|
|
27
|
-
`function r(){var h=m();if(Math.abs(h-last)<=4)return;last=h;window.parent.postMessage({source:'pmx-canvas-frame',type:'content-height',token:T,height:h},'*');}` +
|
|
27
|
+
`function r(force){var h=m(),v=window.innerHeight;if(!force&&Math.abs(h-last)<=4&&v===lastViewport)return;last=h;lastViewport=v;window.parent.postMessage({source:'pmx-canvas-frame',type:'content-height',token:T,height:h},'*');}` +
|
|
28
28
|
`function s(){if(timer)return;timer=setTimeout(function(){timer=null;r();},100);}` +
|
|
29
|
-
`
|
|
29
|
+
`window.addEventListener('message',function(e){var d=e.data;if(e.source===window.parent&&d&&d.source==='pmx-canvas-frame'&&d.type==='measure-content'&&d.token===T)r(true);});` +
|
|
30
|
+
`if(document.readyState!=='loading')s();window.addEventListener('load',s,true);window.addEventListener('resize',s);` +
|
|
30
31
|
`try{new ResizeObserver(s).observe(document.documentElement);}catch(e){}` +
|
|
31
32
|
`new MutationObserver(s).observe(document.documentElement,{subtree:true,childList:true,characterData:true,attributes:true});` +
|
|
32
33
|
`if(document.fonts)document.fonts.ready.then(s);setTimeout(s,60);})();`
|
package/src/shared/tour.ts
CHANGED
|
@@ -21,6 +21,14 @@ export const tourStopSchema = z.object({
|
|
|
21
21
|
pullback: z.number().min(0).max(4).optional(),
|
|
22
22
|
});
|
|
23
23
|
export const tourSchema = z.object({ stops: z.array(tourStopSchema).max(1000) });
|
|
24
|
+
/** Latest intent from one document, scoped to the server's board activation. */
|
|
25
|
+
export const tourPresenterSchema = z.object({
|
|
26
|
+
clientId: z.string().min(1).max(128),
|
|
27
|
+
intent: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
28
|
+
catalogEpoch: z.string().min(1).max(128),
|
|
29
|
+
boardGeneration: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
30
|
+
});
|
|
31
|
+
export type TourPresenter = z.infer<typeof tourPresenterSchema>;
|
|
24
32
|
export type Tour = z.infer<typeof tourSchema>;
|
|
25
33
|
export type TourStop = z.infer<typeof tourStopSchema>;
|
|
26
34
|
export type Camera = z.infer<typeof cameraSchema>;
|