yaml-flow 4.0.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/browser/board-livegraph-runtime.js +1453 -0
  2. package/browser/board-livegraph-runtime.js.map +1 -0
  3. package/browser/card-compute.js +36 -17
  4. package/browser/live-cards.js +848 -109
  5. package/browser/live-cards.schema.json +46 -21
  6. package/dist/board-livegraph-runtime/index.cjs +1448 -0
  7. package/dist/board-livegraph-runtime/index.cjs.map +1 -0
  8. package/dist/board-livegraph-runtime/index.d.cts +101 -0
  9. package/dist/board-livegraph-runtime/index.d.ts +101 -0
  10. package/dist/board-livegraph-runtime/index.js +1441 -0
  11. package/dist/board-livegraph-runtime/index.js.map +1 -0
  12. package/dist/card-compute/index.cjs +159 -44
  13. package/dist/card-compute/index.cjs.map +1 -1
  14. package/dist/card-compute/index.d.cts +36 -11
  15. package/dist/card-compute/index.d.ts +36 -11
  16. package/dist/card-compute/index.js +156 -44
  17. package/dist/card-compute/index.js.map +1 -1
  18. package/dist/cli/board-live-cards-cli.cjs +476 -105
  19. package/dist/cli/board-live-cards-cli.cjs.map +1 -1
  20. package/dist/cli/board-live-cards-cli.d.cts +8 -16
  21. package/dist/cli/board-live-cards-cli.d.ts +8 -16
  22. package/dist/cli/board-live-cards-cli.js +476 -106
  23. package/dist/cli/board-live-cards-cli.js.map +1 -1
  24. package/dist/continuous-event-graph/index.cjs +74 -33
  25. package/dist/continuous-event-graph/index.cjs.map +1 -1
  26. package/dist/continuous-event-graph/index.d.cts +7 -23
  27. package/dist/continuous-event-graph/index.d.ts +7 -23
  28. package/dist/continuous-event-graph/index.js +73 -32
  29. package/dist/continuous-event-graph/index.js.map +1 -1
  30. package/dist/index.cjs +1440 -56
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +21 -3
  33. package/dist/index.d.ts +21 -3
  34. package/dist/index.js +1434 -56
  35. package/dist/index.js.map +1 -1
  36. package/dist/journal-DRfJiheM.d.cts +28 -0
  37. package/dist/journal-NLYuqege.d.ts +28 -0
  38. package/dist/{journal-B_2JnBMF.d.ts → live-cards-bridge-Or7fdEJV.d.ts} +5 -32
  39. package/dist/{journal-BJDjWb5Q.d.cts → live-cards-bridge-vGJ6tMzN.d.cts} +5 -32
  40. package/dist/schedule-CMcZe5Ny.d.ts +21 -0
  41. package/dist/schedule-CiucyCan.d.cts +21 -0
  42. package/examples/browser/boards/portfolio-tracker/cards/holdings-table.json +1 -1
  43. package/examples/browser/boards/portfolio-tracker/cards/portfolio-form.json +3 -3
  44. package/examples/browser/boards/portfolio-tracker/cards/portfolio-value.json +1 -1
  45. package/examples/browser/boards/portfolio-tracker/cards/price-fetch.json +3 -3
  46. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-task-executor.cjs +96 -0
  47. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.js +33 -5
  48. package/examples/browser/livecards-browser/index.html +37 -684
  49. package/examples/cli/step-machine-cli/portfolio-tracker/cards/holdings-table.json +1 -1
  50. package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +3 -3
  51. package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +1 -1
  52. package/examples/cli/step-machine-cli/portfolio-tracker/cards/price-fetch.json +3 -3
  53. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +2 -2
  54. package/examples/example-board/board.yaml +23 -0
  55. package/examples/example-board/bootstrap_payload.json +1 -0
  56. package/examples/example-board/cards/card-chain-region-alert.json +39 -0
  57. package/examples/example-board/cards/card-chain-region-totals.json +26 -0
  58. package/examples/example-board/cards/card-chain-top-region.json +24 -0
  59. package/examples/example-board/cards/card-ex-actions.json +32 -0
  60. package/examples/example-board/cards/card-ex-chart.json +30 -0
  61. package/examples/example-board/cards/card-ex-filter.json +36 -0
  62. package/examples/example-board/cards/card-ex-filtered-by-preference.json +59 -0
  63. package/examples/example-board/cards/card-ex-form.json +91 -0
  64. package/examples/example-board/cards/card-ex-list.json +22 -0
  65. package/examples/example-board/cards/card-ex-markdown.json +17 -0
  66. package/examples/example-board/cards/card-ex-metric.json +19 -0
  67. package/examples/example-board/cards/card-ex-narrative.json +36 -0
  68. package/examples/example-board/cards/card-ex-source-http.json +28 -0
  69. package/examples/example-board/cards/card-ex-source.json +21 -0
  70. package/examples/example-board/cards/card-ex-status.json +35 -0
  71. package/examples/example-board/cards/card-ex-table.json +30 -0
  72. package/examples/example-board/cards/card-ex-todo.json +29 -0
  73. package/examples/example-board/demo-chat-handler.js +69 -0
  74. package/examples/example-board/demo-server-config.json +7 -0
  75. package/examples/example-board/demo-server.js +124 -0
  76. package/examples/example-board/demo-shell-browser.html +806 -0
  77. package/examples/example-board/demo-shell-with-server.html +280 -0
  78. package/examples/example-board/demo-shell.html +62 -0
  79. package/examples/example-board/demo-task-executor.js +255 -0
  80. package/examples/example-board/mock.db +15 -0
  81. package/examples/example-board/reusable-board-runtime-client.js +265 -0
  82. package/examples/example-board/reusable-runtime-artifacts-adapter.js +233 -0
  83. package/examples/example-board/reusable-server-runtime.js +1341 -0
  84. package/examples/index.html +16 -9
  85. package/examples/npm-libs/continuous-event-graph/live-cards-board.ts +17 -17
  86. package/examples/npm-libs/continuous-event-graph/live-portfolio-dashboard.ts +23 -23
  87. package/examples/step-machine-cli/portfolio-tracker/cards/holdings-table.json +1 -1
  88. package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +3 -3
  89. package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +1 -1
  90. package/examples/step-machine-cli/portfolio-tracker/cards/price-fetch.json +1 -1
  91. package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker-task-executor.cjs +96 -0
  92. package/package.json +16 -2
  93. package/schema/card-runtime.schema.json +25 -0
  94. package/schema/live-cards.schema.json +46 -21
  95. package/browser/ingest-board.js +0 -296
  96. package/examples/ingest.js +0 -733
@@ -1,688 +1,41 @@
1
- <!DOCTYPE html>
1
+ <!doctype html>
2
2
  <html lang="en">
3
3
  <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>LiveCards v3 Demo</title>
7
-
8
- <!-- Bootstrap 5 -->
9
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
10
-
11
- <!-- DOMPurify -->
12
- <script src="https://cdn.jsdelivr.net/npm/dompurify@3.1.6/dist/purify.min.js"></script>
13
-
14
- <!-- marked (Markdown) -->
15
- <script src="https://cdn.jsdelivr.net/npm/marked@12.0.2/marked.min.js"></script>
16
-
17
- <!-- Chart.js -->
18
- <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.4/dist/chart.umd.min.js"></script>
19
-
20
- <!--
21
- CDN usage (after npm publish):
22
- <script src="https://cdn.jsdelivr.net/npm/jsonata/jsonata.min.js"></script>
23
- <script src="https://cdn.jsdelivr.net/npm/yaml-flow@2.6.0/browser/card-compute.js"></script>
24
- <script src="https://cdn.jsdelivr.net/npm/yaml-flow@2.6.0/browser/live-cards.js"></script>
25
- Local dev: references ../../browser/ relative path
26
- -->
27
-
28
- <!-- JSONata — expression engine (required by card-compute.js) -->
29
- <script src="https://cdn.jsdelivr.net/npm/jsonata/jsonata.min.js"></script>
30
-
31
- <!-- CardCompute (JSONata-powered compute engine) -->
32
- <script src="../../../browser/card-compute.js"></script>
33
-
34
- <!-- LiveCards v3 engine -->
35
- <script src="../../../browser/live-cards.js"></script>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>LiveCards Browser Demo Moved</title>
7
+ <meta http-equiv="refresh" content="0; url=../../example-board/demo-shell.html" />
8
+ <style>
9
+ body {
10
+ font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
11
+ background: #f5f1e8;
12
+ color: #1f2937;
13
+ margin: 0;
14
+ min-height: 100vh;
15
+ display: grid;
16
+ place-items: center;
17
+ padding: 24px;
18
+ }
19
+
20
+ main {
21
+ max-width: 680px;
22
+ background: #fffdf8;
23
+ border: 1px solid #d6cdbd;
24
+ border-radius: 16px;
25
+ padding: 28px;
26
+ box-shadow: 0 20px 50px rgba(56, 46, 33, 0.08);
27
+ }
28
+
29
+ a {
30
+ color: #8a3b12;
31
+ }
32
+ </style>
36
33
  </head>
37
- <body class="bg-light">
38
-
39
- <!-- ====================================================================
40
- API Reference (collapsible)
41
- ==================================================================== -->
42
- <div class="container-fluid py-3">
43
-
44
- <details class="mb-4">
45
- <summary class="h5 mb-0" style="cursor:pointer">LiveCards v3 — API Reference</summary>
46
- <div class="row g-4 mt-2">
47
-
48
- <!-- ---- Node Schema ---- -->
49
- <div class="col-12">
50
- <div class="card">
51
- <div class="card-header fw-bold">Node Schema</div>
52
- <div class="card-body small">
53
- <p>Every node shares a common shape. Two types:</p>
54
- <table class="table table-sm table-bordered mb-2">
55
- <thead><tr><th>Key</th><th>Card (<code>type:"card"</code>)</th><th>Source (<code>type:"source"</code>)</th></tr></thead>
56
- <tbody>
57
- <tr><td><code>id</code></td><td colspan="2">Unique string identifier</td></tr>
58
- <tr><td><code>meta</code></td><td colspan="2"><code>{ title, tags[] }</code></td></tr>
59
- <tr><td><code>state</code></td><td colspan="2">Mutable persisted state object. Element binds read from <code>state.*</code></td></tr>
60
- <tr><td><code>requires</code></td><td colspan="2"><code>[nodeId, ...]</code> — upstream dependencies, engine auto-subscribes</td></tr>
61
- <tr><td><code>provides</code></td><td colspan="2"><code>[{ bindTo, src }]</code> — explicit downstream token bindings. <code>src</code> is a <code>state.*</code> / <code>computed_values.*</code> path</td></tr>
62
- <tr><td><code>sources</code></td><td colspan="2"><code>[{ bindTo, outputFile, script?, optional?, kind?, ... }]</code> — data sources. <code>optional: true</code> sources don’t gate completion; absence of <code>outputFile</code> = undelivered.</td></tr>
63
- <tr><td><code>compute</code></td><td colspan="2"><code>[{ bindTo, expr }]</code> — ordered JSONata steps writing to ephemeral <code>computed_values[bindTo]</code>. Never persisted.</td></tr>
64
- <tr><td><code>view</code></td><td colspan="2"><code>{ elements[], layout, features }</code> — present on renderable cards; absent on source-only nodes</td></tr>
65
- </tbody>
66
- </table>
67
- </div>
68
- </div>
69
- </div>
70
-
71
- <!-- ---- LiveCard Engine API ---- -->
72
- <div class="col-12 col-lg-6">
73
- <div class="card h-100">
74
- <div class="card-header fw-bold">LiveCard — Engine API</div>
75
- <div class="card-body small">
76
- <pre class="mb-0"><code>const engine = LiveCard.init({
77
- resolve: (id) =&gt; node, // look up node by id
78
- onPatch: (id, data) =&gt; {}, // persist config changes
79
- onPatchState: (id, data) =&gt; {}, // persist state changes
80
- onRefresh: (id) =&gt; {}, // trigger external fetch
81
- onChat: (id, msg) =&gt; {}, // send chat message
82
- markdown: (text) =&gt; html, // e.g. marked.parse
83
- sanitize: (html) =&gt; safe, // e.g. DOMPurify.sanitize
84
- chartLib: Chart, // Chart.js constructor
85
- });
86
-
87
- engine.render(node, el, opts?) // render a card node
88
- engine.update(nodeId, patch) // in-place update (status, data)
89
- engine.destroy(nodeId) // tear down one node
90
- engine.destroyAll() // tear down all
91
- engine.notify(nodeId, data?) // signal change → downstream
92
- engine.subscribe(nodeId, cb) // listen; returns unsub fn
93
- engine.appendChatMessage(id, role, text)
94
- engine.registerRenderer(name, fn) // add custom element kind</code></pre>
95
- </div>
96
- </div>
97
- </div>
98
-
99
- <!-- ---- Board API ---- -->
100
- <div class="col-12 col-lg-6">
101
- <div class="card h-100">
102
- <div class="card-header fw-bold">LiveCard.Board — Layout API</div>
103
- <div class="card-body small">
104
- <pre class="mb-0"><code>const board = LiveCard.Board(engine, el, {
105
- nodes: [...], // Card + Source nodes
106
- positions: { id: {x,y,w} },// canvas overrides for sources
107
- mode: 'board', // 'board' (grid) | 'canvas' (DAG)
108
- showNotes: true,
109
- showChat: true,
110
- defaultCol: 6, // Bootstrap col width fallback
111
- canvas: {
112
- snap: 20, edges: true,
113
- zoom: { min: 0.25, max: 2, initial: 1 },
114
- height: '600px', overflow: 'auto',
115
- minWidth: 220, maxWidth: 450, defaultW: 350,
116
- gapX: 280, gapY: 320, padX: 20, padY: 20,
117
- cardMaxH: 300,
118
- },
119
- });
120
-
121
- board.setMode('board' | 'canvas')
122
- board.autoLayout() // topological L→R arrange
123
- board.add(node) // add a node dynamically
124
- board.remove(nodeId) // remove + destroy
125
- board.reorder([ids]) // reorder in board mode
126
- board.refresh() // re-render current mode
127
- board.clear() // destroy all, empty board
128
- board.destroy() // full teardown + DOM removal
129
- board.mode // current mode (getter)
130
- board.nodes // shallow copy of node list</code></pre>
131
- </div>
132
- </div>
133
- </div>
134
-
135
- <!-- ---- Element Kinds ---- -->
136
- <div class="col-12 col-lg-6">
137
- <div class="card h-100">
138
- <div class="card-header fw-bold">Element Kinds (view.elements[])</div>
139
- <div class="card-body small">
140
- <table class="table table-sm table-bordered mb-2">
141
- <thead><tr><th>Kind</th><th>Key <code>data.*</code> options</th></tr></thead>
142
- <tbody>
143
- <tr><td><code>table</code></td><td><code>bind, columns[], maxRows, sortable, placeholder</code></td></tr>
144
- <tr><td><code>filter</code></td><td><code>bind, writeTo, fields</code></td></tr>
145
- <tr><td><code>metric</code></td><td><code>bind</code></td></tr>
146
- <tr><td><code>list</code></td><td><code>bind, maxRows, placeholder</code></td></tr>
147
- <tr><td><code>chart</code></td><td><code>bind, chartType, chartOptions</code></td></tr>
148
- <tr><td><code>form</code></td><td><code>writeTo, fields (JSON Schema)</code></td></tr>
149
- <tr><td><code>notes</code></td><td><code>bind, writeTo</code></td></tr>
150
- <tr><td><code>todo</code></td><td><code>bind, writeTo</code></td></tr>
151
- <tr><td><code>alert</code></td><td><code>bind, thresholds: { green, amber }</code></td></tr>
152
- <tr><td><code>narrative</code></td><td><code>bind</code></td></tr>
153
- <tr><td><code>badge</code></td><td><code>bind, colorMap</code></td></tr>
154
- <tr><td><code>text</code></td><td><code>bind, style: heading|muted|default</code></td></tr>
155
- <tr><td><code>markdown</code></td><td><code>bind</code></td></tr>
156
- <tr><td><code>custom</code></td><td><code>bind</code> (renders JSON)</td></tr>
157
- </tbody>
158
- </table>
159
- <p class="mb-0">Common props: <code>kind, label, className, visible, data.bind, data.writeTo</code></p>
160
- </div>
161
- </div>
162
- </div>
163
-
164
- <!-- ---- CardCompute API ---- -->
165
- <div class="col-12 col-lg-6">
166
- <div class="card h-100">
167
- <div class="card-header fw-bold">CardCompute — Compute API</div>
168
- <div class="card-body small">
169
- <pre><code>await CardCompute.run(node) // eval all node.compute → computed_values
170
- CardCompute.eval(expr, node) // eval single expression
171
- CardCompute.resolve(node, path) // deep-get "state.foo.bar"
172
- CardCompute.registerFunction(name, fn) // add custom fn</code></pre>
173
- <p class="fw-medium mb-1">53 built-in functions:</p>
174
- <table class="table table-sm table-bordered mb-2">
175
- <tbody>
176
- <tr><td class="text-muted">Aggregates</td><td><code>sum avg min max count first last</code></td></tr>
177
- <tr><td class="text-muted">Math</td><td><code>add sub mul div round abs mod</code></td></tr>
178
- <tr><td class="text-muted">Compare</td><td><code>gt gte lt lte eq neq</code></td></tr>
179
- <tr><td class="text-muted">Logic</td><td><code>and or not if</code></td></tr>
180
- <tr><td class="text-muted">String</td><td><code>concat upper lower template join split trim</code></td></tr>
181
- <tr><td class="text-muted">Collection</td><td><code>pluck filter map sort slice flat unique group flatten_keys entries from_entries length</code></td></tr>
182
- <tr><td class="text-muted">Lookup</td><td><code>get default coalesce</code></td></tr>
183
- <tr><td class="text-muted">Date</td><td><code>now diff_days format_date parse_date</code></td></tr>
184
- <tr><td class="text-muted">Type</td><td><code>to_number to_string to_bool type_of is_null is_empty</code></td></tr>
185
- </tbody>
186
- </table>
187
- <pre class="mb-0"><code>// JSONata expression — computed against { state, requires, computed_values }
188
- { "bindTo": "avg_revenue",
189
- "expr": "$round($average(state.sales.revenue), 2)" }</code></pre>
190
- </div>
191
- </div>
192
- </div>
193
-
194
- <!-- ---- Files ---- -->
195
- <div class="col-12">
196
- <div class="card">
197
- <div class="card-header fw-bold">Files</div>
198
- <div class="card-body small">
199
- <table class="table table-sm mb-0">
200
- <tbody>
201
- <tr><td><code>live-cards.js</code></td><td>Rendering engine + Board layout (browser, no dependencies except Bootstrap 5 CSS)</td></tr>
202
- <tr><td><code>card-compute.js</code></td><td>Isomorphic compute module — UMD (browser global / Node.js / AMD). No DOM needed.</td></tr>
203
- <tr><td><code>live-cards.schema.json</code></td><td>JSON Schema (draft-07) for Card and ExternalSource nodes</td></tr>
204
- <tr><td><code>index.html</code></td><td>This demo page — all element kinds + ExternalSource nodes</td></tr>
205
- </tbody>
206
- </table>
207
- <p class="mt-2 mb-0">Optional libs: <code>Chart.js</code> (charts), <code>marked</code> (markdown), <code>DOMPurify</code> (sanitize). Pass via <code>init(config)</code>.</p>
208
- </div>
209
- </div>
210
- </div>
211
-
212
- <div class="col-12">
213
- <div class="card">
214
- <div class="card-header fw-bold">Related Docs</div>
215
- <div class="card-body small">
216
- <p class="mb-2">This page focuses on the LiveCards browser runtime. Broader yaml-flow architecture and non-LiveCards APIs live in the main docs.</p>
217
- <ul class="mb-0">
218
- <li><a href="../../../docs/index.html">yaml-flow docs index</a> — framework modules and product surfaces</li>
219
- <li><a href="../../../docs/browser-runtime-livecards-boards.html">browser runtime docs</a> — setup and browser integration details</li>
220
- <li><a href="../../../docs/board-live-cards-cli.html">board-live-cards CLI reference</a> — server/runtime orchestration contracts</li>
221
- <li><a href="../../index.html">examples index</a> — broader example catalog across yaml-flow</li>
222
- </ul>
223
- </div>
224
- </div>
225
- </div>
226
-
227
- </div>
228
- </details>
229
-
230
- <!-- ====================================================================
231
- Board Demo
232
- ==================================================================== -->
233
- <div class="d-flex align-items-center gap-3 mb-3">
234
- <h4 class="mb-0">LiveCards v3 — Node-based Board</h4>
235
- <div class="btn-group btn-group-sm ms-auto" id="modeToggle">
236
- <button class="btn btn-primary" data-mode="board">Board</button>
237
- <button class="btn btn-outline-primary" data-mode="canvas">Canvas</button>
238
- </div>
239
- <button class="btn btn-sm btn-outline-secondary" id="autoLayoutBtn" style="display:none">Auto Layout</button>
240
- </div>
241
- <div id="boardRoot"></div>
242
- </div>
243
-
244
- <script>
245
- (function () {
246
- 'use strict';
247
-
248
- // =========================================================================
249
- // Node definitions (v3 schema: id, type, meta, data, view, source, state, compute)
250
- // =========================================================================
251
-
252
- var nodes = {};
253
-
254
- // ---- ExternalSource nodes (data-only, no view) ----
255
-
256
- nodes['src-sales'] = {
257
- id: 'src-sales',
258
- meta: { title: 'Sales API', tags: ['api', 'sales'] },
259
- provides: [{ bindTo: 'sales', src: 'state.raw' }],
260
- sources: [{
261
- bindTo: 'raw',
262
- kind: 'api',
263
- url_template: 'https://api.example.com/v1/sales?region={{region}}',
264
- template_vars: { region: 'all' },
265
- poll_interval: 300,
266
- transform: 'data.items'
267
- }],
268
- state: {
269
- status: 'fresh',
270
- lastRun: new Date().toISOString(),
271
- raw: [
272
- { region: 'North', product: 'Widget A', revenue: 12400, units: 310 },
273
- { region: 'South', product: 'Widget B', revenue: 8700, units: 220 },
274
- { region: 'East', product: 'Widget A', revenue: 15200, units: 380 },
275
- { region: 'West', product: 'Widget C', revenue: 6300, units: 125 },
276
- { region: 'North', product: 'Widget B', revenue: 9100, units: 230 },
277
- ]
278
- }
279
- };
280
-
281
- nodes['src-metrics'] = {
282
- id: 'src-metrics',
283
- meta: { title: 'Metrics API', tags: ['api', 'monitoring'] },
284
- provides: [{ bindTo: 'metrics', src: 'state.raw' }],
285
- sources: [{
286
- bindTo: 'raw',
287
- kind: 'api',
288
- url_template: 'https://api.example.com/v1/metrics',
289
- poll_interval: 60
290
- }],
291
- state: {
292
- status: 'fresh',
293
- lastRun: new Date().toISOString(),
294
- raw: { cpu: 72, memory: 45, disk: 38, latency_p50: 12, latency_p95: 45 }
295
- }
296
- };
297
-
298
- // ---- Card nodes ----
299
-
300
- nodes['flt1'] = {
301
- id: 'flt1',
302
- meta: { title: 'Filters' },
303
- view: {
304
- elements: [{
305
- kind: 'filter',
306
- className: 'col-12',
307
- data: {
308
- bind: 'state.options',
309
- writeTo: 'state.selected',
310
- fields: { properties: { region: { title: 'Region' }, product: { title: 'Product' } } }
311
- }
312
- }],
313
- layout: { board: { col: 12, order: 0 }, canvas: { x: 40, y: 40, w: 400 } }
314
- },
315
- state: {
316
- status: 'fresh',
317
- options: { region: ['North','South','East','West'], product: ['Widget A','Widget B','Widget C'] },
318
- selected: {}
319
- }
320
- };
321
-
322
- nodes['tbl1'] = {
323
- id: 'tbl1',
324
- meta: { title: 'Sales Data', tags: ['table'] },
325
- requires: ['src-sales'],
326
- view: {
327
- elements: [{
328
- kind: 'table',
329
- className: 'col-12',
330
- data: { bind: 'state.sales', sortable: true }
331
- }],
332
- layout: { board: { col: 8, order: 1 }, canvas: { x: 500, y: 40, w: 450 } },
333
- features: { refresh: true }
334
- },
335
- state: {
336
- status: 'fresh',
337
- lastRun: new Date().toISOString(),
338
- sales: [
339
- { region: 'North', product: 'Widget A', revenue: 12400, units: 310 },
340
- { region: 'South', product: 'Widget B', revenue: 8700, units: 220 },
341
- { region: 'East', product: 'Widget A', revenue: 15200, units: 380 },
342
- { region: 'West', product: 'Widget C', revenue: 6300, units: 125 },
343
- { region: 'North', product: 'Widget B', revenue: 9100, units: 230 },
344
- ]
345
- }
346
- };
347
-
348
- nodes['met1'] = {
349
- id: 'met1',
350
- meta: { title: 'Revenue KPIs' },
351
- requires: ['tbl1'],
352
- compute: [
353
- { bindTo: 'total_revenue', expr: '$sum(state.sales.revenue)' },
354
- { bindTo: 'avg_revenue', expr: '$round($average(state.sales.revenue), 0)' }
355
- ],
356
- view: {
357
- elements: [
358
- { kind: 'metric', label: 'Total Revenue', className: 'col-6', data: { bind: 'computed_values.total_revenue' } },
359
- { kind: 'metric', label: 'Avg per Region', className: 'col-6', data: { bind: 'computed_values.avg_revenue' } }
360
- ],
361
- layout: { board: { col: 4, order: 2 }, canvas: { x: 500, y: 380, w: 300 } }
362
- },
363
- state: {
364
- status: 'fresh',
365
- sales: [
366
- { region: 'North', product: 'Widget A', revenue: 12400, units: 310 },
367
- { region: 'South', product: 'Widget B', revenue: 8700, units: 220 },
368
- { region: 'East', product: 'Widget A', revenue: 15200, units: 380 },
369
- { region: 'West', product: 'Widget C', revenue: 6300, units: 125 },
370
- { region: 'North', product: 'Widget B', revenue: 9100, units: 230 },
371
- ]
372
- }
373
- };
374
-
375
- nodes['cht1'] = {
376
- id: 'cht1',
377
- meta: { title: 'Revenue by Region' },
378
- requires: ['tbl1'],
379
- view: {
380
- elements: [{
381
- kind: 'chart',
382
- className: 'col-12',
383
- data: { bind: 'state.chartData' }
384
- }],
385
- layout: { board: { col: 6, order: 3 }, canvas: { x: 40, y: 360, w: 400 } }
386
- },
387
- state: {
388
- status: 'fresh',
389
- chartData: [
390
- { label: 'North', value: 21500 },
391
- { label: 'South', value: 8700 },
392
- { label: 'East', value: 15200 },
393
- { label: 'West', value: 6300 },
394
- ]
395
- }
396
- };
397
-
398
- nodes['cht2'] = {
399
- id: 'cht2',
400
- meta: { title: 'Weekly Trend' },
401
- view: {
402
- elements: [{
403
- kind: 'chart',
404
- className: 'col-12',
405
- data: { bind: 'state.trend', chartType: 'line' }
406
- }],
407
- layout: { board: { col: 6, order: 4 }, canvas: { x: 860, y: 380, w: 400 } }
408
- },
409
- state: {
410
- status: 'fresh',
411
- trend: [
412
- { x: 'Mon', y: 120 }, { x: 'Tue', y: 190 }, { x: 'Wed', y: 150 },
413
- { x: 'Thu', y: 210 }, { x: 'Fri', y: 280 }, { x: 'Sat', y: 170 }, { x: 'Sun', y: 90 },
414
- ]
415
- }
416
- };
417
-
418
- nodes['lst1'] = {
419
- id: 'lst1',
420
- meta: { title: 'Config Summary' },
421
- view: {
422
- elements: [{
423
- kind: 'list',
424
- className: 'col-12',
425
- data: { bind: 'state.config' }
426
- }],
427
- layout: { board: { col: 4, order: 5 }, canvas: { x: 1000, y: 40, w: 280 } }
428
- },
429
- state: {
430
- status: 'fresh',
431
- config: { Environment: 'Production', Region: 'US-West-2', Version: '3.4.1', Replicas: 3 }
432
- }
433
- };
434
-
435
- nodes['alt1'] = {
436
- id: 'alt1',
437
- meta: { title: 'CPU Alert' },
438
- requires: ['src-metrics'],
439
- view: {
440
- elements: [{
441
- kind: 'alert',
442
- label: 'CPU %',
443
- className: 'col-12',
444
- data: { bind: 'state.cpu', thresholds: { green: '< 60', amber: '< 85' } }
445
- }],
446
- layout: { board: { col: 4, order: 6 }, canvas: { x: 40, y: 680 } }
447
- },
448
- state: { status: 'fresh', cpu: 72 }
449
- };
450
-
451
- nodes['alt2'] = {
452
- id: 'alt2',
453
- meta: { title: 'Memory Alert' },
454
- requires: ['src-metrics'],
455
- view: {
456
- elements: [{
457
- kind: 'alert',
458
- label: 'Memory %',
459
- className: 'col-12',
460
- data: { bind: 'state.memory', thresholds: { green: '< 70', amber: '< 90' } }
461
- }],
462
- layout: { board: { col: 4, order: 7 }, canvas: { x: 340, y: 680 } }
463
- },
464
- state: { status: 'fresh', memory: 45 }
465
- };
466
-
467
- nodes['frm1'] = {
468
- id: 'frm1',
469
- meta: { title: 'New Alert Rule' },
470
- view: {
471
- elements: [{
472
- kind: 'form',
473
- className: 'col-12',
474
- data: {
475
- writeTo: 'state.formValues',
476
- fields: {
477
- properties: {
478
- name: { type: 'string', title: 'Rule Name', placeholder: 'e.g. High CPU' },
479
- metric: { type: 'string', title: 'Metric', enum: ['cpu_percent','memory_mb','disk_io','latency_ms'] },
480
- threshold: { type: 'number', title: 'Threshold', minimum: 0, maximum: 100 },
481
- severity: { type: 'string', title: 'Severity', enum: ['low','medium','high','critical'] },
482
- enabled: { type: 'boolean', title: 'Enabled' },
483
- },
484
- required: ['name', 'metric', 'threshold'],
485
- }
486
- }
487
- }],
488
- layout: { board: { col: 6, order: 8 }, canvas: { x: 640, y: 680, w: 380 } }
489
- },
490
- state: { status: 'fresh', formValues: { enabled: true } }
491
- };
492
-
493
- nodes['nts1'] = {
494
- id: 'nts1',
495
- meta: { title: 'Investigation Notes' },
496
- view: {
497
- elements: [{
498
- kind: 'notes',
499
- className: 'col-12',
500
- data: { bind: 'state.content', writeTo: 'state.content' }
501
- }],
502
- layout: { board: { col: 6, order: 9 }, canvas: { x: 1060, y: 680, w: 380 } }
503
- },
504
- state: {
505
- status: 'fresh',
506
- content: '## Incident 4821\n\n- Root cause: **memory leak** in worker pool\n- Mitigated at 14:32 UTC\n- Follow-up: capacity test scheduled Friday'
507
- }
508
- };
509
-
510
- nodes['td1'] = {
511
- id: 'td1',
512
- meta: { title: 'Sprint Tasks' },
513
- view: {
514
- elements: [{
515
- kind: 'todo',
516
- className: 'col-12',
517
- data: { bind: 'state.items', writeTo: 'state.items' }
518
- }],
519
- layout: { board: { col: 6, order: 10 }, canvas: { x: 40, y: 960, w: 350 } }
520
- },
521
- state: {
522
- status: 'fresh',
523
- items: [
524
- { text: 'Finish LiveCards v3 engine', done: true },
525
- { text: 'Write integration tests', done: false },
526
- { text: 'Update overlay to use new engine', done: false },
527
- { text: 'Deploy to staging', done: false },
528
- ]
529
- }
530
- };
531
-
532
- nodes['nar1'] = {
533
- id: 'nar1',
534
- meta: { title: 'Weekly Summary', tags: ['llm'] },
535
- requires: ['tbl1', 'met1'],
536
- view: {
537
- elements: [{
538
- kind: 'narrative',
539
- className: 'col-12',
540
- data: { bind: 'state.narrative' }
541
- }],
542
- layout: { board: { col: 12, order: 11 }, canvas: { x: 440, y: 960, w: 500 } },
543
- features: { refresh: true, chat: true }
544
- },
545
- state: {
546
- status: 'fresh',
547
- lastRun: new Date(Date.now() - 3600000).toISOString(),
548
- narrative: '### Week 16 Summary\n\nRevenue grew **18%** week-over-week driven by the North region. Widget A continues to lead with 690 units sold.\n\n> Action: Consider increasing inventory allocation for Widget A in East region where demand is highest.'
549
- }
550
- };
551
-
552
- nodes['cst1'] = {
553
- id: 'cst1',
554
- meta: { title: 'Service Health' },
555
- view: {
556
- elements: [
557
- { kind: 'badge', className: 'col-6', label: 'Status', data: { bind: 'state.health.status', colorMap: { healthy: 'green', degraded: 'amber', down: 'red' } } },
558
- { kind: 'metric', className: 'col-6', label: 'Uptime', data: { bind: 'state.health.uptime' } },
559
- { kind: 'list', className: 'col-12 col-md-6', label: 'Latency', data: { bind: 'state.health.latency' } },
560
- { kind: 'table', className: 'col-12 col-md-6', label: 'Recent', data: { bind: 'state.health.recent', columns: ['time','requests','errors'] } },
561
- ],
562
- layout: { board: { col: 12, order: 12 }, canvas: { x: 40, y: 1260, w: 500 } }
563
- },
564
- state: {
565
- status: 'fresh',
566
- health: {
567
- status: 'healthy',
568
- uptime: '99.97%',
569
- latency: { p50: 12, p95: 45, p99: 120 },
570
- recent: [
571
- { time: '14:00', requests: 1240, errors: 2 },
572
- { time: '14:05', requests: 1380, errors: 0 },
573
- { time: '14:10', requests: 1100, errors: 1 },
574
- ]
575
- }
576
- }
577
- };
578
-
579
- nodes['load1'] = {
580
- id: 'load1',
581
- meta: { title: 'Loading Example' },
582
- view: {
583
- elements: [{
584
- kind: 'table',
585
- className: 'col-12',
586
- data: { bind: 'state.data', placeholder: 'Fetching data...' }
587
- }],
588
- layout: { board: { col: 6, order: 13 }, canvas: { x: 600, y: 1260, w: 350 } },
589
- features: { refresh: true }
590
- },
591
- state: { status: 'loading', lastRun: new Date().toISOString() }
592
- };
593
-
594
- nodes['err1'] = {
595
- id: 'err1',
596
- meta: { title: 'Error Example' },
597
- view: {
598
- elements: [{
599
- kind: 'narrative',
600
- className: 'col-12',
601
- data: { bind: 'state.text' }
602
- }],
603
- layout: { board: { col: 6, order: 14 }, canvas: { x: 1000, y: 1260, w: 350 } },
604
- features: { refresh: true }
605
- },
606
- state: {
607
- status: 'error',
608
- lastRun: new Date().toISOString(),
609
- error: 'LLM timeout after 30s — model overloaded, retry in 60s'
610
- }
611
- };
612
-
613
- // =========================================================================
614
- // Init engine
615
- // =========================================================================
616
-
617
- var engine = LiveCard.init({
618
- resolve: function (id) { return nodes[id]; },
619
- onPatch: function (id, data) { Object.assign(nodes[id], data); console.log('patch', id, data); },
620
- onPatchState: function (id, data) { Object.assign(nodes[id].state, data); console.log('patchState', id, data); },
621
- onRefresh: function (id) {
622
- console.log('refresh', id);
623
- engine.update(id, { status: 'loading' });
624
- setTimeout(function () {
625
- nodes[id].state.status = 'fresh';
626
- nodes[id].state.lastRun = new Date().toISOString();
627
- engine.update(id, { status: 'fresh', lastRun: nodes[id].state.lastRun });
628
- }, 1500);
629
- },
630
- onChat: function (id, msg) {
631
- console.log('chat', id, msg);
632
- setTimeout(function () {
633
- engine.appendChatMessage(id, 'assistant', 'This is a simulated response to: *' + msg + '*');
634
- }, 800);
635
- },
636
- markdown: function (t) { return marked.parse(t); },
637
- sanitize: function (h) { return DOMPurify.sanitize(h); },
638
- chartLib: Chart,
639
- });
640
-
641
- // =========================================================================
642
- // Create board — pass all nodes including sources
643
- // =========================================================================
644
-
645
- var board = LiveCard.Board(engine, document.getElementById('boardRoot'), {
646
- nodes: Object.values(nodes),
647
- positions: {
648
- 'src-sales': { x: 200, y: 0 },
649
- 'src-metrics': { x: 200, y: 80 },
650
- },
651
- mode: 'board',
652
- showNotes: true,
653
- showChat: true,
654
- canvas: { snap: 20, zoom: { min: 0.25, max: 2, initial: 0.75 }, edges: true, height: '1800px' },
655
- });
656
-
657
- // =========================================================================
658
- // Mode toggle
659
- // =========================================================================
660
-
661
- var toggle = document.getElementById('modeToggle');
662
- var autoBtn = document.getElementById('autoLayoutBtn');
663
- toggle.addEventListener('click', function (e) {
664
- var btn = e.target.closest('[data-mode]');
665
- if (!btn) return;
666
- var m = btn.dataset.mode;
667
- board.setMode(m);
668
- toggle.querySelectorAll('button').forEach(function (b) {
669
- b.className = b.dataset.mode === m ? 'btn btn-primary' : 'btn btn-outline-primary';
670
- });
671
- autoBtn.style.display = m === 'canvas' ? '' : 'none';
672
- });
673
- autoBtn.addEventListener('click', function () { board.autoLayout(); });
674
-
675
- // Expose for console testing
676
- window._board = board;
677
- window._engine = engine;
678
- window._nodes = nodes;
679
- console.log('LiveCards v3 Board demo ready.');
680
- console.log(' _board.setMode("canvas") — switch to canvas (shows source nodes)');
681
- console.log(' _board.autoLayout() — auto-arrange DAG');
682
- console.log(' _board.add(node) — add a node');
683
- console.log(' _board.remove("tbl1") — remove a node');
684
- console.log(' await CardCompute.eval("$sum(state.sales.revenue)", _nodes["met1"])');
685
- })();
686
- </script>
34
+ <body>
35
+ <main>
36
+ <h1>LiveCards browser demo moved</h1>
37
+ <p>The maintained browser demo is now the example-board shell so there is a single LiveCards example path to keep current.</p>
38
+ <p><a href="../../example-board/demo-shell.html">Open the example-board demo shell</a></p>
39
+ </main>
687
40
  </body>
688
- </html>
41
+ </html>