@pixerate/editor-svelte 0.116.0 → 0.117.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.
@@ -16,6 +16,8 @@
16
16
  handlePaintShortcut,
17
17
  pointerPressure,
18
18
  FreeTransformTool,
19
+ boxFrameHit,
20
+ quadFrameOverlay,
19
21
  getViewportProjection,
20
22
  measureCanvasViewport,
21
23
  watchCanvasViewport,
@@ -26,6 +28,7 @@
26
28
  type SelectionToolOptions,
27
29
  type ShapeToolOptions,
28
30
  type TextEditSession,
31
+ type TransformOverlay,
29
32
  type TypeToolOptions,
30
33
  } from '@pixerate/editor/image-editor';
31
34
  import { untrack } from 'svelte';
@@ -343,6 +346,64 @@
343
346
  return () => window.removeEventListener('keydown', onKey);
344
347
  });
345
348
 
349
+ /** How far outside a frame (in hit tolerances) a drag rotates instead of picking a layer. */
350
+ const ROTATE_BAND = 4;
351
+
352
+ /**
353
+ * Transform mode, with no transform open: the active layer's frame, drawn as a transform's.
354
+ * A layer locked in place gets the outline without handles.
355
+ */
356
+ function layerFrame(): TransformOverlay | null {
357
+ if (mode !== 'transform' || transformTool.active) return null;
358
+ const c = editor.controller;
359
+ const id = c.getState().activeLayerId;
360
+ const layer = id ? c.getLayer(id) : undefined;
361
+ if (!layer || layer.visible === false) return null;
362
+ const corners = c.getLayerFrame(layer.id);
363
+ if (!corners) return null;
364
+ return quadFrameOverlay(corners, !(layer.locks?.all || layer.locks?.position));
365
+ }
366
+
367
+ /**
368
+ * Transform mode: a drag on the active layer's frame transforms it, and a press elsewhere picks
369
+ * the layer there (or none) and drags that. Each drag opens a Free Transform and closes it on
370
+ * release, one undo step when it changed something, so other edits never meet an open one.
371
+ */
372
+ let frameDrag = false;
373
+ function handleFramePointerDown(e: PointerEvent): boolean {
374
+ if (mode !== 'transform' || transformTool.active || !canvasEl) return false;
375
+ // Middle-button and Space drags pan (see handleMouseDown).
376
+ if (e.button !== 0 || (viewportGestures && isSpacePressed)) return false;
377
+ const c = editor.controller;
378
+ const { pt, tolerance } = transformPoint(e);
379
+ const frame = layerFrame();
380
+ let target = frame && boxFrameHit(frame, pt, tolerance, tolerance * ROTATE_BAND) ? c.getState().activeLayerId : null;
381
+ if (!target) {
382
+ target = c.layerAt(pt);
383
+ c.setActiveLayer(target);
384
+ }
385
+ const layer = target ? c.getLayer(target) : undefined;
386
+ if (!layer || layer.locks?.all || layer.locks?.position) return true;
387
+ e.preventDefault();
388
+ if (transformTool.begin({ layerId: layer.id })) return true;
389
+ try {
390
+ canvasEl.setPointerCapture?.(e.pointerId);
391
+ } catch {
392
+ // Synthetic events have no active pointer to capture.
393
+ }
394
+ // A press anywhere inside moves the layer, the centre included.
395
+ transformTool.down(pt, tolerance, { reference: false });
396
+ frameDrag = true;
397
+ return true;
398
+ }
399
+ function endFrameDrag(cancelled = false) {
400
+ if (!frameDrag) return;
401
+ frameDrag = false;
402
+ if (!transformTool.active) return;
403
+ if (!cancelled && transformTool.modified) transformTool.commit();
404
+ else transformTool.cancel();
405
+ }
406
+
346
407
  /** The document point of a pointer event and the hit tolerance (6 screen px). */
347
408
  function transformPoint(e: PointerEvent) {
348
409
  const rect = canvasEl!.getBoundingClientRect();
@@ -448,7 +509,7 @@
448
509
  mode === 'paint' && brushCursor && editor.controller.getState().activeTool !== 'move' && !transformTool.active
449
510
  ? { ...brushCursor, diameter: editor.controller.getState().paint.brush.size }
450
511
  : null,
451
- freeTransform: transformTool.overlay(),
512
+ freeTransform: transformTool.overlay() ?? layerFrame(),
452
513
  textEdit: typeTool.session?.alive ? typeTool.session.overlay() : null,
453
514
  });
454
515
  }
@@ -468,6 +529,7 @@
468
529
  }
469
530
  function handlePaintPointerDown(e: PointerEvent) {
470
531
  if (handleTransformPointerDown(e)) return;
532
+ if (handleFramePointerDown(e)) return;
471
533
  if (mode !== 'paint' || e.button !== 0 || !canvasEl) return;
472
534
  e.preventDefault();
473
535
  try {
@@ -479,6 +541,12 @@
479
541
  }
480
542
  function handlePaintPointerMove(e: PointerEvent) {
481
543
  if (handleTransformPointerMove(e)) return;
544
+ if (mode === 'transform' && canvasEl) {
545
+ const { pt, tolerance } = transformPoint(e);
546
+ const frame = layerFrame();
547
+ transformCursor = frame ? transformTool.cursor(boxFrameHit(frame, pt, tolerance, tolerance * ROTATE_BAND)) : 'default';
548
+ return;
549
+ }
482
550
  if (mode !== 'paint' || !canvasEl) return;
483
551
  const samples = paintSamples(e);
484
552
  brushCursor = samples[samples.length - 1] ?? null;
@@ -486,7 +554,10 @@
486
554
  else void renderCanvas();
487
555
  }
488
556
  function handlePaintPointerUp() {
489
- if (handleTransformPointerUp()) return;
557
+ if (handleTransformPointerUp()) {
558
+ endFrameDrag();
559
+ return;
560
+ }
490
561
  if (mode !== 'paint' || !paintGesture.active) return;
491
562
  paintGesture.up();
492
563
  if (paintGesture.error) report('paint', paintGesture.error);
@@ -552,23 +623,8 @@
552
623
  return;
553
624
  }
554
625
 
555
- if (mode === 'transform') {
556
- const canvasPt = getCanvasEventPoint(e);
557
- const imgPt = canvasToImagePoint(canvasPt, editor.state, view.width, view.height, false);
558
- const hitLayerId = editor.controller.layerAt(imgPt);
559
- if (hitLayerId) {
560
- editor.controller.setActiveLayer(hitLayerId);
561
- if (!transformTool.active) {
562
- transformTool.begin();
563
- }
564
- } else {
565
- editor.controller.setActiveLayer(null);
566
- if (transformTool.active) {
567
- transformTool.cancel();
568
- }
569
- }
570
- return;
571
- }
626
+ // Transform mode picks and drags layers on pointerdown (handleFramePointerDown).
627
+ if (mode === 'transform') return;
572
628
 
573
629
  if (transformTool.active) return;
574
630
  const canvasPt = getCanvasEventPoint(e);
@@ -952,10 +1008,13 @@
952
1008
  onpointerdown={handlePaintPointerDown}
953
1009
  onpointermove={handlePaintPointerMove}
954
1010
  onpointerup={handlePaintPointerUp}
955
- onpointercancel={() => paintGesture.cancel()}
1011
+ onpointercancel={() => {
1012
+ paintGesture.cancel();
1013
+ endFrameDrag(true);
1014
+ }}
956
1015
  data-testid="image-editor-canvas"
957
- style="width: 100%; height: 100%;{transformActive ? ` cursor: ${transformCursor};` : ''}"
958
- 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' : ''}"
1016
+ style="width: 100%; height: 100%;{transformActive || mode === 'transform' ? ` cursor: ${transformCursor};` : ''}"
1017
+ class="absolute inset-0 w-full h-full block {transformActive || mode === 'transform' ? 'touch-none' : mode === 'paint' ? 'touch-none cursor-crosshair' : mode === 'annotate' && editor.state.activeTool === 'type' ? 'cursor-text' : mode === 'annotate' && editor.state.activeTool === 'shape' ? 'cursor-crosshair' : ''}"
959
1018
  ></canvas>
960
1019
  {#if typeSession}
961
1020
  <textarea
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor-svelte",
3
- "version": "0.116.0",
3
+ "version": "0.117.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.158.0"
163
+ "@pixerate/editor": "0.160.0"
164
164
  },
165
165
  "peerDependencies": {
166
166
  "@dagrejs/dagre": "^1.1.4",