pmx-canvas 0.1.26 → 0.1.27
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 +191 -0
- package/CHANGELOG.md +74 -0
- package/Readme.md +74 -27
- package/dist/canvas/index.js +82 -82
- package/dist/json-render/index.css +1 -1
- package/dist/json-render/index.js +944 -164
- package/dist/types/json-render/catalog.d.ts +195 -20
- package/dist/types/json-render/charts/components.d.ts +7 -0
- package/dist/types/json-render/charts/definitions.d.ts +13 -1
- package/dist/types/json-render/charts/tufte-components.d.ts +65 -0
- package/dist/types/json-render/charts/tufte-definitions.d.ts +164 -0
- package/dist/types/json-render/directives.d.ts +23 -0
- package/dist/types/json-render/renderer/index.d.ts +1 -0
- package/dist/types/json-render/server.d.ts +32 -1
- package/dist/types/mcp/canvas-access.d.ts +62 -0
- package/dist/types/server/ax-state.d.ts +170 -0
- package/dist/types/server/canvas-db.d.ts +17 -1
- package/dist/types/server/canvas-operations.d.ts +45 -0
- package/dist/types/server/canvas-schema.d.ts +5 -1
- package/dist/types/server/canvas-state.d.ts +95 -4
- package/dist/types/server/index.d.ts +114 -2
- package/dist/types/server/mutation-history.d.ts +1 -1
- package/docs/cli.md +42 -0
- package/docs/http-api.md +64 -0
- package/docs/mcp.md +23 -5
- package/docs/node-types.md +1 -1
- package/docs/screenshots/codex-app.png +0 -0
- package/docs/screenshots/github-copilot-app.png +0 -0
- package/docs/sdk.md +19 -1
- package/package.json +10 -7
- package/skills/control-session-orchestrator/SKILL.md +359 -0
- package/skills/control-session-orchestrator/evals/evals.json +75 -0
- package/skills/data-analysis/SKILL.md +6 -0
- package/skills/pmx-canvas/SKILL.md +50 -4
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +6 -0
- package/skills/tufte-viz/SKILL.md +157 -0
- package/skills/tufte-viz/references/analytical-design.md +217 -0
- package/skills/tufte-viz/references/tufte-principles.md +147 -0
- package/src/cli/agent.ts +280 -2
- package/src/cli/index.ts +2 -1
- package/src/client/nodes/ExtAppFrame.tsx +23 -1
- package/src/client/nodes/McpAppNode.tsx +6 -2
- package/src/json-render/catalog.ts +22 -1
- package/src/json-render/charts/components.tsx +97 -10
- package/src/json-render/charts/definitions.ts +19 -2
- package/src/json-render/charts/extra-components.tsx +5 -4
- package/src/json-render/charts/tufte-components.tsx +383 -0
- package/src/json-render/charts/tufte-definitions.ts +128 -0
- package/src/json-render/directives.ts +29 -0
- package/src/json-render/renderer/index.css +101 -0
- package/src/json-render/renderer/index.tsx +33 -0
- package/src/json-render/server.ts +257 -5
- package/src/mcp/canvas-access.ts +261 -0
- package/src/mcp/server.ts +496 -7
- package/src/server/ax-context.ts +8 -3
- package/src/server/ax-state.ts +447 -0
- package/src/server/canvas-db.ts +184 -1
- package/src/server/canvas-operations.ts +107 -0
- package/src/server/canvas-schema.ts +26 -3
- package/src/server/canvas-state.ts +349 -2
- package/src/server/index.ts +234 -2
- package/src/server/mutation-history.ts +6 -0
- package/src/server/server.ts +419 -2
package/src/server/index.ts
CHANGED
|
@@ -2,7 +2,27 @@ 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 type {
|
|
5
|
+
import type {
|
|
6
|
+
PmxAxApprovalGate,
|
|
7
|
+
PmxAxContext,
|
|
8
|
+
PmxAxEvent,
|
|
9
|
+
PmxAxEvidence,
|
|
10
|
+
PmxAxEvidenceKind,
|
|
11
|
+
PmxAxFocusState,
|
|
12
|
+
PmxAxHostCapability,
|
|
13
|
+
PmxAxReviewAnchorType,
|
|
14
|
+
PmxAxReviewAnnotation,
|
|
15
|
+
PmxAxReviewKind,
|
|
16
|
+
PmxAxReviewRegion,
|
|
17
|
+
PmxAxReviewSeverity,
|
|
18
|
+
PmxAxReviewStatus,
|
|
19
|
+
PmxAxSource,
|
|
20
|
+
PmxAxState,
|
|
21
|
+
PmxAxSteeringMessage,
|
|
22
|
+
PmxAxWorkItem,
|
|
23
|
+
PmxAxWorkItemStatus,
|
|
24
|
+
} from './ax-state.js';
|
|
25
|
+
import type { AxTimelineQuery } from './canvas-db.js';
|
|
6
26
|
import { findCanvasExtAppNodeId } from './ext-app-lookup.js';
|
|
7
27
|
import { onFileNodeChanged } from './file-watcher.js';
|
|
8
28
|
import { findOpenCanvasPosition, computeGroupBounds } from './placement.js';
|
|
@@ -12,6 +32,8 @@ import { recomputeCodeGraph, buildCodeGraphSummary, formatCodeGraph } from './co
|
|
|
12
32
|
import {
|
|
13
33
|
addCanvasNode,
|
|
14
34
|
addCanvasEdge,
|
|
35
|
+
appendCanvasJsonRenderStream,
|
|
36
|
+
createCanvasStreamingJsonRenderNode,
|
|
15
37
|
MARKDOWN_NODE_DEFAULT_SIZE,
|
|
16
38
|
MCP_APP_NODE_DEFAULT_SIZE,
|
|
17
39
|
applyCanvasNodeUpdates,
|
|
@@ -30,6 +52,7 @@ import {
|
|
|
30
52
|
refreshCanvasWebpageNode,
|
|
31
53
|
removeCanvasNode,
|
|
32
54
|
removeCanvasEdge,
|
|
55
|
+
resolveHtmlContent,
|
|
33
56
|
restoreCanvasSnapshot,
|
|
34
57
|
saveCanvasSnapshot,
|
|
35
58
|
scheduleCodeGraphRecompute,
|
|
@@ -453,6 +476,132 @@ export class PmxCanvas extends EventEmitter {
|
|
|
453
476
|
return focus;
|
|
454
477
|
}
|
|
455
478
|
|
|
479
|
+
recordAxEvent(
|
|
480
|
+
input: { kind: PmxAxEvent['kind']; summary: string; detail?: string | null; nodeIds?: string[]; data?: Record<string, unknown> | null },
|
|
481
|
+
options?: { source?: PmxAxSource },
|
|
482
|
+
): PmxAxEvent {
|
|
483
|
+
const event = canvasState.recordAxEvent(input, { source: options?.source ?? 'sdk' });
|
|
484
|
+
emitPrimaryWorkbenchEvent('ax-event-created', { event });
|
|
485
|
+
return event;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
sendSteering(message: string, options?: { source?: PmxAxSource }): PmxAxSteeringMessage {
|
|
489
|
+
const steering = canvasState.recordSteeringMessage(message, { source: options?.source ?? 'sdk' });
|
|
490
|
+
emitPrimaryWorkbenchEvent('ax-event-created', { steering });
|
|
491
|
+
return steering;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
markSteeringDelivered(id: string): boolean {
|
|
495
|
+
const ok = canvasState.markSteeringDelivered(id);
|
|
496
|
+
if (ok) emitPrimaryWorkbenchEvent('ax-event-created', { steeringDelivered: id });
|
|
497
|
+
return ok;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
getAxTimeline(query?: AxTimelineQuery): ReturnType<typeof canvasState.getAxTimeline> {
|
|
501
|
+
return canvasState.getAxTimeline(query);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
listWorkItems(): PmxAxWorkItem[] {
|
|
505
|
+
return canvasState.getWorkItems();
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
addWorkItem(
|
|
509
|
+
input: { title: string; status?: PmxAxWorkItemStatus; detail?: string | null; nodeIds?: string[] },
|
|
510
|
+
options?: { source?: PmxAxSource },
|
|
511
|
+
): PmxAxWorkItem {
|
|
512
|
+
const workItem = canvasState.addWorkItem(input, { source: options?.source ?? 'sdk' });
|
|
513
|
+
emitPrimaryWorkbenchEvent('ax-state-changed', { workItem });
|
|
514
|
+
return workItem;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
updateWorkItem(
|
|
518
|
+
id: string,
|
|
519
|
+
patch: { title?: string; status?: PmxAxWorkItemStatus; detail?: string | null; nodeIds?: string[] },
|
|
520
|
+
options?: { source?: PmxAxSource },
|
|
521
|
+
): PmxAxWorkItem | null {
|
|
522
|
+
const workItem = canvasState.updateWorkItem(id, patch, { source: options?.source ?? 'sdk' });
|
|
523
|
+
if (workItem) emitPrimaryWorkbenchEvent('ax-state-changed', { workItem });
|
|
524
|
+
return workItem;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
listApprovalGates(): PmxAxApprovalGate[] {
|
|
528
|
+
return canvasState.getApprovalGates();
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
requestApproval(
|
|
532
|
+
input: { title: string; detail?: string | null; action?: string | null; nodeIds?: string[] },
|
|
533
|
+
options?: { source?: PmxAxSource },
|
|
534
|
+
): PmxAxApprovalGate {
|
|
535
|
+
const approvalGate = canvasState.requestApproval(input, { source: options?.source ?? 'sdk' });
|
|
536
|
+
emitPrimaryWorkbenchEvent('ax-state-changed', { approvalGate });
|
|
537
|
+
return approvalGate;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
resolveApproval(
|
|
541
|
+
id: string,
|
|
542
|
+
decision: 'approved' | 'rejected',
|
|
543
|
+
options?: { resolution?: string; source?: PmxAxSource },
|
|
544
|
+
): PmxAxApprovalGate | null {
|
|
545
|
+
const approvalGate = canvasState.resolveApproval(id, decision, {
|
|
546
|
+
...(options?.resolution !== undefined ? { resolution: options.resolution } : {}),
|
|
547
|
+
source: options?.source ?? 'sdk',
|
|
548
|
+
});
|
|
549
|
+
if (approvalGate) emitPrimaryWorkbenchEvent('ax-state-changed', { approvalGate });
|
|
550
|
+
return approvalGate;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
addEvidence(
|
|
554
|
+
input: { kind: PmxAxEvidenceKind; title: string; body?: string | null; ref?: string | null; nodeIds?: string[]; data?: Record<string, unknown> | null },
|
|
555
|
+
options?: { source?: PmxAxSource },
|
|
556
|
+
): PmxAxEvidence {
|
|
557
|
+
const evidence = canvasState.addEvidence(input, { source: options?.source ?? 'sdk' });
|
|
558
|
+
emitPrimaryWorkbenchEvent('ax-event-created', { evidence });
|
|
559
|
+
return evidence;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
listReviewAnnotations(): PmxAxReviewAnnotation[] {
|
|
563
|
+
return canvasState.getReviewAnnotations();
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
addReviewAnnotation(
|
|
567
|
+
input: {
|
|
568
|
+
body: string;
|
|
569
|
+
kind?: PmxAxReviewKind;
|
|
570
|
+
severity?: PmxAxReviewSeverity;
|
|
571
|
+
anchorType?: PmxAxReviewAnchorType;
|
|
572
|
+
nodeId?: string | null;
|
|
573
|
+
file?: string | null;
|
|
574
|
+
region?: PmxAxReviewRegion | null;
|
|
575
|
+
author?: string | null;
|
|
576
|
+
},
|
|
577
|
+
options?: { source?: PmxAxSource },
|
|
578
|
+
): PmxAxReviewAnnotation | null {
|
|
579
|
+
const reviewAnnotation = canvasState.addReviewAnnotation(input, { source: options?.source ?? 'sdk' });
|
|
580
|
+
if (!reviewAnnotation) return null;
|
|
581
|
+
emitPrimaryWorkbenchEvent('ax-state-changed', { reviewAnnotation });
|
|
582
|
+
return reviewAnnotation;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
updateReviewAnnotation(
|
|
586
|
+
id: string,
|
|
587
|
+
patch: { body?: string; status?: PmxAxReviewStatus; severity?: PmxAxReviewSeverity; kind?: PmxAxReviewKind },
|
|
588
|
+
options?: { source?: PmxAxSource },
|
|
589
|
+
): PmxAxReviewAnnotation | null {
|
|
590
|
+
const reviewAnnotation = canvasState.updateReviewAnnotation(id, patch, { source: options?.source ?? 'sdk' });
|
|
591
|
+
if (reviewAnnotation) emitPrimaryWorkbenchEvent('ax-state-changed', { reviewAnnotation });
|
|
592
|
+
return reviewAnnotation;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
getHostCapability(): PmxAxHostCapability | null {
|
|
596
|
+
return canvasState.getHostCapability();
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
reportHostCapability(input: unknown, options?: { source?: PmxAxSource }): PmxAxHostCapability {
|
|
600
|
+
const host = canvasState.setHostCapability(input, { source: options?.source ?? 'sdk' });
|
|
601
|
+
emitPrimaryWorkbenchEvent('ax-state-changed', { host });
|
|
602
|
+
return host;
|
|
603
|
+
}
|
|
604
|
+
|
|
456
605
|
fitView(options?: {
|
|
457
606
|
width?: number;
|
|
458
607
|
height?: number;
|
|
@@ -687,6 +836,65 @@ export class PmxCanvas extends EventEmitter {
|
|
|
687
836
|
return result;
|
|
688
837
|
}
|
|
689
838
|
|
|
839
|
+
/**
|
|
840
|
+
* Progressively build a json-render node from SpecStream patches. Omit nodeId
|
|
841
|
+
* to create a new streaming node; pass the same nodeId on later calls to
|
|
842
|
+
* append more patches. The server accumulates the spec and the browser
|
|
843
|
+
* reloads the viewer as the specVersion bumps.
|
|
844
|
+
*/
|
|
845
|
+
streamJsonRenderNode(input: {
|
|
846
|
+
nodeId?: string;
|
|
847
|
+
title?: string;
|
|
848
|
+
patches?: unknown[];
|
|
849
|
+
done?: boolean;
|
|
850
|
+
x?: number;
|
|
851
|
+
y?: number;
|
|
852
|
+
width?: number;
|
|
853
|
+
height?: number;
|
|
854
|
+
strictSize?: boolean;
|
|
855
|
+
}): {
|
|
856
|
+
id: string;
|
|
857
|
+
url: string;
|
|
858
|
+
applied: number;
|
|
859
|
+
skipped: number;
|
|
860
|
+
specVersion: number;
|
|
861
|
+
elementCount: number;
|
|
862
|
+
streamStatus: 'open' | 'closed';
|
|
863
|
+
} {
|
|
864
|
+
let nodeId = input.nodeId;
|
|
865
|
+
let url = '';
|
|
866
|
+
if (!nodeId) {
|
|
867
|
+
const created = createCanvasStreamingJsonRenderNode({
|
|
868
|
+
...(input.title !== undefined ? { title: input.title } : {}),
|
|
869
|
+
...(input.x !== undefined ? { x: input.x } : {}),
|
|
870
|
+
...(input.y !== undefined ? { y: input.y } : {}),
|
|
871
|
+
...(input.width !== undefined ? { width: input.width } : {}),
|
|
872
|
+
...(input.height !== undefined ? { height: input.height } : {}),
|
|
873
|
+
...(input.strictSize ? { strictSize: true } : {}),
|
|
874
|
+
});
|
|
875
|
+
nodeId = created.id;
|
|
876
|
+
url = created.url;
|
|
877
|
+
} else {
|
|
878
|
+
url = String(canvasState.getNode(nodeId)?.data.url ?? '');
|
|
879
|
+
}
|
|
880
|
+
const result = appendCanvasJsonRenderStream(
|
|
881
|
+
nodeId,
|
|
882
|
+
Array.isArray(input.patches) ? input.patches : [],
|
|
883
|
+
input.done === true,
|
|
884
|
+
);
|
|
885
|
+
if (!result.ok) throw new Error(result.error);
|
|
886
|
+
emitPrimaryWorkbenchEvent('canvas-layout-update', { layout: canvasState.getLayout() });
|
|
887
|
+
return {
|
|
888
|
+
id: nodeId,
|
|
889
|
+
url,
|
|
890
|
+
applied: result.applied,
|
|
891
|
+
skipped: result.skipped,
|
|
892
|
+
specVersion: result.specVersion,
|
|
893
|
+
elementCount: result.elementCount,
|
|
894
|
+
streamStatus: result.streamStatus,
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
|
|
690
898
|
addHtmlNode(input: {
|
|
691
899
|
html: string;
|
|
692
900
|
title?: string;
|
|
@@ -707,7 +915,7 @@ export class PmxCanvas extends EventEmitter {
|
|
|
707
915
|
type: 'html',
|
|
708
916
|
...(typeof input.title === 'string' ? { title: input.title } : {}),
|
|
709
917
|
data: {
|
|
710
|
-
html: input.html,
|
|
918
|
+
html: resolveHtmlContent(input.html),
|
|
711
919
|
...(typeof input.summary === 'string' ? { summary: input.summary } : {}),
|
|
712
920
|
...(typeof input.agentSummary === 'string' ? { agentSummary: input.agentSummary } : {}),
|
|
713
921
|
...(typeof input.description === 'string' ? { description: input.description } : {}),
|
|
@@ -875,3 +1083,27 @@ export type {
|
|
|
875
1083
|
export type { GraphNodeInput, JsonRenderNodeInput, JsonRenderSpec } from '../json-render/server.js';
|
|
876
1084
|
export type { HtmlPrimitiveKind, HtmlPrimitiveDescriptor, HtmlPrimitiveInput, HtmlPrimitiveBuildResult } from './html-primitives.js';
|
|
877
1085
|
export { traceManager } from './trace-manager.js';
|
|
1086
|
+
export type {
|
|
1087
|
+
PmxAxApprovalGate,
|
|
1088
|
+
PmxAxApprovalStatus,
|
|
1089
|
+
PmxAxContext,
|
|
1090
|
+
PmxAxEvent,
|
|
1091
|
+
PmxAxEventKind,
|
|
1092
|
+
PmxAxEvidence,
|
|
1093
|
+
PmxAxEvidenceKind,
|
|
1094
|
+
PmxAxFocusState,
|
|
1095
|
+
PmxAxHostCapability,
|
|
1096
|
+
PmxAxReviewAnchorType,
|
|
1097
|
+
PmxAxReviewAnnotation,
|
|
1098
|
+
PmxAxReviewKind,
|
|
1099
|
+
PmxAxReviewRegion,
|
|
1100
|
+
PmxAxReviewSeverity,
|
|
1101
|
+
PmxAxReviewStatus,
|
|
1102
|
+
PmxAxSource,
|
|
1103
|
+
PmxAxState,
|
|
1104
|
+
PmxAxSteeringMessage,
|
|
1105
|
+
PmxAxTimelineSummary,
|
|
1106
|
+
PmxAxWorkItem,
|
|
1107
|
+
PmxAxWorkItemStatus,
|
|
1108
|
+
} from './ax-state.js';
|
|
1109
|
+
export type { AxTimelineQuery } from './canvas-db.js';
|
|
@@ -29,6 +29,12 @@ export type MutationOp =
|
|
|
29
29
|
| 'restoreSnapshot'
|
|
30
30
|
| 'setPins'
|
|
31
31
|
| 'setAxFocus'
|
|
32
|
+
| 'addWorkItem'
|
|
33
|
+
| 'updateWorkItem'
|
|
34
|
+
| 'requestApproval'
|
|
35
|
+
| 'resolveApproval'
|
|
36
|
+
| 'addReviewAnnotation'
|
|
37
|
+
| 'updateReviewAnnotation'
|
|
32
38
|
| 'batch'
|
|
33
39
|
| 'viewport'
|
|
34
40
|
| 'groupNodes'
|