@buoy-gg/core 7.0.26 → 7.0.27
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/bin/buoy.js +6 -0
- package/lib/commonjs/floatingMenu/DevToolsSettingsModal.js +524 -1520
- package/lib/commonjs/floatingMenu/DevToolsSettingsModal.web.js +541 -566
- package/lib/commonjs/floatingMenu/FloatingDevTools.web.js +5 -1
- package/lib/commonjs/floatingMenu/ToolHost.js +2 -1
- package/lib/commonjs/floatingMenu/dial/DialDevTools.js +50 -21
- package/lib/commonjs/floatingMenu/dial/DialHyperspace.js +177 -0
- package/lib/commonjs/floatingMenu/dial/DialSnow.js +92 -0
- package/lib/commonjs/version.js +1 -1
- package/lib/module/floatingMenu/DevToolsSettingsModal.js +525 -1524
- package/lib/module/floatingMenu/DevToolsSettingsModal.web.js +542 -569
- package/lib/module/floatingMenu/FloatingDevTools.web.js +5 -1
- package/lib/module/floatingMenu/ToolHost.js +2 -1
- package/lib/module/floatingMenu/dial/DialDevTools.js +52 -23
- package/lib/module/floatingMenu/dial/DialHyperspace.js +173 -0
- package/lib/module/floatingMenu/dial/DialSnow.js +90 -0
- package/lib/module/version.js +1 -1
- package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts +19 -13
- package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.web.d.ts +3 -3
- package/lib/typescript/commonjs/floatingMenu/DevToolsSettingsModal.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
- package/lib/typescript/commonjs/floatingMenu/dial/DialHyperspace.d.ts +24 -0
- package/lib/typescript/commonjs/floatingMenu/dial/DialHyperspace.d.ts.map +1 -0
- package/lib/typescript/commonjs/floatingMenu/dial/DialSnow.d.ts +23 -0
- package/lib/typescript/commonjs/floatingMenu/dial/DialSnow.d.ts.map +1 -0
- package/lib/typescript/commonjs/version.d.ts +1 -1
- package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts +19 -13
- package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.web.d.ts +3 -3
- package/lib/typescript/module/floatingMenu/DevToolsSettingsModal.web.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/FloatingDevTools.web.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/dial/DialDevTools.d.ts.map +1 -1
- package/lib/typescript/module/floatingMenu/dial/DialHyperspace.d.ts +24 -0
- package/lib/typescript/module/floatingMenu/dial/DialHyperspace.d.ts.map +1 -0
- package/lib/typescript/module/floatingMenu/dial/DialSnow.d.ts +23 -0
- package/lib/typescript/module/floatingMenu/dial/DialSnow.d.ts.map +1 -0
- package/lib/typescript/module/version.d.ts +1 -1
- package/package.json +5 -5
|
@@ -240,7 +240,11 @@ function FloatingDevToolsInner({
|
|
|
240
240
|
id: tool.id,
|
|
241
241
|
name: tool.name || (0, _floatingToolsReact.getToolLabel)(tool.id),
|
|
242
242
|
description: tool.description || (0, _floatingToolsReact.getToolDescription)(tool.id),
|
|
243
|
-
slot: tool.slot
|
|
243
|
+
slot: tool.slot,
|
|
244
|
+
// Settings rows fall back to the tool's own icon when the modal has
|
|
245
|
+
// no hardcoded glyph for it (mirrors native DialDevTools).
|
|
246
|
+
icon: tool.icon,
|
|
247
|
+
color: tool.color
|
|
244
248
|
})), [tools]);
|
|
245
249
|
|
|
246
250
|
// Welcome tooltip — native FloatingMenu.tsx:134-157: 1000ms after mount,
|
|
@@ -241,7 +241,8 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
241
241
|
bodyRoot: {
|
|
242
242
|
flex: 1,
|
|
243
243
|
minHeight: 0,
|
|
244
|
-
|
|
244
|
+
// Transparent so JsModal's ToolBackground shows through (the modal paints opaque base beneath it).
|
|
245
|
+
backgroundColor: "transparent"
|
|
245
246
|
},
|
|
246
247
|
toast: {
|
|
247
248
|
position: "absolute",
|
|
@@ -8,6 +8,7 @@ var _react = require("react");
|
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
10
10
|
var _DialIcon = require("./DialIcon.js");
|
|
11
|
+
var _DialHyperspace = require("./DialHyperspace.js");
|
|
11
12
|
var _allToolsRegistry = require("../allToolsRegistry.js");
|
|
12
13
|
var _DialPagination = require("./DialPagination.js");
|
|
13
14
|
var _dialUsageStore = require("./dialUsageStore.js");
|
|
@@ -24,6 +25,19 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
24
25
|
// the component via useWindowDimensions — a module-scope Dimensions.get
|
|
25
26
|
// snapshot goes stale when the window resizes after load (web/desktop).
|
|
26
27
|
const BUTTON_SIZE = _floatingToolsCore.DIAL_BUTTON_SIZE;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The centre button's idle pulse, off by design.
|
|
31
|
+
*
|
|
32
|
+
* It is built as `Animated.loop(Animated.sequence([...]))`, and that pairing is
|
|
33
|
+
* NOT handed to the native driver even though every child sets
|
|
34
|
+
* `useNativeDriver: true` — so it ticked on the JS thread for as long as the
|
|
35
|
+
* dial stayed open, taxing every interaction launched from the dial.
|
|
36
|
+
*
|
|
37
|
+
* Re-enabling it means rewriting the loop so it genuinely runs natively;
|
|
38
|
+
* flipping this alone just puts the JS-thread cost back.
|
|
39
|
+
*/
|
|
40
|
+
const DIAL_IDLE_PULSE = false;
|
|
27
41
|
const ONBOARDING_STORAGE_KEY = "@react_buoy_settings_tooltip_shown";
|
|
28
42
|
/** A non-interactive placeholder used to fill out the last dial page. */
|
|
29
43
|
const createEmptySlot = slotIndex => ({
|
|
@@ -67,6 +81,11 @@ const DialDevTools = ({
|
|
|
67
81
|
const circleSize = (0, _floatingToolsCore.getDialLayout)({
|
|
68
82
|
screenWidth
|
|
69
83
|
}).circleSize;
|
|
84
|
+
// The dial face shows the same backdrop the tools use (persisted shared
|
|
85
|
+
// selection). Hyperspace keeps its hand-tuned dial port below.
|
|
86
|
+
const {
|
|
87
|
+
id: backgroundId
|
|
88
|
+
} = (0, _sharedUi.useToolBackground)();
|
|
70
89
|
const sizeStyles = (0, _react.useMemo)(() => ({
|
|
71
90
|
parent: {
|
|
72
91
|
width: circleSize,
|
|
@@ -153,16 +172,35 @@ const DialDevTools = ({
|
|
|
153
172
|
id,
|
|
154
173
|
name,
|
|
155
174
|
slot,
|
|
156
|
-
description
|
|
175
|
+
description,
|
|
176
|
+
icon,
|
|
177
|
+
color
|
|
157
178
|
}) => ({
|
|
158
179
|
id,
|
|
159
180
|
name,
|
|
160
181
|
slot,
|
|
161
|
-
description
|
|
182
|
+
description,
|
|
183
|
+
// Settings rows fall back to the app's own icon when the modal has no
|
|
184
|
+
// hardcoded glyph for the tool.
|
|
185
|
+
icon,
|
|
186
|
+
color
|
|
162
187
|
})), [apps]);
|
|
163
188
|
|
|
164
189
|
// Animation tracking refs
|
|
165
190
|
const pulseAnimationRef = (0, _react.useRef)(null);
|
|
191
|
+
const toggleSettingsModal = () => {
|
|
192
|
+
if (isSettingsModalOpen) {
|
|
193
|
+
setIsSettingsModalOpen(false);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
setIsSettingsModalOpen(true);
|
|
197
|
+
// Dismiss onboarding tooltip when user opens settings
|
|
198
|
+
if (showOnboardingTooltip && !onboardingDismissedRef.current) {
|
|
199
|
+
handleOnboardingDismiss();
|
|
200
|
+
}
|
|
201
|
+
// Also call external handler if provided
|
|
202
|
+
onSettingsPress?.();
|
|
203
|
+
};
|
|
166
204
|
|
|
167
205
|
// Dial-eligible apps: everything except row-only tools. All of them are
|
|
168
206
|
// shown — paginated across pages of MAX_DIAL_SLOTS — so there is no longer
|
|
@@ -410,6 +448,7 @@ const DialDevTools = ({
|
|
|
410
448
|
})]).start();
|
|
411
449
|
|
|
412
450
|
// Pulse animation - only continuous effect kept for performance
|
|
451
|
+
if (!DIAL_IDLE_PULSE) return;
|
|
413
452
|
pulseAnimationRef.current = _reactNative.Animated.loop(_reactNative.Animated.sequence([_reactNative.Animated.timing(pulseScale, {
|
|
414
453
|
toValue: continuous.pulse.maxScale,
|
|
415
454
|
duration: continuous.pulse.duration,
|
|
@@ -598,7 +637,9 @@ const DialDevTools = ({
|
|
|
598
637
|
}]
|
|
599
638
|
}]
|
|
600
639
|
}, i))
|
|
601
|
-
})
|
|
640
|
+
}), backgroundId === "hyperspace" ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_DialHyperspace.DialHyperspace, {
|
|
641
|
+
size: circleSize
|
|
642
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolBackground, {})]
|
|
602
643
|
}), allDialIcons.filter(({
|
|
603
644
|
page
|
|
604
645
|
}) => page === safePage).map(({
|
|
@@ -633,26 +674,14 @@ const DialDevTools = ({
|
|
|
633
674
|
style: styles.buttonBorder,
|
|
634
675
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
635
676
|
style: [styles.button, pulseAnimatedStyle],
|
|
636
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.
|
|
677
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
637
678
|
accessibilityRole: "button",
|
|
638
679
|
accessibilityLabel: isSettingsModalOpen ? "Close Settings" : "Open Dev Tools Settings",
|
|
639
|
-
onPress:
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
// Open internal settings modal
|
|
645
|
-
setIsSettingsModalOpen(true);
|
|
646
|
-
// Dismiss onboarding tooltip when user opens settings
|
|
647
|
-
if (showOnboardingTooltip && !onboardingDismissedRef.current) {
|
|
648
|
-
handleOnboardingDismiss();
|
|
649
|
-
}
|
|
650
|
-
// Also call external handler if provided
|
|
651
|
-
if (onSettingsPress) {
|
|
652
|
-
onSettingsPress();
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
},
|
|
680
|
+
onPress: toggleSettingsModal
|
|
681
|
+
// Dims on touch-down so the press registers visually straight
|
|
682
|
+
// away, even though the panel only opens on release.
|
|
683
|
+
,
|
|
684
|
+
activeOpacity: 0.5,
|
|
656
685
|
style: styles.buttonPressable,
|
|
657
686
|
children: isSettingsModalOpen ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
658
687
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.DialHyperspace = void 0;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
10
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
11
|
+
/**
|
|
12
|
+
* DialHyperspace — the Hyperspace backdrop's lightspeed starfield, scaled
|
|
13
|
+
* down to live inside the open dial's face. Replaces DialSnow (kept next
|
|
14
|
+
* door for an easy swap back) so the dial matches the tool background.
|
|
15
|
+
*
|
|
16
|
+
* Same construction as the full preset (shared-ui
|
|
17
|
+
* `backgrounds/variants/hyperspace.tsx`, see its header for the derivation):
|
|
18
|
+
* the Skia original's per-frame `screen = plane/z` projection becomes scale
|
|
19
|
+
* keyframes on phase-offset shells — a layer scaled by 1/z moves every star
|
|
20
|
+
* outward on the true hyperbolic curve — with each shell's wrap snap hidden
|
|
21
|
+
* inside the fade the original applies to the first/last 6% of a star's
|
|
22
|
+
* life. Dial tuning: fewer stars, a slightly gentler cycle and lower alpha
|
|
23
|
+
* so the field reads behind the icons instead of competing with them.
|
|
24
|
+
* Stars are round crisp points, as in the original — never streaks.
|
|
25
|
+
*
|
|
26
|
+
* Cost while the dial is open: 1 animated node, ~48 static views — the same
|
|
27
|
+
* budget DialSnow ran. The dial is fixed-size (never user-resized), so
|
|
28
|
+
* sizing geometry from the passed circle size is correct here, and the
|
|
29
|
+
* circle's `overflow: "hidden"` clips the streaks.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const Z_SPAN = 0.94;
|
|
33
|
+
const MAX_SCALE = 6;
|
|
34
|
+
const CYCLE_MS = 2400;
|
|
35
|
+
const SHELLS = 3;
|
|
36
|
+
const STARS_PER_SHELL = 16;
|
|
37
|
+
/** Stride 4: angle, radius, size jitter, palette pick — fixed seeds so the
|
|
38
|
+
* field is identical every open. */
|
|
39
|
+
const SHELL_TABLES = Array.from({
|
|
40
|
+
length: SHELLS
|
|
41
|
+
}, (_, k) => (0, _sharedUi.scatter)(0x5747 ^ k * 0x9e37, STARS_PER_SHELL * 4));
|
|
42
|
+
const STAR_COLORS = ["#FFFFFF", "#B3D9FF", "#8CB3FF"];
|
|
43
|
+
|
|
44
|
+
/** The dial face draws 6 grid spokes, one every 60° starting at 0°. Star
|
|
45
|
+
* motion is radial (uniform scale about the centre), so a star whose angle
|
|
46
|
+
* sits near a spoke rides that line for its entire life. Remap the uniform
|
|
47
|
+
* angle pick into the clear wedge between spokes, leaving `SPOKE_GAP` of
|
|
48
|
+
* clearance centred on each line. */
|
|
49
|
+
const SPOKE_STEP = Math.PI / 3;
|
|
50
|
+
const SPOKE_GAP = 14 * Math.PI / 180;
|
|
51
|
+
function spokeClearAngle(u) {
|
|
52
|
+
const sector = Math.min(5, Math.floor(u * 6));
|
|
53
|
+
const t = u * 6 - sector;
|
|
54
|
+
return sector * SPOKE_STEP + SPOKE_GAP / 2 + t * (SPOKE_STEP - SPOKE_GAP);
|
|
55
|
+
}
|
|
56
|
+
function scaleAt(p) {
|
|
57
|
+
return Math.min(MAX_SCALE, 1 / (1 - p * Z_SPAN));
|
|
58
|
+
}
|
|
59
|
+
function opacityAt(p) {
|
|
60
|
+
let bright = 0.15 + 0.85 * p * p;
|
|
61
|
+
// The original's 6% fade at both ends of a star's life.
|
|
62
|
+
if (p < 0.06) bright *= p / 0.06;else if (p > 0.94) bright *= (1 - p) / 0.06;
|
|
63
|
+
// Dial tuning: never brighter than 0.7 — the icons own the foreground.
|
|
64
|
+
return Math.min(0.7, bright);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Interpolate stops for shell k (phase = (v + k/K) mod 1), the wrap point
|
|
68
|
+
* double-sampled 1e-4 apart inside the zero-opacity sliver. */
|
|
69
|
+
function shellStops(k) {
|
|
70
|
+
const offset = k / SHELLS;
|
|
71
|
+
const vs = [];
|
|
72
|
+
const SEGMENTS = 12;
|
|
73
|
+
for (let i = 0; i <= SEGMENTS; i++) vs.push(i / SEGMENTS);
|
|
74
|
+
const vWrap = (1 - offset) % 1;
|
|
75
|
+
if (vWrap > 1e-3 && vWrap < 1 - 1e-3) vs.push(vWrap - 1e-4, vWrap + 1e-4);
|
|
76
|
+
vs.sort((a, b) => a - b);
|
|
77
|
+
const input = [];
|
|
78
|
+
const scale = [];
|
|
79
|
+
const opacity = [];
|
|
80
|
+
for (const v of vs) {
|
|
81
|
+
// At the loop boundary the offset-0 shell's phase must read as END of
|
|
82
|
+
// life (p=1, max scale, zero opacity), not wrap to 0 — otherwise the
|
|
83
|
+
// last segment lerps scale 6→1 at visible opacity and the stars
|
|
84
|
+
// retract to the centre once per cycle.
|
|
85
|
+
const p = offset === 0 && v === 1 ? 1 : (v + offset) % 1;
|
|
86
|
+
input.push(v);
|
|
87
|
+
scale.push(scaleAt(p));
|
|
88
|
+
opacity.push(opacityAt(p));
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
input,
|
|
92
|
+
scale,
|
|
93
|
+
opacity
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const SHELL_STOPS = Array.from({
|
|
97
|
+
length: SHELLS
|
|
98
|
+
}, (_, k) => shellStops(k));
|
|
99
|
+
const DialHyperspace = exports.DialHyperspace = /*#__PURE__*/(0, _react.memo)(function DialHyperspace({
|
|
100
|
+
size
|
|
101
|
+
}) {
|
|
102
|
+
const drive = (0, _sharedUi.useLoop)(CYCLE_MS);
|
|
103
|
+
const c = size / 2;
|
|
104
|
+
const shells = [];
|
|
105
|
+
for (let k = 0; k < SHELLS; k++) {
|
|
106
|
+
const table = SHELL_TABLES[k];
|
|
107
|
+
const stops = SHELL_STOPS[k];
|
|
108
|
+
const stars = [];
|
|
109
|
+
for (let i = 0; i < STARS_PER_SHELL; i++) {
|
|
110
|
+
const ang = spokeClearAngle(table[i * 4]);
|
|
111
|
+
// sqrt-biased outward, inner 15% clear of the vanishing point. Offsets
|
|
112
|
+
// scale with the HALF-extent, as in the original's projection.
|
|
113
|
+
const rad = (0.15 + 0.85 * Math.sqrt(table[i * 4 + 1])) * 0.5 * c;
|
|
114
|
+
const color = STAR_COLORS[Math.floor(table[i * 4 + 3] * STAR_COLORS.length) % 3];
|
|
115
|
+
const x = c + Math.cos(ang) * rad;
|
|
116
|
+
const y = c + Math.sin(ang) * rad;
|
|
117
|
+
// Round crisp points, as in the original — the visible star is the
|
|
118
|
+
// sprite's lit core (~12.5% of the stamp), so it starts sub-pixel and
|
|
119
|
+
// only reaches ~2-3px before the fade-out kills it. A soft halo disc
|
|
120
|
+
// at α0.3 stands in for the sprite's blur.
|
|
121
|
+
const core = 0.6;
|
|
122
|
+
const halo = core * 2.4;
|
|
123
|
+
stars.push(/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
124
|
+
style: {
|
|
125
|
+
position: "absolute",
|
|
126
|
+
left: x - halo / 2,
|
|
127
|
+
top: y - halo / 2,
|
|
128
|
+
width: halo,
|
|
129
|
+
height: halo,
|
|
130
|
+
borderRadius: halo / 2,
|
|
131
|
+
backgroundColor: (0, _sharedUi.withBgAlpha)(color, 0.3)
|
|
132
|
+
}
|
|
133
|
+
}, `${i}-h`), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
134
|
+
style: {
|
|
135
|
+
position: "absolute",
|
|
136
|
+
left: x - core / 2,
|
|
137
|
+
top: y - core / 2,
|
|
138
|
+
width: core,
|
|
139
|
+
height: core,
|
|
140
|
+
borderRadius: core / 2,
|
|
141
|
+
backgroundColor: color
|
|
142
|
+
}
|
|
143
|
+
}, i));
|
|
144
|
+
}
|
|
145
|
+
shells.push(/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
146
|
+
style: {
|
|
147
|
+
position: "absolute",
|
|
148
|
+
left: 0,
|
|
149
|
+
top: 0,
|
|
150
|
+
width: size,
|
|
151
|
+
height: size,
|
|
152
|
+
opacity: drive.interpolate({
|
|
153
|
+
inputRange: stops.input,
|
|
154
|
+
outputRange: stops.opacity
|
|
155
|
+
}),
|
|
156
|
+
transform: [{
|
|
157
|
+
scale: drive.interpolate({
|
|
158
|
+
inputRange: stops.input,
|
|
159
|
+
outputRange: stops.scale
|
|
160
|
+
})
|
|
161
|
+
}]
|
|
162
|
+
},
|
|
163
|
+
pointerEvents: "none",
|
|
164
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.RasterLayer, {
|
|
165
|
+
style: {
|
|
166
|
+
width: size,
|
|
167
|
+
height: size
|
|
168
|
+
},
|
|
169
|
+
raster: false,
|
|
170
|
+
children: stars
|
|
171
|
+
})
|
|
172
|
+
}, k));
|
|
173
|
+
}
|
|
174
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
|
|
175
|
+
children: shells
|
|
176
|
+
});
|
|
177
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.DialSnow = void 0;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
10
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
11
|
+
/**
|
|
12
|
+
* DialSnow — pale particles drifting down inside the open dial's face, built
|
|
13
|
+
* from the shared background primitives (packages/shared/src/ui/backgrounds).
|
|
14
|
+
*
|
|
15
|
+
* Same performance contract as the backgrounds: the loop is native-driven
|
|
16
|
+
* (useLoop hands the whole animation to the UI thread once — zero JS work per
|
|
17
|
+
* frame), the flakes are one rasterized texture, and the scatter table is
|
|
18
|
+
* generated at module load. Cost while the dial is open: 1 animated node and
|
|
19
|
+
* ~24 static views, mounted only for the dial's lifetime.
|
|
20
|
+
*
|
|
21
|
+
* Rendered inside the circle's `overflow: "hidden"` background. The dial is
|
|
22
|
+
* fixed-size (never user-resized), so sizing geometry from the passed circle
|
|
23
|
+
* size is correct here.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** Pale mint-ice flake tint — sits quietly on the dial's dark face. */const SNOW_COLOR = "#A8E0D2";
|
|
27
|
+
|
|
28
|
+
/** Stride 3: x, y, size — fixed seed so the flurry is identical every open. */
|
|
29
|
+
const SNOW_COUNT = 12;
|
|
30
|
+
const SNOW_TABLE = (0, _sharedUi.scatter)(0x5ea1, SNOW_COUNT * 3);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The two-copy tiling trick from the backdrops: everything drawn twice one
|
|
34
|
+
* height apart, translated a full height per loop, so the seam never shows.
|
|
35
|
+
* Sway is keyframed onto the SAME loop value.
|
|
36
|
+
*/
|
|
37
|
+
const DialSnow = exports.DialSnow = /*#__PURE__*/(0, _react.memo)(function DialSnow({
|
|
38
|
+
size
|
|
39
|
+
}) {
|
|
40
|
+
const t = (0, _sharedUi.useLoop)(26000);
|
|
41
|
+
const translateY = t.interpolate({
|
|
42
|
+
inputRange: [0, 1],
|
|
43
|
+
outputRange: [0, size]
|
|
44
|
+
});
|
|
45
|
+
const translateX = t.interpolate({
|
|
46
|
+
inputRange: [0, 0.25, 0.5, 0.75, 1],
|
|
47
|
+
outputRange: [0, 5, 0, -5, 0]
|
|
48
|
+
});
|
|
49
|
+
const flakes = [];
|
|
50
|
+
for (let i = 0; i < SNOW_COUNT; i++) {
|
|
51
|
+
const x = SNOW_TABLE[i * 3] * size;
|
|
52
|
+
const y = SNOW_TABLE[i * 3 + 1] * size;
|
|
53
|
+
// Bigger flakes read nearer, so they get more alpha too.
|
|
54
|
+
const dia = 1.4 + SNOW_TABLE[i * 3 + 2] * 1.2;
|
|
55
|
+
const alpha = 0.28 + SNOW_TABLE[i * 3 + 2] * 0.18;
|
|
56
|
+
for (let copy = 0; copy < 2; copy++) {
|
|
57
|
+
flakes.push(/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
58
|
+
style: {
|
|
59
|
+
position: "absolute",
|
|
60
|
+
left: x - dia / 2,
|
|
61
|
+
top: y + copy * size - dia / 2,
|
|
62
|
+
width: dia,
|
|
63
|
+
height: dia,
|
|
64
|
+
borderRadius: dia / 2,
|
|
65
|
+
backgroundColor: (0, _sharedUi.withBgAlpha)(SNOW_COLOR, alpha)
|
|
66
|
+
}
|
|
67
|
+
}, `${i}-${copy}`));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
71
|
+
style: {
|
|
72
|
+
position: "absolute",
|
|
73
|
+
left: 0,
|
|
74
|
+
top: -size,
|
|
75
|
+
width: size,
|
|
76
|
+
height: size * 2,
|
|
77
|
+
transform: [{
|
|
78
|
+
translateY
|
|
79
|
+
}, {
|
|
80
|
+
translateX
|
|
81
|
+
}]
|
|
82
|
+
},
|
|
83
|
+
pointerEvents: "none",
|
|
84
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.RasterLayer, {
|
|
85
|
+
style: {
|
|
86
|
+
width: size,
|
|
87
|
+
height: size * 2
|
|
88
|
+
},
|
|
89
|
+
children: flakes
|
|
90
|
+
})
|
|
91
|
+
});
|
|
92
|
+
});
|
package/lib/commonjs/version.js
CHANGED
|
@@ -15,4 +15,4 @@ exports.SDK_VERSION = void 0;
|
|
|
15
15
|
* Drift is prevented by a test that fails when this disagrees with
|
|
16
16
|
* package.json, so a version bump that forgets this file cannot ship.
|
|
17
17
|
*/
|
|
18
|
-
const SDK_VERSION = exports.SDK_VERSION = "7.0.
|
|
18
|
+
const SDK_VERSION = exports.SDK_VERSION = "7.0.27";
|