@buoy-gg/core 7.0.36 → 7.0.38
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 +57 -69
- package/bin/buoy.js +71 -20
- package/lib/commonjs/Buoy.js +3 -16
- package/lib/commonjs/floatingMenu/AppHost.js +1 -1
- package/lib/commonjs/floatingMenu/AppHost.web.js +1 -1
- package/lib/commonjs/floatingMenu/AppIcon.web.js +53 -0
- package/lib/commonjs/floatingMenu/DevToolsSettingsModal.js +12 -5
- package/lib/commonjs/floatingMenu/DevToolsSettingsModal.web.js +14 -34
- package/lib/commonjs/floatingMenu/FloatingDevTools.js +32 -48
- package/lib/commonjs/floatingMenu/FloatingDevTools.web.js +24 -4
- package/lib/commonjs/floatingMenu/FloatingMenu.js +1 -1
- package/lib/commonjs/floatingMenu/ToolHost.js +1 -0
- package/lib/commonjs/floatingMenu/autoExternalSync.js +3 -2
- package/lib/commonjs/floatingMenu/dial/DialDevTools.js +43 -10
- package/lib/commonjs/floatingMenu/dial/DialDevTools.web.js +18 -5
- package/lib/commonjs/floatingMenu/dial/dialUsageStore.js +13 -17
- package/lib/commonjs/floatingMenu/dial/onboardingConstants.js +37 -65
- package/lib/commonjs/floatingMenu/dial/onboardingConstants.web.js +37 -29
- package/lib/commonjs/floatingMenu/floatingTools.js +23 -889
- package/lib/commonjs/floatingMenu/floatingTools.web.js +24 -514
- package/lib/commonjs/floatingMenu/optionalRequire.js +4 -0
- package/lib/commonjs/telemetry/installPing.js +7 -0
- package/lib/commonjs/version.js +1 -1
- package/lib/module/Buoy.js +3 -16
- package/lib/module/floatingMenu/AppHost.js +1 -1
- package/lib/module/floatingMenu/AppHost.web.js +1 -1
- package/lib/module/floatingMenu/AppIcon.web.js +47 -0
- package/lib/module/floatingMenu/DevToolsSettingsModal.js +13 -6
- package/lib/module/floatingMenu/DevToolsSettingsModal.web.js +15 -35
- package/lib/module/floatingMenu/FloatingDevTools.js +35 -49
- package/lib/module/floatingMenu/FloatingDevTools.web.js +24 -3
- package/lib/module/floatingMenu/FloatingMenu.js +1 -1
- package/lib/module/floatingMenu/ToolHost.js +1 -0
- package/lib/module/floatingMenu/autoExternalSync.js +3 -2
- package/lib/module/floatingMenu/dial/DialDevTools.js +43 -10
- package/lib/module/floatingMenu/dial/DialDevTools.web.js +18 -5
- package/lib/module/floatingMenu/dial/dialUsageStore.js +14 -18
- package/lib/module/floatingMenu/dial/onboardingConstants.js +1 -62
- package/lib/module/floatingMenu/dial/onboardingConstants.web.js +1 -25
- package/lib/module/floatingMenu/floatingTools.js +15 -890
- package/lib/module/floatingMenu/floatingTools.web.js +1 -517
- package/lib/module/floatingMenu/optionalRequire.js +4 -0
- package/lib/module/telemetry/installPing.js +7 -0
- package/lib/module/version.js +1 -1
- package/lib/typescript/commonjs/Buoy.d.ts +14 -3
- package/lib/typescript/commonjs/Buoy.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/AppIcon.web.d.ts +14 -0
- package/lib/typescript/commonjs/floatingMenu/AppIcon.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts +4 -14
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts +4 -19
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/ToolHost.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts +3 -4
- package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/dial/dialUsageStore.d.ts +3 -15
- package/lib/typescript/commonjs/floatingMenu/dial/dialUsageStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts +1 -29
- package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.web.d.ts +1 -12
- package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts +6 -55
- package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/floatingTools.web.d.ts +3 -53
- package/lib/typescript/commonjs/floatingMenu/floatingTools.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/optionalRequire.d.ts.map +1 -1
- package/lib/typescript/commonjs/telemetry/installPing.d.ts +8 -1
- package/lib/typescript/commonjs/telemetry/installPing.d.ts.map +1 -1
- package/lib/typescript/commonjs/version.d.ts +1 -1
- package/lib/typescript/module/Buoy.d.ts +14 -3
- package/lib/typescript/module/Buoy.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/AppIcon.web.d.ts +14 -0
- package/lib/typescript/module/floatingMenu/AppIcon.web.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.web.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts +4 -14
- package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts +4 -19
- package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/ToolHost.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts +3 -4
- package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/dial/DialDevTools.web.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/dial/dialUsageStore.d.ts +3 -15
- package/lib/typescript/module/floatingMenu/dial/dialUsageStore.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts +1 -29
- package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/dial/onboardingConstants.web.d.ts +1 -12
- package/lib/typescript/module/floatingMenu/dial/onboardingConstants.web.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/floatingTools.d.ts +6 -55
- package/lib/typescript/module/floatingMenu/floatingTools.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/floatingTools.web.d.ts +3 -53
- package/lib/typescript/module/floatingMenu/floatingTools.web.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/optionalRequire.d.ts.map +1 -1
- package/lib/typescript/module/telemetry/installPing.d.ts +8 -1
- package/lib/typescript/module/telemetry/installPing.d.ts.map +1 -1
- package/lib/typescript/module/version.d.ts +1 -1
- package/package.json +6 -6
|
@@ -4,10 +4,10 @@ import { useState, useEffect, useCallback, useMemo } from "react";
|
|
|
4
4
|
import { View, Text, StyleSheet, ScrollView, TouchableOpacity, Dimensions, Platform } from "react-native";
|
|
5
5
|
import { settingsBus } from "./settingsBus.js";
|
|
6
6
|
import { useDesktopSyncStatus } from "./useDesktopSyncStatus.js";
|
|
7
|
-
import {
|
|
7
|
+
import { SentryIcon, WifiCircuitIcon, ImageOverlayIcon, RenderCountIcon, Info, Layers, ChevronRightIcon, ChevronDown, getStorageBackendType, persistentStorage, Trash2, CheckCircle2, LicenseEntryModal, BackgroundSwitcher, night, NightCard, NightRows, NightSectionLabel, NightFootnote, NightSwitch, NightButton, NightSegmented, NightBadge, NightSeparator, monoFont } from "@buoy-gg/shared-ui";
|
|
8
8
|
import { EnvIcon, StorageIcon, RoutesIcon, NetworkIcon, QueryIcon, HighlighterIcon, ReduxIcon, EventsIcon } from "@buoy-gg/floating-tools-core";
|
|
9
9
|
import { useDefaultConfig } from "./DefaultConfigContext.js";
|
|
10
|
-
import { renderAppIcon } from "./AppIcon
|
|
10
|
+
import { renderAppIcon } from "./AppIcon";
|
|
11
11
|
import { JsModal, devToolsStorageKeys } from "@buoy-gg/shared-ui";
|
|
12
12
|
import { useSafeAreaInsets } from "@buoy-gg/shared-ui";
|
|
13
13
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
@@ -374,10 +374,9 @@ export const DevToolsSettingsModal = ({
|
|
|
374
374
|
size: 18
|
|
375
375
|
});
|
|
376
376
|
case "sentry":
|
|
377
|
-
return /*#__PURE__*/_jsx(
|
|
377
|
+
return /*#__PURE__*/_jsx(SentryIcon, {
|
|
378
378
|
size: 18,
|
|
379
|
-
|
|
380
|
-
noBackground: true
|
|
379
|
+
color: "#9945FF"
|
|
381
380
|
});
|
|
382
381
|
case "storage":
|
|
383
382
|
return /*#__PURE__*/_jsx(StorageIcon, {
|
|
@@ -595,6 +594,7 @@ export const DevToolsSettingsModal = ({
|
|
|
595
594
|
contentContainerStyle: styles.scrollContainer,
|
|
596
595
|
children: [activeTab === "floating" && /*#__PURE__*/_jsx(_Fragment, {
|
|
597
596
|
children: renderSection("Floating Tools", /*#__PURE__*/_jsx(NightCard, {
|
|
597
|
+
glass: true,
|
|
598
598
|
children: /*#__PURE__*/_jsx(NightRows, {
|
|
599
599
|
children: Object.entries(settings.floatingTools).map(([key, value]) => renderToolRow(key, value, false, () => toggleFloatingTool(key)))
|
|
600
600
|
})
|
|
@@ -603,11 +603,13 @@ export const DevToolsSettingsModal = ({
|
|
|
603
603
|
}))
|
|
604
604
|
}), activeTab === "settings" && /*#__PURE__*/_jsxs(_Fragment, {
|
|
605
605
|
children: [renderSection("Background", /*#__PURE__*/_jsx(NightCard, {
|
|
606
|
+
glass: true,
|
|
606
607
|
padded: true,
|
|
607
608
|
children: /*#__PURE__*/_jsx(BackgroundSwitcher, {
|
|
608
609
|
embedded: true
|
|
609
610
|
})
|
|
610
611
|
})), renderSection("Storage", /*#__PURE__*/_jsxs(NightCard, {
|
|
612
|
+
glass: true,
|
|
611
613
|
children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
612
614
|
activeOpacity: 0.7,
|
|
613
615
|
onPress: () => setIsStorageExpanded(!isStorageExpanded),
|
|
@@ -688,6 +690,7 @@ export const DevToolsSettingsModal = ({
|
|
|
688
690
|
})]
|
|
689
691
|
})]
|
|
690
692
|
})), desktopSyncStatus && desktopSyncStatus.state !== "idle" && renderSection("Desktop Sync", /*#__PURE__*/_jsxs(NightCard, {
|
|
693
|
+
glass: true,
|
|
691
694
|
children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
692
695
|
activeOpacity: 0.7,
|
|
693
696
|
onPress: () => toggleSettingExpanded("desktopSync"),
|
|
@@ -729,12 +732,14 @@ export const DevToolsSettingsModal = ({
|
|
|
729
732
|
})]
|
|
730
733
|
})]
|
|
731
734
|
})), renderSection("Modal Behavior", /*#__PURE__*/_jsx(NightCard, {
|
|
735
|
+
glass: true,
|
|
732
736
|
children: /*#__PURE__*/_jsxs(NightRows, {
|
|
733
737
|
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]
|
|
734
738
|
})
|
|
735
739
|
}))]
|
|
736
740
|
}), activeTab === "pro" && /*#__PURE__*/_jsxs(_Fragment, {
|
|
737
741
|
children: [renderSection("License", /*#__PURE__*/_jsx(NightCard, {
|
|
742
|
+
glass: true,
|
|
738
743
|
children: /*#__PURE__*/_jsxs(View, {
|
|
739
744
|
style: styles.settingRow,
|
|
740
745
|
children: [/*#__PURE__*/_jsx(View, {
|
|
@@ -755,6 +760,7 @@ export const DevToolsSettingsModal = ({
|
|
|
755
760
|
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 api.keygen.sh — 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."
|
|
756
761
|
})), !isPro && /*#__PURE__*/_jsxs(_Fragment, {
|
|
757
762
|
children: [renderSection("Pro Features", /*#__PURE__*/_jsx(NightCard, {
|
|
763
|
+
glass: true,
|
|
758
764
|
children: /*#__PURE__*/_jsx(NightRows, {
|
|
759
765
|
children: ["Advanced Settings", "Priority Support"].map(feature => /*#__PURE__*/_jsxs(View, {
|
|
760
766
|
style: styles.featureRow,
|
|
@@ -790,6 +796,7 @@ export const DevToolsSettingsModal = ({
|
|
|
790
796
|
customContent: /*#__PURE__*/_jsx(View, {
|
|
791
797
|
style: styles.headerRow,
|
|
792
798
|
children: /*#__PURE__*/_jsx(NightSegmented, {
|
|
799
|
+
glass: true,
|
|
793
800
|
tabs: [{
|
|
794
801
|
key: "floating",
|
|
795
802
|
label: "Floating"
|
|
@@ -1079,7 +1086,7 @@ const styles = StyleSheet.create({
|
|
|
1079
1086
|
maxHeight: 150
|
|
1080
1087
|
},
|
|
1081
1088
|
storageKeyItem: {
|
|
1082
|
-
backgroundColor: night.color.
|
|
1089
|
+
backgroundColor: night.color.glassElevated,
|
|
1083
1090
|
borderRadius: night.radius.segment,
|
|
1084
1091
|
paddingHorizontal: 10,
|
|
1085
1092
|
paddingVertical: 7,
|
|
@@ -27,7 +27,7 @@ getToolLabel, getToolDescription,
|
|
|
27
27
|
globalSettingsConfig
|
|
28
28
|
// Types
|
|
29
29
|
} from '@buoy-gg/floating-tools-react';
|
|
30
|
-
import { QueryIcon, EnvIcon, StorageIcon, NetworkIcon, RoutesIcon, HighlighterIcon, ReduxIcon, EventsIcon, WifiIcon, ImageOverlayIcon, ZustandIcon, JotaiIcon } from '@buoy-gg/floating-tools-core';
|
|
30
|
+
import { QueryIcon, SentryIcon, EnvIcon, StorageIcon, NetworkIcon, RoutesIcon, HighlighterIcon, ReduxIcon, EventsIcon, WifiIcon, ImageOverlayIcon, ZustandIcon, JotaiIcon } from '@buoy-gg/floating-tools-core';
|
|
31
31
|
// JsModal.web + storage keys via the consumer's shared-ui web shim — the
|
|
32
32
|
// settings sheet is a real JsModal on web now, same as native. The
|
|
33
33
|
// BackgroundSwitcher is the same RN component native mounts: it renders under
|
|
@@ -61,6 +61,9 @@ const night = {
|
|
|
61
61
|
surfaceElevated: '#161618',
|
|
62
62
|
border: '#1A1A1C',
|
|
63
63
|
separator: 'rgba(255, 255, 255, 0.08)',
|
|
64
|
+
glass: 'rgba(5, 5, 5, 0.50)',
|
|
65
|
+
glassElevated: 'rgba(255, 255, 255, 0.06)',
|
|
66
|
+
glassBorder: 'rgba(255, 255, 255, 0.07)',
|
|
64
67
|
fillTertiary: 'rgba(255, 255, 255, 0.12)',
|
|
65
68
|
buttonSurface: 'rgba(8, 10, 14, 0.85)',
|
|
66
69
|
button: '#1C1C1C',
|
|
@@ -147,28 +150,6 @@ const Sparkles = ({
|
|
|
147
150
|
})]
|
|
148
151
|
});
|
|
149
152
|
|
|
150
|
-
/** Fallback for shared-ui SentryBugIcon (native sentry row icon, red). */
|
|
151
|
-
const AlertTriangle = ({
|
|
152
|
-
size = 24,
|
|
153
|
-
color = 'currentColor'
|
|
154
|
-
}) => /*#__PURE__*/_jsxs("svg", {
|
|
155
|
-
width: size,
|
|
156
|
-
height: size,
|
|
157
|
-
viewBox: "0 0 24 24",
|
|
158
|
-
fill: "none",
|
|
159
|
-
stroke: color,
|
|
160
|
-
strokeWidth: "2",
|
|
161
|
-
strokeLinecap: "round",
|
|
162
|
-
strokeLinejoin: "round",
|
|
163
|
-
children: [/*#__PURE__*/_jsx("path", {
|
|
164
|
-
d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"
|
|
165
|
-
}), /*#__PURE__*/_jsx("path", {
|
|
166
|
-
d: "M12 9v4"
|
|
167
|
-
}), /*#__PURE__*/_jsx("path", {
|
|
168
|
-
d: "M12 17h.01"
|
|
169
|
-
})]
|
|
170
|
-
});
|
|
171
|
-
|
|
172
153
|
/** shared-ui Info icon (native default tool icon). */
|
|
173
154
|
const Info = ({
|
|
174
155
|
size = 24,
|
|
@@ -305,10 +286,9 @@ function ToolIcon({
|
|
|
305
286
|
size: size
|
|
306
287
|
});
|
|
307
288
|
case 'sentry':
|
|
308
|
-
|
|
309
|
-
return /*#__PURE__*/_jsx(AlertTriangle, {
|
|
289
|
+
return /*#__PURE__*/_jsx(SentryIcon, {
|
|
310
290
|
size: size,
|
|
311
|
-
color:
|
|
291
|
+
color: "#9945FF"
|
|
312
292
|
});
|
|
313
293
|
case 'storage':
|
|
314
294
|
// native passes bgColor=night surface for the BIF knock-out parts
|
|
@@ -444,11 +424,11 @@ function Footnote({
|
|
|
444
424
|
});
|
|
445
425
|
}
|
|
446
426
|
|
|
447
|
-
/** native NightCard:
|
|
427
|
+
/** native NightCard glass: 50% black, radius 14, 1px white-alpha hairline, clipped */
|
|
448
428
|
const cardStyle = {
|
|
449
|
-
backgroundColor: night.
|
|
429
|
+
backgroundColor: night.glass,
|
|
450
430
|
borderRadius: 14,
|
|
451
|
-
border: `1px solid ${night.
|
|
431
|
+
border: `1px solid ${night.glassBorder}`,
|
|
452
432
|
overflow: 'hidden'
|
|
453
433
|
};
|
|
454
434
|
|
|
@@ -793,8 +773,8 @@ export function SettingsModal({
|
|
|
793
773
|
const displayTabs = settingsTabs.filter(tab => tab.key !== 'pro');
|
|
794
774
|
|
|
795
775
|
// Segmented control rendered inside the JsModal header — native passes a
|
|
796
|
-
// NightSegmented as header.customContent the same way. Track =
|
|
797
|
-
// radius 10 / padding 3; active thumb =
|
|
776
|
+
// NightSegmented glass as header.customContent the same way. Track = glass
|
|
777
|
+
// at radius 10 / padding 3; active thumb = glassElevated at radius 8 with
|
|
798
778
|
// the accent underline glow (the Everlights All/Day tabs).
|
|
799
779
|
const tabHeader = /*#__PURE__*/_jsx("div", {
|
|
800
780
|
style: {
|
|
@@ -809,9 +789,9 @@ export function SettingsModal({
|
|
|
809
789
|
style: {
|
|
810
790
|
flex: 1,
|
|
811
791
|
display: 'flex',
|
|
812
|
-
backgroundColor: night.
|
|
792
|
+
backgroundColor: night.glass,
|
|
813
793
|
borderRadius: 10,
|
|
814
|
-
border: `1px solid ${night.
|
|
794
|
+
border: `1px solid ${night.glassBorder}`,
|
|
815
795
|
padding: 3,
|
|
816
796
|
boxSizing: 'border-box'
|
|
817
797
|
},
|
|
@@ -829,7 +809,7 @@ export function SettingsModal({
|
|
|
829
809
|
padding: '8px 0',
|
|
830
810
|
borderRadius: 8,
|
|
831
811
|
border: 'none',
|
|
832
|
-
backgroundColor: isActive ? night.
|
|
812
|
+
backgroundColor: isActive ? night.glassElevated : 'transparent',
|
|
833
813
|
color: isActive ? night.text : night.textSecondary,
|
|
834
814
|
fontSize: 13,
|
|
835
815
|
fontWeight: isActive ? 600 : 500,
|
|
@@ -1014,7 +994,7 @@ export function SettingsModal({
|
|
|
1014
994
|
},
|
|
1015
995
|
children: savedKeys.map(key => /*#__PURE__*/_jsx("div", {
|
|
1016
996
|
style: {
|
|
1017
|
-
backgroundColor: night.
|
|
997
|
+
backgroundColor: night.glassElevated,
|
|
1018
998
|
borderRadius: 8,
|
|
1019
999
|
padding: '7px 10px',
|
|
1020
1000
|
marginBottom: 4
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { FloatingToolHost } from "@buoy-gg/shared-ui";
|
|
4
|
+
import { AccountSetup } from "@buoy-gg/shared-ui";
|
|
5
|
+
import React, { useMemo, useCallback, useEffect } from "react";
|
|
4
6
|
import { View, StyleSheet } from "react-native";
|
|
5
7
|
import { AppHostProvider } from "./AppHost";
|
|
6
8
|
import { FloatingMenu } from "./FloatingMenu.js";
|
|
@@ -9,10 +11,10 @@ import { ToolOpenerBridge } from "./ToolOpenerBridge.js";
|
|
|
9
11
|
import { autoDiscoverPresets, autoDiscoverPresetsWithCustom } from "./autoDiscoverPresets.js";
|
|
10
12
|
import { sendInstallPing } from "../telemetry/installPing.js";
|
|
11
13
|
import { isTelemetryEnabled } from "../Buoy.js";
|
|
12
|
-
import { renderAppIcon } from "./AppIcon
|
|
14
|
+
import { renderAppIcon } from "./AppIcon";
|
|
13
15
|
import { DevToolsVisibilityProvider } from "./DevToolsVisibilityContext.js";
|
|
14
16
|
import { reportOptionalRequireError } from "./optionalRequire.js";
|
|
15
|
-
import { HintsProvider,
|
|
17
|
+
import { HintsProvider, normalizeEnvironment } from "@buoy-gg/shared-ui";
|
|
16
18
|
import { MinimizedToolsProvider } from "./MinimizedToolsContext.js";
|
|
17
19
|
import { validateDialConfig } from "./defaultConfig.js";
|
|
18
20
|
import { DefaultConfigProvider } from "./DefaultConfigContext.js";
|
|
@@ -120,7 +122,7 @@ export const FloatingDevTools = ({
|
|
|
120
122
|
disableHints = false,
|
|
121
123
|
defaultFloatingTools,
|
|
122
124
|
defaultDialTools,
|
|
123
|
-
requireLicense
|
|
125
|
+
requireLicense: _requireLicense,
|
|
124
126
|
licenseKey: licenseKeyProp,
|
|
125
127
|
zustandStores,
|
|
126
128
|
environment,
|
|
@@ -138,24 +140,27 @@ export const FloatingDevTools = ({
|
|
|
138
140
|
isLicensed
|
|
139
141
|
} = useProAccess();
|
|
140
142
|
|
|
141
|
-
//
|
|
143
|
+
// Account admission is mandatory; paid feature gates remain separate.
|
|
142
144
|
const {
|
|
143
145
|
licenseKey,
|
|
144
146
|
isInitialized,
|
|
145
|
-
tier
|
|
147
|
+
tier,
|
|
148
|
+
access
|
|
146
149
|
} = useLicense();
|
|
150
|
+
const configuredMatches = licenseKeyProp === undefined || licenseKeyProp.trim().toUpperCase() === (licenseKey ?? "");
|
|
151
|
+
const accountAdmitted = configuredMatches && access.status === "admitted";
|
|
147
152
|
|
|
148
153
|
// Anonymous install ping — dev-only, once a day, no project or tool data.
|
|
149
|
-
// Deliberately waits for
|
|
150
|
-
//
|
|
154
|
+
// Deliberately waits for admission to resolve so what it reports is the
|
|
155
|
+
// real outcome (the account's tier, or "locked") rather than the manager's
|
|
156
|
+
// starting state.
|
|
151
157
|
useEffect(() => {
|
|
152
|
-
if (!isInitialized) return;
|
|
158
|
+
if (!isInitialized || access.status === "initializing") return;
|
|
153
159
|
void sendInstallPing({
|
|
154
|
-
tier,
|
|
160
|
+
tier: access.status === "admitted" ? tier : "locked",
|
|
155
161
|
enabled: isTelemetryEnabled()
|
|
156
162
|
});
|
|
157
|
-
}, [isInitialized, tier]);
|
|
158
|
-
const [showLicenseModal, setShowLicenseModal] = useState(false);
|
|
163
|
+
}, [isInitialized, access.status, tier]);
|
|
159
164
|
|
|
160
165
|
// Initialize license manager on mount, and set/clear license key when prop changes.
|
|
161
166
|
// The `licenseKey` prop is the source of truth WHEN PROVIDED:
|
|
@@ -163,28 +168,12 @@ export const FloatingDevTools = ({
|
|
|
163
168
|
// - "" (blank) → EXPLICITLY clear the cached license (key was removed)
|
|
164
169
|
// - undefined → don't touch it (the app may set a key via Buoy.init())
|
|
165
170
|
useEffect(() => {
|
|
166
|
-
|
|
167
|
-
LicenseManager.initialize().then(() => LicenseManager.setLicenseKey(licenseKeyProp)).catch(() => {});
|
|
168
|
-
} else if (licenseKeyProp === "") {
|
|
169
|
-
// Explicit removal: wipe the 30-day cache so Pro actually turns off.
|
|
170
|
-
LicenseManager.initialize().then(() => LicenseManager.clearLicense()).catch(() => {});
|
|
171
|
-
} else {
|
|
172
|
-
LicenseManager.initialize();
|
|
173
|
-
}
|
|
171
|
+
void LicenseManager.configure(licenseKeyProp).catch(() => {});
|
|
174
172
|
}, [licenseKeyProp]);
|
|
175
173
|
|
|
176
|
-
// Show license modal if requireLicense is enabled and no valid license
|
|
177
|
-
useEffect(() => {
|
|
178
|
-
if (requireLicense && isInitialized && !licenseKey) {
|
|
179
|
-
setShowLicenseModal(true);
|
|
180
|
-
} else if (licenseKey) {
|
|
181
|
-
setShowLicenseModal(false);
|
|
182
|
-
}
|
|
183
|
-
}, [requireLicense, isInitialized, licenseKey]);
|
|
184
|
-
|
|
185
174
|
// Register zustand stores once on mount if provided
|
|
186
175
|
useEffect(() => {
|
|
187
|
-
if (!zustandStores) return;
|
|
176
|
+
if (!zustandStores || access.status === "locked") return;
|
|
188
177
|
try {
|
|
189
178
|
const {
|
|
190
179
|
watchStores
|
|
@@ -193,7 +182,7 @@ export const FloatingDevTools = ({
|
|
|
193
182
|
} catch (e) {
|
|
194
183
|
reportOptionalRequireError("@buoy-gg/zustand", e);
|
|
195
184
|
}
|
|
196
|
-
}, []);
|
|
185
|
+
}, [zustandStores, access.status]);
|
|
197
186
|
|
|
198
187
|
// Normalize dial tools configuration (truncates to max 6 with warning if needed)
|
|
199
188
|
const normalizedDialTools = useMemo(() => {
|
|
@@ -450,17 +439,16 @@ export const FloatingDevTools = ({
|
|
|
450
439
|
|
|
451
440
|
// Gate: only a PAID license works outside development.
|
|
452
441
|
//
|
|
453
|
-
//
|
|
454
|
-
//
|
|
455
|
-
//
|
|
456
|
-
// here even though it validates perfectly well.
|
|
442
|
+
// A free key (and, of course, no key at all) stays dev-only. `isLicensed` is
|
|
443
|
+
// safe to gate on because `isPro` is a strict mirror of `tier === "pro"`, so
|
|
444
|
+
// a free key reports false here even though it validates perfectly well.
|
|
457
445
|
//
|
|
458
446
|
// When Business on-device login lands, this becomes the boundary that a
|
|
459
447
|
// logged-in QA/support seat crosses WITHOUT a key in the build — i.e.
|
|
460
448
|
// `isLicensed || tierSource === "session"`. Nothing else should.
|
|
461
449
|
const isDevelopment = typeof __DEV__ !== "undefined" && __DEV__;
|
|
462
450
|
if (!isDevelopment && !isLicensed) {
|
|
463
|
-
//
|
|
451
|
+
// Free or unadmitted user in production - don't render DevTools.
|
|
464
452
|
// NOTE for whoever relaxes this for the free tier: desktop sync does NOT
|
|
465
453
|
// ride on this check. It re-tests `isLicensed` itself (externalSyncGate),
|
|
466
454
|
// so opening this door does not open a broker socket. Keep it that way.
|
|
@@ -487,10 +475,16 @@ export const FloatingDevTools = ({
|
|
|
487
475
|
requiredEnvVars: requiredEnvVars,
|
|
488
476
|
customApps: syncApps
|
|
489
477
|
});
|
|
478
|
+
if (!accountAdmitted) {
|
|
479
|
+
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
480
|
+
children: [access.status === "initializing" && /*#__PURE__*/_jsxs(_Fragment, {
|
|
481
|
+
children: [ReduxAutoInstrument && /*#__PURE__*/_jsx(ReduxAutoInstrument, {}), ConsoleRoot && /*#__PURE__*/_jsx(ConsoleRoot, {}), RouteTracker && /*#__PURE__*/_jsx(RouteTracker, {}), ReactQueryTracker && /*#__PURE__*/_jsx(ReactQueryTracker, {})]
|
|
482
|
+
}), !headless && /*#__PURE__*/_jsx(AccountSetup, {})]
|
|
483
|
+
});
|
|
484
|
+
}
|
|
490
485
|
|
|
491
486
|
// Headless (sync-only): tool adapters + route tracking + desktop sync, no
|
|
492
|
-
// on-device UI.
|
|
493
|
-
// to gate. All hooks above already ran, so hook order is preserved, and
|
|
487
|
+
// on-device UI. Account admission above applies to this path too. All hooks above already ran, so hook order is preserved, and
|
|
494
488
|
// AutoExternalSync still obeys `syncEnabled` (the broker must never be
|
|
495
489
|
// contacted from a shipped build that didn't ask for it).
|
|
496
490
|
if (headless) {
|
|
@@ -506,16 +500,6 @@ export const FloatingDevTools = ({
|
|
|
506
500
|
});
|
|
507
501
|
}
|
|
508
502
|
|
|
509
|
-
// Gate: If requireLicense is enabled and no license, show only the license modal
|
|
510
|
-
if (requireLicense && isInitialized && !licenseKey) {
|
|
511
|
-
return /*#__PURE__*/_jsx(LicenseEntryModal, {
|
|
512
|
-
visible: showLicenseModal,
|
|
513
|
-
onClose: () => {} // Can't close without entering a license
|
|
514
|
-
,
|
|
515
|
-
onSuccess: () => setShowLicenseModal(false)
|
|
516
|
-
});
|
|
517
|
-
}
|
|
518
|
-
|
|
519
503
|
// Session blocking is handled inline by JsModal's SessionBlockedOverlay
|
|
520
504
|
// No need to replace the entire UI - tools still render, but content is blocked
|
|
521
505
|
|
|
@@ -538,7 +522,9 @@ export const FloatingDevTools = ({
|
|
|
538
522
|
...props,
|
|
539
523
|
apps: finalApps,
|
|
540
524
|
environment: resolvedEnvironment
|
|
541
|
-
}), props.hidden ? null : /*#__PURE__*/
|
|
525
|
+
}), props.hidden ? null : /*#__PURE__*/_jsxs(_Fragment, {
|
|
526
|
+
children: [/*#__PURE__*/_jsx(AppOverlay, {}), /*#__PURE__*/_jsx(FloatingToolHost, {})]
|
|
527
|
+
}), /*#__PURE__*/_jsx(ToolOpenerBridge, {})]
|
|
542
528
|
})
|
|
543
529
|
}), children, DebugBordersOverlay && /*#__PURE__*/_jsx(DebugBordersOverlay, {}), HighlightUpdatesOverlay && /*#__PURE__*/_jsx(HighlightUpdatesOverlay, {}), ImageOverlayOverlay && /*#__PURE__*/_jsx(ImageOverlayOverlay, {}), PerfMonitorOverlay && /*#__PURE__*/_jsx(PerfMonitorOverlay, {}), JsTopOverlay && /*#__PURE__*/_jsx(JsTopOverlay, {}), ImpersonateOverlay && /*#__PURE__*/_jsx(ImpersonateOverlay, {}), ScenariosOverlay && /*#__PURE__*/_jsx(ScenariosOverlay, {}), ReduxAutoInstrument && /*#__PURE__*/_jsx(ReduxAutoInstrument, {}), ConsoleRoot && /*#__PURE__*/_jsx(ConsoleRoot, {}), RouteTracker && /*#__PURE__*/_jsx(RouteTracker, {}), ReactQueryTracker && /*#__PURE__*/_jsx(ReactQueryTracker, {}), toolRegistry, syncEnabled && /*#__PURE__*/_jsx(AutoExternalSync, {
|
|
544
530
|
options: externalSyncOptions(externalSync),
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
import { FloatingToolHost } from "@buoy-gg/shared-ui/floating";
|
|
4
|
+
import { LicenseManager, useLicense } from "@buoy-gg/license";
|
|
5
|
+
import { AccountSetup } from "@buoy-gg/shared-ui";
|
|
3
6
|
/**
|
|
4
7
|
* FloatingDevTools - Unified dev tools component for React DOM Web.
|
|
5
8
|
*
|
|
@@ -84,7 +87,7 @@ function MenuLauncherIcon({
|
|
|
84
87
|
// Main Component
|
|
85
88
|
// =============================
|
|
86
89
|
|
|
87
|
-
|
|
90
|
+
function AdmittedFloatingDevTools(props) {
|
|
88
91
|
// The tool host wraps everything so dial presses can open tool components
|
|
89
92
|
// into the AppOverlay (web twin of the native AppHostProvider/AppOverlay).
|
|
90
93
|
return /*#__PURE__*/_jsx(AppHostProvider, {
|
|
@@ -369,10 +372,28 @@ function FloatingDevToolsInner({
|
|
|
369
372
|
onSettingsChange: _newSettings => {
|
|
370
373
|
// Settings are already saved by the hook
|
|
371
374
|
}
|
|
372
|
-
}), /*#__PURE__*/_jsx(AppOverlay, {})]
|
|
375
|
+
}), /*#__PURE__*/_jsx(AppOverlay, {}), /*#__PURE__*/_jsx(FloatingToolHost, {})]
|
|
373
376
|
});
|
|
374
377
|
}
|
|
375
378
|
|
|
376
379
|
// =============================
|
|
377
380
|
// Re-exports for convenience
|
|
378
|
-
// =============================
|
|
381
|
+
// =============================
|
|
382
|
+
|
|
383
|
+
export function FloatingDevTools(props) {
|
|
384
|
+
const {
|
|
385
|
+
access,
|
|
386
|
+
isPro,
|
|
387
|
+
licenseKey
|
|
388
|
+
} = useLicense();
|
|
389
|
+
useEffect(() => {
|
|
390
|
+
void LicenseManager.configure(props.licenseKey);
|
|
391
|
+
}, [props.licenseKey]);
|
|
392
|
+
const matches = props.licenseKey === undefined || props.licenseKey.trim().toUpperCase() === (licenseKey ?? "");
|
|
393
|
+
const dev = typeof __DEV__ !== "undefined" ? __DEV__ : process.env.NODE_ENV !== "production";
|
|
394
|
+
if (!dev && !isPro) return null;
|
|
395
|
+
if (!matches || access.status !== "admitted") return props.headless ? null : /*#__PURE__*/_jsx(AccountSetup, {});
|
|
396
|
+
return /*#__PURE__*/_jsx(AdmittedFloatingDevTools, {
|
|
397
|
+
...props
|
|
398
|
+
});
|
|
399
|
+
}
|
|
@@ -4,7 +4,7 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
|
4
4
|
import { TouchableOpacity, StyleSheet, View } from "react-native";
|
|
5
5
|
import { absoluteFill } from "@buoy-gg/shared-ui";
|
|
6
6
|
import { FloatingTools, UserStatus } from "./floatingTools";
|
|
7
|
-
import { renderAppIcon } from "./AppIcon
|
|
7
|
+
import { renderAppIcon } from "./AppIcon";
|
|
8
8
|
import { DialDevTools } from "./dial/DialDevTools";
|
|
9
9
|
import { EnvironmentIndicator, persistentStorage, useHintsDisabled, devToolsStorageKeys, buoyColors } from "@buoy-gg/shared-ui";
|
|
10
10
|
import { useDevToolsSettings } from "./DevToolsSettingsModal";
|
|
@@ -189,6 +189,7 @@ export function ToolModalShell({
|
|
|
189
189
|
subtitle: subtitle
|
|
190
190
|
}), /*#__PURE__*/_jsxs(ModalHeader.Actions, {
|
|
191
191
|
children: [!isSearchActive && actions, searchable && !isSearchActive && /*#__PURE__*/_jsx(HeaderSearchButton, {
|
|
192
|
+
variant: "chip",
|
|
192
193
|
testID: `tool-search-${app.id}`,
|
|
193
194
|
onPress: () => setIsSearchActive(true)
|
|
194
195
|
})]
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { useEffect, useMemo, useRef } from "react";
|
|
21
21
|
import { Platform } from "react-native";
|
|
22
|
+
import { LicenseManager } from "@buoy-gg/license";
|
|
22
23
|
import { reportOptionalRequireError } from "./optionalRequire.js";
|
|
23
24
|
import { appSyncAdapter } from "./sync/appSyncAdapter.js";
|
|
24
25
|
import { useDeviceIdentity } from "./deviceIdentity.js";
|
|
@@ -285,11 +286,11 @@ export function AutoExternalSync({
|
|
|
285
286
|
useEffect(() => {
|
|
286
287
|
if (!socket) return;
|
|
287
288
|
const emitLicense = () => {
|
|
289
|
+
if (!LicenseManager.hasAccountAccess() || !socket.connected) return;
|
|
288
290
|
socket.emit("device-license", {
|
|
289
291
|
isPro: licenseRef.current.isPro ? "1" : "0",
|
|
290
292
|
// Brokers older than the tier split ignore this; newer ones prefer it.
|
|
291
|
-
tier: licenseRef.current.tier ?? (licenseRef.current.isPro ? "pro" : "
|
|
292
|
-
licenseKey: licenseRef.current.licenseKey ?? ""
|
|
293
|
+
tier: licenseRef.current.tier ?? (licenseRef.current.isPro ? "pro" : "free")
|
|
293
294
|
});
|
|
294
295
|
};
|
|
295
296
|
if (socket.connected) emitLicense();
|
|
@@ -11,7 +11,7 @@ import { getRankedToolIds, isDialUsageLoaded, loadDialUsage, recordToolUsage } f
|
|
|
11
11
|
import { persistentStorage, useHintsDisabled, devToolsStorageKeys, buoyColors, ToolBackground, useToolBackground } from "@buoy-gg/shared-ui";
|
|
12
12
|
import { DevToolsSettingsModal, useDevToolsSettings } from "../DevToolsSettingsModal";
|
|
13
13
|
import { useProAccess } from "@buoy-gg/license";
|
|
14
|
-
import { renderAppIcon } from "../AppIcon
|
|
14
|
+
import { renderAppIcon } from "../AppIcon";
|
|
15
15
|
import { useAppHost } from "../AppHost";
|
|
16
16
|
import { OnboardingTooltip } from "./OnboardingTooltip";
|
|
17
17
|
import { getDialLayout, MAX_DIAL_SLOTS, DIAL_BUTTON_SIZE, dialAnimationConfig, dialColors } from "@buoy-gg/floating-tools-core";
|
|
@@ -21,6 +21,14 @@ import { getDialLayout, MAX_DIAL_SLOTS, DIAL_BUTTON_SIZE, dialAnimationConfig, d
|
|
|
21
21
|
// snapshot goes stale when the window resizes after load (web/desktop).
|
|
22
22
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
23
23
|
const BUTTON_SIZE = DIAL_BUTTON_SIZE;
|
|
24
|
+
/** The dial face is a circle, so its centre is the middle of the measured square. */
|
|
25
|
+
const DIAL_BACKGROUND_FOCUS = {
|
|
26
|
+
x: 0.5,
|
|
27
|
+
y: 0.5
|
|
28
|
+
};
|
|
29
|
+
// Radius kept clear of grid spokes around the glass hub (its outer disc is
|
|
30
|
+
// 0.75 × BUTTON_SIZE, plus a breath).
|
|
31
|
+
const HUB_CLEARANCE = BUTTON_SIZE * 0.75 + 6;
|
|
24
32
|
|
|
25
33
|
/**
|
|
26
34
|
* The centre button's idle pulse, off by design.
|
|
@@ -97,6 +105,11 @@ export const DialDevTools = ({
|
|
|
97
105
|
},
|
|
98
106
|
gridLine: {
|
|
99
107
|
width: circleSize
|
|
108
|
+
},
|
|
109
|
+
// Each spoke is two segments that stop short of the glass hub, so the
|
|
110
|
+
// six lines never meet in the middle where the background shows through.
|
|
111
|
+
spoke: {
|
|
112
|
+
width: circleSize / 2 - HUB_CLEARANCE
|
|
100
113
|
}
|
|
101
114
|
}), [circleSize]);
|
|
102
115
|
|
|
@@ -222,7 +235,7 @@ export const DialDevTools = ({
|
|
|
222
235
|
iconComponent: typeof app.icon === "function" ? app.icon : undefined,
|
|
223
236
|
color: app.color ?? buoyColors.primary,
|
|
224
237
|
onPress: () => {
|
|
225
|
-
//
|
|
238
|
+
// Put this tool first the next time the dial opens.
|
|
226
239
|
void recordToolUsage(app.id);
|
|
227
240
|
|
|
228
241
|
// Call the app's onPress callback if provided, passing actions for
|
|
@@ -586,16 +599,23 @@ export const DialDevTools = ({
|
|
|
586
599
|
style: styles.gridPattern,
|
|
587
600
|
children: Array.from({
|
|
588
601
|
length: 6
|
|
589
|
-
}).map((_, i) => /*#__PURE__*/
|
|
602
|
+
}).map((_, i) => /*#__PURE__*/_jsxs(View, {
|
|
590
603
|
style: [styles.gridLine, sizeStyles.gridLine, {
|
|
591
604
|
transform: [{
|
|
592
605
|
rotate: `${i * 60}deg`
|
|
593
606
|
}]
|
|
594
|
-
}]
|
|
607
|
+
}],
|
|
608
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
609
|
+
style: [styles.spoke, sizeStyles.spoke, styles.spokeLeft]
|
|
610
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
611
|
+
style: [styles.spoke, sizeStyles.spoke, styles.spokeRight]
|
|
612
|
+
})]
|
|
595
613
|
}, i))
|
|
596
614
|
}), backgroundId === "hyperspace" ? /*#__PURE__*/_jsx(DialHyperspace, {
|
|
597
615
|
size: circleSize
|
|
598
|
-
}) : /*#__PURE__*/_jsx(ToolBackground, {
|
|
616
|
+
}) : /*#__PURE__*/_jsx(ToolBackground, {
|
|
617
|
+
focus: DIAL_BACKGROUND_FOCUS
|
|
618
|
+
})]
|
|
599
619
|
}), allDialIcons.filter(({
|
|
600
620
|
page
|
|
601
621
|
}) => page === safePage).map(({
|
|
@@ -753,9 +773,20 @@ const styles = StyleSheet.create({
|
|
|
753
773
|
},
|
|
754
774
|
gridLine: {
|
|
755
775
|
position: "absolute",
|
|
776
|
+
height: 1
|
|
777
|
+
},
|
|
778
|
+
spoke: {
|
|
779
|
+
position: "absolute",
|
|
780
|
+
top: 0,
|
|
756
781
|
height: 1,
|
|
757
782
|
backgroundColor: dialColors.dialGridLine
|
|
758
783
|
},
|
|
784
|
+
spokeLeft: {
|
|
785
|
+
left: 0
|
|
786
|
+
},
|
|
787
|
+
spokeRight: {
|
|
788
|
+
right: 0
|
|
789
|
+
},
|
|
759
790
|
buttonContainer: {
|
|
760
791
|
zIndex: 1,
|
|
761
792
|
backgroundColor: "transparent",
|
|
@@ -766,6 +797,10 @@ const styles = StyleSheet.create({
|
|
|
766
797
|
height: BUTTON_SIZE * 1.5,
|
|
767
798
|
borderRadius: BUTTON_SIZE
|
|
768
799
|
},
|
|
800
|
+
// The hub is glass: no rings, no opaque fill. The outer disc is a barely-
|
|
801
|
+
// there shade and the inner button a soft puck, so the tool background runs
|
|
802
|
+
// through the middle of the dial while BUOY / PRO stay legible. The spokes
|
|
803
|
+
// stop at HUB_CLEARANCE so nothing draws under it.
|
|
769
804
|
buttonGradient: {
|
|
770
805
|
width: "100%",
|
|
771
806
|
height: "100%",
|
|
@@ -773,7 +808,7 @@ const styles = StyleSheet.create({
|
|
|
773
808
|
alignItems: "center",
|
|
774
809
|
justifyContent: "center",
|
|
775
810
|
padding: 4,
|
|
776
|
-
backgroundColor: dialColors.
|
|
811
|
+
backgroundColor: dialColors.hubHalo,
|
|
777
812
|
position: "relative",
|
|
778
813
|
overflow: "hidden"
|
|
779
814
|
},
|
|
@@ -800,14 +835,12 @@ const styles = StyleSheet.create({
|
|
|
800
835
|
borderRadius: BUTTON_SIZE
|
|
801
836
|
},
|
|
802
837
|
buttonBorder: {
|
|
803
|
-
backgroundColor: dialColors.
|
|
838
|
+
backgroundColor: dialColors.hubFill,
|
|
804
839
|
alignItems: "center",
|
|
805
840
|
justifyContent: "center",
|
|
806
841
|
width: BUTTON_SIZE * 1.2,
|
|
807
842
|
height: BUTTON_SIZE * 1.2,
|
|
808
|
-
borderRadius: BUTTON_SIZE * 0.6
|
|
809
|
-
borderWidth: 2,
|
|
810
|
-
borderColor: dialColors.dialBorder
|
|
843
|
+
borderRadius: BUTTON_SIZE * 0.6
|
|
811
844
|
},
|
|
812
845
|
button: {
|
|
813
846
|
width: BUTTON_SIZE,
|
|
@@ -548,6 +548,8 @@ export function DialMenu({
|
|
|
548
548
|
// Computed values
|
|
549
549
|
const buttonContainerSize = layout.buttonSize * dialStyles.centerButton.containerRatio;
|
|
550
550
|
const buttonBorderSize = layout.buttonSize * dialStyles.centerButton.borderRatio;
|
|
551
|
+
// Grid spokes stop this far from the centre: the hub's outer disc plus a breath.
|
|
552
|
+
const hubClearance = layout.buttonSize * dialStyles.centerButton.containerRatio / 2 + 6;
|
|
551
553
|
const isAnimating = entranceComplete && !isExiting;
|
|
552
554
|
return /*#__PURE__*/_jsxs("div", {
|
|
553
555
|
role: "dialog",
|
|
@@ -623,10 +625,19 @@ export function DialMenu({
|
|
|
623
625
|
left: 0,
|
|
624
626
|
right: 0,
|
|
625
627
|
height: dialStyles.grid.lineHeight,
|
|
626
|
-
backgroundColor: dialColors.dialGridLine,
|
|
627
628
|
transform: `rotate(${deg}deg)`,
|
|
628
629
|
transformOrigin: 'center'
|
|
629
|
-
}
|
|
630
|
+
},
|
|
631
|
+
children: ['left', 'right'].map(side => /*#__PURE__*/_jsx("div", {
|
|
632
|
+
style: {
|
|
633
|
+
position: 'absolute',
|
|
634
|
+
top: 0,
|
|
635
|
+
[side]: 0,
|
|
636
|
+
width: `calc(50% - ${hubClearance}px)`,
|
|
637
|
+
height: dialStyles.grid.lineHeight,
|
|
638
|
+
backgroundColor: dialColors.dialGridLine
|
|
639
|
+
}
|
|
640
|
+
}, side))
|
|
630
641
|
}, deg)), paddedIcons.map((icon, index) => /*#__PURE__*/_jsx(DialIconItem, {
|
|
631
642
|
icon: icon,
|
|
632
643
|
index: index,
|
|
@@ -646,7 +657,10 @@ export function DialMenu({
|
|
|
646
657
|
display: 'flex',
|
|
647
658
|
alignItems: 'center',
|
|
648
659
|
justifyContent: 'center',
|
|
649
|
-
|
|
660
|
+
// Glass hub — see the native twin: a barely-there shade on the outer
|
|
661
|
+
// disc, a soft puck inside, no rings, so the tool background runs
|
|
662
|
+
// through the middle of the dial.
|
|
663
|
+
backgroundColor: dialColors.hubHalo,
|
|
650
664
|
borderRadius: layout.buttonSize,
|
|
651
665
|
overflow: 'hidden',
|
|
652
666
|
transform: `scale(${centerButtonScale * centerPulseScale})`,
|
|
@@ -685,8 +699,7 @@ export function DialMenu({
|
|
|
685
699
|
width: buttonBorderSize,
|
|
686
700
|
height: buttonBorderSize,
|
|
687
701
|
borderRadius: layout.buttonSize * dialStyles.centerButton.borderRadiusRatio,
|
|
688
|
-
backgroundColor: dialColors.
|
|
689
|
-
border: `${dialStyles.centerButton.borderWidth}px solid ${dialColors.dialBorder}`,
|
|
702
|
+
backgroundColor: dialColors.hubFill,
|
|
690
703
|
display: 'flex',
|
|
691
704
|
alignItems: 'center',
|
|
692
705
|
justifyContent: 'center'
|