import type { Renderer, ViewerSource } from "./types.ts"; /** * What an action can see when it is offered and when it runs. `content` is the * element the renderer drew into, so an action can read or print what is on * screen rather than re-fetching the file. */ export interface ViewerActionContext { host: HTMLElement; content: HTMLElement; source: ViewerSource; /** Absent while the preview is still resolving, or when nothing could render. */ renderer?: Renderer; kind?: string; } export interface ViewerAction { /** Stable identifier; registering the same id again replaces the earlier one. */ id: string; label: string; /** Name from the built-in icon set. Falls back to one derived from the label. */ icon?: string; /** Toolbar side: alongside the renderer's own controls, or with download. */ placement?: "controls" | "actions"; /** Offered only when this returns true. Defaults to always. */ when?: (context: ViewerActionContext) => boolean; run: (context: ViewerActionContext) => void | Promise; } const registered = new Map(); /** * Adds a toolbar action to every viewer in the page. Returns an unregister * function, so a component can add one for as long as it is mounted. * * ```ts * registerViewerAction({ * id: "open-in-tab", * label: "Open in new tab", * when: ({ source }) => Boolean(source.src), * run: ({ source }) => window.open(source.src, "_blank", "noopener"), * }); * ``` */ export function registerViewerAction(action: ViewerAction): () => void { registered.set(action.id, action); return () => { if (registered.get(action.id) === action) registered.delete(action.id); }; } /** Globally registered actions, in registration order. */ export function viewerActions(): ViewerAction[] { return [...registered.values()]; } /** Testing helper: drops every globally registered action. */ export function clearViewerActions(): void { registered.clear(); }