@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,924 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.FREE_TIER_KEY_LIMIT = void 0;
7
+ exports.GameUIStorageBrowser = GameUIStorageBrowser;
8
+ var _react = require("react");
9
+ var _reactNative = require("react-native");
10
+ var _sharedUi = require("@buoy-gg/shared-ui");
11
+ var _StorageKeySection = require("./StorageKeySection");
12
+ var _StorageFilterCards = require("./StorageFilterCards");
13
+ var _useAsyncStorageKeys = require("../hooks/useAsyncStorageKeys");
14
+ var _AsyncStorageListener = require("../utils/AsyncStorageListener");
15
+ var _useMMKVKeys = require("../hooks/useMMKVKeys");
16
+ var _useMMKVInstances = require("../hooks/useMMKVInstances");
17
+ var _mmkvAvailability = require("../utils/mmkvAvailability");
18
+ var _StorageActionButtons = require("./StorageActionButtons");
19
+ var _SelectionActionBar = require("./SelectionActionBar");
20
+ var _jsxRuntime = require("react/jsx-runtime");
21
+ // Conditionally import MMKV listener
22
+ let addMMKVListener;
23
+ if ((0, _mmkvAvailability.isMMKVAvailable)()) {
24
+ const listener = require("../utils/MMKVListener");
25
+ addMMKVListener = listener.addMMKVListener;
26
+ }
27
+
28
+ // Import shared Game UI components
29
+
30
+ // Lazy load the license hooks to avoid circular dependencies
31
+ let _useIsPro = null;
32
+ let _licenseLoadAttempted = false;
33
+ function loadLicenseModule() {
34
+ if (_licenseLoadAttempted) return;
35
+ _licenseLoadAttempted = true;
36
+ try {
37
+ const mod = require("@buoy-gg/license");
38
+ if (mod) {
39
+ _useIsPro = mod.useIsPro ?? null;
40
+ }
41
+ } catch {
42
+ // License package not available
43
+ }
44
+ }
45
+ function getUseIsPro() {
46
+ loadLicenseModule();
47
+ return _useIsPro ?? (() => false);
48
+ }
49
+
50
+ // MMKV Instance color palette - consistent colors per instance
51
+ const INSTANCE_COLORS = [_sharedUi.macOSColors.semantic.info,
52
+ // Blue
53
+ _sharedUi.macOSColors.semantic.success,
54
+ // Green
55
+ _sharedUi.macOSColors.semantic.warning,
56
+ // Orange
57
+ _sharedUi.macOSColors.semantic.debug,
58
+ // Purple
59
+ '#FF6B9D',
60
+ // Pink
61
+ '#00D9FF' // Cyan
62
+ ];
63
+
64
+ /**
65
+ * Get consistent color for an MMKV instance based on its ID
66
+ * Uses simple hash to ensure same instance always gets same color
67
+ */
68
+ function getInstanceColor(instanceId) {
69
+ const hash = instanceId.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
70
+ return INSTANCE_COLORS[hash % INSTANCE_COLORS.length];
71
+ }
72
+
73
+ /** Free tier limit for storage keys */
74
+ const FREE_TIER_KEY_LIMIT = exports.FREE_TIER_KEY_LIMIT = 25;
75
+ function GameUIStorageBrowser({
76
+ requiredStorageKeys = [],
77
+ showFilters = false,
78
+ ignoredPatterns = new Set(["@react_buoy"]),
79
+ onTogglePattern,
80
+ onAddPattern,
81
+ searchQuery = "",
82
+ storageDataRef
83
+ }) {
84
+ // Check Pro status internally
85
+ const useIsPro = getUseIsPro();
86
+ const isPro = useIsPro();
87
+ const [showUpgradeModal, setShowUpgradeModal] = (0, _react.useState)(false);
88
+ const [activeFilter, setActiveFilter] = (0, _react.useState)("all");
89
+ const [activeStorageType, setActiveStorageType] = (0, _react.useState)("all");
90
+ const [selectedMMKVInstance, setSelectedMMKVInstance] = (0, _react.useState)(null);
91
+
92
+ // Selection mode state
93
+ const [isSelectMode, setIsSelectMode] = (0, _react.useState)(false);
94
+ const [selectedKeyIds, setSelectedKeyIds] = (0, _react.useState)(new Set());
95
+
96
+ // Get all MMKV instances
97
+ const {
98
+ instances: mmkvInstances
99
+ } = (0, _useMMKVInstances.useMMKVInstances)(false);
100
+
101
+ // REMOVED: Auto-selection is now handled by the instance navbar
102
+ // Users can manually select an instance from the navbar if they want to filter by instance
103
+ // By default, selectedMMKVInstance is null, which shows ALL MMKV keys
104
+
105
+ // Use AsyncStorage hook
106
+ const {
107
+ storageKeys: asyncStorageKeys,
108
+ isLoading: isLoadingAsync,
109
+ error: asyncError,
110
+ refresh: refreshAsync
111
+ } = (0, _useAsyncStorageKeys.useAsyncStorageKeys)(requiredStorageKeys);
112
+
113
+ // Memoize the instances array to prevent infinite re-renders
114
+ const mmkvInstancesForHook = (0, _react.useMemo)(() => mmkvInstances.map(inst => ({
115
+ instance: inst.instance,
116
+ id: inst.id
117
+ })), [mmkvInstances]);
118
+
119
+ // Use MMKV hook - always fetch from ALL instances, then filter by selected instance
120
+ const {
121
+ storageKeys: allMMKVKeys,
122
+ isLoading: isLoadingMMKV,
123
+ error: mmkvError,
124
+ refresh: refreshMMKV
125
+ } = (0, _useMMKVKeys.useMultiMMKVKeys)(mmkvInstancesForHook, requiredStorageKeys);
126
+
127
+ // Filter MMKV keys by selected instance (if one is selected)
128
+ const mmkvStorageKeys = (0, _react.useMemo)(() => {
129
+ if (!selectedMMKVInstance) {
130
+ return allMMKVKeys; // Show all MMKV keys
131
+ }
132
+ return allMMKVKeys.filter(k => k.instanceId === selectedMMKVInstance);
133
+ }, [allMMKVKeys, selectedMMKVInstance]);
134
+
135
+ // Merge storage keys from AsyncStorage and MMKV
136
+ const allStorageKeys = (0, _react.useMemo)(() => {
137
+ return [...asyncStorageKeys, ...mmkvStorageKeys];
138
+ }, [asyncStorageKeys, mmkvStorageKeys]);
139
+
140
+ // Determine loading and error states
141
+ const isLoading = isLoadingAsync || isLoadingMMKV;
142
+ const error = asyncError || mmkvError;
143
+
144
+ // Combined refresh function
145
+ const refresh = (0, _react.useCallback)(() => {
146
+ refreshAsync();
147
+ refreshMMKV();
148
+ }, [refreshAsync, refreshMMKV]);
149
+
150
+ // Generate unique identifier for a storage key
151
+ const getKeyIdentifier = (0, _react.useCallback)(storageKey => {
152
+ return storageKey.instanceId ? `${storageKey.storageType}-${storageKey.instanceId}-${storageKey.key}` : `${storageKey.storageType}-${storageKey.key}`;
153
+ }, []);
154
+
155
+ // Toggle select mode
156
+ const handleToggleSelectMode = (0, _react.useCallback)(() => {
157
+ setIsSelectMode(prev => {
158
+ if (prev) {
159
+ // When exiting select mode, clear selection
160
+ setSelectedKeyIds(new Set());
161
+ }
162
+ return !prev;
163
+ });
164
+ }, []);
165
+
166
+ // Handle selection change for a single key
167
+ const handleSelectionChange = (0, _react.useCallback)((storageKey, selected) => {
168
+ const keyId = getKeyIdentifier(storageKey);
169
+ setSelectedKeyIds(prev => {
170
+ const newSet = new Set(prev);
171
+ if (selected) {
172
+ newSet.add(keyId);
173
+ } else {
174
+ newSet.delete(keyId);
175
+ }
176
+ return newSet;
177
+ });
178
+ }, [getKeyIdentifier]);
179
+
180
+ // Clear selection and exit select mode after deletion
181
+ const handleDeleteComplete = (0, _react.useCallback)(() => {
182
+ setSelectedKeyIds(new Set());
183
+ setIsSelectMode(false);
184
+ refresh();
185
+ }, [refresh]);
186
+
187
+ // Memoized export data for copy functionality
188
+ const copyExportData = (0, _react.useMemo)(() => {
189
+ const allKeys = allStorageKeys;
190
+
191
+ // Calculate stats
192
+ const stats = {
193
+ valid: allKeys.filter(k => k.status === 'required_present' || k.status === 'optional_present').length,
194
+ missing: allKeys.filter(k => k.status === 'required_missing').length,
195
+ issues: allKeys.filter(k => k.status === 'required_wrong_value' || k.status === 'required_wrong_type').length,
196
+ total: allKeys.length
197
+ };
198
+
199
+ // Group by storage type
200
+ const asyncKeys = allKeys.filter(k => k.storageType === 'async');
201
+ const mmkvKeys = allKeys.filter(k => k.storageType === 'mmkv');
202
+ const secureKeys = allKeys.filter(k => k.storageType === 'secure');
203
+
204
+ // Build structured export
205
+ return {
206
+ summary: {
207
+ valid: stats.valid,
208
+ missing: stats.missing,
209
+ issues: stats.issues,
210
+ total: stats.total,
211
+ timestamp: new Date().toISOString()
212
+ },
213
+ asyncStorage: asyncKeys.reduce((acc, k) => {
214
+ acc[k.key] = k.value;
215
+ return acc;
216
+ }, {}),
217
+ mmkv: mmkvKeys.reduce((acc, k) => {
218
+ const instanceId = k.instanceId || 'default';
219
+ if (!acc[instanceId]) acc[instanceId] = {};
220
+ acc[instanceId][k.key] = k.value;
221
+ return acc;
222
+ }, {}),
223
+ secure: secureKeys.reduce((acc, k) => {
224
+ acc[k.key] = k.value;
225
+ return acc;
226
+ }, {})
227
+ };
228
+ }, [allStorageKeys]);
229
+
230
+ // Update storage data ref for copy functionality
231
+ (0, _react.useEffect)(() => {
232
+ if (storageDataRef) {
233
+ storageDataRef.current = allStorageKeys;
234
+ }
235
+ }, [allStorageKeys, storageDataRef]);
236
+
237
+ // Auto-refresh on storage events (AsyncStorage)
238
+ (0, _react.useEffect)(() => {
239
+ let timeoutId;
240
+ const unsubscribe = (0, _AsyncStorageListener.addListener)(event => {
241
+ clearTimeout(timeoutId);
242
+ timeoutId = setTimeout(() => {
243
+ refreshAsync(); // Auto-refresh AsyncStorage when it changes
244
+ }, 100); // 100ms debounce
245
+ });
246
+ return () => {
247
+ clearTimeout(timeoutId);
248
+ unsubscribe();
249
+ };
250
+ }, [refreshAsync]);
251
+
252
+ // Auto-refresh on MMKV storage events (only if MMKV is available)
253
+ (0, _react.useEffect)(() => {
254
+ if (!(0, _mmkvAvailability.isMMKVAvailable)() || !addMMKVListener) {
255
+ return; // Skip if MMKV not available
256
+ }
257
+ let timeoutId;
258
+ const unsubscribe = addMMKVListener(event => {
259
+ // Only refresh if event is for the selected instance
260
+ if (event.instanceId === selectedMMKVInstance) {
261
+ clearTimeout(timeoutId);
262
+ timeoutId = setTimeout(() => {
263
+ refreshMMKV(); // Auto-refresh MMKV when it changes
264
+ }, 100); // 100ms debounce
265
+ }
266
+ });
267
+ return () => {
268
+ clearTimeout(timeoutId);
269
+ unsubscribe();
270
+ };
271
+ }, [refreshMMKV, selectedMMKVInstance]);
272
+
273
+ // Calculate stats from ALL keys (not filtered) - base stats for display
274
+ const stats = (0, _react.useMemo)(() => {
275
+ const allKeys = allStorageKeys;
276
+ const appKeys = allKeys.filter(k => !(0, _sharedUi.isDevToolsStorageKey)(k.key));
277
+ const devKeys = allKeys.filter(k => (0, _sharedUi.isDevToolsStorageKey)(k.key));
278
+ const storageStats = {
279
+ totalCount: allKeys.length,
280
+ requiredCount: appKeys.filter(k => k.category === "required").length,
281
+ missingCount: appKeys.filter(k => k.status === "required_missing").length,
282
+ wrongValueCount: appKeys.filter(k => k.status === "required_wrong_value").length,
283
+ wrongTypeCount: appKeys.filter(k => k.status === "required_wrong_type").length,
284
+ presentRequiredCount: appKeys.filter(k => k.status === "required_present").length,
285
+ optionalCount: appKeys.filter(k => k.category === "optional").length,
286
+ // Count keys by their actual storageType property
287
+ mmkvCount: allKeys.filter(k => k.storageType === "mmkv").length,
288
+ asyncCount: allKeys.filter(k => k.storageType === "async").length,
289
+ secureCount: allKeys.filter(k => k.storageType === "secure").length,
290
+ devToolsCount: devKeys.length
291
+ };
292
+ return storageStats;
293
+ }, [allStorageKeys]);
294
+
295
+ // Calculate stats for tab badges based on current filters
296
+ const tabStats = (0, _react.useMemo)(() => {
297
+ // Start with all keys, excluding devtools keys
298
+ let keysForStats = allStorageKeys.filter(k => !(0, _sharedUi.isDevToolsStorageKey)(k.key));
299
+
300
+ // If a specific storage type is selected, filter by that type first
301
+ if (activeStorageType !== "all") {
302
+ keysForStats = keysForStats.filter(k => k.storageType === activeStorageType);
303
+ }
304
+
305
+ // Calculate status counts from filtered keys
306
+ const validCount = keysForStats.filter(k => k.status === "required_present" || k.status === "optional_present").length;
307
+ const missingCount = keysForStats.filter(k => k.status === "required_missing").length;
308
+ const issuesCount = keysForStats.filter(k => k.status === "required_wrong_type" || k.status === "required_wrong_value").length;
309
+
310
+ // Calculate storage type counts based on active status filter
311
+ let asyncCount = 0;
312
+ let mmkvCount = 0;
313
+ let secureCount = 0;
314
+ let totalCount = 0;
315
+ if (activeFilter === "all") {
316
+ // Valid: only keys with valid status
317
+ asyncCount = allStorageKeys.filter(k => !(0, _sharedUi.isDevToolsStorageKey)(k.key) && k.storageType === "async" && (k.status === "required_present" || k.status === "optional_present")).length;
318
+ mmkvCount = allStorageKeys.filter(k => !(0, _sharedUi.isDevToolsStorageKey)(k.key) && k.storageType === "mmkv" && (k.status === "required_present" || k.status === "optional_present")).length;
319
+ secureCount = allStorageKeys.filter(k => !(0, _sharedUi.isDevToolsStorageKey)(k.key) && k.storageType === "secure" && (k.status === "required_present" || k.status === "optional_present")).length;
320
+ totalCount = asyncCount + mmkvCount + secureCount;
321
+ } else if (activeFilter === "missing") {
322
+ // Missing: only keys with missing status
323
+ asyncCount = allStorageKeys.filter(k => !(0, _sharedUi.isDevToolsStorageKey)(k.key) && k.storageType === "async" && k.status === "required_missing").length;
324
+ mmkvCount = allStorageKeys.filter(k => !(0, _sharedUi.isDevToolsStorageKey)(k.key) && k.storageType === "mmkv" && k.status === "required_missing").length;
325
+ secureCount = allStorageKeys.filter(k => !(0, _sharedUi.isDevToolsStorageKey)(k.key) && k.storageType === "secure" && k.status === "required_missing").length;
326
+ totalCount = asyncCount + mmkvCount + secureCount;
327
+ } else if (activeFilter === "issues") {
328
+ // Issues: only keys with issue status
329
+ asyncCount = allStorageKeys.filter(k => !(0, _sharedUi.isDevToolsStorageKey)(k.key) && k.storageType === "async" && (k.status === "required_wrong_type" || k.status === "required_wrong_value")).length;
330
+ mmkvCount = allStorageKeys.filter(k => !(0, _sharedUi.isDevToolsStorageKey)(k.key) && k.storageType === "mmkv" && (k.status === "required_wrong_type" || k.status === "required_wrong_value")).length;
331
+ secureCount = allStorageKeys.filter(k => !(0, _sharedUi.isDevToolsStorageKey)(k.key) && k.storageType === "secure" && (k.status === "required_wrong_type" || k.status === "required_wrong_value")).length;
332
+ totalCount = asyncCount + mmkvCount + secureCount;
333
+ }
334
+ return {
335
+ validCount,
336
+ missingCount,
337
+ issuesCount,
338
+ asyncCount,
339
+ mmkvCount,
340
+ secureCount,
341
+ totalCount
342
+ };
343
+ }, [allStorageKeys, activeFilter, activeStorageType]);
344
+
345
+ // Sort all keys by priority (issues first)
346
+ const sortedKeys = (0, _react.useMemo)(() => {
347
+ // Sort by status priority: errors first, then warnings, then valid
348
+ return [...allStorageKeys].sort((a, b) => {
349
+ const priorityMap = {
350
+ required_missing: 1,
351
+ required_wrong_type: 2,
352
+ required_wrong_value: 3,
353
+ required_present: 4,
354
+ optional_present: 5
355
+ };
356
+ return (priorityMap[a.status] || 999) - (priorityMap[b.status] || 999);
357
+ });
358
+ }, [allStorageKeys]);
359
+
360
+ // Get all unique keys for filter suggestions
361
+ const allUniqueKeys = (0, _react.useMemo)(() => {
362
+ return Array.from(new Set(allStorageKeys.map(k => k.key))).sort();
363
+ }, [allStorageKeys]);
364
+
365
+ // Filter keys based on active filter, storage type, ignored patterns, and search
366
+ const filteredKeys = (0, _react.useMemo)(() => {
367
+ let keys = sortedKeys;
368
+
369
+ // Apply ignored pattern filter first
370
+ keys = keys.filter(k => {
371
+ // Check if key matches any ignored pattern
372
+ const shouldIgnore = Array.from(ignoredPatterns).some(pattern => k.key.includes(pattern));
373
+ return !shouldIgnore;
374
+ });
375
+
376
+ // Apply search filter
377
+ if (searchQuery) {
378
+ const query = searchQuery.toLowerCase();
379
+ keys = keys.filter(k => k.key.toLowerCase().includes(query));
380
+ }
381
+
382
+ // Apply status filter
383
+ switch (activeFilter) {
384
+ case "all":
385
+ // "All" (Valid) shows only keys without issues
386
+ keys = keys.filter(k => k.status === "required_present" || k.status === "optional_present");
387
+ break;
388
+ case "missing":
389
+ keys = keys.filter(k => k.status === "required_missing");
390
+ break;
391
+ case "issues":
392
+ keys = keys.filter(k => k.status === "required_wrong_type" || k.status === "required_wrong_value");
393
+ break;
394
+ }
395
+
396
+ // Apply storage type filter
397
+ if (activeStorageType !== "all") {
398
+ keys = keys.filter(k => k.storageType === activeStorageType);
399
+ }
400
+
401
+ // NOTE: MMKV instance filtering is now handled in mmkvStorageKeys memo (line 128)
402
+ // No need to filter again here
403
+
404
+ return keys;
405
+ }, [sortedKeys, activeFilter, activeStorageType, ignoredPatterns, searchQuery, selectedMMKVInstance]);
406
+
407
+ // For free users, limit visible keys to FREE_TIER_KEY_LIMIT
408
+ const visibleKeys = (0, _react.useMemo)(() => {
409
+ if (isPro) return filteredKeys;
410
+ return filteredKeys.slice(0, FREE_TIER_KEY_LIMIT);
411
+ }, [filteredKeys, isPro]);
412
+
413
+ // Calculate how many keys are locked (only those matching current filter)
414
+ const lockedKeyCount = (0, _react.useMemo)(() => {
415
+ if (isPro) return 0;
416
+ return Math.max(0, filteredKeys.length - FREE_TIER_KEY_LIMIT);
417
+ }, [filteredKeys.length, isPro]);
418
+ const hasLockedKeys = lockedKeyCount > 0;
419
+
420
+ // Calculate stats from FILTERED keys to show actual visible counts
421
+ const filteredStats = (0, _react.useMemo)(() => {
422
+ const appKeys = filteredKeys.filter(k => !(0, _sharedUi.isDevToolsStorageKey)(k.key));
423
+ const storageStats = {
424
+ totalCount: filteredKeys.length,
425
+ requiredCount: appKeys.filter(k => k.category === "required").length,
426
+ missingCount: appKeys.filter(k => k.status === "required_missing").length,
427
+ wrongValueCount: appKeys.filter(k => k.status === "required_wrong_value").length,
428
+ wrongTypeCount: appKeys.filter(k => k.status === "required_wrong_type").length,
429
+ presentRequiredCount: appKeys.filter(k => k.status === "required_present").length,
430
+ optionalCount: appKeys.filter(k => k.category === "optional").length,
431
+ mmkvCount: filteredKeys.filter(k => k.storageType === "mmkv").length,
432
+ asyncCount: filteredKeys.filter(k => k.storageType === "async").length,
433
+ secureCount: filteredKeys.filter(k => k.storageType === "secure").length,
434
+ devToolsCount: filteredKeys.filter(k => (0, _sharedUi.isDevToolsStorageKey)(k.key)).length
435
+ };
436
+ return storageStats;
437
+ }, [filteredKeys]);
438
+
439
+ // Get selected keys as StorageKeyInfo objects (from visible keys only)
440
+ const selectedKeysInfo = (0, _react.useMemo)(() => {
441
+ return visibleKeys.filter(key => selectedKeyIds.has(getKeyIdentifier(key)));
442
+ }, [visibleKeys, selectedKeyIds, getKeyIdentifier]);
443
+
444
+ // Select all visible keys (only unlocked keys can be selected)
445
+ const handleSelectAll = (0, _react.useCallback)(() => {
446
+ const allIds = new Set(visibleKeys.map(getKeyIdentifier));
447
+ setSelectedKeyIds(allIds);
448
+ }, [visibleKeys, getKeyIdentifier]);
449
+
450
+ // Clear all selections
451
+ const handleClearSelection = (0, _react.useCallback)(() => {
452
+ setSelectedKeyIds(new Set());
453
+ }, []);
454
+
455
+ // Calculate health percentage
456
+ const healthPercentage = stats.requiredCount > 0 ? Math.round(stats.presentRequiredCount / stats.requiredCount * 100) : stats.totalCount > 0 ? 100 : 0;
457
+ const healthStatus = healthPercentage >= 90 ? "OPTIMAL" : healthPercentage >= 70 ? "WARNING" : "CRITICAL";
458
+ const healthColor = healthPercentage >= 90 ? _sharedUi.gameUIColors.success : healthPercentage >= 70 ? _sharedUi.gameUIColors.warning : _sharedUi.gameUIColors.error;
459
+
460
+ // Loading state
461
+ if (isLoading && allStorageKeys.length === 0) {
462
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
463
+ style: styles.emptyState,
464
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Database, {
465
+ size: 48,
466
+ color: _sharedUi.macOSColors.text.muted
467
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
468
+ style: styles.emptyTitle,
469
+ children: "Loading storage keys..."
470
+ })]
471
+ });
472
+ }
473
+
474
+ // Error state
475
+ if (error) {
476
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
477
+ style: styles.emptyState,
478
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Database, {
479
+ size: 48,
480
+ color: _sharedUi.gameUIColors.error
481
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
482
+ style: [styles.emptyTitle, {
483
+ color: _sharedUi.gameUIColors.error
484
+ }],
485
+ children: "Error loading storage"
486
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
487
+ style: styles.emptySubtitle,
488
+ children: error.message
489
+ })]
490
+ });
491
+ }
492
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
493
+ style: styles.scrollContainer,
494
+ contentContainerStyle: styles.container,
495
+ showsVerticalScrollIndicator: false,
496
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
497
+ style: styles.backgroundGrid
498
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_StorageFilterCards.StorageFilterCards, {
499
+ stats: stats,
500
+ tabStats: tabStats,
501
+ healthPercentage: healthPercentage,
502
+ healthStatus: healthStatus,
503
+ healthColor: healthColor,
504
+ activeFilter: activeFilter,
505
+ onFilterChange: setActiveFilter,
506
+ activeStorageType: activeStorageType,
507
+ onStorageTypeChange: setActiveStorageType
508
+ }), activeStorageType === "mmkv" && mmkvInstances.length === 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
509
+ style: styles.emptyMMKVState,
510
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
511
+ style: styles.emptyMMKVTitle,
512
+ children: "No MMKV Instances Detected"
513
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
514
+ style: styles.emptyMMKVSubtitle,
515
+ children: "MMKV instances must be registered with registerMMKVInstance()"
516
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
517
+ style: styles.emptyMMKVCode,
518
+ children: `import { registerMMKVInstance } from '@buoy-gg/storage';\n\nconst storage = createMMKV();\nregisterMMKVInstance('mmkv.default', storage);`
519
+ })]
520
+ }), activeStorageType === "mmkv" && mmkvInstances.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
521
+ style: styles.instanceNavbar,
522
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
523
+ style: styles.instanceNavbarHeader,
524
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
525
+ style: styles.instanceNavbarLabel,
526
+ children: "INSTANCES"
527
+ }), !isPro && mmkvInstances.length > 1 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ProBadge, {
528
+ style: {
529
+ transform: [{
530
+ scale: 0.85
531
+ }]
532
+ }
533
+ })]
534
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
535
+ horizontal: true,
536
+ showsHorizontalScrollIndicator: false,
537
+ contentContainerStyle: styles.instanceNavbarScroll,
538
+ children: [(isPro || mmkvInstances.length === 1) && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
539
+ onPress: () => setSelectedMMKVInstance(null),
540
+ style: [styles.instanceNavbarButton, selectedMMKVInstance === null && styles.instanceNavbarButtonActive],
541
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.HardDrive, {
542
+ size: 12,
543
+ color: selectedMMKVInstance === null ? _sharedUi.macOSColors.text.primary : _sharedUi.macOSColors.text.secondary
544
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
545
+ style: [styles.instanceNavbarButtonText, selectedMMKVInstance === null && styles.instanceNavbarButtonTextActive],
546
+ children: "All"
547
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
548
+ style: styles.instanceNavbarBadge,
549
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
550
+ style: styles.instanceNavbarBadgeText,
551
+ children: mmkvInstances.reduce((sum, inst) => sum + inst.keyCount, 0)
552
+ })
553
+ })]
554
+ }), mmkvInstances.map((inst, index) => {
555
+ const isLocked = !isPro && index > 0;
556
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
557
+ onPress: () => {
558
+ if (isLocked) {
559
+ setShowUpgradeModal(true);
560
+ } else {
561
+ setSelectedMMKVInstance(inst.id);
562
+ }
563
+ },
564
+ style: [styles.instanceNavbarButton, inst.id === selectedMMKVInstance && styles.instanceNavbarButtonActive, {
565
+ borderColor: isLocked ? _sharedUi.macOSColors.border.default : getInstanceColor(inst.id) + '40',
566
+ backgroundColor: inst.id === selectedMMKVInstance ? getInstanceColor(inst.id) + '20' : _sharedUi.macOSColors.background.card,
567
+ opacity: isLocked ? 0.5 : 1
568
+ }],
569
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.HardDrive, {
570
+ size: 12,
571
+ color: inst.id === selectedMMKVInstance ? getInstanceColor(inst.id) : _sharedUi.macOSColors.text.secondary
572
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
573
+ style: [styles.instanceNavbarButtonText, inst.id === selectedMMKVInstance && {
574
+ color: getInstanceColor(inst.id),
575
+ fontWeight: '700'
576
+ }],
577
+ children: inst.id
578
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
579
+ style: [styles.instanceNavbarBadge, inst.id === selectedMMKVInstance && {
580
+ backgroundColor: getInstanceColor(inst.id) + '30'
581
+ }],
582
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
583
+ style: [styles.instanceNavbarBadgeText, inst.id === selectedMMKVInstance && {
584
+ color: getInstanceColor(inst.id)
585
+ }],
586
+ children: inst.keyCount
587
+ })
588
+ })]
589
+ }, inst.id);
590
+ })]
591
+ })]
592
+ }), filteredKeys.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
593
+ style: styles.keysSection,
594
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
595
+ style: styles.sectionHeader,
596
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
597
+ style: styles.sectionHeaderLeft,
598
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
599
+ style: styles.sectionTitle,
600
+ children: [activeFilter === "all" ? "KEYS" : activeFilter === "missing" ? "MISSING KEYS" : "ISSUES TO FIX", activeStorageType !== "all" && ` (${activeStorageType.toUpperCase()})`]
601
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
602
+ style: styles.countBadge,
603
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
604
+ style: styles.countText,
605
+ children: visibleKeys.length
606
+ })
607
+ })]
608
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_StorageActionButtons.StorageActionButtons, {
609
+ copyValue: copyExportData,
610
+ mmkvInstances: mmkvInstances.map(inst => ({
611
+ id: inst.id,
612
+ instance: inst.instance
613
+ })),
614
+ activeStorageType: activeStorageType,
615
+ onClearComplete: refresh,
616
+ isSelectMode: isSelectMode,
617
+ onToggleSelectMode: handleToggleSelectMode,
618
+ selectedCount: selectedKeyIds.size
619
+ })]
620
+ }), isSelectMode && /*#__PURE__*/(0, _jsxRuntime.jsx)(_SelectionActionBar.SelectionActionBar, {
621
+ selectedKeys: selectedKeysInfo,
622
+ mmkvInstances: mmkvInstances.map(inst => ({
623
+ id: inst.id,
624
+ instance: inst.instance
625
+ })),
626
+ onDeleteComplete: handleDeleteComplete,
627
+ onSelectAll: handleSelectAll,
628
+ onClearSelection: handleClearSelection,
629
+ totalVisibleKeys: visibleKeys.length
630
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_StorageKeySection.StorageKeySection, {
631
+ title: "",
632
+ count: -1,
633
+ keys: visibleKeys,
634
+ emptyMessage: "",
635
+ isSelectMode: isSelectMode,
636
+ selectedKeys: selectedKeyIds,
637
+ onSelectionChange: handleSelectionChange
638
+ }), hasLockedKeys && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
639
+ style: styles.limitBanner,
640
+ onPress: () => setShowUpgradeModal(true),
641
+ activeOpacity: 0.8,
642
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
643
+ style: styles.limitBannerContent,
644
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
645
+ style: styles.limitBannerLeft,
646
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Zap, {
647
+ size: 16,
648
+ color: "#20C997"
649
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
650
+ style: styles.limitBannerText,
651
+ children: searchQuery ? `${lockedKeyCount} matching ${lockedKeyCount === 1 ? 'key' : 'keys'} in locked storage` : `${lockedKeyCount} keys locked`
652
+ })]
653
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ProBadge, {})]
654
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
655
+ style: styles.limitBannerSubtext,
656
+ children: searchQuery ? "Upgrade to Pro to search your full storage" : "Upgrade to Pro to unlock full storage access"
657
+ })]
658
+ })]
659
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
660
+ style: styles.emptyState,
661
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Database, {
662
+ size: 32,
663
+ color: _sharedUi.macOSColors.text.muted
664
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
665
+ style: styles.emptyTitle,
666
+ children: searchQuery ? "No results found" : activeFilter === "all" ? "No valid keys found" : activeFilter === "missing" ? "No missing keys" : "No issues found"
667
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
668
+ style: styles.emptySubtitle,
669
+ children: searchQuery ? `No keys matching "${searchQuery}"` : activeFilter === "all" ? "All keys have issues or are missing" : activeFilter === "missing" ? "All required keys are present" : "All storage keys are correctly configured"
670
+ })]
671
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
672
+ style: styles.techFooter,
673
+ children: "ASYNC STORAGE | MMKV | SECURE STORAGE BACKENDS"
674
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ProUpgradeModal, {
675
+ visible: showUpgradeModal,
676
+ onClose: () => setShowUpgradeModal(false),
677
+ featureName: "Storage Keys"
678
+ })]
679
+ });
680
+ }
681
+ const styles = _reactNative.StyleSheet.create({
682
+ scrollContainer: {
683
+ flex: 1,
684
+ backgroundColor: _sharedUi.gameUIColors.background
685
+ },
686
+ container: {
687
+ padding: 12,
688
+ paddingBottom: 32
689
+ },
690
+ backgroundGrid: {
691
+ ..._reactNative.StyleSheet.absoluteFillObject,
692
+ opacity: 0.006,
693
+ backgroundColor: _sharedUi.gameUIColors.info
694
+ },
695
+ techFooter: {
696
+ fontSize: 8,
697
+ color: _sharedUi.gameUIColors.muted,
698
+ fontFamily: "monospace",
699
+ textAlign: "center",
700
+ marginTop: 20,
701
+ letterSpacing: 1,
702
+ opacity: 0.5
703
+ },
704
+ // Keys section
705
+ keysSection: {
706
+ marginTop: 8,
707
+ backgroundColor: _sharedUi.macOSColors.background.base,
708
+ borderRadius: 12,
709
+ padding: 12,
710
+ borderWidth: 1,
711
+ borderColor: _sharedUi.macOSColors.border.default
712
+ },
713
+ sectionHeader: {
714
+ flexDirection: "row",
715
+ justifyContent: "space-between",
716
+ alignItems: "center",
717
+ marginBottom: 10,
718
+ paddingBottom: 6,
719
+ borderBottomWidth: 1,
720
+ borderBottomColor: _sharedUi.macOSColors.border.default
721
+ },
722
+ sectionHeaderLeft: {
723
+ flexDirection: "row",
724
+ alignItems: "center",
725
+ gap: 8
726
+ },
727
+ sectionTitle: {
728
+ fontSize: 12,
729
+ fontWeight: "600",
730
+ color: _sharedUi.macOSColors.text.secondary,
731
+ letterSpacing: 0.4,
732
+ textTransform: "uppercase"
733
+ },
734
+ countBadge: {
735
+ backgroundColor: _sharedUi.macOSColors.background.card,
736
+ paddingHorizontal: 8,
737
+ paddingVertical: 3,
738
+ borderRadius: 6,
739
+ borderWidth: 1,
740
+ borderColor: _sharedUi.macOSColors.border.default
741
+ },
742
+ countText: {
743
+ fontSize: 11,
744
+ fontWeight: "600",
745
+ color: _sharedUi.macOSColors.text.primary,
746
+ fontFamily: "monospace"
747
+ },
748
+ // Empty state
749
+ emptyState: {
750
+ alignItems: "center",
751
+ justifyContent: "center",
752
+ paddingVertical: 48
753
+ },
754
+ emptyTitle: {
755
+ fontSize: 16,
756
+ fontWeight: "600",
757
+ color: _sharedUi.macOSColors.text.primary,
758
+ marginTop: 12,
759
+ marginBottom: 8
760
+ },
761
+ emptySubtitle: {
762
+ fontSize: 13,
763
+ color: _sharedUi.macOSColors.text.secondary,
764
+ textAlign: "center"
765
+ },
766
+ // Empty MMKV state
767
+ emptyMMKVState: {
768
+ backgroundColor: _sharedUi.macOSColors.background.card,
769
+ borderRadius: 12,
770
+ padding: 20,
771
+ borderWidth: 1,
772
+ borderColor: _sharedUi.macOSColors.border.default,
773
+ marginTop: 8,
774
+ alignItems: "center"
775
+ },
776
+ emptyMMKVTitle: {
777
+ fontSize: 14,
778
+ fontWeight: "600",
779
+ color: _sharedUi.macOSColors.text.primary,
780
+ marginBottom: 6
781
+ },
782
+ emptyMMKVSubtitle: {
783
+ fontSize: 12,
784
+ color: _sharedUi.macOSColors.text.secondary,
785
+ textAlign: "center",
786
+ marginBottom: 12
787
+ },
788
+ emptyMMKVCode: {
789
+ fontSize: 10,
790
+ fontFamily: "monospace",
791
+ color: _sharedUi.macOSColors.semantic.info,
792
+ backgroundColor: _sharedUi.macOSColors.background.input,
793
+ padding: 12,
794
+ borderRadius: 8,
795
+ borderWidth: 1,
796
+ borderColor: _sharedUi.macOSColors.border.default,
797
+ alignSelf: "stretch"
798
+ },
799
+ // Instance Navbar styles
800
+ instanceNavbar: {
801
+ marginTop: 12,
802
+ marginBottom: 8,
803
+ backgroundColor: _sharedUi.macOSColors.background.card,
804
+ borderRadius: 10,
805
+ borderWidth: 1,
806
+ borderColor: _sharedUi.macOSColors.border.default,
807
+ padding: 10,
808
+ shadowColor: '#000',
809
+ shadowOpacity: 0.08,
810
+ shadowRadius: 6,
811
+ shadowOffset: {
812
+ width: 0,
813
+ height: 2
814
+ }
815
+ },
816
+ instanceNavbarHeader: {
817
+ flexDirection: 'row',
818
+ alignItems: 'center',
819
+ justifyContent: 'space-between',
820
+ marginBottom: 8,
821
+ paddingLeft: 2
822
+ },
823
+ instanceNavbarLabel: {
824
+ fontSize: 9,
825
+ fontWeight: '700',
826
+ color: _sharedUi.macOSColors.text.muted,
827
+ textTransform: 'uppercase',
828
+ letterSpacing: 1
829
+ },
830
+ instanceNavbarScroll: {
831
+ gap: 8,
832
+ paddingRight: 8
833
+ },
834
+ instanceNavbarButton: {
835
+ flexDirection: 'row',
836
+ alignItems: 'center',
837
+ gap: 6,
838
+ paddingHorizontal: 12,
839
+ paddingVertical: 8,
840
+ backgroundColor: _sharedUi.macOSColors.background.card,
841
+ borderRadius: 8,
842
+ borderWidth: 1,
843
+ borderColor: _sharedUi.macOSColors.border.default,
844
+ minWidth: 90
845
+ },
846
+ instanceNavbarButtonActive: {
847
+ borderWidth: 1.5,
848
+ shadowColor: '#000',
849
+ shadowOpacity: 0.1,
850
+ shadowRadius: 4,
851
+ shadowOffset: {
852
+ width: 0,
853
+ height: 1
854
+ }
855
+ },
856
+ instanceNavbarButtonText: {
857
+ fontSize: 11,
858
+ fontWeight: '600',
859
+ color: _sharedUi.macOSColors.text.secondary,
860
+ fontFamily: 'monospace',
861
+ flex: 1
862
+ },
863
+ instanceNavbarButtonTextActive: {
864
+ fontWeight: '700',
865
+ color: _sharedUi.macOSColors.text.primary
866
+ },
867
+ instanceNavbarBadge: {
868
+ backgroundColor: _sharedUi.macOSColors.background.input,
869
+ paddingHorizontal: 6,
870
+ paddingVertical: 2,
871
+ borderRadius: 6,
872
+ minWidth: 24,
873
+ alignItems: 'center'
874
+ },
875
+ instanceNavbarBadgeText: {
876
+ fontSize: 10,
877
+ fontWeight: '700',
878
+ color: _sharedUi.macOSColors.text.secondary,
879
+ fontFamily: 'monospace'
880
+ },
881
+ clearSearchButton: {
882
+ marginTop: 16,
883
+ paddingHorizontal: 16,
884
+ paddingVertical: 8,
885
+ backgroundColor: _sharedUi.macOSColors.background.hover,
886
+ borderRadius: 6,
887
+ borderWidth: 1,
888
+ borderColor: _sharedUi.macOSColors.border.default
889
+ },
890
+ clearSearchText: {
891
+ fontSize: 13,
892
+ color: _sharedUi.macOSColors.text.primary,
893
+ fontWeight: "500"
894
+ },
895
+ // Free tier limit banner
896
+ limitBanner: {
897
+ backgroundColor: "#1A1A1A",
898
+ borderRadius: 10,
899
+ borderWidth: 1,
900
+ borderColor: "#20C997" + "40",
901
+ padding: 12,
902
+ marginTop: 12
903
+ },
904
+ limitBannerContent: {
905
+ flexDirection: "row",
906
+ alignItems: "center",
907
+ justifyContent: "space-between",
908
+ marginBottom: 4
909
+ },
910
+ limitBannerLeft: {
911
+ flexDirection: "row",
912
+ alignItems: "center",
913
+ gap: 8
914
+ },
915
+ limitBannerText: {
916
+ color: "#E0E0E0",
917
+ fontSize: 13,
918
+ fontWeight: "600"
919
+ },
920
+ limitBannerSubtext: {
921
+ color: "#888888",
922
+ fontSize: 11
923
+ }
924
+ });