@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.
- package/README.md +43 -0
- package/lib/commonjs/floatingMenu/AppHost.js +410 -0
- package/lib/commonjs/floatingMenu/AppHostLogic.js +44 -0
- package/lib/commonjs/floatingMenu/DefaultConfigContext.js +45 -0
- package/lib/commonjs/floatingMenu/DevToolsSettingsModal.js +2274 -0
- package/lib/commonjs/floatingMenu/DevToolsVisibilityContext.js +49 -0
- package/lib/commonjs/floatingMenu/DraggableHeader.js +114 -0
- package/lib/commonjs/floatingMenu/FloatingDevTools.js +254 -0
- package/lib/commonjs/floatingMenu/FloatingMenu.js +364 -0
- package/lib/commonjs/floatingMenu/MinimizedToolsContext.js +247 -0
- package/lib/commonjs/floatingMenu/MinimizedToolsStack.js +206 -0
- package/lib/commonjs/floatingMenu/ToggleStateManager.js +36 -0
- package/lib/commonjs/floatingMenu/autoDiscoverPresets.js +241 -0
- package/lib/commonjs/floatingMenu/defaultConfig.js +160 -0
- package/lib/commonjs/floatingMenu/dial/DialDevTools.js +835 -0
- package/lib/commonjs/floatingMenu/dial/DialIcon.js +246 -0
- package/lib/commonjs/floatingMenu/dial/OnboardingTooltip.js +249 -0
- package/lib/commonjs/floatingMenu/dial/onboardingConstants.js +70 -0
- package/lib/commonjs/floatingMenu/floatingTools.js +771 -0
- package/lib/commonjs/floatingMenu/settingsBus.js +23 -0
- package/lib/commonjs/floatingMenu/types.js +5 -0
- package/lib/commonjs/index.js +240 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/module/floatingMenu/AppHost.js +402 -0
- package/lib/module/floatingMenu/AppHostLogic.js +39 -0
- package/lib/module/floatingMenu/DefaultConfigContext.js +39 -0
- package/lib/module/floatingMenu/DevToolsSettingsModal.js +2273 -0
- package/lib/module/floatingMenu/DevToolsVisibilityContext.js +44 -0
- package/lib/module/floatingMenu/DraggableHeader.js +110 -0
- package/lib/module/floatingMenu/FloatingDevTools.js +249 -0
- package/lib/module/floatingMenu/FloatingMenu.js +358 -0
- package/lib/module/floatingMenu/MinimizedToolsContext.js +239 -0
- package/lib/module/floatingMenu/MinimizedToolsStack.js +202 -0
- package/lib/module/floatingMenu/ToggleStateManager.js +32 -0
- package/lib/module/floatingMenu/autoDiscoverPresets.js +236 -0
- package/lib/module/floatingMenu/defaultConfig.js +151 -0
- package/lib/module/floatingMenu/dial/DialDevTools.js +829 -0
- package/lib/module/floatingMenu/dial/DialIcon.js +241 -0
- package/lib/module/floatingMenu/dial/OnboardingTooltip.js +244 -0
- package/lib/module/floatingMenu/dial/onboardingConstants.js +64 -0
- package/lib/module/floatingMenu/floatingTools.js +767 -0
- package/lib/module/floatingMenu/settingsBus.js +19 -0
- package/lib/module/floatingMenu/types.js +3 -0
- package/lib/module/index.js +29 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/commonjs/floatingMenu/AppHost.d.ts +39 -0
- package/lib/typescript/commonjs/floatingMenu/AppHost.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/AppHostLogic.d.ts +37 -0
- package/lib/typescript/commonjs/floatingMenu/AppHostLogic.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/DefaultConfigContext.d.ts +27 -0
- package/lib/typescript/commonjs/floatingMenu/DefaultConfigContext.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts +57 -0
- package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/DevToolsVisibilityContext.d.ts +25 -0
- package/lib/typescript/commonjs/floatingMenu/DevToolsVisibilityContext.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/DraggableHeader.d.ts +30 -0
- package/lib/typescript/commonjs/floatingMenu/DraggableHeader.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts +226 -0
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts +39 -0
- package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/MinimizedToolsContext.d.ts +95 -0
- package/lib/typescript/commonjs/floatingMenu/MinimizedToolsContext.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.d.ts +10 -0
- package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/ToggleStateManager.d.ts +21 -0
- package/lib/typescript/commonjs/floatingMenu/ToggleStateManager.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/autoDiscoverPresets.d.ts +75 -0
- package/lib/typescript/commonjs/floatingMenu/autoDiscoverPresets.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts +120 -0
- package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts +35 -0
- package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts +14 -0
- package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.d.ts +12 -0
- package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts +30 -0
- package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts +56 -0
- package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/settingsBus.d.ts +10 -0
- package/lib/typescript/commonjs/floatingMenu/settingsBus.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/types.d.ts +56 -0
- package/lib/typescript/commonjs/floatingMenu/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +18 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/module/floatingMenu/AppHost.d.ts +39 -0
- package/lib/typescript/module/floatingMenu/AppHost.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/AppHostLogic.d.ts +37 -0
- package/lib/typescript/module/floatingMenu/AppHostLogic.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/DefaultConfigContext.d.ts +27 -0
- package/lib/typescript/module/floatingMenu/DefaultConfigContext.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts +57 -0
- package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/DevToolsVisibilityContext.d.ts +25 -0
- package/lib/typescript/module/floatingMenu/DevToolsVisibilityContext.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/DraggableHeader.d.ts +30 -0
- package/lib/typescript/module/floatingMenu/DraggableHeader.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts +226 -0
- package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts +39 -0
- package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/MinimizedToolsContext.d.ts +95 -0
- package/lib/typescript/module/floatingMenu/MinimizedToolsContext.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/MinimizedToolsStack.d.ts +10 -0
- package/lib/typescript/module/floatingMenu/MinimizedToolsStack.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/ToggleStateManager.d.ts +21 -0
- package/lib/typescript/module/floatingMenu/ToggleStateManager.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/autoDiscoverPresets.d.ts +75 -0
- package/lib/typescript/module/floatingMenu/autoDiscoverPresets.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/defaultConfig.d.ts +120 -0
- package/lib/typescript/module/floatingMenu/defaultConfig.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts +35 -0
- package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts +14 -0
- package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.d.ts +12 -0
- package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts +30 -0
- package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/floatingTools.d.ts +56 -0
- package/lib/typescript/module/floatingMenu/floatingTools.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/settingsBus.d.ts +10 -0
- package/lib/typescript/module/floatingMenu/settingsBus.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/types.d.ts +56 -0
- package/lib/typescript/module/floatingMenu/types.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +18 -0
- package/lib/typescript/module/index.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- 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
|
+
}
|