attachments, errors

frdel committed Jan 21, 2026 at 12:02 UTC e53db344b756da6f86e4f4953aee15d739a04a91
4 files changed +47 -58
webui/components/messages/process-group/process-group.css
+4 -3
@@ -210,7 +210,7 @@
210
211 /* ERR - error type (red) */
212 .status-err {
213 - --step-accent: var(--color-accent);
213 + --step-accent: var(--color-error-text);
214 color: var(--step-accent);
215 }
216
@@ -250,7 +250,8 @@
250 display: flex;
251 align-items: center;
252 gap: var(--spacing-sm);
253 - margin-left: auto;
253 + /* margin-left: auto; */
254 + margin-left: 2em;
255 font-size: 0.65rem;
256 font-family: var(--font-family-code);
257 color: var(--color-text);
@@ -742,7 +743,7 @@
743 }
744
745 .light-mode .status-err {
745 - --step-accent: var(--color-accent);
746 + --step-accent: var(--color-error-text);
747 color: var(--step-accent);
748 }
749
webui/css/messages.css
+12 -18
@@ -74,8 +74,16 @@
74 .message-user {
75 text-align: end;
76 margin-bottom: var(--spacing-md);
77 +
78 +}
79 +
80 +.message-user .message-text {
81 + display: inline-block;
82 + padding: var(--spacing-sm);
83 border: 1px solid var(--color-border);
84 border-radius: var(--border-radius);
85 + max-width: 80%;
86 + width: auto;
87 }
88
89 .message-user > div {
@@ -338,8 +346,7 @@
346 flex: 0 0 auto;
347 font-size: var(--font-size-medium);
348 font-weight: 600;
341 - color: var(--color-accent);
342 - opacity: 0.95;
349 + color: var(--color-error-text);
350 }
351
352 /* Error subtitle (short description) */
@@ -383,7 +390,7 @@
390 .error-content-inner {
391 padding: var(--spacing-md);
392 border-radius: 8px;
386 - border: 1px solid var(--color-accent);
393 + /* border: 1px solid var(--color-error-text); */
394 margin-left: var(--spacing-md);
395 }
396
@@ -395,12 +402,11 @@
402 font-family: var(--font-family-code);
403 font-size: 0.72rem;
404 line-height: 1.5;
398 - color: var(--color-accent);
399 - opacity: 0.9;
405 + color: var(--color-error-text);
406 max-height: 30vh;
407 overflow-y: auto;
408 scrollbar-width: thin;
403 - scrollbar-color: var(--color-accent) transparent;
409 + scrollbar-color: var(--color-error-text) transparent;
410 }
411
412
@@ -622,18 +628,6 @@
628 border-bottom: none;
629 }
630
625 -/* Light mode error group */
626 -.light-mode .error-group-header .expand-icon {
627 - border-color: transparent transparent transparent var(--color-accent);
628 -}
629 -
630 -.light-mode .error-group-header .error-title {
631 - color: var(--color-accent);
632 -}
633 -
634 -.light-mode .error-content-inner {
635 - border: 1px solid var(--color-accent);
636 -}
631
632 .light-mode .message-user {
633 color: #4e4e4e;
webui/index.css
+15 -28
@@ -23,6 +23,7 @@
23 --color-input-dark: #131313;
24 --color-input-focus-dark: #101010;
25 --color-chat-background-dark: #212121;
26 + --color-error-text-dark:#e72323;
27
28 /* Light mode */
29 --color-background-light: #fafafa;
@@ -39,6 +40,8 @@
40 --color-input-light: #e4e4e4;
41 --color-input-focus-light: #dadada;
42 --color-chat-background-light: #fafafaf3;
43 + --color-error-text-light:#920000;
44 +
45
46 /* Default to dark mode */
47 --color-background: var(--color-background-dark);
@@ -56,6 +59,7 @@
59 --color-input-focus: var(--color-input-focus-dark);
60 --color-background-hover: color-mix(in srgb, var(--color-border) 50%, transparent);
61 --color-chat-background: var(--color-chat-background-dark);
62 + --color-error-text:var(--color-error-text-dark);
63
64 /* Spacing variables */
65 --spacing-xxs: 0.15rem;
@@ -98,6 +102,7 @@
102 --color-input-focus: var(--color-input-focus-light);
103 --color-background-hover: color-mix(in srgb, var(--color-border) 50%, transparent);
104 --color-chat-background: var(--color-chat-background-light);
105 + --color-error-text:var(--color-error-text-light);
106 }
107
108
@@ -457,24 +462,20 @@ h4 {
462 margin-bottom: 10px;
463 padding: var(--spacing-sm);
464 transition: all 0.3s ease;
460 - background-color: var(--color-input);
465 border-radius: 8px;
466 margin-bottom: var(--spacing-xs);
467 max-height: 10em;
468 overflow-y: auto;
469 }
470
467 -.light-mode .preview-section {
468 - background-color: none;
469 -}
471
472 .preview-item {
473 position: relative;
474 width: 120px;
475 height: 120px;
475 - background-color: var(--color-background);
476 - border: 2px solid var(--color-border);
477 - border-radius: 12px;
476 + /* background-color: var(--color-background); */
477 + border: 1px solid var(--color-border);
478 + border-radius: var(--border-radius);
479 display: flex;
480 align-items: center;
481 justify-content: center;
@@ -668,9 +669,8 @@ h4 {
669 position: relative;
670 width: 120px;
671 height: 120px;
671 - background-color: var(--color-background);
672 - border: 2px solid var(--color-border);
673 - border-radius: 12px;
672 + /* background-color: var(--color-background); */
673 + border: 1px solid var(--color-border);
674 display: flex;
675 align-items: center;
676 justify-content: center;
@@ -678,11 +678,7 @@ h4 {
678 cursor: pointer;
679 transition: all 0.3s ease;
680 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
681 -}
682 -
683 -.light-mode .attachment-item {
684 - background-color: var(--color-panel-light);
685 - border: 2px solid var(--color-border-light);
681 + border-radius: var(--border-radius);
682 }
683
684 .attachment-item:hover {
@@ -705,10 +701,11 @@ h4 {
701 height: 100%;
702 object-fit: cover;
703 border-radius: 10px;
708 - background: repeating-linear-gradient(45deg, #fff 0 10px, #e0e0e0 10px 20px);
704 + background: repeating-linear-gradient(45deg, var(--color-panel) 0 10px, var(--color-chat-background) 10px 20px);
705
706 }
707
708 +
709 /* File attachment styling */
710 .attachment-item.file-type {
711 flex-direction: column;
@@ -738,22 +735,13 @@ h4 {
735 margin-bottom: 4px;
736 }
737
741 -.attachment-preview {
742 - width: 100%;
743 - height: 100%;
744 - object-fit: cover;
745 - border-radius: 10px;
746 - background: repeating-linear-gradient(45deg, #fff 0 10px, #e0e0e0 10px 20px);
747 -
748 -}
749 -
738 /* Chat-history attachments overrides (flat styling) */
739
740 #chat-history .attachments-container {
741 padding: 0 !important;
742 }
743
756 -#chat-history .attachment-item {
744 +/* #chat-history .attachment-item {
745 padding: 0 !important;
746 background-color: transparent !important;
747 border: none !important;
@@ -775,7 +763,7 @@ h4 {
763
764 .attachment-image .attachment-preview {
765 margin-right: 0;
778 -}
766 +} */
767
768 .attachment-info,
769 .file-info {
@@ -821,7 +809,6 @@ h4 {
809 gap: 8px;
810 margin-bottom: 10px;
811 padding: var(--spacing-xs);
824 - background-color: var(--color-border);
812 }
813
814 .preview-item {
webui/js/messages.js
+16 -9
@@ -87,9 +87,9 @@ function updateBadgeText(badge, newCode) {
87 }
88
89 // Process types that should be grouped into collapsible sections
90 -const PROCESS_TYPES = ['agent', 'tool', 'code_exe', 'browser', 'progress', 'info', 'hint', 'util', 'warning'];
90 +const PROCESS_TYPES = ['agent', 'tool', 'code_exe', 'browser', 'progress', 'hint', 'util', 'warning', 'rate_limit'];
91 // Main types that should always be visible (not collapsed)
92 -const MAIN_TYPES = ['user', 'response', 'error', 'rate_limit'];
92 +const MAIN_TYPES = ['user', 'response', 'error', 'info'];
93
94 /**
95 * Helper to append a message container to the correct group in chat history
@@ -319,6 +319,7 @@ export function _drawMessage(
319 let processedContent = content;
320 processedContent = convertImageTags(processedContent);
321 processedContent = convertImgFilePaths(processedContent);
322 + processedContent = convertFilePaths(processedContent);
323 processedContent = marked.parse(processedContent, { breaks: true });
324 processedContent = convertPathsToLinks(processedContent);
325 processedContent = addBlankTargetsToLinks(processedContent);
@@ -335,7 +336,7 @@ export function _drawMessage(
336 }
337
338 // Ensure action buttons exist
338 - addActionButtonsToElement(bodyDiv);
339 + // addActionButtonsToElement(bodyDiv);
340 adjustMarkdownRender(contentDiv);
341
342 } else {
@@ -360,7 +361,7 @@ export function _drawMessage(
361 spanElement.innerHTML = convertHTML(content);
362
363 // Ensure action buttons exist
363 - addActionButtonsToElement(bodyDiv);
364 + // addActionButtonsToElement(bodyDiv);
365
366 }
367 } else {
@@ -549,7 +550,7 @@ export function drawMessageUser(
550 textDiv.appendChild(spanElement);
551 }
552 spanElement.innerHTML = escapeHTML(content);
552 - addActionButtonsToElement(textDiv);
553 + // addActionButtonsToElement(textDiv);
554 } else {
555 if (textDiv) textDiv.remove();
556 }
@@ -905,7 +906,7 @@ export function drawMessageError(
906 contentInner.appendChild(pre);
907
908 // Add action buttons for copy functionality
908 - addActionButtonsToElement(contentInner);
909 + // addActionButtonsToElement(contentInner);
910 }
911
912 messageContainer.classList.add("center-container");
@@ -941,7 +942,7 @@ function drawKvps(container, kvps, latex) {
942 addValue(value);
943 }
944
944 - addActionButtonsToElement(tdiv);
945 + // addActionButtonsToElement(tdiv);
946
947 // autoscroll the KVP value if needed
948 // if (getAutoScroll()) #TODO needs a better redraw system
@@ -1044,7 +1045,7 @@ function drawKvpsIncremental(container, kvps, latex) {
1045 // Clear and rebuild content (for now - could be optimized further)
1046 tdiv.innerHTML = "";
1047
1047 - addActionButtonsToElement(tdiv);
1048 + // addActionButtonsToElement(tdiv);
1049
1050 if (Array.isArray(value)) {
1051 for (const item of value) {
@@ -1165,6 +1166,10 @@ function convertImgFilePaths(str) {
1166 return str.replace(/img:\/\//g, "/image_get?path=");
1167 }
1168
1169 +function convertFilePaths(str) {
1170 + return str.replace(/file:\/\//g, "/download_work_dir_file?path=");
1171 +}
1172 +
1173 export function convertIcons(str) {
1174 return str.replace(
1175 /icon:\/\/([a-zA-Z0-9_]+)/g,
@@ -1696,6 +1701,7 @@ function renderStepDetailContent(container, content, kvps, type = null) {
1701 let processedContent = content;
1702 processedContent = convertImageTags(processedContent);
1703 processedContent = convertImgFilePaths(processedContent);
1704 + processedContent = convertFilePaths(processedContent);
1705 processedContent = marked.parse(processedContent, { breaks: true });
1706 processedContent = convertPathsToLinks(processedContent);
1707 processedContent = addBlankTargetsToLinks(processedContent);
@@ -2115,7 +2121,8 @@ function markProcessGroupComplete(group, responseTitle) {
2121 // Update status badge to END
2122 const statusEl = group.querySelector(".group-status");
2123 if (statusEl) {
2118 - statusEl.innerHTML = '<span class="badge-icon material-symbols-outlined">check</span>END';
2124 + // statusEl.innerHTML = '<span class="badge-icon material-symbols-outlined">check</span>END';
2125 + statusEl.innerHTML = 'END';
2126 statusEl.className = "status-badge status-end group-status";
2127 }
2128