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);