From edd4ccfc9e738b3414e40a009964e05d0b2624fc Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Sat, 31 Oct 2026 21:59:26 -0700 Subject: [PATCH 1/3] fix(v2): settings uses the page-shell header, left-aligned Look labels, token folded until Generate (TASK-688) Co-Authored-By: Claude Sonnet 5.5 Co-authored-by: Ember Agent-Model: claude/claude-opus-5-5/medium --- .../src/v2/__tests__/V2SettingsPage.test.tsx | 6 ++--- frontend/src/v2/components/V2SettingsPage.tsx | 24 ++++++++----------- frontend/src/v2/v2.css | 19 ++++----------- 3 files changed, 18 insertions(+), 31 deletions(-) diff --git a/frontend/src/v2/__tests__/V2SettingsPage.test.tsx b/frontend/src/v2/__tests__/V2SettingsPage.test.tsx index edb01efad..c59d60f18 100644 --- a/frontend/src/v2/__tests__/V2SettingsPage.test.tsx +++ b/frontend/src/v2/__tests__/V2SettingsPage.test.tsx @@ -241,9 +241,9 @@ describe('V2SettingsPage', () => { }); renderSettings(); - expect(await screen.findByText(/shown once, when generated/i)).toBeInTheDocument(); - expect(screen.getByText(/ends in cret/i)).toBeInTheDocument(); - expect(screen.getByText(/^Created [45] Sep 2026$/)).toBeInTheDocument(); + expect(await screen.findByText(/active · ends in cret/i)).toBeInTheDocument(); + expect(screen.queryByText(/shown once/i)).not.toBeInTheDocument(); + expect(screen.queryByText('••••••••••••••••••••••••••••••••')).not.toBeInTheDocument(); expect(screen.queryByRole('button', { name: 'Copy' })).not.toBeInTheDocument(); expect(screen.queryByRole('button', { name: 'Show' })).not.toBeInTheDocument(); expect(screen.queryByText('cm_user_secret')).not.toBeInTheDocument(); diff --git a/frontend/src/v2/components/V2SettingsPage.tsx b/frontend/src/v2/components/V2SettingsPage.tsx index b94755c0c..63b5a7460 100644 --- a/frontend/src/v2/components/V2SettingsPage.tsx +++ b/frontend/src/v2/components/V2SettingsPage.tsx @@ -5,6 +5,7 @@ import axios from '../../utils/axiosConfig'; import { getAvatarSrc } from '../../utils/avatarUtils'; import { useAuth } from '../../context/AuthContext'; import AppsManagement from '../../components/AppsManagement'; +import '../primitives/primitives.css'; import V2BillingPanel from './V2BillingPanel'; import V2DevicesPanel from './V2DevicesPanel'; import V2DeleteAccount from './V2DeleteAccount'; @@ -488,7 +489,7 @@ const V2ApiTokenSection: React.FC = () => { const maskedToken = '••••••••••••••••••••••••••••••••'; const createdLabel = createdAt ? formatTokenCreatedAt(createdAt) : null; - const statusLabel = hasToken ? 'Active' : 'None'; + const statusLabel = hasToken ? `Active${last4 ? ` · ends in ${last4}` : ''}` : 'None'; return (
@@ -511,24 +512,19 @@ const V2ApiTokenSection: React.FC = () => { )}
- {(error || notice || hasToken) && ( + {(error || notice || token) && (
{error &&

{error}

} {notice &&

{notice}

} - {hasToken && ( + {token && ( <>
{token && showToken ? token : maskedToken} - {token && ( - <> - - - - )} + +
- {!token &&

Shown once, when generated.{last4 ? ` Ends in ${last4}.` : ''}

} {createdLabel &&

{createdLabel}

} )} @@ -590,8 +586,8 @@ const V2SettingsPage: React.FC = () => { return (
-
-

Settings

+

Settings

+
diff --git a/frontend/src/v2/v2.css b/frontend/src/v2/v2.css index 0cb32c445..15b351a56 100644 --- a/frontend/src/v2/v2.css +++ b/frontend/src/v2/v2.css @@ -7789,8 +7789,7 @@ body.modern-ui.v2-canvas { /* ---------- Settings (/v2/settings) ---------- */ .v2-settings { - width: min(100%, 1040px); - margin: 0 auto; + width: 100%; color: var(--v2-text-primary); } @@ -7798,7 +7797,6 @@ body.modern-ui.v2-canvas { display: flex; flex-direction: column; gap: 24px; - padding-top: 40px; } .v2-settings__title { @@ -7880,7 +7878,7 @@ details[open] > .v2-settings__summary > .v2-settings__icon:last-child { transfor .v2-settings__group .v2-settings__developer-disclosure { margin: 0; padding: 12px 14px; } .v2-settings__token-detail { padding: 0 14px 12px; } @media (max-width: 560px) { - .v2-settings__layout { padding-top: 20px; gap: 16px; } + .v2-settings__layout { gap: 16px; } .v2-settings__row > .v2-settings__row-label { min-width: 0; } .v2-settings__token > .v2-settings__row { flex-wrap: wrap; } .v2-settings__row-actions { flex: 1 0 100%; justify-content: flex-end; } @@ -8418,7 +8416,8 @@ details[open] > .v2-settings__summary > .v2-settings__icon:last-child { transfor /* D215 Settings › Look: a label and a segment per row. Language moved here from the rail (TASK-691). */ .v2-settings__look { display: flex; flex-direction: column; } -.v2-settings__look .v2-settings__row { justify-content: space-between; } +.v2-settings__look .v2-settings__row { justify-content: flex-start; } +.v2-settings__look .v2-settings__row > :last-child { margin-left: auto; } .v2-settings__look .v2-settings__row + .v2-settings__row { border-top: 1px solid var(--v2-border-soft); } @@ -8494,15 +8493,7 @@ details[open] > .v2-settings__summary > .v2-settings__icon:last-child { transfor } @media (max-width: 680px) { - .v2-settings__layout { - display: block; - padding-top: 24px; - } - - .v2-settings__title { - margin-bottom: 24px; - font-size: 28px; - } + .v2-settings__layout { display: block; } .v2-settings__token-value { From ce649567d5b72b2ba08025578a44f1c60b16921d Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Sat, 31 Oct 2026 22:00:37 -0700 Subject: [PATCH 2/3] fix(v2): settings header bar runs edge to edge and stays pinned (TASK-688) Co-Authored-By: Claude Sonnet 5.5 Co-authored-by: Ember Agent-Model: claude/claude-opus-5-5/medium --- frontend/src/v2/V2App.tsx | 2 +- frontend/src/v2/v2.css | 13 ++----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/frontend/src/v2/V2App.tsx b/frontend/src/v2/V2App.tsx index 9fef21f9a..06ae05df2 100644 --- a/frontend/src/v2/V2App.tsx +++ b/frontend/src/v2/V2App.tsx @@ -335,7 +335,7 @@ const V2App: React.FC = () => { /> , false, false)} + element={feature('Settings', , false, false, 'v2-feature--settings')} /> .v2-settings__summary > .v2-settings__icon:last-child { transfor flex-direction: column; overflow: hidden; } +.v2-feature--settings > .v2-feature__body { padding: 0; } +.v2-feature--settings .v2p-ph { position: sticky; top: 0; z-index: 2; background: var(--v2-surface); } /* margin auto stops a flex item stretching, so the board sized to its 1240px max-content width and ran off a 390px phone. An explicit width keeps it full width on a phone, and the auto margin still centres it on desktop. */ @@ -10882,7 +10874,6 @@ details[open] > .v2-settings__summary > .v2-settings__icon:last-child { transfor .v2-root:lang(zh) .v2-inspector__detail-name, .v2-root:lang(zh) .v2-login__title, .v2-root:lang(zh) .v2-feature__title, -.v2-root:lang(zh) .v2-settings__title, .v2-root:lang(zh) .v2-settings__label, .v2-root:lang(zh) .v2-settings__account-name, .v2-root:lang(zh) .v2-settings__section .v2-billing__title, From 04935c7e89507c1561aaa7f0f975f61c3c4166e5 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Sat, 31 Oct 2026 22:08:05 -0700 Subject: [PATCH 3/3] test(v2): assert the folded token detail container is absent (TASK-688) Co-Authored-By: Claude Sonnet 5.5 Co-authored-by: Ember Agent-Model: claude/claude-opus-5-5/medium --- frontend/src/v2/__tests__/V2SettingsPage.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/v2/__tests__/V2SettingsPage.test.tsx b/frontend/src/v2/__tests__/V2SettingsPage.test.tsx index c59d60f18..9373db085 100644 --- a/frontend/src/v2/__tests__/V2SettingsPage.test.tsx +++ b/frontend/src/v2/__tests__/V2SettingsPage.test.tsx @@ -244,6 +244,7 @@ describe('V2SettingsPage', () => { expect(await screen.findByText(/active · ends in cret/i)).toBeInTheDocument(); expect(screen.queryByText(/shown once/i)).not.toBeInTheDocument(); expect(screen.queryByText('••••••••••••••••••••••••••••••••')).not.toBeInTheDocument(); + expect(document.querySelector('.v2-settings__token-detail')).toBeNull(); expect(screen.queryByRole('button', { name: 'Copy' })).not.toBeInTheDocument(); expect(screen.queryByRole('button', { name: 'Show' })).not.toBeInTheDocument(); expect(screen.queryByText('cm_user_secret')).not.toBeInTheDocument();