react-native-inapp-inspector 2.3.0 → 2.3.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.
@@ -49,6 +49,7 @@ const LogDetail_1 = __importDefault(require("./LogDetail"));
49
49
  const ConsoleTab_1 = __importDefault(require("./ConsoleTab"));
50
50
  const AnalyticsTab_1 = __importDefault(require("./AnalyticsTab"));
51
51
  const AnalyticsDetail_1 = __importDefault(require("../AnalyticsDetail"));
52
+ const SkeletonPlaceholder_1 = __importDefault(require("../SkeletonPlaceholder"));
52
53
  const ReduxTab_1 = __importDefault(require("./ReduxTab"));
53
54
  const ReduxDetail_1 = __importDefault(require("./ReduxDetail"));
54
55
  const BundleTab_1 = __importDefault(require("./BundleTab"));
@@ -64,7 +65,7 @@ const styles_1 = __importDefault(require("../../styles"));
64
65
  const AppColors_1 = require("../../styles/AppColors");
65
66
  const NavigationTracker_1 = __importDefault(require("./NavigationTracker"));
66
67
  const MainScreen = () => {
67
- const { visible, modalAnimationType, closeModal, modalHeightPercent, selected, selectedEvent, selectedLog, selectedReduxSlice, selectedReduxAction, selectedCrash, settingsPage, activeTab, isReady, isEnabled, useNativeFab, hasNavigationContext, setNavState, } = (0, InspectorContext_1.useInspector)();
68
+ const { visible, modalAnimationType, closeModal, modalHeightPercent, selected, selectedEvent, selectedLog, selectedReduxSlice, selectedReduxAction, selectedCrash, settingsPage, activeTab, isReady, enabled, useNativeFab, hasNavigationContext, setNavState, } = (0, InspectorContext_1.useInspector)();
68
69
  const isDetailActive = (activeTab === 'apis' && selected != null) ||
69
70
  (activeTab === 'analytics' && selectedEvent != null) ||
70
71
  (activeTab === 'logs' && selectedLog != null) ||
@@ -106,7 +107,7 @@ const MainScreen = () => {
106
107
  }, [activeTab]);
107
108
  return (<>
108
109
  {(react_native_1.Platform.OS === 'ios' || react_native_1.Platform.OS === 'android') &&
109
- isEnabled &&
110
+ enabled &&
110
111
  !visible &&
111
112
  !useNativeFab && <FabLauncher_1.default />}
112
113
  <react_native_1.Modal visible={visible} animationType={modalAnimationType} transparent statusBarTranslucent={true}>
@@ -126,10 +127,10 @@ const MainScreen = () => {
126
127
  <InspectorHeader_1.default />
127
128
 
128
129
  <react_native_1.View style={{ flex: 1 }}>
129
- {isReady ? (<react_native_1.View style={{ flex: 1 }}>
130
- {/* ─── Horizontal Scrollable Tab Bar inside Content ─── */}
131
- {!isDetailActive && <TabBar_1.default />}
130
+ {/* ─── Horizontal Scrollable Tab Bar inside Content (Always visible) ─── */}
131
+ {!isDetailActive && <TabBar_1.default />}
132
132
 
133
+ {isReady ? (<react_native_1.View style={{ flex: 1 }}>
133
134
  {/* Persistent List Layer - Never unmounted, preserves 100% native scroll with smooth tab transition */}
134
135
  <react_native_1.Animated.View style={[
135
136
  {
@@ -181,12 +182,7 @@ const MainScreen = () => {
181
182
  {activeTab === 'redux' && <ReduxDetail_1.default />}
182
183
  {activeTab === 'crash' && selectedCrash != null && (<CrashDetail_1.default />)}
183
184
  </react_native_1.Animated.View>)}
184
- </react_native_1.View>) : (<react_native_1.View style={styles_1.default.empty}>
185
- <react_native_1.ActivityIndicator size="large" color={AppColors_1.AppColors.purple}/>
186
- <react_native_1.Text style={[styles_1.default.emptySub, { marginTop: 12 }]}>
187
- Loading logs...
188
- </react_native_1.Text>
189
- </react_native_1.View>)}
185
+ </react_native_1.View>) : (<SkeletonPlaceholder_1.default />)}
190
186
 
191
187
  {/* Settings Panel Layer - Rendered on top with smooth slide & spring transition */}
192
188
  {settingsPage !== null && (<react_native_1.Animated.View style={[
@@ -47,6 +47,92 @@ const NetworkIcons_1 = require("../NetworkIcons");
47
47
  const storageInspector_1 = require("../../customHooks/storageInspector");
48
48
  const helpers_1 = require("../../helpers");
49
49
  const toast_1 = require("../../helpers/toast");
50
+ const getTypeBadge = (type) => {
51
+ switch (type) {
52
+ case 'json':
53
+ return { label: 'JSON', color: AppColors_1.AppColors.purple, bg: `${AppColors_1.AppColors.purple}16` };
54
+ case 'number':
55
+ return { label: 'NUM', color: AppColors_1.AppColors.warningIconGold, bg: `${AppColors_1.AppColors.warningIconGold}16` };
56
+ case 'boolean':
57
+ return { label: 'BOOL', color: AppColors_1.AppColors.emerald500, bg: `${AppColors_1.AppColors.emerald500}16` };
58
+ case 'null':
59
+ return { label: 'NULL', color: AppColors_1.AppColors.grayTextWeak, bg: `${AppColors_1.AppColors.grayTextWeak}16` };
60
+ default:
61
+ return { label: 'STR', color: AppColors_1.AppColors.blue500, bg: `${AppColors_1.AppColors.blue500}16` };
62
+ }
63
+ };
64
+ const StorageEntryCard = react_1.default.memo(function StorageEntryCard({ entry, isExpanded, onToggleExpand, onCopy, onEdit, onDelete, badge, }) {
65
+ const formattedBytes = (0, react_1.useMemo)(() => {
66
+ return entry.byteSize < 1024
67
+ ? `${entry.byteSize} B`
68
+ : `${(entry.byteSize / 1024).toFixed(1)} KB`;
69
+ }, [entry.byteSize]);
70
+ // Lazy compute displayed value to avoid layout thrashing on large JSON strings
71
+ const displayValue = (0, react_1.useMemo)(() => {
72
+ if (!isExpanded) {
73
+ if (entry.value.length > 250) {
74
+ return entry.value.slice(0, 250) + '...';
75
+ }
76
+ return entry.value;
77
+ }
78
+ if (entry.type === 'json') {
79
+ try {
80
+ const parsed = entry.parsedValue ?? JSON.parse(entry.value);
81
+ return JSON.stringify(parsed, null, 2);
82
+ }
83
+ catch {
84
+ return entry.value;
85
+ }
86
+ }
87
+ return entry.value;
88
+ }, [isExpanded, entry.value, entry.type, entry.parsedValue]);
89
+ return (<react_native_1.View style={styles.entryCard}>
90
+ {/* Entry Header: Key name, Type Badge, Size, Actions */}
91
+ <react_native_1.View style={styles.entryHeader}>
92
+ <react_native_1.View style={{ flex: 1, marginRight: 8 }}>
93
+ <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
94
+ <react_native_1.Text style={styles.entryKey} numberOfLines={1} selectable>
95
+ {entry.key}
96
+ </react_native_1.Text>
97
+ <react_native_1.View style={[
98
+ styles.typeBadge,
99
+ { backgroundColor: badge.bg, borderColor: `${badge.color}33` },
100
+ ]}>
101
+ <react_native_1.Text style={[styles.typeBadgeText, { color: badge.color }]}>
102
+ {badge.label}
103
+ </react_native_1.Text>
104
+ </react_native_1.View>
105
+ <react_native_1.Text style={styles.sizeText}>{formattedBytes}</react_native_1.Text>
106
+ </react_native_1.View>
107
+ </react_native_1.View>
108
+
109
+ {/* Actions: Copy, Edit, Delete */}
110
+ <react_native_1.View style={styles.entryActions}>
111
+ <TouchableScale_1.default hitSlop={6} onPress={() => onCopy(entry)} style={styles.entryActionBtn}>
112
+ <NetworkIcons_1.CopyIcon size={12} color={AppColors_1.AppColors.grayText}/>
113
+ </TouchableScale_1.default>
114
+
115
+ <TouchableScale_1.default hitSlop={6} onPress={() => onEdit(entry)} style={styles.entryActionBtn}>
116
+ <NetworkIcons_1.PencilIcon size={12} color={AppColors_1.AppColors.purple}/>
117
+ </TouchableScale_1.default>
118
+
119
+ <TouchableScale_1.default hitSlop={6} onPress={() => onDelete(entry.key)} style={[styles.entryActionBtn, { backgroundColor: `${AppColors_1.AppColors.errorColor}12` }]}>
120
+ <NetworkIcons_1.TrashIcon size={12} color={AppColors_1.AppColors.errorColor}/>
121
+ </TouchableScale_1.default>
122
+ </react_native_1.View>
123
+ </react_native_1.View>
124
+
125
+ {/* Entry Value Preview / Viewer */}
126
+ <TouchableScale_1.default onPress={() => onToggleExpand(entry.key)} style={styles.valuePreviewBox}>
127
+ <react_native_1.Text style={styles.valuePreviewText} numberOfLines={isExpanded ? undefined : 3} selectable={isExpanded}>
128
+ {displayValue}
129
+ </react_native_1.Text>
130
+ {entry.value.length > 80 && (<react_native_1.Text style={styles.expandHint}>
131
+ {isExpanded ? '▲ Collapse' : '▼ Expand'}
132
+ </react_native_1.Text>)}
133
+ </TouchableScale_1.default>
134
+ </react_native_1.View>);
135
+ });
50
136
  exports.StorageTab = react_1.default.memo(() => {
51
137
  const { t } = (0, i18n_1.useTranslation)();
52
138
  const [activeDriver, setActiveDriver] = (0, react_1.useState)('asyncStorage');
@@ -205,20 +291,11 @@ exports.StorageTab = react_1.default.memo(() => {
205
291
  return `${(totalBytes / 1024).toFixed(1)} KB`;
206
292
  return `${(totalBytes / (1024 * 1024)).toFixed(2)} MB`;
207
293
  }, [totalBytes]);
208
- const getTypeBadge = (type) => {
209
- switch (type) {
210
- case 'json':
211
- return { label: 'JSON', color: AppColors_1.AppColors.purple, bg: `${AppColors_1.AppColors.purple}16` };
212
- case 'number':
213
- return { label: 'NUM', color: AppColors_1.AppColors.warningIconGold, bg: `${AppColors_1.AppColors.warningIconGold}16` };
214
- case 'boolean':
215
- return { label: 'BOOL', color: AppColors_1.AppColors.emerald500, bg: `${AppColors_1.AppColors.emerald500}16` };
216
- case 'null':
217
- return { label: 'NULL', color: AppColors_1.AppColors.grayTextWeak, bg: `${AppColors_1.AppColors.grayTextWeak}16` };
218
- default:
219
- return { label: 'STR', color: AppColors_1.AppColors.blue500, bg: `${AppColors_1.AppColors.blue500}16` };
220
- }
221
- };
294
+ const handleCopyEntry = (0, react_1.useCallback)((entry) => {
295
+ (0, helpers_1.copyToClipboard)(entry.value, entry.key);
296
+ (0, toast_1.showToast)(`Copied value of "${entry.key}"`);
297
+ }, []);
298
+ const renderItem = (0, react_1.useCallback)(({ item }) => (<StorageEntryCard entry={item} isExpanded={Boolean(expandedKeys[item.key])} onToggleExpand={toggleExpand} onCopy={handleCopyEntry} onEdit={handleOpenEdit} onDelete={handleDeleteKey} badge={getTypeBadge(item.type)}/>), [expandedKeys, handleCopyEntry]);
222
299
  const isConnected = activeDriver === 'asyncStorage'
223
300
  ? (0, storageInspector_1.isAsyncStorageConnected)()
224
301
  : (0, storageInspector_1.isMMKVConnected)();
@@ -334,81 +411,19 @@ exports.StorageTab = react_1.default.memo(() => {
334
411
  {isLoading ? (<react_native_1.View style={styles.loadingContainer}>
335
412
  <react_native_1.ActivityIndicator size="small" color={AppColors_1.AppColors.purple}/>
336
413
  <react_native_1.Text style={styles.loadingText}>Loading storage entries...</react_native_1.Text>
337
- </react_native_1.View>) : filteredEntries.length === 0 ? (<react_native_1.ScrollView contentContainerStyle={styles.emptyContainer} showsVerticalScrollIndicator={false}>
338
- <react_native_1.View style={styles.emptyIconWrap}>
339
- <NetworkIcons_1.DatabaseIcon size={28} color={AppColors_1.AppColors.grayTextWeak}/>
340
- </react_native_1.View>
341
- <react_native_1.Text style={styles.emptyTitle}>
342
- {search ? 'No matching keys found' : `No ${activeDriver === 'asyncStorage' ? 'AsyncStorage' : 'MMKV'} Keys`}
343
- </react_native_1.Text>
344
- <react_native_1.Text style={styles.emptySubtitle}>
345
- {search
346
- ? 'Try modifying your search query'
347
- : `Storage is auto-detected. Tap "+ Add" above to create your first ${activeDriver === 'asyncStorage' ? 'AsyncStorage' : 'MMKV'} key!`}
348
- </react_native_1.Text>
349
- </react_native_1.ScrollView>) : (<react_native_1.ScrollView style={styles.scrollArea} contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false}>
350
- {filteredEntries.map(entry => {
351
- const isExpanded = Boolean(expandedKeys[entry.key]);
352
- const badge = getTypeBadge(entry.type);
353
- const formattedBytes = entry.byteSize < 1024
354
- ? `${entry.byteSize} B`
355
- : `${(entry.byteSize / 1024).toFixed(1)} KB`;
356
- return (<react_native_1.View key={entry.key} style={styles.entryCard}>
357
- {/* Entry Header: Key name, Type Badge, Size, Actions */}
358
- <react_native_1.View style={styles.entryHeader}>
359
- <react_native_1.View style={{ flex: 1, marginRight: 8 }}>
360
- <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
361
- <react_native_1.Text style={styles.entryKey} numberOfLines={1} selectable>
362
- {entry.key}
363
- </react_native_1.Text>
364
- <react_native_1.View style={[
365
- styles.typeBadge,
366
- { backgroundColor: badge.bg, borderColor: `${badge.color}33` },
367
- ]}>
368
- <react_native_1.Text style={[styles.typeBadgeText, { color: badge.color }]}>
369
- {badge.label}
370
- </react_native_1.Text>
371
- </react_native_1.View>
372
- <react_native_1.Text style={styles.sizeText}>{formattedBytes}</react_native_1.Text>
373
- </react_native_1.View>
374
- </react_native_1.View>
375
-
376
- {/* Actions: Copy, Edit, Delete */}
377
- <react_native_1.View style={styles.entryActions}>
378
- <TouchableScale_1.default hitSlop={6} onPress={() => {
379
- (0, helpers_1.copyToClipboard)(entry.value, entry.key);
380
- (0, toast_1.showToast)(`Copied value of "${entry.key}"`);
381
- }} style={styles.entryActionBtn}>
382
- <NetworkIcons_1.CopyIcon size={12} color={AppColors_1.AppColors.grayText}/>
383
- </TouchableScale_1.default>
384
-
385
- <TouchableScale_1.default hitSlop={6} onPress={() => handleOpenEdit(entry)} style={styles.entryActionBtn}>
386
- <NetworkIcons_1.PencilIcon size={12} color={AppColors_1.AppColors.purple}/>
387
- </TouchableScale_1.default>
388
-
389
- <TouchableScale_1.default hitSlop={6} onPress={() => handleDeleteKey(entry.key)} style={[styles.entryActionBtn, { backgroundColor: `${AppColors_1.AppColors.errorColor}12` }]}>
390
- <NetworkIcons_1.TrashIcon size={12} color={AppColors_1.AppColors.errorColor}/>
391
- </TouchableScale_1.default>
392
- </react_native_1.View>
393
- </react_native_1.View>
394
-
395
- {/* Entry Value Preview / Viewer */}
396
- <TouchableScale_1.default onPress={() => toggleExpand(entry.key)} style={styles.valuePreviewBox}>
397
- <react_native_1.Text style={styles.valuePreviewText} numberOfLines={isExpanded ? undefined : 3} selectable>
398
- {entry.type === 'json' && entry.parsedValue
399
- ? isExpanded
400
- ? JSON.stringify(entry.parsedValue, null, 2)
401
- : entry.value
402
- : entry.value}
403
- </react_native_1.Text>
404
- {entry.value.length > 80 && (<react_native_1.Text style={styles.expandHint}>
405
- {isExpanded ? '▲ Collapse' : '▼ Expand'}
406
- </react_native_1.Text>)}
407
- </TouchableScale_1.default>
408
- </react_native_1.View>);
409
- })}
410
- <react_native_1.View style={{ height: 60 }}/>
411
- </react_native_1.ScrollView>)}
414
+ </react_native_1.View>) : (<react_native_1.FlatList data={filteredEntries} keyExtractor={item => item.key} renderItem={renderItem} initialNumToRender={10} maxToRenderPerBatch={10} windowSize={5} removeClippedSubviews={react_native_1.Platform.OS === 'android'} style={styles.scrollArea} contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false} ListEmptyComponent={<react_native_1.View style={styles.emptyContainer}>
415
+ <react_native_1.View style={styles.emptyIconWrap}>
416
+ <NetworkIcons_1.DatabaseIcon size={28} color={AppColors_1.AppColors.grayTextWeak}/>
417
+ </react_native_1.View>
418
+ <react_native_1.Text style={styles.emptyTitle}>
419
+ {search ? 'No matching keys found' : `No ${activeDriver === 'asyncStorage' ? 'AsyncStorage' : 'MMKV'} Keys`}
420
+ </react_native_1.Text>
421
+ <react_native_1.Text style={styles.emptySubtitle}>
422
+ {search
423
+ ? 'Try modifying your search query'
424
+ : `Storage is auto-detected. Tap "+ Add" above to create your first ${activeDriver === 'asyncStorage' ? 'AsyncStorage' : 'MMKV'} key!`}
425
+ </react_native_1.Text>
426
+ </react_native_1.View>} ListFooterComponent={<react_native_1.View style={{ height: 60 }}/>}/>)}
412
427
 
413
428
  {/* ── Create / Edit Key Modal ── */}
414
429
  <react_native_1.Modal visible={modalVisible} transparent animationType="fade" onRequestClose={() => setModalVisible(false)}>
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ interface SkeletonPlaceholderProps {
3
+ cardCount?: number;
4
+ }
5
+ declare const SkeletonPlaceholder: React.NamedExoticComponent<SkeletonPlaceholderProps>;
6
+ export default SkeletonPlaceholder;
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const react_1 = __importStar(require("react"));
37
+ const react_native_1 = require("react-native");
38
+ const AppColors_1 = require("../styles/AppColors");
39
+ const SkeletonPlaceholder = react_1.default.memo(function SkeletonPlaceholder({ cardCount = 4, }) {
40
+ const shimmerAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(0.35)).current;
41
+ (0, react_1.useEffect)(() => {
42
+ const animation = react_native_1.Animated.loop(react_native_1.Animated.sequence([
43
+ react_native_1.Animated.timing(shimmerAnim, {
44
+ toValue: 0.85,
45
+ duration: 750,
46
+ useNativeDriver: true,
47
+ }),
48
+ react_native_1.Animated.timing(shimmerAnim, {
49
+ toValue: 0.35,
50
+ duration: 750,
51
+ useNativeDriver: true,
52
+ }),
53
+ ]));
54
+ animation.start();
55
+ return () => animation.stop();
56
+ }, [shimmerAnim]);
57
+ return (<react_native_1.View style={skeletonStyles.container}>
58
+ {/* ─── Search & Scope Toolbar Skeleton ─── */}
59
+ <react_native_1.View style={skeletonStyles.toolbarSkeleton}>
60
+ <react_native_1.Animated.View style={[
61
+ skeletonStyles.searchBarSkeleton,
62
+ { opacity: shimmerAnim },
63
+ ]}/>
64
+ <react_native_1.View style={skeletonStyles.actionButtonsRow}>
65
+ <react_native_1.Animated.View style={[skeletonStyles.iconButtonSkeleton, { opacity: shimmerAnim }]}/>
66
+ <react_native_1.Animated.View style={[skeletonStyles.iconButtonSkeleton, { opacity: shimmerAnim }]}/>
67
+ </react_native_1.View>
68
+ </react_native_1.View>
69
+
70
+ {/* ─── Quick Filter Chips Skeleton Strip ─── */}
71
+ <react_native_1.View style={skeletonStyles.chipStripSkeleton}>
72
+ <react_native_1.Animated.View style={[skeletonStyles.chipSkeleton, { width: 48, opacity: shimmerAnim }]}/>
73
+ <react_native_1.Animated.View style={[skeletonStyles.chipSkeleton, { width: 68, opacity: shimmerAnim }]}/>
74
+ <react_native_1.Animated.View style={[skeletonStyles.chipSkeleton, { width: 76, opacity: shimmerAnim }]}/>
75
+ <react_native_1.Animated.View style={[skeletonStyles.chipSkeleton, { width: 58, opacity: shimmerAnim }]}/>
76
+ </react_native_1.View>
77
+
78
+ {/* ─── List Cards Skeleton ─── */}
79
+ {Array.from({ length: cardCount }).map((_, i) => (<react_native_1.Animated.View key={`skeleton_card_${i}`} style={[skeletonStyles.cardSkeleton, { opacity: shimmerAnim }]}>
80
+ {/* Top row: Status pill + Method + Time */}
81
+ <react_native_1.View style={skeletonStyles.cardTopRow}>
82
+ <react_native_1.View style={skeletonStyles.badgeGroup}>
83
+ <react_native_1.View style={skeletonStyles.statusBadgeSkeleton}/>
84
+ <react_native_1.View style={skeletonStyles.methodBadgeSkeleton}/>
85
+ </react_native_1.View>
86
+ <react_native_1.View style={skeletonStyles.timeSkeleton}/>
87
+ </react_native_1.View>
88
+
89
+ {/* Middle row: URL lines */}
90
+ <react_native_1.View style={skeletonStyles.urlLineLong}/>
91
+ <react_native_1.View style={skeletonStyles.urlLineShort}/>
92
+
93
+ {/* Bottom row: Latency & Size */}
94
+ <react_native_1.View style={skeletonStyles.cardBottomRow}>
95
+ <react_native_1.View style={skeletonStyles.metaPillSkeleton}/>
96
+ <react_native_1.View style={skeletonStyles.metaPillSkeleton}/>
97
+ </react_native_1.View>
98
+ </react_native_1.Animated.View>))}
99
+ </react_native_1.View>);
100
+ });
101
+ const skeletonStyles = react_native_1.StyleSheet.create({
102
+ container: {
103
+ flex: 1,
104
+ paddingHorizontal: 12,
105
+ paddingTop: 8,
106
+ },
107
+ toolbarSkeleton: {
108
+ flexDirection: 'row',
109
+ alignItems: 'center',
110
+ gap: 8,
111
+ marginBottom: 8,
112
+ },
113
+ searchBarSkeleton: {
114
+ flex: 1,
115
+ height: 36,
116
+ borderRadius: 8,
117
+ backgroundColor: AppColors_1.AppColors.graySurface,
118
+ borderWidth: 1,
119
+ borderColor: AppColors_1.AppColors.dividerColor,
120
+ },
121
+ actionButtonsRow: {
122
+ flexDirection: 'row',
123
+ gap: 6,
124
+ },
125
+ iconButtonSkeleton: {
126
+ width: 36,
127
+ height: 36,
128
+ borderRadius: 8,
129
+ backgroundColor: AppColors_1.AppColors.graySurface,
130
+ borderWidth: 1,
131
+ borderColor: AppColors_1.AppColors.dividerColor,
132
+ },
133
+ chipStripSkeleton: {
134
+ flexDirection: 'row',
135
+ gap: 6,
136
+ marginBottom: 10,
137
+ },
138
+ chipSkeleton: {
139
+ height: 24,
140
+ borderRadius: 6,
141
+ backgroundColor: AppColors_1.AppColors.graySurface,
142
+ borderWidth: 1,
143
+ borderColor: AppColors_1.AppColors.dividerColor,
144
+ },
145
+ cardSkeleton: {
146
+ backgroundColor: AppColors_1.AppColors.primaryLight,
147
+ borderRadius: 10,
148
+ padding: 12,
149
+ marginBottom: 8,
150
+ borderWidth: 1,
151
+ borderColor: AppColors_1.AppColors.dividerColor,
152
+ },
153
+ cardTopRow: {
154
+ flexDirection: 'row',
155
+ justifyContent: 'space-between',
156
+ alignItems: 'center',
157
+ marginBottom: 8,
158
+ },
159
+ badgeGroup: {
160
+ flexDirection: 'row',
161
+ alignItems: 'center',
162
+ gap: 6,
163
+ },
164
+ statusBadgeSkeleton: {
165
+ width: 38,
166
+ height: 18,
167
+ borderRadius: 4,
168
+ backgroundColor: AppColors_1.AppColors.graySurface,
169
+ },
170
+ methodBadgeSkeleton: {
171
+ width: 44,
172
+ height: 18,
173
+ borderRadius: 4,
174
+ backgroundColor: AppColors_1.AppColors.graySurface,
175
+ },
176
+ timeSkeleton: {
177
+ width: 48,
178
+ height: 12,
179
+ borderRadius: 4,
180
+ backgroundColor: AppColors_1.AppColors.graySurface,
181
+ },
182
+ urlLineLong: {
183
+ height: 13,
184
+ borderRadius: 4,
185
+ backgroundColor: AppColors_1.AppColors.graySurface,
186
+ marginBottom: 5,
187
+ width: '90%',
188
+ },
189
+ urlLineShort: {
190
+ height: 11,
191
+ borderRadius: 4,
192
+ backgroundColor: AppColors_1.AppColors.graySurface,
193
+ marginBottom: 8,
194
+ width: '55%',
195
+ },
196
+ cardBottomRow: {
197
+ flexDirection: 'row',
198
+ gap: 8,
199
+ marginTop: 2,
200
+ },
201
+ metaPillSkeleton: {
202
+ width: 52,
203
+ height: 14,
204
+ borderRadius: 4,
205
+ backgroundColor: AppColors_1.AppColors.graySurface,
206
+ },
207
+ });
208
+ exports.default = SkeletonPlaceholder;
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "2.3.0";
1
+ export declare const LIB_VERSION = "2.3.1";
@@ -3,4 +3,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LIB_VERSION = void 0;
4
4
  // AUTO-GENERATED FILE — do not edit by hand.
5
5
  // Regenerated from package.json on every build by scripts/gen-version.js.
6
- exports.LIB_VERSION = '2.3.0';
6
+ exports.LIB_VERSION = '2.3.1';
@@ -37,11 +37,11 @@ export declare const getRegisteredMMKVInstanceIds: () => string[];
37
37
  */
38
38
  export declare const calculateByteSize: (str: string) => number;
39
39
  /**
40
- * Determine type and format value safely
40
+ * Determine type and format value safely without blocking JS thread
41
41
  */
42
42
  export declare const analyzeStorageValue: (rawVal: any) => {
43
43
  strVal: string;
44
- parsedVal?: any;
44
+ parsedValue?: any;
45
45
  type: "json" | "string" | "number" | "boolean" | "null";
46
46
  byteSize: number;
47
47
  };
@@ -266,34 +266,38 @@ const calculateByteSize = (str) => {
266
266
  };
267
267
  exports.calculateByteSize = calculateByteSize;
268
268
  /**
269
- * Determine type and format value safely
269
+ * Determine type and format value safely without blocking JS thread
270
270
  */
271
271
  const analyzeStorageValue = (rawVal) => {
272
272
  if (rawVal === null || rawVal === undefined) {
273
273
  return { strVal: 'null', type: 'null', byteSize: 4 };
274
274
  }
275
275
  const strVal = typeof rawVal === 'string' ? rawVal : String(rawVal);
276
- const byteSize = (0, exports.calculateByteSize)(strVal);
276
+ const byteSize = strVal.length * 2;
277
277
  if (typeof rawVal === 'number') {
278
- return { strVal, parsedVal: rawVal, type: 'number', byteSize };
278
+ return { strVal, parsedValue: rawVal, type: 'number', byteSize };
279
279
  }
280
280
  if (typeof rawVal === 'boolean') {
281
- return { strVal, parsedVal: rawVal, type: 'boolean', byteSize };
281
+ return { strVal, parsedValue: rawVal, type: 'boolean', byteSize };
282
282
  }
283
283
  const trimmed = strVal.trim();
284
284
  if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
285
285
  (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
286
- try {
287
- const parsed = JSON.parse(trimmed);
288
- return { strVal, parsedVal: parsed, type: 'json', byteSize };
286
+ // For small/medium JSON, parse eagerly; for huge payloads (>10KB), parse lazily on expand
287
+ if (trimmed.length < 10000) {
288
+ try {
289
+ const parsed = JSON.parse(trimmed);
290
+ return { strVal, parsedValue: parsed, type: 'json', byteSize };
291
+ }
292
+ catch { }
289
293
  }
290
- catch { }
294
+ return { strVal, type: 'json', byteSize };
291
295
  }
292
296
  if (trimmed === 'true' || trimmed === 'false') {
293
- return { strVal, parsedVal: trimmed === 'true', type: 'boolean', byteSize };
297
+ return { strVal, parsedValue: trimmed === 'true', type: 'boolean', byteSize };
294
298
  }
295
299
  if (!isNaN(Number(trimmed)) && trimmed !== '') {
296
- return { strVal, parsedVal: Number(trimmed), type: 'number', byteSize };
300
+ return { strVal, parsedValue: Number(trimmed), type: 'number', byteSize };
297
301
  }
298
302
  return { strVal, type: 'string', byteSize };
299
303
  };
@@ -319,7 +323,7 @@ const fetchStorageEntries = async (driver, instanceId) => {
319
323
  entries.push({
320
324
  key: k,
321
325
  value: analysis.strVal,
322
- parsedValue: analysis.parsedVal,
326
+ parsedValue: analysis.parsedValue,
323
327
  type: analysis.type,
324
328
  byteSize: analysis.byteSize,
325
329
  });
@@ -332,7 +336,7 @@ const fetchStorageEntries = async (driver, instanceId) => {
332
336
  entries.push({
333
337
  key: k,
334
338
  value: analysis.strVal,
335
- parsedValue: analysis.parsedVal,
339
+ parsedValue: analysis.parsedValue,
336
340
  type: analysis.type,
337
341
  byteSize: analysis.byteSize,
338
342
  });
@@ -373,7 +377,7 @@ const fetchStorageEntries = async (driver, instanceId) => {
373
377
  entries.push({
374
378
  key: k,
375
379
  value: analysis.strVal,
376
- parsedValue: analysis.parsedVal,
380
+ parsedValue: analysis.parsedValue,
377
381
  type: analysis.type,
378
382
  byteSize: analysis.byteSize,
379
383
  });