@gradio/imageeditor 0.12.14 → 0.14.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 (131) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/Example.svelte +1 -1
  3. package/IMAGE_EDITOR_OVERVIEW.md +224 -0
  4. package/ImageEditor.stories.svelte +13 -5
  5. package/Index.svelte +46 -45
  6. package/{shared/InteractiveImageEditor.svelte → InteractiveImageEditor.svelte} +65 -103
  7. package/README.md +2 -0
  8. package/dist/Example.svelte.d.ts +1 -1
  9. package/dist/Index.svelte +35 -33
  10. package/dist/Index.svelte.d.ts +25 -16
  11. package/dist/{shared/InteractiveImageEditor.svelte → InteractiveImageEditor.svelte} +54 -88
  12. package/dist/{shared/InteractiveImageEditor.svelte.d.ts → InteractiveImageEditor.svelte.d.ts} +17 -15
  13. package/dist/shared/Anchor.svelte +311 -0
  14. package/dist/shared/Anchor.svelte.d.ts +18 -0
  15. package/dist/shared/Controls.svelte +169 -18
  16. package/dist/shared/Controls.svelte.d.ts +9 -2
  17. package/dist/shared/{tools/IconButton.svelte → IconButton.svelte} +19 -3
  18. package/dist/shared/{tools/IconButton.svelte.d.ts → IconButton.svelte.d.ts} +4 -1
  19. package/dist/shared/ImageEditor.svelte +572 -289
  20. package/dist/shared/ImageEditor.svelte.d.ts +54 -56
  21. package/dist/shared/Layers.svelte +260 -0
  22. package/dist/shared/Layers.svelte.d.ts +37 -0
  23. package/dist/shared/Resize.svelte +218 -0
  24. package/dist/shared/Resize.svelte.d.ts +29 -0
  25. package/dist/shared/SecondaryToolbar.svelte +55 -0
  26. package/dist/shared/SecondaryToolbar.svelte.d.ts +37 -0
  27. package/dist/shared/Toolbar.svelte +337 -0
  28. package/dist/shared/Toolbar.svelte.d.ts +42 -0
  29. package/dist/shared/{tools → brush}/BrushOptions.svelte +68 -64
  30. package/dist/shared/{tools → brush}/BrushOptions.svelte.d.ts +9 -9
  31. package/dist/shared/{tools → brush}/BrushSize.svelte +2 -27
  32. package/dist/shared/{tools → brush}/ColorField.svelte +2 -1
  33. package/dist/shared/{tools → brush}/ColorField.svelte.d.ts +1 -0
  34. package/dist/shared/{tools → brush}/ColorPicker.svelte +81 -9
  35. package/dist/shared/{tools → brush}/ColorPicker.svelte.d.ts +1 -0
  36. package/dist/shared/{tools → brush}/ColorSwatch.svelte +78 -38
  37. package/dist/shared/{tools → brush}/ColorSwatch.svelte.d.ts +6 -2
  38. package/dist/shared/brush/brush-cursor.d.ts +101 -0
  39. package/dist/shared/brush/brush-cursor.js +327 -0
  40. package/dist/shared/brush/brush-textures.d.ts +65 -0
  41. package/dist/shared/brush/brush-textures.js +385 -0
  42. package/dist/shared/brush/brush-utils.d.ts +32 -0
  43. package/dist/shared/brush/brush-utils.js +59 -0
  44. package/dist/shared/brush/brush.d.ts +152 -0
  45. package/dist/shared/brush/brush.js +397 -0
  46. package/dist/shared/brush/types.d.ts +37 -0
  47. package/dist/shared/brush/types.js +1 -0
  48. package/dist/shared/core/editor.d.ts +261 -0
  49. package/dist/shared/core/editor.js +1264 -0
  50. package/dist/shared/crop/crop.d.ts +201 -0
  51. package/dist/shared/crop/crop.js +727 -0
  52. package/dist/shared/image/image.d.ts +89 -0
  53. package/dist/shared/image/image.js +183 -0
  54. package/dist/shared/resize/resize.d.ts +270 -0
  55. package/dist/shared/resize/resize.js +1413 -0
  56. package/dist/shared/types.d.ts +16 -0
  57. package/dist/shared/utils/pixi.d.ts +16 -57
  58. package/dist/shared/utils/pixi.js +15 -113
  59. package/dist/shared/zoom/zoom.d.ts +136 -0
  60. package/dist/shared/zoom/zoom.js +548 -0
  61. package/package.json +8 -7
  62. package/shared/Anchor.svelte +384 -0
  63. package/shared/Controls.svelte +178 -37
  64. package/shared/{tools/IconButton.svelte → IconButton.svelte} +21 -4
  65. package/shared/ImageEditor.svelte +712 -350
  66. package/shared/Layers.svelte +282 -0
  67. package/shared/Resize.svelte +239 -0
  68. package/shared/SecondaryToolbar.svelte +67 -0
  69. package/shared/Toolbar.svelte +386 -0
  70. package/shared/brush/BRUSH_TOOL.md +134 -0
  71. package/shared/brush/BrushOptions.svelte +200 -0
  72. package/shared/{tools → brush}/BrushSize.svelte +2 -30
  73. package/shared/{tools → brush}/ColorField.svelte +3 -1
  74. package/shared/{tools → brush}/ColorPicker.svelte +86 -9
  75. package/shared/{tools → brush}/ColorSwatch.svelte +95 -42
  76. package/shared/brush/brush-cursor.ts +408 -0
  77. package/shared/brush/brush-textures.ts +485 -0
  78. package/shared/brush/brush-utils.ts +76 -0
  79. package/shared/brush/brush.ts +510 -0
  80. package/shared/brush/types.ts +41 -0
  81. package/shared/core/EDITOR.md +182 -0
  82. package/shared/core/editor.ts +1788 -0
  83. package/shared/crop/CROP.md +139 -0
  84. package/shared/crop/crop.ts +1027 -0
  85. package/shared/image/IMAGE.md +135 -0
  86. package/shared/image/image.ts +298 -0
  87. package/shared/resize/resize.ts +1894 -0
  88. package/shared/types.ts +19 -0
  89. package/shared/utils/pixi.ts +19 -246
  90. package/shared/zoom/zoom.ts +729 -0
  91. package/dist/shared/layers/Layers.svelte +0 -231
  92. package/dist/shared/layers/Layers.svelte.d.ts +0 -18
  93. package/dist/shared/layers/utils.d.ts +0 -73
  94. package/dist/shared/layers/utils.js +0 -156
  95. package/dist/shared/tools/Brush.svelte +0 -215
  96. package/dist/shared/tools/Brush.svelte.d.ts +0 -29
  97. package/dist/shared/tools/Crop.svelte +0 -155
  98. package/dist/shared/tools/Crop.svelte.d.ts +0 -16
  99. package/dist/shared/tools/Cropper.svelte +0 -271
  100. package/dist/shared/tools/Cropper.svelte.d.ts +0 -41
  101. package/dist/shared/tools/Handle.svelte +0 -260
  102. package/dist/shared/tools/Handle.svelte.d.ts +0 -29
  103. package/dist/shared/tools/Sources.svelte +0 -257
  104. package/dist/shared/tools/Sources.svelte.d.ts +0 -34
  105. package/dist/shared/tools/Tools.svelte +0 -102
  106. package/dist/shared/tools/Tools.svelte.d.ts +0 -48
  107. package/dist/shared/tools/brush.d.ts +0 -62
  108. package/dist/shared/tools/brush.js +0 -133
  109. package/dist/shared/tools/crop.d.ts +0 -15
  110. package/dist/shared/tools/crop.js +0 -154
  111. package/dist/shared/tools/index.d.ts +0 -5
  112. package/dist/shared/tools/index.js +0 -5
  113. package/dist/shared/tools/sources.d.ts +0 -46
  114. package/dist/shared/tools/sources.js +0 -121
  115. package/dist/shared/tools/types.d.ts +0 -1
  116. package/shared/layers/Layers.svelte +0 -250
  117. package/shared/layers/utils.ts +0 -294
  118. package/shared/tools/Brush.svelte +0 -280
  119. package/shared/tools/BrushOptions.svelte +0 -180
  120. package/shared/tools/Crop.svelte +0 -189
  121. package/shared/tools/Cropper.svelte +0 -346
  122. package/shared/tools/Handle.svelte +0 -289
  123. package/shared/tools/Sources.svelte +0 -283
  124. package/shared/tools/Tools.svelte +0 -162
  125. package/shared/tools/brush.ts +0 -247
  126. package/shared/tools/crop.ts +0 -213
  127. package/shared/tools/index.ts +0 -5
  128. package/shared/tools/sources.ts +0 -195
  129. package/shared/tools/types.ts +0 -1
  130. /package/dist/shared/{tools → brush}/BrushSize.svelte.d.ts +0 -0
  131. /package/dist/shared/{tools/types.js → types.js} +0 -0
@@ -0,0 +1,397 @@
1
+ import { Container } from "pixi.js";
2
+ import {} from "../core/editor";
3
+ import {} from "../Toolbar.svelte";
4
+ import tinycolor from "tinycolor2";
5
+ import { BrushCursor } from "./brush-cursor";
6
+ import { BrushTextures } from "./brush-textures";
7
+ import { recurse_set_cursor } from "./brush-utils";
8
+ import {} from "./types";
9
+ /**
10
+ * BrushTool class implements the Tool interface for drawing and erasing.
11
+ * @class
12
+ * @implements {Tool}
13
+ */
14
+ export class BrushTool {
15
+ /**
16
+ * The name of the tool.
17
+ * @type {string}
18
+ */
19
+ name = "brush";
20
+ /**
21
+ * The context of the image editor.
22
+ * @private
23
+ */
24
+ image_editor_context;
25
+ /**
26
+ * The current tool selected in the toolbar.
27
+ * @private
28
+ */
29
+ current_tool;
30
+ /**
31
+ * The current subtool selected in the toolbar.
32
+ * @private
33
+ */
34
+ current_subtool;
35
+ /**
36
+ * The state of the brush tool.
37
+ * @private
38
+ */
39
+ state = {
40
+ opacity: 1,
41
+ brush_size: 10,
42
+ color: "#000000",
43
+ mode: "draw"
44
+ };
45
+ /**
46
+ * Size settings for brush and eraser
47
+ * @private
48
+ */
49
+ brush_size = 10;
50
+ eraser_size = 20;
51
+ /**
52
+ * Drawing state
53
+ * @private
54
+ */
55
+ is_drawing = false;
56
+ last_x = 0;
57
+ last_y = 0;
58
+ scale = 1;
59
+ /**
60
+ * Event listeners
61
+ * @private
62
+ */
63
+ _bound_pointer_down = null;
64
+ _bound_pointer_move = null;
65
+ _bound_pointer_up = null;
66
+ event_callbacks = new Map();
67
+ /**
68
+ * Utility modules
69
+ * @private
70
+ */
71
+ brush_cursor = null;
72
+ brush_textures = null;
73
+ /**
74
+ * Sets up the brush tool with the given context, tool, and subtool.
75
+ * @param {ImageEditorContext} context - The image editor context.
76
+ * @param {ToolbarTool} tool - The tool from the toolbar.
77
+ * @param {Subtool} subtool - The subtool from the toolbar.
78
+ * @returns {Promise<void>}
79
+ */
80
+ async setup(context, tool, subtool) {
81
+ // Store context and tool information
82
+ this.image_editor_context = context;
83
+ this.current_tool = tool;
84
+ this.current_subtool = subtool;
85
+ // Set mode based on tool
86
+ this.state.mode = tool === "erase" ? "erase" : "draw";
87
+ // Set size based on mode
88
+ if (this.state.mode === "draw") {
89
+ this.state.brush_size = this.brush_size;
90
+ }
91
+ else {
92
+ this.state.brush_size = this.eraser_size;
93
+ }
94
+ // Subscribe to scale changes
95
+ context.scale.subscribe((scale) => {
96
+ this.scale = scale;
97
+ if (this.brush_cursor) {
98
+ this.brush_cursor.update_state(this.state, this.scale);
99
+ }
100
+ });
101
+ // Initialize brush cursor
102
+ this.brush_cursor = new BrushCursor(this.image_editor_context, this.state, this.scale);
103
+ // Set initial active state
104
+ this.brush_cursor.set_active(tool === "draw" || tool === "erase");
105
+ // Initialize brush textures
106
+ this.brush_textures = new BrushTextures(this.image_editor_context, context.app);
107
+ this.brush_textures.initialize_textures();
108
+ // Set up event listeners
109
+ this.setup_event_listeners();
110
+ // Set cursor style for image container
111
+ this.handle_cursors(tool);
112
+ }
113
+ /**
114
+ * Handles cursor styles for the brush tool.
115
+ * @param {ToolbarTool} tool - The current tool.
116
+ * @private
117
+ */
118
+ handle_cursors(tool) {
119
+ // Set cursor style to 'none' for all children of the image container
120
+ recurse_set_cursor(this.image_editor_context.image_container.children, "none");
121
+ }
122
+ /**
123
+ * Sets the current tool and subtool.
124
+ * @param {ToolbarTool} tool - The current tool.
125
+ * @param {Subtool} subtool - The current subtool.
126
+ */
127
+ set_tool(tool, subtool) {
128
+ // Update current tool and subtool
129
+ this.current_tool = tool;
130
+ this.current_subtool = subtool;
131
+ if (this.current_tool !== "erase" && this.current_tool !== "draw") {
132
+ this.commit_pending_changes();
133
+ }
134
+ // Commit any pending changes
135
+ // Set brush cursor active state
136
+ if (this.brush_cursor) {
137
+ this.brush_cursor.set_active(tool === "draw" || tool === "erase");
138
+ }
139
+ // Update mode based on tool
140
+ const new_mode = tool === "erase" ? "erase" : "draw";
141
+ if (tool === "erase" || tool === "draw") {
142
+ this.brush_textures?.initialize_textures();
143
+ }
144
+ if (this.state.mode !== new_mode) {
145
+ this.state.mode = new_mode;
146
+ // Update brush size based on mode
147
+ if (this.state.mode === "draw") {
148
+ this.state.brush_size = this.brush_size;
149
+ }
150
+ else {
151
+ this.state.brush_size = this.eraser_size;
152
+ }
153
+ // Update cursor
154
+ if (this.brush_cursor) {
155
+ this.brush_cursor.update_state(this.state, this.scale);
156
+ }
157
+ }
158
+ }
159
+ /**
160
+ * Commits any pending changes to the canvas.
161
+ * @private
162
+ */
163
+ commit_pending_changes() {
164
+ if (this.is_drawing) {
165
+ this.on_pointer_up();
166
+ }
167
+ }
168
+ /**
169
+ * Called when the user presses the mouse button.
170
+ * @param {FederatedPointerEvent} event - The pointer event.
171
+ * @private
172
+ */
173
+ on_pointer_down(event) {
174
+ const current_layer = this.image_editor_context.layer_manager.get_active_layer();
175
+ if (!current_layer?.visible ||
176
+ (this.current_tool !== "erase" && this.current_tool !== "draw")) {
177
+ return;
178
+ }
179
+ if (this.brush_cursor && !this.brush_cursor.is_over_image())
180
+ return;
181
+ // Preserve the current canvas state
182
+ if (this.brush_textures) {
183
+ this.brush_textures.preserve_canvas_state();
184
+ }
185
+ // Get the local position
186
+ const local_pos = this.image_editor_context.image_container.toLocal(event.global);
187
+ // Start drawing
188
+ this.is_drawing = true;
189
+ this.last_x = local_pos.x;
190
+ this.last_y = local_pos.y;
191
+ // Draw a single point
192
+ if (this.brush_textures) {
193
+ this.brush_textures.draw_segment(local_pos.x, local_pos.y, local_pos.x, local_pos.y, this.state.brush_size, this.state.color, this.state.opacity, this.state.mode);
194
+ }
195
+ }
196
+ /**
197
+ * Called when the user moves the mouse while drawing.
198
+ * @param {FederatedPointerEvent} event - The pointer event.
199
+ * @private
200
+ */
201
+ on_pointer_move(event) {
202
+ // Update cursor position
203
+ if (this.brush_cursor) {
204
+ this.brush_cursor.update_cursor_position(event);
205
+ }
206
+ // If not drawing, exit
207
+ if (!this.is_drawing)
208
+ return;
209
+ // Get the local position
210
+ const local_pos = this.image_editor_context.image_container.toLocal(event.global);
211
+ // Draw line from last position to current position
212
+ if (this.brush_textures) {
213
+ this.brush_textures.draw_segment(this.last_x, this.last_y, local_pos.x, local_pos.y, this.state.brush_size, this.state.color, this.state.opacity, this.state.mode);
214
+ }
215
+ // Update last position
216
+ this.last_x = local_pos.x;
217
+ this.last_y = local_pos.y;
218
+ }
219
+ /**
220
+ * Called when the user releases the mouse button.
221
+ * @private
222
+ */
223
+ on_pointer_up() {
224
+ // If not drawing, exit
225
+ if (!this.is_drawing)
226
+ return;
227
+ // Stop drawing
228
+ this.is_drawing = false;
229
+ // Commit the stroke
230
+ if (this.brush_textures) {
231
+ this.brush_textures.commit_stroke();
232
+ }
233
+ this.notify("change");
234
+ }
235
+ /**
236
+ * Sets up event listeners for drawing.
237
+ * @private
238
+ */
239
+ setup_event_listeners() {
240
+ // Remove existing listeners if they exist
241
+ this.cleanup_event_listeners();
242
+ // Create bound event handlers
243
+ this._bound_pointer_down = this.on_pointer_down.bind(this);
244
+ this._bound_pointer_move = this.on_pointer_move.bind(this);
245
+ this._bound_pointer_up = this.on_pointer_up.bind(this);
246
+ // Set interactive mode for image container
247
+ const image_container = this.image_editor_context.image_container;
248
+ image_container.eventMode = "static";
249
+ image_container.interactiveChildren = true;
250
+ // Setup for stage
251
+ const stage = this.image_editor_context.app.stage;
252
+ stage.eventMode = "static";
253
+ // Add pointer events to the stage for global tracking
254
+ stage.on("pointerdown", this._bound_pointer_down);
255
+ stage.on("pointermove", this._bound_pointer_move);
256
+ stage.on("pointerup", this._bound_pointer_up);
257
+ stage.on("pointerupoutside", this._bound_pointer_up);
258
+ // Set up cursor event listeners
259
+ if (this.brush_cursor) {
260
+ this.brush_cursor.setup_event_listeners();
261
+ }
262
+ }
263
+ /**
264
+ * Cleans up event listeners.
265
+ * @private
266
+ */
267
+ cleanup_event_listeners() {
268
+ const stage = this.image_editor_context.app.stage;
269
+ // Remove event listeners if they exist
270
+ if (this._bound_pointer_down) {
271
+ stage.off("pointerdown", this._bound_pointer_down);
272
+ this._bound_pointer_down = null;
273
+ }
274
+ if (this._bound_pointer_move) {
275
+ stage.off("pointermove", this._bound_pointer_move);
276
+ this._bound_pointer_move = null;
277
+ }
278
+ if (this._bound_pointer_up) {
279
+ stage.off("pointerup", this._bound_pointer_up);
280
+ stage.off("pointerupoutside", this._bound_pointer_up);
281
+ this._bound_pointer_up = null;
282
+ }
283
+ // Clean up cursor event listeners
284
+ if (this.brush_cursor) {
285
+ this.brush_cursor.cleanup_event_listeners();
286
+ }
287
+ }
288
+ /**
289
+ * Sets the brush size.
290
+ * @param {number} size - The new brush size.
291
+ */
292
+ set_brush_size(size) {
293
+ // Set brush size
294
+ this.brush_size = size;
295
+ // Update state if in draw mode
296
+ if (this.state.mode === "draw") {
297
+ this.state.brush_size = size;
298
+ // Update cursor
299
+ if (this.brush_cursor) {
300
+ this.brush_cursor.update_state(this.state, this.scale);
301
+ }
302
+ }
303
+ }
304
+ /**
305
+ * Sets the brush color.
306
+ * @param {string|ColorInput} color - The new brush color.
307
+ */
308
+ set_brush_color(color) {
309
+ // Convert to hex string
310
+ const color_string = tinycolor(color).toHexString();
311
+ // Set color
312
+ this.state.color = color_string;
313
+ // Update cursor
314
+ if (this.brush_cursor) {
315
+ this.brush_cursor.update_state(this.state, this.scale);
316
+ }
317
+ }
318
+ /**
319
+ * Sets the brush opacity.
320
+ * @param {number} opacity - The new brush opacity.
321
+ */
322
+ set_brush_opacity(opacity) {
323
+ // Clamp opacity between 0 and 1
324
+ const clamped_opacity = Math.max(0, Math.min(1, opacity));
325
+ // Set opacity
326
+ this.state.opacity = clamped_opacity;
327
+ // Update cursor
328
+ if (this.brush_cursor) {
329
+ this.brush_cursor.update_state(this.state, this.scale);
330
+ }
331
+ }
332
+ /**
333
+ * Sets the eraser size.
334
+ * @param {number} size - The new eraser size.
335
+ */
336
+ set_eraser_size(size) {
337
+ // Store the new eraser size
338
+ this.eraser_size = size;
339
+ // Update state if in erase mode
340
+ if (this.state.mode === "erase") {
341
+ this.state.brush_size = size;
342
+ // Update cursor
343
+ if (this.brush_cursor) {
344
+ this.brush_cursor.update_state(this.state, this.scale);
345
+ }
346
+ }
347
+ }
348
+ /**
349
+ * Gets the current size based on mode (brush or eraser).
350
+ * @returns {number} The current size.
351
+ */
352
+ get_current_size() {
353
+ return this.state.mode === "draw" ? this.brush_size : this.eraser_size;
354
+ }
355
+ /**
356
+ * Shows or hides the brush preview.
357
+ * @param {boolean} show - Whether to show the preview.
358
+ */
359
+ preview_brush(show) {
360
+ if (this.brush_cursor) {
361
+ this.brush_cursor.preview_brush(show);
362
+ }
363
+ }
364
+ /**
365
+ * Cleans up resources used by the brush tool.
366
+ */
367
+ cleanup() {
368
+ // Commit any pending changes
369
+ this.commit_pending_changes();
370
+ // Clean up event listeners
371
+ this.cleanup_event_listeners();
372
+ // Clean up brush textures
373
+ if (this.brush_textures) {
374
+ this.brush_textures.cleanup();
375
+ this.brush_textures = null;
376
+ }
377
+ // Clean up brush cursor
378
+ if (this.brush_cursor) {
379
+ this.brush_cursor.cleanup();
380
+ this.brush_cursor = null;
381
+ }
382
+ }
383
+ on(event, callback) {
384
+ this.event_callbacks.set(event, [
385
+ ...(this.event_callbacks.get(event) || []),
386
+ callback
387
+ ]);
388
+ }
389
+ off(event, callback) {
390
+ this.event_callbacks.set(event, this.event_callbacks.get(event)?.filter((cb) => cb !== callback) || []);
391
+ }
392
+ notify(event) {
393
+ for (const callback of this.event_callbacks.get(event) || []) {
394
+ callback();
395
+ }
396
+ }
397
+ }
@@ -0,0 +1,37 @@
1
+ import { type ColorInput } from "tinycolor2";
2
+ /**
3
+ * Represents the state of the brush tool.
4
+ * @interface BrushState
5
+ * @property {number} opacity - The opacity of the brush.
6
+ * @property {number} brush_size - The size of the brush.
7
+ * @property {string} color - The color of the brush in hex format.
8
+ * @property {"draw"|"erase"} mode - The mode of the brush, either draw or erase.
9
+ */
10
+ export interface BrushState {
11
+ opacity: number;
12
+ brush_size: number;
13
+ color: string;
14
+ mode: "draw" | "erase";
15
+ }
16
+ export interface Eraser {
17
+ /**
18
+ * The default size of the eraser.
19
+ */
20
+ default_size: number | "auto";
21
+ }
22
+ export type ColorTuple = [ColorInput, number];
23
+ export interface Brush extends Eraser {
24
+ /**
25
+ * The default color of the brush.
26
+ */
27
+ default_color: ColorInput;
28
+ /**
29
+ * The colors to show in the color swatch.
30
+ * Can be either simple color strings or [color, opacity] tuples.
31
+ */
32
+ colors: (ColorInput | ColorTuple)[];
33
+ /**
34
+ * Whether to show _only_ the color swatches specified in `colors`, or to show the color swatches specified in `colors` along with the colorpicker.
35
+ */
36
+ color_mode: "fixed" | "defaults";
37
+ }
@@ -0,0 +1 @@
1
+ import {} from "tinycolor2";
@@ -0,0 +1,261 @@
1
+ import { Application, Container, Sprite, RenderTexture } from "pixi.js";
2
+ import type { Subtool, Tool as ToolbarTool } from "../Toolbar.svelte";
3
+ import type { Readable, Writable } from "svelte/store";
4
+ import { type Spring } from "svelte/motion";
5
+ import { type ImageBlobs, type LayerOptions } from "../types";
6
+ export interface Command {
7
+ execute: () => Promise<void>;
8
+ undo: () => Promise<void>;
9
+ }
10
+ export interface Tool {
11
+ name: string;
12
+ setup(context: ImageEditorContext, tool: ToolbarTool, subtool: Subtool): Promise<void>;
13
+ cleanup(): void;
14
+ set_tool(tool: ToolbarTool, subtool: Subtool): void;
15
+ on?: (event: string, callback: () => void) => void;
16
+ off?: (event: string, callback: () => void) => void;
17
+ }
18
+ export declare class CommandManager {
19
+ private undo_stack;
20
+ private redo_stack;
21
+ execute(command: Command): Promise<void>;
22
+ undo(): Promise<void>;
23
+ redo(): Promise<void>;
24
+ }
25
+ export declare class LayerManager {
26
+ private layers;
27
+ private active_layer;
28
+ private active_layer_id;
29
+ private draw_textures;
30
+ layer_store: Writable<{
31
+ active_layer: string;
32
+ layers: {
33
+ name: string;
34
+ id: string;
35
+ user_created: boolean;
36
+ visible: boolean;
37
+ }[];
38
+ }>;
39
+ private background_layer;
40
+ private image_container;
41
+ private app;
42
+ private fixed_canvas;
43
+ private dark;
44
+ private border_region;
45
+ private layer_options;
46
+ constructor(image_container: Container, app: Application, fixed_canvas: boolean, dark: boolean, border_region: number, layer_options: LayerOptions);
47
+ toggle_layer_visibility(id: string): void;
48
+ create_background_layer(width: number, height: number): Container;
49
+ set_layer_options(layer_options: LayerOptions, width: number, height: number): void;
50
+ /**
51
+ * Creates a background layer from an image URL
52
+ * @param url The URL of the image to use as background
53
+ * @param width The width of the layer (if fixed_canvas is true)
54
+ * @param height The height of the layer (if fixed_canvas is true)
55
+ * @param borderRegion Minimum border region to add around the image for outpainting (in pixels)
56
+ * @returns A Promise that resolves to the background Container when the image is loaded
57
+ */
58
+ create_background_layer_from_url(url: string, width?: number, height?: number): Promise<Container>;
59
+ create_layer({ width, height, layer_name, user_created, layer_id, make_active }: {
60
+ width: number;
61
+ height: number;
62
+ layer_name?: string;
63
+ user_created: boolean;
64
+ layer_id?: string;
65
+ make_active?: boolean;
66
+ }): Container;
67
+ /**
68
+ * Creates a new layer with an image loaded from a URL
69
+ * @param url The URL of the image to load
70
+ * @param borderRegion Minimum border region to add around the image for outpainting (in pixels)
71
+ * @returns A Promise that resolves to the layer ID
72
+ */
73
+ add_layer_from_url(url: string): Promise<string>;
74
+ get_active_layer(): typeof this.active_layer;
75
+ set_active_layer(id: string): void;
76
+ get_layers(): typeof this.layers;
77
+ get_layer_textures(id: string): {
78
+ draw: RenderTexture;
79
+ } | null;
80
+ delete_layer(id: string): void;
81
+ private update_layer_order;
82
+ move_layer(id: string, direction: "up" | "down"): void;
83
+ /**
84
+ * Resizes all layers to a new width and height
85
+ * @param newWidth The new width of the layers
86
+ * @param newHeight The new height of the layers
87
+ * @param scale If true, scales the layer content to fit the new dimensions. If false, keeps the content size unchanged.
88
+ * @param anchor The anchor point to position the content relative to the new size
89
+ */
90
+ resize_all_layers(newWidth: number, newHeight: number, scale: boolean, anchor: "top-left" | "top" | "top-right" | "left" | "center" | "right" | "bottom-left" | "bottom" | "bottom-right", oldCanvasWidth: number, oldCanvasHeight: number): void;
91
+ /**
92
+ * Helper method to resize the background layer.
93
+ */
94
+ private _resize_background_layer;
95
+ /**
96
+ * Helper method to resize a single regular layer.
97
+ */
98
+ private _resize_single_layer;
99
+ get_blobs(width: number, height: number): Promise<ImageBlobs>;
100
+ reset_layers(width: number, height: number, persist?: boolean): void;
101
+ init_layers(width: number, height: number): void;
102
+ }
103
+ declare const core_tools: readonly ["image", "zoom"];
104
+ interface ImageEditorOptions {
105
+ target_element: HTMLElement;
106
+ width: number;
107
+ height: number;
108
+ tools: ((typeof core_tools)[number] | Tool)[];
109
+ fixed_canvas?: boolean;
110
+ dark?: boolean;
111
+ border_region?: number;
112
+ layer_options?: LayerOptions;
113
+ pad_bottom?: number;
114
+ theme_mode?: "dark" | "light";
115
+ }
116
+ export interface ImageEditorContext {
117
+ app: Application;
118
+ ui_container: Container;
119
+ image_container: Container;
120
+ background_image?: Sprite;
121
+ command_manager: CommandManager;
122
+ layer_manager: LayerManager;
123
+ dimensions: Readable<{
124
+ width: number;
125
+ height: number;
126
+ }>;
127
+ scale: Readable<number>;
128
+ position: Readable<{
129
+ x: number;
130
+ y: number;
131
+ }>;
132
+ set_image_properties: (properties: {
133
+ width?: number;
134
+ height?: number;
135
+ scale?: number;
136
+ position?: {
137
+ x: number;
138
+ y: number;
139
+ };
140
+ animate?: boolean;
141
+ }) => Promise<void>;
142
+ execute_command: (command: Command) => Promise<void> | void;
143
+ resize_canvas: (width: number, height: number) => void;
144
+ reset: () => void;
145
+ set_background_image: (image: Sprite) => void;
146
+ pad_bottom: number;
147
+ }
148
+ export declare class ImageEditor {
149
+ ready: Promise<void>;
150
+ background_image_present: Writable<boolean>;
151
+ min_zoom: Writable<boolean>;
152
+ private app;
153
+ private ui_container;
154
+ private image_container;
155
+ private command_manager;
156
+ private layer_manager;
157
+ private tools;
158
+ private current_tool;
159
+ private current_subtool;
160
+ private target_element;
161
+ private width;
162
+ private height;
163
+ dimensions: Spring<{
164
+ width: number;
165
+ height: number;
166
+ }>;
167
+ scale: Spring<number>;
168
+ private position;
169
+ private state;
170
+ private scale_value;
171
+ private position_value;
172
+ private dimensions_value;
173
+ layers: Writable<{
174
+ active_layer: string;
175
+ layers: {
176
+ name: string;
177
+ id: string;
178
+ user_created: boolean;
179
+ visible: boolean;
180
+ }[];
181
+ }>;
182
+ private outline_container;
183
+ private outline_graphics;
184
+ private background_image?;
185
+ private ready_resolve;
186
+ private event_callbacks;
187
+ private fixed_canvas;
188
+ private dark;
189
+ private border_region;
190
+ private layer_options;
191
+ private overlay_container;
192
+ private overlay_graphics;
193
+ private pad_bottom;
194
+ private theme_mode;
195
+ constructor(options: ImageEditorOptions);
196
+ private get context();
197
+ init(): Promise<void>;
198
+ private setup_containers;
199
+ resize_canvas(width: number, height: number): void;
200
+ reset(): void;
201
+ set_image_properties(properties: {
202
+ width?: number;
203
+ height?: number;
204
+ scale?: number;
205
+ position?: {
206
+ x: number;
207
+ y: number;
208
+ };
209
+ animate?: boolean;
210
+ }): Promise<void>;
211
+ execute_command(command: Command): Promise<void>;
212
+ undo(): void;
213
+ redo(): void;
214
+ add_image({ image, resize }: {
215
+ image: Blob | File;
216
+ resize?: boolean;
217
+ }): Promise<void>;
218
+ /**
219
+ * Adds an image from a URL as the background layer
220
+ * @param url The URL of the image to add
221
+ */
222
+ add_image_from_url(url: string): Promise<void>;
223
+ set_tool(tool: ToolbarTool): void;
224
+ set_subtool(subtool: Subtool): void;
225
+ set_background_image(image: Sprite): void;
226
+ reset_canvas(): Promise<void>;
227
+ add_layer(): void;
228
+ /**
229
+ * Adds a new layer with an image loaded from a URL
230
+ * @param layer_urls The URLs of the images to load
231
+ * @returns A Promise that resolves when all layers are added
232
+ */
233
+ add_layers_from_url(layer_urls: string[] | undefined): Promise<void>;
234
+ set_layer(id: string): void;
235
+ move_layer(id: string, direction: "up" | "down"): void;
236
+ delete_layer(id: string): void;
237
+ modify_canvas_size(width: number, height: number, anchor: "top-left" | "top" | "top-right" | "left" | "center" | "right" | "bottom-left" | "bottom" | "bottom-right", scale: boolean): void;
238
+ get_blobs(): Promise<ImageBlobs>;
239
+ on(event: "change" | "input", callback: () => void): void;
240
+ off(event: "change", callback: () => void): void;
241
+ private notify;
242
+ destroy(): void;
243
+ resize(width: number, height: number): void;
244
+ get_crop_bounds(): Promise<{
245
+ image: Blob | null;
246
+ crop_dimensions: {
247
+ width: number;
248
+ height: number;
249
+ };
250
+ image_dimensions: {
251
+ width: number;
252
+ height: number;
253
+ };
254
+ x: number;
255
+ y: number;
256
+ }>;
257
+ get background_image_sprite(): Sprite | undefined;
258
+ set_layer_options(layer_options: LayerOptions): void;
259
+ toggle_layer_visibility(id: string): void;
260
+ }
261
+ export {};