@xpressai/xircuits-viewer 0.1.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.
Files changed (69) hide show
  1. package/dist/index.d.ts +10 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +36 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/interaction/panZoom.d.ts +16 -0
  6. package/dist/interaction/panZoom.d.ts.map +1 -0
  7. package/dist/interaction/panZoom.js +84 -0
  8. package/dist/interaction/panZoom.js.map +1 -0
  9. package/dist/layout/metrics.d.ts +18 -0
  10. package/dist/layout/metrics.d.ts.map +1 -0
  11. package/dist/layout/metrics.js +78 -0
  12. package/dist/layout/metrics.js.map +1 -0
  13. package/dist/layout/viewBox.d.ts +9 -0
  14. package/dist/layout/viewBox.d.ts.map +1 -0
  15. package/dist/layout/viewBox.js +25 -0
  16. package/dist/layout/viewBox.js.map +1 -0
  17. package/dist/parser/classify.d.ts +10 -0
  18. package/dist/parser/classify.d.ts.map +1 -0
  19. package/dist/parser/classify.js +52 -0
  20. package/dist/parser/classify.js.map +1 -0
  21. package/dist/parser/parse.d.ts +3 -0
  22. package/dist/parser/parse.d.ts.map +1 -0
  23. package/dist/parser/parse.js +92 -0
  24. package/dist/parser/parse.js.map +1 -0
  25. package/dist/parser/validate.d.ts +5 -0
  26. package/dist/parser/validate.d.ts.map +1 -0
  27. package/dist/parser/validate.js +31 -0
  28. package/dist/parser/validate.js.map +1 -0
  29. package/dist/render/colors.d.ts +14 -0
  30. package/dist/render/colors.d.ts.map +1 -0
  31. package/dist/render/colors.js +78 -0
  32. package/dist/render/colors.js.map +1 -0
  33. package/dist/render/renderDefs.d.ts +4 -0
  34. package/dist/render/renderDefs.d.ts.map +1 -0
  35. package/dist/render/renderDefs.js +128 -0
  36. package/dist/render/renderDefs.js.map +1 -0
  37. package/dist/render/renderEdge.d.ts +3 -0
  38. package/dist/render/renderEdge.d.ts.map +1 -0
  39. package/dist/render/renderEdge.js +13 -0
  40. package/dist/render/renderEdge.js.map +1 -0
  41. package/dist/render/renderGraph.d.ts +5 -0
  42. package/dist/render/renderGraph.d.ts.map +1 -0
  43. package/dist/render/renderGraph.js +40 -0
  44. package/dist/render/renderGraph.js.map +1 -0
  45. package/dist/render/renderNode.d.ts +3 -0
  46. package/dist/render/renderNode.d.ts.map +1 -0
  47. package/dist/render/renderNode.js +81 -0
  48. package/dist/render/renderNode.js.map +1 -0
  49. package/dist/render/renderPort.d.ts +3 -0
  50. package/dist/render/renderPort.d.ts.map +1 -0
  51. package/dist/render/renderPort.js +57 -0
  52. package/dist/render/renderPort.js.map +1 -0
  53. package/dist/render/symbols.d.ts +6 -0
  54. package/dist/render/symbols.d.ts.map +1 -0
  55. package/dist/render/symbols.js +45 -0
  56. package/dist/render/symbols.js.map +1 -0
  57. package/dist/svg/bezier.d.ts +7 -0
  58. package/dist/svg/bezier.d.ts.map +1 -0
  59. package/dist/svg/bezier.js +9 -0
  60. package/dist/svg/bezier.js.map +1 -0
  61. package/dist/svg/builder.d.ts +11 -0
  62. package/dist/svg/builder.d.ts.map +1 -0
  63. package/dist/svg/builder.js +48 -0
  64. package/dist/svg/builder.js.map +1 -0
  65. package/dist/types.d.ts +69 -0
  66. package/dist/types.d.ts.map +1 -0
  67. package/dist/types.js +2 -0
  68. package/dist/types.js.map +1 -0
  69. package/package.json +25 -0
@@ -0,0 +1,10 @@
1
+ export type { XGraph, XNode, XEdge, XPort, NodeMetrics, RenderOptions, PortDirection, PortKind, NodeKind, EdgeKind, } from './types.js';
2
+ export { parse } from './parser/parse.js';
3
+ export { validate } from './parser/validate.js';
4
+ export { computeNodeMetrics } from './layout/metrics.js';
5
+ export { computeViewBox } from './layout/viewBox.js';
6
+ export { renderToString, renderToElement } from './render/renderGraph.js';
7
+ export { renderXircuits } from './render/renderGraph.js';
8
+ export declare function getCanvasStyle(theme?: 'dark' | 'light'): string;
9
+ export declare function renderFromUrl(url: string, options?: import('./types.js').RenderOptions): Promise<string>;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,MAAM,EACN,KAAK,EACL,KAAK,EACL,KAAK,EACL,WAAW,EACX,aAAa,EACb,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,QAAQ,GACT,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGhD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG1E,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGzD,wBAAgB,cAAc,CAAC,KAAK,GAAE,MAAM,GAAG,OAAgB,GAAG,MAAM,CAavE;AAGD,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,OAAO,YAAY,EAAE,aAAa,GAC3C,OAAO,CAAC,MAAM,CAAC,CAOjB"}
package/dist/index.js ADDED
@@ -0,0 +1,36 @@
1
+ // Parser
2
+ export { parse } from './parser/parse.js';
3
+ export { validate } from './parser/validate.js';
4
+ // Layout
5
+ export { computeNodeMetrics } from './layout/metrics.js';
6
+ export { computeViewBox } from './layout/viewBox.js';
7
+ // Renderer
8
+ export { renderToString, renderToElement } from './render/renderGraph.js';
9
+ // Convenience: one-shot parse + render
10
+ export { renderXircuits } from './render/renderGraph.js';
11
+ // Container background CSS (matching Xircuits canvas)
12
+ export function getCanvasStyle(theme = 'dark') {
13
+ if (theme === 'dark') {
14
+ return [
15
+ 'background-color: oklch(0.3 0.01 300)',
16
+ 'background-image: radial-gradient(oklch(40% 0 0) 1px, transparent 0)',
17
+ 'background-size: 15px 15px',
18
+ ].join(';');
19
+ }
20
+ return [
21
+ 'background-color: #f5f5f5',
22
+ 'background-image: radial-gradient(oklch(85% 0 0) 1px, transparent 0)',
23
+ 'background-size: 15px 15px',
24
+ ].join(';');
25
+ }
26
+ // Browser helper: fetch a .xircuits URL and render
27
+ export async function renderFromUrl(url, options) {
28
+ const res = await fetch(url);
29
+ if (!res.ok)
30
+ throw new Error(`Failed to load ${url}: ${res.status}`);
31
+ const json = await res.json();
32
+ const { parse } = await import('./parser/parse.js');
33
+ const { renderToString } = await import('./render/renderGraph.js');
34
+ return renderToString(parse(json), options);
35
+ }
36
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,SAAS;AACT,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,SAAS;AACT,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,WAAW;AACX,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE1E,uCAAuC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,sDAAsD;AACtD,MAAM,UAAU,cAAc,CAAC,QAA0B,MAAM;IAC7D,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QACrB,OAAO;YACL,uCAAuC;YACvC,sEAAsE;YACtE,4BAA4B;SAC7B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACd,CAAC;IACD,OAAO;QACL,2BAA2B;QAC3B,sEAAsE;QACtE,4BAA4B;KAC7B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED,mDAAmD;AACnD,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,GAAW,EACX,OAA4C;IAE5C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,GAAG,KAAK,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACrE,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACpD,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;IACnE,OAAO,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,16 @@
1
+ export interface PanZoomOptions {
2
+ minZoom?: number;
3
+ maxZoom?: number;
4
+ onViewportChange?: (viewport: {
5
+ x: number;
6
+ y: number;
7
+ zoom: number;
8
+ }) => void;
9
+ }
10
+ export interface PanZoomInstance {
11
+ destroy: () => void;
12
+ zoomBy: (factor: number) => void;
13
+ fitView: () => void;
14
+ }
15
+ export declare function attachPanZoom(svg: SVGSVGElement, options?: PanZoomOptions): PanZoomInstance;
16
+ //# sourceMappingURL=panZoom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panZoom.d.ts","sourceRoot":"","sources":["../../src/interaction/panZoom.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC/E;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,aAAa,CAC3B,GAAG,EAAE,aAAa,EAClB,OAAO,GAAE,cAAmB,GAC3B,eAAe,CA0FjB"}
@@ -0,0 +1,84 @@
1
+ export function attachPanZoom(svg, options = {}) {
2
+ const { minZoom = 0.1, maxZoom = 4, onViewportChange } = options;
3
+ const initialViewBox = readViewBox();
4
+ let isPanning = false;
5
+ let startX = 0;
6
+ let startY = 0;
7
+ let panStartVB = { x: 0, y: 0, w: 0, h: 0 };
8
+ function readViewBox() {
9
+ const vb = svg.getAttribute('viewBox')?.split(/\s+/).map(Number) || [0, 0, 800, 600];
10
+ return { x: vb[0], y: vb[1], w: vb[2], h: vb[3] };
11
+ }
12
+ function writeViewBox(vb) {
13
+ svg.setAttribute('viewBox', `${vb.x} ${vb.y} ${vb.w} ${vb.h}`);
14
+ onViewportChange?.({ x: vb.x, y: vb.y, zoom: svg.clientWidth / vb.w });
15
+ }
16
+ function onWheel(e) {
17
+ e.preventDefault();
18
+ const rect = svg.getBoundingClientRect();
19
+ const mx = (e.clientX - rect.left) / rect.width;
20
+ const my = (e.clientY - rect.top) / rect.height;
21
+ const factor = e.deltaY > 0 ? 1.1 : 0.9;
22
+ applyZoom(factor, mx, my);
23
+ }
24
+ function applyZoom(factor, anchorX = 0.5, anchorY = 0.5) {
25
+ const vb = readViewBox();
26
+ const newW = vb.w * factor;
27
+ const newH = vb.h * factor;
28
+ const currentZoom = svg.clientWidth / vb.w;
29
+ const newZoom = svg.clientWidth / newW;
30
+ if (newZoom < minZoom || newZoom > maxZoom)
31
+ return;
32
+ vb.x += (vb.w - newW) * anchorX;
33
+ vb.y += (vb.h - newH) * anchorY;
34
+ vb.w = newW;
35
+ vb.h = newH;
36
+ writeViewBox(vb);
37
+ }
38
+ function onPointerDown(e) {
39
+ if (e.button !== 0)
40
+ return;
41
+ isPanning = true;
42
+ startX = e.clientX;
43
+ startY = e.clientY;
44
+ panStartVB = readViewBox();
45
+ svg.setPointerCapture(e.pointerId);
46
+ svg.style.cursor = 'grabbing';
47
+ }
48
+ function onPointerMove(e) {
49
+ if (!isPanning)
50
+ return;
51
+ const rect = svg.getBoundingClientRect();
52
+ const vb = panStartVB;
53
+ const dx = (e.clientX - startX) / rect.width * vb.w;
54
+ const dy = (e.clientY - startY) / rect.height * vb.h;
55
+ writeViewBox({ x: vb.x - dx, y: vb.y - dy, w: vb.w, h: vb.h });
56
+ }
57
+ function onPointerUp(e) {
58
+ if (!isPanning)
59
+ return;
60
+ isPanning = false;
61
+ svg.releasePointerCapture(e.pointerId);
62
+ svg.style.cursor = 'grab';
63
+ }
64
+ svg.style.cursor = 'grab';
65
+ svg.addEventListener('wheel', onWheel, { passive: false });
66
+ svg.addEventListener('pointerdown', onPointerDown);
67
+ svg.addEventListener('pointermove', onPointerMove);
68
+ svg.addEventListener('pointerup', onPointerUp);
69
+ return {
70
+ destroy() {
71
+ svg.removeEventListener('wheel', onWheel);
72
+ svg.removeEventListener('pointerdown', onPointerDown);
73
+ svg.removeEventListener('pointermove', onPointerMove);
74
+ svg.removeEventListener('pointerup', onPointerUp);
75
+ },
76
+ zoomBy(factor) {
77
+ applyZoom(factor);
78
+ },
79
+ fitView() {
80
+ writeViewBox(initialViewBox);
81
+ },
82
+ };
83
+ }
84
+ //# sourceMappingURL=panZoom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panZoom.js","sourceRoot":"","sources":["../../src/interaction/panZoom.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { XNode, NodeMetrics } from '../types.js';
2
+ export declare const TITLE_HEIGHT = 26;
3
+ export declare const PORT_ROW_HEIGHT = 19;
4
+ export declare const NODE_MIN_WIDTH = 100;
5
+ export declare const PORT_GAP = 10;
6
+ export declare const PORT_DOT_SIZE = 15;
7
+ export declare const FONT_SIZE = 11;
8
+ export declare const CHAR_WIDTH = 6.6;
9
+ export declare const TITLE_ICON_SIZE = 15;
10
+ export declare const TITLE_PADDING_X = 5;
11
+ export declare const TITLE_PADDING_Y = 5;
12
+ export declare const PORT_LABEL_PADDING = 5;
13
+ export declare const COMMENT_FONT_SIZE = 12;
14
+ export declare const COMMENT_CHAR_WIDTH = 7.2;
15
+ export declare const COMMENT_PADDING = 5;
16
+ export declare function measureTextWidth(text: string, charWidth?: number): number;
17
+ export declare function computeNodeMetrics(node: XNode): NodeMetrics;
18
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/layout/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAGtD,eAAO,MAAM,YAAY,KAAK,CAAC;AAC/B,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,cAAc,MAAM,CAAC;AAClC,eAAO,MAAM,QAAQ,KAAK,CAAC;AAC3B,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,SAAS,KAAK,CAAC;AAC5B,eAAO,MAAM,UAAU,MAAM,CAAC;AAC9B,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,MAAmB,GAAG,MAAM,CAErF;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,KAAK,GAAG,WAAW,CAkD3D"}
@@ -0,0 +1,78 @@
1
+ // Constants derived from Xircuits CSS
2
+ export const TITLE_HEIGHT = 26;
3
+ export const PORT_ROW_HEIGHT = 19; // 15px port + 2px margin top + 2px margin bottom
4
+ export const NODE_MIN_WIDTH = 100;
5
+ export const PORT_GAP = 10; // gap between in-port column and out-port column (margin-right: 10px)
6
+ export const PORT_DOT_SIZE = 15;
7
+ export const FONT_SIZE = 11;
8
+ export const CHAR_WIDTH = 6.6; // approx for sans-serif 11px
9
+ export const TITLE_ICON_SIZE = 15;
10
+ export const TITLE_PADDING_X = 5;
11
+ export const TITLE_PADDING_Y = 5;
12
+ export const PORT_LABEL_PADDING = 5; // padding: 0 5px on label
13
+ export const COMMENT_FONT_SIZE = 12;
14
+ export const COMMENT_CHAR_WIDTH = 7.2;
15
+ export const COMMENT_PADDING = 5;
16
+ export function measureTextWidth(text, charWidth = CHAR_WIDTH) {
17
+ return text.length * charWidth;
18
+ }
19
+ export function computeNodeMetrics(node) {
20
+ if (node.kind === 'comment') {
21
+ return computeCommentMetrics(node);
22
+ }
23
+ // Title width = icon + padding + name text + padding
24
+ const titleTextWidth = TITLE_PADDING_X + TITLE_ICON_SIZE + TITLE_PADDING_X + measureTextWidth(node.name) + TITLE_PADDING_X;
25
+ // In-ports column width: dot(15) + label padding(5) + label text + label padding(5)
26
+ let maxInWidth = 0;
27
+ for (const port of node.portsIn) {
28
+ const labelW = measureTextWidth(port.label) + PORT_LABEL_PADDING * 2;
29
+ maxInWidth = Math.max(maxInWidth, PORT_DOT_SIZE + labelW);
30
+ }
31
+ // Out-ports column width: label padding(5) + label text + label padding(5) + dot(15)
32
+ let maxOutWidth = 0;
33
+ for (const port of node.portsOut) {
34
+ const labelW = measureTextWidth(port.label) + PORT_LABEL_PADDING * 2;
35
+ maxOutWidth = Math.max(maxOutWidth, labelW + PORT_DOT_SIZE);
36
+ }
37
+ const hasIn = node.portsIn.length > 0;
38
+ const hasOut = node.portsOut.length > 0;
39
+ const portsWidth = maxInWidth + (hasIn && hasOut ? PORT_GAP : 0) + maxOutWidth;
40
+ const width = Math.max(NODE_MIN_WIDTH, titleTextWidth, portsWidth);
41
+ const maxPortCount = Math.max(node.portsIn.length, node.portsOut.length);
42
+ const bodyHeight = maxPortCount > 0 ? maxPortCount * PORT_ROW_HEIGHT : 0;
43
+ const height = TITLE_HEIGHT + bodyHeight;
44
+ // Compute port positions (relative to node top-left, at the connection point)
45
+ const portPositions = new Map();
46
+ node.portsIn.forEach((port, i) => {
47
+ portPositions.set(port.id, {
48
+ x: 0, // left edge of node
49
+ y: TITLE_HEIGHT + i * PORT_ROW_HEIGHT + PORT_ROW_HEIGHT / 2,
50
+ });
51
+ });
52
+ node.portsOut.forEach((port, i) => {
53
+ portPositions.set(port.id, {
54
+ x: width, // right edge of node
55
+ y: TITLE_HEIGHT + i * PORT_ROW_HEIGHT + PORT_ROW_HEIGHT / 2,
56
+ });
57
+ });
58
+ return { width, height, titleHeight: TITLE_HEIGHT, portRowHeight: PORT_ROW_HEIGHT, portPositions };
59
+ }
60
+ function computeCommentMetrics(node) {
61
+ const title = 'Comment:';
62
+ const text = node.extras.commentInput || '';
63
+ const lines = text.split('\n');
64
+ const titleWidth = measureTextWidth(title, COMMENT_CHAR_WIDTH) + COMMENT_PADDING * 2;
65
+ const maxLineWidth = Math.max(...lines.map(l => measureTextWidth(l, COMMENT_CHAR_WIDTH)));
66
+ const width = Math.max(NODE_MIN_WIDTH, titleWidth, maxLineWidth + COMMENT_PADDING * 2);
67
+ // title line + content lines + padding
68
+ const lineHeight = 18;
69
+ const height = COMMENT_PADDING + 16 + lines.length * lineHeight + COMMENT_PADDING;
70
+ return {
71
+ width,
72
+ height,
73
+ titleHeight: 0,
74
+ portRowHeight: 0,
75
+ portPositions: new Map(),
76
+ };
77
+ }
78
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/layout/metrics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { XNode, NodeMetrics } from '../types.js';
2
+ export interface ViewBox {
3
+ x: number;
4
+ y: number;
5
+ width: number;
6
+ height: number;
7
+ }
8
+ export declare function computeViewBox(nodes: XNode[], metricsMap: Map<string, NodeMetrics>, padding?: number): ViewBox;
9
+ //# sourceMappingURL=viewBox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewBox.d.ts","sourceRoot":"","sources":["../../src/layout/viewBox.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,WAAW,OAAO;IACtB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,KAAK,EAAE,EACd,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,EACpC,OAAO,GAAE,MAAW,GACnB,OAAO,CA0BT"}
@@ -0,0 +1,25 @@
1
+ export function computeViewBox(nodes, metricsMap, padding = 40) {
2
+ if (nodes.length === 0) {
3
+ return { x: 0, y: 0, width: 800, height: 600 };
4
+ }
5
+ let minX = Infinity;
6
+ let minY = Infinity;
7
+ let maxX = -Infinity;
8
+ let maxY = -Infinity;
9
+ for (const node of nodes) {
10
+ const metrics = metricsMap.get(node.id);
11
+ if (!metrics)
12
+ continue;
13
+ minX = Math.min(minX, node.x);
14
+ minY = Math.min(minY, node.y);
15
+ maxX = Math.max(maxX, node.x + metrics.width);
16
+ maxY = Math.max(maxY, node.y + metrics.height);
17
+ }
18
+ return {
19
+ x: minX - padding,
20
+ y: minY - padding,
21
+ width: maxX - minX + padding * 2,
22
+ height: maxY - minY + padding * 2,
23
+ };
24
+ }
25
+ //# sourceMappingURL=viewBox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewBox.js","sourceRoot":"","sources":["../../src/layout/viewBox.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,cAAc,CAC5B,KAAc,EACd,UAAoC,EACpC,UAAkB,EAAE;IAEpB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IACjD,CAAC;IAED,IAAI,IAAI,GAAG,QAAQ,CAAC;IACpB,IAAI,IAAI,GAAG,QAAQ,CAAC;IACpB,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC;IACrB,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC;IAErB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO;YAAE,SAAS;QAEvB,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9C,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC;IAED,OAAO;QACL,CAAC,EAAE,IAAI,GAAG,OAAO;QACjB,CAAC,EAAE,IAAI,GAAG,OAAO;QACjB,KAAK,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,CAAC;QAChC,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,CAAC;KAClC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { NodeKind, PortKind, PortDirection, EdgeKind } from '../types.js';
2
+ export declare function classifyNode(name: string, extras: Record<string, unknown>): NodeKind;
3
+ export declare function classifyPort(portName: string, label: string, isIn: boolean, nodeName: string): {
4
+ kind: PortKind;
5
+ direction: PortDirection;
6
+ dataType: string | null;
7
+ varName: string;
8
+ };
9
+ export declare function classifyEdge(type: string): EdgeKind;
10
+ //# sourceMappingURL=classify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../../src/parser/classify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE/E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ,CAiBpF;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,MAAM,GACf;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CA2BxF;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAEnD"}
@@ -0,0 +1,52 @@
1
+ export function classifyNode(name, extras) {
2
+ const type = extras.type;
3
+ if (type === 'comment')
4
+ return 'comment';
5
+ if (name.startsWith('Literal') || name.startsWith('Argument'))
6
+ return 'literal';
7
+ if (type === 'xircuits_workflow')
8
+ return 'workflow';
9
+ if (name === 'Start')
10
+ return 'start';
11
+ if (name === 'Finish')
12
+ return 'finish';
13
+ // Sub-kinds from extras.type
14
+ if (type === 'branch')
15
+ return 'branch';
16
+ if (type === 'function')
17
+ return 'function';
18
+ if (type === 'context_set')
19
+ return 'context_set';
20
+ if (type === 'context_get')
21
+ return 'context_get';
22
+ if (type === 'variable')
23
+ return 'variable';
24
+ return 'component';
25
+ }
26
+ export function classifyPort(portName, label, isIn, nodeName) {
27
+ const direction = isIn ? 'in' : 'out';
28
+ // Flow ports: name starts with "in-" or "out-" (flow connectors with ▶ label)
29
+ const isFlowPort = label === '▶' && !nodeName.startsWith('Argument');
30
+ if (isFlowPort || portName.startsWith('out-') && label === '▶' || portName.startsWith('in-') && label === '▶') {
31
+ return { kind: 'flow', direction, dataType: null, varName: label };
32
+ }
33
+ // Parameter ports: "parameter-TYPE-NAME" or "parameter-out-TYPE-NAME"
34
+ let dataType = null;
35
+ let varName = label;
36
+ if (portName.startsWith('parameter-')) {
37
+ const parts = portName.split('-');
38
+ if (parts[1] === 'out') {
39
+ // parameter-out-TYPE-NAME
40
+ dataType = parts[2] || null;
41
+ }
42
+ else {
43
+ // parameter-TYPE-NAME
44
+ dataType = parts[1] || null;
45
+ }
46
+ }
47
+ return { kind: 'parameter', direction, dataType, varName };
48
+ }
49
+ export function classifyEdge(type) {
50
+ return type === 'triangle-link' ? 'flow' : 'data';
51
+ }
52
+ //# sourceMappingURL=classify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify.js","sourceRoot":"","sources":["../../src/parser/classify.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,MAA+B;IACxE,MAAM,IAAI,GAAG,MAAM,CAAC,IAA0B,CAAC;IAE/C,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzC,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,SAAS,CAAC;IAChF,IAAI,IAAI,KAAK,mBAAmB;QAAE,OAAO,UAAU,CAAC;IACpD,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IACrC,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAEvC,6BAA6B;IAC7B,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IACvC,IAAI,IAAI,KAAK,UAAU;QAAE,OAAO,UAAU,CAAC;IAC3C,IAAI,IAAI,KAAK,aAAa;QAAE,OAAO,aAAa,CAAC;IACjD,IAAI,IAAI,KAAK,aAAa;QAAE,OAAO,aAAa,CAAC;IACjD,IAAI,IAAI,KAAK,UAAU;QAAE,OAAO,UAAU,CAAC;IAE3C,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,QAAgB,EAChB,KAAa,EACb,IAAa,EACb,QAAgB;IAEhB,MAAM,SAAS,GAAkB,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IAErD,8EAA8E;IAC9E,MAAM,UAAU,GACd,KAAK,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IAEpD,IAAI,UAAU,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,KAAK,KAAK,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;QAC9G,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACrE,CAAC;IAED,sEAAsE;IACtE,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,IAAI,QAAQ,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;YACvB,0BAA0B;YAC1B,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;QAC9B,CAAC;aAAM,CAAC;YACN,sBAAsB;YACtB,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;AACpD,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { XGraph } from '../types.js';
2
+ export declare function parse(json: unknown): XGraph;
3
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/parser/parse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAuB,MAAM,aAAa,CAAC;AAmD/D,wBAAgB,KAAK,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CA4F3C"}
@@ -0,0 +1,92 @@
1
+ import { classifyNode, classifyPort, classifyEdge } from './classify.js';
2
+ import { validate } from './validate.js';
3
+ export function parse(json) {
4
+ const { valid, errors } = validate(json);
5
+ if (!valid) {
6
+ throw new Error(`Invalid .xircuits file: ${errors.join(', ')}`);
7
+ }
8
+ const obj = json;
9
+ const layers = obj.layers;
10
+ const linkLayer = layers.find(l => l.type === 'diagram-links');
11
+ const nodeLayer = layers.find(l => l.type === 'diagram-nodes');
12
+ const rawNodes = Object.values(nodeLayer.models);
13
+ const rawLinks = linkLayer
14
+ ? Object.values(linkLayer.models)
15
+ : [];
16
+ // Build a port ID set for quick lookup
17
+ const portMap = new Map();
18
+ for (const rawNode of rawNodes) {
19
+ for (const port of rawNode.ports) {
20
+ portMap.set(port.id, port);
21
+ }
22
+ }
23
+ const nodes = rawNodes.map(raw => {
24
+ const kind = classifyNode(raw.name, raw.extras);
25
+ // Order ports according to portsInOrder/portsOutOrder
26
+ const portsInOrdered = orderPorts(raw.ports, raw.portsInOrder, true);
27
+ const portsOutOrdered = orderPorts(raw.ports, raw.portsOutOrder, false);
28
+ const portsIn = portsInOrdered.map(p => {
29
+ const cls = classifyPort(p.name, p.label, true, raw.name);
30
+ return {
31
+ id: p.id,
32
+ direction: cls.direction,
33
+ kind: cls.kind,
34
+ label: p.label,
35
+ varName: cls.varName,
36
+ dataType: p.dataType || cls.dataType,
37
+ linkIds: p.links || [],
38
+ };
39
+ });
40
+ const portsOut = portsOutOrdered.map(p => {
41
+ const cls = classifyPort(p.name, p.label, false, raw.name);
42
+ return {
43
+ id: p.id,
44
+ direction: cls.direction,
45
+ kind: cls.kind,
46
+ label: p.label,
47
+ varName: cls.varName,
48
+ dataType: p.dataType || cls.dataType,
49
+ linkIds: p.links || [],
50
+ };
51
+ });
52
+ return {
53
+ id: raw.id,
54
+ kind,
55
+ name: raw.name,
56
+ color: raw.color || 'red',
57
+ x: raw.x,
58
+ y: raw.y,
59
+ portsIn,
60
+ portsOut,
61
+ extras: raw.extras || {},
62
+ };
63
+ });
64
+ const edges = rawLinks.map(raw => ({
65
+ id: raw.id,
66
+ kind: classifyEdge(raw.type),
67
+ sourceNodeId: raw.source,
68
+ sourcePortId: raw.sourcePort,
69
+ targetNodeId: raw.target,
70
+ targetPortId: raw.targetPort,
71
+ points: raw.points || [],
72
+ color: raw.color || 'gray',
73
+ }));
74
+ return {
75
+ id: obj.id,
76
+ viewport: {
77
+ x: obj.offsetX || 0,
78
+ y: obj.offsetY || 0,
79
+ zoom: obj.zoom || 100,
80
+ },
81
+ nodes,
82
+ edges,
83
+ };
84
+ }
85
+ function orderPorts(ports, order, isIn) {
86
+ if (!order || order.length === 0) {
87
+ return ports.filter(p => p.in === isIn);
88
+ }
89
+ const portById = new Map(ports.map(p => [p.id, p]));
90
+ return order.map(id => portById.get(id)).filter((p) => p != null);
91
+ }
92
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/parser/parse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare function validate(json: unknown): {
2
+ valid: boolean;
3
+ errors: string[];
4
+ };
5
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/parser/validate.ts"],"names":[],"mappings":"AAAA,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAuC5E"}
@@ -0,0 +1,31 @@
1
+ export function validate(json) {
2
+ const errors = [];
3
+ if (!json || typeof json !== 'object') {
4
+ return { valid: false, errors: ['Input must be an object'] };
5
+ }
6
+ const obj = json;
7
+ if (typeof obj.id !== 'string') {
8
+ errors.push('Missing or invalid "id" field');
9
+ }
10
+ if (!Array.isArray(obj.layers)) {
11
+ errors.push('Missing or invalid "layers" array');
12
+ return { valid: false, errors };
13
+ }
14
+ const layers = obj.layers;
15
+ const linkLayer = layers.find(l => l.type === 'diagram-links');
16
+ const nodeLayer = layers.find(l => l.type === 'diagram-nodes');
17
+ if (!nodeLayer) {
18
+ errors.push('Missing diagram-nodes layer');
19
+ }
20
+ if (!linkLayer) {
21
+ // Links layer is optional (a graph with no connections)
22
+ }
23
+ if (nodeLayer) {
24
+ const models = nodeLayer.models;
25
+ if (!models || typeof models !== 'object') {
26
+ errors.push('diagram-nodes layer missing "models" object');
27
+ }
28
+ }
29
+ return { valid: errors.length === 0, errors };
30
+ }
31
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/parser/validate.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,QAAQ,CAAC,IAAa;IACpC,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,yBAAyB,CAAC,EAAE,CAAC;IAC/D,CAAC;IAED,MAAM,GAAG,GAAG,IAA+B,CAAC;IAE5C,IAAI,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;QACjD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAClC,CAAC;IAED,MAAM,MAAM,GAAG,GAAG,CAAC,MAAwC,CAAC;IAE5D,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,eAAe,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,eAAe,CAAC,CAAC;IAE/D,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAC7C,CAAC;IAED,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,wDAAwD;IAC1D,CAAC;IAED,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC;QAChC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC1C,MAAM,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;AAChD,CAAC"}
@@ -0,0 +1,14 @@
1
+ export interface GradientColors {
2
+ color1: string;
3
+ color2: string;
4
+ }
5
+ /**
6
+ * Replicate the Xircuits title gradient from CustomNodeWidget.tsx:
7
+ * const color = new Color(p.background);
8
+ * color.alpha = 0.75; color.oklch.c *= 1.2;
9
+ * const color1 = color.to('oklch').toString()
10
+ * color.oklch.c *= 1.2; color.oklch.l /= 2;
11
+ * const color2 = color.to('oklch').toString()
12
+ */
13
+ export declare function titleGradient(baseColor: string): GradientColors;
14
+ //# sourceMappingURL=colors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../../src/render/colors.ts"],"names":[],"mappings":"AAkEA,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,CAW/D"}