@pixerate/editor-svelte 0.151.0 → 0.152.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.
@@ -57,6 +57,10 @@
57
57
  void tick;
58
58
  return session.note;
59
59
  });
60
+ const computing = $derived.by(() => {
61
+ void tick;
62
+ return session.computing;
63
+ });
60
64
 
61
65
  // Draw the preview whenever it changes.
62
66
  $effect(() => {
@@ -168,6 +172,9 @@
168
172
  <span class="text-muted-foreground">{edits} strokes</span>
169
173
  <button type="button" class={button} disabled={!edits} onclick={() => session.undoEdit()}>Undo Stroke</button>
170
174
  <span class="font-mono text-muted-foreground">{Math.round(zoom * 100)}%</span>
175
+ {#if computing}
176
+ <span role="status" class="text-muted-foreground">Updating preview…</span>
177
+ {/if}
171
178
  </div>
172
179
  <div class="flex-1 overflow-hidden flex items-center justify-center bg-muted/30">
173
180
  <canvas
@@ -107,6 +107,20 @@
107
107
  void tick;
108
108
  return Boolean(controller.segmentationProvider);
109
109
  });
110
+ // The long selection running now (Select Subject, Sky, Focus Area): its progress and Cancel.
111
+ let task = $state<{ stage: string; fraction: number } | null>(null);
112
+ let taskAbort: AbortController | null = null;
113
+ function tracked() {
114
+ const abort = new AbortController();
115
+ taskAbort = abort;
116
+ task = { stage: 'Starting', fraction: 0 };
117
+ return { signal: abort.signal, onProgress: (fraction: number, stage: string) => (task = { stage, fraction }) };
118
+ }
119
+ function untracked() {
120
+ taskAbort = null;
121
+ task = null;
122
+ }
123
+ const failure = (err: unknown) => ((err as { name?: string })?.name === 'AbortError' ? 'Cancelled.' : err instanceof Error ? err.message : String(err));
110
124
  let subject = $state('');
111
125
  let segmenting = $state(false);
112
126
  let subjectStatus = $state<string | null>(null);
@@ -118,11 +132,12 @@
118
132
  skying = true;
119
133
  skyStatus = null;
120
134
  try {
121
- const { note } = await controller.selectSky({ mode: options.mode });
135
+ const { note } = await controller.selectSky({ mode: options.mode, ...tracked() });
122
136
  skyStatus = note ?? null;
123
137
  } catch (err) {
124
- skyStatus = err instanceof Error ? err.message : String(err);
138
+ skyStatus = failure(err);
125
139
  } finally {
140
+ untracked();
126
141
  skying = false;
127
142
  }
128
143
  }
@@ -138,11 +153,13 @@
138
153
  noise: options.focusNoise ?? 0,
139
154
  softness: options.focusSoftness ?? 4,
140
155
  mode: options.mode,
156
+ ...tracked(),
141
157
  });
142
158
  focusStatus = selection ? null : 'No part of the image stands out as in focus.';
143
159
  } catch (err) {
144
- focusStatus = err instanceof Error ? err.message : String(err);
160
+ focusStatus = failure(err);
145
161
  } finally {
162
+ untracked();
146
163
  focusing = false;
147
164
  }
148
165
  }
@@ -152,12 +169,13 @@
152
169
  segmenting = true;
153
170
  subjectStatus = null;
154
171
  try {
155
- const { segments, method, note } = await controller.selectSubject(canSegment ? target : '', { mode: options.mode });
172
+ const { segments, method, note } = await controller.selectSubject(canSegment ? target : '', { mode: options.mode, ...tracked() });
156
173
  // Without a model, say only when nothing stood out.
157
174
  subjectStatus = segments.length ? null : method === 'saliency' ? (note ?? 'Nothing stands out from the background.') : `Nothing matching “${target}” was found.`;
158
175
  } catch (err) {
159
- subjectStatus = err instanceof Error ? err.message : String(err);
176
+ subjectStatus = failure(err);
160
177
  } finally {
178
+ untracked();
161
179
  segmenting = false;
162
180
  }
163
181
  }
@@ -428,6 +446,22 @@
428
446
  <p role="status" class="text-muted-foreground">{subjectStatus}</p>
429
447
  {/if}
430
448
  </form>
449
+ {#if task}
450
+ <div data-testid="selection-progress" class="flex items-center gap-2">
451
+ <div
452
+ role="progressbar"
453
+ aria-label={task.stage}
454
+ aria-valuemin={0}
455
+ aria-valuemax={100}
456
+ aria-valuenow={Math.round(task.fraction * 100)}
457
+ class="flex-1 h-1.5 rounded bg-muted overflow-hidden"
458
+ >
459
+ <div class="h-full bg-primary" style:width="{Math.round(task.fraction * 100)}%"></div>
460
+ </div>
461
+ <span class="text-muted-foreground">{task.stage}</span>
462
+ <button type="button" class={btn(false)} onclick={() => taskAbort?.abort()}>Cancel</button>
463
+ </div>
464
+ {/if}
431
465
  <button type="button" class={btn(false)} onclick={() => (workspace = true)}>Select and Mask…</button>
432
466
  {#if workspace}
433
467
  <SelectAndMaskWorkspace {controller} onclose={() => (workspace = false)} />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor-svelte",
3
- "version": "0.151.0",
3
+ "version": "0.152.0",
4
4
  "description": "Svelte bindings and prompt/rich-text components for @pixerate/editor",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -165,7 +165,7 @@
165
165
  "**/*.css"
166
166
  ],
167
167
  "dependencies": {
168
- "@pixerate/editor": "0.207.0"
168
+ "@pixerate/editor": "0.208.0"
169
169
  },
170
170
  "peerDependencies": {
171
171
  "@dagrejs/dagre": "^1.1.4",