diff --git a/src/client/apes/locales.ts b/src/client/apes/locales.ts index ebc301f..e487e9b 100644 --- a/src/client/apes/locales.ts +++ b/src/client/apes/locales.ts @@ -19,12 +19,15 @@ export const apesZh = { // + menu tab titles apesTasks: '任务', apesAgents: '智能体', + apesApps: '应用', apesProfile: '画像', // Placeholder view copy apesTasksTitle: '任务', apesTasksHint: 'Phase 1 占位 — 数据接入见 Phase 2', apesAgentsTitle: '智能体', apesAgentsHint: 'Phase 1 占位 — 数据接入见 Phase 2', + apesAppsTitle: '应用', + apesAppsHint: 'Phase 1 占位 — 数据接入见 Phase 2', apesProfileTitle: '画像', apesProfileHint: 'Phase 1 占位 — 数据接入见 Phase 2', } as const @@ -33,11 +36,14 @@ export const apesZh = { export const apesEn: Record = { apesTasks: 'Tasks', apesAgents: 'Agents', + apesApps: 'Apps', apesProfile: 'Profile', apesTasksTitle: 'Tasks', apesTasksHint: 'Phase 1 placeholder — data wiring lands in Phase 2', apesAgentsTitle: 'Agents', apesAgentsHint: 'Phase 1 placeholder — data wiring lands in Phase 2', + apesAppsTitle: 'Apps', + apesAppsHint: 'Phase 1 placeholder — data wiring lands in Phase 2', apesProfileTitle: 'Profile', apesProfileHint: 'Phase 1 placeholder — data wiring lands in Phase 2', } diff --git a/src/client/apes/tabs.tsx b/src/client/apes/tabs.tsx index 243c5c1..f190bd1 100644 --- a/src/client/apes/tabs.tsx +++ b/src/client/apes/tabs.tsx @@ -1,20 +1,21 @@ /** - * apes:* tab descriptors + static placeholder views (Phase 1). + * apes:* tab descriptors + static placeholder views (Phase 1 / 1b). * - * Phase 1 ships the three highest-value tabs as STATIC placeholders — the - * goal is to prove the registration / render / i18n / icon pipeline end to - * end before wiring real data (Phase 2). Each view renders a titled card + * Phase 1/1b ship the four information-layer tabs as STATIC placeholders — + * the goal is to prove the registration / render / i18n / icon pipeline end + * to end before wiring real data (Phase 2). Each view renders a titled card * with a "TODO: data" hint; no service calls yet. * * Tab IDs follow the `apes:*` namespace (the fork's own prefix, distinct * from the upstream builtins' bare ids and from the legacy `wb:*` prefix). - * All three are `single: true` (one instance each — focusing an existing + * All four are `single: true` (one instance each — focusing an existing * tab instead of stacking duplicates). */ import type { ComponentType } from 'react' import { FiList, FiCpu, + FiGrid, FiUser, } from 'react-icons/fi' import type { Context } from '../../context-types.ts' @@ -63,6 +64,9 @@ const TasksView = (_props: TabComponentProps) => ( const AgentsView = (_props: TabComponentProps) => ( ) +const AppsView = (_props: TabComponentProps) => ( + +) const ProfileView = (_props: TabComponentProps) => ( ) @@ -94,11 +98,19 @@ export function apesTabs(_ctx: Context): readonly TabDescriptor[] { single: true, component: AgentsView, }, + { + id: 'apes:apps', + title: () => t('apesApps'), + icon: iconOf(FiGrid), + order: 902, + single: true, + component: AppsView, + }, { id: 'apes:profile', title: () => t('apesProfile'), icon: iconOf(FiUser), - order: 902, + order: 903, single: true, component: ProfileView, }, diff --git a/tests/apes-tabs.spec.ts b/tests/apes-tabs.spec.ts index f8426d4..826a198 100644 --- a/tests/apes-tabs.spec.ts +++ b/tests/apes-tabs.spec.ts @@ -1,6 +1,6 @@ /** - * apes:* tab registration tests (Phase 1): the fork registers its three - * information-layer tabs (tasks / agents / profile) through the same + * apes:* tab registration tests (Phase 1/1b): the fork registers its four + * information-layer tabs (tasks / agents / apps / profile) through the same * BetterSidebarService the upstream builtins use (dogfood). The spec pins * the registration contract (id / single / order / component presence) and * the isolated i18n contract (the apes namespace resolves zh vs en through @@ -31,11 +31,11 @@ function setup(): { } describe('apes:* tab registrations', () => { - it('registers the 3 apes tabs with the apes: namespace', () => { + it('registers the 4 apes tabs with the apes: namespace', () => { const { service, dispose } = setup() try { expect(service.getTabs().map(t => t.id).sort()).toEqual( - ['apes:agents', 'apes:profile', 'apes:tasks'], + ['apes:agents', 'apes:apps', 'apes:profile', 'apes:tasks'], ) } finally { dispose() @@ -45,7 +45,7 @@ describe('apes:* tab registrations', () => { it('every apes tab is single-instance, visible, and component-carrying', () => { const { service, dispose } = setup() try { - for (const id of ['apes:tasks', 'apes:agents', 'apes:profile']) { + for (const id of ['apes:tasks', 'apes:agents', 'apes:apps', 'apes:profile']) { const tab = service.getTab(id) expect(tab, `${id} registered`).toBeDefined() expect(tab?.single, `${id} single`).toBe(true) @@ -64,14 +64,17 @@ describe('apes:* tab registrations', () => { const orders = { tasks: service.getTab('apes:tasks')?.order, agents: service.getTab('apes:agents')?.order, + apps: service.getTab('apes:apps')?.order, profile: service.getTab('apes:profile')?.order, } expect(orders.tasks).toBeGreaterThanOrEqual(900) expect(orders.agents).toBeGreaterThanOrEqual(900) + expect(orders.apps).toBeGreaterThanOrEqual(900) expect(orders.profile).toBeGreaterThanOrEqual(900) - // Strictly ordered: tasks < agents < profile. + // Strictly ordered: tasks < agents < apps < profile. expect(orders.tasks!).toBeLessThan(orders.agents!) - expect(orders.agents!).toBeLessThan(orders.profile!) + expect(orders.agents!).toBeLessThan(orders.apps!) + expect(orders.apps!).toBeLessThan(orders.profile!) } finally { dispose() } @@ -81,7 +84,7 @@ describe('apes:* tab registrations', () => { const store = createSidebarStore() const service = createBetterSidebarService(store) const dispose = registerApesTabs({} as Context, service) - expect(service.getTabs()).toHaveLength(3) + expect(service.getTabs()).toHaveLength(4) dispose() expect(service.getTabs()).toHaveLength(0) }) @@ -101,6 +104,7 @@ describe('apes i18n contract', () => { attachApesLocale({ getSnapshot: () => ({ active: 'zh-CN' }) }) expect(t('apesTasks')).toBe('任务') expect(t('apesAgents')).toBe('智能体') + expect(t('apesApps')).toBe('应用') expect(t('apesProfile')).toBe('画像') }) @@ -108,6 +112,7 @@ describe('apes i18n contract', () => { attachApesLocale({ getSnapshot: () => ({ active: 'en' }) }) expect(t('apesTasks')).toBe('Tasks') expect(t('apesAgents')).toBe('Agents') + expect(t('apesApps')).toBe('Apps') expect(t('apesProfile')).toBe('Profile') })