Files
IdeA/frontend/src/features/plugins/ProjectPluginsPanel.tsx

144 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Panel, Spinner } from "@/shared";
import { useProjectPlugins } from "./useProjectPlugins";
export interface ProjectPluginsPanelProps {
projectId: string;
}
function countLabel(count: number, singular: string, plural: string): string {
return `${count} ${count === 1 ? singular : plural}`;
}
export function ProjectPluginsPanel({ projectId }: ProjectPluginsPanelProps) {
const vm = useProjectPlugins(projectId);
if (vm.loading && !vm.settings) {
return (
<Panel aria-label="plugins du projet" className="flex items-center gap-2">
<Spinner size={14} />
<span className="text-sm text-muted">Chargement des plugins du projet…</span>
</Panel>
);
}
const installedIds = new Set(vm.installed.map((plugin) => plugin.id));
const missing = (vm.settings?.plugins ?? []).filter(
(activation) => !installedIds.has(activation.pluginId),
);
return (
<section aria-label="plugins du projet" className="flex flex-col gap-3">
<div>
<h2 className="text-sm font-semibold text-content">Plugins du projet</h2>
<p className="mt-1 text-xs text-muted">
Un plugin reste installé globalement. Activez-le ici avant d’attribuer ses
capabilities aux agents de ce projet.
</p>
</div>
{vm.error && <p role="alert" className="text-sm text-danger">{vm.error}</p>}
{vm.installed.length === 0 && missing.length === 0 ? (
<Panel><p className="text-sm text-muted">Aucun plugin installé globalement.</p></Panel>
) : (
<ul className="flex flex-col gap-2">
{vm.installed.map((plugin) => {
const enabled = vm.settings?.plugins.some(
(entry) => entry.pluginId === plugin.id && entry.enabled,
) ?? false;
const skills = plugin.contributes.skills ?? [];
const servers = plugin.contributes.mcpServers ?? [];
return (
<li key={plugin.id}>
<Panel className="flex flex-col gap-3">
<div className="flex items-start justify-between gap-4">
<div className="min-w-0">
<div className="flex flex-wrap items-center gap-2">
<span className="text-sm font-semibold text-content">{plugin.displayName}</span>
<span className="text-xs text-faint">v{plugin.version}</span>
<span className="rounded bg-raised px-1.5 py-0.5 text-[0.65rem] text-muted">
Installé globalement
</span>
</div>
<p className="mt-1 text-xs text-muted">
{countLabel(skills.length, "skill", "skills")} · {countLabel(servers.length, "serveur MCP", "serveurs MCP")}
</p>
</div>
<label className="flex shrink-0 items-center gap-2 text-xs text-content">
<input
type="checkbox"
checked={enabled}
disabled={vm.busy}
onChange={(event) => void vm.setEnabled(plugin.id, event.target.checked)}
/>
Activé pour ce projet
</label>
</div>
<details>
<summary className="cursor-pointer text-xs font-medium text-primary">
Voir les capabilities
</summary>
<div className="mt-3 grid gap-3 sm:grid-cols-2">
<div>
<h3 className="text-xs font-semibold text-content">Skills fournis</h3>
{skills.length === 0 ? (
<p className="mt-1 text-xs text-muted">Aucun skill fourni.</p>
) : (
<ul className="mt-1 flex flex-col gap-1">
{skills.map((skill) => (
<li key={skill.id} className="text-xs text-muted">
<span className="font-medium text-content">{skill.name}</span>
{skill.description ? ` — ${skill.description}` : ""}
</li>
))}
</ul>
)}
</div>
<div>
<h3 className="text-xs font-semibold text-content">Tools MCP fournis</h3>
{servers.length === 0 ? (
<p className="mt-1 text-xs text-muted">Aucun serveur MCP fourni.</p>
) : (
<ul className="mt-1 flex flex-col gap-1">
{servers.map((server) => (
<li key={server.id} className="text-xs text-muted">
<span className="font-medium text-content">{server.displayName}</span>
<span className="text-faint"> · {server.id}</span>
</li>
))}
</ul>
)}
</div>
</div>
</details>
</Panel>
</li>
);
})}
{missing.map((activation) => (
<li key={activation.pluginId}>
<Panel className="flex items-center justify-between gap-4 border-warning/40">
<div>
<span className="text-sm font-semibold text-content">{activation.pluginId}</span>
<p className="text-xs text-warning">Plugin manquant · configuration conservée</p>
</div>
<label className="flex items-center gap-2 text-xs text-muted">
<input
type="checkbox"
checked={activation.enabled}
disabled={vm.busy || !activation.enabled}
onChange={(event) => void vm.setEnabled(activation.pluginId, event.target.checked)}
/>
Activé pour ce projet
</label>
</Panel>
</li>
))}
</ul>
)}
</section>
);
}