@pixerate/editor-svelte 0.182.1 → 0.185.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.
- package/dist/image-editor/LayerEffectsPanel.svelte +5 -3
- package/dist/image-editor/LayerEffectsPanel.svelte.d.ts +3 -2
- package/dist/paint-editor/BrushPanel.svelte +49 -10
- package/dist/paint-editor/PaintLayersPanel.svelte +4 -0
- package/dist/paint-editor/StickerPanel.svelte +17 -8
- package/dist/paint-editor/StickerPicker.svelte +60 -0
- package/dist/paint-editor/StickerPicker.svelte.d.ts +13 -0
- package/dist/paint-editor/index.d.ts +3 -0
- package/dist/paint-editor/index.js +1 -0
- package/package.json +2 -2
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
import ImageRange from './controls/ImageRange.svelte';
|
|
5
5
|
import ImageSelect from './controls/ImageSelect.svelte';
|
|
6
6
|
import { imageIconButtonClass } from './styles.js';
|
|
7
|
-
|
|
8
|
-
import {
|
|
7
|
+
// From the raster entry, not the image editor's: the paint editor shows this panel too.
|
|
8
|
+
import type { EffectControl, LayerEffectKind, LayerEffectsController } from '@pixerate/editor/raster';
|
|
9
|
+
import { LAYER_EFFECT_MENU, effectControlChange, layerEffectControls, layerEffectsPanel } from '@pixerate/editor/raster';
|
|
9
10
|
import { untrack } from 'svelte';
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -14,7 +15,8 @@
|
|
|
14
15
|
* settings. Every change is one undo step (a slider drag is one step too).
|
|
15
16
|
*/
|
|
16
17
|
interface Props {
|
|
17
|
-
controller
|
|
18
|
+
/** The image editor's controller, the paint editor's, or anything else with layer effects. */
|
|
19
|
+
controller: LayerEffectsController;
|
|
18
20
|
/** The layer whose effects to show (default: the active layer). */
|
|
19
21
|
layerId?: string | null;
|
|
20
22
|
/**
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { LayerEffectsController } from '@pixerate/editor/raster';
|
|
2
2
|
/**
|
|
3
3
|
* A layer's effects (Photoshop's layer styles): the master switch, Scale Effects, an "Add
|
|
4
4
|
* style" menu, and each effect with its enabled toggle, remove button and, when expanded, its
|
|
5
5
|
* settings. Every change is one undo step (a slider drag is one step too).
|
|
6
6
|
*/
|
|
7
7
|
interface Props {
|
|
8
|
-
controller
|
|
8
|
+
/** The image editor's controller, the paint editor's, or anything else with layer effects. */
|
|
9
|
+
controller: LayerEffectsController;
|
|
9
10
|
/** The layer whose effects to show (default: the active layer). */
|
|
10
11
|
layerId?: string | null;
|
|
11
12
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import StickerPicker from './StickerPicker.svelte';
|
|
2
3
|
import {
|
|
3
4
|
BLEND_MENU_GROUPS,
|
|
4
5
|
BRUSH_ROTATIONS,
|
|
@@ -20,11 +21,21 @@
|
|
|
20
21
|
PARTICLE_KIND_LABELS,
|
|
21
22
|
PARTICLE_RENDERS,
|
|
22
23
|
PARTICLE_RENDER_LABELS,
|
|
24
|
+
RIBBON_PAIRS,
|
|
25
|
+
RIBBON_PAIR_LABELS,
|
|
26
|
+
DOT_TEXTURE_ORDERS,
|
|
27
|
+
DOT_TEXTURE_ORDER_LABELS,
|
|
28
|
+
DOT_ROTATIONS,
|
|
29
|
+
DOT_ROTATION_LABELS,
|
|
30
|
+
toggleDotTexture,
|
|
23
31
|
STAMP_KINDS,
|
|
24
32
|
STAMP_LABELS,
|
|
25
|
-
|
|
33
|
+
toggleStickerRef,
|
|
26
34
|
type ParticleKind,
|
|
27
35
|
type ParticleRender,
|
|
36
|
+
type RibbonPairs,
|
|
37
|
+
type DotTextureOrder,
|
|
38
|
+
type DotRotation,
|
|
28
39
|
type ImpastoTool,
|
|
29
40
|
type WetMediumKind,
|
|
30
41
|
type BlendMode,
|
|
@@ -150,17 +161,45 @@
|
|
|
150
161
|
{#each PARTICLE_RENDERS as k (k)}<option value={k}>{PARTICLE_RENDER_LABELS[k]}</option>{/each}
|
|
151
162
|
</select>
|
|
152
163
|
</label>
|
|
164
|
+
{#if brush.particle.render === 'ribbon'}
|
|
165
|
+
<label class="flex items-center justify-between gap-2">
|
|
166
|
+
<span class="text-slate-400">Ribbon joins</span>
|
|
167
|
+
<select aria-label="Ribbon pairs" value={brush.particle.ribbonPairs} onchange={(e) => controller.setBrush({ particle: { ribbonPairs: e.currentTarget.value as RibbonPairs } })} class="rounded bg-slate-800 px-1 py-0.5">
|
|
168
|
+
{#each RIBBON_PAIRS as k (k)}<option value={k}>{RIBBON_PAIR_LABELS[k]}</option>{/each}
|
|
169
|
+
</select>
|
|
170
|
+
</label>
|
|
171
|
+
{/if}
|
|
172
|
+
{#if brush.particle.render === 'dot' || brush.particle.render === 'sequence'}
|
|
173
|
+
<div class="flex flex-wrap gap-1" role="group" aria-label="Dot textures" title="Stamps drawn instead of round dots (up to 4; none: round)">
|
|
174
|
+
{#each STAMP_KINDS as k (k)}
|
|
175
|
+
<button
|
|
176
|
+
type="button"
|
|
177
|
+
aria-pressed={brush.particle.dotTextures.includes(k)}
|
|
178
|
+
class={`px-1.5 py-0.5 rounded text-[10px] ${brush.particle.dotTextures.includes(k) ? 'bg-indigo-600 text-white' : 'bg-slate-800 text-slate-300'}`}
|
|
179
|
+
onclick={() => controller.setBrush({ particle: { dotTextures: toggleDotTexture(brush.particle.dotTextures, k) } })}
|
|
180
|
+
>
|
|
181
|
+
{STAMP_LABELS[k]}
|
|
182
|
+
</button>
|
|
183
|
+
{/each}
|
|
184
|
+
</div>
|
|
185
|
+
{#if brush.particle.dotTextures.length > 0}
|
|
186
|
+
<label class="flex items-center justify-between gap-2">
|
|
187
|
+
<span class="text-slate-400">Texture order</span>
|
|
188
|
+
<select aria-label="Dot texture order" value={brush.particle.dotTextureOrder} onchange={(e) => controller.setBrush({ particle: { dotTextureOrder: e.currentTarget.value as DotTextureOrder } })} class="rounded bg-slate-800 px-1 py-0.5">
|
|
189
|
+
{#each DOT_TEXTURE_ORDERS as k (k)}<option value={k}>{DOT_TEXTURE_ORDER_LABELS[k]}</option>{/each}
|
|
190
|
+
</select>
|
|
191
|
+
</label>
|
|
192
|
+
<label class="flex items-center justify-between gap-2">
|
|
193
|
+
<span class="text-slate-400">Texture turn</span>
|
|
194
|
+
<select aria-label="Dot rotation" value={brush.particle.dotRotation} onchange={(e) => controller.setBrush({ particle: { dotRotation: e.currentTarget.value as DotRotation } })} class="rounded bg-slate-800 px-1 py-0.5">
|
|
195
|
+
{#each DOT_ROTATIONS as k (k)}<option value={k}>{DOT_ROTATION_LABELS[k]}</option>{/each}
|
|
196
|
+
</select>
|
|
197
|
+
</label>
|
|
198
|
+
{/if}
|
|
199
|
+
{/if}
|
|
153
200
|
{/if}
|
|
154
201
|
{#if engine.stickerStamps}
|
|
155
|
-
<
|
|
156
|
-
{#each STAMP_KINDS as k (k)}
|
|
157
|
-
<button
|
|
158
|
-
type="button"
|
|
159
|
-
aria-pressed={brush.sticker.stamps.includes(k)}
|
|
160
|
-
class={`px-1.5 py-0.5 rounded text-[10px] ${brush.sticker.stamps.includes(k) ? 'bg-indigo-600 text-white' : 'bg-slate-800 text-slate-300'}`}
|
|
161
|
-
onclick={() => controller.setBrush({ sticker: { stamps: toggleStamp(brush.sticker.stamps, k) } })}>{STAMP_LABELS[k]}</button>
|
|
162
|
-
{/each}
|
|
163
|
-
</div>
|
|
202
|
+
<StickerPicker {controller} label="Sticker stamps" selected={brush.sticker.stamps} onpick={(ref) => controller.setBrush({ sticker: { stamps: toggleStickerRef(brush.sticker.stamps, ref) } })} />
|
|
164
203
|
{/if}
|
|
165
204
|
{#if engine.impastoTool}
|
|
166
205
|
<label class="flex items-center justify-between gap-2">
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { BLEND_MENU_GROUPS, type BlendMode, type PaintEditorController, type PaintLayerInfo } from '@pixerate/editor/paint-editor';
|
|
3
3
|
import { untrack } from 'svelte';
|
|
4
4
|
import { attempt, button, chip, paintEditorState, panel } from './paintEditorRune.svelte.js';
|
|
5
|
+
import LayerEffectsPanel from '../image-editor/LayerEffectsPanel.svelte';
|
|
5
6
|
|
|
6
7
|
interface Props {
|
|
7
8
|
controller: PaintEditorController;
|
|
@@ -128,6 +129,9 @@
|
|
|
128
129
|
</label>
|
|
129
130
|
</div>
|
|
130
131
|
{/if}
|
|
132
|
+
<div class="border-t border-slate-800 pt-1.5" data-testid="paint-layer-effects" title="Live layer styles drawn from this layer's paint: glows, shadows, strokes">
|
|
133
|
+
<LayerEffectsPanel {controller} layerId={active.id} />
|
|
134
|
+
</div>
|
|
131
135
|
</div>
|
|
132
136
|
{/if}
|
|
133
137
|
{#if error}<p role="alert" class="text-[11px] text-rose-400">{error}</p>{/if}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { STICKER_SLIDERS, readPath, stickerRefLabel, type PaintEditorController } from '@pixerate/editor/paint-editor';
|
|
3
|
+
import StickerPicker from './StickerPicker.svelte';
|
|
3
4
|
import PaintSlider from './PaintSlider.svelte';
|
|
4
5
|
import { attempt, button, paintEditorState, panel } from './paintEditorRune.svelte.js';
|
|
5
6
|
|
|
@@ -23,17 +24,25 @@
|
|
|
23
24
|
<span class="font-medium text-slate-300">Stickers ({stickers.length})</span>
|
|
24
25
|
<div class="flex max-h-24 flex-wrap gap-1 overflow-y-auto" role="listbox" aria-label="Stickers">
|
|
25
26
|
{#each stickers as s (s.id)}
|
|
26
|
-
<button type="button" role="option" aria-selected={s.id === picked} title={`${
|
|
27
|
+
<button type="button" role="option" aria-selected={s.id === picked} title={`${stickerRefLabel(s.ref)} ${s.id}`} class={`h-5 w-5 rounded ring-1 ${s.id === picked ? 'ring-indigo-400' : 'ring-slate-700'}`} style={`background: ${s.color}`} onclick={() => controller.selectSticker(s.id)}></button>
|
|
27
28
|
{/each}
|
|
28
29
|
</div>
|
|
29
30
|
{#if sel}
|
|
30
31
|
<div class="flex flex-col gap-1.5" data-testid="sticker-edit">
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
<span class="text-slate-400">Sticker</span>
|
|
33
|
+
<!-- The layer is redrawn, so the new sticker's art (and every other's) loads first. -->
|
|
34
|
+
<StickerPicker
|
|
35
|
+
{controller}
|
|
36
|
+
label="Sticker art"
|
|
37
|
+
selected={[sel.ref]}
|
|
38
|
+
onpick={(ref) => {
|
|
39
|
+
const id = sel.id;
|
|
40
|
+
void controller
|
|
41
|
+
.prepareStickers([ref, ...stickers.map((x) => x.ref)])
|
|
42
|
+
.then(() => run(() => controller.updateSticker(layerId, id, { ref })))
|
|
43
|
+
.catch((e: unknown) => run(() => { throw e; }));
|
|
44
|
+
}}
|
|
45
|
+
/>
|
|
37
46
|
<div class="flex items-center justify-between gap-2">
|
|
38
47
|
<span class="text-slate-400">Position</span>
|
|
39
48
|
<span class="flex gap-1">
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { StickerBrowser, type PaintEditorController } from '@pixerate/editor/paint-editor';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
controller: PaintEditorController;
|
|
6
|
+
/** Refs shown as chosen. */
|
|
7
|
+
selected: readonly string[];
|
|
8
|
+
/** Called with a sticker's ref when it is clicked. */
|
|
9
|
+
onpick: (ref: string) => void;
|
|
10
|
+
/** Accessible name of the sticker list (default "Stickers"). */
|
|
11
|
+
label?: string;
|
|
12
|
+
}
|
|
13
|
+
let { controller, selected, onpick, label = 'Stickers' }: Props = $props();
|
|
14
|
+
const browser = $derived(new StickerBrowser(() => controller.stickersProvider));
|
|
15
|
+
let s = $state(new StickerBrowser(() => controller.stickersProvider).state);
|
|
16
|
+
$effect(() => {
|
|
17
|
+
const b = browser;
|
|
18
|
+
s = b.state;
|
|
19
|
+
const off = b.subscribe(() => (s = b.state));
|
|
20
|
+
void b.refresh();
|
|
21
|
+
const offProvider = controller.stickers.subscribe(() => void b.refresh());
|
|
22
|
+
return () => {
|
|
23
|
+
off();
|
|
24
|
+
offProvider();
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
const failed = $derived(selected.map((ref) => ({ ref, ...controller.stickerArtStatus(ref) })).filter((x) => x.status === 'failed'));
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<!-- Stickers from the paint editor's sticker library, searchable, by category and a page at a time; clicking one calls onpick with its ref. -->
|
|
31
|
+
<div class="flex flex-col gap-1" data-testid="sticker-picker">
|
|
32
|
+
<div class="flex items-center gap-1">
|
|
33
|
+
<input type="search" aria-label={`Search ${label.toLowerCase()}`} placeholder={`Search ${s.provider}`} value={s.query} oninput={(e) => void browser.setQuery(e.currentTarget.value)} class="min-w-0 flex-1 rounded bg-slate-800 px-1 py-0.5" />
|
|
34
|
+
{#if s.categories.length > 1}
|
|
35
|
+
<select aria-label="Sticker category" value={s.category} onchange={(e) => void browser.setCategory(e.currentTarget.value)} class="rounded bg-slate-800 px-1 py-0.5">
|
|
36
|
+
<option value="">All</option>
|
|
37
|
+
{#each s.categories as c (c.id)}<option value={c.id}>{c.name}</option>{/each}
|
|
38
|
+
</select>
|
|
39
|
+
{/if}
|
|
40
|
+
</div>
|
|
41
|
+
<div class="flex max-h-32 flex-wrap gap-1 overflow-y-auto" role="group" aria-label={label}>
|
|
42
|
+
{#each s.items as it (it.ref)}
|
|
43
|
+
<button
|
|
44
|
+
type="button"
|
|
45
|
+
aria-pressed={selected.includes(it.ref)}
|
|
46
|
+
title={`${it.name} (${it.ref}) · ${it.licence.name}`}
|
|
47
|
+
class={`flex items-center gap-1 rounded px-1.5 py-0.5 text-[10px] ${selected.includes(it.ref) ? 'bg-indigo-600 text-white' : 'bg-slate-800 text-slate-300'}`}
|
|
48
|
+
onclick={() => onpick(it.ref)}
|
|
49
|
+
>
|
|
50
|
+
{#if it.thumbnail}<img src={it.thumbnail} alt="" class="h-4 w-4" />{/if}{it.name}
|
|
51
|
+
</button>
|
|
52
|
+
{/each}
|
|
53
|
+
</div>
|
|
54
|
+
<div class="flex items-center justify-between text-[10px] text-slate-500">
|
|
55
|
+
<span>{s.loading ? 'Loading…' : `${s.items.length} of ${s.total}`}</span>
|
|
56
|
+
{#if s.hasMore}<button type="button" class="text-indigo-300" onclick={() => void browser.more()}>More</button>{/if}
|
|
57
|
+
</div>
|
|
58
|
+
{#if s.error}<p role="alert" class="text-[11px] text-rose-400">{s.error}</p>{/if}
|
|
59
|
+
{#each failed as f (f.ref)}<p role="alert" class="text-[11px] text-rose-400">{f.ref}: {f.error}</p>{/each}
|
|
60
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type PaintEditorController } from '@pixerate/editor/paint-editor';
|
|
2
|
+
interface Props {
|
|
3
|
+
controller: PaintEditorController;
|
|
4
|
+
/** Refs shown as chosen. */
|
|
5
|
+
selected: readonly string[];
|
|
6
|
+
/** Called with a sticker's ref when it is clicked. */
|
|
7
|
+
onpick: (ref: string) => void;
|
|
8
|
+
/** Accessible name of the sticker list (default "Stickers"). */
|
|
9
|
+
label?: string;
|
|
10
|
+
}
|
|
11
|
+
declare const StickerPicker: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type StickerPicker = ReturnType<typeof StickerPicker>;
|
|
13
|
+
export default StickerPicker;
|
|
@@ -8,6 +8,7 @@ import type ColorPanelComponent from './ColorPanel.svelte';
|
|
|
8
8
|
import type PaintLayersPanelComponent from './PaintLayersPanel.svelte';
|
|
9
9
|
import type WetPanelComponent from './WetPanel.svelte';
|
|
10
10
|
import type StickerPanelComponent from './StickerPanel.svelte';
|
|
11
|
+
import type StickerPickerComponent from './StickerPicker.svelte';
|
|
11
12
|
import type StrokePanelComponent from './StrokePanel.svelte';
|
|
12
13
|
import type PaintEffectsPanelComponent from './PaintEffectsPanel.svelte';
|
|
13
14
|
import type PaintGradientEditorComponent from './PaintGradientEditor.svelte';
|
|
@@ -20,6 +21,7 @@ export { default as ColorPanel } from './ColorPanel.svelte';
|
|
|
20
21
|
export { default as PaintLayersPanel } from './PaintLayersPanel.svelte';
|
|
21
22
|
export { default as WetPanel } from './WetPanel.svelte';
|
|
22
23
|
export { default as StickerPanel } from './StickerPanel.svelte';
|
|
24
|
+
export { default as StickerPicker } from './StickerPicker.svelte';
|
|
23
25
|
export { default as StrokePanel } from './StrokePanel.svelte';
|
|
24
26
|
export { default as PaintEffectsPanel } from './PaintEffectsPanel.svelte';
|
|
25
27
|
export { default as PaintGradientEditor } from './PaintGradientEditor.svelte';
|
|
@@ -35,6 +37,7 @@ export type ColorPanelProps = ComponentProps<typeof ColorPanelComponent>;
|
|
|
35
37
|
export type PaintLayersPanelProps = ComponentProps<typeof PaintLayersPanelComponent>;
|
|
36
38
|
export type WetPanelProps = ComponentProps<typeof WetPanelComponent>;
|
|
37
39
|
export type StickerPanelProps = ComponentProps<typeof StickerPanelComponent>;
|
|
40
|
+
export type StickerPickerProps = ComponentProps<typeof StickerPickerComponent>;
|
|
38
41
|
export type StrokePanelProps = ComponentProps<typeof StrokePanelComponent>;
|
|
39
42
|
export type PaintEffectsPanelProps = ComponentProps<typeof PaintEffectsPanelComponent>;
|
|
40
43
|
export type PaintGradientEditorProps = ComponentProps<typeof PaintGradientEditorComponent>;
|
|
@@ -7,6 +7,7 @@ export { default as ColorPanel } from './ColorPanel.svelte';
|
|
|
7
7
|
export { default as PaintLayersPanel } from './PaintLayersPanel.svelte';
|
|
8
8
|
export { default as WetPanel } from './WetPanel.svelte';
|
|
9
9
|
export { default as StickerPanel } from './StickerPanel.svelte';
|
|
10
|
+
export { default as StickerPicker } from './StickerPicker.svelte';
|
|
10
11
|
export { default as StrokePanel } from './StrokePanel.svelte';
|
|
11
12
|
export { default as PaintEffectsPanel } from './PaintEffectsPanel.svelte';
|
|
12
13
|
export { default as PaintGradientEditor } from './PaintGradientEditor.svelte';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixerate/editor-svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.185.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.249.0"
|
|
169
169
|
},
|
|
170
170
|
"peerDependencies": {
|
|
171
171
|
"@dagrejs/dagre": "^1.1.4",
|