@pixerate/editor-svelte 0.145.0 → 0.147.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.
@@ -109,14 +109,35 @@
109
109
  let subject = $state('');
110
110
  let segmenting = $state(false);
111
111
  let subjectStatus = $state<string | null>(null);
112
+ let focusing = $state(false);
113
+ let focusStatus = $state<string | null>(null);
114
+ async function selectFocusArea() {
115
+ if (focusing) return;
116
+ focusing = true;
117
+ focusStatus = null;
118
+ try {
119
+ const { selection } = await controller.selectFocusArea({
120
+ range: options.focusRange ?? 50,
121
+ noise: options.focusNoise ?? 0,
122
+ softness: options.focusSoftness ?? 4,
123
+ mode: options.mode,
124
+ });
125
+ focusStatus = selection ? null : 'No part of the image stands out as in focus.';
126
+ } catch (err) {
127
+ focusStatus = err instanceof Error ? err.message : String(err);
128
+ } finally {
129
+ focusing = false;
130
+ }
131
+ }
112
132
  async function selectSubject() {
113
133
  const target = subject.trim();
114
- if (!target || segmenting) return;
134
+ if ((canSegment && !target) || segmenting) return;
115
135
  segmenting = true;
116
136
  subjectStatus = null;
117
137
  try {
118
- const { segments } = await controller.selectSubject(target, { mode: options.mode });
119
- subjectStatus = segments.length ? null : `Nothing matching “${target}” was found.`;
138
+ const { segments, method, note } = await controller.selectSubject(canSegment ? target : '', { mode: options.mode });
139
+ // Without a model, say only when nothing stood out.
140
+ subjectStatus = segments.length ? null : method === 'saliency' ? (note ?? 'Nothing stands out from the background.') : `Nothing matching “${target}” was found.`;
120
141
  } catch (err) {
121
142
  subjectStatus = err instanceof Error ? err.message : String(err);
122
143
  } finally {
@@ -357,16 +378,16 @@
357
378
  />
358
379
  px
359
380
  </label>
360
- {#if canSegment}
361
- <form
362
- data-testid="select-subject"
363
- class="flex flex-col gap-1"
364
- onsubmit={(e) => {
365
- e.preventDefault();
366
- void selectSubject();
367
- }}
368
- >
369
- <div class="flex gap-1">
381
+ <form
382
+ data-testid="select-subject"
383
+ class="flex flex-col gap-1"
384
+ onsubmit={(e) => {
385
+ e.preventDefault();
386
+ void selectSubject();
387
+ }}
388
+ >
389
+ <div class="flex gap-1">
390
+ {#if canSegment}
370
391
  <input
371
392
  type="text"
372
393
  aria-label="Subject to select"
@@ -375,15 +396,53 @@
375
396
  disabled={segmenting}
376
397
  class="min-w-0 flex-1 bg-background border border-border rounded px-2 py-1 text-foreground"
377
398
  />
378
- <button type="submit" class={btn(false)} disabled={segmenting || !subject.trim()} aria-busy={segmenting}>
379
- {segmenting ? 'Selecting…' : 'Select Subject'}
380
- </button>
381
- </div>
382
- {#if subjectStatus}
383
- <p role="status" class="text-muted-foreground">{subjectStatus}</p>
384
399
  {/if}
385
- </form>
386
- {/if}
400
+ <button
401
+ type="submit"
402
+ class={btn(false)}
403
+ disabled={segmenting || (canSegment && !subject.trim())}
404
+ aria-busy={segmenting}
405
+ title={canSegment ? undefined : 'Selects the subject that stands out most (no segmentation model is set)'}
406
+ >
407
+ {segmenting ? 'Selecting…' : 'Select Subject'}
408
+ </button>
409
+ </div>
410
+ {#if subjectStatus}
411
+ <p role="status" class="text-muted-foreground">{subjectStatus}</p>
412
+ {/if}
413
+ </form>
414
+ <details data-testid="focus-area" class="flex flex-col gap-2">
415
+ <summary class="cursor-pointer text-foreground">Focus Area</summary>
416
+ <div class="flex flex-col gap-2 pt-2">
417
+ <label class="flex flex-col gap-1">
418
+ <span class="flex justify-between text-muted-foreground">
419
+ <span>In-focus range</span>
420
+ <span class="font-mono">{options.focusRange ?? 50}</span>
421
+ </span>
422
+ <ImageRange label="In-focus range" min={0} max={100} step={1} value={options.focusRange ?? 50} oninput={(v) => set({ focusRange: v })} />
423
+ </label>
424
+ <label class="flex flex-col gap-1">
425
+ <span class="flex justify-between text-muted-foreground">
426
+ <span>Image noise level</span>
427
+ <span class="font-mono">{options.focusNoise ?? 0}</span>
428
+ </span>
429
+ <ImageRange label="Image noise level" min={0} max={100} step={1} value={options.focusNoise ?? 0} oninput={(v) => set({ focusNoise: v })} />
430
+ </label>
431
+ <label class="flex flex-col gap-1">
432
+ <span class="flex justify-between text-muted-foreground">
433
+ <span>Soften edge</span>
434
+ <span class="font-mono">{options.focusSoftness ?? 4} px</span>
435
+ </span>
436
+ <ImageRange label="Focus soften edge" min={0} max={50} step={1} value={options.focusSoftness ?? 4} oninput={(v) => set({ focusSoftness: v })} />
437
+ </label>
438
+ <button type="button" class={btn(false)} disabled={focusing} aria-busy={focusing} onclick={() => void selectFocusArea()}>
439
+ {focusing ? 'Selecting…' : 'Select Focus Area'}
440
+ </button>
441
+ {#if focusStatus}
442
+ <p role="status" class="text-muted-foreground">{focusStatus}</p>
443
+ {/if}
444
+ </div>
445
+ </details>
387
446
  <label class="flex items-center gap-2 text-foreground">
388
447
  <ImageCheckbox label="Anti-alias" checked={options.antiAlias} onchange={(on) => set({ antiAlias: on })} />
389
448
  Anti-alias
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor-svelte",
3
- "version": "0.145.0",
3
+ "version": "0.147.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.198.0"
168
+ "@pixerate/editor": "0.200.0"
169
169
  },
170
170
  "peerDependencies": {
171
171
  "@dagrejs/dagre": "^1.1.4",