@buoy-gg/core 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 (132) hide show
  1. package/README.md +43 -0
  2. package/lib/commonjs/floatingMenu/AppHost.js +410 -0
  3. package/lib/commonjs/floatingMenu/AppHostLogic.js +44 -0
  4. package/lib/commonjs/floatingMenu/DefaultConfigContext.js +45 -0
  5. package/lib/commonjs/floatingMenu/DevToolsSettingsModal.js +2274 -0
  6. package/lib/commonjs/floatingMenu/DevToolsVisibilityContext.js +49 -0
  7. package/lib/commonjs/floatingMenu/DraggableHeader.js +114 -0
  8. package/lib/commonjs/floatingMenu/FloatingDevTools.js +254 -0
  9. package/lib/commonjs/floatingMenu/FloatingMenu.js +364 -0
  10. package/lib/commonjs/floatingMenu/MinimizedToolsContext.js +247 -0
  11. package/lib/commonjs/floatingMenu/MinimizedToolsStack.js +206 -0
  12. package/lib/commonjs/floatingMenu/ToggleStateManager.js +36 -0
  13. package/lib/commonjs/floatingMenu/autoDiscoverPresets.js +241 -0
  14. package/lib/commonjs/floatingMenu/defaultConfig.js +160 -0
  15. package/lib/commonjs/floatingMenu/dial/DialDevTools.js +835 -0
  16. package/lib/commonjs/floatingMenu/dial/DialIcon.js +246 -0
  17. package/lib/commonjs/floatingMenu/dial/OnboardingTooltip.js +249 -0
  18. package/lib/commonjs/floatingMenu/dial/onboardingConstants.js +70 -0
  19. package/lib/commonjs/floatingMenu/floatingTools.js +771 -0
  20. package/lib/commonjs/floatingMenu/settingsBus.js +23 -0
  21. package/lib/commonjs/floatingMenu/types.js +5 -0
  22. package/lib/commonjs/index.js +240 -0
  23. package/lib/commonjs/package.json +1 -0
  24. package/lib/module/floatingMenu/AppHost.js +402 -0
  25. package/lib/module/floatingMenu/AppHostLogic.js +39 -0
  26. package/lib/module/floatingMenu/DefaultConfigContext.js +39 -0
  27. package/lib/module/floatingMenu/DevToolsSettingsModal.js +2273 -0
  28. package/lib/module/floatingMenu/DevToolsVisibilityContext.js +44 -0
  29. package/lib/module/floatingMenu/DraggableHeader.js +110 -0
  30. package/lib/module/floatingMenu/FloatingDevTools.js +249 -0
  31. package/lib/module/floatingMenu/FloatingMenu.js +358 -0
  32. package/lib/module/floatingMenu/MinimizedToolsContext.js +239 -0
  33. package/lib/module/floatingMenu/MinimizedToolsStack.js +202 -0
  34. package/lib/module/floatingMenu/ToggleStateManager.js +32 -0
  35. package/lib/module/floatingMenu/autoDiscoverPresets.js +236 -0
  36. package/lib/module/floatingMenu/defaultConfig.js +151 -0
  37. package/lib/module/floatingMenu/dial/DialDevTools.js +829 -0
  38. package/lib/module/floatingMenu/dial/DialIcon.js +241 -0
  39. package/lib/module/floatingMenu/dial/OnboardingTooltip.js +244 -0
  40. package/lib/module/floatingMenu/dial/onboardingConstants.js +64 -0
  41. package/lib/module/floatingMenu/floatingTools.js +767 -0
  42. package/lib/module/floatingMenu/settingsBus.js +19 -0
  43. package/lib/module/floatingMenu/types.js +3 -0
  44. package/lib/module/index.js +29 -0
  45. package/lib/module/package.json +1 -0
  46. package/lib/typescript/commonjs/floatingMenu/AppHost.d.ts +39 -0
  47. package/lib/typescript/commonjs/floatingMenu/AppHost.d.ts.map +1 -0
  48. package/lib/typescript/commonjs/floatingMenu/AppHostLogic.d.ts +37 -0
  49. package/lib/typescript/commonjs/floatingMenu/AppHostLogic.d.ts.map +1 -0
  50. package/lib/typescript/commonjs/floatingMenu/DefaultConfigContext.d.ts +27 -0
  51. package/lib/typescript/commonjs/floatingMenu/DefaultConfigContext.d.ts.map +1 -0
  52. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts +57 -0
  53. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -0
  54. package/lib/typescript/commonjs/floatingMenu/DevToolsVisibilityContext.d.ts +25 -0
  55. package/lib/typescript/commonjs/floatingMenu/DevToolsVisibilityContext.d.ts.map +1 -0
  56. package/lib/typescript/commonjs/floatingMenu/DraggableHeader.d.ts +30 -0
  57. package/lib/typescript/commonjs/floatingMenu/DraggableHeader.d.ts.map +1 -0
  58. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts +226 -0
  59. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -0
  60. package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts +39 -0
  61. package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts.map +1 -0
  62. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsContext.d.ts +95 -0
  63. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsContext.d.ts.map +1 -0
  64. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.d.ts +10 -0
  65. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.d.ts.map +1 -0
  66. package/lib/typescript/commonjs/floatingMenu/ToggleStateManager.d.ts +21 -0
  67. package/lib/typescript/commonjs/floatingMenu/ToggleStateManager.d.ts.map +1 -0
  68. package/lib/typescript/commonjs/floatingMenu/autoDiscoverPresets.d.ts +75 -0
  69. package/lib/typescript/commonjs/floatingMenu/autoDiscoverPresets.d.ts.map +1 -0
  70. package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts +120 -0
  71. package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts.map +1 -0
  72. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts +35 -0
  73. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -0
  74. package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts +14 -0
  75. package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts.map +1 -0
  76. package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.d.ts +12 -0
  77. package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.d.ts.map +1 -0
  78. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts +30 -0
  79. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts.map +1 -0
  80. package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts +56 -0
  81. package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts.map +1 -0
  82. package/lib/typescript/commonjs/floatingMenu/settingsBus.d.ts +10 -0
  83. package/lib/typescript/commonjs/floatingMenu/settingsBus.d.ts.map +1 -0
  84. package/lib/typescript/commonjs/floatingMenu/types.d.ts +56 -0
  85. package/lib/typescript/commonjs/floatingMenu/types.d.ts.map +1 -0
  86. package/lib/typescript/commonjs/index.d.ts +18 -0
  87. package/lib/typescript/commonjs/index.d.ts.map +1 -0
  88. package/lib/typescript/commonjs/package.json +1 -0
  89. package/lib/typescript/module/floatingMenu/AppHost.d.ts +39 -0
  90. package/lib/typescript/module/floatingMenu/AppHost.d.ts.map +1 -0
  91. package/lib/typescript/module/floatingMenu/AppHostLogic.d.ts +37 -0
  92. package/lib/typescript/module/floatingMenu/AppHostLogic.d.ts.map +1 -0
  93. package/lib/typescript/module/floatingMenu/DefaultConfigContext.d.ts +27 -0
  94. package/lib/typescript/module/floatingMenu/DefaultConfigContext.d.ts.map +1 -0
  95. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts +57 -0
  96. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -0
  97. package/lib/typescript/module/floatingMenu/DevToolsVisibilityContext.d.ts +25 -0
  98. package/lib/typescript/module/floatingMenu/DevToolsVisibilityContext.d.ts.map +1 -0
  99. package/lib/typescript/module/floatingMenu/DraggableHeader.d.ts +30 -0
  100. package/lib/typescript/module/floatingMenu/DraggableHeader.d.ts.map +1 -0
  101. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts +226 -0
  102. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -0
  103. package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts +39 -0
  104. package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts.map +1 -0
  105. package/lib/typescript/module/floatingMenu/MinimizedToolsContext.d.ts +95 -0
  106. package/lib/typescript/module/floatingMenu/MinimizedToolsContext.d.ts.map +1 -0
  107. package/lib/typescript/module/floatingMenu/MinimizedToolsStack.d.ts +10 -0
  108. package/lib/typescript/module/floatingMenu/MinimizedToolsStack.d.ts.map +1 -0
  109. package/lib/typescript/module/floatingMenu/ToggleStateManager.d.ts +21 -0
  110. package/lib/typescript/module/floatingMenu/ToggleStateManager.d.ts.map +1 -0
  111. package/lib/typescript/module/floatingMenu/autoDiscoverPresets.d.ts +75 -0
  112. package/lib/typescript/module/floatingMenu/autoDiscoverPresets.d.ts.map +1 -0
  113. package/lib/typescript/module/floatingMenu/defaultConfig.d.ts +120 -0
  114. package/lib/typescript/module/floatingMenu/defaultConfig.d.ts.map +1 -0
  115. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts +35 -0
  116. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -0
  117. package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts +14 -0
  118. package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts.map +1 -0
  119. package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.d.ts +12 -0
  120. package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.d.ts.map +1 -0
  121. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts +30 -0
  122. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts.map +1 -0
  123. package/lib/typescript/module/floatingMenu/floatingTools.d.ts +56 -0
  124. package/lib/typescript/module/floatingMenu/floatingTools.d.ts.map +1 -0
  125. package/lib/typescript/module/floatingMenu/settingsBus.d.ts +10 -0
  126. package/lib/typescript/module/floatingMenu/settingsBus.d.ts.map +1 -0
  127. package/lib/typescript/module/floatingMenu/types.d.ts +56 -0
  128. package/lib/typescript/module/floatingMenu/types.d.ts.map +1 -0
  129. package/lib/typescript/module/index.d.ts +18 -0
  130. package/lib/typescript/module/index.d.ts.map +1 -0
  131. package/lib/typescript/module/package.json +1 -0
  132. package/package.json +79 -0
@@ -0,0 +1,2274 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useDevToolsSettings = exports.DevToolsSettingsModal = void 0;
7
+ var _react = require("react");
8
+ var _reactNative = require("react-native");
9
+ var _settingsBus = require("./settingsBus.js");
10
+ var _sharedUi = require("@buoy-gg/shared-ui");
11
+ var _floatingToolsCore = require("@buoy-gg/floating-tools-core");
12
+ var _DefaultConfigContext = require("./DefaultConfigContext.js");
13
+ var _jsxRuntime = require("react/jsx-runtime");
14
+ const STORAGE_KEY = "@react_buoy_dev_tools_settings";
15
+ const MAX_DIAL_SLOTS = 6;
16
+
17
+ // Lazy load license hooks to avoid circular dependencies
18
+ let _useLicense = null;
19
+ let _useSeats = null;
20
+ let _useDevices = null;
21
+ function getUseLicense() {
22
+ if (!_useLicense) {
23
+ try {
24
+ const mod = require("@buoy-gg/license");
25
+ _useLicense = mod.useLicense;
26
+ } catch {
27
+ // License package not available
28
+ }
29
+ }
30
+ return _useLicense;
31
+ }
32
+ function getUseSeats() {
33
+ if (!_useSeats) {
34
+ try {
35
+ const mod = require("@buoy-gg/license");
36
+ _useSeats = mod.useSeats;
37
+ } catch {
38
+ // License package not available
39
+ }
40
+ }
41
+ return _useSeats;
42
+ }
43
+ function getUseDevices() {
44
+ if (!_useDevices) {
45
+ try {
46
+ const mod = require("@buoy-gg/license");
47
+ _useDevices = mod.useDevices;
48
+ } catch {
49
+ // License package not available
50
+ }
51
+ }
52
+ return _useDevices;
53
+ }
54
+ const enforceDialLimit = dialTools => {
55
+ let remaining = MAX_DIAL_SLOTS;
56
+ const limited = {};
57
+ for (const [id, enabled] of Object.entries(dialTools)) {
58
+ if (enabled && remaining > 0) {
59
+ limited[id] = true;
60
+ remaining -= 1;
61
+ } else {
62
+ limited[id] = false;
63
+ }
64
+ }
65
+ return limited;
66
+ };
67
+ const sanitizeFloating = (floating, allowedKeys) => {
68
+ const {
69
+ userStatus,
70
+ environment,
71
+ ...rest
72
+ } = floating;
73
+ const filteredEntries = allowedKeys ? Object.entries(rest).filter(([key]) => allowedKeys.includes(key)) : Object.entries(rest);
74
+ return {
75
+ ...Object.fromEntries(filteredEntries),
76
+ environment: environment ?? false
77
+ };
78
+ };
79
+ const mergeWithDefaults = (defaults, stored, options) => {
80
+ if (!stored) return defaults;
81
+ const combinedDial = {
82
+ ...defaults.dialTools,
83
+ ...(stored.dialTools ?? {})
84
+ };
85
+ const dialEntries = options?.allowedDialKeys ? Object.entries(combinedDial).filter(([key]) => options.allowedDialKeys?.includes(key)) : Object.entries(combinedDial);
86
+ const mergedDial = enforceDialLimit(Object.fromEntries(dialEntries));
87
+ return {
88
+ dialTools: mergedDial,
89
+ floatingTools: sanitizeFloating({
90
+ ...defaults.floatingTools,
91
+ ...(stored.floatingTools ?? {})
92
+ }, options?.allowedFloatingKeys),
93
+ globalSettings: {
94
+ enableSharedModalDimensions: false,
95
+ // Default to false
96
+ ...(stored.globalSettings ?? {})
97
+ }
98
+ };
99
+ };
100
+
101
+ /**
102
+ * Global settings that apply to all dev tools.
103
+ * These settings override individual tool configurations.
104
+ */
105
+
106
+ /**
107
+ * Serialized preferences that power the floating dev tools UI. Values persist across sessions
108
+ * via AsyncStorage so teams can tailor which tools appear in the dial or floating row.
109
+ */
110
+
111
+ /**
112
+ * Generate default settings based on available apps and optional team default configuration.
113
+ *
114
+ * @param availableApps - List of available apps from auto-discovery
115
+ * @param defaultFloatingTools - Optional array of tool IDs to enable by default in floating bubble
116
+ * @param defaultDialTools - Optional array of tool IDs to enable by default in dial menu
117
+ */
118
+ const generateDefaultSettings = (availableApps = [], defaultFloatingTools, defaultDialTools) => {
119
+ const dialDefaults = {};
120
+ const floatingDefaults = {};
121
+
122
+ // Create sets for quick lookup of default-enabled tools
123
+ // Cast to Set<string> to allow comparison with any tool ID (including custom tools)
124
+ const enabledFloatingSet = new Set(defaultFloatingTools ?? []);
125
+ const enabledDialSet = new Set(defaultDialTools ?? []);
126
+ for (const app of availableApps) {
127
+ const {
128
+ id,
129
+ slot = "both"
130
+ } = app;
131
+ if (slot === "dial" || slot === "both") {
132
+ // Enable if in defaultDialTools, otherwise false
133
+ dialDefaults[id] = enabledDialSet.has(id);
134
+ }
135
+ if (slot === "row" || slot === "both") {
136
+ // Enable if in defaultFloatingTools, otherwise false
137
+ floatingDefaults[id] = enabledFloatingSet.has(id);
138
+ }
139
+ }
140
+ return {
141
+ dialTools: enforceDialLimit(dialDefaults),
142
+ floatingTools: {
143
+ ...floatingDefaults,
144
+ // Special: environment badge - check if 'environment' is in the floating defaults
145
+ environment: enabledFloatingSet.has('environment')
146
+ },
147
+ globalSettings: {
148
+ enableSharedModalDimensions: false // Default to false - each modal has its own persistence
149
+ }
150
+ };
151
+ };
152
+
153
+ /**
154
+ * Configurable modal surface that lets engineers pick which dev tools appear in the dial and
155
+ * floating row. Persists preferences and enforces slot limits for a consistent UX.
156
+ */
157
+ const DevToolsSettingsModal = ({
158
+ visible,
159
+ onClose,
160
+ onSettingsChange,
161
+ initialSettings,
162
+ availableApps = []
163
+ }) => {
164
+ // Get team default configuration from context
165
+ const {
166
+ defaultFloatingTools,
167
+ defaultDialTools
168
+ } = (0, _DefaultConfigContext.useDefaultConfig)();
169
+ const defaultSettings = (0, _react.useMemo)(() => generateDefaultSettings(availableApps, defaultFloatingTools, defaultDialTools), [availableApps, defaultFloatingTools, defaultDialTools]);
170
+ const allowedDialKeys = (0, _react.useMemo)(() => Object.keys(defaultSettings.dialTools), [defaultSettings]);
171
+ const allowedFloatingKeys = (0, _react.useMemo)(() => Object.keys(defaultSettings.floatingTools).filter(key => key !== "environment"), [defaultSettings]);
172
+ const [settings, setSettings] = (0, _react.useState)(initialSettings || defaultSettings);
173
+ const [activeTab, setActiveTab] = (0, _react.useState)("dial");
174
+ const [activeTabLoaded, setActiveTabLoaded] = (0, _react.useState)(false);
175
+ const [expandedSettings, setExpandedSettings] = (0, _react.useState)(new Set());
176
+ const [showLicenseModal, setShowLicenseModal] = (0, _react.useState)(false);
177
+ const [licenseModalForDeviceRegistration, setLicenseModalForDeviceRegistration] = (0, _react.useState)(false);
178
+
179
+ // Load persisted active tab on mount
180
+ (0, _react.useEffect)(() => {
181
+ const loadActiveTab = async () => {
182
+ try {
183
+ const savedTab = await (0, _sharedUi.safeGetItem)(_sharedUi.devToolsStorageKeys.settings.activeTab());
184
+ if (savedTab && ["dial", "floating", "settings", "pro"].includes(savedTab)) {
185
+ setActiveTab(savedTab);
186
+ }
187
+ } catch (error) {
188
+ // Failed to load active tab - use default
189
+ } finally {
190
+ setActiveTabLoaded(true);
191
+ }
192
+ };
193
+ loadActiveTab();
194
+ }, []);
195
+
196
+ // Persist active tab when it changes
197
+ (0, _react.useEffect)(() => {
198
+ // Only persist after initial state is loaded to avoid overwriting with default
199
+ if (!activeTabLoaded) return;
200
+ (0, _sharedUi.safeSetItem)(_sharedUi.devToolsStorageKeys.settings.activeTab(), activeTab).catch(error => {
201
+ // Failed to save active tab - continue without persistence
202
+ console.warn("Failed to save settings active tab:", error);
203
+ });
204
+ }, [activeTab, activeTabLoaded]);
205
+
206
+ // License hooks
207
+ const useLicenseHook = getUseLicense();
208
+ const useSeatsHook = getUseSeats();
209
+ const useDevicesHook = getUseDevices();
210
+ const license = useLicenseHook?.();
211
+ const seats = useSeatsHook?.();
212
+ const devicesData = useDevicesHook?.();
213
+ const isPro = license?.isPro ?? false;
214
+
215
+ // Devices data
216
+ const devices = devicesData?.devices ?? [];
217
+ const devicesLoading = devicesData?.isLoading ?? false;
218
+ const devicesError = devicesData?.error ?? null;
219
+ const refreshDevices = devicesData?.refreshDevices;
220
+ const registerDevice = devicesData?.registerDevice;
221
+ const deactivateDevice = devicesData?.deactivateDevice;
222
+ const isCurrentDeviceRegistered = devicesData?.isCurrentDeviceRegistered ?? false;
223
+ const [deactivatingDeviceId, setDeactivatingDeviceId] = (0, _react.useState)(null);
224
+ const [storageBackend, setStorageBackend] = (0, _react.useState)(null);
225
+ const [isClearing, setIsClearing] = (0, _react.useState)(false);
226
+ const [clearSuccess, setClearSuccess] = (0, _react.useState)(false);
227
+ const [isStorageExpanded, setIsStorageExpanded] = (0, _react.useState)(false);
228
+ const [savedKeys, setSavedKeys] = (0, _react.useState)([]);
229
+ const [savedKeysLoading, setSavedKeysLoading] = (0, _react.useState)(false);
230
+ const [copySuccess, setCopySuccess] = (0, _react.useState)(false);
231
+ const [isExportExpanded, setIsExportExpanded] = (0, _react.useState)(false);
232
+ const insets = (0, _sharedUi.useSafeAreaInsets)();
233
+
234
+ // Check if in development mode - used to show dev-only features like Export Config
235
+ const isDevelopmentMode = typeof __DEV__ !== "undefined" && __DEV__;
236
+ const screenHeight = _reactNative.Dimensions.get("window").height;
237
+ const screenWidth = _reactNative.Dimensions.get("window").width;
238
+ const modalHeight = Math.floor(screenHeight * 0.33); // 1/3 of screen height
239
+ const modalWidth = Math.min(screenWidth - 32, 400); // Modal width with padding
240
+
241
+ const loadSettings = (0, _react.useCallback)(async () => {
242
+ try {
243
+ const savedSettings = await (0, _sharedUi.safeGetItem)(STORAGE_KEY);
244
+ if (savedSettings) {
245
+ const parsed = JSON.parse(savedSettings);
246
+ const merged = mergeWithDefaults(defaultSettings, parsed, {
247
+ allowedDialKeys,
248
+ allowedFloatingKeys
249
+ });
250
+ setSettings(merged);
251
+ return;
252
+ }
253
+ setSettings(defaultSettings);
254
+ } catch (error) {
255
+ console.error("Failed to load dev tools settings:", error);
256
+ setSettings(defaultSettings);
257
+ }
258
+ }, [defaultSettings, allowedDialKeys, allowedFloatingKeys]);
259
+ (0, _react.useEffect)(() => {
260
+ loadSettings();
261
+ }, [loadSettings]);
262
+
263
+ // Load storage backend type
264
+ (0, _react.useEffect)(() => {
265
+ (0, _sharedUi.getStorageBackendType)().then(setStorageBackend);
266
+ }, []);
267
+
268
+ // Load saved keys when storage card is expanded
269
+ (0, _react.useEffect)(() => {
270
+ if (isStorageExpanded) {
271
+ setSavedKeysLoading(true);
272
+ _sharedUi.persistentStorage.getAllKeys().then(keys => {
273
+ setSavedKeys(keys.sort());
274
+ }).catch(() => {
275
+ setSavedKeys([]);
276
+ }).finally(() => {
277
+ setSavedKeysLoading(false);
278
+ });
279
+ }
280
+ }, [isStorageExpanded]);
281
+ const saveSettings = async newSettings => {
282
+ try {
283
+ const limitedSettings = {
284
+ ...newSettings,
285
+ dialTools: enforceDialLimit(newSettings.dialTools)
286
+ };
287
+ await (0, _sharedUi.safeSetItem)(STORAGE_KEY, JSON.stringify(limitedSettings));
288
+ setSettings(limitedSettings);
289
+ onSettingsChange?.(limitedSettings);
290
+ // Notify listeners (e.g., floating bubble) to refresh immediately
291
+ _settingsBus.settingsBus.emit(limitedSettings);
292
+ } catch (error) {
293
+ console.error("Failed to save dev tools settings:", error);
294
+ }
295
+ };
296
+ const toggleDialTool = tool => {
297
+ const currentEnabled = Object.values(settings.dialTools).filter(v => v).length;
298
+ const isCurrentlyEnabled = settings.dialTools[tool];
299
+
300
+ // If trying to enable and already at 6, don't allow
301
+ if (!isCurrentlyEnabled && currentEnabled >= MAX_DIAL_SLOTS) {
302
+ return; // Could also show a toast/alert here
303
+ }
304
+ const newSettings = {
305
+ ...settings,
306
+ dialTools: {
307
+ ...settings.dialTools,
308
+ [tool]: !settings.dialTools[tool]
309
+ }
310
+ };
311
+ saveSettings(newSettings);
312
+ };
313
+ const toggleFloatingTool = tool => {
314
+ const newSettings = {
315
+ ...settings,
316
+ floatingTools: {
317
+ ...settings.floatingTools,
318
+ [tool]: !settings.floatingTools[tool]
319
+ }
320
+ };
321
+ saveSettings(newSettings);
322
+ };
323
+ const toggleGlobalSetting = setting => {
324
+ const newSettings = {
325
+ ...settings,
326
+ globalSettings: {
327
+ ...settings.globalSettings,
328
+ [setting]: !settings.globalSettings?.[setting]
329
+ }
330
+ };
331
+ saveSettings(newSettings);
332
+ };
333
+ const handleClearStorage = async () => {
334
+ setIsClearing(true);
335
+ setClearSuccess(false);
336
+ try {
337
+ await _sharedUi.persistentStorage.clear();
338
+ setClearSuccess(true);
339
+ // Reset settings to defaults after clearing
340
+ setSettings(defaultSettings);
341
+ _settingsBus.settingsBus.emit(defaultSettings);
342
+ // Reset success state after 2 seconds
343
+ setTimeout(() => setClearSuccess(false), 2000);
344
+ } catch (error) {
345
+ console.error("Failed to clear storage:", error);
346
+ } finally {
347
+ setIsClearing(false);
348
+ }
349
+ };
350
+
351
+ // Get the enabled tools as arrays for display and copy
352
+ const enabledConfig = (0, _react.useMemo)(() => {
353
+ const enabledFloating = Object.entries(settings.floatingTools).filter(([_, enabled]) => enabled).map(([id]) => id);
354
+ const enabledDial = Object.entries(settings.dialTools).filter(([_, enabled]) => enabled).map(([id]) => id);
355
+ return {
356
+ floating: enabledFloating,
357
+ dial: enabledDial
358
+ };
359
+ }, [settings]);
360
+
361
+ // Generate exportable code snippet from current settings
362
+ // Only outputs the defaultFloatingTools and defaultDialTools props
363
+ // so users can add them to their existing FloatingDevTools config
364
+ const generateConfigCode = (0, _react.useCallback)(() => {
365
+ const {
366
+ floating,
367
+ dial
368
+ } = enabledConfig;
369
+
370
+ // Build only the default config props (not the full component)
371
+ const props = [];
372
+ if (floating.length > 0) {
373
+ const floatingStr = floating.map(id => `'${id}'`).join(', ');
374
+ props.push(`defaultFloatingTools={[${floatingStr}]}`);
375
+ }
376
+ if (dial.length > 0) {
377
+ const dialStr = dial.map(id => `'${id}'`).join(', ');
378
+ props.push(`defaultDialTools={[${dialStr}]}`);
379
+ }
380
+ if (props.length === 0) {
381
+ return `// No tools enabled`;
382
+ }
383
+ return props.join('\n');
384
+ }, [enabledConfig]);
385
+ const handleCopyConfig = async () => {
386
+ const code = generateConfigCode();
387
+ const success = await (0, _sharedUi.copyToClipboard)(code);
388
+ if (success) {
389
+ setCopySuccess(true);
390
+ setTimeout(() => setCopySuccess(false), 2000);
391
+ }
392
+ };
393
+ const handleRemoveDevice = async (deviceId, deviceName, isCurrentDevice) => {
394
+ if (!deactivateDevice) return;
395
+ const message = isCurrentDevice ? "This will remove your current device. You'll need to re-register to use Pro features on this device." : `Remove "${deviceName}" from your license?`;
396
+ const {
397
+ Alert
398
+ } = require("react-native");
399
+ const doRemove = await new Promise(resolve => {
400
+ Alert.alert("Remove Device", message, [{
401
+ text: "Cancel",
402
+ style: "cancel",
403
+ onPress: () => resolve(false)
404
+ }, {
405
+ text: "Remove",
406
+ style: "destructive",
407
+ onPress: () => resolve(true)
408
+ }]);
409
+ });
410
+ if (!doRemove) return;
411
+ setDeactivatingDeviceId(deviceId);
412
+ try {
413
+ await deactivateDevice(deviceId);
414
+ } catch (error) {
415
+ console.error("Failed to remove device:", error);
416
+ } finally {
417
+ setDeactivatingDeviceId(null);
418
+ }
419
+ };
420
+ const handleRegisterDevice = () => {
421
+ // Open the license modal in device registration mode (skip license key entry)
422
+ setLicenseModalForDeviceRegistration(true);
423
+ setShowLicenseModal(true);
424
+ };
425
+ const formatDeviceDate = date => {
426
+ const now = new Date();
427
+ const diffMs = now.getTime() - date.getTime();
428
+ const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
429
+ if (diffDays === 0) return "Today";
430
+ if (diffDays === 1) return "Yesterday";
431
+ if (diffDays < 7) return `${diffDays} days ago`;
432
+ return date.toLocaleDateString(undefined, {
433
+ month: "short",
434
+ day: "numeric",
435
+ year: date.getFullYear() !== now.getFullYear() ? "numeric" : undefined
436
+ });
437
+ };
438
+
439
+ // Modal is fixed to bottom sheet mode
440
+ const handleModeChange = (0, _react.useCallback)(_mode => {
441
+ // Mode changes handled by JsModal
442
+ }, []);
443
+ const getToolDescription = tool => {
444
+ // Get description from availableApps
445
+ const app = availableApps.find(a => a.id === tool);
446
+ if (app?.description) {
447
+ return app.description;
448
+ }
449
+ if (tool === "environment") {
450
+ return "Environment badge.";
451
+ }
452
+ return "";
453
+ };
454
+ const getToolLabel = tool => {
455
+ if (tool === "environment") {
456
+ return "ENV BADGE";
457
+ }
458
+ const app = availableApps.find(a => a.id === tool);
459
+ return app?.name ?? tool.toUpperCase().replace(/_/g, " ");
460
+ };
461
+
462
+ // Clean tool card renderer - unified Buoy theme
463
+ const renderToolCard = (keyName, value, disabled, onToggle) => {
464
+ const getToolIcon = tool => {
465
+ switch (tool) {
466
+ case "query":
467
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.QueryIcon, {
468
+ size: 16
469
+ });
470
+ case "env":
471
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.EnvIcon, {
472
+ size: 16
473
+ });
474
+ case "sentry":
475
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SentryBugIcon, {
476
+ size: 16,
477
+ colorPreset: "red",
478
+ noBackground: true
479
+ });
480
+ case "storage":
481
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.StorageIcon, {
482
+ size: 16
483
+ });
484
+ case "wifi":
485
+ case "query-wifi-toggle":
486
+ // Support both IDs for wifi toggle
487
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.WifiCircuitIcon, {
488
+ size: 16,
489
+ colorPreset: "green",
490
+ strength: 4,
491
+ noBackground: true
492
+ });
493
+ case "route-events":
494
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.RoutesIcon, {
495
+ size: 16
496
+ });
497
+ case "network":
498
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.NetworkIcon, {
499
+ size: 16
500
+ });
501
+ case "environment":
502
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.EnvIcon, {
503
+ size: 16
504
+ });
505
+ case "debug-borders":
506
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Layers, {
507
+ size: 16,
508
+ color: _sharedUi.buoyColors.primary
509
+ });
510
+ case "highlight-updates":
511
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.RenderCountIcon, {
512
+ size: 16,
513
+ color: _sharedUi.buoyColors.primary
514
+ });
515
+ case "highlight-updates-modal":
516
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.HighlighterIcon, {
517
+ size: 16
518
+ });
519
+ case "benchmark":
520
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.BenchmarkIcon, {
521
+ size: 16,
522
+ color: _sharedUi.buoyColors.textSecondary
523
+ });
524
+ default:
525
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Info, {
526
+ size: 16,
527
+ color: _sharedUi.buoyColors.textSecondary
528
+ });
529
+ }
530
+ };
531
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
532
+ activeOpacity: disabled ? 1 : 0.85,
533
+ onPress: () => !disabled && onToggle(),
534
+ style: {
535
+ marginBottom: 8,
536
+ opacity: disabled ? 0.5 : 1
537
+ },
538
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
539
+ style: [styles.glassCard, value && styles.glassCardActive],
540
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
541
+ style: styles.glassCardInner,
542
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
543
+ style: [styles.iconContainer, value && styles.iconContainerActive],
544
+ children: getToolIcon(keyName)
545
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
546
+ style: styles.toolInfo,
547
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
548
+ style: [styles.toolName, value && styles.toolNameActive],
549
+ children: [getToolLabel(keyName), disabled ? " (MAX 6)" : ""]
550
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
551
+ style: styles.toolDescription,
552
+ numberOfLines: 1,
553
+ children: getToolDescription(keyName)
554
+ })]
555
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
556
+ style: [styles.pillToggle, value ? styles.pillToggleOn : styles.pillToggleOff, disabled && {
557
+ opacity: 0.5
558
+ }],
559
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
560
+ style: [styles.pillToggleText, value ? styles.pillToggleTextOn : styles.pillToggleTextOff],
561
+ children: value ? "ON" : "OFF"
562
+ })
563
+ })]
564
+ })
565
+ })
566
+ }, keyName);
567
+ };
568
+
569
+ // Toggle expanded state for a setting card
570
+ const toggleSettingExpanded = settingKey => {
571
+ setExpandedSettings(prev => {
572
+ const newSet = new Set(prev);
573
+ if (newSet.has(settingKey)) {
574
+ newSet.delete(settingKey);
575
+ } else {
576
+ newSet.add(settingKey);
577
+ }
578
+ return newSet;
579
+ });
580
+ };
581
+
582
+ // Render a global setting toggle card with expandable description
583
+ const renderGlobalSettingCard = (settingKey, label, category, shortDescription, fullDescription, recommendation) => {
584
+ const value = settings.globalSettings?.[settingKey] ?? false;
585
+ const isExpanded = expandedSettings.has(settingKey);
586
+ const color = _sharedUi.buoyColors.primary;
587
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
588
+ style: {
589
+ marginBottom: 10
590
+ },
591
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
592
+ activeOpacity: 0.85,
593
+ onPress: () => toggleSettingExpanded(settingKey),
594
+ style: [styles.expandableCard, isExpanded && {
595
+ borderColor: color,
596
+ borderWidth: 2,
597
+ shadowColor: color,
598
+ shadowOpacity: 0.8,
599
+ shadowRadius: 20,
600
+ shadowOffset: {
601
+ width: 0,
602
+ height: 0
603
+ },
604
+ elevation: 10,
605
+ transform: [{
606
+ scale: 1.01
607
+ }]
608
+ }],
609
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
610
+ style: styles.expandableCardHeader,
611
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
612
+ style: styles.expandableCardCategory,
613
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
614
+ style: styles.expandableCardCategoryText,
615
+ children: category
616
+ })
617
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
618
+ style: styles.expandableCardTitle,
619
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
620
+ style: styles.expandableCardTitleText,
621
+ children: label
622
+ }), !isExpanded && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
623
+ style: styles.expandableCardSubtitle,
624
+ numberOfLines: 1,
625
+ children: shortDescription
626
+ })]
627
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
628
+ style: styles.expandableCardActions,
629
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
630
+ onPress: () => toggleGlobalSetting(settingKey),
631
+ activeOpacity: 0.8,
632
+ style: [styles.pillToggle, {
633
+ backgroundColor: value ? `${_sharedUi.buoyColors.success}33` : _sharedUi.buoyColors.hover,
634
+ borderColor: value ? `${_sharedUi.buoyColors.success}88` : _sharedUi.buoyColors.border,
635
+ shadowColor: value ? _sharedUi.buoyColors.success : "transparent",
636
+ shadowOffset: {
637
+ width: 0,
638
+ height: 0
639
+ },
640
+ shadowOpacity: value ? 0.4 : 0,
641
+ shadowRadius: value ? 8 : 0
642
+ }],
643
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
644
+ style: [styles.pillToggleText, {
645
+ color: value ? _sharedUi.buoyColors.success : _sharedUi.buoyColors.textMuted
646
+ }],
647
+ children: value ? "ON" : "OFF"
648
+ })
649
+ }), isExpanded ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChevronDown, {
650
+ size: 18,
651
+ color: "#7F91B2"
652
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChevronRightIcon, {
653
+ size: 18,
654
+ color: "#7F91B2"
655
+ })]
656
+ })]
657
+ }), isExpanded && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
658
+ style: styles.expandableCardBody,
659
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
660
+ style: styles.expandableCardSection,
661
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
662
+ style: styles.expandableCardSectionTitle,
663
+ children: "DESCRIPTION"
664
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
665
+ style: styles.expandableCardSectionText,
666
+ children: fullDescription
667
+ })]
668
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
669
+ style: styles.expandableCardSection,
670
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
671
+ style: styles.expandableCardSectionTitle,
672
+ children: "RECOMMENDATION"
673
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
674
+ style: styles.expandableCardSectionText,
675
+ children: recommendation
676
+ })]
677
+ })]
678
+ })]
679
+ })
680
+ }, settingKey);
681
+ };
682
+ const renderContent = () => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
683
+ style: styles.container,
684
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
685
+ style: styles.scrollContent,
686
+ showsVerticalScrollIndicator: false,
687
+ contentContainerStyle: styles.scrollContainer,
688
+ children: [activeTab === "dial" && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
689
+ style: styles.section,
690
+ children: (() => {
691
+ const enabledCount = Object.values(settings.dialTools).filter(v => v).length;
692
+ const isAtLimit = enabledCount >= MAX_DIAL_SLOTS;
693
+ return Object.entries(settings.dialTools).map(([key, value]) => {
694
+ const isDisabled = !value && isAtLimit;
695
+ return renderToolCard(key, value, isDisabled, () => toggleDialTool(key));
696
+ });
697
+ })()
698
+ }), activeTab === "floating" && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
699
+ style: styles.section,
700
+ children: Object.entries(settings.floatingTools).map(([key, value]) => renderToolCard(key, value, false, () => toggleFloatingTool(key)))
701
+ }), activeTab === "settings" && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
702
+ style: styles.section,
703
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
704
+ style: [styles.storageStatusCard, isStorageExpanded && {
705
+ borderColor: storageBackend === "filesystem" ? _sharedUi.buoyColors.success + "60" : storageBackend === "asyncstorage" ? _sharedUi.buoyColors.warning + "60" : _sharedUi.buoyColors.error + "60"
706
+ }],
707
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
708
+ activeOpacity: 0.8,
709
+ onPress: () => setIsStorageExpanded(!isStorageExpanded),
710
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
711
+ style: styles.storageStatusHeader,
712
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
713
+ style: [styles.storageStatusIcon, {
714
+ backgroundColor: storageBackend === "filesystem" ? _sharedUi.buoyColors.success + "15" : storageBackend === "asyncstorage" ? _sharedUi.buoyColors.warning + "15" : _sharedUi.buoyColors.error + "15"
715
+ }],
716
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Database, {
717
+ size: 18,
718
+ color: storageBackend === "filesystem" ? _sharedUi.buoyColors.success : storageBackend === "asyncstorage" ? _sharedUi.buoyColors.warning : _sharedUi.buoyColors.error
719
+ })
720
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
721
+ style: styles.storageStatusInfo,
722
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
723
+ style: styles.storageStatusLabel,
724
+ children: "STORAGE TYPE"
725
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
726
+ style: styles.storageStatusBadgeContainer,
727
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
728
+ style: [styles.storageStatusBadge, {
729
+ backgroundColor: storageBackend === "filesystem" ? _sharedUi.buoyColors.success + "20" : storageBackend === "asyncstorage" ? _sharedUi.buoyColors.warning + "20" : _sharedUi.buoyColors.error + "20",
730
+ borderColor: storageBackend === "filesystem" ? _sharedUi.buoyColors.success + "60" : storageBackend === "asyncstorage" ? _sharedUi.buoyColors.warning + "60" : _sharedUi.buoyColors.error + "60"
731
+ }],
732
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
733
+ style: [styles.storageStatusBadgeText, {
734
+ color: storageBackend === "filesystem" ? _sharedUi.buoyColors.success : storageBackend === "asyncstorage" ? _sharedUi.buoyColors.warning : _sharedUi.buoyColors.error
735
+ }],
736
+ children: storageBackend === "filesystem" ? "FILE SYSTEM" : storageBackend === "asyncstorage" ? "ASYNC STORAGE" : storageBackend === "memory" ? "MEMORY" : "LOADING..."
737
+ })
738
+ }), storageBackend === "filesystem" && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CheckCircle2, {
739
+ size: 14,
740
+ color: _sharedUi.buoyColors.success
741
+ }), storageBackend === "asyncstorage" && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.AlertTriangle, {
742
+ size: 14,
743
+ color: _sharedUi.buoyColors.warning
744
+ }), storageBackend === "memory" && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.AlertTriangle, {
745
+ size: 14,
746
+ color: _sharedUi.buoyColors.error
747
+ })]
748
+ })]
749
+ }), isStorageExpanded ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChevronDown, {
750
+ size: 18,
751
+ color: _sharedUi.buoyColors.textMuted
752
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChevronRightIcon, {
753
+ size: 18,
754
+ color: _sharedUi.buoyColors.textMuted
755
+ })]
756
+ })
757
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
758
+ style: styles.storageStatusDescription,
759
+ children: storageBackend === "filesystem" ? "Settings persist independently and survive AsyncStorage.clear() calls during logout." : storageBackend === "asyncstorage" ? "Settings may be lost if AsyncStorage is cleared during logout." : storageBackend === "memory" ? "Settings are stored in memory only and will be lost on app restart." : "Checking storage backend..."
760
+ }), storageBackend === "asyncstorage" && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
761
+ style: styles.adviceHint,
762
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Zap, {
763
+ size: 14,
764
+ color: _sharedUi.buoyColors.warning
765
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
766
+ style: styles.adviceHintText,
767
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
768
+ style: styles.adviceHintBold,
769
+ children: "Tip:"
770
+ }), " Install", " ", /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
771
+ style: styles.adviceHintCode,
772
+ children: "expo-file-system"
773
+ }), " to persist settings through logout flows."]
774
+ })]
775
+ }), storageBackend === "memory" && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
776
+ style: styles.adviceHintsContainer,
777
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
778
+ style: [styles.adviceHint, {
779
+ backgroundColor: _sharedUi.buoyColors.error + "10"
780
+ }],
781
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.AlertTriangle, {
782
+ size: 14,
783
+ color: _sharedUi.buoyColors.error
784
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
785
+ style: [styles.adviceHintText, {
786
+ color: _sharedUi.buoyColors.error
787
+ }],
788
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
789
+ style: styles.adviceHintBold,
790
+ children: "No persistent storage available!"
791
+ }), " Settings will reset on every app restart."]
792
+ })]
793
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
794
+ style: styles.adviceHint,
795
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Zap, {
796
+ size: 14,
797
+ color: _sharedUi.buoyColors.primary
798
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
799
+ style: styles.adviceHintText,
800
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
801
+ style: styles.adviceHintBold,
802
+ children: "Best:"
803
+ }), " Install", " ", /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
804
+ style: styles.adviceHintCode,
805
+ children: "expo-file-system"
806
+ }), " for logout-safe persistence."]
807
+ })]
808
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
809
+ style: styles.adviceHint,
810
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.HardDrive, {
811
+ size: 14,
812
+ color: _sharedUi.buoyColors.textMuted
813
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
814
+ style: styles.adviceHintText,
815
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
816
+ style: styles.adviceHintBold,
817
+ children: "Alternative:"
818
+ }), " Install", " ", /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
819
+ style: styles.adviceHintCode,
820
+ children: "@react-native-async-storage/async-storage"
821
+ }), " for basic persistence."]
822
+ })]
823
+ })]
824
+ }), isStorageExpanded && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
825
+ style: styles.storageExpandedContent,
826
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
827
+ style: styles.storageExpandedHeader,
828
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.FileText, {
829
+ size: 14,
830
+ color: _sharedUi.buoyColors.primary
831
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
832
+ style: styles.storageExpandedTitle,
833
+ children: "SAVED SETTINGS"
834
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
835
+ style: styles.storageExpandedCount,
836
+ children: savedKeysLoading ? "..." : `${savedKeys.length} keys`
837
+ })]
838
+ }), savedKeysLoading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
839
+ style: styles.storageKeyItem,
840
+ children: "Loading..."
841
+ }) : savedKeys.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
842
+ style: styles.storageKeyItemEmpty,
843
+ children: "No settings saved yet"
844
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
845
+ style: styles.storageKeysList,
846
+ nestedScrollEnabled: true,
847
+ showsVerticalScrollIndicator: true,
848
+ children: savedKeys.map(key => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
849
+ style: styles.storageKeyItem,
850
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
851
+ style: styles.storageKeyText,
852
+ numberOfLines: 1,
853
+ children: key.replace("@react_buoy_", "")
854
+ })
855
+ }, key))
856
+ })]
857
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
858
+ style: [styles.clearStorageButton, isClearing && styles.clearStorageButtonDisabled, clearSuccess && styles.clearStorageButtonSuccess],
859
+ onPress: handleClearStorage,
860
+ disabled: isClearing,
861
+ activeOpacity: 0.7,
862
+ children: [clearSuccess ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CheckCircle2, {
863
+ size: 14,
864
+ color: _sharedUi.buoyColors.success
865
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Trash2, {
866
+ size: 14,
867
+ color: isClearing ? _sharedUi.buoyColors.textMuted : _sharedUi.buoyColors.error
868
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
869
+ style: [styles.clearStorageButtonText, clearSuccess && {
870
+ color: _sharedUi.buoyColors.success
871
+ }, isClearing && {
872
+ color: _sharedUi.buoyColors.textMuted
873
+ }],
874
+ children: clearSuccess ? "CLEARED" : isClearing ? "CLEARING..." : "CLEAR ALL SETTINGS"
875
+ })]
876
+ })]
877
+ }), renderGlobalSettingCard("enableSharedModalDimensions", "SHARED MODAL SIZE", "MODAL", "Sync dimensions across all tools", "When enabled, all tool modals will share the same size and position. Resizing one modal will affect all others. When disabled, each tool remembers its own size and position independently.", "Keep OFF for the best experience. This allows you to customize each tool's modal size separately. Enable only if you prefer uniform modal sizes across all dev tools."), isDevelopmentMode && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
878
+ style: styles.exportConfigCard,
879
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
880
+ activeOpacity: 0.85,
881
+ onPress: () => setIsExportExpanded(!isExportExpanded),
882
+ style: styles.exportConfigHeader,
883
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
884
+ style: styles.exportConfigIconContainer,
885
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.FileCode, {
886
+ size: 18,
887
+ color: _sharedUi.buoyColors.success
888
+ })
889
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
890
+ style: styles.exportConfigInfo,
891
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
892
+ style: styles.exportConfigLabel,
893
+ children: "EXPORT CONFIG"
894
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
895
+ style: styles.exportConfigHint,
896
+ children: "Save your settings to code"
897
+ })]
898
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
899
+ style: styles.exportConfigActions,
900
+ children: isExportExpanded ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChevronDown, {
901
+ size: 18,
902
+ color: _sharedUi.buoyColors.textMuted
903
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChevronRightIcon, {
904
+ size: 18,
905
+ color: _sharedUi.buoyColors.textMuted
906
+ })
907
+ })]
908
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
909
+ style: styles.exportHintBanner,
910
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Zap, {
911
+ size: 14,
912
+ color: _sharedUi.buoyColors.warning
913
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
914
+ style: styles.exportHintText,
915
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
916
+ style: styles.exportHintBold,
917
+ children: "New!"
918
+ }), " Configure your tools above, then export to set team defaults."]
919
+ })]
920
+ }), isExportExpanded && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
921
+ style: styles.exportCodeContainer,
922
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
923
+ style: styles.exportCodeHeader,
924
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
925
+ style: styles.exportCodeTitle,
926
+ children: "PROPS TO ADD"
927
+ })
928
+ }), enabledConfig.floating.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
929
+ style: styles.exportJsonBlock,
930
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
931
+ style: styles.exportJsonProp,
932
+ children: "defaultFloatingTools"
933
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
934
+ style: styles.exportJsonEquals,
935
+ children: "="
936
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
937
+ style: styles.exportJsonBracket,
938
+ children: "{"
939
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
940
+ style: styles.exportJsonArrayBracket,
941
+ children: "["
942
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
943
+ style: styles.exportJsonArrayContent,
944
+ children: enabledConfig.floating.map((id, index) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
945
+ style: styles.exportJsonArrayItem,
946
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
947
+ style: styles.exportJsonString,
948
+ children: ["'", id, "'"]
949
+ }), index < enabledConfig.floating.length - 1 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
950
+ style: styles.exportJsonComma,
951
+ children: ","
952
+ })]
953
+ }, id))
954
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
955
+ style: styles.exportJsonArrayBracket,
956
+ children: "]"
957
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
958
+ style: styles.exportJsonBracket,
959
+ children: "}"
960
+ })]
961
+ }), enabledConfig.dial.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
962
+ style: [styles.exportJsonBlock, enabledConfig.floating.length > 0 && {
963
+ marginTop: 8
964
+ }],
965
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
966
+ style: styles.exportJsonProp,
967
+ children: "defaultDialTools"
968
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
969
+ style: styles.exportJsonEquals,
970
+ children: "="
971
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
972
+ style: styles.exportJsonBracket,
973
+ children: "{"
974
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
975
+ style: styles.exportJsonArrayBracket,
976
+ children: "["
977
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
978
+ style: styles.exportJsonArrayContent,
979
+ children: enabledConfig.dial.map((id, index) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
980
+ style: styles.exportJsonArrayItem,
981
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
982
+ style: styles.exportJsonString,
983
+ children: ["'", id, "'"]
984
+ }), index < enabledConfig.dial.length - 1 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
985
+ style: styles.exportJsonComma,
986
+ children: ","
987
+ })]
988
+ }, id))
989
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
990
+ style: styles.exportJsonArrayBracket,
991
+ children: "]"
992
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
993
+ style: styles.exportJsonBracket,
994
+ children: "}"
995
+ })]
996
+ }), enabledConfig.floating.length === 0 && enabledConfig.dial.length === 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
997
+ style: styles.exportJsonBlock,
998
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
999
+ style: styles.exportJsonComment,
1000
+ children: "// No tools enabled"
1001
+ })
1002
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
1003
+ style: styles.exportCodeDescription,
1004
+ children: ["Add these props to your existing", " ", /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1005
+ style: styles.exportCodeInline,
1006
+ children: "<FloatingDevTools />"
1007
+ }), " ", "component to set team defaults."]
1008
+ })]
1009
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
1010
+ style: [styles.exportCopyButton, copySuccess && styles.exportCopyButtonSuccess],
1011
+ onPress: handleCopyConfig,
1012
+ activeOpacity: 0.7,
1013
+ children: [copySuccess ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CheckCircle2, {
1014
+ size: 14,
1015
+ color: _sharedUi.buoyColors.success
1016
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Copy, {
1017
+ size: 14,
1018
+ color: _sharedUi.buoyColors.success
1019
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1020
+ style: [styles.exportCopyButtonText, copySuccess && {
1021
+ color: _sharedUi.buoyColors.success
1022
+ }],
1023
+ children: copySuccess ? "COPIED!" : "COPY CONFIG TO CLIPBOARD"
1024
+ })]
1025
+ })]
1026
+ })]
1027
+ }), activeTab === "pro" && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1028
+ style: styles.proContainer,
1029
+ children: isPro ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
1030
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1031
+ style: styles.proSection,
1032
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.SectionHeader, {
1033
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Icon, {
1034
+ icon: _sharedUi.Zap,
1035
+ color: _sharedUi.buoyColors.primary,
1036
+ size: 12
1037
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Title, {
1038
+ children: "LICENSE STATUS"
1039
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Badge, {
1040
+ count: "Active",
1041
+ color: _sharedUi.buoyColors.success
1042
+ })]
1043
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1044
+ style: styles.proSectionContent,
1045
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1046
+ style: styles.proStatsRow,
1047
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1048
+ style: styles.proStatItem,
1049
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1050
+ style: styles.proStatValue,
1051
+ children: seats?.used ?? 0
1052
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1053
+ style: styles.proStatLabel,
1054
+ children: "USED"
1055
+ })]
1056
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1057
+ style: styles.proStatDivider
1058
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1059
+ style: styles.proStatItem,
1060
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1061
+ style: styles.proStatValue,
1062
+ children: seats?.total ?? "∞"
1063
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1064
+ style: styles.proStatLabel,
1065
+ children: "LIMIT"
1066
+ })]
1067
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1068
+ style: styles.proStatDivider
1069
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1070
+ style: styles.proStatItem,
1071
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1072
+ style: [styles.proStatValue, (seats?.remaining ?? 0) <= 0 && styles.proStatValueDanger],
1073
+ children: seats?.remaining ?? "∞"
1074
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1075
+ style: styles.proStatLabel,
1076
+ children: "AVAILABLE"
1077
+ })]
1078
+ })]
1079
+ })
1080
+ })]
1081
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1082
+ style: styles.proSection,
1083
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.SectionHeader, {
1084
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Icon, {
1085
+ icon: _sharedUi.Smartphone,
1086
+ color: _sharedUi.buoyColors.info,
1087
+ size: 12
1088
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Title, {
1089
+ children: "REGISTERED DEVICES"
1090
+ }), devices.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Badge, {
1091
+ count: devices.length,
1092
+ color: _sharedUi.buoyColors.info
1093
+ }), refreshDevices && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Actions, {
1094
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1095
+ onPress: () => refreshDevices(),
1096
+ disabled: devicesLoading,
1097
+ style: {
1098
+ marginLeft: 8
1099
+ },
1100
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.RefreshCw, {
1101
+ size: 14,
1102
+ color: devicesLoading ? _sharedUi.buoyColors.textMuted : _sharedUi.buoyColors.textSecondary
1103
+ })
1104
+ })
1105
+ })]
1106
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1107
+ style: styles.proSectionContent,
1108
+ children: [devicesLoading && devices.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1109
+ style: styles.proEmptyState,
1110
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1111
+ style: styles.proEmptyStateText,
1112
+ children: "Loading devices..."
1113
+ })
1114
+ }) : devicesError ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1115
+ style: styles.proErrorState,
1116
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.AlertTriangle, {
1117
+ size: 16,
1118
+ color: _sharedUi.buoyColors.error
1119
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1120
+ style: styles.proErrorStateText,
1121
+ children: devicesError
1122
+ })]
1123
+ }) : devices.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1124
+ style: styles.proEmptyState,
1125
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Smartphone, {
1126
+ size: 24,
1127
+ color: _sharedUi.buoyColors.textMuted
1128
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1129
+ style: styles.proEmptyStateText,
1130
+ children: "No devices registered"
1131
+ })]
1132
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1133
+ style: styles.proDevicesList,
1134
+ children: devices.map(device => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1135
+ style: [styles.proDeviceRow, device.isCurrentDevice && styles.proDeviceRowCurrent],
1136
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1137
+ style: styles.proDeviceIcon,
1138
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Smartphone, {
1139
+ size: 16,
1140
+ color: _sharedUi.buoyColors.text
1141
+ })
1142
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1143
+ style: styles.proDeviceInfo,
1144
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1145
+ style: styles.proDeviceNameRow,
1146
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1147
+ style: styles.proDeviceName,
1148
+ numberOfLines: 1,
1149
+ children: device.name
1150
+ }), device.isCurrentDevice && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1151
+ style: styles.proDeviceBadge,
1152
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1153
+ style: styles.proDeviceBadgeText,
1154
+ children: "THIS DEVICE"
1155
+ })
1156
+ })]
1157
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
1158
+ style: styles.proDeviceDetails,
1159
+ children: [device.platform, device.osVersion ? ` ${device.osVersion}` : "", device.model ? ` • ${device.model}` : ""]
1160
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
1161
+ style: styles.proDeviceDate,
1162
+ children: ["Registered ", formatDeviceDate(device.registeredAt)]
1163
+ })]
1164
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1165
+ style: styles.proDeviceRemoveBtn,
1166
+ onPress: () => handleRemoveDevice(device.id, device.name, device.isCurrentDevice),
1167
+ disabled: deactivatingDeviceId === device.id,
1168
+ children: deactivatingDeviceId === device.id ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.RefreshCw, {
1169
+ size: 14,
1170
+ color: _sharedUi.buoyColors.textMuted
1171
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Trash2, {
1172
+ size: 14,
1173
+ color: _sharedUi.buoyColors.error
1174
+ })
1175
+ })]
1176
+ }, device.id))
1177
+ }), !isCurrentDeviceRegistered && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
1178
+ style: styles.proRegisterDeviceBtn,
1179
+ onPress: handleRegisterDevice,
1180
+ disabled: devicesLoading,
1181
+ activeOpacity: 0.7,
1182
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Plus, {
1183
+ size: 14,
1184
+ color: _sharedUi.buoyColors.primary
1185
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1186
+ style: styles.proRegisterDeviceBtnText,
1187
+ children: "Register This Device"
1188
+ })]
1189
+ })]
1190
+ })]
1191
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1192
+ style: styles.proSignOutBtn,
1193
+ onPress: () => {
1194
+ const {
1195
+ Alert
1196
+ } = require("react-native");
1197
+ Alert.alert("Sign Out of Pro?", "This will:\n\n• Remove your license key from this device\n• Deactivate this device from your license\n• Free up a seat for another device\n\nYou can sign back in anytime with your license key.", [{
1198
+ text: "Cancel",
1199
+ style: "cancel"
1200
+ }, {
1201
+ text: "Sign Out",
1202
+ style: "destructive",
1203
+ onPress: () => license?.clearLicense()
1204
+ }]);
1205
+ },
1206
+ activeOpacity: 0.7,
1207
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1208
+ style: styles.proSignOutBtnText,
1209
+ children: "Sign Out of Pro"
1210
+ })
1211
+ })]
1212
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
1213
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1214
+ style: styles.proSection,
1215
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.SectionHeader, {
1216
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Icon, {
1217
+ icon: _sharedUi.Zap,
1218
+ color: _sharedUi.buoyColors.textMuted,
1219
+ size: 12
1220
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Title, {
1221
+ children: "LICENSE STATUS"
1222
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Badge, {
1223
+ count: "Free",
1224
+ color: _sharedUi.buoyColors.textMuted
1225
+ })]
1226
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1227
+ style: styles.proSectionContent,
1228
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1229
+ style: styles.proFreeDescription,
1230
+ children: "Upgrade to Pro to unlock all features and support development."
1231
+ })
1232
+ })]
1233
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1234
+ style: styles.proSection,
1235
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.SectionHeader, {
1236
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Icon, {
1237
+ icon: _sharedUi.CheckCircle2,
1238
+ color: _sharedUi.buoyColors.primary,
1239
+ size: 12
1240
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Title, {
1241
+ children: "PRO FEATURES"
1242
+ })]
1243
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1244
+ style: styles.proSectionContent,
1245
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1246
+ style: styles.proFeaturesList,
1247
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1248
+ style: styles.proFeatureItem,
1249
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CheckCircle2, {
1250
+ size: 14,
1251
+ color: _sharedUi.buoyColors.primary
1252
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1253
+ style: styles.proFeatureText,
1254
+ children: "Advanced Settings"
1255
+ })]
1256
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1257
+ style: styles.proFeatureItem,
1258
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CheckCircle2, {
1259
+ size: 14,
1260
+ color: _sharedUi.buoyColors.primary
1261
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1262
+ style: styles.proFeatureText,
1263
+ children: "Export Configuration"
1264
+ })]
1265
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1266
+ style: styles.proFeatureItem,
1267
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CheckCircle2, {
1268
+ size: 14,
1269
+ color: _sharedUi.buoyColors.primary
1270
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1271
+ style: styles.proFeatureText,
1272
+ children: "Team Defaults"
1273
+ })]
1274
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1275
+ style: styles.proFeatureItem,
1276
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CheckCircle2, {
1277
+ size: 14,
1278
+ color: _sharedUi.buoyColors.primary
1279
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1280
+ style: styles.proFeatureText,
1281
+ children: "Priority Support"
1282
+ })]
1283
+ })]
1284
+ })
1285
+ })]
1286
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
1287
+ style: styles.proUpgradeBtn,
1288
+ onPress: () => setShowLicenseModal(true),
1289
+ activeOpacity: 0.8,
1290
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Zap, {
1291
+ size: 16,
1292
+ color: _sharedUi.buoyColors.base
1293
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1294
+ style: styles.proUpgradeBtnText,
1295
+ children: "Upgrade to Pro"
1296
+ })]
1297
+ })]
1298
+ })
1299
+ })]
1300
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.LicenseEntryModal, {
1301
+ visible: showLicenseModal,
1302
+ onClose: () => {
1303
+ setShowLicenseModal(false);
1304
+ setLicenseModalForDeviceRegistration(false);
1305
+ },
1306
+ onSuccess: () => {
1307
+ setShowLicenseModal(false);
1308
+ setLicenseModalForDeviceRegistration(false);
1309
+ },
1310
+ license: license,
1311
+ startAtDeviceRegistration: licenseModalForDeviceRegistration,
1312
+ initialExistingDevices: licenseModalForDeviceRegistration ? devices : [],
1313
+ initialMaxDevices: licenseModalForDeviceRegistration ? seats?.total ?? undefined : undefined,
1314
+ initialCurrentDeviceCount: licenseModalForDeviceRegistration ? seats?.used ?? undefined : undefined
1315
+ })]
1316
+ });
1317
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.JsModal, {
1318
+ visible: visible,
1319
+ onClose: onClose,
1320
+ header: {
1321
+ showToggleButton: false,
1322
+ customContent: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader, {
1323
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Content, {
1324
+ title: "",
1325
+ noMargin: true,
1326
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TabSelector, {
1327
+ tabs: [{
1328
+ key: "dial",
1329
+ label: "DIAL"
1330
+ }, {
1331
+ key: "floating",
1332
+ label: "FLOATING"
1333
+ }, {
1334
+ key: "settings",
1335
+ label: "SETTINGS"
1336
+ }, {
1337
+ key: "pro",
1338
+ label: "PRO"
1339
+ }],
1340
+ activeTab: activeTab,
1341
+ onTabChange: tab => setActiveTab(tab)
1342
+ })
1343
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Actions, {})]
1344
+ })
1345
+ },
1346
+ initialMode: "bottomSheet",
1347
+ onModeChange: handleModeChange,
1348
+ persistenceKey: _sharedUi.devToolsStorageKeys.settings.root(),
1349
+ enablePersistence: true,
1350
+ maxHeight: screenHeight - insets.top,
1351
+ initialHeight: modalHeight,
1352
+ initialFloatingPosition: {
1353
+ x: (screenWidth - modalWidth) / 2,
1354
+ y: insets.top + 20
1355
+ },
1356
+ enableGlitchEffects: true,
1357
+ children: renderContent()
1358
+ });
1359
+ };
1360
+
1361
+ // Basic default settings for the hook (when apps are not available and no defaults configured)
1362
+ exports.DevToolsSettingsModal = DevToolsSettingsModal;
1363
+ const basicDefaultSettings = {
1364
+ dialTools: {},
1365
+ floatingTools: {
1366
+ environment: false
1367
+ },
1368
+ globalSettings: {
1369
+ enableSharedModalDimensions: false
1370
+ }
1371
+ };
1372
+
1373
+ /**
1374
+ * Creates default settings from the team configuration arrays.
1375
+ * Used by the hook when no stored settings exist.
1376
+ */
1377
+ const createDefaultsFromConfig = (defaultFloatingTools, defaultDialTools) => {
1378
+ const floatingSet = new Set(defaultFloatingTools ?? []);
1379
+ const dialSet = new Set(defaultDialTools ?? []);
1380
+
1381
+ // Build dial tools record from defaults
1382
+ const dialTools = {};
1383
+ for (const id of dialSet) {
1384
+ dialTools[id] = true;
1385
+ }
1386
+
1387
+ // Build floating tools record from defaults
1388
+ const floatingTools = {
1389
+ environment: floatingSet.has('environment')
1390
+ };
1391
+ for (const id of floatingSet) {
1392
+ if (id !== 'environment') {
1393
+ floatingTools[id] = true;
1394
+ }
1395
+ }
1396
+ return {
1397
+ dialTools: enforceDialLimit(dialTools),
1398
+ floatingTools,
1399
+ globalSettings: {
1400
+ enableSharedModalDimensions: false
1401
+ }
1402
+ };
1403
+ };
1404
+
1405
+ /**
1406
+ * Convenience hook for accessing persisted dev tools settings. Subscribes to the internal
1407
+ * event bus so all surfaces stay in sync when the modal saves new preferences.
1408
+ *
1409
+ * When no saved settings exist, the hook uses the default configuration from
1410
+ * the DefaultConfigProvider (if available) to determine initial tool states.
1411
+ */
1412
+ const useDevToolsSettings = () => {
1413
+ // Get team default configuration from context
1414
+ const {
1415
+ defaultFloatingTools,
1416
+ defaultDialTools
1417
+ } = (0, _DefaultConfigContext.useDefaultConfig)();
1418
+
1419
+ // Compute the effective defaults based on team configuration
1420
+ const effectiveDefaults = (0, _react.useMemo)(() => {
1421
+ if (!defaultFloatingTools && !defaultDialTools) {
1422
+ return basicDefaultSettings;
1423
+ }
1424
+ return createDefaultsFromConfig(defaultFloatingTools, defaultDialTools);
1425
+ }, [defaultFloatingTools, defaultDialTools]);
1426
+ const [settings, setSettings] = (0, _react.useState)(effectiveDefaults);
1427
+ const loadSettings = (0, _react.useCallback)(async () => {
1428
+ try {
1429
+ const savedSettings = await (0, _sharedUi.safeGetItem)(STORAGE_KEY);
1430
+ if (savedSettings) {
1431
+ const parsed = JSON.parse(savedSettings);
1432
+ const merged = mergeWithDefaults(effectiveDefaults, parsed);
1433
+ setSettings(merged);
1434
+ return;
1435
+ }
1436
+ // No saved settings - use the effective defaults (including team config)
1437
+ setSettings(effectiveDefaults);
1438
+ } catch (error) {
1439
+ console.error("Failed to load dev tools settings:", error);
1440
+ setSettings(effectiveDefaults);
1441
+ }
1442
+ }, [effectiveDefaults]);
1443
+ (0, _react.useEffect)(() => {
1444
+ loadSettings();
1445
+ // Subscribe to settings changes
1446
+ const unsub = _settingsBus.settingsBus.addListener(payload => {
1447
+ try {
1448
+ if (payload) {
1449
+ setSettings(payload);
1450
+ }
1451
+ } catch (err) {
1452
+ // Listener errors are intentionally swallowed to avoid breaking subscribers
1453
+ }
1454
+ });
1455
+ return () => {
1456
+ unsub();
1457
+ };
1458
+ }, [loadSettings]);
1459
+
1460
+ // Refresh settings when component using this hook becomes visible
1461
+ const refreshSettings = (0, _react.useCallback)(() => {
1462
+ loadSettings();
1463
+ }, [loadSettings]);
1464
+ return {
1465
+ settings,
1466
+ refreshSettings
1467
+ };
1468
+ };
1469
+ exports.useDevToolsSettings = useDevToolsSettings;
1470
+ const styles = _reactNative.StyleSheet.create({
1471
+ container: {
1472
+ flex: 1,
1473
+ backgroundColor: _sharedUi.buoyColors.base
1474
+ },
1475
+ // Header styles matching React Query modal exactly
1476
+ headerContainer: {
1477
+ flex: 1,
1478
+ flexDirection: "row",
1479
+ alignItems: "center",
1480
+ justifyContent: "space-between",
1481
+ paddingHorizontal: 12
1482
+ },
1483
+ tabNavigationContainer: {
1484
+ flex: 1,
1485
+ flexDirection: "row",
1486
+ backgroundColor: _sharedUi.buoyColors.hover,
1487
+ borderRadius: 6,
1488
+ padding: 2,
1489
+ borderWidth: 1,
1490
+ borderColor: _sharedUi.buoyColors.border,
1491
+ justifyContent: "space-evenly"
1492
+ },
1493
+ tabButton: {
1494
+ paddingHorizontal: 8,
1495
+ paddingVertical: 5,
1496
+ borderRadius: 4,
1497
+ alignItems: "center",
1498
+ justifyContent: "center",
1499
+ flex: 1,
1500
+ marginHorizontal: 1
1501
+ },
1502
+ tabButtonActive: {
1503
+ backgroundColor: _sharedUi.buoyColors.primary + "20",
1504
+ borderWidth: 1,
1505
+ borderColor: _sharedUi.buoyColors.primary
1506
+ },
1507
+ tabButtonInactive: {
1508
+ backgroundColor: "transparent"
1509
+ },
1510
+ tabButtonText: {
1511
+ fontSize: 12,
1512
+ fontWeight: "600",
1513
+ letterSpacing: 0.5,
1514
+ fontFamily: "monospace",
1515
+ textTransform: "uppercase"
1516
+ },
1517
+ tabButtonTextActive: {
1518
+ color: _sharedUi.buoyColors.primary
1519
+ },
1520
+ tabButtonTextInactive: {
1521
+ color: _sharedUi.buoyColors.textMuted
1522
+ },
1523
+ // Scroll content
1524
+ scrollContent: {
1525
+ flex: 1
1526
+ },
1527
+ scrollContainer: {
1528
+ paddingTop: 16,
1529
+ paddingBottom: 24
1530
+ },
1531
+ // Sections
1532
+ section: {
1533
+ marginHorizontal: 16,
1534
+ marginBottom: 24
1535
+ },
1536
+ sectionHeader: {
1537
+ flexDirection: "row",
1538
+ alignItems: "center",
1539
+ marginBottom: 12,
1540
+ paddingHorizontal: 4
1541
+ },
1542
+ sectionIndicator: {
1543
+ width: 3,
1544
+ height: 16,
1545
+ borderRadius: 2,
1546
+ backgroundColor: _sharedUi.buoyColors.primary,
1547
+ marginRight: 8,
1548
+ shadowColor: _sharedUi.buoyColors.primary,
1549
+ shadowOffset: {
1550
+ width: 0,
1551
+ height: 0
1552
+ },
1553
+ shadowOpacity: 0.6,
1554
+ shadowRadius: 4
1555
+ },
1556
+ sectionTitle: {
1557
+ flex: 1,
1558
+ color: _sharedUi.buoyColors.primary,
1559
+ fontSize: 13,
1560
+ fontWeight: "700",
1561
+ letterSpacing: 1.2
1562
+ },
1563
+ sectionCount: {
1564
+ color: _sharedUi.buoyColors.textSecondary,
1565
+ fontSize: 11,
1566
+ opacity: 0.7
1567
+ },
1568
+ // Tool Cards - Clean unified Buoy theme
1569
+ glassCard: {
1570
+ borderRadius: 12,
1571
+ paddingVertical: 12,
1572
+ paddingHorizontal: 14,
1573
+ backgroundColor: _sharedUi.buoyColors.card,
1574
+ borderWidth: 1,
1575
+ borderColor: _sharedUi.buoyColors.border
1576
+ },
1577
+ glassCardActive: {
1578
+ borderColor: _sharedUi.buoyColors.primary + "50",
1579
+ backgroundColor: _sharedUi.buoyColors.primary + "08"
1580
+ },
1581
+ glassCardInner: {
1582
+ flexDirection: "row",
1583
+ alignItems: "center",
1584
+ gap: 12
1585
+ },
1586
+ iconContainer: {
1587
+ width: 32,
1588
+ height: 32,
1589
+ borderRadius: 8,
1590
+ backgroundColor: _sharedUi.buoyColors.hover,
1591
+ alignItems: "center",
1592
+ justifyContent: "center"
1593
+ },
1594
+ iconContainerActive: {
1595
+ backgroundColor: _sharedUi.buoyColors.primary + "20"
1596
+ },
1597
+ toolInfo: {
1598
+ flex: 1
1599
+ },
1600
+ toolName: {
1601
+ color: _sharedUi.buoyColors.text,
1602
+ fontWeight: "600",
1603
+ fontSize: 13
1604
+ },
1605
+ toolNameActive: {
1606
+ color: _sharedUi.buoyColors.text
1607
+ },
1608
+ toolDescription: {
1609
+ color: _sharedUi.buoyColors.textMuted,
1610
+ fontSize: 11,
1611
+ marginTop: 2
1612
+ },
1613
+ pillToggle: {
1614
+ paddingHorizontal: 14,
1615
+ paddingVertical: 6,
1616
+ borderRadius: 6,
1617
+ borderWidth: 1,
1618
+ minWidth: 48,
1619
+ alignItems: "center"
1620
+ },
1621
+ pillToggleOn: {
1622
+ backgroundColor: _sharedUi.buoyColors.primary + "20",
1623
+ borderColor: _sharedUi.buoyColors.primary
1624
+ },
1625
+ pillToggleOff: {
1626
+ backgroundColor: _sharedUi.buoyColors.hover,
1627
+ borderColor: _sharedUi.buoyColors.border
1628
+ },
1629
+ pillToggleText: {
1630
+ fontWeight: "600",
1631
+ fontSize: 11,
1632
+ letterSpacing: 0.5
1633
+ },
1634
+ pillToggleTextOn: {
1635
+ color: _sharedUi.buoyColors.primary
1636
+ },
1637
+ pillToggleTextOff: {
1638
+ color: _sharedUi.buoyColors.textMuted
1639
+ },
1640
+ closeButton: {
1641
+ marginLeft: 8,
1642
+ paddingHorizontal: 10,
1643
+ paddingVertical: 6,
1644
+ borderRadius: 6,
1645
+ backgroundColor: _sharedUi.buoyColors.error + "1A",
1646
+ borderWidth: 1,
1647
+ borderColor: _sharedUi.buoyColors.error + "33"
1648
+ },
1649
+ closeButtonText: {
1650
+ color: _sharedUi.buoyColors.error,
1651
+ fontSize: 14,
1652
+ fontWeight: "700",
1653
+ letterSpacing: 0.5
1654
+ },
1655
+ // Expandable Settings Card styles
1656
+ expandableCard: {
1657
+ backgroundColor: _sharedUi.buoyColors.card,
1658
+ borderRadius: 8,
1659
+ borderWidth: 1,
1660
+ borderColor: _sharedUi.buoyColors.border,
1661
+ padding: 12
1662
+ },
1663
+ expandableCardHeader: {
1664
+ flexDirection: "row",
1665
+ alignItems: "center",
1666
+ gap: 12
1667
+ },
1668
+ expandableCardCategory: {
1669
+ backgroundColor: _sharedUi.buoyColors.primary + "20",
1670
+ borderWidth: 1,
1671
+ borderColor: _sharedUi.buoyColors.primary + "40",
1672
+ borderRadius: 4,
1673
+ paddingHorizontal: 8,
1674
+ paddingVertical: 4
1675
+ },
1676
+ expandableCardCategoryText: {
1677
+ fontSize: 10,
1678
+ fontWeight: "700",
1679
+ color: _sharedUi.buoyColors.primary,
1680
+ letterSpacing: 0.5
1681
+ },
1682
+ expandableCardTitle: {
1683
+ flex: 1,
1684
+ paddingHorizontal: 4
1685
+ },
1686
+ expandableCardTitleText: {
1687
+ fontFamily: "monospace",
1688
+ fontSize: 12,
1689
+ fontWeight: "700",
1690
+ color: _sharedUi.buoyColors.text
1691
+ },
1692
+ expandableCardSubtitle: {
1693
+ fontSize: 10,
1694
+ color: _sharedUi.buoyColors.textMuted,
1695
+ marginTop: 2
1696
+ },
1697
+ expandableCardActions: {
1698
+ flexDirection: "row",
1699
+ alignItems: "center",
1700
+ gap: 8
1701
+ },
1702
+ expandableCardBody: {
1703
+ marginTop: 12,
1704
+ paddingTop: 12,
1705
+ borderTopWidth: 1,
1706
+ borderTopColor: _sharedUi.buoyColors.border + "50"
1707
+ },
1708
+ expandableCardSection: {
1709
+ marginBottom: 12
1710
+ },
1711
+ expandableCardSectionTitle: {
1712
+ fontSize: 10,
1713
+ fontWeight: "700",
1714
+ color: _sharedUi.buoyColors.primary,
1715
+ letterSpacing: 1,
1716
+ marginBottom: 6
1717
+ },
1718
+ expandableCardSectionText: {
1719
+ fontSize: 12,
1720
+ color: _sharedUi.buoyColors.textSecondary,
1721
+ lineHeight: 18
1722
+ },
1723
+ // Storage Status Card styles
1724
+ storageStatusCard: {
1725
+ backgroundColor: _sharedUi.buoyColors.card,
1726
+ borderRadius: 8,
1727
+ borderWidth: 1,
1728
+ borderColor: _sharedUi.buoyColors.border,
1729
+ padding: 12,
1730
+ marginBottom: 10
1731
+ },
1732
+ storageStatusHeader: {
1733
+ flexDirection: "row",
1734
+ alignItems: "center",
1735
+ gap: 12,
1736
+ marginBottom: 8
1737
+ },
1738
+ storageStatusIcon: {
1739
+ width: 36,
1740
+ height: 36,
1741
+ borderRadius: 8,
1742
+ backgroundColor: _sharedUi.buoyColors.hover,
1743
+ alignItems: "center",
1744
+ justifyContent: "center"
1745
+ },
1746
+ storageStatusInfo: {
1747
+ flex: 1
1748
+ },
1749
+ storageStatusLabel: {
1750
+ fontSize: 10,
1751
+ fontWeight: "700",
1752
+ color: _sharedUi.buoyColors.textMuted,
1753
+ letterSpacing: 1,
1754
+ marginBottom: 4
1755
+ },
1756
+ storageStatusBadgeContainer: {
1757
+ flexDirection: "row",
1758
+ alignItems: "center",
1759
+ gap: 6
1760
+ },
1761
+ storageStatusBadge: {
1762
+ paddingHorizontal: 8,
1763
+ paddingVertical: 4,
1764
+ borderRadius: 4,
1765
+ borderWidth: 1
1766
+ },
1767
+ storageStatusBadgeText: {
1768
+ fontSize: 11,
1769
+ fontWeight: "700",
1770
+ letterSpacing: 0.5
1771
+ },
1772
+ storageStatusDescription: {
1773
+ fontSize: 11,
1774
+ color: _sharedUi.buoyColors.textMuted,
1775
+ lineHeight: 16,
1776
+ marginBottom: 12
1777
+ },
1778
+ clearStorageButton: {
1779
+ flexDirection: "row",
1780
+ alignItems: "center",
1781
+ justifyContent: "center",
1782
+ gap: 8,
1783
+ paddingVertical: 10,
1784
+ paddingHorizontal: 16,
1785
+ borderRadius: 6,
1786
+ backgroundColor: _sharedUi.buoyColors.error + "15",
1787
+ borderWidth: 1,
1788
+ borderColor: _sharedUi.buoyColors.error + "40"
1789
+ },
1790
+ clearStorageButtonDisabled: {
1791
+ opacity: 0.5
1792
+ },
1793
+ clearStorageButtonSuccess: {
1794
+ backgroundColor: _sharedUi.buoyColors.success + "15",
1795
+ borderColor: _sharedUi.buoyColors.success + "40"
1796
+ },
1797
+ clearStorageButtonText: {
1798
+ fontSize: 12,
1799
+ fontWeight: "700",
1800
+ color: _sharedUi.buoyColors.error,
1801
+ letterSpacing: 0.5
1802
+ },
1803
+ // Advice hints
1804
+ adviceHintsContainer: {
1805
+ gap: 8,
1806
+ marginBottom: 12
1807
+ },
1808
+ adviceHint: {
1809
+ flexDirection: "row",
1810
+ alignItems: "flex-start",
1811
+ gap: 8,
1812
+ backgroundColor: _sharedUi.buoyColors.warning + "10",
1813
+ borderRadius: 6,
1814
+ padding: 10,
1815
+ marginBottom: 8
1816
+ },
1817
+ adviceHintText: {
1818
+ flex: 1,
1819
+ fontSize: 11,
1820
+ color: _sharedUi.buoyColors.textSecondary,
1821
+ lineHeight: 16
1822
+ },
1823
+ adviceHintBold: {
1824
+ fontWeight: "700",
1825
+ color: _sharedUi.buoyColors.text
1826
+ },
1827
+ adviceHintCode: {
1828
+ fontFamily: "monospace",
1829
+ fontSize: 10,
1830
+ color: _sharedUi.buoyColors.info,
1831
+ backgroundColor: _sharedUi.buoyColors.info + "15",
1832
+ paddingHorizontal: 4,
1833
+ borderRadius: 3
1834
+ },
1835
+ // Expanded storage content
1836
+ storageExpandedContent: {
1837
+ marginTop: 12,
1838
+ paddingTop: 12,
1839
+ borderTopWidth: 1,
1840
+ borderTopColor: _sharedUi.buoyColors.border + "50"
1841
+ },
1842
+ storageExpandedHeader: {
1843
+ flexDirection: "row",
1844
+ alignItems: "center",
1845
+ gap: 8,
1846
+ marginBottom: 10
1847
+ },
1848
+ storageExpandedTitle: {
1849
+ fontSize: 10,
1850
+ fontWeight: "700",
1851
+ color: _sharedUi.buoyColors.primary,
1852
+ letterSpacing: 1,
1853
+ flex: 1
1854
+ },
1855
+ storageExpandedCount: {
1856
+ fontSize: 10,
1857
+ color: _sharedUi.buoyColors.textMuted
1858
+ },
1859
+ storageKeysList: {
1860
+ gap: 4,
1861
+ maxHeight: 150
1862
+ },
1863
+ storageKeyItem: {
1864
+ backgroundColor: _sharedUi.buoyColors.hover,
1865
+ borderRadius: 4,
1866
+ paddingHorizontal: 8,
1867
+ paddingVertical: 6,
1868
+ borderWidth: 1,
1869
+ borderColor: _sharedUi.buoyColors.border + "50"
1870
+ },
1871
+ storageKeyItemEmpty: {
1872
+ fontSize: 11,
1873
+ color: _sharedUi.buoyColors.textMuted,
1874
+ fontStyle: "italic",
1875
+ paddingVertical: 8
1876
+ },
1877
+ storageKeyText: {
1878
+ fontSize: 11,
1879
+ color: _sharedUi.buoyColors.textSecondary,
1880
+ fontFamily: "monospace"
1881
+ },
1882
+ // Export Config Card styles
1883
+ exportConfigCard: {
1884
+ backgroundColor: _sharedUi.buoyColors.card,
1885
+ borderRadius: 8,
1886
+ borderWidth: 1,
1887
+ borderColor: _sharedUi.buoyColors.success + "40",
1888
+ padding: 12,
1889
+ marginTop: 10
1890
+ },
1891
+ exportConfigHeader: {
1892
+ flexDirection: "row",
1893
+ alignItems: "center",
1894
+ gap: 12
1895
+ },
1896
+ exportConfigIconContainer: {
1897
+ width: 36,
1898
+ height: 36,
1899
+ borderRadius: 8,
1900
+ backgroundColor: _sharedUi.buoyColors.success + "15",
1901
+ alignItems: "center",
1902
+ justifyContent: "center"
1903
+ },
1904
+ exportConfigInfo: {
1905
+ flex: 1
1906
+ },
1907
+ exportConfigLabel: {
1908
+ fontSize: 12,
1909
+ fontWeight: "700",
1910
+ color: _sharedUi.buoyColors.success,
1911
+ letterSpacing: 0.5
1912
+ },
1913
+ exportConfigHint: {
1914
+ fontSize: 10,
1915
+ color: _sharedUi.buoyColors.textMuted,
1916
+ marginTop: 2
1917
+ },
1918
+ exportConfigActions: {
1919
+ flexDirection: "row",
1920
+ alignItems: "center"
1921
+ },
1922
+ exportHintBanner: {
1923
+ flexDirection: "row",
1924
+ alignItems: "flex-start",
1925
+ gap: 8,
1926
+ backgroundColor: _sharedUi.buoyColors.warning + "10",
1927
+ borderRadius: 6,
1928
+ padding: 10,
1929
+ marginTop: 12
1930
+ },
1931
+ exportHintText: {
1932
+ flex: 1,
1933
+ fontSize: 11,
1934
+ color: _sharedUi.buoyColors.textSecondary,
1935
+ lineHeight: 16
1936
+ },
1937
+ exportHintBold: {
1938
+ fontWeight: "700",
1939
+ color: _sharedUi.buoyColors.warning
1940
+ },
1941
+ exportCodeContainer: {
1942
+ marginTop: 12,
1943
+ paddingTop: 12,
1944
+ borderTopWidth: 1,
1945
+ borderTopColor: _sharedUi.buoyColors.border + "50"
1946
+ },
1947
+ exportCodeHeader: {
1948
+ flexDirection: "row",
1949
+ alignItems: "center",
1950
+ gap: 8,
1951
+ marginBottom: 8
1952
+ },
1953
+ exportCodeTitle: {
1954
+ fontSize: 10,
1955
+ fontWeight: "700",
1956
+ color: _sharedUi.buoyColors.success,
1957
+ letterSpacing: 1
1958
+ },
1959
+ exportCodeBlock: {
1960
+ backgroundColor: _sharedUi.buoyColors.hover,
1961
+ borderRadius: 6,
1962
+ padding: 12,
1963
+ borderWidth: 1,
1964
+ borderColor: _sharedUi.buoyColors.border + "50"
1965
+ },
1966
+ exportCodeText: {
1967
+ fontSize: 11,
1968
+ color: _sharedUi.buoyColors.text,
1969
+ fontFamily: "monospace",
1970
+ lineHeight: 18
1971
+ },
1972
+ exportCodeDescription: {
1973
+ fontSize: 10,
1974
+ color: _sharedUi.buoyColors.textMuted,
1975
+ marginTop: 8,
1976
+ lineHeight: 14
1977
+ },
1978
+ exportCodeInline: {
1979
+ fontFamily: "monospace",
1980
+ fontSize: 10,
1981
+ color: _sharedUi.buoyColors.info,
1982
+ backgroundColor: _sharedUi.buoyColors.info + "15",
1983
+ paddingHorizontal: 4,
1984
+ borderRadius: 3
1985
+ },
1986
+ exportCopyButton: {
1987
+ flexDirection: "row",
1988
+ alignItems: "center",
1989
+ justifyContent: "center",
1990
+ gap: 8,
1991
+ paddingVertical: 10,
1992
+ paddingHorizontal: 16,
1993
+ borderRadius: 6,
1994
+ backgroundColor: _sharedUi.buoyColors.success + "15",
1995
+ borderWidth: 1,
1996
+ borderColor: _sharedUi.buoyColors.success + "40",
1997
+ marginTop: 12
1998
+ },
1999
+ exportCopyButtonSuccess: {
2000
+ backgroundColor: _sharedUi.buoyColors.success + "25",
2001
+ borderColor: _sharedUi.buoyColors.success + "60"
2002
+ },
2003
+ exportCopyButtonText: {
2004
+ fontSize: 12,
2005
+ fontWeight: "700",
2006
+ color: _sharedUi.buoyColors.success,
2007
+ letterSpacing: 0.5
2008
+ },
2009
+ // JSON-like viewer styles (syntax highlighting)
2010
+ exportJsonBlock: {
2011
+ backgroundColor: _sharedUi.buoyColors.hover,
2012
+ borderRadius: 6,
2013
+ padding: 12,
2014
+ borderWidth: 1,
2015
+ borderColor: _sharedUi.buoyColors.border + "50",
2016
+ flexDirection: "row",
2017
+ flexWrap: "wrap",
2018
+ alignItems: "center",
2019
+ gap: 4
2020
+ },
2021
+ exportJsonProp: {
2022
+ fontSize: 12,
2023
+ fontFamily: "monospace",
2024
+ color: _sharedUi.buoyColors.info,
2025
+ // Cyan for prop names
2026
+ fontWeight: "600"
2027
+ },
2028
+ exportJsonEquals: {
2029
+ fontSize: 12,
2030
+ fontFamily: "monospace",
2031
+ color: _sharedUi.buoyColors.textMuted
2032
+ },
2033
+ exportJsonBracket: {
2034
+ fontSize: 12,
2035
+ fontFamily: "monospace",
2036
+ color: _sharedUi.buoyColors.warning // Yellow for JSX brackets
2037
+ },
2038
+ exportJsonArrayBracket: {
2039
+ fontSize: 12,
2040
+ fontFamily: "monospace",
2041
+ color: _sharedUi.buoyColors.text
2042
+ },
2043
+ exportJsonArrayContent: {
2044
+ flexDirection: "row",
2045
+ flexWrap: "wrap",
2046
+ alignItems: "center",
2047
+ gap: 2
2048
+ },
2049
+ exportJsonArrayItem: {
2050
+ flexDirection: "row",
2051
+ alignItems: "center"
2052
+ },
2053
+ exportJsonString: {
2054
+ fontSize: 12,
2055
+ fontFamily: "monospace",
2056
+ color: _sharedUi.buoyColors.success // Green for strings
2057
+ },
2058
+ exportJsonComma: {
2059
+ fontSize: 12,
2060
+ fontFamily: "monospace",
2061
+ color: _sharedUi.buoyColors.textMuted,
2062
+ marginRight: 4
2063
+ },
2064
+ exportJsonComment: {
2065
+ fontSize: 12,
2066
+ fontFamily: "monospace",
2067
+ color: _sharedUi.buoyColors.textMuted,
2068
+ fontStyle: "italic"
2069
+ },
2070
+ // Pro tab styles - using buoyColors for consistency with other modals
2071
+ proContainer: {
2072
+ paddingHorizontal: 16,
2073
+ paddingTop: 16,
2074
+ paddingBottom: 24
2075
+ },
2076
+ proSection: {
2077
+ backgroundColor: _sharedUi.buoyColors.card,
2078
+ borderRadius: 8,
2079
+ borderWidth: 1,
2080
+ borderColor: _sharedUi.buoyColors.border,
2081
+ marginBottom: 12,
2082
+ overflow: "hidden"
2083
+ },
2084
+ proSectionContent: {
2085
+ paddingHorizontal: 16,
2086
+ paddingTop: 8,
2087
+ paddingBottom: 16
2088
+ },
2089
+ proStatsRow: {
2090
+ flexDirection: "row",
2091
+ justifyContent: "center",
2092
+ alignItems: "center",
2093
+ paddingVertical: 8
2094
+ },
2095
+ proStatItem: {
2096
+ alignItems: "center",
2097
+ paddingHorizontal: 20
2098
+ },
2099
+ proStatValue: {
2100
+ fontSize: 20,
2101
+ fontWeight: "700",
2102
+ color: _sharedUi.buoyColors.text
2103
+ },
2104
+ proStatValueDanger: {
2105
+ color: _sharedUi.buoyColors.error
2106
+ },
2107
+ proStatLabel: {
2108
+ fontSize: 10,
2109
+ color: _sharedUi.buoyColors.textMuted,
2110
+ letterSpacing: 0.5,
2111
+ marginTop: 2
2112
+ },
2113
+ proStatDivider: {
2114
+ width: 1,
2115
+ height: 24,
2116
+ backgroundColor: _sharedUi.buoyColors.border
2117
+ },
2118
+ proEmptyState: {
2119
+ alignItems: "center",
2120
+ paddingVertical: 20,
2121
+ gap: 8
2122
+ },
2123
+ proEmptyStateText: {
2124
+ fontSize: 11,
2125
+ color: _sharedUi.buoyColors.textMuted,
2126
+ fontStyle: "italic"
2127
+ },
2128
+ proErrorState: {
2129
+ flexDirection: "row",
2130
+ alignItems: "center",
2131
+ gap: 8,
2132
+ paddingVertical: 12,
2133
+ paddingHorizontal: 12,
2134
+ backgroundColor: _sharedUi.buoyColors.error + "15",
2135
+ borderRadius: 6
2136
+ },
2137
+ proErrorStateText: {
2138
+ fontSize: 11,
2139
+ color: _sharedUi.buoyColors.error,
2140
+ flex: 1
2141
+ },
2142
+ proDevicesList: {
2143
+ gap: 6
2144
+ },
2145
+ proDeviceRow: {
2146
+ flexDirection: "row",
2147
+ alignItems: "center",
2148
+ paddingVertical: 8,
2149
+ paddingHorizontal: 10,
2150
+ backgroundColor: _sharedUi.buoyColors.input,
2151
+ borderRadius: 6,
2152
+ borderWidth: 1,
2153
+ borderColor: _sharedUi.buoyColors.border
2154
+ },
2155
+ proDeviceRowCurrent: {
2156
+ borderColor: _sharedUi.buoyColors.success + "50",
2157
+ backgroundColor: _sharedUi.buoyColors.success + "08"
2158
+ },
2159
+ proDeviceIcon: {
2160
+ width: 28,
2161
+ height: 28,
2162
+ borderRadius: 14,
2163
+ backgroundColor: _sharedUi.buoyColors.hover,
2164
+ alignItems: "center",
2165
+ justifyContent: "center"
2166
+ },
2167
+ proDeviceInfo: {
2168
+ flex: 1,
2169
+ marginLeft: 10
2170
+ },
2171
+ proDeviceNameRow: {
2172
+ flexDirection: "row",
2173
+ alignItems: "center",
2174
+ gap: 6
2175
+ },
2176
+ proDeviceName: {
2177
+ fontSize: 12,
2178
+ fontWeight: "600",
2179
+ color: _sharedUi.buoyColors.text,
2180
+ flexShrink: 1
2181
+ },
2182
+ proDeviceBadge: {
2183
+ backgroundColor: _sharedUi.buoyColors.success + "20",
2184
+ paddingHorizontal: 5,
2185
+ paddingVertical: 1,
2186
+ borderRadius: 3
2187
+ },
2188
+ proDeviceBadgeText: {
2189
+ fontSize: 8,
2190
+ fontWeight: "700",
2191
+ color: _sharedUi.buoyColors.success,
2192
+ letterSpacing: 0.3
2193
+ },
2194
+ proDeviceDetails: {
2195
+ fontSize: 10,
2196
+ color: _sharedUi.buoyColors.textMuted,
2197
+ marginTop: 2
2198
+ },
2199
+ proDeviceDate: {
2200
+ fontSize: 9,
2201
+ color: _sharedUi.buoyColors.textMuted,
2202
+ marginTop: 2
2203
+ },
2204
+ proDeviceRemoveBtn: {
2205
+ padding: 6,
2206
+ marginLeft: 4
2207
+ },
2208
+ proRegisterDeviceBtn: {
2209
+ flexDirection: "row",
2210
+ alignItems: "center",
2211
+ justifyContent: "center",
2212
+ gap: 8,
2213
+ paddingVertical: 10,
2214
+ paddingHorizontal: 16,
2215
+ borderRadius: 8,
2216
+ backgroundColor: _sharedUi.buoyColors.primary + "15",
2217
+ borderWidth: 1,
2218
+ borderColor: _sharedUi.buoyColors.primary + "40",
2219
+ marginTop: 10
2220
+ },
2221
+ proRegisterDeviceBtnText: {
2222
+ fontSize: 12,
2223
+ fontWeight: "600",
2224
+ color: _sharedUi.buoyColors.primary
2225
+ },
2226
+ proSignOutBtn: {
2227
+ alignItems: "center",
2228
+ justifyContent: "center",
2229
+ paddingVertical: 12,
2230
+ marginTop: 8
2231
+ },
2232
+ proSignOutBtnText: {
2233
+ fontSize: 12,
2234
+ color: _sharedUi.buoyColors.textMuted
2235
+ },
2236
+ proFreeDescription: {
2237
+ fontSize: 11,
2238
+ color: _sharedUi.buoyColors.textSecondary,
2239
+ lineHeight: 16
2240
+ },
2241
+ proFeaturesList: {
2242
+ gap: 8
2243
+ },
2244
+ proFeatureItem: {
2245
+ flexDirection: "row",
2246
+ alignItems: "center",
2247
+ gap: 10
2248
+ },
2249
+ proFeatureText: {
2250
+ fontSize: 12,
2251
+ color: _sharedUi.buoyColors.text
2252
+ },
2253
+ proUpgradeBtn: {
2254
+ flexDirection: "row",
2255
+ alignItems: "center",
2256
+ justifyContent: "center",
2257
+ gap: 8,
2258
+ backgroundColor: _sharedUi.buoyColors.primary,
2259
+ borderRadius: 10,
2260
+ paddingVertical: 14,
2261
+ shadowColor: _sharedUi.buoyColors.primary,
2262
+ shadowOffset: {
2263
+ width: 0,
2264
+ height: 0
2265
+ },
2266
+ shadowOpacity: 0.4,
2267
+ shadowRadius: 8
2268
+ },
2269
+ proUpgradeBtnText: {
2270
+ fontSize: 14,
2271
+ fontWeight: "700",
2272
+ color: _sharedUi.buoyColors.base
2273
+ }
2274
+ });