import { ContextMenuItem, CustomCommandRegistration, CustomCommandRegistrationResult, CustomCommandHandle, SuperDocEditorLike, SuperDocLike, SuperDocUIState, Subscribable, UIToolbarCommandState, ViewportContext, ViewportEntityHit } from './types.js';
export interface CustomCommandsRegistry {
    /**
     * Public `register` surface bound to the controller. The factory exposes
     * this so `createSuperDocUI` can attach it to the `commands` Proxy.
     */
    register<TPayload = unknown, TValue = unknown>(registration: CustomCommandRegistration<TPayload, TValue>): CustomCommandRegistrationResult<TPayload, TValue>;
    /** Whether `id` is currently registered as a custom command. */
    has(id: string): boolean;
    /**
     * Build the per-command snapshot states for every registered custom
     * command, given the current controller state. Errors in `getState`
     * are caught here and folded to the static fallback.
     */
    computeStates(state: SuperDocUIState): Record<string, UIToolbarCommandState>;
    /**
     * Get a stable {@link CustomCommandHandle} for a registered id. The
     * handle is created on first access and cached.
     */
    getHandle<TPayload = unknown, TValue = unknown>(id: string): CustomCommandHandle<TPayload, TValue> | undefined;
    /**
     * Run `execute` for a registered id. Returns false if not registered.
     * `context` (SD-2945) forwards the {@link ViewportContext} bundle when
     * the dispatch came from a `ContextMenuItem.invoke()`; direct
     * controller calls leave it `undefined`.
     */
    execute(id: string, payload?: unknown, context?: ViewportContext): boolean | Promise<boolean>;
    /**
     * Collect context-menu items contributed by registered customs.
     * Filtered by each contribution's `when` predicate against the
     * supplied entities + the current selection slice; sorted by
     * `(group, order, registrationSeq)`. Errors from `when` are
     * caught and the item is hidden for that query.
     *
     * SD-2945: when `input` is the full {@link ViewportContext} bundle,
     * predicates receive `point` / `position` / `insideSelection` and
     * each returned item carries an `invoke()` closure that fires
     * execute with the bundle bound. Pass an entities array for the
     * legacy "entities only" call shape.
     */
    getContextMenuItems(state: SuperDocUIState, input: ViewportEntityHit[] | ViewportContext): ContextMenuItem[];
    /**
     * Look up the custom command id (if any) bound to a normalized
     * shortcut string. Used by the controller's keydown listener to
     * dispatch matched shortcuts. Returns `undefined` when nothing is
     * registered for that combo.
     */
    resolveShortcut(shortcut: string): string | undefined;
    /** Drop every registration and tear down per-command Subscribables. */
    destroy(): void;
}
interface CustomCommandsRegistryDeps {
    /**
     * Whether the given id is a built-in. Used to enforce the `override`
     * rule without coupling this module to the toolbar registry directly.
     */
    isBuiltIn(id: string): boolean;
    /** Host superdoc passed to custom `execute` callbacks. */
    superdoc: SuperDocLike;
    /**
     * Resolve the routed editor at execute-time. Passed to custom
     * `execute` callbacks alongside `superdoc` so registrations can
     * reach `editor.doc.*` without a structural cast. Late-bound (a
     * function, not a captured value) so the registry sees whichever
     * story editor is active when the command runs, matching the
     * routing the rest of `ui.*` uses.
     */
    getEditor(): SuperDocEditorLike | null;
    /**
     * Re-emit the controller snapshot. Called whenever the registry
     * changes (register / unregister / invalidate) so subscribers see the
     * new custom command state. Should be microtask-coalesced.
     */
    scheduleNotify(): void;
    /**
     * Build a per-id Subscribable that emits this custom command's state
     * from `state.toolbar.commands[id]`. Equivalent to the built-in cache
     * in `create-super-doc-ui.ts`; we delegate so both built-ins and custom
     * commands share the same selector substrate (and the same dedupe
     * posture).
     */
    buildSubscribable(id: string): Subscribable<UIToolbarCommandState | undefined>;
}
/**
 * Stateful registry for custom toolbar commands. Owns the registration
 * map, the per-command Subscribable cache, and the error-dedupe table.
 *
 * Created once per controller; teardown is part of `ui.destroy()`.
 */
export declare function createCustomCommandsRegistry(deps: CustomCommandsRegistryDeps): CustomCommandsRegistry;
export {};
//# sourceMappingURL=custom-commands.d.ts.map