@apollovisionlabs/guide-core 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +295 -15
- package/dist/index.cjs +555 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +202 -4
- package/dist/index.d.ts +202 -4
- package/dist/index.mjs +561 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/storage.ts","../src/matchRoute.ts","../src/tourMachine.ts","../src/useTargetElement.ts","../src/selector.ts","../src/useElementRect.ts","../src/a11y.ts","../src/GuideProvider.tsx","../src/validateTour.ts","../src/useTour.ts","../src/useGuideStep.ts"],"sourcesContent":["import type { GuideStorage, TourProgress } from './types'\n\nexport function createMemoryStorage(\n initial: Record<string, TourProgress> = {},\n): GuideStorage {\n const store = new Map<string, TourProgress>(Object.entries(initial))\n return {\n async read(tourId) {\n return store.get(tourId) ?? null\n },\n async write(tourId, progress) {\n store.set(tourId, progress)\n },\n }\n}\n\nexport function createBrowserStorage(namespace = 'guide'): GuideStorage {\n const key = (tourId: string) => `${namespace}:${tourId}`\n const available = () => typeof window !== 'undefined' && !!window.localStorage\n\n return {\n async read(tourId) {\n if (!available()) return null\n try {\n const raw = window.localStorage.getItem(key(tourId))\n return raw ? (JSON.parse(raw) as TourProgress) : null\n } catch {\n return null\n }\n },\n async write(tourId, progress) {\n if (!available()) return\n try {\n window.localStorage.setItem(key(tourId), JSON.stringify(progress))\n } catch {\n // quota exceeded or storage blocked: persistence is optional\n }\n },\n }\n}\n","function segments(value: string): string[] {\n const path = value.split('?')[0] ?? ''\n const trimmed = path.replace(/\\/+$/, '')\n return (trimmed === '' ? '/' : trimmed).split('/')\n}\n\nexport function isLiteralRoute(pattern: string): boolean {\n return !pattern.includes(':') && !pattern.includes('*')\n}\n\nexport function matchRoute(pattern: string, pathname: string): boolean {\n const expected = segments(pattern)\n const actual = segments(pathname)\n\n for (let index = 0; index < expected.length; index += 1) {\n const segment = expected[index]\n if (segment === '*') return true\n\n const candidate = actual[index]\n if (candidate === undefined) return false\n\n if (segment?.startsWith(':')) {\n if (candidate === '') return false\n continue\n }\n\n if (segment !== candidate) return false\n }\n\n return expected.length === actual.length\n}\n","import type { TourStatus } from './types'\n\nexport interface TourState {\n tourId: string | null\n stepIndex: number\n status: TourStatus\n}\n\nexport type TourAction =\n | { type: 'START'; tourId: string; stepIndex: number }\n | { type: 'NEXT'; stepCount: number }\n | { type: 'PREVIOUS' }\n | { type: 'PAUSE' }\n | { type: 'RESUME' }\n | { type: 'STOP' }\n\nexport const initialTourState: TourState = {\n tourId: null,\n stepIndex: 0,\n status: 'idle',\n}\n\nexport function tourReducer(state: TourState, action: TourAction): TourState {\n switch (action.type) {\n case 'START':\n return { tourId: action.tourId, stepIndex: action.stepIndex, status: 'running' }\n\n // Navigating away from a paused tour resumes it: next() and previous() are public API, and calling them is an explicit request to move on.\n case 'NEXT': {\n if (state.status !== 'running' && state.status !== 'paused') return state\n const isLast = state.stepIndex >= action.stepCount - 1\n return isLast\n ? { ...state, status: 'completed' }\n : { ...state, stepIndex: state.stepIndex + 1, status: 'running' }\n }\n\n case 'PREVIOUS':\n if (state.status !== 'running' && state.status !== 'paused') return state\n return { ...state, stepIndex: Math.max(0, state.stepIndex - 1), status: 'running' }\n\n case 'PAUSE':\n return state.status === 'running' ? { ...state, status: 'paused' } : state\n\n case 'RESUME':\n return state.status === 'paused' ? { ...state, status: 'running' } : state\n\n case 'STOP':\n return initialTourState\n\n default:\n return state\n }\n}\n","import { useEffect, useState } from 'react'\nimport { targetSelector } from './selector'\n\nconst DEFAULT_TIMEOUT_MS = 5000\n\nexport interface UseTargetElementOptions {\n timeoutMs?: number\n attribute?: string\n}\n\ninterface TargetState {\n target: string | null\n element: HTMLElement | null\n timedOut: boolean\n}\n\nconst EMPTY: TargetState = { target: null, element: null, timedOut: false }\n\nexport function useTargetElement(\n target: string | null,\n options: UseTargetElementOptions = {},\n): { element: HTMLElement | null; timedOut: boolean } {\n const { timeoutMs = DEFAULT_TIMEOUT_MS, attribute = 'data-guide' } = options\n const [state, setState] = useState<TargetState>(EMPTY)\n\n useEffect(() => {\n if (!target || typeof document === 'undefined') {\n setState({ target, element: null, timedOut: false })\n return\n }\n\n const selector = targetSelector(target, attribute)\n const find = () => document.querySelector<HTMLElement>(selector)\n\n const found = find()\n if (found) {\n setState({ target, element: found, timedOut: false })\n return\n }\n\n setState({ target, element: null, timedOut: false })\n\n let timer: ReturnType<typeof setTimeout> | undefined\n\n const observer = new MutationObserver(() => {\n const candidate = find()\n if (!candidate) return\n observer.disconnect()\n if (timer) clearTimeout(timer)\n setState({ target, element: candidate, timedOut: false })\n })\n\n observer.observe(document.body, { childList: true, subtree: true, attributes: true })\n\n timer = setTimeout(() => {\n // Do not disconnect: the wait policy must be able to resume if the target appears later.\n // The observer callback and the cleanup take care of disconnecting.\n setState({ target, element: null, timedOut: true })\n }, timeoutMs)\n\n return () => {\n observer.disconnect()\n if (timer) clearTimeout(timer)\n }\n }, [target, timeoutMs, attribute])\n\n // Only expose the state when it matches the requested target: otherwise the caller would read\n // the previous step's state until the effect runs, and would skip twice.\n const current = state.target === target ? state : EMPTY\n return { element: current.element, timedOut: current.timedOut }\n}\n","// Target selector construction, shared by runtime resolution and by development-time\n// validation: a target containing a quote must be escaped on both paths, otherwise validation\n// throws a SyntaxError where resolution works.\nexport function escapeAttributeValue(value: string): string {\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {\n return CSS.escape(value)\n }\n return value.replace(/[\"\\\\]/g, '\\\\$&')\n}\n\nexport function targetSelector(target: string, attribute: string): string {\n return `[${attribute}=\"${escapeAttributeValue(target)}\"]`\n}\n","import { useEffect, useLayoutEffect, useState } from 'react'\nimport type { Rect } from './types'\n\n// Measure before paint: on a step change, a plain useEffect would let one frame through with\n// the spotlight still on the previous step's target.\nconst useIsomorphicLayoutEffect =\n typeof window !== 'undefined' ? useLayoutEffect : useEffect\n\nfunction sameRect(a: Rect, b: DOMRect): boolean {\n return a.top === b.top && a.left === b.left && a.width === b.width && a.height === b.height\n}\n\nexport function useElementRect(element: HTMLElement | null): Rect | null {\n const [rect, setRect] = useState<Rect | null>(null)\n\n useIsomorphicLayoutEffect(() => {\n if (!element) {\n setRect(null)\n return\n }\n\n const measure = () => {\n const next = element.getBoundingClientRect()\n setRect((previous) =>\n previous && sameRect(previous, next)\n ? previous\n : { top: next.top, left: next.left, width: next.width, height: next.height },\n )\n }\n\n measure()\n\n const observer =\n typeof ResizeObserver !== 'undefined' ? new ResizeObserver(measure) : null\n observer?.observe(element)\n\n window.addEventListener('scroll', measure, true)\n window.addEventListener('resize', measure)\n\n return () => {\n observer?.disconnect()\n window.removeEventListener('scroll', measure, true)\n window.removeEventListener('resize', measure)\n }\n }, [element])\n\n return rect\n}\n","import { useCallback, useEffect, useState } from 'react'\n\nconst FOCUSABLE =\n 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n\nexport interface UseFocusTrapOptions {\n /**\n * Element that receives focus on entry. 'first' takes the first focusable element; 'container'\n * takes the container itself, which must then carry tabIndex={-1}. Defaults to 'first'.\n */\n initialFocus?: 'first' | 'container'\n}\n\nexport function useFocusTrap(\n container: HTMLElement | null,\n active: boolean,\n options: UseFocusTrapOptions = {},\n): void {\n const { initialFocus = 'first' } = options\n\n useEffect(() => {\n if (!container || !active) return\n\n const previouslyFocused = document.activeElement as HTMLElement | null\n // No visibility filter: the selector already excludes disabled elements and elements out of\n // the tab order, and the popover mounts or unmounts its controls rather than hiding them.\n const focusable = () => Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE))\n\n // 'container' avoids putting focus on an actionable button: a reflex Enter after an arrow\n // key must not close the tour.\n const first = initialFocus === 'container' ? undefined : focusable()[0]\n if (first) first.focus()\n else container.focus()\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Tab') return\n const elements = focusable()\n if (elements.length === 0) return\n\n const firstElement = elements[0]!\n const lastElement = elements[elements.length - 1]!\n\n if (event.shiftKey && document.activeElement === firstElement) {\n event.preventDefault()\n lastElement.focus()\n } else if (!event.shiftKey && document.activeElement === lastElement) {\n event.preventDefault()\n firstElement.focus()\n }\n }\n\n document.addEventListener('keydown', onKeyDown, true)\n\n return () => {\n document.removeEventListener('keydown', onKeyDown, true)\n previouslyFocused?.focus?.()\n }\n }, [container, active, initialFocus])\n}\n\nfunction announcerNode(): HTMLElement {\n const existing = document.querySelector<HTMLElement>('[data-guide-announcer]')\n if (existing) return existing\n\n const node = document.createElement('div')\n node.setAttribute('data-guide-announcer', '')\n node.setAttribute('aria-live', 'polite')\n node.setAttribute('aria-atomic', 'true')\n node.style.position = 'absolute'\n node.style.width = '1px'\n node.style.height = '1px'\n node.style.overflow = 'hidden'\n node.style.clip = 'rect(0 0 0 0)'\n node.style.whiteSpace = 'nowrap'\n document.body.appendChild(node)\n return node\n}\n\nexport function useAnnouncer(): (message: string) => void {\n return useCallback((message: string) => {\n if (typeof document === 'undefined') return\n announcerNode().textContent = message\n }, [])\n}\n\nexport function usePrefersReducedMotion(): boolean {\n const [reduced, setReduced] = useState(false)\n\n useEffect(() => {\n if (typeof window === 'undefined' || !window.matchMedia) return\n const query = window.matchMedia('(prefers-reduced-motion: reduce)')\n setReduced(query.matches)\n const onChange = (event: MediaQueryListEvent) => setReduced(event.matches)\n query.addEventListener('change', onChange)\n return () => query.removeEventListener('change', onChange)\n }, [])\n\n return reduced\n}\n","'use client'\n\nimport {\n createContext,\n useCallback,\n useEffect,\n useMemo,\n useReducer,\n useRef,\n useState,\n type ReactNode,\n} from 'react'\nimport type {\n GuideEvent,\n GuideStorage,\n MissingTargetPolicy,\n Rect,\n Step,\n Tour,\n TourProgress,\n Translate,\n} from './types'\nimport { initialTourState, tourReducer, type TourState } from './tourMachine'\nimport { isLiteralRoute, matchRoute } from './matchRoute'\nimport { useTargetElement } from './useTargetElement'\nimport { useElementRect } from './useElementRect'\nimport { useAnnouncer } from './a11y'\nimport { findMissingTargets } from './validateTour'\n\nexport interface ActiveStep {\n tourId: string\n step: Step\n stepIndex: number\n stepCount: number\n element: HTMLElement | null\n rect: Rect | null\n title: string\n body: string\n isFirst: boolean\n isLast: boolean\n next: () => void\n previous: () => void\n stop: () => void\n}\n\nexport interface GuideContextValue {\n state: TourState\n activeStep: ActiveStep | null\n start: (tourId: string, options?: { from?: number; resume?: boolean }) => Promise<void>\n next: () => void\n previous: () => void\n stop: () => void\n}\n\nexport const GuideContext = createContext<GuideContextValue | null>(null)\n\nexport interface GuideProviderProps {\n tours: Tour[]\n children: ReactNode\n navigate?: (path: string) => void\n location?: string\n storage?: GuideStorage\n translate?: Translate\n onEvent?: (event: GuideEvent) => void\n onMissingTarget?: MissingTargetPolicy\n targetTimeoutMs?: number\n}\n\nfunction resolveText(\n value: string | undefined,\n key: string | undefined,\n translate: Translate | undefined,\n): string {\n if (value !== undefined) return value\n if (key === undefined) return ''\n return translate ? translate(key) : key\n}\n\nexport function GuideProvider({\n tours,\n children,\n navigate,\n location,\n storage,\n translate,\n onEvent,\n onMissingTarget = 'wait',\n targetTimeoutMs = 5000,\n}: GuideProviderProps) {\n const toursById = useMemo(() => {\n const map = new Map<string, Tour>()\n for (const candidate of tours) {\n if (map.has(candidate.id)) {\n throw new Error(`[guide] duplicate tour id: ${candidate.id}`)\n }\n map.set(candidate.id, candidate)\n }\n return map\n }, [tours])\n\n const [state, dispatch] = useReducer(tourReducer, initialTourState)\n const announce = useAnnouncer()\n\n // Element that held focus when the tour started: the popover unmounts and remounts on every\n // step, so its own focus trap cannot restore focus to that origin.\n const focusOriginRef = useRef<HTMLElement | null>(null)\n const storageWarnedRef = useRef(false)\n\n const warnStorageFailure = useCallback((error: unknown) => {\n if (storageWarnedRef.current) return\n storageWarnedRef.current = true\n console.warn('[guide] storage failed; tour progress will not be persisted', error)\n }, [])\n\n const onEventRef = useRef(onEvent)\n onEventRef.current = onEvent\n const emit = useCallback((event: GuideEvent) => onEventRef.current?.(event), [])\n\n const tour = state.tourId ? (toursById.get(state.tourId) ?? null) : null\n const step = tour ? (tour.steps[state.stepIndex] ?? null) : null\n const isActive = state.status === 'running' || state.status === 'paused'\n\n const routeMatches =\n !step?.route || location === undefined || matchRoute(step.route, location)\n\n const { element, timedOut: targetTimedOut } = useTargetElement(\n isActive && routeMatches && step ? step.target : null,\n { timeoutMs: targetTimeoutMs },\n )\n const rect = useElementRect(element)\n\n // A step whose route never matches requests no target, so no timeout is running. Without this\n // timer, a wrong route pattern or a failed navigation would leave the tour running, invisible\n // and with no way out. The timer armed here is what makes the policy apply.\n const waitingForRoute = isActive && !!step && !routeMatches\n // The expired step is stored rather than a boolean: otherwise the next step would inherit the\n // previous one's expiry for one render and the policy would apply twice.\n const [routeTimeoutStep, setRouteTimeoutStep] = useState<Step | null>(null)\n\n useEffect(() => {\n if (!waitingForRoute) {\n setRouteTimeoutStep(null)\n return\n }\n const timer = setTimeout(() => setRouteTimeoutStep(step), targetTimeoutMs)\n return () => clearTimeout(timer)\n }, [waitingForRoute, step, targetTimeoutMs])\n\n const timedOut = targetTimedOut || (waitingForRoute && routeTimeoutStep === step)\n\n const next = useCallback(() => {\n if (!tour) return\n const isLast = state.stepIndex >= tour.steps.length - 1\n dispatch({ type: 'NEXT', stepCount: tour.steps.length })\n if (isLast) emit({ type: 'tour:complete', tourId: tour.id })\n }, [tour, state.stepIndex, emit])\n\n const previous = useCallback(() => dispatch({ type: 'PREVIOUS' }), [])\n\n const stop = useCallback(() => {\n if (tour) emit({ type: 'tour:stop', tourId: tour.id, stepIndex: state.stepIndex })\n dispatch({ type: 'STOP' })\n }, [tour, state.stepIndex, emit])\n\n // Delegated navigation: the step lives elsewhere, so we ask for the move.\n // The destination already requested for the current step is kept in a ref: without it, if the\n // route never matches, this effect would call navigate again on every render.\n const navigationRef = useRef<{ step: Step | null; destination: string | null }>({\n step: null,\n destination: null,\n })\n\n const start = useCallback(\n async (tourId: string, options?: { from?: number; resume?: boolean }) => {\n // Reentrancy: a second call while this same tour is running would re-read persistence and\n // could move the progress backwards. Switching to another tour stays allowed.\n if (state.tourId === tourId && state.status === 'running') return\n\n const target = toursById.get(tourId)\n if (!target) throw new Error(`[guide] unknown tour: ${tourId}`)\n if (target.steps.length === 0) {\n throw new Error(`[guide] tour has no steps: ${tourId}`)\n }\n\n if (typeof document !== 'undefined') {\n focusOriginRef.current = document.activeElement as HTMLElement | null\n }\n\n let stepIndex = options?.from ?? 0\n if (options?.from === undefined && options?.resume !== false && storage) {\n // Storage that fails must not block the tour: we start from the beginning.\n let progress: TourProgress | null = null\n try {\n progress = await storage.read(tourId)\n } catch (error) {\n warnStorageFailure(error)\n }\n if (progress?.status === 'in-progress') stepIndex = progress.stepIndex\n }\n\n if (process.env.NODE_ENV !== 'production') {\n const missing = findMissingTargets(target, location)\n if (missing.length > 0) {\n console.warn(\n `[guide] tour \"${tourId}\" declares targets that are not present on this page: ${missing.join(', ')}`,\n )\n }\n }\n\n // Restarting the same tour on the same step must navigate again: without this reset, the\n // destination already requested would stay remembered and the effect would skip navigate.\n navigationRef.current = { step: null, destination: null }\n\n dispatch({ type: 'START', tourId, stepIndex })\n emit({ type: 'tour:start', tourId, stepIndex })\n },\n [toursById, storage, location, emit, state.tourId, state.status, warnStorageFailure],\n )\n\n useEffect(() => {\n if (!isActive || !step || routeMatches) return\n\n if (navigationRef.current.step !== step) {\n navigationRef.current = { step, destination: null }\n }\n\n const destination =\n step.navigateTo ?? (step.route && isLiteralRoute(step.route) ? step.route : null)\n\n if (!destination) return\n if (navigationRef.current.destination === destination) return\n if (!navigate) {\n console.warn('[guide] a step declares a route but no navigate function was provided')\n return\n }\n navigationRef.current.destination = destination\n navigate(destination)\n }, [isActive, step, routeMatches, navigate])\n\n // Target not found: apply the policy.\n useEffect(() => {\n if (!timedOut || !tour || !step) return\n\n emit({\n type: 'target:missing',\n tourId: tour.id,\n stepIndex: state.stepIndex,\n target: step.target,\n })\n\n const policy = step.onMissingTarget ?? onMissingTarget\n if (policy === 'skip') dispatch({ type: 'NEXT', stepCount: tour.steps.length })\n else if (policy === 'error') dispatch({ type: 'STOP' })\n else dispatch({ type: 'PAUSE' })\n }, [timedOut, tour, step, state.stepIndex, onMissingTarget, emit])\n\n // Automatic resume when the target reappears after a pause.\n useEffect(() => {\n if (state.status === 'paused' && element) dispatch({ type: 'RESUME' })\n }, [state.status, element])\n\n // Step actually on screen.\n useEffect(() => {\n if (state.status !== 'running' || !tour || !step || !element) return\n emit({\n type: 'step:show',\n tourId: tour.id,\n stepIndex: state.stepIndex,\n target: step.target,\n })\n announce(`${state.stepIndex + 1} / ${tour.steps.length}`)\n }, [state.status, state.stepIndex, tour, step, element, emit, announce])\n\n // Progress persistence. A write that fails breaks nothing: the progress is simply not kept.\n useEffect(() => {\n if (!storage || !state.tourId) return\n const status =\n state.status === 'running'\n ? 'in-progress'\n : state.status === 'completed'\n ? 'completed'\n : null\n if (!status) return\n try {\n void Promise.resolve(\n storage.write(state.tourId, { status, stepIndex: state.stepIndex }),\n ).catch(warnStorageFailure)\n } catch (error) {\n warnStorageFailure(error)\n }\n }, [storage, state.tourId, state.status, state.stepIndex, warnStorageFailure])\n\n // Focus returns to its origin once the tour is stopped or completed.\n useEffect(() => {\n if (state.status !== 'idle' && state.status !== 'completed') return\n const origin = focusOriginRef.current\n if (!origin) return\n focusOriginRef.current = null\n if (typeof document !== 'undefined' && document.contains(origin)) origin.focus()\n }, [state.status])\n\n const activeStep = useMemo<ActiveStep | null>(() => {\n if (!tour || !step || !isActive) return null\n return {\n tourId: tour.id,\n step,\n stepIndex: state.stepIndex,\n stepCount: tour.steps.length,\n element,\n rect,\n title: resolveText(step.title, step.titleKey, translate),\n body: resolveText(step.body, step.bodyKey, translate),\n isFirst: state.stepIndex === 0,\n isLast: state.stepIndex === tour.steps.length - 1,\n next,\n previous,\n stop,\n }\n }, [tour, step, isActive, state.stepIndex, element, rect, translate, next, previous, stop])\n\n const value = useMemo<GuideContextValue>(\n () => ({ state, activeStep, start, next, previous, stop }),\n [state, activeStep, start, next, previous, stop],\n )\n\n return <GuideContext.Provider value={value}>{children}</GuideContext.Provider>\n}\n","import type { Tour } from './types'\nimport { matchRoute } from './matchRoute'\nimport { targetSelector } from './selector'\n\nexport function findMissingTargets(\n tour: Tour,\n location: string | undefined,\n attribute = 'data-guide',\n): string[] {\n if (typeof document === 'undefined') return []\n\n return tour.steps\n .filter((step) => !step.route || location === undefined || matchRoute(step.route, location))\n .map((step) => step.target)\n .filter((target) => !document.querySelector(targetSelector(target, attribute)))\n}\n","'use client'\n\nimport { useContext, useMemo } from 'react'\nimport { GuideContext } from './GuideProvider'\nimport type { TourStatus } from './types'\n\nexport interface UseTourResult {\n start: (options?: { from?: number; resume?: boolean }) => Promise<void>\n next: () => void\n previous: () => void\n stop: () => void\n status: TourStatus\n stepIndex: number\n}\n\nexport function useTour(tourId: string): UseTourResult {\n const context = useContext(GuideContext)\n if (!context) throw new Error('[guide] useTour must be used inside a GuideProvider')\n\n const { state, start, next, previous, stop } = context\n const isCurrent = state.tourId === tourId\n\n return useMemo(\n () => ({\n start: (options) => start(tourId, options),\n next,\n previous,\n stop,\n status: isCurrent ? state.status : 'idle',\n stepIndex: isCurrent ? state.stepIndex : 0,\n }),\n [tourId, start, next, previous, stop, isCurrent, state.status, state.stepIndex],\n )\n}\n","'use client'\n\nimport { useContext } from 'react'\nimport { GuideContext, type ActiveStep } from './GuideProvider'\n\nexport function useGuideStep(): ActiveStep | null {\n const context = useContext(GuideContext)\n if (!context) throw new Error('[guide] useGuideStep must be used inside a GuideProvider')\n return context.activeStep\n}\n"],"mappings":"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","names":["useEffect","useState","useEffect","useState","useCallback","useEffect","useState","useCallback","useState","useEffect","useMemo","useMemo","useContext","useContext"]}
|
|
1
|
+
{"version":3,"sources":["../src/storage.ts","../src/matchRoute.ts","../src/tourMachine.ts","../src/useTargetElement.ts","../src/selector.ts","../src/useElementRect.ts","../src/a11y.ts","../src/GuideProvider.tsx","../src/validateTour.ts","../src/resolveText.ts","../src/useTour.ts","../src/useGuideStep.ts","../src/ChecklistProvider.tsx","../src/useChecklist.ts","../src/HotspotProvider.tsx","../src/useHotspots.ts"],"sourcesContent":["import type { ChecklistProgress, GuideStorage, HotspotsProgress, TourProgress } from './types'\n\nexport function createMemoryStorage(\n initial: Record<string, unknown> = {},\n): GuideStorage {\n const store = new Map<string, unknown>(Object.entries(initial))\n return {\n async read<T>(key: string) {\n return (store.get(key) ?? null) as T | null\n },\n async write<T>(key: string, value: T) {\n store.set(key, value)\n },\n }\n}\n\nexport function createBrowserStorage(namespace = 'guide'): GuideStorage {\n const key = (storageKey: string) => `${namespace}:${storageKey}`\n const available = () => typeof window !== 'undefined' && !!window.localStorage\n\n return {\n async read<T>(storageKey: string) {\n if (!available()) return null\n try {\n const raw = window.localStorage.getItem(key(storageKey))\n return raw ? (JSON.parse(raw) as T) : null\n } catch {\n return null\n }\n },\n async write<T>(storageKey: string, value: T) {\n if (!available()) return\n try {\n window.localStorage.setItem(key(storageKey), JSON.stringify(value))\n } catch {\n // quota exceeded or storage blocked: persistence is optional\n }\n },\n }\n}\n\n/**\n * A stored value survives code changes, browser extensions and hand editing,\n * so nothing read back is trusted until its shape is checked.\n */\nexport function isTourProgress(value: unknown): value is TourProgress {\n if (typeof value !== 'object' || value === null) return false\n const candidate = value as Record<string, unknown>\n return (\n typeof candidate.stepIndex === 'number' &&\n Number.isInteger(candidate.stepIndex) &&\n candidate.stepIndex >= 0 &&\n (candidate.status === 'in-progress' || candidate.status === 'completed')\n )\n}\n\n/**\n * Same defensive posture as isTourProgress: a stored checklist value is\n * never trusted until its shape is checked.\n */\nexport function isChecklistProgress(value: unknown): value is ChecklistProgress {\n if (typeof value !== 'object' || value === null) return false\n const candidate = value as Record<string, unknown>\n return (\n Array.isArray(candidate.completed) &&\n candidate.completed.every((entry) => typeof entry === 'string') &&\n typeof candidate.dismissed === 'boolean'\n )\n}\n\n/**\n * Same defensive posture as the two guards above: a stored hotspot value is never trusted\n * until its shape is checked.\n */\nexport function isHotspotsProgress(value: unknown): value is HotspotsProgress {\n if (typeof value !== 'object' || value === null) return false\n const candidate = value as Record<string, unknown>\n return (\n Array.isArray(candidate.seen) &&\n candidate.seen.every((entry) => typeof entry === 'string')\n )\n}\n","function segments(value: string): string[] {\n const path = value.split('?')[0] ?? ''\n const trimmed = path.replace(/\\/+$/, '')\n return (trimmed === '' ? '/' : trimmed).split('/')\n}\n\nexport function isLiteralRoute(pattern: string): boolean {\n return !pattern.includes(':') && !pattern.includes('*')\n}\n\nexport function matchRoute(pattern: string, pathname: string): boolean {\n const expected = segments(pattern)\n const actual = segments(pathname)\n\n for (let index = 0; index < expected.length; index += 1) {\n const segment = expected[index]\n if (segment === '*') return true\n\n const candidate = actual[index]\n if (candidate === undefined) return false\n\n if (segment?.startsWith(':')) {\n if (candidate === '') return false\n continue\n }\n\n if (segment !== candidate) return false\n }\n\n return expected.length === actual.length\n}\n","import type { TourStatus } from './types'\n\nexport interface TourState {\n tourId: string | null\n stepIndex: number\n status: TourStatus\n}\n\nexport type TourAction =\n | { type: 'START'; tourId: string; stepIndex: number }\n | { type: 'NEXT'; stepCount: number }\n | { type: 'PREVIOUS' }\n | { type: 'PAUSE' }\n | { type: 'RESUME' }\n | { type: 'STOP' }\n\nexport const initialTourState: TourState = {\n tourId: null,\n stepIndex: 0,\n status: 'idle',\n}\n\nexport function tourReducer(state: TourState, action: TourAction): TourState {\n switch (action.type) {\n case 'START':\n return { tourId: action.tourId, stepIndex: action.stepIndex, status: 'running' }\n\n // Navigating away from a paused tour resumes it: next() and previous() are public API, and calling them is an explicit request to move on.\n case 'NEXT': {\n if (state.status !== 'running' && state.status !== 'paused') return state\n const isLast = state.stepIndex >= action.stepCount - 1\n return isLast\n ? { ...state, status: 'completed' }\n : { ...state, stepIndex: state.stepIndex + 1, status: 'running' }\n }\n\n case 'PREVIOUS':\n if (state.status !== 'running' && state.status !== 'paused') return state\n return { ...state, stepIndex: Math.max(0, state.stepIndex - 1), status: 'running' }\n\n case 'PAUSE':\n return state.status === 'running' ? { ...state, status: 'paused' } : state\n\n case 'RESUME':\n return state.status === 'paused' ? { ...state, status: 'running' } : state\n\n case 'STOP':\n return initialTourState\n\n default:\n return state\n }\n}\n","import { useEffect, useState } from 'react'\nimport { targetSelector } from './selector'\n\nconst DEFAULT_TIMEOUT_MS = 5000\n\nexport interface UseTargetElementOptions {\n timeoutMs?: number\n attribute?: string\n}\n\ninterface TargetState {\n target: string | null\n element: HTMLElement | null\n timedOut: boolean\n}\n\nconst EMPTY: TargetState = { target: null, element: null, timedOut: false }\n\nexport function useTargetElement(\n target: string | null,\n options: UseTargetElementOptions = {},\n): { element: HTMLElement | null; timedOut: boolean } {\n const { timeoutMs = DEFAULT_TIMEOUT_MS, attribute = 'data-guide' } = options\n const [state, setState] = useState<TargetState>(EMPTY)\n\n useEffect(() => {\n if (!target || typeof document === 'undefined') {\n setState({ target, element: null, timedOut: false })\n return\n }\n\n const selector = targetSelector(target, attribute)\n const find = () => document.querySelector<HTMLElement>(selector)\n\n const found = find()\n if (found) {\n setState({ target, element: found, timedOut: false })\n return\n }\n\n setState({ target, element: null, timedOut: false })\n\n let timer: ReturnType<typeof setTimeout> | undefined\n\n const observer = new MutationObserver(() => {\n const candidate = find()\n if (!candidate) return\n observer.disconnect()\n if (timer) clearTimeout(timer)\n setState({ target, element: candidate, timedOut: false })\n })\n\n observer.observe(document.body, { childList: true, subtree: true, attributes: true })\n\n timer = setTimeout(() => {\n // Do not disconnect: the wait policy must be able to resume if the target appears later.\n // The observer callback and the cleanup take care of disconnecting.\n setState({ target, element: null, timedOut: true })\n }, timeoutMs)\n\n return () => {\n observer.disconnect()\n if (timer) clearTimeout(timer)\n }\n }, [target, timeoutMs, attribute])\n\n // Only expose the state when it matches the requested target: otherwise the caller would read\n // the previous step's state until the effect runs, and would skip twice.\n const current = state.target === target ? state : EMPTY\n return { element: current.element, timedOut: current.timedOut }\n}\n","// Target selector construction, shared by runtime resolution and by development-time\n// validation: a target containing a quote must be escaped on both paths, otherwise validation\n// throws a SyntaxError where resolution works.\nexport function escapeAttributeValue(value: string): string {\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {\n return CSS.escape(value)\n }\n return value.replace(/[\"\\\\]/g, '\\\\$&')\n}\n\nexport function targetSelector(target: string, attribute: string): string {\n return `[${attribute}=\"${escapeAttributeValue(target)}\"]`\n}\n","import { useEffect, useLayoutEffect, useState } from 'react'\nimport type { Rect } from './types'\n\n// Measure before paint: on a step change, a plain useEffect would let one frame through with\n// the spotlight still on the previous step's target.\nconst useIsomorphicLayoutEffect =\n typeof window !== 'undefined' ? useLayoutEffect : useEffect\n\nfunction sameRect(a: Rect, b: DOMRect): boolean {\n return a.top === b.top && a.left === b.left && a.width === b.width && a.height === b.height\n}\n\nexport function useElementRect(element: HTMLElement | null): Rect | null {\n const [rect, setRect] = useState<Rect | null>(null)\n\n useIsomorphicLayoutEffect(() => {\n if (!element) {\n setRect(null)\n return\n }\n\n const measure = () => {\n const next = element.getBoundingClientRect()\n setRect((previous) =>\n previous && sameRect(previous, next)\n ? previous\n : { top: next.top, left: next.left, width: next.width, height: next.height },\n )\n }\n\n measure()\n\n const observer =\n typeof ResizeObserver !== 'undefined' ? new ResizeObserver(measure) : null\n observer?.observe(element)\n\n window.addEventListener('scroll', measure, true)\n window.addEventListener('resize', measure)\n\n return () => {\n observer?.disconnect()\n window.removeEventListener('scroll', measure, true)\n window.removeEventListener('resize', measure)\n }\n }, [element])\n\n return rect\n}\n","import { useCallback, useEffect, useState } from 'react'\n\nconst FOCUSABLE =\n 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n\nexport interface UseFocusTrapOptions {\n /**\n * Element that receives focus on entry. 'first' takes the first focusable element; 'container'\n * takes the container itself, which must then carry tabIndex={-1}. Defaults to 'first'.\n */\n initialFocus?: 'first' | 'container'\n}\n\nexport function useFocusTrap(\n container: HTMLElement | null,\n active: boolean,\n options: UseFocusTrapOptions = {},\n): void {\n const { initialFocus = 'first' } = options\n\n useEffect(() => {\n if (!container || !active) return\n\n const previouslyFocused = document.activeElement as HTMLElement | null\n // No visibility filter: the selector already excludes disabled elements and elements out of\n // the tab order, and the popover mounts or unmounts its controls rather than hiding them.\n const focusable = () => Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE))\n\n // 'container' avoids putting focus on an actionable button: a reflex Enter after an arrow\n // key must not close the tour.\n const first = initialFocus === 'container' ? undefined : focusable()[0]\n if (first) first.focus()\n else container.focus()\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Tab') return\n const elements = focusable()\n if (elements.length === 0) return\n\n const firstElement = elements[0]!\n const lastElement = elements[elements.length - 1]!\n\n if (event.shiftKey && document.activeElement === firstElement) {\n event.preventDefault()\n lastElement.focus()\n } else if (!event.shiftKey && document.activeElement === lastElement) {\n event.preventDefault()\n firstElement.focus()\n }\n }\n\n document.addEventListener('keydown', onKeyDown, true)\n\n return () => {\n document.removeEventListener('keydown', onKeyDown, true)\n previouslyFocused?.focus?.()\n }\n }, [container, active, initialFocus])\n}\n\nfunction announcerNode(): HTMLElement {\n const existing = document.querySelector<HTMLElement>('[data-guide-announcer]')\n if (existing) return existing\n\n const node = document.createElement('div')\n node.setAttribute('data-guide-announcer', '')\n node.setAttribute('aria-live', 'polite')\n node.setAttribute('aria-atomic', 'true')\n node.style.position = 'absolute'\n node.style.width = '1px'\n node.style.height = '1px'\n node.style.overflow = 'hidden'\n node.style.clip = 'rect(0 0 0 0)'\n node.style.whiteSpace = 'nowrap'\n document.body.appendChild(node)\n return node\n}\n\nexport function useAnnouncer(): (message: string) => void {\n return useCallback((message: string) => {\n if (typeof document === 'undefined') return\n announcerNode().textContent = message\n }, [])\n}\n\nexport function usePrefersReducedMotion(): boolean {\n const [reduced, setReduced] = useState(false)\n\n useEffect(() => {\n if (typeof window === 'undefined' || !window.matchMedia) return\n const query = window.matchMedia('(prefers-reduced-motion: reduce)')\n setReduced(query.matches)\n const onChange = (event: MediaQueryListEvent) => setReduced(event.matches)\n query.addEventListener('change', onChange)\n return () => query.removeEventListener('change', onChange)\n }, [])\n\n return reduced\n}\n","'use client'\n\nimport {\n createContext,\n useCallback,\n useEffect,\n useMemo,\n useReducer,\n useRef,\n useState,\n type ReactNode,\n} from 'react'\nimport type {\n GuideEvent,\n GuideStorage,\n MissingTargetPolicy,\n Rect,\n Step,\n Tour,\n TourProgress,\n Translate,\n} from './types'\nimport { initialTourState, tourReducer, type TourState } from './tourMachine'\nimport { isLiteralRoute, matchRoute } from './matchRoute'\nimport { useTargetElement } from './useTargetElement'\nimport { useElementRect } from './useElementRect'\nimport { useAnnouncer } from './a11y'\nimport { findMissingTargets } from './validateTour'\nimport { isTourProgress } from './storage'\nimport { resolveText } from './resolveText'\n\nexport interface ActiveStep {\n tourId: string\n step: Step\n stepIndex: number\n stepCount: number\n element: HTMLElement | null\n rect: Rect | null\n /**\n * Whether the page stays reachable during the step. True when the step declares\n * `interactive`, and also when it declares `advanceOn`, whose click has to reach the\n * element. Renderers read this instead of `step.interactive`, so the rule lives here.\n */\n interactive: boolean\n /** True when the step advances on a user action rather than on a button. */\n awaitsAction: boolean\n title: string\n body: string\n isFirst: boolean\n isLast: boolean\n next: () => void\n previous: () => void\n stop: () => void\n}\n\nexport interface GuideContextValue {\n state: TourState\n activeStep: ActiveStep | null\n start: (tourId: string, options?: { from?: number; resume?: boolean }) => Promise<void>\n next: () => void\n previous: () => void\n stop: () => void\n}\n\nexport const GuideContext = createContext<GuideContextValue | null>(null)\n\nexport interface GuideProviderProps {\n tours: Tour[]\n children: ReactNode\n navigate?: (path: string) => void\n location?: string\n storage?: GuideStorage\n translate?: Translate\n onEvent?: (event: GuideEvent) => void\n onMissingTarget?: MissingTargetPolicy\n targetTimeoutMs?: number\n}\n\n/**\n * Focus an element that is ordinary application markup and need not be focusable. A tabindex is\n * added only when the element does not already carry one and cannot take focus on its own, and\n * it is dropped again as soon as focus leaves, so nothing permanent is left in the host's DOM;\n * -1 keeps the element out of the tab order meanwhile. An element the host put in the tab order\n * itself is focused as it is and never touched.\n *\n * The attribute is verified to have worked before anything is left behind. An element that\n * cannot take focus at all, one with no box because it or an ancestor is `display: none` being\n * the ordinary case, silently ignores `focus()` and fires no blur, so a tabindex written\n * optimistically and a listener waiting for that blur would both outlive the page. Where focus\n * cannot be placed, this places none and leaves the element exactly as it found it.\n */\nfunction focusFallback(element: HTMLElement): void {\n const needsTabIndex = !element.hasAttribute('tabindex') && element.tabIndex < 0\n if (!needsTabIndex) {\n element.focus()\n return\n }\n\n element.setAttribute('tabindex', '-1')\n element.focus()\n if (document.activeElement !== element) {\n element.removeAttribute('tabindex')\n return\n }\n\n const onBlur = () => {\n element.removeAttribute('tabindex')\n element.removeEventListener('blur', onBlur)\n }\n element.addEventListener('blur', onBlur)\n}\n\nexport function GuideProvider({\n tours,\n children,\n navigate,\n location,\n storage,\n translate,\n onEvent,\n onMissingTarget = 'wait',\n targetTimeoutMs = 5000,\n}: GuideProviderProps) {\n const toursById = useMemo(() => {\n const map = new Map<string, Tour>()\n for (const candidate of tours) {\n if (map.has(candidate.id)) {\n throw new Error(`[guide] duplicate tour id: ${candidate.id}`)\n }\n map.set(candidate.id, candidate)\n }\n return map\n }, [tours])\n\n const [state, dispatch] = useReducer(tourReducer, initialTourState)\n const announce = useAnnouncer()\n\n // Element that held focus when the tour started: the popover unmounts and remounts on every\n // step, so its own focus trap cannot restore focus to that origin.\n const focusOriginRef = useRef<HTMLElement | null>(null)\n // Element the tour last pointed at, kept in a ref because by the time focus is restored the\n // step has already been torn down and `element` is null again. This is the destination of\n // last resort: see the restore effect below.\n const lastElementRef = useRef<HTMLElement | null>(null)\n const storageWarnedRef = useRef(false)\n\n const warnStorageFailure = useCallback((error: unknown) => {\n if (storageWarnedRef.current) return\n storageWarnedRef.current = true\n console.warn('[guide] storage failed; tour progress will not be persisted', error)\n }, [])\n\n const onEventRef = useRef(onEvent)\n onEventRef.current = onEvent\n const emit = useCallback((event: GuideEvent) => onEventRef.current?.(event), [])\n\n const tour = state.tourId ? (toursById.get(state.tourId) ?? null) : null\n const step = tour ? (tour.steps[state.stepIndex] ?? null) : null\n const isActive = state.status === 'running' || state.status === 'paused'\n\n const routeMatches =\n !step?.route || location === undefined || matchRoute(step.route, location)\n\n const { element, timedOut: targetTimedOut } = useTargetElement(\n isActive && routeMatches && step ? step.target : null,\n { timeoutMs: targetTimeoutMs },\n )\n const rect = useElementRect(element)\n\n // A step whose route never matches requests no target, so no timeout is running. Without this\n // timer, a wrong route pattern or a failed navigation would leave the tour running, invisible\n // and with no way out. The timer armed here is what makes the policy apply.\n const waitingForRoute = isActive && !!step && !routeMatches\n // The expired step is stored rather than a boolean: otherwise the next step would inherit the\n // previous one's expiry for one render and the policy would apply twice.\n const [routeTimeoutStep, setRouteTimeoutStep] = useState<Step | null>(null)\n\n useEffect(() => {\n if (!waitingForRoute) {\n setRouteTimeoutStep(null)\n return\n }\n const timer = setTimeout(() => setRouteTimeoutStep(step), targetTimeoutMs)\n return () => clearTimeout(timer)\n }, [waitingForRoute, step, targetTimeoutMs])\n\n const timedOut = targetTimedOut || (waitingForRoute && routeTimeoutStep === step)\n\n const next = useCallback(() => {\n if (!tour) return\n const isLast = state.stepIndex >= tour.steps.length - 1\n dispatch({ type: 'NEXT', stepCount: tour.steps.length })\n if (isLast) emit({ type: 'tour:complete', tourId: tour.id })\n }, [tour, state.stepIndex, emit])\n\n // A step that waits for a click observes the element rather than wrapping it: bubble phase,\n // no preventDefault, no stopPropagation. The application's own handler runs first and keeps\n // working; the tour only notices that it ran.\n //\n // `next` is read through a ref so that a re-render does not detach and reattach the listener\n // on every step of every tour.\n const nextRef = useRef(next)\n nextRef.current = next\n\n useEffect(() => {\n if (state.status !== 'running' || !element || step?.advanceOn !== 'click') return\n const onClick = () => nextRef.current()\n element.addEventListener('click', onClick)\n return () => element.removeEventListener('click', onClick)\n }, [state.status, element, step?.advanceOn])\n\n const previous = useCallback(() => dispatch({ type: 'PREVIOUS' }), [])\n\n const stop = useCallback(() => {\n if (tour) emit({ type: 'tour:stop', tourId: tour.id, stepIndex: state.stepIndex })\n dispatch({ type: 'STOP' })\n }, [tour, state.stepIndex, emit])\n\n // Delegated navigation: the step lives elsewhere, so we ask for the move.\n // The destination already requested for the current step is kept in a ref: without it, if the\n // route never matches, this effect would call navigate again on every render.\n const navigationRef = useRef<{ step: Step | null; destination: string | null }>({\n step: null,\n destination: null,\n })\n\n const start = useCallback(\n async (tourId: string, options?: { from?: number; resume?: boolean }) => {\n // Reentrancy: a second call while this same tour is running would re-read persistence and\n // could move the progress backwards. Switching to another tour stays allowed.\n if (state.tourId === tourId && state.status === 'running') return\n\n const target = toursById.get(tourId)\n if (!target) throw new Error(`[guide] unknown tour: ${tourId}`)\n if (target.steps.length === 0) {\n throw new Error(`[guide] tour has no steps: ${tourId}`)\n }\n\n if (typeof document !== 'undefined') {\n focusOriginRef.current = document.activeElement as HTMLElement | null\n }\n\n let stepIndex = options?.from ?? 0\n if (options?.from === undefined && options?.resume !== false && storage) {\n // Storage that fails must not block the tour: we start from the beginning.\n let progress: TourProgress | null = null\n try {\n const stored = await storage.read<unknown>(`tour:${tourId}`)\n progress = isTourProgress(stored) ? stored : null\n } catch (error) {\n warnStorageFailure(error)\n }\n if (progress?.status === 'in-progress') stepIndex = progress.stepIndex\n }\n\n if (process.env.NODE_ENV !== 'production') {\n const missing = findMissingTargets(target, location)\n if (missing.length > 0) {\n console.warn(\n `[guide] tour \"${tourId}\" declares targets that are not present on this page: ${missing.join(', ')}`,\n )\n }\n }\n\n // Restarting the same tour on the same step must navigate again: without this reset, the\n // destination already requested would stay remembered and the effect would skip navigate.\n navigationRef.current = { step: null, destination: null }\n\n dispatch({ type: 'START', tourId, stepIndex })\n emit({ type: 'tour:start', tourId, stepIndex })\n },\n [toursById, storage, location, emit, state.tourId, state.status, warnStorageFailure],\n )\n\n useEffect(() => {\n if (!isActive || !step || routeMatches) return\n\n if (navigationRef.current.step !== step) {\n navigationRef.current = { step, destination: null }\n }\n\n const destination =\n step.navigateTo ?? (step.route && isLiteralRoute(step.route) ? step.route : null)\n\n if (!destination) return\n if (navigationRef.current.destination === destination) return\n if (!navigate) {\n console.warn('[guide] a step declares a route but no navigate function was provided')\n return\n }\n navigationRef.current.destination = destination\n navigate(destination)\n }, [isActive, step, routeMatches, navigate])\n\n // Target not found: apply the policy.\n useEffect(() => {\n if (!timedOut || !tour || !step) return\n\n emit({\n type: 'target:missing',\n tourId: tour.id,\n stepIndex: state.stepIndex,\n target: step.target,\n })\n\n const policy = step.onMissingTarget ?? onMissingTarget\n if (policy === 'skip') dispatch({ type: 'NEXT', stepCount: tour.steps.length })\n else if (policy === 'error') dispatch({ type: 'STOP' })\n else dispatch({ type: 'PAUSE' })\n }, [timedOut, tour, step, state.stepIndex, onMissingTarget, emit])\n\n // Automatic resume when the target reappears after a pause.\n useEffect(() => {\n if (state.status === 'paused' && element) dispatch({ type: 'RESUME' })\n }, [state.status, element])\n\n // Step actually on screen.\n useEffect(() => {\n if (state.status !== 'running' || !tour || !step || !element) return\n emit({\n type: 'step:show',\n tourId: tour.id,\n stepIndex: state.stepIndex,\n target: step.target,\n })\n announce(`${state.stepIndex + 1} / ${tour.steps.length}`)\n }, [state.status, state.stepIndex, tour, step, element, emit, announce])\n\n // Progress persistence. A write that fails breaks nothing: the progress is simply not kept.\n useEffect(() => {\n if (!storage || !state.tourId) return\n const status =\n state.status === 'running'\n ? 'in-progress'\n : state.status === 'completed'\n ? 'completed'\n : null\n if (!status) return\n try {\n void Promise.resolve(\n storage.write(`tour:${state.tourId}`, { status, stepIndex: state.stepIndex }),\n ).catch(warnStorageFailure)\n } catch (error) {\n warnStorageFailure(error)\n }\n }, [storage, state.tourId, state.status, state.stepIndex, warnStorageFailure])\n\n useEffect(() => {\n if (element) lastElementRef.current = element\n }, [element])\n\n // Focus returns to its origin once the tour is stopped or completed.\n useEffect(() => {\n if (state.status !== 'idle' && state.status !== 'completed') return\n const origin = focusOriginRef.current\n const fallback = lastElementRef.current\n focusOriginRef.current = null\n lastElementRef.current = null\n if (typeof document === 'undefined') return\n\n if (origin && document.contains(origin)) {\n origin.focus()\n return\n }\n\n // The origin is gone. A tour launched from a hotspot bubble is how that happens: the\n // control captured here and the marker captured by the bubble's own focus trap both\n // unmount the instant the tour starts, so `origin.focus()` is a no-op on a detached node\n // and the user is dropped on document.body, with no focus ring, nothing announced, and the\n // next Tab restarting at the top of the page. Keeping the marker alive is not an option:\n // it is hidden for the duration of the tour on purpose, and the hotspot is seen by then\n // anyway, so it has no marker to come back to. The element the tour last pointed at is\n // present, is what the user was just being shown, and is where reading should resume, so\n // that is where focus lands.\n //\n // Only when focus would otherwise have nowhere to go. A user who has already clicked or\n // tabbed somewhere keeps what they chose: not stranding them and not stealing from them\n // are both requirements, the same rule ChecklistLauncher and the hotspot bubble's\n // outside-click recovery already follow.\n if (document.activeElement !== document.body) return\n if (!fallback || !document.contains(fallback)) return\n focusFallback(fallback)\n }, [state.status])\n\n const activeStep = useMemo<ActiveStep | null>(() => {\n if (!tour || !step || !isActive) return null\n return {\n tourId: tour.id,\n step,\n stepIndex: state.stepIndex,\n stepCount: tour.steps.length,\n element,\n rect,\n interactive: step.interactive === true || step.advanceOn !== undefined,\n awaitsAction: step.advanceOn !== undefined,\n title: resolveText(step.title, step.titleKey, translate),\n body: resolveText(step.body, step.bodyKey, translate),\n isFirst: state.stepIndex === 0,\n isLast: state.stepIndex === tour.steps.length - 1,\n next,\n previous,\n stop,\n }\n }, [tour, step, isActive, state.stepIndex, element, rect, translate, next, previous, stop])\n\n const value = useMemo<GuideContextValue>(\n () => ({ state, activeStep, start, next, previous, stop }),\n [state, activeStep, start, next, previous, stop],\n )\n\n return <GuideContext.Provider value={value}>{children}</GuideContext.Provider>\n}\n","import type { Tour } from './types'\nimport { matchRoute } from './matchRoute'\nimport { targetSelector } from './selector'\n\nexport function findMissingTargets(\n tour: Tour,\n location: string | undefined,\n attribute = 'data-guide',\n): string[] {\n if (typeof document === 'undefined') return []\n\n return tour.steps\n .filter((step) => !step.route || location === undefined || matchRoute(step.route, location))\n .map((step) => step.target)\n .filter((target) => !document.querySelector(targetSelector(target, attribute)))\n}\n","import type { Translate } from './types'\n\nexport function resolveText(\n value: string | undefined,\n key: string | undefined,\n translate: Translate | undefined,\n): string {\n if (value !== undefined) return value\n if (key === undefined) return ''\n return translate ? translate(key) : key\n}\n","'use client'\n\nimport { useContext, useMemo } from 'react'\nimport { GuideContext } from './GuideProvider'\nimport type { TourStatus } from './types'\n\nexport interface UseTourResult {\n start: (options?: { from?: number; resume?: boolean }) => Promise<void>\n next: () => void\n previous: () => void\n stop: () => void\n status: TourStatus\n stepIndex: number\n}\n\nexport function useTour(tourId: string): UseTourResult {\n const context = useContext(GuideContext)\n if (!context) throw new Error('[guide] useTour must be used inside a GuideProvider')\n\n const { state, start, next, previous, stop } = context\n const isCurrent = state.tourId === tourId\n\n return useMemo(\n () => ({\n start: (options) => start(tourId, options),\n next,\n previous,\n stop,\n status: isCurrent ? state.status : 'idle',\n stepIndex: isCurrent ? state.stepIndex : 0,\n }),\n [tourId, start, next, previous, stop, isCurrent, state.status, state.stepIndex],\n )\n}\n","'use client'\n\nimport { useContext } from 'react'\nimport { GuideContext, type ActiveStep } from './GuideProvider'\n\nexport function useGuideStep(): ActiveStep | null {\n const context = useContext(GuideContext)\n if (!context) throw new Error('[guide] useGuideStep must be used inside a GuideProvider')\n return context.activeStep\n}\n","'use client'\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react'\nimport type {\n Checklist,\n ChecklistProgress,\n GuideEvent,\n GuideStorage,\n Translate,\n} from './types'\nimport { GuideContext } from './GuideProvider'\nimport { isChecklistProgress } from './storage'\n\nexport interface ChecklistContextValue {\n checklists: Checklist[]\n progress: Record<string, ChecklistProgress>\n translate?: Translate\n /**\n * Whether each checklist's initial restore from storage has settled, keyed by checklist id:\n * true immediately for a checklist when no `storage` prop was given (there is nothing to\n * wait for), and true once that checklist's own read has resolved or rejected. Settled\n * independently per checklist, so a slow or hung read for one checklist never holds another\n * checklist's rendering hostage, and a renderer can wait for its own entry without a broken\n * backend hiding it forever.\n */\n restored: Record<string, boolean>\n activate: (checklistId: string, itemId: string) => void\n toggle: (checklistId: string, itemId: string) => void\n complete: (checklistId: string, itemId: string) => void\n dismiss: (checklistId: string) => void\n reset: (checklistId: string) => void\n}\n\nexport const ChecklistContext = createContext<ChecklistContextValue | null>(null)\n\nexport interface ChecklistProviderProps {\n checklists: Checklist[]\n children: ReactNode\n storage?: GuideStorage\n translate?: Translate\n navigate?: (path: string) => void\n onEvent?: (event: GuideEvent) => void\n}\n\nconst emptyProgress: ChecklistProgress = { completed: [], dismissed: false }\n\nexport function ChecklistProvider({\n checklists,\n children,\n storage,\n translate,\n navigate,\n onEvent,\n}: ChecklistProviderProps) {\n const checklistsById = useMemo(() => {\n const map = new Map<string, Checklist>()\n for (const candidate of checklists) map.set(candidate.id, candidate)\n return map\n }, [checklists])\n\n const [progress, setProgress] = useState<Record<string, ChecklistProgress>>(() => {\n const initial: Record<string, ChecklistProgress> = {}\n for (const candidate of checklists) initial[candidate.id] = emptyProgress\n return initial\n })\n\n // Synchronous mirror of `progress`, used by complete/toggle/dismiss/reset to read the current\n // state instead of the render-time `progress` closure. Two of those calls can happen back to\n // back in the same tick with no render in between (completeItemsForTour ticking two items that\n // share a tourId is the case that surfaced this): reading the stale closure would make the\n // second call compute its next value from a snapshot that does not include the first call's\n // write, silently dropping it. progressRef is updated synchronously by applyProgress below, so\n // consecutive calls compose correctly.\n const progressRef = useRef(progress)\n\n // No storage means nothing to wait for a checklist. With storage, each checklist's own entry\n // flips once its own read has settled, one way or the other: see the restore effect below.\n const [restoredById, setRestoredById] = useState<Record<string, boolean>>(() => {\n const initial: Record<string, boolean> = {}\n for (const candidate of checklists) initial[candidate.id] = !storage\n return initial\n })\n\n const guide = useContext(GuideContext)\n\n const storageWarnedRef = useRef(false)\n const warnStorageFailure = useCallback((error: unknown) => {\n if (storageWarnedRef.current) return\n storageWarnedRef.current = true\n console.warn('[guide] storage failed; checklist progress will not be persisted', error)\n }, [])\n\n const noGuideWarnedRef = useRef(false)\n const warnNoGuide = useCallback(() => {\n if (noGuideWarnedRef.current) return\n noGuideWarnedRef.current = true\n console.warn('[guide] a checklist item needs a GuideProvider to launch a tour')\n }, [])\n\n // guide.start rejects for a tour id the GuideProvider does not hold (a typo in an item's\n // tourId being the obvious way to hit this). Without a catch here that rejection is\n // unhandled: nothing warns and the failure is invisible. Same once-only shape as\n // warnNoGuide and warnStorageFailure above.\n const tourStartFailedWarnedRef = useRef(false)\n const warnTourStartFailure = useCallback((error: unknown) => {\n if (tourStartFailedWarnedRef.current) return\n tourStartFailedWarnedRef.current = true\n console.warn('[guide] starting a tour for a checklist item failed', error)\n }, [])\n\n // Same once-only shape as warnNoGuide and warnTourStartFailure: a missing navigate function\n // is a host wiring mistake, so repeating the warning on every activation only buries it.\n const noNavigateWarnedRef = useRef(false)\n const warnNoNavigate = useCallback(() => {\n if (noNavigateWarnedRef.current) return\n noNavigateWarnedRef.current = true\n console.warn('[guide] a checklist item declares an href but no navigate function was provided')\n }, [])\n\n const onEventRef = useRef(onEvent)\n onEventRef.current = onEvent\n const emit = useCallback((event: GuideEvent) => onEventRef.current?.(event), [])\n\n // Restore persisted progress once on mount: later checklist prop changes are not re-read.\n // Every checklist's read runs concurrently, not in sequence, and each one applies its own\n // progress and settles its own `restoredById` entry as soon as it lands: a hung read for one\n // checklist must never hold up another checklist's read, its progress, or its `restored` flag.\n // A single shared flag that only flipped once every read had settled tried this once and got\n // it backwards, trading a wrong-but-visible render for one that hides a working checklist\n // behind a different checklist's stuck I/O forever, exactly the outcome the \"degrade to\n // showing, never hide forever\" rule below exists to rule out.\n useEffect(() => {\n if (!storage) return\n let cancelled = false\n for (const candidate of checklists) {\n void (async () => {\n try {\n const stored = await storage.read<unknown>(`checklist:${candidate.id}`)\n if (!cancelled && isChecklistProgress(stored)) {\n // A read that was already in flight must never undo something the user did while it\n // was running. With a server-backed storage the read can take hundreds of\n // milliseconds, and the list is on screen and interactive for all of it.\n //\n // Merging into the live entry rather than replacing it is what makes that true. Every\n // checklist starts empty at mount, so the moves a user makes before the read lands are\n // one-way in practice: ticking an item and dismissing the list. The union of the stored\n // completions with the live ones, dismissed if either side says so, keeps both.\n // Replacing the live entry instead erased a tick the user could see on screen, and the\n // next toggle persisted the erased state.\n //\n // The union cannot subtract, so the two moves that do subtract lose against a read\n // still in flight: unticking an item the stored value has ticked, and reset. Both come\n // back when the read lands. That window is bounded by one read at mount and it is\n // strictly better than the replace it replaced, which lost these too and lost ticks\n // besides. If a deliberate clearing ever has to survive the window, it needs to be\n // sequenced against the read rather than merged with it.\n const live = progressRef.current[candidate.id] ?? emptyProgress\n const merged = {\n ...progressRef.current,\n [candidate.id]: {\n completed: live.completed.concat(\n stored.completed.filter((id) => !live.completed.includes(id)),\n ),\n dismissed: live.dismissed || stored.dismissed,\n },\n }\n progressRef.current = merged\n setProgress(merged)\n }\n } catch (error) {\n warnStorageFailure(error)\n } finally {\n if (!cancelled) {\n setRestoredById((current) => ({ ...current, [candidate.id]: true }))\n }\n }\n })()\n }\n return () => {\n cancelled = true\n }\n // Reads only on mount and when storage itself changes: the checklists prop is not\n // watched, so a later change to it does not trigger a re-read of persisted progress.\n }, [storage])\n\n // Applies one checklist's next progress. Reads and writes go through progressRef, not a\n // setProgress updater function, so this composes correctly across synchronous calls (see\n // progressRef above) without ever putting decision logic or emit calls inside a React updater:\n // Strict Mode invokes updater functions passed to setState twice to catch impurities, and an\n // emit inside one would double-fire.\n const applyProgress = useCallback(\n (checklistId: string, next: ChecklistProgress) => {\n const merged = { ...progressRef.current, [checklistId]: next }\n progressRef.current = merged\n setProgress(merged)\n if (!storage) return\n try {\n void Promise.resolve(storage.write(`checklist:${checklistId}`, next)).catch(\n warnStorageFailure,\n )\n } catch (error) {\n warnStorageFailure(error)\n }\n },\n [storage, warnStorageFailure],\n )\n\n // Shared lookup for every action below: one place to warn on an unknown checklist or item id,\n // so toggle, complete, activate, dismiss and reset all reject bad ids the same way.\n const resolveChecklist = useCallback(\n (checklistId: string): Checklist | null => {\n const checklist = checklistsById.get(checklistId)\n if (!checklist) {\n console.warn(`[guide] unknown checklist \"${checklistId}\"`)\n return null\n }\n return checklist\n },\n [checklistsById],\n )\n\n const resolveItem = useCallback(\n (checklistId: string, itemId: string) => {\n const checklist = resolveChecklist(checklistId)\n if (!checklist) return null\n const item = checklist.items.find((candidate) => candidate.id === itemId)\n if (!item) {\n console.warn(`[guide] unknown checklist item \"${itemId}\"`)\n return null\n }\n return { checklist, item }\n },\n [resolveChecklist],\n )\n\n // Idempotent: ticking an item that is already ticked does nothing and emits nothing. This is\n // the one place item-complete and checklist-complete are emitted, so toggle's ticking half and\n // activate's plain-item branch both delegate here rather than duplicating that logic.\n const complete = useCallback(\n (checklistId: string, itemId: string) => {\n const resolved = resolveItem(checklistId, itemId)\n if (!resolved) return\n const { checklist } = resolved\n\n const current = progressRef.current[checklistId] ?? emptyProgress\n if (current.completed.includes(itemId)) return\n\n const wasComplete =\n checklist.items.length > 0 &&\n checklist.items.every((candidate) => current.completed.includes(candidate.id))\n\n const nextCompleted = [...current.completed, itemId]\n applyProgress(checklistId, { ...current, completed: nextCompleted })\n emit({ type: 'checklist:item-complete', checklistId, itemId })\n\n const isNowComplete =\n checklist.items.length > 0 &&\n checklist.items.every((candidate) => nextCompleted.includes(candidate.id))\n if (isNowComplete && !wasComplete) emit({ type: 'checklist:complete', checklistId })\n },\n [resolveItem, applyProgress, emit],\n )\n\n // Marks every item across every checklist whose tourId matches the finished tour. Delegates to\n // complete, which is idempotent, so an item already ticked produces no event.\n const completeItemsForTour = useCallback(\n (tourId: string) => {\n for (const candidate of checklists) {\n for (const item of candidate.items) {\n if (item.tourId === tourId) complete(candidate.id, item.id)\n }\n }\n },\n [checklists, complete],\n )\n\n // Watches the tour state for a finished run and ticks the matching item. The guard is keyed by\n // tourId rather than a plain boolean: leaving the completed state (STOP, or starting another\n // tour) clears it, so running the same tour again after a reset can tick it again.\n //\n // Correctness does not depend on this guard. `complete` is idempotent, so deleting the ref\n // entirely leaves every test green; it only saves a repeated walk of the checklists while a\n // completed tour stays on screen. Do not read it as the thing that prevents a double tick.\n const handledCompletionRef = useRef<string | null>(null)\n useEffect(() => {\n const state = guide?.state\n if (!state || state.status !== 'completed' || !state.tourId) {\n handledCompletionRef.current = null\n return\n }\n if (handledCompletionRef.current === state.tourId) return\n handledCompletionRef.current = state.tourId\n completeItemsForTour(state.tourId)\n }, [guide?.state, completeItemsForTour])\n\n const toggle = useCallback(\n (checklistId: string, itemId: string) => {\n const resolved = resolveItem(checklistId, itemId)\n if (!resolved) return\n\n const current = progressRef.current[checklistId] ?? emptyProgress\n if (current.completed.includes(itemId)) {\n const nextCompleted = current.completed.filter((id) => id !== itemId)\n applyProgress(checklistId, { ...current, completed: nextCompleted })\n return\n }\n\n complete(checklistId, itemId)\n },\n [resolveItem, applyProgress, complete],\n )\n\n const dismiss = useCallback(\n (checklistId: string) => {\n if (!resolveChecklist(checklistId)) return\n const current = progressRef.current[checklistId] ?? emptyProgress\n applyProgress(checklistId, { ...current, dismissed: true })\n emit({ type: 'checklist:dismiss', checklistId })\n },\n [resolveChecklist, applyProgress, emit],\n )\n\n const reset = useCallback(\n (checklistId: string) => {\n if (!resolveChecklist(checklistId)) return\n applyProgress(checklistId, { completed: [], dismissed: false })\n },\n [resolveChecklist, applyProgress],\n )\n\n const activate = useCallback(\n (checklistId: string, itemId: string) => {\n const resolved = resolveItem(checklistId, itemId)\n if (!resolved) return\n const { item } = resolved\n\n if (item.tourId) {\n if (!guide) {\n warnNoGuide()\n return\n }\n void guide.start(item.tourId).catch(warnTourStartFailure)\n return\n }\n\n if (item.href) {\n if (!navigate) {\n warnNoNavigate()\n return\n }\n navigate(item.href)\n return\n }\n\n toggle(checklistId, itemId)\n },\n [resolveItem, guide, navigate, toggle, warnNoGuide, warnNoNavigate, warnTourStartFailure],\n )\n\n const value = useMemo<ChecklistContextValue>(\n () => ({\n checklists,\n progress,\n translate,\n restored: restoredById,\n activate,\n toggle,\n complete,\n dismiss,\n reset,\n }),\n [checklists, progress, translate, restoredById, activate, toggle, complete, dismiss, reset],\n )\n\n return <ChecklistContext.Provider value={value}>{children}</ChecklistContext.Provider>\n}\n","'use client'\n\nimport { useContext, useMemo } from 'react'\nimport { ChecklistContext } from './ChecklistProvider'\nimport { resolveText } from './resolveText'\nimport type { ResolvedChecklistItem } from './types'\n\nexport interface UseChecklistResult {\n items: ResolvedChecklistItem[]\n completedCount: number\n total: number\n isComplete: boolean\n dismissed: boolean\n /**\n * Whether this checklist's own initial restore from storage has settled. A renderer should\n * wait for this before drawing anything, or a checklist already dismissed or partly\n * completed in storage can flash its empty initial state on screen once before the restore\n * lands. Settled per checklist: a slow or hung read for a different checklist on the same\n * provider never holds this one false.\n */\n restored: boolean\n activate: (itemId: string) => void\n toggle: (itemId: string) => void\n complete: (itemId: string) => void\n dismiss: () => void\n reset: () => void\n}\n\nexport function useChecklist(checklistId: string): UseChecklistResult {\n const context = useContext(ChecklistContext)\n if (!context)\n throw new Error('[guide] useChecklist must be used inside a ChecklistProvider')\n\n const checklist = context.checklists.find((entry) => entry.id === checklistId)\n if (!checklist) throw new Error(`[guide] unknown checklist \"${checklistId}\"`)\n\n const progress = context.progress[checklistId]\n const completed = progress?.completed ?? []\n const dismissed = progress?.dismissed ?? false\n const translate = context.translate\n // Falls back to true for a checklist this provider has no entry for yet, the same way a\n // checklist added to `checklists` after mount (which the restore effect does not watch) is\n // never tracked: nothing here should block on a read that was never started.\n const restored = context.restored[checklistId] ?? true\n\n const items = useMemo<ResolvedChecklistItem[]>(\n () =>\n checklist.items.map((item) => ({\n id: item.id,\n title: resolveText(item.title, item.titleKey, translate),\n body: resolveText(item.body, item.bodyKey, translate),\n completed: completed.includes(item.id),\n tourId: item.tourId,\n href: item.href,\n })),\n [checklist, completed, translate],\n )\n\n const total = checklist.items.length\n const completedCount = items.filter((item) => item.completed).length\n const isComplete = total > 0 && completedCount === total\n\n const { activate, toggle, complete, dismiss, reset } = context\n\n return useMemo(\n () => ({\n items,\n completedCount,\n total,\n isComplete,\n dismissed,\n restored,\n activate: (itemId: string) => activate(checklistId, itemId),\n toggle: (itemId: string) => toggle(checklistId, itemId),\n complete: (itemId: string) => complete(checklistId, itemId),\n dismiss: () => dismiss(checklistId),\n reset: () => reset(checklistId),\n }),\n [\n items,\n completedCount,\n total,\n isComplete,\n dismissed,\n restored,\n activate,\n toggle,\n complete,\n dismiss,\n reset,\n checklistId,\n ],\n )\n}\n","'use client'\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react'\nimport type { GuideEvent, GuideStorage, Hotspot, Translate } from './types'\nimport { GuideContext } from './GuideProvider'\nimport { isHotspotsProgress } from './storage'\n\nconst STORAGE_KEY = 'hotspots:seen'\n\nexport interface HotspotContextValue {\n hotspots: Hotspot[]\n seen: string[]\n translate?: Translate\n /**\n * Whether the initial restore from storage has settled: true immediately when no `storage`\n * prop was given (there is nothing to wait for), and true once the read resolves or\n * rejects, so a renderer can wait for it without a broken backend hiding hotspots forever.\n */\n restored: boolean\n open: (hotspotId: string) => void\n startTour: (hotspotId: string) => void\n reset: () => void\n notifyShown: (hotspotId: string) => void\n}\n\nexport const HotspotContext = createContext<HotspotContextValue | null>(null)\n\nexport interface HotspotProviderProps {\n hotspots: Hotspot[]\n children: ReactNode\n storage?: GuideStorage\n translate?: Translate\n onEvent?: (event: GuideEvent) => void\n}\n\nexport function HotspotProvider({\n hotspots,\n children,\n storage,\n translate,\n onEvent,\n}: HotspotProviderProps) {\n const hotspotsById = useMemo(() => {\n const map = new Map<string, Hotspot>()\n for (const candidate of hotspots) {\n // Silence here would hide a wiring mistake until production, the same way a duplicate\n // tour id would.\n if (map.has(candidate.id)) {\n throw new Error(`[guide] duplicate hotspot id: ${candidate.id}`)\n }\n map.set(candidate.id, candidate)\n }\n return map\n }, [hotspots])\n\n const [seen, setSeen] = useState<string[]>([])\n\n // No storage means nothing to wait for. With storage, this flips once the read settles,\n // one way or the other: see the restore effect below.\n const [restored, setRestored] = useState(() => !storage)\n\n // Synchronous mirror of `seen`, for the same reason ChecklistProvider keeps one (see\n // progressRef there): two calls in a single tick would otherwise both compute their next\n // value from the same stale render-time closure, and the first write would be dropped.\n const seenRef = useRef(seen)\n\n const guide = useContext(GuideContext)\n\n const storageWarnedRef = useRef(false)\n const warnStorageFailure = useCallback((error: unknown) => {\n if (storageWarnedRef.current) return\n storageWarnedRef.current = true\n console.warn('[guide] storage failed; hotspot state will not be persisted', error)\n }, [])\n\n const noGuideWarnedRef = useRef(false)\n const warnNoGuide = useCallback(() => {\n if (noGuideWarnedRef.current) return\n noGuideWarnedRef.current = true\n console.warn('[guide] a hotspot needs a GuideProvider to launch a tour')\n }, [])\n\n const tourStartFailedWarnedRef = useRef(false)\n const warnTourStartFailure = useCallback((error: unknown) => {\n if (tourStartFailedWarnedRef.current) return\n tourStartFailedWarnedRef.current = true\n console.warn('[guide] starting a tour for a hotspot failed', error)\n }, [])\n\n const onEventRef = useRef(onEvent)\n onEventRef.current = onEvent\n const emit = useCallback((event: GuideEvent) => onEventRef.current?.(event), [])\n\n // Restore once on mount. `seen` only ever grows, so merging by union is correct even when a\n // slow read lands after the user has already opened a hotspot: nothing the union does can\n // un-see one. `reset` is the single move that subtracts and therefore loses that race, the\n // same bounded window ChecklistProvider documents at the same place.\n useEffect(() => {\n if (!storage) return\n let cancelled = false\n void (async () => {\n let stored: unknown = null\n try {\n stored = await storage.read<unknown>(STORAGE_KEY)\n } catch (error) {\n warnStorageFailure(error)\n // A broken read must degrade to showing hotspots, not hiding them forever.\n if (!cancelled) setRestored(true)\n return\n }\n if (cancelled) return\n if (isHotspotsProgress(stored)) {\n const merged = seenRef.current.concat(\n stored.seen.filter((id) => !seenRef.current.includes(id)),\n )\n seenRef.current = merged\n setSeen(merged)\n }\n setRestored(true)\n })()\n return () => {\n cancelled = true\n }\n }, [storage, warnStorageFailure])\n\n const applySeen = useCallback(\n (next: string[]) => {\n seenRef.current = next\n setSeen(next)\n if (!storage) return\n try {\n void Promise.resolve(storage.write(STORAGE_KEY, { seen: next })).catch(\n warnStorageFailure,\n )\n } catch (error) {\n warnStorageFailure(error)\n }\n },\n [storage, warnStorageFailure],\n )\n\n const resolve = useCallback(\n (hotspotId: string): Hotspot | null => {\n const hotspot = hotspotsById.get(hotspotId)\n if (!hotspot) {\n console.warn(`[guide] unknown hotspot \"${hotspotId}\"`)\n return null\n }\n return hotspot\n },\n [hotspotsById],\n )\n\n const open = useCallback(\n (hotspotId: string) => {\n if (!resolve(hotspotId)) return\n emit({ type: 'hotspot:open', hotspotId })\n if (seenRef.current.includes(hotspotId)) return\n applySeen([...seenRef.current, hotspotId])\n },\n [resolve, applySeen, emit],\n )\n\n const startTour = useCallback(\n (hotspotId: string) => {\n const hotspot = resolve(hotspotId)\n if (!hotspot?.tourId) return\n if (!guide) {\n warnNoGuide()\n return\n }\n void guide.start(hotspot.tourId).catch(warnTourStartFailure)\n },\n [resolve, guide, warnNoGuide, warnTourStartFailure],\n )\n\n const reset = useCallback(() => applySeen([]), [applySeen])\n\n // Emitted by the renderer, which is the only layer that knows whether a marker is actually\n // on screen. Once per hotspot per mount: a scroll that re-measures must not re-announce.\n const shownRef = useRef<Set<string>>(new Set())\n const notifyShown = useCallback(\n (hotspotId: string) => {\n if (shownRef.current.has(hotspotId)) return\n shownRef.current.add(hotspotId)\n emit({ type: 'hotspot:show', hotspotId })\n },\n [emit],\n )\n\n const value = useMemo<HotspotContextValue>(\n () => ({ hotspots, seen, translate, restored, open, startTour, reset, notifyShown }),\n [hotspots, seen, translate, restored, open, startTour, reset, notifyShown],\n )\n\n return <HotspotContext.Provider value={value}>{children}</HotspotContext.Provider>\n}\n","'use client'\n\nimport { useContext, useMemo } from 'react'\nimport { HotspotContext } from './HotspotProvider'\nimport { resolveText } from './resolveText'\nimport type { ResolvedHotspot } from './types'\n\nexport interface UseHotspotsResult {\n /**\n * Every hotspot, each carrying its own `seen`, rather than the unseen ones alone. A\n * renderer has to keep a marker mounted while its own bubble closes, so it needs the seen\n * one too; filtering is one line at the call site.\n */\n hotspots: ResolvedHotspot[]\n /**\n * Whether the initial restore from storage has settled. A renderer should wait for this\n * before drawing any marker, or a hotspot already seen in storage can flash on screen once\n * before the restore lands.\n */\n restored: boolean\n open: (hotspotId: string) => void\n startTour: (hotspotId: string) => void\n reset: () => void\n notifyShown: (hotspotId: string) => void\n}\n\nexport function useHotspots(): UseHotspotsResult {\n const context = useContext(HotspotContext)\n if (!context)\n throw new Error('[guide] useHotspots must be used inside a HotspotProvider')\n\n const { seen, translate, restored, open, startTour, reset, notifyShown } = context\n\n const hotspots = useMemo<ResolvedHotspot[]>(\n () =>\n context.hotspots.map((hotspot) => ({\n id: hotspot.id,\n target: hotspot.target,\n title: resolveText(hotspot.title, hotspot.titleKey, translate),\n body: resolveText(hotspot.body, hotspot.bodyKey, translate),\n seen: seen.includes(hotspot.id),\n tourId: hotspot.tourId,\n placement: hotspot.placement,\n })),\n [context.hotspots, seen, translate],\n )\n\n return useMemo(\n () => ({ hotspots, restored, open, startTour, reset, notifyShown }),\n [hotspots, restored, open, startTour, reset, notifyShown],\n )\n}\n"],"mappings":"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","names":["useEffect","useState","useEffect","useState","useCallback","useEffect","useState","useCallback","useState","useEffect","useMemo","useMemo","useContext","useContext","createContext","useCallback","useContext","useEffect","useMemo","useRef","useState","jsx","createContext","useMemo","useState","useRef","useContext","useCallback","useEffect","useContext","useMemo","useContext","useMemo","createContext","useCallback","useContext","useEffect","useMemo","useRef","useState","jsx","createContext","useMemo","useState","useRef","useContext","useCallback","useEffect","useContext","useMemo","useContext","useMemo"]}
|