@pooder/kit 5.0.2 → 5.0.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/.test-dist/src/feature.js +87 -15
- package/.test-dist/src/image.js +28 -12
- package/CHANGELOG.md +12 -0
- package/dist/index.d.mts +10 -4
- package/dist/index.d.ts +10 -4
- package/dist/index.js +167 -111
- package/dist/index.mjs +167 -111
- package/package.json +1 -1
- package/src/feature.ts +96 -20
- package/src/image.ts +93 -108
package/src/feature.ts
CHANGED
|
@@ -34,6 +34,8 @@ export class FeatureTool implements Extension {
|
|
|
34
34
|
private context?: ExtensionContext;
|
|
35
35
|
private isUpdatingConfig = false;
|
|
36
36
|
private isToolActive = false;
|
|
37
|
+
private isFeatureSessionActive = false;
|
|
38
|
+
private sessionOriginalFeatures: ConstraintFeature[] | null = null;
|
|
37
39
|
private hasWorkingChanges = false;
|
|
38
40
|
private dirtyTrackerDisposable?: { dispose(): void };
|
|
39
41
|
|
|
@@ -76,6 +78,7 @@ export class FeatureTool implements Extension {
|
|
|
76
78
|
if (this.isUpdatingConfig) return;
|
|
77
79
|
|
|
78
80
|
if (e.key === "dieline.features") {
|
|
81
|
+
if (this.isFeatureSessionActive) return;
|
|
79
82
|
const next = (e.value || []) as ConstraintFeature[];
|
|
80
83
|
this.workingFeatures = this.cloneFeatures(next);
|
|
81
84
|
this.hasWorkingChanges = false;
|
|
@@ -100,6 +103,7 @@ export class FeatureTool implements Extension {
|
|
|
100
103
|
|
|
101
104
|
deactivate(context: ExtensionContext) {
|
|
102
105
|
context.eventBus.off("tool:activated", this.onToolActivated);
|
|
106
|
+
this.restoreSessionFeaturesToConfig();
|
|
103
107
|
this.dirtyTrackerDisposable?.dispose();
|
|
104
108
|
this.dirtyTrackerDisposable = undefined;
|
|
105
109
|
this.teardown();
|
|
@@ -107,8 +111,11 @@ export class FeatureTool implements Extension {
|
|
|
107
111
|
this.context = undefined;
|
|
108
112
|
}
|
|
109
113
|
|
|
110
|
-
private onToolActivated = (event: { id: string }) => {
|
|
114
|
+
private onToolActivated = (event: { id: string | null }) => {
|
|
111
115
|
this.isToolActive = event.id === this.id;
|
|
116
|
+
if (!this.isToolActive) {
|
|
117
|
+
this.restoreSessionFeaturesToConfig();
|
|
118
|
+
}
|
|
112
119
|
this.updateVisibility();
|
|
113
120
|
};
|
|
114
121
|
|
|
@@ -140,9 +147,9 @@ export class FeatureTool implements Extension {
|
|
|
140
147
|
name: "Feature",
|
|
141
148
|
interaction: "session",
|
|
142
149
|
commands: {
|
|
143
|
-
begin: "
|
|
150
|
+
begin: "beginFeatureSession",
|
|
144
151
|
commit: "completeFeatures",
|
|
145
|
-
rollback: "
|
|
152
|
+
rollback: "rollbackFeatureSession",
|
|
146
153
|
},
|
|
147
154
|
session: {
|
|
148
155
|
autoBegin: false,
|
|
@@ -151,6 +158,25 @@ export class FeatureTool implements Extension {
|
|
|
151
158
|
},
|
|
152
159
|
],
|
|
153
160
|
[ContributionPointIds.COMMANDS]: [
|
|
161
|
+
{
|
|
162
|
+
command: "beginFeatureSession",
|
|
163
|
+
title: "Begin Feature Session",
|
|
164
|
+
handler: async () => {
|
|
165
|
+
if (this.isFeatureSessionActive) {
|
|
166
|
+
return { ok: true };
|
|
167
|
+
}
|
|
168
|
+
const original = this.getCommittedFeatures();
|
|
169
|
+
this.sessionOriginalFeatures = this.cloneFeatures(original);
|
|
170
|
+
this.isFeatureSessionActive = true;
|
|
171
|
+
await this.refreshGeometry();
|
|
172
|
+
this.setWorkingFeatures(this.cloneFeatures(original));
|
|
173
|
+
this.hasWorkingChanges = false;
|
|
174
|
+
this.redraw();
|
|
175
|
+
this.emitWorkingChange();
|
|
176
|
+
this.updateCommittedFeatures([]);
|
|
177
|
+
return { ok: true };
|
|
178
|
+
},
|
|
179
|
+
},
|
|
154
180
|
{
|
|
155
181
|
command: "addFeature",
|
|
156
182
|
title: "Add Edge Feature",
|
|
@@ -203,21 +229,27 @@ export class FeatureTool implements Extension {
|
|
|
203
229
|
},
|
|
204
230
|
},
|
|
205
231
|
{
|
|
206
|
-
command: "
|
|
207
|
-
title: "
|
|
232
|
+
command: "rollbackFeatureSession",
|
|
233
|
+
title: "Rollback Feature Session",
|
|
208
234
|
handler: async () => {
|
|
209
|
-
const
|
|
210
|
-
this.
|
|
211
|
-
|
|
212
|
-
);
|
|
213
|
-
const next = (configService?.get("dieline.features", []) ||
|
|
214
|
-
[]) as ConstraintFeature[];
|
|
215
|
-
|
|
235
|
+
const original = this.cloneFeatures(
|
|
236
|
+
this.sessionOriginalFeatures || this.getCommittedFeatures(),
|
|
237
|
+
);
|
|
216
238
|
await this.refreshGeometry();
|
|
217
|
-
this.setWorkingFeatures(
|
|
239
|
+
this.setWorkingFeatures(original);
|
|
218
240
|
this.hasWorkingChanges = false;
|
|
219
241
|
this.redraw();
|
|
220
242
|
this.emitWorkingChange();
|
|
243
|
+
this.updateCommittedFeatures(original);
|
|
244
|
+
this.clearFeatureSessionState();
|
|
245
|
+
return { ok: true };
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
command: "resetWorkingFeatures",
|
|
250
|
+
title: "Reset Working Features",
|
|
251
|
+
handler: async () => {
|
|
252
|
+
await this.resetWorkingFeaturesFromSource();
|
|
221
253
|
return { ok: true };
|
|
222
254
|
},
|
|
223
255
|
},
|
|
@@ -243,6 +275,42 @@ export class FeatureTool implements Extension {
|
|
|
243
275
|
return JSON.parse(JSON.stringify(features || [])) as ConstraintFeature[];
|
|
244
276
|
}
|
|
245
277
|
|
|
278
|
+
private getConfigService(): ConfigurationService | undefined {
|
|
279
|
+
return this.context?.services.get<ConfigurationService>("ConfigurationService");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
private getCommittedFeatures(): ConstraintFeature[] {
|
|
283
|
+
const configService = this.getConfigService();
|
|
284
|
+
const committed = (configService?.get("dieline.features", []) ||
|
|
285
|
+
[]) as ConstraintFeature[];
|
|
286
|
+
return this.cloneFeatures(committed);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
private updateCommittedFeatures(next: ConstraintFeature[]) {
|
|
290
|
+
const configService = this.getConfigService();
|
|
291
|
+
if (!configService) return;
|
|
292
|
+
this.isUpdatingConfig = true;
|
|
293
|
+
try {
|
|
294
|
+
configService.update("dieline.features", next);
|
|
295
|
+
} finally {
|
|
296
|
+
this.isUpdatingConfig = false;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private clearFeatureSessionState() {
|
|
301
|
+
this.isFeatureSessionActive = false;
|
|
302
|
+
this.sessionOriginalFeatures = null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
private restoreSessionFeaturesToConfig() {
|
|
306
|
+
if (!this.isFeatureSessionActive) return;
|
|
307
|
+
const original = this.cloneFeatures(
|
|
308
|
+
this.sessionOriginalFeatures || this.getCommittedFeatures(),
|
|
309
|
+
);
|
|
310
|
+
this.updateCommittedFeatures(original);
|
|
311
|
+
this.clearFeatureSessionState();
|
|
312
|
+
}
|
|
313
|
+
|
|
246
314
|
private emitWorkingChange() {
|
|
247
315
|
this.context?.eventBus.emit("feature:working:change", {
|
|
248
316
|
features: this.cloneFeatures(this.workingFeatures),
|
|
@@ -261,6 +329,19 @@ export class FeatureTool implements Extension {
|
|
|
261
329
|
} catch (e) {}
|
|
262
330
|
}
|
|
263
331
|
|
|
332
|
+
private async resetWorkingFeaturesFromSource() {
|
|
333
|
+
const next = this.cloneFeatures(
|
|
334
|
+
this.isFeatureSessionActive && this.sessionOriginalFeatures
|
|
335
|
+
? this.sessionOriginalFeatures
|
|
336
|
+
: this.getCommittedFeatures(),
|
|
337
|
+
);
|
|
338
|
+
await this.refreshGeometry();
|
|
339
|
+
this.setWorkingFeatures(next);
|
|
340
|
+
this.hasWorkingChanges = false;
|
|
341
|
+
this.redraw();
|
|
342
|
+
this.emitWorkingChange();
|
|
343
|
+
}
|
|
344
|
+
|
|
264
345
|
private setWorkingFeatures(next: ConstraintFeature[]) {
|
|
265
346
|
this.workingFeatures = next;
|
|
266
347
|
}
|
|
@@ -335,13 +416,7 @@ export class FeatureTool implements Extension {
|
|
|
335
416
|
this.workingFeatures,
|
|
336
417
|
{ dielineWidth, dielineHeight },
|
|
337
418
|
(next) => {
|
|
338
|
-
this.
|
|
339
|
-
try {
|
|
340
|
-
configService.update("dieline.features", next);
|
|
341
|
-
} finally {
|
|
342
|
-
this.isUpdatingConfig = false;
|
|
343
|
-
}
|
|
344
|
-
|
|
419
|
+
this.updateCommittedFeatures(next as ConstraintFeature[]);
|
|
345
420
|
this.workingFeatures = this.cloneFeatures(next as any);
|
|
346
421
|
this.emitWorkingChange();
|
|
347
422
|
},
|
|
@@ -355,6 +430,7 @@ export class FeatureTool implements Extension {
|
|
|
355
430
|
}
|
|
356
431
|
|
|
357
432
|
this.hasWorkingChanges = false;
|
|
433
|
+
this.clearFeatureSessionState();
|
|
358
434
|
// Keep feature markers above dieline overlay after config-driven redraw.
|
|
359
435
|
this.redraw();
|
|
360
436
|
return { ok: true };
|
package/src/image.ts
CHANGED
|
@@ -57,8 +57,7 @@ interface FrameVisualConfig {
|
|
|
57
57
|
|
|
58
58
|
interface UpsertImageOptions {
|
|
59
59
|
id?: string;
|
|
60
|
-
mode?: "
|
|
61
|
-
createIfMissing?: boolean;
|
|
60
|
+
mode?: "replace" | "add";
|
|
62
61
|
addOptions?: Partial<ImageItem>;
|
|
63
62
|
fitOnAdd?: boolean;
|
|
64
63
|
}
|
|
@@ -108,7 +107,6 @@ interface DetectFromFrameOptions {
|
|
|
108
107
|
|
|
109
108
|
const IMAGE_OBJECT_LAYER_ID = "image.user";
|
|
110
109
|
const IMAGE_OVERLAY_LAYER_ID = "image-overlay";
|
|
111
|
-
const IMAGE_REPLACE_GUARD_MS = 2500;
|
|
112
110
|
const IMAGE_DETECT_EXPAND_DEFAULT = 30;
|
|
113
111
|
const IMAGE_DETECT_SIMPLIFY_TOLERANCE_DEFAULT = 2;
|
|
114
112
|
const IMAGE_DETECT_MULTIPLIER_DEFAULT = 2;
|
|
@@ -131,7 +129,6 @@ export class ImageTool implements Extension {
|
|
|
131
129
|
private isToolActive = false;
|
|
132
130
|
private isImageSelectionActive = false;
|
|
133
131
|
private focusedImageId: string | null = null;
|
|
134
|
-
private suppressSelectionClearUntil = 0;
|
|
135
132
|
private renderSeq = 0;
|
|
136
133
|
private dirtyTrackerDisposable?: { dispose(): void };
|
|
137
134
|
|
|
@@ -218,13 +215,10 @@ export class ImageTool implements Extension {
|
|
|
218
215
|
const before = this.isToolActive;
|
|
219
216
|
this.syncToolActiveFromWorkbench(event.id);
|
|
220
217
|
if (!this.isToolActive) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
this.isImageSelectionActive = false;
|
|
226
|
-
this.focusedImageId = null;
|
|
227
|
-
}
|
|
218
|
+
this.setImageFocus(null, {
|
|
219
|
+
syncCanvasSelection: true,
|
|
220
|
+
skipRender: true,
|
|
221
|
+
});
|
|
228
222
|
}
|
|
229
223
|
this.debug("tool:activated", {
|
|
230
224
|
id: event.id,
|
|
@@ -233,7 +227,6 @@ export class ImageTool implements Extension {
|
|
|
233
227
|
before,
|
|
234
228
|
isToolActive: this.isToolActive,
|
|
235
229
|
focusedImageId: this.focusedImageId,
|
|
236
|
-
suppressSelectionClearUntil: this.suppressSelectionClearUntil,
|
|
237
230
|
});
|
|
238
231
|
if (!this.isToolActive && this.isDebugEnabled()) {
|
|
239
232
|
console.trace("[ImageTool] tool deactivated trace");
|
|
@@ -271,16 +264,10 @@ export class ImageTool implements Extension {
|
|
|
271
264
|
};
|
|
272
265
|
|
|
273
266
|
private onSelectionCleared = () => {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
focusedImageId: this.focusedImageId,
|
|
279
|
-
});
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
this.isImageSelectionActive = false;
|
|
283
|
-
this.focusedImageId = null;
|
|
267
|
+
this.setImageFocus(null, {
|
|
268
|
+
syncCanvasSelection: false,
|
|
269
|
+
skipRender: true,
|
|
270
|
+
});
|
|
284
271
|
this.debug("selection:cleared applied");
|
|
285
272
|
this.updateImages();
|
|
286
273
|
};
|
|
@@ -353,7 +340,7 @@ export class ImageTool implements Extension {
|
|
|
353
340
|
id: "image.frame.strokeColor",
|
|
354
341
|
type: "color",
|
|
355
342
|
label: "Image Frame Stroke Color",
|
|
356
|
-
default: "#
|
|
343
|
+
default: "#808080",
|
|
357
344
|
},
|
|
358
345
|
{
|
|
359
346
|
id: "image.frame.strokeWidth",
|
|
@@ -369,7 +356,7 @@ export class ImageTool implements Extension {
|
|
|
369
356
|
type: "select",
|
|
370
357
|
label: "Image Frame Stroke Style",
|
|
371
358
|
options: ["solid", "dashed", "hidden"],
|
|
372
|
-
default: "
|
|
359
|
+
default: "dashed",
|
|
373
360
|
},
|
|
374
361
|
{
|
|
375
362
|
id: "image.frame.dashLength",
|
|
@@ -474,6 +461,16 @@ export class ImageTool implements Extension {
|
|
|
474
461
|
await this.fitImageToDefaultArea(id);
|
|
475
462
|
},
|
|
476
463
|
},
|
|
464
|
+
{
|
|
465
|
+
command: "focusImage",
|
|
466
|
+
title: "Focus Image",
|
|
467
|
+
handler: (
|
|
468
|
+
id: string | null,
|
|
469
|
+
options: { syncCanvasSelection?: boolean } = {},
|
|
470
|
+
) => {
|
|
471
|
+
return this.setImageFocus(id, options);
|
|
472
|
+
},
|
|
473
|
+
},
|
|
477
474
|
{
|
|
478
475
|
command: "removeImage",
|
|
479
476
|
title: "Remove Image",
|
|
@@ -483,8 +480,10 @@ export class ImageTool implements Extension {
|
|
|
483
480
|
if (next.length !== this.items.length) {
|
|
484
481
|
this.purgeSourceSizeCacheForItem(removed);
|
|
485
482
|
if (this.focusedImageId === id) {
|
|
486
|
-
this.
|
|
487
|
-
|
|
483
|
+
this.setImageFocus(null, {
|
|
484
|
+
syncCanvasSelection: true,
|
|
485
|
+
skipRender: true,
|
|
486
|
+
});
|
|
488
487
|
}
|
|
489
488
|
this.updateConfig(next);
|
|
490
489
|
}
|
|
@@ -506,8 +505,10 @@ export class ImageTool implements Extension {
|
|
|
506
505
|
title: "Clear Images",
|
|
507
506
|
handler: () => {
|
|
508
507
|
this.sourceSizeBySrc.clear();
|
|
509
|
-
this.
|
|
510
|
-
|
|
508
|
+
this.setImageFocus(null, {
|
|
509
|
+
syncCanvasSelection: true,
|
|
510
|
+
skipRender: true,
|
|
511
|
+
});
|
|
511
512
|
this.updateConfig([]);
|
|
512
513
|
},
|
|
513
514
|
},
|
|
@@ -584,29 +585,50 @@ export class ImageTool implements Extension {
|
|
|
584
585
|
return Math.random().toString(36).substring(2, 9);
|
|
585
586
|
}
|
|
586
587
|
|
|
587
|
-
private
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
) {
|
|
593
|
-
return active.data.id;
|
|
594
|
-
}
|
|
595
|
-
return null;
|
|
588
|
+
private hasImageItem(id: string): boolean {
|
|
589
|
+
return (
|
|
590
|
+
this.items.some((item) => item.id === id) ||
|
|
591
|
+
this.workingItems.some((item) => item.id === id)
|
|
592
|
+
);
|
|
596
593
|
}
|
|
597
594
|
|
|
598
|
-
private
|
|
599
|
-
|
|
600
|
-
|
|
595
|
+
private setImageFocus(
|
|
596
|
+
id: string | null,
|
|
597
|
+
options: { syncCanvasSelection?: boolean; skipRender?: boolean } = {},
|
|
598
|
+
) {
|
|
599
|
+
const syncCanvasSelection = options.syncCanvasSelection !== false;
|
|
601
600
|
|
|
602
|
-
if (
|
|
603
|
-
|
|
601
|
+
if (id && !this.hasImageItem(id)) {
|
|
602
|
+
return { ok: false, reason: "image-not-found" as const };
|
|
603
|
+
}
|
|
604
604
|
|
|
605
|
-
|
|
606
|
-
|
|
605
|
+
this.focusedImageId = id;
|
|
606
|
+
this.isImageSelectionActive = !!id;
|
|
607
|
+
|
|
608
|
+
if (syncCanvasSelection && this.canvasService) {
|
|
609
|
+
const canvas = this.canvasService.canvas;
|
|
610
|
+
if (!id) {
|
|
611
|
+
canvas.discardActiveObject();
|
|
612
|
+
} else {
|
|
613
|
+
const obj = this.getImageObject(id);
|
|
614
|
+
if (obj) {
|
|
615
|
+
obj.set({
|
|
616
|
+
selectable: true,
|
|
617
|
+
evented: true,
|
|
618
|
+
hasControls: true,
|
|
619
|
+
hasBorders: true,
|
|
620
|
+
});
|
|
621
|
+
canvas.setActiveObject(obj);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
this.canvasService.requestRenderAll();
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (!options.skipRender) {
|
|
628
|
+
this.updateImages();
|
|
629
|
+
}
|
|
607
630
|
|
|
608
|
-
|
|
609
|
-
return null;
|
|
631
|
+
return { ok: true, id };
|
|
610
632
|
}
|
|
611
633
|
|
|
612
634
|
private async addImageEntry(
|
|
@@ -622,9 +644,6 @@ export class ImageTool implements Extension {
|
|
|
622
644
|
...options,
|
|
623
645
|
} as ImageItem);
|
|
624
646
|
|
|
625
|
-
this.focusedImageId = id;
|
|
626
|
-
this.isImageSelectionActive = true;
|
|
627
|
-
this.suppressSelectionClearUntil = Date.now() + IMAGE_REPLACE_GUARD_MS;
|
|
628
647
|
const sessionDirtyBeforeAdd = this.isToolActive && this.hasWorkingChanges;
|
|
629
648
|
const waitLoaded = this.waitImageLoaded(id, true);
|
|
630
649
|
this.updateConfig([...this.items, newItem]);
|
|
@@ -634,7 +653,7 @@ export class ImageTool implements Extension {
|
|
|
634
653
|
await this.fitImageToDefaultArea(id);
|
|
635
654
|
}
|
|
636
655
|
if (loaded) {
|
|
637
|
-
this.
|
|
656
|
+
this.setImageFocus(id);
|
|
638
657
|
}
|
|
639
658
|
return id;
|
|
640
659
|
}
|
|
@@ -643,23 +662,20 @@ export class ImageTool implements Extension {
|
|
|
643
662
|
url: string,
|
|
644
663
|
options: UpsertImageOptions = {},
|
|
645
664
|
): Promise<{ id: string; mode: "replace" | "add" }> {
|
|
646
|
-
const mode = options.mode || "
|
|
665
|
+
const mode = options.mode || (options.id ? "replace" : "add");
|
|
647
666
|
const fitOnAdd = options.fitOnAdd !== false;
|
|
648
|
-
if (mode === "
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
667
|
+
if (mode === "replace") {
|
|
668
|
+
if (!options.id) {
|
|
669
|
+
throw new Error("replace-target-id-required");
|
|
670
|
+
}
|
|
671
|
+
const targetId = options.id;
|
|
672
|
+
if (!this.hasImageItem(targetId)) {
|
|
673
|
+
throw new Error("replace-target-not-found");
|
|
674
|
+
}
|
|
655
675
|
await this.updateImageInConfig(targetId, { url });
|
|
656
676
|
return { id: targetId, mode: "replace" };
|
|
657
677
|
}
|
|
658
678
|
|
|
659
|
-
if (mode === "replace" || options.createIfMissing === false) {
|
|
660
|
-
throw new Error("replace-target-not-found");
|
|
661
|
-
}
|
|
662
|
-
|
|
663
679
|
const id = await this.addImageEntry(url, options.addOptions, fitOnAdd);
|
|
664
680
|
return { id, mode: "add" };
|
|
665
681
|
}
|
|
@@ -870,12 +886,12 @@ export class ImageTool implements Extension {
|
|
|
870
886
|
private getFrameVisualConfig(): FrameVisualConfig {
|
|
871
887
|
const strokeStyleRaw = (this.getConfig<string>(
|
|
872
888
|
"image.frame.strokeStyle",
|
|
873
|
-
"
|
|
874
|
-
) || "
|
|
889
|
+
"dashed",
|
|
890
|
+
) || "dashed") as string;
|
|
875
891
|
const strokeStyle: "solid" | "dashed" | "hidden" =
|
|
876
892
|
strokeStyleRaw === "dashed" || strokeStyleRaw === "hidden"
|
|
877
893
|
? strokeStyleRaw
|
|
878
|
-
: "
|
|
894
|
+
: "dashed";
|
|
879
895
|
|
|
880
896
|
const strokeWidth = Number(
|
|
881
897
|
this.getConfig<number>("image.frame.strokeWidth", 2) ?? 2,
|
|
@@ -886,8 +902,8 @@ export class ImageTool implements Extension {
|
|
|
886
902
|
|
|
887
903
|
return {
|
|
888
904
|
strokeColor:
|
|
889
|
-
this.getConfig<string>("image.frame.strokeColor", "#
|
|
890
|
-
"#
|
|
905
|
+
this.getConfig<string>("image.frame.strokeColor", "#808080") ||
|
|
906
|
+
"#808080",
|
|
891
907
|
strokeWidth: Number.isFinite(strokeWidth) ? Math.max(0, strokeWidth) : 2,
|
|
892
908
|
strokeStyle,
|
|
893
909
|
dashLength: Number.isFinite(dashLength) ? Math.max(1, dashLength) : 8,
|
|
@@ -1199,8 +1215,10 @@ export class ImageTool implements Extension {
|
|
|
1199
1215
|
const frame = this.getFrameRect();
|
|
1200
1216
|
const desiredIds = new Set(renderItems.map((item) => item.id));
|
|
1201
1217
|
if (this.focusedImageId && !desiredIds.has(this.focusedImageId)) {
|
|
1202
|
-
this.
|
|
1203
|
-
|
|
1218
|
+
this.setImageFocus(null, {
|
|
1219
|
+
syncCanvasSelection: false,
|
|
1220
|
+
skipRender: true,
|
|
1221
|
+
});
|
|
1204
1222
|
}
|
|
1205
1223
|
|
|
1206
1224
|
this.getImageObjects().forEach((obj: any) => {
|
|
@@ -1281,8 +1299,10 @@ export class ImageTool implements Extension {
|
|
|
1281
1299
|
next[index] = this.normalizeItem({ ...next[index], ...updates });
|
|
1282
1300
|
this.workingItems = next;
|
|
1283
1301
|
this.hasWorkingChanges = true;
|
|
1284
|
-
this.
|
|
1285
|
-
|
|
1302
|
+
this.setImageFocus(id, {
|
|
1303
|
+
syncCanvasSelection: false,
|
|
1304
|
+
skipRender: true,
|
|
1305
|
+
});
|
|
1286
1306
|
if (this.isToolActive) {
|
|
1287
1307
|
this.updateImages();
|
|
1288
1308
|
}
|
|
@@ -1318,16 +1338,13 @@ export class ImageTool implements Extension {
|
|
|
1318
1338
|
this.updateConfig(next);
|
|
1319
1339
|
|
|
1320
1340
|
if (replacingSource) {
|
|
1321
|
-
this.focusedImageId = id;
|
|
1322
|
-
this.isImageSelectionActive = true;
|
|
1323
|
-
this.suppressSelectionClearUntil = Date.now() + IMAGE_REPLACE_GUARD_MS;
|
|
1324
1341
|
this.debug("replace:image:begin", { id, replacingUrl });
|
|
1325
1342
|
this.purgeSourceSizeCacheForItem(base);
|
|
1326
1343
|
const loaded = await this.waitImageLoaded(id, true);
|
|
1327
1344
|
this.debug("replace:image:loaded", { id, loaded });
|
|
1328
1345
|
if (loaded) {
|
|
1329
1346
|
await this.refitImageToFrame(id);
|
|
1330
|
-
this.
|
|
1347
|
+
this.setImageFocus(id);
|
|
1331
1348
|
}
|
|
1332
1349
|
}
|
|
1333
1350
|
}
|
|
@@ -1380,32 +1397,10 @@ export class ImageTool implements Extension {
|
|
|
1380
1397
|
this.updateConfig(next);
|
|
1381
1398
|
this.workingItems = this.cloneItems(next);
|
|
1382
1399
|
this.hasWorkingChanges = false;
|
|
1383
|
-
this.isImageSelectionActive = true;
|
|
1384
|
-
this.focusedImageId = id;
|
|
1385
1400
|
this.updateImages();
|
|
1386
1401
|
this.emitWorkingChange(id);
|
|
1387
1402
|
}
|
|
1388
1403
|
|
|
1389
|
-
private focusImageSelection(id: string) {
|
|
1390
|
-
if (!this.canvasService) return;
|
|
1391
|
-
const obj = this.getImageObject(id);
|
|
1392
|
-
if (!obj) return;
|
|
1393
|
-
|
|
1394
|
-
this.isImageSelectionActive = true;
|
|
1395
|
-
this.focusedImageId = id;
|
|
1396
|
-
this.suppressSelectionClearUntil = Date.now() + 700;
|
|
1397
|
-
obj.set({
|
|
1398
|
-
selectable: true,
|
|
1399
|
-
evented: true,
|
|
1400
|
-
hasControls: true,
|
|
1401
|
-
hasBorders: true,
|
|
1402
|
-
});
|
|
1403
|
-
this.canvasService.canvas.setActiveObject(obj);
|
|
1404
|
-
this.debug("focus:image", { id });
|
|
1405
|
-
this.canvasService.requestRenderAll();
|
|
1406
|
-
this.updateImages();
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
1404
|
private async fitImageToArea(
|
|
1410
1405
|
id: string,
|
|
1411
1406
|
area: { width: number; height: number; left?: number; top?: number },
|
|
@@ -1473,10 +1468,6 @@ export class ImageTool implements Extension {
|
|
|
1473
1468
|
return { ok: false, reason: "frame-not-ready" };
|
|
1474
1469
|
}
|
|
1475
1470
|
|
|
1476
|
-
const focusId =
|
|
1477
|
-
this.resolveReplaceTargetId(this.focusedImageId) ||
|
|
1478
|
-
(this.workingItems.length === 1 ? this.workingItems[0].id : null);
|
|
1479
|
-
|
|
1480
1471
|
const next: ImageItem[] = [];
|
|
1481
1472
|
for (const item of this.workingItems) {
|
|
1482
1473
|
const url = await this.exportCroppedImageByIds([item.id], {
|
|
@@ -1502,13 +1493,7 @@ export class ImageTool implements Extension {
|
|
|
1502
1493
|
this.hasWorkingChanges = false;
|
|
1503
1494
|
this.workingItems = this.cloneItems(next);
|
|
1504
1495
|
this.updateConfig(next);
|
|
1505
|
-
this.emitWorkingChange(
|
|
1506
|
-
if (focusId) {
|
|
1507
|
-
this.focusedImageId = focusId;
|
|
1508
|
-
this.isImageSelectionActive = true;
|
|
1509
|
-
this.suppressSelectionClearUntil = Date.now() + IMAGE_REPLACE_GUARD_MS;
|
|
1510
|
-
this.focusImageSelection(focusId);
|
|
1511
|
-
}
|
|
1496
|
+
this.emitWorkingChange(this.focusedImageId);
|
|
1512
1497
|
return { ok: true };
|
|
1513
1498
|
}
|
|
1514
1499
|
|