yaml-flow 6.0.0 → 7.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.
- 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 +261 -150
- package/card-store.js +4 -4
- package/dist/{board-live-cards-public-CltXYgaY.d.cts → board-live-cards-public-CW5074xr.d.cts} +9 -5
- package/dist/{board-live-cards-public-f-E-FAyp.d.ts → board-live-cards-public-hnZo0mAf.d.ts} +9 -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 +49 -11
- package/dist/execution-refs.d.ts +49 -11
- 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 +2 -0
- package/dist/step-machine-public/index.cjs.map +1 -0
- package/dist/step-machine-public/index.d.cts +159 -0
- package/dist/step-machine-public/index.d.ts +159 -0
- package/dist/step-machine-public/index.js +2 -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 +7 -6
- package/dist/storage-refs.d.ts +7 -6
- package/dist/storage-refs.js +2 -2
- package/dist/storage-refs.js.map +1 -1
- package/dist/types-B1ZRa4aI.d.ts +147 -0
- package/dist/types-BxEFcVK9.d.cts +147 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-t4.js +9 -10
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker-http-test.js +357 -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/cards/_index.json +47 -0
- package/examples/example-board/cards/card-market-prices.json +33 -9
- 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-portfolio-value.json +38 -10
- package/examples/example-board/cards/card-portfolio.json +57 -13
- 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/demo-server.js +360 -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 +217 -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/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', () => {
|
|
@@ -1259,6 +1286,7 @@ var LiveCard = (function () {
|
|
|
1259
1286
|
const nextValue = textarea.value;
|
|
1260
1287
|
cfg.onPatchState(node.id, { notes: nextValue });
|
|
1261
1288
|
saveBtn.textContent = 'Saving...';
|
|
1289
|
+
_showSavingOverlay(el);
|
|
1262
1290
|
}, { signal });
|
|
1263
1291
|
|
|
1264
1292
|
discardBtn.addEventListener('click', () => {
|
|
@@ -1343,6 +1371,7 @@ var LiveCard = (function () {
|
|
|
1343
1371
|
cfg.onPatchState(node.id, { fieldValues: rows });
|
|
1344
1372
|
const saveBtn = el.querySelector('.lc-et-save');
|
|
1345
1373
|
if (saveBtn) saveBtn.textContent = 'Saving...';
|
|
1374
|
+
_showSavingOverlay(el);
|
|
1346
1375
|
}
|
|
1347
1376
|
|
|
1348
1377
|
function commitDiscard() {
|
|
@@ -1961,11 +1990,11 @@ var LiveCard = (function () {
|
|
|
1961
1990
|
//
|
|
1962
1991
|
// Usage:
|
|
1963
1992
|
// { "kind": "ref",
|
|
1964
|
-
// "data": { "bind": "
|
|
1993
|
+
// "data": { "bind": "computed_values.proposed_trades",
|
|
1965
1994
|
// "viewBind": "card_data.display_mode",
|
|
1966
1995
|
// "fallbackKind": "table" } }
|
|
1967
1996
|
//
|
|
1968
|
-
// viewBind can point to any namespace: card_data,
|
|
1997
|
+
// viewBind can point to any namespace: card_data, requires, computed_values, runtime_state.
|
|
1969
1998
|
// If the resolved view object contains a "bind" sub-path, that overrides data.bind.
|
|
1970
1999
|
const _REF_KIND_WHITELIST = new Set([
|
|
1971
2000
|
'table','editable-table','chart','metric','list','badge',
|
|
@@ -2180,7 +2209,7 @@ var LiveCard = (function () {
|
|
|
2180
2209
|
if (node.card_data && node.card_data.status === 'error' && node.card_data.error) {
|
|
2181
2210
|
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
2211
|
} else {
|
|
2183
|
-
|
|
2212
|
+
_renderElements(node, resultEl);
|
|
2184
2213
|
}
|
|
2185
2214
|
|
|
2186
2215
|
// ---- Wire refresh ----
|
|
@@ -2275,7 +2304,7 @@ var LiveCard = (function () {
|
|
|
2275
2304
|
if (node.card_data.status === 'error' && node.card_data.error) {
|
|
2276
2305
|
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
2306
|
} else {
|
|
2278
|
-
|
|
2307
|
+
_renderElements(node, info.resultEl);
|
|
2279
2308
|
}
|
|
2280
2309
|
}
|
|
2281
2310
|
|
|
@@ -2354,10 +2383,11 @@ var LiveCard = (function () {
|
|
|
2354
2383
|
}
|
|
2355
2384
|
|
|
2356
2385
|
// ===========================================================================
|
|
2357
|
-
//
|
|
2386
|
+
// BoardCore — imperative grid (board) and DAG (canvas) modes.
|
|
2387
|
+
// Most callers should use Board (reactive wrapper) instead.
|
|
2358
2388
|
// ===========================================================================
|
|
2359
2389
|
|
|
2360
|
-
function
|
|
2390
|
+
function BoardCore(engine, containerEl, opts) {
|
|
2361
2391
|
opts = opts || {};
|
|
2362
2392
|
const mode = { current: opts.mode || 'board' };
|
|
2363
2393
|
const devMode = { current: opts.devMode || false };
|
|
@@ -2409,20 +2439,18 @@ var LiveCard = (function () {
|
|
|
2409
2439
|
|
|
2410
2440
|
const canvasEl = document.createElement('div');
|
|
2411
2441
|
canvasEl.className = 'lc-canvas';
|
|
2412
|
-
|
|
2413
|
-
const canvasOverflow = co.overflow || 'auto';
|
|
2414
|
-
canvasEl.style.cssText = 'position:relative;overflow:' + canvasOverflow + ';width:100%;height:' + canvasHeight + ';';
|
|
2442
|
+
canvasEl.style.cssText = 'position:relative;overflow:auto;width:100%;';
|
|
2415
2443
|
const canvasInner = document.createElement('div');
|
|
2416
2444
|
canvasInner.className = 'lc-canvas-inner';
|
|
2417
|
-
canvasInner.style.cssText = 'position:
|
|
2445
|
+
canvasInner.style.cssText = 'position:relative;transform-origin:0 0;min-width:100%;min-height:100%;';
|
|
2418
2446
|
canvasEl.appendChild(canvasInner);
|
|
2419
2447
|
|
|
2420
2448
|
// SVG overlay for edges
|
|
2421
2449
|
const svgEl = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
2422
2450
|
svgEl.setAttribute('class', 'lc-canvas-edges');
|
|
2423
|
-
svgEl.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;overflow:
|
|
2451
|
+
svgEl.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;overflow:hidden;z-index:0;';
|
|
2424
2452
|
const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
|
|
2425
|
-
defs.innerHTML = '<marker id="lc-arrow" viewBox="0 0 10 10" refX="
|
|
2453
|
+
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
2454
|
svgEl.appendChild(defs);
|
|
2427
2455
|
canvasInner.appendChild(svgEl);
|
|
2428
2456
|
|
|
@@ -2433,13 +2461,13 @@ var LiveCard = (function () {
|
|
|
2433
2461
|
s.textContent = `
|
|
2434
2462
|
.lc-canvas-card { position:absolute; min-width:${cvs.minWidth}px; cursor:grab; user-select:none; z-index:1; }
|
|
2435
2463
|
.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:
|
|
2464
|
+
.lc-canvas-card .card-body { overflow:hidden; }
|
|
2437
2465
|
.lc-canvas-card.lc-resizing { cursor:nwse-resize; z-index:10; }
|
|
2438
2466
|
.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
2467
|
.lc-resize-handle:hover { opacity:1; }
|
|
2440
2468
|
.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:
|
|
2469
|
+
.lc-canvas-edges path.lc-edge-path { stroke:rgba(100,140,200,0.5); stroke-width:2; fill:none; stroke-linecap:round; }
|
|
2470
|
+
.lc-canvas-edges line { stroke:rgba(100,140,200,0.4); stroke-width:2; }
|
|
2443
2471
|
@keyframes lc-edge-flow { to { stroke-dashoffset:-10; } }
|
|
2444
2472
|
.lc-source-node { position:absolute; cursor:grab; user-select:none; z-index:1; }
|
|
2445
2473
|
.lc-source-node.lc-dragging { cursor:grabbing; z-index:10; }
|
|
@@ -2545,32 +2573,9 @@ var LiveCard = (function () {
|
|
|
2545
2573
|
|
|
2546
2574
|
const cardSection = document.createElement('div');
|
|
2547
2575
|
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);
|
|
2576
|
+
cardSection.innerHTML = '<h6 class="fw-semibold mb-2">Card Definition (Read-only)</h6>';
|
|
2577
|
+
const cardDef = (node && node.card) ? node.card : {};
|
|
2578
|
+
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
2579
|
body.appendChild(cardSection);
|
|
2575
2580
|
|
|
2576
2581
|
const computedSection = document.createElement('div');
|
|
@@ -2580,13 +2585,6 @@ var LiveCard = (function () {
|
|
|
2580
2585
|
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
2586
|
body.appendChild(computedSection);
|
|
2582
2587
|
|
|
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
2588
|
const requiresSection = document.createElement('div');
|
|
2591
2589
|
requiresSection.className = 'mb-4';
|
|
2592
2590
|
requiresSection.innerHTML = '<h6 class="fw-semibold mb-2">Requires (Read-only)</h6>';
|
|
@@ -2609,43 +2607,6 @@ var LiveCard = (function () {
|
|
|
2609
2607
|
closeBtn.textContent = 'Close';
|
|
2610
2608
|
closeBtn.addEventListener('click', closeModal);
|
|
2611
2609
|
|
|
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
2610
|
footer.appendChild(closeBtn);
|
|
2650
2611
|
content.appendChild(header);
|
|
2651
2612
|
content.appendChild(body);
|
|
@@ -2796,8 +2757,23 @@ var LiveCard = (function () {
|
|
|
2796
2757
|
|
|
2797
2758
|
// ---- Board mode ----
|
|
2798
2759
|
|
|
2760
|
+
// Compute canvas inner size from card positions + padding
|
|
2761
|
+
function _fitCanvasToContent() {
|
|
2762
|
+
var pad = 100;
|
|
2763
|
+
var maxR = 0, maxB = 0;
|
|
2764
|
+
canvasInner.querySelectorAll('.lc-canvas-card,.lc-source-node').forEach(function(el) {
|
|
2765
|
+
var r = el.offsetLeft + el.offsetWidth;
|
|
2766
|
+
var b = el.offsetTop + el.offsetHeight;
|
|
2767
|
+
if (r > maxR) maxR = r;
|
|
2768
|
+
if (b > maxB) maxB = b;
|
|
2769
|
+
});
|
|
2770
|
+
canvasInner.style.width = (maxR + pad) + 'px';
|
|
2771
|
+
canvasInner.style.height = (maxB + pad) + 'px';
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2799
2774
|
function _renderBoard() {
|
|
2800
2775
|
_destroyEdges();
|
|
2776
|
+
document.body.style.overflow = '';
|
|
2801
2777
|
root.innerHTML = '';
|
|
2802
2778
|
root.appendChild(gridEl);
|
|
2803
2779
|
gridEl.innerHTML = '';
|
|
@@ -2836,13 +2812,13 @@ var LiveCard = (function () {
|
|
|
2836
2812
|
*/
|
|
2837
2813
|
function _updateTokenAvailability() {
|
|
2838
2814
|
var tokenMap = _buildTokenMap();
|
|
2839
|
-
//
|
|
2815
|
+
// A node "has data" when card_data or computed_values is non-empty, or status is fresh/completed.
|
|
2840
2816
|
var nodeHasData = {};
|
|
2841
2817
|
nodeList.forEach(function(node) {
|
|
2842
2818
|
var cd = node.card_data || (node.card && node.card.card_data);
|
|
2843
|
-
var
|
|
2819
|
+
var cv = node.computed_values;
|
|
2844
2820
|
var status = cd && cd.status;
|
|
2845
|
-
var hasOutput = (cd && Object.keys(cd).length > 0) || (
|
|
2821
|
+
var hasOutput = (cd && Object.keys(cd).length > 0) || (cv && Object.keys(cv).length > 0);
|
|
2846
2822
|
nodeHasData[node.id] = hasOutput || status === 'fresh' || status === 'completed';
|
|
2847
2823
|
});
|
|
2848
2824
|
|
|
@@ -2881,41 +2857,7 @@ var LiveCard = (function () {
|
|
|
2881
2857
|
// Build token → provider nodeId map
|
|
2882
2858
|
var tokenMap = _buildTokenMap();
|
|
2883
2859
|
|
|
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
|
|
2860
|
+
// SVG edges — rendered behind cards (z-index:0) for a clean look
|
|
2919
2861
|
nodeList.forEach(function(node) {
|
|
2920
2862
|
var tgtInfo = nodeMap[node.id];
|
|
2921
2863
|
if (!tgtInfo || !tgtInfo.colEl) return;
|
|
@@ -2947,8 +2889,26 @@ var LiveCard = (function () {
|
|
|
2947
2889
|
tx = tEl.offsetLeft + tEl.offsetWidth / 2;
|
|
2948
2890
|
ty = tEl.offsetTop;
|
|
2949
2891
|
}
|
|
2950
|
-
|
|
2951
|
-
var
|
|
2892
|
+
// Route bezier curves around cards — offset control points outward
|
|
2893
|
+
var dx = tx - sx;
|
|
2894
|
+
var dy = ty - sy;
|
|
2895
|
+
var dist = Math.sqrt(dx * dx + dy * dy);
|
|
2896
|
+
var cpLen = Math.max(40, Math.min(dist * 0.4, 120));
|
|
2897
|
+
// Determine if src is roughly above, below, left, or right of target
|
|
2898
|
+
var absDx = Math.abs(dx);
|
|
2899
|
+
var absDy = Math.abs(dy);
|
|
2900
|
+
var cp1x, cp1y, cp2x, cp2y;
|
|
2901
|
+
if (absDy > absDx * 0.4) {
|
|
2902
|
+
// Mostly vertical — curve control points go straight down/up
|
|
2903
|
+
cp1x = sx; cp1y = sy + cpLen;
|
|
2904
|
+
cp2x = tx; cp2y = ty - cpLen;
|
|
2905
|
+
} else {
|
|
2906
|
+
// Mostly horizontal — swing control points outward to avoid overlapping cards
|
|
2907
|
+
var sideSign = dx > 0 ? 1 : -1;
|
|
2908
|
+
cp1x = sx + sideSign * cpLen; cp1y = sy + cpLen * 0.5;
|
|
2909
|
+
cp2x = tx - sideSign * cpLen; cp2y = ty - cpLen * 0.5;
|
|
2910
|
+
}
|
|
2911
|
+
var d = 'M ' + sx + ' ' + sy + ' C ' + cp1x + ' ' + cp1y + ', ' + cp2x + ' ' + cp2y + ', ' + tx + ' ' + ty;
|
|
2952
2912
|
var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
2953
2913
|
path.setAttribute('d', d);
|
|
2954
2914
|
path.setAttribute('fill', 'none');
|
|
@@ -2999,6 +2959,7 @@ var LiveCard = (function () {
|
|
|
2999
2959
|
node.card.view.layout.canvas.y = y;
|
|
3000
2960
|
}
|
|
3001
2961
|
engine.notify(node.id);
|
|
2962
|
+
_fitCanvasToContent();
|
|
3002
2963
|
if (_edges.length) _repositionEdges();
|
|
3003
2964
|
else _drawEdges();
|
|
3004
2965
|
}, { signal });
|
|
@@ -3057,6 +3018,7 @@ var LiveCard = (function () {
|
|
|
3057
3018
|
node.card.view.layout.canvas.h = sh;
|
|
3058
3019
|
}
|
|
3059
3020
|
engine.notify(node.id);
|
|
3021
|
+
_fitCanvasToContent();
|
|
3060
3022
|
if (_edges.length) _repositionEdges();
|
|
3061
3023
|
else _drawEdges();
|
|
3062
3024
|
}, { signal });
|
|
@@ -3064,8 +3026,12 @@ var LiveCard = (function () {
|
|
|
3064
3026
|
|
|
3065
3027
|
function _renderCanvas() {
|
|
3066
3028
|
_destroyEdges();
|
|
3029
|
+
document.body.style.overflow = 'hidden';
|
|
3067
3030
|
root.innerHTML = '';
|
|
3068
3031
|
root.appendChild(canvasEl);
|
|
3032
|
+
// Fill remaining viewport height
|
|
3033
|
+
var top = canvasEl.getBoundingClientRect().top;
|
|
3034
|
+
canvasEl.style.height = co.height || ('calc(100vh - ' + top + 'px)');
|
|
3069
3035
|
canvasInner.querySelectorAll('.lc-canvas-card,.lc-source-node').forEach(el => el.remove());
|
|
3070
3036
|
svgEl.querySelectorAll('line,path').forEach(function(el) { el.remove(); });
|
|
3071
3037
|
_initPositions();
|
|
@@ -3109,12 +3075,11 @@ var LiveCard = (function () {
|
|
|
3109
3075
|
|
|
3110
3076
|
_updateTokenAvailability();
|
|
3111
3077
|
|
|
3112
|
-
//
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
} else {
|
|
3078
|
+
// Fit canvas to content then draw edges
|
|
3079
|
+
requestAnimationFrame(function() {
|
|
3080
|
+
_fitCanvasToContent();
|
|
3116
3081
|
_drawEdges();
|
|
3117
|
-
}
|
|
3082
|
+
});
|
|
3118
3083
|
|
|
3119
3084
|
// Reposition LeaderLine edges on scroll
|
|
3120
3085
|
canvasEl.addEventListener('scroll', function() { _repositionEdges(); }, { signal, passive: true });
|
|
@@ -3226,7 +3191,41 @@ var LiveCard = (function () {
|
|
|
3226
3191
|
_render();
|
|
3227
3192
|
}
|
|
3228
3193
|
|
|
3229
|
-
|
|
3194
|
+
/**
|
|
3195
|
+
* Per-node update: replace runtime fields on the existing node object in place
|
|
3196
|
+
* and re-render only that node's body. Outer wrapper is rebuilt to pick up
|
|
3197
|
+
* status/badges, but the surrounding column element is reused so layout is stable.
|
|
3198
|
+
* Editable element state is preserved via journal overlays keyed by nodeId:bindPath.
|
|
3199
|
+
*/
|
|
3200
|
+
function updateNode(id, model) {
|
|
3201
|
+
const entry = nodeMap[id];
|
|
3202
|
+
if (!entry) throw new Error('updateNode: unknown node id ' + id);
|
|
3203
|
+
const node = entry.node;
|
|
3204
|
+
if (model && typeof model === 'object') {
|
|
3205
|
+
if (model.card !== undefined) node.card = model.card;
|
|
3206
|
+
if (model.card_data !== undefined) node.card_data = model.card_data;
|
|
3207
|
+
if (model.requires !== undefined) node.requires = model.requires;
|
|
3208
|
+
if (model.computed_values !== undefined) node.computed_values = model.computed_values;
|
|
3209
|
+
if (model.runtime_state !== undefined) node.runtime_state = model.runtime_state;
|
|
3210
|
+
}
|
|
3211
|
+
engine.destroy(id);
|
|
3212
|
+
if (mode.current === 'board') {
|
|
3213
|
+
const colEl = entry.colEl;
|
|
3214
|
+
colEl.innerHTML = '';
|
|
3215
|
+
const built = _buildCardWrapper(node);
|
|
3216
|
+
colEl.appendChild(built.wrap);
|
|
3217
|
+
nodeMap[id] = { node, colEl, bodyEl: built.body };
|
|
3218
|
+
engine.render(node, built.body, { showChat });
|
|
3219
|
+
} else {
|
|
3220
|
+
const el = entry.colEl;
|
|
3221
|
+
el.innerHTML = '';
|
|
3222
|
+
const built = _buildCardWrapper(node);
|
|
3223
|
+
while (built.wrap.firstChild) el.appendChild(built.wrap.firstChild);
|
|
3224
|
+
nodeMap[id] = { node, colEl: el, bodyEl: built.body };
|
|
3225
|
+
engine.render(node, built.body, { showChat: false });
|
|
3226
|
+
}
|
|
3227
|
+
_updateTokenAvailability();
|
|
3228
|
+
}
|
|
3230
3229
|
|
|
3231
3230
|
function clear() {
|
|
3232
3231
|
_destroyEdges();
|
|
@@ -3250,6 +3249,7 @@ var LiveCard = (function () {
|
|
|
3250
3249
|
|
|
3251
3250
|
function destroy() {
|
|
3252
3251
|
_destroyEdges();
|
|
3252
|
+
document.body.style.overflow = '';
|
|
3253
3253
|
ac.abort();
|
|
3254
3254
|
engine.destroyAll();
|
|
3255
3255
|
nodeList.length = 0;
|
|
@@ -3268,7 +3268,7 @@ var LiveCard = (function () {
|
|
|
3268
3268
|
add,
|
|
3269
3269
|
remove,
|
|
3270
3270
|
reorder,
|
|
3271
|
-
|
|
3271
|
+
updateNode,
|
|
3272
3272
|
clear,
|
|
3273
3273
|
setMode,
|
|
3274
3274
|
setDevMode,
|
|
@@ -3281,9 +3281,120 @@ var LiveCard = (function () {
|
|
|
3281
3281
|
};
|
|
3282
3282
|
}
|
|
3283
3283
|
|
|
3284
|
+
// ===========================================================================
|
|
3285
|
+
// Board — reactive host. State in, view out. No destructive re-renders.
|
|
3286
|
+
// ===========================================================================
|
|
3287
|
+
|
|
3288
|
+
function Board(engine, containerEl, opts) {
|
|
3289
|
+
opts = opts || {};
|
|
3290
|
+
const initialState = opts.initialState;
|
|
3291
|
+
const getNodeIds = opts.getNodeIds;
|
|
3292
|
+
const selectNode = opts.selectNode;
|
|
3293
|
+
if (typeof getNodeIds !== 'function' || typeof selectNode !== 'function') {
|
|
3294
|
+
throw new Error('LiveCard.Board requires getNodeIds and selectNode functions');
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
let state = initialState;
|
|
3298
|
+
const prevModelsById = {};
|
|
3299
|
+
const prevFingerprintsById = {};
|
|
3300
|
+
|
|
3301
|
+
function _stableStringify(v) {
|
|
3302
|
+
if (v == null || typeof v !== 'object') return JSON.stringify(v);
|
|
3303
|
+
if (Array.isArray(v)) return '[' + v.map(_stableStringify).join(',') + ']';
|
|
3304
|
+
const keys = Object.keys(v).sort();
|
|
3305
|
+
return '{' + keys.map(k => JSON.stringify(k) + ':' + _stableStringify(v[k])).join(',') + '}';
|
|
3306
|
+
}
|
|
3307
|
+
|
|
3308
|
+
function _modelFingerprint(model) {
|
|
3309
|
+
if (!model || typeof model !== 'object') return 'null';
|
|
3310
|
+
return _stableStringify({
|
|
3311
|
+
card: model.card,
|
|
3312
|
+
card_data: model.card_data,
|
|
3313
|
+
requires: model.requires,
|
|
3314
|
+
computed_values: model.computed_values,
|
|
3315
|
+
runtime_state: model.runtime_state,
|
|
3316
|
+
});
|
|
3317
|
+
}
|
|
3318
|
+
|
|
3319
|
+
const initialIds = getNodeIds(state);
|
|
3320
|
+
const initialNodes = initialIds.map(id => {
|
|
3321
|
+
const m = selectNode(state, id);
|
|
3322
|
+
prevModelsById[id] = m;
|
|
3323
|
+
prevFingerprintsById[id] = _modelFingerprint(m);
|
|
3324
|
+
return m;
|
|
3325
|
+
});
|
|
3326
|
+
|
|
3327
|
+
const coreOpts = {};
|
|
3328
|
+
Object.keys(opts).forEach(k => {
|
|
3329
|
+
if (k === 'initialState' || k === 'getNodeIds' || k === 'selectNode' || k === 'nodes') return;
|
|
3330
|
+
coreOpts[k] = opts[k];
|
|
3331
|
+
});
|
|
3332
|
+
coreOpts.nodes = initialNodes;
|
|
3333
|
+
|
|
3334
|
+
const core = BoardCore(engine, containerEl, coreOpts);
|
|
3335
|
+
|
|
3336
|
+
function _changed(prevFingerprint, nextFingerprint) {
|
|
3337
|
+
return prevFingerprint !== nextFingerprint;
|
|
3338
|
+
}
|
|
3339
|
+
|
|
3340
|
+
function setState(nextStateOrUpdater) {
|
|
3341
|
+
const nextState = (typeof nextStateOrUpdater === 'function')
|
|
3342
|
+
? nextStateOrUpdater(state)
|
|
3343
|
+
: nextStateOrUpdater;
|
|
3344
|
+
if (nextState === undefined) return;
|
|
3345
|
+
|
|
3346
|
+
state = nextState;
|
|
3347
|
+
const nextIds = getNodeIds(state);
|
|
3348
|
+
const nextSet = new Set(nextIds);
|
|
3349
|
+
|
|
3350
|
+
// Removals
|
|
3351
|
+
Object.keys(prevModelsById).forEach(id => {
|
|
3352
|
+
if (!nextSet.has(id)) {
|
|
3353
|
+
core.remove(id);
|
|
3354
|
+
delete prevModelsById[id];
|
|
3355
|
+
delete prevFingerprintsById[id];
|
|
3356
|
+
}
|
|
3357
|
+
});
|
|
3358
|
+
|
|
3359
|
+
// Additions and per-node updates
|
|
3360
|
+
nextIds.forEach(id => {
|
|
3361
|
+
const next = selectNode(state, id);
|
|
3362
|
+
const prev = prevModelsById[id];
|
|
3363
|
+
const nextFingerprint = _modelFingerprint(next);
|
|
3364
|
+
const prevFingerprint = prevFingerprintsById[id];
|
|
3365
|
+
if (!prev) {
|
|
3366
|
+
core.add(next);
|
|
3367
|
+
} else if (_changed(prevFingerprint, nextFingerprint)) {
|
|
3368
|
+
core.updateNode(id, next);
|
|
3369
|
+
}
|
|
3370
|
+
prevModelsById[id] = next;
|
|
3371
|
+
prevFingerprintsById[id] = nextFingerprint;
|
|
3372
|
+
});
|
|
3373
|
+
|
|
3374
|
+
// Reorder if id sequence differs
|
|
3375
|
+
const currentOrder = core.nodes.map(n => n.id);
|
|
3376
|
+
const orderDiffers = nextIds.length !== currentOrder.length
|
|
3377
|
+
|| nextIds.some((id, i) => id !== currentOrder[i]);
|
|
3378
|
+
if (orderDiffers) core.reorder(nextIds);
|
|
3379
|
+
}
|
|
3380
|
+
|
|
3381
|
+
function destroy() {
|
|
3382
|
+
Object.keys(prevModelsById).forEach(k => delete prevModelsById[k]);
|
|
3383
|
+
Object.keys(prevFingerprintsById).forEach(k => delete prevFingerprintsById[k]);
|
|
3384
|
+
core.destroy();
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3387
|
+
return {
|
|
3388
|
+
setState,
|
|
3389
|
+
destroy,
|
|
3390
|
+
core,
|
|
3391
|
+
get state() { return state; },
|
|
3392
|
+
};
|
|
3393
|
+
}
|
|
3394
|
+
|
|
3284
3395
|
// ===========================================================================
|
|
3285
3396
|
// Module export
|
|
3286
3397
|
// ===========================================================================
|
|
3287
3398
|
|
|
3288
|
-
return { init, Board };
|
|
3399
|
+
return { init, Board, BoardCore };
|
|
3289
3400
|
})();
|
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);
|