pmx-canvas 0.4.6 → 0.4.7

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.
Files changed (70) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/Readme.md +56 -33
  3. package/dist/canvas/global.css +101 -3
  4. package/dist/canvas/index.js +87 -83
  5. package/dist/types/cli/shared.d.ts +13 -1
  6. package/dist/types/client/canvas/CanvasNode.d.ts +11 -0
  7. package/dist/types/client/canvas/EdgeLayer.d.ts +8 -0
  8. package/dist/types/client/nodes/AxStepControls.d.ts +4 -0
  9. package/dist/types/client/nodes/FileNode.d.ts +14 -0
  10. package/dist/types/client/state/canvas-store.d.ts +10 -0
  11. package/dist/types/client/state/intent-bridge.d.ts +9 -0
  12. package/dist/types/server/ax-flow-loop.d.ts +5 -0
  13. package/dist/types/server/ax-interaction.d.ts +21 -2
  14. package/dist/types/server/canvas-operations.d.ts +5 -0
  15. package/dist/types/server/canvas-state.d.ts +8 -0
  16. package/dist/types/server/canvas-validation.d.ts +15 -21
  17. package/dist/types/server/file-content.d.ts +46 -0
  18. package/dist/types/server/html-primitives.d.ts +13 -1
  19. package/dist/types/server/operations/ops/ax-work.d.ts +1 -0
  20. package/dist/types/server/server.d.ts +1 -0
  21. package/dist/types/server/web-artifacts.d.ts +14 -0
  22. package/dist/types/shared/ax-flow.d.ts +51 -0
  23. package/dist/types/shared/node-sizes.d.ts +33 -0
  24. package/docs/cli.md +19 -2
  25. package/docs/environment.md +1 -0
  26. package/docs/http-api.md +17 -0
  27. package/docs/mcp.md +12 -0
  28. package/docs/node-types.md +45 -1
  29. package/docs/screenshot.png +0 -0
  30. package/package.json +2 -2
  31. package/skills/pmx-canvas/SKILL.md +68 -7
  32. package/skills/pmx-canvas/evals/evals.json +54 -3
  33. package/skills/pmx-canvas/references/full-reference.md +11 -6
  34. package/src/cli/commands/history.ts +1 -1
  35. package/src/cli/commands/nodes.ts +2 -2
  36. package/src/cli/commands/query.ts +1 -1
  37. package/src/cli/commands/view.ts +1 -1
  38. package/src/cli/index.ts +6 -0
  39. package/src/cli/shared.ts +34 -5
  40. package/src/client/App.tsx +28 -15
  41. package/src/client/canvas/CanvasNode.tsx +54 -5
  42. package/src/client/canvas/EdgeLayer.tsx +44 -24
  43. package/src/client/canvas/ExpandedNodeOverlay.tsx +11 -0
  44. package/src/client/canvas/auto-fit.ts +10 -1
  45. package/src/client/canvas/use-pan-zoom.ts +16 -1
  46. package/src/client/nodes/AxStepControls.tsx +226 -0
  47. package/src/client/nodes/FileNode.tsx +249 -51
  48. package/src/client/state/canvas-store.ts +19 -0
  49. package/src/client/state/intent-bridge.ts +32 -0
  50. package/src/client/state/sse-bridge.ts +4 -1
  51. package/src/client/theme/global.css +101 -3
  52. package/src/server/ax-flow-loop.ts +114 -0
  53. package/src/server/ax-interaction.ts +299 -4
  54. package/src/server/canvas-operations.ts +44 -17
  55. package/src/server/canvas-state.ts +20 -10
  56. package/src/server/canvas-validation.ts +50 -41
  57. package/src/server/demo-state.json +4605 -523
  58. package/src/server/demo.ts +24 -0
  59. package/src/server/file-content.ts +152 -0
  60. package/src/server/file-watcher.ts +7 -10
  61. package/src/server/html-primitives.ts +636 -0
  62. package/src/server/operations/ops/ax-work.ts +5 -0
  63. package/src/server/operations/ops/nodes.ts +15 -1
  64. package/src/server/operations/ops/viewport.ts +7 -1
  65. package/src/server/server.ts +106 -5
  66. package/src/server/web-artifacts.ts +109 -0
  67. package/src/shared/ax-flow.ts +102 -0
  68. package/src/shared/node-sizes.ts +52 -0
  69. package/dist/types/server/chart-template.d.ts +0 -29
  70. package/src/server/chart-template.ts +0 -448
@@ -1,46 +1,11 @@
1
1
  import type { CanvasLayout, CanvasNodeState } from './canvas-state.js';
2
2
  import { getCanvasNodeTitle } from './canvas-serialization.js';
3
+ // The readability floor is SHARED with the client: the server clamps it at
4
+ // creation and `validate` reports violations, but the browser's auto-fit must
5
+ // honor the same floor or it silently undoes the clamp (0.4.6 Finding AA).
6
+ import { nodeMinSize } from '../shared/node-sizes.js';
3
7
 
4
- /**
5
- * Per-type minimum node size (0.4.5 report follow-up): agents frequently
6
- * create nodes with explicit frames far too small for their content — clipped
7
- * markdown, charts squeezed behind inner scrollbars. Explicit sizes below the
8
- * floor are clamped UP at creation (canvas-operations.ts creators);
9
- * `strictSize: true` is the escape hatch for a genuinely small fixed frame.
10
- * Floors sit below every type default and above the point of unreadability.
11
- * Creation-only: later updates are untouched (the browser's drag-resize
12
- * enforces its own 200×100 floor client-side), but `validate` reports any
13
- * node below its floor as an advisory sizeWarning. Absent types (trace,
14
- * group, prompt/response) are intentionally unclamped — trace is small by
15
- * design, groups size to their children.
16
- */
17
- export const NODE_MIN_CREATE_SIZES: Partial<Record<CanvasNodeState['type'], { width: number; height: number }>> = {
18
- markdown: { width: 360, height: 180 },
19
- context: { width: 360, height: 180 },
20
- file: { width: 360, height: 200 },
21
- diff: { width: 420, height: 240 },
22
- mermaid: { width: 360, height: 240 },
23
- status: { width: 280, height: 120 },
24
- ledger: { width: 320, height: 200 },
25
- image: { width: 240, height: 180 },
26
- html: { width: 420, height: 280 },
27
- webpage: { width: 420, height: 280 },
28
- 'json-render': { width: 420, height: 280 },
29
- graph: { width: 420, height: 280 },
30
- 'mcp-app': { width: 480, height: 320 },
31
- };
32
-
33
- export function clampCreateNodeSize(
34
- type: CanvasNodeState['type'],
35
- width: number,
36
- height: number,
37
- strictSize?: boolean,
38
- ): { width: number; height: number } {
39
- if (strictSize === true) return { width, height };
40
- const min = NODE_MIN_CREATE_SIZES[type];
41
- if (!min) return { width, height };
42
- return { width: Math.max(width, min.width), height: Math.max(height, min.height) };
43
- }
8
+ export { NODE_MIN_SIZES, clampCreateNodeSize, nodeMinSize } from '../shared/node-sizes.js';
44
9
 
45
10
  export interface CanvasValidationPair {
46
11
  aId: string;
@@ -66,12 +31,26 @@ export interface CanvasSizeWarning {
66
31
  minHeight: number;
67
32
  }
68
33
 
34
+ export interface CanvasHiddenEdgeEndpoint {
35
+ edgeId: string;
36
+ nodeId: string;
37
+ nodeTitle: string | null;
38
+ dockPosition: 'left' | 'right';
39
+ }
40
+
69
41
  export interface CanvasValidationResult {
70
42
  ok: boolean;
71
43
  collisions: CanvasValidationPair[];
72
44
  containments: CanvasContainmentIssue[];
73
45
  containmentViolations: CanvasContainmentIssue[];
74
46
  missingEdgeEndpoints: Array<{ edgeId: string; from: string; to: string }>;
47
+ /**
48
+ * Edges whose endpoint node is DOCKED — it renders in the HUD column, not on
49
+ * the canvas, so the edge visually terminates in empty space even though both
50
+ * endpoint IDs resolve (0.4.6 orb feedback #1). Advisory: reported for
51
+ * diagnosis, but does NOT fail `ok` (see the note at the return site).
52
+ */
53
+ hiddenEdgeEndpoints: CanvasHiddenEdgeEndpoint[];
75
54
  /** Nodes below their type's readable minimum (advisory — does not fail `ok`). */
76
55
  sizeWarnings: CanvasSizeWarning[];
77
56
  summary: {
@@ -81,6 +60,7 @@ export interface CanvasValidationResult {
81
60
  containments: number;
82
61
  containmentViolations: number;
83
62
  missingEdgeEndpoints: number;
63
+ hiddenEdgeEndpoints: number;
84
64
  sizeWarnings: number;
85
65
  };
86
66
  }
@@ -163,6 +143,28 @@ export function validateCanvasLayout(layout: CanvasLayout): CanvasValidationResu
163
143
  .filter((edge) => !nodeIds.has(edge.from) || !nodeIds.has(edge.to))
164
144
  .map((edge) => ({ edgeId: edge.id, from: edge.from, to: edge.to }));
165
145
 
146
+ // An edge to a DOCKED node cannot be drawn: docked nodes live in the HUD
147
+ // column, and the world-space edge layer skips them, so the edge trails off
148
+ // into empty canvas. Structural validation used to miss this entirely because
149
+ // both endpoint IDs resolve (0.4.6 orb feedback #1).
150
+ const dockedById = new Map(
151
+ layout.nodes.filter((node) => node.dockPosition != null).map((node) => [node.id, node] as const),
152
+ );
153
+ const hiddenEdgeEndpoints: CanvasHiddenEdgeEndpoint[] = layout.edges.flatMap((edge) =>
154
+ [edge.from, edge.to].flatMap((nodeId) => {
155
+ const docked = dockedById.get(nodeId);
156
+ if (!docked || docked.dockPosition == null) return [];
157
+ return [
158
+ {
159
+ edgeId: edge.id,
160
+ nodeId,
161
+ nodeTitle: getCanvasNodeTitle(docked),
162
+ dockPosition: docked.dockPosition,
163
+ },
164
+ ];
165
+ }),
166
+ );
167
+
166
168
  // Advisory: nodes below their type's readable floor (clipped/unreadable
167
169
  // content — 0.4.5 report follow-up). Creation clamps these, but resized or
168
170
  // strictSize-created nodes can still be undersized; surface them so an
@@ -171,7 +173,7 @@ export function validateCanvasLayout(layout: CanvasLayout): CanvasValidationResu
171
173
  const sizeWarnings: CanvasSizeWarning[] = layout.nodes
172
174
  .filter((node) => !node.collapsed && node.dockPosition == null && node.data?.strictSize !== true)
173
175
  .flatMap((node) => {
174
- const min = NODE_MIN_CREATE_SIZES[node.type];
176
+ const min = nodeMinSize(node.type);
175
177
  if (!min || (node.size.width >= min.width && node.size.height >= min.height)) return [];
176
178
  return [
177
179
  {
@@ -187,11 +189,17 @@ export function validateCanvasLayout(layout: CanvasLayout): CanvasValidationResu
187
189
  });
188
190
 
189
191
  return {
192
+ // `hiddenEdgeEndpoints` is ADVISORY (like sizeWarnings): a docked endpoint
193
+ // is a rendering observation, not a structural defect, and folding it into
194
+ // `ok` would flip previously-healthy boards to ok:false with no user change
195
+ // — `ok` is a documented response field of a frozen surface, so that is a
196
+ // contract break, not a patch. It is still reported for diagnosis.
190
197
  ok: collisions.length === 0 && containmentViolations.length === 0 && missingEdgeEndpoints.length === 0,
191
198
  collisions,
192
199
  containments,
193
200
  containmentViolations,
194
201
  missingEdgeEndpoints,
202
+ hiddenEdgeEndpoints,
195
203
  sizeWarnings,
196
204
  summary: {
197
205
  nodes: layout.nodes.length,
@@ -200,6 +208,7 @@ export function validateCanvasLayout(layout: CanvasLayout): CanvasValidationResu
200
208
  containments: containments.length,
201
209
  containmentViolations: containmentViolations.length,
202
210
  missingEdgeEndpoints: missingEdgeEndpoints.length,
211
+ hiddenEdgeEndpoints: hiddenEdgeEndpoints.length,
203
212
  sizeWarnings: sizeWarnings.length,
204
213
  },
205
214
  };