@pixerate/editor-svelte 0.206.0 → 0.207.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/paint-editor/FerroPanel.svelte +66 -0
- package/dist/paint-editor/FerroPanel.svelte.d.ts +8 -0
- package/dist/paint-editor/PaintCanvas.svelte +40 -0
- package/dist/paint-editor/PaperPanel.svelte +2 -0
- package/dist/paint-editor/index.d.ts +3 -0
- package/dist/paint-editor/index.js +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { MAGNET_SLIDERS, readPath, type PaintEditorController } from '@pixerate/editor/paint-editor';
|
|
3
|
+
import PaintSlider from './PaintSlider.svelte';
|
|
4
|
+
import { attempt, button, paintEditorState, panel } from './paintEditorRune.svelte.js';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
controller: PaintEditorController;
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
let { controller, class: className = '' }: Props = $props();
|
|
11
|
+
const editor = paintEditorState(() => controller);
|
|
12
|
+
const magnets = $derived(editor.current.document.magnets ?? []);
|
|
13
|
+
let error = $state<string | null>(null);
|
|
14
|
+
const run = (fn: () => unknown) => attempt(fn, (m) => (error = m));
|
|
15
|
+
const info = $derived.by(() => {
|
|
16
|
+
const activeId = editor.current.document.activeLayerId;
|
|
17
|
+
try {
|
|
18
|
+
return activeId ? controller.getFerrofluid(activeId) : null;
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<!-- Ferrofluid & magnets panel: add, adjust and remove magnets, inspect spikes and cure ferrofluid. -->
|
|
26
|
+
<div class={`${panel} ${className}`} data-testid="ferro-panel">
|
|
27
|
+
<div class="flex items-center justify-between">
|
|
28
|
+
<span class="font-medium text-slate-300">Ferrofluid & magnets</span>
|
|
29
|
+
<div class="flex gap-1">
|
|
30
|
+
<button
|
|
31
|
+
type="button"
|
|
32
|
+
class={button}
|
|
33
|
+
onclick={() => run(() => controller.addMagnet({ x: Math.round(editor.current.document.canvas.width / 2), y: Math.round(editor.current.document.canvas.height / 2) }))}
|
|
34
|
+
>
|
|
35
|
+
Add magnet
|
|
36
|
+
</button>
|
|
37
|
+
{#if magnets.length > 0}
|
|
38
|
+
<button type="button" class={button} onclick={() => run(() => controller.removeMagnets())}>Clear magnets</button>
|
|
39
|
+
{/if}
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
{#each magnets as m (m.id)}
|
|
43
|
+
<div class="flex flex-col gap-1 rounded border border-slate-800 p-1.5" data-magnet={m.id}>
|
|
44
|
+
<div class="flex items-center justify-between text-[11px]">
|
|
45
|
+
<span class="font-mono text-slate-300">{m.id} ({Math.round(m.x)}, {Math.round(m.y)})</span>
|
|
46
|
+
<button type="button" aria-label={`Remove magnet ${m.id}`} class="px-1 text-slate-400 hover:text-white" onclick={() => run(() => controller.removeMagnets([m.id]))}>
|
|
47
|
+
×
|
|
48
|
+
</button>
|
|
49
|
+
</div>
|
|
50
|
+
{#each MAGNET_SLIDERS as c (c.path)}
|
|
51
|
+
<PaintSlider
|
|
52
|
+
control={{ ...c, label: `${c.label} (${m.id})` }}
|
|
53
|
+
value={readPath(m, c.path)}
|
|
54
|
+
oncommit={(v) => run(() => controller.updateMagnet(m.id, { [c.path]: v }))}
|
|
55
|
+
/>
|
|
56
|
+
{/each}
|
|
57
|
+
</div>
|
|
58
|
+
{/each}
|
|
59
|
+
<div class="flex items-center justify-between gap-2">
|
|
60
|
+
<span class="text-[11px] text-slate-400">
|
|
61
|
+
{info?.ferro ? `${info.activeSpikeArea} spike px · max ${Math.round(info.maxSpikeHeight * 100)}%` : 'No liquid ferrofluid on layer'}
|
|
62
|
+
</span>
|
|
63
|
+
<button type="button" class={button} disabled={!info?.ferro} onclick={() => run(() => controller.cureFerrofluid())}>Cure ferrofluid</button>
|
|
64
|
+
</div>
|
|
65
|
+
{#if error}<p role="alert" class="text-[11px] text-rose-400">{error}</p>{/if}
|
|
66
|
+
</div>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type PaintEditorController } from '@pixerate/editor/paint-editor';
|
|
2
|
+
interface Props {
|
|
3
|
+
controller: PaintEditorController;
|
|
4
|
+
class?: string;
|
|
5
|
+
}
|
|
6
|
+
declare const FerroPanel: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type FerroPanel = ReturnType<typeof FerroPanel>;
|
|
8
|
+
export default FerroPanel;
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import {
|
|
3
3
|
PaintCanvasPresenter,
|
|
4
4
|
PaintGesture,
|
|
5
|
+
documentToScreen,
|
|
6
|
+
screenToDocument,
|
|
5
7
|
drawWaterOverlay,
|
|
6
8
|
fitZoom,
|
|
7
9
|
startDeviceTilt,
|
|
@@ -68,6 +70,7 @@
|
|
|
68
70
|
let size = $state({ width: 800, height: 600 });
|
|
69
71
|
let predicted = $state<Array<{ x: number; y: number }>>([]);
|
|
70
72
|
let strokeDrag = $state<{ id: string; dx: number; dy: number } | null>(null);
|
|
73
|
+
let magnetDrag: { id: string; moved: boolean } | null = null;
|
|
71
74
|
const presenter = $derived(new PaintCanvasPresenter(controller));
|
|
72
75
|
const gesture = $derived(
|
|
73
76
|
new PaintGesture(controller, () => {
|
|
@@ -239,6 +242,43 @@
|
|
|
239
242
|
{#each snap.guides.references.filter((r) => r.visible) as r (r.id)}
|
|
240
243
|
<ReferencePanel {controller} reference={r} />
|
|
241
244
|
{/each}
|
|
245
|
+
{#if showGuides}
|
|
246
|
+
{#each snap.document.magnets ?? [] as m (m.id)}
|
|
247
|
+
{@const p = documentToScreen(snap.view, doc, size, m.x, m.y)}
|
|
248
|
+
<button
|
|
249
|
+
type="button"
|
|
250
|
+
aria-label={`Magnet ${m.id}`}
|
|
251
|
+
data-testid={`paint-magnet-${m.id}`}
|
|
252
|
+
data-magnet={m.id}
|
|
253
|
+
title={`Magnet ${m.id} (${Math.round(m.x)}, ${Math.round(m.y)})`}
|
|
254
|
+
class="absolute -translate-x-1/2 -translate-y-1/2 flex h-5 w-5 items-center justify-center rounded-full border border-indigo-300 bg-slate-900/90 text-[9px] font-bold text-indigo-200 shadow cursor-move"
|
|
255
|
+
style={`left: ${p.x}px; top: ${p.y}px`}
|
|
256
|
+
onpointerdown={(e) => {
|
|
257
|
+
e.stopPropagation();
|
|
258
|
+
magnetDrag = { id: m.id, moved: false };
|
|
259
|
+
(e.target as Element).setPointerCapture?.(e.pointerId);
|
|
260
|
+
}}
|
|
261
|
+
onpointermove={(e) => {
|
|
262
|
+
if (!magnetDrag || magnetDrag.id !== m.id) return;
|
|
263
|
+
e.stopPropagation();
|
|
264
|
+
const r = viewport?.getBoundingClientRect();
|
|
265
|
+
const box = { width: r?.width || size.width, height: r?.height || size.height };
|
|
266
|
+
const pt = screenToDocument(snap.view, doc, box, e.clientX - (r?.left ?? 0), e.clientY - (r?.top ?? 0));
|
|
267
|
+
controller.updateMagnet(m.id, { x: Math.round(pt.x * 100) / 100, y: Math.round(pt.y * 100) / 100 }, !magnetDrag.moved);
|
|
268
|
+
magnetDrag.moved = true;
|
|
269
|
+
}}
|
|
270
|
+
onpointerup={(e) => {
|
|
271
|
+
e.stopPropagation();
|
|
272
|
+
magnetDrag = null;
|
|
273
|
+
}}
|
|
274
|
+
onpointercancel={() => {
|
|
275
|
+
magnetDrag = null;
|
|
276
|
+
}}
|
|
277
|
+
>
|
|
278
|
+
{m.strength >= 0 ? 'N' : 'S'}
|
|
279
|
+
</button>
|
|
280
|
+
{/each}
|
|
281
|
+
{/if}
|
|
242
282
|
{#if proposals}<PaintProposalGhosts {proposals} {transform} zoom={snap.view.zoom} />{/if}
|
|
243
283
|
{#if presence}<PaintPresenceLayer {presence} view={snap.view} canvas={doc} viewport={size} />{/if}
|
|
244
284
|
<div class="absolute bottom-1 right-2 text-[10px] text-slate-500 pointer-events-none" data-testid="paint-view-status">
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { untrack } from 'svelte';
|
|
4
4
|
import PaintSlider from './PaintSlider.svelte';
|
|
5
5
|
import PixelCanvas from './PixelCanvas.svelte';
|
|
6
|
+
import FerroPanel from './FerroPanel.svelte';
|
|
6
7
|
import { attempt, button, chip, paintEditorState, panel } from './paintEditorRune.svelte.js';
|
|
7
8
|
|
|
8
9
|
interface Props {
|
|
@@ -102,5 +103,6 @@
|
|
|
102
103
|
{#if editor.current.heat.hotCells > 0}<span class="text-[11px] text-amber-400">{Math.round(editor.current.heat.max * 100)}% warm</span>{/if}
|
|
103
104
|
</div>
|
|
104
105
|
</div>
|
|
106
|
+
<FerroPanel {controller} />
|
|
105
107
|
{#if error}<p role="alert" class="text-[11px] text-rose-400">{error}</p>{/if}
|
|
106
108
|
</div>
|
|
@@ -6,6 +6,7 @@ import type BrushPanelComponent from './BrushPanel.svelte';
|
|
|
6
6
|
import type BrushEditorComponent from '../brushes/BrushEditor.svelte';
|
|
7
7
|
import type BrushLibraryBrowserComponent from '../brushes/BrushLibraryBrowser.svelte';
|
|
8
8
|
import type PaperPanelComponent from './PaperPanel.svelte';
|
|
9
|
+
import type FerroPanelComponent from './FerroPanel.svelte';
|
|
9
10
|
import type ColorPanelComponent from './ColorPanel.svelte';
|
|
10
11
|
import type PaintLayersPanelComponent from './PaintLayersPanel.svelte';
|
|
11
12
|
import type WetPanelComponent from './WetPanel.svelte';
|
|
@@ -23,6 +24,7 @@ export { default as BrushPanel } from './BrushPanel.svelte';
|
|
|
23
24
|
export { default as BrushEditor } from '../brushes/BrushEditor.svelte';
|
|
24
25
|
export { default as BrushLibraryBrowser } from '../brushes/BrushLibraryBrowser.svelte';
|
|
25
26
|
export { default as PaperPanel } from './PaperPanel.svelte';
|
|
27
|
+
export { default as FerroPanel } from './FerroPanel.svelte';
|
|
26
28
|
export { default as ColorPanel } from './ColorPanel.svelte';
|
|
27
29
|
export { default as PaintLayersPanel } from './PaintLayersPanel.svelte';
|
|
28
30
|
export { default as WetPanel } from './WetPanel.svelte';
|
|
@@ -45,6 +47,7 @@ export type BrushPanelProps = ComponentProps<typeof BrushPanelComponent>;
|
|
|
45
47
|
export type BrushEditorProps = ComponentProps<typeof BrushEditorComponent>;
|
|
46
48
|
export type BrushLibraryBrowserProps = ComponentProps<typeof BrushLibraryBrowserComponent>;
|
|
47
49
|
export type PaperPanelProps = ComponentProps<typeof PaperPanelComponent>;
|
|
50
|
+
export type FerroPanelProps = ComponentProps<typeof FerroPanelComponent>;
|
|
48
51
|
export type ColorPanelProps = ComponentProps<typeof ColorPanelComponent>;
|
|
49
52
|
export type PaintLayersPanelProps = ComponentProps<typeof PaintLayersPanelComponent>;
|
|
50
53
|
export type WetPanelProps = ComponentProps<typeof WetPanelComponent>;
|
|
@@ -7,6 +7,7 @@ export { default as BrushEditor } from '../brushes/BrushEditor.svelte';
|
|
|
7
7
|
// The library browser moved to `@pixerate/editor-svelte/brushes`; it is exported here too.
|
|
8
8
|
export { default as BrushLibraryBrowser } from '../brushes/BrushLibraryBrowser.svelte';
|
|
9
9
|
export { default as PaperPanel } from './PaperPanel.svelte';
|
|
10
|
+
export { default as FerroPanel } from './FerroPanel.svelte';
|
|
10
11
|
export { default as ColorPanel } from './ColorPanel.svelte';
|
|
11
12
|
export { default as PaintLayersPanel } from './PaintLayersPanel.svelte';
|
|
12
13
|
export { default as WetPanel } from './WetPanel.svelte';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixerate/editor-svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.207.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.277.0"
|
|
188
188
|
},
|
|
189
189
|
"peerDependencies": {
|
|
190
190
|
"@dagrejs/dagre": "^1.1.4",
|