@guidelytech/react-native 0.6.1 → 0.7.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.js CHANGED
@@ -3,8 +3,98 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
 
5
5
  // src/index.tsx
6
- import { useContext as useContext2, useEffect as useEffect8, useRef as useRef5, useState as useState7 } from "react";
7
- import { AppState, Dimensions, Linking, Platform as Platform6 } from "react-native";
6
+ import { useContext as useContext3, useEffect as useEffect9, useMemo as useMemo5, useRef as useRef6, useState as useState8 } from "react";
7
+ import { AppState as AppState2, Dimensions, Linking, Platform as Platform6 } from "react-native";
8
+ import { createStaticFlags as createStaticFlags2, stableStringify as stableStringify2 } from "@guidelytech/flags";
9
+
10
+ // src/flags.tsx
11
+ import { createFlagsClient, createStaticFlags, jsonEquals, stableStringify } from "@guidelytech/flags";
12
+ import { createContext, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
13
+ import { AppState } from "react-native";
14
+ import { Fragment, jsx } from "react/jsx-runtime";
15
+ var FlagsContext = createContext(null);
16
+ var EMPTY = createStaticFlags();
17
+ function useFlagsValue(select) {
18
+ const src = useContext(FlagsContext);
19
+ const reader = src?.client ?? src?.bootstrap ?? EMPTY;
20
+ const cache = useRef(null);
21
+ const get = () => {
22
+ const value = select(reader);
23
+ const c = cache.current;
24
+ if (c && c.reader === reader && jsonEquals(c.value, value)) return c.value;
25
+ cache.current = { reader, value };
26
+ return value;
27
+ };
28
+ return useSyncExternalStore(reader.subscribe, get, get);
29
+ }
30
+ function useFlag(key, fallback = false) {
31
+ return useFlagsValue((r) => r.isEnabled(key, fallback));
32
+ }
33
+ function useFeatureValue(key, fallback) {
34
+ return useFlagsValue((r) => r.getValue(key, fallback));
35
+ }
36
+ function useFlagDetails(key, fallback) {
37
+ return useFlagsValue((r) => r.getDetails(key, fallback));
38
+ }
39
+ function useFlags() {
40
+ return useFlagsValue((r) => r.getAll());
41
+ }
42
+ function useFlagsReady() {
43
+ return useFlagsValue((r) => r.isReady());
44
+ }
45
+ function useFlagsClient() {
46
+ return useContext(FlagsContext)?.client ?? null;
47
+ }
48
+ function Feature({ flag, children, fallback = null }) {
49
+ return /* @__PURE__ */ jsx(Fragment, { children: useFlag(flag) ? children : fallback });
50
+ }
51
+ var FeatureFlag = Feature;
52
+ function FeatureValue({ flag, value, children, fallback = null }) {
53
+ const current = useFeatureValue(flag, null);
54
+ return /* @__PURE__ */ jsx(Fragment, { children: jsonEquals(current, value) ? children : fallback });
55
+ }
56
+ function useBootstrap(payload, attributes) {
57
+ const key = stableStringify(attributes);
58
+ return useMemo(() => payload ? createStaticFlags({ payload, attributes }) : EMPTY, [payload, key]);
59
+ }
60
+ function FlagsProvider({ children, client: external, payload, ...options }) {
61
+ const [created, setCreated] = useState(null);
62
+ const optionsRef = useRef(options);
63
+ optionsRef.current = options;
64
+ const payloadRef = useRef(payload);
65
+ payloadRef.current = payload;
66
+ const sdkKey = options.sdkKey ?? options.projectId;
67
+ const { apiUrl, environment } = options;
68
+ useEffect(() => {
69
+ if (external || !sdkKey) return;
70
+ let c;
71
+ try {
72
+ c = createFlagsClient({ ...optionsRef.current, sdkKey, payload: payloadRef.current ?? void 0 });
73
+ } catch (err) {
74
+ optionsRef.current.onError?.(err instanceof Error ? err : new Error(String(err)));
75
+ return;
76
+ }
77
+ setCreated(c);
78
+ const sub = AppState.addEventListener("change", (s) => {
79
+ if (s === "active") void c.refresh();
80
+ else if (s === "background") void c.flush();
81
+ });
82
+ return () => {
83
+ sub.remove();
84
+ c.destroy();
85
+ setCreated((cur) => cur === c ? null : cur);
86
+ };
87
+ }, [external, sdkKey, apiUrl, environment]);
88
+ const client = external ?? created;
89
+ const attributes = options.attributes ?? {};
90
+ const attrsKey = stableStringify(attributes);
91
+ useEffect(() => {
92
+ if (client && options.attributes) client.setAttributes(options.attributes);
93
+ }, [client, attrsKey]);
94
+ const bootstrap = useBootstrap(payload, attributes);
95
+ const value = useMemo(() => ({ client, bootstrap }), [client, bootstrap]);
96
+ return /* @__PURE__ */ jsx(FlagsContext.Provider, { value, children });
97
+ }
8
98
 
9
99
  // ../shared/dist/chunk-OLWGMDRX.js
10
100
  function fnv1a(input) {
@@ -834,6 +924,9 @@ function isWithinSchedule(guide, now = Date.now()) {
834
924
  return true;
835
925
  }
836
926
 
927
+ // src/core/client.ts
928
+ import { createFlagsClient as createFlagsClient2 } from "@guidelytech/flags";
929
+
837
930
  // src/core/targets.ts
838
931
  var TargetTimeoutError = class extends Error {
839
932
  constructor(ids) {
@@ -1729,7 +1822,7 @@ var PersistentStore = class {
1729
1822
 
1730
1823
  // src/core/client.ts
1731
1824
  var SDK_NAME = "@guidelytech/react-native";
1732
- var SDK_VERSION = "0.6.1";
1825
+ var SDK_VERSION = "0.7.0";
1733
1826
  var DEFAULT_API_URL = "https://guidelytech.com";
1734
1827
  var PAGE_SCOPED = /* @__PURE__ */ new Set(["page_load", "url_match", "element_click", "element_visible", "delay"]);
1735
1828
  var nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
@@ -1776,6 +1869,10 @@ var GuidelyClient = class {
1776
1869
  __publicField(this, "messenger", null);
1777
1870
  __publicField(this, "ui", { steps: [], checklists: [], survey: null, resourceCenterOpen: false, theme: {}, messenger: false });
1778
1871
  __publicField(this, "uiListeners", /* @__PURE__ */ new Set());
1872
+ /** Feature flags for this environment: `guidely.flags.isEnabled("new-checkout")`. */
1873
+ __publicField(this, "flags");
1874
+ /** Experiments already reported this session (`experiment_viewed` is sent once per session). */
1875
+ __publicField(this, "exposures", /* @__PURE__ */ new Set());
1779
1876
  __publicField(this, "customListeners", /* @__PURE__ */ new Map());
1780
1877
  __publicField(this, "eventBus", {
1781
1878
  on: (name, cb) => {
@@ -1804,6 +1901,40 @@ var GuidelyClient = class {
1804
1901
  this.store = new PersistentStore(options.storage ?? null, (err) => this.log("storage error", err));
1805
1902
  if (!options.storage) this.log("no `storage` passed: identity and guide history last only for this app session. Pass AsyncStorage to persist them.");
1806
1903
  this.cleanups.push(this.targets.onTap((id) => this.handleTap(id)));
1904
+ if (options.user) this.user = { ...options.user };
1905
+ this.flags = this.createFlags(options.flags);
1906
+ }
1907
+ createFlags(opts) {
1908
+ const base = { sdkKey: this.options.projectId, apiUrl: this.options.apiUrl, fetch: this.options.fetch, sdk: { name: SDK_NAME, version: SDK_VERSION } };
1909
+ if (opts === false) return createFlagsClient2({ ...base, overrideBehaviour: "localOnly", offline: true, reportUsage: false, trackExposures: false, storage: false, logger: null });
1910
+ const o = opts ?? {};
1911
+ return createFlagsClient2({
1912
+ ...base,
1913
+ storage: this.options.storage ?? false,
1914
+ reportUsage: this.options.analytics,
1915
+ // Failures surface through onError (and `debug` logs); development warnings (unknown flag keys) reach the console.
1916
+ logger: { warn: (...a) => console.warn("[Guidely]", ...a), error: (...a) => this.log(...a) },
1917
+ ...o,
1918
+ contextAttributes: this.flagContext(),
1919
+ // Exposures travel through the event pipeline (with the session) as `experiment_viewed`.
1920
+ trackExposures: false,
1921
+ onFlagEvaluated: (result) => {
1922
+ this.trackExposure(result);
1923
+ o.onFlagEvaluated?.(result);
1924
+ },
1925
+ onReady: () => {
1926
+ this.emitter.emit("flags:ready");
1927
+ o.onReady?.();
1928
+ },
1929
+ onChange: (data) => {
1930
+ this.emitter.emit("flags:change");
1931
+ o.onChange?.(data);
1932
+ },
1933
+ onError: (err) => {
1934
+ this.reportError(err);
1935
+ o.onError?.(err);
1936
+ }
1937
+ });
1807
1938
  }
1808
1939
  // ---------------------------------------------------------------------------
1809
1940
  // Lifecycle
@@ -1834,6 +1965,7 @@ var GuidelyClient = class {
1834
1965
  this.session = saved?.id && !this.sessionExpired(Date.parse(saved.lastActiveAt)) ? saved : newSession();
1835
1966
  this.persistSession();
1836
1967
  this.lastAudiences = new Set(this.state.audienceIds);
1968
+ this.syncFlagAttributes();
1837
1969
  this.queue = new EventQueue({
1838
1970
  store: this.store,
1839
1971
  storageKey: KEYS.queue(key),
@@ -1923,6 +2055,7 @@ var GuidelyClient = class {
1923
2055
  this.persistSession();
1924
2056
  void this.queue?.flush();
1925
2057
  void this.store.flush();
2058
+ void this.flags.flush();
1926
2059
  this.messenger?.setForeground(false);
1927
2060
  } else if (state === "active" && this.backgroundedAt !== null) {
1928
2061
  this.messenger?.setForeground(true);
@@ -1933,6 +2066,7 @@ var GuidelyClient = class {
1933
2066
  this.persistSession();
1934
2067
  this.firedDelays.clear();
1935
2068
  }
2069
+ void this.flags.refresh();
1936
2070
  if (Date.now() - this.configFetchedAt > this.options.configRefreshMs) void this.refresh();
1937
2071
  else if (this.isReady) this.evaluate("ready");
1938
2072
  }
@@ -1959,6 +2093,7 @@ var GuidelyClient = class {
1959
2093
  this.cleanups.splice(0).forEach((fn) => fn());
1960
2094
  void this.queue?.flush();
1961
2095
  this.queue?.destroy();
2096
+ this.flags.destroy();
1962
2097
  this.emitter.clear();
1963
2098
  this.uiListeners.clear();
1964
2099
  }
@@ -1973,6 +2108,7 @@ var GuidelyClient = class {
1973
2108
  const same = this.user?.id === id;
1974
2109
  this.user = { id, traits: { ...same ? this.user?.traits : {}, ...traits || {} }, account: account ?? (same ? this.user?.account : void 0) };
1975
2110
  writeJson(this.store, KEYS.user(this.options.projectId), this.user);
2111
+ this.syncFlagAttributes();
1976
2112
  this.enqueue("identify", { properties: { traits: sanitizeProperties(this.user.traits) } });
1977
2113
  void this.syncIdentify().then(() => {
1978
2114
  this.emitter.emit("identify", { userId: id, anonymousId: this.anonymousId });
@@ -2016,6 +2152,7 @@ var GuidelyClient = class {
2016
2152
  };
2017
2153
  if (data.firstSeenAt && Date.now() - Date.parse(data.firstSeenAt) > 6e4) this.firstVisit = false;
2018
2154
  this.persistState();
2155
+ this.syncFlagAttributes();
2019
2156
  this.notify();
2020
2157
  }
2021
2158
  /** Sets or replaces the account (company / organisation) for the current user. */
@@ -2024,6 +2161,7 @@ var GuidelyClient = class {
2024
2161
  if (!this.user?.id) {
2025
2162
  this.user = { id: "", traits: this.user?.traits, account };
2026
2163
  writeJson(this.store, KEYS.user(this.options.projectId), this.user);
2164
+ this.syncFlagAttributes();
2027
2165
  return;
2028
2166
  }
2029
2167
  await this.identify(this.user.id, void 0, account);
@@ -2033,6 +2171,7 @@ var GuidelyClient = class {
2033
2171
  await this.init();
2034
2172
  if (!this.user?.id) {
2035
2173
  this.user = { id: "", traits: { ...this.user?.traits || {}, ...traits }, account: this.user?.account };
2174
+ this.syncFlagAttributes();
2036
2175
  return;
2037
2176
  }
2038
2177
  await this.identify(this.user.id, traits, this.user.account);
@@ -2060,6 +2199,8 @@ var GuidelyClient = class {
2060
2199
  this.lastAudiences = /* @__PURE__ */ new Set();
2061
2200
  this.firedDelays.clear();
2062
2201
  this.firstVisit = true;
2202
+ this.exposures.clear();
2203
+ this.syncFlagAttributes();
2063
2204
  this.emitter.emit("identify", { userId: null, anonymousId: this.anonymousId });
2064
2205
  this.notify();
2065
2206
  if (this.isReady) this.evaluate("identify");
@@ -2133,6 +2274,7 @@ var GuidelyClient = class {
2133
2274
  const changed = name !== this.screenName;
2134
2275
  this.screenName = name;
2135
2276
  this.targets.setScreen(name);
2277
+ if (changed) this.syncFlagAttributes();
2136
2278
  const url = this.currentUrl();
2137
2279
  if (changed) this.enqueue("page_view", { properties: { ...properties, path: url, screen: name } });
2138
2280
  this.completeChecklistTasksBy("url_visited", url);
@@ -2803,6 +2945,58 @@ var GuidelyClient = class {
2803
2945
  showNewMessage(text) {
2804
2946
  this.whenHydrated(() => this.messenger?.showNewMessage(text));
2805
2947
  }
2948
+ // ---------------------------------------------------------------------------
2949
+ // Feature flags
2950
+ // ---------------------------------------------------------------------------
2951
+ /** Boolean flag value for the current user; `fallback` (default `false`) until loaded or when missing. */
2952
+ isFeatureEnabled(key, fallback = false) {
2953
+ return this.flags.isEnabled(key, fallback);
2954
+ }
2955
+ /** Flag value for the current user; `fallback` when missing or of a different type. */
2956
+ getFeatureValue(key, fallback) {
2957
+ return this.flags.getValue(key, fallback);
2958
+ }
2959
+ /** Flag value plus why it was served (reason, rule, variation, experiment). */
2960
+ getFeatureDetails(key, fallback) {
2961
+ return this.flags.getDetails(key, fallback);
2962
+ }
2963
+ /** Calls `cb` whenever flag values may have changed. Returns an unsubscribe function. */
2964
+ onFlagsChange(cb) {
2965
+ return this.flags.subscribe(cb);
2966
+ }
2967
+ /** Attributes flags are evaluated with: identity, traits, account, audiences, screen and device. */
2968
+ flagContext() {
2969
+ const d = this.bridge.device;
2970
+ const account = this.user?.account;
2971
+ return {
2972
+ platform: d.platform,
2973
+ deviceType: d.deviceType,
2974
+ locale: d.locale,
2975
+ appVersion: this.options.appVersion ?? d.appVersion,
2976
+ environment: this.options.environment,
2977
+ ...this.screenName ? { screen: this.screenName, path: this.currentUrl() } : {},
2978
+ ...this.user?.traits ?? {},
2979
+ id: this.user?.id || this.anonymousId || void 0,
2980
+ anonymousId: this.anonymousId || void 0,
2981
+ account: account ? { ...account.traits ?? {}, id: account.id } : void 0,
2982
+ $audiences: [...this.state.audienceIds]
2983
+ };
2984
+ }
2985
+ syncFlagAttributes() {
2986
+ if (!this.destroyed) this.flags?.setContextAttributes(this.flagContext());
2987
+ }
2988
+ /** `experiment_viewed`, once per session and assignment, through the event queue (consent applies). */
2989
+ trackExposure(result) {
2990
+ const exp = result.inExperiment ? result.experiment : void 0;
2991
+ if (!exp || this.destroyed) return;
2992
+ this.whenHydrated(() => {
2993
+ const key = `${this.session.id}|${exp.key}|${exp.hashValue}|${exp.variationId}`;
2994
+ if (this.exposures.has(key)) return;
2995
+ if (this.exposures.size > 500) this.exposures.clear();
2996
+ this.exposures.add(key);
2997
+ this.enqueue("experiment_viewed", { properties: { experimentKey: exp.key, variationId: exp.variationId, flagKey: result.key, hashAttribute: exp.hashAttribute, ...exp.variationName ? { variationName: exp.variationName } : {} } });
2998
+ });
2999
+ }
2806
3000
  on(event, cb) {
2807
3001
  return this.emitter.on(event, cb);
2808
3002
  }
@@ -2831,7 +3025,8 @@ var GuidelyClient = class {
2831
3025
  audienceIds: this.state.audienceIds,
2832
3026
  session: this.session,
2833
3027
  pendingEvents: this.queue?.size ?? 0,
2834
- targets: this.targets.ids()
3028
+ targets: this.targets.ids(),
3029
+ flagsReady: this.flags.isReady()
2835
3030
  };
2836
3031
  }
2837
3032
  // ---------------------------------------------------------------------------
@@ -2862,7 +3057,7 @@ function forPlatform(config, platform) {
2862
3057
  }
2863
3058
 
2864
3059
  // src/ui/Overlay.tsx
2865
- import { useCallback as useCallback2, useEffect as useEffect6, useRef as useRef3, useState as useState6, useSyncExternalStore as useSyncExternalStore2 } from "react";
3060
+ import { useCallback as useCallback2, useEffect as useEffect7, useRef as useRef4, useState as useState7, useSyncExternalStore as useSyncExternalStore3 } from "react";
2866
3061
  import { Platform as Platform5, Pressable as Pressable8, StatusBar, StyleSheet as StyleSheet9, Text as Text8, useColorScheme, useWindowDimensions, View as View9 } from "react-native";
2867
3062
 
2868
3063
  // src/ui/Checklist.tsx
@@ -2870,11 +3065,11 @@ import { Pressable as Pressable3, ScrollView as ScrollView2, StyleSheet as Style
2870
3065
 
2871
3066
  // src/ui/primitives.tsx
2872
3067
  import { Pressable, StyleSheet, Text, View } from "react-native";
2873
- import { jsx } from "react/jsx-runtime";
3068
+ import { jsx as jsx2 } from "react/jsx-runtime";
2874
3069
  function GButton({ label, variant = "primary", onPress, palette: p, style, accessibilityHint, disabled }) {
2875
3070
  const filled = variant === "primary";
2876
3071
  const outlined = variant === "secondary";
2877
- return /* @__PURE__ */ jsx(
3072
+ return /* @__PURE__ */ jsx2(
2878
3073
  Pressable,
2879
3074
  {
2880
3075
  accessibilityRole: "button",
@@ -2892,15 +3087,15 @@ function GButton({ label, variant = "primary", onPress, palette: p, style, acces
2892
3087
  variant === "link" && styles.link,
2893
3088
  style
2894
3089
  ],
2895
- children: /* @__PURE__ */ jsx(Text, { style: [styles.btnText, { color: filled ? p.onPrimary : variant === "link" ? p.primary : variant === "ghost" ? p.muted : p.text, fontFamily: p.fontFamily }, variant === "link" && styles.underline], numberOfLines: 1, children: label })
3090
+ children: /* @__PURE__ */ jsx2(Text, { style: [styles.btnText, { color: filled ? p.onPrimary : variant === "link" ? p.primary : variant === "ghost" ? p.muted : p.text, fontFamily: p.fontFamily }, variant === "link" && styles.underline], numberOfLines: 1, children: label })
2896
3091
  }
2897
3092
  );
2898
3093
  }
2899
3094
  function CloseButton({ onPress, palette: p, label = "Close" }) {
2900
- return /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: label, onPress, hitSlop: 10, style: ({ pressed }) => [styles.close, { opacity: pressed ? 0.6 : 1 }], children: /* @__PURE__ */ jsx(Text, { style: [styles.closeText, { color: p.muted }], children: "\xD7" }) });
3095
+ return /* @__PURE__ */ jsx2(Pressable, { accessibilityRole: "button", accessibilityLabel: label, onPress, hitSlop: 10, style: ({ pressed }) => [styles.close, { opacity: pressed ? 0.6 : 1 }], children: /* @__PURE__ */ jsx2(Text, { style: [styles.closeText, { color: p.muted }], children: "\xD7" }) });
2901
3096
  }
2902
3097
  function Surface({ palette: p, children, style }) {
2903
- return /* @__PURE__ */ jsx(View, { style: [styles.surface, { backgroundColor: p.background, borderRadius: p.radius, borderColor: p.border }, style], children });
3098
+ return /* @__PURE__ */ jsx2(View, { style: [styles.surface, { backgroundColor: p.background, borderRadius: p.radius, borderColor: p.border }, style], children });
2904
3099
  }
2905
3100
  var styles = StyleSheet.create({
2906
3101
  btn: { minHeight: 36, paddingHorizontal: 14, paddingVertical: 8, alignItems: "center", justifyContent: "center" },
@@ -2920,7 +3115,7 @@ var styles = StyleSheet.create({
2920
3115
  });
2921
3116
 
2922
3117
  // src/ui/RichText.tsx
2923
- import { useEffect, useMemo, useState } from "react";
3118
+ import { useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
2924
3119
  import { Image, Modal, Platform, Pressable as Pressable2, ScrollView, StyleSheet as StyleSheet2, Text as Text2, View as View2 } from "react-native";
2925
3120
 
2926
3121
  // src/core/html.ts
@@ -3147,7 +3342,7 @@ function htmlToText(html) {
3147
3342
  }
3148
3343
 
3149
3344
  // src/ui/RichText.tsx
3150
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
3345
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
3151
3346
  var MONO = Platform.select({ ios: "Menlo", android: "monospace", default: "monospace" });
3152
3347
  var HEADING_SIZE = [0, 20, 17, 15];
3153
3348
  function runStyle(run, p) {
@@ -3161,8 +3356,8 @@ function runStyle(run, p) {
3161
3356
  };
3162
3357
  }
3163
3358
  function BodyImage({ block, palette: p }) {
3164
- const [ratio, setRatio] = useState(block.width && block.height ? block.width / block.height : 16 / 9);
3165
- useEffect(() => {
3359
+ const [ratio, setRatio] = useState2(block.width && block.height ? block.width / block.height : 16 / 9);
3360
+ useEffect2(() => {
3166
3361
  if (block.width && block.height) return;
3167
3362
  let live = true;
3168
3363
  Image.getSize(block.src, (w, h) => live && w > 0 && h > 0 && setRatio(w / h), () => {
@@ -3171,77 +3366,77 @@ function BodyImage({ block, palette: p }) {
3171
3366
  live = false;
3172
3367
  };
3173
3368
  }, [block.src, block.width, block.height]);
3174
- return /* @__PURE__ */ jsx2(Image, { source: { uri: block.src }, accessible: true, accessibilityRole: "image", accessibilityLabel: block.alt || void 0, resizeMode: "contain", style: [styles2.image, { aspectRatio: ratio, backgroundColor: p.surface }] });
3369
+ return /* @__PURE__ */ jsx3(Image, { source: { uri: block.src }, accessible: true, accessibilityRole: "image", accessibilityLabel: block.alt || void 0, resizeMode: "contain", style: [styles2.image, { aspectRatio: ratio, backgroundColor: p.surface }] });
3175
3370
  }
3176
3371
  function BodyVideo({ block, palette: p, onLink }) {
3177
3372
  return /* @__PURE__ */ jsxs(Pressable2, { accessibilityRole: "link", accessibilityLabel: block.alt, onPress: () => onLink(block.href), style: ({ pressed }) => [styles2.video, { borderColor: p.border, backgroundColor: p.surface, opacity: pressed ? 0.7 : 1 }], children: [
3178
- /* @__PURE__ */ jsx2(Text2, { style: [styles2.play, { color: p.primary }], children: "\u25B6" }),
3179
- /* @__PURE__ */ jsx2(Text2, { style: { color: p.primary, fontFamily: p.fontFamily, fontWeight: "600" }, children: block.alt })
3373
+ /* @__PURE__ */ jsx3(Text2, { style: [styles2.play, { color: p.primary }], children: "\u25B6" }),
3374
+ /* @__PURE__ */ jsx3(Text2, { style: { color: p.primary, fontFamily: p.fontFamily, fontWeight: "600" }, children: block.alt })
3180
3375
  ] });
3181
3376
  }
3182
3377
  var PAGE_RATIO = 1 / 1.414;
3183
3378
  var pageRatio = (p) => p.width && p.height ? p.width / p.height : PAGE_RATIO;
3184
3379
  function PdfBadge() {
3185
- return /* @__PURE__ */ jsx2(View2, { style: styles2.badge, children: /* @__PURE__ */ jsx2(Text2, { style: styles2.badgeText, children: "PDF" }) });
3380
+ return /* @__PURE__ */ jsx3(View2, { style: styles2.badge, children: /* @__PURE__ */ jsx3(Text2, { style: styles2.badgeText, children: "PDF" }) });
3186
3381
  }
3187
3382
  function BodyFile({ block, palette: p, onLink }) {
3188
3383
  return /* @__PURE__ */ jsxs(Pressable2, { accessibilityRole: "link", accessibilityLabel: `${block.alt}, PDF`, onPress: () => onLink(block.href), style: ({ pressed }) => [styles2.video, { borderColor: p.border, backgroundColor: p.surface, opacity: pressed ? 0.7 : 1 }], children: [
3189
- /* @__PURE__ */ jsx2(PdfBadge, {}),
3190
- /* @__PURE__ */ jsx2(Text2, { numberOfLines: 2, ellipsizeMode: "middle", style: [styles2.flex, { color: p.text, fontFamily: p.fontFamily, fontWeight: "600" }], children: block.alt })
3384
+ /* @__PURE__ */ jsx3(PdfBadge, {}),
3385
+ /* @__PURE__ */ jsx3(Text2, { numberOfLines: 2, ellipsizeMode: "middle", style: [styles2.flex, { color: p.text, fontFamily: p.fontFamily, fontWeight: "600" }], children: block.alt })
3191
3386
  ] });
3192
3387
  }
3193
3388
  function BodyPdf({ block, palette: p, onLink }) {
3194
3389
  const pages = block.pages ?? [];
3195
- const [width, setWidth] = useState(0);
3196
- const [index, setIndex] = useState(0);
3197
- const [reading, setReading] = useState(false);
3390
+ const [width, setWidth] = useState2(0);
3391
+ const [index, setIndex] = useState2(0);
3392
+ const [reading, setReading] = useState2(false);
3198
3393
  const height = width ? Math.max(...pages.map((pg) => width / pageRatio(pg))) : 0;
3199
3394
  const onScroll = (e) => width && setIndex(Math.round(e.nativeEvent.contentOffset.x / width));
3200
3395
  return /* @__PURE__ */ jsxs(View2, { style: [styles2.pdf, { borderColor: p.border, backgroundColor: p.surface }], children: [
3201
3396
  /* @__PURE__ */ jsxs(Pressable2, { accessibilityRole: "button", accessibilityLabel: `Read ${block.alt} full screen`, onPress: () => setReading(true), style: ({ pressed }) => [styles2.pdfHeader, { opacity: pressed ? 0.7 : 1 }], children: [
3202
- /* @__PURE__ */ jsx2(PdfBadge, {}),
3203
- /* @__PURE__ */ jsx2(Text2, { numberOfLines: 1, ellipsizeMode: "middle", style: [styles2.flex, { color: p.text, fontFamily: p.fontFamily, fontWeight: "600" }], children: block.alt }),
3204
- /* @__PURE__ */ jsx2(Text2, { style: { color: p.muted, fontSize: 16 }, children: "\u2922" })
3397
+ /* @__PURE__ */ jsx3(PdfBadge, {}),
3398
+ /* @__PURE__ */ jsx3(Text2, { numberOfLines: 1, ellipsizeMode: "middle", style: [styles2.flex, { color: p.text, fontFamily: p.fontFamily, fontWeight: "600" }], children: block.alt }),
3399
+ /* @__PURE__ */ jsx3(Text2, { style: { color: p.muted, fontSize: 16 }, children: "\u2922" })
3205
3400
  ] }),
3206
3401
  /* @__PURE__ */ jsxs(View2, { style: styles2.pdfPages, onLayout: (e) => setWidth(e.nativeEvent.layout.width), children: [
3207
- width > 0 && /* @__PURE__ */ jsx2(ScrollView, { horizontal: true, pagingEnabled: true, showsHorizontalScrollIndicator: false, onScroll, scrollEventThrottle: 32, accessibilityLabel: `${block.alt}, page ${index + 1} of ${pages.length}`, children: pages.map((pg, i) => /* @__PURE__ */ jsx2(Pressable2, { onPress: () => setReading(true), accessibilityRole: "imagebutton", accessibilityLabel: pg.alt || `Page ${i + 1}`, accessibilityHint: "Opens the document full screen", style: { width, height, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx2(Image, { source: { uri: pg.src }, resizeMode: "contain", style: [styles2.page, { width: Math.min(width, height * pageRatio(pg)), aspectRatio: pageRatio(pg) }] }) }, i)) }),
3208
- pages.length > 1 && /* @__PURE__ */ jsx2(View2, { pointerEvents: "none", style: styles2.count, children: /* @__PURE__ */ jsxs(Text2, { style: styles2.countText, children: [
3402
+ width > 0 && /* @__PURE__ */ jsx3(ScrollView, { horizontal: true, pagingEnabled: true, showsHorizontalScrollIndicator: false, onScroll, scrollEventThrottle: 32, accessibilityLabel: `${block.alt}, page ${index + 1} of ${pages.length}`, children: pages.map((pg, i) => /* @__PURE__ */ jsx3(Pressable2, { onPress: () => setReading(true), accessibilityRole: "imagebutton", accessibilityLabel: pg.alt || `Page ${i + 1}`, accessibilityHint: "Opens the document full screen", style: { width, height, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx3(Image, { source: { uri: pg.src }, resizeMode: "contain", style: [styles2.page, { width: Math.min(width, height * pageRatio(pg)), aspectRatio: pageRatio(pg) }] }) }, i)) }),
3403
+ pages.length > 1 && /* @__PURE__ */ jsx3(View2, { pointerEvents: "none", style: styles2.count, children: /* @__PURE__ */ jsxs(Text2, { style: styles2.countText, children: [
3209
3404
  index + 1,
3210
3405
  " / ",
3211
3406
  pages.length
3212
3407
  ] }) })
3213
3408
  ] }),
3214
- /* @__PURE__ */ jsx2(Modal, { visible: reading, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: () => setReading(false), children: /* @__PURE__ */ jsxs(View2, { style: styles2.reader, children: [
3409
+ /* @__PURE__ */ jsx3(Modal, { visible: reading, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: () => setReading(false), children: /* @__PURE__ */ jsxs(View2, { style: styles2.reader, children: [
3215
3410
  /* @__PURE__ */ jsxs(View2, { style: styles2.readerBar, children: [
3216
- /* @__PURE__ */ jsx2(Text2, { numberOfLines: 1, ellipsizeMode: "middle", style: [styles2.flex, styles2.readerTitle], accessibilityRole: "header", children: block.alt }),
3217
- /* @__PURE__ */ jsx2(Pressable2, { accessibilityRole: "link", onPress: () => onLink(block.href), style: styles2.readerBtn, children: /* @__PURE__ */ jsx2(Text2, { style: styles2.readerBtnText, children: "Open PDF \u2197" }) }),
3218
- /* @__PURE__ */ jsx2(Pressable2, { accessibilityRole: "button", accessibilityLabel: "Close", onPress: () => setReading(false), style: styles2.readerBtn, children: /* @__PURE__ */ jsx2(Text2, { style: styles2.readerBtnText, children: "Done" }) })
3411
+ /* @__PURE__ */ jsx3(Text2, { numberOfLines: 1, ellipsizeMode: "middle", style: [styles2.flex, styles2.readerTitle], accessibilityRole: "header", children: block.alt }),
3412
+ /* @__PURE__ */ jsx3(Pressable2, { accessibilityRole: "link", onPress: () => onLink(block.href), style: styles2.readerBtn, children: /* @__PURE__ */ jsx3(Text2, { style: styles2.readerBtnText, children: "Open PDF \u2197" }) }),
3413
+ /* @__PURE__ */ jsx3(Pressable2, { accessibilityRole: "button", accessibilityLabel: "Close", onPress: () => setReading(false), style: styles2.readerBtn, children: /* @__PURE__ */ jsx3(Text2, { style: styles2.readerBtnText, children: "Done" }) })
3219
3414
  ] }),
3220
- /* @__PURE__ */ jsx2(ScrollView, { contentContainerStyle: styles2.readerPages, children: pages.map((pg, i) => /* @__PURE__ */ jsx2(Image, { source: { uri: pg.src }, accessible: true, accessibilityLabel: pg.alt || `Page ${i + 1}`, resizeMode: "contain", style: [styles2.readerPage, { aspectRatio: pageRatio(pg) }] }, i)) })
3415
+ /* @__PURE__ */ jsx3(ScrollView, { contentContainerStyle: styles2.readerPages, children: pages.map((pg, i) => /* @__PURE__ */ jsx3(Image, { source: { uri: pg.src }, accessible: true, accessibilityLabel: pg.alt || `Page ${i + 1}`, resizeMode: "contain", style: [styles2.readerPage, { aspectRatio: pageRatio(pg) }] }, i)) })
3221
3416
  ] }) })
3222
3417
  ] });
3223
3418
  }
3224
3419
  function RichText({ html, palette: p, onLink, size = 15 }) {
3225
- const blocks = useMemo(() => parseHtml(html), [html]);
3420
+ const blocks = useMemo2(() => parseHtml(html), [html]);
3226
3421
  if (!blocks.length) return null;
3227
3422
  const base = { color: p.text, fontSize: size, lineHeight: Math.round(size * 1.42), fontFamily: p.fontFamily };
3228
- return /* @__PURE__ */ jsx2(View2, { style: styles2.wrap, children: blocks.map((b, i) => {
3229
- if (b.kind === "image") return /* @__PURE__ */ jsx2(BodyImage, { block: b, palette: p }, i);
3230
- if (b.kind === "video") return /* @__PURE__ */ jsx2(BodyVideo, { block: b, palette: p, onLink }, i);
3231
- if (b.kind === "pdf") return /* @__PURE__ */ jsx2(BodyPdf, { block: b, palette: p, onLink }, i);
3232
- if (b.kind === "file") return /* @__PURE__ */ jsx2(BodyFile, { block: b, palette: p, onLink }, i);
3233
- const runs = b.runs.map((r, j) => /* @__PURE__ */ jsx2(Text2, { style: runStyle(r, p), onPress: r.href ? () => onLink(r.href) : void 0, accessibilityRole: r.href ? "link" : void 0, children: r.text }, j));
3423
+ return /* @__PURE__ */ jsx3(View2, { style: styles2.wrap, children: blocks.map((b, i) => {
3424
+ if (b.kind === "image") return /* @__PURE__ */ jsx3(BodyImage, { block: b, palette: p }, i);
3425
+ if (b.kind === "video") return /* @__PURE__ */ jsx3(BodyVideo, { block: b, palette: p, onLink }, i);
3426
+ if (b.kind === "pdf") return /* @__PURE__ */ jsx3(BodyPdf, { block: b, palette: p, onLink }, i);
3427
+ if (b.kind === "file") return /* @__PURE__ */ jsx3(BodyFile, { block: b, palette: p, onLink }, i);
3428
+ const runs = b.runs.map((r, j) => /* @__PURE__ */ jsx3(Text2, { style: runStyle(r, p), onPress: r.href ? () => onLink(r.href) : void 0, accessibilityRole: r.href ? "link" : void 0, children: r.text }, j));
3234
3429
  if (b.kind === "list-item") {
3235
3430
  return /* @__PURE__ */ jsxs(View2, { style: [styles2.li, { paddingLeft: (b.level - 1) * 14 }], children: [
3236
- /* @__PURE__ */ jsx2(Text2, { style: [base, styles2.marker], children: b.marker }),
3237
- /* @__PURE__ */ jsx2(Text2, { style: [base, styles2.flex], children: runs })
3431
+ /* @__PURE__ */ jsx3(Text2, { style: [base, styles2.marker], children: b.marker }),
3432
+ /* @__PURE__ */ jsx3(Text2, { style: [base, styles2.flex], children: runs })
3238
3433
  ] }, i);
3239
3434
  }
3240
3435
  if (b.kind === "heading") {
3241
3436
  const fs = HEADING_SIZE[b.level] ?? 15;
3242
- return /* @__PURE__ */ jsx2(Text2, { accessibilityRole: "header", style: [base, { fontSize: fs, lineHeight: Math.round(fs * 1.3), fontWeight: "700" }], children: runs }, i);
3437
+ return /* @__PURE__ */ jsx3(Text2, { accessibilityRole: "header", style: [base, { fontSize: fs, lineHeight: Math.round(fs * 1.3), fontWeight: "700" }], children: runs }, i);
3243
3438
  }
3244
- return /* @__PURE__ */ jsx2(Text2, { style: [base, b.kind === "quote" && { borderLeftWidth: 3, borderLeftColor: p.border, paddingLeft: 8, color: p.muted }], children: runs }, i);
3439
+ return /* @__PURE__ */ jsx3(Text2, { style: [base, b.kind === "quote" && { borderLeftWidth: 3, borderLeftColor: p.border, paddingLeft: 8, color: p.muted }], children: runs }, i);
3245
3440
  }) });
3246
3441
  }
3247
3442
  var styles2 = StyleSheet2.create({
@@ -3270,7 +3465,7 @@ var styles2 = StyleSheet2.create({
3270
3465
  });
3271
3466
 
3272
3467
  // src/ui/Checklist.tsx
3273
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
3468
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
3274
3469
  function Checklist({ view, client, palette: p, geometry, showLauncher, launcherBottom }) {
3275
3470
  const config = view.guide.config.checklist;
3276
3471
  if (!config) return null;
@@ -3285,10 +3480,10 @@ function Checklist({ view, client, palette: p, geometry, showLauncher, launcherB
3285
3480
  const nextOpen = config.ordered ? config.tasks.find((t) => !done.has(t.id))?.id : void 0;
3286
3481
  const bottom = insets.bottom + launcherBottom;
3287
3482
  return /* @__PURE__ */ jsxs2(View3, { pointerEvents: "box-none", style: StyleSheet3.absoluteFill, children: [
3288
- view.open ? /* @__PURE__ */ jsx3(Surface, { palette: p, style: [styles3.panel, side, { width, bottom: bottom + (showLauncher ? 56 : 0), maxHeight: viewport.height - insets.top - bottom - 80 }], children: /* @__PURE__ */ jsxs2(View3, { accessibilityViewIsModal: true, style: styles3.panelInner, children: [
3483
+ view.open ? /* @__PURE__ */ jsx4(Surface, { palette: p, style: [styles3.panel, side, { width, bottom: bottom + (showLauncher ? 56 : 0), maxHeight: viewport.height - insets.top - bottom - 80 }], children: /* @__PURE__ */ jsxs2(View3, { accessibilityViewIsModal: true, style: styles3.panelInner, children: [
3289
3484
  /* @__PURE__ */ jsxs2(View3, { style: styles3.head, children: [
3290
3485
  /* @__PURE__ */ jsxs2(View3, { style: styles3.flex, children: [
3291
- /* @__PURE__ */ jsx3(Text3, { accessibilityRole: "header", style: [styles3.title, { color: p.text, fontFamily: p.fontFamily }], children: finished ? config.completionTitle : config.launcherLabel }),
3486
+ /* @__PURE__ */ jsx4(Text3, { accessibilityRole: "header", style: [styles3.title, { color: p.text, fontFamily: p.fontFamily }], children: finished ? config.completionTitle : config.launcherLabel }),
3292
3487
  /* @__PURE__ */ jsxs2(Text3, { style: [styles3.sub, { color: p.muted }], children: [
3293
3488
  completed,
3294
3489
  " of ",
@@ -3296,11 +3491,11 @@ function Checklist({ view, client, palette: p, geometry, showLauncher, launcherB
3296
3491
  " complete"
3297
3492
  ] })
3298
3493
  ] }),
3299
- /* @__PURE__ */ jsx3(CloseButton, { palette: p, onPress: () => client.setChecklistOpen(view.guide.id, false), label: "Close checklist" })
3494
+ /* @__PURE__ */ jsx4(CloseButton, { palette: p, onPress: () => client.setChecklistOpen(view.guide.id, false), label: "Close checklist" })
3300
3495
  ] }),
3301
- /* @__PURE__ */ jsx3(View3, { style: [styles3.bar, { backgroundColor: p.surface }], accessibilityRole: "progressbar", accessibilityValue: { min: 0, max: total, now: completed }, children: /* @__PURE__ */ jsx3(View3, { style: [styles3.fill, { backgroundColor: p.primary, width: `${total ? completed / total * 100 : 0}%` }] }) }),
3302
- finished && config.completionBody ? /* @__PURE__ */ jsx3(RichText, { html: config.completionBody, palette: p, onLink: (u) => client.navigate(u), size: 14 }) : null,
3303
- /* @__PURE__ */ jsx3(ScrollView2, { style: styles3.tasks, contentContainerStyle: styles3.tasksContent, children: config.tasks.map((task) => {
3496
+ /* @__PURE__ */ jsx4(View3, { style: [styles3.bar, { backgroundColor: p.surface }], accessibilityRole: "progressbar", accessibilityValue: { min: 0, max: total, now: completed }, children: /* @__PURE__ */ jsx4(View3, { style: [styles3.fill, { backgroundColor: p.primary, width: `${total ? completed / total * 100 : 0}%` }] }) }),
3497
+ finished && config.completionBody ? /* @__PURE__ */ jsx4(RichText, { html: config.completionBody, palette: p, onLink: (u) => client.navigate(u), size: 14 }) : null,
3498
+ /* @__PURE__ */ jsx4(ScrollView2, { style: styles3.tasks, contentContainerStyle: styles3.tasksContent, children: config.tasks.map((task) => {
3304
3499
  const isDone = done.has(task.id);
3305
3500
  const locked = !!nextOpen && !isDone && task.id !== nextOpen;
3306
3501
  return /* @__PURE__ */ jsxs2(
@@ -3313,20 +3508,20 @@ function Checklist({ view, client, palette: p, geometry, showLauncher, launcherB
3313
3508
  onPress: () => client.checklistTaskPressed(view.guide.id, task.id),
3314
3509
  style: ({ pressed }) => [styles3.task, { borderColor: p.border, opacity: locked ? 0.5 : pressed ? 0.7 : 1 }],
3315
3510
  children: [
3316
- /* @__PURE__ */ jsx3(View3, { style: [styles3.check, isDone ? { backgroundColor: p.primary, borderColor: p.primary } : { borderColor: p.border }], children: isDone ? /* @__PURE__ */ jsx3(Text3, { style: [styles3.tick, { color: p.onPrimary }], children: "\u2713" }) : null }),
3511
+ /* @__PURE__ */ jsx4(View3, { style: [styles3.check, isDone ? { backgroundColor: p.primary, borderColor: p.primary } : { borderColor: p.border }], children: isDone ? /* @__PURE__ */ jsx4(Text3, { style: [styles3.tick, { color: p.onPrimary }], children: "\u2713" }) : null }),
3317
3512
  /* @__PURE__ */ jsxs2(View3, { style: styles3.flex, children: [
3318
3513
  /* @__PURE__ */ jsxs2(Text3, { style: [styles3.taskTitle, { color: p.text, fontFamily: p.fontFamily }, isDone && { textDecorationLine: "line-through", color: p.muted }], children: [
3319
3514
  task.title,
3320
- !task.required ? /* @__PURE__ */ jsx3(Text3, { style: { color: p.muted, fontWeight: "400" }, children: " (optional)" }) : null
3515
+ !task.required ? /* @__PURE__ */ jsx4(Text3, { style: { color: p.muted, fontWeight: "400" }, children: " (optional)" }) : null
3321
3516
  ] }),
3322
- task.description ? /* @__PURE__ */ jsx3(Text3, { style: [styles3.taskDesc, { color: p.muted }], children: task.description }) : null
3517
+ task.description ? /* @__PURE__ */ jsx4(Text3, { style: [styles3.taskDesc, { color: p.muted }], children: task.description }) : null
3323
3518
  ] })
3324
3519
  ]
3325
3520
  },
3326
3521
  task.id
3327
3522
  );
3328
3523
  }) }),
3329
- config.dismissible ? /* @__PURE__ */ jsx3(GButton, { label: finished ? "Close for good" : "Dismiss checklist", variant: "ghost", palette: p, onPress: () => client.dismissChecklist(view.guide.id), style: styles3.dismiss }) : null
3524
+ config.dismissible ? /* @__PURE__ */ jsx4(GButton, { label: finished ? "Close for good" : "Dismiss checklist", variant: "ghost", palette: p, onPress: () => client.dismissChecklist(view.guide.id), style: styles3.dismiss }) : null
3330
3525
  ] }) }) : null,
3331
3526
  showLauncher ? /* @__PURE__ */ jsxs2(
3332
3527
  Pressable3,
@@ -3337,8 +3532,8 @@ function Checklist({ view, client, palette: p, geometry, showLauncher, launcherB
3337
3532
  onPress: () => client.setChecklistOpen(view.guide.id, !view.open),
3338
3533
  style: ({ pressed }) => [styles3.launcher, side, { bottom, backgroundColor: p.primary, opacity: pressed ? 0.85 : 1 }],
3339
3534
  children: [
3340
- /* @__PURE__ */ jsx3(View3, { style: [styles3.count, { borderColor: p.onPrimary }], children: /* @__PURE__ */ jsx3(Text3, { style: [styles3.countText, { color: p.onPrimary }], children: finished ? "\u2713" : `${completed}/${total}` }) }),
3341
- /* @__PURE__ */ jsx3(Text3, { style: [styles3.launcherText, { color: p.onPrimary, fontFamily: p.fontFamily }], numberOfLines: 1, children: config.launcherLabel })
3535
+ /* @__PURE__ */ jsx4(View3, { style: [styles3.count, { borderColor: p.onPrimary }], children: /* @__PURE__ */ jsx4(Text3, { style: [styles3.countText, { color: p.onPrimary }], children: finished ? "\u2713" : `${completed}/${total}` }) }),
3536
+ /* @__PURE__ */ jsx4(Text3, { style: [styles3.launcherText, { color: p.onPrimary, fontFamily: p.fontFamily }], numberOfLines: 1, children: config.launcherLabel })
3342
3537
  ]
3343
3538
  }
3344
3539
  ) : null
@@ -3382,19 +3577,19 @@ var styles3 = StyleSheet3.create({
3382
3577
  });
3383
3578
 
3384
3579
  // src/ui/Messenger.tsx
3385
- import { useCallback, useEffect as useEffect2, useMemo as useMemo2, useRef, useState as useState2, useSyncExternalStore } from "react";
3580
+ import { useCallback, useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2, useState as useState3, useSyncExternalStore as useSyncExternalStore2 } from "react";
3386
3581
  import { ActivityIndicator, Keyboard, Modal as Modal2, Platform as Platform2, Pressable as Pressable4, ScrollView as ScrollView3, StyleSheet as StyleSheet4, Text as Text4, TextInput, View as View4 } from "react-native";
3387
- import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
3582
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
3388
3583
  function useMessenger(store) {
3389
- return useSyncExternalStore(
3584
+ return useSyncExternalStore2(
3390
3585
  useCallback((cb) => store.subscribe(cb), [store]),
3391
3586
  () => store.getSnapshot(),
3392
3587
  () => store.getSnapshot()
3393
3588
  );
3394
3589
  }
3395
3590
  function useKeyboardHeight() {
3396
- const [h, setH] = useState2(0);
3397
- useEffect2(() => {
3591
+ const [h, setH] = useState3(0);
3592
+ useEffect3(() => {
3398
3593
  if (Platform2.OS !== "ios") return;
3399
3594
  const show = Keyboard.addListener("keyboardWillShow", (e) => setH(e.endCoordinates.height));
3400
3595
  const hide = Keyboard.addListener("keyboardWillHide", () => setH(0));
@@ -3417,8 +3612,8 @@ function MessengerLauncher({ store, palette: p, style }) {
3417
3612
  if (snap.open) return null;
3418
3613
  const label = snap.unread ? `Open ${snap.config.name}, ${snap.unread} unread` : `Open ${snap.config.name}`;
3419
3614
  return /* @__PURE__ */ jsxs3(Pressable4, { accessibilityRole: "button", accessibilityLabel: label, onPress: () => store.open(), style: ({ pressed }) => [styles4.launcher, style, { backgroundColor: p.primary, opacity: pressed ? 0.85 : 1 }], children: [
3420
- /* @__PURE__ */ jsx4(Text4, { style: [styles4.launcherIcon, { color: p.onPrimary }], children: "\u{1F4AC}" }),
3421
- snap.unread ? /* @__PURE__ */ jsx4(View4, { style: styles4.badge, children: /* @__PURE__ */ jsx4(Text4, { style: styles4.badgeText, children: snap.unread }) }) : null
3615
+ /* @__PURE__ */ jsx5(Text4, { style: [styles4.launcherIcon, { color: p.onPrimary }], children: "\u{1F4AC}" }),
3616
+ snap.unread ? /* @__PURE__ */ jsx5(View4, { style: styles4.badge, children: /* @__PURE__ */ jsx5(Text4, { style: styles4.badgeText, children: snap.unread }) }) : null
3422
3617
  ] });
3423
3618
  }
3424
3619
  function MessengerSheet({ store, client, palette: p, topInset, bottomInset }) {
@@ -3426,14 +3621,14 @@ function MessengerSheet({ store, client, palette: p, topInset, bottomInset }) {
3426
3621
  const keyboard = useKeyboardHeight();
3427
3622
  const v = snap.view;
3428
3623
  const title = v.kind === "thread" ? snap.thread?.conversation?.subject || "New conversation" : v.kind === "article" ? "" : v.tab === "home" ? snap.config.name : v.tab === "messages" ? "Messages" : "Help";
3429
- return /* @__PURE__ */ jsx4(Modal2, { visible: snap.open, animationType: "slide", presentationStyle: Platform2.OS === "ios" ? "pageSheet" : "fullScreen", onRequestClose: () => v.kind === "tab" ? store.close() : store.back(), children: /* @__PURE__ */ jsxs3(View4, { style: [styles4.sheet, { backgroundColor: p.background, paddingTop: Platform2.OS === "ios" ? 8 : topInset, paddingBottom: keyboard }], children: [
3624
+ return /* @__PURE__ */ jsx5(Modal2, { visible: snap.open, animationType: "slide", presentationStyle: Platform2.OS === "ios" ? "pageSheet" : "fullScreen", onRequestClose: () => v.kind === "tab" ? store.close() : store.back(), children: /* @__PURE__ */ jsxs3(View4, { style: [styles4.sheet, { backgroundColor: p.background, paddingTop: Platform2.OS === "ios" ? 8 : topInset, paddingBottom: keyboard }], children: [
3430
3625
  /* @__PURE__ */ jsxs3(View4, { style: [styles4.header, { borderColor: p.border }], children: [
3431
- v.kind !== "tab" ? /* @__PURE__ */ jsx4(Pressable4, { accessibilityRole: "button", accessibilityLabel: "Back", onPress: () => store.back(), hitSlop: 10, children: /* @__PURE__ */ jsx4(Text4, { style: [styles4.back, { color: p.primary }], children: "\u2039 Back" }) }) : null,
3432
- /* @__PURE__ */ jsx4(Text4, { accessibilityRole: "header", numberOfLines: 1, style: [styles4.title, { color: p.text, fontFamily: p.fontFamily }], children: title }),
3433
- /* @__PURE__ */ jsx4(CloseButton, { palette: p, onPress: () => store.close(), label: `Close ${snap.config.name}` })
3626
+ v.kind !== "tab" ? /* @__PURE__ */ jsx5(Pressable4, { accessibilityRole: "button", accessibilityLabel: "Back", onPress: () => store.back(), hitSlop: 10, children: /* @__PURE__ */ jsx5(Text4, { style: [styles4.back, { color: p.primary }], children: "\u2039 Back" }) }) : null,
3627
+ /* @__PURE__ */ jsx5(Text4, { accessibilityRole: "header", numberOfLines: 1, style: [styles4.title, { color: p.text, fontFamily: p.fontFamily }], children: title }),
3628
+ /* @__PURE__ */ jsx5(CloseButton, { palette: p, onPress: () => store.close(), label: `Close ${snap.config.name}` })
3434
3629
  ] }),
3435
- /* @__PURE__ */ jsx4(View4, { style: styles4.flex, children: v.kind === "thread" ? /* @__PURE__ */ jsx4(Thread, { store, palette: p, bottomInset: keyboard ? 0 : bottomInset }) : v.kind === "article" ? /* @__PURE__ */ jsx4(Article, { article: snap.article, palette: p, client }) : v.tab === "home" ? /* @__PURE__ */ jsx4(Home, { store, palette: p }) : v.tab === "messages" ? /* @__PURE__ */ jsx4(Conversations, { store, palette: p }) : /* @__PURE__ */ jsx4(Help, { store, client, palette: p }) }),
3436
- v.kind === "tab" && !keyboard ? /* @__PURE__ */ jsx4(Tabs, { store, active: v.tab, palette: p, bottomInset }) : null
3630
+ /* @__PURE__ */ jsx5(View4, { style: styles4.flex, children: v.kind === "thread" ? /* @__PURE__ */ jsx5(Thread, { store, palette: p, bottomInset: keyboard ? 0 : bottomInset }) : v.kind === "article" ? /* @__PURE__ */ jsx5(Article, { article: snap.article, palette: p, client }) : v.tab === "home" ? /* @__PURE__ */ jsx5(Home, { store, palette: p }) : v.tab === "messages" ? /* @__PURE__ */ jsx5(Conversations, { store, palette: p }) : /* @__PURE__ */ jsx5(Help, { store, client, palette: p }) }),
3631
+ v.kind === "tab" && !keyboard ? /* @__PURE__ */ jsx5(Tabs, { store, active: v.tab, palette: p, bottomInset }) : null
3437
3632
  ] }) });
3438
3633
  }
3439
3634
  function Tabs({ store, active, palette: p, bottomInset }) {
@@ -3443,32 +3638,32 @@ function Tabs({ store, active, palette: p, bottomInset }) {
3443
3638
  ["messages", "\u2709\uFE0E", "Messages"],
3444
3639
  ["help", "?", "Help"]
3445
3640
  ];
3446
- return /* @__PURE__ */ jsx4(View4, { accessibilityRole: "tablist", style: [styles4.tabs, { borderColor: p.border, paddingBottom: Math.max(8, bottomInset) }], children: tabs.map(([tab, icon, label]) => {
3641
+ return /* @__PURE__ */ jsx5(View4, { accessibilityRole: "tablist", style: [styles4.tabs, { borderColor: p.border, paddingBottom: Math.max(8, bottomInset) }], children: tabs.map(([tab, icon, label]) => {
3447
3642
  const on = tab === active;
3448
3643
  return /* @__PURE__ */ jsxs3(Pressable4, { accessibilityRole: "tab", accessibilityState: { selected: on }, accessibilityLabel: tab === "messages" && unread ? `${label}, ${unread} unread` : label, onPress: () => store.setTab(tab), style: styles4.tab, children: [
3449
- /* @__PURE__ */ jsx4(Text4, { style: [styles4.tabIcon, { color: on ? p.primary : p.muted }], children: icon }),
3450
- /* @__PURE__ */ jsx4(Text4, { style: [styles4.tabLabel, { color: on ? p.primary : p.muted }], children: label }),
3451
- tab === "messages" && unread ? /* @__PURE__ */ jsx4(View4, { style: [styles4.dot, styles4.tabDot] }) : null
3644
+ /* @__PURE__ */ jsx5(Text4, { style: [styles4.tabIcon, { color: on ? p.primary : p.muted }], children: icon }),
3645
+ /* @__PURE__ */ jsx5(Text4, { style: [styles4.tabLabel, { color: on ? p.primary : p.muted }], children: label }),
3646
+ tab === "messages" && unread ? /* @__PURE__ */ jsx5(View4, { style: [styles4.dot, styles4.tabDot] }) : null
3452
3647
  ] }, tab);
3453
3648
  }) });
3454
3649
  }
3455
3650
  function Home({ store, palette: p }) {
3456
3651
  const snap = useMessenger(store);
3457
3652
  const recent = snap.conversations[0];
3458
- const [q, setQ] = useState2("");
3653
+ const [q, setQ] = useState3("");
3459
3654
  return /* @__PURE__ */ jsxs3(ScrollView3, { contentContainerStyle: styles4.content, keyboardShouldPersistTaps: "handled", children: [
3460
3655
  /* @__PURE__ */ jsxs3(View4, { style: [styles4.hero, { backgroundColor: p.primary, borderRadius: p.radius }], children: [
3461
- /* @__PURE__ */ jsx4(Text4, { style: [styles4.heroTitle, { color: p.onPrimary }], children: snap.config.greeting || `Hi! I'm ${snap.config.name}.` }),
3462
- /* @__PURE__ */ jsx4(Text4, { style: [styles4.heroSub, { color: p.onPrimary }], children: snap.config.aiAvailable ? `${snap.config.name} answers right away${snap.config.handoffEnabled ? ", and the team is here when you need a person" : ""}.` : "The team usually replies within a few hours." })
3656
+ /* @__PURE__ */ jsx5(Text4, { style: [styles4.heroTitle, { color: p.onPrimary }], children: snap.config.greeting || `Hi! I'm ${snap.config.name}.` }),
3657
+ /* @__PURE__ */ jsx5(Text4, { style: [styles4.heroSub, { color: p.onPrimary }], children: snap.config.aiAvailable ? `${snap.config.name} answers right away${snap.config.handoffEnabled ? ", and the team is here when you need a person" : ""}.` : "The team usually replies within a few hours." })
3463
3658
  ] }),
3464
- /* @__PURE__ */ jsx4(GButton, { label: "Send us a message", palette: p, onPress: () => store.showNewMessage(), style: styles4.fullWidth }),
3659
+ /* @__PURE__ */ jsx5(GButton, { label: "Send us a message", palette: p, onPress: () => store.showNewMessage(), style: styles4.fullWidth }),
3465
3660
  recent ? /* @__PURE__ */ jsxs3(View4, { style: styles4.section, children: [
3466
- /* @__PURE__ */ jsx4(Text4, { style: [styles4.sectionTitle, { color: p.muted }], children: "RECENT" }),
3467
- /* @__PURE__ */ jsx4(ConversationRow, { c: recent, palette: p, onPress: () => void store.openConversation(recent.id) })
3661
+ /* @__PURE__ */ jsx5(Text4, { style: [styles4.sectionTitle, { color: p.muted }], children: "RECENT" }),
3662
+ /* @__PURE__ */ jsx5(ConversationRow, { c: recent, palette: p, onPress: () => void store.openConversation(recent.id) })
3468
3663
  ] }) : null,
3469
3664
  /* @__PURE__ */ jsxs3(View4, { style: styles4.section, children: [
3470
- /* @__PURE__ */ jsx4(Text4, { style: [styles4.sectionTitle, { color: p.muted }], children: "SEARCH FOR HELP" }),
3471
- /* @__PURE__ */ jsx4(
3665
+ /* @__PURE__ */ jsx5(Text4, { style: [styles4.sectionTitle, { color: p.muted }], children: "SEARCH FOR HELP" }),
3666
+ /* @__PURE__ */ jsx5(
3472
3667
  TextInput,
3473
3668
  {
3474
3669
  value: q,
@@ -3491,35 +3686,35 @@ var pendingSearch = { current: "" };
3491
3686
  function ConversationRow({ c, palette: p, onPress }) {
3492
3687
  return /* @__PURE__ */ jsxs3(Pressable4, { accessibilityRole: "button", accessibilityLabel: `${c.subject || "Conversation"}${c.unread ? ", unread" : ""}`, onPress, style: ({ pressed }) => [styles4.row, { borderColor: p.border, opacity: pressed ? 0.7 : 1 }], children: [
3493
3688
  /* @__PURE__ */ jsxs3(View4, { style: styles4.flex, children: [
3494
- /* @__PURE__ */ jsx4(Text4, { numberOfLines: 1, style: [styles4.rowTitle, { color: p.text }], children: c.subject || "Conversation" }),
3495
- /* @__PURE__ */ jsx4(Text4, { numberOfLines: 1, style: [styles4.rowSub, { color: p.muted }], children: c.lastMessagePreview || (c.status === "closed" ? "Closed" : " ") })
3689
+ /* @__PURE__ */ jsx5(Text4, { numberOfLines: 1, style: [styles4.rowTitle, { color: p.text }], children: c.subject || "Conversation" }),
3690
+ /* @__PURE__ */ jsx5(Text4, { numberOfLines: 1, style: [styles4.rowSub, { color: p.muted }], children: c.lastMessagePreview || (c.status === "closed" ? "Closed" : " ") })
3496
3691
  ] }),
3497
3692
  /* @__PURE__ */ jsxs3(View4, { style: styles4.rowEnd, children: [
3498
- /* @__PURE__ */ jsx4(Text4, { style: [styles4.time, { color: p.muted }], children: ago(c.lastMessageAt) }),
3499
- c.unread ? /* @__PURE__ */ jsx4(View4, { style: [styles4.dot, { backgroundColor: p.primary }] }) : null
3693
+ /* @__PURE__ */ jsx5(Text4, { style: [styles4.time, { color: p.muted }], children: ago(c.lastMessageAt) }),
3694
+ c.unread ? /* @__PURE__ */ jsx5(View4, { style: [styles4.dot, { backgroundColor: p.primary }] }) : null
3500
3695
  ] })
3501
3696
  ] });
3502
3697
  }
3503
3698
  function Conversations({ store, palette: p }) {
3504
3699
  const snap = useMessenger(store);
3505
3700
  return /* @__PURE__ */ jsxs3(ScrollView3, { contentContainerStyle: styles4.content, children: [
3506
- snap.conversations.length ? snap.conversations.map((c) => /* @__PURE__ */ jsx4(ConversationRow, { c, palette: p, onPress: () => void store.openConversation(c.id) }, c.id)) : /* @__PURE__ */ jsx4(Text4, { style: [styles4.empty, { color: p.muted }], children: "No conversations yet." }),
3507
- /* @__PURE__ */ jsx4(GButton, { label: "New conversation", palette: p, onPress: () => store.showNewMessage(), style: styles4.fullWidth })
3701
+ snap.conversations.length ? snap.conversations.map((c) => /* @__PURE__ */ jsx5(ConversationRow, { c, palette: p, onPress: () => void store.openConversation(c.id) }, c.id)) : /* @__PURE__ */ jsx5(Text4, { style: [styles4.empty, { color: p.muted }], children: "No conversations yet." }),
3702
+ /* @__PURE__ */ jsx5(GButton, { label: "New conversation", palette: p, onPress: () => store.showNewMessage(), style: styles4.fullWidth })
3508
3703
  ] });
3509
3704
  }
3510
3705
  function Bubble({ m, text, name, palette: p, onArticle, onGuide }) {
3511
3706
  if (m.author === "system")
3512
- return /* @__PURE__ */ jsx4(Text4, { accessibilityRole: "text", style: [styles4.system, { color: p.muted }], children: text });
3707
+ return /* @__PURE__ */ jsx5(Text4, { accessibilityRole: "text", style: [styles4.system, { color: p.muted }], children: text });
3513
3708
  const mine = m.author === "user";
3514
3709
  return /* @__PURE__ */ jsxs3(View4, { style: [styles4.bubbleWrap, mine ? styles4.mine : styles4.theirs], children: [
3515
- !mine ? /* @__PURE__ */ jsx4(Text4, { style: [styles4.author, { color: p.muted }], children: m.author === "agent" ? m.authorName || "Team" : name }) : null,
3516
- /* @__PURE__ */ jsx4(View4, { style: [styles4.bubble, { borderRadius: Math.max(12, p.radius) }, mine ? { backgroundColor: p.primary } : { backgroundColor: p.surface }], children: m.status === "pending" && !text ? /* @__PURE__ */ jsx4(TypingDots, { color: p.muted }) : /* @__PURE__ */ jsx4(Text4, { style: [styles4.bubbleText, { color: mine ? p.onPrimary : p.text }], children: text }) }),
3710
+ !mine ? /* @__PURE__ */ jsx5(Text4, { style: [styles4.author, { color: p.muted }], children: m.author === "agent" ? m.authorName || "Team" : name }) : null,
3711
+ /* @__PURE__ */ jsx5(View4, { style: [styles4.bubble, { borderRadius: Math.max(12, p.radius) }, mine ? { backgroundColor: p.primary } : { backgroundColor: p.surface }], children: m.status === "pending" && !text ? /* @__PURE__ */ jsx5(TypingDots, { color: p.muted }) : /* @__PURE__ */ jsx5(Text4, { style: [styles4.bubbleText, { color: mine ? p.onPrimary : p.text }], children: text }) }),
3517
3712
  m.citations.length || m.suggestedGuides.length ? /* @__PURE__ */ jsxs3(View4, { style: styles4.chips, children: [
3518
- m.citations.map((c) => /* @__PURE__ */ jsx4(Pressable4, { accessibilityRole: "link", accessibilityLabel: `Article: ${c.title}`, onPress: () => onArticle(c.articleId), style: [styles4.chip, { borderColor: p.border }], children: /* @__PURE__ */ jsxs3(Text4, { style: [styles4.chipText, { color: p.text }], children: [
3713
+ m.citations.map((c) => /* @__PURE__ */ jsx5(Pressable4, { accessibilityRole: "link", accessibilityLabel: `Article: ${c.title}`, onPress: () => onArticle(c.articleId), style: [styles4.chip, { borderColor: p.border }], children: /* @__PURE__ */ jsxs3(Text4, { style: [styles4.chipText, { color: p.text }], children: [
3519
3714
  "\u{1F4C4} ",
3520
3715
  c.title
3521
3716
  ] }) }, c.articleId)),
3522
- m.suggestedGuides.map((g) => /* @__PURE__ */ jsx4(Pressable4, { accessibilityRole: "button", accessibilityLabel: `Show me: ${g.name}`, onPress: () => onGuide(g.key), style: [styles4.chip, { borderColor: p.primary }], children: /* @__PURE__ */ jsxs3(Text4, { style: [styles4.chipText, { color: p.primary }], children: [
3717
+ m.suggestedGuides.map((g) => /* @__PURE__ */ jsx5(Pressable4, { accessibilityRole: "button", accessibilityLabel: `Show me: ${g.name}`, onPress: () => onGuide(g.key), style: [styles4.chip, { borderColor: p.primary }], children: /* @__PURE__ */ jsxs3(Text4, { style: [styles4.chipText, { color: p.primary }], children: [
3523
3718
  "\u25B6 Show me: ",
3524
3719
  g.name
3525
3720
  ] }) }, g.key))
@@ -3527,18 +3722,18 @@ function Bubble({ m, text, name, palette: p, onArticle, onGuide }) {
3527
3722
  ] });
3528
3723
  }
3529
3724
  function TypingDots({ color }) {
3530
- return /* @__PURE__ */ jsx4(ActivityIndicator, { size: "small", color, accessibilityLabel: "Typing" });
3725
+ return /* @__PURE__ */ jsx5(ActivityIndicator, { size: "small", color, accessibilityLabel: "Typing" });
3531
3726
  }
3532
3727
  function Thread({ store, palette: p, bottomInset }) {
3533
3728
  const snap = useMessenger(store);
3534
3729
  const t = snap.thread;
3535
- const [text, setText] = useState2(t?.draft ?? "");
3536
- const scroll = useRef(null);
3730
+ const [text, setText] = useState3(t?.draft ?? "");
3731
+ const scroll = useRef2(null);
3537
3732
  const convo = t?.conversation ?? null;
3538
- useEffect2(() => {
3733
+ useEffect3(() => {
3539
3734
  if (t?.draft) setText(t.draft);
3540
3735
  }, [t?.draft]);
3541
- const streamingIds = useMemo2(() => Object.keys(t?.streaming ?? {}).filter((id) => !t?.messages.some((m) => m.id === id)), [t]);
3736
+ const streamingIds = useMemo3(() => Object.keys(t?.streaming ?? {}).filter((id) => !t?.messages.some((m) => m.id === id)), [t]);
3542
3737
  if (!t) return null;
3543
3738
  const send = () => {
3544
3739
  const value = text;
@@ -3548,19 +3743,19 @@ function Thread({ store, palette: p, bottomInset }) {
3548
3743
  const canHandoff = snap.config.handoffEnabled && snap.config.aiAvailable && convo?.status === "bot" && !t.sending;
3549
3744
  return /* @__PURE__ */ jsxs3(View4, { style: styles4.flex, children: [
3550
3745
  /* @__PURE__ */ jsxs3(ScrollView3, { ref: scroll, contentContainerStyle: styles4.thread, onContentSizeChange: () => scroll.current?.scrollToEnd({ animated: true }), keyboardShouldPersistTaps: "handled", children: [
3551
- !t.messages.length && !t.sending ? /* @__PURE__ */ jsx4(Text4, { style: [styles4.empty, { color: p.muted }], children: snap.config.greeting || "Ask us anything." }) : null,
3552
- t.messages.map((m) => /* @__PURE__ */ jsx4(Bubble, { m, text: t.streaming[m.id] ?? m.body, name: snap.config.name, palette: p, onArticle: (id) => void store.openArticle(id), onGuide: (k) => store.startGuide(k) }, m.id)),
3553
- streamingIds.map((id) => /* @__PURE__ */ jsx4(Bubble, { m: { id, author: "scout", body: "", citations: [], suggestedGuides: [], status: "pending", createdAt: (/* @__PURE__ */ new Date()).toISOString() }, text: t.streaming[id] ?? "", name: snap.config.name, palette: p, onArticle: () => {
3746
+ !t.messages.length && !t.sending ? /* @__PURE__ */ jsx5(Text4, { style: [styles4.empty, { color: p.muted }], children: snap.config.greeting || "Ask us anything." }) : null,
3747
+ t.messages.map((m) => /* @__PURE__ */ jsx5(Bubble, { m, text: t.streaming[m.id] ?? m.body, name: snap.config.name, palette: p, onArticle: (id) => void store.openArticle(id), onGuide: (k) => store.startGuide(k) }, m.id)),
3748
+ streamingIds.map((id) => /* @__PURE__ */ jsx5(Bubble, { m: { id, author: "scout", body: "", citations: [], suggestedGuides: [], status: "pending", createdAt: (/* @__PURE__ */ new Date()).toISOString() }, text: t.streaming[id] ?? "", name: snap.config.name, palette: p, onArticle: () => {
3554
3749
  }, onGuide: () => {
3555
3750
  } }, id)),
3556
- t.sending && !streamingIds.length && snap.config.aiAvailable && t.messages.length ? /* @__PURE__ */ jsx4(TypingDots, { color: p.muted }) : null,
3557
- t.error ? /* @__PURE__ */ jsx4(Text4, { accessibilityLiveRegion: "polite", style: styles4.error, children: t.error }) : null,
3558
- convo?.status === "closed" && convo.rating == null ? /* @__PURE__ */ jsx4(Rating, { palette: p, onRate: (n) => void store.rate(n) }) : null,
3559
- convo?.status === "closed" && convo.rating != null ? /* @__PURE__ */ jsx4(Text4, { style: [styles4.system, { color: p.muted }], children: "Thanks for rating this conversation." }) : null
3751
+ t.sending && !streamingIds.length && snap.config.aiAvailable && t.messages.length ? /* @__PURE__ */ jsx5(TypingDots, { color: p.muted }) : null,
3752
+ t.error ? /* @__PURE__ */ jsx5(Text4, { accessibilityLiveRegion: "polite", style: styles4.error, children: t.error }) : null,
3753
+ convo?.status === "closed" && convo.rating == null ? /* @__PURE__ */ jsx5(Rating, { palette: p, onRate: (n) => void store.rate(n) }) : null,
3754
+ convo?.status === "closed" && convo.rating != null ? /* @__PURE__ */ jsx5(Text4, { style: [styles4.system, { color: p.muted }], children: "Thanks for rating this conversation." }) : null
3560
3755
  ] }),
3561
- canHandoff ? /* @__PURE__ */ jsx4(GButton, { label: "Talk to a person", variant: "secondary", palette: p, onPress: () => void store.handoff(), style: styles4.handoff }) : null,
3756
+ canHandoff ? /* @__PURE__ */ jsx5(GButton, { label: "Talk to a person", variant: "secondary", palette: p, onPress: () => void store.handoff(), style: styles4.handoff }) : null,
3562
3757
  /* @__PURE__ */ jsxs3(View4, { style: [styles4.composer, { borderColor: p.border, paddingBottom: Math.max(10, bottomInset) }], children: [
3563
- /* @__PURE__ */ jsx4(
3758
+ /* @__PURE__ */ jsx5(
3564
3759
  TextInput,
3565
3760
  {
3566
3761
  value: text,
@@ -3572,26 +3767,26 @@ function Thread({ store, palette: p, bottomInset }) {
3572
3767
  style: [styles4.input, { color: p.text, backgroundColor: p.surface, borderRadius: Math.min(18, p.radius + 6) }]
3573
3768
  }
3574
3769
  ),
3575
- /* @__PURE__ */ jsx4(GButton, { label: "Send", palette: p, onPress: send, disabled: !text.trim() || t.sending })
3770
+ /* @__PURE__ */ jsx5(GButton, { label: "Send", palette: p, onPress: send, disabled: !text.trim() || t.sending })
3576
3771
  ] })
3577
3772
  ] });
3578
3773
  }
3579
3774
  function Rating({ palette: p, onRate }) {
3580
3775
  return /* @__PURE__ */ jsxs3(View4, { style: styles4.rating, children: [
3581
- /* @__PURE__ */ jsx4(Text4, { style: [styles4.ratingTitle, { color: p.text }], children: "How did we do?" }),
3582
- /* @__PURE__ */ jsx4(View4, { accessibilityRole: "radiogroup", style: styles4.stars, children: [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ jsx4(Pressable4, { accessibilityRole: "radio", accessibilityLabel: `${n} of 5`, onPress: () => onRate(n), hitSlop: 6, children: /* @__PURE__ */ jsx4(Text4, { style: [styles4.star, { color: p.primary }], children: "\u2605" }) }, n)) })
3776
+ /* @__PURE__ */ jsx5(Text4, { style: [styles4.ratingTitle, { color: p.text }], children: "How did we do?" }),
3777
+ /* @__PURE__ */ jsx5(View4, { accessibilityRole: "radiogroup", style: styles4.stars, children: [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ jsx5(Pressable4, { accessibilityRole: "radio", accessibilityLabel: `${n} of 5`, onPress: () => onRate(n), hitSlop: 6, children: /* @__PURE__ */ jsx5(Text4, { style: [styles4.star, { color: p.primary }], children: "\u2605" }) }, n)) })
3583
3778
  ] });
3584
3779
  }
3585
3780
  function Help({ store, client, palette: p }) {
3586
3781
  const snap = useMessenger(store);
3587
3782
  const rc = client.getResourceCenter();
3588
- const [q, setQ] = useState2(pendingSearch.current);
3589
- const [results, setResults] = useState2(null);
3590
- const [faq, setFaq] = useState2(null);
3591
- useEffect2(() => {
3783
+ const [q, setQ] = useState3(pendingSearch.current);
3784
+ const [results, setResults] = useState3(null);
3785
+ const [faq, setFaq] = useState3(null);
3786
+ useEffect3(() => {
3592
3787
  pendingSearch.current = "";
3593
3788
  }, []);
3594
- useEffect2(() => {
3789
+ useEffect3(() => {
3595
3790
  const query = q.trim();
3596
3791
  if (query.length < 2) return setResults(null);
3597
3792
  let alive = true;
@@ -3606,33 +3801,33 @@ function Help({ store, client, palette: p }) {
3606
3801
  const articles = snap.articles;
3607
3802
  const row = (key, title, sub, onPress, icon = "\u203A") => /* @__PURE__ */ jsxs3(Pressable4, { accessibilityRole: "button", accessibilityLabel: title, onPress, style: ({ pressed }) => [styles4.row, { borderColor: p.border, opacity: pressed ? 0.7 : 1 }], children: [
3608
3803
  /* @__PURE__ */ jsxs3(View4, { style: styles4.flex, children: [
3609
- /* @__PURE__ */ jsx4(Text4, { style: [styles4.rowTitle, { color: p.text }], children: title }),
3610
- sub ? /* @__PURE__ */ jsx4(Text4, { numberOfLines: 2, style: [styles4.rowSub, { color: p.muted }], children: sub }) : null
3804
+ /* @__PURE__ */ jsx5(Text4, { style: [styles4.rowTitle, { color: p.text }], children: title }),
3805
+ sub ? /* @__PURE__ */ jsx5(Text4, { numberOfLines: 2, style: [styles4.rowSub, { color: p.muted }], children: sub }) : null
3611
3806
  ] }),
3612
- /* @__PURE__ */ jsx4(Text4, { style: [styles4.chev, { color: p.muted }], children: icon })
3807
+ /* @__PURE__ */ jsx5(Text4, { style: [styles4.chev, { color: p.muted }], children: icon })
3613
3808
  ] }, key);
3614
3809
  const open = (a) => a.externalUrl ? client.navigate(a.externalUrl) : void store.openArticle(a.id);
3615
3810
  const section = (title, children) => /* @__PURE__ */ jsxs3(View4, { style: styles4.section, children: [
3616
- /* @__PURE__ */ jsx4(Text4, { accessibilityRole: "header", style: [styles4.sectionTitle, { color: p.muted }], children: title.toUpperCase() }),
3811
+ /* @__PURE__ */ jsx5(Text4, { accessibilityRole: "header", style: [styles4.sectionTitle, { color: p.muted }], children: title.toUpperCase() }),
3617
3812
  children
3618
3813
  ] }, title);
3619
3814
  const audiences = client.getAudienceIds();
3620
3815
  const sections = resourceCenterSections(articles?.collections ?? [], articles?.articles ?? [], audiences);
3621
3816
  const listed = results?.filter((a) => isArticleListed(articles?.collections ?? [], a, audiences));
3622
3817
  return /* @__PURE__ */ jsxs3(ScrollView3, { contentContainerStyle: styles4.content, keyboardShouldPersistTaps: "handled", children: [
3623
- /* @__PURE__ */ jsx4(TextInput, { value: q, onChangeText: setQ, placeholder: "Search articles", placeholderTextColor: p.muted, accessibilityLabel: "Search help articles", returnKeyType: "search", style: [styles4.search, { color: p.text, backgroundColor: p.surface, borderRadius: Math.min(10, p.radius) }] }),
3624
- listed ? section(`Results for \u201C${q.trim()}\u201D`, listed.length ? listed.map((a) => row(a.id, a.title, a.summary, () => open(a))) : /* @__PURE__ */ jsx4(Text4, { style: [styles4.rowSub, { color: p.muted }], children: "No articles found." })) : /* @__PURE__ */ jsxs3(Fragment, { children: [
3818
+ /* @__PURE__ */ jsx5(TextInput, { value: q, onChangeText: setQ, placeholder: "Search articles", placeholderTextColor: p.muted, accessibilityLabel: "Search help articles", returnKeyType: "search", style: [styles4.search, { color: p.text, backgroundColor: p.surface, borderRadius: Math.min(10, p.radius) }] }),
3819
+ listed ? section(`Results for \u201C${q.trim()}\u201D`, listed.length ? listed.map((a) => row(a.id, a.title, a.summary, () => open(a))) : /* @__PURE__ */ jsx5(Text4, { style: [styles4.rowSub, { color: p.muted }], children: "No articles found." })) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
3625
3820
  recommended.length ? section("Recommended", recommended.map((g) => row(g.id, g.name, void 0, () => store.startGuide(g.key), "\u25B6"))) : null,
3626
- !articles ? /* @__PURE__ */ jsx4(ActivityIndicator, { color: p.muted }) : null,
3821
+ !articles ? /* @__PURE__ */ jsx5(ActivityIndicator, { color: p.muted }) : null,
3627
3822
  sections.map((s) => section(s.collection ? s.path.join(" \u203A ") : "Articles", s.articles.map((a) => row(a.id, a.title, a.summary, () => open(a))))),
3628
3823
  rc?.faqs.length ? section(
3629
3824
  "FAQ",
3630
3825
  rc.faqs.map((f) => /* @__PURE__ */ jsxs3(View4, { style: [styles4.row, styles4.faq, { borderColor: p.border }], children: [
3631
3826
  /* @__PURE__ */ jsxs3(Pressable4, { accessibilityRole: "button", accessibilityState: { expanded: faq === f.id }, onPress: () => setFaq(faq === f.id ? null : f.id), style: styles4.faqHead, children: [
3632
- /* @__PURE__ */ jsx4(Text4, { style: [styles4.rowTitle, styles4.flex, { color: p.text }], children: f.question }),
3633
- /* @__PURE__ */ jsx4(Text4, { style: [styles4.chev, { color: p.muted }], children: faq === f.id ? "\u2212" : "+" })
3827
+ /* @__PURE__ */ jsx5(Text4, { style: [styles4.rowTitle, styles4.flex, { color: p.text }], children: f.question }),
3828
+ /* @__PURE__ */ jsx5(Text4, { style: [styles4.chev, { color: p.muted }], children: faq === f.id ? "\u2212" : "+" })
3634
3829
  ] }),
3635
- faq === f.id ? /* @__PURE__ */ jsx4(RichText, { html: f.answer, palette: p, onLink: (u) => client.navigate(u), size: 14 }) : null
3830
+ faq === f.id ? /* @__PURE__ */ jsx5(RichText, { html: f.answer, palette: p, onLink: (u) => client.navigate(u), size: 14 }) : null
3636
3831
  ] }, f.id))
3637
3832
  ) : null,
3638
3833
  rc?.links.length ? section("Links", rc.links.map((l) => row(l.id, l.label, void 0, () => client.navigate(l.url), "\u2197"))) : null
@@ -3640,10 +3835,10 @@ function Help({ store, client, palette: p }) {
3640
3835
  ] });
3641
3836
  }
3642
3837
  function Article({ article, palette: p, client }) {
3643
- if (!article) return /* @__PURE__ */ jsx4(ActivityIndicator, { style: styles4.loading, color: p.muted });
3838
+ if (!article) return /* @__PURE__ */ jsx5(ActivityIndicator, { style: styles4.loading, color: p.muted });
3644
3839
  return /* @__PURE__ */ jsxs3(ScrollView3, { contentContainerStyle: styles4.content, children: [
3645
- /* @__PURE__ */ jsx4(Text4, { accessibilityRole: "header", style: [styles4.articleTitle, { color: p.text, fontFamily: p.fontFamily }], children: article.title }),
3646
- /* @__PURE__ */ jsx4(RichText, { html: article.body, palette: p, onLink: (u) => client.navigate(u) })
3840
+ /* @__PURE__ */ jsx5(Text4, { accessibilityRole: "header", style: [styles4.articleTitle, { color: p.text, fontFamily: p.fontFamily }], children: article.title }),
3841
+ /* @__PURE__ */ jsx5(RichText, { html: article.body, palette: p, onLink: (u) => client.navigate(u) })
3647
3842
  ] });
3648
3843
  }
3649
3844
  var styles4 = StyleSheet4.create({
@@ -3703,23 +3898,23 @@ var styles4 = StyleSheet4.create({
3703
3898
  });
3704
3899
 
3705
3900
  // src/ui/ResourceCenter.tsx
3706
- import { useEffect as useEffect3, useMemo as useMemo3, useState as useState3 } from "react";
3901
+ import { useEffect as useEffect4, useMemo as useMemo4, useState as useState4 } from "react";
3707
3902
  import { Modal as Modal3, Platform as Platform3, Pressable as Pressable5, ScrollView as ScrollView4, StyleSheet as StyleSheet5, Text as Text5, TextInput as TextInput2, View as View5 } from "react-native";
3708
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
3903
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
3709
3904
  function ResourceCenter({ client, palette: p, open, topInset, bottomInset }) {
3710
3905
  const rc = client.getResourceCenter();
3711
- const [query, setQuery] = useState3("");
3712
- const [results, setResults] = useState3(null);
3713
- const [article, setArticle] = useState3(null);
3714
- const [faq, setFaq] = useState3(null);
3715
- useEffect3(() => {
3906
+ const [query, setQuery] = useState4("");
3907
+ const [results, setResults] = useState4(null);
3908
+ const [article, setArticle] = useState4(null);
3909
+ const [faq, setFaq] = useState4(null);
3910
+ useEffect4(() => {
3716
3911
  if (!open) {
3717
3912
  setQuery("");
3718
3913
  setResults(null);
3719
3914
  setArticle(null);
3720
3915
  }
3721
3916
  }, [open]);
3722
- useEffect3(() => {
3917
+ useEffect4(() => {
3723
3918
  const q = query.trim();
3724
3919
  if (q.length < 2) {
3725
3920
  setResults(null);
@@ -3735,8 +3930,8 @@ function ResourceCenter({ client, palette: p, open, topInset, bottomInset }) {
3735
3930
  };
3736
3931
  }, [client, query]);
3737
3932
  const guides = client.getGuides();
3738
- const recommended = useMemo3(() => (rc?.recommendedGuideIds ?? []).map((id) => guides.find((g) => g.id === id || g.key === id)).filter((g) => !!g && g.kind !== "checklist"), [rc, guides]);
3739
- const checklists = useMemo3(() => (rc?.checklistGuideIds ?? []).map((id) => guides.find((g) => g.id === id || g.key === id)).filter((g) => !!g && g.kind === "checklist"), [rc, guides]);
3933
+ const recommended = useMemo4(() => (rc?.recommendedGuideIds ?? []).map((id) => guides.find((g) => g.id === id || g.key === id)).filter((g) => !!g && g.kind !== "checklist"), [rc, guides]);
3934
+ const checklists = useMemo4(() => (rc?.checklistGuideIds ?? []).map((id) => guides.find((g) => g.id === id || g.key === id)).filter((g) => !!g && g.kind === "checklist"), [rc, guides]);
3740
3935
  if (!rc) return null;
3741
3936
  const close = () => client.closeResourceCenter();
3742
3937
  const openArticle = (a) => {
@@ -3746,29 +3941,29 @@ function ResourceCenter({ client, palette: p, open, topInset, bottomInset }) {
3746
3941
  };
3747
3942
  const row = (key, title, subtitle, onPress, icon = "\u203A") => /* @__PURE__ */ jsxs4(Pressable5, { accessibilityRole: "button", accessibilityLabel: title, onPress, style: ({ pressed }) => [styles5.row, { borderColor: p.border, opacity: pressed ? 0.7 : 1 }], children: [
3748
3943
  /* @__PURE__ */ jsxs4(View5, { style: styles5.flex, children: [
3749
- /* @__PURE__ */ jsx5(Text5, { style: [styles5.rowTitle, { color: p.text, fontFamily: p.fontFamily }], children: title }),
3750
- subtitle ? /* @__PURE__ */ jsx5(Text5, { style: [styles5.rowSub, { color: p.muted }], numberOfLines: 2, children: subtitle }) : null
3944
+ /* @__PURE__ */ jsx6(Text5, { style: [styles5.rowTitle, { color: p.text, fontFamily: p.fontFamily }], children: title }),
3945
+ subtitle ? /* @__PURE__ */ jsx6(Text5, { style: [styles5.rowSub, { color: p.muted }], numberOfLines: 2, children: subtitle }) : null
3751
3946
  ] }),
3752
- /* @__PURE__ */ jsx5(Text5, { style: [styles5.chev, { color: p.muted }], children: icon })
3947
+ /* @__PURE__ */ jsx6(Text5, { style: [styles5.chev, { color: p.muted }], children: icon })
3753
3948
  ] }, key);
3754
3949
  const section = (title, children) => /* @__PURE__ */ jsxs4(View5, { style: styles5.section, children: [
3755
- /* @__PURE__ */ jsx5(Text5, { accessibilityRole: "header", style: [styles5.sectionTitle, { color: p.muted }], children: title.toUpperCase() }),
3950
+ /* @__PURE__ */ jsx6(Text5, { accessibilityRole: "header", style: [styles5.sectionTitle, { color: p.muted }], children: title.toUpperCase() }),
3756
3951
  children
3757
3952
  ] }, title);
3758
3953
  const audiences = client.getAudienceIds();
3759
3954
  const sections = resourceCenterSections(rc.collections, rc.articles, audiences);
3760
3955
  const listed = results?.filter((a) => isArticleListed(rc.collections, a, audiences));
3761
- return /* @__PURE__ */ jsx5(Modal3, { visible: open, animationType: "slide", presentationStyle: Platform3.OS === "ios" ? "pageSheet" : "fullScreen", onRequestClose: () => article ? setArticle(null) : close(), children: /* @__PURE__ */ jsxs4(View5, { style: [styles5.sheet, { backgroundColor: p.background, paddingTop: Platform3.OS === "ios" ? 8 : topInset }], children: [
3956
+ return /* @__PURE__ */ jsx6(Modal3, { visible: open, animationType: "slide", presentationStyle: Platform3.OS === "ios" ? "pageSheet" : "fullScreen", onRequestClose: () => article ? setArticle(null) : close(), children: /* @__PURE__ */ jsxs4(View5, { style: [styles5.sheet, { backgroundColor: p.background, paddingTop: Platform3.OS === "ios" ? 8 : topInset }], children: [
3762
3957
  /* @__PURE__ */ jsxs4(View5, { style: [styles5.header, { borderColor: p.border }], children: [
3763
- article ? /* @__PURE__ */ jsx5(Pressable5, { accessibilityRole: "button", accessibilityLabel: "Back", onPress: () => setArticle(null), hitSlop: 10, children: /* @__PURE__ */ jsx5(Text5, { style: [styles5.back, { color: p.primary }], children: "\u2039 Back" }) }) : null,
3764
- /* @__PURE__ */ jsx5(Text5, { accessibilityRole: "header", style: [styles5.title, { color: p.text, fontFamily: p.fontFamily }], numberOfLines: 1, children: article ? "" : rc.title }),
3765
- /* @__PURE__ */ jsx5(CloseButton, { palette: p, onPress: close, label: "Close help" })
3958
+ article ? /* @__PURE__ */ jsx6(Pressable5, { accessibilityRole: "button", accessibilityLabel: "Back", onPress: () => setArticle(null), hitSlop: 10, children: /* @__PURE__ */ jsx6(Text5, { style: [styles5.back, { color: p.primary }], children: "\u2039 Back" }) }) : null,
3959
+ /* @__PURE__ */ jsx6(Text5, { accessibilityRole: "header", style: [styles5.title, { color: p.text, fontFamily: p.fontFamily }], numberOfLines: 1, children: article ? "" : rc.title }),
3960
+ /* @__PURE__ */ jsx6(CloseButton, { palette: p, onPress: close, label: "Close help" })
3766
3961
  ] }),
3767
3962
  article ? /* @__PURE__ */ jsxs4(ScrollView4, { contentContainerStyle: [styles5.content, { paddingBottom: bottomInset + 24 }], children: [
3768
- /* @__PURE__ */ jsx5(Text5, { accessibilityRole: "header", style: [styles5.articleTitle, { color: p.text, fontFamily: p.fontFamily }], children: article.title }),
3769
- /* @__PURE__ */ jsx5(RichText, { html: article.body, palette: p, onLink: (u) => client.navigate(u) })
3963
+ /* @__PURE__ */ jsx6(Text5, { accessibilityRole: "header", style: [styles5.articleTitle, { color: p.text, fontFamily: p.fontFamily }], children: article.title }),
3964
+ /* @__PURE__ */ jsx6(RichText, { html: article.body, palette: p, onLink: (u) => client.navigate(u) })
3770
3965
  ] }) : /* @__PURE__ */ jsxs4(ScrollView4, { contentContainerStyle: [styles5.content, { paddingBottom: bottomInset + 24 }], keyboardShouldPersistTaps: "handled", children: [
3771
- /* @__PURE__ */ jsx5(
3966
+ /* @__PURE__ */ jsx6(
3772
3967
  TextInput2,
3773
3968
  {
3774
3969
  value: query,
@@ -3783,8 +3978,8 @@ function ResourceCenter({ client, palette: p, open, topInset, bottomInset }) {
3783
3978
  ),
3784
3979
  listed ? section(
3785
3980
  `Results for \u201C${query.trim()}\u201D`,
3786
- listed.length ? listed.map((a) => row(a.id, a.title, a.summary, () => openArticle(a))) : /* @__PURE__ */ jsx5(Text5, { style: [styles5.rowSub, { color: p.muted }], children: "No articles found." })
3787
- ) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
3981
+ listed.length ? listed.map((a) => row(a.id, a.title, a.summary, () => openArticle(a))) : /* @__PURE__ */ jsx6(Text5, { style: [styles5.rowSub, { color: p.muted }], children: "No articles found." })
3982
+ ) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
3788
3983
  recommended.length ? section("Recommended", recommended.map((g) => row(g.id, g.name, void 0, () => (close(), client.startGuide(g.id)), "\u25B6"))) : null,
3789
3984
  checklists.length ? section("Checklists", checklists.map((g) => row(g.id, g.name, void 0, () => (close(), client.openChecklist(g.id)), "\u2611"))) : null,
3790
3985
  sections.map((s) => section(s.collection ? s.path.join(" \u203A ") : "Articles", s.articles.map((a) => row(a.id, a.title, a.summary, () => openArticle(a))))),
@@ -3792,10 +3987,10 @@ function ResourceCenter({ client, palette: p, open, topInset, bottomInset }) {
3792
3987
  "FAQ",
3793
3988
  rc.faqs.map((f) => /* @__PURE__ */ jsxs4(View5, { style: [styles5.row, styles5.faq, { borderColor: p.border }], children: [
3794
3989
  /* @__PURE__ */ jsxs4(Pressable5, { accessibilityRole: "button", accessibilityState: { expanded: faq === f.id }, onPress: () => setFaq(faq === f.id ? null : f.id), style: styles5.faqHead, children: [
3795
- /* @__PURE__ */ jsx5(Text5, { style: [styles5.rowTitle, styles5.flex, { color: p.text }], children: f.question }),
3796
- /* @__PURE__ */ jsx5(Text5, { style: [styles5.chev, { color: p.muted }], children: faq === f.id ? "\u2212" : "+" })
3990
+ /* @__PURE__ */ jsx6(Text5, { style: [styles5.rowTitle, styles5.flex, { color: p.text }], children: f.question }),
3991
+ /* @__PURE__ */ jsx6(Text5, { style: [styles5.chev, { color: p.muted }], children: faq === f.id ? "\u2212" : "+" })
3797
3992
  ] }),
3798
- faq === f.id ? /* @__PURE__ */ jsx5(RichText, { html: f.answer, palette: p, onLink: (u) => client.navigate(u), size: 14 }) : null
3993
+ faq === f.id ? /* @__PURE__ */ jsx6(RichText, { html: f.answer, palette: p, onLink: (u) => client.navigate(u), size: 14 }) : null
3799
3994
  ] }, f.id))
3800
3995
  ) : null,
3801
3996
  rc.links.length ? section("Links", rc.links.map((l) => row(l.id, l.label, void 0, () => client.navigate(l.url), "\u2197"))) : null
@@ -3823,7 +4018,7 @@ var styles5 = StyleSheet5.create({
3823
4018
  });
3824
4019
 
3825
4020
  // src/ui/StepLayer.tsx
3826
- import { useEffect as useEffect4, useRef as useRef2, useState as useState4 } from "react";
4021
+ import { useEffect as useEffect5, useRef as useRef3, useState as useState5 } from "react";
3827
4022
  import { AccessibilityInfo, Animated, Easing, Pressable as Pressable6, StyleSheet as StyleSheet7, View as View7 } from "react-native";
3828
4023
 
3829
4024
  // src/core/position.ts
@@ -3865,31 +4060,31 @@ function positionCard(target, size, viewport, insets, placement, offset) {
3865
4060
 
3866
4061
  // src/ui/StepCard.tsx
3867
4062
  import { Image as Image2, StyleSheet as StyleSheet6, Text as Text6, View as View6 } from "react-native";
3868
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
4063
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
3869
4064
  function StepCard({ view, client, palette: p, width, style, compact }) {
3870
4065
  const { step, guide } = view;
3871
4066
  const buttons = visibleButtons(step, view.canGoBack);
3872
4067
  const body = step.type === "custom" ? step.customHtml || step.body : step.body;
3873
4068
  const showProgress = step.showProgress && view.total > 1;
3874
4069
  const onLink = (url) => client.navigate(url);
3875
- return /* @__PURE__ */ jsx6(Surface, { palette: p, style: [{ width }, style], children: /* @__PURE__ */ jsxs5(View6, { accessible: false, accessibilityViewIsModal: view.asModal || step.type === "modal", style: compact ? styles6.compact : styles6.inner, children: [
3876
- step.imageUrl ? /* @__PURE__ */ jsx6(Image2, { source: { uri: step.imageUrl }, style: [styles6.image, { borderRadius: Math.max(0, p.radius - 4) }], resizeMode: "cover", accessibilityIgnoresInvertColors: true }) : null,
4070
+ return /* @__PURE__ */ jsx7(Surface, { palette: p, style: [{ width }, style], children: /* @__PURE__ */ jsxs5(View6, { accessible: false, accessibilityViewIsModal: view.asModal || step.type === "modal", style: compact ? styles6.compact : styles6.inner, children: [
4071
+ step.imageUrl ? /* @__PURE__ */ jsx7(Image2, { source: { uri: step.imageUrl }, style: [styles6.image, { borderRadius: Math.max(0, p.radius - 4) }], resizeMode: "cover", accessibilityIgnoresInvertColors: true }) : null,
3877
4072
  step.title ? /* @__PURE__ */ jsxs5(View6, { style: styles6.header, children: [
3878
- /* @__PURE__ */ jsx6(Text6, { accessibilityRole: "header", style: [styles6.title, { color: p.text, fontFamily: p.fontFamily }], children: step.title }),
3879
- step.showClose ? /* @__PURE__ */ jsx6(CloseButton, { palette: p, onPress: () => client.closeStep(guide.id), label: "Dismiss guide" }) : null
4073
+ /* @__PURE__ */ jsx7(Text6, { accessibilityRole: "header", style: [styles6.title, { color: p.text, fontFamily: p.fontFamily }], children: step.title }),
4074
+ step.showClose ? /* @__PURE__ */ jsx7(CloseButton, { palette: p, onPress: () => client.closeStep(guide.id), label: "Dismiss guide" }) : null
3880
4075
  ] }) : null,
3881
4076
  body ? (
3882
4077
  // Without a title the close button floats beside the body instead of taking a row.
3883
- /* @__PURE__ */ jsx6(View6, { style: !step.title && step.showClose ? styles6.bodyBesideClose : void 0, children: /* @__PURE__ */ jsx6(RichText, { html: body, palette: p, onLink }) })
4078
+ /* @__PURE__ */ jsx7(View6, { style: !step.title && step.showClose ? styles6.bodyBesideClose : void 0, children: /* @__PURE__ */ jsx7(RichText, { html: body, palette: p, onLink }) })
3884
4079
  ) : null,
3885
- !step.title && step.showClose ? /* @__PURE__ */ jsx6(View6, { style: compact ? styles6.floatingCloseCompact : styles6.floatingClose, children: /* @__PURE__ */ jsx6(CloseButton, { palette: p, onPress: () => client.closeStep(guide.id), label: "Dismiss guide" }) }) : null,
4080
+ !step.title && step.showClose ? /* @__PURE__ */ jsx7(View6, { style: compact ? styles6.floatingCloseCompact : styles6.floatingClose, children: /* @__PURE__ */ jsx7(CloseButton, { palette: p, onPress: () => client.closeStep(guide.id), label: "Dismiss guide" }) }) : null,
3886
4081
  showProgress || buttons.length ? /* @__PURE__ */ jsxs5(View6, { style: styles6.footer, children: [
3887
4082
  showProgress ? /* @__PURE__ */ jsxs5(Text6, { style: [styles6.progress, { color: p.muted }], accessibilityLabel: `Step ${view.index + 1} of ${view.total}`, children: [
3888
4083
  view.index + 1,
3889
4084
  " of ",
3890
4085
  view.total
3891
- ] }) : /* @__PURE__ */ jsx6(View6, { style: styles6.flex }),
3892
- /* @__PURE__ */ jsx6(View6, { style: styles6.buttons, children: buttons.map((b) => /* @__PURE__ */ jsx6(GButton, { label: b.label, variant: b.variant, palette: p, onPress: () => client.pressButton(guide.id, b) }, b.id)) })
4086
+ ] }) : /* @__PURE__ */ jsx7(View6, { style: styles6.flex }),
4087
+ /* @__PURE__ */ jsx7(View6, { style: styles6.buttons, children: buttons.map((b) => /* @__PURE__ */ jsx7(GButton, { label: b.label, variant: b.variant, palette: p, onPress: () => client.pressButton(guide.id, b) }, b.id)) })
3893
4088
  ] }) : null
3894
4089
  ] }) });
3895
4090
  }
@@ -3914,10 +4109,10 @@ var styles6 = StyleSheet6.create({
3914
4109
  });
3915
4110
 
3916
4111
  // src/ui/StepLayer.tsx
3917
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
4112
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
3918
4113
  function useTargetRect(client, targetId, origin) {
3919
- const [rect, setRect] = useState4(null);
3920
- useEffect4(() => {
4114
+ const [rect, setRect] = useState5(null);
4115
+ useEffect5(() => {
3921
4116
  if (!targetId) {
3922
4117
  setRect(null);
3923
4118
  return;
@@ -3944,8 +4139,8 @@ function useTargetRect(client, targetId, origin) {
3944
4139
  return rect;
3945
4140
  }
3946
4141
  function useFadeIn(key) {
3947
- const opacity = useRef2(new Animated.Value(0)).current;
3948
- useEffect4(() => {
4142
+ const opacity = useRef3(new Animated.Value(0)).current;
4143
+ useEffect5(() => {
3949
4144
  opacity.setValue(0);
3950
4145
  Animated.timing(opacity, { toValue: 1, duration: 160, easing: Easing.out(Easing.quad), useNativeDriver: true }).start();
3951
4146
  }, [key, opacity]);
@@ -3957,34 +4152,34 @@ function StepLayer({ view, client, palette: p, geometry }) {
3957
4152
  const opacity = useFadeIn(view.key);
3958
4153
  const { viewport, insets } = geometry;
3959
4154
  const cardWidth = Math.min(viewport.width - 32, step.width ?? 340);
3960
- useEffect4(() => {
4155
+ useEffect5(() => {
3961
4156
  if (step.type === "hotspot" && !view.expanded) return;
3962
4157
  AccessibilityInfo.announceForAccessibility?.(announcement(view));
3963
4158
  }, [view.key, view.expanded]);
3964
- const backdrop = (children) => /* @__PURE__ */ jsx7(Pressable6, { accessible: false, style: [StyleSheet7.absoluteFill, { backgroundColor: p.overlay }], onPress: () => client.backdropPressed(view.guide.id), children });
4159
+ const backdrop = (children) => /* @__PURE__ */ jsx8(Pressable6, { accessible: false, style: [StyleSheet7.absoluteFill, { backgroundColor: p.overlay }], onPress: () => client.backdropPressed(view.guide.id), children });
3965
4160
  const anchored = !view.asModal && ["tooltip", "popover", "spotlight", "hotspot"].includes(step.type);
3966
4161
  if (!anchored && step.type === "banner") {
3967
4162
  const top = step.position.startsWith("top");
3968
- return /* @__PURE__ */ jsx7(Animated.View, { pointerEvents: "box-none", style: [styles7.banner, top ? { top: insets.top + 8 } : { bottom: insets.bottom + 8 }, { left: insets.left + 8, right: insets.right + 8, opacity }], children: /* @__PURE__ */ jsx7(StepCard, { view, client, palette: p, width: viewport.width - insets.left - insets.right - 16, compact: true }) });
4163
+ return /* @__PURE__ */ jsx8(Animated.View, { pointerEvents: "box-none", style: [styles7.banner, top ? { top: insets.top + 8 } : { bottom: insets.bottom + 8 }, { left: insets.left + 8, right: insets.right + 8, opacity }], children: /* @__PURE__ */ jsx8(StepCard, { view, client, palette: p, width: viewport.width - insets.left - insets.right - 16, compact: true }) });
3969
4164
  }
3970
4165
  if (!anchored && step.type === "slideout") {
3971
4166
  const wide = viewport.width >= 600;
3972
4167
  const right = !step.position.endsWith("left");
3973
4168
  const width = wide ? Math.min(380, cardWidth) : viewport.width - insets.left - insets.right - 24;
3974
- return /* @__PURE__ */ jsx7(Animated.View, { pointerEvents: "box-none", style: [styles7.abs, { bottom: insets.bottom + 12, opacity }, wide ? right ? { right: insets.right + 16 } : { left: insets.left + 16 } : { left: insets.left + 12 }], children: /* @__PURE__ */ jsx7(StepCard, { view, client, palette: p, width }) });
4169
+ return /* @__PURE__ */ jsx8(Animated.View, { pointerEvents: "box-none", style: [styles7.abs, { bottom: insets.bottom + 12, opacity }, wide ? right ? { right: insets.right + 16 } : { left: insets.left + 16 } : { left: insets.left + 12 }], children: /* @__PURE__ */ jsx8(StepCard, { view, client, palette: p, width }) });
3975
4170
  }
3976
4171
  if (!anchored) {
3977
4172
  return /* @__PURE__ */ jsxs6(Animated.View, { pointerEvents: "box-none", style: [StyleSheet7.absoluteFill, styles7.center, { opacity }], children: [
3978
4173
  step.showOverlay ? backdrop() : null,
3979
- /* @__PURE__ */ jsx7(StepCard, { view, client, palette: p, width: Math.min(viewport.width - 40, step.width ?? 360) })
4174
+ /* @__PURE__ */ jsx8(StepCard, { view, client, palette: p, width: Math.min(viewport.width - 40, step.width ?? 360) })
3980
4175
  ] });
3981
4176
  }
3982
4177
  if (!rect) return null;
3983
4178
  if (step.type === "hotspot") {
3984
4179
  const size = step.hotspot?.size ?? 14;
3985
4180
  return /* @__PURE__ */ jsxs6(View7, { pointerEvents: "box-none", style: StyleSheet7.absoluteFill, children: [
3986
- /* @__PURE__ */ jsx7(Hotspot, { x: rect.x + rect.width - size / 2, y: rect.y - size / 2, size, pulse: step.hotspot?.pulse ?? true, color: p.primary, onPress: () => client.toggleHotspot(view.guide.id), label: step.title || "Show tip" }),
3987
- view.expanded ? /* @__PURE__ */ jsx7(Anchored, { view, client, palette: p, geometry, rect, width: cardWidth }) : null
4181
+ /* @__PURE__ */ jsx8(Hotspot, { x: rect.x + rect.width - size / 2, y: rect.y - size / 2, size, pulse: step.hotspot?.pulse ?? true, color: p.primary, onPress: () => client.toggleHotspot(view.guide.id), label: step.title || "Show tip" }),
4182
+ view.expanded ? /* @__PURE__ */ jsx8(Anchored, { view, client, palette: p, geometry, rect, width: cardWidth }) : null
3988
4183
  ] });
3989
4184
  }
3990
4185
  if (step.type === "spotlight") {
@@ -4000,40 +4195,40 @@ function StepLayer({ view, client, palette: p, geometry }) {
4000
4195
  [hole.x + hole.width - r, hole.y + hole.height - r, -2 * r, -2 * r]
4001
4196
  ];
4002
4197
  return /* @__PURE__ */ jsxs6(Animated.View, { pointerEvents: "box-none", style: [StyleSheet7.absoluteFill, { opacity }], children: [
4003
- /* @__PURE__ */ jsx7(Pressable6, { accessible: false, onPress: press, style: [styles7.abs, shade, { left: 0, right: 0, top: 0, height: Math.max(0, hole.y) }] }),
4004
- /* @__PURE__ */ jsx7(Pressable6, { accessible: false, onPress: press, style: [styles7.abs, shade, { left: 0, right: 0, top: hole.y + hole.height, bottom: 0 }] }),
4005
- /* @__PURE__ */ jsx7(Pressable6, { accessible: false, onPress: press, style: [styles7.abs, shade, { left: 0, width: Math.max(0, hole.x), top: hole.y, height: hole.height }] }),
4006
- /* @__PURE__ */ jsx7(Pressable6, { accessible: false, onPress: press, style: [styles7.abs, shade, { left: hole.x + hole.width, right: 0, top: hole.y, height: hole.height }] }),
4007
- r > 0 ? corners.map(([left, top, dx, dy], i) => /* @__PURE__ */ jsx7(View7, { pointerEvents: "none", style: [styles7.abs, { left, top, width: r, height: r, overflow: "hidden" }], children: /* @__PURE__ */ jsx7(View7, { style: [styles7.abs, { left: dx, top: dy, width: 4 * r, height: 4 * r, borderRadius: 2 * r, borderWidth: r, borderColor: p.overlay }] }) }, i)) : null,
4008
- /* @__PURE__ */ jsx7(View7, { pointerEvents: "none", style: [styles7.abs, { left: hole.x, top: hole.y, width: hole.width, height: hole.height, borderRadius: r, borderWidth: 2, borderColor: p.primary }] }),
4009
- /* @__PURE__ */ jsx7(Anchored, { view, client, palette: p, geometry, rect: hole, width: cardWidth })
4198
+ /* @__PURE__ */ jsx8(Pressable6, { accessible: false, onPress: press, style: [styles7.abs, shade, { left: 0, right: 0, top: 0, height: Math.max(0, hole.y) }] }),
4199
+ /* @__PURE__ */ jsx8(Pressable6, { accessible: false, onPress: press, style: [styles7.abs, shade, { left: 0, right: 0, top: hole.y + hole.height, bottom: 0 }] }),
4200
+ /* @__PURE__ */ jsx8(Pressable6, { accessible: false, onPress: press, style: [styles7.abs, shade, { left: 0, width: Math.max(0, hole.x), top: hole.y, height: hole.height }] }),
4201
+ /* @__PURE__ */ jsx8(Pressable6, { accessible: false, onPress: press, style: [styles7.abs, shade, { left: hole.x + hole.width, right: 0, top: hole.y, height: hole.height }] }),
4202
+ r > 0 ? corners.map(([left, top, dx, dy], i) => /* @__PURE__ */ jsx8(View7, { pointerEvents: "none", style: [styles7.abs, { left, top, width: r, height: r, overflow: "hidden" }], children: /* @__PURE__ */ jsx8(View7, { style: [styles7.abs, { left: dx, top: dy, width: 4 * r, height: 4 * r, borderRadius: 2 * r, borderWidth: r, borderColor: p.overlay }] }) }, i)) : null,
4203
+ /* @__PURE__ */ jsx8(View7, { pointerEvents: "none", style: [styles7.abs, { left: hole.x, top: hole.y, width: hole.width, height: hole.height, borderRadius: r, borderWidth: 2, borderColor: p.primary }] }),
4204
+ /* @__PURE__ */ jsx8(Anchored, { view, client, palette: p, geometry, rect: hole, width: cardWidth })
4010
4205
  ] });
4011
4206
  }
4012
4207
  return /* @__PURE__ */ jsxs6(Animated.View, { pointerEvents: "box-none", style: [StyleSheet7.absoluteFill, { opacity }], children: [
4013
4208
  step.showOverlay ? backdrop() : null,
4014
- /* @__PURE__ */ jsx7(Anchored, { view, client, palette: p, geometry, rect, width: cardWidth })
4209
+ /* @__PURE__ */ jsx8(Anchored, { view, client, palette: p, geometry, rect, width: cardWidth })
4015
4210
  ] });
4016
4211
  }
4017
4212
  var ARROW = 12;
4018
4213
  function Anchored({ view, client, palette: p, geometry, rect, width }) {
4019
- const [height, setHeight] = useState4(0);
4214
+ const [height, setHeight] = useState5(0);
4020
4215
  const onLayout = (e) => setHeight(Math.round(e.nativeEvent.layout.height));
4021
4216
  const offset = view.step.offset + ARROW / 2;
4022
4217
  const pos = positionCard(rect, { width, height: height || 120 }, geometry.viewport, geometry.insets, view.step.placement, offset);
4023
4218
  return /* @__PURE__ */ jsxs6(View7, { pointerEvents: "box-none", style: [styles7.abs, { left: pos.x, top: pos.y, width, opacity: height ? 1 : 0 }], onLayout, children: [
4024
- /* @__PURE__ */ jsx7(Arrow, { side: pos.side, at: pos.arrow, color: p.background, border: p.border }),
4025
- /* @__PURE__ */ jsx7(StepCard, { view, client, palette: p, width })
4219
+ /* @__PURE__ */ jsx8(Arrow, { side: pos.side, at: pos.arrow, color: p.background, border: p.border }),
4220
+ /* @__PURE__ */ jsx8(StepCard, { view, client, palette: p, width })
4026
4221
  ] });
4027
4222
  }
4028
4223
  function Arrow({ side, at, color, border }) {
4029
4224
  const half = ARROW / 2;
4030
4225
  const place = side === "bottom" ? { top: -half, left: at - half } : side === "top" ? { bottom: -half, left: at - half } : side === "right" ? { left: -half, top: at - half } : { right: -half, top: at - half };
4031
- return /* @__PURE__ */ jsx7(View7, { pointerEvents: "none", style: [styles7.abs, styles7.arrow, place, { backgroundColor: color, borderColor: border }] });
4226
+ return /* @__PURE__ */ jsx8(View7, { pointerEvents: "none", style: [styles7.abs, styles7.arrow, place, { backgroundColor: color, borderColor: border }] });
4032
4227
  }
4033
4228
  function Hotspot({ x, y, size, pulse, color, onPress, label }) {
4034
- const scale = useRef2(new Animated.Value(0)).current;
4035
- const [reduceMotion, setReduceMotion] = useState4(false);
4036
- useEffect4(() => {
4229
+ const scale = useRef3(new Animated.Value(0)).current;
4230
+ const [reduceMotion, setReduceMotion] = useState5(false);
4231
+ useEffect5(() => {
4037
4232
  let alive = true;
4038
4233
  AccessibilityInfo.isReduceMotionEnabled?.().then((v) => alive && setReduceMotion(v), () => {
4039
4234
  });
@@ -4041,21 +4236,21 @@ function Hotspot({ x, y, size, pulse, color, onPress, label }) {
4041
4236
  alive = false;
4042
4237
  };
4043
4238
  }, []);
4044
- useEffect4(() => {
4239
+ useEffect5(() => {
4045
4240
  if (!pulse || reduceMotion) return;
4046
4241
  const loop = Animated.loop(Animated.timing(scale, { toValue: 1, duration: 1400, easing: Easing.out(Easing.ease), useNativeDriver: true }));
4047
4242
  loop.start();
4048
4243
  return () => loop.stop();
4049
4244
  }, [pulse, reduceMotion, scale]);
4050
4245
  return /* @__PURE__ */ jsxs6(Pressable6, { accessibilityRole: "button", accessibilityLabel: label, onPress, hitSlop: 14, style: [styles7.abs, { left: x, top: y, width: size, height: size }], children: [
4051
- pulse && !reduceMotion ? /* @__PURE__ */ jsx7(
4246
+ pulse && !reduceMotion ? /* @__PURE__ */ jsx8(
4052
4247
  Animated.View,
4053
4248
  {
4054
4249
  pointerEvents: "none",
4055
4250
  style: [styles7.abs, { width: size, height: size, borderRadius: size / 2, backgroundColor: color, opacity: scale.interpolate({ inputRange: [0, 1], outputRange: [0.55, 0] }), transform: [{ scale: scale.interpolate({ inputRange: [0, 1], outputRange: [1, 2.6] }) }] }]
4056
4251
  }
4057
4252
  ) : null,
4058
- /* @__PURE__ */ jsx7(View7, { style: { width: size, height: size, borderRadius: size / 2, backgroundColor: color, borderWidth: 2, borderColor: "#ffffff" } })
4253
+ /* @__PURE__ */ jsx8(View7, { style: { width: size, height: size, borderRadius: size / 2, backgroundColor: color, borderWidth: 2, borderColor: "#ffffff" } })
4059
4254
  ] });
4060
4255
  }
4061
4256
  var styles7 = StyleSheet7.create({
@@ -4066,7 +4261,7 @@ var styles7 = StyleSheet7.create({
4066
4261
  });
4067
4262
 
4068
4263
  // src/ui/Survey.tsx
4069
- import { useEffect as useEffect5, useState as useState5 } from "react";
4264
+ import { useEffect as useEffect6, useState as useState6 } from "react";
4070
4265
  import {
4071
4266
  AccessibilityInfo as AccessibilityInfo2,
4072
4267
  KeyboardAvoidingView,
@@ -4078,7 +4273,7 @@ import {
4078
4273
  TextInput as TextInput3,
4079
4274
  View as View8
4080
4275
  } from "react-native";
4081
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4276
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
4082
4277
  function Survey({
4083
4278
  view,
4084
4279
  client,
@@ -4086,9 +4281,9 @@ function Survey({
4086
4281
  geometry
4087
4282
  }) {
4088
4283
  const config = view.guide.config.survey;
4089
- const [answers, setAnswers] = useState5({});
4090
- const [error, setError] = useState5("");
4091
- useEffect5(() => {
4284
+ const [answers, setAnswers] = useState6({});
4285
+ const [error, setError] = useState6("");
4286
+ useEffect6(() => {
4092
4287
  setAnswers({});
4093
4288
  setError("");
4094
4289
  AccessibilityInfo2.announceForAccessibility?.(
@@ -4108,11 +4303,11 @@ function Survey({
4108
4303
  return next;
4109
4304
  });
4110
4305
  const submit = () => {
4111
- const missing = config.questions.filter(
4306
+ const missing2 = config.questions.filter(
4112
4307
  (q) => q.required && isQuestionVisible(q, answers) && answers[q.id] === void 0
4113
4308
  );
4114
- if (missing.length) {
4115
- setError(`Please answer: ${missing.map((q) => q.title).join(", ")}`);
4309
+ if (missing2.length) {
4310
+ setError(`Please answer: ${missing2.map((q) => q.title).join(", ")}`);
4116
4311
  return;
4117
4312
  }
4118
4313
  setError("");
@@ -4128,7 +4323,7 @@ function Survey({
4128
4323
  behavior: Platform4.OS === "ios" ? "padding" : void 0,
4129
4324
  style: StyleSheet8.absoluteFill,
4130
4325
  children: [
4131
- centered ? /* @__PURE__ */ jsx8(
4326
+ centered ? /* @__PURE__ */ jsx9(
4132
4327
  Pressable7,
4133
4328
  {
4134
4329
  accessible: false,
@@ -4136,7 +4331,7 @@ function Survey({
4136
4331
  onPress: close
4137
4332
  }
4138
4333
  ) : null,
4139
- /* @__PURE__ */ jsx8(
4334
+ /* @__PURE__ */ jsx9(
4140
4335
  View8,
4141
4336
  {
4142
4337
  pointerEvents: "box-none",
@@ -4145,13 +4340,13 @@ function Survey({
4145
4340
  centered ? styles8.center : styles8.bottom,
4146
4341
  !centered && { paddingBottom: insets.bottom + 12 }
4147
4342
  ],
4148
- children: /* @__PURE__ */ jsx8(
4343
+ children: /* @__PURE__ */ jsx9(
4149
4344
  Surface,
4150
4345
  {
4151
4346
  palette: p,
4152
4347
  style: { width, maxHeight: viewport.height - insets.top - insets.bottom - 40 },
4153
4348
  children: view.submitted ? /* @__PURE__ */ jsxs7(View8, { style: styles8.inner, children: [
4154
- /* @__PURE__ */ jsx8(
4349
+ /* @__PURE__ */ jsx9(
4155
4350
  Text7,
4156
4351
  {
4157
4352
  accessibilityRole: "header",
@@ -4159,7 +4354,7 @@ function Survey({
4159
4354
  children: config.thankYouTitle
4160
4355
  }
4161
4356
  ),
4162
- config.thankYouBody ? /* @__PURE__ */ jsx8(
4357
+ config.thankYouBody ? /* @__PURE__ */ jsx9(
4163
4358
  RichText,
4164
4359
  {
4165
4360
  html: config.thankYouBody,
@@ -4167,18 +4362,18 @@ function Survey({
4167
4362
  onLink: (u) => client.navigate(u)
4168
4363
  }
4169
4364
  ) : null,
4170
- /* @__PURE__ */ jsx8(GButton, { label: "Done", palette: p, onPress: close, style: styles8.end })
4365
+ /* @__PURE__ */ jsx9(GButton, { label: "Done", palette: p, onPress: close, style: styles8.end })
4171
4366
  ] }) : /* @__PURE__ */ jsxs7(ScrollView5, { contentContainerStyle: styles8.inner, keyboardShouldPersistTaps: "handled", children: [
4172
4367
  config.questions.filter((q) => isQuestionVisible(q, answers)).map((q, i) => /* @__PURE__ */ jsxs7(View8, { style: styles8.question, children: [
4173
4368
  /* @__PURE__ */ jsxs7(View8, { style: styles8.head, children: [
4174
4369
  /* @__PURE__ */ jsxs7(Text7, { style: [styles8.qTitle, { color: p.text, fontFamily: p.fontFamily }], children: [
4175
4370
  q.title,
4176
- !q.required ? /* @__PURE__ */ jsx8(Text7, { style: { color: p.muted, fontWeight: "400" }, children: " (optional)" }) : null
4371
+ !q.required ? /* @__PURE__ */ jsx9(Text7, { style: { color: p.muted, fontWeight: "400" }, children: " (optional)" }) : null
4177
4372
  ] }),
4178
- i === 0 ? /* @__PURE__ */ jsx8(CloseButton, { palette: p, onPress: close, label: "Dismiss survey" }) : null
4373
+ i === 0 ? /* @__PURE__ */ jsx9(CloseButton, { palette: p, onPress: close, label: "Dismiss survey" }) : null
4179
4374
  ] }),
4180
- q.description ? /* @__PURE__ */ jsx8(Text7, { style: [styles8.desc, { color: p.muted }], children: q.description }) : null,
4181
- /* @__PURE__ */ jsx8(
4375
+ q.description ? /* @__PURE__ */ jsx9(Text7, { style: [styles8.desc, { color: p.muted }], children: q.description }) : null,
4376
+ /* @__PURE__ */ jsx9(
4182
4377
  QuestionInput,
4183
4378
  {
4184
4379
  q,
@@ -4188,8 +4383,8 @@ function Survey({
4188
4383
  }
4189
4384
  )
4190
4385
  ] }, q.id)),
4191
- error ? /* @__PURE__ */ jsx8(Text7, { accessibilityLiveRegion: "polite", style: [styles8.error], children: error }) : null,
4192
- /* @__PURE__ */ jsx8(GButton, { label: "Submit", palette: p, onPress: submit, style: styles8.end })
4386
+ error ? /* @__PURE__ */ jsx9(Text7, { accessibilityLiveRegion: "polite", style: [styles8.error], children: error }) : null,
4387
+ /* @__PURE__ */ jsx9(GButton, { label: "Submit", palette: p, onPress: submit, style: styles8.end })
4193
4388
  ] })
4194
4389
  }
4195
4390
  )
@@ -4225,7 +4420,7 @@ function QuestionInput({
4225
4420
  const max = q.type === "nps" ? 10 : q.scaleMax ?? 5;
4226
4421
  const values = Array.from({ length: max - min + 1 }, (_, i) => min + i);
4227
4422
  return /* @__PURE__ */ jsxs7(View8, { children: [
4228
- /* @__PURE__ */ jsx8(View8, { style: styles8.scale, accessibilityRole: "radiogroup", accessibilityLabel: q.title, children: values.map((n) => /* @__PURE__ */ jsx8(
4423
+ /* @__PURE__ */ jsx9(View8, { style: styles8.scale, accessibilityRole: "radiogroup", accessibilityLabel: q.title, children: values.map((n) => /* @__PURE__ */ jsx9(
4229
4424
  Pressable7,
4230
4425
  {
4231
4426
  accessibilityRole: "radio",
@@ -4233,19 +4428,19 @@ function QuestionInput({
4233
4428
  accessibilityLabel: String(n),
4234
4429
  onPress: () => onChange(n),
4235
4430
  style: [...chip(value === n), styles8.scaleItem],
4236
- children: /* @__PURE__ */ jsx8(Text7, { style: chipText(value === n), children: n })
4431
+ children: /* @__PURE__ */ jsx9(Text7, { style: chipText(value === n), children: n })
4237
4432
  },
4238
4433
  n
4239
4434
  )) }),
4240
4435
  q.lowLabel || q.highLabel ? /* @__PURE__ */ jsxs7(View8, { style: styles8.labels, children: [
4241
- /* @__PURE__ */ jsx8(Text7, { style: [styles8.small, { color: p.muted }], children: q.lowLabel }),
4242
- /* @__PURE__ */ jsx8(Text7, { style: [styles8.small, { color: p.muted }], children: q.highLabel })
4436
+ /* @__PURE__ */ jsx9(Text7, { style: [styles8.small, { color: p.muted }], children: q.lowLabel }),
4437
+ /* @__PURE__ */ jsx9(Text7, { style: [styles8.small, { color: p.muted }], children: q.highLabel })
4243
4438
  ] }) : null
4244
4439
  ] });
4245
4440
  }
4246
4441
  case "rating": {
4247
4442
  const max = q.scaleMax ?? 5;
4248
- return /* @__PURE__ */ jsx8(View8, { style: styles8.stars, accessibilityRole: "radiogroup", accessibilityLabel: q.title, children: Array.from({ length: max }, (_, i) => i + 1).map((n) => /* @__PURE__ */ jsx8(
4443
+ return /* @__PURE__ */ jsx9(View8, { style: styles8.stars, accessibilityRole: "radiogroup", accessibilityLabel: q.title, children: Array.from({ length: max }, (_, i) => i + 1).map((n) => /* @__PURE__ */ jsx9(
4249
4444
  Pressable7,
4250
4445
  {
4251
4446
  accessibilityRole: "radio",
@@ -4253,7 +4448,7 @@ function QuestionInput({
4253
4448
  accessibilityState: { selected: value === n },
4254
4449
  onPress: () => onChange(n),
4255
4450
  hitSlop: 4,
4256
- children: /* @__PURE__ */ jsx8(
4451
+ children: /* @__PURE__ */ jsx9(
4257
4452
  Text7,
4258
4453
  {
4259
4454
  style: [
@@ -4268,10 +4463,10 @@ function QuestionInput({
4268
4463
  )) });
4269
4464
  }
4270
4465
  case "thumbs":
4271
- return /* @__PURE__ */ jsx8(View8, { style: styles8.row, accessibilityRole: "radiogroup", children: [
4466
+ return /* @__PURE__ */ jsx9(View8, { style: styles8.row, accessibilityRole: "radiogroup", children: [
4272
4467
  [true, "\u{1F44D}", "Thumbs up"],
4273
4468
  [false, "\u{1F44E}", "Thumbs down"]
4274
- ].map(([val, icon, label]) => /* @__PURE__ */ jsx8(
4469
+ ].map(([val, icon, label]) => /* @__PURE__ */ jsx9(
4275
4470
  Pressable7,
4276
4471
  {
4277
4472
  accessibilityRole: "radio",
@@ -4279,7 +4474,7 @@ function QuestionInput({
4279
4474
  accessibilityState: { selected: value === val },
4280
4475
  onPress: () => onChange(val),
4281
4476
  style: [...chip(value === val), styles8.thumb],
4282
- children: /* @__PURE__ */ jsx8(Text7, { style: styles8.thumbText, children: icon })
4477
+ children: /* @__PURE__ */ jsx9(Text7, { style: styles8.thumbText, children: icon })
4283
4478
  },
4284
4479
  String(val)
4285
4480
  )) });
@@ -4289,7 +4484,7 @@ function QuestionInput({
4289
4484
  const selected = new Set(
4290
4485
  multi ? value ?? [] : value !== void 0 ? [String(value)] : []
4291
4486
  );
4292
- return /* @__PURE__ */ jsx8(View8, { style: styles8.choices, children: q.options.map((o) => {
4487
+ return /* @__PURE__ */ jsx9(View8, { style: styles8.choices, children: q.options.map((o) => {
4293
4488
  const on = selected.has(o.id);
4294
4489
  return /* @__PURE__ */ jsxs7(
4295
4490
  Pressable7,
@@ -4309,7 +4504,7 @@ function QuestionInput({
4309
4504
  { borderColor: on ? p.primary : p.border, borderRadius: Math.min(10, p.radius) }
4310
4505
  ],
4311
4506
  children: [
4312
- /* @__PURE__ */ jsx8(
4507
+ /* @__PURE__ */ jsx9(
4313
4508
  View8,
4314
4509
  {
4315
4510
  style: [
@@ -4322,7 +4517,7 @@ function QuestionInput({
4322
4517
  ]
4323
4518
  }
4324
4519
  ),
4325
- /* @__PURE__ */ jsx8(Text7, { style: [styles8.choiceText, { color: p.text }], children: o.label })
4520
+ /* @__PURE__ */ jsx9(Text7, { style: [styles8.choiceText, { color: p.text }], children: o.label })
4326
4521
  ]
4327
4522
  },
4328
4523
  o.id
@@ -4331,7 +4526,7 @@ function QuestionInput({
4331
4526
  }
4332
4527
  case "text":
4333
4528
  default:
4334
- return /* @__PURE__ */ jsx8(
4529
+ return /* @__PURE__ */ jsx9(
4335
4530
  TextInput3,
4336
4531
  {
4337
4532
  accessibilityLabel: q.title,
@@ -4439,7 +4634,7 @@ function palette(theme, systemScheme, explicit) {
4439
4634
  }
4440
4635
 
4441
4636
  // src/ui/Overlay.tsx
4442
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
4637
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
4443
4638
  function defaultInsets(width, height) {
4444
4639
  if (Platform5.OS === "ios") {
4445
4640
  const notch = Math.max(width, height) >= 812 && !Platform5.isPad;
@@ -4449,16 +4644,16 @@ function defaultInsets(width, height) {
4449
4644
  return { top: StatusBar.currentHeight ?? 24, bottom: 16, left: 0, right: 0 };
4450
4645
  }
4451
4646
  function GuidelyOverlay({ client, insets: insetsProp, launcherOffset }) {
4452
- const ui = useSyncExternalStore2(
4647
+ const ui = useSyncExternalStore3(
4453
4648
  useCallback2((cb) => client.subscribeUi(cb), [client]),
4454
4649
  () => client.getUiState(),
4455
4650
  () => client.getUiState()
4456
4651
  );
4457
4652
  const { width, height } = useWindowDimensions();
4458
4653
  const scheme = useColorScheme();
4459
- const ref = useRef3(null);
4460
- const [origin, setOrigin] = useState6({ x: 0, y: 0 });
4461
- useEffect6(() => {
4654
+ const ref = useRef4(null);
4655
+ const [origin, setOrigin] = useState7({ x: 0, y: 0 });
4656
+ useEffect7(() => {
4462
4657
  client.targets.setViewport(width, height);
4463
4658
  }, [client, width, height]);
4464
4659
  const measureOrigin = useCallback2(() => {
@@ -4474,21 +4669,21 @@ function GuidelyOverlay({ client, insets: insetsProp, launcherOffset }) {
4474
4669
  const messengerLeft = messenger?.getSnapshot().config.launcher.position === "bottom-left";
4475
4670
  const checklistOnSameSide = ui.checklists.some((c) => c.guide.config.checklist?.position === "bottom-left" === messengerLeft) && client.options.showChecklistLauncher !== false;
4476
4671
  return /* @__PURE__ */ jsxs8(View9, { ref, collapsable: false, pointerEvents: "box-none", style: StyleSheet9.absoluteFill, onLayout: measureOrigin, children: [
4477
- ui.checklists.map((c) => /* @__PURE__ */ jsx9(Checklist, { view: c, client, palette: palette(client.themeFor(c.guide), scheme), geometry, showLauncher: client.options.showChecklistLauncher !== false, launcherBottom: launcherOffset }, c.guide.id)),
4478
- showRcLauncher ? /* @__PURE__ */ jsx9(
4672
+ ui.checklists.map((c) => /* @__PURE__ */ jsx10(Checklist, { view: c, client, palette: palette(client.themeFor(c.guide), scheme), geometry, showLauncher: client.options.showChecklistLauncher !== false, launcherBottom: launcherOffset }, c.guide.id)),
4673
+ showRcLauncher ? /* @__PURE__ */ jsx10(
4479
4674
  Pressable8,
4480
4675
  {
4481
4676
  accessibilityRole: "button",
4482
4677
  accessibilityLabel: client.getResourceCenter()?.launcher.label || "Help",
4483
4678
  onPress: () => client.openResourceCenter(),
4484
4679
  style: [styles9.rc, client.getResourceCenter()?.launcher.position === "bottom-left" ? { left: insets.left + 16 } : { right: insets.right + 16 }, { bottom: insets.bottom + launcherOffset + (ui.checklists.length ? 56 : 0), backgroundColor: basePalette.primary }],
4485
- children: /* @__PURE__ */ jsx9(Text8, { style: [styles9.rcText, { color: basePalette.onPrimary }], children: "?" })
4680
+ children: /* @__PURE__ */ jsx10(Text8, { style: [styles9.rcText, { color: basePalette.onPrimary }], children: "?" })
4486
4681
  }
4487
4682
  ) : null,
4488
- messenger && client.options.showMessengerLauncher !== false ? /* @__PURE__ */ jsx9(MessengerLauncher, { store: messenger, palette: basePalette, style: [messengerLeft ? { left: insets.left + 16 } : { right: insets.right + 16 }, { bottom: insets.bottom + launcherOffset + (checklistOnSameSide ? 56 : 0) }] }) : null,
4489
- ui.survey ? /* @__PURE__ */ jsx9(Survey, { view: ui.survey, client, palette: palette(client.themeFor(ui.survey.guide), scheme), geometry }) : null,
4490
- ui.steps.map((s) => /* @__PURE__ */ jsx9(StepLayer, { view: s, client, palette: palette(client.themeFor(s.guide), scheme, s.guide.config.appearance), geometry }, s.key)),
4491
- messenger ? /* @__PURE__ */ jsx9(MessengerSheet, { store: messenger, client, palette: basePalette, topInset: insets.top, bottomInset: insets.bottom }) : /* @__PURE__ */ jsx9(ResourceCenter, { client, palette: basePalette, open: ui.resourceCenterOpen, topInset: insets.top, bottomInset: insets.bottom })
4683
+ messenger && client.options.showMessengerLauncher !== false ? /* @__PURE__ */ jsx10(MessengerLauncher, { store: messenger, palette: basePalette, style: [messengerLeft ? { left: insets.left + 16 } : { right: insets.right + 16 }, { bottom: insets.bottom + launcherOffset + (checklistOnSameSide ? 56 : 0) }] }) : null,
4684
+ ui.survey ? /* @__PURE__ */ jsx10(Survey, { view: ui.survey, client, palette: palette(client.themeFor(ui.survey.guide), scheme), geometry }) : null,
4685
+ ui.steps.map((s) => /* @__PURE__ */ jsx10(StepLayer, { view: s, client, palette: palette(client.themeFor(s.guide), scheme, s.guide.config.appearance), geometry }, s.key)),
4686
+ messenger ? /* @__PURE__ */ jsx10(MessengerSheet, { store: messenger, client, palette: basePalette, topInset: insets.top, bottomInset: insets.bottom }) : /* @__PURE__ */ jsx10(ResourceCenter, { client, palette: basePalette, open: ui.resourceCenterOpen, topInset: insets.top, bottomInset: insets.bottom })
4492
4687
  ] });
4493
4688
  }
4494
4689
  var styles9 = StyleSheet9.create({
@@ -4497,24 +4692,24 @@ var styles9 = StyleSheet9.create({
4497
4692
  });
4498
4693
 
4499
4694
  // src/ui/Target.tsx
4500
- import { createContext, useCallback as useCallback3, useContext, useEffect as useEffect7, useRef as useRef4 } from "react";
4695
+ import { createContext as createContext2, useCallback as useCallback3, useContext as useContext2, useEffect as useEffect8, useRef as useRef5 } from "react";
4501
4696
  import { View as View10 } from "react-native";
4502
- import { jsx as jsx10 } from "react/jsx-runtime";
4503
- var GuidelyContext = createContext(null);
4504
- var GuidelyScreenContext = createContext(null);
4697
+ import { jsx as jsx11 } from "react/jsx-runtime";
4698
+ var GuidelyContext = createContext2(null);
4699
+ var GuidelyScreenContext = createContext2(null);
4505
4700
  function GuidelyScreenScope({ name, children }) {
4506
- return /* @__PURE__ */ jsx10(GuidelyScreenContext.Provider, { value: name, children });
4701
+ return /* @__PURE__ */ jsx11(GuidelyScreenContext.Provider, { value: name, children });
4507
4702
  }
4508
4703
  function guidelyScreenLayout({ route, children }) {
4509
- return /* @__PURE__ */ jsx10(GuidelyScreenScope, { name: route.name, children });
4704
+ return /* @__PURE__ */ jsx11(GuidelyScreenScope, { name: route.name, children });
4510
4705
  }
4511
4706
  var TAP_SLOP = 10;
4512
4707
  function useGuidelyTarget(id) {
4513
- const client = useContext(GuidelyContext);
4514
- const screen = useContext(GuidelyScreenContext);
4515
- const ref = useRef4(null);
4516
- const start = useRef4(null);
4517
- useEffect7(() => {
4708
+ const client = useContext2(GuidelyContext);
4709
+ const screen = useContext2(GuidelyScreenContext);
4710
+ const ref = useRef5(null);
4711
+ const start = useRef5(null);
4712
+ useEffect8(() => {
4518
4713
  if (!client || !id) return;
4519
4714
  return client.targets.register(id, {
4520
4715
  screen,
@@ -4541,7 +4736,7 @@ function useGuidelyTarget(id) {
4541
4736
  }
4542
4737
  function GuidelyTarget({ id, children, onLayout, onTouchStart, onTouchEnd, ...rest }) {
4543
4738
  const t = useGuidelyTarget(id);
4544
- return /* @__PURE__ */ jsx10(
4739
+ return /* @__PURE__ */ jsx11(
4545
4740
  View10,
4546
4741
  {
4547
4742
  ...rest,
@@ -4565,7 +4760,8 @@ function GuidelyTarget({ id, children, onLayout, onTouchStart, onTouchEnd, ...re
4565
4760
  }
4566
4761
 
4567
4762
  // src/index.tsx
4568
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
4763
+ import { createFlagsClient as createFlagsClient3, InMemoryStickyBucketStore } from "@guidelytech/flags";
4764
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
4569
4765
  function deviceLocale() {
4570
4766
  try {
4571
4767
  return Intl.DateTimeFormat().resolvedOptions().locale;
@@ -4586,18 +4782,38 @@ function defaultBridge(appVersion) {
4586
4782
  })
4587
4783
  };
4588
4784
  }
4589
- function GuidelyProvider({ children, user, insets, launcherOffset = 72, ...options }) {
4590
- const [client, setClient] = useState7(null);
4591
- const optionsRef = useRef5(options);
4785
+ function GuidelyProvider({ children, user, insets, launcherOffset = 72, flagsPayload, flagAttributes, ...options }) {
4786
+ const [client, setClient] = useState8(null);
4787
+ const optionsRef = useRef6(options);
4592
4788
  optionsRef.current = options;
4789
+ const userRef = useRef6(user);
4790
+ userRef.current = user;
4791
+ const flagsOpts = options.flags === false ? null : options.flags ?? {};
4792
+ const payload = flagsPayload ?? flagsOpts?.payload ?? null;
4793
+ const payloadRef = useRef6(payload);
4794
+ payloadRef.current = payload;
4795
+ const appAttributes = useMemo5(() => ({ ...flagsOpts?.attributes ?? {}, ...flagAttributes ?? {} }), [stableStringify2(flagsOpts?.attributes ?? {}), stableStringify2(flagAttributes ?? {})]);
4796
+ const appAttributesRef = useRef6(appAttributes);
4797
+ appAttributesRef.current = appAttributes;
4593
4798
  const { projectId, environment, apiUrl, storage } = options;
4594
- useEffect8(() => {
4799
+ useEffect9(() => {
4595
4800
  if (!projectId) return;
4596
4801
  let c;
4597
4802
  try {
4598
4803
  const latest = optionsRef.current;
4599
4804
  c = new GuidelyClient(
4600
- { ...latest, projectId, environment, apiUrl, storage, onNavigate: (url) => optionsRef.current.onNavigate?.(url), onError: (err) => optionsRef.current.onError?.(err) },
4805
+ {
4806
+ ...latest,
4807
+ projectId,
4808
+ environment,
4809
+ apiUrl,
4810
+ storage,
4811
+ // Known up front so flags evaluate for this user from the first render.
4812
+ user: userRef.current?.id ? userRef.current : void 0,
4813
+ flags: latest.flags === false ? false : { ...latest.flags ?? {}, payload: payloadRef.current ?? void 0, attributes: appAttributesRef.current },
4814
+ onNavigate: (url) => optionsRef.current.onNavigate?.(url),
4815
+ onError: (err) => optionsRef.current.onError?.(err)
4816
+ },
4601
4817
  defaultBridge(latest.appVersion)
4602
4818
  );
4603
4819
  } catch (err) {
@@ -4606,7 +4822,7 @@ function GuidelyProvider({ children, user, insets, launcherOffset = 72, ...optio
4606
4822
  }
4607
4823
  setClient(c);
4608
4824
  void c.init();
4609
- const sub = AppState.addEventListener("change", (s) => c.setAppState(s));
4825
+ const sub = AppState2.addEventListener("change", (s) => c.setAppState(s));
4610
4826
  return () => {
4611
4827
  sub.remove();
4612
4828
  c.destroy();
@@ -4614,8 +4830,8 @@ function GuidelyProvider({ children, user, insets, launcherOffset = 72, ...optio
4614
4830
  };
4615
4831
  }, [projectId, environment, apiUrl, storage]);
4616
4832
  const userKey = user ? JSON.stringify([user.id, user.traits ?? null, user.account ?? null]) : "";
4617
- const hadUser = useRef5(false);
4618
- useEffect8(() => {
4833
+ const hadUser = useRef6(false);
4834
+ useEffect9(() => {
4619
4835
  if (!client) return;
4620
4836
  if (user && user.id) {
4621
4837
  hadUser.current = true;
@@ -4625,10 +4841,15 @@ function GuidelyProvider({ children, user, insets, launcherOffset = 72, ...optio
4625
4841
  client.reset();
4626
4842
  }
4627
4843
  }, [client, userKey]);
4628
- return /* @__PURE__ */ jsxs9(GuidelyContext.Provider, { value: client, children: [
4844
+ useEffect9(() => {
4845
+ client?.flags.setAttributes(appAttributes);
4846
+ }, [client, appAttributes]);
4847
+ const bootstrap = useBootstrap(payload, { ...user?.id ? { ...user.traits ?? {}, id: user.id } : {}, ...appAttributes });
4848
+ const flags = useMemo5(() => ({ client: client?.flags ?? null, bootstrap }), [client, bootstrap]);
4849
+ return /* @__PURE__ */ jsx12(GuidelyContext.Provider, { value: client, children: /* @__PURE__ */ jsxs9(FlagsContext.Provider, { value: flags, children: [
4629
4850
  children,
4630
- client ? /* @__PURE__ */ jsx11(GuidelyOverlay, { client, insets, launcherOffset }) : null
4631
- ] });
4851
+ client ? /* @__PURE__ */ jsx12(GuidelyOverlay, { client, insets, launcherOffset }) : null
4852
+ ] }) });
4632
4853
  }
4633
4854
  var NOOP_RESULTS = {
4634
4855
  init: Promise.resolve(),
@@ -4652,20 +4873,38 @@ var NOOP_RESULTS = {
4652
4873
  },
4653
4874
  getState: { ready: false, destroyed: false }
4654
4875
  };
4876
+ var noopFlags = new Proxy(createStaticFlags2(), {
4877
+ get(target, prop, receiver) {
4878
+ if (prop in target) return Reflect.get(target, prop, receiver);
4879
+ if (prop === "ready" || prop === "refresh" || prop === "flush") return () => Promise.resolve();
4880
+ if (prop === "getPayload") return () => null;
4881
+ if (prop === "forUser") return () => target;
4882
+ return typeof prop === "string" ? () => void 0 : void 0;
4883
+ }
4884
+ });
4885
+ var missing = (key, fallback) => ({ key, value: fallback, reason: "FLAG_NOT_FOUND", inExperiment: false, usedFallback: true });
4886
+ var NOOP_FUNCTIONS = {
4887
+ isFeatureEnabled: (_key, fallback = false) => fallback,
4888
+ getFeatureValue: (_key, fallback) => fallback,
4889
+ getFeatureDetails: (key, fallback) => missing(key, fallback),
4890
+ onFlagsChange: () => () => {
4891
+ }
4892
+ };
4655
4893
  var noopClient = new Proxy({}, {
4656
4894
  get(_target, prop) {
4657
4895
  if (prop === "options") return { projectId: "" };
4896
+ if (prop === "flags") return noopFlags;
4658
4897
  if (typeof prop !== "string") return void 0;
4659
- return () => NOOP_RESULTS[prop];
4898
+ return NOOP_FUNCTIONS[prop] ?? (() => NOOP_RESULTS[prop]);
4660
4899
  }
4661
4900
  });
4662
4901
  function useGuidely() {
4663
- return useContext2(GuidelyContext) ?? noopClient;
4902
+ return useContext3(GuidelyContext) ?? noopClient;
4664
4903
  }
4665
4904
  function useGuidelyReady() {
4666
- const client = useContext2(GuidelyContext);
4667
- const [ready, setReady] = useState7(() => !!client?.isClientReady());
4668
- useEffect8(() => {
4905
+ const client = useContext3(GuidelyContext);
4906
+ const [ready, setReady] = useState8(() => !!client?.isClientReady());
4907
+ useEffect9(() => {
4669
4908
  if (!client) return setReady(false);
4670
4909
  setReady(client.isClientReady());
4671
4910
  const off = client.on("ready", () => setReady(true));
@@ -4675,44 +4914,44 @@ function useGuidelyReady() {
4675
4914
  return ready;
4676
4915
  }
4677
4916
  function useGuidelyIdentify(userId, traits, account) {
4678
- const client = useContext2(GuidelyContext);
4917
+ const client = useContext3(GuidelyContext);
4679
4918
  const key = JSON.stringify([userId ?? null, traits ?? null, account ?? null]);
4680
- useEffect8(() => {
4919
+ useEffect9(() => {
4681
4920
  if (client && userId) void client.identify(userId, traits, account);
4682
4921
  }, [client, key]);
4683
4922
  }
4684
4923
  function useGuidelyEvent(event, handler) {
4685
- const client = useContext2(GuidelyContext);
4686
- const ref = useRef5(handler);
4924
+ const client = useContext3(GuidelyContext);
4925
+ const ref = useRef6(handler);
4687
4926
  ref.current = handler;
4688
- useEffect8(() => {
4927
+ useEffect9(() => {
4689
4928
  if (!client) return;
4690
4929
  return client.on(event, ((...args) => ref.current(...args)));
4691
4930
  }, [client, event]);
4692
4931
  }
4693
4932
  function useGuidelyScreen(name, properties) {
4694
- const client = useContext2(GuidelyContext);
4695
- useEffect8(() => {
4933
+ const client = useContext3(GuidelyContext);
4934
+ useEffect9(() => {
4696
4935
  if (client && name) client.screen(name, properties);
4697
4936
  }, [client, name]);
4698
4937
  }
4699
4938
  function useGuidelyNavigation(navigationRef, options = {}) {
4700
- const client = useContext2(GuidelyContext);
4701
- const nameRef = useRef5(options.screenName);
4939
+ const client = useContext3(GuidelyContext);
4940
+ const nameRef = useRef6(options.screenName);
4702
4941
  nameRef.current = options.screenName;
4703
4942
  const report = () => {
4704
4943
  const route = navigationRef.getCurrentRoute?.();
4705
4944
  if (client && route?.name) client.screen(nameRef.current ? nameRef.current(route) : route.name);
4706
4945
  };
4707
- useEffect8(report, [client]);
4946
+ useEffect9(report, [client]);
4708
4947
  return { onReady: report, onStateChange: report };
4709
4948
  }
4710
4949
  function useUiSelector(select, fallback) {
4711
- const client = useContext2(GuidelyContext);
4712
- const selectRef = useRef5(select);
4950
+ const client = useContext3(GuidelyContext);
4951
+ const selectRef = useRef6(select);
4713
4952
  selectRef.current = select;
4714
- const [value, setValue] = useState7(() => client ? select(client) : fallback);
4715
- useEffect8(() => {
4953
+ const [value, setValue] = useState8(() => client ? select(client) : fallback);
4954
+ useEffect9(() => {
4716
4955
  if (!client) return;
4717
4956
  let last = JSON.stringify(selectRef.current(client));
4718
4957
  setValue(selectRef.current(client));
@@ -4746,15 +4985,21 @@ function useActiveGuide() {
4746
4985
  }
4747
4986
  export {
4748
4987
  DEFAULT_API_URL,
4988
+ Feature,
4989
+ FeatureFlag,
4990
+ FeatureValue,
4991
+ FlagsProvider,
4749
4992
  GuidelyClient,
4750
4993
  GuidelyOverlay,
4751
4994
  GuidelyProvider,
4752
4995
  GuidelyScreenScope,
4753
4996
  GuidelyTarget,
4997
+ InMemoryStickyBucketStore,
4754
4998
  Messenger,
4755
4999
  SDK_NAME,
4756
5000
  SDK_VERSION,
4757
5001
  TargetRegistry,
5002
+ createFlagsClient3 as createFlagsClient,
4758
5003
  createSseParser,
4759
5004
  guidelyScreenLayout,
4760
5005
  htmlToText,
@@ -4764,6 +5009,12 @@ export {
4764
5009
  targetIdsFor,
4765
5010
  useActiveGuide,
4766
5011
  useChecklistProgress,
5012
+ useFeatureValue,
5013
+ useFlag,
5014
+ useFlagDetails,
5015
+ useFlags,
5016
+ useFlagsClient,
5017
+ useFlagsReady,
4767
5018
  useGuideState,
4768
5019
  useGuidely,
4769
5020
  useGuidelyEvent,