@buoy-gg/shared-ui 7.0.28 → 7.0.29
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
|
@@ -822,7 +822,8 @@ const VirtualizedItemComponent = ({
|
|
|
822
822
|
selectable,
|
|
823
823
|
onRequestInlineEdit,
|
|
824
824
|
isInlineEditing,
|
|
825
|
-
inlineEditor
|
|
825
|
+
inlineEditor,
|
|
826
|
+
testIDPrefix
|
|
826
827
|
}) => {
|
|
827
828
|
const [isPressed, setIsPressed] = useState(false);
|
|
828
829
|
const [showFullKey, setShowFullKey] = useState(false);
|
|
@@ -891,7 +892,13 @@ const VirtualizedItemComponent = ({
|
|
|
891
892
|
return /*#__PURE__*/_jsxs(View, {
|
|
892
893
|
style: [STABLE_STYLES.itemContainer, indentStyle],
|
|
893
894
|
children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
894
|
-
"sentry-label": "ignore devtools data explorer item"
|
|
895
|
+
"sentry-label": "ignore devtools data explorer item"
|
|
896
|
+
// `item.id` is the dotted path from the root ("root.settings.theme"),
|
|
897
|
+
// so this handle addresses a NODE rather than a list position — stable
|
|
898
|
+
// across expand/collapse and across re-renders.
|
|
899
|
+
,
|
|
900
|
+
testID: `${testIDPrefix}-${item.id}`,
|
|
901
|
+
accessibilityLabel: `${item.key}: ${item.valueType}`,
|
|
895
902
|
style: [STABLE_STYLES.itemTouchable, isPressed && STABLE_STYLES.itemTouchablePressed,
|
|
896
903
|
// The selected wash exists for the action-bar/edit flow. In a plain
|
|
897
904
|
// read-only viewer every expand/collapse tap ALSO selects, which
|
|
@@ -966,7 +973,8 @@ export const VirtualizedDataExplorer = ({
|
|
|
966
973
|
onRequestInlineEdit,
|
|
967
974
|
inlineEditPath,
|
|
968
975
|
renderInlineEditor,
|
|
969
|
-
selectPath
|
|
976
|
+
selectPath,
|
|
977
|
+
testIDPrefix = "buoy-tree-row"
|
|
970
978
|
}) => {
|
|
971
979
|
const [isExpanded, setIsExpanded] = useState(rawMode); // Auto-expand in raw mode
|
|
972
980
|
const {
|
|
@@ -1082,7 +1090,8 @@ export const VirtualizedDataExplorer = ({
|
|
|
1082
1090
|
selectable: onSelectionChange != null,
|
|
1083
1091
|
onRequestInlineEdit: handleRequestInlineEdit,
|
|
1084
1092
|
isInlineEditing: inlineEditPath != null && inlineEditPath.join(".") === item.path.slice(1).join("."),
|
|
1085
|
-
inlineEditor: renderInlineEditor ? renderInlineEditor(selectionOf(item)) : null
|
|
1093
|
+
inlineEditor: renderInlineEditor ? renderInlineEditor(selectionOf(item)) : null,
|
|
1094
|
+
testIDPrefix: testIDPrefix
|
|
1086
1095
|
});
|
|
1087
1096
|
|
|
1088
1097
|
// Uniform row height for crisp guide geometry
|
|
@@ -379,11 +379,20 @@ export const UpgradeModal = ({
|
|
|
379
379
|
style: styles.modalContent,
|
|
380
380
|
activeOpacity: 1,
|
|
381
381
|
onPress: () => {} // Prevent overlay dismiss when tapping content
|
|
382
|
+
// The handle a test needs to say a Pro gate did NOT fire. Without
|
|
383
|
+
// it "the action worked" and "the upsell appeared over it" are
|
|
384
|
+
// told apart only by what happened next, which is exactly the
|
|
385
|
+
// assertion that was supposed to be the proof.
|
|
382
386
|
,
|
|
387
|
+
testID: "buoy-upgrade-modal",
|
|
388
|
+
accessibilityLabel: `Unlock ${featureName}`,
|
|
383
389
|
children: [/*#__PURE__*/_jsx(TouchableOpacity, {
|
|
384
390
|
style: styles.modalCloseButton,
|
|
385
391
|
onPress: onClose,
|
|
386
392
|
activeOpacity: 0.7,
|
|
393
|
+
testID: "buoy-upgrade-modal-close",
|
|
394
|
+
accessibilityRole: "button",
|
|
395
|
+
accessibilityLabel: "Close upgrade prompt",
|
|
387
396
|
children: /*#__PURE__*/_jsx(X, {
|
|
388
397
|
size: 18,
|
|
389
398
|
color: buoyColors.textMuted
|
|
@@ -25,9 +25,10 @@ export const BACKGROUND_PRESETS = [{
|
|
|
25
25
|
id: "deepfield",
|
|
26
26
|
label: "Deep Field",
|
|
27
27
|
blurb: "The Everlights field — 160 stars, with meteors slamming the walls",
|
|
28
|
-
// The old standalone Meteors preset folded in: +2 meteor loops, ~
|
|
28
|
+
// The old standalone Meteors preset folded in: +2 meteor loops, ~55 views
|
|
29
|
+
// (streak + flash + the pop's 6 rays and ~3 embers per impact).
|
|
29
30
|
nodes: 24,
|
|
30
|
-
views:
|
|
31
|
+
views: 515,
|
|
31
32
|
Component: DeepField
|
|
32
33
|
}, {
|
|
33
34
|
id: "hyperspace",
|
|
@@ -4,12 +4,28 @@
|
|
|
4
4
|
* MeteorShower — shooting stars that slam into the walls.
|
|
5
5
|
*
|
|
6
6
|
* Each meteor streaks in from off-screen, hits an EDGE of the surface, and
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* blows out like a firework: a flash, then a fan of streak RAYS that shoot
|
|
8
|
+
* back into the room — each ray grows out of the impact point while its tail
|
|
9
|
+
* chases the head, so it reads as a burning trail rather than a ring — with
|
|
10
|
+
* ember dots at the tips that decelerate, droop under gravity and gutter out.
|
|
11
|
+
* The whole life cycle — parked invisible, streak, impact, decay — is
|
|
12
|
+
* keyframed into interpolate stops on ONE plain native loop per meteor;
|
|
13
|
+
* `Animated.sequence([timing, loop])` silently fails to drive natively, so
|
|
14
|
+
* nothing here sequences.
|
|
15
|
+
*
|
|
16
|
+
* Each meteor also rolls WHAT IT'S MADE OF once per mount — a weighted draw
|
|
17
|
+
* over the real emission-line palette (sodium orange-yellow, iron yellow,
|
|
18
|
+
* magnesium blue-green, calcium violet, shocked atmospheric N₂/O₂ red,
|
|
19
|
+
* ionized-air blue-white) — and that mineral tints streak, trail, flash,
|
|
20
|
+
* rays and embers as one. See `MINERALS`.
|
|
21
|
+
*
|
|
22
|
+
* The ray trick, since there is no `transformOrigin`: a bar of full length
|
|
23
|
+
* `dist` is CENTERED on the impact point, rotated to its heading, then driven
|
|
24
|
+
* with `[{rotate}, {translateX}, {scaleX}]`. Rotate is outermost, so the
|
|
25
|
+
* translate runs along the ray's own axis; scaleX squashes about the bar's
|
|
26
|
+
* center, and translating by the midpoint of (head, tail) while scaling by
|
|
27
|
+
* (head − tail) puts the visible segment exactly between the two — a line
|
|
28
|
+
* growing out of the burst with both endpoints scripted.
|
|
13
29
|
*
|
|
14
30
|
* This used to be a standalone "Meteors" preset with its own 40-star sky;
|
|
15
31
|
* that scene was folded into Deep Field (which supplies the far better sky),
|
|
@@ -20,16 +36,16 @@
|
|
|
20
36
|
* `transformOrigin`.
|
|
21
37
|
*/
|
|
22
38
|
|
|
23
|
-
import { memo } from "react";
|
|
39
|
+
import { memo, useState } from "react";
|
|
24
40
|
import { Animated, View } from "react-native";
|
|
25
41
|
import { absoluteFill } from "../../../utils/absoluteFill.js";
|
|
26
42
|
import { RadialGlow, useLoop } from "../primitives.js";
|
|
27
|
-
import { withAlpha } from "../rng.js";
|
|
43
|
+
import { mulberry32, withAlpha } from "../rng.js";
|
|
28
44
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
29
45
|
/**
|
|
30
46
|
* `to` is ON a wall — that's the impact point. `sparkArc` is the hemisphere
|
|
31
|
-
* (in degrees)
|
|
32
|
-
* meteor just hit. Different periods keep the two impacts from syncing up.
|
|
47
|
+
* (in degrees) the firework rays fan into, pointed back away from the wall
|
|
48
|
+
* the meteor just hit. Different periods keep the two impacts from syncing up.
|
|
33
49
|
*/
|
|
34
50
|
const METEORS = [{
|
|
35
51
|
from: [0.08, -0.06],
|
|
@@ -44,8 +60,112 @@ const METEORS = [{
|
|
|
44
60
|
window: [0.56, 0.66],
|
|
45
61
|
sparkArc: [-70, 70] // left wall → sparks fly right
|
|
46
62
|
}];
|
|
47
|
-
const
|
|
48
|
-
|
|
63
|
+
const RAYS_PER_IMPACT = 6;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Real meteor color comes from what's burning. The dominant visible emission
|
|
67
|
+
* lines are sodium (orange-yellow), iron (yellow) and magnesium (blue-green);
|
|
68
|
+
* calcium-rich rock flares violet, and the shocked ATMOSPHERE itself — the
|
|
69
|
+
* excited nitrogen/oxygen sheath around the meteoroid — glows red. Faint fast
|
|
70
|
+
* meteors mostly show the blue-white of ionized air. The weights lean the
|
|
71
|
+
* same way the sky does: yellows and oranges common, teal frequent, red
|
|
72
|
+
* occasional, violet the rare treat.
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
const MINERALS = [{
|
|
76
|
+
name: "sodium",
|
|
77
|
+
weight: 26,
|
|
78
|
+
head: "#FFF3DC",
|
|
79
|
+
glow: "#FFDCA8",
|
|
80
|
+
trail: "#FFCB85",
|
|
81
|
+
rays: ["#FFE8C2", "#FFCB85", "#F0B45E"]
|
|
82
|
+
}, {
|
|
83
|
+
name: "iron",
|
|
84
|
+
weight: 24,
|
|
85
|
+
head: "#FFF9E3",
|
|
86
|
+
glow: "#FFEDAE",
|
|
87
|
+
trail: "#F2DC8A",
|
|
88
|
+
rays: ["#FBF0C0", "#F2DC8A", "#E3C868"]
|
|
89
|
+
}, {
|
|
90
|
+
name: "magnesium",
|
|
91
|
+
weight: 20,
|
|
92
|
+
head: "#EAFFF9",
|
|
93
|
+
glow: "#ACF0DE",
|
|
94
|
+
trail: "#82E3CA",
|
|
95
|
+
rays: ["#D3FAEF", "#82E3CA", "#5FD6BC"]
|
|
96
|
+
}, {
|
|
97
|
+
// Not a mineral at all — ionized air, the old blue-white default.
|
|
98
|
+
name: "ionized-air",
|
|
99
|
+
weight: 14,
|
|
100
|
+
head: "#F5F9FF",
|
|
101
|
+
glow: "#E5EEFC",
|
|
102
|
+
trail: "#C9D9F0",
|
|
103
|
+
rays: ["#F5F9FF", "#DCE9FF", "#BBE5FA"]
|
|
104
|
+
}, {
|
|
105
|
+
// Also atmospheric: nitrogen/oxygen shocked red-hot in the sheath.
|
|
106
|
+
name: "nitrogen-oxygen",
|
|
107
|
+
weight: 10,
|
|
108
|
+
head: "#FFEAE5",
|
|
109
|
+
glow: "#FFB4A6",
|
|
110
|
+
trail: "#FF917E",
|
|
111
|
+
rays: ["#FFCFC6", "#FF917E", "#F27461"]
|
|
112
|
+
}, {
|
|
113
|
+
name: "calcium",
|
|
114
|
+
weight: 6,
|
|
115
|
+
head: "#F6EEFF",
|
|
116
|
+
glow: "#DCC2FF",
|
|
117
|
+
trail: "#C29BF7",
|
|
118
|
+
rays: ["#EADCFF", "#C29BF7", "#AB7EEC"]
|
|
119
|
+
}];
|
|
120
|
+
const MINERAL_WEIGHT = MINERALS.reduce((sum, m) => sum + m.weight, 0);
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Weighted draw — `Math.random` ON PURPOSE, unlike the seeded burst geometry:
|
|
124
|
+
* each meteor rolls its make-up once per mount, so every session's sky burns
|
|
125
|
+
* a different mix while the animation itself stays deterministic.
|
|
126
|
+
*/
|
|
127
|
+
function pickMineral() {
|
|
128
|
+
let r = Math.random() * MINERAL_WEIGHT;
|
|
129
|
+
for (const m of MINERALS) {
|
|
130
|
+
r -= m.weight;
|
|
131
|
+
if (r <= 0) return m;
|
|
132
|
+
}
|
|
133
|
+
return MINERALS[0];
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The firework: a deterministic fan of rays per impact, seeded so both
|
|
137
|
+
* platforms (and every reload) burst identically. Angles are stratified
|
|
138
|
+
* across the arc with jitter, so the fan covers it without reading as a
|
|
139
|
+
* perfect star; reach, thickness and stagger vary per ray so it reads as
|
|
140
|
+
* debris blowing out, not a rigid spoke wheel.
|
|
141
|
+
*/
|
|
142
|
+
function buildBurst(index) {
|
|
143
|
+
const m = METEORS[index];
|
|
144
|
+
const rnd = mulberry32((0xb1a57 ^ index) >>> 0);
|
|
145
|
+
const [a0, a1] = m.sparkArc;
|
|
146
|
+
const rays = [];
|
|
147
|
+
for (let i = 0; i < RAYS_PER_IMPACT; i++) {
|
|
148
|
+
const frac = (i + 0.5) / RAYS_PER_IMPACT;
|
|
149
|
+
const jitter = (rnd() - 0.5) * 0.7 / RAYS_PER_IMPACT;
|
|
150
|
+
const deg = a0 + (a1 - a0) * (frac + jitter);
|
|
151
|
+
// Small on purpose — a game-sprite pop, not a fireworks display: rays
|
|
152
|
+
// reach 10–22px, a hair over 1px thick, and the whole thing is over in a
|
|
153
|
+
// few frames.
|
|
154
|
+
rays.push({
|
|
155
|
+
rad: deg * Math.PI / 180,
|
|
156
|
+
dist: 10 + rnd() * 12,
|
|
157
|
+
thick: 1 + rnd() * 0.5,
|
|
158
|
+
delay: rnd() * 0.008,
|
|
159
|
+
grow: 0.03 + rnd() * 0.015,
|
|
160
|
+
ember: rnd() > 0.5,
|
|
161
|
+
emberDrop: 2 + rnd() * 3,
|
|
162
|
+
emberDrift: 1 + rnd() * 3,
|
|
163
|
+
emberDia: 1.2 + rnd() * 0.6
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
return rays;
|
|
167
|
+
}
|
|
168
|
+
const BURSTS = METEORS.map((_, i) => buildBurst(i));
|
|
49
169
|
const Meteor = /*#__PURE__*/memo(function Meteor({
|
|
50
170
|
index,
|
|
51
171
|
width,
|
|
@@ -53,6 +173,8 @@ const Meteor = /*#__PURE__*/memo(function Meteor({
|
|
|
53
173
|
animated
|
|
54
174
|
}) {
|
|
55
175
|
const m = METEORS[index];
|
|
176
|
+
// What this meteor is made of — rolled once per mount, held for its life.
|
|
177
|
+
const [mineral] = useState(pickMineral);
|
|
56
178
|
const t = useLoop(m.periodMs, animated);
|
|
57
179
|
const [a, b] = m.window;
|
|
58
180
|
const x0 = m.from[0] * width;
|
|
@@ -92,12 +214,12 @@ const Meteor = /*#__PURE__*/memo(function Meteor({
|
|
|
92
214
|
width: d,
|
|
93
215
|
height: d,
|
|
94
216
|
borderRadius: d / 2,
|
|
95
|
-
backgroundColor: withAlpha(
|
|
217
|
+
backgroundColor: withAlpha(mineral.trail, 0.68 - i * 0.08)
|
|
96
218
|
}
|
|
97
219
|
}, i));
|
|
98
220
|
}
|
|
99
221
|
|
|
100
|
-
// --- The impact
|
|
222
|
+
// --- The impact: firework blow-out --------------------------------------
|
|
101
223
|
// Everything below reads the SAME loop value; the burst simply lives in the
|
|
102
224
|
// slice of the timeline right after the streak arrives at the wall.
|
|
103
225
|
const flashOpacity = t.interpolate({
|
|
@@ -105,56 +227,94 @@ const Meteor = /*#__PURE__*/memo(function Meteor({
|
|
|
105
227
|
outputRange: [0, 0, 1, 0, 0]
|
|
106
228
|
});
|
|
107
229
|
const flashScale = t.interpolate({
|
|
108
|
-
inputRange: [0, b, Math.min(b + 0.
|
|
109
|
-
outputRange: [0.
|
|
110
|
-
});
|
|
111
|
-
const waveOpacity = t.interpolate({
|
|
112
|
-
inputRange: [0, b, Math.min(b + 0.02, 0.999), Math.min(b + 0.11, 0.9995), 1],
|
|
113
|
-
outputRange: [0, 0, 0.85, 0, 0]
|
|
114
|
-
});
|
|
115
|
-
const waveScale = t.interpolate({
|
|
116
|
-
inputRange: [0, b, Math.min(b + 0.11, 0.999), 1],
|
|
117
|
-
outputRange: [0.12, 0.12, 1, 1]
|
|
230
|
+
inputRange: [0, b, Math.min(b + 0.05, 0.999), 1],
|
|
231
|
+
outputRange: [0.5, 0.5, 1.05, 1.05]
|
|
118
232
|
});
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
233
|
+
const rays = [];
|
|
234
|
+
const embers = [];
|
|
235
|
+
const burst = BURSTS[index];
|
|
236
|
+
for (let i = 0; i < burst.length; i++) {
|
|
237
|
+
const r = burst[i];
|
|
238
|
+
// The ray's timeline: head races out over `grow`, the tail leaves the
|
|
239
|
+
// impact point once the head is under way and catches it shortly after —
|
|
240
|
+
// the segment between them is the burning trail.
|
|
241
|
+
const t0 = b + r.delay;
|
|
242
|
+
const k1 = t0 + r.grow * 0.5; // head at 75%, tail just leaving
|
|
243
|
+
const k2 = t0 + r.grow; // head at full reach, tail halfway
|
|
244
|
+
const k3 = k2 + 0.035; // tail catches the head — ray gone
|
|
245
|
+
// Segment endpoints head/tail (fractions of dist):
|
|
246
|
+
// t0 → 0/0, k1 → 0.75/0.1, k2 → 1/0.5, k3 → 1/1
|
|
247
|
+
// scaleX = head − tail, translateX = dist · (head + tail)/2.
|
|
248
|
+
const len = t.interpolate({
|
|
249
|
+
inputRange: [0, t0, k1, k2, k3, 1],
|
|
250
|
+
outputRange: [0, 0, 0.65, 0.5, 0, 0]
|
|
130
251
|
});
|
|
131
|
-
const
|
|
132
|
-
inputRange: [0,
|
|
133
|
-
outputRange: [0, 0,
|
|
252
|
+
const mid = t.interpolate({
|
|
253
|
+
inputRange: [0, t0, k1, k2, k3, 1],
|
|
254
|
+
outputRange: [0, 0, r.dist * 0.425, r.dist * 0.75, r.dist, r.dist]
|
|
134
255
|
});
|
|
135
|
-
const
|
|
136
|
-
inputRange: [0,
|
|
137
|
-
outputRange: [0, 0, 0.
|
|
256
|
+
const rayOpacity = t.interpolate({
|
|
257
|
+
inputRange: [0, t0, t0 + 0.006, k2, k3, 1],
|
|
258
|
+
outputRange: [0, 0, 0.85, 0.7, 0, 0]
|
|
138
259
|
});
|
|
139
|
-
|
|
140
|
-
sparks.push(/*#__PURE__*/_jsx(Animated.View, {
|
|
260
|
+
rays.push(/*#__PURE__*/_jsx(Animated.View, {
|
|
141
261
|
style: {
|
|
142
262
|
position: "absolute",
|
|
143
|
-
left: x1 -
|
|
144
|
-
top: y1 -
|
|
145
|
-
width:
|
|
146
|
-
height:
|
|
147
|
-
borderRadius:
|
|
148
|
-
backgroundColor: withAlpha(
|
|
149
|
-
opacity:
|
|
263
|
+
left: x1 - r.dist / 2,
|
|
264
|
+
top: y1 - r.thick / 2,
|
|
265
|
+
width: r.dist,
|
|
266
|
+
height: r.thick,
|
|
267
|
+
borderRadius: r.thick / 2,
|
|
268
|
+
backgroundColor: withAlpha(mineral.rays[i % mineral.rays.length], 0.8),
|
|
269
|
+
opacity: rayOpacity,
|
|
270
|
+
// Rotate is OUTERMOST, so the translate runs along the ray's own
|
|
271
|
+
// axis — see the header note on faking a transform origin.
|
|
150
272
|
transform: [{
|
|
151
|
-
|
|
273
|
+
rotate: `${r.rad * 180 / Math.PI}deg`
|
|
152
274
|
}, {
|
|
153
|
-
|
|
275
|
+
translateX: mid
|
|
276
|
+
}, {
|
|
277
|
+
scaleX: len
|
|
154
278
|
}]
|
|
155
279
|
},
|
|
156
280
|
pointerEvents: "none"
|
|
157
|
-
}, i));
|
|
281
|
+
}, `ray-${i}`));
|
|
282
|
+
if (!r.ember) continue;
|
|
283
|
+
// The ember rides the ray's head, then keeps coasting a touch further
|
|
284
|
+
// while gravity drags it DOWN in world space, guttering out last.
|
|
285
|
+
const e3 = k3 + 0.04;
|
|
286
|
+
const cos = Math.cos(r.rad);
|
|
287
|
+
const sin = Math.sin(r.rad);
|
|
288
|
+
const ex = t.interpolate({
|
|
289
|
+
inputRange: [0, t0, k1, k2, e3, 1],
|
|
290
|
+
outputRange: [0, 0, cos * r.dist * 0.75, cos * r.dist, cos * (r.dist + r.emberDrift), cos * (r.dist + r.emberDrift)]
|
|
291
|
+
});
|
|
292
|
+
const ey = t.interpolate({
|
|
293
|
+
inputRange: [0, t0, k1, k2, e3, 1],
|
|
294
|
+
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]
|
|
295
|
+
});
|
|
296
|
+
const eo = t.interpolate({
|
|
297
|
+
inputRange: [0, t0, k1, k2, e3, 1],
|
|
298
|
+
outputRange: [0, 0, 0.8, 0.65, 0, 0]
|
|
299
|
+
});
|
|
300
|
+
embers.push(/*#__PURE__*/_jsx(Animated.View, {
|
|
301
|
+
style: {
|
|
302
|
+
position: "absolute",
|
|
303
|
+
left: x1 - r.emberDia / 2,
|
|
304
|
+
top: y1 - r.emberDia / 2,
|
|
305
|
+
width: r.emberDia,
|
|
306
|
+
height: r.emberDia,
|
|
307
|
+
borderRadius: r.emberDia / 2,
|
|
308
|
+
backgroundColor: withAlpha(mineral.head, 1),
|
|
309
|
+
opacity: eo,
|
|
310
|
+
transform: [{
|
|
311
|
+
translateX: ex
|
|
312
|
+
}, {
|
|
313
|
+
translateY: ey
|
|
314
|
+
}]
|
|
315
|
+
},
|
|
316
|
+
pointerEvents: "none"
|
|
317
|
+
}, `ember-${i}`));
|
|
158
318
|
}
|
|
159
319
|
return /*#__PURE__*/_jsxs(View, {
|
|
160
320
|
style: absoluteFill,
|
|
@@ -184,7 +344,7 @@ const Meteor = /*#__PURE__*/memo(function Meteor({
|
|
|
184
344
|
width: 20,
|
|
185
345
|
height: 20,
|
|
186
346
|
borderRadius: 10,
|
|
187
|
-
backgroundColor: withAlpha(
|
|
347
|
+
backgroundColor: withAlpha(mineral.glow, 0.16)
|
|
188
348
|
}
|
|
189
349
|
}), /*#__PURE__*/_jsx(View, {
|
|
190
350
|
style: {
|
|
@@ -194,16 +354,16 @@ const Meteor = /*#__PURE__*/memo(function Meteor({
|
|
|
194
354
|
width: 3,
|
|
195
355
|
height: 3,
|
|
196
356
|
borderRadius: 1.5,
|
|
197
|
-
backgroundColor: withAlpha(
|
|
357
|
+
backgroundColor: withAlpha(mineral.head, 1)
|
|
198
358
|
}
|
|
199
359
|
}), trail]
|
|
200
360
|
}), /*#__PURE__*/_jsx(Animated.View, {
|
|
201
361
|
style: {
|
|
202
362
|
position: "absolute",
|
|
203
|
-
left: x1 -
|
|
204
|
-
top: y1 -
|
|
205
|
-
width:
|
|
206
|
-
height:
|
|
363
|
+
left: x1 - 18,
|
|
364
|
+
top: y1 - 18,
|
|
365
|
+
width: 36,
|
|
366
|
+
height: 36,
|
|
207
367
|
opacity: flashOpacity,
|
|
208
368
|
transform: [{
|
|
209
369
|
scale: flashScale
|
|
@@ -211,29 +371,13 @@ const Meteor = /*#__PURE__*/memo(function Meteor({
|
|
|
211
371
|
},
|
|
212
372
|
pointerEvents: "none",
|
|
213
373
|
children: /*#__PURE__*/_jsx(RadialGlow, {
|
|
214
|
-
size:
|
|
215
|
-
color:
|
|
216
|
-
alpha: 0.
|
|
217
|
-
rings:
|
|
374
|
+
size: 36,
|
|
375
|
+
color: mineral.glow,
|
|
376
|
+
alpha: 0.45,
|
|
377
|
+
rings: 5,
|
|
218
378
|
falloff: 1.6
|
|
219
379
|
})
|
|
220
|
-
}),
|
|
221
|
-
style: {
|
|
222
|
-
position: "absolute",
|
|
223
|
-
left: x1 - SHOCKWAVE_DIA / 2,
|
|
224
|
-
top: y1 - SHOCKWAVE_DIA / 2,
|
|
225
|
-
width: SHOCKWAVE_DIA,
|
|
226
|
-
height: SHOCKWAVE_DIA,
|
|
227
|
-
borderRadius: SHOCKWAVE_DIA / 2,
|
|
228
|
-
borderWidth: 1.5,
|
|
229
|
-
borderColor: withAlpha("#BBE5FA", 0.9),
|
|
230
|
-
opacity: waveOpacity,
|
|
231
|
-
transform: [{
|
|
232
|
-
scale: waveScale
|
|
233
|
-
}]
|
|
234
|
-
},
|
|
235
|
-
pointerEvents: "none"
|
|
236
|
-
}), sparks]
|
|
380
|
+
}), rays, embers]
|
|
237
381
|
});
|
|
238
382
|
});
|
|
239
383
|
|
|
@@ -98,7 +98,8 @@ export function TypeBadge({
|
|
|
98
98
|
type,
|
|
99
99
|
color,
|
|
100
100
|
size = "small",
|
|
101
|
-
style
|
|
101
|
+
style,
|
|
102
|
+
testID
|
|
102
103
|
}) {
|
|
103
104
|
const typeColor = color || getTypeColor(type);
|
|
104
105
|
const badgeStyles = getBadgeStyles("type", typeColor, size);
|
|
@@ -108,6 +109,7 @@ export function TypeBadge({
|
|
|
108
109
|
style: [badgeStyles.text, {
|
|
109
110
|
color: typeColor
|
|
110
111
|
}],
|
|
112
|
+
testID: testID,
|
|
111
113
|
children: type
|
|
112
114
|
})
|
|
113
115
|
});
|
|
@@ -23,6 +23,8 @@ export function CompactRow({
|
|
|
23
23
|
disabled,
|
|
24
24
|
expandedGlowColor,
|
|
25
25
|
testID,
|
|
26
|
+
statusTestID,
|
|
27
|
+
statusSublabelTestID,
|
|
26
28
|
accessibilityLabel
|
|
27
29
|
}) {
|
|
28
30
|
return /*#__PURE__*/_jsx(View, {
|
|
@@ -41,7 +43,12 @@ export function CompactRow({
|
|
|
41
43
|
activeOpacity: 0.8,
|
|
42
44
|
disabled: disabled || !onPress,
|
|
43
45
|
testID: testID,
|
|
44
|
-
accessibilityLabel: accessibilityLabel
|
|
46
|
+
accessibilityLabel: accessibilityLabel
|
|
47
|
+
// Label alone is not enough on Fabric: a row that announces text but
|
|
48
|
+
// no role reads to VoiceOver as static content, so the fact that it
|
|
49
|
+
// opens something is only discoverable by tapping it.
|
|
50
|
+
,
|
|
51
|
+
accessibilityRole: onPress ? "button" : undefined,
|
|
45
52
|
children: /*#__PURE__*/_jsxs(View, {
|
|
46
53
|
style: styles.rowContent,
|
|
47
54
|
children: [/*#__PURE__*/_jsxs(View, {
|
|
@@ -57,10 +64,12 @@ export function CompactRow({
|
|
|
57
64
|
color: statusDotColor
|
|
58
65
|
}],
|
|
59
66
|
numberOfLines: 1,
|
|
67
|
+
testID: statusTestID,
|
|
60
68
|
children: statusLabel
|
|
61
69
|
}), statusSublabel ? /*#__PURE__*/_jsx(Text, {
|
|
62
70
|
style: styles.observerText,
|
|
63
71
|
numberOfLines: 1,
|
|
72
|
+
testID: statusSublabelTestID,
|
|
64
73
|
children: statusSublabel
|
|
65
74
|
}) : null]
|
|
66
75
|
})]
|
|
@@ -261,7 +261,8 @@ export const ActionCopyButton = /*#__PURE__*/memo(function ActionCopyButton(prop
|
|
|
261
261
|
export const ToolbarClearButton = /*#__PURE__*/memo(function ToolbarClearButton({
|
|
262
262
|
onPress,
|
|
263
263
|
disabled = false,
|
|
264
|
-
buttonStyle
|
|
264
|
+
buttonStyle,
|
|
265
|
+
testID
|
|
265
266
|
}) {
|
|
266
267
|
return /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
267
268
|
onPress: onPress,
|
|
@@ -273,6 +274,7 @@ export const ToolbarClearButton = /*#__PURE__*/memo(function ToolbarClearButton(
|
|
|
273
274
|
left: 8,
|
|
274
275
|
right: 8
|
|
275
276
|
},
|
|
277
|
+
testID: testID,
|
|
276
278
|
accessibilityLabel: "Clear",
|
|
277
279
|
accessibilityRole: "button",
|
|
278
280
|
children: /*#__PURE__*/_jsx(Trash2, {
|
|
@@ -36,7 +36,9 @@ export const DraggableHeader = /*#__PURE__*/memo(function DraggableHeader({
|
|
|
36
36
|
},
|
|
37
37
|
style,
|
|
38
38
|
enabled = true,
|
|
39
|
-
maxOverflowX = 0
|
|
39
|
+
maxOverflowX = 0,
|
|
40
|
+
testID,
|
|
41
|
+
accessibilityLabel
|
|
40
42
|
}) {
|
|
41
43
|
const isDraggingRef = useRef(false);
|
|
42
44
|
const dragDistanceRef = useRef(0);
|
|
@@ -186,6 +188,8 @@ export const DraggableHeader = /*#__PURE__*/memo(function DraggableHeader({
|
|
|
186
188
|
}, [position]);
|
|
187
189
|
return /*#__PURE__*/_jsx(View, {
|
|
188
190
|
style: style,
|
|
191
|
+
testID: testID,
|
|
192
|
+
accessibilityLabel: accessibilityLabel,
|
|
189
193
|
...panResponder.panHandlers,
|
|
190
194
|
children: children
|
|
191
195
|
});
|
|
@@ -103,6 +103,9 @@ export function DynamicFilterView({
|
|
|
103
103
|
const countIsZero = hasCount && option.count === 0;
|
|
104
104
|
const showLabel = section.type !== "method" || option.icon;
|
|
105
105
|
return /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
106
|
+
testID: `buoy-filter-option-${option.id}`,
|
|
107
|
+
accessibilityRole: "button",
|
|
108
|
+
accessibilityLabel: String(option.label ?? option.id),
|
|
106
109
|
style: [styles.filterCard, option.isActive && styles.activeFilterCard],
|
|
107
110
|
onPress: () => onFilterChange?.(option.id, option.value),
|
|
108
111
|
children: [option.icon ? /*#__PURE__*/_jsx(option.icon, {
|
|
@@ -187,7 +190,8 @@ export function DynamicFilterView({
|
|
|
187
190
|
children: !includeFilterManager.showAddInput ? /*#__PURE__*/_jsx(AddFilterButton, {
|
|
188
191
|
onPress: () => includeFilterManager.setShowAddInput(true),
|
|
189
192
|
color: buoyColors.success,
|
|
190
|
-
label: "Add include pattern"
|
|
193
|
+
label: "Add include pattern",
|
|
194
|
+
testID: "buoy-filter-include-add-open"
|
|
191
195
|
}) : /*#__PURE__*/_jsx(AddFilterInput, {
|
|
192
196
|
value: includeFilterManager.newFilter,
|
|
193
197
|
onChange: includeFilterManager.setNewFilter,
|
|
@@ -197,9 +201,11 @@ export function DynamicFilterView({
|
|
|
197
201
|
includeFilterManager.setNewFilter("");
|
|
198
202
|
},
|
|
199
203
|
placeholder: includeOnlySection.placeholder || "Enter pattern to include...",
|
|
200
|
-
color: buoyColors.success
|
|
204
|
+
color: buoyColors.success,
|
|
205
|
+
testID: "buoy-filter-include-input"
|
|
201
206
|
})
|
|
202
207
|
}), includeOnlySection.patterns.size > 0 && /*#__PURE__*/_jsx(ScrollView, {
|
|
208
|
+
testID: "buoy-filter-include-scroll",
|
|
203
209
|
style: styles.includeOnlyFiltersContent,
|
|
204
210
|
showsVerticalScrollIndicator: true,
|
|
205
211
|
nestedScrollEnabled: true,
|
|
@@ -244,7 +250,11 @@ export function DynamicFilterView({
|
|
|
244
250
|
color: buoyColors.primary
|
|
245
251
|
})]
|
|
246
252
|
})
|
|
247
|
-
}), activePatterns.size > 0 && /*#__PURE__*/_jsx(ScrollView
|
|
253
|
+
}), activePatterns.size > 0 && /*#__PURE__*/_jsx(ScrollView
|
|
254
|
+
// The active patterns live in their own short scroller, so a
|
|
255
|
+
// pattern added a moment ago is usually below its fold.
|
|
256
|
+
, {
|
|
257
|
+
testID: "buoy-filter-active-scroll",
|
|
248
258
|
style: styles.excludeFiltersContent,
|
|
249
259
|
showsVerticalScrollIndicator: true,
|
|
250
260
|
nestedScrollEnabled: true,
|
|
@@ -283,6 +293,9 @@ export function DynamicFilterView({
|
|
|
283
293
|
},
|
|
284
294
|
style: styles.availableKeyItem,
|
|
285
295
|
"sentry-label": "ignore-touchable-opacity",
|
|
296
|
+
testID: `buoy-filter-available-${item}`,
|
|
297
|
+
accessibilityRole: "button",
|
|
298
|
+
accessibilityLabel: `Add filter ${item}`,
|
|
286
299
|
children: [/*#__PURE__*/_jsx(Text, {
|
|
287
300
|
style: styles.availableKeyText,
|
|
288
301
|
numberOfLines: 1,
|
|
@@ -340,7 +353,13 @@ export function DynamicFilterView({
|
|
|
340
353
|
};
|
|
341
354
|
return /*#__PURE__*/_jsxs(View, {
|
|
342
355
|
style: styles.container,
|
|
343
|
-
children: [renderTabs(), /*#__PURE__*/_jsx(ScrollView
|
|
356
|
+
children: [renderTabs(), /*#__PURE__*/_jsx(ScrollView
|
|
357
|
+
// The filter screen's own scroller. Named because the sections it
|
|
358
|
+
// holds — including the active-pattern list — run past the bottom of
|
|
359
|
+
// a docked sheet, so reaching one means scrolling THIS, not the short
|
|
360
|
+
// nested scroller the pattern happens to sit in.
|
|
361
|
+
, {
|
|
362
|
+
testID: "buoy-filter-scroll",
|
|
344
363
|
style: styles.content,
|
|
345
364
|
contentContainerStyle: styles.scrollContent,
|
|
346
365
|
showsVerticalScrollIndicator: false,
|