fix(frontend,plugins): évite le crash sur install/uninstall/reinstall par rechargement systématique de la liste (#120)

- usePlugins.ts: remplace les mises à jour optimistes du state par des appels listPlugins() après chaque opération backend
- PluginsPanel.tsx: ajoute la normalisation isInstallable() pour éviter les crashes si installable est undefined
- Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-01 19:10:44 +02:00
parent fd2ab4a0d7
commit 0061685b08
2 changed files with 19 additions and 11 deletions

View File

@ -217,11 +217,13 @@ export function PluginsPanel() {
<ConfirmDialog <ConfirmDialog
title={`Installer "${installFlow.review.displayName}" ?`} title={`Installer "${installFlow.review.displayName}" ?`}
body={reviewSummary(installFlow.review)} body={reviewSummary(installFlow.review)}
confirmLabel={installFlow.review.installable ? "Installer" : "Corriger avant d'installer"} confirmLabel={
isInstallable(installFlow.review) ? "Installer" : "Corriger avant d'installer"
}
busy={vm.busy} busy={vm.busy}
danger={!installFlow.review.installable} danger={!isInstallable(installFlow.review)}
onConfirm={() => { onConfirm={() => {
if (installFlow.review.installable) void confirmInstall(); if (isInstallable(installFlow.review)) void confirmInstall();
else setInstallFlow(null); else setInstallFlow(null);
}} }}
onCancel={() => setInstallFlow(null)} onCancel={() => setInstallFlow(null)}
@ -259,7 +261,13 @@ function reviewSummary(review: PluginReview): string {
review.description ?? null, review.description ?? null,
`Confiance : full-trust — ce plugin peut exécuter du code arbitraire dans IdeA.`, `Confiance : full-trust — ce plugin peut exécuter du code arbitraire dans IdeA.`,
`Contributions : ${c.topLevelMenus} menu(s), ${c.menuItems} item(s), ${c.layouts} layout(s), ${c.mcpServers} serveur(s) MCP.`, `Contributions : ${c.topLevelMenus} menu(s), ${c.menuItems} item(s), ${c.layouts} layout(s), ${c.mcpServers} serveur(s) MCP.`,
...review.issues.map((i) => `${i.severity === "error" ? "Erreur" : "Avertissement"} : ${i.message}`), ...(review.issues ?? []).map(
(i) => `${i.severity === "error" ? "Erreur" : "Avertissement"} : ${i.message}`,
),
].filter((p): p is string => Boolean(p)); ].filter((p): p is string => Boolean(p));
return parts.join("\n"); return parts.join("\n");
} }
function isInstallable(review: PluginReview): boolean {
return review.installable ?? true;
}

View File

@ -86,8 +86,8 @@ export function usePlugins(): PluginsViewModel {
setBusy(true); setBusy(true);
setError(null); setError(null);
try { try {
const result = await plugin.installFromArchive(path); await plugin.installFromArchive(path);
setPlugins((prev) => [...prev.filter((p) => p.id !== result.plugin.id), result.plugin]); setPlugins(await plugin.listPlugins());
return true; return true;
} catch (e) { } catch (e) {
setError(describe(e)); setError(describe(e));
@ -104,8 +104,8 @@ export function usePlugins(): PluginsViewModel {
setBusy(true); setBusy(true);
setError(null); setError(null);
try { try {
const result = await plugin.installFromDirectory(path); await plugin.installFromDirectory(path);
setPlugins((prev) => [...prev.filter((p) => p.id !== result.plugin.id), result.plugin]); setPlugins(await plugin.listPlugins());
return true; return true;
} catch (e) { } catch (e) {
setError(describe(e)); setError(describe(e));
@ -122,8 +122,8 @@ export function usePlugins(): PluginsViewModel {
setBusy(true); setBusy(true);
setError(null); setError(null);
try { try {
const updated = await plugin.setEnabled(pluginId, enabled); await plugin.setEnabled(pluginId, enabled);
setPlugins((prev) => prev.map((p) => (p.id === pluginId ? updated : p))); setPlugins(await plugin.listPlugins());
} catch (e) { } catch (e) {
setError(describe(e)); setError(describe(e));
} finally { } finally {
@ -139,7 +139,7 @@ export function usePlugins(): PluginsViewModel {
setError(null); setError(null);
try { try {
await plugin.uninstall(pluginId); await plugin.uninstall(pluginId);
setPlugins((prev) => prev.filter((p) => p.id !== pluginId)); setPlugins(await plugin.listPlugins());
} catch (e) { } catch (e) {
setError(describe(e)); setError(describe(e));
} finally { } finally {