@bensitu/image-editor 2.0.0 → 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 (83) hide show
  1. package/README.md +118 -16
  2. package/dist/cjs/index.cjs +1800 -330
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/esm/animation/animation-queue.js +16 -9
  5. package/dist/esm/animation/animation-queue.js.map +1 -1
  6. package/dist/esm/core/default-options.js +216 -9
  7. package/dist/esm/core/default-options.js.map +1 -1
  8. package/dist/esm/core/operation-guard.js +28 -0
  9. package/dist/esm/core/operation-guard.js.map +1 -1
  10. package/dist/esm/core/public-types.js.map +1 -1
  11. package/dist/esm/core/state-serializer.js +5 -4
  12. package/dist/esm/core/state-serializer.js.map +1 -1
  13. package/dist/esm/crop/crop-controller.js +4 -2
  14. package/dist/esm/crop/crop-controller.js.map +1 -1
  15. package/dist/esm/export/export-service.js +21 -10
  16. package/dist/esm/export/export-service.js.map +1 -1
  17. package/dist/esm/fabric/fabric-animation.js +56 -4
  18. package/dist/esm/fabric/fabric-animation.js.map +1 -1
  19. package/dist/esm/image/image-loader.js +9 -16
  20. package/dist/esm/image/image-loader.js.map +1 -1
  21. package/dist/esm/image/image-resampler.js +7 -2
  22. package/dist/esm/image/image-resampler.js.map +1 -1
  23. package/dist/esm/image/layout-manager.js +2 -20
  24. package/dist/esm/image/layout-manager.js.map +1 -1
  25. package/dist/esm/image/transform-controller.js.map +1 -1
  26. package/dist/esm/image-editor.js +383 -47
  27. package/dist/esm/image-editor.js.map +1 -1
  28. package/dist/esm/mask/mask-factory.js +53 -29
  29. package/dist/esm/mask/mask-factory.js.map +1 -1
  30. package/dist/esm/mask/mask-list.js +9 -3
  31. package/dist/esm/mask/mask-list.js.map +1 -1
  32. package/dist/esm/mosaic/mosaic-controller.js +670 -0
  33. package/dist/esm/mosaic/mosaic-controller.js.map +1 -0
  34. package/dist/esm/mosaic/mosaic-geometry.js +81 -0
  35. package/dist/esm/mosaic/mosaic-geometry.js.map +1 -0
  36. package/dist/esm/mosaic/mosaic-pixelate.js +71 -0
  37. package/dist/esm/mosaic/mosaic-pixelate.js.map +1 -0
  38. package/dist/esm/ui/dom-bindings.js +10 -3
  39. package/dist/esm/ui/dom-bindings.js.map +1 -1
  40. package/dist/esm/utils/number.js.map +1 -1
  41. package/dist/types/animation/animation-queue.d.ts.map +1 -1
  42. package/dist/types/core/default-options.d.ts +34 -6
  43. package/dist/types/core/default-options.d.ts.map +1 -1
  44. package/dist/types/core/errors.d.ts +1 -1
  45. package/dist/types/core/operation-guard.d.ts +2 -0
  46. package/dist/types/core/operation-guard.d.ts.map +1 -1
  47. package/dist/types/core/public-types.d.ts +123 -13
  48. package/dist/types/core/public-types.d.ts.map +1 -1
  49. package/dist/types/core/state-serializer.d.ts +3 -1
  50. package/dist/types/core/state-serializer.d.ts.map +1 -1
  51. package/dist/types/crop/crop-controller.d.ts.map +1 -1
  52. package/dist/types/export/export-service.d.ts.map +1 -1
  53. package/dist/types/fabric/fabric-animation.d.ts.map +1 -1
  54. package/dist/types/image/image-loader.d.ts +2 -4
  55. package/dist/types/image/image-loader.d.ts.map +1 -1
  56. package/dist/types/image/image-resampler.d.ts +1 -1
  57. package/dist/types/image/image-resampler.d.ts.map +1 -1
  58. package/dist/types/image/layout-manager.d.ts +5 -49
  59. package/dist/types/image/layout-manager.d.ts.map +1 -1
  60. package/dist/types/image/transform-controller.d.ts +1 -2
  61. package/dist/types/image/transform-controller.d.ts.map +1 -1
  62. package/dist/types/image-editor.d.ts +20 -9
  63. package/dist/types/image-editor.d.ts.map +1 -1
  64. package/dist/types/index.d.cts +1 -1
  65. package/dist/types/index.d.cts.map +1 -1
  66. package/dist/types/index.d.ts +1 -1
  67. package/dist/types/index.d.ts.map +1 -1
  68. package/dist/types/mask/mask-factory.d.ts +24 -21
  69. package/dist/types/mask/mask-factory.d.ts.map +1 -1
  70. package/dist/types/mask/mask-list.d.ts.map +1 -1
  71. package/dist/types/mosaic/mosaic-controller.d.ts +82 -0
  72. package/dist/types/mosaic/mosaic-controller.d.ts.map +1 -0
  73. package/dist/types/mosaic/mosaic-geometry.d.ts +29 -0
  74. package/dist/types/mosaic/mosaic-geometry.d.ts.map +1 -0
  75. package/dist/types/mosaic/mosaic-pixelate.d.ts +23 -0
  76. package/dist/types/mosaic/mosaic-pixelate.d.ts.map +1 -0
  77. package/dist/types/ui/dom-bindings.d.ts +3 -1
  78. package/dist/types/ui/dom-bindings.d.ts.map +1 -1
  79. package/dist/types/utils/number.d.ts +1 -2
  80. package/dist/types/utils/number.d.ts.map +1 -1
  81. package/dist/umd/image-editor.umd.js +1 -1
  82. package/dist/umd/image-editor.umd.js.map +1 -1
  83. package/package.json +1 -1
package/README.md CHANGED
@@ -6,15 +6,10 @@
6
6
 
7
7
  A lightweight, TypeScript-first canvas image editor built on top of
8
8
  [Fabric.js](https://fabricjs.com/) v7. `ImageEditor` wraps a Fabric canvas
9
- with image loading, scale and rotation, mask creation, crop, history
9
+ with image loading, scale and rotation, mask creation, crop, Mosaic mode, history
10
10
  (undo/redo), and base64/file export — exposed as a single canonical class
11
11
  with a stable public surface.
12
12
 
13
- > **v2.0.0 is a behavior-preserving migration.** The v1 deprecated method
14
- > and property aliases have been removed in favor of the canonical names
15
- > documented below. See [`CHANGELOG.md`](./CHANGELOG.md) for the complete
16
- > rename map.
17
-
18
13
  ## Demo
19
14
 
20
15
  [https://bensitu.github.io/image-editor/](https://bensitu.github.io/image-editor/)
@@ -33,6 +28,8 @@ with a stable public surface.
33
28
  interleave
34
29
  - Bounded history stack with idempotent dispose
35
30
  - Crop session with mask preservation toggle and atomic apply/cancel
31
+ - Mosaic mode with circular brush preview, runtime brush/block controls, and
32
+ one undo step per successful pixelation click
36
33
  - Base64 and `File` exports with PNG/JPEG/WebP support, configurable
37
34
  multiplier, and mask compositing
38
35
 
@@ -135,6 +132,17 @@ resolve to `undefined`.
135
132
  <button id="applyCropButton">Apply Crop</button>
136
133
  <button id="cancelCropButton">Cancel Crop</button>
137
134
 
135
+ <button id="enterMosaicModeButton">Mosaic</button>
136
+ <button id="exitMosaicModeButton">Exit Mosaic</button>
137
+ <label>
138
+ Brush size
139
+ <input id="mosaicBrushSizeInput" type="range" min="8" max="160" step="1" value="48" />
140
+ </label>
141
+ <label>
142
+ Block size
143
+ <input id="mosaicBlockSizeInput" type="range" min="2" max="40" step="1" value="8" />
144
+ </label>
145
+
138
146
  <button id="mergeMasksButton">Merge</button>
139
147
  <button id="downloadImageButton">Download</button>
140
148
  <button id="undoButton">Undo</button>
@@ -156,6 +164,10 @@ const editor = new ImageEditor(fabric, {
156
164
  canvasWidth: 800,
157
165
  canvasHeight: 600,
158
166
  backgroundColor: '#ffffff',
167
+ defaultMosaicConfig: {
168
+ brushSize: 48,
169
+ blockSize: 8,
170
+ },
159
171
  } satisfies ImageEditorOptions);
160
172
 
161
173
  editor.init({
@@ -172,6 +184,10 @@ editor.init({
172
184
  enterCropModeButton: 'enterCropModeButton',
173
185
  applyCropButton: 'applyCropButton',
174
186
  cancelCropButton: 'cancelCropButton',
187
+ enterMosaicModeButton: 'enterMosaicModeButton',
188
+ exitMosaicModeButton: 'exitMosaicModeButton',
189
+ mosaicBrushSizeInput: 'mosaicBrushSizeInput',
190
+ mosaicBlockSizeInput: 'mosaicBlockSizeInput',
175
191
  mergeMasksButton: 'mergeMasksButton',
176
192
  downloadImageButton: 'downloadImageButton',
177
193
  undoButton: 'undoButton',
@@ -232,22 +248,31 @@ new ImageEditor(options?: ImageEditorOptions) // UMD: reads globalThis.fabric
232
248
  | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
233
249
  | `loadImage(base64, options?)` | Load an image from a `data:image/...` URL. Returns `Promise<void>`. Transactional: any failure restores the prior canvas, scroll, overflow, and snapshot state. |
234
250
  | `isImageLoaded()` | Returns `true` if a valid image is currently loaded on the canvas. |
235
- | `isBusy()` | Returns `true` while the editor is loading, animating, or in crop mode. |
251
+ | `isBusy()` | Returns `true` while the editor is loading, animating, in crop mode, or in Mosaic mode. |
236
252
  | `setLayoutMode(mode)` | Select the layout strategy for future image loads. `mode` is `'fit'`, `'cover'`, or `'expand'`. |
237
253
 
238
254
  `LoadImageOptions` currently includes `preserveScroll?: boolean` for
239
255
  preserving the container's scroll position across both successful loads and
240
256
  rollback paths.
241
257
 
242
- Use `setLayoutMode()` instead of mutating internal options when a UI lets users
243
- choose how the next image should be placed:
258
+ Use `defaultLayoutMode` to choose the initial image-load strategy, then call
259
+ `setLayoutMode()` when a UI should change how future images are placed:
244
260
 
245
261
  ```ts
246
- editor.setLayoutMode('fit');
262
+ const editor = new ImageEditor(fabric, {
263
+ defaultLayoutMode: 'fit',
264
+ });
265
+
266
+ await editor.loadImage(imageA);
267
+
268
+ // Future loads use cover. The current image is not re-laid out immediately.
247
269
  editor.setLayoutMode('cover');
248
- editor.setLayoutMode('expand');
270
+ await editor.loadImage(imageB);
249
271
  ```
250
272
 
273
+ Invalid JavaScript `defaultLayoutMode` values fall back to `'expand'`.
274
+ Invalid `setLayoutMode()` calls are ignored and preserve the current mode.
275
+
251
276
  File-input helpers accept JPG, PNG, WebP, GIF, and BMP files. GIF and BMP are
252
277
  decoded as static raster input for canvas editing; GIF animation and BMP/GIF
253
278
  source-format preservation are not retained. Export output remains controlled by
@@ -273,6 +298,27 @@ the JPEG, PNG, or WebP export options.
273
298
  `fabricGenerator`. Falsy values in `styles` (`0`, `false`, `null`, `''`,
274
299
  `NaN`) are applied verbatim.
275
300
 
301
+ Use `defaultMaskConfig` to define constructor-level defaults for masks created
302
+ through either `createMask()` or the built-in `createMaskButton`. Per-call
303
+ `createMask(config)` values override `defaultMaskConfig`.
304
+
305
+ ```ts
306
+ const editor = new ImageEditor(fabric, {
307
+ defaultMaskConfig: {
308
+ color: 'rgba(255, 0, 0, 0.35)',
309
+ alpha: 0.35,
310
+ styles: {
311
+ stroke: '#ff0000',
312
+ strokeWidth: 2,
313
+ strokeDashArray: [6, 4],
314
+ },
315
+ },
316
+ });
317
+
318
+ editor.createMask(); // Uses defaultMaskConfig.
319
+ editor.createMask({ color: 'rgba(0, 128, 255, 0.35)' }); // Per-call override.
320
+ ```
321
+
276
322
  ### Crop
277
323
 
278
324
  | Method | Description |
@@ -281,6 +327,53 @@ the JPEG, PNG, or WebP export options.
281
327
  | `applyCrop()` | Apply the current crop region. Atomic: failure rolls back to the pre-crop snapshot. |
282
328
  | `cancelCrop()` | Cancel crop mode and restore the prior canvas state without pushing a history entry. |
283
329
 
330
+ ### Mosaic mode
331
+
332
+ | Method | Description |
333
+ | -------------------------- | ---------------------------------------------------------------------- |
334
+ | `enterMosaicMode()` | Enter circular-brush Mosaic mode and show the hover preview on canvas. |
335
+ | `exitMosaicMode()` | Leave Mosaic mode and remove preview/session handlers. |
336
+ | `isMosaicMode()` | Returns `true` while a Mosaic session is active. |
337
+ | `getMosaicConfig()` | Returns a defensive copy of the current runtime Mosaic config. |
338
+ | `setMosaicConfig(config)` | Patch current Mosaic config without creating a history entry. |
339
+ | `resetMosaicConfig()` | Restore current Mosaic config from constructor defaults. |
340
+ | `setMosaicBrushSize(size)` | Set brush diameter in canvas pixels. |
341
+ | `setMosaicBlockSize(size)` | Set source-pixel block size; values are floored to integers. |
342
+
343
+ `defaultMosaicConfig` initializes the current runtime Mosaic config. Runtime
344
+ setters update only the current config and never mutate constructor defaults.
345
+ `resetMosaicConfig()` clones the constructor defaults back into the current
346
+ config.
347
+
348
+ ```ts
349
+ const editor = new ImageEditor(fabric, {
350
+ defaultMosaicConfig: {
351
+ brushSize: 48,
352
+ blockSize: 8,
353
+ },
354
+ });
355
+
356
+ editor.init({
357
+ canvas: 'canvas',
358
+ enterMosaicModeButton: 'enterMosaicModeButton',
359
+ exitMosaicModeButton: 'exitMosaicModeButton',
360
+ mosaicBrushSizeInput: 'mosaicBrushSizeInput',
361
+ mosaicBlockSizeInput: 'mosaicBlockSizeInput',
362
+ });
363
+
364
+ editor.enterMosaicMode();
365
+ editor.setMosaicConfig({ brushSize: 64, blockSize: 12 });
366
+ editor.resetMosaicConfig();
367
+ ```
368
+
369
+ `brushSize` is the circular brush diameter in canvas pixels. `blockSize` is
370
+ the source-image pixel block size; larger values produce chunkier pixelation.
371
+ Clicking outside the image is a no-op. Each successful Mosaic click bakes the
372
+ pixelated region into the base image and creates exactly one undo step. Because
373
+ Mosaic edits replace base image pixels rather than adding Fabric overlay
374
+ objects, exported images include the Mosaic naturally while the preview circle
375
+ is never exported or saved in history.
376
+
284
377
  ### Merge and export
285
378
 
286
379
  | Method | Description |
@@ -335,9 +428,7 @@ ignored; nested `label` and `crop` objects are deep-merged with the defaults.
335
428
  | `maxScale` | `5.0` | Maximum scale factor. |
336
429
  | `scaleStep` | `0.05` | Scale delta per zoom step. |
337
430
  | `rotationStep` | `90` | Rotation step in degrees. |
338
- | `expandCanvasToImage` | `true` | Grow the canvas to fit the loaded image (lowest layout precedence). |
339
- | `fitImageToCanvas` | `false` | Fit the image inside the visible workspace viewport (highest layout precedence). |
340
- | `coverImageToCanvas` | `false` | Scale large images down to cover the visible workspace, cap at native size, and expand overflowing canvas axes so the container can scroll. |
431
+ | `defaultLayoutMode` | `'expand'` | Initial layout mode for image loads until changed by `setLayoutMode()`. Use `'fit'`, `'cover'`, or `'expand'`. Invalid runtime values fall back to `'expand'`. |
341
432
  | `downsampleOnLoad` | `true` | Downsample large images on load. |
342
433
  | `downsampleMaxWidth` | `4000` | Max width before downsampling kicks in. |
343
434
  | `downsampleMaxHeight` | `3000` | Max height before downsampling kicks in. |
@@ -352,6 +443,8 @@ ignored; nested `label` and `crop` objects are deep-merged with the defaults.
352
443
  | `mergeMaskByDefault` | `true` | Default mask compositing behavior for `exportImageBase64`, `exportImageFile`, and `downloadImage`. |
353
444
  | `defaultMaskWidth` | `50` | Default mask width. |
354
445
  | `defaultMaskHeight` | `80` | Default mask height. |
446
+ | `defaultMaskConfig` | `{}` | Defaults applied by `createMask()` after `defaultMaskWidth` / `defaultMaskHeight` and before per-call config. Supports `MaskConfig` fields except `onCreate` and `fabricGenerator`. |
447
+ | `defaultMosaicConfig` | see source | Defaults used to initialize the current Mosaic tool config. Supports `brushSize`, `blockSize`, preview circle styling, `outputFileType`, and `outputQuality`. Runtime Mosaic setters update the current config only. |
355
448
  | `maskRotatable` | `false` | Allow masks to be rotated by the user. |
356
449
  | `maskLabelOnSelect` | `true` | Show a label above a selected mask. |
357
450
  | `maskLabelOffset` | `3` | Pixel offset of the label from the mask's top-left corner. |
@@ -379,12 +472,17 @@ lossless and ignores `crop.exportQuality`; JPEG/WebP use `crop.exportQuality`
379
472
  when finite, otherwise `downsampleQuality`, otherwise `0.92`. Choose JPEG/WebP
380
473
  only when smaller intermediate crop output is preferred.
381
474
 
475
+ `defaultMosaicConfig.outputFileType` also defaults to `'source'`. Mosaic commits
476
+ preserve the current image MIME type when known and fall back to PNG when the
477
+ source format cannot be determined. JPEG/WebP commits use
478
+ `defaultMosaicConfig.outputQuality` when finite, otherwise `downsampleQuality`.
479
+
382
480
  ## Example workflow
383
481
 
384
482
  1. Construct `ImageEditor` with options and call `init(idMap)` to wire it up.
385
483
  2. Load an image via `loadImage(base64)` or the bound file input.
386
- 3. Adjust with `scaleImage`, `rotateImage`, `resetImageTransform`, and the
387
- crop session.
484
+ 3. Adjust with `scaleImage`, `rotateImage`, `resetImageTransform`, the crop
485
+ session, or Mosaic mode.
388
486
  4. Add `createMask` calls and inspect via the `maskList` element.
389
487
  5. Use `mergeMasks` to bake masks into the image, then
390
488
  `exportImageBase64`, `exportImageFile`, or `downloadImage` to produce
@@ -447,8 +545,11 @@ import type {
447
545
  ResolvedOptions,
448
546
  LabelConfig,
449
547
  CropConfig,
548
+ MosaicConfig,
549
+ ResolvedMosaicConfig,
450
550
  LoadImageOptions,
451
551
  RemoveAllMasksOptions,
552
+ DefaultMaskConfig,
452
553
  MaskConfig,
453
554
  MaskObject,
454
555
  MaskNumericProp,
@@ -458,6 +559,7 @@ import type {
458
559
  NormalizedImageFormat,
459
560
  ExportArea,
460
561
  CropExportFileType,
562
+ MosaicOutputFileType,
461
563
  Base64ExportOptions,
462
564
  ImageFileExportOptions,
463
565
  ImageInfo,