@gradio/imageeditor 0.15.4 → 0.16.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @gradio/imageeditor
2
2
 
3
+ ## 0.16.0
4
+
5
+ ### Features
6
+
7
+ - [#11427](https://github.com/gradio-app/gradio/pull/11427) [`6b2bcd0`](https://github.com/gradio-app/gradio/commit/6b2bcd097ae5ef999a7fb273ecf7c7e4c0eab305) - Improve load times of the Gradio front-end. Thanks @pngwn!
8
+
9
+ ### Dependency updates
10
+
11
+ - @gradio/atoms@0.16.3
12
+ - @gradio/statustracker@0.10.14
13
+ - @gradio/upload@0.16.10
14
+ - @gradio/client@1.15.5
15
+ - @gradio/image@0.22.12
16
+
17
+ ## 0.15.5
18
+
19
+ ### Dependency updates
20
+
21
+ - @gradio/upload@0.16.9
22
+ - @gradio/client@1.15.4
23
+ - @gradio/image@0.22.11
24
+
3
25
  ## 0.15.4
4
26
 
5
27
  ### Dependency updates
@@ -3,7 +3,6 @@
3
3
  import ImageEditor from "./Index.svelte";
4
4
  import { format } from "svelte-i18n";
5
5
  import { get } from "svelte/store";
6
- import { userEvent, within } from "@storybook/test";
7
6
  import { allModes } from "../storybook/modes";
8
7
 
9
8
  export const meta = {
package/Index.svelte CHANGED
@@ -19,6 +19,7 @@
19
19
  Source,
20
20
  WebcamOptions
21
21
  } from "./shared/types";
22
+ import type { CommandNode } from "./shared/core/commands";
22
23
 
23
24
  export let elem_id = "";
24
25
  export let elem_classes: string[] = [];
@@ -57,7 +58,7 @@
57
58
  export let canvas_size: [number, number];
58
59
  export let fixed_canvas = false;
59
60
  export let show_fullscreen_button = true;
60
- export let full_history: any = null;
61
+ export let full_history: CommandNode | null = null;
61
62
  export let gradio: Gradio<{
62
63
  change: never;
63
64
  error: string;
@@ -199,7 +200,6 @@
199
200
 
200
201
  <InteractiveImageEditor
201
202
  {border_region}
202
- on:history={(e) => (full_history = e.detail)}
203
203
  bind:is_dragging
204
204
  {canvas_size}
205
205
  on:change={() => handle_history_change()}
@@ -242,7 +242,7 @@
242
242
  layer_options={layers}
243
243
  upload={(...args) => gradio.client.upload(...args)}
244
244
  {placeholder}
245
- {full_history}
245
+ bind:full_history
246
246
  {webcam_options}
247
247
  {show_download_button}
248
248
  {theme_mode}
@@ -223,7 +223,6 @@
223
223
  on:clear={() => dispatch("clear")}
224
224
  on:download_error
225
225
  {sources}
226
- {full_history}
227
226
  bind:background_image
228
227
  bind:current_tool
229
228
  brush_options={brush}
@@ -240,6 +239,7 @@
240
239
  {show_download_button}
241
240
  {theme_mode}
242
241
  bind:can_undo
242
+ bind:full_history
243
243
  >
244
244
  {#if current_tool === "image" && !can_undo}
245
245
  <div class="empty wrap">
package/dist/Index.svelte CHANGED
@@ -152,7 +152,6 @@ $:
152
152
 
153
153
  <InteractiveImageEditor
154
154
  {border_region}
155
- on:history={(e) => (full_history = e.detail)}
156
155
  bind:is_dragging
157
156
  {canvas_size}
158
157
  on:change={() => handle_history_change()}
@@ -195,7 +194,7 @@ $:
195
194
  layer_options={layers}
196
195
  upload={(...args) => gradio.client.upload(...args)}
197
196
  {placeholder}
198
- {full_history}
197
+ bind:full_history
199
198
  {webcam_options}
200
199
  {show_download_button}
201
200
  {theme_mode}
@@ -4,6 +4,7 @@ import type { EditorData, ImageBlobs } from "./InteractiveImageEditor.svelte";
4
4
  import type { Gradio, SelectData } from "@gradio/utils";
5
5
  import type { LoadingStatus } from "@gradio/statustracker";
6
6
  import type { LayerOptions, Transform, Source, WebcamOptions } from "./shared/types";
7
+ import type { CommandNode } from "./shared/core/commands";
7
8
  declare const __propDef: {
8
9
  props: {
9
10
  elem_id?: string | undefined;
@@ -37,7 +38,7 @@ declare const __propDef: {
37
38
  canvas_size: [number, number];
38
39
  fixed_canvas?: boolean | undefined;
39
40
  show_fullscreen_button?: boolean | undefined;
40
- full_history?: any;
41
+ full_history?: (CommandNode | null) | undefined;
41
42
  gradio: Gradio<{
42
43
  change: never;
43
44
  error: string;
@@ -163,9 +164,9 @@ export default class Index extends SvelteComponent<IndexProps, IndexEvents, Inde
163
164
  get show_fullscreen_button(): boolean | undefined;
164
165
  /**accessor*/
165
166
  set show_fullscreen_button(_: boolean | undefined);
166
- get full_history(): any;
167
+ get full_history(): CommandNode | null | undefined;
167
168
  /**accessor*/
168
- set full_history(_: any);
169
+ set full_history(_: CommandNode | null | undefined);
169
170
  get gradio(): Gradio<{
170
171
  change: never;
171
172
  error: string;
@@ -173,7 +173,6 @@ let current_tool;
173
173
  on:clear={() => dispatch("clear")}
174
174
  on:download_error
175
175
  {sources}
176
- {full_history}
177
176
  bind:background_image
178
177
  bind:current_tool
179
178
  brush_options={brush}
@@ -190,6 +189,7 @@ let current_tool;
190
189
  {show_download_button}
191
190
  {theme_mode}
192
191
  bind:can_undo
192
+ bind:full_history
193
193
  >
194
194
  {#if current_tool === "image" && !can_undo}
195
195
  <div class="empty wrap">
@@ -45,7 +45,7 @@ declare const __propDef: {
45
45
  image_id?: (null | string) | undefined;
46
46
  };
47
47
  events: {
48
- history: CustomEvent<import("svelte/store").Writable<CommandNode>>;
48
+ history: CustomEvent<any>;
49
49
  save: CustomEvent<void>;
50
50
  download_error: CustomEvent<string>;
51
51
  clear?: CustomEvent<undefined> | undefined;
@@ -2,6 +2,7 @@
2
2
  </script>
3
3
 
4
4
  <script>import { onMount, createEventDispatcher, tick } from "svelte";
5
+ import { get } from "svelte/store";
5
6
  import Toolbar, {} from "./Toolbar.svelte";
6
7
  import { CropTool } from "./crop/crop";
7
8
  import { ResizeTool } from "./resize/resize";
@@ -21,7 +22,6 @@ import IconButton from "./IconButton.svelte";
21
22
  const { drag, open_file_upload } = create_drag();
22
23
  const dispatch = createEventDispatcher();
23
24
  export const antialias = true;
24
- export const full_history = null;
25
25
  export let changeable = false;
26
26
  export let sources = ["upload", "webcam", "clipboard"];
27
27
  export let transforms = ["crop", "resize"];
@@ -43,6 +43,7 @@ export let current_tool;
43
43
  export let webcam_options;
44
44
  export let show_download_button = false;
45
45
  export let theme_mode;
46
+ export let full_history = null;
46
47
  let pixi_target;
47
48
  let pixi_target_crop;
48
49
  $:
@@ -53,9 +54,34 @@ $:
53
54
  }
54
55
  }
55
56
  function refresh_tools() {
57
+ if (!editor || !ready)
58
+ return;
56
59
  editor.set_tool(current_tool);
57
60
  editor.set_subtool(current_subtool);
58
61
  }
62
+ $:
63
+ if (editor && ready && editor.layers) {
64
+ const current_layers = get(editor.layers);
65
+ if (current_layers.layers.length > 0 && !current_layers.active_layer) {
66
+ refresh_tools_for_layer_changes(current_layers);
67
+ }
68
+ }
69
+ function refresh_tools_for_layer_changes(current_layers) {
70
+ if (!editor || !ready)
71
+ return;
72
+ if (current_layers.layers.length > 0 && !current_layers.active_layer) {
73
+ editor.set_layer(current_layers.layers[0].id);
74
+ }
75
+ if (current_tool) {
76
+ editor.set_tool(current_tool);
77
+ if (current_subtool) {
78
+ editor.set_subtool(current_subtool);
79
+ }
80
+ }
81
+ if (brush && (current_tool === "draw" || current_tool === "erase")) {
82
+ brush.set_tool(current_tool, current_subtool);
83
+ }
84
+ }
59
85
  function check_if_should_init() {
60
86
  return layer_options && editor && ready;
61
87
  }
@@ -148,7 +174,17 @@ onMount(() => {
148
174
  });
149
175
  intersection_observer.observe(pixi_target);
150
176
  resize_observer.observe(pixi_target);
177
+ setTimeout(() => {
178
+ if (full_history && editor) {
179
+ editor.command_manager.replay(full_history, editor.context).then(() => {
180
+ refresh_tools_after_history();
181
+ });
182
+ }
183
+ }, 0);
151
184
  });
185
+ if (typeof window !== "undefined") {
186
+ window.editor = editor;
187
+ }
152
188
  return () => {
153
189
  if (intersection_observer) {
154
190
  intersection_observer.disconnect();
@@ -161,6 +197,24 @@ onMount(() => {
161
197
  }
162
198
  };
163
199
  });
200
+ function refresh_tools_after_history() {
201
+ if (!editor || !ready)
202
+ return;
203
+ const current_layers = get(editor.layers);
204
+ if (current_layers.layers.length > 0 && !current_layers.active_layer) {
205
+ editor.set_layer(current_layers.layers[0].id);
206
+ }
207
+ if (current_tool) {
208
+ editor.set_tool(current_tool);
209
+ if (current_subtool) {
210
+ editor.set_subtool(current_subtool);
211
+ }
212
+ }
213
+ if (brush && (current_tool === "draw" || current_tool === "erase")) {
214
+ brush.set_tool(current_tool, current_subtool);
215
+ }
216
+ full_history = editor.command_manager.history;
217
+ }
164
218
  let crop;
165
219
  let crop_zoom;
166
220
  export let can_undo = false;
@@ -201,6 +255,10 @@ async function init_image_editor() {
201
255
  editor.dimensions.subscribe((dimensions) => {
202
256
  last_dimensions = { ...dimensions };
203
257
  });
258
+ editor.command_manager.current_history.subscribe((history) => {
259
+ can_undo = history.previous !== null;
260
+ can_redo = history.next !== null;
261
+ });
204
262
  await Promise.all([editor.ready, crop.ready]).then(() => {
205
263
  handle_tool_change({ tool: "image" });
206
264
  ready = true;
@@ -213,8 +271,7 @@ async function init_image_editor() {
213
271
  });
214
272
  editor.on("change", () => {
215
273
  dispatch("change");
216
- can_undo = editor.command_manager.history.previous !== null;
217
- can_redo = editor.command_manager.history.next !== null;
274
+ full_history = editor.command_manager.history;
218
275
  });
219
276
  if (background || layers.length > 0) {
220
277
  if (background) {
@@ -228,10 +285,10 @@ async function init_image_editor() {
228
285
  await add_image_from_url(composite);
229
286
  handle_tool_change({ tool: "draw" });
230
287
  }
288
+ refresh_tools();
231
289
  }
232
290
  $:
233
291
  if (background == null && layers.length == 0 && composite == null && editor && ready) {
234
- editor.reset_canvas();
235
292
  handle_tool_change({ tool: "image" });
236
293
  background_image = false;
237
294
  has_drawn = false;
@@ -243,6 +300,7 @@ async function handle_files(files) {
243
300
  return;
244
301
  if (!sources.includes("upload"))
245
302
  return;
303
+ editor.reset_canvas();
246
304
  const _file = Array.isArray(files) ? files[0] : files;
247
305
  await editor.add_image({ image: _file });
248
306
  await crop.add_image({ image: _file });
@@ -252,8 +310,6 @@ async function handle_files(files) {
252
310
  dispatch("upload");
253
311
  dispatch("input");
254
312
  dispatch("change");
255
- can_undo = editor.command_manager.history.previous !== null;
256
- can_redo = editor.command_manager.history.next !== null;
257
313
  }
258
314
  $:
259
315
  background_image = can_undo && editor.command_manager.contains("AddImage");
@@ -418,13 +474,9 @@ async function handle_download() {
418
474
  }
419
475
  function handle_undo() {
420
476
  editor.undo();
421
- can_undo = editor.command_manager.history.previous !== null;
422
- can_redo = editor.command_manager.history.next !== null;
423
477
  }
424
478
  function handle_redo() {
425
479
  editor.redo();
426
- can_undo = editor.command_manager.history.previous !== null;
427
- can_redo = editor.command_manager.history.next !== null;
428
480
  }
429
481
  </script>
430
482
 
@@ -11,7 +11,6 @@ import { type ImageBlobs } from "./types";
11
11
  declare const __propDef: {
12
12
  props: {
13
13
  antialias?: true | undefined;
14
- full_history?: (CommandNode | null) | undefined;
15
14
  changeable?: boolean | undefined;
16
15
  sources?: Source[] | undefined;
17
16
  transforms?: Transform[] | undefined;
@@ -33,6 +32,7 @@ declare const __propDef: {
33
32
  webcam_options: WebcamOptions;
34
33
  show_download_button?: boolean | undefined;
35
34
  theme_mode: "dark" | "light";
35
+ full_history?: (CommandNode | null) | undefined;
36
36
  has_drawn?: boolean | undefined;
37
37
  get_blobs?: (() => Promise<ImageBlobs>) | undefined;
38
38
  add_image?: ((image: Blob | File) => void) | undefined;
@@ -51,7 +51,6 @@ declare const __propDef: {
51
51
  clear?: CustomEvent<undefined> | undefined;
52
52
  save: CustomEvent<void>;
53
53
  change: CustomEvent<void>;
54
- history: CustomEvent<import("svelte/store").Writable<CommandNode>>;
55
54
  upload: CustomEvent<void>;
56
55
  input: CustomEvent<void>;
57
56
  download_error: CustomEvent<string>;
@@ -67,7 +66,6 @@ export type ImageEditorEvents = typeof __propDef.events;
67
66
  export type ImageEditorSlots = typeof __propDef.slots;
68
67
  export default class ImageEditor extends SvelteComponent<ImageEditorProps, ImageEditorEvents, ImageEditorSlots> {
69
68
  get antialias(): true;
70
- get full_history(): CommandNode;
71
69
  get get_blobs(): () => Promise<ImageBlobs>;
72
70
  get add_image(): (image: Blob | File) => void;
73
71
  get add_image_from_url(): (source: string | {
@@ -1,18 +1,46 @@
1
1
  import { Application, Texture } from "pixi.js";
2
2
  import { type ImageEditorContext } from "../core/editor";
3
3
  import { type Command } from "../core/commands";
4
+ /**
5
+ * Represents a single drawing segment with all its parameters
6
+ */
7
+ interface BrushSegment {
8
+ from_x: number;
9
+ from_y: number;
10
+ to_x: number;
11
+ to_y: number;
12
+ size: number;
13
+ color: string;
14
+ opacity: number;
15
+ mode: "draw" | "erase";
16
+ }
17
+ /**
18
+ * Represents a complete brush stroke containing multiple segments
19
+ */
20
+ interface BrushStroke {
21
+ segments: BrushSegment[];
22
+ layer_id: string;
23
+ }
4
24
  export declare class BrushCommand implements Command {
25
+ private stroke_data;
5
26
  private context;
6
- private layer_id;
7
27
  private original_texture;
8
- private final_texture;
9
28
  name: string;
10
- constructor(context: ImageEditorContext, layer_id: string, original_texture: Texture, final_texture: Texture);
29
+ constructor(context: ImageEditorContext, stroke_data: BrushStroke, original_texture?: Texture);
11
30
  /**
12
31
  * Creates a new texture with the same content as the source texture
13
32
  */
14
- private createTextureFrom;
15
- execute(): Promise<void>;
33
+ private create_texture_from;
34
+ /**
35
+ * Recreates the stroke by rendering all segments from the stored parameters
36
+ * Preserves the exact order of draw/erase operations
37
+ */
38
+ private render_stroke_from_data;
39
+ /**
40
+ * Renders a segment to a graphics object (extracted from renderSegment)
41
+ */
42
+ private render_segment_to_graphics;
43
+ execute(context: ImageEditorContext): Promise<void>;
16
44
  undo(): Promise<void>;
17
45
  }
18
46
  /**
@@ -20,8 +48,6 @@ export declare class BrushCommand implements Command {
20
48
  * Simplified to work directly with layer textures.
21
49
  */
22
50
  export declare class BrushTextures {
23
- private image_editor_context;
24
- private app;
25
51
  private stroke_texture;
26
52
  private erase_texture;
27
53
  private display_container;
@@ -30,11 +56,13 @@ export declare class BrushTextures {
30
56
  private preview_sprite;
31
57
  private erase_graphics;
32
58
  private dimensions;
59
+ private image_editor_context;
60
+ private app;
33
61
  private is_new_stroke;
34
62
  private current_opacity;
35
- private current_mode;
36
63
  private original_layer_texture;
37
64
  private active_layer_id;
65
+ private current_stroke_segments;
38
66
  constructor(image_editor_context: ImageEditorContext, app: Application);
39
67
  /**
40
68
  * Initializes textures needed for the brush tool.
@@ -63,7 +91,7 @@ export declare class BrushTextures {
63
91
  /**
64
92
  * Calculates the distance between two points.
65
93
  */
66
- private calculateDistance;
94
+ private calculate_distance;
67
95
  /**
68
96
  * Draws a segment for the current stroke using a simple "stamp" approach.
69
97
  */
@@ -75,8 +103,13 @@ export declare class BrushTextures {
75
103
  width: number;
76
104
  height: number;
77
105
  };
106
+ /**
107
+ * Checks if textures are properly initialized.
108
+ */
109
+ get textures_initialized(): boolean;
78
110
  /**
79
111
  * Cleanup all resources.
80
112
  */
81
113
  cleanup(): void;
82
114
  }
115
+ export {};