@buoy-gg/storage 1.7.2

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 (187) hide show
  1. package/README.md +607 -0
  2. package/lib/commonjs/index.js +34 -0
  3. package/lib/commonjs/package.json +1 -0
  4. package/lib/commonjs/preset.js +94 -0
  5. package/lib/commonjs/storage/components/DiffViewer/DiffOptionsPanel.js +356 -0
  6. package/lib/commonjs/storage/components/DiffViewer/TreeDiffViewer.js +29 -0
  7. package/lib/commonjs/storage/components/DiffViewer/components/DiffSummary.js +121 -0
  8. package/lib/commonjs/storage/components/DiffViewer/modes/ThemedSplitView.js +419 -0
  9. package/lib/commonjs/storage/components/DiffViewer/themes/diffThemes.js +122 -0
  10. package/lib/commonjs/storage/components/GameUIStorageBrowser.js +924 -0
  11. package/lib/commonjs/storage/components/GameUIStorageStats.js +746 -0
  12. package/lib/commonjs/storage/components/MMKVInstanceInfoPanel.js +257 -0
  13. package/lib/commonjs/storage/components/MMKVInstanceSelector.js +418 -0
  14. package/lib/commonjs/storage/components/SelectionActionBar.js +224 -0
  15. package/lib/commonjs/storage/components/StorageActionButtons.js +239 -0
  16. package/lib/commonjs/storage/components/StorageActions.js +192 -0
  17. package/lib/commonjs/storage/components/StorageBrowserMode.js +31 -0
  18. package/lib/commonjs/storage/components/StorageEventDetailContent.js +1025 -0
  19. package/lib/commonjs/storage/components/StorageEventFilterView.js +141 -0
  20. package/lib/commonjs/storage/components/StorageEventListener.js +357 -0
  21. package/lib/commonjs/storage/components/StorageEventsSection.js +24 -0
  22. package/lib/commonjs/storage/components/StorageFilterCards.js +345 -0
  23. package/lib/commonjs/storage/components/StorageFilterViewV2.js +42 -0
  24. package/lib/commonjs/storage/components/StorageKeyCard.js +516 -0
  25. package/lib/commonjs/storage/components/StorageKeyRow.js +356 -0
  26. package/lib/commonjs/storage/components/StorageKeySection.js +105 -0
  27. package/lib/commonjs/storage/components/StorageKeyStats.js +344 -0
  28. package/lib/commonjs/storage/components/StorageModalWithTabs.js +871 -0
  29. package/lib/commonjs/storage/components/StorageSection.js +43 -0
  30. package/lib/commonjs/storage/hooks/useAsyncStorageKeys.js +126 -0
  31. package/lib/commonjs/storage/hooks/useMMKVInstances.js +221 -0
  32. package/lib/commonjs/storage/hooks/useMMKVKeys.js +362 -0
  33. package/lib/commonjs/storage/hooks/useTickEverySecond.js +21 -0
  34. package/lib/commonjs/storage/index.js +148 -0
  35. package/lib/commonjs/storage/types.js +5 -0
  36. package/lib/commonjs/storage/utils/AsyncStorageListener.js +510 -0
  37. package/lib/commonjs/storage/utils/MMKVInstanceRegistry.js +202 -0
  38. package/lib/commonjs/storage/utils/MMKVListener.js +380 -0
  39. package/lib/commonjs/storage/utils/clearAllStorage.js +47 -0
  40. package/lib/commonjs/storage/utils/index.js +180 -0
  41. package/lib/commonjs/storage/utils/lineDiff.js +363 -0
  42. package/lib/commonjs/storage/utils/mmkvAvailability.js +62 -0
  43. package/lib/commonjs/storage/utils/mmkvTypeDetection.js +139 -0
  44. package/lib/commonjs/storage/utils/objectDiff.js +157 -0
  45. package/lib/commonjs/storage/utils/safeAsyncStorage.js +140 -0
  46. package/lib/commonjs/storage/utils/storageActionHelpers.js +46 -0
  47. package/lib/commonjs/storage/utils/storageQueryUtils.js +35 -0
  48. package/lib/commonjs/storage/utils/valueType.js +18 -0
  49. package/lib/module/index.js +7 -0
  50. package/lib/module/preset.js +89 -0
  51. package/lib/module/storage/components/DiffViewer/DiffOptionsPanel.js +352 -0
  52. package/lib/module/storage/components/DiffViewer/TreeDiffViewer.js +25 -0
  53. package/lib/module/storage/components/DiffViewer/components/DiffSummary.js +117 -0
  54. package/lib/module/storage/components/DiffViewer/modes/ThemedSplitView.js +415 -0
  55. package/lib/module/storage/components/DiffViewer/themes/diffThemes.js +118 -0
  56. package/lib/module/storage/components/GameUIStorageBrowser.js +922 -0
  57. package/lib/module/storage/components/GameUIStorageStats.js +742 -0
  58. package/lib/module/storage/components/MMKVInstanceInfoPanel.js +253 -0
  59. package/lib/module/storage/components/MMKVInstanceSelector.js +414 -0
  60. package/lib/module/storage/components/SelectionActionBar.js +221 -0
  61. package/lib/module/storage/components/StorageActionButtons.js +236 -0
  62. package/lib/module/storage/components/StorageActions.js +189 -0
  63. package/lib/module/storage/components/StorageBrowserMode.js +27 -0
  64. package/lib/module/storage/components/StorageEventDetailContent.js +1020 -0
  65. package/lib/module/storage/components/StorageEventFilterView.js +137 -0
  66. package/lib/module/storage/components/StorageEventListener.js +354 -0
  67. package/lib/module/storage/components/StorageEventsSection.js +20 -0
  68. package/lib/module/storage/components/StorageFilterCards.js +341 -0
  69. package/lib/module/storage/components/StorageFilterViewV2.js +38 -0
  70. package/lib/module/storage/components/StorageKeyCard.js +513 -0
  71. package/lib/module/storage/components/StorageKeyRow.js +353 -0
  72. package/lib/module/storage/components/StorageKeySection.js +101 -0
  73. package/lib/module/storage/components/StorageKeyStats.js +340 -0
  74. package/lib/module/storage/components/StorageModalWithTabs.js +867 -0
  75. package/lib/module/storage/components/StorageSection.js +40 -0
  76. package/lib/module/storage/hooks/useAsyncStorageKeys.js +121 -0
  77. package/lib/module/storage/hooks/useMMKVInstances.js +216 -0
  78. package/lib/module/storage/hooks/useMMKVKeys.js +359 -0
  79. package/lib/module/storage/hooks/useTickEverySecond.js +18 -0
  80. package/lib/module/storage/index.js +25 -0
  81. package/lib/module/storage/types.js +3 -0
  82. package/lib/module/storage/utils/AsyncStorageListener.js +500 -0
  83. package/lib/module/storage/utils/MMKVInstanceRegistry.js +196 -0
  84. package/lib/module/storage/utils/MMKVListener.js +367 -0
  85. package/lib/module/storage/utils/clearAllStorage.js +42 -0
  86. package/lib/module/storage/utils/index.js +22 -0
  87. package/lib/module/storage/utils/lineDiff.js +359 -0
  88. package/lib/module/storage/utils/mmkvAvailability.js +56 -0
  89. package/lib/module/storage/utils/mmkvTypeDetection.js +133 -0
  90. package/lib/module/storage/utils/objectDiff.js +153 -0
  91. package/lib/module/storage/utils/safeAsyncStorage.js +134 -0
  92. package/lib/module/storage/utils/storageActionHelpers.js +42 -0
  93. package/lib/module/storage/utils/storageQueryUtils.js +30 -0
  94. package/lib/module/storage/utils/valueType.js +14 -0
  95. package/lib/typescript/index.d.ts +3 -0
  96. package/lib/typescript/index.d.ts.map +1 -0
  97. package/lib/typescript/preset.d.ts +90 -0
  98. package/lib/typescript/preset.d.ts.map +1 -0
  99. package/lib/typescript/storage/components/DiffViewer/DiffOptionsPanel.d.ts +18 -0
  100. package/lib/typescript/storage/components/DiffViewer/DiffOptionsPanel.d.ts.map +1 -0
  101. package/lib/typescript/storage/components/DiffViewer/TreeDiffViewer.d.ts +7 -0
  102. package/lib/typescript/storage/components/DiffViewer/TreeDiffViewer.d.ts.map +1 -0
  103. package/lib/typescript/storage/components/DiffViewer/components/DiffSummary.d.ts +12 -0
  104. package/lib/typescript/storage/components/DiffViewer/components/DiffSummary.d.ts.map +1 -0
  105. package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts +13 -0
  106. package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts.map +1 -0
  107. package/lib/typescript/storage/components/DiffViewer/themes/diffThemes.d.ts +64 -0
  108. package/lib/typescript/storage/components/DiffViewer/themes/diffThemes.d.ts.map +1 -0
  109. package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts +16 -0
  110. package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts.map +1 -0
  111. package/lib/typescript/storage/components/GameUIStorageStats.d.ts +7 -0
  112. package/lib/typescript/storage/components/GameUIStorageStats.d.ts.map +1 -0
  113. package/lib/typescript/storage/components/MMKVInstanceInfoPanel.d.ts +42 -0
  114. package/lib/typescript/storage/components/MMKVInstanceInfoPanel.d.ts.map +1 -0
  115. package/lib/typescript/storage/components/MMKVInstanceSelector.d.ts +35 -0
  116. package/lib/typescript/storage/components/MMKVInstanceSelector.d.ts.map +1 -0
  117. package/lib/typescript/storage/components/SelectionActionBar.d.ts +21 -0
  118. package/lib/typescript/storage/components/SelectionActionBar.d.ts.map +1 -0
  119. package/lib/typescript/storage/components/StorageActionButtons.d.ts +21 -0
  120. package/lib/typescript/storage/components/StorageActionButtons.d.ts.map +1 -0
  121. package/lib/typescript/storage/components/StorageActions.d.ts +10 -0
  122. package/lib/typescript/storage/components/StorageActions.d.ts.map +1 -0
  123. package/lib/typescript/storage/components/StorageBrowserMode.d.ts +18 -0
  124. package/lib/typescript/storage/components/StorageBrowserMode.d.ts.map +1 -0
  125. package/lib/typescript/storage/components/StorageEventDetailContent.d.ts +40 -0
  126. package/lib/typescript/storage/components/StorageEventDetailContent.d.ts.map +1 -0
  127. package/lib/typescript/storage/components/StorageEventFilterView.d.ts +11 -0
  128. package/lib/typescript/storage/components/StorageEventFilterView.d.ts.map +1 -0
  129. package/lib/typescript/storage/components/StorageEventListener.d.ts +6 -0
  130. package/lib/typescript/storage/components/StorageEventListener.d.ts.map +1 -0
  131. package/lib/typescript/storage/components/StorageEventsSection.d.ts +7 -0
  132. package/lib/typescript/storage/components/StorageEventsSection.d.ts.map +1 -0
  133. package/lib/typescript/storage/components/StorageFilterCards.d.ts +36 -0
  134. package/lib/typescript/storage/components/StorageFilterCards.d.ts.map +1 -0
  135. package/lib/typescript/storage/components/StorageFilterViewV2.d.ts +9 -0
  136. package/lib/typescript/storage/components/StorageFilterViewV2.d.ts.map +1 -0
  137. package/lib/typescript/storage/components/StorageKeyCard.d.ts +17 -0
  138. package/lib/typescript/storage/components/StorageKeyCard.d.ts.map +1 -0
  139. package/lib/typescript/storage/components/StorageKeyRow.d.ts +15 -0
  140. package/lib/typescript/storage/components/StorageKeyRow.d.ts.map +1 -0
  141. package/lib/typescript/storage/components/StorageKeySection.d.ts +25 -0
  142. package/lib/typescript/storage/components/StorageKeySection.d.ts.map +1 -0
  143. package/lib/typescript/storage/components/StorageKeyStats.d.ts +15 -0
  144. package/lib/typescript/storage/components/StorageKeyStats.d.ts.map +1 -0
  145. package/lib/typescript/storage/components/StorageModalWithTabs.d.ts +13 -0
  146. package/lib/typescript/storage/components/StorageModalWithTabs.d.ts.map +1 -0
  147. package/lib/typescript/storage/components/StorageSection.d.ts +10 -0
  148. package/lib/typescript/storage/components/StorageSection.d.ts.map +1 -0
  149. package/lib/typescript/storage/hooks/useAsyncStorageKeys.d.ts +10 -0
  150. package/lib/typescript/storage/hooks/useAsyncStorageKeys.d.ts.map +1 -0
  151. package/lib/typescript/storage/hooks/useMMKVInstances.d.ts +114 -0
  152. package/lib/typescript/storage/hooks/useMMKVInstances.d.ts.map +1 -0
  153. package/lib/typescript/storage/hooks/useMMKVKeys.d.ts +94 -0
  154. package/lib/typescript/storage/hooks/useMMKVKeys.d.ts.map +1 -0
  155. package/lib/typescript/storage/hooks/useTickEverySecond.d.ts +6 -0
  156. package/lib/typescript/storage/hooks/useTickEverySecond.d.ts.map +1 -0
  157. package/lib/typescript/storage/index.d.ts +15 -0
  158. package/lib/typescript/storage/index.d.ts.map +1 -0
  159. package/lib/typescript/storage/types.d.ts +41 -0
  160. package/lib/typescript/storage/types.d.ts.map +1 -0
  161. package/lib/typescript/storage/utils/AsyncStorageListener.d.ts +195 -0
  162. package/lib/typescript/storage/utils/AsyncStorageListener.d.ts.map +1 -0
  163. package/lib/typescript/storage/utils/MMKVInstanceRegistry.d.ts +224 -0
  164. package/lib/typescript/storage/utils/MMKVInstanceRegistry.d.ts.map +1 -0
  165. package/lib/typescript/storage/utils/MMKVListener.d.ts +218 -0
  166. package/lib/typescript/storage/utils/MMKVListener.d.ts.map +1 -0
  167. package/lib/typescript/storage/utils/clearAllStorage.d.ts +11 -0
  168. package/lib/typescript/storage/utils/clearAllStorage.d.ts.map +1 -0
  169. package/lib/typescript/storage/utils/index.d.ts +8 -0
  170. package/lib/typescript/storage/utils/index.d.ts.map +1 -0
  171. package/lib/typescript/storage/utils/lineDiff.d.ts +34 -0
  172. package/lib/typescript/storage/utils/lineDiff.d.ts.map +1 -0
  173. package/lib/typescript/storage/utils/mmkvAvailability.d.ts +23 -0
  174. package/lib/typescript/storage/utils/mmkvAvailability.d.ts.map +1 -0
  175. package/lib/typescript/storage/utils/mmkvTypeDetection.d.ts +71 -0
  176. package/lib/typescript/storage/utils/mmkvTypeDetection.d.ts.map +1 -0
  177. package/lib/typescript/storage/utils/objectDiff.d.ts +35 -0
  178. package/lib/typescript/storage/utils/objectDiff.d.ts.map +1 -0
  179. package/lib/typescript/storage/utils/safeAsyncStorage.d.ts +56 -0
  180. package/lib/typescript/storage/utils/safeAsyncStorage.d.ts.map +1 -0
  181. package/lib/typescript/storage/utils/storageActionHelpers.d.ts +5 -0
  182. package/lib/typescript/storage/utils/storageActionHelpers.d.ts.map +1 -0
  183. package/lib/typescript/storage/utils/storageQueryUtils.d.ts +6 -0
  184. package/lib/typescript/storage/utils/storageQueryUtils.d.ts.map +1 -0
  185. package/lib/typescript/storage/utils/valueType.d.ts +3 -0
  186. package/lib/typescript/storage/utils/valueType.d.ts.map +1 -0
  187. package/package.json +68 -0
@@ -0,0 +1,867 @@
1
+ "use strict";
2
+
3
+ import { useState, useCallback, useEffect, useRef, useMemo } from "react";
4
+ import { Text, View, TouchableOpacity, StyleSheet, FlatList, Alert } from "react-native";
5
+ import AsyncStorage from "@react-native-async-storage/async-storage";
6
+ import { JsModal, ModalHeader, TabSelector, ValueTypeBadge, StorageTypeBadge, formatRelativeTime, parseValue, devToolsStorageKeys, macOSColors, Database, Pause, Play, Trash2, Filter, Search, SearchBar } from "@buoy-gg/shared-ui";
7
+ import { StorageBrowserMode } from "./StorageBrowserMode";
8
+ import { clearAllAppStorage, clearAllStorageIncludingDevTools } from "../utils/clearAllStorage";
9
+ import { ProFeatureBanner } from "@buoy-gg/shared-ui";
10
+ import { startListening, stopListening, addListener, isListening as checkIsListening } from "../utils/AsyncStorageListener";
11
+ import { StorageEventDetailContent, StorageEventDetailFooter } from "./StorageEventDetailContent";
12
+ import { StorageFilterViewV2 } from "./StorageFilterViewV2";
13
+ import { StorageEventFilterView } from "./StorageEventFilterView";
14
+ import { translateStorageAction } from "../utils/storageActionHelpers";
15
+ import { isMMKVAvailable } from "../utils/mmkvAvailability";
16
+
17
+ // Conditionally import MMKV listener
18
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
19
+ let addMMKVListener;
20
+ if (isMMKVAvailable()) {
21
+ const mmkvListener = require("../utils/MMKVListener");
22
+ addMMKVListener = mmkvListener.addMMKVListener;
23
+ }
24
+
25
+ // Unified storage event type
26
+
27
+ // Lazy load the license hooks to avoid circular dependencies
28
+ let _useIsPro = null;
29
+ let _licenseLoadAttempted = false;
30
+ function loadLicenseModule() {
31
+ if (_licenseLoadAttempted) return;
32
+ _licenseLoadAttempted = true;
33
+ try {
34
+ const mod = require("@buoy-gg/license");
35
+ if (mod) {
36
+ _useIsPro = mod.useIsPro ?? null;
37
+ }
38
+ } catch {
39
+ // License package not available
40
+ }
41
+ }
42
+ function getUseIsPro() {
43
+ loadLicenseModule();
44
+ return _useIsPro ?? (() => false);
45
+ }
46
+
47
+ // Free tier limit for storage events/conversations
48
+ export const FREE_TIER_EVENT_LIMIT = 25;
49
+ export function StorageModalWithTabs({
50
+ visible,
51
+ onClose,
52
+ onBack,
53
+ onMinimize,
54
+ enableSharedModalDimensions = false,
55
+ requiredStorageKeys = []
56
+ }) {
57
+ // Check Pro status internally
58
+ const useIsPro = getUseIsPro();
59
+ const isPro = useIsPro();
60
+ const [activeTab, setActiveTab] = useState("browser");
61
+
62
+ // Storage Browser state
63
+ const [showStorageFilters, setShowStorageFilters] = useState(false);
64
+ const [storageIgnoredPatterns, setStorageIgnoredPatterns] = useState(new Set(["@react_buoy"]) // Auto-hide dev tool keys by default
65
+ );
66
+ const [searchQuery, setSearchQuery] = useState("");
67
+ const [isSearchActive, setIsSearchActive] = useState(false);
68
+ const hasLoadedStorageFilters = useRef(false);
69
+
70
+ // Event Listener state
71
+ const [events, setEvents] = useState([]);
72
+ const [isListening, setIsListening] = useState(false);
73
+ const [selectedConversationKey, setSelectedConversationKey] = useState(null);
74
+ const [selectedEventIndex, setSelectedEventIndex] = useState(0);
75
+ const [showFilters, setShowFilters] = useState(false);
76
+ const [ignoredPatterns, setIgnoredPatterns] = useState(new Set(["@RNAsyncStorage", "redux-persist", "persist:"]) // Only show @react_buoy events by default
77
+ );
78
+ const [enabledStorageTypes, setEnabledStorageTypes] = useState(new Set(['async', 'mmkv', 'secure']) // All enabled by default
79
+ );
80
+ const lastEventRef = useRef(null);
81
+ const hasLoadedFilters = useRef(false);
82
+ const hasLoadedTabState = useRef(false);
83
+ const hasLoadedMonitoringState = useRef(false);
84
+ const handleModeChange = useCallback(_mode => {
85
+ // Mode changes handled by JsModal
86
+ }, []);
87
+
88
+ // Timer removed - using useTickEveryMinute hook instead
89
+
90
+ // Load persisted tab state on mount
91
+ useEffect(() => {
92
+ if (!visible || hasLoadedTabState.current) return;
93
+ const loadTabState = async () => {
94
+ try {
95
+ const storedTab = await AsyncStorage.getItem(devToolsStorageKeys.storage.activeTab());
96
+ if (storedTab && (storedTab === "browser" || storedTab === "events")) {
97
+ setActiveTab(storedTab);
98
+ }
99
+ hasLoadedTabState.current = true;
100
+ } catch (error) {
101
+ // Failed to load tab state
102
+ }
103
+ };
104
+ loadTabState();
105
+ }, [visible]);
106
+
107
+ // Load persisted monitoring state on mount
108
+ useEffect(() => {
109
+ if (!visible || hasLoadedMonitoringState.current) return;
110
+ const loadMonitoringState = async () => {
111
+ try {
112
+ const storedMonitoring = await AsyncStorage.getItem(devToolsStorageKeys.storage.isMonitoring());
113
+ if (storedMonitoring !== null) {
114
+ const shouldMonitor = storedMonitoring === "true";
115
+ if (shouldMonitor && !checkIsListening()) {
116
+ await startListening();
117
+ setIsListening(true);
118
+ }
119
+ }
120
+ hasLoadedMonitoringState.current = true;
121
+ } catch (error) {
122
+ // Failed to load monitoring state
123
+ }
124
+ };
125
+ loadMonitoringState();
126
+ }, [visible]);
127
+
128
+ // Note: Conversations will appear when storage events are triggered
129
+ // Click on any conversation to see the unified view with toggle cards
130
+
131
+ // Save tab state when it changes
132
+ useEffect(() => {
133
+ if (!hasLoadedTabState.current) return; // Don't save on initial load
134
+
135
+ const saveTabState = async () => {
136
+ try {
137
+ await AsyncStorage.setItem(devToolsStorageKeys.storage.activeTab(), activeTab);
138
+ } catch (error) {
139
+ // Failed to save tab state
140
+ }
141
+ };
142
+ saveTabState();
143
+ }, [activeTab]);
144
+
145
+ // Save monitoring state when it changes
146
+ useEffect(() => {
147
+ if (!hasLoadedMonitoringState.current) return; // Don't save on initial load
148
+
149
+ const saveMonitoringState = async () => {
150
+ try {
151
+ await AsyncStorage.setItem(devToolsStorageKeys.storage.isMonitoring(), isListening.toString());
152
+ } catch (error) {
153
+ // Failed to save monitoring state
154
+ }
155
+ };
156
+ saveMonitoringState();
157
+ }, [isListening]);
158
+
159
+ // Load persisted filters on mount
160
+ useEffect(() => {
161
+ if (!visible || hasLoadedFilters.current) return;
162
+ const loadFilters = async () => {
163
+ try {
164
+ const storedFilters = await AsyncStorage.getItem(devToolsStorageKeys.storage.eventFilters());
165
+ if (storedFilters) {
166
+ const filters = JSON.parse(storedFilters);
167
+ setIgnoredPatterns(new Set(filters));
168
+ }
169
+ hasLoadedFilters.current = true;
170
+ } catch (error) {
171
+ // Failed to load filters
172
+ }
173
+ };
174
+ loadFilters();
175
+ }, [visible]);
176
+
177
+ // Save filters when they change
178
+ useEffect(() => {
179
+ if (!hasLoadedFilters.current) return; // Don't save on initial load
180
+
181
+ const saveFilters = async () => {
182
+ try {
183
+ const filters = Array.from(ignoredPatterns);
184
+ await AsyncStorage.setItem(devToolsStorageKeys.storage.eventFilters(), JSON.stringify(filters));
185
+ } catch (error) {
186
+ // Failed to save filters
187
+ }
188
+ };
189
+ saveFilters();
190
+ }, [ignoredPatterns]);
191
+
192
+ // Sync isListening state with actual listener state on mount
193
+ useEffect(() => {
194
+ if (!visible) return;
195
+
196
+ // Check if already listening and sync state
197
+ const listening = checkIsListening();
198
+ setIsListening(listening);
199
+ }, [visible]);
200
+
201
+ // Event listener setup - only collect events when isListening is true
202
+ useEffect(() => {
203
+ if (!visible || !isListening) return;
204
+
205
+ // Set up AsyncStorage event listener
206
+ const unsubscribeAsync = addListener(event => {
207
+ const storageEvent = {
208
+ ...event,
209
+ storageType: 'async'
210
+ };
211
+ lastEventRef.current = storageEvent;
212
+ setEvents(prev => {
213
+ const updated = [storageEvent, ...prev];
214
+ return updated.slice(0, 500);
215
+ });
216
+ });
217
+
218
+ // Set up MMKV event listener (if available)
219
+ let unsubscribeMMKV = () => {};
220
+ if (isMMKVAvailable() && addMMKVListener) {
221
+ unsubscribeMMKV = addMMKVListener(event => {
222
+ const storageEvent = {
223
+ ...event,
224
+ storageType: 'mmkv'
225
+ };
226
+ lastEventRef.current = storageEvent;
227
+ setEvents(prev => {
228
+ const updated = [storageEvent, ...prev];
229
+ return updated.slice(0, 500);
230
+ });
231
+ });
232
+ }
233
+ return () => {
234
+ unsubscribeAsync();
235
+ unsubscribeMMKV();
236
+ };
237
+ }, [visible, isListening]);
238
+ const handleToggleListening = useCallback(async () => {
239
+ if (isListening) {
240
+ stopListening();
241
+ setIsListening(false);
242
+ } else {
243
+ await startListening();
244
+ setIsListening(true);
245
+ }
246
+ }, [isListening]);
247
+ const handleClearEvents = useCallback(() => {
248
+ setEvents([]);
249
+ setSelectedConversationKey(null);
250
+ }, []);
251
+ const handleConversationPress = useCallback(conversation => {
252
+ setSelectedConversationKey(conversation.key);
253
+ setSelectedEventIndex(0);
254
+ }, []);
255
+ const handleTogglePattern = useCallback(pattern => {
256
+ setIgnoredPatterns(prev => {
257
+ const next = new Set(prev);
258
+ if (next.has(pattern)) {
259
+ next.delete(pattern);
260
+ } else {
261
+ next.add(pattern);
262
+ }
263
+ return next;
264
+ });
265
+ }, []);
266
+ const handleAddPattern = useCallback(pattern => {
267
+ setIgnoredPatterns(prev => new Set([...prev, pattern]));
268
+ }, []);
269
+ const handleToggleFilters = useCallback(() => {
270
+ setShowFilters(!showFilters);
271
+ }, [showFilters]);
272
+ const handleToggleStorageType = useCallback(type => {
273
+ setEnabledStorageTypes(prev => {
274
+ const next = new Set(prev);
275
+ if (next.has(type)) {
276
+ next.delete(type);
277
+ } else {
278
+ next.add(type);
279
+ }
280
+ return next;
281
+ });
282
+ }, []);
283
+
284
+ // Storage Browser handlers
285
+ const storageDataRef = useRef([]);
286
+ const handleToggleStorageFilters = useCallback(() => {
287
+ setShowStorageFilters(!showStorageFilters);
288
+ }, [showStorageFilters]);
289
+ const handleToggleStoragePattern = useCallback(pattern => {
290
+ setStorageIgnoredPatterns(prev => {
291
+ const next = new Set(prev);
292
+ if (next.has(pattern)) {
293
+ next.delete(pattern);
294
+ } else {
295
+ next.add(pattern);
296
+ }
297
+ return next;
298
+ });
299
+ }, []);
300
+ const handleAddStoragePattern = useCallback(pattern => {
301
+ setStorageIgnoredPatterns(prev => new Set([...prev, pattern]));
302
+ }, []);
303
+ const handlePurgeStorage = useCallback(async () => {
304
+ Alert.alert("Clear Storage", "Choose what to clear:", [{
305
+ text: "Cancel",
306
+ style: "cancel"
307
+ }, {
308
+ text: "Clear App Storage",
309
+ onPress: async () => {
310
+ try {
311
+ await clearAllAppStorage();
312
+ // Refresh will be handled by GameUIStorageBrowser
313
+ } catch (error) {
314
+ Alert.alert("Error", "Failed to clear app storage");
315
+ }
316
+ }
317
+ }, {
318
+ text: "Clear All (Including Dev Tools)",
319
+ style: "destructive",
320
+ onPress: async () => {
321
+ try {
322
+ await clearAllStorageIncludingDevTools();
323
+ // Refresh will be handled by GameUIStorageBrowser
324
+ } catch (error) {
325
+ Alert.alert("Error", "Failed to clear all storage");
326
+ }
327
+ }
328
+ }]);
329
+ }, []);
330
+ const getValueType = value => {
331
+ const parsed = parseValue(value);
332
+ if (parsed === null) return "null";
333
+ if (parsed === undefined) return "undefined";
334
+ if (Array.isArray(parsed)) return "array";
335
+ if (typeof parsed === "boolean") return "boolean";
336
+ if (typeof parsed === "number") return "number";
337
+ if (typeof parsed === "string") return "string";
338
+ if (typeof parsed === "object") return "object";
339
+ return "undefined";
340
+ };
341
+
342
+ // Get all unique keys from events AND from AsyncStorage for filter view
343
+ const [allStorageKeys, setAllStorageKeys] = useState([]);
344
+ useEffect(() => {
345
+ const fetchAllKeys = async () => {
346
+ try {
347
+ const keys = await AsyncStorage.getAllKeys();
348
+ setAllStorageKeys([...keys].sort());
349
+ } catch (error) {
350
+ // Failed to fetch keys
351
+ }
352
+ };
353
+
354
+ // Fetch all keys for both tabs when visible
355
+ if (visible) {
356
+ fetchAllKeys();
357
+ }
358
+ }, [visible]);
359
+ const allEventKeys = useMemo(() => {
360
+ // Combine storage keys with event keys for comprehensive filter view
361
+ const keys = new Set(allStorageKeys);
362
+ events.forEach(event => {
363
+ if (event.data?.key) {
364
+ keys.add(event.data.key);
365
+ }
366
+ });
367
+ return Array.from(keys).sort();
368
+ }, [events, allStorageKeys]);
369
+
370
+ // Group events by key and create conversations
371
+ const conversations = useMemo(() => {
372
+ const keyMap = new Map();
373
+ events.forEach(event => {
374
+ if (!event.data?.key) return;
375
+ const key = event.data.key;
376
+
377
+ // Filter by enabled storage types
378
+ if (!enabledStorageTypes.has(event.storageType)) return;
379
+
380
+ // Filter out keys that match ignored patterns
381
+ const shouldIgnore = Array.from(ignoredPatterns).some(pattern => key.includes(pattern));
382
+ if (shouldIgnore) return;
383
+ const existing = keyMap.get(key);
384
+ if (!existing) {
385
+ keyMap.set(key, {
386
+ key,
387
+ lastEvent: event,
388
+ events: [event],
389
+ totalOperations: 1,
390
+ currentValue: event.data.value,
391
+ valueType: getValueType(event.data.value),
392
+ storageTypes: new Set([event.storageType])
393
+ });
394
+ } else {
395
+ existing.events.push(event);
396
+ existing.totalOperations++;
397
+ existing.storageTypes.add(event.storageType);
398
+
399
+ // Update last event if this one is newer
400
+ if (event.timestamp > existing.lastEvent.timestamp) {
401
+ existing.lastEvent = event;
402
+ existing.currentValue = event.data.value;
403
+ existing.valueType = getValueType(event.data.value);
404
+ }
405
+ }
406
+ });
407
+
408
+ // Convert to array and sort by last updated
409
+ return Array.from(keyMap.values()).sort((a, b) => b.lastEvent.timestamp.getTime() - a.lastEvent.timestamp.getTime());
410
+ }, [events, ignoredPatterns, enabledStorageTypes]);
411
+
412
+ // Get the live selected conversation from the current conversations array
413
+ const selectedConversation = useMemo(() => {
414
+ if (!selectedConversationKey) return null;
415
+ return conversations.find(c => c.key === selectedConversationKey) || null;
416
+ }, [selectedConversationKey, conversations]);
417
+
418
+ // For free users, limit visible conversations
419
+ const visibleConversations = useMemo(() => {
420
+ if (isPro) return conversations;
421
+ return conversations.slice(0, FREE_TIER_EVENT_LIMIT);
422
+ }, [conversations, isPro]);
423
+ const lockedConversationCount = useMemo(() => {
424
+ if (isPro) return 0;
425
+ return Math.max(0, conversations.length - FREE_TIER_EVENT_LIMIT);
426
+ }, [conversations.length, isPro]);
427
+ const getActionColor = action => {
428
+ switch (action) {
429
+ // AsyncStorage - Set operations
430
+ case "setItem":
431
+ case "multiSet":
432
+ // MMKV - Set operations
433
+ // falls through
434
+ case "set.string":
435
+ case "set.number":
436
+ case "set.boolean":
437
+ case "set.buffer":
438
+ return macOSColors.semantic.success;
439
+
440
+ // AsyncStorage - Remove operations
441
+ case "removeItem":
442
+ case "multiRemove":
443
+ case "clear":
444
+ // MMKV - Delete operations
445
+ // falls through
446
+ case "delete":
447
+ case "clearAll":
448
+ return macOSColors.semantic.error;
449
+
450
+ // AsyncStorage - Merge operations
451
+ case "mergeItem":
452
+ case "multiMerge":
453
+ return macOSColors.semantic.info;
454
+
455
+ // MMKV - Get operations
456
+ case "get.string":
457
+ case "get.number":
458
+ case "get.boolean":
459
+ case "get.buffer":
460
+ return macOSColors.semantic.warning;
461
+ default:
462
+ return macOSColors.text.muted;
463
+ }
464
+ };
465
+
466
+ // FlatList optimization constants
467
+ const END_REACHED_THRESHOLD = 0.8;
468
+
469
+ // Stable keyExtractor for FlatList
470
+ const keyExtractor = useCallback(item => {
471
+ return item.key;
472
+ }, []);
473
+
474
+ // Removed getItemType as it's FlatList-specific
475
+
476
+ // Create stable ref for event handler
477
+ const selectConversationRef = useRef(undefined);
478
+ selectConversationRef.current = handleConversationPress;
479
+
480
+ // Stable renderItem with ref pattern
481
+ const renderConversationItem = useCallback(({
482
+ item
483
+ }) => {
484
+ return /*#__PURE__*/_jsxs(TouchableOpacity, {
485
+ onPress: () => selectConversationRef.current?.(item),
486
+ style: styles.conversationItem,
487
+ children: [/*#__PURE__*/_jsxs(View, {
488
+ style: styles.conversationHeader,
489
+ children: [/*#__PURE__*/_jsx(Text, {
490
+ style: styles.keyText,
491
+ numberOfLines: 1,
492
+ children: item.key
493
+ }), /*#__PURE__*/_jsx(Text, {
494
+ style: [styles.actionText, {
495
+ color: getActionColor(item.lastEvent.action)
496
+ }],
497
+ children: translateStorageAction(item.lastEvent.action)
498
+ })]
499
+ }), /*#__PURE__*/_jsxs(View, {
500
+ style: styles.conversationDetails,
501
+ children: [item.storageTypes && Array.from(item.storageTypes).map(storageType => /*#__PURE__*/_jsx(StorageTypeBadge, {
502
+ type: storageType
503
+ }, storageType)), /*#__PURE__*/_jsx(ValueTypeBadge, {
504
+ type: item.valueType
505
+ }), /*#__PURE__*/_jsxs(Text, {
506
+ style: styles.operationCount,
507
+ children: [item.totalOperations, " operation", item.totalOperations !== 1 ? "s" : ""]
508
+ }), /*#__PURE__*/_jsx(Text, {
509
+ style: styles.timestamp,
510
+ children: formatRelativeTime(item.lastEvent.timestamp)
511
+ })]
512
+ })]
513
+ });
514
+ }, []);
515
+ if (!visible) return null;
516
+ const persistenceKey = enableSharedModalDimensions ? devToolsStorageKeys.modal.root() : devToolsStorageKeys.storage.modal();
517
+ const renderContent = () => {
518
+ if (activeTab === "browser") {
519
+ if (showStorageFilters) {
520
+ return /*#__PURE__*/_jsx(StorageFilterViewV2, {
521
+ ignoredPatterns: storageIgnoredPatterns,
522
+ onTogglePattern: handleToggleStoragePattern,
523
+ onAddPattern: handleAddStoragePattern,
524
+ availableKeys: allStorageKeys
525
+ });
526
+ }
527
+ return /*#__PURE__*/_jsx(StorageBrowserMode, {
528
+ requiredStorageKeys: requiredStorageKeys,
529
+ showFilters: showStorageFilters,
530
+ ignoredPatterns: storageIgnoredPatterns,
531
+ onTogglePattern: handleToggleStoragePattern,
532
+ onAddPattern: handleAddStoragePattern,
533
+ searchQuery: searchQuery,
534
+ storageDataRef: storageDataRef
535
+ });
536
+ }
537
+
538
+ // Events tab content
539
+ if (selectedConversation) {
540
+ return /*#__PURE__*/_jsx(View, {
541
+ style: styles.contentWrapper,
542
+ children: /*#__PURE__*/_jsx(StorageEventDetailContent, {
543
+ conversation: selectedConversation,
544
+ selectedEventIndex: selectedEventIndex,
545
+ onEventIndexChange: setSelectedEventIndex,
546
+ disableInternalFooter: true
547
+ })
548
+ });
549
+ }
550
+ if (showFilters) {
551
+ return /*#__PURE__*/_jsx(StorageEventFilterView, {
552
+ ignoredPatterns: ignoredPatterns,
553
+ onTogglePattern: handleTogglePattern,
554
+ onAddPattern: handleAddPattern,
555
+ availableKeys: allEventKeys,
556
+ enabledStorageTypes: enabledStorageTypes,
557
+ onToggleStorageType: handleToggleStorageType
558
+ });
559
+ }
560
+ if (conversations.length === 0) {
561
+ return /*#__PURE__*/_jsxs(View, {
562
+ style: styles.emptyState,
563
+ children: [/*#__PURE__*/_jsx(Database, {
564
+ size: 48,
565
+ color: macOSColors.text.muted
566
+ }), /*#__PURE__*/_jsx(Text, {
567
+ style: styles.emptyTitle,
568
+ children: isListening ? "No storage events yet" : "Event listener is paused"
569
+ }), /*#__PURE__*/_jsx(Text, {
570
+ style: styles.emptySubtitle,
571
+ children: isListening ? "Storage operations will appear here" : "Press play to start monitoring"
572
+ })]
573
+ });
574
+ }
575
+ return /*#__PURE__*/_jsx(FlatList, {
576
+ data: visibleConversations,
577
+ renderItem: renderConversationItem,
578
+ keyExtractor: keyExtractor,
579
+ onEndReachedThreshold: END_REACHED_THRESHOLD,
580
+ contentContainerStyle: styles.listContent,
581
+ ItemSeparatorComponent: () => /*#__PURE__*/_jsx(View, {
582
+ style: styles.separator
583
+ }),
584
+ ListFooterComponent: lockedConversationCount > 0 ? /*#__PURE__*/_jsx(View, {
585
+ style: styles.lockedBannerContainer,
586
+ children: /*#__PURE__*/_jsx(ProFeatureBanner, {
587
+ featureName: `${lockedConversationCount} event${lockedConversationCount > 1 ? 's' : ''} locked`,
588
+ description: "Upgrade to Pro to unlock full event history"
589
+ })
590
+ }) : null,
591
+ initialNumToRender: 10,
592
+ maxToRenderPerBatch: 10,
593
+ windowSize: 10,
594
+ scrollEnabled: false
595
+ });
596
+ };
597
+ const footerNode = selectedConversation ? /*#__PURE__*/_jsx(StorageEventDetailFooter, {
598
+ conversation: selectedConversation,
599
+ selectedEventIndex: selectedEventIndex,
600
+ onEventIndexChange: setSelectedEventIndex
601
+ }) : null;
602
+
603
+ // Determine the appropriate back handler based on current view state
604
+ const currentBackHandler = showStorageFilters ? () => setShowStorageFilters(false) : showFilters ? () => setShowFilters(false) : selectedConversation ? () => {
605
+ setSelectedConversationKey(null);
606
+ setSelectedEventIndex(0);
607
+ } : onBack;
608
+ return /*#__PURE__*/_jsx(JsModal, {
609
+ visible: visible,
610
+ onClose: onClose,
611
+ onBack: currentBackHandler,
612
+ onMinimize: onMinimize,
613
+ persistenceKey: persistenceKey,
614
+ header: {
615
+ showToggleButton: true,
616
+ customContent: showStorageFilters ? /*#__PURE__*/_jsxs(ModalHeader, {
617
+ children: [/*#__PURE__*/_jsx(ModalHeader.Navigation, {
618
+ onBack: () => setShowStorageFilters(false)
619
+ }), /*#__PURE__*/_jsx(ModalHeader.Content, {
620
+ title: "Filters"
621
+ })]
622
+ }) : showFilters ? /*#__PURE__*/_jsxs(ModalHeader, {
623
+ children: [/*#__PURE__*/_jsx(ModalHeader.Navigation, {
624
+ onBack: () => setShowFilters(false)
625
+ }), /*#__PURE__*/_jsx(ModalHeader.Content, {
626
+ title: "Event Filters"
627
+ })]
628
+ }) : selectedConversation ? /*#__PURE__*/_jsxs(ModalHeader, {
629
+ children: [/*#__PURE__*/_jsx(ModalHeader.Navigation, {
630
+ onBack: () => {
631
+ setSelectedConversationKey(null);
632
+ setSelectedEventIndex(0);
633
+ }
634
+ }), /*#__PURE__*/_jsx(ModalHeader.Content, {
635
+ title: selectedConversation.key
636
+ })]
637
+ }) : /*#__PURE__*/_jsxs(ModalHeader, {
638
+ children: [onBack && /*#__PURE__*/_jsx(ModalHeader.Navigation, {
639
+ onBack: onBack
640
+ }), /*#__PURE__*/_jsx(ModalHeader.Content, {
641
+ title: "",
642
+ children: isSearchActive ? /*#__PURE__*/_jsx(SearchBar, {
643
+ value: searchQuery,
644
+ onChange: setSearchQuery,
645
+ onClear: () => {
646
+ setSearchQuery("");
647
+ setIsSearchActive(false);
648
+ },
649
+ placeholder: "Search storage keys...",
650
+ autoFocus: true,
651
+ onSubmitEditing: () => setIsSearchActive(false),
652
+ returnKeyType: "search",
653
+ suggestions: allStorageKeys,
654
+ containerStyle: styles.headerSearchContainer
655
+ }) : /*#__PURE__*/_jsx(TabSelector, {
656
+ tabs: [{
657
+ key: "browser",
658
+ label: "Storage"
659
+ }, {
660
+ key: "events",
661
+ label: `Events${events.length > 0 && activeTab !== "events" ? ` (${events.length})` : ""}`
662
+ }],
663
+ activeTab: activeTab,
664
+ onTabChange: tab => setActiveTab(tab)
665
+ })
666
+ }), /*#__PURE__*/_jsxs(ModalHeader.Actions, {
667
+ children: [activeTab === "browser" && !isSearchActive && /*#__PURE__*/_jsxs(_Fragment, {
668
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
669
+ onPress: () => setIsSearchActive(true),
670
+ style: styles.iconButton,
671
+ children: /*#__PURE__*/_jsx(Search, {
672
+ size: 14,
673
+ color: macOSColors.text.secondary
674
+ })
675
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
676
+ onPress: handleToggleStorageFilters,
677
+ style: [styles.iconButton, storageIgnoredPatterns.size > 0 && styles.activeFilterButton],
678
+ children: /*#__PURE__*/_jsx(Filter, {
679
+ size: 14,
680
+ color: storageIgnoredPatterns.size > 0 ? macOSColors.semantic.debug : macOSColors.text.secondary
681
+ })
682
+ })]
683
+ }), activeTab === "events" && /*#__PURE__*/_jsxs(_Fragment, {
684
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
685
+ onPress: handleToggleFilters,
686
+ style: [styles.iconButton, ignoredPatterns.size > 0 && styles.activeFilterButton],
687
+ children: /*#__PURE__*/_jsx(Filter, {
688
+ size: 14,
689
+ color: ignoredPatterns.size > 0 ? macOSColors.semantic.debug : macOSColors.text.secondary
690
+ })
691
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
692
+ onPress: handleToggleListening,
693
+ style: [styles.iconButton, isListening && styles.activeButton],
694
+ children: isListening ? /*#__PURE__*/_jsx(Pause, {
695
+ size: 14,
696
+ color: macOSColors.semantic.success
697
+ }) : /*#__PURE__*/_jsx(Play, {
698
+ size: 14,
699
+ color: macOSColors.semantic.success
700
+ })
701
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
702
+ onPress: handleClearEvents,
703
+ style: styles.iconButton,
704
+ children: /*#__PURE__*/_jsx(Trash2, {
705
+ size: 14,
706
+ color: macOSColors.semantic.error
707
+ })
708
+ })]
709
+ })]
710
+ })]
711
+ })
712
+ },
713
+ onModeChange: handleModeChange,
714
+ enablePersistence: true,
715
+ initialMode: "bottomSheet",
716
+ enableGlitchEffects: true,
717
+ styles: {},
718
+ footer: footerNode,
719
+ footerHeight: footerNode ? 68 : 0,
720
+ children: renderContent()
721
+ });
722
+ }
723
+ const styles = StyleSheet.create({
724
+ headerSearchContainer: {
725
+ flex: 1
726
+ },
727
+ iconButton: {
728
+ width: 32,
729
+ height: 32,
730
+ borderRadius: 8,
731
+ backgroundColor: macOSColors.background.hover,
732
+ borderWidth: 1,
733
+ borderColor: macOSColors.border.default,
734
+ alignItems: "center",
735
+ justifyContent: "center"
736
+ },
737
+ activeButton: {
738
+ backgroundColor: macOSColors.semantic.successBackground
739
+ },
740
+ activeFilterButton: {
741
+ backgroundColor: macOSColors.semantic.infoBackground
742
+ },
743
+ conversationItem: {
744
+ padding: 12,
745
+ backgroundColor: macOSColors.background.card,
746
+ borderRadius: 8,
747
+ marginHorizontal: 16
748
+ },
749
+ conversationHeader: {
750
+ flexDirection: "row",
751
+ justifyContent: "space-between",
752
+ alignItems: "center",
753
+ marginBottom: 8
754
+ },
755
+ keyText: {
756
+ color: macOSColors.text.primary,
757
+ fontSize: 14,
758
+ fontWeight: "600",
759
+ flex: 1,
760
+ marginRight: 8,
761
+ fontFamily: "monospace"
762
+ },
763
+ actionText: {
764
+ fontSize: 11,
765
+ fontWeight: "600",
766
+ fontFamily: "monospace",
767
+ textTransform: "uppercase"
768
+ },
769
+ conversationDetails: {
770
+ flexDirection: "row",
771
+ alignItems: "center",
772
+ gap: 8
773
+ },
774
+ operationCount: {
775
+ color: macOSColors.text.secondary,
776
+ fontSize: 11,
777
+ flex: 1,
778
+ fontFamily: "monospace"
779
+ },
780
+ timestamp: {
781
+ color: macOSColors.text.muted,
782
+ fontSize: 11,
783
+ fontFamily: "monospace"
784
+ },
785
+ separator: {
786
+ height: 8
787
+ },
788
+ listContent: {
789
+ paddingVertical: 16
790
+ },
791
+ lockedBannerContainer: {
792
+ marginTop: 8,
793
+ marginHorizontal: 16
794
+ },
795
+ emptyState: {
796
+ flex: 1,
797
+ justifyContent: "center",
798
+ alignItems: "center",
799
+ padding: 32
800
+ },
801
+ emptyTitle: {
802
+ color: macOSColors.text.primary,
803
+ fontSize: 16,
804
+ fontWeight: "600",
805
+ marginTop: 16,
806
+ marginBottom: 8,
807
+ fontFamily: "monospace",
808
+ letterSpacing: 0.5,
809
+ textTransform: "uppercase"
810
+ },
811
+ emptySubtitle: {
812
+ color: macOSColors.text.secondary,
813
+ fontSize: 14,
814
+ textAlign: "center",
815
+ fontFamily: "monospace"
816
+ },
817
+ eventNavigation: {
818
+ flexDirection: "row",
819
+ alignItems: "center",
820
+ gap: 8,
821
+ paddingHorizontal: 8
822
+ },
823
+ navButton: {
824
+ padding: 4,
825
+ borderRadius: 4
826
+ },
827
+ navButtonDisabled: {
828
+ opacity: 0.3
829
+ },
830
+ eventCounter: {
831
+ color: macOSColors.text.primary,
832
+ fontSize: 12,
833
+ fontWeight: "600",
834
+ fontFamily: "monospace",
835
+ paddingHorizontal: 8
836
+ },
837
+ headerTopRow: {
838
+ flexDirection: "row",
839
+ alignItems: "center",
840
+ gap: 8,
841
+ marginBottom: 12
842
+ },
843
+ keyNameContainer: {
844
+ flex: 1,
845
+ flexDirection: "row",
846
+ alignItems: "center",
847
+ gap: 6,
848
+ backgroundColor: macOSColors.background.input,
849
+ paddingHorizontal: 10,
850
+ paddingVertical: 4,
851
+ borderRadius: 6,
852
+ borderWidth: 1,
853
+ borderColor: macOSColors.border.input,
854
+ height: 28
855
+ },
856
+ keyNameText: {
857
+ flex: 1,
858
+ fontSize: 13,
859
+ fontWeight: "600",
860
+ color: macOSColors.semantic.debug,
861
+ fontFamily: "monospace",
862
+ letterSpacing: 0.5
863
+ },
864
+ contentWrapper: {
865
+ flex: 1
866
+ }
867
+ });