gospelo-iconcraft-react 0.3.1 → 0.3.5

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 CHANGED
@@ -161,7 +161,7 @@ import { IconCraftShape } from 'gospelo-iconcraft-react';
161
161
  | Prop | Type | Default | Description |
162
162
  |------|------|---------|-------------|
163
163
  | `svg` | `string` | required | SVG content string |
164
- | `mode` | `'jelly' \| 'droplet' \| 'wax'` | `'jelly'` | Shape mode |
164
+ | `mode` | `'sticker' \| 'jelly' \| 'bubble' \| 'wax'` | `'jelly'` | Shape mode |
165
165
  | `shapeColor` | `string` | `'#6366f1'` | Shape base color (hex) |
166
166
  | `iconColor` | `string` | `'#1d1d1f'` | Icon color (for stroke/fill styles) |
167
167
  | `iconStyle` | `'original' \| 'emboss' \| 'stroke' \| 'fill'` | `'emboss'` | Icon rendering style |
@@ -208,8 +208,9 @@ icon1.config.update({ shapeColor: '#10b981', rotation: 45 });
208
208
 
209
209
  | Mode | Description |
210
210
  |------|-------------|
211
+ | `sticker` | Jelly-based shape with paper-like noise texture, no highlights |
211
212
  | `jelly` | Smooth, organic blob shape following icon contours |
212
- | `droplet` | Perfect circle/sphere with glass-like highlights |
213
+ | `bubble` | Perfect circle/sphere with glass-like highlights |
213
214
  | `wax` | Irregular wax seal shape with pressed icon indent |
214
215
 
215
216
  ## Animations
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { CSSProperties, ReactNode } from 'react';
2
2
  import * as gospelo_iconcraft_wasm from 'gospelo-iconcraft-wasm';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
 
5
- type ShapeMode = 'jelly' | 'droplet' | 'wax';
5
+ type ShapeMode = 'jelly' | 'bubble' | 'wax' | 'sticker';
6
6
  type IconStyle = 'original' | 'emboss' | 'stroke' | 'fill';
7
7
  /** Built-in animation types */
8
8
  type BuiltInAnimationType = 'none' | 'shake' | 'bounce' | 'pulse' | 'swing' | 'wobble' | 'jello' | 'heartbeat' | 'float' | 'spin' | 'rubberBand' | 'squish' | 'tada' | 'flip' | 'drop' | 'pop' | 'wiggle' | 'breathe';
@@ -93,7 +93,7 @@ interface IconCraftResult {
93
93
  interface IconCraftShapeProps {
94
94
  /** SVG content string or URL */
95
95
  svg: string;
96
- /** Shape mode: jelly, droplet, or wax */
96
+ /** Shape mode: jelly, bubble, or wax */
97
97
  mode?: ShapeMode;
98
98
  /** Base color for the shape (hex) */
99
99
  shapeColor?: string;
@@ -208,6 +208,7 @@ declare class IconCraftConfig {
208
208
  includeIcon: boolean;
209
209
  shapeColor: string;
210
210
  rotation: number;
211
+ iconColor: string;
211
212
  };
212
213
  /**
213
214
  * スタイル用のサイズを取得
@@ -335,6 +336,7 @@ interface WasmGenerateParams {
335
336
  includeIcon: boolean;
336
337
  shapeColor: string;
337
338
  rotation?: number;
339
+ iconColor?: string;
338
340
  }
339
341
  /**
340
342
  * WASMマネージャー(Singleton)
@@ -563,6 +565,12 @@ interface IconCraftViewProps {
563
565
  renderReticle?: (props: ReticleProps) => React.ReactNode;
564
566
  /** Reticle preset object (overrides renderReticle) */
565
567
  reticlePreset?: ReticlePreset;
568
+ /** Dial ring color (default: '#000') */
569
+ dialColor?: string;
570
+ /** Notch color (default: '#000') */
571
+ notchColor?: string;
572
+ /** Reticle color (default: '#000') */
573
+ reticleColor?: string;
566
574
  }
567
575
  /** Props passed to custom reticle renderer */
568
576
  interface ReticleProps {
@@ -603,7 +611,7 @@ interface DialLabelProps {
603
611
  * <IconCraftView instance={icon} zIndex={10} />
604
612
  * ```
605
613
  */
606
- declare function IconCraftView({ instance, animation, animationTarget, animateOnHover, zIndex, className, style, onClick, onLoad, onError, showRotationDial, onRotationChange, rotationSnap, renderRing: renderRingProp, renderNotch: renderNotchProp, renderLabel: renderLabelProp, dialPreset, showReticle, renderReticle: renderReticleProp, reticlePreset, }: IconCraftViewProps): react_jsx_runtime.JSX.Element | null;
614
+ declare function IconCraftView({ instance, animation, animationTarget, animateOnHover, zIndex, className, style, onClick, onLoad, onError, showRotationDial, onRotationChange, rotationSnap, renderRing: renderRingProp, renderNotch: renderNotchProp, renderLabel: renderLabelProp, dialPreset, showReticle, renderReticle: renderReticleProp, reticlePreset, dialColor: dialColorProp, notchColor: notchColorProp, reticleColor: reticleColorProp, }: IconCraftViewProps): react_jsx_runtime.JSX.Element | null;
607
615
 
608
616
  /**
609
617
  * IconCraftSimple Props
@@ -668,7 +676,7 @@ interface IconCraftSimpleProps {
668
676
  * // SVG文字列を直接渡す
669
677
  * <IconCraftSimple
670
678
  * src={`<svg>...</svg>`}
671
- * mode="droplet"
679
+ * mode="bubble"
672
680
  * iconStyle="emboss"
673
681
  * />
674
682
  * ```
package/dist/index.js CHANGED
@@ -60,7 +60,7 @@ var IconCraftConfig = class _IconCraftConfig {
60
60
  * WASM呼び出し用のパラメータを取得
61
61
  */
62
62
  getWasmParams() {
63
- const needsEmbossSvg = this.mode === "wax" || this.iconStyle === "emboss";
63
+ const needsEmbossSvg = this.mode === "wax" || this.mode === "sticker" || this.iconStyle === "emboss";
64
64
  return {
65
65
  mode: this.mode,
66
66
  offset: this.offset,
@@ -68,7 +68,8 @@ var IconCraftConfig = class _IconCraftConfig {
68
68
  simplify: this.simplify,
69
69
  includeIcon: needsEmbossSvg,
70
70
  shapeColor: this.shapeColor,
71
- rotation: this.rotation
71
+ rotation: this.rotation,
72
+ iconColor: this.iconColor
72
73
  };
73
74
  }
74
75
  /**
@@ -96,13 +97,15 @@ function createCacheKey(params) {
96
97
  simplify: params.simplify,
97
98
  includeIcon: params.includeIcon,
98
99
  shapeColor: params.shapeColor,
99
- rotation: params.rotation ?? 0
100
+ rotation: params.rotation ?? 0,
101
+ iconColor: params.iconColor ?? ""
100
102
  });
101
103
  }
102
104
  var shapeModeMap = {
103
105
  jelly: 0,
104
- droplet: 1,
105
- wax: 2
106
+ bubble: 1,
107
+ wax: 2,
108
+ sticker: 3
106
109
  };
107
110
  var WasmManagerClass = class {
108
111
  constructor() {
@@ -142,6 +145,7 @@ var WasmManagerClass = class {
142
145
  }
143
146
  const wasm = await this.init();
144
147
  const rotation = params.rotation ?? 0;
148
+ const iconColor = params.iconColor ?? null;
145
149
  const result = typeof wasm.generate_clippath_with_rotation === "function" ? wasm.generate_clippath_with_rotation(
146
150
  params.svgContent,
147
151
  shapeModeMap[params.mode],
@@ -150,7 +154,8 @@ var WasmManagerClass = class {
150
154
  params.simplify,
151
155
  params.includeIcon,
152
156
  params.shapeColor,
153
- rotation
157
+ rotation,
158
+ iconColor
154
159
  ) : wasm.generate_clippath_with_color(
155
160
  params.svgContent,
156
161
  shapeModeMap[params.mode],
@@ -542,7 +547,9 @@ import { useEffect, useState, useMemo, useCallback, useRef } from "react";
542
547
 
543
548
  // src/components/dialPresets.tsx
544
549
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
545
- var dialColor = "var(--iconcraft-dial-color, #0071e3)";
550
+ var dialColor = "var(--iconcraft-dial-color, #000)";
551
+ var notchColor = "var(--iconcraft-notch-color, #000)";
552
+ var reticleColor = "var(--iconcraft-reticle-color, #000)";
546
553
  var dialPresetDashed = {
547
554
  name: "dashed",
548
555
  renderRing: ({ cx, cy, radius }) => /* @__PURE__ */ jsx(
@@ -564,7 +571,7 @@ var dialPresetDashed = {
564
571
  cx: x,
565
572
  cy: y,
566
573
  r: "7",
567
- fill: dialColor,
574
+ fill: notchColor,
568
575
  stroke: "#fff",
569
576
  strokeWidth: "2",
570
577
  style: { pointerEvents: "auto", cursor: "grab" },
@@ -599,7 +606,7 @@ var dialPresetSolid = {
599
606
  y: "-5",
600
607
  width: "10",
601
608
  height: "10",
602
- fill: dialColor,
609
+ fill: notchColor,
603
610
  stroke: "#fff",
604
611
  strokeWidth: "1.5",
605
612
  rx: "1"
@@ -646,7 +653,7 @@ var dialPresetTicks = {
646
653
  cx: x,
647
654
  cy: y,
648
655
  r: "5",
649
- fill: dialColor,
656
+ fill: notchColor,
650
657
  stroke: "#fff",
651
658
  strokeWidth: "1.5",
652
659
  style: { pointerEvents: "auto", cursor: "grab" },
@@ -689,8 +696,8 @@ var dialPresetDotted = {
689
696
  children: /* @__PURE__ */ jsx(
690
697
  "polygon",
691
698
  {
692
- points: "0,4 -5,16 5,16",
693
- fill: dialColor,
699
+ points: "0,-2 -5,10 5,10",
700
+ fill: notchColor,
694
701
  stroke: "#fff",
695
702
  strokeWidth: "1.5",
696
703
  strokeLinejoin: "round"
@@ -746,7 +753,7 @@ var dialPresetDouble = {
746
753
  width: "6",
747
754
  height: "16",
748
755
  rx: "3",
749
- fill: dialColor,
756
+ fill: notchColor,
750
757
  stroke: "#fff",
751
758
  strokeWidth: "1.5"
752
759
  }
@@ -805,9 +812,9 @@ var dialPresetCrosshair = {
805
812
  style: { pointerEvents: "auto", cursor: "grab" },
806
813
  onMouseDown,
807
814
  children: [
808
- /* @__PURE__ */ jsx("line", { x1: -6, y1: 0, x2: 6, y2: 0, stroke: dialColor, strokeWidth: "2" }),
809
- /* @__PURE__ */ jsx("line", { x1: 0, y1: -6, x2: 0, y2: 6, stroke: dialColor, strokeWidth: "2" }),
810
- /* @__PURE__ */ jsx("circle", { cx: 0, cy: 0, r: "3", fill: "#fff", stroke: dialColor, strokeWidth: "1.5" })
815
+ /* @__PURE__ */ jsx("line", { x1: -6, y1: 0, x2: 6, y2: 0, stroke: notchColor, strokeWidth: "2" }),
816
+ /* @__PURE__ */ jsx("line", { x1: 0, y1: -6, x2: 0, y2: 6, stroke: notchColor, strokeWidth: "2" }),
817
+ /* @__PURE__ */ jsx("circle", { cx: 0, cy: 0, r: "3", fill: "#fff", stroke: notchColor, strokeWidth: "1.5" })
811
818
  ]
812
819
  }
813
820
  )
@@ -852,7 +859,7 @@ var dialPresetMinimal = {
852
859
  "polygon",
853
860
  {
854
861
  points: "0,-8 -5,4 5,4",
855
- fill: dialColor,
862
+ fill: notchColor,
856
863
  stroke: "#fff",
857
864
  strokeWidth: "1.5",
858
865
  strokeLinejoin: "round"
@@ -886,7 +893,7 @@ var dialPresetNeedle = {
886
893
  "path",
887
894
  {
888
895
  d: "M 0,-12 L 2,-2 0,4 -2,-2 Z",
889
- fill: dialColor,
896
+ fill: notchColor,
890
897
  stroke: "#fff",
891
898
  strokeWidth: "1",
892
899
  strokeLinejoin: "round"
@@ -923,7 +930,7 @@ var dialPresetBar = {
923
930
  width: "4",
924
931
  height: "20",
925
932
  rx: "2",
926
- fill: dialColor,
933
+ fill: notchColor,
927
934
  stroke: "#fff",
928
935
  strokeWidth: "1.5"
929
936
  }
@@ -971,7 +978,7 @@ var dialPresetArrow = {
971
978
  "path",
972
979
  {
973
980
  d: "M 0,-11 L 4,-3 1,-3 1,5 -1,5 -1,-3 -4,-3 Z",
974
- fill: dialColor,
981
+ fill: notchColor,
975
982
  stroke: "#fff",
976
983
  strokeWidth: "1",
977
984
  strokeLinejoin: "round"
@@ -1005,7 +1012,7 @@ var reticlePresetCross = {
1005
1012
  y1: cy - half,
1006
1013
  x2: cx,
1007
1014
  y2: cy + half,
1008
- stroke: dialColor,
1015
+ stroke: reticleColor,
1009
1016
  strokeWidth: "1"
1010
1017
  }
1011
1018
  ),
@@ -1016,11 +1023,11 @@ var reticlePresetCross = {
1016
1023
  y1: cy,
1017
1024
  x2: cx + half,
1018
1025
  y2: cy,
1019
- stroke: dialColor,
1026
+ stroke: reticleColor,
1020
1027
  strokeWidth: "1"
1021
1028
  }
1022
1029
  ),
1023
- /* @__PURE__ */ jsx("circle", { cx, cy, r: "2", fill: dialColor })
1030
+ /* @__PURE__ */ jsx("circle", { cx, cy, r: "2", fill: reticleColor })
1024
1031
  ] });
1025
1032
  }
1026
1033
  };
@@ -1037,11 +1044,11 @@ var reticlePresetBullseye = {
1037
1044
  cy,
1038
1045
  r,
1039
1046
  fill: "none",
1040
- stroke: dialColor,
1047
+ stroke: reticleColor,
1041
1048
  strokeWidth: "1"
1042
1049
  }
1043
1050
  ),
1044
- /* @__PURE__ */ jsx("circle", { cx, cy, r: "2.5", fill: dialColor })
1051
+ /* @__PURE__ */ jsx("circle", { cx, cy, r: "2.5", fill: reticleColor })
1045
1052
  ] });
1046
1053
  }
1047
1054
  };
@@ -1064,7 +1071,7 @@ var reticlePresetGlobe = {
1064
1071
  {
1065
1072
  d: `M ${cx + lx} ${cy - ly} A ${rx} ${ly} 0 0 0 ${cx + lx} ${cy + ly}`,
1066
1073
  fill: "none",
1067
- stroke: dialColor,
1074
+ stroke: reticleColor,
1068
1075
  strokeWidth: sw
1069
1076
  }
1070
1077
  ),
@@ -1073,7 +1080,7 @@ var reticlePresetGlobe = {
1073
1080
  {
1074
1081
  d: `M ${cx - lx} ${cy - ly} A ${rx} ${ly} 0 0 1 ${cx - lx} ${cy + ly}`,
1075
1082
  fill: "none",
1076
- stroke: dialColor,
1083
+ stroke: reticleColor,
1077
1084
  strokeWidth: sw
1078
1085
  }
1079
1086
  )
@@ -1086,7 +1093,7 @@ var reticlePresetGlobe = {
1086
1093
  y1: cy,
1087
1094
  x2: cx + r,
1088
1095
  y2: cy,
1089
- stroke: dialColor,
1096
+ stroke: reticleColor,
1090
1097
  strokeWidth: sw
1091
1098
  }
1092
1099
  ),
@@ -1100,7 +1107,7 @@ var reticlePresetGlobe = {
1100
1107
  {
1101
1108
  d: `M ${cx - lx} ${cy - ly} A ${lx} ${ry} 0 0 0 ${cx + lx} ${cy - ly}`,
1102
1109
  fill: "none",
1103
- stroke: dialColor,
1110
+ stroke: reticleColor,
1104
1111
  strokeWidth: sw
1105
1112
  }
1106
1113
  ),
@@ -1109,7 +1116,7 @@ var reticlePresetGlobe = {
1109
1116
  {
1110
1117
  d: `M ${cx - lx} ${cy + ly} A ${lx} ${ry} 0 0 1 ${cx + lx} ${cy + ly}`,
1111
1118
  fill: "none",
1112
- stroke: dialColor,
1119
+ stroke: reticleColor,
1113
1120
  strokeWidth: sw
1114
1121
  }
1115
1122
  )
@@ -1125,7 +1132,7 @@ var reticlePresetGlobe = {
1125
1132
  cy,
1126
1133
  r,
1127
1134
  fill: "none",
1128
- stroke: dialColor,
1135
+ stroke: reticleColor,
1129
1136
  strokeWidth: sw
1130
1137
  }
1131
1138
  ),
@@ -1136,7 +1143,7 @@ var reticlePresetGlobe = {
1136
1143
  y1: cy - r,
1137
1144
  x2: cx,
1138
1145
  y2: cy + r,
1139
- stroke: dialColor,
1146
+ stroke: reticleColor,
1140
1147
  strokeWidth: sw
1141
1148
  }
1142
1149
  ),
@@ -1150,7 +1157,7 @@ var reticlePresetGlobe = {
1150
1157
  {
1151
1158
  d: `M ${cx + lx} ${cy - ly} A ${rx} ${ly} 0 0 1 ${cx + lx} ${cy + ly}`,
1152
1159
  fill: "none",
1153
- stroke: dialColor,
1160
+ stroke: reticleColor,
1154
1161
  strokeWidth: sw
1155
1162
  }
1156
1163
  ),
@@ -1159,7 +1166,7 @@ var reticlePresetGlobe = {
1159
1166
  {
1160
1167
  d: `M ${cx - lx} ${cy - ly} A ${rx} ${ly} 0 0 0 ${cx - lx} ${cy + ly}`,
1161
1168
  fill: "none",
1162
- stroke: dialColor,
1169
+ stroke: reticleColor,
1163
1170
  strokeWidth: sw
1164
1171
  }
1165
1172
  )
@@ -1172,7 +1179,7 @@ var reticlePresetGlobe = {
1172
1179
  y1: cy,
1173
1180
  x2: cx + r,
1174
1181
  y2: cy,
1175
- stroke: dialColor,
1182
+ stroke: reticleColor,
1176
1183
  strokeWidth: sw
1177
1184
  }
1178
1185
  ),
@@ -1186,7 +1193,7 @@ var reticlePresetGlobe = {
1186
1193
  {
1187
1194
  d: `M ${cx - lx} ${cy - ly} A ${lx} ${ry} 0 0 1 ${cx + lx} ${cy - ly}`,
1188
1195
  fill: "none",
1189
- stroke: dialColor,
1196
+ stroke: reticleColor,
1190
1197
  strokeWidth: sw
1191
1198
  }
1192
1199
  ),
@@ -1195,7 +1202,7 @@ var reticlePresetGlobe = {
1195
1202
  {
1196
1203
  d: `M ${cx - lx} ${cy + ly} A ${lx} ${ry} 0 0 0 ${cx + lx} ${cy + ly}`,
1197
1204
  fill: "none",
1198
- stroke: dialColor,
1205
+ stroke: reticleColor,
1199
1206
  strokeWidth: sw
1200
1207
  }
1201
1208
  )
@@ -1466,9 +1473,18 @@ function getParser() {
1466
1473
  }
1467
1474
  function sanitizeSvg(html) {
1468
1475
  if (!html) return "";
1469
- const doc = getParser().parseFromString(html, "text/html");
1476
+ const imagePlaceholders = [];
1477
+ const preserved = html.replace(/<image\b[^>]*\/?\s*>/gi, (match) => {
1478
+ imagePlaceholders.push(match);
1479
+ return `<!--__IMAGE_${imagePlaceholders.length - 1}__-->`;
1480
+ });
1481
+ const doc = getParser().parseFromString(preserved, "text/html");
1470
1482
  sanitizeNode(doc.body);
1471
- return doc.body.innerHTML;
1483
+ let result = doc.body.innerHTML;
1484
+ result = result.replace(/<!--__IMAGE_(\d+)__-->/g, (_, idx) => {
1485
+ return imagePlaceholders[parseInt(idx, 10)] ?? "";
1486
+ });
1487
+ return result;
1472
1488
  }
1473
1489
  function sanitizeNode(node) {
1474
1490
  const toRemove = [];
@@ -1537,6 +1553,8 @@ function resolveTransformOrigin(value) {
1537
1553
  return "center center";
1538
1554
  }
1539
1555
  }
1556
+ var ROTATE_CURSOR_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7"/><polyline points="21 3 21 9 15 9"/></svg>`;
1557
+ var ROTATE_CURSOR = `url("data:image/svg+xml,${encodeURIComponent(ROTATE_CURSOR_SVG)}") 12 12, pointer`;
1540
1558
  function IconCraftView({
1541
1559
  instance,
1542
1560
  animation,
@@ -1557,7 +1575,10 @@ function IconCraftView({
1557
1575
  dialPreset,
1558
1576
  showReticle = false,
1559
1577
  renderReticle: renderReticleProp,
1560
- reticlePreset
1578
+ reticlePreset,
1579
+ dialColor: dialColorProp,
1580
+ notchColor: notchColorProp,
1581
+ reticleColor: reticleColorProp
1561
1582
  }) {
1562
1583
  const resolvedDial = dialPreset ?? (renderRingProp || renderNotchProp || renderLabelProp ? void 0 : dialPresetDotted);
1563
1584
  const renderRing = resolvedDial?.renderRing ?? renderRingProp;
@@ -1654,7 +1675,7 @@ function IconCraftView({
1654
1675
  const isWax = mode === "wax";
1655
1676
  if (isWax && instance.embossSvg) {
1656
1677
  let svg2 = instance.embossSvg;
1657
- const modes = ["wax", "jelly", "droplet"];
1678
+ const modes = ["wax", "jelly", "bubble", "sticker"];
1658
1679
  for (const m of modes) {
1659
1680
  svg2 = svg2.replace(
1660
1681
  new RegExp(`id="${m}-`, "g"),
@@ -1666,14 +1687,6 @@ function IconCraftView({
1666
1687
  );
1667
1688
  }
1668
1689
  if (iconStyle === "emboss") {
1669
- const rotation3 = instance.config.rotation;
1670
- if (rotation3) {
1671
- const layout2 = result?.icon_layout;
1672
- const iconCx = (layout2?.left_percent ?? 28) + (layout2?.width_percent ?? 44) / 2;
1673
- const iconCy = (layout2?.top_percent ?? 28) + (layout2?.height_percent ?? 44) / 2;
1674
- const rotateTransform = `transform="rotate(${rotation3}, ${iconCx.toFixed(2)}, ${iconCy.toFixed(2)})"`;
1675
- svg2 = svg2.replace(/<g filter="none">/, `<g filter="none" ${rotateTransform}>`);
1676
- }
1677
1690
  return svg2;
1678
1691
  }
1679
1692
  {
@@ -1709,7 +1722,7 @@ function IconCraftView({
1709
1722
  ${iconContent2}
1710
1723
  </svg>
1711
1724
  </g>`;
1712
- svg2 = svg2.replace(/<g filter="none">[\s\S]*?<\/g>\s*<\/svg>$/, `${newIconSvg}
1725
+ svg2 = svg2.replace(/<g filter="none"[^>]*>[\s\S]*?<\/g>\s*<\/svg>$/, `${newIconSvg}
1713
1726
  </svg>`);
1714
1727
  }
1715
1728
  }
@@ -1717,7 +1730,7 @@ function IconCraftView({
1717
1730
  }
1718
1731
  if (iconStyle === "emboss" && instance.embossSvg) {
1719
1732
  let svg2 = instance.embossSvg;
1720
- const modes = ["wax", "jelly", "droplet"];
1733
+ const modes = ["wax", "jelly", "bubble", "sticker"];
1721
1734
  for (const m of modes) {
1722
1735
  svg2 = svg2.replace(
1723
1736
  new RegExp(`id="${m}-`, "g"),
@@ -1728,14 +1741,6 @@ function IconCraftView({
1728
1741
  `url(#${instanceId}-${m}-`
1729
1742
  );
1730
1743
  }
1731
- const rotation3 = instance.config.rotation;
1732
- if (rotation3) {
1733
- const layout2 = result?.icon_layout;
1734
- const iconCx = (layout2?.left_percent ?? 28) + (layout2?.width_percent ?? 44) / 2;
1735
- const iconCy = (layout2?.top_percent ?? 28) + (layout2?.height_percent ?? 44) / 2;
1736
- const rotateTransform = `transform="rotate(${rotation3}, ${iconCx.toFixed(2)}, ${iconCy.toFixed(2)})"`;
1737
- svg2 = svg2.replace(/<g filter="none">/, `<g filter="none" ${rotateTransform}>`);
1738
- }
1739
1744
  return svg2;
1740
1745
  }
1741
1746
  if (!result || !result.svg_paths?.clip) return "";
@@ -1766,7 +1771,8 @@ function IconCraftView({
1766
1771
  }
1767
1772
  const clipPath = result.svg_paths.clip;
1768
1773
  const highlightPath = result.svg_paths.highlight;
1769
- const isJellyOrDroplet = mode === "jelly" || mode === "droplet";
1774
+ const isSticker = mode === "sticker";
1775
+ const isJellyOrBubble = mode === "jelly" || mode === "bubble" || isSticker;
1770
1776
  const gradientId = `${instanceId}-bg-grad`;
1771
1777
  const clipId = `${instanceId}-clip`;
1772
1778
  const viewBoxMatch = svgContent.match(/viewBox="([^"]*)"/);
@@ -1779,28 +1785,28 @@ function IconCraftView({
1779
1785
  innerSvg = innerSvg.replace(/stroke="[^"]*"/g, "");
1780
1786
  innerSvg = innerSvg.replace(/stroke-width="[^"]*"/g, "");
1781
1787
  }
1782
- const bgGradient = isJellyOrDroplet ? `<linearGradient id="${gradientId}" x1="0%" y1="0%" x2="100%" y2="100%"${gradTransform}>
1788
+ const bgGradient = isJellyOrBubble ? `<linearGradient id="${gradientId}" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="100" y2="100">
1783
1789
  <stop offset="0%" stop-color="${color}" stop-opacity="0.35"/>
1784
1790
  <stop offset="50%" stop-color="${color}" stop-opacity="0.4"/>
1785
1791
  <stop offset="100%" stop-color="${color}" stop-opacity="0.5"/>
1786
1792
  </linearGradient>
1787
- <linearGradient id="${instanceId}-top-highlight" x1="50%" y1="0%" x2="50%" y2="100%"${gradTransform}>
1793
+ ${isSticker ? "" : `<linearGradient id="${instanceId}-top-highlight" gradientUnits="userSpaceOnUse" x1="50" y1="0" x2="50" y2="100">
1788
1794
  <stop offset="0%" stop-color="#fff" stop-opacity="0.6"/>
1789
1795
  <stop offset="30%" stop-color="#fff" stop-opacity="0.3"/>
1790
1796
  <stop offset="60%" stop-color="#fff" stop-opacity="0.1"/>
1791
1797
  <stop offset="100%" stop-color="#fff" stop-opacity="0"/>
1792
1798
  </linearGradient>
1793
- <linearGradient id="${instanceId}-bottom-shadow" x1="50%" y1="0%" x2="50%" y2="100%"${gradTransform}>
1799
+ <linearGradient id="${instanceId}-bottom-shadow" gradientUnits="userSpaceOnUse" x1="50" y1="0" x2="50" y2="100">
1794
1800
  <stop offset="0%" stop-color="#000" stop-opacity="0"/>
1795
1801
  <stop offset="40%" stop-color="#000" stop-opacity="0.05"/>
1796
1802
  <stop offset="70%" stop-color="#000" stop-opacity="0.15"/>
1797
1803
  <stop offset="100%" stop-color="#000" stop-opacity="0.3"/>
1798
1804
  </linearGradient>
1799
- <linearGradient id="${instanceId}-edge-highlight" x1="0%" y1="0%" x2="100%" y2="100%"${gradTransform}>
1805
+ <linearGradient id="${instanceId}-edge-highlight" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="100" y2="100">
1800
1806
  <stop offset="0%" stop-color="#fff" stop-opacity="0.85"/>
1801
1807
  <stop offset="50%" stop-color="#fff" stop-opacity="0.5"/>
1802
1808
  <stop offset="100%" stop-color="#fff" stop-opacity="0"/>
1803
- </linearGradient>` : `<linearGradient id="${gradientId}" x1="0%" y1="0%" x2="100%" y2="100%"${gradTransform}>
1809
+ </linearGradient>`}` : `<linearGradient id="${gradientId}" x1="0%" y1="0%" x2="100%" y2="100%"${gradTransform}>
1804
1810
  <stop offset="0%" stop-color="${color}"/>
1805
1811
  <stop offset="100%" stop-color="${color}"/>
1806
1812
  </linearGradient>
@@ -1814,7 +1820,7 @@ function IconCraftView({
1814
1820
  <stop offset="60%" stop-color="#000" stop-opacity="0.1"/>
1815
1821
  <stop offset="100%" stop-color="#000" stop-opacity="0.25"/>
1816
1822
  </linearGradient>`;
1817
- const shapeContent = isJellyOrDroplet ? `<path d="${clipPath}" fill="url(#${gradientId})"/>
1823
+ const shapeContent = isJellyOrBubble ? isSticker ? `<path d="${clipPath}" fill="url(#${gradientId})"/>` : `<path d="${clipPath}" fill="url(#${gradientId})"/>
1818
1824
  <path d="${clipPath}" fill="url(#${instanceId}-top-highlight)"/>
1819
1825
  <path d="${clipPath}" fill="url(#${instanceId}-bottom-shadow)"/>
1820
1826
  ${highlightPath ? `<path d="${highlightPath}" fill="url(#${instanceId}-edge-highlight)"/>` : ""}` : `<path d="${clipPath}" fill="url(#${gradientId})"/>
@@ -1871,6 +1877,9 @@ function IconCraftView({
1871
1877
  ...style
1872
1878
  };
1873
1879
  const cssVars = {};
1880
+ if (dialColorProp) cssVars["--iconcraft-dial-color"] = dialColorProp;
1881
+ if (notchColorProp) cssVars["--iconcraft-notch-color"] = notchColorProp;
1882
+ if (reticleColorProp) cssVars["--iconcraft-reticle-color"] = reticleColorProp;
1874
1883
  if (target === "shape") {
1875
1884
  cssVars["--iconcraft-shape-animation"] = animStyle;
1876
1885
  cssVars["--iconcraft-icon-animation"] = "none";
@@ -2000,7 +2009,7 @@ function IconCraftView({
2000
2009
  rx: reticleSize * 0.5,
2001
2010
  ry: reticleSize * 0.15,
2002
2011
  fill: "none",
2003
- stroke: "var(--iconcraft-dial-color, #0071e3)",
2012
+ stroke: "var(--iconcraft-reticle-color, #000)",
2004
2013
  strokeWidth: "1",
2005
2014
  strokeDasharray: "3 2"
2006
2015
  }
@@ -2013,7 +2022,7 @@ function IconCraftView({
2013
2022
  rx: reticleSize * 0.15,
2014
2023
  ry: reticleSize * 0.5,
2015
2024
  fill: "none",
2016
- stroke: "var(--iconcraft-dial-color, #0071e3)",
2025
+ stroke: "var(--iconcraft-reticle-color, #000)",
2017
2026
  strokeWidth: "1",
2018
2027
  strokeDasharray: "3 2"
2019
2028
  }
@@ -2059,7 +2068,7 @@ function IconCraftView({
2059
2068
  rx: reticleSize * 0.5,
2060
2069
  ry: reticleSize * 0.3,
2061
2070
  fill: "none",
2062
- stroke: "var(--iconcraft-dial-color, #0071e3)",
2071
+ stroke: "var(--iconcraft-reticle-color, #000)",
2063
2072
  strokeWidth: "1.2",
2064
2073
  transform: `rotate(45, ${dialCenter}, ${dialCenter})`
2065
2074
  }
@@ -2070,7 +2079,7 @@ function IconCraftView({
2070
2079
  cx: dialCenter,
2071
2080
  cy: dialCenter,
2072
2081
  r: "2",
2073
- fill: "var(--iconcraft-dial-color, #0071e3)"
2082
+ fill: "var(--iconcraft-reticle-color, #000)"
2074
2083
  }
2075
2084
  )
2076
2085
  ] })
@@ -2098,22 +2107,33 @@ function IconCraftView({
2098
2107
  cy: dialCenter,
2099
2108
  r: ringRadius,
2100
2109
  fill: "none",
2101
- stroke: "var(--iconcraft-dial-color, #0071e3)",
2110
+ stroke: "var(--iconcraft-dial-color, #000)",
2102
2111
  strokeWidth: "2",
2103
2112
  strokeDasharray: "6 4",
2104
2113
  opacity: 0.7
2105
2114
  }
2106
2115
  ),
2116
+ /* @__PURE__ */ jsx2("style", { children: `
2117
+ .iconcraft-notch {
2118
+ transition: r 0.15s ease, filter 0.15s ease;
2119
+ filter: none;
2120
+ }
2121
+ .iconcraft-notch:hover {
2122
+ r: 9;
2123
+ filter: drop-shadow(0 0 3px rgba(0,0,0,0.4));
2124
+ }
2125
+ ` }),
2107
2126
  renderNotch ? renderNotch({ x: notchX, y: notchY, degrees: dialDegValue, onMouseDown: handleNotchMouseDown }) : /* @__PURE__ */ jsx2(
2108
2127
  "circle",
2109
2128
  {
2129
+ className: "iconcraft-notch",
2110
2130
  cx: notchX,
2111
2131
  cy: notchY,
2112
- r: "7",
2113
- fill: "var(--iconcraft-dial-color, #0071e3)",
2132
+ r: 7,
2133
+ fill: "var(--iconcraft-notch-color, #000)",
2114
2134
  stroke: "#fff",
2115
2135
  strokeWidth: "2",
2116
- style: { pointerEvents: "auto", cursor: "grab" },
2136
+ style: { pointerEvents: "auto", cursor: ROTATE_CURSOR },
2117
2137
  onMouseDown: handleNotchMouseDown
2118
2138
  }
2119
2139
  ),
@@ -2129,7 +2149,7 @@ function IconCraftView({
2129
2149
  y: labelY,
2130
2150
  textAnchor: "middle",
2131
2151
  dominantBaseline: "central",
2132
- fill: "var(--iconcraft-dial-color, #0071e3)",
2152
+ fill: "var(--iconcraft-dial-color, #000)",
2133
2153
  fontSize: "11",
2134
2154
  fontWeight: "600",
2135
2155
  fontFamily: "system-ui, sans-serif",
@@ -2250,7 +2270,8 @@ async function initWasm() {
2250
2270
  }
2251
2271
  var shapeModeMap2 = {
2252
2272
  jelly: 0,
2253
- droplet: 1,
2273
+ bubble: 1,
2274
+ sticker: 3,
2254
2275
  wax: 2
2255
2276
  };
2256
2277
  function isUrl(str) {
@@ -2466,7 +2487,8 @@ async function initWasm2() {
2466
2487
  }
2467
2488
  var shapeModeMap3 = {
2468
2489
  jelly: 0,
2469
- droplet: 1,
2490
+ bubble: 1,
2491
+ sticker: 3,
2470
2492
  wax: 2
2471
2493
  };
2472
2494
  function isUrl2(str) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gospelo-iconcraft-react",
3
- "version": "0.3.1",
3
+ "version": "0.3.5",
4
4
  "description": "IconCraft React components - 3D decorative icon shapes from SVG",
5
5
  "license": "MIT",
6
6
  "author": "gorosun <goro-hayakawa@no-studio.net>",
@@ -32,7 +32,7 @@
32
32
  "react-dom": ">=18.0.0"
33
33
  },
34
34
  "dependencies": {
35
- "gospelo-iconcraft-wasm": "^0.3.0",
35
+ "gospelo-iconcraft-wasm": "workspace:*",
36
36
  "ulid": "^3.0.2"
37
37
  },
38
38
  "devDependencies": {