@rehover/icons 0.1.0-beta.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 +7 -0
- package/dist/cli/index.js +1839 -0
- package/dist/index.js +1778 -0
- package/package.json +66 -0
|
@@ -0,0 +1,1839 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// cli/index.ts
|
|
5
|
+
var import_node_fs = require("node:fs");
|
|
6
|
+
var import_node_path = require("node:path");
|
|
7
|
+
var import_node_readline = require("node:readline");
|
|
8
|
+
|
|
9
|
+
// icons/badge-check/paths.ts
|
|
10
|
+
var badgeCheckPaths = {
|
|
11
|
+
badge: {
|
|
12
|
+
type: "path",
|
|
13
|
+
d: "M12 3l2.1 1.2 2.4-.3 1.2 2.1 2.1 1.2-.3 2.4L21 12l-1.2 2.1.3 2.4-2.1 1.2-1.2 2.1-2.4-.3L12 21l-2.1-1.2-2.4.3-1.2-2.1-2.1-1.2.3-2.4L3 12l1.2-2.1-.3-2.4 2.1-1.2 1.2-2.1 2.4.3L12 3"
|
|
14
|
+
},
|
|
15
|
+
check: {
|
|
16
|
+
type: "path",
|
|
17
|
+
d: "M9 12.5l2 2 4-4"
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// icons/badge-check/animation.spec.ts
|
|
22
|
+
var badgeCheckSpec = {
|
|
23
|
+
elements: {
|
|
24
|
+
badge: { id: "badge-check-badge", description: "Award badge" },
|
|
25
|
+
check: { id: "badge-check-check", description: "Verification checkmark" }
|
|
26
|
+
},
|
|
27
|
+
sequences: {
|
|
28
|
+
trigger: [
|
|
29
|
+
{
|
|
30
|
+
element: "badge",
|
|
31
|
+
property: "scale",
|
|
32
|
+
values: [1, 1.08, 0.98, 1],
|
|
33
|
+
duration: 0.5,
|
|
34
|
+
ease: "easeInOut"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
element: "check",
|
|
38
|
+
property: "pathLength",
|
|
39
|
+
values: [0, 1],
|
|
40
|
+
duration: 0.4,
|
|
41
|
+
ease: "easeInOut"
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
defaultTrigger: "hover"
|
|
46
|
+
};
|
|
47
|
+
var animation_spec_default = badgeCheckSpec;
|
|
48
|
+
|
|
49
|
+
// icons/badge-check/metadata.ts
|
|
50
|
+
var metadata = {
|
|
51
|
+
name: "BadgeCheck",
|
|
52
|
+
slug: "badge-check",
|
|
53
|
+
category: "Status",
|
|
54
|
+
tags: ["badge", "check", "verified", "verification", "approved", "success"],
|
|
55
|
+
featured: false,
|
|
56
|
+
description: "An award badge with a verification checkmark.",
|
|
57
|
+
animationDescription: "The badge gently pops while the checkmark draws itself into place."
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// icons/ball/paths.ts
|
|
61
|
+
var ballPaths = {
|
|
62
|
+
// A ball resting just above the ground line; bottom sits at y=15.
|
|
63
|
+
ball: { type: "circle", cx: 12, cy: 9, r: 6 },
|
|
64
|
+
ground: { type: "line", x1: 4, y1: 19, x2: 20, y2: 19 }
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// icons/ball/animation.spec.ts
|
|
68
|
+
var ballSpec = {
|
|
69
|
+
elements: {
|
|
70
|
+
ball: { id: "ball", description: "The ball that squashes and stretches." },
|
|
71
|
+
ground: { id: "ground", description: "The static ground line." }
|
|
72
|
+
},
|
|
73
|
+
sequences: {
|
|
74
|
+
// Non-uniform scale around the ball's base (12,15): it widens + flattens on
|
|
75
|
+
// impact, then stretches tall before settling. No perspective involved.
|
|
76
|
+
trigger: [
|
|
77
|
+
{
|
|
78
|
+
element: "ball",
|
|
79
|
+
property: "scaleY",
|
|
80
|
+
values: [1, 0.78, 1.08, 1],
|
|
81
|
+
duration: 0.55,
|
|
82
|
+
ease: "easeOut",
|
|
83
|
+
origin: { x: 12, y: 15 }
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
element: "ball",
|
|
87
|
+
property: "scaleX",
|
|
88
|
+
values: [1, 1.22, 0.96, 1],
|
|
89
|
+
duration: 0.55,
|
|
90
|
+
ease: "easeOut",
|
|
91
|
+
origin: { x: 12, y: 15 }
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
defaultTrigger: "hover"
|
|
96
|
+
};
|
|
97
|
+
var animation_spec_default2 = ballSpec;
|
|
98
|
+
|
|
99
|
+
// icons/ball/metadata.ts
|
|
100
|
+
var metadata2 = {
|
|
101
|
+
name: "Ball",
|
|
102
|
+
slug: "ball",
|
|
103
|
+
category: "Objects",
|
|
104
|
+
tags: ["ball", "bounce", "squash", "stretch", "scale", "physics"],
|
|
105
|
+
featured: false,
|
|
106
|
+
description: "A ball that squashes and stretches above a ground line.",
|
|
107
|
+
animationDescription: "The ball flattens and widens on impact, then stretches tall before settling."
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// icons/bell/paths.ts
|
|
111
|
+
var bellPaths = {
|
|
112
|
+
body: { type: "path", d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" },
|
|
113
|
+
clapper: { type: "path", d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" }
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// icons/bell/animation.spec.ts
|
|
117
|
+
var bellSpec = {
|
|
118
|
+
elements: {
|
|
119
|
+
body: { id: "bell-body", description: "Bell body" },
|
|
120
|
+
clapper: { id: "bell-clapper", description: "Bell clapper" }
|
|
121
|
+
},
|
|
122
|
+
sequences: {
|
|
123
|
+
// The whole bell rocks around its top mount, so body AND clapper rotate
|
|
124
|
+
// together around the same pivot (matching the React <g> wrapper).
|
|
125
|
+
trigger: [
|
|
126
|
+
{
|
|
127
|
+
element: "body",
|
|
128
|
+
property: "rotate",
|
|
129
|
+
values: [0, -12, 12, -9, 9, -5, 0],
|
|
130
|
+
duration: 0.7,
|
|
131
|
+
ease: "easeInOut",
|
|
132
|
+
origin: { x: 12, y: 3 }
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
element: "clapper",
|
|
136
|
+
property: "rotate",
|
|
137
|
+
values: [0, -12, 12, -9, 9, -5, 0],
|
|
138
|
+
duration: 0.7,
|
|
139
|
+
ease: "easeInOut",
|
|
140
|
+
origin: { x: 12, y: 3 }
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
},
|
|
144
|
+
defaultTrigger: "hover"
|
|
145
|
+
};
|
|
146
|
+
var animation_spec_default3 = bellSpec;
|
|
147
|
+
|
|
148
|
+
// icons/bell/metadata.ts
|
|
149
|
+
var metadata3 = {
|
|
150
|
+
name: "Bell",
|
|
151
|
+
slug: "bell",
|
|
152
|
+
category: "Notification",
|
|
153
|
+
tags: ["bell", "notification", "alert", "ring", "alarm"],
|
|
154
|
+
featured: true,
|
|
155
|
+
description: "A notification bell with a swinging ring.",
|
|
156
|
+
animationDescription: "The bell rocks back and forth around its top mount, settling to center."
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// icons/card-flip/paths.ts
|
|
160
|
+
var cardFlipPaths = {
|
|
161
|
+
// Rounded card outline spanning x:4–20, y:6–18.
|
|
162
|
+
card: {
|
|
163
|
+
type: "path",
|
|
164
|
+
d: "M6 6h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2z"
|
|
165
|
+
},
|
|
166
|
+
// A line of "content" across the middle of the card.
|
|
167
|
+
line: { type: "path", d: "M8 12h8" }
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// icons/card-flip/animation.spec.ts
|
|
171
|
+
var cardFlipSpec = {
|
|
172
|
+
elements: {
|
|
173
|
+
card: { id: "card", description: "The card body." },
|
|
174
|
+
line: { id: "card-line", description: "The content line on the card." }
|
|
175
|
+
},
|
|
176
|
+
sequences: {
|
|
177
|
+
// Both elements rotate identically around the card's center, so the whole
|
|
178
|
+
// card flips to its back as one rigid surface (3D rotation around the X
|
|
179
|
+
// axis). A half-turn (0→180) so hoverHold can hold the flipped face.
|
|
180
|
+
trigger: [
|
|
181
|
+
{
|
|
182
|
+
element: "card",
|
|
183
|
+
property: "rotateX",
|
|
184
|
+
values: [0, 180],
|
|
185
|
+
duration: 0.7,
|
|
186
|
+
ease: "easeInOut",
|
|
187
|
+
origin: { x: 12, y: 12 }
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
element: "line",
|
|
191
|
+
property: "rotateX",
|
|
192
|
+
values: [0, 180],
|
|
193
|
+
duration: 0.7,
|
|
194
|
+
ease: "easeInOut",
|
|
195
|
+
origin: { x: 12, y: 12 }
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
defaultTrigger: "hoverHold",
|
|
200
|
+
perspective: 500
|
|
201
|
+
};
|
|
202
|
+
var animation_spec_default4 = cardFlipSpec;
|
|
203
|
+
|
|
204
|
+
// icons/card-flip/metadata.ts
|
|
205
|
+
var metadata4 = {
|
|
206
|
+
name: "CardFlip",
|
|
207
|
+
slug: "card-flip",
|
|
208
|
+
category: "Objects",
|
|
209
|
+
tags: ["card", "flip", "rotate", "3d", "reveal"],
|
|
210
|
+
featured: false,
|
|
211
|
+
description: "A card that flips forward to reveal its other side.",
|
|
212
|
+
animationDescription: "The card flips to its back around its horizontal axis (a 3D half-turn), holding the flipped face while hovered."
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// icons/check/paths.ts
|
|
216
|
+
var checkPaths = {
|
|
217
|
+
checkmark: { type: "path", d: "M5 12.5l4.5 4.5L19 7" }
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// icons/check/animation.spec.ts
|
|
221
|
+
var checkSpec = {
|
|
222
|
+
elements: {
|
|
223
|
+
checkmark: { id: "check-path", description: "Checkmark path" }
|
|
224
|
+
},
|
|
225
|
+
sequences: {
|
|
226
|
+
trigger: [
|
|
227
|
+
{
|
|
228
|
+
element: "checkmark",
|
|
229
|
+
property: "pathLength",
|
|
230
|
+
from: 0,
|
|
231
|
+
to: 1,
|
|
232
|
+
duration: 0.4,
|
|
233
|
+
ease: "easeOut"
|
|
234
|
+
}
|
|
235
|
+
],
|
|
236
|
+
mount: [
|
|
237
|
+
{
|
|
238
|
+
element: "checkmark",
|
|
239
|
+
property: "pathLength",
|
|
240
|
+
from: 0,
|
|
241
|
+
to: 1,
|
|
242
|
+
duration: 0.4,
|
|
243
|
+
ease: "easeOut"
|
|
244
|
+
}
|
|
245
|
+
]
|
|
246
|
+
},
|
|
247
|
+
defaultTrigger: "hover"
|
|
248
|
+
};
|
|
249
|
+
var animation_spec_default5 = checkSpec;
|
|
250
|
+
|
|
251
|
+
// icons/check/metadata.ts
|
|
252
|
+
var metadata5 = {
|
|
253
|
+
name: "Check",
|
|
254
|
+
slug: "check",
|
|
255
|
+
category: "Status",
|
|
256
|
+
tags: ["check", "done", "success", "confirm", "tick", "complete"],
|
|
257
|
+
featured: true,
|
|
258
|
+
description: "A checkmark for success and confirmation states.",
|
|
259
|
+
animationDescription: "The stroke draws itself from the bottom-left up to the top-right."
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// icons/clock/paths.ts
|
|
263
|
+
var clockPaths = {
|
|
264
|
+
face: { type: "circle", cx: 12, cy: 12, r: 9 },
|
|
265
|
+
minuteHand: { type: "line", x1: 12, y1: 12, x2: 12, y2: 7.5 },
|
|
266
|
+
hourHand: { type: "line", x1: 12, y1: 12, x2: 14.5, y2: 12 }
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// icons/clock/animation.spec.ts
|
|
270
|
+
var clockSpec = {
|
|
271
|
+
elements: {
|
|
272
|
+
hourHand: { id: "clock-hour-hand", description: "Hour hand" },
|
|
273
|
+
minuteHand: { id: "clock-minute-hand", description: "Minute hand" }
|
|
274
|
+
},
|
|
275
|
+
sequences: {
|
|
276
|
+
trigger: [
|
|
277
|
+
{
|
|
278
|
+
element: "minuteHand",
|
|
279
|
+
property: "rotate",
|
|
280
|
+
values: [0, 360],
|
|
281
|
+
duration: 0.8,
|
|
282
|
+
ease: "easeInOut",
|
|
283
|
+
origin: { x: 12, y: 12 }
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
element: "hourHand",
|
|
287
|
+
property: "rotate",
|
|
288
|
+
values: [0, 30],
|
|
289
|
+
duration: 0.8,
|
|
290
|
+
ease: "easeInOut",
|
|
291
|
+
origin: { x: 12, y: 12 }
|
|
292
|
+
}
|
|
293
|
+
]
|
|
294
|
+
},
|
|
295
|
+
defaultTrigger: "hoverHold"
|
|
296
|
+
};
|
|
297
|
+
var animation_spec_default6 = clockSpec;
|
|
298
|
+
|
|
299
|
+
// icons/clock/metadata.ts
|
|
300
|
+
var metadata6 = {
|
|
301
|
+
name: "Clock",
|
|
302
|
+
slug: "clock",
|
|
303
|
+
category: "Time",
|
|
304
|
+
tags: ["time", "watch", "schedule", "clock", "hour", "alarm"],
|
|
305
|
+
featured: true,
|
|
306
|
+
description: "A clock face with sweeping hour and minute hands.",
|
|
307
|
+
animationDescription: "Both hands sweep a full rotation, the minute hand faster than the hour hand."
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
// icons/creating-file/paths.ts
|
|
311
|
+
var creatingFilePaths = {
|
|
312
|
+
file: {
|
|
313
|
+
type: "path",
|
|
314
|
+
d: "M8 3h6l5 5v13H8a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2Z"
|
|
315
|
+
},
|
|
316
|
+
fold: {
|
|
317
|
+
type: "path",
|
|
318
|
+
d: "M10 11l1-.8 1.2 1.4 1-.9 1.2 1"
|
|
319
|
+
},
|
|
320
|
+
lineTop: {
|
|
321
|
+
type: "path",
|
|
322
|
+
d: "M10 11l1-.7 1.1 1.2 1-.8 1.2.9H16"
|
|
323
|
+
},
|
|
324
|
+
lineMiddle: {
|
|
325
|
+
type: "path",
|
|
326
|
+
d: "M10 14l.9-.5 1.2 1.5 1-.9 1.1.6H15"
|
|
327
|
+
},
|
|
328
|
+
lineBottom: {
|
|
329
|
+
type: "path",
|
|
330
|
+
d: "M10 17l1-.6 1 1 1.1-.7 1.1.8H16"
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// icons/creating-file/animation.spec.ts
|
|
335
|
+
var creatingFileSpec = {
|
|
336
|
+
elements: {
|
|
337
|
+
file: {
|
|
338
|
+
id: "creating-file-file",
|
|
339
|
+
description: "Document outline"
|
|
340
|
+
},
|
|
341
|
+
fold: {
|
|
342
|
+
id: "creating-file-fold",
|
|
343
|
+
description: "Folded page corner"
|
|
344
|
+
},
|
|
345
|
+
lineTop: {
|
|
346
|
+
id: "creating-file-lineTop",
|
|
347
|
+
description: "Top content line"
|
|
348
|
+
},
|
|
349
|
+
lineMiddle: {
|
|
350
|
+
id: "creating-file-lineMiddle",
|
|
351
|
+
description: "Middle content line"
|
|
352
|
+
},
|
|
353
|
+
lineBottom: {
|
|
354
|
+
id: "creating-file-lineBottom",
|
|
355
|
+
description: "Bottom content line"
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
sequences: {
|
|
359
|
+
trigger: [
|
|
360
|
+
{
|
|
361
|
+
element: "lineTop",
|
|
362
|
+
property: "opacity",
|
|
363
|
+
values: [0.3, 1, 0.3],
|
|
364
|
+
duration: 0.35,
|
|
365
|
+
ease: "easeInOut"
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
element: "lineMiddle",
|
|
369
|
+
property: "opacity",
|
|
370
|
+
values: [0.3, 1, 0.3],
|
|
371
|
+
duration: 0.35,
|
|
372
|
+
delay: 0.12,
|
|
373
|
+
ease: "easeInOut"
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
element: "lineBottom",
|
|
377
|
+
property: "opacity",
|
|
378
|
+
values: [0.3, 1, 0.3],
|
|
379
|
+
duration: 0.35,
|
|
380
|
+
delay: 0.24,
|
|
381
|
+
ease: "easeInOut"
|
|
382
|
+
}
|
|
383
|
+
]
|
|
384
|
+
},
|
|
385
|
+
defaultTrigger: "inView",
|
|
386
|
+
defaultLoop: true
|
|
387
|
+
};
|
|
388
|
+
var animation_spec_default7 = creatingFileSpec;
|
|
389
|
+
|
|
390
|
+
// icons/creating-file/metadata.ts
|
|
391
|
+
var metadata7 = {
|
|
392
|
+
name: "CreatingFile",
|
|
393
|
+
slug: "creating-file",
|
|
394
|
+
category: "AI",
|
|
395
|
+
tags: ["file", "document", "create", "writing", "generation", "ai", "text"],
|
|
396
|
+
featured: false,
|
|
397
|
+
description: "A document icon with animated content lines representing AI-generated text.",
|
|
398
|
+
animationDescription: "The document stays still while its text lines softly pulse one after another, suggesting content being generated."
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
// icons/door/paths.ts
|
|
402
|
+
var doorPaths = {
|
|
403
|
+
frame: {
|
|
404
|
+
type: "path",
|
|
405
|
+
d: "M5 21V3h14v18"
|
|
406
|
+
},
|
|
407
|
+
panel: {
|
|
408
|
+
type: "path",
|
|
409
|
+
d: "M7 4h10v16H7z"
|
|
410
|
+
},
|
|
411
|
+
knob: {
|
|
412
|
+
type: "circle",
|
|
413
|
+
cx: 14.5,
|
|
414
|
+
cy: 12,
|
|
415
|
+
r: 0.8
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// icons/door/animation.spec.ts
|
|
420
|
+
var doorSpec = {
|
|
421
|
+
elements: {
|
|
422
|
+
panel: {
|
|
423
|
+
id: "door-panel",
|
|
424
|
+
description: "The door swings open in 3D around its left hinge."
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
sequences: {
|
|
428
|
+
trigger: [
|
|
429
|
+
{
|
|
430
|
+
element: "panel",
|
|
431
|
+
property: "rotateY",
|
|
432
|
+
values: [0, -55, 0],
|
|
433
|
+
duration: 0.6,
|
|
434
|
+
ease: "easeInOut",
|
|
435
|
+
origin: { x: 7, y: 12 }
|
|
436
|
+
// left edge (hinge)
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
element: "panel",
|
|
440
|
+
property: "scaleX",
|
|
441
|
+
values: [1, 0.85, 1],
|
|
442
|
+
duration: 0.6,
|
|
443
|
+
ease: "easeInOut",
|
|
444
|
+
origin: { x: 7, y: 12 }
|
|
445
|
+
// foreshorten toward the hinge as it swings
|
|
446
|
+
}
|
|
447
|
+
]
|
|
448
|
+
},
|
|
449
|
+
defaultTrigger: "hoverHold",
|
|
450
|
+
perspective: 500
|
|
451
|
+
};
|
|
452
|
+
var animation_spec_default8 = doorSpec;
|
|
453
|
+
|
|
454
|
+
// icons/door/metadata.ts
|
|
455
|
+
var metadata8 = {
|
|
456
|
+
name: "Door",
|
|
457
|
+
slug: "door",
|
|
458
|
+
category: "Objects",
|
|
459
|
+
tags: ["door", "entrance", "home", "open", "exit", "room"],
|
|
460
|
+
featured: false,
|
|
461
|
+
description: "A simple door icon with a hinged panel and handle.",
|
|
462
|
+
animationDescription: "The door panel swings open from its hinge and returns to the closed position."
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
// icons/download/paths.ts
|
|
466
|
+
var downloadPaths = {
|
|
467
|
+
shaft: { type: "path", d: "M12 3v11" },
|
|
468
|
+
arrowhead: { type: "path", d: "m8 10 4 4 4-4" },
|
|
469
|
+
tray: { type: "path", d: "M4 19h16" }
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
// icons/download/animation.spec.ts
|
|
473
|
+
var downloadSpec = {
|
|
474
|
+
elements: {
|
|
475
|
+
shaft: { id: "download-shaft", description: "Download arrow shaft" },
|
|
476
|
+
arrowhead: { id: "download-arrowhead", description: "Download arrowhead" },
|
|
477
|
+
tray: { id: "download-tray", description: "Download tray" }
|
|
478
|
+
},
|
|
479
|
+
sequences: {
|
|
480
|
+
trigger: [
|
|
481
|
+
{
|
|
482
|
+
element: "shaft",
|
|
483
|
+
property: "translateY",
|
|
484
|
+
values: [0, 3, 0],
|
|
485
|
+
duration: 0.55,
|
|
486
|
+
ease: "easeInOut"
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
element: "arrowhead",
|
|
490
|
+
property: "translateY",
|
|
491
|
+
values: [0, 3, 0],
|
|
492
|
+
duration: 0.55,
|
|
493
|
+
ease: "easeInOut"
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
element: "tray",
|
|
497
|
+
property: "scaleX",
|
|
498
|
+
values: [1, 1.08, 1],
|
|
499
|
+
duration: 0.25,
|
|
500
|
+
ease: "easeInOut"
|
|
501
|
+
}
|
|
502
|
+
]
|
|
503
|
+
},
|
|
504
|
+
defaultTrigger: "hoverHold"
|
|
505
|
+
};
|
|
506
|
+
var animation_spec_default9 = downloadSpec;
|
|
507
|
+
|
|
508
|
+
// icons/download/metadata.ts
|
|
509
|
+
var metadata9 = {
|
|
510
|
+
name: "Download",
|
|
511
|
+
slug: "download",
|
|
512
|
+
category: "Actions",
|
|
513
|
+
tags: ["download", "save", "arrow", "file", "import"],
|
|
514
|
+
featured: false,
|
|
515
|
+
description: "A downward arrow landing into a download tray.",
|
|
516
|
+
animationDescription: "The arrow drops into the tray, which compresses slightly before both settle."
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
// icons/external-link/paths.ts
|
|
520
|
+
var externalLinkPaths = {
|
|
521
|
+
box: {
|
|
522
|
+
type: "path",
|
|
523
|
+
d: "M13 5H7a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-6"
|
|
524
|
+
},
|
|
525
|
+
arrow: {
|
|
526
|
+
type: "path",
|
|
527
|
+
d: "M10 14L20 4"
|
|
528
|
+
},
|
|
529
|
+
arrowHead: {
|
|
530
|
+
type: "path",
|
|
531
|
+
d: "M15 4h5v5"
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
// icons/external-link/animation.spec.ts
|
|
536
|
+
var externalLinkSpec = {
|
|
537
|
+
elements: {
|
|
538
|
+
box: {
|
|
539
|
+
id: "external-link-box",
|
|
540
|
+
description: "The square representing the destination window."
|
|
541
|
+
},
|
|
542
|
+
arrow: {
|
|
543
|
+
id: "external-link-arrow",
|
|
544
|
+
description: "The arrow leaving the square."
|
|
545
|
+
},
|
|
546
|
+
arrowHead: {
|
|
547
|
+
id: "external-link-arrow-head",
|
|
548
|
+
description: "The arrow head pointing outward."
|
|
549
|
+
}
|
|
550
|
+
},
|
|
551
|
+
sequences: {
|
|
552
|
+
trigger: [
|
|
553
|
+
// Arrow moves to the top-right
|
|
554
|
+
{
|
|
555
|
+
element: "arrow",
|
|
556
|
+
property: "translateX",
|
|
557
|
+
values: [0, 1.5, 0],
|
|
558
|
+
duration: 0.4,
|
|
559
|
+
ease: "easeInOut"
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
element: "arrow",
|
|
563
|
+
property: "translateY",
|
|
564
|
+
values: [0, -1.5, 0],
|
|
565
|
+
duration: 0.4,
|
|
566
|
+
ease: "easeInOut"
|
|
567
|
+
},
|
|
568
|
+
// Arrow head follows
|
|
569
|
+
{
|
|
570
|
+
element: "arrowHead",
|
|
571
|
+
property: "translateX",
|
|
572
|
+
values: [0, 1.5, 0],
|
|
573
|
+
duration: 0.4,
|
|
574
|
+
ease: "easeInOut"
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
element: "arrowHead",
|
|
578
|
+
property: "translateY",
|
|
579
|
+
values: [0, -1.5, 0],
|
|
580
|
+
duration: 0.4,
|
|
581
|
+
ease: "easeInOut"
|
|
582
|
+
}
|
|
583
|
+
]
|
|
584
|
+
},
|
|
585
|
+
defaultTrigger: "hoverHold"
|
|
586
|
+
};
|
|
587
|
+
var animation_spec_default10 = externalLinkSpec;
|
|
588
|
+
|
|
589
|
+
// icons/external-link/metadata.ts
|
|
590
|
+
var metadata10 = {
|
|
591
|
+
name: "ExternalLink",
|
|
592
|
+
slug: "external-link",
|
|
593
|
+
category: "Uncategorized",
|
|
594
|
+
tags: [],
|
|
595
|
+
featured: false,
|
|
596
|
+
description: "TODO: one-line description for ExternalLink.",
|
|
597
|
+
animationDescription: "TODO: describe what the animation does."
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
// icons/eye/paths.ts
|
|
601
|
+
var eyePaths = {
|
|
602
|
+
outline: {
|
|
603
|
+
type: "path",
|
|
604
|
+
d: "M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12z"
|
|
605
|
+
},
|
|
606
|
+
pupil: { type: "circle", cx: 12, cy: 12, r: 2.5 },
|
|
607
|
+
upperLid: { type: "path", d: "M2 12s3.5-7 10-7 10 7 10 7" }
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
// icons/eye/animation.spec.ts
|
|
611
|
+
var eyeSpec = {
|
|
612
|
+
elements: {
|
|
613
|
+
outline: { id: "eye-outline", description: "Eye outline" },
|
|
614
|
+
pupil: { id: "eye-pupil", description: "Eye pupil" },
|
|
615
|
+
upperLid: { id: "eye-upperLid", description: "Upper eyelid blink motion" }
|
|
616
|
+
},
|
|
617
|
+
sequences: {
|
|
618
|
+
// The upper lid lowers down over the eye to roughly the centre and back.
|
|
619
|
+
// Round-trip values (0 → 7 → 0): `hoverHold` trims the return frame to hold
|
|
620
|
+
// the lid half-closed (lifting on leave), while `hover`/`click`/`inView`
|
|
621
|
+
// play the full drop-and-lift as a one-shot half-blink.
|
|
622
|
+
trigger: [
|
|
623
|
+
{
|
|
624
|
+
element: "upperLid",
|
|
625
|
+
property: "translateY",
|
|
626
|
+
values: [0, 7, 0],
|
|
627
|
+
duration: 0.4,
|
|
628
|
+
ease: "easeInOut"
|
|
629
|
+
}
|
|
630
|
+
]
|
|
631
|
+
},
|
|
632
|
+
defaultTrigger: "hoverHold"
|
|
633
|
+
};
|
|
634
|
+
var animation_spec_default11 = eyeSpec;
|
|
635
|
+
|
|
636
|
+
// icons/eye/metadata.ts
|
|
637
|
+
var metadata11 = {
|
|
638
|
+
name: "Eye",
|
|
639
|
+
slug: "eye",
|
|
640
|
+
category: "Visibility",
|
|
641
|
+
tags: ["eye", "vision", "view", "watch", "visibility", "blink"],
|
|
642
|
+
featured: false,
|
|
643
|
+
description: "An eye icon whose lid lowers halfway.",
|
|
644
|
+
animationDescription: "The upper eyelid lowers over the eye and holds it half-closed while hovered, lifting back up on leave."
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
// icons/heart/paths.ts
|
|
648
|
+
var heartPaths = {
|
|
649
|
+
heart: {
|
|
650
|
+
type: "path",
|
|
651
|
+
d: "M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.29 1.5 4.04 3 5.5l7 7Z"
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
// icons/heart/animation.spec.ts
|
|
656
|
+
var heartSpec = {
|
|
657
|
+
elements: {
|
|
658
|
+
heart: { id: "heart-shape", description: "Heart shape" }
|
|
659
|
+
},
|
|
660
|
+
sequences: {
|
|
661
|
+
trigger: [
|
|
662
|
+
{
|
|
663
|
+
element: "heart",
|
|
664
|
+
property: "scale",
|
|
665
|
+
values: [1, 1.2, 0.95, 1.1, 1],
|
|
666
|
+
duration: 0.5,
|
|
667
|
+
ease: "spring",
|
|
668
|
+
origin: { x: 12, y: 12 }
|
|
669
|
+
}
|
|
670
|
+
]
|
|
671
|
+
},
|
|
672
|
+
defaultTrigger: "hover"
|
|
673
|
+
};
|
|
674
|
+
var animation_spec_default12 = heartSpec;
|
|
675
|
+
|
|
676
|
+
// icons/heart/metadata.ts
|
|
677
|
+
var metadata12 = {
|
|
678
|
+
name: "Heart",
|
|
679
|
+
slug: "heart",
|
|
680
|
+
category: "Social",
|
|
681
|
+
tags: ["heart", "like", "love", "favorite", "save"],
|
|
682
|
+
featured: true,
|
|
683
|
+
description: "A heart for likes, favorites, and saves.",
|
|
684
|
+
animationDescription: "A springy heartbeat pulse while the stroke briefly thickens."
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
// icons/help/paths.ts
|
|
688
|
+
var helpPaths = {
|
|
689
|
+
circle: { type: "path", d: "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20" },
|
|
690
|
+
question: {
|
|
691
|
+
type: "path",
|
|
692
|
+
d: "M9.5 9a2.5 2.5 0 1 1 4.3 1.7c-.9.8-1.8 1.3-1.8 2.8"
|
|
693
|
+
},
|
|
694
|
+
dot: { type: "path", d: "M12 17h.01" }
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
// icons/help/animation.spec.ts
|
|
698
|
+
var helpSpec = {
|
|
699
|
+
elements: {
|
|
700
|
+
question: { id: "help-question", description: "Question mark" },
|
|
701
|
+
dot: { id: "help-dot", description: "Question mark dot" },
|
|
702
|
+
circle: { id: "help-circle", description: "Outer circle" }
|
|
703
|
+
},
|
|
704
|
+
sequences: {
|
|
705
|
+
trigger: [
|
|
706
|
+
{
|
|
707
|
+
element: "question",
|
|
708
|
+
property: "scale",
|
|
709
|
+
values: [1, 1.12, 0.96, 1.06, 1],
|
|
710
|
+
duration: 0.55,
|
|
711
|
+
ease: "easeInOut",
|
|
712
|
+
origin: { x: 12, y: 12 }
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
element: "dot",
|
|
716
|
+
property: "translateY",
|
|
717
|
+
values: [0, -1.5, 0],
|
|
718
|
+
duration: 0.55,
|
|
719
|
+
ease: "easeInOut"
|
|
720
|
+
}
|
|
721
|
+
]
|
|
722
|
+
},
|
|
723
|
+
defaultTrigger: "hover"
|
|
724
|
+
};
|
|
725
|
+
var animation_spec_default13 = helpSpec;
|
|
726
|
+
|
|
727
|
+
// icons/help/metadata.ts
|
|
728
|
+
var metadata13 = {
|
|
729
|
+
name: "Help",
|
|
730
|
+
slug: "help",
|
|
731
|
+
category: "Interface",
|
|
732
|
+
tags: ["help", "question", "support", "faq", "info", "assist"],
|
|
733
|
+
featured: false,
|
|
734
|
+
description: "A circled question mark representing help or support.",
|
|
735
|
+
animationDescription: "The question mark gently pops while its dot bounces upward before settling."
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
// icons/layers/paths.ts
|
|
739
|
+
var layersPaths = {
|
|
740
|
+
base: {
|
|
741
|
+
type: "path",
|
|
742
|
+
d: "M4 14 12 18 20 14 12 10 4 14Z"
|
|
743
|
+
},
|
|
744
|
+
middle: {
|
|
745
|
+
type: "path",
|
|
746
|
+
d: "M6 10 12 13 18 10 12 7 6 10Z"
|
|
747
|
+
},
|
|
748
|
+
top: {
|
|
749
|
+
type: "path",
|
|
750
|
+
d: "M8 6 12 8 16 6 12 4 8 6Z"
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
|
|
754
|
+
// icons/layers/animation.spec.ts
|
|
755
|
+
var layersSpec = {
|
|
756
|
+
elements: {
|
|
757
|
+
base: { id: "layers-base", description: "Bottom layer" },
|
|
758
|
+
middle: { id: "layers-middle", description: "Middle layer" },
|
|
759
|
+
top: { id: "layers-top", description: "Top layer" }
|
|
760
|
+
},
|
|
761
|
+
sequences: {
|
|
762
|
+
trigger: [
|
|
763
|
+
{
|
|
764
|
+
element: "base",
|
|
765
|
+
property: "translateY",
|
|
766
|
+
values: [0, 0, 0],
|
|
767
|
+
duration: 0.7,
|
|
768
|
+
ease: "easeInOut"
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
element: "middle",
|
|
772
|
+
property: "translateY",
|
|
773
|
+
values: [0, -4, 0],
|
|
774
|
+
duration: 0.7,
|
|
775
|
+
ease: "easeInOut"
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
element: "top",
|
|
779
|
+
property: "translateY",
|
|
780
|
+
values: [0, -8, 0],
|
|
781
|
+
duration: 0.7,
|
|
782
|
+
ease: "easeInOut"
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
element: "middle",
|
|
786
|
+
property: "scale",
|
|
787
|
+
values: [1, 1.03, 1],
|
|
788
|
+
duration: 0.7,
|
|
789
|
+
ease: "easeInOut"
|
|
790
|
+
},
|
|
791
|
+
{
|
|
792
|
+
element: "top",
|
|
793
|
+
property: "scale",
|
|
794
|
+
values: [1, 1.06, 1],
|
|
795
|
+
duration: 0.7,
|
|
796
|
+
ease: "easeInOut"
|
|
797
|
+
}
|
|
798
|
+
]
|
|
799
|
+
},
|
|
800
|
+
defaultTrigger: "hover"
|
|
801
|
+
};
|
|
802
|
+
var animation_spec_default14 = layersSpec;
|
|
803
|
+
|
|
804
|
+
// icons/layers/metadata.ts
|
|
805
|
+
var metadata14 = {
|
|
806
|
+
name: "Layers",
|
|
807
|
+
slug: "layers",
|
|
808
|
+
category: "Interface",
|
|
809
|
+
tags: ["layers", "stack", "sheets", "depth", "arrangement", "structure"],
|
|
810
|
+
featured: false,
|
|
811
|
+
description: "A stacked set of layers representing hierarchy or composition.",
|
|
812
|
+
animationDescription: "The layers subtly separate vertically, with the top layers lifting slightly before re-stacking."
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
// icons/refresh/paths.ts
|
|
816
|
+
var refreshPaths = {
|
|
817
|
+
arc: {
|
|
818
|
+
type: "path",
|
|
819
|
+
d: "M21 12a9 9 0 1 1-2.64-6.36M21 3v6h-6"
|
|
820
|
+
}
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
// icons/refresh/animation.spec.ts
|
|
824
|
+
var refreshSpec = {
|
|
825
|
+
elements: {
|
|
826
|
+
arc: { id: "refresh-arc", description: "Circular refresh arrow" }
|
|
827
|
+
},
|
|
828
|
+
sequences: {
|
|
829
|
+
trigger: [
|
|
830
|
+
{
|
|
831
|
+
element: "arc",
|
|
832
|
+
property: "rotate",
|
|
833
|
+
values: [0, 120, 240, 360],
|
|
834
|
+
duration: 0.8,
|
|
835
|
+
ease: "easeInOut",
|
|
836
|
+
origin: { x: 12, y: 12 }
|
|
837
|
+
}
|
|
838
|
+
]
|
|
839
|
+
},
|
|
840
|
+
defaultTrigger: "hover"
|
|
841
|
+
};
|
|
842
|
+
var animation_spec_default15 = refreshSpec;
|
|
843
|
+
|
|
844
|
+
// icons/refresh/metadata.ts
|
|
845
|
+
var metadata15 = {
|
|
846
|
+
name: "Refresh",
|
|
847
|
+
slug: "refresh",
|
|
848
|
+
category: "Interface",
|
|
849
|
+
tags: ["refresh", "reload", "sync", "update", "rotate"],
|
|
850
|
+
featured: false,
|
|
851
|
+
description: "A circular refresh arrow.",
|
|
852
|
+
animationDescription: "The refresh arrow spins one full turn around the center."
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
// icons/search/paths.ts
|
|
856
|
+
var searchPaths = {
|
|
857
|
+
lens: { type: "circle", cx: 11, cy: 11, r: 7 },
|
|
858
|
+
handle: { type: "line", x1: 21, y1: 21, x2: 16.65, y2: 16.65 }
|
|
859
|
+
};
|
|
860
|
+
|
|
861
|
+
// icons/search/animation.spec.ts
|
|
862
|
+
var searchSpec = {
|
|
863
|
+
elements: {
|
|
864
|
+
lens: { id: "search-lens", description: "Search lens circle" },
|
|
865
|
+
handle: { id: "search-handle", description: "Search handle line" }
|
|
866
|
+
},
|
|
867
|
+
sequences: {
|
|
868
|
+
trigger: [
|
|
869
|
+
// The lens pulses first. (Listed before translateX so it becomes the
|
|
870
|
+
// inner wrapper, leaving the shared jiggle as the outermost transform.)
|
|
871
|
+
{
|
|
872
|
+
element: "lens",
|
|
873
|
+
property: "scale",
|
|
874
|
+
values: [1, 1.18, 1],
|
|
875
|
+
duration: 0.3,
|
|
876
|
+
ease: "easeOut",
|
|
877
|
+
origin: { x: 11, y: 11 }
|
|
878
|
+
},
|
|
879
|
+
// Then the whole glass jiggles left-right: the same translateX is applied
|
|
880
|
+
// to both the lens and the handle so they move as one (the React <g>).
|
|
881
|
+
{
|
|
882
|
+
element: "lens",
|
|
883
|
+
property: "translateX",
|
|
884
|
+
values: [0, -2.5, 2.5, -1.5, 1.5, 0],
|
|
885
|
+
duration: 0.5,
|
|
886
|
+
delay: 0.2,
|
|
887
|
+
ease: "easeInOut"
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
element: "handle",
|
|
891
|
+
property: "translateX",
|
|
892
|
+
values: [0, -2.5, 2.5, -1.5, 1.5, 0],
|
|
893
|
+
duration: 0.5,
|
|
894
|
+
delay: 0.2,
|
|
895
|
+
ease: "easeInOut"
|
|
896
|
+
}
|
|
897
|
+
]
|
|
898
|
+
},
|
|
899
|
+
defaultTrigger: "hover"
|
|
900
|
+
};
|
|
901
|
+
var animation_spec_default16 = searchSpec;
|
|
902
|
+
|
|
903
|
+
// icons/search/metadata.ts
|
|
904
|
+
var metadata16 = {
|
|
905
|
+
name: "Search",
|
|
906
|
+
slug: "search",
|
|
907
|
+
category: "Interface",
|
|
908
|
+
tags: ["search", "find", "magnifier", "lookup", "explore"],
|
|
909
|
+
featured: false,
|
|
910
|
+
description: "A magnifying glass for search and discovery.",
|
|
911
|
+
animationDescription: "The lens pulses, then the whole glass jiggles left-right once."
|
|
912
|
+
};
|
|
913
|
+
|
|
914
|
+
// icons/signal/paths.ts
|
|
915
|
+
var signalPaths = {
|
|
916
|
+
bar1: { type: "path", d: "M4 18v2" },
|
|
917
|
+
bar2: { type: "path", d: "M9 14v6" },
|
|
918
|
+
bar3: { type: "path", d: "M14 10v10" },
|
|
919
|
+
bar4: { type: "path", d: "M19 6v14" }
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
// icons/signal/animation.spec.ts
|
|
923
|
+
var signalSpec = {
|
|
924
|
+
elements: {
|
|
925
|
+
bar1: { id: "signal-bar1", description: "Signal bar 1 (shortest)" },
|
|
926
|
+
bar2: { id: "signal-bar2", description: "Signal bar 2" },
|
|
927
|
+
bar3: { id: "signal-bar3", description: "Signal bar 3" },
|
|
928
|
+
bar4: { id: "signal-bar4", description: "Signal bar 4 (tallest)" }
|
|
929
|
+
},
|
|
930
|
+
sequences: {
|
|
931
|
+
trigger: [
|
|
932
|
+
{
|
|
933
|
+
element: "bar1",
|
|
934
|
+
property: "scaleY",
|
|
935
|
+
values: [1, 0.4, 1],
|
|
936
|
+
duration: 0.4,
|
|
937
|
+
ease: "easeInOut",
|
|
938
|
+
origin: { x: 4, y: 20 }
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
element: "bar2",
|
|
942
|
+
property: "scaleY",
|
|
943
|
+
values: [1, 0.4, 1],
|
|
944
|
+
duration: 0.5,
|
|
945
|
+
ease: "easeInOut",
|
|
946
|
+
origin: { x: 9, y: 20 }
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
element: "bar3",
|
|
950
|
+
property: "scaleY",
|
|
951
|
+
values: [1, 0.4, 1],
|
|
952
|
+
duration: 0.6,
|
|
953
|
+
ease: "easeInOut",
|
|
954
|
+
origin: { x: 14, y: 20 }
|
|
955
|
+
},
|
|
956
|
+
{
|
|
957
|
+
element: "bar4",
|
|
958
|
+
property: "scaleY",
|
|
959
|
+
values: [1, 0.4, 1],
|
|
960
|
+
duration: 0.7,
|
|
961
|
+
ease: "easeInOut",
|
|
962
|
+
origin: { x: 19, y: 20 }
|
|
963
|
+
}
|
|
964
|
+
]
|
|
965
|
+
},
|
|
966
|
+
defaultTrigger: "hover"
|
|
967
|
+
};
|
|
968
|
+
var animation_spec_default17 = signalSpec;
|
|
969
|
+
|
|
970
|
+
// icons/signal/metadata.ts
|
|
971
|
+
var metadata17 = {
|
|
972
|
+
name: "Signal",
|
|
973
|
+
slug: "signal",
|
|
974
|
+
category: "Communication",
|
|
975
|
+
tags: ["signal", "network", "cellular", "strength", "connection", "wifi"],
|
|
976
|
+
featured: false,
|
|
977
|
+
description: "Four ascending signal-strength bars.",
|
|
978
|
+
animationDescription: "The bars compress and spring back in a staggered cascade, like a pulse sweeping across the signal."
|
|
979
|
+
};
|
|
980
|
+
|
|
981
|
+
// icons/user/paths.ts
|
|
982
|
+
var userPaths = {
|
|
983
|
+
shape: {
|
|
984
|
+
type: "path",
|
|
985
|
+
d: "M12 12a5 5 0 1 0 0-10 5 5 0 0 0 0 10Zm0 2c-5 0-9 2.5-9 6v2h18v-2c0-3.5-4-6-9-6Z"
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
|
|
989
|
+
// icons/user/animation.spec.ts
|
|
990
|
+
var userSpec = {
|
|
991
|
+
elements: {
|
|
992
|
+
shape: { id: "user-shape", description: "The user silhouette." }
|
|
993
|
+
},
|
|
994
|
+
sequences: {
|
|
995
|
+
// A gentle pop with a side-to-side wobble (scale + rotate around center),
|
|
996
|
+
// matching the React component.
|
|
997
|
+
trigger: [
|
|
998
|
+
{
|
|
999
|
+
element: "shape",
|
|
1000
|
+
property: "scale",
|
|
1001
|
+
values: [1, 1.08, 1],
|
|
1002
|
+
duration: 0.6,
|
|
1003
|
+
ease: "easeInOut",
|
|
1004
|
+
origin: { x: 12, y: 12 }
|
|
1005
|
+
},
|
|
1006
|
+
{
|
|
1007
|
+
element: "shape",
|
|
1008
|
+
property: "rotate",
|
|
1009
|
+
values: [0, -3, 3, 0],
|
|
1010
|
+
duration: 0.6,
|
|
1011
|
+
ease: "easeInOut",
|
|
1012
|
+
origin: { x: 12, y: 12 }
|
|
1013
|
+
}
|
|
1014
|
+
]
|
|
1015
|
+
},
|
|
1016
|
+
defaultTrigger: "hover"
|
|
1017
|
+
};
|
|
1018
|
+
var animation_spec_default18 = userSpec;
|
|
1019
|
+
|
|
1020
|
+
// icons/user/metadata.ts
|
|
1021
|
+
var metadata18 = {
|
|
1022
|
+
name: "User",
|
|
1023
|
+
slug: "user",
|
|
1024
|
+
category: "Uncategorized",
|
|
1025
|
+
tags: [],
|
|
1026
|
+
featured: false,
|
|
1027
|
+
description: "TODO: one-line description for User.",
|
|
1028
|
+
animationDescription: "TODO: describe what the animation does."
|
|
1029
|
+
};
|
|
1030
|
+
|
|
1031
|
+
// icons/wifi/paths.ts
|
|
1032
|
+
var wifiPaths = {
|
|
1033
|
+
outer: { type: "path", d: "M5 13a11 11 0 0 1 14 0" },
|
|
1034
|
+
middle: { type: "path", d: "M8.5 16.5a6 6 0 0 1 7 0" },
|
|
1035
|
+
inner: { type: "path", d: "M12 20h.01" }
|
|
1036
|
+
};
|
|
1037
|
+
|
|
1038
|
+
// icons/wifi/animation.spec.ts
|
|
1039
|
+
var wifiSpec = {
|
|
1040
|
+
elements: {
|
|
1041
|
+
outer: { id: "wifi-outer", description: "Outer wifi signal arc" },
|
|
1042
|
+
middle: { id: "wifi-middle", description: "Middle wifi signal arc" },
|
|
1043
|
+
inner: { id: "wifi-inner", description: "Wifi signal dot" }
|
|
1044
|
+
},
|
|
1045
|
+
sequences: {
|
|
1046
|
+
trigger: [
|
|
1047
|
+
{
|
|
1048
|
+
element: "inner",
|
|
1049
|
+
property: "opacity",
|
|
1050
|
+
values: [1, 0, 1],
|
|
1051
|
+
duration: 0.6,
|
|
1052
|
+
ease: "easeInOut"
|
|
1053
|
+
},
|
|
1054
|
+
{
|
|
1055
|
+
element: "middle",
|
|
1056
|
+
property: "pathLength",
|
|
1057
|
+
values: [1, 0, 1],
|
|
1058
|
+
duration: 0.6,
|
|
1059
|
+
ease: "easeInOut"
|
|
1060
|
+
},
|
|
1061
|
+
{
|
|
1062
|
+
element: "outer",
|
|
1063
|
+
property: "pathLength",
|
|
1064
|
+
values: [1, 0, 1],
|
|
1065
|
+
duration: 0.6,
|
|
1066
|
+
ease: "easeInOut"
|
|
1067
|
+
}
|
|
1068
|
+
]
|
|
1069
|
+
},
|
|
1070
|
+
defaultTrigger: "hover"
|
|
1071
|
+
};
|
|
1072
|
+
var animation_spec_default19 = wifiSpec;
|
|
1073
|
+
|
|
1074
|
+
// icons/wifi/metadata.ts
|
|
1075
|
+
var metadata19 = {
|
|
1076
|
+
name: "Wifi",
|
|
1077
|
+
slug: "wifi",
|
|
1078
|
+
category: "Connectivity",
|
|
1079
|
+
tags: ["wifi", "wireless", "signal", "network", "connection"],
|
|
1080
|
+
featured: false,
|
|
1081
|
+
description: "A wireless connection icon with animated signal bars.",
|
|
1082
|
+
animationDescription: "The wifi bars fill in and disappear in sequence, creating a signal pulse effect."
|
|
1083
|
+
};
|
|
1084
|
+
|
|
1085
|
+
// lib/icon-sources.generated.ts
|
|
1086
|
+
var ICON_SOURCES = {
|
|
1087
|
+
"badge-check": { paths: badgeCheckPaths, spec: animation_spec_default, metadata },
|
|
1088
|
+
"ball": { paths: ballPaths, spec: animation_spec_default2, metadata: metadata2 },
|
|
1089
|
+
"bell": { paths: bellPaths, spec: animation_spec_default3, metadata: metadata3 },
|
|
1090
|
+
"card-flip": { paths: cardFlipPaths, spec: animation_spec_default4, metadata: metadata4 },
|
|
1091
|
+
"check": { paths: checkPaths, spec: animation_spec_default5, metadata: metadata5 },
|
|
1092
|
+
"clock": { paths: clockPaths, spec: animation_spec_default6, metadata: metadata6 },
|
|
1093
|
+
"creating-file": { paths: creatingFilePaths, spec: animation_spec_default7, metadata: metadata7 },
|
|
1094
|
+
"door": { paths: doorPaths, spec: animation_spec_default8, metadata: metadata8 },
|
|
1095
|
+
"download": { paths: downloadPaths, spec: animation_spec_default9, metadata: metadata9 },
|
|
1096
|
+
"external-link": { paths: externalLinkPaths, spec: animation_spec_default10, metadata: metadata10 },
|
|
1097
|
+
"eye": { paths: eyePaths, spec: animation_spec_default11, metadata: metadata11 },
|
|
1098
|
+
"heart": { paths: heartPaths, spec: animation_spec_default12, metadata: metadata12 },
|
|
1099
|
+
"help": { paths: helpPaths, spec: animation_spec_default13, metadata: metadata13 },
|
|
1100
|
+
"layers": { paths: layersPaths, spec: animation_spec_default14, metadata: metadata14 },
|
|
1101
|
+
"refresh": { paths: refreshPaths, spec: animation_spec_default15, metadata: metadata15 },
|
|
1102
|
+
"search": { paths: searchPaths, spec: animation_spec_default16, metadata: metadata16 },
|
|
1103
|
+
"signal": { paths: signalPaths, spec: animation_spec_default17, metadata: metadata17 },
|
|
1104
|
+
"user": { paths: userPaths, spec: animation_spec_default18, metadata: metadata18 },
|
|
1105
|
+
"wifi": { paths: wifiPaths, spec: animation_spec_default19, metadata: metadata19 }
|
|
1106
|
+
};
|
|
1107
|
+
|
|
1108
|
+
// lib/icon-sources.ts
|
|
1109
|
+
function getIconSource(slug) {
|
|
1110
|
+
return ICON_SOURCES[slug];
|
|
1111
|
+
}
|
|
1112
|
+
function listIconSlugs() {
|
|
1113
|
+
return Object.keys(ICON_SOURCES).sort();
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// lib/utils.ts
|
|
1117
|
+
function slugify(input) {
|
|
1118
|
+
return input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1119
|
+
}
|
|
1120
|
+
function toPascalCase(input) {
|
|
1121
|
+
return input.replace(/[^a-zA-Z0-9]+/g, " ").trim().split(/\s+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// lib/generators/types.ts
|
|
1125
|
+
var DEFAULT_ICON_PROPS = {
|
|
1126
|
+
size: 24,
|
|
1127
|
+
color: "currentColor",
|
|
1128
|
+
strokeWidth: 1.5,
|
|
1129
|
+
trigger: "hover",
|
|
1130
|
+
speed: 1,
|
|
1131
|
+
loop: false,
|
|
1132
|
+
delay: 0
|
|
1133
|
+
};
|
|
1134
|
+
var DEFAULT_IMPORT_PATH = "@/components/icons";
|
|
1135
|
+
|
|
1136
|
+
// lib/generators/shared.ts
|
|
1137
|
+
function componentName(slug) {
|
|
1138
|
+
return toPascalCase(slug);
|
|
1139
|
+
}
|
|
1140
|
+
var DEFAULT_ORIGIN = { x: 12, y: 12 };
|
|
1141
|
+
function stepValues(step) {
|
|
1142
|
+
if (step.values && step.values.length > 0) return step.values;
|
|
1143
|
+
return [step.from ?? 0, step.to ?? 0];
|
|
1144
|
+
}
|
|
1145
|
+
function allSteps(spec) {
|
|
1146
|
+
return [
|
|
1147
|
+
...spec.sequences.trigger ?? [],
|
|
1148
|
+
...spec.sequences.continuous ?? [],
|
|
1149
|
+
...spec.sequences.mount ?? []
|
|
1150
|
+
];
|
|
1151
|
+
}
|
|
1152
|
+
function isContinuous(spec) {
|
|
1153
|
+
return (spec.sequences.continuous?.length ?? 0) > 0;
|
|
1154
|
+
}
|
|
1155
|
+
var ROTATE_3D_PROPS = /* @__PURE__ */ new Set(["rotateX", "rotateY"]);
|
|
1156
|
+
function uses3DTransform(spec) {
|
|
1157
|
+
return allSteps(spec).some((s) => ROTATE_3D_PROPS.has(s.property));
|
|
1158
|
+
}
|
|
1159
|
+
function perspectiveFor(spec) {
|
|
1160
|
+
return spec.perspective ?? 500;
|
|
1161
|
+
}
|
|
1162
|
+
function cssEasing(ease) {
|
|
1163
|
+
switch (ease) {
|
|
1164
|
+
case "linear":
|
|
1165
|
+
return "linear";
|
|
1166
|
+
case "easeIn":
|
|
1167
|
+
return "ease-in";
|
|
1168
|
+
case "easeOut":
|
|
1169
|
+
return "ease-out";
|
|
1170
|
+
case "easeInOut":
|
|
1171
|
+
return "ease-in-out";
|
|
1172
|
+
case "spring":
|
|
1173
|
+
return "cubic-bezier(0.34, 1.56, 0.64, 1)";
|
|
1174
|
+
case "bounce":
|
|
1175
|
+
return "cubic-bezier(0.22, 1.2, 0.36, 1)";
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
function framerEasing(ease) {
|
|
1179
|
+
switch (ease) {
|
|
1180
|
+
case "spring":
|
|
1181
|
+
case "bounce":
|
|
1182
|
+
return `[0.34, 1.56, 0.64, 1]`;
|
|
1183
|
+
case "easeIn":
|
|
1184
|
+
return `"easeIn"`;
|
|
1185
|
+
case "easeOut":
|
|
1186
|
+
return `"easeOut"`;
|
|
1187
|
+
case "easeInOut":
|
|
1188
|
+
return `"easeInOut"`;
|
|
1189
|
+
case "linear":
|
|
1190
|
+
return `"linear"`;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
// lib/generators/react.ts
|
|
1195
|
+
var FRAMER_PROP = {
|
|
1196
|
+
rotate: "rotate",
|
|
1197
|
+
rotateX: "rotateX",
|
|
1198
|
+
rotateY: "rotateY",
|
|
1199
|
+
scale: "scale",
|
|
1200
|
+
scaleX: "scaleX",
|
|
1201
|
+
scaleY: "scaleY",
|
|
1202
|
+
translateX: "x",
|
|
1203
|
+
translateY: "y",
|
|
1204
|
+
opacity: "opacity",
|
|
1205
|
+
pathLength: "pathLength",
|
|
1206
|
+
strokeWidth: "strokeWidth"
|
|
1207
|
+
};
|
|
1208
|
+
var TRANSFORM_PROPS = /* @__PURE__ */ new Set([
|
|
1209
|
+
"rotate",
|
|
1210
|
+
"rotateX",
|
|
1211
|
+
"rotateY",
|
|
1212
|
+
"scale",
|
|
1213
|
+
"scaleX",
|
|
1214
|
+
"scaleY",
|
|
1215
|
+
"translateX",
|
|
1216
|
+
"translateY"
|
|
1217
|
+
]);
|
|
1218
|
+
function restValue(step) {
|
|
1219
|
+
if (step.property === "pathLength") return step.to ?? 1;
|
|
1220
|
+
return stepValues(step)[0];
|
|
1221
|
+
}
|
|
1222
|
+
function animateTarget(step) {
|
|
1223
|
+
const values = stepValues(step);
|
|
1224
|
+
return `[${values.join(", ")}]`;
|
|
1225
|
+
}
|
|
1226
|
+
function buildVariants(elementKey, steps, continuous) {
|
|
1227
|
+
const rest = [];
|
|
1228
|
+
const active = [];
|
|
1229
|
+
const transitions = [];
|
|
1230
|
+
for (const step of steps) {
|
|
1231
|
+
const prop = FRAMER_PROP[step.property];
|
|
1232
|
+
rest.push(`${prop}: ${restValue(step)}`);
|
|
1233
|
+
active.push(`${prop}: ${animateTarget(step)}`);
|
|
1234
|
+
const repeat = continuous || step.repeat ? "Infinity" : "0";
|
|
1235
|
+
const delay = step.delay ? `, delay: ${step.delay} / speed` : "";
|
|
1236
|
+
transitions.push(
|
|
1237
|
+
`${prop}: { duration: ${step.duration} / speed, ease: ${framerEasing(step.ease)}, repeat: ${repeat}${delay} }`
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
return ` const ${elementKey}Variants: Variants = {
|
|
1241
|
+
rest: { ${rest.join(", ")} },
|
|
1242
|
+
active: {
|
|
1243
|
+
${active.join(",\n ")},
|
|
1244
|
+
transition: { ${transitions.join(", ")} },
|
|
1245
|
+
},
|
|
1246
|
+
};`;
|
|
1247
|
+
}
|
|
1248
|
+
function originStyle(steps) {
|
|
1249
|
+
const transform = steps.find((s) => TRANSFORM_PROPS.has(s.property));
|
|
1250
|
+
if (!transform) return "";
|
|
1251
|
+
const o = transform.origin ?? DEFAULT_ORIGIN;
|
|
1252
|
+
return ` style={{ transformBox: "view-box", transformOrigin: "${o.x}px ${o.y}px" } as React.CSSProperties}`;
|
|
1253
|
+
}
|
|
1254
|
+
function motionElement(data, id, variantsVar, style) {
|
|
1255
|
+
const tag = data.type;
|
|
1256
|
+
const motionTag = variantsVar ? `motion.${tag}` : tag;
|
|
1257
|
+
const variants = variantsVar ? ` variants={${variantsVar}}${style}` : "";
|
|
1258
|
+
const geom = data.type === "path" ? `d="${data.d}"` : data.type === "circle" ? `cx={${data.cx}} cy={${data.cy}} r={${data.r}}` : data.type === "line" ? `x1={${data.x1}} y1={${data.y1}} x2={${data.x2}} y2={${data.y2}}` : `points="${data.points}"`;
|
|
1259
|
+
return ` <${motionTag} id="${id}" ${geom}${variants} />`;
|
|
1260
|
+
}
|
|
1261
|
+
var reactGenerator = {
|
|
1262
|
+
framework: "react",
|
|
1263
|
+
displayName: "React",
|
|
1264
|
+
fileExtension: "tsx",
|
|
1265
|
+
generate(spec, paths, meta, config) {
|
|
1266
|
+
const name = componentName(meta.slug);
|
|
1267
|
+
const p = config?.props ?? {};
|
|
1268
|
+
const size = p.size ?? DEFAULT_ICON_PROPS.size;
|
|
1269
|
+
const color = p.color ?? "currentColor";
|
|
1270
|
+
const strokeWidth = p.strokeWidth ?? DEFAULT_ICON_PROPS.strokeWidth;
|
|
1271
|
+
const trigger = p.trigger ?? spec.defaultTrigger;
|
|
1272
|
+
const speed = p.speed ?? DEFAULT_ICON_PROPS.speed;
|
|
1273
|
+
const continuous = isContinuous(spec);
|
|
1274
|
+
const is3D = uses3DTransform(spec);
|
|
1275
|
+
const animSteps = [
|
|
1276
|
+
...spec.sequences.trigger ?? [],
|
|
1277
|
+
...spec.sequences.continuous ?? []
|
|
1278
|
+
];
|
|
1279
|
+
const variantDecls = [];
|
|
1280
|
+
const elementMarkup = Object.keys(paths).map((key) => {
|
|
1281
|
+
const data = paths[key];
|
|
1282
|
+
const id = spec.elements[key]?.id ?? key;
|
|
1283
|
+
const steps = animSteps.filter((s) => s.element === key);
|
|
1284
|
+
if (steps.length === 0) return motionElement(data, id, null, "");
|
|
1285
|
+
variantDecls.push(buildVariants(key, steps, continuous));
|
|
1286
|
+
return motionElement(data, id, `${key}Variants`, originStyle(steps));
|
|
1287
|
+
});
|
|
1288
|
+
const autoActive = continuous || trigger === "autoplay";
|
|
1289
|
+
const animateExpr = autoActive ? '"active"' : 'trigger === "click" && clicked ? "active" : "rest"';
|
|
1290
|
+
const perspectiveStyle = is3D ? `
|
|
1291
|
+
style={{ perspective: "${perspectiveFor(spec)}px" }}` : "";
|
|
1292
|
+
const code = `"use client";
|
|
1293
|
+
|
|
1294
|
+
import { useState } from "react";
|
|
1295
|
+
import { motion, type Variants } from "motion/react";
|
|
1296
|
+
|
|
1297
|
+
interface ${name}Props {
|
|
1298
|
+
size?: number;
|
|
1299
|
+
color?: string;
|
|
1300
|
+
strokeWidth?: number;
|
|
1301
|
+
trigger?: "hover" | "hoverHold" | "click" | "inView" | "autoplay" | "none";
|
|
1302
|
+
speed?: number;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
/** ${meta.name} \u2014 ${meta.animationDescription} */
|
|
1306
|
+
export default function ${name}({
|
|
1307
|
+
size = ${size},
|
|
1308
|
+
color = "${color}",
|
|
1309
|
+
strokeWidth = ${strokeWidth},
|
|
1310
|
+
trigger = "${trigger}",
|
|
1311
|
+
speed = ${speed},
|
|
1312
|
+
}: ${name}Props) {
|
|
1313
|
+
const [clicked, setClicked] = useState(false);
|
|
1314
|
+
|
|
1315
|
+
${variantDecls.join("\n\n")}
|
|
1316
|
+
|
|
1317
|
+
const animateState = ${animateExpr};
|
|
1318
|
+
|
|
1319
|
+
return (
|
|
1320
|
+
<motion.svg
|
|
1321
|
+
width={size}
|
|
1322
|
+
height={size}
|
|
1323
|
+
viewBox="0 0 24 24"
|
|
1324
|
+
fill="none"
|
|
1325
|
+
stroke={color}
|
|
1326
|
+
strokeWidth={strokeWidth}
|
|
1327
|
+
strokeLinecap="round"
|
|
1328
|
+
strokeLinejoin="round"
|
|
1329
|
+
role="img"
|
|
1330
|
+
aria-label="${meta.name} icon"${perspectiveStyle}
|
|
1331
|
+
initial="rest"
|
|
1332
|
+
animate={animateState}
|
|
1333
|
+
whileHover={trigger === "hover" ? "active" : undefined}
|
|
1334
|
+
whileInView={trigger === "inView" ? "active" : undefined}
|
|
1335
|
+
viewport={trigger === "inView" ? { once: true } : undefined}
|
|
1336
|
+
onClick={() => trigger === "click" && setClicked((c) => !c)}
|
|
1337
|
+
>
|
|
1338
|
+
${elementMarkup.join("\n")}
|
|
1339
|
+
</motion.svg>
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1342
|
+
`;
|
|
1343
|
+
const importPath = config?.importPath ?? DEFAULT_IMPORT_PATH;
|
|
1344
|
+
return {
|
|
1345
|
+
code,
|
|
1346
|
+
fileExtension: "tsx",
|
|
1347
|
+
importStatement: `import ${name} from "${importPath}/${name}";`,
|
|
1348
|
+
usageSnippet: `<${name} />`,
|
|
1349
|
+
dependencies: "npm install motion"
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
};
|
|
1353
|
+
|
|
1354
|
+
// lib/runtime/compile.ts
|
|
1355
|
+
var TRANSFORM_PROPS2 = /* @__PURE__ */ new Set([
|
|
1356
|
+
"rotate",
|
|
1357
|
+
"rotateX",
|
|
1358
|
+
"rotateY",
|
|
1359
|
+
"scale",
|
|
1360
|
+
"scaleX",
|
|
1361
|
+
"scaleY",
|
|
1362
|
+
"translateX",
|
|
1363
|
+
"translateY"
|
|
1364
|
+
]);
|
|
1365
|
+
function transformValue(property, v) {
|
|
1366
|
+
switch (property) {
|
|
1367
|
+
case "rotate":
|
|
1368
|
+
return `rotate(${v}deg)`;
|
|
1369
|
+
case "rotateX":
|
|
1370
|
+
return `rotateX(${v}deg)`;
|
|
1371
|
+
case "rotateY":
|
|
1372
|
+
return `rotateY(${v}deg)`;
|
|
1373
|
+
case "scale":
|
|
1374
|
+
return `scale(${v})`;
|
|
1375
|
+
case "scaleX":
|
|
1376
|
+
return `scaleX(${v})`;
|
|
1377
|
+
case "scaleY":
|
|
1378
|
+
return `scaleY(${v})`;
|
|
1379
|
+
case "translateX":
|
|
1380
|
+
return `translateX(${v}px)`;
|
|
1381
|
+
case "translateY":
|
|
1382
|
+
return `translateY(${v}px)`;
|
|
1383
|
+
default:
|
|
1384
|
+
return "";
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
function keyframeFor(property, v) {
|
|
1388
|
+
if (TRANSFORM_PROPS2.has(property)) return { transform: transformValue(property, v) };
|
|
1389
|
+
if (property === "opacity") return { opacity: v };
|
|
1390
|
+
if (property === "strokeWidth") return { strokeWidth: v };
|
|
1391
|
+
if (property === "pathLength") return { strokeDashoffset: 1 - v };
|
|
1392
|
+
return {};
|
|
1393
|
+
}
|
|
1394
|
+
function buildKeyframes(step) {
|
|
1395
|
+
const values = stepValues(step);
|
|
1396
|
+
const roundTrip = values.length > 1 && values[0] === values[values.length - 1];
|
|
1397
|
+
const holdValues = roundTrip ? values.slice(0, -1) : values;
|
|
1398
|
+
return {
|
|
1399
|
+
active: values.map((v) => keyframeFor(step.property, v)),
|
|
1400
|
+
hold: holdValues.map((v) => keyframeFor(step.property, v))
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
function groupStyle(step, is3D) {
|
|
1404
|
+
const origin = step.origin ?? DEFAULT_ORIGIN;
|
|
1405
|
+
const style = {
|
|
1406
|
+
"transform-box": "view-box",
|
|
1407
|
+
"transform-origin": `${origin.x}px ${origin.y}px`
|
|
1408
|
+
};
|
|
1409
|
+
if (is3D) style["transform-style"] = "preserve-3d";
|
|
1410
|
+
return style;
|
|
1411
|
+
}
|
|
1412
|
+
function compileSpec(spec, paths) {
|
|
1413
|
+
const continuous = isContinuous(spec);
|
|
1414
|
+
const is3D = uses3DTransform(spec);
|
|
1415
|
+
const animations = [];
|
|
1416
|
+
const elements = [];
|
|
1417
|
+
for (const elementKey of Object.keys(paths)) {
|
|
1418
|
+
const id = spec.elements[elementKey]?.id ?? elementKey;
|
|
1419
|
+
const steps = [
|
|
1420
|
+
...(spec.sequences.trigger ?? []).filter((s) => s.element === elementKey).map((step) => ({ step, alwaysLoop: Boolean(step.repeat) })),
|
|
1421
|
+
...(spec.sequences.continuous ?? []).filter((s) => s.element === elementKey).map((step) => ({ step, alwaysLoop: true }))
|
|
1422
|
+
];
|
|
1423
|
+
const draw = steps.find((s) => s.step.property === "pathLength");
|
|
1424
|
+
let node = {
|
|
1425
|
+
kind: "shape",
|
|
1426
|
+
data: paths[elementKey],
|
|
1427
|
+
id,
|
|
1428
|
+
drawKey: draw ? `${id}-draw` : void 0
|
|
1429
|
+
};
|
|
1430
|
+
if (draw) {
|
|
1431
|
+
const kf = buildKeyframes(draw.step);
|
|
1432
|
+
animations.push({
|
|
1433
|
+
key: `${id}-draw`,
|
|
1434
|
+
active: kf.active,
|
|
1435
|
+
hold: kf.hold,
|
|
1436
|
+
duration: draw.step.duration,
|
|
1437
|
+
delay: draw.step.delay ?? 0,
|
|
1438
|
+
easing: cssEasing(draw.step.ease),
|
|
1439
|
+
alwaysLoop: draw.alwaysLoop
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
for (const { step, alwaysLoop } of steps.filter(
|
|
1443
|
+
(s) => s.step.property !== "pathLength"
|
|
1444
|
+
)) {
|
|
1445
|
+
const key = `${id}-${step.property.toLowerCase()}`;
|
|
1446
|
+
const kf = buildKeyframes(step);
|
|
1447
|
+
animations.push({
|
|
1448
|
+
key,
|
|
1449
|
+
active: kf.active,
|
|
1450
|
+
hold: kf.hold,
|
|
1451
|
+
duration: step.duration,
|
|
1452
|
+
delay: step.delay ?? 0,
|
|
1453
|
+
easing: cssEasing(step.ease),
|
|
1454
|
+
alwaysLoop
|
|
1455
|
+
});
|
|
1456
|
+
node = { kind: "group", key, style: groupStyle(step, is3D), child: node };
|
|
1457
|
+
}
|
|
1458
|
+
elements.push(node);
|
|
1459
|
+
}
|
|
1460
|
+
return {
|
|
1461
|
+
elements,
|
|
1462
|
+
animations,
|
|
1463
|
+
continuous,
|
|
1464
|
+
is3D,
|
|
1465
|
+
perspective: is3D ? perspectiveFor(spec) : null
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
// lib/generators/runtime-template.generated.ts
|
|
1470
|
+
var FLUX_RUNTIME_TS = '/**\r\n * Rehover animation runtime \u2014 framework-agnostic, dependency-free, and safe to\r\n * copy into your project. Every generated icon (Vue/Svelte/Angular/Astro) ships\r\n * its SVG markup with `data-flux` targets plus a compiled `plan`, then calls\r\n * `animateIcon(root, plan, props)` from here. All trigger behavior lives in this\r\n * one file, so it is identical across frameworks.\r\n *\r\n * This file has no imports on purpose: it is emitted verbatim next to your icons\r\n * (as `flux-runtime.ts`) so nothing has to be installed. Tweak it freely.\r\n */\r\n\r\n/** What event starts an icon\'s animation. */\r\nexport type AnimationTrigger =\r\n | "hover"\r\n | "hoverHold"\r\n | "click"\r\n | "inView"\r\n | "autoplay"\r\n | "none";\r\n\r\n/** One element\'s compiled animation, keyed by its `data-flux` target. */\r\nexport interface StepAnimation {\r\n /** Matches the `data-flux` attribute on the element this animates. */\r\n key: string;\r\n /** Full keyframes (round-trip or destination) for hover/click/inView/autoplay. */\r\n active: Keyframe[];\r\n /** Rest \u2192 peak keyframes for `hoverHold` (the return is the reverse of this). */\r\n hold: Keyframe[];\r\n /** Base duration in seconds (before the `speed` prop divides it). */\r\n duration: number;\r\n /** Base delay in seconds (before `speed`; the `delay` prop is added at runtime). */\r\n delay: number;\r\n /** CSS timing-function string. */\r\n easing: string;\r\n /** Loops regardless of the `loop` prop (continuous/`repeat` steps). */\r\n alwaysLoop: boolean;\r\n}\r\n\r\n/** What the runtime needs from a compiled plan. */\r\nexport interface RuntimePlan {\r\n /** Per-element animations. */\r\n animations: StepAnimation[];\r\n /** Whether the icon autostarts (continuous specs). */\r\n continuous: boolean;\r\n}\r\n\r\n/** The runtime-tunable props that map to the icon\'s public API. */\r\nexport interface IconRuntimeProps {\r\n trigger: AnimationTrigger;\r\n speed: number;\r\n loop: boolean;\r\n delay: number;\r\n}\r\n\r\n/** Handle returned by {@link animateIcon} for prop updates and teardown. */\r\nexport interface IconController {\r\n /** Re-wire for new props (e.g. the playground changing `trigger`). */\r\n update(props: IconRuntimeProps): void;\r\n /** Remove all listeners/observers and cancel running animations. */\r\n destroy(): void;\r\n}\r\n\r\ntype Variant = "active" | "hold";\r\n\r\ninterface Bound {\r\n anim: StepAnimation;\r\n el: Element;\r\n}\r\n\r\n/** Attach the animation runtime to an already-rendered icon root. */\r\nexport function animateIcon(\r\n root: SVGSVGElement,\r\n plan: RuntimePlan,\r\n initialProps: IconRuntimeProps,\r\n): IconController {\r\n let props = initialProps;\r\n\r\n // Resolve each animation\'s DOM target once.\r\n const bound: Bound[] = [];\r\n for (const anim of plan.animations) {\r\n const el = root.querySelector(`[data-flux="${anim.key}"]`);\r\n if (el) bound.push({ anim, el });\r\n }\r\n\r\n // The currently-playing Animation per target (for restart/reverse).\r\n const current = new Map<string, Animation>();\r\n const cleanups: Array<() => void> = [];\r\n let observer: IntersectionObserver | null = null;\r\n\r\n function makeAnimation(b: Bound, variant: Variant): Animation {\r\n const { anim } = b;\r\n const keyframes = variant === "hold" ? anim.hold : anim.active;\r\n const loops = variant === "active" && (anim.alwaysLoop || props.loop);\r\n return b.el.animate(keyframes, {\r\n duration: (anim.duration / props.speed) * 1000,\r\n delay: ((anim.delay + props.delay) / props.speed) * 1000,\r\n easing: anim.easing,\r\n fill: "forwards",\r\n iterations: loops ? Infinity : 1,\r\n });\r\n }\r\n\r\n /** (Re)start a variant on every target, cancelling any in-flight run. */\r\n function play(variant: Variant): void {\r\n for (const b of bound) {\r\n current.get(b.anim.key)?.cancel();\r\n current.set(b.anim.key, makeAnimation(b, variant));\r\n }\r\n }\r\n\r\n /** Reverse the held pose back to rest (smooth, from the current position). */\r\n function reverseToRest(): void {\r\n for (const b of bound) {\r\n const running = current.get(b.anim.key);\r\n if (running) running.reverse();\r\n }\r\n }\r\n\r\n function on(type: string, handler: EventListener): void {\r\n root.addEventListener(type, handler);\r\n cleanups.push(() => root.removeEventListener(type, handler));\r\n }\r\n\r\n /** Tear down listeners/observers and stop animations (keeps final pose). */\r\n function teardown(): void {\r\n for (const c of cleanups) c();\r\n cleanups.length = 0;\r\n observer?.disconnect();\r\n observer = null;\r\n }\r\n\r\n function setup(): void {\r\n teardown();\r\n\r\n // Accessibility reflects interactivity and stays in sync with `trigger`.\r\n if (props.trigger === "click") {\r\n root.setAttribute("role", "button");\r\n root.setAttribute("tabindex", "0");\r\n } else {\r\n root.setAttribute("role", "img");\r\n root.removeAttribute("tabindex");\r\n }\r\n\r\n // Continuous specs and autoplay start immediately.\r\n if (plan.continuous || props.trigger === "autoplay") play("active");\r\n\r\n switch (props.trigger) {\r\n case "hover":\r\n on("mouseenter", () => play("active"));\r\n on("focus", () => play("active"));\r\n break;\r\n case "hoverHold":\r\n on("mouseenter", () => play("hold"));\r\n on("mouseleave", () => reverseToRest());\r\n on("focus", () => play("hold"));\r\n on("blur", () => reverseToRest());\r\n break;\r\n case "click":\r\n on("click", () => play("active"));\r\n on("keydown", (e) => {\r\n const key = (e as KeyboardEvent).key;\r\n if (key === "Enter" || key === " ") {\r\n e.preventDefault();\r\n play("active");\r\n }\r\n });\r\n break;\r\n case "inView":\r\n observer = new IntersectionObserver(\r\n (entries) => {\r\n if (entries.some((entry) => entry.isIntersecting)) {\r\n play("active");\r\n observer?.disconnect();\r\n observer = null;\r\n }\r\n },\r\n { threshold: 0.5 },\r\n );\r\n observer.observe(root);\r\n break;\r\n }\r\n }\r\n\r\n setup();\r\n\r\n return {\r\n update(next: IconRuntimeProps) {\r\n props = next;\r\n // Reset every target to rest before re-wiring for the new props.\r\n for (const a of current.values()) a.cancel();\r\n current.clear();\r\n setup();\r\n },\r\n destroy() {\r\n teardown();\r\n for (const a of current.values()) a.cancel();\r\n current.clear();\r\n },\r\n };\r\n}\r\n';
|
|
1471
|
+
|
|
1472
|
+
// lib/generators/vue.ts
|
|
1473
|
+
var RUNTIME_FILENAME = "rehover-runtime";
|
|
1474
|
+
function styleAttr(style) {
|
|
1475
|
+
const s = Object.entries(style).map(([k, v]) => `${k}: ${v}`).join("; ");
|
|
1476
|
+
return s ? ` style="${s}"` : "";
|
|
1477
|
+
}
|
|
1478
|
+
function shapeMarkup(node) {
|
|
1479
|
+
const draw = node.drawKey ? ` data-flux="${node.drawKey}" pathLength="1" style="stroke-dasharray: 1"` : "";
|
|
1480
|
+
const d = node.data;
|
|
1481
|
+
switch (d.type) {
|
|
1482
|
+
case "path":
|
|
1483
|
+
return `<path id="${node.id}" d="${d.d}"${draw} />`;
|
|
1484
|
+
case "circle":
|
|
1485
|
+
return `<circle id="${node.id}" cx="${d.cx}" cy="${d.cy}" r="${d.r}"${draw} />`;
|
|
1486
|
+
case "line":
|
|
1487
|
+
return `<line id="${node.id}" x1="${d.x1}" y1="${d.y1}" x2="${d.x2}" y2="${d.y2}"${draw} />`;
|
|
1488
|
+
case "polyline":
|
|
1489
|
+
return `<polyline id="${node.id}" points="${d.points}"${draw} />`;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
function nodeMarkup(node, indent) {
|
|
1493
|
+
if (node.kind === "shape") return shapeMarkup(node);
|
|
1494
|
+
const child = nodeMarkup(node.child, `${indent} `);
|
|
1495
|
+
return `<g data-flux="${node.key}"${styleAttr(node.style)}>
|
|
1496
|
+
${indent} ${child}
|
|
1497
|
+
${indent}</g>`;
|
|
1498
|
+
}
|
|
1499
|
+
var vueGenerator = {
|
|
1500
|
+
framework: "vue",
|
|
1501
|
+
displayName: "Vue 3",
|
|
1502
|
+
fileExtension: "vue",
|
|
1503
|
+
generate(spec, paths, meta, config) {
|
|
1504
|
+
const name = componentName(meta.slug);
|
|
1505
|
+
const p = config?.props ?? {};
|
|
1506
|
+
const size = p.size ?? DEFAULT_ICON_PROPS.size;
|
|
1507
|
+
const color = p.color === "currentColor" || !p.color ? "currentColor" : p.color;
|
|
1508
|
+
const strokeWidth = p.strokeWidth ?? DEFAULT_ICON_PROPS.strokeWidth;
|
|
1509
|
+
const trigger = p.trigger ?? spec.defaultTrigger;
|
|
1510
|
+
const speed = p.speed ?? DEFAULT_ICON_PROPS.speed;
|
|
1511
|
+
const loop = p.loop ?? spec.defaultLoop ?? DEFAULT_ICON_PROPS.loop;
|
|
1512
|
+
const delay = p.delay ?? DEFAULT_ICON_PROPS.delay;
|
|
1513
|
+
const runtimeImport = `./${RUNTIME_FILENAME}`;
|
|
1514
|
+
const plan = compileSpec(spec, paths);
|
|
1515
|
+
const body = plan.elements.map((el) => nodeMarkup(el, " ")).join("\n ");
|
|
1516
|
+
const perspectiveAttr = plan.perspective != null ? `
|
|
1517
|
+
style="perspective: ${plan.perspective}px"` : "";
|
|
1518
|
+
const runtimePlan = JSON.stringify(
|
|
1519
|
+
{ continuous: plan.continuous, animations: plan.animations },
|
|
1520
|
+
null,
|
|
1521
|
+
2
|
|
1522
|
+
);
|
|
1523
|
+
const code = `<script setup lang="ts">
|
|
1524
|
+
import { ref, onMounted, onUnmounted, watch } from 'vue'
|
|
1525
|
+
import { animateIcon, type RuntimePlan } from '${runtimeImport}'
|
|
1526
|
+
|
|
1527
|
+
interface Props {
|
|
1528
|
+
size?: number
|
|
1529
|
+
color?: string
|
|
1530
|
+
strokeWidth?: number
|
|
1531
|
+
trigger?: 'hover' | 'hoverHold' | 'click' | 'inView' | 'autoplay' | 'none'
|
|
1532
|
+
speed?: number
|
|
1533
|
+
loop?: boolean
|
|
1534
|
+
delay?: number
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
1538
|
+
size: ${size},
|
|
1539
|
+
color: '${color}',
|
|
1540
|
+
strokeWidth: ${strokeWidth},
|
|
1541
|
+
trigger: '${trigger}',
|
|
1542
|
+
speed: ${speed},
|
|
1543
|
+
loop: ${loop},
|
|
1544
|
+
delay: ${delay},
|
|
1545
|
+
})
|
|
1546
|
+
|
|
1547
|
+
// Keyframes + timing compiled from the icon's animation spec. Identical across
|
|
1548
|
+
// every framework because they come from the one shared compiler.
|
|
1549
|
+
const plan: RuntimePlan = ${runtimePlan}
|
|
1550
|
+
|
|
1551
|
+
const rootRef = ref<SVGSVGElement | null>(null)
|
|
1552
|
+
let controller: ReturnType<typeof animateIcon> | null = null
|
|
1553
|
+
|
|
1554
|
+
function runtimeProps() {
|
|
1555
|
+
return {
|
|
1556
|
+
trigger: props.trigger,
|
|
1557
|
+
speed: props.speed,
|
|
1558
|
+
loop: props.loop,
|
|
1559
|
+
delay: props.delay,
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
onMounted(() => {
|
|
1564
|
+
if (rootRef.value) controller = animateIcon(rootRef.value, plan, runtimeProps())
|
|
1565
|
+
})
|
|
1566
|
+
|
|
1567
|
+
// Re-wire when an animation-affecting prop changes (e.g. the playground).
|
|
1568
|
+
watch(
|
|
1569
|
+
() => [props.trigger, props.speed, props.loop, props.delay],
|
|
1570
|
+
() => controller?.update(runtimeProps()),
|
|
1571
|
+
)
|
|
1572
|
+
|
|
1573
|
+
onUnmounted(() => controller?.destroy())
|
|
1574
|
+
</script>
|
|
1575
|
+
|
|
1576
|
+
<template>
|
|
1577
|
+
<svg
|
|
1578
|
+
ref="rootRef"
|
|
1579
|
+
:width="size"
|
|
1580
|
+
:height="size"
|
|
1581
|
+
viewBox="0 0 24 24"
|
|
1582
|
+
fill="none"
|
|
1583
|
+
:stroke="color"
|
|
1584
|
+
:stroke-width="strokeWidth"
|
|
1585
|
+
stroke-linecap="round"
|
|
1586
|
+
stroke-linejoin="round"
|
|
1587
|
+
role="img"
|
|
1588
|
+
aria-label="${meta.name} icon"${perspectiveAttr}
|
|
1589
|
+
>
|
|
1590
|
+
${body}
|
|
1591
|
+
</svg>
|
|
1592
|
+
</template>
|
|
1593
|
+
`;
|
|
1594
|
+
return {
|
|
1595
|
+
code,
|
|
1596
|
+
fileExtension: "vue",
|
|
1597
|
+
importStatement: `import ${name} from '@/components/icons/${name}.vue';`,
|
|
1598
|
+
usageSnippet: `<${name} />`,
|
|
1599
|
+
dependencies: "none \u2014 self-contained (no npm install)",
|
|
1600
|
+
runtime: { filename: `${RUNTIME_FILENAME}.ts`, code: FLUX_RUNTIME_TS }
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
};
|
|
1604
|
+
|
|
1605
|
+
// lib/generators/index.ts
|
|
1606
|
+
var ICON_GENERATORS = {
|
|
1607
|
+
react: reactGenerator,
|
|
1608
|
+
vue: vueGenerator
|
|
1609
|
+
};
|
|
1610
|
+
function getIconGenerator(framework) {
|
|
1611
|
+
return ICON_GENERATORS[framework];
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
// cli/index.ts
|
|
1615
|
+
var VERSION = true ? "0.1.0-beta.1" : "0.0.0";
|
|
1616
|
+
var supportsColor = process.stdout.isTTY && process.env.NO_COLOR === void 0;
|
|
1617
|
+
var paint = (code, s) => supportsColor ? `\x1B[${code}m${s}\x1B[0m` : s;
|
|
1618
|
+
var green = (s) => paint("32", s);
|
|
1619
|
+
var red = (s) => paint("31", s);
|
|
1620
|
+
var cyan = (s) => paint("36", s);
|
|
1621
|
+
var yellow = (s) => paint("33", s);
|
|
1622
|
+
var dim = (s) => paint("2", s);
|
|
1623
|
+
var bold = (s) => paint("1", s);
|
|
1624
|
+
function fail(message) {
|
|
1625
|
+
console.error(red(`\u2717 ${message}`));
|
|
1626
|
+
process.exit(1);
|
|
1627
|
+
}
|
|
1628
|
+
function resolveFramework(input) {
|
|
1629
|
+
const key = input.trim().toLowerCase();
|
|
1630
|
+
const aliases = {
|
|
1631
|
+
react: "react",
|
|
1632
|
+
next: "react",
|
|
1633
|
+
"next.js": "react",
|
|
1634
|
+
nextjs: "react",
|
|
1635
|
+
vue: "vue",
|
|
1636
|
+
vue3: "vue"
|
|
1637
|
+
};
|
|
1638
|
+
return aliases[key];
|
|
1639
|
+
}
|
|
1640
|
+
var FLUX_ICONS_DIR = "rehover";
|
|
1641
|
+
function detectComponentsDir() {
|
|
1642
|
+
const candidates = [
|
|
1643
|
+
(0, import_node_path.join)("src", "components"),
|
|
1644
|
+
"components",
|
|
1645
|
+
(0, import_node_path.join)("app", "components"),
|
|
1646
|
+
(0, import_node_path.join)("src", "app", "components")
|
|
1647
|
+
];
|
|
1648
|
+
for (const candidate of candidates) {
|
|
1649
|
+
if ((0, import_node_fs.existsSync)((0, import_node_path.join)(process.cwd(), candidate))) return candidate;
|
|
1650
|
+
}
|
|
1651
|
+
return (0, import_node_fs.existsSync)((0, import_node_path.join)(process.cwd(), "src")) ? (0, import_node_path.join)("src", "components") : "components";
|
|
1652
|
+
}
|
|
1653
|
+
function resolveOutputDir(outDir) {
|
|
1654
|
+
return outDir ?? (0, import_node_path.join)(detectComponentsDir(), FLUX_ICONS_DIR);
|
|
1655
|
+
}
|
|
1656
|
+
function outputPath(slug, ext, dir) {
|
|
1657
|
+
const fileBase = toPascalCase(slug);
|
|
1658
|
+
return (0, import_node_path.join)(process.cwd(), dir, `${fileBase}.${ext}`);
|
|
1659
|
+
}
|
|
1660
|
+
function ask(question) {
|
|
1661
|
+
const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
1662
|
+
return new Promise((resolve) => {
|
|
1663
|
+
rl.question(question, (answer) => {
|
|
1664
|
+
rl.close();
|
|
1665
|
+
resolve(answer.trim());
|
|
1666
|
+
});
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1669
|
+
async function promptFramework() {
|
|
1670
|
+
const keys = Object.keys(ICON_GENERATORS);
|
|
1671
|
+
if (!process.stdin.isTTY) return keys[0];
|
|
1672
|
+
console.log(`
|
|
1673
|
+
${bold("Which framework?")}`);
|
|
1674
|
+
keys.forEach((key, i) => {
|
|
1675
|
+
console.log(` ${cyan(String(i + 1))}) ${ICON_GENERATORS[key].displayName}`);
|
|
1676
|
+
});
|
|
1677
|
+
const answer = await ask(`
|
|
1678
|
+
${dim(`Select [1-${keys.length}, default 1]:`)} `);
|
|
1679
|
+
if (answer === "") return keys[0];
|
|
1680
|
+
const num = Number(answer);
|
|
1681
|
+
if (Number.isInteger(num) && num >= 1 && num <= keys.length) {
|
|
1682
|
+
return keys[num - 1];
|
|
1683
|
+
}
|
|
1684
|
+
const resolved = resolveFramework(answer);
|
|
1685
|
+
if (resolved && keys.includes(resolved)) return resolved;
|
|
1686
|
+
console.log(
|
|
1687
|
+
yellow(`Unrecognized choice \u2014 defaulting to ${ICON_GENERATORS[keys[0]].displayName}.`)
|
|
1688
|
+
);
|
|
1689
|
+
return keys[0];
|
|
1690
|
+
}
|
|
1691
|
+
function rel(absPath) {
|
|
1692
|
+
return absPath.replace(`${process.cwd()}\\`, "").replace(`${process.cwd()}/`, "").replace(/\\/g, "/");
|
|
1693
|
+
}
|
|
1694
|
+
function parseArgs(argv) {
|
|
1695
|
+
const args = {
|
|
1696
|
+
icons: [],
|
|
1697
|
+
force: false,
|
|
1698
|
+
help: false,
|
|
1699
|
+
version: false
|
|
1700
|
+
};
|
|
1701
|
+
let frameworkRaw;
|
|
1702
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1703
|
+
const a = argv[i];
|
|
1704
|
+
if (a === "--help" || a === "-h") args.help = true;
|
|
1705
|
+
else if (a === "--version" || a === "-v") args.version = true;
|
|
1706
|
+
else if (a === "--force") args.force = true;
|
|
1707
|
+
else if (a === "--framework" || a === "-f") frameworkRaw = argv[++i] ?? "";
|
|
1708
|
+
else if (a.startsWith("--framework=")) frameworkRaw = a.slice("--framework=".length);
|
|
1709
|
+
else if (a === "--out" || a === "-o") args.outDir = argv[++i];
|
|
1710
|
+
else if (a.startsWith("--out=")) args.outDir = a.slice("--out=".length);
|
|
1711
|
+
else if (a.startsWith("-")) fail(`Unknown option "${a}". Run \`rehover --help\`.`);
|
|
1712
|
+
else if (!args.command) args.command = a;
|
|
1713
|
+
else args.icons.push(a);
|
|
1714
|
+
}
|
|
1715
|
+
if (frameworkRaw !== void 0) {
|
|
1716
|
+
const resolved = resolveFramework(frameworkRaw);
|
|
1717
|
+
if (!resolved) {
|
|
1718
|
+
fail(
|
|
1719
|
+
`Unknown framework "${frameworkRaw}". Available: ${Object.keys(ICON_GENERATORS).join(", ")}.`
|
|
1720
|
+
);
|
|
1721
|
+
}
|
|
1722
|
+
args.framework = resolved;
|
|
1723
|
+
}
|
|
1724
|
+
return args;
|
|
1725
|
+
}
|
|
1726
|
+
function printHelp() {
|
|
1727
|
+
console.log(`
|
|
1728
|
+
${bold("Rehover")} ${dim(`v${VERSION}`)} \u2014 animated icons for every framework
|
|
1729
|
+
|
|
1730
|
+
${bold("Usage")}
|
|
1731
|
+
${cyan("npx @rehover/icons add <icon...> [options]")}
|
|
1732
|
+
|
|
1733
|
+
${bold("Commands")}
|
|
1734
|
+
${cyan("add <icon...>")} Add one or more icons to your project
|
|
1735
|
+
${cyan("list")} List every available icon
|
|
1736
|
+
|
|
1737
|
+
${bold("Options")}
|
|
1738
|
+
${cyan("-f, --framework")} react | vue ${dim("(prompts if omitted)")}
|
|
1739
|
+
${cyan("-o, --out")} Output directory ${dim("(default: <components>/rehover, auto-detected)")}
|
|
1740
|
+
${cyan(" --force")} Overwrite existing files
|
|
1741
|
+
${cyan("-h, --help")} Show this help
|
|
1742
|
+
${cyan("-v, --version")} Show the version
|
|
1743
|
+
|
|
1744
|
+
${bold("Examples")}
|
|
1745
|
+
${dim("$")} npx @rehover/icons add bell
|
|
1746
|
+
${dim("$")} npx @rehover/icons add clock heart --framework vue
|
|
1747
|
+
${dim("$")} npx @rehover/icons add download --out src/icons
|
|
1748
|
+
`);
|
|
1749
|
+
}
|
|
1750
|
+
function printList() {
|
|
1751
|
+
const slugs = listIconSlugs();
|
|
1752
|
+
console.log(`
|
|
1753
|
+
${bold(`${slugs.length} icons available`)}
|
|
1754
|
+
`);
|
|
1755
|
+
for (const slug of slugs) {
|
|
1756
|
+
const meta = ICON_SOURCES[slug].metadata;
|
|
1757
|
+
console.log(` ${cyan(slug.padEnd(12))} ${dim(meta.description)}`);
|
|
1758
|
+
}
|
|
1759
|
+
console.log(`
|
|
1760
|
+
${dim("Add one with:")} npx @rehover/icons add ${slugs[0]}
|
|
1761
|
+
`);
|
|
1762
|
+
}
|
|
1763
|
+
async function add(args) {
|
|
1764
|
+
if (args.icons.length === 0) {
|
|
1765
|
+
fail("No icon specified. Try `rehover add bell` or `rehover list`.");
|
|
1766
|
+
}
|
|
1767
|
+
const framework = args.framework ?? await promptFramework();
|
|
1768
|
+
const generator = getIconGenerator(framework);
|
|
1769
|
+
if (!generator) {
|
|
1770
|
+
fail(`Unknown framework "${framework}".`);
|
|
1771
|
+
}
|
|
1772
|
+
const outDir = resolveOutputDir(args.outDir);
|
|
1773
|
+
let written = 0;
|
|
1774
|
+
let depsNote = "";
|
|
1775
|
+
const runtimesWritten = /* @__PURE__ */ new Set();
|
|
1776
|
+
for (const name of args.icons) {
|
|
1777
|
+
const slug = slugify(name);
|
|
1778
|
+
const source = getIconSource(slug);
|
|
1779
|
+
if (!source) {
|
|
1780
|
+
console.error(
|
|
1781
|
+
red(`\u2717 Unknown icon "${name}".`) + dim(" Run `rehover list` to see what's available.")
|
|
1782
|
+
);
|
|
1783
|
+
continue;
|
|
1784
|
+
}
|
|
1785
|
+
const output = generator.generate(source.spec, source.paths, source.metadata);
|
|
1786
|
+
const dest = outputPath(slug, output.fileExtension, outDir);
|
|
1787
|
+
if ((0, import_node_fs.existsSync)(dest) && !args.force) {
|
|
1788
|
+
console.log(
|
|
1789
|
+
yellow(`\u2022 Skipped ${rel(dest)}`) + dim(" (already exists \u2014 use --force to overwrite)")
|
|
1790
|
+
);
|
|
1791
|
+
continue;
|
|
1792
|
+
}
|
|
1793
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(dest), { recursive: true });
|
|
1794
|
+
(0, import_node_fs.writeFileSync)(dest, output.code);
|
|
1795
|
+
console.log(green(`\u2713 ${rel(dest)}`) + dim(` (${generator.displayName})`));
|
|
1796
|
+
written++;
|
|
1797
|
+
depsNote = output.dependencies;
|
|
1798
|
+
if (output.runtime && !runtimesWritten.has(output.runtime.filename)) {
|
|
1799
|
+
runtimesWritten.add(output.runtime.filename);
|
|
1800
|
+
const runtimeDest = (0, import_node_path.join)(process.cwd(), outDir, output.runtime.filename);
|
|
1801
|
+
if (!(0, import_node_fs.existsSync)(runtimeDest)) {
|
|
1802
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(runtimeDest), { recursive: true });
|
|
1803
|
+
(0, import_node_fs.writeFileSync)(runtimeDest, output.runtime.code);
|
|
1804
|
+
console.log(green(`\u2713 ${rel(runtimeDest)}`) + dim(" (shared runtime)"));
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
if (written > 0) {
|
|
1809
|
+
console.log();
|
|
1810
|
+
console.log(dim("Dependencies: ") + depsNote);
|
|
1811
|
+
console.log(dim("Done.") + ` Added ${written} icon${written === 1 ? "" : "s"}.`);
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
async function main() {
|
|
1815
|
+
const args = parseArgs(process.argv.slice(2));
|
|
1816
|
+
if (args.version) {
|
|
1817
|
+
console.log(VERSION);
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
if (args.help || !args.command) {
|
|
1821
|
+
printHelp();
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
switch (args.command) {
|
|
1825
|
+
case "add":
|
|
1826
|
+
await add(args);
|
|
1827
|
+
break;
|
|
1828
|
+
case "list":
|
|
1829
|
+
case "ls":
|
|
1830
|
+
printList();
|
|
1831
|
+
break;
|
|
1832
|
+
default:
|
|
1833
|
+
fail(`Unknown command "${args.command}". Run \`rehover --help\`.`);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
main().catch((err) => {
|
|
1837
|
+
console.error(err);
|
|
1838
|
+
process.exit(1);
|
|
1839
|
+
});
|