@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,211 @@
1
+ /**
2
+ * Pure layout helpers and a small viewport cache used by the
3
+ * `image-loader` pipeline. The layout manager owns three concerns
4
+ * used by the image-load pipeline:
5
+ *
6
+ * 1. Selecting the layout strategy from the current canonical layout mode.
7
+ * 2. Computing canvas dimensions and image scale for the selected
8
+ * strategy.
9
+ * 3. Measuring the visible container viewport with a hidden-tab cache
10
+ * and a final fall-back to `options.canvasWidth/canvasHeight`.
11
+ *
12
+ * The module also exposes a single sizing primitive,
13
+ * `applyCanvasDimensions`, which is the only place in the editor
14
+ * that calls `Canvas.setDimensions`. It rounds to integer pixels
15
+ * and forces a synchronous reflow on the container so that auto
16
+ * scrollbars settle before the next paint.
17
+ *
18
+ * The layout manager intentionally
19
+ * does NOT mutate developer CSS:
20
+ * - it never touches `canvas.style` or `container.style`
21
+ * (`width`, `height`, `display`, `overflow`),
22
+ * - it reads `clientWidth` / `clientHeight` and compensates for
23
+ * pre-existing auto scrollbars without any `overflow` toggle.
24
+ *
25
+ * @module
26
+ */
27
+ import type * as FabricNS from 'fabric';
28
+ import type { LayoutMode } from '../core/public-types.js';
29
+ /**
30
+ * Discriminator for the active layout strategy. Exactly one value is
31
+ * returned by {@link selectLayoutStrategy} per `loadImage` call.
32
+ */
33
+ export type LayoutStrategy = LayoutMode;
34
+ /**
35
+ * Choose the active layout strategy from the current layout mode.
36
+ */
37
+ export declare function selectLayoutStrategy(mode: LayoutMode): LayoutStrategy;
38
+ /**
39
+ * Two-axis viewport size in CSS pixels.
40
+ *
41
+ * Both axes are non-negative integers. `0` is permitted in transient
42
+ * states (e.g. before the editor is attached) but {@link ViewportCache}
43
+ * treats any axis of `0` as "hidden" and falls back to a cached or
44
+ * default value.
45
+ */
46
+ export interface ViewportSize {
47
+ width: number;
48
+ height: number;
49
+ }
50
+ /** Native scrollbar gutter size in CSS pixels. */
51
+ export type ScrollbarSize = ViewportSize;
52
+ export type OverflowAxis = 'horizontal' | 'vertical';
53
+ /**
54
+ * Hidden-container viewport cache.
55
+ *
56
+ * The editor often runs inside a tab, modal, or accordion that starts
57
+ * collapsed. A naive `clientWidth` read in that state returns `0` and
58
+ * collapses the canvas. The cache remembers the last non-zero
59
+ * measurement and reuses it while the container reports a zero
60
+ * dimension on either axis. When no non-zero
61
+ * measurement has been observed yet, the caller-supplied fallback —
62
+ * normally `(options.canvasWidth, options.canvasHeight)` — is used
63
+ * instead. When the container becomes visible
64
+ * again, the next `measure` call updates the cache so subsequent
65
+ * sizing decisions reflect the new viewport.
66
+ *
67
+ * Measurements compensate for pre-existing auto scrollbars by adding
68
+ * the scrollbar gutter back to the visible client size. This mirrors
69
+ * v1.4.2's viewport recovery while still preserving v2's rule that
70
+ * layout code never mutates the container's `overflow` style.
71
+ *
72
+ */
73
+ export declare class ViewportCache {
74
+ private lastVisible;
75
+ /**
76
+ * Measure the visible viewport for `container`, caching the result
77
+ * when both axes are non-zero. When either axis is zero, return the
78
+ * cached value if one exists, otherwise the supplied `fallback`.
79
+ *
80
+ * `null` containers (no element attached) yield the fallback
81
+ * directly; the cache is left untouched.
82
+ *
83
+ * @param container - The scrollable wrapper around the canvas, or
84
+ * `null` if no container has been resolved.
85
+ * @param fallback - The size to use when no live measurement and no
86
+ * cached measurement is available. Callers should
87
+ * pass `(options.canvasWidth, options.canvasHeight)`.
88
+ */
89
+ measure(container: HTMLElement | null, fallback: ViewportSize, scrollbarSize?: Partial<ScrollbarSize> | null): ViewportSize;
90
+ /**
91
+ * Return the cached viewport size without re-measuring. Useful for
92
+ * tests and for diagnostic logging. `null` indicates no non-zero
93
+ * measurement has been observed yet.
94
+ */
95
+ peek(): ViewportSize | null;
96
+ /**
97
+ * Discard the cached measurement. Intended for `dispose` paths.
98
+ * Calling `measure` again after `clear` behaves as if the editor
99
+ * has just been instantiated.
100
+ */
101
+ clear(): void;
102
+ }
103
+ /**
104
+ * Result of a layout computation. The image-loader applies these
105
+ * values to the Fabric canvas and the image object after a successful
106
+ * decode and Fabric load.
107
+ *
108
+ * `imageScale` is the Fabric `scaleX/scaleY` value that produces the
109
+ * desired on-canvas size for the image. `baseImageScale` is the
110
+ * editor's anchor scale used when computing zoom factors — see
111
+ * `image/transform-controller.ts` for how it is consumed.
112
+ */
113
+ export interface LayoutResult {
114
+ canvasWidth: number;
115
+ canvasHeight: number;
116
+ imageScale: number;
117
+ imageLeft: number;
118
+ imageTop: number;
119
+ baseImageScale: number;
120
+ }
121
+ /**
122
+ * Measure the browser's native scrollbar gutter. Overlay-scrollbar
123
+ * environments legitimately return zero on one or both axes.
124
+ */
125
+ export declare function measureScrollbarSize(ownerDocument?: Document | null): ScrollbarSize;
126
+ /**
127
+ * Measure the full layout viewport represented by the canvas container.
128
+ *
129
+ * In `overflow: auto` containers, `clientWidth` / `clientHeight` can already
130
+ * be reduced by scrollbars left over from the previous canvas size. v1.4.2
131
+ * avoided using that reduced viewport by adding the gutter back before the
132
+ * next Cover/Fit calculation. v2 keeps the same recovery rule without
133
+ * mutating `style.overflow`.
134
+ */
135
+ export declare function measureContainerViewport(container: HTMLElement | null, fallback: ViewportSize, scrollbarSize?: Partial<ScrollbarSize> | null): ViewportSize;
136
+ /**
137
+ * Compute canvas dimensions for content that may overflow the visible
138
+ * viewport.
139
+ *
140
+ * An overflowing axis grows to the content size, while a non-overflowing
141
+ * axis uses the viewport space left after the perpendicular scrollbar
142
+ * gutter is accounted for. This keeps Cover/Fit from accidentally creating
143
+ * a second scrollbar solely because the first scrollbar reduced the cross
144
+ * axis client size.
145
+ */
146
+ export declare function computeScrollableCanvasSize(contentWidth: number, contentHeight: number, viewport: ViewportSize, scrollbarSize?: Partial<ScrollbarSize> | null): ViewportSize;
147
+ /**
148
+ * Compute layout for the `fit` strategy.
149
+ *
150
+ * The canvas is set to the visible container viewport, falling back to
151
+ * `(options.canvasWidth/Height)` only when no viewport measurement is
152
+ * available, minus one pixel per axis to leave room for any sub-pixel
153
+ * rounding error and avoid tripping the container's auto scrollbars.
154
+ * The image is uniformly scaled down to fit, but never up
155
+ * (`Math.min(..., 1)`).
156
+ *
157
+ */
158
+ export declare function computeFitLayout(imageWidth: number, imageHeight: number, optionsCanvasWidth: number, optionsCanvasHeight: number, containerSize: ViewportSize): LayoutResult;
159
+ /**
160
+ * Compute layout for the `cover` strategy.
161
+ *
162
+ * The visible viewport determines the cover target (with a final fall-back
163
+ * to the configured canvas dimensions if a viewport axis is zero — the
164
+ * {@link ViewportCache} normally prevents this from happening). Large
165
+ * images are scaled down until Cover fills one axis without upscaling small
166
+ * images. When the filled axis would need a scrollbar, the scale is
167
+ * recomputed against the cross-axis space left after that scrollbar appears;
168
+ * this preserves the Cover invariant that at least one axis stays scroll-free.
169
+ *
170
+ */
171
+ export declare function computeCoverLayout(imageWidth: number, imageHeight: number, optionsCanvasWidth: number, optionsCanvasHeight: number, containerSize: ViewportSize, scrollbarSize?: Partial<ScrollbarSize> | null): LayoutResult;
172
+ /**
173
+ * Compute layout for the `expand` strategy.
174
+ *
175
+ * The canvas grows per-axis to `max(viewport, image)` and the image is
176
+ * placed at `(0, 0)` at its native size. `baseImageScale` is `1` to
177
+ * preserve the image's natural aspect ratio and top-left placement.
178
+ *
179
+ */
180
+ export declare function computeExpandLayout(imageWidth: number, imageHeight: number, optionsCanvasWidth: number, optionsCanvasHeight: number, containerSize: ViewportSize): LayoutResult;
181
+ /**
182
+ * Apply canvas pixel dimensions atomically and force a synchronous
183
+ * reflow on the container.
184
+ *
185
+ * In Fabric.js v7 the canvas is a pair of `<canvas>` elements
186
+ * (`lowerCanvasEl` for rendering, `upperCanvasEl` for pointer events).
187
+ * `Canvas.setDimensions` is the only API that updates both layers
188
+ * atomically and keeps their CSS in sync. Manually mutating
189
+ * Direct canvas element style writes only resize the lower layer and
190
+ * misaligns the upper layer's hit-test regions, so the editor always
191
+ * routes through this helper instead of touching the canvas element
192
+ * styles.
193
+ *
194
+ * After `setDimensions`, the container is reflowed by reading
195
+ * `offsetWidth` (see `utils/dom.ts → forceReflow`). This makes
196
+ * `overflow: auto` containers show or hide their scrollbars before
197
+ * the next paint instead of waiting for the following frame.
198
+ *
199
+ * The `width` and `height` arguments are clamped to a minimum of `1`
200
+ * and rounded to integer pixels. Non-finite or non-numeric inputs
201
+ * collapse to `1` rather than crashing the editor.
202
+ *
203
+ * @param canvas - The Fabric canvas to resize. Required.
204
+ * @param width - Target pixel width. Clamped to `>= 1` and rounded.
205
+ * @param height - Target pixel height. Clamped to `>= 1` and rounded.
206
+ * @param containerElement - The wrapper element to reflow. May be `null`
207
+ * when no container has been resolved; in that
208
+ * case the reflow is skipped without error.
209
+ */
210
+ export declare function applyCanvasDimensions(canvas: FabricNS.Canvas, width: number, height: number, containerElement: HTMLElement | null): void;
211
+ //# sourceMappingURL=layout-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout-manager.d.ts","sourceRoot":"","sources":["../../../src/image/layout-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,KAAK,QAAQ,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAK1D;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC;AAExC;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,CAErE;AAID;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,kDAAkD;AAClD,MAAM,MAAM,aAAa,GAAG,YAAY,CAAC;AAEzC,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,UAAU,CAAC;AAErD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,aAAa;IACtB,OAAO,CAAC,WAAW,CAA6B;IAEhD;;;;;;;;;;;;;OAaG;IACH,OAAO,CACH,SAAS,EAAE,WAAW,GAAG,IAAI,EAC7B,QAAQ,EAAE,YAAY,EACtB,aAAa,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,GAC9C,YAAY;IAWf;;;;OAIG;IACH,IAAI,IAAI,YAAY,GAAG,IAAI;IAI3B;;;;OAIG;IACH,KAAK,IAAI,IAAI;CAGhB;AAID;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CAC1B;AAiDD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,aAAa,CAAC,EAAE,QAAQ,GAAG,IAAI,GAAG,aAAa,CAoBnF;AASD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACpC,SAAS,EAAE,WAAW,GAAG,IAAI,EAC7B,QAAQ,EAAE,YAAY,EACtB,aAAa,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,GAC9C,YAAY,CAwBd;AAED;;;;;;;;;GASG;AACH,wBAAgB,2BAA2B,CACvC,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,YAAY,EACtB,aAAa,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,GAC9C,YAAY,CA0Bd;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC5B,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,MAAM,EAC1B,mBAAmB,EAAE,MAAM,EAC3B,aAAa,EAAE,YAAY,GAC5B,YAAY,CAYd;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAC9B,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,MAAM,EAC1B,mBAAmB,EAAE,MAAM,EAC3B,aAAa,EAAE,YAAY,EAC3B,aAAa,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,GAC9C,YAAY,CA2Cd;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAC/B,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,MAAM,EAC1B,mBAAmB,EAAE,MAAM,EAC3B,aAAa,EAAE,YAAY,GAC5B,YAAY,CAWd;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,qBAAqB,CACjC,MAAM,EAAE,QAAQ,CAAC,MAAM,EACvB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,WAAW,GAAG,IAAI,GACrC,IAAI,CAKN"}
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Animated scale, rotate, and reset operations on the
3
+ * `originalImage` with history integration. Owns the current
4
+ * transform pipeline that the `ImageEditor` facade routes
5
+ * through the `AnimationQueue`.
6
+ *
7
+ * ## Owned contracts
8
+ *
9
+ * - `scaleImage(factor)` clamps `factor` to
10
+ * `[options.minScale, options.maxScale]` and animates the image scale
11
+ * to the clamped factor over `options.animationDuration` milliseconds.
12
+ * `scaleX` and `scaleY` are tweened together; {@link animateProps}
13
+ * hides the v7 per-property
14
+ * `onComplete` shape.
15
+ * - `rotateImage(degrees)` animates `angle` to
16
+ * the requested value over `options.animationDuration` milliseconds.
17
+ * The animation tweens around the visual centroid by temporarily
18
+ * switching the image origin to `'center'/'center'`; the original
19
+ * `'left'/'top'` origin is restored after the animation settles.
20
+ * - `scaleImage()` and `rotateImage()` return resolved promises for
21
+ * non-finite inputs without modifying canvas state. The guards are the
22
+ * first checks so no rollback bundle is needed.
23
+ * - `resetImageTransform` records exactly
24
+ * one history entry covering the full reset. Per-operation
25
+ * `saveCanvasState` calls inside the chained `scaleImage(1)` and
26
+ * `rotateImage(0)` are suppressed via
27
+ * {@link TransformContext.setSuppressSaveState}, and a single
28
+ * `saveCanvasState` is emitted at the very end (success path) so
29
+ * the entire reset is one undoable step. Failures inside the chain
30
+ * still release the suppression flag in `finally` so subsequent
31
+ * transforms continue to record history.
32
+ * - `scaleImage`, `rotateImage`, and
33
+ * `resetImageTransform` each call `saveCanvasState` on success so
34
+ * the new state is undoable. The orchestrator wires
35
+ * `saveCanvasState` to the full `core/state-serializer.ts → saveState`
36
+ * path; this module does not serialize the canvas itself.
37
+ *
38
+ * ## Dispose, animation guard, and origin safety
39
+ *
40
+ * The transform pipeline cooperates with three guards:
41
+ *
42
+ * 1. The orchestrator's {@link TransformContext.guard} sets
43
+ * `isAnimating` true/false around the Fabric animation. The
44
+ * `OperationGuard.runAnimation()` bracket clears the flag inside a
45
+ * `finally` so the public promise sees `isAnimating === false` before
46
+ * settling.
47
+ * 2. The animation queue (owned by the orchestrator) serializes
48
+ * `scaleImage`, `rotateImage`, and `resetImageTransform` so concurrent
49
+ * callers do not interleave mid-animation Fabric mutations. The
50
+ * transform controller does NOT enqueue on the queue itself; the
51
+ * orchestrator wraps each call through `animQueue.add(...)` before
52
+ * invoking the controller method.
53
+ * 3. The dispose flag on {@link TransformContext.guard} lets animation
54
+ * callbacks consult `guard.isDisposed()` before touching the canvas.
55
+ * Rotation animations also restore the `'left'/'top'` origin via
56
+ * {@link restoreOrigin} when dispose interrupts the animation so a
57
+ * post-dispose inspector or a re-init that reuses the image reference
58
+ * still sees the documented origin.
59
+ *
60
+ * ## Why a class with a context bundle?
61
+ *
62
+ * The legacy monolithic `ImageEditor` owned all transform state. This module keeps that
63
+ * state on the facade so `currentScale`, `currentRotation`,
64
+ * `baseImageScale`, and `shouldSuppressSaveState` remain on a single owner
65
+ * (these are part of the snapshot wire format). The
66
+ * controller therefore reads and writes through the
67
+ * {@link TransformContext} accessor pairs rather than duplicating the
68
+ * fields. Mirrors the same pattern used by
69
+ * `LoadImageContext`.
70
+ *
71
+ * Owner module references (per the documented "Mapping Contracts to
72
+ * modules" table): this module is imported by `image-editor.ts`. It is
73
+ * intentionally NOT re-exported from `src/index.ts`.
74
+ *
75
+ * @module
76
+ */
77
+ import type * as FabricNS from 'fabric';
78
+ import type { ResolvedOptions } from '../core/public-types.js';
79
+ import type { OperationGuard } from '../core/operation-guard.js';
80
+ /**
81
+ * Dependency bundle passed by the `ImageEditor` facade into
82
+ * {@link TransformController}. Mirrors the
83
+ * `LoadImageContext` shape so each pipeline
84
+ * keeps the orchestrator as the single owner of editor state.
85
+ *
86
+ * The facade is responsible for:
87
+ *
88
+ * - constructing a single {@link OperationGuard} per editor and reusing it
89
+ * across pipelines so `isAnimating` and `isDisposed` live in one place,
90
+ * - routing each public transform method through the animation queue
91
+ * before calling into the controller,
92
+ * - wiring {@link TransformContext.saveCanvasState} to the shared
93
+ * `core/state-serializer.ts → saveState` path so the snapshot embeds
94
+ * `_editorState`,
95
+ * - honouring {@link TransformContext.setSuppressSaveState} by making
96
+ * `saveCanvasState` a no-op while the suppression flag is `true` —
97
+ * this is what lets `resetImageTransform` record exactly one history
98
+ * entry,
99
+ * - performing post-animation UI refresh (rotation/scale input boxes,
100
+ * undo/redo buttons, mask label sync) — those concerns belong on the
101
+ * facade, not in the controller, so per-step UI updates remain
102
+ * centralized.
103
+ *
104
+ */
105
+ export interface TransformContext {
106
+ /** The live Fabric canvas the original image lives on. */
107
+ canvas: FabricNS.Canvas;
108
+ /** Resolved editor options (animation duration, min/max scale). */
109
+ options: ResolvedOptions;
110
+ /**
111
+ * Shared operation guard. The controller calls
112
+ * {@link OperationGuard.runAnimation} to set `isAnimating` for the
113
+ * lifetime of each Fabric animation and to honour the dispose flag.
114
+ */
115
+ guard: OperationGuard;
116
+ /** Reads the previously-committed `originalImage`. */
117
+ getOriginalImage(): FabricNS.FabricImage | null;
118
+ /** Reads `currentScale`. */
119
+ getCurrentScale(): number;
120
+ /** Writes `currentScale`. */
121
+ setCurrentScale(n: number): void;
122
+ /** Reads `currentRotation` in degrees. */
123
+ getCurrentRotation(): number;
124
+ /** Writes `currentRotation` in degrees. */
125
+ setCurrentRotation(n: number): void;
126
+ /** Reads `baseImageScale` chosen at load time. */
127
+ getBaseImageScale(): number;
128
+ /**
129
+ * Persist a snapshot to the history stack. Wired by the orchestrator
130
+ * to `core/state-serializer.ts → saveState` plus the surrounding
131
+ * mask-label hide/restore bracket. While the suppression flag set by
132
+ * `setSuppressSaveState(true)` is active, the orchestrator
133
+ * MUST treat this as a no-op so `resetImageTransform` records exactly
134
+ * one history entry.
135
+ */
136
+ saveCanvasState(): void;
137
+ /**
138
+ * Toggle the suppression flag that turns {@link saveCanvasState} into
139
+ * a no-op. Used by {@link TransformController.resetImageTransform} to
140
+ * collapse the per-operation history entries from the chained
141
+ * `scaleImage(1)` and `rotateImage(0)` calls into a single reset
142
+ * entry. The orchestrator owns the flag itself; this method is the
143
+ * controller's only handle on it.
144
+ */
145
+ setSuppressSaveState(suppress: boolean): void;
146
+ /**
147
+ * Optional post-snap hook the orchestrator wires for legacy parity. Runs
148
+ * AFTER the controller commits the final value with `set` /
149
+ * `setCoords` and BEFORE `saveCanvasState`. Used to:
150
+ *
151
+ * - resize the canvas according to the current layout mode,
152
+ * - re-align the image bounding box to the canvas top-left,
153
+ * - re-sync mask label positions for visible labels.
154
+ *
155
+ * The hook is invoked only on the success path (no dispose) and only
156
+ * when defined — controllers running outside the facade (in tests)
157
+ * may omit it. Errors thrown from the hook propagate to the caller's
158
+ * `try/catch`, which mirrors legacy behaviour where the post-snap UI
159
+ * helpers ran inline inside the transform method.
160
+ */
161
+ afterTransformSnap?(): void;
162
+ }
163
+ /**
164
+ * Owns the animated `scaleImage`, `rotateImage`, and
165
+ * `resetImageTransform` operations. Each method is invoked from a queue
166
+ * entry on the orchestrator's animation queue and returns a Promise that
167
+ * resolves once the Fabric animation has settled and `saveCanvasState`
168
+ * has been called (or the operation no-opped because of dispose, an
169
+ * already-running animation, or non-finite input).
170
+ *
171
+ * Lifetime is one-per-editor — a fresh controller is constructed inside
172
+ * the `ImageEditor` constructor and lives until `dispose` runs. The
173
+ * controller holds no mutable state of its own; the {@link TransformContext}
174
+ * is the single source of truth.
175
+ *
176
+ */
177
+ export declare class TransformController {
178
+ private readonly context;
179
+ /**
180
+ * @param context - Dependency bundle owned by the `ImageEditor` facade.
181
+ */
182
+ constructor(context: TransformContext);
183
+ /**
184
+ * Animate the image scale to `factor`, clamped to
185
+ * `[options.minScale, options.maxScale]`.
186
+ *
187
+ * Steps:
188
+ *
189
+ * 1. Bail (resolved) when no image is loaded, an animation is already
190
+ * in progress, or the editor has been disposed.
191
+ * 2. Clamp `factor` to `[minScale, maxScale]` and update
192
+ * `currentScale` so toolbar inputs reflect the requested value
193
+ * BEFORE the animation begins (matches legacy timing).
194
+ * 3. Re-anchor the image origin to its current visual top-left so
195
+ * `scaleX` / `scaleY` tweens scale around the upper-left corner
196
+ * rather than the Fabric default centre.
197
+ * 4. Run a `scaleX` + `scaleY` tween via
198
+ * {@link animateProps} inside an
199
+ * {@link OperationGuard.runAnimation} bracket.
200
+ * 5. After the animation settles, snap to the exact target via
201
+ * `set({ scaleX, scaleY})` + `setCoords` so floating-point
202
+ * drift on the last tick does not leak into history.
203
+ * 6. Run the optional {@link TransformContext.afterTransformSnap}
204
+ * hook for canvas resize / mask label sync.
205
+ * 7. Call {@link TransformContext.saveCanvasState} so the new state
206
+ * is undoable. When dispose ran during the
207
+ * animation, the controller exits without snapping or saving so
208
+ * no torn-down canvas reference is touched.
209
+ *
210
+ * @param factor - Requested scale factor (1 = base, may exceed bounds —
211
+ * the value is clamped before use).
212
+ * @returns A promise that resolves once the animation has settled and
213
+ * history has been recorded, or immediately when the call
214
+ * short-circuits due to one of the bail conditions.
215
+ *
216
+ */
217
+ scaleImage(factor: number): Promise<void>;
218
+ /**
219
+ * Animate the image rotation to `degrees`. Returns a resolved promise
220
+ * without modifying canvas state when `degrees` is not finite.
221
+ *
222
+ * Steps:
223
+ *
224
+ * 1. Bail (resolved) on non-finite input, missing image, in-flight animation,
225
+ * or disposed editor.
226
+ * 2. Update `currentRotation` BEFORE the animation begins so toolbar
227
+ * inputs reflect the requested value during the tween.
228
+ * 3. Switch the image origin to `'center'/'center'` around its
229
+ * visual centroid so Fabric tweens the angle around the centre
230
+ * of mass rather than the top-left corner.
231
+ * 4. Run an `angle` tween via {@link animateProps} inside an
232
+ * {@link OperationGuard.runAnimation} bracket.
233
+ * 5. After the animation settles, snap to the exact target via
234
+ * `set('angle', degrees)` + `setCoords`.
235
+ * 6. Restore the `'left'/'top'` origin around the new visual
236
+ * top-left corner so subsequent placements (mask creation, crop
237
+ * rectangle, etc.) read off the documented origin.
238
+ * 7. Run the optional {@link TransformContext.afterTransformSnap}
239
+ * hook for canvas resize and mask label sync.
240
+ * 8. Call {@link TransformContext.saveCanvasState}.
241
+ *
242
+ * If dispose runs during the animation, step 6's origin restore
243
+ * branch is skipped — leaving the image in the temporary
244
+ * centre-origin state. The controller invokes
245
+ * {@link restoreOrigin} from `finally` so a post-dispose inspector
246
+ * still sees the documented `'left'/'top'` origin. `restoreOrigin`
247
+ * is documented as silent
248
+ * best-effort cleanup so it cannot mask the original animation
249
+ * error.
250
+ *
251
+ * @param degrees - Target rotation angle in degrees. Non-finite values are no-ops.
252
+ * @returns A promise that resolves once the animation has settled and
253
+ * history has been recorded, or immediately when the call
254
+ * short-circuits due to one of the bail conditions.
255
+ *
256
+ */
257
+ rotateImage(degrees: number): Promise<void>;
258
+ /**
259
+ * Animate the image to `scale = 1` and `rotation = 0` and record
260
+ * exactly one history entry covering the whole reset.
261
+ *
262
+ * Implementation strategy: chain `scaleImage(1)` and `rotateImage(0)`
263
+ * but suppress their per-operation history entries via
264
+ * {@link TransformContext.setSuppressSaveState}. After both
265
+ * sub-animations settle (or one rejects), release the suppression
266
+ * flag and emit a single `saveCanvasState` so the entire reset is
267
+ * one undoable step.
268
+ *
269
+ * Failure handling:
270
+ *
271
+ * - If `scaleImage(1)` or `rotateImage(0)` throws, the suppression
272
+ * flag is still released in `finally` so subsequent transforms
273
+ * continue to record history.
274
+ * - The single `saveCanvasState` call only runs on the success path.
275
+ * A failed reset therefore does not push a half-applied snapshot
276
+ * to history (the partially-applied scale or rotation is still
277
+ * recoverable via subsequent successful transforms).
278
+ *
279
+ * @returns A promise that resolves once both sub-animations have
280
+ * settled and the single history entry has been recorded.
281
+ * Resolves immediately as a no-op when no image is loaded.
282
+ *
283
+ */
284
+ resetImageTransform(): Promise<void>;
285
+ }
286
+ //# sourceMappingURL=transform-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform-controller.d.ts","sourceRoot":"","sources":["../../../src/image/transform-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2EG;AAEH,OAAO,KAAK,KAAK,QAAQ,MAAM,QAAQ,CAAC;AAExC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAKjE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,gBAAgB;IAC7B,0DAA0D;IAC1D,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC;IACxB,mEAAmE;IACnE,OAAO,EAAE,eAAe,CAAC;IACzB;;;;OAIG;IACH,KAAK,EAAE,cAAc,CAAC;IAEtB,sDAAsD;IACtD,gBAAgB,IAAI,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC;IAEhD,4BAA4B;IAC5B,eAAe,IAAI,MAAM,CAAC;IAC1B,6BAA6B;IAC7B,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEjC,0CAA0C;IAC1C,kBAAkB,IAAI,MAAM,CAAC;IAC7B,2CAA2C;IAC3C,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEpC,kDAAkD;IAClD,iBAAiB,IAAI,MAAM,CAAC;IAE5B;;;;;;;OAOG;IACH,eAAe,IAAI,IAAI,CAAC;IAExB;;;;;;;OAOG;IACH,oBAAoB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;IAE9C;;;;;;;;;;;;;;OAcG;IACH,kBAAkB,CAAC,IAAI,IAAI,CAAC;CAC/B;AAID;;;;;;;;;;;;;GAaG;AACH,qBAAa,mBAAmB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAE3C;;OAEG;gBACS,OAAO,EAAE,gBAAgB;IAIrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6D/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAsCG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0EjD;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;CAgB7C"}