@bbearai/react-native 0.8.4 → 0.9.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.
Files changed (3) hide show
  1. package/dist/index.js +183 -81
  2. package/dist/index.mjs +186 -84
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -12793,55 +12793,66 @@ var BugBearClient = class {
12793
12793
  ...pendingResult.data || [],
12794
12794
  ...completedResult.data || []
12795
12795
  ];
12796
- const mapItem = (item) => ({
12797
- id: item.id,
12798
- status: item.status,
12799
- startedAt: item.started_at,
12800
- skipReason: item.skip_reason,
12801
- isVerification: item.is_verification || false,
12802
- originalReportId: item.original_report_id,
12803
- testCase: {
12804
- id: item.test_case.id,
12805
- title: item.test_case.title,
12806
- testKey: item.test_case.test_key,
12807
- description: item.test_case.description,
12808
- steps: item.test_case.steps,
12809
- expectedResult: item.test_case.expected_result,
12810
- priority: item.test_case.priority,
12811
- targetRoute: item.test_case.target_route,
12812
- track: item.test_case.track ? {
12813
- id: item.test_case.track.id,
12814
- name: item.test_case.track.name,
12815
- icon: item.test_case.track.icon,
12816
- color: item.test_case.track.color,
12817
- testTemplate: item.test_case.track.test_template,
12818
- rubricMode: item.test_case.track.rubric_mode || "pass_fail",
12819
- description: item.test_case.track.description
12820
- } : void 0,
12821
- group: item.test_case.group ? {
12822
- id: item.test_case.group.id,
12823
- name: item.test_case.group.name,
12824
- description: item.test_case.group.description,
12825
- sortOrder: item.test_case.group.sort_order
12826
- } : void 0,
12827
- role: item.test_case.role ? {
12828
- id: item.test_case.role.id,
12829
- name: item.test_case.role.name,
12830
- slug: item.test_case.role.slug,
12831
- color: item.test_case.role.color,
12832
- description: item.test_case.role.description,
12833
- loginHint: item.test_case.role.login_hint
12834
- } : void 0,
12835
- platforms: item.test_case.platforms || void 0
12836
- }
12837
- });
12838
- const mapped = allData.filter((item) => {
12839
- if (!item.test_case) {
12840
- console.warn("BugBear: Assignment returned without test_case", { id: item.id });
12841
- return false;
12842
- }
12843
- return true;
12844
- }).map(mapItem);
12796
+ const mapItem = (item) => {
12797
+ const tc = item.test_case;
12798
+ return {
12799
+ id: item.id,
12800
+ status: item.status,
12801
+ startedAt: item.started_at,
12802
+ skipReason: item.skip_reason,
12803
+ isVerification: item.is_verification || false,
12804
+ originalReportId: item.original_report_id,
12805
+ testCase: tc ? {
12806
+ id: tc.id,
12807
+ title: tc.title,
12808
+ testKey: tc.test_key,
12809
+ description: tc.description,
12810
+ steps: tc.steps,
12811
+ expectedResult: tc.expected_result,
12812
+ priority: tc.priority,
12813
+ targetRoute: tc.target_route,
12814
+ track: tc.track ? {
12815
+ id: tc.track.id,
12816
+ name: tc.track.name,
12817
+ icon: tc.track.icon,
12818
+ color: tc.track.color,
12819
+ testTemplate: tc.track.test_template,
12820
+ rubricMode: tc.track.rubric_mode || "pass_fail",
12821
+ description: tc.track.description
12822
+ } : void 0,
12823
+ group: tc.group ? {
12824
+ id: tc.group.id,
12825
+ name: tc.group.name,
12826
+ description: tc.group.description,
12827
+ sortOrder: tc.group.sort_order
12828
+ } : void 0,
12829
+ role: tc.role ? {
12830
+ id: tc.role.id,
12831
+ name: tc.role.name,
12832
+ slug: tc.role.slug,
12833
+ color: tc.role.color,
12834
+ description: tc.role.description,
12835
+ loginHint: tc.role.login_hint
12836
+ } : void 0,
12837
+ platforms: tc.platforms || void 0
12838
+ } : {
12839
+ // Standalone verification assignment (bug reported without a test case)
12840
+ id: item.original_report_id || item.id,
12841
+ title: item.notes || "Bug Verification",
12842
+ testKey: "VERIFY",
12843
+ description: "Verify that the reported bug has been fixed",
12844
+ steps: [],
12845
+ expectedResult: "The bug should no longer be reproducible",
12846
+ priority: "P1",
12847
+ targetRoute: void 0,
12848
+ track: void 0,
12849
+ group: void 0,
12850
+ role: void 0,
12851
+ platforms: void 0
12852
+ }
12853
+ };
12854
+ };
12855
+ const mapped = allData.map(mapItem);
12845
12856
  mapped.sort((a, b) => {
12846
12857
  if (a.isVerification && !b.isVerification) return -1;
12847
12858
  if (!a.isVerification && b.isVerification) return 1;
@@ -12935,7 +12946,7 @@ var BugBearClient = class {
12935
12946
  async updateAssignmentStatus(assignmentId, status, options) {
12936
12947
  try {
12937
12948
  await this.ensureReady();
12938
- const { data: currentAssignment, error: fetchError } = await this.supabase.from("test_assignments").select("status, started_at").eq("id", assignmentId).single();
12949
+ const { data: currentAssignment, error: fetchError } = await this.supabase.from("test_assignments").select("status, started_at, tester_id, project_id").eq("id", assignmentId).single();
12939
12950
  if (fetchError || !currentAssignment) {
12940
12951
  console.error("BugBear: Assignment not found", {
12941
12952
  message: fetchError?.message,
@@ -12956,6 +12967,19 @@ var BugBearClient = class {
12956
12967
  const completedAt = /* @__PURE__ */ new Date();
12957
12968
  durationSeconds = Math.round((completedAt.getTime() - startedAt.getTime()) / 1e3);
12958
12969
  updateData.duration_seconds = durationSeconds;
12970
+ if (currentAssignment.tester_id && currentAssignment.project_id) {
12971
+ try {
12972
+ const { data: activeTime } = await this.supabase.rpc("compute_assignment_active_time", {
12973
+ p_tester_id: currentAssignment.tester_id,
12974
+ p_project_id: currentAssignment.project_id,
12975
+ p_started_at: currentAssignment.started_at,
12976
+ p_completed_at: updateData.completed_at
12977
+ });
12978
+ updateData.active_seconds = typeof activeTime === "number" ? activeTime : Math.min(durationSeconds, 1800);
12979
+ } catch {
12980
+ updateData.active_seconds = Math.min(durationSeconds, 1800);
12981
+ }
12982
+ }
12959
12983
  }
12960
12984
  }
12961
12985
  if (options?.notes) {
@@ -13034,6 +13058,7 @@ var BugBearClient = class {
13034
13058
  started_at: (/* @__PURE__ */ new Date()).toISOString(),
13035
13059
  completed_at: null,
13036
13060
  duration_seconds: null,
13061
+ active_seconds: null,
13037
13062
  skip_reason: null
13038
13063
  }).eq("id", assignmentId).eq("status", current.status);
13039
13064
  if (error) {
@@ -13341,7 +13366,11 @@ var BugBearClient = class {
13341
13366
  verifiedByName: row.verified_by_name || void 0,
13342
13367
  verifiedAt: row.verified_at || void 0,
13343
13368
  originalBugId: row.original_bug_id || void 0,
13344
- originalBugTitle: row.original_bug_title || void 0
13369
+ originalBugTitle: row.original_bug_title || void 0,
13370
+ resolutionNotes: row.resolution_notes || void 0,
13371
+ fixCommitSha: row.code_context?.fix?.commit_sha || void 0,
13372
+ fixCommitMessage: row.code_context?.fix?.commit_message || void 0,
13373
+ fixFilesChanged: row.code_context?.fix?.files_changed || void 0
13345
13374
  }));
13346
13375
  } catch (err) {
13347
13376
  console.error("BugBear: Error fetching issues", err);
@@ -17923,7 +17952,10 @@ function IssueListScreen({ nav, category }) {
17923
17952
  styles5.tab,
17924
17953
  { borderBottomColor: isActive ? catConfig.accent : "transparent" }
17925
17954
  ],
17926
- onPress: () => setActiveCategory(cat),
17955
+ onPress: () => {
17956
+ setActiveCategory(cat);
17957
+ nav.replace({ name: "ISSUE_LIST", category: cat });
17958
+ },
17927
17959
  activeOpacity: 0.7
17928
17960
  },
17929
17961
  /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: [
@@ -18234,7 +18266,14 @@ function IssueDetailScreen({ nav, issue }) {
18234
18266
  activeOpacity: 0.7
18235
18267
  },
18236
18268
  /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles5.reopenCancelText }, "Cancel")
18237
- ))), issue.screenshotUrls && issue.screenshotUrls.length > 0 && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles5.screenshotSection }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles5.screenshotLabel }, "Screenshots (", issue.screenshotUrls.length, ")"), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles5.screenshotRow }, issue.screenshotUrls.map((url, i) => /* @__PURE__ */ import_react19.default.createElement(import_react_native18.TouchableOpacity, { key: i, onPress: () => import_react_native18.Linking.openURL(url), activeOpacity: 0.7 }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Image, { source: { uri: url }, style: styles5.screenshotThumb }))))), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles5.metaSection }, issue.reporterName && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles5.metaText }, "Reported by ", issue.reporterName), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles5.metaTextSmall }, "Created ", formatRelativeTime(issue.createdAt), " ", "\xB7", " Updated ", formatRelativeTime(issue.updatedAt))), dashboardUrl && /* @__PURE__ */ import_react19.default.createElement(
18269
+ ))), issue.status === "ready_to_test" && (issue.fixCommitSha || issue.resolutionNotes) && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: {
18270
+ backgroundColor: "rgba(59, 130, 246, 0.1)",
18271
+ borderWidth: 1,
18272
+ borderColor: "rgba(96, 165, 250, 0.2)",
18273
+ borderRadius: 8,
18274
+ padding: 12,
18275
+ marginBottom: 12
18276
+ } }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: { flexDirection: "row", alignItems: "center", gap: 6, marginBottom: 8 } }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: { fontSize: 13 } }, "\u{1F527}"), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: { fontSize: 12, fontWeight: "600", color: "#60a5fa" } }, "Fix Details")), issue.resolutionNotes ? /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: { fontSize: 12, color: colors.textSecondary, marginBottom: 6, lineHeight: 17 } }, issue.resolutionNotes) : null, issue.fixCommitSha ? /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: { fontSize: 11, color: colors.textDim, fontFamily: "monospace" } }, "Commit: ", issue.fixCommitSha.slice(0, 7), issue.fixCommitMessage && ` \u2014 ${issue.fixCommitMessage}`) : null, issue.fixFilesChanged && issue.fixFilesChanged.length > 0 ? /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: { fontSize: 11, color: colors.textDim, marginTop: 4 } }, issue.fixFilesChanged.length, " file", issue.fixFilesChanged.length > 1 ? "s" : "", " changed") : null), issue.screenshotUrls && issue.screenshotUrls.length > 0 && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles5.screenshotSection }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles5.screenshotLabel }, "Screenshots (", issue.screenshotUrls.length, ")"), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles5.screenshotRow }, issue.screenshotUrls.map((url, i) => /* @__PURE__ */ import_react19.default.createElement(import_react_native18.TouchableOpacity, { key: i, onPress: () => import_react_native18.Linking.openURL(url), activeOpacity: 0.7 }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Image, { source: { uri: url }, style: styles5.screenshotThumb }))))), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles5.metaSection }, issue.reporterName && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles5.metaText }, "Reported by ", issue.reporterName), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles5.metaTextSmall }, "Created ", formatRelativeTime(issue.createdAt), " ", "\xB7", " Updated ", formatRelativeTime(issue.updatedAt))), dashboardUrl && /* @__PURE__ */ import_react19.default.createElement(
18238
18277
  import_react_native18.TouchableOpacity,
18239
18278
  {
18240
18279
  style: styles5.dashboardLink,
@@ -19115,14 +19154,31 @@ function BugBearButton({
19115
19154
  }) {
19116
19155
  const { shouldShowWidget, testerInfo, isLoading, unreadCount, assignments, widgetMode, widgetColorScheme } = useBugBear();
19117
19156
  const { currentScreen, canGoBack, push, pop, replace, reset } = useNavigation();
19118
- const [modalVisible, setModalVisible] = (0, import_react23.useState)(false);
19157
+ const [panelVisible, setPanelVisible] = (0, import_react23.useState)(false);
19158
+ const panelAnim = (0, import_react23.useRef)(new import_react_native22.Animated.Value(0)).current;
19119
19159
  const styles5 = (0, import_react23.useMemo)(() => createStyles16(), [widgetColorScheme]);
19120
19160
  const screenCaptureRef = (0, import_react23.useRef)(null);
19121
- const openModal = () => {
19161
+ const openPanel = () => {
19122
19162
  captureAppScreen().then((uri) => {
19123
19163
  screenCaptureRef.current = uri;
19124
19164
  });
19125
- setModalVisible(true);
19165
+ setPanelVisible(true);
19166
+ import_react_native22.Animated.spring(panelAnim, {
19167
+ toValue: 1,
19168
+ useNativeDriver: true,
19169
+ friction: 8,
19170
+ tension: 65
19171
+ }).start();
19172
+ };
19173
+ const closePanel = () => {
19174
+ import_react_native22.Keyboard.dismiss();
19175
+ import_react_native22.Animated.timing(panelAnim, {
19176
+ toValue: 0,
19177
+ duration: 250,
19178
+ useNativeDriver: true
19179
+ }).start(() => {
19180
+ setPanelVisible(false);
19181
+ });
19126
19182
  };
19127
19183
  const getInitialPosition = () => {
19128
19184
  const buttonSize = 56;
@@ -19173,7 +19229,7 @@ function BugBearButton({
19173
19229
  tension: 40
19174
19230
  }).start();
19175
19231
  if (!isDragging.current && Math.abs(gs.dx) < 5 && Math.abs(gs.dy) < 5) {
19176
- openModal();
19232
+ openPanel();
19177
19233
  }
19178
19234
  isDragging.current = false;
19179
19235
  }
@@ -19181,6 +19237,19 @@ function BugBearButton({
19181
19237
  ).current;
19182
19238
  const pendingTests = widgetMode === "qa" ? assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length : 0;
19183
19239
  const badgeCount = pendingTests + unreadCount;
19240
+ (0, import_react23.useEffect)(() => {
19241
+ if (!panelVisible || import_react_native22.Platform.OS !== "android") return;
19242
+ const handler = import_react_native22.BackHandler.addEventListener("hardwareBackPress", () => {
19243
+ if (canGoBack) {
19244
+ import_react_native22.Keyboard.dismiss();
19245
+ pop();
19246
+ } else {
19247
+ closePanel();
19248
+ }
19249
+ return true;
19250
+ });
19251
+ return () => handler.remove();
19252
+ }, [panelVisible, canGoBack]);
19184
19253
  if (!shouldShowWidget) return null;
19185
19254
  const getHeaderTitle = () => {
19186
19255
  switch (currentScreen.name) {
@@ -19219,8 +19288,7 @@ function BugBearButton({
19219
19288
  }
19220
19289
  };
19221
19290
  const handleClose = () => {
19222
- import_react_native22.Keyboard.dismiss();
19223
- setModalVisible(false);
19291
+ closePanel();
19224
19292
  };
19225
19293
  const nav = {
19226
19294
  push: (screen) => {
@@ -19292,7 +19360,7 @@ function BugBearButton({
19292
19360
  return /* @__PURE__ */ import_react23.default.createElement(HomeScreen, { nav });
19293
19361
  }
19294
19362
  };
19295
- return /* @__PURE__ */ import_react23.default.createElement(import_react23.default.Fragment, null, /* @__PURE__ */ import_react23.default.createElement(
19363
+ return /* @__PURE__ */ import_react23.default.createElement(import_react23.default.Fragment, null, !panelVisible && /* @__PURE__ */ import_react23.default.createElement(
19296
19364
  import_react_native22.Animated.View,
19297
19365
  {
19298
19366
  style: [styles5.fabContainer, { transform: pan.getTranslateTransform() }, buttonStyle],
@@ -19302,27 +19370,37 @@ function BugBearButton({
19302
19370
  import_react_native22.TouchableOpacity,
19303
19371
  {
19304
19372
  style: styles5.fab,
19305
- onPress: openModal,
19373
+ onPress: openPanel,
19306
19374
  activeOpacity: draggable ? 1 : 0.7
19307
19375
  },
19308
19376
  /* @__PURE__ */ import_react23.default.createElement(import_react_native22.Image, { source: { uri: BUGBEAR_LOGO_BASE64 }, style: styles5.fabIcon }),
19309
19377
  badgeCount > 0 && /* @__PURE__ */ import_react23.default.createElement(import_react_native22.View, { style: styles5.badge }, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.Text, { style: styles5.badgeText }, badgeCount > 9 ? "9+" : badgeCount))
19310
19378
  )
19311
- ), /* @__PURE__ */ import_react23.default.createElement(
19312
- import_react_native22.Modal,
19379
+ ), panelVisible && /* @__PURE__ */ import_react23.default.createElement(import_react_native22.View, { style: styles5.panelWrapper, pointerEvents: "box-none" }, /* @__PURE__ */ import_react23.default.createElement(
19380
+ import_react_native22.KeyboardAvoidingView,
19313
19381
  {
19314
- visible: modalVisible,
19315
- animationType: "slide",
19316
- transparent: true,
19317
- onRequestClose: handleClose
19382
+ behavior: import_react_native22.Platform.OS === "ios" ? "padding" : "height",
19383
+ style: styles5.panelKeyboardAvoid,
19384
+ pointerEvents: "box-none"
19318
19385
  },
19319
19386
  /* @__PURE__ */ import_react23.default.createElement(
19320
- import_react_native22.KeyboardAvoidingView,
19387
+ import_react_native22.Animated.View,
19321
19388
  {
19322
- behavior: import_react_native22.Platform.OS === "ios" ? "padding" : "height",
19323
- style: styles5.modalOverlay
19389
+ style: [
19390
+ styles5.panelContainer,
19391
+ {
19392
+ transform: [{
19393
+ translateY: panelAnim.interpolate({
19394
+ inputRange: [0, 1],
19395
+ outputRange: [screenHeight, 0]
19396
+ })
19397
+ }]
19398
+ }
19399
+ ]
19324
19400
  },
19325
- /* @__PURE__ */ import_react23.default.createElement(import_react_native22.View, { style: styles5.modalContainer }, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.View, { style: styles5.header }, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.View, { style: styles5.headerLeft }, canGoBack ? /* @__PURE__ */ import_react23.default.createElement(import_react_native22.TouchableOpacity, { onPress: () => nav.pop(), style: styles5.backButton }, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.Text, { style: styles5.backText }, "\u2190 Back")) : /* @__PURE__ */ import_react23.default.createElement(import_react_native22.View, { style: styles5.headerTitleRow }, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.Text, { style: styles5.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ import_react23.default.createElement(import_react_native22.TouchableOpacity, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.Text, { style: styles5.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ import_react23.default.createElement(import_react_native22.Text, { style: styles5.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.View, { style: styles5.headerActions }, currentScreen.name !== "HOME" && /* @__PURE__ */ import_react23.default.createElement(import_react_native22.TouchableOpacity, { onPress: () => nav.reset(), style: styles5.homeButton }, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.Text, { style: styles5.homeIcon }, "\u2302")), /* @__PURE__ */ import_react23.default.createElement(import_react_native22.TouchableOpacity, { onPress: handleClose, style: styles5.closeButton }, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.Text, { style: styles5.closeText }, "\u2715")))), /* @__PURE__ */ import_react23.default.createElement(
19401
+ /* @__PURE__ */ import_react23.default.createElement(import_react_native22.View, { style: styles5.panelHandle }, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.View, { style: styles5.panelHandleBar })),
19402
+ /* @__PURE__ */ import_react23.default.createElement(import_react_native22.View, { style: styles5.header }, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.View, { style: styles5.headerLeft }, canGoBack ? /* @__PURE__ */ import_react23.default.createElement(import_react_native22.TouchableOpacity, { onPress: () => nav.pop(), style: styles5.backButton }, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.Text, { style: styles5.backText }, "\u2190 Back")) : /* @__PURE__ */ import_react23.default.createElement(import_react_native22.View, { style: styles5.headerTitleRow }, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.Text, { style: styles5.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ import_react23.default.createElement(import_react_native22.TouchableOpacity, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.Text, { style: styles5.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ import_react23.default.createElement(import_react_native22.Text, { style: styles5.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.View, { style: styles5.headerActions }, currentScreen.name !== "HOME" && /* @__PURE__ */ import_react23.default.createElement(import_react_native22.TouchableOpacity, { onPress: () => nav.reset(), style: styles5.homeButton }, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.Text, { style: styles5.homeIcon }, "\u2302")), /* @__PURE__ */ import_react23.default.createElement(import_react_native22.TouchableOpacity, { onPress: handleClose, style: styles5.closeButton }, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.Text, { style: styles5.closeText }, "\u2715")))),
19403
+ /* @__PURE__ */ import_react23.default.createElement(
19326
19404
  import_react_native22.ScrollView,
19327
19405
  {
19328
19406
  style: styles5.content,
@@ -19331,9 +19409,9 @@ function BugBearButton({
19331
19409
  showsVerticalScrollIndicator: false
19332
19410
  },
19333
19411
  isLoading ? /* @__PURE__ */ import_react23.default.createElement(import_react_native22.View, { style: styles5.loadingContainer }, /* @__PURE__ */ import_react23.default.createElement(import_react_native22.ActivityIndicator, { size: "large", color: colors.blue }), /* @__PURE__ */ import_react23.default.createElement(import_react_native22.Text, { style: styles5.loadingText }, "Loading...")) : renderScreen()
19334
- ))
19412
+ )
19335
19413
  )
19336
- ));
19414
+ )));
19337
19415
  }
19338
19416
  function createStyles16() {
19339
19417
  return import_react_native22.StyleSheet.create({
@@ -19377,18 +19455,42 @@ function createStyles16() {
19377
19455
  fontSize: 11,
19378
19456
  fontWeight: "700"
19379
19457
  },
19380
- // Modal
19381
- modalOverlay: {
19458
+ // Panel (replaces Modal — no backdrop, app stays interactive)
19459
+ panelWrapper: {
19460
+ position: "absolute",
19461
+ top: 0,
19462
+ left: 0,
19463
+ right: 0,
19464
+ bottom: 0,
19465
+ zIndex: 9998,
19466
+ justifyContent: "flex-end"
19467
+ },
19468
+ panelKeyboardAvoid: {
19382
19469
  flex: 1,
19383
- justifyContent: "flex-end",
19384
- backgroundColor: "rgba(0,0,0,0.5)"
19470
+ justifyContent: "flex-end"
19385
19471
  },
19386
- modalContainer: {
19472
+ panelContainer: {
19387
19473
  backgroundColor: colors.bg,
19388
19474
  borderTopLeftRadius: 20,
19389
19475
  borderTopRightRadius: 20,
19390
- maxHeight: "85%",
19391
- minHeight: "50%"
19476
+ maxHeight: "70%",
19477
+ minHeight: "40%",
19478
+ shadowColor: colors.onBright,
19479
+ shadowOffset: { width: 0, height: -4 },
19480
+ shadowOpacity: 0.25,
19481
+ shadowRadius: 12,
19482
+ elevation: 16
19483
+ },
19484
+ panelHandle: {
19485
+ alignItems: "center",
19486
+ paddingTop: 8,
19487
+ paddingBottom: 4
19488
+ },
19489
+ panelHandleBar: {
19490
+ width: 36,
19491
+ height: 4,
19492
+ borderRadius: 2,
19493
+ backgroundColor: colors.border
19392
19494
  },
19393
19495
  // Header
19394
19496
  header: {
@@ -19471,7 +19573,7 @@ function createStyles16() {
19471
19573
  },
19472
19574
  contentContainer: {
19473
19575
  padding: 16,
19474
- paddingBottom: 32
19576
+ paddingBottom: import_react_native22.Platform.OS === "ios" ? 50 : 28
19475
19577
  },
19476
19578
  // Loading
19477
19579
  loadingContainer: {
package/dist/index.mjs CHANGED
@@ -12760,55 +12760,66 @@ var BugBearClient = class {
12760
12760
  ...pendingResult.data || [],
12761
12761
  ...completedResult.data || []
12762
12762
  ];
12763
- const mapItem = (item) => ({
12764
- id: item.id,
12765
- status: item.status,
12766
- startedAt: item.started_at,
12767
- skipReason: item.skip_reason,
12768
- isVerification: item.is_verification || false,
12769
- originalReportId: item.original_report_id,
12770
- testCase: {
12771
- id: item.test_case.id,
12772
- title: item.test_case.title,
12773
- testKey: item.test_case.test_key,
12774
- description: item.test_case.description,
12775
- steps: item.test_case.steps,
12776
- expectedResult: item.test_case.expected_result,
12777
- priority: item.test_case.priority,
12778
- targetRoute: item.test_case.target_route,
12779
- track: item.test_case.track ? {
12780
- id: item.test_case.track.id,
12781
- name: item.test_case.track.name,
12782
- icon: item.test_case.track.icon,
12783
- color: item.test_case.track.color,
12784
- testTemplate: item.test_case.track.test_template,
12785
- rubricMode: item.test_case.track.rubric_mode || "pass_fail",
12786
- description: item.test_case.track.description
12787
- } : void 0,
12788
- group: item.test_case.group ? {
12789
- id: item.test_case.group.id,
12790
- name: item.test_case.group.name,
12791
- description: item.test_case.group.description,
12792
- sortOrder: item.test_case.group.sort_order
12793
- } : void 0,
12794
- role: item.test_case.role ? {
12795
- id: item.test_case.role.id,
12796
- name: item.test_case.role.name,
12797
- slug: item.test_case.role.slug,
12798
- color: item.test_case.role.color,
12799
- description: item.test_case.role.description,
12800
- loginHint: item.test_case.role.login_hint
12801
- } : void 0,
12802
- platforms: item.test_case.platforms || void 0
12803
- }
12804
- });
12805
- const mapped = allData.filter((item) => {
12806
- if (!item.test_case) {
12807
- console.warn("BugBear: Assignment returned without test_case", { id: item.id });
12808
- return false;
12809
- }
12810
- return true;
12811
- }).map(mapItem);
12763
+ const mapItem = (item) => {
12764
+ const tc = item.test_case;
12765
+ return {
12766
+ id: item.id,
12767
+ status: item.status,
12768
+ startedAt: item.started_at,
12769
+ skipReason: item.skip_reason,
12770
+ isVerification: item.is_verification || false,
12771
+ originalReportId: item.original_report_id,
12772
+ testCase: tc ? {
12773
+ id: tc.id,
12774
+ title: tc.title,
12775
+ testKey: tc.test_key,
12776
+ description: tc.description,
12777
+ steps: tc.steps,
12778
+ expectedResult: tc.expected_result,
12779
+ priority: tc.priority,
12780
+ targetRoute: tc.target_route,
12781
+ track: tc.track ? {
12782
+ id: tc.track.id,
12783
+ name: tc.track.name,
12784
+ icon: tc.track.icon,
12785
+ color: tc.track.color,
12786
+ testTemplate: tc.track.test_template,
12787
+ rubricMode: tc.track.rubric_mode || "pass_fail",
12788
+ description: tc.track.description
12789
+ } : void 0,
12790
+ group: tc.group ? {
12791
+ id: tc.group.id,
12792
+ name: tc.group.name,
12793
+ description: tc.group.description,
12794
+ sortOrder: tc.group.sort_order
12795
+ } : void 0,
12796
+ role: tc.role ? {
12797
+ id: tc.role.id,
12798
+ name: tc.role.name,
12799
+ slug: tc.role.slug,
12800
+ color: tc.role.color,
12801
+ description: tc.role.description,
12802
+ loginHint: tc.role.login_hint
12803
+ } : void 0,
12804
+ platforms: tc.platforms || void 0
12805
+ } : {
12806
+ // Standalone verification assignment (bug reported without a test case)
12807
+ id: item.original_report_id || item.id,
12808
+ title: item.notes || "Bug Verification",
12809
+ testKey: "VERIFY",
12810
+ description: "Verify that the reported bug has been fixed",
12811
+ steps: [],
12812
+ expectedResult: "The bug should no longer be reproducible",
12813
+ priority: "P1",
12814
+ targetRoute: void 0,
12815
+ track: void 0,
12816
+ group: void 0,
12817
+ role: void 0,
12818
+ platforms: void 0
12819
+ }
12820
+ };
12821
+ };
12822
+ const mapped = allData.map(mapItem);
12812
12823
  mapped.sort((a, b) => {
12813
12824
  if (a.isVerification && !b.isVerification) return -1;
12814
12825
  if (!a.isVerification && b.isVerification) return 1;
@@ -12902,7 +12913,7 @@ var BugBearClient = class {
12902
12913
  async updateAssignmentStatus(assignmentId, status, options) {
12903
12914
  try {
12904
12915
  await this.ensureReady();
12905
- const { data: currentAssignment, error: fetchError } = await this.supabase.from("test_assignments").select("status, started_at").eq("id", assignmentId).single();
12916
+ const { data: currentAssignment, error: fetchError } = await this.supabase.from("test_assignments").select("status, started_at, tester_id, project_id").eq("id", assignmentId).single();
12906
12917
  if (fetchError || !currentAssignment) {
12907
12918
  console.error("BugBear: Assignment not found", {
12908
12919
  message: fetchError?.message,
@@ -12923,6 +12934,19 @@ var BugBearClient = class {
12923
12934
  const completedAt = /* @__PURE__ */ new Date();
12924
12935
  durationSeconds = Math.round((completedAt.getTime() - startedAt.getTime()) / 1e3);
12925
12936
  updateData.duration_seconds = durationSeconds;
12937
+ if (currentAssignment.tester_id && currentAssignment.project_id) {
12938
+ try {
12939
+ const { data: activeTime } = await this.supabase.rpc("compute_assignment_active_time", {
12940
+ p_tester_id: currentAssignment.tester_id,
12941
+ p_project_id: currentAssignment.project_id,
12942
+ p_started_at: currentAssignment.started_at,
12943
+ p_completed_at: updateData.completed_at
12944
+ });
12945
+ updateData.active_seconds = typeof activeTime === "number" ? activeTime : Math.min(durationSeconds, 1800);
12946
+ } catch {
12947
+ updateData.active_seconds = Math.min(durationSeconds, 1800);
12948
+ }
12949
+ }
12926
12950
  }
12927
12951
  }
12928
12952
  if (options?.notes) {
@@ -13001,6 +13025,7 @@ var BugBearClient = class {
13001
13025
  started_at: (/* @__PURE__ */ new Date()).toISOString(),
13002
13026
  completed_at: null,
13003
13027
  duration_seconds: null,
13028
+ active_seconds: null,
13004
13029
  skip_reason: null
13005
13030
  }).eq("id", assignmentId).eq("status", current.status);
13006
13031
  if (error) {
@@ -13308,7 +13333,11 @@ var BugBearClient = class {
13308
13333
  verifiedByName: row.verified_by_name || void 0,
13309
13334
  verifiedAt: row.verified_at || void 0,
13310
13335
  originalBugId: row.original_bug_id || void 0,
13311
- originalBugTitle: row.original_bug_title || void 0
13336
+ originalBugTitle: row.original_bug_title || void 0,
13337
+ resolutionNotes: row.resolution_notes || void 0,
13338
+ fixCommitSha: row.code_context?.fix?.commit_sha || void 0,
13339
+ fixCommitMessage: row.code_context?.fix?.commit_message || void 0,
13340
+ fixFilesChanged: row.code_context?.fix?.files_changed || void 0
13312
13341
  }));
13313
13342
  } catch (err) {
13314
13343
  console.error("BugBear: Error fetching issues", err);
@@ -15133,13 +15162,12 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
15133
15162
  }
15134
15163
 
15135
15164
  // src/BugBearButton.tsx
15136
- import React21, { useState as useState18, useRef as useRef6, useMemo as useMemo16 } from "react";
15165
+ import React21, { useState as useState18, useRef as useRef6, useEffect as useEffect12, useMemo as useMemo16 } from "react";
15137
15166
  import {
15138
15167
  View as View21,
15139
15168
  Text as Text19,
15140
15169
  Image as Image4,
15141
15170
  TouchableOpacity as TouchableOpacity18,
15142
- Modal as Modal3,
15143
15171
  ScrollView as ScrollView4,
15144
15172
  StyleSheet as StyleSheet21,
15145
15173
  Dimensions as Dimensions2,
@@ -15148,7 +15176,8 @@ import {
15148
15176
  PanResponder,
15149
15177
  Animated as Animated2,
15150
15178
  ActivityIndicator as ActivityIndicator3,
15151
- Keyboard as Keyboard4
15179
+ Keyboard as Keyboard4,
15180
+ BackHandler
15152
15181
  } from "react-native";
15153
15182
 
15154
15183
  // src/widget/logo.ts
@@ -17905,7 +17934,10 @@ function IssueListScreen({ nav, category }) {
17905
17934
  styles5.tab,
17906
17935
  { borderBottomColor: isActive ? catConfig.accent : "transparent" }
17907
17936
  ],
17908
- onPress: () => setActiveCategory(cat),
17937
+ onPress: () => {
17938
+ setActiveCategory(cat);
17939
+ nav.replace({ name: "ISSUE_LIST", category: cat });
17940
+ },
17909
17941
  activeOpacity: 0.7
17910
17942
  },
17911
17943
  /* @__PURE__ */ React16.createElement(Text14, { style: [
@@ -18216,7 +18248,14 @@ function IssueDetailScreen({ nav, issue }) {
18216
18248
  activeOpacity: 0.7
18217
18249
  },
18218
18250
  /* @__PURE__ */ React17.createElement(Text15, { style: styles5.reopenCancelText }, "Cancel")
18219
- ))), issue.screenshotUrls && issue.screenshotUrls.length > 0 && /* @__PURE__ */ React17.createElement(View17, { style: styles5.screenshotSection }, /* @__PURE__ */ React17.createElement(Text15, { style: styles5.screenshotLabel }, "Screenshots (", issue.screenshotUrls.length, ")"), /* @__PURE__ */ React17.createElement(View17, { style: styles5.screenshotRow }, issue.screenshotUrls.map((url, i) => /* @__PURE__ */ React17.createElement(TouchableOpacity14, { key: i, onPress: () => Linking4.openURL(url), activeOpacity: 0.7 }, /* @__PURE__ */ React17.createElement(Image3, { source: { uri: url }, style: styles5.screenshotThumb }))))), /* @__PURE__ */ React17.createElement(View17, { style: styles5.metaSection }, issue.reporterName && /* @__PURE__ */ React17.createElement(Text15, { style: styles5.metaText }, "Reported by ", issue.reporterName), /* @__PURE__ */ React17.createElement(Text15, { style: styles5.metaTextSmall }, "Created ", formatRelativeTime(issue.createdAt), " ", "\xB7", " Updated ", formatRelativeTime(issue.updatedAt))), dashboardUrl && /* @__PURE__ */ React17.createElement(
18251
+ ))), issue.status === "ready_to_test" && (issue.fixCommitSha || issue.resolutionNotes) && /* @__PURE__ */ React17.createElement(View17, { style: {
18252
+ backgroundColor: "rgba(59, 130, 246, 0.1)",
18253
+ borderWidth: 1,
18254
+ borderColor: "rgba(96, 165, 250, 0.2)",
18255
+ borderRadius: 8,
18256
+ padding: 12,
18257
+ marginBottom: 12
18258
+ } }, /* @__PURE__ */ React17.createElement(View17, { style: { flexDirection: "row", alignItems: "center", gap: 6, marginBottom: 8 } }, /* @__PURE__ */ React17.createElement(Text15, { style: { fontSize: 13 } }, "\u{1F527}"), /* @__PURE__ */ React17.createElement(Text15, { style: { fontSize: 12, fontWeight: "600", color: "#60a5fa" } }, "Fix Details")), issue.resolutionNotes ? /* @__PURE__ */ React17.createElement(Text15, { style: { fontSize: 12, color: colors.textSecondary, marginBottom: 6, lineHeight: 17 } }, issue.resolutionNotes) : null, issue.fixCommitSha ? /* @__PURE__ */ React17.createElement(Text15, { style: { fontSize: 11, color: colors.textDim, fontFamily: "monospace" } }, "Commit: ", issue.fixCommitSha.slice(0, 7), issue.fixCommitMessage && ` \u2014 ${issue.fixCommitMessage}`) : null, issue.fixFilesChanged && issue.fixFilesChanged.length > 0 ? /* @__PURE__ */ React17.createElement(Text15, { style: { fontSize: 11, color: colors.textDim, marginTop: 4 } }, issue.fixFilesChanged.length, " file", issue.fixFilesChanged.length > 1 ? "s" : "", " changed") : null), issue.screenshotUrls && issue.screenshotUrls.length > 0 && /* @__PURE__ */ React17.createElement(View17, { style: styles5.screenshotSection }, /* @__PURE__ */ React17.createElement(Text15, { style: styles5.screenshotLabel }, "Screenshots (", issue.screenshotUrls.length, ")"), /* @__PURE__ */ React17.createElement(View17, { style: styles5.screenshotRow }, issue.screenshotUrls.map((url, i) => /* @__PURE__ */ React17.createElement(TouchableOpacity14, { key: i, onPress: () => Linking4.openURL(url), activeOpacity: 0.7 }, /* @__PURE__ */ React17.createElement(Image3, { source: { uri: url }, style: styles5.screenshotThumb }))))), /* @__PURE__ */ React17.createElement(View17, { style: styles5.metaSection }, issue.reporterName && /* @__PURE__ */ React17.createElement(Text15, { style: styles5.metaText }, "Reported by ", issue.reporterName), /* @__PURE__ */ React17.createElement(Text15, { style: styles5.metaTextSmall }, "Created ", formatRelativeTime(issue.createdAt), " ", "\xB7", " Updated ", formatRelativeTime(issue.updatedAt))), dashboardUrl && /* @__PURE__ */ React17.createElement(
18220
18259
  TouchableOpacity14,
18221
18260
  {
18222
18261
  style: styles5.dashboardLink,
@@ -19097,14 +19136,31 @@ function BugBearButton({
19097
19136
  }) {
19098
19137
  const { shouldShowWidget, testerInfo, isLoading, unreadCount, assignments, widgetMode, widgetColorScheme } = useBugBear();
19099
19138
  const { currentScreen, canGoBack, push, pop, replace, reset } = useNavigation();
19100
- const [modalVisible, setModalVisible] = useState18(false);
19139
+ const [panelVisible, setPanelVisible] = useState18(false);
19140
+ const panelAnim = useRef6(new Animated2.Value(0)).current;
19101
19141
  const styles5 = useMemo16(() => createStyles16(), [widgetColorScheme]);
19102
19142
  const screenCaptureRef = useRef6(null);
19103
- const openModal = () => {
19143
+ const openPanel = () => {
19104
19144
  captureAppScreen().then((uri) => {
19105
19145
  screenCaptureRef.current = uri;
19106
19146
  });
19107
- setModalVisible(true);
19147
+ setPanelVisible(true);
19148
+ Animated2.spring(panelAnim, {
19149
+ toValue: 1,
19150
+ useNativeDriver: true,
19151
+ friction: 8,
19152
+ tension: 65
19153
+ }).start();
19154
+ };
19155
+ const closePanel = () => {
19156
+ Keyboard4.dismiss();
19157
+ Animated2.timing(panelAnim, {
19158
+ toValue: 0,
19159
+ duration: 250,
19160
+ useNativeDriver: true
19161
+ }).start(() => {
19162
+ setPanelVisible(false);
19163
+ });
19108
19164
  };
19109
19165
  const getInitialPosition = () => {
19110
19166
  const buttonSize = 56;
@@ -19155,7 +19211,7 @@ function BugBearButton({
19155
19211
  tension: 40
19156
19212
  }).start();
19157
19213
  if (!isDragging.current && Math.abs(gs.dx) < 5 && Math.abs(gs.dy) < 5) {
19158
- openModal();
19214
+ openPanel();
19159
19215
  }
19160
19216
  isDragging.current = false;
19161
19217
  }
@@ -19163,6 +19219,19 @@ function BugBearButton({
19163
19219
  ).current;
19164
19220
  const pendingTests = widgetMode === "qa" ? assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length : 0;
19165
19221
  const badgeCount = pendingTests + unreadCount;
19222
+ useEffect12(() => {
19223
+ if (!panelVisible || Platform5.OS !== "android") return;
19224
+ const handler = BackHandler.addEventListener("hardwareBackPress", () => {
19225
+ if (canGoBack) {
19226
+ Keyboard4.dismiss();
19227
+ pop();
19228
+ } else {
19229
+ closePanel();
19230
+ }
19231
+ return true;
19232
+ });
19233
+ return () => handler.remove();
19234
+ }, [panelVisible, canGoBack]);
19166
19235
  if (!shouldShowWidget) return null;
19167
19236
  const getHeaderTitle = () => {
19168
19237
  switch (currentScreen.name) {
@@ -19201,8 +19270,7 @@ function BugBearButton({
19201
19270
  }
19202
19271
  };
19203
19272
  const handleClose = () => {
19204
- Keyboard4.dismiss();
19205
- setModalVisible(false);
19273
+ closePanel();
19206
19274
  };
19207
19275
  const nav = {
19208
19276
  push: (screen) => {
@@ -19274,7 +19342,7 @@ function BugBearButton({
19274
19342
  return /* @__PURE__ */ React21.createElement(HomeScreen, { nav });
19275
19343
  }
19276
19344
  };
19277
- return /* @__PURE__ */ React21.createElement(React21.Fragment, null, /* @__PURE__ */ React21.createElement(
19345
+ return /* @__PURE__ */ React21.createElement(React21.Fragment, null, !panelVisible && /* @__PURE__ */ React21.createElement(
19278
19346
  Animated2.View,
19279
19347
  {
19280
19348
  style: [styles5.fabContainer, { transform: pan.getTranslateTransform() }, buttonStyle],
@@ -19284,27 +19352,37 @@ function BugBearButton({
19284
19352
  TouchableOpacity18,
19285
19353
  {
19286
19354
  style: styles5.fab,
19287
- onPress: openModal,
19355
+ onPress: openPanel,
19288
19356
  activeOpacity: draggable ? 1 : 0.7
19289
19357
  },
19290
19358
  /* @__PURE__ */ React21.createElement(Image4, { source: { uri: BUGBEAR_LOGO_BASE64 }, style: styles5.fabIcon }),
19291
19359
  badgeCount > 0 && /* @__PURE__ */ React21.createElement(View21, { style: styles5.badge }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.badgeText }, badgeCount > 9 ? "9+" : badgeCount))
19292
19360
  )
19293
- ), /* @__PURE__ */ React21.createElement(
19294
- Modal3,
19361
+ ), panelVisible && /* @__PURE__ */ React21.createElement(View21, { style: styles5.panelWrapper, pointerEvents: "box-none" }, /* @__PURE__ */ React21.createElement(
19362
+ KeyboardAvoidingView,
19295
19363
  {
19296
- visible: modalVisible,
19297
- animationType: "slide",
19298
- transparent: true,
19299
- onRequestClose: handleClose
19364
+ behavior: Platform5.OS === "ios" ? "padding" : "height",
19365
+ style: styles5.panelKeyboardAvoid,
19366
+ pointerEvents: "box-none"
19300
19367
  },
19301
19368
  /* @__PURE__ */ React21.createElement(
19302
- KeyboardAvoidingView,
19369
+ Animated2.View,
19303
19370
  {
19304
- behavior: Platform5.OS === "ios" ? "padding" : "height",
19305
- style: styles5.modalOverlay
19371
+ style: [
19372
+ styles5.panelContainer,
19373
+ {
19374
+ transform: [{
19375
+ translateY: panelAnim.interpolate({
19376
+ inputRange: [0, 1],
19377
+ outputRange: [screenHeight, 0]
19378
+ })
19379
+ }]
19380
+ }
19381
+ ]
19306
19382
  },
19307
- /* @__PURE__ */ React21.createElement(View21, { style: styles5.modalContainer }, /* @__PURE__ */ React21.createElement(View21, { style: styles5.header }, /* @__PURE__ */ React21.createElement(View21, { style: styles5.headerLeft }, canGoBack ? /* @__PURE__ */ React21.createElement(TouchableOpacity18, { onPress: () => nav.pop(), style: styles5.backButton }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.backText }, "\u2190 Back")) : /* @__PURE__ */ React21.createElement(View21, { style: styles5.headerTitleRow }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ React21.createElement(TouchableOpacity18, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ React21.createElement(Text19, { style: styles5.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ React21.createElement(View21, { style: styles5.headerActions }, currentScreen.name !== "HOME" && /* @__PURE__ */ React21.createElement(TouchableOpacity18, { onPress: () => nav.reset(), style: styles5.homeButton }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.homeIcon }, "\u2302")), /* @__PURE__ */ React21.createElement(TouchableOpacity18, { onPress: handleClose, style: styles5.closeButton }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.closeText }, "\u2715")))), /* @__PURE__ */ React21.createElement(
19383
+ /* @__PURE__ */ React21.createElement(View21, { style: styles5.panelHandle }, /* @__PURE__ */ React21.createElement(View21, { style: styles5.panelHandleBar })),
19384
+ /* @__PURE__ */ React21.createElement(View21, { style: styles5.header }, /* @__PURE__ */ React21.createElement(View21, { style: styles5.headerLeft }, canGoBack ? /* @__PURE__ */ React21.createElement(TouchableOpacity18, { onPress: () => nav.pop(), style: styles5.backButton }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.backText }, "\u2190 Back")) : /* @__PURE__ */ React21.createElement(View21, { style: styles5.headerTitleRow }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ React21.createElement(TouchableOpacity18, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ React21.createElement(Text19, { style: styles5.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ React21.createElement(View21, { style: styles5.headerActions }, currentScreen.name !== "HOME" && /* @__PURE__ */ React21.createElement(TouchableOpacity18, { onPress: () => nav.reset(), style: styles5.homeButton }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.homeIcon }, "\u2302")), /* @__PURE__ */ React21.createElement(TouchableOpacity18, { onPress: handleClose, style: styles5.closeButton }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.closeText }, "\u2715")))),
19385
+ /* @__PURE__ */ React21.createElement(
19308
19386
  ScrollView4,
19309
19387
  {
19310
19388
  style: styles5.content,
@@ -19313,9 +19391,9 @@ function BugBearButton({
19313
19391
  showsVerticalScrollIndicator: false
19314
19392
  },
19315
19393
  isLoading ? /* @__PURE__ */ React21.createElement(View21, { style: styles5.loadingContainer }, /* @__PURE__ */ React21.createElement(ActivityIndicator3, { size: "large", color: colors.blue }), /* @__PURE__ */ React21.createElement(Text19, { style: styles5.loadingText }, "Loading...")) : renderScreen()
19316
- ))
19394
+ )
19317
19395
  )
19318
- ));
19396
+ )));
19319
19397
  }
19320
19398
  function createStyles16() {
19321
19399
  return StyleSheet21.create({
@@ -19359,18 +19437,42 @@ function createStyles16() {
19359
19437
  fontSize: 11,
19360
19438
  fontWeight: "700"
19361
19439
  },
19362
- // Modal
19363
- modalOverlay: {
19440
+ // Panel (replaces Modal — no backdrop, app stays interactive)
19441
+ panelWrapper: {
19442
+ position: "absolute",
19443
+ top: 0,
19444
+ left: 0,
19445
+ right: 0,
19446
+ bottom: 0,
19447
+ zIndex: 9998,
19448
+ justifyContent: "flex-end"
19449
+ },
19450
+ panelKeyboardAvoid: {
19364
19451
  flex: 1,
19365
- justifyContent: "flex-end",
19366
- backgroundColor: "rgba(0,0,0,0.5)"
19452
+ justifyContent: "flex-end"
19367
19453
  },
19368
- modalContainer: {
19454
+ panelContainer: {
19369
19455
  backgroundColor: colors.bg,
19370
19456
  borderTopLeftRadius: 20,
19371
19457
  borderTopRightRadius: 20,
19372
- maxHeight: "85%",
19373
- minHeight: "50%"
19458
+ maxHeight: "70%",
19459
+ minHeight: "40%",
19460
+ shadowColor: colors.onBright,
19461
+ shadowOffset: { width: 0, height: -4 },
19462
+ shadowOpacity: 0.25,
19463
+ shadowRadius: 12,
19464
+ elevation: 16
19465
+ },
19466
+ panelHandle: {
19467
+ alignItems: "center",
19468
+ paddingTop: 8,
19469
+ paddingBottom: 4
19470
+ },
19471
+ panelHandleBar: {
19472
+ width: 36,
19473
+ height: 4,
19474
+ borderRadius: 2,
19475
+ backgroundColor: colors.border
19374
19476
  },
19375
19477
  // Header
19376
19478
  header: {
@@ -19453,7 +19555,7 @@ function createStyles16() {
19453
19555
  },
19454
19556
  contentContainer: {
19455
19557
  padding: 16,
19456
- paddingBottom: 32
19558
+ paddingBottom: Platform5.OS === "ios" ? 50 : 28
19457
19559
  },
19458
19560
  // Loading
19459
19561
  loadingContainer: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbearai/react-native",
3
- "version": "0.8.4",
3
+ "version": "0.9.1",
4
4
  "description": "BugBear React Native components for mobile apps",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",