@bensitu/image-editor 1.5.2 → 2.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 (166) hide show
  1. package/README.md +460 -509
  2. package/dist/cjs/index.cjs +6892 -0
  3. package/dist/cjs/index.cjs.map +1 -0
  4. package/dist/esm/animation/animation-queue.js +74 -0
  5. package/dist/esm/animation/animation-queue.js.map +1 -0
  6. package/dist/esm/core/callback-reporter.js +23 -0
  7. package/dist/esm/core/callback-reporter.js.map +1 -0
  8. package/dist/esm/core/default-options.js +529 -0
  9. package/dist/esm/core/default-options.js.map +1 -0
  10. package/dist/esm/core/errors.js +156 -0
  11. package/dist/esm/core/errors.js.map +1 -0
  12. package/dist/esm/core/operation-guard.js +157 -0
  13. package/dist/esm/core/operation-guard.js.map +1 -0
  14. package/dist/esm/core/public-types.js +4 -0
  15. package/dist/esm/core/public-types.js.map +1 -0
  16. package/dist/esm/core/state-serializer.js +252 -0
  17. package/dist/esm/core/state-serializer.js.map +1 -0
  18. package/dist/esm/crop/crop-controller.js +405 -0
  19. package/dist/esm/crop/crop-controller.js.map +1 -0
  20. package/dist/esm/export/export-format.js +53 -0
  21. package/dist/esm/export/export-format.js.map +1 -0
  22. package/dist/esm/export/export-service.js +607 -0
  23. package/dist/esm/export/export-service.js.map +1 -0
  24. package/dist/esm/fabric/fabric-adapter.js +37 -0
  25. package/dist/esm/fabric/fabric-adapter.js.map +1 -0
  26. package/dist/esm/fabric/fabric-animation.js +89 -0
  27. package/dist/esm/fabric/fabric-animation.js.map +1 -0
  28. package/dist/esm/history/command.js +2 -0
  29. package/dist/esm/history/command.js.map +1 -0
  30. package/dist/esm/history/history-manager.js +103 -0
  31. package/dist/esm/history/history-manager.js.map +1 -0
  32. package/dist/esm/image/image-loader.js +238 -0
  33. package/dist/esm/image/image-loader.js.map +1 -0
  34. package/dist/esm/image/image-resampler.js +60 -0
  35. package/dist/esm/image/image-resampler.js.map +1 -0
  36. package/dist/esm/image/layout-manager.js +206 -0
  37. package/dist/esm/image/layout-manager.js.map +1 -0
  38. package/dist/esm/image/transform-controller.js +132 -0
  39. package/dist/esm/image/transform-controller.js.map +1 -0
  40. package/dist/esm/image-editor.js +2076 -0
  41. package/dist/esm/image-editor.js.map +1 -0
  42. package/dist/esm/index.js +5 -0
  43. package/dist/esm/index.js.map +1 -0
  44. package/dist/esm/mask/mask-factory.js +356 -0
  45. package/dist/esm/mask/mask-factory.js.map +1 -0
  46. package/dist/esm/mask/mask-label-manager.js +120 -0
  47. package/dist/esm/mask/mask-label-manager.js.map +1 -0
  48. package/dist/esm/mask/mask-list.js +53 -0
  49. package/dist/esm/mask/mask-list.js.map +1 -0
  50. package/dist/esm/mask/mask-style.js +182 -0
  51. package/dist/esm/mask/mask-style.js.map +1 -0
  52. package/dist/esm/mosaic/mosaic-controller.js +670 -0
  53. package/dist/esm/mosaic/mosaic-controller.js.map +1 -0
  54. package/dist/esm/mosaic/mosaic-geometry.js +81 -0
  55. package/dist/esm/mosaic/mosaic-geometry.js.map +1 -0
  56. package/dist/esm/mosaic/mosaic-pixelate.js +71 -0
  57. package/dist/esm/mosaic/mosaic-pixelate.js.map +1 -0
  58. package/dist/esm/ui/dom-bindings.js +67 -0
  59. package/dist/esm/ui/dom-bindings.js.map +1 -0
  60. package/dist/esm/ui/ui-state.js +25 -0
  61. package/dist/esm/ui/ui-state.js.map +1 -0
  62. package/dist/esm/ui/visibility-state.js +11 -0
  63. package/dist/esm/ui/visibility-state.js.map +1 -0
  64. package/dist/esm/utils/canvas-region.js +100 -0
  65. package/dist/esm/utils/canvas-region.js.map +1 -0
  66. package/dist/esm/utils/dom.js +6 -0
  67. package/dist/esm/utils/dom.js.map +1 -0
  68. package/dist/esm/utils/file.js +53 -0
  69. package/dist/esm/utils/file.js.map +1 -0
  70. package/dist/esm/utils/number.js +24 -0
  71. package/dist/esm/utils/number.js.map +1 -0
  72. package/dist/esm/utils/timeout.js +17 -0
  73. package/dist/esm/utils/timeout.js.map +1 -0
  74. package/dist/types/animation/animation-queue.d.ts +111 -0
  75. package/dist/types/animation/animation-queue.d.ts.map +1 -0
  76. package/dist/types/core/callback-reporter.d.ts +125 -0
  77. package/dist/types/core/callback-reporter.d.ts.map +1 -0
  78. package/dist/types/core/default-options.d.ts +84 -0
  79. package/dist/types/core/default-options.d.ts.map +1 -0
  80. package/dist/types/core/errors.d.ts +142 -0
  81. package/dist/types/core/errors.d.ts.map +1 -0
  82. package/dist/types/core/operation-guard.d.ts +194 -0
  83. package/dist/types/core/operation-guard.d.ts.map +1 -0
  84. package/dist/types/core/public-types.d.ts +788 -0
  85. package/dist/types/core/public-types.d.ts.map +1 -0
  86. package/dist/types/core/state-serializer.d.ts +303 -0
  87. package/dist/types/core/state-serializer.d.ts.map +1 -0
  88. package/dist/types/crop/crop-controller.d.ts +407 -0
  89. package/dist/types/crop/crop-controller.d.ts.map +1 -0
  90. package/dist/types/export/export-format.d.ts +136 -0
  91. package/dist/types/export/export-format.d.ts.map +1 -0
  92. package/dist/types/export/export-service.d.ts +333 -0
  93. package/dist/types/export/export-service.d.ts.map +1 -0
  94. package/dist/types/fabric/fabric-adapter.d.ts +74 -0
  95. package/dist/types/fabric/fabric-adapter.d.ts.map +1 -0
  96. package/dist/types/fabric/fabric-animation.d.ts +141 -0
  97. package/dist/types/fabric/fabric-animation.d.ts.map +1 -0
  98. package/dist/types/history/command.d.ts +16 -0
  99. package/dist/types/history/command.d.ts.map +1 -0
  100. package/dist/types/history/history-manager.d.ts +129 -0
  101. package/dist/types/history/history-manager.d.ts.map +1 -0
  102. package/dist/types/image/image-loader.d.ts +263 -0
  103. package/dist/types/image/image-loader.d.ts.map +1 -0
  104. package/dist/types/image/image-resampler.d.ts +139 -0
  105. package/dist/types/image/image-resampler.d.ts.map +1 -0
  106. package/dist/types/image/layout-manager.d.ts +211 -0
  107. package/dist/types/image/layout-manager.d.ts.map +1 -0
  108. package/dist/types/image/transform-controller.d.ts +286 -0
  109. package/dist/types/image/transform-controller.d.ts.map +1 -0
  110. package/dist/types/image-editor.d.ts +661 -0
  111. package/dist/types/image-editor.d.ts.map +1 -0
  112. package/dist/types/index.d.cts +31 -0
  113. package/dist/types/index.d.cts.map +1 -0
  114. package/dist/types/index.d.ts +31 -0
  115. package/dist/types/index.d.ts.map +1 -0
  116. package/dist/types/mask/mask-factory.d.ts +212 -0
  117. package/dist/types/mask/mask-factory.d.ts.map +1 -0
  118. package/dist/types/mask/mask-label-manager.d.ts +171 -0
  119. package/dist/types/mask/mask-label-manager.d.ts.map +1 -0
  120. package/dist/types/mask/mask-list.d.ts +144 -0
  121. package/dist/types/mask/mask-list.d.ts.map +1 -0
  122. package/dist/types/mask/mask-style.d.ts +338 -0
  123. package/dist/types/mask/mask-style.d.ts.map +1 -0
  124. package/dist/types/mosaic/mosaic-controller.d.ts +82 -0
  125. package/dist/types/mosaic/mosaic-controller.d.ts.map +1 -0
  126. package/dist/types/mosaic/mosaic-geometry.d.ts +29 -0
  127. package/dist/types/mosaic/mosaic-geometry.d.ts.map +1 -0
  128. package/dist/types/mosaic/mosaic-pixelate.d.ts +23 -0
  129. package/dist/types/mosaic/mosaic-pixelate.d.ts.map +1 -0
  130. package/dist/types/ui/dom-bindings.d.ts +105 -0
  131. package/dist/types/ui/dom-bindings.d.ts.map +1 -0
  132. package/dist/types/ui/ui-state.d.ts +112 -0
  133. package/dist/types/ui/ui-state.d.ts.map +1 -0
  134. package/dist/types/ui/visibility-state.d.ts +77 -0
  135. package/dist/types/ui/visibility-state.d.ts.map +1 -0
  136. package/dist/types/utils/canvas-region.d.ts +177 -0
  137. package/dist/types/utils/canvas-region.d.ts.map +1 -0
  138. package/dist/types/utils/dom.d.ts +26 -0
  139. package/dist/types/utils/dom.d.ts.map +1 -0
  140. package/dist/types/utils/file.d.ts +80 -0
  141. package/dist/types/utils/file.d.ts.map +1 -0
  142. package/dist/types/utils/number.d.ts +131 -0
  143. package/dist/types/utils/number.d.ts.map +1 -0
  144. package/dist/types/utils/timeout.d.ts +84 -0
  145. package/dist/types/utils/timeout.d.ts.map +1 -0
  146. package/dist/umd/image-editor.umd.js +2 -0
  147. package/dist/umd/image-editor.umd.js.map +1 -0
  148. package/package.json +72 -66
  149. package/dist/image-editor.cjs +0 -4407
  150. package/dist/image-editor.cjs.map +0 -7
  151. package/dist/image-editor.esm.js +0 -4376
  152. package/dist/image-editor.esm.js.map +0 -7
  153. package/dist/image-editor.esm.min.js +0 -9
  154. package/dist/image-editor.esm.min.js.map +0 -7
  155. package/dist/image-editor.esm.min.mjs +0 -9
  156. package/dist/image-editor.esm.min.mjs.map +0 -7
  157. package/dist/image-editor.esm.mjs +0 -4376
  158. package/dist/image-editor.esm.mjs.map +0 -7
  159. package/dist/image-editor.js +0 -4373
  160. package/dist/image-editor.js.map +0 -7
  161. package/dist/image-editor.min.js +0 -9
  162. package/dist/image-editor.min.js.map +0 -7
  163. package/image-editor.d.ts +0 -271
  164. package/src/browser.js +0 -11
  165. package/src/esm.js +0 -9
  166. package/src/image-editor.js +0 -5013
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Bounded LIFO history stack of {@link Command} entries with
3
+ * dispose-aware async `undo` / `redo` semantics.
4
+ *
5
+ * Behavior contract:
6
+ *
7
+ * • {@link HistoryManager.execute} and {@link HistoryManager.push} are
8
+ * **synchronous** for the history-push step so callers can immediately
9
+ * inspect {@link HistoryManager.canUndo} / {@link HistoryManager.canRedo}
10
+ * on the next line (e.g. `updateUi` calls that immediately follow
11
+ * `saveState`).
12
+ *
13
+ * • {@link HistoryManager.undo} and {@link HistoryManager.redo} are
14
+ * **async** and protected by an internal `isProcessing` lock. Overlapping
15
+ * calls (rapid clicks) become no-ops that resolve without touching the
16
+ * stack so canvas restores cannot interleave.
17
+ *
18
+ * • `currentIndex` only advances **after** the awaited `execute` / `undo`
19
+ * promise settles successfully. A rejection leaves the pointer where it
20
+ * was so the next click retries the same step instead of skipping past
21
+ * a failed restore.
22
+ *
23
+ * • When the stack overflows past `maxSize`, the oldest entry is evicted
24
+ * and `currentIndex` stays the same numerically (the entry it pointed to
25
+ * has shifted one slot toward the front).
26
+ *
27
+ * `Command` is defined in this file (and re-exported from
28
+ * `./command.ts` as a one-line shim) so that the module can be imported
29
+ * directly from source by property tests running under Node's
30
+ * type-stripping mode without needing to resolve a sibling `.js`
31
+ * specifier at runtime.
32
+ *
33
+ * @module
34
+ */
35
+ /**
36
+ * Encapsulates a reversible canvas operation as a paired
37
+ * `execute` / `undo` async closure.
38
+ *
39
+ * Both functions return `Promise<void>` so async Fabric.js operations
40
+ * (`loadFromJSON`, `FabricImage.fromURL`, …) complete before the history
41
+ * manager marks the step as finished and advances its pointer.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * const cmd = new Command(
46
+ * async () => {
47
+ * await canvas.loadFromJSON(afterJson);
48
+ * },
49
+ * async () => {
50
+ * await canvas.loadFromJSON(beforeJson);
51
+ * },
52
+ * );
53
+ * historyManager.execute(cmd);
54
+ * ```
55
+ */
56
+ export declare class Command {
57
+ /** Performs (or re-performs) the action. */
58
+ readonly execute: () => Promise<void>;
59
+ /** Reverts the action. */
60
+ readonly undo: () => Promise<void>;
61
+ constructor(execute: () => Promise<void>, undo: () => Promise<void>);
62
+ }
63
+ /**
64
+ * Manages a bounded LIFO stack of {@link Command} objects supporting
65
+ * unlimited undo and redo within the configured history size.
66
+ */
67
+ export declare class HistoryManager {
68
+ private history;
69
+ private currentIndex;
70
+ private isProcessing;
71
+ /** Maximum number of commands retained. */
72
+ readonly maxSize: number;
73
+ /**
74
+ * @param maxSize - Maximum number of commands retained.
75
+ * @default 50
76
+ */
77
+ constructor(maxSize?: number);
78
+ /**
79
+ * Records a command on the history stack **and** fires its `execute`
80
+ * (fire-and-forget).
81
+ *
82
+ * The history push is synchronous so that {@link canUndo} /
83
+ * {@link canRedo} reflect the new state on the next line. In the
84
+ * `saveState` pattern, `command.execute` is a no-op on its first
85
+ * invocation (guarded by an `executedOnce` flag inside the closure), so
86
+ * the fire-and-forget is safe and produces no canvas side-effect.
87
+ */
88
+ execute(command: Command): void;
89
+ /**
90
+ * Pushes a command onto the history stack **without** calling
91
+ * `execute`. Use this when the operation has already been performed
92
+ * (for example `applyCrop`) and only the undo/redo wiring is needed.
93
+ */
94
+ push(command: Command): void;
95
+ /** Returns `true` if there is at least one action to undo. */
96
+ canUndo(): boolean;
97
+ /** Returns `true` if there is at least one action to redo. */
98
+ canRedo(): boolean;
99
+ /**
100
+ * Undoes the most recent command.
101
+ *
102
+ * Resolves as a no-op if {@link canUndo} is `false` or another
103
+ * `undo` / `redo` is currently in flight (overlapping calls are
104
+ * rejected via the `isProcessing` lock). The `currentIndex` only moves
105
+ * after the awaited `command.undo` settles successfully; if it
106
+ * rejects, the pointer stays where it was so a subsequent click
107
+ * retries the same step.
108
+ */
109
+ undo(): Promise<void>;
110
+ /**
111
+ * Re-executes the next command.
112
+ *
113
+ * Resolves as a no-op if {@link canRedo} is `false` or another
114
+ * `undo` / `redo` is currently in flight. The `currentIndex` only
115
+ * advances after the awaited `command.execute` settles successfully.
116
+ */
117
+ redo(): Promise<void>;
118
+ /**
119
+ * Shared push/trim path for {@link execute} and {@link push}.
120
+ *
121
+ * Discards any redo branch past `currentIndex`, appends the new
122
+ * command, and either advances `currentIndex` (within capacity) or
123
+ * evicts the oldest entry without changing `currentIndex` numerically
124
+ * (overflow past `maxSize`).
125
+ *
126
+ */
127
+ private pushAndTrim;
128
+ }
129
+ //# sourceMappingURL=history-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history-manager.d.ts","sourceRoot":"","sources":["../../../src/history/history-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,OAAO;IAChB,4CAA4C;IAC5C,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,0BAA0B;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;gBAEvB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;CAItE;AAED;;;GAGG;AACH,qBAAa,cAAc;IACvB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,YAAY,CAAS;IAE7B,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB;;;OAGG;gBACS,OAAO,GAAE,MAAW;IAIhC;;;;;;;;;OASG;IACH,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAQ/B;;;;OAIG;IACH,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAI5B,8DAA8D;IAC9D,OAAO,IAAI,OAAO;IAIlB,8DAA8D;IAC9D,OAAO,IAAI,OAAO;IAIlB;;;;;;;;;OASG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAc3B;;;;;;OAMG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAc3B;;;;;;;;OAQG;IACH,OAAO,CAAC,WAAW;CAgBtB"}
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Transactional `loadImage` pipeline. Validates the input,
3
+ * snapshots every field that the pipeline is about to mutate
4
+ * into a {@link RollbackBundle}, decodes the data URL into an
5
+ * `HTMLImageElement` under the configured timeout, optionally
6
+ * downsamples via {@link resampleImage}, awaits
7
+ * `FabricImage.fromURL` under the same timeout, applies the
8
+ * layout strategy chosen by `image/layout-manager.ts`, commits
9
+ * the new image to the canvas, and emits a fresh
10
+ * `lastSnapshot` via `core/state-serializer.ts`. Any failure
11
+ * between the snapshot and the commit replays the bundle and
12
+ * rejects with the original error.
13
+ *
14
+ * ## Owned contracts
15
+ *
16
+ * - When `loadImage` rejects, the original error is
17
+ * routed through the public `onError(error, message)` callback via
18
+ * `core/callback-reporter.ts → reportError`, AFTER `replayRollback` has
19
+ * restored editor state. Callback exceptions are caught and logged so a
20
+ * faulty integrator callback cannot mask the original error that the
21
+ * loader re-throws. The success path does NOT invoke `onError`.
22
+ * - Strings that do not start with `data:image/`
23
+ * resolve without mutating placeholder visibility, scroll position,
24
+ * image state, or canvas state. The function returns before
25
+ * capturing the rollback bundle, so no observable side effect occurs.
26
+ * - On a valid `data:image/` URL, the loader captures
27
+ * the rollback bundle *before* mutating any of the fields it tracks
28
+ * (placeholder `hidden`, container `scrollTop`/`scrollLeft`, container
29
+ * `originalImage`, `lastSnapshot`, the canvas JSON
30
+ * snapshot, plus the editor transform fields the rollback needs to
31
+ * restore the live canvas to a consistent state).
32
+ * - Decode, Fabric, downsample, and timeout failures
33
+ * restore every field captured in the rollback bundle and reject with the
34
+ * original error.
35
+ * - On success, `isImageLoadedToCanvas` is set to
36
+ * `true`, `lastSnapshot` is replaced with a fresh snapshot derived from
37
+ * the new canvas, and `maskCounter` is reset to `0`.
38
+ * - Either the new image is committed fully, or the
39
+ * prior committed state is restored fully. No partial state is observable
40
+ * after the returned promise settles.
41
+ * - Both the decode step and the
42
+ * `FabricImage.fromURL` step are bounded by `options.imageLoadTimeoutMs`
43
+ * via {@link withTimeout}.
44
+ * - Timeout failures reject with
45
+ * {@link ImageLoadTimeoutError} (built by `utils/timeout.ts`) and replay
46
+ * the rollback bundle.
47
+ * - The 2D-context failure inside
48
+ * {@link resampleImage} surfaces as {@link DownsampleError}; the loader
49
+ * catches it and routes through the rollback path.
50
+ * - On success, `maskCounter` is reset to `0`.
51
+ *
52
+ * ## Implementation notes
53
+ *
54
+ * The loader is an exported **function** that takes its dependencies in a
55
+ * {@link LoadImageContext} parameter rather than a class. The `ImageEditor`
56
+ * facade owns all editor state (the canvas reference, the placeholder
57
+ * element, the editor scalar fields), so the loader must read and write
58
+ * that state through a small set of getter/setter callbacks. The class
59
+ * shape of legacy was a side effect of the monolith; current keeps the loader
60
+ * stateless so the rollback bundle is the single source of truth for what
61
+ * the operation has captured.
62
+ *
63
+ * The rollback bundle is built before the loader hides the placeholder or
64
+ * touches the canvas. It captures *every* field listed in the documented
65
+ * RollbackBundle definition plus the editor scalar fields
66
+ * (`isImageLoadedToCanvas`, `maskCounter`, `currentScale`,
67
+ * `currentRotation`, `baseImageScale`) the success path mutates. Restoring
68
+ * those scalars is required for atomic rollback — without them, a failed
69
+ * load that ran past the scalar reset would leave the editor with
70
+ * `currentScale = 1` and `currentRotation = 0` even though
71
+ * `originalImage` (and therefore the live canvas) had been rewound to the
72
+ * previous image.
73
+ *
74
+ * `preserveScroll` is honored on both the success path
75
+ * and the rollback path. On success it is conditional on
76
+ * `loadOptions.preserveScroll === true`; on rollback the bundle is replayed
77
+ * unconditionally, which the rollback contract already requires for
78
+ * transactional rewind. When `preserveScroll` is omitted or `false`, the
79
+ * success path leaves the container scroll untouched, so legacy's documented
80
+ * scroll/viewport behavior for the selected layout mode prevails.
81
+ *
82
+ * The loader does not invoke public success callbacks. It owns
83
+ * transactional mutation and rollback; the `ImageEditor` facade emits
84
+ * `onImageLoaded`, `onImageChanged`, `onMasksChanged`, and related lifecycle
85
+ * callbacks after this function returns from a committed load. The rollback
86
+ * path still reports load failures through `onError` after replaying the
87
+ * rollback bundle.
88
+ *
89
+ * Owner module references (per the documented "Mapping Contracts to
90
+ * modules" table): this module is the canonical owner of the transactional
91
+ * load helpers. It is NOT re-exported from `src/index.ts`.
92
+ *
93
+ * @module
94
+ */
95
+ import type * as FabricNS from 'fabric';
96
+ import type { FabricModule, ImageMimeType, LoadImageOptions, ResolvedOptions } from '../core/public-types.js';
97
+ import { type ViewportCache } from './layout-manager.js';
98
+ /**
99
+ * Snapshot of every field the loader is about to mutate, captured before
100
+ * the first mutation so a failure mid-pipeline can rewind the editor to
101
+ * its pre-call state.
102
+ *
103
+ * Mirrors the documented `RollbackBundle` definition with the addition of
104
+ * the editor scalar fields the success path also rewrites
105
+ * (`isImageLoadedToCanvas`, `maskCounter`, `currentScale`,
106
+ * `currentRotation`, `baseImageScale`). Those scalars must be restored
107
+ * together with the canvas JSON for atomic rewind.
108
+ *
109
+ */
110
+ export interface RollbackBundle {
111
+ /** `placeholderElement.hidden` immediately before the loader hid it. */
112
+ placeholderHidden: boolean | null;
113
+ /** Container `scrollTop` immediately before the loader started. */
114
+ containerScrollTop: number | null;
115
+ /** Container `scrollLeft` immediately before the loader started. */
116
+ containerScrollLeft: number | null;
117
+ /** The previously-committed `originalImage` reference, if any. */
118
+ originalImage: FabricNS.FabricImage | null;
119
+ /** Whether an image was already committed before this call. */
120
+ isImageLoadedToCanvas: boolean;
121
+ /** Snapshot string used as the history baseline before the call. */
122
+ lastSnapshot: string | null;
123
+ /**
124
+ * Full canvas JSON serialization captured via `canvas.toJSON` with the
125
+ * editor's custom keys. Restored via `loadFromJSON` on rollback.
126
+ */
127
+ canvasJson: string;
128
+ /** Mask counter value before the loader reset it to 0. */
129
+ maskCounter: number;
130
+ /** Image scale factor before the loader reset it to 1. */
131
+ currentScale: number;
132
+ /** Image rotation in degrees before the loader reset it to 0. */
133
+ currentRotation: number;
134
+ /** Base scale chosen by the previous load, restored on rollback. */
135
+ baseImageScale: number;
136
+ /** MIME type of the image committed before the load started. */
137
+ currentImageMimeType: ImageMimeType | null;
138
+ }
139
+ /**
140
+ * Dependency bundle passed by the `ImageEditor` facade into
141
+ * {@link loadImage}. The loader has no class state of its own — every
142
+ * editor field it reads or writes is exposed here as a getter/setter pair
143
+ * so the facade keeps ownership of the canonical state.
144
+ *
145
+ * The facade is responsible for:
146
+ * - constructing the {@link ViewportCache} once and reusing it across
147
+ * loads (so hidden-tab fallbacks work),
148
+ * - providing a `setPlaceholderVisible` callback that delegates to
149
+ * `ui/visibility-state.ts`.
150
+ *
151
+ * The facade is also responsible for public success lifecycle callbacks after
152
+ * this transactional helper returns. The loader only reports failed loads
153
+ * through `onError` after rollback.
154
+ */
155
+ export interface LoadImageContext {
156
+ /** The Fabric module providing `FabricImage.fromURL`. */
157
+ fabric: FabricModule;
158
+ /** The live Fabric canvas. */
159
+ canvas: FabricNS.Canvas;
160
+ /** Resolved editor options (timeouts, downsample knobs, layout flags). */
161
+ options: ResolvedOptions;
162
+ /** Scrollable container wrapping the canvas, or `null`. */
163
+ containerElement: HTMLElement | null;
164
+ /** Empty-state placeholder element, or `null`. */
165
+ placeholderElement: HTMLElement | null;
166
+ /** Hidden-container viewport cache shared with the layout manager. */
167
+ viewportCache: ViewportCache;
168
+ /** Reads the previously-committed `originalImage`. */
169
+ getOriginalImage(): FabricNS.FabricImage | null;
170
+ /** Writes `originalImage` (used both on commit and on rollback). */
171
+ setOriginalImage(imageObject: FabricNS.FabricImage | null): void;
172
+ /** Reads `isImageLoadedToCanvas`. */
173
+ getIsImageLoadedToCanvas(): boolean;
174
+ /** Writes `isImageLoadedToCanvas`. */
175
+ setIsImageLoadedToCanvas(v: boolean): void;
176
+ /** Reads `lastSnapshot`. */
177
+ getLastSnapshot(): string | null;
178
+ /** Writes `lastSnapshot`. */
179
+ setLastSnapshot(s: string | null): void;
180
+ /** Reads `maskCounter`. */
181
+ getMaskCounter(): number;
182
+ /** Writes `maskCounter`. */
183
+ setMaskCounter(n: number): void;
184
+ /** Reads `currentScale`. */
185
+ getCurrentScale(): number;
186
+ /** Writes `currentScale`. */
187
+ setCurrentScale(n: number): void;
188
+ /** Reads `currentRotation`. */
189
+ getCurrentRotation(): number;
190
+ /** Writes `currentRotation`. */
191
+ setCurrentRotation(n: number): void;
192
+ /** Reads `baseImageScale`. */
193
+ getBaseImageScale(): number;
194
+ /** Writes `baseImageScale`. */
195
+ setBaseImageScale(n: number): void;
196
+ /** Reads the MIME type of the currently committed image. */
197
+ getCurrentImageMimeType(): ImageMimeType | null;
198
+ /** Writes the MIME type of the currently committed image. */
199
+ setCurrentImageMimeType(mimeType: ImageMimeType | null): void;
200
+ /**
201
+ * Toggle placeholder/canvas-container visibility via
202
+ * `ui/visibility-state.ts`. `show === false` means "an image is now on
203
+ * the canvas — hide the placeholder".
204
+ */
205
+ setPlaceholderVisible(show: boolean): void;
206
+ }
207
+ /**
208
+ * Transactional image loader. Loads a base64 data URL onto the Fabric
209
+ * canvas with full rollback on any failure.
210
+ *
211
+ * Steps, in order:
212
+ *
213
+ * 1. **Validate** — non-`data:image/` strings resolve
214
+ * immediately without capturing the bundle or touching state.
215
+ * 2. **Snapshot** — capture every field the pipeline
216
+ * will mutate into a {@link RollbackBundle}.
217
+ * 3. **Hide placeholder** — first observable mutation. Restored on
218
+ * rollback.
219
+ * 4. **Decode** — race the `<img>.onload` against
220
+ * `imageLoadTimeoutMs` via {@link withTimeout}.
221
+ * 5. **Downsample** — if the source exceeds the
222
+ * configured bounds, run {@link resampleImage}; a 2D-context failure
223
+ * surfaces as {@link DownsampleError} and triggers rollback.
224
+ * 6. **Fabric load** — `FabricImage.fromURL` under the
225
+ * same timeout.
226
+ * 7. **Layout** — pick a strategy via {@link selectLayoutStrategy} and
227
+ * apply via {@link applyCanvasDimensions}.
228
+ * 8. **Commit** — set `isImageLoadedToCanvas`,
229
+ * reset `maskCounter` to 0, reset transforms, and emit a fresh
230
+ * `lastSnapshot` via {@link saveState}.
231
+ *
232
+ * Any rejection between step 3 and step 8 routes through {@link replayRollback}
233
+ * before re-throwing the original error. On the rollback
234
+ * path, the original error is also dispatched to the public `onError`
235
+ * callback via {@link reportError}; the helper catches
236
+ * and logs callback exceptions so a faulty integrator callback cannot
237
+ * replace the original error that this function re-throws.
238
+ *
239
+ * `preserveScroll` is honored on success when
240
+ * `loadOptions.preserveScroll === true`: after the canvas has been resized
241
+ * and the new image committed, the captured pre-load `scrollTop` and
242
+ * `scrollLeft` are written back to the container. The rollback path always
243
+ * restores scroll regardless of `preserveScroll` because the rollback
244
+ * requires the bundle to be replayed in full on failure. When
245
+ * `preserveScroll` is omitted or `false`, the success path leaves scroll
246
+ * untouched and legacy's documented scroll/viewport behavior for the selected
247
+ * layout mode applies.
248
+ *
249
+ * Public success lifecycle callbacks are emitted by the facade after this
250
+ * helper returns from a committed load. Keeping them outside the loader keeps
251
+ * transactional mutation/rollback separate from facade-level event ordering.
252
+ *
253
+ * @param context - Editor dependency bundle.
254
+ * @param imageBase64 - Base64 data URL to load (`data:image/...;base64...`).
255
+ * @param loadOptions - Public {@link LoadImageOptions}. Currently only
256
+ * `preserveScroll` is consulted; defaults to `false`.
257
+ * @returns Resolved promise on success, rejected with the original error
258
+ * (after rollback) on failure. Non-data:image inputs resolve
259
+ * without observable mutation.
260
+ *
261
+ */
262
+ export declare function loadImage(context: LoadImageContext, imageBase64: string, loadOptions?: LoadImageOptions): Promise<void>;
263
+ //# sourceMappingURL=image-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-loader.d.ts","sourceRoot":"","sources":["../../../src/image/image-loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Aspect-preserving downsampling and alpha-aware MIME selection
3
+ * for oversized source images loaded by `image/image-loader.ts`.
4
+ *
5
+ * The resampler is a pure helper module: it does not know about the editor,
6
+ * the canvas, or the rollback bundle. It exposes three small, individually
7
+ * testable building blocks plus one orchestrating function:
8
+ *
9
+ * - {@link computeDownsampleDimensions} — pure aspect-ratio math used by
10
+ * property tests for the resampler size contract.
11
+ * - {@link selectDownsampleMimeType} — pure MIME-resolution table used by
12
+ * property tests for the alpha-preserving fallback path.
13
+ * - {@link detectSourceMimeType} — extracts the MIME prefix from a
14
+ * base64 data URL.
15
+ * - {@link resampleImage} — composes the above with a real
16
+ * `<canvas>` to produce the downsampled data URL. Throws
17
+ * {@link DownsampleError} when the offscreen canvas fails to obtain a
18
+ * 2D context so the loader can replay its rollback
19
+ * bundle transactionally.
20
+ *
21
+ * This module is internal — it is NOT re-exported from `src/index.ts`.
22
+ *
23
+ * @module
24
+ */
25
+ import type { ImageMimeType } from '../core/public-types.js';
26
+ /**
27
+ * Result returned by {@link resampleImage}.
28
+ *
29
+ * `dataUrl` is the rasterised data URL produced by the offscreen canvas,
30
+ * `width` / `height` are the integer pixel dimensions used for that raster,
31
+ * and `mimeType` is the MIME chosen by {@link selectDownsampleMimeType}.
32
+ */
33
+ export interface ResampleResult {
34
+ /** Rasterised data URL (`data:image/...;base64...`). */
35
+ dataUrl: string;
36
+ /** Output width in pixels (integer). */
37
+ width: number;
38
+ /** Output height in pixels (integer). */
39
+ height: number;
40
+ /** Resolved output MIME type. */
41
+ mimeType: ImageMimeType;
42
+ }
43
+ /**
44
+ * Compute target dimensions while preserving the source aspect ratio.
45
+ *
46
+ * Returns the source dimensions unchanged when both axes are already within
47
+ * `(maxWidth, maxHeight)`. When either bound is exceeded, returns the
48
+ * largest box that fits inside both bounds and matches the source aspect
49
+ * ratio, with each axis rounded to an integer.
50
+ *
51
+ * Pure function — no DOM access, safe to call from property tests.
52
+ *
53
+ * @param srcWidth - Source pixel width (must be > 0 for meaningful output).
54
+ * @param srcHeight - Source pixel height (must be > 0 for meaningful output).
55
+ * @param maxWidth - Maximum allowed output width in pixels.
56
+ * @param maxHeight - Maximum allowed output height in pixels.
57
+ * @returns `{ width, height, needsResize}` where `needsResize` is `true`
58
+ * only when at least one source axis exceeded its bound.
59
+ *
60
+ */
61
+ export declare function computeDownsampleDimensions(srcWidth: number, srcHeight: number, maxWidth: number, maxHeight: number): {
62
+ width: number;
63
+ height: number;
64
+ needsResize: boolean;
65
+ };
66
+ /**
67
+ * Select the output MIME type for downsampling.
68
+ *
69
+ * Selection table:
70
+ *
71
+ * | sourceMime | preserveSourceFormat | downsampleMimeType | result |
72
+ * | ------------------- | -------------------- | ------------------ | -------------------- |
73
+ * | image/png | true | unset | image/png |
74
+ * | image/webp | true | unset | image/webp |
75
+ * | image/png | false | unset | image/jpeg |
76
+ * | image/png | true | image/jpeg | image/jpeg |
77
+ * | image/jpeg | (any) | unset | image/jpeg |
78
+ * | image/jpeg | (any) | image/webp | image/webp |
79
+ * | null / unknown | (any) | unset | image/jpeg |
80
+ *
81
+ * Pure function — no DOM access, safe to call from property tests.
82
+ *
83
+ * @param sourceMime - Detected source MIME (e.g. from
84
+ * {@link detectSourceMimeType}) or `null` if
85
+ * unknown.
86
+ * @param preserveSourceFormat - When `true`, alpha-capable source MIMEs
87
+ * survive downsampling unless overridden by
88
+ * `downsampleMimeType`.
89
+ * @param downsampleMimeType - Explicit MIME override; when truthy, wins over
90
+ * both `sourceMime` and `preserveSourceFormat`.
91
+ * @returns The MIME type to emit from the offscreen canvas.
92
+ *
93
+ */
94
+ export declare function selectDownsampleMimeType(sourceMime: string | null, preserveSourceFormat: boolean, downsampleMimeType: ImageMimeType | null | undefined): ImageMimeType;
95
+ /**
96
+ * Detect the MIME type embedded in a base64 data URL.
97
+ *
98
+ * Matches the standard `data:<mime>;...` prefix. Returns `null` when the
99
+ * input does not start with a recognizable image data URL prefix, so callers
100
+ * can pass the result straight into {@link selectDownsampleMimeType}.
101
+ *
102
+ * @param dataUrl - Base64 data URL (e.g. `data:image/png;base64,iVBOR...`).
103
+ * @returns The lowercased MIME type, or `null` when no `image/*` prefix is
104
+ * present.
105
+ */
106
+ export declare function detectSourceMimeType(dataUrl: string): string | null;
107
+ /**
108
+ * Downsample an `HTMLImageElement` to fit within `(maxWidth, maxHeight)` and
109
+ * return the resampled data URL alongside its final dimensions and MIME.
110
+ *
111
+ * The function is the only piece of the resampler that touches the DOM. It
112
+ * creates an offscreen `<canvas>`, paints the source image into it at the
113
+ * computed dimensions, and reads back a data URL using the MIME selected by
114
+ * {@link selectDownsampleMimeType}. PNG output ignores `quality` because PNG
115
+ * is lossless; JPEG and WebP output use `quality` as the
116
+ * lossy compression knob.
117
+ *
118
+ * Failure mode: when `<canvas>.getContext('2d')` returns
119
+ * `null`, this function throws {@link DownsampleError} so
120
+ * `image/image-loader.ts` can replay its Transactional_Load rollback bundle
121
+ * before rejecting the public `loadImage` promise.
122
+ *
123
+ * @param imageElement - Decoded source image element.
124
+ * @param maxWidth - Maximum allowed output width in pixels.
125
+ * @param maxHeight - Maximum allowed output height in pixels.
126
+ * @param sourceMime - Detected source MIME from the original data
127
+ * URL (or `null` if unknown).
128
+ * @param preserveSourceFormat - When `true`, alpha-capable MIMEs survive.
129
+ * @param downsampleMimeType - Optional explicit MIME override.
130
+ * @param quality - Lossy compression quality in `[0, 1]` for
131
+ * JPEG/WebP output. Ignored for PNG.
132
+ * @returns The resampled data URL plus its dimensions and MIME.
133
+ *
134
+ * @throws {@link DownsampleError} when the offscreen canvas cannot obtain a
135
+ * 2D rendering context.
136
+ *
137
+ */
138
+ export declare function resampleImage(imageElement: HTMLImageElement, maxWidth: number, maxHeight: number, sourceMime: string | null, preserveSourceFormat: boolean, downsampleMimeType: ImageMimeType | null | undefined, quality: number, ownerDocument?: Document): ResampleResult;
139
+ //# sourceMappingURL=image-resampler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-resampler.d.ts","sourceRoot":"","sources":["../../../src/image/image-resampler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAG7D;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC3B,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,QAAQ,EAAE,aAAa,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,2BAA2B,CACvC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAClB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CA8BzD;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,wBAAwB,CACpC,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,oBAAoB,EAAE,OAAO,EAC7B,kBAAkB,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,GACrD,aAAa,CAaf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGnE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,aAAa,CACzB,YAAY,EAAE,gBAAgB,EAC9B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,oBAAoB,EAAE,OAAO,EAC7B,kBAAkB,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,EACpD,OAAO,EAAE,MAAM,EACf,aAAa,CAAC,EAAE,QAAQ,GACzB,cAAc,CAmDhB"}