@pooder/kit 2.0.0 → 3.0.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/CHANGELOG.md +11 -0
- package/dist/index.d.mts +246 -134
- package/dist/index.d.ts +246 -134
- package/dist/index.js +2051 -1045
- package/dist/index.mjs +2042 -1050
- package/package.json +3 -2
- package/src/CanvasService.ts +65 -0
- package/src/background.ts +156 -109
- package/src/coordinate.ts +49 -0
- package/src/dieline.ts +536 -336
- package/src/film.ts +120 -89
- package/src/geometry.ts +251 -38
- package/src/hole.ts +422 -286
- package/src/image.ts +374 -174
- package/src/index.ts +1 -0
- package/src/mirror.ts +86 -49
- package/src/ruler.ts +188 -118
- package/src/tracer.ts +372 -0
- package/src/white-ink.ts +186 -142
package/src/white-ink.ts
CHANGED
|
@@ -1,56 +1,146 @@
|
|
|
1
1
|
import {
|
|
2
|
-
Command,
|
|
3
|
-
Editor,
|
|
4
|
-
EditorState,
|
|
5
|
-
EventHandler,
|
|
6
2
|
Extension,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
PooderLayer,
|
|
3
|
+
ExtensionContext,
|
|
4
|
+
ContributionPointIds,
|
|
5
|
+
CommandContribution,
|
|
6
|
+
ConfigurationContribution,
|
|
12
7
|
} from "@pooder/core";
|
|
8
|
+
import { FabricImage as Image, filters } from "fabric";
|
|
9
|
+
import CanvasService from "./CanvasService";
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
opacity: number;
|
|
17
|
-
enableClip: boolean;
|
|
18
|
-
}
|
|
19
|
-
export class WhiteInkTool implements Extension<WhiteInkToolOptions> {
|
|
20
|
-
public name = "WhiteInkTool";
|
|
21
|
-
public options: WhiteInkToolOptions = {
|
|
22
|
-
customMask: "",
|
|
23
|
-
opacity: 1,
|
|
24
|
-
enableClip: false,
|
|
25
|
-
};
|
|
11
|
+
export class WhiteInkTool implements Extension {
|
|
12
|
+
id = "pooder.kit.white-ink";
|
|
26
13
|
|
|
27
|
-
public
|
|
28
|
-
|
|
29
|
-
opacity: { type: "number", min: 0, max: 1, step: 0.01, label: "Opacity" },
|
|
30
|
-
enableClip: { type: "boolean", label: "Enable Clip" },
|
|
14
|
+
public metadata = {
|
|
15
|
+
name: "WhiteInkTool",
|
|
31
16
|
};
|
|
32
17
|
|
|
33
|
-
private
|
|
18
|
+
private customMask: string = "";
|
|
19
|
+
private opacity: number = 1;
|
|
20
|
+
private enableClip: boolean = false;
|
|
34
21
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
22
|
+
private canvasService?: CanvasService;
|
|
23
|
+
private syncHandler: ((e: any) => void) | undefined;
|
|
24
|
+
private _loadingUrl: string | null = null;
|
|
25
|
+
|
|
26
|
+
constructor(
|
|
27
|
+
options?: Partial<{
|
|
28
|
+
customMask: string;
|
|
29
|
+
opacity: number;
|
|
30
|
+
enableClip: boolean;
|
|
31
|
+
}>,
|
|
32
|
+
) {
|
|
33
|
+
if (options) {
|
|
34
|
+
Object.assign(this, options);
|
|
35
|
+
}
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
|
|
41
|
-
this.
|
|
38
|
+
activate(context: ExtensionContext) {
|
|
39
|
+
this.canvasService = context.services.get<CanvasService>("CanvasService");
|
|
40
|
+
if (!this.canvasService) {
|
|
41
|
+
console.warn("CanvasService not found for WhiteInkTool");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const configService = context.services.get<any>("ConfigurationService");
|
|
46
|
+
if (configService) {
|
|
47
|
+
// Load initial config
|
|
48
|
+
this.customMask = configService.get(
|
|
49
|
+
"whiteInk.customMask",
|
|
50
|
+
this.customMask,
|
|
51
|
+
);
|
|
52
|
+
this.opacity = configService.get("whiteInk.opacity", this.opacity);
|
|
53
|
+
this.enableClip = configService.get(
|
|
54
|
+
"whiteInk.enableClip",
|
|
55
|
+
this.enableClip,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Listen for changes
|
|
59
|
+
configService.onAnyChange((e: { key: string; value: any }) => {
|
|
60
|
+
if (e.key.startsWith("whiteInk.")) {
|
|
61
|
+
const prop = e.key.split(".")[1];
|
|
62
|
+
console.log(
|
|
63
|
+
`[WhiteInkTool] Config change detected: ${e.key} -> ${e.value}`,
|
|
64
|
+
);
|
|
65
|
+
if (prop && prop in this) {
|
|
66
|
+
(this as any)[prop] = e.value;
|
|
67
|
+
this.updateWhiteInk();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
this.setup();
|
|
74
|
+
this.updateWhiteInk();
|
|
42
75
|
}
|
|
43
76
|
|
|
44
|
-
|
|
45
|
-
this.teardown(
|
|
77
|
+
deactivate(context: ExtensionContext) {
|
|
78
|
+
this.teardown();
|
|
79
|
+
this.canvasService = undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
contribute() {
|
|
83
|
+
return {
|
|
84
|
+
[ContributionPointIds.CONFIGURATIONS]: [
|
|
85
|
+
{
|
|
86
|
+
id: "whiteInk.customMask",
|
|
87
|
+
type: "string",
|
|
88
|
+
label: "Custom Mask URL",
|
|
89
|
+
default: "",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: "whiteInk.opacity",
|
|
93
|
+
type: "number",
|
|
94
|
+
label: "Opacity",
|
|
95
|
+
min: 0,
|
|
96
|
+
max: 1,
|
|
97
|
+
step: 0.01,
|
|
98
|
+
default: 1,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: "whiteInk.enableClip",
|
|
102
|
+
type: "boolean",
|
|
103
|
+
label: "Enable Clip",
|
|
104
|
+
default: false,
|
|
105
|
+
},
|
|
106
|
+
] as ConfigurationContribution[],
|
|
107
|
+
[ContributionPointIds.COMMANDS]: [
|
|
108
|
+
{
|
|
109
|
+
command: "setWhiteInkImage",
|
|
110
|
+
title: "Set White Ink Image",
|
|
111
|
+
handler: (
|
|
112
|
+
customMask: string,
|
|
113
|
+
opacity: number,
|
|
114
|
+
enableClip: boolean = true,
|
|
115
|
+
) => {
|
|
116
|
+
if (
|
|
117
|
+
this.customMask === customMask &&
|
|
118
|
+
this.opacity === opacity &&
|
|
119
|
+
this.enableClip === enableClip
|
|
120
|
+
)
|
|
121
|
+
return true;
|
|
122
|
+
|
|
123
|
+
this.customMask = customMask;
|
|
124
|
+
this.opacity = opacity;
|
|
125
|
+
this.enableClip = enableClip;
|
|
126
|
+
|
|
127
|
+
this.updateWhiteInk();
|
|
128
|
+
return true;
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
] as CommandContribution[],
|
|
132
|
+
};
|
|
46
133
|
}
|
|
47
134
|
|
|
48
|
-
private setup(
|
|
49
|
-
|
|
135
|
+
private setup() {
|
|
136
|
+
if (!this.canvasService) return;
|
|
137
|
+
const canvas = this.canvasService.canvas;
|
|
138
|
+
|
|
139
|
+
let userLayer = this.canvasService.getLayer("user");
|
|
50
140
|
if (!userLayer) {
|
|
51
|
-
userLayer =
|
|
52
|
-
width:
|
|
53
|
-
height:
|
|
141
|
+
userLayer = this.canvasService.createLayer("user", {
|
|
142
|
+
width: canvas.width,
|
|
143
|
+
height: canvas.height,
|
|
54
144
|
left: 0,
|
|
55
145
|
top: 0,
|
|
56
146
|
originX: "left",
|
|
@@ -59,114 +149,65 @@ export class WhiteInkTool implements Extension<WhiteInkToolOptions> {
|
|
|
59
149
|
evented: true,
|
|
60
150
|
subTargetCheck: true,
|
|
61
151
|
interactive: true,
|
|
62
|
-
data: {
|
|
63
|
-
id: "user",
|
|
64
|
-
},
|
|
65
152
|
});
|
|
66
|
-
|
|
153
|
+
canvas.add(userLayer);
|
|
67
154
|
}
|
|
68
155
|
|
|
69
156
|
if (!this.syncHandler) {
|
|
70
157
|
this.syncHandler = (e: any) => {
|
|
71
158
|
const target = e.target;
|
|
72
159
|
if (target && target.data?.id === "user-image") {
|
|
73
|
-
this.syncWithUserImage(
|
|
160
|
+
this.syncWithUserImage();
|
|
74
161
|
}
|
|
75
162
|
};
|
|
76
163
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
164
|
+
canvas.on("object:moving", this.syncHandler);
|
|
165
|
+
canvas.on("object:scaling", this.syncHandler);
|
|
166
|
+
canvas.on("object:rotating", this.syncHandler);
|
|
167
|
+
canvas.on("object:modified", this.syncHandler);
|
|
81
168
|
}
|
|
82
169
|
}
|
|
83
170
|
|
|
84
|
-
private teardown(
|
|
171
|
+
private teardown() {
|
|
172
|
+
if (!this.canvasService) return;
|
|
173
|
+
const canvas = this.canvasService.canvas;
|
|
174
|
+
|
|
85
175
|
if (this.syncHandler) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
176
|
+
canvas.off("object:moving", this.syncHandler);
|
|
177
|
+
canvas.off("object:scaling", this.syncHandler);
|
|
178
|
+
canvas.off("object:rotating", this.syncHandler);
|
|
179
|
+
canvas.off("object:modified", this.syncHandler);
|
|
90
180
|
this.syncHandler = undefined;
|
|
91
181
|
}
|
|
92
182
|
|
|
93
|
-
const layer =
|
|
183
|
+
const layer = this.canvasService.getLayer("user");
|
|
94
184
|
if (layer) {
|
|
95
|
-
const whiteInk =
|
|
185
|
+
const whiteInk = this.canvasService.getObject("white-ink", "user");
|
|
96
186
|
if (whiteInk) {
|
|
97
187
|
layer.remove(whiteInk);
|
|
98
188
|
}
|
|
99
189
|
}
|
|
100
190
|
|
|
101
|
-
const userImage =
|
|
191
|
+
const userImage = this.canvasService.getObject("user-image", "user") as any;
|
|
102
192
|
if (userImage && userImage.clipPath) {
|
|
103
193
|
userImage.set({ clipPath: undefined });
|
|
104
194
|
}
|
|
105
195
|
|
|
106
|
-
|
|
196
|
+
this.canvasService.requestRenderAll();
|
|
107
197
|
}
|
|
108
198
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
199
|
+
private updateWhiteInk() {
|
|
200
|
+
if (!this.canvasService) return;
|
|
201
|
+
const { customMask, opacity, enableClip } = this;
|
|
112
202
|
|
|
113
|
-
|
|
114
|
-
setWhiteInkImage: {
|
|
115
|
-
execute: (
|
|
116
|
-
editor: Editor,
|
|
117
|
-
customMask: string,
|
|
118
|
-
opacity: number,
|
|
119
|
-
enableClip: boolean = true,
|
|
120
|
-
) => {
|
|
121
|
-
if (
|
|
122
|
-
this.options.customMask === customMask &&
|
|
123
|
-
this.options.opacity === opacity &&
|
|
124
|
-
this.options.enableClip === enableClip
|
|
125
|
-
)
|
|
126
|
-
return true;
|
|
127
|
-
|
|
128
|
-
this.options.customMask = customMask;
|
|
129
|
-
this.options.opacity = opacity;
|
|
130
|
-
this.options.enableClip = enableClip;
|
|
131
|
-
|
|
132
|
-
this.updateWhiteInk(editor, this.options);
|
|
133
|
-
|
|
134
|
-
return true;
|
|
135
|
-
},
|
|
136
|
-
schema: {
|
|
137
|
-
customMask: {
|
|
138
|
-
type: "string",
|
|
139
|
-
label: "Custom Mask URL",
|
|
140
|
-
required: true,
|
|
141
|
-
},
|
|
142
|
-
opacity: {
|
|
143
|
-
type: "number",
|
|
144
|
-
label: "Opacity",
|
|
145
|
-
min: 0,
|
|
146
|
-
max: 1,
|
|
147
|
-
required: true,
|
|
148
|
-
},
|
|
149
|
-
enableClip: {
|
|
150
|
-
type: "boolean",
|
|
151
|
-
label: "Enable Clip",
|
|
152
|
-
default: true,
|
|
153
|
-
required: false,
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
private updateWhiteInk(editor: Editor, opts: WhiteInkToolOptions) {
|
|
160
|
-
const { customMask, opacity, enableClip } = opts;
|
|
161
|
-
|
|
162
|
-
const layer = editor.getLayer("user");
|
|
203
|
+
const layer = this.canvasService.getLayer("user");
|
|
163
204
|
if (!layer) {
|
|
164
205
|
console.warn("[WhiteInkTool] User layer not found");
|
|
165
206
|
return;
|
|
166
207
|
}
|
|
167
208
|
|
|
168
|
-
const whiteInk =
|
|
169
|
-
const userImage =
|
|
209
|
+
const whiteInk = this.canvasService.getObject("white-ink", "user") as any;
|
|
210
|
+
const userImage = this.canvasService.getObject("user-image", "user") as any;
|
|
170
211
|
|
|
171
212
|
if (!customMask) {
|
|
172
213
|
if (whiteInk) {
|
|
@@ -175,7 +216,8 @@ export class WhiteInkTool implements Extension<WhiteInkToolOptions> {
|
|
|
175
216
|
if (userImage && userImage.clipPath) {
|
|
176
217
|
userImage.set({ clipPath: undefined });
|
|
177
218
|
}
|
|
178
|
-
|
|
219
|
+
layer.dirty = true;
|
|
220
|
+
this.canvasService.requestRenderAll();
|
|
179
221
|
return;
|
|
180
222
|
}
|
|
181
223
|
|
|
@@ -183,55 +225,52 @@ export class WhiteInkTool implements Extension<WhiteInkToolOptions> {
|
|
|
183
225
|
if (whiteInk) {
|
|
184
226
|
const currentSrc = whiteInk.getSrc?.() || whiteInk._element?.src;
|
|
185
227
|
if (currentSrc !== customMask) {
|
|
186
|
-
this.loadWhiteInk(
|
|
187
|
-
editor,
|
|
188
|
-
layer,
|
|
189
|
-
customMask,
|
|
190
|
-
opacity,
|
|
191
|
-
enableClip,
|
|
192
|
-
whiteInk,
|
|
193
|
-
);
|
|
228
|
+
this.loadWhiteInk(layer, customMask, opacity, enableClip, whiteInk);
|
|
194
229
|
} else {
|
|
195
230
|
if (whiteInk.opacity !== opacity) {
|
|
196
231
|
whiteInk.set({ opacity });
|
|
197
|
-
|
|
232
|
+
layer.dirty = true;
|
|
233
|
+
this.canvasService.requestRenderAll();
|
|
198
234
|
}
|
|
199
235
|
}
|
|
200
236
|
} else {
|
|
201
|
-
this.loadWhiteInk(
|
|
237
|
+
this.loadWhiteInk(layer, customMask, opacity, enableClip);
|
|
202
238
|
}
|
|
203
239
|
|
|
204
240
|
// Handle Clip Path Toggle
|
|
205
241
|
if (userImage) {
|
|
206
242
|
if (enableClip) {
|
|
207
|
-
// If enabled but missing, or mask changed (handled by re-load above, but good to ensure), apply it
|
|
208
|
-
// We check if clipPath is present. Ideally we should check if it matches current mask,
|
|
209
|
-
// but re-applying is safe.
|
|
210
243
|
if (!userImage.clipPath) {
|
|
211
|
-
this.applyClipPath(
|
|
244
|
+
this.applyClipPath(customMask);
|
|
212
245
|
}
|
|
213
246
|
} else {
|
|
214
|
-
// If disabled but present, remove it
|
|
215
247
|
if (userImage.clipPath) {
|
|
216
248
|
userImage.set({ clipPath: undefined });
|
|
217
|
-
|
|
249
|
+
layer.dirty = true;
|
|
250
|
+
this.canvasService.requestRenderAll();
|
|
218
251
|
}
|
|
219
252
|
}
|
|
220
253
|
}
|
|
221
254
|
}
|
|
222
255
|
|
|
223
256
|
private loadWhiteInk(
|
|
224
|
-
|
|
225
|
-
layer: PooderLayer,
|
|
257
|
+
layer: any,
|
|
226
258
|
url: string,
|
|
227
259
|
opacity: number,
|
|
228
260
|
enableClip: boolean,
|
|
229
261
|
oldImage?: any,
|
|
230
262
|
) {
|
|
263
|
+
if (!this.canvasService) return;
|
|
264
|
+
|
|
265
|
+
if (this._loadingUrl === url) return;
|
|
266
|
+
this._loadingUrl = url;
|
|
267
|
+
|
|
231
268
|
Image.fromURL(url, { crossOrigin: "anonymous" })
|
|
232
269
|
.then((image) => {
|
|
270
|
+
if (this._loadingUrl !== url) return;
|
|
271
|
+
this._loadingUrl = null;
|
|
272
|
+
|
|
233
273
|
if (oldImage) {
|
|
234
|
-
// Remove old image but don't copy properties yet, we'll sync with user-image
|
|
235
274
|
layer.remove(oldImage);
|
|
236
275
|
}
|
|
237
276
|
|
|
@@ -256,7 +295,7 @@ export class WhiteInkTool implements Extension<WhiteInkToolOptions> {
|
|
|
256
295
|
layer.add(image);
|
|
257
296
|
|
|
258
297
|
// Ensure white-ink is behind user-image
|
|
259
|
-
const userImage =
|
|
298
|
+
const userImage = this.canvasService!.getObject("user-image", "user");
|
|
260
299
|
if (userImage) {
|
|
261
300
|
// Re-adding moves it to the top of the stack
|
|
262
301
|
layer.remove(userImage);
|
|
@@ -265,29 +304,31 @@ export class WhiteInkTool implements Extension<WhiteInkToolOptions> {
|
|
|
265
304
|
|
|
266
305
|
// Apply clip path to user-image if enabled
|
|
267
306
|
if (enableClip) {
|
|
268
|
-
this.applyClipPath(
|
|
307
|
+
this.applyClipPath(url);
|
|
269
308
|
} else if (userImage) {
|
|
270
309
|
userImage.set({ clipPath: undefined });
|
|
271
310
|
}
|
|
272
311
|
|
|
273
312
|
// Sync position immediately
|
|
274
|
-
this.syncWithUserImage(
|
|
313
|
+
this.syncWithUserImage();
|
|
275
314
|
|
|
276
|
-
|
|
315
|
+
layer.dirty = true;
|
|
316
|
+
this.canvasService!.requestRenderAll();
|
|
277
317
|
})
|
|
278
318
|
.catch((err) => {
|
|
279
319
|
console.error("Failed to load white ink mask", url, err);
|
|
320
|
+
this._loadingUrl = null;
|
|
280
321
|
});
|
|
281
322
|
}
|
|
282
323
|
|
|
283
|
-
private applyClipPath(
|
|
284
|
-
|
|
324
|
+
private applyClipPath(url: string) {
|
|
325
|
+
if (!this.canvasService) return;
|
|
326
|
+
const userImage = this.canvasService.getObject("user-image", "user") as any;
|
|
285
327
|
if (!userImage) return;
|
|
286
328
|
|
|
287
329
|
Image.fromURL(url, { crossOrigin: "anonymous" })
|
|
288
330
|
.then((maskImage) => {
|
|
289
331
|
// Configure clipPath
|
|
290
|
-
// It needs to be relative to the object center
|
|
291
332
|
maskImage.set({
|
|
292
333
|
originX: "center",
|
|
293
334
|
originY: "center",
|
|
@@ -299,16 +340,19 @@ export class WhiteInkTool implements Extension<WhiteInkToolOptions> {
|
|
|
299
340
|
});
|
|
300
341
|
|
|
301
342
|
userImage.set({ clipPath: maskImage });
|
|
302
|
-
|
|
343
|
+
const layer = this.canvasService!.getLayer("user");
|
|
344
|
+
if (layer) layer.dirty = true;
|
|
345
|
+
this.canvasService!.requestRenderAll();
|
|
303
346
|
})
|
|
304
347
|
.catch((err) => {
|
|
305
348
|
console.error("Failed to load clip path", url, err);
|
|
306
349
|
});
|
|
307
350
|
}
|
|
308
351
|
|
|
309
|
-
private syncWithUserImage(
|
|
310
|
-
|
|
311
|
-
const
|
|
352
|
+
private syncWithUserImage() {
|
|
353
|
+
if (!this.canvasService) return;
|
|
354
|
+
const userImage = this.canvasService.getObject("user-image", "user");
|
|
355
|
+
const whiteInk = this.canvasService.getObject("white-ink", "user");
|
|
312
356
|
|
|
313
357
|
if (userImage && whiteInk) {
|
|
314
358
|
whiteInk.set({
|