29
let _massRender = false;
30
let _scrollOnNextProcessGroup = null;
31
32
+/**
33
+ * @typedef {object} MessageHandlerArgs
34
+ * @property {number} [no]
35
+ * @property {string | number} id
36
+ * @property {string} type
37
+ * @property {string | undefined} [heading]
38
+ * @property {string | undefined} [content]
39
+ * @property {object | undefined} [kvps]
40
+ * @property {number | undefined} [timestamp]
41
+ * @property {number} [agentno]
42
+ */
43
+
44
+/**
45
+ * @typedef {{ element: Element } & Record<string, any>} MessageHandlerResult
46
+ */
47
+
48
+/**
49
+ * @typedef {object} SetMessageResult
50
+ * @property {IArguments} args
51
+ * @property {MessageHandlerResult} result
52
+ */
53
+
54
+/**
55
+ * @typedef {(args: MessageHandlerArgs & Record<string, any>) => MessageHandlerResult} MessageHandler
56
+ */
57
+
58
+/**
59
+ * @typedef {object} ProcessStepArgs
60
+ * @property {string | number} id
61
+ * @property {string} title
62
+ * @property {string} code
63
+ * @property {string[] | undefined} [classes]
64
+ * @property {any} [kvps]
65
+ * @property {string | undefined} [content]
66
+ * @property {string[] | undefined} [contentClasses]
67
+ * @property {Element[] | undefined} [actionButtons]
68
+ * @property {any} log
69
+ * @property {boolean} [allowCompletedGroup]
70
+ */
71
+
72
+
73
export function scrollOnNextProcessGroup() {
74
_scrollOnNextProcessGroup = "wait";
75
}
76
77
// handlers for log message rendering
78
+/**
79
+ * Returns a message renderer for a given log message type.
80
+ *
81
+ * The returned handler has the same input object shape as `setMessage(...)` passes through
82
+ * and may return a rich object `{ element, actionButtons?, ...additional }`.
83
+ *
84
+ * @param {string} type
85
+ * @returns {MessageHandler}
86
+ */
87
export function getMessageHandler(type) {
88
switch (type) {
89
case "user":
137
reapplyDelayMs: 1000,
138
applyStabilization: true,
139
},
140
+ /** @type {Scroller | null} */
141
mainScroller: null,
142
+ /** @type {SetMessageResult[]} */
143
results: [],
144
};
145
151
152
await callJsExtensions("set_messages_before_loop", context);
153
154
+ //@ts-ignore
155
context.mainScroller = new Scroller(context.history, context.scrollerOptions);
156
157
// process messages
158
for (let i = 0; i < context.messages.length; i++) {
159
_massRender = context.historyEmpty || (context.isLargeAppend && i < context.cutoff);
107
- context.results.push(setMessage(context.messages[i]) || {});
160
+ context.results.push(setMessage(context.messages[i]));
161
}
162
163
await callJsExtensions("set_messages_after_loop", context);
165
// reset _massRender flag
166
_massRender = false;
167
115
- const shouldScroll = context.historyEmpty || !context.results[context.results.length - 1]?.dontScroll;
168
+ const shouldScroll = context.historyEmpty || !context.results[context.results.length - 1]?.result?.dontScroll;
169
117
- if (shouldScroll) context.mainScroller.reApplyScroll();
170
+ if (shouldScroll) context.mainScroller?.reApplyScroll();
171
172
if (_scrollOnNextProcessGroup === "scroll") {
173
requestAnimationFrame(() => {
121
- context.mainScroller.scrollToBottom();
174
+ context.mainScroller?.scrollToBottom();
175
_scrollOnNextProcessGroup = null;
176
});
177
}
179
180
// entrypoint called from poll/WS communication, this is how all messages are rendered and updated
181
// input is raw log format
182
+/**
183
+ * @param {MessageHandlerArgs & Record<string, any>} param0
184
+ * @returns {SetMessageResult}
185
+ */
186
export function setMessage({
187
no,
188
id,
196
}) {
197
const handler = getMessageHandler(type);
198
// prefer log ID if set to match user message created on frontend with backend updates
142
- return handler({
143
- id: id || no,
199
+ const handlerResult = handler({
200
+ no,
201
+ id: id || String(no) || "",
202
type,
203
heading,
204
content,
207
agentno,
208
...additional,
209
});
210
+ return {
211
+ args: arguments[0],
212
+ result: handlerResult,
213
+ }
214
}
215
216
function getOrCreateMessageContainer(
317
};
318
}
319
320
+/**
321
+ * @param {ProcessStepArgs & Record<string, any>} param0
322
+ * @returns {MessageHandlerResult}
323
+ */
324
function drawProcessStep({
325
id,
326
title,
355
group.setAttribute("data-start-timestamp", String(log.timestamp));
356
}
357
292
- if (isNewStep) {
358
+ if (!step) {
359
// create the base DOM element for the step
360
step = document.createElement("div");
361
step.id = stepId;
363
364
// set data attributes of the step
365
step.setAttribute("data-log-type", log.type);
300
- step.setAttribute("data-step-id", id);
366
+ step.setAttribute("data-step-id", String(id));
367
step.setAttribute("data-agent-number", log.agentno);
368
369
// set timestamp attribute (convert to milliseconds for duration calculation)
475
if (prevCode) step.classList.remove(prevCode);
476
step.setAttribute("data-step-code", code);
477
step.classList.add(code);
412
- step.querySelector(".step-badge").textContent = code;
478
badge.innerText = code;
479
}
480
536
537
// return anything useful
538
return {
539
+ element: step,
540
+ actionButtons,
541
step,
542
detail: stepDetail,
543
content: stepDetailContent,
544
contentScroller: detailScroller,
545
kvpsTable,
479
- actionButtons: stepActionBtns,
546
isExpanded,
547
};
548
}
711
// KaTeX rendering for markdown
712
if (latex) {
713
contentDiv.querySelectorAll("latex").forEach((element) => {
648
- katex.render(element.innerHTML, element, {
714
+ globalThis.katex.render(element.innerHTML, element, {
715
throwOnError: false,
716
});
717
});
781
return doc.body.innerHTML;
782
}
783
784
+/**
785
+ * @param {MessageHandlerArgs & Record<string, any>} param0
786
+ * @returns {MessageHandlerResult}
787
+ */
788
export function drawMessageDefault({
789
id,
790
heading,
800
].filter(Boolean)
801
: [];
802
733
- return drawStandaloneMessage({
803
+ const element = drawStandaloneMessage({
804
id,
805
heading,
806
content,
812
kvps,
813
actionButtons,
814
});
815
+
816
+ return { element };
817
}
818
819
+/**
820
+ * @param {MessageHandlerArgs & Record<string, any>} param0
821
+ * @returns {MessageHandlerResult}
822
+ */
823
export function drawMessageAgent({
824
id,
825
type,
826
heading,
827
content,
752
- kvps = null,
753
- timestamp = null,
828
+ kvps = undefined,
829
+ timestamp = undefined,
830
agentno = 0,
831
...additional
832
}) {
859
id,
860
title,
861
code: "GEN",
786
- classes: null,
862
+ classes: undefined,
863
kvps: displayKvps,
864
actionButtons,
865
log: arguments[0],
866
});
867
}
868
869
+/**
870
+ * @param {MessageHandlerArgs & Record<string, any>} param0
871
+ * @returns {MessageHandlerResult}
872
+ */
873
export function drawMessageResponse({
874
id,
875
type,
876
heading,
877
content,
798
- kvps = null,
799
- timestamp = null,
878
+ kvps = undefined,
879
+ timestamp = undefined,
880
agentno = 0,
881
...additional
882
}) {
938
939
const messageDiv = _drawMessage({
940
messageContainer: container,
861
- heading: null,
941
+ heading: undefined,
942
content,
863
- kvps: null,
943
+ kvps: undefined,
944
messageClasses: [],
945
contentClasses: [],
946
markdown: true,
966
967
if (group) updateProcessGroupHeader(group);
968
889
- return container;
969
+ return { element: container };
970
}
971
972
+/**
973
+ * @param {MessageHandlerArgs & Record<string, any>} param0
974
+ * @returns {MessageHandlerResult}
975
+ */
976
export function drawMessageUser({
977
id,
978
heading,
1072
1073
attachmentDiv.addEventListener("click", displayInfo.clickHandler);
1074
1075
+ // @ts-ignore
1076
attachmentsContainer.appendChild(attachmentDiv);
1077
});
1078
} else {
1110
userActionButtons.forEach((button) =>
1111
actionButtonsContainer.appendChild(button),
1112
);
1113
+
1114
+ return { element: messageContainer };
1115
}
1116
1117
+/**
1118
+ * @param {MessageHandlerArgs & Record<string, any>} param0
1119
+ * @returns {MessageHandlerResult}
1120
+ */
1121
export function drawMessageTool({
1122
id,
1123
type,
1124
heading,
1125
content,
1035
- kvps = null,
1036
- timestamp = null,
1126
+ kvps,
1127
+ timestamp,
1128
agentno = 0,
1129
...additional
1130
}) {
1147
}
1148
}
1149
1150
+/**
1151
+ * @param {MessageHandlerArgs & Record<string, any>} param0
1152
+ * @returns {MessageHandlerResult}
1153
+ */
1154
export function drawMessageToolSimple({
1155
id,
1156
type,
1157
heading,
1158
content,
1064
- kvps = null,
1065
- timestamp = null,
1159
+ kvps,
1160
+ timestamp,
1161
agentno = 0,
1162
code,
1163
displayKvps,
1185
id,
1186
title,
1187
code: code || "USE",
1093
- classes: null,
1188
+ classes: undefined,
1189
kvps: displayKvps,
1190
content,
1191
// contentClasses: [],
1194
});
1195
}
1196
1197
+/**
1198
+ * @param {MessageHandlerArgs & Record<string, any>} param0
1199
+ * @returns {MessageHandlerResult}
1200
+ */
1201
export function drawMessageCodeExe({
1202
id,
1203
type,
1204
heading,
1205
+ test,
1206
content,
1107
- kvps = null,
1108
- timestamp = null,
1207
+ kvps,
1208
+ timestamp,
1209
agentno = 0,
1210
...additional
1211
}) {
1235
// render the standard step
1236
const commandText = String(kvps?.code ?? "");
1237
const outputText = String(content ?? "");
1138
- const actionButtons = [
1238
+
1239
+ const actionButtons = [];
1240
+ actionButtons.push(
1241
createActionButton("detail", "", () =>
1242
stepDetailStore.showStepDetail(
1243
buildDetailPayload(arguments[0], { headerLabels }),
1244
),
1245
),
1144
- commandText.trim()
1145
- ? createActionButton("copy", "Command", () =>
1146
- copyToClipboard(commandText),
1147
- )
1148
- : null,
1149
- outputText.trim()
1150
- ? createActionButton("copy", "Output", () => copyToClipboard(outputText))
1151
- : null,
1152
- ].filter(Boolean);
1246
+ );
1247
+ if (commandText.trim()) {
1248
+ actionButtons.push(
1249
+ createActionButton("copy", "Command", () => copyToClipboard(commandText)),
1250
+ );
1251
+ }
1252
+ if (outputText.trim()) {
1253
+ actionButtons.push(
1254
+ createActionButton("copy", "Output", () => copyToClipboard(outputText)),
1255
+ );
1256
+ }
1257
const stepData = drawProcessStep({
1258
id,
1259
title,
1260
code: "EXE",
1157
- classes: null,
1261
+ classes: undefined,
1262
kvps: displayKvps,
1263
content,
1264
contentClasses: ["terminal-output"],
1265
actionButtons,
1266
log: arguments[0],
1267
});
1268
+
1269
+ return stepData;
1270
}
1271
1272
+/**
1273
+ * @param {MessageHandlerArgs & Record<string, any>} param0
1274
+ * @returns {MessageHandlerResult}
1275
+ */
1276
export function drawMessageBrowser({
1277
id,
1278
type,
1279
heading,
1280
content,
1171
- kvps = null,
1172
- timestamp = null,
1281
+ kvps,
1282
+ timestamp,
1283
agentno = 0,
1284
...additional
1285
}) {
1302
id,
1303
title,
1304
code: "WWW",
1195
- classes: null,
1305
+ classes: undefined,
1306
kvps: displayKvps,
1307
content,
1308
// contentClasses: [],
1311
});
1312
}
1313
1314
+/**
1315
+ * @param {MessageHandlerArgs & Record<string, any>} param0
1316
+ * @returns {MessageHandlerResult}
1317
+ */
1318
export function drawMessageMcp({
1319
id,
1320
type,
1321
heading,
1322
content,
1209
- kvps = null,
1210
- timestamp = null,
1323
+ kvps,
1324
+ timestamp,
1325
agentno = 0,
1326
...additional
1327
}) {
1347
id,
1348
title,
1349
code: "MCP",
1236
- classes: null,
1350
+ classes: undefined,
1351
kvps: displayKvps,
1352
content,
1353
// contentClasses: [],
1356
});
1357
}
1358
1359
+/**
1360
+ * @param {MessageHandlerArgs & Record<string, any>} param0
1361
+ * @returns {MessageHandlerResult}
1362
+ */
1363
export function drawMessageSubagent({
1364
id,
1365
type,
1366
heading,
1367
content,
1250
- kvps = null,
1251
- timestamp = null,
1368
+ kvps,
1369
+ timestamp,
1370
agentno = 0,
1371
...additional
1372
}) {
1392
id,
1393
title,
1394
code: "SUB",
1277
- classes: null,
1395
+ classes: undefined,
1396
kvps: displayKvps,
1397
content,
1398
// contentClasses: [],
1401
});
1402
}
1403
1404
+/**
1405
+ * @param {MessageHandlerArgs & Record<string, any>} param0
1406
+ * @returns {MessageHandlerResult}
1407
+ */
1408
export function drawMessageInfo({
1409
id,
1410
heading,
1411
content,
1290
- kvps = null,
1412
+ kvps,
1413
...additional
1414
}) {
1415
const title = cleanStepTitle(heading || content);
1426
id,
1427
title,
1428
code: "INF",
1307
- classes: null,
1429
+ classes: undefined,
1430
kvps: displayKvps,
1431
content,
1432
// contentClasses: [],
1435
});
1436
}
1437
1438
+/**
1439
+ * @param {MessageHandlerArgs & Record<string, any>} param0
1440
+ * @returns {MessageHandlerResult}
1441
+ */
1442
export function drawMessageUtil({
1443
id,
1444
type,
1445
heading,
1446
content,
1321
- kvps = null,
1322
- timestamp = null,
1447
+ kvps,
1448
+ timestamp,
1449
agentno = 0,
1450
...additional
1451
}) {
1474
return result;
1475
}
1476
1477
+/**
1478
+ * @param {MessageHandlerArgs & Record<string, any>} param0
1479
+ * @returns {MessageHandlerResult}
1480
+ */
1481
export function drawMessageHint({
1482
id,
1483
type,
1484
heading,
1485
content,
1356
- kvps = null,
1357
- timestamp = null,
1486
+ kvps,
1487
+ timestamp,
1488
agentno = 0,
1489
...additional
1490
}) {
1497
].filter(Boolean)
1498
: [];
1499
1370
- return drawStandaloneMessage({
1500
+ const element = drawStandaloneMessage({
1501
id,
1372
- title,
1502
+ heading: title,
1503
// statusClass,
1374
- statusCode: "HNT",
1504
+ // statusCode: "HNT",
1505
kvps,
1376
- type,
1377
- heading,
1506
+ // type,
1507
content,
1379
- timestamp,
1380
- agentno,
1508
+ // timestamp,
1509
+ // agentno,
1510
actionButtons,
1511
});
1512
+
1513
+ return { element };
1514
}
1515
1516
+/**
1517
+ * @param {MessageHandlerArgs & Record<string, any>} param0
1518
+ * @returns {MessageHandlerResult}
1519
+ */
1520
export function drawMessageProgress({
1521
id,
1522
type,
1523
heading,
1524
content,
1390
- kvps = null,
1391
- timestamp = null,
1525
+ kvps,
1526
+ timestamp,
1527
agentno = 0,
1528
...additional
1529
}) {
1534
id,
1535
title,
1536
code: "HDL",
1402
- classes: null,
1537
+ classes: undefined,
1538
kvps: displayKvps,
1539
content,
1540
// contentClasses: [],
1543
});
1544
}
1545
1546
+/**
1547
+ * @param {MessageHandlerArgs & Record<string, any>} param0
1548
+ * @returns {MessageHandlerResult}
1549
+ */
1550
export function drawMessageWarning({
1551
id,
1552
type,
1582
}
1583
1584
// if no process group is running, draw as standalone
1446
- return drawStandaloneMessage({
1585
+ const element = drawStandaloneMessage({
1586
id,
1448
- title,
1587
+ heading: title,
1588
content,
1589
position: "mid",
1590
containerClasses: ["ai-container", "center-container"],
1592
kvps: displayKvps,
1593
actionButtons,
1594
});
1595
+
1596
+ return { element };
1597
}
1598
1599
+/**
1600
+ * @param {MessageHandlerArgs & Record<string, any>} param0
1601
+ * @returns {MessageHandlerResult}
1602
+ */
1603
export function drawMessageError({
1604
id,
1605
type,
1611
const contentText = String(content ?? "");
1612
let title = getStepTitle(heading, content, type);
1613
let displayKvps = { ...kvps };
1469
- const actionButtons = [
1614
+
1615
+ const actionButtons = [];
1616
+ actionButtons.push(
1617
createActionButton("detail", "", () =>
1618
stepDetailStore.showStepDetail(
1619
buildDetailPayload(arguments[0], { headerLabels: [] }),
1620
),
1621
),
1475
- contentText.trim()
1476
- ? createActionButton("copy", "", () => copyToClipboard(contentText))
1477
- : null,
1478
- ].filter(Boolean);
1622
+ );
1623
+ if (contentText.trim()) {
1624
+ actionButtons.push(
1625
+ createActionButton("copy", "", () => copyToClipboard(contentText)),
1626
+ );
1627
+ }
1628
1480
- return drawStandaloneMessage({
1629
+ const element = drawStandaloneMessage({
1630
id,
1631
heading: title,
1632
content: contentText,
1636
kvps: displayKvps,
1637
actionButtons,
1638
});
1639
+
1640
+ return { element };
1641
}
1642
1643
function drawKvpsIncremental(container, kvps, latex) {
1740
// KaTeX rendering for markdown
1741
if (latex) {
1742
span.querySelectorAll("latex").forEach((element) => {
1592
- katex.render(element.innerHTML, element, {
1743
+ globalThis.katex.render(element.innerHTML, element, {
1744
throwOnError: false,
1745
});
1746
});
2312
}
2313
2314
// gets or creates a child DOM element
2315
+/**
2316
+ * @param {Element} parent
2317
+ * @param {string} selector
2318
+ * @param {string} tagName
2319
+ * @param {...string} classNames
2320
+ * @returns {HTMLElement}
2321
+ */
2322
function ensureChild(parent, selector, tagName, ...classNames) {
2165
- let el = parent.querySelector(selector);
2323
+ /** @type {HTMLElement | null} */
2324
+ let el = /** @type {any} */ (parent.querySelector(selector));
2325
if (!el) {
2326
el = document.createElement(tagName);
2327
if (classNames.length) el.classList.add(...classNames);