yaml-flow 4.0.0 → 5.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/browser/board-livegraph-runtime.js +1453 -0
- package/browser/board-livegraph-runtime.js.map +1 -0
- package/browser/card-compute.js +36 -17
- package/browser/live-cards.js +848 -109
- package/browser/live-cards.schema.json +46 -21
- package/dist/board-livegraph-runtime/index.cjs +1448 -0
- package/dist/board-livegraph-runtime/index.cjs.map +1 -0
- package/dist/board-livegraph-runtime/index.d.cts +101 -0
- package/dist/board-livegraph-runtime/index.d.ts +101 -0
- package/dist/board-livegraph-runtime/index.js +1441 -0
- package/dist/board-livegraph-runtime/index.js.map +1 -0
- package/dist/card-compute/index.cjs +159 -44
- package/dist/card-compute/index.cjs.map +1 -1
- package/dist/card-compute/index.d.cts +36 -11
- package/dist/card-compute/index.d.ts +36 -11
- package/dist/card-compute/index.js +156 -44
- package/dist/card-compute/index.js.map +1 -1
- package/dist/cli/board-live-cards-cli.cjs +476 -105
- package/dist/cli/board-live-cards-cli.cjs.map +1 -1
- package/dist/cli/board-live-cards-cli.d.cts +8 -16
- package/dist/cli/board-live-cards-cli.d.ts +8 -16
- package/dist/cli/board-live-cards-cli.js +476 -106
- package/dist/cli/board-live-cards-cli.js.map +1 -1
- package/dist/continuous-event-graph/index.cjs +74 -33
- package/dist/continuous-event-graph/index.cjs.map +1 -1
- package/dist/continuous-event-graph/index.d.cts +7 -23
- package/dist/continuous-event-graph/index.d.ts +7 -23
- package/dist/continuous-event-graph/index.js +73 -32
- package/dist/continuous-event-graph/index.js.map +1 -1
- package/dist/index.cjs +1440 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -3
- package/dist/index.d.ts +21 -3
- package/dist/index.js +1434 -56
- package/dist/index.js.map +1 -1
- package/dist/journal-DRfJiheM.d.cts +28 -0
- package/dist/journal-NLYuqege.d.ts +28 -0
- package/dist/{journal-B_2JnBMF.d.ts → live-cards-bridge-Or7fdEJV.d.ts} +5 -32
- package/dist/{journal-BJDjWb5Q.d.cts → live-cards-bridge-vGJ6tMzN.d.cts} +5 -32
- package/dist/schedule-CMcZe5Ny.d.ts +21 -0
- package/dist/schedule-CiucyCan.d.cts +21 -0
- package/examples/browser/boards/portfolio-tracker/cards/holdings-table.json +1 -1
- package/examples/browser/boards/portfolio-tracker/cards/portfolio-form.json +3 -3
- package/examples/browser/boards/portfolio-tracker/cards/portfolio-value.json +1 -1
- package/examples/browser/boards/portfolio-tracker/cards/price-fetch.json +3 -3
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker-task-executor.cjs +96 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker.js +33 -5
- package/examples/browser/livecards-browser/index.html +37 -684
- package/examples/cli/step-machine-cli/portfolio-tracker/cards/holdings-table.json +1 -1
- package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +3 -3
- package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +1 -1
- package/examples/cli/step-machine-cli/portfolio-tracker/cards/price-fetch.json +3 -3
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +2 -2
- package/examples/example-board/board.yaml +23 -0
- package/examples/example-board/bootstrap_payload.json +1 -0
- package/examples/example-board/cards/card-chain-region-alert.json +39 -0
- package/examples/example-board/cards/card-chain-region-totals.json +26 -0
- package/examples/example-board/cards/card-chain-top-region.json +24 -0
- package/examples/example-board/cards/card-ex-actions.json +32 -0
- package/examples/example-board/cards/card-ex-chart.json +30 -0
- package/examples/example-board/cards/card-ex-filter.json +36 -0
- package/examples/example-board/cards/card-ex-filtered-by-preference.json +59 -0
- package/examples/example-board/cards/card-ex-form.json +91 -0
- package/examples/example-board/cards/card-ex-list.json +22 -0
- package/examples/example-board/cards/card-ex-markdown.json +17 -0
- package/examples/example-board/cards/card-ex-metric.json +19 -0
- package/examples/example-board/cards/card-ex-narrative.json +36 -0
- package/examples/example-board/cards/card-ex-source-http.json +28 -0
- package/examples/example-board/cards/card-ex-source.json +21 -0
- package/examples/example-board/cards/card-ex-status.json +35 -0
- package/examples/example-board/cards/card-ex-table.json +30 -0
- package/examples/example-board/cards/card-ex-todo.json +29 -0
- package/examples/example-board/demo-chat-handler.js +69 -0
- package/examples/example-board/demo-server.js +87 -0
- package/examples/example-board/demo-shell-browser.html +806 -0
- package/examples/example-board/demo-shell-with-server.html +280 -0
- package/examples/example-board/demo-shell.html +62 -0
- package/examples/example-board/demo-task-executor.js +255 -0
- package/examples/example-board/mock.db +15 -0
- package/examples/example-board/reusable-board-runtime-client.js +265 -0
- package/examples/example-board/reusable-runtime-artifacts-adapter.js +233 -0
- package/examples/example-board/reusable-server-runtime.js +1284 -0
- package/examples/index.html +16 -9
- package/examples/npm-libs/continuous-event-graph/live-cards-board.ts +17 -17
- package/examples/npm-libs/continuous-event-graph/live-portfolio-dashboard.ts +23 -23
- package/examples/step-machine-cli/portfolio-tracker/cards/holdings-table.json +1 -1
- package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +3 -3
- package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +1 -1
- package/examples/step-machine-cli/portfolio-tracker/cards/price-fetch.json +1 -1
- package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker-task-executor.cjs +96 -0
- package/package.json +16 -2
- package/schema/card-runtime.schema.json +25 -0
- package/schema/live-cards.schema.json +46 -21
- package/browser/ingest-board.js +0 -296
- package/examples/ingest.js +0 -733
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"$id": "https://nsreehari.github.io/boards/live-cards.schema.json",
|
|
4
|
-
"title": "LiveCards Node Schema",
|
|
5
|
-
"description": "Schema for Card and ExternalSource nodes in the LiveCards Board/Canvas engine",
|
|
6
4
|
|
|
7
5
|
"definitions": {
|
|
8
6
|
|
|
9
7
|
"bind_ref": {
|
|
10
|
-
"description": "A
|
|
8
|
+
"description": "A card data path reference, e.g. 'card_data.raw_quotes' or 'requires.upstream'",
|
|
11
9
|
"type": "string",
|
|
12
|
-
"pattern": "^(
|
|
10
|
+
"pattern": "^(card_data|requires|fetched_sources|computed_values)(\\.|$)"
|
|
13
11
|
},
|
|
14
12
|
|
|
15
13
|
"bind_or_literal": {
|
|
@@ -46,7 +44,7 @@
|
|
|
46
44
|
]
|
|
47
45
|
},
|
|
48
46
|
"input": {
|
|
49
|
-
"description": "
|
|
47
|
+
"description": "card_data.path, literal, array of inputs, or nested compute_expr",
|
|
50
48
|
"oneOf": [
|
|
51
49
|
{ "type": "string" },
|
|
52
50
|
{ "type": "number" },
|
|
@@ -85,19 +83,19 @@
|
|
|
85
83
|
"required": ["bindTo", "src"],
|
|
86
84
|
"properties": {
|
|
87
85
|
"bindTo": { "type": "string", "description": "Token name published downstream" },
|
|
88
|
-
"src": { "type": "string", "description": "Path to read value from (
|
|
86
|
+
"src": { "type": "string", "description": "Path to read value from (card_data.*, requires.*, fetched_sources.*, computed_values.*)" }
|
|
89
87
|
}
|
|
90
88
|
},
|
|
91
|
-
"description": "Explicit bindings exposing computed
|
|
89
|
+
"description": "Explicit bindings exposing computed or card_data values downstream as named tokens"
|
|
92
90
|
},
|
|
93
91
|
|
|
94
92
|
"compute_step": {
|
|
95
|
-
"description": "A single ordered compute step: reads
|
|
93
|
+
"description": "A single ordered compute step: reads card_data.*/requires.*/computed_values.*, writes to computed_values[bindTo]",
|
|
96
94
|
"type": "object",
|
|
97
95
|
"required": ["bindTo", "expr"],
|
|
98
96
|
"properties": {
|
|
99
97
|
"bindTo": { "type": "string", "description": "Key in computed_values to write result" },
|
|
100
|
-
"expr": { "type": "string", "description": "JSONata expression evaluated against {
|
|
98
|
+
"expr": { "type": "string", "description": "JSONata expression evaluated against { card_data, requires, fetched_sources, computed_values }" }
|
|
101
99
|
}
|
|
102
100
|
},
|
|
103
101
|
|
|
@@ -107,7 +105,7 @@
|
|
|
107
105
|
"required": ["bindTo"],
|
|
108
106
|
"additionalProperties": true,
|
|
109
107
|
"properties": {
|
|
110
|
-
"bindTo": { "type": "string", "description": "Key under
|
|
108
|
+
"bindTo": { "type": "string", "description": "Key under fetched_sources.* available in compute expressions" },
|
|
111
109
|
"outputFile": { "type": "string", "description": "Board-relative path the executor writes its JSON result to. Presence of this file signals delivery." },
|
|
112
110
|
"optionalForCompletionGating": { "type": "boolean", "default": false, "description": "When true this source does not gate card completion. Default false when absent, so sources are completion-gating by default." },
|
|
113
111
|
"timeout": { "type": "integer", "minimum": 0, "default": 120000, "description": "Executor/script timeout in ms. Default: 120 000 (2 min)." },
|
|
@@ -123,16 +121,16 @@
|
|
|
123
121
|
"kind": {
|
|
124
122
|
"enum": ["metric", "table", "chart", "form", "filter", "list",
|
|
125
123
|
"notes", "todo", "alert", "narrative", "badge", "text",
|
|
126
|
-
"markdown", "custom", "
|
|
124
|
+
"markdown", "custom", "actions"]
|
|
127
125
|
},
|
|
128
126
|
"label": { "type": "string", "description": "Heading above this element" },
|
|
129
127
|
"className": { "type": "string", "description": "Bootstrap grid class, e.g. 'col-12 col-md-6'" },
|
|
130
|
-
"visible": { "type": "string", "description": "
|
|
128
|
+
"visible": { "type": "string", "description": "card_data/requires/fetched_sources/computed_values path — element shown only if truthy" },
|
|
131
129
|
"data": {
|
|
132
130
|
"type": "object",
|
|
133
131
|
"properties": {
|
|
134
|
-
"bind": { "$ref": "#/definitions/bind_ref", "description": "
|
|
135
|
-
"writeTo": { "$ref": "#/definitions/bind_ref", "description": "
|
|
132
|
+
"bind": { "$ref": "#/definitions/bind_ref", "description": "card_data/requires/fetched_sources/computed_values path to read data from" },
|
|
133
|
+
"writeTo": { "$ref": "#/definitions/bind_ref", "description": "card_data path for user input (form, filter, todo, notes)" },
|
|
136
134
|
"columns": { "type": "array", "items": { "type": "string" }, "description": "table: visible columns" },
|
|
137
135
|
"maxRows": { "type": "integer", "description": "table/list: max rows to display" },
|
|
138
136
|
"sortable": { "type": "boolean", "default": true, "description": "table: enable click-to-sort" },
|
|
@@ -165,7 +163,7 @@
|
|
|
165
163
|
"label": { "type": "string" },
|
|
166
164
|
"style": { "type": "string", "description": "Bootstrap button variant, e.g. 'success', 'outline-danger'" },
|
|
167
165
|
"size": { "type": "string", "default": "sm" },
|
|
168
|
-
"disabled": { "oneOf": [{ "type": "boolean" }, { "type": "string", "description": "
|
|
166
|
+
"disabled": { "oneOf": [{ "type": "boolean" }, { "type": "string", "description": "card_data/requires/fetched_sources/computed_values path — truthy = disabled" }] }
|
|
169
167
|
}
|
|
170
168
|
}
|
|
171
169
|
}
|
|
@@ -227,12 +225,39 @@
|
|
|
227
225
|
"provides": { "$ref": "#/definitions/provides" },
|
|
228
226
|
"meta": { "$ref": "#/definitions/meta" },
|
|
229
227
|
"view": { "$ref": "#/definitions/view" },
|
|
230
|
-
"
|
|
228
|
+
"card_data": {
|
|
229
|
+
"type": "object",
|
|
230
|
+
"description": "Authored card data supplied in the card definition",
|
|
231
231
|
"properties": {
|
|
232
|
-
"
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
232
|
+
"files": {
|
|
233
|
+
"type": "array",
|
|
234
|
+
"description": "Optional uploaded-file metadata maintained by host handlers. Stored name is normalized and serial-prefixed (for example 001-my_file.pdf).",
|
|
235
|
+
"items": {
|
|
236
|
+
"type": "object",
|
|
237
|
+
"required": ["name", "stored_name"],
|
|
238
|
+
"properties": {
|
|
239
|
+
"name": { "type": "string", "minLength": 1 },
|
|
240
|
+
"stored_name": {
|
|
241
|
+
"type": "string",
|
|
242
|
+
"minLength": 5,
|
|
243
|
+
"maxLength": 32,
|
|
244
|
+
"pattern": "^[0-9]{3,}-[a-z0-9._-]+$"
|
|
245
|
+
},
|
|
246
|
+
"size": {
|
|
247
|
+
"oneOf": [
|
|
248
|
+
{ "type": "integer", "minimum": 0 },
|
|
249
|
+
{ "type": "null" }
|
|
250
|
+
]
|
|
251
|
+
},
|
|
252
|
+
"mime_type": { "type": "string" },
|
|
253
|
+
"path": { "type": "string", "pattern": "^[^\\s]+/files/[0-9]{3,}-[a-z0-9._-]+$" },
|
|
254
|
+
"uploaded_at": { "type": "string", "format": "date-time" }
|
|
255
|
+
},
|
|
256
|
+
"additionalProperties": false
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
"additionalProperties": true
|
|
236
261
|
},
|
|
237
262
|
"sources": {
|
|
238
263
|
"type": "array",
|
|
@@ -241,7 +266,7 @@
|
|
|
241
266
|
},
|
|
242
267
|
"compute": {
|
|
243
268
|
"type": "array",
|
|
244
|
-
"description": "Ordered array of compute steps. Each reads
|
|
269
|
+
"description": "Ordered array of compute steps. Each reads card_data.*/requires.*/fetched_sources.*/computed_values.* and writes to ephemeral computed_values[bindTo].",
|
|
245
270
|
"items": { "$ref": "#/definitions/compute_step" }
|
|
246
271
|
}
|
|
247
272
|
}
|
package/browser/ingest-board.js
DELETED
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
// ingest-board.js — Ingest Board: a LiveCard Board type for batch ingest UIs
|
|
2
|
-
//
|
|
3
|
-
// Pure component. Zero I/O (no fetch, no EventSource, no polling).
|
|
4
|
-
// All side-effects delegated to host via callbacks.
|
|
5
|
-
//
|
|
6
|
-
// API:
|
|
7
|
-
// const ib = IngestBoard.create(containerEl, opts)
|
|
8
|
-
//
|
|
9
|
-
// ib.setBatches(batches) — rebuild board from batch array
|
|
10
|
-
// ib.getChat(boardId) — get chat element API for a card
|
|
11
|
-
// ib.getFileUpload(boardId) — get file-upload element API for a card
|
|
12
|
-
// ib.getActions(boardId) — get actions element API for a card
|
|
13
|
-
// ib.showChatModal(boardId, messages) — open Bootstrap modal with chat history
|
|
14
|
-
// ib.destroy()
|
|
15
|
-
//
|
|
16
|
-
// Required opts:
|
|
17
|
-
// onSend(boardId, { text, files }) — host handles: upload files + message → call getChat().appendMessage() with results
|
|
18
|
-
// onConfirm(boardId) — host handles: POST confirm → SSE → update board
|
|
19
|
-
// onDiscard(boardId) — host handles: POST discard → call ib.setBatches()
|
|
20
|
-
//
|
|
21
|
-
// Optional opts:
|
|
22
|
-
// onViewChat(boardId) — host handles: fetch chat → call ib.showChatModal()
|
|
23
|
-
// onRefresh() — host handles: fetch batches → call ib.setBatches()
|
|
24
|
-
// compact — smaller card columns (default: false)
|
|
25
|
-
// engine — existing LiveCard engine (one is created if omitted)
|
|
26
|
-
// markdown — markdown renderer fn
|
|
27
|
-
// sanitize — HTML sanitizer fn
|
|
28
|
-
|
|
29
|
-
// eslint-disable-next-line no-unused-vars
|
|
30
|
-
var IngestBoard = (function () {
|
|
31
|
-
'use strict';
|
|
32
|
-
|
|
33
|
-
function create(containerEl, opts) {
|
|
34
|
-
opts = opts || {};
|
|
35
|
-
if (!opts.onSend) throw new Error('IngestBoard: opts.onSend is required');
|
|
36
|
-
if (!opts.onConfirm) throw new Error('IngestBoard: opts.onConfirm is required');
|
|
37
|
-
if (!opts.onDiscard) throw new Error('IngestBoard: opts.onDiscard is required');
|
|
38
|
-
|
|
39
|
-
const compact = opts.compact || false;
|
|
40
|
-
const onSend = opts.onSend;
|
|
41
|
-
const onConfirm = opts.onConfirm;
|
|
42
|
-
const onDiscard = opts.onDiscard;
|
|
43
|
-
const onViewChat = opts.onViewChat || null;
|
|
44
|
-
const onRefresh = opts.onRefresh || null;
|
|
45
|
-
const mdFn = opts.markdown || (typeof marked !== 'undefined' ? function (t) { return marked.parse(t); } : null);
|
|
46
|
-
const sanFn = opts.sanitize || (typeof DOMPurify !== 'undefined' ? function (h) { return DOMPurify.sanitize(h); } : null);
|
|
47
|
-
|
|
48
|
-
let board = null;
|
|
49
|
-
const nodes = {}; // id → node
|
|
50
|
-
|
|
51
|
-
// ---- Engine ----
|
|
52
|
-
|
|
53
|
-
const engine = opts.engine || LiveCard.init({
|
|
54
|
-
resolve: function (id) { return nodes[id]; },
|
|
55
|
-
onPatch: function () {},
|
|
56
|
-
onPatchState: function () {},
|
|
57
|
-
onRefresh: onRefresh || function () {},
|
|
58
|
-
onAction: handleAction,
|
|
59
|
-
markdown: mdFn,
|
|
60
|
-
sanitize: sanFn,
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// ---- Action dispatcher (pure — delegates to host callbacks) ----
|
|
64
|
-
|
|
65
|
-
function handleAction(nodeId, actionType, payload) {
|
|
66
|
-
if (actionType === 'chat-send') {
|
|
67
|
-
// For the "new" card, boardId is null until host creates one
|
|
68
|
-
var boardId = nodeId === '__new__' ? null : nodeId;
|
|
69
|
-
onSend(boardId, { text: payload.text, files: payload.files });
|
|
70
|
-
} else if (actionType === 'action') {
|
|
71
|
-
if (payload.buttonId === 'confirm') onConfirm(nodeId);
|
|
72
|
-
else if (payload.buttonId === 'discard') onDiscard(nodeId);
|
|
73
|
-
else if (payload.buttonId === 'view-chat' && onViewChat) onViewChat(nodeId);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// ---- Node builders ----
|
|
78
|
-
|
|
79
|
-
function buildActiveNode(batch) {
|
|
80
|
-
return {
|
|
81
|
-
id: batch.id,
|
|
82
|
-
type: 'card',
|
|
83
|
-
meta: { title: batch.id, tags: [batch.status === 'open-items' ? 'open-items' : 'ready'] },
|
|
84
|
-
state: {
|
|
85
|
-
status: batch.processing ? 'loading' : 'fresh',
|
|
86
|
-
messages: batch.chat || [],
|
|
87
|
-
files: batch.files || [],
|
|
88
|
-
batchStatus: batch.status,
|
|
89
|
-
},
|
|
90
|
-
view: {
|
|
91
|
-
elements: [
|
|
92
|
-
{
|
|
93
|
-
id: 'chat',
|
|
94
|
-
kind: 'chat',
|
|
95
|
-
data: { bind: 'state.messages', fileAttach: true, placeholder: 'Add files or type a message...' }
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
id: 'actions',
|
|
99
|
-
kind: 'actions',
|
|
100
|
-
data: {
|
|
101
|
-
buttons: [
|
|
102
|
-
{ id: 'confirm', label: 'Confirm & Merge', style: 'success', disabled: batch.status !== 'ready' || !!batch.processing },
|
|
103
|
-
{ id: 'discard', label: 'Discard', style: 'outline-danger' }
|
|
104
|
-
]
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
],
|
|
108
|
-
layout: { board: { col: compact ? 6 : 8, order: 0 } },
|
|
109
|
-
features: { refresh: false }
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function buildCompletedNode(batch, order) {
|
|
115
|
-
var elements = [];
|
|
116
|
-
|
|
117
|
-
if (batch.files && batch.files.length) {
|
|
118
|
-
elements.push({
|
|
119
|
-
id: 'files',
|
|
120
|
-
kind: 'file-upload',
|
|
121
|
-
data: { bind: 'state.files', upload: false }
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (batch.summary) {
|
|
126
|
-
elements.push({
|
|
127
|
-
id: 'summary',
|
|
128
|
-
kind: 'text',
|
|
129
|
-
data: { bind: 'state.summary', style: 'muted' }
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (batch.chatCount > 0 && onViewChat) {
|
|
134
|
-
elements.push({
|
|
135
|
-
id: 'card-actions',
|
|
136
|
-
kind: 'actions',
|
|
137
|
-
data: { buttons: [{ id: 'view-chat', label: '\uD83D\uDCAC ' + batch.chatCount + ' message(s)', style: 'outline-secondary' }] }
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return {
|
|
142
|
-
id: batch.id,
|
|
143
|
-
type: 'card',
|
|
144
|
-
meta: { title: batch.id, tags: ['confirmed'] },
|
|
145
|
-
state: {
|
|
146
|
-
status: 'fresh',
|
|
147
|
-
files: batch.files || [],
|
|
148
|
-
summary: batch.summary || '',
|
|
149
|
-
},
|
|
150
|
-
view: {
|
|
151
|
-
elements: elements,
|
|
152
|
-
layout: { board: { col: compact ? 6 : 4, order: order } },
|
|
153
|
-
features: { refresh: false }
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function buildNewNode() {
|
|
159
|
-
return {
|
|
160
|
-
id: '__new__',
|
|
161
|
-
type: 'card',
|
|
162
|
-
meta: { title: 'New Batch', tags: ['new'] },
|
|
163
|
-
state: { status: 'fresh', messages: [] },
|
|
164
|
-
view: {
|
|
165
|
-
elements: [
|
|
166
|
-
{
|
|
167
|
-
id: 'chat',
|
|
168
|
-
kind: 'chat',
|
|
169
|
-
data: { bind: 'state.messages', fileAttach: true, placeholder: 'Add files or type a message...' }
|
|
170
|
-
}
|
|
171
|
-
],
|
|
172
|
-
layout: { board: { col: compact ? 6 : 8, order: -1 } },
|
|
173
|
-
features: { refresh: false }
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// ---- setBatches — rebuild the board from data ----
|
|
179
|
-
|
|
180
|
-
function setBatches(batches) {
|
|
181
|
-
Object.keys(nodes).forEach(function (k) { delete nodes[k]; });
|
|
182
|
-
|
|
183
|
-
var hasActive = batches.some(function (b) { return b.status === 'ready' || b.status === 'open-items'; });
|
|
184
|
-
var allNodes = [];
|
|
185
|
-
|
|
186
|
-
if (!hasActive) {
|
|
187
|
-
var nn = buildNewNode();
|
|
188
|
-
nodes.__new__ = nn;
|
|
189
|
-
allNodes.push(nn);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
var order = 1;
|
|
193
|
-
batches.forEach(function (b) {
|
|
194
|
-
var isActive = b.status === 'ready' || b.status === 'open-items';
|
|
195
|
-
var n = isActive ? buildActiveNode(b) : buildCompletedNode(b, order++);
|
|
196
|
-
nodes[b.id] = n;
|
|
197
|
-
allNodes.push(n);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
if (board) board.destroy();
|
|
201
|
-
board = LiveCard.Board(engine, containerEl, {
|
|
202
|
-
nodes: allNodes,
|
|
203
|
-
mode: 'board',
|
|
204
|
-
showNotes: false,
|
|
205
|
-
showChat: false,
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// ---- Element accessors (host uses these to push data in) ----
|
|
210
|
-
|
|
211
|
-
function getChat(boardId) {
|
|
212
|
-
var el = engine.getElement(boardId, 'chat');
|
|
213
|
-
return el && el._chat || null;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function getFileUpload(boardId) {
|
|
217
|
-
var el = engine.getElement(boardId, 'files');
|
|
218
|
-
return el && el._fileUpload || null;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function getActions(boardId) {
|
|
222
|
-
var el = engine.getElement(boardId, 'actions') || engine.getElement(boardId, 'card-actions');
|
|
223
|
-
return el && el._actions || null;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// ---- Chat modal (pure — caller passes messages) ----
|
|
227
|
-
|
|
228
|
-
function showChatModal(boardId, messages) {
|
|
229
|
-
var existing = document.getElementById('lc-chat-modal');
|
|
230
|
-
if (existing) existing.remove();
|
|
231
|
-
var bd = document.querySelector('.modal-backdrop');
|
|
232
|
-
if (bd) bd.remove();
|
|
233
|
-
|
|
234
|
-
var _e = function (t) { var d = document.createElement('div'); d.textContent = t; return d.innerHTML; };
|
|
235
|
-
|
|
236
|
-
var wrap = document.createElement('div');
|
|
237
|
-
wrap.innerHTML =
|
|
238
|
-
'<div class="modal fade" id="lc-chat-modal" tabindex="-1">' +
|
|
239
|
-
'<div class="modal-dialog modal-dialog-scrollable">' +
|
|
240
|
-
'<div class="modal-content">' +
|
|
241
|
-
'<div class="modal-header">' +
|
|
242
|
-
'<h6 class="modal-title">' + _e(boardId) + '</h6>' +
|
|
243
|
-
'<button type="button" class="btn-close" data-bs-dismiss="modal"></button>' +
|
|
244
|
-
'</div>' +
|
|
245
|
-
'<div class="modal-body"><div class="lc-chat-body" id="lc-chat-modal-body" style="max-height:none"></div></div>' +
|
|
246
|
-
'</div>' +
|
|
247
|
-
'</div>' +
|
|
248
|
-
'</div>';
|
|
249
|
-
document.body.appendChild(wrap.firstElementChild);
|
|
250
|
-
|
|
251
|
-
var bodyEl = document.getElementById('lc-chat-modal-body');
|
|
252
|
-
messages.forEach(function (msg) {
|
|
253
|
-
var bub = document.createElement('div');
|
|
254
|
-
var rc = msg.role === 'user' ? 'lc-chat-bubble-user'
|
|
255
|
-
: msg.role === 'assistant' ? 'lc-chat-bubble-assistant'
|
|
256
|
-
: 'lc-chat-bubble-system';
|
|
257
|
-
bub.className = 'lc-chat-bubble ' + rc;
|
|
258
|
-
if (msg.role === 'assistant' && mdFn) {
|
|
259
|
-
var html = mdFn(msg.text);
|
|
260
|
-
if (sanFn) html = sanFn(html);
|
|
261
|
-
bub.innerHTML = html;
|
|
262
|
-
} else {
|
|
263
|
-
bub.textContent = msg.text;
|
|
264
|
-
}
|
|
265
|
-
bodyEl.appendChild(bub);
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
var modal = new bootstrap.Modal(document.getElementById('lc-chat-modal'));
|
|
269
|
-
modal.show();
|
|
270
|
-
document.getElementById('lc-chat-modal').addEventListener('hidden.bs.modal', function () {
|
|
271
|
-
document.getElementById('lc-chat-modal').remove();
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// ---- Lifecycle ----
|
|
276
|
-
|
|
277
|
-
function destroy() {
|
|
278
|
-
if (board) board.destroy();
|
|
279
|
-
board = null;
|
|
280
|
-
Object.keys(nodes).forEach(function (k) { delete nodes[k]; });
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return {
|
|
284
|
-
setBatches: setBatches,
|
|
285
|
-
getChat: getChat,
|
|
286
|
-
getFileUpload: getFileUpload,
|
|
287
|
-
getActions: getActions,
|
|
288
|
-
showChatModal: showChatModal,
|
|
289
|
-
destroy: destroy,
|
|
290
|
-
get engine() { return engine; },
|
|
291
|
-
get board() { return board; },
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
return { create: create };
|
|
296
|
-
})();
|