Companion fixes from a full dashboard QA pass (every page dogfooded live), on top of the cherry-picked #31863 header-slot fix: - ChatPage: harden the header-slot effect further — useLayoutEffect and never write the slot while inactive, so the handoff commentary and ownership rule live next to the code. - LogsPage: level classification used raw substring matching, so INFO lines carrying 'parse_errors=0' (or paths like errors.log) rendered red. New unit-tested classifier (web/src/lib/log-classify.ts) anchors on the hermes_logging level token with a word-boundary fallback. - Channels API: plugin platforms (irc, ntfy, photon, teams, …) rendered as nameless title-cased cards ('Irc', 'Ntfy') with empty descriptions. Two root causes: (1) plugin discovery never ran in the dashboard server process, so plugin_entries() was empty; (2) Platform enum pseudo-members claimed plugin ids before the registry could attach labels. The catalog now discovers plugins explicitly and resolves plugin metadata first; added descriptions + docs links for bundled plugin platforms and the msgraph_webhook / whatsapp_cloud / relay enum members. Regression test sabotage-verified against the old enum-first ordering. - Config schema: updates.refresh_cua_driver declared type 'bool' (schema vocabulary is 'boolean'), so the switch rendered as a text input holding 'true'. - Page titles: '/mcp' rendered as 'Mcp' via the naive capitalize fallback; literal-label table now covers MCP/Files/Channels/Webhooks/ Pairing/System (unit-tested). - AuthWidget: skip the guaranteed-401 /api/auth/me probe in loopback mode — every dashboard load logged a console error for nothing. - Model picker: with no filter, providers that actually have models float above the wall of '0 models' rows. - Cron: empty state now carries an actionable Create button.
58 lines
1.5 KiB
TypeScript
58 lines
1.5 KiB
TypeScript
import type { Translations } from "@/i18n/types";
|
|
|
|
const BUILTIN: Record<string, keyof Translations["app"]["nav"]> = {
|
|
"/chat": "chat",
|
|
"/sessions": "sessions",
|
|
"/analytics": "analytics",
|
|
"/models": "models",
|
|
"/logs": "logs",
|
|
"/cron": "cron",
|
|
"/skills": "skills",
|
|
"/plugins": "plugins",
|
|
"/profiles": "profiles",
|
|
"/config": "config",
|
|
"/env": "keys",
|
|
"/docs": "documentation",
|
|
};
|
|
|
|
// Built-in routes without an i18n nav key. Keep these in sync with the
|
|
// sidebar labels in App.tsx — the naive capitalize fallback below mangles
|
|
// initialisms ("/mcp" → "Mcp") and can't match multi-word labels.
|
|
const BUILTIN_LITERAL: Record<string, string> = {
|
|
"/files": "Files",
|
|
"/mcp": "MCP",
|
|
"/channels": "Channels",
|
|
"/webhooks": "Webhooks",
|
|
"/pairing": "Pairing",
|
|
"/system": "System",
|
|
};
|
|
|
|
export function resolvePageTitle(
|
|
pathname: string,
|
|
t: Translations,
|
|
pluginTabs: { path: string; label: string }[],
|
|
): string {
|
|
const normalized = pathname.replace(/\/$/, "") || "/";
|
|
if (normalized === "/") {
|
|
return t.app.nav.sessions;
|
|
}
|
|
const plugin = pluginTabs.find((p) => p.path === normalized);
|
|
if (plugin) {
|
|
return plugin.label;
|
|
}
|
|
const key = BUILTIN[normalized];
|
|
if (key) {
|
|
return t.app.nav[key];
|
|
}
|
|
const literal = BUILTIN_LITERAL[normalized];
|
|
if (literal) {
|
|
return literal;
|
|
}
|
|
// Derive title from pathname: "/profiles" → "Profiles"
|
|
const segment = normalized.slice(1);
|
|
if (segment) {
|
|
return segment.charAt(0).toUpperCase() + segment.slice(1);
|
|
}
|
|
return t.app.webUi;
|
|
}
|