@pooder/kit 6.2.2 → 6.3.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/.test-dist/src/extensions/image/ImageTool.js +134 -119
- package/.test-dist/src/extensions/image/commands.js +60 -40
- package/.test-dist/src/extensions/image/imageOperations.js +75 -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 +39 -5
- package/CHANGELOG.md +6 -0
- package/dist/index.d.mts +250 -163
- package/dist/index.d.ts +250 -163
- package/dist/index.js +265 -157
- package/dist/index.mjs +261 -156
- package/package.json +1 -1
- package/src/extensions/image/ImageTool.ts +172 -145
- package/src/extensions/image/commands.ts +69 -48
- package/src/extensions/image/imageOperations.ts +135 -0
- package/src/extensions/image/index.ts +1 -0
- package/src/extensions/image/model.ts +13 -1
- package/tests/run.ts +49 -8
|
@@ -11,6 +11,7 @@ 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");
|
|
14
15
|
const sessionOverlay_1 = require("./sessionOverlay");
|
|
15
16
|
const IMAGE_DEFAULT_CONTROL_CAPABILITIES = [
|
|
16
17
|
"rotate",
|
|
@@ -255,6 +256,7 @@ class ImageTool {
|
|
|
255
256
|
this.clearRenderedImages();
|
|
256
257
|
this.renderProducerDisposable?.dispose();
|
|
257
258
|
this.renderProducerDisposable = undefined;
|
|
259
|
+
this.emitImageStateChange();
|
|
258
260
|
if (this.canvasService) {
|
|
259
261
|
void this.canvasService.flushRenderFromProducers();
|
|
260
262
|
this.canvasService = undefined;
|
|
@@ -645,9 +647,9 @@ class ImageTool {
|
|
|
645
647
|
name: "Image",
|
|
646
648
|
interaction: "session",
|
|
647
649
|
commands: {
|
|
648
|
-
begin: "
|
|
650
|
+
begin: "imageSessionReset",
|
|
649
651
|
commit: "completeImages",
|
|
650
|
-
rollback: "
|
|
652
|
+
rollback: "imageSessionReset",
|
|
651
653
|
},
|
|
652
654
|
session: {
|
|
653
655
|
autoBegin: true,
|
|
@@ -685,6 +687,29 @@ class ImageTool {
|
|
|
685
687
|
cloneItems(items) {
|
|
686
688
|
return this.normalizeItems((items || []).map((i) => ({ ...i })));
|
|
687
689
|
}
|
|
690
|
+
getViewItems() {
|
|
691
|
+
return this.isToolActive ? this.workingItems : this.items;
|
|
692
|
+
}
|
|
693
|
+
getImageViewState() {
|
|
694
|
+
this.syncToolActiveFromWorkbench();
|
|
695
|
+
const items = this.cloneItems(this.getViewItems());
|
|
696
|
+
const focusedItem = this.focusedImageId == null
|
|
697
|
+
? null
|
|
698
|
+
: items.find((item) => item.id === this.focusedImageId) || null;
|
|
699
|
+
return {
|
|
700
|
+
items,
|
|
701
|
+
hasAnyImage: items.length > 0,
|
|
702
|
+
focusedId: this.focusedImageId,
|
|
703
|
+
focusedItem,
|
|
704
|
+
isToolActive: this.isToolActive,
|
|
705
|
+
isImageSelectionActive: this.isImageSelectionActive,
|
|
706
|
+
hasWorkingChanges: this.hasWorkingChanges,
|
|
707
|
+
source: this.isToolActive ? "working" : "committed",
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
emitImageStateChange() {
|
|
711
|
+
this.context?.eventBus.emit("image:state:change", this.getImageViewState());
|
|
712
|
+
}
|
|
688
713
|
emitWorkingChange(changedId = null) {
|
|
689
714
|
this.context?.eventBus.emit("image:working:change", {
|
|
690
715
|
changedId,
|
|
@@ -722,9 +747,13 @@ class ImageTool {
|
|
|
722
747
|
if (!options.skipRender) {
|
|
723
748
|
this.updateImages();
|
|
724
749
|
}
|
|
750
|
+
else {
|
|
751
|
+
this.emitImageStateChange();
|
|
752
|
+
}
|
|
725
753
|
return { ok: true, id };
|
|
726
754
|
}
|
|
727
|
-
async addImageEntry(url, options,
|
|
755
|
+
async addImageEntry(url, options, operation) {
|
|
756
|
+
this.syncToolActiveFromWorkbench();
|
|
728
757
|
const id = this.generateId();
|
|
729
758
|
const newItem = this.normalizeItem({
|
|
730
759
|
id,
|
|
@@ -732,13 +761,21 @@ class ImageTool {
|
|
|
732
761
|
opacity: 1,
|
|
733
762
|
...options,
|
|
734
763
|
});
|
|
735
|
-
const sessionDirtyBeforeAdd = this.isToolActive && this.hasWorkingChanges;
|
|
736
764
|
const waitLoaded = this.waitImageLoaded(id, true);
|
|
737
|
-
|
|
738
|
-
|
|
765
|
+
if (this.isToolActive) {
|
|
766
|
+
this.workingItems = this.cloneItems([...this.workingItems, newItem]);
|
|
767
|
+
this.hasWorkingChanges = true;
|
|
768
|
+
this.updateImages();
|
|
769
|
+
this.emitWorkingChange(id);
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
this.updateConfig([...this.items, newItem]);
|
|
773
|
+
}
|
|
739
774
|
const loaded = await waitLoaded;
|
|
740
|
-
if (loaded &&
|
|
741
|
-
await this.
|
|
775
|
+
if (loaded && operation) {
|
|
776
|
+
await this.applyImageOperation(id, operation, {
|
|
777
|
+
target: this.isToolActive ? "working" : "config",
|
|
778
|
+
});
|
|
742
779
|
}
|
|
743
780
|
if (loaded) {
|
|
744
781
|
this.setImageFocus(id);
|
|
@@ -746,8 +783,8 @@ class ImageTool {
|
|
|
746
783
|
return id;
|
|
747
784
|
}
|
|
748
785
|
async upsertImageEntry(url, options = {}) {
|
|
786
|
+
this.syncToolActiveFromWorkbench();
|
|
749
787
|
const mode = options.mode || (options.id ? "replace" : "add");
|
|
750
|
-
const fitOnAdd = options.fitOnAdd !== false;
|
|
751
788
|
if (mode === "replace") {
|
|
752
789
|
if (!options.id) {
|
|
753
790
|
throw new Error("replace-target-id-required");
|
|
@@ -756,21 +793,33 @@ class ImageTool {
|
|
|
756
793
|
if (!this.hasImageItem(targetId)) {
|
|
757
794
|
throw new Error("replace-target-not-found");
|
|
758
795
|
}
|
|
759
|
-
|
|
796
|
+
if (this.isToolActive) {
|
|
797
|
+
const current = this.workingItems.find((item) => item.id === targetId) ||
|
|
798
|
+
this.items.find((item) => item.id === targetId);
|
|
799
|
+
this.purgeSourceSizeCacheForItem(current);
|
|
800
|
+
this.updateImageInWorking(targetId, {
|
|
801
|
+
url,
|
|
802
|
+
sourceUrl: url,
|
|
803
|
+
committedUrl: undefined,
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
await this.updateImageInConfig(targetId, { url });
|
|
808
|
+
}
|
|
809
|
+
const loaded = await this.waitImageLoaded(targetId, true);
|
|
810
|
+
if (loaded && options.operation) {
|
|
811
|
+
await this.applyImageOperation(targetId, options.operation, {
|
|
812
|
+
target: this.isToolActive ? "working" : "config",
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
if (loaded) {
|
|
816
|
+
this.setImageFocus(targetId);
|
|
817
|
+
}
|
|
760
818
|
return { id: targetId, mode: "replace" };
|
|
761
819
|
}
|
|
762
|
-
const id = await this.addImageEntry(url, options.addOptions,
|
|
820
|
+
const id = await this.addImageEntry(url, options.addOptions, options.operation);
|
|
763
821
|
return { id, mode: "add" };
|
|
764
822
|
}
|
|
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
823
|
async updateImage(id, updates, options = {}) {
|
|
775
824
|
this.syncToolActiveFromWorkbench();
|
|
776
825
|
const target = options.target || "auto";
|
|
@@ -825,37 +874,6 @@ class ImageTool {
|
|
|
825
874
|
}
|
|
826
875
|
return this.canvasService.toScreenRect(frame || this.getFrameRect());
|
|
827
876
|
}
|
|
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
877
|
getImageObjects() {
|
|
860
878
|
if (!this.canvasService)
|
|
861
879
|
return [];
|
|
@@ -1182,11 +1200,39 @@ class ImageTool {
|
|
|
1182
1200
|
isImageSelectionActive: this.isImageSelectionActive,
|
|
1183
1201
|
focusedImageId: this.focusedImageId,
|
|
1184
1202
|
});
|
|
1203
|
+
this.emitImageStateChange();
|
|
1185
1204
|
this.canvasService.requestRenderAll();
|
|
1186
1205
|
}
|
|
1187
1206
|
clampNormalized(value) {
|
|
1188
1207
|
return Math.max(-1, Math.min(2, value));
|
|
1189
1208
|
}
|
|
1209
|
+
async setImageTransform(id, updates, options = {}) {
|
|
1210
|
+
const next = {};
|
|
1211
|
+
if (Number.isFinite(updates.scale)) {
|
|
1212
|
+
next.scale = Math.max(0.05, Number(updates.scale));
|
|
1213
|
+
}
|
|
1214
|
+
if (Number.isFinite(updates.angle)) {
|
|
1215
|
+
next.angle = Number(updates.angle);
|
|
1216
|
+
}
|
|
1217
|
+
if (Number.isFinite(updates.left)) {
|
|
1218
|
+
next.left = this.clampNormalized(Number(updates.left));
|
|
1219
|
+
}
|
|
1220
|
+
if (Number.isFinite(updates.top)) {
|
|
1221
|
+
next.top = this.clampNormalized(Number(updates.top));
|
|
1222
|
+
}
|
|
1223
|
+
if (Number.isFinite(updates.opacity)) {
|
|
1224
|
+
next.opacity = Math.max(0, Math.min(1, Number(updates.opacity)));
|
|
1225
|
+
}
|
|
1226
|
+
if (!Object.keys(next).length)
|
|
1227
|
+
return;
|
|
1228
|
+
await this.updateImage(id, next, options);
|
|
1229
|
+
}
|
|
1230
|
+
resetImageSession() {
|
|
1231
|
+
this.workingItems = this.cloneItems(this.items);
|
|
1232
|
+
this.hasWorkingChanges = false;
|
|
1233
|
+
this.updateImages();
|
|
1234
|
+
this.emitWorkingChange();
|
|
1235
|
+
}
|
|
1190
1236
|
updateImageInWorking(id, updates) {
|
|
1191
1237
|
const index = this.workingItems.findIndex((item) => item.id === id);
|
|
1192
1238
|
if (index < 0)
|
|
@@ -1220,23 +1266,12 @@ class ImageTool {
|
|
|
1220
1266
|
url: replacingUrl,
|
|
1221
1267
|
sourceUrl: replacingUrl,
|
|
1222
1268
|
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
1269
|
}
|
|
1228
1270
|
: {}),
|
|
1229
1271
|
});
|
|
1230
1272
|
this.updateConfig(next);
|
|
1231
1273
|
if (replacingSource) {
|
|
1232
|
-
this.debug("replace:image:begin", { id, replacingUrl });
|
|
1233
1274
|
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
1275
|
}
|
|
1241
1276
|
}
|
|
1242
1277
|
waitImageLoaded(id, forceWait = false) {
|
|
@@ -1254,73 +1289,53 @@ class ImageTool {
|
|
|
1254
1289
|
});
|
|
1255
1290
|
});
|
|
1256
1291
|
}
|
|
1257
|
-
async
|
|
1292
|
+
async resolveImageSourceSize(id, src) {
|
|
1258
1293
|
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);
|
|
1294
|
+
if (obj) {
|
|
1295
|
+
this.rememberSourceSize(src, obj);
|
|
1296
|
+
}
|
|
1297
|
+
const ensured = await this.ensureSourceSize(src);
|
|
1298
|
+
if (ensured)
|
|
1299
|
+
return ensured;
|
|
1300
|
+
if (!obj)
|
|
1301
|
+
return null;
|
|
1302
|
+
const width = Number(obj?.width || 0);
|
|
1303
|
+
const height = Number(obj?.height || 0);
|
|
1304
|
+
if (width <= 0 || height <= 0)
|
|
1305
|
+
return null;
|
|
1306
|
+
return { width, height };
|
|
1287
1307
|
}
|
|
1288
|
-
async
|
|
1308
|
+
async applyImageOperation(id, operation, options = {}) {
|
|
1289
1309
|
if (!this.canvasService)
|
|
1290
1310
|
return;
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
return;
|
|
1297
|
-
const renderItems = this.isToolActive ? this.workingItems : this.items;
|
|
1311
|
+
this.syncToolActiveFromWorkbench();
|
|
1312
|
+
const target = options.target || "auto";
|
|
1313
|
+
const renderItems = target === "working" || (target === "auto" && this.isToolActive)
|
|
1314
|
+
? this.workingItems
|
|
1315
|
+
: this.items;
|
|
1298
1316
|
const current = renderItems.find((item) => item.id === id);
|
|
1299
1317
|
if (!current)
|
|
1300
1318
|
return;
|
|
1301
1319
|
const render = this.resolveRenderImageState(current);
|
|
1302
|
-
this.
|
|
1303
|
-
|
|
1320
|
+
const source = await this.resolveImageSourceSize(id, render.src);
|
|
1321
|
+
if (!source)
|
|
1322
|
+
return;
|
|
1304
1323
|
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
1324
|
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) {
|
|
1325
|
+
const area = operation.type === "resetTransform"
|
|
1326
|
+
? (0, imageOperations_1.resolveImageOperationArea)({ frame, viewport })
|
|
1327
|
+
: (0, imageOperations_1.resolveImageOperationArea)({
|
|
1328
|
+
frame,
|
|
1329
|
+
viewport,
|
|
1330
|
+
area: operation.area,
|
|
1331
|
+
});
|
|
1332
|
+
const updates = (0, imageOperations_1.computeImageOperationUpdates)({
|
|
1333
|
+
frame,
|
|
1334
|
+
source,
|
|
1335
|
+
operation,
|
|
1336
|
+
area,
|
|
1337
|
+
});
|
|
1338
|
+
if (target === "working" || (target === "auto" && this.isToolActive)) {
|
|
1324
1339
|
this.updateImageInWorking(id, updates);
|
|
1325
1340
|
return;
|
|
1326
1341
|
}
|
|
@@ -24,30 +24,35 @@ function createImageCommands(tool) {
|
|
|
24
24
|
},
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
|
-
command: "
|
|
28
|
-
id: "
|
|
29
|
-
title: "
|
|
27
|
+
command: "applyImageOperation",
|
|
28
|
+
id: "applyImageOperation",
|
|
29
|
+
title: "Apply Image Operation",
|
|
30
|
+
handler: async (id, operation, options = {}) => {
|
|
31
|
+
await tool.applyImageOperation(id, operation, options);
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
command: "getImageViewState",
|
|
36
|
+
id: "getImageViewState",
|
|
37
|
+
title: "Get Image View State",
|
|
30
38
|
handler: () => {
|
|
31
|
-
return tool.
|
|
39
|
+
return tool.getImageViewState();
|
|
32
40
|
},
|
|
33
41
|
},
|
|
34
42
|
{
|
|
35
|
-
command: "
|
|
36
|
-
id: "
|
|
37
|
-
title: "Set
|
|
38
|
-
handler: (id, updates) => {
|
|
39
|
-
tool.
|
|
43
|
+
command: "setImageTransform",
|
|
44
|
+
id: "setImageTransform",
|
|
45
|
+
title: "Set Image Transform",
|
|
46
|
+
handler: async (id, updates, options = {}) => {
|
|
47
|
+
await tool.setImageTransform(id, updates, options);
|
|
40
48
|
},
|
|
41
49
|
},
|
|
42
50
|
{
|
|
43
|
-
command: "
|
|
44
|
-
id: "
|
|
45
|
-
title: "Reset
|
|
51
|
+
command: "imageSessionReset",
|
|
52
|
+
id: "imageSessionReset",
|
|
53
|
+
title: "Reset Image Session",
|
|
46
54
|
handler: () => {
|
|
47
|
-
tool.
|
|
48
|
-
tool.hasWorkingChanges = false;
|
|
49
|
-
tool.updateImages();
|
|
50
|
-
tool.emitWorkingChange();
|
|
55
|
+
tool.resetImageSession();
|
|
51
56
|
},
|
|
52
57
|
},
|
|
53
58
|
{
|
|
@@ -66,22 +71,6 @@ function createImageCommands(tool) {
|
|
|
66
71
|
return await tool.exportUserCroppedImage(options);
|
|
67
72
|
},
|
|
68
73
|
},
|
|
69
|
-
{
|
|
70
|
-
command: "fitImageToArea",
|
|
71
|
-
id: "fitImageToArea",
|
|
72
|
-
title: "Fit Image to Area",
|
|
73
|
-
handler: async (id, area) => {
|
|
74
|
-
await tool.fitImageToArea(id, area);
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
command: "fitImageToDefaultArea",
|
|
79
|
-
id: "fitImageToDefaultArea",
|
|
80
|
-
title: "Fit Image to Default Area",
|
|
81
|
-
handler: async (id) => {
|
|
82
|
-
await tool.fitImageToDefaultArea(id);
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
74
|
{
|
|
86
75
|
command: "focusImage",
|
|
87
76
|
id: "focusImage",
|
|
@@ -95,9 +84,10 @@ function createImageCommands(tool) {
|
|
|
95
84
|
id: "removeImage",
|
|
96
85
|
title: "Remove Image",
|
|
97
86
|
handler: (id) => {
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
|
|
87
|
+
const sourceItems = tool.isToolActive ? tool.workingItems : tool.items;
|
|
88
|
+
const removed = sourceItems.find((item) => item.id === id);
|
|
89
|
+
const next = sourceItems.filter((item) => item.id !== id);
|
|
90
|
+
if (next.length !== sourceItems.length) {
|
|
101
91
|
tool.purgeSourceSizeCacheForItem(removed);
|
|
102
92
|
if (tool.focusedImageId === id) {
|
|
103
93
|
tool.setImageFocus(null, {
|
|
@@ -105,6 +95,13 @@ function createImageCommands(tool) {
|
|
|
105
95
|
skipRender: true,
|
|
106
96
|
});
|
|
107
97
|
}
|
|
98
|
+
if (tool.isToolActive) {
|
|
99
|
+
tool.workingItems = tool.cloneItems(next);
|
|
100
|
+
tool.hasWorkingChanges = true;
|
|
101
|
+
tool.updateImages();
|
|
102
|
+
tool.emitWorkingChange(id);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
108
105
|
tool.updateConfig(next);
|
|
109
106
|
}
|
|
110
107
|
},
|
|
@@ -127,6 +124,13 @@ function createImageCommands(tool) {
|
|
|
127
124
|
syncCanvasSelection: true,
|
|
128
125
|
skipRender: true,
|
|
129
126
|
});
|
|
127
|
+
if (tool.isToolActive) {
|
|
128
|
+
tool.workingItems = [];
|
|
129
|
+
tool.hasWorkingChanges = true;
|
|
130
|
+
tool.updateImages();
|
|
131
|
+
tool.emitWorkingChange();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
130
134
|
tool.updateConfig([]);
|
|
131
135
|
},
|
|
132
136
|
},
|
|
@@ -135,11 +139,19 @@ function createImageCommands(tool) {
|
|
|
135
139
|
id: "bringToFront",
|
|
136
140
|
title: "Bring Image to Front",
|
|
137
141
|
handler: (id) => {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
const sourceItems = tool.isToolActive ? tool.workingItems : tool.items;
|
|
143
|
+
const index = sourceItems.findIndex((item) => item.id === id);
|
|
144
|
+
if (index !== -1 && index < sourceItems.length - 1) {
|
|
145
|
+
const next = [...sourceItems];
|
|
141
146
|
const [item] = next.splice(index, 1);
|
|
142
147
|
next.push(item);
|
|
148
|
+
if (tool.isToolActive) {
|
|
149
|
+
tool.workingItems = tool.cloneItems(next);
|
|
150
|
+
tool.hasWorkingChanges = true;
|
|
151
|
+
tool.updateImages();
|
|
152
|
+
tool.emitWorkingChange(id);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
143
155
|
tool.updateConfig(next);
|
|
144
156
|
}
|
|
145
157
|
},
|
|
@@ -149,11 +161,19 @@ function createImageCommands(tool) {
|
|
|
149
161
|
id: "sendToBack",
|
|
150
162
|
title: "Send Image to Back",
|
|
151
163
|
handler: (id) => {
|
|
152
|
-
const
|
|
164
|
+
const sourceItems = tool.isToolActive ? tool.workingItems : tool.items;
|
|
165
|
+
const index = sourceItems.findIndex((item) => item.id === id);
|
|
153
166
|
if (index > 0) {
|
|
154
|
-
const next = [...
|
|
167
|
+
const next = [...sourceItems];
|
|
155
168
|
const [item] = next.splice(index, 1);
|
|
156
169
|
next.unshift(item);
|
|
170
|
+
if (tool.isToolActive) {
|
|
171
|
+
tool.workingItems = tool.cloneItems(next);
|
|
172
|
+
tool.hasWorkingChanges = true;
|
|
173
|
+
tool.updateImages();
|
|
174
|
+
tool.emitWorkingChange(id);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
157
177
|
tool.updateConfig(next);
|
|
158
178
|
}
|
|
159
179
|
},
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveImageOperationArea = resolveImageOperationArea;
|
|
4
|
+
exports.computeImageOperationUpdates = computeImageOperationUpdates;
|
|
5
|
+
const sourceSizeCache_1 = require("../../shared/imaging/sourceSizeCache");
|
|
6
|
+
function clampNormalizedAnchor(value) {
|
|
7
|
+
return Math.max(-1, Math.min(2, value));
|
|
8
|
+
}
|
|
9
|
+
function toNormalizedAnchor(center, start, size) {
|
|
10
|
+
return clampNormalizedAnchor((center - start) / Math.max(1, size));
|
|
11
|
+
}
|
|
12
|
+
function resolveAbsoluteScale(operation, area, source) {
|
|
13
|
+
const widthScale = Math.max(1, area.width) / Math.max(1, source.width);
|
|
14
|
+
const heightScale = Math.max(1, area.height) / Math.max(1, source.height);
|
|
15
|
+
switch (operation.type) {
|
|
16
|
+
case "cover":
|
|
17
|
+
return Math.max(widthScale, heightScale);
|
|
18
|
+
case "contain":
|
|
19
|
+
return Math.min(widthScale, heightScale);
|
|
20
|
+
case "maximizeWidth":
|
|
21
|
+
return widthScale;
|
|
22
|
+
case "maximizeHeight":
|
|
23
|
+
return heightScale;
|
|
24
|
+
default:
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function resolveImageOperationArea(args) {
|
|
29
|
+
const spec = args.area || { type: "frame" };
|
|
30
|
+
if (spec.type === "custom") {
|
|
31
|
+
return {
|
|
32
|
+
width: Math.max(1, spec.width),
|
|
33
|
+
height: Math.max(1, spec.height),
|
|
34
|
+
centerX: spec.centerX,
|
|
35
|
+
centerY: spec.centerY,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (spec.type === "viewport") {
|
|
39
|
+
return {
|
|
40
|
+
width: Math.max(1, args.viewport.width),
|
|
41
|
+
height: Math.max(1, args.viewport.height),
|
|
42
|
+
centerX: args.viewport.left + args.viewport.width / 2,
|
|
43
|
+
centerY: args.viewport.top + args.viewport.height / 2,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
width: Math.max(1, args.frame.width),
|
|
48
|
+
height: Math.max(1, args.frame.height),
|
|
49
|
+
centerX: args.frame.left + args.frame.width / 2,
|
|
50
|
+
centerY: args.frame.top + args.frame.height / 2,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function computeImageOperationUpdates(args) {
|
|
54
|
+
const { frame, source, operation, area } = args;
|
|
55
|
+
if (operation.type === "resetTransform") {
|
|
56
|
+
return {
|
|
57
|
+
scale: 1,
|
|
58
|
+
left: 0.5,
|
|
59
|
+
top: 0.5,
|
|
60
|
+
angle: 0,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const left = toNormalizedAnchor(area.centerX, frame.left, frame.width);
|
|
64
|
+
const top = toNormalizedAnchor(area.centerY, frame.top, frame.height);
|
|
65
|
+
if (operation.type === "center") {
|
|
66
|
+
return { left, top };
|
|
67
|
+
}
|
|
68
|
+
const absoluteScale = resolveAbsoluteScale(operation, area, source);
|
|
69
|
+
const coverScale = (0, sourceSizeCache_1.getCoverScale)(frame, source);
|
|
70
|
+
return {
|
|
71
|
+
scale: Math.max(0.05, (absoluteScale || coverScale) / coverScale),
|
|
72
|
+
left,
|
|
73
|
+
top,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -17,5 +17,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./ImageTool"), exports);
|
|
18
18
|
__exportStar(require("./commands"), exports);
|
|
19
19
|
__exportStar(require("./config"), exports);
|
|
20
|
+
__exportStar(require("./imageOperations"), exports);
|
|
20
21
|
__exportStar(require("./model"), exports);
|
|
21
22
|
__exportStar(require("./renderer"), exports);
|