@bbearai/react-native 0.3.6 → 0.3.8

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.
Files changed (3) hide show
  1. package/dist/index.js +316 -167
  2. package/dist/index.mjs +316 -167
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11392,6 +11392,239 @@ function shouldShowDeprecationWarning() {
11392
11392
  if (shouldShowDeprecationWarning()) console.warn("\u26A0\uFE0F Node.js 18 and below are deprecated and will no longer be supported in future versions of @supabase/supabase-js. Please upgrade to Node.js 20 or later. For more information, visit: https://github.com/orgs/supabase/discussions/37217");
11393
11393
 
11394
11394
  // ../core/dist/index.mjs
11395
+ var MAX_CONSOLE_LOGS = 50;
11396
+ var MAX_NETWORK_REQUESTS = 20;
11397
+ var MAX_NAVIGATION_HISTORY = 20;
11398
+ var MAX_RESPONSE_BODY_LENGTH = 500;
11399
+ var ContextCaptureManager = class {
11400
+ constructor() {
11401
+ this.consoleLogs = [];
11402
+ this.networkRequests = [];
11403
+ this.navigationHistory = [];
11404
+ this.originalConsole = {};
11405
+ this.isCapturing = false;
11406
+ }
11407
+ /**
11408
+ * Start capturing console logs, network requests, and navigation
11409
+ */
11410
+ startCapture() {
11411
+ if (this.isCapturing) return;
11412
+ this.isCapturing = true;
11413
+ this.captureConsole();
11414
+ this.captureFetch();
11415
+ this.captureNavigation();
11416
+ }
11417
+ /**
11418
+ * Stop capturing and restore original functions
11419
+ */
11420
+ stopCapture() {
11421
+ if (!this.isCapturing) return;
11422
+ this.isCapturing = false;
11423
+ if (this.originalConsole.log) console.log = this.originalConsole.log;
11424
+ if (this.originalConsole.warn) console.warn = this.originalConsole.warn;
11425
+ if (this.originalConsole.error) console.error = this.originalConsole.error;
11426
+ if (this.originalConsole.info) console.info = this.originalConsole.info;
11427
+ if (this.originalFetch && typeof window !== "undefined") {
11428
+ window.fetch = this.originalFetch;
11429
+ }
11430
+ if (typeof window !== "undefined" && typeof history !== "undefined") {
11431
+ if (this.originalPushState) {
11432
+ history.pushState = this.originalPushState;
11433
+ }
11434
+ if (this.originalReplaceState) {
11435
+ history.replaceState = this.originalReplaceState;
11436
+ }
11437
+ if (this.popstateHandler) {
11438
+ window.removeEventListener("popstate", this.popstateHandler);
11439
+ }
11440
+ }
11441
+ }
11442
+ /**
11443
+ * Get captured context for a bug report
11444
+ */
11445
+ getEnhancedContext() {
11446
+ return {
11447
+ consoleLogs: [...this.consoleLogs],
11448
+ networkRequests: [...this.networkRequests],
11449
+ navigationHistory: [...this.navigationHistory],
11450
+ performanceMetrics: this.getPerformanceMetrics(),
11451
+ environment: this.getEnvironmentInfo()
11452
+ };
11453
+ }
11454
+ /**
11455
+ * Get the auto-captured navigation history
11456
+ */
11457
+ getNavigationHistory() {
11458
+ return [...this.navigationHistory];
11459
+ }
11460
+ /**
11461
+ * Get the current route (last entry in navigation history, or window.location)
11462
+ */
11463
+ getCurrentRoute() {
11464
+ if (this.navigationHistory.length > 0) {
11465
+ return this.navigationHistory[this.navigationHistory.length - 1];
11466
+ }
11467
+ if (typeof window !== "undefined") {
11468
+ return window.location.pathname;
11469
+ }
11470
+ return "unknown";
11471
+ }
11472
+ /**
11473
+ * Manually track a navigation event (for React Native or custom routing)
11474
+ */
11475
+ trackNavigation(route) {
11476
+ const last = this.navigationHistory[this.navigationHistory.length - 1];
11477
+ if (route === last) return;
11478
+ this.navigationHistory.push(route);
11479
+ if (this.navigationHistory.length > MAX_NAVIGATION_HISTORY) {
11480
+ this.navigationHistory.shift();
11481
+ }
11482
+ }
11483
+ /**
11484
+ * Clear captured data
11485
+ */
11486
+ clear() {
11487
+ this.consoleLogs = [];
11488
+ this.networkRequests = [];
11489
+ this.navigationHistory = [];
11490
+ }
11491
+ /**
11492
+ * Add a log entry manually (for custom logging)
11493
+ */
11494
+ addLog(level, message, args) {
11495
+ this.consoleLogs.push({
11496
+ level,
11497
+ message,
11498
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11499
+ args
11500
+ });
11501
+ if (this.consoleLogs.length > MAX_CONSOLE_LOGS) {
11502
+ this.consoleLogs = this.consoleLogs.slice(-MAX_CONSOLE_LOGS);
11503
+ }
11504
+ }
11505
+ /**
11506
+ * Add a network request manually
11507
+ */
11508
+ addNetworkRequest(request) {
11509
+ this.networkRequests.push(request);
11510
+ if (this.networkRequests.length > MAX_NETWORK_REQUESTS) {
11511
+ this.networkRequests = this.networkRequests.slice(-MAX_NETWORK_REQUESTS);
11512
+ }
11513
+ }
11514
+ captureConsole() {
11515
+ if (typeof console === "undefined") return;
11516
+ const levels = ["log", "warn", "error", "info"];
11517
+ levels.forEach((level) => {
11518
+ this.originalConsole[level] = console[level];
11519
+ console[level] = (...args) => {
11520
+ this.originalConsole[level]?.apply(console, args);
11521
+ try {
11522
+ const message = args.map((arg) => {
11523
+ if (typeof arg === "string") return arg;
11524
+ try {
11525
+ return JSON.stringify(arg);
11526
+ } catch {
11527
+ return String(arg);
11528
+ }
11529
+ }).join(" ");
11530
+ this.addLog(level, message.slice(0, 500));
11531
+ } catch {
11532
+ }
11533
+ };
11534
+ });
11535
+ }
11536
+ captureFetch() {
11537
+ if (typeof window === "undefined" || typeof fetch === "undefined") return;
11538
+ this.originalFetch = window.fetch;
11539
+ const self2 = this;
11540
+ window.fetch = async function(input, init) {
11541
+ const startTime = Date.now();
11542
+ const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
11543
+ const method = init?.method || "GET";
11544
+ try {
11545
+ const response = await self2.originalFetch.call(window, input, init);
11546
+ const requestEntry = {
11547
+ method,
11548
+ url: url.slice(0, 200),
11549
+ // Limit URL length
11550
+ status: response.status,
11551
+ duration: Date.now() - startTime,
11552
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
11553
+ };
11554
+ if (response.status >= 400) {
11555
+ try {
11556
+ const cloned = response.clone();
11557
+ const body = await cloned.text();
11558
+ if (body) {
11559
+ requestEntry.responseBody = body.slice(0, MAX_RESPONSE_BODY_LENGTH);
11560
+ }
11561
+ } catch {
11562
+ }
11563
+ }
11564
+ self2.addNetworkRequest(requestEntry);
11565
+ return response;
11566
+ } catch (error) {
11567
+ self2.addNetworkRequest({
11568
+ method,
11569
+ url: url.slice(0, 200),
11570
+ duration: Date.now() - startTime,
11571
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11572
+ error: error instanceof Error ? error.message : "Unknown error"
11573
+ });
11574
+ throw error;
11575
+ }
11576
+ };
11577
+ }
11578
+ captureNavigation() {
11579
+ if (typeof window === "undefined" || typeof history === "undefined") return;
11580
+ this.trackNavigation(window.location.pathname);
11581
+ const self2 = this;
11582
+ this.originalPushState = history.pushState;
11583
+ history.pushState = function(...args) {
11584
+ self2.originalPushState.apply(history, args);
11585
+ self2.trackNavigation(window.location.pathname);
11586
+ };
11587
+ this.originalReplaceState = history.replaceState;
11588
+ history.replaceState = function(...args) {
11589
+ self2.originalReplaceState.apply(history, args);
11590
+ self2.trackNavigation(window.location.pathname);
11591
+ };
11592
+ this.popstateHandler = () => {
11593
+ self2.trackNavigation(window.location.pathname);
11594
+ };
11595
+ window.addEventListener("popstate", this.popstateHandler);
11596
+ }
11597
+ getPerformanceMetrics() {
11598
+ if (typeof window === "undefined" || typeof performance === "undefined") return void 0;
11599
+ const metrics = {};
11600
+ try {
11601
+ const navigation = performance.getEntriesByType("navigation")[0];
11602
+ if (navigation) {
11603
+ metrics.pageLoadTime = Math.round(navigation.loadEventEnd - navigation.startTime);
11604
+ }
11605
+ } catch {
11606
+ }
11607
+ try {
11608
+ const memory = performance.memory;
11609
+ if (memory) {
11610
+ metrics.memoryUsage = Math.round(memory.usedJSHeapSize / 1024 / 1024);
11611
+ }
11612
+ } catch {
11613
+ }
11614
+ return Object.keys(metrics).length > 0 ? metrics : void 0;
11615
+ }
11616
+ getEnvironmentInfo() {
11617
+ if (typeof window === "undefined" || typeof navigator === "undefined") return void 0;
11618
+ return {
11619
+ language: navigator.language,
11620
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
11621
+ cookiesEnabled: navigator.cookieEnabled,
11622
+ localStorage: typeof localStorage !== "undefined",
11623
+ online: navigator.onLine
11624
+ };
11625
+ }
11626
+ };
11627
+ var contextCapture = new ContextCaptureManager();
11395
11628
  var DEFAULT_SUPABASE_URL = "https://kyxgzjnqgvapvlnvqawz.supabase.co";
11396
11629
  var getEnvVar = (key) => {
11397
11630
  try {
@@ -11413,22 +11646,40 @@ var BugBearClient = class {
11413
11646
  );
11414
11647
  }
11415
11648
  /**
11416
- * Track navigation for context
11649
+ * Track navigation for context.
11650
+ * Also forwards to contextCapture for auto-tracked navigation.
11417
11651
  */
11418
11652
  trackNavigation(route) {
11419
11653
  this.navigationHistory.push(route);
11420
11654
  if (this.navigationHistory.length > 10) {
11421
11655
  this.navigationHistory.shift();
11422
11656
  }
11657
+ contextCapture.trackNavigation(route);
11423
11658
  }
11424
11659
  /**
11425
- * Get current navigation history
11660
+ * Get current navigation history.
11661
+ * Priority: config callback > manual tracking > auto-captured (pushState/popstate)
11426
11662
  */
11427
11663
  getNavigationHistory() {
11428
11664
  if (this.config.getNavigationHistory) {
11429
11665
  return this.config.getNavigationHistory();
11430
11666
  }
11431
- return [...this.navigationHistory];
11667
+ if (this.navigationHistory.length > 0) {
11668
+ return [...this.navigationHistory];
11669
+ }
11670
+ return contextCapture.getNavigationHistory();
11671
+ }
11672
+ /**
11673
+ * Get current app context.
11674
+ * Uses config.getAppContext() callback if provided, otherwise builds from available data.
11675
+ */
11676
+ getAppContext() {
11677
+ if (this.config.getAppContext) {
11678
+ return this.config.getAppContext();
11679
+ }
11680
+ return {
11681
+ currentRoute: contextCapture.getCurrentRoute()
11682
+ };
11432
11683
  }
11433
11684
  /**
11434
11685
  * Get current user info from host app or BugBear's own auth
@@ -11468,6 +11719,8 @@ var BugBearClient = class {
11468
11719
  project_id: this.config.projectId,
11469
11720
  reporter_id: userInfo.id,
11470
11721
  // User ID from host app (required)
11722
+ reporter_name: testerInfo?.name || userInfo.name || null,
11723
+ reporter_email: userInfo.email || null,
11471
11724
  tester_id: testerInfo?.id || null,
11472
11725
  // Tester record ID (optional)
11473
11726
  report_type: report.type,
@@ -11481,6 +11734,7 @@ var BugBearClient = class {
11481
11734
  app_context: report.appContext,
11482
11735
  device_info: report.deviceInfo || this.getDeviceInfo(),
11483
11736
  navigation_history: this.getNavigationHistory(),
11737
+ enhanced_context: report.enhancedContext || contextCapture.getEnhancedContext(),
11484
11738
  assignment_id: report.assignmentId,
11485
11739
  test_case_id: report.testCaseId
11486
11740
  };
@@ -11543,7 +11797,7 @@ var BugBearClient = class {
11543
11797
  console.error("BugBear: Failed to fetch assignments", error);
11544
11798
  return [];
11545
11799
  }
11546
- return (data || []).map((item) => ({
11800
+ const mapped = (data || []).map((item) => ({
11547
11801
  id: item.id,
11548
11802
  status: item.status,
11549
11803
  startedAt: item.started_at,
@@ -11576,6 +11830,12 @@ var BugBearClient = class {
11576
11830
  } : void 0
11577
11831
  }
11578
11832
  }));
11833
+ mapped.sort((a, b) => {
11834
+ if (a.isVerification && !b.isVerification) return -1;
11835
+ if (!a.isVerification && b.isVerification) return 1;
11836
+ return 0;
11837
+ });
11838
+ return mapped;
11579
11839
  } catch (err) {
11580
11840
  console.error("BugBear: Error fetching assignments", err);
11581
11841
  return [];
@@ -12708,163 +12968,6 @@ var BugBearClient = class {
12708
12968
  function createBugBear(config) {
12709
12969
  return new BugBearClient(config);
12710
12970
  }
12711
- var MAX_CONSOLE_LOGS = 50;
12712
- var MAX_NETWORK_REQUESTS = 20;
12713
- var ContextCaptureManager = class {
12714
- constructor() {
12715
- this.consoleLogs = [];
12716
- this.networkRequests = [];
12717
- this.originalConsole = {};
12718
- this.isCapturing = false;
12719
- }
12720
- /**
12721
- * Start capturing console logs and network requests
12722
- */
12723
- startCapture() {
12724
- if (this.isCapturing) return;
12725
- this.isCapturing = true;
12726
- this.captureConsole();
12727
- this.captureFetch();
12728
- }
12729
- /**
12730
- * Stop capturing and restore original functions
12731
- */
12732
- stopCapture() {
12733
- if (!this.isCapturing) return;
12734
- this.isCapturing = false;
12735
- if (this.originalConsole.log) console.log = this.originalConsole.log;
12736
- if (this.originalConsole.warn) console.warn = this.originalConsole.warn;
12737
- if (this.originalConsole.error) console.error = this.originalConsole.error;
12738
- if (this.originalConsole.info) console.info = this.originalConsole.info;
12739
- if (this.originalFetch && typeof window !== "undefined") {
12740
- window.fetch = this.originalFetch;
12741
- }
12742
- }
12743
- /**
12744
- * Get captured context for a bug report
12745
- */
12746
- getEnhancedContext() {
12747
- return {
12748
- consoleLogs: [...this.consoleLogs],
12749
- networkRequests: [...this.networkRequests],
12750
- performanceMetrics: this.getPerformanceMetrics(),
12751
- environment: this.getEnvironmentInfo()
12752
- };
12753
- }
12754
- /**
12755
- * Clear captured data
12756
- */
12757
- clear() {
12758
- this.consoleLogs = [];
12759
- this.networkRequests = [];
12760
- }
12761
- /**
12762
- * Add a log entry manually (for custom logging)
12763
- */
12764
- addLog(level, message, args) {
12765
- this.consoleLogs.push({
12766
- level,
12767
- message,
12768
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
12769
- args
12770
- });
12771
- if (this.consoleLogs.length > MAX_CONSOLE_LOGS) {
12772
- this.consoleLogs = this.consoleLogs.slice(-MAX_CONSOLE_LOGS);
12773
- }
12774
- }
12775
- /**
12776
- * Add a network request manually
12777
- */
12778
- addNetworkRequest(request) {
12779
- this.networkRequests.push(request);
12780
- if (this.networkRequests.length > MAX_NETWORK_REQUESTS) {
12781
- this.networkRequests = this.networkRequests.slice(-MAX_NETWORK_REQUESTS);
12782
- }
12783
- }
12784
- captureConsole() {
12785
- if (typeof console === "undefined") return;
12786
- const levels = ["log", "warn", "error", "info"];
12787
- levels.forEach((level) => {
12788
- this.originalConsole[level] = console[level];
12789
- console[level] = (...args) => {
12790
- this.originalConsole[level]?.apply(console, args);
12791
- try {
12792
- const message = args.map((arg) => {
12793
- if (typeof arg === "string") return arg;
12794
- try {
12795
- return JSON.stringify(arg);
12796
- } catch {
12797
- return String(arg);
12798
- }
12799
- }).join(" ");
12800
- this.addLog(level, message.slice(0, 500));
12801
- } catch {
12802
- }
12803
- };
12804
- });
12805
- }
12806
- captureFetch() {
12807
- if (typeof window === "undefined" || typeof fetch === "undefined") return;
12808
- this.originalFetch = window.fetch;
12809
- const self2 = this;
12810
- window.fetch = async function(input, init) {
12811
- const startTime = Date.now();
12812
- const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
12813
- const method = init?.method || "GET";
12814
- try {
12815
- const response = await self2.originalFetch.call(window, input, init);
12816
- self2.addNetworkRequest({
12817
- method,
12818
- url: url.slice(0, 200),
12819
- // Limit URL length
12820
- status: response.status,
12821
- duration: Date.now() - startTime,
12822
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
12823
- });
12824
- return response;
12825
- } catch (error) {
12826
- self2.addNetworkRequest({
12827
- method,
12828
- url: url.slice(0, 200),
12829
- duration: Date.now() - startTime,
12830
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
12831
- error: error instanceof Error ? error.message : "Unknown error"
12832
- });
12833
- throw error;
12834
- }
12835
- };
12836
- }
12837
- getPerformanceMetrics() {
12838
- if (typeof window === "undefined" || typeof performance === "undefined") return void 0;
12839
- const metrics = {};
12840
- try {
12841
- const navigation = performance.getEntriesByType("navigation")[0];
12842
- if (navigation) {
12843
- metrics.pageLoadTime = Math.round(navigation.loadEventEnd - navigation.startTime);
12844
- }
12845
- } catch {
12846
- }
12847
- try {
12848
- const memory = performance.memory;
12849
- if (memory) {
12850
- metrics.memoryUsage = Math.round(memory.usedJSHeapSize / 1024 / 1024);
12851
- }
12852
- } catch {
12853
- }
12854
- return Object.keys(metrics).length > 0 ? metrics : void 0;
12855
- }
12856
- getEnvironmentInfo() {
12857
- if (typeof window === "undefined" || typeof navigator === "undefined") return void 0;
12858
- return {
12859
- language: navigator.language,
12860
- timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
12861
- cookiesEnabled: navigator.cookieEnabled,
12862
- localStorage: typeof localStorage !== "undefined",
12863
- online: navigator.onLine
12864
- };
12865
- }
12866
- };
12867
- var contextCapture = new ContextCaptureManager();
12868
12971
 
12869
12972
  // src/BugBearProvider.tsx
12870
12973
  var import_react_native = require("react-native");
@@ -13065,6 +13168,7 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
13065
13168
  (0, import_react.useEffect)(() => {
13066
13169
  if (enabled && !hasInitialized.current) {
13067
13170
  hasInitialized.current = true;
13171
+ contextCapture.startCapture();
13068
13172
  const newClient = createBugBear(config);
13069
13173
  setClient(newClient);
13070
13174
  initializeBugBear(newClient);
@@ -13344,7 +13448,9 @@ function HomeScreen({ nav }) {
13344
13448
  refreshAssignments();
13345
13449
  refreshThreads();
13346
13450
  }, []);
13347
- const pendingCount = assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length;
13451
+ const pendingAssignments = assignments.filter((a) => a.status === "pending" || a.status === "in_progress");
13452
+ const pendingCount = pendingAssignments.length;
13453
+ const retestCount = pendingAssignments.filter((a) => a.isVerification).length;
13348
13454
  const completedCount = assignments.filter((a) => a.status === "passed" || a.status === "failed").length;
13349
13455
  const totalTests = assignments.length;
13350
13456
  return /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, null, pendingCount > 0 ? /* @__PURE__ */ import_react3.default.createElement(
@@ -13356,6 +13462,7 @@ function HomeScreen({ nav }) {
13356
13462
  },
13357
13463
  /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.heroCount }, pendingCount),
13358
13464
  /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.heroLabel }, "test", pendingCount !== 1 ? "s" : "", " waiting"),
13465
+ retestCount > 0 && /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: styles.retestPill }, /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.retestPillText }, "\u{1F504} ", retestCount, " retest", retestCount !== 1 ? "s" : "")),
13359
13466
  /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.heroAction }, "Start Testing \u2192")
13360
13467
  ) : unreadCount > 0 ? /* @__PURE__ */ import_react3.default.createElement(
13361
13468
  import_react_native3.TouchableOpacity,
@@ -13459,6 +13566,22 @@ var styles = import_react_native3.StyleSheet.create({
13459
13566
  color: colors.blueText,
13460
13567
  marginTop: 2
13461
13568
  },
13569
+ retestPill: {
13570
+ flexDirection: "row",
13571
+ alignItems: "center",
13572
+ backgroundColor: "#422006",
13573
+ borderWidth: 1,
13574
+ borderColor: "#854d0e",
13575
+ borderRadius: 6,
13576
+ paddingHorizontal: 8,
13577
+ paddingVertical: 3,
13578
+ marginTop: 8
13579
+ },
13580
+ retestPillText: {
13581
+ fontSize: 12,
13582
+ fontWeight: "600",
13583
+ color: "#fbbf24"
13584
+ },
13462
13585
  heroAction: {
13463
13586
  fontSize: 14,
13464
13587
  fontWeight: "600",
@@ -13669,7 +13792,7 @@ function TestDetailScreen({ testId, nav }) {
13669
13792
  const steps = testCase.steps;
13670
13793
  const info = templateInfo[template] || templateInfo.steps;
13671
13794
  const rubricMode = testCase.track?.rubricMode || "pass_fail";
13672
- return /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.container }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.topRow }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.positionInfo }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.positionText }, "Test ", currentIndex + 1, " of ", allTests.length), displayedAssignment.status === "in_progress" && assignmentElapsedTime > 0 && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.timerBadge }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.timerText }, formatElapsedTime(assignmentElapsedTime)))), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.TouchableOpacity, { onPress: () => nav.push({ name: "TEST_LIST" }) }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.viewAllLink }, "View All \u2192"))), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.testTitle }, testCase.title), testCase.key && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.testKey }, testCase.key), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.TouchableOpacity, { onPress: () => setShowSteps(!showSteps), style: styles2.sectionHeader }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.sectionHeaderText }, showSteps ? "\u25BC" : "\u25B6", " ", info.icon, " ", template === "freeform" ? "Instructions" : `${steps.length} ${template === "checklist" ? "items" : template === "rubric" ? "criteria" : "steps"}`)), showSteps && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.templateContent }, template === "steps" && steps.map((step, idx) => /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { key: idx, style: styles2.step }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.stepNumber }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.stepNumberText }, step.stepNumber)), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.stepBody }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.stepAction }, step.action), step.expectedResult && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.stepExpected }, "\u2192 ", step.expectedResult)))), template === "checklist" && /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, steps.map((step, idx) => /* @__PURE__ */ import_react4.default.createElement(
13795
+ return /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.container }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.topRow }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.positionInfo }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.positionText }, "Test ", currentIndex + 1, " of ", allTests.length), displayedAssignment.status === "in_progress" && assignmentElapsedTime > 0 && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.timerBadge }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.timerText }, formatElapsedTime(assignmentElapsedTime)))), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.TouchableOpacity, { onPress: () => nav.push({ name: "TEST_LIST" }) }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.viewAllLink }, "View All \u2192"))), displayedAssignment.isVerification && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.retestBanner }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.retestIcon }, "\u{1F504}"), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.retestLabel }, "Retest"), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.retestSub }, "\u2014 Verify bug fix")), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.testTitle }, testCase.title), testCase.key && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.testKey }, testCase.key), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.TouchableOpacity, { onPress: () => setShowSteps(!showSteps), style: styles2.sectionHeader }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.sectionHeaderText }, showSteps ? "\u25BC" : "\u25B6", " ", info.icon, " ", template === "freeform" ? "Instructions" : `${steps.length} ${template === "checklist" ? "items" : template === "rubric" ? "criteria" : "steps"}`)), showSteps && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.templateContent }, template === "steps" && steps.map((step, idx) => /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { key: idx, style: styles2.step }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.stepNumber }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.stepNumberText }, step.stepNumber)), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.stepBody }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.stepAction }, step.action), step.expectedResult && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.stepExpected }, "\u2192 ", step.expectedResult)))), template === "checklist" && /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, steps.map((step, idx) => /* @__PURE__ */ import_react4.default.createElement(
13673
13796
  import_react_native4.TouchableOpacity,
13674
13797
  {
13675
13798
  key: idx,
@@ -13753,6 +13876,10 @@ function TestDetailScreen({ testId, nav }) {
13753
13876
  }
13754
13877
  var styles2 = import_react_native4.StyleSheet.create({
13755
13878
  container: { paddingBottom: 16 },
13879
+ retestBanner: { flexDirection: "row", alignItems: "center", gap: 6, backgroundColor: "#422006", borderWidth: 1, borderColor: "#854d0e", borderRadius: 8, paddingVertical: 6, paddingHorizontal: 10, marginBottom: 10 },
13880
+ retestIcon: { fontSize: 14 },
13881
+ retestLabel: { fontSize: 13, fontWeight: "600", color: "#fbbf24" },
13882
+ retestSub: { fontSize: 12, color: "#d97706" },
13756
13883
  topRow: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", marginBottom: 12 },
13757
13884
  positionInfo: { flexDirection: "row", alignItems: "center", gap: 8 },
13758
13885
  positionText: { fontSize: 13, color: colors.textMuted },
@@ -13868,6 +13995,8 @@ function TestListScreen({ nav }) {
13868
13995
  const statusOrder = { in_progress: 0, pending: 1, failed: 2, skipped: 3, passed: 4 };
13869
13996
  for (const folder of groups.values()) {
13870
13997
  folder.assignments.sort((a, b) => {
13998
+ if (a.isVerification && !b.isVerification) return -1;
13999
+ if (!a.isVerification && b.isVerification) return 1;
13871
14000
  const sd = (statusOrder[a.status] ?? 5) - (statusOrder[b.status] ?? 5);
13872
14001
  if (sd !== 0) return sd;
13873
14002
  return (priorityOrder[a.testCase.priority] ?? 4) - (priorityOrder[b.testCase.priority] ?? 4);
@@ -13909,7 +14038,7 @@ function TestListScreen({ nav }) {
13909
14038
  onPress: () => nav.push({ name: "TEST_DETAIL", testId: assignment.id })
13910
14039
  },
13911
14040
  /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.testBadge }, badge.icon),
13912
- /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.testInfo }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.testTitle, numberOfLines: 1 }, assignment.testCase.title), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.testMeta }, assignment.testCase.key, " \xB7 ", assignment.testCase.priority))
14041
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.testInfo }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.testTitle, numberOfLines: 1 }, assignment.testCase.title), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.testMetaRow }, assignment.isVerification && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.retestTag }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.retestTagText }, "Retest")), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.testMeta }, assignment.testCase.key, " \xB7 ", assignment.testCase.priority)))
13913
14042
  );
13914
14043
  }));
13915
14044
  }), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: styles3.refreshBtn, onPress: refreshAssignments }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.refreshText }, "\u21BB Refresh")));
@@ -13932,6 +14061,9 @@ var styles3 = import_react_native5.StyleSheet.create({
13932
14061
  testBadge: { fontSize: 16, marginRight: 10, width: 20 },
13933
14062
  testInfo: { flex: 1 },
13934
14063
  testTitle: { fontSize: 14, color: colors.textPrimary, marginBottom: 2 },
14064
+ testMetaRow: { flexDirection: "row", alignItems: "center", gap: 6 },
14065
+ retestTag: { backgroundColor: "#422006", borderWidth: 1, borderColor: "#854d0e", borderRadius: 4, paddingHorizontal: 5, paddingVertical: 1 },
14066
+ retestTagText: { fontSize: 10, fontWeight: "600", color: "#fbbf24" },
13935
14067
  testMeta: { fontSize: 11, color: colors.textDim },
13936
14068
  refreshBtn: { alignItems: "center", paddingVertical: 12 },
13937
14069
  refreshText: { fontSize: 13, color: colors.blue }
@@ -14262,12 +14394,18 @@ function ReportScreen({ nav, prefill }) {
14262
14394
  const [reportType, setReportType] = (0, import_react10.useState)(prefill?.type || "bug");
14263
14395
  const [severity, setSeverity] = (0, import_react10.useState)("medium");
14264
14396
  const [description, setDescription] = (0, import_react10.useState)("");
14397
+ const [affectedScreen, setAffectedScreen] = (0, import_react10.useState)("");
14265
14398
  const [submitting, setSubmitting] = (0, import_react10.useState)(false);
14266
14399
  const images = useImageAttachments(uploadImage, 5, "screenshots");
14267
14400
  const isBugType = reportType === "bug" || reportType === "test_fail";
14268
14401
  const handleSubmit = async () => {
14269
14402
  if (!client || !description.trim()) return;
14270
14403
  setSubmitting(true);
14404
+ const baseContext = client.getAppContext();
14405
+ const appContext = {
14406
+ ...baseContext,
14407
+ currentRoute: affectedScreen.trim() || baseContext.currentRoute
14408
+ };
14271
14409
  const screenshotUrls = images.getScreenshotUrls();
14272
14410
  await client.submitReport({
14273
14411
  type: reportType,
@@ -14275,7 +14413,7 @@ function ReportScreen({ nav, prefill }) {
14275
14413
  severity: isBugType ? severity : void 0,
14276
14414
  assignmentId: prefill?.assignmentId,
14277
14415
  testCaseId: prefill?.testCaseId,
14278
- appContext: { currentRoute: "unknown" },
14416
+ appContext,
14279
14417
  deviceInfo: getDeviceInfo(),
14280
14418
  screenshots: screenshotUrls.length > 0 ? screenshotUrls : void 0
14281
14419
  });
@@ -14323,7 +14461,16 @@ function ReportScreen({ nav, prefill }) {
14323
14461
  numberOfLines: 4,
14324
14462
  textAlignVertical: "top"
14325
14463
  }
14326
- )), /* @__PURE__ */ import_react10.default.createElement(
14464
+ )), isBugType && /* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, { style: styles7.section }, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: shared.label }, "Which screen?"), /* @__PURE__ */ import_react10.default.createElement(
14465
+ import_react_native9.TextInput,
14466
+ {
14467
+ style: styles7.screenInput,
14468
+ value: affectedScreen,
14469
+ onChangeText: setAffectedScreen,
14470
+ placeholder: "e.g. Reservations, Settings...",
14471
+ placeholderTextColor: colors.textMuted
14472
+ }
14473
+ ), /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles7.screenHint }, "Which screen or area was the bug on? (optional)")), /* @__PURE__ */ import_react10.default.createElement(
14327
14474
  ImagePickerButtons,
14328
14475
  {
14329
14476
  images: images.images,
@@ -14354,7 +14501,9 @@ var styles7 = import_react_native9.StyleSheet.create({
14354
14501
  severityRow: { flexDirection: "row", gap: 8 },
14355
14502
  sevButton: { flex: 1, paddingVertical: 8, borderRadius: 8, alignItems: "center", borderWidth: 1, borderColor: colors.border, backgroundColor: colors.card },
14356
14503
  sevText: { fontSize: 12, fontWeight: "500", color: colors.textSecondary, textTransform: "capitalize" },
14357
- descInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 12, paddingHorizontal: 14, paddingVertical: 12, fontSize: 14, color: colors.textPrimary, minHeight: 100 }
14504
+ descInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 12, paddingHorizontal: 14, paddingVertical: 12, fontSize: 14, color: colors.textPrimary, minHeight: 100 },
14505
+ screenInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 8, paddingHorizontal: 12, paddingVertical: 8, fontSize: 13, color: colors.textPrimary },
14506
+ screenHint: { fontSize: 11, color: colors.textMuted, marginTop: 4 }
14358
14507
  });
14359
14508
 
14360
14509
  // src/widget/screens/ReportSuccessScreen.tsx
package/dist/index.mjs CHANGED
@@ -11361,6 +11361,239 @@ function shouldShowDeprecationWarning() {
11361
11361
  if (shouldShowDeprecationWarning()) console.warn("\u26A0\uFE0F Node.js 18 and below are deprecated and will no longer be supported in future versions of @supabase/supabase-js. Please upgrade to Node.js 20 or later. For more information, visit: https://github.com/orgs/supabase/discussions/37217");
11362
11362
 
11363
11363
  // ../core/dist/index.mjs
11364
+ var MAX_CONSOLE_LOGS = 50;
11365
+ var MAX_NETWORK_REQUESTS = 20;
11366
+ var MAX_NAVIGATION_HISTORY = 20;
11367
+ var MAX_RESPONSE_BODY_LENGTH = 500;
11368
+ var ContextCaptureManager = class {
11369
+ constructor() {
11370
+ this.consoleLogs = [];
11371
+ this.networkRequests = [];
11372
+ this.navigationHistory = [];
11373
+ this.originalConsole = {};
11374
+ this.isCapturing = false;
11375
+ }
11376
+ /**
11377
+ * Start capturing console logs, network requests, and navigation
11378
+ */
11379
+ startCapture() {
11380
+ if (this.isCapturing) return;
11381
+ this.isCapturing = true;
11382
+ this.captureConsole();
11383
+ this.captureFetch();
11384
+ this.captureNavigation();
11385
+ }
11386
+ /**
11387
+ * Stop capturing and restore original functions
11388
+ */
11389
+ stopCapture() {
11390
+ if (!this.isCapturing) return;
11391
+ this.isCapturing = false;
11392
+ if (this.originalConsole.log) console.log = this.originalConsole.log;
11393
+ if (this.originalConsole.warn) console.warn = this.originalConsole.warn;
11394
+ if (this.originalConsole.error) console.error = this.originalConsole.error;
11395
+ if (this.originalConsole.info) console.info = this.originalConsole.info;
11396
+ if (this.originalFetch && typeof window !== "undefined") {
11397
+ window.fetch = this.originalFetch;
11398
+ }
11399
+ if (typeof window !== "undefined" && typeof history !== "undefined") {
11400
+ if (this.originalPushState) {
11401
+ history.pushState = this.originalPushState;
11402
+ }
11403
+ if (this.originalReplaceState) {
11404
+ history.replaceState = this.originalReplaceState;
11405
+ }
11406
+ if (this.popstateHandler) {
11407
+ window.removeEventListener("popstate", this.popstateHandler);
11408
+ }
11409
+ }
11410
+ }
11411
+ /**
11412
+ * Get captured context for a bug report
11413
+ */
11414
+ getEnhancedContext() {
11415
+ return {
11416
+ consoleLogs: [...this.consoleLogs],
11417
+ networkRequests: [...this.networkRequests],
11418
+ navigationHistory: [...this.navigationHistory],
11419
+ performanceMetrics: this.getPerformanceMetrics(),
11420
+ environment: this.getEnvironmentInfo()
11421
+ };
11422
+ }
11423
+ /**
11424
+ * Get the auto-captured navigation history
11425
+ */
11426
+ getNavigationHistory() {
11427
+ return [...this.navigationHistory];
11428
+ }
11429
+ /**
11430
+ * Get the current route (last entry in navigation history, or window.location)
11431
+ */
11432
+ getCurrentRoute() {
11433
+ if (this.navigationHistory.length > 0) {
11434
+ return this.navigationHistory[this.navigationHistory.length - 1];
11435
+ }
11436
+ if (typeof window !== "undefined") {
11437
+ return window.location.pathname;
11438
+ }
11439
+ return "unknown";
11440
+ }
11441
+ /**
11442
+ * Manually track a navigation event (for React Native or custom routing)
11443
+ */
11444
+ trackNavigation(route) {
11445
+ const last = this.navigationHistory[this.navigationHistory.length - 1];
11446
+ if (route === last) return;
11447
+ this.navigationHistory.push(route);
11448
+ if (this.navigationHistory.length > MAX_NAVIGATION_HISTORY) {
11449
+ this.navigationHistory.shift();
11450
+ }
11451
+ }
11452
+ /**
11453
+ * Clear captured data
11454
+ */
11455
+ clear() {
11456
+ this.consoleLogs = [];
11457
+ this.networkRequests = [];
11458
+ this.navigationHistory = [];
11459
+ }
11460
+ /**
11461
+ * Add a log entry manually (for custom logging)
11462
+ */
11463
+ addLog(level, message, args) {
11464
+ this.consoleLogs.push({
11465
+ level,
11466
+ message,
11467
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11468
+ args
11469
+ });
11470
+ if (this.consoleLogs.length > MAX_CONSOLE_LOGS) {
11471
+ this.consoleLogs = this.consoleLogs.slice(-MAX_CONSOLE_LOGS);
11472
+ }
11473
+ }
11474
+ /**
11475
+ * Add a network request manually
11476
+ */
11477
+ addNetworkRequest(request) {
11478
+ this.networkRequests.push(request);
11479
+ if (this.networkRequests.length > MAX_NETWORK_REQUESTS) {
11480
+ this.networkRequests = this.networkRequests.slice(-MAX_NETWORK_REQUESTS);
11481
+ }
11482
+ }
11483
+ captureConsole() {
11484
+ if (typeof console === "undefined") return;
11485
+ const levels = ["log", "warn", "error", "info"];
11486
+ levels.forEach((level) => {
11487
+ this.originalConsole[level] = console[level];
11488
+ console[level] = (...args) => {
11489
+ this.originalConsole[level]?.apply(console, args);
11490
+ try {
11491
+ const message = args.map((arg) => {
11492
+ if (typeof arg === "string") return arg;
11493
+ try {
11494
+ return JSON.stringify(arg);
11495
+ } catch {
11496
+ return String(arg);
11497
+ }
11498
+ }).join(" ");
11499
+ this.addLog(level, message.slice(0, 500));
11500
+ } catch {
11501
+ }
11502
+ };
11503
+ });
11504
+ }
11505
+ captureFetch() {
11506
+ if (typeof window === "undefined" || typeof fetch === "undefined") return;
11507
+ this.originalFetch = window.fetch;
11508
+ const self2 = this;
11509
+ window.fetch = async function(input, init) {
11510
+ const startTime = Date.now();
11511
+ const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
11512
+ const method = init?.method || "GET";
11513
+ try {
11514
+ const response = await self2.originalFetch.call(window, input, init);
11515
+ const requestEntry = {
11516
+ method,
11517
+ url: url.slice(0, 200),
11518
+ // Limit URL length
11519
+ status: response.status,
11520
+ duration: Date.now() - startTime,
11521
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
11522
+ };
11523
+ if (response.status >= 400) {
11524
+ try {
11525
+ const cloned = response.clone();
11526
+ const body = await cloned.text();
11527
+ if (body) {
11528
+ requestEntry.responseBody = body.slice(0, MAX_RESPONSE_BODY_LENGTH);
11529
+ }
11530
+ } catch {
11531
+ }
11532
+ }
11533
+ self2.addNetworkRequest(requestEntry);
11534
+ return response;
11535
+ } catch (error) {
11536
+ self2.addNetworkRequest({
11537
+ method,
11538
+ url: url.slice(0, 200),
11539
+ duration: Date.now() - startTime,
11540
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11541
+ error: error instanceof Error ? error.message : "Unknown error"
11542
+ });
11543
+ throw error;
11544
+ }
11545
+ };
11546
+ }
11547
+ captureNavigation() {
11548
+ if (typeof window === "undefined" || typeof history === "undefined") return;
11549
+ this.trackNavigation(window.location.pathname);
11550
+ const self2 = this;
11551
+ this.originalPushState = history.pushState;
11552
+ history.pushState = function(...args) {
11553
+ self2.originalPushState.apply(history, args);
11554
+ self2.trackNavigation(window.location.pathname);
11555
+ };
11556
+ this.originalReplaceState = history.replaceState;
11557
+ history.replaceState = function(...args) {
11558
+ self2.originalReplaceState.apply(history, args);
11559
+ self2.trackNavigation(window.location.pathname);
11560
+ };
11561
+ this.popstateHandler = () => {
11562
+ self2.trackNavigation(window.location.pathname);
11563
+ };
11564
+ window.addEventListener("popstate", this.popstateHandler);
11565
+ }
11566
+ getPerformanceMetrics() {
11567
+ if (typeof window === "undefined" || typeof performance === "undefined") return void 0;
11568
+ const metrics = {};
11569
+ try {
11570
+ const navigation = performance.getEntriesByType("navigation")[0];
11571
+ if (navigation) {
11572
+ metrics.pageLoadTime = Math.round(navigation.loadEventEnd - navigation.startTime);
11573
+ }
11574
+ } catch {
11575
+ }
11576
+ try {
11577
+ const memory = performance.memory;
11578
+ if (memory) {
11579
+ metrics.memoryUsage = Math.round(memory.usedJSHeapSize / 1024 / 1024);
11580
+ }
11581
+ } catch {
11582
+ }
11583
+ return Object.keys(metrics).length > 0 ? metrics : void 0;
11584
+ }
11585
+ getEnvironmentInfo() {
11586
+ if (typeof window === "undefined" || typeof navigator === "undefined") return void 0;
11587
+ return {
11588
+ language: navigator.language,
11589
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
11590
+ cookiesEnabled: navigator.cookieEnabled,
11591
+ localStorage: typeof localStorage !== "undefined",
11592
+ online: navigator.onLine
11593
+ };
11594
+ }
11595
+ };
11596
+ var contextCapture = new ContextCaptureManager();
11364
11597
  var DEFAULT_SUPABASE_URL = "https://kyxgzjnqgvapvlnvqawz.supabase.co";
11365
11598
  var getEnvVar = (key) => {
11366
11599
  try {
@@ -11382,22 +11615,40 @@ var BugBearClient = class {
11382
11615
  );
11383
11616
  }
11384
11617
  /**
11385
- * Track navigation for context
11618
+ * Track navigation for context.
11619
+ * Also forwards to contextCapture for auto-tracked navigation.
11386
11620
  */
11387
11621
  trackNavigation(route) {
11388
11622
  this.navigationHistory.push(route);
11389
11623
  if (this.navigationHistory.length > 10) {
11390
11624
  this.navigationHistory.shift();
11391
11625
  }
11626
+ contextCapture.trackNavigation(route);
11392
11627
  }
11393
11628
  /**
11394
- * Get current navigation history
11629
+ * Get current navigation history.
11630
+ * Priority: config callback > manual tracking > auto-captured (pushState/popstate)
11395
11631
  */
11396
11632
  getNavigationHistory() {
11397
11633
  if (this.config.getNavigationHistory) {
11398
11634
  return this.config.getNavigationHistory();
11399
11635
  }
11400
- return [...this.navigationHistory];
11636
+ if (this.navigationHistory.length > 0) {
11637
+ return [...this.navigationHistory];
11638
+ }
11639
+ return contextCapture.getNavigationHistory();
11640
+ }
11641
+ /**
11642
+ * Get current app context.
11643
+ * Uses config.getAppContext() callback if provided, otherwise builds from available data.
11644
+ */
11645
+ getAppContext() {
11646
+ if (this.config.getAppContext) {
11647
+ return this.config.getAppContext();
11648
+ }
11649
+ return {
11650
+ currentRoute: contextCapture.getCurrentRoute()
11651
+ };
11401
11652
  }
11402
11653
  /**
11403
11654
  * Get current user info from host app or BugBear's own auth
@@ -11437,6 +11688,8 @@ var BugBearClient = class {
11437
11688
  project_id: this.config.projectId,
11438
11689
  reporter_id: userInfo.id,
11439
11690
  // User ID from host app (required)
11691
+ reporter_name: testerInfo?.name || userInfo.name || null,
11692
+ reporter_email: userInfo.email || null,
11440
11693
  tester_id: testerInfo?.id || null,
11441
11694
  // Tester record ID (optional)
11442
11695
  report_type: report.type,
@@ -11450,6 +11703,7 @@ var BugBearClient = class {
11450
11703
  app_context: report.appContext,
11451
11704
  device_info: report.deviceInfo || this.getDeviceInfo(),
11452
11705
  navigation_history: this.getNavigationHistory(),
11706
+ enhanced_context: report.enhancedContext || contextCapture.getEnhancedContext(),
11453
11707
  assignment_id: report.assignmentId,
11454
11708
  test_case_id: report.testCaseId
11455
11709
  };
@@ -11512,7 +11766,7 @@ var BugBearClient = class {
11512
11766
  console.error("BugBear: Failed to fetch assignments", error);
11513
11767
  return [];
11514
11768
  }
11515
- return (data || []).map((item) => ({
11769
+ const mapped = (data || []).map((item) => ({
11516
11770
  id: item.id,
11517
11771
  status: item.status,
11518
11772
  startedAt: item.started_at,
@@ -11545,6 +11799,12 @@ var BugBearClient = class {
11545
11799
  } : void 0
11546
11800
  }
11547
11801
  }));
11802
+ mapped.sort((a, b) => {
11803
+ if (a.isVerification && !b.isVerification) return -1;
11804
+ if (!a.isVerification && b.isVerification) return 1;
11805
+ return 0;
11806
+ });
11807
+ return mapped;
11548
11808
  } catch (err) {
11549
11809
  console.error("BugBear: Error fetching assignments", err);
11550
11810
  return [];
@@ -12677,163 +12937,6 @@ var BugBearClient = class {
12677
12937
  function createBugBear(config) {
12678
12938
  return new BugBearClient(config);
12679
12939
  }
12680
- var MAX_CONSOLE_LOGS = 50;
12681
- var MAX_NETWORK_REQUESTS = 20;
12682
- var ContextCaptureManager = class {
12683
- constructor() {
12684
- this.consoleLogs = [];
12685
- this.networkRequests = [];
12686
- this.originalConsole = {};
12687
- this.isCapturing = false;
12688
- }
12689
- /**
12690
- * Start capturing console logs and network requests
12691
- */
12692
- startCapture() {
12693
- if (this.isCapturing) return;
12694
- this.isCapturing = true;
12695
- this.captureConsole();
12696
- this.captureFetch();
12697
- }
12698
- /**
12699
- * Stop capturing and restore original functions
12700
- */
12701
- stopCapture() {
12702
- if (!this.isCapturing) return;
12703
- this.isCapturing = false;
12704
- if (this.originalConsole.log) console.log = this.originalConsole.log;
12705
- if (this.originalConsole.warn) console.warn = this.originalConsole.warn;
12706
- if (this.originalConsole.error) console.error = this.originalConsole.error;
12707
- if (this.originalConsole.info) console.info = this.originalConsole.info;
12708
- if (this.originalFetch && typeof window !== "undefined") {
12709
- window.fetch = this.originalFetch;
12710
- }
12711
- }
12712
- /**
12713
- * Get captured context for a bug report
12714
- */
12715
- getEnhancedContext() {
12716
- return {
12717
- consoleLogs: [...this.consoleLogs],
12718
- networkRequests: [...this.networkRequests],
12719
- performanceMetrics: this.getPerformanceMetrics(),
12720
- environment: this.getEnvironmentInfo()
12721
- };
12722
- }
12723
- /**
12724
- * Clear captured data
12725
- */
12726
- clear() {
12727
- this.consoleLogs = [];
12728
- this.networkRequests = [];
12729
- }
12730
- /**
12731
- * Add a log entry manually (for custom logging)
12732
- */
12733
- addLog(level, message, args) {
12734
- this.consoleLogs.push({
12735
- level,
12736
- message,
12737
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
12738
- args
12739
- });
12740
- if (this.consoleLogs.length > MAX_CONSOLE_LOGS) {
12741
- this.consoleLogs = this.consoleLogs.slice(-MAX_CONSOLE_LOGS);
12742
- }
12743
- }
12744
- /**
12745
- * Add a network request manually
12746
- */
12747
- addNetworkRequest(request) {
12748
- this.networkRequests.push(request);
12749
- if (this.networkRequests.length > MAX_NETWORK_REQUESTS) {
12750
- this.networkRequests = this.networkRequests.slice(-MAX_NETWORK_REQUESTS);
12751
- }
12752
- }
12753
- captureConsole() {
12754
- if (typeof console === "undefined") return;
12755
- const levels = ["log", "warn", "error", "info"];
12756
- levels.forEach((level) => {
12757
- this.originalConsole[level] = console[level];
12758
- console[level] = (...args) => {
12759
- this.originalConsole[level]?.apply(console, args);
12760
- try {
12761
- const message = args.map((arg) => {
12762
- if (typeof arg === "string") return arg;
12763
- try {
12764
- return JSON.stringify(arg);
12765
- } catch {
12766
- return String(arg);
12767
- }
12768
- }).join(" ");
12769
- this.addLog(level, message.slice(0, 500));
12770
- } catch {
12771
- }
12772
- };
12773
- });
12774
- }
12775
- captureFetch() {
12776
- if (typeof window === "undefined" || typeof fetch === "undefined") return;
12777
- this.originalFetch = window.fetch;
12778
- const self2 = this;
12779
- window.fetch = async function(input, init) {
12780
- const startTime = Date.now();
12781
- const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
12782
- const method = init?.method || "GET";
12783
- try {
12784
- const response = await self2.originalFetch.call(window, input, init);
12785
- self2.addNetworkRequest({
12786
- method,
12787
- url: url.slice(0, 200),
12788
- // Limit URL length
12789
- status: response.status,
12790
- duration: Date.now() - startTime,
12791
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
12792
- });
12793
- return response;
12794
- } catch (error) {
12795
- self2.addNetworkRequest({
12796
- method,
12797
- url: url.slice(0, 200),
12798
- duration: Date.now() - startTime,
12799
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
12800
- error: error instanceof Error ? error.message : "Unknown error"
12801
- });
12802
- throw error;
12803
- }
12804
- };
12805
- }
12806
- getPerformanceMetrics() {
12807
- if (typeof window === "undefined" || typeof performance === "undefined") return void 0;
12808
- const metrics = {};
12809
- try {
12810
- const navigation = performance.getEntriesByType("navigation")[0];
12811
- if (navigation) {
12812
- metrics.pageLoadTime = Math.round(navigation.loadEventEnd - navigation.startTime);
12813
- }
12814
- } catch {
12815
- }
12816
- try {
12817
- const memory = performance.memory;
12818
- if (memory) {
12819
- metrics.memoryUsage = Math.round(memory.usedJSHeapSize / 1024 / 1024);
12820
- }
12821
- } catch {
12822
- }
12823
- return Object.keys(metrics).length > 0 ? metrics : void 0;
12824
- }
12825
- getEnvironmentInfo() {
12826
- if (typeof window === "undefined" || typeof navigator === "undefined") return void 0;
12827
- return {
12828
- language: navigator.language,
12829
- timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
12830
- cookiesEnabled: navigator.cookieEnabled,
12831
- localStorage: typeof localStorage !== "undefined",
12832
- online: navigator.onLine
12833
- };
12834
- }
12835
- };
12836
- var contextCapture = new ContextCaptureManager();
12837
12940
 
12838
12941
  // src/BugBearProvider.tsx
12839
12942
  import { Platform, Dimensions } from "react-native";
@@ -13034,6 +13137,7 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
13034
13137
  useEffect(() => {
13035
13138
  if (enabled && !hasInitialized.current) {
13036
13139
  hasInitialized.current = true;
13140
+ contextCapture.startCapture();
13037
13141
  const newClient = createBugBear(config);
13038
13142
  setClient(newClient);
13039
13143
  initializeBugBear(newClient);
@@ -13327,7 +13431,9 @@ function HomeScreen({ nav }) {
13327
13431
  refreshAssignments();
13328
13432
  refreshThreads();
13329
13433
  }, []);
13330
- const pendingCount = assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length;
13434
+ const pendingAssignments = assignments.filter((a) => a.status === "pending" || a.status === "in_progress");
13435
+ const pendingCount = pendingAssignments.length;
13436
+ const retestCount = pendingAssignments.filter((a) => a.isVerification).length;
13331
13437
  const completedCount = assignments.filter((a) => a.status === "passed" || a.status === "failed").length;
13332
13438
  const totalTests = assignments.length;
13333
13439
  return /* @__PURE__ */ React2.createElement(View, null, pendingCount > 0 ? /* @__PURE__ */ React2.createElement(
@@ -13339,6 +13445,7 @@ function HomeScreen({ nav }) {
13339
13445
  },
13340
13446
  /* @__PURE__ */ React2.createElement(Text, { style: styles.heroCount }, pendingCount),
13341
13447
  /* @__PURE__ */ React2.createElement(Text, { style: styles.heroLabel }, "test", pendingCount !== 1 ? "s" : "", " waiting"),
13448
+ retestCount > 0 && /* @__PURE__ */ React2.createElement(View, { style: styles.retestPill }, /* @__PURE__ */ React2.createElement(Text, { style: styles.retestPillText }, "\u{1F504} ", retestCount, " retest", retestCount !== 1 ? "s" : "")),
13342
13449
  /* @__PURE__ */ React2.createElement(Text, { style: styles.heroAction }, "Start Testing \u2192")
13343
13450
  ) : unreadCount > 0 ? /* @__PURE__ */ React2.createElement(
13344
13451
  TouchableOpacity,
@@ -13442,6 +13549,22 @@ var styles = StyleSheet2.create({
13442
13549
  color: colors.blueText,
13443
13550
  marginTop: 2
13444
13551
  },
13552
+ retestPill: {
13553
+ flexDirection: "row",
13554
+ alignItems: "center",
13555
+ backgroundColor: "#422006",
13556
+ borderWidth: 1,
13557
+ borderColor: "#854d0e",
13558
+ borderRadius: 6,
13559
+ paddingHorizontal: 8,
13560
+ paddingVertical: 3,
13561
+ marginTop: 8
13562
+ },
13563
+ retestPillText: {
13564
+ fontSize: 12,
13565
+ fontWeight: "600",
13566
+ color: "#fbbf24"
13567
+ },
13445
13568
  heroAction: {
13446
13569
  fontSize: 14,
13447
13570
  fontWeight: "600",
@@ -13652,7 +13775,7 @@ function TestDetailScreen({ testId, nav }) {
13652
13775
  const steps = testCase.steps;
13653
13776
  const info = templateInfo[template] || templateInfo.steps;
13654
13777
  const rubricMode = testCase.track?.rubricMode || "pass_fail";
13655
- return /* @__PURE__ */ React3.createElement(View2, { style: styles2.container }, /* @__PURE__ */ React3.createElement(View2, { style: styles2.topRow }, /* @__PURE__ */ React3.createElement(View2, { style: styles2.positionInfo }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.positionText }, "Test ", currentIndex + 1, " of ", allTests.length), displayedAssignment.status === "in_progress" && assignmentElapsedTime > 0 && /* @__PURE__ */ React3.createElement(View2, { style: styles2.timerBadge }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.timerText }, formatElapsedTime(assignmentElapsedTime)))), /* @__PURE__ */ React3.createElement(TouchableOpacity2, { onPress: () => nav.push({ name: "TEST_LIST" }) }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.viewAllLink }, "View All \u2192"))), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.testTitle }, testCase.title), testCase.key && /* @__PURE__ */ React3.createElement(Text2, { style: styles2.testKey }, testCase.key), /* @__PURE__ */ React3.createElement(TouchableOpacity2, { onPress: () => setShowSteps(!showSteps), style: styles2.sectionHeader }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.sectionHeaderText }, showSteps ? "\u25BC" : "\u25B6", " ", info.icon, " ", template === "freeform" ? "Instructions" : `${steps.length} ${template === "checklist" ? "items" : template === "rubric" ? "criteria" : "steps"}`)), showSteps && /* @__PURE__ */ React3.createElement(View2, { style: styles2.templateContent }, template === "steps" && steps.map((step, idx) => /* @__PURE__ */ React3.createElement(View2, { key: idx, style: styles2.step }, /* @__PURE__ */ React3.createElement(View2, { style: styles2.stepNumber }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.stepNumberText }, step.stepNumber)), /* @__PURE__ */ React3.createElement(View2, { style: styles2.stepBody }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.stepAction }, step.action), step.expectedResult && /* @__PURE__ */ React3.createElement(Text2, { style: styles2.stepExpected }, "\u2192 ", step.expectedResult)))), template === "checklist" && /* @__PURE__ */ React3.createElement(React3.Fragment, null, steps.map((step, idx) => /* @__PURE__ */ React3.createElement(
13778
+ return /* @__PURE__ */ React3.createElement(View2, { style: styles2.container }, /* @__PURE__ */ React3.createElement(View2, { style: styles2.topRow }, /* @__PURE__ */ React3.createElement(View2, { style: styles2.positionInfo }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.positionText }, "Test ", currentIndex + 1, " of ", allTests.length), displayedAssignment.status === "in_progress" && assignmentElapsedTime > 0 && /* @__PURE__ */ React3.createElement(View2, { style: styles2.timerBadge }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.timerText }, formatElapsedTime(assignmentElapsedTime)))), /* @__PURE__ */ React3.createElement(TouchableOpacity2, { onPress: () => nav.push({ name: "TEST_LIST" }) }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.viewAllLink }, "View All \u2192"))), displayedAssignment.isVerification && /* @__PURE__ */ React3.createElement(View2, { style: styles2.retestBanner }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.retestIcon }, "\u{1F504}"), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.retestLabel }, "Retest"), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.retestSub }, "\u2014 Verify bug fix")), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.testTitle }, testCase.title), testCase.key && /* @__PURE__ */ React3.createElement(Text2, { style: styles2.testKey }, testCase.key), /* @__PURE__ */ React3.createElement(TouchableOpacity2, { onPress: () => setShowSteps(!showSteps), style: styles2.sectionHeader }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.sectionHeaderText }, showSteps ? "\u25BC" : "\u25B6", " ", info.icon, " ", template === "freeform" ? "Instructions" : `${steps.length} ${template === "checklist" ? "items" : template === "rubric" ? "criteria" : "steps"}`)), showSteps && /* @__PURE__ */ React3.createElement(View2, { style: styles2.templateContent }, template === "steps" && steps.map((step, idx) => /* @__PURE__ */ React3.createElement(View2, { key: idx, style: styles2.step }, /* @__PURE__ */ React3.createElement(View2, { style: styles2.stepNumber }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.stepNumberText }, step.stepNumber)), /* @__PURE__ */ React3.createElement(View2, { style: styles2.stepBody }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.stepAction }, step.action), step.expectedResult && /* @__PURE__ */ React3.createElement(Text2, { style: styles2.stepExpected }, "\u2192 ", step.expectedResult)))), template === "checklist" && /* @__PURE__ */ React3.createElement(React3.Fragment, null, steps.map((step, idx) => /* @__PURE__ */ React3.createElement(
13656
13779
  TouchableOpacity2,
13657
13780
  {
13658
13781
  key: idx,
@@ -13736,6 +13859,10 @@ function TestDetailScreen({ testId, nav }) {
13736
13859
  }
13737
13860
  var styles2 = StyleSheet3.create({
13738
13861
  container: { paddingBottom: 16 },
13862
+ retestBanner: { flexDirection: "row", alignItems: "center", gap: 6, backgroundColor: "#422006", borderWidth: 1, borderColor: "#854d0e", borderRadius: 8, paddingVertical: 6, paddingHorizontal: 10, marginBottom: 10 },
13863
+ retestIcon: { fontSize: 14 },
13864
+ retestLabel: { fontSize: 13, fontWeight: "600", color: "#fbbf24" },
13865
+ retestSub: { fontSize: 12, color: "#d97706" },
13739
13866
  topRow: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", marginBottom: 12 },
13740
13867
  positionInfo: { flexDirection: "row", alignItems: "center", gap: 8 },
13741
13868
  positionText: { fontSize: 13, color: colors.textMuted },
@@ -13851,6 +13978,8 @@ function TestListScreen({ nav }) {
13851
13978
  const statusOrder = { in_progress: 0, pending: 1, failed: 2, skipped: 3, passed: 4 };
13852
13979
  for (const folder of groups.values()) {
13853
13980
  folder.assignments.sort((a, b) => {
13981
+ if (a.isVerification && !b.isVerification) return -1;
13982
+ if (!a.isVerification && b.isVerification) return 1;
13854
13983
  const sd = (statusOrder[a.status] ?? 5) - (statusOrder[b.status] ?? 5);
13855
13984
  if (sd !== 0) return sd;
13856
13985
  return (priorityOrder[a.testCase.priority] ?? 4) - (priorityOrder[b.testCase.priority] ?? 4);
@@ -13892,7 +14021,7 @@ function TestListScreen({ nav }) {
13892
14021
  onPress: () => nav.push({ name: "TEST_DETAIL", testId: assignment.id })
13893
14022
  },
13894
14023
  /* @__PURE__ */ React4.createElement(Text3, { style: styles3.testBadge }, badge.icon),
13895
- /* @__PURE__ */ React4.createElement(View3, { style: styles3.testInfo }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.testTitle, numberOfLines: 1 }, assignment.testCase.title), /* @__PURE__ */ React4.createElement(Text3, { style: styles3.testMeta }, assignment.testCase.key, " \xB7 ", assignment.testCase.priority))
14024
+ /* @__PURE__ */ React4.createElement(View3, { style: styles3.testInfo }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.testTitle, numberOfLines: 1 }, assignment.testCase.title), /* @__PURE__ */ React4.createElement(View3, { style: styles3.testMetaRow }, assignment.isVerification && /* @__PURE__ */ React4.createElement(View3, { style: styles3.retestTag }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.retestTagText }, "Retest")), /* @__PURE__ */ React4.createElement(Text3, { style: styles3.testMeta }, assignment.testCase.key, " \xB7 ", assignment.testCase.priority)))
13896
14025
  );
13897
14026
  }));
13898
14027
  }), /* @__PURE__ */ React4.createElement(TouchableOpacity3, { style: styles3.refreshBtn, onPress: refreshAssignments }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.refreshText }, "\u21BB Refresh")));
@@ -13915,6 +14044,9 @@ var styles3 = StyleSheet4.create({
13915
14044
  testBadge: { fontSize: 16, marginRight: 10, width: 20 },
13916
14045
  testInfo: { flex: 1 },
13917
14046
  testTitle: { fontSize: 14, color: colors.textPrimary, marginBottom: 2 },
14047
+ testMetaRow: { flexDirection: "row", alignItems: "center", gap: 6 },
14048
+ retestTag: { backgroundColor: "#422006", borderWidth: 1, borderColor: "#854d0e", borderRadius: 4, paddingHorizontal: 5, paddingVertical: 1 },
14049
+ retestTagText: { fontSize: 10, fontWeight: "600", color: "#fbbf24" },
13918
14050
  testMeta: { fontSize: 11, color: colors.textDim },
13919
14051
  refreshBtn: { alignItems: "center", paddingVertical: 12 },
13920
14052
  refreshText: { fontSize: 13, color: colors.blue }
@@ -14245,12 +14377,18 @@ function ReportScreen({ nav, prefill }) {
14245
14377
  const [reportType, setReportType] = useState6(prefill?.type || "bug");
14246
14378
  const [severity, setSeverity] = useState6("medium");
14247
14379
  const [description, setDescription] = useState6("");
14380
+ const [affectedScreen, setAffectedScreen] = useState6("");
14248
14381
  const [submitting, setSubmitting] = useState6(false);
14249
14382
  const images = useImageAttachments(uploadImage, 5, "screenshots");
14250
14383
  const isBugType = reportType === "bug" || reportType === "test_fail";
14251
14384
  const handleSubmit = async () => {
14252
14385
  if (!client || !description.trim()) return;
14253
14386
  setSubmitting(true);
14387
+ const baseContext = client.getAppContext();
14388
+ const appContext = {
14389
+ ...baseContext,
14390
+ currentRoute: affectedScreen.trim() || baseContext.currentRoute
14391
+ };
14254
14392
  const screenshotUrls = images.getScreenshotUrls();
14255
14393
  await client.submitReport({
14256
14394
  type: reportType,
@@ -14258,7 +14396,7 @@ function ReportScreen({ nav, prefill }) {
14258
14396
  severity: isBugType ? severity : void 0,
14259
14397
  assignmentId: prefill?.assignmentId,
14260
14398
  testCaseId: prefill?.testCaseId,
14261
- appContext: { currentRoute: "unknown" },
14399
+ appContext,
14262
14400
  deviceInfo: getDeviceInfo(),
14263
14401
  screenshots: screenshotUrls.length > 0 ? screenshotUrls : void 0
14264
14402
  });
@@ -14306,7 +14444,16 @@ function ReportScreen({ nav, prefill }) {
14306
14444
  numberOfLines: 4,
14307
14445
  textAlignVertical: "top"
14308
14446
  }
14309
- )), /* @__PURE__ */ React8.createElement(
14447
+ )), isBugType && /* @__PURE__ */ React8.createElement(View7, { style: styles7.section }, /* @__PURE__ */ React8.createElement(Text7, { style: shared.label }, "Which screen?"), /* @__PURE__ */ React8.createElement(
14448
+ TextInput3,
14449
+ {
14450
+ style: styles7.screenInput,
14451
+ value: affectedScreen,
14452
+ onChangeText: setAffectedScreen,
14453
+ placeholder: "e.g. Reservations, Settings...",
14454
+ placeholderTextColor: colors.textMuted
14455
+ }
14456
+ ), /* @__PURE__ */ React8.createElement(Text7, { style: styles7.screenHint }, "Which screen or area was the bug on? (optional)")), /* @__PURE__ */ React8.createElement(
14310
14457
  ImagePickerButtons,
14311
14458
  {
14312
14459
  images: images.images,
@@ -14337,7 +14484,9 @@ var styles7 = StyleSheet8.create({
14337
14484
  severityRow: { flexDirection: "row", gap: 8 },
14338
14485
  sevButton: { flex: 1, paddingVertical: 8, borderRadius: 8, alignItems: "center", borderWidth: 1, borderColor: colors.border, backgroundColor: colors.card },
14339
14486
  sevText: { fontSize: 12, fontWeight: "500", color: colors.textSecondary, textTransform: "capitalize" },
14340
- descInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 12, paddingHorizontal: 14, paddingVertical: 12, fontSize: 14, color: colors.textPrimary, minHeight: 100 }
14487
+ descInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 12, paddingHorizontal: 14, paddingVertical: 12, fontSize: 14, color: colors.textPrimary, minHeight: 100 },
14488
+ screenInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 8, paddingHorizontal: 12, paddingVertical: 8, fontSize: 13, color: colors.textPrimary },
14489
+ screenHint: { fontSize: 11, color: colors.textMuted, marginTop: 4 }
14341
14490
  });
14342
14491
 
14343
14492
  // src/widget/screens/ReportSuccessScreen.tsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbearai/react-native",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "BugBear React Native components for mobile apps",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",