gospelo-iconcraft-react 0.1.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.
package/dist/index.js ADDED
@@ -0,0 +1,1954 @@
1
+ // src/core/IconCraftConfig.ts
2
+ var DEFAULT_CONFIG = {
3
+ mode: "wax",
4
+ color: "#6366f1",
5
+ iconStyle: "emboss",
6
+ shadow: true,
7
+ highlight: true,
8
+ offset: 20,
9
+ resolution: 256,
10
+ simplify: 2,
11
+ size: 120,
12
+ width: void 0,
13
+ height: void 0,
14
+ animation: void 0,
15
+ animateOnHover: false
16
+ };
17
+ var IconCraftConfig = class _IconCraftConfig {
18
+ constructor(options = {}) {
19
+ this.mode = options.mode ?? DEFAULT_CONFIG.mode;
20
+ this.color = options.color ?? DEFAULT_CONFIG.color;
21
+ this.iconStyle = options.iconStyle ?? DEFAULT_CONFIG.iconStyle;
22
+ this.shadow = options.shadow ?? DEFAULT_CONFIG.shadow;
23
+ this.highlight = options.highlight ?? DEFAULT_CONFIG.highlight;
24
+ this.offset = options.offset ?? DEFAULT_CONFIG.offset;
25
+ this.resolution = options.resolution ?? DEFAULT_CONFIG.resolution;
26
+ this.simplify = options.simplify ?? DEFAULT_CONFIG.simplify;
27
+ this.size = options.size ?? DEFAULT_CONFIG.size;
28
+ this.width = options.width;
29
+ this.height = options.height;
30
+ this.animation = options.animation;
31
+ this.animateOnHover = options.animateOnHover ?? DEFAULT_CONFIG.animateOnHover;
32
+ }
33
+ /**
34
+ * 設定を部分的に上書きした新しいConfigを生成
35
+ */
36
+ clone(overrides = {}) {
37
+ return new _IconCraftConfig({
38
+ mode: overrides.mode ?? this.mode,
39
+ color: overrides.color ?? this.color,
40
+ iconStyle: overrides.iconStyle ?? this.iconStyle,
41
+ shadow: overrides.shadow ?? this.shadow,
42
+ highlight: overrides.highlight ?? this.highlight,
43
+ offset: overrides.offset ?? this.offset,
44
+ resolution: overrides.resolution ?? this.resolution,
45
+ simplify: overrides.simplify ?? this.simplify,
46
+ size: overrides.size ?? this.size,
47
+ width: overrides.width ?? this.width,
48
+ height: overrides.height ?? this.height,
49
+ animation: overrides.animation ?? this.animation,
50
+ animateOnHover: overrides.animateOnHover ?? this.animateOnHover
51
+ });
52
+ }
53
+ /**
54
+ * WASM呼び出し用のパラメータを取得
55
+ */
56
+ getWasmParams() {
57
+ const needsEmbossSvg = this.mode === "wax" || this.iconStyle === "emboss";
58
+ return {
59
+ mode: this.mode,
60
+ offset: this.offset,
61
+ resolution: this.resolution,
62
+ simplify: this.simplify,
63
+ includeIcon: needsEmbossSvg,
64
+ color: this.color
65
+ };
66
+ }
67
+ /**
68
+ * スタイル用のサイズを取得
69
+ */
70
+ getSize() {
71
+ const w = this.width ?? this.size;
72
+ const h = this.height ?? this.size;
73
+ return {
74
+ width: typeof w === "number" ? `${w}px` : w,
75
+ height: typeof h === "number" ? `${h}px` : h
76
+ };
77
+ }
78
+ };
79
+
80
+ // src/core/WasmManager.ts
81
+ function createCacheKey(params) {
82
+ return JSON.stringify({
83
+ svg: params.svgContent.slice(0, 100),
84
+ // SVGの先頭100文字でハッシュ
85
+ svgLen: params.svgContent.length,
86
+ mode: params.mode,
87
+ offset: params.offset,
88
+ resolution: params.resolution,
89
+ simplify: params.simplify,
90
+ includeIcon: params.includeIcon,
91
+ color: params.color
92
+ });
93
+ }
94
+ var shapeModeMap = {
95
+ jelly: 0,
96
+ droplet: 1,
97
+ wax: 2
98
+ };
99
+ var WasmManagerClass = class {
100
+ constructor() {
101
+ this.module = null;
102
+ this.initPromise = null;
103
+ this.cache = /* @__PURE__ */ new Map();
104
+ this.maxCacheSize = 100;
105
+ }
106
+ /**
107
+ * WASMモジュールを初期化
108
+ */
109
+ async init() {
110
+ if (this.module) return this.module;
111
+ if (this.initPromise) return this.initPromise;
112
+ this.initPromise = (async () => {
113
+ const wasm = await import("./icon_craft_wasm-JDPURZOP.js");
114
+ await wasm.default();
115
+ this.module = wasm;
116
+ return wasm;
117
+ })();
118
+ return this.initPromise;
119
+ }
120
+ /**
121
+ * 初期化済みかどうか
122
+ */
123
+ get isReady() {
124
+ return this.module !== null;
125
+ }
126
+ /**
127
+ * アイコンを生成(キャッシュ付き)
128
+ */
129
+ async generate(params) {
130
+ const cacheKey = createCacheKey(params);
131
+ const cached = this.cache.get(cacheKey);
132
+ if (cached) {
133
+ return cached;
134
+ }
135
+ const wasm = await this.init();
136
+ const result = wasm.generate_clippath_with_color(
137
+ params.svgContent,
138
+ shapeModeMap[params.mode],
139
+ params.offset,
140
+ params.resolution,
141
+ params.simplify,
142
+ params.includeIcon,
143
+ params.color
144
+ );
145
+ if (result.success) {
146
+ this.addToCache(cacheKey, result);
147
+ }
148
+ return result;
149
+ }
150
+ /**
151
+ * キャッシュに追加(LRU)
152
+ */
153
+ addToCache(key, result) {
154
+ if (this.cache.size >= this.maxCacheSize) {
155
+ const firstKey = this.cache.keys().next().value;
156
+ if (firstKey) {
157
+ this.cache.delete(firstKey);
158
+ }
159
+ }
160
+ this.cache.set(key, result);
161
+ }
162
+ /**
163
+ * キャッシュをクリア
164
+ */
165
+ clearCache() {
166
+ this.cache.clear();
167
+ }
168
+ /**
169
+ * キャッシュサイズを設定
170
+ */
171
+ setMaxCacheSize(size) {
172
+ this.maxCacheSize = size;
173
+ }
174
+ /**
175
+ * 現在のキャッシュ数
176
+ */
177
+ get cacheSize() {
178
+ return this.cache.size;
179
+ }
180
+ };
181
+ var WasmManager = new WasmManagerClass();
182
+
183
+ // src/core/IconCraftRegistry.ts
184
+ import { ulid, decodeTime } from "ulid";
185
+ function generateIconId() {
186
+ return `ic_${ulid()}`;
187
+ }
188
+ function getTimestampFromId(id) {
189
+ const ulidPart = id.replace(/^ic_/, "");
190
+ try {
191
+ return new Date(decodeTime(ulidPart));
192
+ } catch {
193
+ return null;
194
+ }
195
+ }
196
+ var IconCraftRegistry = class {
197
+ constructor() {
198
+ this.byId = /* @__PURE__ */ new Map();
199
+ this.byMode = /* @__PURE__ */ new Map();
200
+ this.byColor = /* @__PURE__ */ new Map();
201
+ }
202
+ /**
203
+ * インスタンスを登録
204
+ */
205
+ register(instance) {
206
+ const id = instance.id;
207
+ const mode = instance.config.mode;
208
+ const color = instance.config.color;
209
+ this.byId.set(id, instance);
210
+ if (!this.byMode.has(mode)) {
211
+ this.byMode.set(mode, /* @__PURE__ */ new Set());
212
+ }
213
+ this.byMode.get(mode).add(id);
214
+ if (!this.byColor.has(color)) {
215
+ this.byColor.set(color, /* @__PURE__ */ new Set());
216
+ }
217
+ this.byColor.get(color).add(id);
218
+ }
219
+ /**
220
+ * インスタンスを削除
221
+ */
222
+ unregister(id) {
223
+ const instance = this.byId.get(id);
224
+ if (!instance) return false;
225
+ const mode = instance.config.mode;
226
+ const color = instance.config.color;
227
+ this.byId.delete(id);
228
+ this.byMode.get(mode)?.delete(id);
229
+ if (this.byMode.get(mode)?.size === 0) {
230
+ this.byMode.delete(mode);
231
+ }
232
+ this.byColor.get(color)?.delete(id);
233
+ if (this.byColor.get(color)?.size === 0) {
234
+ this.byColor.delete(color);
235
+ }
236
+ return true;
237
+ }
238
+ /**
239
+ * IDで取得
240
+ */
241
+ get(id) {
242
+ return this.byId.get(id);
243
+ }
244
+ /**
245
+ * 全インスタンスを取得
246
+ */
247
+ getAll() {
248
+ return Array.from(this.byId.values());
249
+ }
250
+ /**
251
+ * モードで検索
252
+ */
253
+ findByMode(mode) {
254
+ const ids = this.byMode.get(mode);
255
+ if (!ids) return [];
256
+ return Array.from(ids).map((id) => this.byId.get(id)).filter((inst) => inst !== void 0);
257
+ }
258
+ /**
259
+ * 色で検索
260
+ */
261
+ findByColor(color) {
262
+ const ids = this.byColor.get(color);
263
+ if (!ids) return [];
264
+ return Array.from(ids).map((id) => this.byId.get(id)).filter((inst) => inst !== void 0);
265
+ }
266
+ /**
267
+ * 時間範囲で検索(ULIDのタイムスタンプを利用)
268
+ */
269
+ findByTimeRange(start, end) {
270
+ const startTime = start.getTime();
271
+ const endTime = end.getTime();
272
+ return Array.from(this.byId.entries()).filter(([id]) => {
273
+ const timestamp = getTimestampFromId(id);
274
+ if (!timestamp) return false;
275
+ const time = timestamp.getTime();
276
+ return time >= startTime && time <= endTime;
277
+ }).map(([, instance]) => instance);
278
+ }
279
+ /**
280
+ * 作成順でソート(ULIDは辞書順でソート可能)
281
+ */
282
+ getAllSorted(order = "asc") {
283
+ const entries = Array.from(this.byId.entries());
284
+ entries.sort((a, b) => {
285
+ const cmp = a[0].localeCompare(b[0]);
286
+ return order === "asc" ? cmp : -cmp;
287
+ });
288
+ return entries.map(([, instance]) => instance);
289
+ }
290
+ /**
291
+ * 登録数
292
+ */
293
+ get size() {
294
+ return this.byId.size;
295
+ }
296
+ /**
297
+ * すべてクリア
298
+ */
299
+ clear() {
300
+ this.byId.clear();
301
+ this.byMode.clear();
302
+ this.byColor.clear();
303
+ }
304
+ /**
305
+ * インデックスの統計情報
306
+ */
307
+ getStats() {
308
+ const byModeStats = {};
309
+ for (const [mode, ids] of this.byMode) {
310
+ byModeStats[mode] = ids.size;
311
+ }
312
+ const byColorStats = {};
313
+ for (const [color, ids] of this.byColor) {
314
+ byColorStats[color] = ids.size;
315
+ }
316
+ return {
317
+ total: this.byId.size,
318
+ byMode: byModeStats,
319
+ byColor: byColorStats
320
+ };
321
+ }
322
+ };
323
+ var globalRegistry = new IconCraftRegistry();
324
+
325
+ // src/core/IconCraftInstance.ts
326
+ var IconCraftInstance = class _IconCraftInstance {
327
+ constructor(svg, config, id) {
328
+ this._svgState = { status: "pending" };
329
+ this._generateState = { status: "idle" };
330
+ this._generatePromise = null;
331
+ this._id = id ?? generateIconId();
332
+ this._svg = svg;
333
+ this._config = config;
334
+ }
335
+ // ============================================
336
+ // Getters
337
+ // ============================================
338
+ get id() {
339
+ return this._id;
340
+ }
341
+ get svg() {
342
+ return this._svg;
343
+ }
344
+ get config() {
345
+ return this._config;
346
+ }
347
+ get isUrl() {
348
+ return this._svg.startsWith("http://") || this._svg.startsWith("https://") || this._svg.startsWith("/");
349
+ }
350
+ get svgContent() {
351
+ return this._svgState.status === "ready" ? this._svgState.content : null;
352
+ }
353
+ get result() {
354
+ return this._generateState.status === "done" ? this._generateState.result : null;
355
+ }
356
+ get embossSvg() {
357
+ return this.result?.emboss_svg ?? null;
358
+ }
359
+ get isLoading() {
360
+ return this._svgState.status === "loading" || this._generateState.status === "generating";
361
+ }
362
+ get isReady() {
363
+ return this._generateState.status === "done";
364
+ }
365
+ get error() {
366
+ if (this._svgState.status === "error") return this._svgState.error;
367
+ if (this._generateState.status === "error") return this._generateState.error;
368
+ return null;
369
+ }
370
+ // ============================================
371
+ // Methods
372
+ // ============================================
373
+ /**
374
+ * SVGコンテンツを取得
375
+ */
376
+ async fetchSvg() {
377
+ if (this._svgState.status === "ready") {
378
+ return this._svgState.content;
379
+ }
380
+ if (!this.isUrl) {
381
+ this._svgState = { status: "ready", content: this._svg };
382
+ return this._svg;
383
+ }
384
+ this._svgState = { status: "loading" };
385
+ try {
386
+ const response = await fetch(this._svg);
387
+ if (!response.ok) {
388
+ throw new Error(`Failed to fetch SVG: ${response.status}`);
389
+ }
390
+ const content = await response.text();
391
+ this._svgState = { status: "ready", content };
392
+ return content;
393
+ } catch (err) {
394
+ const error = err instanceof Error ? err.message : "Unknown error";
395
+ this._svgState = { status: "error", error };
396
+ throw err;
397
+ }
398
+ }
399
+ /**
400
+ * アイコンを生成
401
+ */
402
+ async generate() {
403
+ if (this._generatePromise) {
404
+ return this._generatePromise;
405
+ }
406
+ if (this._generateState.status === "done") {
407
+ return this._generateState.result;
408
+ }
409
+ this._generatePromise = this._doGenerate();
410
+ return this._generatePromise;
411
+ }
412
+ async _doGenerate() {
413
+ this._generateState = { status: "generating" };
414
+ try {
415
+ const svgContent = await this.fetchSvg();
416
+ const params = this._config.getWasmParams();
417
+ console.log("[IconCraftInstance] Generating with params:", {
418
+ id: this._id,
419
+ mode: params.mode,
420
+ color: params.color
421
+ });
422
+ const result = await WasmManager.generate({
423
+ svgContent,
424
+ mode: params.mode,
425
+ offset: params.offset,
426
+ resolution: params.resolution,
427
+ simplify: params.simplify,
428
+ includeIcon: params.includeIcon,
429
+ color: params.color
430
+ });
431
+ if (!result.success) {
432
+ throw new Error(result.error || "Generation failed");
433
+ }
434
+ console.log("[IconCraftInstance] Generated result:", {
435
+ id: this._id,
436
+ success: result.success,
437
+ embossSvgPreview: result.emboss_svg?.slice(0, 300)
438
+ });
439
+ this._generateState = { status: "done", result };
440
+ return result;
441
+ } catch (err) {
442
+ const error = err instanceof Error ? err.message : "Unknown error";
443
+ this._generateState = { status: "error", error };
444
+ throw err;
445
+ } finally {
446
+ this._generatePromise = null;
447
+ }
448
+ }
449
+ /**
450
+ * 新しい設定でクローン
451
+ */
452
+ clone(overrides) {
453
+ const newConfig = overrides ? this._config.clone(overrides) : this._config;
454
+ return new _IconCraftInstance(this._svg, newConfig);
455
+ }
456
+ /**
457
+ * 別のSVGで新しいインスタンスを作成
458
+ */
459
+ withSvg(svg) {
460
+ return new _IconCraftInstance(svg, this._config);
461
+ }
462
+ /**
463
+ * 状態をリセット
464
+ */
465
+ reset() {
466
+ this._svgState = { status: "pending" };
467
+ this._generateState = { status: "idle" };
468
+ this._generatePromise = null;
469
+ }
470
+ };
471
+
472
+ // src/core/IconCraftFactory.ts
473
+ var IconCraftFactory = class _IconCraftFactory {
474
+ constructor(options = {}) {
475
+ this.prototype = new IconCraftConfig(options);
476
+ }
477
+ /**
478
+ * 新しいインスタンスを生成
479
+ *
480
+ * @param svg - SVGコンテンツまたはURL
481
+ * @param overrides - このインスタンス固有の設定(オプション)
482
+ */
483
+ create(svg, overrides) {
484
+ const config = overrides ? this.prototype.clone(overrides) : this.prototype;
485
+ return new IconCraftInstance(svg, config);
486
+ }
487
+ /**
488
+ * プロトタイプ設定を取得
489
+ */
490
+ getConfig() {
491
+ return this.prototype;
492
+ }
493
+ /**
494
+ * 新しい設定でFactoryを複製
495
+ */
496
+ clone(overrides) {
497
+ const newConfig = this.prototype.clone(overrides);
498
+ return new _IconCraftFactory({
499
+ mode: newConfig.mode,
500
+ color: newConfig.color,
501
+ iconStyle: newConfig.iconStyle,
502
+ shadow: newConfig.shadow,
503
+ highlight: newConfig.highlight,
504
+ offset: newConfig.offset,
505
+ resolution: newConfig.resolution,
506
+ simplify: newConfig.simplify,
507
+ size: newConfig.size,
508
+ width: newConfig.width,
509
+ height: newConfig.height,
510
+ animation: newConfig.animation,
511
+ animateOnHover: newConfig.animateOnHover
512
+ });
513
+ }
514
+ /**
515
+ * 複数のSVGから一括でインスタンスを生成
516
+ */
517
+ createMany(svgs, overrides) {
518
+ return svgs.map((svg) => this.create(svg, overrides));
519
+ }
520
+ /**
521
+ * 複数のSVGを一括生成
522
+ */
523
+ async generateMany(svgs, overrides) {
524
+ const instances = this.createMany(svgs, overrides);
525
+ await Promise.all(instances.map((inst) => inst.generate()));
526
+ return instances;
527
+ }
528
+ };
529
+ var defaultFactory = new IconCraftFactory();
530
+
531
+ // src/components/IconCraftView.tsx
532
+ import { useEffect, useState, useMemo } from "react";
533
+
534
+ // src/animations.ts
535
+ var customAnimationRegistry = /* @__PURE__ */ new Map();
536
+ function registerAnimation(name, definition) {
537
+ customAnimationRegistry.set(name, definition);
538
+ }
539
+ function getCustomAnimation(name) {
540
+ return customAnimationRegistry.get(name);
541
+ }
542
+ function getTransformOrigin(type) {
543
+ const custom = customAnimationRegistry.get(type);
544
+ return custom?.transformOrigin ?? "center";
545
+ }
546
+ var builtInKeyframes = {
547
+ none: "",
548
+ shake: `
549
+ @keyframes iconcraft-shake {
550
+ 0%, 100% { transform: translateX(0); }
551
+ 10%, 30%, 50%, 70%, 90% { transform: translateX(-4px); }
552
+ 20%, 40%, 60%, 80% { transform: translateX(4px); }
553
+ }
554
+ `,
555
+ bounce: `
556
+ @keyframes iconcraft-bounce {
557
+ 0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
558
+ 40% { transform: translateY(-15px); }
559
+ 60% { transform: translateY(-8px); }
560
+ }
561
+ `,
562
+ pulse: `
563
+ @keyframes iconcraft-pulse {
564
+ 0%, 100% { transform: scale(1); }
565
+ 50% { transform: scale(1.08); }
566
+ }
567
+ `,
568
+ swing: `
569
+ @keyframes iconcraft-swing {
570
+ 0%, 100% { transform: rotate(0deg); transform-origin: top center; }
571
+ 20% { transform: rotate(12deg); }
572
+ 40% { transform: rotate(-8deg); }
573
+ 60% { transform: rotate(4deg); }
574
+ 80% { transform: rotate(-4deg); }
575
+ }
576
+ `,
577
+ wobble: `
578
+ @keyframes iconcraft-wobble {
579
+ 0%, 100% { transform: translateX(0) rotate(0deg); }
580
+ 15% { transform: translateX(-8px) rotate(-5deg); }
581
+ 30% { transform: translateX(6px) rotate(3deg); }
582
+ 45% { transform: translateX(-5px) rotate(-3deg); }
583
+ 60% { transform: translateX(3px) rotate(2deg); }
584
+ 75% { transform: translateX(-2px) rotate(-1deg); }
585
+ }
586
+ `,
587
+ jello: `
588
+ @keyframes iconcraft-jello {
589
+ 0%, 100% { transform: scale3d(1, 1, 1); }
590
+ 30% { transform: scale3d(1.15, 0.85, 1); }
591
+ 40% { transform: scale3d(0.85, 1.15, 1); }
592
+ 50% { transform: scale3d(1.08, 0.92, 1); }
593
+ 65% { transform: scale3d(0.95, 1.05, 1); }
594
+ 75% { transform: scale3d(1.03, 0.97, 1); }
595
+ }
596
+ `,
597
+ heartbeat: `
598
+ @keyframes iconcraft-heartbeat {
599
+ 0%, 100% { transform: scale(1); }
600
+ 14% { transform: scale(1.15); }
601
+ 28% { transform: scale(1); }
602
+ 42% { transform: scale(1.15); }
603
+ 70% { transform: scale(1); }
604
+ }
605
+ `,
606
+ float: `
607
+ @keyframes iconcraft-float {
608
+ 0%, 100% { transform: translateY(0); }
609
+ 50% { transform: translateY(-8px); }
610
+ }
611
+ `,
612
+ spin: `
613
+ @keyframes iconcraft-spin {
614
+ 0% { transform: rotate(0deg); }
615
+ 100% { transform: rotate(360deg); }
616
+ }
617
+ `,
618
+ rubberBand: `
619
+ @keyframes iconcraft-rubberBand {
620
+ 0%, 100% { transform: scale3d(1, 1, 1); }
621
+ 30% { transform: scale3d(1.2, 0.8, 1); }
622
+ 40% { transform: scale3d(0.8, 1.2, 1); }
623
+ 50% { transform: scale3d(1.1, 0.9, 1); }
624
+ 65% { transform: scale3d(0.95, 1.05, 1); }
625
+ 75% { transform: scale3d(1.02, 0.98, 1); }
626
+ }
627
+ `,
628
+ // === New fun animations ===
629
+ squish: `
630
+ @keyframes iconcraft-squish {
631
+ 0%, 100% { transform: scale(1, 1); }
632
+ 25% { transform: scale(1.2, 0.8); }
633
+ 50% { transform: scale(0.9, 1.1); }
634
+ 75% { transform: scale(1.05, 0.95); }
635
+ }
636
+ `,
637
+ tada: `
638
+ @keyframes iconcraft-tada {
639
+ 0% { transform: scale(1) rotate(0deg); }
640
+ 10%, 20% { transform: scale(0.9) rotate(-3deg); }
641
+ 30%, 50%, 70%, 90% { transform: scale(1.1) rotate(3deg); }
642
+ 40%, 60%, 80% { transform: scale(1.1) rotate(-3deg); }
643
+ 100% { transform: scale(1) rotate(0deg); }
644
+ }
645
+ `,
646
+ flip: `
647
+ @keyframes iconcraft-flip {
648
+ 0% { transform: perspective(400px) rotateY(0deg); }
649
+ 40% { transform: perspective(400px) rotateY(-180deg); }
650
+ 100% { transform: perspective(400px) rotateY(-360deg); }
651
+ }
652
+ `,
653
+ drop: `
654
+ @keyframes iconcraft-drop {
655
+ 0% { transform: translateY(-30px) scale(1, 1); opacity: 0; }
656
+ 50% { transform: translateY(0) scale(1.15, 0.85); opacity: 1; }
657
+ 65% { transform: translateY(-8px) scale(0.95, 1.05); }
658
+ 80% { transform: translateY(0) scale(1.03, 0.97); }
659
+ 100% { transform: translateY(0) scale(1, 1); }
660
+ }
661
+ `,
662
+ pop: `
663
+ @keyframes iconcraft-pop {
664
+ 0% { transform: scale(0); opacity: 0; }
665
+ 50% { transform: scale(1.2); }
666
+ 70% { transform: scale(0.9); }
667
+ 100% { transform: scale(1); opacity: 1; }
668
+ }
669
+ `,
670
+ wiggle: `
671
+ @keyframes iconcraft-wiggle {
672
+ 0%, 100% { transform: rotate(0deg); }
673
+ 25% { transform: rotate(-5deg); }
674
+ 75% { transform: rotate(5deg); }
675
+ }
676
+ `,
677
+ breathe: `
678
+ @keyframes iconcraft-breathe {
679
+ 0%, 100% { transform: scale(1); opacity: 1; }
680
+ 50% { transform: scale(1.05); opacity: 0.9; }
681
+ }
682
+ `
683
+ };
684
+ var builtInAnimationDefaults = {
685
+ none: {},
686
+ shake: { duration: 0.6, iterationCount: 1, timingFunction: "ease" },
687
+ bounce: { duration: 1, iterationCount: "infinite", timingFunction: "ease" },
688
+ pulse: { duration: 1.5, iterationCount: "infinite", timingFunction: "ease-in-out" },
689
+ swing: { duration: 1, iterationCount: 1, timingFunction: "ease-in-out" },
690
+ wobble: { duration: 1, iterationCount: 1, timingFunction: "ease-in-out" },
691
+ jello: { duration: 0.9, iterationCount: 1, timingFunction: "ease" },
692
+ heartbeat: { duration: 1.3, iterationCount: "infinite", timingFunction: "ease-in-out" },
693
+ float: { duration: 2, iterationCount: "infinite", timingFunction: "ease-in-out" },
694
+ spin: { duration: 1.5, iterationCount: "infinite", timingFunction: "linear" },
695
+ rubberBand: { duration: 1, iterationCount: 1, timingFunction: "ease" },
696
+ // New fun animations
697
+ squish: { duration: 0.6, iterationCount: "infinite", timingFunction: "ease-in-out" },
698
+ tada: { duration: 1, iterationCount: 1, timingFunction: "ease" },
699
+ flip: { duration: 1.2, iterationCount: 1, timingFunction: "ease-in-out" },
700
+ drop: { duration: 0.8, iterationCount: 1, timingFunction: "ease-out" },
701
+ pop: { duration: 0.5, iterationCount: 1, timingFunction: "ease-out" },
702
+ wiggle: { duration: 0.5, iterationCount: "infinite", timingFunction: "ease-in-out" },
703
+ breathe: { duration: 3, iterationCount: "infinite", timingFunction: "ease-in-out" }
704
+ };
705
+ function getAnimationDefaults(type) {
706
+ if (type in builtInAnimationDefaults) {
707
+ return builtInAnimationDefaults[type];
708
+ }
709
+ const custom = customAnimationRegistry.get(type);
710
+ if (custom?.defaults) {
711
+ return custom.defaults;
712
+ }
713
+ return { duration: 1, iterationCount: 1, timingFunction: "ease" };
714
+ }
715
+ var animationDefaults = builtInAnimationDefaults;
716
+ function getAnimationName(type) {
717
+ if (type === "none") return "none";
718
+ return `iconcraft-${type}`;
719
+ }
720
+ function parseAnimationOptions(animation) {
721
+ if (!animation || animation === "none") return null;
722
+ if (typeof animation === "string") {
723
+ return {
724
+ type: animation,
725
+ ...getAnimationDefaults(animation)
726
+ };
727
+ }
728
+ return {
729
+ ...getAnimationDefaults(animation.type),
730
+ ...animation
731
+ };
732
+ }
733
+ function getAnimationStyle(options) {
734
+ if (!options || options.type === "none") return "none";
735
+ const name = getAnimationName(options.type);
736
+ const duration = options.duration ?? 1;
737
+ const timing = options.timingFunction ?? "ease";
738
+ const delay = options.delay ?? 0;
739
+ const iterations = options.iterationCount ?? 1;
740
+ return `${name} ${duration}s ${timing} ${delay}s ${iterations}`;
741
+ }
742
+ function getKeyframes(type) {
743
+ if (type in builtInKeyframes) {
744
+ return builtInKeyframes[type];
745
+ }
746
+ const custom = customAnimationRegistry.get(type);
747
+ if (custom) {
748
+ return custom.keyframes;
749
+ }
750
+ return "";
751
+ }
752
+ var keyframes = builtInKeyframes;
753
+ var injectedKeyframes = /* @__PURE__ */ new Set();
754
+ function injectKeyframes(type) {
755
+ if (type === "none" || injectedKeyframes.has(type)) return;
756
+ if (typeof document === "undefined") return;
757
+ const keyframesCss = getKeyframes(type);
758
+ if (!keyframesCss) return;
759
+ const style = document.createElement("style");
760
+ style.textContent = keyframesCss;
761
+ document.head.appendChild(style);
762
+ injectedKeyframes.add(type);
763
+ }
764
+
765
+ // src/components/IconCraftView.tsx
766
+ import { jsx, jsxs } from "react/jsx-runtime";
767
+ function formatCoordinate(value) {
768
+ if (typeof value === "number") {
769
+ return value === 0 ? "0" : `${value}px`;
770
+ }
771
+ return value;
772
+ }
773
+ function resolveTransformOrigin(value) {
774
+ if (typeof value === "object" && value !== null) {
775
+ const x = formatCoordinate(value.x);
776
+ const y = formatCoordinate(value.y);
777
+ return `${x} ${y}`;
778
+ }
779
+ switch (value) {
780
+ case "center":
781
+ case "icon":
782
+ return "center center";
783
+ case "top":
784
+ return "50% 0%";
785
+ case "bottom":
786
+ return "50% 100%";
787
+ case "left":
788
+ return "0% 50%";
789
+ case "right":
790
+ return "100% 50%";
791
+ case "top-left":
792
+ return "0% 0%";
793
+ case "top-right":
794
+ return "100% 0%";
795
+ case "bottom-left":
796
+ return "0% 100%";
797
+ case "bottom-right":
798
+ return "100% 100%";
799
+ default:
800
+ return "center center";
801
+ }
802
+ }
803
+ function IconCraftView({
804
+ instance,
805
+ animation,
806
+ animationTarget,
807
+ animateOnHover = false,
808
+ zIndex,
809
+ className,
810
+ style,
811
+ onClick,
812
+ onLoad,
813
+ onError
814
+ }) {
815
+ const [, forceUpdate] = useState({});
816
+ const [isHovering, setIsHovering] = useState(false);
817
+ const animationOptions = useMemo(() => {
818
+ const anim = animation ?? instance.config.animation;
819
+ return parseAnimationOptions(anim);
820
+ }, [animation, instance.config.animation]);
821
+ useEffect(() => {
822
+ if (animationOptions?.type) {
823
+ injectKeyframes(animationOptions.type);
824
+ }
825
+ }, [animationOptions?.type]);
826
+ useEffect(() => {
827
+ if (instance.isReady) {
828
+ onLoad?.();
829
+ return;
830
+ }
831
+ instance.generate().then(() => {
832
+ forceUpdate({});
833
+ onLoad?.();
834
+ }).catch((err) => {
835
+ forceUpdate({});
836
+ onError?.(err.message);
837
+ });
838
+ }, [instance, onLoad, onError]);
839
+ const renderedSvg = useMemo(() => {
840
+ const instanceId = instance.id;
841
+ const iconStyle = instance.config.iconStyle;
842
+ const result = instance.result;
843
+ const mode = instance.config.mode;
844
+ const isWax = mode === "wax";
845
+ if (isWax && instance.embossSvg) {
846
+ let svg2 = instance.embossSvg;
847
+ const modes = ["wax", "jelly", "droplet"];
848
+ for (const m of modes) {
849
+ svg2 = svg2.replace(
850
+ new RegExp(`id="${m}-`, "g"),
851
+ `id="${instanceId}-${m}-`
852
+ );
853
+ svg2 = svg2.replace(
854
+ new RegExp(`url\\(#${m}-`, "g"),
855
+ `url(#${instanceId}-${m}-`
856
+ );
857
+ }
858
+ if (iconStyle !== "emboss") {
859
+ const svgContent2 = instance.svgContent;
860
+ if (svgContent2) {
861
+ const viewBoxMatch2 = svgContent2.match(/viewBox="([^"]*)"/);
862
+ const viewBox2 = viewBoxMatch2 ? viewBoxMatch2[1] : "0 0 36 36";
863
+ let innerSvg2 = svgContent2.replace(/<\/?svg[^>]*>/g, "");
864
+ let iconFill2;
865
+ let useOriginalColors2 = false;
866
+ switch (iconStyle) {
867
+ case "white":
868
+ iconFill2 = "#ffffff";
869
+ innerSvg2 = innerSvg2.replace(/fill="[^"]*"/g, "");
870
+ break;
871
+ case "dark":
872
+ iconFill2 = "#1d1d1f";
873
+ innerSvg2 = innerSvg2.replace(/fill="[^"]*"/g, "");
874
+ break;
875
+ case "original":
876
+ default:
877
+ iconFill2 = "currentColor";
878
+ useOriginalColors2 = true;
879
+ break;
880
+ }
881
+ const layout2 = result?.icon_layout;
882
+ const newIconSvg = `<g filter="none">
883
+ <svg x="${layout2?.left_percent ?? 28}" y="${layout2?.top_percent ?? 28}" width="${layout2?.width_percent ?? 44}" height="${layout2?.height_percent ?? 44}" viewBox="${viewBox2}" overflow="visible">
884
+ ${useOriginalColors2 ? innerSvg2 : `<g fill="${iconFill2}">${innerSvg2}</g>`}
885
+ </svg>
886
+ </g>`;
887
+ svg2 = svg2.replace(/<g filter="none">[\s\S]*?<\/g>\s*<\/svg>$/, `${newIconSvg}
888
+ </svg>`);
889
+ }
890
+ }
891
+ return svg2;
892
+ }
893
+ if (iconStyle === "emboss" && instance.embossSvg) {
894
+ let svg2 = instance.embossSvg;
895
+ const modes = ["wax", "jelly", "droplet"];
896
+ for (const m of modes) {
897
+ svg2 = svg2.replace(
898
+ new RegExp(`id="${m}-`, "g"),
899
+ `id="${instanceId}-${m}-`
900
+ );
901
+ svg2 = svg2.replace(
902
+ new RegExp(`url\\(#${m}-`, "g"),
903
+ `url(#${instanceId}-${m}-`
904
+ );
905
+ }
906
+ return svg2;
907
+ }
908
+ if (!result || !result.svg_paths?.clip) return "";
909
+ const svgContent = instance.svgContent;
910
+ if (!svgContent) return "";
911
+ const color = instance.config.color;
912
+ const layout = result.icon_layout;
913
+ let iconFill;
914
+ let useOriginalColors = false;
915
+ switch (iconStyle) {
916
+ case "white":
917
+ iconFill = "#ffffff";
918
+ break;
919
+ case "dark":
920
+ iconFill = "#1d1d1f";
921
+ break;
922
+ case "original":
923
+ default:
924
+ iconFill = "currentColor";
925
+ useOriginalColors = true;
926
+ break;
927
+ }
928
+ const clipPath = result.svg_paths.clip;
929
+ const highlightPath = result.svg_paths.highlight;
930
+ const isJellyOrDroplet = mode === "jelly" || mode === "droplet";
931
+ const gradientId = `${instanceId}-bg-grad`;
932
+ const clipId = `${instanceId}-clip`;
933
+ const viewBoxMatch = svgContent.match(/viewBox="([^"]*)"/);
934
+ const viewBox = viewBoxMatch ? viewBoxMatch[1] : "0 0 24 24";
935
+ let innerSvg = svgContent.replace(/<\/?svg[^>]*>/g, "");
936
+ if (iconStyle === "white" || iconStyle === "dark") {
937
+ innerSvg = innerSvg.replace(/fill="[^"]*"/g, "");
938
+ }
939
+ const bgGradient = isJellyOrDroplet ? `<linearGradient id="${gradientId}" x1="0%" y1="0%" x2="100%" y2="100%">
940
+ <stop offset="0%" stop-color="${color}" stop-opacity="0.35"/>
941
+ <stop offset="50%" stop-color="${color}" stop-opacity="0.4"/>
942
+ <stop offset="100%" stop-color="${color}" stop-opacity="0.5"/>
943
+ </linearGradient>
944
+ <linearGradient id="${instanceId}-top-highlight" x1="50%" y1="0%" x2="50%" y2="100%">
945
+ <stop offset="0%" stop-color="#fff" stop-opacity="0.6"/>
946
+ <stop offset="30%" stop-color="#fff" stop-opacity="0.3"/>
947
+ <stop offset="60%" stop-color="#fff" stop-opacity="0.1"/>
948
+ <stop offset="100%" stop-color="#fff" stop-opacity="0"/>
949
+ </linearGradient>
950
+ <linearGradient id="${instanceId}-bottom-shadow" x1="50%" y1="0%" x2="50%" y2="100%">
951
+ <stop offset="0%" stop-color="#000" stop-opacity="0"/>
952
+ <stop offset="40%" stop-color="#000" stop-opacity="0.05"/>
953
+ <stop offset="70%" stop-color="#000" stop-opacity="0.15"/>
954
+ <stop offset="100%" stop-color="#000" stop-opacity="0.3"/>
955
+ </linearGradient>
956
+ <linearGradient id="${instanceId}-edge-highlight" x1="0%" y1="0%" x2="100%" y2="100%">
957
+ <stop offset="0%" stop-color="#fff" stop-opacity="0.85"/>
958
+ <stop offset="50%" stop-color="#fff" stop-opacity="0.5"/>
959
+ <stop offset="100%" stop-color="#fff" stop-opacity="0"/>
960
+ </linearGradient>` : `<linearGradient id="${gradientId}" x1="0%" y1="0%" x2="100%" y2="100%">
961
+ <stop offset="0%" stop-color="${color}"/>
962
+ <stop offset="100%" stop-color="${color}"/>
963
+ </linearGradient>
964
+ <linearGradient id="${instanceId}-wax-top-highlight" x1="50%" y1="0%" x2="50%" y2="100%">
965
+ <stop offset="0%" stop-color="#fff" stop-opacity="0.4"/>
966
+ <stop offset="40%" stop-color="#fff" stop-opacity="0.15"/>
967
+ <stop offset="100%" stop-color="#fff" stop-opacity="0"/>
968
+ </linearGradient>
969
+ <linearGradient id="${instanceId}-wax-bottom-shadow" x1="50%" y1="0%" x2="50%" y2="100%">
970
+ <stop offset="0%" stop-color="#000" stop-opacity="0"/>
971
+ <stop offset="60%" stop-color="#000" stop-opacity="0.1"/>
972
+ <stop offset="100%" stop-color="#000" stop-opacity="0.25"/>
973
+ </linearGradient>`;
974
+ const shapeContent = isJellyOrDroplet ? `<path d="${clipPath}" fill="url(#${gradientId})"/>
975
+ <path d="${clipPath}" fill="url(#${instanceId}-top-highlight)"/>
976
+ <path d="${clipPath}" fill="url(#${instanceId}-bottom-shadow)"/>
977
+ ${highlightPath ? `<path d="${highlightPath}" fill="url(#${instanceId}-edge-highlight)"/>` : ""}` : `<path d="${clipPath}" fill="url(#${gradientId})"/>
978
+ <path d="${clipPath}" fill="url(#${instanceId}-wax-top-highlight)"/>
979
+ <path d="${clipPath}" fill="url(#${instanceId}-wax-bottom-shadow)"/>`;
980
+ const svg = `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
981
+ <defs>
982
+ ${bgGradient}
983
+ <clipPath id="${clipId}">
984
+ <path d="${clipPath}"/>
985
+ </clipPath>
986
+ <filter id="${instanceId}-shadow" x="-50%" y="-50%" width="200%" height="200%">
987
+ <feDropShadow dx="2" dy="6" stdDeviation="8" flood-opacity="0.25"/>
988
+ </filter>
989
+ </defs>
990
+ <!-- \u30B7\u30A7\u30A4\u30D7\u80CC\u666F -->
991
+ <g filter="url(#${instanceId}-shadow)">
992
+ ${shapeContent}
993
+ </g>
994
+ <!-- \u30A2\u30A4\u30B3\u30F3\uFF08\u30B7\u30A7\u30A4\u30D7\u306E\u4E0A\u306B\u91CD\u306D\u308B\uFF09 -->
995
+ <svg x="${layout?.left_percent ?? 28}" y="${layout?.top_percent ?? 28}"
996
+ width="${layout?.width_percent ?? 44}" height="${layout?.height_percent ?? 44}"
997
+ viewBox="${viewBox}" overflow="visible">
998
+ ${useOriginalColors ? innerSvg : `<g fill="${iconFill}">${innerSvg}</g>`}
999
+ </svg>
1000
+ </svg>`;
1001
+ return svg;
1002
+ }, [instance.id, instance.config.mode, instance.config.iconStyle, instance.config.color, instance.embossSvg, instance.result, instance.svgContent]);
1003
+ const { width, height } = instance.config.getSize();
1004
+ const hoverAnim = animateOnHover ?? instance.config.animateOnHover;
1005
+ const shouldAnimate = hoverAnim ? isHovering : true;
1006
+ const animStyle = shouldAnimate ? getAnimationStyle(animationOptions) : "none";
1007
+ const target = animationTarget ?? animationOptions?.target ?? "both";
1008
+ const containerStyle = {
1009
+ width,
1010
+ height,
1011
+ display: "inline-block",
1012
+ position: "relative",
1013
+ cursor: onClick ? "pointer" : void 0,
1014
+ animation: target === "both" ? animStyle : void 0,
1015
+ zIndex,
1016
+ ...style
1017
+ };
1018
+ const cssVars = {};
1019
+ if (target === "shape") {
1020
+ cssVars["--iconcraft-shape-animation"] = animStyle;
1021
+ cssVars["--iconcraft-icon-animation"] = "none";
1022
+ } else if (target === "icon") {
1023
+ cssVars["--iconcraft-shape-animation"] = "none";
1024
+ cssVars["--iconcraft-icon-animation"] = animStyle;
1025
+ } else {
1026
+ cssVars["--iconcraft-shape-animation"] = "none";
1027
+ cssVars["--iconcraft-icon-animation"] = "none";
1028
+ }
1029
+ const transformOriginValue = animationOptions?.type ? getTransformOrigin(animationOptions.type) : "center";
1030
+ const transformOriginCss = resolveTransformOrigin(transformOriginValue);
1031
+ const animationStyles = target !== "both" ? `
1032
+ <style>
1033
+ .iconcraft-shape {
1034
+ animation: var(--iconcraft-shape-animation, none);
1035
+ transform-origin: ${transformOriginCss};
1036
+ transform-box: fill-box;
1037
+ }
1038
+ .iconcraft-icon-wrapper {
1039
+ animation: var(--iconcraft-icon-animation, none);
1040
+ transform-origin: ${transformOriginCss};
1041
+ transform-box: fill-box;
1042
+ }
1043
+ </style>
1044
+ ` : "";
1045
+ const processedSvg = useMemo(() => {
1046
+ if (!renderedSvg) return "";
1047
+ if (target === "both") return renderedSvg;
1048
+ let svg = renderedSvg;
1049
+ svg = svg.replace(
1050
+ /<g filter="url\(#[^"]*-shadow"\)/g,
1051
+ '$& class="iconcraft-shape"'
1052
+ );
1053
+ svg = svg.replace(
1054
+ /(<g filter="none">)\s*(<svg[^>]*>[\s\S]*?<\/svg>)\s*(<\/g>)/g,
1055
+ '$1<g class="iconcraft-icon-wrapper">$2</g>$3'
1056
+ );
1057
+ svg = svg.replace(
1058
+ /(<\/g>\s*<!-- アイコン[^>]*-->\s*)(<svg[^>]*x="[^"]*"[^>]*y="[^"]*"[^>]*>[\s\S]*?<\/svg>)/g,
1059
+ '$1<g class="iconcraft-icon-wrapper">$2</g>'
1060
+ );
1061
+ return svg;
1062
+ }, [renderedSvg, target]);
1063
+ if (instance.isLoading) {
1064
+ return /* @__PURE__ */ jsx("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx(LoadingIndicator, {}) });
1065
+ }
1066
+ if (instance.error) {
1067
+ return /* @__PURE__ */ jsx(
1068
+ "div",
1069
+ {
1070
+ className,
1071
+ style: {
1072
+ ...containerStyle,
1073
+ display: "flex",
1074
+ alignItems: "center",
1075
+ justifyContent: "center",
1076
+ background: "#fee",
1077
+ borderRadius: "8px",
1078
+ color: "#c00",
1079
+ fontSize: "11px",
1080
+ padding: "8px",
1081
+ textAlign: "center"
1082
+ },
1083
+ children: instance.error
1084
+ }
1085
+ );
1086
+ }
1087
+ if (!processedSvg) {
1088
+ return null;
1089
+ }
1090
+ return /* @__PURE__ */ jsx(
1091
+ "div",
1092
+ {
1093
+ className,
1094
+ style: { ...containerStyle, ...cssVars },
1095
+ onClick,
1096
+ onMouseEnter: hoverAnim ? () => setIsHovering(true) : void 0,
1097
+ onMouseLeave: hoverAnim ? () => setIsHovering(false) : void 0,
1098
+ dangerouslySetInnerHTML: { __html: animationStyles + processedSvg }
1099
+ }
1100
+ );
1101
+ }
1102
+ function LoadingIndicator() {
1103
+ return /* @__PURE__ */ jsx(
1104
+ "div",
1105
+ {
1106
+ style: {
1107
+ width: "100%",
1108
+ height: "100%",
1109
+ display: "flex",
1110
+ alignItems: "center",
1111
+ justifyContent: "center",
1112
+ background: "rgba(0,0,0,0.03)",
1113
+ borderRadius: "50%"
1114
+ },
1115
+ children: /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", children: [
1116
+ /* @__PURE__ */ jsx("style", { children: `@keyframes iconcraft-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }` }),
1117
+ /* @__PURE__ */ jsx(
1118
+ "circle",
1119
+ {
1120
+ cx: "12",
1121
+ cy: "12",
1122
+ r: "10",
1123
+ stroke: "#ccc",
1124
+ strokeWidth: "2",
1125
+ fill: "none",
1126
+ strokeDasharray: "31.4 31.4",
1127
+ strokeLinecap: "round",
1128
+ style: { animation: "iconcraft-spin 1s linear infinite" }
1129
+ }
1130
+ )
1131
+ ] })
1132
+ }
1133
+ );
1134
+ }
1135
+
1136
+ // src/IconCraftShape.tsx
1137
+ import { useEffect as useEffect2, useState as useState2, useMemo as useMemo2, useCallback } from "react";
1138
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1139
+ var wasmModule = null;
1140
+ var wasmInitPromise = null;
1141
+ async function initWasm() {
1142
+ if (wasmModule) return wasmModule;
1143
+ if (wasmInitPromise) return wasmInitPromise;
1144
+ wasmInitPromise = (async () => {
1145
+ const wasm = await import("./icon_craft_wasm-JDPURZOP.js");
1146
+ await wasm.default();
1147
+ wasmModule = wasm;
1148
+ return wasm;
1149
+ })();
1150
+ return wasmInitPromise;
1151
+ }
1152
+ var shapeModeMap2 = {
1153
+ jelly: 0,
1154
+ droplet: 1,
1155
+ wax: 2
1156
+ };
1157
+ function isUrl(str) {
1158
+ return str.startsWith("http://") || str.startsWith("https://") || str.startsWith("/");
1159
+ }
1160
+ async function fetchSvg(url) {
1161
+ const response = await fetch(url);
1162
+ if (!response.ok) {
1163
+ throw new Error(`Failed to fetch SVG: ${response.status}`);
1164
+ }
1165
+ return response.text();
1166
+ }
1167
+ function IconCraftShape({
1168
+ // Required
1169
+ svg,
1170
+ // Shape Settings
1171
+ mode = "wax",
1172
+ color = "#6366f1",
1173
+ // Icon Style
1174
+ iconStyle = "emboss",
1175
+ // Effects (reserved for future use)
1176
+ shadow: _shadow = true,
1177
+ highlight: _highlight = true,
1178
+ // WASM Parameters
1179
+ offset = 20,
1180
+ resolution = 256,
1181
+ simplify = 2,
1182
+ // Size & Layout
1183
+ width,
1184
+ height,
1185
+ size,
1186
+ // Animation
1187
+ animation,
1188
+ animateOnHover = false,
1189
+ // Styling
1190
+ className,
1191
+ style,
1192
+ // Events
1193
+ onLoad,
1194
+ onError,
1195
+ onClick
1196
+ }) {
1197
+ const [result, setResult] = useState2(null);
1198
+ const [isLoading, setIsLoading] = useState2(true);
1199
+ const [error, setError] = useState2(null);
1200
+ const [isHovering, setIsHovering] = useState2(false);
1201
+ const animationOptions = useMemo2(
1202
+ () => parseAnimationOptions(animation),
1203
+ [animation]
1204
+ );
1205
+ useEffect2(() => {
1206
+ if (animationOptions?.type) {
1207
+ injectKeyframes(animationOptions.type);
1208
+ }
1209
+ }, [animationOptions?.type]);
1210
+ const generate = useCallback(async () => {
1211
+ setIsLoading(true);
1212
+ setError(null);
1213
+ try {
1214
+ let svgContent = svg;
1215
+ if (isUrl(svg)) {
1216
+ svgContent = await fetchSvg(svg);
1217
+ }
1218
+ const wasm = await initWasm();
1219
+ const modeValue = shapeModeMap2[mode];
1220
+ const wasmResult = wasm.generate_clippath_with_color(
1221
+ svgContent,
1222
+ modeValue,
1223
+ offset,
1224
+ resolution,
1225
+ simplify,
1226
+ iconStyle === "emboss",
1227
+ // include_icon
1228
+ color
1229
+ );
1230
+ if (!wasmResult.success) {
1231
+ throw new Error(wasmResult.error || "Generation failed");
1232
+ }
1233
+ setResult(wasmResult);
1234
+ onLoad?.(wasmResult);
1235
+ } catch (err) {
1236
+ const message = err instanceof Error ? err.message : "Unknown error";
1237
+ setError(message);
1238
+ onError?.(message);
1239
+ } finally {
1240
+ setIsLoading(false);
1241
+ }
1242
+ }, [svg, mode, color, iconStyle, offset, resolution, simplify, onLoad, onError]);
1243
+ useEffect2(() => {
1244
+ generate();
1245
+ }, [generate]);
1246
+ const computedWidth = size ?? width ?? 120;
1247
+ const computedHeight = size ?? height ?? 120;
1248
+ const shouldAnimate = animateOnHover ? isHovering : true;
1249
+ const animationStyle = shouldAnimate ? getAnimationStyle(animationOptions) : "none";
1250
+ const containerStyle = {
1251
+ width: typeof computedWidth === "number" ? `${computedWidth}px` : computedWidth,
1252
+ height: typeof computedHeight === "number" ? `${computedHeight}px` : computedHeight,
1253
+ display: "inline-block",
1254
+ position: "relative",
1255
+ cursor: onClick ? "pointer" : void 0,
1256
+ animation: animationStyle,
1257
+ ...style
1258
+ };
1259
+ const handleMouseEnter = animateOnHover ? () => setIsHovering(true) : void 0;
1260
+ const handleMouseLeave = animateOnHover ? () => setIsHovering(false) : void 0;
1261
+ if (isLoading) {
1262
+ return /* @__PURE__ */ jsx2(
1263
+ "div",
1264
+ {
1265
+ className,
1266
+ style: {
1267
+ ...containerStyle,
1268
+ display: "flex",
1269
+ alignItems: "center",
1270
+ justifyContent: "center",
1271
+ background: "#f0f0f0",
1272
+ borderRadius: "50%"
1273
+ },
1274
+ children: /* @__PURE__ */ jsx2(LoadingSpinner, {})
1275
+ }
1276
+ );
1277
+ }
1278
+ if (error) {
1279
+ return /* @__PURE__ */ jsx2(
1280
+ "div",
1281
+ {
1282
+ className,
1283
+ style: {
1284
+ ...containerStyle,
1285
+ display: "flex",
1286
+ alignItems: "center",
1287
+ justifyContent: "center",
1288
+ background: "#fee",
1289
+ borderRadius: "8px",
1290
+ color: "#c00",
1291
+ fontSize: "12px",
1292
+ padding: "8px",
1293
+ textAlign: "center"
1294
+ },
1295
+ children: error
1296
+ }
1297
+ );
1298
+ }
1299
+ if (!result?.emboss_svg) {
1300
+ return null;
1301
+ }
1302
+ return /* @__PURE__ */ jsx2(
1303
+ "div",
1304
+ {
1305
+ className,
1306
+ style: containerStyle,
1307
+ onClick,
1308
+ onMouseEnter: handleMouseEnter,
1309
+ onMouseLeave: handleMouseLeave,
1310
+ dangerouslySetInnerHTML: { __html: result.emboss_svg }
1311
+ }
1312
+ );
1313
+ }
1314
+ function LoadingSpinner() {
1315
+ return /* @__PURE__ */ jsxs2(
1316
+ "svg",
1317
+ {
1318
+ width: "24",
1319
+ height: "24",
1320
+ viewBox: "0 0 24 24",
1321
+ style: {
1322
+ animation: "spin 1s linear infinite"
1323
+ },
1324
+ children: [
1325
+ /* @__PURE__ */ jsx2("style", { children: `@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }` }),
1326
+ /* @__PURE__ */ jsx2(
1327
+ "circle",
1328
+ {
1329
+ cx: "12",
1330
+ cy: "12",
1331
+ r: "10",
1332
+ stroke: "#ccc",
1333
+ strokeWidth: "3",
1334
+ fill: "none",
1335
+ strokeDasharray: "31.4 31.4",
1336
+ strokeLinecap: "round"
1337
+ }
1338
+ )
1339
+ ]
1340
+ }
1341
+ );
1342
+ }
1343
+
1344
+ // src/useIconCraft.ts
1345
+ import { useState as useState3, useEffect as useEffect3, useCallback as useCallback2 } from "react";
1346
+ var wasmModule2 = null;
1347
+ var wasmInitPromise2 = null;
1348
+ async function initWasm2() {
1349
+ if (wasmModule2) return wasmModule2;
1350
+ if (wasmInitPromise2) return wasmInitPromise2;
1351
+ wasmInitPromise2 = (async () => {
1352
+ const wasm = await import("./icon_craft_wasm-JDPURZOP.js");
1353
+ await wasm.default();
1354
+ wasmModule2 = wasm;
1355
+ return wasm;
1356
+ })();
1357
+ return wasmInitPromise2;
1358
+ }
1359
+ var shapeModeMap3 = {
1360
+ jelly: 0,
1361
+ droplet: 1,
1362
+ wax: 2
1363
+ };
1364
+ function isUrl2(str) {
1365
+ return str.startsWith("http://") || str.startsWith("https://") || str.startsWith("/");
1366
+ }
1367
+ async function fetchSvg2(url) {
1368
+ const response = await fetch(url);
1369
+ if (!response.ok) {
1370
+ throw new Error(`Failed to fetch SVG: ${response.status}`);
1371
+ }
1372
+ return response.text();
1373
+ }
1374
+ function useIconCraft(options) {
1375
+ const {
1376
+ svg,
1377
+ mode = "wax",
1378
+ color = "#6366f1",
1379
+ iconStyle = "emboss",
1380
+ offset = 20,
1381
+ resolution = 256,
1382
+ simplify = 2,
1383
+ autoGenerate = true
1384
+ } = options;
1385
+ const [result, setResult] = useState3(null);
1386
+ const [isLoading, setIsLoading] = useState3(false);
1387
+ const [error, setError] = useState3(null);
1388
+ const [svgContent, setSvgContent] = useState3(null);
1389
+ const reset = useCallback2(() => {
1390
+ setResult(null);
1391
+ setError(null);
1392
+ setSvgContent(null);
1393
+ }, []);
1394
+ const generate = useCallback2(async () => {
1395
+ if (!svg) {
1396
+ setError("SVG content is required");
1397
+ return;
1398
+ }
1399
+ setIsLoading(true);
1400
+ setError(null);
1401
+ try {
1402
+ let content = svg;
1403
+ if (isUrl2(svg)) {
1404
+ content = await fetchSvg2(svg);
1405
+ }
1406
+ setSvgContent(content);
1407
+ const wasm = await initWasm2();
1408
+ const modeValue = shapeModeMap3[mode];
1409
+ const wasmResult = wasm.generate_clippath_with_color(
1410
+ content,
1411
+ modeValue,
1412
+ offset,
1413
+ resolution,
1414
+ simplify,
1415
+ iconStyle === "emboss",
1416
+ color
1417
+ );
1418
+ if (!wasmResult.success) {
1419
+ throw new Error(wasmResult.error || "Generation failed");
1420
+ }
1421
+ setResult(wasmResult);
1422
+ } catch (err) {
1423
+ setError(err instanceof Error ? err.message : "Unknown error");
1424
+ } finally {
1425
+ setIsLoading(false);
1426
+ }
1427
+ }, [svg, mode, color, iconStyle, offset, resolution, simplify]);
1428
+ useEffect3(() => {
1429
+ if (autoGenerate && svg) {
1430
+ generate();
1431
+ }
1432
+ }, [autoGenerate, generate, svg]);
1433
+ return {
1434
+ result,
1435
+ isLoading,
1436
+ error,
1437
+ svgContent,
1438
+ generate,
1439
+ reset
1440
+ };
1441
+ }
1442
+ function useLegacyIconCraft(options) {
1443
+ const {
1444
+ svgContent,
1445
+ mode = "jelly",
1446
+ baseColor = "#6366f1",
1447
+ offset = 5,
1448
+ resolution = 256,
1449
+ simplifyEpsilon = 0.5
1450
+ } = options;
1451
+ const { result, isLoading, error, generate } = useIconCraft({
1452
+ svg: svgContent,
1453
+ mode,
1454
+ color: baseColor,
1455
+ offset,
1456
+ resolution,
1457
+ simplify: simplifyEpsilon,
1458
+ iconStyle: "emboss"
1459
+ });
1460
+ return { result, isLoading, error, generate };
1461
+ }
1462
+
1463
+ // src/IconCraft.tsx
1464
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1465
+ function IconCraft({
1466
+ svgContent,
1467
+ mode = "jelly",
1468
+ baseColor = "#6366f1",
1469
+ offset = 5,
1470
+ resolution = 256,
1471
+ simplifyEpsilon = 0.5,
1472
+ showShadow: _showShadow = true,
1473
+ showHighlight: _showHighlight = true,
1474
+ className,
1475
+ style
1476
+ }) {
1477
+ const { result, isLoading, error } = useIconCraft({
1478
+ svg: svgContent,
1479
+ mode,
1480
+ color: baseColor,
1481
+ offset,
1482
+ resolution,
1483
+ simplify: simplifyEpsilon
1484
+ });
1485
+ if (isLoading) {
1486
+ return /* @__PURE__ */ jsx3("div", { className, style, children: "Loading..." });
1487
+ }
1488
+ if (error) {
1489
+ return /* @__PURE__ */ jsxs3("div", { className, style, children: [
1490
+ "Error: ",
1491
+ error
1492
+ ] });
1493
+ }
1494
+ if (!result?.emboss_svg) {
1495
+ return null;
1496
+ }
1497
+ return /* @__PURE__ */ jsx3(
1498
+ "div",
1499
+ {
1500
+ className,
1501
+ style: {
1502
+ ...style,
1503
+ width: "100%",
1504
+ height: "100%"
1505
+ },
1506
+ dangerouslySetInnerHTML: { __html: result.emboss_svg }
1507
+ }
1508
+ );
1509
+ }
1510
+
1511
+ // src/context/IconCraftProvider.tsx
1512
+ import {
1513
+ createContext,
1514
+ useContext,
1515
+ useReducer,
1516
+ useCallback as useCallback3,
1517
+ useMemo as useMemo3,
1518
+ useRef
1519
+ } from "react";
1520
+
1521
+ // src/context/IconCraftDispatcher.ts
1522
+ function createDispatcher() {
1523
+ const subscriptions = /* @__PURE__ */ new Set();
1524
+ const dispatch = (event) => {
1525
+ for (const sub of subscriptions) {
1526
+ if (sub.filter !== "*" && sub.filter !== event.id) {
1527
+ continue;
1528
+ }
1529
+ if (sub.eventTypes !== "*" && !sub.eventTypes.has(event.type)) {
1530
+ continue;
1531
+ }
1532
+ try {
1533
+ sub.handler(event);
1534
+ } catch (err) {
1535
+ console.error("[IconCraft] Event handler error:", err);
1536
+ }
1537
+ }
1538
+ };
1539
+ const subscribe = (filter, eventType, handler) => {
1540
+ const eventTypes = eventType === "*" ? "*" : new Set(Array.isArray(eventType) ? eventType : [eventType]);
1541
+ const subscription = {
1542
+ filter,
1543
+ eventTypes,
1544
+ handler
1545
+ };
1546
+ subscriptions.add(subscription);
1547
+ return () => {
1548
+ subscriptions.delete(subscription);
1549
+ };
1550
+ };
1551
+ return { dispatch, subscribe };
1552
+ }
1553
+
1554
+ // src/context/types.ts
1555
+ var DEFAULT_METADATA = {
1556
+ x: 0,
1557
+ y: 0,
1558
+ zIndex: 0
1559
+ };
1560
+
1561
+ // src/context/IconCraftProvider.tsx
1562
+ import { jsx as jsx4 } from "react/jsx-runtime";
1563
+ var IconCraftContext = createContext(null);
1564
+ function reducer(state, action) {
1565
+ switch (action.type) {
1566
+ case "ADD": {
1567
+ const newInstances = new Map(state.instances);
1568
+ const newMetadata = new Map(state.metadata);
1569
+ newInstances.set(action.id, action.instance);
1570
+ newMetadata.set(action.id, action.metadata);
1571
+ return { ...state, instances: newInstances, metadata: newMetadata };
1572
+ }
1573
+ case "REMOVE": {
1574
+ const newInstances = new Map(state.instances);
1575
+ const newMetadata = new Map(state.metadata);
1576
+ const newSelection = new Set(state.selection);
1577
+ newInstances.delete(action.id);
1578
+ newMetadata.delete(action.id);
1579
+ newSelection.delete(action.id);
1580
+ return { instances: newInstances, metadata: newMetadata, selection: newSelection };
1581
+ }
1582
+ case "UPDATE_METADATA": {
1583
+ const existing = state.metadata.get(action.id);
1584
+ if (!existing) return state;
1585
+ const newMetadata = new Map(state.metadata);
1586
+ newMetadata.set(action.id, { ...existing, ...action.changes });
1587
+ return { ...state, metadata: newMetadata };
1588
+ }
1589
+ case "SELECT": {
1590
+ if (state.selection.has(action.id)) return state;
1591
+ const newSelection = new Set(state.selection);
1592
+ newSelection.add(action.id);
1593
+ return { ...state, selection: newSelection };
1594
+ }
1595
+ case "DESELECT": {
1596
+ if (!state.selection.has(action.id)) return state;
1597
+ const newSelection = new Set(state.selection);
1598
+ newSelection.delete(action.id);
1599
+ return { ...state, selection: newSelection };
1600
+ }
1601
+ case "TOGGLE_SELECT": {
1602
+ const newSelection = new Set(state.selection);
1603
+ if (newSelection.has(action.id)) {
1604
+ newSelection.delete(action.id);
1605
+ } else {
1606
+ newSelection.add(action.id);
1607
+ }
1608
+ return { ...state, selection: newSelection };
1609
+ }
1610
+ case "CLEAR_SELECTION": {
1611
+ if (state.selection.size === 0) return state;
1612
+ return { ...state, selection: /* @__PURE__ */ new Set() };
1613
+ }
1614
+ case "CLEAR_ALL": {
1615
+ return {
1616
+ instances: /* @__PURE__ */ new Map(),
1617
+ metadata: /* @__PURE__ */ new Map(),
1618
+ selection: /* @__PURE__ */ new Set()
1619
+ };
1620
+ }
1621
+ default:
1622
+ return state;
1623
+ }
1624
+ }
1625
+ var initialState = {
1626
+ instances: /* @__PURE__ */ new Map(),
1627
+ metadata: /* @__PURE__ */ new Map(),
1628
+ selection: /* @__PURE__ */ new Set()
1629
+ };
1630
+ function IconCraftProvider({
1631
+ children,
1632
+ defaultConfig = {}
1633
+ }) {
1634
+ const [state, dispatch] = useReducer(reducer, initialState);
1635
+ const dispatcherRef = useRef(null);
1636
+ if (!dispatcherRef.current) {
1637
+ dispatcherRef.current = createDispatcher();
1638
+ }
1639
+ const dispatcher = dispatcherRef.current;
1640
+ const zIndexRef = useRef(0);
1641
+ const factory = useMemo3(
1642
+ () => new IconCraftFactory(defaultConfig),
1643
+ [defaultConfig]
1644
+ );
1645
+ const create = useCallback3(
1646
+ (svg, config, metadataOverrides) => {
1647
+ const instance = config ? factory.create(svg, config) : factory.create(svg);
1648
+ const id = instance.id;
1649
+ zIndexRef.current += 1;
1650
+ const metadata = {
1651
+ ...DEFAULT_METADATA,
1652
+ zIndex: zIndexRef.current,
1653
+ ...metadataOverrides
1654
+ };
1655
+ dispatch({ type: "ADD", id, instance, metadata });
1656
+ dispatcher.dispatch({ type: "created", id, instance });
1657
+ return id;
1658
+ },
1659
+ [factory, dispatcher]
1660
+ );
1661
+ const remove = useCallback3(
1662
+ (id) => {
1663
+ if (!state.instances.has(id)) return false;
1664
+ dispatch({ type: "REMOVE", id });
1665
+ dispatcher.dispatch({ type: "removed", id });
1666
+ return true;
1667
+ },
1668
+ [state.instances, dispatcher]
1669
+ );
1670
+ const getById = useCallback3(
1671
+ (id) => state.instances.get(id),
1672
+ [state.instances]
1673
+ );
1674
+ const getAll = useCallback3(
1675
+ () => Array.from(state.instances.values()),
1676
+ [state.instances]
1677
+ );
1678
+ const getMetadata = useCallback3(
1679
+ (id) => state.metadata.get(id),
1680
+ [state.metadata]
1681
+ );
1682
+ const updateMetadata = useCallback3(
1683
+ (id, changes) => {
1684
+ dispatch({ type: "UPDATE_METADATA", id, changes });
1685
+ dispatcher.dispatch({ type: "metadata", id, changes });
1686
+ if (changes.x !== void 0 || changes.y !== void 0) {
1687
+ const meta = state.metadata.get(id);
1688
+ if (meta) {
1689
+ dispatcher.dispatch({
1690
+ type: "move",
1691
+ id,
1692
+ x: changes.x ?? meta.x,
1693
+ y: changes.y ?? meta.y
1694
+ });
1695
+ }
1696
+ }
1697
+ if (changes.zIndex !== void 0) {
1698
+ dispatcher.dispatch({ type: "zIndex", id, zIndex: changes.zIndex });
1699
+ }
1700
+ },
1701
+ [state.metadata, dispatcher]
1702
+ );
1703
+ const select = useCallback3(
1704
+ (id) => {
1705
+ dispatch({ type: "SELECT", id });
1706
+ dispatcher.dispatch({ type: "select", id });
1707
+ },
1708
+ [dispatcher]
1709
+ );
1710
+ const deselect = useCallback3(
1711
+ (id) => {
1712
+ dispatch({ type: "DESELECT", id });
1713
+ dispatcher.dispatch({ type: "deselect", id });
1714
+ },
1715
+ [dispatcher]
1716
+ );
1717
+ const toggleSelect = useCallback3(
1718
+ (id) => {
1719
+ const wasSelected = state.selection.has(id);
1720
+ dispatch({ type: "TOGGLE_SELECT", id });
1721
+ dispatcher.dispatch({ type: wasSelected ? "deselect" : "select", id });
1722
+ },
1723
+ [state.selection, dispatcher]
1724
+ );
1725
+ const clearSelection = useCallback3(() => {
1726
+ for (const id of state.selection) {
1727
+ dispatcher.dispatch({ type: "deselect", id });
1728
+ }
1729
+ dispatch({ type: "CLEAR_SELECTION" });
1730
+ }, [state.selection, dispatcher]);
1731
+ const getSelected = useCallback3(
1732
+ () => Array.from(state.selection),
1733
+ [state.selection]
1734
+ );
1735
+ const isSelected = useCallback3(
1736
+ (id) => state.selection.has(id),
1737
+ [state.selection]
1738
+ );
1739
+ const clear = useCallback3(() => {
1740
+ for (const id of state.instances.keys()) {
1741
+ dispatcher.dispatch({ type: "removed", id });
1742
+ }
1743
+ dispatch({ type: "CLEAR_ALL" });
1744
+ }, [state.instances, dispatcher]);
1745
+ const contextValue = useMemo3(
1746
+ () => ({
1747
+ state,
1748
+ actions: {
1749
+ create,
1750
+ remove,
1751
+ getById,
1752
+ getAll,
1753
+ getMetadata,
1754
+ updateMetadata,
1755
+ select,
1756
+ deselect,
1757
+ toggleSelect,
1758
+ clearSelection,
1759
+ getSelected,
1760
+ isSelected,
1761
+ clear
1762
+ },
1763
+ dispatcher,
1764
+ defaultConfig
1765
+ }),
1766
+ [
1767
+ state,
1768
+ create,
1769
+ remove,
1770
+ getById,
1771
+ getAll,
1772
+ getMetadata,
1773
+ updateMetadata,
1774
+ select,
1775
+ deselect,
1776
+ toggleSelect,
1777
+ clearSelection,
1778
+ getSelected,
1779
+ isSelected,
1780
+ clear,
1781
+ dispatcher,
1782
+ defaultConfig
1783
+ ]
1784
+ );
1785
+ return /* @__PURE__ */ jsx4(IconCraftContext.Provider, { value: contextValue, children });
1786
+ }
1787
+ function useIconCraftContext() {
1788
+ const context = useContext(IconCraftContext);
1789
+ if (!context) {
1790
+ throw new Error("useIconCraftContext must be used within IconCraftProvider");
1791
+ }
1792
+ return context;
1793
+ }
1794
+
1795
+ // src/context/hooks.ts
1796
+ import { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo4 } from "react";
1797
+ function useIconCraftStore() {
1798
+ const { actions, state } = useIconCraftContext();
1799
+ return useMemo4(
1800
+ () => ({
1801
+ // CRUD
1802
+ create: actions.create,
1803
+ remove: actions.remove,
1804
+ getById: actions.getById,
1805
+ getAll: actions.getAll,
1806
+ // メタデータ
1807
+ getMetadata: actions.getMetadata,
1808
+ updateMetadata: actions.updateMetadata,
1809
+ // クリア
1810
+ clear: actions.clear,
1811
+ // 状態(読み取り専用)
1812
+ count: state.instances.size,
1813
+ ids: Array.from(state.instances.keys())
1814
+ }),
1815
+ [actions, state.instances]
1816
+ );
1817
+ }
1818
+ function useIconCraft2(id) {
1819
+ const { actions, state } = useIconCraftContext();
1820
+ const instance = state.instances.get(id);
1821
+ const metadata = state.metadata.get(id);
1822
+ const isSelected = state.selection.has(id);
1823
+ const remove = useCallback4(() => actions.remove(id), [actions, id]);
1824
+ const updateMetadata = useCallback4(
1825
+ (changes) => actions.updateMetadata(id, changes),
1826
+ [actions, id]
1827
+ );
1828
+ const select = useCallback4(() => actions.select(id), [actions, id]);
1829
+ const deselect = useCallback4(() => actions.deselect(id), [actions, id]);
1830
+ const toggleSelect = useCallback4(() => actions.toggleSelect(id), [actions, id]);
1831
+ const move = useCallback4(
1832
+ (x, y) => actions.updateMetadata(id, { x, y }),
1833
+ [actions, id]
1834
+ );
1835
+ const setZIndex = useCallback4(
1836
+ (zIndex) => actions.updateMetadata(id, { zIndex }),
1837
+ [actions, id]
1838
+ );
1839
+ return useMemo4(
1840
+ () => ({
1841
+ instance,
1842
+ metadata,
1843
+ isSelected,
1844
+ exists: !!instance,
1845
+ remove,
1846
+ updateMetadata,
1847
+ select,
1848
+ deselect,
1849
+ toggleSelect,
1850
+ move,
1851
+ setZIndex
1852
+ }),
1853
+ [
1854
+ instance,
1855
+ metadata,
1856
+ isSelected,
1857
+ remove,
1858
+ updateMetadata,
1859
+ select,
1860
+ deselect,
1861
+ toggleSelect,
1862
+ move,
1863
+ setZIndex
1864
+ ]
1865
+ );
1866
+ }
1867
+ function useIconCraftSelection() {
1868
+ const { actions, state } = useIconCraftContext();
1869
+ const selected = useMemo4(
1870
+ () => Array.from(state.selection),
1871
+ [state.selection]
1872
+ );
1873
+ const selectAll = useCallback4(() => {
1874
+ for (const id of state.instances.keys()) {
1875
+ actions.select(id);
1876
+ }
1877
+ }, [state.instances, actions]);
1878
+ const getSelectedInstances = useCallback4(() => {
1879
+ return selected.map((id) => state.instances.get(id)).filter((inst) => inst !== void 0);
1880
+ }, [selected, state.instances]);
1881
+ return useMemo4(
1882
+ () => ({
1883
+ selected,
1884
+ count: selected.length,
1885
+ hasSelection: selected.length > 0,
1886
+ select: actions.select,
1887
+ deselect: actions.deselect,
1888
+ toggle: actions.toggleSelect,
1889
+ clear: actions.clearSelection,
1890
+ isSelected: actions.isSelected,
1891
+ selectAll,
1892
+ getSelectedInstances
1893
+ }),
1894
+ [selected, actions, selectAll, getSelectedInstances]
1895
+ );
1896
+ }
1897
+ function useIconCraftEvent(filter, eventType, handler) {
1898
+ const { dispatcher } = useIconCraftContext();
1899
+ useEffect4(() => {
1900
+ const unsubscribe = dispatcher.subscribe(filter, eventType, handler);
1901
+ return unsubscribe;
1902
+ }, [dispatcher, filter, eventType, handler]);
1903
+ }
1904
+ function useIconCraftCreate(options = {}) {
1905
+ const { actions } = useIconCraftContext();
1906
+ const { config, metadata, autoSelect = false } = options;
1907
+ return useCallback4(
1908
+ (svg, overrides) => {
1909
+ const id = actions.create(svg, { ...config, ...overrides }, metadata);
1910
+ if (autoSelect) {
1911
+ actions.select(id);
1912
+ }
1913
+ return id;
1914
+ },
1915
+ [actions, config, metadata, autoSelect]
1916
+ );
1917
+ }
1918
+ export {
1919
+ DEFAULT_CONFIG,
1920
+ DEFAULT_METADATA,
1921
+ IconCraft,
1922
+ IconCraftConfig,
1923
+ IconCraftFactory,
1924
+ IconCraftInstance,
1925
+ IconCraftProvider,
1926
+ IconCraftRegistry,
1927
+ IconCraftShape,
1928
+ IconCraftView,
1929
+ WasmManager,
1930
+ animationDefaults,
1931
+ createDispatcher,
1932
+ defaultFactory,
1933
+ generateIconId,
1934
+ getAnimationDefaults,
1935
+ getAnimationName,
1936
+ getAnimationStyle,
1937
+ getCustomAnimation,
1938
+ getKeyframes,
1939
+ getTimestampFromId,
1940
+ getTransformOrigin,
1941
+ globalRegistry,
1942
+ injectKeyframes,
1943
+ keyframes,
1944
+ parseAnimationOptions,
1945
+ registerAnimation,
1946
+ useIconCraft,
1947
+ useIconCraftContext,
1948
+ useIconCraftCreate,
1949
+ useIconCraftEvent,
1950
+ useIconCraft2 as useIconCraftInstance,
1951
+ useIconCraftSelection,
1952
+ useIconCraftStore,
1953
+ useLegacyIconCraft
1954
+ };