@buoy-gg/core 2.1.15 → 3.0.0
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/lib/commonjs/floatingMenu/DevToolsSettingsModal.js +4 -34
- package/lib/commonjs/floatingMenu/DevToolsSettingsModal.web.js +3 -25
- package/lib/commonjs/floatingMenu/FloatingDevTools.js +14 -1
- package/lib/commonjs/floatingMenu/FloatingDevTools.web.js +19 -9
- package/lib/commonjs/floatingMenu/FloatingMenu.js +6 -6
- package/lib/commonjs/floatingMenu/defaultConfig.js +1 -1
- package/lib/commonjs/floatingMenu/dial/DialDevTools.js +206 -224
- package/lib/commonjs/floatingMenu/dial/DialDevTools.web.js +82 -7
- package/lib/commonjs/floatingMenu/dial/DialIcon.js +77 -71
- package/lib/commonjs/floatingMenu/dial/DialPagination.js +170 -0
- package/lib/commonjs/floatingMenu/dial/dialUsageStore.js +97 -0
- package/lib/module/floatingMenu/DevToolsSettingsModal.js +5 -35
- package/lib/module/floatingMenu/DevToolsSettingsModal.web.js +4 -28
- package/lib/module/floatingMenu/FloatingDevTools.js +14 -1
- package/lib/module/floatingMenu/FloatingDevTools.web.js +19 -9
- package/lib/module/floatingMenu/FloatingMenu.js +7 -7
- package/lib/module/floatingMenu/defaultConfig.js +1 -1
- package/lib/module/floatingMenu/dial/DialDevTools.js +209 -226
- package/lib/module/floatingMenu/dial/DialDevTools.web.js +82 -7
- package/lib/module/floatingMenu/dial/DialIcon.js +81 -74
- package/lib/module/floatingMenu/dial/DialPagination.js +165 -0
- package/lib/module/floatingMenu/dial/dialUsageStore.js +89 -0
- package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/FloatingMenu.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts +1 -1
- package/lib/typescript/commonjs/floatingMenu/defaultConfig.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts +0 -2
- package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts +7 -2
- package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/dial/DialPagination.d.ts +22 -0
- package/lib/typescript/commonjs/floatingMenu/dial/DialPagination.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/dial/dialUsageStore.d.ts +34 -0
- package/lib/typescript/commonjs/floatingMenu/dial/dialUsageStore.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.web.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/FloatingMenu.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/defaultConfig.d.ts +1 -1
- package/lib/typescript/module/floatingMenu/defaultConfig.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts +0 -2
- package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/dial/DialDevTools.web.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts +7 -2
- package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/dial/DialPagination.d.ts +22 -0
- package/lib/typescript/module/floatingMenu/dial/DialPagination.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/dial/dialUsageStore.d.ts +34 -0
- package/lib/typescript/module/floatingMenu/dial/dialUsageStore.d.ts.map +1 -0
- package/package.json +5 -5
|
@@ -7,6 +7,8 @@ exports.DialDevTools = void 0;
|
|
|
7
7
|
var _react = require("react");
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
var _DialIcon = require("./DialIcon.js");
|
|
10
|
+
var _DialPagination = require("./DialPagination.js");
|
|
11
|
+
var _dialUsageStore = require("./dialUsageStore.js");
|
|
10
12
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
11
13
|
var _DevToolsSettingsModal = require("../DevToolsSettingsModal");
|
|
12
14
|
var _license = require("@buoy-gg/license");
|
|
@@ -16,21 +18,22 @@ var _floatingToolsCore = require("@buoy-gg/floating-tools-core");
|
|
|
16
18
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
17
19
|
// Icons are provided by installedApps; no direct icon imports here.
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// Use shared layout calculation from core
|
|
24
|
-
const layout = (0, _floatingToolsCore.getDialLayout)({
|
|
25
|
-
screenWidth: SCREEN_WIDTH
|
|
26
|
-
});
|
|
27
|
-
const CIRCLE_SIZE = layout.circleSize;
|
|
28
|
-
const BUTTON_SIZE = layout.buttonSize;
|
|
21
|
+
// The circle size depends on the live window width, so it's computed inside
|
|
22
|
+
// the component via useWindowDimensions — a module-scope Dimensions.get
|
|
23
|
+
// snapshot goes stale when the window resizes after load (web/desktop).
|
|
24
|
+
const BUTTON_SIZE = _floatingToolsCore.DIAL_BUTTON_SIZE;
|
|
29
25
|
const ONBOARDING_STORAGE_KEY = "@react_buoy_settings_tooltip_shown";
|
|
26
|
+
/** A non-interactive placeholder used to fill out the last dial page. */
|
|
27
|
+
const createEmptySlot = slotIndex => ({
|
|
28
|
+
id: `empty-${slotIndex}`,
|
|
29
|
+
name: `empty-${slotIndex}`,
|
|
30
|
+
icon: null,
|
|
31
|
+
color: "transparent",
|
|
32
|
+
onPress: () => {}
|
|
33
|
+
});
|
|
30
34
|
const DialDevTools = ({
|
|
31
35
|
onClose,
|
|
32
36
|
onSettingsPress,
|
|
33
|
-
settings: externalSettings,
|
|
34
37
|
autoOpenSettings = false,
|
|
35
38
|
apps,
|
|
36
39
|
state,
|
|
@@ -43,18 +46,39 @@ const DialDevTools = ({
|
|
|
43
46
|
const onboardingDismissedRef = (0, _react.useRef)(false); // Track if onboarding was dismissed
|
|
44
47
|
const hintsDisabled = (0, _sharedUi.useHintsDisabled)();
|
|
45
48
|
const {
|
|
46
|
-
settings: hookSettings,
|
|
47
49
|
refreshSettings
|
|
48
50
|
} = (0, _DevToolsSettingsModal.useDevToolsSettings)();
|
|
49
51
|
const {
|
|
50
52
|
open
|
|
51
53
|
} = (0, _AppHost.useAppHost)();
|
|
52
54
|
const isPro = (0, _license.useIsPro)();
|
|
53
|
-
// Initialize with external settings if provided, otherwise use hook settings
|
|
54
|
-
const [localSettings, setLocalSettings] = (0, _react.useState)(externalSettings || hookSettings);
|
|
55
55
|
|
|
56
|
-
//
|
|
57
|
-
|
|
56
|
+
// Live window size — keeps the dial centered and sized correctly when the
|
|
57
|
+
// window resizes (Electron/web) or the device rotates.
|
|
58
|
+
const {
|
|
59
|
+
width: screenWidth,
|
|
60
|
+
height: screenHeight
|
|
61
|
+
} = (0, _reactNative.useWindowDimensions)();
|
|
62
|
+
const circleSize = (0, _floatingToolsCore.getDialLayout)({
|
|
63
|
+
screenWidth
|
|
64
|
+
}).circleSize;
|
|
65
|
+
const sizeStyles = (0, _react.useMemo)(() => ({
|
|
66
|
+
parent: {
|
|
67
|
+
width: circleSize,
|
|
68
|
+
height: circleSize
|
|
69
|
+
},
|
|
70
|
+
circle: {
|
|
71
|
+
width: circleSize,
|
|
72
|
+
height: circleSize,
|
|
73
|
+
borderRadius: circleSize / 2
|
|
74
|
+
},
|
|
75
|
+
rounded: {
|
|
76
|
+
borderRadius: circleSize / 2
|
|
77
|
+
},
|
|
78
|
+
gridLine: {
|
|
79
|
+
width: circleSize
|
|
80
|
+
}
|
|
81
|
+
}), [circleSize]);
|
|
58
82
|
|
|
59
83
|
// Load persisted settings modal state on mount
|
|
60
84
|
(0, _react.useEffect)(() => {
|
|
@@ -83,20 +107,6 @@ const DialDevTools = ({
|
|
|
83
107
|
});
|
|
84
108
|
}, [isSettingsModalOpen, settingsModalStateLoaded]);
|
|
85
109
|
|
|
86
|
-
// Update local settings when external settings change
|
|
87
|
-
(0, _react.useEffect)(() => {
|
|
88
|
-
if (externalSettings) {
|
|
89
|
-
setLocalSettings(externalSettings);
|
|
90
|
-
}
|
|
91
|
-
}, [externalSettings]);
|
|
92
|
-
|
|
93
|
-
// Update local settings when hook settings change (if no external settings)
|
|
94
|
-
(0, _react.useEffect)(() => {
|
|
95
|
-
if (!externalSettings) {
|
|
96
|
-
setLocalSettings(hookSettings);
|
|
97
|
-
}
|
|
98
|
-
}, [hookSettings, externalSettings]);
|
|
99
|
-
|
|
100
110
|
// Auto-open settings modal when prop is true
|
|
101
111
|
(0, _react.useEffect)(() => {
|
|
102
112
|
if (autoOpenSettings && !isSettingsModalOpen) {
|
|
@@ -133,7 +143,6 @@ const DialDevTools = ({
|
|
|
133
143
|
const dialRotation = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
|
|
134
144
|
const centerButtonScale = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
|
|
135
145
|
const iconsProgress = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
|
|
136
|
-
const glitchOffset = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
|
|
137
146
|
const pulseScale = (0, _react.useRef)(new _reactNative.Animated.Value(1)).current;
|
|
138
147
|
const availableApps = (0, _react.useMemo)(() => apps.map(({
|
|
139
148
|
id,
|
|
@@ -147,88 +156,111 @@ const DialDevTools = ({
|
|
|
147
156
|
description
|
|
148
157
|
})), [apps]);
|
|
149
158
|
|
|
150
|
-
// Subtle animations
|
|
151
|
-
const floatingAnim = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
|
|
152
|
-
const breathingScale = (0, _react.useRef)(new _reactNative.Animated.Value(1)).current;
|
|
153
|
-
const circuitOpacity = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
|
|
154
|
-
|
|
155
159
|
// Animation tracking refs
|
|
156
|
-
const glitchIntervalRef = (0, _react.useRef)(null);
|
|
157
160
|
const pulseAnimationRef = (0, _react.useRef)(null);
|
|
158
161
|
|
|
159
|
-
//
|
|
160
|
-
|
|
162
|
+
// Dial-eligible apps: everything except row-only tools. All of them are
|
|
163
|
+
// shown — paginated across pages of MAX_DIAL_SLOTS — so there is no longer
|
|
164
|
+
// a per-tool show/hide setting.
|
|
165
|
+
const dialApps = (0, _react.useMemo)(() => apps.filter(a => (a.slot ?? "both") !== "row"), [apps]);
|
|
161
166
|
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
167
|
+
// Build a stable IconType for every dial-eligible app, keyed by id.
|
|
168
|
+
const iconsById = (0, _react.useMemo)(() => {
|
|
169
|
+
const map = new Map();
|
|
170
|
+
for (const app of dialApps) {
|
|
171
|
+
map.set(app.id, {
|
|
172
|
+
id: app.id,
|
|
173
|
+
name: app.name,
|
|
174
|
+
// Pass both the pre-rendered icon (for non-function icons) and the
|
|
175
|
+
// component (for dynamic rendering).
|
|
176
|
+
icon: typeof app.icon === "function" ? null : app.icon,
|
|
177
|
+
// Cast to the expected type - the signature is compatible at runtime.
|
|
178
|
+
iconComponent: typeof app.icon === "function" ? app.icon : undefined,
|
|
179
|
+
color: app.color ?? _sharedUi.buoyColors.primary,
|
|
180
|
+
onPress: () => {
|
|
181
|
+
// Record usage so frequently/recently used tools rank toward page 1.
|
|
182
|
+
void (0, _dialUsageStore.recordToolUsage)(app.id);
|
|
183
|
+
|
|
184
|
+
// Call the app's onPress callback if provided, passing actions for
|
|
185
|
+
// toggle tools.
|
|
186
|
+
app?.onPress?.(actions);
|
|
187
|
+
|
|
188
|
+
// Only open modal if not a toggle-only tool.
|
|
189
|
+
if (app.launchMode !== "toggle-only") {
|
|
190
|
+
const resolvedIcon = typeof app.icon === "function" ? app.icon({
|
|
191
|
+
slot: "dial",
|
|
192
|
+
size: 20
|
|
193
|
+
}) : app.icon;
|
|
194
|
+
open({
|
|
195
|
+
id: app.id,
|
|
196
|
+
title: app.name,
|
|
197
|
+
component: app.component,
|
|
198
|
+
props: app.props,
|
|
199
|
+
launchMode: app.launchMode ?? "self-modal",
|
|
200
|
+
singleton: app.singleton ?? true,
|
|
201
|
+
icon: resolvedIcon,
|
|
202
|
+
color: app.color
|
|
203
|
+
});
|
|
204
|
+
}
|
|
198
205
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const resolvedIcon = typeof app.icon === "function" ? app.icon({
|
|
202
|
-
slot: "dial",
|
|
203
|
-
size: 20
|
|
204
|
-
}) : app.icon;
|
|
205
|
-
open({
|
|
206
|
-
id: app.id,
|
|
207
|
-
title: app.name,
|
|
208
|
-
component: app.component,
|
|
209
|
-
props: app.props,
|
|
210
|
-
launchMode: app.launchMode ?? "self-modal",
|
|
211
|
-
singleton: app.singleton ?? true,
|
|
212
|
-
icon: resolvedIcon,
|
|
213
|
-
color: app.color
|
|
214
|
-
});
|
|
206
|
+
// Close the dial.
|
|
207
|
+
onClose?.();
|
|
215
208
|
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
return map;
|
|
212
|
+
}, [dialApps, actions, open, onClose]);
|
|
216
213
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
// More tools enabled than can be shown - they will be hidden
|
|
214
|
+
// Snapshot the usage-ranked order when the dial opens. It stays stable while
|
|
215
|
+
// open so icons don't jump positions mid-interaction.
|
|
216
|
+
const [rankedIds, setRankedIds] = (0, _react.useState)(() => (0, _dialUsageStore.getRankedToolIds)(dialApps.map(a => a.id)));
|
|
217
|
+
(0, _react.useEffect)(() => {
|
|
218
|
+
const ids = dialApps.map(a => a.id);
|
|
219
|
+
if ((0, _dialUsageStore.isDialUsageLoaded)()) {
|
|
220
|
+
setRankedIds((0, _dialUsageStore.getRankedToolIds)(ids));
|
|
221
|
+
return;
|
|
226
222
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
223
|
+
// Usage data not loaded yet — show default order, then re-rank once ready.
|
|
224
|
+
let cancelled = false;
|
|
225
|
+
(0, _dialUsageStore.loadDialUsage)().then(() => {
|
|
226
|
+
if (!cancelled) setRankedIds((0, _dialUsageStore.getRankedToolIds)(ids));
|
|
227
|
+
});
|
|
228
|
+
return () => {
|
|
229
|
+
cancelled = true;
|
|
230
|
+
};
|
|
231
|
+
}, [dialApps]);
|
|
232
|
+
const pageCount = Math.max(1, Math.ceil(rankedIds.length / _floatingToolsCore.MAX_DIAL_SLOTS));
|
|
233
|
+
const [currentPage, setCurrentPage] = (0, _react.useState)(0);
|
|
234
|
+
const safePage = Math.min(currentPage, pageCount - 1);
|
|
235
|
+
|
|
236
|
+
// Every dial-eligible icon, with the page/slot it occupies. The ranking is
|
|
237
|
+
// snapshotted on open, so each icon's page and slot are fixed for the
|
|
238
|
+
// session — which lets us mount all icons once and paginate purely by
|
|
239
|
+
// toggling visibility (no remounts on page change).
|
|
240
|
+
const allDialIcons = (0, _react.useMemo)(() => {
|
|
241
|
+
return rankedIds.map(id => iconsById.get(id)).filter(icon => Boolean(icon)).map((icon, i) => ({
|
|
242
|
+
icon,
|
|
243
|
+
page: Math.floor(i / _floatingToolsCore.MAX_DIAL_SLOTS),
|
|
244
|
+
slot: i % _floatingToolsCore.MAX_DIAL_SLOTS
|
|
245
|
+
}));
|
|
246
|
+
}, [rankedIds, iconsById]);
|
|
247
|
+
|
|
248
|
+
// Empty slot indices for the current page (only the last page can be
|
|
249
|
+
// partial). These are cheap placeholder dots.
|
|
250
|
+
const emptySlots = (0, _react.useMemo)(() => {
|
|
251
|
+
const onThisPage = allDialIcons.filter(d => d.page === safePage).length;
|
|
252
|
+
const slots = [];
|
|
253
|
+
for (let s = onThisPage; s < _floatingToolsCore.MAX_DIAL_SLOTS; s += 1) slots.push(s);
|
|
254
|
+
return slots;
|
|
255
|
+
}, [allDialIcons, safePage]);
|
|
256
|
+
|
|
257
|
+
// Swap to another page. Every icon is already mounted, so this only toggles
|
|
258
|
+
// which ones are visible — no remount, no re-animation — keeping page
|
|
259
|
+
// changes instant.
|
|
260
|
+
const handlePageChange = next => {
|
|
261
|
+
const clamped = Math.max(0, Math.min(next, pageCount - 1));
|
|
262
|
+
if (clamped !== safePage) setCurrentPage(clamped);
|
|
263
|
+
};
|
|
232
264
|
|
|
233
265
|
// Initialize animations on mount - using shared config from core
|
|
234
266
|
(0, _react.useEffect)(() => {
|
|
@@ -249,13 +281,13 @@ const DialDevTools = ({
|
|
|
249
281
|
}
|
|
250
282
|
},
|
|
251
283
|
centerButton: {
|
|
252
|
-
delay:
|
|
284
|
+
delay: 150,
|
|
253
285
|
damping: 10,
|
|
254
286
|
stiffness: 200
|
|
255
287
|
},
|
|
256
288
|
icons: {
|
|
257
|
-
delay:
|
|
258
|
-
duration:
|
|
289
|
+
delay: 200,
|
|
290
|
+
duration: 400
|
|
259
291
|
},
|
|
260
292
|
circuitTraces: {
|
|
261
293
|
delay: 600,
|
|
@@ -326,81 +358,21 @@ const DialDevTools = ({
|
|
|
326
358
|
useNativeDriver: true
|
|
327
359
|
})]).start();
|
|
328
360
|
|
|
329
|
-
//
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
duration: continuous.glitch.stepDuration,
|
|
334
|
-
useNativeDriver: true
|
|
335
|
-
}), _reactNative.Animated.timing(glitchOffset, {
|
|
336
|
-
toValue: -continuous.glitch.offset,
|
|
337
|
-
duration: continuous.glitch.stepDuration,
|
|
338
|
-
useNativeDriver: true
|
|
339
|
-
}), _reactNative.Animated.timing(glitchOffset, {
|
|
340
|
-
toValue: 0,
|
|
341
|
-
duration: continuous.glitch.stepDuration,
|
|
342
|
-
useNativeDriver: true
|
|
343
|
-
})]).start();
|
|
344
|
-
};
|
|
345
|
-
glitchIntervalRef.current = setInterval(glitchAnimation, continuous.glitch.interval);
|
|
346
|
-
|
|
347
|
-
// Pulse animation - using shared config
|
|
348
|
-
const startPulse = () => {
|
|
349
|
-
pulseAnimationRef.current = _reactNative.Animated.loop(_reactNative.Animated.sequence([_reactNative.Animated.timing(pulseScale, {
|
|
350
|
-
toValue: continuous.pulse.maxScale,
|
|
351
|
-
duration: continuous.pulse.duration,
|
|
352
|
-
easing: _reactNative.Easing.inOut(_reactNative.Easing.ease),
|
|
353
|
-
useNativeDriver: true
|
|
354
|
-
}), _reactNative.Animated.timing(pulseScale, {
|
|
355
|
-
toValue: continuous.pulse.minScale,
|
|
356
|
-
duration: continuous.pulse.duration,
|
|
357
|
-
easing: _reactNative.Easing.inOut(_reactNative.Easing.ease),
|
|
358
|
-
useNativeDriver: true
|
|
359
|
-
})]));
|
|
360
|
-
pulseAnimationRef.current.start();
|
|
361
|
-
};
|
|
362
|
-
startPulse();
|
|
363
|
-
|
|
364
|
-
// Subtle floating animation for the dial - using shared config
|
|
365
|
-
_reactNative.Animated.loop(_reactNative.Animated.sequence([_reactNative.Animated.timing(floatingAnim, {
|
|
366
|
-
toValue: continuous.floating.maxY,
|
|
367
|
-
duration: continuous.floating.duration,
|
|
368
|
-
easing: _reactNative.Easing.inOut(_reactNative.Easing.ease),
|
|
369
|
-
useNativeDriver: true
|
|
370
|
-
}), _reactNative.Animated.timing(floatingAnim, {
|
|
371
|
-
toValue: continuous.floating.minY,
|
|
372
|
-
duration: continuous.floating.duration,
|
|
373
|
-
easing: _reactNative.Easing.inOut(_reactNative.Easing.ease),
|
|
374
|
-
useNativeDriver: true
|
|
375
|
-
})])).start();
|
|
376
|
-
|
|
377
|
-
// Gentle breathing effect for center button - using shared config
|
|
378
|
-
_reactNative.Animated.loop(_reactNative.Animated.sequence([_reactNative.Animated.timing(breathingScale, {
|
|
379
|
-
toValue: continuous.breathing.maxScale,
|
|
380
|
-
duration: continuous.breathing.duration,
|
|
361
|
+
// Pulse animation - only continuous effect kept for performance
|
|
362
|
+
pulseAnimationRef.current = _reactNative.Animated.loop(_reactNative.Animated.sequence([_reactNative.Animated.timing(pulseScale, {
|
|
363
|
+
toValue: continuous.pulse.maxScale,
|
|
364
|
+
duration: continuous.pulse.duration,
|
|
381
365
|
easing: _reactNative.Easing.inOut(_reactNative.Easing.ease),
|
|
382
366
|
useNativeDriver: true
|
|
383
|
-
}), _reactNative.Animated.timing(
|
|
384
|
-
toValue: continuous.
|
|
385
|
-
duration: continuous.
|
|
367
|
+
}), _reactNative.Animated.timing(pulseScale, {
|
|
368
|
+
toValue: continuous.pulse.minScale,
|
|
369
|
+
duration: continuous.pulse.duration,
|
|
386
370
|
easing: _reactNative.Easing.inOut(_reactNative.Easing.ease),
|
|
387
371
|
useNativeDriver: true
|
|
388
|
-
})]))
|
|
389
|
-
|
|
390
|
-
// Circuit traces fade in - using shared config
|
|
391
|
-
_reactNative.Animated.timing(circuitOpacity, {
|
|
392
|
-
toValue: 1,
|
|
393
|
-
duration: entrance.circuitTraces.duration,
|
|
394
|
-
delay: entrance.circuitTraces.delay,
|
|
395
|
-
useNativeDriver: true
|
|
396
|
-
}).start();
|
|
372
|
+
})]));
|
|
373
|
+
pulseAnimationRef.current.start();
|
|
397
374
|
return () => {
|
|
398
|
-
|
|
399
|
-
clearInterval(glitchIntervalRef.current);
|
|
400
|
-
}
|
|
401
|
-
if (pulseAnimationRef.current) {
|
|
402
|
-
pulseAnimationRef.current.stop();
|
|
403
|
-
}
|
|
375
|
+
pulseAnimationRef.current?.stop();
|
|
404
376
|
};
|
|
405
377
|
}, []);
|
|
406
378
|
const handleOnboardingDismiss = () => {
|
|
@@ -472,8 +444,8 @@ const DialDevTools = ({
|
|
|
472
444
|
}
|
|
473
445
|
});
|
|
474
446
|
};
|
|
475
|
-
const handleIconPress =
|
|
476
|
-
setSelectedIcon(
|
|
447
|
+
const handleIconPress = icon => {
|
|
448
|
+
setSelectedIcon(1);
|
|
477
449
|
const interaction = _floatingToolsCore.dialAnimationConfig?.interaction ?? {
|
|
478
450
|
iconSelect: {
|
|
479
451
|
pulse: [{
|
|
@@ -505,9 +477,9 @@ const DialDevTools = ({
|
|
|
505
477
|
|
|
506
478
|
// Trigger action - using shared delay
|
|
507
479
|
setTimeout(() => {
|
|
508
|
-
|
|
480
|
+
icon.onPress();
|
|
509
481
|
// Only close if it's not the WiFi toggle (by id)
|
|
510
|
-
if (
|
|
482
|
+
if (icon.id !== "wifi") {
|
|
511
483
|
handleClose();
|
|
512
484
|
}
|
|
513
485
|
}, interaction.iconSelect.actionDelay);
|
|
@@ -517,14 +489,9 @@ const DialDevTools = ({
|
|
|
517
489
|
const backdropAnimatedStyle = {
|
|
518
490
|
opacity: backdropOpacity
|
|
519
491
|
};
|
|
520
|
-
const glitchAnimatedStyle = {
|
|
521
|
-
transform: [{
|
|
522
|
-
translateX: glitchOffset
|
|
523
|
-
}]
|
|
524
|
-
};
|
|
525
492
|
const centerButtonAnimatedStyle = {
|
|
526
493
|
transform: [{
|
|
527
|
-
scale:
|
|
494
|
+
scale: centerButtonScale
|
|
528
495
|
}]
|
|
529
496
|
};
|
|
530
497
|
const pulseAnimatedStyle = {
|
|
@@ -542,13 +509,11 @@ const DialDevTools = ({
|
|
|
542
509
|
onPress: handleClose
|
|
543
510
|
})
|
|
544
511
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Animated.View, {
|
|
545
|
-
style: [styles.parent, {
|
|
512
|
+
style: [styles.parent, sizeStyles.parent, {
|
|
546
513
|
position: "absolute",
|
|
547
|
-
left: (
|
|
514
|
+
left: (screenWidth - circleSize) / 2,
|
|
548
515
|
bottom: 80,
|
|
549
516
|
transform: [{
|
|
550
|
-
translateY: floatingAnim
|
|
551
|
-
}, {
|
|
552
517
|
scale: dialScale
|
|
553
518
|
}, {
|
|
554
519
|
rotate: dialRotation.interpolate({
|
|
@@ -558,35 +523,47 @@ const DialDevTools = ({
|
|
|
558
523
|
}]
|
|
559
524
|
}],
|
|
560
525
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Animated.View, {
|
|
561
|
-
style: [styles.circle,
|
|
526
|
+
style: [styles.circle, sizeStyles.circle],
|
|
562
527
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
563
|
-
style: styles.gradientBackground,
|
|
528
|
+
style: [styles.gradientBackground, sizeStyles.rounded],
|
|
564
529
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
565
|
-
style: styles.gradientLayer1
|
|
530
|
+
style: [styles.gradientLayer1, sizeStyles.rounded]
|
|
566
531
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
567
|
-
style: styles.gradientLayer2
|
|
532
|
+
style: [styles.gradientLayer2, sizeStyles.rounded]
|
|
568
533
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
569
|
-
style: styles.gradientLayer3
|
|
534
|
+
style: [styles.gradientLayer3, sizeStyles.rounded]
|
|
570
535
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
571
536
|
style: styles.gridPattern,
|
|
572
537
|
children: Array.from({
|
|
573
538
|
length: 6
|
|
574
539
|
}).map((_, i) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
575
|
-
style: [styles.gridLine, {
|
|
540
|
+
style: [styles.gridLine, sizeStyles.gridLine, {
|
|
576
541
|
transform: [{
|
|
577
542
|
rotate: `${i * 60}deg`
|
|
578
543
|
}]
|
|
579
544
|
}]
|
|
580
545
|
}, i))
|
|
581
546
|
})]
|
|
582
|
-
}),
|
|
583
|
-
|
|
547
|
+
}), allDialIcons.filter(({
|
|
548
|
+
page
|
|
549
|
+
}) => page === safePage).map(({
|
|
550
|
+
icon,
|
|
551
|
+
slot
|
|
552
|
+
}) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_DialIcon.DialIcon, {
|
|
584
553
|
onPress: handleIconPress,
|
|
585
554
|
iconsProgress: iconsProgress,
|
|
586
555
|
icon: icon,
|
|
587
|
-
index:
|
|
588
|
-
totalIcons:
|
|
589
|
-
|
|
556
|
+
index: slot,
|
|
557
|
+
totalIcons: _floatingToolsCore.MAX_DIAL_SLOTS,
|
|
558
|
+
active: true
|
|
559
|
+
}, icon.id ?? `page${safePage}-${slot}`)), emptySlots.map(slot => /*#__PURE__*/(0, _jsxRuntime.jsx)(_DialIcon.DialIcon, {
|
|
560
|
+
onPress: handleIconPress,
|
|
561
|
+
iconsProgress: iconsProgress,
|
|
562
|
+
icon: createEmptySlot(slot),
|
|
563
|
+
index: slot,
|
|
564
|
+
totalIcons: _floatingToolsCore.MAX_DIAL_SLOTS,
|
|
565
|
+
active: true
|
|
566
|
+
}, `empty-${slot}`))]
|
|
590
567
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
591
568
|
style: [styles.buttonContainer, centerButtonAnimatedStyle],
|
|
592
569
|
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
@@ -644,19 +621,32 @@ const DialDevTools = ({
|
|
|
644
621
|
})]
|
|
645
622
|
})
|
|
646
623
|
})]
|
|
647
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(
|
|
648
|
-
|
|
624
|
+
}), pageCount > 1 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_DialPagination.DialPagination, {
|
|
625
|
+
page: safePage,
|
|
626
|
+
pageCount: pageCount,
|
|
627
|
+
onPrev: () => handlePageChange(safePage - 1),
|
|
628
|
+
onNext: () => handlePageChange(safePage + 1),
|
|
629
|
+
animatedStyle: {
|
|
630
|
+
position: "absolute",
|
|
631
|
+
left: (screenWidth - circleSize) / 2,
|
|
632
|
+
// Circle's bottom edge sits at bottom: 80 -> screenHeight - 80
|
|
633
|
+
// from the top. Place the pager 16px below that edge.
|
|
634
|
+
top: screenHeight - 80 + 16,
|
|
635
|
+
width: circleSize,
|
|
636
|
+
opacity: dialScale,
|
|
637
|
+
transform: [{
|
|
638
|
+
scale: dialScale
|
|
639
|
+
}]
|
|
640
|
+
}
|
|
641
|
+
}), isSettingsModalOpen && /*#__PURE__*/(0, _jsxRuntime.jsx)(_DevToolsSettingsModal.DevToolsSettingsModal, {
|
|
642
|
+
visible: true,
|
|
649
643
|
onClose: () => {
|
|
650
644
|
setIsSettingsModalOpen(false);
|
|
651
645
|
refreshSettings(); // Refresh from storage
|
|
652
646
|
},
|
|
653
|
-
onSettingsChange: newSettings => {
|
|
654
|
-
// Immediately update local settings for instant feedback
|
|
655
|
-
setLocalSettings(newSettings);
|
|
656
|
-
},
|
|
657
647
|
availableApps: availableApps
|
|
658
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_OnboardingTooltip.OnboardingTooltip, {
|
|
659
|
-
visible:
|
|
648
|
+
}), showOnboardingTooltip && !isSettingsModalOpen && !onboardingDismissedRef.current && /*#__PURE__*/(0, _jsxRuntime.jsx)(_OnboardingTooltip.OnboardingTooltip, {
|
|
649
|
+
visible: true,
|
|
660
650
|
onDismiss: handleOnboardingDismiss
|
|
661
651
|
})]
|
|
662
652
|
});
|
|
@@ -671,16 +661,13 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
671
661
|
..._reactNative.StyleSheet.absoluteFillObject,
|
|
672
662
|
backgroundColor: _floatingToolsCore.dialColors.dialBackdrop
|
|
673
663
|
},
|
|
664
|
+
// width/height/borderRadius for the circle pieces come from sizeStyles —
|
|
665
|
+
// they track the live window width.
|
|
674
666
|
parent: {
|
|
675
|
-
width: CIRCLE_SIZE,
|
|
676
|
-
height: CIRCLE_SIZE,
|
|
677
667
|
alignItems: "center",
|
|
678
668
|
justifyContent: "center"
|
|
679
669
|
},
|
|
680
670
|
circle: {
|
|
681
|
-
width: CIRCLE_SIZE,
|
|
682
|
-
height: CIRCLE_SIZE,
|
|
683
|
-
borderRadius: CIRCLE_SIZE / 2,
|
|
684
671
|
position: "absolute",
|
|
685
672
|
backgroundColor: "transparent",
|
|
686
673
|
borderWidth: 1,
|
|
@@ -697,7 +684,6 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
697
684
|
gradientBackground: {
|
|
698
685
|
width: "100%",
|
|
699
686
|
height: "100%",
|
|
700
|
-
borderRadius: CIRCLE_SIZE / 2,
|
|
701
687
|
position: "relative",
|
|
702
688
|
backgroundColor: _floatingToolsCore.dialColors.dialBackground,
|
|
703
689
|
overflow: "hidden"
|
|
@@ -705,24 +691,21 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
705
691
|
gradientLayer1: {
|
|
706
692
|
..._reactNative.StyleSheet.absoluteFillObject,
|
|
707
693
|
backgroundColor: _floatingToolsCore.dialColors.dialGradient1,
|
|
708
|
-
opacity: 0.6
|
|
709
|
-
borderRadius: CIRCLE_SIZE / 2
|
|
694
|
+
opacity: 0.6
|
|
710
695
|
},
|
|
711
696
|
gradientLayer2: {
|
|
712
697
|
..._reactNative.StyleSheet.absoluteFillObject,
|
|
713
698
|
backgroundColor: _floatingToolsCore.dialColors.dialGradient2,
|
|
714
699
|
opacity: 0.4,
|
|
715
700
|
top: "30%",
|
|
716
|
-
left: "30%"
|
|
717
|
-
borderRadius: CIRCLE_SIZE / 2
|
|
701
|
+
left: "30%"
|
|
718
702
|
},
|
|
719
703
|
gradientLayer3: {
|
|
720
704
|
..._reactNative.StyleSheet.absoluteFillObject,
|
|
721
705
|
backgroundColor: _floatingToolsCore.dialColors.dialGradient3,
|
|
722
706
|
opacity: 0.3,
|
|
723
707
|
top: "50%",
|
|
724
|
-
left: "50%"
|
|
725
|
-
borderRadius: CIRCLE_SIZE / 2
|
|
708
|
+
left: "50%"
|
|
726
709
|
},
|
|
727
710
|
gridPattern: {
|
|
728
711
|
..._reactNative.StyleSheet.absoluteFillObject,
|
|
@@ -731,7 +714,6 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
731
714
|
},
|
|
732
715
|
gridLine: {
|
|
733
716
|
position: "absolute",
|
|
734
|
-
width: CIRCLE_SIZE,
|
|
735
717
|
height: 1,
|
|
736
718
|
backgroundColor: _floatingToolsCore.dialColors.dialGridLine
|
|
737
719
|
},
|