@pixerate/editor-svelte 0.80.0 → 0.81.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/ImageEditor.svelte +138 -1
- package/dist/image-editor/ImageEditor.svelte.d.ts.map +1 -1
- package/dist/image-editor/LayersPanel.svelte +20 -8
- package/dist/image-editor/LayersPanel.svelte.d.ts.map +1 -1
- package/dist/image-editor/ShapeOptionsBar.svelte +120 -0
- package/dist/image-editor/ShapeOptionsBar.svelte.d.ts +16 -0
- package/dist/image-editor/ShapeOptionsBar.svelte.d.ts.map +1 -0
- package/dist/image-editor/TypeOptionsBar.svelte +116 -0
- package/dist/image-editor/TypeOptionsBar.svelte.d.ts +18 -0
- package/dist/image-editor/TypeOptionsBar.svelte.d.ts.map +1 -0
- package/dist/image-editor/index.d.ts +6 -0
- package/dist/image-editor/index.d.ts.map +1 -1
- package/dist/image-editor/index.js +2 -0
- package/package.json +2 -2
|
@@ -28,7 +28,14 @@
|
|
|
28
28
|
pointerPressure,
|
|
29
29
|
FreeTransformTool,
|
|
30
30
|
getViewportProjection,
|
|
31
|
+
ShapeDrawGesture,
|
|
32
|
+
TypeToolGesture,
|
|
33
|
+
DEFAULT_SHAPE_TOOL_OPTIONS,
|
|
34
|
+
DEFAULT_TYPE_TOOL_OPTIONS,
|
|
31
35
|
type SelectionToolOptions,
|
|
36
|
+
type ShapeToolOptions,
|
|
37
|
+
type TextEditSession,
|
|
38
|
+
type TypeToolOptions,
|
|
32
39
|
} from '@pixerate/editor/image-editor';
|
|
33
40
|
import ControlSlider from './ControlSlider.svelte';
|
|
34
41
|
import Histogram from './Histogram.svelte';
|
|
@@ -38,6 +45,8 @@
|
|
|
38
45
|
import SelectionOptionsBar from './SelectionOptionsBar.svelte';
|
|
39
46
|
import RefineEdgePanel from './RefineEdgePanel.svelte';
|
|
40
47
|
import FreeTransformOptionsBar from './FreeTransformOptionsBar.svelte';
|
|
48
|
+
import ShapeOptionsBar from './ShapeOptionsBar.svelte';
|
|
49
|
+
import TypeOptionsBar from './TypeOptionsBar.svelte';
|
|
41
50
|
import { onDestroy, untrack } from 'svelte';
|
|
42
51
|
import { createReactiveImageEditor, type ReactiveImageEditor } from './imageEditorState.svelte.js';
|
|
43
52
|
|
|
@@ -85,6 +94,60 @@
|
|
|
85
94
|
const paintGesture = $derived(new PaintGesture(editor.controller));
|
|
86
95
|
let brushCursor: ImagePoint | null = null;
|
|
87
96
|
|
|
97
|
+
// Shape and type tools: their options and gestures; type is edited on the canvas through a hidden textarea.
|
|
98
|
+
let shapeOptions = $state<ShapeToolOptions>({ ...DEFAULT_SHAPE_TOOL_OPTIONS });
|
|
99
|
+
let typeOptions = $state<TypeToolOptions>({ ...DEFAULT_TYPE_TOOL_OPTIONS });
|
|
100
|
+
const shapeGesture = $derived(new ShapeDrawGesture(editor.controller, () => shapeOptions));
|
|
101
|
+
const typeTool = $derived(new TypeToolGesture(editor.controller, () => typeOptions));
|
|
102
|
+
let typeSession = $state<TextEditSession | null>(null);
|
|
103
|
+
let typeInputEl = $state<HTMLTextAreaElement | null>(null);
|
|
104
|
+
let shapeTypeError = $state<string | null>(null);
|
|
105
|
+
$effect(() => {
|
|
106
|
+
const s = typeSession;
|
|
107
|
+
if (!s) return;
|
|
108
|
+
return untrack(() => s.subscribe(() => void renderCanvas()));
|
|
109
|
+
});
|
|
110
|
+
$effect(() => {
|
|
111
|
+
// End the edit when the tool or tab changes.
|
|
112
|
+
const keep = activeTab === 'annotate' && editor.state.activeTool === 'type';
|
|
113
|
+
untrack(() => {
|
|
114
|
+
if (!keep && typeTool.session) finishTyping();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
function finishTyping() {
|
|
118
|
+
typeTool.finish();
|
|
119
|
+
typeSession = null;
|
|
120
|
+
void renderCanvas();
|
|
121
|
+
}
|
|
122
|
+
function handleTypeKeyDown(e: KeyboardEvent) {
|
|
123
|
+
const session = typeTool.session;
|
|
124
|
+
if (!session || e.isComposing) return;
|
|
125
|
+
if (e.key === 'Escape') {
|
|
126
|
+
e.preventDefault();
|
|
127
|
+
finishTyping();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if ((e.metaKey || e.ctrlKey) && ['z', 'y', 'c', 'x', 'v'].includes(e.key.toLowerCase())) return;
|
|
131
|
+
if (session.handleKey(e)) {
|
|
132
|
+
e.preventDefault();
|
|
133
|
+
shapeTypeError = session.error;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function handleTypeInput(e: Event) {
|
|
137
|
+
// Composed (IME) and pasted text arrives here; plain keys were handled on keydown.
|
|
138
|
+
const el = e.currentTarget as HTMLTextAreaElement;
|
|
139
|
+
const session = typeTool.session;
|
|
140
|
+
if (session && el.value && !(e as InputEvent).isComposing) {
|
|
141
|
+
session.insert(el.value);
|
|
142
|
+
el.value = '';
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function handleTypeComposition(e: CompositionEvent) {
|
|
146
|
+
const session = typeTool.session;
|
|
147
|
+
if (session && e.data) session.insert(e.data);
|
|
148
|
+
(e.currentTarget as HTMLTextAreaElement).value = '';
|
|
149
|
+
}
|
|
150
|
+
|
|
88
151
|
// Free Transform (Ctrl/Cmd+T): the shared tool drives a session; its overlay is drawn by the renderer.
|
|
89
152
|
const transformTool = $derived(
|
|
90
153
|
new FreeTransformTool(editor.controller, {
|
|
@@ -292,6 +355,7 @@
|
|
|
292
355
|
? { ...brushCursor, diameter: editor.controller.getState().paint.brush.size }
|
|
293
356
|
: null,
|
|
294
357
|
freeTransform: transformTool.overlay(),
|
|
358
|
+
textEdit: typeTool.session?.alive ? typeTool.session.overlay() : null,
|
|
295
359
|
});
|
|
296
360
|
}
|
|
297
361
|
|
|
@@ -434,6 +498,18 @@
|
|
|
434
498
|
return;
|
|
435
499
|
}
|
|
436
500
|
|
|
501
|
+
if (activeTab === 'annotate' && (editor.state.activeTool === 'shape' || editor.state.activeTool === 'type')) {
|
|
502
|
+
const imgPt = canvasToImagePoint(canvasPt, editor.state, canvasEl.width, canvasEl.height, false);
|
|
503
|
+
shapeTypeError = null;
|
|
504
|
+
if (editor.state.activeTool === 'shape') shapeGesture.down(imgPt);
|
|
505
|
+
else {
|
|
506
|
+
typeTool.down(imgPt, { shift: e.shiftKey });
|
|
507
|
+
typeSession = typeTool.session;
|
|
508
|
+
void renderCanvas();
|
|
509
|
+
}
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
|
|
437
513
|
if (activeTab === 'annotate') {
|
|
438
514
|
const imgPt = canvasToImagePoint(canvasPt, editor.state, canvasEl.width, canvasEl.height, false);
|
|
439
515
|
dragStartPoint = imgPt;
|
|
@@ -530,6 +606,14 @@
|
|
|
530
606
|
return;
|
|
531
607
|
}
|
|
532
608
|
|
|
609
|
+
if (activeTab === 'annotate' && editor.state.activeTool === 'shape') {
|
|
610
|
+
if (shapeGesture.active) {
|
|
611
|
+
shapeGesture.move(canvasToImagePoint(canvasPt, editor.state, canvasEl.width, canvasEl.height, false), { shift: e.shiftKey, alt: e.altKey });
|
|
612
|
+
if (shapeGesture.error) shapeTypeError = shapeGesture.error;
|
|
613
|
+
}
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
|
|
533
617
|
if (activeTab === 'annotate') {
|
|
534
618
|
if (annotationDragInfo) {
|
|
535
619
|
const currentImgPt = canvasToImagePoint(canvasPt, editor.state, canvasEl.width, canvasEl.height, false);
|
|
@@ -660,6 +744,20 @@
|
|
|
660
744
|
return;
|
|
661
745
|
}
|
|
662
746
|
|
|
747
|
+
if (activeTab === 'annotate' && editor.state.activeTool === 'shape') {
|
|
748
|
+
shapeGesture.up();
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
if (activeTab === 'annotate' && editor.state.activeTool === 'type') {
|
|
752
|
+
typeTool.up(canvasToImagePoint(canvasPt, editor.state, canvasEl.width, canvasEl.height, false));
|
|
753
|
+
typeSession = typeTool.session;
|
|
754
|
+
if (typeTool.error) shapeTypeError = typeTool.error;
|
|
755
|
+
void renderCanvas();
|
|
756
|
+
// Keystrokes go to the hidden textarea while text is being edited.
|
|
757
|
+
if (typeTool.session) setTimeout(() => typeInputEl?.focus(), 0);
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
|
|
663
761
|
if (activeTab === 'annotate') {
|
|
664
762
|
if (annotationDragInfo) {
|
|
665
763
|
annotationDragInfo = null;
|
|
@@ -977,8 +1075,21 @@
|
|
|
977
1075
|
onpointercancel={() => paintGesture.cancel()}
|
|
978
1076
|
data-testid="image-editor-canvas"
|
|
979
1077
|
style={transformActive ? `cursor: ${transformCursor}` : undefined}
|
|
980
|
-
class="absolute inset-0 w-full h-full block {transformActive ? 'touch-none' : activeTab === 'paint' ? 'touch-none cursor-crosshair' : ''}"
|
|
1078
|
+
class="absolute inset-0 w-full h-full block {transformActive ? 'touch-none' : activeTab === 'paint' ? 'touch-none cursor-crosshair' : activeTab === 'annotate' && editor.state.activeTool === 'type' ? 'cursor-text' : activeTab === 'annotate' && editor.state.activeTool === 'shape' ? 'cursor-crosshair' : ''}"
|
|
981
1079
|
></canvas>
|
|
1080
|
+
{#if typeSession}
|
|
1081
|
+
<textarea
|
|
1082
|
+
bind:this={typeInputEl}
|
|
1083
|
+
aria-label="Edit text"
|
|
1084
|
+
data-testid="type-input"
|
|
1085
|
+
autocapitalize="off"
|
|
1086
|
+
spellcheck={false}
|
|
1087
|
+
onkeydown={handleTypeKeyDown}
|
|
1088
|
+
oninput={handleTypeInput}
|
|
1089
|
+
oncompositionend={handleTypeComposition}
|
|
1090
|
+
class="absolute left-0 top-0 h-px w-px opacity-0"
|
|
1091
|
+
></textarea>
|
|
1092
|
+
{/if}
|
|
982
1093
|
|
|
983
1094
|
</div>
|
|
984
1095
|
|
|
@@ -1192,6 +1303,32 @@
|
|
|
1192
1303
|
|
|
1193
1304
|
{#if activeTab === 'annotate'}
|
|
1194
1305
|
<div class="flex flex-col gap-3 text-xs">
|
|
1306
|
+
<span class="font-semibold text-slate-200 uppercase tracking-wider text-[11px]">Shape & Type Layers</span>
|
|
1307
|
+
<div class="grid grid-cols-2 gap-1.5">
|
|
1308
|
+
{#each ['shape', 'type'] as const as t (t)}
|
|
1309
|
+
<button
|
|
1310
|
+
type="button"
|
|
1311
|
+
aria-pressed={editor.state.activeTool === t}
|
|
1312
|
+
onclick={() => editor.setTool(t)}
|
|
1313
|
+
class="py-1.5 px-2 rounded-lg font-medium border transition-colors {editor.state.activeTool === t
|
|
1314
|
+
? 'bg-indigo-600 border-indigo-500 text-white'
|
|
1315
|
+
: 'bg-slate-800 border-slate-700 text-slate-300 hover:bg-slate-700'}">{t === 'shape' ? 'Shape' : 'Type'}</button>
|
|
1316
|
+
{/each}
|
|
1317
|
+
</div>
|
|
1318
|
+
{#if editor.state.activeTool === 'shape'}
|
|
1319
|
+
<ShapeOptionsBar controller={editor.controller} options={shapeOptions} onchange={(o) => (shapeOptions = o)} />
|
|
1320
|
+
{/if}
|
|
1321
|
+
{#if editor.state.activeTool === 'type'}
|
|
1322
|
+
<TypeOptionsBar controller={editor.controller} options={typeOptions} onchange={(o) => (typeOptions = o)} session={typeSession} />
|
|
1323
|
+
{#if typeSession}
|
|
1324
|
+
<button type="button" onclick={finishTyping} class="self-start rounded bg-slate-800 px-2 py-1 text-[11px] text-slate-200 hover:bg-slate-700">Done editing text</button>
|
|
1325
|
+
{/if}
|
|
1326
|
+
{/if}
|
|
1327
|
+
{#if shapeTypeError}
|
|
1328
|
+
<p role="alert" class="text-rose-400" data-testid="shape-type-error">{shapeTypeError}</p>
|
|
1329
|
+
{/if}
|
|
1330
|
+
<div class="h-px bg-slate-800 my-1"></div>
|
|
1331
|
+
|
|
1195
1332
|
<span class="font-semibold text-slate-200 uppercase tracking-wider text-[11px]">
|
|
1196
1333
|
Draw & Annotate
|
|
1197
1334
|
</span>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/ImageEditor.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAIR,aAAa,EAGb,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACf,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"ImageEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/ImageEditor.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAIR,aAAa,EAGb,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACf,MAAM,+BAA+B,CAAC;AAsCzC,OAAO,EAA6B,KAAK,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAGjG,UAAU,KAAK;IACb,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;IAC1C,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,GAAG,CAAC,EAAE,cAAc,CAAC;CACtB;AAm2CH,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
dropZone,
|
|
9
9
|
layerBadge,
|
|
10
10
|
layerEffectsBadge,
|
|
11
|
+
layerHasThumbnail,
|
|
12
|
+
layerWarning,
|
|
11
13
|
layerMenu,
|
|
12
14
|
layerPanelRows,
|
|
13
15
|
openPsdFile,
|
|
@@ -136,7 +138,7 @@
|
|
|
136
138
|
|
|
137
139
|
function thumb(layer: ImageLayer): PixelBuffer | null {
|
|
138
140
|
void tick;
|
|
139
|
-
return layer
|
|
141
|
+
return layerHasThumbnail(layer) ? controller.getLayerThumbnail(layer.id, { maxSide: 64 }) : null;
|
|
140
142
|
}
|
|
141
143
|
function maskThumb(layer: ImageLayer): PixelBuffer | null {
|
|
142
144
|
void tick;
|
|
@@ -314,13 +316,20 @@
|
|
|
314
316
|
}}>{layer.collapsed ? '▸' : '▾'}</button>
|
|
315
317
|
{/if}
|
|
316
318
|
{#if row.clipped}<span class="text-slate-500" aria-label="Clipped">↳</span>{/if}
|
|
317
|
-
{#if layer
|
|
318
|
-
<
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
319
|
+
{#if layerHasThumbnail(layer)}
|
|
320
|
+
<span class="relative shrink-0">
|
|
321
|
+
<canvas
|
|
322
|
+
use:paint={thumb(layer)}
|
|
323
|
+
aria-label={`${row.label} pixels`}
|
|
324
|
+
style="width: {thumbnailSize}px; height: {thumbnailSize}px"
|
|
325
|
+
class={thumbClass(row.active && snapshot.activeTarget === 'pixels' && Boolean(layer.mask))}
|
|
326
|
+
></canvas>
|
|
327
|
+
{#if layer.type !== 'pixel'}
|
|
328
|
+
<span
|
|
329
|
+
data-testid="layer-kind-badge"
|
|
330
|
+
class="absolute -bottom-1 -right-1 rounded bg-slate-700 px-0.5 text-[8px] font-bold leading-tight text-slate-100 ring-1 ring-slate-900">{layerBadge(layer)}</span>
|
|
331
|
+
{/if}
|
|
332
|
+
</span>
|
|
324
333
|
{:else}
|
|
325
334
|
<span
|
|
326
335
|
class="shrink-0 grid place-items-center rounded bg-slate-800 text-[9px] text-slate-400 ring-1 ring-slate-700"
|
|
@@ -382,6 +391,9 @@
|
|
|
382
391
|
renaming = layer.id;
|
|
383
392
|
}}>{row.label}</span>
|
|
384
393
|
{/if}
|
|
394
|
+
{#if layerWarning(layer)}
|
|
395
|
+
<span role="img" data-testid="layer-warning" aria-label={layerWarning(layer)} title={layerWarning(layer)} class="text-[10px] text-amber-400">⚠</span>
|
|
396
|
+
{/if}
|
|
385
397
|
{#if layerEffectsBadge(layer)}
|
|
386
398
|
{@const fxOn = layerEffectsBadge(layer) === 'on'}
|
|
387
399
|
<button
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LayersPanel.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/LayersPanel.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAA2D,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"LayersPanel.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/LayersPanel.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAA2D,MAAM,+BAA+B,CAAC;AAoBlI,UAAU,KAAK;IACb,UAAU,EAAE,qBAAqB,CAAC;IAClC,yCAAyC;IACzC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAgSH,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ImageEditorController, ShapeToolOptions } from '@pixerate/editor/image-editor';
|
|
3
|
+
import { LIVE_SHAPE_KINDS, SHAPE_KIND_LABELS, applyShapeOptions } from '@pixerate/editor/image-editor';
|
|
4
|
+
import { untrack } from 'svelte';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The shape tool's options: the kind to draw, fill and stroke (colour or none), stroke width and
|
|
8
|
+
* the kind's own settings (corner radius, sides, points, line weight and arrowhead). Changes also
|
|
9
|
+
* apply to the active shape layer, as one undo step each.
|
|
10
|
+
*/
|
|
11
|
+
interface Props {
|
|
12
|
+
controller: ImageEditorController;
|
|
13
|
+
options: ShapeToolOptions;
|
|
14
|
+
onchange: (options: ShapeToolOptions) => void;
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let { controller, options, onchange, class: className = '' }: Props = $props();
|
|
19
|
+
|
|
20
|
+
let tick = $state(0);
|
|
21
|
+
$effect(() => {
|
|
22
|
+
const c = controller;
|
|
23
|
+
return untrack(() => c.subscribe(() => tick++));
|
|
24
|
+
});
|
|
25
|
+
const shapeId = $derived.by(() => {
|
|
26
|
+
void tick;
|
|
27
|
+
const a = controller.getActiveLayer();
|
|
28
|
+
return a?.type === 'shape' ? a.id : null;
|
|
29
|
+
});
|
|
30
|
+
let error = $state<string | null>(null);
|
|
31
|
+
|
|
32
|
+
const chip = (on: boolean) => `px-2 py-1 rounded text-[11px] ${on ? 'bg-indigo-600 text-white' : 'bg-slate-800 text-slate-300 hover:text-white'}`;
|
|
33
|
+
const num = (v: string, min: number, max: number) => Math.max(min, Math.min(max, Number(v) || min));
|
|
34
|
+
|
|
35
|
+
function set(change: Partial<ShapeToolOptions>) {
|
|
36
|
+
onchange({ ...options, ...change });
|
|
37
|
+
if (!shapeId) return;
|
|
38
|
+
try {
|
|
39
|
+
error = null;
|
|
40
|
+
applyShapeOptions(controller, shapeId, change);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
error = e instanceof Error ? e.message : String(e);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const value = (e: Event) => (e.target as HTMLInputElement).value;
|
|
46
|
+
const checked = (e: Event) => (e.target as HTMLInputElement).checked;
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<div class="flex flex-col gap-2 text-xs text-slate-200 {className}" data-testid="shape-options">
|
|
50
|
+
<div class="flex flex-wrap gap-1" role="toolbar" aria-label="Shape kind">
|
|
51
|
+
{#each LIVE_SHAPE_KINDS as k (k)}
|
|
52
|
+
<button type="button" aria-pressed={options.kind === k} class={chip(options.kind === k)} onclick={() => onchange({ ...options, kind: k })}>{SHAPE_KIND_LABELS[k]}</button>
|
|
53
|
+
{/each}
|
|
54
|
+
</div>
|
|
55
|
+
<div class="flex flex-wrap items-center gap-3">
|
|
56
|
+
<label class="flex items-center gap-1">
|
|
57
|
+
<input type="checkbox" aria-label="Fill" checked={options.fill !== null} onchange={(e) => set({ fill: checked(e) ? '#4f46e5' : null })} />
|
|
58
|
+
Fill
|
|
59
|
+
<input
|
|
60
|
+
type="color"
|
|
61
|
+
aria-label="Fill colour"
|
|
62
|
+
disabled={options.fill === null}
|
|
63
|
+
value={options.fill ?? '#4f46e5'}
|
|
64
|
+
oninput={(e) => set({ fill: value(e) })}
|
|
65
|
+
class="h-6 w-8 rounded border border-slate-700 bg-transparent"
|
|
66
|
+
/>
|
|
67
|
+
</label>
|
|
68
|
+
<label class="flex items-center gap-1">
|
|
69
|
+
<input type="checkbox" aria-label="Stroke" checked={options.stroke !== null} onchange={(e) => set({ stroke: checked(e) ? '#000000' : null })} />
|
|
70
|
+
Stroke
|
|
71
|
+
<input
|
|
72
|
+
type="color"
|
|
73
|
+
aria-label="Stroke colour"
|
|
74
|
+
disabled={options.stroke === null}
|
|
75
|
+
value={options.stroke ?? '#000000'}
|
|
76
|
+
oninput={(e) => set({ stroke: value(e) })}
|
|
77
|
+
class="h-6 w-8 rounded border border-slate-700 bg-transparent"
|
|
78
|
+
/>
|
|
79
|
+
</label>
|
|
80
|
+
<label class="flex items-center gap-1">
|
|
81
|
+
Width
|
|
82
|
+
<input type="number" aria-label="Stroke width" min="0" max="200" value={options.strokeWidth} oninput={(e) => set({ strokeWidth: num(value(e), 0, 200) })} class="w-14 rounded border border-slate-700 bg-slate-950 px-1 py-0.5" />
|
|
83
|
+
</label>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="flex flex-wrap items-center gap-3">
|
|
86
|
+
{#if options.kind === 'rect'}
|
|
87
|
+
<label class="flex items-center gap-1">
|
|
88
|
+
Radius
|
|
89
|
+
<input type="number" aria-label="Corner radius" min="0" max="1000" value={options.radius} oninput={(e) => set({ radius: num(value(e), 0, 1000) })} class="w-14 rounded border border-slate-700 bg-slate-950 px-1 py-0.5" />
|
|
90
|
+
</label>
|
|
91
|
+
{:else if options.kind === 'polygon'}
|
|
92
|
+
<label class="flex items-center gap-1">
|
|
93
|
+
Sides
|
|
94
|
+
<input type="number" aria-label="Sides" min="3" max="100" value={options.sides} oninput={(e) => set({ sides: num(value(e), 3, 100) })} class="w-14 rounded border border-slate-700 bg-slate-950 px-1 py-0.5" />
|
|
95
|
+
</label>
|
|
96
|
+
{:else if options.kind === 'star'}
|
|
97
|
+
<label class="flex items-center gap-1">
|
|
98
|
+
Points
|
|
99
|
+
<input type="number" aria-label="Points" min="3" max="100" value={options.points} oninput={(e) => set({ points: num(value(e), 3, 100) })} class="w-14 rounded border border-slate-700 bg-slate-950 px-1 py-0.5" />
|
|
100
|
+
</label>
|
|
101
|
+
<label class="flex items-center gap-1">
|
|
102
|
+
Inner
|
|
103
|
+
<input type="range" aria-label="Inner radius" min="0.05" max="1" step="0.05" value={options.innerRatio} oninput={(e) => set({ innerRatio: Number(value(e)) })} class="accent-indigo-500" />
|
|
104
|
+
</label>
|
|
105
|
+
{:else if options.kind === 'line'}
|
|
106
|
+
<label class="flex items-center gap-1">
|
|
107
|
+
Weight
|
|
108
|
+
<input type="number" aria-label="Line weight" min="1" max="200" value={options.weight} oninput={(e) => set({ weight: num(value(e), 1, 200) })} class="w-14 rounded border border-slate-700 bg-slate-950 px-1 py-0.5" />
|
|
109
|
+
</label>
|
|
110
|
+
<label class="flex items-center gap-1">
|
|
111
|
+
<input type="checkbox" aria-label="Arrowhead" checked={options.arrow} onchange={(e) => set({ arrow: checked(e) })} />
|
|
112
|
+
Arrowhead
|
|
113
|
+
</label>
|
|
114
|
+
{/if}
|
|
115
|
+
</div>
|
|
116
|
+
<p class="text-[10px] text-slate-500">Drag on the image to draw. Shift: square, circle or 45° line. Alt: from the centre.</p>
|
|
117
|
+
{#if error}
|
|
118
|
+
<p role="alert" class="text-rose-400">{error}</p>
|
|
119
|
+
{/if}
|
|
120
|
+
</div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ImageEditorController, ShapeToolOptions } from '@pixerate/editor/image-editor';
|
|
2
|
+
/**
|
|
3
|
+
* The shape tool's options: the kind to draw, fill and stroke (colour or none), stroke width and
|
|
4
|
+
* the kind's own settings (corner radius, sides, points, line weight and arrowhead). Changes also
|
|
5
|
+
* apply to the active shape layer, as one undo step each.
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
controller: ImageEditorController;
|
|
9
|
+
options: ShapeToolOptions;
|
|
10
|
+
onchange: (options: ShapeToolOptions) => void;
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
declare const ShapeOptionsBar: import("svelte").Component<Props, {}, "">;
|
|
14
|
+
type ShapeOptionsBar = ReturnType<typeof ShapeOptionsBar>;
|
|
15
|
+
export default ShapeOptionsBar;
|
|
16
|
+
//# sourceMappingURL=ShapeOptionsBar.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShapeOptionsBar.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/ShapeOptionsBar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAK3F;;;;GAIG;AACH,UAAU,KAAK;IACb,UAAU,EAAE,qBAAqB,CAAC;IAClC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,QAAQ,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAmGH,QAAA,MAAM,eAAe,2CAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ImageEditorController, TextAlign, TextEditSession, TextStyle, TypeToolOptions } from '@pixerate/editor/image-editor';
|
|
3
|
+
import { TEXT_ALIGNS } from '@pixerate/editor/image-editor';
|
|
4
|
+
import { untrack } from 'svelte';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The type tool's options: font family (from the editor's fonts), size, bold, italic, colour and
|
|
8
|
+
* alignment. While text is being edited they style the selection (or the whole text); with a
|
|
9
|
+
* type layer active they style all of it; otherwise they set what the next text uses.
|
|
10
|
+
*/
|
|
11
|
+
interface Props {
|
|
12
|
+
controller: ImageEditorController;
|
|
13
|
+
options: TypeToolOptions;
|
|
14
|
+
onchange: (options: TypeToolOptions) => void;
|
|
15
|
+
/** The text being edited on the canvas: changes style its selection (or all of it). */
|
|
16
|
+
session?: TextEditSession | null;
|
|
17
|
+
class?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let { controller, options, onchange, session = null, class: className = '' }: Props = $props();
|
|
21
|
+
|
|
22
|
+
let tick = $state(0);
|
|
23
|
+
$effect(() => {
|
|
24
|
+
const c = controller;
|
|
25
|
+
return untrack(() => c.subscribe(() => tick++));
|
|
26
|
+
});
|
|
27
|
+
$effect(() => {
|
|
28
|
+
const s = session;
|
|
29
|
+
if (!s) return;
|
|
30
|
+
return untrack(() => s.subscribe(() => tick++));
|
|
31
|
+
});
|
|
32
|
+
const ALIGN_LABELS: Record<TextAlign, string> = { left: 'Left', center: 'Centre', right: 'Right', justify: 'Justify' };
|
|
33
|
+
const chip = (on: boolean) => `px-2 py-1 rounded text-[11px] ${on ? 'bg-indigo-600 text-white' : 'bg-slate-800 text-slate-300 hover:text-white'}`;
|
|
34
|
+
|
|
35
|
+
const view = $derived.by(() => {
|
|
36
|
+
void tick;
|
|
37
|
+
const active = controller.getActiveLayer();
|
|
38
|
+
const here: Partial<TextStyle> = session?.alive ? session.styleHere() : {};
|
|
39
|
+
const shown: TypeToolOptions = {
|
|
40
|
+
...options,
|
|
41
|
+
...(here.fontFamily ? { fontFamily: here.fontFamily } : {}),
|
|
42
|
+
...(here.fontSize ? { fontSize: here.fontSize } : {}),
|
|
43
|
+
...(here.fontWeight ? { fontWeight: here.fontWeight } : {}),
|
|
44
|
+
...(session?.alive ? { italic: Boolean(here.italic), color: here.color ?? options.color } : {}),
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
shown,
|
|
48
|
+
typeId: !session && active?.type === 'type' ? active.id : null,
|
|
49
|
+
missing: active?.type === 'type' ? (active.missingFonts ?? []) : [],
|
|
50
|
+
fonts: controller.listFonts(),
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
let error = $state<string | null>(null);
|
|
54
|
+
|
|
55
|
+
function style(change: Partial<TypeToolOptions>) {
|
|
56
|
+
onchange({ ...options, ...change });
|
|
57
|
+
const s: Record<string, unknown> = {};
|
|
58
|
+
for (const k of ['fontFamily', 'fontSize', 'fontWeight', 'italic', 'color'] as const) if (k in change) s[k] = change[k];
|
|
59
|
+
const p = change.align ? { align: change.align } : null;
|
|
60
|
+
try {
|
|
61
|
+
error = null;
|
|
62
|
+
if (session?.alive) {
|
|
63
|
+
if (Object.keys(s).length) session.setStyle(s as Partial<TextStyle>);
|
|
64
|
+
if (p) session.setParagraph(p);
|
|
65
|
+
if (session.error) error = session.error;
|
|
66
|
+
} else if (view.typeId) {
|
|
67
|
+
if (Object.keys(s).length) controller.setTextStyle(view.typeId, s);
|
|
68
|
+
if (p) controller.setParagraphStyle(view.typeId, p);
|
|
69
|
+
}
|
|
70
|
+
} catch (e) {
|
|
71
|
+
error = e instanceof Error ? e.message : String(e);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const value = (e: Event) => (e.target as HTMLInputElement).value;
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<div class="flex flex-col gap-2 text-xs text-slate-200 {className}" data-testid="type-options">
|
|
78
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
79
|
+
<label class="flex items-center gap-1">
|
|
80
|
+
Font
|
|
81
|
+
<input list="pixerate-type-fonts" aria-label="Font family" value={view.shown.fontFamily} oninput={(e) => value(e).trim() && style({ fontFamily: value(e) })} class="w-32 rounded border border-slate-700 bg-slate-950 px-1 py-0.5" />
|
|
82
|
+
<datalist id="pixerate-type-fonts">
|
|
83
|
+
{#each view.fonts as f (f)}
|
|
84
|
+
<option value={f}></option>
|
|
85
|
+
{/each}
|
|
86
|
+
</datalist>
|
|
87
|
+
</label>
|
|
88
|
+
<label class="flex items-center gap-1">
|
|
89
|
+
Size
|
|
90
|
+
<input
|
|
91
|
+
type="number"
|
|
92
|
+
aria-label="Font size"
|
|
93
|
+
min="1"
|
|
94
|
+
max="2000"
|
|
95
|
+
value={Math.round(view.shown.fontSize * 10) / 10}
|
|
96
|
+
oninput={(e) => Number(value(e)) > 0 && style({ fontSize: Number(value(e)) })}
|
|
97
|
+
class="w-16 rounded border border-slate-700 bg-slate-950 px-1 py-0.5"
|
|
98
|
+
/>
|
|
99
|
+
</label>
|
|
100
|
+
<button type="button" aria-pressed={view.shown.fontWeight >= 600} class="{chip(view.shown.fontWeight >= 600)} font-bold" onclick={() => style({ fontWeight: view.shown.fontWeight >= 600 ? 400 : 700 })}>B</button>
|
|
101
|
+
<button type="button" aria-pressed={view.shown.italic} class="{chip(view.shown.italic)} italic" onclick={() => style({ italic: !view.shown.italic })}>I</button>
|
|
102
|
+
<input type="color" aria-label="Text colour" value={view.shown.color} oninput={(e) => style({ color: value(e) })} class="h-6 w-8 rounded border border-slate-700 bg-transparent" />
|
|
103
|
+
</div>
|
|
104
|
+
<div class="flex gap-1" role="toolbar" aria-label="Alignment">
|
|
105
|
+
{#each TEXT_ALIGNS as a (a)}
|
|
106
|
+
<button type="button" aria-pressed={options.align === a} class={chip(options.align === a)} onclick={() => style({ align: a })}>{ALIGN_LABELS[a]}</button>
|
|
107
|
+
{/each}
|
|
108
|
+
</div>
|
|
109
|
+
<p class="text-[10px] text-slate-500">Click to type point text, drag for a paragraph box, click text to edit it. Esc finishes.</p>
|
|
110
|
+
{#if view.missing.length > 0}
|
|
111
|
+
<p role="status" class="text-amber-400" data-testid="type-missing-fonts">Missing {view.missing.length === 1 ? 'font' : 'fonts'}: {view.missing.join(', ')}</p>
|
|
112
|
+
{/if}
|
|
113
|
+
{#if error}
|
|
114
|
+
<p role="alert" class="text-rose-400">{error}</p>
|
|
115
|
+
{/if}
|
|
116
|
+
</div>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ImageEditorController, TextEditSession, TypeToolOptions } from '@pixerate/editor/image-editor';
|
|
2
|
+
/**
|
|
3
|
+
* The type tool's options: font family (from the editor's fonts), size, bold, italic, colour and
|
|
4
|
+
* alignment. While text is being edited they style the selection (or the whole text); with a
|
|
5
|
+
* type layer active they style all of it; otherwise they set what the next text uses.
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
controller: ImageEditorController;
|
|
9
|
+
options: TypeToolOptions;
|
|
10
|
+
onchange: (options: TypeToolOptions) => void;
|
|
11
|
+
/** The text being edited on the canvas: changes style its selection (or all of it). */
|
|
12
|
+
session?: TextEditSession | null;
|
|
13
|
+
class?: string;
|
|
14
|
+
}
|
|
15
|
+
declare const TypeOptionsBar: import("svelte").Component<Props, {}, "">;
|
|
16
|
+
type TypeOptionsBar = ReturnType<typeof TypeOptionsBar>;
|
|
17
|
+
export default TypeOptionsBar;
|
|
18
|
+
//# sourceMappingURL=TypeOptionsBar.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TypeOptionsBar.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/TypeOptionsBar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAa,eAAe,EAAa,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAKjI;;;;GAIG;AACH,UAAU,KAAK;IACb,UAAU,EAAE,qBAAqB,CAAC;IAClC,OAAO,EAAE,eAAe,CAAC;IACzB,QAAQ,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAC7C,uFAAuF;IACvF,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAmGH,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -10,6 +10,8 @@ import type LayersPanelComponent from "./LayersPanel.svelte";
|
|
|
10
10
|
import type LayerEffectsPanelComponent from "./LayerEffectsPanel.svelte";
|
|
11
11
|
import type BrushOptionsBarComponent from "./BrushOptionsBar.svelte";
|
|
12
12
|
import type FreeTransformOptionsBarComponent from "./FreeTransformOptionsBar.svelte";
|
|
13
|
+
import type ShapeOptionsBarComponent from "./ShapeOptionsBar.svelte";
|
|
14
|
+
import type TypeOptionsBarComponent from "./TypeOptionsBar.svelte";
|
|
13
15
|
export { default as ImageEditor } from './ImageEditor.svelte';
|
|
14
16
|
export { default as Histogram } from './Histogram.svelte';
|
|
15
17
|
export { default as AdjustmentLayersPanel } from './AdjustmentLayersPanel.svelte';
|
|
@@ -21,6 +23,8 @@ export { default as LayersPanel } from './LayersPanel.svelte';
|
|
|
21
23
|
export { default as LayerEffectsPanel } from './LayerEffectsPanel.svelte';
|
|
22
24
|
export { default as BrushOptionsBar } from './BrushOptionsBar.svelte';
|
|
23
25
|
export { default as FreeTransformOptionsBar } from './FreeTransformOptionsBar.svelte';
|
|
26
|
+
export { default as ShapeOptionsBar } from './ShapeOptionsBar.svelte';
|
|
27
|
+
export { default as TypeOptionsBar } from './TypeOptionsBar.svelte';
|
|
24
28
|
export * from './imageEditorState.svelte.js';
|
|
25
29
|
export type ImageEditorProps = ComponentProps<typeof ImageEditorComponent>;
|
|
26
30
|
export type HistogramProps = ComponentProps<typeof HistogramComponent>;
|
|
@@ -33,5 +37,7 @@ export type LayersPanelProps = ComponentProps<typeof LayersPanelComponent>;
|
|
|
33
37
|
export type LayerEffectsPanelProps = ComponentProps<typeof LayerEffectsPanelComponent>;
|
|
34
38
|
export type BrushOptionsBarProps = ComponentProps<typeof BrushOptionsBarComponent>;
|
|
35
39
|
export type FreeTransformOptionsBarProps = ComponentProps<typeof FreeTransformOptionsBarComponent>;
|
|
40
|
+
export type ShapeOptionsBarProps = ComponentProps<typeof ShapeOptionsBarComponent>;
|
|
41
|
+
export type TypeOptionsBarProps = ComponentProps<typeof TypeOptionsBarComponent>;
|
|
36
42
|
export { applyRegionMosaic, applyRegionBlur, resolveMosaicRegionBounds, type MosaicRegion, type MosaicImageDataLike } from '@pixerate/editor/image-editor';
|
|
37
43
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/image-editor/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,oBAAoB,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,kBAAkB,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,8BAA8B,MAAM,gCAAgC,CAAC;AACjF,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,sBAAsB,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,4BAA4B,MAAM,8BAA8B,CAAC;AAC7E,OAAO,KAAK,wBAAwB,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,oBAAoB,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,0BAA0B,MAAM,4BAA4B,CAAC;AACzE,OAAO,KAAK,wBAAwB,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,gCAAgC,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/image-editor/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,5 +9,7 @@ export { default as LayersPanel } from './LayersPanel.svelte';
|
|
|
9
9
|
export { default as LayerEffectsPanel } from './LayerEffectsPanel.svelte';
|
|
10
10
|
export { default as BrushOptionsBar } from './BrushOptionsBar.svelte';
|
|
11
11
|
export { default as FreeTransformOptionsBar } from './FreeTransformOptionsBar.svelte';
|
|
12
|
+
export { default as ShapeOptionsBar } from './ShapeOptionsBar.svelte';
|
|
13
|
+
export { default as TypeOptionsBar } from './TypeOptionsBar.svelte';
|
|
12
14
|
export * from './imageEditorState.svelte.js';
|
|
13
15
|
export { applyRegionMosaic, applyRegionBlur, resolveMosaicRegionBounds } from '@pixerate/editor/image-editor';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixerate/editor-svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.81.0",
|
|
4
4
|
"description": "Svelte bindings and prompt/rich-text components for @pixerate/editor",
|
|
5
5
|
"author": "Pixerate",
|
|
6
6
|
"license": "MIT",
|
|
@@ -139,7 +139,7 @@
|
|
|
139
139
|
"@tiptap/extension-bubble-menu": "^2.11.5",
|
|
140
140
|
"@tiptap/extension-placeholder": "^2.11.5",
|
|
141
141
|
"@tiptap/starter-kit": "^2.11.5",
|
|
142
|
-
"@pixerate/editor": "0.
|
|
142
|
+
"@pixerate/editor": "0.112.0"
|
|
143
143
|
},
|
|
144
144
|
"peerDependencies": {
|
|
145
145
|
"@dagrejs/dagre": "^1.1.4",
|