@pooder/kit 6.0.0 → 6.1.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.
Files changed (99) hide show
  1. package/.test-dist/src/extensions/background/BackgroundTool.js +524 -0
  2. package/.test-dist/src/extensions/background/index.js +17 -0
  3. package/.test-dist/src/extensions/background.js +1 -1
  4. package/.test-dist/src/extensions/dieline/DielineTool.js +748 -0
  5. package/.test-dist/src/extensions/dieline/commands.js +127 -0
  6. package/.test-dist/src/extensions/dieline/config.js +107 -0
  7. package/.test-dist/src/extensions/dieline/index.js +21 -0
  8. package/.test-dist/src/extensions/dieline/model.js +2 -0
  9. package/.test-dist/src/extensions/dieline/renderer.js +2 -0
  10. package/.test-dist/src/extensions/dieline.js +4 -0
  11. package/.test-dist/src/extensions/feature/FeatureTool.js +914 -0
  12. package/.test-dist/src/extensions/feature/index.js +17 -0
  13. package/.test-dist/src/extensions/film/FilmTool.js +207 -0
  14. package/.test-dist/src/extensions/film/index.js +17 -0
  15. package/.test-dist/src/extensions/image/ImageTool.js +1499 -0
  16. package/.test-dist/src/extensions/image/commands.js +162 -0
  17. package/.test-dist/src/extensions/image/config.js +129 -0
  18. package/.test-dist/src/extensions/image/index.js +21 -0
  19. package/.test-dist/src/extensions/image/model.js +2 -0
  20. package/.test-dist/src/extensions/image/renderer.js +5 -0
  21. package/.test-dist/src/extensions/image.js +182 -7
  22. package/.test-dist/src/extensions/mirror/MirrorTool.js +104 -0
  23. package/.test-dist/src/extensions/mirror/index.js +17 -0
  24. package/.test-dist/src/extensions/ruler/RulerTool.js +442 -0
  25. package/.test-dist/src/extensions/ruler/index.js +17 -0
  26. package/.test-dist/src/extensions/sceneLayout.js +2 -93
  27. package/.test-dist/src/extensions/sceneLayoutModel.js +15 -200
  28. package/.test-dist/src/extensions/size/SizeTool.js +332 -0
  29. package/.test-dist/src/extensions/size/index.js +17 -0
  30. package/.test-dist/src/extensions/white-ink/WhiteInkTool.js +1003 -0
  31. package/.test-dist/src/extensions/white-ink/commands.js +148 -0
  32. package/.test-dist/src/extensions/white-ink/config.js +31 -0
  33. package/.test-dist/src/extensions/white-ink/index.js +21 -0
  34. package/.test-dist/src/extensions/white-ink/model.js +2 -0
  35. package/.test-dist/src/extensions/white-ink/renderer.js +5 -0
  36. package/.test-dist/src/services/CanvasService.js +34 -13
  37. package/.test-dist/src/services/SceneLayoutService.js +96 -0
  38. package/.test-dist/src/services/index.js +1 -0
  39. package/.test-dist/src/services/visibility.js +3 -0
  40. package/.test-dist/src/shared/constants/layers.js +25 -0
  41. package/.test-dist/src/shared/imaging/sourceSizeCache.js +82 -0
  42. package/.test-dist/src/shared/index.js +22 -0
  43. package/.test-dist/src/shared/runtime/sessionState.js +74 -0
  44. package/.test-dist/src/shared/runtime/subscriptions.js +30 -0
  45. package/.test-dist/src/shared/scene/frame.js +34 -0
  46. package/.test-dist/src/shared/scene/sceneLayoutModel.js +202 -0
  47. package/.test-dist/tests/run.js +118 -0
  48. package/CHANGELOG.md +12 -0
  49. package/dist/index.d.mts +403 -366
  50. package/dist/index.d.ts +403 -366
  51. package/dist/index.js +5172 -4752
  52. package/dist/index.mjs +1410 -2027
  53. package/dist/tracer-PO7CRBYY.mjs +1016 -0
  54. package/package.json +1 -1
  55. package/src/extensions/{background.ts → background/BackgroundTool.ts} +33 -50
  56. package/src/extensions/background/index.ts +1 -0
  57. package/src/extensions/{dieline.ts → dieline/DielineTool.ts} +18 -218
  58. package/src/extensions/dieline/commands.ts +109 -0
  59. package/src/extensions/dieline/config.ts +106 -0
  60. package/src/extensions/dieline/index.ts +5 -0
  61. package/src/extensions/dieline/model.ts +1 -0
  62. package/src/extensions/dieline/renderer.ts +1 -0
  63. package/src/extensions/{feature.ts → feature/FeatureTool.ts} +27 -21
  64. package/src/extensions/feature/index.ts +1 -0
  65. package/src/extensions/{film.ts → film/FilmTool.ts} +36 -48
  66. package/src/extensions/film/index.ts +1 -0
  67. package/src/extensions/{image.ts → image/ImageTool.ts} +289 -335
  68. package/src/extensions/image/commands.ts +176 -0
  69. package/src/extensions/image/config.ts +128 -0
  70. package/src/extensions/image/index.ts +5 -0
  71. package/src/extensions/image/model.ts +1 -0
  72. package/src/extensions/image/renderer.ts +1 -0
  73. package/src/extensions/{mirror.ts → mirror/MirrorTool.ts} +1 -1
  74. package/src/extensions/mirror/index.ts +1 -0
  75. package/src/extensions/{ruler.ts → ruler/RulerTool.ts} +4 -5
  76. package/src/extensions/ruler/index.ts +1 -0
  77. package/src/extensions/sceneLayout.ts +1 -140
  78. package/src/extensions/sceneLayoutModel.ts +1 -364
  79. package/src/extensions/{size.ts → size/SizeTool.ts} +7 -6
  80. package/src/extensions/size/index.ts +1 -0
  81. package/src/extensions/{white-ink.ts → white-ink/WhiteInkTool.ts} +130 -317
  82. package/src/extensions/white-ink/commands.ts +157 -0
  83. package/src/extensions/white-ink/config.ts +30 -0
  84. package/src/extensions/white-ink/index.ts +5 -0
  85. package/src/extensions/white-ink/model.ts +1 -0
  86. package/src/extensions/white-ink/renderer.ts +1 -0
  87. package/src/services/CanvasService.ts +43 -12
  88. package/src/services/SceneLayoutService.ts +139 -0
  89. package/src/services/index.ts +1 -0
  90. package/src/services/renderSpec.ts +2 -0
  91. package/src/services/visibility.ts +5 -0
  92. package/src/shared/constants/layers.ts +23 -0
  93. package/src/shared/imaging/sourceSizeCache.ts +103 -0
  94. package/src/shared/index.ts +6 -0
  95. package/src/shared/runtime/sessionState.ts +105 -0
  96. package/src/shared/runtime/subscriptions.ts +45 -0
  97. package/src/shared/scene/frame.ts +46 -0
  98. package/src/shared/scene/sceneLayoutModel.ts +367 -0
  99. package/tests/run.ts +151 -0
@@ -2,14 +2,34 @@ import {
2
2
  Extension,
3
3
  ExtensionContext,
4
4
  ContributionPointIds,
5
- CommandContribution,
6
- ConfigurationContribution,
7
5
  ConfigurationService,
8
6
  ToolSessionService,
9
7
  WorkbenchService,
10
8
  } from "@pooder/core";
11
- import { CanvasService, RenderLayoutRect, RenderObjectSpec } from "../services";
12
- import { computeSceneLayout, readSizeState } from "./sceneLayoutModel";
9
+ import { CanvasService, RenderLayoutRect, RenderObjectSpec } from "../../services";
10
+ import {
11
+ type FrameRect,
12
+ resolveCutFrameRect,
13
+ toLayoutSceneRect as toSceneLayoutRect,
14
+ } from "../../shared/scene/frame";
15
+ import {
16
+ createSourceSizeCache,
17
+ getCoverScale as getCoverScaleFromRect,
18
+ type SourceSize,
19
+ } from "../../shared/imaging/sourceSizeCache";
20
+ import { SubscriptionBag } from "../../shared/runtime/subscriptions";
21
+ import {
22
+ applyCommittedSnapshot,
23
+ runDeferredConfigUpdate,
24
+ } from "../../shared/runtime/sessionState";
25
+ import {
26
+ IMAGE_OBJECT_LAYER_ID,
27
+ WHITE_INK_COVER_LAYER_ID,
28
+ WHITE_INK_OBJECT_LAYER_ID,
29
+ WHITE_INK_OVERLAY_LAYER_ID,
30
+ } from "../../shared/constants/layers";
31
+ import { createWhiteInkCommands } from "./commands";
32
+ import { createWhiteInkConfigurations } from "./config";
13
33
 
14
34
  export interface WhiteInkItem {
15
35
  id: string;
@@ -18,11 +38,6 @@ export interface WhiteInkItem {
18
38
  opacity: number;
19
39
  }
20
40
 
21
- interface SourceSize {
22
- width: number;
23
- height: number;
24
- }
25
-
26
41
  interface MaskTint {
27
42
  r: number;
28
43
  g: number;
@@ -30,13 +45,6 @@ interface MaskTint {
30
45
  key: string;
31
46
  }
32
47
 
33
- interface FrameRect {
34
- left: number;
35
- top: number;
36
- width: number;
37
- height: number;
38
- }
39
-
40
48
  interface ImageSnapshot {
41
49
  id: string;
42
50
  src: string;
@@ -84,11 +92,6 @@ interface UpdateWhiteInkOptions {
84
92
  target?: "auto" | "config" | "working";
85
93
  }
86
94
 
87
- const WHITE_INK_OBJECT_LAYER_ID = "white-ink.user";
88
- const WHITE_INK_COVER_LAYER_ID = "white-ink.cover";
89
- const WHITE_INK_OVERLAY_LAYER_ID = "white-ink.overlay";
90
- const IMAGE_OBJECT_LAYER_ID = "image.user";
91
-
92
95
  const WHITE_INK_DEBUG_KEY = "whiteInk.debug";
93
96
  const WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY = "whiteInk.previewImageVisible";
94
97
  const WHITE_INK_DEFAULT_OPACITY = 0.85;
@@ -111,7 +114,9 @@ export class WhiteInkTool implements Extension {
111
114
  private workingItems: WhiteInkItem[] = [];
112
115
  private hasWorkingChanges = false;
113
116
 
114
- private sourceSizeBySrc: Map<string, SourceSize> = new Map();
117
+ private sourceSizeCache = createSourceSizeCache((src) =>
118
+ this.loadImageSize(src),
119
+ );
115
120
  private previewMaskBySource: Map<string, string> = new Map();
116
121
  private pendingPreviewMaskBySource: Map<string, Promise<string | null>> =
117
122
  new Map();
@@ -128,8 +133,10 @@ export class WhiteInkTool implements Extension {
128
133
  private coverSpecs: RenderObjectSpec[] = [];
129
134
  private overlaySpecs: RenderObjectSpec[] = [];
130
135
  private renderProducerDisposable?: { dispose: () => void };
136
+ private readonly subscriptions = new SubscriptionBag();
131
137
 
132
138
  activate(context: ExtensionContext) {
139
+ this.subscriptions.disposeAll();
133
140
  this.context = context;
134
141
  this.canvasService = context.services.get<CanvasService>("CanvasService");
135
142
  if (!this.canvasService) {
@@ -164,22 +171,34 @@ export class WhiteInkTool implements Extension {
164
171
  { priority: 260 },
165
172
  );
166
173
 
167
- context.eventBus.on("tool:activated", this.onToolActivated);
168
- context.eventBus.on("scene:layout:change", this.onSceneLayoutChanged);
169
- context.eventBus.on("object:added", this.onObjectAdded);
170
- context.eventBus.on("object:modified", this.onObjectModified);
171
- context.eventBus.on("object:removed", this.onObjectRemoved);
172
- context.eventBus.on("image:working:change", this.onImageWorkingChanged);
174
+ this.subscriptions.on(context.eventBus, "tool:activated", this.onToolActivated);
175
+ this.subscriptions.on(
176
+ context.eventBus,
177
+ "scene:layout:change",
178
+ this.onSceneLayoutChanged,
179
+ );
180
+ this.subscriptions.on(context.eventBus, "object:added", this.onObjectAdded);
181
+ this.subscriptions.on(
182
+ context.eventBus,
183
+ "object:modified",
184
+ this.onObjectModified,
185
+ );
186
+ this.subscriptions.on(
187
+ context.eventBus,
188
+ "object:removed",
189
+ this.onObjectRemoved,
190
+ );
191
+ this.subscriptions.on(
192
+ context.eventBus,
193
+ "image:working:change",
194
+ this.onImageWorkingChanged,
195
+ );
173
196
 
174
197
  const configService = context.services.get<ConfigurationService>(
175
198
  "ConfigurationService",
176
199
  );
177
200
  if (configService) {
178
- this.items = this.normalizeItems(
179
- configService.get("whiteInk.items", []) || [],
180
- );
181
- this.workingItems = this.cloneItems(this.items);
182
- this.hasWorkingChanges = false;
201
+ this.applyCommittedItems(configService.get("whiteInk.items", []) || []);
183
202
  this.printWithWhiteInk = !!configService.get(
184
203
  "whiteInk.printWithWhiteInk",
185
204
  true,
@@ -191,44 +210,43 @@ export class WhiteInkTool implements Extension {
191
210
 
192
211
  this.migrateLegacyConfigIfNeeded(configService);
193
212
 
194
- configService.onAnyChange((e: { key: string; value: any }) => {
195
- if (this.isUpdatingConfig) return;
213
+ this.subscriptions.onConfigChange(
214
+ configService,
215
+ (e: { key: string; value: any }) => {
216
+ if (this.isUpdatingConfig) return;
196
217
 
197
- if (e.key === "whiteInk.items") {
198
- this.items = this.normalizeItems(e.value || []);
199
- if (!this.isToolActive || !this.hasWorkingChanges) {
200
- this.workingItems = this.cloneItems(this.items);
201
- this.hasWorkingChanges = false;
218
+ if (e.key === "whiteInk.items") {
219
+ this.applyCommittedItems(e.value || []);
220
+ this.updateWhiteInks();
221
+ return;
202
222
  }
203
- this.updateWhiteInks();
204
- return;
205
- }
206
223
 
207
- if (e.key === "whiteInk.printWithWhiteInk") {
208
- this.printWithWhiteInk = !!e.value;
209
- this.updateWhiteInks();
210
- return;
211
- }
224
+ if (e.key === "whiteInk.printWithWhiteInk") {
225
+ this.printWithWhiteInk = !!e.value;
226
+ this.updateWhiteInks();
227
+ return;
228
+ }
212
229
 
213
- if (e.key === WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY) {
214
- this.previewImageVisible = !!e.value;
215
- this.updateWhiteInks();
216
- return;
217
- }
230
+ if (e.key === WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY) {
231
+ this.previewImageVisible = !!e.value;
232
+ this.updateWhiteInks();
233
+ return;
234
+ }
218
235
 
219
- if (e.key === "image.items") {
220
- this.updateWhiteInks();
221
- return;
222
- }
236
+ if (e.key === "image.items") {
237
+ this.updateWhiteInks();
238
+ return;
239
+ }
223
240
 
224
- if (e.key === WHITE_INK_DEBUG_KEY) {
225
- return;
226
- }
241
+ if (e.key === WHITE_INK_DEBUG_KEY) {
242
+ return;
243
+ }
227
244
 
228
- if (e.key.startsWith("size.")) {
229
- this.updateWhiteInks();
230
- }
231
- });
245
+ if (e.key.startsWith("size.")) {
246
+ this.updateWhiteInks();
247
+ }
248
+ },
249
+ );
232
250
  }
233
251
 
234
252
  const toolSessionService =
@@ -242,15 +260,11 @@ export class WhiteInkTool implements Extension {
242
260
  }
243
261
 
244
262
  deactivate(context: ExtensionContext) {
245
- context.eventBus.off("tool:activated", this.onToolActivated);
246
- context.eventBus.off("scene:layout:change", this.onSceneLayoutChanged);
247
- context.eventBus.off("object:added", this.onObjectAdded);
248
- context.eventBus.off("object:modified", this.onObjectModified);
249
- context.eventBus.off("object:removed", this.onObjectRemoved);
250
- context.eventBus.off("image:working:change", this.onImageWorkingChanged);
263
+ this.subscriptions.disposeAll();
251
264
 
252
265
  this.dirtyTrackerDisposable?.dispose();
253
266
  this.dirtyTrackerDisposable = undefined;
267
+ this.sourceSizeCache.clear();
254
268
  this.clearRenderedWhiteInks();
255
269
  this.renderProducerDisposable?.dispose();
256
270
  this.renderProducerDisposable = undefined;
@@ -280,177 +294,8 @@ export class WhiteInkTool implements Extension {
280
294
  },
281
295
  },
282
296
  ],
283
- [ContributionPointIds.CONFIGURATIONS]: [
284
- {
285
- id: "whiteInk.items",
286
- type: "array",
287
- label: "White Ink Images",
288
- default: [],
289
- },
290
- {
291
- id: "whiteInk.printWithWhiteInk",
292
- type: "boolean",
293
- label: "Preview White Ink",
294
- default: true,
295
- },
296
- {
297
- id: WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY,
298
- type: "boolean",
299
- label: "Show Cover During White Ink Preview",
300
- default: true,
301
- },
302
- {
303
- id: WHITE_INK_DEBUG_KEY,
304
- type: "boolean",
305
- label: "White Ink Debug Log",
306
- default: false,
307
- },
308
- ] as ConfigurationContribution[],
309
- [ContributionPointIds.COMMANDS]: [
310
- {
311
- command: "addWhiteInk",
312
- title: "Add White Ink",
313
- handler: async (url: string, options?: Partial<WhiteInkItem>) => {
314
- return await this.addWhiteInkEntry(url, options);
315
- },
316
- },
317
- {
318
- command: "upsertWhiteInk",
319
- title: "Upsert White Ink",
320
- handler: async (url: string, options: UpsertWhiteInkOptions = {}) => {
321
- return await this.upsertWhiteInkEntry(url, options);
322
- },
323
- },
324
- {
325
- command: "getWhiteInks",
326
- title: "Get White Inks",
327
- handler: () => this.cloneItems(this.items),
328
- },
329
- {
330
- command: "getWhiteInkSettings",
331
- title: "Get White Ink Settings",
332
- handler: () => {
333
- const first = this.getEffectiveWhiteInkItem(this.items);
334
- const primarySource = this.getPrimaryImageSource();
335
- const sourceUrl = this.resolveSourceUrl(first) || primarySource;
336
-
337
- return {
338
- id: first?.id || null,
339
- url: sourceUrl,
340
- sourceUrl,
341
- opacity: WHITE_INK_DEFAULT_OPACITY,
342
- printWithWhiteInk: this.printWithWhiteInk,
343
- previewImageVisible: this.previewImageVisible,
344
- };
345
- },
346
- },
347
- {
348
- command: "setWhiteInkPrintEnabled",
349
- title: "Set White Ink Preview Enabled",
350
- handler: (enabled: boolean) => {
351
- this.printWithWhiteInk = !!enabled;
352
- const configService =
353
- this.context?.services.get<ConfigurationService>(
354
- "ConfigurationService",
355
- );
356
- configService?.update(
357
- "whiteInk.printWithWhiteInk",
358
- this.printWithWhiteInk,
359
- );
360
- this.updateWhiteInks();
361
- return { ok: true };
362
- },
363
- },
364
- {
365
- command: "setWhiteInkPreviewImageVisible",
366
- title: "Set White Ink Cover Visible",
367
- handler: (visible: boolean) => {
368
- this.previewImageVisible = !!visible;
369
- const configService =
370
- this.context?.services.get<ConfigurationService>(
371
- "ConfigurationService",
372
- );
373
- configService?.update(
374
- WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY,
375
- this.previewImageVisible,
376
- );
377
- this.updateWhiteInks();
378
- return { ok: true };
379
- },
380
- },
381
- {
382
- command: "getWorkingWhiteInks",
383
- title: "Get Working White Inks",
384
- handler: () => this.cloneItems(this.workingItems),
385
- },
386
- {
387
- command: "setWorkingWhiteInk",
388
- title: "Set Working White Ink",
389
- handler: (id: string, updates: Partial<WhiteInkItem>) => {
390
- this.updateWhiteInkInWorking(id, updates);
391
- },
392
- },
393
- {
394
- command: "updateWhiteInk",
395
- title: "Update White Ink",
396
- handler: async (
397
- id: string,
398
- updates: Partial<WhiteInkItem>,
399
- options: UpdateWhiteInkOptions = {},
400
- ) => {
401
- await this.updateWhiteInkItem(id, updates, options);
402
- },
403
- },
404
- {
405
- command: "removeWhiteInk",
406
- title: "Remove White Ink",
407
- handler: (id: string) => {
408
- this.removeWhiteInk(id);
409
- },
410
- },
411
- {
412
- command: "clearWhiteInks",
413
- title: "Clear White Inks",
414
- handler: () => {
415
- this.clearWhiteInks();
416
- },
417
- },
418
- {
419
- command: "resetWorkingWhiteInks",
420
- title: "Reset Working White Inks",
421
- handler: () => {
422
- this.workingItems = this.cloneItems(this.items);
423
- this.hasWorkingChanges = false;
424
- this.updateWhiteInks();
425
- },
426
- },
427
- {
428
- command: "completeWhiteInks",
429
- title: "Complete White Inks",
430
- handler: async () => {
431
- return await this.completeWhiteInks();
432
- },
433
- },
434
- {
435
- command: "setWhiteInkImage",
436
- title: "Set White Ink Image",
437
- handler: async (url: string) => {
438
- if (!url) {
439
- this.clearWhiteInks();
440
- return { ok: true };
441
- }
442
-
443
- const targetId = this.resolveReplaceTargetId(null);
444
- const upsertResult = await this.upsertWhiteInkEntry(url, {
445
- id: targetId || undefined,
446
- mode: targetId ? "replace" : "add",
447
- createIfMissing: true,
448
- addOptions: {},
449
- });
450
- return { ok: true, id: upsertResult.id };
451
- },
452
- },
453
- ] as CommandContribution[],
297
+ [ContributionPointIds.CONFIGURATIONS]: createWhiteInkConfigurations(),
298
+ [ContributionPointIds.COMMANDS]: createWhiteInkCommands(this),
454
299
  };
455
300
  }
456
301
 
@@ -506,13 +351,10 @@ export class WhiteInkTool implements Extension {
506
351
  opacity: WHITE_INK_DEFAULT_OPACITY,
507
352
  });
508
353
 
509
- this.items = [item];
510
- this.workingItems = this.cloneItems(this.items);
511
- this.isUpdatingConfig = true;
512
- configService.update("whiteInk.items", this.items);
513
- setTimeout(() => {
514
- this.isUpdatingConfig = false;
515
- }, 0);
354
+ this.applyCommittedItems([item]);
355
+ runDeferredConfigUpdate(this, () => {
356
+ configService.update("whiteInk.items", this.items);
357
+ });
516
358
  }
517
359
 
518
360
  private syncToolActiveFromWorkbench(fallbackId?: string | null) {
@@ -612,28 +454,39 @@ export class WhiteInkTool implements Extension {
612
454
  return null;
613
455
  }
614
456
 
457
+ private applyCommittedItems(nextItems: WhiteInkItem[]) {
458
+ const session = {
459
+ committed: this.items,
460
+ working: this.workingItems,
461
+ hasWorkingChanges: this.hasWorkingChanges,
462
+ };
463
+ applyCommittedSnapshot(session, this.normalizeItems(nextItems), {
464
+ clone: (items) => this.cloneItems(items),
465
+ toolActive: this.isToolActive,
466
+ preserveDirtyWorking: true,
467
+ });
468
+ this.items = session.committed;
469
+ this.workingItems = session.working;
470
+ this.hasWorkingChanges = session.hasWorkingChanges;
471
+ }
472
+
615
473
  private updateConfig(newItems: WhiteInkItem[], skipCanvasUpdate = false) {
616
474
  if (!this.context) return;
617
-
618
- this.isUpdatingConfig = true;
619
- this.items = this.normalizeItems(newItems);
620
- if (!this.isToolActive || !this.hasWorkingChanges) {
621
- this.workingItems = this.cloneItems(this.items);
622
- this.hasWorkingChanges = false;
623
- }
624
-
625
- const configService = this.context.services.get<ConfigurationService>(
626
- "ConfigurationService",
475
+ this.applyCommittedItems(newItems);
476
+ runDeferredConfigUpdate(
477
+ this,
478
+ () => {
479
+ const configService = this.context?.services.get<ConfigurationService>(
480
+ "ConfigurationService",
481
+ );
482
+ configService?.update("whiteInk.items", this.items);
483
+
484
+ if (!skipCanvasUpdate) {
485
+ this.updateWhiteInks();
486
+ }
487
+ },
488
+ 50,
627
489
  );
628
- configService?.update("whiteInk.items", this.items);
629
-
630
- if (!skipCanvasUpdate) {
631
- this.updateWhiteInks();
632
- }
633
-
634
- setTimeout(() => {
635
- this.isUpdatingConfig = false;
636
- }, 50);
637
490
  }
638
491
 
639
492
  private async addWhiteInkEntry(
@@ -753,7 +606,7 @@ export class WhiteInkTool implements Extension {
753
606
  }
754
607
 
755
608
  private clearWhiteInks() {
756
- this.sourceSizeBySrc.clear();
609
+ this.sourceSizeCache.clear();
757
610
  this.previewMaskBySource.clear();
758
611
  this.pendingPreviewMaskBySource.clear();
759
612
  this.updateConfig([]);
@@ -766,37 +619,14 @@ export class WhiteInkTool implements Extension {
766
619
  }
767
620
 
768
621
  private getFrameRect(): FrameRect {
769
- if (!this.canvasService) {
770
- return { left: 0, top: 0, width: 0, height: 0 };
771
- }
772
622
  const configService = this.context?.services.get<ConfigurationService>(
773
623
  "ConfigurationService",
774
624
  );
775
- if (!configService) {
776
- return { left: 0, top: 0, width: 0, height: 0 };
777
- }
778
- const sizeState = readSizeState(configService);
779
- const layout = computeSceneLayout(this.canvasService, sizeState);
780
- if (!layout) {
781
- return { left: 0, top: 0, width: 0, height: 0 };
782
- }
783
-
784
- return this.canvasService.toSceneRect({
785
- left: layout.cutRect.left,
786
- top: layout.cutRect.top,
787
- width: layout.cutRect.width,
788
- height: layout.cutRect.height,
789
- });
625
+ return resolveCutFrameRect(this.canvasService, configService);
790
626
  }
791
627
 
792
628
  private toLayoutSceneRect(rect: FrameRect): RenderLayoutRect {
793
- return {
794
- left: rect.left,
795
- top: rect.top,
796
- width: rect.width,
797
- height: rect.height,
798
- space: "scene",
799
- };
629
+ return toSceneLayoutRect(rect);
800
630
  }
801
631
 
802
632
  private getImageObjects(): any[] {
@@ -909,25 +739,20 @@ export class WhiteInkTool implements Extension {
909
739
  }
910
740
 
911
741
  private getCoverScale(frame: FrameRect, source: SourceSize): number {
912
- const frameW = Math.max(1, frame.width);
913
- const frameH = Math.max(1, frame.height);
914
- const sourceW = Math.max(1, source.width);
915
- const sourceH = Math.max(1, source.height);
916
- return Math.max(frameW / sourceW, frameH / sourceH);
742
+ return getCoverScaleFromRect(frame, source);
917
743
  }
918
744
 
919
745
  private async ensureSourceSize(
920
746
  sourceUrl: string,
921
747
  ): Promise<SourceSize | null> {
922
- if (!sourceUrl) return null;
923
- const cached = this.getSourceSize(sourceUrl);
924
- if (cached) return cached;
748
+ return this.sourceSizeCache.ensureImageSize(sourceUrl);
749
+ }
925
750
 
751
+ private async loadImageSize(sourceUrl: string): Promise<SourceSize | null> {
926
752
  try {
927
753
  const image = await this.loadImageElement(sourceUrl);
928
754
  const size = this.getElementSize(image);
929
755
  if (!size) return null;
930
- this.rememberSourceSize(sourceUrl, size);
931
756
  return {
932
757
  width: size.width,
933
758
  height: size.height,
@@ -980,23 +805,11 @@ export class WhiteInkTool implements Extension {
980
805
  }
981
806
 
982
807
  private rememberSourceSize(src: string, size: SourceSize) {
983
- if (!src) return;
984
- if (!Number.isFinite(size.width) || !Number.isFinite(size.height)) return;
985
- if (size.width <= 0 || size.height <= 0) return;
986
- this.sourceSizeBySrc.set(src, {
987
- width: size.width,
988
- height: size.height,
989
- });
808
+ this.sourceSizeCache.rememberSourceSize(src, size);
990
809
  }
991
810
 
992
811
  private getSourceSize(src: string): SourceSize | null {
993
- if (!src) return null;
994
- const cached = this.sourceSizeBySrc.get(src);
995
- if (!cached) return null;
996
- return {
997
- width: cached.width,
998
- height: cached.height,
999
- };
812
+ return this.sourceSizeCache.getSourceSize(src);
1000
813
  }
1001
814
 
1002
815
  private computeWhiteScaleAdjust(
@@ -1322,7 +1135,7 @@ export class WhiteInkTool implements Extension {
1322
1135
  private purgeSourceCaches(item?: WhiteInkItem) {
1323
1136
  const sourceUrl = this.resolveSourceUrl(item);
1324
1137
  if (!sourceUrl) return;
1325
- this.sourceSizeBySrc.delete(sourceUrl);
1138
+ this.sourceSizeCache.deleteSourceSize(sourceUrl);
1326
1139
  const prefix = `${sourceUrl}::`;
1327
1140
  Array.from(this.previewMaskBySource.keys()).forEach((cacheKey) => {
1328
1141
  if (cacheKey.startsWith(prefix)) {