import { type Position } from "./useEditableUtils.mjs";
import type { EditingEngineLoader } from "./editingEngineCache.mjs";
type History = [Position, string];
export interface State {
  disconnected: boolean;
  onChange(text: string, position: Position, preParseResult?: unknown): void;
  pendingContent: string | null;
  queue: MutationRecord[];
  history: History[];
  historyAt: number;
  /**
   * The text most recently reported via `onChange` (i.e. last seen by the
   * controlled host), independent of the undo stack. Lets the
   * external-swap detector recover edits that the 500ms dedup kept out
   * of `history`: when the host swaps the editable's content, anything
   * the user typed since the last history checkpoint is still reachable
   * here and gets pushed onto the stack just before the swap is
   * recorded. Cleared on every undo/redo so we don't double-record after
   * navigating the existing history.
   */
  lastCommittedContent: string | null;
  /**
   * Set whenever the MutationObserver sees DOM changes between renders,
   * cleared after the snapshot block consumes them. Lets the per-render
   * layout effect skip the O(N) `toString` walk on idle re-renders
   * (parent updates, async state syncs, variant switches that don't
   * actually touch the editable's DOM). React's reconciliation of an
   * unchanged highlighted subtree produces zero mutation records, so
   * `domDirty` stays false and the snapshot is a no-op.
   */
  domDirty: boolean;
  position: Position | null;
  /** setTimeout id used to debounce flushChanges() calls during key-repeat */
  repeatFlushId: ReturnType<typeof setTimeout> | null;
  /**
   * AbortController for the in-flight `preParse` callback (if any). Reset
   * on every new flush so a rapidly-typed sequence aborts stale parses
   * before posting a fresh request.
   */
  preParseAbort: AbortController | null;
  /**
   * Set when an arrow-key handler invokes `onBoundary` (which typically
   * triggers a host re-render to expand a collapsed region). The native
   * arrow-key default action moves the caret AFTER our keydown handler
   * returns, but the host's re-render commits BEFORE the resulting
   * `selectionchange` updates `state.position`. Without this flag, the
   * unconditional restore effect would snap the caret back to the stale
   * pre-arrow `state.position` on that intermediate render. The flag is
   * cleared after one skipped restore.
   */
  skipNextRestore: boolean;
}
export interface Options<TPreParseResult = unknown> {
  disabled?: boolean;
  indentation?: number;
  /**
   * Minimum column the cursor is allowed to occupy on indented lines.
   *
   * When set, horizontal arrow navigation skips over the leading whitespace
   * up to `minColumn` so the caret never lands inside a clipped/hidden
   * indent region:
   *
   * - `ArrowLeft` at column `minColumn` (with that line's first `minColumn`
   *   characters all whitespace) jumps to the end of the previous line
   *   instead of stepping into the indent.
   * - `ArrowRight` at the end of a line jumps to column `minColumn` of the
   *   next line (when the next line is indented at least that far) instead
   *   of landing at column 0.
   *
   * Useful when the editor is rendered in a horizontally-shifted view (for
   * example a collapsed code block whose left padding is translated off
   * screen) where columns below `minColumn` are not visible. Leave
   * `undefined` for default arrow-key behavior.
   */
  minColumn?: number;
  /**
   * First row of the visible region. When set, `ArrowUp` on this row and
   * `ArrowLeft` at the start of this row are blocked (no caret movement)
   * and `onBoundary` is invoked. Useful when content above the visible
   * region is hidden and the host wants a chance to reveal it.
   */
  minRow?: number;
  /**
   * Last row of the visible region. When set, `ArrowDown` on this row and
   * `ArrowRight` at the end of this row are blocked (no caret movement)
   * and `onBoundary` is invoked.
   */
  maxRow?: number;
  /**
   * Called when the user attempts to navigate past `minRow`/`maxRow` via
   * arrow keys. When `onBoundary` is provided, the navigation is allowed
   * to proceed natively so the host can react (e.g. expand a collapsed
   * code block) and the caret continues moving in the now-visible
   * content. When `onBoundary` is omitted, the navigation is blocked
   * (caret stays put).
   */
  onBoundary?: () => void;
  /**
   * CSS selector identifying the elements that represent selectable
   * "lines" inside the editable. When set, and only while the caret is
   * actually inside an element matching the selector:
   *
   * - `ArrowLeft` at column 0 jumps synchronously to the end of the
   *   previous line.
   * - `ArrowRight` at the end of a line jumps synchronously to the start
   *   of the next line.
   *
   * Useful when the editable contains intentionally-empty whitespace
   * text nodes between block-level children (e.g. newline text nodes
   * separating `.line` spans inside a `.frame`). Without this, the
   * browser would place the caret in those gap nodes on horizontal
   * navigation, making `ArrowLeft`/`ArrowRight` appear to no-op.
   *
   * Vertical navigation (`ArrowUp`/`ArrowDown`) is intentionally left to
   * the browser so wrapped visual lines in `pre-wrap` layouts continue
   * to behave natively. Gap nodes styled with `line-height: 0` are
   * skipped by browsers vertically without intervention.
   *
   * The selector is matched against the caret's containing element via
   * `Element.closest`, so non-`.line` render paths (e.g. plain-string
   * editables) never trigger the wrap behavior.
   */
  caretSelector?: string;
  /**
   * Optional async pre-parse hook invoked before each `onChange` flush.
   * When provided, the parser receives the post-edit `text` and caret
   * `position` plus an `AbortSignal` that fires when a newer keystroke
   * supersedes this flush. Its resolved value is forwarded as the third
   * argument to `onChange`, allowing the host to cache an already-parsed
   * HAST (or any other derived state) keyed off the same source string.
   *
   * If `preParse` is omitted, `onChange` runs synchronously inside the
   * keyup / debounce handler as before. If it is provided, the React
   * state sync is delayed until the returned promise settles. Structural
   * edits that need a synchronous re-render (Enter, paste, cut, undo/redo,
   * programmatic `edit.update`/`edit.insert`, `minColumn` blank-line
   * collapse) bypass `preParse` and fire `onChange` immediately without
   * a third argument.
   */
  preParse?: (text: string, position: Position, signal: AbortSignal) => Promise<TPreParseResult>;
  /**
   * Loads the editing engine module on demand. Supplied by `CodeProvider` via
   * context (eager → bundled, resolves instantly; lazy → dynamic `import()`).
   * When omitted, `useEditable` falls back to a built-in dynamic import so
   * editing still works without a provider.
   */
  engineLoader?: EditingEngineLoader;
  /**
   * Controls when the editing engine loads once the block is editable:
   * `'eager'` (default) loads it immediately; `'interaction'` defers until the
   * user hovers, focuses, or clicks the editable.
   */
  activation?: 'eager' | 'interaction';
  /**
   * Called once when the block is first activated for editing — immediately in
   * `'eager'` mode, or on first engagement (hover / focus / click) in
   * `'interaction'` mode. Lets the host warm the rest of the live-editing
   * dependencies (grammars, worker) at the right moment, especially when
   * `'interaction'` has deferred them.
   */
  onActivate?: () => void;
}
export interface Edit {
  /** Replaces the entire content of the editable while adjusting the caret position. */
  update(content: string): void;
  /** Inserts new text at the caret position while deleting text in range of the offset (which accepts negative offsets). */
  insert(append: string, offset?: number): void;
  /** Positions the caret where specified */
  move(pos: number | {
    row: number;
    column: number;
  }): void;
  /** Returns the current editor state, as usually received in onChange */
  getState(): {
    text: string;
    position: Position;
  };
}
export type Bounds = {
  minColumn?: number;
  minRow?: number;
  maxRow?: number;
  onBoundary?: () => void;
  caretSelector?: string;
  preParse?: (text: string, position: Position, signal: AbortSignal) => Promise<unknown>;
};
/**
 * Everything {@link createEditableEngine} needs from its host hook. `useEditable`
 * owns this state and these refs so they survive this module's lazy load; the
 * engine only reads and mutates them, and they are shared by reference so the
 * engine's handlers always observe live values.
 */
export interface EditableEngineContext {
  elementRef: {
    current: HTMLElement | undefined | null;
  };
  state: State;
  observerRef: {
    current: MutationObserver | null;
  };
  boundsRef: {
    current: Bounds;
  };
  configRef: {
    current: Options;
  };
  unblock: (value: never[]) => void;
}
/**
 * The heavy editing runtime bound to a host element. `setup` applies
 * `contentEditable` and binds the keyboard/paste/caret handlers; `observeAndRestore`
 * runs the per-render MutationObserver + caret-restore pass. Each returns its cleanup.
 */
export interface EditableEngine {
  edit: Edit;
  observeAndRestore(): (() => void) | undefined;
  setup(): (() => void) | undefined;
}
export type CreateEditableEngine = (ctx: EditableEngineContext) => EditableEngine;
/**
 * Resolves the editing engine factory. `CodeProvider` supplies one via context
 * (eager → bundled, resolves instantly; lazy → dynamic `import()`); `useEditable`
 * also has a built-in fallback so editing works without a provider.
 */
export type EditableEngineLoader = () => Promise<CreateEditableEngine>;
/**
 * Builds the editing engine for a host element. This module statically imports
 * the heavy editing utilities (`useEditableUtils`, `cloneRangeWithInlineStyles`,
 * `stripLeadingPerLine`) and `react-dom`, so the bundler emits it as a separate
 * chunk that `useEditable` loads on demand — read-only code blocks never pull it in.
 */
export declare const createEditableEngine: CreateEditableEngine;
export {};