@buoy-gg/shared-ui 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/lib/commonjs/JsModal.js +156 -20
- package/lib/commonjs/JsModal.web.js +85 -39
- package/lib/commonjs/dataViewer/VirtualizedDataExplorer.js +14 -4
- package/lib/commonjs/dataViewer/diffThemes.js +20 -18
- package/lib/commonjs/dataViewer/tree/TreeDiffViewer.js +8 -8
- package/lib/commonjs/hooks/safe-area-impl.js +1 -1
- package/lib/commonjs/icons/lucide-icons.js +74 -15
- package/lib/commonjs/index.js +86 -0
- package/lib/commonjs/ui/backgrounds/BackgroundSwitcher.js +248 -74
- package/lib/commonjs/ui/backgrounds/README.md +102 -20
- package/lib/commonjs/ui/backgrounds/backgroundStore.js +20 -6
- package/lib/commonjs/ui/backgrounds/index.js +91 -1
- package/lib/commonjs/ui/backgrounds/primitives.js +50 -14
- package/lib/commonjs/ui/backgrounds/registry.js +41 -64
- package/lib/commonjs/ui/backgrounds/rng.js +21 -0
- package/lib/commonjs/ui/backgrounds/skyClock.js +45 -0
- package/lib/commonjs/ui/backgrounds/variants/daylight.js +453 -0
- package/lib/commonjs/ui/backgrounds/variants/deepField.js +60 -30
- package/lib/commonjs/ui/backgrounds/variants/hyperspace.js +218 -0
- package/lib/commonjs/ui/backgrounds/variants/liveSky.js +898 -0
- package/lib/commonjs/ui/backgrounds/variants/meteors.js +257 -0
- package/lib/commonjs/ui/backgrounds/variants/moonlight.js +472 -0
- package/lib/commonjs/ui/backgrounds/variants/underwater.js +757 -0
- package/lib/commonjs/ui/components/Badge.js +11 -10
- package/lib/commonjs/ui/components/CompactRow.js +30 -17
- package/lib/commonjs/ui/components/DevToolsCard.js +12 -8
- package/lib/commonjs/ui/components/DynamicFilterView.js +43 -41
- package/lib/commonjs/ui/components/EventStepperFooter.js +38 -10
- package/lib/commonjs/ui/components/FilterComponents.js +11 -11
- package/lib/commonjs/ui/components/ModalHeader.js +13 -13
- package/lib/commonjs/ui/components/SectionHeader.js +3 -1
- package/lib/commonjs/ui/components/StorageTypeBadge.js +3 -2
- package/lib/commonjs/ui/components/TabSelector.js +56 -38
- package/lib/commonjs/ui/components/ValueTypeBadge.js +7 -6
- package/lib/commonjs/ui/components/WindowControls.js +37 -10
- package/lib/commonjs/ui/gameUI/constants/gameUIColors.js +8 -0
- package/lib/commonjs/ui/night/nightTheme.js +120 -0
- package/lib/commonjs/ui/night/primitives.js +498 -0
- package/lib/module/JsModal.js +156 -20
- package/lib/module/JsModal.web.js +83 -39
- package/lib/module/dataViewer/VirtualizedDataExplorer.js +14 -4
- package/lib/module/dataViewer/diffThemes.js +20 -18
- package/lib/module/dataViewer/tree/TreeDiffViewer.js +9 -9
- package/lib/module/hooks/safe-area-impl.js +1 -1
- package/lib/module/icons/lucide-icons.js +74 -15
- package/lib/module/index.js +4 -0
- package/lib/module/ui/backgrounds/BackgroundSwitcher.js +250 -76
- package/lib/module/ui/backgrounds/README.md +102 -20
- package/lib/module/ui/backgrounds/backgroundStore.js +19 -5
- package/lib/module/ui/backgrounds/index.js +11 -2
- package/lib/module/ui/backgrounds/primitives.js +53 -16
- package/lib/module/ui/backgrounds/registry.js +41 -64
- package/lib/module/ui/backgrounds/rng.js +20 -0
- package/lib/module/ui/backgrounds/skyClock.js +39 -0
- package/lib/module/ui/backgrounds/variants/daylight.js +449 -0
- package/lib/module/ui/backgrounds/variants/deepField.js +62 -31
- package/lib/module/ui/backgrounds/variants/hyperspace.js +214 -0
- package/lib/module/ui/backgrounds/variants/liveSky.js +893 -0
- package/lib/module/ui/backgrounds/variants/meteors.js +253 -0
- package/lib/module/ui/backgrounds/variants/moonlight.js +468 -0
- package/lib/module/ui/backgrounds/variants/underwater.js +753 -0
- package/lib/module/ui/components/Badge.js +11 -10
- package/lib/module/ui/components/CompactRow.js +30 -17
- package/lib/module/ui/components/DevToolsCard.js +12 -8
- package/lib/module/ui/components/DynamicFilterView.js +43 -41
- package/lib/module/ui/components/EventStepperFooter.js +38 -10
- package/lib/module/ui/components/FilterComponents.js +11 -11
- package/lib/module/ui/components/ModalHeader.js +13 -13
- package/lib/module/ui/components/SectionHeader.js +3 -1
- package/lib/module/ui/components/StorageTypeBadge.js +3 -2
- package/lib/module/ui/components/TabSelector.js +57 -39
- package/lib/module/ui/components/ValueTypeBadge.js +7 -6
- package/lib/module/ui/components/WindowControls.js +37 -10
- package/lib/module/ui/gameUI/constants/gameUIColors.js +8 -0
- package/lib/module/ui/night/nightTheme.js +116 -0
- package/lib/module/ui/night/primitives.js +485 -0
- package/lib/typescript/commonjs/JsModal.d.ts +18 -0
- package/lib/typescript/commonjs/JsModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/JsModal.web.d.ts +13 -0
- package/lib/typescript/commonjs/JsModal.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/VirtualizedDataExplorer.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/diffThemes.d.ts +3 -1
- package/lib/typescript/commonjs/dataViewer/diffThemes.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/tree/TreeDiffViewer.d.ts.map +1 -1
- package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
- package/lib/typescript/commonjs/icons/lucide-icons.d.ts +12 -1
- package/lib/typescript/commonjs/icons/lucide-icons.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +3 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/backgrounds/BackgroundSwitcher.d.ts +26 -6
- package/lib/typescript/commonjs/ui/backgrounds/BackgroundSwitcher.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/backgrounds/backgroundStore.d.ts +3 -0
- package/lib/typescript/commonjs/ui/backgrounds/backgroundStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/backgrounds/index.d.ts +7 -1
- package/lib/typescript/commonjs/ui/backgrounds/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/backgrounds/primitives.d.ts +18 -3
- package/lib/typescript/commonjs/ui/backgrounds/primitives.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/backgrounds/registry.d.ts +8 -0
- package/lib/typescript/commonjs/ui/backgrounds/registry.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/backgrounds/rng.d.ts +6 -0
- package/lib/typescript/commonjs/ui/backgrounds/rng.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/backgrounds/skyClock.d.ts +17 -0
- package/lib/typescript/commonjs/ui/backgrounds/skyClock.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/types.d.ts +1 -1
- package/lib/typescript/commonjs/ui/backgrounds/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/backgrounds/variants/daylight.d.ts +30 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/daylight.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/deepField.d.ts +19 -4
- package/lib/typescript/commonjs/ui/backgrounds/variants/deepField.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/backgrounds/variants/hyperspace.d.ts +39 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/hyperspace.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/liveSky.d.ts +41 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/liveSky.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/meteors.d.ts +26 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/meteors.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/moonlight.d.ts +35 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/moonlight.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/underwater.d.ts +51 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/underwater.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/Badge.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/CompactRow.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 +2 -0
- package/lib/typescript/commonjs/ui/components/EventStepperFooter.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/StorageTypeBadge.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/TabSelector.d.ts +9 -0
- package/lib/typescript/commonjs/ui/components/TabSelector.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ValueTypeBadge.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/WindowControls.d.ts +4 -2
- package/lib/typescript/commonjs/ui/components/WindowControls.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/gameUI/constants/gameUIColors.d.ts +8 -0
- package/lib/typescript/commonjs/ui/gameUI/constants/gameUIColors.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/night/nightTheme.d.ts +113 -0
- package/lib/typescript/commonjs/ui/night/nightTheme.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/night/primitives.d.ts +111 -0
- package/lib/typescript/commonjs/ui/night/primitives.d.ts.map +1 -0
- package/lib/typescript/module/JsModal.d.ts +18 -0
- package/lib/typescript/module/JsModal.d.ts.map +1 -1
- package/lib/typescript/module/JsModal.web.d.ts +13 -0
- package/lib/typescript/module/JsModal.web.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/VirtualizedDataExplorer.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/diffThemes.d.ts +3 -1
- package/lib/typescript/module/dataViewer/diffThemes.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/tree/TreeDiffViewer.d.ts.map +1 -1
- package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
- package/lib/typescript/module/icons/lucide-icons.d.ts +12 -1
- package/lib/typescript/module/icons/lucide-icons.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +3 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/backgrounds/BackgroundSwitcher.d.ts +26 -6
- package/lib/typescript/module/ui/backgrounds/BackgroundSwitcher.d.ts.map +1 -1
- package/lib/typescript/module/ui/backgrounds/backgroundStore.d.ts +3 -0
- package/lib/typescript/module/ui/backgrounds/backgroundStore.d.ts.map +1 -1
- package/lib/typescript/module/ui/backgrounds/index.d.ts +7 -1
- package/lib/typescript/module/ui/backgrounds/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/backgrounds/primitives.d.ts +18 -3
- package/lib/typescript/module/ui/backgrounds/primitives.d.ts.map +1 -1
- package/lib/typescript/module/ui/backgrounds/registry.d.ts +8 -0
- package/lib/typescript/module/ui/backgrounds/registry.d.ts.map +1 -1
- package/lib/typescript/module/ui/backgrounds/rng.d.ts +6 -0
- package/lib/typescript/module/ui/backgrounds/rng.d.ts.map +1 -1
- package/lib/typescript/module/ui/backgrounds/skyClock.d.ts +17 -0
- package/lib/typescript/module/ui/backgrounds/skyClock.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/types.d.ts +1 -1
- package/lib/typescript/module/ui/backgrounds/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/backgrounds/variants/daylight.d.ts +30 -0
- package/lib/typescript/module/ui/backgrounds/variants/daylight.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/variants/deepField.d.ts +19 -4
- package/lib/typescript/module/ui/backgrounds/variants/deepField.d.ts.map +1 -1
- package/lib/typescript/module/ui/backgrounds/variants/hyperspace.d.ts +39 -0
- package/lib/typescript/module/ui/backgrounds/variants/hyperspace.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/variants/liveSky.d.ts +41 -0
- package/lib/typescript/module/ui/backgrounds/variants/liveSky.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/variants/meteors.d.ts +26 -0
- package/lib/typescript/module/ui/backgrounds/variants/meteors.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/variants/moonlight.d.ts +35 -0
- package/lib/typescript/module/ui/backgrounds/variants/moonlight.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/variants/underwater.d.ts +51 -0
- package/lib/typescript/module/ui/backgrounds/variants/underwater.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/Badge.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/CompactRow.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 +2 -0
- package/lib/typescript/module/ui/components/EventStepperFooter.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/StorageTypeBadge.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/TabSelector.d.ts +9 -0
- package/lib/typescript/module/ui/components/TabSelector.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ValueTypeBadge.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/WindowControls.d.ts +4 -2
- package/lib/typescript/module/ui/components/WindowControls.d.ts.map +1 -1
- package/lib/typescript/module/ui/gameUI/constants/gameUIColors.d.ts +8 -0
- package/lib/typescript/module/ui/gameUI/constants/gameUIColors.d.ts.map +1 -1
- package/lib/typescript/module/ui/night/nightTheme.d.ts +113 -0
- package/lib/typescript/module/ui/night/nightTheme.d.ts.map +1 -0
- package/lib/typescript/module/ui/night/primitives.d.ts +111 -0
- package/lib/typescript/module/ui/night/primitives.d.ts.map +1 -0
- package/package.json +4 -4
- package/lib/commonjs/ui/backgrounds/variants/lattice.js +0 -579
- package/lib/commonjs/ui/backgrounds/variants/starfield.js +0 -210
- package/lib/module/ui/backgrounds/variants/lattice.js +0 -575
- package/lib/module/ui/backgrounds/variants/starfield.js +0 -206
- package/lib/typescript/commonjs/ui/backgrounds/variants/lattice.d.ts +0 -33
- package/lib/typescript/commonjs/ui/backgrounds/variants/lattice.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/backgrounds/variants/starfield.d.ts +0 -12
- package/lib/typescript/commonjs/ui/backgrounds/variants/starfield.d.ts.map +0 -1
- package/lib/typescript/module/ui/backgrounds/variants/lattice.d.ts +0 -33
- package/lib/typescript/module/ui/backgrounds/variants/lattice.d.ts.map +0 -1
- package/lib/typescript/module/ui/backgrounds/variants/starfield.d.ts +0 -12
- package/lib/typescript/module/ui/backgrounds/variants/starfield.d.ts.map +0 -1
|
@@ -10,87 +10,205 @@ var _lucideIcons = require("../../icons/lucide-icons.js");
|
|
|
10
10
|
var _macOSDesignSystemColors = require("../gameUI/constants/macOSDesignSystemColors.js");
|
|
11
11
|
var _backgroundStore = require("./backgroundStore.js");
|
|
12
12
|
var _registry = require("./registry.js");
|
|
13
|
+
var _skyClock = require("./skyClock.js");
|
|
13
14
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
15
|
/**
|
|
15
|
-
* The ‹ prev / next › strip for
|
|
16
|
+
* The ‹ prev / next › strip for choosing a background.
|
|
16
17
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* Shipping mount: the dial menu's settings modal (pass `embedded` there so the
|
|
19
|
+
* host card supplies the chrome). It can also be dropped at the top of a
|
|
20
|
+
* tool's body while auditioning new presets — the cost of each preset shows
|
|
21
|
+
* next to its name, so it doubles as the review tool it started as.
|
|
21
22
|
*
|
|
22
23
|
* It writes to the shared `backgroundStore`, so stepping here changes the
|
|
23
24
|
* background in every tool at once.
|
|
25
|
+
*
|
|
26
|
+
* While Live Sky is showing, a second row appears: a WALL-clock slider that
|
|
27
|
+
* scrubs the scene through the whole cycle (Live Sky applies its workday
|
|
28
|
+
* remap to whatever wall time the scrub holds, same as it does to the live
|
|
29
|
+
* clock). LIVE snaps back to the real clock. The scrub is quantized to
|
|
30
|
+
* 5-minute steps so a drag re-renders the scene at most once per step, and it
|
|
31
|
+
* is not persisted — it's a preview gesture.
|
|
32
|
+
*
|
|
33
|
+
* ▶ runs the cycle like a movie: one full 24h lap in 15s, starting from the
|
|
34
|
+
* displayed time and finishing exactly where it began (back on LIVE if that's
|
|
35
|
+
* where it was). It drives the SAME scrub value on a JS interval — ~19 5-min
|
|
36
|
+
* steps a second, each one small re-render of the scene — which is fine for a
|
|
37
|
+
* 15s preview gesture and is exactly what a fast drag already does.
|
|
24
38
|
*/
|
|
25
39
|
|
|
26
|
-
const
|
|
40
|
+
const THUMB = 14;
|
|
41
|
+
function formatMinutes(m) {
|
|
42
|
+
const h = Math.floor(m / 60) % 24;
|
|
43
|
+
const min = m % 60;
|
|
44
|
+
return `${String(h).padStart(2, "0")}:${String(min).padStart(2, "0")}`;
|
|
45
|
+
}
|
|
46
|
+
const PLAY_DURATION_MS = 15000;
|
|
47
|
+
const PLAY_FRAME_MS = 50;
|
|
48
|
+
const TimeSlider = /*#__PURE__*/(0, _react.memo)(function TimeSlider() {
|
|
49
|
+
const scrub = (0, _skyClock.useSkyScrub)();
|
|
50
|
+
const [trackW, setTrackW] = (0, _react.useState)(0);
|
|
51
|
+
const widthRef = (0, _react.useRef)(0);
|
|
52
|
+
const [playing, setPlaying] = (0, _react.useState)(false);
|
|
53
|
+
const playTimer = (0, _react.useRef)(null);
|
|
54
|
+
const stopPlay = () => {
|
|
55
|
+
if (playTimer.current) clearInterval(playTimer.current);
|
|
56
|
+
playTimer.current = null;
|
|
57
|
+
setPlaying(false);
|
|
58
|
+
};
|
|
59
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
60
|
+
(0, _react.useEffect)(() => stopPlay, []);
|
|
61
|
+
const startPlay = () => {
|
|
62
|
+
stopPlay();
|
|
63
|
+
const now = new Date();
|
|
64
|
+
const startMinutes = (0, _skyClock.getSkyScrub)() ?? now.getHours() * 60 + now.getMinutes();
|
|
65
|
+
const wasLive = (0, _skyClock.getSkyScrub)() === null;
|
|
66
|
+
const begin = Date.now();
|
|
67
|
+
setPlaying(true);
|
|
68
|
+
playTimer.current = setInterval(() => {
|
|
69
|
+
const t = (Date.now() - begin) / PLAY_DURATION_MS;
|
|
70
|
+
if (t >= 1) {
|
|
71
|
+
stopPlay();
|
|
72
|
+
// Finish exactly where the day began.
|
|
73
|
+
(0, _skyClock.setSkyScrub)(wasLive ? null : startMinutes);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
(0, _skyClock.setSkyScrub)(Math.floor((startMinutes + t * 1440) % 1440 / 5) * 5);
|
|
77
|
+
}, PLAY_FRAME_MS);
|
|
78
|
+
};
|
|
79
|
+
const applyX = x => {
|
|
80
|
+
stopPlay();
|
|
81
|
+
const w = widthRef.current;
|
|
82
|
+
if (w <= 0) return;
|
|
83
|
+
const frac = Math.min(1, Math.max(0, x / w));
|
|
84
|
+
(0, _skyClock.setSkyScrub)(Math.round(frac * 1439 / 5) * 5);
|
|
85
|
+
};
|
|
86
|
+
const responder = (0, _react.useRef)(_reactNative.PanResponder.create({
|
|
87
|
+
onStartShouldSetPanResponder: () => true,
|
|
88
|
+
onMoveShouldSetPanResponder: () => true,
|
|
89
|
+
onPanResponderGrant: e => applyX(e.nativeEvent.locationX),
|
|
90
|
+
onPanResponderMove: e => applyX(e.nativeEvent.locationX)
|
|
91
|
+
})).current;
|
|
92
|
+
const now = new Date();
|
|
93
|
+
// Wall time throughout — the scene applies its own remap to this.
|
|
94
|
+
const minutes = scrub ?? now.getHours() * 60 + now.getMinutes();
|
|
95
|
+
const frac = minutes / 1440;
|
|
96
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
97
|
+
style: styles.sliderRow,
|
|
98
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
99
|
+
onPress: () => {
|
|
100
|
+
stopPlay();
|
|
101
|
+
(0, _skyClock.setSkyScrub)(null);
|
|
102
|
+
},
|
|
103
|
+
style: [styles.live, scrub === null ? styles.liveOn : null],
|
|
104
|
+
hitSlop: HIT_SLOP,
|
|
105
|
+
accessibilityLabel: "Follow the real clock",
|
|
106
|
+
"sentry-label": "ignore background sky live",
|
|
107
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
108
|
+
style: [styles.liveText, scrub === null ? styles.liveTextOn : null],
|
|
109
|
+
children: "LIVE"
|
|
110
|
+
})
|
|
111
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
112
|
+
onPress: playing ? stopPlay : startPlay,
|
|
113
|
+
style: [styles.live, playing ? styles.playOn : null],
|
|
114
|
+
hitSlop: HIT_SLOP,
|
|
115
|
+
accessibilityLabel: playing ? "Stop the day cycle" : "Play the full day in 15 seconds",
|
|
116
|
+
"sentry-label": "ignore background sky play",
|
|
117
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
118
|
+
style: [styles.liveText, playing ? styles.playTextOn : null],
|
|
119
|
+
children: playing ? "■" : "▶"
|
|
120
|
+
})
|
|
121
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
122
|
+
style: styles.track,
|
|
123
|
+
onLayout: e => {
|
|
124
|
+
widthRef.current = e.nativeEvent.layout.width;
|
|
125
|
+
setTrackW(e.nativeEvent.layout.width);
|
|
126
|
+
},
|
|
127
|
+
...responder.panHandlers,
|
|
128
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
129
|
+
style: styles.trackBar,
|
|
130
|
+
pointerEvents: "none"
|
|
131
|
+
}), trackW > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
132
|
+
pointerEvents: "none",
|
|
133
|
+
style: [styles.thumb, scrub === null ? null : styles.thumbScrub, {
|
|
134
|
+
left: frac * trackW - THUMB / 2
|
|
135
|
+
}]
|
|
136
|
+
}) : null]
|
|
137
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
138
|
+
style: styles.time,
|
|
139
|
+
children: formatMinutes(minutes)
|
|
140
|
+
})]
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
const BackgroundSwitcher = exports.BackgroundSwitcher = /*#__PURE__*/(0, _react.memo)(function BackgroundSwitcher({
|
|
144
|
+
embedded = false
|
|
145
|
+
}) {
|
|
27
146
|
const {
|
|
28
147
|
id,
|
|
29
148
|
index,
|
|
30
149
|
count,
|
|
31
|
-
motion,
|
|
32
150
|
next,
|
|
33
151
|
previous,
|
|
34
|
-
|
|
152
|
+
setId
|
|
35
153
|
} = (0, _backgroundStore.useToolBackground)();
|
|
36
154
|
const preset = (0, _registry.getPreset)(id);
|
|
155
|
+
const counterpart = preset.counterpart;
|
|
37
156
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
38
|
-
style: styles.root,
|
|
39
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
style: styles.
|
|
53
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.
|
|
54
|
-
|
|
55
|
-
|
|
157
|
+
style: embedded ? styles.rootEmbedded : styles.root,
|
|
158
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
159
|
+
style: styles.row,
|
|
160
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
161
|
+
onPress: previous,
|
|
162
|
+
style: styles.step,
|
|
163
|
+
hitSlop: HIT_SLOP,
|
|
164
|
+
accessibilityLabel: "Previous background",
|
|
165
|
+
"sentry-label": "ignore background previous",
|
|
166
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_lucideIcons.ChevronLeft, {
|
|
167
|
+
size: 16,
|
|
168
|
+
color: _macOSDesignSystemColors.macOSColors.text.secondary
|
|
169
|
+
})
|
|
170
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
171
|
+
style: styles.center,
|
|
172
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
173
|
+
style: styles.titleRow,
|
|
174
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_lucideIcons.Palette, {
|
|
175
|
+
size: 11,
|
|
176
|
+
color: _macOSDesignSystemColors.macOSColors.semantic.info
|
|
177
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
178
|
+
style: styles.title,
|
|
179
|
+
numberOfLines: 1,
|
|
180
|
+
children: preset.label
|
|
181
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
|
|
182
|
+
style: styles.counter,
|
|
183
|
+
children: [index + 1, "/", count]
|
|
184
|
+
})]
|
|
56
185
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
57
|
-
style: styles.
|
|
186
|
+
style: styles.blurb,
|
|
58
187
|
numberOfLines: 1,
|
|
59
|
-
children: preset.
|
|
60
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
|
|
61
|
-
style: styles.counter,
|
|
62
|
-
children: [index + 1, "/", count]
|
|
188
|
+
children: preset.blurb
|
|
63
189
|
})]
|
|
64
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
190
|
+
}), counterpart ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
191
|
+
onPress: () => setId(counterpart.id),
|
|
192
|
+
style: styles.motion,
|
|
193
|
+
hitSlop: HIT_SLOP,
|
|
194
|
+
accessibilityLabel: `Switch to ${counterpart.id} background`,
|
|
195
|
+
"sentry-label": "ignore background counterpart",
|
|
196
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
197
|
+
style: styles.motionText,
|
|
198
|
+
children: counterpart.label
|
|
199
|
+
})
|
|
200
|
+
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
201
|
+
onPress: next,
|
|
202
|
+
style: styles.step,
|
|
203
|
+
hitSlop: HIT_SLOP,
|
|
204
|
+
accessibilityLabel: "Next background",
|
|
205
|
+
"sentry-label": "ignore background next",
|
|
206
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_lucideIcons.ChevronRight, {
|
|
207
|
+
size: 16,
|
|
208
|
+
color: _macOSDesignSystemColors.macOSColors.text.secondary
|
|
209
|
+
})
|
|
72
210
|
})]
|
|
73
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(
|
|
74
|
-
onPress: () => setMotion(!motion),
|
|
75
|
-
style: [styles.motion, motion ? styles.motionOn : null],
|
|
76
|
-
hitSlop: HIT_SLOP,
|
|
77
|
-
accessibilityLabel: motion ? "Turn motion off" : "Turn motion on",
|
|
78
|
-
"sentry-label": "ignore background motion",
|
|
79
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
80
|
-
style: [styles.motionText, motion ? styles.motionTextOn : null],
|
|
81
|
-
children: motion ? "MOTION" : "STATIC"
|
|
82
|
-
})
|
|
83
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
84
|
-
onPress: next,
|
|
85
|
-
style: styles.step,
|
|
86
|
-
hitSlop: HIT_SLOP,
|
|
87
|
-
accessibilityLabel: "Next background",
|
|
88
|
-
"sentry-label": "ignore background next",
|
|
89
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_lucideIcons.ChevronRight, {
|
|
90
|
-
size: 16,
|
|
91
|
-
color: _macOSDesignSystemColors.macOSColors.text.secondary
|
|
92
|
-
})
|
|
93
|
-
})]
|
|
211
|
+
}), id === "livesky" ? /*#__PURE__*/(0, _jsxRuntime.jsx)(TimeSlider, {}) : null]
|
|
94
212
|
});
|
|
95
213
|
});
|
|
96
214
|
const HIT_SLOP = {
|
|
@@ -101,9 +219,6 @@ const HIT_SLOP = {
|
|
|
101
219
|
};
|
|
102
220
|
const styles = _reactNative.StyleSheet.create({
|
|
103
221
|
root: {
|
|
104
|
-
flexDirection: "row",
|
|
105
|
-
alignItems: "center",
|
|
106
|
-
gap: 8,
|
|
107
222
|
paddingHorizontal: 8,
|
|
108
223
|
paddingVertical: 6,
|
|
109
224
|
marginHorizontal: 8,
|
|
@@ -116,6 +231,14 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
116
231
|
// previewing, so you can see the field through it.
|
|
117
232
|
backgroundColor: "rgba(26,26,28,0.72)"
|
|
118
233
|
},
|
|
234
|
+
rootEmbedded: {
|
|
235
|
+
// The host card supplies border/fill/margins.
|
|
236
|
+
},
|
|
237
|
+
row: {
|
|
238
|
+
flexDirection: "row",
|
|
239
|
+
alignItems: "center",
|
|
240
|
+
gap: 8
|
|
241
|
+
},
|
|
119
242
|
step: {
|
|
120
243
|
width: 28,
|
|
121
244
|
height: 28,
|
|
@@ -150,12 +273,6 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
150
273
|
fontSize: 10,
|
|
151
274
|
marginTop: 1
|
|
152
275
|
},
|
|
153
|
-
cost: {
|
|
154
|
-
color: _macOSDesignSystemColors.macOSColors.text.icon,
|
|
155
|
-
fontSize: 9,
|
|
156
|
-
marginTop: 1,
|
|
157
|
-
fontVariant: ["tabular-nums"]
|
|
158
|
-
},
|
|
159
276
|
motion: {
|
|
160
277
|
paddingHorizontal: 7,
|
|
161
278
|
paddingVertical: 4,
|
|
@@ -164,17 +281,74 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
164
281
|
borderColor: _macOSDesignSystemColors.macOSColors.border.default,
|
|
165
282
|
backgroundColor: "rgba(38,38,42,0.8)"
|
|
166
283
|
},
|
|
167
|
-
motionOn: {
|
|
168
|
-
borderColor: _macOSDesignSystemColors.macOSColors.semantic.info + "55",
|
|
169
|
-
backgroundColor: _macOSDesignSystemColors.macOSColors.semantic.infoBackground
|
|
170
|
-
},
|
|
171
284
|
motionText: {
|
|
172
285
|
fontSize: 9,
|
|
173
286
|
fontWeight: "700",
|
|
174
287
|
letterSpacing: 0.6,
|
|
175
288
|
color: _macOSDesignSystemColors.macOSColors.text.muted
|
|
176
289
|
},
|
|
177
|
-
|
|
290
|
+
sliderRow: {
|
|
291
|
+
flexDirection: "row",
|
|
292
|
+
alignItems: "center",
|
|
293
|
+
gap: 8,
|
|
294
|
+
marginTop: 7
|
|
295
|
+
},
|
|
296
|
+
live: {
|
|
297
|
+
paddingHorizontal: 7,
|
|
298
|
+
paddingVertical: 3,
|
|
299
|
+
borderRadius: 6,
|
|
300
|
+
borderWidth: 1,
|
|
301
|
+
borderColor: _macOSDesignSystemColors.macOSColors.border.default,
|
|
302
|
+
backgroundColor: "rgba(38,38,42,0.8)"
|
|
303
|
+
},
|
|
304
|
+
liveOn: {
|
|
305
|
+
borderColor: _macOSDesignSystemColors.macOSColors.semantic.success + "55",
|
|
306
|
+
backgroundColor: _macOSDesignSystemColors.macOSColors.semantic.successBackground
|
|
307
|
+
},
|
|
308
|
+
liveText: {
|
|
309
|
+
fontSize: 9,
|
|
310
|
+
fontWeight: "700",
|
|
311
|
+
letterSpacing: 0.6,
|
|
312
|
+
color: _macOSDesignSystemColors.macOSColors.text.muted
|
|
313
|
+
},
|
|
314
|
+
liveTextOn: {
|
|
315
|
+
color: _macOSDesignSystemColors.macOSColors.semantic.success
|
|
316
|
+
},
|
|
317
|
+
playOn: {
|
|
318
|
+
borderColor: _macOSDesignSystemColors.macOSColors.semantic.info + "55",
|
|
319
|
+
backgroundColor: _macOSDesignSystemColors.macOSColors.semantic.infoBackground
|
|
320
|
+
},
|
|
321
|
+
playTextOn: {
|
|
178
322
|
color: _macOSDesignSystemColors.macOSColors.semantic.info
|
|
323
|
+
},
|
|
324
|
+
track: {
|
|
325
|
+
flex: 1,
|
|
326
|
+
height: 24,
|
|
327
|
+
justifyContent: "center"
|
|
328
|
+
},
|
|
329
|
+
trackBar: {
|
|
330
|
+
height: 3,
|
|
331
|
+
borderRadius: 1.5,
|
|
332
|
+
backgroundColor: "rgba(120,130,150,0.35)"
|
|
333
|
+
},
|
|
334
|
+
thumb: {
|
|
335
|
+
position: "absolute",
|
|
336
|
+
top: (24 - THUMB) / 2,
|
|
337
|
+
width: THUMB,
|
|
338
|
+
height: THUMB,
|
|
339
|
+
borderRadius: THUMB / 2,
|
|
340
|
+
backgroundColor: _macOSDesignSystemColors.macOSColors.text.secondary,
|
|
341
|
+
borderWidth: 1,
|
|
342
|
+
borderColor: "rgba(255,255,255,0.5)"
|
|
343
|
+
},
|
|
344
|
+
thumbScrub: {
|
|
345
|
+
backgroundColor: _macOSDesignSystemColors.macOSColors.semantic.info
|
|
346
|
+
},
|
|
347
|
+
time: {
|
|
348
|
+
color: _macOSDesignSystemColors.macOSColors.text.secondary,
|
|
349
|
+
fontSize: 10,
|
|
350
|
+
fontVariant: ["tabular-nums"],
|
|
351
|
+
width: 32,
|
|
352
|
+
textAlign: "right"
|
|
179
353
|
}
|
|
180
354
|
});
|
|
@@ -11,27 +11,65 @@ top.
|
|
|
11
11
|
</View>
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
The preset is a single shared, persisted choice (`backgroundStore
|
|
15
|
-
switching it in one tool switches it everywhere.
|
|
16
|
-
|
|
14
|
+
The preset is a single shared, persisted choice (`backgroundStore`, default:
|
|
15
|
+
**Hyperspace**), so switching it in one tool switches it everywhere. Shipping
|
|
16
|
+
surfaces get it automatically: `JsModal` and `BottomSheet` mount
|
|
17
|
+
`<ToolBackground />` inside their content box by default (opt out per tool
|
|
18
|
+
with `background={false}`; the network tool does, because it mounts its own so
|
|
19
|
+
the desktop inline panel matches).
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
`<BackgroundSwitcher />` is the ‹ prev / next › review strip used to audition
|
|
22
|
+
the catalogue (plus Live Sky's time slider and ▶ movie). It is currently
|
|
23
|
+
mounted NOWHERE — the catalogue picked its winner — but stays in the package:
|
|
24
|
+
drop it back at the top of a tool body to audition changes.
|
|
25
|
+
|
|
26
|
+
## The catalogue
|
|
27
|
+
|
|
28
|
+
Round 3 cut the set to the keepers plus three underwater variations (round 4
|
|
29
|
+
folded the Meteors preset into Deep Field):
|
|
19
30
|
|
|
20
|
-
- **Starfield** (`variants/starfield.tsx`) — the cheap scenic preset: 99 stars
|
|
21
|
-
quantised into five depth bands.
|
|
22
31
|
- **Deep Field** (`variants/deepField.tsx`) — the Everlights
|
|
23
32
|
`ParallaxDriftBackground`, ported 1:1: same seed and generator call order,
|
|
24
33
|
same 160 stars, same far-plane bias, sizes, palette and drift. Speed is
|
|
25
34
|
quantised into 18 bands (a distinct speed costs a distinct animated node);
|
|
26
|
-
everything else stays per-star.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
everything else stays per-star. The priciest preset — the file documents
|
|
36
|
+
exactly what could and couldn't come across from Skia. The shipping preset
|
|
37
|
+
also composes the **meteor layer** (`variants/meteors.tsx`, once its own
|
|
38
|
+
"Meteors" preset): shooting stars that streak in, hit a WALL of the surface
|
|
39
|
+
and burst — flash, expanding shockwave ring (half-clipped by the edge, which
|
|
40
|
+
sells the impact), sparks ricocheting back into the room; each meteor's
|
|
41
|
+
whole life cycle is keyframed into interpolate stops on one plain loop.
|
|
42
|
+
`DeepFieldTunable` stays meteor-free for the bench.
|
|
43
|
+
- **Hyperspace** (`variants/hyperspace.tsx`) — the Everlights Skia
|
|
44
|
+
`HyperspaceBackground` (lightspeed starfield) re-derived for this contract:
|
|
45
|
+
the per-frame `screen = plane/z` projection becomes scale keyframes on
|
|
46
|
+
phase-offset shells (a layer scaled by 1/z moves every star on exactly the
|
|
47
|
+
original's hyperbolic curve), with the wrap snap hidden inside the
|
|
48
|
+
original's own 6% fade edges. One animated node total; raster OFF on the
|
|
49
|
+
shells because a cached bitmap scaled 8× goes to mush.
|
|
50
|
+
- **Live Sky** (`variants/liveSky.tsx`) — the cove on the real local clock:
|
|
51
|
+
sun up 06:30–19:00 on a sine arc, moon on the night shift, dawn/golden/
|
|
52
|
+
sunset/dusk as lerped palette keyframes (night = Moonlight's palette, noon =
|
|
53
|
+
Daylight's). ZERO extra animated nodes — the sun moves ~0.4px/minute, so the
|
|
54
|
+
scene is laid out statically per minute and a once-a-minute AppState-gated
|
|
55
|
+
tick re-renders it; this also sidesteps the native driver's
|
|
56
|
+
transform/opacity-only limit for the colour transitions. Discs draw UNDER
|
|
57
|
+
the opaque sea so the horizon clips a rising/setting body for free. While
|
|
58
|
+
it's showing, the switcher grows a time slider (writes `skyClock.ts`,
|
|
59
|
+
5-minute quantized, not persisted) to scrub the whole cycle by hand; LIVE
|
|
60
|
+
snaps back to the clock. See `LIVE_SKY_PLAN.md`.
|
|
61
|
+
- **The underwater family** (`variants/underwater.tsx`) — Abyss, Bubbles,
|
|
62
|
+
Jellyfish. One register — lit from the surface, near-black at the bottom,
|
|
63
|
+
motion in tens of seconds — with different life in the water: marine snow,
|
|
64
|
+
rising bubbles (a ring plus an off-centre highlight is what makes a bubble),
|
|
65
|
+
and pulsing jellies that traverse bottom-to-top entirely off-screen at the
|
|
66
|
+
loop seam.
|
|
67
|
+
|
|
68
|
+
Catalogue trims (2026-08-20): Off, Night Drift, Moonlight, Daylight, Kelp and
|
|
69
|
+
Kelp Forest were cut from the registry. `variants/moonlight.tsx` and
|
|
70
|
+
`variants/daylight.tsx` stay in the tree — Live Sky's palettes are documented
|
|
71
|
+
against them and the bg-bench lanes (`MoonlightTunable`/`DaylightTunable`)
|
|
72
|
+
still drive them — they just aren't offered in the switcher.
|
|
35
73
|
|
|
36
74
|
## The constraint
|
|
37
75
|
|
|
@@ -58,6 +96,15 @@ translucent layers. For the same reason nothing here uses `transformOrigin`
|
|
|
58
96
|
|
|
59
97
|
Loops stop when the app backgrounds, and unmount with the tool.
|
|
60
98
|
|
|
99
|
+
4. **Geometry never scales with the modal.** Resizing the surface must crop
|
|
100
|
+
the scene, not stretch it — rays, plants, blooms and particles size
|
|
101
|
+
themselves from `Dimensions.get("window")` and only their POSITIONS
|
|
102
|
+
distribute across the measured width/height (the preset root clips with
|
|
103
|
+
`overflow: "hidden"`). Smooth gradients (base washes, vignettes) may track
|
|
104
|
+
the live size — their scaling is imperceptible. The one sanctioned
|
|
105
|
+
exception is geometry that is ABOUT the surface itself, like the meteors'
|
|
106
|
+
wall-impact points.
|
|
107
|
+
|
|
61
108
|
**Known trap:** `Animated.sequence([timing, Animated.loop(...)])` silently fails
|
|
62
109
|
to drive the value on the native driver — no warning, the field renders and
|
|
63
110
|
never moves. It's the obvious way to phase-offset siblings; don't. Stagger via
|
|
@@ -66,8 +113,9 @@ different periods, or bake the offset into the `interpolate`.
|
|
|
66
113
|
### Measured
|
|
67
114
|
|
|
68
115
|
Network tool open on an iPhone 16 simulator, via perf-monitor and
|
|
69
|
-
`measure_renders`. Starfield
|
|
70
|
-
|
|
116
|
+
`measure_renders`. (Starfield and the lattice presets were measured before the
|
|
117
|
+
round-3 cull; the rows stay as calibration points — Deep Field is still the
|
|
118
|
+
ceiling, and the newer presets all run 3–7 nodes.)
|
|
71
119
|
|
|
72
120
|
**Steady state** — 12–13s idle recordings:
|
|
73
121
|
|
|
@@ -87,6 +135,40 @@ The commits in every column are the network tool's own once-a-minute
|
|
|
87
135
|
reported one component render in every configuration — the animation contributes
|
|
88
136
|
zero React work while it runs, which is rule 1 holding regardless of node count.
|
|
89
137
|
|
|
138
|
+
**Moonlight v3** (buoy-optimize loop, iPhone 17 Pro sim, bg-bench, 3-run
|
|
139
|
+
medians, reproduced across two batches):
|
|
140
|
+
|
|
141
|
+
| | UI FPS | UI jank | JS FPS |
|
|
142
|
+
|---|---:|---:|---:|
|
|
143
|
+
| control (no background) | 60.0 | 0 | 58.3 |
|
|
144
|
+
| Moonlight, shipping (~330 views, 5 nodes) | 59.9 | 0 | 57.5–58.1 |
|
|
145
|
+
| glade ×12 (~1.2k blocks) | 59.5 | 0 | 57.0 |
|
|
146
|
+
| glade ×30 (~3.1k blocks) | 56.4–56.8 | 2 | 53.8 |
|
|
147
|
+
|
|
148
|
+
Shipping Moonlight is indistinguishable from no background; the UI cliff sits
|
|
149
|
+
between 12× and 30× the shipping glade. Structural variants (fused glade,
|
|
150
|
+
raster off, chrome off) all tied with shipping at 1× AND failed to separate at
|
|
151
|
+
×30 (±1 FPS, and raster-off ballooned memory 287→351MB), so the shipping
|
|
152
|
+
structure stays. Bench lane: `bg-bench?v=ml-*` drives `MoonlightTunable`.
|
|
153
|
+
|
|
154
|
+
**Confirmed on a real iPhone SE (375×667, 60Hz)** — both in-batch controls
|
|
155
|
+
held 60.0/0 (zero drift): ship 59.6 UI / 0 UI jank; ×12 58.8 / 1; ×30 breaks
|
|
156
|
+
(54.4 UI, CPU 57). Same ranking as the sim; the sim → device carry-over held.
|
|
157
|
+
|
|
158
|
+
**Animated vs static (real iPhone, same batch):** identical — 59.6 UI / 0
|
|
159
|
+
jank / 0 JS jank both; static saved ~2.7 CPU points, less than the batch's
|
|
160
|
+
own control-to-control CPU spread (4.4), though the direction repeated on the
|
|
161
|
+
sim (~2.4). The motion toggle is a preference, not a perf lever, at shipping
|
|
162
|
+
scale — which is the native-driver contract holding. (A JS-jank scare in an
|
|
163
|
+
earlier batch attributed ~3 events to the ml cases; a later batch put 4 on a
|
|
164
|
+
CONTROL, so those are between-case reload noise.)
|
|
165
|
+
|
|
166
|
+
**Benchmark-protocol note:** the batch's FINAL case is unreliable — in two
|
|
167
|
+
consecutive batches the trailing control recorded byte-identical bad numbers
|
|
168
|
+
(53.7 UI FPS, 4 JS jank, only 2/3 runs) while an identical control at slot 1
|
|
169
|
+
was clean, so the batch teardown bleeds into the last recording. End every
|
|
170
|
+
batch with a sacrificial case, the same way it starts with one.
|
|
171
|
+
|
|
90
172
|
**Mount** — the cost that is real for all of them:
|
|
91
173
|
|
|
92
174
|
```
|
|
@@ -110,6 +192,6 @@ Add the component to `variants/`, then one entry in `registry.tsx` with an
|
|
|
110
192
|
honest `nodes` (animated nodes) and `views` (static views at ~380×620). The
|
|
111
193
|
switcher shows both, which is the point — it's a review tool, not decoration.
|
|
112
194
|
|
|
113
|
-
Alpha calibration:
|
|
114
|
-
disappears under
|
|
115
|
-
or a second pixel of height to register at all.
|
|
195
|
+
Alpha calibration: every preset runs a 0.4–0.55 vignette, and anything fainter
|
|
196
|
+
than ~0.2 alpha disappears under it — hairline strokes in particular need ~0.2
|
|
197
|
+
alpha or a second pixel of height to register at all.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.backgroundStore = void 0;
|
|
6
|
+
exports.backgroundStore = exports.BACKGROUND_STORAGE_KEY = void 0;
|
|
7
7
|
exports.useToolBackground = useToolBackground;
|
|
8
8
|
var _react = require("react");
|
|
9
9
|
var _devToolsStorageKeys = require("../../storage/devToolsStorageKeys.js");
|
|
@@ -19,7 +19,20 @@ var _registry = require("./registry.js");
|
|
|
19
19
|
* choice the user already made.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
// v3: bumped when the default became "off" (backgrounds are opt-in now), so
|
|
23
|
+
// devices that stepped the switcher during the (unreleased) audition rounds
|
|
24
|
+
// re-default to none instead of keeping a pre-release pick forever. Earlier
|
|
25
|
+
// keys are simply abandoned, same as v1 was.
|
|
26
|
+
const KEY = `${_devToolsStorageKeys.devToolsStorageKeys.base}_tool_background_v3`;
|
|
27
|
+
|
|
28
|
+
/** The persisted-selection key, exported so a host can ask "has the user ever
|
|
29
|
+
* picked a background?" (the desktop's first-run hint modal reads it). */
|
|
30
|
+
const BACKGROUND_STORAGE_KEY = exports.BACKGROUND_STORAGE_KEY = KEY;
|
|
31
|
+
|
|
32
|
+
// Motion is deliberately NOT persisted: the switcher no longer offers a
|
|
33
|
+
// motion toggle, so every session starts animated. `setMotion` survives as a
|
|
34
|
+
// session-only seam (bench harnesses, a tool forcing stillness).
|
|
35
|
+
|
|
23
36
|
function isValidId(value) {
|
|
24
37
|
return typeof value === "string" && _registry.BACKGROUND_PRESETS.some(p => p.id === value);
|
|
25
38
|
}
|
|
@@ -52,8 +65,10 @@ class BackgroundStore {
|
|
|
52
65
|
const raw = await _persistentStorage.persistentStorage.getItem(KEY);
|
|
53
66
|
if (!raw || this.dirty) return;
|
|
54
67
|
const parsed = JSON.parse(raw);
|
|
55
|
-
|
|
56
|
-
|
|
68
|
+
// The standalone Meteors preset was folded into Deep Field — re-home
|
|
69
|
+
// anyone still persisted on it rather than dropping them to the default.
|
|
70
|
+
const id = parsed.id === "meteors" ? "deepfield" : parsed.id;
|
|
71
|
+
if (isValidId(id)) this.id = id;
|
|
57
72
|
this.notify();
|
|
58
73
|
} catch {
|
|
59
74
|
// A missing or corrupt entry just means "use the default".
|
|
@@ -64,8 +79,7 @@ class BackgroundStore {
|
|
|
64
79
|
persist() {
|
|
65
80
|
this.dirty = true;
|
|
66
81
|
const payload = {
|
|
67
|
-
id: this.id
|
|
68
|
-
motion: this.motion
|
|
82
|
+
id: this.id
|
|
69
83
|
};
|
|
70
84
|
void _persistentStorage.persistentStorage.setItem(KEY, JSON.stringify(payload)).catch(() => {});
|
|
71
85
|
}
|