@leftium/logo 0.0.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/AppLogo.svelte +146 -0
  2. package/dist/AppLogo.svelte.d.ts +7 -0
  3. package/dist/LeftiumLogo.svelte +267 -186
  4. package/dist/LeftiumLogo.svelte.d.ts +1 -0
  5. package/dist/app-logo/color-transform.d.ts +40 -0
  6. package/dist/app-logo/color-transform.js +142 -0
  7. package/dist/app-logo/config-serialization.d.ts +80 -0
  8. package/dist/app-logo/config-serialization.js +489 -0
  9. package/dist/app-logo/defaults.d.ts +60 -0
  10. package/dist/app-logo/defaults.js +55 -0
  11. package/dist/app-logo/generate-favicon-set.d.ts +44 -0
  12. package/dist/app-logo/generate-favicon-set.js +97 -0
  13. package/dist/app-logo/generate-ico.d.ts +18 -0
  14. package/dist/app-logo/generate-ico.js +63 -0
  15. package/dist/app-logo/generate-png.d.ts +16 -0
  16. package/dist/app-logo/generate-png.js +60 -0
  17. package/dist/app-logo/generate-svg.d.ts +9 -0
  18. package/dist/app-logo/generate-svg.js +160 -0
  19. package/dist/app-logo/iconify.d.ts +35 -0
  20. package/dist/app-logo/iconify.js +223 -0
  21. package/dist/app-logo/squircle.d.ts +43 -0
  22. package/dist/app-logo/squircle.js +213 -0
  23. package/dist/app-logo/types.d.ts +39 -0
  24. package/dist/app-logo/types.js +1 -0
  25. package/dist/assets/logo-parts/glow-squircle.svg +44 -0
  26. package/dist/index.d.ts +8 -3
  27. package/dist/index.js +9 -3
  28. package/dist/leftium-logo/generate-svg.d.ts +29 -0
  29. package/dist/leftium-logo/generate-svg.js +470 -0
  30. package/dist/tooltip.d.ts +18 -0
  31. package/dist/tooltip.js +38 -0
  32. package/dist/webgl-ripples/webgl-ripples.d.ts +0 -4
  33. package/dist/webgl-ripples/webgl-ripples.js +1 -1
  34. package/package.json +35 -20
@@ -0,0 +1,470 @@
1
+ /**
2
+ * Generate a self-contained static SVG for the Leftium logo.
3
+ *
4
+ * Replicates the LeftiumLogo.svelte layout geometry as pure SVG,
5
+ * with all assets inlined as base64 data URIs.
6
+ */
7
+ // ─── Raw SVG asset strings (imported at build time via ?raw) ─────────────────
8
+ import squareSvgRaw from '../assets/logo-parts/square.svg?raw';
9
+ import glowSvgRaw from '../assets/logo-parts/glow.svg?raw';
10
+ import glowSquircleSvgRaw from '../assets/logo-parts/glow-squircle.svg?raw';
11
+ import ligatureSvgRaw from '../assets/logo-parts/ligature.svg?raw';
12
+ import shadowSvgRaw from '../assets/logo-parts/shadow.svg?raw';
13
+ // ─── Geometry constants (mirrored from LeftiumLogo.svelte) ───────────────────
14
+ // The inner grid (grid-logo) is always 532×532 in "native" units.
15
+ const GRID = 532;
16
+ // Glow is 647×647, centered on the square (overflows by 57.5 on each side)
17
+ const GLOW_W = 647;
18
+ const GLOW_OFFSET = -57.5; // (647-532)/2 = 57.5
19
+ // Ligature positioning — square mode
20
+ const LIG_ORIG_W = 440;
21
+ const LIG_ORIG_H = 666;
22
+ const LIG_ORIG_L = 133.5;
23
+ const LIG_ORIG_T = -65.75;
24
+ const BLUR_PAD_ORIG = 50;
25
+ // Ligature positioning — squircle mode
26
+ const LIG_SQRC_W = 425.2;
27
+ const LIG_SQRC_H = 643.6;
28
+ const LIG_SQRC_L = 129.5;
29
+ const LIG_SQRC_T = -47.6;
30
+ const BLUR_PAD_SQRC = 48.3;
31
+ // Bounding box scaling factors for the grid within the output canvas.
32
+ // CSS: grid width = canvas / scale_factor, centered.
33
+ const BBOX_SCALE = {
34
+ square: 1,
35
+ default: 1.2519,
36
+ encircled: 1.5037,
37
+ cropped: 1 // handled separately — non-square canvas
38
+ };
39
+ // Squircle encircled gets an extra 1.04× scale-up
40
+ const ENCIRCLED_SQUIRCLE_EXTRA = 1.04;
41
+ // Cropped mode dimensions (from LeftiumLogo.svelte CSS comments)
42
+ // Container aspect ratio ≈ 0.8906 (height/width = 1/0.8906)
43
+ const CROPPED_ASPECT = 1 / 0.8906; // height = width * this
44
+ const CROPPED_GRID_W_FRAC = 0.782; // grid is 78.2% of container width
45
+ const CROPPED_LEFT_FRAC = 0.0844; // 8.44% from left
46
+ const CROPPED_TOP_FRAC = 0.1523; // 15.23% from top
47
+ // Squircle clip polygon points (percentages → will be scaled to px).
48
+ // This is the same polygon as LeftiumLogo.svelte SQUIRCLE_CLIP but converted
49
+ // to absolute pixel coordinates at render time.
50
+ const SQUIRCLE_POLY_PCTS = [
51
+ [50, 0],
52
+ [53.05, 0],
53
+ [55.96, 0],
54
+ [58.74, 0],
55
+ [61.38, 0],
56
+ [63.89, 0],
57
+ [66.27, 0],
58
+ [68.54, 0.01],
59
+ [70.69, 0.01],
60
+ [72.73, 0.02],
61
+ [74.66, 0.03],
62
+ [76.48, 0.04],
63
+ [78.21, 0.06],
64
+ [79.84, 0.09],
65
+ [81.37, 0.11],
66
+ [82.82, 0.15],
67
+ [84.18, 0.2],
68
+ [85.46, 0.25],
69
+ [86.66, 0.31],
70
+ [87.78, 0.39],
71
+ [88.83, 0.48],
72
+ [89.81, 0.58],
73
+ [90.73, 0.7],
74
+ [91.58, 0.83],
75
+ [92.37, 0.99],
76
+ [93.11, 1.16],
77
+ [93.79, 1.36],
78
+ [94.41, 1.58],
79
+ [94.99, 1.83],
80
+ [95.53, 2.11],
81
+ [96.02, 2.41],
82
+ [96.47, 2.75],
83
+ [96.88, 3.13],
84
+ [97.25, 3.53],
85
+ [97.59, 3.98],
86
+ [97.89, 4.47],
87
+ [98.17, 5.01],
88
+ [98.42, 5.59],
89
+ [98.64, 6.21],
90
+ [98.84, 6.89],
91
+ [99.01, 7.63],
92
+ [99.17, 8.42],
93
+ [99.3, 9.27],
94
+ [99.42, 10.19],
95
+ [99.52, 11.17],
96
+ [99.61, 12.22],
97
+ [99.69, 13.34],
98
+ [99.75, 14.54],
99
+ [99.8, 15.82],
100
+ [99.85, 17.18],
101
+ [99.89, 18.63],
102
+ [99.91, 20.16],
103
+ [99.94, 21.79],
104
+ [99.96, 23.52],
105
+ [99.97, 25.34],
106
+ [99.98, 27.27],
107
+ [99.99, 29.31],
108
+ [99.99, 31.46],
109
+ [100, 33.73],
110
+ [100, 36.11],
111
+ [100, 38.62],
112
+ [100, 41.26],
113
+ [100, 44.04],
114
+ [100, 46.95],
115
+ [100, 50],
116
+ [100, 50],
117
+ [100, 53.05],
118
+ [100, 55.96],
119
+ [100, 58.74],
120
+ [100, 61.38],
121
+ [100, 63.89],
122
+ [100, 66.27],
123
+ [99.99, 68.54],
124
+ [99.99, 70.69],
125
+ [99.98, 72.73],
126
+ [99.97, 74.66],
127
+ [99.96, 76.48],
128
+ [99.94, 78.21],
129
+ [99.91, 79.84],
130
+ [99.89, 81.37],
131
+ [99.85, 82.82],
132
+ [99.8, 84.18],
133
+ [99.75, 85.46],
134
+ [99.69, 86.66],
135
+ [99.61, 87.78],
136
+ [99.52, 88.83],
137
+ [99.42, 89.81],
138
+ [99.3, 90.73],
139
+ [99.17, 91.58],
140
+ [99.01, 92.37],
141
+ [98.84, 93.11],
142
+ [98.64, 93.79],
143
+ [98.42, 94.41],
144
+ [98.17, 94.99],
145
+ [97.89, 95.53],
146
+ [97.59, 96.02],
147
+ [97.25, 96.47],
148
+ [96.88, 96.88],
149
+ [96.47, 97.25],
150
+ [96.02, 97.59],
151
+ [95.53, 97.89],
152
+ [94.99, 98.17],
153
+ [94.41, 98.42],
154
+ [93.79, 98.64],
155
+ [93.11, 98.84],
156
+ [92.37, 99.01],
157
+ [91.58, 99.17],
158
+ [90.73, 99.3],
159
+ [89.81, 99.42],
160
+ [88.83, 99.52],
161
+ [87.78, 99.61],
162
+ [86.66, 99.69],
163
+ [85.46, 99.75],
164
+ [84.18, 99.8],
165
+ [82.82, 99.85],
166
+ [81.37, 99.89],
167
+ [79.84, 99.91],
168
+ [78.21, 99.94],
169
+ [76.48, 99.96],
170
+ [74.66, 99.97],
171
+ [72.73, 99.98],
172
+ [70.69, 99.99],
173
+ [68.54, 99.99],
174
+ [66.27, 100],
175
+ [63.89, 100],
176
+ [61.38, 100],
177
+ [58.74, 100],
178
+ [55.96, 100],
179
+ [53.05, 100],
180
+ [50, 100],
181
+ [50, 100],
182
+ [46.95, 100],
183
+ [44.04, 100],
184
+ [41.26, 100],
185
+ [38.62, 100],
186
+ [36.11, 100],
187
+ [33.73, 100],
188
+ [31.46, 99.99],
189
+ [29.31, 99.99],
190
+ [27.27, 99.98],
191
+ [25.34, 99.97],
192
+ [23.52, 99.96],
193
+ [21.79, 99.94],
194
+ [20.16, 99.91],
195
+ [18.63, 99.89],
196
+ [17.18, 99.85],
197
+ [15.82, 99.8],
198
+ [14.54, 99.75],
199
+ [13.34, 99.69],
200
+ [12.22, 99.61],
201
+ [11.17, 99.52],
202
+ [10.19, 99.42],
203
+ [9.27, 99.3],
204
+ [8.42, 99.17],
205
+ [7.63, 99.01],
206
+ [6.89, 98.84],
207
+ [6.21, 98.64],
208
+ [5.59, 98.42],
209
+ [5.01, 98.17],
210
+ [4.47, 97.89],
211
+ [3.98, 97.59],
212
+ [3.53, 97.25],
213
+ [3.13, 96.88],
214
+ [2.75, 96.47],
215
+ [2.41, 96.02],
216
+ [2.11, 95.53],
217
+ [1.83, 94.99],
218
+ [1.58, 94.41],
219
+ [1.36, 93.79],
220
+ [1.16, 93.11],
221
+ [0.99, 92.37],
222
+ [0.83, 91.58],
223
+ [0.7, 90.73],
224
+ [0.58, 89.81],
225
+ [0.48, 88.83],
226
+ [0.39, 87.78],
227
+ [0.31, 86.66],
228
+ [0.25, 85.46],
229
+ [0.2, 84.18],
230
+ [0.15, 82.82],
231
+ [0.11, 81.37],
232
+ [0.09, 79.84],
233
+ [0.06, 78.21],
234
+ [0.04, 76.48],
235
+ [0.03, 74.66],
236
+ [0.02, 72.73],
237
+ [0.01, 70.69],
238
+ [0.01, 68.54],
239
+ [0, 66.27],
240
+ [0, 63.89],
241
+ [0, 61.38],
242
+ [0, 58.74],
243
+ [0, 55.96],
244
+ [0, 53.05],
245
+ [0, 50],
246
+ [0, 50],
247
+ [0, 46.95],
248
+ [0, 44.04],
249
+ [0, 41.26],
250
+ [0, 38.62],
251
+ [0, 36.11],
252
+ [0, 33.73],
253
+ [0.01, 31.46],
254
+ [0.01, 29.31],
255
+ [0.02, 27.27],
256
+ [0.03, 25.34],
257
+ [0.04, 23.52],
258
+ [0.06, 21.79],
259
+ [0.09, 20.16],
260
+ [0.11, 18.63],
261
+ [0.15, 17.18],
262
+ [0.2, 15.82],
263
+ [0.25, 14.54],
264
+ [0.31, 13.34],
265
+ [0.39, 12.22],
266
+ [0.48, 11.17],
267
+ [0.58, 10.19],
268
+ [0.7, 9.27],
269
+ [0.83, 8.42],
270
+ [0.99, 7.63],
271
+ [1.16, 6.89],
272
+ [1.36, 6.21],
273
+ [1.58, 5.59],
274
+ [1.83, 5.01],
275
+ [2.11, 4.47],
276
+ [2.41, 3.98],
277
+ [2.75, 3.53],
278
+ [3.13, 3.13],
279
+ [3.53, 2.75],
280
+ [3.98, 2.41],
281
+ [4.47, 2.11],
282
+ [5.01, 1.83],
283
+ [5.59, 1.58],
284
+ [6.21, 1.36],
285
+ [6.89, 1.16],
286
+ [7.63, 0.99],
287
+ [8.42, 0.83],
288
+ [9.27, 0.7],
289
+ [10.19, 0.58],
290
+ [11.17, 0.48],
291
+ [12.22, 0.39],
292
+ [13.34, 0.31],
293
+ [14.54, 0.25],
294
+ [15.82, 0.2],
295
+ [17.18, 0.15],
296
+ [18.63, 0.11],
297
+ [20.16, 0.09],
298
+ [21.79, 0.06],
299
+ [23.52, 0.04],
300
+ [25.34, 0.03],
301
+ [27.27, 0.02],
302
+ [29.31, 0.01],
303
+ [31.46, 0.01],
304
+ [33.73, 0],
305
+ [36.11, 0],
306
+ [38.62, 0],
307
+ [41.26, 0],
308
+ [44.04, 0],
309
+ [46.95, 0],
310
+ [50, 0]
311
+ ];
312
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
313
+ /** Encode a raw SVG string as a base64 data URI */
314
+ function svgToDataUri(svgRaw) {
315
+ // btoa works in both browser and modern Node/Bun
316
+ const b64 = btoa(unescape(encodeURIComponent(svgRaw)));
317
+ return `data:image/svg+xml;base64,${b64}`;
318
+ }
319
+ /** Round to 4 decimal places to keep SVG compact */
320
+ function r(n) {
321
+ return Math.round(n * 10000) / 10000;
322
+ }
323
+ /**
324
+ * Convert the squircle polygon (% coords) to absolute px points
325
+ * offset by (ox, oy) within the SVG canvas.
326
+ */
327
+ function squircleClipPath(gridPx, ox, oy, clipId) {
328
+ const pts = SQUIRCLE_POLY_PCTS.map(([px, py]) => `${r(ox + (px / 100) * gridPx)},${r(oy + (py / 100) * gridPx)}`).join(' ');
329
+ return `<clipPath id="${clipId}"><polygon points="${pts}"/></clipPath>`;
330
+ }
331
+ // ─── Main export ─────────────────────────────────────────────────────────────
332
+ /**
333
+ * Generate a complete, self-contained SVG string for the static Leftium logo.
334
+ *
335
+ * All asset SVGs are inlined as base64 data URIs so the output is a single
336
+ * portable file with no external dependencies.
337
+ */
338
+ export function generateLeftiumLogoSvg(config = {}) {
339
+ const { size = 800, squircle = true, boundingBox = 'default', background = 'transparent' } = config;
340
+ // ── Choose ligature/shadow constants based on squircle mode ──────────────
341
+ const ligW = squircle ? LIG_SQRC_W : LIG_ORIG_W;
342
+ const ligH = squircle ? LIG_SQRC_H : LIG_ORIG_H;
343
+ const ligL = squircle ? LIG_SQRC_L : LIG_ORIG_L;
344
+ const ligT = squircle ? LIG_SQRC_T : LIG_ORIG_T;
345
+ const blurPad = squircle ? BLUR_PAD_SQRC : BLUR_PAD_ORIG;
346
+ const shadW = ligW + blurPad * 2;
347
+ const shadH = ligH + blurPad * 2;
348
+ const shadL = ligL - blurPad;
349
+ const shadT = ligT - blurPad;
350
+ // ── Compute canvas size and grid position ─────────────────────────────────
351
+ let canvasW;
352
+ let canvasH;
353
+ let gridPx; // rendered size of the 532-unit grid
354
+ let gridX; // left offset of grid on canvas
355
+ let gridY; // top offset of grid on canvas
356
+ if (boundingBox === 'cropped') {
357
+ canvasW = size;
358
+ canvasH = r(size * CROPPED_ASPECT);
359
+ gridPx = r(size * CROPPED_GRID_W_FRAC);
360
+ gridX = r(size * CROPPED_LEFT_FRAC);
361
+ gridY = r(canvasH * CROPPED_TOP_FRAC);
362
+ }
363
+ else {
364
+ canvasW = size;
365
+ canvasH = size;
366
+ let scale = BBOX_SCALE[boundingBox];
367
+ if (boundingBox === 'encircled' && squircle)
368
+ scale = scale / ENCIRCLED_SQUIRCLE_EXTRA;
369
+ gridPx = r(size / scale);
370
+ gridX = r((size - gridPx) / 2);
371
+ gridY = r((size - gridPx) / 2);
372
+ }
373
+ // Scale factor: native grid (532) → rendered pixels
374
+ const s = gridPx / GRID;
375
+ // ── Asset data URIs ───────────────────────────────────────────────────────
376
+ const squareUri = svgToDataUri(squareSvgRaw);
377
+ const glowUri = svgToDataUri(squircle ? glowSquircleSvgRaw : glowSvgRaw);
378
+ const ligUri = svgToDataUri(ligatureSvgRaw);
379
+ const shadUri = svgToDataUri(shadowSvgRaw);
380
+ // ── Element positions (all in canvas px coords) ───────────────────────────
381
+ // Square: fills the full grid
382
+ const sqX = gridX;
383
+ const sqY = gridY;
384
+ const sqW = gridPx;
385
+ const sqH = gridPx;
386
+ // Glow: 647/532 × gridPx, centered (overflows grid on all sides)
387
+ const glowPx = r((GLOW_W / GRID) * gridPx);
388
+ const glowX = r(gridX + (GLOW_OFFSET / GRID) * gridPx);
389
+ const glowY = r(gridY + (GLOW_OFFSET / GRID) * gridPx);
390
+ // Shadow: scaled from native 532 coords
391
+ const shadXpx = r(gridX + (shadL / GRID) * gridPx);
392
+ const shadYpx = r(gridY + (shadT / GRID) * gridPx);
393
+ const shadWpx = r((shadW / GRID) * gridPx);
394
+ const shadHpx = r((shadH / GRID) * gridPx);
395
+ // Ligature: scaled from native 532 coords
396
+ const ligXpx = r(gridX + (ligL / GRID) * gridPx);
397
+ const ligYpx = r(gridY + (ligT / GRID) * gridPx);
398
+ const ligWpx = r((ligW / GRID) * gridPx);
399
+ const ligHpx = r((ligH / GRID) * gridPx);
400
+ // ── Build SVG defs (background rect + optional squircle clip) ─────────────
401
+ const defs = [];
402
+ let squareClipAttr = '';
403
+ if (squircle) {
404
+ const clipId = 'leftium-squircle-clip';
405
+ defs.push(squircleClipPath(gridPx, gridX, gridY, clipId));
406
+ squareClipAttr = ` clip-path="url(#${clipId})"`;
407
+ }
408
+ const defsBlock = defs.length ? `\n <defs>\n ${defs.join('\n ')}\n </defs>` : '';
409
+ // ── Background ────────────────────────────────────────────────────────────
410
+ const bgEl = background === 'transparent'
411
+ ? ''
412
+ : `\n <rect width="${canvasW}" height="${canvasH}" fill="${background}"/>`;
413
+ // ── Assemble SVG ──────────────────────────────────────────────────────────
414
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${canvasW}" height="${canvasH}" viewBox="0 0 ${canvasW} ${canvasH}">${defsBlock}${bgEl}
415
+ <!-- shadow (z=0) -->
416
+ <image href="${shadUri}" x="${shadXpx}" y="${shadYpx}" width="${shadWpx}" height="${shadHpx}"/>
417
+ <!-- glow (z=1) -->
418
+ <image href="${glowUri}" x="${glowX}" y="${glowY}" width="${glowPx}" height="${glowPx}"/>
419
+ <!-- square (z=2)${squircle ? ', squircle-clipped' : ''} -->
420
+ <image href="${squareUri}" x="${sqX}" y="${sqY}" width="${sqW}" height="${sqH}"${squareClipAttr}/>
421
+ <!-- ligature (z=3) -->
422
+ <image href="${ligUri}" x="${ligXpx}" y="${ligYpx}" width="${ligWpx}" height="${ligHpx}"/>
423
+ </svg>`;
424
+ }
425
+ // ─── PNG / WebP rasteriser ────────────────────────────────────────────────────
426
+ /**
427
+ * Rasterise the Leftium logo to a PNG or WebP Blob.
428
+ * Browser-only (requires canvas + Image).
429
+ */
430
+ export async function generateLeftiumLogoPng(config, format = 'png') {
431
+ const svg = generateLeftiumLogoSvg(config);
432
+ const size = config.size ?? 800;
433
+ const { squircle = true, boundingBox = 'default' } = config;
434
+ const canvasW = size;
435
+ const canvasH = boundingBox === 'cropped' ? Math.round(size * (1 / 0.8906)) : size;
436
+ return new Promise((resolve, reject) => {
437
+ const img = new Image();
438
+ const blob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' });
439
+ const url = URL.createObjectURL(blob);
440
+ img.onload = () => {
441
+ try {
442
+ const canvas = document.createElement('canvas');
443
+ canvas.width = canvasW;
444
+ canvas.height = canvasH;
445
+ const ctx = canvas.getContext('2d');
446
+ if (!ctx) {
447
+ reject(new Error('Failed to get canvas 2d context'));
448
+ return;
449
+ }
450
+ ctx.drawImage(img, 0, 0, canvasW, canvasH);
451
+ canvas.toBlob((outBlob) => {
452
+ URL.revokeObjectURL(url);
453
+ if (outBlob)
454
+ resolve(outBlob);
455
+ else
456
+ reject(new Error('Canvas toBlob returned null'));
457
+ }, format === 'webp' ? 'image/webp' : 'image/png');
458
+ }
459
+ catch (err) {
460
+ URL.revokeObjectURL(url);
461
+ reject(err);
462
+ }
463
+ };
464
+ img.onerror = () => {
465
+ URL.revokeObjectURL(url);
466
+ reject(new Error('Failed to load SVG into Image element'));
467
+ };
468
+ img.src = url;
469
+ });
470
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Svelte action wrapping tippy.js for instant, styled tooltips.
3
+ *
4
+ * Usage:
5
+ * <button use:tooltip={'Copy to clipboard'}>Copy</button>
6
+ * <button use:tooltip={{ content: 'Preview', placement: 'top' }}>Hover</button>
7
+ */
8
+ import type { Props } from 'tippy.js';
9
+ import 'tippy.js/dist/tippy.css';
10
+ type TooltipParam = string | Partial<Props> | null | undefined;
11
+ export declare function tooltip(node: HTMLElement, param: TooltipParam): {
12
+ update?: undefined;
13
+ destroy?: undefined;
14
+ } | {
15
+ update(newParam: TooltipParam): void;
16
+ destroy(): void;
17
+ };
18
+ export {};
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Svelte action wrapping tippy.js for instant, styled tooltips.
3
+ *
4
+ * Usage:
5
+ * <button use:tooltip={'Copy to clipboard'}>Copy</button>
6
+ * <button use:tooltip={{ content: 'Preview', placement: 'top' }}>Hover</button>
7
+ */
8
+ import tippy from 'tippy.js';
9
+ import 'tippy.js/dist/tippy.css';
10
+ export function tooltip(node, param) {
11
+ if (!param)
12
+ return {};
13
+ const opts = typeof param === 'string' ? { content: param } : param;
14
+ // @ts-expect-error -- tippy.js CJS/ESM interop under module:"NodeNext"
15
+ const instance = tippy(node, {
16
+ delay: [80, 0], // 80ms show delay (fast but not jumpy), instant hide
17
+ duration: [150, 100],
18
+ ...opts
19
+ });
20
+ // Remove native title so browser tooltip doesn't fight tippy
21
+ if (node.hasAttribute('title')) {
22
+ node.removeAttribute('title');
23
+ }
24
+ return {
25
+ update(newParam) {
26
+ if (!newParam) {
27
+ instance.disable();
28
+ return;
29
+ }
30
+ instance.enable();
31
+ const newOpts = typeof newParam === 'string' ? { content: newParam } : newParam;
32
+ instance.setProps(newOpts);
33
+ },
34
+ destroy() {
35
+ instance.destroy();
36
+ }
37
+ };
38
+ }
@@ -16,7 +16,3 @@ export declare class Ripples {
16
16
  set(property: string, value: unknown): void;
17
17
  updateSize(): void;
18
18
  }
19
-
20
- export default Ripples;
21
-
22
- export declare function isWebGLSupported(): boolean;
@@ -1 +1 @@
1
- const isBrowser="undefined"!=typeof window&&"undefined"!=typeof document;let gl=null,config=null,transparentPixels=null;function isPercentage(e){return"%"===e[e.length-1]}function loadConfig(){if(!isBrowser)return null;const e=document.createElement("canvas");if(gl=e.getContext("webgl")||e.getContext("experimental-webgl"),!gl)return null;const t={};if(["OES_texture_float","OES_texture_half_float","OES_texture_float_linear","OES_texture_half_float_linear"].forEach(e=>{const i=gl.getExtension(e);i&&(t[e]=i)}),!t.OES_texture_float)return null;const i=[];function r(e,i,r){const n="OES_texture_"+e,s=n+"_linear",o=s in t,a=[n];return o&&a.push(s),{type:i,arrayType:r,linearSupport:o,extensions:a}}i.push(r("float",gl.FLOAT,Float32Array)),t.OES_texture_half_float&&i.push(r("half_float",t.OES_texture_half_float.HALF_FLOAT_OES,null));const n=gl.createTexture(),s=gl.createFramebuffer();gl.bindFramebuffer(gl.FRAMEBUFFER,s),gl.bindTexture(gl.TEXTURE_2D,n),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);let o=null;for(let e=0;e<i.length;e++)if(gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,32,32,0,gl.RGBA,i[e].type,null),gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,n,0),gl.checkFramebufferStatus(gl.FRAMEBUFFER)===gl.FRAMEBUFFER_COMPLETE){o=i[e];break}return o}function createImageData(e,t){if(!isBrowser)return null;try{return new ImageData(e,t)}catch{return document.createElement("canvas").getContext("2d").createImageData(e,t)}}function translateBackgroundPosition(e){const t=e.split(" ");if(1!==t.length)return t.map(t=>{switch(e){case"center":return"50%";case"top":case"left":return"0";case"right":case"bottom":return"100%";default:return t}});switch(e){case"center":return["50%","50%"];case"top":return["50%","0"];case"bottom":return["50%","100%"];case"left":return["0","50%"];case"right":return["100%","50%"];default:return[e,"50%"]}}function createProgram(e,t){function i(e,t){const i=gl.createShader(e);if(gl.shaderSource(i,t),gl.compileShader(i),!gl.getShaderParameter(i,gl.COMPILE_STATUS))throw new Error("compile error: "+gl.getShaderInfoLog(i));return i}const r={};if(r.id=gl.createProgram(),gl.attachShader(r.id,i(gl.VERTEX_SHADER,e)),gl.attachShader(r.id,i(gl.FRAGMENT_SHADER,t)),gl.linkProgram(r.id),!gl.getProgramParameter(r.id,gl.LINK_STATUS))throw new Error("link error: "+gl.getProgramInfoLog(r.id));r.uniforms={},r.locations={},gl.useProgram(r.id),gl.enableVertexAttribArray(0);let n,s,o=/uniform (\w+) (\w+)/g,a=e+t;for(;null!=(n=o.exec(a));)s=n[2],r.locations[s]=gl.getUniformLocation(r.id,s);return r}function bindTexture(e,t){gl.activeTexture(gl.TEXTURE0+(t||0)),gl.bindTexture(gl.TEXTURE_2D,e)}function extractUrl(e){const t=/url\(["']?([^"']*)["']?\)/.exec(e);return null==t?null:t[1]}function isDataUri(e){return e.match(/^data:/)}isBrowser&&(config=loadConfig(),transparentPixels=createImageData(32,32));const DEFAULTS={imageUrl:null,dropRadius:15,perturbance:.04,resolution:512,interactive:!0,crossOrigin:"",wavePropagation:2,dampening:.997};class Ripples{constructor(e,t){if(!isBrowser)return console.warn("Ripples: Cannot initialize in non-browser environment"),void(this.destroyed=!0);if(this.el="string"==typeof e?document.querySelector(e):e,!this.el)return void console.error("Ripples: Element not found.",e);this.options={...DEFAULTS,...t},this.interactive=this.options.interactive,this.resolution=this.options.resolution,this.textureDelta=new Float32Array([1/this.resolution,1/this.resolution]),this.perturbance=this.options.perturbance,this.dropRadius=this.options.dropRadius,this.crossOrigin=this.options.crossOrigin,this.imageUrl=this.options.imageUrl;const i=this.el.clientWidth,r=this.el.clientHeight;if(0===i||0===r)return console.warn("Ripples: Element has zero dimensions. Deferring initialization."),this.pendingInitialization=!0,this.destroyed=!1,this.visible=!1,this.running=!1,this.inited=!1,void this.observeVisibility();const n=document.createElement("canvas");if(n.width=i,n.height=r,this.canvas=n,this.canvas.style.position="absolute",this.canvas.style.inset=0,0===n.width||0===n.height)return console.warn("Ripples: Canvas has zero dimensions. Deferring initialization."),this.pendingInitialization=!0,this.destroyed=!1,this.visible=!1,this.running=!1,this.inited=!1,void this.observeVisibility();if(this.el.append(n),this.context=gl=n.getContext("webgl")||n.getContext("experimental-webgl"),!this.context)return console.error("Ripples: WebGL is not supported by your browser or is disabled."),void(this.destroyed=!0);config&&config.extensions&&config.extensions.forEach(e=>{gl.getExtension(e)}),this.abortController=new AbortController;const s=this.signal=this.abortController.signal;this.updateSize=this.updateSize.bind(this),window.addEventListener("resize",this.updateSize,{signal:s}),this.textures=[],this.framebuffers=[],this.bufferWriteIndex=0,this.bufferReadIndex=1;const o=config?config.arrayType:null,a=o?new o(this.resolution*this.resolution*4):null;for(let e=0;e<2;e++){const e=gl.createTexture(),t=gl.createFramebuffer();gl.bindFramebuffer(gl.FRAMEBUFFER,t),gl.bindTexture(gl.TEXTURE_2D,e),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,config&&config.linearSupport?gl.LINEAR:gl.NEAREST),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,config&&config.linearSupport?gl.LINEAR:gl.NEAREST),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE),gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,this.resolution,this.resolution,0,gl.RGBA,config&&config.type||gl.UNSIGNED_BYTE,a),gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,e,0),this.textures.push(e),this.framebuffers.push(t)}this.quad=gl.createBuffer(),gl.bindBuffer(gl.ARRAY_BUFFER,this.quad),gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,1,1,-1,1]),gl.STATIC_DRAW),this.initShaders(),this.initTexture(),this.setTransparentTexture(),this.originalCssBackgroundImage=getComputedStyle(this.el).backgroundImage,this.originalInlineCss=this.el.style.backgroundImage,this.loadImage(),gl.clearColor(0,0,0,0),gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA),this.visible=!0,this.running=!0,this.inited=!0,this.destroyed=!1,this.setupPointerEvents(),this.animationId=null;const l=()=>{this.destroyed||(this.step(),this.running||this.needsRender?this.animationId=requestAnimationFrame(l):this.animationId=null)};return this.animationId=requestAnimationFrame(l),this}observeVisibility(){isBrowser&&this.el&&("undefined"!=typeof ResizeObserver&&(this.resizeObserver=new ResizeObserver(()=>{if(this.pendingInitialization){const e=this.el.clientWidth,t=this.el.clientHeight;e>0&&t>0&&this.completeInitialization()}else this.inited&&this.canvas&&this.updateSize()}),this.resizeObserver.observe(this.el)),"undefined"!=typeof IntersectionObserver&&(this.intersectionObserver=new IntersectionObserver(e=>{e.forEach(e=>{if(e.isIntersecting&&this.pendingInitialization){const e=this.el.clientWidth,t=this.el.clientHeight;e>0&&t>0&&this.completeInitialization()}})}),this.intersectionObserver.observe(this.el)),this.resizeObserver||this.intersectionObserver||(this.visibilityCheckInterval=setInterval(()=>{if(this.pendingInitialization){const e=this.el.clientWidth,t=this.el.clientHeight;e>0&&t>0&&this.completeInitialization()}},500)))}completeInitialization(){if(!this.pendingInitialization||this.inited)return;console.log("Ripples: Element now visible, completing initialization."),this.pendingInitialization=!1,this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.intersectionObserver&&(this.intersectionObserver.disconnect(),this.intersectionObserver=null),this.visibilityCheckInterval&&(clearInterval(this.visibilityCheckInterval),this.visibilityCheckInterval=null);const e=this.options,t=this.el;Object.assign(this,new Ripples(t,e))}setupPointerEvents(){const e=()=>{const e=Math.max(this.el.clientWidth,this.el.clientHeight),t=Math.max(1,150/e);return Math.min(.04,.01*t)},t=t=>{if(this.visible&&this.running&&this.interactive){const i=t.changedTouches,r=e();for(let e=0;e<i.length;e++)this.dropAtPointer(i[e],1*this.dropRadius,r)}},i=this.signal;this.el.addEventListener("mousemove",t=>{this.visible&&this.running&&this.interactive&&this.dropAtPointer(t,1*this.dropRadius,e())},{signal:i}),this.el.addEventListener("touchmove",t,{signal:i,passive:!1}),this.el.addEventListener("touchstart",t,{signal:i,passive:!0}),this.el.addEventListener("mousedown",e=>{this.visible&&this.running&&this.interactive&&this.dropAtPointer(e,1.5*this.dropRadius,.14)},{signal:i})}loadImage(){gl=this.context;const e=this.imageUrl||extractUrl(this.originalCssBackgroundImage)||extractUrl(getComputedStyle(this.el).backgroundImage);if(e==this.imageSource)return;if(this.imageSource=e,!this.imageSource)return void this.setTransparentTexture();const t=new Image;t.onload=()=>{function e(e){return!(e&e-1)}gl=this.context;const i=e(t.width)&&e(t.height)?gl.REPEAT:gl.CLAMP_TO_EDGE;gl.bindTexture(gl.TEXTURE_2D,this.backgroundTexture),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,i),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,i),gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,t),this.backgroundWidth=t.width,this.backgroundHeight=t.height},t.onerror=()=>{gl=this.context,this.setTransparentTexture()},t.crossOrigin=isDataUri(this.imageSource)?null:this.crossOrigin,t.src=this.imageSource}step(){this.pendingInitialization||this.destroyed||(gl=this.context,gl&&this.canvas&&0!==this.canvas.width&&0!==this.canvas.height&&this.visible&&(this.computeTextureBoundaries(),this.running&&(this.update(),this.render())))}drawQuad(){gl.bindBuffer(gl.ARRAY_BUFFER,this.quad),gl.vertexAttribPointer(0,2,gl.FLOAT,!1,0,0),gl.drawArrays(gl.TRIANGLE_FAN,0,4)}render(){gl&&this.canvas&&0!==this.canvas.width&&0!==this.canvas.height&&(gl.bindFramebuffer(gl.FRAMEBUFFER,null),gl.viewport(0,0,this.canvas.width,this.canvas.height),gl.enable(gl.BLEND),gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT),gl.useProgram(this.renderProgram.id),bindTexture(this.backgroundTexture,0),bindTexture(this.textures[0],1),gl.uniform1f(this.renderProgram.locations.perturbance,this.perturbance),gl.uniform2fv(this.renderProgram.locations.topLeft,this.renderProgram.uniforms.topLeft),gl.uniform2fv(this.renderProgram.locations.bottomRight,this.renderProgram.uniforms.bottomRight),gl.uniform2fv(this.renderProgram.locations.containerRatio,this.renderProgram.uniforms.containerRatio),gl.uniform1i(this.renderProgram.locations.samplerBackground,0),gl.uniform1i(this.renderProgram.locations.samplerRipples,1),this.drawQuad(),gl.disable(gl.BLEND))}update(){gl&&0!==this.resolution&&(gl.viewport(0,0,this.resolution,this.resolution),gl.bindFramebuffer(gl.FRAMEBUFFER,this.framebuffers[this.bufferWriteIndex]),bindTexture(this.textures[this.bufferReadIndex]),gl.useProgram(this.updateProgram.id),gl.uniform1f(this.updateProgram.locations.wavePropagation,this.options.wavePropagation),gl.uniform1f(this.updateProgram.locations.dampening,this.options.dampening),this.drawQuad(),this.swapBufferIndices())}swapBufferIndices(){this.bufferWriteIndex=1-this.bufferWriteIndex,this.bufferReadIndex=1-this.bufferReadIndex}computeTextureBoundaries(){const e=getComputedStyle(this.el),t=e.backgroundSize,i=e.backgroundAttachment,r=translateBackgroundPosition(e.backgroundPosition);let n,s,o;if("fixed"==i)n={left:window.pageXOffset,top:window.pageYOffset},n.width=window.innerWidth,n.height=window.innerHeight;else{const e=this.el.getBoundingClientRect();n={left:e.left+window.pageXOffset,top:e.top+window.pageYOffset},n.width=this.el.clientWidth,n.height=this.el.clientHeight}if("cover"==t){const e=Math.max(n.width/this.backgroundWidth,n.height/this.backgroundHeight);s=this.backgroundWidth*e,o=this.backgroundHeight*e}else if("contain"==t){const e=Math.min(n.width/this.backgroundWidth,n.height/this.backgroundHeight);s=this.backgroundWidth*e,o=this.backgroundHeight*e}else{const e=t.split(" ");s=e[0]||"",o=e[1]||s,isPercentage(s)?s=n.width*parseFloat(s)/100:"auto"!=s&&(s=parseFloat(s)),isPercentage(o)?o=n.height*parseFloat(o)/100:"auto"!=o&&(o=parseFloat(o)),"auto"==s&&"auto"==o?(s=this.backgroundWidth,o=this.backgroundHeight):("auto"==s&&(s=this.backgroundWidth*(o/this.backgroundHeight)),"auto"==o&&(o=this.backgroundHeight*(s/this.backgroundWidth)))}let a=r[0],l=r[1];a=isPercentage(a)?n.left+(n.width-s)*parseFloat(a)/100:n.left+parseFloat(a),l=isPercentage(l)?n.top+(n.height-o)*parseFloat(l)/100:n.top+parseFloat(l);const g=this.el.getBoundingClientRect(),h=g.left+window.pageXOffset,d=g.top+window.pageYOffset;this.renderProgram.uniforms.topLeft=new Float32Array([(h-a)/s,(d-l)/o]),this.renderProgram.uniforms.bottomRight=new Float32Array([this.renderProgram.uniforms.topLeft[0]+this.el.clientWidth/s,this.renderProgram.uniforms.topLeft[1]+this.el.clientHeight/o]);const c=Math.max(this.canvas.width,this.canvas.height);this.renderProgram.uniforms.containerRatio=new Float32Array([this.canvas.width/c,this.canvas.height/c])}initShaders(){if(!gl)return;const e=["attribute vec2 vertex;","varying vec2 coord;","void main() {","coord = vertex * 0.5 + 0.5;","gl_Position = vec4(vertex, 0.0, 1.0);","}"].join("\n");this.dropProgram=createProgram(e,["precision highp float;","const float PI = 3.141592653589793;","uniform sampler2D texture;","uniform vec2 center;","uniform float radius;","uniform float strength;","varying vec2 coord;","void main() {","vec4 info = texture2D(texture, coord);","float drop = max(0.0, 1.0 - length(center * 0.5 + 0.5 - coord) / radius);","drop = 0.5 - cos(drop * PI) * 0.5;","info.r += drop * strength;","gl_FragColor = info;","}"].join("\n")),this.updateProgram=createProgram(e,["precision highp float;","uniform sampler2D texture;","uniform vec2 delta;","uniform float wavePropagation;","uniform float dampening;","varying vec2 coord;","void main() {","vec4 info = texture2D(texture, coord);","vec2 dx = vec2(delta.x, 0.0);","vec2 dy = vec2(0.0, delta.y);","// Detect edges and apply boundary conditions","float edgeFactor = 1.0;","if (coord.x <= delta.x || coord.x >= 1.0 - delta.x ||"," coord.y <= delta.y || coord.y >= 1.0 - delta.y) {"," edgeFactor = 0.95; // Dampen waves near edges more aggressively","}","float average = (","texture2D(texture, coord - dx).r +","texture2D(texture, coord - dy).r +","texture2D(texture, coord + dx).r +","texture2D(texture, coord + dy).r",") * 0.25;","// Wave propagation and dampening with configurable parameters","info.g += (average - info.r) * wavePropagation;","info.g *= dampening * edgeFactor;","info.r += info.g;","// Clamp values to prevent overflow artifacts","info.r = clamp(info.r, -1.0, 1.0);","info.g = clamp(info.g, -1.0, 1.0);","gl_FragColor = info;","}"].join("\n")),gl.uniform2fv(this.updateProgram.locations.delta,this.textureDelta),gl.uniform1f(this.updateProgram.locations.wavePropagation,this.options.wavePropagation),gl.uniform1f(this.updateProgram.locations.dampening,this.options.dampening),this.renderProgram=createProgram(["precision highp float;","attribute vec2 vertex;","uniform vec2 topLeft;","uniform vec2 bottomRight;","uniform vec2 containerRatio;","varying vec2 ripplesCoord;","varying vec2 backgroundCoord;","void main() {","backgroundCoord = mix(topLeft, bottomRight, vertex * 0.5 + 0.5);","backgroundCoord.y = 1.0 - backgroundCoord.y;","ripplesCoord = vec2(vertex.x, -vertex.y) * containerRatio * 0.5 + 0.5;","gl_Position = vec4(vertex.x, -vertex.y, 0.0, 1.0);","}"].join("\n"),["precision highp float;","uniform sampler2D samplerBackground;","uniform sampler2D samplerRipples;","uniform vec2 delta;","uniform float perturbance;","varying vec2 ripplesCoord;","varying vec2 backgroundCoord;","void main() {","float height = texture2D(samplerRipples, ripplesCoord).r;","// Sample neighboring pixels for calculating normals","vec2 texelSize = delta;","float heightX = texture2D(samplerRipples, clamp(ripplesCoord + vec2(texelSize.x, 0.0), 0.0, 1.0)).r;","float heightY = texture2D(samplerRipples, clamp(ripplesCoord + vec2(0.0, texelSize.y), 0.0, 1.0)).r;","vec3 dx = vec3(texelSize.x, heightX - height, 0.0);","vec3 dy = vec3(0.0, heightY - height, texelSize.y);","vec2 offset = -normalize(cross(dy, dx)).xz;","// Apply edge fading to reduce artifacts at boundaries","float edgeFade = 1.0;","float edgeDistance = 0.05;","edgeFade *= smoothstep(0.0, edgeDistance, ripplesCoord.x);","edgeFade *= smoothstep(0.0, edgeDistance, ripplesCoord.y);","edgeFade *= smoothstep(0.0, edgeDistance, 1.0 - ripplesCoord.x);","edgeFade *= smoothstep(0.0, edgeDistance, 1.0 - ripplesCoord.y);","float specular = pow(max(0.0, dot(offset, normalize(vec2(-0.6, 1.0)))), 4.0) * edgeFade;","gl_FragColor = texture2D(samplerBackground, backgroundCoord + offset * perturbance * edgeFade) + specular;","}"].join("\n")),gl.uniform2fv(this.renderProgram.locations.delta,this.textureDelta)}initTexture(){gl&&(this.backgroundTexture=gl.createTexture(),gl.bindTexture(gl.TEXTURE_2D,this.backgroundTexture),gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,1),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.LINEAR),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR))}setTransparentTexture(){gl&&(gl.bindTexture(gl.TEXTURE_2D,this.backgroundTexture),gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,transparentPixels))}dropAtPointer(e,t,i){if(!this.canvas||this.pendingInitialization)return;const r=this.canvas.getBoundingClientRect();let n,s;void 0!==e.clientX?(n=e.clientX-r.left,s=e.clientY-r.top):(n=e.pageX-(r.left+window.pageXOffset),s=e.pageY-(r.top+window.pageYOffset)),n<0||n>r.width||s<0||s>r.height||(this.canvas.width===r.width&&this.canvas.height===r.height||(n*=this.canvas.width/r.width,s*=this.canvas.height/r.height),this.drop(n,s,t,i))}drop(e,t,i,r){if(gl=this.context,!gl)return;const n=this.el.clientWidth,s=this.el.clientHeight,o=Math.max(n,s);i/=o;const a=new Float32Array([(2*e-n)/o,(s-2*t)/o]);gl.viewport(0,0,this.resolution,this.resolution),gl.bindFramebuffer(gl.FRAMEBUFFER,this.framebuffers[this.bufferWriteIndex]),bindTexture(this.textures[this.bufferReadIndex]),gl.useProgram(this.dropProgram.id),gl.uniform2fv(this.dropProgram.locations.center,a),gl.uniform1f(this.dropProgram.locations.radius,i),gl.uniform1f(this.dropProgram.locations.strength,r),this.drawQuad(),this.swapBufferIndices()}updateSize(){const e=this.el.clientWidth,t=this.el.clientHeight;e===this.canvas.width&&t===this.canvas.height||(this.canvas.width=e,this.canvas.height=t)}destroy(){this.destroyed=!0,this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.intersectionObserver&&(this.intersectionObserver.disconnect(),this.intersectionObserver=null),this.visibilityCheckInterval&&(clearInterval(this.visibilityCheckInterval),this.visibilityCheckInterval=null),this.abortController&&this.abortController.abort(),this.canvas&&this.canvas.remove();const e=this.context;e&&(this.quad&&e.deleteBuffer(this.quad),this.dropProgram&&e.deleteProgram(this.dropProgram.id),this.updateProgram&&e.deleteProgram(this.updateProgram.id),this.renderProgram&&e.deleteProgram(this.renderProgram.id),this.backgroundTexture&&e.deleteTexture(this.backgroundTexture),this.textures&&this.textures.forEach(t=>e.deleteTexture(t)),this.framebuffers&&this.framebuffers.forEach(t=>e.deleteFramebuffer(t)),gl===e&&(gl=null),this.context=null)}pause(){this.destroyed||(this.running=!1,this.needsRender=!0,setTimeout(()=>{this.needsRender=!1},50))}play(){if(!this.destroyed&&(this.running=!0,!this.animationId)){const e=()=>{this.destroyed||(this.step(),this.running||this.needsRender?this.animationId=requestAnimationFrame(e):this.animationId=null)};this.animationId=requestAnimationFrame(e)}}reset(){if(this.destroyed||!gl)return;gl=this.context;const e=config?config.arrayType:null,t=e?new e(this.resolution*this.resolution*4):null;for(let e=0;e<2;e++)gl.bindTexture(gl.TEXTURE_2D,this.textures[e]),gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,this.resolution,this.resolution,0,gl.RGBA,config&&config.type||gl.UNSIGNED_BYTE,t)}set(e,t){if(!this.destroyed)switch(e){case"dropRadius":case"perturbance":case"interactive":case"crossOrigin":this[e]=t,this.options[e]=t;break;case"imageUrl":this.imageUrl=t,this.options.imageUrl=t,this.loadImage();break;case"resolution":console.warn("Ripples: Changing 'resolution' dynamically is not supported. Please re-initialize the Ripples instance.");break;default:console.warn(`Ripples: Property "${e}" is not a settable option.`)}}}export{Ripples};export default Ripples;export const isWebGLSupported=()=>!!isBrowser&&null!==config;
1
+ const isBrowser="undefined"!=typeof window&&"undefined"!=typeof document;let gl=null,config=null,transparentPixels=null;function isPercentage(e){return"%"===e[e.length-1]}function loadConfig(){if(!isBrowser)return null;const e=document.createElement("canvas");if(gl=e.getContext("webgl")||e.getContext("experimental-webgl"),!gl)return null;const t={};if(["OES_texture_float","OES_texture_half_float","OES_texture_float_linear","OES_texture_half_float_linear"].forEach(e=>{const i=gl.getExtension(e);i&&(t[e]=i)}),!t.OES_texture_float)return null;const i=[];function r(e,i,r){const n="OES_texture_"+e,s=n+"_linear",o=s in t,a=[n];return o&&a.push(s),{type:i,arrayType:r,linearSupport:o,extensions:a}}i.push(r("float",gl.FLOAT,Float32Array)),t.OES_texture_half_float&&i.push(r("half_float",t.OES_texture_half_float.HALF_FLOAT_OES,null));const n=gl.createTexture(),s=gl.createFramebuffer();gl.bindFramebuffer(gl.FRAMEBUFFER,s),gl.bindTexture(gl.TEXTURE_2D,n),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);let o=null;for(let e=0;e<i.length;e++)if(gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,32,32,0,gl.RGBA,i[e].type,null),gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,n,0),gl.checkFramebufferStatus(gl.FRAMEBUFFER)===gl.FRAMEBUFFER_COMPLETE){o=i[e];break}return o}function createImageData(e,t){if(!isBrowser)return null;try{return new ImageData(e,t)}catch{return document.createElement("canvas").getContext("2d").createImageData(e,t)}}function translateBackgroundPosition(e){const t=e.split(" ");if(1!==t.length)return t.map(t=>{switch(e){case"center":return"50%";case"top":case"left":return"0";case"right":case"bottom":return"100%";default:return t}});switch(e){case"center":return["50%","50%"];case"top":return["50%","0"];case"bottom":return["50%","100%"];case"left":return["0","50%"];case"right":return["100%","50%"];default:return[e,"50%"]}}function createProgram(e,t){function i(e,t){const i=gl.createShader(e);if(gl.shaderSource(i,t),gl.compileShader(i),!gl.getShaderParameter(i,gl.COMPILE_STATUS))throw new Error("compile error: "+gl.getShaderInfoLog(i));return i}const r={};if(r.id=gl.createProgram(),gl.attachShader(r.id,i(gl.VERTEX_SHADER,e)),gl.attachShader(r.id,i(gl.FRAGMENT_SHADER,t)),gl.linkProgram(r.id),!gl.getProgramParameter(r.id,gl.LINK_STATUS))throw new Error("link error: "+gl.getProgramInfoLog(r.id));r.uniforms={},r.locations={},gl.useProgram(r.id),gl.enableVertexAttribArray(0);let n,s,o=/uniform (\w+) (\w+)/g,a=e+t;for(;null!=(n=o.exec(a));)s=n[2],r.locations[s]=gl.getUniformLocation(r.id,s);return r}function bindTexture(e,t){gl.activeTexture(gl.TEXTURE0+(t||0)),gl.bindTexture(gl.TEXTURE_2D,e)}function extractUrl(e){const t=/url\(["']?([^"']*)["']?\)/.exec(e);return null==t?null:t[1]}function isDataUri(e){return e.match(/^data:/)}isBrowser&&(config=loadConfig(),transparentPixels=createImageData(32,32));const DEFAULTS={imageUrl:null,dropRadius:15,perturbance:.04,resolution:512,interactive:!0,crossOrigin:"",wavePropagation:2,dampening:.997};class Ripples{constructor(e,t){if(!isBrowser)return console.warn("Ripples: Cannot initialize in non-browser environment"),void(this.destroyed=!0);if(this.el="string"==typeof e?document.querySelector(e):e,!this.el)return void console.error("Ripples: Element not found.",e);this.options={...DEFAULTS,...t},this.interactive=this.options.interactive,this.resolution=this.options.resolution,this.textureDelta=new Float32Array([1/this.resolution,1/this.resolution]),this.perturbance=this.options.perturbance,this.dropRadius=this.options.dropRadius,this.crossOrigin=this.options.crossOrigin,this.imageUrl=this.options.imageUrl;const i=this.el.clientWidth,r=this.el.clientHeight;if(0===i||0===r)return console.warn("Ripples: Element has zero dimensions. Deferring initialization."),this.pendingInitialization=!0,this.destroyed=!1,this.visible=!1,this.running=!1,this.inited=!1,void this.observeVisibility();const n=document.createElement("canvas");if(n.width=i,n.height=r,this.canvas=n,this.canvas.style.position="absolute",this.canvas.style.inset=0,0===n.width||0===n.height)return console.warn("Ripples: Canvas has zero dimensions. Deferring initialization."),this.pendingInitialization=!0,this.destroyed=!1,this.visible=!1,this.running=!1,this.inited=!1,void this.observeVisibility();if(this.el.append(n),this.context=gl=n.getContext("webgl")||n.getContext("experimental-webgl"),!this.context)return console.error("Ripples: WebGL is not supported by your browser or is disabled."),void(this.destroyed=!0);config&&config.extensions&&config.extensions.forEach(e=>{gl.getExtension(e)}),this.abortController=new AbortController;const s=this.signal=this.abortController.signal;this.updateSize=this.updateSize.bind(this),window.addEventListener("resize",this.updateSize,{signal:s}),this.textures=[],this.framebuffers=[],this.bufferWriteIndex=0,this.bufferReadIndex=1;const o=config?config.arrayType:null,a=o?new o(this.resolution*this.resolution*4):null;for(let e=0;e<2;e++){const e=gl.createTexture(),t=gl.createFramebuffer();gl.bindFramebuffer(gl.FRAMEBUFFER,t),gl.bindTexture(gl.TEXTURE_2D,e),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,config&&config.linearSupport?gl.LINEAR:gl.NEAREST),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,config&&config.linearSupport?gl.LINEAR:gl.NEAREST),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE),gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,this.resolution,this.resolution,0,gl.RGBA,config&&config.type||gl.UNSIGNED_BYTE,a),gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,e,0),this.textures.push(e),this.framebuffers.push(t)}this.quad=gl.createBuffer(),gl.bindBuffer(gl.ARRAY_BUFFER,this.quad),gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,1,1,-1,1]),gl.STATIC_DRAW),this.initShaders(),this.initTexture(),this.setTransparentTexture(),this.originalCssBackgroundImage=getComputedStyle(this.el).backgroundImage,this.originalInlineCss=this.el.style.backgroundImage,this.loadImage(),gl.clearColor(0,0,0,0),gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA),this.visible=!0,this.running=!0,this.inited=!0,this.destroyed=!1,this.setupPointerEvents(),this.animationId=null;const l=()=>{this.destroyed||(this.step(),this.running||this.needsRender?this.animationId=requestAnimationFrame(l):this.animationId=null)};return this.animationId=requestAnimationFrame(l),this}observeVisibility(){isBrowser&&this.el&&("undefined"!=typeof ResizeObserver&&(this.resizeObserver=new ResizeObserver(()=>{if(this.pendingInitialization){const e=this.el.clientWidth,t=this.el.clientHeight;e>0&&t>0&&this.completeInitialization()}else this.inited&&this.canvas&&this.updateSize()}),this.resizeObserver.observe(this.el)),"undefined"!=typeof IntersectionObserver&&(this.intersectionObserver=new IntersectionObserver(e=>{e.forEach(e=>{if(e.isIntersecting&&this.pendingInitialization){const e=this.el.clientWidth,t=this.el.clientHeight;e>0&&t>0&&this.completeInitialization()}})}),this.intersectionObserver.observe(this.el)),this.resizeObserver||this.intersectionObserver||(this.visibilityCheckInterval=setInterval(()=>{if(this.pendingInitialization){const e=this.el.clientWidth,t=this.el.clientHeight;e>0&&t>0&&this.completeInitialization()}},500)))}completeInitialization(){if(!this.pendingInitialization||this.inited)return;console.log("Ripples: Element now visible, completing initialization."),this.pendingInitialization=!1,this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.intersectionObserver&&(this.intersectionObserver.disconnect(),this.intersectionObserver=null),this.visibilityCheckInterval&&(clearInterval(this.visibilityCheckInterval),this.visibilityCheckInterval=null);const e=this.options,t=this.el;Object.assign(this,new Ripples(t,e))}setupPointerEvents(){const e=()=>{const e=Math.max(this.el.clientWidth,this.el.clientHeight),t=Math.max(1,150/e);return Math.min(.04,.01*t)},t=t=>{if(this.visible&&this.running&&this.interactive){const i=t.changedTouches,r=e();for(let e=0;e<i.length;e++)this.dropAtPointer(i[e],1*this.dropRadius,r)}},i=this.signal;this.el.addEventListener("mousemove",t=>{this.visible&&this.running&&this.interactive&&this.dropAtPointer(t,1*this.dropRadius,e())},{signal:i}),this.el.addEventListener("touchmove",t,{signal:i,passive:!1}),this.el.addEventListener("touchstart",t,{signal:i,passive:!0}),this.el.addEventListener("mousedown",e=>{this.visible&&this.running&&this.interactive&&this.dropAtPointer(e,1.5*this.dropRadius,.14)},{signal:i})}loadImage(){gl=this.context;const e=this.imageUrl||extractUrl(this.originalCssBackgroundImage)||extractUrl(getComputedStyle(this.el).backgroundImage);if(e==this.imageSource)return;if(this.imageSource=e,!this.imageSource)return void this.setTransparentTexture();const t=new Image;t.onload=()=>{function e(e){return!(e&e-1)}gl=this.context;const i=e(t.width)&&e(t.height)?gl.REPEAT:gl.CLAMP_TO_EDGE;gl.bindTexture(gl.TEXTURE_2D,this.backgroundTexture),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,i),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,i),gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,t),this.backgroundWidth=t.width,this.backgroundHeight=t.height},t.onerror=()=>{gl=this.context,this.setTransparentTexture()},t.crossOrigin=isDataUri(this.imageSource)?null:this.crossOrigin,t.src=this.imageSource}step(){this.pendingInitialization||this.destroyed||(gl=this.context,gl&&this.canvas&&0!==this.canvas.width&&0!==this.canvas.height&&this.visible&&(this.computeTextureBoundaries(),this.running&&(this.update(),this.render())))}drawQuad(){gl.bindBuffer(gl.ARRAY_BUFFER,this.quad),gl.vertexAttribPointer(0,2,gl.FLOAT,!1,0,0),gl.drawArrays(gl.TRIANGLE_FAN,0,4)}render(){gl&&this.canvas&&0!==this.canvas.width&&0!==this.canvas.height&&(gl.bindFramebuffer(gl.FRAMEBUFFER,null),gl.viewport(0,0,this.canvas.width,this.canvas.height),gl.enable(gl.BLEND),gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT),gl.useProgram(this.renderProgram.id),bindTexture(this.backgroundTexture,0),bindTexture(this.textures[0],1),gl.uniform1f(this.renderProgram.locations.perturbance,this.perturbance),gl.uniform2fv(this.renderProgram.locations.topLeft,this.renderProgram.uniforms.topLeft),gl.uniform2fv(this.renderProgram.locations.bottomRight,this.renderProgram.uniforms.bottomRight),gl.uniform2fv(this.renderProgram.locations.containerRatio,this.renderProgram.uniforms.containerRatio),gl.uniform1i(this.renderProgram.locations.samplerBackground,0),gl.uniform1i(this.renderProgram.locations.samplerRipples,1),this.drawQuad(),gl.disable(gl.BLEND))}update(){gl&&0!==this.resolution&&(gl.viewport(0,0,this.resolution,this.resolution),gl.bindFramebuffer(gl.FRAMEBUFFER,this.framebuffers[this.bufferWriteIndex]),bindTexture(this.textures[this.bufferReadIndex]),gl.useProgram(this.updateProgram.id),gl.uniform1f(this.updateProgram.locations.wavePropagation,this.options.wavePropagation),gl.uniform1f(this.updateProgram.locations.dampening,this.options.dampening),this.drawQuad(),this.swapBufferIndices())}swapBufferIndices(){this.bufferWriteIndex=1-this.bufferWriteIndex,this.bufferReadIndex=1-this.bufferReadIndex}computeTextureBoundaries(){const e=getComputedStyle(this.el),t=e.backgroundSize,i=e.backgroundAttachment,r=translateBackgroundPosition(e.backgroundPosition);let n,s,o;if("fixed"==i)n={left:window.pageXOffset,top:window.pageYOffset},n.width=window.innerWidth,n.height=window.innerHeight;else{const e=this.el.getBoundingClientRect();n={left:e.left+window.pageXOffset,top:e.top+window.pageYOffset},n.width=this.el.clientWidth,n.height=this.el.clientHeight}if("cover"==t){const e=Math.max(n.width/this.backgroundWidth,n.height/this.backgroundHeight);s=this.backgroundWidth*e,o=this.backgroundHeight*e}else if("contain"==t){const e=Math.min(n.width/this.backgroundWidth,n.height/this.backgroundHeight);s=this.backgroundWidth*e,o=this.backgroundHeight*e}else{const e=t.split(" ");s=e[0]||"",o=e[1]||s,isPercentage(s)?s=n.width*parseFloat(s)/100:"auto"!=s&&(s=parseFloat(s)),isPercentage(o)?o=n.height*parseFloat(o)/100:"auto"!=o&&(o=parseFloat(o)),"auto"==s&&"auto"==o?(s=this.backgroundWidth,o=this.backgroundHeight):("auto"==s&&(s=this.backgroundWidth*(o/this.backgroundHeight)),"auto"==o&&(o=this.backgroundHeight*(s/this.backgroundWidth)))}let a=r[0],l=r[1];a=isPercentage(a)?n.left+(n.width-s)*parseFloat(a)/100:n.left+parseFloat(a),l=isPercentage(l)?n.top+(n.height-o)*parseFloat(l)/100:n.top+parseFloat(l);const g=this.el.getBoundingClientRect(),h=g.left+window.pageXOffset,d=g.top+window.pageYOffset;this.renderProgram.uniforms.topLeft=new Float32Array([(h-a)/s,(d-l)/o]),this.renderProgram.uniforms.bottomRight=new Float32Array([this.renderProgram.uniforms.topLeft[0]+this.el.clientWidth/s,this.renderProgram.uniforms.topLeft[1]+this.el.clientHeight/o]);const c=Math.max(this.canvas.width,this.canvas.height);this.renderProgram.uniforms.containerRatio=new Float32Array([this.canvas.width/c,this.canvas.height/c])}initShaders(){if(!gl)return;const e=["attribute vec2 vertex;","varying vec2 coord;","void main() {","coord = vertex * 0.5 + 0.5;","gl_Position = vec4(vertex, 0.0, 1.0);","}"].join("\n");this.dropProgram=createProgram(e,["precision highp float;","const float PI = 3.141592653589793;","uniform sampler2D texture;","uniform vec2 center;","uniform float radius;","uniform float strength;","varying vec2 coord;","void main() {","vec4 info = texture2D(texture, coord);","float drop = max(0.0, 1.0 - length(center * 0.5 + 0.5 - coord) / radius);","drop = 0.5 - cos(drop * PI) * 0.5;","info.r += drop * strength;","gl_FragColor = info;","}"].join("\n")),this.updateProgram=createProgram(e,["precision highp float;","uniform sampler2D texture;","uniform vec2 delta;","uniform float wavePropagation;","uniform float dampening;","varying vec2 coord;","void main() {","vec4 info = texture2D(texture, coord);","vec2 dx = vec2(delta.x, 0.0);","vec2 dy = vec2(0.0, delta.y);","// Detect edges and apply boundary conditions","float edgeFactor = 1.0;","if (coord.x <= delta.x || coord.x >= 1.0 - delta.x ||"," coord.y <= delta.y || coord.y >= 1.0 - delta.y) {"," edgeFactor = 0.95; // Dampen waves near edges more aggressively","}","float average = (","texture2D(texture, coord - dx).r +","texture2D(texture, coord - dy).r +","texture2D(texture, coord + dx).r +","texture2D(texture, coord + dy).r",") * 0.25;","// Wave propagation and dampening with configurable parameters","info.g += (average - info.r) * wavePropagation;","info.g *= dampening * edgeFactor;","info.r += info.g;","// Clamp values to prevent overflow artifacts","info.r = clamp(info.r, -1.0, 1.0);","info.g = clamp(info.g, -1.0, 1.0);","gl_FragColor = info;","}"].join("\n")),gl.uniform2fv(this.updateProgram.locations.delta,this.textureDelta),gl.uniform1f(this.updateProgram.locations.wavePropagation,this.options.wavePropagation),gl.uniform1f(this.updateProgram.locations.dampening,this.options.dampening),this.renderProgram=createProgram(["precision highp float;","attribute vec2 vertex;","uniform vec2 topLeft;","uniform vec2 bottomRight;","uniform vec2 containerRatio;","varying vec2 ripplesCoord;","varying vec2 backgroundCoord;","void main() {","backgroundCoord = mix(topLeft, bottomRight, vertex * 0.5 + 0.5);","backgroundCoord.y = 1.0 - backgroundCoord.y;","ripplesCoord = vec2(vertex.x, -vertex.y) * containerRatio * 0.5 + 0.5;","gl_Position = vec4(vertex.x, -vertex.y, 0.0, 1.0);","}"].join("\n"),["precision highp float;","uniform sampler2D samplerBackground;","uniform sampler2D samplerRipples;","uniform vec2 delta;","uniform float perturbance;","varying vec2 ripplesCoord;","varying vec2 backgroundCoord;","void main() {","float height = texture2D(samplerRipples, ripplesCoord).r;","// Sample neighboring pixels for calculating normals","vec2 texelSize = delta;","float heightX = texture2D(samplerRipples, clamp(ripplesCoord + vec2(texelSize.x, 0.0), 0.0, 1.0)).r;","float heightY = texture2D(samplerRipples, clamp(ripplesCoord + vec2(0.0, texelSize.y), 0.0, 1.0)).r;","vec3 dx = vec3(texelSize.x, heightX - height, 0.0);","vec3 dy = vec3(0.0, heightY - height, texelSize.y);","vec2 offset = -normalize(cross(dy, dx)).xz;","// Apply edge fading to reduce artifacts at boundaries","float edgeFade = 1.0;","float edgeDistance = 0.05;","edgeFade *= smoothstep(0.0, edgeDistance, ripplesCoord.x);","edgeFade *= smoothstep(0.0, edgeDistance, ripplesCoord.y);","edgeFade *= smoothstep(0.0, edgeDistance, 1.0 - ripplesCoord.x);","edgeFade *= smoothstep(0.0, edgeDistance, 1.0 - ripplesCoord.y);","float specular = pow(max(0.0, dot(offset, normalize(vec2(-0.6, 1.0)))), 4.0) * edgeFade;","gl_FragColor = texture2D(samplerBackground, backgroundCoord + offset * perturbance * edgeFade) + specular;","}"].join("\n")),gl.uniform2fv(this.renderProgram.locations.delta,this.textureDelta)}initTexture(){gl&&(this.backgroundTexture=gl.createTexture(),gl.bindTexture(gl.TEXTURE_2D,this.backgroundTexture),gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,1),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.LINEAR),gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR))}setTransparentTexture(){gl&&(gl.bindTexture(gl.TEXTURE_2D,this.backgroundTexture),gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,transparentPixels))}dropAtPointer(e,t,i){if(!this.canvas||this.pendingInitialization)return;const r=this.canvas.getBoundingClientRect();let n,s;void 0!==e.clientX?(n=e.clientX-r.left,s=e.clientY-r.top):(n=e.pageX-(r.left+window.pageXOffset),s=e.pageY-(r.top+window.pageYOffset)),n<0||n>r.width||s<0||s>r.height||(this.canvas.width===r.width&&this.canvas.height===r.height||(n*=this.canvas.width/r.width,s*=this.canvas.height/r.height),this.drop(n,s,t,i))}drop(e,t,i,r){if(gl=this.context,!gl)return;const n=this.el.clientWidth,s=this.el.clientHeight,o=Math.max(n,s);i/=o;const a=new Float32Array([(2*e-n)/o,(s-2*t)/o]);gl.viewport(0,0,this.resolution,this.resolution),gl.bindFramebuffer(gl.FRAMEBUFFER,this.framebuffers[this.bufferWriteIndex]),bindTexture(this.textures[this.bufferReadIndex]),gl.useProgram(this.dropProgram.id),gl.uniform2fv(this.dropProgram.locations.center,a),gl.uniform1f(this.dropProgram.locations.radius,i),gl.uniform1f(this.dropProgram.locations.strength,r),this.drawQuad(),this.swapBufferIndices()}updateSize(){const e=this.el.clientWidth,t=this.el.clientHeight;e===this.canvas.width&&t===this.canvas.height||(this.canvas.width=e,this.canvas.height=t)}destroy(){this.destroyed=!0,this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.intersectionObserver&&(this.intersectionObserver.disconnect(),this.intersectionObserver=null),this.visibilityCheckInterval&&(clearInterval(this.visibilityCheckInterval),this.visibilityCheckInterval=null),this.abortController&&this.abortController.abort(),this.canvas&&this.canvas.remove();const e=this.context;e&&(this.quad&&e.deleteBuffer(this.quad),this.dropProgram&&e.deleteProgram(this.dropProgram.id),this.updateProgram&&e.deleteProgram(this.updateProgram.id),this.renderProgram&&e.deleteProgram(this.renderProgram.id),this.backgroundTexture&&e.deleteTexture(this.backgroundTexture),this.textures&&this.textures.forEach(t=>e.deleteTexture(t)),this.framebuffers&&this.framebuffers.forEach(t=>e.deleteFramebuffer(t)),gl===e&&(gl=null),this.context=null)}pause(){this.destroyed||(this.running=!1,this.needsRender=!0,setTimeout(()=>{this.needsRender=!1},50))}play(){if(!this.destroyed&&(this.running=!0,!this.animationId)){const e=()=>{this.destroyed||(this.step(),this.running||this.needsRender?this.animationId=requestAnimationFrame(e):this.animationId=null)};this.animationId=requestAnimationFrame(e)}}reset(){if(this.destroyed||!gl)return;gl=this.context;const e=config?config.arrayType:null,t=e?new e(this.resolution*this.resolution*4):null;for(let e=0;e<2;e++)gl.bindTexture(gl.TEXTURE_2D,this.textures[e]),gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,this.resolution,this.resolution,0,gl.RGBA,config&&config.type||gl.UNSIGNED_BYTE,t)}set(e,t){if(!this.destroyed)switch(e){case"dropRadius":case"perturbance":case"interactive":case"crossOrigin":this[e]=t,this.options[e]=t;break;case"imageUrl":this.imageUrl=t,this.options.imageUrl=t,this.loadImage();break;case"resolution":console.warn("Ripples: Changing 'resolution' dynamically is not supported. Please re-initialize the Ripples instance.");break;default:console.warn(`Ripples: Property "${e}" is not a settable option.`)}}}export{Ripples};