@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
|
@@ -54,6 +54,9 @@ const night = {
|
|
|
54
54
|
surfaceElevated: '#161618',
|
|
55
55
|
border: '#1A1A1C',
|
|
56
56
|
separator: 'rgba(255, 255, 255, 0.08)',
|
|
57
|
+
glass: 'rgba(5, 5, 5, 0.50)',
|
|
58
|
+
glassElevated: 'rgba(255, 255, 255, 0.06)',
|
|
59
|
+
glassBorder: 'rgba(255, 255, 255, 0.07)',
|
|
57
60
|
fillTertiary: 'rgba(255, 255, 255, 0.12)',
|
|
58
61
|
buttonSurface: 'rgba(8, 10, 14, 0.85)',
|
|
59
62
|
button: '#1C1C1C',
|
|
@@ -140,28 +143,6 @@ const Sparkles = ({
|
|
|
140
143
|
})]
|
|
141
144
|
});
|
|
142
145
|
|
|
143
|
-
/** Fallback for shared-ui SentryBugIcon (native sentry row icon, red). */
|
|
144
|
-
const AlertTriangle = ({
|
|
145
|
-
size = 24,
|
|
146
|
-
color = 'currentColor'
|
|
147
|
-
}) => /*#__PURE__*/(0, _jsxRuntime.jsxs)("svg", {
|
|
148
|
-
width: size,
|
|
149
|
-
height: size,
|
|
150
|
-
viewBox: "0 0 24 24",
|
|
151
|
-
fill: "none",
|
|
152
|
-
stroke: color,
|
|
153
|
-
strokeWidth: "2",
|
|
154
|
-
strokeLinecap: "round",
|
|
155
|
-
strokeLinejoin: "round",
|
|
156
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("path", {
|
|
157
|
-
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"
|
|
158
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("path", {
|
|
159
|
-
d: "M12 9v4"
|
|
160
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("path", {
|
|
161
|
-
d: "M12 17h.01"
|
|
162
|
-
})]
|
|
163
|
-
});
|
|
164
|
-
|
|
165
146
|
/** shared-ui Info icon (native default tool icon). */
|
|
166
147
|
const Info = ({
|
|
167
148
|
size = 24,
|
|
@@ -298,10 +279,9 @@ function ToolIcon({
|
|
|
298
279
|
size: size
|
|
299
280
|
});
|
|
300
281
|
case 'sentry':
|
|
301
|
-
|
|
302
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsx)(AlertTriangle, {
|
|
282
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.SentryIcon, {
|
|
303
283
|
size: size,
|
|
304
|
-
color:
|
|
284
|
+
color: "#9945FF"
|
|
305
285
|
});
|
|
306
286
|
case 'storage':
|
|
307
287
|
// native passes bgColor=night surface for the BIF knock-out parts
|
|
@@ -437,11 +417,11 @@ function Footnote({
|
|
|
437
417
|
});
|
|
438
418
|
}
|
|
439
419
|
|
|
440
|
-
/** native NightCard:
|
|
420
|
+
/** native NightCard glass: 50% black, radius 14, 1px white-alpha hairline, clipped */
|
|
441
421
|
const cardStyle = {
|
|
442
|
-
backgroundColor: night.
|
|
422
|
+
backgroundColor: night.glass,
|
|
443
423
|
borderRadius: 14,
|
|
444
|
-
border: `1px solid ${night.
|
|
424
|
+
border: `1px solid ${night.glassBorder}`,
|
|
445
425
|
overflow: 'hidden'
|
|
446
426
|
};
|
|
447
427
|
|
|
@@ -786,8 +766,8 @@ function SettingsModal({
|
|
|
786
766
|
const displayTabs = _floatingToolsReact.settingsTabs.filter(tab => tab.key !== 'pro');
|
|
787
767
|
|
|
788
768
|
// Segmented control rendered inside the JsModal header — native passes a
|
|
789
|
-
// NightSegmented as header.customContent the same way. Track =
|
|
790
|
-
// radius 10 / padding 3; active thumb =
|
|
769
|
+
// NightSegmented glass as header.customContent the same way. Track = glass
|
|
770
|
+
// at radius 10 / padding 3; active thumb = glassElevated at radius 8 with
|
|
791
771
|
// the accent underline glow (the Everlights All/Day tabs).
|
|
792
772
|
const tabHeader = /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
793
773
|
style: {
|
|
@@ -802,9 +782,9 @@ function SettingsModal({
|
|
|
802
782
|
style: {
|
|
803
783
|
flex: 1,
|
|
804
784
|
display: 'flex',
|
|
805
|
-
backgroundColor: night.
|
|
785
|
+
backgroundColor: night.glass,
|
|
806
786
|
borderRadius: 10,
|
|
807
|
-
border: `1px solid ${night.
|
|
787
|
+
border: `1px solid ${night.glassBorder}`,
|
|
808
788
|
padding: 3,
|
|
809
789
|
boxSizing: 'border-box'
|
|
810
790
|
},
|
|
@@ -822,7 +802,7 @@ function SettingsModal({
|
|
|
822
802
|
padding: '8px 0',
|
|
823
803
|
borderRadius: 8,
|
|
824
804
|
border: 'none',
|
|
825
|
-
backgroundColor: isActive ? night.
|
|
805
|
+
backgroundColor: isActive ? night.glassElevated : 'transparent',
|
|
826
806
|
color: isActive ? night.text : night.textSecondary,
|
|
827
807
|
fontSize: 13,
|
|
828
808
|
fontWeight: isActive ? 600 : 500,
|
|
@@ -1007,7 +987,7 @@ function SettingsModal({
|
|
|
1007
987
|
},
|
|
1008
988
|
children: savedKeys.map(key => /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
1009
989
|
style: {
|
|
1010
|
-
backgroundColor: night.
|
|
990
|
+
backgroundColor: night.glassElevated,
|
|
1011
991
|
borderRadius: 8,
|
|
1012
992
|
padding: '7px 10px',
|
|
1013
993
|
marginBottom: 4
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.FloatingDevTools = void 0;
|
|
7
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
7
8
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
9
|
var _reactNative = require("react-native");
|
|
9
10
|
var _AppHost = require("./AppHost");
|
|
@@ -12,10 +13,9 @@ var _ToolOpenerBridge = require("./ToolOpenerBridge.js");
|
|
|
12
13
|
var _autoDiscoverPresets = require("./autoDiscoverPresets.js");
|
|
13
14
|
var _installPing = require("../telemetry/installPing.js");
|
|
14
15
|
var _Buoy = require("../Buoy.js");
|
|
15
|
-
var _AppIcon = require("./AppIcon
|
|
16
|
+
var _AppIcon = require("./AppIcon");
|
|
16
17
|
var _DevToolsVisibilityContext = require("./DevToolsVisibilityContext.js");
|
|
17
18
|
var _optionalRequire = require("./optionalRequire.js");
|
|
18
|
-
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
19
19
|
var _MinimizedToolsContext = require("./MinimizedToolsContext.js");
|
|
20
20
|
var _defaultConfig = require("./defaultConfig.js");
|
|
21
21
|
var _DefaultConfigContext = require("./DefaultConfigContext.js");
|
|
@@ -124,7 +124,7 @@ const FloatingDevTools = ({
|
|
|
124
124
|
disableHints = false,
|
|
125
125
|
defaultFloatingTools,
|
|
126
126
|
defaultDialTools,
|
|
127
|
-
requireLicense
|
|
127
|
+
requireLicense: _requireLicense,
|
|
128
128
|
licenseKey: licenseKeyProp,
|
|
129
129
|
zustandStores,
|
|
130
130
|
environment,
|
|
@@ -142,24 +142,27 @@ const FloatingDevTools = ({
|
|
|
142
142
|
isLicensed
|
|
143
143
|
} = (0, _license.useProAccess)();
|
|
144
144
|
|
|
145
|
-
//
|
|
145
|
+
// Account admission is mandatory; paid feature gates remain separate.
|
|
146
146
|
const {
|
|
147
147
|
licenseKey,
|
|
148
148
|
isInitialized,
|
|
149
|
-
tier
|
|
149
|
+
tier,
|
|
150
|
+
access
|
|
150
151
|
} = (0, _license.useLicense)();
|
|
152
|
+
const configuredMatches = licenseKeyProp === undefined || licenseKeyProp.trim().toUpperCase() === (licenseKey ?? "");
|
|
153
|
+
const accountAdmitted = configuredMatches && access.status === "admitted";
|
|
151
154
|
|
|
152
155
|
// Anonymous install ping — dev-only, once a day, no project or tool data.
|
|
153
|
-
// Deliberately waits for
|
|
154
|
-
//
|
|
156
|
+
// Deliberately waits for admission to resolve so what it reports is the
|
|
157
|
+
// real outcome (the account's tier, or "locked") rather than the manager's
|
|
158
|
+
// starting state.
|
|
155
159
|
(0, _react.useEffect)(() => {
|
|
156
|
-
if (!isInitialized) return;
|
|
160
|
+
if (!isInitialized || access.status === "initializing") return;
|
|
157
161
|
void (0, _installPing.sendInstallPing)({
|
|
158
|
-
tier,
|
|
162
|
+
tier: access.status === "admitted" ? tier : "locked",
|
|
159
163
|
enabled: (0, _Buoy.isTelemetryEnabled)()
|
|
160
164
|
});
|
|
161
|
-
}, [isInitialized, tier]);
|
|
162
|
-
const [showLicenseModal, setShowLicenseModal] = (0, _react.useState)(false);
|
|
165
|
+
}, [isInitialized, access.status, tier]);
|
|
163
166
|
|
|
164
167
|
// Initialize license manager on mount, and set/clear license key when prop changes.
|
|
165
168
|
// The `licenseKey` prop is the source of truth WHEN PROVIDED:
|
|
@@ -167,28 +170,12 @@ const FloatingDevTools = ({
|
|
|
167
170
|
// - "" (blank) → EXPLICITLY clear the cached license (key was removed)
|
|
168
171
|
// - undefined → don't touch it (the app may set a key via Buoy.init())
|
|
169
172
|
(0, _react.useEffect)(() => {
|
|
170
|
-
|
|
171
|
-
_license.LicenseManager.initialize().then(() => _license.LicenseManager.setLicenseKey(licenseKeyProp)).catch(() => {});
|
|
172
|
-
} else if (licenseKeyProp === "") {
|
|
173
|
-
// Explicit removal: wipe the 30-day cache so Pro actually turns off.
|
|
174
|
-
_license.LicenseManager.initialize().then(() => _license.LicenseManager.clearLicense()).catch(() => {});
|
|
175
|
-
} else {
|
|
176
|
-
_license.LicenseManager.initialize();
|
|
177
|
-
}
|
|
173
|
+
void _license.LicenseManager.configure(licenseKeyProp).catch(() => {});
|
|
178
174
|
}, [licenseKeyProp]);
|
|
179
175
|
|
|
180
|
-
// Show license modal if requireLicense is enabled and no valid license
|
|
181
|
-
(0, _react.useEffect)(() => {
|
|
182
|
-
if (requireLicense && isInitialized && !licenseKey) {
|
|
183
|
-
setShowLicenseModal(true);
|
|
184
|
-
} else if (licenseKey) {
|
|
185
|
-
setShowLicenseModal(false);
|
|
186
|
-
}
|
|
187
|
-
}, [requireLicense, isInitialized, licenseKey]);
|
|
188
|
-
|
|
189
176
|
// Register zustand stores once on mount if provided
|
|
190
177
|
(0, _react.useEffect)(() => {
|
|
191
|
-
if (!zustandStores) return;
|
|
178
|
+
if (!zustandStores || access.status === "locked") return;
|
|
192
179
|
try {
|
|
193
180
|
const {
|
|
194
181
|
watchStores
|
|
@@ -197,7 +184,7 @@ const FloatingDevTools = ({
|
|
|
197
184
|
} catch (e) {
|
|
198
185
|
(0, _optionalRequire.reportOptionalRequireError)("@buoy-gg/zustand", e);
|
|
199
186
|
}
|
|
200
|
-
}, []);
|
|
187
|
+
}, [zustandStores, access.status]);
|
|
201
188
|
|
|
202
189
|
// Normalize dial tools configuration (truncates to max 6 with warning if needed)
|
|
203
190
|
const normalizedDialTools = (0, _react.useMemo)(() => {
|
|
@@ -454,17 +441,16 @@ const FloatingDevTools = ({
|
|
|
454
441
|
|
|
455
442
|
// Gate: only a PAID license works outside development.
|
|
456
443
|
//
|
|
457
|
-
//
|
|
458
|
-
//
|
|
459
|
-
//
|
|
460
|
-
// here even though it validates perfectly well.
|
|
444
|
+
// A free key (and, of course, no key at all) stays dev-only. `isLicensed` is
|
|
445
|
+
// safe to gate on because `isPro` is a strict mirror of `tier === "pro"`, so
|
|
446
|
+
// a free key reports false here even though it validates perfectly well.
|
|
461
447
|
//
|
|
462
448
|
// When Business on-device login lands, this becomes the boundary that a
|
|
463
449
|
// logged-in QA/support seat crosses WITHOUT a key in the build — i.e.
|
|
464
450
|
// `isLicensed || tierSource === "session"`. Nothing else should.
|
|
465
451
|
const isDevelopment = typeof __DEV__ !== "undefined" && __DEV__;
|
|
466
452
|
if (!isDevelopment && !isLicensed) {
|
|
467
|
-
//
|
|
453
|
+
// Free or unadmitted user in production - don't render DevTools.
|
|
468
454
|
// NOTE for whoever relaxes this for the free tier: desktop sync does NOT
|
|
469
455
|
// ride on this check. It re-tests `isLicensed` itself (externalSyncGate),
|
|
470
456
|
// so opening this door does not open a broker socket. Keep it that way.
|
|
@@ -491,10 +477,16 @@ const FloatingDevTools = ({
|
|
|
491
477
|
requiredEnvVars: requiredEnvVars,
|
|
492
478
|
customApps: syncApps
|
|
493
479
|
});
|
|
480
|
+
if (!accountAdmitted) {
|
|
481
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
482
|
+
children: [access.status === "initializing" && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
483
|
+
children: [ReduxAutoInstrument && /*#__PURE__*/(0, _jsxRuntime.jsx)(ReduxAutoInstrument, {}), ConsoleRoot && /*#__PURE__*/(0, _jsxRuntime.jsx)(ConsoleRoot, {}), RouteTracker && /*#__PURE__*/(0, _jsxRuntime.jsx)(RouteTracker, {}), ReactQueryTracker && /*#__PURE__*/(0, _jsxRuntime.jsx)(ReactQueryTracker, {})]
|
|
484
|
+
}), !headless && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.AccountSetup, {})]
|
|
485
|
+
});
|
|
486
|
+
}
|
|
494
487
|
|
|
495
488
|
// Headless (sync-only): tool adapters + route tracking + desktop sync, no
|
|
496
|
-
// on-device UI.
|
|
497
|
-
// to gate. All hooks above already ran, so hook order is preserved, and
|
|
489
|
+
// on-device UI. Account admission above applies to this path too. All hooks above already ran, so hook order is preserved, and
|
|
498
490
|
// AutoExternalSync still obeys `syncEnabled` (the broker must never be
|
|
499
491
|
// contacted from a shipped build that didn't ask for it).
|
|
500
492
|
if (headless) {
|
|
@@ -510,16 +502,6 @@ const FloatingDevTools = ({
|
|
|
510
502
|
});
|
|
511
503
|
}
|
|
512
504
|
|
|
513
|
-
// Gate: If requireLicense is enabled and no license, show only the license modal
|
|
514
|
-
if (requireLicense && isInitialized && !licenseKey) {
|
|
515
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.LicenseEntryModal, {
|
|
516
|
-
visible: showLicenseModal,
|
|
517
|
-
onClose: () => {} // Can't close without entering a license
|
|
518
|
-
,
|
|
519
|
-
onSuccess: () => setShowLicenseModal(false)
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
|
|
523
505
|
// Session blocking is handled inline by JsModal's SessionBlockedOverlay
|
|
524
506
|
// No need to replace the entire UI - tools still render, but content is blocked
|
|
525
507
|
|
|
@@ -542,7 +524,9 @@ const FloatingDevTools = ({
|
|
|
542
524
|
...props,
|
|
543
525
|
apps: finalApps,
|
|
544
526
|
environment: resolvedEnvironment
|
|
545
|
-
}), props.hidden ? null : /*#__PURE__*/(0, _jsxRuntime.
|
|
527
|
+
}), props.hidden ? null : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
528
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_AppHost.AppOverlay, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.FloatingToolHost, {})]
|
|
529
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_ToolOpenerBridge.ToolOpenerBridge, {})]
|
|
546
530
|
})
|
|
547
531
|
}), children, DebugBordersOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(DebugBordersOverlay, {}), HighlightUpdatesOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(HighlightUpdatesOverlay, {}), ImageOverlayOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(ImageOverlayOverlay, {}), PerfMonitorOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(PerfMonitorOverlay, {}), JsTopOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(JsTopOverlay, {}), ImpersonateOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(ImpersonateOverlay, {}), ScenariosOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(ScenariosOverlay, {}), ReduxAutoInstrument && /*#__PURE__*/(0, _jsxRuntime.jsx)(ReduxAutoInstrument, {}), ConsoleRoot && /*#__PURE__*/(0, _jsxRuntime.jsx)(ConsoleRoot, {}), RouteTracker && /*#__PURE__*/(0, _jsxRuntime.jsx)(RouteTracker, {}), ReactQueryTracker && /*#__PURE__*/(0, _jsxRuntime.jsx)(ReactQueryTracker, {}), toolRegistry, syncEnabled && /*#__PURE__*/(0, _jsxRuntime.jsx)(_autoExternalSync.AutoExternalSync, {
|
|
548
532
|
options: (0, _externalSyncGate.externalSyncOptions)(externalSync),
|
|
@@ -4,9 +4,11 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.FloatingDevTools = FloatingDevTools;
|
|
7
|
+
var _floating = require("@buoy-gg/shared-ui/floating");
|
|
8
|
+
var _license = require("@buoy-gg/license");
|
|
9
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
7
10
|
var _react = require("react");
|
|
8
11
|
var _floatingToolsReact = require("@buoy-gg/floating-tools-react");
|
|
9
|
-
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
10
12
|
var _autoDiscoverPresetsWeb = require("../utils/autoDiscoverPresets.web.js");
|
|
11
13
|
var _floatingToolsWeb = require("./floatingTools.web.js");
|
|
12
14
|
var _DialDevToolsWeb = require("./dial/DialDevTools.web.js");
|
|
@@ -87,7 +89,7 @@ function MenuLauncherIcon({
|
|
|
87
89
|
// Main Component
|
|
88
90
|
// =============================
|
|
89
91
|
|
|
90
|
-
function
|
|
92
|
+
function AdmittedFloatingDevTools(props) {
|
|
91
93
|
// The tool host wraps everything so dial presses can open tool components
|
|
92
94
|
// into the AppOverlay (web twin of the native AppHostProvider/AppOverlay).
|
|
93
95
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_AppHostWeb.AppHostProvider, {
|
|
@@ -372,10 +374,28 @@ function FloatingDevToolsInner({
|
|
|
372
374
|
onSettingsChange: _newSettings => {
|
|
373
375
|
// Settings are already saved by the hook
|
|
374
376
|
}
|
|
375
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_AppHostWeb.AppOverlay, {})]
|
|
377
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_AppHostWeb.AppOverlay, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_floating.FloatingToolHost, {})]
|
|
376
378
|
});
|
|
377
379
|
}
|
|
378
380
|
|
|
379
381
|
// =============================
|
|
380
382
|
// Re-exports for convenience
|
|
381
|
-
// =============================
|
|
383
|
+
// =============================
|
|
384
|
+
|
|
385
|
+
function FloatingDevTools(props) {
|
|
386
|
+
const {
|
|
387
|
+
access,
|
|
388
|
+
isPro,
|
|
389
|
+
licenseKey
|
|
390
|
+
} = (0, _license.useLicense)();
|
|
391
|
+
(0, _react.useEffect)(() => {
|
|
392
|
+
void _license.LicenseManager.configure(props.licenseKey);
|
|
393
|
+
}, [props.licenseKey]);
|
|
394
|
+
const matches = props.licenseKey === undefined || props.licenseKey.trim().toUpperCase() === (licenseKey ?? "");
|
|
395
|
+
const dev = typeof __DEV__ !== "undefined" ? __DEV__ : process.env.NODE_ENV !== "production";
|
|
396
|
+
if (!dev && !isPro) return null;
|
|
397
|
+
if (!matches || access.status !== "admitted") return props.headless ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.AccountSetup, {});
|
|
398
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(AdmittedFloatingDevTools, {
|
|
399
|
+
...props
|
|
400
|
+
});
|
|
401
|
+
}
|
|
@@ -8,7 +8,7 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
10
10
|
var _floatingTools = require("./floatingTools");
|
|
11
|
-
var _AppIcon = require("./AppIcon
|
|
11
|
+
var _AppIcon = require("./AppIcon");
|
|
12
12
|
var _DialDevTools = require("./dial/DialDevTools");
|
|
13
13
|
var _DevToolsSettingsModal = require("./DevToolsSettingsModal");
|
|
14
14
|
var _AppHost = require("./AppHost");
|
|
@@ -197,6 +197,7 @@ function ToolModalShell({
|
|
|
197
197
|
subtitle: subtitle
|
|
198
198
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader.Actions, {
|
|
199
199
|
children: [!isSearchActive && actions, searchable && !isSearchActive && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.HeaderSearchButton, {
|
|
200
|
+
variant: "chip",
|
|
200
201
|
testID: `tool-search-${app.id}`,
|
|
201
202
|
onPress: () => setIsSearchActive(true)
|
|
202
203
|
})]
|
|
@@ -9,6 +9,7 @@ exports.isExternalSyncAvailable = isExternalSyncAvailable;
|
|
|
9
9
|
exports.useToolAdapterMap = useToolAdapterMap;
|
|
10
10
|
var _react = require("react");
|
|
11
11
|
var _reactNative = require("react-native");
|
|
12
|
+
var _license = require("@buoy-gg/license");
|
|
12
13
|
var _optionalRequire = require("./optionalRequire.js");
|
|
13
14
|
var _appSyncAdapter = require("./sync/appSyncAdapter.js");
|
|
14
15
|
var _deviceIdentity = require("./deviceIdentity.js");
|
|
@@ -292,11 +293,11 @@ function AutoExternalSync({
|
|
|
292
293
|
(0, _react.useEffect)(() => {
|
|
293
294
|
if (!socket) return;
|
|
294
295
|
const emitLicense = () => {
|
|
296
|
+
if (!_license.LicenseManager.hasAccountAccess() || !socket.connected) return;
|
|
295
297
|
socket.emit("device-license", {
|
|
296
298
|
isPro: licenseRef.current.isPro ? "1" : "0",
|
|
297
299
|
// Brokers older than the tier split ignore this; newer ones prefer it.
|
|
298
|
-
tier: licenseRef.current.tier ?? (licenseRef.current.isPro ? "pro" : "
|
|
299
|
-
licenseKey: licenseRef.current.licenseKey ?? ""
|
|
300
|
+
tier: licenseRef.current.tier ?? (licenseRef.current.isPro ? "pro" : "free")
|
|
300
301
|
});
|
|
301
302
|
};
|
|
302
303
|
if (socket.connected) emitLicense();
|
|
@@ -13,7 +13,7 @@ var _DialPagination = require("./DialPagination.js");
|
|
|
13
13
|
var _dialUsageStore = require("./dialUsageStore.js");
|
|
14
14
|
var _DevToolsSettingsModal = require("../DevToolsSettingsModal");
|
|
15
15
|
var _license = require("@buoy-gg/license");
|
|
16
|
-
var _AppIcon = require("../AppIcon
|
|
16
|
+
var _AppIcon = require("../AppIcon");
|
|
17
17
|
var _AppHost = require("../AppHost");
|
|
18
18
|
var _OnboardingTooltip = require("./OnboardingTooltip");
|
|
19
19
|
var _floatingToolsCore = require("@buoy-gg/floating-tools-core");
|
|
@@ -24,6 +24,14 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
24
24
|
// the component via useWindowDimensions — a module-scope Dimensions.get
|
|
25
25
|
// snapshot goes stale when the window resizes after load (web/desktop).
|
|
26
26
|
const BUTTON_SIZE = _floatingToolsCore.DIAL_BUTTON_SIZE;
|
|
27
|
+
/** The dial face is a circle, so its centre is the middle of the measured square. */
|
|
28
|
+
const DIAL_BACKGROUND_FOCUS = {
|
|
29
|
+
x: 0.5,
|
|
30
|
+
y: 0.5
|
|
31
|
+
};
|
|
32
|
+
// Radius kept clear of grid spokes around the glass hub (its outer disc is
|
|
33
|
+
// 0.75 × BUTTON_SIZE, plus a breath).
|
|
34
|
+
const HUB_CLEARANCE = BUTTON_SIZE * 0.75 + 6;
|
|
27
35
|
|
|
28
36
|
/**
|
|
29
37
|
* The centre button's idle pulse, off by design.
|
|
@@ -100,6 +108,11 @@ const DialDevTools = ({
|
|
|
100
108
|
},
|
|
101
109
|
gridLine: {
|
|
102
110
|
width: circleSize
|
|
111
|
+
},
|
|
112
|
+
// Each spoke is two segments that stop short of the glass hub, so the
|
|
113
|
+
// six lines never meet in the middle where the background shows through.
|
|
114
|
+
spoke: {
|
|
115
|
+
width: circleSize / 2 - HUB_CLEARANCE
|
|
103
116
|
}
|
|
104
117
|
}), [circleSize]);
|
|
105
118
|
|
|
@@ -225,7 +238,7 @@ const DialDevTools = ({
|
|
|
225
238
|
iconComponent: typeof app.icon === "function" ? app.icon : undefined,
|
|
226
239
|
color: app.color ?? _sharedUi.buoyColors.primary,
|
|
227
240
|
onPress: () => {
|
|
228
|
-
//
|
|
241
|
+
// Put this tool first the next time the dial opens.
|
|
229
242
|
void (0, _dialUsageStore.recordToolUsage)(app.id);
|
|
230
243
|
|
|
231
244
|
// Call the app's onPress callback if provided, passing actions for
|
|
@@ -589,16 +602,23 @@ const DialDevTools = ({
|
|
|
589
602
|
style: styles.gridPattern,
|
|
590
603
|
children: Array.from({
|
|
591
604
|
length: 6
|
|
592
|
-
}).map((_, i) => /*#__PURE__*/(0, _jsxRuntime.
|
|
605
|
+
}).map((_, i) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
593
606
|
style: [styles.gridLine, sizeStyles.gridLine, {
|
|
594
607
|
transform: [{
|
|
595
608
|
rotate: `${i * 60}deg`
|
|
596
609
|
}]
|
|
597
|
-
}]
|
|
610
|
+
}],
|
|
611
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
612
|
+
style: [styles.spoke, sizeStyles.spoke, styles.spokeLeft]
|
|
613
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
614
|
+
style: [styles.spoke, sizeStyles.spoke, styles.spokeRight]
|
|
615
|
+
})]
|
|
598
616
|
}, i))
|
|
599
617
|
}), backgroundId === "hyperspace" ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_DialHyperspace.DialHyperspace, {
|
|
600
618
|
size: circleSize
|
|
601
|
-
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolBackground, {
|
|
619
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolBackground, {
|
|
620
|
+
focus: DIAL_BACKGROUND_FOCUS
|
|
621
|
+
})]
|
|
602
622
|
}), allDialIcons.filter(({
|
|
603
623
|
page
|
|
604
624
|
}) => page === safePage).map(({
|
|
@@ -757,9 +777,20 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
757
777
|
},
|
|
758
778
|
gridLine: {
|
|
759
779
|
position: "absolute",
|
|
780
|
+
height: 1
|
|
781
|
+
},
|
|
782
|
+
spoke: {
|
|
783
|
+
position: "absolute",
|
|
784
|
+
top: 0,
|
|
760
785
|
height: 1,
|
|
761
786
|
backgroundColor: _floatingToolsCore.dialColors.dialGridLine
|
|
762
787
|
},
|
|
788
|
+
spokeLeft: {
|
|
789
|
+
left: 0
|
|
790
|
+
},
|
|
791
|
+
spokeRight: {
|
|
792
|
+
right: 0
|
|
793
|
+
},
|
|
763
794
|
buttonContainer: {
|
|
764
795
|
zIndex: 1,
|
|
765
796
|
backgroundColor: "transparent",
|
|
@@ -770,6 +801,10 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
770
801
|
height: BUTTON_SIZE * 1.5,
|
|
771
802
|
borderRadius: BUTTON_SIZE
|
|
772
803
|
},
|
|
804
|
+
// The hub is glass: no rings, no opaque fill. The outer disc is a barely-
|
|
805
|
+
// there shade and the inner button a soft puck, so the tool background runs
|
|
806
|
+
// through the middle of the dial while BUOY / PRO stay legible. The spokes
|
|
807
|
+
// stop at HUB_CLEARANCE so nothing draws under it.
|
|
773
808
|
buttonGradient: {
|
|
774
809
|
width: "100%",
|
|
775
810
|
height: "100%",
|
|
@@ -777,7 +812,7 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
777
812
|
alignItems: "center",
|
|
778
813
|
justifyContent: "center",
|
|
779
814
|
padding: 4,
|
|
780
|
-
backgroundColor: _floatingToolsCore.dialColors.
|
|
815
|
+
backgroundColor: _floatingToolsCore.dialColors.hubHalo,
|
|
781
816
|
position: "relative",
|
|
782
817
|
overflow: "hidden"
|
|
783
818
|
},
|
|
@@ -804,14 +839,12 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
804
839
|
borderRadius: BUTTON_SIZE
|
|
805
840
|
},
|
|
806
841
|
buttonBorder: {
|
|
807
|
-
backgroundColor: _floatingToolsCore.dialColors.
|
|
842
|
+
backgroundColor: _floatingToolsCore.dialColors.hubFill,
|
|
808
843
|
alignItems: "center",
|
|
809
844
|
justifyContent: "center",
|
|
810
845
|
width: BUTTON_SIZE * 1.2,
|
|
811
846
|
height: BUTTON_SIZE * 1.2,
|
|
812
|
-
borderRadius: BUTTON_SIZE * 0.6
|
|
813
|
-
borderWidth: 2,
|
|
814
|
-
borderColor: _floatingToolsCore.dialColors.dialBorder
|
|
847
|
+
borderRadius: BUTTON_SIZE * 0.6
|
|
815
848
|
},
|
|
816
849
|
button: {
|
|
817
850
|
width: BUTTON_SIZE,
|
|
@@ -551,6 +551,8 @@ function DialMenu({
|
|
|
551
551
|
// Computed values
|
|
552
552
|
const buttonContainerSize = layout.buttonSize * _floatingToolsReact.dialStyles.centerButton.containerRatio;
|
|
553
553
|
const buttonBorderSize = layout.buttonSize * _floatingToolsReact.dialStyles.centerButton.borderRatio;
|
|
554
|
+
// Grid spokes stop this far from the centre: the hub's outer disc plus a breath.
|
|
555
|
+
const hubClearance = layout.buttonSize * _floatingToolsReact.dialStyles.centerButton.containerRatio / 2 + 6;
|
|
554
556
|
const isAnimating = entranceComplete && !isExiting;
|
|
555
557
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
556
558
|
role: "dialog",
|
|
@@ -626,10 +628,19 @@ function DialMenu({
|
|
|
626
628
|
left: 0,
|
|
627
629
|
right: 0,
|
|
628
630
|
height: _floatingToolsReact.dialStyles.grid.lineHeight,
|
|
629
|
-
backgroundColor: _floatingToolsReact.dialColors.dialGridLine,
|
|
630
631
|
transform: `rotate(${deg}deg)`,
|
|
631
632
|
transformOrigin: 'center'
|
|
632
|
-
}
|
|
633
|
+
},
|
|
634
|
+
children: ['left', 'right'].map(side => /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
635
|
+
style: {
|
|
636
|
+
position: 'absolute',
|
|
637
|
+
top: 0,
|
|
638
|
+
[side]: 0,
|
|
639
|
+
width: `calc(50% - ${hubClearance}px)`,
|
|
640
|
+
height: _floatingToolsReact.dialStyles.grid.lineHeight,
|
|
641
|
+
backgroundColor: _floatingToolsReact.dialColors.dialGridLine
|
|
642
|
+
}
|
|
643
|
+
}, side))
|
|
633
644
|
}, deg)), paddedIcons.map((icon, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(DialIconItem, {
|
|
634
645
|
icon: icon,
|
|
635
646
|
index: index,
|
|
@@ -649,7 +660,10 @@ function DialMenu({
|
|
|
649
660
|
display: 'flex',
|
|
650
661
|
alignItems: 'center',
|
|
651
662
|
justifyContent: 'center',
|
|
652
|
-
|
|
663
|
+
// Glass hub — see the native twin: a barely-there shade on the outer
|
|
664
|
+
// disc, a soft puck inside, no rings, so the tool background runs
|
|
665
|
+
// through the middle of the dial.
|
|
666
|
+
backgroundColor: _floatingToolsReact.dialColors.hubHalo,
|
|
653
667
|
borderRadius: layout.buttonSize,
|
|
654
668
|
overflow: 'hidden',
|
|
655
669
|
transform: `scale(${centerButtonScale * centerPulseScale})`,
|
|
@@ -688,8 +702,7 @@ function DialMenu({
|
|
|
688
702
|
width: buttonBorderSize,
|
|
689
703
|
height: buttonBorderSize,
|
|
690
704
|
borderRadius: layout.buttonSize * _floatingToolsReact.dialStyles.centerButton.borderRadiusRatio,
|
|
691
|
-
backgroundColor: _floatingToolsReact.dialColors.
|
|
692
|
-
border: `${_floatingToolsReact.dialStyles.centerButton.borderWidth}px solid ${_floatingToolsReact.dialColors.dialBorder}`,
|
|
705
|
+
backgroundColor: _floatingToolsReact.dialColors.hubFill,
|
|
693
706
|
display: 'flex',
|
|
694
707
|
alignItems: 'center',
|
|
695
708
|
justifyContent: 'center'
|
|
@@ -11,14 +11,8 @@ exports.resetDialUsage = resetDialUsage;
|
|
|
11
11
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
12
12
|
var _floatingToolsCore = require("@buoy-gg/floating-tools-core");
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* Wraps the pure scoring logic from `@buoy-gg/floating-tools-core` with a
|
|
17
|
-
* persisted, in-memory cache. The dial menu uses this to order tools by how
|
|
18
|
-
* recently/frequently they are used, so the most-used tools land on page 1.
|
|
19
|
-
*
|
|
20
|
-
* The cache is loaded eagerly on import so `getRankedToolIds` can run
|
|
21
|
-
* synchronously by the time the dial opens.
|
|
14
|
+
* Persists dial usage and orders tools by their last launch.
|
|
15
|
+
* The cache loads on import so opening the dial can rank tools synchronously.
|
|
22
16
|
*/
|
|
23
17
|
|
|
24
18
|
const STORAGE_KEY = _sharedUi.devToolsStorageKeys.dial.usage();
|
|
@@ -58,15 +52,15 @@ function isDialUsageLoaded() {
|
|
|
58
52
|
return loaded;
|
|
59
53
|
}
|
|
60
54
|
|
|
61
|
-
/**
|
|
62
|
-
* Rank tool ids by recency-weighted usage, highest first. Synchronous —
|
|
63
|
-
* operates against the in-memory cache. Never-used tools keep their original
|
|
64
|
-
* order as a tie-breaker.
|
|
65
|
-
*
|
|
66
|
-
* @param orderedIds - Tool ids in their default/registration order
|
|
67
|
-
*/
|
|
55
|
+
/** Most recently opened first; unused tools keep registration order. */
|
|
68
56
|
function getRankedToolIds(orderedIds) {
|
|
69
|
-
return (
|
|
57
|
+
return orderedIds.map((id, index) => ({
|
|
58
|
+
id,
|
|
59
|
+
index,
|
|
60
|
+
lastUsed: cache[id]?.lastUsed ?? 0
|
|
61
|
+
})).sort((a, b) => b.lastUsed - a.lastUsed || a.index - b.index).map(({
|
|
62
|
+
id
|
|
63
|
+
}) => id);
|
|
70
64
|
}
|
|
71
65
|
|
|
72
66
|
/**
|
|
@@ -77,7 +71,9 @@ function getRankedToolIds(orderedIds) {
|
|
|
77
71
|
async function recordToolUsage(id) {
|
|
78
72
|
if (!id) return;
|
|
79
73
|
if (!loaded) await loadDialUsage();
|
|
80
|
-
|
|
74
|
+
|
|
75
|
+
// Preserve launch order even when presses share a timestamp or the clock moves back.
|
|
76
|
+
const now = Math.max(Date.now(), ...Object.values(cache).map(entry => entry.lastUsed + 1));
|
|
81
77
|
cache = (0, _floatingToolsCore.pruneUsage)((0, _floatingToolsCore.recordUsage)(cache, id, now), now);
|
|
82
78
|
try {
|
|
83
79
|
await _sharedUi.persistentStorage.setItem(STORAGE_KEY, JSON.stringify(cache));
|