@buoy-gg/core 1.7.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 (132) hide show
  1. package/README.md +43 -0
  2. package/lib/commonjs/floatingMenu/AppHost.js +410 -0
  3. package/lib/commonjs/floatingMenu/AppHostLogic.js +44 -0
  4. package/lib/commonjs/floatingMenu/DefaultConfigContext.js +45 -0
  5. package/lib/commonjs/floatingMenu/DevToolsSettingsModal.js +2274 -0
  6. package/lib/commonjs/floatingMenu/DevToolsVisibilityContext.js +49 -0
  7. package/lib/commonjs/floatingMenu/DraggableHeader.js +114 -0
  8. package/lib/commonjs/floatingMenu/FloatingDevTools.js +254 -0
  9. package/lib/commonjs/floatingMenu/FloatingMenu.js +364 -0
  10. package/lib/commonjs/floatingMenu/MinimizedToolsContext.js +247 -0
  11. package/lib/commonjs/floatingMenu/MinimizedToolsStack.js +206 -0
  12. package/lib/commonjs/floatingMenu/ToggleStateManager.js +36 -0
  13. package/lib/commonjs/floatingMenu/autoDiscoverPresets.js +241 -0
  14. package/lib/commonjs/floatingMenu/defaultConfig.js +160 -0
  15. package/lib/commonjs/floatingMenu/dial/DialDevTools.js +835 -0
  16. package/lib/commonjs/floatingMenu/dial/DialIcon.js +246 -0
  17. package/lib/commonjs/floatingMenu/dial/OnboardingTooltip.js +249 -0
  18. package/lib/commonjs/floatingMenu/dial/onboardingConstants.js +70 -0
  19. package/lib/commonjs/floatingMenu/floatingTools.js +771 -0
  20. package/lib/commonjs/floatingMenu/settingsBus.js +23 -0
  21. package/lib/commonjs/floatingMenu/types.js +5 -0
  22. package/lib/commonjs/index.js +240 -0
  23. package/lib/commonjs/package.json +1 -0
  24. package/lib/module/floatingMenu/AppHost.js +402 -0
  25. package/lib/module/floatingMenu/AppHostLogic.js +39 -0
  26. package/lib/module/floatingMenu/DefaultConfigContext.js +39 -0
  27. package/lib/module/floatingMenu/DevToolsSettingsModal.js +2273 -0
  28. package/lib/module/floatingMenu/DevToolsVisibilityContext.js +44 -0
  29. package/lib/module/floatingMenu/DraggableHeader.js +110 -0
  30. package/lib/module/floatingMenu/FloatingDevTools.js +249 -0
  31. package/lib/module/floatingMenu/FloatingMenu.js +358 -0
  32. package/lib/module/floatingMenu/MinimizedToolsContext.js +239 -0
  33. package/lib/module/floatingMenu/MinimizedToolsStack.js +202 -0
  34. package/lib/module/floatingMenu/ToggleStateManager.js +32 -0
  35. package/lib/module/floatingMenu/autoDiscoverPresets.js +236 -0
  36. package/lib/module/floatingMenu/defaultConfig.js +151 -0
  37. package/lib/module/floatingMenu/dial/DialDevTools.js +829 -0
  38. package/lib/module/floatingMenu/dial/DialIcon.js +241 -0
  39. package/lib/module/floatingMenu/dial/OnboardingTooltip.js +244 -0
  40. package/lib/module/floatingMenu/dial/onboardingConstants.js +64 -0
  41. package/lib/module/floatingMenu/floatingTools.js +767 -0
  42. package/lib/module/floatingMenu/settingsBus.js +19 -0
  43. package/lib/module/floatingMenu/types.js +3 -0
  44. package/lib/module/index.js +29 -0
  45. package/lib/module/package.json +1 -0
  46. package/lib/typescript/commonjs/floatingMenu/AppHost.d.ts +39 -0
  47. package/lib/typescript/commonjs/floatingMenu/AppHost.d.ts.map +1 -0
  48. package/lib/typescript/commonjs/floatingMenu/AppHostLogic.d.ts +37 -0
  49. package/lib/typescript/commonjs/floatingMenu/AppHostLogic.d.ts.map +1 -0
  50. package/lib/typescript/commonjs/floatingMenu/DefaultConfigContext.d.ts +27 -0
  51. package/lib/typescript/commonjs/floatingMenu/DefaultConfigContext.d.ts.map +1 -0
  52. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts +57 -0
  53. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -0
  54. package/lib/typescript/commonjs/floatingMenu/DevToolsVisibilityContext.d.ts +25 -0
  55. package/lib/typescript/commonjs/floatingMenu/DevToolsVisibilityContext.d.ts.map +1 -0
  56. package/lib/typescript/commonjs/floatingMenu/DraggableHeader.d.ts +30 -0
  57. package/lib/typescript/commonjs/floatingMenu/DraggableHeader.d.ts.map +1 -0
  58. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts +226 -0
  59. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -0
  60. package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts +39 -0
  61. package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts.map +1 -0
  62. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsContext.d.ts +95 -0
  63. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsContext.d.ts.map +1 -0
  64. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.d.ts +10 -0
  65. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.d.ts.map +1 -0
  66. package/lib/typescript/commonjs/floatingMenu/ToggleStateManager.d.ts +21 -0
  67. package/lib/typescript/commonjs/floatingMenu/ToggleStateManager.d.ts.map +1 -0
  68. package/lib/typescript/commonjs/floatingMenu/autoDiscoverPresets.d.ts +75 -0
  69. package/lib/typescript/commonjs/floatingMenu/autoDiscoverPresets.d.ts.map +1 -0
  70. package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts +120 -0
  71. package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts.map +1 -0
  72. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts +35 -0
  73. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -0
  74. package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts +14 -0
  75. package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts.map +1 -0
  76. package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.d.ts +12 -0
  77. package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.d.ts.map +1 -0
  78. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts +30 -0
  79. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts.map +1 -0
  80. package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts +56 -0
  81. package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts.map +1 -0
  82. package/lib/typescript/commonjs/floatingMenu/settingsBus.d.ts +10 -0
  83. package/lib/typescript/commonjs/floatingMenu/settingsBus.d.ts.map +1 -0
  84. package/lib/typescript/commonjs/floatingMenu/types.d.ts +56 -0
  85. package/lib/typescript/commonjs/floatingMenu/types.d.ts.map +1 -0
  86. package/lib/typescript/commonjs/index.d.ts +18 -0
  87. package/lib/typescript/commonjs/index.d.ts.map +1 -0
  88. package/lib/typescript/commonjs/package.json +1 -0
  89. package/lib/typescript/module/floatingMenu/AppHost.d.ts +39 -0
  90. package/lib/typescript/module/floatingMenu/AppHost.d.ts.map +1 -0
  91. package/lib/typescript/module/floatingMenu/AppHostLogic.d.ts +37 -0
  92. package/lib/typescript/module/floatingMenu/AppHostLogic.d.ts.map +1 -0
  93. package/lib/typescript/module/floatingMenu/DefaultConfigContext.d.ts +27 -0
  94. package/lib/typescript/module/floatingMenu/DefaultConfigContext.d.ts.map +1 -0
  95. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts +57 -0
  96. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -0
  97. package/lib/typescript/module/floatingMenu/DevToolsVisibilityContext.d.ts +25 -0
  98. package/lib/typescript/module/floatingMenu/DevToolsVisibilityContext.d.ts.map +1 -0
  99. package/lib/typescript/module/floatingMenu/DraggableHeader.d.ts +30 -0
  100. package/lib/typescript/module/floatingMenu/DraggableHeader.d.ts.map +1 -0
  101. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts +226 -0
  102. package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -0
  103. package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts +39 -0
  104. package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts.map +1 -0
  105. package/lib/typescript/module/floatingMenu/MinimizedToolsContext.d.ts +95 -0
  106. package/lib/typescript/module/floatingMenu/MinimizedToolsContext.d.ts.map +1 -0
  107. package/lib/typescript/module/floatingMenu/MinimizedToolsStack.d.ts +10 -0
  108. package/lib/typescript/module/floatingMenu/MinimizedToolsStack.d.ts.map +1 -0
  109. package/lib/typescript/module/floatingMenu/ToggleStateManager.d.ts +21 -0
  110. package/lib/typescript/module/floatingMenu/ToggleStateManager.d.ts.map +1 -0
  111. package/lib/typescript/module/floatingMenu/autoDiscoverPresets.d.ts +75 -0
  112. package/lib/typescript/module/floatingMenu/autoDiscoverPresets.d.ts.map +1 -0
  113. package/lib/typescript/module/floatingMenu/defaultConfig.d.ts +120 -0
  114. package/lib/typescript/module/floatingMenu/defaultConfig.d.ts.map +1 -0
  115. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts +35 -0
  116. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -0
  117. package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts +14 -0
  118. package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts.map +1 -0
  119. package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.d.ts +12 -0
  120. package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.d.ts.map +1 -0
  121. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts +30 -0
  122. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts.map +1 -0
  123. package/lib/typescript/module/floatingMenu/floatingTools.d.ts +56 -0
  124. package/lib/typescript/module/floatingMenu/floatingTools.d.ts.map +1 -0
  125. package/lib/typescript/module/floatingMenu/settingsBus.d.ts +10 -0
  126. package/lib/typescript/module/floatingMenu/settingsBus.d.ts.map +1 -0
  127. package/lib/typescript/module/floatingMenu/types.d.ts +56 -0
  128. package/lib/typescript/module/floatingMenu/types.d.ts.map +1 -0
  129. package/lib/typescript/module/index.d.ts +18 -0
  130. package/lib/typescript/module/index.d.ts.map +1 -0
  131. package/lib/typescript/module/package.json +1 -0
  132. package/package.json +79 -0
@@ -0,0 +1,364 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.FloatingMenu = void 0;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _floatingTools = require("./floatingTools.js");
10
+ var _DialDevTools = require("./dial/DialDevTools.js");
11
+ var _sharedUi = require("@buoy-gg/shared-ui");
12
+ var _DevToolsSettingsModal = require("./DevToolsSettingsModal.js");
13
+ var _AppHost = require("./AppHost.js");
14
+ var _DevToolsVisibilityContext = require("./DevToolsVisibilityContext.js");
15
+ var _ToggleStateManager = require("./ToggleStateManager.js");
16
+ var _OnboardingTooltip = require("./dial/OnboardingTooltip.js");
17
+ var _jsxRuntime = require("react/jsx-runtime");
18
+ 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); }
19
+ /**
20
+ * Props for the floating developer tools launcher. Controls which apps are shown and
21
+ * how the menu integrates with the current host environment.
22
+ */
23
+
24
+ /**
25
+ * FloatingMenu renders the persistent developer tools entry point. It handles visibility,
26
+ * integrates with the AppHost, and presents available tools as floating shortcuts and a dial.
27
+ */
28
+ const FLOATING_MENU_ONBOARDING_KEY = "@react_buoy_floating_menu_tooltip_shown";
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
+ const FloatingMenu = ({
35
+ apps,
36
+ state,
37
+ actions,
38
+ hidden,
39
+ environment,
40
+ userRole,
41
+ availableEnvironments,
42
+ onEnvironmentSwitch
43
+ }) => {
44
+ const [internalHidden, setInternalHidden] = (0, _react.useState)(false);
45
+ const [showDial, setShowDial] = (0, _react.useState)(false);
46
+ const [dialStateLoaded, setDialStateLoaded] = (0, _react.useState)(false);
47
+ const [onboardingStep, setOnboardingStep] = (0, _react.useState)(null);
48
+ const [, forceUpdate] = (0, _react.useState)(0); // Used to force re-render when toggle states change
49
+ const onboardingDismissedRef = (0, _react.useRef)(false); // Track if onboarding was dismissed
50
+ const hintsDisabled = (0, _sharedUi.useHintsDisabled)();
51
+
52
+ // Load persisted dial state on mount
53
+ (0, _react.useEffect)(() => {
54
+ const loadDialState = async () => {
55
+ try {
56
+ const savedDialOpen = await (0, _sharedUi.safeGetItem)(_sharedUi.devToolsStorageKeys.dial.isOpen());
57
+ if (savedDialOpen === "true") {
58
+ setShowDial(true);
59
+ }
60
+ } catch (error) {
61
+ // Failed to load dial state - use default (closed)
62
+ } finally {
63
+ setDialStateLoaded(true);
64
+ }
65
+ };
66
+ loadDialState();
67
+ }, []);
68
+
69
+ // Persist dial state when it changes
70
+ (0, _react.useEffect)(() => {
71
+ // Only persist after initial state is loaded to avoid overwriting with default
72
+ if (!dialStateLoaded) return;
73
+ (0, _sharedUi.safeSetItem)(_sharedUi.devToolsStorageKeys.dial.isOpen(), showDial ? "true" : "false").catch(error => {
74
+ // Failed to save dial state - continue without persistence
75
+ console.warn("Failed to save dial state:", error);
76
+ });
77
+ }, [showDial, dialStateLoaded]);
78
+
79
+ // Determine if environment selector should be shown
80
+ const showEnvironmentSelector = Boolean(availableEnvironments?.length && onEnvironmentSwitch);
81
+ const {
82
+ isAnyOpen,
83
+ open,
84
+ registerApps
85
+ } = (0, _AppHost.useAppHost)();
86
+ const wasAppOpenRef = (0, _react.useRef)(isAnyOpen);
87
+ const {
88
+ setDialOpen,
89
+ setToolOpen
90
+ } = (0, _DevToolsVisibilityContext.useDevToolsVisibility)();
91
+
92
+ // Check onboarding status on first load
93
+ (0, _react.useEffect)(() => {
94
+ // Skip onboarding if hints are disabled
95
+ if (hintsDisabled) {
96
+ return;
97
+ }
98
+ const checkOnboarding = async () => {
99
+ try {
100
+ const hasSeenOnboarding = await (0, _sharedUi.safeGetItem)(FLOATING_MENU_ONBOARDING_KEY);
101
+ if (!hasSeenOnboarding) {
102
+ // Small delay to let the UI settle before showing tooltip
103
+ setTimeout(() => {
104
+ setOnboardingStep("positioning");
105
+ }, 1000);
106
+ }
107
+ } catch (error) {
108
+ // If there's an error reading storage, don't show onboarding
109
+ }
110
+ };
111
+ checkOnboarding();
112
+ }, [hintsDisabled]);
113
+
114
+ // Subscribe to toggle state changes to update icon colors
115
+ (0, _react.useEffect)(() => {
116
+ const unsubscribe = _ToggleStateManager.toggleStateManager.subscribe(() => {
117
+ forceUpdate(prev => prev + 1);
118
+ });
119
+ return unsubscribe;
120
+ }, []);
121
+
122
+ // Sync dial state with visibility context
123
+ (0, _react.useEffect)(() => {
124
+ setDialOpen(showDial);
125
+ }, [showDial, setDialOpen]);
126
+
127
+ // Sync tool open state with visibility context
128
+ (0, _react.useEffect)(() => {
129
+ setToolOpen(isAnyOpen);
130
+ }, [isAnyOpen, setToolOpen]);
131
+
132
+ // When an app is open or dial is shown, push the menu to the side instead of hiding completely
133
+ const shouldPushToSide = (0, _react.useMemo)(() => Boolean(showDial || isAnyOpen), [showDial, isAnyOpen]);
134
+
135
+ // Use external hidden prop or internal hidden state for complete hiding
136
+ const isCompletelyHidden = (0, _react.useMemo)(() => Boolean(hidden ?? internalHidden), [hidden, internalHidden]);
137
+ const {
138
+ settings: devToolsSettings
139
+ } = (0, _DevToolsSettingsModal.useDevToolsSettings)();
140
+
141
+ // Register apps with AppHost for persistence
142
+ (0, _react.useEffect)(() => {
143
+ if (registerApps) {
144
+ registerApps(apps);
145
+ }
146
+ }, [apps, registerApps]);
147
+ const mergedActions = (0, _react.useMemo)(() => {
148
+ return {
149
+ ...(actions ?? {}),
150
+ closeMenu: () => setShowDial(false),
151
+ hideFloatingRow: () => setInternalHidden(true),
152
+ showFloatingRow: () => setInternalHidden(false),
153
+ notifyToggleChange: () => forceUpdate(prev => prev + 1)
154
+ };
155
+ }, [actions]);
156
+ (0, _react.useEffect)(() => {
157
+ if (wasAppOpenRef.current && !isAnyOpen) {
158
+ setInternalHidden(false);
159
+ setShowDial(false);
160
+ }
161
+ wasAppOpenRef.current = isAnyOpen;
162
+ }, [isAnyOpen]);
163
+
164
+ // Filter function for floating tools based on settings
165
+ const isFloatingEnabled = id => {
166
+ if (!devToolsSettings) return false;
167
+ // Default to disabled for tools without explicit preferences
168
+ return devToolsSettings.floatingTools[id] ?? false;
169
+ };
170
+
171
+ // Dial is the default/only layout
172
+
173
+ const handlePress = app => {
174
+ // Call the app's onPress callback if provided, passing actions for toggle tools
175
+ app?.onPress?.(mergedActions);
176
+
177
+ // Only open modal if not a toggle-only tool
178
+ if (app.launchMode !== "toggle-only") {
179
+ // Resolve the icon for minimize stack display
180
+ // IMPORTANT: Use React.createElement for function components to preserve hooks
181
+ const resolvedIcon = typeof app.icon === "function" ? /*#__PURE__*/_react.default.createElement(app.icon, {
182
+ slot: "dial",
183
+ size: 20
184
+ }) : app.icon;
185
+ open({
186
+ id: app.id,
187
+ title: app.name,
188
+ component: app.component,
189
+ props: app.props,
190
+ launchMode: app.launchMode ?? "self-modal",
191
+ singleton: app.singleton ?? true,
192
+ icon: resolvedIcon,
193
+ color: app.color
194
+ });
195
+ }
196
+ };
197
+ const handleOnboardingDismiss = () => {
198
+ // Mark as dismissed immediately in ref (synchronous, no re-render needed)
199
+ onboardingDismissedRef.current = true;
200
+
201
+ // Update state to hide tooltip
202
+ setOnboardingStep(null);
203
+
204
+ // Save to storage asynchronously in the background
205
+ (0, _sharedUi.safeSetItem)(FLOATING_MENU_ONBOARDING_KEY, "true").catch(error => {
206
+ // Silently fail - user already saw onboarding, just won't persist
207
+ console.warn("Failed to save onboarding state:", error);
208
+ });
209
+ };
210
+ const handleDialOpen = () => {
211
+ // If user opens dial during onboarding, mark onboarding as complete
212
+ if (isOnboarding) {
213
+ handleOnboardingDismiss();
214
+ }
215
+ setShowDial(true);
216
+ };
217
+
218
+ // Determine if we're in onboarding mode (only when explicitly set to positioning AND not dismissed AND hints not disabled)
219
+ const isOnboarding = onboardingStep === "positioning" && !onboardingDismissedRef.current && !hintsDisabled;
220
+
221
+ // During onboarding, disable position persistence and use centered position
222
+ const shouldEnablePositionPersistence = !isOnboarding;
223
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
224
+ children: [isOnboarding && !showDial && !isAnyOpen && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
225
+ style: styles.onboardingContainer,
226
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
227
+ style: styles.onboardingBackdrop
228
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_OnboardingTooltip.OnboardingTooltip, {
229
+ visible: true,
230
+ onDismiss: handleOnboardingDismiss,
231
+ title: "Welcome to Buoy Dev Tools!",
232
+ message: "Grab and position this menu wherever you want, then tap the icon to open your dev tools."
233
+ })]
234
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
235
+ nativeID: "floating-devtools-root",
236
+ pointerEvents: isCompletelyHidden ? "none" : "box-none",
237
+ style: {
238
+ position: "absolute",
239
+ top: 0,
240
+ left: 0,
241
+ right: 0,
242
+ bottom: 0,
243
+ zIndex: isOnboarding ? 10001 : 9999,
244
+ // Higher z-index during onboarding to show above backdrop
245
+ opacity: isCompletelyHidden ? 0 : 1
246
+ },
247
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_floatingTools.FloatingTools, {
248
+ enablePositionPersistence: shouldEnablePositionPersistence,
249
+ pushToSide: shouldPushToSide,
250
+ centerOnboarding: isOnboarding,
251
+ environment: environment,
252
+ availableEnvironments: availableEnvironments,
253
+ onEnvironmentSwitch: onEnvironmentSwitch,
254
+ showEnvironmentSelector: showEnvironmentSelector && devToolsSettings?.floatingTools?.environment,
255
+ children: [devToolsSettings?.floatingTools?.environment && environment && !showEnvironmentSelector ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.EnvironmentIndicator, {
256
+ environment: environment
257
+ }) : null, userRole ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingTools.UserStatus, {
258
+ userRole: userRole,
259
+ onPress: handleDialOpen
260
+ }) :
261
+ /*#__PURE__*/
262
+ // Fallback: small launcher icon to ensure settings are always accessible
263
+ (0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
264
+ accessibilityLabel: "Open Dev Tools Menu",
265
+ onPress: handleDialOpen,
266
+ style: styles.fab,
267
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
268
+ style: styles.menuButton,
269
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(MenuLauncherIcon, {
270
+ size: 14
271
+ })
272
+ })
273
+ }), apps.filter(a => (a.slot ?? "both") !== "dial" && isFloatingEnabled(a.id)).map(app => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
274
+ accessibilityLabel: app.name,
275
+ onPress: () => handlePress(app),
276
+ style: styles.fab,
277
+ children: (() => {
278
+ if (typeof app.icon === "function") {
279
+ const IconComponent = app.icon;
280
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(IconComponent, {
281
+ slot: "row",
282
+ size: 16,
283
+ state: state,
284
+ actions: mergedActions
285
+ });
286
+ }
287
+ return app.icon;
288
+ })()
289
+ }, `row-${app.id}`))]
290
+ })
291
+ }), showDial && /*#__PURE__*/(0, _jsxRuntime.jsx)(_DialDevTools.DialDevTools, {
292
+ apps: apps,
293
+ state: state,
294
+ actions: mergedActions,
295
+ onClose: () => {
296
+ setShowDial(false);
297
+ }
298
+ })]
299
+ });
300
+ };
301
+ exports.FloatingMenu = FloatingMenu;
302
+ const styles = _reactNative.StyleSheet.create({
303
+ fab: {
304
+ paddingHorizontal: 6,
305
+ paddingVertical: 4,
306
+ borderRadius: 6,
307
+ marginRight: 4,
308
+ alignItems: "center",
309
+ justifyContent: "center",
310
+ minWidth: 0,
311
+ minHeight: 0,
312
+ backgroundColor: "transparent"
313
+ },
314
+ menuButton: {
315
+ paddingHorizontal: 4,
316
+ paddingVertical: 2,
317
+ minWidth: 16,
318
+ alignItems: "center",
319
+ justifyContent: "center"
320
+ },
321
+ menuDots: {
322
+ color: "#8CA2C8",
323
+ fontSize: 14,
324
+ fontWeight: "900"
325
+ },
326
+ onboardingContainer: {
327
+ ..._reactNative.StyleSheet.absoluteFillObject,
328
+ zIndex: 10000
329
+ },
330
+ onboardingBackdrop: {
331
+ ..._reactNative.StyleSheet.absoluteFillObject,
332
+ backgroundColor: "rgba(0, 0, 0, 0.85)"
333
+ }
334
+ });
335
+ /** Minimal 3x3 dot icon used when the user status badge is unavailable. */
336
+ const MenuLauncherIcon = ({
337
+ size = 14,
338
+ color = _sharedUi.buoyColors.primary
339
+ }) => {
340
+ const dotSize = Math.max(2, Math.floor(size / 4));
341
+ const gap = Math.max(1, Math.floor(size / 16));
342
+ const items = Array.from({
343
+ length: 9
344
+ });
345
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
346
+ style: {
347
+ width: size,
348
+ height: size,
349
+ flexDirection: "row",
350
+ flexWrap: "wrap",
351
+ alignContent: "center",
352
+ justifyContent: "center"
353
+ },
354
+ children: items.map((_, i) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
355
+ style: {
356
+ width: dotSize,
357
+ height: dotSize,
358
+ margin: gap,
359
+ borderRadius: 2,
360
+ backgroundColor: color
361
+ }
362
+ }, i))
363
+ });
364
+ };
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.MinimizedToolsProvider = MinimizedToolsProvider;
7
+ exports.getIconPosition = getIconPosition;
8
+ exports.getIconSize = getIconSize;
9
+ exports.useMinimizedTools = useMinimizedTools;
10
+ var _react = _interopRequireWildcard(require("react"));
11
+ var _reactNative = require("react-native");
12
+ var _sharedUi = require("@buoy-gg/shared-ui");
13
+ var _jsxRuntime = require("react/jsx-runtime");
14
+ 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); }
15
+ // ============================================================================
16
+ // Types
17
+ // ============================================================================
18
+
19
+ /**
20
+ * Modal state to restore when reopening a minimized tool
21
+ */
22
+
23
+ /**
24
+ * Represents a minimized tool that can be restored
25
+ */
26
+
27
+ /**
28
+ * Position for a minimized tool icon in the stack
29
+ */
30
+
31
+ // ============================================================================
32
+ // Constants
33
+ // ============================================================================
34
+
35
+ const ICON_SIZE = 44;
36
+ const ICON_GAP = 12;
37
+ const BOTTOM_OFFSET = 100; // Distance from safe area bottom
38
+ const RIGHT_OFFSET = 16; // Distance from right edge
39
+
40
+ /**
41
+ * Calculate the position of a minimized tool icon in the stack
42
+ * Icons stack upward from bottom-right corner
43
+ */
44
+ function getIconPosition(index) {
45
+ const {
46
+ width: screenWidth,
47
+ height: screenHeight
48
+ } = _reactNative.Dimensions.get("window");
49
+ const safeArea = (0, _sharedUi.getSafeAreaInsets)();
50
+
51
+ // X position: right edge minus icon size and offset
52
+ const x = screenWidth - ICON_SIZE - RIGHT_OFFSET;
53
+
54
+ // Y position: bottom up, starting at safe area bottom + offset
55
+ const baseY = screenHeight - safeArea.bottom - BOTTOM_OFFSET - ICON_SIZE;
56
+ const y = baseY - index * (ICON_SIZE + ICON_GAP);
57
+ return {
58
+ x,
59
+ y
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Get the icon size constant
65
+ */
66
+ function getIconSize() {
67
+ return ICON_SIZE;
68
+ }
69
+
70
+ /**
71
+ * Context value for minimized tools management
72
+ */
73
+
74
+ // ============================================================================
75
+ // Storage
76
+ // ============================================================================
77
+
78
+ const STORAGE_KEY = "@react_buoy_minimized_tools";
79
+ const PERSISTENCE_DELAY = 500;
80
+
81
+ /**
82
+ * Serializable version of MinimizedTool for storage
83
+ * (icon is not serializable, so we only store the id to reconstruct)
84
+ */
85
+
86
+ // ============================================================================
87
+ // Context
88
+ // ============================================================================
89
+
90
+ const MinimizedToolsContext = /*#__PURE__*/(0, _react.createContext)(null);
91
+
92
+ // ============================================================================
93
+ // Provider
94
+ // ============================================================================
95
+
96
+ /**
97
+ * Provider component for minimized tools management.
98
+ * Handles state, persistence, and provides context to children.
99
+ */
100
+ function MinimizedToolsProvider({
101
+ children,
102
+ getToolIcon
103
+ }) {
104
+ const [minimizedTools, setMinimizedTools] = (0, _react.useState)([]);
105
+ const [isRestored, setIsRestored] = (0, _react.useState)(false);
106
+ const persistenceTimeoutRef = (0, _react.useRef)(null);
107
+ const getToolIconRef = (0, _react.useRef)(getToolIcon);
108
+
109
+ // Keep ref updated
110
+ (0, _react.useEffect)(() => {
111
+ getToolIconRef.current = getToolIcon;
112
+ }, [getToolIcon]);
113
+
114
+ // Restore minimized tools from storage on mount
115
+ (0, _react.useEffect)(() => {
116
+ const restoreMinimizedTools = async () => {
117
+ try {
118
+ const saved = await (0, _sharedUi.safeGetItem)(STORAGE_KEY);
119
+ if (saved) {
120
+ const serialized = JSON.parse(saved);
121
+ // Reconstruct tools with icons
122
+ const restored = serialized.map(tool => ({
123
+ ...tool,
124
+ icon: getToolIconRef.current?.(tool.id) ?? null
125
+ }));
126
+ setMinimizedTools(restored);
127
+ }
128
+ } catch (error) {
129
+ // Failed to restore minimized tools - continue with empty state
130
+ }
131
+ setIsRestored(true);
132
+ };
133
+ restoreMinimizedTools();
134
+ }, []);
135
+
136
+ // Persist minimized tools to storage with debounce
137
+ (0, _react.useEffect)(() => {
138
+ if (!isRestored) return;
139
+ if (persistenceTimeoutRef.current) {
140
+ clearTimeout(persistenceTimeoutRef.current);
141
+ }
142
+ persistenceTimeoutRef.current = setTimeout(() => {
143
+ // Serialize tools (exclude icon which is not serializable)
144
+ const serialized = minimizedTools.map(({
145
+ icon,
146
+ ...rest
147
+ }) => rest);
148
+ (0, _sharedUi.safeSetItem)(STORAGE_KEY, JSON.stringify(serialized));
149
+ }, PERSISTENCE_DELAY);
150
+ return () => {
151
+ if (persistenceTimeoutRef.current) {
152
+ clearTimeout(persistenceTimeoutRef.current);
153
+ }
154
+ };
155
+ }, [minimizedTools, isRestored]);
156
+ const minimize = (0, _react.useCallback)(tool => {
157
+ setMinimizedTools(current => {
158
+ // Check if already minimized (by id for singleton behavior)
159
+ const existing = current.find(t => t.id === tool.id);
160
+ if (existing) {
161
+ // Update existing minimized tool
162
+ return current.map(t => t.id === tool.id ? {
163
+ ...tool,
164
+ minimizedAt: Date.now()
165
+ } : t);
166
+ }
167
+ // Add new minimized tool
168
+ return [...current, {
169
+ ...tool,
170
+ minimizedAt: Date.now()
171
+ }];
172
+ });
173
+ }, []);
174
+ const restore = (0, _react.useCallback)(instanceId => {
175
+ let restoredTool = null;
176
+ setMinimizedTools(current => {
177
+ const index = current.findIndex(t => t.instanceId === instanceId);
178
+ if (index === -1) return current;
179
+ restoredTool = current[index];
180
+ return current.filter(t => t.instanceId !== instanceId);
181
+ });
182
+ return restoredTool;
183
+ }, []);
184
+ const isMinimized = (0, _react.useCallback)(id => {
185
+ return minimizedTools.some(t => t.id === id);
186
+ }, [minimizedTools]);
187
+ const getMinimizedTool = (0, _react.useCallback)(instanceId => {
188
+ return minimizedTools.find(t => t.instanceId === instanceId);
189
+ }, [minimizedTools]);
190
+ const clearAll = (0, _react.useCallback)(() => {
191
+ setMinimizedTools([]);
192
+ }, []);
193
+ const getNextIconPosition = (0, _react.useCallback)(() => {
194
+ // Next icon will be at the current count index
195
+ return getIconPosition(minimizedTools.length);
196
+ }, [minimizedTools.length]);
197
+ const getToolIconPosition = (0, _react.useCallback)(instanceId => {
198
+ const index = minimizedTools.findIndex(t => t.instanceId === instanceId);
199
+ if (index === -1) return null;
200
+ return getIconPosition(index);
201
+ }, [minimizedTools]);
202
+ const value = (0, _react.useMemo)(() => ({
203
+ minimizedTools,
204
+ minimize,
205
+ restore,
206
+ isMinimized,
207
+ getMinimizedTool,
208
+ clearAll,
209
+ count: minimizedTools.length,
210
+ getNextIconPosition,
211
+ getToolIconPosition
212
+ }), [minimizedTools, minimize, restore, isMinimized, getMinimizedTool, clearAll, getNextIconPosition, getToolIconPosition]);
213
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(MinimizedToolsContext.Provider, {
214
+ value: value,
215
+ children: children
216
+ });
217
+ }
218
+
219
+ // ============================================================================
220
+ // Hook
221
+ // ============================================================================
222
+
223
+ /**
224
+ * Hook to access minimized tools context.
225
+ * Must be used within a MinimizedToolsProvider.
226
+ */
227
+ function useMinimizedTools() {
228
+ const context = (0, _react.useContext)(MinimizedToolsContext);
229
+ if (!context) {
230
+ // Return a no-op implementation when outside provider
231
+ return {
232
+ minimizedTools: [],
233
+ minimize: () => {},
234
+ restore: () => null,
235
+ isMinimized: () => false,
236
+ getMinimizedTool: () => undefined,
237
+ clearAll: () => {},
238
+ count: 0,
239
+ getNextIconPosition: () => ({
240
+ x: 0,
241
+ y: 0
242
+ }),
243
+ getToolIconPosition: () => null
244
+ };
245
+ }
246
+ return context;
247
+ }