yaml-flow 6.0.0 → 7.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.
- package/board-live-cards-cli.js +4 -4
- package/browser/asset-integrity.json +3 -3
- package/browser/board-livecards-client.js +2 -0
- package/browser/board-livecards-client.js.map +1 -0
- package/browser/board-livecards-localstorage.js +10 -0
- package/browser/board-livecards-localstorage.js.map +1 -0
- package/browser/board-livegraph-engine.js +2 -2
- package/browser/board-livegraph-engine.js.map +1 -1
- package/browser/card-compute.js +28 -28
- package/browser/compute-jsonata.js +5 -0
- package/browser/compute-jsonata.js.map +1 -0
- package/browser/live-cards.js +264 -151
- package/card-store.js +4 -4
- package/dist/{board-live-cards-public-CltXYgaY.d.cts → board-live-cards-public-5n1-syA3.d.cts} +8 -5
- package/dist/{board-live-cards-public-f-E-FAyp.d.ts → board-live-cards-public-CK_J8uv0.d.ts} +8 -5
- package/dist/board-livegraph-runtime/index.cjs +2 -2
- package/dist/board-livegraph-runtime/index.cjs.map +1 -1
- package/dist/board-livegraph-runtime/index.d.cts +11 -9
- package/dist/board-livegraph-runtime/index.d.ts +11 -9
- package/dist/board-livegraph-runtime/index.js +2 -2
- package/dist/board-livegraph-runtime/index.js.map +1 -1
- package/dist/board-livegraph-runtime/jsonata-sync.cjs +37 -1
- package/dist/card-compute/index.cjs +4 -4
- package/dist/card-compute/index.cjs.map +1 -1
- package/dist/card-compute/index.d.cts +5 -1
- package/dist/card-compute/index.d.ts +5 -1
- package/dist/card-compute/index.js +4 -4
- package/dist/card-compute/index.js.map +1 -1
- package/dist/card-compute/jsonata-sync.cjs +37 -1
- package/dist/cli/browser-api/board-live-cards-browser-adapter.cjs +2 -1
- package/dist/cli/browser-api/board-live-cards-browser-adapter.cjs.map +1 -1
- package/dist/cli/browser-api/board-live-cards-browser-adapter.d.cts +27 -14
- package/dist/cli/browser-api/board-live-cards-browser-adapter.d.ts +27 -14
- package/dist/cli/browser-api/board-live-cards-browser-adapter.js +2 -1
- package/dist/cli/browser-api/board-live-cards-browser-adapter.js.map +1 -1
- package/dist/cli/browser-api/card-store-browser-api.cjs +1 -1
- package/dist/cli/browser-api/card-store-browser-api.cjs.map +1 -1
- package/dist/cli/browser-api/card-store-browser-api.js +1 -1
- package/dist/cli/browser-api/card-store-browser-api.js.map +1 -1
- package/dist/cli/browser-api/jsonata-sync.cjs +37 -1
- package/dist/cli/node/artifacts-store-cli.cjs +8 -8
- package/dist/cli/node/artifacts-store-cli.cjs.map +1 -1
- package/dist/cli/node/artifacts-store-cli.js +8 -8
- package/dist/cli/node/artifacts-store-cli.js.map +1 -1
- package/dist/cli/node/board-live-cards-cli.cjs +7 -7
- package/dist/cli/node/board-live-cards-cli.cjs.map +1 -1
- package/dist/cli/node/board-live-cards-cli.js +7 -7
- package/dist/cli/node/board-live-cards-cli.js.map +1 -1
- package/dist/cli/node/card-store-cli.cjs +5 -5
- package/dist/cli/node/card-store-cli.cjs.map +1 -1
- package/dist/cli/node/card-store-cli.js +5 -5
- package/dist/cli/node/card-store-cli.js.map +1 -1
- package/dist/cli/node/execution-adapter.cjs +3 -0
- package/dist/cli/node/execution-adapter.cjs.map +1 -0
- package/dist/cli/node/execution-adapter.d.cts +174 -0
- package/dist/cli/node/execution-adapter.d.ts +174 -0
- package/dist/cli/node/execution-adapter.js +3 -0
- package/dist/cli/node/execution-adapter.js.map +1 -0
- package/dist/cli/node/fs-board-adapter.cjs +7 -7
- package/dist/cli/node/fs-board-adapter.cjs.map +1 -1
- package/dist/cli/node/fs-board-adapter.d.cts +2 -2
- package/dist/cli/node/fs-board-adapter.d.ts +2 -2
- package/dist/cli/node/fs-board-adapter.js +7 -7
- package/dist/cli/node/fs-board-adapter.js.map +1 -1
- package/dist/cli/node/jsonata-sync.cjs +37 -1
- package/dist/cli/node/source-cli-task-executor.cjs +4 -4
- package/dist/cli/node/source-cli-task-executor.cjs.map +1 -1
- package/dist/cli/node/source-cli-task-executor.js +4 -4
- package/dist/cli/node/source-cli-task-executor.js.map +1 -1
- package/dist/continuous-event-graph/index.cjs +2 -2
- package/dist/continuous-event-graph/index.cjs.map +1 -1
- package/dist/continuous-event-graph/index.js +2 -2
- package/dist/continuous-event-graph/index.js.map +1 -1
- package/dist/continuous-event-graph/jsonata-sync.cjs +37 -1
- package/dist/execution-refs.cjs +2 -1
- package/dist/execution-refs.cjs.map +1 -1
- package/dist/execution-refs.d.cts +55 -12
- package/dist/execution-refs.d.ts +55 -12
- package/dist/execution-refs.js +2 -1
- package/dist/execution-refs.js.map +1 -1
- package/dist/index.cjs +10 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +10 -10
- package/dist/index.js.map +1 -1
- package/dist/jsonata-sync.cjs +37 -1
- package/dist/server-runtime/index.cjs +9 -0
- package/dist/server-runtime/index.cjs.map +1 -0
- package/dist/server-runtime/index.d.cts +31 -0
- package/dist/server-runtime/index.d.ts +31 -0
- package/dist/server-runtime/index.js +9 -0
- package/dist/server-runtime/index.js.map +1 -0
- package/dist/server-runtime/jsonata-sync.cjs +7623 -0
- package/dist/step-machine-public/index.cjs +3 -0
- package/dist/step-machine-public/index.cjs.map +1 -0
- package/dist/step-machine-public/index.d.cts +166 -0
- package/dist/step-machine-public/index.d.ts +166 -0
- package/dist/step-machine-public/index.js +3 -0
- package/dist/step-machine-public/index.js.map +1 -0
- package/dist/step-machine-public/jsonata-sync.cjs +7623 -0
- package/dist/storage-refs.cjs +2 -2
- package/dist/storage-refs.cjs.map +1 -1
- package/dist/storage-refs.d.cts +6 -6
- package/dist/storage-refs.d.ts +6 -6
- package/dist/storage-refs.js +2 -2
- package/dist/storage-refs.js.map +1 -1
- package/dist/types-CU3DjTKL.d.cts +147 -0
- package/dist/types-HGDTWIun.d.ts +147 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-t4.js +9 -10
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker-http-test.js +370 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker-http-test.py +398 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker-public.js +9 -10
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker-server.js +300 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker-server.py +617 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker-sse-worker.js +48 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker.py +11 -10
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +19 -4
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +4 -8
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +6 -10
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/poll-status-cli.js +8 -16
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +2 -6
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +4 -8
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/status-cli.js +3 -7
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +4 -8
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +7 -16
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +2 -6
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/_board_pycli.py +13 -3
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/add-cards.py +2 -1
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/init-board.py +2 -1
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/poll-status.py +2 -1
- package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +20 -24
- package/examples/cli/step-machine-cli/portfolio-tracker/run-inline-python-demo-pycli.py +0 -3
- package/examples/cli/step-machine-demo/jsonata-init-board-cli.js +8 -13
- package/examples/cli/step-machine-demo/jsonata-init-board.flow.yaml +33 -9
- package/examples/cli/step-machine-demo/one-step-cli-only.flow.yaml +3 -1
- package/examples/cli/step-machine-demo/step2-double-cli.js +6 -12
- package/examples/cli/step-machine-demo/two-step-math.flow.yaml +66 -4
- package/examples/cli/step-machine-demo/two-step-mixed.flow.yaml +13 -5
- package/examples/example-board/agent-instructions.md +1 -1
- package/examples/example-board/cards/card-my-identity.json +30 -6
- package/examples/example-board/cards/card-portfolio-action.json +24 -6
- package/examples/example-board/cards/card-portfolio-intelligence.json +97 -0
- package/examples/example-board/cards/card-portfolio-risks.json +24 -6
- package/examples/example-board/cards/card-rebalance-impact.json +22 -6
- package/examples/example-board/cards/card-rebalance-sim.json +66 -15
- package/examples/example-board/cards/cardT-market-prices.json +80 -0
- package/examples/example-board/cards/{card-portfolio-value.json → cardT-portfolio-value.json} +38 -10
- package/examples/example-board/cards/cardT-portfolio.json +78 -0
- package/examples/example-board/demo-server-config.json +1 -1
- package/examples/example-board/demo-server.js +383 -69
- package/examples/example-board/demo-shell-localstorage.html +774 -0
- package/examples/example-board/demo-shell-with-server.html +18 -36
- package/examples/example-board/demo-shell.html +5 -4
- package/examples/example-board/demo-task-executor.js +213 -265
- package/package.json +15 -13
- package/step-machine-cli.js +43 -310
- package/board-livecards-server-runtime.js +0 -1513
- package/browser/board-livecards-runtime-client.js +0 -263
- package/dist/pycli/quickjs-board-runtime.global.js +0 -9
- package/dist/pycli/quickjs-board-runtime.global.js.map +0 -1
- package/dist/pycli/quickjs-step-machine-runtime.global.js +0 -5
- package/dist/pycli/quickjs-step-machine-runtime.global.js.map +0 -1
- package/examples/cli/step-machine-demo/two-step-math-handlers.js +0 -32
- package/examples/cli/step-machine-demo/two-step-mixed-handlers.js +0 -24
- package/examples/example-board/cards/card-market-prices.json +0 -56
- package/examples/example-board/cards/card-portfolio.json +0 -44
- package/examples/example-board/demo-shell-browser.html +0 -675
package/browser/live-cards.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
// live-cards.js — LiveCards v3: Node-based Board/Canvas engine
|
|
2
2
|
//
|
|
3
3
|
// Schema: Each node has { id } required; all else optional.
|
|
4
|
-
// id, meta, card_data, requires, provides,
|
|
5
|
-
// Nodes with view render as cards; nodes with
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// shape (kind, url, mailbox, channel, model, ...) per executor.
|
|
10
|
-
// requires[] — upstream node IDs; engine subscribes automatically
|
|
4
|
+
// id, meta, card_data, requires, provides, view
|
|
5
|
+
// Nodes with view render as cards; nodes with no view but with source_defs declared on the
|
|
6
|
+
// underlying card definition render as source pills in canvas mode (source_defs are runtime-only;
|
|
7
|
+
// they are not interpreted here).
|
|
8
|
+
// requires[] — upstream provider tokens; engine subscribes automatically
|
|
11
9
|
// provides[] — [{ bindTo, src }] explicit downstream token bindings
|
|
10
|
+
// computed_values — derived values produced by the runtime; rendered as-is, never recomputed here
|
|
11
|
+
//
|
|
12
|
+
// Rendering contract: this module renders derived state only. View bind paths resolve to one of
|
|
13
|
+
// card_data | requires | computed_values | runtime_state. Raw fetched-source payloads stay in the
|
|
14
|
+
// runtime and never reach the Board.
|
|
12
15
|
//
|
|
13
16
|
// Uses Bootstrap 5 for layout/forms, optional Chart.js for charts.
|
|
14
|
-
// Uses CardCompute (card-compute.js) for declarative compute expressions.
|
|
15
17
|
//
|
|
16
18
|
// API:
|
|
17
19
|
// const engine = LiveCard.init({ resolve, onPatch, onPatchState, onRefresh, onAction, getChatMessages, markdown, sanitize, chartLib });
|
|
@@ -24,8 +26,15 @@
|
|
|
24
26
|
// engine.appendChatMessage(nodeId, role, text)
|
|
25
27
|
// engine.registerRenderer(name, fn)
|
|
26
28
|
//
|
|
27
|
-
//
|
|
28
|
-
// board.
|
|
29
|
+
// Reactive board (preferred): state in, view out. No destructive re-renders.
|
|
30
|
+
// const board = LiveCard.Board(engine, el, { initialState, getNodeIds, selectNode, mode?, canvas? });
|
|
31
|
+
// board.setState(nextState) — diff vs prev; per-node updates only
|
|
32
|
+
// board.destroy()
|
|
33
|
+
//
|
|
34
|
+
// Imperative core (advanced): direct node-list manipulation.
|
|
35
|
+
// const core = LiveCard.BoardCore(engine, el, { nodes, positions?, mode, canvas });
|
|
36
|
+
// core.add(node), core.remove(id), core.reorder(ids), core.updateNode(id, model)
|
|
37
|
+
// core.setMode('board'|'canvas'), core.setDevMode(flag), core.autoLayout(), core.clear(), core.destroy()
|
|
29
38
|
|
|
30
39
|
// eslint-disable-next-line no-unused-vars
|
|
31
40
|
var LiveCard = (function () {
|
|
@@ -230,6 +239,30 @@ var LiveCard = (function () {
|
|
|
230
239
|
const _formState = {}; // stateKey → { baseValues, journal }
|
|
231
240
|
const _notesState = {}; // stateKey → { baseContent, journal|null }
|
|
232
241
|
const _todoState = {}; // stateKey → { currentState, pending } for todo dirty tracking
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Overlay a "Saving…" spinner over `el` while a patch is in-flight.
|
|
245
|
+
* The overlay is removed automatically on the next SSE re-render because
|
|
246
|
+
* every editable renderer does `el.innerHTML = …` on refresh.
|
|
247
|
+
*/
|
|
248
|
+
function _showSavingOverlay(el) {
|
|
249
|
+
// Ensure the container is a positioned ancestor so the overlay can fill it.
|
|
250
|
+
if (getComputedStyle(el).position === 'static') el.style.position = 'relative';
|
|
251
|
+
const overlay = document.createElement('div');
|
|
252
|
+
overlay.className = 'lc-saving-overlay';
|
|
253
|
+
overlay.setAttribute('aria-live', 'polite');
|
|
254
|
+
overlay.style.cssText = [
|
|
255
|
+
'position:absolute', 'inset:0',
|
|
256
|
+
'background:rgba(255,255,255,0.78)',
|
|
257
|
+
'display:flex', 'align-items:center', 'justify-content:center',
|
|
258
|
+
'gap:0.5rem', 'z-index:20', 'border-radius:inherit',
|
|
259
|
+
'pointer-events:all', // blocks all clicks on underlying inputs
|
|
260
|
+
].join(';');
|
|
261
|
+
overlay.innerHTML =
|
|
262
|
+
'<span class="spinner-border spinner-border-sm text-primary" role="status" aria-hidden="true"></span>' +
|
|
263
|
+
'<span class="text-primary fw-medium small">Saving…</span>';
|
|
264
|
+
el.appendChild(overlay);
|
|
265
|
+
}
|
|
233
266
|
const _renderers = {}; // kind → fn
|
|
234
267
|
const _nodeEls = {}; // nodeId → { container, resultEl, uid }
|
|
235
268
|
const _chatModal = {
|
|
@@ -275,11 +308,6 @@ var LiveCard = (function () {
|
|
|
275
308
|
return _cleanup[id];
|
|
276
309
|
}
|
|
277
310
|
|
|
278
|
-
function _runCompute() {
|
|
279
|
-
// Runtime payload is authoritative; UI never recomputes derived values.
|
|
280
|
-
return Promise.resolve();
|
|
281
|
-
}
|
|
282
|
-
|
|
283
311
|
function _ensureChatModal() {
|
|
284
312
|
if (_chatModal.backdrop) return;
|
|
285
313
|
|
|
@@ -767,11 +795,9 @@ var LiveCard = (function () {
|
|
|
767
795
|
const ns = {
|
|
768
796
|
card: node && node.card ? node.card : {},
|
|
769
797
|
card_data: node && node.card_data ? node.card_data : {},
|
|
770
|
-
fetched_sources: node && node.fetched_sources ? node.fetched_sources : {},
|
|
771
798
|
requires: node && node.requires ? node.requires : {},
|
|
772
799
|
computed_values: node && node.computed_values ? node.computed_values : {},
|
|
773
800
|
runtime_state: node && node.runtime_state ? node.runtime_state : {},
|
|
774
|
-
data_objects: node && node.data_objects ? node.data_objects : {},
|
|
775
801
|
};
|
|
776
802
|
|
|
777
803
|
if (!Object.prototype.hasOwnProperty.call(ns, root)) return undefined;
|
|
@@ -1181,6 +1207,7 @@ var LiveCard = (function () {
|
|
|
1181
1207
|
const nextValues = getEffectiveValues();
|
|
1182
1208
|
cfg.onPatchState(node.id, { fieldValues: nextValues });
|
|
1183
1209
|
btn.textContent = 'Saving...';
|
|
1210
|
+
_showSavingOverlay(el);
|
|
1184
1211
|
}, { signal });
|
|
1185
1212
|
|
|
1186
1213
|
discardBtn.addEventListener('click', () => {
|
|
@@ -1257,8 +1284,11 @@ var LiveCard = (function () {
|
|
|
1257
1284
|
|
|
1258
1285
|
saveBtn.addEventListener('click', () => {
|
|
1259
1286
|
const nextValue = textarea.value;
|
|
1260
|
-
|
|
1287
|
+
// Wrap in a named key so spread into card_data gives card_data.notes = "..."
|
|
1288
|
+
// (same dict pattern as form/filter; writeTo is not required).
|
|
1289
|
+
cfg.onPatchState(node.id, { fieldValues: { notes: nextValue } });
|
|
1261
1290
|
saveBtn.textContent = 'Saving...';
|
|
1291
|
+
_showSavingOverlay(el);
|
|
1262
1292
|
}, { signal });
|
|
1263
1293
|
|
|
1264
1294
|
discardBtn.addEventListener('click', () => {
|
|
@@ -1343,6 +1373,7 @@ var LiveCard = (function () {
|
|
|
1343
1373
|
cfg.onPatchState(node.id, { fieldValues: rows });
|
|
1344
1374
|
const saveBtn = el.querySelector('.lc-et-save');
|
|
1345
1375
|
if (saveBtn) saveBtn.textContent = 'Saving...';
|
|
1376
|
+
_showSavingOverlay(el);
|
|
1346
1377
|
}
|
|
1347
1378
|
|
|
1348
1379
|
function commitDiscard() {
|
|
@@ -1961,11 +1992,11 @@ var LiveCard = (function () {
|
|
|
1961
1992
|
//
|
|
1962
1993
|
// Usage:
|
|
1963
1994
|
// { "kind": "ref",
|
|
1964
|
-
// "data": { "bind": "
|
|
1995
|
+
// "data": { "bind": "computed_values.proposed_trades",
|
|
1965
1996
|
// "viewBind": "card_data.display_mode",
|
|
1966
1997
|
// "fallbackKind": "table" } }
|
|
1967
1998
|
//
|
|
1968
|
-
// viewBind can point to any namespace: card_data,
|
|
1999
|
+
// viewBind can point to any namespace: card_data, requires, computed_values, runtime_state.
|
|
1969
2000
|
// If the resolved view object contains a "bind" sub-path, that overrides data.bind.
|
|
1970
2001
|
const _REF_KIND_WHITELIST = new Set([
|
|
1971
2002
|
'table','editable-table','chart','metric','list','badge',
|
|
@@ -2180,7 +2211,7 @@ var LiveCard = (function () {
|
|
|
2180
2211
|
if (node.card_data && node.card_data.status === 'error' && node.card_data.error) {
|
|
2181
2212
|
resultEl.innerHTML = `<div class="text-danger small fw-semibold">Refresh failed</div><pre class="text-muted small mt-1" style="white-space:pre-wrap">${_esc(node.card_data.error)}</pre>`;
|
|
2182
2213
|
} else {
|
|
2183
|
-
|
|
2214
|
+
_renderElements(node, resultEl);
|
|
2184
2215
|
}
|
|
2185
2216
|
|
|
2186
2217
|
// ---- Wire refresh ----
|
|
@@ -2275,7 +2306,7 @@ var LiveCard = (function () {
|
|
|
2275
2306
|
if (node.card_data.status === 'error' && node.card_data.error) {
|
|
2276
2307
|
info.resultEl.innerHTML = `<div class="text-danger small fw-semibold">Refresh failed</div><pre class="text-muted small mt-1" style="white-space:pre-wrap">${_esc(node.card_data.error)}</pre>`;
|
|
2277
2308
|
} else {
|
|
2278
|
-
|
|
2309
|
+
_renderElements(node, info.resultEl);
|
|
2279
2310
|
}
|
|
2280
2311
|
}
|
|
2281
2312
|
|
|
@@ -2354,10 +2385,11 @@ var LiveCard = (function () {
|
|
|
2354
2385
|
}
|
|
2355
2386
|
|
|
2356
2387
|
// ===========================================================================
|
|
2357
|
-
//
|
|
2388
|
+
// BoardCore — imperative grid (board) and DAG (canvas) modes.
|
|
2389
|
+
// Most callers should use Board (reactive wrapper) instead.
|
|
2358
2390
|
// ===========================================================================
|
|
2359
2391
|
|
|
2360
|
-
function
|
|
2392
|
+
function BoardCore(engine, containerEl, opts) {
|
|
2361
2393
|
opts = opts || {};
|
|
2362
2394
|
const mode = { current: opts.mode || 'board' };
|
|
2363
2395
|
const devMode = { current: opts.devMode || false };
|
|
@@ -2409,20 +2441,18 @@ var LiveCard = (function () {
|
|
|
2409
2441
|
|
|
2410
2442
|
const canvasEl = document.createElement('div');
|
|
2411
2443
|
canvasEl.className = 'lc-canvas';
|
|
2412
|
-
|
|
2413
|
-
const canvasOverflow = co.overflow || 'auto';
|
|
2414
|
-
canvasEl.style.cssText = 'position:relative;overflow:' + canvasOverflow + ';width:100%;height:' + canvasHeight + ';';
|
|
2444
|
+
canvasEl.style.cssText = 'position:relative;overflow:auto;width:100%;';
|
|
2415
2445
|
const canvasInner = document.createElement('div');
|
|
2416
2446
|
canvasInner.className = 'lc-canvas-inner';
|
|
2417
|
-
canvasInner.style.cssText = 'position:
|
|
2447
|
+
canvasInner.style.cssText = 'position:relative;transform-origin:0 0;min-width:100%;min-height:100%;';
|
|
2418
2448
|
canvasEl.appendChild(canvasInner);
|
|
2419
2449
|
|
|
2420
2450
|
// SVG overlay for edges
|
|
2421
2451
|
const svgEl = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
2422
2452
|
svgEl.setAttribute('class', 'lc-canvas-edges');
|
|
2423
|
-
svgEl.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;overflow:
|
|
2453
|
+
svgEl.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;overflow:hidden;z-index:0;';
|
|
2424
2454
|
const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
|
|
2425
|
-
defs.innerHTML = '<marker id="lc-arrow" viewBox="0 0 10 10" refX="
|
|
2455
|
+
defs.innerHTML = '<marker id="lc-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 1 L 8 5 L 0 9 z" fill="rgba(108,117,125,0.55)"/></marker>';
|
|
2426
2456
|
svgEl.appendChild(defs);
|
|
2427
2457
|
canvasInner.appendChild(svgEl);
|
|
2428
2458
|
|
|
@@ -2433,13 +2463,13 @@ var LiveCard = (function () {
|
|
|
2433
2463
|
s.textContent = `
|
|
2434
2464
|
.lc-canvas-card { position:absolute; min-width:${cvs.minWidth}px; cursor:grab; user-select:none; z-index:1; }
|
|
2435
2465
|
.lc-canvas-card.lc-dragging { cursor:grabbing; z-index:10; box-shadow:0 8px 24px rgba(0,0,0,0.18)!important; }
|
|
2436
|
-
.lc-canvas-card .card-body { overflow:
|
|
2466
|
+
.lc-canvas-card .card-body { overflow:hidden; }
|
|
2437
2467
|
.lc-canvas-card.lc-resizing { cursor:nwse-resize; z-index:10; }
|
|
2438
2468
|
.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
2469
|
.lc-resize-handle:hover { opacity:1; }
|
|
2440
2470
|
.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:
|
|
2442
|
-
.lc-canvas-edges line { stroke:
|
|
2471
|
+
.lc-canvas-edges path.lc-edge-path { stroke:rgba(100,140,200,0.5); stroke-width:2; fill:none; stroke-linecap:round; }
|
|
2472
|
+
.lc-canvas-edges line { stroke:rgba(100,140,200,0.4); stroke-width:2; }
|
|
2443
2473
|
@keyframes lc-edge-flow { to { stroke-dashoffset:-10; } }
|
|
2444
2474
|
.lc-source-node { position:absolute; cursor:grab; user-select:none; z-index:1; }
|
|
2445
2475
|
.lc-source-node.lc-dragging { cursor:grabbing; z-index:10; }
|
|
@@ -2545,32 +2575,9 @@ var LiveCard = (function () {
|
|
|
2545
2575
|
|
|
2546
2576
|
const cardSection = document.createElement('div');
|
|
2547
2577
|
cardSection.className = 'mb-4';
|
|
2548
|
-
cardSection.innerHTML = '<h6 class="fw-semibold mb-2">Card
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
const editor = document.createElement('textarea');
|
|
2553
|
-
editor.className = 'form-control form-control-sm font-monospace';
|
|
2554
|
-
editor.rows = 16;
|
|
2555
|
-
editor.style.whiteSpace = 'pre';
|
|
2556
|
-
editor.value = JSON.stringify(editableCardObject, null, 2);
|
|
2557
|
-
|
|
2558
|
-
const editorHint = document.createElement('div');
|
|
2559
|
-
editorHint.className = 'small text-muted mt-2';
|
|
2560
|
-
editorHint.textContent = 'Edit JSON and click Submit to apply updates to this card.';
|
|
2561
|
-
|
|
2562
|
-
const editorError = document.createElement('div');
|
|
2563
|
-
editorError.className = 'small text-danger mt-1 d-none';
|
|
2564
|
-
|
|
2565
|
-
const submitBtn = document.createElement('button');
|
|
2566
|
-
submitBtn.type = 'button';
|
|
2567
|
-
submitBtn.className = 'btn btn-primary btn-sm mb-2';
|
|
2568
|
-
submitBtn.textContent = 'Submit';
|
|
2569
|
-
|
|
2570
|
-
cardSection.appendChild(submitBtn);
|
|
2571
|
-
cardSection.appendChild(editor);
|
|
2572
|
-
cardSection.appendChild(editorHint);
|
|
2573
|
-
cardSection.appendChild(editorError);
|
|
2578
|
+
cardSection.innerHTML = '<h6 class="fw-semibold mb-2">Card Definition (Read-only)</h6>';
|
|
2579
|
+
const cardDef = (node && node.card) ? node.card : {};
|
|
2580
|
+
cardSection.innerHTML += `<pre style="background: #f5f5f5; padding: 10px; border-radius: 4px; overflow-x: auto; font-size: 12px; white-space: pre-wrap; word-wrap: break-word;">${_esc(JSON.stringify(cardDef, null, 2))}</pre>`;
|
|
2574
2581
|
body.appendChild(cardSection);
|
|
2575
2582
|
|
|
2576
2583
|
const computedSection = document.createElement('div');
|
|
@@ -2580,13 +2587,6 @@ var LiveCard = (function () {
|
|
|
2580
2587
|
computedSection.innerHTML += `<pre style="background: #f5f5f5; padding: 10px; border-radius: 4px; overflow-x: auto; font-size: 12px; white-space: pre-wrap; word-wrap: break-word;">${_esc(JSON.stringify(computedValues, null, 2))}</pre>`;
|
|
2581
2588
|
body.appendChild(computedSection);
|
|
2582
2589
|
|
|
2583
|
-
const sourcesSection = document.createElement('div');
|
|
2584
|
-
sourcesSection.className = 'mb-4';
|
|
2585
|
-
sourcesSection.innerHTML = '<h6 class="fw-semibold mb-2">Fetched Sources (Read-only)</h6>';
|
|
2586
|
-
const sourcesData = node.fetched_sources || {};
|
|
2587
|
-
sourcesSection.innerHTML += `<pre style="background: #f5f5f5; padding: 10px; border-radius: 4px; overflow-x: auto; font-size: 12px; white-space: pre-wrap; word-wrap: break-word;">${_esc(JSON.stringify(sourcesData, null, 2))}</pre>`;
|
|
2588
|
-
body.appendChild(sourcesSection);
|
|
2589
|
-
|
|
2590
2590
|
const requiresSection = document.createElement('div');
|
|
2591
2591
|
requiresSection.className = 'mb-4';
|
|
2592
2592
|
requiresSection.innerHTML = '<h6 class="fw-semibold mb-2">Requires (Read-only)</h6>';
|
|
@@ -2609,43 +2609,6 @@ var LiveCard = (function () {
|
|
|
2609
2609
|
closeBtn.textContent = 'Close';
|
|
2610
2610
|
closeBtn.addEventListener('click', closeModal);
|
|
2611
2611
|
|
|
2612
|
-
submitBtn.addEventListener('click', function () {
|
|
2613
|
-
editorError.classList.add('d-none');
|
|
2614
|
-
editorError.textContent = '';
|
|
2615
|
-
try {
|
|
2616
|
-
const parsed = JSON.parse(editor.value);
|
|
2617
|
-
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
2618
|
-
throw new Error('Card Object must be a JSON object.');
|
|
2619
|
-
}
|
|
2620
|
-
if (parsed.id && parsed.id !== node.id) {
|
|
2621
|
-
throw new Error('Changing card id is not supported in the inspector.');
|
|
2622
|
-
}
|
|
2623
|
-
|
|
2624
|
-
const fixedId = node.id;
|
|
2625
|
-
const preservedRuntime = {
|
|
2626
|
-
card_data: node.card_data,
|
|
2627
|
-
fetched_sources: node.fetched_sources,
|
|
2628
|
-
requires: node.requires,
|
|
2629
|
-
computed_values: node.computed_values,
|
|
2630
|
-
runtime_state: node.runtime_state,
|
|
2631
|
-
data_objects: node.data_objects,
|
|
2632
|
-
};
|
|
2633
|
-
node.card = parsed;
|
|
2634
|
-
node.id = fixedId;
|
|
2635
|
-
Object.assign(node, preservedRuntime);
|
|
2636
|
-
|
|
2637
|
-
engine.notify(node.id, { inspector: 'card-object-updated' });
|
|
2638
|
-
_render();
|
|
2639
|
-
|
|
2640
|
-
submitBtn.textContent = '✓ Saved';
|
|
2641
|
-
setTimeout(function () { submitBtn.textContent = 'Submit'; }, 1200);
|
|
2642
|
-
closeModal();
|
|
2643
|
-
} catch (err) {
|
|
2644
|
-
editorError.textContent = 'Invalid JSON: ' + String((err && err.message) || err);
|
|
2645
|
-
editorError.classList.remove('d-none');
|
|
2646
|
-
}
|
|
2647
|
-
});
|
|
2648
|
-
|
|
2649
2612
|
footer.appendChild(closeBtn);
|
|
2650
2613
|
content.appendChild(header);
|
|
2651
2614
|
content.appendChild(body);
|
|
@@ -2796,8 +2759,23 @@ var LiveCard = (function () {
|
|
|
2796
2759
|
|
|
2797
2760
|
// ---- Board mode ----
|
|
2798
2761
|
|
|
2762
|
+
// Compute canvas inner size from card positions + padding
|
|
2763
|
+
function _fitCanvasToContent() {
|
|
2764
|
+
var pad = 100;
|
|
2765
|
+
var maxR = 0, maxB = 0;
|
|
2766
|
+
canvasInner.querySelectorAll('.lc-canvas-card,.lc-source-node').forEach(function(el) {
|
|
2767
|
+
var r = el.offsetLeft + el.offsetWidth;
|
|
2768
|
+
var b = el.offsetTop + el.offsetHeight;
|
|
2769
|
+
if (r > maxR) maxR = r;
|
|
2770
|
+
if (b > maxB) maxB = b;
|
|
2771
|
+
});
|
|
2772
|
+
canvasInner.style.width = (maxR + pad) + 'px';
|
|
2773
|
+
canvasInner.style.height = (maxB + pad) + 'px';
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2799
2776
|
function _renderBoard() {
|
|
2800
2777
|
_destroyEdges();
|
|
2778
|
+
document.body.style.overflow = '';
|
|
2801
2779
|
root.innerHTML = '';
|
|
2802
2780
|
root.appendChild(gridEl);
|
|
2803
2781
|
gridEl.innerHTML = '';
|
|
@@ -2836,13 +2814,13 @@ var LiveCard = (function () {
|
|
|
2836
2814
|
*/
|
|
2837
2815
|
function _updateTokenAvailability() {
|
|
2838
2816
|
var tokenMap = _buildTokenMap();
|
|
2839
|
-
//
|
|
2817
|
+
// A node "has data" when card_data or computed_values is non-empty, or status is fresh/completed.
|
|
2840
2818
|
var nodeHasData = {};
|
|
2841
2819
|
nodeList.forEach(function(node) {
|
|
2842
2820
|
var cd = node.card_data || (node.card && node.card.card_data);
|
|
2843
|
-
var
|
|
2821
|
+
var cv = node.computed_values;
|
|
2844
2822
|
var status = cd && cd.status;
|
|
2845
|
-
var hasOutput = (cd && Object.keys(cd).length > 0) || (
|
|
2823
|
+
var hasOutput = (cd && Object.keys(cd).length > 0) || (cv && Object.keys(cv).length > 0);
|
|
2846
2824
|
nodeHasData[node.id] = hasOutput || status === 'fresh' || status === 'completed';
|
|
2847
2825
|
});
|
|
2848
2826
|
|
|
@@ -2881,41 +2859,7 @@ var LiveCard = (function () {
|
|
|
2881
2859
|
// Build token → provider nodeId map
|
|
2882
2860
|
var tokenMap = _buildTokenMap();
|
|
2883
2861
|
|
|
2884
|
-
//
|
|
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
|
|
2862
|
+
// SVG edges — rendered behind cards (z-index:0) for a clean look
|
|
2919
2863
|
nodeList.forEach(function(node) {
|
|
2920
2864
|
var tgtInfo = nodeMap[node.id];
|
|
2921
2865
|
if (!tgtInfo || !tgtInfo.colEl) return;
|
|
@@ -2947,8 +2891,26 @@ var LiveCard = (function () {
|
|
|
2947
2891
|
tx = tEl.offsetLeft + tEl.offsetWidth / 2;
|
|
2948
2892
|
ty = tEl.offsetTop;
|
|
2949
2893
|
}
|
|
2950
|
-
|
|
2951
|
-
var
|
|
2894
|
+
// Route bezier curves around cards — offset control points outward
|
|
2895
|
+
var dx = tx - sx;
|
|
2896
|
+
var dy = ty - sy;
|
|
2897
|
+
var dist = Math.sqrt(dx * dx + dy * dy);
|
|
2898
|
+
var cpLen = Math.max(40, Math.min(dist * 0.4, 120));
|
|
2899
|
+
// Determine if src is roughly above, below, left, or right of target
|
|
2900
|
+
var absDx = Math.abs(dx);
|
|
2901
|
+
var absDy = Math.abs(dy);
|
|
2902
|
+
var cp1x, cp1y, cp2x, cp2y;
|
|
2903
|
+
if (absDy > absDx * 0.4) {
|
|
2904
|
+
// Mostly vertical — curve control points go straight down/up
|
|
2905
|
+
cp1x = sx; cp1y = sy + cpLen;
|
|
2906
|
+
cp2x = tx; cp2y = ty - cpLen;
|
|
2907
|
+
} else {
|
|
2908
|
+
// Mostly horizontal — swing control points outward to avoid overlapping cards
|
|
2909
|
+
var sideSign = dx > 0 ? 1 : -1;
|
|
2910
|
+
cp1x = sx + sideSign * cpLen; cp1y = sy + cpLen * 0.5;
|
|
2911
|
+
cp2x = tx - sideSign * cpLen; cp2y = ty - cpLen * 0.5;
|
|
2912
|
+
}
|
|
2913
|
+
var d = 'M ' + sx + ' ' + sy + ' C ' + cp1x + ' ' + cp1y + ', ' + cp2x + ' ' + cp2y + ', ' + tx + ' ' + ty;
|
|
2952
2914
|
var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
2953
2915
|
path.setAttribute('d', d);
|
|
2954
2916
|
path.setAttribute('fill', 'none');
|
|
@@ -2999,6 +2961,7 @@ var LiveCard = (function () {
|
|
|
2999
2961
|
node.card.view.layout.canvas.y = y;
|
|
3000
2962
|
}
|
|
3001
2963
|
engine.notify(node.id);
|
|
2964
|
+
_fitCanvasToContent();
|
|
3002
2965
|
if (_edges.length) _repositionEdges();
|
|
3003
2966
|
else _drawEdges();
|
|
3004
2967
|
}, { signal });
|
|
@@ -3057,6 +3020,7 @@ var LiveCard = (function () {
|
|
|
3057
3020
|
node.card.view.layout.canvas.h = sh;
|
|
3058
3021
|
}
|
|
3059
3022
|
engine.notify(node.id);
|
|
3023
|
+
_fitCanvasToContent();
|
|
3060
3024
|
if (_edges.length) _repositionEdges();
|
|
3061
3025
|
else _drawEdges();
|
|
3062
3026
|
}, { signal });
|
|
@@ -3064,8 +3028,12 @@ var LiveCard = (function () {
|
|
|
3064
3028
|
|
|
3065
3029
|
function _renderCanvas() {
|
|
3066
3030
|
_destroyEdges();
|
|
3031
|
+
document.body.style.overflow = 'hidden';
|
|
3067
3032
|
root.innerHTML = '';
|
|
3068
3033
|
root.appendChild(canvasEl);
|
|
3034
|
+
// Fill remaining viewport height
|
|
3035
|
+
var top = canvasEl.getBoundingClientRect().top;
|
|
3036
|
+
canvasEl.style.height = co.height || ('calc(100vh - ' + top + 'px)');
|
|
3069
3037
|
canvasInner.querySelectorAll('.lc-canvas-card,.lc-source-node').forEach(el => el.remove());
|
|
3070
3038
|
svgEl.querySelectorAll('line,path').forEach(function(el) { el.remove(); });
|
|
3071
3039
|
_initPositions();
|
|
@@ -3109,12 +3077,11 @@ var LiveCard = (function () {
|
|
|
3109
3077
|
|
|
3110
3078
|
_updateTokenAvailability();
|
|
3111
3079
|
|
|
3112
|
-
//
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
} else {
|
|
3080
|
+
// Fit canvas to content then draw edges
|
|
3081
|
+
requestAnimationFrame(function() {
|
|
3082
|
+
_fitCanvasToContent();
|
|
3116
3083
|
_drawEdges();
|
|
3117
|
-
}
|
|
3084
|
+
});
|
|
3118
3085
|
|
|
3119
3086
|
// Reposition LeaderLine edges on scroll
|
|
3120
3087
|
canvasEl.addEventListener('scroll', function() { _repositionEdges(); }, { signal, passive: true });
|
|
@@ -3226,7 +3193,41 @@ var LiveCard = (function () {
|
|
|
3226
3193
|
_render();
|
|
3227
3194
|
}
|
|
3228
3195
|
|
|
3229
|
-
|
|
3196
|
+
/**
|
|
3197
|
+
* Per-node update: replace runtime fields on the existing node object in place
|
|
3198
|
+
* and re-render only that node's body. Outer wrapper is rebuilt to pick up
|
|
3199
|
+
* status/badges, but the surrounding column element is reused so layout is stable.
|
|
3200
|
+
* Editable element state is preserved via journal overlays keyed by nodeId:bindPath.
|
|
3201
|
+
*/
|
|
3202
|
+
function updateNode(id, model) {
|
|
3203
|
+
const entry = nodeMap[id];
|
|
3204
|
+
if (!entry) throw new Error('updateNode: unknown node id ' + id);
|
|
3205
|
+
const node = entry.node;
|
|
3206
|
+
if (model && typeof model === 'object') {
|
|
3207
|
+
if (model.card !== undefined) node.card = model.card;
|
|
3208
|
+
if (model.card_data !== undefined) node.card_data = model.card_data;
|
|
3209
|
+
if (model.requires !== undefined) node.requires = model.requires;
|
|
3210
|
+
if (model.computed_values !== undefined) node.computed_values = model.computed_values;
|
|
3211
|
+
if (model.runtime_state !== undefined) node.runtime_state = model.runtime_state;
|
|
3212
|
+
}
|
|
3213
|
+
engine.destroy(id);
|
|
3214
|
+
if (mode.current === 'board') {
|
|
3215
|
+
const colEl = entry.colEl;
|
|
3216
|
+
colEl.innerHTML = '';
|
|
3217
|
+
const built = _buildCardWrapper(node);
|
|
3218
|
+
colEl.appendChild(built.wrap);
|
|
3219
|
+
nodeMap[id] = { node, colEl, bodyEl: built.body };
|
|
3220
|
+
engine.render(node, built.body, { showChat });
|
|
3221
|
+
} else {
|
|
3222
|
+
const el = entry.colEl;
|
|
3223
|
+
el.innerHTML = '';
|
|
3224
|
+
const built = _buildCardWrapper(node);
|
|
3225
|
+
while (built.wrap.firstChild) el.appendChild(built.wrap.firstChild);
|
|
3226
|
+
nodeMap[id] = { node, colEl: el, bodyEl: built.body };
|
|
3227
|
+
engine.render(node, built.body, { showChat: false });
|
|
3228
|
+
}
|
|
3229
|
+
_updateTokenAvailability();
|
|
3230
|
+
}
|
|
3230
3231
|
|
|
3231
3232
|
function clear() {
|
|
3232
3233
|
_destroyEdges();
|
|
@@ -3250,6 +3251,7 @@ var LiveCard = (function () {
|
|
|
3250
3251
|
|
|
3251
3252
|
function destroy() {
|
|
3252
3253
|
_destroyEdges();
|
|
3254
|
+
document.body.style.overflow = '';
|
|
3253
3255
|
ac.abort();
|
|
3254
3256
|
engine.destroyAll();
|
|
3255
3257
|
nodeList.length = 0;
|
|
@@ -3268,7 +3270,7 @@ var LiveCard = (function () {
|
|
|
3268
3270
|
add,
|
|
3269
3271
|
remove,
|
|
3270
3272
|
reorder,
|
|
3271
|
-
|
|
3273
|
+
updateNode,
|
|
3272
3274
|
clear,
|
|
3273
3275
|
setMode,
|
|
3274
3276
|
setDevMode,
|
|
@@ -3281,9 +3283,120 @@ var LiveCard = (function () {
|
|
|
3281
3283
|
};
|
|
3282
3284
|
}
|
|
3283
3285
|
|
|
3286
|
+
// ===========================================================================
|
|
3287
|
+
// Board — reactive host. State in, view out. No destructive re-renders.
|
|
3288
|
+
// ===========================================================================
|
|
3289
|
+
|
|
3290
|
+
function Board(engine, containerEl, opts) {
|
|
3291
|
+
opts = opts || {};
|
|
3292
|
+
const initialState = opts.initialState;
|
|
3293
|
+
const getNodeIds = opts.getNodeIds;
|
|
3294
|
+
const selectNode = opts.selectNode;
|
|
3295
|
+
if (typeof getNodeIds !== 'function' || typeof selectNode !== 'function') {
|
|
3296
|
+
throw new Error('LiveCard.Board requires getNodeIds and selectNode functions');
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3299
|
+
let state = initialState;
|
|
3300
|
+
const prevModelsById = {};
|
|
3301
|
+
const prevFingerprintsById = {};
|
|
3302
|
+
|
|
3303
|
+
function _stableStringify(v) {
|
|
3304
|
+
if (v == null || typeof v !== 'object') return JSON.stringify(v);
|
|
3305
|
+
if (Array.isArray(v)) return '[' + v.map(_stableStringify).join(',') + ']';
|
|
3306
|
+
const keys = Object.keys(v).sort();
|
|
3307
|
+
return '{' + keys.map(k => JSON.stringify(k) + ':' + _stableStringify(v[k])).join(',') + '}';
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3310
|
+
function _modelFingerprint(model) {
|
|
3311
|
+
if (!model || typeof model !== 'object') return 'null';
|
|
3312
|
+
return _stableStringify({
|
|
3313
|
+
card: model.card,
|
|
3314
|
+
card_data: model.card_data,
|
|
3315
|
+
requires: model.requires,
|
|
3316
|
+
computed_values: model.computed_values,
|
|
3317
|
+
runtime_state: model.runtime_state,
|
|
3318
|
+
});
|
|
3319
|
+
}
|
|
3320
|
+
|
|
3321
|
+
const initialIds = getNodeIds(state);
|
|
3322
|
+
const initialNodes = initialIds.map(id => {
|
|
3323
|
+
const m = selectNode(state, id);
|
|
3324
|
+
prevModelsById[id] = m;
|
|
3325
|
+
prevFingerprintsById[id] = _modelFingerprint(m);
|
|
3326
|
+
return m;
|
|
3327
|
+
});
|
|
3328
|
+
|
|
3329
|
+
const coreOpts = {};
|
|
3330
|
+
Object.keys(opts).forEach(k => {
|
|
3331
|
+
if (k === 'initialState' || k === 'getNodeIds' || k === 'selectNode' || k === 'nodes') return;
|
|
3332
|
+
coreOpts[k] = opts[k];
|
|
3333
|
+
});
|
|
3334
|
+
coreOpts.nodes = initialNodes;
|
|
3335
|
+
|
|
3336
|
+
const core = BoardCore(engine, containerEl, coreOpts);
|
|
3337
|
+
|
|
3338
|
+
function _changed(prevFingerprint, nextFingerprint) {
|
|
3339
|
+
return prevFingerprint !== nextFingerprint;
|
|
3340
|
+
}
|
|
3341
|
+
|
|
3342
|
+
function setState(nextStateOrUpdater) {
|
|
3343
|
+
const nextState = (typeof nextStateOrUpdater === 'function')
|
|
3344
|
+
? nextStateOrUpdater(state)
|
|
3345
|
+
: nextStateOrUpdater;
|
|
3346
|
+
if (nextState === undefined) return;
|
|
3347
|
+
|
|
3348
|
+
state = nextState;
|
|
3349
|
+
const nextIds = getNodeIds(state);
|
|
3350
|
+
const nextSet = new Set(nextIds);
|
|
3351
|
+
|
|
3352
|
+
// Removals
|
|
3353
|
+
Object.keys(prevModelsById).forEach(id => {
|
|
3354
|
+
if (!nextSet.has(id)) {
|
|
3355
|
+
core.remove(id);
|
|
3356
|
+
delete prevModelsById[id];
|
|
3357
|
+
delete prevFingerprintsById[id];
|
|
3358
|
+
}
|
|
3359
|
+
});
|
|
3360
|
+
|
|
3361
|
+
// Additions and per-node updates
|
|
3362
|
+
nextIds.forEach(id => {
|
|
3363
|
+
const next = selectNode(state, id);
|
|
3364
|
+
const prev = prevModelsById[id];
|
|
3365
|
+
const nextFingerprint = _modelFingerprint(next);
|
|
3366
|
+
const prevFingerprint = prevFingerprintsById[id];
|
|
3367
|
+
if (!prev) {
|
|
3368
|
+
core.add(next);
|
|
3369
|
+
} else if (_changed(prevFingerprint, nextFingerprint)) {
|
|
3370
|
+
core.updateNode(id, next);
|
|
3371
|
+
}
|
|
3372
|
+
prevModelsById[id] = next;
|
|
3373
|
+
prevFingerprintsById[id] = nextFingerprint;
|
|
3374
|
+
});
|
|
3375
|
+
|
|
3376
|
+
// Reorder if id sequence differs
|
|
3377
|
+
const currentOrder = core.nodes.map(n => n.id);
|
|
3378
|
+
const orderDiffers = nextIds.length !== currentOrder.length
|
|
3379
|
+
|| nextIds.some((id, i) => id !== currentOrder[i]);
|
|
3380
|
+
if (orderDiffers) core.reorder(nextIds);
|
|
3381
|
+
}
|
|
3382
|
+
|
|
3383
|
+
function destroy() {
|
|
3384
|
+
Object.keys(prevModelsById).forEach(k => delete prevModelsById[k]);
|
|
3385
|
+
Object.keys(prevFingerprintsById).forEach(k => delete prevFingerprintsById[k]);
|
|
3386
|
+
core.destroy();
|
|
3387
|
+
}
|
|
3388
|
+
|
|
3389
|
+
return {
|
|
3390
|
+
setState,
|
|
3391
|
+
destroy,
|
|
3392
|
+
core,
|
|
3393
|
+
get state() { return state; },
|
|
3394
|
+
};
|
|
3395
|
+
}
|
|
3396
|
+
|
|
3284
3397
|
// ===========================================================================
|
|
3285
3398
|
// Module export
|
|
3286
3399
|
// ===========================================================================
|
|
3287
3400
|
|
|
3288
|
-
return { init, Board };
|
|
3401
|
+
return { init, Board, BoardCore };
|
|
3289
3402
|
})();
|
package/card-store.js
CHANGED
|
@@ -10,10 +10,7 @@ const distCli = path.join(__dirname, 'dist', 'cli', 'node', 'card-store-cli.js')
|
|
|
10
10
|
const srcCli = path.join(__dirname, 'src', 'cli', 'node', 'card-store-cli.ts');
|
|
11
11
|
const tsxCli = path.join(__dirname, 'node_modules', 'tsx', 'dist', 'cli.mjs');
|
|
12
12
|
|
|
13
|
-
if (fs.existsSync(
|
|
14
|
-
const { cli } = await import(pathToFileUrl(distCli).href);
|
|
15
|
-
await cli(process.argv.slice(2));
|
|
16
|
-
} else if (fs.existsSync(srcCli)) {
|
|
13
|
+
if (fs.existsSync(srcCli)) {
|
|
17
14
|
const result = spawnSync(process.execPath, [tsxCli, srcCli, ...process.argv.slice(2)], {
|
|
18
15
|
stdio: 'inherit',
|
|
19
16
|
shell: false,
|
|
@@ -26,6 +23,9 @@ if (fs.existsSync(distCli)) {
|
|
|
26
23
|
}
|
|
27
24
|
|
|
28
25
|
process.exit(result.status ?? 0);
|
|
26
|
+
} else if (fs.existsSync(distCli)) {
|
|
27
|
+
const { cli } = await import(pathToFileUrl(distCli).href);
|
|
28
|
+
await cli(process.argv.slice(2));
|
|
29
29
|
} else {
|
|
30
30
|
console.error('[card-store] Could not find dist or src CLI entrypoint.');
|
|
31
31
|
process.exit(1);
|