feat(apes): Phase 1b — 补 apes:apps 信息层 tab (4 tab 齐活)
- tabs.tsx: +FiGrid icon, +AppsView placeholder, +apes:apps descriptor (order 902, profile 推到 903) - locales.ts: +apesApps/apesAppsTitle/apesAppsHint zh/en 4 key - tests: 3→4 tab 断言 (id 列表/contract/order 严格递增/disposer/i18n zh+en) 4 个 apes:* tab: tasks(900) / agents(901) / apps(902) / profile(903) gate: typecheck + build + test 1250 passed / 9 skipped / 0 failed (无回归)
This commit is contained in:
@@ -19,12 +19,15 @@ export const apesZh = {
|
|||||||
// + menu tab titles
|
// + menu tab titles
|
||||||
apesTasks: '任务',
|
apesTasks: '任务',
|
||||||
apesAgents: '智能体',
|
apesAgents: '智能体',
|
||||||
|
apesApps: '应用',
|
||||||
apesProfile: '画像',
|
apesProfile: '画像',
|
||||||
// Placeholder view copy
|
// Placeholder view copy
|
||||||
apesTasksTitle: '任务',
|
apesTasksTitle: '任务',
|
||||||
apesTasksHint: 'Phase 1 占位 — 数据接入见 Phase 2',
|
apesTasksHint: 'Phase 1 占位 — 数据接入见 Phase 2',
|
||||||
apesAgentsTitle: '智能体',
|
apesAgentsTitle: '智能体',
|
||||||
apesAgentsHint: 'Phase 1 占位 — 数据接入见 Phase 2',
|
apesAgentsHint: 'Phase 1 占位 — 数据接入见 Phase 2',
|
||||||
|
apesAppsTitle: '应用',
|
||||||
|
apesAppsHint: 'Phase 1 占位 — 数据接入见 Phase 2',
|
||||||
apesProfileTitle: '画像',
|
apesProfileTitle: '画像',
|
||||||
apesProfileHint: 'Phase 1 占位 — 数据接入见 Phase 2',
|
apesProfileHint: 'Phase 1 占位 — 数据接入见 Phase 2',
|
||||||
} as const
|
} as const
|
||||||
@@ -33,11 +36,14 @@ export const apesZh = {
|
|||||||
export const apesEn: Record<keyof typeof apesZh, string> = {
|
export const apesEn: Record<keyof typeof apesZh, string> = {
|
||||||
apesTasks: 'Tasks',
|
apesTasks: 'Tasks',
|
||||||
apesAgents: 'Agents',
|
apesAgents: 'Agents',
|
||||||
|
apesApps: 'Apps',
|
||||||
apesProfile: 'Profile',
|
apesProfile: 'Profile',
|
||||||
apesTasksTitle: 'Tasks',
|
apesTasksTitle: 'Tasks',
|
||||||
apesTasksHint: 'Phase 1 placeholder — data wiring lands in Phase 2',
|
apesTasksHint: 'Phase 1 placeholder — data wiring lands in Phase 2',
|
||||||
apesAgentsTitle: 'Agents',
|
apesAgentsTitle: 'Agents',
|
||||||
apesAgentsHint: 'Phase 1 placeholder — data wiring lands in Phase 2',
|
apesAgentsHint: 'Phase 1 placeholder — data wiring lands in Phase 2',
|
||||||
|
apesAppsTitle: 'Apps',
|
||||||
|
apesAppsHint: 'Phase 1 placeholder — data wiring lands in Phase 2',
|
||||||
apesProfileTitle: 'Profile',
|
apesProfileTitle: 'Profile',
|
||||||
apesProfileHint: 'Phase 1 placeholder — data wiring lands in Phase 2',
|
apesProfileHint: 'Phase 1 placeholder — data wiring lands in Phase 2',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
* Phase 1/1b ship the four information-layer tabs as STATIC placeholders —
|
||||||
* goal is to prove the registration / render / i18n / icon pipeline end to
|
* the goal is to prove the registration / render / i18n / icon pipeline end
|
||||||
* end before wiring real data (Phase 2). Each view renders a titled card
|
* to end before wiring real data (Phase 2). Each view renders a titled card
|
||||||
* with a "TODO: data" hint; no service calls yet.
|
* with a "TODO: data" hint; no service calls yet.
|
||||||
*
|
*
|
||||||
* Tab IDs follow the `apes:*` namespace (the fork's own prefix, distinct
|
* Tab IDs follow the `apes:*` namespace (the fork's own prefix, distinct
|
||||||
* from the upstream builtins' bare ids and from the legacy `wb:*` prefix).
|
* 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).
|
* tab instead of stacking duplicates).
|
||||||
*/
|
*/
|
||||||
import type { ComponentType } from 'react'
|
import type { ComponentType } from 'react'
|
||||||
import {
|
import {
|
||||||
FiList,
|
FiList,
|
||||||
FiCpu,
|
FiCpu,
|
||||||
|
FiGrid,
|
||||||
FiUser,
|
FiUser,
|
||||||
} from 'react-icons/fi'
|
} from 'react-icons/fi'
|
||||||
import type { Context } from '../../context-types.ts'
|
import type { Context } from '../../context-types.ts'
|
||||||
@@ -63,6 +64,9 @@ const TasksView = (_props: TabComponentProps) => (
|
|||||||
const AgentsView = (_props: TabComponentProps) => (
|
const AgentsView = (_props: TabComponentProps) => (
|
||||||
<Placeholder titleKey="apesAgentsTitle" hintKey="apesAgentsHint" />
|
<Placeholder titleKey="apesAgentsTitle" hintKey="apesAgentsHint" />
|
||||||
)
|
)
|
||||||
|
const AppsView = (_props: TabComponentProps) => (
|
||||||
|
<Placeholder titleKey="apesAppsTitle" hintKey="apesAppsHint" />
|
||||||
|
)
|
||||||
const ProfileView = (_props: TabComponentProps) => (
|
const ProfileView = (_props: TabComponentProps) => (
|
||||||
<Placeholder titleKey="apesProfileTitle" hintKey="apesProfileHint" />
|
<Placeholder titleKey="apesProfileTitle" hintKey="apesProfileHint" />
|
||||||
)
|
)
|
||||||
@@ -94,11 +98,19 @@ export function apesTabs(_ctx: Context): readonly TabDescriptor[] {
|
|||||||
single: true,
|
single: true,
|
||||||
component: AgentsView,
|
component: AgentsView,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'apes:apps',
|
||||||
|
title: () => t('apesApps'),
|
||||||
|
icon: iconOf(FiGrid),
|
||||||
|
order: 902,
|
||||||
|
single: true,
|
||||||
|
component: AppsView,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'apes:profile',
|
id: 'apes:profile',
|
||||||
title: () => t('apesProfile'),
|
title: () => t('apesProfile'),
|
||||||
icon: iconOf(FiUser),
|
icon: iconOf(FiUser),
|
||||||
order: 902,
|
order: 903,
|
||||||
single: true,
|
single: true,
|
||||||
component: ProfileView,
|
component: ProfileView,
|
||||||
},
|
},
|
||||||
|
|||||||
+13
-8
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* apes:* tab registration tests (Phase 1): the fork registers its three
|
* apes:* tab registration tests (Phase 1/1b): the fork registers its four
|
||||||
* information-layer tabs (tasks / agents / profile) through the same
|
* information-layer tabs (tasks / agents / apps / profile) through the same
|
||||||
* BetterSidebarService the upstream builtins use (dogfood). The spec pins
|
* BetterSidebarService the upstream builtins use (dogfood). The spec pins
|
||||||
* the registration contract (id / single / order / component presence) and
|
* the registration contract (id / single / order / component presence) and
|
||||||
* the isolated i18n contract (the apes namespace resolves zh vs en through
|
* the isolated i18n contract (the apes namespace resolves zh vs en through
|
||||||
@@ -31,11 +31,11 @@ function setup(): {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('apes:* tab registrations', () => {
|
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()
|
const { service, dispose } = setup()
|
||||||
try {
|
try {
|
||||||
expect(service.getTabs().map(t => t.id).sort()).toEqual(
|
expect(service.getTabs().map(t => t.id).sort()).toEqual(
|
||||||
['apes:agents', 'apes:profile', 'apes:tasks'],
|
['apes:agents', 'apes:apps', 'apes:profile', 'apes:tasks'],
|
||||||
)
|
)
|
||||||
} finally {
|
} finally {
|
||||||
dispose()
|
dispose()
|
||||||
@@ -45,7 +45,7 @@ describe('apes:* tab registrations', () => {
|
|||||||
it('every apes tab is single-instance, visible, and component-carrying', () => {
|
it('every apes tab is single-instance, visible, and component-carrying', () => {
|
||||||
const { service, dispose } = setup()
|
const { service, dispose } = setup()
|
||||||
try {
|
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)
|
const tab = service.getTab(id)
|
||||||
expect(tab, `${id} registered`).toBeDefined()
|
expect(tab, `${id} registered`).toBeDefined()
|
||||||
expect(tab?.single, `${id} single`).toBe(true)
|
expect(tab?.single, `${id} single`).toBe(true)
|
||||||
@@ -64,14 +64,17 @@ describe('apes:* tab registrations', () => {
|
|||||||
const orders = {
|
const orders = {
|
||||||
tasks: service.getTab('apes:tasks')?.order,
|
tasks: service.getTab('apes:tasks')?.order,
|
||||||
agents: service.getTab('apes:agents')?.order,
|
agents: service.getTab('apes:agents')?.order,
|
||||||
|
apps: service.getTab('apes:apps')?.order,
|
||||||
profile: service.getTab('apes:profile')?.order,
|
profile: service.getTab('apes:profile')?.order,
|
||||||
}
|
}
|
||||||
expect(orders.tasks).toBeGreaterThanOrEqual(900)
|
expect(orders.tasks).toBeGreaterThanOrEqual(900)
|
||||||
expect(orders.agents).toBeGreaterThanOrEqual(900)
|
expect(orders.agents).toBeGreaterThanOrEqual(900)
|
||||||
|
expect(orders.apps).toBeGreaterThanOrEqual(900)
|
||||||
expect(orders.profile).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.tasks!).toBeLessThan(orders.agents!)
|
||||||
expect(orders.agents!).toBeLessThan(orders.profile!)
|
expect(orders.agents!).toBeLessThan(orders.apps!)
|
||||||
|
expect(orders.apps!).toBeLessThan(orders.profile!)
|
||||||
} finally {
|
} finally {
|
||||||
dispose()
|
dispose()
|
||||||
}
|
}
|
||||||
@@ -81,7 +84,7 @@ describe('apes:* tab registrations', () => {
|
|||||||
const store = createSidebarStore()
|
const store = createSidebarStore()
|
||||||
const service = createBetterSidebarService(store)
|
const service = createBetterSidebarService(store)
|
||||||
const dispose = registerApesTabs({} as Context, service)
|
const dispose = registerApesTabs({} as Context, service)
|
||||||
expect(service.getTabs()).toHaveLength(3)
|
expect(service.getTabs()).toHaveLength(4)
|
||||||
dispose()
|
dispose()
|
||||||
expect(service.getTabs()).toHaveLength(0)
|
expect(service.getTabs()).toHaveLength(0)
|
||||||
})
|
})
|
||||||
@@ -101,6 +104,7 @@ describe('apes i18n contract', () => {
|
|||||||
attachApesLocale({ getSnapshot: () => ({ active: 'zh-CN' }) })
|
attachApesLocale({ getSnapshot: () => ({ active: 'zh-CN' }) })
|
||||||
expect(t('apesTasks')).toBe('任务')
|
expect(t('apesTasks')).toBe('任务')
|
||||||
expect(t('apesAgents')).toBe('智能体')
|
expect(t('apesAgents')).toBe('智能体')
|
||||||
|
expect(t('apesApps')).toBe('应用')
|
||||||
expect(t('apesProfile')).toBe('画像')
|
expect(t('apesProfile')).toBe('画像')
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -108,6 +112,7 @@ describe('apes i18n contract', () => {
|
|||||||
attachApesLocale({ getSnapshot: () => ({ active: 'en' }) })
|
attachApesLocale({ getSnapshot: () => ({ active: 'en' }) })
|
||||||
expect(t('apesTasks')).toBe('Tasks')
|
expect(t('apesTasks')).toBe('Tasks')
|
||||||
expect(t('apesAgents')).toBe('Agents')
|
expect(t('apesAgents')).toBe('Agents')
|
||||||
|
expect(t('apesApps')).toBe('Apps')
|
||||||
expect(t('apesProfile')).toBe('Profile')
|
expect(t('apesProfile')).toBe('Profile')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user