Files
fusion/packages/dashboard/app/plugins/pluginSlotRegistry.tsx
gsxdsm 1e49494bac feat(i18n): full-sweep string migration — 5,930 keys across 5 locales (#1352)
Migration (multi-agent sweep over 216 files, 60 batches):
- Every user-visible dashboard + TUI string moved to t() with the exact
  English inline default (en rendering byte-identical)
- Catalogs merged from per-batch fragments: en/zh-CN/zh-TW/fr/es now
  carry ~5,930 keys each across common/app/errors/cli namespaces;
  CLI bundles regenerated (6 locales incl. ko)

Integration fixes:
- 18 type errors: reserved {{count}} interpolations renamed, malformed
  plural call, hand-rolled t-param types replaced with TFunction<"app">
- 23 lint errors: superseded label constants/helpers removed
- ExecutorStatusBar hook-order violation (keyboard-open early return
  moved below hooks)
- TUI tests wrapped in I18nextProvider (uninitialized fallback renders
  literal {{placeholders}}); dashboard vitest.setup boots a minimal en
  i18next instance for the same reason

Known WIP (next commits): ~457 residual strings across 50 batches,
Korean drafts for swept keys, and a dashboard test-suite pass that is
still being stabilized (~283 failures under investigation — fake-timer
waitFor interaction, likely stale node_modules vs merged lockfile).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 19:06:53 -07:00

117 lines
3.7 KiB
TypeScript

import type { ComponentType, ReactNode } from "react";
import { useTranslation } from "react-i18next";
import type { PluginUiSlotEntry } from "../api";
import { DroidCliProviderCard } from "../components/DroidCliProviderCard";
export interface PluginSlotHostActions {
refreshAuthProviders?: () => void;
openSettingsSection?: (section: string) => void;
openModelOnboarding?: () => void;
}
interface PluginSlotComponentProps {
entry: PluginUiSlotEntry;
actions?: PluginSlotHostActions;
}
interface PluginSlotRegistration {
pluginId: string;
slotId: string;
componentPath: string;
component: ComponentType<PluginSlotComponentProps>;
}
function DroidSettingsProviderCard({ actions }: PluginSlotComponentProps): ReactNode {
return (
<DroidCliProviderCard
compact
authenticated={false}
onToggled={() => {
actions?.refreshAuthProviders?.();
}}
/>
);
}
function DroidOnboardingProviderCard({ actions }: PluginSlotComponentProps): ReactNode {
return (
<DroidCliProviderCard
authenticated={false}
onToggled={() => {
actions?.refreshAuthProviders?.();
}}
/>
);
}
function DroidOnboardingSetupHelp(): ReactNode {
const { t } = useTranslation("app");
return (
<p className="onboarding-helper-text" data-testid="droid-onboarding-setup-help">
{t("plugins.droidOnboardingTip", "Tip: Enable Droid CLI to reuse your Factory AI subscription without adding an API key.")}
</p>
);
}
function DroidPostOnboardingRecommendation({ actions }: PluginSlotComponentProps): ReactNode {
const { t } = useTranslation("app");
return (
<div className="post-onboarding-recommendations__item" data-testid="droid-post-onboarding-recommendation">
<span className="post-onboarding-recommendations__item-text">
<strong>{t("plugins.droidRecommendTitle", "Enable Droid CLI")}</strong>
<span>{t("plugins.droidRecommendDesc", "Use your local Droid CLI session as an AI provider in Fusion.")}</span>
</span>
<button type="button" className="btn btn-sm" onClick={() => actions?.openSettingsSection?.("authentication")}>
{t("plugins.openAuthentication", "Open Authentication")}
</button>
<button type="button" className="btn btn-sm" onClick={() => actions?.openModelOnboarding?.()}>
{t("plugins.openOnboarding", "Open Onboarding")}
</button>
</div>
);
}
const REGISTRY: PluginSlotRegistration[] = [
{
pluginId: "fusion-plugin-droid-runtime",
slotId: "settings-provider-card",
componentPath: "./components/settings-provider-card.js",
component: DroidSettingsProviderCard,
},
{
pluginId: "fusion-plugin-droid-runtime",
slotId: "settings-integration-card",
componentPath: "./components/settings-integration-card.js",
component: DroidSettingsProviderCard,
},
{
pluginId: "fusion-plugin-droid-runtime",
slotId: "onboarding-provider-card",
componentPath: "./components/onboarding-provider-card.js",
component: DroidOnboardingProviderCard,
},
{
pluginId: "fusion-plugin-droid-runtime",
slotId: "onboarding-setup-help",
componentPath: "./components/onboarding-setup-help.js",
component: DroidOnboardingSetupHelp,
},
{
pluginId: "fusion-plugin-droid-runtime",
slotId: "post-onboarding-recommendation",
componentPath: "./components/post-onboarding-recommendation.js",
component: DroidPostOnboardingRecommendation,
},
];
export function resolvePluginSlotComponent(entry: PluginUiSlotEntry): ComponentType<PluginSlotComponentProps> | null {
const hit = REGISTRY.find(
(candidate) =>
candidate.pluginId === entry.pluginId
&& candidate.slotId === entry.slot.slotId
&& candidate.componentPath === entry.slot.componentPath,
);
return hit?.component ?? null;
}