fix(plugins): isole les contributions plugin en erreur et durcit menus.ts

Le chargement de l'archive hello-plugin (build/hello-plugin-0.1.0.zip)
vidait la fenêtre principale : une contribution plugin fautive
remontait jusqu'au rendu global au lieu de rester locale à la cellule.
Ajoute un boundary local dans PluginLayoutCellView/PluginLayoutSelectorSection
et durcit menus.ts/loader.ts/registry.ts contre les entrées de menu ou
contributions malformées.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-30 09:08:14 +02:00
parent eec3e0a5bc
commit aa850374ce
9 changed files with 316 additions and 58 deletions

View File

@ -26,15 +26,44 @@ export interface PluginLayoutChoice {
layout: PluginLayoutContribution;
}
function nonEmptyString(value: unknown): string | undefined {
return typeof value === "string" && value.trim().length > 0 ? value : undefined;
}
function finiteOrder(value: unknown): number {
return typeof value === "number" && Number.isFinite(value) ? value : 0;
}
function compareText(a: unknown, b: unknown): number {
return String(a ?? "").localeCompare(String(b ?? ""));
}
export function listPluginLayoutChoices(registry: PluginRuntimeRegistry): PluginLayoutChoice[] {
return registry
.layoutContributions()
.map(({ pluginId, pluginDisplayName, layout }) => ({ pluginId, pluginDisplayName, layout }))
.flatMap(({ pluginId, pluginDisplayName, layout }) => {
const type = nonEmptyString(layout.type);
const label = nonEmptyString(layout.label);
if (!type || !label) return [];
return [
{
pluginId,
pluginDisplayName: nonEmptyString(pluginDisplayName) ?? pluginId,
layout: {
...layout,
type,
label,
order: finiteOrder(layout.order),
icon: nonEmptyString(layout.icon),
},
},
];
})
.sort(
(a, b) =>
(a.layout.order ?? 0) - (b.layout.order ?? 0) ||
a.pluginDisplayName.localeCompare(b.pluginDisplayName) ||
a.layout.label.localeCompare(b.layout.label),
compareText(a.pluginDisplayName, b.pluginDisplayName) ||
compareText(a.layout.label, b.layout.label),
);
}