action buttons restore/fix
3clyp50 committed
Jan 22, 2026 at 02:36 UTC
2b867f00f0d6bb48ee1136235022dd9ac0260da5
2 files changed
+52
-47
webui/components/messages/action-buttons/simple-action-buttons.css
+19
-26
@@ -1,6 +1,6 @@
1
/* ===========================================
2
- Step Action Buttons - Always visible, icon-only
3
- Used in process steps for view details, copy, speak
2
+ Action Buttons - Icon-only copy/speak/view details
3
+ Show on hover for pointer devices, always visible on touch
4
=========================================== */
5
6
.step-action-buttons {
@@ -9,8 +9,9 @@
9
align-items: center;
10
gap: var(--spacing-sm);
11
padding: var(--spacing-sm) 0;
12
- opacity: 1;
13
- animation: none;
12
+ opacity: 0;
13
+ transition: opacity 0.2s ease-in-out;
14
+ pointer-events: none;
15
}
16
17
.step-action-buttons .action-button {
@@ -21,7 +22,6 @@
22
border: none;
23
padding: 2px;
24
cursor: pointer;
24
- transition: color 0.15s ease;
25
}
26
27
.step-action-buttons .action-button .material-symbols-outlined {
@@ -36,47 +36,40 @@
36
color: var(--color-text);
37
}
38
39
-/* Success state for step buttons */
39
+/* Success state */
40
.step-action-buttons .action-button.success .material-symbols-outlined {
41
color: #4CAF50;
42
font-variation-settings: 'FILL' 1, 'wght' 500, 'GRAD' 0, 'opsz' 20;
43
}
44
45
-/* Error state for step buttons */
45
+/* Error state */
46
.step-action-buttons .action-button.error .material-symbols-outlined {
47
color: var(--color-accent);
48
font-variation-settings: 'FILL' 1, 'wght' 500, 'GRAD' 0, 'opsz' 20;
49
}
50
51
-/* ===========================================
52
- User Message Action Buttons - Hover behavior
53
- Right-aligned, hidden by default on pointer devices, show on hover
54
- Always visible on touch devices
55
- =========================================== */
56
-
57
-/* Base styling for user message action buttons */
51
+/* User messages: right-aligned */
52
.message-user .step-action-buttons {
53
justify-content: flex-end;
54
width: 100%;
61
- opacity: 0;
62
- transition: opacity 0.2s ease-in-out;
63
- margin-top: var(--spacing-sm);
55
}
56
66
-/* Hide by default for user messages on pointer devices */
67
-.device-pointer .message-user .step-action-buttons {
68
- opacity: 0;
69
- pointer-events: none;
70
-}
57
+/* ===========================================
58
+ Hover behavior - Pointer devices
59
+ =========================================== */
60
72
-/* Show on hover for user messages on pointer devices */
73
-.device-pointer .message-user:hover .step-action-buttons {
61
+.device-pointer .message-user:hover .step-action-buttons,
62
+.device-pointer .message-agent-response:hover .step-action-buttons,
63
+.device-pointer .process-step:hover > .process-step-detail .step-action-buttons {
64
opacity: 1;
65
pointer-events: auto;
66
}
67
78
-/* Always show for user messages on touch devices */
79
-.device-touch .message-user .step-action-buttons {
68
+/* ===========================================
69
+ Touch devices - Always visible
70
+ =========================================== */
71
+
72
+.device-touch .step-action-buttons {
73
opacity: 1;
74
pointer-events: auto;
75
}
webui/js/messages.js
+33
-21
@@ -346,8 +346,8 @@ export function _drawMessage(
346
});
347
}
348
349
- // Ensure action buttons exist
350
- addActionButtonsToElement(bodyDiv);
349
+ // Ensure action buttons exist - pass content directly
350
+ addActionButtonsToElement(bodyDiv, { contentRef: content });
351
adjustMarkdownRender(contentDiv);
352
353
} else {
@@ -371,8 +371,8 @@ export function _drawMessage(
371
372
spanElement.innerHTML = convertHTML(content);
373
374
- // Ensure action buttons exist
375
- addActionButtonsToElement(bodyDiv);
374
+ // Ensure action buttons exist - pass content directly
375
+ addActionButtonsToElement(bodyDiv, { contentRef: content });
376
377
}
378
} else {
@@ -631,8 +631,8 @@ export function drawMessageUser(
631
headingElement.remove();
632
}
633
634
- addActionButtonsToElement(messageDiv);
635
-
634
+ // Add action buttons below text and attachments (hover for pointer, always for touch - via CSS)
635
+ addActionButtonsToElement(messageDiv, { contentRef: content });
636
}
637
638
export function drawMessageTool(
@@ -1589,9 +1589,9 @@ function addProcessStep(group, id, type, heading, content, kvps, timestamp = nul
1589
toolName: toolNameToUse
1590
};
1591
1592
- // Add "View Details" button for full modal view (reads fresh data from step._stepData)
1593
- const viewDetailsBtn = createViewDetailsButton(step);
1594
- detail.appendChild(viewDetailsBtn);
1592
+ // Add step action buttons (view details, copy, speak)
1593
+ const stepActionBtns = createStepActionButtons(step);
1594
+ detail.appendChild(stepActionBtns);
1595
1596
step.appendChild(detail);
1597
@@ -2046,23 +2046,35 @@ function renderThoughts(container, value) {
2046
}
2047
2048
/**
2049
- * Create "View Details" button for opening step detail modal
2049
+ * Create step action buttons (view details, copy, speak) using unified action buttons
2050
* @param {HTMLElement} stepElement - The step DOM element containing _stepData property
2051
*/
2052
-function createViewDetailsButton(stepElement) {
2052
+function createStepActionButtons(stepElement) {
2053
const btnContainer = document.createElement("div");
2054
btnContainer.classList.add("step-detail-actions");
2055
2056
- const btn = document.createElement("button");
2057
- btn.classList.add("btn", "text-button");
2058
- btn.innerHTML = '<span class="material-symbols-outlined">open_in_full</span> View Details';
2059
- btn.title = "Open full step details in modal";
2060
-
2061
- btn.addEventListener("click", (e) => {
2062
- e.stopPropagation();
2063
- // Read fresh data from the step element at click time
2064
- const freshData = stepElement._stepData || {};
2065
- stepDetailStore.showStepDetail(freshData);
2056
+ // Use unified action buttons with step-specific options
2057
+ addActionButtonsToElement(btnContainer, {
2058
+ contentRef: () => {
2059
+ // Get text content from step data at action time
2060
+ const data = stepElement._stepData || {};
2061
+ const parts = [];
2062
+ if (data.heading) parts.push(data.heading);
2063
+ if (data.content) parts.push(data.content);
2064
+ if (data.kvps) {
2065
+ for (const [key, value] of Object.entries(data.kvps)) {
2066
+ if (key === "reasoning" || key === "finished" || key === "attachments") continue;
2067
+ const valStr = typeof value === "object" ? JSON.stringify(value, null, 2) : String(value);
2068
+ parts.push(`${key}: ${valStr}`);
2069
+ }
2070
+ }
2071
+ return parts.join("\n\n");
2072
+ },
2073
+ onViewDetails: () => {
2074
+ // Read fresh data from the step element at click time
2075
+ const freshData = stepElement._stepData || {};
2076
+ stepDetailStore.showStepDetail(freshData);
2077
+ }
2078
});
2079
2080
btnContainer.appendChild(btn);