react-native-inapp-inspector 1.1.29 → 1.1.32

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 (51) hide show
  1. package/dist/commonjs/components/AnalyticsDetail.js +668 -108
  2. package/dist/commonjs/components/AppHeaderLogo.js +5 -2
  3. package/dist/commonjs/components/ConsoleLogCard.js +81 -59
  4. package/dist/commonjs/components/DomainHeader.js +21 -17
  5. package/dist/commonjs/components/Inspector/AnalyticsTab.js +297 -200
  6. package/dist/commonjs/components/Inspector/ConsoleTab.js +162 -56
  7. package/dist/commonjs/components/Inspector/InspectorHeader.js +56 -119
  8. package/dist/commonjs/components/Inspector/LogDetail.js +45 -12
  9. package/dist/commonjs/components/Inspector/NetworkTab.js +4 -1
  10. package/dist/commonjs/components/Inspector/ReduxDetail.js +237 -42
  11. package/dist/commonjs/components/Inspector/ReduxTab.js +456 -35
  12. package/dist/commonjs/components/JsonViewer.js +1 -1
  13. package/dist/commonjs/components/NetworkIcons.js +19 -10
  14. package/dist/commonjs/components/TreeNode.js +12 -2
  15. package/dist/commonjs/constants/version.d.ts +1 -1
  16. package/dist/commonjs/constants/version.js +1 -1
  17. package/dist/commonjs/customHooks/reduxLogger.js +128 -5
  18. package/dist/commonjs/helpers/index.d.ts +1 -1
  19. package/dist/commonjs/helpers/index.js +32 -16
  20. package/dist/commonjs/i18n/locales/en.json +45 -2
  21. package/dist/commonjs/styles/AppColors.d.ts +6 -0
  22. package/dist/commonjs/styles/AppColors.js +6 -0
  23. package/dist/commonjs/styles/index.d.ts +22 -1
  24. package/dist/commonjs/styles/index.js +25 -4
  25. package/dist/commonjs/types/interfaces.d.ts +7 -0
  26. package/dist/esm/components/AnalyticsDetail.js +670 -110
  27. package/dist/esm/components/AppHeaderLogo.js +5 -2
  28. package/dist/esm/components/ConsoleLogCard.js +81 -59
  29. package/dist/esm/components/DomainHeader.js +21 -17
  30. package/dist/esm/components/Inspector/AnalyticsTab.js +298 -201
  31. package/dist/esm/components/Inspector/ConsoleTab.js +163 -57
  32. package/dist/esm/components/Inspector/InspectorHeader.js +57 -120
  33. package/dist/esm/components/Inspector/LogDetail.js +45 -12
  34. package/dist/esm/components/Inspector/NetworkTab.js +4 -1
  35. package/dist/esm/components/Inspector/ReduxDetail.js +239 -44
  36. package/dist/esm/components/Inspector/ReduxTab.js +458 -37
  37. package/dist/esm/components/JsonViewer.js +1 -1
  38. package/dist/esm/components/NetworkIcons.js +19 -10
  39. package/dist/esm/components/TreeNode.js +12 -2
  40. package/dist/esm/constants/version.d.ts +1 -1
  41. package/dist/esm/constants/version.js +1 -1
  42. package/dist/esm/customHooks/reduxLogger.js +128 -5
  43. package/dist/esm/helpers/index.d.ts +1 -1
  44. package/dist/esm/helpers/index.js +32 -16
  45. package/dist/esm/i18n/locales/en.json +45 -2
  46. package/dist/esm/styles/AppColors.d.ts +6 -0
  47. package/dist/esm/styles/AppColors.js +6 -0
  48. package/dist/esm/styles/index.d.ts +22 -1
  49. package/dist/esm/styles/index.js +25 -4
  50. package/dist/esm/types/interfaces.d.ts +7 -0
  51. package/package.json +11 -11
@@ -80,27 +80,60 @@ const LogDetail = React.memo(() => {
80
80
  return null;
81
81
  const isDark = AppColors.primaryLight !== AppColors.white;
82
82
  const getTypeColors = () => {
83
- if (selectedLog.type === 'error') {
83
+ const type = (selectedLog.type || 'log').toLowerCase();
84
+ const method = (selectedLog.sourceMethod || type).toLowerCase();
85
+ if (type === 'error' || method === 'error') {
84
86
  return {
85
- border: AppColors.errorColor,
86
- badgeBg: `${AppColors.errorColor}15`,
87
- badgeText: AppColors.errorColor,
87
+ border: AppColors.red500,
88
+ badgeBg: AppColors.red100,
89
+ badgeText: AppColors.redErrorText,
88
90
  label: 'ERROR',
91
+ methodBorder: AppColors.errorBorder,
92
+ methodBg: AppColors.red100,
93
+ methodText: AppColors.redErrorText,
89
94
  };
90
95
  }
91
- if (selectedLog.type === 'warn') {
96
+ if (type === 'warn' || method === 'warn') {
92
97
  return {
93
- border: AppColors.lightOrange,
94
- badgeBg: `${AppColors.lightOrange}15`,
95
- badgeText: AppColors.darkOrange || AppColors.lightOrange,
98
+ border: AppColors.amber500,
99
+ badgeBg: AppColors.amber100,
100
+ badgeText: AppColors.amber800Warm,
96
101
  label: 'WARN',
102
+ methodBorder: AppColors.amber200,
103
+ methodBg: AppColors.amber100,
104
+ methodText: AppColors.amber800Warm,
105
+ };
106
+ }
107
+ if (type === 'debug' || method === 'debug') {
108
+ return {
109
+ border: AppColors.violet500,
110
+ badgeBg: AppColors.purple100,
111
+ badgeText: AppColors.violet600,
112
+ label: 'DEBUG',
113
+ methodBorder: AppColors.purple200,
114
+ methodBg: AppColors.purple100,
115
+ methodText: AppColors.violet600,
116
+ };
117
+ }
118
+ if (type === 'info' || method === 'info') {
119
+ return {
120
+ border: AppColors.sky500,
121
+ badgeBg: AppColors.sky100,
122
+ badgeText: AppColors.sky600,
123
+ label: 'INFO',
124
+ methodBorder: AppColors.sky400,
125
+ methodBg: AppColors.sky100,
126
+ methodText: AppColors.sky600,
97
127
  };
98
128
  }
99
129
  return {
100
- border: AppColors.purple,
101
- badgeBg: `${AppColors.purple}15`,
102
- badgeText: AppColors.purple,
103
- label: 'INFO',
130
+ border: AppColors.indigo500,
131
+ badgeBg: AppColors.indigo50,
132
+ badgeText: AppColors.indigo600Alt,
133
+ label: 'LOG',
134
+ methodBorder: AppColors.indigo400,
135
+ methodBg: AppColors.indigo50,
136
+ methodText: AppColors.indigo600Alt,
104
137
  };
105
138
  };
106
139
  const typeColors = getTypeColors();
@@ -67,7 +67,10 @@ const NetworkTab = React.memo(() => {
67
67
  </AnimatedEntrance>);
68
68
  }
69
69
  const { log, isLast, color } = item;
70
- return (<AnimatedEntrance index={index} distance={8} style={styles.treeNodeRow}>
70
+ return (<AnimatedEntrance index={index} distance={8} style={[
71
+ styles.treeNodeRow,
72
+ isLast && styles.treeNodeRowLast,
73
+ ]}>
71
74
  <View style={styles.treeLines}>
72
75
  <View style={[
73
76
  styles.modernTreeLine,
@@ -10,11 +10,55 @@ import TouchableScale from '../TouchableScale';
10
10
  import HighlightText from '../HighlightText';
11
11
  import AnimatedEntrance from '../AnimatedEntrance';
12
12
  import { getActionHistory } from '../../customHooks/reduxLogger';
13
- import { getSize } from '../../helpers';
13
+ import { getSize, openInVSCode, parseStackLine } from '../../helpers';
14
14
  import { getCustomStorage } from '../../helpers/settingsStore';
15
15
  import { AppColors } from '../../styles/AppColors';
16
16
  import { AppFonts } from '../../styles/AppFonts';
17
- import { SearchIcon, ClearIcon, ClockIcon, TrashIcon, ForwardChevronIcon, LiveStateIcon, TimelineIcon, StorageIcon, MetadataIcon, } from '../NetworkIcons';
17
+ import { SearchIcon, ClearIcon, ClockIcon, LayersIcon, TrashIcon, ForwardChevronIcon, LiveStateIcon, TimelineIcon, StorageIcon, MetadataIcon, BoltIcon, ExternalLinkIcon, } from '../NetworkIcons';
18
+ const getOriginBadge = (originType) => {
19
+ switch (originType) {
20
+ case 'saga':
21
+ return {
22
+ label: 'SAGA',
23
+ icon: '⚡',
24
+ bg: AppColors.purple100,
25
+ text: AppColors.brandPurple,
26
+ border: AppColors.purple200,
27
+ };
28
+ case 'thunk':
29
+ return {
30
+ label: 'THUNK',
31
+ icon: '⚛️',
32
+ bg: AppColors.amber100,
33
+ text: AppColors.amber800Warm,
34
+ border: AppColors.amber200,
35
+ };
36
+ case 'ui':
37
+ return {
38
+ label: 'UI',
39
+ icon: '📱',
40
+ bg: AppColors.sky100,
41
+ text: AppColors.sky600,
42
+ border: AppColors.sky400,
43
+ };
44
+ case 'listener':
45
+ return {
46
+ label: 'LISTENER',
47
+ icon: '👂',
48
+ bg: AppColors.teal100,
49
+ text: AppColors.teal700,
50
+ border: AppColors.teal400,
51
+ };
52
+ default:
53
+ return {
54
+ label: 'DIRECT',
55
+ icon: '⚡',
56
+ bg: AppColors.slate100,
57
+ text: AppColors.slate700,
58
+ border: AppColors.slate200,
59
+ };
60
+ }
61
+ };
18
62
  const ReduxDetail = React.memo(() => {
19
63
  const { t } = useTranslation();
20
64
  const { reduxState, reduxLastActionMap, selectedReduxSlice, selectedReduxAction, setSelectedReduxSlice, setSelectedReduxAction, } = useInspector();
@@ -136,29 +180,46 @@ const ReduxDetail = React.memo(() => {
136
180
  : 0;
137
181
  const lastAction = reduxLastActionMap[selectedReduxSlice];
138
182
  const sliceTabs = [
139
- { key: 'live', label: 'Live State', icon: () => <LiveStateIcon color={AppColors.purple} size={11}/> },
140
- { key: 'timeline', label: `Timeline (${sliceActions.length})`, icon: () => <TimelineIcon color={AppColors.purple} size={11}/> },
141
- { key: 'persisted', label: isPersisted ? 'Persisted' : 'Storage', icon: () => <StorageIcon color={AppColors.purple} size={11}/> },
142
- { key: 'metadata', label: 'Metadata', icon: () => <MetadataIcon color={AppColors.purple} size={11}/> },
183
+ {
184
+ key: 'live',
185
+ label: t('redux.liveState'),
186
+ icon: (isActive) => (<LiveStateIcon color={isActive ? AppColors.white : AppColors.grayText} size={12}/>),
187
+ },
188
+ {
189
+ key: 'timeline',
190
+ label: `${t('redux.timeline')} (${sliceActions.length})`,
191
+ icon: (isActive) => (<TimelineIcon color={isActive ? AppColors.white : AppColors.grayText} size={12}/>),
192
+ },
193
+ {
194
+ key: 'persisted',
195
+ label: isPersisted ? t('redux.persisted') : t('redux.storage'),
196
+ icon: (isActive) => (<StorageIcon color={isActive ? AppColors.white : AppColors.grayText} size={12}/>),
197
+ },
198
+ {
199
+ key: 'metadata',
200
+ label: t('redux.metadata'),
201
+ icon: (isActive) => (<MetadataIcon color={isActive ? AppColors.white : AppColors.grayText} size={12}/>),
202
+ },
143
203
  ];
144
204
  return (<View style={{ flex: 1, backgroundColor: AppColors.grayBackground }}>
145
205
  {/* Info Header Bar */}
146
206
  <View style={reduxDetailStyles.infoBar}>
147
207
  <View style={reduxDetailStyles.infoTopRow}>
148
208
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: 'wrap', flex: 1 }}>
149
- <View style={[reduxDetailStyles.methodBadge, { backgroundColor: AppColors.purple }]}>
150
- <Text style={reduxDetailStyles.methodBadgeText}>SLICE</Text>
209
+ <View style={[reduxDetailStyles.methodBadge, { backgroundColor: AppColors.indigo600Alt }]}>
210
+ <Text style={reduxDetailStyles.methodBadgeText}>{t('redux.slice')}</Text>
151
211
  </View>
152
212
  <Text style={reduxDetailStyles.sliceNameText}>
153
213
  {selectedReduxSlice}
154
214
  </Text>
155
215
  {isPersisted ? (<View style={reduxDetailStyles.persistedChip}>
156
- <Text style={reduxDetailStyles.persistedChipText}>PERSISTED</Text>
216
+ <StorageIcon color={AppColors.emerald700} size={10}/>
217
+ <Text style={reduxDetailStyles.persistedChipText}>{t('redux.persisted').toUpperCase()}</Text>
157
218
  </View>) : (<View style={reduxDetailStyles.inMemoryChip}>
158
- <Text style={reduxDetailStyles.inMemoryChipText}>IN-MEMORY</Text>
219
+ <Text style={reduxDetailStyles.inMemoryChipText}>{t('redux.inMemory').toUpperCase()}</Text>
159
220
  </View>)}
160
221
  {lastAction?.timestamp && (<View style={reduxDetailStyles.timePill}>
161
- <ClockIcon color={AppColors.purple} size={10}/>
222
+ <ClockIcon color={AppColors.violet600} size={10}/>
162
223
  <Text style={reduxDetailStyles.timeText}>
163
224
  {lastAction.timestamp}
164
225
  </Text>
@@ -166,26 +227,29 @@ const ReduxDetail = React.memo(() => {
166
227
  </View>
167
228
 
168
229
  {/* Copy Button */}
169
- <CopyButton value={() => (sliceTab === 'live' ? sliceData : persistedData)} label="Slice JSON"/>
230
+ <CopyButton value={() => (sliceTab === 'live' ? sliceData : persistedData)} label={t('redux.sliceJson')}/>
170
231
  </View>
171
232
 
172
233
  {/* Sub Stats Row */}
173
234
  <View style={reduxDetailStyles.subStatsRow}>
174
- <View style={reduxDetailStyles.statPill}>
175
- <Text style={reduxDetailStyles.statLabel}>Root Keys:</Text>
176
- <Text style={reduxDetailStyles.statValue}>{fieldsCount}</Text>
235
+ <View style={[reduxDetailStyles.statPill, { backgroundColor: AppColors.indigo50, borderColor: AppColors.indigo400 }]}>
236
+ <LayersIcon color={AppColors.indigo600Alt} size={11}/>
237
+ <Text style={[reduxDetailStyles.statLabel, { color: AppColors.indigo600Alt }]}>{t('redux.keys')}:</Text>
238
+ <Text style={[reduxDetailStyles.statValue, { color: AppColors.indigo600Alt }]}>{fieldsCount}</Text>
177
239
  </View>
178
- <View style={reduxDetailStyles.statPill}>
179
- <Text style={reduxDetailStyles.statLabel}>Size:</Text>
180
- <Text style={reduxDetailStyles.statValue}>{getSize(sliceData)}</Text>
240
+ <View style={[reduxDetailStyles.statPill, { backgroundColor: AppColors.sky100, borderColor: AppColors.sky400 }]}>
241
+ <Text style={[reduxDetailStyles.statLabel, { color: AppColors.sky600 }]}>{t('redux.size')}:</Text>
242
+ <Text style={[reduxDetailStyles.statValue, { color: AppColors.sky600 }]}>{getSize(sliceData)}</Text>
181
243
  </View>
182
- <View style={reduxDetailStyles.statPill}>
183
- <Text style={reduxDetailStyles.statLabel}>Timeline:</Text>
184
- <Text style={reduxDetailStyles.statValue}>{sliceActions.length} actions</Text>
244
+ <View style={[reduxDetailStyles.statPill, { backgroundColor: AppColors.purple100, borderColor: AppColors.purple200 }]}>
245
+ <TimelineIcon color={AppColors.violet600} size={11}/>
246
+ <Text style={[reduxDetailStyles.statLabel, { color: AppColors.brandPurple }]}>{t('redux.timeline')}:</Text>
247
+ <Text style={[reduxDetailStyles.statValue, { color: AppColors.brandPurple }]}>{sliceActions.length}</Text>
185
248
  </View>
186
- {lastAction && (<View style={[reduxDetailStyles.statPill, { flex: 1, minWidth: 140 }]}>
187
- <Text style={reduxDetailStyles.statLabel}>Last Action:</Text>
188
- <Text style={reduxDetailStyles.statValue} numberOfLines={1}>
249
+ {lastAction && (<View style={[reduxDetailStyles.statPill, { backgroundColor: AppColors.amber100, borderColor: AppColors.amber200, flex: 1, minWidth: 140 }]}>
250
+ <BoltIcon color={AppColors.amber800Warm} size={11}/>
251
+ <Text style={[reduxDetailStyles.statLabel, { color: AppColors.amber800Warm }]}>{t('redux.last')}:</Text>
252
+ <Text style={[reduxDetailStyles.statValue, { color: AppColors.amber800Warm }]} numberOfLines={1}>
189
253
  {lastAction.type}
190
254
  </Text>
191
255
  </View>)}
@@ -212,15 +276,45 @@ const ReduxDetail = React.memo(() => {
212
276
  </Text>
213
277
  </View>) : (sliceActions.map((action, aIdx) => {
214
278
  const isExpanded = expandedActionId === action.id;
279
+ const originMeta = getOriginBadge(action.originType);
280
+ const callerBasename = action.callerFile
281
+ ? action.callerFile.split('/').pop()
282
+ : null;
215
283
  return (<AnimatedEntrance key={action.id} index={aIdx} distance={6}>
216
284
  <TouchableScale onPress={() => setExpandedActionId(isExpanded ? null : action.id)} style={reduxDetailStyles.timelineCard}>
217
285
  <View style={reduxDetailStyles.timelineHeader}>
218
- <View style={reduxDetailStyles.actionBadge}>
219
- <Text style={reduxDetailStyles.actionBadgeText}>
220
- #{action.id}
221
- </Text>
286
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flex: 1, minWidth: 0, flexWrap: 'wrap' }}>
287
+ <View style={reduxDetailStyles.actionBadge}>
288
+ <Text style={reduxDetailStyles.actionBadgeText}>
289
+ #{action.id}
290
+ </Text>
291
+ </View>
292
+ {/* Origin Badge */}
293
+ <View style={{
294
+ flexDirection: 'row',
295
+ alignItems: 'center',
296
+ gap: 3,
297
+ backgroundColor: originMeta.bg,
298
+ paddingHorizontal: 6,
299
+ paddingVertical: 2,
300
+ borderRadius: 4,
301
+ borderWidth: 1,
302
+ borderColor: originMeta.border,
303
+ }}>
304
+ <Text style={{ fontSize: 9 }}>{originMeta.icon}</Text>
305
+ <Text style={{
306
+ fontFamily: AppFonts.interBold,
307
+ fontSize: 8.5,
308
+ color: originMeta.text,
309
+ letterSpacing: 0.3,
310
+ }}>
311
+ {originMeta.label}
312
+ </Text>
313
+ </View>
314
+
315
+ <HighlightText text={action.type} search={detailSearch} style={reduxDetailStyles.actionTitle} highlightStyle={reduxDetailStyles.highlight}/>
222
316
  </View>
223
- <HighlightText text={action.type} search={detailSearch} style={reduxDetailStyles.actionTitle} highlightStyle={reduxDetailStyles.highlight}/>
317
+
224
318
  <View style={reduxDetailStyles.timePill}>
225
319
  <ClockIcon color={AppColors.purple} size={10}/>
226
320
  <Text style={reduxDetailStyles.timeText}>
@@ -229,22 +323,120 @@ const ReduxDetail = React.memo(() => {
229
323
  </View>
230
324
  </View>
231
325
 
232
- {/* Expanded Payload & Diff Viewer */}
326
+ {/* Trigger caller preview row */}
327
+ {action.callerFile && (<View style={{
328
+ flexDirection: 'row',
329
+ alignItems: 'center',
330
+ justifyContent: 'space-between',
331
+ marginTop: 5,
332
+ marginBottom: 2,
333
+ paddingHorizontal: 7,
334
+ paddingVertical: 3,
335
+ backgroundColor: AppColors.grayBackground,
336
+ borderRadius: 5,
337
+ borderWidth: 1,
338
+ borderColor: AppColors.dividerColor,
339
+ }}>
340
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4, flex: 1, minWidth: 0 }}>
341
+ <Text style={{ fontFamily: AppFonts.interMedium, fontSize: 9.5, color: AppColors.grayTextWeak }}>
342
+ {t('redux.triggeredFrom')}
343
+ </Text>
344
+ <Text style={{
345
+ fontFamily: AppFonts.interBold,
346
+ fontSize: 9.5,
347
+ color: AppColors.sky600,
348
+ }} numberOfLines={1}>
349
+ {callerBasename}:{action.callerLine || 1}
350
+ </Text>
351
+ </View>
352
+
353
+ <Pressable onPress={() => openInVSCode(action.callerFile, action.callerLine, action.callerCol)} hitSlop={8} style={{
354
+ flexDirection: 'row',
355
+ alignItems: 'center',
356
+ gap: 3,
357
+ backgroundColor: AppColors.sky100,
358
+ paddingHorizontal: 5,
359
+ paddingVertical: 1.5,
360
+ borderRadius: 3,
361
+ }}>
362
+ <ExternalLinkIcon color={AppColors.sky600} size={9}/>
363
+ <Text style={{ fontFamily: AppFonts.interBold, fontSize: 8.5, color: AppColors.sky600 }}>
364
+ {t('redux.openInEditor')}
365
+ </Text>
366
+ </Pressable>
367
+ </View>)}
368
+
369
+ {/* Expanded Payload, Diff Viewer & Call Stack */}
233
370
  {isExpanded ? (<View style={reduxDetailStyles.expandedContent}>
371
+ {/* Call Stack Trace Box */}
372
+ {action.stack ? (<View style={{ marginBottom: 10 }}>
373
+ <Text style={reduxDetailStyles.expandedSectionTitle}>
374
+ {t('redux.callStack')}:
375
+ </Text>
376
+ <View style={{
377
+ backgroundColor: AppColors.grayBackground,
378
+ borderRadius: 8,
379
+ padding: 8,
380
+ borderWidth: 1,
381
+ borderColor: AppColors.dividerColor,
382
+ gap: 4,
383
+ }}>
384
+ {action.stack
385
+ .split('\n')
386
+ .map(l => l.trim())
387
+ .filter(l => l.length > 0 && !l.includes('reduxLogger') && !l.includes('inspectorReduxMiddleware'))
388
+ .slice(0, 8)
389
+ .map((frameLine, fIdx) => {
390
+ const parsed = parseStackLine(frameLine, fIdx === 0);
391
+ const isApp = parsed.frameType === 'app';
392
+ return (<Pressable key={fIdx} onPress={() => {
393
+ if (parsed.fileName !== 'Unknown') {
394
+ openInVSCode(parsed.fullPath || parsed.fileName, parsed.lineNumber, parsed.columnNumber);
395
+ }
396
+ }} style={{
397
+ flexDirection: 'row',
398
+ alignItems: 'center',
399
+ justifyContent: 'space-between',
400
+ paddingVertical: 2.5,
401
+ borderBottomWidth: fIdx < 7 ? 0.5 : 0,
402
+ borderBottomColor: AppColors.dividerColor,
403
+ }}>
404
+ <View style={{ flex: 1, minWidth: 0 }}>
405
+ <Text style={{
406
+ fontFamily: isApp ? AppFonts.interBold : AppFonts.interRegular,
407
+ fontSize: 9.5,
408
+ color: isApp ? AppColors.primaryBlack : AppColors.grayTextWeak,
409
+ }} numberOfLines={1}>
410
+ {parsed.functionName || 'anonymous'}
411
+ </Text>
412
+ <Text style={{
413
+ fontFamily: AppFonts.interRegular,
414
+ fontSize: 8.5,
415
+ color: isApp ? AppColors.sky600 : AppColors.grayTextWeak,
416
+ }} numberOfLines={1}>
417
+ {parsed.fileName}:{parsed.lineNumber || 1}:{parsed.columnNumber || 1}
418
+ </Text>
419
+ </View>
420
+ {isApp && (<ExternalLinkIcon color={AppColors.sky600} size={10}/>)}
421
+ </Pressable>);
422
+ })}
423
+ </View>
424
+ </View>) : null}
425
+
234
426
  {action.payload !== undefined && (<View style={{ marginBottom: 10 }}>
235
427
  <Text style={reduxDetailStyles.expandedSectionTitle}>
236
- Action Payload:
428
+ {t('redux.actionPayload')}
237
429
  </Text>
238
430
  <JsonViewer data={action.payload} search={detailSearch} forceOpen={true}/>
239
431
  </View>)}
240
432
 
241
433
  <Text style={reduxDetailStyles.expandedSectionTitle}>
242
- State Changes (Diff):
434
+ {t('redux.stateChangesDiff')}
243
435
  </Text>
244
436
  <DiffViewer oldData={action.prevState?.[selectedReduxSlice] || {}} newData={action.nextState?.[selectedReduxSlice] || {}} forceOpen={true}/>
245
437
  </View>) : (<View style={reduxDetailStyles.collapsedPrompt}>
246
438
  <Text style={reduxDetailStyles.collapsedPromptText}>
247
- Tap to inspect action payload & diff changes
439
+ {t('redux.tapToInspectAction')}
248
440
  </Text>
249
441
  <ForwardChevronIcon color={AppColors.grayTextWeak} size={12}/>
250
442
  </View>)}
@@ -327,12 +519,15 @@ const reduxDetailStyles = StyleSheet.create({
327
519
  color: AppColors.primaryBlack,
328
520
  },
329
521
  persistedChip: {
330
- backgroundColor: '#D1FAE5',
522
+ flexDirection: 'row',
523
+ alignItems: 'center',
524
+ gap: 3.5,
525
+ backgroundColor: '#ECFDF5',
331
526
  paddingHorizontal: 6,
332
527
  paddingVertical: 2,
333
528
  borderRadius: 4,
334
529
  borderWidth: 1,
335
- borderColor: AppColors.emeraldBorder,
530
+ borderColor: '#A7F3D0',
336
531
  },
337
532
  persistedChipText: {
338
533
  fontFamily: AppFonts.interBold,
@@ -340,22 +535,22 @@ const reduxDetailStyles = StyleSheet.create({
340
535
  color: '#047857',
341
536
  },
342
537
  inMemoryChip: {
343
- backgroundColor: '#F3E8FF',
538
+ backgroundColor: '#EEF2FF',
344
539
  paddingHorizontal: 6,
345
540
  paddingVertical: 2,
346
541
  borderRadius: 4,
347
542
  borderWidth: 1,
348
- borderColor: '#E9D5FF',
543
+ borderColor: '#C7D2FE',
349
544
  },
350
545
  inMemoryChipText: {
351
546
  fontFamily: AppFonts.interBold,
352
547
  fontSize: 9,
353
- color: '#7E22CE',
548
+ color: '#4338CA',
354
549
  },
355
550
  subStatsRow: {
356
551
  flexDirection: 'row',
357
552
  alignItems: 'center',
358
- gap: 8,
553
+ gap: 6,
359
554
  flexWrap: 'wrap',
360
555
  },
361
556
  statPill: {
@@ -363,20 +558,20 @@ const reduxDetailStyles = StyleSheet.create({
363
558
  alignItems: 'center',
364
559
  gap: 4,
365
560
  backgroundColor: AppColors.grayBackground,
366
- paddingHorizontal: 8,
367
- paddingVertical: 4,
561
+ paddingHorizontal: 7,
562
+ paddingVertical: 3.5,
368
563
  borderRadius: 6,
369
564
  borderWidth: 1,
370
565
  borderColor: AppColors.dividerColor,
371
566
  },
372
567
  statLabel: {
373
- fontFamily: AppFonts.interMedium,
374
- fontSize: 10.5,
568
+ fontFamily: AppFonts.interBold,
569
+ fontSize: 10,
375
570
  color: AppColors.grayTextWeak,
376
571
  },
377
572
  statValue: {
378
573
  fontFamily: AppFonts.interBold,
379
- fontSize: 11,
574
+ fontSize: 10.5,
380
575
  color: AppColors.primaryBlack,
381
576
  },
382
577
  searchContainer: {