- useTasksPrefs:ctx.settings.register('apesSidebar.tasks') namespace,
uSES 观察 + update 写回,applies:'live' 跨 reload 持久化
- tasks 面板加 showInactive 开关:OFF 时只渲染 running 会话
- filterTasksRows 纯函数导出(hook 需渲染,过滤决策单测锁定)
- locales:tasks.showInactive zh/en
- tests/tabs.spec.ts:+4 个 per-tab state 过滤测试(25→29)
- AGENTS.md:Phase 2 更新(per-tab state 落地,scratch 冒烟 +
apps·msgs·profile feed 延后——better-sidebar 无对应数据源 + 无 dsh CLI)
门禁:typecheck rc=0 · build rc=0 · test 29/29
137 lines
4.4 KiB
TypeScript
137 lines
4.4 KiB
TypeScript
/**
|
|
* apes-sidebar i18n dictionaries, registered into the DSH locale runtime
|
|
* under the `apesSidebar` namespace (follows the DSH i18n system: the
|
|
* active locale is the Host-backed preference, not the raw browser language).
|
|
*
|
|
* zh + en are primary. A third language falls back to en (the better-sidebar
|
|
* convention: every new zh key must also have an en entry).
|
|
*
|
|
* The module tracks the active locale in a small external store so `t()`
|
|
* always resolves against the current locale, and React components re-render
|
|
* on locale change via {@link useApesLocale} (useSyncExternalStore).
|
|
*/
|
|
import { useSyncExternalStore } from 'react'
|
|
import type { ApesLocaleService } from '../context-types.ts'
|
|
|
|
const NS = 'apesSidebar'
|
|
|
|
const zh: Record<string, string> = {
|
|
'tab.tasks.title': '任务',
|
|
'tab.agents.title': 'Agent',
|
|
'tab.messages.title': '消息',
|
|
'tab.apps.title': '应用',
|
|
'tab.profile.title': '账户',
|
|
'tasks.empty': '暂无会话',
|
|
'tasks.running': '运行中',
|
|
'tasks.current': '当前',
|
|
'tasks.subagent': '子代理',
|
|
'tasks.showInactive': '显示非运行会话',
|
|
'agents.empty': '暂无子代理',
|
|
'agents.hint': '子代理会话会在此按父会话分组展示。',
|
|
'apps.empty': '暂无应用',
|
|
'apps.hint': '工作区应用与外部入口将在此展示。',
|
|
'messages.empty': '暂无消息',
|
|
'messages.hint': '会话消息流与提醒将在此汇总。',
|
|
'profile.model': '模型',
|
|
'profile.provider': '提供商',
|
|
'profile.unknown': '未知',
|
|
'profile.hint': '账户与运行配置概览。',
|
|
}
|
|
|
|
const en: Record<string, string> = {
|
|
'tab.tasks.title': 'Tasks',
|
|
'tab.agents.title': 'Agents',
|
|
'tab.messages.title': 'Messages',
|
|
'tab.apps.title': 'Apps',
|
|
'tab.profile.title': 'Profile',
|
|
'tasks.empty': 'No sessions yet',
|
|
'tasks.running': 'running',
|
|
'tasks.current': 'current',
|
|
'tasks.subagent': 'subagent',
|
|
'tasks.showInactive': 'Show inactive sessions',
|
|
'agents.empty': 'No subagents yet',
|
|
'agents.hint': 'Subagent sessions appear here, grouped by parent.',
|
|
'apps.empty': 'No apps yet',
|
|
'apps.hint': 'Workspace apps and external entry points appear here.',
|
|
'messages.empty': 'No messages yet',
|
|
'messages.hint': 'Session message flow and reminders are aggregated here.',
|
|
'profile.model': 'Model',
|
|
'profile.provider': 'Provider',
|
|
'profile.unknown': 'Unknown',
|
|
'profile.hint': 'Account and runtime configuration overview.',
|
|
}
|
|
|
|
const DICTS: Record<string, Record<string, string>> = { zh, en }
|
|
|
|
/** The currently resolved active locale code ('zh' default). */
|
|
let activeLocale = 'zh'
|
|
|
|
const listeners = new Set<() => void>()
|
|
|
|
function notify(): void {
|
|
for (const fn of listeners) fn()
|
|
}
|
|
|
|
/** The active locale snapshot (uSES-safe: returns the same code string). */
|
|
export function getActiveLocale(): string {
|
|
return activeLocale
|
|
}
|
|
|
|
export function subscribeLocale(fn: () => void): () => void {
|
|
listeners.add(fn)
|
|
return () => {
|
|
listeners.delete(fn)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Re-render the calling component whenever the active locale changes.
|
|
* Returns the active locale code; components re-resolve `t(key)` against it.
|
|
*/
|
|
export function useApesLocale(): string {
|
|
return useSyncExternalStore(subscribeLocale, getActiveLocale, getActiveLocale)
|
|
}
|
|
|
|
/** Translate a key in the active locale; falls back to en, then the key. */
|
|
export function t(key: string): string {
|
|
const dict = DICTS[activeLocale] ?? en
|
|
return dict[key] ?? en[key] ?? key
|
|
}
|
|
|
|
/**
|
|
* Register the dictionaries into the locale runtime AND subscribe to its
|
|
* active-locale changes, keeping {@link activeLocale} in sync. Returns the
|
|
* combined disposer (locale registrations + the snapshot subscription).
|
|
*/
|
|
export function registerLocales(locale: ApesLocaleService | undefined): () => void {
|
|
if (!locale) return () => undefined
|
|
const disposes = [
|
|
locale.register(NS, 'zh', zh),
|
|
locale.register(NS, 'en', en),
|
|
]
|
|
let unsub: (() => void) | undefined
|
|
try {
|
|
// Seed the active locale from the runtime snapshot (uSES-safe read).
|
|
activeLocale = locale.getSnapshot().active || 'zh'
|
|
unsub = locale.subscribe(() => {
|
|
const next = locale.getSnapshot().active || 'en'
|
|
if (next !== activeLocale) {
|
|
activeLocale = next
|
|
notify()
|
|
}
|
|
})
|
|
} catch {
|
|
// Locale runtime unavailable (very early boot): stay on the default.
|
|
}
|
|
return () => {
|
|
unsub?.()
|
|
for (const d of disposes) {
|
|
try {
|
|
d()
|
|
} catch {
|
|
// Disposer already run (double-dispose) — ignore.
|
|
}
|
|
}
|
|
}
|
|
}
|