From c64d8a462c785eb5c1c7119cc15983f80102cb64 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Mon, 19 Oct 2026 08:07:57 -0700 Subject: [PATCH] fix(v2): announce compact pod header counts Reuse the translated agent plural form in the compact header and expose full member and agent counts as its accessible name. Keep the Chinese visible count unchanged. Update the older composer contract to expect the singular label. Written by Sprint Impl, a Commonly agent Co-authored-by: sprint-impl Agent-Model: codex/gpt-6-sol/medium --- frontend/src/i18n/__tests__/i18n.test.ts | 18 ++++++++++++++++++ frontend/src/i18n/locales/en.json | 3 ++- frontend/src/i18n/locales/zh-CN.json | 3 ++- frontend/src/v2/__tests__/V2Composer.test.tsx | 3 ++- .../src/v2/__tests__/V2ThreadStarter.test.tsx | 11 +++++++++++ frontend/src/v2/components/V2Thread.tsx | 14 ++++++++++---- 6 files changed, 45 insertions(+), 7 deletions(-) diff --git a/frontend/src/i18n/__tests__/i18n.test.ts b/frontend/src/i18n/__tests__/i18n.test.ts index b1ae453107..f4a9654dde 100644 --- a/frontend/src/i18n/__tests__/i18n.test.ts +++ b/frontend/src/i18n/__tests__/i18n.test.ts @@ -38,6 +38,24 @@ describe('i18n configuration', () => { expect(i18n.t(key, { lng: 'en' })).toBe(expected); }); + it.each([ + [0, '1 · 0 agents', '1 member, 0 agents'], + [1, '1 · 1 agent', '1 member, 1 agent'], + [2, '1 · 2 agents', '1 member, 2 agents'], + ])('pluralizes the compact pod header at %i agents', (count, visible, accessible) => { + const members = i18n.t('podChat.header.members', { lng: 'en', count: 1 }); + const agents = i18n.t('podChat.header.agents', { lng: 'en', count }); + expect(i18n.t('podChat.header.compactMeta', { lng: 'en', members: 1, agents })).toBe(visible); + expect(i18n.t('podChat.header.compactMetaAccessible', { lng: 'en', members, agents })).toBe(accessible); + }); + + it('keeps the Chinese compact count while naming it for assistive tech', () => { + const members = i18n.t('podChat.header.members', { lng: 'zh-CN', count: 1 }); + const agents = i18n.t('podChat.header.agents', { lng: 'zh-CN', count: 1 }); + expect(i18n.t('podChat.header.compactMeta', { lng: 'zh-CN', members: 1, agents })).toBe('1 · 1个智能体'); + expect(i18n.t('podChat.header.compactMetaAccessible', { lng: 'zh-CN', members, agents })).toBe('1名成员,1个智能体'); + }); + it('defaults to English when no stored supported language exists', async () => { const languages = jest.spyOn(window.navigator, 'languages', 'get').mockReturnValue(['en-US']); const language = jest.spyOn(window.navigator, 'language', 'get').mockReturnValue('en-US'); diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json index 0ed63fecb4..db4438f0db 100644 --- a/frontend/src/i18n/locales/en.json +++ b/frontend/src/i18n/locales/en.json @@ -518,7 +518,8 @@ "agents_other": "{{count}} agents", "boardOpen": "board {{count}} open", "backToPods": "Back to pods", - "compactMeta": "{{members}} · {{count}} agents" + "compactMeta": "{{members}} · {{agents}}", + "compactMetaAccessible": "{{members}}, {{agents}}" }, "team": { "view": "View pod team", diff --git a/frontend/src/i18n/locales/zh-CN.json b/frontend/src/i18n/locales/zh-CN.json index 623e7e9b7e..6bcc60ec01 100644 --- a/frontend/src/i18n/locales/zh-CN.json +++ b/frontend/src/i18n/locales/zh-CN.json @@ -517,7 +517,8 @@ "agents_other": "{{count}}个智能体", "boardOpen": "看板{{count}}项待办", "backToPods": "返回 Pod 列表", - "compactMeta": "{{members}} · {{count}}个智能体" + "compactMeta": "{{members}} · {{agents}}", + "compactMetaAccessible": "{{members}},{{agents}}" }, "team": { "view": "查看 Pod 团队", diff --git a/frontend/src/v2/__tests__/V2Composer.test.tsx b/frontend/src/v2/__tests__/V2Composer.test.tsx index 02da3266c1..fbd52e7558 100644 --- a/frontend/src/v2/__tests__/V2Composer.test.tsx +++ b/frontend/src/v2/__tests__/V2Composer.test.tsx @@ -97,7 +97,8 @@ describe('V2Composer send button', () => { const meta = header.querySelector('.v2-pod-header__meta:not(.v2-pod-header__meta--compact)'); expect(meta).toHaveTextContent('2 members'); expect(meta).toHaveTextContent('1 agent'); - expect(header.querySelector('.v2-pod-header__meta--compact')).toHaveTextContent('2 · 1 agents'); + expect(header.querySelector('.v2-pod-header__meta--compact')).toHaveTextContent('2 · 1 agent'); + expect(view.getByRole('group', { name: '2 members, 1 agent' })).toBeInTheDocument(); expect(view.container.querySelector('.v2-thread__working')).toBeNull(); }); diff --git a/frontend/src/v2/__tests__/V2ThreadStarter.test.tsx b/frontend/src/v2/__tests__/V2ThreadStarter.test.tsx index 099ddcdbfb..e30c7050c5 100644 --- a/frontend/src/v2/__tests__/V2ThreadStarter.test.tsx +++ b/frontend/src/v2/__tests__/V2ThreadStarter.test.tsx @@ -106,6 +106,17 @@ describe('V2Thread teaching empty states', () => { expect(document.querySelector('.v2-thread__empty-line')).toHaveTextContent('no messages yet'); }); + test('compact header shows singular agents and names both counts for assistive tech', () => { + const detail = makeDetail({ + agents: [{ agentName: 'openclaw', instanceId: 'aria', displayName: 'Aria' }], + }); + renderChat(detail); + + const compact = screen.getByRole('group', { name: '1 member, 1 agent' }); + expect(compact).toHaveTextContent('1 · 1 agent'); + expect(compact.querySelector('[aria-hidden="true"]')).toHaveTextContent('1 · 1 agent'); + }); + test('multi-member regular pods use the same teaching state', () => { renderChat(makeDetail({ members: [ diff --git a/frontend/src/v2/components/V2Thread.tsx b/frontend/src/v2/components/V2Thread.tsx index e6141d4621..f92f31c877 100644 --- a/frontend/src/v2/components/V2Thread.tsx +++ b/frontend/src/v2/components/V2Thread.tsx @@ -1564,9 +1564,11 @@ const V2Thread: React.FC = ({ detail, firstRunVisible = false, in // Header meta (direction C): members · agents · board N open · bound // channels. The agents-working count left the header for the inspector. const humanMemberCount = (members || []).filter((member) => !member?.isBot).length; + const memberCountText = t('podChat.header.members', { count: humanMemberCount }); + const agentCountText = t('podChat.header.agents', { count: agents.length }); const metaParts: React.ReactNode[] = [ - {t('podChat.header.members', { count: humanMemberCount })}, - {t('podChat.header.agents', { count: agents.length })}, + {memberCountText}, + {agentCountText}, ]; if (headerMeta.boardOpen !== null) { metaParts.push( @@ -1591,8 +1593,12 @@ const V2Thread: React.FC = ({ detail, firstRunVisible = false, in {pod.description &&

{pod.description}

} -