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>
This commit is contained in:
@ -837,11 +837,18 @@ export interface GridContainer {
|
||||
/**
|
||||
* A node in the layout tree. Tagged on `type` with the payload under `node`,
|
||||
* matching the backend `#[serde(tag = "type", content = "node")]`.
|
||||
*
|
||||
* `customPluginLayout` (#43, F4, carnet v2 §3) is a true top-level variant —
|
||||
* a plugin-provided layout occupies a slot in the tree at the same conceptual
|
||||
* level as a terminal leaf, a split or a grid, never a field bolted onto
|
||||
* `LeafCell` (that shape was tried and explicitly retired by Architect after
|
||||
* QA flagged the ambiguity — see `CustomPluginLayoutCell`'s doc comment).
|
||||
*/
|
||||
export type LayoutNode =
|
||||
| { type: "leaf"; node: LeafCell }
|
||||
| { type: "split"; node: SplitContainer }
|
||||
| { type: "grid"; node: GridContainer };
|
||||
| { type: "grid"; node: GridContainer }
|
||||
| { type: "customPluginLayout"; node: CustomPluginLayoutCell };
|
||||
|
||||
/** The root of a tab's terminal layout. */
|
||||
export interface LayoutTree {
|
||||
@ -1448,3 +1455,190 @@ export interface PairingCode {
|
||||
export function normalizePairingCode(raw: string): string {
|
||||
return raw.replace(/[\s-]+/g, "").toUpperCase();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plugin system (ticket #43) — mirrors of the backend DTOs described in the
|
||||
// carnet §§2, 5, 6, 7. Full-trust, locally-installed, globally-scoped plugins
|
||||
// contributing menus/menu-items/layouts/MCP servers via a pre-compiled ESM
|
||||
// bundle loaded at bootstrap. See `@/plugins/runtime` for the loader/registry
|
||||
// and `@/features/plugins` for the admin surface + menu/layout integration.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Where an installed plugin package came from (admin display only). */
|
||||
export type PluginSourceKind = "archive" | "directory";
|
||||
|
||||
/** Persisted lifecycle state of an installed plugin (carnet §1.4). */
|
||||
export type PluginLifecycleState =
|
||||
| "enabled"
|
||||
| "disabled"
|
||||
| "pending-enable"
|
||||
| "pending-disable"
|
||||
| "pending-uninstall"
|
||||
| "invalid";
|
||||
|
||||
/** Counts of what a plugin declares, shown in the admin list (carnet §5). */
|
||||
export interface PluginContributionSummary {
|
||||
topLevelMenus: number;
|
||||
menuItems: number;
|
||||
layouts: number;
|
||||
mcpServers: number;
|
||||
}
|
||||
|
||||
/** One installed plugin, as shown in `Paramètres > Plugins` (mirror of `PluginAdminDto`). */
|
||||
export interface PluginAdmin {
|
||||
id: string;
|
||||
displayName: string;
|
||||
publisher?: string;
|
||||
version: string;
|
||||
description?: string;
|
||||
iconUrl?: string;
|
||||
sourceKind: PluginSourceKind;
|
||||
sourceLabel?: string;
|
||||
lifecycleState: PluginLifecycleState;
|
||||
enabled: boolean;
|
||||
pendingEnableState?: boolean;
|
||||
pendingUninstall: boolean;
|
||||
restartRequired: boolean;
|
||||
trustLevel: "full";
|
||||
contributionSummary: PluginContributionSummary;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/** A manifest validation issue surfaced during pre-install review. */
|
||||
export interface PluginReviewIssue {
|
||||
severity: "error" | "warning";
|
||||
message: string;
|
||||
}
|
||||
|
||||
/** Pre-install review of a candidate package (carnet §4, `ReviewPluginPackage`). */
|
||||
export interface PluginReview {
|
||||
id: string;
|
||||
displayName: string;
|
||||
publisher?: string;
|
||||
version: string;
|
||||
description?: string;
|
||||
trustLevel: "full";
|
||||
contributionSummary: PluginContributionSummary;
|
||||
issues: PluginReviewIssue[];
|
||||
/** False when an `error`-severity issue makes install unsafe/impossible. */
|
||||
installable: boolean;
|
||||
}
|
||||
|
||||
/** Outcome of `install_from_archive` / `install_from_directory`. */
|
||||
export interface PluginInstallResult {
|
||||
plugin: PluginAdmin;
|
||||
restartRequired: boolean;
|
||||
}
|
||||
|
||||
/** Outcome of `uninstall`. */
|
||||
export interface PluginUninstallResult {
|
||||
pluginId: string;
|
||||
restartRequired: boolean;
|
||||
}
|
||||
|
||||
/** Contribution declarations for one plugin, as consumed by the runtime loader. */
|
||||
export interface PluginContributionDto {
|
||||
menus: PluginTopLevelMenuContribution[];
|
||||
menuItems: PluginMenuItemContribution[];
|
||||
layouts: PluginLayoutContribution[];
|
||||
mcpServers: PluginMcpServerSummary[];
|
||||
}
|
||||
|
||||
/** One entry of `plugin_list_runtime_contributions` (mirror of `PluginRuntimePluginDto`). */
|
||||
export interface PluginRuntimePlugin {
|
||||
id: string;
|
||||
displayName: string;
|
||||
publisher?: string;
|
||||
version: string;
|
||||
bundleUrl: string;
|
||||
iconUrl?: string;
|
||||
contentHash: string;
|
||||
contributes: PluginContributionDto;
|
||||
}
|
||||
|
||||
/** Bootstrap catalog fetched once, before loading any plugin bundle. */
|
||||
export interface PluginRuntimeContributionCatalog {
|
||||
plugins: PluginRuntimePlugin[];
|
||||
}
|
||||
|
||||
/** Manifest declaration of a top-level menu (carnet §7.1). */
|
||||
export interface PluginTopLevelMenuContribution {
|
||||
id: string;
|
||||
label: string;
|
||||
topLevel: true;
|
||||
order?: number;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
/** Native + plugin menu ids an item can target (carnet §7.2). */
|
||||
export type MenuTargetId = "panels" | "settings" | `plugin:${string}`;
|
||||
|
||||
/** Manifest declaration of a menu item contributed into an existing/plugin menu. */
|
||||
export interface PluginMenuItemContribution {
|
||||
id: string;
|
||||
targetMenuId: MenuTargetId;
|
||||
label: string;
|
||||
command: string;
|
||||
order?: number;
|
||||
icon?: string;
|
||||
when?: string;
|
||||
}
|
||||
|
||||
/** A menu item resolved for rendering — enablement already evaluated (carnet §7.2). */
|
||||
export interface ResolvedPluginMenuItem {
|
||||
id: string;
|
||||
pluginId: string;
|
||||
pluginDisplayName: string;
|
||||
targetMenuId: MenuTargetId;
|
||||
label: string;
|
||||
command: string;
|
||||
enabled: boolean;
|
||||
disabledReason?: string;
|
||||
groupLabel?: string;
|
||||
order: number;
|
||||
iconUrl?: string;
|
||||
}
|
||||
|
||||
/** Manifest declaration of a custom React layout (carnet §7.3). */
|
||||
export interface PluginLayoutContribution {
|
||||
type: string;
|
||||
label: string;
|
||||
component: string;
|
||||
order?: number;
|
||||
icon?: string;
|
||||
when?: string;
|
||||
}
|
||||
|
||||
/** Whether a persisted plugin layout can currently be rendered by its provider. */
|
||||
export type PluginLayoutAvailability =
|
||||
| "available"
|
||||
| "plugin-disabled"
|
||||
| "plugin-missing"
|
||||
| "incompatible";
|
||||
|
||||
/**
|
||||
* Opaque, domain-persisted identity + state of a `customPluginLayout`
|
||||
* top-level {@link LayoutNode} variant (#43, carnet v2 §3.2 — the canonical
|
||||
* schema, mirroring the backend `CustomPluginLayoutCell` exactly: `id`,
|
||||
* `pluginId`, `layoutType`, `state`, camelCase). No display name travels on
|
||||
* the wire — the UI derives it from the plugin runtime registry/admin list at
|
||||
* render time, never persists it here.
|
||||
*
|
||||
* A previous frontend-only shape nested this under `LeafCell.pluginLayout`
|
||||
* with a `kind`/`nodeId`/`providerPluginId` field naming; that shape was
|
||||
* never part of the IPC/persistence contract and has been retired — do not
|
||||
* reintroduce it.
|
||||
*/
|
||||
export interface CustomPluginLayoutCell {
|
||||
id: string;
|
||||
pluginId: string;
|
||||
layoutType: string;
|
||||
state: unknown;
|
||||
}
|
||||
|
||||
/** Manifest declaration of an MCP server the plugin supervises (carnet §7.4, summary only). */
|
||||
export interface PluginMcpServerSummary {
|
||||
id: string;
|
||||
displayName: string;
|
||||
autoStart?: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user