@buoy-gg/core 7.0.35 → 7.0.38
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 +57 -66
- package/bin/buoy.js +71 -20
- package/lib/commonjs/Buoy.js +3 -16
- package/lib/commonjs/floatingMenu/AppHost.js +1 -1
- package/lib/commonjs/floatingMenu/AppHost.web.js +1 -1
- package/lib/commonjs/floatingMenu/AppIcon.web.js +53 -0
- package/lib/commonjs/floatingMenu/DevToolsSettingsModal.js +12 -5
- package/lib/commonjs/floatingMenu/DevToolsSettingsModal.web.js +14 -34
- package/lib/commonjs/floatingMenu/FloatingDevTools.js +32 -48
- package/lib/commonjs/floatingMenu/FloatingDevTools.web.js +24 -4
- package/lib/commonjs/floatingMenu/FloatingMenu.js +1 -1
- package/lib/commonjs/floatingMenu/MinimizedToolsStack.js +83 -4
- package/lib/commonjs/floatingMenu/MinimizedToolsStack.web.js +90 -6
- package/lib/commonjs/floatingMenu/ToolHost.js +1 -0
- package/lib/commonjs/floatingMenu/autoExternalSync.js +3 -2
- package/lib/commonjs/floatingMenu/dial/DialDevTools.js +43 -10
- package/lib/commonjs/floatingMenu/dial/DialDevTools.web.js +18 -5
- package/lib/commonjs/floatingMenu/dial/dialUsageStore.js +13 -17
- package/lib/commonjs/floatingMenu/dial/onboardingConstants.js +37 -65
- package/lib/commonjs/floatingMenu/dial/onboardingConstants.web.js +37 -29
- package/lib/commonjs/floatingMenu/floatingTools.js +23 -889
- package/lib/commonjs/floatingMenu/floatingTools.web.js +24 -514
- package/lib/commonjs/floatingMenu/optionalRequire.js +4 -0
- package/lib/commonjs/telemetry/installPing.js +7 -0
- package/lib/commonjs/version.js +1 -1
- package/lib/module/Buoy.js +3 -16
- package/lib/module/floatingMenu/AppHost.js +1 -1
- package/lib/module/floatingMenu/AppHost.web.js +1 -1
- package/lib/module/floatingMenu/AppIcon.web.js +47 -0
- package/lib/module/floatingMenu/DevToolsSettingsModal.js +13 -6
- package/lib/module/floatingMenu/DevToolsSettingsModal.web.js +15 -35
- package/lib/module/floatingMenu/FloatingDevTools.js +35 -49
- package/lib/module/floatingMenu/FloatingDevTools.web.js +24 -3
- package/lib/module/floatingMenu/FloatingMenu.js +1 -1
- package/lib/module/floatingMenu/MinimizedToolsStack.js +88 -8
- package/lib/module/floatingMenu/MinimizedToolsStack.web.js +92 -8
- package/lib/module/floatingMenu/ToolHost.js +1 -0
- package/lib/module/floatingMenu/autoExternalSync.js +3 -2
- package/lib/module/floatingMenu/dial/DialDevTools.js +43 -10
- package/lib/module/floatingMenu/dial/DialDevTools.web.js +18 -5
- package/lib/module/floatingMenu/dial/dialUsageStore.js +14 -18
- package/lib/module/floatingMenu/dial/onboardingConstants.js +1 -62
- package/lib/module/floatingMenu/dial/onboardingConstants.web.js +1 -25
- package/lib/module/floatingMenu/floatingTools.js +15 -890
- package/lib/module/floatingMenu/floatingTools.web.js +1 -517
- package/lib/module/floatingMenu/optionalRequire.js +4 -0
- package/lib/module/telemetry/installPing.js +7 -0
- package/lib/module/version.js +1 -1
- package/lib/typescript/commonjs/Buoy.d.ts +14 -3
- package/lib/typescript/commonjs/Buoy.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/AppIcon.web.d.ts +14 -0
- package/lib/typescript/commonjs/floatingMenu/AppIcon.web.d.ts.map +1 -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 +4 -14
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts +4 -19
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/ToolHost.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts +3 -4
- package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts.map +1 -1
- 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/dialUsageStore.d.ts +3 -15
- package/lib/typescript/commonjs/floatingMenu/dial/dialUsageStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts +1 -29
- package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.web.d.ts +1 -12
- package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts +6 -55
- package/lib/typescript/commonjs/floatingMenu/floatingTools.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/floatingTools.web.d.ts +3 -53
- package/lib/typescript/commonjs/floatingMenu/floatingTools.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/optionalRequire.d.ts.map +1 -1
- package/lib/typescript/commonjs/telemetry/installPing.d.ts +8 -1
- package/lib/typescript/commonjs/telemetry/installPing.d.ts.map +1 -1
- package/lib/typescript/commonjs/version.d.ts +1 -1
- package/lib/typescript/module/Buoy.d.ts +14 -3
- package/lib/typescript/module/Buoy.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/AppIcon.web.d.ts +14 -0
- package/lib/typescript/module/floatingMenu/AppIcon.web.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 +4 -14
- package/lib/typescript/module/floatingMenu/FloatingDevTools.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts +4 -19
- package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/MinimizedToolsStack.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/MinimizedToolsStack.web.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/ToolHost.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts +3 -4
- package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts.map +1 -1
- 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/dialUsageStore.d.ts +3 -15
- package/lib/typescript/module/floatingMenu/dial/dialUsageStore.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts +1 -29
- package/lib/typescript/module/floatingMenu/dial/onboardingConstants.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/dial/onboardingConstants.web.d.ts +1 -12
- package/lib/typescript/module/floatingMenu/dial/onboardingConstants.web.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/floatingTools.d.ts +6 -55
- package/lib/typescript/module/floatingMenu/floatingTools.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/floatingTools.web.d.ts +3 -53
- package/lib/typescript/module/floatingMenu/floatingTools.web.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/optionalRequire.d.ts.map +1 -1
- package/lib/typescript/module/telemetry/installPing.d.ts +8 -1
- package/lib/typescript/module/telemetry/installPing.d.ts.map +1 -1
- package/lib/typescript/module/version.d.ts +1 -1
- package/package.json +6 -6
|
@@ -16,8 +16,41 @@
|
|
|
16
16
|
* used before this replaced it.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
20
|
-
import { persistentStorage } from '@buoy-gg/shared-ui';
|
|
19
|
+
import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react';
|
|
20
|
+
import { persistentStorage, getToolAttention, subscribeToolAttention } from '@buoy-gg/shared-ui';
|
|
21
|
+
|
|
22
|
+
/** Twin of the native hook — see MinimizedToolsStack.tsx. */
|
|
23
|
+
function useToolAttention(toolId) {
|
|
24
|
+
return useSyncExternalStore(subscribeToolAttention, () => getToolAttention(toolId), () => getToolAttention(toolId));
|
|
25
|
+
}
|
|
26
|
+
const ATTENTION_COLORS = {
|
|
27
|
+
needsInput: '#FF8329',
|
|
28
|
+
done: '#20C997',
|
|
29
|
+
failed: '#FF3B30'
|
|
30
|
+
};
|
|
31
|
+
const ATTENTION_RANK = {
|
|
32
|
+
needsInput: 3,
|
|
33
|
+
failed: 2,
|
|
34
|
+
done: 1
|
|
35
|
+
};
|
|
36
|
+
const strongestCache = new Map();
|
|
37
|
+
function strongest(key) {
|
|
38
|
+
let best = null;
|
|
39
|
+
for (const id of key ? key.split('|') : []) {
|
|
40
|
+
const a = getToolAttention(id);
|
|
41
|
+
if (a && (!best || ATTENTION_RANK[a.kind] > ATTENTION_RANK[best.kind])) best = a;
|
|
42
|
+
}
|
|
43
|
+
const prev = strongestCache.get(key);
|
|
44
|
+
if (prev && best && prev.kind === best.kind && prev.label === best.label) return prev;
|
|
45
|
+
if (!prev && !best) return null;
|
|
46
|
+
strongestCache.set(key, best);
|
|
47
|
+
return best;
|
|
48
|
+
}
|
|
49
|
+
/** Twin of the native hook: the strongest attention across the folded dock. */
|
|
50
|
+
function useStrongestAttention(toolIds) {
|
|
51
|
+
const key = toolIds.join('|');
|
|
52
|
+
return useSyncExternalStore(subscribeToolAttention, () => strongest(key), () => strongest(key));
|
|
53
|
+
}
|
|
21
54
|
import { useAppHost } from "./AppHost.web.js";
|
|
22
55
|
|
|
23
56
|
// native MinimizedToolsStack.tsx constants
|
|
@@ -58,11 +91,13 @@ function ChevronGlyph({
|
|
|
58
91
|
});
|
|
59
92
|
}
|
|
60
93
|
function GlitchToolButton({
|
|
94
|
+
id,
|
|
61
95
|
title,
|
|
62
96
|
icon,
|
|
63
97
|
index,
|
|
64
98
|
onPress
|
|
65
99
|
}) {
|
|
100
|
+
const attention = useToolAttention(id);
|
|
66
101
|
const innerRef = useRef(null);
|
|
67
102
|
useEffect(() => {
|
|
68
103
|
let mounted = true;
|
|
@@ -113,9 +148,9 @@ function GlitchToolButton({
|
|
|
113
148
|
clearTimeout(timer);
|
|
114
149
|
};
|
|
115
150
|
}, [index]);
|
|
116
|
-
return /*#__PURE__*/
|
|
151
|
+
return /*#__PURE__*/_jsxs("button", {
|
|
117
152
|
type: "button",
|
|
118
|
-
"aria-label": `Restore ${title}`,
|
|
153
|
+
"aria-label": attention ? `${title} — ${attention.label}. Restore` : `Restore ${title}`,
|
|
119
154
|
onClick: onPress,
|
|
120
155
|
style: {
|
|
121
156
|
width: TOOL_ITEM_SIZE,
|
|
@@ -126,9 +161,10 @@ function GlitchToolButton({
|
|
|
126
161
|
background: 'none',
|
|
127
162
|
border: 'none',
|
|
128
163
|
padding: 0,
|
|
129
|
-
cursor: 'pointer'
|
|
164
|
+
cursor: 'pointer',
|
|
165
|
+
position: 'relative'
|
|
130
166
|
},
|
|
131
|
-
children: /*#__PURE__*/_jsx("div", {
|
|
167
|
+
children: [/*#__PURE__*/_jsx("div", {
|
|
132
168
|
ref: innerRef,
|
|
133
169
|
style: {
|
|
134
170
|
display: 'flex',
|
|
@@ -136,7 +172,30 @@ function GlitchToolButton({
|
|
|
136
172
|
justifyContent: 'center'
|
|
137
173
|
},
|
|
138
174
|
children: icon
|
|
139
|
-
})
|
|
175
|
+
}), attention ? /*#__PURE__*/_jsx("span", {
|
|
176
|
+
"aria-hidden": true,
|
|
177
|
+
"data-testid": `buoy-dock-badge-${id}-${attention.kind}`,
|
|
178
|
+
style: {
|
|
179
|
+
position: 'absolute',
|
|
180
|
+
top: 0,
|
|
181
|
+
right: 0,
|
|
182
|
+
minWidth: 12,
|
|
183
|
+
height: 12,
|
|
184
|
+
borderRadius: 6,
|
|
185
|
+
padding: '0 2px',
|
|
186
|
+
boxSizing: 'border-box',
|
|
187
|
+
background: ATTENTION_COLORS[attention.kind],
|
|
188
|
+
border: '1.5px solid #0C0C0C',
|
|
189
|
+
color: '#000',
|
|
190
|
+
fontSize: 8,
|
|
191
|
+
fontWeight: 800,
|
|
192
|
+
lineHeight: '9px',
|
|
193
|
+
display: 'flex',
|
|
194
|
+
alignItems: 'center',
|
|
195
|
+
justifyContent: 'center'
|
|
196
|
+
},
|
|
197
|
+
children: attention.kind === 'needsInput' ? '!' : ''
|
|
198
|
+
}) : null]
|
|
140
199
|
});
|
|
141
200
|
}
|
|
142
201
|
export function MinimizedToolsStack({
|
|
@@ -149,6 +208,7 @@ export function MinimizedToolsStack({
|
|
|
149
208
|
const minimized = openApps.filter(app => app.minimized && app.launchMode !== 'toggle-only');
|
|
150
209
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
151
210
|
const [isStateRestored, setIsStateRestored] = useState(false);
|
|
211
|
+
const peekAttention = useStrongestAttention(minimized.map(app => app.id));
|
|
152
212
|
|
|
153
213
|
// Restore + persist the expanded flag (native ExpandablePopover persistState).
|
|
154
214
|
useEffect(() => {
|
|
@@ -216,6 +276,7 @@ export function MinimizedToolsStack({
|
|
|
216
276
|
boxSizing: 'border-box'
|
|
217
277
|
},
|
|
218
278
|
children: [[...minimized].reverse().map((app, index) => /*#__PURE__*/_jsx(GlitchToolButton, {
|
|
279
|
+
id: app.id,
|
|
219
280
|
title: app.title || app.id,
|
|
220
281
|
icon: app.icon ?? /*#__PURE__*/_jsx("span", {
|
|
221
282
|
style: {
|
|
@@ -270,7 +331,30 @@ export function MinimizedToolsStack({
|
|
|
270
331
|
pointerEvents: isExpanded ? 'none' : 'auto',
|
|
271
332
|
...frame
|
|
272
333
|
},
|
|
273
|
-
children: [/*#__PURE__*/_jsx(ChevronGlyph, {}),
|
|
334
|
+
children: [/*#__PURE__*/_jsx(ChevronGlyph, {}), peekAttention ? /*#__PURE__*/_jsx("span", {
|
|
335
|
+
"aria-hidden": true,
|
|
336
|
+
"data-testid": `buoy-dock-peek-attention`,
|
|
337
|
+
style: {
|
|
338
|
+
position: 'absolute',
|
|
339
|
+
top: 2,
|
|
340
|
+
right: 2,
|
|
341
|
+
minWidth: 11,
|
|
342
|
+
height: 11,
|
|
343
|
+
borderRadius: 6,
|
|
344
|
+
padding: '0 2px',
|
|
345
|
+
boxSizing: 'border-box',
|
|
346
|
+
background: ATTENTION_COLORS[peekAttention.kind],
|
|
347
|
+
border: '1.5px solid #0C0C0C',
|
|
348
|
+
color: '#000',
|
|
349
|
+
fontSize: 8,
|
|
350
|
+
fontWeight: 800,
|
|
351
|
+
lineHeight: '9px',
|
|
352
|
+
display: 'flex',
|
|
353
|
+
alignItems: 'center',
|
|
354
|
+
justifyContent: 'center'
|
|
355
|
+
},
|
|
356
|
+
children: peekAttention.kind === 'needsInput' ? '!' : ''
|
|
357
|
+
}) : null, minimized.length > 1 && /*#__PURE__*/_jsx("span", {
|
|
274
358
|
style: {
|
|
275
359
|
fontSize: 9,
|
|
276
360
|
fontWeight: 600,
|
|
@@ -189,6 +189,7 @@ export function ToolModalShell({
|
|
|
189
189
|
subtitle: subtitle
|
|
190
190
|
}), /*#__PURE__*/_jsxs(ModalHeader.Actions, {
|
|
191
191
|
children: [!isSearchActive && actions, searchable && !isSearchActive && /*#__PURE__*/_jsx(HeaderSearchButton, {
|
|
192
|
+
variant: "chip",
|
|
192
193
|
testID: `tool-search-${app.id}`,
|
|
193
194
|
onPress: () => setIsSearchActive(true)
|
|
194
195
|
})]
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { useEffect, useMemo, useRef } from "react";
|
|
21
21
|
import { Platform } from "react-native";
|
|
22
|
+
import { LicenseManager } from "@buoy-gg/license";
|
|
22
23
|
import { reportOptionalRequireError } from "./optionalRequire.js";
|
|
23
24
|
import { appSyncAdapter } from "./sync/appSyncAdapter.js";
|
|
24
25
|
import { useDeviceIdentity } from "./deviceIdentity.js";
|
|
@@ -285,11 +286,11 @@ export function AutoExternalSync({
|
|
|
285
286
|
useEffect(() => {
|
|
286
287
|
if (!socket) return;
|
|
287
288
|
const emitLicense = () => {
|
|
289
|
+
if (!LicenseManager.hasAccountAccess() || !socket.connected) return;
|
|
288
290
|
socket.emit("device-license", {
|
|
289
291
|
isPro: licenseRef.current.isPro ? "1" : "0",
|
|
290
292
|
// Brokers older than the tier split ignore this; newer ones prefer it.
|
|
291
|
-
tier: licenseRef.current.tier ?? (licenseRef.current.isPro ? "pro" : "
|
|
292
|
-
licenseKey: licenseRef.current.licenseKey ?? ""
|
|
293
|
+
tier: licenseRef.current.tier ?? (licenseRef.current.isPro ? "pro" : "free")
|
|
293
294
|
});
|
|
294
295
|
};
|
|
295
296
|
if (socket.connected) emitLicense();
|
|
@@ -11,7 +11,7 @@ import { getRankedToolIds, isDialUsageLoaded, loadDialUsage, recordToolUsage } f
|
|
|
11
11
|
import { persistentStorage, useHintsDisabled, devToolsStorageKeys, buoyColors, ToolBackground, useToolBackground } from "@buoy-gg/shared-ui";
|
|
12
12
|
import { DevToolsSettingsModal, useDevToolsSettings } from "../DevToolsSettingsModal";
|
|
13
13
|
import { useProAccess } from "@buoy-gg/license";
|
|
14
|
-
import { renderAppIcon } from "../AppIcon
|
|
14
|
+
import { renderAppIcon } from "../AppIcon";
|
|
15
15
|
import { useAppHost } from "../AppHost";
|
|
16
16
|
import { OnboardingTooltip } from "./OnboardingTooltip";
|
|
17
17
|
import { getDialLayout, MAX_DIAL_SLOTS, DIAL_BUTTON_SIZE, dialAnimationConfig, dialColors } from "@buoy-gg/floating-tools-core";
|
|
@@ -21,6 +21,14 @@ import { getDialLayout, MAX_DIAL_SLOTS, DIAL_BUTTON_SIZE, dialAnimationConfig, d
|
|
|
21
21
|
// snapshot goes stale when the window resizes after load (web/desktop).
|
|
22
22
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
23
23
|
const BUTTON_SIZE = DIAL_BUTTON_SIZE;
|
|
24
|
+
/** The dial face is a circle, so its centre is the middle of the measured square. */
|
|
25
|
+
const DIAL_BACKGROUND_FOCUS = {
|
|
26
|
+
x: 0.5,
|
|
27
|
+
y: 0.5
|
|
28
|
+
};
|
|
29
|
+
// Radius kept clear of grid spokes around the glass hub (its outer disc is
|
|
30
|
+
// 0.75 × BUTTON_SIZE, plus a breath).
|
|
31
|
+
const HUB_CLEARANCE = BUTTON_SIZE * 0.75 + 6;
|
|
24
32
|
|
|
25
33
|
/**
|
|
26
34
|
* The centre button's idle pulse, off by design.
|
|
@@ -97,6 +105,11 @@ export const DialDevTools = ({
|
|
|
97
105
|
},
|
|
98
106
|
gridLine: {
|
|
99
107
|
width: circleSize
|
|
108
|
+
},
|
|
109
|
+
// Each spoke is two segments that stop short of the glass hub, so the
|
|
110
|
+
// six lines never meet in the middle where the background shows through.
|
|
111
|
+
spoke: {
|
|
112
|
+
width: circleSize / 2 - HUB_CLEARANCE
|
|
100
113
|
}
|
|
101
114
|
}), [circleSize]);
|
|
102
115
|
|
|
@@ -222,7 +235,7 @@ export const DialDevTools = ({
|
|
|
222
235
|
iconComponent: typeof app.icon === "function" ? app.icon : undefined,
|
|
223
236
|
color: app.color ?? buoyColors.primary,
|
|
224
237
|
onPress: () => {
|
|
225
|
-
//
|
|
238
|
+
// Put this tool first the next time the dial opens.
|
|
226
239
|
void recordToolUsage(app.id);
|
|
227
240
|
|
|
228
241
|
// Call the app's onPress callback if provided, passing actions for
|
|
@@ -586,16 +599,23 @@ export const DialDevTools = ({
|
|
|
586
599
|
style: styles.gridPattern,
|
|
587
600
|
children: Array.from({
|
|
588
601
|
length: 6
|
|
589
|
-
}).map((_, i) => /*#__PURE__*/
|
|
602
|
+
}).map((_, i) => /*#__PURE__*/_jsxs(View, {
|
|
590
603
|
style: [styles.gridLine, sizeStyles.gridLine, {
|
|
591
604
|
transform: [{
|
|
592
605
|
rotate: `${i * 60}deg`
|
|
593
606
|
}]
|
|
594
|
-
}]
|
|
607
|
+
}],
|
|
608
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
609
|
+
style: [styles.spoke, sizeStyles.spoke, styles.spokeLeft]
|
|
610
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
611
|
+
style: [styles.spoke, sizeStyles.spoke, styles.spokeRight]
|
|
612
|
+
})]
|
|
595
613
|
}, i))
|
|
596
614
|
}), backgroundId === "hyperspace" ? /*#__PURE__*/_jsx(DialHyperspace, {
|
|
597
615
|
size: circleSize
|
|
598
|
-
}) : /*#__PURE__*/_jsx(ToolBackground, {
|
|
616
|
+
}) : /*#__PURE__*/_jsx(ToolBackground, {
|
|
617
|
+
focus: DIAL_BACKGROUND_FOCUS
|
|
618
|
+
})]
|
|
599
619
|
}), allDialIcons.filter(({
|
|
600
620
|
page
|
|
601
621
|
}) => page === safePage).map(({
|
|
@@ -753,9 +773,20 @@ const styles = StyleSheet.create({
|
|
|
753
773
|
},
|
|
754
774
|
gridLine: {
|
|
755
775
|
position: "absolute",
|
|
776
|
+
height: 1
|
|
777
|
+
},
|
|
778
|
+
spoke: {
|
|
779
|
+
position: "absolute",
|
|
780
|
+
top: 0,
|
|
756
781
|
height: 1,
|
|
757
782
|
backgroundColor: dialColors.dialGridLine
|
|
758
783
|
},
|
|
784
|
+
spokeLeft: {
|
|
785
|
+
left: 0
|
|
786
|
+
},
|
|
787
|
+
spokeRight: {
|
|
788
|
+
right: 0
|
|
789
|
+
},
|
|
759
790
|
buttonContainer: {
|
|
760
791
|
zIndex: 1,
|
|
761
792
|
backgroundColor: "transparent",
|
|
@@ -766,6 +797,10 @@ const styles = StyleSheet.create({
|
|
|
766
797
|
height: BUTTON_SIZE * 1.5,
|
|
767
798
|
borderRadius: BUTTON_SIZE
|
|
768
799
|
},
|
|
800
|
+
// The hub is glass: no rings, no opaque fill. The outer disc is a barely-
|
|
801
|
+
// there shade and the inner button a soft puck, so the tool background runs
|
|
802
|
+
// through the middle of the dial while BUOY / PRO stay legible. The spokes
|
|
803
|
+
// stop at HUB_CLEARANCE so nothing draws under it.
|
|
769
804
|
buttonGradient: {
|
|
770
805
|
width: "100%",
|
|
771
806
|
height: "100%",
|
|
@@ -773,7 +808,7 @@ const styles = StyleSheet.create({
|
|
|
773
808
|
alignItems: "center",
|
|
774
809
|
justifyContent: "center",
|
|
775
810
|
padding: 4,
|
|
776
|
-
backgroundColor: dialColors.
|
|
811
|
+
backgroundColor: dialColors.hubHalo,
|
|
777
812
|
position: "relative",
|
|
778
813
|
overflow: "hidden"
|
|
779
814
|
},
|
|
@@ -800,14 +835,12 @@ const styles = StyleSheet.create({
|
|
|
800
835
|
borderRadius: BUTTON_SIZE
|
|
801
836
|
},
|
|
802
837
|
buttonBorder: {
|
|
803
|
-
backgroundColor: dialColors.
|
|
838
|
+
backgroundColor: dialColors.hubFill,
|
|
804
839
|
alignItems: "center",
|
|
805
840
|
justifyContent: "center",
|
|
806
841
|
width: BUTTON_SIZE * 1.2,
|
|
807
842
|
height: BUTTON_SIZE * 1.2,
|
|
808
|
-
borderRadius: BUTTON_SIZE * 0.6
|
|
809
|
-
borderWidth: 2,
|
|
810
|
-
borderColor: dialColors.dialBorder
|
|
843
|
+
borderRadius: BUTTON_SIZE * 0.6
|
|
811
844
|
},
|
|
812
845
|
button: {
|
|
813
846
|
width: BUTTON_SIZE,
|
|
@@ -548,6 +548,8 @@ export function DialMenu({
|
|
|
548
548
|
// Computed values
|
|
549
549
|
const buttonContainerSize = layout.buttonSize * dialStyles.centerButton.containerRatio;
|
|
550
550
|
const buttonBorderSize = layout.buttonSize * dialStyles.centerButton.borderRatio;
|
|
551
|
+
// Grid spokes stop this far from the centre: the hub's outer disc plus a breath.
|
|
552
|
+
const hubClearance = layout.buttonSize * dialStyles.centerButton.containerRatio / 2 + 6;
|
|
551
553
|
const isAnimating = entranceComplete && !isExiting;
|
|
552
554
|
return /*#__PURE__*/_jsxs("div", {
|
|
553
555
|
role: "dialog",
|
|
@@ -623,10 +625,19 @@ export function DialMenu({
|
|
|
623
625
|
left: 0,
|
|
624
626
|
right: 0,
|
|
625
627
|
height: dialStyles.grid.lineHeight,
|
|
626
|
-
backgroundColor: dialColors.dialGridLine,
|
|
627
628
|
transform: `rotate(${deg}deg)`,
|
|
628
629
|
transformOrigin: 'center'
|
|
629
|
-
}
|
|
630
|
+
},
|
|
631
|
+
children: ['left', 'right'].map(side => /*#__PURE__*/_jsx("div", {
|
|
632
|
+
style: {
|
|
633
|
+
position: 'absolute',
|
|
634
|
+
top: 0,
|
|
635
|
+
[side]: 0,
|
|
636
|
+
width: `calc(50% - ${hubClearance}px)`,
|
|
637
|
+
height: dialStyles.grid.lineHeight,
|
|
638
|
+
backgroundColor: dialColors.dialGridLine
|
|
639
|
+
}
|
|
640
|
+
}, side))
|
|
630
641
|
}, deg)), paddedIcons.map((icon, index) => /*#__PURE__*/_jsx(DialIconItem, {
|
|
631
642
|
icon: icon,
|
|
632
643
|
index: index,
|
|
@@ -646,7 +657,10 @@ export function DialMenu({
|
|
|
646
657
|
display: 'flex',
|
|
647
658
|
alignItems: 'center',
|
|
648
659
|
justifyContent: 'center',
|
|
649
|
-
|
|
660
|
+
// Glass hub — see the native twin: a barely-there shade on the outer
|
|
661
|
+
// disc, a soft puck inside, no rings, so the tool background runs
|
|
662
|
+
// through the middle of the dial.
|
|
663
|
+
backgroundColor: dialColors.hubHalo,
|
|
650
664
|
borderRadius: layout.buttonSize,
|
|
651
665
|
overflow: 'hidden',
|
|
652
666
|
transform: `scale(${centerButtonScale * centerPulseScale})`,
|
|
@@ -685,8 +699,7 @@ export function DialMenu({
|
|
|
685
699
|
width: buttonBorderSize,
|
|
686
700
|
height: buttonBorderSize,
|
|
687
701
|
borderRadius: layout.buttonSize * dialStyles.centerButton.borderRadiusRatio,
|
|
688
|
-
backgroundColor: dialColors.
|
|
689
|
-
border: `${dialStyles.centerButton.borderWidth}px solid ${dialColors.dialBorder}`,
|
|
702
|
+
backgroundColor: dialColors.hubFill,
|
|
690
703
|
display: 'flex',
|
|
691
704
|
alignItems: 'center',
|
|
692
705
|
justifyContent: 'center'
|
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Wraps the pure scoring logic from `@buoy-gg/floating-tools-core` with a
|
|
7
|
-
* persisted, in-memory cache. The dial menu uses this to order tools by how
|
|
8
|
-
* recently/frequently they are used, so the most-used tools land on page 1.
|
|
9
|
-
*
|
|
10
|
-
* The cache is loaded eagerly on import so `getRankedToolIds` can run
|
|
11
|
-
* synchronously by the time the dial opens.
|
|
4
|
+
* Persists dial usage and orders tools by their last launch.
|
|
5
|
+
* The cache loads on import so opening the dial can rank tools synchronously.
|
|
12
6
|
*/
|
|
13
7
|
|
|
14
8
|
import { persistentStorage, devToolsStorageKeys } from "@buoy-gg/shared-ui";
|
|
15
|
-
import {
|
|
9
|
+
import { recordUsage, pruneUsage } from "@buoy-gg/floating-tools-core";
|
|
16
10
|
const STORAGE_KEY = devToolsStorageKeys.dial.usage();
|
|
17
11
|
let cache = {};
|
|
18
12
|
let loaded = false;
|
|
@@ -50,15 +44,15 @@ export function isDialUsageLoaded() {
|
|
|
50
44
|
return loaded;
|
|
51
45
|
}
|
|
52
46
|
|
|
53
|
-
/**
|
|
54
|
-
* Rank tool ids by recency-weighted usage, highest first. Synchronous —
|
|
55
|
-
* operates against the in-memory cache. Never-used tools keep their original
|
|
56
|
-
* order as a tie-breaker.
|
|
57
|
-
*
|
|
58
|
-
* @param orderedIds - Tool ids in their default/registration order
|
|
59
|
-
*/
|
|
47
|
+
/** Most recently opened first; unused tools keep registration order. */
|
|
60
48
|
export function getRankedToolIds(orderedIds) {
|
|
61
|
-
return
|
|
49
|
+
return orderedIds.map((id, index) => ({
|
|
50
|
+
id,
|
|
51
|
+
index,
|
|
52
|
+
lastUsed: cache[id]?.lastUsed ?? 0
|
|
53
|
+
})).sort((a, b) => b.lastUsed - a.lastUsed || a.index - b.index).map(({
|
|
54
|
+
id
|
|
55
|
+
}) => id);
|
|
62
56
|
}
|
|
63
57
|
|
|
64
58
|
/**
|
|
@@ -69,7 +63,9 @@ export function getRankedToolIds(orderedIds) {
|
|
|
69
63
|
export async function recordToolUsage(id) {
|
|
70
64
|
if (!id) return;
|
|
71
65
|
if (!loaded) await loadDialUsage();
|
|
72
|
-
|
|
66
|
+
|
|
67
|
+
// Preserve launch order even when presses share a timestamp or the clock moves back.
|
|
68
|
+
const now = Math.max(Date.now(), ...Object.values(cache).map(entry => entry.lastUsed + 1));
|
|
73
69
|
cache = pruneUsage(recordUsage(cache, id, now), now);
|
|
74
70
|
try {
|
|
75
71
|
await persistentStorage.setItem(STORAGE_KEY, JSON.stringify(cache));
|
|
@@ -1,64 +1,3 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Shared constants for onboarding tooltip positioning
|
|
7
|
-
* These ensure the floating menu aligns perfectly under the tooltip arrow
|
|
8
|
-
* All positions are calculated dynamically based on screen size
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
// Arrow dimensions (relative positioning)
|
|
12
|
-
export const ARROW_BOTTOM_OFFSET = -40; // Arrow extends below tooltip
|
|
13
|
-
export const ARROW_HEIGHT = 20; // borderTopWidth of arrow
|
|
14
|
-
export const SPACING_GAP = 10; // Gap between arrow tip and target element
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Calculate the center Y position of the screen
|
|
18
|
-
* This is used to vertically center the onboarding UI
|
|
19
|
-
*
|
|
20
|
-
* @returns Y coordinate at the center of the screen
|
|
21
|
-
*/
|
|
22
|
-
export function getScreenCenterY() {
|
|
23
|
-
const {
|
|
24
|
-
height: screenHeight
|
|
25
|
-
} = Dimensions.get("window");
|
|
26
|
-
return screenHeight / 2;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Calculate the tooltip position to center it vertically on screen
|
|
31
|
-
* Uses flexbox-style centering - tooltip will center itself with its actual size
|
|
32
|
-
*
|
|
33
|
-
* @returns Distance from screen bottom for tooltip positioning
|
|
34
|
-
*/
|
|
35
|
-
export function calculateTooltipPosition() {
|
|
36
|
-
// Position from center - tooltip will be centered with transform or flex
|
|
37
|
-
// We return a value that's slightly above center to account for the arrow below
|
|
38
|
-
const centerY = getScreenCenterY();
|
|
39
|
-
|
|
40
|
-
// Approximate offset to center the tooltip+arrow combo
|
|
41
|
-
// This is a reasonable estimate that works for most tooltip sizes
|
|
42
|
-
const estimatedHalfHeight = 130; // ~half of (tooltip + arrow)
|
|
43
|
-
|
|
44
|
-
return centerY - estimatedHalfHeight;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Calculate the position where a target element should be placed
|
|
49
|
-
* to align directly under the tooltip arrow
|
|
50
|
-
*
|
|
51
|
-
* @returns Distance from screen bottom where element should be positioned
|
|
52
|
-
*/
|
|
53
|
-
export function calculateTargetPosition() {
|
|
54
|
-
const tooltipBottom = calculateTooltipPosition();
|
|
55
|
-
|
|
56
|
-
// Arrow tip position = tooltip bottom + arrow offset (arrow extends below, so negative)
|
|
57
|
-
const arrowTipFromBottom = tooltipBottom + ARROW_BOTTOM_OFFSET;
|
|
58
|
-
|
|
59
|
-
// Subtract arrow height to get to the actual tip
|
|
60
|
-
const actualArrowTip = arrowTipFromBottom - ARROW_HEIGHT;
|
|
61
|
-
|
|
62
|
-
// Subtract spacing gap to position element below arrow
|
|
63
|
-
return actualArrowTip - SPACING_GAP;
|
|
64
|
-
}
|
|
3
|
+
export { ARROW_BOTTOM_OFFSET, ARROW_HEIGHT, SPACING_GAP, getScreenCenterY, calculateTooltipPosition, calculateTargetPosition } from "@buoy-gg/shared-ui";
|
|
@@ -1,27 +1,3 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
* Web twin of onboardingConstants.ts — same math, window.innerHeight instead
|
|
5
|
-
* of RN Dimensions.
|
|
6
|
-
*/
|
|
7
|
-
export const ARROW_BOTTOM_OFFSET = -40; // Arrow extends below tooltip
|
|
8
|
-
export const ARROW_HEIGHT = 20; // borderTopWidth of arrow
|
|
9
|
-
export const SPACING_GAP = 10; // Gap between arrow tip and target element
|
|
10
|
-
|
|
11
|
-
export function getScreenCenterY() {
|
|
12
|
-
return window.innerHeight / 2;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/** Distance from screen bottom for tooltip positioning (centered-ish). */
|
|
16
|
-
export function calculateTooltipPosition() {
|
|
17
|
-
const estimatedHalfHeight = 130; // ~half of (tooltip + arrow)
|
|
18
|
-
return getScreenCenterY() - estimatedHalfHeight;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Distance from screen bottom where the bubble should sit (under the arrow). */
|
|
22
|
-
export function calculateTargetPosition() {
|
|
23
|
-
const tooltipBottom = calculateTooltipPosition();
|
|
24
|
-
const arrowTipFromBottom = tooltipBottom + ARROW_BOTTOM_OFFSET;
|
|
25
|
-
const actualArrowTip = arrowTipFromBottom - ARROW_HEIGHT;
|
|
26
|
-
return actualArrowTip - SPACING_GAP;
|
|
27
|
-
}
|
|
3
|
+
export { ARROW_BOTTOM_OFFSET, ARROW_HEIGHT, SPACING_GAP, getScreenCenterY, calculateTooltipPosition, calculateTargetPosition } from "@buoy-gg/shared-ui/floating";
|