@pooder/kit 6.2.2 → 6.3.1
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/extensions/image/ImageTool.js +250 -119
- package/.test-dist/src/extensions/image/commands.js +69 -41
- package/.test-dist/src/extensions/image/config.js +7 -0
- package/.test-dist/src/extensions/image/imageOperations.js +75 -0
- package/.test-dist/src/extensions/image/imagePlacement.js +44 -0
- package/.test-dist/src/extensions/image/index.js +1 -0
- package/.test-dist/src/extensions/image/model.js +4 -0
- package/.test-dist/tests/run.js +51 -9
- package/CHANGELOG.md +14 -0
- package/dist/index.d.mts +270 -163
- package/dist/index.d.ts +270 -163
- package/dist/index.js +458 -159
- package/dist/index.mjs +454 -158
- package/package.json +2 -2
- package/src/extensions/image/ImageTool.ts +351 -145
- package/src/extensions/image/commands.ts +78 -49
- package/src/extensions/image/config.ts +7 -0
- package/src/extensions/image/imageOperations.ts +135 -0
- package/src/extensions/image/imagePlacement.ts +78 -0
- package/src/extensions/image/index.ts +1 -0
- package/src/extensions/image/model.ts +13 -1
- package/tests/run.ts +89 -15
|
@@ -11,6 +11,8 @@ const sessionState_1 = require("../../shared/runtime/sessionState");
|
|
|
11
11
|
const layers_1 = require("../../shared/constants/layers");
|
|
12
12
|
const commands_1 = require("./commands");
|
|
13
13
|
const config_1 = require("./config");
|
|
14
|
+
const imageOperations_1 = require("./imageOperations");
|
|
15
|
+
const imagePlacement_1 = require("./imagePlacement");
|
|
14
16
|
const sessionOverlay_1 = require("./sessionOverlay");
|
|
15
17
|
const IMAGE_DEFAULT_CONTROL_CAPABILITIES = [
|
|
16
18
|
"rotate",
|
|
@@ -62,6 +64,7 @@ class ImageTool {
|
|
|
62
64
|
this.activeSnapX = null;
|
|
63
65
|
this.activeSnapY = null;
|
|
64
66
|
this.movingImageId = null;
|
|
67
|
+
this.sessionNotice = null;
|
|
65
68
|
this.hasRenderedSnapGuides = false;
|
|
66
69
|
this.subscriptions = new subscriptions_1.SubscriptionBag();
|
|
67
70
|
this.imageControlsByCapabilityKey = new Map();
|
|
@@ -227,7 +230,11 @@ class ImageTool {
|
|
|
227
230
|
}
|
|
228
231
|
if (e.key.startsWith("size.") ||
|
|
229
232
|
e.key.startsWith("image.frame.") ||
|
|
233
|
+
e.key.startsWith("image.session.") ||
|
|
230
234
|
e.key.startsWith("image.control.")) {
|
|
235
|
+
if (e.key === "image.session.placementPolicy") {
|
|
236
|
+
this.clearSessionNotice();
|
|
237
|
+
}
|
|
231
238
|
if (e.key.startsWith("image.control.")) {
|
|
232
239
|
this.imageControlsByCapabilityKey.clear();
|
|
233
240
|
}
|
|
@@ -255,6 +262,7 @@ class ImageTool {
|
|
|
255
262
|
this.clearRenderedImages();
|
|
256
263
|
this.renderProducerDisposable?.dispose();
|
|
257
264
|
this.renderProducerDisposable = undefined;
|
|
265
|
+
this.emitImageStateChange();
|
|
258
266
|
if (this.canvasService) {
|
|
259
267
|
void this.canvasService.flushRenderFromProducers();
|
|
260
268
|
this.canvasService = undefined;
|
|
@@ -645,9 +653,10 @@ class ImageTool {
|
|
|
645
653
|
name: "Image",
|
|
646
654
|
interaction: "session",
|
|
647
655
|
commands: {
|
|
648
|
-
begin: "
|
|
656
|
+
begin: "imageSessionReset",
|
|
657
|
+
validate: "validateImageSession",
|
|
649
658
|
commit: "completeImages",
|
|
650
|
-
rollback: "
|
|
659
|
+
rollback: "imageSessionReset",
|
|
651
660
|
},
|
|
652
661
|
session: {
|
|
653
662
|
autoBegin: true,
|
|
@@ -685,6 +694,59 @@ class ImageTool {
|
|
|
685
694
|
cloneItems(items) {
|
|
686
695
|
return this.normalizeItems((items || []).map((i) => ({ ...i })));
|
|
687
696
|
}
|
|
697
|
+
getViewItems() {
|
|
698
|
+
return this.isToolActive ? this.workingItems : this.items;
|
|
699
|
+
}
|
|
700
|
+
getPlacementPolicy() {
|
|
701
|
+
const policy = this.getConfig("image.session.placementPolicy", "free");
|
|
702
|
+
return policy === "warn" || policy === "strict" ? policy : "free";
|
|
703
|
+
}
|
|
704
|
+
areSessionNoticesEqual(a, b) {
|
|
705
|
+
if (!a && !b)
|
|
706
|
+
return true;
|
|
707
|
+
if (!a || !b)
|
|
708
|
+
return false;
|
|
709
|
+
return (a.code === b.code &&
|
|
710
|
+
a.level === b.level &&
|
|
711
|
+
a.message === b.message &&
|
|
712
|
+
a.policy === b.policy &&
|
|
713
|
+
JSON.stringify(a.imageIds) === JSON.stringify(b.imageIds));
|
|
714
|
+
}
|
|
715
|
+
setSessionNotice(notice, options = {}) {
|
|
716
|
+
if (this.areSessionNoticesEqual(this.sessionNotice, notice)) {
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
this.sessionNotice = notice;
|
|
720
|
+
if (options.emit !== false) {
|
|
721
|
+
this.context?.eventBus.emit("image:session:notice", this.sessionNotice);
|
|
722
|
+
this.emitImageStateChange();
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
clearSessionNotice(options = {}) {
|
|
726
|
+
this.setSessionNotice(null, options);
|
|
727
|
+
}
|
|
728
|
+
getImageViewState() {
|
|
729
|
+
this.syncToolActiveFromWorkbench();
|
|
730
|
+
const items = this.cloneItems(this.getViewItems());
|
|
731
|
+
const focusedItem = this.focusedImageId == null
|
|
732
|
+
? null
|
|
733
|
+
: items.find((item) => item.id === this.focusedImageId) || null;
|
|
734
|
+
return {
|
|
735
|
+
items,
|
|
736
|
+
hasAnyImage: items.length > 0,
|
|
737
|
+
focusedId: this.focusedImageId,
|
|
738
|
+
focusedItem,
|
|
739
|
+
isToolActive: this.isToolActive,
|
|
740
|
+
isImageSelectionActive: this.isImageSelectionActive,
|
|
741
|
+
hasWorkingChanges: this.hasWorkingChanges,
|
|
742
|
+
source: this.isToolActive ? "working" : "committed",
|
|
743
|
+
placementPolicy: this.getPlacementPolicy(),
|
|
744
|
+
sessionNotice: this.sessionNotice,
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
emitImageStateChange() {
|
|
748
|
+
this.context?.eventBus.emit("image:state:change", this.getImageViewState());
|
|
749
|
+
}
|
|
688
750
|
emitWorkingChange(changedId = null) {
|
|
689
751
|
this.context?.eventBus.emit("image:working:change", {
|
|
690
752
|
changedId,
|
|
@@ -722,9 +784,14 @@ class ImageTool {
|
|
|
722
784
|
if (!options.skipRender) {
|
|
723
785
|
this.updateImages();
|
|
724
786
|
}
|
|
787
|
+
else {
|
|
788
|
+
this.emitImageStateChange();
|
|
789
|
+
}
|
|
725
790
|
return { ok: true, id };
|
|
726
791
|
}
|
|
727
|
-
async addImageEntry(url, options,
|
|
792
|
+
async addImageEntry(url, options, operation) {
|
|
793
|
+
this.syncToolActiveFromWorkbench();
|
|
794
|
+
this.clearSessionNotice({ emit: false });
|
|
728
795
|
const id = this.generateId();
|
|
729
796
|
const newItem = this.normalizeItem({
|
|
730
797
|
id,
|
|
@@ -732,13 +799,21 @@ class ImageTool {
|
|
|
732
799
|
opacity: 1,
|
|
733
800
|
...options,
|
|
734
801
|
});
|
|
735
|
-
const sessionDirtyBeforeAdd = this.isToolActive && this.hasWorkingChanges;
|
|
736
802
|
const waitLoaded = this.waitImageLoaded(id, true);
|
|
737
|
-
|
|
738
|
-
|
|
803
|
+
if (this.isToolActive) {
|
|
804
|
+
this.workingItems = this.cloneItems([...this.workingItems, newItem]);
|
|
805
|
+
this.hasWorkingChanges = true;
|
|
806
|
+
this.updateImages();
|
|
807
|
+
this.emitWorkingChange(id);
|
|
808
|
+
}
|
|
809
|
+
else {
|
|
810
|
+
this.updateConfig([...this.items, newItem]);
|
|
811
|
+
}
|
|
739
812
|
const loaded = await waitLoaded;
|
|
740
|
-
if (loaded &&
|
|
741
|
-
await this.
|
|
813
|
+
if (loaded && operation) {
|
|
814
|
+
await this.applyImageOperation(id, operation, {
|
|
815
|
+
target: this.isToolActive ? "working" : "config",
|
|
816
|
+
});
|
|
742
817
|
}
|
|
743
818
|
if (loaded) {
|
|
744
819
|
this.setImageFocus(id);
|
|
@@ -746,8 +821,8 @@ class ImageTool {
|
|
|
746
821
|
return id;
|
|
747
822
|
}
|
|
748
823
|
async upsertImageEntry(url, options = {}) {
|
|
824
|
+
this.syncToolActiveFromWorkbench();
|
|
749
825
|
const mode = options.mode || (options.id ? "replace" : "add");
|
|
750
|
-
const fitOnAdd = options.fitOnAdd !== false;
|
|
751
826
|
if (mode === "replace") {
|
|
752
827
|
if (!options.id) {
|
|
753
828
|
throw new Error("replace-target-id-required");
|
|
@@ -756,21 +831,33 @@ class ImageTool {
|
|
|
756
831
|
if (!this.hasImageItem(targetId)) {
|
|
757
832
|
throw new Error("replace-target-not-found");
|
|
758
833
|
}
|
|
759
|
-
|
|
834
|
+
if (this.isToolActive) {
|
|
835
|
+
const current = this.workingItems.find((item) => item.id === targetId) ||
|
|
836
|
+
this.items.find((item) => item.id === targetId);
|
|
837
|
+
this.purgeSourceSizeCacheForItem(current);
|
|
838
|
+
this.updateImageInWorking(targetId, {
|
|
839
|
+
url,
|
|
840
|
+
sourceUrl: url,
|
|
841
|
+
committedUrl: undefined,
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
await this.updateImageInConfig(targetId, { url });
|
|
846
|
+
}
|
|
847
|
+
const loaded = await this.waitImageLoaded(targetId, true);
|
|
848
|
+
if (loaded && options.operation) {
|
|
849
|
+
await this.applyImageOperation(targetId, options.operation, {
|
|
850
|
+
target: this.isToolActive ? "working" : "config",
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
if (loaded) {
|
|
854
|
+
this.setImageFocus(targetId);
|
|
855
|
+
}
|
|
760
856
|
return { id: targetId, mode: "replace" };
|
|
761
857
|
}
|
|
762
|
-
const id = await this.addImageEntry(url, options.addOptions,
|
|
858
|
+
const id = await this.addImageEntry(url, options.addOptions, options.operation);
|
|
763
859
|
return { id, mode: "add" };
|
|
764
860
|
}
|
|
765
|
-
addItemToWorkingSessionIfNeeded(item, sessionDirtyBeforeAdd) {
|
|
766
|
-
if (!sessionDirtyBeforeAdd || !this.isToolActive)
|
|
767
|
-
return;
|
|
768
|
-
if (this.workingItems.some((existing) => existing.id === item.id))
|
|
769
|
-
return;
|
|
770
|
-
this.workingItems = this.cloneItems([...this.workingItems, item]);
|
|
771
|
-
this.updateImages();
|
|
772
|
-
this.emitWorkingChange(item.id);
|
|
773
|
-
}
|
|
774
861
|
async updateImage(id, updates, options = {}) {
|
|
775
862
|
this.syncToolActiveFromWorkbench();
|
|
776
863
|
const target = options.target || "auto";
|
|
@@ -806,6 +893,7 @@ class ImageTool {
|
|
|
806
893
|
updateConfig(newItems, skipCanvasUpdate = false) {
|
|
807
894
|
if (!this.context)
|
|
808
895
|
return;
|
|
896
|
+
this.clearSessionNotice({ emit: false });
|
|
809
897
|
this.applyCommittedItems(newItems);
|
|
810
898
|
(0, sessionState_1.runDeferredConfigUpdate)(this, () => {
|
|
811
899
|
const configService = this.context?.services.get("ConfigurationService");
|
|
@@ -825,37 +913,6 @@ class ImageTool {
|
|
|
825
913
|
}
|
|
826
914
|
return this.canvasService.toScreenRect(frame || this.getFrameRect());
|
|
827
915
|
}
|
|
828
|
-
async resolveDefaultFitArea() {
|
|
829
|
-
if (!this.canvasService)
|
|
830
|
-
return null;
|
|
831
|
-
const frame = this.getFrameRect();
|
|
832
|
-
if (frame.width <= 0 || frame.height <= 0)
|
|
833
|
-
return null;
|
|
834
|
-
return {
|
|
835
|
-
width: Math.max(1, frame.width),
|
|
836
|
-
height: Math.max(1, frame.height),
|
|
837
|
-
left: frame.left + frame.width / 2,
|
|
838
|
-
top: frame.top + frame.height / 2,
|
|
839
|
-
};
|
|
840
|
-
}
|
|
841
|
-
async fitImageToDefaultArea(id) {
|
|
842
|
-
if (!this.canvasService)
|
|
843
|
-
return;
|
|
844
|
-
const area = await this.resolveDefaultFitArea();
|
|
845
|
-
if (area) {
|
|
846
|
-
await this.fitImageToArea(id, area);
|
|
847
|
-
return;
|
|
848
|
-
}
|
|
849
|
-
const viewport = this.canvasService.getSceneViewportRect();
|
|
850
|
-
const canvasW = Math.max(1, viewport.width || 0);
|
|
851
|
-
const canvasH = Math.max(1, viewport.height || 0);
|
|
852
|
-
await this.fitImageToArea(id, {
|
|
853
|
-
width: canvasW,
|
|
854
|
-
height: canvasH,
|
|
855
|
-
left: viewport.left + canvasW / 2,
|
|
856
|
-
top: viewport.top + canvasH / 2,
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
916
|
getImageObjects() {
|
|
860
917
|
if (!this.canvasService)
|
|
861
918
|
return [];
|
|
@@ -929,6 +986,72 @@ class ImageTool {
|
|
|
929
986
|
getCoverScale(frame, size) {
|
|
930
987
|
return (0, sourceSizeCache_1.getCoverScale)(frame, size);
|
|
931
988
|
}
|
|
989
|
+
resolvePlacementState(item) {
|
|
990
|
+
return {
|
|
991
|
+
left: Number.isFinite(item.left) ? item.left : 0.5,
|
|
992
|
+
top: Number.isFinite(item.top) ? item.top : 0.5,
|
|
993
|
+
scale: Math.max(0.05, item.scale ?? 1),
|
|
994
|
+
angle: Number.isFinite(item.angle) ? item.angle : 0,
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
async validatePlacementForItem(item) {
|
|
998
|
+
const frame = this.getFrameRect();
|
|
999
|
+
if (!frame.width || !frame.height) {
|
|
1000
|
+
return true;
|
|
1001
|
+
}
|
|
1002
|
+
const src = item.sourceUrl || item.url;
|
|
1003
|
+
if (!src) {
|
|
1004
|
+
return true;
|
|
1005
|
+
}
|
|
1006
|
+
const source = await this.resolveImageSourceSize(item.id, src);
|
|
1007
|
+
if (!source) {
|
|
1008
|
+
return true;
|
|
1009
|
+
}
|
|
1010
|
+
return (0, imagePlacement_1.validateImagePlacement)({
|
|
1011
|
+
frame,
|
|
1012
|
+
source,
|
|
1013
|
+
placement: this.resolvePlacementState(item),
|
|
1014
|
+
}).ok;
|
|
1015
|
+
}
|
|
1016
|
+
async validateImageSession() {
|
|
1017
|
+
const policy = this.getPlacementPolicy();
|
|
1018
|
+
if (policy === "free") {
|
|
1019
|
+
this.clearSessionNotice();
|
|
1020
|
+
return { ok: true, policy };
|
|
1021
|
+
}
|
|
1022
|
+
const invalidImageIds = [];
|
|
1023
|
+
for (const item of this.workingItems) {
|
|
1024
|
+
const valid = await this.validatePlacementForItem(item);
|
|
1025
|
+
if (!valid) {
|
|
1026
|
+
invalidImageIds.push(item.id);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
if (!invalidImageIds.length) {
|
|
1030
|
+
this.clearSessionNotice();
|
|
1031
|
+
return { ok: true, policy };
|
|
1032
|
+
}
|
|
1033
|
+
const notice = {
|
|
1034
|
+
code: "image-outside-frame",
|
|
1035
|
+
level: policy === "strict" ? "error" : "warning",
|
|
1036
|
+
message: policy === "strict"
|
|
1037
|
+
? "图片位置不能超出 frame,请调整后再提交。"
|
|
1038
|
+
: "图片位置已超出 frame,建议调整后再提交。",
|
|
1039
|
+
imageIds: invalidImageIds,
|
|
1040
|
+
policy,
|
|
1041
|
+
};
|
|
1042
|
+
this.setSessionNotice(notice);
|
|
1043
|
+
this.setImageFocus(invalidImageIds[0], {
|
|
1044
|
+
syncCanvasSelection: true,
|
|
1045
|
+
skipRender: true,
|
|
1046
|
+
});
|
|
1047
|
+
return {
|
|
1048
|
+
ok: policy !== "strict",
|
|
1049
|
+
reason: notice.code,
|
|
1050
|
+
message: notice.message,
|
|
1051
|
+
imageIds: notice.imageIds,
|
|
1052
|
+
policy: notice.policy,
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
932
1055
|
getFrameVisualConfig() {
|
|
933
1056
|
const strokeStyleRaw = (this.getConfig("image.frame.strokeStyle", "dashed") || "dashed");
|
|
934
1057
|
const strokeStyle = strokeStyleRaw === "dashed" || strokeStyleRaw === "hidden"
|
|
@@ -1182,15 +1305,45 @@ class ImageTool {
|
|
|
1182
1305
|
isImageSelectionActive: this.isImageSelectionActive,
|
|
1183
1306
|
focusedImageId: this.focusedImageId,
|
|
1184
1307
|
});
|
|
1308
|
+
this.emitImageStateChange();
|
|
1185
1309
|
this.canvasService.requestRenderAll();
|
|
1186
1310
|
}
|
|
1187
1311
|
clampNormalized(value) {
|
|
1188
1312
|
return Math.max(-1, Math.min(2, value));
|
|
1189
1313
|
}
|
|
1314
|
+
async setImageTransform(id, updates, options = {}) {
|
|
1315
|
+
const next = {};
|
|
1316
|
+
if (Number.isFinite(updates.scale)) {
|
|
1317
|
+
next.scale = Math.max(0.05, Number(updates.scale));
|
|
1318
|
+
}
|
|
1319
|
+
if (Number.isFinite(updates.angle)) {
|
|
1320
|
+
next.angle = Number(updates.angle);
|
|
1321
|
+
}
|
|
1322
|
+
if (Number.isFinite(updates.left)) {
|
|
1323
|
+
next.left = this.clampNormalized(Number(updates.left));
|
|
1324
|
+
}
|
|
1325
|
+
if (Number.isFinite(updates.top)) {
|
|
1326
|
+
next.top = this.clampNormalized(Number(updates.top));
|
|
1327
|
+
}
|
|
1328
|
+
if (Number.isFinite(updates.opacity)) {
|
|
1329
|
+
next.opacity = Math.max(0, Math.min(1, Number(updates.opacity)));
|
|
1330
|
+
}
|
|
1331
|
+
if (!Object.keys(next).length)
|
|
1332
|
+
return;
|
|
1333
|
+
await this.updateImage(id, next, options);
|
|
1334
|
+
}
|
|
1335
|
+
resetImageSession() {
|
|
1336
|
+
this.clearSessionNotice({ emit: false });
|
|
1337
|
+
this.workingItems = this.cloneItems(this.items);
|
|
1338
|
+
this.hasWorkingChanges = false;
|
|
1339
|
+
this.updateImages();
|
|
1340
|
+
this.emitWorkingChange();
|
|
1341
|
+
}
|
|
1190
1342
|
updateImageInWorking(id, updates) {
|
|
1191
1343
|
const index = this.workingItems.findIndex((item) => item.id === id);
|
|
1192
1344
|
if (index < 0)
|
|
1193
1345
|
return;
|
|
1346
|
+
this.clearSessionNotice({ emit: false });
|
|
1194
1347
|
const next = [...this.workingItems];
|
|
1195
1348
|
next[index] = this.normalizeItem({ ...next[index], ...updates });
|
|
1196
1349
|
this.workingItems = next;
|
|
@@ -1208,6 +1361,7 @@ class ImageTool {
|
|
|
1208
1361
|
const index = this.items.findIndex((item) => item.id === id);
|
|
1209
1362
|
if (index < 0)
|
|
1210
1363
|
return;
|
|
1364
|
+
this.clearSessionNotice({ emit: false });
|
|
1211
1365
|
const replacingSource = typeof updates.url === "string" && updates.url.length > 0;
|
|
1212
1366
|
const next = [...this.items];
|
|
1213
1367
|
const base = next[index];
|
|
@@ -1220,23 +1374,12 @@ class ImageTool {
|
|
|
1220
1374
|
url: replacingUrl,
|
|
1221
1375
|
sourceUrl: replacingUrl,
|
|
1222
1376
|
committedUrl: undefined,
|
|
1223
|
-
scale: updates.scale ?? 1,
|
|
1224
|
-
angle: updates.angle ?? 0,
|
|
1225
|
-
left: updates.left ?? 0.5,
|
|
1226
|
-
top: updates.top ?? 0.5,
|
|
1227
1377
|
}
|
|
1228
1378
|
: {}),
|
|
1229
1379
|
});
|
|
1230
1380
|
this.updateConfig(next);
|
|
1231
1381
|
if (replacingSource) {
|
|
1232
|
-
this.debug("replace:image:begin", { id, replacingUrl });
|
|
1233
1382
|
this.purgeSourceSizeCacheForItem(base);
|
|
1234
|
-
const loaded = await this.waitImageLoaded(id, true);
|
|
1235
|
-
this.debug("replace:image:loaded", { id, loaded });
|
|
1236
|
-
if (loaded) {
|
|
1237
|
-
await this.refitImageToFrame(id);
|
|
1238
|
-
this.setImageFocus(id);
|
|
1239
|
-
}
|
|
1240
1383
|
}
|
|
1241
1384
|
}
|
|
1242
1385
|
waitImageLoaded(id, forceWait = false) {
|
|
@@ -1254,73 +1397,53 @@ class ImageTool {
|
|
|
1254
1397
|
});
|
|
1255
1398
|
});
|
|
1256
1399
|
}
|
|
1257
|
-
async
|
|
1400
|
+
async resolveImageSourceSize(id, src) {
|
|
1258
1401
|
const obj = this.getImageObject(id);
|
|
1259
|
-
if (
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
const
|
|
1268
|
-
const
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
scale: Number.isFinite(zoom) ? zoom : 1,
|
|
1273
|
-
angle: 0,
|
|
1274
|
-
left: 0.5,
|
|
1275
|
-
top: 0.5,
|
|
1276
|
-
};
|
|
1277
|
-
const index = this.items.findIndex((item) => item.id === id);
|
|
1278
|
-
if (index < 0)
|
|
1279
|
-
return;
|
|
1280
|
-
const next = [...this.items];
|
|
1281
|
-
next[index] = this.normalizeItem({ ...next[index], ...updated });
|
|
1282
|
-
this.updateConfig(next);
|
|
1283
|
-
this.workingItems = this.cloneItems(next);
|
|
1284
|
-
this.hasWorkingChanges = false;
|
|
1285
|
-
this.updateImages();
|
|
1286
|
-
this.emitWorkingChange(id);
|
|
1402
|
+
if (obj) {
|
|
1403
|
+
this.rememberSourceSize(src, obj);
|
|
1404
|
+
}
|
|
1405
|
+
const ensured = await this.ensureSourceSize(src);
|
|
1406
|
+
if (ensured)
|
|
1407
|
+
return ensured;
|
|
1408
|
+
if (!obj)
|
|
1409
|
+
return null;
|
|
1410
|
+
const width = Number(obj?.width || 0);
|
|
1411
|
+
const height = Number(obj?.height || 0);
|
|
1412
|
+
if (width <= 0 || height <= 0)
|
|
1413
|
+
return null;
|
|
1414
|
+
return { width, height };
|
|
1287
1415
|
}
|
|
1288
|
-
async
|
|
1416
|
+
async applyImageOperation(id, operation, options = {}) {
|
|
1289
1417
|
if (!this.canvasService)
|
|
1290
1418
|
return;
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
return;
|
|
1297
|
-
const renderItems = this.isToolActive ? this.workingItems : this.items;
|
|
1419
|
+
this.syncToolActiveFromWorkbench();
|
|
1420
|
+
const target = options.target || "auto";
|
|
1421
|
+
const renderItems = target === "working" || (target === "auto" && this.isToolActive)
|
|
1422
|
+
? this.workingItems
|
|
1423
|
+
: this.items;
|
|
1298
1424
|
const current = renderItems.find((item) => item.id === id);
|
|
1299
1425
|
if (!current)
|
|
1300
1426
|
return;
|
|
1301
1427
|
const render = this.resolveRenderImageState(current);
|
|
1302
|
-
this.
|
|
1303
|
-
|
|
1428
|
+
const source = await this.resolveImageSourceSize(id, render.src);
|
|
1429
|
+
if (!source)
|
|
1430
|
+
return;
|
|
1304
1431
|
const frame = this.getFrameRect();
|
|
1305
|
-
const baseCover = this.getCoverScale(frame, source);
|
|
1306
|
-
const desiredScale = Math.max(Math.max(1, area.width) / Math.max(1, source.width), Math.max(1, area.height) / Math.max(1, source.height));
|
|
1307
1432
|
const viewport = this.canvasService.getSceneViewportRect();
|
|
1308
|
-
const
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
const
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
};
|
|
1323
|
-
if (this.isToolActive) {
|
|
1433
|
+
const area = operation.type === "resetTransform"
|
|
1434
|
+
? (0, imageOperations_1.resolveImageOperationArea)({ frame, viewport })
|
|
1435
|
+
: (0, imageOperations_1.resolveImageOperationArea)({
|
|
1436
|
+
frame,
|
|
1437
|
+
viewport,
|
|
1438
|
+
area: operation.area,
|
|
1439
|
+
});
|
|
1440
|
+
const updates = (0, imageOperations_1.computeImageOperationUpdates)({
|
|
1441
|
+
frame,
|
|
1442
|
+
source,
|
|
1443
|
+
operation,
|
|
1444
|
+
area,
|
|
1445
|
+
});
|
|
1446
|
+
if (target === "working" || (target === "auto" && this.isToolActive)) {
|
|
1324
1447
|
this.updateImageInWorking(id, updates);
|
|
1325
1448
|
return;
|
|
1326
1449
|
}
|
|
@@ -1357,11 +1480,19 @@ class ImageTool {
|
|
|
1357
1480
|
}
|
|
1358
1481
|
}
|
|
1359
1482
|
this.hasWorkingChanges = false;
|
|
1483
|
+
this.clearSessionNotice({ emit: false });
|
|
1360
1484
|
this.workingItems = this.cloneItems(next);
|
|
1361
1485
|
this.updateConfig(next);
|
|
1362
1486
|
this.emitWorkingChange(this.focusedImageId);
|
|
1363
1487
|
return { ok: true };
|
|
1364
1488
|
}
|
|
1489
|
+
async completeImageSession() {
|
|
1490
|
+
const validation = await this.validateImageSession();
|
|
1491
|
+
if (!validation.ok) {
|
|
1492
|
+
return validation;
|
|
1493
|
+
}
|
|
1494
|
+
return await this.commitWorkingImagesAsCropped();
|
|
1495
|
+
}
|
|
1365
1496
|
async exportCroppedImageByIds(imageIds, options) {
|
|
1366
1497
|
if (!this.canvasService) {
|
|
1367
1498
|
throw new Error("CanvasService not initialized");
|