pmx-canvas 0.1.28 → 0.1.30
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 +193 -0
- package/Readme.md +20 -10
- package/dist/canvas/global.css +13 -0
- package/dist/canvas/index.js +80 -163
- package/dist/canvas/surface-theme.css +142 -0
- package/dist/json-render/index.js +103 -103
- package/dist/types/client/nodes/HtmlNode.d.ts +0 -7
- package/dist/types/client/nodes/ax-node-actions.d.ts +18 -0
- package/dist/types/client/nodes/surface-url.d.ts +22 -0
- package/dist/types/client/state/attention-bridge.d.ts +3 -0
- package/dist/types/client/state/intent-bridge.d.ts +17 -0
- package/dist/types/json-render/renderer/index.d.ts +2 -0
- package/dist/types/json-render/schema.d.ts +2 -0
- package/dist/types/json-render/server.d.ts +2 -0
- package/dist/types/mcp/canvas-access.d.ts +47 -0
- package/dist/types/server/ax-interaction.d.ts +210 -0
- package/dist/types/server/ax-state.d.ts +67 -1
- package/dist/types/server/canvas-db.d.ts +4 -0
- package/dist/types/server/canvas-serialization.d.ts +2 -0
- package/dist/types/server/canvas-state.d.ts +47 -2
- package/dist/types/server/html-surface.d.ts +40 -0
- package/dist/types/server/index.d.ts +56 -2
- package/dist/types/server/mutation-history.d.ts +1 -1
- package/dist/types/server/placement.d.ts +1 -1
- package/dist/types/shared/surface.d.ts +19 -0
- package/docs/cli.md +30 -0
- package/docs/http-api.md +55 -0
- package/docs/mcp.md +40 -2
- package/docs/node-types.md +26 -0
- package/docs/plans/plan-004-pmx-ax-primitives.md +623 -394
- package/docs/sdk.md +20 -0
- package/package.json +2 -2
- package/skills/pmx-canvas/SKILL.md +107 -9
- package/src/cli/agent.ts +190 -0
- package/src/client/canvas/CanvasNode.tsx +8 -4
- package/src/client/canvas/ExpandedNodeOverlay.tsx +12 -0
- package/src/client/nodes/ContextNode.tsx +17 -0
- package/src/client/nodes/ExtAppFrame.tsx +40 -3
- package/src/client/nodes/FileNode.tsx +26 -0
- package/src/client/nodes/HtmlNode.tsx +60 -188
- package/src/client/nodes/LedgerNode.tsx +39 -5
- package/src/client/nodes/McpAppNode.tsx +47 -2
- package/src/client/nodes/StatusNode.tsx +20 -0
- package/src/client/nodes/ax-node-actions.ts +39 -0
- package/src/client/nodes/surface-url.ts +48 -0
- package/src/client/state/attention-bridge.ts +5 -0
- package/src/client/state/intent-bridge.ts +33 -0
- package/src/client/theme/global.css +13 -0
- package/src/client/theme/surface-theme.css +142 -0
- package/src/json-render/renderer/index.tsx +31 -0
- package/src/json-render/schema.ts +4 -0
- package/src/json-render/server.ts +31 -1
- package/src/mcp/canvas-access.ts +212 -1
- package/src/mcp/server.ts +238 -5
- package/src/server/ax-context.ts +3 -0
- package/src/server/ax-interaction.ts +549 -0
- package/src/server/ax-state.ts +188 -2
- package/src/server/canvas-db.ts +20 -0
- package/src/server/canvas-operations.ts +11 -0
- package/src/server/canvas-serialization.ts +9 -0
- package/src/server/canvas-state.ts +177 -16
- package/src/server/html-surface.ts +170 -0
- package/src/server/index.ts +105 -1
- package/src/server/mutation-history.ts +5 -0
- package/src/server/placement.ts +5 -1
- package/src/server/server.ts +305 -0
- package/src/shared/surface.ts +38 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side builder for an `html` node's standalone surface document.
|
|
3
|
+
*
|
|
4
|
+
* This is the canonical wrapper that used to live in the client (HtmlNode's
|
|
5
|
+
* `buildSrcDoc`). It now lives on the server so a single document definition
|
|
6
|
+
* backs BOTH the in-canvas iframe and the "Open as site" tab — the iframe and
|
|
7
|
+
* the standalone tab load the exact same URL (/api/canvas/surface/:nodeId), so
|
|
8
|
+
* there is one render path and no content fork.
|
|
9
|
+
*
|
|
10
|
+
* Theming: instead of inlining a token `<style>` block, the document links the
|
|
11
|
+
* same-origin `/canvas/surface-theme.css` stylesheet and selects a palette via
|
|
12
|
+
* the `<html data-theme="...">` attribute. A sandboxed (opaque-origin) document
|
|
13
|
+
* can still load this same-origin stylesheet, and live theme switching works by
|
|
14
|
+
* toggling the attribute (the theme bridge below) — no CSS payload over
|
|
15
|
+
* postMessage required.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export type SurfaceTheme = 'dark' | 'light' | 'high-contrast';
|
|
19
|
+
|
|
20
|
+
/** Path the surface document links for its theme tokens (served from dist/canvas). */
|
|
21
|
+
export const SURFACE_THEME_STYLESHEET = '/canvas/surface-theme.css';
|
|
22
|
+
|
|
23
|
+
/** CSP sandbox tokens for an `html`/`html-primitive` surface — scripts only, opaque origin. */
|
|
24
|
+
export const HTML_SURFACE_SANDBOX = 'allow-scripts';
|
|
25
|
+
|
|
26
|
+
export function normalizeSurfaceTheme(value: string | null | undefined): SurfaceTheme {
|
|
27
|
+
return value === 'light' || value === 'high-contrast' ? value : 'dark';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Restrict a caller-supplied token to a safe charset before it is embedded
|
|
32
|
+
* inside an inline `<script>` string. The token is a CSRF-style nonce minted by
|
|
33
|
+
* the client (shape `theme-<base36>-<base36>` / `presentation-<...>`), but it
|
|
34
|
+
* arrives as a query parameter, so it must never be trusted verbatim — anything
|
|
35
|
+
* outside `[A-Za-z0-9_-]` (notably `<`, `"`, backtick) could break out of the
|
|
36
|
+
* script context.
|
|
37
|
+
*/
|
|
38
|
+
function sanitizeToken(value: string | null | undefined): string {
|
|
39
|
+
if (typeof value !== 'string') return '';
|
|
40
|
+
return value.replace(/[^A-Za-z0-9_-]/g, '').slice(0, 64);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Bridge that lets the parent canvas live-switch the surface theme by toggling
|
|
45
|
+
* the `data-theme` attribute. Validates source + type + nonce so unrelated
|
|
46
|
+
* windows cannot drive the attribute. No-op in a standalone tab (no parent
|
|
47
|
+
* posts to it), which is exactly what we want there.
|
|
48
|
+
*/
|
|
49
|
+
function buildThemeBridge(themeToken: string): string {
|
|
50
|
+
const token = JSON.stringify(themeToken);
|
|
51
|
+
return `<script data-pmx-canvas-theme-bridge>
|
|
52
|
+
const PMX_CANVAS_THEME_TOKEN = ${token};
|
|
53
|
+
window.addEventListener('message', (event) => {
|
|
54
|
+
const message = event.data;
|
|
55
|
+
if (!message || message.source !== 'pmx-canvas-html-node' || message.type !== 'theme-update' || message.token !== PMX_CANVAS_THEME_TOKEN) return;
|
|
56
|
+
if (typeof message.theme !== 'string') return;
|
|
57
|
+
document.documentElement.setAttribute('data-pmx-canvas-theme', message.theme);
|
|
58
|
+
document.documentElement.setAttribute('data-theme', message.theme);
|
|
59
|
+
});
|
|
60
|
+
</script>`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Presentation bridge (deck mode). Identical contract to the previous client
|
|
65
|
+
* version: Escape posts an exit message to the parent overlay, and the parent
|
|
66
|
+
* can forward slide keys back in. Only relevant when the surface is embedded in
|
|
67
|
+
* the in-canvas presentation overlay; harmless (inert) in a standalone tab.
|
|
68
|
+
*/
|
|
69
|
+
function buildPresentationEscapeBridge(exitToken: string): string {
|
|
70
|
+
const token = JSON.stringify(exitToken);
|
|
71
|
+
return `<script data-pmx-canvas-presentation-bridge>
|
|
72
|
+
const PMX_CANVAS_PRESENTATION_EXIT_TOKEN = ${token};
|
|
73
|
+
document.addEventListener('keydown', (event) => {
|
|
74
|
+
if (event.key === 'Escape') {
|
|
75
|
+
window.parent.postMessage({ source: 'pmx-canvas-html-node', type: 'presentation-exit', token: PMX_CANVAS_PRESENTATION_EXIT_TOKEN }, '*');
|
|
76
|
+
}
|
|
77
|
+
}, true);
|
|
78
|
+
window.addEventListener('message', (event) => {
|
|
79
|
+
const message = event.data;
|
|
80
|
+
if (!message || message.source !== 'pmx-canvas-html-node' || message.type !== 'presentation-key' || message.token !== PMX_CANVAS_PRESENTATION_EXIT_TOKEN) return;
|
|
81
|
+
if (typeof message.key !== 'string') return;
|
|
82
|
+
if (typeof window.PMX_CANVAS_PRESENTATION_HANDLE_KEY === 'function') {
|
|
83
|
+
window.PMX_CANVAS_PRESENTATION_HANDLE_KEY(message.key);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
document.dispatchEvent(new CustomEvent('pmx-presentation-key', { detail: { key: message.key }, bubbles: true, cancelable: true }));
|
|
87
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: message.key, bubbles: true, cancelable: true }));
|
|
88
|
+
});
|
|
89
|
+
</script>`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function injectIntoHead(html: string, content: string): string {
|
|
93
|
+
if (/<head[\s>]/i.test(html)) {
|
|
94
|
+
return html.replace(/<head([^>]*)>/i, `<head$1>${content}`);
|
|
95
|
+
}
|
|
96
|
+
if (/<html[\s>]/i.test(html)) {
|
|
97
|
+
return html.replace(/<html([^>]*)>/i, `<html$1><head>${content}</head>`);
|
|
98
|
+
}
|
|
99
|
+
return html;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Bridge that exposes `window.PMX_AX.emit(type, payload)` to author HTML. Calls
|
|
104
|
+
* post a nonce-tagged message to the parent canvas, which validates the nonce +
|
|
105
|
+
* node id and submits the interaction through the capability-gated endpoint. Only
|
|
106
|
+
* injected when the node's AX capabilities are enabled (opt-in for `html`), and
|
|
107
|
+
* the server re-validates every interaction — so this is a convenience surface,
|
|
108
|
+
* not a trust boundary.
|
|
109
|
+
*/
|
|
110
|
+
function buildAxBridge(axToken: string, nodeId: string): string {
|
|
111
|
+
const token = JSON.stringify(axToken);
|
|
112
|
+
const node = JSON.stringify(nodeId);
|
|
113
|
+
return `<script data-pmx-canvas-ax-bridge>
|
|
114
|
+
const PMX_AX_TOKEN = ${token};
|
|
115
|
+
const PMX_AX_NODE_ID = ${node};
|
|
116
|
+
window.PMX_AX = {
|
|
117
|
+
emit(type, payload) {
|
|
118
|
+
window.parent.postMessage({
|
|
119
|
+
source: 'pmx-canvas-ax',
|
|
120
|
+
token: PMX_AX_TOKEN,
|
|
121
|
+
nodeId: PMX_AX_NODE_ID,
|
|
122
|
+
interaction: { type: String(type), payload: payload && typeof payload === 'object' ? payload : {} },
|
|
123
|
+
}, '*');
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
</script>`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface HtmlSurfaceOptions {
|
|
130
|
+
theme: SurfaceTheme;
|
|
131
|
+
/** Client nonce that authorizes parent → iframe theme-update messages. */
|
|
132
|
+
themeToken?: string;
|
|
133
|
+
presentation?: boolean;
|
|
134
|
+
presentationExitToken?: string;
|
|
135
|
+
/** Inject window.PMX_AX.emit (only when the node's AX capabilities are enabled). */
|
|
136
|
+
axBridge?: boolean;
|
|
137
|
+
/** Nonce authorizing iframe → parent AX emits; embedded in the bridge. */
|
|
138
|
+
axToken?: string;
|
|
139
|
+
/** Node id stamped on emitted interactions. */
|
|
140
|
+
nodeId?: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Wrap author HTML into a complete, themed standalone document. Accepts either a
|
|
145
|
+
* full HTML document (injects into its `<head>`) or a fragment (wraps it).
|
|
146
|
+
*/
|
|
147
|
+
export function buildHtmlSurfaceDocument(userHtml: string, options: HtmlSurfaceOptions): string {
|
|
148
|
+
const themeToken = sanitizeToken(options.themeToken);
|
|
149
|
+
const link = `<link rel="stylesheet" href="${SURFACE_THEME_STYLESHEET}">`;
|
|
150
|
+
const themeBridge = buildThemeBridge(themeToken);
|
|
151
|
+
const presentationBridge = options.presentation
|
|
152
|
+
? buildPresentationEscapeBridge(sanitizeToken(options.presentationExitToken))
|
|
153
|
+
: '';
|
|
154
|
+
const axBridge = options.axBridge
|
|
155
|
+
? buildAxBridge(sanitizeToken(options.axToken), sanitizeToken(options.nodeId))
|
|
156
|
+
: '';
|
|
157
|
+
const injectedHeadContent = `${link}${themeBridge}${presentationBridge}${axBridge}`;
|
|
158
|
+
const presentationAttr = options.presentation ? ' data-pmx-presentation-mode="present"' : '';
|
|
159
|
+
const trimmed = userHtml.trim();
|
|
160
|
+
const isFullDoc = /<html[\s>]/i.test(trimmed);
|
|
161
|
+
if (isFullDoc) {
|
|
162
|
+
const withTheme = trimmed.replace(
|
|
163
|
+
/<html([^>]*)>/i,
|
|
164
|
+
`<html$1 data-pmx-canvas-theme="${options.theme}" data-theme="${options.theme}"${presentationAttr}>`,
|
|
165
|
+
);
|
|
166
|
+
return injectIntoHead(withTheme, injectedHeadContent);
|
|
167
|
+
}
|
|
168
|
+
// Fragment — wrap in a full document.
|
|
169
|
+
return `<!doctype html><html data-pmx-canvas-theme="${options.theme}" data-theme="${options.theme}"${presentationAttr}><head><meta charset="utf-8">${injectedHeadContent}</head><body>${userHtml}</body></html>`;
|
|
170
|
+
}
|
package/src/server/index.ts
CHANGED
|
@@ -2,14 +2,20 @@ import { EventEmitter } from 'node:events';
|
|
|
2
2
|
import { canvasState, IMAGE_MIME_MAP } from './canvas-state.js';
|
|
3
3
|
import type { CanvasAnnotation, CanvasNodeState, CanvasEdge, CanvasLayout, ViewportState } from './canvas-state.js';
|
|
4
4
|
import { buildCanvasAxContext } from './ax-context.js';
|
|
5
|
+
import { applyAxInteraction, type AxInteractionInput, type AxInteractionPublicResult } from './ax-interaction.js';
|
|
5
6
|
import type {
|
|
6
7
|
PmxAxApprovalGate,
|
|
8
|
+
PmxAxCommandDescriptor,
|
|
7
9
|
PmxAxContext,
|
|
10
|
+
PmxAxElicitation,
|
|
8
11
|
PmxAxEvent,
|
|
9
12
|
PmxAxEvidence,
|
|
10
13
|
PmxAxEvidenceKind,
|
|
11
14
|
PmxAxFocusState,
|
|
12
15
|
PmxAxHostCapability,
|
|
16
|
+
PmxAxMode,
|
|
17
|
+
PmxAxModeRequest,
|
|
18
|
+
PmxAxPolicy,
|
|
13
19
|
PmxAxReviewAnchorType,
|
|
14
20
|
PmxAxReviewAnnotation,
|
|
15
21
|
PmxAxReviewKind,
|
|
@@ -130,8 +136,14 @@ export class PmxCanvas extends EventEmitter {
|
|
|
130
136
|
async start(options?: {
|
|
131
137
|
open?: boolean;
|
|
132
138
|
automationWebView?: boolean | CanvasAutomationWebViewOptions;
|
|
139
|
+
/**
|
|
140
|
+
* Bind a nearby free port when the preferred one is taken instead of
|
|
141
|
+
* failing. Default false (an explicit SDK port is honored exactly); the
|
|
142
|
+
* MCP auto-start opts in so a daemon already on the port can't crash it.
|
|
143
|
+
*/
|
|
144
|
+
allowPortFallback?: boolean;
|
|
133
145
|
}): Promise<void> {
|
|
134
|
-
const base = startCanvasServer({ port: this._port, allowPortFallback: false });
|
|
146
|
+
const base = startCanvasServer({ port: this._port, allowPortFallback: options?.allowPortFallback ?? false });
|
|
135
147
|
if (!base) {
|
|
136
148
|
throw new Error(`Failed to start canvas server on port ${this._port}`);
|
|
137
149
|
}
|
|
@@ -517,6 +529,22 @@ export class PmxCanvas extends EventEmitter {
|
|
|
517
529
|
return ok;
|
|
518
530
|
}
|
|
519
531
|
|
|
532
|
+
/** Undelivered steering for a consumer (loop-safe; excludes consumer-originated). */
|
|
533
|
+
getPendingSteering(options?: { consumer?: string; limit?: number }): PmxAxSteeringMessage[] {
|
|
534
|
+
return canvasState.getPendingSteering(options ?? {});
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Submit a node-originated AX interaction (plan-004 Phase 1). Validates the
|
|
539
|
+
* envelope + node capabilities, maps the interaction onto the matching AX
|
|
540
|
+
* operation, and emits the outcome + state SSE events.
|
|
541
|
+
*/
|
|
542
|
+
submitAxInteraction(input: AxInteractionInput, options?: { source?: PmxAxSource }): AxInteractionPublicResult {
|
|
543
|
+
const { result, events } = applyAxInteraction(canvasState, input, options?.source ?? 'sdk');
|
|
544
|
+
for (const e of events) emitPrimaryWorkbenchEvent(e.event, e.payload);
|
|
545
|
+
return result;
|
|
546
|
+
}
|
|
547
|
+
|
|
520
548
|
getAxTimeline(query?: AxTimelineQuery): ReturnType<typeof canvasState.getAxTimeline> {
|
|
521
549
|
return canvasState.getAxTimeline(query);
|
|
522
550
|
}
|
|
@@ -622,6 +650,75 @@ export class PmxCanvas extends EventEmitter {
|
|
|
622
650
|
return host;
|
|
623
651
|
}
|
|
624
652
|
|
|
653
|
+
listElicitations(): PmxAxElicitation[] {
|
|
654
|
+
return canvasState.getElicitations();
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
requestElicitation(
|
|
658
|
+
input: { prompt: string; fields?: string[]; nodeIds?: string[] },
|
|
659
|
+
options?: { source?: PmxAxSource },
|
|
660
|
+
): PmxAxElicitation {
|
|
661
|
+
const elicitation = canvasState.requestElicitation(input, { source: options?.source ?? 'sdk' });
|
|
662
|
+
emitPrimaryWorkbenchEvent('ax-state-changed', { elicitation });
|
|
663
|
+
return elicitation;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
respondElicitation(
|
|
667
|
+
id: string,
|
|
668
|
+
response: Record<string, unknown>,
|
|
669
|
+
options?: { source?: PmxAxSource },
|
|
670
|
+
): PmxAxElicitation | null {
|
|
671
|
+
const elicitation = canvasState.respondElicitation(id, response, { source: options?.source ?? 'sdk' });
|
|
672
|
+
if (elicitation) emitPrimaryWorkbenchEvent('ax-state-changed', { elicitation });
|
|
673
|
+
return elicitation;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
listModeRequests(): PmxAxModeRequest[] {
|
|
677
|
+
return canvasState.getModeRequests();
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
requestMode(
|
|
681
|
+
input: { mode: PmxAxMode; reason?: string | null; nodeIds?: string[] },
|
|
682
|
+
options?: { source?: PmxAxSource },
|
|
683
|
+
): PmxAxModeRequest {
|
|
684
|
+
const modeRequest = canvasState.requestMode(input, { source: options?.source ?? 'sdk' });
|
|
685
|
+
emitPrimaryWorkbenchEvent('ax-state-changed', { modeRequest });
|
|
686
|
+
return modeRequest;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
resolveModeRequest(
|
|
690
|
+
id: string,
|
|
691
|
+
decision: 'approved' | 'rejected',
|
|
692
|
+
options?: { resolution?: string; source?: PmxAxSource },
|
|
693
|
+
): PmxAxModeRequest | null {
|
|
694
|
+
const modeRequest = canvasState.resolveModeRequest(id, decision, { ...(options ?? {}), source: options?.source ?? 'sdk' });
|
|
695
|
+
if (modeRequest) emitPrimaryWorkbenchEvent('ax-state-changed', { modeRequest });
|
|
696
|
+
return modeRequest;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
getCommandRegistry(): PmxAxCommandDescriptor[] {
|
|
700
|
+
return canvasState.getCommandRegistry();
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
invokeCommand(name: string, args?: Record<string, unknown> | null, options?: { source?: PmxAxSource }): PmxAxEvent | null {
|
|
704
|
+
const event = canvasState.invokeCommand(name, args ?? null, { source: options?.source ?? 'sdk' });
|
|
705
|
+
if (event) emitPrimaryWorkbenchEvent('ax-event-created', { event });
|
|
706
|
+
return event;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
getPolicy(): PmxAxPolicy {
|
|
710
|
+
return canvasState.getPolicy();
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
setPolicy(
|
|
714
|
+
patch: { tools?: Partial<PmxAxPolicy['tools']>; prompt?: Partial<PmxAxPolicy['prompt']> },
|
|
715
|
+
options?: { source?: PmxAxSource },
|
|
716
|
+
): PmxAxPolicy {
|
|
717
|
+
const policy = canvasState.setPolicy(patch, { source: options?.source ?? 'sdk' });
|
|
718
|
+
emitPrimaryWorkbenchEvent('ax-state-changed', { policy });
|
|
719
|
+
return policy;
|
|
720
|
+
}
|
|
721
|
+
|
|
625
722
|
fitView(options?: {
|
|
626
723
|
width?: number;
|
|
627
724
|
height?: number;
|
|
@@ -1106,13 +1203,20 @@ export { traceManager } from './trace-manager.js';
|
|
|
1106
1203
|
export type {
|
|
1107
1204
|
PmxAxApprovalGate,
|
|
1108
1205
|
PmxAxApprovalStatus,
|
|
1206
|
+
PmxAxCommandDescriptor,
|
|
1109
1207
|
PmxAxContext,
|
|
1110
1208
|
PmxAxEvent,
|
|
1209
|
+
PmxAxElicitation,
|
|
1210
|
+
PmxAxElicitationStatus,
|
|
1111
1211
|
PmxAxEventKind,
|
|
1112
1212
|
PmxAxEvidence,
|
|
1113
1213
|
PmxAxEvidenceKind,
|
|
1114
1214
|
PmxAxFocusState,
|
|
1115
1215
|
PmxAxHostCapability,
|
|
1216
|
+
PmxAxMode,
|
|
1217
|
+
PmxAxModeRequest,
|
|
1218
|
+
PmxAxModeRequestStatus,
|
|
1219
|
+
PmxAxPolicy,
|
|
1116
1220
|
PmxAxReviewAnchorType,
|
|
1117
1221
|
PmxAxReviewAnnotation,
|
|
1118
1222
|
PmxAxReviewKind,
|
|
@@ -35,6 +35,11 @@ export type MutationOp =
|
|
|
35
35
|
| 'resolveApproval'
|
|
36
36
|
| 'addReviewAnnotation'
|
|
37
37
|
| 'updateReviewAnnotation'
|
|
38
|
+
| 'requestElicitation'
|
|
39
|
+
| 'respondElicitation'
|
|
40
|
+
| 'requestMode'
|
|
41
|
+
| 'resolveModeRequest'
|
|
42
|
+
| 'setPolicy'
|
|
38
43
|
| 'batch'
|
|
39
44
|
| 'viewport'
|
|
40
45
|
| 'groupNodes'
|
package/src/server/placement.ts
CHANGED
|
@@ -7,7 +7,11 @@ import {
|
|
|
7
7
|
|
|
8
8
|
export { findOpenCanvasPosition, type CanvasPlacementRect } from '../shared/placement.js';
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
// Margin between a group frame and the children it contains. Kept generous so
|
|
11
|
+
// the auto-fit frame stays visibly larger than its children — leaving room for
|
|
12
|
+
// the node-count/type badge in the header that would otherwise sit under the
|
|
13
|
+
// top-left child.
|
|
14
|
+
export const GROUP_PAD = 56;
|
|
11
15
|
export const GROUP_TITLEBAR_HEIGHT = 32;
|
|
12
16
|
const GROUP_LAYOUT_GAP_X = 32;
|
|
13
17
|
const GROUP_LAYOUT_GAP_Y = 32;
|