@widgetic/canvas 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -0
- package/dist/canvas/Canvas.svelte +10678 -0
- package/dist/canvas/Canvas.svelte.d.ts +147 -0
- package/dist/canvas/CanvasToolbar.svelte +1422 -0
- package/dist/canvas/CanvasToolbar.svelte.d.ts +54 -0
- package/dist/canvas/ContextMenu.svelte +279 -0
- package/dist/canvas/ContextMenu.svelte.d.ts +36 -0
- package/dist/canvas/PanZoomPanel.svelte +315 -0
- package/dist/canvas/PanZoomPanel.svelte.d.ts +32 -0
- package/dist/canvas/canvasLogger.d.ts +5 -0
- package/dist/canvas/canvasLogger.js +19 -0
- package/dist/canvas/index.d.ts +13 -0
- package/dist/canvas/index.js +12 -0
- package/dist/canvas/props-panel/PropsPanel.svelte +1902 -0
- package/dist/canvas/props-panel/PropsPanel.svelte.d.ts +73 -0
- package/dist/canvas/props-panel/TextPropsICSection.svelte +238 -0
- package/dist/canvas/props-panel/TextPropsICSection.svelte.d.ts +36 -0
- package/dist/canvas/shapes/FrameShape.d.ts +97 -0
- package/dist/canvas/shapes/FrameShape.js +951 -0
- package/dist/canvas/shapes/ImageShape.d.ts +38 -0
- package/dist/canvas/shapes/ImageShape.js +245 -0
- package/dist/canvas/shapes/ShapeLibrary.d.ts +64 -0
- package/dist/canvas/shapes/ShapeLibrary.js +526 -0
- package/dist/canvas/shapes/WidgetShape.d.ts +21 -0
- package/dist/canvas/shapes/WidgetShape.js +132 -0
- package/dist/canvas/types.d.ts +26 -0
- package/dist/canvas/types.js +5 -0
- package/dist/components/Tooltip.svelte +179 -0
- package/dist/components/Tooltip.svelte.d.ts +21 -0
- package/dist/components/index.d.ts +11 -0
- package/dist/components/index.js +13 -0
- package/dist/components/input-controls/ColorIC.svelte +388 -0
- package/dist/components/input-controls/ColorIC.svelte.d.ts +22 -0
- package/dist/components/input-controls/FontSelectorIC.svelte +69 -0
- package/dist/components/input-controls/FontSelectorIC.svelte.d.ts +17 -0
- package/dist/components/input-controls/SliderUnitIC.svelte +217 -0
- package/dist/components/input-controls/SliderUnitIC.svelte.d.ts +24 -0
- package/dist/components/input-controls/TextAlignIC.svelte +84 -0
- package/dist/components/input-controls/TextAlignIC.svelte.d.ts +17 -0
- package/dist/components/input-controls/TextStyleIC.svelte +85 -0
- package/dist/components/input-controls/TextStyleIC.svelte.d.ts +21 -0
- package/dist/icons/arrow.svg +3 -0
- package/dist/icons/checkmark.svg +3 -0
- package/dist/icons/draw.svg +22 -0
- package/dist/icons/eraser.svg +23 -0
- package/dist/icons/hand.svg +6 -0
- package/dist/icons/select.svg +3 -0
- package/dist/icons/text.svg +5 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +12 -0
- package/package.json +101 -0
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ShapeLibrary.ts
|
|
3
|
+
*
|
|
4
|
+
* Pre-defined SVG path data for special shapes (media controls, UI icons, etc.).
|
|
5
|
+
* All paths are normalized to a 100×100 viewBox so Fabric.js scales them uniformly.
|
|
6
|
+
*
|
|
7
|
+
* cornerRadiusMode per shape:
|
|
8
|
+
* 'none' – shape already curved (circle, heart) → hide Corner Radius slider
|
|
9
|
+
* 'clipPath' – clip bounding-box rect to rounded rect (arrows, …)
|
|
10
|
+
* 'strokeRound' – apply round linecap / linejoin (line-only: check, X, +, -)
|
|
11
|
+
* 'pathRegen' – regenerate sub-shape path data with rounded corners (rects & polygons)
|
|
12
|
+
*/
|
|
13
|
+
// ── Path-regen helpers ────────────────────────────────────────────────────────
|
|
14
|
+
/** Generate SVG path data for a rounded rectangle. */
|
|
15
|
+
export function roundedRectPath(x, y, w, h, r) {
|
|
16
|
+
r = Math.min(r, w / 2, h / 2);
|
|
17
|
+
const x2 = x + w, y2 = y + h;
|
|
18
|
+
return [
|
|
19
|
+
`M ${x + r} ${y}`,
|
|
20
|
+
`L ${x2 - r} ${y}`, `Q ${x2} ${y} ${x2} ${y + r}`,
|
|
21
|
+
`L ${x2} ${y2 - r}`, `Q ${x2} ${y2} ${x2 - r} ${y2}`,
|
|
22
|
+
`L ${x + r} ${y2}`, `Q ${x} ${y2} ${x} ${y2 - r}`,
|
|
23
|
+
`L ${x} ${y + r}`, `Q ${x} ${y} ${x + r} ${y}`,
|
|
24
|
+
'Z',
|
|
25
|
+
].join(' ');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Generate SVG path data for a convex polygon with rounded corners.
|
|
29
|
+
* @param points – vertices in order (any winding)
|
|
30
|
+
* @param r – corner radius in path units
|
|
31
|
+
*/
|
|
32
|
+
export function roundedPolygonPath(points, r) {
|
|
33
|
+
if (r <= 0) {
|
|
34
|
+
return (points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p[0]} ${p[1]}`).join(' ') + ' Z');
|
|
35
|
+
}
|
|
36
|
+
const n = points.length;
|
|
37
|
+
// Cap r to 45% of the shortest edge to stay inside the polygon
|
|
38
|
+
let minEdge = Infinity;
|
|
39
|
+
for (let i = 0; i < n; i++) {
|
|
40
|
+
const a = points[i], b = points[(i + 1) % n];
|
|
41
|
+
const d = Math.sqrt((b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2);
|
|
42
|
+
if (d < minEdge)
|
|
43
|
+
minEdge = d;
|
|
44
|
+
}
|
|
45
|
+
const rClamped = Math.min(r, minEdge * 0.45);
|
|
46
|
+
let d = '';
|
|
47
|
+
for (let i = 0; i < n; i++) {
|
|
48
|
+
const prev = points[(i - 1 + n) % n];
|
|
49
|
+
const curr = points[i];
|
|
50
|
+
const next = points[(i + 1) % n];
|
|
51
|
+
// Unit vectors from corner toward its neighbours
|
|
52
|
+
const pLen = Math.sqrt((prev[0] - curr[0]) ** 2 + (prev[1] - curr[1]) ** 2) || 1;
|
|
53
|
+
const nLen = Math.sqrt((next[0] - curr[0]) ** 2 + (next[1] - curr[1]) ** 2) || 1;
|
|
54
|
+
const pn = [(prev[0] - curr[0]) / pLen, (prev[1] - curr[1]) / pLen];
|
|
55
|
+
const nn = [(next[0] - curr[0]) / nLen, (next[1] - curr[1]) / nLen];
|
|
56
|
+
// Points along each edge, distance rClamped from the corner vertex
|
|
57
|
+
const p1 = [curr[0] + pn[0] * rClamped, curr[1] + pn[1] * rClamped];
|
|
58
|
+
const p2 = [curr[0] + nn[0] * rClamped, curr[1] + nn[1] * rClamped];
|
|
59
|
+
d += i === 0
|
|
60
|
+
? `M ${p1[0].toFixed(2)} ${p1[1].toFixed(2)} `
|
|
61
|
+
: `L ${p1[0].toFixed(2)} ${p1[1].toFixed(2)} `;
|
|
62
|
+
d += `Q ${curr[0]} ${curr[1]} ${p2[0].toFixed(2)} ${p2[1].toFixed(2)} `;
|
|
63
|
+
}
|
|
64
|
+
return d + 'Z';
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Regenerate the full SVG path string for a library shape at the given roundness (0-100 %).
|
|
68
|
+
* Returns null for shapes that don't use pathRegen.
|
|
69
|
+
*/
|
|
70
|
+
export function generatePathForShape(shape, roundnessPercent) {
|
|
71
|
+
// Special case: curved-arrow has a Bezier-curve body that cannot be expressed as polygon
|
|
72
|
+
// regenData. Each L-command corner and the two C-to-L junctions are rounded individually
|
|
73
|
+
// using Quadratic Bezier arcs so the entire shape looks smooth at high roundness values.
|
|
74
|
+
if (shape.key === 'curved-arrow') {
|
|
75
|
+
const t = (roundnessPercent / 100) * 8; // max 8-unit radius at 100%
|
|
76
|
+
if (t < 0.1)
|
|
77
|
+
return null;
|
|
78
|
+
// Round a corner at (cx,cy): px/py is the INCOMING reference point, nx/ny is OUTGOING.
|
|
79
|
+
// For C commands use the adjacent control point as the reference direction.
|
|
80
|
+
// Returns { s: arcStart, e: arcEnd } strings for "…s Q cx cy e…" insertion.
|
|
81
|
+
const rc = (px, py, cx, cy, nx, ny) => {
|
|
82
|
+
const dx1 = px - cx, dy1 = py - cy;
|
|
83
|
+
const l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1) || 1;
|
|
84
|
+
const dx2 = nx - cx, dy2 = ny - cy;
|
|
85
|
+
const l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2) || 1;
|
|
86
|
+
const r = Math.min(t, l1 * 0.45, l2 * 0.45);
|
|
87
|
+
return {
|
|
88
|
+
s: `${(cx + dx1 / l1 * r).toFixed(1)} ${(cy + dy1 / l1 * r).toFixed(1)}`,
|
|
89
|
+
e: `${(cx + dx2 / l2 * r).toFixed(1)} ${(cy + dy2 / l2 * r).toFixed(1)}`,
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
// Corners with reference points for tangent direction:
|
|
93
|
+
// C0: M/Z corner at (80,20) — see detailed note below
|
|
94
|
+
// C1: arrowhead TIP (95,35) prev=(80,20) next=(80,50)
|
|
95
|
+
// C2: outer lower wing (80,50) prev=(95,35) next=(80,40)
|
|
96
|
+
// C3: inner shoulder (80,40) prev=(80,50) next=C cp1=(50,40)
|
|
97
|
+
// C4: C-to-L junction (30,80) prev=C cp2=(30,55) next=(15,80) [modifies C endpoint]
|
|
98
|
+
// C5: tail end (15,80) prev=(30,80) next=C cp1=(15,45)
|
|
99
|
+
//
|
|
100
|
+
// C0 corner at M(80,20) — ASYMMETRIC arc approach:
|
|
101
|
+
//
|
|
102
|
+
// Constraint: the Z-line (from C endpoint (80,25) to M(80,20)) is only 5px, so a symmetric
|
|
103
|
+
// rc() would cap the radius to 2.25px. Moving the C endpoint to get more room (previous
|
|
104
|
+
// attempts) either deformed the shape (moved horizontally) or changed the 0% base shape
|
|
105
|
+
// (moved vertically by changing pathData).
|
|
106
|
+
//
|
|
107
|
+
// Solution: use a fixed incoming side (C endpoint NEVER changes, always stays at (80,25))
|
|
108
|
+
// and a scaling outgoing side (M moves along the first-L edge toward (95,35) with t).
|
|
109
|
+
// — c0_s is always (80,25): the Z-line start, no modification to the C command at all.
|
|
110
|
+
// — c0_e scales with t: at t=0 → M=(80,20) = exact original path; at t=8 → M=(85.7,25.7).
|
|
111
|
+
// The Q arc from (80,25) Q(80,20) to c0_e smoothly rounds the top corner with up to 8px
|
|
112
|
+
// effective radius on the outgoing side, while the shape stays correct at any % level.
|
|
113
|
+
const r0 = Math.min(t, 21.2 * 0.45); // cap only by first-L edge length (21.2px × 0.45)
|
|
114
|
+
const c0_s = '80.0 25.0'; // original C endpoint — Z-line start, never moves
|
|
115
|
+
const c0_dirX = 15 / 21.2, c0_dirY = 15 / 21.2; // unit direction (80,20)→(95,35)
|
|
116
|
+
const c0_e = `${(80 + c0_dirX * r0).toFixed(1)} ${(20 + c0_dirY * r0).toFixed(1)}`;
|
|
117
|
+
const c1 = rc(80, 20, 95, 35, 80, 50); // arrowhead tip
|
|
118
|
+
const c2 = rc(95, 35, 80, 50, 80, 40); // outer lower arrowhead wing
|
|
119
|
+
const c3 = rc(80, 50, 80, 40, 50, 40); // inner shoulder (L→C, use C cp1 as direction)
|
|
120
|
+
const c4 = rc(30, 55, 30, 80, 15, 80); // C-to-L junction (C cp2 as incoming direction)
|
|
121
|
+
const c5 = rc(30, 80, 15, 80, 15, 45); // tail end (L→C, use C cp1 as direction)
|
|
122
|
+
return [
|
|
123
|
+
`M ${c0_e}`, // start at c0 arc end (Z closes back here)
|
|
124
|
+
`L ${c1.s} Q 95 35 ${c1.e}`, // arrowhead tip
|
|
125
|
+
`L ${c2.s} Q 80 50 ${c2.e}`, // outer lower arrowhead wing
|
|
126
|
+
`L ${c3.s} Q 80 40 ${c3.e}`, // inner shoulder
|
|
127
|
+
`C 50 40 30 55 ${c4.s} Q 30 80 ${c4.e}`, // curve body + C→L junction
|
|
128
|
+
`L ${c5.s} Q 15 80 ${c5.e}`, // tail end
|
|
129
|
+
`C 15 45 40 25 ${c0_s} Q 80 20 ${c0_e} Z`, // return C (endpoint unchanged) + M arc
|
|
130
|
+
].join(' ');
|
|
131
|
+
}
|
|
132
|
+
// ── Shield: quadratic-arc rounding of 5 angular corners, bezier bottom preserved ──
|
|
133
|
+
// Path: M 50 5 L 90 20 L 90 55 C 90 75 70 90 50 95 C 30 90 10 75 10 55 L 10 20 Z
|
|
134
|
+
// Corners: P0=(50,5) P1=(90,20) P2=(90,55) P4=(10,55) P5=(10,20)
|
|
135
|
+
// At 0%, returns null so the original pathData is used exactly (no shape change).
|
|
136
|
+
if (shape.key === 'shield') {
|
|
137
|
+
const maxT = 10; // max arc radius in path units at 100% roundness
|
|
138
|
+
const t = (roundnessPercent / 100) * maxT;
|
|
139
|
+
if (t < 0.1)
|
|
140
|
+
return null;
|
|
141
|
+
// rc: quadratic bezier arc at (cx,cy), optional tMax clamps radius for corners
|
|
142
|
+
// adjacent to bezier curves to limit distortion of the bezier endpoints.
|
|
143
|
+
const rc2 = (px, py, cx, cy, nx, ny, tMax = t) => {
|
|
144
|
+
const tEff = Math.min(t, tMax);
|
|
145
|
+
const dx1 = px - cx, dy1 = py - cy;
|
|
146
|
+
const l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1) || 1;
|
|
147
|
+
const dx2 = nx - cx, dy2 = ny - cy;
|
|
148
|
+
const l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2) || 1;
|
|
149
|
+
const r = Math.min(tEff, l1 * 0.45, l2 * 0.45);
|
|
150
|
+
return {
|
|
151
|
+
s: `${(cx + dx1 / l1 * r).toFixed(1)} ${(cy + dy1 / l1 * r).toFixed(1)}`,
|
|
152
|
+
e: `${(cx + dx2 / l2 * r).toFixed(1)} ${(cy + dy2 / l2 * r).toFixed(1)}`,
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
// c0: top point (50,5) — prev is (10,20) via Z-line, next is (90,20)
|
|
156
|
+
// c1: top-right shoulder (90,20) — prev (50,5), next (90,55)
|
|
157
|
+
// c2: right side base (90,55) — limited tMax=4 to minimize bezier bottom distortion
|
|
158
|
+
// c3: bottom point (50,95) — junction of two bezier curves; reference points are
|
|
159
|
+
// the last cp of C1 (70,90) and the first cp of C2 (30,90)
|
|
160
|
+
// c4: left side base (10,55) — limited tMax=4 to minimize bezier bottom distortion
|
|
161
|
+
// c5: top-left shoulder (10,20) — prev (10,55), next (50,5)
|
|
162
|
+
const c0 = rc2(10, 20, 50, 5, 90, 20);
|
|
163
|
+
const c1 = rc2(50, 5, 90, 20, 90, 55);
|
|
164
|
+
const c2 = rc2(90, 20, 90, 55, 90, 75, 4); // cap arc size near bezier
|
|
165
|
+
const c3 = rc2(70, 90, 50, 95, 30, 90, 6); // bottom point (bezier junction, cap=6)
|
|
166
|
+
const c4 = rc2(10, 75, 10, 55, 10, 20, 4); // cap arc size near bezier
|
|
167
|
+
const c5 = rc2(10, 55, 10, 20, 50, 5);
|
|
168
|
+
// The top corner arc (c0) is placed LAST, just before Z, so the path visits:
|
|
169
|
+
// M(c0.e) → c1 arc → c2 arc → bezier1 (shortened to c3.s) → c3 Q arc →
|
|
170
|
+
// bezier2 (from c3.e, shortened) → c4 arc → c5 arc → c0 arc → Z
|
|
171
|
+
return [
|
|
172
|
+
`M ${c0.e}`, // start past top-point toward P1
|
|
173
|
+
`L ${c1.s} Q 90 20 ${c1.e}`, // arc at top-right shoulder
|
|
174
|
+
`L ${c2.s} Q 90 55 ${c2.e}`, // arc at right base (limited radius)
|
|
175
|
+
`C 90 75 70 90 ${c3.s}`, // bottom-right bezier, shortened to c3 arc entry
|
|
176
|
+
`Q 50 95 ${c3.e}`, // arc at bottom point
|
|
177
|
+
`C 30 90 10 75 ${c4.s}`, // bottom-left bezier, shortened to c4 arc entry
|
|
178
|
+
`Q 10 55 ${c4.e}`, // arc at left base (limited radius)
|
|
179
|
+
`L ${c5.s} Q 10 20 ${c5.e}`, // arc at top-left shoulder
|
|
180
|
+
`L ${c0.s} Q 50 5 ${c0.e}`, // arc at top point
|
|
181
|
+
`Z`, // zero-distance close back to M (c0.e)
|
|
182
|
+
].join(' ');
|
|
183
|
+
}
|
|
184
|
+
if (shape.cornerRadiusMode !== 'pathRegen' || !shape.regenData)
|
|
185
|
+
return null;
|
|
186
|
+
return shape.regenData.map(sub => {
|
|
187
|
+
if (sub.type === 'fixed')
|
|
188
|
+
return sub.path;
|
|
189
|
+
if (sub.type === 'rect') {
|
|
190
|
+
const maxR = Math.min(sub.w, sub.h) / 2;
|
|
191
|
+
const r = (roundnessPercent / 100) * maxR;
|
|
192
|
+
return roundedRectPath(sub.x, sub.y, sub.w, sub.h, r);
|
|
193
|
+
}
|
|
194
|
+
// polygon
|
|
195
|
+
const pts = sub.points;
|
|
196
|
+
const n = pts.length;
|
|
197
|
+
let minEdge = Infinity;
|
|
198
|
+
for (let i = 0; i < n; i++) {
|
|
199
|
+
const a = pts[i], b = pts[(i + 1) % n];
|
|
200
|
+
minEdge = Math.min(minEdge, Math.sqrt((b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2));
|
|
201
|
+
}
|
|
202
|
+
const maxR = minEdge * 0.45;
|
|
203
|
+
const r = (roundnessPercent / 100) * maxR;
|
|
204
|
+
return roundedPolygonPath(pts, r);
|
|
205
|
+
}).join(' ');
|
|
206
|
+
}
|
|
207
|
+
// ── Geometric Shapes (library) ─────────────────────────────────────────────
|
|
208
|
+
const GEOMETRIC_SHAPES = [
|
|
209
|
+
{
|
|
210
|
+
key: 'hexagon',
|
|
211
|
+
label: 'Hexagon',
|
|
212
|
+
category: 'Geometric Shapes',
|
|
213
|
+
pathData: 'M 50 3 L 93 27 L 93 73 L 50 97 L 7 73 L 7 27 Z',
|
|
214
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><polygon points="12,2 21.5,7 21.5,17 12,22 2.5,17 2.5,7" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>`,
|
|
215
|
+
cornerRadiusMode: 'pathRegen',
|
|
216
|
+
regenData: [
|
|
217
|
+
{ type: 'polygon', points: [[50, 3], [93, 27], [93, 73], [50, 97], [7, 73], [7, 27]] },
|
|
218
|
+
],
|
|
219
|
+
lockAspectRatio: true,
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
key: 'oval',
|
|
223
|
+
label: 'Oval',
|
|
224
|
+
category: 'Geometric Shapes',
|
|
225
|
+
// Capsule/stadium shape: wide rounded rect with max corner radius (half the height).
|
|
226
|
+
// 160×90 content area, corner radius = 45 (half of 90). Produces semicircle caps like a pill.
|
|
227
|
+
pathData: 'M 50 5 L 110 5 Q 155 5 155 50 L 155 50 Q 155 95 110 95 L 50 95 Q 5 95 5 50 L 5 50 Q 5 5 50 5 Z',
|
|
228
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="7" fill="none" stroke="currentColor" stroke-width="1.5"/></svg>`,
|
|
229
|
+
cornerRadiusMode: 'none',
|
|
230
|
+
lockAspectRatio: false,
|
|
231
|
+
},
|
|
232
|
+
];
|
|
233
|
+
// ── Media Icons ──────────────────────────────────────────────────────────────
|
|
234
|
+
const MEDIA_SHAPES = [
|
|
235
|
+
{
|
|
236
|
+
key: 'play',
|
|
237
|
+
label: 'Play',
|
|
238
|
+
category: 'Media Icons',
|
|
239
|
+
pathData: 'M 10 5 L 10 95 L 90 50 Z',
|
|
240
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><polygon points="5,3 5,21 20,12" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>`,
|
|
241
|
+
cornerRadiusMode: 'pathRegen',
|
|
242
|
+
regenData: [
|
|
243
|
+
{ type: 'polygon', points: [[10, 5], [10, 95], [90, 50]] },
|
|
244
|
+
],
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
key: 'pause',
|
|
248
|
+
label: 'Pause',
|
|
249
|
+
category: 'Media Icons',
|
|
250
|
+
pathData: 'M 15 5 L 15 95 L 35 95 L 35 5 Z M 65 5 L 65 95 L 85 95 L 85 5 Z',
|
|
251
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><rect x="6" y="4" width="4" height="16" rx="1" fill="none" stroke="currentColor" stroke-width="1.5"/><rect x="14" y="4" width="4" height="16" rx="1" fill="none" stroke="currentColor" stroke-width="1.5"/></svg>`,
|
|
252
|
+
cornerRadiusMode: 'pathRegen',
|
|
253
|
+
regenData: [
|
|
254
|
+
{ type: 'rect', x: 15, y: 5, w: 20, h: 90 },
|
|
255
|
+
{ type: 'rect', x: 65, y: 5, w: 20, h: 90 },
|
|
256
|
+
],
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
key: 'stop',
|
|
260
|
+
label: 'Stop',
|
|
261
|
+
category: 'Media Icons',
|
|
262
|
+
pathData: 'M 10 10 L 10 90 L 90 90 L 90 10 Z',
|
|
263
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><rect x="5" y="5" width="14" height="14" rx="1.5" fill="none" stroke="currentColor" stroke-width="1.5"/></svg>`,
|
|
264
|
+
cornerRadiusMode: 'pathRegen',
|
|
265
|
+
regenData: [
|
|
266
|
+
{ type: 'rect', x: 10, y: 10, w: 80, h: 80 },
|
|
267
|
+
],
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
key: 'rewind',
|
|
271
|
+
label: 'Rewind',
|
|
272
|
+
category: 'Media Icons',
|
|
273
|
+
pathData: 'M 50 5 L 50 95 L 5 50 Z M 95 5 L 95 95 L 50 50 Z',
|
|
274
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><polygon points="12,3 12,21 3,12" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><polygon points="21,3 21,21 12,12" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>`,
|
|
275
|
+
cornerRadiusMode: 'pathRegen',
|
|
276
|
+
regenData: [
|
|
277
|
+
{ type: 'polygon', points: [[50, 5], [50, 95], [5, 50]] },
|
|
278
|
+
{ type: 'polygon', points: [[95, 5], [95, 95], [50, 50]] },
|
|
279
|
+
],
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
key: 'fast-forward',
|
|
283
|
+
label: 'Fast Forward',
|
|
284
|
+
category: 'Media Icons',
|
|
285
|
+
pathData: 'M 5 5 L 5 95 L 50 50 Z M 50 5 L 50 95 L 95 50 Z',
|
|
286
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><polygon points="3,3 3,21 12,12" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><polygon points="12,3 12,21 21,12" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>`,
|
|
287
|
+
cornerRadiusMode: 'pathRegen',
|
|
288
|
+
regenData: [
|
|
289
|
+
{ type: 'polygon', points: [[5, 5], [5, 95], [50, 50]] },
|
|
290
|
+
{ type: 'polygon', points: [[50, 5], [50, 95], [95, 50]] },
|
|
291
|
+
],
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
key: 'record',
|
|
295
|
+
label: 'Record',
|
|
296
|
+
category: 'Media Icons',
|
|
297
|
+
// Already a circle — no corner rounding needed
|
|
298
|
+
pathData: 'M 50 5 A 45 45 0 1 1 49.999 5 Z',
|
|
299
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><circle cx="12" cy="12" r="7" fill="none" stroke="currentColor" stroke-width="1.5"/></svg>`,
|
|
300
|
+
cornerRadiusMode: 'none',
|
|
301
|
+
},
|
|
302
|
+
];
|
|
303
|
+
// ── Arrow Shapes ─────────────────────────────────────────────────────────────
|
|
304
|
+
const ARROW_SHAPES = [
|
|
305
|
+
{
|
|
306
|
+
key: 'arrow-right',
|
|
307
|
+
label: 'Arrow Right',
|
|
308
|
+
category: 'Arrow Shapes',
|
|
309
|
+
pathData: 'M 5 40 L 65 40 L 65 20 L 95 50 L 65 80 L 65 60 L 5 60 Z',
|
|
310
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M5 12 H19 M13 6 L19 12 L13 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
311
|
+
cornerRadiusMode: 'pathRegen',
|
|
312
|
+
regenData: [{ type: 'polygon', points: [[5, 40], [65, 40], [65, 20], [95, 50], [65, 80], [65, 60], [5, 60]] }],
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
key: 'arrow-left',
|
|
316
|
+
label: 'Arrow Left',
|
|
317
|
+
category: 'Arrow Shapes',
|
|
318
|
+
pathData: 'M 95 40 L 35 40 L 35 20 L 5 50 L 35 80 L 35 60 L 95 60 Z',
|
|
319
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M19 12 H5 M11 6 L5 12 L11 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
320
|
+
cornerRadiusMode: 'pathRegen',
|
|
321
|
+
regenData: [{ type: 'polygon', points: [[95, 40], [35, 40], [35, 20], [5, 50], [35, 80], [35, 60], [95, 60]] }],
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
key: 'arrow-up',
|
|
325
|
+
label: 'Arrow Up',
|
|
326
|
+
category: 'Arrow Shapes',
|
|
327
|
+
pathData: 'M 40 95 L 40 35 L 20 35 L 50 5 L 80 35 L 60 35 L 60 95 Z',
|
|
328
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M12 19 V5 M6 11 L12 5 L18 11" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
329
|
+
cornerRadiusMode: 'pathRegen',
|
|
330
|
+
regenData: [{ type: 'polygon', points: [[40, 95], [40, 35], [20, 35], [50, 5], [80, 35], [60, 35], [60, 95]] }],
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
key: 'arrow-down',
|
|
334
|
+
label: 'Arrow Down',
|
|
335
|
+
category: 'Arrow Shapes',
|
|
336
|
+
pathData: 'M 60 5 L 60 65 L 80 65 L 50 95 L 20 65 L 40 65 L 40 5 Z',
|
|
337
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M12 5 V19 M6 13 L12 19 L18 13" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
338
|
+
cornerRadiusMode: 'pathRegen',
|
|
339
|
+
regenData: [{ type: 'polygon', points: [[60, 5], [60, 65], [80, 65], [50, 95], [20, 65], [40, 65], [40, 5]] }],
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
key: 'double-arrow',
|
|
343
|
+
label: 'Double Arrow',
|
|
344
|
+
category: 'Arrow Shapes',
|
|
345
|
+
pathData: 'M 30 20 L 5 50 L 30 80 L 30 62 L 70 62 L 70 80 L 95 50 L 70 20 L 70 38 L 30 38 Z',
|
|
346
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M7 12 H17 M5 8 L1 12 L5 16 M19 8 L23 12 L19 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
347
|
+
cornerRadiusMode: 'pathRegen',
|
|
348
|
+
regenData: [{ type: 'polygon', points: [[30, 20], [5, 50], [30, 80], [30, 62], [70, 62], [70, 80], [95, 50], [70, 20], [70, 38], [30, 38]] }],
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
key: 'curved-arrow',
|
|
352
|
+
label: 'Curved Arrow',
|
|
353
|
+
category: 'Arrow Shapes',
|
|
354
|
+
// Uses pathRegen mode: generatePathForShape() has a special case for 'curved-arrow'
|
|
355
|
+
// that rounds only the arrowhead tip via a Quadratic Bezier. clipPath was avoided
|
|
356
|
+
// because it clips the bounding box, cutting off arrowhead tips at high roundness.
|
|
357
|
+
// strokeRound was avoided because it only affects the stroke outline, not the fill corners.
|
|
358
|
+
pathData: 'M 80 20 L 95 35 L 80 50 L 80 40 C 50 40 30 55 30 80 L 15 80 C 15 45 40 25 80 25 Z',
|
|
359
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M17 4 L21 8 L17 12 M21 8 C15 8 9 11 9 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
360
|
+
cornerRadiusMode: 'pathRegen',
|
|
361
|
+
// regenData is intentionally omitted — generatePathForShape handles this shape directly.
|
|
362
|
+
},
|
|
363
|
+
];
|
|
364
|
+
// ── UI Icons ─────────────────────────────────────────────────────────────────
|
|
365
|
+
const UI_ICON_SHAPES = [
|
|
366
|
+
{
|
|
367
|
+
key: 'check',
|
|
368
|
+
label: 'Check Mark',
|
|
369
|
+
category: 'UI Icons',
|
|
370
|
+
// Open path — round by setting strokeLineCap/strokeLineJoin
|
|
371
|
+
pathData: 'M 5 55 L 35 85 L 95 15',
|
|
372
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M4 13 L9 18 L20 6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
373
|
+
cornerRadiusMode: 'strokeRound',
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
key: 'cross',
|
|
377
|
+
label: 'Cross / X',
|
|
378
|
+
category: 'UI Icons',
|
|
379
|
+
pathData: 'M 10 10 L 90 90 M 90 10 L 10 90',
|
|
380
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M5 5 L19 19 M19 5 L5 19" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>`,
|
|
381
|
+
cornerRadiusMode: 'strokeRound',
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
key: 'plus',
|
|
385
|
+
label: 'Plus',
|
|
386
|
+
category: 'UI Icons',
|
|
387
|
+
pathData: 'M 50 10 L 50 90 M 10 50 L 90 50',
|
|
388
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M12 5 V19 M5 12 H19" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>`,
|
|
389
|
+
cornerRadiusMode: 'strokeRound',
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
key: 'minus',
|
|
393
|
+
label: 'Minus',
|
|
394
|
+
category: 'UI Icons',
|
|
395
|
+
pathData: 'M 10 50 L 90 50',
|
|
396
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M5 12 H19" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>`,
|
|
397
|
+
cornerRadiusMode: 'strokeRound',
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
key: 'info',
|
|
401
|
+
label: 'Info',
|
|
402
|
+
category: 'UI Icons',
|
|
403
|
+
// Outer circle is already curved — no corner rounding
|
|
404
|
+
pathData: 'M 50 5 A 45 45 0 1 1 49.999 5 Z M 50 30 L 50 32 M 50 42 L 50 72',
|
|
405
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M12 8 V8.5 M12 11 V16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>`,
|
|
406
|
+
cornerRadiusMode: 'none',
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
key: 'warning',
|
|
410
|
+
label: 'Warning',
|
|
411
|
+
category: 'UI Icons',
|
|
412
|
+
// Outer triangle + inner ! lines (inner lines kept fixed)
|
|
413
|
+
pathData: 'M 50 5 L 95 90 L 5 90 Z M 50 40 L 50 60 M 50 70 L 50 72',
|
|
414
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M12 3 L22 20 H2 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M12 10 V14 M12 17 V17.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>`,
|
|
415
|
+
cornerRadiusMode: 'pathRegen',
|
|
416
|
+
regenData: [
|
|
417
|
+
{ type: 'polygon', points: [[50, 5], [95, 90], [5, 90]] },
|
|
418
|
+
{ type: 'fixed', path: 'M 50 40 L 50 60 M 50 70 L 50 72' },
|
|
419
|
+
],
|
|
420
|
+
},
|
|
421
|
+
];
|
|
422
|
+
// ── Symbol Shapes ─────────────────────────────────────────────────────────────
|
|
423
|
+
const SYMBOL_SHAPES = [
|
|
424
|
+
{
|
|
425
|
+
key: 'star',
|
|
426
|
+
label: 'Star',
|
|
427
|
+
category: 'Symbol Shapes',
|
|
428
|
+
pathData: 'M 50 5 L 61 35 L 95 35 L 68 57 L 79 91 L 50 70 L 21 91 L 32 57 L 5 35 L 39 35 Z',
|
|
429
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><polygon points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>`,
|
|
430
|
+
// pathRegen rounds each polygon corner individually (outer tips + inner concave vertices)
|
|
431
|
+
cornerRadiusMode: 'pathRegen',
|
|
432
|
+
regenData: [
|
|
433
|
+
{ type: 'polygon', points: [[50, 5], [61, 35], [95, 35], [68, 57], [79, 91], [50, 70], [21, 91], [32, 57], [5, 35], [39, 35]] },
|
|
434
|
+
],
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
key: 'heart',
|
|
438
|
+
label: 'Heart',
|
|
439
|
+
category: 'Symbol Shapes',
|
|
440
|
+
// Already curved — no corner rounding
|
|
441
|
+
pathData: 'M 50 85 C 5 55 5 20 25 15 C 35 12 45 17 50 25 C 55 17 65 12 75 15 C 95 20 95 55 50 85 Z',
|
|
442
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M12 21C12 21 3 14 3 8.5A4.5 4.5 0 0 1 12 6.5 4.5 4.5 0 0 1 21 8.5C21 14 12 21 12 21Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>`,
|
|
443
|
+
cornerRadiusMode: 'none',
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
key: 'speech-bubble',
|
|
447
|
+
label: 'Speech Bubble',
|
|
448
|
+
category: 'Symbol Shapes',
|
|
449
|
+
pathData: 'M 10 10 L 10 65 L 35 65 L 50 90 L 65 65 L 90 65 L 90 10 Z',
|
|
450
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M4 4 H20 A2 2 0 0 1 22 6 V15 A2 2 0 0 1 20 17 H9 L4 21 V17 A2 2 0 0 1 2 15 V6 A2 2 0 0 1 4 4 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>`,
|
|
451
|
+
// pathRegen rounds box corners; the tail corner also softens slightly
|
|
452
|
+
cornerRadiusMode: 'pathRegen',
|
|
453
|
+
regenData: [
|
|
454
|
+
{ type: 'polygon', points: [[10, 10], [10, 65], [35, 65], [50, 90], [65, 65], [90, 65], [90, 10]] },
|
|
455
|
+
],
|
|
456
|
+
// Speech bubbles are intentionally rectangular — allow free resize to set width/height
|
|
457
|
+
lockAspectRatio: false,
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
key: 'diamond',
|
|
461
|
+
label: 'Rhombus',
|
|
462
|
+
category: 'Symbol Shapes',
|
|
463
|
+
pathData: 'M 50 5 L 95 50 L 50 95 L 5 50 Z',
|
|
464
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><polygon points="12,2 22,12 12,22 2,12" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>`,
|
|
465
|
+
cornerRadiusMode: 'pathRegen',
|
|
466
|
+
regenData: [
|
|
467
|
+
{ type: 'polygon', points: [[50, 5], [95, 50], [50, 95], [5, 50]] },
|
|
468
|
+
],
|
|
469
|
+
lockAspectRatio: false,
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
key: 'lightning',
|
|
473
|
+
label: 'Lightning',
|
|
474
|
+
category: 'Symbol Shapes',
|
|
475
|
+
pathData: 'M 55 5 L 20 52 L 45 52 L 38 95 L 80 42 L 55 42 Z',
|
|
476
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M13 2 L5 13 H11 L9 22 L19 9 H13 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>`,
|
|
477
|
+
cornerRadiusMode: 'pathRegen',
|
|
478
|
+
regenData: [
|
|
479
|
+
{ type: 'polygon', points: [[55, 5], [20, 52], [45, 52], [38, 95], [80, 42], [55, 42]] },
|
|
480
|
+
],
|
|
481
|
+
lockAspectRatio: false,
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
key: 'shield',
|
|
485
|
+
label: 'Shield',
|
|
486
|
+
category: 'Symbol Shapes',
|
|
487
|
+
pathData: 'M 50 5 L 90 20 L 90 55 C 90 75 70 90 50 95 C 30 90 10 75 10 55 L 10 20 Z',
|
|
488
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M12 3 L20 7 V13 C20 17.5 16.5 21 12 22 C7.5 21 4 17.5 4 13 V7 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>`,
|
|
489
|
+
cornerRadiusMode: 'pathRegen',
|
|
490
|
+
lockAspectRatio: false,
|
|
491
|
+
},
|
|
492
|
+
];
|
|
493
|
+
/** All library shapes, grouped by category (Geometric first, then Arrows) */
|
|
494
|
+
export const SHAPE_LIBRARY = [
|
|
495
|
+
...GEOMETRIC_SHAPES,
|
|
496
|
+
...ARROW_SHAPES,
|
|
497
|
+
...MEDIA_SHAPES,
|
|
498
|
+
...UI_ICON_SHAPES,
|
|
499
|
+
...SYMBOL_SHAPES,
|
|
500
|
+
];
|
|
501
|
+
/**
|
|
502
|
+
* Keyed shape index: single canonical id→shape lookup, built once at module
|
|
503
|
+
* load. Replaces the repeated `SHAPE_LIBRARY.find(s => s.key === ...)`
|
|
504
|
+
* linear scans across Canvas.svelte. Keys are unique by construction
|
|
505
|
+
* (categories partition the array); a duplicate would be an authoring bug.
|
|
506
|
+
*/
|
|
507
|
+
const SHAPE_LIBRARY_BY_KEY = new Map(SHAPE_LIBRARY.map(shape => [shape.key, shape]));
|
|
508
|
+
/**
|
|
509
|
+
* Look up a library shape definition by its stable key.
|
|
510
|
+
* Returns undefined for unknown keys (dynamic/legacy shape keys).
|
|
511
|
+
*/
|
|
512
|
+
export function getShapeByKey(key) {
|
|
513
|
+
if (!key)
|
|
514
|
+
return undefined;
|
|
515
|
+
return SHAPE_LIBRARY_BY_KEY.get(key);
|
|
516
|
+
}
|
|
517
|
+
/** Shapes grouped by category for rendering in the UI */
|
|
518
|
+
export function getShapesByCategory() {
|
|
519
|
+
const map = new Map();
|
|
520
|
+
for (const shape of SHAPE_LIBRARY) {
|
|
521
|
+
const existing = map.get(shape.category) ?? [];
|
|
522
|
+
existing.push(shape);
|
|
523
|
+
map.set(shape.category, existing);
|
|
524
|
+
}
|
|
525
|
+
return map;
|
|
526
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ImageShape } from './ImageShape';
|
|
2
|
+
export declare const WIDGET_SHAPE_EXTRA_PROPS: string[];
|
|
3
|
+
export declare const WIDGET_SHAPE_CUSTOM_PROPS: string[];
|
|
4
|
+
export declare class WidgetShape extends ImageShape {
|
|
5
|
+
static type: string;
|
|
6
|
+
_isWidgetImage: boolean;
|
|
7
|
+
_widgetId: string | null;
|
|
8
|
+
_widgetName: string | null;
|
|
9
|
+
/** True when _widgetId no longer exists in the host widget list (deleted or moved). */
|
|
10
|
+
_widgetOrphaned: boolean;
|
|
11
|
+
/** Custom placeholder overlay: replace generic "Drop image" text with the widget name. */
|
|
12
|
+
_render(ctx: CanvasRenderingContext2D): void;
|
|
13
|
+
private _renderWidgetPlaceholder;
|
|
14
|
+
toObject(propertiesToInclude?: any[]): any;
|
|
15
|
+
static fromObject(object: Record<string, unknown>, options?: Record<string, unknown>): Promise<WidgetShape>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Upgrades an ImageShape to a WidgetShape by swapping the prototype chain.
|
|
19
|
+
* Called when user clicks "Convert to Widget".
|
|
20
|
+
*/
|
|
21
|
+
export declare function upgradeToWidgetShape(obj: any): WidgetShape | null;
|