@pixerate/editor-svelte 0.202.1 → 0.203.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.
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
handlePaintShortcut,
|
|
10
10
|
viewCssTransform,
|
|
11
11
|
type PaintEditorController,
|
|
12
|
+
type FillRegionOptions,
|
|
12
13
|
type PaintPresence,
|
|
13
14
|
type PaintProposals,
|
|
14
15
|
type PaintAgentStrokes,
|
|
@@ -26,6 +27,8 @@
|
|
|
26
27
|
controller: PaintEditorController;
|
|
27
28
|
/** The active tool (default brush). */
|
|
28
29
|
tool?: PaintTool;
|
|
30
|
+
/** Options for the Fill tool (`tool === 'fill'`). */
|
|
31
|
+
fillOptions?: Omit<FillRegionOptions, 'x' | 'y' | 'points'>;
|
|
29
32
|
/** Draw the symmetry axes and perspective lines (default true). */
|
|
30
33
|
showGuides?: boolean;
|
|
31
34
|
/** Fit the canvas in the viewport on mount (default true). */
|
|
@@ -40,7 +43,7 @@
|
|
|
40
43
|
agentStrokes?: PaintAgentStrokes;
|
|
41
44
|
class?: string;
|
|
42
45
|
}
|
|
43
|
-
let { controller, tool = 'brush', showGuides = true, fitOnMount = true, onerror, presence, proposals, agentStrokes, class: className = '' }: Props = $props();
|
|
46
|
+
let { controller, tool = 'brush', fillOptions, showGuides = true, fitOnMount = true, onerror, presence, proposals, agentStrokes, class: className = '' }: Props = $props();
|
|
44
47
|
let agentLines = $state.raw<PaintAgentStrokesState>({ strokes: [] });
|
|
45
48
|
$effect(() => {
|
|
46
49
|
const store = agentStrokes;
|
|
@@ -71,6 +74,7 @@
|
|
|
71
74
|
);
|
|
72
75
|
$effect(() => {
|
|
73
76
|
gesture.tool = tool;
|
|
77
|
+
if (fillOptions) gesture.fillOptions = fillOptions;
|
|
74
78
|
});
|
|
75
79
|
const doc = $derived(snap.document.canvas);
|
|
76
80
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { type PaintEditorController, type PaintPresence, type PaintProposals, type PaintAgentStrokes, type PaintTool } from '@pixerate/editor/paint-editor';
|
|
1
|
+
import { type PaintEditorController, type FillRegionOptions, type PaintPresence, type PaintProposals, type PaintAgentStrokes, type PaintTool } from '@pixerate/editor/paint-editor';
|
|
2
2
|
interface Props {
|
|
3
3
|
controller: PaintEditorController;
|
|
4
4
|
/** The active tool (default brush). */
|
|
5
5
|
tool?: PaintTool;
|
|
6
|
+
/** Options for the Fill tool (`tool === 'fill'`). */
|
|
7
|
+
fillOptions?: Omit<FillRegionOptions, 'x' | 'y' | 'points'>;
|
|
6
8
|
/** Draw the symmetry axes and perspective lines (default true). */
|
|
7
9
|
showGuides?: boolean;
|
|
8
10
|
/** Fit the canvas in the viewport on mount (default true). */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { PaintPresence, documentToScreen, screenToDocument, type PaintEditorController, type PaintProposals, type PaintAgentStrokes, type PaintTool } from '@pixerate/editor/paint-editor';
|
|
2
|
+
import { PaintPresence, documentToScreen, screenToDocument, type FillRegionOptions, type PaintEditorController, type PaintProposals, type PaintAgentStrokes, type PaintTool } from '@pixerate/editor/paint-editor';
|
|
3
3
|
import type { AgentPlayback, PlaybackPoint, PlaybackRect, PlaybackRef } from '@pixerate/editor/tools';
|
|
4
4
|
import { createPlaybackSurface, getAgentPlayback } from '../playback/playback.svelte.js';
|
|
5
5
|
import PresenceLayer from '../playback/PresenceLayer.svelte';
|
|
@@ -105,6 +105,7 @@
|
|
|
105
105
|
return c && box ? { x: c.x - box.left, y: c.y - box.top } : p;
|
|
106
106
|
};
|
|
107
107
|
let tool = $state<PaintTool>('brush');
|
|
108
|
+
let fillOptions = $state<Omit<FillRegionOptions, 'x' | 'y' | 'points'>>({ reference: 'all', tolerance: 32, closeGap: 4, expand: 1, antialias: true });
|
|
108
109
|
let tab = $state<'color' | 'wet' | 'paper' | 'layers' | 'effects'>('color');
|
|
109
110
|
const effects = paintEffectsProvider(() => controller);
|
|
110
111
|
const tabs = $derived(effects.current ? (['color', 'wet', 'paper', 'layers', 'effects'] as const) : (['color', 'wet', 'paper', 'layers'] as const));
|
|
@@ -116,12 +117,12 @@
|
|
|
116
117
|
{#if playing}
|
|
117
118
|
<PresenceLayer presence={agentPresence} viewport={docToRoot} />
|
|
118
119
|
{/if}
|
|
119
|
-
<PaintToolbar {controller} {tool} ontoolchange={(t) => (tool = t)} {fileName} />
|
|
120
|
+
<PaintToolbar {controller} {tool} ontoolchange={(t) => (tool = t)} {fillOptions} onfilloptionschange={(f) => (fillOptions = f)} {fileName} />
|
|
120
121
|
<div class="flex min-h-0 flex-1 gap-2">
|
|
121
122
|
<BrushPanel {controller} class="w-60 shrink-0 overflow-y-auto pr-1" />
|
|
122
123
|
<div class="flex min-w-0 flex-1 flex-col">
|
|
123
124
|
<!-- With playback the editor draws the cursor (over the panels too), so the canvas doesn't. -->
|
|
124
|
-
<PaintCanvas {controller} {tool} presence={playing ? undefined : presence} {proposals} {agentStrokes} onerror={(e) => (error = e)} class="flex-1 rounded-lg" />
|
|
125
|
+
<PaintCanvas {controller} {tool} {fillOptions} presence={playing ? undefined : presence} {proposals} {agentStrokes} onerror={(e) => (error = e)} class="flex-1 rounded-lg" />
|
|
125
126
|
{#if error}<p role="alert" class="mt-1 text-[11px] text-rose-400">{error}</p>{/if}
|
|
126
127
|
</div>
|
|
127
128
|
<div class="flex w-64 shrink-0 flex-col gap-2 overflow-y-auto pr-1">
|
|
@@ -1,18 +1,33 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { PAINT_TOOLS, SYMMETRY_MODES, decodeImageBlob, downloadBytes, quickStencil, timeLapseZip, type PaintEditorController, type PaintTool, type SymmetryMode } from '@pixerate/editor/paint-editor';
|
|
2
|
+
import { PAINT_TOOLS, SYMMETRY_MODES, decodeImageBlob, downloadBytes, quickStencil, timeLapseZip, type FillRegionOptions, type PaintEditorController, type PaintTool, type SymmetryMode } from '@pixerate/editor/paint-editor';
|
|
3
3
|
import { attempt, button, chip, paintEditorState } from './paintEditorRune.svelte.js';
|
|
4
4
|
|
|
5
|
+
type FillToolbarOptions = Omit<FillRegionOptions, 'x' | 'y' | 'points'>;
|
|
6
|
+
|
|
5
7
|
interface Props {
|
|
6
8
|
controller: PaintEditorController;
|
|
7
9
|
tool: PaintTool;
|
|
8
10
|
ontoolchange: (tool: PaintTool) => void;
|
|
11
|
+
fillOptions?: FillToolbarOptions;
|
|
12
|
+
onfilloptionschange?: (options: FillToolbarOptions) => void;
|
|
9
13
|
/** Base name of saved and exported files (default `painting`). */
|
|
10
14
|
fileName?: string;
|
|
11
15
|
class?: string;
|
|
12
16
|
}
|
|
13
|
-
let { controller, tool, ontoolchange, fileName = 'painting', class: className = '' }: Props = $props();
|
|
17
|
+
let { controller, tool, ontoolchange, fillOptions: controlledFill, onfilloptionschange, fileName = 'painting', class: className = '' }: Props = $props();
|
|
14
18
|
const editor = paintEditorState(() => controller);
|
|
15
19
|
const snap = $derived(editor.current);
|
|
20
|
+
let localFill = $state<FillToolbarOptions>({ reference: 'all', tolerance: 32, closeGap: 4, expand: 1, antialias: true });
|
|
21
|
+
const fill = $derived(controlledFill ?? localFill);
|
|
22
|
+
const updateFill = (patch: Partial<FillToolbarOptions>) => {
|
|
23
|
+
const next = { ...fill, ...patch };
|
|
24
|
+
localFill = next;
|
|
25
|
+
onfilloptionschange?.(next);
|
|
26
|
+
};
|
|
27
|
+
const layers = $derived.by(() => {
|
|
28
|
+
void snap;
|
|
29
|
+
return controller.listLayers().filter((l) => l.type === 'pixel');
|
|
30
|
+
});
|
|
16
31
|
const symmetry = $derived(snap.guides.symmetry);
|
|
17
32
|
const perspective = $derived(snap.guides.perspective);
|
|
18
33
|
const tracing = $derived(snap.guides.tracing);
|
|
@@ -63,6 +78,33 @@
|
|
|
63
78
|
{#each PAINT_TOOLS as t (t.id)}
|
|
64
79
|
<button type="button" aria-pressed={tool === t.id} title={`${t.label} (${t.key.toUpperCase()})`} class={chip(tool === t.id)} onclick={() => ontoolchange(t.id)}>{t.label}</button>
|
|
65
80
|
{/each}
|
|
81
|
+
{#if tool === 'fill'}
|
|
82
|
+
<div class="flex items-center gap-1.5 rounded border border-slate-700/80 bg-slate-900/60 px-2 py-0.5 text-[11px] text-slate-300" data-testid="paint-fill-options">
|
|
83
|
+
<label class="flex items-center gap-1">
|
|
84
|
+
<span>Ref</span>
|
|
85
|
+
<select aria-label="Fill reference" class="rounded bg-slate-800 px-1 py-0.5 text-[11px]" value={fill.reference ?? 'all'} onchange={(e) => updateFill({ reference: e.currentTarget.value })}>
|
|
86
|
+
<option value="all">All layers</option>
|
|
87
|
+
<option value="layer">Active layer</option>
|
|
88
|
+
{#each layers as l (l.id)}
|
|
89
|
+
<option value={l.id}>{l.name}</option>
|
|
90
|
+
{/each}
|
|
91
|
+
</select>
|
|
92
|
+
</label>
|
|
93
|
+
<label class="flex items-center gap-1">
|
|
94
|
+
<span>Tol</span>
|
|
95
|
+
<input type="number" aria-label="Fill tolerance" min="0" max="255" value={fill.tolerance ?? 32} oninput={(e) => updateFill({ tolerance: Math.max(0, Math.min(255, Number(e.currentTarget.value) || 0)) })} class="w-11 rounded bg-slate-800 px-1 py-0.5 text-[11px]" />
|
|
96
|
+
</label>
|
|
97
|
+
<label class="flex items-center gap-1">
|
|
98
|
+
<span>Gap</span>
|
|
99
|
+
<input type="number" aria-label="Close gap" min="0" max="32" value={fill.closeGap ?? 4} oninput={(e) => updateFill({ closeGap: Math.max(0, Math.min(32, Number(e.currentTarget.value) || 0)) })} class="w-10 rounded bg-slate-800 px-1 py-0.5 text-[11px]" />
|
|
100
|
+
</label>
|
|
101
|
+
<label class="flex items-center gap-1">
|
|
102
|
+
<span>Expand</span>
|
|
103
|
+
<input type="number" aria-label="Expand fill" min="-16" max="16" value={fill.expand ?? 1} oninput={(e) => updateFill({ expand: Math.max(-16, Math.min(16, Number(e.currentTarget.value) || 0)) })} class="w-10 rounded bg-slate-800 px-1 py-0.5 text-[11px]" />
|
|
104
|
+
</label>
|
|
105
|
+
<button type="button" class={chip(Boolean(fill.wet))} aria-pressed={Boolean(fill.wet)} onclick={() => updateFill({ wet: !fill.wet })}>Wet</button>
|
|
106
|
+
</div>
|
|
107
|
+
{/if}
|
|
66
108
|
<span class="mx-1 h-4 w-px bg-slate-700"></span>
|
|
67
109
|
<button type="button" class={button} disabled={!snap.canUndo} title={snap.undoLabel ? `Undo ${snap.undoLabel}` : 'Undo'} onclick={() => controller.undo()}>Undo</button>
|
|
68
110
|
<button type="button" class={button} disabled={!snap.canRedo} title={snap.redoLabel ? `Redo ${snap.redoLabel}` : 'Redo'} onclick={() => controller.redo()}>Redo</button>
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { type PaintEditorController, type PaintTool } from '@pixerate/editor/paint-editor';
|
|
1
|
+
import { type FillRegionOptions, type PaintEditorController, type PaintTool } from '@pixerate/editor/paint-editor';
|
|
2
|
+
type FillToolbarOptions = Omit<FillRegionOptions, 'x' | 'y' | 'points'>;
|
|
2
3
|
interface Props {
|
|
3
4
|
controller: PaintEditorController;
|
|
4
5
|
tool: PaintTool;
|
|
5
6
|
ontoolchange: (tool: PaintTool) => void;
|
|
7
|
+
fillOptions?: FillToolbarOptions;
|
|
8
|
+
onfilloptionschange?: (options: FillToolbarOptions) => void;
|
|
6
9
|
/** Base name of saved and exported files (default `painting`). */
|
|
7
10
|
fileName?: string;
|
|
8
11
|
class?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixerate/editor-svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.203.0",
|
|
4
4
|
"description": "Svelte bindings and prompt/rich-text components for @pixerate/editor",
|
|
5
5
|
"author": "Pixerate",
|
|
6
6
|
"license": "MIT",
|
|
@@ -184,7 +184,7 @@
|
|
|
184
184
|
"**/*.css"
|
|
185
185
|
],
|
|
186
186
|
"dependencies": {
|
|
187
|
-
"@pixerate/editor": "0.
|
|
187
|
+
"@pixerate/editor": "0.273.0"
|
|
188
188
|
},
|
|
189
189
|
"peerDependencies": {
|
|
190
190
|
"@dagrejs/dagre": "^1.1.4",
|