feat(web): NetX workbench, UME-only UI, i18n, and header status

- Replace sidebar app with workbench home and module tabs (openOrFocusModule)

- Remove legacy pages; add module registry, shared tab channel, WEB.md

- Add zh/en i18n, global ToastProvider, restore oclaw bridge pill in header

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-05-27 11:18:30 +08:00
parent 664775927b
commit 282dbe75af
33 changed files with 1631 additions and 1677 deletions

56
web/src/config/modules.ts Normal file
View file

@ -0,0 +1,56 @@
/**
* Single registry for workbench modules. Add new modules here only.
*/
export type ModuleIconTone = "blue" | "green" | "amber";
export type WorkbenchSection = "monitoring";
export type ModuleDefinition = {
moduleId: string;
path: string;
section: WorkbenchSection;
labelKey: string;
descKey?: string;
iconTone: ModuleIconTone;
titleKey: string;
};
export const MODULES: readonly ModuleDefinition[] = [
{
moduleId: "ume",
path: "/ume",
section: "monitoring",
labelKey: "workbench.cards.umeSync",
descKey: "workbench.cards.umeSyncDesc",
iconTone: "blue",
titleKey: "layout.titleUme",
},
] as const;
export function getModuleById(moduleId: string): ModuleDefinition | undefined {
return MODULES.find((m) => m.moduleId === moduleId);
}
export function moduleIdFromPath(pathname: string): string | null {
for (const m of MODULES) {
if (pathname === m.path || pathname.startsWith(`${m.path}/`)) return m.moduleId;
}
return null;
}
export function getPageTitleKey(pathname: string): string {
const moduleId = moduleIdFromPath(pathname);
if (moduleId) {
const mod = getModuleById(moduleId);
if (mod) return mod.titleKey;
}
return "workbench.title";
}
export function modulesInSection(section: WorkbenchSection): ModuleDefinition[] {
return MODULES.filter((m) => m.section === section);
}
export function isWorkbenchPath(pathname: string): boolean {
return pathname === "/" || pathname === "/workbench";
}