@bbearai/react-native 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -11393,7 +11393,7 @@ function shouldShowDeprecationWarning() {
11393
11393
  }
11394
11394
  if (shouldShowDeprecationWarning()) console.warn("\u26A0\uFE0F Node.js 18 and below are deprecated and will no longer be supported in future versions of @supabase/supabase-js. Please upgrade to Node.js 20 or later. For more information, visit: https://github.com/orgs/supabase/discussions/37217");
11395
11395
 
11396
- // ../core/dist/index.mjs
11396
+ // node_modules/@bbearai/core/dist/index.mjs
11397
11397
  var MAX_CONSOLE_LOGS = 50;
11398
11398
  var MAX_NETWORK_REQUESTS = 20;
11399
11399
  var MAX_NAVIGATION_HISTORY = 20;
@@ -14021,10 +14021,19 @@ var import_react_native5 = require("react-native");
14021
14021
  function TestListScreen({ nav }) {
14022
14022
  const { assignments, currentAssignment, refreshAssignments } = useBugBear();
14023
14023
  const [filter, setFilter] = (0, import_react5.useState)("all");
14024
+ const [roleFilter, setRoleFilter] = (0, import_react5.useState)(null);
14024
14025
  const [collapsedFolders, setCollapsedFolders] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
14025
14026
  (0, import_react5.useEffect)(() => {
14026
14027
  refreshAssignments();
14027
14028
  }, []);
14029
+ const availableRoles = (0, import_react5.useMemo)(() => {
14030
+ const roleMap = /* @__PURE__ */ new Map();
14031
+ for (const a of assignments) {
14032
+ if (a.testCase.role) roleMap.set(a.testCase.role.id, a.testCase.role);
14033
+ }
14034
+ return Array.from(roleMap.values());
14035
+ }, [assignments]);
14036
+ const selectedRole = availableRoles.find((r) => r.id === roleFilter);
14028
14037
  const groupedAssignments = (0, import_react5.useMemo)(() => {
14029
14038
  const groups = /* @__PURE__ */ new Map();
14030
14039
  for (const assignment of assignments) {
@@ -14068,16 +14077,39 @@ function TestListScreen({ nav }) {
14068
14077
  });
14069
14078
  }, []);
14070
14079
  const filterAssignment = (a) => {
14080
+ if (roleFilter && a.testCase.role?.id !== roleFilter) return false;
14071
14081
  if (filter === "pending") return a.status === "pending" || a.status === "in_progress";
14072
14082
  if (filter === "completed") return a.status === "passed" || a.status === "failed";
14073
14083
  return true;
14074
14084
  };
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) => {
14085
+ 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(
14086
+ import_react_native5.TouchableOpacity,
14087
+ {
14088
+ style: [styles3.roleBtn, !roleFilter && styles3.roleBtnActive],
14089
+ onPress: () => setRoleFilter(null)
14090
+ },
14091
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles3.roleBtnText, !roleFilter && styles3.roleBtnTextActive] }, "All Roles")
14092
+ ), availableRoles.map((role) => {
14093
+ const isActive = roleFilter === role.id;
14094
+ return /* @__PURE__ */ import_react5.default.createElement(
14095
+ import_react_native5.TouchableOpacity,
14096
+ {
14097
+ key: role.id,
14098
+ style: [
14099
+ styles3.roleBtn,
14100
+ isActive && { backgroundColor: role.color + "20", borderColor: role.color + "60", borderWidth: 1 }
14101
+ ],
14102
+ onPress: () => setRoleFilter(isActive ? null : role.id)
14103
+ },
14104
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [styles3.roleDot, { backgroundColor: role.color }] }),
14105
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles3.roleBtnText, isActive && { color: role.color, fontWeight: "600" }] }, role.name)
14106
+ );
14107
+ })), 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
14108
  const folderId = folder.group?.id || "ungrouped";
14077
14109
  const isCollapsed = collapsedFolders.has(folderId);
14078
14110
  const filtered = folder.assignments.filter(filterAssignment);
14079
14111
  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) => {
14112
+ 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
14113
  const badge = getStatusBadge(assignment.status);
14082
14114
  const isCurrent = currentAssignment?.id === assignment.id;
14083
14115
  return /* @__PURE__ */ import_react5.default.createElement(
@@ -14088,17 +14120,28 @@ function TestListScreen({ nav }) {
14088
14120
  onPress: () => nav.push({ name: "TEST_DETAIL", testId: assignment.id })
14089
14121
  },
14090
14122
  /* @__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)))
14123
+ /* @__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
14124
  );
14093
14125
  }));
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")));
14126
+ }), /* @__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
14127
  }
14096
14128
  var styles3 = import_react_native5.StyleSheet.create({
14097
- filterBar: { flexDirection: "row", gap: 8, marginBottom: 16 },
14129
+ filterBar: { flexDirection: "row", gap: 8, marginBottom: 8 },
14098
14130
  filterBtn: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 8, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
14099
14131
  filterBtnActive: { backgroundColor: colors.blue, borderColor: colors.blue },
14100
14132
  filterBtnText: { fontSize: 12, color: colors.textSecondary },
14101
14133
  filterBtnTextActive: { color: "#fff", fontWeight: "600" },
14134
+ roleSection: { marginBottom: 12 },
14135
+ roleBar: { flexDirection: "row", marginBottom: 6 },
14136
+ roleBtn: { flexDirection: "row", alignItems: "center", gap: 5, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
14137
+ roleBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
14138
+ roleBtnText: { fontSize: 11, color: colors.textMuted },
14139
+ roleBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
14140
+ roleDot: { width: 6, height: 6, borderRadius: 3 },
14141
+ loginHint: { borderRadius: 6, padding: 8, borderWidth: 1 },
14142
+ loginHintText: { fontSize: 11, color: colors.textSecondary },
14143
+ roleBadgeRow: { flexDirection: "row", alignItems: "center", gap: 3 },
14144
+ roleBadgeDot: { width: 5, height: 5, borderRadius: 3 },
14102
14145
  folder: { marginBottom: 12 },
14103
14146
  folderHeader: { flexDirection: "row", alignItems: "center", gap: 8, paddingVertical: 8, paddingHorizontal: 4 },
14104
14147
  folderToggle: { fontSize: 10, color: colors.textMuted, width: 14 },
@@ -14111,7 +14154,7 @@ var styles3 = import_react_native5.StyleSheet.create({
14111
14154
  testBadge: { fontSize: 16, marginRight: 10, width: 20 },
14112
14155
  testInfo: { flex: 1 },
14113
14156
  testTitle: { fontSize: 14, color: colors.textPrimary, marginBottom: 2 },
14114
- testMetaRow: { flexDirection: "row", alignItems: "center", gap: 6 },
14157
+ testMetaRow: { flexDirection: "row", alignItems: "center", gap: 6, flexWrap: "wrap" },
14115
14158
  retestTag: { backgroundColor: "#422006", borderWidth: 1, borderColor: "#854d0e", borderRadius: 4, paddingHorizontal: 5, paddingVertical: 1 },
14116
14159
  retestTagText: { fontSize: 10, fontWeight: "600", color: "#fbbf24" },
14117
14160
  testMeta: { fontSize: 11, color: colors.textDim },
package/dist/index.mjs CHANGED
@@ -11360,7 +11360,7 @@ function shouldShowDeprecationWarning() {
11360
11360
  }
11361
11361
  if (shouldShowDeprecationWarning()) console.warn("\u26A0\uFE0F Node.js 18 and below are deprecated and will no longer be supported in future versions of @supabase/supabase-js. Please upgrade to Node.js 20 or later. For more information, visit: https://github.com/orgs/supabase/discussions/37217");
11362
11362
 
11363
- // ../core/dist/index.mjs
11363
+ // node_modules/@bbearai/core/dist/index.mjs
11364
11364
  var MAX_CONSOLE_LOGS = 50;
11365
11365
  var MAX_NETWORK_REQUESTS = 20;
11366
11366
  var MAX_NAVIGATION_HISTORY = 20;
@@ -13220,7 +13220,7 @@ import {
13220
13220
  Image as Image3,
13221
13221
  TouchableOpacity as TouchableOpacity12,
13222
13222
  Modal as Modal2,
13223
- ScrollView as ScrollView2,
13223
+ ScrollView as ScrollView3,
13224
13224
  StyleSheet as StyleSheet14,
13225
13225
  Dimensions as Dimensions2,
13226
13226
  KeyboardAvoidingView,
@@ -13999,14 +13999,23 @@ var styles2 = StyleSheet3.create({
13999
13999
 
14000
14000
  // src/widget/screens/TestListScreen.tsx
14001
14001
  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";
14002
+ import { View as View3, Text as Text3, TouchableOpacity as TouchableOpacity3, StyleSheet as StyleSheet4, ScrollView } from "react-native";
14003
14003
  function TestListScreen({ nav }) {
14004
14004
  const { assignments, currentAssignment, refreshAssignments } = useBugBear();
14005
14005
  const [filter, setFilter] = useState3("all");
14006
+ const [roleFilter, setRoleFilter] = useState3(null);
14006
14007
  const [collapsedFolders, setCollapsedFolders] = useState3(/* @__PURE__ */ new Set());
14007
14008
  useEffect4(() => {
14008
14009
  refreshAssignments();
14009
14010
  }, []);
14011
+ const availableRoles = useMemo2(() => {
14012
+ const roleMap = /* @__PURE__ */ new Map();
14013
+ for (const a of assignments) {
14014
+ if (a.testCase.role) roleMap.set(a.testCase.role.id, a.testCase.role);
14015
+ }
14016
+ return Array.from(roleMap.values());
14017
+ }, [assignments]);
14018
+ const selectedRole = availableRoles.find((r) => r.id === roleFilter);
14010
14019
  const groupedAssignments = useMemo2(() => {
14011
14020
  const groups = /* @__PURE__ */ new Map();
14012
14021
  for (const assignment of assignments) {
@@ -14050,16 +14059,39 @@ function TestListScreen({ nav }) {
14050
14059
  });
14051
14060
  }, []);
14052
14061
  const filterAssignment = (a) => {
14062
+ if (roleFilter && a.testCase.role?.id !== roleFilter) return false;
14053
14063
  if (filter === "pending") return a.status === "pending" || a.status === "in_progress";
14054
14064
  if (filter === "completed") return a.status === "passed" || a.status === "failed";
14055
14065
  return true;
14056
14066
  };
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) => {
14067
+ 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(
14068
+ TouchableOpacity3,
14069
+ {
14070
+ style: [styles3.roleBtn, !roleFilter && styles3.roleBtnActive],
14071
+ onPress: () => setRoleFilter(null)
14072
+ },
14073
+ /* @__PURE__ */ React4.createElement(Text3, { style: [styles3.roleBtnText, !roleFilter && styles3.roleBtnTextActive] }, "All Roles")
14074
+ ), availableRoles.map((role) => {
14075
+ const isActive = roleFilter === role.id;
14076
+ return /* @__PURE__ */ React4.createElement(
14077
+ TouchableOpacity3,
14078
+ {
14079
+ key: role.id,
14080
+ style: [
14081
+ styles3.roleBtn,
14082
+ isActive && { backgroundColor: role.color + "20", borderColor: role.color + "60", borderWidth: 1 }
14083
+ ],
14084
+ onPress: () => setRoleFilter(isActive ? null : role.id)
14085
+ },
14086
+ /* @__PURE__ */ React4.createElement(View3, { style: [styles3.roleDot, { backgroundColor: role.color }] }),
14087
+ /* @__PURE__ */ React4.createElement(Text3, { style: [styles3.roleBtnText, isActive && { color: role.color, fontWeight: "600" }] }, role.name)
14088
+ );
14089
+ })), 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
14090
  const folderId = folder.group?.id || "ungrouped";
14059
14091
  const isCollapsed = collapsedFolders.has(folderId);
14060
14092
  const filtered = folder.assignments.filter(filterAssignment);
14061
14093
  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) => {
14094
+ 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
14095
  const badge = getStatusBadge(assignment.status);
14064
14096
  const isCurrent = currentAssignment?.id === assignment.id;
14065
14097
  return /* @__PURE__ */ React4.createElement(
@@ -14070,17 +14102,28 @@ function TestListScreen({ nav }) {
14070
14102
  onPress: () => nav.push({ name: "TEST_DETAIL", testId: assignment.id })
14071
14103
  },
14072
14104
  /* @__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)))
14105
+ /* @__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
14106
  );
14075
14107
  }));
14076
- }), /* @__PURE__ */ React4.createElement(TouchableOpacity3, { style: styles3.refreshBtn, onPress: refreshAssignments }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.refreshText }, "\u21BB Refresh")));
14108
+ }), /* @__PURE__ */ React4.createElement(TouchableOpacity3, { style: styles3.refreshBtn, onPress: refreshAssignments }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.refreshText }, "\u21BB", " Refresh")));
14077
14109
  }
14078
14110
  var styles3 = StyleSheet4.create({
14079
- filterBar: { flexDirection: "row", gap: 8, marginBottom: 16 },
14111
+ filterBar: { flexDirection: "row", gap: 8, marginBottom: 8 },
14080
14112
  filterBtn: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 8, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
14081
14113
  filterBtnActive: { backgroundColor: colors.blue, borderColor: colors.blue },
14082
14114
  filterBtnText: { fontSize: 12, color: colors.textSecondary },
14083
14115
  filterBtnTextActive: { color: "#fff", fontWeight: "600" },
14116
+ roleSection: { marginBottom: 12 },
14117
+ roleBar: { flexDirection: "row", marginBottom: 6 },
14118
+ roleBtn: { flexDirection: "row", alignItems: "center", gap: 5, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
14119
+ roleBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
14120
+ roleBtnText: { fontSize: 11, color: colors.textMuted },
14121
+ roleBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
14122
+ roleDot: { width: 6, height: 6, borderRadius: 3 },
14123
+ loginHint: { borderRadius: 6, padding: 8, borderWidth: 1 },
14124
+ loginHintText: { fontSize: 11, color: colors.textSecondary },
14125
+ roleBadgeRow: { flexDirection: "row", alignItems: "center", gap: 3 },
14126
+ roleBadgeDot: { width: 5, height: 5, borderRadius: 3 },
14084
14127
  folder: { marginBottom: 12 },
14085
14128
  folderHeader: { flexDirection: "row", alignItems: "center", gap: 8, paddingVertical: 8, paddingHorizontal: 4 },
14086
14129
  folderToggle: { fontSize: 10, color: colors.textMuted, width: 14 },
@@ -14093,7 +14136,7 @@ var styles3 = StyleSheet4.create({
14093
14136
  testBadge: { fontSize: 16, marginRight: 10, width: 20 },
14094
14137
  testInfo: { flex: 1 },
14095
14138
  testTitle: { fontSize: 14, color: colors.textPrimary, marginBottom: 2 },
14096
- testMetaRow: { flexDirection: "row", alignItems: "center", gap: 6 },
14139
+ testMetaRow: { flexDirection: "row", alignItems: "center", gap: 6, flexWrap: "wrap" },
14097
14140
  retestTag: { backgroundColor: "#422006", borderWidth: 1, borderColor: "#854d0e", borderRadius: 4, paddingHorizontal: 5, paddingVertical: 1 },
14098
14141
  retestTagText: { fontSize: 10, fontWeight: "600", color: "#fbbf24" },
14099
14142
  testMeta: { fontSize: 11, color: colors.textDim },
@@ -14188,10 +14231,10 @@ import { View as View5, Text as Text5, TouchableOpacity as TouchableOpacity5, St
14188
14231
 
14189
14232
  // src/widget/ImagePreviewStrip.tsx
14190
14233
  import React5 from "react";
14191
- import { View as View4, Text as Text4, TouchableOpacity as TouchableOpacity4, ScrollView, Image, ActivityIndicator, StyleSheet as StyleSheet5 } from "react-native";
14234
+ import { View as View4, Text as Text4, TouchableOpacity as TouchableOpacity4, ScrollView as ScrollView2, Image, ActivityIndicator, StyleSheet as StyleSheet5 } from "react-native";
14192
14235
  function ImagePreviewStrip({ images, onRemove }) {
14193
14236
  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")))));
14237
+ 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
14238
  }
14196
14239
  var styles4 = StyleSheet5.create({
14197
14240
  strip: {
@@ -15157,7 +15200,7 @@ function BugBearButton({
15157
15200
  style: styles13.modalOverlay
15158
15201
  },
15159
15202
  /* @__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,
15203
+ ScrollView3,
15161
15204
  {
15162
15205
  style: styles13.content,
15163
15206
  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.0",
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",