/**
 * Mock 2D canvas rendering context — structurally a `Partial<CanvasRenderingContext2D>`
 * with method spies, plus a few extra stub fields. Typed as a loose stringy
 * bag because vitest's `vi.fn()` return type doesn't line up with
 * `CanvasRenderingContext2D`'s method signatures and tests inspect the spies
 * directly via `ctx.fillRect.mock.calls` etc.
 */
export type CanvasContextMock = Record<string, unknown>;
/**
 * Creates a mock 2D canvas rendering context with all commonly used
 * methods stubbed as vi.fn() and properties set to sensible defaults.
 *
 * This is the single canonical canvas mock for the entire test suite.
 * Import from "../../test-utils/canvasMock" (or appropriate relative path).
 */
export declare function createMockCanvasContext(): CanvasContextMock;
/**
 * Record style state at the moment each draw op fires.
 *
 * Counting `ctx.fill` / `ctx.stroke` calls is brittle — a refactor that
 * batches two paths into one stroke changes the count without changing
 * what's visible. This helper instead captures the style state (`fillStyle`,
 * `strokeStyle`, `globalAlpha`, `lineWidth`) at each draw call, so tests
 * can assert on *what was drawn with what appearance* rather than on how
 * many primitives the renderer emitted to get there.
 *
 * Usage:
 *   const ctx = createMockCanvasContext()
 *   const ops = recordCanvasOps(ctx)
 *   someRenderer(ctx, nodes, ...)
 *   expect(ops.fillStyles).toEqual(["#e41a1c", "rgba(255,255,0,0.5)"])
 */
export interface CanvasOpLog {
    fillStyles: string[];
    strokeStyles: string[];
    fillAlphas: number[];
    strokeAlphas: number[];
    /** `lineWidth` at the moment each stroke fired — useful for pulse /
     *  emphasis renderers that vary stroke weight by intensity. */
    strokeLineWidths: number[];
}
export declare function recordCanvasOps(ctx: CanvasContextMock): CanvasOpLog;
/**
 * rAF stubbing flavor:
 *
 * - `true` — synchronous fire: the rAF callback runs immediately when
 *   scheduled. Default. Right for most tests that just want a paint
 *   to happen before assertions, but recurses indefinitely for
 *   components that re-schedule rAF inside the callback (force
 *   simulations, continuous render loops).
 *
 * - `false` — leave jsdom's default rAF in place (setTimeout-backed,
 *   ~16ms). Right for tests that exercise the actual rAF cadence and
 *   use `waitFor` to settle. Pairs with force-simulation specs.
 *
 * - `"noop"` — install a stub that returns an id but never fires the
 *   callback. Right for "observe initial state" regression tests that
 *   want a single mount-time render and nothing else (the frame
 *   regression suites that spy on `updateConfig` use this).
 *
 * - `"microtask"` — fire the callback on a `Promise.resolve().then(...)`
 *   microtask. Right for tests where synchronous fire would recurse
 *   `scheduleRender` but jsdom's setTimeout cadence introduces
 *   undesirable latency (StrictMode double-mount tests use this).
 */
export type StubRafMode = boolean | "noop" | "microtask";
export interface SetupCanvasMockOptions {
    stubRaf?: StubRafMode;
}
/**
 * Sets up the canvas getContext mock on HTMLCanvasElement.prototype, the
 * Path2D global, and (optionally) synchronous rAF/cAF stubs.
 *
 * @returns A cleanup function that restores the mocked globals.
 *
 * @example
 * ```ts
 * let cleanup: () => void
 * beforeEach(() => { cleanup = setupCanvasMock() })          // sync rAF (default)
 * beforeEach(() => { cleanup = setupCanvasMock({ stubRaf: false }) })       // jsdom rAF (force-sim specs)
 * beforeEach(() => { cleanup = setupCanvasMock({ stubRaf: "noop" }) })      // never fires (mount-only regression)
 * beforeEach(() => { cleanup = setupCanvasMock({ stubRaf: "microtask" }) }) // Promise.resolve fire (StrictMode)
 * afterEach(() => { cleanup() })
 * ```
 */
export declare function setupCanvasMock(options?: SetupCanvasMockOptions): () => void;
