@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.
@@ -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: "resetWorkingImages",
650
+ begin: "imageSessionReset",
649
651
  commit: "completeImages",
650
- rollback: "resetWorkingImages",
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, fitOnAdd = true) {
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
- this.updateConfig([...this.items, newItem]);
738
- this.addItemToWorkingSessionIfNeeded(newItem, sessionDirtyBeforeAdd);
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 && fitOnAdd) {
741
- await this.fitImageToDefaultArea(id);
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
- await this.updateImageInConfig(targetId, { url });
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, fitOnAdd);
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 refitImageToFrame(id) {
1292
+ async resolveImageSourceSize(id, src) {
1258
1293
  const obj = this.getImageObject(id);
1259
- if (!obj || !this.canvasService)
1260
- return;
1261
- const current = this.items.find((item) => item.id === id);
1262
- if (!current)
1263
- return;
1264
- const render = this.resolveRenderImageState(current);
1265
- this.rememberSourceSize(render.src, obj);
1266
- const source = this.getSourceSize(render.src, obj);
1267
- const frame = this.getFrameRect();
1268
- const coverScale = this.getCoverScale(frame, source);
1269
- const currentScale = this.toSceneObjectScale(obj.scaleX || 1);
1270
- const zoom = Math.max(0.05, currentScale / coverScale);
1271
- const updated = {
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 fitImageToArea(id, area) {
1308
+ async applyImageOperation(id, operation, options = {}) {
1289
1309
  if (!this.canvasService)
1290
1310
  return;
1291
- const loaded = await this.waitImageLoaded(id, false);
1292
- if (!loaded)
1293
- return;
1294
- const obj = this.getImageObject(id);
1295
- if (!obj)
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.rememberSourceSize(render.src, obj);
1303
- const source = this.getSourceSize(render.src, obj);
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 canvasW = viewport.width || 1;
1309
- const canvasH = viewport.height || 1;
1310
- const areaLeftInput = area.left ?? 0.5;
1311
- const areaTopInput = area.top ?? 0.5;
1312
- const areaLeftPx = areaLeftInput <= 1.5
1313
- ? viewport.left + areaLeftInput * canvasW
1314
- : areaLeftInput;
1315
- const areaTopPx = areaTopInput <= 1.5
1316
- ? viewport.top + areaTopInput * canvasH
1317
- : areaTopInput;
1318
- const updates = {
1319
- scale: Math.max(0.05, desiredScale / baseCover),
1320
- left: this.clampNormalized((areaLeftPx - frame.left) / Math.max(1, frame.width)),
1321
- top: this.clampNormalized((areaTopPx - frame.top) / Math.max(1, frame.height)),
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: "getWorkingImages",
28
- id: "getWorkingImages",
29
- title: "Get Working Images",
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.cloneItems(tool.workingItems);
39
+ return tool.getImageViewState();
32
40
  },
33
41
  },
34
42
  {
35
- command: "setWorkingImage",
36
- id: "setWorkingImage",
37
- title: "Set Working Image",
38
- handler: (id, updates) => {
39
- tool.updateImageInWorking(id, updates);
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: "resetWorkingImages",
44
- id: "resetWorkingImages",
45
- title: "Reset Working Images",
51
+ command: "imageSessionReset",
52
+ id: "imageSessionReset",
53
+ title: "Reset Image Session",
46
54
  handler: () => {
47
- tool.workingItems = tool.cloneItems(tool.items);
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 removed = tool.items.find((item) => item.id === id);
99
- const next = tool.items.filter((item) => item.id !== id);
100
- if (next.length !== tool.items.length) {
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 index = tool.items.findIndex((item) => item.id === id);
139
- if (index !== -1 && index < tool.items.length - 1) {
140
- const next = [...tool.items];
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 index = tool.items.findIndex((item) => item.id === id);
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 = [...tool.items];
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);
@@ -1,2 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasAnyImageInViewState = hasAnyImageInViewState;
4
+ function hasAnyImageInViewState(state) {
5
+ return Boolean(state?.hasAnyImage);
6
+ }