react-native-inapp-inspector 2.3.18 → 2.3.19

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 (79) hide show
  1. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +47 -0
  2. package/dist/commonjs/capture.d.ts +2 -0
  3. package/dist/commonjs/capture.js +3 -0
  4. package/dist/commonjs/components/EmptyState.d.ts +1 -0
  5. package/dist/commonjs/components/EmptyState.js +2 -2
  6. package/dist/commonjs/components/Inspector/FabLauncher.js +494 -39
  7. package/dist/commonjs/components/Inspector/InspectorHeader.js +45 -26
  8. package/dist/commonjs/components/Inspector/MainScreen.js +4 -3
  9. package/dist/commonjs/components/Inspector/MediaGalleryTab.js +418 -13
  10. package/dist/commonjs/components/Inspector/MediaPreviewModal.js +376 -88
  11. package/dist/commonjs/components/Inspector/NetworkDetail.js +49 -63
  12. package/dist/commonjs/components/Inspector/SettingsPanel.js +499 -119
  13. package/dist/commonjs/components/Inspector/TabBar.js +166 -4
  14. package/dist/commonjs/components/JsonViewer.js +4 -3
  15. package/dist/commonjs/components/NetworkIcons.d.ts +4 -0
  16. package/dist/commonjs/components/NetworkIcons.js +26 -1
  17. package/dist/commonjs/components/SegmentedTabs.js +1 -0
  18. package/dist/commonjs/constants/version.d.ts +1 -1
  19. package/dist/commonjs/constants/version.js +1 -1
  20. package/dist/commonjs/i18n/locales/en.json +20 -2
  21. package/dist/commonjs/index.js +45 -30
  22. package/dist/commonjs/native/NativeInspector.d.ts +3 -0
  23. package/dist/commonjs/native/NativeInspector.js +14 -1
  24. package/dist/commonjs/native/NativeNetworkInspector.d.ts +1 -0
  25. package/dist/commonjs/styles/AppColors.js +78 -78
  26. package/dist/commonjs/types/enums.d.ts +2 -0
  27. package/dist/commonjs/types/enums.js +2 -0
  28. package/dist/commonjs/types/interfaces.d.ts +5 -0
  29. package/dist/esm/capture.d.ts +2 -0
  30. package/dist/esm/capture.js +4 -1
  31. package/dist/esm/components/EmptyState.d.ts +1 -0
  32. package/dist/esm/components/EmptyState.js +2 -2
  33. package/dist/esm/components/Inspector/FabLauncher.js +462 -40
  34. package/dist/esm/components/Inspector/InspectorHeader.js +46 -27
  35. package/dist/esm/components/Inspector/MainScreen.js +4 -3
  36. package/dist/esm/components/Inspector/MediaGalleryTab.js +419 -14
  37. package/dist/esm/components/Inspector/MediaPreviewModal.js +379 -91
  38. package/dist/esm/components/Inspector/NetworkDetail.js +49 -63
  39. package/dist/esm/components/Inspector/SettingsPanel.js +500 -120
  40. package/dist/esm/components/Inspector/TabBar.js +135 -6
  41. package/dist/esm/components/JsonViewer.js +4 -3
  42. package/dist/esm/components/NetworkIcons.d.ts +4 -0
  43. package/dist/esm/components/NetworkIcons.js +21 -0
  44. package/dist/esm/components/SegmentedTabs.js +1 -0
  45. package/dist/esm/constants/version.d.ts +1 -1
  46. package/dist/esm/constants/version.js +1 -1
  47. package/dist/esm/i18n/locales/en.json +20 -2
  48. package/dist/esm/index.js +44 -29
  49. package/dist/esm/native/NativeInspector.d.ts +3 -0
  50. package/dist/esm/native/NativeInspector.js +12 -0
  51. package/dist/esm/native/NativeNetworkInspector.d.ts +1 -0
  52. package/dist/esm/styles/AppColors.js +78 -78
  53. package/dist/esm/types/enums.d.ts +2 -0
  54. package/dist/esm/types/enums.js +2 -0
  55. package/dist/esm/types/interfaces.d.ts +5 -0
  56. package/ios/NetworkInspectorModule.mm +241 -136
  57. package/package.json +1 -1
  58. package/react-native-inapp-inspector.podspec +1 -1
  59. package/src/capture.ts +8 -0
  60. package/src/components/EmptyState.tsx +3 -1
  61. package/src/components/Inspector/FabLauncher.tsx +562 -52
  62. package/src/components/Inspector/InspectorHeader.tsx +60 -22
  63. package/src/components/Inspector/MainScreen.tsx +3 -2
  64. package/src/components/Inspector/MediaGalleryTab.tsx +502 -28
  65. package/src/components/Inspector/MediaPreviewModal.tsx +465 -117
  66. package/src/components/Inspector/NetworkDetail.tsx +80 -97
  67. package/src/components/Inspector/SettingsPanel.tsx +708 -152
  68. package/src/components/Inspector/TabBar.tsx +174 -6
  69. package/src/components/JsonViewer.tsx +4 -2
  70. package/src/components/NetworkIcons.tsx +56 -0
  71. package/src/components/SegmentedTabs.tsx +1 -0
  72. package/src/constants/version.ts +1 -1
  73. package/src/i18n/locales/en.json +20 -4
  74. package/src/index.tsx +46 -31
  75. package/src/native/NativeInspector.ts +17 -0
  76. package/src/native/NativeNetworkInspector.ts +1 -0
  77. package/src/styles/AppColors.ts +80 -80
  78. package/src/types/enums.ts +2 -0
  79. package/src/types/interfaces.ts +5 -0
@@ -4,27 +4,32 @@ import { AppColors } from '../../styles/AppColors';
4
4
  import { AppFonts } from '../../styles/AppFonts';
5
5
  import TouchableScale from '../TouchableScale';
6
6
  import EmptyState from '../EmptyState';
7
- import { FilmIcon, GifIcon, ImageIcon, TrashIcon, } from '../NetworkIcons';
7
+ import { CheckIcon, FilmIcon, GifIcon, GridIcon, ImageIcon, ListIcon, PlayIcon, ScreencastIcon, TrashIcon, } from '../NetworkIcons';
8
8
  import { MediaPreviewModal } from './MediaPreviewModal';
9
9
  import { ScreenCapture, } from '../../capture';
10
10
  import { formatBytes } from '../../helpers';
11
11
  import { showToast } from '../../helpers/toast';
12
12
  import { useTranslation } from '../../i18n';
13
+ import { useInspector } from './InspectorContext';
13
14
  export const MediaGalleryTab = () => {
14
15
  const { t } = useTranslation();
16
+ const { refreshMediaCount, switchActiveTab } = useInspector();
15
17
  const [mediaList, setMediaList] = useState([]);
16
18
  const [refreshing, setRefreshing] = useState(false);
17
19
  const [selectedFilter, setSelectedFilter] = useState('all');
18
20
  const [selectedItem, setSelectedItem] = useState(null);
21
+ const [selectedIds, setSelectedIds] = useState(new Set());
22
+ const [viewMode, setViewMode] = useState('grid');
19
23
  const loadMedia = useCallback(async () => {
20
24
  try {
21
25
  const items = await ScreenCapture.getMediaList();
22
26
  setMediaList(items);
27
+ refreshMediaCount?.().catch(() => { });
23
28
  }
24
29
  catch {
25
30
  setMediaList([]);
26
31
  }
27
- }, []);
32
+ }, [refreshMediaCount]);
28
33
  useEffect(() => {
29
34
  loadMedia();
30
35
  }, [loadMedia]);
@@ -41,11 +46,57 @@ export const MediaGalleryTab = () => {
41
46
  const totalStorageBytes = useMemo(() => {
42
47
  return mediaList.reduce((sum, item) => sum + (item.sizeBytes || 0), 0);
43
48
  }, [mediaList]);
49
+ const toggleSelect = useCallback((id) => {
50
+ setSelectedIds(prev => {
51
+ const next = new Set(prev);
52
+ if (next.has(id)) {
53
+ next.delete(id);
54
+ }
55
+ else {
56
+ next.add(id);
57
+ }
58
+ return next;
59
+ });
60
+ }, []);
44
61
  const handleDeleteItem = async (item) => {
45
62
  await ScreenCapture.deleteMedia(item.uri);
63
+ setSelectedIds(prev => {
64
+ const next = new Set(prev);
65
+ next.delete(item.id);
66
+ return next;
67
+ });
46
68
  await loadMedia();
69
+ const count = await refreshMediaCount?.();
70
+ if (count === 0) {
71
+ switchActiveTab('apis');
72
+ }
47
73
  showToast(t('mediaGallery.deleted'));
48
74
  };
75
+ const handleDeleteSelected = () => {
76
+ const count = selectedIds.size;
77
+ if (count === 0)
78
+ return;
79
+ Alert.alert(t('mediaGallery.deleteSelectedTitle'), t('mediaGallery.deleteSelectedMessage', { count }), [
80
+ { text: t('common.cancel'), style: 'cancel' },
81
+ {
82
+ text: t('mediaGallery.delete'),
83
+ style: 'destructive',
84
+ onPress: async () => {
85
+ const itemsToDelete = mediaList.filter(item => selectedIds.has(item.id));
86
+ for (const item of itemsToDelete) {
87
+ await ScreenCapture.deleteMedia(item.uri);
88
+ }
89
+ setSelectedIds(new Set());
90
+ await loadMedia();
91
+ const remainingCount = await refreshMediaCount?.();
92
+ if (remainingCount === 0) {
93
+ switchActiveTab('apis');
94
+ }
95
+ showToast(t('mediaGallery.deletedCount', { count: itemsToDelete.length }));
96
+ },
97
+ },
98
+ ]);
99
+ };
49
100
  const handleClearAll = () => {
50
101
  Alert.alert(t('mediaGallery.purgeTitle'), t('mediaGallery.purgeMessage', { count: mediaList.length, size: formatBytes(totalStorageBytes) }), [
51
102
  { text: t('common.cancel'), style: 'cancel' },
@@ -54,7 +105,10 @@ export const MediaGalleryTab = () => {
54
105
  style: 'destructive',
55
106
  onPress: async () => {
56
107
  await ScreenCapture.clearAllMedia();
108
+ setSelectedIds(new Set());
57
109
  await loadMedia();
110
+ await refreshMediaCount?.();
111
+ switchActiveTab('apis');
58
112
  showToast(t('mediaGallery.allPurged'));
59
113
  },
60
114
  },
@@ -64,14 +118,45 @@ export const MediaGalleryTab = () => {
64
118
  const gifResult = await ScreenCapture.convertToGif(item.uri, { fps: 12, width: 480 });
65
119
  if (gifResult) {
66
120
  await loadMedia();
121
+ refreshMediaCount?.().catch(() => { });
67
122
  }
68
123
  };
69
- const renderItem = ({ item }) => {
124
+ const renderFilterIcon = (filterKey, isActive) => {
125
+ const iconColor = isActive ? AppColors.sky400 : AppColors.slate400;
126
+ const iconSize = 12;
127
+ switch (filterKey) {
128
+ case 'all':
129
+ return <ScreencastIcon size={iconSize} color={iconColor}/>;
130
+ case 'image':
131
+ return <ImageIcon size={iconSize} color={iconColor}/>;
132
+ case 'video':
133
+ return <FilmIcon size={iconSize} color={iconColor}/>;
134
+ case 'gif':
135
+ return <GifIcon size={iconSize} color={iconColor}/>;
136
+ }
137
+ };
138
+ const renderGridItem = ({ item }) => {
70
139
  const isVideo = item.type === 'video';
71
140
  const isGif = item.type === 'gif';
72
- return (<TouchableScale onPress={() => setSelectedItem(item)} style={galleryStyles.card}>
141
+ const thumbUri = isVideo ? item.thumbnailUri : item.uri;
142
+ const isSelected = selectedIds.has(item.id);
143
+ return (<TouchableScale onPress={() => setSelectedItem(item)} style={[galleryStyles.card, isSelected && galleryStyles.cardSelected]}>
73
144
  <View style={galleryStyles.thumbnailContainer}>
74
- <Image source={{ uri: item.uri }} style={galleryStyles.thumbnail} resizeMode="cover"/>
145
+ {thumbUri ? (<Image source={{ uri: thumbUri }} style={galleryStyles.thumbnail} resizeMode="cover"/>) : (<View style={galleryStyles.thumbnailFallback}>
146
+ {isVideo ? (<FilmIcon size={26} color={AppColors.sky400}/>) : (<ImageIcon size={26} color={AppColors.slate400}/>)}
147
+ </View>)}
148
+
149
+
150
+ <TouchableOpacity onPress={() => toggleSelect(item.id)} style={[
151
+ galleryStyles.checkbox,
152
+ isSelected && galleryStyles.checkboxSelected,
153
+ ]} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
154
+ {isSelected && <CheckIcon size={12} color={AppColors.white}/>}
155
+ </TouchableOpacity>
156
+
157
+ {isVideo && (<View style={galleryStyles.miniPlayCircle}>
158
+ <PlayIcon size={14} color={AppColors.white}/>
159
+ </View>)}
75
160
  <View style={galleryStyles.typeBadge}>
76
161
  {isVideo ? (<FilmIcon size={11} color={AppColors.sky400}/>) : isGif ? (<GifIcon size={11} color={AppColors.warningAmber}/>) : (<ImageIcon size={11} color={AppColors.emerald500}/>)}
77
162
  <Text style={galleryStyles.typeBadgeText}>
@@ -96,6 +181,59 @@ export const MediaGalleryTab = () => {
96
181
  </View>
97
182
  </TouchableScale>);
98
183
  };
184
+ const renderListItem = ({ item }) => {
185
+ const isVideo = item.type === 'video';
186
+ const isGif = item.type === 'gif';
187
+ const thumbUri = isVideo ? item.thumbnailUri : item.uri;
188
+ const isSelected = selectedIds.has(item.id);
189
+ return (<TouchableScale onPress={() => setSelectedItem(item)} style={[galleryStyles.listRow, isSelected && galleryStyles.listRowSelected]}>
190
+
191
+ <TouchableOpacity onPress={() => toggleSelect(item.id)} style={[
192
+ galleryStyles.listCheckbox,
193
+ isSelected && galleryStyles.checkboxSelected,
194
+ ]} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
195
+ {isSelected && <CheckIcon size={12} color={AppColors.white}/>}
196
+ </TouchableOpacity>
197
+
198
+
199
+ <View style={galleryStyles.listThumbContainer}>
200
+ {thumbUri ? (<Image source={{ uri: thumbUri }} style={galleryStyles.listThumb} resizeMode="cover"/>) : (<View style={galleryStyles.listThumbFallback}>
201
+ {isVideo ? (<FilmIcon size={18} color={AppColors.sky400}/>) : (<ImageIcon size={18} color={AppColors.slate400}/>)}
202
+ </View>)}
203
+ {isVideo && (<View style={galleryStyles.listMiniPlay}>
204
+ <PlayIcon size={10} color={AppColors.white}/>
205
+ </View>)}
206
+ </View>
207
+
208
+
209
+ <View style={galleryStyles.listInfo}>
210
+ <Text style={galleryStyles.listTitle} numberOfLines={1}>
211
+ {item.filename}
212
+ </Text>
213
+ <View style={galleryStyles.listMetaRow}>
214
+ <View style={galleryStyles.listBadge}>
215
+ {isVideo ? (<FilmIcon size={10} color={AppColors.sky400}/>) : isGif ? (<GifIcon size={10} color={AppColors.warningAmber}/>) : (<ImageIcon size={10} color={AppColors.emerald500}/>)}
216
+ <Text style={galleryStyles.listBadgeText}>
217
+ {item.format.toUpperCase()}
218
+ </Text>
219
+ </View>
220
+ <Text style={galleryStyles.cardMeta}>
221
+ {formatBytes(item.sizeBytes)}
222
+ </Text>
223
+ <Text style={galleryStyles.cardMetaDot}>•</Text>
224
+ <Text style={galleryStyles.cardMeta}>
225
+ {new Date(item.timestamp).toLocaleDateString([], { month: 'short', day: 'numeric' })}{' '}
226
+ {new Date(item.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
227
+ </Text>
228
+ </View>
229
+ </View>
230
+
231
+
232
+ <TouchableOpacity onPress={() => handleDeleteItem(item)} style={galleryStyles.listDeleteBtn} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
233
+ <TrashIcon size={14} color={AppColors.red500}/>
234
+ </TouchableOpacity>
235
+ </TouchableScale>);
236
+ };
99
237
  return (<View style={galleryStyles.container}>
100
238
 
101
239
  <View style={galleryStyles.filterBar}>
@@ -113,6 +251,7 @@ export const MediaGalleryTab = () => {
113
251
  galleryStyles.chip,
114
252
  isActive && galleryStyles.chipActive,
115
253
  ]}>
254
+ {renderFilterIcon(filterKey, isActive)}
116
255
  <Text style={[
117
256
  galleryStyles.chipText,
118
257
  isActive && galleryStyles.chipTextActive,
@@ -123,15 +262,68 @@ export const MediaGalleryTab = () => {
123
262
  })}
124
263
  </View>
125
264
 
126
- {mediaList.length > 0 && (<TouchableOpacity onPress={handleClearAll} style={galleryStyles.clearBtn} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
127
- <TrashIcon size={13} color={AppColors.red500}/>
128
- <Text style={galleryStyles.clearBtnText}>{t('mediaGallery.purge', { size: formatBytes(totalStorageBytes) })}</Text>
129
- </TouchableOpacity>)}
265
+
266
+ <View style={galleryStyles.viewToggleContainer}>
267
+ <TouchableOpacity onPress={() => setViewMode('grid')} style={[
268
+ galleryStyles.viewToggleBtn,
269
+ viewMode === 'grid' && galleryStyles.viewToggleBtnActive,
270
+ ]}>
271
+ <GridIcon size={13} color={viewMode === 'grid' ? AppColors.white : AppColors.slate400}/>
272
+ </TouchableOpacity>
273
+ <TouchableOpacity onPress={() => setViewMode('list')} style={[
274
+ galleryStyles.viewToggleBtn,
275
+ viewMode === 'list' && galleryStyles.viewToggleBtnActive,
276
+ ]}>
277
+ <ListIcon size={13} color={viewMode === 'list' ? AppColors.white : AppColors.slate400}/>
278
+ </TouchableOpacity>
279
+ </View>
130
280
  </View>
131
281
 
132
282
 
133
- <FlatList data={filteredMedia} keyExtractor={item => item.id} renderItem={renderItem} numColumns={2} columnWrapperStyle={galleryStyles.columnWrapper} contentContainerStyle={galleryStyles.listContent} refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor={AppColors.sky400}/>} ListEmptyComponent={<EmptyState customTitle={t('mediaGallery.noMediaTitle')} customSub={t('mediaGallery.noMediaDesc')}/>}/>
283
+ {mediaList.length > 0 && (<View style={galleryStyles.subBar}>
284
+ <View style={galleryStyles.resultCountWrapper}>
285
+ <Text style={[
286
+ galleryStyles.resultCountText,
287
+ selectedIds.size > 0 && galleryStyles.resultCountTextSelected,
288
+ ]}>
289
+ {selectedIds.size > 0
290
+ ? t('mediaGallery.selectedOfResults', {
291
+ selected: selectedIds.size,
292
+ total: filteredMedia.length,
293
+ defaultValue: `Selected ${selectedIds.size} of ${filteredMedia.length} results`,
294
+ })
295
+ : filteredMedia.length !== mediaList.length
296
+ ? t('mediaGallery.showingFilteredResults', {
297
+ count: filteredMedia.length,
298
+ total: mediaList.length,
299
+ defaultValue: `Showing ${filteredMedia.length} of ${mediaList.length} results`,
300
+ })
301
+ : t('mediaGallery.showingResults', {
302
+ count: filteredMedia.length,
303
+ defaultValue: `Showing ${filteredMedia.length} results`,
304
+ })}
305
+ </Text>
306
+ </View>
134
307
 
308
+ <View style={galleryStyles.actionsGroup}>
309
+ {selectedIds.size > 0 && (<TouchableOpacity onPress={handleDeleteSelected} style={galleryStyles.deleteBtn} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
310
+ <TrashIcon size={13} color={AppColors.red500}/>
311
+ <Text style={galleryStyles.deleteBtnText}>
312
+ {t('mediaGallery.deleteSelected', { count: selectedIds.size })}
313
+ </Text>
314
+ </TouchableOpacity>)}
315
+
316
+ <TouchableOpacity onPress={handleClearAll} style={galleryStyles.clearBtn} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
317
+ <TrashIcon size={13} color={AppColors.red500}/>
318
+ <Text style={galleryStyles.clearBtnText}>
319
+ {t('mediaGallery.purge', { size: formatBytes(totalStorageBytes) })}
320
+ </Text>
321
+ </TouchableOpacity>
322
+ </View>
323
+ </View>)}
324
+
325
+
326
+ <FlatList key={viewMode} data={filteredMedia} keyExtractor={item => item.id} renderItem={viewMode === 'grid' ? renderGridItem : renderListItem} numColumns={viewMode === 'grid' ? 2 : 1} columnWrapperStyle={viewMode === 'grid' ? galleryStyles.columnWrapper : undefined} contentContainerStyle={galleryStyles.listContent} refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor={AppColors.sky400}/>} ListEmptyComponent={<EmptyState customTitle={t('mediaGallery.noMediaTitle')} customSub={t('mediaGallery.noMediaDesc')} showReload={false}/>}/>
135
327
 
136
328
 
137
329
  <MediaPreviewModal item={selectedItem} visible={!!selectedItem} onClose={() => setSelectedItem(null)} onDelete={handleDeleteItem} onConvertToGif={handleConvertToGif}/>
@@ -149,19 +341,22 @@ const galleryStyles = StyleSheet.create({
149
341
  alignItems: 'center',
150
342
  justifyContent: 'space-between',
151
343
  paddingHorizontal: 12,
152
- paddingVertical: 10,
344
+ paddingVertical: 9,
153
345
  borderBottomWidth: 1,
154
346
  borderBottomColor: AppColors.borderGlassLight,
155
- flexWrap: 'wrap',
156
347
  gap: 8,
157
348
  },
158
349
  chipGroup: {
159
350
  flexDirection: 'row',
160
351
  alignItems: 'center',
161
352
  gap: 6,
353
+ flexShrink: 1,
162
354
  },
163
355
  chip: {
164
- paddingHorizontal: 9,
356
+ flexDirection: 'row',
357
+ alignItems: 'center',
358
+ gap: 4.5,
359
+ paddingHorizontal: 8,
165
360
  paddingVertical: 4.5,
166
361
  borderRadius: 6,
167
362
  backgroundColor: AppColors.whiteAlpha06,
@@ -181,6 +376,71 @@ const galleryStyles = StyleSheet.create({
181
376
  fontFamily: AppFonts.interBold,
182
377
  color: AppColors.sky400,
183
378
  },
379
+ subBar: {
380
+ flexDirection: 'row',
381
+ alignItems: 'center',
382
+ justifyContent: 'space-between',
383
+ paddingHorizontal: 12,
384
+ paddingVertical: 7,
385
+ backgroundColor: `${AppColors.slate900}99`,
386
+ borderBottomWidth: 1,
387
+ borderBottomColor: AppColors.borderGlassLight,
388
+ gap: 8,
389
+ },
390
+ resultCountWrapper: {
391
+ flexDirection: 'row',
392
+ alignItems: 'center',
393
+ flexShrink: 1,
394
+ },
395
+ resultCountText: {
396
+ fontFamily: AppFonts.interMedium,
397
+ fontSize: 11,
398
+ color: AppColors.slate400,
399
+ },
400
+ resultCountTextSelected: {
401
+ fontFamily: AppFonts.interSemiBold,
402
+ color: AppColors.sky400,
403
+ },
404
+ actionsGroup: {
405
+ flexDirection: 'row',
406
+ alignItems: 'center',
407
+ gap: 6,
408
+ },
409
+ viewToggleContainer: {
410
+ flexDirection: 'row',
411
+ alignItems: 'center',
412
+ backgroundColor: AppColors.whiteAlpha06,
413
+ borderRadius: 6,
414
+ padding: 2,
415
+ borderWidth: 1,
416
+ borderColor: AppColors.whiteAlpha10,
417
+ },
418
+ viewToggleBtn: {
419
+ paddingHorizontal: 7,
420
+ paddingVertical: 4,
421
+ borderRadius: 4,
422
+ alignItems: 'center',
423
+ justifyContent: 'center',
424
+ },
425
+ viewToggleBtnActive: {
426
+ backgroundColor: `${AppColors.violet600}80`,
427
+ },
428
+ deleteBtn: {
429
+ flexDirection: 'row',
430
+ alignItems: 'center',
431
+ gap: 4,
432
+ paddingHorizontal: 8,
433
+ paddingVertical: 4,
434
+ borderRadius: 6,
435
+ backgroundColor: `${AppColors.red500}26`,
436
+ borderWidth: 1,
437
+ borderColor: `${AppColors.red500}80`,
438
+ },
439
+ deleteBtnText: {
440
+ fontFamily: AppFonts.interBold,
441
+ fontSize: 10.5,
442
+ color: AppColors.red500,
443
+ },
184
444
  clearBtn: {
185
445
  flexDirection: 'row',
186
446
  alignItems: 'center',
@@ -199,7 +459,7 @@ const galleryStyles = StyleSheet.create({
199
459
  },
200
460
  listContent: {
201
461
  padding: 12,
202
- gap: 12,
462
+ gap: 10,
203
463
  },
204
464
  columnWrapper: {
205
465
  gap: 12,
@@ -212,6 +472,10 @@ const galleryStyles = StyleSheet.create({
212
472
  borderColor: AppColors.borderGlassLight,
213
473
  overflow: 'hidden',
214
474
  },
475
+ cardSelected: {
476
+ borderColor: AppColors.violet600,
477
+ borderWidth: 1.5,
478
+ },
215
479
  thumbnailContainer: {
216
480
  width: '100%',
217
481
  height: 110,
@@ -222,6 +486,50 @@ const galleryStyles = StyleSheet.create({
222
486
  width: '100%',
223
487
  height: '100%',
224
488
  },
489
+ thumbnailFallback: {
490
+ width: '100%',
491
+ height: '100%',
492
+ alignItems: 'center',
493
+ justifyContent: 'center',
494
+ backgroundColor: AppColors.slate900,
495
+ },
496
+ checkbox: {
497
+ position: 'absolute',
498
+ top: 6,
499
+ left: 6,
500
+ width: 22,
501
+ height: 22,
502
+ borderRadius: 5,
503
+ borderWidth: 1.5,
504
+ borderColor: 'rgba(255, 255, 255, 0.65)',
505
+ backgroundColor: 'rgba(15, 23, 42, 0.65)',
506
+ alignItems: 'center',
507
+ justifyContent: 'center',
508
+ zIndex: 10,
509
+ },
510
+ checkboxSelected: {
511
+ backgroundColor: AppColors.violet600,
512
+ borderColor: AppColors.violet600,
513
+ },
514
+ miniPlayCircle: {
515
+ position: 'absolute',
516
+ top: '50%',
517
+ left: '50%',
518
+ width: 32,
519
+ height: 32,
520
+ marginTop: -16,
521
+ marginLeft: -16,
522
+ borderRadius: 16,
523
+ backgroundColor: `${AppColors.sky400}D9`,
524
+ alignItems: 'center',
525
+ justifyContent: 'center',
526
+ paddingLeft: 2,
527
+ shadowColor: '#000',
528
+ shadowOffset: { width: 0, height: 2 },
529
+ shadowOpacity: 0.4,
530
+ shadowRadius: 4,
531
+ elevation: 4,
532
+ },
225
533
  typeBadge: {
226
534
  position: 'absolute',
227
535
  top: 6,
@@ -264,4 +572,101 @@ const galleryStyles = StyleSheet.create({
264
572
  fontSize: 10,
265
573
  color: AppColors.whiteAlpha30,
266
574
  },
575
+ listRow: {
576
+ flexDirection: 'row',
577
+ alignItems: 'center',
578
+ backgroundColor: AppColors.slate800,
579
+ borderRadius: 8,
580
+ borderWidth: 1,
581
+ borderColor: AppColors.borderGlassLight,
582
+ padding: 8,
583
+ gap: 10,
584
+ },
585
+ listRowSelected: {
586
+ borderColor: AppColors.violet600,
587
+ borderWidth: 1.5,
588
+ backgroundColor: `${AppColors.violet600}14`,
589
+ },
590
+ listCheckbox: {
591
+ width: 20,
592
+ height: 20,
593
+ borderRadius: 4,
594
+ borderWidth: 1.5,
595
+ borderColor: 'rgba(255, 255, 255, 0.65)',
596
+ backgroundColor: 'rgba(15, 23, 42, 0.65)',
597
+ alignItems: 'center',
598
+ justifyContent: 'center',
599
+ },
600
+ listThumbContainer: {
601
+ width: 46,
602
+ height: 46,
603
+ borderRadius: 6,
604
+ backgroundColor: AppColors.slate850,
605
+ overflow: 'hidden',
606
+ position: 'relative',
607
+ },
608
+ listThumb: {
609
+ width: '100%',
610
+ height: '100%',
611
+ },
612
+ listThumbFallback: {
613
+ width: '100%',
614
+ height: '100%',
615
+ alignItems: 'center',
616
+ justifyContent: 'center',
617
+ backgroundColor: AppColors.slate900,
618
+ },
619
+ listMiniPlay: {
620
+ position: 'absolute',
621
+ top: '50%',
622
+ left: '50%',
623
+ width: 20,
624
+ height: 20,
625
+ marginTop: -10,
626
+ marginLeft: -10,
627
+ borderRadius: 10,
628
+ backgroundColor: `${AppColors.sky400}D9`,
629
+ alignItems: 'center',
630
+ justifyContent: 'center',
631
+ paddingLeft: 1,
632
+ },
633
+ listInfo: {
634
+ flex: 1,
635
+ gap: 4,
636
+ minWidth: 0,
637
+ },
638
+ listTitle: {
639
+ fontFamily: AppFonts.interMedium,
640
+ fontSize: 12,
641
+ color: AppColors.white,
642
+ },
643
+ listMetaRow: {
644
+ flexDirection: 'row',
645
+ alignItems: 'center',
646
+ gap: 6,
647
+ flexWrap: 'wrap',
648
+ },
649
+ listBadge: {
650
+ flexDirection: 'row',
651
+ alignItems: 'center',
652
+ gap: 3,
653
+ backgroundColor: AppColors.badgeGlassDark,
654
+ paddingHorizontal: 4,
655
+ paddingVertical: 1.5,
656
+ borderRadius: 4,
657
+ borderWidth: 1,
658
+ borderColor: AppColors.borderGlassMedium,
659
+ },
660
+ listBadgeText: {
661
+ fontFamily: AppFonts.interBold,
662
+ fontSize: 8.5,
663
+ color: AppColors.slate200,
664
+ },
665
+ listDeleteBtn: {
666
+ padding: 6,
667
+ borderRadius: 6,
668
+ backgroundColor: `${AppColors.red500}14`,
669
+ borderWidth: 1,
670
+ borderColor: `${AppColors.red500}33`,
671
+ },
267
672
  });