@buoy-gg/core 7.0.41 → 7.0.43
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.
- package/README.md +14 -95
- package/bin/buoy.js +19 -340
- package/lib/commonjs/Buoy.js +1 -107
- package/lib/commonjs/browserRegister.js +1 -48
- package/lib/commonjs/browserRegister.server.js +1 -10
- package/lib/commonjs/earlyHookScript.js +1 -0
- package/lib/commonjs/earlyNetwork.js +1 -0
- package/lib/commonjs/floatingMenu/AppHost.js +1 -460
- package/lib/commonjs/floatingMenu/AppHost.web.js +1 -376
- package/lib/commonjs/floatingMenu/AppHostLogic.js +1 -50
- package/lib/commonjs/floatingMenu/AppIcon.js +1 -91
- package/lib/commonjs/floatingMenu/AppIcon.web.js +1 -53
- package/lib/commonjs/floatingMenu/BrowserToolRuntime.js +1 -150
- package/lib/commonjs/floatingMenu/DefaultConfigContext.js +1 -45
- package/lib/commonjs/floatingMenu/DevToolsSettingsModal.js +1 -18
- package/lib/commonjs/floatingMenu/DevToolsSettingsModal.shared.js +1 -1114
- package/lib/commonjs/floatingMenu/DevToolsSettingsModal.web.js +1 -48
- package/lib/commonjs/floatingMenu/DevToolsVisibilityContext.js +1 -49
- package/lib/commonjs/floatingMenu/FloatingDevTools.js +1 -559
- package/lib/commonjs/floatingMenu/FloatingDevTools.web.js +1 -415
- package/lib/commonjs/floatingMenu/FloatingMenu.js +1 -371
- package/lib/commonjs/floatingMenu/MinimizedToolsContext.js +1 -274
- package/lib/commonjs/floatingMenu/MinimizedToolsStack.js +1 -288
- package/lib/commonjs/floatingMenu/MinimizedToolsStack.web.js +1 -377
- package/lib/commonjs/floatingMenu/ToggleStateManager.js +1 -36
- package/lib/commonjs/floatingMenu/ToolHost.js +1 -281
- package/lib/commonjs/floatingMenu/ToolOpenerBridge.js +1 -41
- package/lib/commonjs/floatingMenu/autoDiscoverPresets.js +1 -471
- package/lib/commonjs/floatingMenu/autoExternalSync.js +1 -443
- package/lib/commonjs/floatingMenu/defaultConfig.js +1 -167
- package/lib/commonjs/floatingMenu/deviceIdentity.js +1 -173
- package/lib/commonjs/floatingMenu/dial/DialDevTools.js +1 -904
- package/lib/commonjs/floatingMenu/dial/DialDevTools.web.js +1 -895
- package/lib/commonjs/floatingMenu/dial/DialHyperspace.js +1 -177
- package/lib/commonjs/floatingMenu/dial/DialIcon.js +1 -304
- package/lib/commonjs/floatingMenu/dial/DialPagination.js +1 -165
- package/lib/commonjs/floatingMenu/dial/DialSnow.js +1 -92
- package/lib/commonjs/floatingMenu/dial/OnboardingTooltip.js +1 -246
- package/lib/commonjs/floatingMenu/dial/OnboardingTooltip.web.js +2 -147
- package/lib/commonjs/floatingMenu/dial/dialUsageStore.js +1 -93
- package/lib/commonjs/floatingMenu/dial/onboardingConstants.js +1 -42
- package/lib/commonjs/floatingMenu/dial/onboardingConstants.web.js +1 -42
- package/lib/commonjs/floatingMenu/externalSyncGate.js +1 -49
- package/lib/commonjs/floatingMenu/floatingTools.js +1 -42
- package/lib/commonjs/floatingMenu/floatingTools.web.js +1 -30
- package/lib/commonjs/floatingMenu/optionalRequire.js +1 -37
- package/lib/commonjs/floatingMenu/settingsBus.js +1 -12
- package/lib/commonjs/floatingMenu/sync/appSyncAdapter.js +1 -115
- package/lib/commonjs/floatingMenu/types.js +1 -5
- package/lib/commonjs/floatingMenu/useDesktopSyncStatus.js +1 -28
- package/lib/commonjs/floatingMenu/useDesktopSyncStatus.web.js +1 -14
- package/lib/commonjs/index.js +1 -291
- package/lib/commonjs/index.web.js +1 -241
- package/lib/commonjs/reactDevtoolsTypes.js +1 -1
- package/lib/commonjs/releaseOptIn.js +1 -0
- package/lib/commonjs/telemetry/installPing.js +1 -131
- package/lib/commonjs/toolkit.js +1 -584
- package/lib/commonjs/toolkitComponents.js +1 -241
- package/lib/commonjs/utils/autoDiscoverPresets.web.js +1 -46
- package/lib/commonjs/utils/createWebTool.js +1 -20
- package/lib/commonjs/utils/createWebTools.js +1 -30
- package/lib/commonjs/version.js +1 -18
- package/lib/module/Buoy.js +1 -102
- package/lib/module/browserRegister.js +1 -44
- package/lib/module/browserRegister.server.js +1 -6
- package/lib/module/earlyHookScript.js +1 -0
- package/lib/module/earlyNetwork.js +1 -0
- package/lib/module/floatingMenu/AppHost.js +1 -452
- package/lib/module/floatingMenu/AppHost.web.js +1 -368
- package/lib/module/floatingMenu/AppHostLogic.js +1 -45
- package/lib/module/floatingMenu/AppIcon.js +1 -84
- package/lib/module/floatingMenu/AppIcon.web.js +1 -47
- package/lib/module/floatingMenu/BrowserToolRuntime.js +1 -146
- package/lib/module/floatingMenu/DefaultConfigContext.js +1 -39
- package/lib/module/floatingMenu/DevToolsSettingsModal.js +1 -3
- package/lib/module/floatingMenu/DevToolsSettingsModal.shared.js +1 -1110
- package/lib/module/floatingMenu/DevToolsSettingsModal.web.js +1 -44
- package/lib/module/floatingMenu/DevToolsVisibilityContext.js +1 -44
- package/lib/module/floatingMenu/FloatingDevTools.js +1 -556
- package/lib/module/floatingMenu/FloatingDevTools.web.js +1 -412
- package/lib/module/floatingMenu/FloatingMenu.js +1 -366
- package/lib/module/floatingMenu/MinimizedToolsContext.js +1 -266
- package/lib/module/floatingMenu/MinimizedToolsStack.js +1 -285
- package/lib/module/floatingMenu/MinimizedToolsStack.web.js +1 -374
- package/lib/module/floatingMenu/ToggleStateManager.js +1 -32
- package/lib/module/floatingMenu/ToolHost.js +1 -273
- package/lib/module/floatingMenu/ToolOpenerBridge.js +1 -37
- package/lib/module/floatingMenu/autoDiscoverPresets.js +1 -467
- package/lib/module/floatingMenu/autoExternalSync.js +1 -436
- package/lib/module/floatingMenu/defaultConfig.js +1 -158
- package/lib/module/floatingMenu/deviceIdentity.js +1 -163
- package/lib/module/floatingMenu/dial/DialDevTools.js +1 -900
- package/lib/module/floatingMenu/dial/DialDevTools.web.js +1 -891
- package/lib/module/floatingMenu/dial/DialHyperspace.js +1 -173
- package/lib/module/floatingMenu/dial/DialIcon.js +1 -301
- package/lib/module/floatingMenu/dial/DialPagination.js +1 -160
- package/lib/module/floatingMenu/dial/DialSnow.js +1 -90
- package/lib/module/floatingMenu/dial/OnboardingTooltip.js +1 -240
- package/lib/module/floatingMenu/dial/OnboardingTooltip.web.js +2 -142
- package/lib/module/floatingMenu/dial/dialUsageStore.js +1 -85
- package/lib/module/floatingMenu/dial/onboardingConstants.js +1 -3
- package/lib/module/floatingMenu/dial/onboardingConstants.web.js +1 -3
- package/lib/module/floatingMenu/externalSyncGate.js +1 -44
- package/lib/module/floatingMenu/floatingTools.js +1 -27
- package/lib/module/floatingMenu/floatingTools.web.js +1 -3
- package/lib/module/floatingMenu/optionalRequire.js +1 -33
- package/lib/module/floatingMenu/settingsBus.js +1 -3
- package/lib/module/floatingMenu/sync/appSyncAdapter.js +1 -112
- package/lib/module/floatingMenu/types.js +1 -3
- package/lib/module/floatingMenu/useDesktopSyncStatus.js +1 -25
- package/lib/module/floatingMenu/useDesktopSyncStatus.web.js +1 -10
- package/lib/module/index.js +1 -41
- package/lib/module/index.web.js +1 -36
- package/lib/module/reactDevtoolsTypes.js +1 -1
- package/lib/module/releaseOptIn.js +1 -0
- package/lib/module/telemetry/installPing.js +1 -127
- package/lib/module/toolkit.js +1 -62
- package/lib/module/toolkitComponents.js +1 -230
- package/lib/module/utils/autoDiscoverPresets.web.js +1 -38
- package/lib/module/utils/createWebTool.js +1 -17
- package/lib/module/utils/createWebTools.js +1 -25
- package/lib/module/version.js +1 -14
- package/lib/typescript/commonjs/browserRegister.d.ts +9 -1
- package/lib/typescript/commonjs/browserRegister.server.d.ts +1 -0
- package/lib/typescript/commonjs/earlyHookScript.d.ts +12 -0
- package/lib/typescript/commonjs/earlyNetwork.d.ts +52 -0
- package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +1 -0
- package/lib/typescript/commonjs/releaseOptIn.d.ts +4 -0
- package/lib/typescript/commonjs/version.d.ts +1 -1
- package/lib/typescript/module/browserRegister.d.ts +9 -1
- package/lib/typescript/module/browserRegister.server.d.ts +1 -0
- package/lib/typescript/module/earlyHookScript.d.ts +12 -0
- package/lib/typescript/module/earlyNetwork.d.ts +52 -0
- package/lib/typescript/module/floatingMenu/defaultConfig.d.ts +1 -1
- package/lib/typescript/module/index.web.d.ts +1 -0
- package/lib/typescript/module/releaseOptIn.d.ts +4 -0
- package/lib/typescript/module/version.d.ts +1 -1
- package/lib/web/chunks/chunk-5SGL6QL4.mjs +1 -0
- package/lib/web/chunks/chunk-FROELTJH.mjs +1 -0
- package/lib/web/chunks/chunk-QZDVCMCT.mjs +1 -2
- package/lib/web/chunks/chunk-R7RGRTY5.mjs +1 -2
- package/lib/web/index.mjs +2 -3
- package/lib/web/register-server.mjs +1 -2
- package/lib/web/register.mjs +1 -2
- package/lib/web/toolkit.mjs +1 -2
- package/package.json +6 -9
- package/lib/typescript/commonjs/Buoy.d.ts.map +0 -1
- package/lib/typescript/commonjs/browserRegister.d.ts.map +0 -1
- package/lib/typescript/commonjs/browserRegister.server.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/AppHost.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/AppHost.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/AppHostLogic.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/AppIcon.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/AppIcon.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/BrowserToolRuntime.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/DefaultConfigContext.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.shared.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/DevToolsVisibilityContext.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/MinimizedToolsContext.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/ToggleStateManager.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/ToolHost.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/ToolOpenerBridge.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/autoDiscoverPresets.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/deviceIdentity.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/dial/DialHyperspace.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/dial/DialPagination.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/dial/DialSnow.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/dial/dialUsageStore.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/externalSyncGate.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/floatingTools.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/optionalRequire.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/settingsBus.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/sync/appSyncAdapter.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/types.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/useDesktopSyncStatus.d.ts.map +0 -1
- package/lib/typescript/commonjs/floatingMenu/useDesktopSyncStatus.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/index.d.ts.map +0 -1
- package/lib/typescript/commonjs/index.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/reactDevtoolsTypes.d.ts.map +0 -1
- package/lib/typescript/commonjs/telemetry/installPing.d.ts.map +0 -1
- package/lib/typescript/commonjs/toolkit.d.ts.map +0 -1
- package/lib/typescript/commonjs/toolkitComponents.d.ts.map +0 -1
- package/lib/typescript/commonjs/utils/autoDiscoverPresets.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/utils/createWebTool.d.ts.map +0 -1
- package/lib/typescript/commonjs/utils/createWebTools.d.ts.map +0 -1
- package/lib/typescript/commonjs/version.d.ts.map +0 -1
- package/lib/typescript/module/Buoy.d.ts.map +0 -1
- package/lib/typescript/module/browserRegister.d.ts.map +0 -1
- package/lib/typescript/module/browserRegister.server.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/AppHost.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/AppHost.web.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/AppHostLogic.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/AppIcon.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/AppIcon.web.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/BrowserToolRuntime.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/DefaultConfigContext.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.shared.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.web.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/DevToolsVisibilityContext.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/MinimizedToolsContext.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/MinimizedToolsStack.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/MinimizedToolsStack.web.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/ToggleStateManager.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/ToolHost.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/ToolOpenerBridge.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/autoDiscoverPresets.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/defaultConfig.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/deviceIdentity.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/dial/DialDevTools.web.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/dial/DialHyperspace.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/dial/DialPagination.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/dial/DialSnow.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.web.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/dial/dialUsageStore.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/dial/onboardingConstants.web.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/externalSyncGate.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/floatingTools.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/floatingTools.web.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/optionalRequire.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/settingsBus.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/sync/appSyncAdapter.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/types.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/useDesktopSyncStatus.d.ts.map +0 -1
- package/lib/typescript/module/floatingMenu/useDesktopSyncStatus.web.d.ts.map +0 -1
- package/lib/typescript/module/index.d.ts.map +0 -1
- package/lib/typescript/module/index.web.d.ts.map +0 -1
- package/lib/typescript/module/reactDevtoolsTypes.d.ts.map +0 -1
- package/lib/typescript/module/telemetry/installPing.d.ts.map +0 -1
- package/lib/typescript/module/toolkit.d.ts.map +0 -1
- package/lib/typescript/module/toolkitComponents.d.ts.map +0 -1
- package/lib/typescript/module/utils/autoDiscoverPresets.web.d.ts.map +0 -1
- package/lib/typescript/module/utils/createWebTool.d.ts.map +0 -1
- package/lib/typescript/module/utils/createWebTools.d.ts.map +0 -1
- package/lib/typescript/module/version.d.ts.map +0 -1
- package/lib/web/chunks/chunk-5NZOQR6Q.mjs +0 -2
- package/lib/web/chunks/chunk-CGDSLLZF.mjs +0 -2
- package/lib/web/chunks/chunk-DCJDUSNR.mjs +0 -2
- package/lib/web/chunks/chunk-IDSHFS6G.mjs +0 -2
- package/lib/web/chunks/chunk-JOAMJGCS.mjs +0 -2
- package/lib/web/chunks/chunk-KYKZ2N52.mjs +0 -2
- package/lib/web/chunks/chunk-NGM7JGGS.mjs +0 -2
- package/lib/web/chunks/chunk-Z3XWDDIO.mjs +0 -2
- package/lib/web/chunks/chunk-ZTGQ6TTY.mjs +0 -2
|
@@ -1,1110 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { useState, useEffect, useCallback, useMemo } from "react";
|
|
4
|
-
import { View, Text, StyleSheet, ScrollView, TouchableOpacity, Dimensions, Platform } from "react-native";
|
|
5
|
-
import { settingsBus } from "./settingsBus.js";
|
|
6
|
-
import { useDesktopSyncStatus } from "./useDesktopSyncStatus";
|
|
7
|
-
import { SentryIcon, WifiCircuitIcon, ImageOverlayIcon, RenderCountIcon, Info, Layers, ChevronRightIcon, ChevronDown, getStorageBackendType, getUseLicense, persistentStorage, Trash2, CheckCircle2, LicenseEntryModal, BackgroundSwitcher, night, NightCard, NightRows, NightSectionLabel, NightFootnote, NightSwitch, NightButton, NightSegmented, NightBadge, NightSeparator, monoFont } from "@buoy-gg/shared-ui";
|
|
8
|
-
import { EnvIcon, StorageIcon, RoutesIcon, NetworkIcon, QueryIcon, HighlighterIcon, ReduxIcon, EventsIcon } from "@buoy-gg/floating-tools-core";
|
|
9
|
-
import { useDefaultConfig } from "./DefaultConfigContext.js";
|
|
10
|
-
import { renderAppIcon } from "./AppIcon";
|
|
11
|
-
import { JsModal, devToolsStorageKeys } from "@buoy-gg/shared-ui";
|
|
12
|
-
import { useSafeAreaInsets } from "@buoy-gg/shared-ui";
|
|
13
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
14
|
-
const STORAGE_KEY = "@react_buoy_dev_tools_settings";
|
|
15
|
-
const MAX_DIAL_SLOTS = 6;
|
|
16
|
-
const enforceDialLimit = dialTools => {
|
|
17
|
-
let remaining = MAX_DIAL_SLOTS;
|
|
18
|
-
const limited = {};
|
|
19
|
-
for (const [id, enabled] of Object.entries(dialTools)) {
|
|
20
|
-
if (enabled && remaining > 0) {
|
|
21
|
-
limited[id] = true;
|
|
22
|
-
remaining -= 1;
|
|
23
|
-
} else {
|
|
24
|
-
limited[id] = false;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return limited;
|
|
28
|
-
};
|
|
29
|
-
const sanitizeFloating = (floating, allowedKeys) => {
|
|
30
|
-
const {
|
|
31
|
-
userStatus,
|
|
32
|
-
environment,
|
|
33
|
-
...rest
|
|
34
|
-
} = floating;
|
|
35
|
-
const filteredEntries = allowedKeys ? Object.entries(rest).filter(([key]) => allowedKeys.includes(key)) : Object.entries(rest);
|
|
36
|
-
return {
|
|
37
|
-
...Object.fromEntries(filteredEntries),
|
|
38
|
-
environment: environment ?? false
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
const mergeWithDefaults = (defaults, stored, options) => {
|
|
42
|
-
if (!stored) return defaults;
|
|
43
|
-
const combinedDial = {
|
|
44
|
-
...defaults.dialTools,
|
|
45
|
-
...(stored.dialTools ?? {})
|
|
46
|
-
};
|
|
47
|
-
const dialEntries = options?.allowedDialKeys ? Object.entries(combinedDial).filter(([key]) => options.allowedDialKeys?.includes(key)) : Object.entries(combinedDial);
|
|
48
|
-
const mergedDial = enforceDialLimit(Object.fromEntries(dialEntries));
|
|
49
|
-
return {
|
|
50
|
-
dialTools: mergedDial,
|
|
51
|
-
floatingTools: sanitizeFloating({
|
|
52
|
-
...defaults.floatingTools,
|
|
53
|
-
...(stored.floatingTools ?? {})
|
|
54
|
-
}, options?.allowedFloatingKeys),
|
|
55
|
-
globalSettings: {
|
|
56
|
-
enableSharedModalDimensions: false,
|
|
57
|
-
// Default to false
|
|
58
|
-
expandableWindowControls: true,
|
|
59
|
-
// Default to true
|
|
60
|
-
...(stored.globalSettings ?? {})
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Global settings that apply to all dev tools.
|
|
67
|
-
* These settings override individual tool configurations.
|
|
68
|
-
*/
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Serialized preferences that power the floating dev tools UI. Values persist across sessions
|
|
72
|
-
* via AsyncStorage so teams can tailor which tools appear in the dial or floating row.
|
|
73
|
-
*/
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* The subset of InstalledApp the settings modal needs per tool. `icon`/`color`
|
|
77
|
-
* let rows fall back to the tool's own glyph when getToolIcon() has no
|
|
78
|
-
* night-tuned override for it.
|
|
79
|
-
*/
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Generate default settings based on available apps and optional team default configuration.
|
|
83
|
-
*
|
|
84
|
-
* @param availableApps - List of available apps from auto-discovery
|
|
85
|
-
* @param defaultFloatingTools - Optional array of tool IDs to enable by default in floating bubble
|
|
86
|
-
* @param defaultDialTools - Optional array of tool IDs to enable by default in dial menu
|
|
87
|
-
* If not provided, all dial-compatible tools are enabled by default (up to 6)
|
|
88
|
-
*/
|
|
89
|
-
const generateDefaultSettings = (availableApps = [], defaultFloatingTools, defaultDialTools) => {
|
|
90
|
-
const dialDefaults = {};
|
|
91
|
-
const floatingDefaults = {};
|
|
92
|
-
|
|
93
|
-
// Create sets for quick lookup of default-enabled tools
|
|
94
|
-
// Cast to Set<string> to allow comparison with any tool ID (including custom tools)
|
|
95
|
-
const enabledFloatingSet = new Set(defaultFloatingTools ?? []);
|
|
96
|
-
const enabledDialSet = new Set(defaultDialTools ?? []);
|
|
97
|
-
|
|
98
|
-
// If no defaultDialTools specified, auto-enable all dial-compatible tools
|
|
99
|
-
const autoEnableDialTools = !defaultDialTools || defaultDialTools.length === 0;
|
|
100
|
-
for (const app of availableApps) {
|
|
101
|
-
const {
|
|
102
|
-
id,
|
|
103
|
-
slot = "both"
|
|
104
|
-
} = app;
|
|
105
|
-
if (slot === "dial" || slot === "both") {
|
|
106
|
-
// Auto-enable if no defaults specified, otherwise check the set
|
|
107
|
-
dialDefaults[id] = autoEnableDialTools ? true : enabledDialSet.has(id);
|
|
108
|
-
}
|
|
109
|
-
if (slot === "row" || slot === "both") {
|
|
110
|
-
// Enable if in defaultFloatingTools, otherwise false
|
|
111
|
-
floatingDefaults[id] = enabledFloatingSet.has(id);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return {
|
|
115
|
-
dialTools: enforceDialLimit(dialDefaults),
|
|
116
|
-
floatingTools: {
|
|
117
|
-
...floatingDefaults,
|
|
118
|
-
// Special: environment badge - check if 'environment' is in the floating defaults
|
|
119
|
-
environment: enabledFloatingSet.has('environment')
|
|
120
|
-
},
|
|
121
|
-
globalSettings: {
|
|
122
|
-
enableSharedModalDimensions: false,
|
|
123
|
-
// Default to false - each modal has its own persistence
|
|
124
|
-
expandableWindowControls: true // Default to true - expandable on touch devices
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Configurable modal surface that lets engineers pick which dev tools appear in the dial and
|
|
131
|
-
* floating row. Persists preferences and enforces slot limits for a consistent UX.
|
|
132
|
-
*
|
|
133
|
-
* Styled in the "night" language (Everlights port — see shared-ui's
|
|
134
|
-
* ui/night): section labels outside near-black cards, hairline-separated
|
|
135
|
-
* rows, real switches, and a transparent header the star field runs through.
|
|
136
|
-
*/
|
|
137
|
-
export const DevToolsSettingsModal = ({
|
|
138
|
-
visible,
|
|
139
|
-
onClose,
|
|
140
|
-
onSettingsChange,
|
|
141
|
-
initialSettings,
|
|
142
|
-
availableApps = [],
|
|
143
|
-
storage = persistentStorage,
|
|
144
|
-
zIndex
|
|
145
|
-
}) => {
|
|
146
|
-
// Get team default configuration from context
|
|
147
|
-
const {
|
|
148
|
-
defaultFloatingTools,
|
|
149
|
-
defaultDialTools
|
|
150
|
-
} = useDefaultConfig();
|
|
151
|
-
const defaultSettings = useMemo(() => generateDefaultSettings(availableApps, defaultFloatingTools, defaultDialTools), [availableApps, defaultFloatingTools, defaultDialTools]);
|
|
152
|
-
const allowedDialKeys = useMemo(() => Object.keys(defaultSettings.dialTools), [defaultSettings]);
|
|
153
|
-
const allowedFloatingKeys = useMemo(() => Object.keys(defaultSettings.floatingTools).filter(key => key !== "environment"), [defaultSettings]);
|
|
154
|
-
const [settings, setSettings] = useState(initialSettings || cachedSettings || defaultSettings);
|
|
155
|
-
const [activeTab, setActiveTab] = useState(cachedActiveTab ?? "floating");
|
|
156
|
-
const [activeTabLoaded, setActiveTabLoaded] = useState(cachedActiveTab !== null);
|
|
157
|
-
const [expandedSettings, setExpandedSettings] = useState(new Set());
|
|
158
|
-
const [showLicenseModal, setShowLicenseModal] = useState(false);
|
|
159
|
-
/**
|
|
160
|
-
* Tool icons are the expensive half of this modal: ~20 of them, each a
|
|
161
|
-
* data-driven glyph that mounts one view per shape. Rendering them in the
|
|
162
|
-
* first pass pushed the modal's first paint out to ~240ms, so the tap felt
|
|
163
|
-
* dead. Holding them back a single frame lets the chrome and the tool rows
|
|
164
|
-
* paint at ~100ms and the glyphs land during the entrance fade.
|
|
165
|
-
*
|
|
166
|
-
* There is no layout shift: `styles.toolIconSlot` is a fixed box, so it
|
|
167
|
-
* reads as the icon's placeholder until filled.
|
|
168
|
-
*/
|
|
169
|
-
const [iconsReady, setIconsReady] = useState(false);
|
|
170
|
-
useEffect(() => {
|
|
171
|
-
const frame = requestAnimationFrame(() => setIconsReady(true));
|
|
172
|
-
return () => cancelAnimationFrame(frame);
|
|
173
|
-
}, []);
|
|
174
|
-
|
|
175
|
-
// null when external-sync isn't installed (or predates the status store);
|
|
176
|
-
// "idle" until AutoExternalSync initializes — the row hides in both cases.
|
|
177
|
-
const desktopSyncStatus = useDesktopSyncStatus();
|
|
178
|
-
|
|
179
|
-
// Load persisted active tab on mount
|
|
180
|
-
useEffect(() => {
|
|
181
|
-
const loadActiveTab = async () => {
|
|
182
|
-
try {
|
|
183
|
-
const savedTab = await persistentStorage.getItem(devToolsStorageKeys.settings.activeTab());
|
|
184
|
-
if (savedTab && ["floating", "settings", "pro"].includes(savedTab)) {
|
|
185
|
-
const tab = savedTab;
|
|
186
|
-
cachedActiveTab = tab;
|
|
187
|
-
setActiveTab(prev => prev === tab ? prev : tab);
|
|
188
|
-
}
|
|
189
|
-
} catch (error) {
|
|
190
|
-
// Failed to load active tab - use default
|
|
191
|
-
} finally {
|
|
192
|
-
setActiveTabLoaded(true);
|
|
193
|
-
}
|
|
194
|
-
};
|
|
195
|
-
loadActiveTab();
|
|
196
|
-
}, []);
|
|
197
|
-
|
|
198
|
-
// Persist active tab when it changes
|
|
199
|
-
useEffect(() => {
|
|
200
|
-
// Only persist after initial state is loaded to avoid overwriting with default
|
|
201
|
-
if (!activeTabLoaded) return;
|
|
202
|
-
persistentStorage.setItem(devToolsStorageKeys.settings.activeTab(), activeTab).catch(error => {
|
|
203
|
-
// Failed to save active tab - continue without persistence
|
|
204
|
-
console.warn("Failed to save settings active tab:", error);
|
|
205
|
-
});
|
|
206
|
-
}, [activeTab, activeTabLoaded]);
|
|
207
|
-
|
|
208
|
-
// License hooks
|
|
209
|
-
const useLicenseHook = getUseLicense();
|
|
210
|
-
const license = useLicenseHook?.();
|
|
211
|
-
const isPro = license?.isPro ?? false;
|
|
212
|
-
// A key is set but validation is failing. Without this the tab renders a bare
|
|
213
|
-
// "Free" badge, which reads as "your key was rejected" when what actually
|
|
214
|
-
// happened is that we never reached the license server. `errorKind` decides
|
|
215
|
-
// which story to tell: only a transport failure gets the VPN/proxy advice — a
|
|
216
|
-
// real verdict (suspended, expired) with that advice would send the user off
|
|
217
|
-
// to fight their network over a key that was genuinely rejected.
|
|
218
|
-
const licenseError = !isPro && license?.licenseKey ? license.error : null;
|
|
219
|
-
const licenseErrorIsTransport = license?.errorKind === "transport";
|
|
220
|
-
const [storageBackend, setStorageBackend] = useState(cachedStorageBackend);
|
|
221
|
-
const [isClearing, setIsClearing] = useState(false);
|
|
222
|
-
const [clearSuccess, setClearSuccess] = useState(false);
|
|
223
|
-
const [isStorageExpanded, setIsStorageExpanded] = useState(false);
|
|
224
|
-
const [savedKeys, setSavedKeys] = useState([]);
|
|
225
|
-
const [savedKeysLoading, setSavedKeysLoading] = useState(false);
|
|
226
|
-
const insets = useSafeAreaInsets();
|
|
227
|
-
const screenHeight = Dimensions.get("window").height;
|
|
228
|
-
const screenWidth = Dimensions.get("window").width;
|
|
229
|
-
const modalHeight = Math.floor(screenHeight * 0.33); // 1/3 of screen height
|
|
230
|
-
const modalWidth = Math.min(screenWidth - 32, 400); // Modal width with padding
|
|
231
|
-
|
|
232
|
-
const loadSettings = useCallback(async () => {
|
|
233
|
-
try {
|
|
234
|
-
const savedSettings = await storage.getItem(STORAGE_KEY);
|
|
235
|
-
if (savedSettings) {
|
|
236
|
-
const parsed = JSON.parse(savedSettings);
|
|
237
|
-
const merged = mergeWithDefaults(defaultSettings, parsed, {
|
|
238
|
-
allowedDialKeys,
|
|
239
|
-
allowedFloatingKeys
|
|
240
|
-
});
|
|
241
|
-
cachedSettings = merged;
|
|
242
|
-
setSettings(prev => sameSettings(prev, merged) ? prev : merged);
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
cachedSettings = defaultSettings;
|
|
246
|
-
setSettings(prev => sameSettings(prev, defaultSettings) ? prev : defaultSettings);
|
|
247
|
-
} catch (error) {
|
|
248
|
-
console.error("Failed to load dev tools settings:", error);
|
|
249
|
-
setSettings(defaultSettings);
|
|
250
|
-
}
|
|
251
|
-
}, [defaultSettings, allowedDialKeys, allowedFloatingKeys, storage]);
|
|
252
|
-
useEffect(() => {
|
|
253
|
-
loadSettings();
|
|
254
|
-
}, [loadSettings]);
|
|
255
|
-
|
|
256
|
-
// Load storage backend type
|
|
257
|
-
useEffect(() => {
|
|
258
|
-
getStorageBackendType().then(backend => {
|
|
259
|
-
cachedStorageBackend = backend;
|
|
260
|
-
setStorageBackend(prev => prev === backend ? prev : backend);
|
|
261
|
-
});
|
|
262
|
-
}, []);
|
|
263
|
-
|
|
264
|
-
// Load saved keys when storage card is expanded
|
|
265
|
-
useEffect(() => {
|
|
266
|
-
if (isStorageExpanded) {
|
|
267
|
-
setSavedKeysLoading(true);
|
|
268
|
-
persistentStorage.getAllKeys().then(keys => {
|
|
269
|
-
setSavedKeys(keys.sort());
|
|
270
|
-
}).catch(() => {
|
|
271
|
-
setSavedKeys([]);
|
|
272
|
-
}).finally(() => {
|
|
273
|
-
setSavedKeysLoading(false);
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
}, [isStorageExpanded]);
|
|
277
|
-
const saveSettings = async newSettings => {
|
|
278
|
-
try {
|
|
279
|
-
const limitedSettings = {
|
|
280
|
-
...newSettings,
|
|
281
|
-
dialTools: enforceDialLimit(newSettings.dialTools)
|
|
282
|
-
};
|
|
283
|
-
await storage.setItem(STORAGE_KEY, JSON.stringify(limitedSettings));
|
|
284
|
-
cachedSettings = limitedSettings;
|
|
285
|
-
setSettings(limitedSettings);
|
|
286
|
-
onSettingsChange?.(limitedSettings);
|
|
287
|
-
// Notify listeners (e.g., floating bubble) to refresh immediately
|
|
288
|
-
settingsBus.emit(limitedSettings);
|
|
289
|
-
} catch (error) {
|
|
290
|
-
console.error("Failed to save dev tools settings:", error);
|
|
291
|
-
}
|
|
292
|
-
};
|
|
293
|
-
const toggleFloatingTool = tool => {
|
|
294
|
-
const newSettings = {
|
|
295
|
-
...settings,
|
|
296
|
-
floatingTools: {
|
|
297
|
-
...settings.floatingTools,
|
|
298
|
-
[tool]: !settings.floatingTools[tool]
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
saveSettings(newSettings);
|
|
302
|
-
};
|
|
303
|
-
const toggleGlobalSetting = setting => {
|
|
304
|
-
const newSettings = {
|
|
305
|
-
...settings,
|
|
306
|
-
globalSettings: {
|
|
307
|
-
...settings.globalSettings,
|
|
308
|
-
[setting]: !settings.globalSettings?.[setting]
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
saveSettings(newSettings);
|
|
312
|
-
};
|
|
313
|
-
const handleClearStorage = async () => {
|
|
314
|
-
setIsClearing(true);
|
|
315
|
-
setClearSuccess(false);
|
|
316
|
-
try {
|
|
317
|
-
await persistentStorage.clear();
|
|
318
|
-
setClearSuccess(true);
|
|
319
|
-
// Reset settings to defaults after clearing
|
|
320
|
-
setSettings(defaultSettings);
|
|
321
|
-
if (storage !== persistentStorage) await storage.setItem(STORAGE_KEY, JSON.stringify(defaultSettings));
|
|
322
|
-
onSettingsChange?.(defaultSettings);
|
|
323
|
-
settingsBus.emit(defaultSettings);
|
|
324
|
-
// Reset success state after 2 seconds
|
|
325
|
-
setTimeout(() => setClearSuccess(false), 2000);
|
|
326
|
-
} catch (error) {
|
|
327
|
-
console.error("Failed to clear storage:", error);
|
|
328
|
-
} finally {
|
|
329
|
-
setIsClearing(false);
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
// Modal is fixed to bottom sheet mode
|
|
334
|
-
const handleModeChange = useCallback(_mode => {
|
|
335
|
-
// Mode changes handled by JsModal
|
|
336
|
-
}, []);
|
|
337
|
-
const getToolDescription = tool => {
|
|
338
|
-
// Get description from availableApps
|
|
339
|
-
const app = availableApps.find(a => a.id === tool);
|
|
340
|
-
if (app?.description) {
|
|
341
|
-
return app.description;
|
|
342
|
-
}
|
|
343
|
-
if (tool === "environment") {
|
|
344
|
-
return "Environment badge.";
|
|
345
|
-
}
|
|
346
|
-
return "";
|
|
347
|
-
};
|
|
348
|
-
const getToolLabel = tool => {
|
|
349
|
-
if (tool === "environment") {
|
|
350
|
-
return "Env Badge";
|
|
351
|
-
}
|
|
352
|
-
const app = availableApps.find(a => a.id === tool);
|
|
353
|
-
return app?.name ?? tool.toUpperCase().replace(/_/g, " ");
|
|
354
|
-
};
|
|
355
|
-
const getToolIcon = tool => {
|
|
356
|
-
if (!iconsReady) return null;
|
|
357
|
-
switch (tool) {
|
|
358
|
-
case "query":
|
|
359
|
-
return /*#__PURE__*/_jsx(QueryIcon, {
|
|
360
|
-
size: 18
|
|
361
|
-
});
|
|
362
|
-
case "env":
|
|
363
|
-
return /*#__PURE__*/_jsx(EnvIcon, {
|
|
364
|
-
size: 18
|
|
365
|
-
});
|
|
366
|
-
case "sentry":
|
|
367
|
-
return /*#__PURE__*/_jsx(SentryIcon, {
|
|
368
|
-
size: 18,
|
|
369
|
-
color: "#9945FF"
|
|
370
|
-
});
|
|
371
|
-
case "storage":
|
|
372
|
-
return /*#__PURE__*/_jsx(StorageIcon, {
|
|
373
|
-
size: 18,
|
|
374
|
-
bgColor: night.color.surface
|
|
375
|
-
});
|
|
376
|
-
case "wifi":
|
|
377
|
-
case "query-wifi-toggle":
|
|
378
|
-
// Support both IDs for wifi toggle
|
|
379
|
-
return /*#__PURE__*/_jsx(WifiCircuitIcon, {
|
|
380
|
-
size: 18,
|
|
381
|
-
colorPreset: "green",
|
|
382
|
-
strength: 4,
|
|
383
|
-
noBackground: true
|
|
384
|
-
});
|
|
385
|
-
case "route-events":
|
|
386
|
-
return /*#__PURE__*/_jsx(RoutesIcon, {
|
|
387
|
-
size: 18,
|
|
388
|
-
bgColor: night.color.surface
|
|
389
|
-
});
|
|
390
|
-
case "network":
|
|
391
|
-
return /*#__PURE__*/_jsx(NetworkIcon, {
|
|
392
|
-
size: 18
|
|
393
|
-
});
|
|
394
|
-
case "environment":
|
|
395
|
-
return /*#__PURE__*/_jsx(EnvIcon, {
|
|
396
|
-
size: 18
|
|
397
|
-
});
|
|
398
|
-
case "debug-borders":
|
|
399
|
-
return /*#__PURE__*/_jsx(Layers, {
|
|
400
|
-
size: 18,
|
|
401
|
-
color: night.color.accent
|
|
402
|
-
});
|
|
403
|
-
case "highlight-updates":
|
|
404
|
-
return /*#__PURE__*/_jsx(RenderCountIcon, {
|
|
405
|
-
size: 18,
|
|
406
|
-
color: night.color.accent
|
|
407
|
-
});
|
|
408
|
-
case "highlight-updates-modal":
|
|
409
|
-
return /*#__PURE__*/_jsx(HighlighterIcon, {
|
|
410
|
-
size: 18
|
|
411
|
-
});
|
|
412
|
-
case "redux":
|
|
413
|
-
return /*#__PURE__*/_jsx(ReduxIcon, {
|
|
414
|
-
size: 18
|
|
415
|
-
});
|
|
416
|
-
case "image-overlay":
|
|
417
|
-
return /*#__PURE__*/_jsx(ImageOverlayIcon, {
|
|
418
|
-
size: 18
|
|
419
|
-
});
|
|
420
|
-
case "events":
|
|
421
|
-
return /*#__PURE__*/_jsx(EventsIcon, {
|
|
422
|
-
size: 18
|
|
423
|
-
});
|
|
424
|
-
case "zustand":
|
|
425
|
-
try {
|
|
426
|
-
// @ts-ignore - Optional package
|
|
427
|
-
const {
|
|
428
|
-
ZustandIcon
|
|
429
|
-
} = require("@buoy-gg/zustand");
|
|
430
|
-
return /*#__PURE__*/_jsx(ZustandIcon, {
|
|
431
|
-
size: 18
|
|
432
|
-
});
|
|
433
|
-
} catch {
|
|
434
|
-
return /*#__PURE__*/_jsx(Info, {
|
|
435
|
-
size: 18,
|
|
436
|
-
color: night.color.textSecondary
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
case "jotai":
|
|
440
|
-
try {
|
|
441
|
-
// @ts-ignore - Optional package
|
|
442
|
-
const {
|
|
443
|
-
JotaiIcon
|
|
444
|
-
} = require("@buoy-gg/jotai");
|
|
445
|
-
return /*#__PURE__*/_jsx(JotaiIcon, {
|
|
446
|
-
size: 18
|
|
447
|
-
});
|
|
448
|
-
} catch {
|
|
449
|
-
return /*#__PURE__*/_jsx(Info, {
|
|
450
|
-
size: 18,
|
|
451
|
-
color: night.color.textSecondary
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
default:
|
|
455
|
-
{
|
|
456
|
-
// Tools without a night-tuned override (console, images, time
|
|
457
|
-
// machine, perf monitor, custom toolkit tools, …) render their own
|
|
458
|
-
// app icon — the same artwork the dial shows.
|
|
459
|
-
const app = availableApps.find(a => a.id === tool);
|
|
460
|
-
if (app?.icon != null) {
|
|
461
|
-
return renderAppIcon(app.icon, {
|
|
462
|
-
slot: "row",
|
|
463
|
-
size: 18,
|
|
464
|
-
color: app.color
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
|
-
return /*#__PURE__*/_jsx(Info, {
|
|
468
|
-
size: 18,
|
|
469
|
-
color: night.color.textSecondary
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
};
|
|
474
|
-
|
|
475
|
-
/**
|
|
476
|
-
* One tool row: glyph, name + one-line description, switch. Lives inside a
|
|
477
|
-
* NightCard's hairline-separated rows.
|
|
478
|
-
*/
|
|
479
|
-
const renderToolRow = (keyName, value, disabled, onToggle) => {
|
|
480
|
-
const description = getToolDescription(keyName);
|
|
481
|
-
return /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
482
|
-
activeOpacity: disabled ? 1 : 0.7,
|
|
483
|
-
onPress: () => !disabled && onToggle(),
|
|
484
|
-
accessibilityLabel: getToolLabel(keyName),
|
|
485
|
-
accessibilityRole: "button",
|
|
486
|
-
style: [styles.toolRow, disabled && styles.rowDisabled],
|
|
487
|
-
children: [/*#__PURE__*/_jsx(View, {
|
|
488
|
-
style: styles.toolIconSlot,
|
|
489
|
-
children: getToolIcon(keyName)
|
|
490
|
-
}), /*#__PURE__*/_jsxs(View, {
|
|
491
|
-
style: styles.rowInfo,
|
|
492
|
-
children: [/*#__PURE__*/_jsxs(Text, {
|
|
493
|
-
style: styles.rowLabel,
|
|
494
|
-
children: [getToolLabel(keyName), disabled ? " · max 6" : ""]
|
|
495
|
-
}), description ? /*#__PURE__*/_jsx(Text, {
|
|
496
|
-
style: styles.rowCaption,
|
|
497
|
-
numberOfLines: 1,
|
|
498
|
-
children: description
|
|
499
|
-
}) : null]
|
|
500
|
-
}), /*#__PURE__*/_jsx(NightSwitch, {
|
|
501
|
-
value: value,
|
|
502
|
-
disabled: disabled,
|
|
503
|
-
onValueChange: () => onToggle()
|
|
504
|
-
})]
|
|
505
|
-
}, keyName);
|
|
506
|
-
};
|
|
507
|
-
|
|
508
|
-
// Toggle expanded state for a setting card
|
|
509
|
-
const toggleSettingExpanded = settingKey => {
|
|
510
|
-
setExpandedSettings(prev => {
|
|
511
|
-
const newSet = new Set(prev);
|
|
512
|
-
if (newSet.has(settingKey)) {
|
|
513
|
-
newSet.delete(settingKey);
|
|
514
|
-
} else {
|
|
515
|
-
newSet.add(settingKey);
|
|
516
|
-
}
|
|
517
|
-
return newSet;
|
|
518
|
-
});
|
|
519
|
-
};
|
|
520
|
-
|
|
521
|
-
/**
|
|
522
|
-
* One global-setting row: name + one-line hint, switch, and a tap-to-expand
|
|
523
|
-
* body holding the full description and recommendation.
|
|
524
|
-
*/
|
|
525
|
-
const renderGlobalSettingRow = (settingKey, label, shortDescription, fullDescription, recommendation) => {
|
|
526
|
-
const value = settings.globalSettings?.[settingKey] ?? false;
|
|
527
|
-
const isExpanded = expandedSettings.has(settingKey);
|
|
528
|
-
return /*#__PURE__*/_jsxs(View, {
|
|
529
|
-
children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
530
|
-
activeOpacity: 0.7,
|
|
531
|
-
onPress: () => toggleSettingExpanded(settingKey),
|
|
532
|
-
accessibilityLabel: label,
|
|
533
|
-
style: styles.settingRow,
|
|
534
|
-
children: [/*#__PURE__*/_jsxs(View, {
|
|
535
|
-
style: styles.rowInfo,
|
|
536
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
537
|
-
style: styles.rowLabel,
|
|
538
|
-
children: label
|
|
539
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
540
|
-
style: styles.rowCaption,
|
|
541
|
-
numberOfLines: isExpanded ? 0 : 1,
|
|
542
|
-
children: shortDescription
|
|
543
|
-
})]
|
|
544
|
-
}), /*#__PURE__*/_jsx(NightSwitch, {
|
|
545
|
-
value: value,
|
|
546
|
-
onValueChange: () => toggleGlobalSetting(settingKey)
|
|
547
|
-
})]
|
|
548
|
-
}), isExpanded && /*#__PURE__*/_jsxs(View, {
|
|
549
|
-
style: styles.settingRowBody,
|
|
550
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
551
|
-
style: styles.settingRowBodyText,
|
|
552
|
-
children: fullDescription
|
|
553
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
554
|
-
style: styles.settingRowBodyHint,
|
|
555
|
-
children: recommendation
|
|
556
|
-
})]
|
|
557
|
-
})]
|
|
558
|
-
}, settingKey);
|
|
559
|
-
};
|
|
560
|
-
|
|
561
|
-
/** Section = uppercase label, card, optional footnote(s) under it. */
|
|
562
|
-
const renderSection = (label, card, footnotes) => /*#__PURE__*/_jsxs(View, {
|
|
563
|
-
children: [/*#__PURE__*/_jsx(NightSectionLabel, {
|
|
564
|
-
children: label
|
|
565
|
-
}), card, footnotes]
|
|
566
|
-
});
|
|
567
|
-
const storageBadge = storageBackend === "filesystem" ? /*#__PURE__*/_jsx(NightBadge, {
|
|
568
|
-
label: "File System",
|
|
569
|
-
tone: "accent"
|
|
570
|
-
}) : storageBackend === "asyncstorage" ? /*#__PURE__*/_jsx(NightBadge, {
|
|
571
|
-
label: "Async Storage",
|
|
572
|
-
tone: "warning"
|
|
573
|
-
}) : storageBackend === "localstorage" ? /*#__PURE__*/_jsx(NightBadge, {
|
|
574
|
-
label: "Local Storage",
|
|
575
|
-
tone: "accent"
|
|
576
|
-
}) : storageBackend === "memory" ? /*#__PURE__*/_jsx(NightBadge, {
|
|
577
|
-
label: "Memory",
|
|
578
|
-
tone: "danger"
|
|
579
|
-
}) : /*#__PURE__*/_jsx(NightBadge, {
|
|
580
|
-
label: "Loading\u2026",
|
|
581
|
-
tone: "neutral"
|
|
582
|
-
});
|
|
583
|
-
const renderContent = () => /*#__PURE__*/_jsxs(View, {
|
|
584
|
-
style: styles.container,
|
|
585
|
-
children: [/*#__PURE__*/_jsxs(ScrollView, {
|
|
586
|
-
style: styles.scrollContent,
|
|
587
|
-
showsVerticalScrollIndicator: false,
|
|
588
|
-
contentContainerStyle: styles.scrollContainer,
|
|
589
|
-
children: [activeTab === "floating" && /*#__PURE__*/_jsx(_Fragment, {
|
|
590
|
-
children: renderSection("Floating Tools", /*#__PURE__*/_jsx(NightCard, {
|
|
591
|
-
glass: true,
|
|
592
|
-
children: /*#__PURE__*/_jsx(NightRows, {
|
|
593
|
-
children: Object.entries(settings.floatingTools).map(([key, value]) => renderToolRow(key, value, false, () => toggleFloatingTool(key)))
|
|
594
|
-
})
|
|
595
|
-
}), /*#__PURE__*/_jsx(NightFootnote, {
|
|
596
|
-
children: "Tools switched on here appear in the floating bubble row."
|
|
597
|
-
}))
|
|
598
|
-
}), activeTab === "settings" && /*#__PURE__*/_jsxs(_Fragment, {
|
|
599
|
-
children: [renderSection("Background", /*#__PURE__*/_jsx(NightCard, {
|
|
600
|
-
glass: true,
|
|
601
|
-
padded: true,
|
|
602
|
-
children: /*#__PURE__*/_jsx(BackgroundSwitcher, {
|
|
603
|
-
embedded: true
|
|
604
|
-
})
|
|
605
|
-
})), renderSection("Storage", /*#__PURE__*/_jsxs(NightCard, {
|
|
606
|
-
glass: true,
|
|
607
|
-
children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
608
|
-
activeOpacity: 0.7,
|
|
609
|
-
onPress: () => setIsStorageExpanded(!isStorageExpanded),
|
|
610
|
-
accessibilityLabel: "Storage Type",
|
|
611
|
-
style: styles.settingRow,
|
|
612
|
-
children: [/*#__PURE__*/_jsx(View, {
|
|
613
|
-
style: styles.rowInfo,
|
|
614
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
615
|
-
style: styles.rowLabel,
|
|
616
|
-
children: "Storage Type"
|
|
617
|
-
})
|
|
618
|
-
}), storageBadge, isStorageExpanded ? /*#__PURE__*/_jsx(ChevronDown, {
|
|
619
|
-
size: 16,
|
|
620
|
-
color: night.color.textTertiary
|
|
621
|
-
}) : /*#__PURE__*/_jsx(ChevronRightIcon, {
|
|
622
|
-
size: 16,
|
|
623
|
-
color: night.color.textTertiary
|
|
624
|
-
})]
|
|
625
|
-
}), isStorageExpanded && /*#__PURE__*/_jsxs(_Fragment, {
|
|
626
|
-
children: [/*#__PURE__*/_jsxs(View, {
|
|
627
|
-
style: styles.settingRowBody,
|
|
628
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
629
|
-
style: styles.settingRowBodyText,
|
|
630
|
-
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 === "localstorage" ? "Settings are saved in this browser. Clearing site data removes them." : storageBackend === "memory" ? "Settings are stored in memory only and will be lost on app restart." : "Checking storage backend…"
|
|
631
|
-
}), storageBackend === "asyncstorage" && /*#__PURE__*/_jsx(Text, {
|
|
632
|
-
style: [styles.settingRowBodyText, styles.bodyWarning],
|
|
633
|
-
children: "Install expo-file-system to persist settings through logout flows."
|
|
634
|
-
}), storageBackend === "memory" && /*#__PURE__*/_jsx(Text, {
|
|
635
|
-
style: [styles.settingRowBodyText, styles.bodyDanger],
|
|
636
|
-
children: "No persistent storage available \u2014 install expo-file-system (best) or @react-native-async-storage/async-storage."
|
|
637
|
-
})]
|
|
638
|
-
}), /*#__PURE__*/_jsx(NightSeparator, {}), /*#__PURE__*/_jsxs(View, {
|
|
639
|
-
style: styles.storageExpanded,
|
|
640
|
-
children: [/*#__PURE__*/_jsxs(View, {
|
|
641
|
-
style: styles.storageExpandedHeader,
|
|
642
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
643
|
-
style: styles.storageExpandedTitle,
|
|
644
|
-
children: "SAVED SETTINGS"
|
|
645
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
646
|
-
style: styles.storageExpandedCount,
|
|
647
|
-
children: savedKeysLoading ? "…" : `${savedKeys.length} keys`
|
|
648
|
-
})]
|
|
649
|
-
}), savedKeysLoading ? /*#__PURE__*/_jsx(Text, {
|
|
650
|
-
style: styles.storageKeyEmpty,
|
|
651
|
-
children: "Loading\u2026"
|
|
652
|
-
}) : savedKeys.length === 0 ? /*#__PURE__*/_jsx(Text, {
|
|
653
|
-
style: styles.storageKeyEmpty,
|
|
654
|
-
children: "No settings saved yet"
|
|
655
|
-
}) : /*#__PURE__*/_jsx(ScrollView, {
|
|
656
|
-
style: styles.storageKeysList,
|
|
657
|
-
nestedScrollEnabled: true,
|
|
658
|
-
showsVerticalScrollIndicator: true,
|
|
659
|
-
children: savedKeys.map(key => /*#__PURE__*/_jsx(View, {
|
|
660
|
-
style: styles.storageKeyItem,
|
|
661
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
662
|
-
style: styles.storageKeyText,
|
|
663
|
-
numberOfLines: 1,
|
|
664
|
-
children: key.replace("@react_buoy_", "")
|
|
665
|
-
})
|
|
666
|
-
}, key))
|
|
667
|
-
})]
|
|
668
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
669
|
-
style: styles.cardButtonWrap,
|
|
670
|
-
children: /*#__PURE__*/_jsx(NightButton, {
|
|
671
|
-
label: clearSuccess ? "Cleared" : isClearing ? "Clearing…" : "Clear All Settings",
|
|
672
|
-
variant: clearSuccess ? "secondary" : "destructive",
|
|
673
|
-
size: "md",
|
|
674
|
-
disabled: isClearing,
|
|
675
|
-
onPress: handleClearStorage,
|
|
676
|
-
icon: clearSuccess ? /*#__PURE__*/_jsx(CheckCircle2, {
|
|
677
|
-
size: 15,
|
|
678
|
-
color: night.color.accent
|
|
679
|
-
}) : /*#__PURE__*/_jsx(Trash2, {
|
|
680
|
-
size: 15,
|
|
681
|
-
color: isClearing ? night.color.textTertiary : night.color.danger
|
|
682
|
-
})
|
|
683
|
-
})
|
|
684
|
-
})]
|
|
685
|
-
})]
|
|
686
|
-
})), desktopSyncStatus && desktopSyncStatus.state !== "idle" && renderSection("Desktop Sync", /*#__PURE__*/_jsxs(NightCard, {
|
|
687
|
-
glass: true,
|
|
688
|
-
children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
689
|
-
activeOpacity: 0.7,
|
|
690
|
-
onPress: () => toggleSettingExpanded("desktopSync"),
|
|
691
|
-
accessibilityLabel: "Buoy Desktop",
|
|
692
|
-
style: styles.settingRow,
|
|
693
|
-
children: [/*#__PURE__*/_jsx(View, {
|
|
694
|
-
style: styles.rowInfo,
|
|
695
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
696
|
-
style: styles.rowLabel,
|
|
697
|
-
children: "Buoy Desktop"
|
|
698
|
-
})
|
|
699
|
-
}), desktopSyncStatus.state === "connected" ? /*#__PURE__*/_jsx(NightBadge, {
|
|
700
|
-
label: "Connected",
|
|
701
|
-
tone: "accent"
|
|
702
|
-
}) : desktopSyncStatus.state === "connecting" ? /*#__PURE__*/_jsx(NightBadge, {
|
|
703
|
-
label: "Connecting\u2026",
|
|
704
|
-
tone: "neutral"
|
|
705
|
-
}) : /*#__PURE__*/_jsx(NightBadge, {
|
|
706
|
-
label: "Retrying",
|
|
707
|
-
tone: "warning"
|
|
708
|
-
}), expandedSettings.has("desktopSync") ? /*#__PURE__*/_jsx(ChevronDown, {
|
|
709
|
-
size: 16,
|
|
710
|
-
color: night.color.textTertiary
|
|
711
|
-
}) : /*#__PURE__*/_jsx(ChevronRightIcon, {
|
|
712
|
-
size: 16,
|
|
713
|
-
color: night.color.textTertiary
|
|
714
|
-
})]
|
|
715
|
-
}), expandedSettings.has("desktopSync") && /*#__PURE__*/_jsxs(View, {
|
|
716
|
-
style: styles.settingRowBody,
|
|
717
|
-
children: [/*#__PURE__*/_jsxs(Text, {
|
|
718
|
-
style: styles.settingRowBodyText,
|
|
719
|
-
children: [desktopSyncStatus.state === "connected" ? "Streaming tool data to Buoy Desktop at " : "Trying to reach Buoy Desktop at ", /*#__PURE__*/_jsx(Text, {
|
|
720
|
-
style: styles.footnoteMono,
|
|
721
|
-
children: desktopSyncStatus.targetURL ?? "(unknown URL)"
|
|
722
|
-
}), desktopSyncStatus.state !== "connected" && " — is the desktop app running? Override with the externalSync socketURL option if it runs elsewhere."]
|
|
723
|
-
}), desktopSyncStatus.state === "retrying" && desktopSyncStatus.lastError && /*#__PURE__*/_jsxs(Text, {
|
|
724
|
-
style: [styles.settingRowBodyText, styles.bodyWarning],
|
|
725
|
-
children: ["Last error: ", desktopSyncStatus.lastError]
|
|
726
|
-
})]
|
|
727
|
-
})]
|
|
728
|
-
})), renderSection("Modal Behavior", /*#__PURE__*/_jsx(NightCard, {
|
|
729
|
-
glass: true,
|
|
730
|
-
children: /*#__PURE__*/_jsxs(NightRows, {
|
|
731
|
-
children: [renderGlobalSettingRow("enableSharedModalDimensions", "Shared Modal Size", "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."), Platform.OS !== "web" ? renderGlobalSettingRow("expandableWindowControls", "Expand Controls", "iPad-style expandable window buttons", "When enabled, the window control buttons (minimize, toggle mode, close) start as small dots and expand into larger, easy-to-tap buttons when pressed. When disabled, buttons are directly tappable at their small size.", "Keep ON for touch devices where the small buttons are hard to press. Turn OFF if you prefer direct single-tap access.") : null]
|
|
732
|
-
})
|
|
733
|
-
}))]
|
|
734
|
-
}), activeTab === "pro" && /*#__PURE__*/_jsxs(_Fragment, {
|
|
735
|
-
children: [renderSection("License", /*#__PURE__*/_jsx(NightCard, {
|
|
736
|
-
glass: true,
|
|
737
|
-
children: /*#__PURE__*/_jsxs(View, {
|
|
738
|
-
style: styles.settingRow,
|
|
739
|
-
children: [/*#__PURE__*/_jsx(View, {
|
|
740
|
-
style: styles.rowInfo,
|
|
741
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
742
|
-
style: styles.rowLabel,
|
|
743
|
-
children: "License Status"
|
|
744
|
-
})
|
|
745
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
746
|
-
testID: "buoy-settings-license-status",
|
|
747
|
-
accessibilityLabel: isPro ? "License: Active" : "License: Free",
|
|
748
|
-
children: isPro ? /*#__PURE__*/_jsx(NightBadge, {
|
|
749
|
-
label: "Active",
|
|
750
|
-
tone: "accent"
|
|
751
|
-
}) : /*#__PURE__*/_jsx(NightBadge, {
|
|
752
|
-
label: "Free",
|
|
753
|
-
tone: "neutral"
|
|
754
|
-
})
|
|
755
|
-
})]
|
|
756
|
-
})
|
|
757
|
-
}), /*#__PURE__*/_jsx(NightFootnote, {
|
|
758
|
-
children: isPro ? "You have full access to all Buoy DevTools features." : licenseError ? licenseErrorIsTransport ? `Couldn't reach the license server (${licenseError}). A VPN or corporate proxy can block buoy.gg — turn it off and reload. Your key hasn't been rejected.` : `License check failed: ${licenseError}.` : "Upgrade to Pro to unlock all features and support development."
|
|
759
|
-
})), !isPro && /*#__PURE__*/_jsxs(_Fragment, {
|
|
760
|
-
children: [renderSection("Pro Features", /*#__PURE__*/_jsx(NightCard, {
|
|
761
|
-
glass: true,
|
|
762
|
-
children: /*#__PURE__*/_jsx(NightRows, {
|
|
763
|
-
children: ["Advanced Settings", "Priority Support"].map(feature => /*#__PURE__*/_jsxs(View, {
|
|
764
|
-
style: styles.featureRow,
|
|
765
|
-
children: [/*#__PURE__*/_jsx(CheckCircle2, {
|
|
766
|
-
size: 16,
|
|
767
|
-
color: night.color.accent
|
|
768
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
769
|
-
style: styles.rowLabel,
|
|
770
|
-
children: feature
|
|
771
|
-
})]
|
|
772
|
-
}, feature))
|
|
773
|
-
})
|
|
774
|
-
})), /*#__PURE__*/_jsx(NightButton, {
|
|
775
|
-
label: "Upgrade to Pro",
|
|
776
|
-
variant: "primary",
|
|
777
|
-
size: "lg",
|
|
778
|
-
onPress: () => setShowLicenseModal(true)
|
|
779
|
-
})]
|
|
780
|
-
})]
|
|
781
|
-
})]
|
|
782
|
-
}), /*#__PURE__*/_jsx(LicenseEntryModal, {
|
|
783
|
-
visible: showLicenseModal,
|
|
784
|
-
onClose: () => setShowLicenseModal(false),
|
|
785
|
-
onSuccess: () => setShowLicenseModal(false)
|
|
786
|
-
})]
|
|
787
|
-
});
|
|
788
|
-
return /*#__PURE__*/_jsx(JsModal, {
|
|
789
|
-
visible: visible,
|
|
790
|
-
onClose: onClose,
|
|
791
|
-
zIndex: zIndex,
|
|
792
|
-
variant: "night",
|
|
793
|
-
header: {
|
|
794
|
-
showToggleButton: false,
|
|
795
|
-
customContent: /*#__PURE__*/_jsx(View, {
|
|
796
|
-
style: styles.headerRow,
|
|
797
|
-
children: /*#__PURE__*/_jsx(NightSegmented, {
|
|
798
|
-
glass: true,
|
|
799
|
-
tabs: [{
|
|
800
|
-
key: "floating",
|
|
801
|
-
label: "Floating"
|
|
802
|
-
}, {
|
|
803
|
-
key: "settings",
|
|
804
|
-
label: "Settings"
|
|
805
|
-
}, {
|
|
806
|
-
key: "pro",
|
|
807
|
-
label: "Pro"
|
|
808
|
-
}],
|
|
809
|
-
activeKey: activeTab,
|
|
810
|
-
onChange: tab => setActiveTab(tab)
|
|
811
|
-
})
|
|
812
|
-
})
|
|
813
|
-
},
|
|
814
|
-
initialMode: "bottomSheet",
|
|
815
|
-
onModeChange: handleModeChange,
|
|
816
|
-
persistenceKey: devToolsStorageKeys.settings.root(),
|
|
817
|
-
enablePersistence: true,
|
|
818
|
-
maxHeight: screenHeight - insets.top,
|
|
819
|
-
initialHeight: modalHeight,
|
|
820
|
-
initialFloatingPosition: {
|
|
821
|
-
x: (screenWidth - modalWidth) / 2,
|
|
822
|
-
y: insets.top + 20
|
|
823
|
-
},
|
|
824
|
-
enableGlitchEffects: true,
|
|
825
|
-
children: renderContent()
|
|
826
|
-
});
|
|
827
|
-
};
|
|
828
|
-
|
|
829
|
-
// Basic default settings for the hook (when apps are not available and no defaults configured)
|
|
830
|
-
const basicDefaultSettings = {
|
|
831
|
-
dialTools: {},
|
|
832
|
-
floatingTools: {
|
|
833
|
-
environment: false
|
|
834
|
-
},
|
|
835
|
-
globalSettings: {
|
|
836
|
-
enableSharedModalDimensions: false
|
|
837
|
-
}
|
|
838
|
-
};
|
|
839
|
-
|
|
840
|
-
/**
|
|
841
|
-
* Creates default settings from the team configuration arrays.
|
|
842
|
-
* Used by the hook when no stored settings exist.
|
|
843
|
-
*
|
|
844
|
-
* Note: This function doesn't have access to availableApps, so it can only
|
|
845
|
-
* enable tools that are explicitly listed in the config arrays.
|
|
846
|
-
* For auto-enabling all dial tools, the generateDefaultSettings function
|
|
847
|
-
* (which has access to availableApps) handles that case.
|
|
848
|
-
*/
|
|
849
|
-
const createDefaultsFromConfig = (defaultFloatingTools, defaultDialTools) => {
|
|
850
|
-
const floatingSet = new Set(defaultFloatingTools ?? []);
|
|
851
|
-
const dialSet = new Set(defaultDialTools ?? []);
|
|
852
|
-
|
|
853
|
-
// Build dial tools record from defaults
|
|
854
|
-
// Note: If no defaults specified, dialTools will be empty here
|
|
855
|
-
// but generateDefaultSettings will auto-enable all tools when it has access to availableApps
|
|
856
|
-
const dialTools = {};
|
|
857
|
-
for (const id of dialSet) {
|
|
858
|
-
dialTools[id] = true;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
// Build floating tools record from defaults
|
|
862
|
-
const floatingTools = {
|
|
863
|
-
environment: floatingSet.has('environment')
|
|
864
|
-
};
|
|
865
|
-
for (const id of floatingSet) {
|
|
866
|
-
if (id !== 'environment') {
|
|
867
|
-
floatingTools[id] = true;
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
return {
|
|
871
|
-
dialTools: enforceDialLimit(dialTools),
|
|
872
|
-
floatingTools,
|
|
873
|
-
globalSettings: {
|
|
874
|
-
enableSharedModalDimensions: false
|
|
875
|
-
}
|
|
876
|
-
};
|
|
877
|
-
};
|
|
878
|
-
|
|
879
|
-
/**
|
|
880
|
-
* Convenience hook for accessing persisted dev tools settings. Subscribes to the internal
|
|
881
|
-
* event bus so all surfaces stay in sync when the modal saves new preferences.
|
|
882
|
-
*
|
|
883
|
-
* When no saved settings exist, the hook uses the default configuration from
|
|
884
|
-
* the DefaultConfigProvider (if available) to determine initial tool states.
|
|
885
|
-
*/
|
|
886
|
-
/**
|
|
887
|
-
* Session cache for the values the settings modal loads asynchronously on mount.
|
|
888
|
-
*
|
|
889
|
-
* Without it every open renders three times: once with defaults, then again as
|
|
890
|
-
* each of the persisted reads (settings, active tab, storage backend) resolves —
|
|
891
|
-
* and each of those re-renders rebuilds the whole tool list. The values cannot
|
|
892
|
-
* change behind our back (writes go through this module, and settingsBus
|
|
893
|
-
* broadcasts them), so after the first open we can render the real values
|
|
894
|
-
* immediately and only re-render if a read actually came back different.
|
|
895
|
-
*/
|
|
896
|
-
let cachedSettings = null;
|
|
897
|
-
let cachedActiveTab = null;
|
|
898
|
-
let cachedStorageBackend = null;
|
|
899
|
-
|
|
900
|
-
/** Cheap structural equality — these objects are small and JSON-safe. */
|
|
901
|
-
const sameSettings = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
902
|
-
|
|
903
|
-
// Every surface that changes settings broadcasts on the bus, so listening here
|
|
904
|
-
// keeps the cache authoritative rather than merely last-seen — otherwise a
|
|
905
|
-
// change made while the modal was unmounted would show stale for one frame.
|
|
906
|
-
// Registered for the app's lifetime on purpose; there is nothing to unsubscribe.
|
|
907
|
-
settingsBus.addListener(payload => {
|
|
908
|
-
if (payload) cachedSettings = payload;
|
|
909
|
-
});
|
|
910
|
-
export const useDevToolsSettings = () => {
|
|
911
|
-
// Get team default configuration from context
|
|
912
|
-
const {
|
|
913
|
-
defaultFloatingTools,
|
|
914
|
-
defaultDialTools
|
|
915
|
-
} = useDefaultConfig();
|
|
916
|
-
|
|
917
|
-
// Compute the effective defaults based on team configuration
|
|
918
|
-
const effectiveDefaults = useMemo(() => {
|
|
919
|
-
if (!defaultFloatingTools && !defaultDialTools) {
|
|
920
|
-
return basicDefaultSettings;
|
|
921
|
-
}
|
|
922
|
-
return createDefaultsFromConfig(defaultFloatingTools, defaultDialTools);
|
|
923
|
-
}, [defaultFloatingTools, defaultDialTools]);
|
|
924
|
-
const [settings, setSettings] = useState(effectiveDefaults);
|
|
925
|
-
const loadSettings = useCallback(async () => {
|
|
926
|
-
try {
|
|
927
|
-
const savedSettings = await persistentStorage.getItem(STORAGE_KEY);
|
|
928
|
-
if (savedSettings) {
|
|
929
|
-
const parsed = JSON.parse(savedSettings);
|
|
930
|
-
const merged = mergeWithDefaults(effectiveDefaults, parsed);
|
|
931
|
-
setSettings(merged);
|
|
932
|
-
return;
|
|
933
|
-
}
|
|
934
|
-
// No saved settings - use the effective defaults (including team config)
|
|
935
|
-
setSettings(effectiveDefaults);
|
|
936
|
-
} catch (error) {
|
|
937
|
-
console.error("Failed to load dev tools settings:", error);
|
|
938
|
-
setSettings(effectiveDefaults);
|
|
939
|
-
}
|
|
940
|
-
}, [effectiveDefaults]);
|
|
941
|
-
useEffect(() => {
|
|
942
|
-
loadSettings();
|
|
943
|
-
// Subscribe to settings changes
|
|
944
|
-
const unsub = settingsBus.addListener(payload => {
|
|
945
|
-
try {
|
|
946
|
-
if (payload) {
|
|
947
|
-
setSettings(payload);
|
|
948
|
-
}
|
|
949
|
-
} catch (err) {
|
|
950
|
-
// Listener errors are intentionally swallowed to avoid breaking subscribers
|
|
951
|
-
}
|
|
952
|
-
});
|
|
953
|
-
return () => {
|
|
954
|
-
unsub();
|
|
955
|
-
};
|
|
956
|
-
}, [loadSettings]);
|
|
957
|
-
|
|
958
|
-
// Refresh settings when component using this hook becomes visible
|
|
959
|
-
const refreshSettings = useCallback(() => {
|
|
960
|
-
loadSettings();
|
|
961
|
-
}, [loadSettings]);
|
|
962
|
-
return {
|
|
963
|
-
settings,
|
|
964
|
-
refreshSettings
|
|
965
|
-
};
|
|
966
|
-
};
|
|
967
|
-
const styles = StyleSheet.create({
|
|
968
|
-
container: {
|
|
969
|
-
flex: 1,
|
|
970
|
-
// Transparent so JsModal's night star field shows through everything.
|
|
971
|
-
backgroundColor: "transparent"
|
|
972
|
-
},
|
|
973
|
-
// Header — the segmented control. The window controls are cleared
|
|
974
|
-
// vertically by JsModal's drag-indicator strip, so this runs full width.
|
|
975
|
-
headerRow: {
|
|
976
|
-
flexDirection: "row",
|
|
977
|
-
alignItems: "center",
|
|
978
|
-
paddingLeft: night.gutter,
|
|
979
|
-
paddingRight: night.gutter,
|
|
980
|
-
paddingBottom: 8
|
|
981
|
-
},
|
|
982
|
-
// Scroll content — labeled groups on a 24pt rhythm (Everlights create screen)
|
|
983
|
-
scrollContent: {
|
|
984
|
-
flex: 1
|
|
985
|
-
},
|
|
986
|
-
scrollContainer: {
|
|
987
|
-
paddingHorizontal: night.gutter,
|
|
988
|
-
paddingTop: 8,
|
|
989
|
-
paddingBottom: 24,
|
|
990
|
-
gap: night.groupGap
|
|
991
|
-
},
|
|
992
|
-
// Rows
|
|
993
|
-
toolRow: {
|
|
994
|
-
flexDirection: "row",
|
|
995
|
-
alignItems: "center",
|
|
996
|
-
gap: 12,
|
|
997
|
-
paddingHorizontal: night.rowPadH,
|
|
998
|
-
paddingVertical: 12
|
|
999
|
-
},
|
|
1000
|
-
settingRow: {
|
|
1001
|
-
flexDirection: "row",
|
|
1002
|
-
alignItems: "center",
|
|
1003
|
-
gap: 10,
|
|
1004
|
-
paddingHorizontal: night.rowPadH,
|
|
1005
|
-
paddingVertical: night.rowPadV,
|
|
1006
|
-
minHeight: 52
|
|
1007
|
-
},
|
|
1008
|
-
featureRow: {
|
|
1009
|
-
flexDirection: "row",
|
|
1010
|
-
alignItems: "center",
|
|
1011
|
-
gap: 10,
|
|
1012
|
-
paddingHorizontal: night.rowPadH,
|
|
1013
|
-
paddingVertical: 12
|
|
1014
|
-
},
|
|
1015
|
-
rowDisabled: {
|
|
1016
|
-
opacity: 0.4
|
|
1017
|
-
},
|
|
1018
|
-
toolIconSlot: {
|
|
1019
|
-
width: 24,
|
|
1020
|
-
alignItems: "center"
|
|
1021
|
-
},
|
|
1022
|
-
rowInfo: {
|
|
1023
|
-
flex: 1,
|
|
1024
|
-
gap: 2
|
|
1025
|
-
},
|
|
1026
|
-
rowLabel: {
|
|
1027
|
-
color: night.color.text,
|
|
1028
|
-
fontSize: night.font.row,
|
|
1029
|
-
fontWeight: "500"
|
|
1030
|
-
},
|
|
1031
|
-
rowCaption: {
|
|
1032
|
-
color: night.color.textTertiary,
|
|
1033
|
-
fontSize: night.font.label
|
|
1034
|
-
},
|
|
1035
|
-
// Expanded body under a global-setting row
|
|
1036
|
-
settingRowBody: {
|
|
1037
|
-
paddingHorizontal: night.rowPadH,
|
|
1038
|
-
paddingBottom: night.rowPadV,
|
|
1039
|
-
gap: 6
|
|
1040
|
-
},
|
|
1041
|
-
settingRowBodyText: {
|
|
1042
|
-
color: night.color.textSecondary,
|
|
1043
|
-
fontSize: night.font.caption,
|
|
1044
|
-
lineHeight: night.font.caption * 1.45
|
|
1045
|
-
},
|
|
1046
|
-
settingRowBodyHint: {
|
|
1047
|
-
color: night.color.textTertiary,
|
|
1048
|
-
fontSize: night.font.caption,
|
|
1049
|
-
lineHeight: night.font.caption * 1.45
|
|
1050
|
-
},
|
|
1051
|
-
bodyWarning: {
|
|
1052
|
-
color: night.color.warning
|
|
1053
|
-
},
|
|
1054
|
-
bodyDanger: {
|
|
1055
|
-
color: night.color.danger
|
|
1056
|
-
},
|
|
1057
|
-
// Buttons that live inside a card
|
|
1058
|
-
cardButtonWrap: {
|
|
1059
|
-
paddingHorizontal: night.cardPad,
|
|
1060
|
-
paddingTop: 10,
|
|
1061
|
-
paddingBottom: night.cardPad
|
|
1062
|
-
},
|
|
1063
|
-
// Storage expanded key list
|
|
1064
|
-
storageExpanded: {
|
|
1065
|
-
paddingHorizontal: night.rowPadH,
|
|
1066
|
-
paddingVertical: 12,
|
|
1067
|
-
gap: 8
|
|
1068
|
-
},
|
|
1069
|
-
storageExpandedHeader: {
|
|
1070
|
-
flexDirection: "row",
|
|
1071
|
-
alignItems: "center"
|
|
1072
|
-
},
|
|
1073
|
-
storageExpandedTitle: {
|
|
1074
|
-
flex: 1,
|
|
1075
|
-
color: night.color.textSecondary,
|
|
1076
|
-
fontSize: night.font.micro,
|
|
1077
|
-
fontWeight: "600",
|
|
1078
|
-
letterSpacing: 0.5
|
|
1079
|
-
},
|
|
1080
|
-
storageExpandedCount: {
|
|
1081
|
-
color: night.color.textTertiary,
|
|
1082
|
-
fontSize: night.font.micro
|
|
1083
|
-
},
|
|
1084
|
-
storageKeysList: {
|
|
1085
|
-
maxHeight: 150
|
|
1086
|
-
},
|
|
1087
|
-
storageKeyItem: {
|
|
1088
|
-
backgroundColor: night.color.glassElevated,
|
|
1089
|
-
borderRadius: night.radius.segment,
|
|
1090
|
-
paddingHorizontal: 10,
|
|
1091
|
-
paddingVertical: 7,
|
|
1092
|
-
marginBottom: 4
|
|
1093
|
-
},
|
|
1094
|
-
storageKeyText: {
|
|
1095
|
-
color: night.color.textSecondary,
|
|
1096
|
-
fontSize: night.font.micro,
|
|
1097
|
-
fontFamily: monoFont
|
|
1098
|
-
},
|
|
1099
|
-
storageKeyEmpty: {
|
|
1100
|
-
color: night.color.textTertiary,
|
|
1101
|
-
fontSize: night.font.caption,
|
|
1102
|
-
fontStyle: "italic"
|
|
1103
|
-
},
|
|
1104
|
-
// Footnote inline code
|
|
1105
|
-
footnoteMono: {
|
|
1106
|
-
fontFamily: monoFont,
|
|
1107
|
-
fontSize: night.font.label,
|
|
1108
|
-
color: night.color.textSecondary
|
|
1109
|
-
}
|
|
1110
|
-
});
|
|
1
|
+
"use strict";import{useState as m,useEffect as L,useCallback as K,useMemo as W}from"react";import{View as y,Text as u,StyleSheet as Xe,ScrollView as me,TouchableOpacity as j,Dimensions as Se,Platform as Ze}from"react-native";import{settingsBus as A}from"./settingsBus.js";import{useDesktopSyncStatus as et}from"./useDesktopSyncStatus";import{SentryIcon as tt,WifiCircuitIcon as ot,ImageOverlayIcon as nt,RenderCountIcon as rt,Info as Q,Layers as st,ChevronRightIcon as be,ChevronDown as we,getStorageBackendType as at,getUseLicense as it,persistentStorage as D,Trash2 as lt,CheckCircle2 as Te,LicenseEntryModal as ct,BackgroundSwitcher as dt,night as o,NightCard as R,NightRows as X,NightSectionLabel as gt,NightFootnote as ve,NightSwitch as xe,NightButton as ze,NightSegmented as ut,NightBadge as v,NightSeparator as ht,monoFont as Ie}from"@buoy-gg/shared-ui";import{EnvIcon as Ce,StorageIcon as yt,RoutesIcon as ft,NetworkIcon as pt,QueryIcon as mt,HighlighterIcon as St,ReduxIcon as bt,EventsIcon as wt}from"@buoy-gg/floating-tools-core";import{useDefaultConfig as Le}from"./DefaultConfigContext.js";import{renderAppIcon as Tt}from"./AppIcon";import{JsModal as vt,devToolsStorageKeys as Z}from"@buoy-gg/shared-ui";import{useSafeAreaInsets as xt}from"@buoy-gg/shared-ui";import{jsx as e,jsxs as a,Fragment as k}from"react/jsx-runtime";const N="@react_buoy_dev_tools_settings";const zt=6;const V=d=>{let g=zt;const i={};for(const[f,l]of Object.entries(d)){if(l&&g>0){i[f]=true;g-=1}else{i[f]=false}}return i};const It=(d,g)=>{const{userStatus:i,environment:f,...l}=d;const h=g?Object.entries(l).filter(([p])=>g.includes(p)):Object.entries(l);return{...Object.fromEntries(h),environment:f??false}};const De=(d,g,i)=>{if(!g)return d;const f={...d.dialTools,...g.dialTools??{}};const l=i?.allowedDialKeys?Object.entries(f).filter(([p])=>i.allowedDialKeys?.includes(p)):Object.entries(f);const h=V(Object.fromEntries(l));return{dialTools:h,floatingTools:It({...d.floatingTools,...g.floatingTools??{}},i?.allowedFloatingKeys),globalSettings:{enableSharedModalDimensions:false,expandableWindowControls:true,...g.globalSettings??{}}}};const Ct=(d=[],g,i)=>{const f={};const l={};const h=new Set(g??[]);const p=new Set(i??[]);const S=!i||i.length===0;for(const x of d){const{id:c,slot:I="both"}=x;if(I==="dial"||I==="both"){f[c]=S?true:p.has(c)}if(I==="row"||I==="both"){l[c]=h.has(c)}}return{dialTools:V(f),floatingTools:{...l,environment:h.has("environment")},globalSettings:{enableSharedModalDimensions:false,expandableWindowControls:true}}};export const DevToolsSettingsModal=({visible:d,onClose:g,onSettingsChange:i,initialSettings:f,availableApps:l=[],storage:h=D,zIndex:p})=>{const{defaultFloatingTools:S,defaultDialTools:x}=Le();const c=W(()=>Ct(l,S,x),[l,S,x]);const I=W(()=>Object.keys(c.dialTools),[c]);const te=W(()=>Object.keys(c.floatingTools).filter(t=>t!=="environment"),[c]);const[z,E]=m(f||P||c);const[B,oe]=m(ee??"floating");const[ne,Ee]=m(ee!==null);const[_,Fe]=m(new Set);const[Oe,J]=m(false);const[ke,Pe]=m(false);L(()=>{const t=requestAnimationFrame(()=>Pe(true));return()=>cancelAnimationFrame(t)},[]);const b=et();L(()=>{const t=async()=>{try{const r=await D.getItem(Z.settings.activeTab());if(r&&["floating","settings","pro"].includes(r)){const s=r;ee=s;oe(T=>T===s?T:s)}}catch(r){}finally{Ee(true)}};t()},[]);L(()=>{if(!ne)return;D.setItem(Z.settings.activeTab(),B).catch(t=>{console.warn("Failed to save settings active tab:",t)})},[B,ne]);const He=it();const H=He?.();const F=H?.isPro??false;const U=!F&&H?.licenseKey?H.error:null;const Me=H?.errorKind==="transport";const[w,Ke]=m(Re);const[G,re]=m(false);const[q,Y]=m(false);const[O,We]=m(false);const[$,se]=m([]);const[ae,ie]=m(false);const le=xt();const ce=Se.get("window").height;const de=Se.get("window").width;const je=Math.floor(ce*.33);const Ae=Math.min(de-32,400);const ge=K(async()=>{try{const t=await h.getItem(N);if(t){const r=JSON.parse(t);const s=De(c,r,{allowedDialKeys:I,allowedFloatingKeys:te});P=s;E(T=>Be(T,s)?T:s);return}P=c;E(r=>Be(r,c)?r:c)}catch(t){console.error("Failed to load dev tools settings:",t);E(c)}},[c,I,te,h]);L(()=>{ge()},[ge]);L(()=>{at().then(t=>{Re=t;Ke(r=>r===t?r:t)})},[]);L(()=>{if(O){ie(true);D.getAllKeys().then(t=>{se(t.sort())}).catch(()=>{se([])}).finally(()=>{ie(false)})}},[O]);const ue=async t=>{try{const r={...t,dialTools:V(t.dialTools)};await h.setItem(N,JSON.stringify(r));P=r;E(r);i?.(r);A.emit(r)}catch(r){console.error("Failed to save dev tools settings:",r)}};const Ne=t=>{const r={...z,floatingTools:{...z.floatingTools,[t]:!z.floatingTools[t]}};ue(r)};const Ve=t=>{const r={...z,globalSettings:{...z.globalSettings,[t]:!z.globalSettings?.[t]}};ue(r)};const _e=async()=>{re(true);Y(false);try{await D.clear();Y(true);E(c);if(h!==D)await h.setItem(N,JSON.stringify(c));i?.(c);A.emit(c);setTimeout(()=>Y(false),2e3)}catch(t){console.error("Failed to clear storage:",t)}finally{re(false)}};const Je=K(t=>{},[]);const Ue=t=>{const r=l.find(s=>s.id===t);if(r?.description){return r.description}if(t==="environment"){return"Environment badge."}return""};const he=t=>{if(t==="environment"){return"Env Badge"}const r=l.find(s=>s.id===t);return r?.name??t.toUpperCase().replace(/_/g," ")};const Ge=t=>{if(!ke)return null;switch(t){case"query":return e(mt,{size:18});case"env":return e(Ce,{size:18});case"sentry":return e(tt,{size:18,color:"#9945FF"});case"storage":return e(yt,{size:18,bgColor:o.color.surface});case"wifi":case"query-wifi-toggle":return e(ot,{size:18,colorPreset:"green",strength:4,noBackground:true});case"route-events":return e(ft,{size:18,bgColor:o.color.surface});case"network":return e(pt,{size:18});case"environment":return e(Ce,{size:18});case"debug-borders":return e(st,{size:18,color:o.color.accent});case"highlight-updates":return e(rt,{size:18,color:o.color.accent});case"highlight-updates-modal":return e(St,{size:18});case"redux":return e(bt,{size:18});case"image-overlay":return e(nt,{size:18});case"events":return e(wt,{size:18});case"zustand":try{const{ZustandIcon:r}=require("@buoy-gg/zustand");return e(r,{size:18})}catch{return e(Q,{size:18,color:o.color.textSecondary})}case"jotai":try{const{JotaiIcon:r}=require("@buoy-gg/jotai");return e(r,{size:18})}catch{return e(Q,{size:18,color:o.color.textSecondary})}default:{const r=l.find(s=>s.id===t);if(r?.icon!=null){return Tt(r.icon,{slot:"row",size:18,color:r.color})}return e(Q,{size:18,color:o.color.textSecondary})}}};const qe=(t,r,s,T)=>{const M=Ue(t);return a(j,{activeOpacity:s?1:.7,onPress:()=>!s&&T(),accessibilityLabel:he(t),accessibilityRole:"button",style:[n.toolRow,s&&n.rowDisabled],children:[e(y,{style:n.toolIconSlot,children:Ge(t)}),a(y,{style:n.rowInfo,children:[a(u,{style:n.rowLabel,children:[he(t),s?" \xB7 max 6":""]}),M?e(u,{style:n.rowCaption,numberOfLines:1,children:M}):null]}),e(xe,{value:r,disabled:s,onValueChange:()=>T()})]},t)};const ye=t=>{Fe(r=>{const s=new Set(r);if(s.has(t)){s.delete(t)}else{s.add(t)}return s})};const fe=(t,r,s,T,M)=>{const Qe=z.globalSettings?.[t]??false;const pe=_.has(t);return a(y,{children:[a(j,{activeOpacity:.7,onPress:()=>ye(t),accessibilityLabel:r,style:n.settingRow,children:[a(y,{style:n.rowInfo,children:[e(u,{style:n.rowLabel,children:r}),e(u,{style:n.rowCaption,numberOfLines:pe?0:1,children:s})]}),e(xe,{value:Qe,onValueChange:()=>Ve(t)})]}),pe&&a(y,{style:n.settingRowBody,children:[e(u,{style:n.settingRowBodyText,children:T}),e(u,{style:n.settingRowBodyHint,children:M})]})]},t)};const C=(t,r,s)=>a(y,{children:[e(gt,{children:t}),r,s]});const Ye=w==="filesystem"?e(v,{label:"File System",tone:"accent"}):w==="asyncstorage"?e(v,{label:"Async Storage",tone:"warning"}):w==="localstorage"?e(v,{label:"Local Storage",tone:"accent"}):w==="memory"?e(v,{label:"Memory",tone:"danger"}):e(v,{label:"Loading\u2026",tone:"neutral"});const $e=()=>a(y,{style:n.container,children:[a(me,{style:n.scrollContent,showsVerticalScrollIndicator:false,contentContainerStyle:n.scrollContainer,children:[B==="floating"&&e(k,{children:C("Floating Tools",e(R,{glass:true,children:e(X,{children:Object.entries(z.floatingTools).map(([t,r])=>qe(t,r,false,()=>Ne(t)))})}),e(ve,{children:"Tools switched on here appear in the floating bubble row."}))}),B==="settings"&&a(k,{children:[C("Background",e(R,{glass:true,padded:true,children:e(dt,{embedded:true})})),C("Storage",a(R,{glass:true,children:[a(j,{activeOpacity:.7,onPress:()=>We(!O),accessibilityLabel:"Storage Type",style:n.settingRow,children:[e(y,{style:n.rowInfo,children:e(u,{style:n.rowLabel,children:"Storage Type"})}),Ye,O?e(we,{size:16,color:o.color.textTertiary}):e(be,{size:16,color:o.color.textTertiary})]}),O&&a(k,{children:[a(y,{style:n.settingRowBody,children:[e(u,{style:n.settingRowBodyText,children:w==="filesystem"?"Settings persist independently and survive AsyncStorage.clear() calls during logout.":w==="asyncstorage"?"Settings may be lost if AsyncStorage is cleared during logout.":w==="localstorage"?"Settings are saved in this browser. Clearing site data removes them.":w==="memory"?"Settings are stored in memory only and will be lost on app restart.":"Checking storage backend\u2026"}),w==="asyncstorage"&&e(u,{style:[n.settingRowBodyText,n.bodyWarning],children:"Install expo-file-system to persist settings through logout flows."}),w==="memory"&&e(u,{style:[n.settingRowBodyText,n.bodyDanger],children:"No persistent storage available \u2014 install expo-file-system (best) or @react-native-async-storage/async-storage."})]}),e(ht,{}),a(y,{style:n.storageExpanded,children:[a(y,{style:n.storageExpandedHeader,children:[e(u,{style:n.storageExpandedTitle,children:"SAVED SETTINGS"}),e(u,{style:n.storageExpandedCount,children:ae?"\u2026":`${$.length} keys`})]}),ae?e(u,{style:n.storageKeyEmpty,children:"Loading\u2026"}):$.length===0?e(u,{style:n.storageKeyEmpty,children:"No settings saved yet"}):e(me,{style:n.storageKeysList,nestedScrollEnabled:true,showsVerticalScrollIndicator:true,children:$.map(t=>e(y,{style:n.storageKeyItem,children:e(u,{style:n.storageKeyText,numberOfLines:1,children:t.replace("@react_buoy_","")})},t))})]}),e(y,{style:n.cardButtonWrap,children:e(ze,{label:q?"Cleared":G?"Clearing\u2026":"Clear All Settings",variant:q?"secondary":"destructive",size:"md",disabled:G,onPress:_e,icon:q?e(Te,{size:15,color:o.color.accent}):e(lt,{size:15,color:G?o.color.textTertiary:o.color.danger})})})]})]})),b&&b.state!=="idle"&&C("Desktop Sync",a(R,{glass:true,children:[a(j,{activeOpacity:.7,onPress:()=>ye("desktopSync"),accessibilityLabel:"Buoy Desktop",style:n.settingRow,children:[e(y,{style:n.rowInfo,children:e(u,{style:n.rowLabel,children:"Buoy Desktop"})}),b.state==="connected"?e(v,{label:"Connected",tone:"accent"}):b.state==="connecting"?e(v,{label:"Connecting\u2026",tone:"neutral"}):e(v,{label:"Retrying",tone:"warning"}),_.has("desktopSync")?e(we,{size:16,color:o.color.textTertiary}):e(be,{size:16,color:o.color.textTertiary})]}),_.has("desktopSync")&&a(y,{style:n.settingRowBody,children:[a(u,{style:n.settingRowBodyText,children:[b.state==="connected"?"Streaming tool data to Buoy Desktop at ":"Trying to reach Buoy Desktop at ",e(u,{style:n.footnoteMono,children:b.targetURL??"(unknown URL)"}),b.state!=="connected"&&" \u2014 is the desktop app running? Override with the externalSync socketURL option if it runs elsewhere."]}),b.state==="retrying"&&b.lastError&&a(u,{style:[n.settingRowBodyText,n.bodyWarning],children:["Last error: ",b.lastError]})]})]})),C("Modal Behavior",e(R,{glass:true,children:a(X,{children:[fe("enableSharedModalDimensions","Shared Modal Size","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."),Ze.OS!=="web"?fe("expandableWindowControls","Expand Controls","iPad-style expandable window buttons","When enabled, the window control buttons (minimize, toggle mode, close) start as small dots and expand into larger, easy-to-tap buttons when pressed. When disabled, buttons are directly tappable at their small size.","Keep ON for touch devices where the small buttons are hard to press. Turn OFF if you prefer direct single-tap access."):null]})}))]}),B==="pro"&&a(k,{children:[C("License",e(R,{glass:true,children:a(y,{style:n.settingRow,children:[e(y,{style:n.rowInfo,children:e(u,{style:n.rowLabel,children:"License Status"})}),e(y,{testID:"buoy-settings-license-status",accessibilityLabel:F?"License: Active":"License: Free",children:F?e(v,{label:"Active",tone:"accent"}):e(v,{label:"Free",tone:"neutral"})})]})}),e(ve,{children:F?"You have full access to all Buoy DevTools features.":U?Me?`Couldn't reach the license server (${U}). A VPN or corporate proxy can block buoy.gg \u2014 turn it off and reload. Your key hasn't been rejected.`:`License check failed: ${U}.`:"Upgrade to Pro to unlock all features and support development."})),!F&&a(k,{children:[C("Pro Features",e(R,{glass:true,children:e(X,{children:["Advanced Settings","Priority Support"].map(t=>a(y,{style:n.featureRow,children:[e(Te,{size:16,color:o.color.accent}),e(u,{style:n.rowLabel,children:t})]},t))})})),e(ze,{label:"Upgrade to Pro",variant:"primary",size:"lg",onPress:()=>J(true)})]})]})]}),e(ct,{visible:Oe,onClose:()=>J(false),onSuccess:()=>J(false)})]});return e(vt,{visible:d,onClose:g,zIndex:p,variant:"night",header:{showToggleButton:false,customContent:e(y,{style:n.headerRow,children:e(ut,{glass:true,tabs:[{key:"floating",label:"Floating"},{key:"settings",label:"Settings"},{key:"pro",label:"Pro"}],activeKey:B,onChange:t=>oe(t)})})},initialMode:"bottomSheet",onModeChange:Je,persistenceKey:Z.settings.root(),enablePersistence:true,maxHeight:ce-le.top,initialHeight:je,initialFloatingPosition:{x:(de-Ae)/2,y:le.top+20},enableGlitchEffects:true,children:$e()})};const Lt={dialTools:{},floatingTools:{environment:false},globalSettings:{enableSharedModalDimensions:false}};const Dt=(d,g)=>{const i=new Set(d??[]);const f=new Set(g??[]);const l={};for(const p of f){l[p]=true}const h={environment:i.has("environment")};for(const p of i){if(p!=="environment"){h[p]=true}}return{dialTools:V(l),floatingTools:h,globalSettings:{enableSharedModalDimensions:false}}};let P=null;let ee=null;let Re=null;const Be=(d,g)=>JSON.stringify(d)===JSON.stringify(g);A.addListener(d=>{if(d)P=d});export const useDevToolsSettings=()=>{const{defaultFloatingTools:d,defaultDialTools:g}=Le();const i=W(()=>{if(!d&&!g){return Lt}return Dt(d,g)},[d,g]);const[f,l]=m(i);const h=K(async()=>{try{const S=await D.getItem(N);if(S){const x=JSON.parse(S);const c=De(i,x);l(c);return}l(i)}catch(S){console.error("Failed to load dev tools settings:",S);l(i)}},[i]);L(()=>{h();const S=A.addListener(x=>{try{if(x){l(x)}}catch(c){}});return()=>{S()}},[h]);const p=K(()=>{h()},[h]);return{settings:f,refreshSettings:p}};const n=Xe.create({container:{flex:1,backgroundColor:"transparent"},headerRow:{flexDirection:"row",alignItems:"center",paddingLeft:o.gutter,paddingRight:o.gutter,paddingBottom:8},scrollContent:{flex:1},scrollContainer:{paddingHorizontal:o.gutter,paddingTop:8,paddingBottom:24,gap:o.groupGap},toolRow:{flexDirection:"row",alignItems:"center",gap:12,paddingHorizontal:o.rowPadH,paddingVertical:12},settingRow:{flexDirection:"row",alignItems:"center",gap:10,paddingHorizontal:o.rowPadH,paddingVertical:o.rowPadV,minHeight:52},featureRow:{flexDirection:"row",alignItems:"center",gap:10,paddingHorizontal:o.rowPadH,paddingVertical:12},rowDisabled:{opacity:.4},toolIconSlot:{width:24,alignItems:"center"},rowInfo:{flex:1,gap:2},rowLabel:{color:o.color.text,fontSize:o.font.row,fontWeight:"500"},rowCaption:{color:o.color.textTertiary,fontSize:o.font.label},settingRowBody:{paddingHorizontal:o.rowPadH,paddingBottom:o.rowPadV,gap:6},settingRowBodyText:{color:o.color.textSecondary,fontSize:o.font.caption,lineHeight:o.font.caption*1.45},settingRowBodyHint:{color:o.color.textTertiary,fontSize:o.font.caption,lineHeight:o.font.caption*1.45},bodyWarning:{color:o.color.warning},bodyDanger:{color:o.color.danger},cardButtonWrap:{paddingHorizontal:o.cardPad,paddingTop:10,paddingBottom:o.cardPad},storageExpanded:{paddingHorizontal:o.rowPadH,paddingVertical:12,gap:8},storageExpandedHeader:{flexDirection:"row",alignItems:"center"},storageExpandedTitle:{flex:1,color:o.color.textSecondary,fontSize:o.font.micro,fontWeight:"600",letterSpacing:.5},storageExpandedCount:{color:o.color.textTertiary,fontSize:o.font.micro},storageKeysList:{maxHeight:150},storageKeyItem:{backgroundColor:o.color.glassElevated,borderRadius:o.radius.segment,paddingHorizontal:10,paddingVertical:7,marginBottom:4},storageKeyText:{color:o.color.textSecondary,fontSize:o.font.micro,fontFamily:Ie},storageKeyEmpty:{color:o.color.textTertiary,fontSize:o.font.caption,fontStyle:"italic"},footnoteMono:{fontFamily:Ie,fontSize:o.font.label,color:o.color.textSecondary}});
|