@buoy-gg/core 7.0.15 → 7.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/lib/commonjs/floatingMenu/AppHost.web.js +359 -0
  2. package/lib/commonjs/floatingMenu/AppHostLogic.js +6 -0
  3. package/lib/commonjs/floatingMenu/DevToolsSettingsModal.web.js +787 -418
  4. package/lib/commonjs/floatingMenu/FloatingDevTools.js +1 -1
  5. package/lib/commonjs/floatingMenu/FloatingDevTools.web.js +244 -36
  6. package/lib/commonjs/floatingMenu/FloatingMenu.js +2 -2
  7. package/lib/commonjs/floatingMenu/MinimizedToolsContext.js +2 -3
  8. package/lib/commonjs/floatingMenu/MinimizedToolsStack.web.js +287 -0
  9. package/lib/commonjs/floatingMenu/autoDiscoverPresets.js +16 -0
  10. package/lib/commonjs/floatingMenu/autoExternalSync.js +9 -0
  11. package/lib/commonjs/floatingMenu/dial/DialDevTools.js +7 -4
  12. package/lib/commonjs/floatingMenu/dial/DialDevTools.web.js +373 -231
  13. package/lib/commonjs/floatingMenu/dial/DialIcon.js +25 -3
  14. package/lib/commonjs/floatingMenu/dial/OnboardingTooltip.js +1 -1
  15. package/lib/commonjs/floatingMenu/dial/OnboardingTooltip.web.js +154 -0
  16. package/lib/commonjs/floatingMenu/dial/onboardingConstants.web.js +34 -0
  17. package/lib/commonjs/floatingMenu/floatingTools.js +3 -3
  18. package/lib/commonjs/floatingMenu/floatingTools.web.js +191 -28
  19. package/lib/commonjs/index.js +2 -2
  20. package/lib/commonjs/index.web.js +19 -0
  21. package/lib/commonjs/utils/autoDiscoverPresets.web.js +50 -5
  22. package/lib/module/floatingMenu/AppHost.web.js +351 -0
  23. package/lib/module/floatingMenu/AppHostLogic.js +6 -0
  24. package/lib/module/floatingMenu/DevToolsSettingsModal.web.js +792 -422
  25. package/lib/module/floatingMenu/FloatingDevTools.js +2 -2
  26. package/lib/module/floatingMenu/FloatingDevTools.web.js +247 -38
  27. package/lib/module/floatingMenu/FloatingMenu.js +2 -2
  28. package/lib/module/floatingMenu/MinimizedToolsContext.js +2 -3
  29. package/lib/module/floatingMenu/MinimizedToolsStack.web.js +284 -0
  30. package/lib/module/floatingMenu/autoDiscoverPresets.js +16 -0
  31. package/lib/module/floatingMenu/autoExternalSync.js +9 -0
  32. package/lib/module/floatingMenu/dial/DialDevTools.js +8 -5
  33. package/lib/module/floatingMenu/dial/DialDevTools.web.js +375 -233
  34. package/lib/module/floatingMenu/dial/DialIcon.js +26 -4
  35. package/lib/module/floatingMenu/dial/OnboardingTooltip.js +1 -1
  36. package/lib/module/floatingMenu/dial/OnboardingTooltip.web.js +149 -0
  37. package/lib/module/floatingMenu/dial/onboardingConstants.web.js +27 -0
  38. package/lib/module/floatingMenu/floatingTools.js +3 -3
  39. package/lib/module/floatingMenu/floatingTools.web.js +190 -28
  40. package/lib/module/index.js +2 -2
  41. package/lib/module/index.web.js +5 -0
  42. package/lib/module/utils/autoDiscoverPresets.web.js +53 -7
  43. package/lib/typescript/commonjs/floatingMenu/AppHost.web.d.ts +38 -0
  44. package/lib/typescript/commonjs/floatingMenu/AppHost.web.d.ts.map +1 -0
  45. package/lib/typescript/commonjs/floatingMenu/AppHostLogic.d.ts +21 -1
  46. package/lib/typescript/commonjs/floatingMenu/AppHostLogic.d.ts.map +1 -1
  47. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.web.d.ts +5 -2
  48. package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.web.d.ts.map +1 -1
  49. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts +2 -14
  50. package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
  51. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsContext.d.ts +2 -20
  52. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsContext.d.ts.map +1 -1
  53. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.web.d.ts +21 -0
  54. package/lib/typescript/commonjs/floatingMenu/MinimizedToolsStack.web.d.ts.map +1 -0
  55. package/lib/typescript/commonjs/floatingMenu/autoDiscoverPresets.d.ts.map +1 -1
  56. package/lib/typescript/commonjs/floatingMenu/autoExternalSync.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  58. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.web.d.ts +16 -2
  59. package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.web.d.ts.map +1 -1
  60. package/lib/typescript/commonjs/floatingMenu/dial/DialIcon.d.ts.map +1 -1
  61. package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.web.d.ts +9 -0
  62. package/lib/typescript/commonjs/floatingMenu/dial/OnboardingTooltip.web.d.ts.map +1 -0
  63. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.web.d.ts +13 -0
  64. package/lib/typescript/commonjs/floatingMenu/dial/onboardingConstants.web.d.ts.map +1 -0
  65. package/lib/typescript/commonjs/floatingMenu/floatingTools.web.d.ts +29 -2
  66. package/lib/typescript/commonjs/floatingMenu/floatingTools.web.d.ts.map +1 -1
  67. package/lib/typescript/commonjs/index.web.d.ts +1 -0
  68. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  69. package/lib/typescript/commonjs/utils/autoDiscoverPresets.web.d.ts +2 -0
  70. package/lib/typescript/commonjs/utils/autoDiscoverPresets.web.d.ts.map +1 -1
  71. package/lib/typescript/module/floatingMenu/AppHost.web.d.ts +38 -0
  72. package/lib/typescript/module/floatingMenu/AppHost.web.d.ts.map +1 -0
  73. package/lib/typescript/module/floatingMenu/AppHostLogic.d.ts +21 -1
  74. package/lib/typescript/module/floatingMenu/AppHostLogic.d.ts.map +1 -1
  75. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.web.d.ts +5 -2
  76. package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.web.d.ts.map +1 -1
  77. package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts +2 -14
  78. package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
  79. package/lib/typescript/module/floatingMenu/MinimizedToolsContext.d.ts +2 -20
  80. package/lib/typescript/module/floatingMenu/MinimizedToolsContext.d.ts.map +1 -1
  81. package/lib/typescript/module/floatingMenu/MinimizedToolsStack.web.d.ts +21 -0
  82. package/lib/typescript/module/floatingMenu/MinimizedToolsStack.web.d.ts.map +1 -0
  83. package/lib/typescript/module/floatingMenu/autoDiscoverPresets.d.ts.map +1 -1
  84. package/lib/typescript/module/floatingMenu/autoExternalSync.d.ts.map +1 -1
  85. package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
  86. package/lib/typescript/module/floatingMenu/dial/DialDevTools.web.d.ts +16 -2
  87. package/lib/typescript/module/floatingMenu/dial/DialDevTools.web.d.ts.map +1 -1
  88. package/lib/typescript/module/floatingMenu/dial/DialIcon.d.ts.map +1 -1
  89. package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.web.d.ts +9 -0
  90. package/lib/typescript/module/floatingMenu/dial/OnboardingTooltip.web.d.ts.map +1 -0
  91. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.web.d.ts +13 -0
  92. package/lib/typescript/module/floatingMenu/dial/onboardingConstants.web.d.ts.map +1 -0
  93. package/lib/typescript/module/floatingMenu/floatingTools.web.d.ts +29 -2
  94. package/lib/typescript/module/floatingMenu/floatingTools.web.d.ts.map +1 -1
  95. package/lib/typescript/module/index.web.d.ts +1 -0
  96. package/lib/typescript/module/index.web.d.ts.map +1 -1
  97. package/lib/typescript/module/utils/autoDiscoverPresets.web.d.ts +2 -0
  98. package/lib/typescript/module/utils/autoDiscoverPresets.web.d.ts.map +1 -1
  99. package/package.json +5 -5
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.DialMenu = DialMenu;
7
7
  var _react = require("react");
8
8
  var _floatingToolsReact = require("@buoy-gg/floating-tools-react");
9
+ var _sharedUi = require("@buoy-gg/shared-ui");
10
+ var _OnboardingTooltipWeb = require("./OnboardingTooltip.web.js");
9
11
  var _jsxRuntime = require("react/jsx-runtime");
10
12
  /**
11
13
  * DialDevTools - Web renderer for the radial dial menu.
@@ -13,11 +15,15 @@ var _jsxRuntime = require("react/jsx-runtime");
13
15
  * Uses shared layout calculations, styling, and animation config from @buoy-gg/floating-tools-core.
14
16
  * Platform-specific: CSS animations, mouse events, web JSX.
15
17
  *
16
- * Animation behavior matches mobile exactly - see docs/dial-animation-spec.md
18
+ * Visuals mirror the native DialDevTools.tsx exactly — values that never made
19
+ * it into the shared dial.ts are inlined here with provenance comments.
17
20
  *
18
21
  * @platform React DOM Web (Vite, CRA, Next.js, Electron)
19
22
  */
20
23
 
24
+ // native DialDevTools.tsx SETTINGS_TOOLTIP_KEY
25
+ const SETTINGS_TOOLTIP_KEY = '@react_buoy_settings_tooltip_shown';
26
+
21
27
  // Inject CSS keyframes once on module load
22
28
  let keyframesInjected = false;
23
29
  function injectKeyframes() {
@@ -51,6 +57,8 @@ function DialIconItem({
51
57
  const [isHovered, setIsHovered] = (0, _react.useState)(false);
52
58
  const [isPressed, setIsPressed] = (0, _react.useState)(false);
53
59
  const isEmpty = !icon.icon && icon.id.startsWith('empty-');
60
+ const isUnavailable = !isEmpty && icon.unavailable === true;
61
+ const interactive = !isEmpty && !isUnavailable;
54
62
  const {
55
63
  icons: iconConfig
56
64
  } = _floatingToolsReact.dialAnimationConfig;
@@ -68,12 +76,13 @@ function DialIconItem({
68
76
  iconProgress = 1;
69
77
  }
70
78
  iconProgress = Math.max(0, Math.min(1, iconProgress));
71
- const iconSize = 60;
79
+ const iconSize = _floatingToolsReact.DIAL_ICON_SIZE;
72
80
  const pressedScale = iconConfig.pressIn.scale;
73
- const hoveredScale = 1.1;
81
+ const hoveredScale = 1.1; // web-only hover affordance (no native counterpart)
82
+
74
83
  const scale = iconProgress * (isPressed ? pressedScale : isHovered ? hoveredScale : 1);
75
84
 
76
- // Opacity interpolation
85
+ // Opacity interpolation — native DialIcon.tsx: [0, 0.3, 1] -> [0, 0.3, 1]
77
86
  let opacity = 0;
78
87
  if (iconProgress <= 0) {
79
88
  opacity = 0;
@@ -87,15 +96,15 @@ function DialIconItem({
87
96
  return /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
88
97
  role: "menuitem",
89
98
  "aria-label": isEmpty ? 'Empty slot' : icon.name,
90
- "aria-disabled": isEmpty,
91
- tabIndex: isEmpty ? -1 : 0,
92
- onClick: isEmpty ? undefined : onPress,
93
- onMouseEnter: () => !isEmpty && setIsHovered(true),
99
+ "aria-disabled": !interactive,
100
+ tabIndex: interactive ? 0 : -1,
101
+ onClick: interactive ? onPress : undefined,
102
+ onMouseEnter: () => interactive && setIsHovered(true),
94
103
  onMouseLeave: () => {
95
104
  setIsHovered(false);
96
105
  setIsPressed(false);
97
106
  },
98
- onMouseDown: () => !isEmpty && setIsPressed(true),
107
+ onMouseDown: () => interactive && setIsPressed(true),
99
108
  onMouseUp: () => setIsPressed(false),
100
109
  style: {
101
110
  position: 'absolute',
@@ -109,7 +118,7 @@ function DialIconItem({
109
118
  flexDirection: 'column',
110
119
  alignItems: 'center',
111
120
  justifyContent: 'center',
112
- cursor: isEmpty ? 'default' : 'pointer',
121
+ cursor: interactive ? 'pointer' : 'default',
113
122
  transform: `translate(${x}px, ${y}px) scale(${scale})`,
114
123
  opacity,
115
124
  transition: isHovered || isPressed ? 'transform 100ms ease' : 'none',
@@ -151,25 +160,130 @@ function DialIconItem({
151
160
  alignItems: 'center',
152
161
  justifyContent: 'center',
153
162
  fontSize: 32,
163
+ // native DialIcon.tsx: icon rendered at size 32
154
164
  filter: isHovered ? 'brightness(1.2)' : 'none'
155
165
  },
156
166
  children: icon.icon
157
167
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
158
168
  style: {
159
- fontSize: _floatingToolsReact.dialStyles.icon.label.fontSize,
169
+ fontSize:
170
+ // native: adjustsFontSizeToFit minimumFontScale 0.7 — long
171
+ // names shrink instead of wrapping/overflowing the slot.
172
+ icon.name.length > 8 ? Math.max(_floatingToolsReact.dialStyles.icon.label.fontSize * 0.7, _floatingToolsReact.dialStyles.icon.label.fontSize * 8 / icon.name.length) : _floatingToolsReact.dialStyles.icon.label.fontSize,
160
173
  fontWeight: _floatingToolsReact.dialStyles.icon.label.fontWeight,
161
174
  fontFamily: _floatingToolsReact.dialStyles.icon.label.fontFamily,
162
175
  color: _floatingToolsReact.dialColors.iconLabel,
163
176
  letterSpacing: _floatingToolsReact.dialStyles.icon.label.letterSpacing,
164
177
  marginTop: _floatingToolsReact.dialStyles.icon.label.marginTop,
165
- textTransform: 'uppercase'
178
+ textTransform: 'uppercase',
179
+ whiteSpace: 'nowrap'
166
180
  },
167
181
  children: icon.name
182
+ }), isUnavailable &&
183
+ /*#__PURE__*/
184
+ // native DialIcon.tsx unavailableCaption: label size - 1, muted
185
+ (0, _jsxRuntime.jsx)("span", {
186
+ style: {
187
+ fontSize: _floatingToolsReact.dialStyles.icon.label.fontSize - 1,
188
+ fontWeight: _floatingToolsReact.dialStyles.icon.label.fontWeight,
189
+ fontFamily: _floatingToolsReact.dialStyles.icon.label.fontFamily,
190
+ letterSpacing: _floatingToolsReact.dialStyles.icon.label.letterSpacing,
191
+ color: _floatingToolsReact.floatingToolsColors.muted,
192
+ textTransform: 'uppercase',
193
+ whiteSpace: 'nowrap'
194
+ },
195
+ children: "Unavailable"
168
196
  })]
169
197
  })
170
198
  });
171
199
  }
172
200
 
201
+ // =============================
202
+ // Pagination (mirrors native DialPagination.tsx)
203
+ // =============================
204
+
205
+ /**
206
+ * Chevron glyph matching the native pager's ChevronLeft/ChevronRight from
207
+ * @buoy-gg/shared-ui (lucide two-line chevron, size 18, strokeWidth 2).
208
+ * Inlined as SVG because shared-ui is React Native-only.
209
+ */
210
+ function PagerChevron({
211
+ side,
212
+ color
213
+ }) {
214
+ // native lucide-icons.tsx ChevronLeft: (15,18)->(9,12)->(15,6); ChevronRight mirrored
215
+ const lines = side === 'prev' ? [[15, 18, 9, 12], [9, 12, 15, 6]] : [[9, 18, 15, 12], [15, 12, 9, 6]];
216
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)("svg", {
217
+ width: 18,
218
+ height: 18,
219
+ viewBox: "0 0 24 24",
220
+ "aria-hidden": "true",
221
+ focusable: "false",
222
+ children: lines.map(([x1, y1, x2, y2], i) => /*#__PURE__*/(0, _jsxRuntime.jsx)("line", {
223
+ x1: x1,
224
+ y1: y1,
225
+ x2: x2,
226
+ y2: y2,
227
+ stroke: color,
228
+ strokeWidth: 2
229
+ }, i))
230
+ });
231
+ }
232
+ function PagerButton({
233
+ side,
234
+ disabled,
235
+ onClick
236
+ }) {
237
+ const [isPressed, setIsPressed] = (0, _react.useState)(false);
238
+
239
+ // native DialPagination.tsx: accent = disabled ? emptyDotBorder : buoyColors.primary
240
+ // (buoyColors.primary === floatingToolsColors.info === dialColors.dialShadow, #20C997)
241
+ const accent = disabled ? _floatingToolsReact.dialColors.emptyDotBorder : _floatingToolsReact.dialColors.dialShadow;
242
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)("button", {
243
+ type: "button",
244
+ "aria-label": `${side === 'prev' ? 'Previous' : 'Next'} dial page`,
245
+ disabled: disabled,
246
+ onClick: onClick,
247
+ onMouseDown: () => !disabled && setIsPressed(true),
248
+ onMouseUp: () => setIsPressed(false),
249
+ onMouseLeave: () => setIsPressed(false),
250
+ style: {
251
+ display: 'flex',
252
+ alignItems: 'center',
253
+ gap: 4,
254
+ // native DialPagination.tsx: gap 4
255
+ padding: '10px 16px',
256
+ // native: paddingVertical 10, paddingHorizontal 16
257
+ borderRadius: 10,
258
+ border: `1px solid ${_floatingToolsReact.dialColors.dialBorder}`,
259
+ backgroundColor: _floatingToolsReact.dialColors.dialBackground,
260
+ color: accent,
261
+ fontSize: 12,
262
+ fontWeight: 900,
263
+ fontFamily: 'monospace',
264
+ letterSpacing: 1.5,
265
+ textTransform: 'uppercase',
266
+ // native: textShadow buoyColors.primary radius 4 (removed when disabled)
267
+ textShadow: disabled ? 'none' : `0 0 4px ${accent}`,
268
+ cursor: disabled ? 'default' : 'pointer',
269
+ opacity: disabled ? 0.4 : 1,
270
+ // native: buttonDisabled opacity 0.4
271
+ // native: shadowColor dialShadow, shadowOpacity 0.4, shadowRadius 8
272
+ boxShadow: disabled ? 'none' : `0 0 8px ${(0, _floatingToolsReact.withAlpha)(_floatingToolsReact.dialColors.dialShadow, 0.4)}`,
273
+ // native: press-in springs to scale 0.92
274
+ transform: isPressed ? 'scale(0.92)' : 'scale(1)',
275
+ transition: 'transform 100ms ease'
276
+ },
277
+ children: [side === 'prev' && /*#__PURE__*/(0, _jsxRuntime.jsx)(PagerChevron, {
278
+ side: "prev",
279
+ color: accent
280
+ }), side === 'prev' ? 'PREV' : 'NEXT', side === 'next' && /*#__PURE__*/(0, _jsxRuntime.jsx)(PagerChevron, {
281
+ side: "next",
282
+ color: accent
283
+ })]
284
+ });
285
+ }
286
+
173
287
  // =============================
174
288
  // Main DialMenu Component
175
289
  // =============================
@@ -178,7 +292,9 @@ function DialMenu({
178
292
  icons,
179
293
  onClose,
180
294
  centerLabel = 'MENU',
181
- onCenterPress
295
+ onCenterPress,
296
+ settingsOpen = false,
297
+ hintsDisabled = false
182
298
  }) {
183
299
  // Animation values
184
300
  const [backdropOpacity, setBackdropOpacity] = (0, _react.useState)(0);
@@ -190,20 +306,17 @@ function DialMenu({
190
306
 
191
307
  // UI state
192
308
  const [isHoveringCenter, setIsHoveringCenter] = (0, _react.useState)(false);
193
- const [triggerGlitch, setTriggerGlitch] = (0, _react.useState)(0);
194
309
  const [isExiting, setIsExiting] = (0, _react.useState)(false);
195
310
  const [entranceComplete, setEntranceComplete] = (0, _react.useState)(false);
196
311
 
197
312
  // Refs
198
313
  const animationRef = (0, _react.useRef)(null);
199
- const glitchIntervalRef = (0, _react.useRef)(null);
200
314
  const isClosingRef = (0, _react.useRef)(false);
201
315
 
202
316
  // Config
203
317
  const {
204
318
  entrance,
205
319
  exit,
206
- continuous,
207
320
  interaction
208
321
  } = _floatingToolsReact.dialAnimationConfig;
209
322
  const cssAnimations = (0, _floatingToolsReact.getDialCSSAnimationStyles)();
@@ -246,7 +359,6 @@ function DialMenu({
246
359
  const centerButtonDelay = entrance.centerButton.delay;
247
360
  const iconsDelay = entrance.icons.delay;
248
361
  const iconsDuration = entrance.icons.duration;
249
- const glitchInterval = continuous.glitch.interval;
250
362
  const startTime = performance.now();
251
363
  const animate = currentTime => {
252
364
  const elapsed = currentTime - startTime;
@@ -299,14 +411,8 @@ function DialMenu({
299
411
  }
300
412
  };
301
413
  animationRef.current = requestAnimationFrame(animate);
302
-
303
- // Glitch interval
304
- glitchIntervalRef.current = setInterval(() => {
305
- setTriggerGlitch(prev => prev + 1);
306
- }, glitchInterval);
307
414
  return () => {
308
415
  if (animationRef.current) cancelAnimationFrame(animationRef.current);
309
- if (glitchIntervalRef.current) clearInterval(glitchIntervalRef.current);
310
416
  };
311
417
  }, []);
312
418
 
@@ -315,16 +421,14 @@ function DialMenu({
315
421
  if (isClosingRef.current) return;
316
422
  isClosingRef.current = true;
317
423
  setIsExiting(true);
318
- if (glitchIntervalRef.current) {
319
- clearInterval(glitchIntervalRef.current);
320
- glitchIntervalRef.current = null;
321
- }
322
424
  if (animationRef.current) {
323
425
  cancelAnimationFrame(animationRef.current);
324
426
  }
325
427
  const startTime = performance.now();
326
428
  const iconsDuration = exit.icons.duration;
327
- const dialDuration = Math.max(exit.centerButton.duration, exit.dialScale.duration);
429
+ const centerDuration = exit.centerButton.duration;
430
+ const dialDuration = exit.dialScale.duration;
431
+ const step2Duration = Math.max(centerDuration, dialDuration);
328
432
  const backdropDuration = exit.backdrop.duration;
329
433
  const animate = currentTime => {
330
434
  const elapsed = currentTime - startTime;
@@ -337,19 +441,21 @@ function DialMenu({
337
441
  return;
338
442
  }
339
443
 
340
- // Step 2: Dial + Center scale down (300 - 550ms)
444
+ // Step 2: Dial + Center scale down in parallel — native runs the center
445
+ // button (200ms) and dial (250ms) with their own durations.
341
446
  const step2Start = iconsDuration;
342
- if (elapsed < step2Start + dialDuration) {
447
+ if (elapsed < step2Start + step2Duration) {
343
448
  setIconProgress(0);
344
- const progress = easeInCubic((elapsed - step2Start) / dialDuration);
345
- setDialScale(1 - progress);
346
- setCenterButtonScale(1 - progress);
449
+ const centerProgress = Math.min(1, (elapsed - step2Start) / centerDuration);
450
+ const dialProgress = Math.min(1, (elapsed - step2Start) / dialDuration);
451
+ setCenterButtonScale(1 - easeInCubic(centerProgress));
452
+ setDialScale(1 - easeInCubic(dialProgress));
347
453
  animationRef.current = requestAnimationFrame(animate);
348
454
  return;
349
455
  }
350
456
 
351
457
  // Step 3: Backdrop fade (550 - 750ms)
352
- const step3Start = step2Start + dialDuration;
458
+ const step3Start = step2Start + step2Duration;
353
459
  if (elapsed < step3Start + backdropDuration) {
354
460
  setDialScale(0);
355
461
  setCenterButtonScale(0);
@@ -377,6 +483,33 @@ function DialMenu({
377
483
  return () => document.removeEventListener('keydown', handleKeyDown);
378
484
  }, [handleClose]);
379
485
 
486
+ // Settings onboarding tooltip — native DialDevTools.tsx:174-196: shown
487
+ // 1200ms after the dial mounts (entrance animations settle first) if never
488
+ // dismissed; suppressed while settings is open; dismissed forever when
489
+ // settings opens.
490
+ const [showSettingsTooltip, setShowSettingsTooltip] = (0, _react.useState)(false);
491
+ (0, _react.useEffect)(() => {
492
+ if (hintsDisabled) return;
493
+ let cancelled = false;
494
+ const timer = setTimeout(async () => {
495
+ const seen = await _sharedUi.persistentStorage.getItem(SETTINGS_TOOLTIP_KEY);
496
+ if (!cancelled && !seen) setShowSettingsTooltip(true);
497
+ }, 1200);
498
+ return () => {
499
+ cancelled = true;
500
+ clearTimeout(timer);
501
+ };
502
+ }, [hintsDisabled]);
503
+ const dismissSettingsTooltip = (0, _react.useCallback)(() => {
504
+ setShowSettingsTooltip(false);
505
+ void _sharedUi.persistentStorage.setItem(SETTINGS_TOOLTIP_KEY, 'true');
506
+ }, []);
507
+ (0, _react.useEffect)(() => {
508
+ if (settingsOpen && showSettingsTooltip) {
509
+ dismissSettingsTooltip();
510
+ }
511
+ }, [settingsOpen, showSettingsTooltip, dismissSettingsTooltip]);
512
+
380
513
  // Icon press handler
381
514
  const handleIconPress = (0, _react.useCallback)(icon => {
382
515
  if (icon.id.startsWith('empty-') || isClosingRef.current) return;
@@ -421,24 +554,6 @@ function DialMenu({
421
554
  const buttonContainerSize = layout.buttonSize * _floatingToolsReact.dialStyles.centerButton.containerRatio;
422
555
  const buttonBorderSize = layout.buttonSize * _floatingToolsReact.dialStyles.centerButton.borderRatio;
423
556
  const isAnimating = entranceComplete && !isExiting;
424
- const pagerButtonStyle = disabled => ({
425
- display: 'flex',
426
- alignItems: 'center',
427
- gap: 4,
428
- padding: '8px 16px',
429
- borderRadius: 10,
430
- border: `1px solid ${_floatingToolsReact.dialColors.dialBorder}`,
431
- backgroundColor: _floatingToolsReact.dialColors.dialBackground,
432
- color: disabled ? _floatingToolsReact.dialColors.emptyDotBorder : _floatingToolsReact.dialColors.dialShadow,
433
- fontSize: 12,
434
- fontWeight: 900,
435
- fontFamily: 'monospace',
436
- letterSpacing: 1.5,
437
- textTransform: 'uppercase',
438
- cursor: disabled ? 'default' : 'pointer',
439
- opacity: disabled ? 0.4 : 1,
440
- boxShadow: disabled ? 'none' : `0 0 8px ${_floatingToolsReact.dialColors.dialShadow}66`
441
- });
442
557
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
443
558
  role: "dialog",
444
559
  "aria-label": "Dial Menu",
@@ -446,10 +561,7 @@ function DialMenu({
446
561
  style: {
447
562
  position: 'fixed',
448
563
  inset: 0,
449
- zIndex: 10000,
450
- display: 'flex',
451
- alignItems: 'center',
452
- justifyContent: 'center'
564
+ zIndex: 10000
453
565
  },
454
566
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
455
567
  role: "button",
@@ -465,204 +577,234 @@ function DialMenu({
465
577
  }
466
578
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
467
579
  style: {
468
- animation: isAnimating ? cssAnimations.floating : 'none',
469
- display: 'flex',
470
- flexDirection: 'column',
471
- alignItems: 'center',
472
- gap: 16
580
+ position: 'absolute',
581
+ left: '50%',
582
+ marginLeft: -layout.circleSize / 2,
583
+ bottom: 80,
584
+ // native DialDevTools.tsx: bottom: 80
585
+ width: layout.circleSize,
586
+ height: layout.circleSize,
587
+ borderRadius: '50%',
588
+ backgroundColor: _floatingToolsReact.dialColors.dialBackground,
589
+ border: `${_floatingToolsReact.dialStyles.circle.borderWidth}px solid ${_floatingToolsReact.dialColors.dialBorder}`,
590
+ // native: shadowColor dialShadow at shadowOpacity 0.5, shadowRadius 20
591
+ boxShadow: `0 0 ${_floatingToolsReact.dialStyles.circle.shadowRadius}px ${(0, _floatingToolsReact.withAlpha)(_floatingToolsReact.dialColors.dialShadow, _floatingToolsReact.dialStyles.circle.shadowOpacity)}`,
592
+ transform: `scale(${dialScale}) rotate(${dialRotation}deg)`,
593
+ opacity: dialScale > 0.01 ? 1 : 0,
594
+ overflow: 'hidden'
473
595
  },
474
596
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
475
597
  style: {
476
- animation: triggerGlitch > 0 && isAnimating ? cssAnimations.glitch : 'none'
598
+ position: 'absolute',
599
+ inset: 0,
600
+ borderRadius: '50%',
601
+ backgroundColor: _floatingToolsReact.dialColors.dialGradient1,
602
+ opacity: _floatingToolsReact.dialStyles.gradientLayers[0].opacity
603
+ }
604
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
605
+ style: {
606
+ position: 'absolute',
607
+ inset: 0,
608
+ top: '30%',
609
+ left: '30%',
610
+ borderRadius: '50%',
611
+ backgroundColor: _floatingToolsReact.dialColors.dialGradient2,
612
+ opacity: _floatingToolsReact.dialStyles.gradientLayers[1].opacity
613
+ }
614
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
615
+ style: {
616
+ position: 'absolute',
617
+ inset: 0,
618
+ top: '50%',
619
+ left: '50%',
620
+ borderRadius: '50%',
621
+ backgroundColor: _floatingToolsReact.dialColors.dialGradient3,
622
+ opacity: _floatingToolsReact.dialStyles.gradientLayers[2].opacity
623
+ }
624
+ }), gridRotations.map(deg => /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
625
+ style: {
626
+ position: 'absolute',
627
+ top: '50%',
628
+ left: 0,
629
+ right: 0,
630
+ height: _floatingToolsReact.dialStyles.grid.lineHeight,
631
+ backgroundColor: _floatingToolsReact.dialColors.dialGridLine,
632
+ transform: `rotate(${deg}deg)`,
633
+ transformOrigin: 'center'
634
+ }
635
+ }, deg)), paddedIcons.map((icon, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(DialIconItem, {
636
+ icon: icon,
637
+ index: index,
638
+ totalIcons: paddedIcons.length,
639
+ position: positions[index],
640
+ progress: iconProgress,
641
+ onPress: () => handleIconPress(icon)
642
+ }, index)), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
643
+ style: {
644
+ position: 'absolute',
645
+ left: '50%',
646
+ top: '50%',
647
+ width: buttonContainerSize,
648
+ height: buttonContainerSize,
649
+ marginLeft: -buttonContainerSize / 2,
650
+ marginTop: -buttonContainerSize / 2,
651
+ display: 'flex',
652
+ alignItems: 'center',
653
+ justifyContent: 'center',
654
+ backgroundColor: _floatingToolsReact.dialColors.dialBackground,
655
+ borderRadius: layout.buttonSize,
656
+ overflow: 'hidden',
657
+ transform: `scale(${centerButtonScale * centerPulseScale})`,
658
+ opacity: centerButtonScale > 0.01 ? 1 : 0
477
659
  },
478
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
660
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
661
+ style: {
662
+ position: 'absolute',
663
+ inset: 0,
664
+ backgroundColor: _floatingToolsReact.dialColors.dialGradient1,
665
+ opacity: 0.5,
666
+ borderRadius: layout.buttonSize
667
+ }
668
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
669
+ style: {
670
+ position: 'absolute',
671
+ inset: 0,
672
+ top: '20%',
673
+ left: '20%',
674
+ backgroundColor: _floatingToolsReact.dialColors.dialGradient2,
675
+ opacity: 0.3,
676
+ borderRadius: layout.buttonSize
677
+ }
678
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
479
679
  style: {
480
- position: 'relative',
481
- width: layout.circleSize,
482
- height: layout.circleSize,
483
- borderRadius: '50%',
484
- backgroundColor: _floatingToolsReact.dialColors.dialBackground,
485
- border: `${_floatingToolsReact.dialStyles.circle.borderWidth}px solid ${_floatingToolsReact.dialColors.dialBorder}`,
486
- boxShadow: `0 0 ${_floatingToolsReact.dialStyles.circle.shadowRadius}px ${_floatingToolsReact.dialColors.dialShadow}`,
487
- transform: `scale(${dialScale}) rotate(${dialRotation}deg)`,
488
- opacity: dialScale > 0.01 ? 1 : 0,
489
- overflow: 'hidden'
680
+ position: 'absolute',
681
+ inset: 0,
682
+ top: '40%',
683
+ left: '40%',
684
+ backgroundColor: _floatingToolsReact.dialColors.dialGradient3,
685
+ opacity: 0.2,
686
+ borderRadius: layout.buttonSize
687
+ }
688
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
689
+ style: {
690
+ width: buttonBorderSize,
691
+ height: buttonBorderSize,
692
+ borderRadius: layout.buttonSize * _floatingToolsReact.dialStyles.centerButton.borderRadiusRatio,
693
+ backgroundColor: _floatingToolsReact.dialColors.dialGridLine,
694
+ border: `${_floatingToolsReact.dialStyles.centerButton.borderWidth}px solid ${_floatingToolsReact.dialColors.dialBorder}`,
695
+ display: 'flex',
696
+ alignItems: 'center',
697
+ justifyContent: 'center'
490
698
  },
491
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
492
- style: {
493
- position: 'absolute',
494
- inset: 0,
495
- borderRadius: '50%',
496
- backgroundColor: _floatingToolsReact.dialColors.dialGradient1,
497
- opacity: _floatingToolsReact.dialStyles.gradientLayers[0].opacity
498
- }
499
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
699
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
700
+ onClick: onCenterPress,
701
+ onMouseEnter: () => setIsHoveringCenter(true),
702
+ onMouseLeave: () => setIsHoveringCenter(false),
500
703
  style: {
501
- position: 'absolute',
502
- inset: 0,
503
- top: '30%',
504
- left: '30%',
505
- borderRadius: '50%',
506
- backgroundColor: _floatingToolsReact.dialColors.dialGradient2,
507
- opacity: _floatingToolsReact.dialStyles.gradientLayers[1].opacity
508
- }
509
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
510
- style: {
511
- position: 'absolute',
512
- inset: 0,
513
- top: '50%',
514
- left: '50%',
515
- borderRadius: '50%',
516
- backgroundColor: _floatingToolsReact.dialColors.dialGradient3,
517
- opacity: _floatingToolsReact.dialStyles.gradientLayers[2].opacity
518
- }
519
- }), gridRotations.map(deg => /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
520
- style: {
521
- position: 'absolute',
522
- top: '50%',
523
- left: 0,
524
- right: 0,
525
- height: _floatingToolsReact.dialStyles.grid.lineHeight,
526
- backgroundColor: _floatingToolsReact.dialColors.dialGridLine,
527
- transform: `rotate(${deg}deg)`,
528
- transformOrigin: 'center'
529
- }
530
- }, deg)), paddedIcons.map((icon, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(DialIconItem, {
531
- icon: icon,
532
- index: index,
533
- totalIcons: paddedIcons.length,
534
- position: positions[index],
535
- progress: iconProgress,
536
- onPress: () => handleIconPress(icon)
537
- }, index)), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
538
- style: {
539
- position: 'absolute',
540
- left: '50%',
541
- top: '50%',
542
- width: buttonContainerSize,
543
- height: buttonContainerSize,
544
- marginLeft: -buttonContainerSize / 2,
545
- marginTop: -buttonContainerSize / 2,
704
+ width: layout.buttonSize,
705
+ height: layout.buttonSize,
706
+ borderRadius: layout.buttonSize / 2,
546
707
  display: 'flex',
708
+ flexDirection: 'column',
547
709
  alignItems: 'center',
548
710
  justifyContent: 'center',
549
- backgroundColor: _floatingToolsReact.dialColors.dialBackground,
550
- borderRadius: layout.buttonSize,
551
- overflow: 'hidden',
552
- transform: `scale(${centerButtonScale * centerPulseScale})`,
553
- opacity: centerButtonScale > 0.01 ? 1 : 0
711
+ cursor: onCenterPress ? 'pointer' : 'default',
712
+ transform: isHoveringCenter ? `scale(${interaction.centerHover.scale})` : 'scale(1)',
713
+ transition: `transform ${interaction.centerHover.duration}ms ease`,
714
+ animation: isAnimating && !isHoveringCenter ? cssAnimations.pulse : 'none'
554
715
  },
555
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
556
- style: {
557
- position: 'absolute',
558
- inset: 0,
559
- backgroundColor: _floatingToolsReact.dialColors.dialGradient1,
560
- opacity: 0.5,
561
- borderRadius: layout.buttonSize
562
- }
563
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
564
- style: {
565
- position: 'absolute',
566
- inset: 0,
567
- top: '20%',
568
- left: '20%',
569
- backgroundColor: _floatingToolsReact.dialColors.dialGradient2,
570
- opacity: 0.3,
571
- borderRadius: layout.buttonSize
572
- }
573
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
574
- style: {
575
- position: 'absolute',
576
- inset: 0,
577
- top: '40%',
578
- left: '40%',
579
- backgroundColor: _floatingToolsReact.dialColors.dialGradient3,
580
- opacity: 0.2,
581
- borderRadius: layout.buttonSize
582
- }
583
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
716
+ children: settingsOpen ?
717
+ /*#__PURE__*/
718
+ // native DialDevTools.tsx:716-724 — stacked CLOSE / SETTINGS
719
+ (0, _jsxRuntime.jsxs)("span", {
584
720
  style: {
585
- width: buttonBorderSize,
586
- height: buttonBorderSize,
587
- borderRadius: layout.buttonSize * _floatingToolsReact.dialStyles.centerButton.borderRadiusRatio,
588
- backgroundColor: _floatingToolsReact.dialColors.dialGridLine,
589
- border: `${_floatingToolsReact.dialStyles.centerButton.borderWidth}px solid ${_floatingToolsReact.dialColors.dialBorder}`,
590
721
  display: 'flex',
722
+ flexDirection: 'column',
591
723
  alignItems: 'center',
592
- justifyContent: 'center'
724
+ fontSize: _floatingToolsReact.dialStyles.centerButton.text.fontSize - 2,
725
+ fontWeight: _floatingToolsReact.dialStyles.centerButton.text.fontWeight,
726
+ fontFamily: _floatingToolsReact.dialStyles.centerButton.text.fontFamily,
727
+ color: '#FFFFFF',
728
+ letterSpacing: _floatingToolsReact.dialStyles.centerButton.text.letterSpacing,
729
+ textAlign: 'center',
730
+ textShadow: `0 0 4px ${_floatingToolsReact.dialColors.centerTextGlow}`,
731
+ textTransform: 'uppercase',
732
+ lineHeight: 1.2
593
733
  },
594
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
595
- onClick: onCenterPress,
596
- onMouseEnter: () => setIsHoveringCenter(true),
597
- onMouseLeave: () => setIsHoveringCenter(false),
734
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
598
735
  style: {
599
- width: layout.buttonSize,
600
- height: layout.buttonSize,
601
- borderRadius: layout.buttonSize / 2,
602
- display: 'flex',
603
- flexDirection: 'column',
604
- alignItems: 'center',
605
- justifyContent: 'center',
606
- cursor: onCenterPress ? 'pointer' : 'default',
607
- transform: isHoveringCenter ? `scale(${interaction.centerHover.scale})` : 'scale(1)',
608
- transition: `transform ${interaction.centerHover.duration}ms ease`,
609
- animation: isAnimating && !isHoveringCenter ? cssAnimations.breathing : 'none'
736
+ marginBottom: -2
610
737
  },
611
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
612
- style: {
613
- fontSize: _floatingToolsReact.dialStyles.centerButton.text.fontSize,
614
- fontWeight: _floatingToolsReact.dialStyles.centerButton.text.fontWeight,
615
- fontFamily: _floatingToolsReact.dialStyles.centerButton.text.fontFamily,
616
- color: _floatingToolsReact.dialColors.centerText,
617
- letterSpacing: _floatingToolsReact.dialStyles.centerButton.text.letterSpacing,
618
- textShadow: `0 0 4px ${_floatingToolsReact.dialColors.centerTextGlow}`,
619
- textTransform: 'uppercase'
620
- },
621
- children: centerLabel
622
- })
623
- })
624
- })]
625
- })]
626
- })
627
- }, triggerGlitch), pageCount > 1 && /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
738
+ children: "CLOSE"
739
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
740
+ style: {
741
+ marginTop: -2
742
+ },
743
+ children: "SETTINGS"
744
+ })]
745
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
746
+ style: {
747
+ fontSize: _floatingToolsReact.dialStyles.centerButton.text.fontSize,
748
+ fontWeight: _floatingToolsReact.dialStyles.centerButton.text.fontWeight,
749
+ fontFamily: _floatingToolsReact.dialStyles.centerButton.text.fontFamily,
750
+ color: '#FFFFFF',
751
+ // native DialDevTools.tsx centerText: #FFFFFF
752
+ letterSpacing: _floatingToolsReact.dialStyles.centerButton.text.letterSpacing,
753
+ textAlign: 'center',
754
+ textShadow: `0 0 4px ${_floatingToolsReact.dialColors.centerTextGlow}`,
755
+ textTransform: 'uppercase'
756
+ },
757
+ children: centerLabel
758
+ })
759
+ })
760
+ })]
761
+ })]
762
+ }), pageCount > 1 && /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
763
+ style: {
764
+ position: 'absolute',
765
+ left: '50%',
766
+ marginLeft: -layout.circleSize / 2,
767
+ top: 'calc(100% - 64px)',
768
+ // native: screenHeight - 80 + 16
769
+ width: layout.circleSize,
770
+ display: 'flex',
771
+ alignItems: 'center',
772
+ justifyContent: 'space-between',
773
+ opacity: Math.min(1, dialScale),
774
+ transform: `scale(${Math.min(1, dialScale)})`
775
+ },
776
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(PagerButton, {
777
+ side: "prev",
778
+ disabled: safePage <= 0,
779
+ onClick: () => handlePageChange(safePage - 1)
780
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", {
628
781
  style: {
629
- display: 'flex',
630
- alignItems: 'center',
631
- gap: 12,
632
- opacity: Math.min(1, dialScale)
782
+ padding: '6px 12px',
783
+ // native indicator: paddingVertical 6, paddingHorizontal 12
784
+ fontSize: 13,
785
+ fontWeight: 900,
786
+ fontFamily: 'monospace',
787
+ letterSpacing: 2,
788
+ color: '#FFFFFF',
789
+ textShadow: `0 0 6px ${_floatingToolsReact.dialColors.dialShadow}`
633
790
  },
634
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
635
- type: "button",
636
- "aria-label": "Previous dial page",
637
- disabled: safePage <= 0,
638
- onClick: () => handlePageChange(safePage - 1),
639
- style: pagerButtonStyle(safePage <= 0),
640
- children: "\u2039 PREV"
641
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", {
791
+ children: [String(safePage + 1).padStart(2, '0'), /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", {
642
792
  style: {
643
- fontSize: 13,
644
- fontWeight: 900,
645
- fontFamily: 'monospace',
646
- letterSpacing: 2,
647
- color: '#FFFFFF',
648
- textShadow: `0 0 6px ${_floatingToolsReact.dialColors.dialShadow}`
793
+ color: _floatingToolsReact.dialColors.iconLabel,
794
+ textShadow: 'none'
649
795
  },
650
- children: [String(safePage + 1).padStart(2, '0'), /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", {
651
- style: {
652
- color: _floatingToolsReact.dialColors.iconLabel,
653
- textShadow: 'none'
654
- },
655
- children: [' / ', String(pageCount).padStart(2, '0')]
656
- })]
657
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
658
- type: "button",
659
- "aria-label": "Next dial page",
660
- disabled: safePage >= pageCount - 1,
661
- onClick: () => handlePageChange(safePage + 1),
662
- style: pagerButtonStyle(safePage >= pageCount - 1),
663
- children: "NEXT \u203A"
796
+ children: [' / ', String(pageCount).padStart(2, '0')]
664
797
  })]
798
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(PagerButton, {
799
+ side: "next",
800
+ disabled: safePage >= pageCount - 1,
801
+ onClick: () => handlePageChange(safePage + 1)
665
802
  })]
803
+ }), showSettingsTooltip && !settingsOpen && /*#__PURE__*/(0, _jsxRuntime.jsx)(_OnboardingTooltipWeb.OnboardingTooltip, {
804
+ title: "Welcome to Buoy!",
805
+ message: "Tap the center button to configure and add tools to your dial menu.",
806
+ onDismiss: dismissSettingsTooltip,
807
+ showArrow: false
666
808
  })]
667
809
  });
668
810
  }