From 330507c8d71e5434ace30c6481037e5de886d440 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Tue, 8 Sep 2026 00:00:08 -0700 Subject: [PATCH 1/2] feat(v2): align agent profile tokens --- .../v2/__tests__/v2-layout-invariants.test.ts | 43 ++++++++++++++++++ frontend/src/v2/agents/v2-agent-profile.css | 45 +++++++++++-------- 2 files changed, 70 insertions(+), 18 deletions(-) diff --git a/frontend/src/v2/__tests__/v2-layout-invariants.test.ts b/frontend/src/v2/__tests__/v2-layout-invariants.test.ts index 696c80c7f..656097e23 100644 --- a/frontend/src/v2/__tests__/v2-layout-invariants.test.ts +++ b/frontend/src/v2/__tests__/v2-layout-invariants.test.ts @@ -669,6 +669,49 @@ describe('v2 layout invariants (CSS rule presence)', () => { expect(ruleBody(aprofile, '.v2-root.v2-aprofile')).toContain('overflow-y: auto'); }); + test('agent profile Phase B uses ink actions, the halo, and the type ladder', () => { + const primary = ruleBody(aprofile, '.v2-aprofile__btn--primary'); + expect(primary).toContain('background: var(--v2-ink)'); + expect(primary).toContain('border-color: var(--v2-ink)'); + expect(primary).not.toContain('var(--v2-accent)'); + expect(ruleBody(aprofile, '.v2-aprofile__btn--primary:hover')).toContain('var(--v2-ink-hover)'); + expect(ruleBody(aprofile, '.v2-root .v2-aprofile__btn--primary')).toContain('color: var(--v2-on-ink)'); + + const profileFocusRules = aprofile.split('}').filter((block) => { + const brace = block.indexOf('{'); + if (brace === -1) return false; + const selector = block.slice(0, brace); + return selector.includes('.v2-aprofile') && selector.includes(':focus-visible'); + }); + expect(profileFocusRules.length).toBeGreaterThan(0); + for (const block of profileFocusRules) expect(block.slice(block.indexOf('{'))).not.toContain('outline: 2px'); + const avatarFocus = ruleBody(aprofile, '.v2-root button.v2-aprofile__avatar-button:focus-visible'); + expect(avatarFocus).toContain('outline: none'); + expect(avatarFocus).toContain('box-shadow: var(--v2-focus-ring)'); + + expect(aprofile.match(/font-size:\s*13px/g) || []).toHaveLength(0); + for (const selector of ['.v2-aprofile__empty-title', '.v2-aprofile__name', '.v2-aprofile__stat-num']) { + expect(ruleBody(aprofile, selector)).toContain('font-weight: 700'); + } + expect(aprofile).not.toContain('font-weight: 850'); + }); + + test('agent profile enumerated rows use transparent borders and background-only hover', () => { + for (const selector of ['.v2-aprofile__pod', '.v2-aprofile__memnote']) { + const row = ruleBody(aprofile, selector); + expect(row).toContain('border: 1px solid transparent'); + expect(row).toContain('border-radius: var(--v2-radius)'); + expect(row).toContain('padding: 12px 14px'); + const hover = ruleBody(aprofile, `${selector}:hover`); + const declarations = hover.slice(hover.indexOf('{') + 1) + .split(';').map((declaration) => declaration.trim()).filter(Boolean); + expect(declarations.length).toBeGreaterThan(0); + for (const declaration of declarations) expect(declaration).toMatch(/^background/); + } + expect(aprofile).not.toContain('.v2-aprofile__pod:first-child'); + expect(aprofile).not.toContain('.v2-aprofile__memnote:first-child'); + }); + test('the nav rail pane lets its language dropdown escape the pane clip', () => { // The rail is 76px wide; its language menu is a wider floating popover. // Base `.v2-pane { overflow: hidden }` clips it to the rail edge, so the diff --git a/frontend/src/v2/agents/v2-agent-profile.css b/frontend/src/v2/agents/v2-agent-profile.css index 03fdb65dc..35da2ccf7 100644 --- a/frontend/src/v2/agents/v2-agent-profile.css +++ b/frontend/src/v2/agents/v2-agent-profile.css @@ -51,12 +51,12 @@ border: 1px solid transparent; white-space: nowrap; } -.v2-aprofile__btn--primary { background: var(--v2-accent); border-color: var(--v2-accent); } -.v2-aprofile__btn--primary:hover { background: var(--v2-accent-strong); border-color: var(--v2-accent-strong); } +.v2-aprofile__btn--primary { background: var(--v2-ink); border-color: var(--v2-ink); } +.v2-aprofile__btn--primary:hover { background: var(--v2-ink-hover); border-color: var(--v2-ink-hover); } .v2-aprofile__btn--ghost { background: var(--v2-surface); border-color: var(--v2-border-strong, var(--v2-border)); } .v2-aprofile__btn--ghost:hover { background: var(--v2-surface-hover); } /* `.v2-root a { color: inherit }` (0,1,1) beats the single-class rules — re-assert. */ -.v2-root .v2-aprofile__btn--primary { color: #ffffff; } +.v2-root .v2-aprofile__btn--primary { color: var(--v2-on-ink); } .v2-root .v2-aprofile__btn--ghost { color: var(--v2-text-primary); } .v2-root button.v2-aprofile__btn:disabled { cursor: wait; opacity: 0.72; } .v2-aprofile__room-error { margin: 12px 0 0; color: var(--v2-danger); font-size: 14px; } @@ -67,7 +67,7 @@ max-width: 1040px; margin: 0 auto; padding: 120px 24px; display: flex; flex-direction: column; align-items: center; text-align: center; gap: 12px; } -.v2-aprofile__empty-title { font-size: 24px; font-weight: 850; letter-spacing: -0.02em; margin: 0; } +.v2-aprofile__empty-title { font-size: 24px; font-weight: 700; letter-spacing: -0.02em; margin: 0; } .v2-aprofile__muted { color: var(--v2-text-tertiary); font-size: 14px; line-height: 1.5; } .v2-aprofile__cta-row { display: flex; gap: 10px; margin-top: 8px; flex-wrap: wrap; justify-content: center; } @@ -85,7 +85,7 @@ .v2-aprofile__hero-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 14px; } .v2-aprofile__hero-text { flex: 1 1 auto; min-width: 0; } .v2-aprofile__name-row { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; } -.v2-aprofile__name { margin: 0; font-size: 24px; font-weight: 850; letter-spacing: -0.02em; color: var(--v2-text-primary); } +.v2-aprofile__name { margin: 0; font-size: 24px; font-weight: 700; letter-spacing: -0.02em; color: var(--v2-text-primary); } .v2-aprofile__runtime { font-size: 11px; font-weight: 600; letter-spacing: 0.03em; text-transform: uppercase; color: var(--v2-text-secondary); background: var(--v2-bg-subtle); @@ -96,7 +96,7 @@ background: var(--v2-accent-soft); padding: 2px 8px; border-radius: var(--v2-radius-pill); } .v2-aprofile__desc { margin: 8px 0 0; font-size: 15px; line-height: 1.5; color: var(--v2-text-secondary); } -.v2-aprofile__meta { margin-top: 10px; font-size: 13px; color: var(--v2-text-tertiary); display: flex; gap: 6px; flex-wrap: wrap; } +.v2-aprofile__meta { margin-top: 10px; font-size: var(--v2-fs-meta); line-height: var(--v2-lh-meta); color: var(--v2-text-tertiary); display: flex; gap: 6px; flex-wrap: wrap; } /* Cards grid */ .v2-aprofile__grid { @@ -111,7 +111,7 @@ padding: 20px; } .v2-aprofile__card-title { - margin: 0 0 14px; font-size: 13px; font-weight: 700; text-transform: uppercase; + margin: 0 0 14px; font-size: var(--v2-fs-label); line-height: var(--v2-lh-label); font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; color: var(--v2-text-tertiary); display: flex; align-items: center; gap: 8px; } @@ -134,8 +134,8 @@ .v2-aprofile__memory { display: flex; flex-direction: column; gap: 10px; } .v2-aprofile__stat { display: flex; align-items: baseline; gap: 8px; } -.v2-aprofile__stat-num { font-size: 34px; font-weight: 850; letter-spacing: -0.03em; color: var(--v2-accent); } -.v2-aprofile__stat-label { font-size: 13px; color: var(--v2-text-secondary); } +.v2-aprofile__stat-num { font-size: 34px; font-weight: 700; letter-spacing: -0.03em; color: var(--v2-accent); } +.v2-aprofile__stat-label { font-size: var(--v2-fs-meta); line-height: var(--v2-lh-meta); color: var(--v2-text-secondary); } /* Memory index (owner/admin private view) */ .v2-aprofile__card--wide { grid-column: 1 / -1; } @@ -157,9 +157,13 @@ letter-spacing: 0.05em; color: var(--v2-text-secondary); display: flex; align-items: center; gap: 6px; } -.v2-aprofile__memnote { display: flex; flex-direction: column; gap: 2px; padding: 6px 0; border-top: 1px solid var(--v2-border-soft); } -.v2-aprofile__memnote:first-child { border-top: none; padding-top: 0; } -.v2-aprofile__memnote-h { font-size: 13px; font-weight: 600; color: var(--v2-text-primary); } +.v2-aprofile__memnote { + display: flex; flex-direction: column; gap: 2px; padding: 12px 14px; + border: 1px solid transparent; border-radius: var(--v2-radius); + transition: background 80ms ease; +} +.v2-aprofile__memnote:hover { background: var(--v2-surface-hover); } +.v2-aprofile__memnote-h { font-size: var(--v2-fs-body); line-height: var(--v2-lh-body); font-weight: 600; color: var(--v2-text-primary); } .v2-aprofile__memnote-s { font-size: 12px; color: var(--v2-text-tertiary); line-height: 1.45; } .v2-aprofile__more { font-size: 12px; color: var(--v2-text-tertiary); margin-top: 8px; display: inline-block; } @@ -172,14 +176,18 @@ .v2-aprofile__scroll::-webkit-scrollbar-track { background: transparent; } /* Pods list */ -.v2-aprofile__pod { display: flex; flex-direction: column; gap: 2px; padding: 8px 0; border-top: 1px solid var(--v2-border-soft); } -.v2-aprofile__pod:first-child { border-top: none; padding-top: 0; } +.v2-aprofile__pod { + display: flex; flex-direction: column; gap: 2px; padding: 12px 14px; + border: 1px solid transparent; border-radius: var(--v2-radius); + transition: background 80ms ease; +} +.v2-aprofile__pod:hover { background: var(--v2-surface-hover); } .v2-root .v2-aprofile__pod-name { font-size: 14px; font-weight: 600; color: var(--v2-accent); text-decoration: none; } .v2-root .v2-aprofile__pod-name:hover { text-decoration: underline; } .v2-aprofile__pod-msg { font-size: 12.5px; color: var(--v2-text-tertiary); line-height: 1.4; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } .v2-aprofile__pod-when { font-size: 11.5px; color: var(--v2-text-muted, var(--v2-text-tertiary)); } -.v2-aprofile__run { display: flex; align-items: center; gap: 8px; font-size: 13px; } +.v2-aprofile__run { display: flex; align-items: center; gap: 8px; font-size: var(--v2-fs-body); line-height: var(--v2-lh-body); } .v2-aprofile__run-label { font-weight: 500; color: var(--v2-text-primary); } .v2-aprofile__run-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; background: var(--v2-text-muted); } .v2-aprofile__run-dot--succeeded { background: var(--v2-success); } @@ -233,8 +241,8 @@ line-height: 0; } .v2-root button.v2-aprofile__avatar-button:focus-visible { - outline: 2px solid var(--v2-accent); - outline-offset: 2px; + outline: none; + box-shadow: var(--v2-focus-ring); } .v2-aprofile__avatar-hint { position: absolute; @@ -294,7 +302,8 @@ .v2-aprofile__hero { position: relative; } .v2-aprofile__avatar-title { margin-bottom: 10px; - font-size: 13px; + font-size: var(--v2-fs-label); + line-height: var(--v2-lh-label); font-weight: 600; color: var(--v2-text); } From 41327d8ba94bec71b15d0d260f37b2af3322ba8d Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Tue, 8 Sep 2026 00:03:32 -0700 Subject: [PATCH 2/2] feat(v2): ink agent profile footer CTA --- frontend/src/v2/__tests__/v2-layout-invariants.test.ts | 4 ++++ frontend/src/v2/agents/v2-agent-profile.css | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/frontend/src/v2/__tests__/v2-layout-invariants.test.ts b/frontend/src/v2/__tests__/v2-layout-invariants.test.ts index 656097e23..91fee480b 100644 --- a/frontend/src/v2/__tests__/v2-layout-invariants.test.ts +++ b/frontend/src/v2/__tests__/v2-layout-invariants.test.ts @@ -676,6 +676,10 @@ describe('v2 layout invariants (CSS rule presence)', () => { expect(primary).not.toContain('var(--v2-accent)'); expect(ruleBody(aprofile, '.v2-aprofile__btn--primary:hover')).toContain('var(--v2-ink-hover)'); expect(ruleBody(aprofile, '.v2-root .v2-aprofile__btn--primary')).toContain('color: var(--v2-on-ink)'); + const footerPrimary = ruleBody(aprofile, '.v2-root .v2-aprofile__footer-cta .v2-aprofile__btn--primary'); + expect(footerPrimary).toContain('var(--v2-ink)'); + expect(footerPrimary).not.toContain('var(--v2-accent)'); + expect(ruleBody(aprofile, '.v2-root .v2-aprofile__footer-cta .v2-aprofile__btn--primary:hover')).toContain('var(--v2-ink-hover)'); const profileFocusRules = aprofile.split('}').filter((block) => { const brace = block.indexOf('{'); diff --git a/frontend/src/v2/agents/v2-agent-profile.css b/frontend/src/v2/agents/v2-agent-profile.css index 35da2ccf7..e81f41c8f 100644 --- a/frontend/src/v2/agents/v2-agent-profile.css +++ b/frontend/src/v2/agents/v2-agent-profile.css @@ -199,8 +199,12 @@ background: var(--v2-accent-deep); border-radius: var(--v2-radius-lg); color: rgba(255, 255, 255, 0.85); font-size: 15px; } -.v2-root .v2-aprofile__footer-cta .v2-aprofile__btn--primary { background: #fff; color: var(--v2-accent); border-color: #fff; } -.v2-root .v2-aprofile__footer-cta .v2-aprofile__btn--primary:hover { background: rgba(255,255,255,0.9); } +.v2-root .v2-aprofile__footer-cta .v2-aprofile__btn--primary { + background: var(--v2-ink); color: var(--v2-on-ink); border-color: var(--v2-ink); +} +.v2-root .v2-aprofile__footer-cta .v2-aprofile__btn--primary:hover { + background: var(--v2-ink-hover); border-color: var(--v2-ink-hover); +} @media (max-width: 760px) { .v2-aprofile__hero { flex-direction: column; align-items: flex-start; }