react-native-inapp-inspector 2.3.14 → 2.3.15

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 (65) hide show
  1. package/dist/commonjs/components/AnimatedEntrance.js +3 -3
  2. package/dist/commonjs/components/ConsoleLogCard.js +174 -100
  3. package/dist/commonjs/components/Inspector/ConsoleTab.js +25 -16
  4. package/dist/commonjs/components/Inspector/CrashTab.js +19 -5
  5. package/dist/commonjs/components/Inspector/DeviceInfoTab.js +198 -98
  6. package/dist/commonjs/components/Inspector/InspectorHeader.js +2 -2
  7. package/dist/commonjs/components/Inspector/MainScreen.js +56 -56
  8. package/dist/commonjs/components/Inspector/NetworkTab.js +1 -335
  9. package/dist/commonjs/components/Inspector/PerformanceTab.js +32 -6
  10. package/dist/commonjs/components/Inspector/ReduxTab.js +3 -1
  11. package/dist/commonjs/components/Inspector/StorageTab.js +23 -5
  12. package/dist/commonjs/components/JsonViewer.js +14 -2
  13. package/dist/commonjs/components/LogCard.js +428 -227
  14. package/dist/commonjs/constants/version.d.ts +1 -1
  15. package/dist/commonjs/constants/version.js +1 -1
  16. package/dist/commonjs/customHooks/analyticsLogger.js +1 -1
  17. package/dist/commonjs/customHooks/consoleLogger.js +34 -2
  18. package/dist/commonjs/customHooks/crashHandler.js +1 -1
  19. package/dist/commonjs/customHooks/networkLogger.js +184 -26
  20. package/dist/commonjs/helpers/searchQueryParser.d.ts +1 -1
  21. package/dist/commonjs/helpers/searchQueryParser.js +37 -291
  22. package/dist/commonjs/index.js +141 -27
  23. package/dist/esm/components/AnimatedEntrance.js +3 -3
  24. package/dist/esm/components/ConsoleLogCard.js +141 -100
  25. package/dist/esm/components/Inspector/ConsoleTab.js +25 -16
  26. package/dist/esm/components/Inspector/CrashTab.js +19 -5
  27. package/dist/esm/components/Inspector/DeviceInfoTab.js +198 -98
  28. package/dist/esm/components/Inspector/InspectorHeader.js +3 -3
  29. package/dist/esm/components/Inspector/MainScreen.js +56 -56
  30. package/dist/esm/components/Inspector/NetworkTab.js +2 -336
  31. package/dist/esm/components/Inspector/PerformanceTab.js +32 -6
  32. package/dist/esm/components/Inspector/ReduxTab.js +3 -1
  33. package/dist/esm/components/Inspector/StorageTab.js +23 -5
  34. package/dist/esm/components/JsonViewer.js +14 -2
  35. package/dist/esm/components/LogCard.js +422 -221
  36. package/dist/esm/constants/version.d.ts +1 -1
  37. package/dist/esm/constants/version.js +1 -1
  38. package/dist/esm/customHooks/analyticsLogger.js +1 -1
  39. package/dist/esm/customHooks/consoleLogger.js +34 -2
  40. package/dist/esm/customHooks/crashHandler.js +1 -1
  41. package/dist/esm/customHooks/networkLogger.js +184 -26
  42. package/dist/esm/helpers/searchQueryParser.d.ts +1 -1
  43. package/dist/esm/helpers/searchQueryParser.js +37 -291
  44. package/dist/esm/index.js +141 -27
  45. package/package.json +1 -1
  46. package/src/components/AnimatedEntrance.tsx +3 -3
  47. package/src/components/ConsoleLogCard.tsx +154 -103
  48. package/src/components/Inspector/ConsoleTab.tsx +27 -18
  49. package/src/components/Inspector/CrashTab.tsx +19 -5
  50. package/src/components/Inspector/DeviceInfoTab.tsx +224 -102
  51. package/src/components/Inspector/InspectorHeader.tsx +5 -3
  52. package/src/components/Inspector/MainScreen.tsx +2 -3
  53. package/src/components/Inspector/NetworkTab.tsx +1 -402
  54. package/src/components/Inspector/PerformanceTab.tsx +36 -7
  55. package/src/components/Inspector/ReduxTab.tsx +9 -1
  56. package/src/components/Inspector/StorageTab.tsx +27 -7
  57. package/src/components/JsonViewer.tsx +15 -2
  58. package/src/components/LogCard.tsx +528 -339
  59. package/src/constants/version.ts +1 -1
  60. package/src/customHooks/analyticsLogger.ts +1 -1
  61. package/src/customHooks/consoleLogger.ts +36 -3
  62. package/src/customHooks/crashHandler.ts +1 -1
  63. package/src/customHooks/networkLogger.ts +214 -26
  64. package/src/helpers/searchQueryParser.ts +40 -285
  65. package/src/index.tsx +332 -211
@@ -173,85 +173,6 @@ const NetworkTab = react_1.default.memo(() => {
173
173
  color: '#E10098',
174
174
  },
175
175
  ], [quickCounts]);
176
- const SEARCH_SCOPES = [
177
- {
178
- id: 'all',
179
- label: 'All',
180
- icon: color => <NetworkIcons_1.LayersIcon size={9.5} color={color}/>,
181
- },
182
- {
183
- id: 'url',
184
- label: 'URL / Path',
185
- icon: color => <NetworkIcons_1.GlobeIcon size={9.5} color={color}/>,
186
- },
187
- {
188
- id: 'reqBody',
189
- label: 'Payload',
190
- icon: color => <NetworkIcons_1.RequestIcon size={9.5} color={color}/>,
191
- },
192
- {
193
- id: 'resBody',
194
- label: 'Response',
195
- icon: color => <NetworkIcons_1.ResponseIcon size={9.5} color={color}/>,
196
- },
197
- {
198
- id: 'headers',
199
- label: 'Headers',
200
- icon: color => <NetworkIcons_1.HeadersIcon size={9.5} color={color}/>,
201
- },
202
- ];
203
- const QUICK_API_QUERY_TAGS = (0, react_1.useMemo)(() => [
204
- {
205
- label: 'Failed',
206
- query: 'is:error',
207
- icon: (color) => <NetworkIcons_1.CircleXIcon size={9.5} color={color}/>,
208
- },
209
- {
210
- label: 'Slow (>1s)',
211
- query: 'slow:>1s',
212
- icon: (color) => <NetworkIcons_1.ClockIcon size={9.5} color={color}/>,
213
- },
214
- {
215
- label: 'POST',
216
- query: 'm:POST',
217
- icon: (color) => <NetworkIcons_1.RequestIcon size={9.5} color={color}/>,
218
- },
219
- {
220
- label: 'GET',
221
- query: 'm:GET',
222
- icon: (color) => <NetworkIcons_1.ResponseIcon size={9.5} color={color}/>,
223
- },
224
- {
225
- label: 'GraphQL',
226
- query: 'is:graphql',
227
- icon: (color) => <NetworkIcons_1.AtomIcon size={9.5} color={color}/>,
228
- },
229
- {
230
- label: 'Axios',
231
- query: 'client:axios',
232
- icon: (color) => <NetworkIcons_1.BoltIcon size={9.5} color={color}/>,
233
- },
234
- {
235
- label: '200 OK',
236
- query: 'status:200',
237
- icon: (color) => <NetworkIcons_1.CircleCheckIcon size={9.5} color={color}/>,
238
- },
239
- {
240
- label: '404',
241
- query: 'status:404',
242
- icon: (color) => <NetworkIcons_1.CircleAlertIcon size={9.5} color={color}/>,
243
- },
244
- {
245
- label: '500',
246
- query: 'status:500',
247
- icon: (color) => <NetworkIcons_1.CircleXIcon size={9.5} color={color}/>,
248
- },
249
- {
250
- label: 'HTTPS',
251
- query: 'is:https',
252
- icon: (color) => <NetworkIcons_1.ShieldAlertIcon size={9.5} color={color}/>,
253
- },
254
- ], []);
255
176
  const networkMetrics = (0, react_1.useMemo)(() => {
256
177
  if (!logs || logs.length === 0)
257
178
  return null;
@@ -397,61 +318,7 @@ const NetworkTab = react_1.default.memo(() => {
397
318
  <NetworkIcons_1.SearchIcon color={isSearchFocused
398
319
  ? AppColors_1.AppColors.purple
399
320
  : AppColors_1.AppColors.grayTextWeak} size={15}/>
400
- <react_native_1.TextInput placeholder="Search url, body, is:error, slow:>1s..." placeholderTextColor={AppColors_1.AppColors.grayTextWeak} value={search} onChangeText={setSearch} onFocus={() => setIsSearchFocused(true)} onBlur={() => setIsSearchFocused(false)} style={styles_1.default.searchInput} autoCorrect={false} autoCapitalize="none"/>
401
-
402
-
403
- <react_native_1.View style={{
404
- flexDirection: 'row',
405
- alignItems: 'center',
406
- gap: 3,
407
- marginRight: 2,
408
- }}>
409
- <react_native_1.Pressable hitSlop={8} onPress={() => setIsCaseSensitive(prev => !prev)} style={{
410
- paddingHorizontal: 4.5,
411
- paddingVertical: 2,
412
- borderRadius: 4,
413
- backgroundColor: isCaseSensitive
414
- ? AppColors_1.AppColors.purple
415
- : 'transparent',
416
- borderWidth: 1,
417
- borderColor: isCaseSensitive
418
- ? AppColors_1.AppColors.purple
419
- : `${AppColors_1.AppColors.grayBorderSecondary}`,
420
- }}>
421
- <react_native_1.Text style={{
422
- fontFamily: AppFonts_1.AppFonts.interBold,
423
- fontSize: 9.5,
424
- color: isCaseSensitive
425
- ? AppColors_1.AppColors.white
426
- : AppColors_1.AppColors.grayTextWeak,
427
- }}>
428
- Aa
429
- </react_native_1.Text>
430
- </react_native_1.Pressable>
431
-
432
- <react_native_1.Pressable hitSlop={8} onPress={() => setIsRegexSearch(prev => !prev)} style={{
433
- paddingHorizontal: 4.5,
434
- paddingVertical: 2,
435
- borderRadius: 4,
436
- backgroundColor: isRegexSearch
437
- ? AppColors_1.AppColors.purple
438
- : 'transparent',
439
- borderWidth: 1,
440
- borderColor: isRegexSearch
441
- ? AppColors_1.AppColors.purple
442
- : `${AppColors_1.AppColors.grayBorderSecondary}`,
443
- }}>
444
- <react_native_1.Text style={{
445
- fontFamily: AppFonts_1.AppFonts.interBold,
446
- fontSize: 9.5,
447
- color: isRegexSearch
448
- ? AppColors_1.AppColors.white
449
- : AppColors_1.AppColors.grayTextWeak,
450
- }}>
451
- .*
452
- </react_native_1.Text>
453
- </react_native_1.Pressable>
454
- </react_native_1.View>
321
+ <react_native_1.TextInput placeholder="Search URL, status, method, headers, body..." placeholderTextColor={AppColors_1.AppColors.grayTextWeak} value={search} onChangeText={setSearch} onFocus={() => setIsSearchFocused(true)} onBlur={() => setIsSearchFocused(false)} style={styles_1.default.searchInput} autoCorrect={false} autoCapitalize="none"/>
455
322
 
456
323
  {search.length > 0 && (<react_native_1.View style={{
457
324
  flexDirection: 'row',
@@ -527,94 +394,6 @@ const NetworkTab = react_1.default.memo(() => {
527
394
  </react_native_1.View>
528
395
 
529
396
 
530
- <react_native_1.View style={{ paddingBottom: 4 }}>
531
- <react_native_1.ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{
532
- paddingHorizontal: 12,
533
- flexDirection: 'row',
534
- alignItems: 'center',
535
- gap: 5,
536
- paddingVertical: 2,
537
- }}>
538
-
539
- {SEARCH_SCOPES.map(s => {
540
- const isActive = searchScope === s.id;
541
- return (<TouchableScale_1.default key={`scope_${s.id}`} onPress={() => setSearchScope(s.id)} hitSlop={6} style={{
542
- flexDirection: 'row',
543
- alignItems: 'center',
544
- gap: 3.5,
545
- paddingHorizontal: 7.5,
546
- paddingVertical: 3.5,
547
- borderRadius: 6,
548
- backgroundColor: isActive
549
- ? AppColors_1.AppColors.purple
550
- : AppColors_1.AppColors.grayBackground,
551
- borderWidth: 1,
552
- borderColor: isActive
553
- ? AppColors_1.AppColors.purple
554
- : AppColors_1.AppColors.grayBorderSecondary,
555
- }}>
556
- {s.icon(isActive ? AppColors_1.AppColors.white : AppColors_1.AppColors.grayTextWeak)}
557
- <react_native_1.Text style={{
558
- fontFamily: AppFonts_1.AppFonts.interBold,
559
- fontSize: 10,
560
- color: isActive ? AppColors_1.AppColors.white : AppColors_1.AppColors.grayText,
561
- }}>
562
- {s.label}
563
- </react_native_1.Text>
564
- </TouchableScale_1.default>);
565
- })}
566
-
567
- <react_native_1.View style={{
568
- width: 1,
569
- height: 14,
570
- backgroundColor: AppColors_1.AppColors.dividerColor,
571
- marginHorizontal: 3,
572
- }}/>
573
-
574
-
575
- {QUICK_API_QUERY_TAGS.map(q => {
576
- const isActive = search.includes(q.query);
577
- return (<TouchableScale_1.default key={`quick_${q.query}`} onPress={() => {
578
- setSearch(prev => {
579
- if (prev.includes(q.query)) {
580
- return prev
581
- .replace(q.query, '')
582
- .replace(/\s+/g, ' ')
583
- .trim();
584
- }
585
- return `${prev} ${q.query}`.trim();
586
- });
587
- }} hitSlop={6} style={{
588
- flexDirection: 'row',
589
- alignItems: 'center',
590
- gap: 3.5,
591
- paddingHorizontal: 7.5,
592
- paddingVertical: 3.5,
593
- borderRadius: 6,
594
- backgroundColor: isActive
595
- ? `${AppColors_1.AppColors.purple}20`
596
- : AppColors_1.AppColors.grayBackground,
597
- borderWidth: 1,
598
- borderColor: isActive
599
- ? AppColors_1.AppColors.purple
600
- : AppColors_1.AppColors.grayBorderSecondary,
601
- }}>
602
- {q.icon(isActive ? AppColors_1.AppColors.purple : AppColors_1.AppColors.grayTextWeak)}
603
- <react_native_1.Text style={{
604
- fontFamily: AppFonts_1.AppFonts.interMedium,
605
- fontSize: 10,
606
- color: isActive
607
- ? AppColors_1.AppColors.purple
608
- : AppColors_1.AppColors.grayText,
609
- }}>
610
- {q.label}
611
- </react_native_1.Text>
612
- </TouchableScale_1.default>);
613
- })}
614
- </react_native_1.ScrollView>
615
- </react_native_1.View>
616
-
617
-
618
397
  <react_native_1.View style={{ marginBottom: 8 }}>
619
398
  <react_native_1.ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{
620
399
  paddingHorizontal: 12,
@@ -728,110 +507,6 @@ const NetworkTab = react_1.default.memo(() => {
728
507
  </react_native_1.View>
729
508
 
730
509
 
731
- {(isSearchFocused || search.length > 0) && (<react_native_1.View style={{
732
- flexDirection: 'row',
733
- alignItems: 'center',
734
- paddingHorizontal: 12,
735
- marginBottom: 6,
736
- gap: 6,
737
- }}>
738
- <react_native_1.Text style={{
739
- fontFamily: AppFonts_1.AppFonts.interBold,
740
- fontSize: 9.5,
741
- color: AppColors_1.AppColors.grayTextWeak,
742
- textTransform: 'uppercase',
743
- letterSpacing: 0.5,
744
- }}>
745
- Scope:
746
- </react_native_1.Text>
747
- <react_native_1.ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{
748
- flexDirection: 'row',
749
- alignItems: 'center',
750
- gap: 5,
751
- }}>
752
- {SEARCH_SCOPES.map(s => {
753
- const isSelected = searchScope === s.id;
754
- const iconColor = isSelected ? AppColors_1.AppColors.white : AppColors_1.AppColors.grayText;
755
- return (<TouchableScale_1.default key={s.id} onPress={() => setSearchScope(s.id)}>
756
- <react_native_1.View style={{
757
- flexDirection: 'row',
758
- alignItems: 'center',
759
- gap: 4,
760
- paddingHorizontal: 7,
761
- paddingVertical: 2.5,
762
- borderRadius: 5,
763
- backgroundColor: isSelected
764
- ? AppColors_1.AppColors.purple
765
- : `${AppColors_1.AppColors.grayBorderSecondary}40`,
766
- borderWidth: 1,
767
- borderColor: isSelected
768
- ? AppColors_1.AppColors.purple
769
- : AppColors_1.AppColors.grayBorderSecondary,
770
- }}>
771
- {s.icon(iconColor)}
772
- <react_native_1.Text style={{
773
- fontFamily: AppFonts_1.AppFonts.interBold,
774
- fontSize: 9.5,
775
- color: isSelected
776
- ? AppColors_1.AppColors.white
777
- : AppColors_1.AppColors.grayText,
778
- }}>
779
- {s.label}
780
- </react_native_1.Text>
781
- </react_native_1.View>
782
- </TouchableScale_1.default>);
783
- })}
784
- </react_native_1.ScrollView>
785
- </react_native_1.View>)}
786
-
787
-
788
- {(isSearchFocused || search.length > 0) && (<react_native_1.ScrollView horizontal showsHorizontalScrollIndicator={false} style={{ marginBottom: 8, maxHeight: 28 }} contentContainerStyle={{
789
- paddingHorizontal: 12,
790
- flexDirection: 'row',
791
- alignItems: 'center',
792
- gap: 5,
793
- }}>
794
- {QUICK_API_QUERY_TAGS.map(item => {
795
- const isSelected = search.includes(item.query);
796
- return (<TouchableScale_1.default key={item.query} onPress={() => {
797
- if (isSelected) {
798
- setSearch(prev => prev.replace(item.query, '').trim());
799
- }
800
- else {
801
- setSearch(prev => prev ? `${prev} ${item.query}`.trim() : item.query);
802
- }
803
- }}>
804
- <react_native_1.View style={{
805
- flexDirection: 'row',
806
- alignItems: 'center',
807
- gap: 4,
808
- paddingHorizontal: 7,
809
- paddingVertical: 2.5,
810
- borderRadius: 5,
811
- backgroundColor: isSelected
812
- ? AppColors_1.AppColors.purple
813
- : `${AppColors_1.AppColors.purple}14`,
814
- borderWidth: 1,
815
- borderColor: isSelected
816
- ? AppColors_1.AppColors.purple
817
- : `${AppColors_1.AppColors.purple}30`,
818
- }}>
819
- {item.icon(isSelected ? AppColors_1.AppColors.white : AppColors_1.AppColors.purple)}
820
- <react_native_1.Text style={{
821
- fontFamily: AppFonts_1.AppFonts.interBold,
822
- fontSize: 9.5,
823
- color: isSelected
824
- ? AppColors_1.AppColors.white
825
- : AppColors_1.AppColors.purple,
826
- }}>
827
- {item.label}
828
- </react_native_1.Text>
829
- </react_native_1.View>
830
- </TouchableScale_1.default>);
831
- })}
832
- </react_native_1.ScrollView>)}
833
-
834
-
835
510
  {(quickFilter !== 'all' ||
836
511
  search.trim().length > 0 ||
837
512
  statusFilters.size > 0 ||
@@ -871,15 +546,6 @@ const NetworkTab = react_1.default.memo(() => {
871
546
  </react_native_1.Text>
872
547
  </TouchableScale_1.default>
873
548
  </react_native_1.View>)}
874
-
875
- {(search ||
876
- statusFilters.size > 0 ||
877
- methodFilters.size > 0 ||
878
- quickFilter !== 'all') && (<react_native_1.Text style={styles_1.default.resultCount}>
879
- {filteredLogs.length === logs.length
880
- ? `${logs.length} requests`
881
- : `${filteredLogs.length} of ${logs.length} filtered requests`}
882
- </react_native_1.Text>)}
883
549
  </react_native_1.View>} ListEmptyComponent={<EmptyState_1.default isSearch={search.length > 0 ||
884
550
  statusFilters.size > 0 ||
885
551
  methodFilters.size > 0 ||
@@ -186,10 +186,19 @@ const PerformanceTab = react_1.default.memo(() => {
186
186
  }, [isRecording]);
187
187
  const filteredEvents = (0, react_1.useMemo)(() => {
188
188
  return events.filter(e => {
189
- const matchSearch = !search ||
190
- e.label.toLowerCase().includes(search.toLowerCase()) ||
191
- e.detail.toLowerCase().includes(search.toLowerCase()) ||
192
- (e.source && e.source.toLowerCase().includes(search.toLowerCase()));
189
+ let matchSearch = true;
190
+ if (search && search.trim().length > 0) {
191
+ const queryTokens = search.trim().toLowerCase().split(/\s+/).filter(Boolean);
192
+ const corpus = [
193
+ e.label || '',
194
+ e.detail || '',
195
+ e.source || '',
196
+ e.category || '',
197
+ e.severity || '',
198
+ e.bottleneckThread || '',
199
+ ].join(' ').toLowerCase();
200
+ matchSearch = queryTokens.every(tok => corpus.includes(tok));
201
+ }
193
202
  const matchFilter = filterCategory === 'ALL'
194
203
  ? true
195
204
  : filterCategory === 'JANKY'
@@ -814,10 +823,27 @@ const PerformanceTab = react_1.default.memo(() => {
814
823
 
815
824
  {filteredEvents.length === 0 ? (<react_native_1.View style={perfStyles.emptyCard}>
816
825
  <NetworkIcons_1.BoltIcon color={AppColors_1.AppColors.amber500} size={30}/>
817
- <react_native_1.Text style={perfStyles.emptyTitle}>{t('performance.emptyTitle')}</react_native_1.Text>
826
+ <react_native_1.Text style={perfStyles.emptyTitle}>
827
+ {search.trim().length > 0 ? 'No Matching Events' : t('performance.emptyTitle')}
828
+ </react_native_1.Text>
818
829
  <react_native_1.Text style={perfStyles.emptySub}>
819
- {t('performance.emptySubtitle')}
830
+ {search.trim().length > 0
831
+ ? `No performance events match "${search}"`
832
+ : t('performance.emptySubtitle')}
820
833
  </react_native_1.Text>
834
+ {search.trim().length > 0 && (<TouchableScale_1.default onPress={() => setSearch('')} style={{
835
+ marginTop: 10,
836
+ paddingHorizontal: 14,
837
+ paddingVertical: 6,
838
+ borderRadius: 8,
839
+ backgroundColor: AppColors_1.AppColors.grayBackground,
840
+ borderWidth: 1,
841
+ borderColor: AppColors_1.AppColors.grayBorderSecondary,
842
+ }}>
843
+ <react_native_1.Text style={{ color: AppColors_1.AppColors.brandPurple, fontSize: 12, fontFamily: AppFonts_1.AppFonts.interBold }}>
844
+ Clear Search
845
+ </react_native_1.Text>
846
+ </TouchableScale_1.default>)}
821
847
  </react_native_1.View>) : (filteredEvents.map(event => {
822
848
  const isOptimal = event.severity === 'optimal';
823
849
  const isWarning = event.severity === 'warning';
@@ -492,7 +492,9 @@ const ReduxTab = react_1.default.memo(() => {
492
492
  </react_native_1.View>
493
493
 
494
494
 
495
- <react_native_1.FlatList ref={listRef} data={filteredSlices} keyExtractor={item => item.id} renderItem={renderSliceItem} initialNumToRender={12} maxToRenderPerBatch={8} windowSize={5} removeClippedSubviews={true} renderToHardwareTextureAndroid={true} ListEmptyComponent={<EmptyState_1.default isSearch={reduxSearch.length > 0}/>} ListFooterComponent={filteredSlices.length > 0 ? (<EndOfListFooter_1.default count={filteredSlices.length} label="slices"/>) : null} contentContainerStyle={[
495
+ <react_native_1.FlatList ref={listRef} data={filteredSlices} keyExtractor={item => item.id} renderItem={renderSliceItem} initialNumToRender={12} maxToRenderPerBatch={8} windowSize={5} removeClippedSubviews={true} renderToHardwareTextureAndroid={true} ListEmptyComponent={<EmptyState_1.default isSearch={reduxSearch.trim().length > 0} searchQuery={reduxSearch} onClearSearch={() => setReduxSearch('')} customTitle={reduxSearch.trim().length > 0 ? 'No Matching Slices' : 'No Redux State Detected'} customSub={reduxSearch.trim().length > 0
496
+ ? `No Redux slices match "${reduxSearch}"`
497
+ : 'Attach the Redux store or dispatch actions to see state slices.'}/>} ListFooterComponent={filteredSlices.length > 0 ? (<EndOfListFooter_1.default count={filteredSlices.length} label="slices"/>) : null} contentContainerStyle={[
496
498
  reduxTabStyles.listContent,
497
499
  filteredSlices.length === 0 && { flexGrow: 1 },
498
500
  ]} keyboardShouldPersistTaps="handled"/>
@@ -271,8 +271,11 @@ exports.StorageTab = react_1.default.memo(() => {
271
271
  const filteredEntries = (0, react_1.useMemo)(() => {
272
272
  if (!search.trim())
273
273
  return entries;
274
- const q = search.toLowerCase().trim();
275
- return entries.filter(e => e.key.toLowerCase().includes(q) || e.value.toLowerCase().includes(q));
274
+ const tokens = search.toLowerCase().trim().split(/\s+/).filter(Boolean);
275
+ return entries.filter(e => {
276
+ const corpus = `${e.key} ${e.value}`.toLowerCase();
277
+ return tokens.every(tok => corpus.includes(tok));
278
+ });
276
279
  }, [entries, search]);
277
280
  const totalBytes = (0, react_1.useMemo)(() => {
278
281
  return entries.reduce((sum, e) => sum + e.byteSize, 0);
@@ -410,13 +413,28 @@ exports.StorageTab = react_1.default.memo(() => {
410
413
  <NetworkIcons_1.DatabaseIcon size={28} color={AppColors_1.AppColors.grayTextWeak}/>
411
414
  </react_native_1.View>
412
415
  <react_native_1.Text style={styles.emptyTitle}>
413
- {search ? 'No matching keys found' : `No ${activeDriver === 'asyncStorage' ? 'AsyncStorage' : 'MMKV'} Keys`}
416
+ {search.trim().length > 0
417
+ ? 'No matching keys found'
418
+ : `No ${activeDriver === 'asyncStorage' ? 'AsyncStorage' : 'MMKV'} Keys`}
414
419
  </react_native_1.Text>
415
420
  <react_native_1.Text style={styles.emptySubtitle}>
416
- {search
417
- ? 'Try modifying your search query'
421
+ {search.trim().length > 0
422
+ ? `No keys or values match "${search}"`
418
423
  : `Storage is auto-detected. Tap "+ Add" above to create your first ${activeDriver === 'asyncStorage' ? 'AsyncStorage' : 'MMKV'} key!`}
419
424
  </react_native_1.Text>
425
+ {search.trim().length > 0 && (<TouchableScale_1.default onPress={() => setSearch('')} style={{
426
+ marginTop: 12,
427
+ paddingHorizontal: 14,
428
+ paddingVertical: 6,
429
+ borderRadius: 8,
430
+ backgroundColor: AppColors_1.AppColors.grayBackground,
431
+ borderWidth: 1,
432
+ borderColor: AppColors_1.AppColors.grayBorderSecondary,
433
+ }}>
434
+ <react_native_1.Text style={{ color: AppColors_1.AppColors.brandPurple, fontSize: 12, fontFamily: AppFonts_1.AppFonts.interBold }}>
435
+ Clear Search
436
+ </react_native_1.Text>
437
+ </TouchableScale_1.default>)}
420
438
  </react_native_1.View>} ListFooterComponent={<react_native_1.View style={{ height: 60 }}/>}/>
421
439
 
422
440
  <TouchableScale_1.default onPress={() => {
@@ -149,10 +149,22 @@ const JsonViewer = react_1.default.memo(({ data, search, forceOpen, defaultExpan
149
149
  <react_native_1.Text style={localStyles.tableCellValue}>{String(data)}</react_native_1.Text>
150
150
  </react_native_1.View>);
151
151
  }
152
- const keys = Object.keys(data);
152
+ let keys = Object.keys(data);
153
+ if (search && search.trim().length > 0) {
154
+ const q = search.trim().toLowerCase();
155
+ keys = keys.filter(key => {
156
+ const val = data[key];
157
+ const valStr = typeof val === 'object' && val !== null ? JSON.stringify(val) : String(val);
158
+ return key.toLowerCase().includes(q) || valStr.toLowerCase().includes(q);
159
+ });
160
+ }
153
161
  if (keys.length === 0) {
154
162
  return (<react_native_1.View style={localStyles.emptyTable}>
155
- <react_native_1.Text style={localStyles.emptyTableText}>{t('network.jsonViewer.emptyTable')}</react_native_1.Text>
163
+ <react_native_1.Text style={localStyles.emptyTableText}>
164
+ {search && search.trim().length > 0
165
+ ? 'No matching keys or values found'
166
+ : t('network.jsonViewer.emptyTable')}
167
+ </react_native_1.Text>
156
168
  </react_native_1.View>);
157
169
  }
158
170
  return (<react_native_1.View style={localStyles.tableView}>