@buoy-gg/shared-ui 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 (92) hide show
  1. package/lib/commonjs/clipboard/clipboard-impl.js +1 -1
  2. package/lib/commonjs/dataViewer/VirtualizedDataExplorer.js +20 -23
  3. package/lib/commonjs/hooks/safe-area-impl.js +1 -1
  4. package/lib/commonjs/index.js +98 -65
  5. package/lib/commonjs/license/DeviceLimitModal.js +479 -0
  6. package/lib/commonjs/license/FeatureGate.js +4 -9
  7. package/lib/commonjs/license/LicenseEntryModal.js +205 -770
  8. package/lib/commonjs/license/index.js +0 -7
  9. package/lib/commonjs/storage/devToolsStorageKeys.js +2 -0
  10. package/lib/commonjs/stores/BaseEventStore.js +257 -0
  11. package/lib/commonjs/stores/index.js +12 -0
  12. package/lib/commonjs/ui/components/PowerToggleButton.js +73 -0
  13. package/lib/commonjs/ui/components/index.js +7 -0
  14. package/lib/commonjs/utils/index.js +27 -1
  15. package/lib/commonjs/utils/subscribable.js +113 -0
  16. package/lib/commonjs/utils/subscriberCountNotifier.js +72 -0
  17. package/lib/module/JsModal.js +1 -1
  18. package/lib/module/clipboard/clipboard-impl.js +1 -1
  19. package/lib/module/dataViewer/VirtualizedDataExplorer.js +20 -23
  20. package/lib/module/hooks/safe-area-impl.js +1 -1
  21. package/lib/module/index.js +9 -2
  22. package/lib/module/license/DeviceLimitModal.js +473 -0
  23. package/lib/module/license/FeatureGate.js +4 -9
  24. package/lib/module/license/LicenseEntryModal.js +209 -773
  25. package/lib/module/license/index.js +0 -1
  26. package/lib/module/storage/devToolsStorageKeys.js +2 -0
  27. package/lib/module/stores/BaseEventStore.js +253 -0
  28. package/lib/module/stores/index.js +7 -0
  29. package/lib/module/ui/components/PowerToggleButton.js +69 -0
  30. package/lib/module/ui/components/index.js +1 -0
  31. package/lib/module/utils/index.js +3 -1
  32. package/lib/module/utils/subscribable.js +108 -0
  33. package/lib/module/utils/subscriberCountNotifier.js +66 -0
  34. package/lib/typescript/commonjs/JsModal.d.ts.map +1 -1
  35. package/lib/typescript/commonjs/clipboard/clipboard-impl.d.ts +1 -1
  36. package/lib/typescript/commonjs/dataViewer/VirtualizedDataExplorer.d.ts.map +1 -1
  37. package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
  38. package/lib/typescript/commonjs/index.d.ts +4 -3
  39. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  40. package/lib/typescript/commonjs/license/DeviceLimitModal.d.ts +23 -0
  41. package/lib/typescript/commonjs/license/DeviceLimitModal.d.ts.map +1 -0
  42. package/lib/typescript/commonjs/license/FeatureGate.d.ts.map +1 -1
  43. package/lib/typescript/commonjs/license/LicenseEntryModal.d.ts +10 -62
  44. package/lib/typescript/commonjs/license/LicenseEntryModal.d.ts.map +1 -1
  45. package/lib/typescript/commonjs/license/index.d.ts +0 -2
  46. package/lib/typescript/commonjs/license/index.d.ts.map +1 -1
  47. package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts +2 -0
  48. package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts.map +1 -1
  49. package/lib/typescript/commonjs/stores/BaseEventStore.d.ts +145 -0
  50. package/lib/typescript/commonjs/stores/BaseEventStore.d.ts.map +1 -0
  51. package/lib/typescript/commonjs/stores/index.d.ts +5 -0
  52. package/lib/typescript/commonjs/stores/index.d.ts.map +1 -0
  53. package/lib/typescript/commonjs/ui/components/PowerToggleButton.d.ts +32 -0
  54. package/lib/typescript/commonjs/ui/components/PowerToggleButton.d.ts.map +1 -0
  55. package/lib/typescript/commonjs/ui/components/index.d.ts +2 -0
  56. package/lib/typescript/commonjs/ui/components/index.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/utils/index.d.ts +2 -0
  58. package/lib/typescript/commonjs/utils/index.d.ts.map +1 -1
  59. package/lib/typescript/commonjs/utils/subscribable.d.ts +78 -0
  60. package/lib/typescript/commonjs/utils/subscribable.d.ts.map +1 -0
  61. package/lib/typescript/commonjs/utils/subscriberCountNotifier.d.ts +47 -0
  62. package/lib/typescript/commonjs/utils/subscriberCountNotifier.d.ts.map +1 -0
  63. package/lib/typescript/module/JsModal.d.ts.map +1 -1
  64. package/lib/typescript/module/clipboard/clipboard-impl.d.ts +1 -1
  65. package/lib/typescript/module/dataViewer/VirtualizedDataExplorer.d.ts.map +1 -1
  66. package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
  67. package/lib/typescript/module/index.d.ts +4 -3
  68. package/lib/typescript/module/index.d.ts.map +1 -1
  69. package/lib/typescript/module/license/DeviceLimitModal.d.ts +23 -0
  70. package/lib/typescript/module/license/DeviceLimitModal.d.ts.map +1 -0
  71. package/lib/typescript/module/license/FeatureGate.d.ts.map +1 -1
  72. package/lib/typescript/module/license/LicenseEntryModal.d.ts +10 -62
  73. package/lib/typescript/module/license/LicenseEntryModal.d.ts.map +1 -1
  74. package/lib/typescript/module/license/index.d.ts +0 -2
  75. package/lib/typescript/module/license/index.d.ts.map +1 -1
  76. package/lib/typescript/module/storage/devToolsStorageKeys.d.ts +2 -0
  77. package/lib/typescript/module/storage/devToolsStorageKeys.d.ts.map +1 -1
  78. package/lib/typescript/module/stores/BaseEventStore.d.ts +145 -0
  79. package/lib/typescript/module/stores/BaseEventStore.d.ts.map +1 -0
  80. package/lib/typescript/module/stores/index.d.ts +5 -0
  81. package/lib/typescript/module/stores/index.d.ts.map +1 -0
  82. package/lib/typescript/module/ui/components/PowerToggleButton.d.ts +32 -0
  83. package/lib/typescript/module/ui/components/PowerToggleButton.d.ts.map +1 -0
  84. package/lib/typescript/module/ui/components/index.d.ts +2 -0
  85. package/lib/typescript/module/ui/components/index.d.ts.map +1 -1
  86. package/lib/typescript/module/utils/index.d.ts +2 -0
  87. package/lib/typescript/module/utils/index.d.ts.map +1 -1
  88. package/lib/typescript/module/utils/subscribable.d.ts +78 -0
  89. package/lib/typescript/module/utils/subscribable.d.ts.map +1 -0
  90. package/lib/typescript/module/utils/subscriberCountNotifier.d.ts +47 -0
  91. package/lib/typescript/module/utils/subscriberCountNotifier.d.ts.map +1 -0
  92. package/package.json +3 -3
@@ -1,583 +1,188 @@
1
1
  "use strict";
2
2
 
3
3
  /**
4
- * LicenseEntryModal - Modal for entering and activating a license key
4
+ * LicenseEntryModal - Modal showing how to activate a license
5
5
  *
6
- * Flow:
7
- * 1. Enter license key
8
- * 2. Validate key - check if device registration needed
9
- * 3a. If existing devices, show selection (claim or register new)
10
- * 3b. If no devices, prompt for device name
11
- * 4. Register device with user-provided name
12
- * 5. Success!
6
+ * Shows two options:
7
+ * 1. Get a license (link to pricing)
8
+ * 2. Already have one? Add Buoy.init() to your code
13
9
  */
14
10
 
15
- import React, { useState, useCallback, useMemo, useRef, useEffect } from "react";
16
- import { View, Text, TextInput, TouchableOpacity, StyleSheet, ActivityIndicator, Linking, Modal, KeyboardAvoidingView, Platform, ScrollView } from "react-native";
11
+ import React, { useCallback } from "react";
12
+ import { View, Text, TouchableOpacity, StyleSheet, Linking, Modal, Platform } from "react-native";
17
13
  import { macOSColors } from "../ui/gameUI/constants/macOSDesignSystemColors.js";
18
- import { X, Key, Link, CheckCircle, AlertCircle, Smartphone, Plus } from "../icons/lucide-icons.js";
14
+ import { X, Key, Link, FileCode, Copy } from "../icons/lucide-icons.js";
19
15
 
20
- /**
21
- * Registered device interface
22
- */
16
+ // For clipboard functionality
17
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
18
+ let Clipboard = null;
19
+ try {
20
+ Clipboard = require("@react-native-clipboard/clipboard").default;
21
+ } catch {
22
+ // Clipboard not available
23
+ }
23
24
 
24
- /**
25
- * License validation result from the license package
26
- */
25
+ // Keep these for backwards compatibility but they're no longer used
27
26
 
28
- /**
29
- * Result of device registration
30
- */
27
+ const CODE_SNIPPET = `import { Buoy } from '@buoy-gg/core';
31
28
 
32
- /**
33
- * License state interface for prop injection
34
- */
35
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
36
- // Lazy load license hooks to avoid circular dependencies
37
- let _useLicense = null;
38
- function getUseLicense() {
39
- if (!_useLicense) {
40
- try {
41
- const mod = require("@buoy-gg/license");
42
- _useLicense = mod.useLicense;
43
- } catch {
44
- // License package not available - will use injected license prop
45
- }
46
- }
47
- return _useLicense;
48
- }
49
- const LICENSE_KEY_REGEX = /^[A-Z0-9]{6}-[A-Z0-9]{6}-[A-Z0-9]{6}-[A-Z0-9]{6}-[A-Z0-9]{6}-[A-Z0-9]{2,6}$/;
50
- const MIN_KEY_LENGTH = 37;
51
- function validateLicenseKeyFormat(key) {
52
- const trimmedKey = key.trim().toUpperCase();
53
- if (trimmedKey.length === 0) {
54
- return {
55
- isValid: false,
56
- error: null
57
- };
58
- }
59
- if (!/^[A-Z0-9-]+$/.test(trimmedKey)) {
60
- return {
61
- isValid: false,
62
- error: "Invalid characters in key"
63
- };
64
- }
65
- if (LICENSE_KEY_REGEX.test(trimmedKey)) {
66
- return {
67
- isValid: true,
68
- error: null
69
- };
70
- }
71
- if (trimmedKey.length < MIN_KEY_LENGTH) {
72
- return {
73
- isValid: false,
74
- error: "Key is incomplete"
75
- };
76
- }
77
- return {
78
- isValid: false,
79
- error: "Invalid key format"
80
- };
81
- }
29
+ Buoy.init({
30
+ licenseKey: 'YOUR_LICENSE_KEY',
31
+ });`;
82
32
  export const LicenseEntryModal = ({
83
33
  visible,
84
34
  onClose,
85
- onSuccess,
86
- purchaseUrl = "https://buoy.gg/pricing",
87
- license: injectedLicense,
88
- startAtDeviceRegistration = false,
89
- initialExistingDevices = [],
90
- initialMaxDevices,
91
- initialCurrentDeviceCount
35
+ purchaseUrl = "https://buoy.gg/pricing"
92
36
  }) => {
93
- const [licenseKey, setLicenseKey] = useState("");
94
- const [deviceName, setDeviceName] = useState("");
95
- const [modalState, setModalState] = useState("entry");
96
- const [errorMessage, setErrorMessage] = useState(null);
97
- const [isFocused, setIsFocused] = useState(false);
98
- const [isDeviceNameFocused, setIsDeviceNameFocused] = useState(false);
99
- const [existingDevices, setExistingDevices] = useState([]);
100
- const [maxDevices, setMaxDevices] = useState();
101
- const [currentDeviceCount, setCurrentDeviceCount] = useState();
102
- const [hasInitialized, setHasInitialized] = useState(false);
103
-
104
- // Ref to track reset timeout to prevent race condition on quick close/open
105
- const resetTimeoutRef = useRef(null);
106
-
107
- // Use injected license if provided, otherwise try to load from @buoy-gg/license
108
- const useLicenseHook = getUseLicense();
109
- const hookLicense = useLicenseHook?.();
110
- const license = injectedLicense ?? hookLicense;
111
-
112
- // Handle initial state when modal becomes visible
113
- React.useEffect(() => {
114
- if (visible && startAtDeviceRegistration && !hasInitialized) {
115
- // Set device count info from props when starting at device registration
116
- if (initialMaxDevices !== undefined) {
117
- setMaxDevices(initialMaxDevices);
118
- }
119
- if (initialCurrentDeviceCount !== undefined) {
120
- setCurrentDeviceCount(initialCurrentDeviceCount);
121
- }
122
- if (initialExistingDevices.length > 0) {
123
- setExistingDevices(initialExistingDevices);
124
- setModalState("device_select");
125
- } else {
126
- setModalState("device_name");
127
- }
128
- setHasInitialized(true);
129
- }
130
- // Reset initialization flag when modal closes
131
- if (!visible) {
132
- setHasInitialized(false);
133
- }
134
- }, [visible, startAtDeviceRegistration, initialExistingDevices, initialMaxDevices, initialCurrentDeviceCount, hasInitialized]);
135
-
136
- // Validate key format in real-time
137
- const validation = useMemo(() => validateLicenseKeyFormat(licenseKey), [licenseKey]);
138
- const canActivate = validation.isValid && licenseKey.trim().length > 0;
139
- const canRegister = deviceName.trim().length >= 2;
140
- const handleValidate = useCallback(async () => {
141
- if (!canActivate) return;
142
- if (!license) {
143
- setModalState("error");
144
- setErrorMessage("License system unavailable.");
145
- return;
146
- }
147
- setModalState("validating");
148
- setErrorMessage(null);
149
- try {
150
- // Check if the new validateLicenseKey method is available
151
- if (license.validateLicenseKey) {
152
- const result = await license.validateLicenseKey(licenseKey.trim());
153
- if (!result.valid) {
154
- setModalState("error");
155
- setErrorMessage(result.error || "Invalid license key");
156
- return;
157
- }
158
- setMaxDevices(result.maxDevices);
159
- setCurrentDeviceCount(result.currentDeviceCount);
160
- if (!result.needsDeviceRegistration) {
161
- // Already registered or simulator - success!
162
- setModalState("success");
163
- setTimeout(() => {
164
- onSuccess?.();
165
- onClose();
166
- resetState();
167
- }, 1500);
168
- return;
169
- }
170
-
171
- // Device registration needed
172
- if (result.existingDevices.length > 0) {
173
- setExistingDevices(result.existingDevices);
174
- setModalState("device_select");
175
- } else {
176
- setModalState("device_name");
177
- }
178
- } else {
179
- // Fallback to legacy setLicenseKey
180
- const success = await license.setLicenseKey(licenseKey.trim());
181
- if (success) {
182
- setModalState("success");
183
- setTimeout(() => {
184
- onSuccess?.();
185
- onClose();
186
- resetState();
187
- }, 1500);
188
- } else {
189
- setModalState("error");
190
- setErrorMessage(license.error || "Invalid license key");
191
- }
192
- }
193
- } catch (err) {
194
- setModalState("error");
195
- setErrorMessage(err instanceof Error ? err.message : "Validation failed");
196
- }
197
- }, [licenseKey, license, onSuccess, onClose, canActivate]);
198
- const handleRegisterDevice = useCallback(async () => {
199
- if (!canRegister || !license?.registerDevice) {
200
- // Fallback if registerDevice not available
201
- if (license?.setLicenseKey) {
202
- const success = await license.setLicenseKey(licenseKey.trim());
203
- if (success) {
204
- setModalState("success");
205
- setTimeout(() => {
206
- onSuccess?.();
207
- onClose();
208
- resetState();
209
- }, 1500);
210
- } else {
211
- setModalState("error");
212
- setErrorMessage(license.error || "Registration failed");
213
- }
214
- }
215
- return;
216
- }
217
- setModalState("registering");
218
- setErrorMessage(null);
219
- try {
220
- const result = await license.registerDevice(deviceName.trim());
221
- if (result.success) {
222
- setModalState("success");
223
- setTimeout(() => {
224
- onSuccess?.();
225
- onClose();
226
- resetState();
227
- }, 1500);
228
- } else {
229
- setModalState("error");
230
- setErrorMessage(result.error || "Device registration failed");
231
- }
232
- } catch (err) {
233
- setModalState("error");
234
- setErrorMessage(err instanceof Error ? err.message : "Registration failed");
235
- }
236
- }, [deviceName, license, licenseKey, onSuccess, onClose, canRegister]);
237
- const handleClaimDevice = useCallback(async device => {
238
- if (!license?.claimDevice) {
239
- setModalState("error");
240
- setErrorMessage("Claim device not available");
241
- return;
242
- }
243
- setModalState("registering");
244
- setErrorMessage(null);
245
- try {
246
- const success = await license.claimDevice(device.id, existingDevices);
247
- if (success) {
248
- setModalState("success");
249
- setTimeout(() => {
250
- onSuccess?.();
251
- onClose();
252
- resetState();
253
- }, 1500);
254
- } else {
255
- setModalState("error");
256
- setErrorMessage(license.error || "Failed to claim device");
257
- }
258
- } catch (err) {
259
- setModalState("error");
260
- setErrorMessage(err instanceof Error ? err.message : "Failed to claim device");
261
- }
262
- }, [license, existingDevices, onSuccess, onClose]);
263
- const handleRegisterNew = useCallback(() => {
264
- setModalState("device_name");
265
- }, []);
37
+ const [copied, setCopied] = React.useState(false);
266
38
  const handleGetLicense = useCallback(() => {
267
39
  Linking.openURL(purchaseUrl);
268
40
  }, [purchaseUrl]);
269
- const resetState = useCallback(() => {
270
- setLicenseKey("");
271
- setDeviceName("");
272
- setModalState("entry");
273
- setErrorMessage(null);
274
- setIsFocused(false);
275
- setIsDeviceNameFocused(false);
276
- setExistingDevices([]);
277
- setMaxDevices(undefined);
278
- setCurrentDeviceCount(undefined);
279
- }, []);
280
- const handleClose = useCallback(() => {
281
- onClose();
282
- // Store the timeout ref so it can be cancelled if modal reopens quickly
283
- resetTimeoutRef.current = setTimeout(resetState, 300);
284
- }, [onClose, resetState]);
285
-
286
- // Cancel pending reset if modal becomes visible (prevents race condition)
287
- useEffect(() => {
288
- if (visible && resetTimeoutRef.current) {
289
- clearTimeout(resetTimeoutRef.current);
290
- resetTimeoutRef.current = null;
41
+ const handleCopyCode = useCallback(() => {
42
+ if (Clipboard?.setString) {
43
+ Clipboard.setString(CODE_SNIPPET);
44
+ setCopied(true);
45
+ setTimeout(() => setCopied(false), 2000);
291
46
  }
292
- }, [visible]);
293
- const formatLicenseKey = text => {
294
- return text.toUpperCase().replace(/[^A-Z0-9-]/g, "");
295
- };
296
- const renderContent = () => {
297
- switch (modalState) {
298
- case "validating":
299
- case "registering":
300
- return /*#__PURE__*/_jsxs(View, {
301
- style: styles.stateContainer,
47
+ }, []);
48
+ return /*#__PURE__*/_jsx(Modal, {
49
+ visible: visible,
50
+ transparent: true,
51
+ animationType: "fade",
52
+ onRequestClose: onClose,
53
+ children: /*#__PURE__*/_jsxs(View, {
54
+ style: styles.overlay,
55
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
56
+ style: styles.backdrop,
57
+ activeOpacity: 1,
58
+ onPress: onClose
59
+ }), /*#__PURE__*/_jsxs(View, {
60
+ style: styles.modal,
61
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
62
+ style: styles.closeButton,
63
+ onPress: onClose,
64
+ activeOpacity: 0.7,
65
+ children: /*#__PURE__*/_jsx(X, {
66
+ size: 18,
67
+ color: macOSColors.text.muted
68
+ })
69
+ }), /*#__PURE__*/_jsxs(View, {
70
+ style: styles.header,
302
71
  children: [/*#__PURE__*/_jsx(View, {
303
- style: styles.loaderContainer,
304
- children: /*#__PURE__*/_jsx(ActivityIndicator, {
305
- size: "large",
72
+ style: styles.iconContainer,
73
+ children: /*#__PURE__*/_jsx(Key, {
74
+ size: 20,
306
75
  color: macOSColors.semantic.info
307
76
  })
308
77
  }), /*#__PURE__*/_jsx(Text, {
309
- style: styles.stateText,
310
- children: modalState === "validating" ? "Validating license..." : "Registering device..."
78
+ style: styles.title,
79
+ children: "Unlock Pro Features"
311
80
  }), /*#__PURE__*/_jsx(Text, {
312
- style: styles.stateSubtext,
313
- children: "Please wait"
81
+ style: styles.subtitle,
82
+ children: "Get access to all Buoy DevTools features"
314
83
  })]
315
- });
316
- case "success":
317
- return /*#__PURE__*/_jsxs(View, {
318
- style: styles.stateContainer,
319
- children: [/*#__PURE__*/_jsx(View, {
320
- style: [styles.statusIconContainer, styles.successIconContainer],
321
- children: /*#__PURE__*/_jsx(CheckCircle, {
322
- size: 32,
323
- color: macOSColors.semantic.success
324
- })
325
- }), /*#__PURE__*/_jsx(Text, {
326
- style: styles.successText,
327
- children: "License Activated!"
328
- }), /*#__PURE__*/_jsx(Text, {
329
- style: styles.stateSubtext,
330
- children: "Pro features are now unlocked"
84
+ }), /*#__PURE__*/_jsxs(TouchableOpacity, {
85
+ style: styles.primaryButton,
86
+ onPress: handleGetLicense,
87
+ activeOpacity: 0.8,
88
+ children: [/*#__PURE__*/_jsx(Text, {
89
+ style: styles.primaryButtonText,
90
+ children: "Get a License"
91
+ }), /*#__PURE__*/_jsx(Link, {
92
+ size: 14,
93
+ color: "#000"
331
94
  })]
332
- });
333
- case "error":
334
- return /*#__PURE__*/_jsxs(View, {
335
- style: styles.stateContainer,
95
+ }), /*#__PURE__*/_jsxs(View, {
96
+ style: styles.divider,
336
97
  children: [/*#__PURE__*/_jsx(View, {
337
- style: [styles.statusIconContainer, styles.errorIconContainer],
338
- children: /*#__PURE__*/_jsx(AlertCircle, {
339
- size: 32,
340
- color: macOSColors.semantic.error
341
- })
342
- }), /*#__PURE__*/_jsx(Text, {
343
- style: styles.errorText,
344
- children: "Activation Failed"
98
+ style: styles.dividerLine
345
99
  }), /*#__PURE__*/_jsx(Text, {
346
- style: styles.stateSubtext,
347
- children: errorMessage
348
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
349
- style: styles.secondaryButton,
350
- onPress: () => setModalState("entry"),
351
- activeOpacity: 0.8,
352
- children: /*#__PURE__*/_jsx(Text, {
353
- style: styles.secondaryButtonText,
354
- children: "Try Again"
355
- })
100
+ style: styles.dividerText,
101
+ children: "already have one?"
102
+ }), /*#__PURE__*/_jsx(View, {
103
+ style: styles.dividerLine
356
104
  })]
357
- });
358
- case "device_name":
359
- return /*#__PURE__*/_jsxs(_Fragment, {
105
+ }), /*#__PURE__*/_jsxs(View, {
106
+ style: styles.instructionsContainer,
360
107
  children: [/*#__PURE__*/_jsxs(View, {
361
- style: styles.header,
362
- children: [/*#__PURE__*/_jsx(View, {
363
- style: styles.iconContainer,
364
- children: /*#__PURE__*/_jsx(Smartphone, {
365
- size: 20,
366
- color: macOSColors.semantic.info
367
- })
368
- }), /*#__PURE__*/_jsx(Text, {
369
- style: styles.title,
370
- children: "Name This Device"
108
+ style: styles.instructionsHeader,
109
+ children: [/*#__PURE__*/_jsx(FileCode, {
110
+ size: 14,
111
+ color: macOSColors.text.secondary
371
112
  }), /*#__PURE__*/_jsx(Text, {
372
- style: styles.subtitle,
373
- children: "Give this device a name so you can identify it later"
113
+ style: styles.instructionsTitle,
114
+ children: "Add to your App.tsx"
374
115
  })]
375
116
  }), /*#__PURE__*/_jsxs(View, {
376
- style: styles.inputWrapper,
377
- children: [/*#__PURE__*/_jsx(View, {
378
- style: [styles.inputContainer, isDeviceNameFocused && styles.inputContainerFocused],
379
- children: /*#__PURE__*/_jsx(TextInput, {
380
- style: [styles.input, {
381
- fontFamily: undefined,
382
- letterSpacing: 0
383
- }],
384
- value: deviceName,
385
- onChangeText: setDeviceName,
386
- placeholder: "e.g., My iPhone, Work iPad",
387
- placeholderTextColor: macOSColors.text.muted,
388
- autoFocus: true,
389
- onFocus: () => setIsDeviceNameFocused(true),
390
- onBlur: () => setIsDeviceNameFocused(false)
391
- })
392
- }), maxDevices && /*#__PURE__*/_jsxs(Text, {
393
- style: styles.deviceCount,
394
- children: [currentDeviceCount ?? 0, " of ", maxDevices, " devices used"]
395
- })]
396
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
397
- style: [styles.activateButton, !canRegister && styles.activateButtonDisabled],
398
- onPress: handleRegisterDevice,
399
- disabled: !canRegister,
400
- activeOpacity: 0.8,
401
- children: /*#__PURE__*/_jsx(Text, {
402
- style: [styles.activateButtonText, !canRegister && styles.activateButtonTextDisabled],
403
- children: "Register Device"
404
- })
405
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
406
- style: styles.backButton,
407
- onPress: () => setModalState("entry"),
408
- activeOpacity: 0.8,
409
- children: /*#__PURE__*/_jsx(Text, {
410
- style: styles.backButtonText,
411
- children: "Back"
412
- })
413
- })]
414
- });
415
- case "device_select":
416
- return /*#__PURE__*/_jsxs(_Fragment, {
417
- children: [/*#__PURE__*/_jsxs(View, {
418
- style: styles.header,
419
- children: [/*#__PURE__*/_jsx(View, {
420
- style: styles.iconContainer,
421
- children: /*#__PURE__*/_jsx(Smartphone, {
422
- size: 20,
423
- color: macOSColors.semantic.info
424
- })
425
- }), /*#__PURE__*/_jsx(Text, {
426
- style: styles.title,
427
- children: "Select Device"
428
- }), /*#__PURE__*/_jsx(Text, {
429
- style: styles.subtitle,
430
- children: "Claim an existing device or register a new one"
431
- }), maxDevices && /*#__PURE__*/_jsxs(Text, {
432
- style: styles.deviceCountHeader,
433
- children: [currentDeviceCount ?? 0, " of ", maxDevices, " devices used"]
434
- })]
435
- }), /*#__PURE__*/_jsx(ScrollView, {
436
- style: styles.deviceList,
437
- showsVerticalScrollIndicator: false,
438
- children: existingDevices.map(device => /*#__PURE__*/_jsxs(TouchableOpacity, {
439
- style: styles.deviceItem,
440
- onPress: () => handleClaimDevice(device),
441
- activeOpacity: 0.8,
442
- children: [/*#__PURE__*/_jsx(View, {
443
- style: styles.deviceIcon,
444
- children: /*#__PURE__*/_jsx(Smartphone, {
445
- size: 18,
446
- color: macOSColors.text.secondary
447
- })
448
- }), /*#__PURE__*/_jsxs(View, {
449
- style: styles.deviceInfo,
450
- children: [/*#__PURE__*/_jsx(Text, {
451
- style: styles.deviceName,
452
- children: device.name
453
- }), /*#__PURE__*/_jsx(Text, {
454
- style: styles.devicePlatform,
455
- children: device.platform
456
- })]
117
+ style: styles.codeBlock,
118
+ children: [/*#__PURE__*/_jsxs(Text, {
119
+ style: styles.codeText,
120
+ children: [/*#__PURE__*/_jsx(Text, {
121
+ style: styles.codeKeyword,
122
+ children: "import"
123
+ }), /*#__PURE__*/_jsx(Text, {
124
+ style: styles.codePunctuation,
125
+ children: " { "
126
+ }), /*#__PURE__*/_jsx(Text, {
127
+ style: styles.codeVariable,
128
+ children: "Buoy"
129
+ }), /*#__PURE__*/_jsx(Text, {
130
+ style: styles.codePunctuation,
131
+ children: " } "
132
+ }), /*#__PURE__*/_jsx(Text, {
133
+ style: styles.codeKeyword,
134
+ children: "from"
135
+ }), /*#__PURE__*/_jsx(Text, {
136
+ style: styles.codeString,
137
+ children: " '@buoy-gg/core'"
138
+ }), /*#__PURE__*/_jsx(Text, {
139
+ style: styles.codePunctuation,
140
+ children: ";"
141
+ }), "\n\n", /*#__PURE__*/_jsx(Text, {
142
+ style: styles.codeVariable,
143
+ children: "Buoy"
144
+ }), /*#__PURE__*/_jsx(Text, {
145
+ style: styles.codePunctuation,
146
+ children: "."
147
+ }), /*#__PURE__*/_jsx(Text, {
148
+ style: styles.codeFunction,
149
+ children: "init"
150
+ }), /*#__PURE__*/_jsx(Text, {
151
+ style: styles.codePunctuation,
152
+ children: "({"
153
+ }), "\n", /*#__PURE__*/_jsx(Text, {
154
+ style: styles.codeProperty,
155
+ children: " licenseKey"
156
+ }), /*#__PURE__*/_jsx(Text, {
157
+ style: styles.codePunctuation,
158
+ children: ": "
457
159
  }), /*#__PURE__*/_jsx(Text, {
458
- style: styles.claimText,
459
- children: "Claim"
160
+ style: styles.codeString,
161
+ children: "'YOUR_KEY'"
162
+ }), /*#__PURE__*/_jsx(Text, {
163
+ style: styles.codePunctuation,
164
+ children: ","
165
+ }), "\n", /*#__PURE__*/_jsx(Text, {
166
+ style: styles.codePunctuation,
167
+ children: "});"
168
+ })]
169
+ }), Clipboard?.setString && /*#__PURE__*/_jsxs(TouchableOpacity, {
170
+ style: styles.copyButton,
171
+ onPress: handleCopyCode,
172
+ activeOpacity: 0.7,
173
+ children: [/*#__PURE__*/_jsx(Copy, {
174
+ size: 12,
175
+ color: copied ? macOSColors.semantic.success : macOSColors.text.muted
176
+ }), /*#__PURE__*/_jsx(Text, {
177
+ style: [styles.copyText, copied && styles.copyTextSuccess],
178
+ children: copied ? "Copied!" : "Copy"
460
179
  })]
461
- }, device.id))
462
- }), (!maxDevices || (currentDeviceCount ?? 0) < maxDevices) && /*#__PURE__*/_jsxs(TouchableOpacity, {
463
- style: styles.registerNewButton,
464
- onPress: handleRegisterNew,
465
- activeOpacity: 0.8,
466
- children: [/*#__PURE__*/_jsx(Plus, {
467
- size: 16,
468
- color: macOSColors.semantic.info
469
- }), /*#__PURE__*/_jsx(Text, {
470
- style: styles.registerNewText,
471
- children: "Register New Device"
472
- })]
473
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
474
- style: styles.backButton,
475
- onPress: () => setModalState("entry"),
476
- activeOpacity: 0.8,
477
- children: /*#__PURE__*/_jsx(Text, {
478
- style: styles.backButtonText,
479
- children: "Back"
480
- })
481
- })]
482
- });
483
- default:
484
- // entry
485
- return /*#__PURE__*/_jsxs(_Fragment, {
486
- children: [/*#__PURE__*/_jsxs(View, {
487
- style: styles.header,
488
- children: [/*#__PURE__*/_jsx(View, {
489
- style: styles.iconContainer,
490
- children: /*#__PURE__*/_jsx(Key, {
491
- size: 20,
492
- color: macOSColors.semantic.info
493
- })
494
- }), /*#__PURE__*/_jsx(Text, {
495
- style: styles.title,
496
- children: "Enter License Key"
497
- }), /*#__PURE__*/_jsx(Text, {
498
- style: styles.subtitle,
499
- children: "Unlock Pro features by entering your license key"
500
- })]
501
- }), /*#__PURE__*/_jsxs(View, {
502
- style: styles.inputWrapper,
503
- children: [/*#__PURE__*/_jsx(View, {
504
- style: [styles.inputContainer, isFocused && styles.inputContainerFocused, validation.error && licenseKey.length > 0 && styles.inputContainerError, validation.isValid && licenseKey.length > 0 && styles.inputContainerValid],
505
- children: /*#__PURE__*/_jsx(TextInput, {
506
- style: styles.input,
507
- value: licenseKey,
508
- onChangeText: text => setLicenseKey(formatLicenseKey(text)),
509
- placeholder: "XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXXXXX-V3",
510
- placeholderTextColor: macOSColors.text.muted,
511
- autoCapitalize: "characters",
512
- autoCorrect: false,
513
- autoComplete: "off",
514
- onFocus: () => setIsFocused(true),
515
- onBlur: () => setIsFocused(false)
516
- })
517
- }), validation.error && licenseKey.length > 0 && /*#__PURE__*/_jsx(Text, {
518
- style: styles.validationError,
519
- children: validation.error
520
- }), validation.isValid && licenseKey.length > 0 && /*#__PURE__*/_jsx(Text, {
521
- style: styles.validationSuccess,
522
- children: "Valid format"
523
- })]
524
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
525
- style: [styles.activateButton, !canActivate && styles.activateButtonDisabled],
526
- onPress: handleValidate,
527
- disabled: !canActivate,
528
- activeOpacity: 0.8,
529
- children: /*#__PURE__*/_jsx(Text, {
530
- style: [styles.activateButtonText, !canActivate && styles.activateButtonTextDisabled],
531
- children: "Activate License"
532
- })
533
- }), /*#__PURE__*/_jsxs(View, {
534
- style: styles.divider,
535
- children: [/*#__PURE__*/_jsx(View, {
536
- style: styles.dividerLine
537
- }), /*#__PURE__*/_jsx(Text, {
538
- style: styles.dividerText,
539
- children: "or"
540
- }), /*#__PURE__*/_jsx(View, {
541
- style: styles.dividerLine
542
- })]
543
- }), /*#__PURE__*/_jsxs(TouchableOpacity, {
544
- style: styles.purchaseButton,
545
- onPress: handleGetLicense,
546
- activeOpacity: 0.8,
547
- children: [/*#__PURE__*/_jsx(Text, {
548
- style: styles.purchaseButtonText,
549
- children: "Get a License"
550
- }), /*#__PURE__*/_jsx(Link, {
551
- size: 14,
552
- color: macOSColors.semantic.info
553
180
  })]
181
+ }), /*#__PURE__*/_jsx(Text, {
182
+ style: styles.instructionsNote,
183
+ children: "Replace YOUR_KEY with your license key"
554
184
  })]
555
- });
556
- }
557
- };
558
- return /*#__PURE__*/_jsx(Modal, {
559
- visible: visible,
560
- transparent: true,
561
- animationType: "fade",
562
- onRequestClose: handleClose,
563
- children: /*#__PURE__*/_jsxs(KeyboardAvoidingView, {
564
- behavior: Platform.OS === "ios" ? "padding" : "height",
565
- style: styles.overlay,
566
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
567
- style: styles.backdrop,
568
- activeOpacity: 1,
569
- onPress: handleClose
570
- }), /*#__PURE__*/_jsxs(View, {
571
- style: styles.modal,
572
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
573
- style: styles.closeButton,
574
- onPress: handleClose,
575
- activeOpacity: 0.7,
576
- children: /*#__PURE__*/_jsx(X, {
577
- size: 18,
578
- color: macOSColors.text.muted
579
- })
580
- }), renderContent()]
185
+ })]
581
186
  })]
582
187
  })
583
188
  });
@@ -598,7 +203,7 @@ const styles = StyleSheet.create({
598
203
  padding: 24,
599
204
  width: "90%",
600
205
  maxWidth: 380,
601
- maxHeight: "80%",
206
+ maxHeight: "85%",
602
207
  borderWidth: 1,
603
208
  borderColor: macOSColors.semantic.info + "50",
604
209
  shadowColor: macOSColors.semantic.info,
@@ -657,61 +262,14 @@ const styles = StyleSheet.create({
657
262
  textAlign: "center",
658
263
  lineHeight: 18
659
264
  },
660
- inputWrapper: {
661
- marginBottom: 16
662
- },
663
- inputContainer: {
664
- backgroundColor: macOSColors.background.input,
665
- borderRadius: 8,
666
- borderWidth: 1,
667
- borderColor: macOSColors.border.input,
668
- overflow: "hidden"
669
- },
670
- inputContainerFocused: {
671
- borderColor: macOSColors.semantic.info + "60",
672
- shadowColor: macOSColors.semantic.info,
673
- shadowOffset: {
674
- width: 0,
675
- height: 0
676
- },
677
- shadowOpacity: 0.15,
678
- shadowRadius: 8
679
- },
680
- inputContainerError: {
681
- borderColor: macOSColors.semantic.error + "60",
682
- shadowColor: macOSColors.semantic.error,
683
- shadowOffset: {
684
- width: 0,
685
- height: 0
686
- },
687
- shadowOpacity: 0.15,
688
- shadowRadius: 8
689
- },
690
- inputContainerValid: {
691
- borderColor: macOSColors.semantic.success + "60",
692
- shadowColor: macOSColors.semantic.success,
693
- shadowOffset: {
694
- width: 0,
695
- height: 0
696
- },
697
- shadowOpacity: 0.15,
698
- shadowRadius: 8
699
- },
700
- input: {
701
- paddingHorizontal: 12,
702
- paddingVertical: 12,
703
- fontSize: 12,
704
- color: macOSColors.text.primary,
705
- fontFamily: Platform.OS === "ios" ? "Menlo" : "monospace",
706
- textAlign: "center",
707
- letterSpacing: 0.3,
708
- width: "100%"
709
- },
710
- activateButton: {
265
+ primaryButton: {
266
+ flexDirection: "row",
267
+ alignItems: "center",
268
+ justifyContent: "center",
269
+ gap: 8,
711
270
  backgroundColor: macOSColors.semantic.info,
712
271
  borderRadius: 8,
713
- paddingVertical: 12,
714
- alignItems: "center",
272
+ paddingVertical: 14,
715
273
  shadowColor: macOSColors.semantic.info,
716
274
  shadowOffset: {
717
275
  width: 0,
@@ -720,18 +278,11 @@ const styles = StyleSheet.create({
720
278
  shadowOpacity: 0.3,
721
279
  shadowRadius: 8
722
280
  },
723
- activateButtonDisabled: {
724
- backgroundColor: macOSColors.background.hover,
725
- shadowOpacity: 0
726
- },
727
- activateButtonText: {
728
- fontSize: 14,
281
+ primaryButtonText: {
282
+ fontSize: 15,
729
283
  fontWeight: "600",
730
284
  color: "#000"
731
285
  },
732
- activateButtonTextDisabled: {
733
- color: macOSColors.text.muted
734
- },
735
286
  divider: {
736
287
  flexDirection: "row",
737
288
  alignItems: "center",
@@ -749,196 +300,81 @@ const styles = StyleSheet.create({
749
300
  textTransform: "uppercase",
750
301
  letterSpacing: 0.5
751
302
  },
752
- purchaseButton: {
753
- flexDirection: "row",
754
- alignItems: "center",
755
- justifyContent: "center",
756
- gap: 6,
757
- paddingVertical: 10,
303
+ instructionsContainer: {
304
+ backgroundColor: macOSColors.background.hover,
758
305
  borderRadius: 8,
306
+ padding: 16,
759
307
  borderWidth: 1,
760
- borderColor: macOSColors.semantic.info + "30",
761
- backgroundColor: macOSColors.semantic.info + "10"
762
- },
763
- purchaseButtonText: {
764
- fontSize: 13,
765
- fontWeight: "600",
766
- color: macOSColors.semantic.info
767
- },
768
- stateContainer: {
769
- alignItems: "center",
770
- paddingVertical: 24,
771
- paddingTop: 16
772
- },
773
- loaderContainer: {
774
- width: 64,
775
- height: 64,
776
- borderRadius: 16,
777
- backgroundColor: macOSColors.semantic.info + "10",
778
- borderWidth: 1,
779
- borderColor: macOSColors.semantic.info + "20",
780
- alignItems: "center",
781
- justifyContent: "center",
782
- marginBottom: 16
308
+ borderColor: macOSColors.border.default
783
309
  },
784
- statusIconContainer: {
785
- width: 64,
786
- height: 64,
787
- borderRadius: 16,
310
+ instructionsHeader: {
311
+ flexDirection: "row",
788
312
  alignItems: "center",
789
- justifyContent: "center",
790
- marginBottom: 16,
791
- borderWidth: 1
792
- },
793
- successIconContainer: {
794
- backgroundColor: macOSColors.semantic.success + "15",
795
- borderColor: macOSColors.semantic.success + "30",
796
- shadowColor: macOSColors.semantic.success,
797
- shadowOffset: {
798
- width: 0,
799
- height: 0
800
- },
801
- shadowOpacity: 0.2,
802
- shadowRadius: 12
803
- },
804
- errorIconContainer: {
805
- backgroundColor: macOSColors.semantic.error + "15",
806
- borderColor: macOSColors.semantic.error + "30",
807
- shadowColor: macOSColors.semantic.error,
808
- shadowOffset: {
809
- width: 0,
810
- height: 0
811
- },
812
- shadowOpacity: 0.2,
813
- shadowRadius: 12
814
- },
815
- stateText: {
816
- fontSize: 16,
817
- fontWeight: "600",
818
- color: macOSColors.text.primary,
819
- marginBottom: 4
820
- },
821
- stateSubtext: {
822
- fontSize: 13,
823
- color: macOSColors.text.secondary,
824
- textAlign: "center",
825
- lineHeight: 18,
826
- paddingHorizontal: 16
827
- },
828
- successText: {
829
- fontSize: 18,
830
- fontWeight: "600",
831
- color: macOSColors.semantic.success,
832
- marginBottom: 4
313
+ gap: 8,
314
+ marginBottom: 12
833
315
  },
834
- errorText: {
835
- fontSize: 18,
316
+ instructionsTitle: {
317
+ fontSize: 12,
836
318
  fontWeight: "600",
837
- color: macOSColors.semantic.error,
838
- marginBottom: 4
319
+ color: macOSColors.text.secondary
839
320
  },
840
- secondaryButton: {
841
- marginTop: 20,
842
- paddingVertical: 10,
843
- paddingHorizontal: 20,
844
- borderRadius: 8,
321
+ codeBlock: {
322
+ backgroundColor: macOSColors.background.input,
323
+ borderRadius: 6,
324
+ padding: 12,
845
325
  borderWidth: 1,
846
- borderColor: macOSColors.border.default,
847
- backgroundColor: macOSColors.background.hover
848
- },
849
- secondaryButtonText: {
850
- fontSize: 13,
851
- fontWeight: "600",
852
- color: macOSColors.text.primary
326
+ borderColor: macOSColors.border.input,
327
+ position: "relative"
853
328
  },
854
- validationError: {
329
+ codeText: {
855
330
  fontSize: 11,
856
- color: macOSColors.semantic.error,
857
- marginTop: 6,
858
- textAlign: "center"
331
+ fontFamily: Platform.OS === "ios" ? "Menlo" : "monospace",
332
+ lineHeight: 18,
333
+ color: macOSColors.text.primary
859
334
  },
860
- validationSuccess: {
861
- fontSize: 11,
862
- color: macOSColors.semantic.success,
863
- marginTop: 6,
864
- textAlign: "center"
335
+ codeKeyword: {
336
+ color: "#FF79C6"
865
337
  },
866
- backButton: {
867
- marginTop: 16,
868
- alignItems: "center"
338
+ codeVariable: {
339
+ color: "#50FA7B"
869
340
  },
870
- backButtonText: {
871
- fontSize: 13,
872
- color: macOSColors.text.muted
341
+ codeFunction: {
342
+ color: "#8BE9FD"
873
343
  },
874
- deviceCount: {
875
- fontSize: 11,
876
- color: macOSColors.text.muted,
877
- marginTop: 8,
878
- textAlign: "center"
344
+ codeProperty: {
345
+ color: "#FFB86C"
879
346
  },
880
- deviceCountHeader: {
881
- fontSize: 12,
882
- color: macOSColors.text.secondary,
883
- marginTop: 8
347
+ codeString: {
348
+ color: "#F1FA8C"
884
349
  },
885
- deviceList: {
886
- maxHeight: 200,
887
- marginBottom: 16
350
+ codePunctuation: {
351
+ color: macOSColors.text.secondary
888
352
  },
889
- deviceItem: {
353
+ copyButton: {
354
+ position: "absolute",
355
+ top: 8,
356
+ right: 8,
890
357
  flexDirection: "row",
891
358
  alignItems: "center",
892
- padding: 12,
893
- backgroundColor: macOSColors.background.hover,
894
- borderRadius: 8,
895
- marginBottom: 8,
896
- borderWidth: 1,
897
- borderColor: macOSColors.border.default
898
- },
899
- deviceIcon: {
900
- width: 36,
901
- height: 36,
902
- borderRadius: 8,
903
- backgroundColor: macOSColors.background.input,
904
- alignItems: "center",
905
- justifyContent: "center",
906
- marginRight: 12
359
+ gap: 4,
360
+ paddingVertical: 4,
361
+ paddingHorizontal: 8,
362
+ borderRadius: 4,
363
+ backgroundColor: macOSColors.background.hover
907
364
  },
908
- deviceInfo: {
909
- flex: 1
365
+ copyText: {
366
+ fontSize: 10,
367
+ color: macOSColors.text.muted,
368
+ fontWeight: "500"
910
369
  },
911
- deviceName: {
912
- fontSize: 14,
913
- fontWeight: "500",
914
- color: macOSColors.text.primary,
915
- marginBottom: 2
370
+ copyTextSuccess: {
371
+ color: macOSColors.semantic.success
916
372
  },
917
- devicePlatform: {
373
+ instructionsNote: {
918
374
  fontSize: 11,
919
375
  color: macOSColors.text.muted,
920
- textTransform: "capitalize"
921
- },
922
- claimText: {
923
- fontSize: 12,
924
- fontWeight: "600",
925
- color: macOSColors.semantic.info
926
- },
927
- registerNewButton: {
928
- flexDirection: "row",
929
- alignItems: "center",
930
- justifyContent: "center",
931
- gap: 8,
932
- paddingVertical: 12,
933
- borderRadius: 8,
934
- borderWidth: 1,
935
- borderColor: macOSColors.semantic.info + "30",
936
- borderStyle: "dashed",
937
- marginBottom: 8
938
- },
939
- registerNewText: {
940
- fontSize: 13,
941
- fontWeight: "600",
942
- color: macOSColors.semantic.info
376
+ marginTop: 12,
377
+ textAlign: "center",
378
+ fontStyle: "italic"
943
379
  }
944
380
  });