morphico 0.4.0 → 0.5.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 (72) hide show
  1. package/README.md +62 -45
  2. package/dist/esm/all.js +50 -0
  3. package/dist/esm/clip.js +283 -0
  4. package/dist/esm/engine.js +25 -0
  5. package/dist/esm/face.js +153 -0
  6. package/dist/esm/geometry.js +459 -0
  7. package/dist/esm/icon.js +41 -0
  8. package/dist/esm/index.js +50 -0
  9. package/dist/esm/morph.js +107 -0
  10. package/dist/esm/roundness.js +37 -0
  11. package/dist/esm/sets/battery.js +87 -0
  12. package/dist/esm/sets/bell.js +101 -0
  13. package/dist/esm/sets/characters.js +43 -0
  14. package/dist/esm/sets/check-mark.js +74 -0
  15. package/dist/esm/sets/checkbox.js +91 -0
  16. package/dist/esm/sets/chevron.js +70 -0
  17. package/dist/esm/sets/condensed.js +137 -0
  18. package/dist/esm/sets/copy.js +87 -0
  19. package/dist/esm/sets/eye.js +105 -0
  20. package/dist/esm/sets/faces.js +482 -0
  21. package/dist/esm/sets/folder.js +112 -0
  22. package/dist/esm/sets/handwritten.js +136 -0
  23. package/dist/esm/sets/lock.js +75 -0
  24. package/dist/esm/sets/lucide.js +66 -0
  25. package/dist/esm/sets/media.js +109 -0
  26. package/dist/esm/sets/menu.js +84 -0
  27. package/dist/esm/sets/mic.js +102 -0
  28. package/dist/esm/sets/radio.js +84 -0
  29. package/dist/esm/sets/round.js +244 -0
  30. package/dist/esm/sets/shapes.js +455 -0
  31. package/dist/esm/sets/slim.js +148 -0
  32. package/dist/esm/sets/square.js +149 -0
  33. package/dist/esm/sets/theme.js +107 -0
  34. package/dist/esm/sets/toggle.js +73 -0
  35. package/dist/esm/sets/volume.js +89 -0
  36. package/dist/esm/sets/weather.js +691 -0
  37. package/dist/esm/sets/wifi.js +68 -0
  38. package/dist/esm/svg.js +31 -0
  39. package/dist/esm/text.js +88 -0
  40. package/dist/esm/types.js +3 -0
  41. package/dist/morphico.js +3764 -3750
  42. package/dist/types/{iconSets.d.ts → all.d.ts} +0 -2
  43. package/dist/types/clip.d.ts +4 -11
  44. package/dist/types/face.d.ts +2 -6
  45. package/dist/types/icon.d.ts +4 -9
  46. package/dist/types/index.d.ts +1 -3
  47. package/dist/types/roundness.d.ts +8 -0
  48. package/dist/types/sets/battery.d.ts +1 -1
  49. package/dist/types/sets/bell.d.ts +1 -1
  50. package/dist/types/sets/checkbox.d.ts +1 -1
  51. package/dist/types/sets/chevron.d.ts +1 -1
  52. package/dist/types/sets/condensed.d.ts +1 -1
  53. package/dist/types/sets/copy.d.ts +1 -1
  54. package/dist/types/sets/eye.d.ts +1 -1
  55. package/dist/types/sets/folder.d.ts +1 -1
  56. package/dist/types/sets/handwritten.d.ts +1 -1
  57. package/dist/types/sets/lock.d.ts +1 -1
  58. package/dist/types/sets/media.d.ts +1 -1
  59. package/dist/types/sets/menu.d.ts +1 -1
  60. package/dist/types/sets/mic.d.ts +1 -1
  61. package/dist/types/sets/radio.d.ts +1 -1
  62. package/dist/types/sets/round.d.ts +1 -1
  63. package/dist/types/sets/slim.d.ts +1 -1
  64. package/dist/types/sets/square.d.ts +1 -1
  65. package/dist/types/sets/theme.d.ts +1 -1
  66. package/dist/types/sets/toggle.d.ts +1 -1
  67. package/dist/types/sets/volume.d.ts +1 -1
  68. package/dist/types/sets/weather.d.ts +1 -1
  69. package/dist/types/sets/wifi.d.ts +1 -1
  70. package/dist/types/text.d.ts +8 -13
  71. package/package.json +95 -3
  72. package/dist/morphico.mjs +0 -4642
@@ -0,0 +1,136 @@
1
+ /*! morphico v0.5.0 | MIT License, Copyright (c) 2026 Raycho Ivanov (see LICENSE) */
2
+ // Digit set "handwritten": hand-drawn marker digits, traced from the reference images in icons/handwritten/
3
+ // (the set format is described in ../types.ts).
4
+ //
5
+ // Free-form strokes, so the geometry is traced rather than constructed (scratch tools, see CLAUDE.md):
6
+ // thin decorative side lines removed (only ink of a thick stroke kept), the stroke thinned to its centre line,
7
+ // followed through hand-placed waypoints in writing order, smoothed and fitted with cubic Bézier curves
8
+ // (Schneider's algorithm). 7.375 image px = 1 grid unit, each digit centred on 12,12; stroke 1.04 (typical width along
9
+ // the centre line — the marker stroke varies a little, so the pixel match is ~8–17 %).
10
+ // - every digit is one stroke of 3–7 free cubic curves; round ends and corners
11
+ // - the 4 is drawn out and back over its crossbar end and right stem (one continuous stroke)
12
+ import { cubic } from '../geometry.js';
13
+ import { withTextCharacters } from './characters.js';
14
+ // '-' '/' '.' ',' ':' are added by withTextCharacters (characters.ts)
15
+ export const digitsHandwritten = withTextCharacters({
16
+ segmentCount: 16,
17
+ strokeWidth: 1.04,
18
+ lineCap: 'round',
19
+ lineJoin: 'round',
20
+ strokes: {
21
+ 0: [
22
+ cubic(12.61, 4.07, 10.61, 3.51, 9.37, 5.81, 8.74, 7.36),
23
+ cubic(8.74, 7.36, 7.57, 10.27, 6.96, 17.17, 9.79, 19.34),
24
+ cubic(9.79, 19.34, 12.78, 21.64, 15.47, 18.25, 16, 15.5),
25
+ cubic(16, 15.5, 16.6, 12.38, 16.29, 8.49, 14.37, 5.83),
26
+ cubic(14.37, 5.83, 13.87, 5.13, 13.21, 4.66, 12.61, 4.07),
27
+ ],
28
+ 1: [
29
+ cubic(10.24, 7.53, 11.22, 6.54, 12.21, 5.24, 13.49, 4.68),
30
+ cubic(13.49, 4.68, 13.7, 4.79, 13.86, 4.5, 13.9, 4.89),
31
+ cubic(13.9, 4.89, 14.02, 6, 13.9, 7.27, 13.9, 8.42),
32
+ cubic(13.9, 8.42, 13.9, 12.28, 13.9, 16.14, 13.9, 20),
33
+ ],
34
+ 2: [
35
+ cubic(9.02, 7.66, 10.04, 4, 14.93, 1.86, 15.22, 7.15),
36
+ cubic(15.22, 7.15, 15.49, 11.89, 7.96, 15.48, 8.47, 19.86),
37
+ cubic(8.47, 19.86, 9.61, 20.58, 13.97, 20.14, 15.53, 20.14),
38
+ ],
39
+ 3: [
40
+ cubic(9.76, 5.15, 13.23, 2.8, 16.42, 4.68, 14.53, 7.59),
41
+ cubic(14.53, 7.59, 13.4, 9.34, 12.35, 9.39, 10.58, 9.9),
42
+ cubic(10.58, 9.9, 11.34, 9.75, 12.22, 9.41, 13.02, 9.59),
43
+ cubic(13.02, 9.59, 18.13, 10.7, 16.81, 19.23, 11.83, 19.98),
44
+ cubic(11.83, 19.98, 9.89, 20.28, 8.7, 19.11, 7.73, 17.63),
45
+ ],
46
+ 4: [
47
+ cubic(8.27, 4.14, 8.27, 6.1, 7.62, 12.38, 8.38, 13.74),
48
+ cubic(8.41, 13.76, 10.61, 14.38, 13.29, 14.03, 15.59, 14.03),
49
+ cubic(15.59, 14.03, 14.82, 14.03, 14.06, 14.03, 13.29, 14.03),
50
+ cubic(13.29, 14.03, 13.29, 10.73, 13.29, 7.44, 13.29, 4.14),
51
+ cubic(13.29, 4.14, 13.29, 9.33, 13.29, 14.53, 13.29, 19.73),
52
+ ],
53
+ 5: [
54
+ cubic(14.24, 3.86, 12.8, 3.86, 11.11, 3.74, 9.76, 4.14),
55
+ cubic(9.76, 4.14, 9.7, 4.15, 9.31, 4.09, 9.26, 4.23),
56
+ cubic(9.26, 4.23, 9.09, 4.74, 9.28, 8.83, 9.36, 9.58),
57
+ cubic(9.36, 9.58, 9.41, 10.07, 9.44, 9.66, 9.63, 9.83),
58
+ cubic(9.63, 9.83, 10.32, 9.54, 10.97, 9.13, 11.74, 9.04),
59
+ cubic(11.74, 9.04, 16.84, 8.46, 16.99, 16.29, 14.13, 18.87),
60
+ cubic(14.13, 18.87, 11.84, 20.92, 8.19, 19.68, 8, 16.47),
61
+ ],
62
+ 6: [
63
+ cubic(13.08, 3.93, 9.07, 6.47, 7.2, 12.92, 8.79, 17.24),
64
+ cubic(8.79, 17.24, 9.18, 18.28, 9.4, 19.11, 10.46, 19.67),
65
+ cubic(10.46, 19.67, 13.85, 21.46, 16.41, 16.37, 15.4, 13.64),
66
+ cubic(15.4, 13.64, 13.48, 8.42, 9.15, 16.35, 8.94, 16.42),
67
+ cubic(8.94, 16.42, 7.71, 16.83, 8.27, 12.36, 8.47, 11.8),
68
+ ],
69
+ 7: [
70
+ cubic(8.81, 3.86, 10.36, 3.86, 14.03, 3.47, 15.19, 4.08),
71
+ cubic(15.19, 4.14, 14.75, 5.71, 13.41, 7.25, 12.7, 8.8),
72
+ cubic(12.7, 8.8, 11.16, 12.19, 10.31, 16.13, 10.31, 19.86),
73
+ ],
74
+ 8: [
75
+ cubic(14.51, 5.56, 15.17, 3.17, 9.21, 3.46, 8.86, 6.51),
76
+ cubic(8.86, 6.51, 8.64, 8.41, 13.99, 11.13, 14.93, 13.6),
77
+ cubic(14.93, 13.6, 15.9, 16.15, 14.74, 20.7, 11.25, 19.94),
78
+ cubic(11.25, 19.94, 8.97, 19.44, 8.63, 17.22, 9.02, 15.26),
79
+ cubic(9.02, 15.26, 9.77, 11.45, 12.38, 8.68, 14.48, 5.59),
80
+ ],
81
+ 9: [
82
+ cubic(14.98, 6.98, 13.12, 0.31, 6.71, 5.82, 8.47, 10.23),
83
+ cubic(8.47, 10.23, 9.92, 13.9, 13.15, 10.88, 14.01, 8.61),
84
+ cubic(14.01, 8.61, 14.16, 8.23, 14.66, 6.7, 15.3, 7.44),
85
+ cubic(15.3, 7.44, 16.93, 9.31, 14.47, 18.24, 11.59, 19.86),
86
+ ],
87
+ },
88
+ alternativeStrokes: {},
89
+ // optimizer fits (tools/check.ts) for the pairs the user rated bad / midd / not sure
90
+ pairLayouts: {
91
+ 0: {
92
+ 1: { startSegment: 5, reversed: true },
93
+ 2: { startSegment: 5, reversed: true },
94
+ 3: { startSegment: 4, reversed: true },
95
+ 4: { breakpoints: [0, 0.7708, 1, 1.2083, 1.3958, 1.5208, 1.5208, 1.5208, 2, 3, 3.1458, 3.1458, 3.1458, 3.5833, 3.8542, 4, 5] },
96
+ 5: { startSegment: 3, reversed: true },
97
+ 7: { startSegment: 4, reversed: true },
98
+ 9: { startSegment: 5, reversed: true },
99
+ },
100
+ 1: {
101
+ 0: { breakpoints: [0, 0, 0, 0, 0.3542, 1, 2, 3, 3.1667, 3.3958, 3.6042, 3.8125, 3.9167, 3.9167, 3.9167, 3.9167, 4] },
102
+ },
103
+ 2: {
104
+ 6: { breakpoints: [0, 0.4583, 1, 1.5833, 1.5833, 1.5833, 1.5833, 1.5833, 1.5833, 1.5833, 1.5833, 1.5833, 1.5833, 1.6458, 1.7083, 2, 3] },
105
+ 0: { breakpoints: [0, 0, 0, 0, 0.25, 0.5417, 0.8958, 1, 1.2083, 1.3125, 1.4167, 1.5417, 1.75, 1.9792, 1.9792, 2, 3] },
106
+ },
107
+ 3: {
108
+ 8: { breakpoints: [0, 0.5833, 0.5833, 0.5833, 0.5833, 1, 2, 3, 3.1458, 3.3958, 3.5208, 3.6667, 3.8333, 4, 4.2917, 5, 5] },
109
+ 0: { breakpoints: [0, 0, 0, 0, 0.2917, 1, 2, 3, 3.4167, 3.5208, 3.7083, 3.8542, 4, 4.4167, 5, 5, 5] },
110
+ },
111
+ 4: {
112
+ 8: { breakpoints: [0, 0.5833, 0.7708, 1, 1.4583, 2, 2.1458, 3, 3.2917, 3.3125, 3.875, 3.8958, 3.8958, 3.8958, 3.8958, 4, 5] },
113
+ 0: { breakpoints: [0, 0.0833, 0.3542, 0.5208, 0.7708, 1, 1.1042, 1.25, 1.5, 1.7292, 1.9583, 2, 3, 3.3958, 3.5625, 4, 5] },
114
+ },
115
+ 5: {
116
+ 0: { breakpoints: [0, 1, 2, 3, 4, 5, 5.3958, 5.5208, 5.6875, 5.875, 6, 6.2917, 6.5625, 6.8958, 7, 7, 7] },
117
+ },
118
+ 6: {
119
+ 2: { breakpoints: [0, 0.0625, 0.0625, 1, 2, 3, 3.6458, 3.6458, 3.6458, 3.6458, 3.6458, 3.6458, 3.6458, 3.7292, 3.875, 4, 5] },
120
+ },
121
+ 7: {
122
+ 8: { breakpoints: [0, 0.7292, 0.7292, 0.7292, 0.7292, 0.7292, 1, 2, 2.1458, 2.1458, 2.1458, 2.1458, 2.4583, 2.75, 2.75, 2.75, 3] },
123
+ 0: { breakpoints: [0, 0, 0, 0.2083, 0.5417, 1, 1.6667, 2, 2.2708, 2.5208, 2.7708, 2.8542, 2.8542, 2.8542, 2.8542, 2.8542, 3] },
124
+ },
125
+ 8: {
126
+ 7: { breakpoints: [0, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 1, 1.5625, 1.5625, 1.6042, 1.625, 2, 3, 3.6042, 4, 5] },
127
+ 4: { breakpoints: [0, 0, 0, 0, 0, 0.25, 1, 1.625, 1.6875, 2, 2.0417, 2.0417, 3, 4, 4.3125, 4.5208, 5], reversed: true },
128
+ 3: { breakpoints: [0, 0.125, 0.125, 0.125, 0.125, 1, 1.5208, 1.5625, 1.7708, 2, 2.1667, 2.3958, 2.6458, 3, 3.0417, 4, 5] },
129
+ },
130
+ 9: {
131
+ 0: { breakpoints: [0, 0.9792, 0.9792, 1, 2, 2.7917, 2.7917, 3, 3.3125, 3.4375, 3.5833, 3.7083, 3.8333, 4, 4, 4, 4] },
132
+ },
133
+ },
134
+ spins: {},
135
+ textLayout: { cellWidth: 9, spacing: 4, baselineY: 20.2, colonDotsY: [8, 16] },
136
+ });
@@ -0,0 +1,75 @@
1
+ /*! morphico v0.5.0 | code: MIT License, Copyright (c) 2026 Raycho Ivanov (see LICENSE)
2
+ * Icons based on Lucide (https://lucide.dev), some of them from Feather (https://feathericons.com):
3
+ *
4
+ * ISC License
5
+ *
6
+ * Copyright (c) 2026 Lucide Icons and Contributors
7
+ *
8
+ * Permission to use, copy, modify, and/or distribute this software for any
9
+ * purpose with or without fee is hereby granted, provided that the above
10
+ * copyright notice and this permission notice appear in all copies.
11
+ *
12
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
+ *
20
+ * The MIT License (MIT) (for the Lucide icons derived from Feather, listed in LICENSE-lucide)
21
+ *
22
+ * Copyright (c) 2013-present Cole Bemis
23
+ *
24
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
25
+ * of this software and associated documentation files (the "Software"), to deal
26
+ * in the Software without restriction, including without limitation the rights
27
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28
+ * copies of the Software, and to permit persons to whom the Software is
29
+ * furnished to do so, subject to the following conditions:
30
+ *
31
+ * The above copyright notice and this permission notice shall be included in all
32
+ * copies or substantial portions of the Software.
33
+ *
34
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40
+ * SOFTWARE.
41
+ */
42
+ import { LUCIDE_STROKE_WIDTH, lucideArc, lucideLine } from './lucide.js';
43
+ const BODY = [
44
+ lucideLine(5, 11, 19, 11),
45
+ lucideArc(19, 13, 2, -90, 0),
46
+ lucideLine(21, 13, 21, 20),
47
+ lucideArc(19, 20, 2, 0, 90),
48
+ lucideLine(19, 22, 5, 22),
49
+ lucideArc(5, 20, 2, 90, 180),
50
+ lucideLine(3, 20, 3, 13),
51
+ lucideArc(5, 13, 2, 180, 270),
52
+ ];
53
+ const LEFT_LEG = lucideLine(7, 11, 7, 7);
54
+ const body = { strokes: { locked: BODY, unlocked: BODY }, alternativeStrokes: {}, pairLayouts: {} };
55
+ // left leg 2 segments, arch 6, right leg 4 — unlocked has no right leg: 4 zero-length segments at the arch's end
56
+ const shackle = {
57
+ strokes: {
58
+ locked: [LEFT_LEG, lucideArc(12, 7, 5, 180, 360), lucideLine(17, 7, 17, 11)],
59
+ unlocked: [LEFT_LEG, lucideArc(12, 6.995, 5, 179.9424, 348.5218)],
60
+ },
61
+ alternativeStrokes: {},
62
+ pairLayouts: {
63
+ locked: { unlocked: { breakpoints: [0, 0.5, 1, 1.1667, 1.3333, 1.5, 1.6667, 1.8333, 2, 2.25, 2.5, 2.75, 3] } },
64
+ unlocked: { locked: { breakpoints: [0, 0.5, 1, 1.1667, 1.3333, 1.5, 1.6667, 1.8333, 2, 2, 2, 2, 2] } },
65
+ },
66
+ };
67
+ export const iconsLock = {
68
+ segmentCount: 12,
69
+ strokeWidth: LUCIDE_STROKE_WIDTH,
70
+ lineCap: 'round',
71
+ lineJoin: 'round',
72
+ ...body,
73
+ morePieces: [shackle],
74
+ spins: {},
75
+ };
@@ -0,0 +1,66 @@
1
+ /*! morphico v0.5.0 | code: MIT License, Copyright (c) 2026 Raycho Ivanov (see LICENSE)
2
+ * Icons based on Lucide (https://lucide.dev), some of them from Feather (https://feathericons.com):
3
+ *
4
+ * ISC License
5
+ *
6
+ * Copyright (c) 2026 Lucide Icons and Contributors
7
+ *
8
+ * Permission to use, copy, modify, and/or distribute this software for any
9
+ * purpose with or without fee is hereby granted, provided that the above
10
+ * copyright notice and this permission notice appear in all copies.
11
+ *
12
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
+ *
20
+ * The MIT License (MIT) (for the Lucide icons derived from Feather, listed in LICENSE-lucide)
21
+ *
22
+ * Copyright (c) 2013-present Cole Bemis
23
+ *
24
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
25
+ * of this software and associated documentation files (the "Software"), to deal
26
+ * in the Software without restriction, including without limitation the rights
27
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28
+ * copies of the Software, and to permit persons to whom the Software is
29
+ * furnished to do so, subject to the following conditions:
30
+ *
31
+ * The above copyright notice and this permission notice shall be included in all
32
+ * copies or substantial portions of the Software.
33
+ *
34
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40
+ * SOFTWARE.
41
+ */
42
+ // Shared by every Lucide-based icon set: Lucide draws in y 3…21 (18 units) with a 2-unit line; our icons get the
43
+ // same room as the digits (16 units, y 4…20; user's choice 2026-10-26), so every icon is scaled around the centre
44
+ // 12,12 — line width included, so each icon looks exactly like Lucide's, only with more margin in its box.
45
+ import { arc, cubic, line } from '../geometry.js';
46
+ export const LUCIDE_SCALE = 16 / 18;
47
+ /** Lucide's 2-unit line, scaled with the icon. */
48
+ export const LUCIDE_STROKE_WIDTH = 2 * LUCIDE_SCALE;
49
+ /** A point (or corner) of a Lucide icon moved into our icon box. */
50
+ export const fromLucide = ([x, y]) => [12 + (x - 12) * LUCIDE_SCALE, 12 + (y - 12) * LUCIDE_SCALE];
51
+ /** A length of a Lucide icon (e.g. a corner radius) in our icon box. */
52
+ export const lucideLength = (length) => length * LUCIDE_SCALE;
53
+ // Strokes written in Lucide coordinates (for the icon sets added from 2026-10-28 on).
54
+ /** A straight line between two Lucide points. */
55
+ export const lucideLine = (fromX, fromY, toX, toY) => line(...fromLucide([fromX, fromY]), ...fromLucide([toX, toY]));
56
+ /** A circular arc in Lucide coordinates (angles in degrees, `endAngle > startAngle` = clockwise on screen). */
57
+ export const lucideArc = (centerX, centerY, radius, startAngle, endAngle) => arc(...fromLucide([centerX, centerY]), lucideLength(radius), startAngle, endAngle);
58
+ /** A cubic Bézier curve in Lucide coordinates. */
59
+ export const lucideCubic = (...coordinates) => {
60
+ const [startX, startY, control1X, control1Y, control2X, control2Y, endX, endY] = coordinates;
61
+ return cubic(...fromLucide([startX, startY]), ...fromLucide([control1X, control1Y]), ...fromLucide([control2X, control2Y]), ...fromLucide([endX, endY]));
62
+ };
63
+ /** A missing piece at rest: a single point, never drawn (see toPathData). */
64
+ export const lucidePoint = (x, y) => [lucideLine(x, y, x, y)];
65
+ /** The diagonal slash of Lucide's "-off" icons, from the top left. */
66
+ export const LUCIDE_SLASH = [lucideLine(2, 2, 22, 22)];
@@ -0,0 +1,109 @@
1
+ /*! morphico v0.5.0 | code: MIT License, Copyright (c) 2026 Raycho Ivanov (see LICENSE)
2
+ * Icons based on Lucide (https://lucide.dev), some of them from Feather (https://feathericons.com):
3
+ *
4
+ * ISC License
5
+ *
6
+ * Copyright (c) 2026 Lucide Icons and Contributors
7
+ *
8
+ * Permission to use, copy, modify, and/or distribute this software for any
9
+ * purpose with or without fee is hereby granted, provided that the above
10
+ * copyright notice and this permission notice appear in all copies.
11
+ *
12
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
+ *
20
+ * The MIT License (MIT) (for the Lucide icons derived from Feather, listed in LICENSE-lucide)
21
+ *
22
+ * Copyright (c) 2013-present Cole Bemis
23
+ *
24
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
25
+ * of this software and associated documentation files (the "Software"), to deal
26
+ * in the Software without restriction, including without limitation the rights
27
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28
+ * copies of the Software, and to permit persons to whom the Software is
29
+ * furnished to do so, subject to the following conditions:
30
+ *
31
+ * The above copyright notice and this permission notice shall be included in all
32
+ * copies or substantial portions of the Software.
33
+ *
34
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40
+ * SOFTWARE.
41
+ */
42
+ // Icon set "media": play, pause, stop — Lucide 1.61.0 icons (ISC, see LICENSE-lucide).
43
+ // First imported with `node tools/import-lucide.ts play pause square`, then rewritten as rounded polygons so the
44
+ // corners can be made sharper (`withRoundness`); with Lucide's radii (play 2, pause 1, stop 2) the shapes match
45
+ // the import within 0.005 units (Lucide's own rounding).
46
+ // Two pieces: pause has two bars; play and stop draw their single shape in both pieces (the second copy lies
47
+ // exactly on the first, so it doesn't show — during a morph to pause the two copies separate into the bars).
48
+ // Every shape runs clockwise from its top left, like Lucide's. Coordinates below are Lucide's; fromLucide /
49
+ // lucideLength scale them into our icon box (more margin, like the digits — see lucide.ts).
50
+ import { reversedStroke, roundedPolygon } from '../geometry.js';
51
+ import { fromLucide, LUCIDE_STROKE_WIDTH, lucideLength } from './lucide.js';
52
+ /** Lucide's play: a triangle (left side x 5, tip at the right) with rounded corners, starting with the top left arc. */
53
+ const PLAY_CORNERS = [[5, 1.5174], [22.9706, 11.9998], [5, 22.4827]].map(fromLucide);
54
+ const RIGHT_BAR_CORNERS = [[14, 3], [19, 3], [19, 21], [14, 21]].map(fromLucide);
55
+ const LEFT_BAR_CORNERS = [[5, 3], [10, 3], [10, 21], [5, 21]].map(fromLucide);
56
+ const SQUARE_CORNERS = [[3, 3], [21, 3], [21, 21], [3, 21]].map(fromLucide);
57
+ const LUCIDE_RADII = { play: lucideLength(2), pause: lucideLength(1), stop: lucideLength(2) };
58
+ // play ↔ stop (both pieces, the user's sketch): corner to corner, edge to edge, and the tip opens up and down
59
+ // into the square's right side. Play (top left corner, top, tip, bottom, bottom left corner, left):
60
+ // 2 + 1 + 4 + 1 + 2 + 2 segments. Stop is laid out from its top edge and then started at its top left corner,
61
+ // so its segments line up: top left 2, top 1, top right 1, right side 2, bottom right 1, bottom 1, bottom left 2, left 2.
62
+ const PLAY_FOR_STOP = { breakpoints: [0, 0.5, 1, 2, 2.25, 2.5, 2.75, 3, 4, 4.5, 5, 5.5, 6] };
63
+ const STOP_FOR_PLAY = { breakpoints: [0, 1, 2, 2.5, 3, 4, 5, 5.5, 6, 6.5, 7, 7.5, 8], startSegment: 10 };
64
+ const buildMediaIcons = (radii) => {
65
+ const play = roundedPolygon(PLAY_CORNERS, radii.play, true);
66
+ const stop = roundedPolygon(SQUARE_CORNERS, radii.stop);
67
+ return {
68
+ segmentCount: 12,
69
+ strokeWidth: LUCIDE_STROKE_WIDTH,
70
+ lineCap: 'round',
71
+ lineJoin: 'round',
72
+ strokes: { play, pause: roundedPolygon(RIGHT_BAR_CORNERS, radii.pause), stop },
73
+ alternativeStrokes: {},
74
+ // play ↔ stop: the user's sketch (above); play ↔ pause, pause ↔ stop: tools/check.ts
75
+ pairLayouts: {
76
+ play: { pause: { breakpoints: [0, 0.7292, 1, 1.6458, 2, 2.25, 3, 3.3125, 3.3125, 4, 4.2708, 5, 6] }, stop: PLAY_FOR_STOP },
77
+ pause: { play: { startSegment: 10 }, stop: { startSegment: 11 } },
78
+ stop: { pause: { breakpoints: [0, 0.8333, 1, 2, 2.2917, 2.7083, 3, 4, 4.1667, 5, 6, 7, 8] }, play: STOP_FOR_PLAY },
79
+ },
80
+ morePieces: [
81
+ {
82
+ strokes: { play, pause: roundedPolygon(LEFT_BAR_CORNERS, radii.pause), stop },
83
+ // only the triangle's left side, traced down and back up like the left bar's loop (top left corner out
84
+ // and back, left edge down, bottom left corner out and back, left edge up): the left bar inflates from
85
+ // that edge, while the first piece (the whole triangle) opens into the right bar
86
+ alternativeStrokes: {
87
+ 'play-left-side': [
88
+ play[0], reversedStroke(play[0]), reversedStroke(play[5]), reversedStroke(play[4]), play[4], play[5],
89
+ ],
90
+ },
91
+ pairLayouts: {
92
+ play: {
93
+ pause: { tracing: 'play-left-side', breakpoints: [0, 0.7292, 1, 1.7292, 2, 3, 3.7292, 4, 4.7292, 5, 5.7708, 6, 6] },
94
+ stop: PLAY_FOR_STOP,
95
+ },
96
+ pause: {
97
+ stop: { breakpoints: [0, 1, 2, 2.0208, 2.9792, 3, 4, 5, 6, 6.3333, 6.6667, 7, 8] },
98
+ },
99
+ stop: { pause: { breakpoints: [0, 0.1667, 1, 2, 3, 4, 4.8333, 5, 6, 6.2917, 6.7083, 7, 8] }, play: STOP_FOR_PLAY },
100
+ },
101
+ },
102
+ ],
103
+ spins: {},
104
+ withRoundness: (roundness) => buildMediaIcons({
105
+ play: LUCIDE_RADII.play * roundness, pause: LUCIDE_RADII.pause * roundness, stop: LUCIDE_RADII.stop * roundness,
106
+ }),
107
+ };
108
+ };
109
+ export const iconsMedia = buildMediaIcons(LUCIDE_RADII);
@@ -0,0 +1,84 @@
1
+ /*! morphico v0.5.0 | code: MIT License, Copyright (c) 2026 Raycho Ivanov (see LICENSE)
2
+ * Icons based on Lucide (https://lucide.dev), some of them from Feather (https://feathericons.com):
3
+ *
4
+ * ISC License
5
+ *
6
+ * Copyright (c) 2026 Lucide Icons and Contributors
7
+ *
8
+ * Permission to use, copy, modify, and/or distribute this software for any
9
+ * purpose with or without fee is hereby granted, provided that the above
10
+ * copyright notice and this permission notice appear in all copies.
11
+ *
12
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
+ *
20
+ * The MIT License (MIT) (for the Lucide icons derived from Feather, listed in LICENSE-lucide)
21
+ *
22
+ * Copyright (c) 2013-present Cole Bemis
23
+ *
24
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
25
+ * of this software and associated documentation files (the "Software"), to deal
26
+ * in the Software without restriction, including without limitation the rights
27
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28
+ * copies of the Software, and to permit persons to whom the Software is
29
+ * furnished to do so, subject to the following conditions:
30
+ *
31
+ * The above copyright notice and this permission notice shall be included in all
32
+ * copies or substantial portions of the Software.
33
+ *
34
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40
+ * SOFTWARE.
41
+ */
42
+ // Icon set "menu": menu (☰), close (✕), back (←) — Lucide 1.61.0 icons "menu", "x", "arrow-left" (ISC, see
43
+ // LICENSE-lucide), imported with `node tools/import-lucide.ts menu x arrow-left`. Coordinates below are Lucide's;
44
+ // fromLucide scales them into our icon box (more margin, like the digits — see lucide.ts).
45
+ // Three pieces, each one straight line, lined up so the lines of every state correspond:
46
+ // piece 1: top line | "\" of the ✕ | upper half of the arrowhead
47
+ // piece 2: middle line | hidden on the ✕'s centre | the arrow's shaft
48
+ // piece 3: bottom line | "/" of the ✕ | lower half of the arrowhead
49
+ // Lucide draws the arrowhead as one bent line; two lines meeting with round ends look the same.
50
+ // Every line runs from left to right, so lines move without turning over.
51
+ import { line } from '../geometry.js';
52
+ import { fromLucide, LUCIDE_STROKE_WIDTH } from './lucide.js';
53
+ /** A straight line between two Lucide points, moved into our icon box. */
54
+ const lucideLine = (from, to) => [line(...fromLucide(from), ...fromLucide(to))];
55
+ const piece = (strokes, extras = { alternativeStrokes: {}, pairLayouts: {} }) => ({ strokes, ...extras });
56
+ // close ↔ back: the ✕ drawn with its lines swapped ("/" in piece 1, "\" in piece 3), so each line only shortens
57
+ // and tilts into its half of the arrowhead instead of turning over
58
+ const BACKSLASH = lucideLine([6, 6], [18, 18]);
59
+ const SLASH = lucideLine([6, 18], [18, 6]);
60
+ export const iconsMenu = {
61
+ segmentCount: 1,
62
+ strokeWidth: LUCIDE_STROKE_WIDTH,
63
+ lineCap: 'round',
64
+ lineJoin: 'round',
65
+ ...piece({
66
+ menu: lucideLine([4, 5], [20, 5]),
67
+ close: BACKSLASH,
68
+ back: lucideLine([5, 12], [12, 5]),
69
+ }, { alternativeStrokes: { 'close-swapped': SLASH }, pairLayouts: { close: { back: { tracing: 'close-swapped', breakpoints: [0, 1] } } } }),
70
+ morePieces: [
71
+ piece({
72
+ menu: lucideLine([4, 12], [20, 12]),
73
+ // a tiny line on the centre of the ✕'s "\": hidden under it
74
+ close: lucideLine([11.5, 11.5], [12.5, 12.5]),
75
+ back: lucideLine([5, 12], [19, 12]),
76
+ }),
77
+ piece({
78
+ menu: lucideLine([4, 19], [20, 19]),
79
+ close: SLASH,
80
+ back: lucideLine([5, 12], [12, 19]),
81
+ }, { alternativeStrokes: { 'close-swapped': BACKSLASH }, pairLayouts: { close: { back: { tracing: 'close-swapped', breakpoints: [0, 1] } } } }),
82
+ ],
83
+ spins: {},
84
+ };
@@ -0,0 +1,102 @@
1
+ /*! morphico v0.5.0 | code: MIT License, Copyright (c) 2026 Raycho Ivanov (see LICENSE)
2
+ * Icons based on Lucide (https://lucide.dev), some of them from Feather (https://feathericons.com):
3
+ *
4
+ * ISC License
5
+ *
6
+ * Copyright (c) 2026 Lucide Icons and Contributors
7
+ *
8
+ * Permission to use, copy, modify, and/or distribute this software for any
9
+ * purpose with or without fee is hereby granted, provided that the above
10
+ * copyright notice and this permission notice appear in all copies.
11
+ *
12
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
+ *
20
+ * The MIT License (MIT) (for the Lucide icons derived from Feather, listed in LICENSE-lucide)
21
+ *
22
+ * Copyright (c) 2013-present Cole Bemis
23
+ *
24
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
25
+ * of this software and associated documentation files (the "Software"), to deal
26
+ * in the Software without restriction, including without limitation the rights
27
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28
+ * copies of the Software, and to permit persons to whom the Software is
29
+ * furnished to do so, subject to the following conditions:
30
+ *
31
+ * The above copyright notice and this permission notice shall be included in all
32
+ * copies or substantial portions of the Software.
33
+ *
34
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40
+ * SOFTWARE.
41
+ */
42
+ import { LUCIDE_SLASH, LUCIDE_STROKE_WIDTH, lucideArc, lucideLine, lucidePoint } from './lucide.js';
43
+ const STAND = [lucideLine(12, 19, 12, 22)];
44
+ /** Where the mic's capsule is split: the middles of mic-off's gaps (right side y 11.85, left side y 6.3). */
45
+ const RIGHT_SPLIT_Y = 11.85;
46
+ const LEFT_SPLIT_Y = 6.3;
47
+ /** Where the holder is split: the middle of its gap (10.17° … 45°). */
48
+ const HOLDER_GAP_MIDDLE = (10.1709 + 44.9971) / 2;
49
+ const stand = { strokes: { on: STAND, off: STAND }, alternativeStrokes: {}, pairLayouts: {} };
50
+ // line, arc, line — 3 + 6 + 3 segments; off has no last line: 3 zero-length segments at its end
51
+ const SPLIT_LAYOUT = { breakpoints: [0, 0.3333, 0.6667, 1, 1.1667, 1.3333, 1.5, 1.6667, 1.8333, 2, 2.3333, 2.6667, 3] };
52
+ const CUT_LAYOUT = { breakpoints: [0, 0.3333, 0.6667, 1, 1.1667, 1.3333, 1.5, 1.6667, 1.8333, 2, 2, 2, 2] };
53
+ const capsuleTop = {
54
+ strokes: {
55
+ // up the right side, over the top (counterclockwise), down the left side to the split
56
+ on: [lucideLine(15, RIGHT_SPLIT_Y, 15, 5), lucideArc(12, 5, 3, 0, -180), lucideLine(9, 5, 9, LEFT_SPLIT_Y)],
57
+ off: [lucideLine(15, 9.34, 15, 5), lucideArc(12.0001, 5.0181, 3, -0.3452, -153.2975)],
58
+ },
59
+ alternativeStrokes: {},
60
+ pairLayouts: { on: { off: SPLIT_LAYOUT }, off: { on: CUT_LAYOUT } },
61
+ };
62
+ const capsuleBottom = {
63
+ strokes: {
64
+ // down the left side, around the bottom (counterclockwise), up the right side to the split
65
+ on: [lucideLine(9, LEFT_SPLIT_Y, 9, 12), lucideArc(12, 12, 3, 180, 0), lucideLine(15, 12, 15, RIGHT_SPLIT_Y)],
66
+ off: [lucideLine(9, 9, 9, 12), lucideArc(12, 11.9974, 3, 179.9496, 45.0356)],
67
+ },
68
+ alternativeStrokes: {},
69
+ pairLayouts: { on: { off: SPLIT_LAYOUT }, off: { on: CUT_LAYOUT } },
70
+ };
71
+ // arc then line, 6 + 6 segments in both states (the arc only shortens)
72
+ const HOLDER_LAYOUT = { breakpoints: [0, 0.1667, 0.3333, 0.5, 0.6667, 0.8333, 1, 1.1667, 1.3333, 1.5, 1.6667, 1.8333, 2] };
73
+ const holderLeft = {
74
+ strokes: {
75
+ on: [lucideArc(12, 12, 7, HOLDER_GAP_MIDDLE, 180), lucideLine(5, 12, 5, 10)],
76
+ off: [lucideArc(12, 12.0005, 7, 44.9971, 180.0041), lucideLine(5, 12, 5, 10)],
77
+ },
78
+ alternativeStrokes: {},
79
+ pairLayouts: { on: { off: HOLDER_LAYOUT }, off: { on: HOLDER_LAYOUT } },
80
+ };
81
+ const holderRight = {
82
+ strokes: {
83
+ on: [lucideArc(12, 12, 7, HOLDER_GAP_MIDDLE, 0), lucideLine(19, 12, 19, 10)],
84
+ off: [lucideArc(12, 11.9939, 7, 10.1709, 0.0499), lucideLine(19, 12, 19, 10)],
85
+ },
86
+ alternativeStrokes: {},
87
+ pairLayouts: { on: { off: HOLDER_LAYOUT }, off: { on: HOLDER_LAYOUT } },
88
+ };
89
+ const slash = {
90
+ strokes: { on: lucidePoint(2, 2), off: LUCIDE_SLASH },
91
+ alternativeStrokes: {},
92
+ pairLayouts: { on: { off: { draw: true } } },
93
+ };
94
+ export const iconsMic = {
95
+ segmentCount: 12,
96
+ strokeWidth: LUCIDE_STROKE_WIDTH,
97
+ lineCap: 'round',
98
+ lineJoin: 'round',
99
+ ...stand,
100
+ morePieces: [capsuleTop, capsuleBottom, holderLeft, holderRight, slash],
101
+ spins: {},
102
+ };
@@ -0,0 +1,84 @@
1
+ /*! morphico v0.5.0 | code: MIT License, Copyright (c) 2026 Raycho Ivanov (see LICENSE)
2
+ * Icons based on Lucide (https://lucide.dev), some of them from Feather (https://feathericons.com):
3
+ *
4
+ * ISC License
5
+ *
6
+ * Copyright (c) 2026 Lucide Icons and Contributors
7
+ *
8
+ * Permission to use, copy, modify, and/or distribute this software for any
9
+ * purpose with or without fee is hereby granted, provided that the above
10
+ * copyright notice and this permission notice appear in all copies.
11
+ *
12
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
+ *
20
+ * The MIT License (MIT) (for the Lucide icons derived from Feather, listed in LICENSE-lucide)
21
+ *
22
+ * Copyright (c) 2013-present Cole Bemis
23
+ *
24
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
25
+ * of this software and associated documentation files (the "Software"), to deal
26
+ * in the Software without restriction, including without limitation the rights
27
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28
+ * copies of the Software, and to permit persons to whom the Software is
29
+ * furnished to do so, subject to the following conditions:
30
+ *
31
+ * The above copyright notice and this permission notice shall be included in all
32
+ * copies or substantial portions of the Software.
33
+ *
34
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40
+ * SOFTWARE.
41
+ */
42
+ // Icon set "radio": unchecked, checked, checked-big — Lucide 1.61.0 icons "circle", "circle-check",
43
+ // "circle-check-big" (ISC, see LICENSE-lucide). Coordinates below are Lucide's; fromLucide / lucideLength scale them
44
+ // into our icon box (more margin, like the digits — see lucide.ts). Works like "checkbox", with a circle for the box.
45
+ // Two pieces:
46
+ // piece 1, the circle (radius 10 around 12,12): the same full circle in unchecked and checked (it doesn't move);
47
+ // checked-big opens a gap at the top right (from 17,3.335 clockwise round to 21.801,10 is the open arc) where
48
+ // the big check leaves the circle — both ends of the gap pull back to the gap's edges.
49
+ // piece 2, the check (check-mark.ts, shared with "checkbox"): drawn in like a pen from unchecked.
50
+ import { arc } from '../geometry.js';
51
+ import { checkMarkPiece } from './check-mark.js';
52
+ import { fromLucide, LUCIDE_STROKE_WIDTH, lucideLength } from './lucide.js';
53
+ const [CENTER_X, CENTER_Y] = fromLucide([12, 12]);
54
+ const RADIUS = lucideLength(10);
55
+ /** The open arc of circle-check-big runs clockwise from 21.801,10 to 17,3.335 (the gap is the rest). */
56
+ const OPEN_START_ANGLE = (Math.atan2(10 - 12, 21.801 - 12) * 180) / Math.PI;
57
+ const OPEN_END_ANGLE = (Math.atan2(3.335 - 12, 17 - 12) * 180) / Math.PI + 360;
58
+ /** The share of the full circle that stays in checked-big. */
59
+ const OPEN_SHARE = (OPEN_END_ANGLE - OPEN_START_ANGLE) / 360;
60
+ /** The full circle starts where the open arc starts, so the gap is its last part. */
61
+ const CIRCLE = [arc(CENTER_X, CENTER_Y, RADIUS, OPEN_START_ANGLE, OPEN_START_ANGLE + 360)];
62
+ const OPEN_CIRCLE = [arc(CENTER_X, CENTER_Y, RADIUS, OPEN_START_ANGLE, OPEN_END_ANGLE)];
63
+ // circle, unchecked/checked ↔ checked-big: the open part 10 segments, the gap 2 (one per half), started at the gap's
64
+ // second half — so it lines up with the open arc, which gets a zero-length segment at each end of the gap where
65
+ // the vanishing halves pull back to.
66
+ const openPart = Array.from({ length: 11 }, (_, index) => +((index / 10) * OPEN_SHARE).toFixed(4));
67
+ const CIRCLE_FOR_OPEN_CIRCLE = { breakpoints: [...openPart, +((1 + OPEN_SHARE) / 2).toFixed(4), 1], startSegment: 11 };
68
+ const OPEN_CIRCLE_FOR_CIRCLE = { breakpoints: [0, ...Array.from({ length: 11 }, (_, index) => index / 10), 1] };
69
+ export const iconsRadio = {
70
+ segmentCount: 12,
71
+ strokeWidth: LUCIDE_STROKE_WIDTH,
72
+ lineCap: 'round',
73
+ lineJoin: 'round',
74
+ strokes: { unchecked: CIRCLE, checked: CIRCLE, 'checked-big': OPEN_CIRCLE },
75
+ alternativeStrokes: {},
76
+ pairLayouts: {
77
+ unchecked: { 'checked-big': CIRCLE_FOR_OPEN_CIRCLE },
78
+ checked: { 'checked-big': CIRCLE_FOR_OPEN_CIRCLE },
79
+ 'checked-big': { unchecked: OPEN_CIRCLE_FOR_CIRCLE, checked: OPEN_CIRCLE_FOR_CIRCLE },
80
+ },
81
+ // the check rests (hidden) on the circle's left edge
82
+ morePieces: [checkMarkPiece([2, 12])],
83
+ spins: {},
84
+ };