@buoy-gg/core 7.0.36 → 7.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +57 -69
  2. package/bin/buoy.js +71 -20
  3. package/lib/commonjs/Buoy.js +3 -16
  4. package/lib/commonjs/floatingMenu/AppHost.js +1 -1
  5. package/lib/commonjs/floatingMenu/AppHost.web.js +1 -1
  6. package/lib/commonjs/floatingMenu/AppIcon.web.js +53 -0
  7. package/lib/commonjs/floatingMenu/DevToolsSettingsModal.js +12 -5
  8. package/lib/commonjs/floatingMenu/DevToolsSettingsModal.web.js +14 -34
  9. package/lib/commonjs/floatingMenu/FloatingDevTools.js +32 -48
  10. package/lib/commonjs/floatingMenu/FloatingDevTools.web.js +24 -4
  11. package/lib/commonjs/floatingMenu/FloatingMenu.js +1 -1
  12. package/lib/commonjs/floatingMenu/ToolHost.js +1 -0
  13. package/lib/commonjs/floatingMenu/autoExternalSync.js +3 -2
  14. package/lib/commonjs/floatingMenu/dial/DialDevTools.js +43 -10
  15. package/lib/commonjs/floatingMenu/dial/DialDevTools.web.js +18 -5
  16. package/lib/commonjs/floatingMenu/dial/dialUsageStore.js +13 -17
  17. package/lib/commonjs/floatingMenu/dial/onboardingConstants.js +37 -65
  18. package/lib/commonjs/floatingMenu/dial/onboardingConstants.web.js +37 -29
  19. package/lib/commonjs/floatingMenu/floatingTools.js +23 -889
  20. package/lib/commonjs/floatingMenu/floatingTools.web.js +24 -514
  21. package/lib/commonjs/floatingMenu/optionalRequire.js +4 -0
  22. package/lib/commonjs/telemetry/installPing.js +7 -0
  23. package/lib/commonjs/version.js +1 -1
  24. package/lib/module/Buoy.js +3 -16
  25. package/lib/module/floatingMenu/AppHost.js +1 -1
  26. package/lib/module/floatingMenu/AppHost.web.js +1 -1
  27. package/lib/module/floatingMenu/AppIcon.web.js +47 -0
  28. package/lib/module/floatingMenu/DevToolsSettingsModal.js +13 -6
  29. package/lib/module/floatingMenu/DevToolsSettingsModal.web.js +15 -35
  30. package/lib/module/floatingMenu/FloatingDevTools.js +35 -49
  31. package/lib/module/floatingMenu/FloatingDevTools.web.js +24 -3
  32. package/lib/module/floatingMenu/FloatingMenu.js +1 -1
  33. package/lib/module/floatingMenu/ToolHost.js +1 -0
  34. package/lib/module/floatingMenu/autoExternalSync.js +3 -2
  35. package/lib/module/floatingMenu/dial/DialDevTools.js +43 -10
  36. package/lib/module/floatingMenu/dial/DialDevTools.web.js +18 -5
  37. package/lib/module/floatingMenu/dial/dialUsageStore.js +14 -18
  38. package/lib/module/floatingMenu/dial/onboardingConstants.js +1 -62
  39. package/lib/module/floatingMenu/dial/onboardingConstants.web.js +1 -25
  40. package/lib/module/floatingMenu/floatingTools.js +15 -890
  41. package/lib/module/floatingMenu/floatingTools.web.js +1 -517
  42. package/lib/module/floatingMenu/optionalRequire.js +4 -0
  43. package/lib/module/telemetry/installPing.js +7 -0
  44. package/lib/module/version.js +1 -1
  45. package/lib/typescript/commonjs/Buoy.d.ts +14 -3
  46. package/lib/typescript/commonjs/Buoy.d.ts.map +1 -1
  47. package/lib/typescript/commonjs/floatingMenu/AppIcon.web.d.ts +14 -0
  48. package/lib/typescript/commonjs/floatingMenu/AppIcon.web.d.ts.map +1 -0
  49. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
  50. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.web.d.ts.map +1 -1
  51. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts +4 -14
  52. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  53. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts +4 -19
  54. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
  55. package/lib/typescript/commonjs/floatingMenu/ToolHost.d.ts.map +1 -1
  56. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts +3 -4
  57. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts.map +1 -1
  58. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  59. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.web.d.ts.map +1 -1
  60. package/lib/typescript/commonjs/floatingMenu/dial/dialUsageStore.d.ts +3 -15
  61. package/lib/typescript/commonjs/floatingMenu/dial/dialUsageStore.d.ts.map +1 -1
  62. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts +1 -29
  63. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts.map +1 -1
  64. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.web.d.ts +1 -12
  65. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.web.d.ts.map +1 -1
  66. package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts +6 -55
  67. package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts.map +1 -1
  68. package/lib/typescript/commonjs/floatingMenu/floatingTools.web.d.ts +3 -53
  69. package/lib/typescript/commonjs/floatingMenu/floatingTools.web.d.ts.map +1 -1
  70. package/lib/typescript/commonjs/floatingMenu/optionalRequire.d.ts.map +1 -1
  71. package/lib/typescript/commonjs/telemetry/installPing.d.ts +8 -1
  72. package/lib/typescript/commonjs/telemetry/installPing.d.ts.map +1 -1
  73. package/lib/typescript/commonjs/version.d.ts +1 -1
  74. package/lib/typescript/module/Buoy.d.ts +14 -3
  75. package/lib/typescript/module/Buoy.d.ts.map +1 -1
  76. package/lib/typescript/module/floatingMenu/AppIcon.web.d.ts +14 -0
  77. package/lib/typescript/module/floatingMenu/AppIcon.web.d.ts.map +1 -0
  78. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
  79. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.web.d.ts.map +1 -1
  80. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts +4 -14
  81. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  82. package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts +4 -19
  83. package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
  84. package/lib/typescript/module/floatingMenu/ToolHost.d.ts.map +1 -1
  85. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts +3 -4
  86. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts.map +1 -1
  87. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  88. package/lib/typescript/module/floatingMenu/dial/DialDevTools.web.d.ts.map +1 -1
  89. package/lib/typescript/module/floatingMenu/dial/dialUsageStore.d.ts +3 -15
  90. package/lib/typescript/module/floatingMenu/dial/dialUsageStore.d.ts.map +1 -1
  91. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts +1 -29
  92. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts.map +1 -1
  93. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.web.d.ts +1 -12
  94. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.web.d.ts.map +1 -1
  95. package/lib/typescript/module/floatingMenu/floatingTools.d.ts +6 -55
  96. package/lib/typescript/module/floatingMenu/floatingTools.d.ts.map +1 -1
  97. package/lib/typescript/module/floatingMenu/floatingTools.web.d.ts +3 -53
  98. package/lib/typescript/module/floatingMenu/floatingTools.web.d.ts.map +1 -1
  99. package/lib/typescript/module/floatingMenu/optionalRequire.d.ts.map +1 -1
  100. package/lib/typescript/module/telemetry/installPing.d.ts +8 -1
  101. package/lib/typescript/module/telemetry/installPing.d.ts.map +1 -1
  102. package/lib/typescript/module/version.d.ts +1 -1
  103. 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
- // native: SentryBugIcon colorPreset="red" noBackground
302
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(AlertTriangle, {
282
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.SentryIcon, {
303
283
  size: size,
304
- color: night.danger
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: #0C0C0C, radius 14, 1px #1A1A1C, clipped */
420
+ /** native NightCard glass: 50% black, radius 14, 1px white-alpha hairline, clipped */
441
421
  const cardStyle = {
442
- backgroundColor: night.surface,
422
+ backgroundColor: night.glass,
443
423
  borderRadius: 14,
444
- border: `1px solid ${night.border}`,
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 = night bg at
790
- // radius 10 / padding 3; active thumb = surfaceElevated at radius 8 with
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.bg,
785
+ backgroundColor: night.glass,
806
786
  borderRadius: 10,
807
- border: `1px solid ${night.border}`,
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.surfaceElevated : 'transparent',
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.surfaceElevated,
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.js");
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 = false,
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
- // Get full license state for requireLicense gating
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 the license to resolve so the tier it reports is
154
- // the real one rather than the "anonymous" the manager starts at.
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
- if (licenseKeyProp && licenseKeyProp.trim() !== "") {
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
- }, []); // eslint-disable-line react-hooks/exhaustive-deps
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
- // Both untiered surfaces below it — anonymous (no key) and free (a key on the
458
- // Free policy) — stay dev-only. `isLicensed` is safe to gate on because
459
- // `isPro` is a strict mirror of `tier === "pro"`, so a free key reports false
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
- // Anonymous or free user in production - don't render DevTools.
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. requireLicense is intentionally skipped — there's nothing
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.jsx)(_AppHost.AppOverlay, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_ToolOpenerBridge.ToolOpenerBridge, {})]
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 FloatingDevTools(props) {
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.js");
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" : "anonymous"),
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.js");
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
- // Record usage so frequently/recently used tools rank toward page 1.
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.jsx)(_reactNative.View, {
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.dialBackground,
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.dialGridLine,
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
- backgroundColor: _floatingToolsReact.dialColors.dialBackground,
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.dialGridLine,
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
- * Dial Usage Store - persists and ranks dial tool usage.
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 (0, _floatingToolsCore.rankToolIds)(orderedIds, cache, Date.now());
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
- const now = Date.now();
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));