diff --git a/python/extensions/before_main_llm_call/_10_log_for_stream.py b/python/extensions/before_main_llm_call/_10_log_for_stream.py index fa86e1e18..60063dd5c 100644 --- a/python/extensions/before_main_llm_call/_10_log_for_stream.py +++ b/python/extensions/before_main_llm_call/_10_log_for_stream.py @@ -25,4 +25,4 @@ def build_heading(agent, text: str, icon: str = "network_intelligence"): return f"{agent_prefix}{text}" def build_default_heading(agent): - return build_heading(agent, "Generating...") \ No newline at end of file + return build_heading(agent, "Calling LLM...") \ No newline at end of file diff --git a/python/extensions/reasoning_stream/_10_log_from_stream.py b/python/extensions/reasoning_stream/_10_log_from_stream.py index b98da8374..eb954e5a8 100644 --- a/python/extensions/reasoning_stream/_10_log_from_stream.py +++ b/python/extensions/reasoning_stream/_10_log_from_stream.py @@ -13,7 +13,7 @@ class LogFromStream(Extension): # thought length indicator pipes = "|" * math.ceil(math.sqrt(len(text))) - heading = build_heading(self.agent, f"Reasoning.. {pipes}") + heading = build_heading(self.agent, f"Reasoning... {pipes}") # create log message and store it in loop data temporary params if "log_item_generating" not in loop_data.params_temporary: @@ -21,9 +21,10 @@ class LogFromStream(Extension): self.agent.context.log.log( type="agent", heading=heading, + step="Reasoning..." ) ) # update log message log_item = loop_data.params_temporary["log_item_generating"] - log_item.update(heading=heading, reasoning=text) + log_item.update(heading=heading, reasoning=text, step="Reasoning...") diff --git a/python/extensions/response_stream/_10_log_from_stream.py b/python/extensions/response_stream/_10_log_from_stream.py index 308263bbc..d9bb1170d 100644 --- a/python/extensions/response_stream/_10_log_from_stream.py +++ b/python/extensions/response_stream/_10_log_from_stream.py @@ -24,7 +24,11 @@ class LogFromStream(Extension): elif "tool_name" in parsed: heading = build_heading(self.agent, f"Using {parsed['tool_name']}") # if the llm skipped headline elif "thoughts" in parsed: - heading = build_default_heading(self.agent) + # thought length indicator + pipes = "|" * math.ceil(math.sqrt(len(text))) + heading = build_heading(self.agent, f"Thinking... {pipes}") + else: + heading = build_heading(self.agent, "Receiving...") # create log message and store it in loop data temporary params if "log_item_generating" not in loop_data.params_temporary: @@ -48,12 +52,16 @@ class LogFromStream(Extension): kvps["step"] = f"Using {parsed['tool_name']}..." # using tool XY if parsed["tool_name"]=="code_execution_tool": if "tool_args" in parsed and "runtime" in parsed["tool_args"]: + pipes = "" + if "code" in parsed["tool_args"]: + pipes = "|" * math.ceil(math.sqrt(len(parsed["tool_args"]["code"]))) + kvps["step"] = f"Writing code... {pipes}" if parsed["tool_args"]["runtime"] == "python": - kvps["step"] = "Writing Python code..." + kvps["step"] = f"Writing Python code... {pipes}" elif parsed["tool_args"]["runtime"] == "nodejs": - kvps["step"] = "Writing Node.js code..." + kvps["step"] = f"Writing Node.js code... {pipes}" elif parsed["tool_args"]["runtime"] == "terminal": - kvps["step"] = "Writing terminal command..." + kvps["step"] = f"Writing terminal command... {pipes}" kvps.update(parsed) diff --git a/webui/components/messages/action-buttons/simple-action-buttons.css b/webui/components/messages/action-buttons/simple-action-buttons.css index f914ef8ba..682ece1a6 100644 --- a/webui/components/messages/action-buttons/simple-action-buttons.css +++ b/webui/components/messages/action-buttons/simple-action-buttons.css @@ -14,6 +14,24 @@ pointer-events: none; } +.step-action-buttons .expand-btn { + display: none; + align-items: center; + background: transparent; + border: none; + color: var(--color-text-muted); + font-family: "Rubik", Arial, Helvetica, sans-serif; + font-size: var(--font-size-xs); + cursor: pointer; + padding: var(--spacing-xs) 0; + opacity: 0.7; + transition: opacity 0.15s ease; +} + +.step-action-buttons .expand-btn:hover { + opacity: 1; +} + .step-action-buttons .action-button, .action-button { display: flex; @@ -53,6 +71,55 @@ font-variation-settings: 'FILL' 1, 'wght' 500, 'GRAD' 0, 'opsz' 20; } +.show-more-btn, +.show-less-btn { + display: none; +} + +.message.message-collapsible.has-overflow:not(.expanded) > .step-action-buttons { + opacity: 1; + pointer-events: auto; +} + +.message.message-collapsible.has-overflow:not(.expanded) + > .step-action-buttons + .show-more-btn { + display: inline-flex; + pointer-events: auto; +} + +.device-pointer + .message.message-collapsible.has-overflow:not(.expanded) + > .step-action-buttons + .action-button { + opacity: 0; + pointer-events: none; +} + +.device-pointer + .message.message-collapsible.has-overflow:not(.expanded):hover + > .step-action-buttons + .action-button { + opacity: 1; + pointer-events: auto; +} + +.device-pointer + .message.message-collapsible.has-overflow.expanded:hover + > .step-action-buttons + .show-less-btn { + display: inline-flex; + pointer-events: auto; +} + +.device-touch + .message.message-collapsible.has-overflow.expanded + > .step-action-buttons + .show-less-btn { + display: inline-flex; + pointer-events: auto; +} + /* User messages: right-aligned */ .message-user .step-action-buttons { justify-content: flex-end; @@ -70,6 +137,7 @@ .device-pointer .message-user:hover > .step-action-buttons, .device-pointer .message-agent-response:hover > .step-action-buttons, +.device-pointer .message.message-collapsible.expanded:hover > .step-action-buttons, .device-pointer .process-step:hover > .process-step-detail .step-action-buttons { opacity: 1; pointer-events: auto; diff --git a/webui/components/messages/action-buttons/simple-action-buttons.js b/webui/components/messages/action-buttons/simple-action-buttons.js index ea46a2ee3..59cb35cbe 100644 --- a/webui/components/messages/action-buttons/simple-action-buttons.js +++ b/webui/components/messages/action-buttons/simple-action-buttons.js @@ -54,7 +54,7 @@ export function showButtonFeedback(button, success, originalIcon) { setTimeout(() => { icon.textContent = originalIcon; button.classList.remove("success", "error"); - }, 2000); + }, 1000); } /** @@ -77,7 +77,7 @@ export function createActionButton(icon, text = "", handler = null) { if (typeof handler === "function") { button.addEventListener("click", async (event) => { event.stopPropagation(); - const shouldShowFeedback = icon === "copy" || icon === "speak"; + const shouldShowFeedback = true; // icon === "copy" || icon === "speak"; try { await handler(); if (shouldShowFeedback) { diff --git a/webui/components/messages/process-group/process-group.css b/webui/components/messages/process-group/process-group.css index 93d973f29..78064ba0f 100644 --- a/webui/components/messages/process-group/process-group.css +++ b/webui/components/messages/process-group/process-group.css @@ -346,6 +346,7 @@ border-color 0.2s ease-out; overflow: hidden; color: var(--color-text-muted); + max-width: 100%; } .process-group-content > .process-steps { @@ -366,6 +367,8 @@ display: flex; flex-direction: column; gap: 2px; + max-width: 100%; + overflow: hidden; } /* Individual Process Step */ @@ -374,6 +377,8 @@ flex-direction: column; padding: 0.1em; transition: background-color 0.15s ease; + max-width: 100%; + overflow: hidden; } /* Utility/Info/Hint steps have subtle background tint */ @@ -404,6 +409,8 @@ user-select: none; gap: 4px; min-height: 18px; + max-width: 100%; + overflow: hidden; } /* Step title */ @@ -415,6 +422,7 @@ overflow: hidden; text-overflow: ellipsis; margin-left: var(--spacing-xs); + min-width: 0; } /* Step expand icon - CSS triangle (no Material Icons) */ @@ -453,6 +461,7 @@ scrollbar-width: none; /* Firefox */ -ms-overflow-style: none; /* IE/Edge */ overscroll-behavior: contain; /* avoid scroll chaining */ + max-width: 100%; } .process-step:not(.expanded) > .process-step-detail { @@ -486,13 +495,17 @@ .process-step.expanded > .process-step-detail { /* grid-template-rows: 1fr; */ opacity: 1; - overflow: visible; + overflow: hidden; margin-top: var(--spacing-xs); + max-width: 100%; } .process-step.expanded > .process-step-detail > .process-step-detail-scroll { max-height: 40em; overflow-y: auto; + max-width: 100%; + width: fit-content; + box-sizing: content-box; border-radius: var(--border-radius-sm); } @@ -508,6 +521,8 @@ font-size: var(--font-size-xs); line-height: 1.5; overflow-y: auto; + overflow-x: hidden; + max-width: 100%; -webkit-overflow-scrolling: touch; /* smooth scrolling on iOS */ scrollbar-width: none; /* Firefox */ -ms-overflow-style: none; /* IE/Edge */ @@ -527,7 +542,7 @@ /* KVPs in step detail - CSS Grid for aligned columns */ .process-step-detail-scroll .step-kvps { display: grid; - grid-template-columns: auto 1fr; + grid-template-columns: auto minmax(0, 1fr); gap: var(--spacing-xs) var(--spacing-sm); align-items: start; } @@ -543,6 +558,7 @@ display: flex; align-items: center; justify-content: end; + white-space: nowrap; } .process-step-detail-scroll .step-kvp-key .material-symbols-outlined { @@ -554,9 +570,17 @@ /* color: var(--color-text); */ opacity: 0.85; word-break: break-word; + overflow-wrap: anywhere; white-space: pre-wrap; font-size: 0.75rem; line-height: 1.5; + min-width: 0; +} + +.process-step-detail-scroll .step-kvp-value p { + margin: 0; + word-break: break-word; + overflow-wrap: anywhere; } @@ -622,6 +646,7 @@ } */ +/* Terminal output - self-contained scrollable block */ .process-step-detail .terminal-output { margin: var(--spacing-xs) 0 0 0; padding: var(--spacing-xs); @@ -631,12 +656,11 @@ white-space: pre; font-family: monospace; max-width: fit-content; - /* max-height: 300px; */ overflow: auto; -webkit-overflow-scrolling: touch; scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.2) transparent; - overscroll-behavior-x: contain; + overscroll-behavior: contain; } .process-step-detail .terminal-output::-webkit-scrollbar { @@ -739,6 +763,7 @@ overflow: hidden; transition: grid-template-rows 0.25s ease-out, opacity 0.2s ease-out; gap: 2px; + max-width: 100%; } /* Only show nested container when parent step is expanded */ @@ -755,6 +780,8 @@ display: flex; flex-direction: column; gap: 2px; + max-width: 100%; + overflow: hidden; } /* All nested containers have same indentation (no cascading) */ @@ -769,6 +796,8 @@ color: var(--color-text); opacity: 0.9; margin: var(--spacing-xs) 0; + word-break: break-word; + overflow-wrap: anywhere; } .process-step-detail-scroll .step-response-content p { diff --git a/webui/components/modals/memory/memory-detail-modal.html b/webui/components/modals/memory/memory-detail-modal.html index 44723c935..619459d14 100644 --- a/webui/components/modals/memory/memory-detail-modal.html +++ b/webui/components/modals/memory/memory-detail-modal.html @@ -139,10 +139,8 @@