@buoy-gg/core 6.0.1 → 7.0.0

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 (40) hide show
  1. package/README.md +7 -1
  2. package/lib/commonjs/floatingMenu/DevToolsSettingsModal.js +64 -0
  3. package/lib/commonjs/floatingMenu/FloatingDevTools.js +34 -10
  4. package/lib/commonjs/floatingMenu/FloatingDevTools.web.js +7 -1
  5. package/lib/commonjs/floatingMenu/autoDiscoverPresets.js +30 -15
  6. package/lib/commonjs/floatingMenu/autoExternalSync.js +43 -34
  7. package/lib/commonjs/floatingMenu/optionalRequire.js +33 -0
  8. package/lib/commonjs/floatingMenu/useDesktopSyncStatus.js +28 -0
  9. package/lib/module/floatingMenu/DevToolsSettingsModal.js +65 -1
  10. package/lib/module/floatingMenu/FloatingDevTools.js +35 -11
  11. package/lib/module/floatingMenu/FloatingDevTools.web.js +7 -1
  12. package/lib/module/floatingMenu/autoDiscoverPresets.js +31 -15
  13. package/lib/module/floatingMenu/autoExternalSync.js +44 -34
  14. package/lib/module/floatingMenu/optionalRequire.js +29 -0
  15. package/lib/module/floatingMenu/useDesktopSyncStatus.js +25 -0
  16. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
  17. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts +18 -4
  18. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  19. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts +6 -1
  20. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
  21. package/lib/typescript/commonjs/floatingMenu/autoDiscoverPresets.d.ts.map +1 -1
  22. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts +6 -1
  23. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts.map +1 -1
  24. package/lib/typescript/commonjs/floatingMenu/optionalRequire.d.ts +12 -0
  25. package/lib/typescript/commonjs/floatingMenu/optionalRequire.d.ts.map +1 -0
  26. package/lib/typescript/commonjs/floatingMenu/useDesktopSyncStatus.d.ts +14 -0
  27. package/lib/typescript/commonjs/floatingMenu/useDesktopSyncStatus.d.ts.map +1 -0
  28. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
  29. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts +18 -4
  30. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  31. package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts +6 -1
  32. package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
  33. package/lib/typescript/module/floatingMenu/autoDiscoverPresets.d.ts.map +1 -1
  34. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts +6 -1
  35. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts.map +1 -1
  36. package/lib/typescript/module/floatingMenu/optionalRequire.d.ts +12 -0
  37. package/lib/typescript/module/floatingMenu/optionalRequire.d.ts.map +1 -0
  38. package/lib/typescript/module/floatingMenu/useDesktopSyncStatus.d.ts +14 -0
  39. package/lib/typescript/module/floatingMenu/useDesktopSyncStatus.d.ts.map +1 -0
  40. package/package.json +18 -7
@@ -3,7 +3,8 @@
3
3
  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
- import { SentryBugIcon, WifiCircuitIcon, ImageOverlayIcon, RenderCountIcon, Info, Layers, ChevronRightIcon, ChevronDown, getStorageBackendType, persistentStorage, Database, Trash2, CheckCircle2, AlertTriangle, Zap, FileText, HardDrive, Copy, FileCode, copyToClipboard, LicenseEntryModal, SectionHeader } from "@buoy-gg/shared-ui";
6
+ import { useDesktopSyncStatus } from "./useDesktopSyncStatus.js";
7
+ import { SentryBugIcon, WifiCircuitIcon, ImageOverlayIcon, RenderCountIcon, Globe, Info, Layers, ChevronRightIcon, ChevronDown, getStorageBackendType, persistentStorage, Database, Trash2, CheckCircle2, AlertTriangle, Zap, FileText, HardDrive, Copy, FileCode, copyToClipboard, LicenseEntryModal, SectionHeader } from "@buoy-gg/shared-ui";
7
8
  import { EnvIcon, StorageIcon, RoutesIcon, NetworkIcon, QueryIcon, HighlighterIcon, ReduxIcon, EventsIcon } from "@buoy-gg/floating-tools-core";
8
9
  import { useDefaultConfig } from "./DefaultConfigContext.js";
9
10
  import { JsModal, devToolsStorageKeys } from "@buoy-gg/shared-ui";
@@ -159,6 +160,9 @@ export const DevToolsSettingsModal = ({
159
160
  const [activeTabLoaded, setActiveTabLoaded] = useState(false);
160
161
  const [expandedSettings, setExpandedSettings] = useState(new Set());
161
162
  const [showLicenseModal, setShowLicenseModal] = useState(false);
163
+ // null when external-sync isn't installed (or predates the status store);
164
+ // "idle" until AutoExternalSync initializes — the row hides in both cases.
165
+ const desktopSyncStatus = useDesktopSyncStatus();
162
166
 
163
167
  // Load persisted active tab on mount
164
168
  useEffect(() => {
@@ -809,6 +813,66 @@ export const DevToolsSettingsModal = ({
809
813
  children: clearSuccess ? "CLEARED" : isClearing ? "CLEARING..." : "CLEAR ALL SETTINGS"
810
814
  })]
811
815
  })]
816
+ }), desktopSyncStatus && desktopSyncStatus.state !== "idle" && /*#__PURE__*/_jsxs(View, {
817
+ style: [styles.storageStatusCard, {
818
+ borderColor: desktopSyncStatus.state === "connected" ? buoyColors.success + "60" : buoyColors.warning + "60"
819
+ }],
820
+ children: [/*#__PURE__*/_jsxs(View, {
821
+ style: styles.storageStatusHeader,
822
+ children: [/*#__PURE__*/_jsx(View, {
823
+ style: [styles.storageStatusIcon, {
824
+ backgroundColor: desktopSyncStatus.state === "connected" ? buoyColors.success + "15" : buoyColors.warning + "15"
825
+ }],
826
+ children: /*#__PURE__*/_jsx(Globe, {
827
+ size: 18,
828
+ color: desktopSyncStatus.state === "connected" ? buoyColors.success : buoyColors.warning
829
+ })
830
+ }), /*#__PURE__*/_jsxs(View, {
831
+ style: styles.storageStatusInfo,
832
+ children: [/*#__PURE__*/_jsx(Text, {
833
+ style: styles.storageStatusLabel,
834
+ children: "DESKTOP SYNC"
835
+ }), /*#__PURE__*/_jsxs(View, {
836
+ style: styles.storageStatusBadgeContainer,
837
+ children: [/*#__PURE__*/_jsx(View, {
838
+ style: [styles.storageStatusBadge, {
839
+ backgroundColor: desktopSyncStatus.state === "connected" ? buoyColors.success + "20" : buoyColors.warning + "20",
840
+ borderColor: desktopSyncStatus.state === "connected" ? buoyColors.success + "60" : buoyColors.warning + "60"
841
+ }],
842
+ children: /*#__PURE__*/_jsx(Text, {
843
+ style: [styles.storageStatusBadgeText, {
844
+ color: desktopSyncStatus.state === "connected" ? buoyColors.success : buoyColors.warning
845
+ }],
846
+ children: desktopSyncStatus.state === "connected" ? "CONNECTED" : desktopSyncStatus.state === "connecting" ? "CONNECTING..." : "RETRYING"
847
+ })
848
+ }), desktopSyncStatus.state === "connected" ? /*#__PURE__*/_jsx(CheckCircle2, {
849
+ size: 14,
850
+ color: buoyColors.success
851
+ }) : /*#__PURE__*/_jsx(AlertTriangle, {
852
+ size: 14,
853
+ color: buoyColors.warning
854
+ })]
855
+ })]
856
+ })]
857
+ }), /*#__PURE__*/_jsxs(Text, {
858
+ style: styles.storageStatusDescription,
859
+ children: [desktopSyncStatus.state === "connected" ? "Streaming tool data to Buoy Desktop at " : "Trying to reach Buoy Desktop at ", /*#__PURE__*/_jsx(Text, {
860
+ style: styles.adviceHintCode,
861
+ children: desktopSyncStatus.targetURL ?? "(unknown URL)"
862
+ }), desktopSyncStatus.state !== "connected" && " — is the desktop app running? Override with the externalSync socketURL option if it runs elsewhere."]
863
+ }), desktopSyncStatus.state === "retrying" && desktopSyncStatus.lastError && /*#__PURE__*/_jsxs(View, {
864
+ style: styles.adviceHint,
865
+ children: [/*#__PURE__*/_jsx(AlertTriangle, {
866
+ size: 14,
867
+ color: buoyColors.warning
868
+ }), /*#__PURE__*/_jsxs(Text, {
869
+ style: styles.adviceHintText,
870
+ children: [/*#__PURE__*/_jsx(Text, {
871
+ style: styles.adviceHintBold,
872
+ children: "Last error:"
873
+ }), " ", desktopSyncStatus.lastError]
874
+ })]
875
+ })]
812
876
  }), renderGlobalSettingCard("enableSharedModalDimensions", "SHARED MODAL SIZE", "MODAL", "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. Enable only if you prefer uniform modal sizes across all dev tools."), Platform.OS !== "web" && renderGlobalSettingCard("expandableWindowControls", "EXPAND CONTROLS", "MODAL", "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 — similar to iPad window controls. 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 (e.g. when using a mouse or simulator)."), isDevelopmentMode && /*#__PURE__*/_jsxs(View, {
813
877
  style: styles.exportConfigCard,
814
878
  children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
@@ -7,6 +7,7 @@ import { FloatingMenu } from "./FloatingMenu.js";
7
7
  import { AppOverlay } from "./AppHost.js";
8
8
  import { autoDiscoverPresets, autoDiscoverPresetsWithCustom } from "./autoDiscoverPresets.js";
9
9
  import { DevToolsVisibilityProvider } from "./DevToolsVisibilityContext.js";
10
+ import { reportOptionalRequireError } from "./optionalRequire.js";
10
11
  import { HintsProvider, LicenseEntryModal, normalizeEnvironment } from "@buoy-gg/shared-ui";
11
12
  import { MinimizedToolsProvider } from "./MinimizedToolsContext.js";
12
13
  import { validateDialConfig } from "./defaultConfig.js";
@@ -43,7 +44,7 @@ import { AutoExternalSync, isExternalSyncAvailable } from "./autoExternalSync.js
43
44
  * You don't need to pass anything to use network, storage, redux, etc. tools!
44
45
  * Just install the package and it works automatically.
45
46
  */
46
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
47
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
47
48
  /**
48
49
  * Unified floating development tools component with automatic preset discovery.
49
50
  *
@@ -111,6 +112,7 @@ export const FloatingDevTools = ({
111
112
  zustandStores,
112
113
  environment,
113
114
  externalSync = true,
115
+ headless = false,
114
116
  ...props
115
117
  }) => {
116
118
  const resolvedEnvironment = environment ?? normalizeEnvironment(process.env.NODE_ENV ?? "dev");
@@ -163,8 +165,8 @@ export const FloatingDevTools = ({
163
165
  watchStores
164
166
  } = require("@buoy-gg/zustand");
165
167
  return watchStores(zustandStores);
166
- } catch {
167
- // @buoy-gg/zustand not installed
168
+ } catch (e) {
169
+ reportOptionalRequireError("@buoy-gg/zustand", e);
168
170
  }
169
171
  }, []); // eslint-disable-line react-hooks/exhaustive-deps
170
172
 
@@ -192,7 +194,7 @@ export const FloatingDevTools = ({
192
194
  requiredEnvVars: createEnvVarConfig(requiredEnvVars)
193
195
  }));
194
196
  } catch (error) {
195
- // Package not installed, skip
197
+ reportOptionalRequireError("@buoy-gg/env", error);
196
198
  }
197
199
  }
198
200
 
@@ -206,7 +208,7 @@ export const FloatingDevTools = ({
206
208
  requiredStorageKeys
207
209
  }));
208
210
  } catch (error) {
209
- // Package not installed, skip
211
+ reportOptionalRequireError("@buoy-gg/storage", error);
210
212
  }
211
213
  }
212
214
  return overrides;
@@ -230,7 +232,8 @@ export const FloatingDevTools = ({
230
232
  DebugBordersStandaloneOverlay
231
233
  } = require("@buoy-gg/debug-borders");
232
234
  return DebugBordersStandaloneOverlay;
233
- } catch {
235
+ } catch (e) {
236
+ reportOptionalRequireError("@buoy-gg/debug-borders", e);
234
237
  return null;
235
238
  }
236
239
  }, []);
@@ -243,7 +246,8 @@ export const FloatingDevTools = ({
243
246
  HighlightUpdatesOverlay: Overlay
244
247
  } = require("@buoy-gg/highlight-updates");
245
248
  return Overlay;
246
- } catch {
249
+ } catch (e) {
250
+ reportOptionalRequireError("@buoy-gg/highlight-updates", e);
247
251
  return null;
248
252
  }
249
253
  }, []);
@@ -256,7 +260,8 @@ export const FloatingDevTools = ({
256
260
  ImageOverlayStandalone
257
261
  } = require("@buoy-gg/image-overlay");
258
262
  return ImageOverlayStandalone;
259
- } catch {
263
+ } catch (e) {
264
+ reportOptionalRequireError("@buoy-gg/image-overlay", e);
260
265
  return null;
261
266
  }
262
267
  }, []);
@@ -269,7 +274,8 @@ export const FloatingDevTools = ({
269
274
  PerfMonitorOverlay: Overlay
270
275
  } = require("@buoy-gg/perf-monitor");
271
276
  return Overlay;
272
- } catch {
277
+ } catch (e) {
278
+ reportOptionalRequireError("@buoy-gg/perf-monitor", e);
273
279
  return null;
274
280
  }
275
281
  }, []);
@@ -283,7 +289,8 @@ export const FloatingDevTools = ({
283
289
  RouteTracker: Tracker
284
290
  } = require("@buoy-gg/route-events");
285
291
  return Tracker;
286
- } catch {
292
+ } catch (e) {
293
+ reportOptionalRequireError("@buoy-gg/route-events", e);
287
294
  return null;
288
295
  }
289
296
  }, []);
@@ -296,7 +303,8 @@ export const FloatingDevTools = ({
296
303
  ImpersonateOverlay: Overlay
297
304
  } = require("@buoy-gg/impersonate");
298
305
  return Overlay;
299
- } catch {
306
+ } catch (e) {
307
+ reportOptionalRequireError("@buoy-gg/impersonate", e);
300
308
  return null;
301
309
  }
302
310
  }, []);
@@ -322,6 +330,22 @@ export const FloatingDevTools = ({
322
330
  return null;
323
331
  }
324
332
 
333
+ // Headless (sync-only): tool adapters + route tracking + desktop sync, no
334
+ // on-device UI. requireLicense is intentionally skipped — there's nothing
335
+ // to gate. All hooks above already ran, so hook order is preserved, and
336
+ // AutoExternalSync is additionally dev-gated (the broker must never be
337
+ // contacted in production).
338
+ if (headless) {
339
+ return /*#__PURE__*/_jsxs(_Fragment, {
340
+ children: [RouteTracker && /*#__PURE__*/_jsx(RouteTracker, {}), isDevelopment && externalSync !== false && isExternalSyncAvailable() && /*#__PURE__*/_jsx(AutoExternalSync, {
341
+ options: typeof externalSync === "object" ? externalSync : undefined,
342
+ requiredEnvVars: requiredEnvVars,
343
+ isPro: isLicensed,
344
+ licenseKey: licenseKey
345
+ })]
346
+ });
347
+ }
348
+
325
349
  // Gate: If requireLicense is enabled and no license, show only the license modal
326
350
  if (requireLicense && isInitialized && !licenseKey) {
327
351
  return /*#__PURE__*/_jsx(LicenseEntryModal, {
@@ -53,7 +53,8 @@ export function FloatingDevTools({
53
53
  availableEnvironments: _availableEnvironments,
54
54
  onEnvironmentSwitch: _onEnvironmentSwitch,
55
55
  apps,
56
- disableHints: _disableHints
56
+ disableHints: _disableHints,
57
+ headless = false
57
58
  }) {
58
59
  // Internal state for dial and settings
59
60
  const [isDialOpen, setIsDialOpen] = useState(false);
@@ -131,6 +132,11 @@ export function FloatingDevTools({
131
132
  setIsSettingsOpen(true);
132
133
  }, []);
133
134
  const handleCloseSettings = useCallback(() => setIsSettingsOpen(false), []);
135
+
136
+ // Headless: desktop sync is native-only, so there is nothing to mount on
137
+ // web — accept the prop for API parity and render nothing. (After all
138
+ // hooks so hook order is preserved.)
139
+ if (headless) return null;
134
140
  return /*#__PURE__*/_jsxs(_Fragment, {
135
141
  children: [/*#__PURE__*/_jsx(FloatingTools, {
136
142
  enablePositionPersistence: true,
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
 
3
+ import { reportOptionalRequireError } from "./optionalRequire.js";
4
+
3
5
  /**
4
6
  * Automatically discovers and loads dev tool presets from installed packages.
5
7
  *
@@ -63,7 +65,8 @@ export function autoDiscoverPresets() {
63
65
  envToolPreset
64
66
  } = require("@buoy-gg/env");
65
67
  return envToolPreset;
66
- } catch {
68
+ } catch (e) {
69
+ reportOptionalRequireError("@buoy-gg/env", e);
67
70
  return null;
68
71
  }
69
72
  }
@@ -78,7 +81,8 @@ export function autoDiscoverPresets() {
78
81
  networkToolPreset
79
82
  } = require("@buoy-gg/network");
80
83
  return networkToolPreset;
81
- } catch {
84
+ } catch (e) {
85
+ reportOptionalRequireError("@buoy-gg/network", e);
82
86
  return null;
83
87
  }
84
88
  }
@@ -93,7 +97,8 @@ export function autoDiscoverPresets() {
93
97
  storageToolPreset
94
98
  } = require("@buoy-gg/storage");
95
99
  return storageToolPreset;
96
- } catch {
100
+ } catch (e) {
101
+ reportOptionalRequireError("@buoy-gg/storage", e);
97
102
  return null;
98
103
  }
99
104
  }
@@ -109,7 +114,8 @@ export function autoDiscoverPresets() {
109
114
  wifiTogglePreset
110
115
  } = require("@buoy-gg/react-query");
111
116
  return [reactQueryToolPreset, wifiTogglePreset];
112
- } catch {
117
+ } catch (e) {
118
+ reportOptionalRequireError("@buoy-gg/react-query", e);
113
119
  return null;
114
120
  }
115
121
  }
@@ -124,7 +130,8 @@ export function autoDiscoverPresets() {
124
130
  routeEventsToolPreset
125
131
  } = require("@buoy-gg/route-events");
126
132
  return routeEventsToolPreset;
127
- } catch {
133
+ } catch (e) {
134
+ reportOptionalRequireError("@buoy-gg/route-events", e);
128
135
  return null;
129
136
  }
130
137
  }
@@ -139,7 +146,8 @@ export function autoDiscoverPresets() {
139
146
  debugBordersToolPreset
140
147
  } = require("@buoy-gg/debug-borders");
141
148
  return debugBordersToolPreset;
142
- } catch {
149
+ } catch (e) {
150
+ reportOptionalRequireError("@buoy-gg/debug-borders", e);
143
151
  return null;
144
152
  }
145
153
  }
@@ -155,7 +163,8 @@ export function autoDiscoverPresets() {
155
163
  highlightUpdatesModalPreset
156
164
  } = require("@buoy-gg/highlight-updates");
157
165
  return [highlightUpdatesModalPreset, highlightUpdatesPreset];
158
- } catch {
166
+ } catch (e) {
167
+ reportOptionalRequireError("@buoy-gg/highlight-updates", e);
159
168
  return null;
160
169
  }
161
170
  }
@@ -170,7 +179,8 @@ export function autoDiscoverPresets() {
170
179
  reduxToolPreset
171
180
  } = require("@buoy-gg/redux");
172
181
  return reduxToolPreset;
173
- } catch {
182
+ } catch (e) {
183
+ reportOptionalRequireError("@buoy-gg/redux", e);
174
184
  return null;
175
185
  }
176
186
  }
@@ -185,7 +195,8 @@ export function autoDiscoverPresets() {
185
195
  zustandToolPreset
186
196
  } = require("@buoy-gg/zustand");
187
197
  return zustandToolPreset;
188
- } catch {
198
+ } catch (e) {
199
+ reportOptionalRequireError("@buoy-gg/zustand", e);
189
200
  return null;
190
201
  }
191
202
  }
@@ -200,7 +211,8 @@ export function autoDiscoverPresets() {
200
211
  eventsToolPreset
201
212
  } = require("@buoy-gg/events");
202
213
  return eventsToolPreset;
203
- } catch {
214
+ } catch (e) {
215
+ reportOptionalRequireError("@buoy-gg/events", e);
204
216
  return null;
205
217
  }
206
218
  }
@@ -215,7 +227,8 @@ export function autoDiscoverPresets() {
215
227
  jotaiToolPreset
216
228
  } = require("@buoy-gg/jotai");
217
229
  return jotaiToolPreset;
218
- } catch {
230
+ } catch (e) {
231
+ reportOptionalRequireError("@buoy-gg/jotai", e);
219
232
  return null;
220
233
  }
221
234
  }
@@ -230,7 +243,8 @@ export function autoDiscoverPresets() {
230
243
  imageOverlayToolPreset
231
244
  } = require("@buoy-gg/image-overlay");
232
245
  return imageOverlayToolPreset;
233
- } catch {
246
+ } catch (e) {
247
+ reportOptionalRequireError("@buoy-gg/image-overlay", e);
234
248
  return null;
235
249
  }
236
250
  }
@@ -246,7 +260,8 @@ export function autoDiscoverPresets() {
246
260
  perfMonitorModalPreset
247
261
  } = require("@buoy-gg/perf-monitor");
248
262
  return [perfMonitorPreset, perfMonitorModalPreset];
249
- } catch {
263
+ } catch (e) {
264
+ reportOptionalRequireError("@buoy-gg/perf-monitor", e);
250
265
  return null;
251
266
  }
252
267
  }
@@ -267,8 +282,9 @@ export function autoDiscoverPresets() {
267
282
  }
268
283
  }
269
284
  } catch (error) {
270
- // Silently skip packages that aren't installed
271
- // This is expected behavior - not all packages will be installed
285
+ // Loaders swallow their own require failures; anything reaching here
286
+ // is a preset-shape problem in an installed package — surface it.
287
+ reportOptionalRequireError(name, error);
272
288
  }
273
289
  }
274
290
  return discoveredPresets;
@@ -5,7 +5,8 @@
5
5
  *
6
6
  * When @buoy-gg/external-sync is installed, FloatingDevTools renders
7
7
  * <AutoExternalSync /> in dev builds: it connects to the Buoy Desktop broker
8
- * (localhost:42831 — a silent no-op when the desktop app isn't running) and
8
+ * (port 42831 on the Metro host, so physical devices reach the dev machine
9
+ * automatically — a background retry when the desktop app isn't running) and
9
10
  * registers a sync adapter for every installed tool package, discovered with
10
11
  * the same guarded-require pattern as autoDiscoverPresets. No <DevToolsSync>
11
12
  * wiring needed in the app.
@@ -15,6 +16,8 @@
15
16
  */
16
17
  import { useEffect, useMemo, useRef } from "react";
17
18
  import { Platform } from "react-native";
19
+ import { reportOptionalRequireError } from "./optionalRequire.js";
20
+
18
21
  /* eslint-disable @typescript-eslint/no-var-requires */
19
22
 
20
23
  // Module-level guarded requires: presence is constant for the app lifetime,
@@ -41,85 +44,90 @@ let debugBordersModule = null;
41
44
  let impersonateModule = null;
42
45
  let perfMonitorModule = null;
43
46
  let expoConstantsModule = null;
47
+
48
+ // Catch bodies report through reportOptionalRequireError: silent for a
49
+ // genuinely absent package, one dev warning when an installed package fails
50
+ // to load (broken transitive resolve, top-level crash).
44
51
  try {
45
52
  externalSyncModule = require("@buoy-gg/external-sync");
46
- } catch {
47
- // not installed
53
+ } catch (e) {
54
+ reportOptionalRequireError("@buoy-gg/external-sync", e);
48
55
  }
49
56
  try {
50
57
  storageModule = require("@buoy-gg/storage");
51
- } catch {
52
- // not installed
58
+ } catch (e) {
59
+ reportOptionalRequireError("@buoy-gg/storage", e);
53
60
  }
54
61
  try {
55
62
  networkModule = require("@buoy-gg/network");
56
- } catch {
57
- // not installed
63
+ } catch (e) {
64
+ reportOptionalRequireError("@buoy-gg/network", e);
58
65
  }
59
66
  try {
60
67
  reactQueryModule = require("@buoy-gg/react-query");
61
- } catch {
62
- // not installed
68
+ } catch (e) {
69
+ reportOptionalRequireError("@buoy-gg/react-query", e);
63
70
  }
64
71
  try {
65
72
  routeEventsModule = require("@buoy-gg/route-events");
66
- } catch {
67
- // not installed
73
+ } catch (e) {
74
+ reportOptionalRequireError("@buoy-gg/route-events", e);
68
75
  }
69
76
  try {
70
77
  reduxModule = require("@buoy-gg/redux");
71
- } catch {
72
- // not installed
78
+ } catch (e) {
79
+ reportOptionalRequireError("@buoy-gg/redux", e);
73
80
  }
74
81
  try {
75
82
  zustandModule = require("@buoy-gg/zustand");
76
- } catch {
77
- // not installed
83
+ } catch (e) {
84
+ reportOptionalRequireError("@buoy-gg/zustand", e);
78
85
  }
79
86
  try {
80
87
  jotaiModule = require("@buoy-gg/jotai");
81
- } catch {
82
- // not installed
88
+ } catch (e) {
89
+ reportOptionalRequireError("@buoy-gg/jotai", e);
83
90
  }
84
91
  try {
85
92
  eventsModule = require("@buoy-gg/events");
86
- } catch {
87
- // not installed
93
+ } catch (e) {
94
+ reportOptionalRequireError("@buoy-gg/events", e);
88
95
  }
89
96
  try {
90
97
  consoleModule = require("@buoy-gg/console");
91
- } catch {
92
- // not installed
98
+ } catch (e) {
99
+ reportOptionalRequireError("@buoy-gg/console", e);
93
100
  }
94
101
  try {
95
102
  envModule = require("@buoy-gg/env");
96
- } catch {
97
- // not installed
103
+ } catch (e) {
104
+ reportOptionalRequireError("@buoy-gg/env", e);
98
105
  }
99
106
  try {
100
107
  highlightUpdatesModule = require("@buoy-gg/highlight-updates");
101
- } catch {
102
- // not installed
108
+ } catch (e) {
109
+ reportOptionalRequireError("@buoy-gg/highlight-updates", e);
103
110
  }
104
111
  try {
105
112
  debugBordersModule = require("@buoy-gg/debug-borders");
106
- } catch {
107
- // not installed
113
+ } catch (e) {
114
+ reportOptionalRequireError("@buoy-gg/debug-borders", e);
108
115
  }
109
116
  try {
110
117
  impersonateModule = require("@buoy-gg/impersonate");
111
- } catch {
112
- // not installed
118
+ } catch (e) {
119
+ reportOptionalRequireError("@buoy-gg/impersonate", e);
113
120
  }
114
121
  try {
115
122
  perfMonitorModule = require("@buoy-gg/perf-monitor");
116
- } catch {
117
- // not installed
123
+ } catch (e) {
124
+ reportOptionalRequireError("@buoy-gg/perf-monitor", e);
118
125
  }
119
126
  try {
120
127
  expoConstantsModule = require("expo-constants");
121
- } catch {
122
- // not installed (RN CLI apps)
128
+ } catch (e) {
129
+ // not installed (RN CLI apps) — still report real load failures
130
+ reportOptionalRequireError("expo-constants", e);
123
131
  }
124
132
 
125
133
  /** Whether zero-config sync can run (the external-sync package is installed). */
@@ -159,7 +167,9 @@ export function AutoExternalSync({
159
167
  socket
160
168
  } = useExternalSyncSocket({
161
169
  deviceName: options?.deviceName ?? identity.deviceName,
162
- socketURL: options?.socketURL ?? "http://localhost:42831",
170
+ // Older external-sync versions predate getDefaultSocketURL and require
171
+ // an explicit URL — keep the literal fallback for them.
172
+ socketURL: options?.socketURL ?? externalSyncModule.getDefaultSocketURL?.() ?? "http://localhost:42831",
163
173
  persistentDeviceId: deviceId,
164
174
  platform: Platform.OS,
165
175
  enableLogs: options?.enableLogs
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+
3
+ const isDev = typeof __DEV__ !== "undefined" && __DEV__;
4
+ const reported = new Set();
5
+ const MODULE_NOT_FOUND_RE = /could not be found within the project|Cannot find module|Unable to resolve module/i;
6
+
7
+ /**
8
+ * Call from the CATCH block of a guarded optional require (the require
9
+ * literal itself must stay lexically inside try{} for Metro to treat the
10
+ * dependency as optional — this helper only inspects the failure).
11
+ *
12
+ * Silent when the package simply isn't installed. Warns once per module
13
+ * (dev only) when the package IS present but threw during import — a broken
14
+ * transitive resolve (subpath exports, version skew) or a top-level crash —
15
+ * which otherwise fails silently and makes tools vanish with no clue.
16
+ */
17
+ export function reportOptionalRequireError(moduleName, err) {
18
+ if (!isDev) return;
19
+ const message = err instanceof Error ? err.message : String(err);
20
+ const code = err?.code;
21
+ const isNotFound = code === "MODULE_NOT_FOUND" || MODULE_NOT_FOUND_RE.test(message);
22
+ // "Not found" naming the module itself = expected (not installed).
23
+ // "Not found" naming a DIFFERENT module = broken transitive dep inside an
24
+ // installed package — a real error worth surfacing.
25
+ if (isNotFound && message.includes(moduleName)) return;
26
+ if (reported.has(moduleName)) return;
27
+ reported.add(moduleName);
28
+ console.warn(`[buoy] "${moduleName}" is installed but failed to load — its tools won't appear. ` + `Underlying error: ${message}`);
29
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ import { useSyncExternalStore } from "react";
4
+ import { reportOptionalRequireError } from "./optionalRequire.js";
5
+
6
+ /* eslint-disable @typescript-eslint/no-var-requires */
7
+
8
+ // Guarded module-level require (same Metro constraint as autoExternalSync:
9
+ // the require literal must sit lexically inside try{} to stay optional).
10
+ let syncModule = null;
11
+ try {
12
+ syncModule = require("@buoy-gg/external-sync");
13
+ } catch (e) {
14
+ reportOptionalRequireError("@buoy-gg/external-sync", e);
15
+ }
16
+ const noopSubscribe = () => () => {};
17
+
18
+ /**
19
+ * Live desktop-sync status for the settings modal. Returns null when
20
+ * external-sync isn't installed or predates the sync-status store — the
21
+ * caller hides the status row in that case.
22
+ */
23
+ export function useDesktopSyncStatus() {
24
+ return useSyncExternalStore(syncModule?.subscribeSyncStatus ?? noopSubscribe, () => syncModule?.getSyncStatus?.() ?? null);
25
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"DevToolsSettingsModal.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/DevToolsSettingsModal.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6C,EAAE,EAAE,MAAM,OAAO,CAAC;AAsJtE;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC;;;OAGG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,uFAAuF;IACvF,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QACvC,WAAW,EAAE,OAAO,CAAC;KACtB,CAAC;IACF,kDAAkD;IAClD,cAAc,CAAC,EAAE,sBAAsB,CAAC;CACzC;AAED,UAAU,0BAA0B;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACxD,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC,aAAa,CAAC,EAAE;QACd,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;KAChC,EAAE,CAAC;CACL;AA2DD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,EAAE,CAAC,0BAA0B,CA+8BhE,CAAC;AAwDF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB;;;CAuD/B,CAAC"}
1
+ {"version":3,"file":"DevToolsSettingsModal.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/DevToolsSettingsModal.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6C,EAAE,EAAE,MAAM,OAAO,CAAC;AAuJtE;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC;;;OAGG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,uFAAuF;IACvF,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QACvC,WAAW,EAAE,OAAO,CAAC;KACtB,CAAC;IACF,kDAAkD;IAClD,cAAc,CAAC,EAAE,sBAAsB,CAAC;CACzC;AAED,UAAU,0BAA0B;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACxD,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC,aAAa,CAAC,EAAE;QACd,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;KAChC,EAAE,CAAC;CACL;AA2DD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,EAAE,CAAC,0BAA0B,CAwiChE,CAAC;AAwDF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB;;;CAuD/B,CAAC"}
@@ -234,9 +234,11 @@ export interface FloatingDevToolsProps extends Omit<FloatingMenuProps, "apps"> {
234
234
  * Zero-config sync to the Buoy Desktop dashboard.
235
235
  *
236
236
  * Enabled by default in dev builds whenever @buoy-gg/external-sync is
237
- * installed: connects to the desktop broker (localhost:42831 — a silent
238
- * no-op when the desktop app isn't running) and registers a sync adapter
239
- * for every installed tool automatically. Nothing else to render.
237
+ * installed: connects to the desktop broker on port 42831 of the Metro
238
+ * host (derived from the bundle's scriptURL, so physical devices reach
239
+ * the dev machine automatically; retries quietly when the desktop app
240
+ * isn't running) and registers a sync adapter for every installed tool.
241
+ * Nothing else to render.
240
242
  *
241
243
  * Pass `false` to disable, or options to customize the device identity /
242
244
  * broker URL:
@@ -247,6 +249,18 @@ export interface FloatingDevToolsProps extends Omit<FloatingMenuProps, "apps"> {
247
249
  * ```
248
250
  */
249
251
  externalSync?: boolean | ExternalSyncOptions;
252
+ /**
253
+ * Headless (sync-only) mode: mount the desktop sync adapters and route
254
+ * tracking but render NO on-device UI — no floating bubble, dial, or
255
+ * overlays. For builds that ship to non-developers (field/associate
256
+ * builds) where the desktop dashboard is the only debugging surface.
257
+ *
258
+ * `requireLicense` is ignored in headless mode (there is no UI to gate);
259
+ * the production free-tier gate still applies. Native only.
260
+ *
261
+ * @default false
262
+ */
263
+ headless?: boolean;
250
264
  }
251
265
  /**
252
266
  * Unified floating development tools component with automatic preset discovery.
@@ -302,5 +316,5 @@ export interface FloatingDevToolsProps extends Omit<FloatingMenuProps, "apps"> {
302
316
  *
303
317
  * @param props - FloatingDevTools props
304
318
  */
305
- export declare const FloatingDevTools: ({ apps, requiredEnvVars, requiredStorageKeys, children, disableHints, defaultFloatingTools, defaultDialTools, requireLicense, licenseKey: licenseKeyProp, zustandStores, environment, externalSync, ...props }: FloatingDevToolsProps) => React.JSX.Element | null;
319
+ export declare const FloatingDevTools: ({ apps, requiredEnvVars, requiredStorageKeys, children, disableHints, defaultFloatingTools, defaultDialTools, requireLicense, licenseKey: licenseKeyProp, zustandStores, environment, externalSync, headless, ...props }: FloatingDevToolsProps) => React.JSX.Element | null;
306
320
  //# sourceMappingURL=FloatingDevTools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FloatingDevTools.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/FloatingDevTools.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA+D,MAAM,OAAO,CAAC;AAGpF,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAMtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI5C,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EAEvB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,YAAY,GACpB,MAAM,GACN;IACE,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GACD;IACE,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EACR,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,OAAO,GACP,KAAK,CAAC;IACV,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEN;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;CAC1C;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC;IAC5E;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,IAAI,CAAC,EAAE,YAAY,EAAE,CAAC;IAEtB;;;;;;;;;;;;;;OAcG;IACH,eAAe,CAAC,EAAE,YAAY,EAAE,CAAC;IAEjC;;;;;;;;;;;;OAYG;IACH,mBAAmB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAEzC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE3B;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;;;;;;;;;;OAaG;IACH,oBAAoB,CAAC,EAAE,qBAAqB,CAAC;IAE7C;;;;;;;;;;;;;;OAcG;IACH,gBAAgB,CAAC,EAAE,iBAAiB,CAAC;IAErC;;;;;;;;;;;;;;;;OAgBG;IACH,qBAAqB,CAAC,EAAE,OAAO,oBAAoB,EAAE,WAAW,EAAE,CAAC;IAEnE;;;;;;;;;;;;;OAaG;IACH,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,oBAAoB,EAAE,WAAW,KAAK,IAAI,CAAC;IAEtF;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAEpC;;;;;;;;;;;;;;;OAeG;IACH,YAAY,CAAC,EAAE,OAAO,GAAG,mBAAmB,CAAC;CAC9C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,eAAO,MAAM,gBAAgB,GAAI,gNAc9B,qBAAqB,6BAoQvB,CAAC"}
1
+ {"version":3,"file":"FloatingDevTools.d.ts","sourceRoot":"","sources":["../../../../src/floatingMenu/FloatingDevTools.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA+D,MAAM,OAAO,CAAC;AAGpF,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAMtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAK5C,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EAEvB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,YAAY,GACpB,MAAM,GACN;IACE,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GACD;IACE,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EACR,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,OAAO,GACP,KAAK,CAAC;IACV,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEN;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;CAC1C;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC;IAC5E;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,IAAI,CAAC,EAAE,YAAY,EAAE,CAAC;IAEtB;;;;;;;;;;;;;;OAcG;IACH,eAAe,CAAC,EAAE,YAAY,EAAE,CAAC;IAEjC;;;;;;;;;;;;OAYG;IACH,mBAAmB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAEzC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE3B;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;;;;;;;;;;OAaG;IACH,oBAAoB,CAAC,EAAE,qBAAqB,CAAC;IAE7C;;;;;;;;;;;;;;OAcG;IACH,gBAAgB,CAAC,EAAE,iBAAiB,CAAC;IAErC;;;;;;;;;;;;;;;;OAgBG;IACH,qBAAqB,CAAC,EAAE,OAAO,oBAAoB,EAAE,WAAW,EAAE,CAAC;IAEnE;;;;;;;;;;;;;OAaG;IACH,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,oBAAoB,EAAE,WAAW,KAAK,IAAI,CAAC;IAEtF;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAEpC;;;;;;;;;;;;;;;;;OAiBG;IACH,YAAY,CAAC,EAAE,OAAO,GAAG,mBAAmB,CAAC;IAE7C;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,eAAO,MAAM,gBAAgB,GAAI,0NAe9B,qBAAqB,6BAmSvB,CAAC"}