feat(FN-4050): fix mobile session title clipping in ChatView

Fixes mobile session title clipping in ChatView via CSS updates, adds a test for the fix, and updates the mobile switcher docs for FN-4050.

Fusion-Task-Id: FN-4050
This commit is contained in:
Fusion
2026-05-11 18:27:48 -07:00
committed by gsxdsm
parent 52f62f1494
commit 3a45a3fc25
3 changed files with 31 additions and 8 deletions

View File

@@ -92,7 +92,7 @@ Chat view provides project-scoped conversations with agents.
- If a regular Chat stream drops with a hidden-tab/browser-suspension error (for example `Load failed`) while the server is still generating, Chat suppresses the false error banner, re-attaches to the in-progress stream using the durable replay state, and reconciles the final assistant reply when generation completes. - If a regular Chat stream drops with a hidden-tab/browser-suspension error (for example `Load failed`) while the server is still generating, Chat suppresses the false error banner, re-attaches to the in-progress stream using the durable replay state, and reconciles the final assistant reply when generation completes.
- Chat message lists now track near-bottom scroll state: while you are reading older messages, live streaming/new replies do not force-scroll; a **Latest** jump control appears until you return to the tail. - Chat message lists now track near-bottom scroll state: while you are reading older messages, live streaming/new replies do not force-scroll; a **Latest** jump control appears until you return to the tail.
- On mobile direct-chat threads, entering a thread and restoring Chat after tab/page visibility returns re-anchors to the newest message (`scrollTop = scrollHeight`) so the view always opens at the live tail. - On mobile direct-chat threads, entering a thread and restoring Chat after tab/page visibility returns re-anchors to the newest message (`scrollTop = scrollHeight`) so the view always opens at the live tail.
- On mobile direct-chat threads, tapping the active title/identity in the thread header opens a lightweight conversation dropdown so you can switch to another direct session without backing out to the sidebar list first. - On mobile direct-chat threads, tapping the active title/identity in the thread header opens a lightweight conversation dropdown so you can switch to another direct session without backing out to the sidebar list first; long conversation titles now stay readable in the dropdown via wrapped option text and taller touch-friendly rows.
- On mobile (`max-width: 768px`), chat bubbles are slightly wider in full Chat for improved readability while preserving header/composer gutters. - On mobile (`max-width: 768px`), chat bubbles are slightly wider in full Chat for improved readability while preserving header/composer gutters.
- Full Chat tool-call summaries now use a denser mobile layout: grouped and single-call collapsed rows keep icon + label + status on one line (Quick Chat-style scanability) while expanded details remain unchanged. - Full Chat tool-call summaries now use a denser mobile layout: grouped and single-call collapsed rows keep icon + label + status on one line (Quick Chat-style scanability) while expanded details remain unchanged.
- The desktop Chat view toggle and mobile Chat tab now show an unread-response indicator when a live assistant reply arrives for your active chat thread after you leave Chat; opening Chat clears it immediately. - The desktop Chat view toggle and mobile Chat tab now show an unread-response indicator when a live assistant reply arrives for your active chat thread after you leave Chat; opening Chat clears it immediately.

View File

@@ -386,6 +386,7 @@
.chat-mobile-session-trigger { .chat-mobile-session-trigger {
width: 100%; width: 100%;
max-width: 100%; max-width: 100%;
min-width: 0;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;
@@ -398,13 +399,27 @@
min-height: calc(var(--space-lg) * 2); min-height: calc(var(--space-lg) * 2);
} }
.chat-mobile-session-trigger .chat-thread-header-title, .chat-mobile-session-trigger .chat-thread-header-title {
.chat-mobile-session-trigger .chat-model-tag { flex: 1 1 auto;
min-width: 0;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.chat-mobile-session-trigger .chat-model-tag {
flex-shrink: 0;
max-width: calc(var(--space-xl) * 4);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.chat-mobile-session-trigger .provider-icon,
.chat-mobile-session-trigger > svg:first-child {
flex-shrink: 0;
}
.chat-mobile-session-trigger svg:last-child { .chat-mobile-session-trigger svg:last-child {
margin-left: auto; margin-left: auto;
color: var(--text-muted); color: var(--text-muted);
@@ -441,8 +456,8 @@
.chat-mobile-session-option { .chat-mobile-session-option {
width: 100%; width: 100%;
display: flex; display: flex;
align-items: center; align-items: flex-start;
min-height: calc(var(--space-lg) * 2); min-height: calc(var(--space-lg) * 2.25);
border: none; border: none;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
background: transparent; background: transparent;
@@ -467,9 +482,8 @@
.chat-mobile-session-option-title { .chat-mobile-session-option-title {
width: 100%; width: 100%;
white-space: nowrap; white-space: normal;
overflow: hidden; overflow-wrap: anywhere;
text-overflow: ellipsis;
} }
.chat-thread-header-new-chat { .chat-thread-header-new-chat {

View File

@@ -2490,11 +2490,20 @@ describe("ChatView CSS — mobile thread switcher", () => {
it("includes mobile session switcher trigger and dropdown tokenized contracts", () => { it("includes mobile session switcher trigger and dropdown tokenized contracts", () => {
const triggerMatch = css.match(/\.chat-mobile-session-trigger\s*\{([^}]*)\}/); const triggerMatch = css.match(/\.chat-mobile-session-trigger\s*\{([^}]*)\}/);
const dropdownMatch = css.match(/\.chat-mobile-session-dropdown\s*\{([^}]*)\}/); const dropdownMatch = css.match(/\.chat-mobile-session-dropdown\s*\{([^}]*)\}/);
const optionMatch = css.match(/\.chat-mobile-session-option\s*\{([^}]*)\}/);
const optionTitleMatch = css.match(/\.chat-mobile-session-option-title\s*\{([^}]*)\}/);
expect(triggerMatch).toBeTruthy(); expect(triggerMatch).toBeTruthy();
expect(dropdownMatch).toBeTruthy(); expect(dropdownMatch).toBeTruthy();
expect(optionMatch).toBeTruthy();
expect(optionTitleMatch).toBeTruthy();
expect(triggerMatch?.[1]).toContain("min-height: calc(var(--space-lg) * 2)"); expect(triggerMatch?.[1]).toContain("min-height: calc(var(--space-lg) * 2)");
expect(triggerMatch?.[1]).toContain("min-width: 0");
expect(dropdownMatch?.[1]).toContain("background: var(--surface)"); expect(dropdownMatch?.[1]).toContain("background: var(--surface)");
expect(dropdownMatch?.[1]).toContain("border: 1px solid var(--border)"); expect(dropdownMatch?.[1]).toContain("border: 1px solid var(--border)");
expect(optionMatch?.[1]).toContain("min-height: calc(var(--space-lg) * 2.25)");
expect(optionMatch?.[1]).toContain("align-items: flex-start");
expect(optionTitleMatch?.[1]).toContain("white-space: normal");
expect(optionTitleMatch?.[1]).toContain("overflow-wrap: anywhere");
}); });
it("keeps mobile override for header identity overflow visible so dropdown can render", () => { it("keeps mobile override for header identity overflow visible so dropdown can render", () => {