yaml-flow 5.4.2 → 6.0.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 (199) hide show
  1. package/board-live-cards-cli.js +2 -2
  2. package/board-livecards-server-runtime.js +486 -547
  3. package/browser/asset-integrity.json +10 -0
  4. package/browser/board-livegraph-engine.js +2 -1676
  5. package/browser/board-livegraph-engine.js.map +1 -1
  6. package/browser/live-cards.js +347 -26
  7. package/browser/live-cards.schema.json +418 -132
  8. package/card-store.js +37 -0
  9. package/dist/batch/index.cjs +1 -108
  10. package/dist/batch/index.cjs.map +1 -1
  11. package/dist/batch/index.js +1 -106
  12. package/dist/batch/index.js.map +1 -1
  13. package/dist/board-live-cards-lib-Bg6EvCo5.d.cts +136 -0
  14. package/dist/board-live-cards-lib-jM2uYG1v.d.ts +136 -0
  15. package/dist/board-live-cards-public-CltXYgaY.d.cts +314 -0
  16. package/dist/board-live-cards-public-f-E-FAyp.d.ts +314 -0
  17. package/dist/board-livegraph-runtime/index.cjs +2 -1671
  18. package/dist/board-livegraph-runtime/index.cjs.map +1 -1
  19. package/dist/board-livegraph-runtime/index.d.cts +1 -2
  20. package/dist/board-livegraph-runtime/index.d.ts +1 -2
  21. package/dist/board-livegraph-runtime/index.js +2 -1662
  22. package/dist/board-livegraph-runtime/index.js.map +1 -1
  23. package/dist/board-livegraph-runtime/jsonata-sync.cjs +7587 -0
  24. package/dist/card-compute/index.cjs +9 -7159
  25. package/dist/card-compute/index.cjs.map +1 -1
  26. package/dist/card-compute/index.d.cts +22 -0
  27. package/dist/card-compute/index.d.ts +22 -0
  28. package/dist/card-compute/index.js +9 -7145
  29. package/dist/card-compute/index.js.map +1 -1
  30. package/dist/card-compute/jsonata-sync.cjs +7587 -0
  31. package/dist/cli/browser-api/board-live-cards-browser-adapter.cjs +2 -0
  32. package/dist/cli/browser-api/board-live-cards-browser-adapter.cjs.map +1 -0
  33. package/dist/cli/browser-api/board-live-cards-browser-adapter.d.cts +24 -0
  34. package/dist/cli/browser-api/board-live-cards-browser-adapter.d.ts +24 -0
  35. package/dist/cli/browser-api/board-live-cards-browser-adapter.js +2 -0
  36. package/dist/cli/browser-api/board-live-cards-browser-adapter.js.map +1 -0
  37. package/dist/cli/browser-api/card-store-browser-api.cjs +2 -0
  38. package/dist/cli/browser-api/card-store-browser-api.cjs.map +1 -0
  39. package/dist/cli/browser-api/card-store-browser-api.d.cts +26 -0
  40. package/dist/cli/browser-api/card-store-browser-api.d.ts +26 -0
  41. package/dist/cli/browser-api/card-store-browser-api.js +2 -0
  42. package/dist/cli/browser-api/card-store-browser-api.js.map +1 -0
  43. package/dist/cli/browser-api/jsonata-sync.cjs +7587 -0
  44. package/dist/cli/node/artifacts-store-cli.cjs +11 -0
  45. package/dist/cli/node/artifacts-store-cli.cjs.map +1 -0
  46. package/dist/cli/node/artifacts-store-cli.d.cts +8 -0
  47. package/dist/cli/node/artifacts-store-cli.d.ts +8 -0
  48. package/dist/cli/node/artifacts-store-cli.js +11 -0
  49. package/dist/cli/node/artifacts-store-cli.js.map +1 -0
  50. package/dist/cli/node/board-live-cards-cli.cjs +15 -0
  51. package/dist/cli/node/board-live-cards-cli.cjs.map +1 -0
  52. package/dist/cli/node/board-live-cards-cli.d.cts +20 -0
  53. package/dist/cli/node/board-live-cards-cli.d.ts +20 -0
  54. package/dist/cli/node/board-live-cards-cli.js +15 -0
  55. package/dist/cli/node/board-live-cards-cli.js.map +1 -0
  56. package/dist/cli/node/card-store-cli.cjs +8 -0
  57. package/dist/cli/node/card-store-cli.cjs.map +1 -0
  58. package/dist/cli/node/card-store-cli.d.cts +15 -0
  59. package/dist/cli/node/card-store-cli.d.ts +15 -0
  60. package/dist/cli/node/card-store-cli.js +8 -0
  61. package/dist/cli/node/card-store-cli.js.map +1 -0
  62. package/dist/cli/node/fs-board-adapter.cjs +14 -0
  63. package/dist/cli/node/fs-board-adapter.cjs.map +1 -0
  64. package/dist/cli/node/fs-board-adapter.d.cts +204 -0
  65. package/dist/cli/node/fs-board-adapter.d.ts +204 -0
  66. package/dist/cli/node/fs-board-adapter.js +14 -0
  67. package/dist/cli/node/fs-board-adapter.js.map +1 -0
  68. package/dist/cli/node/jsonata-sync.cjs +7587 -0
  69. package/dist/cli/node/source-cli-task-executor.cjs +11 -0
  70. package/dist/cli/node/source-cli-task-executor.cjs.map +1 -0
  71. package/dist/cli/node/source-cli-task-executor.d.cts +1 -0
  72. package/dist/cli/node/source-cli-task-executor.d.ts +1 -0
  73. package/dist/cli/node/source-cli-task-executor.js +11 -0
  74. package/dist/cli/node/source-cli-task-executor.js.map +1 -0
  75. package/dist/config/index.cjs +1 -79
  76. package/dist/config/index.cjs.map +1 -1
  77. package/dist/config/index.js +1 -76
  78. package/dist/config/index.js.map +1 -1
  79. package/dist/continuous-event-graph/index.cjs +2 -2129
  80. package/dist/continuous-event-graph/index.cjs.map +1 -1
  81. package/dist/continuous-event-graph/index.d.cts +81 -5
  82. package/dist/continuous-event-graph/index.d.ts +81 -5
  83. package/dist/continuous-event-graph/index.js +2 -2088
  84. package/dist/continuous-event-graph/index.js.map +1 -1
  85. package/dist/continuous-event-graph/jsonata-sync.cjs +7587 -0
  86. package/dist/event-graph/index.cjs +22 -8292
  87. package/dist/event-graph/index.cjs.map +1 -1
  88. package/dist/event-graph/index.js +22 -8237
  89. package/dist/event-graph/index.js.map +1 -1
  90. package/dist/execution-refs.cjs +2 -0
  91. package/dist/execution-refs.cjs.map +1 -0
  92. package/dist/execution-refs.d.cts +222 -0
  93. package/dist/execution-refs.d.ts +222 -0
  94. package/dist/execution-refs.js +2 -0
  95. package/dist/execution-refs.js.map +1 -0
  96. package/dist/index.cjs +29 -13221
  97. package/dist/index.cjs.map +1 -1
  98. package/dist/index.d.cts +2 -4
  99. package/dist/index.d.ts +2 -4
  100. package/dist/index.js +29 -13112
  101. package/dist/index.js.map +1 -1
  102. package/dist/inference/index.cjs +5 -617
  103. package/dist/inference/index.cjs.map +1 -1
  104. package/dist/inference/index.js +5 -610
  105. package/dist/inference/index.js.map +1 -1
  106. package/dist/jsonata-sync.cjs +7587 -0
  107. package/dist/{live-cards-bridge-x5XREkXm.d.cts → live-cards-bridge-BXbVTsna.d.cts} +27 -4
  108. package/dist/{live-cards-bridge-EQjytzI_.d.ts → live-cards-bridge-Ds28XR15.d.ts} +27 -4
  109. package/dist/pycli/quickjs-board-runtime.global.js +9 -0
  110. package/dist/pycli/quickjs-board-runtime.global.js.map +1 -0
  111. package/dist/pycli/quickjs-step-machine-runtime.global.js +5 -0
  112. package/dist/pycli/quickjs-step-machine-runtime.global.js.map +1 -0
  113. package/dist/step-machine/index.cjs +11 -7129
  114. package/dist/step-machine/index.cjs.map +1 -1
  115. package/dist/step-machine/index.js +11 -7113
  116. package/dist/step-machine/index.js.map +1 -1
  117. package/dist/storage-refs.cjs +10 -0
  118. package/dist/storage-refs.cjs.map +1 -0
  119. package/dist/storage-refs.d.cts +92 -0
  120. package/dist/storage-refs.d.ts +92 -0
  121. package/dist/storage-refs.js +10 -0
  122. package/dist/storage-refs.js.map +1 -0
  123. package/dist/stores/file.cjs +1 -114
  124. package/dist/stores/file.cjs.map +1 -1
  125. package/dist/stores/file.js +1 -112
  126. package/dist/stores/file.js.map +1 -1
  127. package/dist/stores/index.cjs +1 -231
  128. package/dist/stores/index.cjs.map +1 -1
  129. package/dist/stores/index.js +1 -227
  130. package/dist/stores/index.js.map +1 -1
  131. package/dist/stores/localStorage.cjs +1 -76
  132. package/dist/stores/localStorage.cjs.map +1 -1
  133. package/dist/stores/localStorage.js +1 -74
  134. package/dist/stores/localStorage.js.map +1 -1
  135. package/dist/stores/memory.cjs +1 -47
  136. package/dist/stores/memory.cjs.map +1 -1
  137. package/dist/stores/memory.js +1 -45
  138. package/dist/stores/memory.js.map +1 -1
  139. package/examples/browser/boards/portfolio-tracker/portfolio-t4.js +292 -0
  140. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-fetch-prices.js +218 -0
  141. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-fetch-prices.py +201 -0
  142. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-inference-adapter.js +25 -16
  143. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-public.js +553 -0
  144. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.py +365 -0
  145. package/examples/cli/step-machine-cli/portfolio-tracker/--base-ref/.runtime-out +1 -0
  146. package/examples/cli/step-machine-cli/portfolio-tracker/--base-ref/board-graph.json +32 -0
  147. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +53 -1
  148. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +15 -6
  149. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +6 -1
  150. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/poll-status-cli.js +57 -0
  151. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +1 -1
  152. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/status-cli.js +1 -1
  153. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +7 -2
  154. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +6 -2
  155. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/_board_pycli.py +97 -0
  156. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/add-cards.py +50 -0
  157. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/init-board.py +44 -0
  158. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/poll-status.py +70 -0
  159. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/reset-board-dir.py +36 -0
  160. package/examples/cli/step-machine-cli/portfolio-tracker/inline-python-demo.flow.yaml +26 -0
  161. package/examples/cli/step-machine-cli/portfolio-tracker/inline-python-handlers.py +39 -0
  162. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker-pycli.flow.yaml +80 -0
  163. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +25 -172
  164. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +40 -34
  165. package/examples/cli/step-machine-cli/portfolio-tracker/run-inline-python-demo-pycli.py +46 -0
  166. package/examples/cli/step-machine-cli/portfolio-tracker/run-portfolio-tracker-pycli.py +77 -0
  167. package/examples/cli/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +1 -2
  168. package/examples/example-board/agent-instructions.md +11 -5
  169. package/examples/example-board/demo-chat-handler.js +14 -4
  170. package/examples/example-board/demo-server-config.json +1 -0
  171. package/examples/example-board/demo-server.js +14 -7
  172. package/examples/example-board/demo-shell-browser.html +5 -4
  173. package/examples/example-board/demo-shell-with-server.html +6 -5
  174. package/examples/example-board/demo-task-executor.js +81 -35
  175. package/examples/index.html +0 -14
  176. package/examples/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +0 -1
  177. package/examples/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +1 -2
  178. package/package.json +39 -3
  179. package/schema/live-cards.schema.json +418 -132
  180. package/dist/cli/board-live-cards-cli.cjs +0 -10650
  181. package/dist/cli/board-live-cards-cli.cjs.map +0 -1
  182. package/dist/cli/board-live-cards-cli.d.cts +0 -179
  183. package/dist/cli/board-live-cards-cli.d.ts +0 -179
  184. package/dist/cli/board-live-cards-cli.js +0 -10598
  185. package/dist/cli/board-live-cards-cli.js.map +0 -1
  186. package/dist/journal-9HEgs7dU.d.ts +0 -28
  187. package/dist/journal-B-JCfQnh.d.cts +0 -28
  188. package/dist/schedule-Cszq9LYY.d.ts +0 -21
  189. package/dist/schedule-qWNL0RQh.d.cts +0 -21
  190. package/examples/browser/boards/portfolio-tracker/cards/holdings-table.json +0 -22
  191. package/examples/browser/boards/portfolio-tracker/cards/portfolio-form.json +0 -16
  192. package/examples/browser/boards/portfolio-tracker/cards/portfolio-risk-assessment.json +0 -28
  193. package/examples/browser/boards/portfolio-tracker/cards/portfolio-value.json +0 -15
  194. package/examples/browser/boards/portfolio-tracker/cards/price-fetch.json +0 -15
  195. package/examples/browser/boards/portfolio-tracker/cards/rebalancing-strategy.json +0 -28
  196. package/examples/browser/boards/portfolio-tracker/fetch-prices.js +0 -43
  197. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-task-executor.cjs +0 -96
  198. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.bat +0 -7
  199. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.js +0 -351
@@ -98,6 +98,20 @@ var LiveCard = (function () {
98
98
  .lc-gandalf-caret:hover { opacity:1; }
99
99
  .lc-gandalf-card.lc-collapsed .lc-gandalf-caret { transform:rotate(-90deg); }
100
100
  .lc-gandalf-card.lc-collapsed .card-body { display:none !important; }
101
+ .lc-token-row { display:flex; flex-wrap:wrap; gap:0.35rem; padding:0.2rem 0.5rem; background:transparent; align-items:center; justify-content:center; min-height:0; }
102
+ .lc-token-row-requires { border-bottom:none; padding-bottom:0.1rem; }
103
+ .lc-token-row-provides { border-top:none; padding-top:0.1rem; }
104
+ .lc-token-gem { display:inline-block; width:10px; height:10px; border-radius:50%; cursor:default; transition:transform .15s, box-shadow .15s; position:relative; }
105
+ .lc-token-gem:hover { transform:scale(1.5); box-shadow:0 0 4px rgba(0,0,0,0.3); z-index:5; }
106
+ .lc-token-gem-requires { background:var(--bs-secondary,#6c757d); border:1.5px solid var(--bs-secondary,#6c757d); }
107
+ .lc-token-gem-requires.lc-token-available { background:var(--bs-success,#198754); border-color:var(--bs-success,#198754); }
108
+ .lc-token-gem-provides { background:var(--bs-secondary,#6c757d); border:1.5px solid var(--bs-secondary,#6c757d); }
109
+ .lc-token-gem-provides.lc-token-available { background:var(--bs-success,#198754); border-color:var(--bs-success,#198754); }
110
+ .lc-running { animation:lc-running-pulse 2s ease-in-out infinite; position:relative; }
111
+ .lc-running::before { content:''; position:absolute; inset:-2px; border-radius:inherit; background:linear-gradient(90deg,transparent,rgba(13,110,253,.45),rgba(102,16,242,.4),rgba(13,110,253,.45),transparent); background-size:300% 100%; animation:lc-running-shimmer 2s linear infinite; z-index:-1; pointer-events:none; }
112
+ @keyframes lc-running-pulse { 0%,100%{ box-shadow:0 0 4px rgba(13,110,253,.15); } 50%{ box-shadow:0 0 14px 3px rgba(13,110,253,.35); } }
113
+ @keyframes lc-running-shimmer { 0%{ background-position:100% 0; } 100%{ background-position:-100% 0; } }
114
+ .lc-running .card-header { border-bottom-color:rgba(13,110,253,.35); }
101
115
  @media (max-width:576px) {
102
116
  .lc-metric-value { font-size:1.5rem; }
103
117
  .lc-chart-wrap { min-height:150px; }
@@ -781,7 +795,29 @@ var LiveCard = (function () {
781
795
  const requires = (node && node.card && Array.isArray(node.card.requires)) ? node.card.requires : [];
782
796
  if (!requires.length) return;
783
797
  const cleanup = _getCleanup(node.id);
784
- cleanup.unsubs = requires.map(upId => subscribe(upId, () => {
798
+
799
+ // Resolve required tokens to upstream node IDs via provides declarations.
800
+ // Build a token→nodeId map from all nodes the engine knows about.
801
+ const tokenMap = {};
802
+ const allNodeIds = Object.keys(_subs).concat(Object.keys(_nodeEls));
803
+ allNodeIds.forEach(function(nid) {
804
+ const n = cfg.resolve(nid);
805
+ if (!n || !n.card) return;
806
+ var provides = (Array.isArray(n.card.provides) && n.card.provides.length)
807
+ ? n.card.provides.map(function(p) { return typeof p === 'string' ? p : (p.bindTo || p); })
808
+ : [n.id];
809
+ provides.forEach(function(tok) { tokenMap[tok] = n.id; });
810
+ });
811
+
812
+ // Subscribe to each upstream provider node (deduplicated)
813
+ const seen = {};
814
+ const upIds = [];
815
+ requires.forEach(function(token) {
816
+ var srcId = tokenMap[token] || token; // fallback: treat token as nodeId
817
+ if (!seen[srcId]) { seen[srcId] = true; upIds.push(srcId); }
818
+ });
819
+
820
+ cleanup.unsubs = upIds.map(upId => subscribe(upId, () => {
785
821
  const info = _nodeEls[node.id];
786
822
  if (!info || !info.resultEl) return;
787
823
  const updated = cfg.resolve(node.id);
@@ -2351,6 +2387,17 @@ var LiveCard = (function () {
2351
2387
  };
2352
2388
  const ac = new AbortController();
2353
2389
  const signal = ac.signal;
2390
+ const _edges = []; // LeaderLine instances for canvas edges
2391
+
2392
+ // Edge style config (from canvas opts)
2393
+ const edgeOpts = co.edgeStyle || {};
2394
+ const edgeCfg = {
2395
+ color: edgeOpts.color || 'rgba(108, 117, 125, 0.6)',
2396
+ size: edgeOpts.size || 2,
2397
+ dash: edgeOpts.dash !== false,
2398
+ animation: edgeOpts.animation !== false,
2399
+ endPlug: edgeOpts.endPlug || 'arrow1',
2400
+ };
2354
2401
 
2355
2402
  // DOM containers
2356
2403
  const root = document.createElement('div');
@@ -2384,10 +2431,16 @@ var LiveCard = (function () {
2384
2431
  const s = document.createElement('style');
2385
2432
  s.id = 'lc-board-css';
2386
2433
  s.textContent = `
2387
- .lc-canvas-card { position:absolute; min-width:${cvs.minWidth}px; max-width:${cvs.maxWidth}px; cursor:grab; user-select:none; z-index:1; }
2434
+ .lc-canvas-card { position:absolute; min-width:${cvs.minWidth}px; cursor:grab; user-select:none; z-index:1; }
2388
2435
  .lc-canvas-card.lc-dragging { cursor:grabbing; z-index:10; box-shadow:0 8px 24px rgba(0,0,0,0.18)!important; }
2389
- .lc-canvas-card .card-body { max-height:${cvs.cardMaxH}px; overflow:auto; }
2436
+ .lc-canvas-card .card-body { overflow:auto; }
2437
+ .lc-canvas-card.lc-resizing { cursor:nwse-resize; z-index:10; }
2438
+ .lc-resize-handle { position:absolute; bottom:0; right:0; width:14px; height:14px; cursor:nwse-resize; z-index:2; opacity:0.4; transition:opacity .15s; }
2439
+ .lc-resize-handle:hover { opacity:1; }
2440
+ .lc-resize-handle::after { content:''; position:absolute; bottom:3px; right:3px; width:8px; height:8px; border-right:2px solid var(--bs-secondary,#6c757d); border-bottom:2px solid var(--bs-secondary,#6c757d); }
2441
+ .lc-canvas-edges path.lc-edge-path { stroke:var(--bs-secondary,#6c757d); stroke-width:1.5; stroke-dasharray:6 4; animation:lc-edge-flow 0.6s linear infinite; }
2390
2442
  .lc-canvas-edges line { stroke:var(--bs-secondary,#6c757d); stroke-width:1.5; }
2443
+ @keyframes lc-edge-flow { to { stroke-dashoffset:-10; } }
2391
2444
  .lc-source-node { position:absolute; cursor:grab; user-select:none; z-index:1; }
2392
2445
  .lc-source-node.lc-dragging { cursor:grabbing; z-index:10; }
2393
2446
  `;
@@ -2422,6 +2475,51 @@ var LiveCard = (function () {
2422
2475
  return (node && node.card && Array.isArray(node.card.requires)) ? node.card.requires : [];
2423
2476
  }
2424
2477
 
2478
+ /**
2479
+ * Returns tokens this node provides.
2480
+ * Explicit: card.provides[].bindTo
2481
+ * Implicit default: the node's own id (if no provides declared)
2482
+ */
2483
+ function _getProvides(node) {
2484
+ if (!node || !node.card) return [node ? node.id : ''];
2485
+ if (Array.isArray(node.card.provides) && node.card.provides.length > 0) {
2486
+ return node.card.provides.map(function(p) { return (typeof p === 'string') ? p : (p.bindTo || p); });
2487
+ }
2488
+ // Default: node provides a token equal to its own id
2489
+ return [node.id];
2490
+ }
2491
+
2492
+ /**
2493
+ * Build token → provider nodeId map from all nodes in the board.
2494
+ * Called before drawing edges so we can resolve requires tokens → source nodes.
2495
+ */
2496
+ function _buildTokenMap() {
2497
+ var map = {};
2498
+ nodeList.forEach(function(node) {
2499
+ _getProvides(node).forEach(function(token) {
2500
+ map[token] = node.id;
2501
+ });
2502
+ });
2503
+ return map;
2504
+ }
2505
+
2506
+ /**
2507
+ * Resolve required tokens to provider node IDs.
2508
+ * Returns deduplicated array of source node IDs for a given consumer node.
2509
+ */
2510
+ function _resolveEdgeSources(node, tokenMap) {
2511
+ var sources = [];
2512
+ var seen = {};
2513
+ _getRequires(node).forEach(function(token) {
2514
+ var srcId = tokenMap[token];
2515
+ if (srcId && !seen[srcId]) {
2516
+ seen[srcId] = true;
2517
+ sources.push(srcId);
2518
+ }
2519
+ });
2520
+ return sources;
2521
+ }
2522
+
2425
2523
  function _showCardInspector(node) {
2426
2524
  const modal = document.createElement('div');
2427
2525
  modal.className = 'modal d-block';
@@ -2562,8 +2660,9 @@ var LiveCard = (function () {
2562
2660
  const card = node && node.card ? node.card : {};
2563
2661
  const isSimulation = card.meta && card.meta.simulation === true;
2564
2662
  const isGandalfCard = card.meta && card.meta._gandalfCard === true;
2663
+ const isRunning = node && node.runtime_state && node.runtime_state.task_status === 'running';
2565
2664
  const extraClass = isSimulation ? ' lc-simulation-card' : (isGandalfCard ? ' lc-gandalf-card' : '');
2566
- wrap.className = 'card shadow-sm h-100' + extraClass;
2665
+ wrap.className = 'card shadow-sm h-100' + extraClass + (isRunning ? ' lc-running' : '');
2567
2666
  const header = document.createElement('div');
2568
2667
  header.className = 'card-header d-flex align-items-center gap-2 py-2';
2569
2668
  const title = (card.meta && card.meta.title) || node.id;
@@ -2639,8 +2738,44 @@ var LiveCard = (function () {
2639
2738
 
2640
2739
  const body = document.createElement('div');
2641
2740
  body.className = 'card-body p-2';
2741
+
2742
+ // Token gem rows — requires gems above header, provides gems below body
2743
+ const requiresTokens = (card.requires && Array.isArray(card.requires)) ? card.requires : [];
2744
+ const providesTokens = (Array.isArray(card.provides) && card.provides.length)
2745
+ ? card.provides.map(function(p) { return typeof p === 'string' ? p : (p.bindTo || p); })
2746
+ : [node.id];
2747
+
2748
+ // Requires gems — top of card (above header)
2749
+ if (requiresTokens.length) {
2750
+ const reqRow = document.createElement('div');
2751
+ reqRow.className = 'lc-token-row lc-token-row-requires';
2752
+ requiresTokens.forEach(function(token) {
2753
+ const gem = document.createElement('span');
2754
+ gem.className = 'lc-token-gem lc-token-gem-requires';
2755
+ gem.dataset.token = token;
2756
+ gem.title = token;
2757
+ reqRow.appendChild(gem);
2758
+ });
2759
+ wrap.appendChild(reqRow);
2760
+ }
2761
+
2642
2762
  wrap.appendChild(header);
2643
2763
  wrap.appendChild(body);
2764
+
2765
+ // Provides gems — bottom of card (below body)
2766
+ if (providesTokens.length) {
2767
+ const provRow = document.createElement('div');
2768
+ provRow.className = 'lc-token-row lc-token-row-provides';
2769
+ providesTokens.forEach(function(token) {
2770
+ const gem = document.createElement('span');
2771
+ gem.className = 'lc-token-gem lc-token-gem-provides';
2772
+ gem.dataset.token = token;
2773
+ gem.title = token;
2774
+ provRow.appendChild(gem);
2775
+ });
2776
+ wrap.appendChild(provRow);
2777
+ }
2778
+
2644
2779
  return { wrap, header, body };
2645
2780
  }
2646
2781
 
@@ -2662,6 +2797,7 @@ var LiveCard = (function () {
2662
2797
  // ---- Board mode ----
2663
2798
 
2664
2799
  function _renderBoard() {
2800
+ _destroyEdges();
2665
2801
  root.innerHTML = '';
2666
2802
  root.appendChild(gridEl);
2667
2803
  gridEl.innerHTML = '';
@@ -2684,6 +2820,7 @@ var LiveCard = (function () {
2684
2820
  nodeMap[node.id] = { node, colEl: col, bodyEl: body };
2685
2821
  engine.render(node, body, { showChat });
2686
2822
  });
2823
+ _updateTokenAvailability();
2687
2824
  }
2688
2825
 
2689
2826
  // ---- Canvas mode ----
@@ -2692,26 +2829,132 @@ var LiveCard = (function () {
2692
2829
  canvasInner.style.transform = `translate(${cvs.panX}px,${cvs.panY}px) scale(${cvs.zoom})`;
2693
2830
  }
2694
2831
 
2832
+ /**
2833
+ * Update token badge availability: a provides badge turns green when the
2834
+ * node has data; a requires badge turns green when the upstream provider
2835
+ * has data for that token.
2836
+ */
2837
+ function _updateTokenAvailability() {
2838
+ var tokenMap = _buildTokenMap();
2839
+ // Determine which nodes "have data" (non-empty card_data or fetched_sources, or status=fresh/completed)
2840
+ var nodeHasData = {};
2841
+ nodeList.forEach(function(node) {
2842
+ var cd = node.card_data || (node.card && node.card.card_data);
2843
+ var fs = node.fetched_sources;
2844
+ var status = cd && cd.status;
2845
+ var hasOutput = (cd && Object.keys(cd).length > 0) || (fs && Object.keys(fs).length > 0);
2846
+ nodeHasData[node.id] = hasOutput || status === 'fresh' || status === 'completed';
2847
+ });
2848
+
2849
+ // Update all gem elements in root container
2850
+ var allGems = root.querySelectorAll('.lc-token-gem');
2851
+ allGems.forEach(function(gem) {
2852
+ var token = gem.dataset.token;
2853
+ if (!token) return;
2854
+ if (gem.classList.contains('lc-token-gem-provides')) {
2855
+ // The provides gem: green if this node has data
2856
+ var nodeEl = gem.closest('[data-node-id]');
2857
+ var nId = nodeEl && nodeEl.dataset.nodeId;
2858
+ gem.classList.toggle('lc-token-available', !!(nId && nodeHasData[nId]));
2859
+ } else if (gem.classList.contains('lc-token-gem-requires')) {
2860
+ // The requires gem: green if the upstream provider for this token has data
2861
+ var srcId = tokenMap[token];
2862
+ gem.classList.toggle('lc-token-available', !!(srcId && nodeHasData[srcId]));
2863
+ }
2864
+ });
2865
+ }
2866
+
2867
+ function _destroyEdges() {
2868
+ _edges.forEach(function(line) { try { line.remove(); } catch(e) { /* noop */ } });
2869
+ _edges.length = 0;
2870
+ }
2871
+
2872
+ function _repositionEdges() {
2873
+ _edges.forEach(function(line) { try { line.position(); } catch(e) { /* noop */ } });
2874
+ }
2875
+
2695
2876
  function _drawEdges() {
2696
- svgEl.querySelectorAll('line').forEach(l => l.remove());
2877
+ _destroyEdges();
2878
+ svgEl.querySelectorAll('line,path').forEach(function(el) { el.remove(); });
2697
2879
  if (!cvs.edges) return;
2698
2880
 
2699
- nodeList.forEach(node => {
2700
- _getRequires(node).forEach(srcId => {
2701
- const srcInfo = nodeMap[srcId];
2702
- const tgtInfo = nodeMap[node.id];
2703
- if (!srcInfo || !tgtInfo) return;
2704
- const sEl = srcInfo.colEl;
2705
- const tEl = tgtInfo.colEl;
2706
- const sx = sEl.offsetLeft + sEl.offsetWidth;
2707
- const sy = sEl.offsetTop + sEl.offsetHeight / 2;
2708
- const tx = tEl.offsetLeft;
2709
- const ty = tEl.offsetTop + tEl.offsetHeight / 2;
2710
- const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
2711
- line.setAttribute('x1', sx); line.setAttribute('y1', sy);
2712
- line.setAttribute('x2', tx); line.setAttribute('y2', ty);
2713
- line.setAttribute('marker-end', 'url(#lc-arrow)');
2714
- svgEl.appendChild(line);
2881
+ // Build token → provider nodeId map
2882
+ var tokenMap = _buildTokenMap();
2883
+
2884
+ // For each consumer node, for each required token, draw edge from
2885
+ // the provider's "provides" badge → the consumer's "requires" badge.
2886
+ if (typeof LeaderLine !== 'undefined') {
2887
+ nodeList.forEach(function(node) {
2888
+ var tgtInfo = nodeMap[node.id];
2889
+ if (!tgtInfo || !tgtInfo.colEl) return;
2890
+ _getRequires(node).forEach(function(token) {
2891
+ var srcId = tokenMap[token];
2892
+ if (!srcId) return;
2893
+ var srcInfo = nodeMap[srcId];
2894
+ if (!srcInfo || !srcInfo.colEl) return;
2895
+ // Find the specific gem elements for this token
2896
+ var srcGem = srcInfo.colEl.querySelector('.lc-token-gem-provides[data-token="' + token + '"]');
2897
+ var tgtGem = tgtInfo.colEl.querySelector('.lc-token-gem-requires[data-token="' + token + '"]');
2898
+ var startEl = srcGem || srcInfo.colEl;
2899
+ var endEl = tgtGem || tgtInfo.colEl;
2900
+ try {
2901
+ var lineOpts = {
2902
+ color: edgeCfg.color,
2903
+ size: edgeCfg.size,
2904
+ endPlug: edgeCfg.endPlug,
2905
+ startSocket: srcGem ? 'bottom' : 'right',
2906
+ endSocket: tgtGem ? 'top' : 'left',
2907
+ };
2908
+ if (edgeCfg.dash) {
2909
+ lineOpts.dash = edgeCfg.animation ? { animation: true } : true;
2910
+ }
2911
+ _edges.push(new LeaderLine(startEl, endEl, lineOpts));
2912
+ } catch(e) { /* skip edge on error */ }
2913
+ });
2914
+ });
2915
+ return;
2916
+ }
2917
+
2918
+ // SVG fallback — connect badge-to-badge with curved paths
2919
+ nodeList.forEach(function(node) {
2920
+ var tgtInfo = nodeMap[node.id];
2921
+ if (!tgtInfo || !tgtInfo.colEl) return;
2922
+ _getRequires(node).forEach(function(token) {
2923
+ var srcId = tokenMap[token];
2924
+ if (!srcId) return;
2925
+ var srcInfo = nodeMap[srcId];
2926
+ if (!srcInfo || !srcInfo.colEl) return;
2927
+ // Locate gems; fall back to card element if gem not found
2928
+ var srcGem = srcInfo.colEl.querySelector('.lc-token-gem-provides[data-token="' + token + '"]');
2929
+ var tgtGem = tgtInfo.colEl.querySelector('.lc-token-gem-requires[data-token="' + token + '"]');
2930
+ var sx, sy, tx, ty;
2931
+ var innerRect = canvasInner.getBoundingClientRect();
2932
+ if (srcGem) {
2933
+ var srcRect = srcGem.getBoundingClientRect();
2934
+ sx = (srcRect.left + srcRect.width / 2 - innerRect.left) / cvs.zoom;
2935
+ sy = (srcRect.bottom - innerRect.top) / cvs.zoom;
2936
+ } else {
2937
+ var sEl = srcInfo.colEl;
2938
+ sx = sEl.offsetLeft + sEl.offsetWidth / 2;
2939
+ sy = sEl.offsetTop + sEl.offsetHeight;
2940
+ }
2941
+ if (tgtGem) {
2942
+ var tgtRect = tgtGem.getBoundingClientRect();
2943
+ tx = (tgtRect.left + tgtRect.width / 2 - innerRect.left) / cvs.zoom;
2944
+ ty = (tgtRect.top - innerRect.top) / cvs.zoom;
2945
+ } else {
2946
+ var tEl = tgtInfo.colEl;
2947
+ tx = tEl.offsetLeft + tEl.offsetWidth / 2;
2948
+ ty = tEl.offsetTop;
2949
+ }
2950
+ var cpOffset = Math.min(Math.abs(ty - sy) * 0.5, 80);
2951
+ var d = 'M ' + sx + ' ' + sy + ' C ' + sx + ' ' + (sy + cpOffset) + ', ' + tx + ' ' + (ty - cpOffset) + ', ' + tx + ' ' + ty;
2952
+ var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
2953
+ path.setAttribute('d', d);
2954
+ path.setAttribute('fill', 'none');
2955
+ path.setAttribute('marker-end', 'url(#lc-arrow)');
2956
+ path.classList.add('lc-edge-path');
2957
+ svgEl.appendChild(path);
2715
2958
  });
2716
2959
  });
2717
2960
  }
@@ -2736,7 +2979,8 @@ var LiveCard = (function () {
2736
2979
  const dy = (e.clientY - startY) / cvs.zoom;
2737
2980
  el.style.left = (origX + dx) + 'px';
2738
2981
  el.style.top = (origY + dy) + 'px';
2739
- _drawEdges();
2982
+ if (_edges.length) _repositionEdges();
2983
+ else _drawEdges();
2740
2984
  }, { signal });
2741
2985
 
2742
2986
  el.addEventListener('pointerup', () => {
@@ -2755,15 +2999,75 @@ var LiveCard = (function () {
2755
2999
  node.card.view.layout.canvas.y = y;
2756
3000
  }
2757
3001
  engine.notify(node.id);
2758
- _drawEdges();
3002
+ if (_edges.length) _repositionEdges();
3003
+ else _drawEdges();
3004
+ }, { signal });
3005
+ }
3006
+
3007
+ function _makeResizable(el, node) {
3008
+ const handle = document.createElement('div');
3009
+ handle.className = 'lc-resize-handle';
3010
+ el.appendChild(handle);
3011
+ el.style.overflow = 'visible';
3012
+
3013
+ let resizing = false, startX, startY, origW, origH;
3014
+
3015
+ handle.addEventListener('pointerdown', function(e) {
3016
+ if (e.button !== 0) return;
3017
+ e.stopPropagation();
3018
+ e.preventDefault();
3019
+ resizing = true;
3020
+ el.classList.add('lc-resizing');
3021
+ handle.setPointerCapture(e.pointerId);
3022
+ startX = e.clientX;
3023
+ startY = e.clientY;
3024
+ origW = el.offsetWidth;
3025
+ origH = el.offsetHeight;
3026
+ }, { signal });
3027
+
3028
+ handle.addEventListener('pointermove', function(e) {
3029
+ if (!resizing) return;
3030
+ const dw = (e.clientX - startX) / cvs.zoom;
3031
+ const dh = (e.clientY - startY) / cvs.zoom;
3032
+ const newW = Math.max(cvs.minWidth, origW + dw);
3033
+ const newH = Math.max(80, origH + dh);
3034
+ el.style.width = newW + 'px';
3035
+ el.style.height = newH + 'px';
3036
+ if (_edges.length) _repositionEdges();
3037
+ else _drawEdges();
3038
+ }, { signal });
3039
+
3040
+ handle.addEventListener('pointerup', function() {
3041
+ if (!resizing) return;
3042
+ resizing = false;
3043
+ el.classList.remove('lc-resizing');
3044
+ const w = el.offsetWidth;
3045
+ const h = el.offsetHeight;
3046
+ // Snap to grid
3047
+ const sw = cvs.snap > 1 ? Math.round(w / cvs.snap) * cvs.snap : w;
3048
+ const sh = cvs.snap > 1 ? Math.round(h / cvs.snap) * cvs.snap : h;
3049
+ el.style.width = sw + 'px';
3050
+ el.style.height = sh + 'px';
3051
+ // Persist dimensions
3052
+ _positions[node.id] = Object.assign(_positions[node.id] || {}, { w: sw, h: sh });
3053
+ if (node.card && node.card.view) {
3054
+ if (!node.card.view.layout) node.card.view.layout = {};
3055
+ if (!node.card.view.layout.canvas) node.card.view.layout.canvas = {};
3056
+ node.card.view.layout.canvas.w = sw;
3057
+ node.card.view.layout.canvas.h = sh;
3058
+ }
3059
+ engine.notify(node.id);
3060
+ if (_edges.length) _repositionEdges();
3061
+ else _drawEdges();
2759
3062
  }, { signal });
2760
3063
  }
2761
3064
 
2762
3065
  function _renderCanvas() {
3066
+ _destroyEdges();
2763
3067
  root.innerHTML = '';
2764
3068
  root.appendChild(canvasEl);
2765
3069
  canvasInner.querySelectorAll('.lc-canvas-card,.lc-source-node').forEach(el => el.remove());
2766
- svgEl.querySelectorAll('line').forEach(l => l.remove());
3070
+ svgEl.querySelectorAll('line,path').forEach(function(el) { el.remove(); });
2767
3071
  _initPositions();
2768
3072
  _applyTransform();
2769
3073
 
@@ -2788,6 +3092,7 @@ var LiveCard = (function () {
2788
3092
  el.style.left = pos.x + 'px';
2789
3093
  el.style.top = pos.y + 'px';
2790
3094
  if (pos.w) el.style.width = pos.w + 'px';
3095
+ if (pos.h) el.style.height = pos.h + 'px';
2791
3096
 
2792
3097
  const { wrap, body } = _buildCardWrapper(node);
2793
3098
  while (wrap.firstChild) el.appendChild(wrap.firstChild);
@@ -2798,10 +3103,21 @@ var LiveCard = (function () {
2798
3103
  nodeMap[node.id] = { node, colEl: el, bodyEl: body };
2799
3104
  engine.render(node, body, { showChat: false });
2800
3105
  _makeDraggable(el, node);
3106
+ _makeResizable(el, node);
2801
3107
  }
2802
3108
  });
2803
3109
 
2804
- _drawEdges();
3110
+ _updateTokenAvailability();
3111
+
3112
+ // Draw edges — use rAF for LeaderLine so elements are laid out first
3113
+ if (typeof LeaderLine !== 'undefined') {
3114
+ requestAnimationFrame(function() { _drawEdges(); });
3115
+ } else {
3116
+ _drawEdges();
3117
+ }
3118
+
3119
+ // Reposition LeaderLine edges on scroll
3120
+ canvasEl.addEventListener('scroll', function() { _repositionEdges(); }, { signal, passive: true });
2805
3121
 
2806
3122
  // Pan: middle-click or Ctrl+drag on background
2807
3123
  let panning = false, panStartX, panStartY, panOrigX, panOrigY;
@@ -2819,6 +3135,7 @@ var LiveCard = (function () {
2819
3135
  cvs.panX = panOrigX + (e.clientX - panStartX);
2820
3136
  cvs.panY = panOrigY + (e.clientY - panStartY);
2821
3137
  _applyTransform();
3138
+ _repositionEdges();
2822
3139
  }, { signal });
2823
3140
  canvasEl.addEventListener('pointerup', () => { panning = false; }, { signal });
2824
3141
 
@@ -2829,6 +3146,7 @@ var LiveCard = (function () {
2829
3146
  const delta = e.deltaY > 0 ? 0.9 : 1.1;
2830
3147
  cvs.zoom = Math.min(cvs.zoomMax, Math.max(cvs.zoomMin, cvs.zoom * delta));
2831
3148
  _applyTransform();
3149
+ _repositionEdges();
2832
3150
  }, { signal, passive: false });
2833
3151
  }
2834
3152
 
@@ -2840,11 +3158,12 @@ var LiveCard = (function () {
2840
3158
  // ---- Auto-layout (topological L → R) ----
2841
3159
 
2842
3160
  function autoLayout() {
3161
+ const tokenMap = _buildTokenMap();
2843
3162
  const incoming = {};
2844
3163
  const levels = {};
2845
3164
  nodeList.forEach(n => { incoming[n.id] = []; levels[n.id] = 0; });
2846
3165
  nodeList.forEach(n => {
2847
- _getRequires(n).forEach(srcId => {
3166
+ _resolveEdgeSources(n, tokenMap).forEach(srcId => {
2848
3167
  if (incoming[n.id]) incoming[n.id].push(srcId);
2849
3168
  });
2850
3169
  });
@@ -2910,6 +3229,7 @@ var LiveCard = (function () {
2910
3229
  function refresh() { _render(); }
2911
3230
 
2912
3231
  function clear() {
3232
+ _destroyEdges();
2913
3233
  engine.destroyAll();
2914
3234
  nodeList.length = 0;
2915
3235
  Object.keys(nodeMap).forEach(k => delete nodeMap[k]);
@@ -2929,6 +3249,7 @@ var LiveCard = (function () {
2929
3249
  }
2930
3250
 
2931
3251
  function destroy() {
3252
+ _destroyEdges();
2932
3253
  ac.abort();
2933
3254
  engine.destroyAll();
2934
3255
  nodeList.length = 0;