@buoy-gg/core 7.0.35 → 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.
Files changed (111) hide show
  1. package/README.md +57 -66
  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/MinimizedToolsStack.js +83 -4
  13. package/lib/commonjs/floatingMenu/MinimizedToolsStack.web.js +90 -6
  14. package/lib/commonjs/floatingMenu/ToolHost.js +1 -0
  15. package/lib/commonjs/floatingMenu/autoExternalSync.js +3 -2
  16. package/lib/commonjs/floatingMenu/dial/DialDevTools.js +43 -10
  17. package/lib/commonjs/floatingMenu/dial/DialDevTools.web.js +18 -5
  18. package/lib/commonjs/floatingMenu/dial/dialUsageStore.js +13 -17
  19. package/lib/commonjs/floatingMenu/dial/onboardingConstants.js +37 -65
  20. package/lib/commonjs/floatingMenu/dial/onboardingConstants.web.js +37 -29
  21. package/lib/commonjs/floatingMenu/floatingTools.js +23 -889
  22. package/lib/commonjs/floatingMenu/floatingTools.web.js +24 -514
  23. package/lib/commonjs/floatingMenu/optionalRequire.js +4 -0
  24. package/lib/commonjs/telemetry/installPing.js +7 -0
  25. package/lib/commonjs/version.js +1 -1
  26. package/lib/module/Buoy.js +3 -16
  27. package/lib/module/floatingMenu/AppHost.js +1 -1
  28. package/lib/module/floatingMenu/AppHost.web.js +1 -1
  29. package/lib/module/floatingMenu/AppIcon.web.js +47 -0
  30. package/lib/module/floatingMenu/DevToolsSettingsModal.js +13 -6
  31. package/lib/module/floatingMenu/DevToolsSettingsModal.web.js +15 -35
  32. package/lib/module/floatingMenu/FloatingDevTools.js +35 -49
  33. package/lib/module/floatingMenu/FloatingDevTools.web.js +24 -3
  34. package/lib/module/floatingMenu/FloatingMenu.js +1 -1
  35. package/lib/module/floatingMenu/MinimizedToolsStack.js +88 -8
  36. package/lib/module/floatingMenu/MinimizedToolsStack.web.js +92 -8
  37. package/lib/module/floatingMenu/ToolHost.js +1 -0
  38. package/lib/module/floatingMenu/autoExternalSync.js +3 -2
  39. package/lib/module/floatingMenu/dial/DialDevTools.js +43 -10
  40. package/lib/module/floatingMenu/dial/DialDevTools.web.js +18 -5
  41. package/lib/module/floatingMenu/dial/dialUsageStore.js +14 -18
  42. package/lib/module/floatingMenu/dial/onboardingConstants.js +1 -62
  43. package/lib/module/floatingMenu/dial/onboardingConstants.web.js +1 -25
  44. package/lib/module/floatingMenu/floatingTools.js +15 -890
  45. package/lib/module/floatingMenu/floatingTools.web.js +1 -517
  46. package/lib/module/floatingMenu/optionalRequire.js +4 -0
  47. package/lib/module/telemetry/installPing.js +7 -0
  48. package/lib/module/version.js +1 -1
  49. package/lib/typescript/commonjs/Buoy.d.ts +14 -3
  50. package/lib/typescript/commonjs/Buoy.d.ts.map +1 -1
  51. package/lib/typescript/commonjs/floatingMenu/AppIcon.web.d.ts +14 -0
  52. package/lib/typescript/commonjs/floatingMenu/AppIcon.web.d.ts.map +1 -0
  53. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
  54. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.web.d.ts.map +1 -1
  55. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts +4 -14
  56. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts +4 -19
  58. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
  59. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.d.ts.map +1 -1
  60. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.web.d.ts.map +1 -1
  61. package/lib/typescript/commonjs/floatingMenu/ToolHost.d.ts.map +1 -1
  62. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts +3 -4
  63. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts.map +1 -1
  64. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  65. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.web.d.ts.map +1 -1
  66. package/lib/typescript/commonjs/floatingMenu/dial/dialUsageStore.d.ts +3 -15
  67. package/lib/typescript/commonjs/floatingMenu/dial/dialUsageStore.d.ts.map +1 -1
  68. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts +1 -29
  69. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts.map +1 -1
  70. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.web.d.ts +1 -12
  71. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.web.d.ts.map +1 -1
  72. package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts +6 -55
  73. package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts.map +1 -1
  74. package/lib/typescript/commonjs/floatingMenu/floatingTools.web.d.ts +3 -53
  75. package/lib/typescript/commonjs/floatingMenu/floatingTools.web.d.ts.map +1 -1
  76. package/lib/typescript/commonjs/floatingMenu/optionalRequire.d.ts.map +1 -1
  77. package/lib/typescript/commonjs/telemetry/installPing.d.ts +8 -1
  78. package/lib/typescript/commonjs/telemetry/installPing.d.ts.map +1 -1
  79. package/lib/typescript/commonjs/version.d.ts +1 -1
  80. package/lib/typescript/module/Buoy.d.ts +14 -3
  81. package/lib/typescript/module/Buoy.d.ts.map +1 -1
  82. package/lib/typescript/module/floatingMenu/AppIcon.web.d.ts +14 -0
  83. package/lib/typescript/module/floatingMenu/AppIcon.web.d.ts.map +1 -0
  84. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
  85. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.web.d.ts.map +1 -1
  86. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts +4 -14
  87. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  88. package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts +4 -19
  89. package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
  90. package/lib/typescript/module/floatingMenu/MinimizedToolsStack.d.ts.map +1 -1
  91. package/lib/typescript/module/floatingMenu/MinimizedToolsStack.web.d.ts.map +1 -1
  92. package/lib/typescript/module/floatingMenu/ToolHost.d.ts.map +1 -1
  93. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts +3 -4
  94. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts.map +1 -1
  95. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  96. package/lib/typescript/module/floatingMenu/dial/DialDevTools.web.d.ts.map +1 -1
  97. package/lib/typescript/module/floatingMenu/dial/dialUsageStore.d.ts +3 -15
  98. package/lib/typescript/module/floatingMenu/dial/dialUsageStore.d.ts.map +1 -1
  99. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts +1 -29
  100. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts.map +1 -1
  101. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.web.d.ts +1 -12
  102. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.web.d.ts.map +1 -1
  103. package/lib/typescript/module/floatingMenu/floatingTools.d.ts +6 -55
  104. package/lib/typescript/module/floatingMenu/floatingTools.d.ts.map +1 -1
  105. package/lib/typescript/module/floatingMenu/floatingTools.web.d.ts +3 -53
  106. package/lib/typescript/module/floatingMenu/floatingTools.web.d.ts.map +1 -1
  107. package/lib/typescript/module/floatingMenu/optionalRequire.d.ts.map +1 -1
  108. package/lib/typescript/module/telemetry/installPing.d.ts +8 -1
  109. package/lib/typescript/module/telemetry/installPing.d.ts.map +1 -1
  110. package/lib/typescript/module/version.d.ts +1 -1
  111. 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");
@@ -10,9 +10,50 @@ var _sharedUi = require("@buoy-gg/shared-ui");
10
10
  var _MinimizedToolsContext = require("./MinimizedToolsContext.js");
11
11
  var _jsxRuntime = require("react/jsx-runtime");
12
12
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
13
+ /**
14
+ * A hidden tool's "I need you". See `setToolAttention` in shared-ui — the tool
15
+ * publishes, the rail draws. Read through useSyncExternalStore so a badge set
16
+ * from inside a minimized sheet lands on the chip without the rail polling.
17
+ */function useToolAttention(toolId) {
18
+ return (0, _react.useSyncExternalStore)(_sharedUi.subscribeToolAttention, () => (0, _sharedUi.getToolAttention)(toolId), () => (0, _sharedUi.getToolAttention)(toolId));
19
+ }
20
+ const ATTENTION_COLORS = {
21
+ needsInput: "#FF8329",
22
+ done: "#20C997",
23
+ failed: "#FF3B30"
24
+ };
25
+
26
+ /** needsInput outranks failed outranks done — the peek shows the one that matters most. */
27
+ const ATTENTION_RANK = {
28
+ needsInput: 3,
29
+ failed: 2,
30
+ done: 1
31
+ };
32
+
33
+ /** The strongest attention across every minimized tool, for the folded peek. */
34
+ function useStrongestAttention(toolIds) {
35
+ const key = toolIds.join("|");
36
+ return (0, _react.useSyncExternalStore)(_sharedUi.subscribeToolAttention, () => strongest(key), () => strongest(key));
37
+ }
38
+ const strongestCache = new Map();
39
+ function strongest(key) {
40
+ let best = null;
41
+ for (const id of key ? key.split("|") : []) {
42
+ const a = (0, _sharedUi.getToolAttention)(id);
43
+ if (a && (!best || ATTENTION_RANK[a.kind] > ATTENTION_RANK[best.kind])) best = a;
44
+ }
45
+ // Same value → same object, or useSyncExternalStore re-renders forever.
46
+ const prev = strongestCache.get(key);
47
+ if (prev && best && prev.kind === best.kind && prev.label === best.label) return prev;
48
+ if (!prev && !best) return null;
49
+ strongestCache.set(key, best);
50
+ return best;
51
+ }
52
+
13
53
  // ============================================================================
14
54
  // Constants
15
55
  // ============================================================================
56
+
16
57
  const PEEK_HEIGHT = 28;
17
58
  const TOOLBAR_WIDTH = 44;
18
59
  const TOOL_ITEM_SIZE = 32;
@@ -35,6 +76,7 @@ function GlitchToolButton({
35
76
  onSelect,
36
77
  index
37
78
  }) {
79
+ const attention = useToolAttention(tool.id);
38
80
  const glitchX = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
39
81
  const glitchOpacity = (0, _react.useRef)(new _reactNative.Animated.Value(1)).current;
40
82
  const glitchScale = (0, _react.useRef)(new _reactNative.Animated.Value(1)).current;
@@ -126,14 +168,14 @@ function GlitchToolButton({
126
168
  clearTimeout(timeoutId);
127
169
  };
128
170
  }, [glitchX, glitchOpacity, glitchScale, index]);
129
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
171
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
130
172
  onPress: () => onSelect(tool),
131
173
  activeOpacity: 0.7,
132
174
  style: styles.toolButton,
133
- accessibilityLabel: `Restore ${tool.title}`,
175
+ accessibilityLabel: attention ? `${tool.title} — ${attention.label}. Restore` : `Restore ${tool.title}`,
134
176
  accessibilityRole: "button",
135
177
  testID: `buoy-dock-restore-${tool.id}`,
136
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
178
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
137
179
  style: {
138
180
  opacity: glitchOpacity,
139
181
  transform: [{
@@ -143,7 +185,17 @@ function GlitchToolButton({
143
185
  }]
144
186
  },
145
187
  children: tool.icon
146
- })
188
+ }), attention ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
189
+ style: [styles.badge, {
190
+ backgroundColor: ATTENTION_COLORS[attention.kind]
191
+ }],
192
+ testID: `buoy-dock-badge-${tool.id}-${attention.kind}`,
193
+ pointerEvents: "none",
194
+ children: attention.kind === "needsInput" ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
195
+ style: styles.badgeText,
196
+ children: "!"
197
+ }) : null
198
+ }) : null]
147
199
  });
148
200
  }
149
201
 
@@ -163,6 +215,7 @@ function MinimizedToolsStack({
163
215
  minimizedTools,
164
216
  restore
165
217
  } = (0, _MinimizedToolsContext.useMinimizedTools)();
218
+ const peekAttention = useStrongestAttention(minimizedTools.map(t => t.id));
166
219
 
167
220
  // Calculate expanded height based on number of tools
168
221
  const expandedHeight = TOOLBAR_PADDING + minimizedTools.length * TOOL_ITEM_SIZE + (minimizedTools.length - 1) * ICON_GAP + ICON_GAP + COLLAPSE_BUTTON_SIZE + 4;
@@ -186,6 +239,11 @@ function MinimizedToolsStack({
186
239
  collapsedLabel: `Show ${minimizedTools.length} minimized tools`,
187
240
  testID: "buoy-dock-peek",
188
241
  collapseTestID: "buoy-dock-collapse",
242
+ attention: peekAttention ? {
243
+ color: ATTENTION_COLORS[peekAttention.kind],
244
+ glyph: peekAttention.kind === "needsInput" ? "!" : undefined,
245
+ label: peekAttention.label
246
+ } : null,
189
247
  children: [...minimizedTools].reverse().map((tool, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(GlitchToolButton, {
190
248
  tool: tool,
191
249
  onSelect: handleToolPress,
@@ -205,5 +263,26 @@ const styles = _reactNative.StyleSheet.create({
205
263
  alignItems: "center",
206
264
  justifyContent: "center",
207
265
  backgroundColor: "transparent"
266
+ },
267
+ // Top-right of the chip. Needs-input carries a glyph; done/failed are a dot —
268
+ // the colour says which, the glyph says "act".
269
+ badge: {
270
+ position: "absolute",
271
+ top: 0,
272
+ right: 0,
273
+ minWidth: 12,
274
+ height: 12,
275
+ borderRadius: 6,
276
+ paddingHorizontal: 2,
277
+ alignItems: "center",
278
+ justifyContent: "center",
279
+ borderWidth: 1.5,
280
+ borderColor: "#0C0C0C"
281
+ },
282
+ badgeText: {
283
+ color: "#000",
284
+ fontSize: 8,
285
+ fontWeight: "800",
286
+ lineHeight: 9
208
287
  }
209
288
  });
@@ -24,6 +24,39 @@ var _jsxRuntime = require("react/jsx-runtime");
24
24
  * used before this replaced it.
25
25
  */
26
26
 
27
+ /** Twin of the native hook — see MinimizedToolsStack.tsx. */
28
+ function useToolAttention(toolId) {
29
+ return (0, _react.useSyncExternalStore)(_sharedUi.subscribeToolAttention, () => (0, _sharedUi.getToolAttention)(toolId), () => (0, _sharedUi.getToolAttention)(toolId));
30
+ }
31
+ const ATTENTION_COLORS = {
32
+ needsInput: '#FF8329',
33
+ done: '#20C997',
34
+ failed: '#FF3B30'
35
+ };
36
+ const ATTENTION_RANK = {
37
+ needsInput: 3,
38
+ failed: 2,
39
+ done: 1
40
+ };
41
+ const strongestCache = new Map();
42
+ function strongest(key) {
43
+ let best = null;
44
+ for (const id of key ? key.split('|') : []) {
45
+ const a = (0, _sharedUi.getToolAttention)(id);
46
+ if (a && (!best || ATTENTION_RANK[a.kind] > ATTENTION_RANK[best.kind])) best = a;
47
+ }
48
+ const prev = strongestCache.get(key);
49
+ if (prev && best && prev.kind === best.kind && prev.label === best.label) return prev;
50
+ if (!prev && !best) return null;
51
+ strongestCache.set(key, best);
52
+ return best;
53
+ }
54
+ /** Twin of the native hook: the strongest attention across the folded dock. */
55
+ function useStrongestAttention(toolIds) {
56
+ const key = toolIds.join('|');
57
+ return (0, _react.useSyncExternalStore)(_sharedUi.subscribeToolAttention, () => strongest(key), () => strongest(key));
58
+ }
59
+
27
60
  // native MinimizedToolsStack.tsx constants
28
61
  const PEEK_HEIGHT = 28;
29
62
  const TOOL_ITEM_SIZE = 32;
@@ -61,11 +94,13 @@ function ChevronGlyph({
61
94
  });
62
95
  }
63
96
  function GlitchToolButton({
97
+ id,
64
98
  title,
65
99
  icon,
66
100
  index,
67
101
  onPress
68
102
  }) {
103
+ const attention = useToolAttention(id);
69
104
  const innerRef = (0, _react.useRef)(null);
70
105
  (0, _react.useEffect)(() => {
71
106
  let mounted = true;
@@ -116,9 +151,9 @@ function GlitchToolButton({
116
151
  clearTimeout(timer);
117
152
  };
118
153
  }, [index]);
119
- return /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
154
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)("button", {
120
155
  type: "button",
121
- "aria-label": `Restore ${title}`,
156
+ "aria-label": attention ? `${title} — ${attention.label}. Restore` : `Restore ${title}`,
122
157
  onClick: onPress,
123
158
  style: {
124
159
  width: TOOL_ITEM_SIZE,
@@ -129,9 +164,10 @@ function GlitchToolButton({
129
164
  background: 'none',
130
165
  border: 'none',
131
166
  padding: 0,
132
- cursor: 'pointer'
167
+ cursor: 'pointer',
168
+ position: 'relative'
133
169
  },
134
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
170
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
135
171
  ref: innerRef,
136
172
  style: {
137
173
  display: 'flex',
@@ -139,7 +175,30 @@ function GlitchToolButton({
139
175
  justifyContent: 'center'
140
176
  },
141
177
  children: icon
142
- })
178
+ }), attention ? /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
179
+ "aria-hidden": true,
180
+ "data-testid": `buoy-dock-badge-${id}-${attention.kind}`,
181
+ style: {
182
+ position: 'absolute',
183
+ top: 0,
184
+ right: 0,
185
+ minWidth: 12,
186
+ height: 12,
187
+ borderRadius: 6,
188
+ padding: '0 2px',
189
+ boxSizing: 'border-box',
190
+ background: ATTENTION_COLORS[attention.kind],
191
+ border: '1.5px solid #0C0C0C',
192
+ color: '#000',
193
+ fontSize: 8,
194
+ fontWeight: 800,
195
+ lineHeight: '9px',
196
+ display: 'flex',
197
+ alignItems: 'center',
198
+ justifyContent: 'center'
199
+ },
200
+ children: attention.kind === 'needsInput' ? '!' : ''
201
+ }) : null]
143
202
  });
144
203
  }
145
204
  function MinimizedToolsStack({
@@ -152,6 +211,7 @@ function MinimizedToolsStack({
152
211
  const minimized = openApps.filter(app => app.minimized && app.launchMode !== 'toggle-only');
153
212
  const [isExpanded, setIsExpanded] = (0, _react.useState)(false);
154
213
  const [isStateRestored, setIsStateRestored] = (0, _react.useState)(false);
214
+ const peekAttention = useStrongestAttention(minimized.map(app => app.id));
155
215
 
156
216
  // Restore + persist the expanded flag (native ExpandablePopover persistState).
157
217
  (0, _react.useEffect)(() => {
@@ -219,6 +279,7 @@ function MinimizedToolsStack({
219
279
  boxSizing: 'border-box'
220
280
  },
221
281
  children: [[...minimized].reverse().map((app, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(GlitchToolButton, {
282
+ id: app.id,
222
283
  title: app.title || app.id,
223
284
  icon: app.icon ?? /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
224
285
  style: {
@@ -273,7 +334,30 @@ function MinimizedToolsStack({
273
334
  pointerEvents: isExpanded ? 'none' : 'auto',
274
335
  ...frame
275
336
  },
276
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(ChevronGlyph, {}), minimized.length > 1 && /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
337
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(ChevronGlyph, {}), peekAttention ? /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
338
+ "aria-hidden": true,
339
+ "data-testid": `buoy-dock-peek-attention`,
340
+ style: {
341
+ position: 'absolute',
342
+ top: 2,
343
+ right: 2,
344
+ minWidth: 11,
345
+ height: 11,
346
+ borderRadius: 6,
347
+ padding: '0 2px',
348
+ boxSizing: 'border-box',
349
+ background: ATTENTION_COLORS[peekAttention.kind],
350
+ border: '1.5px solid #0C0C0C',
351
+ color: '#000',
352
+ fontSize: 8,
353
+ fontWeight: 800,
354
+ lineHeight: '9px',
355
+ display: 'flex',
356
+ alignItems: 'center',
357
+ justifyContent: 'center'
358
+ },
359
+ children: peekAttention.kind === 'needsInput' ? '!' : ''
360
+ }) : null, minimized.length > 1 && /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
277
361
  style: {
278
362
  fontSize: 9,
279
363
  fontWeight: 600,
@@ -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();