/** * `usePluginMenus` — turns the loaded plugin runtime registry into ready-to- * splice `MenuBarMenu`/`MenuBarItem` entries for `ProjectsView`'s menu bar * (ticket #43, F3, carnet §7.1/§7.2). * * `whenCtx` is supplied by the caller (`ProjectsView` knows `projectOpen` * directly; git/agent/terminal/cell-focus signals are best-effort — see the * F3 gap noted in the delivery report: there is no existing global * agent-selected/terminal-focused/layout-cell-focused signal at the menu-bar * level in this codebase today, so those three default to `false` until a * future lot threads real focus state up to this level). */ import { useMemo } from "react"; import type { MenuTargetId } from "@/domain"; import type { MenuBarItem, MenuBarMenu } from "@/shared"; import type { PluginRuntimeRegistry, WhenContext } from "@/plugins/runtime"; import { resolveMenuItems, resolveTopLevelMenus } from "./menus"; export interface UsePluginMenusResult { /** Top-level plugin menus, in order, ready to splice between Panneaux/Paramètres. */ topLevelMenus: MenuBarMenu[]; /** Resolved+ordered `MenuBarItem`s to append to a native menu's items. */ itemsFor: (targetMenuId: MenuTargetId) => MenuBarItem[]; runCommand: (pluginId: string, commandId: string) => Promise; } export function usePluginMenus( registry: PluginRuntimeRegistry, whenCtx: WhenContext, ): UsePluginMenusResult { const runCommand = useMemo( () => (pluginId: string, commandId: string) => registry.runCommand(pluginId, commandId), [registry], ); const itemsFor = useMemo(() => { return (targetMenuId: MenuTargetId): MenuBarItem[] => resolveMenuItems(registry, targetMenuId, whenCtx).map((item) => ({ id: item.id, // Provenance is shown discreetly (carnet §0 UX decision) rather than // with a visual section divider, which the shared `MenuBar` primitive // doesn't support — a trailing "· Plugin Name" suffix is the least // intrusive option that still surfaces where the item came from. label: `${item.label} · ${item.pluginDisplayName}`, disabled: !item.enabled, onSelect: () => { void runCommand(item.pluginId, item.command); }, })); }, [registry, whenCtx, runCommand]); const topLevelMenus = useMemo(() => { return resolveTopLevelMenus(registry).map((menu) => ({ id: menu.id, label: menu.label, items: itemsFor(menu.id), })); }, [registry, itemsFor]); return { topLevelMenus, itemsFor, runCommand }; }