@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
|
@@ -39,6 +39,11 @@ import {
|
|
|
39
39
|
} from "../../shared/constants/layers";
|
|
40
40
|
import { createImageCommands } from "./commands";
|
|
41
41
|
import { createImageConfigurations } from "./config";
|
|
42
|
+
import {
|
|
43
|
+
computeImageOperationUpdates,
|
|
44
|
+
resolveImageOperationArea,
|
|
45
|
+
type ImageOperation,
|
|
46
|
+
} from "./imageOperations";
|
|
42
47
|
import { buildImageSessionOverlaySpecs } from "./sessionOverlay";
|
|
43
48
|
|
|
44
49
|
export interface ImageItem {
|
|
@@ -53,6 +58,25 @@ export interface ImageItem {
|
|
|
53
58
|
committedUrl?: string;
|
|
54
59
|
}
|
|
55
60
|
|
|
61
|
+
export interface ImageTransformUpdates {
|
|
62
|
+
scale?: number;
|
|
63
|
+
angle?: number;
|
|
64
|
+
left?: number;
|
|
65
|
+
top?: number;
|
|
66
|
+
opacity?: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ImageViewState {
|
|
70
|
+
items: ImageItem[];
|
|
71
|
+
hasAnyImage: boolean;
|
|
72
|
+
focusedId: string | null;
|
|
73
|
+
focusedItem: ImageItem | null;
|
|
74
|
+
isToolActive: boolean;
|
|
75
|
+
isImageSelectionActive: boolean;
|
|
76
|
+
hasWorkingChanges: boolean;
|
|
77
|
+
source: "working" | "committed";
|
|
78
|
+
}
|
|
79
|
+
|
|
56
80
|
interface RenderImageState {
|
|
57
81
|
src: string;
|
|
58
82
|
left: number;
|
|
@@ -92,14 +116,7 @@ interface UpsertImageOptions {
|
|
|
92
116
|
id?: string;
|
|
93
117
|
mode?: "replace" | "add";
|
|
94
118
|
addOptions?: Partial<ImageItem>;
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
interface DielineFitArea {
|
|
99
|
-
width: number;
|
|
100
|
-
height: number;
|
|
101
|
-
left: number;
|
|
102
|
-
top: number;
|
|
119
|
+
operation?: ImageOperation;
|
|
103
120
|
}
|
|
104
121
|
|
|
105
122
|
interface UpdateImageOptions {
|
|
@@ -370,6 +387,7 @@ export class ImageTool implements Extension {
|
|
|
370
387
|
this.clearRenderedImages();
|
|
371
388
|
this.renderProducerDisposable?.dispose();
|
|
372
389
|
this.renderProducerDisposable = undefined;
|
|
390
|
+
this.emitImageStateChange();
|
|
373
391
|
if (this.canvasService) {
|
|
374
392
|
void this.canvasService.flushRenderFromProducers();
|
|
375
393
|
this.canvasService = undefined;
|
|
@@ -932,9 +950,9 @@ export class ImageTool implements Extension {
|
|
|
932
950
|
name: "Image",
|
|
933
951
|
interaction: "session",
|
|
934
952
|
commands: {
|
|
935
|
-
begin: "
|
|
953
|
+
begin: "imageSessionReset",
|
|
936
954
|
commit: "completeImages",
|
|
937
|
-
rollback: "
|
|
955
|
+
rollback: "imageSessionReset",
|
|
938
956
|
},
|
|
939
957
|
session: {
|
|
940
958
|
autoBegin: true,
|
|
@@ -979,6 +997,34 @@ export class ImageTool implements Extension {
|
|
|
979
997
|
return this.normalizeItems((items || []).map((i) => ({ ...i })));
|
|
980
998
|
}
|
|
981
999
|
|
|
1000
|
+
private getViewItems(): ImageItem[] {
|
|
1001
|
+
return this.isToolActive ? this.workingItems : this.items;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
private getImageViewState(): ImageViewState {
|
|
1005
|
+
this.syncToolActiveFromWorkbench();
|
|
1006
|
+
const items = this.cloneItems(this.getViewItems());
|
|
1007
|
+
const focusedItem =
|
|
1008
|
+
this.focusedImageId == null
|
|
1009
|
+
? null
|
|
1010
|
+
: items.find((item) => item.id === this.focusedImageId) || null;
|
|
1011
|
+
|
|
1012
|
+
return {
|
|
1013
|
+
items,
|
|
1014
|
+
hasAnyImage: items.length > 0,
|
|
1015
|
+
focusedId: this.focusedImageId,
|
|
1016
|
+
focusedItem,
|
|
1017
|
+
isToolActive: this.isToolActive,
|
|
1018
|
+
isImageSelectionActive: this.isImageSelectionActive,
|
|
1019
|
+
hasWorkingChanges: this.hasWorkingChanges,
|
|
1020
|
+
source: this.isToolActive ? "working" : "committed",
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
private emitImageStateChange() {
|
|
1025
|
+
this.context?.eventBus.emit("image:state:change", this.getImageViewState());
|
|
1026
|
+
}
|
|
1027
|
+
|
|
982
1028
|
private emitWorkingChange(changedId: string | null = null) {
|
|
983
1029
|
this.context?.eventBus.emit("image:working:change", {
|
|
984
1030
|
changedId,
|
|
@@ -1026,6 +1072,8 @@ export class ImageTool implements Extension {
|
|
|
1026
1072
|
|
|
1027
1073
|
if (!options.skipRender) {
|
|
1028
1074
|
this.updateImages();
|
|
1075
|
+
} else {
|
|
1076
|
+
this.emitImageStateChange();
|
|
1029
1077
|
}
|
|
1030
1078
|
|
|
1031
1079
|
return { ok: true, id };
|
|
@@ -1034,8 +1082,9 @@ export class ImageTool implements Extension {
|
|
|
1034
1082
|
private async addImageEntry(
|
|
1035
1083
|
url: string,
|
|
1036
1084
|
options?: Partial<ImageItem>,
|
|
1037
|
-
|
|
1085
|
+
operation?: ImageOperation,
|
|
1038
1086
|
): Promise<string> {
|
|
1087
|
+
this.syncToolActiveFromWorkbench();
|
|
1039
1088
|
const id = this.generateId();
|
|
1040
1089
|
const newItem = this.normalizeItem({
|
|
1041
1090
|
id,
|
|
@@ -1044,13 +1093,20 @@ export class ImageTool implements Extension {
|
|
|
1044
1093
|
...options,
|
|
1045
1094
|
} as ImageItem);
|
|
1046
1095
|
|
|
1047
|
-
const sessionDirtyBeforeAdd = this.isToolActive && this.hasWorkingChanges;
|
|
1048
1096
|
const waitLoaded = this.waitImageLoaded(id, true);
|
|
1049
|
-
|
|
1050
|
-
|
|
1097
|
+
if (this.isToolActive) {
|
|
1098
|
+
this.workingItems = this.cloneItems([...this.workingItems, newItem]);
|
|
1099
|
+
this.hasWorkingChanges = true;
|
|
1100
|
+
this.updateImages();
|
|
1101
|
+
this.emitWorkingChange(id);
|
|
1102
|
+
} else {
|
|
1103
|
+
this.updateConfig([...this.items, newItem]);
|
|
1104
|
+
}
|
|
1051
1105
|
const loaded = await waitLoaded;
|
|
1052
|
-
if (loaded &&
|
|
1053
|
-
await this.
|
|
1106
|
+
if (loaded && operation) {
|
|
1107
|
+
await this.applyImageOperation(id, operation, {
|
|
1108
|
+
target: this.isToolActive ? "working" : "config",
|
|
1109
|
+
});
|
|
1054
1110
|
}
|
|
1055
1111
|
if (loaded) {
|
|
1056
1112
|
this.setImageFocus(id);
|
|
@@ -1062,8 +1118,8 @@ export class ImageTool implements Extension {
|
|
|
1062
1118
|
url: string,
|
|
1063
1119
|
options: UpsertImageOptions = {},
|
|
1064
1120
|
): Promise<{ id: string; mode: "replace" | "add" }> {
|
|
1121
|
+
this.syncToolActiveFromWorkbench();
|
|
1065
1122
|
const mode = options.mode || (options.id ? "replace" : "add");
|
|
1066
|
-
const fitOnAdd = options.fitOnAdd !== false;
|
|
1067
1123
|
if (mode === "replace") {
|
|
1068
1124
|
if (!options.id) {
|
|
1069
1125
|
throw new Error("replace-target-id-required");
|
|
@@ -1072,25 +1128,35 @@ export class ImageTool implements Extension {
|
|
|
1072
1128
|
if (!this.hasImageItem(targetId)) {
|
|
1073
1129
|
throw new Error("replace-target-not-found");
|
|
1074
1130
|
}
|
|
1075
|
-
|
|
1131
|
+
if (this.isToolActive) {
|
|
1132
|
+
const current =
|
|
1133
|
+
this.workingItems.find((item) => item.id === targetId) ||
|
|
1134
|
+
this.items.find((item) => item.id === targetId);
|
|
1135
|
+
this.purgeSourceSizeCacheForItem(current);
|
|
1136
|
+
this.updateImageInWorking(targetId, {
|
|
1137
|
+
url,
|
|
1138
|
+
sourceUrl: url,
|
|
1139
|
+
committedUrl: undefined,
|
|
1140
|
+
});
|
|
1141
|
+
} else {
|
|
1142
|
+
await this.updateImageInConfig(targetId, { url });
|
|
1143
|
+
}
|
|
1144
|
+
const loaded = await this.waitImageLoaded(targetId, true);
|
|
1145
|
+
if (loaded && options.operation) {
|
|
1146
|
+
await this.applyImageOperation(targetId, options.operation, {
|
|
1147
|
+
target: this.isToolActive ? "working" : "config",
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
if (loaded) {
|
|
1151
|
+
this.setImageFocus(targetId);
|
|
1152
|
+
}
|
|
1076
1153
|
return { id: targetId, mode: "replace" };
|
|
1077
1154
|
}
|
|
1078
1155
|
|
|
1079
|
-
const id = await this.addImageEntry(url, options.addOptions,
|
|
1156
|
+
const id = await this.addImageEntry(url, options.addOptions, options.operation);
|
|
1080
1157
|
return { id, mode: "add" };
|
|
1081
1158
|
}
|
|
1082
1159
|
|
|
1083
|
-
private addItemToWorkingSessionIfNeeded(
|
|
1084
|
-
item: ImageItem,
|
|
1085
|
-
sessionDirtyBeforeAdd: boolean,
|
|
1086
|
-
) {
|
|
1087
|
-
if (!sessionDirtyBeforeAdd || !this.isToolActive) return;
|
|
1088
|
-
if (this.workingItems.some((existing) => existing.id === item.id)) return;
|
|
1089
|
-
this.workingItems = this.cloneItems([...this.workingItems, item]);
|
|
1090
|
-
this.updateImages();
|
|
1091
|
-
this.emitWorkingChange(item.id);
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
1160
|
private async updateImage(
|
|
1095
1161
|
id: string,
|
|
1096
1162
|
updates: Partial<ImageItem>,
|
|
@@ -1165,38 +1231,6 @@ export class ImageTool implements Extension {
|
|
|
1165
1231
|
return this.canvasService.toScreenRect(frame || this.getFrameRect());
|
|
1166
1232
|
}
|
|
1167
1233
|
|
|
1168
|
-
private async resolveDefaultFitArea(): Promise<DielineFitArea | null> {
|
|
1169
|
-
if (!this.canvasService) return null;
|
|
1170
|
-
const frame = this.getFrameRect();
|
|
1171
|
-
if (frame.width <= 0 || frame.height <= 0) return null;
|
|
1172
|
-
return {
|
|
1173
|
-
width: Math.max(1, frame.width),
|
|
1174
|
-
height: Math.max(1, frame.height),
|
|
1175
|
-
left: frame.left + frame.width / 2,
|
|
1176
|
-
top: frame.top + frame.height / 2,
|
|
1177
|
-
};
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
private async fitImageToDefaultArea(id: string) {
|
|
1181
|
-
if (!this.canvasService) return;
|
|
1182
|
-
const area = await this.resolveDefaultFitArea();
|
|
1183
|
-
|
|
1184
|
-
if (area) {
|
|
1185
|
-
await this.fitImageToArea(id, area);
|
|
1186
|
-
return;
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
const viewport = this.canvasService.getSceneViewportRect();
|
|
1190
|
-
const canvasW = Math.max(1, viewport.width || 0);
|
|
1191
|
-
const canvasH = Math.max(1, viewport.height || 0);
|
|
1192
|
-
await this.fitImageToArea(id, {
|
|
1193
|
-
width: canvasW,
|
|
1194
|
-
height: canvasH,
|
|
1195
|
-
left: viewport.left + canvasW / 2,
|
|
1196
|
-
top: viewport.top + canvasH / 2,
|
|
1197
|
-
});
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
1234
|
private getImageObjects(): any[] {
|
|
1201
1235
|
if (!this.canvasService) return [];
|
|
1202
1236
|
return this.canvasService.canvas.getObjects().filter((obj: any) => {
|
|
@@ -1587,6 +1621,7 @@ export class ImageTool implements Extension {
|
|
|
1587
1621
|
isImageSelectionActive: this.isImageSelectionActive,
|
|
1588
1622
|
focusedImageId: this.focusedImageId,
|
|
1589
1623
|
});
|
|
1624
|
+
this.emitImageStateChange();
|
|
1590
1625
|
this.canvasService.requestRenderAll();
|
|
1591
1626
|
}
|
|
1592
1627
|
|
|
@@ -1594,6 +1629,40 @@ export class ImageTool implements Extension {
|
|
|
1594
1629
|
return Math.max(-1, Math.min(2, value));
|
|
1595
1630
|
}
|
|
1596
1631
|
|
|
1632
|
+
private async setImageTransform(
|
|
1633
|
+
id: string,
|
|
1634
|
+
updates: ImageTransformUpdates,
|
|
1635
|
+
options: UpdateImageOptions = {},
|
|
1636
|
+
) {
|
|
1637
|
+
const next: Partial<ImageItem> = {};
|
|
1638
|
+
|
|
1639
|
+
if (Number.isFinite(updates.scale as number)) {
|
|
1640
|
+
next.scale = Math.max(0.05, Number(updates.scale));
|
|
1641
|
+
}
|
|
1642
|
+
if (Number.isFinite(updates.angle as number)) {
|
|
1643
|
+
next.angle = Number(updates.angle);
|
|
1644
|
+
}
|
|
1645
|
+
if (Number.isFinite(updates.left as number)) {
|
|
1646
|
+
next.left = this.clampNormalized(Number(updates.left));
|
|
1647
|
+
}
|
|
1648
|
+
if (Number.isFinite(updates.top as number)) {
|
|
1649
|
+
next.top = this.clampNormalized(Number(updates.top));
|
|
1650
|
+
}
|
|
1651
|
+
if (Number.isFinite(updates.opacity as number)) {
|
|
1652
|
+
next.opacity = Math.max(0, Math.min(1, Number(updates.opacity)));
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
if (!Object.keys(next).length) return;
|
|
1656
|
+
await this.updateImage(id, next, options);
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
private resetImageSession() {
|
|
1660
|
+
this.workingItems = this.cloneItems(this.items);
|
|
1661
|
+
this.hasWorkingChanges = false;
|
|
1662
|
+
this.updateImages();
|
|
1663
|
+
this.emitWorkingChange();
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1597
1666
|
private onObjectModified = (e: any) => {
|
|
1598
1667
|
if (!this.isToolActive) return;
|
|
1599
1668
|
const target = e?.target;
|
|
@@ -1669,10 +1738,6 @@ export class ImageTool implements Extension {
|
|
|
1669
1738
|
url: replacingUrl,
|
|
1670
1739
|
sourceUrl: replacingUrl,
|
|
1671
1740
|
committedUrl: undefined,
|
|
1672
|
-
scale: updates.scale ?? 1,
|
|
1673
|
-
angle: updates.angle ?? 0,
|
|
1674
|
-
left: updates.left ?? 0.5,
|
|
1675
|
-
top: updates.top ?? 0.5,
|
|
1676
1741
|
}
|
|
1677
1742
|
: {}),
|
|
1678
1743
|
});
|
|
@@ -1680,14 +1745,7 @@ export class ImageTool implements Extension {
|
|
|
1680
1745
|
this.updateConfig(next);
|
|
1681
1746
|
|
|
1682
1747
|
if (replacingSource) {
|
|
1683
|
-
this.debug("replace:image:begin", { id, replacingUrl });
|
|
1684
1748
|
this.purgeSourceSizeCacheForItem(base);
|
|
1685
|
-
const loaded = await this.waitImageLoaded(id, true);
|
|
1686
|
-
this.debug("replace:image:loaded", { id, loaded });
|
|
1687
|
-
if (loaded) {
|
|
1688
|
-
await this.refitImageToFrame(id);
|
|
1689
|
-
this.setImageFocus(id);
|
|
1690
|
-
}
|
|
1691
1749
|
}
|
|
1692
1750
|
}
|
|
1693
1751
|
|
|
@@ -1709,93 +1767,62 @@ export class ImageTool implements Extension {
|
|
|
1709
1767
|
});
|
|
1710
1768
|
}
|
|
1711
1769
|
|
|
1712
|
-
private async
|
|
1770
|
+
private async resolveImageSourceSize(
|
|
1771
|
+
id: string,
|
|
1772
|
+
src: string,
|
|
1773
|
+
): Promise<SourceSize | null> {
|
|
1713
1774
|
const obj = this.getImageObject(id);
|
|
1714
|
-
if (
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
const
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
const source = this.getSourceSize(render.src, obj);
|
|
1721
|
-
const frame = this.getFrameRect();
|
|
1722
|
-
const coverScale = this.getCoverScale(frame, source);
|
|
1723
|
-
|
|
1724
|
-
const currentScale = this.toSceneObjectScale(obj.scaleX || 1);
|
|
1725
|
-
const zoom = Math.max(0.05, currentScale / coverScale);
|
|
1726
|
-
|
|
1727
|
-
const updated: Partial<ImageItem> = {
|
|
1728
|
-
scale: Number.isFinite(zoom) ? zoom : 1,
|
|
1729
|
-
angle: 0,
|
|
1730
|
-
left: 0.5,
|
|
1731
|
-
top: 0.5,
|
|
1732
|
-
};
|
|
1733
|
-
|
|
1734
|
-
const index = this.items.findIndex((item) => item.id === id);
|
|
1735
|
-
if (index < 0) return;
|
|
1775
|
+
if (obj) {
|
|
1776
|
+
this.rememberSourceSize(src, obj);
|
|
1777
|
+
}
|
|
1778
|
+
const ensured = await this.ensureSourceSize(src);
|
|
1779
|
+
if (ensured) return ensured;
|
|
1780
|
+
if (!obj) return null;
|
|
1736
1781
|
|
|
1737
|
-
const
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
this.hasWorkingChanges = false;
|
|
1742
|
-
this.updateImages();
|
|
1743
|
-
this.emitWorkingChange(id);
|
|
1782
|
+
const width = Number(obj?.width || 0);
|
|
1783
|
+
const height = Number(obj?.height || 0);
|
|
1784
|
+
if (width <= 0 || height <= 0) return null;
|
|
1785
|
+
return { width, height };
|
|
1744
1786
|
}
|
|
1745
1787
|
|
|
1746
|
-
private async
|
|
1788
|
+
private async applyImageOperation(
|
|
1747
1789
|
id: string,
|
|
1748
|
-
|
|
1790
|
+
operation: ImageOperation,
|
|
1791
|
+
options: UpdateImageOptions = {},
|
|
1749
1792
|
) {
|
|
1750
1793
|
if (!this.canvasService) return;
|
|
1751
1794
|
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1795
|
+
this.syncToolActiveFromWorkbench();
|
|
1796
|
+
const target = options.target || "auto";
|
|
1797
|
+
const renderItems =
|
|
1798
|
+
target === "working" || (target === "auto" && this.isToolActive)
|
|
1799
|
+
? this.workingItems
|
|
1800
|
+
: this.items;
|
|
1758
1801
|
const current = renderItems.find((item) => item.id === id);
|
|
1759
1802
|
if (!current) return;
|
|
1803
|
+
|
|
1760
1804
|
const render = this.resolveRenderImageState(current);
|
|
1805
|
+
const source = await this.resolveImageSourceSize(id, render.src);
|
|
1806
|
+
if (!source) return;
|
|
1761
1807
|
|
|
1762
|
-
this.rememberSourceSize(render.src, obj);
|
|
1763
|
-
const source = this.getSourceSize(render.src, obj);
|
|
1764
1808
|
const frame = this.getFrameRect();
|
|
1765
|
-
const baseCover = this.getCoverScale(frame, source);
|
|
1766
|
-
|
|
1767
|
-
const desiredScale = Math.max(
|
|
1768
|
-
Math.max(1, area.width) / Math.max(1, source.width),
|
|
1769
|
-
Math.max(1, area.height) / Math.max(1, source.height),
|
|
1770
|
-
);
|
|
1771
|
-
|
|
1772
1809
|
const viewport = this.canvasService.getSceneViewportRect();
|
|
1773
|
-
const
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
const updates: Partial<ImageItem> = {
|
|
1789
|
-
scale: Math.max(0.05, desiredScale / baseCover),
|
|
1790
|
-
left: this.clampNormalized(
|
|
1791
|
-
(areaLeftPx - frame.left) / Math.max(1, frame.width),
|
|
1792
|
-
),
|
|
1793
|
-
top: this.clampNormalized(
|
|
1794
|
-
(areaTopPx - frame.top) / Math.max(1, frame.height),
|
|
1795
|
-
),
|
|
1796
|
-
};
|
|
1810
|
+
const area =
|
|
1811
|
+
operation.type === "resetTransform"
|
|
1812
|
+
? resolveImageOperationArea({ frame, viewport })
|
|
1813
|
+
: resolveImageOperationArea({
|
|
1814
|
+
frame,
|
|
1815
|
+
viewport,
|
|
1816
|
+
area: operation.area,
|
|
1817
|
+
});
|
|
1818
|
+
const updates = computeImageOperationUpdates({
|
|
1819
|
+
frame,
|
|
1820
|
+
source,
|
|
1821
|
+
operation,
|
|
1822
|
+
area,
|
|
1823
|
+
});
|
|
1797
1824
|
|
|
1798
|
-
if (this.isToolActive) {
|
|
1825
|
+
if (target === "working" || (target === "auto" && this.isToolActive)) {
|
|
1799
1826
|
this.updateImageInWorking(id, updates);
|
|
1800
1827
|
return;
|
|
1801
1828
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CommandContribution } from "@pooder/core";
|
|
2
|
+
import type { ImageOperation } from "./imageOperations";
|
|
2
3
|
|
|
3
4
|
export function createImageCommands(tool: any): CommandContribution[] {
|
|
4
5
|
return [
|
|
@@ -23,30 +24,43 @@ export function createImageCommands(tool: any): CommandContribution[] {
|
|
|
23
24
|
},
|
|
24
25
|
},
|
|
25
26
|
{
|
|
26
|
-
command: "
|
|
27
|
-
id: "
|
|
28
|
-
title: "
|
|
27
|
+
command: "applyImageOperation",
|
|
28
|
+
id: "applyImageOperation",
|
|
29
|
+
title: "Apply Image Operation",
|
|
30
|
+
handler: async (
|
|
31
|
+
id: string,
|
|
32
|
+
operation: ImageOperation,
|
|
33
|
+
options: Record<string, any> = {},
|
|
34
|
+
) => {
|
|
35
|
+
await tool.applyImageOperation(id, operation, options);
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
command: "getImageViewState",
|
|
40
|
+
id: "getImageViewState",
|
|
41
|
+
title: "Get Image View State",
|
|
29
42
|
handler: () => {
|
|
30
|
-
return tool.
|
|
43
|
+
return tool.getImageViewState();
|
|
31
44
|
},
|
|
32
45
|
},
|
|
33
46
|
{
|
|
34
|
-
command: "
|
|
35
|
-
id: "
|
|
36
|
-
title: "Set
|
|
37
|
-
handler: (
|
|
38
|
-
|
|
47
|
+
command: "setImageTransform",
|
|
48
|
+
id: "setImageTransform",
|
|
49
|
+
title: "Set Image Transform",
|
|
50
|
+
handler: async (
|
|
51
|
+
id: string,
|
|
52
|
+
updates: Record<string, any>,
|
|
53
|
+
options: Record<string, any> = {},
|
|
54
|
+
) => {
|
|
55
|
+
await tool.setImageTransform(id, updates, options);
|
|
39
56
|
},
|
|
40
57
|
},
|
|
41
58
|
{
|
|
42
|
-
command: "
|
|
43
|
-
id: "
|
|
44
|
-
title: "Reset
|
|
59
|
+
command: "imageSessionReset",
|
|
60
|
+
id: "imageSessionReset",
|
|
61
|
+
title: "Reset Image Session",
|
|
45
62
|
handler: () => {
|
|
46
|
-
tool.
|
|
47
|
-
tool.hasWorkingChanges = false;
|
|
48
|
-
tool.updateImages();
|
|
49
|
-
tool.emitWorkingChange();
|
|
63
|
+
tool.resetImageSession();
|
|
50
64
|
},
|
|
51
65
|
},
|
|
52
66
|
{
|
|
@@ -65,30 +79,6 @@ export function createImageCommands(tool: any): CommandContribution[] {
|
|
|
65
79
|
return await tool.exportUserCroppedImage(options);
|
|
66
80
|
},
|
|
67
81
|
},
|
|
68
|
-
{
|
|
69
|
-
command: "fitImageToArea",
|
|
70
|
-
id: "fitImageToArea",
|
|
71
|
-
title: "Fit Image to Area",
|
|
72
|
-
handler: async (
|
|
73
|
-
id: string,
|
|
74
|
-
area: {
|
|
75
|
-
width: number;
|
|
76
|
-
height: number;
|
|
77
|
-
left?: number;
|
|
78
|
-
top?: number;
|
|
79
|
-
},
|
|
80
|
-
) => {
|
|
81
|
-
await tool.fitImageToArea(id, area);
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
command: "fitImageToDefaultArea",
|
|
86
|
-
id: "fitImageToDefaultArea",
|
|
87
|
-
title: "Fit Image to Default Area",
|
|
88
|
-
handler: async (id: string) => {
|
|
89
|
-
await tool.fitImageToDefaultArea(id);
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
82
|
{
|
|
93
83
|
command: "focusImage",
|
|
94
84
|
id: "focusImage",
|
|
@@ -105,9 +95,10 @@ export function createImageCommands(tool: any): CommandContribution[] {
|
|
|
105
95
|
id: "removeImage",
|
|
106
96
|
title: "Remove Image",
|
|
107
97
|
handler: (id: string) => {
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
|
|
98
|
+
const sourceItems = tool.isToolActive ? tool.workingItems : tool.items;
|
|
99
|
+
const removed = sourceItems.find((item: any) => item.id === id);
|
|
100
|
+
const next = sourceItems.filter((item: any) => item.id !== id);
|
|
101
|
+
if (next.length !== sourceItems.length) {
|
|
111
102
|
tool.purgeSourceSizeCacheForItem(removed);
|
|
112
103
|
if (tool.focusedImageId === id) {
|
|
113
104
|
tool.setImageFocus(null, {
|
|
@@ -115,6 +106,13 @@ export function createImageCommands(tool: any): CommandContribution[] {
|
|
|
115
106
|
skipRender: true,
|
|
116
107
|
});
|
|
117
108
|
}
|
|
109
|
+
if (tool.isToolActive) {
|
|
110
|
+
tool.workingItems = tool.cloneItems(next);
|
|
111
|
+
tool.hasWorkingChanges = true;
|
|
112
|
+
tool.updateImages();
|
|
113
|
+
tool.emitWorkingChange(id);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
118
116
|
tool.updateConfig(next);
|
|
119
117
|
}
|
|
120
118
|
},
|
|
@@ -141,6 +139,13 @@ export function createImageCommands(tool: any): CommandContribution[] {
|
|
|
141
139
|
syncCanvasSelection: true,
|
|
142
140
|
skipRender: true,
|
|
143
141
|
});
|
|
142
|
+
if (tool.isToolActive) {
|
|
143
|
+
tool.workingItems = [];
|
|
144
|
+
tool.hasWorkingChanges = true;
|
|
145
|
+
tool.updateImages();
|
|
146
|
+
tool.emitWorkingChange();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
144
149
|
tool.updateConfig([]);
|
|
145
150
|
},
|
|
146
151
|
},
|
|
@@ -149,11 +154,19 @@ export function createImageCommands(tool: any): CommandContribution[] {
|
|
|
149
154
|
id: "bringToFront",
|
|
150
155
|
title: "Bring Image to Front",
|
|
151
156
|
handler: (id: string) => {
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
157
|
+
const sourceItems = tool.isToolActive ? tool.workingItems : tool.items;
|
|
158
|
+
const index = sourceItems.findIndex((item: any) => item.id === id);
|
|
159
|
+
if (index !== -1 && index < sourceItems.length - 1) {
|
|
160
|
+
const next = [...sourceItems];
|
|
155
161
|
const [item] = next.splice(index, 1);
|
|
156
162
|
next.push(item);
|
|
163
|
+
if (tool.isToolActive) {
|
|
164
|
+
tool.workingItems = tool.cloneItems(next);
|
|
165
|
+
tool.hasWorkingChanges = true;
|
|
166
|
+
tool.updateImages();
|
|
167
|
+
tool.emitWorkingChange(id);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
157
170
|
tool.updateConfig(next);
|
|
158
171
|
}
|
|
159
172
|
},
|
|
@@ -163,11 +176,19 @@ export function createImageCommands(tool: any): CommandContribution[] {
|
|
|
163
176
|
id: "sendToBack",
|
|
164
177
|
title: "Send Image to Back",
|
|
165
178
|
handler: (id: string) => {
|
|
166
|
-
const
|
|
179
|
+
const sourceItems = tool.isToolActive ? tool.workingItems : tool.items;
|
|
180
|
+
const index = sourceItems.findIndex((item: any) => item.id === id);
|
|
167
181
|
if (index > 0) {
|
|
168
|
-
const next = [...
|
|
182
|
+
const next = [...sourceItems];
|
|
169
183
|
const [item] = next.splice(index, 1);
|
|
170
184
|
next.unshift(item);
|
|
185
|
+
if (tool.isToolActive) {
|
|
186
|
+
tool.workingItems = tool.cloneItems(next);
|
|
187
|
+
tool.hasWorkingChanges = true;
|
|
188
|
+
tool.updateImages();
|
|
189
|
+
tool.emitWorkingChange(id);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
171
192
|
tool.updateConfig(next);
|
|
172
193
|
}
|
|
173
194
|
},
|