@bbearai/react-native 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -11653,6 +11653,7 @@ var HOSTED_BUGBEAR_ANON_KEY = getEnvVar("BUGBEAR_ANON_KEY") || getEnvVar("NEXT_P
11653
11653
  var BugBearClient = class {
11654
11654
  constructor(config) {
11655
11655
  this.navigationHistory = [];
11656
+ this.reportSubmitInFlight = false;
11656
11657
  this.config = config;
11657
11658
  this.supabase = createClient(
11658
11659
  config.supabaseUrl || DEFAULT_SUPABASE_URL,
@@ -11713,6 +11714,10 @@ var BugBearClient = class {
11713
11714
  * Submit a report
11714
11715
  */
11715
11716
  async submitReport(report) {
11717
+ if (this.reportSubmitInFlight) {
11718
+ return { success: false, error: "A report is already being submitted" };
11719
+ }
11720
+ this.reportSubmitInFlight = true;
11716
11721
  try {
11717
11722
  const validationError = this.validateReport(report);
11718
11723
  if (validationError) {
@@ -11764,6 +11769,8 @@ var BugBearClient = class {
11764
11769
  } catch (err) {
11765
11770
  const message = err instanceof Error ? err.message : "Unknown error";
11766
11771
  return { success: false, error: message };
11772
+ } finally {
11773
+ this.reportSubmitInFlight = false;
11767
11774
  }
11768
11775
  }
11769
11776
  /**
@@ -11804,6 +11811,14 @@ var BugBearClient = class {
11804
11811
  name,
11805
11812
  description,
11806
11813
  sort_order
11814
+ ),
11815
+ role:project_roles(
11816
+ id,
11817
+ name,
11818
+ slug,
11819
+ color,
11820
+ description,
11821
+ login_hint
11807
11822
  )
11808
11823
  )
11809
11824
  `).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["pending", "in_progress"]).order("created_at", { ascending: true }).limit(100);
@@ -11841,6 +11856,14 @@ var BugBearClient = class {
11841
11856
  name: item.test_case.group.name,
11842
11857
  description: item.test_case.group.description,
11843
11858
  sortOrder: item.test_case.group.sort_order
11859
+ } : void 0,
11860
+ role: item.test_case.role ? {
11861
+ id: item.test_case.role.id,
11862
+ name: item.test_case.role.name,
11863
+ slug: item.test_case.role.slug,
11864
+ color: item.test_case.role.color,
11865
+ description: item.test_case.role.description,
11866
+ loginHint: item.test_case.role.login_hint
11844
11867
  } : void 0
11845
11868
  }
11846
11869
  }));
@@ -12256,8 +12279,8 @@ var BugBearClient = class {
12256
12279
  return "Maximum 10 screenshots allowed";
12257
12280
  }
12258
12281
  for (const url of report.screenshots) {
12259
- if (typeof url !== "string" || url.length > 2e3) {
12260
- return "Invalid screenshot URL";
12282
+ if (typeof url !== "string" || url.length > 2e3 || !/^https?:\/\//i.test(url)) {
12283
+ return "Invalid screenshot URL (must be an HTTP/HTTPS URL)";
12261
12284
  }
12262
12285
  }
12263
12286
  }
@@ -12646,7 +12669,10 @@ var BugBearClient = class {
12646
12669
  content
12647
12670
  };
12648
12671
  if (attachments && attachments.length > 0) {
12649
- insertData.attachments = attachments;
12672
+ const safeAttachments = attachments.filter((a) => /^https?:\/\//i.test(a.url));
12673
+ if (safeAttachments.length > 0) {
12674
+ insertData.attachments = safeAttachments;
12675
+ }
12650
12676
  }
12651
12677
  const { error } = await this.supabase.from("discussion_messages").insert(insertData);
12652
12678
  if (error) {
@@ -14021,10 +14047,19 @@ var import_react_native5 = require("react-native");
14021
14047
  function TestListScreen({ nav }) {
14022
14048
  const { assignments, currentAssignment, refreshAssignments } = useBugBear();
14023
14049
  const [filter, setFilter] = (0, import_react5.useState)("all");
14050
+ const [roleFilter, setRoleFilter] = (0, import_react5.useState)(null);
14024
14051
  const [collapsedFolders, setCollapsedFolders] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
14025
14052
  (0, import_react5.useEffect)(() => {
14026
14053
  refreshAssignments();
14027
14054
  }, []);
14055
+ const availableRoles = (0, import_react5.useMemo)(() => {
14056
+ const roleMap = /* @__PURE__ */ new Map();
14057
+ for (const a of assignments) {
14058
+ if (a.testCase.role) roleMap.set(a.testCase.role.id, a.testCase.role);
14059
+ }
14060
+ return Array.from(roleMap.values());
14061
+ }, [assignments]);
14062
+ const selectedRole = availableRoles.find((r) => r.id === roleFilter);
14028
14063
  const groupedAssignments = (0, import_react5.useMemo)(() => {
14029
14064
  const groups = /* @__PURE__ */ new Map();
14030
14065
  for (const assignment of assignments) {
@@ -14068,16 +14103,39 @@ function TestListScreen({ nav }) {
14068
14103
  });
14069
14104
  }, []);
14070
14105
  const filterAssignment = (a) => {
14106
+ if (roleFilter && a.testCase.role?.id !== roleFilter) return false;
14071
14107
  if (filter === "pending") return a.status === "pending" || a.status === "in_progress";
14072
14108
  if (filter === "completed") return a.status === "passed" || a.status === "failed";
14073
14109
  return true;
14074
14110
  };
14075
- return /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, null, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.filterBar }, ["all", "pending", "completed"].map((f) => /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { key: f, style: [styles3.filterBtn, filter === f && styles3.filterBtnActive], onPress: () => setFilter(f) }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles3.filterBtnText, filter === f && styles3.filterBtnTextActive] }, f === "all" ? `All (${assignments.length})` : f === "pending" ? `To Do (${assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length})` : `Done (${assignments.filter((a) => a.status === "passed" || a.status === "failed").length})`)))), groupedAssignments.map((folder) => {
14111
+ return /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, null, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.filterBar }, ["all", "pending", "completed"].map((f) => /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { key: f, style: [styles3.filterBtn, filter === f && styles3.filterBtnActive], onPress: () => setFilter(f) }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles3.filterBtnText, filter === f && styles3.filterBtnTextActive] }, f === "all" ? `All (${assignments.length})` : f === "pending" ? `To Do (${assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length})` : `Done (${assignments.filter((a) => a.status === "passed" || a.status === "failed").length})`)))), availableRoles.length >= 2 && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.roleSection }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles3.roleBar }, /* @__PURE__ */ import_react5.default.createElement(
14112
+ import_react_native5.TouchableOpacity,
14113
+ {
14114
+ style: [styles3.roleBtn, !roleFilter && styles3.roleBtnActive],
14115
+ onPress: () => setRoleFilter(null)
14116
+ },
14117
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles3.roleBtnText, !roleFilter && styles3.roleBtnTextActive] }, "All Roles")
14118
+ ), availableRoles.map((role) => {
14119
+ const isActive = roleFilter === role.id;
14120
+ return /* @__PURE__ */ import_react5.default.createElement(
14121
+ import_react_native5.TouchableOpacity,
14122
+ {
14123
+ key: role.id,
14124
+ style: [
14125
+ styles3.roleBtn,
14126
+ isActive && { backgroundColor: role.color + "20", borderColor: role.color + "60", borderWidth: 1 }
14127
+ ],
14128
+ onPress: () => setRoleFilter(isActive ? null : role.id)
14129
+ },
14130
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [styles3.roleDot, { backgroundColor: role.color }] }),
14131
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles3.roleBtnText, isActive && { color: role.color, fontWeight: "600" }] }, role.name)
14132
+ );
14133
+ })), selectedRole?.loginHint && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [styles3.loginHint, { backgroundColor: selectedRole.color + "10", borderColor: selectedRole.color + "30" }] }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.loginHintText }, "Log in as: ", selectedRole.loginHint))), groupedAssignments.map((folder) => {
14076
14134
  const folderId = folder.group?.id || "ungrouped";
14077
14135
  const isCollapsed = collapsedFolders.has(folderId);
14078
14136
  const filtered = folder.assignments.filter(filterAssignment);
14079
14137
  if (filtered.length === 0 && filter !== "all") return null;
14080
- return /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { key: folderId, style: styles3.folder }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: styles3.folderHeader, onPress: () => toggleFolder(folderId) }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.folderToggle }, isCollapsed ? "\u25B6" : "\u25BC"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.folderName }, folder.group?.name || "Ungrouped"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.folderProgress }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [styles3.folderProgressFill, { width: `${Math.round((folder.stats.passed + folder.stats.failed) / folder.stats.total * 100)}%` }] })), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.folderCount }, folder.stats.passed + folder.stats.failed, "/", folder.stats.total)), !isCollapsed && filtered.map((assignment) => {
14138
+ return /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { key: folderId, style: styles3.folder }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: styles3.folderHeader, onPress: () => toggleFolder(folderId) }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.folderToggle }, isCollapsed ? "\u25B6" : "\u25BC"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.folderName, numberOfLines: 1 }, folder.group?.name || "Ungrouped"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.folderProgress }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [styles3.folderProgressFill, { width: `${folder.stats.total > 0 ? Math.round((folder.stats.passed + folder.stats.failed) / folder.stats.total * 100) : 0}%` }] })), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.folderCount }, folder.stats.passed + folder.stats.failed, "/", folder.stats.total)), !isCollapsed && filtered.map((assignment) => {
14081
14139
  const badge = getStatusBadge(assignment.status);
14082
14140
  const isCurrent = currentAssignment?.id === assignment.id;
14083
14141
  return /* @__PURE__ */ import_react5.default.createElement(
@@ -14088,17 +14146,28 @@ function TestListScreen({ nav }) {
14088
14146
  onPress: () => nav.push({ name: "TEST_DETAIL", testId: assignment.id })
14089
14147
  },
14090
14148
  /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.testBadge }, badge.icon),
14091
- /* @__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)))
14149
+ /* @__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.testKey, " \xB7 ", assignment.testCase.priority), assignment.testCase.role && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.roleBadgeRow }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.testMeta }, " \xB7 "), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [styles3.roleBadgeDot, { backgroundColor: assignment.testCase.role.color }] }), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles3.testMeta, { color: assignment.testCase.role.color, fontWeight: "500" }] }, assignment.testCase.role.name))))
14092
14150
  );
14093
14151
  }));
14094
- }), /* @__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")));
14152
+ }), /* @__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")));
14095
14153
  }
14096
14154
  var styles3 = import_react_native5.StyleSheet.create({
14097
- filterBar: { flexDirection: "row", gap: 8, marginBottom: 16 },
14155
+ filterBar: { flexDirection: "row", gap: 8, marginBottom: 8 },
14098
14156
  filterBtn: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 8, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
14099
14157
  filterBtnActive: { backgroundColor: colors.blue, borderColor: colors.blue },
14100
14158
  filterBtnText: { fontSize: 12, color: colors.textSecondary },
14101
14159
  filterBtnTextActive: { color: "#fff", fontWeight: "600" },
14160
+ roleSection: { marginBottom: 12 },
14161
+ roleBar: { flexDirection: "row", marginBottom: 6 },
14162
+ roleBtn: { flexDirection: "row", alignItems: "center", gap: 5, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
14163
+ roleBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
14164
+ roleBtnText: { fontSize: 11, color: colors.textMuted },
14165
+ roleBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
14166
+ roleDot: { width: 6, height: 6, borderRadius: 3 },
14167
+ loginHint: { borderRadius: 6, padding: 8, borderWidth: 1 },
14168
+ loginHintText: { fontSize: 11, color: colors.textSecondary },
14169
+ roleBadgeRow: { flexDirection: "row", alignItems: "center", gap: 3 },
14170
+ roleBadgeDot: { width: 5, height: 5, borderRadius: 3 },
14102
14171
  folder: { marginBottom: 12 },
14103
14172
  folderHeader: { flexDirection: "row", alignItems: "center", gap: 8, paddingVertical: 8, paddingHorizontal: 4 },
14104
14173
  folderToggle: { fontSize: 10, color: colors.textMuted, width: 14 },
@@ -14111,7 +14180,7 @@ var styles3 = import_react_native5.StyleSheet.create({
14111
14180
  testBadge: { fontSize: 16, marginRight: 10, width: 20 },
14112
14181
  testInfo: { flex: 1 },
14113
14182
  testTitle: { fontSize: 14, color: colors.textPrimary, marginBottom: 2 },
14114
- testMetaRow: { flexDirection: "row", alignItems: "center", gap: 6 },
14183
+ testMetaRow: { flexDirection: "row", alignItems: "center", gap: 6, flexWrap: "wrap" },
14115
14184
  retestTag: { backgroundColor: "#422006", borderWidth: 1, borderColor: "#854d0e", borderRadius: 4, paddingHorizontal: 5, paddingVertical: 1 },
14116
14185
  retestTagText: { fontSize: 10, fontWeight: "600", color: "#fbbf24" },
14117
14186
  testMeta: { fontSize: 11, color: colors.textDim },
@@ -14453,11 +14522,14 @@ function ReportScreen({ nav, prefill }) {
14453
14522
  const [affectedScreen, setAffectedScreen] = (0, import_react10.useState)("");
14454
14523
  const [submitting, setSubmitting] = (0, import_react10.useState)(false);
14455
14524
  const [error, setError] = (0, import_react10.useState)(null);
14525
+ const submittingRef = (0, import_react10.useRef)(false);
14456
14526
  const images = useImageAttachments(uploadImage, 5, "screenshots");
14457
14527
  const isRetestFailure = prefill?.type === "test_fail";
14458
14528
  const isBugType = reportType === "bug" || reportType === "test_fail";
14459
14529
  const handleSubmit = async () => {
14460
14530
  if (!client || !description.trim()) return;
14531
+ if (submittingRef.current) return;
14532
+ submittingRef.current = true;
14461
14533
  setSubmitting(true);
14462
14534
  setError(null);
14463
14535
  try {
@@ -14481,17 +14553,18 @@ function ReportScreen({ nav, prefill }) {
14481
14553
  console.error("BugBear: Report submission failed", result.error);
14482
14554
  setError(result.error || "Failed to submit report. Please try again.");
14483
14555
  setSubmitting(false);
14556
+ submittingRef.current = false;
14484
14557
  return;
14485
14558
  }
14486
14559
  if (prefill?.assignmentId) {
14487
14560
  await refreshAssignments();
14488
14561
  }
14489
- setSubmitting(false);
14490
14562
  nav.replace({ name: "REPORT_SUCCESS" });
14491
14563
  } catch (err) {
14492
14564
  console.error("BugBear: Report submission error", err);
14493
14565
  setError(err instanceof Error ? err.message : "An unexpected error occurred. Please try again.");
14494
14566
  setSubmitting(false);
14567
+ submittingRef.current = false;
14495
14568
  }
14496
14569
  };
14497
14570
  return /* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, null, isRetestFailure ? /* @__PURE__ */ import_react10.default.createElement(import_react10.default.Fragment, null, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, { style: styles7.retestBanner }, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles7.retestIcon }, "\u{1F504}"), /* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, null, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles7.retestTitle }, "Bug Still Present"), /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles7.retestSubtitle }, "The fix did not resolve this issue"))), /* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, { style: styles7.section }, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: shared.label }, "Severity"), /* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, { style: styles7.severityRow }, [
package/dist/index.mjs CHANGED
@@ -11620,6 +11620,7 @@ var HOSTED_BUGBEAR_ANON_KEY = getEnvVar("BUGBEAR_ANON_KEY") || getEnvVar("NEXT_P
11620
11620
  var BugBearClient = class {
11621
11621
  constructor(config) {
11622
11622
  this.navigationHistory = [];
11623
+ this.reportSubmitInFlight = false;
11623
11624
  this.config = config;
11624
11625
  this.supabase = createClient(
11625
11626
  config.supabaseUrl || DEFAULT_SUPABASE_URL,
@@ -11680,6 +11681,10 @@ var BugBearClient = class {
11680
11681
  * Submit a report
11681
11682
  */
11682
11683
  async submitReport(report) {
11684
+ if (this.reportSubmitInFlight) {
11685
+ return { success: false, error: "A report is already being submitted" };
11686
+ }
11687
+ this.reportSubmitInFlight = true;
11683
11688
  try {
11684
11689
  const validationError = this.validateReport(report);
11685
11690
  if (validationError) {
@@ -11731,6 +11736,8 @@ var BugBearClient = class {
11731
11736
  } catch (err) {
11732
11737
  const message = err instanceof Error ? err.message : "Unknown error";
11733
11738
  return { success: false, error: message };
11739
+ } finally {
11740
+ this.reportSubmitInFlight = false;
11734
11741
  }
11735
11742
  }
11736
11743
  /**
@@ -11771,6 +11778,14 @@ var BugBearClient = class {
11771
11778
  name,
11772
11779
  description,
11773
11780
  sort_order
11781
+ ),
11782
+ role:project_roles(
11783
+ id,
11784
+ name,
11785
+ slug,
11786
+ color,
11787
+ description,
11788
+ login_hint
11774
11789
  )
11775
11790
  )
11776
11791
  `).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["pending", "in_progress"]).order("created_at", { ascending: true }).limit(100);
@@ -11808,6 +11823,14 @@ var BugBearClient = class {
11808
11823
  name: item.test_case.group.name,
11809
11824
  description: item.test_case.group.description,
11810
11825
  sortOrder: item.test_case.group.sort_order
11826
+ } : void 0,
11827
+ role: item.test_case.role ? {
11828
+ id: item.test_case.role.id,
11829
+ name: item.test_case.role.name,
11830
+ slug: item.test_case.role.slug,
11831
+ color: item.test_case.role.color,
11832
+ description: item.test_case.role.description,
11833
+ loginHint: item.test_case.role.login_hint
11811
11834
  } : void 0
11812
11835
  }
11813
11836
  }));
@@ -12223,8 +12246,8 @@ var BugBearClient = class {
12223
12246
  return "Maximum 10 screenshots allowed";
12224
12247
  }
12225
12248
  for (const url of report.screenshots) {
12226
- if (typeof url !== "string" || url.length > 2e3) {
12227
- return "Invalid screenshot URL";
12249
+ if (typeof url !== "string" || url.length > 2e3 || !/^https?:\/\//i.test(url)) {
12250
+ return "Invalid screenshot URL (must be an HTTP/HTTPS URL)";
12228
12251
  }
12229
12252
  }
12230
12253
  }
@@ -12613,7 +12636,10 @@ var BugBearClient = class {
12613
12636
  content
12614
12637
  };
12615
12638
  if (attachments && attachments.length > 0) {
12616
- insertData.attachments = attachments;
12639
+ const safeAttachments = attachments.filter((a) => /^https?:\/\//i.test(a.url));
12640
+ if (safeAttachments.length > 0) {
12641
+ insertData.attachments = safeAttachments;
12642
+ }
12617
12643
  }
12618
12644
  const { error } = await this.supabase.from("discussion_messages").insert(insertData);
12619
12645
  if (error) {
@@ -13213,14 +13239,14 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
13213
13239
  }
13214
13240
 
13215
13241
  // src/BugBearButton.tsx
13216
- import React14, { useState as useState10, useRef as useRef2 } from "react";
13242
+ import React14, { useState as useState10, useRef as useRef3 } from "react";
13217
13243
  import {
13218
13244
  View as View13,
13219
13245
  Text as Text13,
13220
13246
  Image as Image3,
13221
13247
  TouchableOpacity as TouchableOpacity12,
13222
13248
  Modal as Modal2,
13223
- ScrollView as ScrollView2,
13249
+ ScrollView as ScrollView3,
13224
13250
  StyleSheet as StyleSheet14,
13225
13251
  Dimensions as Dimensions2,
13226
13252
  KeyboardAvoidingView,
@@ -13999,14 +14025,23 @@ var styles2 = StyleSheet3.create({
13999
14025
 
14000
14026
  // src/widget/screens/TestListScreen.tsx
14001
14027
  import React4, { useState as useState3, useMemo as useMemo2, useCallback as useCallback3, useEffect as useEffect4 } from "react";
14002
- import { View as View3, Text as Text3, TouchableOpacity as TouchableOpacity3, StyleSheet as StyleSheet4 } from "react-native";
14028
+ import { View as View3, Text as Text3, TouchableOpacity as TouchableOpacity3, StyleSheet as StyleSheet4, ScrollView } from "react-native";
14003
14029
  function TestListScreen({ nav }) {
14004
14030
  const { assignments, currentAssignment, refreshAssignments } = useBugBear();
14005
14031
  const [filter, setFilter] = useState3("all");
14032
+ const [roleFilter, setRoleFilter] = useState3(null);
14006
14033
  const [collapsedFolders, setCollapsedFolders] = useState3(/* @__PURE__ */ new Set());
14007
14034
  useEffect4(() => {
14008
14035
  refreshAssignments();
14009
14036
  }, []);
14037
+ const availableRoles = useMemo2(() => {
14038
+ const roleMap = /* @__PURE__ */ new Map();
14039
+ for (const a of assignments) {
14040
+ if (a.testCase.role) roleMap.set(a.testCase.role.id, a.testCase.role);
14041
+ }
14042
+ return Array.from(roleMap.values());
14043
+ }, [assignments]);
14044
+ const selectedRole = availableRoles.find((r) => r.id === roleFilter);
14010
14045
  const groupedAssignments = useMemo2(() => {
14011
14046
  const groups = /* @__PURE__ */ new Map();
14012
14047
  for (const assignment of assignments) {
@@ -14050,16 +14085,39 @@ function TestListScreen({ nav }) {
14050
14085
  });
14051
14086
  }, []);
14052
14087
  const filterAssignment = (a) => {
14088
+ if (roleFilter && a.testCase.role?.id !== roleFilter) return false;
14053
14089
  if (filter === "pending") return a.status === "pending" || a.status === "in_progress";
14054
14090
  if (filter === "completed") return a.status === "passed" || a.status === "failed";
14055
14091
  return true;
14056
14092
  };
14057
- return /* @__PURE__ */ React4.createElement(View3, null, /* @__PURE__ */ React4.createElement(View3, { style: styles3.filterBar }, ["all", "pending", "completed"].map((f) => /* @__PURE__ */ React4.createElement(TouchableOpacity3, { key: f, style: [styles3.filterBtn, filter === f && styles3.filterBtnActive], onPress: () => setFilter(f) }, /* @__PURE__ */ React4.createElement(Text3, { style: [styles3.filterBtnText, filter === f && styles3.filterBtnTextActive] }, f === "all" ? `All (${assignments.length})` : f === "pending" ? `To Do (${assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length})` : `Done (${assignments.filter((a) => a.status === "passed" || a.status === "failed").length})`)))), groupedAssignments.map((folder) => {
14093
+ return /* @__PURE__ */ React4.createElement(View3, null, /* @__PURE__ */ React4.createElement(View3, { style: styles3.filterBar }, ["all", "pending", "completed"].map((f) => /* @__PURE__ */ React4.createElement(TouchableOpacity3, { key: f, style: [styles3.filterBtn, filter === f && styles3.filterBtnActive], onPress: () => setFilter(f) }, /* @__PURE__ */ React4.createElement(Text3, { style: [styles3.filterBtnText, filter === f && styles3.filterBtnTextActive] }, f === "all" ? `All (${assignments.length})` : f === "pending" ? `To Do (${assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length})` : `Done (${assignments.filter((a) => a.status === "passed" || a.status === "failed").length})`)))), availableRoles.length >= 2 && /* @__PURE__ */ React4.createElement(View3, { style: styles3.roleSection }, /* @__PURE__ */ React4.createElement(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles3.roleBar }, /* @__PURE__ */ React4.createElement(
14094
+ TouchableOpacity3,
14095
+ {
14096
+ style: [styles3.roleBtn, !roleFilter && styles3.roleBtnActive],
14097
+ onPress: () => setRoleFilter(null)
14098
+ },
14099
+ /* @__PURE__ */ React4.createElement(Text3, { style: [styles3.roleBtnText, !roleFilter && styles3.roleBtnTextActive] }, "All Roles")
14100
+ ), availableRoles.map((role) => {
14101
+ const isActive = roleFilter === role.id;
14102
+ return /* @__PURE__ */ React4.createElement(
14103
+ TouchableOpacity3,
14104
+ {
14105
+ key: role.id,
14106
+ style: [
14107
+ styles3.roleBtn,
14108
+ isActive && { backgroundColor: role.color + "20", borderColor: role.color + "60", borderWidth: 1 }
14109
+ ],
14110
+ onPress: () => setRoleFilter(isActive ? null : role.id)
14111
+ },
14112
+ /* @__PURE__ */ React4.createElement(View3, { style: [styles3.roleDot, { backgroundColor: role.color }] }),
14113
+ /* @__PURE__ */ React4.createElement(Text3, { style: [styles3.roleBtnText, isActive && { color: role.color, fontWeight: "600" }] }, role.name)
14114
+ );
14115
+ })), selectedRole?.loginHint && /* @__PURE__ */ React4.createElement(View3, { style: [styles3.loginHint, { backgroundColor: selectedRole.color + "10", borderColor: selectedRole.color + "30" }] }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.loginHintText }, "Log in as: ", selectedRole.loginHint))), groupedAssignments.map((folder) => {
14058
14116
  const folderId = folder.group?.id || "ungrouped";
14059
14117
  const isCollapsed = collapsedFolders.has(folderId);
14060
14118
  const filtered = folder.assignments.filter(filterAssignment);
14061
14119
  if (filtered.length === 0 && filter !== "all") return null;
14062
- return /* @__PURE__ */ React4.createElement(View3, { key: folderId, style: styles3.folder }, /* @__PURE__ */ React4.createElement(TouchableOpacity3, { style: styles3.folderHeader, onPress: () => toggleFolder(folderId) }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.folderToggle }, isCollapsed ? "\u25B6" : "\u25BC"), /* @__PURE__ */ React4.createElement(Text3, { style: styles3.folderName }, folder.group?.name || "Ungrouped"), /* @__PURE__ */ React4.createElement(View3, { style: styles3.folderProgress }, /* @__PURE__ */ React4.createElement(View3, { style: [styles3.folderProgressFill, { width: `${Math.round((folder.stats.passed + folder.stats.failed) / folder.stats.total * 100)}%` }] })), /* @__PURE__ */ React4.createElement(Text3, { style: styles3.folderCount }, folder.stats.passed + folder.stats.failed, "/", folder.stats.total)), !isCollapsed && filtered.map((assignment) => {
14120
+ return /* @__PURE__ */ React4.createElement(View3, { key: folderId, style: styles3.folder }, /* @__PURE__ */ React4.createElement(TouchableOpacity3, { style: styles3.folderHeader, onPress: () => toggleFolder(folderId) }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.folderToggle }, isCollapsed ? "\u25B6" : "\u25BC"), /* @__PURE__ */ React4.createElement(Text3, { style: styles3.folderName, numberOfLines: 1 }, folder.group?.name || "Ungrouped"), /* @__PURE__ */ React4.createElement(View3, { style: styles3.folderProgress }, /* @__PURE__ */ React4.createElement(View3, { style: [styles3.folderProgressFill, { width: `${folder.stats.total > 0 ? Math.round((folder.stats.passed + folder.stats.failed) / folder.stats.total * 100) : 0}%` }] })), /* @__PURE__ */ React4.createElement(Text3, { style: styles3.folderCount }, folder.stats.passed + folder.stats.failed, "/", folder.stats.total)), !isCollapsed && filtered.map((assignment) => {
14063
14121
  const badge = getStatusBadge(assignment.status);
14064
14122
  const isCurrent = currentAssignment?.id === assignment.id;
14065
14123
  return /* @__PURE__ */ React4.createElement(
@@ -14070,17 +14128,28 @@ function TestListScreen({ nav }) {
14070
14128
  onPress: () => nav.push({ name: "TEST_DETAIL", testId: assignment.id })
14071
14129
  },
14072
14130
  /* @__PURE__ */ React4.createElement(Text3, { style: styles3.testBadge }, badge.icon),
14073
- /* @__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)))
14131
+ /* @__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.testKey, " \xB7 ", assignment.testCase.priority), assignment.testCase.role && /* @__PURE__ */ React4.createElement(View3, { style: styles3.roleBadgeRow }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.testMeta }, " \xB7 "), /* @__PURE__ */ React4.createElement(View3, { style: [styles3.roleBadgeDot, { backgroundColor: assignment.testCase.role.color }] }), /* @__PURE__ */ React4.createElement(Text3, { style: [styles3.testMeta, { color: assignment.testCase.role.color, fontWeight: "500" }] }, assignment.testCase.role.name))))
14074
14132
  );
14075
14133
  }));
14076
- }), /* @__PURE__ */ React4.createElement(TouchableOpacity3, { style: styles3.refreshBtn, onPress: refreshAssignments }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.refreshText }, "\u21BB Refresh")));
14134
+ }), /* @__PURE__ */ React4.createElement(TouchableOpacity3, { style: styles3.refreshBtn, onPress: refreshAssignments }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.refreshText }, "\u21BB", " Refresh")));
14077
14135
  }
14078
14136
  var styles3 = StyleSheet4.create({
14079
- filterBar: { flexDirection: "row", gap: 8, marginBottom: 16 },
14137
+ filterBar: { flexDirection: "row", gap: 8, marginBottom: 8 },
14080
14138
  filterBtn: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 8, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
14081
14139
  filterBtnActive: { backgroundColor: colors.blue, borderColor: colors.blue },
14082
14140
  filterBtnText: { fontSize: 12, color: colors.textSecondary },
14083
14141
  filterBtnTextActive: { color: "#fff", fontWeight: "600" },
14142
+ roleSection: { marginBottom: 12 },
14143
+ roleBar: { flexDirection: "row", marginBottom: 6 },
14144
+ roleBtn: { flexDirection: "row", alignItems: "center", gap: 5, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
14145
+ roleBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
14146
+ roleBtnText: { fontSize: 11, color: colors.textMuted },
14147
+ roleBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
14148
+ roleDot: { width: 6, height: 6, borderRadius: 3 },
14149
+ loginHint: { borderRadius: 6, padding: 8, borderWidth: 1 },
14150
+ loginHintText: { fontSize: 11, color: colors.textSecondary },
14151
+ roleBadgeRow: { flexDirection: "row", alignItems: "center", gap: 3 },
14152
+ roleBadgeDot: { width: 5, height: 5, borderRadius: 3 },
14084
14153
  folder: { marginBottom: 12 },
14085
14154
  folderHeader: { flexDirection: "row", alignItems: "center", gap: 8, paddingVertical: 8, paddingHorizontal: 4 },
14086
14155
  folderToggle: { fontSize: 10, color: colors.textMuted, width: 14 },
@@ -14093,7 +14162,7 @@ var styles3 = StyleSheet4.create({
14093
14162
  testBadge: { fontSize: 16, marginRight: 10, width: 20 },
14094
14163
  testInfo: { flex: 1 },
14095
14164
  testTitle: { fontSize: 14, color: colors.textPrimary, marginBottom: 2 },
14096
- testMetaRow: { flexDirection: "row", alignItems: "center", gap: 6 },
14165
+ testMetaRow: { flexDirection: "row", alignItems: "center", gap: 6, flexWrap: "wrap" },
14097
14166
  retestTag: { backgroundColor: "#422006", borderWidth: 1, borderColor: "#854d0e", borderRadius: 4, paddingHorizontal: 5, paddingVertical: 1 },
14098
14167
  retestTagText: { fontSize: 10, fontWeight: "600", color: "#fbbf24" },
14099
14168
  testMeta: { fontSize: 11, color: colors.textDim },
@@ -14188,10 +14257,10 @@ import { View as View5, Text as Text5, TouchableOpacity as TouchableOpacity5, St
14188
14257
 
14189
14258
  // src/widget/ImagePreviewStrip.tsx
14190
14259
  import React5 from "react";
14191
- import { View as View4, Text as Text4, TouchableOpacity as TouchableOpacity4, ScrollView, Image, ActivityIndicator, StyleSheet as StyleSheet5 } from "react-native";
14260
+ import { View as View4, Text as Text4, TouchableOpacity as TouchableOpacity4, ScrollView as ScrollView2, Image, ActivityIndicator, StyleSheet as StyleSheet5 } from "react-native";
14192
14261
  function ImagePreviewStrip({ images, onRemove }) {
14193
14262
  if (images.length === 0) return null;
14194
- return /* @__PURE__ */ React5.createElement(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles4.strip }, images.map((img) => /* @__PURE__ */ React5.createElement(View4, { key: img.id, style: styles4.thumbContainer }, /* @__PURE__ */ React5.createElement(Image, { source: { uri: img.localUri }, style: styles4.thumb }), img.status === "uploading" && /* @__PURE__ */ React5.createElement(View4, { style: styles4.thumbOverlay }, /* @__PURE__ */ React5.createElement(ActivityIndicator, { size: "small", color: "#fff" })), img.status === "error" && /* @__PURE__ */ React5.createElement(View4, { style: [styles4.thumbOverlay, styles4.thumbOverlayError] }, /* @__PURE__ */ React5.createElement(Text4, { style: styles4.thumbErrorText }, "!")), /* @__PURE__ */ React5.createElement(TouchableOpacity4, { style: styles4.thumbRemove, onPress: () => onRemove(img.id) }, /* @__PURE__ */ React5.createElement(Text4, { style: styles4.thumbRemoveText }, "\u2715")))));
14263
+ return /* @__PURE__ */ React5.createElement(ScrollView2, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles4.strip }, images.map((img) => /* @__PURE__ */ React5.createElement(View4, { key: img.id, style: styles4.thumbContainer }, /* @__PURE__ */ React5.createElement(Image, { source: { uri: img.localUri }, style: styles4.thumb }), img.status === "uploading" && /* @__PURE__ */ React5.createElement(View4, { style: styles4.thumbOverlay }, /* @__PURE__ */ React5.createElement(ActivityIndicator, { size: "small", color: "#fff" })), img.status === "error" && /* @__PURE__ */ React5.createElement(View4, { style: [styles4.thumbOverlay, styles4.thumbOverlayError] }, /* @__PURE__ */ React5.createElement(Text4, { style: styles4.thumbErrorText }, "!")), /* @__PURE__ */ React5.createElement(TouchableOpacity4, { style: styles4.thumbRemove, onPress: () => onRemove(img.id) }, /* @__PURE__ */ React5.createElement(Text4, { style: styles4.thumbRemoveText }, "\u2715")))));
14195
14264
  }
14196
14265
  var styles4 = StyleSheet5.create({
14197
14266
  strip: {
@@ -14425,7 +14494,7 @@ var styles6 = StyleSheet7.create({
14425
14494
  });
14426
14495
 
14427
14496
  // src/widget/screens/ReportScreen.tsx
14428
- import React8, { useState as useState6 } from "react";
14497
+ import React8, { useState as useState6, useRef as useRef2 } from "react";
14429
14498
  import { View as View7, Text as Text7, TouchableOpacity as TouchableOpacity7, TextInput as TextInput3, StyleSheet as StyleSheet8 } from "react-native";
14430
14499
  function ReportScreen({ nav, prefill }) {
14431
14500
  const { client, getDeviceInfo, uploadImage, refreshAssignments } = useBugBear();
@@ -14435,11 +14504,14 @@ function ReportScreen({ nav, prefill }) {
14435
14504
  const [affectedScreen, setAffectedScreen] = useState6("");
14436
14505
  const [submitting, setSubmitting] = useState6(false);
14437
14506
  const [error, setError] = useState6(null);
14507
+ const submittingRef = useRef2(false);
14438
14508
  const images = useImageAttachments(uploadImage, 5, "screenshots");
14439
14509
  const isRetestFailure = prefill?.type === "test_fail";
14440
14510
  const isBugType = reportType === "bug" || reportType === "test_fail";
14441
14511
  const handleSubmit = async () => {
14442
14512
  if (!client || !description.trim()) return;
14513
+ if (submittingRef.current) return;
14514
+ submittingRef.current = true;
14443
14515
  setSubmitting(true);
14444
14516
  setError(null);
14445
14517
  try {
@@ -14463,17 +14535,18 @@ function ReportScreen({ nav, prefill }) {
14463
14535
  console.error("BugBear: Report submission failed", result.error);
14464
14536
  setError(result.error || "Failed to submit report. Please try again.");
14465
14537
  setSubmitting(false);
14538
+ submittingRef.current = false;
14466
14539
  return;
14467
14540
  }
14468
14541
  if (prefill?.assignmentId) {
14469
14542
  await refreshAssignments();
14470
14543
  }
14471
- setSubmitting(false);
14472
14544
  nav.replace({ name: "REPORT_SUCCESS" });
14473
14545
  } catch (err) {
14474
14546
  console.error("BugBear: Report submission error", err);
14475
14547
  setError(err instanceof Error ? err.message : "An unexpected error occurred. Please try again.");
14476
14548
  setSubmitting(false);
14549
+ submittingRef.current = false;
14477
14550
  }
14478
14551
  };
14479
14552
  return /* @__PURE__ */ React8.createElement(View7, null, isRetestFailure ? /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(View7, { style: styles7.retestBanner }, /* @__PURE__ */ React8.createElement(Text7, { style: styles7.retestIcon }, "\u{1F504}"), /* @__PURE__ */ React8.createElement(View7, null, /* @__PURE__ */ React8.createElement(Text7, { style: styles7.retestTitle }, "Bug Still Present"), /* @__PURE__ */ React8.createElement(Text7, { style: styles7.retestSubtitle }, "The fix did not resolve this issue"))), /* @__PURE__ */ React8.createElement(View7, { style: styles7.section }, /* @__PURE__ */ React8.createElement(Text7, { style: shared.label }, "Severity"), /* @__PURE__ */ React8.createElement(View7, { style: styles7.severityRow }, [
@@ -15003,9 +15076,9 @@ function BugBearButton({
15003
15076
  return { x, y };
15004
15077
  };
15005
15078
  const initialPos = getInitialPosition();
15006
- const pan = useRef2(new Animated.ValueXY(initialPos)).current;
15007
- const isDragging = useRef2(false);
15008
- const panResponder = useRef2(
15079
+ const pan = useRef3(new Animated.ValueXY(initialPos)).current;
15080
+ const isDragging = useRef3(false);
15081
+ const panResponder = useRef3(
15009
15082
  PanResponder.create({
15010
15083
  onStartShouldSetPanResponder: () => draggable,
15011
15084
  onMoveShouldSetPanResponder: (_, gs) => draggable && (Math.abs(gs.dx) > 5 || Math.abs(gs.dy) > 5),
@@ -15157,7 +15230,7 @@ function BugBearButton({
15157
15230
  style: styles13.modalOverlay
15158
15231
  },
15159
15232
  /* @__PURE__ */ React14.createElement(View13, { style: styles13.modalContainer }, /* @__PURE__ */ React14.createElement(View13, { style: styles13.header }, /* @__PURE__ */ React14.createElement(View13, { style: styles13.headerLeft }, canGoBack ? /* @__PURE__ */ React14.createElement(View13, { style: styles13.headerNavRow }, /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: () => nav.pop(), style: styles13.backButton }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.backText }, "\u2190 Back")), /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: () => nav.reset(), style: styles13.homeButton }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.homeText }, "\u{1F3E0}"))) : /* @__PURE__ */ React14.createElement(View13, { style: styles13.headerTitleRow }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ React14.createElement(Text13, { style: styles13.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: handleClose, style: styles13.closeButton }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.closeText }, "\u2715"))), /* @__PURE__ */ React14.createElement(
15160
- ScrollView2,
15233
+ ScrollView3,
15161
15234
  {
15162
15235
  style: styles13.content,
15163
15236
  contentContainerStyle: styles13.contentContainer,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbearai/react-native",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
4
4
  "description": "BugBear React Native components for mobile apps",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -49,7 +49,7 @@
49
49
  }
50
50
  },
51
51
  "devDependencies": {
52
- "@bbearai/core": "^0.3.0",
52
+ "@bbearai/core": "^0.4.0",
53
53
  "@eslint/js": "^9.39.2",
54
54
  "@types/react": "^18.2.0",
55
55
  "eslint": "^9.39.2",