Files
IdeaSDK/frontend/src/features/settings/SettingsView.tsx
Blomios ac726d075e feat(frontend): système de plugins — runtime, menus, layouts custom (#43)
Lots F1-F4 : runtime de chargement/registre plugin, extension des menus
existants, panneau de gestion des plugins, types de layout custom
(sélecteur, fallback, cellule dédiée) branchés sur le port plugin.
Suite npm typecheck/test verte (947/947).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 07:37:11 +02:00

111 lines
3.8 KiB
TypeScript

/**
* `SettingsView` — the Settings surface shell (ticket #68).
*
* Settings is a **main surface**, not a dockable project view: it deliberately
* has no `PanelId` and stays outside the `viewPlacement` model. It takes over
* the main area while the menu bar stays visible above it.
*
* Ticket #68 gives it a second section, so the section list becomes real
* navigation (a left column) instead of the old single toggle. That also kills
* the alternating "Close AI Profiles" menu label, which never scaled past one
* entry: the menu now names sections, the active one is marked, and closing is
* an explicit action inside the view.
*
* `EmbedderSettings` / `ModelServersPanel` are **not** pulled in here — that is
* a separate lateral rework. The section list is the seam they would slot into.
*
* Ticket #78 — UX decision: the human UI of IdeA is French by default, uniform
* per surface (proper nouns/technical acronyms like `URL`, `LAN`, `IP/CIDR`,
* `HTTPS`, `API`, `CLI` stay as-is). Settings previously mixed `AI Profiles` /
* `Deployment` (English) with `Appareils` (French, frozen by UX for #77); this
* aligns the whole surface on French.
*/
import { Button, cn } from "@/shared";
import { ProfilesSettings } from "@/features/first-run";
import { DevicesScreen } from "@/features/devices";
import { PluginsPanel } from "@/features/plugins";
import { DeploymentSettings } from "./DeploymentSettings";
/** The Settings sections, in menu/nav order. */
export type SettingsSection = "aiProfiles" | "deployment" | "devices" | "plugins";
/** Human labels, shared by the nav column and the `Settings` menu (ticket #78 — French). */
export const SETTINGS_SECTION_LABEL: Record<SettingsSection, string> = {
aiProfiles: "Profils IA",
deployment: "Déploiement",
devices: "Appareils",
plugins: "Plugins",
};
/** Section order — the single source of truth for both nav and menu. */
export const SETTINGS_SECTIONS: SettingsSection[] = [
"aiProfiles",
"deployment",
"devices",
"plugins",
];
interface SettingsViewProps {
section: SettingsSection;
onSectionChange: (section: SettingsSection) => void;
onClose: () => void;
}
export function SettingsView({
section,
onSectionChange,
onClose,
}: SettingsViewProps) {
return (
<div className="flex min-h-0 flex-1 overflow-hidden">
<nav
aria-label="settings sections"
className="flex w-52 shrink-0 flex-col gap-1 border-r border-border p-3"
>
{SETTINGS_SECTIONS.map((id) => {
const active = id === section;
return (
<button
key={id}
type="button"
aria-current={active ? "page" : undefined}
onClick={() => onSectionChange(id)}
className={cn(
"rounded-md px-3 py-2 text-left text-sm transition-colors",
active
? "bg-raised font-semibold text-content"
: "text-muted hover:bg-raised hover:text-content",
)}
>
{SETTINGS_SECTION_LABEL[id]}
</button>
);
})}
<div className="mt-auto pt-2">
<Button size="sm" variant="ghost" className="w-full" onClick={onClose}>
Fermer les paramètres
</Button>
</div>
</nav>
<div className="flex flex-1 justify-center overflow-y-auto p-6">
<div className="w-full max-w-2xl">
{section === "aiProfiles" ? (
<ProfilesSettings />
) : section === "deployment" ? (
<DeploymentSettings />
) : section === "plugins" ? (
<PluginsPanel />
) : (
// No `onSessionEnded`: the desktop app hosts the server and is never
// itself a paired device, so it cannot revoke its own session.
<DevicesScreen />
)}
</div>
</div>
</div>
);
}