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:
2026-07-22 07:37:11 +02:00
parent bb35641715
commit ac726d075e
41 changed files with 3245 additions and 24 deletions

View File

@ -15,6 +15,7 @@ import { useEffect, useRef, useState } from "react";
import type { LayoutInfo } from "@/domain";
import { cn } from "@/shared";
import { PluginLayoutSelectorSection, usePluginRuntime } from "@/features/plugins";
import { useLayouts } from "./useLayouts";
interface LayoutTabsProps {
@ -43,6 +44,10 @@ export function LayoutTabs({ projectId, onActiveLayoutChange }: LayoutTabsProps)
const renameInputRef = useRef<HTMLInputElement | null>(null);
// Show/hide the create-kind dropdown.
const [showCreateMenu, setShowCreateMenu] = useState(false);
const { registry: pluginRegistry } = usePluginRuntime();
// Plugin layouts need a `create_layout` backend extension that doesn't exist
// yet (#43, F4 open point) — surfaced rather than silently swallowed.
const [pluginLayoutNotice, setPluginLayoutNotice] = useState<string | null>(null);
async function handleSelect(id: string) {
// The effect above propagates the new active layout (id + kind) to the parent.
@ -196,13 +201,22 @@ export function LayoutTabs({ projectId, onActiveLayoutChange }: LayoutTabsProps)
>
Git graph
</button>
<PluginLayoutSelectorSection
registry={pluginRegistry}
onSelect={(choice) => {
setShowCreateMenu(false);
setPluginLayoutNotice(
`« ${choice.layout.label} » (${choice.pluginDisplayName}) : la création de layouts plugins nécessite une extension backend pas encore livrée.`,
);
}}
/>
</div>
)}
</div>
{vm.error && (
{(vm.error || pluginLayoutNotice) && (
<span className="ml-2 text-xs text-danger" role="alert">
{vm.error}
{vm.error ?? pluginLayoutNotice}
</span>
)}
</div>