@guidelytech/react-native 0.13.0 → 0.14.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.d.cts CHANGED
@@ -526,7 +526,12 @@ interface DeviceEnvironment {
526
526
  platform: "ios" | "android";
527
527
  deviceType: "mobile" | "tablet";
528
528
  locale?: string;
529
+ /** Host app version, detected from a linked native module (see `core/device.ts`). */
529
530
  appVersion?: string;
531
+ /** Host app build number (`CFBundleVersion` / `versionCode`). */
532
+ appBuild?: string;
533
+ /** OS version: `18.1` on iOS, `14` on Android. */
534
+ osVersion?: string;
530
535
  }
531
536
  interface GuidelyOptions {
532
537
  /** Environment public key, e.g. `pk_dev_…`. Not a secret. */
@@ -544,9 +549,13 @@ interface GuidelyOptions {
544
549
  /** Send analytics events (default `true`). Guides still render when disabled. */
545
550
  analytics?: boolean;
546
551
  consent?: ConsentState;
547
- /** Host app version for `app.version` targeting rules and `app_install` / `app_update` events (e.g. from `expo-application`). */
552
+ /**
553
+ * Host app version for `app.version` targeting rules, every event's `appVersion` and `app_install` /
554
+ * `app_update`. Detected on iOS and Android when `@guidelytech/react-native-replay`, `expo-application`
555
+ * or `react-native-device-info` is linked; pass it to override that (or when none is).
556
+ */
548
557
  appVersion?: string;
549
- /** Host app build number (`CFBundleVersion` / `versionCode`), sent as `build` with app lifecycle events. */
558
+ /** Host app build number (`CFBundleVersion` / `versionCode`), sent as `build` with app lifecycle events. Detected like `appVersion`. */
550
559
  appBuild?: string;
551
560
  /**
552
561
  * Product analytics autocapture: taps (`click`), rage taps, heatmaps, time on screen
@@ -682,7 +691,7 @@ interface UiState {
682
691
  }
683
692
 
684
693
  declare const SDK_NAME = "@guidelytech/react-native";
685
- declare const SDK_VERSION = "0.13.0";
694
+ declare const SDK_VERSION = "0.14.0";
686
695
  declare const DEFAULT_API_URL = "https://guidelytech.com";
687
696
  /** Host-platform services the engine needs; the provider supplies React Native implementations. */
688
697
  interface PlatformBridge {
@@ -872,7 +881,7 @@ declare class GuidelyClient {
872
881
  private appInfo;
873
882
  /**
874
883
  * Once per launch, as soon as a config is known: `app_install` / `app_update` from the stored
875
- * version (when the app passes `appVersion` and `storage`), then `app_open` with `coldStart: true`.
884
+ * version (when the app version is known and the app passes `storage`), then `app_open` with `coldStart: true`.
876
885
  */
877
886
  private checkLaunch;
878
887
  private wentToBackground;
@@ -1063,6 +1072,33 @@ declare class GuidelyClient {
1063
1072
  private log;
1064
1073
  }
1065
1074
 
1075
+ /**
1076
+ * Host app and OS versions, read from native modules the app already links (never imported, so none
1077
+ * of them is a dependency): `@guidelytech/react-native-replay`, `expo-application`, then
1078
+ * `react-native-device-info`. React Native itself has no API for the app's own version.
1079
+ */
1080
+ interface AppVersionInfo {
1081
+ /** `CFBundleShortVersionString` / `versionName`. */
1082
+ appVersion?: string;
1083
+ /** `CFBundleVersion` / `versionCode`. */
1084
+ appBuild?: string;
1085
+ }
1086
+ /** Where the lookups read from; the provider passes React Native's objects, tests pass fakes. */
1087
+ interface NativeSources {
1088
+ platform: {
1089
+ OS: string;
1090
+ Version?: string | number;
1091
+ constants?: Record<string, unknown>;
1092
+ };
1093
+ nativeModules: Record<string, unknown>;
1094
+ /** `globalThis.expo.modules` in Expo SDK 50+ apps. */
1095
+ expoModules?: Record<string, unknown>;
1096
+ }
1097
+ /** The host app's version and build, or `{}` when no linked module knows them. */
1098
+ declare function detectAppVersion(src: NativeSources): AppVersionInfo;
1099
+ /** The OS version: `18.1` on iOS, `14` on Android (falling back to the API level). */
1100
+ declare function detectOsVersion(src: NativeSources): string | undefined;
1101
+
1066
1102
  /**
1067
1103
  * Declares which screen its children are on, so targets on screens that are mounted but not
1068
1104
  * visible (inactive tabs, screens under a stack) are never used. `name` must match what you pass
@@ -1281,4 +1317,4 @@ declare function useGuideState(idOrKey: string): {
1281
1317
  /** The guide currently running exclusively (flow, modal, survey…), or null. */
1282
1318
  declare function useActiveGuide(): PublishedGuide | null;
1283
1319
 
1284
- export { type AutocaptureOption, type ChecklistView, DEFAULT_API_URL, type DeviceEnvironment, Feature, FeatureFlag, type FeatureProps, FeatureValue, type FeatureValueProps, FlagsProvider, type FlagsProviderProps, type GuideStepInfo, GuidelyClient, type GuidelyEventName, type GuidelyEvents, GuidelyMask, type GuidelyMaskProps, type GuidelyOptions, GuidelyOverlay, GuidelyProvider, type GuidelyProviderProps, GuidelyScreenScope, type GuidelyStorage, GuidelyTarget, type GuidelyTargetProps, HeatmapBuffer, type Insets, Messenger, type MessengerSnapshot, type MessengerTab, type MessengerThread, type MessengerView, type PlatformBridge, RageClickDetector, type ReplayCaptureOptions, type ReplayFrame, ReplayUploader, RrwebEvents, SDK_NAME, SDK_VERSION, type ScreenCapturer, SessionRecorder, type SessionReplayOptions, type StepView, type SurveyView, type TapElement, type TapInfo, type TapProperties, TapTracker, TargetRegistry, type TouchPhase, type UiState, createSseParser, guidelyScreenLayout, htmlToText, noopClient, parseHtml, positionCard, targetIdsFor, useActiveGuide, useChecklistProgress, useFeatureValue, useFlag, useFlagDetails, useFlags, useFlagsClient, useFlagsReady, useGuideState, useGuidely, useGuidelyEvent, useGuidelyIdentify, useGuidelyNavigation, useGuidelyReady, useGuidelyScreen, useGuidelyTarget };
1320
+ export { type AppVersionInfo, type AutocaptureOption, type ChecklistView, DEFAULT_API_URL, type DeviceEnvironment, Feature, FeatureFlag, type FeatureProps, FeatureValue, type FeatureValueProps, FlagsProvider, type FlagsProviderProps, type GuideStepInfo, GuidelyClient, type GuidelyEventName, type GuidelyEvents, GuidelyMask, type GuidelyMaskProps, type GuidelyOptions, GuidelyOverlay, GuidelyProvider, type GuidelyProviderProps, GuidelyScreenScope, type GuidelyStorage, GuidelyTarget, type GuidelyTargetProps, HeatmapBuffer, type Insets, Messenger, type MessengerSnapshot, type MessengerTab, type MessengerThread, type MessengerView, type NativeSources, type PlatformBridge, RageClickDetector, type ReplayCaptureOptions, type ReplayFrame, ReplayUploader, RrwebEvents, SDK_NAME, SDK_VERSION, type ScreenCapturer, SessionRecorder, type SessionReplayOptions, type StepView, type SurveyView, type TapElement, type TapInfo, type TapProperties, TapTracker, TargetRegistry, type TouchPhase, type UiState, createSseParser, detectAppVersion, detectOsVersion, guidelyScreenLayout, htmlToText, noopClient, parseHtml, positionCard, targetIdsFor, useActiveGuide, useChecklistProgress, useFeatureValue, useFlag, useFlagDetails, useFlags, useFlagsClient, useFlagsReady, useGuideState, useGuidely, useGuidelyEvent, useGuidelyIdentify, useGuidelyNavigation, useGuidelyReady, useGuidelyScreen, useGuidelyTarget };
package/dist/index.d.ts CHANGED
@@ -526,7 +526,12 @@ interface DeviceEnvironment {
526
526
  platform: "ios" | "android";
527
527
  deviceType: "mobile" | "tablet";
528
528
  locale?: string;
529
+ /** Host app version, detected from a linked native module (see `core/device.ts`). */
529
530
  appVersion?: string;
531
+ /** Host app build number (`CFBundleVersion` / `versionCode`). */
532
+ appBuild?: string;
533
+ /** OS version: `18.1` on iOS, `14` on Android. */
534
+ osVersion?: string;
530
535
  }
531
536
  interface GuidelyOptions {
532
537
  /** Environment public key, e.g. `pk_dev_…`. Not a secret. */
@@ -544,9 +549,13 @@ interface GuidelyOptions {
544
549
  /** Send analytics events (default `true`). Guides still render when disabled. */
545
550
  analytics?: boolean;
546
551
  consent?: ConsentState;
547
- /** Host app version for `app.version` targeting rules and `app_install` / `app_update` events (e.g. from `expo-application`). */
552
+ /**
553
+ * Host app version for `app.version` targeting rules, every event's `appVersion` and `app_install` /
554
+ * `app_update`. Detected on iOS and Android when `@guidelytech/react-native-replay`, `expo-application`
555
+ * or `react-native-device-info` is linked; pass it to override that (or when none is).
556
+ */
548
557
  appVersion?: string;
549
- /** Host app build number (`CFBundleVersion` / `versionCode`), sent as `build` with app lifecycle events. */
558
+ /** Host app build number (`CFBundleVersion` / `versionCode`), sent as `build` with app lifecycle events. Detected like `appVersion`. */
550
559
  appBuild?: string;
551
560
  /**
552
561
  * Product analytics autocapture: taps (`click`), rage taps, heatmaps, time on screen
@@ -682,7 +691,7 @@ interface UiState {
682
691
  }
683
692
 
684
693
  declare const SDK_NAME = "@guidelytech/react-native";
685
- declare const SDK_VERSION = "0.13.0";
694
+ declare const SDK_VERSION = "0.14.0";
686
695
  declare const DEFAULT_API_URL = "https://guidelytech.com";
687
696
  /** Host-platform services the engine needs; the provider supplies React Native implementations. */
688
697
  interface PlatformBridge {
@@ -872,7 +881,7 @@ declare class GuidelyClient {
872
881
  private appInfo;
873
882
  /**
874
883
  * Once per launch, as soon as a config is known: `app_install` / `app_update` from the stored
875
- * version (when the app passes `appVersion` and `storage`), then `app_open` with `coldStart: true`.
884
+ * version (when the app version is known and the app passes `storage`), then `app_open` with `coldStart: true`.
876
885
  */
877
886
  private checkLaunch;
878
887
  private wentToBackground;
@@ -1063,6 +1072,33 @@ declare class GuidelyClient {
1063
1072
  private log;
1064
1073
  }
1065
1074
 
1075
+ /**
1076
+ * Host app and OS versions, read from native modules the app already links (never imported, so none
1077
+ * of them is a dependency): `@guidelytech/react-native-replay`, `expo-application`, then
1078
+ * `react-native-device-info`. React Native itself has no API for the app's own version.
1079
+ */
1080
+ interface AppVersionInfo {
1081
+ /** `CFBundleShortVersionString` / `versionName`. */
1082
+ appVersion?: string;
1083
+ /** `CFBundleVersion` / `versionCode`. */
1084
+ appBuild?: string;
1085
+ }
1086
+ /** Where the lookups read from; the provider passes React Native's objects, tests pass fakes. */
1087
+ interface NativeSources {
1088
+ platform: {
1089
+ OS: string;
1090
+ Version?: string | number;
1091
+ constants?: Record<string, unknown>;
1092
+ };
1093
+ nativeModules: Record<string, unknown>;
1094
+ /** `globalThis.expo.modules` in Expo SDK 50+ apps. */
1095
+ expoModules?: Record<string, unknown>;
1096
+ }
1097
+ /** The host app's version and build, or `{}` when no linked module knows them. */
1098
+ declare function detectAppVersion(src: NativeSources): AppVersionInfo;
1099
+ /** The OS version: `18.1` on iOS, `14` on Android (falling back to the API level). */
1100
+ declare function detectOsVersion(src: NativeSources): string | undefined;
1101
+
1066
1102
  /**
1067
1103
  * Declares which screen its children are on, so targets on screens that are mounted but not
1068
1104
  * visible (inactive tabs, screens under a stack) are never used. `name` must match what you pass
@@ -1281,4 +1317,4 @@ declare function useGuideState(idOrKey: string): {
1281
1317
  /** The guide currently running exclusively (flow, modal, survey…), or null. */
1282
1318
  declare function useActiveGuide(): PublishedGuide | null;
1283
1319
 
1284
- export { type AutocaptureOption, type ChecklistView, DEFAULT_API_URL, type DeviceEnvironment, Feature, FeatureFlag, type FeatureProps, FeatureValue, type FeatureValueProps, FlagsProvider, type FlagsProviderProps, type GuideStepInfo, GuidelyClient, type GuidelyEventName, type GuidelyEvents, GuidelyMask, type GuidelyMaskProps, type GuidelyOptions, GuidelyOverlay, GuidelyProvider, type GuidelyProviderProps, GuidelyScreenScope, type GuidelyStorage, GuidelyTarget, type GuidelyTargetProps, HeatmapBuffer, type Insets, Messenger, type MessengerSnapshot, type MessengerTab, type MessengerThread, type MessengerView, type PlatformBridge, RageClickDetector, type ReplayCaptureOptions, type ReplayFrame, ReplayUploader, RrwebEvents, SDK_NAME, SDK_VERSION, type ScreenCapturer, SessionRecorder, type SessionReplayOptions, type StepView, type SurveyView, type TapElement, type TapInfo, type TapProperties, TapTracker, TargetRegistry, type TouchPhase, type UiState, createSseParser, guidelyScreenLayout, htmlToText, noopClient, parseHtml, positionCard, targetIdsFor, useActiveGuide, useChecklistProgress, useFeatureValue, useFlag, useFlagDetails, useFlags, useFlagsClient, useFlagsReady, useGuideState, useGuidely, useGuidelyEvent, useGuidelyIdentify, useGuidelyNavigation, useGuidelyReady, useGuidelyScreen, useGuidelyTarget };
1320
+ export { type AppVersionInfo, type AutocaptureOption, type ChecklistView, DEFAULT_API_URL, type DeviceEnvironment, Feature, FeatureFlag, type FeatureProps, FeatureValue, type FeatureValueProps, FlagsProvider, type FlagsProviderProps, type GuideStepInfo, GuidelyClient, type GuidelyEventName, type GuidelyEvents, GuidelyMask, type GuidelyMaskProps, type GuidelyOptions, GuidelyOverlay, GuidelyProvider, type GuidelyProviderProps, GuidelyScreenScope, type GuidelyStorage, GuidelyTarget, type GuidelyTargetProps, HeatmapBuffer, type Insets, Messenger, type MessengerSnapshot, type MessengerTab, type MessengerThread, type MessengerView, type NativeSources, type PlatformBridge, RageClickDetector, type ReplayCaptureOptions, type ReplayFrame, ReplayUploader, RrwebEvents, SDK_NAME, SDK_VERSION, type ScreenCapturer, SessionRecorder, type SessionReplayOptions, type StepView, type SurveyView, type TapElement, type TapInfo, type TapProperties, TapTracker, TargetRegistry, type TouchPhase, type UiState, createSseParser, detectAppVersion, detectOsVersion, guidelyScreenLayout, htmlToText, noopClient, parseHtml, positionCard, targetIdsFor, useActiveGuide, useChecklistProgress, useFeatureValue, useFlag, useFlagDetails, useFlags, useFlagsClient, useFlagsReady, useGuideState, useGuidely, useGuidelyEvent, useGuidelyIdentify, useGuidelyNavigation, useGuidelyReady, useGuidelyScreen, useGuidelyTarget };
package/dist/index.js CHANGED
@@ -1378,14 +1378,14 @@ function quoteValue(value) {
1378
1378
  function attrSelector(name, value) {
1379
1379
  return `[${name}=${quoteValue(value)}]`.slice(0, 300);
1380
1380
  }
1381
- function cleanText(text) {
1382
- const t = (text ?? "").replace(/\s+/g, " ").trim();
1381
+ function cleanText(text2) {
1382
+ const t = (text2 ?? "").replace(/\s+/g, " ").trim();
1383
1383
  return t ? t.slice(0, 100) : void 0;
1384
1384
  }
1385
1385
  function tapProperties(el, x, y, platform, maskAll) {
1386
1386
  const props = { selector: el.selector.slice(0, 300), tag: el.tag || "View", x: Math.round(x) || 0, y: Math.round(y) || 0, platform };
1387
- const text = maskAll ? void 0 : cleanText(el.text);
1388
- if (text) props.text = text;
1387
+ const text2 = maskAll ? void 0 : cleanText(el.text);
1388
+ if (text2) props.text = text2;
1389
1389
  return props;
1390
1390
  }
1391
1391
  var r2 = (n) => Math.round(Math.min(1, Math.max(0, n)) * 100) / 100;
@@ -2006,10 +2006,10 @@ function xhrStreamPost() {
2006
2006
  const type = xhr.getResponseHeader("content-type") || "";
2007
2007
  if (!type.includes("text/event-stream")) return;
2008
2008
  streamed = true;
2009
- const text = xhr.responseText || "";
2010
- if (text.length > seen) {
2011
- onChunk(text.slice(seen));
2012
- seen = text.length;
2009
+ const text2 = xhr.responseText || "";
2010
+ if (text2.length > seen) {
2011
+ onChunk(text2.slice(seen));
2012
+ seen = text2.length;
2013
2013
  }
2014
2014
  };
2015
2015
  xhr.open("POST", url);
@@ -2084,14 +2084,14 @@ function fetchStreamPost(fetchImpl) {
2084
2084
  if (rest) onChunk(rest);
2085
2085
  return { status: res.status, json: null, streamed: true };
2086
2086
  }
2087
- const text = await race(res.text());
2087
+ const text2 = await race(res.text());
2088
2088
  if (sse) {
2089
- onChunk(text);
2089
+ onChunk(text2);
2090
2090
  return { status: res.status, json: null, streamed: true };
2091
2091
  }
2092
2092
  let json = null;
2093
2093
  try {
2094
- json = JSON.parse(text || "null");
2094
+ json = JSON.parse(text2 || "null");
2095
2095
  } catch {
2096
2096
  json = null;
2097
2097
  }
@@ -2204,10 +2204,10 @@ var Messenger = class {
2204
2204
  if (v.kind === "tab" && v.tab === "messages") void this.refreshConversations();
2205
2205
  }
2206
2206
  /** Opens a new thread (optionally pre-filling the composer). */
2207
- showNewMessage(text = "") {
2207
+ showNewMessage(text2 = "") {
2208
2208
  this.stopThreadPolling();
2209
2209
  if (!this.snapshot.open) this.deps.onOpen();
2210
- this.set({ open: true, view: { kind: "thread", conversationId: null }, thread: { conversation: null, messages: [], streaming: {}, sending: false, error: null, draft: text } });
2210
+ this.set({ open: true, view: { kind: "thread", conversationId: null }, thread: { conversation: null, messages: [], streaming: {}, sending: false, error: null, draft: text2 } });
2211
2211
  }
2212
2212
  async openConversation(id) {
2213
2213
  const known = this.snapshot.conversations.find((c) => c.id === id) ?? null;
@@ -2251,8 +2251,8 @@ var Messenger = class {
2251
2251
  }
2252
2252
  }
2253
2253
  /** Sends the composer text: starts a conversation or posts to the open one. Streams Scout's reply. */
2254
- async send(text) {
2255
- const body = text.trim().slice(0, 4e3);
2254
+ async send(text2) {
2255
+ const body = text2.trim().slice(0, 4e3);
2256
2256
  const thread = this.snapshot.thread;
2257
2257
  if (!body || !thread || thread.sending) return;
2258
2258
  this.setThread({ sending: true, error: null, draft: "" });
@@ -2900,7 +2900,7 @@ var PersistentStore = class {
2900
2900
 
2901
2901
  // src/core/client.ts
2902
2902
  var SDK_NAME = "@guidelytech/react-native";
2903
- var SDK_VERSION = "0.13.0";
2903
+ var SDK_VERSION = "0.14.0";
2904
2904
  var DEFAULT_API_URL = "https://guidelytech.com";
2905
2905
  var PAGE_SCOPED = /* @__PURE__ */ new Set(["page_load", "url_match", "element_click", "element_visible", "delay"]);
2906
2906
  var CACHED_START_MS = 1500;
@@ -3672,12 +3672,12 @@ var GuidelyClient = class {
3672
3672
  /** `{ appVersion, build }` of this launch, without unknown keys. */
3673
3673
  appInfo() {
3674
3674
  const appVersion = this.options.appVersion ?? this.bridge.device.appVersion;
3675
- const build = this.options.appBuild;
3675
+ const build = this.options.appBuild ?? (this.options.appVersion ? void 0 : this.bridge.device.appBuild);
3676
3676
  return { ...appVersion ? { appVersion: String(appVersion) } : {}, ...build ? { build: String(build) } : {} };
3677
3677
  }
3678
3678
  /**
3679
3679
  * Once per launch, as soon as a config is known: `app_install` / `app_update` from the stored
3680
- * version (when the app passes `appVersion` and `storage`), then `app_open` with `coldStart: true`.
3680
+ * version (when the app version is known and the app passes `storage`), then `app_open` with `coldStart: true`.
3681
3681
  */
3682
3682
  checkLaunch() {
3683
3683
  if (this.launchChecked || !this.config || this.destroyed || !this.hydrated) return;
@@ -4494,8 +4494,8 @@ var GuidelyClient = class {
4494
4494
  this.messenger?.close();
4495
4495
  }
4496
4496
  /** Opens a new messenger conversation, optionally pre-filling the message. */
4497
- showNewMessage(text) {
4498
- this.whenHydrated(() => this.messenger?.showNewMessage(text));
4497
+ showNewMessage(text2) {
4498
+ this.whenHydrated(() => this.messenger?.showNewMessage(text2));
4499
4499
  }
4500
4500
  // ---------------------------------------------------------------------------
4501
4501
  // Feature flags
@@ -4519,7 +4519,7 @@ var GuidelyClient = class {
4519
4519
  /** Device facts on every screen view, read by the Pages devices / browsers breakdowns. */
4520
4520
  deviceProperties() {
4521
4521
  const d = this.bridge.device;
4522
- return { platform: d.platform, deviceType: d.deviceType, os: d.platform };
4522
+ return { platform: d.platform, deviceType: d.deviceType, os: d.platform, ...d.osVersion ? { osVersion: d.osVersion } : {} };
4523
4523
  }
4524
4524
  /** Attributes flags are evaluated with: identity, traits, account, audiences, screen and device. */
4525
4525
  flagContext() {
@@ -4530,6 +4530,7 @@ var GuidelyClient = class {
4530
4530
  deviceType: d.deviceType,
4531
4531
  locale: d.locale,
4532
4532
  appVersion: this.options.appVersion ?? d.appVersion,
4533
+ osVersion: d.osVersion,
4533
4534
  environment: this.options.environment,
4534
4535
  ...this.screenName ? { screen: this.screenName, path: this.currentUrl() } : {},
4535
4536
  ...this.user?.traits ?? {},
@@ -4620,6 +4621,51 @@ function withoutCode(guide) {
4620
4621
  return { ...guide, config: { ...guide.config, steps: guide.config.steps.map(({ code: _code, ...step }) => step) } };
4621
4622
  }
4622
4623
 
4624
+ // src/core/device.ts
4625
+ function text(value) {
4626
+ if (typeof value === "number" && Number.isFinite(value)) return String(value);
4627
+ if (typeof value !== "string") return void 0;
4628
+ const v = value.trim();
4629
+ return v ? v.slice(0, 64) : void 0;
4630
+ }
4631
+ function constant(mod, key) {
4632
+ if (!mod || typeof mod !== "object" && typeof mod !== "function") return void 0;
4633
+ const m = mod;
4634
+ try {
4635
+ const fromGetter = typeof m.getConstants === "function" ? m.getConstants()?.[key] : void 0;
4636
+ return fromGetter ?? m[key];
4637
+ } catch {
4638
+ return void 0;
4639
+ }
4640
+ }
4641
+ function read(mod, versionKey, buildKey) {
4642
+ const appVersion = text(constant(mod, versionKey));
4643
+ if (!appVersion) return null;
4644
+ const appBuild = text(constant(mod, buildKey));
4645
+ return { appVersion, ...appBuild ? { appBuild } : {} };
4646
+ }
4647
+ function inExpoGo(src) {
4648
+ const c = src.expoModules?.ExponentConstants ?? src.nativeModules.ExponentConstants;
4649
+ return constant(c, "executionEnvironment") === "storeClient" || constant(c, "appOwnership") === "expo";
4650
+ }
4651
+ function detectAppVersion(src) {
4652
+ try {
4653
+ const expo = inExpoGo(src) ? null : src.expoModules?.ExpoApplication ?? src.nativeModules.ExpoApplication;
4654
+ return read(src.nativeModules.GuidelyReplay, "appVersion", "appBuild") ?? read(expo, "nativeApplicationVersion", "nativeBuildVersion") ?? read(src.nativeModules.RNDeviceInfo, "appVersion", "buildNumber") ?? {};
4655
+ } catch {
4656
+ return {};
4657
+ }
4658
+ }
4659
+ function detectOsVersion(src) {
4660
+ try {
4661
+ const c = src.platform.constants ?? {};
4662
+ if (src.platform.OS === "android") return text(c.Release) ?? text(src.platform.Version);
4663
+ return text(c.osVersion) ?? text(src.platform.Version);
4664
+ } catch {
4665
+ return void 0;
4666
+ }
4667
+ }
4668
+
4623
4669
  // src/ui/Autocapture.tsx
4624
4670
  import { useMemo as useMemo5, useRef as useRef5 } from "react";
4625
4671
  import { View as View10 } from "react-native";
@@ -4751,8 +4797,8 @@ function describe(f, masked, opts, withText) {
4751
4797
  if (!sub.masked) {
4752
4798
  const hostProps = host?.memoizedProps && typeof host.memoizedProps === "object" ? host.memoizedProps : {};
4753
4799
  const label = props.accessibilityLabel ?? props["aria-label"] ?? hostProps.accessibilityLabel ?? hostProps["aria-label"];
4754
- const text = cleanText(sub.text) ?? (typeof label === "string" ? cleanText(label) : void 0);
4755
- if (text) el.text = text;
4800
+ const text2 = cleanText(sub.text) ?? (typeof label === "string" ? cleanText(label) : void 0);
4801
+ if (text2) el.text = text2;
4756
4802
  }
4757
4803
  }
4758
4804
  if (host) el.measure = () => measureHost(host);
@@ -4908,17 +4954,17 @@ function parseHtml(html) {
4908
4954
  flush();
4909
4955
  current = { kind, level, marker, runs: [] };
4910
4956
  };
4911
- const push = (text) => {
4912
- if (!text) return;
4957
+ const push = (text2) => {
4958
+ if (!text2) return;
4913
4959
  if (!current) current = { kind: "paragraph", level: 1, runs: [] };
4914
- const run = { text };
4960
+ const run = { text: text2 };
4915
4961
  if (style.bold) run.bold = true;
4916
4962
  if (style.italic) run.italic = true;
4917
4963
  if (style.underline) run.underline = true;
4918
4964
  if (style.code) run.code = true;
4919
4965
  if (hrefs.length) run.href = hrefs[hrefs.length - 1];
4920
4966
  const last = current.runs[current.runs.length - 1];
4921
- if (last && last.bold === run.bold && last.italic === run.italic && last.underline === run.underline && last.code === run.code && last.href === run.href) last.text += text;
4967
+ if (last && last.bold === run.bold && last.italic === run.italic && last.underline === run.underline && last.code === run.code && last.href === run.href) last.text += text2;
4922
4968
  else current.runs.push(run);
4923
4969
  };
4924
4970
  const size = (attrs, name) => {
@@ -4934,18 +4980,18 @@ function parseHtml(html) {
4934
4980
  const re = /<!--[\s\S]*?-->|<\/?([a-zA-Z][a-zA-Z0-9]*)\b([^>]*)>|([^<]+|<)/g;
4935
4981
  let m;
4936
4982
  while (m = re.exec(html || "")) {
4937
- const [token, rawTag, attrs = "", text] = m;
4938
- if (text !== void 0) {
4983
+ const [token, rawTag, attrs = "", text2] = m;
4984
+ if (text2 !== void 0) {
4939
4985
  if (skipping) continue;
4940
4986
  if (pdf) {
4941
- if (pdf.inLink) pdf.name += decodeEntities(text);
4987
+ if (pdf.inLink) pdf.name += decodeEntities(text2);
4942
4988
  continue;
4943
4989
  }
4944
4990
  if (fileLink) {
4945
- fileLink.name += decodeEntities(text);
4991
+ fileLink.name += decodeEntities(text2);
4946
4992
  continue;
4947
4993
  }
4948
- push(decodeEntities(text.replace(/\s+/g, " ")));
4994
+ push(decodeEntities(text2.replace(/\s+/g, " ")));
4949
4995
  continue;
4950
4996
  }
4951
4997
  if (!rawTag) continue;
@@ -5519,13 +5565,13 @@ function Conversations({ store, palette: p }) {
5519
5565
  /* @__PURE__ */ jsx5(GButton, { label: "New conversation", palette: p, onPress: () => store.showNewMessage(), style: styles4.fullWidth })
5520
5566
  ] });
5521
5567
  }
5522
- function Bubble({ m, text, name, palette: p, onArticle, onGuide }) {
5568
+ function Bubble({ m, text: text2, name, palette: p, onArticle, onGuide }) {
5523
5569
  if (m.author === "system")
5524
- return /* @__PURE__ */ jsx5(Text4, { accessibilityRole: "text", style: [styles4.system, { color: p.muted }], children: text });
5570
+ return /* @__PURE__ */ jsx5(Text4, { accessibilityRole: "text", style: [styles4.system, { color: p.muted }], children: text2 });
5525
5571
  const mine = m.author === "user";
5526
5572
  return /* @__PURE__ */ jsxs3(View4, { style: [styles4.bubbleWrap, mine ? styles4.mine : styles4.theirs], children: [
5527
5573
  !mine ? /* @__PURE__ */ jsx5(Text4, { style: [styles4.author, { color: p.muted }], children: m.author === "agent" ? m.authorName || "Team" : name }) : null,
5528
- /* @__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 }) }),
5574
+ /* @__PURE__ */ jsx5(View4, { style: [styles4.bubble, { borderRadius: Math.max(12, p.radius) }, mine ? { backgroundColor: p.primary } : { backgroundColor: p.surface }], children: m.status === "pending" && !text2 ? /* @__PURE__ */ jsx5(TypingDots, { color: p.muted }) : /* @__PURE__ */ jsx5(Text4, { style: [styles4.bubbleText, { color: mine ? p.onPrimary : p.text }], children: text2 }) }),
5529
5575
  m.citations.length || m.suggestedGuides.length ? /* @__PURE__ */ jsxs3(View4, { style: styles4.chips, children: [
5530
5576
  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: [
5531
5577
  "\u{1F4C4} ",
@@ -5544,7 +5590,7 @@ function TypingDots({ color }) {
5544
5590
  function Thread({ store, palette: p, bottomInset }) {
5545
5591
  const snap = useMessenger(store);
5546
5592
  const t = snap.thread;
5547
- const [text, setText] = useState3(t?.draft ?? "");
5593
+ const [text2, setText] = useState3(t?.draft ?? "");
5548
5594
  const scroll = useRef2(null);
5549
5595
  const convo = t?.conversation ?? null;
5550
5596
  useEffect3(() => {
@@ -5553,7 +5599,7 @@ function Thread({ store, palette: p, bottomInset }) {
5553
5599
  const streamingIds = useMemo3(() => Object.keys(t?.streaming ?? {}).filter((id) => !t?.messages.some((m) => m.id === id)), [t]);
5554
5600
  if (!t) return null;
5555
5601
  const send = () => {
5556
- const value = text;
5602
+ const value = text2;
5557
5603
  setText("");
5558
5604
  void store.send(value);
5559
5605
  };
@@ -5575,7 +5621,7 @@ function Thread({ store, palette: p, bottomInset }) {
5575
5621
  /* @__PURE__ */ jsx5(
5576
5622
  TextInput,
5577
5623
  {
5578
- value: text,
5624
+ value: text2,
5579
5625
  onChangeText: (v) => setText(v.slice(0, 4e3)),
5580
5626
  placeholder: convo ? "Reply\u2026" : "Write a message\u2026",
5581
5627
  placeholderTextColor: p.muted,
@@ -5584,7 +5630,7 @@ function Thread({ store, palette: p, bottomInset }) {
5584
5630
  style: [styles4.input, { color: p.text, backgroundColor: p.surface, borderRadius: Math.min(18, p.radius + 6) }]
5585
5631
  }
5586
5632
  ),
5587
- /* @__PURE__ */ jsx5(GButton, { label: "Send", palette: p, onPress: send, disabled: !text.trim() || t.sending })
5633
+ /* @__PURE__ */ jsx5(GButton, { label: "Send", palette: p, onPress: send, disabled: !text2.trim() || t.sending })
5588
5634
  ] })
5589
5635
  ] });
5590
5636
  }
@@ -6498,15 +6544,15 @@ function palette(theme, systemScheme, explicit) {
6498
6544
  const wantsDark = theme.dark === "dark" || theme.dark === "auto" && systemScheme === "dark";
6499
6545
  const dark = wantsDark && !explicit?.backgroundColor;
6500
6546
  const background = dark ? DARK.background : theme.backgroundColor ?? "#ffffff";
6501
- const text = dark ? DARK.text : theme.textColor ?? "#111827";
6547
+ const text2 = dark ? DARK.text : theme.textColor ?? "#111827";
6502
6548
  return {
6503
6549
  primary,
6504
6550
  onPrimary: luminance(primary) > 0.45 ? "#111827" : "#ffffff",
6505
6551
  background,
6506
- text,
6507
- muted: dark ? DARK.muted : mix(text, 0.65),
6508
- border: dark ? DARK.border : mix(text, 0.12),
6509
- surface: dark ? DARK.surface : mix(text, 0.05),
6552
+ text: text2,
6553
+ muted: dark ? DARK.muted : mix(text2, 0.65),
6554
+ border: dark ? DARK.border : mix(text2, 0.12),
6555
+ surface: dark ? DARK.surface : mix(text2, 0.05),
6510
6556
  radius: theme.borderRadius ?? 12,
6511
6557
  overlay: `rgba(15, 23, 42, ${theme.overlayOpacity ?? 0.55})`,
6512
6558
  fontFamily: nativeFont(theme.fontFamily)
@@ -6733,14 +6779,25 @@ function deviceLocale() {
6733
6779
  return void 0;
6734
6780
  }
6735
6781
  }
6736
- function defaultBridge(appVersion) {
6782
+ function nativeSources() {
6783
+ return {
6784
+ platform: Platform6,
6785
+ nativeModules: NativeModules,
6786
+ expoModules: globalThis.expo?.modules
6787
+ };
6788
+ }
6789
+ function defaultBridge(appVersion, appBuild) {
6737
6790
  const { width, height } = Dimensions.get("window");
6791
+ const sources = nativeSources();
6792
+ const detected = appVersion ? {} : detectAppVersion(sources);
6738
6793
  return {
6739
6794
  device: {
6740
6795
  platform: Platform6.OS === "android" ? "android" : "ios",
6741
6796
  deviceType: Platform6.OS === "ios" && Platform6.isPad || Math.min(width, height) >= 600 ? "tablet" : "mobile",
6742
6797
  locale: deviceLocale(),
6743
- appVersion
6798
+ appVersion: appVersion ?? detected.appVersion,
6799
+ appBuild: appBuild ?? detected.appBuild,
6800
+ osVersion: detectOsVersion(sources)
6744
6801
  },
6745
6802
  openExternal: (url) => void Linking.openURL(url).catch(() => {
6746
6803
  }),
@@ -6785,7 +6842,7 @@ function GuidelyProvider({ children, user, insets, launcherOffset = 72, flagsPay
6785
6842
  onNavigate: (url) => optionsRef.current.onNavigate?.(url),
6786
6843
  onError: (err) => optionsRef.current.onError?.(err)
6787
6844
  },
6788
- defaultBridge(latest.appVersion)
6845
+ defaultBridge(latest.appVersion, latest.appBuild)
6789
6846
  );
6790
6847
  } catch (err) {
6791
6848
  optionsRef.current.onError?.(err instanceof Error ? err : new Error(String(err)));
@@ -6987,6 +7044,8 @@ export {
6987
7044
  TargetRegistry,
6988
7045
  createFlagsClient3 as createFlagsClient,
6989
7046
  createSseParser,
7047
+ detectAppVersion,
7048
+ detectOsVersion,
6990
7049
  guidelyScreenLayout,
6991
7050
  htmlToText,
6992
7051
  noopClient,