@pixerate/editor-svelte 0.110.0 → 0.110.1
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.
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { imageChipClass, imageColorInputClass } from './styles.js';
|
|
3
3
|
import type { ImageEditorController } from '@pixerate/editor/image-editor';
|
|
4
4
|
import {
|
|
5
|
+
userErrorMessage,
|
|
5
6
|
BRUSH_OPTION_CONTROLS,
|
|
6
7
|
MIXER_MIXING_OPTIONS,
|
|
7
8
|
MIXER_OPTION_CONTROLS,
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
error = null;
|
|
50
51
|
fn();
|
|
51
52
|
} catch (err) {
|
|
52
|
-
error =
|
|
53
|
+
error = userErrorMessage(err);
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
</script>
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
pointerPressure,
|
|
18
18
|
FreeTransformTool,
|
|
19
19
|
getViewportProjection,
|
|
20
|
+
measureCanvasViewport,
|
|
21
|
+
watchCanvasViewport,
|
|
20
22
|
ShapeDrawGesture,
|
|
21
23
|
TypeToolGesture,
|
|
22
24
|
DEFAULT_SHAPE_TOOL_OPTIONS,
|
|
@@ -117,6 +119,11 @@
|
|
|
117
119
|
let canvasEl = $state<HTMLCanvasElement | null>(null);
|
|
118
120
|
let containerEl = $state<HTMLDivElement | null>(null);
|
|
119
121
|
let rootEl = $state<HTMLDivElement | null>(null);
|
|
122
|
+
/**
|
|
123
|
+
* The viewport in CSS pixels as last rendered. The backing store is larger (device pixel ratio,
|
|
124
|
+
* CSS transforms), so pointer mapping and the projection use this, never `canvasEl.width`.
|
|
125
|
+
*/
|
|
126
|
+
let view = { width: 0, height: 0 };
|
|
120
127
|
const report = (source: ImageEditorCanvasErrorSource, message: string | null) => onerror?.(source, message);
|
|
121
128
|
|
|
122
129
|
// Agent playback: the agent's cursor in document pixels, drawn over the canvas without clipping so
|
|
@@ -125,19 +132,19 @@
|
|
|
125
132
|
const playing = $derived((playback === undefined ? ambientPlayback : playback) ?? null);
|
|
126
133
|
/** Document pixels → pixels in the canvas root, through the canvas projection. */
|
|
127
134
|
const docToRoot = (p: PlaybackPoint): PlaybackPoint => {
|
|
128
|
-
if (!canvasEl || !rootEl || !
|
|
135
|
+
if (!canvasEl || !rootEl || !view.width) return p;
|
|
129
136
|
const r = canvasEl.getBoundingClientRect();
|
|
130
137
|
const box = rootEl.getBoundingClientRect();
|
|
131
|
-
const c = imageToCanvasPoint(p, editor.controller.getState(),
|
|
132
|
-
return { x: r.left - box.left + (c.x * r.width) /
|
|
138
|
+
const c = imageToCanvasPoint(p, editor.controller.getState(), view.width, view.height, mode === 'crop');
|
|
139
|
+
return { x: r.left - box.left + (c.x * r.width) / view.width, y: r.top - box.top + (c.y * r.height) / view.height };
|
|
133
140
|
};
|
|
134
141
|
/** Viewport (client) pixels → document pixels. */
|
|
135
142
|
const clientToDoc = (p: PlaybackPoint): PlaybackPoint => {
|
|
136
|
-
if (!canvasEl || !
|
|
143
|
+
if (!canvasEl || !view.width) return p;
|
|
137
144
|
const r = canvasEl.getBoundingClientRect();
|
|
138
|
-
const sx = r.width > 0 ?
|
|
139
|
-
const sy = r.height > 0 ?
|
|
140
|
-
return canvasToImagePoint({ x: (p.x - r.left) * sx, y: (p.y - r.top) * sy }, editor.controller.getState(),
|
|
145
|
+
const sx = r.width > 0 ? view.width / r.width : 1;
|
|
146
|
+
const sy = r.height > 0 ? view.height / r.height : 1;
|
|
147
|
+
return canvasToImagePoint({ x: (p.x - r.left) * sx, y: (p.y - r.top) * sy }, editor.controller.getState(), view.width, view.height, mode === 'crop');
|
|
141
148
|
};
|
|
142
149
|
/** A client box as a rectangle in document pixels. */
|
|
143
150
|
const clientRect = (r: DOMRect | null | undefined): PlaybackRect | null => {
|
|
@@ -274,12 +281,12 @@
|
|
|
274
281
|
/** The document point of a pointer event and the hit tolerance (6 screen px). */
|
|
275
282
|
function transformPoint(e: PointerEvent) {
|
|
276
283
|
const rect = canvasEl!.getBoundingClientRect();
|
|
277
|
-
const sx = rect.width > 0 ?
|
|
278
|
-
const sy = rect.height > 0 ?
|
|
284
|
+
const sx = rect.width > 0 ? view.width / rect.width : 1;
|
|
285
|
+
const sy = rect.height > 0 ? view.height / rect.height : 1;
|
|
279
286
|
const s = editor.controller.getState();
|
|
280
287
|
const cropping = mode === 'crop';
|
|
281
|
-
const projection = getViewportProjection(s,
|
|
282
|
-
const pt = canvasToImagePoint({ x: (e.clientX - rect.left) * sx, y: (e.clientY - rect.top) * sy }, s,
|
|
288
|
+
const projection = getViewportProjection(s, view.width, view.height, cropping);
|
|
289
|
+
const pt = canvasToImagePoint({ x: (e.clientX - rect.left) * sx, y: (e.clientY - rect.top) * sy }, s, view.width, view.height, cropping);
|
|
283
290
|
transformTool.setView(projection.scale);
|
|
284
291
|
return { pt, tolerance: 6 / (projection.scale || 1) };
|
|
285
292
|
}
|
|
@@ -358,12 +365,13 @@
|
|
|
358
365
|
|
|
359
366
|
async function renderCanvas(overrideAnnotation?: Annotation | null, overrideCrop?: CropBox | null) {
|
|
360
367
|
if (!canvasEl || !containerEl) return;
|
|
361
|
-
const
|
|
362
|
-
|
|
368
|
+
const viewport = measureCanvasViewport(containerEl);
|
|
369
|
+
view = { width: viewport.width, height: viewport.height };
|
|
363
370
|
|
|
364
371
|
await editor.controller.render(canvasEl, {
|
|
365
|
-
viewportWidth:
|
|
366
|
-
viewportHeight:
|
|
372
|
+
viewportWidth: viewport.width,
|
|
373
|
+
viewportHeight: viewport.height,
|
|
374
|
+
pixelRatio: viewport.pixelRatio,
|
|
367
375
|
fit,
|
|
368
376
|
showCropOverlay: mode === 'crop',
|
|
369
377
|
showSelectionOverlay: true,
|
|
@@ -385,11 +393,11 @@
|
|
|
385
393
|
const events = typeof e.getCoalescedEvents === 'function' ? e.getCoalescedEvents() : [];
|
|
386
394
|
const list = events.length ? events : [e];
|
|
387
395
|
const rect = canvasEl!.getBoundingClientRect();
|
|
388
|
-
const sx = rect.width > 0 ?
|
|
389
|
-
const sy = rect.height > 0 ?
|
|
396
|
+
const sx = rect.width > 0 ? view.width / rect.width : 1;
|
|
397
|
+
const sy = rect.height > 0 ? view.height / rect.height : 1;
|
|
390
398
|
const s = editor.controller.getState();
|
|
391
399
|
return list.map((ev) => ({
|
|
392
|
-
...canvasToImagePoint({ x: (ev.clientX - rect.left) * sx, y: (ev.clientY - rect.top) * sy }, s,
|
|
400
|
+
...canvasToImagePoint({ x: (ev.clientX - rect.left) * sx, y: (ev.clientY - rect.top) * sy }, s, view.width, view.height, false),
|
|
393
401
|
pressure: pointerPressure(ev),
|
|
394
402
|
}));
|
|
395
403
|
}
|
|
@@ -450,24 +458,17 @@
|
|
|
450
458
|
|
|
451
459
|
$effect(() => {
|
|
452
460
|
if (!containerEl) return;
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
observer.observe(containerEl);
|
|
458
|
-
return () => observer.disconnect();
|
|
459
|
-
} else {
|
|
460
|
-
const handleResize = () => renderCanvas();
|
|
461
|
-
window.addEventListener('resize', handleResize);
|
|
462
|
-
return () => window.removeEventListener('resize', handleResize);
|
|
463
|
-
}
|
|
461
|
+
const { width, height } = measureCanvasViewport(containerEl);
|
|
462
|
+
view = { width, height };
|
|
463
|
+
// Re-render when the layout size, the device pixel ratio or a host's CSS transform scale changes.
|
|
464
|
+
return watchCanvasViewport(containerEl, () => renderCanvas());
|
|
464
465
|
});
|
|
465
466
|
|
|
466
467
|
function getCanvasEventPoint(e: MouseEvent): ImagePoint {
|
|
467
468
|
if (!canvasEl) return { x: 0, y: 0 };
|
|
468
469
|
const rect = canvasEl.getBoundingClientRect();
|
|
469
|
-
const scaleX = rect.width > 0 ?
|
|
470
|
-
const scaleY = rect.height > 0 ?
|
|
470
|
+
const scaleX = rect.width > 0 ? view.width / rect.width : 1;
|
|
471
|
+
const scaleY = rect.height > 0 ? view.height / rect.height : 1;
|
|
471
472
|
return {
|
|
472
473
|
x: (e.clientX - rect.left) * scaleX,
|
|
473
474
|
y: (e.clientY - rect.top) * scaleY,
|
|
@@ -480,7 +481,7 @@
|
|
|
480
481
|
|
|
481
482
|
if (mode === 'select') {
|
|
482
483
|
const gesture = selectionGesture;
|
|
483
|
-
const imgPt = canvasToImagePoint(canvasPt, editor.state,
|
|
484
|
+
const imgPt = canvasToImagePoint(canvasPt, editor.state, view.width, view.height, false);
|
|
484
485
|
void gesture.begin(imgPt, e).then(() => report('select', gesture.error));
|
|
485
486
|
return;
|
|
486
487
|
}
|
|
@@ -492,8 +493,8 @@
|
|
|
492
493
|
width: editor.state.imageDimensions.width,
|
|
493
494
|
height: editor.state.imageDimensions.height,
|
|
494
495
|
};
|
|
495
|
-
const handle = hitTestCrop(canvasPt, editor.state,
|
|
496
|
-
const startImgPt = canvasToImagePoint(canvasPt, editor.state,
|
|
496
|
+
const handle = hitTestCrop(canvasPt, editor.state, view.width, view.height, 14, activeCrop);
|
|
497
|
+
const startImgPt = canvasToImagePoint(canvasPt, editor.state, view.width, view.height, true);
|
|
497
498
|
|
|
498
499
|
if (handle) {
|
|
499
500
|
cropDragInfo = {
|
|
@@ -515,7 +516,7 @@
|
|
|
515
516
|
}
|
|
516
517
|
|
|
517
518
|
if (mode === 'annotate' && (editor.state.activeTool === 'shape' || editor.state.activeTool === 'type')) {
|
|
518
|
-
const imgPt = canvasToImagePoint(canvasPt, editor.state,
|
|
519
|
+
const imgPt = canvasToImagePoint(canvasPt, editor.state, view.width, view.height, false);
|
|
519
520
|
report(editor.state.activeTool, null);
|
|
520
521
|
if (editor.state.activeTool === 'shape') shapeGesture.down(imgPt);
|
|
521
522
|
else {
|
|
@@ -527,7 +528,7 @@
|
|
|
527
528
|
}
|
|
528
529
|
|
|
529
530
|
if (mode === 'annotate') {
|
|
530
|
-
const imgPt = canvasToImagePoint(canvasPt, editor.state,
|
|
531
|
+
const imgPt = canvasToImagePoint(canvasPt, editor.state, view.width, view.height, false);
|
|
531
532
|
dragStartPoint = imgPt;
|
|
532
533
|
|
|
533
534
|
if (editor.state.activeTool === 'select') {
|
|
@@ -571,7 +572,7 @@
|
|
|
571
572
|
|
|
572
573
|
if (mode === 'select') {
|
|
573
574
|
const gesture = selectionGesture;
|
|
574
|
-
if (gesture.active && gesture.move(canvasToImagePoint(canvasPt, editor.state,
|
|
575
|
+
if (gesture.active && gesture.move(canvasToImagePoint(canvasPt, editor.state, view.width, view.height, false))) {
|
|
575
576
|
selectionDraft = gesture.draftPath;
|
|
576
577
|
renderCanvas();
|
|
577
578
|
}
|
|
@@ -580,7 +581,7 @@
|
|
|
580
581
|
|
|
581
582
|
if (mode === 'crop') {
|
|
582
583
|
if (cropDragInfo) {
|
|
583
|
-
const currentImgPt = canvasToImagePoint(canvasPt, editor.state,
|
|
584
|
+
const currentImgPt = canvasToImagePoint(canvasPt, editor.state, view.width, view.height, true);
|
|
584
585
|
let newCrop: CropBox;
|
|
585
586
|
|
|
586
587
|
if (cropDragInfo.handle === 'new') {
|
|
@@ -616,7 +617,7 @@
|
|
|
616
617
|
width: editor.state.imageDimensions.width,
|
|
617
618
|
height: editor.state.imageDimensions.height,
|
|
618
619
|
};
|
|
619
|
-
const handle = hitTestCrop(canvasPt, editor.state,
|
|
620
|
+
const handle = hitTestCrop(canvasPt, editor.state, view.width, view.height, 14, activeCrop);
|
|
620
621
|
canvasEl.style.cursor = getCropCursor(handle, editor.state.transform);
|
|
621
622
|
}
|
|
622
623
|
return;
|
|
@@ -624,7 +625,7 @@
|
|
|
624
625
|
|
|
625
626
|
if (mode === 'annotate' && editor.state.activeTool === 'shape') {
|
|
626
627
|
if (shapeGesture.active) {
|
|
627
|
-
shapeGesture.move(canvasToImagePoint(canvasPt, editor.state,
|
|
628
|
+
shapeGesture.move(canvasToImagePoint(canvasPt, editor.state, view.width, view.height, false), { shift: e.shiftKey, alt: e.altKey });
|
|
628
629
|
if (shapeGesture.error) report('shape', shapeGesture.error);
|
|
629
630
|
}
|
|
630
631
|
return;
|
|
@@ -632,7 +633,7 @@
|
|
|
632
633
|
|
|
633
634
|
if (mode === 'annotate') {
|
|
634
635
|
if (annotationDragInfo) {
|
|
635
|
-
const currentImgPt = canvasToImagePoint(canvasPt, editor.state,
|
|
636
|
+
const currentImgPt = canvasToImagePoint(canvasPt, editor.state, view.width, view.height, false);
|
|
636
637
|
const dx = currentImgPt.x - annotationDragInfo.startImgPt.x;
|
|
637
638
|
const dy = currentImgPt.y - annotationDragInfo.startImgPt.y;
|
|
638
639
|
editor.updateAnnotation(annotationDragInfo.annId, {
|
|
@@ -644,7 +645,7 @@
|
|
|
644
645
|
|
|
645
646
|
if (!dragStartPoint) {
|
|
646
647
|
if (editor.state.activeTool === 'select') {
|
|
647
|
-
const imgPt = canvasToImagePoint(canvasPt, editor.state,
|
|
648
|
+
const imgPt = canvasToImagePoint(canvasPt, editor.state, view.width, view.height, false);
|
|
648
649
|
let hovered = false;
|
|
649
650
|
for (let i = editor.state.annotations.length - 1; i >= 0; i--) {
|
|
650
651
|
if (hitTestAnnotation(imgPt, editor.state.annotations[i])) {
|
|
@@ -659,7 +660,7 @@
|
|
|
659
660
|
return;
|
|
660
661
|
}
|
|
661
662
|
|
|
662
|
-
const currentImgPt = canvasToImagePoint(canvasPt, editor.state,
|
|
663
|
+
const currentImgPt = canvasToImagePoint(canvasPt, editor.state, view.width, view.height, false);
|
|
663
664
|
const stroke = { strokeColor: annotationColor, strokeWidth: annotationWidth };
|
|
664
665
|
|
|
665
666
|
if (editor.state.activeTool === 'pen' && isDrawing) {
|
|
@@ -718,7 +719,7 @@
|
|
|
718
719
|
if (mode === 'select') {
|
|
719
720
|
const gesture = selectionGesture;
|
|
720
721
|
if (gesture.active) {
|
|
721
|
-
gesture.end(canvasToImagePoint(canvasPt, editor.state,
|
|
722
|
+
gesture.end(canvasToImagePoint(canvasPt, editor.state, view.width, view.height, false), e);
|
|
722
723
|
selectionDraft = null;
|
|
723
724
|
renderCanvas();
|
|
724
725
|
}
|
|
@@ -742,7 +743,7 @@
|
|
|
742
743
|
return;
|
|
743
744
|
}
|
|
744
745
|
if (mode === 'annotate' && editor.state.activeTool === 'type') {
|
|
745
|
-
typeTool.up(canvasToImagePoint(canvasPt, editor.state,
|
|
746
|
+
typeTool.up(canvasToImagePoint(canvasPt, editor.state, view.width, view.height, false));
|
|
746
747
|
typeSession = typeTool.session;
|
|
747
748
|
if (typeTool.error) report('type', typeTool.error);
|
|
748
749
|
void renderCanvas();
|
|
@@ -758,7 +759,7 @@
|
|
|
758
759
|
}
|
|
759
760
|
|
|
760
761
|
if (dragStartPoint) {
|
|
761
|
-
const currentImgPt = canvasToImagePoint(canvasPt, editor.state,
|
|
762
|
+
const currentImgPt = canvasToImagePoint(canvasPt, editor.state, view.width, view.height, false);
|
|
762
763
|
const stroke = { strokeColor: annotationColor, strokeWidth: annotationWidth };
|
|
763
764
|
|
|
764
765
|
if (editor.state.activeTool === 'pen' && isDrawing) {
|
|
@@ -833,7 +834,7 @@
|
|
|
833
834
|
onpointerup={handlePaintPointerUp}
|
|
834
835
|
onpointercancel={() => paintGesture.cancel()}
|
|
835
836
|
data-testid="image-editor-canvas"
|
|
836
|
-
style={transformActive ? `cursor: ${transformCursor}
|
|
837
|
+
style="width: 100%; height: 100%;{transformActive ? ` cursor: ${transformCursor};` : ''}"
|
|
837
838
|
class="absolute inset-0 w-full h-full block {transformActive ? 'touch-none' : mode === 'paint' ? 'touch-none cursor-crosshair' : mode === 'annotate' && editor.state.activeTool === 'type' ? 'cursor-text' : mode === 'annotate' && editor.state.activeTool === 'shape' ? 'cursor-crosshair' : ''}"
|
|
838
839
|
></canvas>
|
|
839
840
|
{#if typeSession}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixerate/editor-svelte",
|
|
3
|
-
"version": "0.110.
|
|
3
|
+
"version": "0.110.1",
|
|
4
4
|
"description": "Svelte bindings and prompt/rich-text components for @pixerate/editor",
|
|
5
5
|
"author": "Pixerate",
|
|
6
6
|
"license": "MIT",
|
|
@@ -160,7 +160,7 @@
|
|
|
160
160
|
"@tiptap/extension-bubble-menu": "^2.27.3",
|
|
161
161
|
"@tiptap/extension-placeholder": "^2.27.3",
|
|
162
162
|
"@tiptap/starter-kit": "^2.27.3",
|
|
163
|
-
"@pixerate/editor": "0.150.
|
|
163
|
+
"@pixerate/editor": "0.150.1"
|
|
164
164
|
},
|
|
165
165
|
"peerDependencies": {
|
|
166
166
|
"@dagrejs/dagre": "^1.1.4",
|
|
@@ -179,14 +179,14 @@
|
|
|
179
179
|
},
|
|
180
180
|
"devDependencies": {
|
|
181
181
|
"@dagrejs/dagre": "^1.1.4",
|
|
182
|
-
"@sveltejs/package": "^3.0.
|
|
182
|
+
"@sveltejs/package": "^3.0.1",
|
|
183
183
|
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
|
184
184
|
"@tiptap/core": "^2.27.3",
|
|
185
185
|
"@tiptap/pm": "^2.27.3",
|
|
186
186
|
"@types/dagre": "^0.7.52",
|
|
187
187
|
"@xyflow/svelte": "^1.7.0",
|
|
188
188
|
"svelte": "^5.57.2",
|
|
189
|
-
"svelte-check": "^4.7.
|
|
189
|
+
"svelte-check": "^4.7.7",
|
|
190
190
|
"typescript": "^5.7.3",
|
|
191
191
|
"vitest": "^5.0.5"
|
|
192
192
|
},
|