import { WorkflowBuilder } from "../builder"; import type { WorkflowDefinition } from "../definition"; import { type TourViewDriver } from "../dom/tour-view-driver"; import type { GlowTour, GlowTourOptions, RunOptions, StartOptions, TourEventSource, TourState } from "../types"; /** * How long a step stays frozen on its last known position after its target * disappears from the DOM, before the configured `missingTarget.strategy` * takes over. Covers the dominant case — a framework remounting the target * within a frame or two — without a visible unmount/remount flicker. Not * configurable: it is a presentation detail of the recovery, not a policy * choice; `missingTarget.strategy` and `missingTarget.timeout` remain the only knobs. * Exported for the test suite's timing assertions only. */ export declare const TARGET_LOSS_GRACE_MS = 150; interface TourControllerOptions extends GlowTourOptions { assertCanRun?: (workflow: WorkflowDefinition) => Document | void; onDispose?: () => void; reportUnhandledError?: (error: Error) => void; } export declare class TourController { private readonly driver; private readonly options; private snapshot; private workflow; private steps; private index; private direction; private status; private error; /** * The target currently being recovered from a disconnect, if any. The * public status stays "active" through the grace period (see * TARGET_LOSS_GRACE_MS), so it can no longer serve as the re-entrancy guard * a repeated or overlapping `targetDisconnected` notification for the same * target relies on — this field takes over that job instead. */ private recoveringTarget; private operationToken; private publicationRevision; /** * The operation currently parked on a target that has not resolved yet, or `null`. The step being * left is still on screen while a navigation waits, so the wait is reported on its presentation. */ private awaitingTargetOperation; private operation; /** Set by `hidePopover()`, see `TourState.popoverHidden`. Only a running tour reports it. */ private popoverHidden; private disposed; private retainedPresentation; private readonly stateListeners; private readonly stepPropsSubscriptions; private tourStartedAt; private stepEnteredAt; /** * The step `tour:start` names, while that event is held back. It is emitted just before the * first event that can no longer be taken back, so a start that a `beforeEnter` aborts emits nothing. */ private pendingTourStart; private commandSource; readonly state: Readonly<{ get: () => TourState; subscribe: (listener: (state: TourState) => void) => () => void; }>; constructor(driver: TourViewDriver, options?: TourControllerOptions); create(name: string, options?: StartOptions): WorkflowBuilder; start(workflow: WorkflowDefinition, runOptions?: RunOptions): Promise; advance(source?: TourEventSource): Promise; previous(source?: TourEventSource): Promise; goTo(id: string): Promise; /** The direction of a jump from the current step to the step at `index`. */ private directionTo; cancel(source?: TourEventSource): Promise; /** `tour.hidePopover()` and `tour.showPopover()`. */ setPopoverHidden(hidden: boolean): void; private isRunning; dispose(): void; isDisposed(): boolean; /** @internal Called by the private root bridge before it releases DOM resources. */ beginMountRelease(): void; /** @internal Called after the private root bridge has finished releasing its lease. */ completeMountRelease(): void; /** * Moves to the step at `index`, or further in `direction` past steps skipped for a missing target. * Nothing is emitted before `beforeEnter` lets the navigation through, so an abort leaves no trace. * Then come the held `tour:start`, a `step:skip` per skipped step, and one `step:leave` for the * step being left. `lostStep` is the step whose target disappeared during recovery: it cannot stay * on screen, so skipping backward past the first step cancels the tour, and a `beforeEnter` abort * turns into its missing-target error. */ private navigate; private transitionFromPublic; private transition; /** Runs `beforeEnter` or `beforeLeave`, and tells whether it called `abort()` before settling. */ private runStepHook; private runActions; private resolveTarget; /** * Reports a navigation waiting on the next step's target. The presentation on screen still * belongs to the step being left, so the driver marks that popover and disables its advance * control until the target settles. Keyed by operation: a superseded navigation never clears the * wait its replacement declared. The freeze of a target lost mid-step is deliberately not a wait * here, see `recoverDisconnectedTarget`. */ private setAwaitingTarget; /** * Repeatedly re-resolves `step.target` until it succeeds or `budgetMs` * elapses, polling every 16ms like `resolveTarget`. Unlike `resolveTarget` * it never applies `missingTarget.strategy` itself — callers decide what a * timed-out budget means (grace period vs. a "wait" strategy's own * timeout), so the same polling loop serves both. */ private pollForTarget; /** * Recovers from a target disconnecting while its step is on screen. The * driver has already frozen the presentation in place (overlay, popover, * pointer held at their last position; focus guard and scroll lock still * engaged) and stopped polling geometry — this only decides how long to * keep it frozen and what to do once that budget runs out. * * The public status stays "active" for the whole freeze, "wait" included. * A frozen presentation isn't a transition: nothing is animating, the step * and its index are unchanged, and the popover is still on screen. Calling * it "transitioning" would close `canNavigate` and leave the user staring * at a live-looking popover whose buttons are dead for the rest of the * budget — the popover is the escape hatch out of a target that never * comes back, so it has to keep working. */ private recoverDisconnectedTarget; private missingTargetError; private finish; private cancelCurrent; private createLifecycleHookContext; /** * Restores the controller to its pre-`start()` idle state. Used when an * aborted hook prevents the tour from ever becoming active (`onStart`, the * first step's `beforeEnter`, and the zero-step `onFinish` edge case). A * tour this `start()` replaced is still on screen, so it is cleared first. */ private resetToIdle; private handleFailure; private beginOperation; private createStepContext; private createStepHookContext; private invalidateOperation; private signalFor; private assertCurrent; private isCurrent; private assertNotDisposed; private currentStep; private releaseStepPropsSubscriptions; private canNavigate; private isAdvanceButtonAvailable; private isPreviousButtonAvailable; private isCancelAvailable; private isPresentedAdvanceAvailable; private isPresentedPreviousAvailable; private isPresentedCancelAvailable; private canCancel; private setStatus; private publish; private notifyStateListener; /** * True when at least one listener is attached. Every emission site checks this * first so that a tour with no monitoring builds no payloads and reads no clock. */ private hasEventListeners; private emit; private notifyEventListener; /** Emits the held `tour:start` once, before the first event that can no longer be taken back. */ private flushTourStart; /** Emits `step:leave` for the step being left, with the time spent on it. */ private emitStepLeave; private reportSubscriberError; private reportUnhandledError; private createSnapshot; private capturePresentation; } /** * Creates a new GlowTour.js instance. * @param options Tour options for error handling. * @returns A tour controller ready to run workflows. */ export declare function createGlowTour(options?: GlowTourOptions): GlowTour; export {};