From 655a871ab0bf6eed5411590cada81505f8b441a2 Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Fri, 13 Mar 2026 14:30:43 -0500 Subject: [PATCH] style: update chat layout and spacing for improved UI consistency - Adjusted margin and padding for .chat-thread and .content--chat to enhance layout. - Consolidated CSS selectors for better readability and maintainability. - Introduced new test for log parsing functionality to ensure accurate message extraction. --- ui/src/styles/chat/layout.css | 38 ++++++++++++++++++++---------- ui/src/styles/components.css | 4 ++-- ui/src/styles/layout.css | 17 +++++++------ ui/src/styles/layout.mobile.css | 8 +++++-- ui/src/ui/controllers/logs.test.ts | 28 ++++++++++++++++++++++ ui/src/ui/controllers/logs.ts | 2 ++ 6 files changed, 71 insertions(+), 26 deletions(-) create mode 100644 ui/src/ui/controllers/logs.test.ts diff --git a/ui/src/styles/chat/layout.css b/ui/src/styles/chat/layout.css index 536acddd29e..f05a2665ff6 100644 --- a/ui/src/styles/chat/layout.css +++ b/ui/src/styles/chat/layout.css @@ -9,11 +9,15 @@ flex-direction: column; flex: 1 1 0; height: 100%; - min-height: 0; /* Allow flex shrinking */ + width: 100%; + min-height: 0; + /* Allow flex shrinking */ overflow: hidden; background: transparent !important; border: none !important; box-shadow: none !important; + backdrop-filter: blur(14px) saturate(1.15); + -webkit-backdrop-filter: blur(14px) saturate(1.15); } /* Chat header - fixed at top, transparent */ @@ -24,8 +28,8 @@ gap: 12px; flex-wrap: nowrap; flex-shrink: 0; - padding-bottom: 12px; - margin-bottom: 12px; + padding-bottom: 0; + margin-bottom: 0; background: transparent; } @@ -49,16 +53,22 @@ /* Chat thread - scrollable middle section, transparent */ .chat-thread { - flex: 1 1 0; /* Grow, shrink, and use 0 base for proper scrolling */ + flex: 1 1 0; + /* Grow, shrink, and use 0 base for proper scrolling */ overflow-y: auto; overflow-x: hidden; - padding: 12px 4px; - margin: 0 -4px; - min-height: 0; /* Allow shrinking for flex scroll behavior */ + padding: 0 6px 6px; + margin: 0 0 0 0; + min-height: 0; + /* Allow shrinking for flex scroll behavior */ border-radius: 12px; background: transparent; } +.chat-thread-inner> :first-child { + margin-top: 0 !important; +} + /* Focus mode exit button */ .chat-focus-exit { position: absolute; @@ -146,7 +156,8 @@ display: flex; flex-direction: column; gap: 12px; - margin-top: auto; /* Push to bottom of flex container */ + margin-top: auto; + /* Push to bottom of flex container */ padding: 12px 4px 4px; background: linear-gradient(to bottom, transparent, var(--bg) 20%); z-index: 10; @@ -163,7 +174,8 @@ border: 1px solid var(--border); width: fit-content; max-width: 100%; - align-self: flex-start; /* Don't stretch in flex column parent */ + align-self: flex-start; + /* Don't stretch in flex column parent */ } .chat-attachment { @@ -279,7 +291,7 @@ } /* Hide the "Message" label - keep textarea only */ -.chat-compose__field > span { +.chat-compose__field>span { display: none; } @@ -350,7 +362,7 @@ } } -.agent-chat__input > textarea { +.agent-chat__input>textarea { width: 100%; min-height: 40px; max-height: 150px; @@ -366,7 +378,7 @@ box-sizing: border-box; } -.agent-chat__input > textarea::placeholder { +.agent-chat__input>textarea::placeholder { color: var(--muted); } @@ -508,7 +520,7 @@ scrollbar-width: thin; } -.slash-menu-group + .slash-menu-group { +.slash-menu-group+.slash-menu-group { margin-top: 4px; padding-top: 4px; border-top: 1px solid color-mix(in srgb, var(--border) 50%, transparent); diff --git a/ui/src/styles/components.css b/ui/src/styles/components.css index d1dc29ca04e..b2806f3208f 100644 --- a/ui/src/styles/components.css +++ b/ui/src/styles/components.css @@ -2157,7 +2157,7 @@ } .chat-thread { - margin-top: 16px; + margin-top: 0; display: flex; flex-direction: column; gap: 12px; @@ -2165,7 +2165,7 @@ min-height: 0; overflow-y: auto; overflow-x: hidden; - padding: 16px 12px; + padding: 0 12px 16px; min-width: 0; border-radius: 0; border: none; diff --git a/ui/src/styles/layout.css b/ui/src/styles/layout.css index 12f22aef21d..c9bfb8e6951 100644 --- a/ui/src/styles/layout.css +++ b/ui/src/styles/layout.css @@ -70,7 +70,7 @@ padding-top: 0; } -.shell--chat-focus .content > * + * { +.shell--chat-focus .content>*+* { margin-top: 0; } @@ -688,11 +688,9 @@ .sidebar--collapsed .nav-item.active, .sidebar--collapsed .nav-item--active { - background: linear-gradient( - 180deg, + background: linear-gradient(180deg, color-mix(in srgb, #0b2f34 84%, var(--bg-elevated) 16%) 0%, - color-mix(in srgb, #081f25 90%, var(--bg) 10%) 100% - ); + color-mix(in srgb, #081f25 90%, var(--bg) 10%) 100%); border-color: color-mix(in srgb, #1ed2c2 18%, var(--border) 82%); box-shadow: inset 0 1px 0 color-mix(in srgb, white 8%, transparent), @@ -833,7 +831,7 @@ overflow-x: hidden; } -.content > * + * { +.content>*+* { margin-top: 20px; } @@ -844,12 +842,12 @@ .content--chat { display: flex; flex-direction: column; - gap: 24px; + gap: 2px; overflow: hidden; padding-bottom: 0; } -.content--chat > * + * { +.content--chat>*+* { margin-top: 0; } @@ -905,9 +903,10 @@ align-items: center; justify-content: space-between; gap: 16px; + padding-bottom: 0; } -.content--chat .content-header > div:first-child { +.content--chat .content-header>div:first-child { text-align: left; } diff --git a/ui/src/styles/layout.mobile.css b/ui/src/styles/layout.mobile.css index 3c929435a7b..036e6a7c588 100644 --- a/ui/src/styles/layout.mobile.css +++ b/ui/src/styles/layout.mobile.css @@ -323,6 +323,10 @@ gap: 8px; } + .content--chat { + gap: 2px; + } + .content--chat .content-header > div:first-child, .content--chat .page-meta, .content--chat .chat-controls { @@ -417,8 +421,8 @@ } .chat-thread { - margin-top: 8px; - padding: 12px 8px; + margin-top: 0; + padding: 0 8px 12px; } .chat-msg { diff --git a/ui/src/ui/controllers/logs.test.ts b/ui/src/ui/controllers/logs.test.ts new file mode 100644 index 00000000000..5d1a830de7a --- /dev/null +++ b/ui/src/ui/controllers/logs.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from "vitest"; +import { parseLogLine } from "./logs.ts"; + +describe("parseLogLine", () => { + it("prefers the human-readable message field when structured data is stored in slot 1", () => { + const line = JSON.stringify({ + 0: '{"subsystem":"gateway/ws"}', + 1: { + cause: "unauthorized", + authReason: "password_missing", + }, + 2: "closed before connect conn=abc code=4008 reason=connect failed", + _meta: { + date: "2026-03-13T19:07:12.128Z", + logLevelName: "WARN", + }, + time: "2026-03-13T14:07:12.138-05:00", + }); + + expect(parseLogLine(line)).toEqual( + expect.objectContaining({ + level: "warn", + subsystem: "gateway/ws", + message: "closed before connect conn=abc code=4008 reason=connect failed", + }), + ); + }); +}); diff --git a/ui/src/ui/controllers/logs.ts b/ui/src/ui/controllers/logs.ts index d2e919c6210..90c2edcf00a 100644 --- a/ui/src/ui/controllers/logs.ts +++ b/ui/src/ui/controllers/logs.ts @@ -77,6 +77,8 @@ export function parseLogLine(line: string): LogEntry { let message: string | null = null; if (typeof obj["1"] === "string") { message = obj["1"]; + } else if (typeof obj["2"] === "string") { + message = obj["2"]; } else if (!contextObj && typeof obj["0"] === "string") { message = obj["0"]; } else if (typeof obj.message === "string") {