@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
@@ -4,10 +4,10 @@ import { useState, useEffect, useCallback, useMemo } from "react";
4
4
  import { View, Text, StyleSheet, ScrollView, TouchableOpacity, Dimensions, Platform } from "react-native";
5
5
  import { settingsBus } from "./settingsBus.js";
6
6
  import { useDesktopSyncStatus } from "./useDesktopSyncStatus.js";
7
- import { SentryBugIcon, WifiCircuitIcon, ImageOverlayIcon, RenderCountIcon, Info, Layers, ChevronRightIcon, ChevronDown, getStorageBackendType, persistentStorage, Trash2, CheckCircle2, LicenseEntryModal, BackgroundSwitcher, night, NightCard, NightRows, NightSectionLabel, NightFootnote, NightSwitch, NightButton, NightSegmented, NightBadge, NightSeparator, monoFont } from "@buoy-gg/shared-ui";
7
+ import { SentryIcon, WifiCircuitIcon, ImageOverlayIcon, RenderCountIcon, Info, Layers, ChevronRightIcon, ChevronDown, getStorageBackendType, persistentStorage, Trash2, CheckCircle2, LicenseEntryModal, BackgroundSwitcher, night, NightCard, NightRows, NightSectionLabel, NightFootnote, NightSwitch, NightButton, NightSegmented, NightBadge, NightSeparator, monoFont } from "@buoy-gg/shared-ui";
8
8
  import { EnvIcon, StorageIcon, RoutesIcon, NetworkIcon, QueryIcon, HighlighterIcon, ReduxIcon, EventsIcon } from "@buoy-gg/floating-tools-core";
9
9
  import { useDefaultConfig } from "./DefaultConfigContext.js";
10
- import { renderAppIcon } from "./AppIcon.js";
10
+ import { renderAppIcon } from "./AppIcon";
11
11
  import { JsModal, devToolsStorageKeys } from "@buoy-gg/shared-ui";
12
12
  import { useSafeAreaInsets } from "@buoy-gg/shared-ui";
13
13
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
@@ -374,10 +374,9 @@ export const DevToolsSettingsModal = ({
374
374
  size: 18
375
375
  });
376
376
  case "sentry":
377
- return /*#__PURE__*/_jsx(SentryBugIcon, {
377
+ return /*#__PURE__*/_jsx(SentryIcon, {
378
378
  size: 18,
379
- colorPreset: "red",
380
- noBackground: true
379
+ color: "#9945FF"
381
380
  });
382
381
  case "storage":
383
382
  return /*#__PURE__*/_jsx(StorageIcon, {
@@ -595,6 +594,7 @@ export const DevToolsSettingsModal = ({
595
594
  contentContainerStyle: styles.scrollContainer,
596
595
  children: [activeTab === "floating" && /*#__PURE__*/_jsx(_Fragment, {
597
596
  children: renderSection("Floating Tools", /*#__PURE__*/_jsx(NightCard, {
597
+ glass: true,
598
598
  children: /*#__PURE__*/_jsx(NightRows, {
599
599
  children: Object.entries(settings.floatingTools).map(([key, value]) => renderToolRow(key, value, false, () => toggleFloatingTool(key)))
600
600
  })
@@ -603,11 +603,13 @@ export const DevToolsSettingsModal = ({
603
603
  }))
604
604
  }), activeTab === "settings" && /*#__PURE__*/_jsxs(_Fragment, {
605
605
  children: [renderSection("Background", /*#__PURE__*/_jsx(NightCard, {
606
+ glass: true,
606
607
  padded: true,
607
608
  children: /*#__PURE__*/_jsx(BackgroundSwitcher, {
608
609
  embedded: true
609
610
  })
610
611
  })), renderSection("Storage", /*#__PURE__*/_jsxs(NightCard, {
612
+ glass: true,
611
613
  children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
612
614
  activeOpacity: 0.7,
613
615
  onPress: () => setIsStorageExpanded(!isStorageExpanded),
@@ -688,6 +690,7 @@ export const DevToolsSettingsModal = ({
688
690
  })]
689
691
  })]
690
692
  })), desktopSyncStatus && desktopSyncStatus.state !== "idle" && renderSection("Desktop Sync", /*#__PURE__*/_jsxs(NightCard, {
693
+ glass: true,
691
694
  children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
692
695
  activeOpacity: 0.7,
693
696
  onPress: () => toggleSettingExpanded("desktopSync"),
@@ -729,12 +732,14 @@ export const DevToolsSettingsModal = ({
729
732
  })]
730
733
  })]
731
734
  })), renderSection("Modal Behavior", /*#__PURE__*/_jsx(NightCard, {
735
+ glass: true,
732
736
  children: /*#__PURE__*/_jsxs(NightRows, {
733
737
  children: [renderGlobalSettingRow("enableSharedModalDimensions", "Shared Modal Size", "Sync dimensions across all tools", "When enabled, all tool modals will share the same size and position. Resizing one modal will affect all others. When disabled, each tool remembers its own size and position independently.", "Keep OFF for the best experience. This allows you to customize each tool's modal size separately."), Platform.OS !== "web" ? renderGlobalSettingRow("expandableWindowControls", "Expand Controls", "iPad-style expandable window buttons", "When enabled, the window control buttons (minimize, toggle mode, close) start as small dots and expand into larger, easy-to-tap buttons when pressed. When disabled, buttons are directly tappable at their small size.", "Keep ON for touch devices where the small buttons are hard to press. Turn OFF if you prefer direct single-tap access.") : null]
734
738
  })
735
739
  }))]
736
740
  }), activeTab === "pro" && /*#__PURE__*/_jsxs(_Fragment, {
737
741
  children: [renderSection("License", /*#__PURE__*/_jsx(NightCard, {
742
+ glass: true,
738
743
  children: /*#__PURE__*/_jsxs(View, {
739
744
  style: styles.settingRow,
740
745
  children: [/*#__PURE__*/_jsx(View, {
@@ -755,6 +760,7 @@ export const DevToolsSettingsModal = ({
755
760
  children: isPro ? "You have full access to all Buoy DevTools features." : licenseError ? licenseErrorIsTransport ? `Couldn't reach the license server (${licenseError}). A VPN or corporate proxy can block api.keygen.sh — turn it off and reload. Your key hasn't been rejected.` : `License check failed: ${licenseError}.` : "Upgrade to Pro to unlock all features and support development."
756
761
  })), !isPro && /*#__PURE__*/_jsxs(_Fragment, {
757
762
  children: [renderSection("Pro Features", /*#__PURE__*/_jsx(NightCard, {
763
+ glass: true,
758
764
  children: /*#__PURE__*/_jsx(NightRows, {
759
765
  children: ["Advanced Settings", "Priority Support"].map(feature => /*#__PURE__*/_jsxs(View, {
760
766
  style: styles.featureRow,
@@ -790,6 +796,7 @@ export const DevToolsSettingsModal = ({
790
796
  customContent: /*#__PURE__*/_jsx(View, {
791
797
  style: styles.headerRow,
792
798
  children: /*#__PURE__*/_jsx(NightSegmented, {
799
+ glass: true,
793
800
  tabs: [{
794
801
  key: "floating",
795
802
  label: "Floating"
@@ -1079,7 +1086,7 @@ const styles = StyleSheet.create({
1079
1086
  maxHeight: 150
1080
1087
  },
1081
1088
  storageKeyItem: {
1082
- backgroundColor: night.color.surfaceElevated,
1089
+ backgroundColor: night.color.glassElevated,
1083
1090
  borderRadius: night.radius.segment,
1084
1091
  paddingHorizontal: 10,
1085
1092
  paddingVertical: 7,
@@ -27,7 +27,7 @@ getToolLabel, getToolDescription,
27
27
  globalSettingsConfig
28
28
  // Types
29
29
  } from '@buoy-gg/floating-tools-react';
30
- import { QueryIcon, EnvIcon, StorageIcon, NetworkIcon, RoutesIcon, HighlighterIcon, ReduxIcon, EventsIcon, WifiIcon, ImageOverlayIcon, ZustandIcon, JotaiIcon } from '@buoy-gg/floating-tools-core';
30
+ import { QueryIcon, SentryIcon, EnvIcon, StorageIcon, NetworkIcon, RoutesIcon, HighlighterIcon, ReduxIcon, EventsIcon, WifiIcon, ImageOverlayIcon, ZustandIcon, JotaiIcon } from '@buoy-gg/floating-tools-core';
31
31
  // JsModal.web + storage keys via the consumer's shared-ui web shim — the
32
32
  // settings sheet is a real JsModal on web now, same as native. The
33
33
  // BackgroundSwitcher is the same RN component native mounts: it renders under
@@ -61,6 +61,9 @@ const night = {
61
61
  surfaceElevated: '#161618',
62
62
  border: '#1A1A1C',
63
63
  separator: 'rgba(255, 255, 255, 0.08)',
64
+ glass: 'rgba(5, 5, 5, 0.50)',
65
+ glassElevated: 'rgba(255, 255, 255, 0.06)',
66
+ glassBorder: 'rgba(255, 255, 255, 0.07)',
64
67
  fillTertiary: 'rgba(255, 255, 255, 0.12)',
65
68
  buttonSurface: 'rgba(8, 10, 14, 0.85)',
66
69
  button: '#1C1C1C',
@@ -147,28 +150,6 @@ const Sparkles = ({
147
150
  })]
148
151
  });
149
152
 
150
- /** Fallback for shared-ui SentryBugIcon (native sentry row icon, red). */
151
- const AlertTriangle = ({
152
- size = 24,
153
- color = 'currentColor'
154
- }) => /*#__PURE__*/_jsxs("svg", {
155
- width: size,
156
- height: size,
157
- viewBox: "0 0 24 24",
158
- fill: "none",
159
- stroke: color,
160
- strokeWidth: "2",
161
- strokeLinecap: "round",
162
- strokeLinejoin: "round",
163
- children: [/*#__PURE__*/_jsx("path", {
164
- d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"
165
- }), /*#__PURE__*/_jsx("path", {
166
- d: "M12 9v4"
167
- }), /*#__PURE__*/_jsx("path", {
168
- d: "M12 17h.01"
169
- })]
170
- });
171
-
172
153
  /** shared-ui Info icon (native default tool icon). */
173
154
  const Info = ({
174
155
  size = 24,
@@ -305,10 +286,9 @@ function ToolIcon({
305
286
  size: size
306
287
  });
307
288
  case 'sentry':
308
- // native: SentryBugIcon colorPreset="red" noBackground
309
- return /*#__PURE__*/_jsx(AlertTriangle, {
289
+ return /*#__PURE__*/_jsx(SentryIcon, {
310
290
  size: size,
311
- color: night.danger
291
+ color: "#9945FF"
312
292
  });
313
293
  case 'storage':
314
294
  // native passes bgColor=night surface for the BIF knock-out parts
@@ -444,11 +424,11 @@ function Footnote({
444
424
  });
445
425
  }
446
426
 
447
- /** native NightCard: #0C0C0C, radius 14, 1px #1A1A1C, clipped */
427
+ /** native NightCard glass: 50% black, radius 14, 1px white-alpha hairline, clipped */
448
428
  const cardStyle = {
449
- backgroundColor: night.surface,
429
+ backgroundColor: night.glass,
450
430
  borderRadius: 14,
451
- border: `1px solid ${night.border}`,
431
+ border: `1px solid ${night.glassBorder}`,
452
432
  overflow: 'hidden'
453
433
  };
454
434
 
@@ -793,8 +773,8 @@ export function SettingsModal({
793
773
  const displayTabs = settingsTabs.filter(tab => tab.key !== 'pro');
794
774
 
795
775
  // Segmented control rendered inside the JsModal header — native passes a
796
- // NightSegmented as header.customContent the same way. Track = night bg at
797
- // radius 10 / padding 3; active thumb = surfaceElevated at radius 8 with
776
+ // NightSegmented glass as header.customContent the same way. Track = glass
777
+ // at radius 10 / padding 3; active thumb = glassElevated at radius 8 with
798
778
  // the accent underline glow (the Everlights All/Day tabs).
799
779
  const tabHeader = /*#__PURE__*/_jsx("div", {
800
780
  style: {
@@ -809,9 +789,9 @@ export function SettingsModal({
809
789
  style: {
810
790
  flex: 1,
811
791
  display: 'flex',
812
- backgroundColor: night.bg,
792
+ backgroundColor: night.glass,
813
793
  borderRadius: 10,
814
- border: `1px solid ${night.border}`,
794
+ border: `1px solid ${night.glassBorder}`,
815
795
  padding: 3,
816
796
  boxSizing: 'border-box'
817
797
  },
@@ -829,7 +809,7 @@ export function SettingsModal({
829
809
  padding: '8px 0',
830
810
  borderRadius: 8,
831
811
  border: 'none',
832
- backgroundColor: isActive ? night.surfaceElevated : 'transparent',
812
+ backgroundColor: isActive ? night.glassElevated : 'transparent',
833
813
  color: isActive ? night.text : night.textSecondary,
834
814
  fontSize: 13,
835
815
  fontWeight: isActive ? 600 : 500,
@@ -1014,7 +994,7 @@ export function SettingsModal({
1014
994
  },
1015
995
  children: savedKeys.map(key => /*#__PURE__*/_jsx("div", {
1016
996
  style: {
1017
- backgroundColor: night.surfaceElevated,
997
+ backgroundColor: night.glassElevated,
1018
998
  borderRadius: 8,
1019
999
  padding: '7px 10px',
1020
1000
  marginBottom: 4
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
 
3
- import React, { useMemo, useCallback, useEffect, useState } from "react";
3
+ import { FloatingToolHost } from "@buoy-gg/shared-ui";
4
+ import { AccountSetup } from "@buoy-gg/shared-ui";
5
+ import React, { useMemo, useCallback, useEffect } from "react";
4
6
  import { View, StyleSheet } from "react-native";
5
7
  import { AppHostProvider } from "./AppHost";
6
8
  import { FloatingMenu } from "./FloatingMenu.js";
@@ -9,10 +11,10 @@ import { ToolOpenerBridge } from "./ToolOpenerBridge.js";
9
11
  import { autoDiscoverPresets, autoDiscoverPresetsWithCustom } from "./autoDiscoverPresets.js";
10
12
  import { sendInstallPing } from "../telemetry/installPing.js";
11
13
  import { isTelemetryEnabled } from "../Buoy.js";
12
- import { renderAppIcon } from "./AppIcon.js";
14
+ import { renderAppIcon } from "./AppIcon";
13
15
  import { DevToolsVisibilityProvider } from "./DevToolsVisibilityContext.js";
14
16
  import { reportOptionalRequireError } from "./optionalRequire.js";
15
- import { HintsProvider, LicenseEntryModal, normalizeEnvironment } from "@buoy-gg/shared-ui";
17
+ import { HintsProvider, normalizeEnvironment } from "@buoy-gg/shared-ui";
16
18
  import { MinimizedToolsProvider } from "./MinimizedToolsContext.js";
17
19
  import { validateDialConfig } from "./defaultConfig.js";
18
20
  import { DefaultConfigProvider } from "./DefaultConfigContext.js";
@@ -120,7 +122,7 @@ export const FloatingDevTools = ({
120
122
  disableHints = false,
121
123
  defaultFloatingTools,
122
124
  defaultDialTools,
123
- requireLicense = false,
125
+ requireLicense: _requireLicense,
124
126
  licenseKey: licenseKeyProp,
125
127
  zustandStores,
126
128
  environment,
@@ -138,24 +140,27 @@ export const FloatingDevTools = ({
138
140
  isLicensed
139
141
  } = useProAccess();
140
142
 
141
- // Get full license state for requireLicense gating
143
+ // Account admission is mandatory; paid feature gates remain separate.
142
144
  const {
143
145
  licenseKey,
144
146
  isInitialized,
145
- tier
147
+ tier,
148
+ access
146
149
  } = useLicense();
150
+ const configuredMatches = licenseKeyProp === undefined || licenseKeyProp.trim().toUpperCase() === (licenseKey ?? "");
151
+ const accountAdmitted = configuredMatches && access.status === "admitted";
147
152
 
148
153
  // Anonymous install ping — dev-only, once a day, no project or tool data.
149
- // Deliberately waits for the license to resolve so the tier it reports is
150
- // the real one rather than the "anonymous" the manager starts at.
154
+ // Deliberately waits for admission to resolve so what it reports is the
155
+ // real outcome (the account's tier, or "locked") rather than the manager's
156
+ // starting state.
151
157
  useEffect(() => {
152
- if (!isInitialized) return;
158
+ if (!isInitialized || access.status === "initializing") return;
153
159
  void sendInstallPing({
154
- tier,
160
+ tier: access.status === "admitted" ? tier : "locked",
155
161
  enabled: isTelemetryEnabled()
156
162
  });
157
- }, [isInitialized, tier]);
158
- const [showLicenseModal, setShowLicenseModal] = useState(false);
163
+ }, [isInitialized, access.status, tier]);
159
164
 
160
165
  // Initialize license manager on mount, and set/clear license key when prop changes.
161
166
  // The `licenseKey` prop is the source of truth WHEN PROVIDED:
@@ -163,28 +168,12 @@ export const FloatingDevTools = ({
163
168
  // - "" (blank) → EXPLICITLY clear the cached license (key was removed)
164
169
  // - undefined → don't touch it (the app may set a key via Buoy.init())
165
170
  useEffect(() => {
166
- if (licenseKeyProp && licenseKeyProp.trim() !== "") {
167
- LicenseManager.initialize().then(() => LicenseManager.setLicenseKey(licenseKeyProp)).catch(() => {});
168
- } else if (licenseKeyProp === "") {
169
- // Explicit removal: wipe the 30-day cache so Pro actually turns off.
170
- LicenseManager.initialize().then(() => LicenseManager.clearLicense()).catch(() => {});
171
- } else {
172
- LicenseManager.initialize();
173
- }
171
+ void LicenseManager.configure(licenseKeyProp).catch(() => {});
174
172
  }, [licenseKeyProp]);
175
173
 
176
- // Show license modal if requireLicense is enabled and no valid license
177
- useEffect(() => {
178
- if (requireLicense && isInitialized && !licenseKey) {
179
- setShowLicenseModal(true);
180
- } else if (licenseKey) {
181
- setShowLicenseModal(false);
182
- }
183
- }, [requireLicense, isInitialized, licenseKey]);
184
-
185
174
  // Register zustand stores once on mount if provided
186
175
  useEffect(() => {
187
- if (!zustandStores) return;
176
+ if (!zustandStores || access.status === "locked") return;
188
177
  try {
189
178
  const {
190
179
  watchStores
@@ -193,7 +182,7 @@ export const FloatingDevTools = ({
193
182
  } catch (e) {
194
183
  reportOptionalRequireError("@buoy-gg/zustand", e);
195
184
  }
196
- }, []); // eslint-disable-line react-hooks/exhaustive-deps
185
+ }, [zustandStores, access.status]);
197
186
 
198
187
  // Normalize dial tools configuration (truncates to max 6 with warning if needed)
199
188
  const normalizedDialTools = useMemo(() => {
@@ -450,17 +439,16 @@ export const FloatingDevTools = ({
450
439
 
451
440
  // Gate: only a PAID license works outside development.
452
441
  //
453
- // Both untiered surfaces below it — anonymous (no key) and free (a key on the
454
- // Free policy) — stay dev-only. `isLicensed` is safe to gate on because
455
- // `isPro` is a strict mirror of `tier === "pro"`, so a free key reports false
456
- // here even though it validates perfectly well.
442
+ // A free key (and, of course, no key at all) stays dev-only. `isLicensed` is
443
+ // safe to gate on because `isPro` is a strict mirror of `tier === "pro"`, so
444
+ // a free key reports false here even though it validates perfectly well.
457
445
  //
458
446
  // When Business on-device login lands, this becomes the boundary that a
459
447
  // logged-in QA/support seat crosses WITHOUT a key in the build — i.e.
460
448
  // `isLicensed || tierSource === "session"`. Nothing else should.
461
449
  const isDevelopment = typeof __DEV__ !== "undefined" && __DEV__;
462
450
  if (!isDevelopment && !isLicensed) {
463
- // Anonymous or free user in production - don't render DevTools.
451
+ // Free or unadmitted user in production - don't render DevTools.
464
452
  // NOTE for whoever relaxes this for the free tier: desktop sync does NOT
465
453
  // ride on this check. It re-tests `isLicensed` itself (externalSyncGate),
466
454
  // so opening this door does not open a broker socket. Keep it that way.
@@ -487,10 +475,16 @@ export const FloatingDevTools = ({
487
475
  requiredEnvVars: requiredEnvVars,
488
476
  customApps: syncApps
489
477
  });
478
+ if (!accountAdmitted) {
479
+ return /*#__PURE__*/_jsxs(_Fragment, {
480
+ children: [access.status === "initializing" && /*#__PURE__*/_jsxs(_Fragment, {
481
+ children: [ReduxAutoInstrument && /*#__PURE__*/_jsx(ReduxAutoInstrument, {}), ConsoleRoot && /*#__PURE__*/_jsx(ConsoleRoot, {}), RouteTracker && /*#__PURE__*/_jsx(RouteTracker, {}), ReactQueryTracker && /*#__PURE__*/_jsx(ReactQueryTracker, {})]
482
+ }), !headless && /*#__PURE__*/_jsx(AccountSetup, {})]
483
+ });
484
+ }
490
485
 
491
486
  // Headless (sync-only): tool adapters + route tracking + desktop sync, no
492
- // on-device UI. requireLicense is intentionally skipped — there's nothing
493
- // to gate. All hooks above already ran, so hook order is preserved, and
487
+ // on-device UI. Account admission above applies to this path too. All hooks above already ran, so hook order is preserved, and
494
488
  // AutoExternalSync still obeys `syncEnabled` (the broker must never be
495
489
  // contacted from a shipped build that didn't ask for it).
496
490
  if (headless) {
@@ -506,16 +500,6 @@ export const FloatingDevTools = ({
506
500
  });
507
501
  }
508
502
 
509
- // Gate: If requireLicense is enabled and no license, show only the license modal
510
- if (requireLicense && isInitialized && !licenseKey) {
511
- return /*#__PURE__*/_jsx(LicenseEntryModal, {
512
- visible: showLicenseModal,
513
- onClose: () => {} // Can't close without entering a license
514
- ,
515
- onSuccess: () => setShowLicenseModal(false)
516
- });
517
- }
518
-
519
503
  // Session blocking is handled inline by JsModal's SessionBlockedOverlay
520
504
  // No need to replace the entire UI - tools still render, but content is blocked
521
505
 
@@ -538,7 +522,9 @@ export const FloatingDevTools = ({
538
522
  ...props,
539
523
  apps: finalApps,
540
524
  environment: resolvedEnvironment
541
- }), props.hidden ? null : /*#__PURE__*/_jsx(AppOverlay, {}), /*#__PURE__*/_jsx(ToolOpenerBridge, {})]
525
+ }), props.hidden ? null : /*#__PURE__*/_jsxs(_Fragment, {
526
+ children: [/*#__PURE__*/_jsx(AppOverlay, {}), /*#__PURE__*/_jsx(FloatingToolHost, {})]
527
+ }), /*#__PURE__*/_jsx(ToolOpenerBridge, {})]
542
528
  })
543
529
  }), children, DebugBordersOverlay && /*#__PURE__*/_jsx(DebugBordersOverlay, {}), HighlightUpdatesOverlay && /*#__PURE__*/_jsx(HighlightUpdatesOverlay, {}), ImageOverlayOverlay && /*#__PURE__*/_jsx(ImageOverlayOverlay, {}), PerfMonitorOverlay && /*#__PURE__*/_jsx(PerfMonitorOverlay, {}), JsTopOverlay && /*#__PURE__*/_jsx(JsTopOverlay, {}), ImpersonateOverlay && /*#__PURE__*/_jsx(ImpersonateOverlay, {}), ScenariosOverlay && /*#__PURE__*/_jsx(ScenariosOverlay, {}), ReduxAutoInstrument && /*#__PURE__*/_jsx(ReduxAutoInstrument, {}), ConsoleRoot && /*#__PURE__*/_jsx(ConsoleRoot, {}), RouteTracker && /*#__PURE__*/_jsx(RouteTracker, {}), ReactQueryTracker && /*#__PURE__*/_jsx(ReactQueryTracker, {}), toolRegistry, syncEnabled && /*#__PURE__*/_jsx(AutoExternalSync, {
544
530
  options: externalSyncOptions(externalSync),
@@ -1,5 +1,8 @@
1
1
  "use strict";
2
2
 
3
+ import { FloatingToolHost } from "@buoy-gg/shared-ui/floating";
4
+ import { LicenseManager, useLicense } from "@buoy-gg/license";
5
+ import { AccountSetup } from "@buoy-gg/shared-ui";
3
6
  /**
4
7
  * FloatingDevTools - Unified dev tools component for React DOM Web.
5
8
  *
@@ -84,7 +87,7 @@ function MenuLauncherIcon({
84
87
  // Main Component
85
88
  // =============================
86
89
 
87
- export function FloatingDevTools(props) {
90
+ function AdmittedFloatingDevTools(props) {
88
91
  // The tool host wraps everything so dial presses can open tool components
89
92
  // into the AppOverlay (web twin of the native AppHostProvider/AppOverlay).
90
93
  return /*#__PURE__*/_jsx(AppHostProvider, {
@@ -369,10 +372,28 @@ function FloatingDevToolsInner({
369
372
  onSettingsChange: _newSettings => {
370
373
  // Settings are already saved by the hook
371
374
  }
372
- }), /*#__PURE__*/_jsx(AppOverlay, {})]
375
+ }), /*#__PURE__*/_jsx(AppOverlay, {}), /*#__PURE__*/_jsx(FloatingToolHost, {})]
373
376
  });
374
377
  }
375
378
 
376
379
  // =============================
377
380
  // Re-exports for convenience
378
- // =============================
381
+ // =============================
382
+
383
+ export function FloatingDevTools(props) {
384
+ const {
385
+ access,
386
+ isPro,
387
+ licenseKey
388
+ } = useLicense();
389
+ useEffect(() => {
390
+ void LicenseManager.configure(props.licenseKey);
391
+ }, [props.licenseKey]);
392
+ const matches = props.licenseKey === undefined || props.licenseKey.trim().toUpperCase() === (licenseKey ?? "");
393
+ const dev = typeof __DEV__ !== "undefined" ? __DEV__ : process.env.NODE_ENV !== "production";
394
+ if (!dev && !isPro) return null;
395
+ if (!matches || access.status !== "admitted") return props.headless ? null : /*#__PURE__*/_jsx(AccountSetup, {});
396
+ return /*#__PURE__*/_jsx(AdmittedFloatingDevTools, {
397
+ ...props
398
+ });
399
+ }
@@ -4,7 +4,7 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
4
4
  import { TouchableOpacity, StyleSheet, View } from "react-native";
5
5
  import { absoluteFill } from "@buoy-gg/shared-ui";
6
6
  import { FloatingTools, UserStatus } from "./floatingTools";
7
- import { renderAppIcon } from "./AppIcon.js";
7
+ import { renderAppIcon } from "./AppIcon";
8
8
  import { DialDevTools } from "./dial/DialDevTools";
9
9
  import { EnvironmentIndicator, persistentStorage, useHintsDisabled, devToolsStorageKeys, buoyColors } from "@buoy-gg/shared-ui";
10
10
  import { useDevToolsSettings } from "./DevToolsSettingsModal";
@@ -1,14 +1,56 @@
1
1
  "use strict";
2
2
 
3
- import React, { useCallback, useEffect, useRef } from "react";
4
- import { Animated, TouchableOpacity, StyleSheet, Easing } from "react-native";
5
- import { ExpandablePopover } from "@buoy-gg/shared-ui";
3
+ import React, { useCallback, useEffect, useRef, useSyncExternalStore } from "react";
4
+ import { Animated, TouchableOpacity, View, Text, StyleSheet, Easing } from "react-native";
5
+ import { ExpandablePopover, getToolAttention, subscribeToolAttention } from "@buoy-gg/shared-ui";
6
6
  import { useMinimizedTools } from "./MinimizedToolsContext.js";
7
7
 
8
+ /**
9
+ * A hidden tool's "I need you". See `setToolAttention` in shared-ui — the tool
10
+ * publishes, the rail draws. Read through useSyncExternalStore so a badge set
11
+ * from inside a minimized sheet lands on the chip without the rail polling.
12
+ */
13
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
+ function useToolAttention(toolId) {
15
+ return useSyncExternalStore(subscribeToolAttention, () => getToolAttention(toolId), () => getToolAttention(toolId));
16
+ }
17
+ const ATTENTION_COLORS = {
18
+ needsInput: "#FF8329",
19
+ done: "#20C997",
20
+ failed: "#FF3B30"
21
+ };
22
+
23
+ /** needsInput outranks failed outranks done — the peek shows the one that matters most. */
24
+ const ATTENTION_RANK = {
25
+ needsInput: 3,
26
+ failed: 2,
27
+ done: 1
28
+ };
29
+
30
+ /** The strongest attention across every minimized tool, for the folded peek. */
31
+ function useStrongestAttention(toolIds) {
32
+ const key = toolIds.join("|");
33
+ return useSyncExternalStore(subscribeToolAttention, () => strongest(key), () => strongest(key));
34
+ }
35
+ const strongestCache = new Map();
36
+ function strongest(key) {
37
+ let best = null;
38
+ for (const id of key ? key.split("|") : []) {
39
+ const a = getToolAttention(id);
40
+ if (a && (!best || ATTENTION_RANK[a.kind] > ATTENTION_RANK[best.kind])) best = a;
41
+ }
42
+ // Same value → same object, or useSyncExternalStore re-renders forever.
43
+ const prev = strongestCache.get(key);
44
+ if (prev && best && prev.kind === best.kind && prev.label === best.label) return prev;
45
+ if (!prev && !best) return null;
46
+ strongestCache.set(key, best);
47
+ return best;
48
+ }
49
+
8
50
  // ============================================================================
9
51
  // Constants
10
52
  // ============================================================================
11
- import { jsx as _jsx } from "react/jsx-runtime";
53
+
12
54
  const PEEK_HEIGHT = 28;
13
55
  const TOOLBAR_WIDTH = 44;
14
56
  const TOOL_ITEM_SIZE = 32;
@@ -31,6 +73,7 @@ function GlitchToolButton({
31
73
  onSelect,
32
74
  index
33
75
  }) {
76
+ const attention = useToolAttention(tool.id);
34
77
  const glitchX = useRef(new Animated.Value(0)).current;
35
78
  const glitchOpacity = useRef(new Animated.Value(1)).current;
36
79
  const glitchScale = useRef(new Animated.Value(1)).current;
@@ -122,14 +165,14 @@ function GlitchToolButton({
122
165
  clearTimeout(timeoutId);
123
166
  };
124
167
  }, [glitchX, glitchOpacity, glitchScale, index]);
125
- return /*#__PURE__*/_jsx(TouchableOpacity, {
168
+ return /*#__PURE__*/_jsxs(TouchableOpacity, {
126
169
  onPress: () => onSelect(tool),
127
170
  activeOpacity: 0.7,
128
171
  style: styles.toolButton,
129
- accessibilityLabel: `Restore ${tool.title}`,
172
+ accessibilityLabel: attention ? `${tool.title} — ${attention.label}. Restore` : `Restore ${tool.title}`,
130
173
  accessibilityRole: "button",
131
174
  testID: `buoy-dock-restore-${tool.id}`,
132
- children: /*#__PURE__*/_jsx(Animated.View, {
175
+ children: [/*#__PURE__*/_jsx(Animated.View, {
133
176
  style: {
134
177
  opacity: glitchOpacity,
135
178
  transform: [{
@@ -139,7 +182,17 @@ function GlitchToolButton({
139
182
  }]
140
183
  },
141
184
  children: tool.icon
142
- })
185
+ }), attention ? /*#__PURE__*/_jsx(View, {
186
+ style: [styles.badge, {
187
+ backgroundColor: ATTENTION_COLORS[attention.kind]
188
+ }],
189
+ testID: `buoy-dock-badge-${tool.id}-${attention.kind}`,
190
+ pointerEvents: "none",
191
+ children: attention.kind === "needsInput" ? /*#__PURE__*/_jsx(Text, {
192
+ style: styles.badgeText,
193
+ children: "!"
194
+ }) : null
195
+ }) : null]
143
196
  });
144
197
  }
145
198
 
@@ -159,6 +212,7 @@ export function MinimizedToolsStack({
159
212
  minimizedTools,
160
213
  restore
161
214
  } = useMinimizedTools();
215
+ const peekAttention = useStrongestAttention(minimizedTools.map(t => t.id));
162
216
 
163
217
  // Calculate expanded height based on number of tools
164
218
  const expandedHeight = TOOLBAR_PADDING + minimizedTools.length * TOOL_ITEM_SIZE + (minimizedTools.length - 1) * ICON_GAP + ICON_GAP + COLLAPSE_BUTTON_SIZE + 4;
@@ -182,6 +236,11 @@ export function MinimizedToolsStack({
182
236
  collapsedLabel: `Show ${minimizedTools.length} minimized tools`,
183
237
  testID: "buoy-dock-peek",
184
238
  collapseTestID: "buoy-dock-collapse",
239
+ attention: peekAttention ? {
240
+ color: ATTENTION_COLORS[peekAttention.kind],
241
+ glyph: peekAttention.kind === "needsInput" ? "!" : undefined,
242
+ label: peekAttention.label
243
+ } : null,
185
244
  children: [...minimizedTools].reverse().map((tool, index) => /*#__PURE__*/_jsx(GlitchToolButton, {
186
245
  tool: tool,
187
246
  onSelect: handleToolPress,
@@ -201,5 +260,26 @@ const styles = StyleSheet.create({
201
260
  alignItems: "center",
202
261
  justifyContent: "center",
203
262
  backgroundColor: "transparent"
263
+ },
264
+ // Top-right of the chip. Needs-input carries a glyph; done/failed are a dot —
265
+ // the colour says which, the glyph says "act".
266
+ badge: {
267
+ position: "absolute",
268
+ top: 0,
269
+ right: 0,
270
+ minWidth: 12,
271
+ height: 12,
272
+ borderRadius: 6,
273
+ paddingHorizontal: 2,
274
+ alignItems: "center",
275
+ justifyContent: "center",
276
+ borderWidth: 1.5,
277
+ borderColor: "#0C0C0C"
278
+ },
279
+ badgeText: {
280
+ color: "#000",
281
+ fontSize: 8,
282
+ fontWeight: "800",
283
+ lineHeight: 9
204
284
  }
205
285
  });