@apollo-deploy/avatars 0.2.2 → 0.2.3
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/dist/index.cjs +158 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -6
- package/dist/index.d.ts +41 -6
- package/dist/index.js +158 -10
- package/dist/index.js.map +1 -1
- package/package.json +67 -67
package/dist/index.cjs
CHANGED
|
@@ -151,6 +151,57 @@ function drawMeshGradient(ctx, seed, size) {
|
|
|
151
151
|
ctx.fillStyle = hg;
|
|
152
152
|
ctx.fillRect(0, 0, size, size);
|
|
153
153
|
}
|
|
154
|
+
function createAnimatedSpots(seed, size) {
|
|
155
|
+
const s = toSeed(seed);
|
|
156
|
+
const { colors } = generatePalette(s);
|
|
157
|
+
const random = seededRandom(s * 12345);
|
|
158
|
+
const numSpots = 8 + Math.floor(random() * 5);
|
|
159
|
+
const spots = [];
|
|
160
|
+
for (let i = 0; i < numSpots; i++) {
|
|
161
|
+
const angle = random() * Math.PI * 2;
|
|
162
|
+
const distance = random() * size * 0.4;
|
|
163
|
+
const centerX = size / 2 + Math.cos(angle) * distance;
|
|
164
|
+
const centerY = size / 2 + Math.sin(angle) * distance;
|
|
165
|
+
spots.push({
|
|
166
|
+
x: centerX + (random() - 0.5) * size * 0.3,
|
|
167
|
+
y: centerY + (random() - 0.5) * size * 0.3,
|
|
168
|
+
radius: size * (0.3 + random() * 0.4),
|
|
169
|
+
color: colors[i % colors.length],
|
|
170
|
+
phaseX: random(),
|
|
171
|
+
phaseY: random(),
|
|
172
|
+
amplitude: 0.03 + random() * 0.06
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
spots.sort((a, b) => b.radius - a.radius);
|
|
176
|
+
return spots;
|
|
177
|
+
}
|
|
178
|
+
function drawAnimatedSpots(ctx, spots, backgroundColor, size, time) {
|
|
179
|
+
ctx.fillStyle = backgroundColor;
|
|
180
|
+
ctx.fillRect(0, 0, size, size);
|
|
181
|
+
ctx.globalCompositeOperation = "source-over";
|
|
182
|
+
for (const spot of spots) {
|
|
183
|
+
const periodX = 12 + spot.phaseX * 8;
|
|
184
|
+
const periodY = 14 + spot.phaseY * 10;
|
|
185
|
+
const drift = spot.amplitude * size;
|
|
186
|
+
const sx = spot.x + Math.sin(time * (2 * Math.PI / periodX) + spot.phaseX * Math.PI * 2) * drift;
|
|
187
|
+
const sy = spot.y + Math.cos(time * (2 * Math.PI / periodY) + spot.phaseY * Math.PI * 2) * drift;
|
|
188
|
+
const g = ctx.createRadialGradient(sx, sy, 0, sx, sy, spot.radius);
|
|
189
|
+
g.addColorStop(0, `${spot.color}FF`);
|
|
190
|
+
g.addColorStop(0.3, `${spot.color}DD`);
|
|
191
|
+
g.addColorStop(0.6, `${spot.color}88`);
|
|
192
|
+
g.addColorStop(1, `${spot.color}00`);
|
|
193
|
+
ctx.fillStyle = g;
|
|
194
|
+
ctx.fillRect(0, 0, size, size);
|
|
195
|
+
}
|
|
196
|
+
const hSize = size * 0.3;
|
|
197
|
+
const hx = size * 0.35 + Math.sin(time * 0.3) * size * 0.1;
|
|
198
|
+
const hy = size * 0.35 + Math.cos(time * 0.4) * size * 0.1;
|
|
199
|
+
const hg = ctx.createRadialGradient(hx, hy, 0, hx, hy, hSize);
|
|
200
|
+
hg.addColorStop(0, "rgba(255,255,255,0.15)");
|
|
201
|
+
hg.addColorStop(1, "rgba(255,255,255,0)");
|
|
202
|
+
ctx.fillStyle = hg;
|
|
203
|
+
ctx.fillRect(0, 0, size, size);
|
|
204
|
+
}
|
|
154
205
|
function blurFor(size, blur) {
|
|
155
206
|
if (blur === 0) return 0;
|
|
156
207
|
return blur ?? Math.round(size * DEFAULT_BLUR_FRACTION);
|
|
@@ -235,8 +286,57 @@ function gradientToBlob(seed, options = {}) {
|
|
|
235
286
|
}
|
|
236
287
|
var RENDER_SIZE = 256;
|
|
237
288
|
var BLUR_FRACTION = 0.06;
|
|
289
|
+
var REDRAW_MS = 67;
|
|
290
|
+
var subscribers = /* @__PURE__ */ new Set();
|
|
291
|
+
var tickerRaf = null;
|
|
292
|
+
var tickerLast = 0;
|
|
293
|
+
var tickerPaused = false;
|
|
294
|
+
function tick(now) {
|
|
295
|
+
tickerRaf = requestAnimationFrame(tick);
|
|
296
|
+
if (tickerPaused) return;
|
|
297
|
+
if (now - tickerLast < REDRAW_MS) return;
|
|
298
|
+
tickerLast = now;
|
|
299
|
+
for (const draw of subscribers) draw();
|
|
300
|
+
}
|
|
301
|
+
function subscribe(draw) {
|
|
302
|
+
subscribers.add(draw);
|
|
303
|
+
if (subscribers.size === 1 && tickerRaf === null) {
|
|
304
|
+
tickerLast = 0;
|
|
305
|
+
tickerRaf = requestAnimationFrame(tick);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function unsubscribe(draw) {
|
|
309
|
+
subscribers.delete(draw);
|
|
310
|
+
if (subscribers.size === 0 && tickerRaf !== null) {
|
|
311
|
+
cancelAnimationFrame(tickerRaf);
|
|
312
|
+
tickerRaf = null;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
var io = null;
|
|
316
|
+
function ensureIntersectionObserver() {
|
|
317
|
+
if (io) return io;
|
|
318
|
+
io = new IntersectionObserver(
|
|
319
|
+
(entries) => {
|
|
320
|
+
for (const entry of entries) {
|
|
321
|
+
const el = entry.target;
|
|
322
|
+
el.__oaOnVis?.();
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
{ threshold: 0 }
|
|
326
|
+
);
|
|
327
|
+
return io;
|
|
328
|
+
}
|
|
329
|
+
var visHandlerInstalled = false;
|
|
330
|
+
function ensureVisibilityHandler() {
|
|
331
|
+
if (visHandlerInstalled || typeof document === "undefined") return;
|
|
332
|
+
visHandlerInstalled = true;
|
|
333
|
+
document.addEventListener("visibilitychange", () => {
|
|
334
|
+
tickerPaused = document.visibilityState === "hidden";
|
|
335
|
+
if (!tickerPaused) tickerLast = 0;
|
|
336
|
+
});
|
|
337
|
+
}
|
|
238
338
|
var ANIMATED_CLASS = "oa-avatar-canvas--animated";
|
|
239
|
-
var
|
|
339
|
+
var STYLE_ID = "oa-avatar-keyframes";
|
|
240
340
|
var KEYFRAMES_CSS = `
|
|
241
341
|
@keyframes oa-avatar-drift {
|
|
242
342
|
0% { transform: scale(1.25) translate(0%, 0%) rotate(0deg); }
|
|
@@ -265,12 +365,12 @@ var KEYFRAMES_CSS = `
|
|
|
265
365
|
var stylesInjected = false;
|
|
266
366
|
function ensureAnimationStyles() {
|
|
267
367
|
if (stylesInjected || typeof document === "undefined") return;
|
|
268
|
-
if (document.getElementById(
|
|
368
|
+
if (document.getElementById(STYLE_ID)) {
|
|
269
369
|
stylesInjected = true;
|
|
270
370
|
return;
|
|
271
371
|
}
|
|
272
372
|
const el = document.createElement("style");
|
|
273
|
-
el.id =
|
|
373
|
+
el.id = STYLE_ID;
|
|
274
374
|
el.textContent = KEYFRAMES_CSS;
|
|
275
375
|
document.head.appendChild(el);
|
|
276
376
|
stylesInjected = true;
|
|
@@ -285,23 +385,68 @@ function GradientAvatar({
|
|
|
285
385
|
style
|
|
286
386
|
}) {
|
|
287
387
|
const canvasRef = react.useRef(null);
|
|
388
|
+
const wrapperRef = react.useRef(null);
|
|
389
|
+
const startRef = react.useRef(0);
|
|
390
|
+
const visibleRef = react.useRef(true);
|
|
391
|
+
react.useEffect(() => {
|
|
392
|
+
if (animate) {
|
|
393
|
+
ensureAnimationStyles();
|
|
394
|
+
ensureVisibilityHandler();
|
|
395
|
+
}
|
|
396
|
+
}, [animate]);
|
|
397
|
+
const draw = react.useCallback(() => {
|
|
398
|
+
const canvas = canvasRef.current;
|
|
399
|
+
if (!canvas || !visibleRef.current) return;
|
|
400
|
+
const ctx = canvas.getContext("2d");
|
|
401
|
+
if (!ctx) return;
|
|
402
|
+
if (startRef.current === 0) startRef.current = performance.now();
|
|
403
|
+
const elapsed = (performance.now() - startRef.current) / 1e3;
|
|
404
|
+
const time = elapsed * (2 * Math.PI / animationDuration);
|
|
405
|
+
const { colors } = generatePalette(seed);
|
|
406
|
+
const spots = createAnimatedSpots(seed, RENDER_SIZE);
|
|
407
|
+
drawAnimatedSpots(ctx, spots, colors[0], RENDER_SIZE, time);
|
|
408
|
+
}, [seed, animationDuration]);
|
|
288
409
|
react.useEffect(() => {
|
|
289
410
|
const canvas = canvasRef.current;
|
|
290
411
|
if (!canvas) return;
|
|
291
412
|
const ctx = canvas.getContext("2d");
|
|
292
413
|
if (!ctx) return;
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
414
|
+
const prefersReduced = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
415
|
+
if (!animate || prefersReduced) {
|
|
416
|
+
ctx.clearRect(0, 0, RENDER_SIZE, RENDER_SIZE);
|
|
417
|
+
drawMeshGradient(ctx, seed, RENDER_SIZE);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
startRef.current = 0;
|
|
421
|
+
const { colors } = generatePalette(seed);
|
|
422
|
+
const spots = createAnimatedSpots(seed, RENDER_SIZE);
|
|
423
|
+
drawAnimatedSpots(ctx, spots, colors[0], RENDER_SIZE, 0);
|
|
424
|
+
subscribe(draw);
|
|
425
|
+
return () => {
|
|
426
|
+
unsubscribe(draw);
|
|
427
|
+
};
|
|
428
|
+
}, [seed, animate, draw]);
|
|
296
429
|
react.useEffect(() => {
|
|
297
|
-
|
|
430
|
+
const wrapper = wrapperRef.current;
|
|
431
|
+
if (!animate || !wrapper) return;
|
|
432
|
+
const observer = ensureIntersectionObserver();
|
|
433
|
+
wrapper.__oaOnVis = () => {
|
|
434
|
+
visibleRef.current = true;
|
|
435
|
+
};
|
|
436
|
+
observer.observe(wrapper);
|
|
437
|
+
return () => {
|
|
438
|
+
observer.unobserve(wrapper);
|
|
439
|
+
visibleRef.current = false;
|
|
440
|
+
delete wrapper.__oaOnVis;
|
|
441
|
+
};
|
|
298
442
|
}, [animate]);
|
|
299
443
|
const blurPx = Math.max(1, Math.round(size * BLUR_FRACTION));
|
|
300
444
|
const canvasStyle = {
|
|
301
445
|
width: "100%",
|
|
302
446
|
height: "100%",
|
|
303
447
|
display: "block",
|
|
304
|
-
filter: `blur(${blurPx}px)
|
|
448
|
+
filter: `blur(${blurPx}px)`,
|
|
449
|
+
transform: "scale(1.25)"
|
|
305
450
|
};
|
|
306
451
|
if (animate) {
|
|
307
452
|
canvasStyle.animationDuration = `${animationDuration}s`;
|
|
@@ -309,6 +454,7 @@ function GradientAvatar({
|
|
|
309
454
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
310
455
|
"span",
|
|
311
456
|
{
|
|
457
|
+
ref: wrapperRef,
|
|
312
458
|
className,
|
|
313
459
|
style: {
|
|
314
460
|
display: "inline-block",
|
|
@@ -322,6 +468,8 @@ function GradientAvatar({
|
|
|
322
468
|
"canvas",
|
|
323
469
|
{
|
|
324
470
|
ref: canvasRef,
|
|
471
|
+
role: "img",
|
|
472
|
+
"aria-label": `Gradient avatar for seed ${seed}`,
|
|
325
473
|
width: RENDER_SIZE,
|
|
326
474
|
height: RENDER_SIZE,
|
|
327
475
|
className: animate ? ANIMATED_CLASS : void 0,
|
|
@@ -333,6 +481,8 @@ function GradientAvatar({
|
|
|
333
481
|
}
|
|
334
482
|
|
|
335
483
|
exports.GradientAvatar = GradientAvatar;
|
|
484
|
+
exports.createAnimatedSpots = createAnimatedSpots;
|
|
485
|
+
exports.drawAnimatedSpots = drawAnimatedSpots;
|
|
336
486
|
exports.drawMeshGradient = drawMeshGradient;
|
|
337
487
|
exports.generatePalette = generatePalette;
|
|
338
488
|
exports.gradientToBlob = gradientToBlob;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/engine.ts","../src/index.tsx"],"names":["useRef","useEffect","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Gradient engine for @apollo-deploy/avatars.\n *\n * Framework-agnostic mesh-gradient avatar generator. Every seed (string or\n * number) deterministically produces a unique gradient — no stored images,\n * no network. Pure palette/RNG core plus optional Canvas2D render helpers.\n */\n\nexport type Harmony =\n\t| \"analogous\"\n\t| \"triadic\"\n\t| \"splitComplementary\"\n\t| \"tetradic\"\n\t| \"complementary\";\n\nexport interface GradientPalette {\n\t/** The numeric seed the palette was derived from. */\n\tseed: number;\n\t/** Hex color stops used to paint the mesh. */\n\tcolors: string[];\n\t/** Which color-harmony rule produced the hues. */\n\tharmony: Harmony;\n}\n\nconst HARMONY_TYPES: Harmony[] = [\n\t\"analogous\",\n\t\"triadic\",\n\t\"splitComplementary\",\n\t\"tetradic\",\n\t\"complementary\",\n];\n\nconst GOLDEN_ANGLE = 137.5;\n\n/** Default blur radius as a fraction of the rendered dimension. */\nexport const DEFAULT_BLUR_FRACTION = 0.06;\n\nfunction seededRandom(seed: number): () => number {\n\tlet s = seed;\n\treturn () => {\n\t\ts += 0x6d2b79f5;\n\t\tlet t = s;\n\t\tt = Math.imul(t ^ (t >>> 15), t | 1);\n\t\tt ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n\t\treturn ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n\t};\n}\n\nfunction hslToHex(h: number, s: number, l: number): string {\n\th = ((h % 360) + 360) % 360;\n\ts = Math.max(0, Math.min(100, s)) / 100;\n\tl = Math.max(0, Math.min(100, l)) / 100;\n\n\tconst c = (1 - Math.abs(2 * l - 1)) * s;\n\tconst x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n\tconst m = l - c / 2;\n\n\tlet r = 0;\n\tlet g = 0;\n\tlet b = 0;\n\tif (h < 60) {\n\t\tr = c;\n\t\tg = x;\n\t} else if (h < 120) {\n\t\tr = x;\n\t\tg = c;\n\t} else if (h < 180) {\n\t\tg = c;\n\t\tb = x;\n\t} else if (h < 240) {\n\t\tg = x;\n\t\tb = c;\n\t} else if (h < 300) {\n\t\tr = x;\n\t\tb = c;\n\t} else {\n\t\tr = c;\n\t\tb = x;\n\t}\n\n\tconst toHex = (n: number) => {\n\t\tconst hex = Math.round((n + m) * 255).toString(16);\n\t\treturn hex.length === 1 ? `0${hex}` : hex;\n\t};\n\treturn `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();\n}\n\nfunction harmonyHues(baseHue: number, harmony: Harmony): number[] {\n\tswitch (harmony) {\n\t\tcase \"analogous\":\n\t\t\treturn [baseHue, baseHue + 30, baseHue + 60, baseHue - 30];\n\t\tcase \"triadic\":\n\t\t\treturn [baseHue, baseHue + 120, baseHue + 240];\n\t\tcase \"splitComplementary\":\n\t\t\treturn [baseHue, baseHue + 150, baseHue + 210];\n\t\tcase \"tetradic\":\n\t\t\treturn [baseHue, baseHue + 90, baseHue + 180, baseHue + 270];\n\t\tcase \"complementary\":\n\t\t\treturn [baseHue, baseHue + 180, baseHue + 20, baseHue + 200];\n\t}\n}\n\n/**\n * Stable string → 32-bit unsigned hash (FNV-1a + bit-mixing avalanche).\n * Uses the full uint32 range as a seed so similar strings diverge fully.\n */\nexport function seedFromString(input: string): number {\n\tlet h = 2166136261 >>> 0;\n\tfor (let i = 0; i < input.length; i++) {\n\t\th ^= input.charCodeAt(i);\n\t\th = Math.imul(h, 16777619) >>> 0;\n\t}\n\th ^= h >>> 16;\n\th = Math.imul(h, 0x7feb352d) >>> 0;\n\th ^= h >>> 15;\n\th = Math.imul(h, 0x846ca68b) >>> 0;\n\th ^= h >>> 16;\n\treturn h >>> 0;\n}\n\n/** Normalize a string or number seed to the numeric seed used internally. */\nexport function toSeed(seed: number | string): number {\n\tif (typeof seed === \"number\") return seed;\n\treturn seedFromString(seed);\n}\n\n/** Derive the deterministic color palette for a seed. */\nexport function generatePalette(seed: number | string): GradientPalette {\n\tconst s = toSeed(seed);\n\tconst random = seededRandom(s);\n\tconst baseHue = (s * GOLDEN_ANGLE) % 360;\n\tconst harmonyIndex = Math.floor(random() * HARMONY_TYPES.length);\n\tconst harmony = HARMONY_TYPES[harmonyIndex];\n\tconst hues = harmonyHues(baseHue, harmony);\n\tconst colors = hues.map((hue) => {\n\t\tconst saturation = 75 + random() * 25;\n\t\tconst lightness = 50 + random() * 20;\n\t\treturn hslToHex(hue, saturation, lightness);\n\t});\n\treturn { seed: s, colors, harmony };\n}\n\n/**\n * Minimal Canvas2D context surface the renderer needs. Both\n * `HTMLCanvasElement` and `OffscreenCanvas` 2D contexts satisfy it.\n */\nexport type GradientContext = {\n\tfillStyle: string | CanvasGradient | CanvasPattern;\n\tglobalCompositeOperation: GlobalCompositeOperation;\n\tfillRect(x: number, y: number, w: number, h: number): void;\n\tcreateRadialGradient(\n\t\tx0: number,\n\t\ty0: number,\n\t\tr0: number,\n\t\tx1: number,\n\t\ty1: number,\n\t\tr1: number,\n\t): CanvasGradient;\n};\n\n/**\n * Draw the mesh gradient for `seed` into `ctx` at `size` x `size`.\n * The caller is responsible for any blur — apply `filter: blur(…)` on the\n * displayed canvas (≈6% of the rendered dimension) for the signature look,\n * or use {@link renderGradient} / {@link gradientToDataURL} which bake it in.\n */\nexport function drawMeshGradient(\n\tctx: GradientContext,\n\tseed: number | string,\n\tsize: number,\n): void {\n\tconst s = toSeed(seed);\n\tconst { colors } = generatePalette(s);\n\tconst random = seededRandom(s * 12345);\n\n\tctx.fillStyle = colors[0];\n\tctx.fillRect(0, 0, size, size);\n\n\tconst numSpots = 8 + Math.floor(random() * 5);\n\tconst spots: Array<{ x: number; y: number; radius: number; color: string }> =\n\t\t[];\n\n\tfor (let i = 0; i < numSpots; i++) {\n\t\tconst angle = random() * Math.PI * 2;\n\t\tconst distance = random() * size * 0.4;\n\t\tconst centerX = size / 2 + Math.cos(angle) * distance;\n\t\tconst centerY = size / 2 + Math.sin(angle) * distance;\n\t\tspots.push({\n\t\t\tx: centerX + (random() - 0.5) * size * 0.3,\n\t\t\ty: centerY + (random() - 0.5) * size * 0.3,\n\t\t\tradius: size * (0.3 + random() * 0.4),\n\t\t\tcolor: colors[i % colors.length],\n\t\t});\n\t}\n\n\tspots.sort((a, b) => b.radius - a.radius);\n\n\tctx.globalCompositeOperation = \"source-over\";\n\tfor (const spot of spots) {\n\t\tconst g = ctx.createRadialGradient(\n\t\t\tspot.x,\n\t\t\tspot.y,\n\t\t\t0,\n\t\t\tspot.x,\n\t\t\tspot.y,\n\t\t\tspot.radius,\n\t\t);\n\t\tg.addColorStop(0, `${spot.color}FF`);\n\t\tg.addColorStop(0.3, `${spot.color}DD`);\n\t\tg.addColorStop(0.6, `${spot.color}88`);\n\t\tg.addColorStop(1, `${spot.color}00`);\n\t\tctx.fillStyle = g;\n\t\tctx.fillRect(0, 0, size, size);\n\t}\n\n\tconst hx = size * 0.3 + random() * size * 0.2;\n\tconst hy = size * 0.3 + random() * size * 0.2;\n\tconst hg = ctx.createRadialGradient(hx, hy, 0, hx, hy, size * 0.3);\n\thg.addColorStop(0, \"rgba(255,255,255,0.15)\");\n\thg.addColorStop(1, \"rgba(255,255,255,0)\");\n\tctx.fillStyle = hg;\n\tctx.fillRect(0, 0, size, size);\n}\n\nexport interface RenderOptions {\n\t/**\n\t * Blur radius in pixels. Defaults to ~6% of the canvas size for the\n\t * signature soft look. Pass `0` to disable.\n\t */\n\tblur?: number;\n}\n\nfunction blurFor(size: number, blur?: number): number {\n\tif (blur === 0) return 0;\n\treturn blur ?? Math.round(size * DEFAULT_BLUR_FRACTION);\n}\n\n/**\n * Render a seed's gradient into an existing canvas, baking in the soft blur.\n * Draws at the canvas's current `width`/`height`. Browser/OffscreenCanvas only.\n */\nexport function renderGradient(\n\tcanvas: HTMLCanvasElement | OffscreenCanvas,\n\tseed: number | string,\n\toptions: RenderOptions = {},\n): void {\n\tconst size = canvas.width;\n\tconst blur = blurFor(size, options.blur);\n\n\tconst ctx = canvas.getContext(\"2d\") as CanvasRenderingContext2D | null;\n\tif (!ctx) return;\n\n\tif (blur <= 0) {\n\t\tctx.clearRect(0, 0, size, size);\n\t\tdrawMeshGradient(ctx, seed, size);\n\t\treturn;\n\t}\n\n\t// Draw the raw mesh on a scratch canvas, then composite it back with blur\n\t// scaled up slightly so the soft edges fall outside the frame (no ring).\n\tconst scratch = createCanvas(size, size);\n\tconst sctx = scratch.getContext(\"2d\") as CanvasRenderingContext2D | null;\n\tif (!sctx) return;\n\tdrawMeshGradient(sctx, seed, size);\n\n\tconst scaleUp = 1 + (blur / size) * 4;\n\tconst dw = size * scaleUp;\n\tconst offset = (dw - size) / 2;\n\tctx.clearRect(0, 0, size, size);\n\tif (supportsCanvasFilter()) {\n\t\tctx.filter = `blur(${blur}px)`;\n\t\tctx.drawImage(scratch as CanvasImageSource, -offset, -offset, dw, dw);\n\t\tctx.filter = \"none\";\n\t\treturn;\n\t}\n\t// 2D-canvas `filter` is a silent no-op on Safari < 17: approximate the\n\t// gaussian by bouncing through a small canvas. Bilinear resampling on the\n\t// way down and back up smears by roughly the downscale factor, which is\n\t// plenty for a mesh that is already smooth gradients.\n\tconst factor = Math.max(2, Math.min(16, blur / 2));\n\tconst sw = Math.max(1, Math.round(size / factor));\n\tconst small = createCanvas(sw, sw);\n\tconst smallCtx = small.getContext(\"2d\") as CanvasRenderingContext2D | null;\n\tif (!smallCtx) {\n\t\tctx.drawImage(scratch as CanvasImageSource, -offset, -offset, dw, dw);\n\t\treturn;\n\t}\n\tsmallCtx.imageSmoothingEnabled = true;\n\tsmallCtx.imageSmoothingQuality = \"high\";\n\tsmallCtx.drawImage(scratch as CanvasImageSource, 0, 0, sw, sw);\n\tctx.imageSmoothingEnabled = true;\n\tctx.imageSmoothingQuality = \"high\";\n\tctx.drawImage(small as CanvasImageSource, -offset, -offset, dw, dw);\n}\n\n/* Engines that honor 2D-canvas `filter` echo an assigned value back from the\n * property; Safari < 17 ignores the assignment. Probed once, then cached. */\nlet canvasFilterSupport: boolean | null = null;\nfunction supportsCanvasFilter(): boolean {\n\tif (canvasFilterSupport !== null) return canvasFilterSupport;\n\tconst probe = createCanvas(1, 1).getContext(\n\t\t\"2d\",\n\t) as CanvasRenderingContext2D | null;\n\tif (!probe) {\n\t\tcanvasFilterSupport = false;\n\t\treturn canvasFilterSupport;\n\t}\n\tprobe.filter = \"blur(1px)\";\n\tcanvasFilterSupport = probe.filter === \"blur(1px)\";\n\treturn canvasFilterSupport;\n}\n\nfunction createCanvas(\n\tw: number,\n\th: number,\n): HTMLCanvasElement | OffscreenCanvas {\n\tif (typeof OffscreenCanvas !== \"undefined\") {\n\t\treturn new OffscreenCanvas(w, h);\n\t}\n\tconst c = document.createElement(\"canvas\");\n\tc.width = w;\n\tc.height = h;\n\treturn c;\n}\n\nexport interface ExportOptions extends RenderOptions {\n\t/** Output pixel dimensions (square). Default: 512. */\n\tsize?: number;\n\t/** Image MIME type. Default: \"image/png\". */\n\ttype?: string;\n\t/** Quality 0–1 for lossy types. Default: 0.92. */\n\tquality?: number;\n}\n\n/** Render a seed's gradient and return it as a data URL. Browser only. */\nexport function gradientToDataURL(\n\tseed: number | string,\n\toptions: ExportOptions = {},\n): string {\n\tconst { size = 512, type = \"image/png\", quality = 0.92 } = options;\n\tconst canvas = document.createElement(\"canvas\");\n\tcanvas.width = size;\n\tcanvas.height = size;\n\trenderGradient(canvas, seed, options);\n\treturn canvas.toDataURL(type, quality);\n}\n\n/** Render a seed's gradient and resolve a Blob (or null). Browser only. */\nexport function gradientToBlob(\n\tseed: number | string,\n\toptions: ExportOptions = {},\n): Promise<Blob | null> {\n\tconst { size = 512, type = \"image/png\", quality = 0.92 } = options;\n\tconst canvas = document.createElement(\"canvas\");\n\tcanvas.width = size;\n\tcanvas.height = size;\n\trenderGradient(canvas, seed, options);\n\treturn new Promise((resolve) => canvas.toBlob(resolve, type, quality));\n}\n","import type { CSSProperties } from \"react\";\nimport { useEffect, useRef } from \"react\";\nimport { drawMeshGradient } from \"./engine\";\n\nexport interface GradientAvatarProps {\n\t/** Any string or number — each unique seed produces a unique gradient. */\n\tseed: number | string;\n\t/** Rendered size in pixels. Default: 32. */\n\tsize?: number;\n\t/**\n\t * Corner radius. Number = pixels, string = any CSS length.\n\t * Defaults to a full circle; pass `0` for a square or e.g. `12` for a\n\t * rounded square. Default: \"9999px\".\n\t */\n\tradius?: number | string;\n\t/**\n\t * Slowly drift the gradient with a GPU-only CSS animation. The canvas is\n\t * still painted just once — the motion is a compositor transform on the\n\t * cached layer, so there is no per-frame JavaScript, no repaint, and no\n\t * extra memory. Automatically disabled for users who request reduced\n\t * motion. Default: `false`.\n\t */\n\tanimate?: boolean;\n\t/**\n\t * Duration of one full drift cycle in seconds. Slower is subtler and\n\t * cheaper. Only applies when {@link animate} is `true`. Default: `16`.\n\t */\n\tanimationDuration?: number;\n\t/** Additional CSS classes on the wrapper. */\n\tclassName?: string;\n\t/** Extra inline styles merged onto the wrapper. */\n\tstyle?: CSSProperties;\n}\n\n/** Internal render resolution. Higher than display size so the CSS blur is smooth. */\nconst RENDER_SIZE = 256;\n/** Blur radius as a fraction of display size. */\nconst BLUR_FRACTION = 0.06;\n\n/** Class applied to the canvas when animating. Styles injected once, lazily. */\nconst ANIMATED_CLASS = \"oa-avatar-canvas--animated\";\nconst STYLE_ELEMENT_ID = \"oa-avatar-keyframes\";\n\n/**\n * Keyframes for the drift. The canvas is scaled up so that the rotation and\n * translation never expose the clipped container edges, and the whole thing is\n * a single `transform` track so the browser can run it entirely on the\n * compositor thread. `prefers-reduced-motion` opts out with no JS listener.\n */\nconst KEYFRAMES_CSS = `\n@keyframes oa-avatar-drift {\n\t0% { transform: scale(1.25) translate(0%, 0%) rotate(0deg); }\n\t20% { transform: scale(1.30) translate(-3%, 2%) rotate(8deg); }\n\t40% { transform: scale(1.22) translate(3%, 3%) rotate(-6deg); }\n\t60% { transform: scale(1.28) translate(2%, -3%) rotate(5deg); }\n\t80% { transform: scale(1.24) translate(-2%, -2%) rotate(-4deg); }\n\t100% { transform: scale(1.25) translate(0%, 0%) rotate(0deg); }\n}\n.${ANIMATED_CLASS} {\n\ttransform-origin: center;\n\twill-change: transform;\n\tanimation-name: oa-avatar-drift;\n\tanimation-timing-function: ease-in-out;\n\tanimation-iteration-count: infinite;\n\tanimation-direction: alternate;\n}\n@media (prefers-reduced-motion: reduce) {\n\t.${ANIMATED_CLASS} {\n\t\tanimation: none;\n\t\ttransform: none;\n\t\twill-change: auto;\n\t}\n}\n`;\n\nlet stylesInjected = false;\n\n/** Inject the drift keyframes into the document head exactly once (client-only). */\nfunction ensureAnimationStyles(): void {\n\tif (stylesInjected || typeof document === \"undefined\") return;\n\tif (document.getElementById(STYLE_ELEMENT_ID)) {\n\t\tstylesInjected = true;\n\t\treturn;\n\t}\n\tconst el = document.createElement(\"style\");\n\tel.id = STYLE_ELEMENT_ID;\n\tel.textContent = KEYFRAMES_CSS;\n\tdocument.head.appendChild(el);\n\tstylesInjected = true;\n}\n\n/**\n * Renders a deterministic mesh-gradient avatar on a `<canvas>`.\n * The same seed always produces the same gradient.\n */\nexport function GradientAvatar({\n\tseed,\n\tsize = 32,\n\tradius = \"9999px\",\n\tanimate = false,\n\tanimationDuration = 16,\n\tclassName,\n\tstyle,\n}: GradientAvatarProps) {\n\tconst canvasRef = useRef<HTMLCanvasElement>(null);\n\n\tuseEffect(() => {\n\t\tconst canvas = canvasRef.current;\n\t\tif (!canvas) return;\n\t\tconst ctx = canvas.getContext(\"2d\");\n\t\tif (!ctx) return;\n\t\tctx.clearRect(0, 0, RENDER_SIZE, RENDER_SIZE);\n\t\tdrawMeshGradient(ctx, seed, RENDER_SIZE);\n\t}, [seed]);\n\n\tuseEffect(() => {\n\t\tif (animate) ensureAnimationStyles();\n\t}, [animate]);\n\n\tconst blurPx = Math.max(1, Math.round(size * BLUR_FRACTION));\n\n\tconst canvasStyle: CSSProperties = {\n\t\twidth: \"100%\",\n\t\theight: \"100%\",\n\t\tdisplay: \"block\",\n\t\tfilter: `blur(${blurPx}px)`,\n\t};\n\tif (animate) {\n\t\tcanvasStyle.animationDuration = `${animationDuration}s`;\n\t}\n\n\treturn (\n\t\t<span\n\t\t\tclassName={className}\n\t\t\tstyle={{\n\t\t\t\tdisplay: \"inline-block\",\n\t\t\t\toverflow: \"hidden\",\n\t\t\t\tborderRadius: radius,\n\t\t\t\twidth: size,\n\t\t\t\theight: size,\n\t\t\t\t...style,\n\t\t\t}}\n\t\t>\n\t\t\t<canvas\n\t\t\t\tref={canvasRef}\n\t\t\t\twidth={RENDER_SIZE}\n\t\t\t\theight={RENDER_SIZE}\n\t\t\t\tclassName={animate ? ANIMATED_CLASS : undefined}\n\t\t\t\tstyle={canvasStyle}\n\t\t\t/>\n\t\t</span>\n\t);\n}\n\nexport type {\n\tExportOptions,\n\tGradientPalette,\n\tHarmony,\n\tRenderOptions,\n} from \"./engine\";\nexport {\n\tdrawMeshGradient,\n\tgeneratePalette,\n\tgradientToBlob,\n\tgradientToDataURL,\n\trenderGradient,\n\tseedFromString,\n\ttoSeed,\n} from \"./engine\";\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/engine.ts","../src/index.tsx"],"names":["useRef","useEffect","useCallback","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Gradient engine for @apollo-deploy/avatars.\n *\n * Framework-agnostic mesh-gradient avatar generator. Every seed (string or\n * number) deterministically produces a unique gradient — no stored images,\n * no network. Pure palette/RNG core plus optional Canvas2D render helpers.\n */\n\nexport type Harmony =\n \"analogous\" | \"triadic\" | \"splitComplementary\" | \"tetradic\" | \"complementary\";\n\nexport interface GradientPalette {\n /** The numeric seed the palette was derived from. */\n seed: number;\n /** Hex color stops used to paint the mesh. */\n colors: string[];\n /** Which color-harmony rule produced the hues. */\n harmony: Harmony;\n}\n\nconst HARMONY_TYPES: Harmony[] = [\n \"analogous\",\n \"triadic\",\n \"splitComplementary\",\n \"tetradic\",\n \"complementary\",\n];\n\nconst GOLDEN_ANGLE = 137.5;\n\n/** Default blur radius as a fraction of the rendered dimension. */\nexport const DEFAULT_BLUR_FRACTION = 0.06;\n\nfunction seededRandom(seed: number): () => number {\n let s = seed;\n return () => {\n s += 0x6d2b79f5;\n let t = s;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\nfunction hslToHex(h: number, s: number, l: number): string {\n h = ((h % 360) + 360) % 360;\n s = Math.max(0, Math.min(100, s)) / 100;\n l = Math.max(0, Math.min(100, l)) / 100;\n\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n const m = l - c / 2;\n\n let r = 0;\n let g = 0;\n let b = 0;\n if (h < 60) {\n r = c;\n g = x;\n } else if (h < 120) {\n r = x;\n g = c;\n } else if (h < 180) {\n g = c;\n b = x;\n } else if (h < 240) {\n g = x;\n b = c;\n } else if (h < 300) {\n r = x;\n b = c;\n } else {\n r = c;\n b = x;\n }\n\n const toHex = (n: number) => {\n const hex = Math.round((n + m) * 255).toString(16);\n return hex.length === 1 ? `0${hex}` : hex;\n };\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();\n}\n\nfunction harmonyHues(baseHue: number, harmony: Harmony): number[] {\n switch (harmony) {\n case \"analogous\":\n return [baseHue, baseHue + 30, baseHue + 60, baseHue - 30];\n case \"triadic\":\n return [baseHue, baseHue + 120, baseHue + 240];\n case \"splitComplementary\":\n return [baseHue, baseHue + 150, baseHue + 210];\n case \"tetradic\":\n return [baseHue, baseHue + 90, baseHue + 180, baseHue + 270];\n case \"complementary\":\n return [baseHue, baseHue + 180, baseHue + 20, baseHue + 200];\n }\n}\n\n/**\n * Stable string → 32-bit unsigned hash (FNV-1a + bit-mixing avalanche).\n * Uses the full uint32 range as a seed so similar strings diverge fully.\n */\nexport function seedFromString(input: string): number {\n let h = 2166136261 >>> 0;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = Math.imul(h, 16777619) >>> 0;\n }\n h ^= h >>> 16;\n h = Math.imul(h, 0x7feb352d) >>> 0;\n h ^= h >>> 15;\n h = Math.imul(h, 0x846ca68b) >>> 0;\n h ^= h >>> 16;\n return h >>> 0;\n}\n\n/** Normalize a string or number seed to the numeric seed used internally. */\nexport function toSeed(seed: number | string): number {\n if (typeof seed === \"number\") return seed;\n return seedFromString(seed);\n}\n\n/** Derive the deterministic color palette for a seed. */\nexport function generatePalette(seed: number | string): GradientPalette {\n const s = toSeed(seed);\n const random = seededRandom(s);\n const baseHue = (s * GOLDEN_ANGLE) % 360;\n const harmonyIndex = Math.floor(random() * HARMONY_TYPES.length);\n const harmony = HARMONY_TYPES[harmonyIndex];\n const hues = harmonyHues(baseHue, harmony);\n const colors = hues.map((hue) => {\n const saturation = 75 + random() * 25;\n const lightness = 50 + random() * 20;\n return hslToHex(hue, saturation, lightness);\n });\n return { seed: s, colors, harmony };\n}\n\n/**\n * Minimal Canvas2D context surface the renderer needs. Both\n * `HTMLCanvasElement` and `OffscreenCanvas` 2D contexts satisfy it.\n */\nexport type GradientContext = {\n fillStyle: string | CanvasGradient | CanvasPattern;\n globalCompositeOperation: GlobalCompositeOperation;\n fillRect(x: number, y: number, w: number, h: number): void;\n createRadialGradient(\n x0: number,\n y0: number,\n r0: number,\n x1: number,\n y1: number,\n r1: number,\n ): CanvasGradient;\n};\n\ninterface AnimatedSpot {\n x: number;\n y: number;\n radius: number;\n color: string;\n /** Unique phase offset (0–1) so each spot drifts independently. */\n phaseX: number;\n phaseY: number;\n /** How far the spot can drift from its origin (fraction of size). */\n amplitude: number;\n}\n\n/**\n * Draw the mesh gradient for `seed` into `ctx` at `size` x `size`.\n * The caller is responsible for any blur — apply `filter: blur(…)` on the\n * displayed canvas (≈6% of the rendered dimension) for the signature look,\n * or use {@link renderGradient} / {@link gradientToDataURL} which bake it in.\n */\nexport function drawMeshGradient(\n ctx: GradientContext,\n seed: number | string,\n size: number,\n): void {\n const s = toSeed(seed);\n const { colors } = generatePalette(s);\n const random = seededRandom(s * 12345);\n\n ctx.fillStyle = colors[0];\n ctx.fillRect(0, 0, size, size);\n\n const numSpots = 8 + Math.floor(random() * 5);\n const spots: Array<{ x: number; y: number; radius: number; color: string }> =\n [];\n\n for (let i = 0; i < numSpots; i++) {\n const angle = random() * Math.PI * 2;\n const distance = random() * size * 0.4;\n const centerX = size / 2 + Math.cos(angle) * distance;\n const centerY = size / 2 + Math.sin(angle) * distance;\n spots.push({\n x: centerX + (random() - 0.5) * size * 0.3,\n y: centerY + (random() - 0.5) * size * 0.3,\n radius: size * (0.3 + random() * 0.4),\n color: colors[i % colors.length],\n });\n }\n\n spots.sort((a, b) => b.radius - a.radius);\n\n ctx.globalCompositeOperation = \"source-over\";\n for (const spot of spots) {\n const g = ctx.createRadialGradient(\n spot.x,\n spot.y,\n 0,\n spot.x,\n spot.y,\n spot.radius,\n );\n g.addColorStop(0, `${spot.color}FF`);\n g.addColorStop(0.3, `${spot.color}DD`);\n g.addColorStop(0.6, `${spot.color}88`);\n g.addColorStop(1, `${spot.color}00`);\n ctx.fillStyle = g;\n ctx.fillRect(0, 0, size, size);\n }\n\n const hx = size * 0.3 + random() * size * 0.2;\n const hy = size * 0.3 + random() * size * 0.2;\n const hg = ctx.createRadialGradient(hx, hy, 0, hx, hy, size * 0.3);\n hg.addColorStop(0, \"rgba(255,255,255,0.15)\");\n hg.addColorStop(1, \"rgba(255,255,255,0)\");\n ctx.fillStyle = hg;\n ctx.fillRect(0, 0, size, size);\n}\n\n/**\n * Compute animated spot data for a seed — call once, then pass the result\n * and a changing `time` (elapsed seconds) to {@link drawAnimatedSpots}.\n * This separates setup from the per-frame hot path so the palette and\n * random geometry are only derived once.\n */\nexport function createAnimatedSpots(\n seed: number | string,\n size: number,\n): AnimatedSpot[] {\n const s = toSeed(seed);\n const { colors } = generatePalette(s);\n const random = seededRandom(s * 12345);\n\n const numSpots = 8 + Math.floor(random() * 5);\n const spots: AnimatedSpot[] = [];\n\n for (let i = 0; i < numSpots; i++) {\n const angle = random() * Math.PI * 2;\n const distance = random() * size * 0.4;\n const centerX = size / 2 + Math.cos(angle) * distance;\n const centerY = size / 2 + Math.sin(angle) * distance;\n spots.push({\n x: centerX + (random() - 0.5) * size * 0.3,\n y: centerY + (random() - 0.5) * size * 0.3,\n radius: size * (0.3 + random() * 0.4),\n color: colors[i % colors.length],\n phaseX: random(),\n phaseY: random(),\n amplitude: 0.03 + random() * 0.06,\n });\n }\n\n spots.sort((a, b) => b.radius - a.radius);\n return spots;\n}\n\n/**\n * Draw animated mesh spots into `ctx`. Call on every frame with a monotonically\n * increasing `time` (seconds). Each spot orbits its anchor on a tiny Lissajous\n * path, scaled so the composite result always covers the canvas.\n */\nexport function drawAnimatedSpots(\n ctx: GradientContext,\n spots: AnimatedSpot[],\n backgroundColor: string,\n size: number,\n time: number,\n): void {\n ctx.fillStyle = backgroundColor;\n ctx.fillRect(0, 0, size, size);\n\n ctx.globalCompositeOperation = \"source-over\";\n for (const spot of spots) {\n // Lissajous drift: each spot moves on its own tiny elliptical path.\n // Different periods per axis keep it from looking like a uniform rotation.\n const periodX = 12 + spot.phaseX * 8;\n const periodY = 14 + spot.phaseY * 10;\n const drift = spot.amplitude * size;\n const sx =\n spot.x +\n Math.sin(time * ((2 * Math.PI) / periodX) + spot.phaseX * Math.PI * 2) *\n drift;\n const sy =\n spot.y +\n Math.cos(time * ((2 * Math.PI) / periodY) + spot.phaseY * Math.PI * 2) *\n drift;\n\n const g = ctx.createRadialGradient(sx, sy, 0, sx, sy, spot.radius);\n g.addColorStop(0, `${spot.color}FF`);\n g.addColorStop(0.3, `${spot.color}DD`);\n g.addColorStop(0.6, `${spot.color}88`);\n g.addColorStop(1, `${spot.color}00`);\n ctx.fillStyle = g;\n ctx.fillRect(0, 0, size, size);\n }\n\n // Subtle highlight that drifts with time.\n const hSize = size * 0.3;\n const hx = size * 0.35 + Math.sin(time * 0.3) * size * 0.1;\n const hy = size * 0.35 + Math.cos(time * 0.4) * size * 0.1;\n const hg = ctx.createRadialGradient(hx, hy, 0, hx, hy, hSize);\n hg.addColorStop(0, \"rgba(255,255,255,0.15)\");\n hg.addColorStop(1, \"rgba(255,255,255,0)\");\n ctx.fillStyle = hg;\n ctx.fillRect(0, 0, size, size);\n}\n\nexport interface RenderOptions {\n /**\n * Blur radius in pixels. Defaults to ~6% of the canvas size for the\n * signature soft look. Pass `0` to disable.\n */\n blur?: number;\n}\n\nfunction blurFor(size: number, blur?: number): number {\n if (blur === 0) return 0;\n return blur ?? Math.round(size * DEFAULT_BLUR_FRACTION);\n}\n\n/**\n * Render a seed's gradient into an existing canvas, baking in the soft blur.\n * Draws at the canvas's current `width`/`height`. Browser/OffscreenCanvas only.\n */\nexport function renderGradient(\n canvas: HTMLCanvasElement | OffscreenCanvas,\n seed: number | string,\n options: RenderOptions = {},\n): void {\n const size = canvas.width;\n const blur = blurFor(size, options.blur);\n\n const ctx = canvas.getContext(\"2d\") as CanvasRenderingContext2D | null;\n if (!ctx) return;\n\n if (blur <= 0) {\n ctx.clearRect(0, 0, size, size);\n drawMeshGradient(ctx, seed, size);\n return;\n }\n\n // Draw the raw mesh on a scratch canvas, then composite it back with blur\n // scaled up slightly so the soft edges fall outside the frame (no ring).\n const scratch = createCanvas(size, size);\n const sctx = scratch.getContext(\"2d\") as CanvasRenderingContext2D | null;\n if (!sctx) return;\n drawMeshGradient(sctx, seed, size);\n\n const scaleUp = 1 + (blur / size) * 4;\n const dw = size * scaleUp;\n const offset = (dw - size) / 2;\n ctx.clearRect(0, 0, size, size);\n if (supportsCanvasFilter()) {\n ctx.filter = `blur(${blur}px)`;\n ctx.drawImage(scratch as CanvasImageSource, -offset, -offset, dw, dw);\n ctx.filter = \"none\";\n return;\n }\n // 2D-canvas `filter` is a silent no-op on Safari < 17: approximate the\n // gaussian by bouncing through a small canvas. Bilinear resampling on the\n // way down and back up smears by roughly the downscale factor, which is\n // plenty for a mesh that is already smooth gradients.\n const factor = Math.max(2, Math.min(16, blur / 2));\n const sw = Math.max(1, Math.round(size / factor));\n const small = createCanvas(sw, sw);\n const smallCtx = small.getContext(\"2d\") as CanvasRenderingContext2D | null;\n if (!smallCtx) {\n ctx.drawImage(scratch as CanvasImageSource, -offset, -offset, dw, dw);\n return;\n }\n smallCtx.imageSmoothingEnabled = true;\n smallCtx.imageSmoothingQuality = \"high\";\n smallCtx.drawImage(scratch as CanvasImageSource, 0, 0, sw, sw);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = \"high\";\n ctx.drawImage(small as CanvasImageSource, -offset, -offset, dw, dw);\n}\n\n/* Engines that honor 2D-canvas `filter` echo an assigned value back from the\n * property; Safari < 17 ignores the assignment. Probed once, then cached. */\nlet canvasFilterSupport: boolean | null = null;\nfunction supportsCanvasFilter(): boolean {\n if (canvasFilterSupport !== null) return canvasFilterSupport;\n const probe = createCanvas(1, 1).getContext(\n \"2d\",\n ) as CanvasRenderingContext2D | null;\n if (!probe) {\n canvasFilterSupport = false;\n return canvasFilterSupport;\n }\n probe.filter = \"blur(1px)\";\n canvasFilterSupport = probe.filter === \"blur(1px)\";\n return canvasFilterSupport;\n}\n\nfunction createCanvas(\n w: number,\n h: number,\n): HTMLCanvasElement | OffscreenCanvas {\n if (typeof OffscreenCanvas !== \"undefined\") {\n return new OffscreenCanvas(w, h);\n }\n const c = document.createElement(\"canvas\");\n c.width = w;\n c.height = h;\n return c;\n}\n\nexport interface ExportOptions extends RenderOptions {\n /** Output pixel dimensions (square). Default: 512. */\n size?: number;\n /** Image MIME type. Default: \"image/png\". */\n type?: string;\n /** Quality 0–1 for lossy types. Default: 0.92. */\n quality?: number;\n}\n\n/** Render a seed's gradient and return it as a data URL. Browser only. */\nexport function gradientToDataURL(\n seed: number | string,\n options: ExportOptions = {},\n): string {\n const { size = 512, type = \"image/png\", quality = 0.92 } = options;\n const canvas = document.createElement(\"canvas\");\n canvas.width = size;\n canvas.height = size;\n renderGradient(canvas, seed, options);\n return canvas.toDataURL(type, quality);\n}\n\n/** Render a seed's gradient and resolve a Blob (or null). Browser only. */\nexport function gradientToBlob(\n seed: number | string,\n options: ExportOptions = {},\n): Promise<Blob | null> {\n const { size = 512, type = \"image/png\", quality = 0.92 } = options;\n const canvas = document.createElement(\"canvas\");\n canvas.width = size;\n canvas.height = size;\n renderGradient(canvas, seed, options);\n return new Promise((resolve) => canvas.toBlob(resolve, type, quality));\n}\n","import type { CSSProperties } from \"react\";\nimport { useCallback, useEffect, useRef } from \"react\";\nimport {\n createAnimatedSpots,\n drawAnimatedSpots,\n drawMeshGradient,\n generatePalette,\n} from \"./engine\";\n\nexport interface GradientAvatarProps {\n /** Any string or number — each unique seed produces a unique gradient. */\n seed: number | string;\n /** Rendered size in pixels. Default: 32. */\n size?: number;\n /**\n * Corner radius. Number = pixels, string = any CSS length.\n * Defaults to a full circle; pass `0` for a square or e.g. `12` for a\n * rounded square. Default: \"9999px\".\n */\n radius?: number | string;\n /**\n * Animate the gradient with a hybrid approach: CSS keyframes handle the\n * smooth drift (GPU-only, zero JS per frame), and the canvas content is\n * repainted at a low cadence (~15 fps) so the mesh spots evolve slowly.\n * A single shared rAF loop drives all animated instances; the ticker stops\n * when nothing is on screen or the tab is hidden. Automatically disabled\n * for users who request reduced motion. Default: `false`.\n */\n animate?: boolean;\n /**\n * Duration of one full drift cycle in seconds. Slower is subtler and\n * cheaper. Only applies when {@link animate} is `true`. Default: `16`.\n */\n animationDuration?: number;\n /** Additional CSS classes on the wrapper. */\n className?: string;\n /** Extra inline styles merged onto the wrapper. */\n style?: CSSProperties;\n}\n\n/* ------------------------------------------------------------------ */\n/* Shared animation infrastructure (module-level singletons) */\n/* ------------------------------------------------------------------ */\n\n/** Internal render resolution. */\nconst RENDER_SIZE = 256;\n/** Blur radius as a fraction of display size. */\nconst BLUR_FRACTION = 0.06;\n/** Canvas redraw interval (ms). ~15 fps keeps overhead very low. */\nconst REDRAW_MS = 67;\n\ntype DrawFn = () => void;\n\n/** Subscribers to the shared animation ticker. */\nconst subscribers = new Set<DrawFn>();\nlet tickerRaf: number | null = null;\nlet tickerLast = 0;\nlet tickerPaused = false;\n\nfunction tick(now: number): void {\n tickerRaf = requestAnimationFrame(tick);\n if (tickerPaused) return;\n if (now - tickerLast < REDRAW_MS) return;\n tickerLast = now;\n for (const draw of subscribers) draw();\n}\n\nfunction subscribe(draw: DrawFn): void {\n subscribers.add(draw);\n if (subscribers.size === 1 && tickerRaf === null) {\n tickerLast = 0;\n tickerRaf = requestAnimationFrame(tick);\n }\n}\n\nfunction unsubscribe(draw: DrawFn): void {\n subscribers.delete(draw);\n if (subscribers.size === 0 && tickerRaf !== null) {\n cancelAnimationFrame(tickerRaf);\n tickerRaf = null;\n }\n}\n\n/* Shared IntersectionObserver so we don't create one per avatar. */\nlet io: IntersectionObserver | null = null;\nconst visibles = new Set<() => void>();\n\nfunction ensureIntersectionObserver(): IntersectionObserver {\n if (io) return io;\n io = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n const el = entry.target as HTMLElement & { __oaOnVis?: () => void };\n el.__oaOnVis?.();\n }\n },\n { threshold: 0 },\n );\n return io;\n}\n\n/* Shared visibilitychange handler. */\nlet visHandlerInstalled = false;\nfunction ensureVisibilityHandler(): void {\n if (visHandlerInstalled || typeof document === \"undefined\") return;\n visHandlerInstalled = true;\n document.addEventListener(\"visibilitychange\", () => {\n tickerPaused = document.visibilityState === \"hidden\";\n if (!tickerPaused) tickerLast = 0;\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* CSS keyframes (injected once, lazily) */\n/* ------------------------------------------------------------------ */\n\nconst ANIMATED_CLASS = \"oa-avatar-canvas--animated\";\nconst STYLE_ID = \"oa-avatar-keyframes\";\n\nconst KEYFRAMES_CSS = `\n@keyframes oa-avatar-drift {\n\t0% { transform: scale(1.25) translate(0%, 0%) rotate(0deg); }\n\t20% { transform: scale(1.30) translate(-3%, 2%) rotate(8deg); }\n\t40% { transform: scale(1.22) translate(3%, 3%) rotate(-6deg); }\n\t60% { transform: scale(1.28) translate(2%, -3%) rotate(5deg); }\n\t80% { transform: scale(1.24) translate(-2%, -2%) rotate(-4deg); }\n\t100% { transform: scale(1.25) translate(0%, 0%) rotate(0deg); }\n}\n.${ANIMATED_CLASS} {\n\ttransform-origin: center;\n\twill-change: transform;\n\tanimation-name: oa-avatar-drift;\n\tanimation-timing-function: ease-in-out;\n\tanimation-iteration-count: infinite;\n\tanimation-direction: alternate;\n}\n@media (prefers-reduced-motion: reduce) {\n\t.${ANIMATED_CLASS} {\n\t\tanimation: none;\n\t\ttransform: none;\n\t\twill-change: auto;\n\t}\n}\n`;\n\nlet stylesInjected = false;\n\nfunction ensureAnimationStyles(): void {\n if (stylesInjected || typeof document === \"undefined\") return;\n if (document.getElementById(STYLE_ID)) {\n stylesInjected = true;\n return;\n }\n const el = document.createElement(\"style\");\n el.id = STYLE_ID;\n el.textContent = KEYFRAMES_CSS;\n document.head.appendChild(el);\n stylesInjected = true;\n}\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\n/**\n * Renders a deterministic mesh-gradient avatar on a `<canvas>`.\n * The same seed always produces the same gradient.\n *\n * When `animate` is on the gradient comes alive via two complementary\n * mechanisms:\n *\n * 1. **CSS keyframes** — a slow scale+rotate+translate drift that runs\n * entirely on the GPU compositor thread (zero JavaScript, zero repaint).\n * 2. **Low-cadence canvas repaint** — the mesh spots evolve at ~15 fps\n * through a single shared `requestAnimationFrame` loop, so N animated\n * avatars cost 1 rAF callback total. Drawing pauses when the element is\n * off-screen (`IntersectionObserver`) or the tab is hidden.\n */\nexport function GradientAvatar({\n seed,\n size = 32,\n radius = \"9999px\",\n animate = false,\n animationDuration = 16,\n className,\n style,\n}: GradientAvatarProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const wrapperRef = useRef<HTMLSpanElement>(null);\n const startRef = useRef<number>(0);\n const visibleRef = useRef(true);\n\n /* Inject CSS keyframes once (client-only). */\n useEffect(() => {\n if (animate) {\n ensureAnimationStyles();\n ensureVisibilityHandler();\n }\n }, [animate]);\n\n /* Draw loop: subscribe to the shared ticker, unsubscribe on cleanup. */\n const draw = useCallback(() => {\n const canvas = canvasRef.current;\n if (!canvas || !visibleRef.current) return;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n if (startRef.current === 0) startRef.current = performance.now();\n const elapsed = (performance.now() - startRef.current) / 1000;\n const time = elapsed * ((2 * Math.PI) / animationDuration);\n\n const { colors } = generatePalette(seed);\n const spots = createAnimatedSpots(seed, RENDER_SIZE);\n drawAnimatedSpots(ctx, spots, colors[0], RENDER_SIZE, time);\n }, [seed, animationDuration]);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n const prefersReduced =\n typeof window !== \"undefined\" &&\n window.matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches;\n\n if (!animate || prefersReduced) {\n ctx.clearRect(0, 0, RENDER_SIZE, RENDER_SIZE);\n drawMeshGradient(ctx, seed, RENDER_SIZE);\n return;\n }\n\n /* Paint the initial frame. */\n startRef.current = 0;\n const { colors } = generatePalette(seed);\n const spots = createAnimatedSpots(seed, RENDER_SIZE);\n drawAnimatedSpots(ctx, spots, colors[0], RENDER_SIZE, 0);\n\n subscribe(draw);\n\n return () => {\n unsubscribe(draw);\n };\n }, [seed, animate, draw]);\n\n /* IntersectionObserver: pause redraws when the element scrolls off screen. */\n useEffect(() => {\n const wrapper = wrapperRef.current;\n if (!animate || !wrapper) return;\n\n const observer = ensureIntersectionObserver();\n // biome-ignore lint/suspicious/noAssignInExpressions: intentional pattern for attaching per-element callbacks\n (wrapper as HTMLElement & { __oaOnVis?: () => void }).__oaOnVis = () => {\n visibleRef.current = true;\n };\n observer.observe(wrapper);\n\n return () => {\n observer.unobserve(wrapper);\n visibleRef.current = false;\n delete (wrapper as HTMLElement & { __oaOnVis?: () => void }).__oaOnVis;\n };\n }, [animate]);\n\n const blurPx = Math.max(1, Math.round(size * BLUR_FRACTION));\n\n /* Canvas is always scaled 1.25× so drifting spots never expose the edge;\n * the CSS keyframes add their own scale+rotate+translate on top (class). */\n const canvasStyle: CSSProperties = {\n width: \"100%\",\n height: \"100%\",\n display: \"block\",\n filter: `blur(${blurPx}px)`,\n transform: \"scale(1.25)\",\n };\n if (animate) {\n canvasStyle.animationDuration = `${animationDuration}s`;\n }\n\n return (\n <span\n ref={wrapperRef}\n className={className}\n style={{\n display: \"inline-block\",\n overflow: \"hidden\",\n borderRadius: radius,\n width: size,\n height: size,\n ...style,\n }}\n >\n <canvas\n ref={canvasRef}\n role=\"img\"\n aria-label={`Gradient avatar for seed ${seed}`}\n width={RENDER_SIZE}\n height={RENDER_SIZE}\n className={animate ? ANIMATED_CLASS : undefined}\n style={canvasStyle}\n />\n </span>\n );\n}\n\nexport type {\n ExportOptions,\n GradientPalette,\n Harmony,\n RenderOptions,\n} from \"./engine\";\nexport {\n createAnimatedSpots,\n drawAnimatedSpots,\n drawMeshGradient,\n generatePalette,\n gradientToBlob,\n gradientToDataURL,\n renderGradient,\n seedFromString,\n toSeed,\n} from \"./engine\";\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -36,6 +36,17 @@ type GradientContext = {
|
|
|
36
36
|
fillRect(x: number, y: number, w: number, h: number): void;
|
|
37
37
|
createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): CanvasGradient;
|
|
38
38
|
};
|
|
39
|
+
interface AnimatedSpot {
|
|
40
|
+
x: number;
|
|
41
|
+
y: number;
|
|
42
|
+
radius: number;
|
|
43
|
+
color: string;
|
|
44
|
+
/** Unique phase offset (0–1) so each spot drifts independently. */
|
|
45
|
+
phaseX: number;
|
|
46
|
+
phaseY: number;
|
|
47
|
+
/** How far the spot can drift from its origin (fraction of size). */
|
|
48
|
+
amplitude: number;
|
|
49
|
+
}
|
|
39
50
|
/**
|
|
40
51
|
* Draw the mesh gradient for `seed` into `ctx` at `size` x `size`.
|
|
41
52
|
* The caller is responsible for any blur — apply `filter: blur(…)` on the
|
|
@@ -43,6 +54,19 @@ type GradientContext = {
|
|
|
43
54
|
* or use {@link renderGradient} / {@link gradientToDataURL} which bake it in.
|
|
44
55
|
*/
|
|
45
56
|
declare function drawMeshGradient(ctx: GradientContext, seed: number | string, size: number): void;
|
|
57
|
+
/**
|
|
58
|
+
* Compute animated spot data for a seed — call once, then pass the result
|
|
59
|
+
* and a changing `time` (elapsed seconds) to {@link drawAnimatedSpots}.
|
|
60
|
+
* This separates setup from the per-frame hot path so the palette and
|
|
61
|
+
* random geometry are only derived once.
|
|
62
|
+
*/
|
|
63
|
+
declare function createAnimatedSpots(seed: number | string, size: number): AnimatedSpot[];
|
|
64
|
+
/**
|
|
65
|
+
* Draw animated mesh spots into `ctx`. Call on every frame with a monotonically
|
|
66
|
+
* increasing `time` (seconds). Each spot orbits its anchor on a tiny Lissajous
|
|
67
|
+
* path, scaled so the composite result always covers the canvas.
|
|
68
|
+
*/
|
|
69
|
+
declare function drawAnimatedSpots(ctx: GradientContext, spots: AnimatedSpot[], backgroundColor: string, size: number, time: number): void;
|
|
46
70
|
interface RenderOptions {
|
|
47
71
|
/**
|
|
48
72
|
* Blur radius in pixels. Defaults to ~6% of the canvas size for the
|
|
@@ -80,11 +104,12 @@ interface GradientAvatarProps {
|
|
|
80
104
|
*/
|
|
81
105
|
radius?: number | string;
|
|
82
106
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
107
|
+
* Animate the gradient with a hybrid approach: CSS keyframes handle the
|
|
108
|
+
* smooth drift (GPU-only, zero JS per frame), and the canvas content is
|
|
109
|
+
* repainted at a low cadence (~15 fps) so the mesh spots evolve slowly.
|
|
110
|
+
* A single shared rAF loop drives all animated instances; the ticker stops
|
|
111
|
+
* when nothing is on screen or the tab is hidden. Automatically disabled
|
|
112
|
+
* for users who request reduced motion. Default: `false`.
|
|
88
113
|
*/
|
|
89
114
|
animate?: boolean;
|
|
90
115
|
/**
|
|
@@ -100,7 +125,17 @@ interface GradientAvatarProps {
|
|
|
100
125
|
/**
|
|
101
126
|
* Renders a deterministic mesh-gradient avatar on a `<canvas>`.
|
|
102
127
|
* The same seed always produces the same gradient.
|
|
128
|
+
*
|
|
129
|
+
* When `animate` is on the gradient comes alive via two complementary
|
|
130
|
+
* mechanisms:
|
|
131
|
+
*
|
|
132
|
+
* 1. **CSS keyframes** — a slow scale+rotate+translate drift that runs
|
|
133
|
+
* entirely on the GPU compositor thread (zero JavaScript, zero repaint).
|
|
134
|
+
* 2. **Low-cadence canvas repaint** — the mesh spots evolve at ~15 fps
|
|
135
|
+
* through a single shared `requestAnimationFrame` loop, so N animated
|
|
136
|
+
* avatars cost 1 rAF callback total. Drawing pauses when the element is
|
|
137
|
+
* off-screen (`IntersectionObserver`) or the tab is hidden.
|
|
103
138
|
*/
|
|
104
139
|
declare function GradientAvatar({ seed, size, radius, animate, animationDuration, className, style, }: GradientAvatarProps): react_jsx_runtime.JSX.Element;
|
|
105
140
|
|
|
106
|
-
export { type ExportOptions, GradientAvatar, type GradientAvatarProps, type GradientPalette, type Harmony, type RenderOptions, drawMeshGradient, generatePalette, gradientToBlob, gradientToDataURL, renderGradient, seedFromString, toSeed };
|
|
141
|
+
export { type ExportOptions, GradientAvatar, type GradientAvatarProps, type GradientPalette, type Harmony, type RenderOptions, createAnimatedSpots, drawAnimatedSpots, drawMeshGradient, generatePalette, gradientToBlob, gradientToDataURL, renderGradient, seedFromString, toSeed };
|
package/dist/index.d.ts
CHANGED
|
@@ -36,6 +36,17 @@ type GradientContext = {
|
|
|
36
36
|
fillRect(x: number, y: number, w: number, h: number): void;
|
|
37
37
|
createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): CanvasGradient;
|
|
38
38
|
};
|
|
39
|
+
interface AnimatedSpot {
|
|
40
|
+
x: number;
|
|
41
|
+
y: number;
|
|
42
|
+
radius: number;
|
|
43
|
+
color: string;
|
|
44
|
+
/** Unique phase offset (0–1) so each spot drifts independently. */
|
|
45
|
+
phaseX: number;
|
|
46
|
+
phaseY: number;
|
|
47
|
+
/** How far the spot can drift from its origin (fraction of size). */
|
|
48
|
+
amplitude: number;
|
|
49
|
+
}
|
|
39
50
|
/**
|
|
40
51
|
* Draw the mesh gradient for `seed` into `ctx` at `size` x `size`.
|
|
41
52
|
* The caller is responsible for any blur — apply `filter: blur(…)` on the
|
|
@@ -43,6 +54,19 @@ type GradientContext = {
|
|
|
43
54
|
* or use {@link renderGradient} / {@link gradientToDataURL} which bake it in.
|
|
44
55
|
*/
|
|
45
56
|
declare function drawMeshGradient(ctx: GradientContext, seed: number | string, size: number): void;
|
|
57
|
+
/**
|
|
58
|
+
* Compute animated spot data for a seed — call once, then pass the result
|
|
59
|
+
* and a changing `time` (elapsed seconds) to {@link drawAnimatedSpots}.
|
|
60
|
+
* This separates setup from the per-frame hot path so the palette and
|
|
61
|
+
* random geometry are only derived once.
|
|
62
|
+
*/
|
|
63
|
+
declare function createAnimatedSpots(seed: number | string, size: number): AnimatedSpot[];
|
|
64
|
+
/**
|
|
65
|
+
* Draw animated mesh spots into `ctx`. Call on every frame with a monotonically
|
|
66
|
+
* increasing `time` (seconds). Each spot orbits its anchor on a tiny Lissajous
|
|
67
|
+
* path, scaled so the composite result always covers the canvas.
|
|
68
|
+
*/
|
|
69
|
+
declare function drawAnimatedSpots(ctx: GradientContext, spots: AnimatedSpot[], backgroundColor: string, size: number, time: number): void;
|
|
46
70
|
interface RenderOptions {
|
|
47
71
|
/**
|
|
48
72
|
* Blur radius in pixels. Defaults to ~6% of the canvas size for the
|
|
@@ -80,11 +104,12 @@ interface GradientAvatarProps {
|
|
|
80
104
|
*/
|
|
81
105
|
radius?: number | string;
|
|
82
106
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
107
|
+
* Animate the gradient with a hybrid approach: CSS keyframes handle the
|
|
108
|
+
* smooth drift (GPU-only, zero JS per frame), and the canvas content is
|
|
109
|
+
* repainted at a low cadence (~15 fps) so the mesh spots evolve slowly.
|
|
110
|
+
* A single shared rAF loop drives all animated instances; the ticker stops
|
|
111
|
+
* when nothing is on screen or the tab is hidden. Automatically disabled
|
|
112
|
+
* for users who request reduced motion. Default: `false`.
|
|
88
113
|
*/
|
|
89
114
|
animate?: boolean;
|
|
90
115
|
/**
|
|
@@ -100,7 +125,17 @@ interface GradientAvatarProps {
|
|
|
100
125
|
/**
|
|
101
126
|
* Renders a deterministic mesh-gradient avatar on a `<canvas>`.
|
|
102
127
|
* The same seed always produces the same gradient.
|
|
128
|
+
*
|
|
129
|
+
* When `animate` is on the gradient comes alive via two complementary
|
|
130
|
+
* mechanisms:
|
|
131
|
+
*
|
|
132
|
+
* 1. **CSS keyframes** — a slow scale+rotate+translate drift that runs
|
|
133
|
+
* entirely on the GPU compositor thread (zero JavaScript, zero repaint).
|
|
134
|
+
* 2. **Low-cadence canvas repaint** — the mesh spots evolve at ~15 fps
|
|
135
|
+
* through a single shared `requestAnimationFrame` loop, so N animated
|
|
136
|
+
* avatars cost 1 rAF callback total. Drawing pauses when the element is
|
|
137
|
+
* off-screen (`IntersectionObserver`) or the tab is hidden.
|
|
103
138
|
*/
|
|
104
139
|
declare function GradientAvatar({ seed, size, radius, animate, animationDuration, className, style, }: GradientAvatarProps): react_jsx_runtime.JSX.Element;
|
|
105
140
|
|
|
106
|
-
export { type ExportOptions, GradientAvatar, type GradientAvatarProps, type GradientPalette, type Harmony, type RenderOptions, drawMeshGradient, generatePalette, gradientToBlob, gradientToDataURL, renderGradient, seedFromString, toSeed };
|
|
141
|
+
export { type ExportOptions, GradientAvatar, type GradientAvatarProps, type GradientPalette, type Harmony, type RenderOptions, createAnimatedSpots, drawAnimatedSpots, drawMeshGradient, generatePalette, gradientToBlob, gradientToDataURL, renderGradient, seedFromString, toSeed };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useRef, useEffect } from 'react';
|
|
1
|
+
import { useRef, useEffect, useCallback } from 'react';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
// src/index.tsx
|
|
@@ -149,6 +149,57 @@ function drawMeshGradient(ctx, seed, size) {
|
|
|
149
149
|
ctx.fillStyle = hg;
|
|
150
150
|
ctx.fillRect(0, 0, size, size);
|
|
151
151
|
}
|
|
152
|
+
function createAnimatedSpots(seed, size) {
|
|
153
|
+
const s = toSeed(seed);
|
|
154
|
+
const { colors } = generatePalette(s);
|
|
155
|
+
const random = seededRandom(s * 12345);
|
|
156
|
+
const numSpots = 8 + Math.floor(random() * 5);
|
|
157
|
+
const spots = [];
|
|
158
|
+
for (let i = 0; i < numSpots; i++) {
|
|
159
|
+
const angle = random() * Math.PI * 2;
|
|
160
|
+
const distance = random() * size * 0.4;
|
|
161
|
+
const centerX = size / 2 + Math.cos(angle) * distance;
|
|
162
|
+
const centerY = size / 2 + Math.sin(angle) * distance;
|
|
163
|
+
spots.push({
|
|
164
|
+
x: centerX + (random() - 0.5) * size * 0.3,
|
|
165
|
+
y: centerY + (random() - 0.5) * size * 0.3,
|
|
166
|
+
radius: size * (0.3 + random() * 0.4),
|
|
167
|
+
color: colors[i % colors.length],
|
|
168
|
+
phaseX: random(),
|
|
169
|
+
phaseY: random(),
|
|
170
|
+
amplitude: 0.03 + random() * 0.06
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
spots.sort((a, b) => b.radius - a.radius);
|
|
174
|
+
return spots;
|
|
175
|
+
}
|
|
176
|
+
function drawAnimatedSpots(ctx, spots, backgroundColor, size, time) {
|
|
177
|
+
ctx.fillStyle = backgroundColor;
|
|
178
|
+
ctx.fillRect(0, 0, size, size);
|
|
179
|
+
ctx.globalCompositeOperation = "source-over";
|
|
180
|
+
for (const spot of spots) {
|
|
181
|
+
const periodX = 12 + spot.phaseX * 8;
|
|
182
|
+
const periodY = 14 + spot.phaseY * 10;
|
|
183
|
+
const drift = spot.amplitude * size;
|
|
184
|
+
const sx = spot.x + Math.sin(time * (2 * Math.PI / periodX) + spot.phaseX * Math.PI * 2) * drift;
|
|
185
|
+
const sy = spot.y + Math.cos(time * (2 * Math.PI / periodY) + spot.phaseY * Math.PI * 2) * drift;
|
|
186
|
+
const g = ctx.createRadialGradient(sx, sy, 0, sx, sy, spot.radius);
|
|
187
|
+
g.addColorStop(0, `${spot.color}FF`);
|
|
188
|
+
g.addColorStop(0.3, `${spot.color}DD`);
|
|
189
|
+
g.addColorStop(0.6, `${spot.color}88`);
|
|
190
|
+
g.addColorStop(1, `${spot.color}00`);
|
|
191
|
+
ctx.fillStyle = g;
|
|
192
|
+
ctx.fillRect(0, 0, size, size);
|
|
193
|
+
}
|
|
194
|
+
const hSize = size * 0.3;
|
|
195
|
+
const hx = size * 0.35 + Math.sin(time * 0.3) * size * 0.1;
|
|
196
|
+
const hy = size * 0.35 + Math.cos(time * 0.4) * size * 0.1;
|
|
197
|
+
const hg = ctx.createRadialGradient(hx, hy, 0, hx, hy, hSize);
|
|
198
|
+
hg.addColorStop(0, "rgba(255,255,255,0.15)");
|
|
199
|
+
hg.addColorStop(1, "rgba(255,255,255,0)");
|
|
200
|
+
ctx.fillStyle = hg;
|
|
201
|
+
ctx.fillRect(0, 0, size, size);
|
|
202
|
+
}
|
|
152
203
|
function blurFor(size, blur) {
|
|
153
204
|
if (blur === 0) return 0;
|
|
154
205
|
return blur ?? Math.round(size * DEFAULT_BLUR_FRACTION);
|
|
@@ -233,8 +284,57 @@ function gradientToBlob(seed, options = {}) {
|
|
|
233
284
|
}
|
|
234
285
|
var RENDER_SIZE = 256;
|
|
235
286
|
var BLUR_FRACTION = 0.06;
|
|
287
|
+
var REDRAW_MS = 67;
|
|
288
|
+
var subscribers = /* @__PURE__ */ new Set();
|
|
289
|
+
var tickerRaf = null;
|
|
290
|
+
var tickerLast = 0;
|
|
291
|
+
var tickerPaused = false;
|
|
292
|
+
function tick(now) {
|
|
293
|
+
tickerRaf = requestAnimationFrame(tick);
|
|
294
|
+
if (tickerPaused) return;
|
|
295
|
+
if (now - tickerLast < REDRAW_MS) return;
|
|
296
|
+
tickerLast = now;
|
|
297
|
+
for (const draw of subscribers) draw();
|
|
298
|
+
}
|
|
299
|
+
function subscribe(draw) {
|
|
300
|
+
subscribers.add(draw);
|
|
301
|
+
if (subscribers.size === 1 && tickerRaf === null) {
|
|
302
|
+
tickerLast = 0;
|
|
303
|
+
tickerRaf = requestAnimationFrame(tick);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function unsubscribe(draw) {
|
|
307
|
+
subscribers.delete(draw);
|
|
308
|
+
if (subscribers.size === 0 && tickerRaf !== null) {
|
|
309
|
+
cancelAnimationFrame(tickerRaf);
|
|
310
|
+
tickerRaf = null;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
var io = null;
|
|
314
|
+
function ensureIntersectionObserver() {
|
|
315
|
+
if (io) return io;
|
|
316
|
+
io = new IntersectionObserver(
|
|
317
|
+
(entries) => {
|
|
318
|
+
for (const entry of entries) {
|
|
319
|
+
const el = entry.target;
|
|
320
|
+
el.__oaOnVis?.();
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
{ threshold: 0 }
|
|
324
|
+
);
|
|
325
|
+
return io;
|
|
326
|
+
}
|
|
327
|
+
var visHandlerInstalled = false;
|
|
328
|
+
function ensureVisibilityHandler() {
|
|
329
|
+
if (visHandlerInstalled || typeof document === "undefined") return;
|
|
330
|
+
visHandlerInstalled = true;
|
|
331
|
+
document.addEventListener("visibilitychange", () => {
|
|
332
|
+
tickerPaused = document.visibilityState === "hidden";
|
|
333
|
+
if (!tickerPaused) tickerLast = 0;
|
|
334
|
+
});
|
|
335
|
+
}
|
|
236
336
|
var ANIMATED_CLASS = "oa-avatar-canvas--animated";
|
|
237
|
-
var
|
|
337
|
+
var STYLE_ID = "oa-avatar-keyframes";
|
|
238
338
|
var KEYFRAMES_CSS = `
|
|
239
339
|
@keyframes oa-avatar-drift {
|
|
240
340
|
0% { transform: scale(1.25) translate(0%, 0%) rotate(0deg); }
|
|
@@ -263,12 +363,12 @@ var KEYFRAMES_CSS = `
|
|
|
263
363
|
var stylesInjected = false;
|
|
264
364
|
function ensureAnimationStyles() {
|
|
265
365
|
if (stylesInjected || typeof document === "undefined") return;
|
|
266
|
-
if (document.getElementById(
|
|
366
|
+
if (document.getElementById(STYLE_ID)) {
|
|
267
367
|
stylesInjected = true;
|
|
268
368
|
return;
|
|
269
369
|
}
|
|
270
370
|
const el = document.createElement("style");
|
|
271
|
-
el.id =
|
|
371
|
+
el.id = STYLE_ID;
|
|
272
372
|
el.textContent = KEYFRAMES_CSS;
|
|
273
373
|
document.head.appendChild(el);
|
|
274
374
|
stylesInjected = true;
|
|
@@ -283,23 +383,68 @@ function GradientAvatar({
|
|
|
283
383
|
style
|
|
284
384
|
}) {
|
|
285
385
|
const canvasRef = useRef(null);
|
|
386
|
+
const wrapperRef = useRef(null);
|
|
387
|
+
const startRef = useRef(0);
|
|
388
|
+
const visibleRef = useRef(true);
|
|
389
|
+
useEffect(() => {
|
|
390
|
+
if (animate) {
|
|
391
|
+
ensureAnimationStyles();
|
|
392
|
+
ensureVisibilityHandler();
|
|
393
|
+
}
|
|
394
|
+
}, [animate]);
|
|
395
|
+
const draw = useCallback(() => {
|
|
396
|
+
const canvas = canvasRef.current;
|
|
397
|
+
if (!canvas || !visibleRef.current) return;
|
|
398
|
+
const ctx = canvas.getContext("2d");
|
|
399
|
+
if (!ctx) return;
|
|
400
|
+
if (startRef.current === 0) startRef.current = performance.now();
|
|
401
|
+
const elapsed = (performance.now() - startRef.current) / 1e3;
|
|
402
|
+
const time = elapsed * (2 * Math.PI / animationDuration);
|
|
403
|
+
const { colors } = generatePalette(seed);
|
|
404
|
+
const spots = createAnimatedSpots(seed, RENDER_SIZE);
|
|
405
|
+
drawAnimatedSpots(ctx, spots, colors[0], RENDER_SIZE, time);
|
|
406
|
+
}, [seed, animationDuration]);
|
|
286
407
|
useEffect(() => {
|
|
287
408
|
const canvas = canvasRef.current;
|
|
288
409
|
if (!canvas) return;
|
|
289
410
|
const ctx = canvas.getContext("2d");
|
|
290
411
|
if (!ctx) return;
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
412
|
+
const prefersReduced = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
413
|
+
if (!animate || prefersReduced) {
|
|
414
|
+
ctx.clearRect(0, 0, RENDER_SIZE, RENDER_SIZE);
|
|
415
|
+
drawMeshGradient(ctx, seed, RENDER_SIZE);
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
startRef.current = 0;
|
|
419
|
+
const { colors } = generatePalette(seed);
|
|
420
|
+
const spots = createAnimatedSpots(seed, RENDER_SIZE);
|
|
421
|
+
drawAnimatedSpots(ctx, spots, colors[0], RENDER_SIZE, 0);
|
|
422
|
+
subscribe(draw);
|
|
423
|
+
return () => {
|
|
424
|
+
unsubscribe(draw);
|
|
425
|
+
};
|
|
426
|
+
}, [seed, animate, draw]);
|
|
294
427
|
useEffect(() => {
|
|
295
|
-
|
|
428
|
+
const wrapper = wrapperRef.current;
|
|
429
|
+
if (!animate || !wrapper) return;
|
|
430
|
+
const observer = ensureIntersectionObserver();
|
|
431
|
+
wrapper.__oaOnVis = () => {
|
|
432
|
+
visibleRef.current = true;
|
|
433
|
+
};
|
|
434
|
+
observer.observe(wrapper);
|
|
435
|
+
return () => {
|
|
436
|
+
observer.unobserve(wrapper);
|
|
437
|
+
visibleRef.current = false;
|
|
438
|
+
delete wrapper.__oaOnVis;
|
|
439
|
+
};
|
|
296
440
|
}, [animate]);
|
|
297
441
|
const blurPx = Math.max(1, Math.round(size * BLUR_FRACTION));
|
|
298
442
|
const canvasStyle = {
|
|
299
443
|
width: "100%",
|
|
300
444
|
height: "100%",
|
|
301
445
|
display: "block",
|
|
302
|
-
filter: `blur(${blurPx}px)
|
|
446
|
+
filter: `blur(${blurPx}px)`,
|
|
447
|
+
transform: "scale(1.25)"
|
|
303
448
|
};
|
|
304
449
|
if (animate) {
|
|
305
450
|
canvasStyle.animationDuration = `${animationDuration}s`;
|
|
@@ -307,6 +452,7 @@ function GradientAvatar({
|
|
|
307
452
|
return /* @__PURE__ */ jsx(
|
|
308
453
|
"span",
|
|
309
454
|
{
|
|
455
|
+
ref: wrapperRef,
|
|
310
456
|
className,
|
|
311
457
|
style: {
|
|
312
458
|
display: "inline-block",
|
|
@@ -320,6 +466,8 @@ function GradientAvatar({
|
|
|
320
466
|
"canvas",
|
|
321
467
|
{
|
|
322
468
|
ref: canvasRef,
|
|
469
|
+
role: "img",
|
|
470
|
+
"aria-label": `Gradient avatar for seed ${seed}`,
|
|
323
471
|
width: RENDER_SIZE,
|
|
324
472
|
height: RENDER_SIZE,
|
|
325
473
|
className: animate ? ANIMATED_CLASS : void 0,
|
|
@@ -330,6 +478,6 @@ function GradientAvatar({
|
|
|
330
478
|
);
|
|
331
479
|
}
|
|
332
480
|
|
|
333
|
-
export { GradientAvatar, drawMeshGradient, generatePalette, gradientToBlob, gradientToDataURL, renderGradient, seedFromString, toSeed };
|
|
481
|
+
export { GradientAvatar, createAnimatedSpots, drawAnimatedSpots, drawMeshGradient, generatePalette, gradientToBlob, gradientToDataURL, renderGradient, seedFromString, toSeed };
|
|
334
482
|
//# sourceMappingURL=index.js.map
|
|
335
483
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/engine.ts","../src/index.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Gradient engine for @apollo-deploy/avatars.\n *\n * Framework-agnostic mesh-gradient avatar generator. Every seed (string or\n * number) deterministically produces a unique gradient — no stored images,\n * no network. Pure palette/RNG core plus optional Canvas2D render helpers.\n */\n\nexport type Harmony =\n\t| \"analogous\"\n\t| \"triadic\"\n\t| \"splitComplementary\"\n\t| \"tetradic\"\n\t| \"complementary\";\n\nexport interface GradientPalette {\n\t/** The numeric seed the palette was derived from. */\n\tseed: number;\n\t/** Hex color stops used to paint the mesh. */\n\tcolors: string[];\n\t/** Which color-harmony rule produced the hues. */\n\tharmony: Harmony;\n}\n\nconst HARMONY_TYPES: Harmony[] = [\n\t\"analogous\",\n\t\"triadic\",\n\t\"splitComplementary\",\n\t\"tetradic\",\n\t\"complementary\",\n];\n\nconst GOLDEN_ANGLE = 137.5;\n\n/** Default blur radius as a fraction of the rendered dimension. */\nexport const DEFAULT_BLUR_FRACTION = 0.06;\n\nfunction seededRandom(seed: number): () => number {\n\tlet s = seed;\n\treturn () => {\n\t\ts += 0x6d2b79f5;\n\t\tlet t = s;\n\t\tt = Math.imul(t ^ (t >>> 15), t | 1);\n\t\tt ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n\t\treturn ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n\t};\n}\n\nfunction hslToHex(h: number, s: number, l: number): string {\n\th = ((h % 360) + 360) % 360;\n\ts = Math.max(0, Math.min(100, s)) / 100;\n\tl = Math.max(0, Math.min(100, l)) / 100;\n\n\tconst c = (1 - Math.abs(2 * l - 1)) * s;\n\tconst x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n\tconst m = l - c / 2;\n\n\tlet r = 0;\n\tlet g = 0;\n\tlet b = 0;\n\tif (h < 60) {\n\t\tr = c;\n\t\tg = x;\n\t} else if (h < 120) {\n\t\tr = x;\n\t\tg = c;\n\t} else if (h < 180) {\n\t\tg = c;\n\t\tb = x;\n\t} else if (h < 240) {\n\t\tg = x;\n\t\tb = c;\n\t} else if (h < 300) {\n\t\tr = x;\n\t\tb = c;\n\t} else {\n\t\tr = c;\n\t\tb = x;\n\t}\n\n\tconst toHex = (n: number) => {\n\t\tconst hex = Math.round((n + m) * 255).toString(16);\n\t\treturn hex.length === 1 ? `0${hex}` : hex;\n\t};\n\treturn `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();\n}\n\nfunction harmonyHues(baseHue: number, harmony: Harmony): number[] {\n\tswitch (harmony) {\n\t\tcase \"analogous\":\n\t\t\treturn [baseHue, baseHue + 30, baseHue + 60, baseHue - 30];\n\t\tcase \"triadic\":\n\t\t\treturn [baseHue, baseHue + 120, baseHue + 240];\n\t\tcase \"splitComplementary\":\n\t\t\treturn [baseHue, baseHue + 150, baseHue + 210];\n\t\tcase \"tetradic\":\n\t\t\treturn [baseHue, baseHue + 90, baseHue + 180, baseHue + 270];\n\t\tcase \"complementary\":\n\t\t\treturn [baseHue, baseHue + 180, baseHue + 20, baseHue + 200];\n\t}\n}\n\n/**\n * Stable string → 32-bit unsigned hash (FNV-1a + bit-mixing avalanche).\n * Uses the full uint32 range as a seed so similar strings diverge fully.\n */\nexport function seedFromString(input: string): number {\n\tlet h = 2166136261 >>> 0;\n\tfor (let i = 0; i < input.length; i++) {\n\t\th ^= input.charCodeAt(i);\n\t\th = Math.imul(h, 16777619) >>> 0;\n\t}\n\th ^= h >>> 16;\n\th = Math.imul(h, 0x7feb352d) >>> 0;\n\th ^= h >>> 15;\n\th = Math.imul(h, 0x846ca68b) >>> 0;\n\th ^= h >>> 16;\n\treturn h >>> 0;\n}\n\n/** Normalize a string or number seed to the numeric seed used internally. */\nexport function toSeed(seed: number | string): number {\n\tif (typeof seed === \"number\") return seed;\n\treturn seedFromString(seed);\n}\n\n/** Derive the deterministic color palette for a seed. */\nexport function generatePalette(seed: number | string): GradientPalette {\n\tconst s = toSeed(seed);\n\tconst random = seededRandom(s);\n\tconst baseHue = (s * GOLDEN_ANGLE) % 360;\n\tconst harmonyIndex = Math.floor(random() * HARMONY_TYPES.length);\n\tconst harmony = HARMONY_TYPES[harmonyIndex];\n\tconst hues = harmonyHues(baseHue, harmony);\n\tconst colors = hues.map((hue) => {\n\t\tconst saturation = 75 + random() * 25;\n\t\tconst lightness = 50 + random() * 20;\n\t\treturn hslToHex(hue, saturation, lightness);\n\t});\n\treturn { seed: s, colors, harmony };\n}\n\n/**\n * Minimal Canvas2D context surface the renderer needs. Both\n * `HTMLCanvasElement` and `OffscreenCanvas` 2D contexts satisfy it.\n */\nexport type GradientContext = {\n\tfillStyle: string | CanvasGradient | CanvasPattern;\n\tglobalCompositeOperation: GlobalCompositeOperation;\n\tfillRect(x: number, y: number, w: number, h: number): void;\n\tcreateRadialGradient(\n\t\tx0: number,\n\t\ty0: number,\n\t\tr0: number,\n\t\tx1: number,\n\t\ty1: number,\n\t\tr1: number,\n\t): CanvasGradient;\n};\n\n/**\n * Draw the mesh gradient for `seed` into `ctx` at `size` x `size`.\n * The caller is responsible for any blur — apply `filter: blur(…)` on the\n * displayed canvas (≈6% of the rendered dimension) for the signature look,\n * or use {@link renderGradient} / {@link gradientToDataURL} which bake it in.\n */\nexport function drawMeshGradient(\n\tctx: GradientContext,\n\tseed: number | string,\n\tsize: number,\n): void {\n\tconst s = toSeed(seed);\n\tconst { colors } = generatePalette(s);\n\tconst random = seededRandom(s * 12345);\n\n\tctx.fillStyle = colors[0];\n\tctx.fillRect(0, 0, size, size);\n\n\tconst numSpots = 8 + Math.floor(random() * 5);\n\tconst spots: Array<{ x: number; y: number; radius: number; color: string }> =\n\t\t[];\n\n\tfor (let i = 0; i < numSpots; i++) {\n\t\tconst angle = random() * Math.PI * 2;\n\t\tconst distance = random() * size * 0.4;\n\t\tconst centerX = size / 2 + Math.cos(angle) * distance;\n\t\tconst centerY = size / 2 + Math.sin(angle) * distance;\n\t\tspots.push({\n\t\t\tx: centerX + (random() - 0.5) * size * 0.3,\n\t\t\ty: centerY + (random() - 0.5) * size * 0.3,\n\t\t\tradius: size * (0.3 + random() * 0.4),\n\t\t\tcolor: colors[i % colors.length],\n\t\t});\n\t}\n\n\tspots.sort((a, b) => b.radius - a.radius);\n\n\tctx.globalCompositeOperation = \"source-over\";\n\tfor (const spot of spots) {\n\t\tconst g = ctx.createRadialGradient(\n\t\t\tspot.x,\n\t\t\tspot.y,\n\t\t\t0,\n\t\t\tspot.x,\n\t\t\tspot.y,\n\t\t\tspot.radius,\n\t\t);\n\t\tg.addColorStop(0, `${spot.color}FF`);\n\t\tg.addColorStop(0.3, `${spot.color}DD`);\n\t\tg.addColorStop(0.6, `${spot.color}88`);\n\t\tg.addColorStop(1, `${spot.color}00`);\n\t\tctx.fillStyle = g;\n\t\tctx.fillRect(0, 0, size, size);\n\t}\n\n\tconst hx = size * 0.3 + random() * size * 0.2;\n\tconst hy = size * 0.3 + random() * size * 0.2;\n\tconst hg = ctx.createRadialGradient(hx, hy, 0, hx, hy, size * 0.3);\n\thg.addColorStop(0, \"rgba(255,255,255,0.15)\");\n\thg.addColorStop(1, \"rgba(255,255,255,0)\");\n\tctx.fillStyle = hg;\n\tctx.fillRect(0, 0, size, size);\n}\n\nexport interface RenderOptions {\n\t/**\n\t * Blur radius in pixels. Defaults to ~6% of the canvas size for the\n\t * signature soft look. Pass `0` to disable.\n\t */\n\tblur?: number;\n}\n\nfunction blurFor(size: number, blur?: number): number {\n\tif (blur === 0) return 0;\n\treturn blur ?? Math.round(size * DEFAULT_BLUR_FRACTION);\n}\n\n/**\n * Render a seed's gradient into an existing canvas, baking in the soft blur.\n * Draws at the canvas's current `width`/`height`. Browser/OffscreenCanvas only.\n */\nexport function renderGradient(\n\tcanvas: HTMLCanvasElement | OffscreenCanvas,\n\tseed: number | string,\n\toptions: RenderOptions = {},\n): void {\n\tconst size = canvas.width;\n\tconst blur = blurFor(size, options.blur);\n\n\tconst ctx = canvas.getContext(\"2d\") as CanvasRenderingContext2D | null;\n\tif (!ctx) return;\n\n\tif (blur <= 0) {\n\t\tctx.clearRect(0, 0, size, size);\n\t\tdrawMeshGradient(ctx, seed, size);\n\t\treturn;\n\t}\n\n\t// Draw the raw mesh on a scratch canvas, then composite it back with blur\n\t// scaled up slightly so the soft edges fall outside the frame (no ring).\n\tconst scratch = createCanvas(size, size);\n\tconst sctx = scratch.getContext(\"2d\") as CanvasRenderingContext2D | null;\n\tif (!sctx) return;\n\tdrawMeshGradient(sctx, seed, size);\n\n\tconst scaleUp = 1 + (blur / size) * 4;\n\tconst dw = size * scaleUp;\n\tconst offset = (dw - size) / 2;\n\tctx.clearRect(0, 0, size, size);\n\tif (supportsCanvasFilter()) {\n\t\tctx.filter = `blur(${blur}px)`;\n\t\tctx.drawImage(scratch as CanvasImageSource, -offset, -offset, dw, dw);\n\t\tctx.filter = \"none\";\n\t\treturn;\n\t}\n\t// 2D-canvas `filter` is a silent no-op on Safari < 17: approximate the\n\t// gaussian by bouncing through a small canvas. Bilinear resampling on the\n\t// way down and back up smears by roughly the downscale factor, which is\n\t// plenty for a mesh that is already smooth gradients.\n\tconst factor = Math.max(2, Math.min(16, blur / 2));\n\tconst sw = Math.max(1, Math.round(size / factor));\n\tconst small = createCanvas(sw, sw);\n\tconst smallCtx = small.getContext(\"2d\") as CanvasRenderingContext2D | null;\n\tif (!smallCtx) {\n\t\tctx.drawImage(scratch as CanvasImageSource, -offset, -offset, dw, dw);\n\t\treturn;\n\t}\n\tsmallCtx.imageSmoothingEnabled = true;\n\tsmallCtx.imageSmoothingQuality = \"high\";\n\tsmallCtx.drawImage(scratch as CanvasImageSource, 0, 0, sw, sw);\n\tctx.imageSmoothingEnabled = true;\n\tctx.imageSmoothingQuality = \"high\";\n\tctx.drawImage(small as CanvasImageSource, -offset, -offset, dw, dw);\n}\n\n/* Engines that honor 2D-canvas `filter` echo an assigned value back from the\n * property; Safari < 17 ignores the assignment. Probed once, then cached. */\nlet canvasFilterSupport: boolean | null = null;\nfunction supportsCanvasFilter(): boolean {\n\tif (canvasFilterSupport !== null) return canvasFilterSupport;\n\tconst probe = createCanvas(1, 1).getContext(\n\t\t\"2d\",\n\t) as CanvasRenderingContext2D | null;\n\tif (!probe) {\n\t\tcanvasFilterSupport = false;\n\t\treturn canvasFilterSupport;\n\t}\n\tprobe.filter = \"blur(1px)\";\n\tcanvasFilterSupport = probe.filter === \"blur(1px)\";\n\treturn canvasFilterSupport;\n}\n\nfunction createCanvas(\n\tw: number,\n\th: number,\n): HTMLCanvasElement | OffscreenCanvas {\n\tif (typeof OffscreenCanvas !== \"undefined\") {\n\t\treturn new OffscreenCanvas(w, h);\n\t}\n\tconst c = document.createElement(\"canvas\");\n\tc.width = w;\n\tc.height = h;\n\treturn c;\n}\n\nexport interface ExportOptions extends RenderOptions {\n\t/** Output pixel dimensions (square). Default: 512. */\n\tsize?: number;\n\t/** Image MIME type. Default: \"image/png\". */\n\ttype?: string;\n\t/** Quality 0–1 for lossy types. Default: 0.92. */\n\tquality?: number;\n}\n\n/** Render a seed's gradient and return it as a data URL. Browser only. */\nexport function gradientToDataURL(\n\tseed: number | string,\n\toptions: ExportOptions = {},\n): string {\n\tconst { size = 512, type = \"image/png\", quality = 0.92 } = options;\n\tconst canvas = document.createElement(\"canvas\");\n\tcanvas.width = size;\n\tcanvas.height = size;\n\trenderGradient(canvas, seed, options);\n\treturn canvas.toDataURL(type, quality);\n}\n\n/** Render a seed's gradient and resolve a Blob (or null). Browser only. */\nexport function gradientToBlob(\n\tseed: number | string,\n\toptions: ExportOptions = {},\n): Promise<Blob | null> {\n\tconst { size = 512, type = \"image/png\", quality = 0.92 } = options;\n\tconst canvas = document.createElement(\"canvas\");\n\tcanvas.width = size;\n\tcanvas.height = size;\n\trenderGradient(canvas, seed, options);\n\treturn new Promise((resolve) => canvas.toBlob(resolve, type, quality));\n}\n","import type { CSSProperties } from \"react\";\nimport { useEffect, useRef } from \"react\";\nimport { drawMeshGradient } from \"./engine\";\n\nexport interface GradientAvatarProps {\n\t/** Any string or number — each unique seed produces a unique gradient. */\n\tseed: number | string;\n\t/** Rendered size in pixels. Default: 32. */\n\tsize?: number;\n\t/**\n\t * Corner radius. Number = pixels, string = any CSS length.\n\t * Defaults to a full circle; pass `0` for a square or e.g. `12` for a\n\t * rounded square. Default: \"9999px\".\n\t */\n\tradius?: number | string;\n\t/**\n\t * Slowly drift the gradient with a GPU-only CSS animation. The canvas is\n\t * still painted just once — the motion is a compositor transform on the\n\t * cached layer, so there is no per-frame JavaScript, no repaint, and no\n\t * extra memory. Automatically disabled for users who request reduced\n\t * motion. Default: `false`.\n\t */\n\tanimate?: boolean;\n\t/**\n\t * Duration of one full drift cycle in seconds. Slower is subtler and\n\t * cheaper. Only applies when {@link animate} is `true`. Default: `16`.\n\t */\n\tanimationDuration?: number;\n\t/** Additional CSS classes on the wrapper. */\n\tclassName?: string;\n\t/** Extra inline styles merged onto the wrapper. */\n\tstyle?: CSSProperties;\n}\n\n/** Internal render resolution. Higher than display size so the CSS blur is smooth. */\nconst RENDER_SIZE = 256;\n/** Blur radius as a fraction of display size. */\nconst BLUR_FRACTION = 0.06;\n\n/** Class applied to the canvas when animating. Styles injected once, lazily. */\nconst ANIMATED_CLASS = \"oa-avatar-canvas--animated\";\nconst STYLE_ELEMENT_ID = \"oa-avatar-keyframes\";\n\n/**\n * Keyframes for the drift. The canvas is scaled up so that the rotation and\n * translation never expose the clipped container edges, and the whole thing is\n * a single `transform` track so the browser can run it entirely on the\n * compositor thread. `prefers-reduced-motion` opts out with no JS listener.\n */\nconst KEYFRAMES_CSS = `\n@keyframes oa-avatar-drift {\n\t0% { transform: scale(1.25) translate(0%, 0%) rotate(0deg); }\n\t20% { transform: scale(1.30) translate(-3%, 2%) rotate(8deg); }\n\t40% { transform: scale(1.22) translate(3%, 3%) rotate(-6deg); }\n\t60% { transform: scale(1.28) translate(2%, -3%) rotate(5deg); }\n\t80% { transform: scale(1.24) translate(-2%, -2%) rotate(-4deg); }\n\t100% { transform: scale(1.25) translate(0%, 0%) rotate(0deg); }\n}\n.${ANIMATED_CLASS} {\n\ttransform-origin: center;\n\twill-change: transform;\n\tanimation-name: oa-avatar-drift;\n\tanimation-timing-function: ease-in-out;\n\tanimation-iteration-count: infinite;\n\tanimation-direction: alternate;\n}\n@media (prefers-reduced-motion: reduce) {\n\t.${ANIMATED_CLASS} {\n\t\tanimation: none;\n\t\ttransform: none;\n\t\twill-change: auto;\n\t}\n}\n`;\n\nlet stylesInjected = false;\n\n/** Inject the drift keyframes into the document head exactly once (client-only). */\nfunction ensureAnimationStyles(): void {\n\tif (stylesInjected || typeof document === \"undefined\") return;\n\tif (document.getElementById(STYLE_ELEMENT_ID)) {\n\t\tstylesInjected = true;\n\t\treturn;\n\t}\n\tconst el = document.createElement(\"style\");\n\tel.id = STYLE_ELEMENT_ID;\n\tel.textContent = KEYFRAMES_CSS;\n\tdocument.head.appendChild(el);\n\tstylesInjected = true;\n}\n\n/**\n * Renders a deterministic mesh-gradient avatar on a `<canvas>`.\n * The same seed always produces the same gradient.\n */\nexport function GradientAvatar({\n\tseed,\n\tsize = 32,\n\tradius = \"9999px\",\n\tanimate = false,\n\tanimationDuration = 16,\n\tclassName,\n\tstyle,\n}: GradientAvatarProps) {\n\tconst canvasRef = useRef<HTMLCanvasElement>(null);\n\n\tuseEffect(() => {\n\t\tconst canvas = canvasRef.current;\n\t\tif (!canvas) return;\n\t\tconst ctx = canvas.getContext(\"2d\");\n\t\tif (!ctx) return;\n\t\tctx.clearRect(0, 0, RENDER_SIZE, RENDER_SIZE);\n\t\tdrawMeshGradient(ctx, seed, RENDER_SIZE);\n\t}, [seed]);\n\n\tuseEffect(() => {\n\t\tif (animate) ensureAnimationStyles();\n\t}, [animate]);\n\n\tconst blurPx = Math.max(1, Math.round(size * BLUR_FRACTION));\n\n\tconst canvasStyle: CSSProperties = {\n\t\twidth: \"100%\",\n\t\theight: \"100%\",\n\t\tdisplay: \"block\",\n\t\tfilter: `blur(${blurPx}px)`,\n\t};\n\tif (animate) {\n\t\tcanvasStyle.animationDuration = `${animationDuration}s`;\n\t}\n\n\treturn (\n\t\t<span\n\t\t\tclassName={className}\n\t\t\tstyle={{\n\t\t\t\tdisplay: \"inline-block\",\n\t\t\t\toverflow: \"hidden\",\n\t\t\t\tborderRadius: radius,\n\t\t\t\twidth: size,\n\t\t\t\theight: size,\n\t\t\t\t...style,\n\t\t\t}}\n\t\t>\n\t\t\t<canvas\n\t\t\t\tref={canvasRef}\n\t\t\t\twidth={RENDER_SIZE}\n\t\t\t\theight={RENDER_SIZE}\n\t\t\t\tclassName={animate ? ANIMATED_CLASS : undefined}\n\t\t\t\tstyle={canvasStyle}\n\t\t\t/>\n\t\t</span>\n\t);\n}\n\nexport type {\n\tExportOptions,\n\tGradientPalette,\n\tHarmony,\n\tRenderOptions,\n} from \"./engine\";\nexport {\n\tdrawMeshGradient,\n\tgeneratePalette,\n\tgradientToBlob,\n\tgradientToDataURL,\n\trenderGradient,\n\tseedFromString,\n\ttoSeed,\n} from \"./engine\";\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/engine.ts","../src/index.tsx"],"names":[],"mappings":";;;;;;AAoBA,IAAM,aAAA,GAA2B;AAAA,EAC/B,WAAA;AAAA,EACA,SAAA;AAAA,EACA,oBAAA;AAAA,EACA,UAAA;AAAA,EACA;AACF,CAAA;AAEA,IAAM,YAAA,GAAe,KAAA;AAGd,IAAM,qBAAA,GAAwB,IAAA;AAErC,SAAS,aAAa,IAAA,EAA4B;AAChD,EAAA,IAAI,CAAA,GAAI,IAAA;AACR,EAAA,OAAO,MAAM;AACX,IAAA,CAAA,IAAK,UAAA;AACL,IAAA,IAAI,CAAA,GAAI,CAAA;AACR,IAAA,CAAA,GAAI,KAAK,IAAA,CAAK,CAAA,GAAK,CAAA,KAAM,EAAA,EAAK,IAAI,CAAC,CAAA;AACnC,IAAA,CAAA,IAAK,IAAI,IAAA,CAAK,IAAA,CAAK,IAAK,CAAA,KAAM,CAAA,EAAI,IAAI,EAAE,CAAA;AACxC,IAAA,OAAA,CAAA,CAAS,CAAA,GAAK,CAAA,KAAM,EAAA,MAAS,CAAA,IAAK,UAAA;AAAA,EACpC,CAAA;AACF;AAEA,SAAS,QAAA,CAAS,CAAA,EAAW,CAAA,EAAW,CAAA,EAAmB;AACzD,EAAA,CAAA,GAAA,CAAM,CAAA,GAAI,MAAO,GAAA,IAAO,GAAA;AACxB,EAAA,CAAA,GAAI,IAAA,CAAK,IAAI,CAAA,EAAG,IAAA,CAAK,IAAI,GAAA,EAAK,CAAC,CAAC,CAAA,GAAI,GAAA;AACpC,EAAA,CAAA,GAAI,IAAA,CAAK,IAAI,CAAA,EAAG,IAAA,CAAK,IAAI,GAAA,EAAK,CAAC,CAAC,CAAA,GAAI,GAAA;AAEpC,EAAA,MAAM,KAAK,CAAA,GAAI,IAAA,CAAK,IAAI,CAAA,GAAI,CAAA,GAAI,CAAC,CAAA,IAAK,CAAA;AACtC,EAAA,MAAM,CAAA,GAAI,KAAK,CAAA,GAAI,IAAA,CAAK,IAAM,CAAA,GAAI,EAAA,GAAM,IAAK,CAAC,CAAA,CAAA;AAC9C,EAAA,MAAM,CAAA,GAAI,IAAI,CAAA,GAAI,CAAA;AAElB,EAAA,IAAI,CAAA,GAAI,CAAA;AACR,EAAA,IAAI,CAAA,GAAI,CAAA;AACR,EAAA,IAAI,CAAA,GAAI,CAAA;AACR,EAAA,IAAI,IAAI,EAAA,EAAI;AACV,IAAA,CAAA,GAAI,CAAA;AACJ,IAAA,CAAA,GAAI,CAAA;AAAA,EACN,CAAA,MAAA,IAAW,IAAI,GAAA,EAAK;AAClB,IAAA,CAAA,GAAI,CAAA;AACJ,IAAA,CAAA,GAAI,CAAA;AAAA,EACN,CAAA,MAAA,IAAW,IAAI,GAAA,EAAK;AAClB,IAAA,CAAA,GAAI,CAAA;AACJ,IAAA,CAAA,GAAI,CAAA;AAAA,EACN,CAAA,MAAA,IAAW,IAAI,GAAA,EAAK;AAClB,IAAA,CAAA,GAAI,CAAA;AACJ,IAAA,CAAA,GAAI,CAAA;AAAA,EACN,CAAA,MAAA,IAAW,IAAI,GAAA,EAAK;AAClB,IAAA,CAAA,GAAI,CAAA;AACJ,IAAA,CAAA,GAAI,CAAA;AAAA,EACN,CAAA,MAAO;AACL,IAAA,CAAA,GAAI,CAAA;AACJ,IAAA,CAAA,GAAI,CAAA;AAAA,EACN;AAEA,EAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,KAAc;AAC3B,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,CAAA,CAAO,CAAA,GAAI,KAAK,GAAG,CAAA,CAAE,SAAS,EAAE,CAAA;AACjD,IAAA,OAAO,GAAA,CAAI,MAAA,KAAW,CAAA,GAAI,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA,GAAK,GAAA;AAAA,EACxC,CAAA;AACA,EAAA,OAAO,CAAA,CAAA,EAAI,KAAA,CAAM,CAAC,CAAC,CAAA,EAAG,KAAA,CAAM,CAAC,CAAC,CAAA,EAAG,KAAA,CAAM,CAAC,CAAC,GAAG,WAAA,EAAY;AAC1D;AAEA,SAAS,WAAA,CAAY,SAAiB,OAAA,EAA4B;AAChE,EAAA,QAAQ,OAAA;AAAS,IACf,KAAK,WAAA;AACH,MAAA,OAAO,CAAC,OAAA,EAAS,OAAA,GAAU,IAAI,OAAA,GAAU,EAAA,EAAI,UAAU,EAAE,CAAA;AAAA,IAC3D,KAAK,SAAA;AACH,MAAA,OAAO,CAAC,OAAA,EAAS,OAAA,GAAU,GAAA,EAAK,UAAU,GAAG,CAAA;AAAA,IAC/C,KAAK,oBAAA;AACH,MAAA,OAAO,CAAC,OAAA,EAAS,OAAA,GAAU,GAAA,EAAK,UAAU,GAAG,CAAA;AAAA,IAC/C,KAAK,UAAA;AACH,MAAA,OAAO,CAAC,OAAA,EAAS,OAAA,GAAU,IAAI,OAAA,GAAU,GAAA,EAAK,UAAU,GAAG,CAAA;AAAA,IAC7D,KAAK,eAAA;AACH,MAAA,OAAO,CAAC,OAAA,EAAS,OAAA,GAAU,KAAK,OAAA,GAAU,EAAA,EAAI,UAAU,GAAG,CAAA;AAAA;AAEjE;AAMO,SAAS,eAAe,KAAA,EAAuB;AACpD,EAAA,IAAI,IAAI,UAAA,KAAe,CAAA;AACvB,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,IAAA,CAAA,IAAK,KAAA,CAAM,WAAW,CAAC,CAAA;AACvB,IAAA,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,CAAA,EAAG,QAAQ,CAAA,KAAM,CAAA;AAAA,EACjC;AACA,EAAA,CAAA,IAAK,CAAA,KAAM,EAAA;AACX,EAAA,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,CAAA,EAAG,UAAU,CAAA,KAAM,CAAA;AACjC,EAAA,CAAA,IAAK,CAAA,KAAM,EAAA;AACX,EAAA,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,CAAA,EAAG,UAAU,CAAA,KAAM,CAAA;AACjC,EAAA,CAAA,IAAK,CAAA,KAAM,EAAA;AACX,EAAA,OAAO,CAAA,KAAM,CAAA;AACf;AAGO,SAAS,OAAO,IAAA,EAA+B;AACpD,EAAA,IAAI,OAAO,IAAA,KAAS,QAAA,EAAU,OAAO,IAAA;AACrC,EAAA,OAAO,eAAe,IAAI,CAAA;AAC5B;AAGO,SAAS,gBAAgB,IAAA,EAAwC;AACtE,EAAA,MAAM,CAAA,GAAI,OAAO,IAAI,CAAA;AACrB,EAAA,MAAM,MAAA,GAAS,aAAa,CAAC,CAAA;AAC7B,EAAA,MAAM,OAAA,GAAW,IAAI,YAAA,GAAgB,GAAA;AACrC,EAAA,MAAM,eAAe,IAAA,CAAK,KAAA,CAAM,MAAA,EAAO,GAAI,cAAc,MAAM,CAAA;AAC/D,EAAA,MAAM,OAAA,GAAU,cAAc,YAAY,CAAA;AAC1C,EAAA,MAAM,IAAA,GAAO,WAAA,CAAY,OAAA,EAAS,OAAO,CAAA;AACzC,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,KAAQ;AAC/B,IAAA,MAAM,UAAA,GAAa,EAAA,GAAK,MAAA,EAAO,GAAI,EAAA;AACnC,IAAA,MAAM,SAAA,GAAY,EAAA,GAAK,MAAA,EAAO,GAAI,EAAA;AAClC,IAAA,OAAO,QAAA,CAAS,GAAA,EAAK,UAAA,EAAY,SAAS,CAAA;AAAA,EAC5C,CAAC,CAAA;AACD,EAAA,OAAO,EAAE,IAAA,EAAM,CAAA,EAAG,MAAA,EAAQ,OAAA,EAAQ;AACpC;AAsCO,SAAS,gBAAA,CACd,GAAA,EACA,IAAA,EACA,IAAA,EACM;AACN,EAAA,MAAM,CAAA,GAAI,OAAO,IAAI,CAAA;AACrB,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,eAAA,CAAgB,CAAC,CAAA;AACpC,EAAA,MAAM,MAAA,GAAS,YAAA,CAAa,CAAA,GAAI,KAAK,CAAA;AAErC,EAAA,GAAA,CAAI,SAAA,GAAY,OAAO,CAAC,CAAA;AACxB,EAAA,GAAA,CAAI,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,IAAA,EAAM,IAAI,CAAA;AAE7B,EAAA,MAAM,WAAW,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,MAAA,KAAW,CAAC,CAAA;AAC5C,EAAA,MAAM,QACJ,EAAC;AAEH,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,QAAA,EAAU,CAAA,EAAA,EAAK;AACjC,IAAA,MAAM,KAAA,GAAQ,MAAA,EAAO,GAAI,IAAA,CAAK,EAAA,GAAK,CAAA;AACnC,IAAA,MAAM,QAAA,GAAW,MAAA,EAAO,GAAI,IAAA,GAAO,GAAA;AACnC,IAAA,MAAM,UAAU,IAAA,GAAO,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA,GAAI,QAAA;AAC7C,IAAA,MAAM,UAAU,IAAA,GAAO,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA,GAAI,QAAA;AAC7C,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,CAAA,EAAG,OAAA,GAAA,CAAW,MAAA,EAAO,GAAI,OAAO,IAAA,GAAO,GAAA;AAAA,MACvC,CAAA,EAAG,OAAA,GAAA,CAAW,MAAA,EAAO,GAAI,OAAO,IAAA,GAAO,GAAA;AAAA,MACvC,MAAA,EAAQ,IAAA,IAAQ,GAAA,GAAM,MAAA,EAAO,GAAI,GAAA,CAAA;AAAA,MACjC,KAAA,EAAO,MAAA,CAAO,CAAA,GAAI,MAAA,CAAO,MAAM;AAAA,KAChC,CAAA;AAAA,EACH;AAEA,EAAA,KAAA,CAAM,KAAK,CAAC,CAAA,EAAG,MAAM,CAAA,CAAE,MAAA,GAAS,EAAE,MAAM,CAAA;AAExC,EAAA,GAAA,CAAI,wBAAA,GAA2B,aAAA;AAC/B,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,IAAI,GAAA,CAAI,oBAAA;AAAA,MACZ,IAAA,CAAK,CAAA;AAAA,MACL,IAAA,CAAK,CAAA;AAAA,MACL,CAAA;AAAA,MACA,IAAA,CAAK,CAAA;AAAA,MACL,IAAA,CAAK,CAAA;AAAA,MACL,IAAA,CAAK;AAAA,KACP;AACA,IAAA,CAAA,CAAE,YAAA,CAAa,CAAA,EAAG,CAAA,EAAG,IAAA,CAAK,KAAK,CAAA,EAAA,CAAI,CAAA;AACnC,IAAA,CAAA,CAAE,YAAA,CAAa,GAAA,EAAK,CAAA,EAAG,IAAA,CAAK,KAAK,CAAA,EAAA,CAAI,CAAA;AACrC,IAAA,CAAA,CAAE,YAAA,CAAa,GAAA,EAAK,CAAA,EAAG,IAAA,CAAK,KAAK,CAAA,EAAA,CAAI,CAAA;AACrC,IAAA,CAAA,CAAE,YAAA,CAAa,CAAA,EAAG,CAAA,EAAG,IAAA,CAAK,KAAK,CAAA,EAAA,CAAI,CAAA;AACnC,IAAA,GAAA,CAAI,SAAA,GAAY,CAAA;AAChB,IAAA,GAAA,CAAI,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,IAAA,EAAM,IAAI,CAAA;AAAA,EAC/B;AAEA,EAAA,MAAM,EAAA,GAAK,IAAA,GAAO,GAAA,GAAM,MAAA,KAAW,IAAA,GAAO,GAAA;AAC1C,EAAA,MAAM,EAAA,GAAK,IAAA,GAAO,GAAA,GAAM,MAAA,KAAW,IAAA,GAAO,GAAA;AAC1C,EAAA,MAAM,EAAA,GAAK,IAAI,oBAAA,CAAqB,EAAA,EAAI,IAAI,CAAA,EAAG,EAAA,EAAI,EAAA,EAAI,IAAA,GAAO,GAAG,CAAA;AACjE,EAAA,EAAA,CAAG,YAAA,CAAa,GAAG,wBAAwB,CAAA;AAC3C,EAAA,EAAA,CAAG,YAAA,CAAa,GAAG,qBAAqB,CAAA;AACxC,EAAA,GAAA,CAAI,SAAA,GAAY,EAAA;AAChB,EAAA,GAAA,CAAI,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,IAAA,EAAM,IAAI,CAAA;AAC/B;AAQO,SAAS,mBAAA,CACd,MACA,IAAA,EACgB;AAChB,EAAA,MAAM,CAAA,GAAI,OAAO,IAAI,CAAA;AACrB,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,eAAA,CAAgB,CAAC,CAAA;AACpC,EAAA,MAAM,MAAA,GAAS,YAAA,CAAa,CAAA,GAAI,KAAK,CAAA;AAErC,EAAA,MAAM,WAAW,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,MAAA,KAAW,CAAC,CAAA;AAC5C,EAAA,MAAM,QAAwB,EAAC;AAE/B,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,QAAA,EAAU,CAAA,EAAA,EAAK;AACjC,IAAA,MAAM,KAAA,GAAQ,MAAA,EAAO,GAAI,IAAA,CAAK,EAAA,GAAK,CAAA;AACnC,IAAA,MAAM,QAAA,GAAW,MAAA,EAAO,GAAI,IAAA,GAAO,GAAA;AACnC,IAAA,MAAM,UAAU,IAAA,GAAO,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA,GAAI,QAAA;AAC7C,IAAA,MAAM,UAAU,IAAA,GAAO,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA,GAAI,QAAA;AAC7C,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,CAAA,EAAG,OAAA,GAAA,CAAW,MAAA,EAAO,GAAI,OAAO,IAAA,GAAO,GAAA;AAAA,MACvC,CAAA,EAAG,OAAA,GAAA,CAAW,MAAA,EAAO,GAAI,OAAO,IAAA,GAAO,GAAA;AAAA,MACvC,MAAA,EAAQ,IAAA,IAAQ,GAAA,GAAM,MAAA,EAAO,GAAI,GAAA,CAAA;AAAA,MACjC,KAAA,EAAO,MAAA,CAAO,CAAA,GAAI,MAAA,CAAO,MAAM,CAAA;AAAA,MAC/B,QAAQ,MAAA,EAAO;AAAA,MACf,QAAQ,MAAA,EAAO;AAAA,MACf,SAAA,EAAW,IAAA,GAAO,MAAA,EAAO,GAAI;AAAA,KAC9B,CAAA;AAAA,EACH;AAEA,EAAA,KAAA,CAAM,KAAK,CAAC,CAAA,EAAG,MAAM,CAAA,CAAE,MAAA,GAAS,EAAE,MAAM,CAAA;AACxC,EAAA,OAAO,KAAA;AACT;AAOO,SAAS,iBAAA,CACd,GAAA,EACA,KAAA,EACA,eAAA,EACA,MACA,IAAA,EACM;AACN,EAAA,GAAA,CAAI,SAAA,GAAY,eAAA;AAChB,EAAA,GAAA,CAAI,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,IAAA,EAAM,IAAI,CAAA;AAE7B,EAAA,GAAA,CAAI,wBAAA,GAA2B,aAAA;AAC/B,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AAGxB,IAAA,MAAM,OAAA,GAAU,EAAA,GAAK,IAAA,CAAK,MAAA,GAAS,CAAA;AACnC,IAAA,MAAM,OAAA,GAAU,EAAA,GAAK,IAAA,CAAK,MAAA,GAAS,EAAA;AACnC,IAAA,MAAM,KAAA,GAAQ,KAAK,SAAA,GAAY,IAAA;AAC/B,IAAA,MAAM,EAAA,GACJ,IAAA,CAAK,CAAA,GACL,IAAA,CAAK,IAAI,IAAA,IAAS,CAAA,GAAI,IAAA,CAAK,EAAA,GAAM,WAAW,IAAA,CAAK,MAAA,GAAS,IAAA,CAAK,EAAA,GAAK,CAAC,CAAA,GACnE,KAAA;AACJ,IAAA,MAAM,EAAA,GACJ,IAAA,CAAK,CAAA,GACL,IAAA,CAAK,IAAI,IAAA,IAAS,CAAA,GAAI,IAAA,CAAK,EAAA,GAAM,WAAW,IAAA,CAAK,MAAA,GAAS,IAAA,CAAK,EAAA,GAAK,CAAC,CAAA,GACnE,KAAA;AAEJ,IAAA,MAAM,CAAA,GAAI,IAAI,oBAAA,CAAqB,EAAA,EAAI,IAAI,CAAA,EAAG,EAAA,EAAI,EAAA,EAAI,IAAA,CAAK,MAAM,CAAA;AACjE,IAAA,CAAA,CAAE,YAAA,CAAa,CAAA,EAAG,CAAA,EAAG,IAAA,CAAK,KAAK,CAAA,EAAA,CAAI,CAAA;AACnC,IAAA,CAAA,CAAE,YAAA,CAAa,GAAA,EAAK,CAAA,EAAG,IAAA,CAAK,KAAK,CAAA,EAAA,CAAI,CAAA;AACrC,IAAA,CAAA,CAAE,YAAA,CAAa,GAAA,EAAK,CAAA,EAAG,IAAA,CAAK,KAAK,CAAA,EAAA,CAAI,CAAA;AACrC,IAAA,CAAA,CAAE,YAAA,CAAa,CAAA,EAAG,CAAA,EAAG,IAAA,CAAK,KAAK,CAAA,EAAA,CAAI,CAAA;AACnC,IAAA,GAAA,CAAI,SAAA,GAAY,CAAA;AAChB,IAAA,GAAA,CAAI,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,IAAA,EAAM,IAAI,CAAA;AAAA,EAC/B;AAGA,EAAA,MAAM,QAAQ,IAAA,GAAO,GAAA;AACrB,EAAA,MAAM,EAAA,GAAK,OAAO,IAAA,GAAO,IAAA,CAAK,IAAI,IAAA,GAAO,GAAG,IAAI,IAAA,GAAO,GAAA;AACvD,EAAA,MAAM,EAAA,GAAK,OAAO,IAAA,GAAO,IAAA,CAAK,IAAI,IAAA,GAAO,GAAG,IAAI,IAAA,GAAO,GAAA;AACvD,EAAA,MAAM,EAAA,GAAK,IAAI,oBAAA,CAAqB,EAAA,EAAI,IAAI,CAAA,EAAG,EAAA,EAAI,IAAI,KAAK,CAAA;AAC5D,EAAA,EAAA,CAAG,YAAA,CAAa,GAAG,wBAAwB,CAAA;AAC3C,EAAA,EAAA,CAAG,YAAA,CAAa,GAAG,qBAAqB,CAAA;AACxC,EAAA,GAAA,CAAI,SAAA,GAAY,EAAA;AAChB,EAAA,GAAA,CAAI,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,IAAA,EAAM,IAAI,CAAA;AAC/B;AAUA,SAAS,OAAA,CAAQ,MAAc,IAAA,EAAuB;AACpD,EAAA,IAAI,IAAA,KAAS,GAAG,OAAO,CAAA;AACvB,EAAA,OAAO,IAAA,IAAQ,IAAA,CAAK,KAAA,CAAM,IAAA,GAAO,qBAAqB,CAAA;AACxD;AAMO,SAAS,cAAA,CACd,MAAA,EACA,IAAA,EACA,OAAA,GAAyB,EAAC,EACpB;AACN,EAAA,MAAM,OAAO,MAAA,CAAO,KAAA;AACpB,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,IAAA,EAAM,OAAA,CAAQ,IAAI,CAAA;AAEvC,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,UAAA,CAAW,IAAI,CAAA;AAClC,EAAA,IAAI,CAAC,GAAA,EAAK;AAEV,EAAA,IAAI,QAAQ,CAAA,EAAG;AACb,IAAA,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAA,EAAG,IAAA,EAAM,IAAI,CAAA;AAC9B,IAAA,gBAAA,CAAiB,GAAA,EAAK,MAAM,IAAI,CAAA;AAChC,IAAA;AAAA,EACF;AAIA,EAAA,MAAM,OAAA,GAAU,YAAA,CAAa,IAAA,EAAM,IAAI,CAAA;AACvC,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,UAAA,CAAW,IAAI,CAAA;AACpC,EAAA,IAAI,CAAC,IAAA,EAAM;AACX,EAAA,gBAAA,CAAiB,IAAA,EAAM,MAAM,IAAI,CAAA;AAEjC,EAAA,MAAM,OAAA,GAAU,CAAA,GAAK,IAAA,GAAO,IAAA,GAAQ,CAAA;AACpC,EAAA,MAAM,KAAK,IAAA,GAAO,OAAA;AAClB,EAAA,MAAM,MAAA,GAAA,CAAU,KAAK,IAAA,IAAQ,CAAA;AAC7B,EAAA,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAA,EAAG,IAAA,EAAM,IAAI,CAAA;AAC9B,EAAA,IAAI,sBAAqB,EAAG;AAC1B,IAAA,GAAA,CAAI,MAAA,GAAS,QAAQ,IAAI,CAAA,GAAA,CAAA;AACzB,IAAA,GAAA,CAAI,UAAU,OAAA,EAA8B,CAAC,QAAQ,CAAC,MAAA,EAAQ,IAAI,EAAE,CAAA;AACpE,IAAA,GAAA,CAAI,MAAA,GAAS,MAAA;AACb,IAAA;AAAA,EACF;AAKA,EAAA,MAAM,MAAA,GAAS,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,GAAA,CAAI,EAAA,EAAI,IAAA,GAAO,CAAC,CAAC,CAAA;AACjD,EAAA,MAAM,EAAA,GAAK,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,IAAA,GAAO,MAAM,CAAC,CAAA;AAChD,EAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,EAAA,EAAI,EAAE,CAAA;AACjC,EAAA,MAAM,QAAA,GAAW,KAAA,CAAM,UAAA,CAAW,IAAI,CAAA;AACtC,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,GAAA,CAAI,UAAU,OAAA,EAA8B,CAAC,QAAQ,CAAC,MAAA,EAAQ,IAAI,EAAE,CAAA;AACpE,IAAA;AAAA,EACF;AACA,EAAA,QAAA,CAAS,qBAAA,GAAwB,IAAA;AACjC,EAAA,QAAA,CAAS,qBAAA,GAAwB,MAAA;AACjC,EAAA,QAAA,CAAS,SAAA,CAAU,OAAA,EAA8B,CAAA,EAAG,CAAA,EAAG,IAAI,EAAE,CAAA;AAC7D,EAAA,GAAA,CAAI,qBAAA,GAAwB,IAAA;AAC5B,EAAA,GAAA,CAAI,qBAAA,GAAwB,MAAA;AAC5B,EAAA,GAAA,CAAI,UAAU,KAAA,EAA4B,CAAC,QAAQ,CAAC,MAAA,EAAQ,IAAI,EAAE,CAAA;AACpE;AAIA,IAAI,mBAAA,GAAsC,IAAA;AAC1C,SAAS,oBAAA,GAAgC;AACvC,EAAA,IAAI,mBAAA,KAAwB,MAAM,OAAO,mBAAA;AACzC,EAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,CAAA,EAAG,CAAC,CAAA,CAAE,UAAA;AAAA,IAC/B;AAAA,GACF;AACA,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,mBAAA,GAAsB,KAAA;AACtB,IAAA,OAAO,mBAAA;AAAA,EACT;AACA,EAAA,KAAA,CAAM,MAAA,GAAS,WAAA;AACf,EAAA,mBAAA,GAAsB,MAAM,MAAA,KAAW,WAAA;AACvC,EAAA,OAAO,mBAAA;AACT;AAEA,SAAS,YAAA,CACP,GACA,CAAA,EACqC;AACrC,EAAA,IAAI,OAAO,oBAAoB,WAAA,EAAa;AAC1C,IAAA,OAAO,IAAI,eAAA,CAAgB,CAAA,EAAG,CAAC,CAAA;AAAA,EACjC;AACA,EAAA,MAAM,CAAA,GAAI,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AACzC,EAAA,CAAA,CAAE,KAAA,GAAQ,CAAA;AACV,EAAA,CAAA,CAAE,MAAA,GAAS,CAAA;AACX,EAAA,OAAO,CAAA;AACT;AAYO,SAAS,iBAAA,CACd,IAAA,EACA,OAAA,GAAyB,EAAC,EAClB;AACR,EAAA,MAAM,EAAE,IAAA,GAAO,GAAA,EAAK,OAAO,WAAA,EAAa,OAAA,GAAU,MAAK,GAAI,OAAA;AAC3D,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,EAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AACf,EAAA,MAAA,CAAO,MAAA,GAAS,IAAA;AAChB,EAAA,cAAA,CAAe,MAAA,EAAQ,MAAM,OAAO,CAAA;AACpC,EAAA,OAAO,MAAA,CAAO,SAAA,CAAU,IAAA,EAAM,OAAO,CAAA;AACvC;AAGO,SAAS,cAAA,CACd,IAAA,EACA,OAAA,GAAyB,EAAC,EACJ;AACtB,EAAA,MAAM,EAAE,IAAA,GAAO,GAAA,EAAK,OAAO,WAAA,EAAa,OAAA,GAAU,MAAK,GAAI,OAAA;AAC3D,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,EAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AACf,EAAA,MAAA,CAAO,MAAA,GAAS,IAAA;AAChB,EAAA,cAAA,CAAe,MAAA,EAAQ,MAAM,OAAO,CAAA;AACpC,EAAA,OAAO,IAAI,QAAQ,CAAC,OAAA,KAAY,OAAO,MAAA,CAAO,OAAA,EAAS,IAAA,EAAM,OAAO,CAAC,CAAA;AACvE;ACzZA,IAAM,WAAA,GAAc,GAAA;AAEpB,IAAM,aAAA,GAAgB,IAAA;AAEtB,IAAM,SAAA,GAAY,EAAA;AAKlB,IAAM,WAAA,uBAAkB,GAAA,EAAY;AACpC,IAAI,SAAA,GAA2B,IAAA;AAC/B,IAAI,UAAA,GAAa,CAAA;AACjB,IAAI,YAAA,GAAe,KAAA;AAEnB,SAAS,KAAK,GAAA,EAAmB;AAC/B,EAAA,SAAA,GAAY,sBAAsB,IAAI,CAAA;AACtC,EAAA,IAAI,YAAA,EAAc;AAClB,EAAA,IAAI,GAAA,GAAM,aAAa,SAAA,EAAW;AAClC,EAAA,UAAA,GAAa,GAAA;AACb,EAAA,KAAA,MAAW,IAAA,IAAQ,aAAa,IAAA,EAAK;AACvC;AAEA,SAAS,UAAU,IAAA,EAAoB;AACrC,EAAA,WAAA,CAAY,IAAI,IAAI,CAAA;AACpB,EAAA,IAAI,WAAA,CAAY,IAAA,KAAS,CAAA,IAAK,SAAA,KAAc,IAAA,EAAM;AAChD,IAAA,UAAA,GAAa,CAAA;AACb,IAAA,SAAA,GAAY,sBAAsB,IAAI,CAAA;AAAA,EACxC;AACF;AAEA,SAAS,YAAY,IAAA,EAAoB;AACvC,EAAA,WAAA,CAAY,OAAO,IAAI,CAAA;AACvB,EAAA,IAAI,WAAA,CAAY,IAAA,KAAS,CAAA,IAAK,SAAA,KAAc,IAAA,EAAM;AAChD,IAAA,oBAAA,CAAqB,SAAS,CAAA;AAC9B,IAAA,SAAA,GAAY,IAAA;AAAA,EACd;AACF;AAGA,IAAI,EAAA,GAAkC,IAAA;AAGtC,SAAS,0BAAA,GAAmD;AAC1D,EAAA,IAAI,IAAI,OAAO,EAAA;AACf,EAAA,EAAA,GAAK,IAAI,oBAAA;AAAA,IACP,CAAC,OAAA,KAAY;AACX,MAAA,KAAA,MAAW,SAAS,OAAA,EAAS;AAC3B,QAAA,MAAM,KAAK,KAAA,CAAM,MAAA;AACjB,QAAA,EAAA,CAAG,SAAA,IAAY;AAAA,MACjB;AAAA,IACF,CAAA;AAAA,IACA,EAAE,WAAW,CAAA;AAAE,GACjB;AACA,EAAA,OAAO,EAAA;AACT;AAGA,IAAI,mBAAA,GAAsB,KAAA;AAC1B,SAAS,uBAAA,GAAgC;AACvC,EAAA,IAAI,mBAAA,IAAuB,OAAO,QAAA,KAAa,WAAA,EAAa;AAC5D,EAAA,mBAAA,GAAsB,IAAA;AACtB,EAAA,QAAA,CAAS,gBAAA,CAAiB,oBAAoB,MAAM;AAClD,IAAA,YAAA,GAAe,SAAS,eAAA,KAAoB,QAAA;AAC5C,IAAA,IAAI,CAAC,cAAc,UAAA,GAAa,CAAA;AAAA,EAClC,CAAC,CAAA;AACH;AAMA,IAAM,cAAA,GAAiB,4BAAA;AACvB,IAAM,QAAA,GAAW,qBAAA;AAEjB,IAAM,aAAA,GAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA,EASnB,cAAc,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAA,EASb,cAAc,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAQlB,IAAI,cAAA,GAAiB,KAAA;AAErB,SAAS,qBAAA,GAA8B;AACrC,EAAA,IAAI,cAAA,IAAkB,OAAO,QAAA,KAAa,WAAA,EAAa;AACvD,EAAA,IAAI,QAAA,CAAS,cAAA,CAAe,QAAQ,CAAA,EAAG;AACrC,IAAA,cAAA,GAAiB,IAAA;AACjB,IAAA;AAAA,EACF;AACA,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AACzC,EAAA,EAAA,CAAG,EAAA,GAAK,QAAA;AACR,EAAA,EAAA,CAAG,WAAA,GAAc,aAAA;AACjB,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,EAAE,CAAA;AAC5B,EAAA,cAAA,GAAiB,IAAA;AACnB;AAoBO,SAAS,cAAA,CAAe;AAAA,EAC7B,IAAA;AAAA,EACA,IAAA,GAAO,EAAA;AAAA,EACP,MAAA,GAAS,QAAA;AAAA,EACT,OAAA,GAAU,KAAA;AAAA,EACV,iBAAA,GAAoB,EAAA;AAAA,EACpB,SAAA;AAAA,EACA;AACF,CAAA,EAAwB;AACtB,EAAA,MAAM,SAAA,GAAY,OAA0B,IAAI,CAAA;AAChD,EAAA,MAAM,UAAA,GAAa,OAAwB,IAAI,CAAA;AAC/C,EAAA,MAAM,QAAA,GAAW,OAAe,CAAC,CAAA;AACjC,EAAA,MAAM,UAAA,GAAa,OAAO,IAAI,CAAA;AAG9B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,qBAAA,EAAsB;AACtB,MAAA,uBAAA,EAAwB;AAAA,IAC1B;AAAA,EACF,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAGZ,EAAA,MAAM,IAAA,GAAO,YAAY,MAAM;AAC7B,IAAA,MAAM,SAAS,SAAA,CAAU,OAAA;AACzB,IAAA,IAAI,CAAC,MAAA,IAAU,CAAC,UAAA,CAAW,OAAA,EAAS;AACpC,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,UAAA,CAAW,IAAI,CAAA;AAClC,IAAA,IAAI,CAAC,GAAA,EAAK;AAEV,IAAA,IAAI,SAAS,OAAA,KAAY,CAAA,EAAG,QAAA,CAAS,OAAA,GAAU,YAAY,GAAA,EAAI;AAC/D,IAAA,MAAM,OAAA,GAAA,CAAW,WAAA,CAAY,GAAA,EAAI,GAAI,SAAS,OAAA,IAAW,GAAA;AACzD,IAAA,MAAM,IAAA,GAAO,OAAA,IAAY,CAAA,GAAI,IAAA,CAAK,EAAA,GAAM,iBAAA,CAAA;AAExC,IAAA,MAAM,EAAE,MAAA,EAAO,GAAI,eAAA,CAAgB,IAAI,CAAA;AACvC,IAAA,MAAM,KAAA,GAAQ,mBAAA,CAAoB,IAAA,EAAM,WAAW,CAAA;AACnD,IAAA,iBAAA,CAAkB,KAAK,KAAA,EAAO,MAAA,CAAO,CAAC,CAAA,EAAG,aAAa,IAAI,CAAA;AAAA,EAC5D,CAAA,EAAG,CAAC,IAAA,EAAM,iBAAiB,CAAC,CAAA;AAE5B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,SAAS,SAAA,CAAU,OAAA;AACzB,IAAA,IAAI,CAAC,MAAA,EAAQ;AACb,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,UAAA,CAAW,IAAI,CAAA;AAClC,IAAA,IAAI,CAAC,GAAA,EAAK;AAEV,IAAA,MAAM,iBACJ,OAAO,MAAA,KAAW,eAClB,MAAA,CAAO,UAAA,GAAa,kCAAkC,CAAA,CAAE,OAAA;AAE1D,IAAA,IAAI,CAAC,WAAW,cAAA,EAAgB;AAC9B,MAAA,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAA,EAAG,WAAA,EAAa,WAAW,CAAA;AAC5C,MAAA,gBAAA,CAAiB,GAAA,EAAK,MAAM,WAAW,CAAA;AACvC,MAAA;AAAA,IACF;AAGA,IAAA,QAAA,CAAS,OAAA,GAAU,CAAA;AACnB,IAAA,MAAM,EAAE,MAAA,EAAO,GAAI,eAAA,CAAgB,IAAI,CAAA;AACvC,IAAA,MAAM,KAAA,GAAQ,mBAAA,CAAoB,IAAA,EAAM,WAAW,CAAA;AACnD,IAAA,iBAAA,CAAkB,KAAK,KAAA,EAAO,MAAA,CAAO,CAAC,CAAA,EAAG,aAAa,CAAC,CAAA;AAEvD,IAAA,SAAA,CAAU,IAAI,CAAA;AAEd,IAAA,OAAO,MAAM;AACX,MAAA,WAAA,CAAY,IAAI,CAAA;AAAA,IAClB,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,IAAA,EAAM,OAAA,EAAS,IAAI,CAAC,CAAA;AAGxB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,UAAU,UAAA,CAAW,OAAA;AAC3B,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,OAAA,EAAS;AAE1B,IAAA,MAAM,WAAW,0BAAA,EAA2B;AAE5C,IAAC,OAAA,CAAqD,YAAY,MAAM;AACtE,MAAA,UAAA,CAAW,OAAA,GAAU,IAAA;AAAA,IACvB,CAAA;AACA,IAAA,QAAA,CAAS,QAAQ,OAAO,CAAA;AAExB,IAAA,OAAO,MAAM;AACX,MAAA,QAAA,CAAS,UAAU,OAAO,CAAA;AAC1B,MAAA,UAAA,CAAW,OAAA,GAAU,KAAA;AACrB,MAAA,OAAQ,OAAA,CAAqD,SAAA;AAAA,IAC/D,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,MAAM,MAAA,GAAS,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,IAAA,GAAO,aAAa,CAAC,CAAA;AAI3D,EAAA,MAAM,WAAA,GAA6B;AAAA,IACjC,KAAA,EAAO,MAAA;AAAA,IACP,MAAA,EAAQ,MAAA;AAAA,IACR,OAAA,EAAS,OAAA;AAAA,IACT,MAAA,EAAQ,QAAQ,MAAM,CAAA,GAAA,CAAA;AAAA,IACtB,SAAA,EAAW;AAAA,GACb;AACA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,WAAA,CAAY,iBAAA,GAAoB,GAAG,iBAAiB,CAAA,CAAA,CAAA;AAAA,EACtD;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,UAAA;AAAA,MACL,SAAA;AAAA,MACA,KAAA,EAAO;AAAA,QACL,OAAA,EAAS,cAAA;AAAA,QACT,QAAA,EAAU,QAAA;AAAA,QACV,YAAA,EAAc,MAAA;AAAA,QACd,KAAA,EAAO,IAAA;AAAA,QACP,MAAA,EAAQ,IAAA;AAAA,QACR,GAAG;AAAA,OACL;AAAA,MAEA,QAAA,kBAAA,GAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UACC,GAAA,EAAK,SAAA;AAAA,UACL,IAAA,EAAK,KAAA;AAAA,UACL,YAAA,EAAY,4BAA4B,IAAI,CAAA,CAAA;AAAA,UAC5C,KAAA,EAAO,WAAA;AAAA,UACP,MAAA,EAAQ,WAAA;AAAA,UACR,SAAA,EAAW,UAAU,cAAA,GAAiB,MAAA;AAAA,UACtC,KAAA,EAAO;AAAA;AAAA;AACT;AAAA,GACF;AAEJ","file":"index.js","sourcesContent":["/**\n * Gradient engine for @apollo-deploy/avatars.\n *\n * Framework-agnostic mesh-gradient avatar generator. Every seed (string or\n * number) deterministically produces a unique gradient — no stored images,\n * no network. Pure palette/RNG core plus optional Canvas2D render helpers.\n */\n\nexport type Harmony =\n \"analogous\" | \"triadic\" | \"splitComplementary\" | \"tetradic\" | \"complementary\";\n\nexport interface GradientPalette {\n /** The numeric seed the palette was derived from. */\n seed: number;\n /** Hex color stops used to paint the mesh. */\n colors: string[];\n /** Which color-harmony rule produced the hues. */\n harmony: Harmony;\n}\n\nconst HARMONY_TYPES: Harmony[] = [\n \"analogous\",\n \"triadic\",\n \"splitComplementary\",\n \"tetradic\",\n \"complementary\",\n];\n\nconst GOLDEN_ANGLE = 137.5;\n\n/** Default blur radius as a fraction of the rendered dimension. */\nexport const DEFAULT_BLUR_FRACTION = 0.06;\n\nfunction seededRandom(seed: number): () => number {\n let s = seed;\n return () => {\n s += 0x6d2b79f5;\n let t = s;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\nfunction hslToHex(h: number, s: number, l: number): string {\n h = ((h % 360) + 360) % 360;\n s = Math.max(0, Math.min(100, s)) / 100;\n l = Math.max(0, Math.min(100, l)) / 100;\n\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n const m = l - c / 2;\n\n let r = 0;\n let g = 0;\n let b = 0;\n if (h < 60) {\n r = c;\n g = x;\n } else if (h < 120) {\n r = x;\n g = c;\n } else if (h < 180) {\n g = c;\n b = x;\n } else if (h < 240) {\n g = x;\n b = c;\n } else if (h < 300) {\n r = x;\n b = c;\n } else {\n r = c;\n b = x;\n }\n\n const toHex = (n: number) => {\n const hex = Math.round((n + m) * 255).toString(16);\n return hex.length === 1 ? `0${hex}` : hex;\n };\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();\n}\n\nfunction harmonyHues(baseHue: number, harmony: Harmony): number[] {\n switch (harmony) {\n case \"analogous\":\n return [baseHue, baseHue + 30, baseHue + 60, baseHue - 30];\n case \"triadic\":\n return [baseHue, baseHue + 120, baseHue + 240];\n case \"splitComplementary\":\n return [baseHue, baseHue + 150, baseHue + 210];\n case \"tetradic\":\n return [baseHue, baseHue + 90, baseHue + 180, baseHue + 270];\n case \"complementary\":\n return [baseHue, baseHue + 180, baseHue + 20, baseHue + 200];\n }\n}\n\n/**\n * Stable string → 32-bit unsigned hash (FNV-1a + bit-mixing avalanche).\n * Uses the full uint32 range as a seed so similar strings diverge fully.\n */\nexport function seedFromString(input: string): number {\n let h = 2166136261 >>> 0;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = Math.imul(h, 16777619) >>> 0;\n }\n h ^= h >>> 16;\n h = Math.imul(h, 0x7feb352d) >>> 0;\n h ^= h >>> 15;\n h = Math.imul(h, 0x846ca68b) >>> 0;\n h ^= h >>> 16;\n return h >>> 0;\n}\n\n/** Normalize a string or number seed to the numeric seed used internally. */\nexport function toSeed(seed: number | string): number {\n if (typeof seed === \"number\") return seed;\n return seedFromString(seed);\n}\n\n/** Derive the deterministic color palette for a seed. */\nexport function generatePalette(seed: number | string): GradientPalette {\n const s = toSeed(seed);\n const random = seededRandom(s);\n const baseHue = (s * GOLDEN_ANGLE) % 360;\n const harmonyIndex = Math.floor(random() * HARMONY_TYPES.length);\n const harmony = HARMONY_TYPES[harmonyIndex];\n const hues = harmonyHues(baseHue, harmony);\n const colors = hues.map((hue) => {\n const saturation = 75 + random() * 25;\n const lightness = 50 + random() * 20;\n return hslToHex(hue, saturation, lightness);\n });\n return { seed: s, colors, harmony };\n}\n\n/**\n * Minimal Canvas2D context surface the renderer needs. Both\n * `HTMLCanvasElement` and `OffscreenCanvas` 2D contexts satisfy it.\n */\nexport type GradientContext = {\n fillStyle: string | CanvasGradient | CanvasPattern;\n globalCompositeOperation: GlobalCompositeOperation;\n fillRect(x: number, y: number, w: number, h: number): void;\n createRadialGradient(\n x0: number,\n y0: number,\n r0: number,\n x1: number,\n y1: number,\n r1: number,\n ): CanvasGradient;\n};\n\ninterface AnimatedSpot {\n x: number;\n y: number;\n radius: number;\n color: string;\n /** Unique phase offset (0–1) so each spot drifts independently. */\n phaseX: number;\n phaseY: number;\n /** How far the spot can drift from its origin (fraction of size). */\n amplitude: number;\n}\n\n/**\n * Draw the mesh gradient for `seed` into `ctx` at `size` x `size`.\n * The caller is responsible for any blur — apply `filter: blur(…)` on the\n * displayed canvas (≈6% of the rendered dimension) for the signature look,\n * or use {@link renderGradient} / {@link gradientToDataURL} which bake it in.\n */\nexport function drawMeshGradient(\n ctx: GradientContext,\n seed: number | string,\n size: number,\n): void {\n const s = toSeed(seed);\n const { colors } = generatePalette(s);\n const random = seededRandom(s * 12345);\n\n ctx.fillStyle = colors[0];\n ctx.fillRect(0, 0, size, size);\n\n const numSpots = 8 + Math.floor(random() * 5);\n const spots: Array<{ x: number; y: number; radius: number; color: string }> =\n [];\n\n for (let i = 0; i < numSpots; i++) {\n const angle = random() * Math.PI * 2;\n const distance = random() * size * 0.4;\n const centerX = size / 2 + Math.cos(angle) * distance;\n const centerY = size / 2 + Math.sin(angle) * distance;\n spots.push({\n x: centerX + (random() - 0.5) * size * 0.3,\n y: centerY + (random() - 0.5) * size * 0.3,\n radius: size * (0.3 + random() * 0.4),\n color: colors[i % colors.length],\n });\n }\n\n spots.sort((a, b) => b.radius - a.radius);\n\n ctx.globalCompositeOperation = \"source-over\";\n for (const spot of spots) {\n const g = ctx.createRadialGradient(\n spot.x,\n spot.y,\n 0,\n spot.x,\n spot.y,\n spot.radius,\n );\n g.addColorStop(0, `${spot.color}FF`);\n g.addColorStop(0.3, `${spot.color}DD`);\n g.addColorStop(0.6, `${spot.color}88`);\n g.addColorStop(1, `${spot.color}00`);\n ctx.fillStyle = g;\n ctx.fillRect(0, 0, size, size);\n }\n\n const hx = size * 0.3 + random() * size * 0.2;\n const hy = size * 0.3 + random() * size * 0.2;\n const hg = ctx.createRadialGradient(hx, hy, 0, hx, hy, size * 0.3);\n hg.addColorStop(0, \"rgba(255,255,255,0.15)\");\n hg.addColorStop(1, \"rgba(255,255,255,0)\");\n ctx.fillStyle = hg;\n ctx.fillRect(0, 0, size, size);\n}\n\n/**\n * Compute animated spot data for a seed — call once, then pass the result\n * and a changing `time` (elapsed seconds) to {@link drawAnimatedSpots}.\n * This separates setup from the per-frame hot path so the palette and\n * random geometry are only derived once.\n */\nexport function createAnimatedSpots(\n seed: number | string,\n size: number,\n): AnimatedSpot[] {\n const s = toSeed(seed);\n const { colors } = generatePalette(s);\n const random = seededRandom(s * 12345);\n\n const numSpots = 8 + Math.floor(random() * 5);\n const spots: AnimatedSpot[] = [];\n\n for (let i = 0; i < numSpots; i++) {\n const angle = random() * Math.PI * 2;\n const distance = random() * size * 0.4;\n const centerX = size / 2 + Math.cos(angle) * distance;\n const centerY = size / 2 + Math.sin(angle) * distance;\n spots.push({\n x: centerX + (random() - 0.5) * size * 0.3,\n y: centerY + (random() - 0.5) * size * 0.3,\n radius: size * (0.3 + random() * 0.4),\n color: colors[i % colors.length],\n phaseX: random(),\n phaseY: random(),\n amplitude: 0.03 + random() * 0.06,\n });\n }\n\n spots.sort((a, b) => b.radius - a.radius);\n return spots;\n}\n\n/**\n * Draw animated mesh spots into `ctx`. Call on every frame with a monotonically\n * increasing `time` (seconds). Each spot orbits its anchor on a tiny Lissajous\n * path, scaled so the composite result always covers the canvas.\n */\nexport function drawAnimatedSpots(\n ctx: GradientContext,\n spots: AnimatedSpot[],\n backgroundColor: string,\n size: number,\n time: number,\n): void {\n ctx.fillStyle = backgroundColor;\n ctx.fillRect(0, 0, size, size);\n\n ctx.globalCompositeOperation = \"source-over\";\n for (const spot of spots) {\n // Lissajous drift: each spot moves on its own tiny elliptical path.\n // Different periods per axis keep it from looking like a uniform rotation.\n const periodX = 12 + spot.phaseX * 8;\n const periodY = 14 + spot.phaseY * 10;\n const drift = spot.amplitude * size;\n const sx =\n spot.x +\n Math.sin(time * ((2 * Math.PI) / periodX) + spot.phaseX * Math.PI * 2) *\n drift;\n const sy =\n spot.y +\n Math.cos(time * ((2 * Math.PI) / periodY) + spot.phaseY * Math.PI * 2) *\n drift;\n\n const g = ctx.createRadialGradient(sx, sy, 0, sx, sy, spot.radius);\n g.addColorStop(0, `${spot.color}FF`);\n g.addColorStop(0.3, `${spot.color}DD`);\n g.addColorStop(0.6, `${spot.color}88`);\n g.addColorStop(1, `${spot.color}00`);\n ctx.fillStyle = g;\n ctx.fillRect(0, 0, size, size);\n }\n\n // Subtle highlight that drifts with time.\n const hSize = size * 0.3;\n const hx = size * 0.35 + Math.sin(time * 0.3) * size * 0.1;\n const hy = size * 0.35 + Math.cos(time * 0.4) * size * 0.1;\n const hg = ctx.createRadialGradient(hx, hy, 0, hx, hy, hSize);\n hg.addColorStop(0, \"rgba(255,255,255,0.15)\");\n hg.addColorStop(1, \"rgba(255,255,255,0)\");\n ctx.fillStyle = hg;\n ctx.fillRect(0, 0, size, size);\n}\n\nexport interface RenderOptions {\n /**\n * Blur radius in pixels. Defaults to ~6% of the canvas size for the\n * signature soft look. Pass `0` to disable.\n */\n blur?: number;\n}\n\nfunction blurFor(size: number, blur?: number): number {\n if (blur === 0) return 0;\n return blur ?? Math.round(size * DEFAULT_BLUR_FRACTION);\n}\n\n/**\n * Render a seed's gradient into an existing canvas, baking in the soft blur.\n * Draws at the canvas's current `width`/`height`. Browser/OffscreenCanvas only.\n */\nexport function renderGradient(\n canvas: HTMLCanvasElement | OffscreenCanvas,\n seed: number | string,\n options: RenderOptions = {},\n): void {\n const size = canvas.width;\n const blur = blurFor(size, options.blur);\n\n const ctx = canvas.getContext(\"2d\") as CanvasRenderingContext2D | null;\n if (!ctx) return;\n\n if (blur <= 0) {\n ctx.clearRect(0, 0, size, size);\n drawMeshGradient(ctx, seed, size);\n return;\n }\n\n // Draw the raw mesh on a scratch canvas, then composite it back with blur\n // scaled up slightly so the soft edges fall outside the frame (no ring).\n const scratch = createCanvas(size, size);\n const sctx = scratch.getContext(\"2d\") as CanvasRenderingContext2D | null;\n if (!sctx) return;\n drawMeshGradient(sctx, seed, size);\n\n const scaleUp = 1 + (blur / size) * 4;\n const dw = size * scaleUp;\n const offset = (dw - size) / 2;\n ctx.clearRect(0, 0, size, size);\n if (supportsCanvasFilter()) {\n ctx.filter = `blur(${blur}px)`;\n ctx.drawImage(scratch as CanvasImageSource, -offset, -offset, dw, dw);\n ctx.filter = \"none\";\n return;\n }\n // 2D-canvas `filter` is a silent no-op on Safari < 17: approximate the\n // gaussian by bouncing through a small canvas. Bilinear resampling on the\n // way down and back up smears by roughly the downscale factor, which is\n // plenty for a mesh that is already smooth gradients.\n const factor = Math.max(2, Math.min(16, blur / 2));\n const sw = Math.max(1, Math.round(size / factor));\n const small = createCanvas(sw, sw);\n const smallCtx = small.getContext(\"2d\") as CanvasRenderingContext2D | null;\n if (!smallCtx) {\n ctx.drawImage(scratch as CanvasImageSource, -offset, -offset, dw, dw);\n return;\n }\n smallCtx.imageSmoothingEnabled = true;\n smallCtx.imageSmoothingQuality = \"high\";\n smallCtx.drawImage(scratch as CanvasImageSource, 0, 0, sw, sw);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = \"high\";\n ctx.drawImage(small as CanvasImageSource, -offset, -offset, dw, dw);\n}\n\n/* Engines that honor 2D-canvas `filter` echo an assigned value back from the\n * property; Safari < 17 ignores the assignment. Probed once, then cached. */\nlet canvasFilterSupport: boolean | null = null;\nfunction supportsCanvasFilter(): boolean {\n if (canvasFilterSupport !== null) return canvasFilterSupport;\n const probe = createCanvas(1, 1).getContext(\n \"2d\",\n ) as CanvasRenderingContext2D | null;\n if (!probe) {\n canvasFilterSupport = false;\n return canvasFilterSupport;\n }\n probe.filter = \"blur(1px)\";\n canvasFilterSupport = probe.filter === \"blur(1px)\";\n return canvasFilterSupport;\n}\n\nfunction createCanvas(\n w: number,\n h: number,\n): HTMLCanvasElement | OffscreenCanvas {\n if (typeof OffscreenCanvas !== \"undefined\") {\n return new OffscreenCanvas(w, h);\n }\n const c = document.createElement(\"canvas\");\n c.width = w;\n c.height = h;\n return c;\n}\n\nexport interface ExportOptions extends RenderOptions {\n /** Output pixel dimensions (square). Default: 512. */\n size?: number;\n /** Image MIME type. Default: \"image/png\". */\n type?: string;\n /** Quality 0–1 for lossy types. Default: 0.92. */\n quality?: number;\n}\n\n/** Render a seed's gradient and return it as a data URL. Browser only. */\nexport function gradientToDataURL(\n seed: number | string,\n options: ExportOptions = {},\n): string {\n const { size = 512, type = \"image/png\", quality = 0.92 } = options;\n const canvas = document.createElement(\"canvas\");\n canvas.width = size;\n canvas.height = size;\n renderGradient(canvas, seed, options);\n return canvas.toDataURL(type, quality);\n}\n\n/** Render a seed's gradient and resolve a Blob (or null). Browser only. */\nexport function gradientToBlob(\n seed: number | string,\n options: ExportOptions = {},\n): Promise<Blob | null> {\n const { size = 512, type = \"image/png\", quality = 0.92 } = options;\n const canvas = document.createElement(\"canvas\");\n canvas.width = size;\n canvas.height = size;\n renderGradient(canvas, seed, options);\n return new Promise((resolve) => canvas.toBlob(resolve, type, quality));\n}\n","import type { CSSProperties } from \"react\";\nimport { useCallback, useEffect, useRef } from \"react\";\nimport {\n createAnimatedSpots,\n drawAnimatedSpots,\n drawMeshGradient,\n generatePalette,\n} from \"./engine\";\n\nexport interface GradientAvatarProps {\n /** Any string or number — each unique seed produces a unique gradient. */\n seed: number | string;\n /** Rendered size in pixels. Default: 32. */\n size?: number;\n /**\n * Corner radius. Number = pixels, string = any CSS length.\n * Defaults to a full circle; pass `0` for a square or e.g. `12` for a\n * rounded square. Default: \"9999px\".\n */\n radius?: number | string;\n /**\n * Animate the gradient with a hybrid approach: CSS keyframes handle the\n * smooth drift (GPU-only, zero JS per frame), and the canvas content is\n * repainted at a low cadence (~15 fps) so the mesh spots evolve slowly.\n * A single shared rAF loop drives all animated instances; the ticker stops\n * when nothing is on screen or the tab is hidden. Automatically disabled\n * for users who request reduced motion. Default: `false`.\n */\n animate?: boolean;\n /**\n * Duration of one full drift cycle in seconds. Slower is subtler and\n * cheaper. Only applies when {@link animate} is `true`. Default: `16`.\n */\n animationDuration?: number;\n /** Additional CSS classes on the wrapper. */\n className?: string;\n /** Extra inline styles merged onto the wrapper. */\n style?: CSSProperties;\n}\n\n/* ------------------------------------------------------------------ */\n/* Shared animation infrastructure (module-level singletons) */\n/* ------------------------------------------------------------------ */\n\n/** Internal render resolution. */\nconst RENDER_SIZE = 256;\n/** Blur radius as a fraction of display size. */\nconst BLUR_FRACTION = 0.06;\n/** Canvas redraw interval (ms). ~15 fps keeps overhead very low. */\nconst REDRAW_MS = 67;\n\ntype DrawFn = () => void;\n\n/** Subscribers to the shared animation ticker. */\nconst subscribers = new Set<DrawFn>();\nlet tickerRaf: number | null = null;\nlet tickerLast = 0;\nlet tickerPaused = false;\n\nfunction tick(now: number): void {\n tickerRaf = requestAnimationFrame(tick);\n if (tickerPaused) return;\n if (now - tickerLast < REDRAW_MS) return;\n tickerLast = now;\n for (const draw of subscribers) draw();\n}\n\nfunction subscribe(draw: DrawFn): void {\n subscribers.add(draw);\n if (subscribers.size === 1 && tickerRaf === null) {\n tickerLast = 0;\n tickerRaf = requestAnimationFrame(tick);\n }\n}\n\nfunction unsubscribe(draw: DrawFn): void {\n subscribers.delete(draw);\n if (subscribers.size === 0 && tickerRaf !== null) {\n cancelAnimationFrame(tickerRaf);\n tickerRaf = null;\n }\n}\n\n/* Shared IntersectionObserver so we don't create one per avatar. */\nlet io: IntersectionObserver | null = null;\nconst visibles = new Set<() => void>();\n\nfunction ensureIntersectionObserver(): IntersectionObserver {\n if (io) return io;\n io = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n const el = entry.target as HTMLElement & { __oaOnVis?: () => void };\n el.__oaOnVis?.();\n }\n },\n { threshold: 0 },\n );\n return io;\n}\n\n/* Shared visibilitychange handler. */\nlet visHandlerInstalled = false;\nfunction ensureVisibilityHandler(): void {\n if (visHandlerInstalled || typeof document === \"undefined\") return;\n visHandlerInstalled = true;\n document.addEventListener(\"visibilitychange\", () => {\n tickerPaused = document.visibilityState === \"hidden\";\n if (!tickerPaused) tickerLast = 0;\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* CSS keyframes (injected once, lazily) */\n/* ------------------------------------------------------------------ */\n\nconst ANIMATED_CLASS = \"oa-avatar-canvas--animated\";\nconst STYLE_ID = \"oa-avatar-keyframes\";\n\nconst KEYFRAMES_CSS = `\n@keyframes oa-avatar-drift {\n\t0% { transform: scale(1.25) translate(0%, 0%) rotate(0deg); }\n\t20% { transform: scale(1.30) translate(-3%, 2%) rotate(8deg); }\n\t40% { transform: scale(1.22) translate(3%, 3%) rotate(-6deg); }\n\t60% { transform: scale(1.28) translate(2%, -3%) rotate(5deg); }\n\t80% { transform: scale(1.24) translate(-2%, -2%) rotate(-4deg); }\n\t100% { transform: scale(1.25) translate(0%, 0%) rotate(0deg); }\n}\n.${ANIMATED_CLASS} {\n\ttransform-origin: center;\n\twill-change: transform;\n\tanimation-name: oa-avatar-drift;\n\tanimation-timing-function: ease-in-out;\n\tanimation-iteration-count: infinite;\n\tanimation-direction: alternate;\n}\n@media (prefers-reduced-motion: reduce) {\n\t.${ANIMATED_CLASS} {\n\t\tanimation: none;\n\t\ttransform: none;\n\t\twill-change: auto;\n\t}\n}\n`;\n\nlet stylesInjected = false;\n\nfunction ensureAnimationStyles(): void {\n if (stylesInjected || typeof document === \"undefined\") return;\n if (document.getElementById(STYLE_ID)) {\n stylesInjected = true;\n return;\n }\n const el = document.createElement(\"style\");\n el.id = STYLE_ID;\n el.textContent = KEYFRAMES_CSS;\n document.head.appendChild(el);\n stylesInjected = true;\n}\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\n/**\n * Renders a deterministic mesh-gradient avatar on a `<canvas>`.\n * The same seed always produces the same gradient.\n *\n * When `animate` is on the gradient comes alive via two complementary\n * mechanisms:\n *\n * 1. **CSS keyframes** — a slow scale+rotate+translate drift that runs\n * entirely on the GPU compositor thread (zero JavaScript, zero repaint).\n * 2. **Low-cadence canvas repaint** — the mesh spots evolve at ~15 fps\n * through a single shared `requestAnimationFrame` loop, so N animated\n * avatars cost 1 rAF callback total. Drawing pauses when the element is\n * off-screen (`IntersectionObserver`) or the tab is hidden.\n */\nexport function GradientAvatar({\n seed,\n size = 32,\n radius = \"9999px\",\n animate = false,\n animationDuration = 16,\n className,\n style,\n}: GradientAvatarProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const wrapperRef = useRef<HTMLSpanElement>(null);\n const startRef = useRef<number>(0);\n const visibleRef = useRef(true);\n\n /* Inject CSS keyframes once (client-only). */\n useEffect(() => {\n if (animate) {\n ensureAnimationStyles();\n ensureVisibilityHandler();\n }\n }, [animate]);\n\n /* Draw loop: subscribe to the shared ticker, unsubscribe on cleanup. */\n const draw = useCallback(() => {\n const canvas = canvasRef.current;\n if (!canvas || !visibleRef.current) return;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n if (startRef.current === 0) startRef.current = performance.now();\n const elapsed = (performance.now() - startRef.current) / 1000;\n const time = elapsed * ((2 * Math.PI) / animationDuration);\n\n const { colors } = generatePalette(seed);\n const spots = createAnimatedSpots(seed, RENDER_SIZE);\n drawAnimatedSpots(ctx, spots, colors[0], RENDER_SIZE, time);\n }, [seed, animationDuration]);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n const prefersReduced =\n typeof window !== \"undefined\" &&\n window.matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches;\n\n if (!animate || prefersReduced) {\n ctx.clearRect(0, 0, RENDER_SIZE, RENDER_SIZE);\n drawMeshGradient(ctx, seed, RENDER_SIZE);\n return;\n }\n\n /* Paint the initial frame. */\n startRef.current = 0;\n const { colors } = generatePalette(seed);\n const spots = createAnimatedSpots(seed, RENDER_SIZE);\n drawAnimatedSpots(ctx, spots, colors[0], RENDER_SIZE, 0);\n\n subscribe(draw);\n\n return () => {\n unsubscribe(draw);\n };\n }, [seed, animate, draw]);\n\n /* IntersectionObserver: pause redraws when the element scrolls off screen. */\n useEffect(() => {\n const wrapper = wrapperRef.current;\n if (!animate || !wrapper) return;\n\n const observer = ensureIntersectionObserver();\n // biome-ignore lint/suspicious/noAssignInExpressions: intentional pattern for attaching per-element callbacks\n (wrapper as HTMLElement & { __oaOnVis?: () => void }).__oaOnVis = () => {\n visibleRef.current = true;\n };\n observer.observe(wrapper);\n\n return () => {\n observer.unobserve(wrapper);\n visibleRef.current = false;\n delete (wrapper as HTMLElement & { __oaOnVis?: () => void }).__oaOnVis;\n };\n }, [animate]);\n\n const blurPx = Math.max(1, Math.round(size * BLUR_FRACTION));\n\n /* Canvas is always scaled 1.25× so drifting spots never expose the edge;\n * the CSS keyframes add their own scale+rotate+translate on top (class). */\n const canvasStyle: CSSProperties = {\n width: \"100%\",\n height: \"100%\",\n display: \"block\",\n filter: `blur(${blurPx}px)`,\n transform: \"scale(1.25)\",\n };\n if (animate) {\n canvasStyle.animationDuration = `${animationDuration}s`;\n }\n\n return (\n <span\n ref={wrapperRef}\n className={className}\n style={{\n display: \"inline-block\",\n overflow: \"hidden\",\n borderRadius: radius,\n width: size,\n height: size,\n ...style,\n }}\n >\n <canvas\n ref={canvasRef}\n role=\"img\"\n aria-label={`Gradient avatar for seed ${seed}`}\n width={RENDER_SIZE}\n height={RENDER_SIZE}\n className={animate ? ANIMATED_CLASS : undefined}\n style={canvasStyle}\n />\n </span>\n );\n}\n\nexport type {\n ExportOptions,\n GradientPalette,\n Harmony,\n RenderOptions,\n} from \"./engine\";\nexport {\n createAnimatedSpots,\n drawAnimatedSpots,\n drawMeshGradient,\n generatePalette,\n gradientToBlob,\n gradientToDataURL,\n renderGradient,\n seedFromString,\n toSeed,\n} from \"./engine\";\n"]}
|
package/package.json
CHANGED
|
@@ -1,69 +1,69 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
2
|
+
"name": "@apollo-deploy/avatars",
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"description": "React component for generative gradient avatars. Every seed renders a unique, deterministic mesh gradient, with no stored images.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"avatars",
|
|
7
|
+
"generative",
|
|
8
|
+
"generative-avatars",
|
|
9
|
+
"gradient",
|
|
10
|
+
"mesh-gradient",
|
|
11
|
+
"avatar-generator",
|
|
12
|
+
"react",
|
|
13
|
+
"profile-pictures"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://avatars.outpacestudios.com",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/outpacelabs/avatars.git",
|
|
19
|
+
"directory": "packages/avatars"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/outpacelabs/avatars/issues"
|
|
23
|
+
},
|
|
24
|
+
"author": "Outpace Studios (https://outpacestudios.com)",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"main": "./dist/index.cjs",
|
|
29
|
+
"module": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"import": {
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"default": "./dist/index.js"
|
|
36
|
+
},
|
|
37
|
+
"require": {
|
|
38
|
+
"types": "./dist/index.d.cts",
|
|
39
|
+
"default": "./dist/index.cjs"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"./package.json": "./package.json"
|
|
43
|
+
},
|
|
44
|
+
"files": [
|
|
45
|
+
"dist",
|
|
46
|
+
"README.md",
|
|
47
|
+
"LICENSE"
|
|
48
|
+
],
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsup",
|
|
51
|
+
"test": "node test/properties.mjs",
|
|
52
|
+
"dev": "tsup --watch",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"prepublishOnly": "pnpm build"
|
|
55
|
+
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"react": ">=18"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@types/react": "^19.2.10",
|
|
61
|
+
"react": "^19.2.4",
|
|
62
|
+
"tsup": "^8.3.5",
|
|
63
|
+
"typescript": "^5.9.3"
|
|
64
|
+
},
|
|
65
|
+
"publishConfig": {
|
|
66
|
+
"access": "public",
|
|
67
|
+
"registry": "https://registry.npmjs.org/"
|
|
68
|
+
}
|
|
69
69
|
}
|