@leftium/logo 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -0
- package/dist/LeftiumLogo.svelte +502 -0
- package/dist/LeftiumLogo.svelte.d.ts +16 -0
- package/dist/assets/favicon.svg +99 -0
- package/dist/assets/logo-parts/glow.svg +46 -0
- package/dist/assets/logo-parts/ligature.svg +32 -0
- package/dist/assets/logo-parts/shadow.svg +43 -0
- package/dist/assets/logo-parts/square.svg +58 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -0
- package/dist/webgl-ripples/webgl-ripples.d.ts +22 -0
- package/dist/webgl-ripples/webgl-ripples.js +1 -0
- package/package.json +60 -0
package/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
<script module>
|
|
2
|
+
import { dev } from '$app/environment';
|
|
3
|
+
|
|
4
|
+
// Simple shared variable - no store needed!
|
|
5
|
+
let globalAnimated = !dev;
|
|
6
|
+
|
|
7
|
+
// Export control functions instead of the variable
|
|
8
|
+
export function toggleAnimation() {
|
|
9
|
+
globalAnimated = !globalAnimated;
|
|
10
|
+
updateAllInstances();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function setAnimated(value: boolean) {
|
|
14
|
+
globalAnimated = value;
|
|
15
|
+
updateAllInstances();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Keep track of all instances for updates
|
|
19
|
+
let instances = new Set<() => void>();
|
|
20
|
+
|
|
21
|
+
function updateAllInstances() {
|
|
22
|
+
instances.forEach((update) => update());
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function registerInstance(updateFn: () => void) {
|
|
26
|
+
instances.add(updateFn);
|
|
27
|
+
return () => instances.delete(updateFn);
|
|
28
|
+
}
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<script lang="ts">
|
|
32
|
+
import { onDestroy } from 'svelte';
|
|
33
|
+
import type { Attachment } from 'svelte/attachments';
|
|
34
|
+
import { Ripples, type RipplesOptions } from './webgl-ripples/webgl-ripples.js';
|
|
35
|
+
|
|
36
|
+
import logoGlow from './assets/logo-parts/glow.svg';
|
|
37
|
+
import logoLigature from './assets/logo-parts/ligature.svg';
|
|
38
|
+
import logoShadow from './assets/logo-parts/shadow.svg';
|
|
39
|
+
import logoSquare from './assets/logo-parts/square.svg?inline';
|
|
40
|
+
|
|
41
|
+
interface Props {
|
|
42
|
+
size?: string;
|
|
43
|
+
toggleAnimationWithShift?: boolean;
|
|
44
|
+
ripplesOptions?: RipplesOptions;
|
|
45
|
+
boundingBox?: 'square' | 'default' | 'cropped' | 'encircled';
|
|
46
|
+
class?: string;
|
|
47
|
+
onClick?: (event: MouseEvent | KeyboardEvent) => void;
|
|
48
|
+
[key: string]: unknown; // Allow any additional props
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let {
|
|
52
|
+
size = '100%',
|
|
53
|
+
toggleAnimationWithShift = false,
|
|
54
|
+
ripplesOptions: ripplesOptionsProp = {},
|
|
55
|
+
boundingBox = 'default',
|
|
56
|
+
class: className = '',
|
|
57
|
+
onClick = undefined,
|
|
58
|
+
...restProps
|
|
59
|
+
}: Props = $props();
|
|
60
|
+
|
|
61
|
+
// Use global animation state shared across ALL instances
|
|
62
|
+
let animated = $state(globalAnimated);
|
|
63
|
+
|
|
64
|
+
// Register for updates from other instances
|
|
65
|
+
let unregister = registerInstance(() => {
|
|
66
|
+
animated = globalAnimated;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Clean up on destroy
|
|
70
|
+
onDestroy(unregister);
|
|
71
|
+
|
|
72
|
+
let ripples: Ripples | null;
|
|
73
|
+
let animatedElements: Element[];
|
|
74
|
+
let animate: (time: number) => void;
|
|
75
|
+
|
|
76
|
+
// Reactive effect to handle animation state changes from global store
|
|
77
|
+
$effect(() => {
|
|
78
|
+
if (animated) {
|
|
79
|
+
// Start animation - only if elements are available
|
|
80
|
+
if (animatedElements && animate) {
|
|
81
|
+
for (const el of animatedElements) {
|
|
82
|
+
(el as HTMLElement).style.transition = '';
|
|
83
|
+
}
|
|
84
|
+
requestAnimationFrame(animate);
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
// Stop animation - only if elements are available
|
|
88
|
+
if (animatedElements) {
|
|
89
|
+
if (ripples) {
|
|
90
|
+
ripples.destroy();
|
|
91
|
+
ripples = null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Reset transforms with smooth transition
|
|
95
|
+
for (const el of animatedElements) {
|
|
96
|
+
(el as HTMLElement).style.transition = 'transform 0.8s cubic-bezier(0.4, 0, 0.2, 1)';
|
|
97
|
+
(el as HTMLElement).style.transform = 'translate(0%, 0%)';
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const logoAnimation: Attachment = (element) => {
|
|
104
|
+
animatedElements = [...element.children].filter((child) => child.classList.contains('animate'));
|
|
105
|
+
const ripplesElement = element.getElementsByClassName('ripples')[0] as HTMLElement | undefined;
|
|
106
|
+
|
|
107
|
+
// Higher resolution for smaller sizes to avoid blurriness
|
|
108
|
+
// For small logos, use closer to 1:1 ratio, for large logos cap at 512
|
|
109
|
+
const elementSize = ripplesElement?.offsetWidth || 100;
|
|
110
|
+
const resolution = !ripplesElement ? 512 : Math.min(512, Math.max(128, elementSize * 0.8));
|
|
111
|
+
|
|
112
|
+
// Scale drop radius based on element size: 15px at 500px down to 2px at 62px
|
|
113
|
+
// Linear interpolation from 62px:2px to 800px:23.36px, capped at 20px
|
|
114
|
+
// Formula: 2 + ((elementSize - 62) / (800 - 62)) * (23.36 - 2)
|
|
115
|
+
const scaledDropRadius = Math.min(
|
|
116
|
+
20,
|
|
117
|
+
Math.max(2, 2 + ((elementSize - 62) / (800 - 62)) * 21.36)
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// Scale wave propagation based on element size: 2.0 at 500px down to 0.2 at 125px
|
|
121
|
+
// Linear interpolation for sizes 500px down to 125px, then clamp at 0.2 for smaller
|
|
122
|
+
const scaledWavePropagation = Math.max(
|
|
123
|
+
0.2,
|
|
124
|
+
Math.min(2.0, 2.0 - ((500 - elementSize) / (500 - 125)) * 1.5)
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const DEFAULT_RIPPLES_OPTIONS = {
|
|
128
|
+
resolution,
|
|
129
|
+
dropRadius: scaledDropRadius,
|
|
130
|
+
perturbance: 0.04,
|
|
131
|
+
// Ripple tuning parameters
|
|
132
|
+
wavePropagation: scaledWavePropagation,
|
|
133
|
+
dampening: 0.997 // Ripple dampening (0.999 = very long lasting, 0.995 = normal, 0.99 = quick fade)
|
|
134
|
+
};
|
|
135
|
+
const rippleOptions = { ...DEFAULT_RIPPLES_OPTIONS, ...ripplesOptionsProp };
|
|
136
|
+
|
|
137
|
+
// Set up ResizeObserver to handle component resizing
|
|
138
|
+
let resizeObserver: ResizeObserver | null = null;
|
|
139
|
+
let lastWidth = ripplesElement?.offsetWidth;
|
|
140
|
+
let lastHeight = ripplesElement?.offsetHeight;
|
|
141
|
+
let resizeTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
142
|
+
|
|
143
|
+
if (ripplesElement && typeof ResizeObserver !== 'undefined') {
|
|
144
|
+
resizeObserver = new ResizeObserver(() => {
|
|
145
|
+
const currentWidth = ripplesElement.offsetWidth;
|
|
146
|
+
const currentHeight = ripplesElement.offsetHeight;
|
|
147
|
+
|
|
148
|
+
// Only recreate if size actually changed significantly (more than 5px)
|
|
149
|
+
const widthChanged = Math.abs(currentWidth - (lastWidth || 0)) > 5;
|
|
150
|
+
const heightChanged = Math.abs(currentHeight - (lastHeight || 0)) > 5;
|
|
151
|
+
|
|
152
|
+
if (widthChanged || heightChanged) {
|
|
153
|
+
lastWidth = currentWidth;
|
|
154
|
+
lastHeight = currentHeight;
|
|
155
|
+
|
|
156
|
+
// Debounce the recreation to avoid too many WebGL contexts
|
|
157
|
+
if (resizeTimeout) {
|
|
158
|
+
clearTimeout(resizeTimeout);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
resizeTimeout = setTimeout(() => {
|
|
162
|
+
if (animated && ripplesElement) {
|
|
163
|
+
// Destroy old instance first
|
|
164
|
+
if (ripples) {
|
|
165
|
+
try {
|
|
166
|
+
ripples.destroy();
|
|
167
|
+
} catch (e) {
|
|
168
|
+
console.error('Error destroying ripples:', e);
|
|
169
|
+
}
|
|
170
|
+
ripples = null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Wait a frame before creating new instance to ensure cleanup
|
|
174
|
+
requestAnimationFrame(() => {
|
|
175
|
+
if (!ripples && animated && ripplesElement) {
|
|
176
|
+
// Higher resolution for smaller sizes to avoid blurriness
|
|
177
|
+
const newResolution = Math.min(512, Math.max(128, currentWidth * 0.8));
|
|
178
|
+
// Scale drop radius and wave propagation for new size
|
|
179
|
+
// Linear interpolation from 62px:2px to 800px:23.36px, capped at 20px
|
|
180
|
+
const newScaledDropRadius = Math.min(
|
|
181
|
+
20,
|
|
182
|
+
Math.max(2, 2 + ((currentWidth - 62) / (800 - 62)) * 21.36)
|
|
183
|
+
);
|
|
184
|
+
const newScaledWavePropagation = Math.max(
|
|
185
|
+
0.2,
|
|
186
|
+
Math.min(2.0, 2.0 - ((500 - currentWidth) / (500 - 125)) * 1.5)
|
|
187
|
+
);
|
|
188
|
+
const newRippleOptions = {
|
|
189
|
+
...rippleOptions,
|
|
190
|
+
resolution: newResolution,
|
|
191
|
+
dropRadius: newScaledDropRadius,
|
|
192
|
+
wavePropagation: newScaledWavePropagation
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
ripples = new Ripples(ripplesElement, newRippleOptions);
|
|
197
|
+
} catch (e) {
|
|
198
|
+
console.error('Error creating ripples:', e);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}, 100); // 100ms debounce
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
resizeObserver.observe(ripplesElement);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let angle = $state(0);
|
|
210
|
+
let lastDropTime = $state(0);
|
|
211
|
+
let lastTime = 0;
|
|
212
|
+
|
|
213
|
+
animate = (time: number) => {
|
|
214
|
+
// Exit if we shouldn't be running
|
|
215
|
+
if (!animated) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const deltaTime = lastTime ? time - lastTime : 0;
|
|
219
|
+
lastTime = time;
|
|
220
|
+
|
|
221
|
+
// Create ripples if needed (and not already being created by resize)
|
|
222
|
+
if (animated && !ripples && ripplesElement && !resizeTimeout) {
|
|
223
|
+
try {
|
|
224
|
+
ripples = new Ripples(ripplesElement, rippleOptions);
|
|
225
|
+
} catch (e) {
|
|
226
|
+
console.error('Error creating ripples in animation loop:', e);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Automatic drops (only when animated)
|
|
231
|
+
if (animated && lastDropTime !== null && ripples && ripplesElement) {
|
|
232
|
+
if (time - lastDropTime > 3000) {
|
|
233
|
+
lastDropTime = time;
|
|
234
|
+
const x = Math.random() * ripplesElement.offsetWidth;
|
|
235
|
+
const y = Math.random() * ripplesElement.offsetHeight;
|
|
236
|
+
// Use scaled drop radius for automatic drops
|
|
237
|
+
const currentSize = ripplesElement.offsetWidth;
|
|
238
|
+
const autoDropRadius = Math.min(
|
|
239
|
+
20,
|
|
240
|
+
Math.max(2, 2 + ((currentSize - 62) / (800 - 62)) * 21.36)
|
|
241
|
+
);
|
|
242
|
+
// Scale strength based on size - ensure minimum visibility for small sizes
|
|
243
|
+
const sizeFactor = Math.max(0.7, Math.min(1, currentSize / 200)); // Never below 70%
|
|
244
|
+
const baseStrength = 0.1 + Math.random() * 0.04;
|
|
245
|
+
const strength = Math.max(0.08, baseStrength * sizeFactor); // Minimum 0.08 for visibility
|
|
246
|
+
ripples.drop(x, y, autoDropRadius, strength);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Animate ligature
|
|
251
|
+
angle += deltaTime;
|
|
252
|
+
|
|
253
|
+
// Original movement in 0-4 range
|
|
254
|
+
const origX = 2 + 2 * Math.cos(angle / 971 - Math.PI);
|
|
255
|
+
const origY = 2 + 2 * Math.sin(angle / 601 - Math.PI / 2);
|
|
256
|
+
|
|
257
|
+
// Rotate -45 degrees: transform the 4x4 square into a diamond
|
|
258
|
+
const dx = animated ? ((origX + origY) * Math.sqrt(2)) / 2 : 0;
|
|
259
|
+
const dy = animated ? ((-origX + origY) * Math.sqrt(2)) / 2 : 0;
|
|
260
|
+
|
|
261
|
+
// The animation percentages were designed for the full 800px canvas
|
|
262
|
+
// Now we need to scale them for each element's actual size
|
|
263
|
+
for (const el of animatedElements) {
|
|
264
|
+
if (el.classList.contains('shadow')) {
|
|
265
|
+
// Shadow is 540x766, need to scale movement to match original animation
|
|
266
|
+
// Original: 4% of 800px = 32px. For shadow: 32px / 540px = 5.93%
|
|
267
|
+
const scaleX = 800 / 540;
|
|
268
|
+
const scaleY = 800 / 766;
|
|
269
|
+
(el as HTMLElement).style.transform = `translate(${dx * scaleX}%, ${dy * scaleY}%)`;
|
|
270
|
+
} else if (el.classList.contains('ligature')) {
|
|
271
|
+
// Ligature is 440x666, need to scale movement to match original animation
|
|
272
|
+
// Original: 4% of 800px = 32px. For ligature: 32px / 440px = 7.27%
|
|
273
|
+
const scaleX = 800 / 440;
|
|
274
|
+
const scaleY = 800 / 666;
|
|
275
|
+
(el as HTMLElement).style.transform = `translate(${dx * scaleX}%, ${dy * scaleY}%)`;
|
|
276
|
+
} else {
|
|
277
|
+
// Default for any other animated elements
|
|
278
|
+
(el as HTMLElement).style.transform = `translate(${dx}%, ${dy}%)`;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Only continue animation if animated
|
|
283
|
+
if (animated) {
|
|
284
|
+
requestAnimationFrame(animate);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// Start animation if initially animated
|
|
289
|
+
if (animated) {
|
|
290
|
+
requestAnimationFrame(animate);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return function () {
|
|
294
|
+
if (resizeTimeout) {
|
|
295
|
+
clearTimeout(resizeTimeout);
|
|
296
|
+
}
|
|
297
|
+
if (ripples) {
|
|
298
|
+
try {
|
|
299
|
+
ripples.destroy();
|
|
300
|
+
} catch (e) {
|
|
301
|
+
console.error('Error destroying ripples on cleanup:', e);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (resizeObserver) {
|
|
305
|
+
resizeObserver.disconnect();
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
function handleClick(event: MouseEvent | KeyboardEvent) {
|
|
311
|
+
// Call external callback first if provided
|
|
312
|
+
if (onClick) {
|
|
313
|
+
onClick(event);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// For keyboard events, only respond to Enter key
|
|
317
|
+
if ('key' in event && event.key !== 'Enter') {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Shift key controls whether click drops or toggles animation.
|
|
322
|
+
if (toggleAnimationWithShift !== event.shiftKey) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Update global state - affects ALL LeftiumLogo instances immediately
|
|
327
|
+
// The reactive effect above will handle the animation start/stop logic
|
|
328
|
+
toggleAnimation();
|
|
329
|
+
}
|
|
330
|
+
</script>
|
|
331
|
+
|
|
332
|
+
<logo-container style:--size={size} class="{boundingBox} {className}" role="none" {...restProps}>
|
|
333
|
+
<grid-logo {@attach logoAnimation}>
|
|
334
|
+
<img class="animate shadow" alt="" src={logoShadow} />
|
|
335
|
+
<img class="glow" alt="" src={logoGlow} />
|
|
336
|
+
<div
|
|
337
|
+
class="ripples square"
|
|
338
|
+
style:background-image={`url("${logoSquare}")`}
|
|
339
|
+
onclick={handleClick}
|
|
340
|
+
onkeydown={handleClick}
|
|
341
|
+
role="button"
|
|
342
|
+
tabindex="0"
|
|
343
|
+
aria-label="Toggle logo animation"
|
|
344
|
+
></div>
|
|
345
|
+
<img class="animate ligature" alt="" src={logoLigature} />
|
|
346
|
+
</grid-logo>
|
|
347
|
+
</logo-container>
|
|
348
|
+
|
|
349
|
+
<style>
|
|
350
|
+
/* Container that defines the bounding box */
|
|
351
|
+
logo-container {
|
|
352
|
+
display: inline-block;
|
|
353
|
+
position: relative;
|
|
354
|
+
width: var(--size);
|
|
355
|
+
aspect-ratio: 1;
|
|
356
|
+
user-select: none;
|
|
357
|
+
-webkit-user-select: none;
|
|
358
|
+
-moz-user-select: none;
|
|
359
|
+
-ms-user-select: none;
|
|
360
|
+
overflow: visible;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/* Square mode - square container */
|
|
364
|
+
logo-container.square {
|
|
365
|
+
aspect-ratio: 1;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/* Default mode - square container */
|
|
369
|
+
logo-container.default {
|
|
370
|
+
aspect-ratio: 1;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/* Encircled mode - square container */
|
|
374
|
+
logo-container.encircled {
|
|
375
|
+
aspect-ratio: 1;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/* Cropped mode - match ellipse aspect ratio: 723.08875/812.58868 ≈ 0.8906 */
|
|
379
|
+
logo-container.cropped {
|
|
380
|
+
aspect-ratio: 0.8906;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/* Grid that holds all the logo elements */
|
|
384
|
+
grid-logo {
|
|
385
|
+
position: absolute;
|
|
386
|
+
display: grid;
|
|
387
|
+
place-items: center;
|
|
388
|
+
width: 100%; /* Default width, will be overridden for default/encircled modes */
|
|
389
|
+
aspect-ratio: 1;
|
|
390
|
+
|
|
391
|
+
img {
|
|
392
|
+
/* Prevent PicoCSS styles. */
|
|
393
|
+
max-width: unset !important;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/* Square bounding box mode - grid fills the container */
|
|
398
|
+
logo-container.square grid-logo {
|
|
399
|
+
width: 100%;
|
|
400
|
+
left: 0;
|
|
401
|
+
top: 0;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/* Default bounding box mode - grid is scaled down to leave some padding */
|
|
405
|
+
logo-container.default grid-logo {
|
|
406
|
+
/* Grid is 1/1.2519 = 79.88% of container to account for padding */
|
|
407
|
+
width: calc(100% / 1.2519);
|
|
408
|
+
/* Center within container */
|
|
409
|
+
left: calc((100% - 100% / 1.2519) / 2);
|
|
410
|
+
top: calc((100% - 100% / 1.2519) / 2);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/* Encircled bounding box mode - grid is scaled down more to leave full padding */
|
|
414
|
+
logo-container.encircled grid-logo {
|
|
415
|
+
/* Grid is 1/1.5037 = 66.5% of container (532/800) */
|
|
416
|
+
width: calc(100% / 1.5037);
|
|
417
|
+
/* Center within container */
|
|
418
|
+
left: calc((100% - 100% / 1.5037) / 2);
|
|
419
|
+
top: calc((100% - 100% / 1.5037) / 2);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/* Cropped bounding box mode - grid scaled and positioned to match reference SVG */
|
|
423
|
+
logo-container.cropped grid-logo {
|
|
424
|
+
/* Square is 1131.371px in a 1447px wide container = 78.2% */
|
|
425
|
+
width: 78.2%;
|
|
426
|
+
/* Position calculations from SVG transforms */
|
|
427
|
+
/* Final square position after transforms: x=122.18, y=247.73 */
|
|
428
|
+
/* Left offset: 122.18/1447 = 8.44% */
|
|
429
|
+
left: 8.44%;
|
|
430
|
+
/* Top offset: 247.73/1626.9 = 15.23% */
|
|
431
|
+
top: 15.23%;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/* Individual logo elements positioned relative to the square */
|
|
435
|
+
grid-logo > * {
|
|
436
|
+
position: absolute;
|
|
437
|
+
grid-column: 1 / 2;
|
|
438
|
+
grid-row: 1 / 2;
|
|
439
|
+
will-change: auto;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/* Square image - base element at 532x532 */
|
|
443
|
+
.ripples.square {
|
|
444
|
+
width: 100%;
|
|
445
|
+
aspect-ratio: 1;
|
|
446
|
+
background-size: contain;
|
|
447
|
+
background-position: center;
|
|
448
|
+
background-repeat: no-repeat;
|
|
449
|
+
z-index: 2;
|
|
450
|
+
cursor: pointer;
|
|
451
|
+
outline: none;
|
|
452
|
+
|
|
453
|
+
/* Prevent PicoCSS [role=button] styles. */
|
|
454
|
+
padding: 0 !important;
|
|
455
|
+
border: none !important;
|
|
456
|
+
border-radius: 0 !important;
|
|
457
|
+
outline: none !important;
|
|
458
|
+
box-shadow: none !important;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.ripples.square:focus {
|
|
462
|
+
outline: none;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/* Glow - 647x647, centered on square */
|
|
466
|
+
.glow {
|
|
467
|
+
width: calc(100% * 647 / 532);
|
|
468
|
+
height: calc(100% * 647 / 532);
|
|
469
|
+
/* Center it: (647-532)/2 = 57.5px offset at original scale */
|
|
470
|
+
left: calc(100% * -57.5 / 532);
|
|
471
|
+
top: calc(100% * -57.5 / 532);
|
|
472
|
+
z-index: 1;
|
|
473
|
+
/* Glow should not capture clicks */
|
|
474
|
+
pointer-events: none;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/* Shadow - 540x766, positioned to outline the ligature */
|
|
478
|
+
.shadow {
|
|
479
|
+
width: calc(100% * 540 / 532);
|
|
480
|
+
height: calc(100% * 766 / 532);
|
|
481
|
+
/* Shadow should be centered on ligature */
|
|
482
|
+
/* Ligature is 440x666, shadow is 540x766 */
|
|
483
|
+
/* Shadow is (540-440)/2 = 50px wider on each side, (766-666)/2 = 50px taller on each side */
|
|
484
|
+
left: calc(100% * (133 - 50) / 532); /* Center shadow on ligature */
|
|
485
|
+
top: calc(100% * (-66 - 50) / 532);
|
|
486
|
+
z-index: 0;
|
|
487
|
+
/* Shadow should not capture clicks */
|
|
488
|
+
pointer-events: none;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/* Ligature - 440x666, positioned based on SVG */
|
|
492
|
+
.ligature {
|
|
493
|
+
width: calc(100% * 440 / 532);
|
|
494
|
+
height: calc(100% * 666 / 532);
|
|
495
|
+
/* Ligature extends to the right and below the square */
|
|
496
|
+
/* Looking at the SVG path, the ligature starts at top-left and extends right and down */
|
|
497
|
+
left: calc(100% * 133.5 / 532);
|
|
498
|
+
top: calc(100% * -65.75 / 532);
|
|
499
|
+
z-index: 3;
|
|
500
|
+
pointer-events: none;
|
|
501
|
+
}
|
|
502
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare function toggleAnimation(): void;
|
|
2
|
+
export declare function setAnimated(value: boolean): void;
|
|
3
|
+
export declare function registerInstance(updateFn: () => void): () => boolean;
|
|
4
|
+
import { type RipplesOptions } from './webgl-ripples/webgl-ripples.js';
|
|
5
|
+
interface Props {
|
|
6
|
+
size?: string;
|
|
7
|
+
toggleAnimationWithShift?: boolean;
|
|
8
|
+
ripplesOptions?: RipplesOptions;
|
|
9
|
+
boundingBox?: 'square' | 'default' | 'cropped' | 'encircled';
|
|
10
|
+
class?: string;
|
|
11
|
+
onClick?: (event: MouseEvent | KeyboardEvent) => void;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
declare const LeftiumLogo: import("svelte").Component<Props, {}, "">;
|
|
15
|
+
type LeftiumLogo = ReturnType<typeof LeftiumLogo>;
|
|
16
|
+
export default LeftiumLogo;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
3
|
+
|
|
4
|
+
<svg
|
|
5
|
+
width="128"
|
|
6
|
+
height="128"
|
|
7
|
+
viewBox="0 0 128 128"
|
|
8
|
+
version="1.1"
|
|
9
|
+
id="svg1"
|
|
10
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
11
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
12
|
+
xmlns:svg="http://www.w3.org/2000/svg">
|
|
13
|
+
<defs
|
|
14
|
+
id="defs1">
|
|
15
|
+
<linearGradient
|
|
16
|
+
xlink:href="#linearGradient1"
|
|
17
|
+
id="linearGradient2"
|
|
18
|
+
x1="-13953.743"
|
|
19
|
+
y1="20745.297"
|
|
20
|
+
x2="-13153.738"
|
|
21
|
+
y2="19945.293"
|
|
22
|
+
gradientUnits="userSpaceOnUse"
|
|
23
|
+
spreadMethod="pad"
|
|
24
|
+
gradientTransform="matrix(1.4378303,0,0,1.4378303,20080.608,-28687.206)" />
|
|
25
|
+
<linearGradient
|
|
26
|
+
id="linearGradient1">
|
|
27
|
+
<stop
|
|
28
|
+
style="stop-color:#0029c1;stop-opacity:1;"
|
|
29
|
+
offset="0"
|
|
30
|
+
id="stop1" />
|
|
31
|
+
<stop
|
|
32
|
+
style="stop-color:#3973ff;stop-opacity:1;"
|
|
33
|
+
offset="0.28999999"
|
|
34
|
+
id="stop3" />
|
|
35
|
+
<stop
|
|
36
|
+
style="stop-color:#0029c1;stop-opacity:1;"
|
|
37
|
+
offset="1"
|
|
38
|
+
id="stop2" />
|
|
39
|
+
</linearGradient>
|
|
40
|
+
<filter
|
|
41
|
+
style="color-interpolation-filters:sRGB"
|
|
42
|
+
id="filter2"
|
|
43
|
+
x="-0.35644"
|
|
44
|
+
y="-0.13903579"
|
|
45
|
+
width="1.6523672"
|
|
46
|
+
height="1.2780716">
|
|
47
|
+
<feGaussianBlur
|
|
48
|
+
stdDeviation="44.27207"
|
|
49
|
+
id="feGaussianBlur2" />
|
|
50
|
+
</filter>
|
|
51
|
+
<filter
|
|
52
|
+
style="color-interpolation-filters:sRGB"
|
|
53
|
+
id="filter3"
|
|
54
|
+
x="-0.10799999"
|
|
55
|
+
y="-0.108"
|
|
56
|
+
width="1.216"
|
|
57
|
+
height="1.216">
|
|
58
|
+
<feGaussianBlur
|
|
59
|
+
stdDeviation="50.911691"
|
|
60
|
+
id="feGaussianBlur3" />
|
|
61
|
+
</filter>
|
|
62
|
+
</defs>
|
|
63
|
+
<g
|
|
64
|
+
id="layer1">
|
|
65
|
+
<g
|
|
66
|
+
id="g1"
|
|
67
|
+
transform="translate(253.40376,284.58137)"
|
|
68
|
+
style="display:inline">
|
|
69
|
+
<g
|
|
70
|
+
id="g8"
|
|
71
|
+
style="display:inline"
|
|
72
|
+
transform="matrix(0.07868598,0,0,0.07868598,-236.035,-265.10778)">
|
|
73
|
+
<path
|
|
74
|
+
id="path2"
|
|
75
|
+
d="M 1052.0206,-70.521304 415.62473,565.8747 l 636.39587,636.396 m 0,-141.421 A 200.0002,200.0002 0 0 0 769.1778,778.00671 m -141.42104,141.421 a 200,200 0 0 0 0,282.84299"
|
|
76
|
+
style="display:inline;opacity:0.7;mix-blend-mode:normal;fill:none;stroke:#000000;stroke-width:200;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter2)" />
|
|
77
|
+
<rect
|
|
78
|
+
style="opacity:0.7;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3)"
|
|
79
|
+
id="rect2"
|
|
80
|
+
width="1131.371"
|
|
81
|
+
height="1131.3708"
|
|
82
|
+
x="-8.6700182"
|
|
83
|
+
y="0.25409326" />
|
|
84
|
+
<rect
|
|
85
|
+
style="fill:url(#linearGradient2);stroke:#ffffff;stroke-width:0;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
86
|
+
id="rect5345-1-4"
|
|
87
|
+
width="1626.7195"
|
|
88
|
+
height="1626.7192"
|
|
89
|
+
x="-220.73523"
|
|
90
|
+
y="-247.48486"
|
|
91
|
+
rx="222.70804" />
|
|
92
|
+
<path
|
|
93
|
+
id="path1"
|
|
94
|
+
d="M 949.57704,-166.14889 217.55333,565.87478 949.57704,1297.8984 m -0.8134,-161.8586 A 230.05284,230.05284 0 0 0 623.41984,810.69599 M 462.37465,971.74111 a 230.05284,230.05284 0 0 0 0,325.34389"
|
|
95
|
+
style="display:inline;fill:none;stroke:#ffffff;stroke-width:230.053;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
96
|
+
</g>
|
|
97
|
+
</g>
|
|
98
|
+
</g>
|
|
99
|
+
</svg>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
3
|
+
|
|
4
|
+
<svg
|
|
5
|
+
width="1375.7469"
|
|
6
|
+
height="1375.7469"
|
|
7
|
+
viewBox="0 0 1375.7469 1375.7469"
|
|
8
|
+
version="1.1"
|
|
9
|
+
id="svg1"
|
|
10
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
+
xmlns:svg="http://www.w3.org/2000/svg">
|
|
12
|
+
<defs
|
|
13
|
+
id="defs1">
|
|
14
|
+
<filter
|
|
15
|
+
style="color-interpolation-filters:sRGB"
|
|
16
|
+
id="filter3"
|
|
17
|
+
x="-0.10799999"
|
|
18
|
+
y="-0.108"
|
|
19
|
+
width="1.216"
|
|
20
|
+
height="1.216">
|
|
21
|
+
<feGaussianBlur
|
|
22
|
+
stdDeviation="50.911691"
|
|
23
|
+
id="feGaussianBlur3" />
|
|
24
|
+
</filter>
|
|
25
|
+
</defs>
|
|
26
|
+
<g
|
|
27
|
+
id="layer1"
|
|
28
|
+
transform="translate(-122.54569,-162.64743)">
|
|
29
|
+
<g
|
|
30
|
+
id="g1"
|
|
31
|
+
transform="translate(253.40376,284.58137)"
|
|
32
|
+
style="display:inline">
|
|
33
|
+
<g
|
|
34
|
+
id="g8"
|
|
35
|
+
style="display:inline">
|
|
36
|
+
<rect
|
|
37
|
+
style="opacity:0.7;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3)"
|
|
38
|
+
id="rect2"
|
|
39
|
+
width="1131.371"
|
|
40
|
+
height="1131.3708"
|
|
41
|
+
x="-8.6700182"
|
|
42
|
+
y="0.25409326" />
|
|
43
|
+
</g>
|
|
44
|
+
</g>
|
|
45
|
+
</g>
|
|
46
|
+
</svg>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
3
|
+
|
|
4
|
+
<svg
|
|
5
|
+
width="935.01953"
|
|
6
|
+
height="1414.2136"
|
|
7
|
+
viewBox="0 0 935.01953 1414.2136"
|
|
8
|
+
version="1.1"
|
|
9
|
+
id="svg1"
|
|
10
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
+
xmlns:svg="http://www.w3.org/2000/svg">
|
|
12
|
+
<defs
|
|
13
|
+
id="defs1" />
|
|
14
|
+
<g
|
|
15
|
+
id="layer1"
|
|
16
|
+
transform="translate(-527.92273,-143.3493)">
|
|
17
|
+
<g
|
|
18
|
+
id="g1"
|
|
19
|
+
transform="translate(253.40376,284.58137)"
|
|
20
|
+
style="display:inline">
|
|
21
|
+
<g
|
|
22
|
+
id="g8"
|
|
23
|
+
style="display:inline">
|
|
24
|
+
<path
|
|
25
|
+
id="path1"
|
|
26
|
+
d="m 1305.7402,214.05999 -636.39613,636.3961 636.39613,636.39611 m -0.7071,-140.7143 a 200,200 0 0 0 -282.8427,-282.8427 m -140.00713,140.0071 a 200,200 0 0 0 0,282.8427"
|
|
27
|
+
style="display:inline;fill:none;stroke:#ffffff;stroke-width:200;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
28
|
+
transform="translate(-253.40376,-284.58137)" />
|
|
29
|
+
</g>
|
|
30
|
+
</g>
|
|
31
|
+
</g>
|
|
32
|
+
</svg>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
3
|
+
|
|
4
|
+
<svg
|
|
5
|
+
width="1148.1566"
|
|
6
|
+
height="1626.7194"
|
|
7
|
+
viewBox="0 0 1148.1566 1626.7194"
|
|
8
|
+
version="1.1"
|
|
9
|
+
id="svg1"
|
|
10
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
+
xmlns:svg="http://www.w3.org/2000/svg">
|
|
12
|
+
<defs
|
|
13
|
+
id="defs1">
|
|
14
|
+
<filter
|
|
15
|
+
style="color-interpolation-filters:sRGB"
|
|
16
|
+
id="filter2"
|
|
17
|
+
x="-0.35644"
|
|
18
|
+
y="-0.13903579"
|
|
19
|
+
width="1.6523672"
|
|
20
|
+
height="1.2780716">
|
|
21
|
+
<feGaussianBlur
|
|
22
|
+
stdDeviation="44.27207"
|
|
23
|
+
id="feGaussianBlur2" />
|
|
24
|
+
</filter>
|
|
25
|
+
</defs>
|
|
26
|
+
<g
|
|
27
|
+
id="layer1"
|
|
28
|
+
transform="translate(-421.35417,-37.096423)">
|
|
29
|
+
<g
|
|
30
|
+
id="g1"
|
|
31
|
+
transform="translate(253.40376,284.58137)"
|
|
32
|
+
style="display:inline">
|
|
33
|
+
<g
|
|
34
|
+
id="g8"
|
|
35
|
+
style="display:inline">
|
|
36
|
+
<path
|
|
37
|
+
id="path2"
|
|
38
|
+
d="M 1052.0206,-70.521304 415.62473,565.8747 l 636.39587,636.396 m 0,-141.421 A 200.0002,200.0002 0 0 0 769.1778,778.00671 m -141.42104,141.421 a 200,200 0 0 0 0,282.84299"
|
|
39
|
+
style="display:inline;opacity:0.7;mix-blend-mode:normal;fill:none;stroke:#000000;stroke-width:200;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter2)" />
|
|
40
|
+
</g>
|
|
41
|
+
</g>
|
|
42
|
+
</g>
|
|
43
|
+
</svg>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
3
|
+
|
|
4
|
+
<svg
|
|
5
|
+
width="1131.371"
|
|
6
|
+
height="1131.3708"
|
|
7
|
+
viewBox="0 0 1131.371 1131.3708"
|
|
8
|
+
version="1.1"
|
|
9
|
+
id="svg1"
|
|
10
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
11
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
12
|
+
xmlns:svg="http://www.w3.org/2000/svg">
|
|
13
|
+
<defs
|
|
14
|
+
id="defs1">
|
|
15
|
+
<linearGradient
|
|
16
|
+
id="linearGradient2"
|
|
17
|
+
x1="-13953.743"
|
|
18
|
+
y1="20745.297"
|
|
19
|
+
x2="-13153.738"
|
|
20
|
+
y2="19945.293"
|
|
21
|
+
gradientUnits="userSpaceOnUse"
|
|
22
|
+
spreadMethod="pad"
|
|
23
|
+
gradientTransform="translate(14110.759,-19779.355)">
|
|
24
|
+
<stop
|
|
25
|
+
style="stop-color:#0029c1;stop-opacity:1;"
|
|
26
|
+
offset="0"
|
|
27
|
+
id="stop1" />
|
|
28
|
+
<stop
|
|
29
|
+
style="stop-color:#3973ff;stop-opacity:1;"
|
|
30
|
+
offset="0.28999999"
|
|
31
|
+
id="stop3" />
|
|
32
|
+
<stop
|
|
33
|
+
style="stop-color:#0029c1;stop-opacity:1;"
|
|
34
|
+
offset="1"
|
|
35
|
+
id="stop2" />
|
|
36
|
+
</linearGradient>
|
|
37
|
+
</defs>
|
|
38
|
+
<g
|
|
39
|
+
id="layer1"
|
|
40
|
+
transform="translate(-244.73374,-284.83546)">
|
|
41
|
+
<g
|
|
42
|
+
id="g1"
|
|
43
|
+
transform="translate(253.40376,284.58137)"
|
|
44
|
+
style="display:inline">
|
|
45
|
+
<g
|
|
46
|
+
id="g8"
|
|
47
|
+
style="display:inline">
|
|
48
|
+
<rect
|
|
49
|
+
style="fill:url(#linearGradient2);stroke:#ffffff;stroke-width:0;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
50
|
+
id="rect5345-1-4"
|
|
51
|
+
width="1131.371"
|
|
52
|
+
height="1131.3708"
|
|
53
|
+
x="-8.6700182"
|
|
54
|
+
y="0.25409326" />
|
|
55
|
+
</g>
|
|
56
|
+
</g>
|
|
57
|
+
</g>
|
|
58
|
+
</svg>
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Reexport your entry components here
|
|
2
|
+
import LeftiumLogo from './LeftiumLogo.svelte';
|
|
3
|
+
import toggleAnimation from './LeftiumLogo.svelte';
|
|
4
|
+
import setAnimated from './LeftiumLogo.svelte';
|
|
5
|
+
import favicon from './assets/favicon.svg';
|
|
6
|
+
export { LeftiumLogo, toggleAnimation, setAnimated, favicon };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface RipplesOptions {
|
|
2
|
+
imageUrl?: string | null;
|
|
3
|
+
resolution?: number;
|
|
4
|
+
dropRadius?: number;
|
|
5
|
+
perturbance?: number;
|
|
6
|
+
interactive?: boolean;
|
|
7
|
+
crossOrigin?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export declare class Ripples {
|
|
11
|
+
constructor(el: string | HTMLElement, options?: RipplesOptions);
|
|
12
|
+
drop(x: number, y: number, radius: number, strength: number): void;
|
|
13
|
+
destroy(): void;
|
|
14
|
+
pause(): void;
|
|
15
|
+
play(): void;
|
|
16
|
+
set(property: string, value: any): void;
|
|
17
|
+
updateSize(): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default Ripples;
|
|
21
|
+
|
|
22
|
+
export declare function isWebGLSupported(): boolean;
|
|
@@ -0,0 +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;
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@leftium/logo",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist",
|
|
6
|
+
"!dist/**/*.test.*",
|
|
7
|
+
"!dist/**/*.spec.*",
|
|
8
|
+
"!dist/webgl-ripples/webgl-ripples-original.*"
|
|
9
|
+
],
|
|
10
|
+
"sideEffects": [
|
|
11
|
+
"**/*.css"
|
|
12
|
+
],
|
|
13
|
+
"svelte": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"svelte": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"svelte": "^5.0.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@eslint/compat": "^1.2.5",
|
|
30
|
+
"@eslint/js": "^9.18.0",
|
|
31
|
+
"@sveltejs/adapter-auto": "^6.0.0",
|
|
32
|
+
"@sveltejs/kit": "^2.22.0",
|
|
33
|
+
"@sveltejs/package": "^2.0.0",
|
|
34
|
+
"@sveltejs/vite-plugin-svelte": "^6.0.0",
|
|
35
|
+
"eslint": "^9.18.0",
|
|
36
|
+
"eslint-config-prettier": "^10.0.1",
|
|
37
|
+
"eslint-plugin-svelte": "^3.0.0",
|
|
38
|
+
"globals": "^16.0.0",
|
|
39
|
+
"prettier": "^3.4.2",
|
|
40
|
+
"prettier-plugin-svelte": "^3.3.3",
|
|
41
|
+
"publint": "^0.3.2",
|
|
42
|
+
"svelte": "^5.0.0",
|
|
43
|
+
"svelte-check": "^4.0.0",
|
|
44
|
+
"typescript": "^5.0.0",
|
|
45
|
+
"typescript-eslint": "^8.20.0",
|
|
46
|
+
"vite": "^7.0.4"
|
|
47
|
+
},
|
|
48
|
+
"keywords": [
|
|
49
|
+
"svelte"
|
|
50
|
+
],
|
|
51
|
+
"scripts": {
|
|
52
|
+
"dev": "vite dev",
|
|
53
|
+
"build": "vite build && npm run prepack",
|
|
54
|
+
"preview": "vite preview",
|
|
55
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
56
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
57
|
+
"format": "prettier --write .",
|
|
58
|
+
"lint": "prettier --check . && eslint ."
|
|
59
|
+
}
|
|
60
|
+
}
|