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