@buoy-gg/core 3.0.1 → 3.0.2

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 (43) hide show
  1. package/lib/commonjs/floatingMenu/DevToolsSettingsModal.js +1 -1
  2. package/lib/commonjs/floatingMenu/FloatingDevTools.js +6 -1
  3. package/lib/commonjs/floatingMenu/FloatingMenu.js +3 -7
  4. package/lib/commonjs/floatingMenu/autoExternalSync.js +215 -0
  5. package/lib/commonjs/floatingMenu/defaultConfig.js +1 -1
  6. package/lib/commonjs/floatingMenu/dial/DialDevTools.js +59 -47
  7. package/lib/commonjs/floatingMenu/dial/DialIcon.js +12 -13
  8. package/lib/commonjs/floatingMenu/dial/OnboardingTooltip.js +1 -1
  9. package/lib/module/floatingMenu/DevToolsSettingsModal.js +2 -2
  10. package/lib/module/floatingMenu/FloatingDevTools.js +6 -1
  11. package/lib/module/floatingMenu/FloatingMenu.js +4 -7
  12. package/lib/module/floatingMenu/autoExternalSync.js +209 -0
  13. package/lib/module/floatingMenu/defaultConfig.js +1 -1
  14. package/lib/module/floatingMenu/dial/DialDevTools.js +62 -48
  15. package/lib/module/floatingMenu/dial/DialIcon.js +15 -15
  16. package/lib/module/floatingMenu/dial/OnboardingTooltip.js +2 -1
  17. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
  18. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts +19 -1
  19. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  20. package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts.map +1 -1
  21. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts +21 -0
  22. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts.map +1 -0
  23. package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts +1 -1
  24. package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts.map +1 -1
  25. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  26. package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts.map +1 -1
  27. package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.d.ts.map +1 -1
  28. package/lib/typescript/commonjs/index.d.ts +1 -0
  29. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  30. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
  31. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts +19 -1
  32. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  33. package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts.map +1 -1
  34. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts +21 -0
  35. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts.map +1 -0
  36. package/lib/typescript/module/floatingMenu/defaultConfig.d.ts +1 -1
  37. package/lib/typescript/module/floatingMenu/defaultConfig.d.ts.map +1 -1
  38. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  39. package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts.map +1 -1
  40. package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.d.ts.map +1 -1
  41. package/lib/typescript/module/index.d.ts +1 -0
  42. package/lib/typescript/module/index.d.ts.map +1 -1
  43. package/package.json +5 -5
@@ -808,7 +808,7 @@ const DevToolsSettingsModal = ({
808
808
  children: clearSuccess ? "CLEARED" : isClearing ? "CLEARING..." : "CLEAR ALL SETTINGS"
809
809
  })]
810
810
  })]
811
- }), 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."), 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__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
811
+ }), 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."), _reactNative.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__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
812
812
  style: styles.exportConfigCard,
813
813
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
814
814
  activeOpacity: 0.85,
@@ -15,6 +15,7 @@ var _MinimizedToolsContext = require("./MinimizedToolsContext.js");
15
15
  var _defaultConfig = require("./defaultConfig.js");
16
16
  var _DefaultConfigContext = require("./DefaultConfigContext.js");
17
17
  var _license = require("@buoy-gg/license");
18
+ var _autoExternalSync = require("./autoExternalSync.js");
18
19
  var _jsxRuntime = require("react/jsx-runtime");
19
20
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
20
21
  /**
@@ -113,6 +114,7 @@ const FloatingDevTools = ({
113
114
  licenseKey: licenseKeyProp,
114
115
  zustandStores,
115
116
  environment,
117
+ externalSync = true,
116
118
  ...props
117
119
  }) => {
118
120
  const resolvedEnvironment = environment ?? (0, _sharedUi.normalizeEnvironment)(process.env.NODE_ENV ?? "dev");
@@ -342,7 +344,10 @@ const FloatingDevTools = ({
342
344
  environment: resolvedEnvironment
343
345
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_AppHost.AppOverlay, {})]
344
346
  })
345
- }), children, DebugBordersOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(DebugBordersOverlay, {}), HighlightUpdatesOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(HighlightUpdatesOverlay, {}), ImageOverlayOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(ImageOverlayOverlay, {}), PerfMonitorOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(PerfMonitorOverlay, {}), ImpersonateOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(ImpersonateOverlay, {}), RouteTracker && /*#__PURE__*/(0, _jsxRuntime.jsx)(RouteTracker, {})]
347
+ }), children, DebugBordersOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(DebugBordersOverlay, {}), HighlightUpdatesOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(HighlightUpdatesOverlay, {}), ImageOverlayOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(ImageOverlayOverlay, {}), PerfMonitorOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(PerfMonitorOverlay, {}), ImpersonateOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(ImpersonateOverlay, {}), RouteTracker && /*#__PURE__*/(0, _jsxRuntime.jsx)(RouteTracker, {}), isDevelopment && externalSync !== false && (0, _autoExternalSync.isExternalSyncAvailable)() && /*#__PURE__*/(0, _jsxRuntime.jsx)(_autoExternalSync.AutoExternalSync, {
348
+ options: typeof externalSync === "object" ? externalSync : undefined,
349
+ requiredEnvVars: requiredEnvVars
350
+ })]
346
351
  })
347
352
  })
348
353
  })
@@ -6,9 +6,9 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.FloatingMenu = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
8
  var _reactNative = require("react-native");
9
+ var _sharedUi = require("@buoy-gg/shared-ui");
9
10
  var _floatingTools = require("./floatingTools");
10
11
  var _DialDevTools = require("./dial/DialDevTools");
11
- var _sharedUi = require("@buoy-gg/shared-ui");
12
12
  var _DevToolsSettingsModal = require("./DevToolsSettingsModal");
13
13
  var _AppHost = require("./AppHost.js");
14
14
  var _DevToolsVisibilityContext = require("./DevToolsVisibilityContext.js");
@@ -27,10 +27,6 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
27
27
  */
28
28
  const FLOATING_MENU_ONBOARDING_KEY = "@react_buoy_floating_menu_tooltip_shown";
29
29
  const ONBOARDING_STEP_KEY = "@react_buoy_onboarding_step";
30
- const {
31
- width: SCREEN_WIDTH,
32
- height: SCREEN_HEIGHT
33
- } = _reactNative.Dimensions.get("window");
34
30
  const FloatingMenu = ({
35
31
  apps,
36
32
  state,
@@ -328,11 +324,11 @@ const styles = _reactNative.StyleSheet.create({
328
324
  fontWeight: "900"
329
325
  },
330
326
  onboardingContainer: {
331
- ..._reactNative.StyleSheet.absoluteFillObject,
327
+ ..._sharedUi.absoluteFill,
332
328
  zIndex: 10000
333
329
  },
334
330
  onboardingBackdrop: {
335
- ..._reactNative.StyleSheet.absoluteFillObject,
331
+ ..._sharedUi.absoluteFill,
336
332
  backgroundColor: "rgba(0, 0, 0, 0.85)"
337
333
  }
338
334
  });
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.AutoExternalSync = AutoExternalSync;
7
+ exports.isExternalSyncAvailable = isExternalSyncAvailable;
8
+ var _react = require("react");
9
+ var _reactNative = require("react-native");
10
+ /**
11
+ * Zero-config device→desktop sync for FloatingDevTools.
12
+ *
13
+ * When @buoy-gg/external-sync is installed, FloatingDevTools renders
14
+ * <AutoExternalSync /> in dev builds: it connects to the Buoy Desktop broker
15
+ * (localhost:42831 — a silent no-op when the desktop app isn't running) and
16
+ * registers a sync adapter for every installed tool package, discovered with
17
+ * the same guarded-require pattern as autoDiscoverPresets. No <DevToolsSync>
18
+ * wiring needed in the app.
19
+ *
20
+ * Identity defaults come from expo-constants when available (app name →
21
+ * "MyApp (ios)" / "myapp-ios"), overridable via the `externalSync` prop.
22
+ */
23
+
24
+ /* eslint-disable @typescript-eslint/no-var-requires */
25
+
26
+ // Module-level guarded requires: presence is constant for the app lifetime,
27
+ // so hooks pulled from these modules keep a stable call order.
28
+ //
29
+ // IMPORTANT: each require() must sit LEXICALLY inside its own try block
30
+ // (same pattern as autoDiscoverPresets) — Metro only marks a dependency as
31
+ // optional when the require is directly inside a try statement. Wrapping it
32
+ // in a helper/arrow makes Metro hard-fail the bundle for apps that don't
33
+ // install that package.
34
+ let externalSyncModule = null;
35
+ let storageModule = null;
36
+ let networkModule = null;
37
+ let reactQueryModule = null;
38
+ let routeEventsModule = null;
39
+ let reduxModule = null;
40
+ let zustandModule = null;
41
+ let jotaiModule = null;
42
+ let eventsModule = null;
43
+ let envModule = null;
44
+ let highlightUpdatesModule = null;
45
+ let debugBordersModule = null;
46
+ let impersonateModule = null;
47
+ let perfMonitorModule = null;
48
+ let expoConstantsModule = null;
49
+ try {
50
+ externalSyncModule = require("@buoy-gg/external-sync");
51
+ } catch {
52
+ // not installed
53
+ }
54
+ try {
55
+ storageModule = require("@buoy-gg/storage");
56
+ } catch {
57
+ // not installed
58
+ }
59
+ try {
60
+ networkModule = require("@buoy-gg/network");
61
+ } catch {
62
+ // not installed
63
+ }
64
+ try {
65
+ reactQueryModule = require("@buoy-gg/react-query");
66
+ } catch {
67
+ // not installed
68
+ }
69
+ try {
70
+ routeEventsModule = require("@buoy-gg/route-events");
71
+ } catch {
72
+ // not installed
73
+ }
74
+ try {
75
+ reduxModule = require("@buoy-gg/redux");
76
+ } catch {
77
+ // not installed
78
+ }
79
+ try {
80
+ zustandModule = require("@buoy-gg/zustand");
81
+ } catch {
82
+ // not installed
83
+ }
84
+ try {
85
+ jotaiModule = require("@buoy-gg/jotai");
86
+ } catch {
87
+ // not installed
88
+ }
89
+ try {
90
+ eventsModule = require("@buoy-gg/events");
91
+ } catch {
92
+ // not installed
93
+ }
94
+ try {
95
+ envModule = require("@buoy-gg/env");
96
+ } catch {
97
+ // not installed
98
+ }
99
+ try {
100
+ highlightUpdatesModule = require("@buoy-gg/highlight-updates");
101
+ } catch {
102
+ // not installed
103
+ }
104
+ try {
105
+ debugBordersModule = require("@buoy-gg/debug-borders");
106
+ } catch {
107
+ // not installed
108
+ }
109
+ try {
110
+ impersonateModule = require("@buoy-gg/impersonate");
111
+ } catch {
112
+ // not installed
113
+ }
114
+ try {
115
+ perfMonitorModule = require("@buoy-gg/perf-monitor");
116
+ } catch {
117
+ // not installed
118
+ }
119
+ try {
120
+ expoConstantsModule = require("expo-constants");
121
+ } catch {
122
+ // not installed (RN CLI apps)
123
+ }
124
+
125
+ /** Whether zero-config sync can run (the external-sync package is installed). */
126
+ function isExternalSyncAvailable() {
127
+ return !!externalSyncModule?.useExternalSyncSocket;
128
+ }
129
+ function getAppName() {
130
+ const constants = expoConstantsModule?.default ?? expoConstantsModule;
131
+ return constants?.expoConfig?.name ?? constants?.manifest2?.extra?.expoClient?.name ?? null;
132
+ }
133
+ function defaultIdentity() {
134
+ const appName = getAppName();
135
+ const slug = (appName ?? "buoy").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
136
+ return {
137
+ deviceName: `${appName ?? "Buoy Device"} (${_reactNative.Platform.OS})`,
138
+ deviceId: `${slug || "buoy"}-${_reactNative.Platform.OS}`
139
+ };
140
+ }
141
+ function AutoExternalSync({
142
+ options,
143
+ requiredEnvVars
144
+ }) {
145
+ const {
146
+ useExternalSyncSocket,
147
+ useExternalSync
148
+ } = externalSyncModule;
149
+
150
+ // Context-based query adapter; null when there is no QueryClientProvider
151
+ // above FloatingDevTools (or the installed @buoy-gg/react-query predates
152
+ // the hook).
153
+ const queryAdapter = reactQueryModule?.useReactQuerySyncAdapter ? reactQueryModule.useReactQuerySyncAdapter() : null;
154
+ const identity = (0, _react.useMemo)(defaultIdentity, []);
155
+ const deviceId = options?.deviceId ?? identity.deviceId;
156
+ const {
157
+ socket
158
+ } = useExternalSyncSocket({
159
+ deviceName: options?.deviceName ?? identity.deviceName,
160
+ socketURL: options?.socketURL ?? "http://localhost:42831",
161
+ persistentDeviceId: deviceId,
162
+ platform: _reactNative.Platform.OS,
163
+ enableLogs: options?.enableLogs
164
+ });
165
+ const tools = (0, _react.useMemo)(() => {
166
+ const map = {};
167
+ if (storageModule?.storageSyncAdapter) {
168
+ map.storage = storageModule.storageSyncAdapter;
169
+ }
170
+ if (networkModule?.networkSyncAdapter) {
171
+ map.network = networkModule.networkSyncAdapter;
172
+ }
173
+ if (queryAdapter) {
174
+ map.query = queryAdapter;
175
+ }
176
+ if (routeEventsModule?.routeEventsSyncAdapter) {
177
+ map["route-events"] = routeEventsModule.routeEventsSyncAdapter;
178
+ }
179
+ if (reduxModule?.reduxSyncAdapter) {
180
+ map.redux = reduxModule.reduxSyncAdapter;
181
+ }
182
+ if (zustandModule?.zustandSyncAdapter) {
183
+ map.zustand = zustandModule.zustandSyncAdapter;
184
+ }
185
+ if (jotaiModule?.jotaiSyncAdapter) {
186
+ map.jotai = jotaiModule.jotaiSyncAdapter;
187
+ }
188
+ if (eventsModule?.eventsSyncAdapter) {
189
+ map.events = eventsModule.eventsSyncAdapter;
190
+ }
191
+ if (envModule?.createEnvSyncAdapter) {
192
+ map.env = envModule.createEnvSyncAdapter(requiredEnvVars ?? []);
193
+ }
194
+ if (highlightUpdatesModule?.highlightUpdatesSyncAdapter) {
195
+ map["highlight-updates"] = highlightUpdatesModule.highlightUpdatesSyncAdapter;
196
+ }
197
+ if (debugBordersModule?.debugBordersSyncAdapter) {
198
+ map["debug-borders"] = debugBordersModule.debugBordersSyncAdapter;
199
+ }
200
+ if (impersonateModule?.impersonateSyncAdapter) {
201
+ map.impersonate = impersonateModule.impersonateSyncAdapter;
202
+ }
203
+ if (perfMonitorModule?.perfMonitorSyncAdapter) {
204
+ map["perf-monitor"] = perfMonitorModule.perfMonitorSyncAdapter;
205
+ }
206
+ return map;
207
+ }, [queryAdapter, requiredEnvVars]);
208
+ useExternalSync({
209
+ tools,
210
+ socket,
211
+ deviceId,
212
+ enableLogs: options?.enableLogs
213
+ });
214
+ return null;
215
+ }
@@ -32,7 +32,7 @@ exports.validateDialConfig = validateDialConfig;
32
32
  * This is a union type of all auto-discovered tool IDs.
33
33
  */
34
34
 
35
- // Render count analysis modal (@buoy-gg/highlight-updates)
35
+ // Benchmark recording modal (@buoy-gg/perf-monitor)
36
36
 
37
37
  /**
38
38
  * Special floating-only tool IDs that only appear in the floating bubble row.
@@ -6,10 +6,10 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.DialDevTools = void 0;
7
7
  var _react = require("react");
8
8
  var _reactNative = require("react-native");
9
+ var _sharedUi = require("@buoy-gg/shared-ui");
9
10
  var _DialIcon = require("./DialIcon.js");
10
11
  var _DialPagination = require("./DialPagination.js");
11
12
  var _dialUsageStore = require("./dialUsageStore.js");
12
- var _sharedUi = require("@buoy-gg/shared-ui");
13
13
  var _DevToolsSettingsModal = require("../DevToolsSettingsModal");
14
14
  var _license = require("@buoy-gg/license");
15
15
  var _AppHost = require("../AppHost.js");
@@ -18,17 +18,10 @@ var _floatingToolsCore = require("@buoy-gg/floating-tools-core");
18
18
  var _jsxRuntime = require("react/jsx-runtime");
19
19
  // Icons are provided by installedApps; no direct icon imports here.
20
20
 
21
- const {
22
- width: SCREEN_WIDTH,
23
- height: SCREEN_HEIGHT
24
- } = _reactNative.Dimensions.get("window");
25
-
26
- // Use shared layout calculation from core
27
- const layout = (0, _floatingToolsCore.getDialLayout)({
28
- screenWidth: SCREEN_WIDTH
29
- });
30
- const CIRCLE_SIZE = layout.circleSize;
31
- const BUTTON_SIZE = layout.buttonSize;
21
+ // The circle size depends on the live window width, so it's computed inside
22
+ // the component via useWindowDimensions — a module-scope Dimensions.get
23
+ // snapshot goes stale when the window resizes after load (web/desktop).
24
+ const BUTTON_SIZE = _floatingToolsCore.DIAL_BUTTON_SIZE;
32
25
  const ONBOARDING_STORAGE_KEY = "@react_buoy_settings_tooltip_shown";
33
26
  /** A non-interactive placeholder used to fill out the last dial page. */
34
27
  const createEmptySlot = slotIndex => ({
@@ -60,6 +53,33 @@ const DialDevTools = ({
60
53
  } = (0, _AppHost.useAppHost)();
61
54
  const isPro = (0, _license.useIsPro)();
62
55
 
56
+ // Live window size — keeps the dial centered and sized correctly when the
57
+ // window resizes (Electron/web) or the device rotates.
58
+ const {
59
+ width: screenWidth,
60
+ height: screenHeight
61
+ } = (0, _reactNative.useWindowDimensions)();
62
+ const circleSize = (0, _floatingToolsCore.getDialLayout)({
63
+ screenWidth
64
+ }).circleSize;
65
+ const sizeStyles = (0, _react.useMemo)(() => ({
66
+ parent: {
67
+ width: circleSize,
68
+ height: circleSize
69
+ },
70
+ circle: {
71
+ width: circleSize,
72
+ height: circleSize,
73
+ borderRadius: circleSize / 2
74
+ },
75
+ rounded: {
76
+ borderRadius: circleSize / 2
77
+ },
78
+ gridLine: {
79
+ width: circleSize
80
+ }
81
+ }), [circleSize]);
82
+
63
83
  // Load persisted settings modal state on mount
64
84
  (0, _react.useEffect)(() => {
65
85
  const loadSettingsModalState = async () => {
@@ -485,13 +505,13 @@ const DialDevTools = ({
485
505
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
486
506
  style: [styles.backdrop, backdropAnimatedStyle],
487
507
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
488
- style: _reactNative.StyleSheet.absoluteFillObject,
508
+ style: _sharedUi.absoluteFill,
489
509
  onPress: handleClose
490
510
  })
491
511
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Animated.View, {
492
- style: [styles.parent, {
512
+ style: [styles.parent, sizeStyles.parent, {
493
513
  position: "absolute",
494
- left: (SCREEN_WIDTH - CIRCLE_SIZE) / 2,
514
+ left: (screenWidth - circleSize) / 2,
495
515
  bottom: 80,
496
516
  transform: [{
497
517
  scale: dialScale
@@ -503,21 +523,21 @@ const DialDevTools = ({
503
523
  }]
504
524
  }],
505
525
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Animated.View, {
506
- style: styles.circle,
526
+ style: [styles.circle, sizeStyles.circle],
507
527
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
508
- style: styles.gradientBackground,
528
+ style: [styles.gradientBackground, sizeStyles.rounded],
509
529
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
510
- style: styles.gradientLayer1
530
+ style: [styles.gradientLayer1, sizeStyles.rounded]
511
531
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
512
- style: styles.gradientLayer2
532
+ style: [styles.gradientLayer2, sizeStyles.rounded]
513
533
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
514
- style: styles.gradientLayer3
534
+ style: [styles.gradientLayer3, sizeStyles.rounded]
515
535
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
516
536
  style: styles.gridPattern,
517
537
  children: Array.from({
518
538
  length: 6
519
539
  }).map((_, i) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
520
- style: [styles.gridLine, {
540
+ style: [styles.gridLine, sizeStyles.gridLine, {
521
541
  transform: [{
522
542
  rotate: `${i * 60}deg`
523
543
  }]
@@ -608,11 +628,11 @@ const DialDevTools = ({
608
628
  onNext: () => handlePageChange(safePage + 1),
609
629
  animatedStyle: {
610
630
  position: "absolute",
611
- left: (SCREEN_WIDTH - CIRCLE_SIZE) / 2,
612
- // Circle's bottom edge sits at bottom: 80 -> SCREEN_HEIGHT - 80
631
+ left: (screenWidth - circleSize) / 2,
632
+ // Circle's bottom edge sits at bottom: 80 -> screenHeight - 80
613
633
  // from the top. Place the pager 16px below that edge.
614
- top: SCREEN_HEIGHT - 80 + 16,
615
- width: CIRCLE_SIZE,
634
+ top: screenHeight - 80 + 16,
635
+ width: circleSize,
616
636
  opacity: dialScale,
617
637
  transform: [{
618
638
  scale: dialScale
@@ -634,23 +654,20 @@ const DialDevTools = ({
634
654
  exports.DialDevTools = DialDevTools;
635
655
  const styles = _reactNative.StyleSheet.create({
636
656
  container: {
637
- ..._reactNative.StyleSheet.absoluteFillObject,
657
+ ..._sharedUi.absoluteFill,
638
658
  zIndex: 9999
639
659
  },
640
660
  backdrop: {
641
- ..._reactNative.StyleSheet.absoluteFillObject,
661
+ ..._sharedUi.absoluteFill,
642
662
  backgroundColor: _floatingToolsCore.dialColors.dialBackdrop
643
663
  },
664
+ // width/height/borderRadius for the circle pieces come from sizeStyles —
665
+ // they track the live window width.
644
666
  parent: {
645
- width: CIRCLE_SIZE,
646
- height: CIRCLE_SIZE,
647
667
  alignItems: "center",
648
668
  justifyContent: "center"
649
669
  },
650
670
  circle: {
651
- width: CIRCLE_SIZE,
652
- height: CIRCLE_SIZE,
653
- borderRadius: CIRCLE_SIZE / 2,
654
671
  position: "absolute",
655
672
  backgroundColor: "transparent",
656
673
  borderWidth: 1,
@@ -667,41 +684,36 @@ const styles = _reactNative.StyleSheet.create({
667
684
  gradientBackground: {
668
685
  width: "100%",
669
686
  height: "100%",
670
- borderRadius: CIRCLE_SIZE / 2,
671
687
  position: "relative",
672
688
  backgroundColor: _floatingToolsCore.dialColors.dialBackground,
673
689
  overflow: "hidden"
674
690
  },
675
691
  gradientLayer1: {
676
- ..._reactNative.StyleSheet.absoluteFillObject,
692
+ ..._sharedUi.absoluteFill,
677
693
  backgroundColor: _floatingToolsCore.dialColors.dialGradient1,
678
- opacity: 0.6,
679
- borderRadius: CIRCLE_SIZE / 2
694
+ opacity: 0.6
680
695
  },
681
696
  gradientLayer2: {
682
- ..._reactNative.StyleSheet.absoluteFillObject,
697
+ ..._sharedUi.absoluteFill,
683
698
  backgroundColor: _floatingToolsCore.dialColors.dialGradient2,
684
699
  opacity: 0.4,
685
700
  top: "30%",
686
- left: "30%",
687
- borderRadius: CIRCLE_SIZE / 2
701
+ left: "30%"
688
702
  },
689
703
  gradientLayer3: {
690
- ..._reactNative.StyleSheet.absoluteFillObject,
704
+ ..._sharedUi.absoluteFill,
691
705
  backgroundColor: _floatingToolsCore.dialColors.dialGradient3,
692
706
  opacity: 0.3,
693
707
  top: "50%",
694
- left: "50%",
695
- borderRadius: CIRCLE_SIZE / 2
708
+ left: "50%"
696
709
  },
697
710
  gridPattern: {
698
- ..._reactNative.StyleSheet.absoluteFillObject,
711
+ ..._sharedUi.absoluteFill,
699
712
  alignItems: "center",
700
713
  justifyContent: "center"
701
714
  },
702
715
  gridLine: {
703
716
  position: "absolute",
704
- width: CIRCLE_SIZE,
705
717
  height: 1,
706
718
  backgroundColor: _floatingToolsCore.dialColors.dialGridLine
707
719
  },
@@ -727,13 +739,13 @@ const styles = _reactNative.StyleSheet.create({
727
739
  overflow: "hidden"
728
740
  },
729
741
  buttonGradientLayer1: {
730
- ..._reactNative.StyleSheet.absoluteFillObject,
742
+ ..._sharedUi.absoluteFill,
731
743
  backgroundColor: _floatingToolsCore.dialColors.dialGradient1,
732
744
  opacity: 0.5,
733
745
  borderRadius: BUTTON_SIZE
734
746
  },
735
747
  buttonGradientLayer2: {
736
- ..._reactNative.StyleSheet.absoluteFillObject,
748
+ ..._sharedUi.absoluteFill,
737
749
  backgroundColor: _floatingToolsCore.dialColors.dialGradient2,
738
750
  opacity: 0.3,
739
751
  top: "20%",
@@ -741,7 +753,7 @@ const styles = _reactNative.StyleSheet.create({
741
753
  borderRadius: BUTTON_SIZE
742
754
  },
743
755
  buttonGradientLayer3: {
744
- ..._reactNative.StyleSheet.absoluteFillObject,
756
+ ..._sharedUi.absoluteFill,
745
757
  backgroundColor: _floatingToolsCore.dialColors.dialGradient3,
746
758
  opacity: 0.2,
747
759
  top: "40%",
@@ -8,16 +8,9 @@ var _react = require("react");
8
8
  var _reactNative = require("react-native");
9
9
  var _floatingToolsCore = require("@buoy-gg/floating-tools-core");
10
10
  var _jsxRuntime = require("react/jsx-runtime");
11
- const {
12
- width: SCREEN_WIDTH
13
- } = _reactNative.Dimensions.get("window");
14
-
15
- // Use shared layout calculation
16
- const layout = (0, _floatingToolsCore.getDialLayout)({
17
- screenWidth: SCREEN_WIDTH
18
- });
19
- const VIEW_SIZE = layout.iconSize;
20
- const CIRCLE_RADIUS = layout.circleRadius;
11
+ // The circle radius depends on the live window width and is computed inside
12
+ // the component (must match DialDevTools' circle, which does the same).
13
+ const VIEW_SIZE = _floatingToolsCore.DIAL_ICON_SIZE;
21
14
  const DialIcon = ({
22
15
  index,
23
16
  icon,
@@ -28,6 +21,12 @@ const DialIcon = ({
28
21
  }) => {
29
22
  // Animation values - using interpolation for better performance
30
23
  const scale = (0, _react.useRef)(new _reactNative.Animated.Value(1)).current;
24
+ const {
25
+ width: screenWidth
26
+ } = (0, _reactNative.useWindowDimensions)();
27
+ const layout = (0, _react.useMemo)(() => (0, _floatingToolsCore.getDialLayout)({
28
+ screenWidth
29
+ }), [screenWidth]);
31
30
 
32
31
  // Hover animation on press in/out - using shared config
33
32
  // Fallback values in case dialAnimationConfig hasn't loaded yet
@@ -123,14 +122,14 @@ const DialIcon = ({
123
122
  itemOpacity,
124
123
  progressScale: staggeredProgress
125
124
  };
126
- }, [index, totalIcons, iconsProgress]);
125
+ }, [index, totalIcons, iconsProgress, layout]);
127
126
 
128
127
  // Main animated style for position and appearance
129
128
  const animatedStyle = {
130
129
  position: "absolute",
131
- left: CIRCLE_RADIUS - VIEW_SIZE / 2,
130
+ left: layout.circleRadius - VIEW_SIZE / 2,
132
131
  // Center position
133
- top: CIRCLE_RADIUS - VIEW_SIZE / 2,
132
+ top: layout.circleRadius - VIEW_SIZE / 2,
134
133
  // Center position
135
134
  opacity: motion.itemOpacity,
136
135
  transform: [{
@@ -133,7 +133,7 @@ const OnboardingTooltip = ({
133
133
  exports.OnboardingTooltip = OnboardingTooltip;
134
134
  const styles = _reactNative.StyleSheet.create({
135
135
  container: {
136
- ..._reactNative.StyleSheet.absoluteFillObject,
136
+ ..._sharedUi.absoluteFill,
137
137
  alignItems: "center",
138
138
  justifyContent: "center",
139
139
  pointerEvents: "box-none"
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  import { useState, useEffect, useCallback, useMemo } from "react";
4
- import { View, Text, StyleSheet, ScrollView, TouchableOpacity, Dimensions } from "react-native";
4
+ import { View, Text, StyleSheet, ScrollView, TouchableOpacity, Dimensions, Platform } from "react-native";
5
5
  import { settingsBus } from "./settingsBus.js";
6
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";
7
7
  import { EnvIcon, StorageIcon, RoutesIcon, NetworkIcon, QueryIcon, HighlighterIcon, ReduxIcon, EventsIcon } from "@buoy-gg/floating-tools-core";
@@ -809,7 +809,7 @@ export const DevToolsSettingsModal = ({
809
809
  children: clearSuccess ? "CLEARED" : isClearing ? "CLEARING..." : "CLEAR ALL SETTINGS"
810
810
  })]
811
811
  })]
812
- }), 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."), 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, {
812
+ }), 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
813
  style: styles.exportConfigCard,
814
814
  children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
815
815
  activeOpacity: 0.85,
@@ -12,6 +12,7 @@ import { MinimizedToolsProvider } from "./MinimizedToolsContext.js";
12
12
  import { validateDialConfig } from "./defaultConfig.js";
13
13
  import { DefaultConfigProvider } from "./DefaultConfigContext.js";
14
14
  import { LicenseManager, useIsPro, useLicense } from "@buoy-gg/license";
15
+ import { AutoExternalSync, isExternalSyncAvailable } from "./autoExternalSync.js";
15
16
 
16
17
  /**
17
18
  * Environment variable configuration
@@ -109,6 +110,7 @@ export const FloatingDevTools = ({
109
110
  licenseKey: licenseKeyProp,
110
111
  zustandStores,
111
112
  environment,
113
+ externalSync = true,
112
114
  ...props
113
115
  }) => {
114
116
  const resolvedEnvironment = environment ?? normalizeEnvironment(process.env.NODE_ENV ?? "dev");
@@ -338,7 +340,10 @@ export const FloatingDevTools = ({
338
340
  environment: resolvedEnvironment
339
341
  }), /*#__PURE__*/_jsx(AppOverlay, {})]
340
342
  })
341
- }), children, DebugBordersOverlay && /*#__PURE__*/_jsx(DebugBordersOverlay, {}), HighlightUpdatesOverlay && /*#__PURE__*/_jsx(HighlightUpdatesOverlay, {}), ImageOverlayOverlay && /*#__PURE__*/_jsx(ImageOverlayOverlay, {}), PerfMonitorOverlay && /*#__PURE__*/_jsx(PerfMonitorOverlay, {}), ImpersonateOverlay && /*#__PURE__*/_jsx(ImpersonateOverlay, {}), RouteTracker && /*#__PURE__*/_jsx(RouteTracker, {})]
343
+ }), children, DebugBordersOverlay && /*#__PURE__*/_jsx(DebugBordersOverlay, {}), HighlightUpdatesOverlay && /*#__PURE__*/_jsx(HighlightUpdatesOverlay, {}), ImageOverlayOverlay && /*#__PURE__*/_jsx(ImageOverlayOverlay, {}), PerfMonitorOverlay && /*#__PURE__*/_jsx(PerfMonitorOverlay, {}), ImpersonateOverlay && /*#__PURE__*/_jsx(ImpersonateOverlay, {}), RouteTracker && /*#__PURE__*/_jsx(RouteTracker, {}), isDevelopment && externalSync !== false && isExternalSyncAvailable() && /*#__PURE__*/_jsx(AutoExternalSync, {
344
+ options: typeof externalSync === "object" ? externalSync : undefined,
345
+ requiredEnvVars: requiredEnvVars
346
+ })]
342
347
  })
343
348
  })
344
349
  })