rn-backstage 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +84 -2
  2. package/lib/commonjs/Backstage.js +46 -33
  3. package/lib/commonjs/Backstage.js.map +1 -1
  4. package/lib/commonjs/ThemeContext.js +43 -0
  5. package/lib/commonjs/ThemeContext.js.map +1 -0
  6. package/lib/commonjs/components/BackstagePanel.js +35 -12
  7. package/lib/commonjs/components/BackstagePanel.js.map +1 -1
  8. package/lib/commonjs/components/FlagsTab.js +60 -57
  9. package/lib/commonjs/components/FlagsTab.js.map +1 -1
  10. package/lib/commonjs/components/FloatingPill.js +86 -24
  11. package/lib/commonjs/components/FloatingPill.js.map +1 -1
  12. package/lib/commonjs/components/InfoTab.js +77 -107
  13. package/lib/commonjs/components/InfoTab.js.map +1 -1
  14. package/lib/commonjs/components/JsonTreeView.js +39 -34
  15. package/lib/commonjs/components/JsonTreeView.js.map +1 -1
  16. package/lib/commonjs/components/LogItem.js +39 -29
  17. package/lib/commonjs/components/LogItem.js.map +1 -1
  18. package/lib/commonjs/components/LogsTab.js +39 -42
  19. package/lib/commonjs/components/LogsTab.js.map +1 -1
  20. package/lib/commonjs/components/NetworkItem.js +111 -135
  21. package/lib/commonjs/components/NetworkItem.js.map +1 -1
  22. package/lib/commonjs/components/NetworkTab.js +50 -49
  23. package/lib/commonjs/components/NetworkTab.js.map +1 -1
  24. package/lib/commonjs/components/StorageTab.js +655 -0
  25. package/lib/commonjs/components/StorageTab.js.map +1 -0
  26. package/lib/commonjs/components/TabBar.js +8 -5
  27. package/lib/commonjs/components/TabBar.js.map +1 -1
  28. package/lib/commonjs/constants.js +96 -7
  29. package/lib/commonjs/constants.js.map +1 -1
  30. package/lib/commonjs/index.js +96 -0
  31. package/lib/commonjs/index.js.map +1 -1
  32. package/lib/commonjs/types.js +1 -0
  33. package/lib/commonjs/types.js.map +1 -1
  34. package/lib/module/Backstage.js +46 -33
  35. package/lib/module/Backstage.js.map +1 -1
  36. package/lib/module/ThemeContext.js +37 -0
  37. package/lib/module/ThemeContext.js.map +1 -0
  38. package/lib/module/components/BackstagePanel.js +37 -14
  39. package/lib/module/components/BackstagePanel.js.map +1 -1
  40. package/lib/module/components/FlagsTab.js +61 -58
  41. package/lib/module/components/FlagsTab.js.map +1 -1
  42. package/lib/module/components/FloatingPill.js +90 -28
  43. package/lib/module/components/FloatingPill.js.map +1 -1
  44. package/lib/module/components/InfoTab.js +77 -107
  45. package/lib/module/components/InfoTab.js.map +1 -1
  46. package/lib/module/components/JsonTreeView.js +42 -38
  47. package/lib/module/components/JsonTreeView.js.map +1 -1
  48. package/lib/module/components/LogItem.js +41 -31
  49. package/lib/module/components/LogItem.js.map +1 -1
  50. package/lib/module/components/LogsTab.js +40 -43
  51. package/lib/module/components/LogsTab.js.map +1 -1
  52. package/lib/module/components/NetworkItem.js +113 -137
  53. package/lib/module/components/NetworkItem.js.map +1 -1
  54. package/lib/module/components/NetworkTab.js +51 -50
  55. package/lib/module/components/NetworkTab.js.map +1 -1
  56. package/lib/module/components/StorageTab.js +650 -0
  57. package/lib/module/components/StorageTab.js.map +1 -0
  58. package/lib/module/components/TabBar.js +10 -7
  59. package/lib/module/components/TabBar.js.map +1 -1
  60. package/lib/module/constants.js +95 -6
  61. package/lib/module/constants.js.map +1 -1
  62. package/lib/module/index.js +14 -0
  63. package/lib/module/index.js.map +1 -1
  64. package/lib/module/types.js +2 -0
  65. package/lib/module/types.js.map +1 -1
  66. package/lib/typescript/Backstage.d.ts.map +1 -1
  67. package/lib/typescript/ThemeContext.d.ts +11 -0
  68. package/lib/typescript/ThemeContext.d.ts.map +1 -0
  69. package/lib/typescript/components/BackstagePanel.d.ts +3 -1
  70. package/lib/typescript/components/BackstagePanel.d.ts.map +1 -1
  71. package/lib/typescript/components/FlagsTab.d.ts.map +1 -1
  72. package/lib/typescript/components/FloatingPill.d.ts +2 -0
  73. package/lib/typescript/components/FloatingPill.d.ts.map +1 -1
  74. package/lib/typescript/components/InfoTab.d.ts +1 -0
  75. package/lib/typescript/components/InfoTab.d.ts.map +1 -1
  76. package/lib/typescript/components/JsonTreeView.d.ts.map +1 -1
  77. package/lib/typescript/components/LogItem.d.ts +1 -0
  78. package/lib/typescript/components/LogItem.d.ts.map +1 -1
  79. package/lib/typescript/components/LogsTab.d.ts +1 -0
  80. package/lib/typescript/components/LogsTab.d.ts.map +1 -1
  81. package/lib/typescript/components/NetworkItem.d.ts +1 -0
  82. package/lib/typescript/components/NetworkItem.d.ts.map +1 -1
  83. package/lib/typescript/components/NetworkTab.d.ts +1 -0
  84. package/lib/typescript/components/NetworkTab.d.ts.map +1 -1
  85. package/lib/typescript/components/StorageTab.d.ts +9 -0
  86. package/lib/typescript/components/StorageTab.d.ts.map +1 -0
  87. package/lib/typescript/components/TabBar.d.ts.map +1 -1
  88. package/lib/typescript/constants.d.ts +60 -4
  89. package/lib/typescript/constants.d.ts.map +1 -1
  90. package/lib/typescript/index.d.ts +14 -1
  91. package/lib/typescript/index.d.ts.map +1 -1
  92. package/lib/typescript/types.d.ts +20 -0
  93. package/lib/typescript/types.d.ts.map +1 -1
  94. package/package.json +6 -4
  95. package/src/Backstage.tsx +45 -33
  96. package/src/ThemeContext.tsx +38 -0
  97. package/src/components/BackstagePanel.tsx +89 -61
  98. package/src/components/FlagsTab.tsx +125 -174
  99. package/src/components/FloatingPill.tsx +127 -30
  100. package/src/components/InfoTab.tsx +153 -186
  101. package/src/components/JsonTreeView.tsx +53 -73
  102. package/src/components/LogItem.tsx +64 -100
  103. package/src/components/LogsTab.tsx +110 -132
  104. package/src/components/NetworkItem.tsx +306 -417
  105. package/src/components/NetworkTab.tsx +129 -163
  106. package/src/components/StorageTab.tsx +607 -0
  107. package/src/components/TabBar.tsx +51 -47
  108. package/src/constants.ts +103 -4
  109. package/src/index.ts +17 -0
  110. package/src/types.ts +28 -0
@@ -1,8 +1,9 @@
1
1
  import React, { useCallback, useMemo, useState } from 'react'
2
2
  import { FlatList, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native'
3
- import { DarkTheme, MonospaceFont, TestIDs } from '../constants'
3
+ import { MonospaceFont, TestIDs } from '../constants'
4
+ import { useBackstageTheme } from '../ThemeContext'
4
5
  import { NetworkState } from '../types'
5
- import type { NetworkEntry } from '../types'
6
+ import type { NetworkEntry, BackstageTheme } from '../types'
6
7
  import { NetworkItem } from './NetworkItem'
7
8
 
8
9
  // ─── Types ───────────────────────────────────────────────────────────────────
@@ -12,17 +13,24 @@ interface NetworkTabProps {
12
13
  onRefresh: () => void
13
14
  onClear: () => void
14
15
  onCopy?: (text: string) => void
16
+ jsonMaxDepth?: number
15
17
  }
16
18
 
17
19
  // ─── Component ───────────────────────────────────────────────────────────────
18
20
 
19
- export const NetworkTab: React.FC<NetworkTabProps> = ({ entries, onRefresh, onClear, onCopy }) => {
21
+ export const NetworkTab: React.FC<NetworkTabProps> = ({
22
+ entries,
23
+ onRefresh,
24
+ onClear,
25
+ onCopy,
26
+ jsonMaxDepth,
27
+ }) => {
28
+ const theme = useBackstageTheme()
29
+ const s = useMemo(() => createStyles(theme), [theme])
20
30
  const [searchText, setSearchText] = useState('')
21
31
 
22
- // Filter entries based on search text
23
32
  const filteredEntries = useMemo(() => {
24
33
  if (!searchText.trim()) return entries
25
-
26
34
  const query = searchText.toLowerCase()
27
35
  return entries.filter(entry => {
28
36
  return (
@@ -34,7 +42,6 @@ export const NetworkTab: React.FC<NetworkTabProps> = ({ entries, onRefresh, onCl
34
42
  })
35
43
  }, [entries, searchText])
36
44
 
37
- // Summary stats
38
45
  const stats = useMemo(() => {
39
46
  const total = entries.length
40
47
  const errors = entries.filter(
@@ -48,28 +55,28 @@ export const NetworkTab: React.FC<NetworkTabProps> = ({ entries, onRefresh, onCl
48
55
  completed.length > 0
49
56
  ? Math.round(completed.reduce((sum, e) => sum + (e.duration ?? 0), 0) / completed.length)
50
57
  : 0
51
-
52
58
  return { total, errors, pending, avgDuration }
53
59
  }, [entries])
54
60
 
55
61
  const renderItem = useCallback(
56
- ({ item }: { item: NetworkEntry }) => <NetworkItem item={item} onCopy={onCopy} />,
62
+ ({ item }: { item: NetworkEntry }) => (
63
+ <NetworkItem item={item} onCopy={onCopy} jsonMaxDepth={jsonMaxDepth} />
64
+ ),
57
65
  [onCopy],
58
66
  )
59
67
 
60
68
  const keyExtractor = useCallback((item: NetworkEntry) => item.id, [])
61
69
 
62
70
  return (
63
- <View style={styles.container}>
64
- {/* Search Bar */}
65
- <View style={styles.searchContainer}>
66
- <View style={styles.searchInputWrapper}>
67
- <Text style={styles.searchIcon}>⌕</Text>
71
+ <View testID={TestIDs.networkTab.container} style={s.container}>
72
+ <View style={s.searchContainer}>
73
+ <View style={s.searchInputWrapper}>
74
+ <Text style={s.searchIcon}>⌕</Text>
68
75
  <TextInput
69
76
  testID={TestIDs.networkTab.searchInput}
70
- style={styles.searchInput}
77
+ style={s.searchInput}
71
78
  placeholder="Filter by URL, method, status..."
72
- placeholderTextColor={DarkTheme.textMuted}
79
+ placeholderTextColor={theme.textMuted}
73
80
  value={searchText}
74
81
  onChangeText={setSearchText}
75
82
  autoCapitalize="none"
@@ -80,39 +87,37 @@ export const NetworkTab: React.FC<NetworkTabProps> = ({ entries, onRefresh, onCl
80
87
  </View>
81
88
  <TouchableOpacity
82
89
  testID={TestIDs.networkTab.clearButton}
83
- style={styles.clearButton}
90
+ style={s.clearButton}
84
91
  onPress={onClear}
85
92
  activeOpacity={0.7}
86
93
  >
87
- <Text style={styles.clearButtonText}>Clear</Text>
94
+ <Text style={s.clearButtonText}>Clear</Text>
88
95
  </TouchableOpacity>
89
96
  </View>
90
97
 
91
- {/* Stats Bar */}
92
- <View style={styles.statsBar}>
93
- <View style={styles.statsRow}>
94
- <Text style={styles.statText}>
98
+ <View testID={TestIDs.networkTab.statsBar} style={s.statsBar}>
99
+ <View style={s.statsRow}>
100
+ <Text style={s.statText}>
95
101
  {filteredEntries.length === entries.length
96
102
  ? `${stats.total} request${stats.total !== 1 ? 's' : ''}`
97
103
  : `${filteredEntries.length} of ${stats.total}`}
98
104
  </Text>
99
105
  {stats.errors > 0 && (
100
- <View style={styles.statBadge}>
101
- <Text style={styles.statBadgeError}>
106
+ <View style={s.statBadge}>
107
+ <Text style={s.statBadgeError}>
102
108
  {stats.errors} error{stats.errors !== 1 ? 's' : ''}
103
109
  </Text>
104
110
  </View>
105
111
  )}
106
112
  {stats.pending > 0 && (
107
- <View style={[styles.statBadge, styles.statBadgePending]}>
108
- <Text style={styles.statBadgePendingText}>{stats.pending} pending</Text>
113
+ <View style={[s.statBadge, s.statBadgePending]}>
114
+ <Text style={s.statBadgePendingText}>{stats.pending} pending</Text>
109
115
  </View>
110
116
  )}
111
117
  </View>
112
- {stats.avgDuration > 0 && <Text style={styles.avgText}>avg {stats.avgDuration}ms</Text>}
118
+ {stats.avgDuration > 0 && <Text style={s.avgText}>avg {stats.avgDuration}ms</Text>}
113
119
  </View>
114
120
 
115
- {/* Request List */}
116
121
  <FlatList
117
122
  testID={TestIDs.networkTab.list}
118
123
  data={filteredEntries}
@@ -120,18 +125,17 @@ export const NetworkTab: React.FC<NetworkTabProps> = ({ entries, onRefresh, onCl
120
125
  keyExtractor={keyExtractor}
121
126
  refreshing={false}
122
127
  onRefresh={onRefresh}
123
- style={styles.list}
124
- contentContainerStyle={filteredEntries.length === 0 ? styles.emptyContainer : undefined}
128
+ style={s.list}
129
+ contentContainerStyle={filteredEntries.length === 0 ? s.emptyContainer : undefined}
125
130
  ListEmptyComponent={
126
- <View style={styles.emptyState}>
127
- <Text style={styles.emptyIcon}>🌐</Text>
128
- <Text style={styles.emptyTitle}>No network requests</Text>
129
- <Text style={styles.emptySubtitle}>
131
+ <View style={s.emptyState}>
132
+ <Text style={s.emptyIcon}>🌐</Text>
133
+ <Text style={s.emptyTitle}>No network requests</Text>
134
+ <Text style={s.emptySubtitle}>
130
135
  HTTP requests made via fetch or XMLHttpRequest will appear here
131
136
  </Text>
132
137
  </View>
133
138
  }
134
- // Perf optimizations
135
139
  maxToRenderPerBatch={20}
136
140
  windowSize={10}
137
141
  initialNumToRender={20}
@@ -143,132 +147,94 @@ export const NetworkTab: React.FC<NetworkTabProps> = ({ entries, onRefresh, onCl
143
147
 
144
148
  // ─── Styles ──────────────────────────────────────────────────────────────────
145
149
 
146
- const styles = StyleSheet.create({
147
- container: {
148
- flex: 1,
149
- },
150
- searchContainer: {
151
- flexDirection: 'row',
152
- alignItems: 'center',
153
- paddingHorizontal: 14,
154
- paddingVertical: 10,
155
- gap: 10,
156
- borderBottomWidth: 1,
157
- borderBottomColor: DarkTheme.border,
158
- },
159
- searchInputWrapper: {
160
- flex: 1,
161
- flexDirection: 'row',
162
- alignItems: 'center',
163
- backgroundColor: DarkTheme.surfaceElevated,
164
- borderRadius: 10,
165
- borderWidth: 1,
166
- borderColor: DarkTheme.border,
167
- paddingHorizontal: 12,
168
- height: 38,
169
- },
170
- searchIcon: {
171
- fontSize: 16,
172
- color: DarkTheme.textMuted,
173
- marginRight: 8,
174
- },
175
- searchInput: {
176
- flex: 1,
177
- fontFamily: MonospaceFont,
178
- fontSize: 13,
179
- color: DarkTheme.text,
180
- padding: 0,
181
- },
182
- clearButton: {
183
- backgroundColor: DarkTheme.errorDim,
184
- borderRadius: 8,
185
- borderWidth: 1,
186
- borderColor: DarkTheme.error,
187
- paddingHorizontal: 14,
188
- height: 38,
189
- justifyContent: 'center',
190
- },
191
- clearButtonText: {
192
- fontFamily: MonospaceFont,
193
- fontSize: 12,
194
- fontWeight: '700',
195
- color: DarkTheme.error,
196
- letterSpacing: 0.5,
197
- },
198
- statsBar: {
199
- flexDirection: 'row',
200
- justifyContent: 'space-between',
201
- alignItems: 'center',
202
- paddingHorizontal: 16,
203
- paddingVertical: 6,
204
- backgroundColor: DarkTheme.surface,
205
- },
206
- statsRow: {
207
- flexDirection: 'row',
208
- alignItems: 'center',
209
- gap: 8,
210
- },
211
- statText: {
212
- fontFamily: MonospaceFont,
213
- fontSize: 11,
214
- color: DarkTheme.textMuted,
215
- },
216
- statBadge: {
217
- backgroundColor: DarkTheme.errorDim,
218
- borderRadius: 4,
219
- paddingHorizontal: 6,
220
- paddingVertical: 1,
221
- },
222
- statBadgeError: {
223
- fontFamily: MonospaceFont,
224
- fontSize: 10,
225
- fontWeight: '700',
226
- color: DarkTheme.error,
227
- },
228
- statBadgePending: {
229
- backgroundColor: DarkTheme.warningDim,
230
- },
231
- statBadgePendingText: {
232
- fontFamily: MonospaceFont,
233
- fontSize: 10,
234
- fontWeight: '700',
235
- color: DarkTheme.warning,
236
- },
237
- avgText: {
238
- fontFamily: MonospaceFont,
239
- fontSize: 10,
240
- color: DarkTheme.textMuted,
241
- fontStyle: 'italic',
242
- },
243
- list: {
244
- flex: 1,
245
- },
246
- emptyContainer: {
247
- flex: 1,
248
- justifyContent: 'center',
249
- },
250
- emptyState: {
251
- alignItems: 'center',
252
- justifyContent: 'center',
253
- padding: 40,
254
- },
255
- emptyIcon: {
256
- fontSize: 48,
257
- marginBottom: 16,
258
- opacity: 0.5,
259
- },
260
- emptyTitle: {
261
- fontFamily: MonospaceFont,
262
- fontSize: 16,
263
- fontWeight: '700',
264
- color: DarkTheme.textSecondary,
265
- marginBottom: 8,
266
- },
267
- emptySubtitle: {
268
- fontFamily: MonospaceFont,
269
- fontSize: 13,
270
- color: DarkTheme.textMuted,
271
- textAlign: 'center',
272
- lineHeight: 20,
273
- },
274
- })
150
+ const createStyles = (t: BackstageTheme) =>
151
+ StyleSheet.create({
152
+ container: { flex: 1 },
153
+ searchContainer: {
154
+ flexDirection: 'row',
155
+ alignItems: 'center',
156
+ paddingHorizontal: 14,
157
+ paddingVertical: 10,
158
+ gap: 10,
159
+ borderBottomWidth: 1,
160
+ borderBottomColor: t.border,
161
+ },
162
+ searchInputWrapper: {
163
+ flex: 1,
164
+ flexDirection: 'row',
165
+ alignItems: 'center',
166
+ backgroundColor: t.surfaceElevated,
167
+ borderRadius: 10,
168
+ borderWidth: 1,
169
+ borderColor: t.border,
170
+ paddingHorizontal: 12,
171
+ height: 38,
172
+ },
173
+ searchIcon: { fontSize: 16, color: t.textMuted, marginRight: 8 },
174
+ searchInput: {
175
+ flex: 1,
176
+ fontFamily: MonospaceFont,
177
+ fontSize: 13,
178
+ color: t.text,
179
+ padding: 0,
180
+ },
181
+ clearButton: {
182
+ backgroundColor: t.errorDim,
183
+ borderRadius: 8,
184
+ borderWidth: 1,
185
+ borderColor: t.error,
186
+ paddingHorizontal: 14,
187
+ height: 38,
188
+ justifyContent: 'center',
189
+ },
190
+ clearButtonText: {
191
+ fontFamily: MonospaceFont,
192
+ fontSize: 12,
193
+ fontWeight: '700',
194
+ color: t.error,
195
+ letterSpacing: 0.5,
196
+ },
197
+ statsBar: {
198
+ flexDirection: 'row',
199
+ justifyContent: 'space-between',
200
+ alignItems: 'center',
201
+ paddingHorizontal: 16,
202
+ paddingVertical: 6,
203
+ backgroundColor: t.surface,
204
+ },
205
+ statsRow: { flexDirection: 'row', alignItems: 'center', gap: 8 },
206
+ statText: { fontFamily: MonospaceFont, fontSize: 11, color: t.textMuted },
207
+ statBadge: {
208
+ backgroundColor: t.errorDim,
209
+ borderRadius: 4,
210
+ paddingHorizontal: 6,
211
+ paddingVertical: 1,
212
+ },
213
+ statBadgeError: { fontFamily: MonospaceFont, fontSize: 10, fontWeight: '700', color: t.error },
214
+ statBadgePending: { backgroundColor: t.warningDim },
215
+ statBadgePendingText: {
216
+ fontFamily: MonospaceFont,
217
+ fontSize: 10,
218
+ fontWeight: '700',
219
+ color: t.warning,
220
+ },
221
+ avgText: { fontFamily: MonospaceFont, fontSize: 10, color: t.textMuted, fontStyle: 'italic' },
222
+ list: { flex: 1 },
223
+ emptyContainer: { flex: 1, justifyContent: 'center' },
224
+ emptyState: { alignItems: 'center', justifyContent: 'center', padding: 40 },
225
+ emptyIcon: { fontSize: 48, marginBottom: 16, opacity: 0.5 },
226
+ emptyTitle: {
227
+ fontFamily: MonospaceFont,
228
+ fontSize: 16,
229
+ fontWeight: '700',
230
+ color: t.textSecondary,
231
+ marginBottom: 8,
232
+ },
233
+ emptySubtitle: {
234
+ fontFamily: MonospaceFont,
235
+ fontSize: 13,
236
+ color: t.textMuted,
237
+ textAlign: 'center',
238
+ lineHeight: 20,
239
+ },
240
+ })