@buoy-gg/core 3.0.1 → 4.0.1

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 (51) hide show
  1. package/lib/commonjs/floatingMenu/DevToolsSettingsModal.js +1 -1
  2. package/lib/commonjs/floatingMenu/FloatingDevTools.js +25 -5
  3. package/lib/commonjs/floatingMenu/FloatingMenu.js +3 -7
  4. package/lib/commonjs/floatingMenu/allToolsRegistry.js +193 -0
  5. package/lib/commonjs/floatingMenu/autoExternalSync.js +254 -0
  6. package/lib/commonjs/floatingMenu/defaultConfig.js +1 -1
  7. package/lib/commonjs/floatingMenu/dial/DialDevTools.js +115 -53
  8. package/lib/commonjs/floatingMenu/dial/DialIcon.js +53 -13
  9. package/lib/commonjs/floatingMenu/dial/OnboardingTooltip.js +1 -1
  10. package/lib/module/floatingMenu/DevToolsSettingsModal.js +2 -2
  11. package/lib/module/floatingMenu/FloatingDevTools.js +26 -6
  12. package/lib/module/floatingMenu/FloatingMenu.js +4 -7
  13. package/lib/module/floatingMenu/allToolsRegistry.js +190 -0
  14. package/lib/module/floatingMenu/autoExternalSync.js +248 -0
  15. package/lib/module/floatingMenu/defaultConfig.js +1 -1
  16. package/lib/module/floatingMenu/dial/DialDevTools.js +119 -55
  17. package/lib/module/floatingMenu/dial/DialIcon.js +56 -15
  18. package/lib/module/floatingMenu/dial/OnboardingTooltip.js +2 -1
  19. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
  20. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts +19 -1
  21. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  22. package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts.map +1 -1
  23. package/lib/typescript/commonjs/floatingMenu/allToolsRegistry.d.ts +42 -0
  24. package/lib/typescript/commonjs/floatingMenu/allToolsRegistry.d.ts.map +1 -0
  25. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts +25 -0
  26. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts.map +1 -0
  27. package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts +1 -1
  28. package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts.map +1 -1
  29. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts +7 -0
  30. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  31. package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts.map +1 -1
  32. package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.d.ts.map +1 -1
  33. package/lib/typescript/commonjs/index.d.ts +1 -0
  34. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  35. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
  36. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts +19 -1
  37. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -1
  38. package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts.map +1 -1
  39. package/lib/typescript/module/floatingMenu/allToolsRegistry.d.ts +42 -0
  40. package/lib/typescript/module/floatingMenu/allToolsRegistry.d.ts.map +1 -0
  41. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts +25 -0
  42. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts.map +1 -0
  43. package/lib/typescript/module/floatingMenu/defaultConfig.d.ts +1 -1
  44. package/lib/typescript/module/floatingMenu/defaultConfig.d.ts.map +1 -1
  45. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts +7 -0
  46. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  47. package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts.map +1 -1
  48. package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.d.ts.map +1 -1
  49. package/lib/typescript/module/index.d.ts +1 -0
  50. package/lib/typescript/module/index.d.ts.map +1 -1
  51. package/package.json +5 -5
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Canonical registry of ALL dial-eligible tools React Buoy offers on mobile.
5
+ *
6
+ * This is the single source of truth used to compute which tools are
7
+ * "unavailable" (offered but not installed in the current app). It lives
8
+ * inside the floating-menu package so nothing outside needs to be imported.
9
+ *
10
+ * Rules for this file:
11
+ * - Only import from REQUIRED dependencies (@buoy-gg/floating-tools-core,
12
+ * @buoy-gg/shared-ui, react-native) so the icons are always resolvable,
13
+ * even when the optional tool packages are not installed.
14
+ * - Only include tools whose preset slot is NOT "row" (row-only tools never
15
+ * appear in the dial and have no "unavailable" state to show).
16
+ * - Keep the order intentional: this order is the fallback when no usage
17
+ * data exists for new/unavailable tools.
18
+ */
19
+
20
+ import React from "react";
21
+ import { View, Text } from "react-native";
22
+ import { EnvIcon, ENV_ICON_COLOR, NetworkIcon, NETWORK_ICON_COLOR, StorageIcon, STORAGE_ICON_COLOR, QueryIcon, QUERY_ICON_COLOR, RoutesIcon, ROUTES_ICON_COLOR, HighlighterIcon, HIGHLIGHTER_ICON_COLOR, ReduxIcon, REDUX_ICON_COLOR, EventsIcon, EVENTS_ICON_COLOR, BenchmarkIcon, BENCHMARK_ICON_COLOR } from "@buoy-gg/floating-tools-core";
23
+ import { ImageOverlayIcon } from "@buoy-gg/shared-ui";
24
+
25
+ // ─── Color constants for tools whose icons live in optional packages ─────────
26
+ // Copied verbatim from the source icon components so the vendored copies below
27
+ // render identically to the real tool icons.
28
+
29
+ /** Zustand brand color — copied from @buoy-gg/zustand ZustandIcon.tsx */
30
+ import { jsx as _jsx } from "react/jsx-runtime";
31
+ const ZUSTAND_ICON_COLOR = "#463B3F";
32
+ /** Jotai brand color — copied from @buoy-gg/jotai JotaiIcon.tsx */
33
+ const JOTAI_ICON_COLOR = "#6C47FF";
34
+ /** Image overlay purple — matches the preset */
35
+ const IMAGE_OVERLAY_ICON_COLOR = "#A855F7";
36
+
37
+ // ─── Vendored copies of optional-package icons ───────────────────────────────
38
+ // Zustand and Jotai ship their real icons inside their own packages (which may
39
+ // not be installed), so we cannot import them from a required dependency.
40
+ //
41
+ // The REAL icons in @buoy-gg/zustand and @buoy-gg/jotai are themselves a single
42
+ // centered letter glyph (verified — there are no SVG logos in those packages).
43
+ // These are faithful copies of those source components, so unavailable items
44
+ // render the ACTUAL tool icon (just dimmed by DialIcon), not a placeholder.
45
+
46
+ /** Faithful copy of @buoy-gg/zustand `ZustandIcon` */
47
+ const ZustandIcon = ({
48
+ size,
49
+ color
50
+ }) => /*#__PURE__*/_jsx(View, {
51
+ style: {
52
+ width: size,
53
+ height: size,
54
+ justifyContent: "center",
55
+ alignItems: "center"
56
+ },
57
+ children: /*#__PURE__*/_jsx(Text, {
58
+ style: {
59
+ fontSize: size * 0.75,
60
+ color: color || ZUSTAND_ICON_COLOR,
61
+ fontWeight: "700"
62
+ },
63
+ children: "Z"
64
+ })
65
+ });
66
+
67
+ /** Faithful copy of @buoy-gg/jotai `JotaiIcon` */
68
+ const JotaiIcon = ({
69
+ size,
70
+ color
71
+ }) => /*#__PURE__*/_jsx(View, {
72
+ style: {
73
+ width: size,
74
+ height: size,
75
+ justifyContent: "center",
76
+ alignItems: "center"
77
+ },
78
+ children: /*#__PURE__*/_jsx(Text, {
79
+ style: {
80
+ fontSize: size * 0.75,
81
+ color: color || JOTAI_ICON_COLOR,
82
+ fontWeight: "700"
83
+ },
84
+ children: "J"
85
+ })
86
+ });
87
+
88
+ // ─── Registry entry type ──────────────────────────────────────────────────────
89
+
90
+ // ─── Canonical catalog ────────────────────────────────────────────────────────
91
+
92
+ /**
93
+ * Full list of every dial-eligible tool React Buoy offers on mobile.
94
+ *
95
+ * Available (installed) tools are shown normally in the dial.
96
+ * Unavailable (not installed) tools are appended at the end of the dial and look
97
+ * like a normal item (real icon + real name) with a small muted "Unavailable"
98
+ * caption added; they are non-interactive.
99
+ *
100
+ * To add a new tool to Buoy:
101
+ * 1. Add its preset to autoDiscoverPresets.ts
102
+ * 2. Add an entry here so it appears in the "unavailable" section for apps
103
+ * that haven't installed it yet.
104
+ */
105
+ export const ALL_TOOLS_REGISTRY = [{
106
+ id: "env",
107
+ name: "ENV",
108
+ color: ENV_ICON_COLOR,
109
+ renderIcon: size => /*#__PURE__*/_jsx(EnvIcon, {
110
+ size: size
111
+ })
112
+ }, {
113
+ id: "network",
114
+ name: "NETWORK",
115
+ color: NETWORK_ICON_COLOR,
116
+ renderIcon: size => /*#__PURE__*/_jsx(NetworkIcon, {
117
+ size: size
118
+ })
119
+ }, {
120
+ id: "storage",
121
+ name: "STORAGE",
122
+ color: STORAGE_ICON_COLOR,
123
+ renderIcon: size => /*#__PURE__*/_jsx(StorageIcon, {
124
+ size: size
125
+ })
126
+ }, {
127
+ id: "query",
128
+ name: "QUERY",
129
+ color: QUERY_ICON_COLOR,
130
+ renderIcon: size => /*#__PURE__*/_jsx(QueryIcon, {
131
+ size: size
132
+ })
133
+ }, {
134
+ id: "route-events",
135
+ name: "ROUTES",
136
+ color: ROUTES_ICON_COLOR,
137
+ renderIcon: size => /*#__PURE__*/_jsx(RoutesIcon, {
138
+ size: size
139
+ })
140
+ }, {
141
+ id: "highlight-updates-modal",
142
+ name: "HIGHLIGHT",
143
+ color: HIGHLIGHTER_ICON_COLOR,
144
+ renderIcon: size => /*#__PURE__*/_jsx(HighlighterIcon, {
145
+ size: size
146
+ })
147
+ }, {
148
+ id: "redux",
149
+ name: "REDUX",
150
+ color: REDUX_ICON_COLOR,
151
+ renderIcon: size => /*#__PURE__*/_jsx(ReduxIcon, {
152
+ size: size
153
+ })
154
+ }, {
155
+ id: "zustand",
156
+ name: "ZUSTAND",
157
+ color: ZUSTAND_ICON_COLOR,
158
+ renderIcon: size => /*#__PURE__*/_jsx(ZustandIcon, {
159
+ size: size
160
+ })
161
+ }, {
162
+ id: "events",
163
+ name: "EVENTS",
164
+ color: EVENTS_ICON_COLOR,
165
+ renderIcon: size => /*#__PURE__*/_jsx(EventsIcon, {
166
+ size: size
167
+ })
168
+ }, {
169
+ id: "jotai",
170
+ name: "JOTAI",
171
+ color: JOTAI_ICON_COLOR,
172
+ renderIcon: size => /*#__PURE__*/_jsx(JotaiIcon, {
173
+ size: size
174
+ })
175
+ }, {
176
+ id: "image-overlay",
177
+ name: "IMG",
178
+ color: IMAGE_OVERLAY_ICON_COLOR,
179
+ renderIcon: size => /*#__PURE__*/_jsx(ImageOverlayIcon, {
180
+ size: size,
181
+ color: IMAGE_OVERLAY_ICON_COLOR
182
+ })
183
+ }, {
184
+ id: "perf-monitor-modal",
185
+ name: "BENCH",
186
+ color: BENCHMARK_ICON_COLOR,
187
+ renderIcon: size => /*#__PURE__*/_jsx(BenchmarkIcon, {
188
+ size: size
189
+ })
190
+ }];
@@ -0,0 +1,248 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Zero-config device→desktop sync for FloatingDevTools.
5
+ *
6
+ * When @buoy-gg/external-sync is installed, FloatingDevTools renders
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
9
+ * registers a sync adapter for every installed tool package, discovered with
10
+ * the same guarded-require pattern as autoDiscoverPresets. No <DevToolsSync>
11
+ * wiring needed in the app.
12
+ *
13
+ * Identity defaults come from expo-constants when available (app name →
14
+ * "MyApp (ios)" / "myapp-ios"), overridable via the `externalSync` prop.
15
+ */
16
+ import { useEffect, useMemo, useRef } from "react";
17
+ import { Platform } from "react-native";
18
+ /* eslint-disable @typescript-eslint/no-var-requires */
19
+
20
+ // Module-level guarded requires: presence is constant for the app lifetime,
21
+ // so hooks pulled from these modules keep a stable call order.
22
+ //
23
+ // IMPORTANT: each require() must sit LEXICALLY inside its own try block
24
+ // (same pattern as autoDiscoverPresets) — Metro only marks a dependency as
25
+ // optional when the require is directly inside a try statement. Wrapping it
26
+ // in a helper/arrow makes Metro hard-fail the bundle for apps that don't
27
+ // install that package.
28
+ let externalSyncModule = null;
29
+ let storageModule = null;
30
+ let networkModule = null;
31
+ let reactQueryModule = null;
32
+ let routeEventsModule = null;
33
+ let reduxModule = null;
34
+ let zustandModule = null;
35
+ let jotaiModule = null;
36
+ let eventsModule = null;
37
+ let consoleModule = null;
38
+ let envModule = null;
39
+ let highlightUpdatesModule = null;
40
+ let debugBordersModule = null;
41
+ let impersonateModule = null;
42
+ let perfMonitorModule = null;
43
+ let expoConstantsModule = null;
44
+ try {
45
+ externalSyncModule = require("@buoy-gg/external-sync");
46
+ } catch {
47
+ // not installed
48
+ }
49
+ try {
50
+ storageModule = require("@buoy-gg/storage");
51
+ } catch {
52
+ // not installed
53
+ }
54
+ try {
55
+ networkModule = require("@buoy-gg/network");
56
+ } catch {
57
+ // not installed
58
+ }
59
+ try {
60
+ reactQueryModule = require("@buoy-gg/react-query");
61
+ } catch {
62
+ // not installed
63
+ }
64
+ try {
65
+ routeEventsModule = require("@buoy-gg/route-events");
66
+ } catch {
67
+ // not installed
68
+ }
69
+ try {
70
+ reduxModule = require("@buoy-gg/redux");
71
+ } catch {
72
+ // not installed
73
+ }
74
+ try {
75
+ zustandModule = require("@buoy-gg/zustand");
76
+ } catch {
77
+ // not installed
78
+ }
79
+ try {
80
+ jotaiModule = require("@buoy-gg/jotai");
81
+ } catch {
82
+ // not installed
83
+ }
84
+ try {
85
+ eventsModule = require("@buoy-gg/events");
86
+ } catch {
87
+ // not installed
88
+ }
89
+ try {
90
+ consoleModule = require("@buoy-gg/console");
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
+ export 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"} (${Platform.OS})`,
138
+ deviceId: `${slug || "buoy"}-${Platform.OS}`
139
+ };
140
+ }
141
+ export function AutoExternalSync({
142
+ options,
143
+ requiredEnvVars,
144
+ isPro,
145
+ licenseKey
146
+ }) {
147
+ const {
148
+ useExternalSyncSocket,
149
+ useExternalSync
150
+ } = externalSyncModule;
151
+
152
+ // Context-based query adapter; null when there is no QueryClientProvider
153
+ // above FloatingDevTools (or the installed @buoy-gg/react-query predates
154
+ // the hook).
155
+ const queryAdapter = reactQueryModule?.useReactQuerySyncAdapter ? reactQueryModule.useReactQuerySyncAdapter() : null;
156
+ const identity = useMemo(defaultIdentity, []);
157
+ const deviceId = options?.deviceId ?? identity.deviceId;
158
+ const {
159
+ socket
160
+ } = useExternalSyncSocket({
161
+ deviceName: options?.deviceName ?? identity.deviceName,
162
+ socketURL: options?.socketURL ?? "http://localhost:42831",
163
+ persistentDeviceId: deviceId,
164
+ platform: Platform.OS,
165
+ enableLogs: options?.enableLogs
166
+ });
167
+
168
+ // The license validates asynchronously (Keygen call on mount), usually AFTER
169
+ // the socket handshake is already sent — so the one-shot handshake query
170
+ // can't carry Pro status reliably. Emit it as an event whenever it resolves
171
+ // or changes, and on every (re)connect. The broker stores it on the device
172
+ // record and rebroadcasts, so the desktop dashboard can auto-adopt the key.
173
+ const licenseRef = useRef({
174
+ isPro,
175
+ licenseKey
176
+ });
177
+ licenseRef.current = {
178
+ isPro,
179
+ licenseKey
180
+ };
181
+ useEffect(() => {
182
+ if (!socket) return;
183
+ const emitLicense = () => {
184
+ socket.emit("device-license", {
185
+ isPro: licenseRef.current.isPro ? "1" : "0",
186
+ licenseKey: licenseRef.current.licenseKey ?? ""
187
+ });
188
+ };
189
+ if (socket.connected) emitLicense();
190
+ socket.on("connect", emitLicense);
191
+ return () => {
192
+ socket.off("connect", emitLicense);
193
+ };
194
+ }, [socket, isPro, licenseKey]);
195
+ const tools = useMemo(() => {
196
+ const map = {};
197
+ if (storageModule?.storageSyncAdapter) {
198
+ map.storage = storageModule.storageSyncAdapter;
199
+ }
200
+ if (networkModule?.networkSyncAdapter) {
201
+ map.network = networkModule.networkSyncAdapter;
202
+ }
203
+ if (queryAdapter) {
204
+ map.query = queryAdapter;
205
+ }
206
+ if (routeEventsModule?.routeEventsSyncAdapter) {
207
+ map["route-events"] = routeEventsModule.routeEventsSyncAdapter;
208
+ }
209
+ if (reduxModule?.reduxSyncAdapter) {
210
+ map.redux = reduxModule.reduxSyncAdapter;
211
+ }
212
+ if (zustandModule?.zustandSyncAdapter) {
213
+ map.zustand = zustandModule.zustandSyncAdapter;
214
+ }
215
+ if (jotaiModule?.jotaiSyncAdapter) {
216
+ map.jotai = jotaiModule.jotaiSyncAdapter;
217
+ }
218
+ if (eventsModule?.eventsSyncAdapter) {
219
+ map.events = eventsModule.eventsSyncAdapter;
220
+ }
221
+ if (consoleModule?.consoleSyncAdapter) {
222
+ map.console = consoleModule.consoleSyncAdapter;
223
+ }
224
+ if (envModule?.createEnvSyncAdapter) {
225
+ map.env = envModule.createEnvSyncAdapter(requiredEnvVars ?? []);
226
+ }
227
+ if (highlightUpdatesModule?.highlightUpdatesSyncAdapter) {
228
+ map["highlight-updates"] = highlightUpdatesModule.highlightUpdatesSyncAdapter;
229
+ }
230
+ if (debugBordersModule?.debugBordersSyncAdapter) {
231
+ map["debug-borders"] = debugBordersModule.debugBordersSyncAdapter;
232
+ }
233
+ if (impersonateModule?.impersonateSyncAdapter) {
234
+ map.impersonate = impersonateModule.impersonateSyncAdapter;
235
+ }
236
+ if (perfMonitorModule?.perfMonitorSyncAdapter) {
237
+ map["perf-monitor"] = perfMonitorModule.perfMonitorSyncAdapter;
238
+ }
239
+ return map;
240
+ }, [queryAdapter, requiredEnvVars]);
241
+ useExternalSync({
242
+ tools,
243
+ socket,
244
+ deviceId,
245
+ enableLogs: options?.enableLogs
246
+ });
247
+ return null;
248
+ }
@@ -23,7 +23,7 @@
23
23
  * This is a union type of all auto-discovered tool IDs.
24
24
  */
25
25
 
26
- // Render count analysis modal (@buoy-gg/highlight-updates)
26
+ // Benchmark recording modal (@buoy-gg/perf-monitor)
27
27
 
28
28
  /**
29
29
  * Special floating-only tool IDs that only appear in the floating bubble row.