code-exec ux fixes
code-exec ux fixes fix clickable paths in code-exec terminal
3clyp50 committed
Jan 19, 2026 at 03:38 UTC
d5889fa42005674df02d863dae499b92c9799acd
2 files changed
+66
-23
webui/components/messages/process-group/process-group.css
+34
-17
@@ -446,7 +446,6 @@
446
transition: grid-template-rows 0.2s ease-out, opacity 0.15s ease-out, margin-top 0.2s ease-out;
447
overflow: hidden;
448
-webkit-overflow-scrolling: touch; /* smooth scrolling on iOS */
449
- scroll-behavior: smooth;
449
scrollbar-width: none; /* Firefox */
450
-ms-overflow-style: none; /* IE/Edge */
451
overscroll-behavior: contain; /* avoid scroll chaining */
@@ -488,7 +487,7 @@
487
}
488
489
.process-step.step-expanded > .process-step-detail > .process-step-detail-content {
491
- max-height: 300px;
490
+ max-height: 350px;
491
overflow-y: auto;
492
}
493
@@ -500,10 +499,8 @@
499
background-color: transparent;
500
font-size: 0.7rem;
501
line-height: 1.5;
503
- max-height: 300px;
502
overflow-y: auto;
503
-webkit-overflow-scrolling: touch; /* smooth scrolling on iOS */
506
- scroll-behavior: smooth;
504
scrollbar-width: none; /* Firefox */
505
-ms-overflow-style: none; /* IE/Edge */
506
overscroll-behavior: contain; /* avoid scroll chaining */
@@ -656,29 +653,49 @@
653
margin: var(--spacing-xs) 0 0 0;
654
padding: var(--spacing-xs);
655
background: rgba(0, 0, 0, 0.8);
659
- border-radius: 4px;
656
+ border-radius: 8px;
657
color: #c9d1d9;
661
- white-space: pre-wrap;
662
- word-break: break-word;
663
- max-height: 200px;
664
- overflow-y: auto;
665
- -webkit-overflow-scrolling: touch; /* smooth scrolling on iOS */
666
- scroll-behavior: smooth;
667
- scrollbar-width: none; /* Firefox */
668
- -ms-overflow-style: none; /* IE/Edge */
669
- overscroll-behavior: contain; /* avoid scroll chaining */
658
+ white-space: pre;
659
+ max-width: fit-content;
660
+ max-height: 300px;
661
+ overflow: auto;
662
+ -webkit-overflow-scrolling: touch;
663
+ scrollbar-width: thin;
664
+ scrollbar-color: rgba(255,255,255,0.2) transparent;
665
+ overscroll-behavior: contain;
666
}
667
668
.process-step-detail-content .terminal-output::-webkit-scrollbar {
673
- display: none;
674
- width: 0;
675
- height: 0;
669
+ width: 4px;
670
+ height: 4px;
671
+}
672
+
673
+.process-step-detail-content .terminal-output::-webkit-scrollbar-track {
674
+ background: transparent;
675
+}
676
+
677
+.process-step-detail-content .terminal-output::-webkit-scrollbar-thumb {
678
+ background: rgba(255, 255, 255, 0.2);
679
+ border-radius: 2px;
680
+}
681
+
682
+.process-step-detail-content .terminal-output::-webkit-scrollbar-thumb:hover {
683
+ background: rgba(255, 255, 255, 0.35);
684
}
685
686
/* Light mode terminal */
687
.light-mode .process-step-detail-content .terminal-output {
688
background: rgba(0, 0, 0, 0.05);
689
color: black;
690
+ scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
691
+}
692
+
693
+.light-mode .process-step-detail-content .terminal-output::-webkit-scrollbar-thumb {
694
+ background: rgba(0, 0, 0, 0.15);
695
+}
696
+
697
+.light-mode .process-step-detail-content .terminal-output::-webkit-scrollbar-thumb:hover {
698
+ background: rgba(0, 0, 0, 0.3);
699
}
700
701
/* Light mode adjustments */
webui/js/messages.js
+32
-6
@@ -1351,6 +1351,11 @@ function addProcessStep(group, id, type, heading, content, kvps, timestamp = nul
1351
// Explicitly add or remove the class based on state
1352
if (newState) {
1353
step.classList.add("step-expanded");
1354
+ // Scroll terminal for newly expanded steps
1355
+ requestAnimationFrame(() => {
1356
+ const terminal = step.querySelector(".terminal-output");
1357
+ if (terminal) terminal.scrollTop = terminal.scrollHeight;
1358
+ });
1359
} else {
1360
step.classList.remove("step-expanded");
1361
}
@@ -1371,6 +1376,14 @@ function addProcessStep(group, id, type, heading, content, kvps, timestamp = nul
1376
detail.appendChild(detailContent);
1377
step.appendChild(detail);
1378
1379
+ // Scroll terminal for already expanded steps
1380
+ if (isStepExpanded) {
1381
+ requestAnimationFrame(() => {
1382
+ const terminal = step.querySelector(".terminal-output");
1383
+ if (terminal) terminal.scrollTop = terminal.scrollHeight;
1384
+ });
1385
+ }
1386
+
1387
// Track delegation steps for nesting
1388
if (toolNameToUse === "call_subordinate") {
1389
currentDelegationSteps[agentNumber] = step;
@@ -1467,9 +1480,17 @@ function updateProcessStep(stepElement, id, type, heading, content, kvps, durati
1480
* Get a concise title for a process step
1481
*/
1482
function getStepTitle(heading, kvps, type) {
1483
+ // code_exe: show command when finished
1484
+ const showCommand = type === "code_exe" && kvps?.code &&
1485
+ /done_all|code_execution_tool/.test(heading || "");
1486
+ if (showCommand) {
1487
+ const s = kvps.session ?? kvps.Session;
1488
+ return `${s != null ? `[${s}] ` : ""}${kvps.runtime || "bash"}> ${kvps.code.trim()}`;
1489
+ }
1490
+
1491
// Try to get a meaningful title from heading or kvps
1492
if (heading && heading.trim()) {
1472
- return cleanStepTitle(heading, 80);
1493
+ return cleanStepTitle(heading, 100);
1494
}
1495
1496
// For warnings/errors without heading, use content preview as title
@@ -1485,13 +1506,13 @@ function getStepTitle(heading, kvps, type) {
1506
return `${kvps.tool_name}${headline ? ': ' + headline : ''}`;
1507
}
1508
if (kvps.headline) {
1488
- return cleanStepTitle(kvps.headline, 80);
1509
+ return cleanStepTitle(kvps.headline, 100);
1510
}
1511
if (kvps.query) {
1491
- return truncateText(kvps.query, 80);
1512
+ return truncateText(kvps.query, 100);
1513
}
1514
if (kvps.thoughts) {
1494
- return truncateText(String(kvps.thoughts), 80);
1515
+ return truncateText(String(kvps.thoughts), 100);
1516
}
1517
}
1518
@@ -1569,12 +1590,17 @@ function renderStepDetailContent(container, content, kvps, type = null) {
1590
const terminalDiv = document.createElement("div");
1591
terminalDiv.classList.add("step-terminal");
1592
1572
- // Show output if present
1593
+ // Show output if present (no truncation - CSS handles max-height)
1594
if (output && output.trim()) {
1595
const outputPre = document.createElement("pre");
1596
outputPre.classList.add("terminal-output");
1576
- outputPre.textContent = truncateText(output, 1000);
1597
+ // Escape HTML first, then convert paths to clickable links
1598
+ let processedOutput = escapeHTML(output);
1599
+ processedOutput = convertPathsToLinks(processedOutput);
1600
+ outputPre.innerHTML = processedOutput;
1601
terminalDiv.appendChild(outputPre);
1602
+ // Scroll terminal to bottom
1603
+ outputPre.scrollTop = outputPre.scrollHeight;
1604
}
1605
1606
container.appendChild(terminalDiv);