scroller update, action buttons - WIP
frdel committed
Jan 26, 2026 at 09:53 UTC
6c822f1446f59a1b534b827d5b2de04d491f5501
3 files changed
+63
-45
webui/components/messages/process-group/process-group.css
+1
-1
@@ -630,7 +630,7 @@
630
white-space: pre;
631
font-family: monospace;
632
max-width: fit-content;
633
- max-height: 300px;
633
+ /* max-height: 300px; */
634
overflow: auto;
635
-webkit-overflow-scrolling: touch;
636
scrollbar-width: thin;
webui/index.js
+7
-6
@@ -200,12 +200,14 @@ async function updateUserTime() {
200
updateUserTime();
201
setInterval(updateUserTime, 1000);
202
203
-function setMessages(logs) {
204
- const result = msgs.setMessages(logs);
203
+function setMessages(...params) {
204
const chatHistoryEl = document.getElementById("chat-history");
206
- if (preferencesStore.autoScroll && chatHistoryEl) {
207
- chatHistoryEl.scrollTop = chatHistoryEl.scrollHeight;
208
- }
205
+ let scroller;
206
+
207
+ if (preferencesStore.autoScroll && chatHistoryEl) scroller = new msgs.Scroller(chatHistoryEl)
208
+ const result = msgs.setMessages(...params);
209
+ if (scroller) scroller.reApplyScroll();
210
+
211
return result;
212
}
213
@@ -317,7 +319,6 @@ export async function poll() {
319
if (lastLogVersion != response.log_version) {
320
updated = true;
321
setMessages(response.logs);
320
-
322
afterMessagesUpdate(response.logs);
323
applyModeSteps(preferencesStore.detailMode, preferencesStore.showUtils);
324
}
webui/js/messages.js
+55
-38
@@ -651,20 +651,22 @@ function drawProcessStep({
651
};
652
}
653
654
-function drawStandaloneMessage(id, heading, content, options = {}) {
655
- const {
656
- position = "mid",
657
- forceNewGroup = false,
658
- containerClasses = [],
659
- mainClass = "",
660
- messageClasses = [],
661
- contentClasses = [],
662
- markdown = false,
663
- latex = false,
664
- kvps = null,
665
- copyContent = null,
666
- speakContent = null,
667
- } = options;
654
+function drawStandaloneMessage({
655
+ id,
656
+ heading,
657
+ content,
658
+ position = "mid",
659
+ forceNewGroup = false,
660
+ containerClasses = [],
661
+ mainClass = "",
662
+ messageClasses = [],
663
+ contentClasses = [],
664
+ markdown = false,
665
+ latex = false,
666
+ kvps = null,
667
+ copyContent = null,
668
+ speakContent = null,
669
+}) {
670
671
const container = getOrCreateMessageContainer(
672
id,
@@ -857,7 +859,10 @@ export function drawMessageDefault({
859
kvps = null,
860
...additional
861
}) {
860
- return drawStandaloneMessage(id, heading, content, {
862
+ return drawStandaloneMessage({
863
+ id,
864
+ heading,
865
+ content,
866
position: "left",
867
containerClasses: ["ai-container"],
868
mainClass: "message-default",
@@ -886,7 +891,6 @@ export function drawMessageAgent({
891
id,
892
title,
893
code: "GEN",
889
- codeClass: "status-gen",
894
classes: null,
895
kvps: displayKvps,
896
copyText: kvps?.thoughts,
@@ -1101,7 +1105,6 @@ export function drawMessageTool({
1105
id,
1106
title,
1107
code: "USE",
1104
- codeClass: "status-tool",
1108
classes: null,
1109
kvps: displayKvps,
1110
content,
@@ -1125,7 +1128,6 @@ export function drawMessageCodeExe({
1128
let title = "Code Execution";
1129
// show command at the start and end
1130
if (
1128
- type === "code_exe" &&
1131
kvps?.code &&
1132
/done_all|code_execution_tool/.test(heading || "")
1133
) {
@@ -1146,7 +1148,6 @@ export function drawMessageCodeExe({
1148
id,
1149
title,
1150
code: "EXE",
1149
- codeClass: "status-exe",
1151
classes: null,
1152
kvps: displayKvps,
1153
content,
@@ -1174,7 +1175,6 @@ export function drawMessageBrowser({
1175
id,
1176
title,
1177
code: "HDL",
1177
- codeClass: "status-hdl",
1178
classes: null,
1179
kvps: displayKvps,
1180
content,
@@ -1202,17 +1202,23 @@ export function drawMessageMcp({
1202
id,
1203
title,
1204
code: "MCP",
1205
- codeClass: "status-mcp",
1205
classes: null,
1206
kvps: displayKvps,
1207
content,
1208
// contentClasses: [],
1210
- copyText: content,
1211
- speakText: content,
1209
+ actionButtons:[
1210
+ createActionButton("detail","", ()=>{ openDetail(arguments[0]) }),
1211
+ createActionButton("speak","", ()=>{ speakText(content) }),
1212
+ createActionButton("copy","Command", ()=>{ copyToClipboard(kvps?.code) }),
1213
+ createActionButton("copy","Output", ()=>{ copyText(content) })
1214
+ ],
1215
log: arguments[0],
1216
});
1217
}
1218
1219
+// todo - move to store
1220
+function createActionButton(icon, text, handler){}
1221
+
1222
export function drawMessageSubagent({
1223
id,
1224
type,
@@ -1230,7 +1236,6 @@ export function drawMessageSubagent({
1236
id,
1237
title,
1238
code: "SUB",
1233
- codeClass: "status-sub",
1239
classes: null,
1240
kvps: displayKvps,
1241
content,
@@ -1248,11 +1253,20 @@ export function drawMessageInfo({
1253
kvps = null,
1254
...additional
1255
}) {
1251
- return drawStandaloneMessage(id, heading, content, {
1252
- position: "mid",
1253
- containerClasses: ["ai-container", "center-container"],
1254
- mainClass: "message-info",
1255
- kvps,
1256
+ const title = cleanStepTitle(heading);
1257
+ let displayKvps = { ...kvps };
1258
+
1259
+ return drawProcessStep({
1260
+ id,
1261
+ title,
1262
+ code: "INF",
1263
+ classes: null,
1264
+ kvps: displayKvps,
1265
+ content,
1266
+ // contentClasses: [],
1267
+ copyText: content,
1268
+ speakText: content,
1269
+ log: arguments[0],
1270
});
1271
}
1272
@@ -1272,7 +1286,6 @@ export function drawMessageUtil({
1286
id,
1287
title,
1288
code: "UTL",
1275
- codeClass: "status-utl",
1289
classes: ["message-util"],
1290
kvps,
1291
copyText: null,
@@ -1296,7 +1309,7 @@ export function drawMessageHint({
1309
const statusCode = getStatusCode(type);
1310
const statusClass = getStatusClass(type);
1311
1299
- return drawProcessStep({
1312
+ return drawStandaloneMessage({
1313
id,
1314
title,
1315
statusClass,
@@ -1327,7 +1340,6 @@ export function drawMessageProgress({
1340
id,
1341
title,
1342
code: "HDL",
1330
- codeClass: "status-hdl",
1343
classes: null,
1344
kvps: displayKvps,
1345
content,
@@ -1353,7 +1365,6 @@ export function drawMessageWarning({
1365
// id,
1366
// title,
1367
// code: "WRN",
1356
- // codeClass: "status-wrn",
1368
// classes: null,
1369
// kvps: displayKvps,
1370
// content,
@@ -1362,7 +1373,10 @@ export function drawMessageWarning({
1373
// speakText: content,
1374
// log: arguments[0],
1375
// });
1365
- return drawStandaloneMessage(id, heading, content, {
1376
+ return drawStandaloneMessage({
1377
+ id,
1378
+ heading,
1379
+ content,
1380
position: "mid",
1381
containerClasses: ["ai-container", "center-container"],
1382
mainClass: "message-warning",
@@ -1377,7 +1391,10 @@ export function drawMessageError({
1391
kvps = null,
1392
...additional
1393
}) {
1380
- return drawStandaloneMessage(id, heading, content, {
1394
+ return drawStandaloneMessage({
1395
+ id,
1396
+ heading,
1397
+ content,
1398
position: "mid",
1399
containerClasses: ["ai-container", "center-container"],
1400
mainClass: "message-error",
@@ -1783,13 +1800,13 @@ function adjustMarkdownRender(element) {
1800
});
1801
}
1802
1786
-class Scroller {
1803
+export class Scroller {
1804
constructor(element) {
1805
this.element = element;
1806
this.wasAtBottom = this.isAtBottom();
1807
}
1808
1792
- isAtBottom(tolerance = 10) {
1809
+ isAtBottom(tolerance = 80) {
1810
const scrollHeight = this.element.scrollHeight;
1811
const clientHeight = this.element.clientHeight;
1812
const distanceFromBottom =
@@ -1798,7 +1815,7 @@ class Scroller {
1815
}
1816
1817
reApplyScroll() {
1801
- if (this.wasAtBottom) this.element.scrollTop = this.element.scrollHeight;
1818
+ if (this.wasAtBottom && !this.isAtBottom()) this.element.scrollTop = this.element.scrollHeight;
1819
}
1820
}
1821