@pixerate/editor-svelte 0.131.0 → 0.133.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.
|
@@ -18,9 +18,11 @@
|
|
|
18
18
|
FreeTransformTool,
|
|
19
19
|
boxFrameHit,
|
|
20
20
|
quadFrameOverlay,
|
|
21
|
+
TRANSFORM_ROTATE_HANDLE_OFFSET,
|
|
21
22
|
getViewportProjection,
|
|
22
23
|
measureCanvasViewport,
|
|
23
24
|
watchCanvasViewport,
|
|
25
|
+
startMarchingAnts,
|
|
24
26
|
ShapeDrawGesture,
|
|
25
27
|
TypeToolGesture,
|
|
26
28
|
DEFAULT_SHAPE_TOOL_OPTIONS,
|
|
@@ -371,7 +373,8 @@
|
|
|
371
373
|
if (!layer || layer.visible === false) return null;
|
|
372
374
|
const corners = c.getLayerFrame(layer.id);
|
|
373
375
|
if (!corners) return null;
|
|
374
|
-
|
|
376
|
+
const { scale } = getViewportProjection(c.getState(), view.width, view.height, false);
|
|
377
|
+
return quadFrameOverlay(corners, !(layer.locks?.all || layer.locks?.position), TRANSFORM_ROTATE_HANDLE_OFFSET / (scale || 1));
|
|
375
378
|
}
|
|
376
379
|
|
|
377
380
|
/**
|
|
@@ -499,10 +502,23 @@
|
|
|
499
502
|
);
|
|
500
503
|
});
|
|
501
504
|
|
|
505
|
+
// Marching ants: while a selection shows as ants, step the dash offset and redraw.
|
|
506
|
+
let antsOffset = 0;
|
|
507
|
+
const marching = $derived(!!editor.state.selection?.bounds && !editor.state.quickMask);
|
|
508
|
+
$effect(() => {
|
|
509
|
+
if (!marching) return;
|
|
510
|
+
return startMarchingAnts((offset) => {
|
|
511
|
+
antsOffset = offset;
|
|
512
|
+
void renderCanvas();
|
|
513
|
+
});
|
|
514
|
+
});
|
|
515
|
+
|
|
502
516
|
async function renderCanvas(overrideAnnotation?: Annotation | null, overrideCrop?: CropBox | null) {
|
|
503
517
|
if (!canvasEl || !containerEl) return;
|
|
504
518
|
const viewport = measureCanvasViewport(containerEl);
|
|
505
519
|
view = { width: viewport.width, height: viewport.height };
|
|
520
|
+
// The transform overlay's rotate handle sits a fixed distance out on screen.
|
|
521
|
+
transformTool.setView(getViewportProjection(editor.controller.getState(), view.width, view.height, mode === 'crop').scale);
|
|
506
522
|
|
|
507
523
|
await editor.controller.render(canvasEl, {
|
|
508
524
|
viewportWidth: viewport.width,
|
|
@@ -512,6 +528,7 @@
|
|
|
512
528
|
fit,
|
|
513
529
|
showCropOverlay: mode === 'crop',
|
|
514
530
|
showSelectionOverlay: true,
|
|
531
|
+
antsOffset,
|
|
515
532
|
interactive: true,
|
|
516
533
|
draftAnnotation: overrideAnnotation !== undefined ? overrideAnnotation : draftAnnotation,
|
|
517
534
|
draftCrop: overrideCrop !== undefined ? overrideCrop : draftCrop,
|
|
@@ -45,6 +45,29 @@
|
|
|
45
45
|
|
|
46
46
|
let radius = $state(2);
|
|
47
47
|
|
|
48
|
+
// Select › Subject (only with a segmentation provider).
|
|
49
|
+
const canSegment = $derived.by(() => {
|
|
50
|
+
void tick;
|
|
51
|
+
return Boolean(controller.segmentationProvider);
|
|
52
|
+
});
|
|
53
|
+
let subject = $state('');
|
|
54
|
+
let segmenting = $state(false);
|
|
55
|
+
let subjectStatus = $state<string | null>(null);
|
|
56
|
+
async function selectSubject() {
|
|
57
|
+
const target = subject.trim();
|
|
58
|
+
if (!target || segmenting) return;
|
|
59
|
+
segmenting = true;
|
|
60
|
+
subjectStatus = null;
|
|
61
|
+
try {
|
|
62
|
+
const { segments } = await controller.selectSubject(target, { mode: options.mode });
|
|
63
|
+
subjectStatus = segments.length ? null : `Nothing matching “${target}” was found.`;
|
|
64
|
+
} catch (err) {
|
|
65
|
+
subjectStatus = err instanceof Error ? err.message : String(err);
|
|
66
|
+
} finally {
|
|
67
|
+
segmenting = false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
48
71
|
const MODIFY_OPS: { op: SelectionModifyOp; label: string }[] = [
|
|
49
72
|
{ op: 'grow', label: 'Grow' },
|
|
50
73
|
{ op: 'shrink', label: 'Shrink' },
|
|
@@ -109,6 +132,33 @@
|
|
|
109
132
|
</label>
|
|
110
133
|
</div>
|
|
111
134
|
{/if}
|
|
135
|
+
{#if canSegment}
|
|
136
|
+
<form
|
|
137
|
+
data-testid="select-subject"
|
|
138
|
+
class="flex flex-col gap-1"
|
|
139
|
+
onsubmit={(e) => {
|
|
140
|
+
e.preventDefault();
|
|
141
|
+
void selectSubject();
|
|
142
|
+
}}
|
|
143
|
+
>
|
|
144
|
+
<div class="flex gap-1">
|
|
145
|
+
<input
|
|
146
|
+
type="text"
|
|
147
|
+
aria-label="Subject to select"
|
|
148
|
+
placeholder="Subject, e.g. the dog"
|
|
149
|
+
bind:value={subject}
|
|
150
|
+
disabled={segmenting}
|
|
151
|
+
class="min-w-0 flex-1 bg-background border border-border rounded px-2 py-1 text-foreground"
|
|
152
|
+
/>
|
|
153
|
+
<button type="submit" class={btn(false)} disabled={segmenting || !subject.trim()} aria-busy={segmenting}>
|
|
154
|
+
{segmenting ? 'Selecting…' : 'Select Subject'}
|
|
155
|
+
</button>
|
|
156
|
+
</div>
|
|
157
|
+
{#if subjectStatus}
|
|
158
|
+
<p role="status" class="text-muted-foreground">{subjectStatus}</p>
|
|
159
|
+
{/if}
|
|
160
|
+
</form>
|
|
161
|
+
{/if}
|
|
112
162
|
<label class="flex items-center gap-2 text-foreground">
|
|
113
163
|
<ImageCheckbox label="Anti-alias" checked={options.antiAlias} onchange={(on) => set({ antiAlias: on })} />
|
|
114
164
|
Anti-alias
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixerate/editor-svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.133.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.
|
|
168
|
+
"@pixerate/editor": "0.180.0"
|
|
169
169
|
},
|
|
170
170
|
"peerDependencies": {
|
|
171
171
|
"@dagrejs/dagre": "^1.1.4",
|