@flowsterix/react 0.4.2 → 0.6.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/dist/index.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ import "./chunk-WCLT3A6G.mjs";
2
+ import "./chunk-2ZX2Y3JL.mjs";
1
3
  import {
2
4
  createPathString,
3
5
  expandRect,
@@ -44,7 +46,17 @@ var defaultLabels = {
44
46
  ariaStepProgress: ({ current, total }) => `Step ${current} of ${total}`,
45
47
  ariaTimeRemaining: ({ ms }) => `${Math.ceil(ms / 1e3)} seconds remaining`,
46
48
  ariaDelayProgress: "Auto-advance progress",
47
- formatTimeRemaining: ({ ms }) => `${Math.ceil(ms / 1e3)}s remaining`
49
+ formatTimeRemaining: ({ ms }) => `${Math.ceil(ms / 1e3)}s remaining`,
50
+ targetIssue: {
51
+ missingTitle: "Target not visible",
52
+ missingBody: "The target element is not currently visible. Make sure the UI piece is mounted and displayed.",
53
+ missingHint: "Showing the last known position until the element returns.",
54
+ hiddenTitle: "Target not visible",
55
+ hiddenBody: "The target element is not currently visible. Make sure the UI piece is mounted and displayed.",
56
+ hiddenHint: "Showing the last known position until the element returns.",
57
+ detachedTitle: "Target left the page",
58
+ detachedBody: "Navigate back to the screen that contains this element or reopen it before continuing the tour."
59
+ }
48
60
  };
49
61
  var LabelsContext = createContext(defaultLabels);
50
62
  var LabelsProvider = LabelsContext.Provider;
@@ -1404,7 +1416,7 @@ var useTourTarget = () => {
1404
1416
  };
1405
1417
 
1406
1418
  // src/hooks/useHudState.ts
1407
- import { useCallback as useCallback2, useEffect as useEffect7, useMemo as useMemo4, useState as useState6 } from "react";
1419
+ import { useCallback as useCallback2, useEffect as useEffect8, useMemo as useMemo4, useRef as useRef5, useState as useState7 } from "react";
1408
1420
 
1409
1421
  // src/hooks/useAdvanceRules.ts
1410
1422
  import { useEffect as useEffect4 } from "react";
@@ -1619,6 +1631,7 @@ var useAdvanceRules = (target) => {
1619
1631
  // src/hooks/useHiddenTargetFallback.ts
1620
1632
  import { useEffect as useEffect5, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
1621
1633
  var DEFAULT_DELAY_MS = 900;
1634
+ var DEFAULT_GRACE_PERIOD_MS = 400;
1622
1635
  var useHiddenTargetFallback = ({
1623
1636
  step,
1624
1637
  target,
@@ -1626,7 +1639,9 @@ var useHiddenTargetFallback = ({
1626
1639
  onSkip
1627
1640
  }) => {
1628
1641
  const [usingScreenFallback, setUsingScreenFallback] = useState4(false);
1642
+ const [isInGracePeriod, setIsInGracePeriod] = useState4(false);
1629
1643
  const timeoutRef = useRef3(null);
1644
+ const graceTimeoutRef = useRef3(null);
1630
1645
  const skipTriggeredRef = useRef3(false);
1631
1646
  const hiddenMode = step?.targetBehavior?.hidden ?? "screen";
1632
1647
  const hiddenDelayMs = Math.max(
@@ -1639,26 +1654,44 @@ var useHiddenTargetFallback = ({
1639
1654
  timeoutRef.current = null;
1640
1655
  }
1641
1656
  };
1657
+ const clearGraceTimeout = () => {
1658
+ if (graceTimeoutRef.current !== null) {
1659
+ globalThis.clearTimeout(graceTimeoutRef.current);
1660
+ graceTimeoutRef.current = null;
1661
+ }
1662
+ };
1642
1663
  useEffect5(() => {
1643
1664
  skipTriggeredRef.current = false;
1644
1665
  setUsingScreenFallback(false);
1666
+ setIsInGracePeriod(false);
1645
1667
  clearPendingTimeout();
1646
- return clearPendingTimeout;
1668
+ clearGraceTimeout();
1669
+ return () => {
1670
+ clearPendingTimeout();
1671
+ clearGraceTimeout();
1672
+ };
1647
1673
  }, [step?.id]);
1648
1674
  useEffect5(() => {
1649
1675
  if (!isBrowser) return void 0;
1650
1676
  if (!step) return void 0;
1651
1677
  clearPendingTimeout();
1678
+ clearGraceTimeout();
1652
1679
  const isHiddenOrDetached = (target.visibility === "hidden" || target.visibility === "detached") && target.status === "ready";
1653
1680
  const isMissingWithNoRect = target.visibility === "missing" && target.status === "resolving" && target.rect === null && target.lastResolvedRect === null;
1654
- const shouldHandleHiddenTarget = !target.isScreen && (isHiddenOrDetached || isMissingWithNoRect);
1681
+ const isMissingAfterNavigation = target.visibility === "missing" && target.status === "resolving" && target.rect === null;
1682
+ const shouldHandleHiddenTarget = !target.isScreen && (isHiddenOrDetached || isMissingWithNoRect || isMissingAfterNavigation);
1655
1683
  if (!shouldHandleHiddenTarget) {
1656
1684
  setUsingScreenFallback(false);
1685
+ setIsInGracePeriod(false);
1657
1686
  return void 0;
1658
1687
  }
1688
+ setIsInGracePeriod(true);
1659
1689
  if (hiddenMode !== "screen") {
1660
1690
  setUsingScreenFallback(false);
1661
1691
  }
1692
+ graceTimeoutRef.current = globalThis.setTimeout(() => {
1693
+ setIsInGracePeriod(false);
1694
+ }, DEFAULT_GRACE_PERIOD_MS);
1662
1695
  timeoutRef.current = globalThis.setTimeout(() => {
1663
1696
  if (hiddenMode === "screen") {
1664
1697
  setUsingScreenFallback(true);
@@ -1669,7 +1702,10 @@ var useHiddenTargetFallback = ({
1669
1702
  onSkip();
1670
1703
  }
1671
1704
  }, hiddenDelayMs);
1672
- return clearPendingTimeout;
1705
+ return () => {
1706
+ clearPendingTimeout();
1707
+ clearGraceTimeout();
1708
+ };
1673
1709
  }, [
1674
1710
  step,
1675
1711
  target.visibility,
@@ -1697,18 +1733,37 @@ var useHiddenTargetFallback = ({
1697
1733
  }, [target, usingScreenFallback, viewportRect]);
1698
1734
  return {
1699
1735
  target: resolvedTarget,
1700
- usingScreenFallback
1736
+ usingScreenFallback,
1737
+ isInGracePeriod
1738
+ };
1739
+ };
1740
+
1741
+ // src/hooks/useRouteMismatch.ts
1742
+ import { useEffect as useEffect6, useState as useState5 } from "react";
1743
+ var useRouteMismatch = (step) => {
1744
+ const [currentPath, setCurrentPath] = useState5(() => getCurrentRoutePath());
1745
+ useEffect6(() => {
1746
+ return subscribeToRouteChanges((path) => {
1747
+ setCurrentPath(path);
1748
+ });
1749
+ }, []);
1750
+ const expectedRoute = step?.route;
1751
+ const isRouteMismatch = step !== null && expectedRoute !== void 0 && !matchRoute({ pattern: expectedRoute, path: currentPath });
1752
+ return {
1753
+ isRouteMismatch,
1754
+ currentPath,
1755
+ expectedRoute
1701
1756
  };
1702
1757
  };
1703
1758
 
1704
1759
  // src/hooks/useViewportRect.ts
1705
- import { useEffect as useEffect6, useRef as useRef4, useState as useState5 } from "react";
1760
+ import { useEffect as useEffect7, useRef as useRef4, useState as useState6 } from "react";
1706
1761
  var useViewportRect = () => {
1707
- const [viewport, setViewport] = useState5(
1762
+ const [viewport, setViewport] = useState6(
1708
1763
  () => getViewportRect()
1709
1764
  );
1710
1765
  const rafRef = useRef4(null);
1711
- useEffect6(() => {
1766
+ useEffect7(() => {
1712
1767
  if (!isBrowser) return;
1713
1768
  const updateViewport = () => {
1714
1769
  rafRef.current = null;
@@ -1751,7 +1806,7 @@ var normalizeFlowFilter = (value) => {
1751
1806
  var useHudState = (options = {}) => {
1752
1807
  const { flowId } = options;
1753
1808
  const flowFilter = useMemo4(() => normalizeFlowFilter(flowId), [flowId]);
1754
- const { state, activeStep, activeFlowId, flows, next, complete } = useTour();
1809
+ const { state, activeStep, activeFlowId, flows, next, complete, pause, resume } = useTour();
1755
1810
  const target = useTourTarget();
1756
1811
  const viewportRect = useViewportRect();
1757
1812
  useAdvanceRules(target);
@@ -1763,15 +1818,15 @@ var useHudState = (options = {}) => {
1763
1818
  const isRunning = state?.status === "running";
1764
1819
  const runningState = isRunning && matchesFlowFilter ? state : null;
1765
1820
  const runningStep = runningState && activeStep ? activeStep : null;
1766
- const [shouldRender, setShouldRender] = useState6(
1821
+ const [shouldRender, setShouldRender] = useState7(
1767
1822
  Boolean(runningStep)
1768
1823
  );
1769
- useEffect7(() => {
1824
+ useEffect8(() => {
1770
1825
  if (runningStep) {
1771
1826
  setShouldRender(true);
1772
1827
  }
1773
1828
  }, [runningStep?.id]);
1774
- useEffect7(() => {
1829
+ useEffect8(() => {
1775
1830
  if (!shouldRender) return;
1776
1831
  if (runningStep) return;
1777
1832
  if (target.status !== "idle") return;
@@ -1782,6 +1837,19 @@ var useHudState = (options = {}) => {
1782
1837
  window.clearTimeout(timeoutId);
1783
1838
  };
1784
1839
  }, [runningStep, shouldRender, target.status]);
1840
+ const { isRouteMismatch, currentPath } = useRouteMismatch(activeStep);
1841
+ const pausedForMissingTargetRef = useRef5(null);
1842
+ useEffect8(() => {
1843
+ if (!isRouteMismatch) return;
1844
+ if (!runningState || runningState.status !== "running") return;
1845
+ pause();
1846
+ }, [isRouteMismatch, runningState, pause]);
1847
+ useEffect8(() => {
1848
+ if (isRouteMismatch) return;
1849
+ if (pausedForMissingTargetRef.current !== null) return;
1850
+ if (!state || state.status !== "paused") return;
1851
+ resume();
1852
+ }, [isRouteMismatch, state, resume]);
1785
1853
  const skipHiddenStep = useCallback2(() => {
1786
1854
  if (!runningState || runningState.status !== "running") return;
1787
1855
  if (!activeFlowId) return;
@@ -1794,12 +1862,41 @@ var useHudState = (options = {}) => {
1794
1862
  next();
1795
1863
  }
1796
1864
  }, [activeFlowId, complete, flows, next, runningState]);
1797
- const { target: hudTarget } = useHiddenTargetFallback({
1865
+ const { target: hudTarget, isInGracePeriod } = useHiddenTargetFallback({
1798
1866
  step: runningStep,
1799
1867
  target,
1800
1868
  viewportRect,
1801
1869
  onSkip: skipHiddenStep
1802
1870
  });
1871
+ useEffect8(() => {
1872
+ if (isRouteMismatch) return;
1873
+ if (activeStep?.route !== void 0) return;
1874
+ if (isInGracePeriod) return;
1875
+ if (target.visibility !== "missing") return;
1876
+ if (target.isScreen) return;
1877
+ if (!runningState || runningState.status !== "running") return;
1878
+ pausedForMissingTargetRef.current = currentPath;
1879
+ pause();
1880
+ }, [
1881
+ isRouteMismatch,
1882
+ activeStep?.route,
1883
+ isInGracePeriod,
1884
+ target.visibility,
1885
+ target.isScreen,
1886
+ runningState,
1887
+ currentPath,
1888
+ pause
1889
+ ]);
1890
+ useEffect8(() => {
1891
+ if (pausedForMissingTargetRef.current === null) return;
1892
+ if (!state || state.status !== "paused") return;
1893
+ if (currentPath === pausedForMissingTargetRef.current) return;
1894
+ pausedForMissingTargetRef.current = null;
1895
+ resume();
1896
+ }, [currentPath, state, resume]);
1897
+ useEffect8(() => {
1898
+ pausedForMissingTargetRef.current = null;
1899
+ }, [activeStep?.id]);
1803
1900
  const canRenderStep = Boolean(runningStep && runningState);
1804
1901
  const focusTrapActive = canRenderStep;
1805
1902
  const flowHudOptions = matchesFlowFilter && activeFlowId ? flows.get(activeFlowId)?.hud ?? null : null;
@@ -1816,7 +1913,8 @@ var useHudState = (options = {}) => {
1816
1913
  flowHudOptions,
1817
1914
  hudRenderMode,
1818
1915
  matchesFlowFilter,
1819
- activeFlowId
1916
+ activeFlowId,
1917
+ isInGracePeriod
1820
1918
  };
1821
1919
  };
1822
1920
 
@@ -1850,7 +1948,7 @@ var useHudDescription = (options) => {
1850
1948
  };
1851
1949
 
1852
1950
  // src/hooks/useHudShortcuts.ts
1853
- import { useEffect as useEffect8 } from "react";
1951
+ import { useEffect as useEffect9 } from "react";
1854
1952
 
1855
1953
  // src/hooks/useTourControls.ts
1856
1954
  import { useCallback as useCallback3, useMemo as useMemo6 } from "react";
@@ -1975,7 +2073,7 @@ var useHudShortcuts = (target, options) => {
1975
2073
  const escapeEnabled = options?.escape ?? true;
1976
2074
  const { state } = useTour();
1977
2075
  const { cancel, canGoBack, goBack, canGoNext, goNext, isActive } = useTourControls();
1978
- useEffect8(() => {
2076
+ useEffect9(() => {
1979
2077
  if (!isBrowser) return void 0;
1980
2078
  if (!enabled) return void 0;
1981
2079
  if (!target) return void 0;
@@ -2039,10 +2137,10 @@ var useHudShortcuts = (target, options) => {
2039
2137
  };
2040
2138
 
2041
2139
  // src/hooks/useTourHud.ts
2042
- import { useMemo as useMemo8, useState as useState8 } from "react";
2140
+ import { useMemo as useMemo8, useState as useState9 } from "react";
2043
2141
 
2044
2142
  // src/hooks/useBodyScrollLock.ts
2045
- import { useEffect as useEffect9 } from "react";
2143
+ import { useEffect as useEffect10 } from "react";
2046
2144
  var lockCount = 0;
2047
2145
  var previousOverflow = null;
2048
2146
  var acquireLock = () => {
@@ -2063,7 +2161,7 @@ var releaseLock = () => {
2063
2161
  }
2064
2162
  };
2065
2163
  var useBodyScrollLock = (enabled) => {
2066
- useEffect9(() => {
2164
+ useEffect10(() => {
2067
2165
  if (!enabled) return;
2068
2166
  acquireLock();
2069
2167
  return () => {
@@ -2073,42 +2171,45 @@ var useBodyScrollLock = (enabled) => {
2073
2171
  };
2074
2172
 
2075
2173
  // src/hooks/useHudTargetIssue.ts
2076
- import { useEffect as useEffect10, useMemo as useMemo7, useState as useState7 } from "react";
2077
- var deriveTargetIssue = (target) => {
2174
+ import { useEffect as useEffect11, useMemo as useMemo7, useState as useState8 } from "react";
2175
+ var deriveTargetIssue = (params) => {
2176
+ const { target, labels } = params;
2078
2177
  if (target.isScreen) return null;
2079
2178
  if (target.status === "idle") return null;
2080
2179
  switch (target.visibility) {
2081
2180
  case "missing":
2082
2181
  return {
2083
2182
  type: "missing",
2084
- title: "Looking for the target",
2085
- body: "Flowsterix is still trying to find this element. Make sure the UI piece is mounted or adjust the selector.",
2086
- hint: target.rectSource === "stored" ? "Showing the last known position until the element returns." : void 0
2183
+ title: labels.targetIssue.missingTitle,
2184
+ body: labels.targetIssue.missingBody,
2185
+ hint: target.rectSource === "stored" ? labels.targetIssue.missingHint : void 0
2087
2186
  };
2088
2187
  case "hidden":
2089
2188
  return {
2090
2189
  type: "hidden",
2091
- title: "Target is hidden",
2092
- body: "The element exists but is currently hidden, collapsed, or zero-sized. Expand it so the highlight can lock on."
2190
+ title: labels.targetIssue.hiddenTitle,
2191
+ body: labels.targetIssue.hiddenBody,
2192
+ hint: target.rectSource === "stored" ? labels.targetIssue.hiddenHint : void 0
2093
2193
  };
2094
2194
  case "detached":
2095
2195
  return {
2096
2196
  type: "detached",
2097
- title: "Target left the page",
2098
- body: "Navigate back to the screen that contains this element or reopen it before continuing the tour."
2197
+ title: labels.targetIssue.detachedTitle,
2198
+ body: labels.targetIssue.detachedBody
2099
2199
  };
2100
2200
  default:
2101
2201
  return null;
2102
2202
  }
2103
2203
  };
2104
2204
  var useHudTargetIssue = (target, options) => {
2205
+ const labels = useTourLabels();
2105
2206
  const delayMs = Math.max(0, options?.delayMs ?? 500);
2106
- const [armed, setArmed] = useState7(false);
2207
+ const [armed, setArmed] = useState8(false);
2107
2208
  const rawIssue = useMemo7(
2108
- () => deriveTargetIssue(target),
2109
- [target.isScreen, target.rectSource, target.status, target.visibility]
2209
+ () => deriveTargetIssue({ target, labels }),
2210
+ [target.isScreen, target.rectSource, target.status, target.visibility, labels]
2110
2211
  );
2111
- useEffect10(() => {
2212
+ useEffect11(() => {
2112
2213
  if (!rawIssue) {
2113
2214
  setArmed(false);
2114
2215
  return;
@@ -2142,7 +2243,7 @@ var useTourHud = (options = {}) => {
2142
2243
  const { backdropInteraction, lockBodyScroll } = useTour();
2143
2244
  const hudState = useHudState();
2144
2245
  const disableDefaultHud = hudState.hudRenderMode === "none";
2145
- const [popoverNode, setPopoverNode] = useState8(null);
2246
+ const [popoverNode, setPopoverNode] = useState9(null);
2146
2247
  const popoverOptions = hudState.flowHudOptions?.popover;
2147
2248
  const description = useHudDescription({
2148
2249
  step: hudState.runningStep,
@@ -2214,7 +2315,7 @@ var useTourHud = (options = {}) => {
2214
2315
  };
2215
2316
 
2216
2317
  // src/hooks/useTourOverlay.ts
2217
- import { useEffect as useEffect11, useMemo as useMemo9, useRef as useRef5 } from "react";
2318
+ import { useEffect as useEffect12, useMemo as useMemo9, useRef as useRef6 } from "react";
2218
2319
  var DEFAULT_PADDING = 12;
2219
2320
  var DEFAULT_RADIUS = 12;
2220
2321
  var DEFAULT_EDGE_BUFFER = 0;
@@ -2224,11 +2325,12 @@ var useTourOverlay = (options) => {
2224
2325
  padding = DEFAULT_PADDING,
2225
2326
  radius = DEFAULT_RADIUS,
2226
2327
  edgeBuffer = DEFAULT_EDGE_BUFFER,
2227
- interactionMode = "passthrough"
2328
+ interactionMode = "passthrough",
2329
+ isInGracePeriod = false
2228
2330
  } = options;
2229
- const hasShownRef = useRef5(false);
2230
- const lastReadyTargetRef = useRef5(null);
2231
- useEffect11(() => {
2331
+ const hasShownRef = useRef6(false);
2332
+ const lastReadyTargetRef = useRef6(null);
2333
+ useEffect12(() => {
2232
2334
  if (!isBrowser) return;
2233
2335
  if (target.status === "ready") {
2234
2336
  hasShownRef.current = true;
@@ -2279,7 +2381,7 @@ var useTourOverlay = (options) => {
2279
2381
  radius: highlightRadius
2280
2382
  } : null;
2281
2383
  const maskCapable = useMemo9(() => supportsMasking(), []);
2282
- const isActive = target.status === "ready" || target.status === "resolving" && cachedTarget !== null;
2384
+ const isActive = target.status === "ready" || target.status === "resolving" && cachedTarget !== null || isInGracePeriod;
2283
2385
  const shouldMask = maskCapable && isActive;
2284
2386
  const maskId = useMemo9(
2285
2387
  () => `tour-overlay-mask-${Math.random().toString(36).slice(2, 10)}`,
@@ -2464,12 +2566,12 @@ var useRadixDialogAdapter = (options = {}) => {
2464
2566
  };
2465
2567
 
2466
2568
  // src/hooks/useDelayAdvance.ts
2467
- import { useEffect as useEffect12, useMemo as useMemo10, useState as useState9 } from "react";
2569
+ import { useEffect as useEffect13, useMemo as useMemo10, useState as useState10 } from "react";
2468
2570
  var getTimestamp = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
2469
2571
  var useDelayAdvance = () => {
2470
2572
  const { delayInfo, activeStep, state } = useTour();
2471
- const [now, setNow] = useState9(() => getTimestamp());
2472
- useEffect12(() => {
2573
+ const [now, setNow] = useState10(() => getTimestamp());
2574
+ useEffect13(() => {
2473
2575
  if (!delayInfo) return;
2474
2576
  if (!activeStep || activeStep.id !== delayInfo.stepId) return;
2475
2577
  if (!state || state.status !== "running") return;
@@ -2486,7 +2588,7 @@ var useDelayAdvance = () => {
2486
2588
  }
2487
2589
  };
2488
2590
  }, [delayInfo, activeStep, state]);
2489
- useEffect12(() => {
2591
+ useEffect13(() => {
2490
2592
  if (!delayInfo) {
2491
2593
  setNow(getTimestamp());
2492
2594
  }
@@ -2544,7 +2646,7 @@ var useDelayAdvance = () => {
2544
2646
  };
2545
2647
 
2546
2648
  // src/components/OverlayBackdrop.tsx
2547
- import { useEffect as useEffect13, useRef as useRef6 } from "react";
2649
+ import { useEffect as useEffect14, useRef as useRef7 } from "react";
2548
2650
  import { createPortal } from "react-dom";
2549
2651
  import { AnimatePresence } from "motion/react";
2550
2652
  import { jsx as jsx3, jsxs } from "react/jsx-runtime";
@@ -2633,9 +2735,9 @@ var OverlayBackdrop = ({
2633
2735
  viewport
2634
2736
  } = overlay;
2635
2737
  const hasHighlightBounds = Boolean(highlight.rect);
2636
- const prevScreenTargetRef = useRef6(null);
2738
+ const prevScreenTargetRef = useRef7(null);
2637
2739
  const shouldSnapHighlight = prevScreenTargetRef.current === true && !highlight.isScreen && hasHighlightBounds;
2638
- useEffect13(() => {
2740
+ useEffect14(() => {
2639
2741
  prevScreenTargetRef.current = highlight.isScreen;
2640
2742
  }, [highlight.isScreen]);
2641
2743
  const resolvedBlur = typeof blurAmount === "number" ? `${blurAmount}px` : "0px";
@@ -2872,7 +2974,7 @@ var OverlayBackdrop = ({
2872
2974
  };
2873
2975
 
2874
2976
  // src/components/TourPopoverPortal.tsx
2875
- import { useEffect as useEffect14, useLayoutEffect as useLayoutEffect2, useMemo as useMemo11, useRef as useRef7, useState as useState10 } from "react";
2977
+ import { useEffect as useEffect15, useLayoutEffect as useLayoutEffect2, useMemo as useMemo11, useRef as useRef8, useState as useState11 } from "react";
2876
2978
  import { createPortal as createPortal2 } from "react-dom";
2877
2979
  import {
2878
2980
  autoPlacement,
@@ -2939,12 +3041,12 @@ var TourPopoverPortal = ({
2939
3041
  const popoverContentTransition = transitionsOverride?.popoverContent ?? adapter.transitions.popoverContent ?? DEFAULT_POPOVER_CONTENT_TRANSITION;
2940
3042
  const viewport = useViewportRect();
2941
3043
  const prefersMobileLayout = viewport.width <= MOBILE_BREAKPOINT || viewport.height <= MOBILE_HEIGHT_BREAKPOINT;
2942
- const prefersMobileRef = useRef7(prefersMobileLayout);
2943
- useEffect14(() => {
3044
+ const prefersMobileRef = useRef8(prefersMobileLayout);
3045
+ useEffect15(() => {
2944
3046
  prefersMobileRef.current = prefersMobileLayout;
2945
3047
  }, [prefersMobileLayout]);
2946
- const lastReadyTargetRef = useRef7(null);
2947
- useEffect14(() => {
3048
+ const lastReadyTargetRef = useRef8(null);
3049
+ useEffect15(() => {
2948
3050
  if (target.status === "ready" && target.rect) {
2949
3051
  lastReadyTargetRef.current = {
2950
3052
  rect: { ...target.rect },
@@ -2957,9 +3059,10 @@ var TourPopoverPortal = ({
2957
3059
  const cachedTarget = lastReadyTargetRef.current;
2958
3060
  const resolvedRect = target.rect ?? target.lastResolvedRect ?? cachedTarget?.rect ?? null;
2959
3061
  const resolvedIsScreen = target.status === "ready" ? target.isScreen : cachedTarget?.isScreen ?? target.isScreen;
3062
+ const shouldHidePopover = !resolvedRect && !target.isScreen;
2960
3063
  const fallbackRect = resolvedRect ?? viewport;
2961
3064
  const fallbackIsScreen = resolvedIsScreen;
2962
- const [floatingSize, setFloatingSize] = useState10(null);
3065
+ const [floatingSize, setFloatingSize] = useState11(null);
2963
3066
  const clampVertical = (value) => Math.min(viewport.height - 24, Math.max(24, value));
2964
3067
  const clampHorizontal = (value) => Math.min(viewport.width - 24, Math.max(24, value));
2965
3068
  const screenCenteredTop = viewport.height / 2 - (floatingSize?.height ?? 0) / 2;
@@ -2986,22 +3089,22 @@ var TourPopoverPortal = ({
2986
3089
  }),
2987
3090
  [viewport.height, viewport.width]
2988
3091
  );
2989
- const floatingRef = useRef7(null);
2990
- const cachedFloatingPositionRef = useRef7(null);
2991
- const appliedFloatingCacheRef = useRef7(null);
2992
- const deferredScreenSnapRef = useRef7(null);
2993
- const [layoutMode, setLayoutMode] = useState10(
3092
+ const floatingRef = useRef8(null);
3093
+ const cachedFloatingPositionRef = useRef8(null);
3094
+ const appliedFloatingCacheRef = useRef8(null);
3095
+ const deferredScreenSnapRef = useRef8(null);
3096
+ const [layoutMode, setLayoutMode] = useState11(
2994
3097
  () => prefersMobileLayout ? "mobile" : "floating"
2995
3098
  );
2996
- const [floatingPosition, setFloatingPosition] = useState10(fallbackPosition);
2997
- const [dragPosition, setDragPosition] = useState10(null);
2998
- const [isDragging, setIsDragging] = useState10(false);
2999
- const dragStateRef = useRef7(null);
3000
- const overflowRetryRef = useRef7({
3099
+ const [floatingPosition, setFloatingPosition] = useState11(fallbackPosition);
3100
+ const [dragPosition, setDragPosition] = useState11(null);
3101
+ const [isDragging, setIsDragging] = useState11(false);
3102
+ const dragStateRef = useRef8(null);
3103
+ const overflowRetryRef = useRef8({
3001
3104
  stepId: null,
3002
3105
  attempts: 0
3003
3106
  });
3004
- const overflowRetryTimeoutRef = useRef7(null);
3107
+ const overflowRetryTimeoutRef = useRef8(null);
3005
3108
  useLayoutEffect2(() => {
3006
3109
  if (!isBrowser) return;
3007
3110
  const node = floatingRef.current;
@@ -3021,18 +3124,18 @@ var TourPopoverPortal = ({
3021
3124
  const autoAlignment = resolvedPlacement.endsWith(
3022
3125
  "-start"
3023
3126
  ) ? "start" : resolvedPlacement.endsWith("-end") ? "end" : void 0;
3024
- useEffect14(() => {
3127
+ useEffect15(() => {
3025
3128
  setDragPosition(null);
3026
3129
  setLayoutMode(prefersMobileRef.current ? "mobile" : "floating");
3027
3130
  cachedFloatingPositionRef.current = null;
3028
3131
  appliedFloatingCacheRef.current = null;
3029
3132
  }, [target.stepId]);
3030
- useEffect14(() => {
3133
+ useEffect15(() => {
3031
3134
  if (layoutMode !== "manual") {
3032
3135
  setDragPosition(null);
3033
3136
  }
3034
3137
  }, [layoutMode]);
3035
- useEffect14(() => {
3138
+ useEffect15(() => {
3036
3139
  cachedFloatingPositionRef.current = floatingPosition;
3037
3140
  const cacheKey = getFloatingCacheKey(target);
3038
3141
  if (cacheKey) {
@@ -3055,17 +3158,17 @@ var TourPopoverPortal = ({
3055
3158
  }),
3056
3159
  [viewport.height, viewport.width]
3057
3160
  );
3058
- useEffect14(() => {
3161
+ useEffect15(() => {
3059
3162
  if (layoutMode === "docked") {
3060
3163
  setFloatingPosition(dockedPosition);
3061
3164
  }
3062
3165
  }, [dockedPosition, layoutMode]);
3063
- useEffect14(() => {
3166
+ useEffect15(() => {
3064
3167
  if (layoutMode === "mobile") {
3065
3168
  setFloatingPosition(mobilePosition);
3066
3169
  }
3067
3170
  }, [layoutMode, mobilePosition]);
3068
- useEffect14(() => {
3171
+ useEffect15(() => {
3069
3172
  if (prefersMobileLayout) {
3070
3173
  if (layoutMode !== "mobile") {
3071
3174
  setLayoutMode("mobile");
@@ -3078,7 +3181,7 @@ var TourPopoverPortal = ({
3078
3181
  setFloatingPosition(fallbackPosition);
3079
3182
  }
3080
3183
  }, [fallbackPosition, layoutMode, prefersMobileLayout]);
3081
- useEffect14(() => {
3184
+ useEffect15(() => {
3082
3185
  if (layoutMode !== "floating") return;
3083
3186
  const stepId = target.stepId;
3084
3187
  if (!stepId) return;
@@ -3102,7 +3205,7 @@ var TourPopoverPortal = ({
3102
3205
  target.stepId
3103
3206
  ]);
3104
3207
  const shouldDeferScreenSnap = layoutMode === "floating" && target.isScreen && Boolean(layoutId);
3105
- useEffect14(() => {
3208
+ useEffect15(() => {
3106
3209
  return () => {
3107
3210
  if (deferredScreenSnapRef.current !== null) {
3108
3211
  cancelAnimationFrame(deferredScreenSnapRef.current);
@@ -3122,7 +3225,7 @@ var TourPopoverPortal = ({
3122
3225
  target.isScreen,
3123
3226
  target.status
3124
3227
  ]);
3125
- useEffect14(() => {
3228
+ useEffect15(() => {
3126
3229
  if (!shouldDeferScreenSnap) return;
3127
3230
  if (deferredScreenSnapRef.current !== null) {
3128
3231
  cancelAnimationFrame(deferredScreenSnapRef.current);
@@ -3149,7 +3252,7 @@ var TourPopoverPortal = ({
3149
3252
  }
3150
3253
  };
3151
3254
  }, [fallbackPosition, shouldDeferScreenSnap]);
3152
- useEffect14(() => {
3255
+ useEffect15(() => {
3153
3256
  return () => {
3154
3257
  if (overflowRetryTimeoutRef.current !== null) {
3155
3258
  window.clearTimeout(overflowRetryTimeoutRef.current);
@@ -3375,7 +3478,7 @@ var TourPopoverPortal = ({
3375
3478
  }
3376
3479
  event.preventDefault();
3377
3480
  };
3378
- useEffect14(() => endDrag, []);
3481
+ useEffect15(() => endDrag, []);
3379
3482
  const shouldUseFallbackInitial = layoutMode !== "mobile" && (Boolean(target.lastResolvedRect) || Boolean(cachedTarget));
3380
3483
  const floatingCacheKey = layoutMode === "mobile" ? null : getFloatingCacheKey(target);
3381
3484
  const persistedFloatingInitial = floatingCacheKey && floatingPositionCache.has(floatingCacheKey) ? floatingPositionCache.get(floatingCacheKey) ?? null : null;
@@ -3462,11 +3565,12 @@ var TourPopoverPortal = ({
3462
3565
  dragHandleProps,
3463
3566
  descriptionProps
3464
3567
  };
3568
+ if (shouldHidePopover) return null;
3465
3569
  return createPortal2(children(context), host);
3466
3570
  };
3467
3571
 
3468
3572
  // src/components/TourFocusManager.tsx
3469
- import { useEffect as useEffect15, useLayoutEffect as useLayoutEffect3, useRef as useRef8, useState as useState11 } from "react";
3573
+ import { useEffect as useEffect16, useLayoutEffect as useLayoutEffect3, useRef as useRef9, useState as useState12 } from "react";
3470
3574
  import { createPortal as createPortal3 } from "react-dom";
3471
3575
 
3472
3576
  // src/utils/focus.ts
@@ -3543,18 +3647,18 @@ var TourFocusManager = ({
3543
3647
  highlightRect,
3544
3648
  guardElementFocusRing
3545
3649
  }) => {
3546
- const previousFocusRef = useRef8(null);
3547
- const guardNodesRef = useRef8({
3650
+ const previousFocusRef = useRef9(null);
3651
+ const guardNodesRef = useRef9({
3548
3652
  "target-start": null,
3549
3653
  "target-end": null,
3550
3654
  "popover-start": null,
3551
3655
  "popover-end": null
3552
3656
  });
3553
- const lastTabDirectionRef = useRef8("forward");
3554
- const suppressGuardHopRef = useRef8(null);
3555
- const [targetRingActive, setTargetRingActive] = useState11(false);
3556
- const [popoverRingActive, setPopoverRingActive] = useState11(false);
3557
- const [popoverRect, setPopoverRect] = useState11(null);
3657
+ const lastTabDirectionRef = useRef9("forward");
3658
+ const suppressGuardHopRef = useRef9(null);
3659
+ const [targetRingActive, setTargetRingActive] = useState12(false);
3660
+ const [popoverRingActive, setPopoverRingActive] = useState12(false);
3661
+ const [popoverRect, setPopoverRect] = useState12(null);
3558
3662
  const restoreFocus = () => {
3559
3663
  const previous = previousFocusRef.current;
3560
3664
  previousFocusRef.current = null;
@@ -3580,7 +3684,7 @@ var TourFocusManager = ({
3580
3684
  restoreFocus();
3581
3685
  };
3582
3686
  }, [active, popoverNode, target.element]);
3583
- useEffect15(() => {
3687
+ useEffect16(() => {
3584
3688
  if (!isBrowser) return;
3585
3689
  if (!active) return;
3586
3690
  const doc = popoverNode?.ownerDocument ?? target.element?.ownerDocument ?? document;
package/dist/labels.d.ts CHANGED
@@ -15,6 +15,16 @@ export interface TourLabels {
15
15
  formatTimeRemaining: (params: {
16
16
  ms: number;
17
17
  }) => string;
18
+ targetIssue: {
19
+ missingTitle: string;
20
+ missingBody: string;
21
+ missingHint: string;
22
+ hiddenTitle: string;
23
+ hiddenBody: string;
24
+ hiddenHint: string;
25
+ detachedTitle: string;
26
+ detachedBody: string;
27
+ };
18
28
  }
19
29
  export declare const defaultLabels: TourLabels;
20
30
  export declare const LabelsProvider: import("react").Provider<TourLabels>;
@@ -1 +1 @@
1
- {"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../src/labels.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IAEzB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,EAAE,MAAM,CAAA;IAGrB,gBAAgB,EAAE,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAA;IACxE,iBAAiB,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAA;IACrD,iBAAiB,EAAE,MAAM,CAAA;IAGzB,mBAAmB,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAA;CACxD;AAED,eAAO,MAAM,aAAa,EAAE,UAa3B,CAAA;AAID,eAAO,MAAM,cAAc,sCAAyB,CAAA;AAEpD,wBAAgB,aAAa,IAAI,UAAU,CAE1C"}
1
+ {"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../src/labels.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IAEzB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,EAAE,MAAM,CAAA;IAGrB,gBAAgB,EAAE,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAA;IACxE,iBAAiB,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAA;IACrD,iBAAiB,EAAE,MAAM,CAAA;IAGzB,mBAAmB,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAA;IAGvD,WAAW,EAAE;QACX,YAAY,EAAE,MAAM,CAAA;QACpB,WAAW,EAAE,MAAM,CAAA;QACnB,WAAW,EAAE,MAAM,CAAA;QACnB,WAAW,EAAE,MAAM,CAAA;QACnB,UAAU,EAAE,MAAM,CAAA;QAClB,UAAU,EAAE,MAAM,CAAA;QAClB,aAAa,EAAE,MAAM,CAAA;QACrB,YAAY,EAAE,MAAM,CAAA;KACrB,CAAA;CACF;AAED,eAAO,MAAM,aAAa,EAAE,UA2B3B,CAAA;AAID,eAAO,MAAM,cAAc,sCAAyB,CAAA;AAEpD,wBAAgB,aAAa,IAAI,UAAU,CAE1C"}
@@ -1,3 +1,4 @@
1
+ import "../chunk-WCLT3A6G.mjs";
1
2
  import {
2
3
  TanStackRouterSync,
3
4
  getTanStackRouter,