@shopify/klint 0.0.4

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.
@@ -0,0 +1,1180 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/plugins/index.tsx
21
+ var plugins_exports = {};
22
+ __export(plugins_exports, {
23
+ Color: () => Color_default,
24
+ Easing: () => Easing_default,
25
+ SVGfont: () => SVGfont_default,
26
+ State: () => State_default,
27
+ Text: () => Text_default,
28
+ Thing: () => Thing_default,
29
+ Time: () => Time_default,
30
+ Vector: () => Vector_default
31
+ });
32
+ module.exports = __toCommonJS(plugins_exports);
33
+
34
+ // src/plugins/Color.tsx
35
+ var Color = class {
36
+ constructor() {
37
+ // context: KlintContexts;
38
+ /**
39
+ * Array of predefined colors in the Klint color palette
40
+ */
41
+ this.colors = [
42
+ "#E84D37",
43
+ // coral
44
+ "#7F4C2F",
45
+ // brown
46
+ "#EDBC2F",
47
+ // mustard
48
+ "#BF3034",
49
+ // crimson
50
+ "#18599D",
51
+ // navy
52
+ "#45A7C6",
53
+ // sky
54
+ "#8CB151",
55
+ // olive
56
+ "#252120",
57
+ // charcoal
58
+ "#ECA088",
59
+ // peach
60
+ "#C9B1B8",
61
+ // rose
62
+ "#8F3064",
63
+ // plum
64
+ "#7B8870",
65
+ // sage
66
+ "#C0C180",
67
+ // drab
68
+ "#4B423D",
69
+ // taupe
70
+ "#1A2A65",
71
+ // midnight
72
+ "#EAA550",
73
+ // golden
74
+ "#F17B04",
75
+ // orange
76
+ "#404757"
77
+ // slate
78
+ ];
79
+ }
80
+ // /**
81
+ // * Creates a new Color instance
82
+ // * @param ctx - The Klint context
83
+ // */
84
+ // constructor(ctx: KlintContexts) {
85
+ // this.context = ctx;
86
+ // }
87
+ get coral() {
88
+ return this.colors[0];
89
+ }
90
+ get brown() {
91
+ return this.colors[1];
92
+ }
93
+ get mustard() {
94
+ return this.colors[2];
95
+ }
96
+ get crimson() {
97
+ return this.colors[3];
98
+ }
99
+ get navy() {
100
+ return this.colors[4];
101
+ }
102
+ get sky() {
103
+ return this.colors[5];
104
+ }
105
+ get olive() {
106
+ return this.colors[6];
107
+ }
108
+ get charcoal() {
109
+ return this.colors[7];
110
+ }
111
+ get peach() {
112
+ return this.colors[8];
113
+ }
114
+ get rose() {
115
+ return this.colors[9];
116
+ }
117
+ get plum() {
118
+ return this.colors[10];
119
+ }
120
+ get sage() {
121
+ return this.colors[11];
122
+ }
123
+ get drab() {
124
+ return this.colors[12];
125
+ }
126
+ get taupe() {
127
+ return this.colors[13];
128
+ }
129
+ get midnight() {
130
+ return this.colors[14];
131
+ }
132
+ get golden() {
133
+ return this.colors[15];
134
+ }
135
+ get orange() {
136
+ return this.colors[16];
137
+ }
138
+ get slate() {
139
+ return this.colors[17];
140
+ }
141
+ /**
142
+ * Ensures a color string has a # prefix
143
+ * @param color - Color string in hex format (with or without #)
144
+ * @returns Hex color string with # prefix
145
+ */
146
+ hex(color) {
147
+ return color.startsWith("#") ? color : `#${color}`;
148
+ }
149
+ /**
150
+ * Creates an RGB color string
151
+ * @param r - Red component (0-255)
152
+ * @param g - Green component (0-255)
153
+ * @param b - Blue component (0-255)
154
+ * @returns RGB color string
155
+ */
156
+ rgb(r, g, b) {
157
+ return `rgb(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)})`;
158
+ }
159
+ /**
160
+ * Creates an RGBA color string
161
+ * @param r - Red component (0-255)
162
+ * @param g - Green component (0-255)
163
+ * @param b - Blue component (0-255)
164
+ * @param alpha - Alpha/opacity value (0-1)
165
+ * @returns RGBA color string
166
+ */
167
+ rgba(r, g, b, alpha) {
168
+ return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(
169
+ b
170
+ )}, ${alpha})`;
171
+ }
172
+ /**
173
+ * Creates a grayscale color
174
+ * @param value - Gray value (0-255)
175
+ * @param alpha - Optional alpha/opacity value (0-1)
176
+ * @returns RGB or RGBA grayscale color string
177
+ */
178
+ gray(value, alpha) {
179
+ return alpha !== void 0 ? `rgba(${Math.round(value)}, ${Math.round(value)}, ${Math.round(
180
+ value
181
+ )}, ${alpha})` : `rgb(${Math.round(value)}, ${Math.round(value)}, ${Math.round(value)})`;
182
+ }
183
+ /**
184
+ * Creates an HSL color string
185
+ * @param h - Hue (0-360)
186
+ * @param s - Saturation percentage (0-100)
187
+ * @param l - Lightness percentage (0-100)
188
+ * @returns HSL color string
189
+ */
190
+ hsl(h, s, l) {
191
+ return `hsl(${h % 360}, ${Math.max(0, s)}%, ${Math.max(0, l)}%)`;
192
+ }
193
+ /**
194
+ * Creates an HSLA color string
195
+ * @param h - Hue (0-360)
196
+ * @param s - Saturation percentage (0-100)
197
+ * @param l - Lightness percentage (0-100)
198
+ * @param alpha - Alpha/opacity value (0-1)
199
+ * @returns HSLA color string
200
+ */
201
+ hsla(h, s, l, alpha) {
202
+ return `hsla(${h % 360}, ${Math.max(0, s)}%, ${Math.max(0, l)}%, ${alpha})`;
203
+ }
204
+ /**
205
+ * Creates an LCH color string
206
+ * @param l - Lightness percentage (0-100)
207
+ * @param c - Chroma value
208
+ * @param h - Hue (0-360)
209
+ * @returns LCH color string
210
+ */
211
+ lch(l, c, h) {
212
+ return `lch(${l}% ${c} ${h})`;
213
+ }
214
+ /**
215
+ * Creates an LCH color string with alpha
216
+ * @param l - Lightness percentage (0-100)
217
+ * @param c - Chroma value
218
+ * @param h - Hue (0-360)
219
+ * @param alpha - Alpha/opacity value (0-1)
220
+ * @returns LCH color string with alpha
221
+ */
222
+ lcha(l, c, h, alpha) {
223
+ return `lch(${l}% ${c} ${h} / ${alpha})`;
224
+ }
225
+ /**
226
+ * Creates a LAB color string
227
+ * @param l - Lightness percentage (0-100)
228
+ * @param a - A-axis value (green to red)
229
+ * @param b - B-axis value (blue to yellow)
230
+ * @returns LAB color string
231
+ */
232
+ lab(l, a, b) {
233
+ return `lab(${l}% ${a} ${b})`;
234
+ }
235
+ /**
236
+ * Creates a LAB color string with alpha
237
+ * @param l - Lightness percentage (0-100)
238
+ * @param a - A-axis value (green to red)
239
+ * @param b - B-axis value (blue to yellow)
240
+ * @param alpha - Alpha/opacity value (0-1)
241
+ * @returns LAB color string with alpha
242
+ */
243
+ laba(l, a, b, alpha) {
244
+ return `lab(${l}% ${a} ${b} / ${alpha})`;
245
+ }
246
+ /**
247
+ * Creates an OKLCH color string
248
+ * @param l - Lightness value (0-1)
249
+ * @param c - Chroma value
250
+ * @param h - Hue (0-360)
251
+ * @returns OKLCH color string
252
+ */
253
+ oklch(l, c, h) {
254
+ return `oklch(${l} ${c} ${h})`;
255
+ }
256
+ /**
257
+ * Creates an OKLCH color string with alpha
258
+ * @param l - Lightness value (0-1)
259
+ * @param c - Chroma value
260
+ * @param h - Hue (0-360)
261
+ * @param alpha - Alpha/opacity value (0-1)
262
+ * @returns OKLCH color string with alpha
263
+ */
264
+ oklcha(l, c, h, alpha) {
265
+ return `oklch(${l} ${c} ${h} / ${alpha})`;
266
+ }
267
+ /**
268
+ * Creates an OKLAB color string
269
+ * @param l - Lightness value (0-1)
270
+ * @param a - A-axis value (green to red)
271
+ * @param b - B-axis value (blue to yellow)
272
+ * @returns OKLAB color string
273
+ */
274
+ oklab(l, a, b) {
275
+ return `oklab(${l} ${a} ${b})`;
276
+ }
277
+ /**
278
+ * Creates an OKLAB color string with alpha
279
+ * @param l - Lightness value (0-1)
280
+ * @param a - A-axis value (green to red)
281
+ * @param b - B-axis value (blue to yellow)
282
+ * @param alpha - Alpha/opacity value (0-1)
283
+ * @returns OKLAB color string with alpha
284
+ */
285
+ oklaba(l, a, b, alpha) {
286
+ return `oklab(${l} ${a} ${b} / ${alpha})`;
287
+ }
288
+ /**
289
+ * Blends two colors using CSS color-mix
290
+ * @param colorA - First color
291
+ * @param colorB - Second color
292
+ * @param factor - Blend factor (0-1) where 0 is colorA and 1 is colorB
293
+ * @param colorMode - Color space to blend in (e.g., "oklch", "hsl")
294
+ * @returns Blended color string
295
+ */
296
+ blendColors(colorA, colorB, factor, colorMode = "oklch") {
297
+ const t = Math.max(0, Math.min(1, factor)) * 100;
298
+ return `color-mix(in ${colorMode}, ${colorA}, ${colorB} ${t}%)`;
299
+ }
300
+ /**
301
+ * Creates a palette of colors based on a single base color
302
+ * @param baseColor - The base color to create palette from
303
+ * @param steps - Number of steps in each direction (lighter/darker)
304
+ * @returns Array of color strings forming a palette
305
+ */
306
+ createPalette(baseColor, steps = 9) {
307
+ const palette = [];
308
+ for (let i = 1; i < steps; i++) {
309
+ const factor = i / steps;
310
+ palette.unshift(this.blendColors(baseColor, "#ffffff", factor, "oklch"));
311
+ }
312
+ palette.push(baseColor);
313
+ for (let i = 1; i < steps; i++) {
314
+ const factor = i / steps;
315
+ palette.push(this.blendColors(baseColor, "#000000", factor, "oklch"));
316
+ }
317
+ return palette;
318
+ }
319
+ /**
320
+ * Creates a complementary color (opposite on the color wheel)
321
+ * @param color - Base color
322
+ * @returns Complementary color string
323
+ */
324
+ complementary(color) {
325
+ return this.blendColors(color, "hsl(180deg 100% 50%)", 1, "hsl");
326
+ }
327
+ /**
328
+ * Creates analogous colors (adjacent on the color wheel)
329
+ * @param color - Base color
330
+ * @param angle - Angle of separation in degrees
331
+ * @returns Tuple of two analogous color strings
332
+ */
333
+ analogous(color, angle = 30) {
334
+ return [
335
+ this.blendColors(color, `hsl(${-angle}deg 100% 50%)`, 1, "hsl"),
336
+ this.blendColors(color, `hsl(${angle}deg 100% 50%)`, 1, "hsl")
337
+ ];
338
+ }
339
+ /**
340
+ * Creates a triadic color scheme (three colors evenly spaced on the color wheel)
341
+ * @param color - Base color
342
+ * @returns Tuple of two additional colors to form a triadic scheme
343
+ */
344
+ triadic(color) {
345
+ return [
346
+ this.blendColors(color, "hsl(120deg 100% 50%)", 1, "hsl"),
347
+ this.blendColors(color, "hsl(240deg 100% 50%)", 1, "hsl")
348
+ ];
349
+ }
350
+ /**
351
+ * Increases the saturation of a color
352
+ * @param color - Base color
353
+ * @param amount - Amount to saturate (percentage)
354
+ * @returns Saturated color string
355
+ */
356
+ saturate(color, amount) {
357
+ return this.blendColors(
358
+ color,
359
+ "hsl(0deg 100% 50% / 0%)",
360
+ amount / 100,
361
+ "hsl"
362
+ );
363
+ }
364
+ /**
365
+ * Lightens a color by mixing with white
366
+ * @param color - Base color
367
+ * @param amount - Amount to lighten (percentage)
368
+ * @returns Lightened color string
369
+ */
370
+ lighten(color, amount) {
371
+ return this.blendColors(color, "white", amount / 100, "hsl");
372
+ }
373
+ /**
374
+ * Darkens a color by mixing with black
375
+ * @param color - Base color
376
+ * @param amount - Amount to darken (percentage)
377
+ * @returns Darkened color string
378
+ */
379
+ darken(color, amount) {
380
+ return this.blendColors(color, "black", amount / 100, "hsl");
381
+ }
382
+ };
383
+ var Color_default = Color;
384
+
385
+ // src/plugins/Easing.tsx
386
+ var Easing = class {
387
+ constructor(ctx) {
388
+ this.normalize = (val) => {
389
+ return val * 0.5 + 0.5;
390
+ };
391
+ this.expand = (val) => {
392
+ return val * 2 - 1;
393
+ };
394
+ this.inout = (val, power = 2) => {
395
+ const m = val - 1;
396
+ const t = val * 2;
397
+ if (t < 1) {
398
+ return val * Math.pow(t, power - 1);
399
+ }
400
+ return power % 2 === 0 ? 1 - Math.pow(m, power) * Math.pow(2, power - 1) : 1 + Math.pow(m, power) * Math.pow(2, power - 1);
401
+ };
402
+ this.in = (val, power = 2) => {
403
+ return Math.pow(val, power);
404
+ };
405
+ this.out = (val, power = 2) => {
406
+ const m = val - 1;
407
+ return power % 2 === 0 ? 1 - Math.pow(m, power) : 1 + Math.pow(m, power);
408
+ };
409
+ this.overshootIn = (val) => {
410
+ const k = 1.70158;
411
+ return val * val * (val * (k + 1) - k);
412
+ };
413
+ this.overshootOut = (val) => {
414
+ const m = val - 1;
415
+ const k = 1.70158;
416
+ return 1 + m * m * (m * (k + 1) + k);
417
+ };
418
+ this.overshootInOut = (val) => {
419
+ const m = val - 1;
420
+ const t = val * 2;
421
+ const k = 1.70158 * 1.525;
422
+ if (val < 0.5)
423
+ return val * t * (t * (k + 1) - k);
424
+ return 1 + 2 * m * m * (2 * m * (k + 1) + k);
425
+ };
426
+ this.bounceOut = (val) => {
427
+ const r = 1 / 2.75;
428
+ const k1 = r;
429
+ const k2 = 2 * r;
430
+ const k3 = 1.5 * r;
431
+ const k4 = 2.5 * r;
432
+ const k5 = 2.25 * r;
433
+ const k6 = 2.625 * r;
434
+ const k0 = 7.5625;
435
+ let t;
436
+ if (val < k1) {
437
+ return k0 * val * val;
438
+ } else if (val < k2) {
439
+ t = val - k3;
440
+ return k0 * t * t + 0.75;
441
+ } else if (val < k4) {
442
+ t = val - k5;
443
+ return k0 * t * t + 0.9375;
444
+ }
445
+ t = val - k6;
446
+ return k0 * t * t + 0.984375;
447
+ };
448
+ this.bounceIn = (val) => {
449
+ return 1 - this.bounceOut(1 - val);
450
+ };
451
+ this.bounceInOut = (val) => {
452
+ const t = val * 2;
453
+ if (t < 1)
454
+ return 0.5 - 0.5 * this.bounceOut(1 - t);
455
+ return 0.5 + 0.5 * this.bounceOut(t - 1);
456
+ };
457
+ this.elasticIn = (val) => {
458
+ const m = val - 1;
459
+ return -Math.pow(2, 10 * m) * Math.sin((m * 40 - 3) * Math.PI / 6);
460
+ };
461
+ this.elasticOut = (val) => {
462
+ return 1 + Math.pow(2, 10 * -val) * Math.sin((-val * 40 - 3) * Math.PI / 6);
463
+ };
464
+ this.elasticInOut = (val) => {
465
+ const s = 2 * val - 1;
466
+ const k = (80 * s - 9) * Math.PI / 18;
467
+ if (s < 0)
468
+ return -0.5 * Math.pow(2, 10 * s) * Math.sin(k);
469
+ return 1 + 0.5 * Math.pow(2, -10 * s) * Math.sin(k);
470
+ };
471
+ this.smoothstep = (val, x0 = 0, x1 = 1) => {
472
+ let p = (val - x0) / (x1 - x0);
473
+ p = p < 0 ? 0 : p > 1 ? 1 : p;
474
+ return p * p * (3 - 2 * p);
475
+ };
476
+ this.log = () => {
477
+ console.log(this);
478
+ };
479
+ this.context = ctx;
480
+ }
481
+ };
482
+ var Easing_default = Easing;
483
+
484
+ // src/plugins/SVGfont.tsx
485
+ var SVGfont = class {
486
+ constructor(context) {
487
+ this.context = context;
488
+ this.font = "";
489
+ this.SCALE = 1;
490
+ this.targetXHeight = 256;
491
+ this.metrics = {
492
+ fontFamily: "",
493
+ fontWeight: "",
494
+ unitsPerEm: 0,
495
+ ascent: 0,
496
+ descent: 0,
497
+ xHeight: 0,
498
+ capHeight: 0
499
+ };
500
+ this.glyphs = /* @__PURE__ */ new Map();
501
+ this.context = context;
502
+ }
503
+ parse(font, desiredXHeight = 256) {
504
+ const parser = new DOMParser();
505
+ const doc = parser.parseFromString(font, "text/xml");
506
+ const fontFace = doc.querySelector("font-face");
507
+ if (fontFace) {
508
+ this.metrics.fontFamily = fontFace.getAttribute("font-family") || "";
509
+ this.metrics.fontWeight = fontFace.getAttribute("font-weight") || "";
510
+ this.metrics.unitsPerEm = parseInt(
511
+ fontFace.getAttribute("units-per-em") || "0"
512
+ );
513
+ this.metrics.ascent = parseInt(fontFace.getAttribute("ascent") || "0");
514
+ this.metrics.descent = parseInt(fontFace.getAttribute("descent") || "0");
515
+ this.metrics.xHeight = parseInt(fontFace.getAttribute("x-height") || "0");
516
+ this.metrics.capHeight = parseInt(
517
+ fontFace.getAttribute("cap-height") || "0"
518
+ );
519
+ }
520
+ this.targetXHeight = desiredXHeight;
521
+ this.SCALE = this.targetXHeight / this.metrics.xHeight;
522
+ const glyphElements = doc.querySelectorAll("glyph");
523
+ const basicCharRegex = /^[\w]$/;
524
+ const commonGlyphNames = /* @__PURE__ */ new Set([
525
+ "space",
526
+ "period",
527
+ "comma",
528
+ "hyphen",
529
+ "exclam",
530
+ "question",
531
+ "slash",
532
+ "less",
533
+ "greater",
534
+ "parenleft",
535
+ "parenright",
536
+ "braceleft",
537
+ "braceright",
538
+ "bracketleft",
539
+ "bracketright"
540
+ ]);
541
+ glyphElements.forEach((glyph) => {
542
+ const character = glyph.getAttribute("glyph-name");
543
+ const unicode = glyph.getAttribute("unicode");
544
+ if (!character || !unicode || !basicCharRegex.test(character) && !commonGlyphNames.has(character) && character.length > 1)
545
+ return;
546
+ const horizAdvX = parseInt(glyph.getAttribute("horiz-adv-x") || "0");
547
+ let pathData = glyph.getAttribute("d");
548
+ if (pathData) {
549
+ pathData = pathData.replace(
550
+ /[-+]?\d*\.?\d+/g,
551
+ (match) => (parseFloat(match) * this.SCALE).toString()
552
+ );
553
+ }
554
+ const glyphData = {
555
+ name: character,
556
+ unicode,
557
+ horizAdvX: horizAdvX * this.SCALE,
558
+ d: pathData || void 0
559
+ };
560
+ this.glyphs.set(unicode, glyphData);
561
+ });
562
+ if (!this.glyphs.has(" ")) {
563
+ this.glyphs.set(" ", {
564
+ name: "space",
565
+ unicode: " ",
566
+ horizAdvX: this.metrics.unitsPerEm * this.SCALE * 0.25,
567
+ // typical space width
568
+ d: void 0
569
+ });
570
+ }
571
+ }
572
+ toJSON() {
573
+ return {
574
+ metrics: this.metrics,
575
+ glyphs: Array.from(this.glyphs.entries()).reduce((obj, [key, value]) => {
576
+ obj[key] = value;
577
+ return obj;
578
+ }, {})
579
+ };
580
+ }
581
+ getPoints(text, options) {
582
+ const {
583
+ factor,
584
+ align = "left",
585
+ center = "alphabetic",
586
+ letterSpacing = 0
587
+ } = options;
588
+ const points = [];
589
+ const svgns = "http://www.w3.org/2000/svg";
590
+ const svg = document.createElementNS(svgns, "svg");
591
+ const path = document.createElementNS(svgns, "path");
592
+ svg.appendChild(path);
593
+ let totalWidth = 0;
594
+ const chars = text.split("");
595
+ chars.forEach((char, i) => {
596
+ const glyph = this.glyphs.get(char);
597
+ if (glyph) {
598
+ totalWidth += glyph.horizAdvX;
599
+ if (i < chars.length - 1)
600
+ totalWidth += letterSpacing;
601
+ }
602
+ });
603
+ let currentX = 0;
604
+ if (align === "center") {
605
+ currentX = -totalWidth / 2;
606
+ } else if (align === "right") {
607
+ currentX = -totalWidth;
608
+ }
609
+ console.log(this.SCALE);
610
+ let yOffset = 0;
611
+ switch (center) {
612
+ case "middle":
613
+ yOffset = -((this.metrics.ascent - this.metrics.descent) / 2) * this.SCALE * 0.5;
614
+ break;
615
+ case "top":
616
+ yOffset = this.metrics.ascent * this.SCALE;
617
+ break;
618
+ case "alphabetic":
619
+ default:
620
+ yOffset = 0;
621
+ break;
622
+ }
623
+ for (const char of chars) {
624
+ const glyph = this.glyphs.get(char);
625
+ if (!glyph)
626
+ continue;
627
+ if (!glyph.d) {
628
+ currentX += glyph.horizAdvX + letterSpacing;
629
+ continue;
630
+ }
631
+ path.setAttribute("d", glyph.d);
632
+ const subpaths = glyph.d.split(/(?=[Mm])/);
633
+ const glyphContours = [];
634
+ for (const subpath of subpaths) {
635
+ if (!subpath.trim())
636
+ continue;
637
+ path.setAttribute("d", subpath);
638
+ const pathLength = path.getTotalLength();
639
+ const numPoints = Math.max(10, Math.floor(pathLength * factor));
640
+ const contourPoints = [];
641
+ const step = pathLength / (numPoints - 1);
642
+ for (let i = 0; i < numPoints; i++) {
643
+ const point = path.getPointAtLength(i * step);
644
+ contourPoints.push({
645
+ x: point.x + currentX,
646
+ y: -(point.y + yOffset)
647
+ // Apply vertical offset
648
+ });
649
+ }
650
+ if (subpath.toLowerCase().includes("z")) {
651
+ contourPoints.push({ ...contourPoints[0] });
652
+ }
653
+ glyphContours.push(contourPoints);
654
+ }
655
+ glyphContours.sort((a, b) => b.length - a.length);
656
+ for (let i = 1; i < glyphContours.length; i++) {
657
+ glyphContours[i].reverse();
658
+ }
659
+ points.push(glyphContours);
660
+ currentX += glyph.horizAdvX + letterSpacing;
661
+ }
662
+ svg.remove();
663
+ return points;
664
+ }
665
+ flatten(points, displacement) {
666
+ return points.flatMap(
667
+ (glyph) => glyph.flatMap(
668
+ (contour, contourIndex) => contour.map((point) => {
669
+ if (displacement) {
670
+ return displacement({
671
+ point,
672
+ position: point,
673
+ groupIndex: contourIndex,
674
+ letterSpacing: 0
675
+ });
676
+ }
677
+ return point;
678
+ })
679
+ )
680
+ );
681
+ }
682
+ draw(points, displacement) {
683
+ for (const glyph of points) {
684
+ this.context.beginShape();
685
+ glyph.forEach((contour, contourIndex) => {
686
+ if (contourIndex !== 0) {
687
+ this.context.beginContour();
688
+ }
689
+ contour.forEach((point) => {
690
+ if (displacement) {
691
+ const { x, y } = displacement({
692
+ point,
693
+ position: point,
694
+ groupIndex: contourIndex,
695
+ letterSpacing: 0
696
+ // You can add letter spacing logic here
697
+ });
698
+ this.context.vertex(x, y);
699
+ } else {
700
+ this.context.vertex(point.x, point.y);
701
+ }
702
+ });
703
+ if (contourIndex !== 0) {
704
+ this.context.endContour();
705
+ }
706
+ });
707
+ this.context.endShape(true);
708
+ }
709
+ }
710
+ };
711
+ var SVGfont_default = SVGfont;
712
+
713
+ // src/plugins/State.tsx
714
+ var State = class {
715
+ constructor() {
716
+ this.store = /* @__PURE__ */ new Map();
717
+ }
718
+ set(key, value, callback) {
719
+ this.store.set(key, value);
720
+ callback?.(key, value);
721
+ }
722
+ get(key, callback) {
723
+ const value = this.store.get(key);
724
+ callback?.(key, value);
725
+ return value;
726
+ }
727
+ has(key) {
728
+ return this.store.has(key);
729
+ }
730
+ delete(key, callback) {
731
+ this.store.delete(key);
732
+ callback?.(key);
733
+ }
734
+ log() {
735
+ return this.store;
736
+ }
737
+ };
738
+ var State_default = State;
739
+
740
+ // src/plugins/Vector.tsx
741
+ var Vector = class {
742
+ /**
743
+ * Creates a new Vector
744
+ * @param x - X-coordinate (default: 0)
745
+ * @param y - Y-coordinate (default: 0)
746
+ */
747
+ constructor(x = 0, y = 0) {
748
+ this.x = x;
749
+ this.y = y;
750
+ }
751
+ /**
752
+ * Adds another vector to this vector
753
+ * @param v - Vector to add
754
+ * @returns This vector after addition
755
+ */
756
+ add(v) {
757
+ this.x += v.x;
758
+ this.y += v.y;
759
+ return this;
760
+ }
761
+ /**
762
+ * Subtracts another vector from this vector
763
+ * @param v - Vector to subtract
764
+ * @returns This vector after subtraction
765
+ */
766
+ sub(v) {
767
+ this.x -= v.x;
768
+ this.y -= v.y;
769
+ return this;
770
+ }
771
+ /**
772
+ * Multiplies this vector by a scalar
773
+ * @param n - Scalar to multiply by
774
+ * @returns This vector after multiplication
775
+ */
776
+ mult(n) {
777
+ this.x *= n;
778
+ this.y *= n;
779
+ return this;
780
+ }
781
+ /**
782
+ * Divides this vector by a scalar
783
+ * @param n - Scalar to divide by
784
+ * @returns This vector after division
785
+ */
786
+ div(n) {
787
+ this.x /= n;
788
+ this.y /= n;
789
+ return this;
790
+ }
791
+ // to do : project, perp, slerp
792
+ /**
793
+ * Rotates this vector by an angle
794
+ * @param angle - Angle in radians
795
+ * @returns This vector after rotation
796
+ */
797
+ rotate(angle) {
798
+ const cos = Math.cos(angle);
799
+ const sin = Math.sin(angle);
800
+ const x = this.x * cos - this.y * sin;
801
+ const y = this.x * sin + this.y * cos;
802
+ this.x = x;
803
+ this.y = y;
804
+ return this;
805
+ }
806
+ /**
807
+ * Calculates the magnitude (length) of this vector
808
+ * @returns The magnitude of the vector
809
+ */
810
+ mag() {
811
+ return Math.sqrt(this.x * this.x + this.y * this.y);
812
+ }
813
+ /**
814
+ * Alias for mag() - calculates the length of this vector
815
+ * @returns The length of the vector
816
+ */
817
+ length() {
818
+ return this.mag();
819
+ }
820
+ /**
821
+ * Calculates the dot product of this vector with another vector
822
+ * @param v - The other vector
823
+ * @returns The dot product
824
+ */
825
+ dot(v) {
826
+ return this.x * v.x + this.y * v.y;
827
+ }
828
+ /**
829
+ * Calculates the distance between this vector and another vector
830
+ * @param v - The other vector
831
+ * @returns The distance between the vectors
832
+ */
833
+ dist(v) {
834
+ return Math.hypot(this.x - v.x, this.y - v.y);
835
+ }
836
+ /**
837
+ * Calculates the angle of this vector
838
+ * @returns The angle in radians
839
+ */
840
+ angle() {
841
+ return Math.atan2(-this.x, -this.y) + Math.PI;
842
+ }
843
+ /**
844
+ * Creates a copy of this vector
845
+ * @returns A new Vector with the same coordinates
846
+ */
847
+ copy() {
848
+ return new Vector(this.x, this.y);
849
+ }
850
+ /**
851
+ * Normalizes this vector (sets its magnitude to 1)
852
+ * @returns This vector after normalization
853
+ */
854
+ normalize() {
855
+ const m = this.mag();
856
+ return m !== 0 ? this.div(m) : this;
857
+ }
858
+ /**
859
+ * Sets the coordinates of this vector
860
+ * @param x - New X-coordinate
861
+ * @param y - New Y-coordinate
862
+ * @returns This vector after setting coordinates
863
+ */
864
+ set(x, y) {
865
+ this.x = x;
866
+ this.y = y;
867
+ return this;
868
+ }
869
+ /**
870
+ * Creates a new vector at a specified angle and distance from a center point
871
+ * @param center - The center point vector
872
+ * @param a - The angle in radians
873
+ * @param r - The radius (distance from center)
874
+ * @returns A new Vector at the calculated position
875
+ */
876
+ static fromAngle(center, a, r) {
877
+ const x = Math.cos(a) * r + center.x;
878
+ const y = Math.sin(a) * r + center.y;
879
+ return new Vector(x, y);
880
+ }
881
+ };
882
+ var Vector_default = Vector;
883
+
884
+ // src/plugins/Time.tsx
885
+ var Time = class {
886
+ constructor(ctx) {
887
+ this.timelines = /* @__PURE__ */ new Map();
888
+ this.currentTimeline = "default";
889
+ this.DEFAULT_DURATION = 8 * 60;
890
+ this.staggers = [];
891
+ this.context = ctx;
892
+ this.timelines.set("default", {
893
+ progress: 0,
894
+ duration: this.DEFAULT_DURATION
895
+ });
896
+ }
897
+ timeline(key) {
898
+ if (!this.timelines.has(key)) {
899
+ this.timelines.set(key, { progress: 0, duration: this.DEFAULT_DURATION });
900
+ }
901
+ this.currentTimeline = key;
902
+ return this;
903
+ }
904
+ use(progress) {
905
+ const timeline = this.timelines.get(this.currentTimeline);
906
+ if (timeline.duration <= 0) {
907
+ timeline.progress = 0;
908
+ return this;
909
+ }
910
+ timeline.progress = timeline.duration === 1 ? Math.min(progress, 1) : progress / timeline.duration % 1;
911
+ return this;
912
+ }
913
+ for(duration) {
914
+ const timeline = this.timelines.get(this.currentTimeline);
915
+ timeline.duration = duration;
916
+ return this;
917
+ }
918
+ stagger(num, offset = 0, callback) {
919
+ const timeline = this.timelines.get(this.currentTimeline);
920
+ const totalduration = this.context.remap(
921
+ timeline.progress,
922
+ 0,
923
+ 1,
924
+ 0,
925
+ 1 + offset
926
+ );
927
+ for (let i = 0; i < num; i++) {
928
+ const id = 1 - i / (num - 1);
929
+ const progress = this.context.constrain(
930
+ totalduration - id * offset,
931
+ 0,
932
+ 1
933
+ );
934
+ if (!callback) {
935
+ if (this.staggers[i]) {
936
+ this.staggers[i].progress = progress;
937
+ } else {
938
+ this.staggers[i] = { progress, id };
939
+ }
940
+ } else {
941
+ callback?.(progress, id, num);
942
+ }
943
+ }
944
+ return callback ? this : this.staggers;
945
+ }
946
+ between(from = 0, to = 1, callback) {
947
+ const timeline = this.timelines.get(this.currentTimeline);
948
+ const localProgress = this.context.remap(
949
+ timeline.progress,
950
+ Math.max(0, from),
951
+ Math.min(1, to),
952
+ 0,
953
+ 1
954
+ );
955
+ callback(localProgress);
956
+ return this;
957
+ }
958
+ progress() {
959
+ return this.timelines.get(this.currentTimeline)?.progress || 0;
960
+ }
961
+ };
962
+ var Time_default = Time;
963
+
964
+ // src/plugins/Text.tsx
965
+ var Text = class {
966
+ constructor(ctx) {
967
+ this.findTextSize = (text, dist, estimate, direction = "x") => {
968
+ let low = 1;
969
+ let high = estimate || this.context.__textSize * 2;
970
+ let lastValidSize = low;
971
+ for (let i = 0; i < 16; i++) {
972
+ const mid = Math.floor((low + high) / 2);
973
+ this.context.__textSize = mid;
974
+ const bounds = this.textBounds(text);
975
+ const size = direction === "x" ? bounds.width : bounds.height;
976
+ if (size === dist) {
977
+ return mid;
978
+ } else if (size < dist) {
979
+ lastValidSize = mid;
980
+ low = mid + 1;
981
+ } else {
982
+ high = mid - 1;
983
+ }
984
+ }
985
+ return lastValidSize;
986
+ };
987
+ this.getTextMetrics = (text) => {
988
+ const ctx = this.context;
989
+ const metrics = ctx.measureText(text);
990
+ return {
991
+ width: metrics.width,
992
+ height: metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent,
993
+ baseline: metrics.actualBoundingBoxAscent
994
+ };
995
+ };
996
+ this.splitTo = (text, kind, options = {}) => {
997
+ const ctx = this.context;
998
+ const {
999
+ maxWidth = 0,
1000
+ lineSpacing = 0,
1001
+ letterSpacing = 0,
1002
+ wordSpacing = 0
1003
+ } = options;
1004
+ ctx.computeFont();
1005
+ if (maxWidth < this.textBounds(" ").width * 2 && maxWidth !== 0) {
1006
+ return [];
1007
+ }
1008
+ const lines = text.split("\n");
1009
+ const lineHeights = lines.map((line) => this.getTextMetrics(line).height);
1010
+ const totalHeight = lineHeights.reduce((sum, height) => sum + height, 0) + (lines.length - 1) * (lineSpacing || 0);
1011
+ let y = ctx.textBaseline === "middle" ? -totalHeight / 2 : 0;
1012
+ return lines.flatMap((lineText, lineIndex) => {
1013
+ const words = lineText.split(" ");
1014
+ const currentLine = {
1015
+ text: "",
1016
+ width: 0,
1017
+ letters: []
1018
+ };
1019
+ const totalWidth = this.getTextMetrics(lineText).width;
1020
+ let startX = 0;
1021
+ switch (ctx.textAlign) {
1022
+ case "center":
1023
+ startX = -totalWidth / 2;
1024
+ break;
1025
+ case "right":
1026
+ startX = -totalWidth;
1027
+ break;
1028
+ }
1029
+ let x = startX;
1030
+ let letterIndex = 0;
1031
+ let wordIndex = 0;
1032
+ const lineLetters = words.flatMap((word) => {
1033
+ const letters = [];
1034
+ for (const char of word) {
1035
+ const charMetrics = this.getTextMetrics(char);
1036
+ x += charMetrics.width / 2;
1037
+ const letterData = {
1038
+ char,
1039
+ x,
1040
+ y: y + (ctx.textBaseline === "middle" ? lineHeights[lineIndex] / 2 : 0),
1041
+ metrics: charMetrics,
1042
+ ...kind === "all" && {
1043
+ letterIndex,
1044
+ wordIndex,
1045
+ lineIndex
1046
+ }
1047
+ };
1048
+ letters.push(letterData);
1049
+ currentLine.text += char;
1050
+ x += charMetrics.width / 2 + letterSpacing;
1051
+ letterIndex++;
1052
+ }
1053
+ if (wordIndex < words.length - 1) {
1054
+ const spaceMetrics = this.getTextMetrics(" ");
1055
+ x += spaceMetrics.width + wordSpacing;
1056
+ letters.push({
1057
+ char: " ",
1058
+ x,
1059
+ y,
1060
+ metrics: spaceMetrics,
1061
+ ...kind === "all" && {
1062
+ letterIndex,
1063
+ wordIndex,
1064
+ lineIndex
1065
+ }
1066
+ });
1067
+ currentLine.text += " ";
1068
+ letterIndex++;
1069
+ }
1070
+ wordIndex++;
1071
+ return letters;
1072
+ });
1073
+ const lineHeight = lineHeights[lineIndex];
1074
+ y += lineHeight + lineSpacing;
1075
+ return lineLetters;
1076
+ });
1077
+ };
1078
+ this.circularText = (text, radius = 100, fill = "fill", offset = 0, arc = Math.PI * 2) => {
1079
+ const totalAngle = Math.min(Math.max(arc, 0), Math.PI * 2);
1080
+ if (fill === "fill") {
1081
+ const chars = text.split("");
1082
+ const anglePerChar = totalAngle / (chars.length + 1);
1083
+ chars.forEach((char, i) => {
1084
+ const angle = anglePerChar * i + offset;
1085
+ const x = Math.cos(angle) * radius;
1086
+ const y = Math.sin(angle) * radius;
1087
+ this.context.push();
1088
+ this.context.textAlign = "center";
1089
+ this.context.translate(x, y);
1090
+ this.context.rotate(angle + Math.PI / 2);
1091
+ this.context.text(char, 0, 0);
1092
+ this.context.pop();
1093
+ });
1094
+ } else if (fill === "kerned") {
1095
+ let currentAngle = offset;
1096
+ text.split("").forEach((char) => {
1097
+ const charWidth = this.textBounds(char).width;
1098
+ currentAngle += charWidth / radius * 0.5;
1099
+ const angle = currentAngle;
1100
+ const x = Math.cos(angle) * radius;
1101
+ const y = Math.sin(angle) * radius;
1102
+ this.context.push();
1103
+ this.context.textAlign = "center";
1104
+ this.context.translate(x, y);
1105
+ this.context.rotate(angle + Math.PI / 2);
1106
+ this.context.text(char, 0, 0);
1107
+ this.context.pop();
1108
+ currentAngle += charWidth / radius * 0.5;
1109
+ });
1110
+ } else if (fill === "words") {
1111
+ const words = text.split(" ");
1112
+ const wordMetrics = words.map((word) => ({
1113
+ word,
1114
+ width: this.textBounds(word).width
1115
+ }));
1116
+ const spaceCount = words.length;
1117
+ const totalWordWidth = wordMetrics.reduce((sum, m) => sum + m.width, 0);
1118
+ const spaceAngle = (totalAngle - totalWordWidth / radius) / spaceCount;
1119
+ let currentAngle = offset;
1120
+ wordMetrics.forEach(({ word }, i) => {
1121
+ word.split("").forEach((char) => {
1122
+ const charWidth = this.textBounds(char).width;
1123
+ currentAngle += charWidth / radius * 0.5;
1124
+ const x = Math.cos(currentAngle) * radius;
1125
+ const y = Math.sin(currentAngle) * radius;
1126
+ this.context.push();
1127
+ this.context.textAlign = "center";
1128
+ this.context.translate(x, y);
1129
+ this.context.rotate(currentAngle + Math.PI / 2);
1130
+ this.context.text(char, 0, 0);
1131
+ this.context.pop();
1132
+ currentAngle += charWidth / radius * 0.5;
1133
+ });
1134
+ if (i < words.length - 1) {
1135
+ currentAngle += spaceAngle;
1136
+ }
1137
+ });
1138
+ }
1139
+ };
1140
+ this.textBounds = (text) => {
1141
+ if (this.context.font !== this.context.__computedTextFont) {
1142
+ this.context.font = this.context.__computedTextFont;
1143
+ }
1144
+ const metrics = this.context.measureText(text);
1145
+ return {
1146
+ x: metrics.actualBoundingBoxLeft * -1,
1147
+ y: metrics.actualBoundingBoxAscent * -1,
1148
+ width: metrics.width,
1149
+ height: metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent
1150
+ };
1151
+ };
1152
+ this.log = () => {
1153
+ console.log(this.context);
1154
+ };
1155
+ this.context = ctx;
1156
+ }
1157
+ };
1158
+ var Text_default = Text;
1159
+
1160
+ // src/plugins/Thing.tsx
1161
+ var Thing = class {
1162
+ constructor(ctx) {
1163
+ this.context = ctx;
1164
+ }
1165
+ log() {
1166
+ console.log(this.context);
1167
+ }
1168
+ };
1169
+ var Thing_default = Thing;
1170
+ // Annotate the CommonJS export names for ESM import in node:
1171
+ 0 && (module.exports = {
1172
+ Color,
1173
+ Easing,
1174
+ SVGfont,
1175
+ State,
1176
+ Text,
1177
+ Thing,
1178
+ Time,
1179
+ Vector
1180
+ });