@pixerate/editor-svelte 0.162.1 → 0.163.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.
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
import { untrack } from 'svelte';
|
|
25
25
|
import LayerEffectsPanel from './LayerEffectsPanel.svelte';
|
|
26
26
|
import EdgeColorPanel from './EdgeColorPanel.svelte';
|
|
27
|
+
import VectorMaskEdgesPanel from './VectorMaskEdgesPanel.svelte';
|
|
27
28
|
|
|
28
29
|
interface Props {
|
|
29
30
|
controller: ImageEditorController;
|
|
@@ -289,6 +290,9 @@
|
|
|
289
290
|
<EdgeColorPanel {controller} layer={snapshot.active} class="border-t border-border pt-1.5" />
|
|
290
291
|
{/key}
|
|
291
292
|
{/if}
|
|
293
|
+
{#if snapshot.active?.vectorMask}
|
|
294
|
+
<VectorMaskEdgesPanel {controller} layer={snapshot.active} class="border-t border-border pt-1.5" />
|
|
295
|
+
{/if}
|
|
292
296
|
|
|
293
297
|
{#if effects && snapshot.active && snapshot.active.type !== 'adjustment'}
|
|
294
298
|
<LayerEffectsPanel {controller} class="border-t border-border pt-1.5" />
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ImageEditorController, ImageLayer, VectorMaskEdge } from '@pixerate/editor/image-editor';
|
|
3
|
+
import { untrack } from 'svelte';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hybrid mask edges: the soft segments of a layer's vector mask (feather or matte), with their
|
|
7
|
+
* settings, as `set_vector_mask_edge` sets them. Alt+Shift-click a segment with Direct Selection
|
|
8
|
+
* to cycle its mode on the canvas. Every change is one undo step.
|
|
9
|
+
*/
|
|
10
|
+
interface Props {
|
|
11
|
+
controller: ImageEditorController;
|
|
12
|
+
/** The layer whose vector mask to edit. */
|
|
13
|
+
layer: ImageLayer;
|
|
14
|
+
class?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let { controller, layer, class: className = '' }: Props = $props();
|
|
18
|
+
|
|
19
|
+
const MODES = ['hard', 'feather', 'matte'] as const;
|
|
20
|
+
const input = 'w-12 bg-background border border-border rounded px-1 py-0.5 text-foreground';
|
|
21
|
+
let tick = $state(0);
|
|
22
|
+
$effect(() => {
|
|
23
|
+
const c = controller;
|
|
24
|
+
return untrack(() => c.subscribe(() => tick++));
|
|
25
|
+
});
|
|
26
|
+
let error = $state<string | null>(null);
|
|
27
|
+
const info = $derived.by(() => {
|
|
28
|
+
void tick;
|
|
29
|
+
return controller.getVectorMask(layer.id);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
function set(e: VectorMaskEdge, mode: VectorMaskEdge['mode'], options: { radius?: number; inner?: number; outer?: number } = {}) {
|
|
33
|
+
error = null;
|
|
34
|
+
try {
|
|
35
|
+
controller.setVectorMaskEdge(layer.id, { component: e.component, subpath: e.subpath, segment: e.segment }, mode, options);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
error = err instanceof Error ? err.message : String(err);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const num = (v: string, fallback: number) => (Number.isFinite(Number(v)) && Number(v) > 0 ? Number(v) : fallback);
|
|
41
|
+
const value = (ev: Event) => (ev.currentTarget as HTMLInputElement | HTMLSelectElement).value;
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
{#if info}
|
|
45
|
+
<div class="flex flex-col gap-1.5 text-xs {className}" data-testid="vector-mask-edges">
|
|
46
|
+
<span class="text-muted-foreground">Vector mask edges</span>
|
|
47
|
+
<p class="text-muted-foreground">Alt+Shift-click a segment with Direct Selection to make it feathered or a hair matte.</p>
|
|
48
|
+
{#if info.edges.length === 0}
|
|
49
|
+
<p class="text-muted-foreground">Every segment is hard.</p>
|
|
50
|
+
{/if}
|
|
51
|
+
<ul class="flex flex-col gap-1">
|
|
52
|
+
{#each info.edges as e (`${e.component}:${e.subpath}:${e.segment}`)}
|
|
53
|
+
<li data-testid="vector-mask-edge" class="flex flex-wrap items-center gap-1.5">
|
|
54
|
+
<span class="font-mono text-muted-foreground">{e.component}/{e.subpath}/{e.segment}</span>
|
|
55
|
+
<select aria-label="Edge mode" value={e.mode} onchange={(ev) => set(e, value(ev) as VectorMaskEdge['mode'])} class="bg-background border border-border rounded px-1 py-0.5 text-foreground">
|
|
56
|
+
{#each MODES as m (m)}
|
|
57
|
+
<option value={m}>{m}</option>
|
|
58
|
+
{/each}
|
|
59
|
+
</select>
|
|
60
|
+
{#if e.mode === 'feather'}
|
|
61
|
+
<label class="flex items-center gap-1 text-muted-foreground">
|
|
62
|
+
radius
|
|
63
|
+
<input type="number" min="0.5" max="250" step="0.5" aria-label="Edge feather radius" value={e.radius} oninput={(ev) => set(e, 'feather', { radius: num(value(ev), 4) })} class={input} />
|
|
64
|
+
</label>
|
|
65
|
+
{/if}
|
|
66
|
+
{#if e.mode === 'matte'}
|
|
67
|
+
<label class="flex items-center gap-1 text-muted-foreground">
|
|
68
|
+
in
|
|
69
|
+
<input type="number" min="0.5" max="200" step="0.5" aria-label="Edge matte inner" value={e.inner} oninput={(ev) => set(e, 'matte', { inner: num(value(ev), 4), outer: e.outer })} class={input} />
|
|
70
|
+
</label>
|
|
71
|
+
<label class="flex items-center gap-1 text-muted-foreground">
|
|
72
|
+
out
|
|
73
|
+
<input type="number" min="0.5" max="200" step="0.5" aria-label="Edge matte outer" value={e.outer} oninput={(ev) => set(e, 'matte', { inner: e.inner, outer: num(value(ev), 8) })} class={input} />
|
|
74
|
+
</label>
|
|
75
|
+
{/if}
|
|
76
|
+
</li>
|
|
77
|
+
{/each}
|
|
78
|
+
</ul>
|
|
79
|
+
{#if error}
|
|
80
|
+
<p role="alert" class="text-destructive">{error}</p>
|
|
81
|
+
{/if}
|
|
82
|
+
</div>
|
|
83
|
+
{/if}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ImageEditorController, ImageLayer } from '@pixerate/editor/image-editor';
|
|
2
|
+
/**
|
|
3
|
+
* Hybrid mask edges: the soft segments of a layer's vector mask (feather or matte), with their
|
|
4
|
+
* settings, as `set_vector_mask_edge` sets them. Alt+Shift-click a segment with Direct Selection
|
|
5
|
+
* to cycle its mode on the canvas. Every change is one undo step.
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
controller: ImageEditorController;
|
|
9
|
+
/** The layer whose vector mask to edit. */
|
|
10
|
+
layer: ImageLayer;
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
declare const VectorMaskEdgesPanel: import("svelte").Component<Props, {}, "">;
|
|
14
|
+
type VectorMaskEdgesPanel = ReturnType<typeof VectorMaskEdgesPanel>;
|
|
15
|
+
export default VectorMaskEdgesPanel;
|
|
@@ -19,6 +19,7 @@ import type LayersPanelComponent from "./LayersPanel.svelte";
|
|
|
19
19
|
import type ColorProfilePanelComponent from "./ColorProfilePanel.svelte";
|
|
20
20
|
import type LayerEffectsPanelComponent from "./LayerEffectsPanel.svelte";
|
|
21
21
|
import type EdgeColorPanelComponent from "./EdgeColorPanel.svelte";
|
|
22
|
+
import type VectorMaskEdgesPanelComponent from "./VectorMaskEdgesPanel.svelte";
|
|
22
23
|
import type BrushOptionsBarComponent from "./BrushOptionsBar.svelte";
|
|
23
24
|
import type FreeTransformOptionsBarComponent from "./FreeTransformOptionsBar.svelte";
|
|
24
25
|
import type ShapeOptionsBarComponent from "./ShapeOptionsBar.svelte";
|
|
@@ -50,6 +51,7 @@ export { default as LayersPanel } from './LayersPanel.svelte';
|
|
|
50
51
|
export { default as ColorProfilePanel } from './ColorProfilePanel.svelte';
|
|
51
52
|
export { default as LayerEffectsPanel } from './LayerEffectsPanel.svelte';
|
|
52
53
|
export { default as EdgeColorPanel } from './EdgeColorPanel.svelte';
|
|
54
|
+
export { default as VectorMaskEdgesPanel } from './VectorMaskEdgesPanel.svelte';
|
|
53
55
|
export { default as BrushOptionsBar } from './BrushOptionsBar.svelte';
|
|
54
56
|
export { default as FreeTransformOptionsBar } from './FreeTransformOptionsBar.svelte';
|
|
55
57
|
export { default as ShapeOptionsBar } from './ShapeOptionsBar.svelte';
|
|
@@ -83,6 +85,7 @@ export type LayersPanelProps = ComponentProps<typeof LayersPanelComponent>;
|
|
|
83
85
|
export type ColorProfilePanelProps = ComponentProps<typeof ColorProfilePanelComponent>;
|
|
84
86
|
export type LayerEffectsPanelProps = ComponentProps<typeof LayerEffectsPanelComponent>;
|
|
85
87
|
export type EdgeColorPanelProps = ComponentProps<typeof EdgeColorPanelComponent>;
|
|
88
|
+
export type VectorMaskEdgesPanelProps = ComponentProps<typeof VectorMaskEdgesPanelComponent>;
|
|
86
89
|
export type BrushOptionsBarProps = ComponentProps<typeof BrushOptionsBarComponent>;
|
|
87
90
|
export type FreeTransformOptionsBarProps = ComponentProps<typeof FreeTransformOptionsBarComponent>;
|
|
88
91
|
export type ShapeOptionsBarProps = ComponentProps<typeof ShapeOptionsBarComponent>;
|
|
@@ -21,6 +21,7 @@ export { default as LayersPanel } from './LayersPanel.svelte';
|
|
|
21
21
|
export { default as ColorProfilePanel } from './ColorProfilePanel.svelte';
|
|
22
22
|
export { default as LayerEffectsPanel } from './LayerEffectsPanel.svelte';
|
|
23
23
|
export { default as EdgeColorPanel } from './EdgeColorPanel.svelte';
|
|
24
|
+
export { default as VectorMaskEdgesPanel } from './VectorMaskEdgesPanel.svelte';
|
|
24
25
|
export { default as BrushOptionsBar } from './BrushOptionsBar.svelte';
|
|
25
26
|
export { default as FreeTransformOptionsBar } from './FreeTransformOptionsBar.svelte';
|
|
26
27
|
export { default as ShapeOptionsBar } from './ShapeOptionsBar.svelte';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixerate/editor-svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.163.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.220.0"
|
|
169
169
|
},
|
|
170
170
|
"peerDependencies": {
|
|
171
171
|
"@dagrejs/dagre": "^1.1.4",
|