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
- import React, { useCallback, useState } from 'react'
1
+ import React, { useCallback, useMemo, useState } from 'react'
2
2
  import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'
3
- import { DarkTheme, MonospaceFont } from '../constants'
3
+ import { MonospaceFont, TestIDs } from '../constants'
4
+ import { useBackstageTheme } from '../ThemeContext'
4
5
  import { LogLevel } from '../types'
5
- import type { LogEntry, BackstageStyleOverrides } from '../types'
6
+ import type { LogEntry, BackstageStyleOverrides, BackstageTheme } from '../types'
6
7
  import { formatTimestamp } from '../utils/formatTimestamp'
7
8
  import { JsonTreeView } from './JsonTreeView'
8
9
 
@@ -11,50 +12,39 @@ import { JsonTreeView } from './JsonTreeView'
11
12
  interface LogItemProps {
12
13
  item: LogEntry
13
14
  onCopy?: (text: string) => void
15
+ jsonMaxDepth?: number
14
16
  styles?: BackstageStyleOverrides
15
17
  }
16
18
 
17
19
  // ─── Badge Colors ────────────────────────────────────────────────────────────
18
20
 
19
- function getBadgeConfig(level: LogLevel): {
20
- label: string
21
- color: string
22
- bgColor: string
23
- rowBg: string
24
- } {
21
+ function getBadgeConfig(
22
+ level: LogLevel,
23
+ t: BackstageTheme,
24
+ ): { label: string; color: string; bgColor: string; rowBg: string } {
25
25
  switch (level) {
26
26
  case LogLevel.error:
27
27
  return {
28
28
  label: 'ERROR',
29
- color: DarkTheme.error,
30
- bgColor: DarkTheme.errorDim,
29
+ color: t.error,
30
+ bgColor: t.errorDim,
31
31
  rowBg: 'rgba(255, 77, 106, 0.06)',
32
32
  }
33
33
  case LogLevel.warn:
34
34
  return {
35
35
  label: 'WARN',
36
- color: DarkTheme.warning,
37
- bgColor: DarkTheme.warningDim,
36
+ color: t.warning,
37
+ bgColor: t.warningDim,
38
38
  rowBg: 'rgba(255, 178, 36, 0.06)',
39
39
  }
40
40
  case LogLevel.info:
41
- return {
42
- label: 'INFO',
43
- color: DarkTheme.info,
44
- bgColor: DarkTheme.infoDim,
45
- rowBg: 'transparent',
46
- }
41
+ return { label: 'INFO', color: t.info, bgColor: t.infoDim, rowBg: 'transparent' }
47
42
  case LogLevel.debug:
48
- return {
49
- label: 'DEBUG',
50
- color: DarkTheme.debugColor,
51
- bgColor: DarkTheme.debugDim,
52
- rowBg: 'transparent',
53
- }
43
+ return { label: 'DEBUG', color: t.debugColor, bgColor: t.debugDim, rowBg: 'transparent' }
54
44
  default:
55
45
  return {
56
46
  label: 'LOG',
57
- color: DarkTheme.textSecondary,
47
+ color: t.textSecondary,
58
48
  bgColor: 'rgba(160, 160, 176, 0.1)',
59
49
  rowBg: 'transparent',
60
50
  }
@@ -64,9 +54,11 @@ function getBadgeConfig(level: LogLevel): {
64
54
  // ─── Component ───────────────────────────────────────────────────────────────
65
55
 
66
56
  export const LogItem: React.FC<LogItemProps> = React.memo(
67
- ({ item, onCopy, styles: propStyles }) => {
57
+ ({ item, onCopy, jsonMaxDepth, styles: propStyles }) => {
58
+ const theme = useBackstageTheme()
59
+ const s = useMemo(() => createStyles(theme), [theme])
68
60
  const [expanded, setExpanded] = useState(false)
69
- const badge = getBadgeConfig(item.level)
61
+ const badge = getBadgeConfig(item.level, theme)
70
62
  const hasData = item.data !== undefined
71
63
 
72
64
  const toggleExpand = useCallback(() => {
@@ -82,47 +74,52 @@ export const LogItem: React.FC<LogItemProps> = React.memo(
82
74
 
83
75
  return (
84
76
  <TouchableOpacity
85
- style={[styles.container, { backgroundColor: badge.rowBg }]}
77
+ testID={TestIDs.logItem.container(item.id)}
78
+ style={[s.container, { backgroundColor: badge.rowBg }]}
86
79
  onPress={hasData ? toggleExpand : undefined}
87
80
  onLongPress={handleCopy}
88
81
  activeOpacity={hasData ? 0.7 : 1}
89
82
  >
90
- {/* Header row: badge + timestamp + copy */}
91
- <View style={styles.headerRow}>
92
- <View style={[styles.badge, { backgroundColor: badge.bgColor }]}>
93
- <Text style={[styles.badgeText, { color: badge.color }]}>{badge.label}</Text>
83
+ <View style={s.headerRow}>
84
+ <View
85
+ testID={TestIDs.logItem.badge(item.id)}
86
+ style={[s.badge, { backgroundColor: badge.bgColor }]}
87
+ >
88
+ <Text style={[s.badgeText, { color: badge.color }]}>{badge.label}</Text>
94
89
  </View>
95
- <Text style={[styles.timestamp, propStyles?.logTimestampStyle]}>
90
+ <Text
91
+ testID={TestIDs.logItem.timestamp(item.id)}
92
+ style={[s.timestamp, propStyles?.logTimestampStyle]}
93
+ >
96
94
  {formatTimestamp(item.timestamp)}
97
95
  </Text>
98
96
  {onCopy && (
99
97
  <TouchableOpacity
100
- style={styles.copyButton}
98
+ testID={TestIDs.logItem.copyButton(item.id)}
99
+ style={s.copyButton}
101
100
  onPress={handleCopy}
102
101
  hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
103
102
  >
104
- <Text style={styles.copyButtonText}>⧉</Text>
103
+ <Text style={s.copyButtonText}>⧉</Text>
105
104
  </TouchableOpacity>
106
105
  )}
107
106
  </View>
108
107
 
109
- {/* Message */}
110
108
  <Text
111
- style={[styles.message, propStyles?.logMessageStyle]}
109
+ testID={TestIDs.logItem.message(item.id)}
110
+ style={[s.message, propStyles?.logMessageStyle]}
112
111
  numberOfLines={expanded ? undefined : 3}
113
112
  >
114
113
  {item.message}
115
114
  </Text>
116
115
 
117
- {/* Expandable data */}
118
116
  {expanded && hasData && (
119
- <View style={styles.dataContainer}>
120
- <JsonTreeView data={item.data} hideRoot maxDepth={5} />
117
+ <View testID={TestIDs.logItem.dataContainer(item.id)} style={s.dataContainer}>
118
+ <JsonTreeView data={item.data} hideRoot maxDepth={jsonMaxDepth} />
121
119
  </View>
122
120
  )}
123
121
 
124
- {/* Expand hint */}
125
- {hasData && !expanded && <Text style={styles.expandHint}>tap to expand ▾</Text>}
122
+ {hasData && !expanded && <Text style={s.expandHint}>tap to expand ▾</Text>}
126
123
  </TouchableOpacity>
127
124
  )
128
125
  },
@@ -130,60 +127,27 @@ export const LogItem: React.FC<LogItemProps> = React.memo(
130
127
 
131
128
  // ─── Styles ──────────────────────────────────────────────────────────────────
132
129
 
133
- const styles = StyleSheet.create({
134
- container: {
135
- paddingHorizontal: 14,
136
- paddingVertical: 10,
137
- borderBottomWidth: 1,
138
- borderBottomColor: DarkTheme.border,
139
- },
140
- headerRow: {
141
- flexDirection: 'row',
142
- alignItems: 'center',
143
- marginBottom: 4,
144
- },
145
- badge: {
146
- borderRadius: 4,
147
- paddingHorizontal: 6,
148
- paddingVertical: 2,
149
- marginRight: 8,
150
- },
151
- badgeText: {
152
- fontFamily: MonospaceFont,
153
- fontSize: 10,
154
- fontWeight: '800',
155
- letterSpacing: 0.5,
156
- },
157
- timestamp: {
158
- fontFamily: MonospaceFont,
159
- fontSize: 11,
160
- color: DarkTheme.textMuted,
161
- flex: 1,
162
- },
163
- copyButton: {
164
- padding: 4,
165
- },
166
- copyButtonText: {
167
- fontSize: 16,
168
- color: DarkTheme.textMuted,
169
- },
170
- message: {
171
- fontFamily: MonospaceFont,
172
- fontSize: 12,
173
- color: DarkTheme.text,
174
- lineHeight: 18,
175
- },
176
- dataContainer: {
177
- marginTop: 8,
178
- paddingTop: 8,
179
- borderTopWidth: 1,
180
- borderTopColor: DarkTheme.border,
181
- },
182
- expandHint: {
183
- fontFamily: MonospaceFont,
184
- fontSize: 10,
185
- color: DarkTheme.textMuted,
186
- marginTop: 4,
187
- fontStyle: 'italic',
188
- },
189
- })
130
+ const createStyles = (t: BackstageTheme) =>
131
+ StyleSheet.create({
132
+ container: {
133
+ paddingHorizontal: 14,
134
+ paddingVertical: 10,
135
+ borderBottomWidth: 1,
136
+ borderBottomColor: t.border,
137
+ },
138
+ headerRow: { flexDirection: 'row', alignItems: 'center', marginBottom: 4 },
139
+ badge: { borderRadius: 4, paddingHorizontal: 6, paddingVertical: 2, marginRight: 8 },
140
+ badgeText: { fontFamily: MonospaceFont, fontSize: 10, fontWeight: '800', letterSpacing: 0.5 },
141
+ timestamp: { fontFamily: MonospaceFont, fontSize: 11, color: t.textMuted, flex: 1 },
142
+ copyButton: { padding: 4 },
143
+ copyButtonText: { fontSize: 16, color: t.textMuted },
144
+ message: { fontFamily: MonospaceFont, fontSize: 12, color: t.text, lineHeight: 18 },
145
+ dataContainer: { marginTop: 8, paddingTop: 8, borderTopWidth: 1, borderTopColor: t.border },
146
+ expandHint: {
147
+ fontFamily: MonospaceFont,
148
+ fontSize: 10,
149
+ color: t.textMuted,
150
+ marginTop: 4,
151
+ fontStyle: 'italic',
152
+ },
153
+ })
@@ -1,7 +1,8 @@
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'
4
- import type { LogEntry, BackstageStyleOverrides } from '../types'
3
+ import { MonospaceFont, TestIDs } from '../constants'
4
+ import { useBackstageTheme } from '../ThemeContext'
5
+ import type { LogEntry, BackstageStyleOverrides, BackstageTheme } from '../types'
5
6
  import { LogItem } from './LogItem'
6
7
  import { formatTimestamp } from '../utils/formatTimestamp'
7
8
 
@@ -11,6 +12,7 @@ interface LogsTabProps {
11
12
  logs: LogEntry[]
12
13
  onRefresh: () => void
13
14
  onCopyLogs?: (logs: string) => void
15
+ jsonMaxDepth?: number
14
16
  styles?: BackstageStyleOverrides
15
17
  }
16
18
 
@@ -20,31 +22,29 @@ export const LogsTab: React.FC<LogsTabProps> = ({
20
22
  logs,
21
23
  onRefresh,
22
24
  onCopyLogs,
25
+ jsonMaxDepth,
23
26
  styles: propStyles,
24
27
  }) => {
28
+ const theme = useBackstageTheme()
29
+ const s = useMemo(() => createStyles(theme), [theme])
25
30
  const [searchText, setSearchText] = useState('')
26
31
 
27
- // Filter logs based on search text
28
32
  const filteredLogs = useMemo(() => {
29
33
  if (!searchText.trim()) return logs
30
-
31
34
  const query = searchText.toLowerCase()
32
35
  return logs.filter(log => {
33
36
  return log.message.toLowerCase().includes(query) || log.level.toLowerCase().includes(query)
34
37
  })
35
38
  }, [logs, searchText])
36
39
 
37
- // Copy all visible logs to text
38
40
  const handleCopyAll = useCallback(() => {
39
41
  if (!onCopyLogs) return
40
-
41
42
  const text = filteredLogs
42
43
  .map(log => `[${log.level.toUpperCase()}] ${formatTimestamp(log.timestamp)} ${log.message}`)
43
44
  .join('\n')
44
45
  onCopyLogs(text)
45
46
  }, [filteredLogs, onCopyLogs])
46
47
 
47
- // Copy individual log
48
48
  const handleCopyItem = useCallback(
49
49
  (text: string) => {
50
50
  if (onCopyLogs) {
@@ -54,10 +54,14 @@ export const LogsTab: React.FC<LogsTabProps> = ({
54
54
  [onCopyLogs],
55
55
  )
56
56
 
57
- // Render log item
58
57
  const renderItem = useCallback(
59
58
  ({ item }: { item: LogEntry }) => (
60
- <LogItem item={item} onCopy={onCopyLogs ? handleCopyItem : undefined} styles={propStyles} />
59
+ <LogItem
60
+ item={item}
61
+ onCopy={onCopyLogs ? handleCopyItem : undefined}
62
+ jsonMaxDepth={jsonMaxDepth}
63
+ styles={propStyles}
64
+ />
61
65
  ),
62
66
  [handleCopyItem, onCopyLogs, propStyles],
63
67
  )
@@ -65,16 +69,15 @@ export const LogsTab: React.FC<LogsTabProps> = ({
65
69
  const keyExtractor = useCallback((item: LogEntry) => item.id, [])
66
70
 
67
71
  return (
68
- <View style={styles.container}>
69
- {/* Search Bar */}
70
- <View style={styles.searchContainer}>
71
- <View style={styles.searchInputWrapper}>
72
- <Text style={styles.searchIcon}>⌕</Text>
72
+ <View testID={TestIDs.logsTab.container} style={s.container}>
73
+ <View style={s.searchContainer}>
74
+ <View style={s.searchInputWrapper}>
75
+ <Text style={s.searchIcon}>⌕</Text>
73
76
  <TextInput
74
77
  testID={TestIDs.logsTab.searchInput}
75
- style={styles.searchInput}
78
+ style={s.searchInput}
76
79
  placeholder="Filter logs..."
77
- placeholderTextColor={DarkTheme.textMuted}
80
+ placeholderTextColor={theme.textMuted}
78
81
  value={searchText}
79
82
  onChangeText={setSearchText}
80
83
  autoCapitalize="none"
@@ -86,26 +89,24 @@ export const LogsTab: React.FC<LogsTabProps> = ({
86
89
  {onCopyLogs && (
87
90
  <TouchableOpacity
88
91
  testID={TestIDs.logsTab.copyButton}
89
- style={styles.copyAllButton}
92
+ style={s.copyAllButton}
90
93
  onPress={handleCopyAll}
91
94
  activeOpacity={0.7}
92
95
  >
93
- <Text style={styles.copyAllText}>Copy All</Text>
96
+ <Text style={s.copyAllText}>Copy All</Text>
94
97
  </TouchableOpacity>
95
98
  )}
96
99
  </View>
97
100
 
98
- {/* Log Count */}
99
- <View style={styles.countBar}>
100
- <Text style={styles.countText}>
101
+ <View testID={TestIDs.logsTab.statsBar} style={s.countBar}>
102
+ <Text style={s.countText}>
101
103
  {filteredLogs.length === logs.length
102
104
  ? `${logs.length} log${logs.length !== 1 ? 's' : ''}`
103
105
  : `${filteredLogs.length} of ${logs.length} logs`}
104
106
  </Text>
105
- <Text style={styles.pullHint}>↓ pull to refresh</Text>
107
+ <Text style={s.pullHint}>↓ pull to refresh</Text>
106
108
  </View>
107
109
 
108
- {/* Log List */}
109
110
  <FlatList
110
111
  testID={TestIDs.logsTab.list}
111
112
  data={filteredLogs}
@@ -113,16 +114,15 @@ export const LogsTab: React.FC<LogsTabProps> = ({
113
114
  keyExtractor={keyExtractor}
114
115
  refreshing={false}
115
116
  onRefresh={onRefresh}
116
- style={styles.list}
117
- contentContainerStyle={filteredLogs.length === 0 ? styles.emptyContainer : undefined}
117
+ style={s.list}
118
+ contentContainerStyle={filteredLogs.length === 0 ? s.emptyContainer : undefined}
118
119
  ListEmptyComponent={
119
- <View style={styles.emptyState}>
120
- <Text style={styles.emptyIcon}>📋</Text>
121
- <Text style={styles.emptyTitle}>No logs yet</Text>
122
- <Text style={styles.emptySubtitle}>Console output will appear here</Text>
120
+ <View style={s.emptyState}>
121
+ <Text style={s.emptyIcon}>📋</Text>
122
+ <Text style={s.emptyTitle}>No logs yet</Text>
123
+ <Text style={s.emptySubtitle}>Console output will appear here</Text>
123
124
  </View>
124
125
  }
125
- // Perf optimizations
126
126
  maxToRenderPerBatch={20}
127
127
  windowSize={10}
128
128
  initialNumToRender={20}
@@ -135,105 +135,83 @@ export const LogsTab: React.FC<LogsTabProps> = ({
135
135
 
136
136
  // ─── Styles ──────────────────────────────────────────────────────────────────
137
137
 
138
- const styles = StyleSheet.create({
139
- container: {
140
- flex: 1,
141
- },
142
- searchContainer: {
143
- flexDirection: 'row',
144
- alignItems: 'center',
145
- paddingHorizontal: 14,
146
- paddingVertical: 10,
147
- gap: 10,
148
- borderBottomWidth: 1,
149
- borderBottomColor: DarkTheme.border,
150
- },
151
- searchInputWrapper: {
152
- flex: 1,
153
- flexDirection: 'row',
154
- alignItems: 'center',
155
- backgroundColor: DarkTheme.surfaceElevated,
156
- borderRadius: 10,
157
- borderWidth: 1,
158
- borderColor: DarkTheme.border,
159
- paddingHorizontal: 12,
160
- height: 38,
161
- },
162
- searchIcon: {
163
- fontSize: 16,
164
- color: DarkTheme.textMuted,
165
- marginRight: 8,
166
- },
167
- searchInput: {
168
- flex: 1,
169
- fontFamily: MonospaceFont,
170
- fontSize: 13,
171
- color: DarkTheme.text,
172
- padding: 0,
173
- },
174
- copyAllButton: {
175
- backgroundColor: DarkTheme.accentDim,
176
- borderRadius: 8,
177
- borderWidth: 1,
178
- borderColor: DarkTheme.accent,
179
- paddingHorizontal: 14,
180
- height: 38,
181
- justifyContent: 'center',
182
- },
183
- copyAllText: {
184
- fontFamily: MonospaceFont,
185
- fontSize: 12,
186
- fontWeight: '700',
187
- color: DarkTheme.accent,
188
- letterSpacing: 0.5,
189
- },
190
- countBar: {
191
- flexDirection: 'row',
192
- justifyContent: 'space-between',
193
- alignItems: 'center',
194
- paddingHorizontal: 16,
195
- paddingVertical: 6,
196
- backgroundColor: DarkTheme.surface,
197
- },
198
- countText: {
199
- fontFamily: MonospaceFont,
200
- fontSize: 11,
201
- color: DarkTheme.textMuted,
202
- },
203
- pullHint: {
204
- fontFamily: MonospaceFont,
205
- fontSize: 10,
206
- color: DarkTheme.textMuted,
207
- fontStyle: 'italic',
208
- },
209
- list: {
210
- flex: 1,
211
- },
212
- emptyContainer: {
213
- flex: 1,
214
- justifyContent: 'center',
215
- },
216
- emptyState: {
217
- alignItems: 'center',
218
- justifyContent: 'center',
219
- padding: 40,
220
- },
221
- emptyIcon: {
222
- fontSize: 48,
223
- marginBottom: 16,
224
- opacity: 0.5,
225
- },
226
- emptyTitle: {
227
- fontFamily: MonospaceFont,
228
- fontSize: 16,
229
- fontWeight: '700',
230
- color: DarkTheme.textSecondary,
231
- marginBottom: 8,
232
- },
233
- emptySubtitle: {
234
- fontFamily: MonospaceFont,
235
- fontSize: 13,
236
- color: DarkTheme.textMuted,
237
- textAlign: 'center',
238
- },
239
- })
138
+ const createStyles = (t: BackstageTheme) =>
139
+ StyleSheet.create({
140
+ container: { flex: 1 },
141
+ searchContainer: {
142
+ flexDirection: 'row',
143
+ alignItems: 'center',
144
+ paddingHorizontal: 14,
145
+ paddingVertical: 10,
146
+ gap: 10,
147
+ borderBottomWidth: 1,
148
+ borderBottomColor: t.border,
149
+ },
150
+ searchInputWrapper: {
151
+ flex: 1,
152
+ flexDirection: 'row',
153
+ alignItems: 'center',
154
+ backgroundColor: t.surfaceElevated,
155
+ borderRadius: 10,
156
+ borderWidth: 1,
157
+ borderColor: t.border,
158
+ paddingHorizontal: 12,
159
+ height: 38,
160
+ },
161
+ searchIcon: { fontSize: 16, color: t.textMuted, marginRight: 8 },
162
+ searchInput: {
163
+ flex: 1,
164
+ fontFamily: MonospaceFont,
165
+ fontSize: 13,
166
+ color: t.text,
167
+ padding: 0,
168
+ },
169
+ copyAllButton: {
170
+ backgroundColor: t.accentDim,
171
+ borderRadius: 8,
172
+ borderWidth: 1,
173
+ borderColor: t.accent,
174
+ paddingHorizontal: 14,
175
+ height: 38,
176
+ justifyContent: 'center',
177
+ },
178
+ copyAllText: {
179
+ fontFamily: MonospaceFont,
180
+ fontSize: 12,
181
+ fontWeight: '700',
182
+ color: t.accent,
183
+ letterSpacing: 0.5,
184
+ },
185
+ countBar: {
186
+ flexDirection: 'row',
187
+ justifyContent: 'space-between',
188
+ alignItems: 'center',
189
+ paddingHorizontal: 16,
190
+ paddingVertical: 6,
191
+ backgroundColor: t.surface,
192
+ },
193
+ countText: { fontFamily: MonospaceFont, fontSize: 11, color: t.textMuted },
194
+ pullHint: {
195
+ fontFamily: MonospaceFont,
196
+ fontSize: 10,
197
+ color: t.textMuted,
198
+ fontStyle: 'italic',
199
+ },
200
+ list: { flex: 1 },
201
+ emptyContainer: { flex: 1, justifyContent: 'center' },
202
+ emptyState: { alignItems: 'center', justifyContent: 'center', padding: 40 },
203
+ emptyIcon: { fontSize: 48, marginBottom: 16, opacity: 0.5 },
204
+ emptyTitle: {
205
+ fontFamily: MonospaceFont,
206
+ fontSize: 16,
207
+ fontWeight: '700',
208
+ color: t.textSecondary,
209
+ marginBottom: 8,
210
+ },
211
+ emptySubtitle: {
212
+ fontFamily: MonospaceFont,
213
+ fontSize: 13,
214
+ color: t.textMuted,
215
+ textAlign: 'center',
216
+ },
217
+ })