import { createContext, useContext, type ReactNode } from 'react'; /** A single native context-menu entry. */ export type ContextMenuItem = { id: string; label: string; type?: 'normal' | 'separator'; }; /** Show a context menu; resolves with the chosen item id, or null if dismissed. */ export type ShowContextMenu = ( items: ContextMenuItem[], ) => Promise; /** * Default capability: no menu. Keeps `useContextMenu()` safe when no provider * is mounted (e.g. a non-Electron build or a test that doesn't supply one). */ const ContextMenuContext = createContext(async () => null); /** * Injects a context-menu capability into the tree. The composition root passes * the real (Electron) implementation; tests/storybook can pass a fake and * assert on the items it was given. */ export function ContextMenuProvider({ show, children, }: { show: ShowContextMenu; children: ReactNode; }) { return ( {children} ); } /** Access the injected context-menu capability. */ export function useContextMenu(): ShowContextMenu { return useContext(ContextMenuContext); } /** * Electron-backed implementation, sourced from the preload bridge. This is the * single place that touches `window.textile.contextMenu`; wire it at the * composition root via ``. */ export const electronShowContextMenu: ShowContextMenu = async (items) => (await window.textile?.contextMenu.show(items)) ?? null;