@buoy-gg/core 2.1.1 → 2.1.3

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 (27) hide show
  1. package/lib/commonjs/Buoy.js +106 -0
  2. package/lib/commonjs/floatingMenu/DevToolsSettingsModal.js +13 -274
  3. package/lib/commonjs/floatingMenu/FloatingDevTools.js +35 -1
  4. package/lib/commonjs/floatingMenu/floatingTools.js +62 -48
  5. package/lib/commonjs/index.js +24 -8
  6. package/lib/module/Buoy.js +102 -0
  7. package/lib/module/floatingMenu/DevToolsSettingsModal.js +14 -275
  8. package/lib/module/floatingMenu/FloatingDevTools.js +38 -4
  9. package/lib/module/floatingMenu/floatingTools.js +62 -48
  10. package/lib/module/index.js +3 -1
  11. package/lib/typescript/commonjs/Buoy.d.ts +80 -0
  12. package/lib/typescript/commonjs/Buoy.d.ts.map +1 -0
  13. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
  14. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts +38 -6
  15. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  16. package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts.map +1 -1
  17. package/lib/typescript/commonjs/index.d.ts +4 -2
  18. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  19. package/lib/typescript/module/Buoy.d.ts +80 -0
  20. package/lib/typescript/module/Buoy.d.ts.map +1 -0
  21. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
  22. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts +38 -6
  23. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  24. package/lib/typescript/module/floatingMenu/floatingTools.d.ts.map +1 -1
  25. package/lib/typescript/module/index.d.ts +4 -2
  26. package/lib/typescript/module/index.d.ts.map +1 -1
  27. package/package.json +5 -5
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = exports.Buoy = void 0;
7
+ var _license = require("@buoy-gg/license");
8
+ /**
9
+ * Buoy SDK - Main entry point for React Buoy configuration
10
+ *
11
+ * Initialize once at app startup, before rendering any components:
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * import { Buoy } from '@buoy-gg/core';
16
+ *
17
+ * Buoy.init({
18
+ * licenseKey: 'YOUR_LICENSE_KEY',
19
+ * });
20
+ * ```
21
+ */
22
+
23
+ /**
24
+ * Buoy SDK namespace
25
+ *
26
+ * Use `Buoy.init()` to configure your license key at app startup.
27
+ */
28
+ const Buoy = exports.Buoy = {
29
+ /**
30
+ * Initialize Buoy with your license key
31
+ *
32
+ * Call this once at app startup, before rendering FloatingDevTools.
33
+ * Typically placed in App.tsx or index.js.
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * import { Buoy } from '@buoy-gg/core';
38
+ *
39
+ * // With hardcoded key
40
+ * Buoy.init({
41
+ * licenseKey: '36063C-27282E-8E73F5-55488F-2213DF-V3',
42
+ * });
43
+ *
44
+ * // With environment variable
45
+ * Buoy.init({
46
+ * licenseKey: process.env.BUOY_LICENSE_KEY!,
47
+ * });
48
+ * ```
49
+ */
50
+ init(config) {
51
+ // If no license key or empty string, clear any cached license
52
+ if (!config.licenseKey || config.licenseKey.trim() === "") {
53
+ _license.LicenseManager.initialize().then(() => _license.LicenseManager.clearLicense()).catch(() => {});
54
+ return;
55
+ }
56
+
57
+ // Initialize the license manager and set the key
58
+ const key = config.licenseKey; // We've already checked it's not empty above
59
+ _license.LicenseManager.initialize().then(() => _license.LicenseManager.setLicenseKey(key)).catch(error => {
60
+ console.warn("[Buoy] License initialization failed:", error);
61
+ });
62
+ },
63
+ /**
64
+ * Initialize Buoy asynchronously
65
+ *
66
+ * Use this if you need to wait for initialization to complete.
67
+ *
68
+ * @example
69
+ * ```tsx
70
+ * await Buoy.initAsync({
71
+ * licenseKey: 'YOUR_LICENSE_KEY',
72
+ * });
73
+ * console.log('Buoy is ready!');
74
+ * ```
75
+ */
76
+ async initAsync(config) {
77
+ if (!config.licenseKey) {
78
+ console.warn("[Buoy] No license key provided to Buoy.initAsync()");
79
+ return false;
80
+ }
81
+ await _license.LicenseManager.initialize();
82
+ return _license.LicenseManager.setLicenseKey(config.licenseKey);
83
+ },
84
+ /**
85
+ * Check if Buoy Pro is active
86
+ */
87
+ isPro() {
88
+ return _license.LicenseManager.isPro();
89
+ },
90
+ /**
91
+ * Get the current license state
92
+ */
93
+ getState() {
94
+ return _license.LicenseManager.getState();
95
+ },
96
+ /**
97
+ * Force device re-registration (debugging helper)
98
+ * Clears cached fingerprint so next init will register device again
99
+ */
100
+ async forceReregister() {
101
+ await _license.LicenseManager.forceReregister();
102
+ }
103
+ };
104
+
105
+ // Also export as default for flexibility
106
+ var _default = exports.default = Buoy;
@@ -16,8 +16,6 @@ const MAX_DIAL_SLOTS = 6;
16
16
 
17
17
  // Lazy load license hooks to avoid circular dependencies
18
18
  let _useLicense = null;
19
- let _useSeats = null;
20
- let _useDevices = null;
21
19
  function getUseLicense() {
22
20
  if (!_useLicense) {
23
21
  try {
@@ -29,28 +27,6 @@ function getUseLicense() {
29
27
  }
30
28
  return _useLicense;
31
29
  }
32
- function getUseSeats() {
33
- if (!_useSeats) {
34
- try {
35
- const mod = require("@buoy-gg/license");
36
- _useSeats = mod.useSeats;
37
- } catch {
38
- // License package not available
39
- }
40
- }
41
- return _useSeats;
42
- }
43
- function getUseDevices() {
44
- if (!_useDevices) {
45
- try {
46
- const mod = require("@buoy-gg/license");
47
- _useDevices = mod.useDevices;
48
- } catch {
49
- // License package not available
50
- }
51
- }
52
- return _useDevices;
53
- }
54
30
  const enforceDialLimit = dialTools => {
55
31
  let remaining = MAX_DIAL_SLOTS;
56
32
  const limited = {};
@@ -178,7 +154,6 @@ const DevToolsSettingsModal = ({
178
154
  const [activeTabLoaded, setActiveTabLoaded] = (0, _react.useState)(false);
179
155
  const [expandedSettings, setExpandedSettings] = (0, _react.useState)(new Set());
180
156
  const [showLicenseModal, setShowLicenseModal] = (0, _react.useState)(false);
181
- const [licenseModalForDeviceRegistration, setLicenseModalForDeviceRegistration] = (0, _react.useState)(false);
182
157
 
183
158
  // Load persisted active tab on mount
184
159
  (0, _react.useEffect)(() => {
@@ -209,22 +184,8 @@ const DevToolsSettingsModal = ({
209
184
 
210
185
  // License hooks
211
186
  const useLicenseHook = getUseLicense();
212
- const useSeatsHook = getUseSeats();
213
- const useDevicesHook = getUseDevices();
214
187
  const license = useLicenseHook?.();
215
- const seats = useSeatsHook?.();
216
- const devicesData = useDevicesHook?.();
217
188
  const isPro = license?.isPro ?? false;
218
-
219
- // Devices data
220
- const devices = devicesData?.devices ?? [];
221
- const devicesLoading = devicesData?.isLoading ?? false;
222
- const devicesError = devicesData?.error ?? null;
223
- const refreshDevices = devicesData?.refreshDevices;
224
- const registerDevice = devicesData?.registerDevice;
225
- const deactivateDevice = devicesData?.deactivateDevice;
226
- const isCurrentDeviceRegistered = devicesData?.isCurrentDeviceRegistered ?? false;
227
- const [deactivatingDeviceId, setDeactivatingDeviceId] = (0, _react.useState)(null);
228
189
  const [storageBackend, setStorageBackend] = (0, _react.useState)(null);
229
190
  const [isClearing, setIsClearing] = (0, _react.useState)(false);
230
191
  const [clearSuccess, setClearSuccess] = (0, _react.useState)(false);
@@ -394,51 +355,6 @@ const DevToolsSettingsModal = ({
394
355
  setTimeout(() => setCopySuccess(false), 2000);
395
356
  }
396
357
  };
397
- const handleRemoveDevice = async (deviceId, deviceName, isCurrentDevice) => {
398
- if (!deactivateDevice) return;
399
- const message = isCurrentDevice ? "This will remove your current device. You'll need to re-register to use Pro features on this device." : `Remove "${deviceName}" from your license?`;
400
- const {
401
- Alert
402
- } = require("react-native");
403
- const doRemove = await new Promise(resolve => {
404
- Alert.alert("Remove Device", message, [{
405
- text: "Cancel",
406
- style: "cancel",
407
- onPress: () => resolve(false)
408
- }, {
409
- text: "Remove",
410
- style: "destructive",
411
- onPress: () => resolve(true)
412
- }]);
413
- });
414
- if (!doRemove) return;
415
- setDeactivatingDeviceId(deviceId);
416
- try {
417
- await deactivateDevice(deviceId);
418
- } catch (error) {
419
- console.error("Failed to remove device:", error);
420
- } finally {
421
- setDeactivatingDeviceId(null);
422
- }
423
- };
424
- const handleRegisterDevice = () => {
425
- // Open the license modal in device registration mode (skip license key entry)
426
- setLicenseModalForDeviceRegistration(true);
427
- setShowLicenseModal(true);
428
- };
429
- const formatDeviceDate = date => {
430
- const now = new Date();
431
- const diffMs = now.getTime() - date.getTime();
432
- const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
433
- if (diffDays === 0) return "Today";
434
- if (diffDays === 1) return "Yesterday";
435
- if (diffDays < 7) return `${diffDays} days ago`;
436
- return date.toLocaleDateString(undefined, {
437
- month: "short",
438
- day: "numeric",
439
- year: date.getFullYear() !== now.getFullYear() ? "numeric" : undefined
440
- });
441
- };
442
358
 
443
359
  // Modal is fixed to bottom sheet mode
444
360
  const handleModeChange = (0, _react.useCallback)(_mode => {
@@ -1031,8 +947,8 @@ const DevToolsSettingsModal = ({
1031
947
  })]
1032
948
  }), activeTab === "pro" && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1033
949
  style: styles.proContainer,
1034
- children: isPro ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
1035
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
950
+ children: isPro ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
951
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1036
952
  style: styles.proSection,
1037
953
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.SectionHeader, {
1038
954
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Icon, {
@@ -1047,173 +963,12 @@ const DevToolsSettingsModal = ({
1047
963
  })]
1048
964
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1049
965
  style: styles.proSectionContent,
1050
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1051
- style: styles.proStatsRow,
1052
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1053
- style: styles.proStatItem,
1054
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1055
- style: styles.proStatValue,
1056
- children: seats?.used ?? 0
1057
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1058
- style: styles.proStatLabel,
1059
- children: "USED"
1060
- })]
1061
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1062
- style: styles.proStatDivider
1063
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1064
- style: styles.proStatItem,
1065
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1066
- style: styles.proStatValue,
1067
- children: seats?.total ?? "∞"
1068
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1069
- style: styles.proStatLabel,
1070
- children: "LIMIT"
1071
- })]
1072
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1073
- style: styles.proStatDivider
1074
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1075
- style: styles.proStatItem,
1076
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1077
- style: [styles.proStatValue, (seats?.remaining ?? 0) <= 0 && styles.proStatValueDanger],
1078
- children: seats?.remaining ?? "∞"
1079
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1080
- style: styles.proStatLabel,
1081
- children: "AVAILABLE"
1082
- })]
1083
- })]
966
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
967
+ style: styles.proActiveDescription,
968
+ children: "You have full access to all Buoy DevTools features."
1084
969
  })
1085
970
  })]
1086
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1087
- style: styles.proSection,
1088
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.SectionHeader, {
1089
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Icon, {
1090
- icon: _sharedUi.Smartphone,
1091
- color: _sharedUi.buoyColors.info,
1092
- size: 12
1093
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Title, {
1094
- children: "REGISTERED DEVICES"
1095
- }), devices.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Badge, {
1096
- count: devices.length,
1097
- color: _sharedUi.buoyColors.info
1098
- }), refreshDevices && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SectionHeader.Actions, {
1099
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1100
- onPress: () => refreshDevices(),
1101
- disabled: devicesLoading,
1102
- style: {
1103
- marginLeft: 8
1104
- },
1105
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.RefreshCw, {
1106
- size: 14,
1107
- color: devicesLoading ? _sharedUi.buoyColors.textMuted : _sharedUi.buoyColors.textSecondary
1108
- })
1109
- })
1110
- })]
1111
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1112
- style: styles.proSectionContent,
1113
- children: [devicesLoading && devices.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1114
- style: styles.proEmptyState,
1115
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1116
- style: styles.proEmptyStateText,
1117
- children: "Loading devices..."
1118
- })
1119
- }) : devicesError ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1120
- style: styles.proErrorState,
1121
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.AlertTriangle, {
1122
- size: 16,
1123
- color: _sharedUi.buoyColors.error
1124
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1125
- style: styles.proErrorStateText,
1126
- children: devicesError
1127
- })]
1128
- }) : devices.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1129
- style: styles.proEmptyState,
1130
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Smartphone, {
1131
- size: 24,
1132
- color: _sharedUi.buoyColors.textMuted
1133
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1134
- style: styles.proEmptyStateText,
1135
- children: "No devices registered"
1136
- })]
1137
- }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1138
- style: styles.proDevicesList,
1139
- children: devices.map(device => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1140
- style: [styles.proDeviceRow, device.isCurrentDevice && styles.proDeviceRowCurrent],
1141
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1142
- style: styles.proDeviceIcon,
1143
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Smartphone, {
1144
- size: 16,
1145
- color: _sharedUi.buoyColors.text
1146
- })
1147
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1148
- style: styles.proDeviceInfo,
1149
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1150
- style: styles.proDeviceNameRow,
1151
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1152
- style: styles.proDeviceName,
1153
- numberOfLines: 1,
1154
- children: device.name
1155
- }), device.isCurrentDevice && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1156
- style: styles.proDeviceBadge,
1157
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1158
- style: styles.proDeviceBadgeText,
1159
- children: "THIS DEVICE"
1160
- })
1161
- })]
1162
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
1163
- style: styles.proDeviceDetails,
1164
- children: [device.platform, device.osVersion ? ` ${device.osVersion}` : "", device.model ? ` • ${device.model}` : ""]
1165
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
1166
- style: styles.proDeviceDate,
1167
- children: ["Registered ", formatDeviceDate(device.registeredAt)]
1168
- })]
1169
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1170
- style: styles.proDeviceRemoveBtn,
1171
- onPress: () => handleRemoveDevice(device.id, device.name, device.isCurrentDevice),
1172
- disabled: deactivatingDeviceId === device.id,
1173
- children: deactivatingDeviceId === device.id ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.RefreshCw, {
1174
- size: 14,
1175
- color: _sharedUi.buoyColors.textMuted
1176
- }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Trash2, {
1177
- size: 14,
1178
- color: _sharedUi.buoyColors.error
1179
- })
1180
- })]
1181
- }, device.id))
1182
- }), !isCurrentDeviceRegistered && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
1183
- style: styles.proRegisterDeviceBtn,
1184
- onPress: handleRegisterDevice,
1185
- disabled: devicesLoading,
1186
- activeOpacity: 0.7,
1187
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Plus, {
1188
- size: 14,
1189
- color: _sharedUi.buoyColors.primary
1190
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1191
- style: styles.proRegisterDeviceBtnText,
1192
- children: "Register This Device"
1193
- })]
1194
- })]
1195
- })]
1196
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1197
- style: styles.proSignOutBtn,
1198
- onPress: () => {
1199
- const {
1200
- Alert
1201
- } = require("react-native");
1202
- Alert.alert("Sign Out of Pro?", "This will:\n\n• Remove your license key from this device\n• Deactivate this device from your license\n• Free up a seat for another device\n\nYou can sign back in anytime with your license key.", [{
1203
- text: "Cancel",
1204
- style: "cancel"
1205
- }, {
1206
- text: "Sign Out",
1207
- style: "destructive",
1208
- onPress: () => license?.clearLicense()
1209
- }]);
1210
- },
1211
- activeOpacity: 0.7,
1212
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1213
- style: styles.proSignOutBtnText,
1214
- children: "Sign Out of Pro"
1215
- })
1216
- })]
971
+ })
1217
972
  }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
1218
973
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1219
974
  style: styles.proSection,
@@ -1304,19 +1059,8 @@ const DevToolsSettingsModal = ({
1304
1059
  })]
1305
1060
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.LicenseEntryModal, {
1306
1061
  visible: showLicenseModal,
1307
- onClose: () => {
1308
- setShowLicenseModal(false);
1309
- setLicenseModalForDeviceRegistration(false);
1310
- },
1311
- onSuccess: () => {
1312
- setShowLicenseModal(false);
1313
- setLicenseModalForDeviceRegistration(false);
1314
- },
1315
- license: license,
1316
- startAtDeviceRegistration: licenseModalForDeviceRegistration,
1317
- initialExistingDevices: licenseModalForDeviceRegistration ? devices : [],
1318
- initialMaxDevices: licenseModalForDeviceRegistration ? seats?.total ?? undefined : undefined,
1319
- initialCurrentDeviceCount: licenseModalForDeviceRegistration ? seats?.used ?? undefined : undefined
1062
+ onClose: () => setShowLicenseModal(false),
1063
+ onSuccess: () => setShowLicenseModal(false)
1320
1064
  })]
1321
1065
  });
1322
1066
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.JsModal, {
@@ -2235,21 +1979,16 @@ const styles = _reactNative.StyleSheet.create({
2235
1979
  fontWeight: "600",
2236
1980
  color: _sharedUi.buoyColors.primary
2237
1981
  },
2238
- proSignOutBtn: {
2239
- alignItems: "center",
2240
- justifyContent: "center",
2241
- paddingVertical: 12,
2242
- marginTop: 8
2243
- },
2244
- proSignOutBtnText: {
2245
- fontSize: 12,
2246
- color: _sharedUi.buoyColors.textMuted
2247
- },
2248
1982
  proFreeDescription: {
2249
1983
  fontSize: 11,
2250
1984
  color: _sharedUi.buoyColors.textSecondary,
2251
1985
  lineHeight: 16
2252
1986
  },
1987
+ proActiveDescription: {
1988
+ fontSize: 11,
1989
+ color: _sharedUi.buoyColors.textSecondary,
1990
+ lineHeight: 16
1991
+ },
2253
1992
  proFeaturesList: {
2254
1993
  gap: 8
2255
1994
  },
@@ -41,7 +41,10 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
41
41
 
42
42
  /**
43
43
  * Props for FloatingDevTools component.
44
- * Apps prop is optional - if not provided, all installed dev tools are auto-discovered.
44
+ *
45
+ * ZERO-CONFIG: All @buoy-gg/* tools are auto-discovered when installed.
46
+ * You don't need to pass anything to use network, storage, redux, etc. tools!
47
+ * Just install the package and it works automatically.
45
48
  */
46
49
 
47
50
  /**
@@ -106,16 +109,33 @@ const FloatingDevTools = ({
106
109
  disableHints = false,
107
110
  defaultFloatingTools,
108
111
  defaultDialTools,
112
+ requireLicense = false,
109
113
  ...props
110
114
  }) => {
111
115
  // Check Pro status for production gating
112
116
  const isPro = (0, _license.useIsPro)();
113
117
 
118
+ // Get full license state for requireLicense gating
119
+ const {
120
+ licenseKey,
121
+ isInitialized
122
+ } = (0, _license.useLicense)();
123
+ const [showLicenseModal, setShowLicenseModal] = (0, _react.useState)(false);
124
+
114
125
  // Initialize license manager on mount
115
126
  (0, _react.useEffect)(() => {
116
127
  _license.LicenseManager.initialize();
117
128
  }, []);
118
129
 
130
+ // Show license modal if requireLicense is enabled and no valid license
131
+ (0, _react.useEffect)(() => {
132
+ if (requireLicense && isInitialized && !licenseKey) {
133
+ setShowLicenseModal(true);
134
+ } else if (licenseKey) {
135
+ setShowLicenseModal(false);
136
+ }
137
+ }, [requireLicense, isInitialized, licenseKey]);
138
+
119
139
  // Normalize dial tools configuration (truncates to max 6 with warning if needed)
120
140
  const normalizedDialTools = (0, _react.useMemo)(() => {
121
141
  if (defaultDialTools) {
@@ -230,6 +250,20 @@ const FloatingDevTools = ({
230
250
  // Free user in production - don't render DevTools
231
251
  return null;
232
252
  }
253
+
254
+ // Gate: If requireLicense is enabled and no license, show only the license modal
255
+ if (requireLicense && isInitialized && !licenseKey) {
256
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.LicenseEntryModal, {
257
+ visible: showLicenseModal,
258
+ onClose: () => {} // Can't close without entering a license
259
+ ,
260
+ onSuccess: () => setShowLicenseModal(false)
261
+ });
262
+ }
263
+
264
+ // Session blocking is handled inline by JsModal's SessionBlockedOverlay
265
+ // No need to replace the entire UI - tools still render, but content is blocked
266
+
233
267
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_DefaultConfigContext.DefaultConfigProvider, {
234
268
  defaultFloatingTools: defaultFloatingTools,
235
269
  defaultDialTools: normalizedDialTools,