panelui-native 0.13.0 → 0.16.0
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/README.md +41 -23
- package/lib/module/components/button/index.js +12 -9
- package/lib/module/components/button/index.js.map +1 -1
- package/lib/module/components/chip/index.js +313 -0
- package/lib/module/components/chip/index.js.map +1 -0
- package/lib/module/components/direction/index.js +86 -0
- package/lib/module/components/direction/index.js.map +1 -0
- package/lib/module/components/frame/index.js +41 -39
- package/lib/module/components/frame/index.js.map +1 -1
- package/lib/module/components/heatmap-chart/index.js +772 -0
- package/lib/module/components/heatmap-chart/index.js.map +1 -0
- package/lib/module/components/input/index.js +4 -0
- package/lib/module/components/input/index.js.map +1 -1
- package/lib/module/components/message/index.js +16 -1
- package/lib/module/components/message/index.js.map +1 -1
- package/lib/module/components/scroll-canvas/index.js +175 -0
- package/lib/module/components/scroll-canvas/index.js.map +1 -0
- package/lib/module/components/scroll-text/index.js +213 -0
- package/lib/module/components/scroll-text/index.js.map +1 -0
- package/lib/module/components/select/index.js +20 -24
- package/lib/module/components/select/index.js.map +1 -1
- package/lib/module/components/slider/index.js +2 -5
- package/lib/module/components/slider/index.js.map +1 -1
- package/lib/module/components/soundwave/index.js +813 -0
- package/lib/module/components/soundwave/index.js.map +1 -0
- package/lib/module/components/switch/index.js +15 -17
- package/lib/module/components/switch/index.js.map +1 -1
- package/lib/module/components/thinking-orb/index.js +741 -0
- package/lib/module/components/thinking-orb/index.js.map +1 -0
- package/lib/module/components/toggle-button/index.js +12 -3
- package/lib/module/components/toggle-button/index.js.map +1 -1
- package/lib/module/components/typography/index.js +196 -5
- package/lib/module/components/typography/index.js.map +1 -1
- package/lib/module/hooks/index.js +1 -0
- package/lib/module/hooks/index.js.map +1 -1
- package/lib/module/hooks/use-keyboard-avoidance.js +99 -59
- package/lib/module/hooks/use-keyboard-avoidance.js.map +1 -1
- package/lib/module/hooks/use-reveal-progress.js +75 -0
- package/lib/module/hooks/use-reveal-progress.js.map +1 -0
- package/lib/module/icons/index.js +69 -0
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js +9 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/native/index.js.map +1 -1
- package/lib/module/primitives/keyboard-avoider.js +21 -2
- package/lib/module/primitives/keyboard-avoider.js.map +1 -1
- package/lib/module/primitives/scroll-progress.js +114 -0
- package/lib/module/primitives/scroll-progress.js.map +1 -0
- package/lib/typescript/src/components/button/index.d.ts +3 -4
- package/lib/typescript/src/components/button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/chip/index.d.ts +304 -0
- package/lib/typescript/src/components/chip/index.d.ts.map +1 -0
- package/lib/typescript/src/components/direction/index.d.ts +64 -0
- package/lib/typescript/src/components/direction/index.d.ts.map +1 -0
- package/lib/typescript/src/components/frame/index.d.ts +15 -13
- package/lib/typescript/src/components/frame/index.d.ts.map +1 -1
- package/lib/typescript/src/components/heatmap-chart/index.d.ts +260 -0
- package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/components/input/index.d.ts +19 -4
- package/lib/typescript/src/components/input/index.d.ts.map +1 -1
- package/lib/typescript/src/components/message/index.d.ts.map +1 -1
- package/lib/typescript/src/components/scroll-canvas/index.d.ts +66 -0
- package/lib/typescript/src/components/scroll-canvas/index.d.ts.map +1 -0
- package/lib/typescript/src/components/scroll-text/index.d.ts +46 -0
- package/lib/typescript/src/components/scroll-text/index.d.ts.map +1 -0
- package/lib/typescript/src/components/select/index.d.ts.map +1 -1
- package/lib/typescript/src/components/slider/index.d.ts.map +1 -1
- package/lib/typescript/src/components/soundwave/index.d.ts +91 -0
- package/lib/typescript/src/components/soundwave/index.d.ts.map +1 -0
- package/lib/typescript/src/components/switch/index.d.ts.map +1 -1
- package/lib/typescript/src/components/thinking-orb/index.d.ts +28 -0
- package/lib/typescript/src/components/thinking-orb/index.d.ts.map +1 -0
- package/lib/typescript/src/components/toggle-button/index.d.ts +12 -0
- package/lib/typescript/src/components/toggle-button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/typography/index.d.ts +154 -1
- package/lib/typescript/src/components/typography/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/index.d.ts +2 -1
- package/lib/typescript/src/hooks/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/use-keyboard-avoidance.d.ts +16 -3
- package/lib/typescript/src/hooks/use-keyboard-avoidance.d.ts.map +1 -1
- package/lib/typescript/src/hooks/use-reveal-progress.d.ts +56 -0
- package/lib/typescript/src/hooks/use-reveal-progress.d.ts.map +1 -0
- package/lib/typescript/src/icons/index.d.ts +6 -0
- package/lib/typescript/src/icons/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +9 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native/index.d.ts +15 -7
- package/lib/typescript/src/native/index.d.ts.map +1 -1
- package/lib/typescript/src/primitives/keyboard-avoider.d.ts +30 -3
- package/lib/typescript/src/primitives/keyboard-avoider.d.ts.map +1 -1
- package/lib/typescript/src/primitives/scroll-progress.d.ts +61 -0
- package/lib/typescript/src/primitives/scroll-progress.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/button/index.tsx +13 -11
- package/src/components/direction/index.tsx +90 -0
- package/src/components/frame/index.tsx +39 -50
- package/src/components/heatmap-chart/index.tsx +1024 -0
- package/src/components/input/index.tsx +23 -4
- package/src/components/message/index.tsx +17 -1
- package/src/components/scroll-canvas/index.tsx +220 -0
- package/src/components/scroll-text/index.tsx +277 -0
- package/src/components/select/index.tsx +4 -9
- package/src/components/slider/index.tsx +4 -7
- package/src/components/soundwave/index.tsx +1029 -0
- package/src/components/switch/index.tsx +5 -8
- package/src/components/thinking-orb/index.tsx +792 -0
- package/src/components/typography/index.tsx +231 -10
- package/src/hooks/index.ts +6 -0
- package/src/hooks/use-keyboard-avoidance.ts +111 -65
- package/src/hooks/use-reveal-progress.ts +106 -0
- package/src/icons/index.tsx +36 -0
- package/src/index.ts +53 -0
- package/src/native/index.ts +15 -7
- package/src/primitives/keyboard-avoider.tsx +41 -4
- package/src/primitives/scroll-progress.tsx +167 -0
|
@@ -0,0 +1,741 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ThinkingOrb — a dotted orb that says what an agent is doing.
|
|
5
|
+
*
|
|
6
|
+
* A spinner says "busy". These say *which kind of busy*: particles running
|
|
7
|
+
* tilted orbits for work in flight, a scan meridian sweeping a globe for a
|
|
8
|
+
* search, bands that scramble and click back for a solve. Six states, each a
|
|
9
|
+
* distinct silhouette in motion, so a glance at the orb is enough — which is
|
|
10
|
+
* the whole point of putting one next to a streaming reply.
|
|
11
|
+
*
|
|
12
|
+
* ```tsx
|
|
13
|
+
* <ThinkingOrb state="searching" />
|
|
14
|
+
* <ThinkingOrb state="working" size={20} />
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* ## How it is drawn
|
|
18
|
+
*
|
|
19
|
+
* The geometry is honestly three-dimensional — points on a sphere, rotated and
|
|
20
|
+
* tilted, projected orthographically, with depth carried by dot size and ink
|
|
21
|
+
* weight. React Native has no 2D canvas to paint that into, and one animated
|
|
22
|
+
* SVG node per dot would be two hundred native prop writes a frame, which no
|
|
23
|
+
* amount of tuning survives.
|
|
24
|
+
*
|
|
25
|
+
* So the dots are quantised into eight ink buckets and each bucket is emitted
|
|
26
|
+
* as a *single* path of circle arcs. Eight animated props a frame, whatever the
|
|
27
|
+
* dot count, and depth ordering falls out of bucket order for free — depth is
|
|
28
|
+
* what drives the ink in the first place, so painting faint to strong paints
|
|
29
|
+
* far to near. Everything from the trigonometry to the path strings runs in one
|
|
30
|
+
* worklet on the UI thread; React renders once and then never again.
|
|
31
|
+
*
|
|
32
|
+
* Strictly monochrome, from `--color-foreground`, so the orb inverts with the
|
|
33
|
+
* theme and needs no palette of its own.
|
|
34
|
+
*/
|
|
35
|
+
import { useEffect } from 'react';
|
|
36
|
+
import { View } from 'react-native';
|
|
37
|
+
import Animated, { runOnUI, useAnimatedProps, useFrameCallback, useReducedMotion, useSharedValue } from 'react-native-reanimated';
|
|
38
|
+
import Svg, { Path } from 'react-native-svg';
|
|
39
|
+
import { useCSSVariable } from 'uniwind';
|
|
40
|
+
import { cn } from "../../utils/cn.js";
|
|
41
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
42
|
+
const AnimatedPath = Animated.createAnimatedComponent(Path);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* How many ink levels the dots are rounded to.
|
|
46
|
+
*
|
|
47
|
+
* This is the dial. Every bucket is one animated prop and one path per frame,
|
|
48
|
+
* so fewer is cheaper and more is smoother; below about six the depth falloff
|
|
49
|
+
* starts to band visibly, and above about twelve there is nothing left to see.
|
|
50
|
+
*/
|
|
51
|
+
const BUCKETS = 8;
|
|
52
|
+
|
|
53
|
+
/** Ink below this is invisible against any background, so it is not drawn. */
|
|
54
|
+
const MIN_INK = 0.03;
|
|
55
|
+
|
|
56
|
+
/** Dots never shrink past this, or a far dot becomes a gap in the lattice. */
|
|
57
|
+
const MIN_RADIUS = 0.3;
|
|
58
|
+
|
|
59
|
+
/** The frame a still orb shows. Far enough in that no state is at its start. */
|
|
60
|
+
const STILL_FRAME = 2.4;
|
|
61
|
+
/** What each state is doing, for anyone who cannot see it. */
|
|
62
|
+
const STATE_LABEL = {
|
|
63
|
+
working: 'Working',
|
|
64
|
+
searching: 'Searching',
|
|
65
|
+
solving: 'Solving',
|
|
66
|
+
listening: 'Listening',
|
|
67
|
+
composing: 'Composing',
|
|
68
|
+
shaping: 'Shaping'
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Density and speed per state, at the two scales.
|
|
73
|
+
*
|
|
74
|
+
* `sm` is not `md` scaled down. Below about thirty pixels a faithful lattice
|
|
75
|
+
* turns to grey mush, so the small orb is a separate design: far fewer dots,
|
|
76
|
+
* each proportionally much larger, running faster so the motion still reads at
|
|
77
|
+
* a size where the individual dots barely do.
|
|
78
|
+
*
|
|
79
|
+
* The counts are lower than a canvas implementation would use, because every
|
|
80
|
+
* dot here is arc commands in a path string built each frame — the cost is in
|
|
81
|
+
* the string, not in the fill.
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
const PROFILES = {
|
|
85
|
+
working: {
|
|
86
|
+
md: {
|
|
87
|
+
speed: 1.9,
|
|
88
|
+
rows: 9,
|
|
89
|
+
density: 26,
|
|
90
|
+
radius: 1
|
|
91
|
+
},
|
|
92
|
+
sm: {
|
|
93
|
+
speed: 3.9,
|
|
94
|
+
rows: 4,
|
|
95
|
+
density: 12,
|
|
96
|
+
radius: 2.4
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
searching: {
|
|
100
|
+
md: {
|
|
101
|
+
speed: 2.0,
|
|
102
|
+
rows: 11,
|
|
103
|
+
density: 28,
|
|
104
|
+
radius: 1.15
|
|
105
|
+
},
|
|
106
|
+
sm: {
|
|
107
|
+
speed: 2.7,
|
|
108
|
+
rows: 5,
|
|
109
|
+
density: 12,
|
|
110
|
+
radius: 1.75
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
solving: {
|
|
114
|
+
md: {
|
|
115
|
+
speed: 1.8,
|
|
116
|
+
rows: 10,
|
|
117
|
+
density: 24,
|
|
118
|
+
radius: 1.05
|
|
119
|
+
},
|
|
120
|
+
sm: {
|
|
121
|
+
speed: 2.0,
|
|
122
|
+
rows: 5,
|
|
123
|
+
density: 11,
|
|
124
|
+
radius: 1.9
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
listening: {
|
|
128
|
+
md: {
|
|
129
|
+
speed: 4.4,
|
|
130
|
+
rows: 10,
|
|
131
|
+
density: 24,
|
|
132
|
+
radius: 1
|
|
133
|
+
},
|
|
134
|
+
sm: {
|
|
135
|
+
speed: 4.0,
|
|
136
|
+
rows: 5,
|
|
137
|
+
density: 11,
|
|
138
|
+
radius: 1.6
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
composing: {
|
|
142
|
+
md: {
|
|
143
|
+
speed: 2.3,
|
|
144
|
+
rows: 5,
|
|
145
|
+
density: 44,
|
|
146
|
+
radius: 0.85
|
|
147
|
+
},
|
|
148
|
+
sm: {
|
|
149
|
+
speed: 3.1,
|
|
150
|
+
rows: 3,
|
|
151
|
+
density: 20,
|
|
152
|
+
radius: 1.1
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
shaping: {
|
|
156
|
+
md: {
|
|
157
|
+
speed: 2.4,
|
|
158
|
+
rows: 1,
|
|
159
|
+
density: 26,
|
|
160
|
+
radius: 1
|
|
161
|
+
},
|
|
162
|
+
sm: {
|
|
163
|
+
speed: 2.1,
|
|
164
|
+
rows: 1,
|
|
165
|
+
density: 14,
|
|
166
|
+
radius: 2.2
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/* -------------------------------------------------------------------------- */
|
|
172
|
+
/* Worklet maths */
|
|
173
|
+
/* -------------------------------------------------------------------------- */
|
|
174
|
+
|
|
175
|
+
/** Deterministic hash in `[0, 1)`. Stable across frames and across mounts. */
|
|
176
|
+
function hashD(a, b) {
|
|
177
|
+
'worklet';
|
|
178
|
+
|
|
179
|
+
const h = Math.sin(a * 12.9898 + b * 78.233) * 43758.5453;
|
|
180
|
+
return h - Math.floor(h);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Shortest signed angular distance, wrapped to `(-π, π]`. */
|
|
184
|
+
function angleDelta(a, b) {
|
|
185
|
+
'worklet';
|
|
186
|
+
|
|
187
|
+
return Math.atan2(Math.sin(a - b), Math.cos(a - b));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** One decimal place. Path strings are rebuilt every frame; every digit costs. */
|
|
191
|
+
function q(value) {
|
|
192
|
+
'worklet';
|
|
193
|
+
|
|
194
|
+
return Math.round(value * 10) / 10;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Dot radii were tuned against a 300pt frame; scaling them sub-linearly is what
|
|
199
|
+
* keeps a 20pt orb from becoming a smudge and a 96pt one from becoming beads.
|
|
200
|
+
*/
|
|
201
|
+
function radiusScale(size) {
|
|
202
|
+
'worklet';
|
|
203
|
+
|
|
204
|
+
return (size / 300) ** 0.6;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* A dot, straight into its ink bucket as a circle of arcs.
|
|
209
|
+
*
|
|
210
|
+
* Two half-arcs rather than a `<Circle>`, because a bucket has to be one node:
|
|
211
|
+
* the entire point of bucketing is that the frame costs eight prop writes and
|
|
212
|
+
* not two hundred.
|
|
213
|
+
*/
|
|
214
|
+
function dot(out, x, y, r, ink) {
|
|
215
|
+
'worklet';
|
|
216
|
+
|
|
217
|
+
if (ink < MIN_INK) return;
|
|
218
|
+
const bucket = Math.min(BUCKETS - 1, Math.max(0, Math.floor(ink * BUCKETS)));
|
|
219
|
+
const rr = q(Math.max(MIN_RADIUS, r));
|
|
220
|
+
const d = q(rr * 2);
|
|
221
|
+
out[bucket] += `M${q(x - rr)} ${q(y)}a${rr} ${rr} 0 1 0 ${d} 0a${rr} ${rr} 0 1 0 ${-d} 0`;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Spin, tilt and orthographic projection, written into a scratch triple.
|
|
226
|
+
*
|
|
227
|
+
* It writes into an array the caller owns rather than returning one, because it
|
|
228
|
+
* is called once per dot per frame — a fresh triple each time is a few hundred
|
|
229
|
+
* allocations a frame for a value read immediately and thrown away.
|
|
230
|
+
*/
|
|
231
|
+
function project(out, x, y, z, sy, cy, st, ct, ox, oy, scale) {
|
|
232
|
+
'worklet';
|
|
233
|
+
|
|
234
|
+
const x1 = x * cy + z * sy;
|
|
235
|
+
const z1 = -x * sy + z * cy;
|
|
236
|
+
const y1 = y * ct - z1 * st;
|
|
237
|
+
out[0] = ox + x1 * scale;
|
|
238
|
+
out[1] = oy - y1 * scale;
|
|
239
|
+
out[2] = y * st + z1 * ct;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/* -------------------------------------------------------------------------- */
|
|
243
|
+
/* The six states */
|
|
244
|
+
/* -------------------------------------------------------------------------- */
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* `working` — particles running tilted orbits, each on a faint ghost path.
|
|
248
|
+
*
|
|
249
|
+
* The ghosts are what make it read as orbits rather than as loose specks: three
|
|
250
|
+
* bright particles alone look random, and the same three on a visible track
|
|
251
|
+
* look like work being done.
|
|
252
|
+
*/
|
|
253
|
+
function drawWorking(out, size, t, p) {
|
|
254
|
+
'worklet';
|
|
255
|
+
|
|
256
|
+
const c = size / 2;
|
|
257
|
+
const R = c * 0.82;
|
|
258
|
+
const rs = radiusScale(size) * p.radius;
|
|
259
|
+
const sy = Math.sin(t * 0.12);
|
|
260
|
+
const cy = Math.cos(t * 0.12);
|
|
261
|
+
const st = Math.sin(0.3);
|
|
262
|
+
const ct = Math.cos(0.3);
|
|
263
|
+
const v = [0, 0, 0];
|
|
264
|
+
for (let orbit = 0; orbit < p.rows; orbit++) {
|
|
265
|
+
const h1 = hashD(orbit, 1.7);
|
|
266
|
+
const h2 = hashD(orbit, 5.2);
|
|
267
|
+
const h3 = hashD(orbit, 8.9);
|
|
268
|
+
const ro = R * (0.45 + 0.52 * h1);
|
|
269
|
+
const theta = h1 * 2 * Math.PI;
|
|
270
|
+
const phi = Math.acos(2 * h2 - 1);
|
|
271
|
+
|
|
272
|
+
// An orthonormal basis for the orbit's plane, from its normal.
|
|
273
|
+
const nx = Math.sin(phi) * Math.cos(theta);
|
|
274
|
+
const ny = Math.cos(phi);
|
|
275
|
+
const nz = Math.sin(phi) * Math.sin(theta);
|
|
276
|
+
const ul = Math.max(1e-6, Math.hypot(ny, nx));
|
|
277
|
+
const ux = -ny / ul;
|
|
278
|
+
const uy = nx / ul;
|
|
279
|
+
const vx = ny * 0 - nz * uy;
|
|
280
|
+
const vy = nz * ux - nx * 0;
|
|
281
|
+
const vz = nx * uy - ny * ux;
|
|
282
|
+
const speed = (0.25 + 0.55 * h3) * (h3 > 0.5 ? 1 : -1);
|
|
283
|
+
for (let k = 0; k < p.density; k++) {
|
|
284
|
+
const a = k / p.density * 2 * Math.PI;
|
|
285
|
+
const ca = Math.cos(a);
|
|
286
|
+
const sa = Math.sin(a);
|
|
287
|
+
project(v, (ux * ca + vx * sa) * ro, (uy * ca + vy * sa) * ro, vz * sa * ro, sy, cy, st, ct, c, c, 1);
|
|
288
|
+
const depth = (v[2] / ro + 1) / 2;
|
|
289
|
+
dot(out, v[0], v[1], 0.9 * rs, 0.28 * (0.4 + 0.6 * depth));
|
|
290
|
+
}
|
|
291
|
+
for (let m = 0; m < 3; m++) {
|
|
292
|
+
const a = t * speed + m / 3 * 2 * Math.PI + h2 * 6;
|
|
293
|
+
const ca = Math.cos(a);
|
|
294
|
+
const sa = Math.sin(a);
|
|
295
|
+
project(v, (ux * ca + vx * sa) * ro, (uy * ca + vy * sa) * ro, vz * sa * ro, sy, cy, st, ct, c, c, 1);
|
|
296
|
+
const depth = (v[2] / ro + 1) / 2;
|
|
297
|
+
dot(out, v[0], v[1], (1.2 + 1.6 * depth) * rs, 0.7 + 0.22 * depth);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* `searching` — a scan meridian sweeping a dotted globe.
|
|
304
|
+
*
|
|
305
|
+
* The scan is a ripple in dot *size*, not a highlight. A brightness sweep on a
|
|
306
|
+
* monochrome orb reads as a lighting effect; a size ripple reads as something
|
|
307
|
+
* passing over the surface, which is what a search is.
|
|
308
|
+
*/
|
|
309
|
+
function drawSearching(out, size, t, p) {
|
|
310
|
+
'worklet';
|
|
311
|
+
|
|
312
|
+
const c = size / 2;
|
|
313
|
+
const R = c * 0.82;
|
|
314
|
+
const rs = radiusScale(size) * p.radius;
|
|
315
|
+
const spin = 0.5;
|
|
316
|
+
const tilt = 0.4 + 0.06 * Math.sin(t * 0.35);
|
|
317
|
+
const sy = Math.sin(t * spin);
|
|
318
|
+
const cySpin = Math.cos(t * spin);
|
|
319
|
+
const st = Math.sin(tilt);
|
|
320
|
+
const ct = Math.cos(tilt);
|
|
321
|
+
const scan = t * (spin + (1.7 - spin) * 4.1);
|
|
322
|
+
const v = [0, 0, 0];
|
|
323
|
+
for (let li = 0; li <= p.rows; li++) {
|
|
324
|
+
const lat = -Math.PI / 2 + li / p.rows * Math.PI;
|
|
325
|
+
const cosLat = Math.cos(lat);
|
|
326
|
+
const sinLat = Math.sin(lat);
|
|
327
|
+
const count = Math.max(1, Math.round(Math.abs(cosLat) * p.density));
|
|
328
|
+
for (let lj = 0; lj < count; lj++) {
|
|
329
|
+
const lon = lj / count * 2 * Math.PI;
|
|
330
|
+
project(v, cosLat * Math.cos(lon), sinLat, cosLat * Math.sin(lon), sy, cySpin, st, ct, c, c, R);
|
|
331
|
+
const depth = (v[2] + 1) / 2;
|
|
332
|
+
const delta = angleDelta(lon + t * spin, scan);
|
|
333
|
+
const boost = Math.exp(-(delta * delta) / 0.18) * Math.max(0, v[2]);
|
|
334
|
+
const ink = 0.38 + 0.54 * depth;
|
|
335
|
+
dot(out, v[0], v[1], (0.6 + 1.7 * depth + boost) * rs, ink * (0.45 + 0.55 * Math.min(1, boost)));
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* `solving` — bands twist in quarter turns, then replay in reverse and click
|
|
342
|
+
* back to solved.
|
|
343
|
+
*
|
|
344
|
+
* The palindrome is the whole trick. A scramble that never resolves reads as
|
|
345
|
+
* confusion; one that comes apart and goes back together reads as an answer
|
|
346
|
+
* being found, and it loops seamlessly because it ends where it began.
|
|
347
|
+
*/
|
|
348
|
+
function drawSolving(out, size, t, p) {
|
|
349
|
+
'worklet';
|
|
350
|
+
|
|
351
|
+
const c = size / 2;
|
|
352
|
+
const R = c * 0.82;
|
|
353
|
+
const rs = radiusScale(size) * p.radius;
|
|
354
|
+
const yaw = t * 0.55;
|
|
355
|
+
const tilt = 0.35 + 0.1 * Math.sin(t * 0.9);
|
|
356
|
+
const sy = Math.sin(yaw);
|
|
357
|
+
const cySpin = Math.cos(yaw);
|
|
358
|
+
const st = Math.sin(tilt);
|
|
359
|
+
const ct = Math.cos(tilt);
|
|
360
|
+
const v = [0, 0, 0];
|
|
361
|
+
const moves = 10;
|
|
362
|
+
const slot = 0.42;
|
|
363
|
+
const rest = 1.2;
|
|
364
|
+
const cycle = 2 * moves * slot + rest;
|
|
365
|
+
const tc = t % cycle;
|
|
366
|
+
|
|
367
|
+
// How far through each move we are: all the way for the ones already made,
|
|
368
|
+
// eased for the one under way, nothing for the ones still to come.
|
|
369
|
+
const amount = [];
|
|
370
|
+
let active = -1;
|
|
371
|
+
for (let i = 0; i < moves; i++) amount.push(0);
|
|
372
|
+
if (tc < 2 * moves * slot) {
|
|
373
|
+
const index = Math.floor(tc / slot);
|
|
374
|
+
const local = Math.min(1, (tc - index * slot) / slot / 0.7);
|
|
375
|
+
const eased = 1 - (1 - local) ** 3;
|
|
376
|
+
if (index < moves) {
|
|
377
|
+
for (let i = 0; i < index; i++) amount[i] = 1;
|
|
378
|
+
amount[index] = eased;
|
|
379
|
+
active = index;
|
|
380
|
+
} else {
|
|
381
|
+
const undo = 2 * moves - 1 - index;
|
|
382
|
+
for (let i = 0; i < undo; i++) amount[i] = 1;
|
|
383
|
+
amount[undo] = 1 - eased;
|
|
384
|
+
active = undo;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
for (let li = 0; li <= p.rows; li++) {
|
|
388
|
+
const lat = -Math.PI / 2 + li / p.rows * Math.PI;
|
|
389
|
+
const cosLat = Math.cos(lat);
|
|
390
|
+
const sinLat = Math.sin(lat);
|
|
391
|
+
const count = Math.max(1, Math.round(Math.abs(cosLat) * p.density));
|
|
392
|
+
for (let lj = 0; lj < count; lj++) {
|
|
393
|
+
const lon = lj / count * 2 * Math.PI;
|
|
394
|
+
let x = cosLat * Math.cos(lon);
|
|
395
|
+
let y = sinLat;
|
|
396
|
+
let z = cosLat * Math.sin(lon);
|
|
397
|
+
let inActive = false;
|
|
398
|
+
for (let i = 0; i < moves; i++) {
|
|
399
|
+
if (amount[i] <= 0) continue;
|
|
400
|
+
const axis = Math.min(2, Math.floor(hashD(i, 2.3) * 3));
|
|
401
|
+
const lo = -1 + 0.5 * Math.min(3, Math.floor(hashD(i, 5.9) * 4));
|
|
402
|
+
const coord = axis === 0 ? x : axis === 1 ? y : z;
|
|
403
|
+
if (coord < lo || coord >= lo + 0.5) continue;
|
|
404
|
+
if (i === active) inActive = true;
|
|
405
|
+
const dir = hashD(i, 7.7) < 0.5 ? 1 : -1;
|
|
406
|
+
const a = dir * Math.PI / 2 * amount[i];
|
|
407
|
+
const ca = Math.cos(a);
|
|
408
|
+
const sa = Math.sin(a);
|
|
409
|
+
if (axis === 0) {
|
|
410
|
+
const y2 = y * ca - z * sa;
|
|
411
|
+
z = y * sa + z * ca;
|
|
412
|
+
y = y2;
|
|
413
|
+
} else if (axis === 1) {
|
|
414
|
+
const x2 = x * ca + z * sa;
|
|
415
|
+
z = -x * sa + z * ca;
|
|
416
|
+
x = x2;
|
|
417
|
+
} else {
|
|
418
|
+
const x2 = x * ca - y * sa;
|
|
419
|
+
y = x * sa + y * ca;
|
|
420
|
+
x = x2;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
project(v, x, y, z, sy, cySpin, st, ct, c, c, R);
|
|
424
|
+
const depth = (v[2] + 1) / 2;
|
|
425
|
+
// The band under the hand inks a touch stronger, so the turn is legible.
|
|
426
|
+
dot(out, v[0], v[1], (0.6 + 1.7 * depth + (inActive ? 0.3 : 0)) * rs, 0.38 + 0.54 * depth + (inActive ? 0.14 : 0));
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* `listening` — a waveform rolling through the latitude rings.
|
|
433
|
+
*
|
|
434
|
+
* Two waves at unrelated tempi, so the surface never quite repeats. One wave
|
|
435
|
+
* gives a pulse, which reads as a heartbeat rather than as listening.
|
|
436
|
+
*/
|
|
437
|
+
function drawListening(out, size, t, p) {
|
|
438
|
+
'worklet';
|
|
439
|
+
|
|
440
|
+
const c = size / 2;
|
|
441
|
+
const R = c * 0.874;
|
|
442
|
+
const rs = radiusScale(size) * p.radius;
|
|
443
|
+
const sy = Math.sin(t * 0.18);
|
|
444
|
+
const cySpin = Math.cos(t * 0.18);
|
|
445
|
+
const st = Math.sin(0.38);
|
|
446
|
+
const ct = Math.cos(0.38);
|
|
447
|
+
const v = [0, 0, 0];
|
|
448
|
+
for (let ri = 0; ri <= p.rows; ri++) {
|
|
449
|
+
const lat = -Math.PI / 2 + ri / p.rows * Math.PI;
|
|
450
|
+
const cosLat = Math.cos(lat);
|
|
451
|
+
const sinLat = Math.sin(lat);
|
|
452
|
+
const w = 0.62 * Math.sin(t * 2.1 - ri * 0.52) + 0.38 * Math.sin(t * 1.27 + ri * 0.83);
|
|
453
|
+
const rr = R * (0.88 + 0.105 * w);
|
|
454
|
+
const crest = Math.max(0, w);
|
|
455
|
+
const count = Math.max(1, Math.round(Math.abs(cosLat) * p.density));
|
|
456
|
+
for (let lj = 0; lj < count; lj++) {
|
|
457
|
+
const lon = lj / count * 2 * Math.PI;
|
|
458
|
+
project(v, cosLat * Math.cos(lon) * rr, sinLat * rr, cosLat * Math.sin(lon) * rr, sy, cySpin, st, ct, c, c, 1);
|
|
459
|
+
const depth = (v[2] / R + 1) / 2;
|
|
460
|
+
dot(out, v[0], v[1], (0.6 + 1.7 * depth) * (1 + 0.4 * crest) * rs, 0.34 + 0.56 * depth + 0.1 * crest);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* `composing` — an undulating sash of parallel strands on a great circle,
|
|
467
|
+
* inside a faint dotted shell.
|
|
468
|
+
*
|
|
469
|
+
* The band's own tumble is frozen. Left spinning it competes with the
|
|
470
|
+
* undulation and the two motions cancel into noise; held still, the wave
|
|
471
|
+
* travelling along it is the only thing moving, and it reads as a line of
|
|
472
|
+
* something being written.
|
|
473
|
+
*/
|
|
474
|
+
function drawComposing(out, size, t, p) {
|
|
475
|
+
'worklet';
|
|
476
|
+
|
|
477
|
+
const c = size / 2;
|
|
478
|
+
const R = c * 0.78;
|
|
479
|
+
const rs = radiusScale(size) * p.radius;
|
|
480
|
+
const st = Math.sin(0.3);
|
|
481
|
+
const ct = Math.cos(0.3);
|
|
482
|
+
const v = [0, 0, 0];
|
|
483
|
+
|
|
484
|
+
// The shell: a Fibonacci lattice, which is the only way to scatter points on
|
|
485
|
+
// a sphere evenly without them lining up into visible seams.
|
|
486
|
+
const shell = 70;
|
|
487
|
+
const golden = Math.PI * (3 - Math.sqrt(5));
|
|
488
|
+
for (let i = 0; i < shell; i++) {
|
|
489
|
+
const y = 1 - 2 * (i + 0.5) / shell;
|
|
490
|
+
const rad = Math.sqrt(Math.max(0, 1 - y * y));
|
|
491
|
+
const a = i * golden;
|
|
492
|
+
project(v, rad * Math.cos(a) * R, y * R, rad * Math.sin(a) * R, 0, 1, st, ct, c, c, 1);
|
|
493
|
+
const depth = (v[2] / R + 1) / 2;
|
|
494
|
+
dot(out, v[0], v[1], 0.8 * rs, 0.22 * (0.3 + 0.7 * depth));
|
|
495
|
+
}
|
|
496
|
+
const ta = 0.55;
|
|
497
|
+
const ux = 1;
|
|
498
|
+
const uz = 0;
|
|
499
|
+
const vx = -uz * Math.sin(ta);
|
|
500
|
+
const vy = Math.cos(ta);
|
|
501
|
+
const vz = ux * Math.sin(ta);
|
|
502
|
+
const nx = -uz * vy;
|
|
503
|
+
const ny = uz * vx - ux * vz;
|
|
504
|
+
const nz = ux * vy;
|
|
505
|
+
const lanes = p.rows;
|
|
506
|
+
const half = Math.max(1, (lanes - 1) / 2);
|
|
507
|
+
for (let w = 0; w < lanes; w++) {
|
|
508
|
+
const laneOffset = (w - (lanes - 1) / 2) * 0.075;
|
|
509
|
+
const edge = Math.abs(w - (lanes - 1) / 2) / half;
|
|
510
|
+
for (let k = 0; k < p.density; k++) {
|
|
511
|
+
const a = k / p.density * 2 * Math.PI;
|
|
512
|
+
const wobble = 0.16 * Math.sin(a * 3 - t * 1.7 + w * 0.22) + 0.07 * Math.sin(a * 5 + t * 1.1);
|
|
513
|
+
const off = laneOffset + wobble;
|
|
514
|
+
const ca = Math.cos(a);
|
|
515
|
+
const sa = Math.sin(a);
|
|
516
|
+
const x = ux * ca + vx * sa + nx * off;
|
|
517
|
+
const y = vy * sa + ny * off;
|
|
518
|
+
const z = uz * ca + vz * sa + nz * off;
|
|
519
|
+
const l = Math.max(1e-6, Math.sqrt(x * x + y * y + z * z));
|
|
520
|
+
project(v, x / l * R, y / l * R, z / l * R, 0, 1, st, ct, c, c, 1);
|
|
521
|
+
const depth = (v[2] / R + 1) / 2;
|
|
522
|
+
dot(out, v[0], v[1], (1.1 + 1.7 * depth) * (1 - 0.25 * edge) * rs, (0.48 + 0.44 * depth - 0.18 * edge) * (0.4 + 0.6 * depth));
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/** The three outlines `shaping` cycles through, as closed paths. */
|
|
528
|
+
const SHAPES = [
|
|
529
|
+
// A circle is sampled, not listed — the marker below says so.
|
|
530
|
+
[], [0, -0.26, 0.24, 0.16, -0.24, 0.16], [0, -0.2, 0.2, -0.2, 0.2, 0.2, -0.2, 0.2, -0.2, -0.2]];
|
|
531
|
+
|
|
532
|
+
/** Point at arc-length fraction `f` around shape `index`. */
|
|
533
|
+
function shapePoint(out, index, f) {
|
|
534
|
+
'worklet';
|
|
535
|
+
|
|
536
|
+
const verts = SHAPES[index];
|
|
537
|
+
if (!verts.length) {
|
|
538
|
+
// Started at top-centre and walked clockwise, so every shape's dot zero is
|
|
539
|
+
// in the same place and the morph has nothing to unwind.
|
|
540
|
+
const a = -Math.PI / 2 + f * 2 * Math.PI;
|
|
541
|
+
out[0] = Math.cos(a) * 0.24;
|
|
542
|
+
out[1] = Math.sin(a) * 0.24;
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
const n = verts.length / 2;
|
|
546
|
+
let total = 0;
|
|
547
|
+
for (let i = 0; i < n; i++) {
|
|
548
|
+
const j = (i + 1) % n;
|
|
549
|
+
total += Math.hypot(verts[j * 2] - verts[i * 2], verts[j * 2 + 1] - verts[i * 2 + 1]);
|
|
550
|
+
}
|
|
551
|
+
let target = f * total;
|
|
552
|
+
for (let i = 0; i < n; i++) {
|
|
553
|
+
const j = (i + 1) % n;
|
|
554
|
+
const ax = verts[i * 2];
|
|
555
|
+
const ay = verts[i * 2 + 1];
|
|
556
|
+
const bx = verts[j * 2];
|
|
557
|
+
const by = verts[j * 2 + 1];
|
|
558
|
+
const len = Math.hypot(bx - ax, by - ay);
|
|
559
|
+
if (target <= len || i === n - 1) {
|
|
560
|
+
const ff = len ? Math.min(1, target / len) : 0;
|
|
561
|
+
out[0] = ax + (bx - ax) * ff;
|
|
562
|
+
out[1] = ay + (by - ay) * ff;
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
target -= len;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* `shaping` — a dotted outline morphing circle → triangle → square.
|
|
571
|
+
*
|
|
572
|
+
* The two outlines are blended first and the dots laid evenly along the
|
|
573
|
+
* *result*, rather than each dot being tweened from its old place to its new
|
|
574
|
+
* one. Tweening per dot bunches them at the corners halfway through the morph;
|
|
575
|
+
* re-spacing every frame keeps the outline uniform at every instant, which is
|
|
576
|
+
* what makes the shape read as a shape while it is changing.
|
|
577
|
+
*/
|
|
578
|
+
function drawShaping(out, size, t, p) {
|
|
579
|
+
'worklet';
|
|
580
|
+
|
|
581
|
+
const c = size / 2;
|
|
582
|
+
const hold = 1.4;
|
|
583
|
+
const morph = 0.9;
|
|
584
|
+
const seg = hold + morph;
|
|
585
|
+
const shapes = 3;
|
|
586
|
+
const tc = t % (seg * shapes);
|
|
587
|
+
const k = Math.floor(tc / seg);
|
|
588
|
+
const local = tc - k * seg;
|
|
589
|
+
const raw = local > hold ? (local - hold) / morph : 0;
|
|
590
|
+
const m = raw * raw * (3 - 2 * raw);
|
|
591
|
+
const spread = 1.45;
|
|
592
|
+
const samples = 96;
|
|
593
|
+
const px = [];
|
|
594
|
+
const py = [];
|
|
595
|
+
const a = [0, 0];
|
|
596
|
+
const b = [0, 0];
|
|
597
|
+
for (let i = 0; i < samples; i++) {
|
|
598
|
+
const f = i / samples;
|
|
599
|
+
shapePoint(a, k, f);
|
|
600
|
+
shapePoint(b, (k + 1) % shapes, f);
|
|
601
|
+
px.push((a[0] + (b[0] - a[0]) * m) * spread);
|
|
602
|
+
py.push((a[1] + (b[1] - a[1]) * m) * spread);
|
|
603
|
+
}
|
|
604
|
+
const lengths = [];
|
|
605
|
+
let total = 0;
|
|
606
|
+
for (let i = 0; i < samples; i++) {
|
|
607
|
+
const j = (i + 1) % samples;
|
|
608
|
+
const l = Math.hypot(px[j] - px[i], py[j] - py[i]);
|
|
609
|
+
lengths.push(l);
|
|
610
|
+
total += l;
|
|
611
|
+
}
|
|
612
|
+
const n = p.density;
|
|
613
|
+
const r = Math.max(0.35, 0.021 * 1.35 * spread * size * p.radius * 0.4);
|
|
614
|
+
const pulse = 1 + 0.02 * Math.sin(local * 3.1);
|
|
615
|
+
let index = 0;
|
|
616
|
+
let walked = 0;
|
|
617
|
+
for (let i = 0; i < n; i++) {
|
|
618
|
+
const target = i / n * total;
|
|
619
|
+
while (walked + lengths[index] < target && index < samples - 1) {
|
|
620
|
+
walked += lengths[index];
|
|
621
|
+
index++;
|
|
622
|
+
}
|
|
623
|
+
const j = (index + 1) % samples;
|
|
624
|
+
const f = lengths[index] ? Math.min(1, (target - walked) / lengths[index]) : 0;
|
|
625
|
+
const x = (px[index] + (px[j] - px[index]) * f) * pulse;
|
|
626
|
+
const y = (py[index] + (py[j] - py[index]) * f) * pulse;
|
|
627
|
+
dot(out, c + x * size, c + y * size, r, 0.92);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/** Builds one frame's worth of bucket paths. */
|
|
632
|
+
function renderFrame(state, size, t, p) {
|
|
633
|
+
'worklet';
|
|
634
|
+
|
|
635
|
+
const out = [];
|
|
636
|
+
for (let i = 0; i < BUCKETS; i++) out.push('');
|
|
637
|
+
if (state === 'working') drawWorking(out, size, t, p);else if (state === 'searching') drawSearching(out, size, t, p);else if (state === 'solving') drawSolving(out, size, t, p);else if (state === 'listening') drawListening(out, size, t, p);else if (state === 'composing') drawComposing(out, size, t, p);else drawShaping(out, size, t, p);
|
|
638
|
+
return out;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/* -------------------------------------------------------------------------- */
|
|
642
|
+
/* Component */
|
|
643
|
+
/* -------------------------------------------------------------------------- */
|
|
644
|
+
|
|
645
|
+
const BUCKET_INDICES = Array.from({
|
|
646
|
+
length: BUCKETS
|
|
647
|
+
}, (_unused, index) => index);
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* One ink level's worth of dots, as a single path.
|
|
651
|
+
*
|
|
652
|
+
* It is a component rather than a loop of `useAnimatedProps` in the parent so
|
|
653
|
+
* that each bucket owns exactly one hook — the count is a module constant, but
|
|
654
|
+
* hooks in a loop is still a rule waiting to be broken by the next person who
|
|
655
|
+
* makes it configurable.
|
|
656
|
+
*/
|
|
657
|
+
function Bucket({
|
|
658
|
+
index,
|
|
659
|
+
paths,
|
|
660
|
+
ink
|
|
661
|
+
}) {
|
|
662
|
+
const animatedProps = useAnimatedProps(() => ({
|
|
663
|
+
d: paths.value[index] ?? ''
|
|
664
|
+
}));
|
|
665
|
+
return /*#__PURE__*/_jsx(AnimatedPath, {
|
|
666
|
+
animatedProps: animatedProps,
|
|
667
|
+
fill: ink,
|
|
668
|
+
fillOpacity: (index + 0.5) / BUCKETS
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
export function ThinkingOrb({
|
|
672
|
+
className,
|
|
673
|
+
state = 'working',
|
|
674
|
+
size = 64,
|
|
675
|
+
speed = 1,
|
|
676
|
+
paused = false,
|
|
677
|
+
color,
|
|
678
|
+
accessibilityLabel,
|
|
679
|
+
style,
|
|
680
|
+
...props
|
|
681
|
+
}) {
|
|
682
|
+
const reducedMotion = useReducedMotion();
|
|
683
|
+
const foreground = useCSSVariable('--color-foreground');
|
|
684
|
+
const ink = color ?? (typeof foreground === 'string' ? foreground : '#0a0a0a');
|
|
685
|
+
const profile = PROFILES[state][size <= 32 ? 'sm' : 'md'];
|
|
686
|
+
const clock = useSharedValue(0);
|
|
687
|
+
const paths = useSharedValue([]);
|
|
688
|
+
const still = !paused && !reducedMotion;
|
|
689
|
+
const frame = useFrameCallback(info => {
|
|
690
|
+
'worklet';
|
|
691
|
+
|
|
692
|
+
// Elapsed time is accumulated rather than derived from the total, so
|
|
693
|
+
// `speed` can change mid-animation without the orb jumping to wherever the
|
|
694
|
+
// new rate would have put it by now. A dropped frame is clamped rather than
|
|
695
|
+
// honoured — a 300ms hitch played back at full rate is a lurch.
|
|
696
|
+
const delta = Math.min(info.timeSincePreviousFrame ?? 16, 48) / 1000;
|
|
697
|
+
clock.value += delta * profile.speed * speed;
|
|
698
|
+
paths.value = renderFrame(state, size, clock.value, profile);
|
|
699
|
+
}, false);
|
|
700
|
+
const {
|
|
701
|
+
setActive
|
|
702
|
+
} = frame;
|
|
703
|
+
useEffect(() => {
|
|
704
|
+
setActive(still);
|
|
705
|
+
return () => setActive(false);
|
|
706
|
+
}, [still, setActive]);
|
|
707
|
+
|
|
708
|
+
// A still orb is not an empty one: reduced motion and `paused` both get a
|
|
709
|
+
// representative frame rather than nothing, which is the difference between
|
|
710
|
+
// "not animating" and "broken".
|
|
711
|
+
useEffect(() => {
|
|
712
|
+
if (still) return;
|
|
713
|
+
runOnUI(() => {
|
|
714
|
+
'worklet';
|
|
715
|
+
|
|
716
|
+
paths.value = renderFrame(state, size, clock.value > 0 ? clock.value : STILL_FRAME, profile);
|
|
717
|
+
})();
|
|
718
|
+
}, [still, state, size, profile, clock, paths]);
|
|
719
|
+
return /*#__PURE__*/_jsx(View, {
|
|
720
|
+
...props,
|
|
721
|
+
accessibilityRole: "image",
|
|
722
|
+
accessibilityLabel: accessibilityLabel ?? STATE_LABEL[state],
|
|
723
|
+
className: cn('items-center justify-center', className),
|
|
724
|
+
style: [{
|
|
725
|
+
width: size,
|
|
726
|
+
height: size
|
|
727
|
+
}, style],
|
|
728
|
+
children: /*#__PURE__*/_jsx(Svg, {
|
|
729
|
+
width: size,
|
|
730
|
+
height: size,
|
|
731
|
+
viewBox: `0 0 ${size} ${size}`,
|
|
732
|
+
children: BUCKET_INDICES.map(index => /*#__PURE__*/_jsx(Bucket, {
|
|
733
|
+
index: index,
|
|
734
|
+
paths: paths,
|
|
735
|
+
ink: ink
|
|
736
|
+
}, index))
|
|
737
|
+
})
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
ThinkingOrb.displayName = 'ThinkingOrb';
|
|
741
|
+
//# sourceMappingURL=index.js.map
|