@buoy-gg/shared-ui 7.0.22 → 7.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/lib/commonjs/JsModal.js +39 -16
  2. package/lib/commonjs/dataViewer/JsonStringNotice.js +59 -0
  3. package/lib/commonjs/dataViewer/ThemedSplitView.js +20 -1
  4. package/lib/commonjs/dataViewer/TimestampDeltaBar.js +96 -0
  5. package/lib/commonjs/dataViewer/index.js +52 -0
  6. package/lib/commonjs/dataViewer/jsonStringValue.js +94 -0
  7. package/lib/commonjs/dataViewer/timestampDiff.js +146 -0
  8. package/lib/commonjs/hooks/safe-area-impl.js +1 -1
  9. package/lib/commonjs/index.js +50 -1
  10. package/lib/commonjs/license/FeatureGate.js +43 -10
  11. package/lib/commonjs/license/FreeAccountPrompt.js +181 -0
  12. package/lib/commonjs/license/index.js +57 -42
  13. package/lib/commonjs/license/licenseModule.js +99 -0
  14. package/lib/commonjs/license/tierLimits.js +165 -0
  15. package/lib/commonjs/ui/backgrounds/BackgroundSwitcher.js +180 -0
  16. package/lib/commonjs/ui/backgrounds/README.md +115 -0
  17. package/lib/commonjs/ui/backgrounds/ToolBackground.js +74 -0
  18. package/lib/commonjs/ui/backgrounds/backgroundStore.js +116 -0
  19. package/lib/commonjs/ui/backgrounds/index.js +70 -0
  20. package/lib/commonjs/ui/backgrounds/primitives.js +333 -0
  21. package/lib/commonjs/ui/backgrounds/registry.js +112 -0
  22. package/lib/commonjs/ui/backgrounds/rng.js +72 -0
  23. package/lib/commonjs/ui/backgrounds/types.js +1 -0
  24. package/lib/commonjs/ui/backgrounds/variants/deepField.js +351 -0
  25. package/lib/commonjs/ui/backgrounds/variants/lattice.js +579 -0
  26. package/lib/commonjs/ui/backgrounds/variants/starfield.js +210 -0
  27. package/lib/commonjs/ui/index.js +15 -3
  28. package/lib/module/JsModal.js +39 -16
  29. package/lib/module/dataViewer/JsonStringNotice.js +54 -0
  30. package/lib/module/dataViewer/ThemedSplitView.js +20 -1
  31. package/lib/module/dataViewer/TimestampDeltaBar.js +91 -0
  32. package/lib/module/dataViewer/index.js +4 -0
  33. package/lib/module/dataViewer/jsonStringValue.js +89 -0
  34. package/lib/module/dataViewer/timestampDiff.js +139 -0
  35. package/lib/module/hooks/safe-area-impl.js +1 -1
  36. package/lib/module/index.js +1 -1
  37. package/lib/module/license/FeatureGate.js +43 -10
  38. package/lib/module/license/FreeAccountPrompt.js +176 -0
  39. package/lib/module/license/index.js +5 -40
  40. package/lib/module/license/licenseModule.js +92 -0
  41. package/lib/module/license/tierLimits.js +158 -0
  42. package/lib/module/ui/backgrounds/BackgroundSwitcher.js +176 -0
  43. package/lib/module/ui/backgrounds/README.md +115 -0
  44. package/lib/module/ui/backgrounds/ToolBackground.js +70 -0
  45. package/lib/module/ui/backgrounds/backgroundStore.js +111 -0
  46. package/lib/module/ui/backgrounds/index.js +9 -0
  47. package/lib/module/ui/backgrounds/primitives.js +328 -0
  48. package/lib/module/ui/backgrounds/registry.js +105 -0
  49. package/lib/module/ui/backgrounds/rng.js +65 -0
  50. package/lib/module/ui/backgrounds/types.js +1 -0
  51. package/lib/module/ui/backgrounds/variants/deepField.js +347 -0
  52. package/lib/module/ui/backgrounds/variants/lattice.js +575 -0
  53. package/lib/module/ui/backgrounds/variants/starfield.js +206 -0
  54. package/lib/module/ui/index.js +3 -0
  55. package/lib/typescript/commonjs/JsModal.d.ts.map +1 -1
  56. package/lib/typescript/commonjs/dataViewer/JsonStringNotice.d.ts +6 -0
  57. package/lib/typescript/commonjs/dataViewer/JsonStringNotice.d.ts.map +1 -0
  58. package/lib/typescript/commonjs/dataViewer/ThemedSplitView.d.ts.map +1 -1
  59. package/lib/typescript/commonjs/dataViewer/TimestampDeltaBar.d.ts +7 -0
  60. package/lib/typescript/commonjs/dataViewer/TimestampDeltaBar.d.ts.map +1 -0
  61. package/lib/typescript/commonjs/dataViewer/index.d.ts +6 -0
  62. package/lib/typescript/commonjs/dataViewer/index.d.ts.map +1 -1
  63. package/lib/typescript/commonjs/dataViewer/jsonStringValue.d.ts +25 -0
  64. package/lib/typescript/commonjs/dataViewer/jsonStringValue.d.ts.map +1 -0
  65. package/lib/typescript/commonjs/dataViewer/timestampDiff.d.ts +55 -0
  66. package/lib/typescript/commonjs/dataViewer/timestampDiff.d.ts.map +1 -0
  67. package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
  68. package/lib/typescript/commonjs/index.d.ts +2 -2
  69. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  70. package/lib/typescript/commonjs/license/FeatureGate.d.ts +19 -3
  71. package/lib/typescript/commonjs/license/FeatureGate.d.ts.map +1 -1
  72. package/lib/typescript/commonjs/license/FreeAccountPrompt.d.ts +23 -0
  73. package/lib/typescript/commonjs/license/FreeAccountPrompt.d.ts.map +1 -0
  74. package/lib/typescript/commonjs/license/index.d.ts +6 -15
  75. package/lib/typescript/commonjs/license/index.d.ts.map +1 -1
  76. package/lib/typescript/commonjs/license/licenseModule.d.ts +55 -0
  77. package/lib/typescript/commonjs/license/licenseModule.d.ts.map +1 -0
  78. package/lib/typescript/commonjs/license/tierLimits.d.ts +143 -0
  79. package/lib/typescript/commonjs/license/tierLimits.d.ts.map +1 -0
  80. package/lib/typescript/commonjs/ui/backgrounds/BackgroundSwitcher.d.ts +13 -0
  81. package/lib/typescript/commonjs/ui/backgrounds/BackgroundSwitcher.d.ts.map +1 -0
  82. package/lib/typescript/commonjs/ui/backgrounds/ToolBackground.d.ts +30 -0
  83. package/lib/typescript/commonjs/ui/backgrounds/ToolBackground.d.ts.map +1 -0
  84. package/lib/typescript/commonjs/ui/backgrounds/backgroundStore.d.ts +43 -0
  85. package/lib/typescript/commonjs/ui/backgrounds/backgroundStore.d.ts.map +1 -0
  86. package/lib/typescript/commonjs/ui/backgrounds/index.d.ts +7 -0
  87. package/lib/typescript/commonjs/ui/backgrounds/index.d.ts.map +1 -0
  88. package/lib/typescript/commonjs/ui/backgrounds/primitives.d.ts +106 -0
  89. package/lib/typescript/commonjs/ui/backgrounds/primitives.d.ts.map +1 -0
  90. package/lib/typescript/commonjs/ui/backgrounds/registry.d.ts +28 -0
  91. package/lib/typescript/commonjs/ui/backgrounds/registry.d.ts.map +1 -0
  92. package/lib/typescript/commonjs/ui/backgrounds/rng.d.ts +27 -0
  93. package/lib/typescript/commonjs/ui/backgrounds/rng.d.ts.map +1 -0
  94. package/lib/typescript/commonjs/ui/backgrounds/types.d.ts +14 -0
  95. package/lib/typescript/commonjs/ui/backgrounds/types.d.ts.map +1 -0
  96. package/lib/typescript/commonjs/ui/backgrounds/variants/deepField.d.ts +65 -0
  97. package/lib/typescript/commonjs/ui/backgrounds/variants/deepField.d.ts.map +1 -0
  98. package/lib/typescript/commonjs/ui/backgrounds/variants/lattice.d.ts +33 -0
  99. package/lib/typescript/commonjs/ui/backgrounds/variants/lattice.d.ts.map +1 -0
  100. package/lib/typescript/commonjs/ui/backgrounds/variants/starfield.d.ts +12 -0
  101. package/lib/typescript/commonjs/ui/backgrounds/variants/starfield.d.ts.map +1 -0
  102. package/lib/typescript/commonjs/ui/index.d.ts +1 -0
  103. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  104. package/lib/typescript/module/JsModal.d.ts.map +1 -1
  105. package/lib/typescript/module/dataViewer/JsonStringNotice.d.ts +6 -0
  106. package/lib/typescript/module/dataViewer/JsonStringNotice.d.ts.map +1 -0
  107. package/lib/typescript/module/dataViewer/ThemedSplitView.d.ts.map +1 -1
  108. package/lib/typescript/module/dataViewer/TimestampDeltaBar.d.ts +7 -0
  109. package/lib/typescript/module/dataViewer/TimestampDeltaBar.d.ts.map +1 -0
  110. package/lib/typescript/module/dataViewer/index.d.ts +6 -0
  111. package/lib/typescript/module/dataViewer/index.d.ts.map +1 -1
  112. package/lib/typescript/module/dataViewer/jsonStringValue.d.ts +25 -0
  113. package/lib/typescript/module/dataViewer/jsonStringValue.d.ts.map +1 -0
  114. package/lib/typescript/module/dataViewer/timestampDiff.d.ts +55 -0
  115. package/lib/typescript/module/dataViewer/timestampDiff.d.ts.map +1 -0
  116. package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
  117. package/lib/typescript/module/index.d.ts +2 -2
  118. package/lib/typescript/module/index.d.ts.map +1 -1
  119. package/lib/typescript/module/license/FeatureGate.d.ts +19 -3
  120. package/lib/typescript/module/license/FeatureGate.d.ts.map +1 -1
  121. package/lib/typescript/module/license/FreeAccountPrompt.d.ts +23 -0
  122. package/lib/typescript/module/license/FreeAccountPrompt.d.ts.map +1 -0
  123. package/lib/typescript/module/license/index.d.ts +6 -15
  124. package/lib/typescript/module/license/index.d.ts.map +1 -1
  125. package/lib/typescript/module/license/licenseModule.d.ts +55 -0
  126. package/lib/typescript/module/license/licenseModule.d.ts.map +1 -0
  127. package/lib/typescript/module/license/tierLimits.d.ts +143 -0
  128. package/lib/typescript/module/license/tierLimits.d.ts.map +1 -0
  129. package/lib/typescript/module/ui/backgrounds/BackgroundSwitcher.d.ts +13 -0
  130. package/lib/typescript/module/ui/backgrounds/BackgroundSwitcher.d.ts.map +1 -0
  131. package/lib/typescript/module/ui/backgrounds/ToolBackground.d.ts +30 -0
  132. package/lib/typescript/module/ui/backgrounds/ToolBackground.d.ts.map +1 -0
  133. package/lib/typescript/module/ui/backgrounds/backgroundStore.d.ts +43 -0
  134. package/lib/typescript/module/ui/backgrounds/backgroundStore.d.ts.map +1 -0
  135. package/lib/typescript/module/ui/backgrounds/index.d.ts +7 -0
  136. package/lib/typescript/module/ui/backgrounds/index.d.ts.map +1 -0
  137. package/lib/typescript/module/ui/backgrounds/primitives.d.ts +106 -0
  138. package/lib/typescript/module/ui/backgrounds/primitives.d.ts.map +1 -0
  139. package/lib/typescript/module/ui/backgrounds/registry.d.ts +28 -0
  140. package/lib/typescript/module/ui/backgrounds/registry.d.ts.map +1 -0
  141. package/lib/typescript/module/ui/backgrounds/rng.d.ts +27 -0
  142. package/lib/typescript/module/ui/backgrounds/rng.d.ts.map +1 -0
  143. package/lib/typescript/module/ui/backgrounds/types.d.ts +14 -0
  144. package/lib/typescript/module/ui/backgrounds/types.d.ts.map +1 -0
  145. package/lib/typescript/module/ui/backgrounds/variants/deepField.d.ts +65 -0
  146. package/lib/typescript/module/ui/backgrounds/variants/deepField.d.ts.map +1 -0
  147. package/lib/typescript/module/ui/backgrounds/variants/lattice.d.ts +33 -0
  148. package/lib/typescript/module/ui/backgrounds/variants/lattice.d.ts.map +1 -0
  149. package/lib/typescript/module/ui/backgrounds/variants/starfield.d.ts +12 -0
  150. package/lib/typescript/module/ui/backgrounds/variants/starfield.d.ts.map +1 -0
  151. package/lib/typescript/module/ui/index.d.ts +1 -0
  152. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  153. package/package.json +4 -4
  154. package/scripts/detect-safe-area.js +0 -20
@@ -0,0 +1,328 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Drawing + animation primitives for the tool backgrounds.
5
+ *
6
+ * HARD CONSTRAINT: Buoy's packages peer-depend on nothing but `react` and
7
+ * `react-native`. No Skia, no Reanimated, no SVG, no gradient library — a
8
+ * devtool cannot force a native dependency into someone's app. So every
9
+ * background here is built out of plain `View`s plus `Animated`, and the
10
+ * gradients/glows below are *faked* by stacking translucent layers.
11
+ *
12
+ * That sounds expensive. It isn't, because of two rules every preset follows:
13
+ *
14
+ * 1. **Every animation is native-driven.** `useNativeDriver: true` on a
15
+ * looping `Animated.timing` means the whole loop is handed to the UI thread
16
+ * once and never touches JS again — no per-frame callback, no re-render, no
17
+ * bridge traffic while it runs. A preset animates a handful of *layers*
18
+ * (transform on one parent view), never per-particle.
19
+ *
20
+ * 2. **Every static layer is rasterized.** `shouldRasterizeIOS` /
21
+ * `renderToHardwareTextureAndroid` turn a layer of 40 dots into a single
22
+ * cached GPU texture, so sliding it is one composite — the same cost as
23
+ * sliding one view. See `RasterLayer`.
24
+ *
25
+ * The result: a preset costs a one-time mount of static views and then ~0
26
+ * JS-thread work per frame. See `packages/shared/src/ui/backgrounds/README.md`.
27
+ */
28
+
29
+ import { memo, useEffect, useRef } from "react";
30
+ import { Animated, AppState, Easing, Platform, StyleSheet, View } from "react-native";
31
+ import { absoluteFill } from "../../utils/absoluteFill.js";
32
+ import { withAlpha } from "./rng.js";
33
+
34
+ // ============================================================================
35
+ // Animation
36
+ // ============================================================================
37
+
38
+ /**
39
+ * A native-driven 0→1 loop.
40
+ *
41
+ * The returned value is only ever consumed by `interpolate` inside an
42
+ * `Animated.View` style, so React never re-renders while it runs. The loop is
43
+ * stopped when the app backgrounds (a background animation is pure battery
44
+ * burn) and restarted on foreground.
45
+ */
46
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
47
+ export function useLoop(durationMs, enabled = true) {
48
+ const value = useRef(new Animated.Value(0)).current;
49
+ useEffect(() => {
50
+ if (!enabled) return;
51
+ let anim = null;
52
+ const start = () => {
53
+ if (anim) return;
54
+ value.setValue(0);
55
+ anim = Animated.loop(Animated.timing(value, {
56
+ toValue: 1,
57
+ duration: durationMs,
58
+ easing: Easing.linear,
59
+ useNativeDriver: true
60
+ }));
61
+ anim.start();
62
+ };
63
+ const stop = () => {
64
+ anim?.stop();
65
+ anim = null;
66
+ value.setValue(0);
67
+ };
68
+ start();
69
+ const sub = AppState.addEventListener("change", state => {
70
+ if (state === "active") start();else stop();
71
+ });
72
+ return () => {
73
+ stop();
74
+ sub.remove();
75
+ };
76
+ }, [durationMs, enabled, value]);
77
+ return value;
78
+ }
79
+
80
+ /**
81
+ * A native-driven 0→1→0 breathe, for glows that pulse rather than travel.
82
+ * Same no-JS-per-frame guarantee as `useLoop`.
83
+ */
84
+ export function usePulse(durationMs, enabled = true) {
85
+ const value = useRef(new Animated.Value(0)).current;
86
+ useEffect(() => {
87
+ if (!enabled) return;
88
+ let anim = null;
89
+ const start = () => {
90
+ if (anim) return;
91
+ value.setValue(0);
92
+ anim = Animated.loop(Animated.sequence([Animated.timing(value, {
93
+ toValue: 1,
94
+ duration: durationMs / 2,
95
+ easing: Easing.inOut(Easing.quad),
96
+ useNativeDriver: true
97
+ }), Animated.timing(value, {
98
+ toValue: 0,
99
+ duration: durationMs / 2,
100
+ easing: Easing.inOut(Easing.quad),
101
+ useNativeDriver: true
102
+ })]));
103
+ anim.start();
104
+ };
105
+ const stop = () => {
106
+ anim?.stop();
107
+ anim = null;
108
+ };
109
+ start();
110
+ const sub = AppState.addEventListener("change", state => {
111
+ if (state === "active") start();else stop();
112
+ });
113
+ return () => {
114
+ stop();
115
+ sub.remove();
116
+ };
117
+ }, [durationMs, enabled, value]);
118
+ return value;
119
+ }
120
+
121
+ // ============================================================================
122
+ // Layers
123
+ // ============================================================================
124
+
125
+ /**
126
+ * A layer whose children are static and should be flattened into one GPU
127
+ * texture. Wrap anything that gets translated/scaled as a whole — this is what
128
+ * makes a 40-dot star band cost the same to move as a single view.
129
+ */
130
+ export const RasterLayer = /*#__PURE__*/memo(function RasterLayer({
131
+ style,
132
+ raster = true,
133
+ children
134
+ }) {
135
+ return /*#__PURE__*/_jsx(View, {
136
+ style: style,
137
+ pointerEvents: "none"
138
+ // Both are no-ops on the platform they don't belong to, and on web
139
+ // (react-native-web) neither exists — there the browser already
140
+ // composites a transformed subtree on the GPU.
141
+ ,
142
+ shouldRasterizeIOS: raster && Platform.OS === "ios",
143
+ renderToHardwareTextureAndroid: raster && Platform.OS === "android",
144
+ children: children
145
+ });
146
+ });
147
+
148
+ /**
149
+ * Fake radial gradient: concentric rounded views, each a constant per-ring
150
+ * alpha, with radii shrinking on a curve. Alpha compositing of N equal layers
151
+ * gives `1 - (1-a)^N` at the centre, so `a` is solved from the requested peak
152
+ * alpha and the falloff comes from the radius curve.
153
+ *
154
+ * 8 rings is the sweet spot on a dark surface — below that you can see banding,
155
+ * above it you're paying for views nobody can distinguish.
156
+ */
157
+ export const RadialGlow = /*#__PURE__*/memo(function RadialGlow({
158
+ size,
159
+ color,
160
+ alpha = 0.35,
161
+ rings = 8,
162
+ falloff = 1.7,
163
+ style
164
+ }) {
165
+ const per = 1 - Math.pow(1 - alpha, 1 / rings);
166
+ const layers = [];
167
+ for (let i = 0; i < rings; i++) {
168
+ const d = size * Math.pow((rings - i) / rings, falloff);
169
+ layers.push(/*#__PURE__*/_jsx(View, {
170
+ style: {
171
+ position: "absolute",
172
+ left: (size - d) / 2,
173
+ top: (size - d) / 2,
174
+ width: d,
175
+ height: d,
176
+ borderRadius: d / 2,
177
+ backgroundColor: withAlpha(color, per)
178
+ }
179
+ }, i));
180
+ }
181
+ return /*#__PURE__*/_jsx(View, {
182
+ style: [{
183
+ width: size,
184
+ height: size
185
+ }, style],
186
+ pointerEvents: "none",
187
+ shouldRasterizeIOS: Platform.OS === "ios",
188
+ renderToHardwareTextureAndroid: Platform.OS === "android",
189
+ children: layers
190
+ });
191
+ });
192
+
193
+ /**
194
+ * Fake linear gradient: `bands` stacked strips with linearly interpolated
195
+ * alpha. Used for scrims (keep text readable over a busy field) and for
196
+ * horizon glows. 12–16 bands is invisible as banding at these alphas.
197
+ */
198
+ export const LinearFade = /*#__PURE__*/memo(function LinearFade({
199
+ color,
200
+ from = 0,
201
+ to = 1,
202
+ bands = 14,
203
+ direction = "down",
204
+ style
205
+ }) {
206
+ const vertical = direction === "down" || direction === "up";
207
+ const reversed = direction === "up" || direction === "left";
208
+ const pct = 100 / bands;
209
+ const strips = [];
210
+ for (let i = 0; i < bands; i++) {
211
+ const t = (i + 0.5) / bands;
212
+ const a = from + (to - from) * (reversed ? 1 - t : t);
213
+ strips.push(/*#__PURE__*/_jsx(View, {
214
+ style: vertical ? {
215
+ position: "absolute",
216
+ left: 0,
217
+ right: 0,
218
+ top: `${i * pct}%`,
219
+ height: `${pct + 1.2}%`,
220
+ backgroundColor: withAlpha(color, a)
221
+ } : {
222
+ position: "absolute",
223
+ top: 0,
224
+ bottom: 0,
225
+ left: `${i * pct}%`,
226
+ width: `${pct + 1.2}%`,
227
+ backgroundColor: withAlpha(color, a)
228
+ }
229
+ }, i));
230
+ }
231
+ return /*#__PURE__*/_jsx(View, {
232
+ style: [absoluteFill, style],
233
+ pointerEvents: "none",
234
+ children: strips
235
+ });
236
+ });
237
+
238
+ /**
239
+ * Edge darkening. Four `LinearFade`s rather than a real radial vignette —
240
+ * cheaper, and at these alphas the corners read as round anyway.
241
+ */
242
+ export const Vignette = /*#__PURE__*/memo(function Vignette({
243
+ color = "#000000",
244
+ alpha = 0.55,
245
+ spread = 0.34
246
+ }) {
247
+ // Each fade fills its own positioned band, rather than trying to unset
248
+ // `absoluteFill`'s edges through the style array.
249
+ const band = `${spread * 100}%`;
250
+ return /*#__PURE__*/_jsxs(View, {
251
+ style: absoluteFill,
252
+ pointerEvents: "none",
253
+ children: [/*#__PURE__*/_jsx(View, {
254
+ style: {
255
+ position: "absolute",
256
+ left: 0,
257
+ right: 0,
258
+ top: 0,
259
+ height: band
260
+ },
261
+ children: /*#__PURE__*/_jsx(LinearFade, {
262
+ color: color,
263
+ from: alpha,
264
+ to: 0,
265
+ direction: "down",
266
+ bands: 10
267
+ })
268
+ }), /*#__PURE__*/_jsx(View, {
269
+ style: {
270
+ position: "absolute",
271
+ left: 0,
272
+ right: 0,
273
+ bottom: 0,
274
+ height: band
275
+ },
276
+ children: /*#__PURE__*/_jsx(LinearFade, {
277
+ color: color,
278
+ from: 0,
279
+ to: alpha,
280
+ direction: "down",
281
+ bands: 10
282
+ })
283
+ }), /*#__PURE__*/_jsx(View, {
284
+ style: {
285
+ position: "absolute",
286
+ top: 0,
287
+ bottom: 0,
288
+ left: 0,
289
+ width: band
290
+ },
291
+ children: /*#__PURE__*/_jsx(LinearFade, {
292
+ color: color,
293
+ from: alpha,
294
+ to: 0,
295
+ direction: "right",
296
+ bands: 8
297
+ })
298
+ }), /*#__PURE__*/_jsx(View, {
299
+ style: {
300
+ position: "absolute",
301
+ top: 0,
302
+ bottom: 0,
303
+ right: 0,
304
+ width: band
305
+ },
306
+ children: /*#__PURE__*/_jsx(LinearFade, {
307
+ color: color,
308
+ from: 0,
309
+ to: alpha,
310
+ direction: "right",
311
+ bands: 8
312
+ })
313
+ })]
314
+ });
315
+ });
316
+
317
+ /** Flat wash used as the base coat under every preset. */
318
+ export const Base = /*#__PURE__*/memo(function Base({
319
+ color
320
+ }) {
321
+ return /*#__PURE__*/_jsx(View, {
322
+ style: [absoluteFill, {
323
+ backgroundColor: color
324
+ }],
325
+ pointerEvents: "none"
326
+ });
327
+ });
328
+ export const hairline = StyleSheet.hairlineWidth;
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * The catalogue. Adding a preset means adding one entry here — the switcher,
5
+ * the persisted selection and every tool surface read from this list.
6
+ *
7
+ * `nodes` and `views` are not decoration: they are the number of native
8
+ * animated nodes the preset runs and the rough count of static views it mounts,
9
+ * so the switcher can show what each look costs while you compare them. Keep
10
+ * them honest.
11
+ */
12
+
13
+ import { Crosshatch, Halftone, HexMesh, Instrument, IsoGrid, PerfBoard, Radar, Waffle } from "./variants/lattice.js";
14
+ import { DeepField } from "./variants/deepField.js";
15
+ import { Starfield } from "./variants/starfield.js";
16
+ export const BACKGROUND_PRESETS = [{
17
+ id: "off",
18
+ label: "Off",
19
+ blurb: "Plain black — the baseline to compare against",
20
+ nodes: 0,
21
+ views: 1,
22
+ Component: null
23
+ }, {
24
+ id: "starfield",
25
+ label: "Starfield",
26
+ blurb: "Five depth bands drifting at parallax speeds",
27
+ nodes: 6,
28
+ views: 222,
29
+ Component: Starfield
30
+ }, {
31
+ id: "deepfield",
32
+ label: "Deep Field",
33
+ blurb: "The Everlights field, 1:1 — 160 stars, richer but pricier",
34
+ nodes: 22,
35
+ views: 462,
36
+ Component: DeepField
37
+ }, {
38
+ id: "hexmesh",
39
+ label: "Hex Mesh",
40
+ blurb: "Diamond lattice with a slow breath",
41
+ nodes: 1,
42
+ views: 160,
43
+ Component: HexMesh
44
+ }, {
45
+ id: "isogrid",
46
+ label: "Iso Grid",
47
+ blurb: "Three-axis isometric ruling",
48
+ nodes: 1,
49
+ views: 66,
50
+ Component: IsoGrid
51
+ }, {
52
+ id: "crosshatch",
53
+ label: "Crosshatch",
54
+ blurb: "Fine 45° weave, drafting-paper quiet",
55
+ nodes: 1,
56
+ views: 64,
57
+ Component: Crosshatch
58
+ }, {
59
+ id: "perfboard",
60
+ label: "Perf Board",
61
+ blurb: "Solder-pad matrix with datum pads every fourth",
62
+ nodes: 1,
63
+ views: 150,
64
+ Component: PerfBoard
65
+ }, {
66
+ id: "halftone",
67
+ label: "Halftone",
68
+ blurb: "Dot grid swelling toward the light",
69
+ nodes: 1,
70
+ views: 176,
71
+ Component: Halftone
72
+ }, {
73
+ id: "waffle",
74
+ label: "Waffle",
75
+ blurb: "Machined panel of rounded cells",
76
+ nodes: 1,
77
+ views: 88,
78
+ Component: Waffle
79
+ }, {
80
+ id: "instrument",
81
+ label: "Instrument",
82
+ blurb: "Ruled scales with major graduations",
83
+ nodes: 1,
84
+ views: 110,
85
+ Component: Instrument
86
+ }, {
87
+ id: "radar",
88
+ label: "Radar",
89
+ blurb: "Rings and spokes under a rotating sweep",
90
+ nodes: 2,
91
+ views: 40,
92
+ Component: Radar
93
+ }];
94
+ export const DEFAULT_BACKGROUND = "hexmesh";
95
+ export function presetIndex(id) {
96
+ const i = BACKGROUND_PRESETS.findIndex(p => p.id === id);
97
+ return i < 0 ? 0 : i;
98
+ }
99
+ export function presetAt(index) {
100
+ const n = BACKGROUND_PRESETS.length;
101
+ return BACKGROUND_PRESETS[(index % n + n) % n];
102
+ }
103
+ export function getPreset(id) {
104
+ return BACKGROUND_PRESETS[presetIndex(id)];
105
+ }
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Deterministic scatter tables for the tool backgrounds.
5
+ *
6
+ * Every preset needs a pile of "random" positions, sizes and phases. Those are
7
+ * generated ONCE at module load from a fixed seed and frozen, so:
8
+ * - the same seed always paints the same field (screenshots are stable, and
9
+ * two tools showing the same background look identical),
10
+ * - no allocation happens on mount or on any re-render — the presets only
11
+ * read these arrays.
12
+ *
13
+ * Ported from the seeded field in the Everlights starfield (mulberry32).
14
+ */
15
+
16
+ export function mulberry32(seed) {
17
+ return () => {
18
+ seed = seed + 0x6d2b79f5 | 0;
19
+ let t = Math.imul(seed ^ seed >>> 15, 1 | seed);
20
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
21
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
22
+ };
23
+ }
24
+
25
+ /** `count` numbers in [0,1) from `seed`. */
26
+ export function scatter(seed, count) {
27
+ const rnd = mulberry32(seed >>> 0);
28
+ const out = new Array(count);
29
+ for (let i = 0; i < count; i++) out[i] = rnd();
30
+ return out;
31
+ }
32
+
33
+ /** `#RRGGBB` (or `#RGB`) + alpha 0..1 → `rgba(...)`. */
34
+ export function withAlpha(hex, alpha) {
35
+ let h = hex.replace("#", "");
36
+ if (h.length === 3) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
37
+ const n = parseInt(h, 16);
38
+ const r = n >> 16 & 255;
39
+ const g = n >> 8 & 255;
40
+ const b = n & 255;
41
+ const a = alpha < 0 ? 0 : alpha > 1 ? 1 : alpha;
42
+ return `rgba(${r},${g},${b},${a.toFixed(3)})`;
43
+ }
44
+
45
+ /**
46
+ * `hex` at `alpha` composited over an opaque `base`, returned as an OPAQUE
47
+ * colour.
48
+ *
49
+ * Over a known backdrop this is pixel-identical to drawing the translucent
50
+ * colour — the GPU just doesn't have to blend it. Only valid where nothing
51
+ * else sits between the layer and `base`.
52
+ */
53
+ export function flattenOver(hex, alpha, base) {
54
+ const parse = h => {
55
+ let v = h.replace("#", "");
56
+ if (v.length === 3) v = v[0] + v[0] + v[1] + v[1] + v[2] + v[2];
57
+ const n = parseInt(v, 16);
58
+ return [n >> 16 & 255, n >> 8 & 255, n & 255];
59
+ };
60
+ const [r, g, b] = parse(hex);
61
+ const [br, bg, bb] = parse(base);
62
+ const a = alpha < 0 ? 0 : alpha > 1 ? 1 : alpha;
63
+ const mix = (c, bc) => Math.round(c * a + bc * (1 - a));
64
+ return `rgb(${mix(r, br)},${mix(g, bg)},${mix(b, bb)})`;
65
+ }
@@ -0,0 +1 @@
1
+ "use strict";