Files
fusion/packages/dashboard/app/plugins/pluginViewRegistry.tsx
gsxdsm d4d7623ee0 FN-6749: rebaseline i18n lint guardrail
Rebaseline the i18n hardcoded-string lint guardrail for shipping dashboard surfaces.

- Add scoped i18n lint ignores for non-shipping tests, stories, and deferred source-copy files with follow-up rationale.
- Localize remaining plugin/view copy and replace technical glyph literals that should not be linted as prose.
- Refresh synced locale catalogs and generated resource types, and document the lint baseline policy.

Files changed:
 .changeset/fn-6749-i18n-lint-rebaseline.md         |    5 +
 docs/i18n-contributing.md                          |   14 +
 i18next.config.ts                                  |   96 ++
 .../dashboard/app/components/AgentDetailView.tsx   |    4 +-
 .../app/components/ConversationHistory.tsx         |    4 +-
 .../app/components/DockerNodeOnboardingModal.tsx   |    8 +-
 .../components/MilestoneSliceInterviewModal.tsx    |    4 +-
 .../app/components/MissionInterviewModal.tsx       |    4 +-
 .../app/components/ModelSelectionModal.tsx         |    4 +-
 .../dashboard/app/components/NodeDetailModal.tsx   |   15 +-
 .../dashboard/app/components/PlanningModeModal.tsx |    4 +-
 .../dashboard/app/components/SessionTerminal.tsx   |   15 +-
 .../app/components/SubtaskBreakdownModal.tsx       |    4 +-
 packages/dashboard/app/components/TaskForm.tsx     |    4 +-
 .../dashboard/app/components/TerminalModal.tsx     |   18 +-
 .../dashboard/app/plugins/pluginViewRegistry.tsx   |    7 +-
 packages/i18n/locales/en/app.json                  |  890 +++++++++---
 packages/i18n/locales/en/common.json               |   82 +-
 packages/i18n/locales/es/app.json                  |  986 ++++++++++---
 packages/i18n/locales/es/common.json               |   82 +-
 packages/i18n/locales/fr/app.json                  |  986 ++++++++++---
 packages/i18n/locales/fr/common.json               |   82 +-
 packages/i18n/locales/ko/app.json                  | 1482 ++++++++++++++------
 packages/i18n/locales/ko/cli.json                  |    8 +-
 packages/i18n/locales/ko/common.json               |   82 +-
 packages/i18n/locales/zh-CN/app.json               | 1482 ++++++++++++++------
 packages/i18n/locales/zh-CN/cli.json               |    8 +-
 packages/i18n/locales/zh-CN/common.json            |   82 +-
 packages/i18n/locales/zh-TW/app.json               | 1482 ++++++++++++++------
 packages/i18n/locales/zh-TW/cli.json               |    8 +-
 packages/i18n/locales/zh-TW/common.json            |   82 +-
 packages/i18n/src/resources.d.ts                   |  459 +++---
 32 files changed, 6055 insertions(+), 2438 deletions(-)

Fusion-Task-Id: FN-6749

Fusion-Task-Lineage: 789a7cbb-fdf8-4190-8cd2-6bf7d5e2d552
2026-06-19 22:14:57 -07:00

91 lines
3.5 KiB
TypeScript

import type { PluginDashboardViewContext } from "./types";
import { AlertTriangle } from "lucide-react";
import { lazy, Suspense, type LazyExoticComponent, type ReactElement, type ReactNode } from "react";
import { useTranslation } from "react-i18next";
import { ErrorBoundary } from "../components/ErrorBoundary";
import "./pluginViewRegistry.css";
export type PluginTaskView = `plugin:${string}:${string}`;
type PluginViewComponent = LazyExoticComponent<({ context }: { context?: PluginDashboardViewContext }) => ReactElement>;
const registry = new Map<string, PluginViewComponent>();
/** Build composite plugin task view ID: plugin:{pluginId}:{viewId}. */
export function getPluginViewId(pluginId: string, viewId: string): PluginTaskView {
return `plugin:${pluginId}:${viewId}`;
}
/** Parse composite plugin task view ID. Returns null for non-plugin IDs. */
export function parsePluginViewId(value: string): { pluginId: string; viewId: string } | null {
const match = /^plugin:([^:]+):(.+)$/u.exec(value);
if (!match) return null;
return { pluginId: match[1], viewId: match[2] };
}
/** True when a view ID matches the plugin composite ID format. */
export function isPluginViewId(value: string): value is PluginTaskView {
return parsePluginViewId(value) !== null;
}
/** Register a lazy plugin dashboard view component in the static host registry. */
export function registerPluginView(pluginId: string, viewId: string, lazyComponent: PluginViewComponent): void {
registry.set(getPluginViewId(pluginId, viewId), lazyComponent);
}
/** Resolve a plugin dashboard lazy component from the static host registry. */
export function getPluginViewComponent(pluginId: string, viewId: string): PluginViewComponent | null {
return registry.get(getPluginViewId(pluginId, viewId)) ?? null;
}
export function isPluginViewRegistered(pluginId: string, viewId: string): boolean {
return registry.has(getPluginViewId(pluginId, viewId));
}
/** Test helper for clearing global registry state. */
export function __test_clearPluginViewRegistry(): void {
registry.clear();
}
function PluginViewUnavailable({ viewId }: { viewId: string }): ReactNode {
const { t } = useTranslation("app");
return (
<section className="card plugin-dashboard-view-missing" data-testid="plugin-view-unavailable">
<h2 className="plugin-dashboard-view-missing-title">
<AlertTriangle />
{t("plugins.viewUnavailable", "Plugin view unavailable")}
</h2>
<p className="plugin-dashboard-view-missing-description">
{t("plugins.noHostRegistration", "No host registration found for")} <code>{viewId}</code>.
</p>
</section>
);
}
export function PluginDashboardViewHost({ viewId, context }: { viewId: PluginTaskView; context?: PluginDashboardViewContext }): ReactNode {
const parsed = parsePluginViewId(viewId);
if (!parsed) return <PluginViewUnavailable viewId={viewId} />;
const ViewComponent = getPluginViewComponent(parsed.pluginId, parsed.viewId);
if (!ViewComponent) {
return <PluginViewUnavailable viewId={viewId} />;
}
return (
<ErrorBoundary fallback={<PluginViewUnavailable viewId={viewId} />}>
<Suspense fallback={null}>
<ViewComponent context={context} />
</Suspense>
</ErrorBoundary>
);
}
// Backward-compatible aliases.
export const buildPluginTaskViewId = getPluginViewId;
export const parsePluginTaskViewId = parsePluginViewId;
export const resolvePluginDashboardView = getPluginViewComponent;
// Ensure lazy is referenced for plugin authors importing only this module pattern.
void lazy;