@buoy-gg/core 7.0.15 → 7.0.16

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 (99) hide show
  1. package/lib/commonjs/floatingMenu/AppHost.web.js +359 -0
  2. package/lib/commonjs/floatingMenu/AppHostLogic.js +6 -0
  3. package/lib/commonjs/floatingMenu/DevToolsSettingsModal.web.js +787 -418
  4. package/lib/commonjs/floatingMenu/FloatingDevTools.js +1 -1
  5. package/lib/commonjs/floatingMenu/FloatingDevTools.web.js +244 -36
  6. package/lib/commonjs/floatingMenu/FloatingMenu.js +2 -2
  7. package/lib/commonjs/floatingMenu/MinimizedToolsContext.js +2 -3
  8. package/lib/commonjs/floatingMenu/MinimizedToolsStack.web.js +287 -0
  9. package/lib/commonjs/floatingMenu/autoDiscoverPresets.js +16 -0
  10. package/lib/commonjs/floatingMenu/autoExternalSync.js +9 -0
  11. package/lib/commonjs/floatingMenu/dial/DialDevTools.js +7 -4
  12. package/lib/commonjs/floatingMenu/dial/DialDevTools.web.js +373 -231
  13. package/lib/commonjs/floatingMenu/dial/DialIcon.js +25 -3
  14. package/lib/commonjs/floatingMenu/dial/OnboardingTooltip.js +1 -1
  15. package/lib/commonjs/floatingMenu/dial/OnboardingTooltip.web.js +154 -0
  16. package/lib/commonjs/floatingMenu/dial/onboardingConstants.web.js +34 -0
  17. package/lib/commonjs/floatingMenu/floatingTools.js +3 -3
  18. package/lib/commonjs/floatingMenu/floatingTools.web.js +191 -28
  19. package/lib/commonjs/index.js +2 -2
  20. package/lib/commonjs/index.web.js +19 -0
  21. package/lib/commonjs/utils/autoDiscoverPresets.web.js +50 -5
  22. package/lib/module/floatingMenu/AppHost.web.js +351 -0
  23. package/lib/module/floatingMenu/AppHostLogic.js +6 -0
  24. package/lib/module/floatingMenu/DevToolsSettingsModal.web.js +792 -422
  25. package/lib/module/floatingMenu/FloatingDevTools.js +2 -2
  26. package/lib/module/floatingMenu/FloatingDevTools.web.js +247 -38
  27. package/lib/module/floatingMenu/FloatingMenu.js +2 -2
  28. package/lib/module/floatingMenu/MinimizedToolsContext.js +2 -3
  29. package/lib/module/floatingMenu/MinimizedToolsStack.web.js +284 -0
  30. package/lib/module/floatingMenu/autoDiscoverPresets.js +16 -0
  31. package/lib/module/floatingMenu/autoExternalSync.js +9 -0
  32. package/lib/module/floatingMenu/dial/DialDevTools.js +8 -5
  33. package/lib/module/floatingMenu/dial/DialDevTools.web.js +375 -233
  34. package/lib/module/floatingMenu/dial/DialIcon.js +26 -4
  35. package/lib/module/floatingMenu/dial/OnboardingTooltip.js +1 -1
  36. package/lib/module/floatingMenu/dial/OnboardingTooltip.web.js +149 -0
  37. package/lib/module/floatingMenu/dial/onboardingConstants.web.js +27 -0
  38. package/lib/module/floatingMenu/floatingTools.js +3 -3
  39. package/lib/module/floatingMenu/floatingTools.web.js +190 -28
  40. package/lib/module/index.js +2 -2
  41. package/lib/module/index.web.js +5 -0
  42. package/lib/module/utils/autoDiscoverPresets.web.js +53 -7
  43. package/lib/typescript/commonjs/floatingMenu/AppHost.web.d.ts +38 -0
  44. package/lib/typescript/commonjs/floatingMenu/AppHost.web.d.ts.map +1 -0
  45. package/lib/typescript/commonjs/floatingMenu/AppHostLogic.d.ts +21 -1
  46. package/lib/typescript/commonjs/floatingMenu/AppHostLogic.d.ts.map +1 -1
  47. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.web.d.ts +5 -2
  48. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.web.d.ts.map +1 -1
  49. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts +2 -14
  50. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
  51. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsContext.d.ts +2 -20
  52. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsContext.d.ts.map +1 -1
  53. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.web.d.ts +21 -0
  54. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.web.d.ts.map +1 -0
  55. package/lib/typescript/commonjs/floatingMenu/autoDiscoverPresets.d.ts.map +1 -1
  56. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  58. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.web.d.ts +16 -2
  59. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.web.d.ts.map +1 -1
  60. package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts.map +1 -1
  61. package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.web.d.ts +9 -0
  62. package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.web.d.ts.map +1 -0
  63. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.web.d.ts +13 -0
  64. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.web.d.ts.map +1 -0
  65. package/lib/typescript/commonjs/floatingMenu/floatingTools.web.d.ts +29 -2
  66. package/lib/typescript/commonjs/floatingMenu/floatingTools.web.d.ts.map +1 -1
  67. package/lib/typescript/commonjs/index.web.d.ts +1 -0
  68. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  69. package/lib/typescript/commonjs/utils/autoDiscoverPresets.web.d.ts +2 -0
  70. package/lib/typescript/commonjs/utils/autoDiscoverPresets.web.d.ts.map +1 -1
  71. package/lib/typescript/module/floatingMenu/AppHost.web.d.ts +38 -0
  72. package/lib/typescript/module/floatingMenu/AppHost.web.d.ts.map +1 -0
  73. package/lib/typescript/module/floatingMenu/AppHostLogic.d.ts +21 -1
  74. package/lib/typescript/module/floatingMenu/AppHostLogic.d.ts.map +1 -1
  75. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.web.d.ts +5 -2
  76. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.web.d.ts.map +1 -1
  77. package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts +2 -14
  78. package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
  79. package/lib/typescript/module/floatingMenu/MinimizedToolsContext.d.ts +2 -20
  80. package/lib/typescript/module/floatingMenu/MinimizedToolsContext.d.ts.map +1 -1
  81. package/lib/typescript/module/floatingMenu/MinimizedToolsStack.web.d.ts +21 -0
  82. package/lib/typescript/module/floatingMenu/MinimizedToolsStack.web.d.ts.map +1 -0
  83. package/lib/typescript/module/floatingMenu/autoDiscoverPresets.d.ts.map +1 -1
  84. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts.map +1 -1
  85. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  86. package/lib/typescript/module/floatingMenu/dial/DialDevTools.web.d.ts +16 -2
  87. package/lib/typescript/module/floatingMenu/dial/DialDevTools.web.d.ts.map +1 -1
  88. package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts.map +1 -1
  89. package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.web.d.ts +9 -0
  90. package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.web.d.ts.map +1 -0
  91. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.web.d.ts +13 -0
  92. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.web.d.ts.map +1 -0
  93. package/lib/typescript/module/floatingMenu/floatingTools.web.d.ts +29 -2
  94. package/lib/typescript/module/floatingMenu/floatingTools.web.d.ts.map +1 -1
  95. package/lib/typescript/module/index.web.d.ts +1 -0
  96. package/lib/typescript/module/index.web.d.ts.map +1 -1
  97. package/lib/typescript/module/utils/autoDiscoverPresets.web.d.ts +2 -0
  98. package/lib/typescript/module/utils/autoDiscoverPresets.web.d.ts.map +1 -1
  99. package/package.json +5 -5
@@ -2,9 +2,9 @@
2
2
 
3
3
  import React, { useMemo, useCallback, useEffect, useState } from "react";
4
4
  import { View, StyleSheet } from "react-native";
5
- import { AppHostProvider } from "./AppHost.js";
5
+ import { AppHostProvider } from "./AppHost";
6
6
  import { FloatingMenu } from "./FloatingMenu.js";
7
- import { AppOverlay } from "./AppHost.js";
7
+ import { AppOverlay } from "./AppHost";
8
8
  import { autoDiscoverPresets, autoDiscoverPresetsWithCustom } from "./autoDiscoverPresets.js";
9
9
  import { DevToolsVisibilityProvider } from "./DevToolsVisibilityContext.js";
10
10
  import { reportOptionalRequireError } from "./optionalRequire.js";
@@ -19,50 +19,107 @@
19
19
  * @platform React DOM Web (Vite, CRA, Next.js, Electron)
20
20
  */
21
21
 
22
- import { useState, useMemo, useCallback } from 'react';
22
+ import { useState, useMemo, useCallback, useEffect, useRef } from 'react';
23
23
  import { useSettings, getToolLabel, getToolDescription, getToolColor } from '@buoy-gg/floating-tools-react';
24
+ import { persistentStorage, devToolsStorageKeys, EnvironmentIndicator, EnvironmentSelectorInline } from '@buoy-gg/shared-ui';
24
25
  import { autoDiscoverWithCustom, getToolsBySlot } from "../utils/autoDiscoverPresets.web.js";
25
26
  import { FloatingTools, UserStatus } from "./floatingTools.web.js";
26
27
  import { DialMenu } from "./dial/DialDevTools.web.js";
27
28
  import { getRankedToolIds, recordToolUsage } from "./dial/dialUsageStore.js";
29
+ import { OnboardingTooltip } from "./dial/OnboardingTooltip.web.js";
28
30
  import { SettingsModal } from "./DevToolsSettingsModal.web.js";
31
+ import { AppHostProvider, AppOverlay, useAppHost } from "./AppHost.web.js";
32
+ import { MinimizedToolsStack } from "./MinimizedToolsStack.web.js";
33
+
34
+ // native FloatingMenu.tsx TOOLTIP_SHOWN_KEY
35
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
36
+ const MENU_TOOLTIP_KEY = '@react_buoy_floating_menu_tooltip_shown';
37
+
38
+ // Same localStorage adapter the settings modal uses — without it, useSettings
39
+ // never loads persisted settings and this instance sits on defaults across
40
+ // launches (live changes still arrive via the settings event bus).
41
+ const webSettingsStorage = {
42
+ getItem: key => localStorage.getItem(key),
43
+ setItem: (key, value) => localStorage.setItem(key, value)
44
+ };
29
45
 
30
46
  // =============================
31
47
  // Types
32
48
  // =============================
33
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
49
+
50
+ // =============================
51
+ // MenuLauncherIcon — native FloatingMenu.tsx:446-478: 3×3 dot grid rendered
52
+ // when no userRole is set, so the dial/settings stay reachable.
53
+ // =============================
54
+ function MenuLauncherIcon({
55
+ size = 14,
56
+ color = '#20C997'
57
+ }) {
58
+ const dot = Math.max(2, size / 4);
59
+ const gap = Math.max(1, size / 16);
60
+ return /*#__PURE__*/_jsx("div", {
61
+ style: {
62
+ display: 'grid',
63
+ gridTemplateColumns: `repeat(3, ${dot}px)`,
64
+ gap,
65
+ width: size,
66
+ height: size,
67
+ alignContent: 'center',
68
+ justifyContent: 'center'
69
+ },
70
+ children: Array.from({
71
+ length: 9
72
+ }).map((_, i) => /*#__PURE__*/_jsx("div", {
73
+ style: {
74
+ width: dot,
75
+ height: dot,
76
+ borderRadius: dot / 2,
77
+ backgroundColor: color
78
+ }
79
+ }, i))
80
+ });
81
+ }
82
+
34
83
  // =============================
35
84
  // Main Component
36
85
  // =============================
37
86
 
38
- /**
39
- * Unified FloatingDevTools component - self-contained, no wrapping required.
40
- *
41
- * Matches mobile API exactly:
42
- * ```tsx
43
- * // Mobile
44
- * <FloatingDevTools environment="qa" userRole={userRole} />
45
- *
46
- * // Web (this component)
47
- * <FloatingDevTools environment="qa" userRole="admin" />
48
- * ```
49
- */
50
- export function FloatingDevTools({
51
- environment: _environment,
87
+ export function FloatingDevTools(props) {
88
+ // The tool host wraps everything so dial presses can open tool components
89
+ // into the AppOverlay (web twin of the native AppHostProvider/AppOverlay).
90
+ return /*#__PURE__*/_jsx(AppHostProvider, {
91
+ children: /*#__PURE__*/_jsx(FloatingDevToolsInner, {
92
+ ...props
93
+ })
94
+ });
95
+ }
96
+ function FloatingDevToolsInner({
97
+ environment,
52
98
  userRole,
53
- availableEnvironments: _availableEnvironments,
54
- onEnvironmentSwitch: _onEnvironmentSwitch,
99
+ availableEnvironments,
100
+ onEnvironmentSwitch,
55
101
  apps,
56
- disableHints: _disableHints,
102
+ disableHints = false,
57
103
  headless = false
58
104
  }) {
59
105
  // Internal state for dial and settings
60
106
  const [isDialOpen, setIsDialOpen] = useState(false);
61
107
  const [isSettingsOpen, setIsSettingsOpen] = useState(false);
108
+ const stateRestoredRef = useRef(false);
109
+ const appHost = useAppHost();
62
110
 
63
111
  // Auto-discover tools and merge with custom apps
64
112
  const tools = useMemo(() => autoDiscoverWithCustom(apps), [apps]);
65
113
 
114
+ // Let the host restore `@react_buoy_open_apps` from the previous session
115
+ // once it knows which tools exist (native registerApps parity).
116
+ const {
117
+ registerApps
118
+ } = appHost;
119
+ useEffect(() => {
120
+ registerApps(tools);
121
+ }, [registerApps, tools]);
122
+
66
123
  // Convert to available tools config for settings
67
124
  const availableToolsConfig = useMemo(() => tools.map(tool => ({
68
125
  id: tool.id,
@@ -72,11 +129,62 @@ export function FloatingDevTools({
72
129
  })), [tools]);
73
130
 
74
131
  // Register available tools with the shared settings hook (used by the
75
- // settings modal). The dial no longer reads per-tool show/hide state.
76
- useSettings({
77
- availableTools: availableToolsConfig
132
+ // settings modal and the bubble-row visibility gating).
133
+ const {
134
+ settings
135
+ } = useSettings({
136
+ availableTools: availableToolsConfig,
137
+ storage: webSettingsStorage
78
138
  });
79
139
 
140
+ // Dial/settings open-state persistence — native FloatingMenu.tsx:95-122 and
141
+ // DialDevTools.tsx:136-164 restore these across reloads.
142
+ useEffect(() => {
143
+ let cancelled = false;
144
+ const restore = async () => {
145
+ const [dialOpen, settingsOpen] = await Promise.all([persistentStorage.getItem(devToolsStorageKeys.dial.isOpen()), persistentStorage.getItem(devToolsStorageKeys.settings.modalOpen())]);
146
+ if (cancelled) return;
147
+ if (dialOpen === 'true') setIsDialOpen(true);
148
+ if (settingsOpen === 'true') {
149
+ setIsDialOpen(true); // settings lives above the dial (native structure)
150
+ setIsSettingsOpen(true);
151
+ }
152
+ stateRestoredRef.current = true;
153
+ };
154
+ void restore();
155
+ return () => {
156
+ cancelled = true;
157
+ };
158
+ }, []);
159
+ useEffect(() => {
160
+ if (!stateRestoredRef.current) return;
161
+ void persistentStorage.setItem(devToolsStorageKeys.dial.isOpen(), isDialOpen ? 'true' : 'false');
162
+ }, [isDialOpen]);
163
+ useEffect(() => {
164
+ if (!stateRestoredRef.current) return;
165
+ void persistentStorage.setItem(devToolsStorageKeys.settings.modalOpen(), isSettingsOpen ? 'true' : 'false');
166
+ }, [isSettingsOpen]);
167
+
168
+ // Shared open path for dial + bubble-row tools.
169
+ const openTool = useCallback(tool => {
170
+ void recordToolUsage(tool.id);
171
+ if (tool.component) {
172
+ const dockIcon = typeof tool.icon === 'function' ? tool.icon(20) : tool.icon;
173
+ appHost.open({
174
+ id: tool.id,
175
+ title: tool.name || getToolLabel(tool.id),
176
+ component: tool.component,
177
+ props: tool.props,
178
+ launchMode: tool.launchMode || 'self-modal',
179
+ singleton: tool.singleton ?? true,
180
+ icon: dockIcon,
181
+ color: tool.color || getToolColor(tool.id)
182
+ });
183
+ } else {
184
+ tool.onPress?.();
185
+ }
186
+ }, [appHost]);
187
+
80
188
  // Build dial icons for every dial-eligible tool. The dial paginates and
81
189
  // ranks them by usage, so there is no per-tool show/hide setting.
82
190
  const dialIcons = useMemo(() => {
@@ -91,22 +199,26 @@ export function FloatingDevTools({
91
199
  // It's a render function - call it with size
92
200
  icon = tool.icon(32);
93
201
  } else {
94
- icon = '🔧';
202
+ icon = null;
95
203
  }
96
204
  return {
97
205
  id: tool.id,
98
- name: getToolLabel(tool.id),
206
+ // The tool's own name wins (native parity); the registry label is
207
+ // the fallback for preset tools that don't set one.
208
+ name: tool.name || getToolLabel(tool.id),
99
209
  icon,
210
+ // Preset tools with no component and no onPress can't do anything on
211
+ // web — render them like native's uninstalled tools ("Unavailable"
212
+ // caption, non-interactive) instead of a tile that silently no-ops.
213
+ unavailable: !tool.component && !tool.onPress,
100
214
  color,
101
215
  onPress: () => {
102
- // Record usage so frequently/recently used tools rank toward page 1.
103
- void recordToolUsage(tool.id);
104
- tool.onPress?.();
216
+ openTool(tool);
105
217
  setIsDialOpen(false);
106
218
  }
107
219
  };
108
220
  });
109
- }, [tools]);
221
+ }, [tools, openTool]);
110
222
 
111
223
  // Snapshot the usage-ranked order each time the dial opens. It stays stable
112
224
  // while open so icons don't jump positions mid-interaction.
@@ -116,6 +228,10 @@ export function FloatingDevTools({
116
228
  return getRankedToolIds(dialIcons.map(i => i.id)).map(id => byId.get(id)).filter(i => Boolean(i));
117
229
  }, [isDialOpen, dialIcons]);
118
230
 
231
+ // Bubble-row tools — native FloatingMenu.tsx:349-386: slot !== 'dial',
232
+ // explicitly enabled in settings, rendered as small icon buttons.
233
+ const rowTools = useMemo(() => getToolsBySlot(tools, 'row').filter(tool => settings.floatingTools[tool.id] === true && (tool.component || tool.onPress)), [tools, settings.floatingTools]);
234
+
119
235
  // Build available tools for settings modal
120
236
  const availableTools = useMemo(() => tools.map(tool => ({
121
237
  id: tool.id,
@@ -124,31 +240,124 @@ export function FloatingDevTools({
124
240
  slot: tool.slot
125
241
  })), [tools]);
126
242
 
243
+ // Welcome tooltip — native FloatingMenu.tsx:134-157: 1000ms after mount,
244
+ // once ever, only while nothing else is open; the bubble centers under the
245
+ // arrow while it shows.
246
+ const [showWelcomeTooltip, setShowWelcomeTooltip] = useState(false);
247
+ const isAnyToolOpen = appHost.openApps.some(app => !app.minimized);
248
+ useEffect(() => {
249
+ if (disableHints) return;
250
+ let cancelled = false;
251
+ const timer = setTimeout(async () => {
252
+ const seen = await persistentStorage.getItem(MENU_TOOLTIP_KEY);
253
+ if (!cancelled && !seen) setShowWelcomeTooltip(true);
254
+ }, 1000);
255
+ return () => {
256
+ cancelled = true;
257
+ clearTimeout(timer);
258
+ };
259
+ }, [disableHints]);
260
+ const dismissWelcomeTooltip = useCallback(() => {
261
+ setShowWelcomeTooltip(false);
262
+ void persistentStorage.setItem(MENU_TOOLTIP_KEY, 'true');
263
+ }, []);
264
+
127
265
  // Callbacks
128
- const handleOpenDial = useCallback(() => setIsDialOpen(true), []);
129
- const handleCloseDial = useCallback(() => setIsDialOpen(false), []);
130
- const handleOpenSettings = useCallback(() => {
266
+ const handleOpenDial = useCallback(() => {
267
+ // Opening the dial dismisses the welcome tooltip (native handleDialOpen).
268
+ if (showWelcomeTooltip) dismissWelcomeTooltip();
269
+ setIsDialOpen(true);
270
+ }, [showWelcomeTooltip, dismissWelcomeTooltip]);
271
+ const handleCloseDial = useCallback(() => {
131
272
  setIsDialOpen(false);
132
- setIsSettingsOpen(true);
273
+ setIsSettingsOpen(false); // settings lives above the dial
274
+ }, []);
275
+ // Center press toggles settings while the dial STAYS OPEN underneath —
276
+ // the center label flips to CLOSE/SETTINGS (native DialDevTools behavior).
277
+ const handleCenterPress = useCallback(() => {
278
+ setIsSettingsOpen(open => !open);
133
279
  }, []);
134
280
  const handleCloseSettings = useCallback(() => setIsSettingsOpen(false), []);
281
+ const showEnvironmentSelector = Boolean(availableEnvironments?.length && onEnvironmentSwitch);
282
+ const environmentEnabled = settings.floatingTools.environment !== false;
283
+ const welcomeActive = showWelcomeTooltip && !isDialOpen && !isAnyToolOpen;
284
+ const hasMinimizedTools = appHost.openApps.some(app => app.minimized && app.launchMode !== 'toggle-only');
135
285
 
136
286
  // Headless: desktop sync is native-only, so there is nothing to mount on
137
287
  // web — accept the prop for API parity and render nothing. (After all
138
288
  // hooks so hook order is preserved.)
139
289
  if (headless) return null;
140
290
  return /*#__PURE__*/_jsxs(_Fragment, {
141
- children: [/*#__PURE__*/_jsx(FloatingTools, {
142
- enablePositionPersistence: true,
143
- children: userRole && /*#__PURE__*/_jsx(UserStatus, {
291
+ children: [welcomeActive && /*#__PURE__*/_jsx("div", {
292
+ style: {
293
+ position: 'fixed',
294
+ inset: 0,
295
+ backgroundColor: 'rgba(0,0,0,0.85)',
296
+ zIndex: 10000
297
+ }
298
+ }), /*#__PURE__*/_jsxs(FloatingTools, {
299
+ enablePositionPersistence: !welcomeActive,
300
+ pushToSide: isDialOpen || isAnyToolOpen,
301
+ suspendPointerEvents: isDialOpen,
302
+ centerOnboarding: welcomeActive,
303
+ minimizedStack: hasMinimizedTools ? /*#__PURE__*/_jsx(MinimizedToolsStack, {}) : undefined,
304
+ hasMinimizedStack: hasMinimizedTools,
305
+ children: [environment && environmentEnabled && !showEnvironmentSelector && /*#__PURE__*/_jsx(EnvironmentIndicator, {
306
+ environment: environment
307
+ }), showEnvironmentSelector && environmentEnabled && /*#__PURE__*/_jsx(EnvironmentSelectorInline, {
308
+ currentEnvironment: environment ?? 'local',
309
+ availableEnvironments: availableEnvironments,
310
+ onSelect: onEnvironmentSwitch
311
+ }), userRole ? /*#__PURE__*/_jsx(UserStatus, {
144
312
  userRole: userRole,
145
313
  onPress: handleOpenDial
146
- })
314
+ }) :
315
+ /*#__PURE__*/
316
+ // native MenuLauncherIcon fallback: dial stays reachable without a
317
+ // userRole pill.
318
+ _jsx("button", {
319
+ "aria-label": "Open dev tools",
320
+ onClick: handleOpenDial,
321
+ style: {
322
+ display: 'flex',
323
+ alignItems: 'center',
324
+ justifyContent: 'center',
325
+ padding: '6px 8px',
326
+ background: 'none',
327
+ border: 'none',
328
+ cursor: 'pointer'
329
+ },
330
+ children: /*#__PURE__*/_jsx(MenuLauncherIcon, {})
331
+ }), rowTools.map(tool =>
332
+ /*#__PURE__*/
333
+ // native FloatingMenu fab: paddingH 6, paddingV 4, r6, transparent
334
+ _jsx("button", {
335
+ "aria-label": tool.name || getToolLabel(tool.id),
336
+ title: tool.description,
337
+ onClick: () => openTool(tool),
338
+ style: {
339
+ display: 'flex',
340
+ alignItems: 'center',
341
+ justifyContent: 'center',
342
+ padding: '4px 6px',
343
+ borderRadius: 6,
344
+ background: 'none',
345
+ border: 'none',
346
+ cursor: 'pointer'
347
+ },
348
+ children: typeof tool.icon === 'function' ? tool.icon(16) : tool.icon
349
+ }, tool.id))]
350
+ }), welcomeActive && /*#__PURE__*/_jsx(OnboardingTooltip, {
351
+ title: "Welcome to Buoy!",
352
+ message: "Grab and position this menu wherever you want, then tap the icon to open your tools.",
353
+ onDismiss: dismissWelcomeTooltip
147
354
  }), isDialOpen && /*#__PURE__*/_jsx(DialMenu, {
148
355
  icons: rankedDialIcons,
149
356
  onClose: handleCloseDial,
150
357
  centerLabel: "BUOY",
151
- onCenterPress: handleOpenSettings
358
+ onCenterPress: handleCenterPress,
359
+ settingsOpen: isSettingsOpen,
360
+ hintsDisabled: disableHints
152
361
  }), /*#__PURE__*/_jsx(SettingsModal, {
153
362
  visible: isSettingsOpen,
154
363
  onClose: handleCloseSettings,
@@ -156,7 +365,7 @@ export function FloatingDevTools({
156
365
  onSettingsChange: _newSettings => {
157
366
  // Settings are already saved by the hook
158
367
  }
159
- })]
368
+ }), /*#__PURE__*/_jsx(AppOverlay, {})]
160
369
  });
161
370
  }
162
371
 
@@ -7,10 +7,10 @@ import { FloatingTools, UserStatus } from "./floatingTools";
7
7
  import { DialDevTools } from "./dial/DialDevTools";
8
8
  import { EnvironmentIndicator, persistentStorage, useHintsDisabled, devToolsStorageKeys, buoyColors } from "@buoy-gg/shared-ui";
9
9
  import { useDevToolsSettings } from "./DevToolsSettingsModal";
10
- import { useAppHost } from "./AppHost.js";
10
+ import { useAppHost } from "./AppHost";
11
11
  import { useDevToolsVisibility } from "./DevToolsVisibilityContext.js";
12
12
  import { toggleStateManager } from "./ToggleStateManager.js";
13
- import { OnboardingTooltip } from "./dial/OnboardingTooltip.js";
13
+ import { OnboardingTooltip } from "./dial/OnboardingTooltip";
14
14
 
15
15
  /**
16
16
  * Props for the floating developer tools launcher. Controls which apps are shown and
@@ -8,9 +8,8 @@ import { persistentStorage, getSafeAreaInsets } from "@buoy-gg/shared-ui";
8
8
  // Types
9
9
  // ============================================================================
10
10
 
11
- /**
12
- * Modal state to restore when reopening a minimized tool
13
- */
11
+ // Type moved to the platform-free AppHostLogic.ts (so the web host can use
12
+ // it without importing this react-native file); re-exported for back-compat.
14
13
 
15
14
  /**
16
15
  * Represents a minimized tool that can be restored