@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/dist/index.js CHANGED
@@ -1074,6 +1074,7 @@ __export(index_exports, {
1074
1074
  ViewportSystem: () => ViewportSystem,
1075
1075
  WhiteInkTool: () => WhiteInkTool,
1076
1076
  computeImageCoverScale: () => getCoverScale,
1077
+ computeImageOperationUpdates: () => computeImageOperationUpdates,
1077
1078
  computeWhiteInkCoverScale: () => getCoverScale,
1078
1079
  createDefaultDielineState: () => createDefaultDielineState,
1079
1080
  createDielineCommands: () => createDielineCommands,
@@ -1083,7 +1084,9 @@ __export(index_exports, {
1083
1084
  createWhiteInkCommands: () => createWhiteInkCommands,
1084
1085
  createWhiteInkConfigurations: () => createWhiteInkConfigurations,
1085
1086
  evaluateVisibilityExpr: () => evaluateVisibilityExpr,
1086
- readDielineState: () => readDielineState
1087
+ hasAnyImageInViewState: () => hasAnyImageInViewState,
1088
+ readDielineState: () => readDielineState,
1089
+ resolveImageOperationArea: () => resolveImageOperationArea
1087
1090
  });
1088
1091
  module.exports = __toCommonJS(index_exports);
1089
1092
 
@@ -2349,30 +2352,35 @@ function createImageCommands(tool) {
2349
2352
  }
2350
2353
  },
2351
2354
  {
2352
- command: "getWorkingImages",
2353
- id: "getWorkingImages",
2354
- title: "Get Working Images",
2355
+ command: "applyImageOperation",
2356
+ id: "applyImageOperation",
2357
+ title: "Apply Image Operation",
2358
+ handler: async (id, operation, options = {}) => {
2359
+ await tool.applyImageOperation(id, operation, options);
2360
+ }
2361
+ },
2362
+ {
2363
+ command: "getImageViewState",
2364
+ id: "getImageViewState",
2365
+ title: "Get Image View State",
2355
2366
  handler: () => {
2356
- return tool.cloneItems(tool.workingItems);
2367
+ return tool.getImageViewState();
2357
2368
  }
2358
2369
  },
2359
2370
  {
2360
- command: "setWorkingImage",
2361
- id: "setWorkingImage",
2362
- title: "Set Working Image",
2363
- handler: (id, updates) => {
2364
- tool.updateImageInWorking(id, updates);
2371
+ command: "setImageTransform",
2372
+ id: "setImageTransform",
2373
+ title: "Set Image Transform",
2374
+ handler: async (id, updates, options = {}) => {
2375
+ await tool.setImageTransform(id, updates, options);
2365
2376
  }
2366
2377
  },
2367
2378
  {
2368
- command: "resetWorkingImages",
2369
- id: "resetWorkingImages",
2370
- title: "Reset Working Images",
2379
+ command: "imageSessionReset",
2380
+ id: "imageSessionReset",
2381
+ title: "Reset Image Session",
2371
2382
  handler: () => {
2372
- tool.workingItems = tool.cloneItems(tool.items);
2373
- tool.hasWorkingChanges = false;
2374
- tool.updateImages();
2375
- tool.emitWorkingChange();
2383
+ tool.resetImageSession();
2376
2384
  }
2377
2385
  },
2378
2386
  {
@@ -2391,22 +2399,6 @@ function createImageCommands(tool) {
2391
2399
  return await tool.exportUserCroppedImage(options);
2392
2400
  }
2393
2401
  },
2394
- {
2395
- command: "fitImageToArea",
2396
- id: "fitImageToArea",
2397
- title: "Fit Image to Area",
2398
- handler: async (id, area) => {
2399
- await tool.fitImageToArea(id, area);
2400
- }
2401
- },
2402
- {
2403
- command: "fitImageToDefaultArea",
2404
- id: "fitImageToDefaultArea",
2405
- title: "Fit Image to Default Area",
2406
- handler: async (id) => {
2407
- await tool.fitImageToDefaultArea(id);
2408
- }
2409
- },
2410
2402
  {
2411
2403
  command: "focusImage",
2412
2404
  id: "focusImage",
@@ -2420,9 +2412,10 @@ function createImageCommands(tool) {
2420
2412
  id: "removeImage",
2421
2413
  title: "Remove Image",
2422
2414
  handler: (id) => {
2423
- const removed = tool.items.find((item) => item.id === id);
2424
- const next = tool.items.filter((item) => item.id !== id);
2425
- if (next.length !== tool.items.length) {
2415
+ const sourceItems = tool.isToolActive ? tool.workingItems : tool.items;
2416
+ const removed = sourceItems.find((item) => item.id === id);
2417
+ const next = sourceItems.filter((item) => item.id !== id);
2418
+ if (next.length !== sourceItems.length) {
2426
2419
  tool.purgeSourceSizeCacheForItem(removed);
2427
2420
  if (tool.focusedImageId === id) {
2428
2421
  tool.setImageFocus(null, {
@@ -2430,6 +2423,13 @@ function createImageCommands(tool) {
2430
2423
  skipRender: true
2431
2424
  });
2432
2425
  }
2426
+ if (tool.isToolActive) {
2427
+ tool.workingItems = tool.cloneItems(next);
2428
+ tool.hasWorkingChanges = true;
2429
+ tool.updateImages();
2430
+ tool.emitWorkingChange(id);
2431
+ return;
2432
+ }
2433
2433
  tool.updateConfig(next);
2434
2434
  }
2435
2435
  }
@@ -2452,6 +2452,13 @@ function createImageCommands(tool) {
2452
2452
  syncCanvasSelection: true,
2453
2453
  skipRender: true
2454
2454
  });
2455
+ if (tool.isToolActive) {
2456
+ tool.workingItems = [];
2457
+ tool.hasWorkingChanges = true;
2458
+ tool.updateImages();
2459
+ tool.emitWorkingChange();
2460
+ return;
2461
+ }
2455
2462
  tool.updateConfig([]);
2456
2463
  }
2457
2464
  },
@@ -2460,11 +2467,19 @@ function createImageCommands(tool) {
2460
2467
  id: "bringToFront",
2461
2468
  title: "Bring Image to Front",
2462
2469
  handler: (id) => {
2463
- const index = tool.items.findIndex((item) => item.id === id);
2464
- if (index !== -1 && index < tool.items.length - 1) {
2465
- const next = [...tool.items];
2470
+ const sourceItems = tool.isToolActive ? tool.workingItems : tool.items;
2471
+ const index = sourceItems.findIndex((item) => item.id === id);
2472
+ if (index !== -1 && index < sourceItems.length - 1) {
2473
+ const next = [...sourceItems];
2466
2474
  const [item] = next.splice(index, 1);
2467
2475
  next.push(item);
2476
+ if (tool.isToolActive) {
2477
+ tool.workingItems = tool.cloneItems(next);
2478
+ tool.hasWorkingChanges = true;
2479
+ tool.updateImages();
2480
+ tool.emitWorkingChange(id);
2481
+ return;
2482
+ }
2468
2483
  tool.updateConfig(next);
2469
2484
  }
2470
2485
  }
@@ -2474,11 +2489,19 @@ function createImageCommands(tool) {
2474
2489
  id: "sendToBack",
2475
2490
  title: "Send Image to Back",
2476
2491
  handler: (id) => {
2477
- const index = tool.items.findIndex((item) => item.id === id);
2492
+ const sourceItems = tool.isToolActive ? tool.workingItems : tool.items;
2493
+ const index = sourceItems.findIndex((item) => item.id === id);
2478
2494
  if (index > 0) {
2479
- const next = [...tool.items];
2495
+ const next = [...sourceItems];
2480
2496
  const [item] = next.splice(index, 1);
2481
2497
  next.unshift(item);
2498
+ if (tool.isToolActive) {
2499
+ tool.workingItems = tool.cloneItems(next);
2500
+ tool.hasWorkingChanges = true;
2501
+ tool.updateImages();
2502
+ tool.emitWorkingChange(id);
2503
+ return;
2504
+ }
2482
2505
  tool.updateConfig(next);
2483
2506
  }
2484
2507
  }
@@ -2614,6 +2637,78 @@ function createImageConfigurations() {
2614
2637
  ];
2615
2638
  }
2616
2639
 
2640
+ // src/extensions/image/imageOperations.ts
2641
+ function clampNormalizedAnchor(value) {
2642
+ return Math.max(-1, Math.min(2, value));
2643
+ }
2644
+ function toNormalizedAnchor(center, start, size) {
2645
+ return clampNormalizedAnchor((center - start) / Math.max(1, size));
2646
+ }
2647
+ function resolveAbsoluteScale(operation, area, source) {
2648
+ const widthScale = Math.max(1, area.width) / Math.max(1, source.width);
2649
+ const heightScale = Math.max(1, area.height) / Math.max(1, source.height);
2650
+ switch (operation.type) {
2651
+ case "cover":
2652
+ return Math.max(widthScale, heightScale);
2653
+ case "contain":
2654
+ return Math.min(widthScale, heightScale);
2655
+ case "maximizeWidth":
2656
+ return widthScale;
2657
+ case "maximizeHeight":
2658
+ return heightScale;
2659
+ default:
2660
+ return null;
2661
+ }
2662
+ }
2663
+ function resolveImageOperationArea(args) {
2664
+ const spec = args.area || { type: "frame" };
2665
+ if (spec.type === "custom") {
2666
+ return {
2667
+ width: Math.max(1, spec.width),
2668
+ height: Math.max(1, spec.height),
2669
+ centerX: spec.centerX,
2670
+ centerY: spec.centerY
2671
+ };
2672
+ }
2673
+ if (spec.type === "viewport") {
2674
+ return {
2675
+ width: Math.max(1, args.viewport.width),
2676
+ height: Math.max(1, args.viewport.height),
2677
+ centerX: args.viewport.left + args.viewport.width / 2,
2678
+ centerY: args.viewport.top + args.viewport.height / 2
2679
+ };
2680
+ }
2681
+ return {
2682
+ width: Math.max(1, args.frame.width),
2683
+ height: Math.max(1, args.frame.height),
2684
+ centerX: args.frame.left + args.frame.width / 2,
2685
+ centerY: args.frame.top + args.frame.height / 2
2686
+ };
2687
+ }
2688
+ function computeImageOperationUpdates(args) {
2689
+ const { frame, source, operation, area } = args;
2690
+ if (operation.type === "resetTransform") {
2691
+ return {
2692
+ scale: 1,
2693
+ left: 0.5,
2694
+ top: 0.5,
2695
+ angle: 0
2696
+ };
2697
+ }
2698
+ const left = toNormalizedAnchor(area.centerX, frame.left, frame.width);
2699
+ const top = toNormalizedAnchor(area.centerY, frame.top, frame.height);
2700
+ if (operation.type === "center") {
2701
+ return { left, top };
2702
+ }
2703
+ const absoluteScale = resolveAbsoluteScale(operation, area, source);
2704
+ const coverScale = getCoverScale(frame, source);
2705
+ return {
2706
+ scale: Math.max(0.05, (absoluteScale || coverScale) / coverScale),
2707
+ left,
2708
+ top
2709
+ };
2710
+ }
2711
+
2617
2712
  // src/extensions/geometry.ts
2618
2713
  var import_paper = __toESM(require("paper"));
2619
2714
 
@@ -3648,6 +3743,7 @@ var ImageTool = class {
3648
3743
  this.clearRenderedImages();
3649
3744
  (_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
3650
3745
  this.renderProducerDisposable = void 0;
3746
+ this.emitImageStateChange();
3651
3747
  if (this.canvasService) {
3652
3748
  void this.canvasService.flushRenderFromProducers();
3653
3749
  this.canvasService = void 0;
@@ -4057,9 +4153,9 @@ var ImageTool = class {
4057
4153
  name: "Image",
4058
4154
  interaction: "session",
4059
4155
  commands: {
4060
- begin: "resetWorkingImages",
4156
+ begin: "imageSessionReset",
4061
4157
  commit: "completeImages",
4062
- rollback: "resetWorkingImages"
4158
+ rollback: "imageSessionReset"
4063
4159
  },
4064
4160
  session: {
4065
4161
  autoBegin: true,
@@ -4093,6 +4189,28 @@ var ImageTool = class {
4093
4189
  cloneItems(items) {
4094
4190
  return this.normalizeItems((items || []).map((i) => ({ ...i })));
4095
4191
  }
4192
+ getViewItems() {
4193
+ return this.isToolActive ? this.workingItems : this.items;
4194
+ }
4195
+ getImageViewState() {
4196
+ this.syncToolActiveFromWorkbench();
4197
+ const items = this.cloneItems(this.getViewItems());
4198
+ const focusedItem = this.focusedImageId == null ? null : items.find((item) => item.id === this.focusedImageId) || null;
4199
+ return {
4200
+ items,
4201
+ hasAnyImage: items.length > 0,
4202
+ focusedId: this.focusedImageId,
4203
+ focusedItem,
4204
+ isToolActive: this.isToolActive,
4205
+ isImageSelectionActive: this.isImageSelectionActive,
4206
+ hasWorkingChanges: this.hasWorkingChanges,
4207
+ source: this.isToolActive ? "working" : "committed"
4208
+ };
4209
+ }
4210
+ emitImageStateChange() {
4211
+ var _a;
4212
+ (_a = this.context) == null ? void 0 : _a.eventBus.emit("image:state:change", this.getImageViewState());
4213
+ }
4096
4214
  emitWorkingChange(changedId = null) {
4097
4215
  var _a;
4098
4216
  (_a = this.context) == null ? void 0 : _a.eventBus.emit("image:working:change", {
@@ -4128,10 +4246,13 @@ var ImageTool = class {
4128
4246
  }
4129
4247
  if (!options.skipRender) {
4130
4248
  this.updateImages();
4249
+ } else {
4250
+ this.emitImageStateChange();
4131
4251
  }
4132
4252
  return { ok: true, id };
4133
4253
  }
4134
- async addImageEntry(url, options, fitOnAdd = true) {
4254
+ async addImageEntry(url, options, operation) {
4255
+ this.syncToolActiveFromWorkbench();
4135
4256
  const id = this.generateId();
4136
4257
  const newItem = this.normalizeItem({
4137
4258
  id,
@@ -4139,13 +4260,20 @@ var ImageTool = class {
4139
4260
  opacity: 1,
4140
4261
  ...options
4141
4262
  });
4142
- const sessionDirtyBeforeAdd = this.isToolActive && this.hasWorkingChanges;
4143
4263
  const waitLoaded = this.waitImageLoaded(id, true);
4144
- this.updateConfig([...this.items, newItem]);
4145
- this.addItemToWorkingSessionIfNeeded(newItem, sessionDirtyBeforeAdd);
4264
+ if (this.isToolActive) {
4265
+ this.workingItems = this.cloneItems([...this.workingItems, newItem]);
4266
+ this.hasWorkingChanges = true;
4267
+ this.updateImages();
4268
+ this.emitWorkingChange(id);
4269
+ } else {
4270
+ this.updateConfig([...this.items, newItem]);
4271
+ }
4146
4272
  const loaded = await waitLoaded;
4147
- if (loaded && fitOnAdd) {
4148
- await this.fitImageToDefaultArea(id);
4273
+ if (loaded && operation) {
4274
+ await this.applyImageOperation(id, operation, {
4275
+ target: this.isToolActive ? "working" : "config"
4276
+ });
4149
4277
  }
4150
4278
  if (loaded) {
4151
4279
  this.setImageFocus(id);
@@ -4153,8 +4281,8 @@ var ImageTool = class {
4153
4281
  return id;
4154
4282
  }
4155
4283
  async upsertImageEntry(url, options = {}) {
4284
+ this.syncToolActiveFromWorkbench();
4156
4285
  const mode = options.mode || (options.id ? "replace" : "add");
4157
- const fitOnAdd = options.fitOnAdd !== false;
4158
4286
  if (mode === "replace") {
4159
4287
  if (!options.id) {
4160
4288
  throw new Error("replace-target-id-required");
@@ -4163,19 +4291,31 @@ var ImageTool = class {
4163
4291
  if (!this.hasImageItem(targetId)) {
4164
4292
  throw new Error("replace-target-not-found");
4165
4293
  }
4166
- await this.updateImageInConfig(targetId, { url });
4294
+ if (this.isToolActive) {
4295
+ const current = this.workingItems.find((item) => item.id === targetId) || this.items.find((item) => item.id === targetId);
4296
+ this.purgeSourceSizeCacheForItem(current);
4297
+ this.updateImageInWorking(targetId, {
4298
+ url,
4299
+ sourceUrl: url,
4300
+ committedUrl: void 0
4301
+ });
4302
+ } else {
4303
+ await this.updateImageInConfig(targetId, { url });
4304
+ }
4305
+ const loaded = await this.waitImageLoaded(targetId, true);
4306
+ if (loaded && options.operation) {
4307
+ await this.applyImageOperation(targetId, options.operation, {
4308
+ target: this.isToolActive ? "working" : "config"
4309
+ });
4310
+ }
4311
+ if (loaded) {
4312
+ this.setImageFocus(targetId);
4313
+ }
4167
4314
  return { id: targetId, mode: "replace" };
4168
4315
  }
4169
- const id = await this.addImageEntry(url, options.addOptions, fitOnAdd);
4316
+ const id = await this.addImageEntry(url, options.addOptions, options.operation);
4170
4317
  return { id, mode: "add" };
4171
4318
  }
4172
- addItemToWorkingSessionIfNeeded(item, sessionDirtyBeforeAdd) {
4173
- if (!sessionDirtyBeforeAdd || !this.isToolActive) return;
4174
- if (this.workingItems.some((existing) => existing.id === item.id)) return;
4175
- this.workingItems = this.cloneItems([...this.workingItems, item]);
4176
- this.updateImages();
4177
- this.emitWorkingChange(item.id);
4178
- }
4179
4319
  async updateImage(id, updates, options = {}) {
4180
4320
  this.syncToolActiveFromWorkbench();
4181
4321
  const target = options.target || "auto";
@@ -4240,34 +4380,6 @@ var ImageTool = class {
4240
4380
  }
4241
4381
  return this.canvasService.toScreenRect(frame || this.getFrameRect());
4242
4382
  }
4243
- async resolveDefaultFitArea() {
4244
- if (!this.canvasService) return null;
4245
- const frame = this.getFrameRect();
4246
- if (frame.width <= 0 || frame.height <= 0) return null;
4247
- return {
4248
- width: Math.max(1, frame.width),
4249
- height: Math.max(1, frame.height),
4250
- left: frame.left + frame.width / 2,
4251
- top: frame.top + frame.height / 2
4252
- };
4253
- }
4254
- async fitImageToDefaultArea(id) {
4255
- if (!this.canvasService) return;
4256
- const area = await this.resolveDefaultFitArea();
4257
- if (area) {
4258
- await this.fitImageToArea(id, area);
4259
- return;
4260
- }
4261
- const viewport = this.canvasService.getSceneViewportRect();
4262
- const canvasW = Math.max(1, viewport.width || 0);
4263
- const canvasH = Math.max(1, viewport.height || 0);
4264
- await this.fitImageToArea(id, {
4265
- width: canvasW,
4266
- height: canvasH,
4267
- left: viewport.left + canvasW / 2,
4268
- top: viewport.top + canvasH / 2
4269
- });
4270
- }
4271
4383
  getImageObjects() {
4272
4384
  if (!this.canvasService) return [];
4273
4385
  return this.canvasService.canvas.getObjects().filter((obj) => {
@@ -4598,11 +4710,38 @@ var ImageTool = class {
4598
4710
  isImageSelectionActive: this.isImageSelectionActive,
4599
4711
  focusedImageId: this.focusedImageId
4600
4712
  });
4713
+ this.emitImageStateChange();
4601
4714
  this.canvasService.requestRenderAll();
4602
4715
  }
4603
4716
  clampNormalized(value) {
4604
4717
  return Math.max(-1, Math.min(2, value));
4605
4718
  }
4719
+ async setImageTransform(id, updates, options = {}) {
4720
+ const next = {};
4721
+ if (Number.isFinite(updates.scale)) {
4722
+ next.scale = Math.max(0.05, Number(updates.scale));
4723
+ }
4724
+ if (Number.isFinite(updates.angle)) {
4725
+ next.angle = Number(updates.angle);
4726
+ }
4727
+ if (Number.isFinite(updates.left)) {
4728
+ next.left = this.clampNormalized(Number(updates.left));
4729
+ }
4730
+ if (Number.isFinite(updates.top)) {
4731
+ next.top = this.clampNormalized(Number(updates.top));
4732
+ }
4733
+ if (Number.isFinite(updates.opacity)) {
4734
+ next.opacity = Math.max(0, Math.min(1, Number(updates.opacity)));
4735
+ }
4736
+ if (!Object.keys(next).length) return;
4737
+ await this.updateImage(id, next, options);
4738
+ }
4739
+ resetImageSession() {
4740
+ this.workingItems = this.cloneItems(this.items);
4741
+ this.hasWorkingChanges = false;
4742
+ this.updateImages();
4743
+ this.emitWorkingChange();
4744
+ }
4606
4745
  updateImageInWorking(id, updates) {
4607
4746
  const index = this.workingItems.findIndex((item) => item.id === id);
4608
4747
  if (index < 0) return;
@@ -4620,7 +4759,6 @@ var ImageTool = class {
4620
4759
  this.emitWorkingChange(id);
4621
4760
  }
4622
4761
  async updateImageInConfig(id, updates) {
4623
- var _a, _b, _c, _d;
4624
4762
  const index = this.items.findIndex((item) => item.id === id);
4625
4763
  if (index < 0) return;
4626
4764
  const replacingSource = typeof updates.url === "string" && updates.url.length > 0;
@@ -4633,23 +4771,12 @@ var ImageTool = class {
4633
4771
  ...replacingSource ? {
4634
4772
  url: replacingUrl,
4635
4773
  sourceUrl: replacingUrl,
4636
- committedUrl: void 0,
4637
- scale: (_a = updates.scale) != null ? _a : 1,
4638
- angle: (_b = updates.angle) != null ? _b : 0,
4639
- left: (_c = updates.left) != null ? _c : 0.5,
4640
- top: (_d = updates.top) != null ? _d : 0.5
4774
+ committedUrl: void 0
4641
4775
  } : {}
4642
4776
  });
4643
4777
  this.updateConfig(next);
4644
4778
  if (replacingSource) {
4645
- this.debug("replace:image:begin", { id, replacingUrl });
4646
4779
  this.purgeSourceSizeCacheForItem(base);
4647
- const loaded = await this.waitImageLoaded(id, true);
4648
- this.debug("replace:image:loaded", { id, loaded });
4649
- if (loaded) {
4650
- await this.refitImageToFrame(id);
4651
- this.setImageFocus(id);
4652
- }
4653
4780
  }
4654
4781
  }
4655
4782
  waitImageLoaded(id, forceWait = false) {
@@ -4667,70 +4794,43 @@ var ImageTool = class {
4667
4794
  });
4668
4795
  });
4669
4796
  }
4670
- async refitImageToFrame(id) {
4797
+ async resolveImageSourceSize(id, src) {
4671
4798
  const obj = this.getImageObject(id);
4672
- if (!obj || !this.canvasService) return;
4673
- const current = this.items.find((item) => item.id === id);
4674
- if (!current) return;
4675
- const render = this.resolveRenderImageState(current);
4676
- this.rememberSourceSize(render.src, obj);
4677
- const source = this.getSourceSize(render.src, obj);
4678
- const frame = this.getFrameRect();
4679
- const coverScale = this.getCoverScale(frame, source);
4680
- const currentScale = this.toSceneObjectScale(obj.scaleX || 1);
4681
- const zoom = Math.max(0.05, currentScale / coverScale);
4682
- const updated = {
4683
- scale: Number.isFinite(zoom) ? zoom : 1,
4684
- angle: 0,
4685
- left: 0.5,
4686
- top: 0.5
4687
- };
4688
- const index = this.items.findIndex((item) => item.id === id);
4689
- if (index < 0) return;
4690
- const next = [...this.items];
4691
- next[index] = this.normalizeItem({ ...next[index], ...updated });
4692
- this.updateConfig(next);
4693
- this.workingItems = this.cloneItems(next);
4694
- this.hasWorkingChanges = false;
4695
- this.updateImages();
4696
- this.emitWorkingChange(id);
4799
+ if (obj) {
4800
+ this.rememberSourceSize(src, obj);
4801
+ }
4802
+ const ensured = await this.ensureSourceSize(src);
4803
+ if (ensured) return ensured;
4804
+ if (!obj) return null;
4805
+ const width = Number((obj == null ? void 0 : obj.width) || 0);
4806
+ const height = Number((obj == null ? void 0 : obj.height) || 0);
4807
+ if (width <= 0 || height <= 0) return null;
4808
+ return { width, height };
4697
4809
  }
4698
- async fitImageToArea(id, area) {
4699
- var _a, _b;
4810
+ async applyImageOperation(id, operation, options = {}) {
4700
4811
  if (!this.canvasService) return;
4701
- const loaded = await this.waitImageLoaded(id, false);
4702
- if (!loaded) return;
4703
- const obj = this.getImageObject(id);
4704
- if (!obj) return;
4705
- const renderItems = this.isToolActive ? this.workingItems : this.items;
4812
+ this.syncToolActiveFromWorkbench();
4813
+ const target = options.target || "auto";
4814
+ const renderItems = target === "working" || target === "auto" && this.isToolActive ? this.workingItems : this.items;
4706
4815
  const current = renderItems.find((item) => item.id === id);
4707
4816
  if (!current) return;
4708
4817
  const render = this.resolveRenderImageState(current);
4709
- this.rememberSourceSize(render.src, obj);
4710
- const source = this.getSourceSize(render.src, obj);
4818
+ const source = await this.resolveImageSourceSize(id, render.src);
4819
+ if (!source) return;
4711
4820
  const frame = this.getFrameRect();
4712
- const baseCover = this.getCoverScale(frame, source);
4713
- const desiredScale = Math.max(
4714
- Math.max(1, area.width) / Math.max(1, source.width),
4715
- Math.max(1, area.height) / Math.max(1, source.height)
4716
- );
4717
4821
  const viewport = this.canvasService.getSceneViewportRect();
4718
- const canvasW = viewport.width || 1;
4719
- const canvasH = viewport.height || 1;
4720
- const areaLeftInput = (_a = area.left) != null ? _a : 0.5;
4721
- const areaTopInput = (_b = area.top) != null ? _b : 0.5;
4722
- const areaLeftPx = areaLeftInput <= 1.5 ? viewport.left + areaLeftInput * canvasW : areaLeftInput;
4723
- const areaTopPx = areaTopInput <= 1.5 ? viewport.top + areaTopInput * canvasH : areaTopInput;
4724
- const updates = {
4725
- scale: Math.max(0.05, desiredScale / baseCover),
4726
- left: this.clampNormalized(
4727
- (areaLeftPx - frame.left) / Math.max(1, frame.width)
4728
- ),
4729
- top: this.clampNormalized(
4730
- (areaTopPx - frame.top) / Math.max(1, frame.height)
4731
- )
4732
- };
4733
- if (this.isToolActive) {
4822
+ const area = operation.type === "resetTransform" ? resolveImageOperationArea({ frame, viewport }) : resolveImageOperationArea({
4823
+ frame,
4824
+ viewport,
4825
+ area: operation.area
4826
+ });
4827
+ const updates = computeImageOperationUpdates({
4828
+ frame,
4829
+ source,
4830
+ operation,
4831
+ area
4832
+ });
4833
+ if (target === "working" || target === "auto" && this.isToolActive) {
4734
4834
  this.updateImageInWorking(id, updates);
4735
4835
  return;
4736
4836
  }
@@ -4861,6 +4961,11 @@ var ImageTool = class {
4861
4961
  }
4862
4962
  };
4863
4963
 
4964
+ // src/extensions/image/model.ts
4965
+ function hasAnyImageInViewState(state) {
4966
+ return Boolean(state == null ? void 0 : state.hasAnyImage);
4967
+ }
4968
+
4864
4969
  // src/extensions/size/SizeTool.ts
4865
4970
  var import_core3 = require("@pooder/core");
4866
4971
  var SizeTool = class {
@@ -10787,6 +10892,7 @@ var CanvasService = class {
10787
10892
  ViewportSystem,
10788
10893
  WhiteInkTool,
10789
10894
  computeImageCoverScale,
10895
+ computeImageOperationUpdates,
10790
10896
  computeWhiteInkCoverScale,
10791
10897
  createDefaultDielineState,
10792
10898
  createDielineCommands,
@@ -10796,5 +10902,7 @@ var CanvasService = class {
10796
10902
  createWhiteInkCommands,
10797
10903
  createWhiteInkConfigurations,
10798
10904
  evaluateVisibilityExpr,
10799
- readDielineState
10905
+ hasAnyImageInViewState,
10906
+ readDielineState,
10907
+ resolveImageOperationArea
10800
10908
  });