process group step detail toggler

frdel committed Jan 26, 2026 at 14:11 UTC 93419f910645dfd6b843f873502256c8f945c8fd
3 files changed +17 -79
webui/components/messages/process-group/process-group-dom.js
+7 -69
@@ -8,81 +8,19 @@ export function applyModeSteps(detailMode, showUtils) {
8 detailMode ||
9 preferencesStore.detailMode ||
10 "current";
11 - const showUtilsFlag =
12 - typeof showUtils === "boolean"
13 - ? showUtils
14 - : preferencesStore.showUtils || false;
11
12 const chatHistory = document.getElementById("chat-history");
13 if (!chatHistory) return;
14
19 - const shouldExpandGroup = mode !== "collapsed";
20 - const shouldExpandError = mode === "current" || mode === "expanded";
15 + const shouldExpand = mode !== "collapsed";
16 + const messages = chatHistory.querySelectorAll(".process-group");
17 + for (let i = 0; i < messages.length; i += 1) {
18 + messages[i].classList.toggle("expanded", shouldExpand);
19
22 - // Walk DOM once (reverse to match message traversal patterns)
23 - const groups = chatHistory.children;
24 - for (let gi = groups.length - 1; gi >= 0; gi -= 1) {
25 - const messageGroup = groups[gi];
26 - const containers = messageGroup.children;
27 - for (let ci = containers.length - 1; ci >= 0; ci -= 1) {
28 - const container = containers[ci];
29 -
30 - if (container.classList.contains("has-process-group")) {
31 - const processGroup = container.querySelector(".process-group");
32 - if (processGroup) {
33 - applyModeToProcessGroup(processGroup, mode, showUtilsFlag, shouldExpandGroup);
34 - }
35 - }
36 -
37 - const errorGroups = container.getElementsByClassName("error-group");
38 - if (errorGroups.length) {
39 - for (let ei = 0; ei < errorGroups.length; ei += 1) {
40 - errorGroups[ei].classList.toggle("expanded", shouldExpandError);
41 - }
42 - }
20 + const steps = messages[i].querySelectorAll(".process-step");
21 + for (let si = 0; si < steps.length; si += 1) {
22 + steps[si].classList.toggle("expanded", mode === "expanded");
23 }
24 }
25 }
26
47 -function applyModeToProcessGroup(group, mode, showUtilsFlag, shouldExpandGroup) {
48 - group.classList.toggle("expanded", shouldExpandGroup);
49 -
50 - const isActiveGroup = group.classList.contains("active");
51 - const isGroupCompleted = group.classList.contains("process-group-completed");
52 - const steps = group.getElementsByClassName("process-step");
53 - if (!steps.length) return;
54 -
55 - let lastVisibleStep = null;
56 - const shouldFindLastVisible = mode === "current" && isActiveGroup && !isGroupCompleted;
57 -
58 - for (let i = steps.length - 1; i >= 0; i -= 1) {
59 - const step = steps[i];
60 -
61 - if (shouldFindLastVisible && !lastVisibleStep) {
62 - if (showUtilsFlag || !step.classList.contains("message-util")) {
63 - lastVisibleStep = step;
64 - }
65 - }
66 -
67 - let shouldExpand = false;
68 - if (mode === "expanded") {
69 - shouldExpand = true;
70 - } else if (mode === "current" && isActiveGroup) {
71 - shouldExpand = step === lastVisibleStep;
72 - }
73 -
74 - if (shouldExpand) {
75 - step.classList.add("step-expanded");
76 - } else {
77 - const shouldDefer =
78 - mode === "current" &&
79 - isActiveGroup &&
80 - lastVisibleStep &&
81 - step !== lastVisibleStep;
82 - if (!shouldDefer) {
83 - step.classList.remove("step-expanded");
84 - step.removeAttribute("data-user-pinned");
85 - }
86 - }
87 - }
88 -}
webui/components/messages/process-group/process-group.css
+8 -8
@@ -437,7 +437,7 @@
437 }
438
439 /* Use direct child selector to prevent affecting nested steps */
440 -.process-step.step-expanded > .process-step-header > .step-expand-icon {
440 +.process-step.expanded > .process-step-header > .step-expand-icon {
441 transform: rotate(90deg);
442 }
443
@@ -455,11 +455,11 @@
455 overscroll-behavior: contain; /* avoid scroll chaining */
456 }
457
458 -.process-step:not(.step-expanded) > .process-step-detail {
458 +.process-step:not(.expanded) > .process-step-detail {
459 margin-top: 0;
460 }
461
462 -.process-step:not(.step-expanded) > .process-step-detail > .process-step-detail-scroll {
462 +.process-step:not(.expanded) > .process-step-detail > .process-step-detail-scroll {
463 padding: 0;
464 margin-top: 0;
465 max-height: 0;
@@ -483,19 +483,19 @@
483 }
484
485 /* Use direct child selector (>) to prevent cascading to nested steps */
486 -.process-step.step-expanded > .process-step-detail {
486 +.process-step.expanded > .process-step-detail {
487 /* grid-template-rows: 1fr; */
488 opacity: 1;
489 overflow: visible;
490 margin-top: var(--spacing-xs);
491 }
492
493 -.process-step.step-expanded > .process-step-detail > .process-step-detail-scroll {
493 +.process-step.expanded > .process-step-detail > .process-step-detail-scroll {
494 max-height: 40em;
495 overflow-y: auto;
496 }
497
498 -.process-step.step-expanded > .process-step-detail > .process-step-detail-scroll > .process-step-detail-scroll {
498 +.process-step.expanded > .process-step-detail > .process-step-detail-scroll > .process-step-detail-scroll {
499 }
500
501 .process-step-detail-scroll {
@@ -741,7 +741,7 @@
741 }
742
743 /* Only show nested container when parent step is expanded */
744 -.process-step.step-expanded > .process-nested-container {
744 +.process-step.expanded > .process-nested-container {
745 grid-template-rows: 1fr;
746 opacity: 1;
747 margin-top: 2px;
@@ -826,7 +826,7 @@
826 }
827
828 /* Hide View Details button when step is collapsed */
829 -.process-step:not(.step-expanded) > .process-step-detail > .step-detail-actions {
829 +.process-step:not(.expanded) > .process-step-detail > .step-detail-actions {
830 display: none;
831 }
832
webui/js/messages.js
+2 -2
@@ -460,7 +460,7 @@ function drawProcessStep({
460 if (isActiveGroup && !isGroupCompleted) {
461 step.classList.add("step-expanded");
462 const allExpandedSteps = stepsContainer.querySelectorAll(
463 - ".process-step.step-expanded",
463 + ".process-step.expanded",
464 );
465 allExpandedSteps.forEach((expandedStep) => {
466 if (expandedStep.id !== stepId) {
@@ -2584,7 +2584,7 @@ function markProcessGroupComplete(group, responseTitle) {
2584 if (detailMode === "current") {
2585 // Schedule collapse for all expanded steps (deterministic)
2586 const allExpandedSteps = group.querySelectorAll(
2587 - ".process-step.step-expanded",
2587 + ".process-step.expanded",
2588 );
2589 allExpandedSteps.forEach((expandedStep) => {
2590 scheduleStepCollapse(expandedStep, FINAL_STEP_COLLAPSE_DELAY_MS);