@buoy-gg/shared-ui 7.0.28 → 7.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/JsModal.js +12 -2
- package/lib/commonjs/dataViewer/DataTreeActionDock.js +1 -0
- package/lib/commonjs/dataViewer/DataViewer.js +6 -3
- package/lib/commonjs/dataViewer/VirtualizedDataExplorer.js +13 -4
- package/lib/commonjs/hooks/safe-area-impl.js +1 -1
- package/lib/commonjs/license/FeatureGate.js +9 -0
- package/lib/commonjs/ui/backgrounds/registry.js +3 -2
- package/lib/commonjs/ui/backgrounds/variants/meteors.js +222 -78
- package/lib/commonjs/ui/components/Badge.js +3 -1
- package/lib/commonjs/ui/components/CompactRow.js +10 -1
- package/lib/commonjs/ui/components/CopyButton.js +3 -1
- package/lib/commonjs/ui/components/DraggableHeader.js +5 -1
- package/lib/commonjs/ui/components/DynamicFilterView.js +23 -4
- package/lib/commonjs/ui/components/EventStepperFooter.js +8 -0
- package/lib/commonjs/ui/components/FilterComponents.js +18 -2
- package/lib/commonjs/ui/components/ModalHeader.js +12 -3
- package/lib/commonjs/ui/components/PowerToggleButton.js +2 -0
- package/lib/commonjs/ui/components/SearchBar.js +6 -1
- package/lib/commonjs/ui/components/SectionHeader.js +3 -1
- package/lib/commonjs/ui/components/TabSelector.js +15 -4
- package/lib/commonjs/ui/components/WindowControls.js +68 -96
- package/lib/commonjs/ui/gameUI/constants/gameUIColors.js +12 -8
- package/lib/module/JsModal.js +12 -2
- package/lib/module/dataViewer/DataTreeActionDock.js +1 -0
- package/lib/module/dataViewer/DataViewer.js +6 -3
- package/lib/module/dataViewer/VirtualizedDataExplorer.js +13 -4
- package/lib/module/hooks/safe-area-impl.js +1 -1
- package/lib/module/license/FeatureGate.js +9 -0
- package/lib/module/ui/backgrounds/registry.js +3 -2
- package/lib/module/ui/backgrounds/variants/meteors.js +224 -80
- package/lib/module/ui/components/Badge.js +3 -1
- package/lib/module/ui/components/CompactRow.js +10 -1
- package/lib/module/ui/components/CopyButton.js +3 -1
- package/lib/module/ui/components/DraggableHeader.js +5 -1
- package/lib/module/ui/components/DynamicFilterView.js +23 -4
- package/lib/module/ui/components/EventStepperFooter.js +8 -0
- package/lib/module/ui/components/FilterComponents.js +18 -2
- package/lib/module/ui/components/ModalHeader.js +12 -3
- package/lib/module/ui/components/PowerToggleButton.js +2 -0
- package/lib/module/ui/components/SearchBar.js +6 -1
- package/lib/module/ui/components/SectionHeader.js +3 -1
- package/lib/module/ui/components/TabSelector.js +15 -4
- package/lib/module/ui/components/WindowControls.js +69 -97
- package/lib/module/ui/gameUI/constants/gameUIColors.js +12 -8
- package/lib/typescript/commonjs/JsModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/DataTreeActionDock.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/DataViewer.d.ts +2 -0
- package/lib/typescript/commonjs/dataViewer/DataViewer.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/VirtualizedDataExplorer.d.ts +9 -0
- package/lib/typescript/commonjs/dataViewer/VirtualizedDataExplorer.d.ts.map +1 -1
- package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
- package/lib/typescript/commonjs/license/FeatureGate.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/backgrounds/registry.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/backgrounds/variants/meteors.d.ts +22 -6
- package/lib/typescript/commonjs/ui/backgrounds/variants/meteors.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/Badge.d.ts +3 -1
- package/lib/typescript/commonjs/ui/components/Badge.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/CompactRow.d.ts +5 -1
- package/lib/typescript/commonjs/ui/components/CompactRow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/CopyButton.d.ts +5 -0
- package/lib/typescript/commonjs/ui/components/CopyButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/DraggableHeader.d.ts +2 -0
- package/lib/typescript/commonjs/ui/components/DraggableHeader.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/DynamicFilterView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/EventStepperFooter.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/FilterComponents.d.ts +10 -2
- package/lib/typescript/commonjs/ui/components/FilterComponents.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ModalHeader.d.ts +16 -2
- package/lib/typescript/commonjs/ui/components/ModalHeader.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/PowerToggleButton.d.ts +4 -1
- package/lib/typescript/commonjs/ui/components/PowerToggleButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/SearchBar.d.ts +7 -1
- package/lib/typescript/commonjs/ui/components/SearchBar.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/SectionHeader.d.ts +7 -1
- package/lib/typescript/commonjs/ui/components/SectionHeader.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/TabSelector.d.ts +20 -1
- package/lib/typescript/commonjs/ui/components/TabSelector.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/WindowControls.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/gameUI/constants/gameUIColors.d.ts +5 -0
- package/lib/typescript/commonjs/ui/gameUI/constants/gameUIColors.d.ts.map +1 -1
- package/lib/typescript/module/JsModal.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/DataTreeActionDock.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/DataViewer.d.ts +2 -0
- package/lib/typescript/module/dataViewer/DataViewer.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/VirtualizedDataExplorer.d.ts +9 -0
- package/lib/typescript/module/dataViewer/VirtualizedDataExplorer.d.ts.map +1 -1
- package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
- package/lib/typescript/module/license/FeatureGate.d.ts.map +1 -1
- package/lib/typescript/module/ui/backgrounds/registry.d.ts.map +1 -1
- package/lib/typescript/module/ui/backgrounds/variants/meteors.d.ts +22 -6
- package/lib/typescript/module/ui/backgrounds/variants/meteors.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/Badge.d.ts +3 -1
- package/lib/typescript/module/ui/components/Badge.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/CompactRow.d.ts +5 -1
- package/lib/typescript/module/ui/components/CompactRow.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/CopyButton.d.ts +5 -0
- package/lib/typescript/module/ui/components/CopyButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/DraggableHeader.d.ts +2 -0
- package/lib/typescript/module/ui/components/DraggableHeader.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/DynamicFilterView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/EventStepperFooter.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/FilterComponents.d.ts +10 -2
- package/lib/typescript/module/ui/components/FilterComponents.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ModalHeader.d.ts +16 -2
- package/lib/typescript/module/ui/components/ModalHeader.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/PowerToggleButton.d.ts +4 -1
- package/lib/typescript/module/ui/components/PowerToggleButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/SearchBar.d.ts +7 -1
- package/lib/typescript/module/ui/components/SearchBar.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/SectionHeader.d.ts +7 -1
- package/lib/typescript/module/ui/components/SectionHeader.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/TabSelector.d.ts +20 -1
- package/lib/typescript/module/ui/components/TabSelector.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/WindowControls.d.ts.map +1 -1
- package/lib/typescript/module/ui/gameUI/constants/gameUIColors.d.ts +5 -0
- package/lib/typescript/module/ui/gameUI/constants/gameUIColors.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/commonjs/JsModal.js
CHANGED
|
@@ -498,6 +498,16 @@ const JsModalComponent = ({
|
|
|
498
498
|
}
|
|
499
499
|
}, []);
|
|
500
500
|
|
|
501
|
+
// Parents routinely pass an inline `onModeChange` (a new identity every
|
|
502
|
+
// render). The persisted-state load below is mount/key-scoped — if the
|
|
503
|
+
// callback identity re-triggered it, every parent re-render would reload
|
|
504
|
+
// the OLD persisted mode and stomp a live toggleMode() before the 500ms
|
|
505
|
+
// save debounce could write the new one (mode flipped to floating, then
|
|
506
|
+
// snapped back ~50ms later). So the callback rides in a ref instead of
|
|
507
|
+
// the dependency list.
|
|
508
|
+
const onModeChangeRef = (0, _react.useRef)(onModeChange);
|
|
509
|
+
onModeChangeRef.current = onModeChange;
|
|
510
|
+
|
|
501
511
|
// Load persisted state on mount
|
|
502
512
|
(0, _react.useEffect)(() => {
|
|
503
513
|
if (!enablePersistence || !persistenceKey) {
|
|
@@ -512,7 +522,7 @@ const JsModalComponent = ({
|
|
|
512
522
|
if (savedState.mode) {
|
|
513
523
|
setMode(savedState.mode);
|
|
514
524
|
// Notify parent of loaded mode
|
|
515
|
-
|
|
525
|
+
onModeChangeRef.current?.(savedState.mode);
|
|
516
526
|
}
|
|
517
527
|
|
|
518
528
|
// Restore bottom sheet height
|
|
@@ -539,7 +549,7 @@ const JsModalComponent = ({
|
|
|
539
549
|
return () => {
|
|
540
550
|
mounted = false;
|
|
541
551
|
};
|
|
542
|
-
}, [persistenceKey, enablePersistence,
|
|
552
|
+
}, [persistenceKey, enablePersistence, animatedBottomPosition, animatedFloatingHeight, animatedWidth, floatingPosition]);
|
|
543
553
|
|
|
544
554
|
// Cleanup on unmount
|
|
545
555
|
(0, _react.useEffect)(() => {
|
|
@@ -86,6 +86,7 @@ function Tile({
|
|
|
86
86
|
const color = danger && enabled ? T.dangerText : T.tileIcon;
|
|
87
87
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
88
88
|
"sentry-label": "ignore devtools tree action",
|
|
89
|
+
testID: `buoy-tree-action-${id}`,
|
|
89
90
|
accessibilityLabel: label,
|
|
90
91
|
accessibilityState: {
|
|
91
92
|
disabled: !enabled
|
|
@@ -31,7 +31,8 @@ const DataViewer = ({
|
|
|
31
31
|
onRequestInlineEdit,
|
|
32
32
|
inlineEditPath,
|
|
33
33
|
renderInlineEditor,
|
|
34
|
-
selectPath
|
|
34
|
+
selectPath,
|
|
35
|
+
testIDPrefix
|
|
35
36
|
}) => {
|
|
36
37
|
const [activeFilter, setActiveFilter] = (0, _react.useState)(null);
|
|
37
38
|
|
|
@@ -113,7 +114,8 @@ const DataViewer = ({
|
|
|
113
114
|
maxDepth: maxDepth,
|
|
114
115
|
rawMode: rawMode,
|
|
115
116
|
initialExpanded: initialExpanded,
|
|
116
|
-
disableCopy: disableCopy
|
|
117
|
+
disableCopy: disableCopy,
|
|
118
|
+
testIDPrefix: testIDPrefix
|
|
117
119
|
});
|
|
118
120
|
}
|
|
119
121
|
|
|
@@ -129,7 +131,8 @@ const DataViewer = ({
|
|
|
129
131
|
onRequestInlineEdit: onRequestInlineEdit,
|
|
130
132
|
inlineEditPath: inlineEditPath,
|
|
131
133
|
renderInlineEditor: renderInlineEditor,
|
|
132
|
-
selectPath: selectPath
|
|
134
|
+
selectPath: selectPath,
|
|
135
|
+
testIDPrefix: testIDPrefix
|
|
133
136
|
});
|
|
134
137
|
};
|
|
135
138
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
@@ -826,7 +826,8 @@ const VirtualizedItemComponent = ({
|
|
|
826
826
|
selectable,
|
|
827
827
|
onRequestInlineEdit,
|
|
828
828
|
isInlineEditing,
|
|
829
|
-
inlineEditor
|
|
829
|
+
inlineEditor,
|
|
830
|
+
testIDPrefix
|
|
830
831
|
}) => {
|
|
831
832
|
const [isPressed, setIsPressed] = (0, _react.useState)(false);
|
|
832
833
|
const [showFullKey, setShowFullKey] = (0, _react.useState)(false);
|
|
@@ -895,7 +896,13 @@ const VirtualizedItemComponent = ({
|
|
|
895
896
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
896
897
|
style: [STABLE_STYLES.itemContainer, indentStyle],
|
|
897
898
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
898
|
-
"sentry-label": "ignore devtools data explorer item"
|
|
899
|
+
"sentry-label": "ignore devtools data explorer item"
|
|
900
|
+
// `item.id` is the dotted path from the root ("root.settings.theme"),
|
|
901
|
+
// so this handle addresses a NODE rather than a list position — stable
|
|
902
|
+
// across expand/collapse and across re-renders.
|
|
903
|
+
,
|
|
904
|
+
testID: `${testIDPrefix}-${item.id}`,
|
|
905
|
+
accessibilityLabel: `${item.key}: ${item.valueType}`,
|
|
899
906
|
style: [STABLE_STYLES.itemTouchable, isPressed && STABLE_STYLES.itemTouchablePressed,
|
|
900
907
|
// The selected wash exists for the action-bar/edit flow. In a plain
|
|
901
908
|
// read-only viewer every expand/collapse tap ALSO selects, which
|
|
@@ -970,7 +977,8 @@ const VirtualizedDataExplorer = ({
|
|
|
970
977
|
onRequestInlineEdit,
|
|
971
978
|
inlineEditPath,
|
|
972
979
|
renderInlineEditor,
|
|
973
|
-
selectPath
|
|
980
|
+
selectPath,
|
|
981
|
+
testIDPrefix = "buoy-tree-row"
|
|
974
982
|
}) => {
|
|
975
983
|
const [isExpanded, setIsExpanded] = (0, _react.useState)(rawMode); // Auto-expand in raw mode
|
|
976
984
|
const {
|
|
@@ -1086,7 +1094,8 @@ const VirtualizedDataExplorer = ({
|
|
|
1086
1094
|
selectable: onSelectionChange != null,
|
|
1087
1095
|
onRequestInlineEdit: handleRequestInlineEdit,
|
|
1088
1096
|
isInlineEditing: inlineEditPath != null && inlineEditPath.join(".") === item.path.slice(1).join("."),
|
|
1089
|
-
inlineEditor: renderInlineEditor ? renderInlineEditor(selectionOf(item)) : null
|
|
1097
|
+
inlineEditor: renderInlineEditor ? renderInlineEditor(selectionOf(item)) : null,
|
|
1098
|
+
testIDPrefix: testIDPrefix
|
|
1090
1099
|
});
|
|
1091
1100
|
|
|
1092
1101
|
// Uniform row height for crisp guide geometry
|
|
@@ -7,7 +7,7 @@ exports.useNativeSafeAreaInsets = exports.safeAreaType = exports.hasSafeAreaPack
|
|
|
7
7
|
/**
|
|
8
8
|
* Auto-generated safe area implementation
|
|
9
9
|
* Detected: none
|
|
10
|
-
* Generated at: 2026-08-
|
|
10
|
+
* Generated at: 2026-08-24T23:44:23.535Z
|
|
11
11
|
*
|
|
12
12
|
* DO NOT EDIT - This file is generated by scripts/detect-safe-area.js
|
|
13
13
|
*
|
|
@@ -388,11 +388,20 @@ const UpgradeModal = ({
|
|
|
388
388
|
style: styles.modalContent,
|
|
389
389
|
activeOpacity: 1,
|
|
390
390
|
onPress: () => {} // Prevent overlay dismiss when tapping content
|
|
391
|
+
// The handle a test needs to say a Pro gate did NOT fire. Without
|
|
392
|
+
// it "the action worked" and "the upsell appeared over it" are
|
|
393
|
+
// told apart only by what happened next, which is exactly the
|
|
394
|
+
// assertion that was supposed to be the proof.
|
|
391
395
|
,
|
|
396
|
+
testID: "buoy-upgrade-modal",
|
|
397
|
+
accessibilityLabel: `Unlock ${featureName}`,
|
|
392
398
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
393
399
|
style: styles.modalCloseButton,
|
|
394
400
|
onPress: onClose,
|
|
395
401
|
activeOpacity: 0.7,
|
|
402
|
+
testID: "buoy-upgrade-modal-close",
|
|
403
|
+
accessibilityRole: "button",
|
|
404
|
+
accessibilityLabel: "Close upgrade prompt",
|
|
396
405
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_lucideIcons.X, {
|
|
397
406
|
size: 18,
|
|
398
407
|
color: _gameUIColors.buoyColors.textMuted
|
|
@@ -32,9 +32,10 @@ const BACKGROUND_PRESETS = exports.BACKGROUND_PRESETS = [{
|
|
|
32
32
|
id: "deepfield",
|
|
33
33
|
label: "Deep Field",
|
|
34
34
|
blurb: "The Everlights field — 160 stars, with meteors slamming the walls",
|
|
35
|
-
// The old standalone Meteors preset folded in: +2 meteor loops, ~
|
|
35
|
+
// The old standalone Meteors preset folded in: +2 meteor loops, ~55 views
|
|
36
|
+
// (streak + flash + the pop's 6 rays and ~3 embers per impact).
|
|
36
37
|
nodes: 24,
|
|
37
|
-
views:
|
|
38
|
+
views: 515,
|
|
38
39
|
Component: _deepField.DeepField
|
|
39
40
|
}, {
|
|
40
41
|
id: "hyperspace",
|
|
@@ -14,12 +14,28 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
14
14
|
* MeteorShower — shooting stars that slam into the walls.
|
|
15
15
|
*
|
|
16
16
|
* Each meteor streaks in from off-screen, hits an EDGE of the surface, and
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
17
|
+
* blows out like a firework: a flash, then a fan of streak RAYS that shoot
|
|
18
|
+
* back into the room — each ray grows out of the impact point while its tail
|
|
19
|
+
* chases the head, so it reads as a burning trail rather than a ring — with
|
|
20
|
+
* ember dots at the tips that decelerate, droop under gravity and gutter out.
|
|
21
|
+
* The whole life cycle — parked invisible, streak, impact, decay — is
|
|
22
|
+
* keyframed into interpolate stops on ONE plain native loop per meteor;
|
|
23
|
+
* `Animated.sequence([timing, loop])` silently fails to drive natively, so
|
|
24
|
+
* nothing here sequences.
|
|
25
|
+
*
|
|
26
|
+
* Each meteor also rolls WHAT IT'S MADE OF once per mount — a weighted draw
|
|
27
|
+
* over the real emission-line palette (sodium orange-yellow, iron yellow,
|
|
28
|
+
* magnesium blue-green, calcium violet, shocked atmospheric N₂/O₂ red,
|
|
29
|
+
* ionized-air blue-white) — and that mineral tints streak, trail, flash,
|
|
30
|
+
* rays and embers as one. See `MINERALS`.
|
|
31
|
+
*
|
|
32
|
+
* The ray trick, since there is no `transformOrigin`: a bar of full length
|
|
33
|
+
* `dist` is CENTERED on the impact point, rotated to its heading, then driven
|
|
34
|
+
* with `[{rotate}, {translateX}, {scaleX}]`. Rotate is outermost, so the
|
|
35
|
+
* translate runs along the ray's own axis; scaleX squashes about the bar's
|
|
36
|
+
* center, and translating by the midpoint of (head, tail) while scaling by
|
|
37
|
+
* (head − tail) puts the visible segment exactly between the two — a line
|
|
38
|
+
* growing out of the burst with both endpoints scripted.
|
|
23
39
|
*
|
|
24
40
|
* This used to be a standalone "Meteors" preset with its own 40-star sky;
|
|
25
41
|
* that scene was folded into Deep Field (which supplies the far better sky),
|
|
@@ -32,8 +48,8 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
32
48
|
|
|
33
49
|
/**
|
|
34
50
|
* `to` is ON a wall — that's the impact point. `sparkArc` is the hemisphere
|
|
35
|
-
* (in degrees)
|
|
36
|
-
* meteor just hit. Different periods keep the two impacts from syncing up.
|
|
51
|
+
* (in degrees) the firework rays fan into, pointed back away from the wall
|
|
52
|
+
* the meteor just hit. Different periods keep the two impacts from syncing up.
|
|
37
53
|
*/
|
|
38
54
|
const METEORS = [{
|
|
39
55
|
from: [0.08, -0.06],
|
|
@@ -48,8 +64,112 @@ const METEORS = [{
|
|
|
48
64
|
window: [0.56, 0.66],
|
|
49
65
|
sparkArc: [-70, 70] // left wall → sparks fly right
|
|
50
66
|
}];
|
|
51
|
-
const
|
|
52
|
-
|
|
67
|
+
const RAYS_PER_IMPACT = 6;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Real meteor color comes from what's burning. The dominant visible emission
|
|
71
|
+
* lines are sodium (orange-yellow), iron (yellow) and magnesium (blue-green);
|
|
72
|
+
* calcium-rich rock flares violet, and the shocked ATMOSPHERE itself — the
|
|
73
|
+
* excited nitrogen/oxygen sheath around the meteoroid — glows red. Faint fast
|
|
74
|
+
* meteors mostly show the blue-white of ionized air. The weights lean the
|
|
75
|
+
* same way the sky does: yellows and oranges common, teal frequent, red
|
|
76
|
+
* occasional, violet the rare treat.
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
const MINERALS = [{
|
|
80
|
+
name: "sodium",
|
|
81
|
+
weight: 26,
|
|
82
|
+
head: "#FFF3DC",
|
|
83
|
+
glow: "#FFDCA8",
|
|
84
|
+
trail: "#FFCB85",
|
|
85
|
+
rays: ["#FFE8C2", "#FFCB85", "#F0B45E"]
|
|
86
|
+
}, {
|
|
87
|
+
name: "iron",
|
|
88
|
+
weight: 24,
|
|
89
|
+
head: "#FFF9E3",
|
|
90
|
+
glow: "#FFEDAE",
|
|
91
|
+
trail: "#F2DC8A",
|
|
92
|
+
rays: ["#FBF0C0", "#F2DC8A", "#E3C868"]
|
|
93
|
+
}, {
|
|
94
|
+
name: "magnesium",
|
|
95
|
+
weight: 20,
|
|
96
|
+
head: "#EAFFF9",
|
|
97
|
+
glow: "#ACF0DE",
|
|
98
|
+
trail: "#82E3CA",
|
|
99
|
+
rays: ["#D3FAEF", "#82E3CA", "#5FD6BC"]
|
|
100
|
+
}, {
|
|
101
|
+
// Not a mineral at all — ionized air, the old blue-white default.
|
|
102
|
+
name: "ionized-air",
|
|
103
|
+
weight: 14,
|
|
104
|
+
head: "#F5F9FF",
|
|
105
|
+
glow: "#E5EEFC",
|
|
106
|
+
trail: "#C9D9F0",
|
|
107
|
+
rays: ["#F5F9FF", "#DCE9FF", "#BBE5FA"]
|
|
108
|
+
}, {
|
|
109
|
+
// Also atmospheric: nitrogen/oxygen shocked red-hot in the sheath.
|
|
110
|
+
name: "nitrogen-oxygen",
|
|
111
|
+
weight: 10,
|
|
112
|
+
head: "#FFEAE5",
|
|
113
|
+
glow: "#FFB4A6",
|
|
114
|
+
trail: "#FF917E",
|
|
115
|
+
rays: ["#FFCFC6", "#FF917E", "#F27461"]
|
|
116
|
+
}, {
|
|
117
|
+
name: "calcium",
|
|
118
|
+
weight: 6,
|
|
119
|
+
head: "#F6EEFF",
|
|
120
|
+
glow: "#DCC2FF",
|
|
121
|
+
trail: "#C29BF7",
|
|
122
|
+
rays: ["#EADCFF", "#C29BF7", "#AB7EEC"]
|
|
123
|
+
}];
|
|
124
|
+
const MINERAL_WEIGHT = MINERALS.reduce((sum, m) => sum + m.weight, 0);
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Weighted draw — `Math.random` ON PURPOSE, unlike the seeded burst geometry:
|
|
128
|
+
* each meteor rolls its make-up once per mount, so every session's sky burns
|
|
129
|
+
* a different mix while the animation itself stays deterministic.
|
|
130
|
+
*/
|
|
131
|
+
function pickMineral() {
|
|
132
|
+
let r = Math.random() * MINERAL_WEIGHT;
|
|
133
|
+
for (const m of MINERALS) {
|
|
134
|
+
r -= m.weight;
|
|
135
|
+
if (r <= 0) return m;
|
|
136
|
+
}
|
|
137
|
+
return MINERALS[0];
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* The firework: a deterministic fan of rays per impact, seeded so both
|
|
141
|
+
* platforms (and every reload) burst identically. Angles are stratified
|
|
142
|
+
* across the arc with jitter, so the fan covers it without reading as a
|
|
143
|
+
* perfect star; reach, thickness and stagger vary per ray so it reads as
|
|
144
|
+
* debris blowing out, not a rigid spoke wheel.
|
|
145
|
+
*/
|
|
146
|
+
function buildBurst(index) {
|
|
147
|
+
const m = METEORS[index];
|
|
148
|
+
const rnd = (0, _rng.mulberry32)((0xb1a57 ^ index) >>> 0);
|
|
149
|
+
const [a0, a1] = m.sparkArc;
|
|
150
|
+
const rays = [];
|
|
151
|
+
for (let i = 0; i < RAYS_PER_IMPACT; i++) {
|
|
152
|
+
const frac = (i + 0.5) / RAYS_PER_IMPACT;
|
|
153
|
+
const jitter = (rnd() - 0.5) * 0.7 / RAYS_PER_IMPACT;
|
|
154
|
+
const deg = a0 + (a1 - a0) * (frac + jitter);
|
|
155
|
+
// Small on purpose — a game-sprite pop, not a fireworks display: rays
|
|
156
|
+
// reach 10–22px, a hair over 1px thick, and the whole thing is over in a
|
|
157
|
+
// few frames.
|
|
158
|
+
rays.push({
|
|
159
|
+
rad: deg * Math.PI / 180,
|
|
160
|
+
dist: 10 + rnd() * 12,
|
|
161
|
+
thick: 1 + rnd() * 0.5,
|
|
162
|
+
delay: rnd() * 0.008,
|
|
163
|
+
grow: 0.03 + rnd() * 0.015,
|
|
164
|
+
ember: rnd() > 0.5,
|
|
165
|
+
emberDrop: 2 + rnd() * 3,
|
|
166
|
+
emberDrift: 1 + rnd() * 3,
|
|
167
|
+
emberDia: 1.2 + rnd() * 0.6
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return rays;
|
|
171
|
+
}
|
|
172
|
+
const BURSTS = METEORS.map((_, i) => buildBurst(i));
|
|
53
173
|
const Meteor = /*#__PURE__*/(0, _react.memo)(function Meteor({
|
|
54
174
|
index,
|
|
55
175
|
width,
|
|
@@ -57,6 +177,8 @@ const Meteor = /*#__PURE__*/(0, _react.memo)(function Meteor({
|
|
|
57
177
|
animated
|
|
58
178
|
}) {
|
|
59
179
|
const m = METEORS[index];
|
|
180
|
+
// What this meteor is made of — rolled once per mount, held for its life.
|
|
181
|
+
const [mineral] = (0, _react.useState)(pickMineral);
|
|
60
182
|
const t = (0, _primitives.useLoop)(m.periodMs, animated);
|
|
61
183
|
const [a, b] = m.window;
|
|
62
184
|
const x0 = m.from[0] * width;
|
|
@@ -96,12 +218,12 @@ const Meteor = /*#__PURE__*/(0, _react.memo)(function Meteor({
|
|
|
96
218
|
width: d,
|
|
97
219
|
height: d,
|
|
98
220
|
borderRadius: d / 2,
|
|
99
|
-
backgroundColor: (0, _rng.withAlpha)(
|
|
221
|
+
backgroundColor: (0, _rng.withAlpha)(mineral.trail, 0.68 - i * 0.08)
|
|
100
222
|
}
|
|
101
223
|
}, i));
|
|
102
224
|
}
|
|
103
225
|
|
|
104
|
-
// --- The impact
|
|
226
|
+
// --- The impact: firework blow-out --------------------------------------
|
|
105
227
|
// Everything below reads the SAME loop value; the burst simply lives in the
|
|
106
228
|
// slice of the timeline right after the streak arrives at the wall.
|
|
107
229
|
const flashOpacity = t.interpolate({
|
|
@@ -109,56 +231,94 @@ const Meteor = /*#__PURE__*/(0, _react.memo)(function Meteor({
|
|
|
109
231
|
outputRange: [0, 0, 1, 0, 0]
|
|
110
232
|
});
|
|
111
233
|
const flashScale = t.interpolate({
|
|
112
|
-
inputRange: [0, b, Math.min(b + 0.
|
|
113
|
-
outputRange: [0.
|
|
114
|
-
});
|
|
115
|
-
const waveOpacity = t.interpolate({
|
|
116
|
-
inputRange: [0, b, Math.min(b + 0.02, 0.999), Math.min(b + 0.11, 0.9995), 1],
|
|
117
|
-
outputRange: [0, 0, 0.85, 0, 0]
|
|
118
|
-
});
|
|
119
|
-
const waveScale = t.interpolate({
|
|
120
|
-
inputRange: [0, b, Math.min(b + 0.11, 0.999), 1],
|
|
121
|
-
outputRange: [0.12, 0.12, 1, 1]
|
|
234
|
+
inputRange: [0, b, Math.min(b + 0.05, 0.999), 1],
|
|
235
|
+
outputRange: [0.5, 0.5, 1.05, 1.05]
|
|
122
236
|
});
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
237
|
+
const rays = [];
|
|
238
|
+
const embers = [];
|
|
239
|
+
const burst = BURSTS[index];
|
|
240
|
+
for (let i = 0; i < burst.length; i++) {
|
|
241
|
+
const r = burst[i];
|
|
242
|
+
// The ray's timeline: head races out over `grow`, the tail leaves the
|
|
243
|
+
// impact point once the head is under way and catches it shortly after —
|
|
244
|
+
// the segment between them is the burning trail.
|
|
245
|
+
const t0 = b + r.delay;
|
|
246
|
+
const k1 = t0 + r.grow * 0.5; // head at 75%, tail just leaving
|
|
247
|
+
const k2 = t0 + r.grow; // head at full reach, tail halfway
|
|
248
|
+
const k3 = k2 + 0.035; // tail catches the head — ray gone
|
|
249
|
+
// Segment endpoints head/tail (fractions of dist):
|
|
250
|
+
// t0 → 0/0, k1 → 0.75/0.1, k2 → 1/0.5, k3 → 1/1
|
|
251
|
+
// scaleX = head − tail, translateX = dist · (head + tail)/2.
|
|
252
|
+
const len = t.interpolate({
|
|
253
|
+
inputRange: [0, t0, k1, k2, k3, 1],
|
|
254
|
+
outputRange: [0, 0, 0.65, 0.5, 0, 0]
|
|
134
255
|
});
|
|
135
|
-
const
|
|
136
|
-
inputRange: [0,
|
|
137
|
-
outputRange: [0, 0,
|
|
256
|
+
const mid = t.interpolate({
|
|
257
|
+
inputRange: [0, t0, k1, k2, k3, 1],
|
|
258
|
+
outputRange: [0, 0, r.dist * 0.425, r.dist * 0.75, r.dist, r.dist]
|
|
138
259
|
});
|
|
139
|
-
const
|
|
140
|
-
inputRange: [0,
|
|
141
|
-
outputRange: [0, 0, 0.
|
|
260
|
+
const rayOpacity = t.interpolate({
|
|
261
|
+
inputRange: [0, t0, t0 + 0.006, k2, k3, 1],
|
|
262
|
+
outputRange: [0, 0, 0.85, 0.7, 0, 0]
|
|
142
263
|
});
|
|
143
|
-
|
|
144
|
-
sparks.push(/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
264
|
+
rays.push(/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
145
265
|
style: {
|
|
146
266
|
position: "absolute",
|
|
147
|
-
left: x1 -
|
|
148
|
-
top: y1 -
|
|
149
|
-
width:
|
|
150
|
-
height:
|
|
151
|
-
borderRadius:
|
|
152
|
-
backgroundColor: (0, _rng.withAlpha)(
|
|
153
|
-
opacity:
|
|
267
|
+
left: x1 - r.dist / 2,
|
|
268
|
+
top: y1 - r.thick / 2,
|
|
269
|
+
width: r.dist,
|
|
270
|
+
height: r.thick,
|
|
271
|
+
borderRadius: r.thick / 2,
|
|
272
|
+
backgroundColor: (0, _rng.withAlpha)(mineral.rays[i % mineral.rays.length], 0.8),
|
|
273
|
+
opacity: rayOpacity,
|
|
274
|
+
// Rotate is OUTERMOST, so the translate runs along the ray's own
|
|
275
|
+
// axis — see the header note on faking a transform origin.
|
|
154
276
|
transform: [{
|
|
155
|
-
|
|
277
|
+
rotate: `${r.rad * 180 / Math.PI}deg`
|
|
156
278
|
}, {
|
|
157
|
-
|
|
279
|
+
translateX: mid
|
|
280
|
+
}, {
|
|
281
|
+
scaleX: len
|
|
158
282
|
}]
|
|
159
283
|
},
|
|
160
284
|
pointerEvents: "none"
|
|
161
|
-
}, i));
|
|
285
|
+
}, `ray-${i}`));
|
|
286
|
+
if (!r.ember) continue;
|
|
287
|
+
// The ember rides the ray's head, then keeps coasting a touch further
|
|
288
|
+
// while gravity drags it DOWN in world space, guttering out last.
|
|
289
|
+
const e3 = k3 + 0.04;
|
|
290
|
+
const cos = Math.cos(r.rad);
|
|
291
|
+
const sin = Math.sin(r.rad);
|
|
292
|
+
const ex = t.interpolate({
|
|
293
|
+
inputRange: [0, t0, k1, k2, e3, 1],
|
|
294
|
+
outputRange: [0, 0, cos * r.dist * 0.75, cos * r.dist, cos * (r.dist + r.emberDrift), cos * (r.dist + r.emberDrift)]
|
|
295
|
+
});
|
|
296
|
+
const ey = t.interpolate({
|
|
297
|
+
inputRange: [0, t0, k1, k2, e3, 1],
|
|
298
|
+
outputRange: [0, 0, sin * r.dist * 0.75, sin * r.dist, sin * (r.dist + r.emberDrift) + r.emberDrop, sin * (r.dist + r.emberDrift) + r.emberDrop]
|
|
299
|
+
});
|
|
300
|
+
const eo = t.interpolate({
|
|
301
|
+
inputRange: [0, t0, k1, k2, e3, 1],
|
|
302
|
+
outputRange: [0, 0, 0.8, 0.65, 0, 0]
|
|
303
|
+
});
|
|
304
|
+
embers.push(/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
305
|
+
style: {
|
|
306
|
+
position: "absolute",
|
|
307
|
+
left: x1 - r.emberDia / 2,
|
|
308
|
+
top: y1 - r.emberDia / 2,
|
|
309
|
+
width: r.emberDia,
|
|
310
|
+
height: r.emberDia,
|
|
311
|
+
borderRadius: r.emberDia / 2,
|
|
312
|
+
backgroundColor: (0, _rng.withAlpha)(mineral.head, 1),
|
|
313
|
+
opacity: eo,
|
|
314
|
+
transform: [{
|
|
315
|
+
translateX: ex
|
|
316
|
+
}, {
|
|
317
|
+
translateY: ey
|
|
318
|
+
}]
|
|
319
|
+
},
|
|
320
|
+
pointerEvents: "none"
|
|
321
|
+
}, `ember-${i}`));
|
|
162
322
|
}
|
|
163
323
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
164
324
|
style: _absoluteFill.absoluteFill,
|
|
@@ -188,7 +348,7 @@ const Meteor = /*#__PURE__*/(0, _react.memo)(function Meteor({
|
|
|
188
348
|
width: 20,
|
|
189
349
|
height: 20,
|
|
190
350
|
borderRadius: 10,
|
|
191
|
-
backgroundColor: (0, _rng.withAlpha)(
|
|
351
|
+
backgroundColor: (0, _rng.withAlpha)(mineral.glow, 0.16)
|
|
192
352
|
}
|
|
193
353
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
194
354
|
style: {
|
|
@@ -198,16 +358,16 @@ const Meteor = /*#__PURE__*/(0, _react.memo)(function Meteor({
|
|
|
198
358
|
width: 3,
|
|
199
359
|
height: 3,
|
|
200
360
|
borderRadius: 1.5,
|
|
201
|
-
backgroundColor: (0, _rng.withAlpha)(
|
|
361
|
+
backgroundColor: (0, _rng.withAlpha)(mineral.head, 1)
|
|
202
362
|
}
|
|
203
363
|
}), trail]
|
|
204
364
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
205
365
|
style: {
|
|
206
366
|
position: "absolute",
|
|
207
|
-
left: x1 -
|
|
208
|
-
top: y1 -
|
|
209
|
-
width:
|
|
210
|
-
height:
|
|
367
|
+
left: x1 - 18,
|
|
368
|
+
top: y1 - 18,
|
|
369
|
+
width: 36,
|
|
370
|
+
height: 36,
|
|
211
371
|
opacity: flashOpacity,
|
|
212
372
|
transform: [{
|
|
213
373
|
scale: flashScale
|
|
@@ -215,29 +375,13 @@ const Meteor = /*#__PURE__*/(0, _react.memo)(function Meteor({
|
|
|
215
375
|
},
|
|
216
376
|
pointerEvents: "none",
|
|
217
377
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_primitives.RadialGlow, {
|
|
218
|
-
size:
|
|
219
|
-
color:
|
|
220
|
-
alpha: 0.
|
|
221
|
-
rings:
|
|
378
|
+
size: 36,
|
|
379
|
+
color: mineral.glow,
|
|
380
|
+
alpha: 0.45,
|
|
381
|
+
rings: 5,
|
|
222
382
|
falloff: 1.6
|
|
223
383
|
})
|
|
224
|
-
}),
|
|
225
|
-
style: {
|
|
226
|
-
position: "absolute",
|
|
227
|
-
left: x1 - SHOCKWAVE_DIA / 2,
|
|
228
|
-
top: y1 - SHOCKWAVE_DIA / 2,
|
|
229
|
-
width: SHOCKWAVE_DIA,
|
|
230
|
-
height: SHOCKWAVE_DIA,
|
|
231
|
-
borderRadius: SHOCKWAVE_DIA / 2,
|
|
232
|
-
borderWidth: 1.5,
|
|
233
|
-
borderColor: (0, _rng.withAlpha)("#BBE5FA", 0.9),
|
|
234
|
-
opacity: waveOpacity,
|
|
235
|
-
transform: [{
|
|
236
|
-
scale: waveScale
|
|
237
|
-
}]
|
|
238
|
-
},
|
|
239
|
-
pointerEvents: "none"
|
|
240
|
-
}), sparks]
|
|
384
|
+
}), rays, embers]
|
|
241
385
|
});
|
|
242
386
|
});
|
|
243
387
|
|
|
@@ -106,7 +106,8 @@ function TypeBadge({
|
|
|
106
106
|
type,
|
|
107
107
|
color,
|
|
108
108
|
size = "small",
|
|
109
|
-
style
|
|
109
|
+
style,
|
|
110
|
+
testID
|
|
110
111
|
}) {
|
|
111
112
|
const typeColor = color || getTypeColor(type);
|
|
112
113
|
const badgeStyles = getBadgeStyles("type", typeColor, size);
|
|
@@ -116,6 +117,7 @@ function TypeBadge({
|
|
|
116
117
|
style: [badgeStyles.text, {
|
|
117
118
|
color: typeColor
|
|
118
119
|
}],
|
|
120
|
+
testID: testID,
|
|
119
121
|
children: type
|
|
120
122
|
})
|
|
121
123
|
});
|
|
@@ -27,6 +27,8 @@ function CompactRow({
|
|
|
27
27
|
disabled,
|
|
28
28
|
expandedGlowColor,
|
|
29
29
|
testID,
|
|
30
|
+
statusTestID,
|
|
31
|
+
statusSublabelTestID,
|
|
30
32
|
accessibilityLabel
|
|
31
33
|
}) {
|
|
32
34
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
@@ -45,7 +47,12 @@ function CompactRow({
|
|
|
45
47
|
activeOpacity: 0.8,
|
|
46
48
|
disabled: disabled || !onPress,
|
|
47
49
|
testID: testID,
|
|
48
|
-
accessibilityLabel: accessibilityLabel
|
|
50
|
+
accessibilityLabel: accessibilityLabel
|
|
51
|
+
// Label alone is not enough on Fabric: a row that announces text but
|
|
52
|
+
// no role reads to VoiceOver as static content, so the fact that it
|
|
53
|
+
// opens something is only discoverable by tapping it.
|
|
54
|
+
,
|
|
55
|
+
accessibilityRole: onPress ? "button" : undefined,
|
|
49
56
|
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
50
57
|
style: styles.rowContent,
|
|
51
58
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
@@ -61,10 +68,12 @@ function CompactRow({
|
|
|
61
68
|
color: statusDotColor
|
|
62
69
|
}],
|
|
63
70
|
numberOfLines: 1,
|
|
71
|
+
testID: statusTestID,
|
|
64
72
|
children: statusLabel
|
|
65
73
|
}), statusSublabel ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
66
74
|
style: styles.observerText,
|
|
67
75
|
numberOfLines: 1,
|
|
76
|
+
testID: statusSublabelTestID,
|
|
68
77
|
children: statusSublabel
|
|
69
78
|
}) : null]
|
|
70
79
|
})]
|
|
@@ -265,7 +265,8 @@ const ActionCopyButton = exports.ActionCopyButton = /*#__PURE__*/(0, _react.memo
|
|
|
265
265
|
const ToolbarClearButton = exports.ToolbarClearButton = /*#__PURE__*/(0, _react.memo)(function ToolbarClearButton({
|
|
266
266
|
onPress,
|
|
267
267
|
disabled = false,
|
|
268
|
-
buttonStyle
|
|
268
|
+
buttonStyle,
|
|
269
|
+
testID
|
|
269
270
|
}) {
|
|
270
271
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
271
272
|
onPress: onPress,
|
|
@@ -277,6 +278,7 @@ const ToolbarClearButton = exports.ToolbarClearButton = /*#__PURE__*/(0, _react.
|
|
|
277
278
|
left: 8,
|
|
278
279
|
right: 8
|
|
279
280
|
},
|
|
281
|
+
testID: testID,
|
|
280
282
|
accessibilityLabel: "Clear",
|
|
281
283
|
accessibilityRole: "button",
|
|
282
284
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_lucideIcons.Trash2, {
|
|
@@ -40,7 +40,9 @@ const DraggableHeader = exports.DraggableHeader = /*#__PURE__*/(0, _react.memo)(
|
|
|
40
40
|
},
|
|
41
41
|
style,
|
|
42
42
|
enabled = true,
|
|
43
|
-
maxOverflowX = 0
|
|
43
|
+
maxOverflowX = 0,
|
|
44
|
+
testID,
|
|
45
|
+
accessibilityLabel
|
|
44
46
|
}) {
|
|
45
47
|
const isDraggingRef = (0, _react.useRef)(false);
|
|
46
48
|
const dragDistanceRef = (0, _react.useRef)(0);
|
|
@@ -190,6 +192,8 @@ const DraggableHeader = exports.DraggableHeader = /*#__PURE__*/(0, _react.memo)(
|
|
|
190
192
|
}, [position]);
|
|
191
193
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
192
194
|
style: style,
|
|
195
|
+
testID: testID,
|
|
196
|
+
accessibilityLabel: accessibilityLabel,
|
|
193
197
|
...panResponder.panHandlers,
|
|
194
198
|
children: children
|
|
195
199
|
});
|