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>
111 lines
3.8 KiB
TypeScript
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>
|
|
);
|
|
}
|