@synergenius/flow-weaver 0.4.1 → 0.4.3

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.
@@ -59388,20 +59388,6 @@ function assignUnpositionedNodes(layers, diagramNodes, explicitPositions) {
59388
59388
  }
59389
59389
  }
59390
59390
  }
59391
- function resolveHorizontalOverlaps(diagramNodes) {
59392
- const nodes = [...diagramNodes.values()].sort((a, b) => a.x - b.x);
59393
- for (let i = 1; i < nodes.length; i++) {
59394
- const prev = nodes[i - 1];
59395
- const curr = nodes[i];
59396
- const prevRightExtent = maxPortLabelExtent(prev.outputs);
59397
- const currLeftExtent = maxPortLabelExtent(curr.inputs);
59398
- const minGap = Math.max(prevRightExtent + LABEL_CLEARANCE + currLeftExtent, MIN_EDGE_GAP);
59399
- const actualGap = curr.x - (prev.x + prev.width);
59400
- if (actualGap < minGap) {
59401
- curr.x = prev.x + prev.width + minGap;
59402
- }
59403
- }
59404
- }
59405
59391
  function buildDiagramGraph(ast, options = {}) {
59406
59392
  const themeName = options.theme ?? "dark";
59407
59393
  const nodeTypeMap = /* @__PURE__ */ new Map();
@@ -59510,7 +59496,6 @@ function buildDiagramGraph(ast, options = {}) {
59510
59496
  node.y = pos.y;
59511
59497
  }
59512
59498
  }
59513
- resolveHorizontalOverlaps(diagramNodes);
59514
59499
  } else if (nonePositioned) {
59515
59500
  const { layers } = layoutWorkflow(ast);
59516
59501
  assignLayerCoordinates(layers, diagramNodes);
@@ -59713,6 +59698,8 @@ function buildDiagramGraph(ast, options = {}) {
59713
59698
  }
59714
59699
  }
59715
59700
  }
59701
+ let originX = 0;
59702
+ let originY = 0;
59716
59703
  let normalizedMaxY = maxY - minY + padding * 2;
59717
59704
  let normalizedMaxX = maxX - minX + padding * 2;
59718
59705
  const allConns = [...connections];
@@ -59723,11 +59710,15 @@ function buildDiagramGraph(ast, options = {}) {
59723
59710
  const extent = pathExtent(conn.path);
59724
59711
  if (extent.maxY + padding > normalizedMaxY) normalizedMaxY = extent.maxY + padding;
59725
59712
  if (extent.maxX + padding > normalizedMaxX) normalizedMaxX = extent.maxX + padding;
59713
+ if (extent.minY - padding < originY) originY = extent.minY - padding;
59714
+ if (extent.minX - padding < originX) originX = extent.minX - padding;
59726
59715
  }
59716
+ normalizedMaxX -= originX;
59717
+ normalizedMaxY -= originY;
59727
59718
  return {
59728
59719
  nodes,
59729
59720
  connections,
59730
- bounds: { width: normalizedMaxX, height: normalizedMaxY },
59721
+ bounds: { width: normalizedMaxX, height: normalizedMaxY, originX, originY },
59731
59722
  workflowName: ast.name
59732
59723
  };
59733
59724
  }
@@ -59765,15 +59756,19 @@ function resolveDefaultIcon(nt) {
59765
59756
  return "code";
59766
59757
  }
59767
59758
  function pathExtent(path38) {
59768
- let maxX = -Infinity;
59769
- let maxY = -Infinity;
59759
+ let minX = Infinity, minY = Infinity;
59760
+ let maxX = -Infinity, maxY = -Infinity;
59770
59761
  const pattern = /(-?[\d.]+),(-?[\d.]+)/g;
59771
59762
  let m;
59772
59763
  while ((m = pattern.exec(path38)) !== null) {
59773
- maxX = Math.max(maxX, parseFloat(m[1]));
59774
- maxY = Math.max(maxY, parseFloat(m[2]));
59764
+ const x = parseFloat(m[1]);
59765
+ const y = parseFloat(m[2]);
59766
+ minX = Math.min(minX, x);
59767
+ minY = Math.min(minY, y);
59768
+ maxX = Math.max(maxX, x);
59769
+ maxY = Math.max(maxY, y);
59775
59770
  }
59776
- return { maxX, maxY };
59771
+ return { minX, minY, maxX, maxY };
59777
59772
  }
59778
59773
  function maxPortLabelExtent(ports) {
59779
59774
  if (ports.length === 0) return 0;
@@ -59804,7 +59799,9 @@ function renderSVG(graph, options = {}) {
59804
59799
  const themeName = options.theme ?? "dark";
59805
59800
  const theme = getTheme(themeName);
59806
59801
  const showPortLabels = options.showPortLabels ?? true;
59807
- let { width: vbWidth, height: vbHeight } = graph.bounds;
59802
+ let { width: vbWidth, height: vbHeight, originX: vbX, originY: vbY } = graph.bounds;
59803
+ vbX = vbX ?? 0;
59804
+ vbY = vbY ?? 0;
59808
59805
  vbWidth = Math.max(vbWidth, 200);
59809
59806
  vbHeight = Math.max(vbHeight, 100);
59810
59807
  const svgWidth = options.width || vbWidth;
@@ -59812,7 +59809,7 @@ function renderSVG(graph, options = {}) {
59812
59809
  const allConnections = collectAllConnections(graph);
59813
59810
  const parts2 = [];
59814
59811
  parts2.push(
59815
- `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${vbWidth} ${vbHeight}" width="${svgWidth}" height="${svgHeight}">`
59812
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${vbX} ${vbY} ${vbWidth} ${vbHeight}" width="${svgWidth}" height="${svgHeight}">`
59816
59813
  );
59817
59814
  parts2.push(`<style>`);
59818
59815
  parts2.push(` text { font-family: Montserrat, 'Segoe UI', Roboto, sans-serif; }`);
@@ -60065,7 +60062,7 @@ body {
60065
60062
  body.node-active .connections path.dimmed { opacity: 0.15; }
60066
60063
 
60067
60064
  /* Node hover glow */
60068
- .nodes > g:hover rect:first-of-type { filter: brightness(1.08); }
60065
+ .nodes g[data-node-id]:hover > rect:first-of-type { filter: brightness(1.08); }
60069
60066
 
60070
60067
  /* Zoom controls */
60071
60068
  #controls {
@@ -60260,27 +60257,38 @@ body.node-active .connections path.dimmed { opacity: 0.15; }
60260
60257
 
60261
60258
  // ---- Port label visibility via JS (since CSS sibling selectors can't reach .labels group) ----
60262
60259
  var labelEls = content.querySelectorAll('.labels g[data-port-label]');
60263
- var nodeEls = content.querySelectorAll('.nodes > g[data-node-id]');
60260
+ var nodeEls = content.querySelectorAll('.nodes g[data-node-id]');
60261
+
60262
+ function showLabelsFor(id) {
60263
+ labelEls.forEach(function(lbl) {
60264
+ var portId = lbl.getAttribute('data-port-label') || '';
60265
+ if (portId.indexOf(id + '.') === 0) {
60266
+ lbl.style.opacity = '1';
60267
+ lbl.style.pointerEvents = 'auto';
60268
+ }
60269
+ });
60270
+ }
60271
+ function hideLabelsFor(id) {
60272
+ labelEls.forEach(function(lbl) {
60273
+ var portId = lbl.getAttribute('data-port-label') || '';
60274
+ if (portId.indexOf(id + '.') === 0) {
60275
+ lbl.style.opacity = '0';
60276
+ lbl.style.pointerEvents = 'none';
60277
+ }
60278
+ });
60279
+ }
60264
60280
 
60265
60281
  nodeEls.forEach(function(nodeG) {
60266
60282
  var nodeId = nodeG.getAttribute('data-node-id');
60283
+ var parentNodeG = nodeG.parentElement ? nodeG.parentElement.closest('g[data-node-id]') : null;
60284
+ var parentId = parentNodeG ? parentNodeG.getAttribute('data-node-id') : null;
60267
60285
  nodeG.addEventListener('mouseenter', function() {
60268
- labelEls.forEach(function(lbl) {
60269
- var portId = lbl.getAttribute('data-port-label') || '';
60270
- if (portId.indexOf(nodeId + '.') === 0) {
60271
- lbl.style.opacity = '1';
60272
- lbl.style.pointerEvents = 'auto';
60273
- }
60274
- });
60286
+ if (parentId) hideLabelsFor(parentId);
60287
+ showLabelsFor(nodeId);
60275
60288
  });
60276
60289
  nodeG.addEventListener('mouseleave', function() {
60277
- labelEls.forEach(function(lbl) {
60278
- var portId = lbl.getAttribute('data-port-label') || '';
60279
- if (portId.indexOf(nodeId + '.') === 0) {
60280
- lbl.style.opacity = '0';
60281
- lbl.style.pointerEvents = 'none';
60282
- }
60283
- });
60290
+ hideLabelsFor(nodeId);
60291
+ if (parentId) showLabelsFor(parentId);
60284
60292
  });
60285
60293
  });
60286
60294
 
@@ -94412,7 +94420,7 @@ function displayInstalledPackage(pkg) {
94412
94420
  }
94413
94421
 
94414
94422
  // src/cli/index.ts
94415
- var version2 = true ? "0.4.1" : "0.0.0-dev";
94423
+ var version2 = true ? "0.4.3" : "0.0.0-dev";
94416
94424
  var program2 = new Command();
94417
94425
  program2.name("flow-weaver").description("Flow Weaver Annotations - Compile and validate workflow files").version(version2, "-v, --version", "Output the current version");
94418
94426
  program2.configureOutput({
@@ -744,7 +744,7 @@ export function buildDiagramGraph(ast, options = {}) {
744
744
  const allPositioned = [...diagramNodes.keys()].every(id => explicitPositions.has(id));
745
745
  const nonePositioned = ![...diagramNodes.keys()].some(id => explicitPositions.has(id));
746
746
  if (allPositioned) {
747
- // All nodes have explicit positions — apply them
747
+ // All nodes have explicit positions — apply them as-is
748
748
  for (const [id, pos] of explicitPositions) {
749
749
  const node = diagramNodes.get(id);
750
750
  if (node) {
@@ -752,9 +752,6 @@ export function buildDiagramGraph(ast, options = {}) {
752
752
  node.y = pos.y;
753
753
  }
754
754
  }
755
- // Resolve horizontal overlaps: scope nodes may be wider than the
756
- // original positions anticipated, so push downstream nodes apart.
757
- resolveHorizontalOverlaps(diagramNodes);
758
755
  }
759
756
  else if (nonePositioned) {
760
757
  // No positions — full auto-layout (original behavior)
@@ -982,7 +979,9 @@ export function buildDiagramGraph(ast, options = {}) {
982
979
  }
983
980
  }
984
981
  }
985
- // Extend bounds to include connection paths (orthogonal routes can go outside node area)
982
+ // Extend bounds to include connection paths (routes can go outside node area)
983
+ let originX = 0;
984
+ let originY = 0;
986
985
  let normalizedMaxY = (maxY - minY) + padding * 2;
987
986
  let normalizedMaxX = (maxX - minX) + padding * 2;
988
987
  const allConns = [...connections];
@@ -996,11 +995,18 @@ export function buildDiagramGraph(ast, options = {}) {
996
995
  normalizedMaxY = extent.maxY + padding;
997
996
  if (extent.maxX + padding > normalizedMaxX)
998
997
  normalizedMaxX = extent.maxX + padding;
998
+ if (extent.minY - padding < originY)
999
+ originY = extent.minY - padding;
1000
+ if (extent.minX - padding < originX)
1001
+ originX = extent.minX - padding;
999
1002
  }
1003
+ // Expand dimensions to cover the shifted origin
1004
+ normalizedMaxX -= originX;
1005
+ normalizedMaxY -= originY;
1000
1006
  return {
1001
1007
  nodes,
1002
1008
  connections,
1003
- bounds: { width: normalizedMaxX, height: normalizedMaxY },
1009
+ bounds: { width: normalizedMaxX, height: normalizedMaxY, originX, originY },
1004
1010
  workflowName: ast.name,
1005
1011
  };
1006
1012
  }
@@ -1052,17 +1058,21 @@ function resolveDefaultIcon(nt) {
1052
1058
  return 'flow';
1053
1059
  return 'code';
1054
1060
  }
1055
- /** Extract max X/Y extent from an SVG path string (for bounds calculation) */
1061
+ /** Extract min/max X/Y extent from an SVG path string (for bounds calculation) */
1056
1062
  function pathExtent(path) {
1057
- let maxX = -Infinity;
1058
- let maxY = -Infinity;
1063
+ let minX = Infinity, minY = Infinity;
1064
+ let maxX = -Infinity, maxY = -Infinity;
1059
1065
  const pattern = /(-?[\d.]+),(-?[\d.]+)/g;
1060
1066
  let m;
1061
1067
  while ((m = pattern.exec(path)) !== null) {
1062
- maxX = Math.max(maxX, parseFloat(m[1]));
1063
- maxY = Math.max(maxY, parseFloat(m[2]));
1068
+ const x = parseFloat(m[1]);
1069
+ const y = parseFloat(m[2]);
1070
+ minX = Math.min(minX, x);
1071
+ minY = Math.min(minY, y);
1072
+ maxX = Math.max(maxX, x);
1073
+ maxY = Math.max(maxY, y);
1064
1074
  }
1065
- return { maxX, maxY };
1075
+ return { minX, minY, maxX, maxY };
1066
1076
  }
1067
1077
  /** Estimate the maximum port label badge extent beyond the node edge */
1068
1078
  function maxPortLabelExtent(ports) {
@@ -73,7 +73,7 @@ body {
73
73
  body.node-active .connections path.dimmed { opacity: 0.15; }
74
74
 
75
75
  /* Node hover glow */
76
- .nodes > g:hover rect:first-of-type { filter: brightness(1.08); }
76
+ .nodes g[data-node-id]:hover > rect:first-of-type { filter: brightness(1.08); }
77
77
 
78
78
  /* Zoom controls */
79
79
  #controls {
@@ -268,27 +268,38 @@ body.node-active .connections path.dimmed { opacity: 0.15; }
268
268
 
269
269
  // ---- Port label visibility via JS (since CSS sibling selectors can't reach .labels group) ----
270
270
  var labelEls = content.querySelectorAll('.labels g[data-port-label]');
271
- var nodeEls = content.querySelectorAll('.nodes > g[data-node-id]');
271
+ var nodeEls = content.querySelectorAll('.nodes g[data-node-id]');
272
+
273
+ function showLabelsFor(id) {
274
+ labelEls.forEach(function(lbl) {
275
+ var portId = lbl.getAttribute('data-port-label') || '';
276
+ if (portId.indexOf(id + '.') === 0) {
277
+ lbl.style.opacity = '1';
278
+ lbl.style.pointerEvents = 'auto';
279
+ }
280
+ });
281
+ }
282
+ function hideLabelsFor(id) {
283
+ labelEls.forEach(function(lbl) {
284
+ var portId = lbl.getAttribute('data-port-label') || '';
285
+ if (portId.indexOf(id + '.') === 0) {
286
+ lbl.style.opacity = '0';
287
+ lbl.style.pointerEvents = 'none';
288
+ }
289
+ });
290
+ }
272
291
 
273
292
  nodeEls.forEach(function(nodeG) {
274
293
  var nodeId = nodeG.getAttribute('data-node-id');
294
+ var parentNodeG = nodeG.parentElement ? nodeG.parentElement.closest('g[data-node-id]') : null;
295
+ var parentId = parentNodeG ? parentNodeG.getAttribute('data-node-id') : null;
275
296
  nodeG.addEventListener('mouseenter', function() {
276
- labelEls.forEach(function(lbl) {
277
- var portId = lbl.getAttribute('data-port-label') || '';
278
- if (portId.indexOf(nodeId + '.') === 0) {
279
- lbl.style.opacity = '1';
280
- lbl.style.pointerEvents = 'auto';
281
- }
282
- });
297
+ if (parentId) hideLabelsFor(parentId);
298
+ showLabelsFor(nodeId);
283
299
  });
284
300
  nodeG.addEventListener('mouseleave', function() {
285
- labelEls.forEach(function(lbl) {
286
- var portId = lbl.getAttribute('data-port-label') || '';
287
- if (portId.indexOf(nodeId + '.') === 0) {
288
- lbl.style.opacity = '0';
289
- lbl.style.pointerEvents = 'none';
290
- }
291
- });
301
+ hideLabelsFor(nodeId);
302
+ if (parentId) showLabelsFor(parentId);
292
303
  });
293
304
  });
294
305
 
@@ -17,7 +17,9 @@ export function renderSVG(graph, options = {}) {
17
17
  const themeName = options.theme ?? 'dark';
18
18
  const theme = getTheme(themeName);
19
19
  const showPortLabels = options.showPortLabels ?? true;
20
- let { width: vbWidth, height: vbHeight } = graph.bounds;
20
+ let { width: vbWidth, height: vbHeight, originX: vbX, originY: vbY } = graph.bounds;
21
+ vbX = vbX ?? 0;
22
+ vbY = vbY ?? 0;
21
23
  // Ensure minimum bounds
22
24
  vbWidth = Math.max(vbWidth, 200);
23
25
  vbHeight = Math.max(vbHeight, 100);
@@ -27,7 +29,7 @@ export function renderSVG(graph, options = {}) {
27
29
  const allConnections = collectAllConnections(graph);
28
30
  const parts = [];
29
31
  // SVG open
30
- parts.push(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${vbWidth} ${vbHeight}" width="${svgWidth}" height="${svgHeight}">`);
32
+ parts.push(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="${vbX} ${vbY} ${vbWidth} ${vbHeight}" width="${svgWidth}" height="${svgHeight}">`);
31
33
  // Styles
32
34
  parts.push(`<style>`);
33
35
  parts.push(` text { font-family: Montserrat, 'Segoe UI', Roboto, sans-serif; }`);
@@ -52,6 +52,8 @@ export interface DiagramGraph {
52
52
  bounds: {
53
53
  width: number;
54
54
  height: number;
55
+ originX?: number;
56
+ originY?: number;
55
57
  };
56
58
  workflowName: string;
57
59
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synergenius/flow-weaver",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Deterministic workflow compiler for AI agents. Compiles to standalone TypeScript, no runtime dependencies.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -95,8 +95,8 @@
95
95
  "typecheck": "tsc --noEmit -p tsconfig.build.json",
96
96
  "docs": "typedoc && tsx scripts/generate-grammar-docs.ts",
97
97
  "docs:serve": "npm run docs && npx http-server docs/api -c-1 -o",
98
- "prepublishOnly": "npm run build",
99
- "cli": "ts-node src/cli/index.ts"
98
+ "prepare": "npm run build",
99
+ "cli": "tsx src/cli/index.ts"
100
100
  },
101
101
  "keywords": [
102
102
  "flow-weaver",