@pixerate/editor-svelte 0.114.1 → 0.116.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.
@@ -11,6 +11,7 @@
11
11
  import {
12
12
  CHART_COLOR_SCHEMES,
13
13
  CHART_CURVES,
14
+ CHART_RADAR_GRATICULES,
14
15
  CHART_ERROR_BAR_DIRECTIONS,
15
16
  CHART_ERROR_BAR_LABELS,
16
17
  CHART_ERROR_BAR_TYPES,
@@ -408,6 +409,22 @@
408
409
  </div>
409
410
  </div>
410
411
  {/if}
412
+ {#if spec.type === 'radar'}
413
+ <div class="flex items-center gap-2">
414
+ <label for="{idPrefix}-radar-ticks" class="{chartLabelClass} w-20 shrink-0">Rings</label>
415
+ <div class="min-w-0 flex-1">
416
+ <ChartCommitInput id="{idPrefix}-radar-ticks" value={spec.radarTicks === null ? '' : String(spec.radarTicks)} placeholder="Auto" inputmode="numeric" data-option="radarTicks" oncommit={(text) => controller.update({ radarTicks: text.trim() === '' ? null : Number(text) }).ok} />
417
+ </div>
418
+ </div>
419
+ <div class="flex items-center gap-2">
420
+ <label for="{idPrefix}-radar-graticule" class="{chartLabelClass} w-20 shrink-0">Grid</label>
421
+ <select id="{idPrefix}-radar-graticule" class={chartInputClass} value={spec.radarGraticule} data-option="radarGraticule" onchange={(e) => controller.update({ radarGraticule: e.currentTarget.value as (typeof CHART_RADAR_GRATICULES)[number] })}>
422
+ {#each CHART_RADAR_GRATICULES as g (g)}
423
+ <option value={g}>{g === 'polygon' ? 'Polygons' : 'Circles'}</option>
424
+ {/each}
425
+ </select>
426
+ </div>
427
+ {/if}
411
428
  {#if spec.type === 'donut'}
412
429
  <div class="flex items-center gap-2">
413
430
  <label for="{idPrefix}-hole" class="{chartLabelClass} w-20 shrink-0">Hole</label>
@@ -0,0 +1,171 @@
1
+ <script lang="ts">
2
+ import type { ImageEditorController, TransformAnchor } from '@pixerate/editor/image-editor';
3
+ import { CANVAS_SIZE_PRESETS, isTransformAnchor } from '@pixerate/editor/image-editor';
4
+ import { untrack } from 'svelte';
5
+ import { imagePrimaryButtonClass, imageSelectClass } from './styles.js';
6
+
7
+ interface Props {
8
+ controller: ImageEditorController;
9
+ class?: string;
10
+ }
11
+
12
+ let { controller, class: className = '' }: Props = $props();
13
+
14
+ let tick = $state(0);
15
+ $effect(() => {
16
+ const c = controller;
17
+ return untrack(() => c.subscribe(() => tick++));
18
+ });
19
+
20
+ const natural = $derived.by(() => {
21
+ void tick;
22
+ return controller.getCanvasSize();
23
+ });
24
+
25
+ let width = $state(800);
26
+ let height = $state(600);
27
+ let lockAspect = $state(false);
28
+ let anchor = $state<TransformAnchor>('center');
29
+ let error = $state<string | null>(null);
30
+
31
+ // Sync initial dimensions with current canvas size
32
+ $effect(() => {
33
+ width = natural.width;
34
+ height = natural.height;
35
+ });
36
+
37
+ const ANCHOR_GRID: TransformAnchor[][] = [
38
+ ['top-left', 'top', 'top-right'],
39
+ ['left', 'center', 'right'],
40
+ ['bottom-left', 'bottom', 'bottom-right'],
41
+ ];
42
+
43
+ function onWidthChange(val: number) {
44
+ if (lockAspect && natural.width > 0) {
45
+ const ratio = natural.height / natural.width;
46
+ height = Math.max(1, Math.round(val * ratio));
47
+ }
48
+ width = val;
49
+ }
50
+
51
+ function onHeightChange(val: number) {
52
+ if (lockAspect && natural.height > 0) {
53
+ const ratio = natural.width / natural.height;
54
+ width = Math.max(1, Math.round(val * ratio));
55
+ }
56
+ height = val;
57
+ }
58
+
59
+ function onPresetSelect(e: Event) {
60
+ const target = e.currentTarget as HTMLSelectElement;
61
+ const presetId = target.value;
62
+ target.value = '';
63
+ const preset = CANVAS_SIZE_PRESETS.find((p) => p.id === presetId);
64
+ if (!preset) return;
65
+ width = preset.width;
66
+ height = preset.height;
67
+ }
68
+
69
+
70
+ function apply() {
71
+ error = null;
72
+ try {
73
+ controller.resizeCanvas({ width, height, anchor });
74
+ } catch (err) {
75
+ error = err instanceof Error ? err.message : String(err);
76
+ }
77
+ }
78
+ </script>
79
+
80
+ <div class="flex flex-col gap-3 text-xs text-foreground {className}" data-image-panel="canvas-size" data-testid="canvas-size-panel">
81
+ <div class="font-medium text-sm text-foreground">Canvas Size</div>
82
+
83
+ {#if error}
84
+ <div class="text-xs text-destructive bg-destructive/10 p-1.5 rounded">{error}</div>
85
+ {/if}
86
+
87
+ <div class="flex flex-col gap-1.5">
88
+ <label for="canvas-size-preset" class="text-muted-foreground text-[11px]">Preset</label>
89
+ <select
90
+ id="canvas-size-preset"
91
+ class="{imageSelectClass} w-full"
92
+ onchange={onPresetSelect}
93
+ aria-label="Preset canvas sizes"
94
+ >
95
+ <option value="">Choose preset…</option>
96
+ {#each CANVAS_SIZE_PRESETS as preset (preset.id)}
97
+ <option value={preset.id}>{preset.label} ({preset.width} × {preset.height})</option>
98
+ {/each}
99
+ </select>
100
+ </div>
101
+
102
+ <div class="grid grid-cols-2 gap-2 items-center">
103
+ <div class="flex flex-col gap-1">
104
+ <label for="canvas-size-width" class="text-muted-foreground text-[11px]">Width (px)</label>
105
+ <input
106
+ id="canvas-size-width"
107
+ type="number"
108
+ min="1"
109
+ max="16384"
110
+ class="w-full bg-background border border-border rounded px-2 py-1 text-foreground focus:outline-none focus:ring-1 focus:ring-primary"
111
+ value={width}
112
+ oninput={(e) => onWidthChange(Number((e.currentTarget as HTMLInputElement).value))}
113
+ />
114
+ </div>
115
+
116
+ <div class="flex flex-col gap-1">
117
+ <label for="canvas-size-height" class="text-muted-foreground text-[11px]">Height (px)</label>
118
+ <input
119
+ id="canvas-size-height"
120
+ type="number"
121
+ min="1"
122
+ max="16384"
123
+ class="w-full bg-background border border-border rounded px-2 py-1 text-foreground focus:outline-none focus:ring-1 focus:ring-primary"
124
+ value={height}
125
+ oninput={(e) => onHeightChange(Number((e.currentTarget as HTMLInputElement).value))}
126
+ />
127
+ </div>
128
+ </div>
129
+
130
+ <div class="flex items-center gap-2">
131
+ <label class="flex items-center gap-1.5 cursor-pointer text-muted-foreground select-none">
132
+ <input
133
+ type="checkbox"
134
+ class="rounded border-border text-primary focus:ring-primary"
135
+ checked={lockAspect}
136
+ onchange={(e) => (lockAspect = (e.currentTarget as HTMLInputElement).checked)}
137
+ />
138
+ <span>Lock aspect ratio</span>
139
+ </label>
140
+ </div>
141
+
142
+ <div class="flex flex-col gap-1.5">
143
+ <span class="text-muted-foreground text-[11px]">Anchor</span>
144
+ <div class="grid grid-cols-3 gap-1 w-24 h-24 p-1 bg-muted/40 border border-border rounded" role="radiogroup" aria-label="Resize anchor">
145
+ {#each ANCHOR_GRID as row}
146
+ {#each row as a (a)}
147
+ <button
148
+ type="button"
149
+ role="radio"
150
+ aria-checked={anchor === a}
151
+ aria-label={a}
152
+ class="w-full h-full rounded flex items-center justify-center transition-colors {anchor === a ? 'bg-primary text-primary-foreground' : 'bg-background hover:bg-muted text-muted-foreground'}"
153
+ onclick={() => (anchor = a)}
154
+ >
155
+ <div class="w-1.5 h-1.5 rounded-full {anchor === a ? 'bg-primary-foreground' : 'bg-muted-foreground/60'}"></div>
156
+ </button>
157
+ {/each}
158
+ {/each}
159
+ </div>
160
+ </div>
161
+
162
+ <div class="pt-2">
163
+ <button
164
+ type="button"
165
+ class="{imagePrimaryButtonClass} w-full"
166
+ onclick={apply}
167
+ >
168
+ Apply Canvas Size
169
+ </button>
170
+ </div>
171
+ </div>
@@ -0,0 +1,8 @@
1
+ import type { ImageEditorController } from '@pixerate/editor/image-editor';
2
+ interface Props {
3
+ controller: ImageEditorController;
4
+ class?: string;
5
+ }
6
+ declare const CanvasSizePanel: import("svelte").Component<Props, {}, "">;
7
+ type CanvasSizePanel = ReturnType<typeof CanvasSizePanel>;
8
+ export default CanvasSizePanel;
@@ -86,8 +86,10 @@
86
86
  locate?: (ref: PlaybackRef) => PlaybackRect | null;
87
87
  /** Your own element for a playback ref, anywhere on the page; its box is mapped to document pixels. */
88
88
  locateElement?: (ref: PlaybackRef) => Element | null;
89
- /** Show a panel for an `open` step (default: click its `[data-image-tab]` under `chromeRoot`). */
89
+ /** Show a panel for an `open` step (default: click its `[data-image-tab]` or `[data-image-panel]` under `chromeRoot`). */
90
90
  open?: (ref: PlaybackRef) => void | Promise<void>;
91
+ /** Whether wheel zoom and middle/space drag panning are enabled (default true). */
92
+ viewportGestures?: boolean;
91
93
  class?: string;
92
94
  style?: string;
93
95
  }
@@ -112,10 +114,16 @@
112
114
  locate,
113
115
  locateElement,
114
116
  open,
117
+ viewportGestures = true,
115
118
  class: className = '',
116
119
  style = '',
117
120
  }: Props = $props();
118
121
 
122
+ /** Fits the image to view, resetting zoom to 1 and pan to (0, 0). */
123
+ export function fitToView(): void {
124
+ editor.controller.resetZoom();
125
+ }
126
+
119
127
  let canvasEl = $state<HTMLCanvasElement | null>(null);
120
128
  let containerEl = $state<HTMLDivElement | null>(null);
121
129
  let rootEl = $state<HTMLDivElement | null>(null);
@@ -187,7 +195,64 @@
187
195
  reveal: () => onplaybackreveal?.(),
188
196
  }));
189
197
 
190
- // Selection tools (magic wand, marquees, lasso): the pointer gesture.
198
+ let isSpacePressed = $state(false);
199
+ let isPanning = $state(false);
200
+ let panStartPoint: { clientX: number; clientY: number } | null = null;
201
+ let panStartOffset: { x: number; y: number } = { x: 0, y: 0 };
202
+
203
+ function handleWheel(e: WheelEvent) {
204
+ if (!viewportGestures || !canvasEl) return;
205
+ e.preventDefault();
206
+
207
+ const rect = canvasEl.getBoundingClientRect();
208
+ const sx = rect.width > 0 ? view.width / rect.width : 1;
209
+ const sy = rect.height > 0 ? view.height / rect.height : 1;
210
+ const canvasPt = {
211
+ x: (e.clientX - rect.left) * sx,
212
+ y: (e.clientY - rect.top) * sy,
213
+ };
214
+
215
+ const s = editor.controller.getState();
216
+ const currentZoom = s.zoom || 1;
217
+ const factor = e.deltaY < 0 ? 1.1 : 0.9;
218
+ const nextZoom = Math.min(10, Math.max(0.1, currentZoom * factor));
219
+
220
+ // Zoom centered at cursor:
221
+ // in canvas coordinates: img = (canvasPt - pan - center) / zoom
222
+ // We want canvasPt to remain invariant after zoom:
223
+ // (canvasPt - center - newPan) / newZoom = (canvasPt - center - oldPan) / oldZoom
224
+ // newPan = canvasPt - center - (canvasPt - center - oldPan) * (newZoom / oldZoom)
225
+ const centerX = view.width / 2;
226
+ const centerY = view.height / 2;
227
+ const curPan = s.pan || { x: 0, y: 0 };
228
+ const ratio = nextZoom / currentZoom;
229
+
230
+ const newPanX = canvasPt.x - centerX - (canvasPt.x - centerX - curPan.x) * ratio;
231
+ const newPanY = canvasPt.y - centerY - (canvasPt.y - centerY - curPan.y) * ratio;
232
+
233
+ editor.controller.setZoom(nextZoom);
234
+ editor.controller.setPan({ x: Math.round(newPanX), y: Math.round(newPanY) });
235
+ }
236
+
237
+ $effect(() => {
238
+ if (typeof window === 'undefined') return;
239
+ const onKeyDown = (e: KeyboardEvent) => {
240
+ if (e.code === 'Space' && !e.repeat && document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
241
+ isSpacePressed = true;
242
+ }
243
+ };
244
+ const onKeyUp = (e: KeyboardEvent) => {
245
+ if (e.code === 'Space') {
246
+ isSpacePressed = false;
247
+ }
248
+ };
249
+ window.addEventListener('keydown', onKeyDown);
250
+ window.addEventListener('keyup', onKeyUp);
251
+ return () => {
252
+ window.removeEventListener('keydown', onKeyDown);
253
+ window.removeEventListener('keyup', onKeyUp);
254
+ };
255
+ });
191
256
  const selectionGesture = $derived(new SelectionGesture(editor.controller, () => selectionOptions));
192
257
  let selectionDraft = $state.raw<ImagePoint[] | null>(null);
193
258
  // Paint tools (Move, Brush, Eraser): the pointer gesture and the brush cursor.
@@ -476,7 +541,36 @@
476
541
  }
477
542
 
478
543
  function handleMouseDown(e: MouseEvent) {
479
- if (!canvasEl || transformTool.active) return;
544
+ if (!canvasEl) return;
545
+
546
+ if (viewportGestures && (e.button === 1 || (e.button === 0 && isSpacePressed))) {
547
+ e.preventDefault();
548
+ isPanning = true;
549
+ panStartPoint = { clientX: e.clientX, clientY: e.clientY };
550
+ const curPan = editor.controller.getState().pan || { x: 0, y: 0 };
551
+ panStartOffset = { ...curPan };
552
+ return;
553
+ }
554
+
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
+ }
572
+
573
+ if (transformTool.active) return;
480
574
  const canvasPt = getCanvasEventPoint(e);
481
575
 
482
576
  if (mode === 'select') {
@@ -567,7 +661,23 @@
567
661
  }
568
662
 
569
663
  function handleMouseMove(e: MouseEvent) {
570
- if (!canvasEl || transformTool.active) return;
664
+ if (!canvasEl) return;
665
+
666
+ if (isPanning && panStartPoint) {
667
+ const dx = e.clientX - panStartPoint.clientX;
668
+ const dy = e.clientY - panStartPoint.clientY;
669
+ editor.controller.setPan({
670
+ x: Math.round(panStartOffset.x + dx),
671
+ y: Math.round(panStartOffset.y + dy),
672
+ });
673
+ return;
674
+ }
675
+
676
+ if (viewportGestures && isSpacePressed) {
677
+ canvasEl.style.cursor = 'grab';
678
+ }
679
+
680
+ if (transformTool.active) return;
571
681
  const canvasPt = getCanvasEventPoint(e);
572
682
 
573
683
  if (mode === 'select') {
@@ -713,6 +823,11 @@
713
823
  }
714
824
 
715
825
  function handleMouseUp(e: MouseEvent) {
826
+ if (isPanning) {
827
+ isPanning = false;
828
+ panStartPoint = null;
829
+ return;
830
+ }
716
831
  if (!canvasEl || transformTool.active) return;
717
832
  const canvasPt = getCanvasEventPoint(e);
718
833
 
@@ -822,7 +937,12 @@
822
937
  React's `<ImageEditorCanvas>`.
823
938
  -->
824
939
  <div bind:this={rootEl} class="relative {className}" {style} data-testid="image-editor-canvas-root">
825
- <div bind:this={containerEl} class="absolute inset-0 overflow-hidden select-none">
940
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
941
+ <div
942
+ bind:this={containerEl}
943
+ onwheel={handleWheel}
944
+ class="absolute inset-0 overflow-hidden select-none"
945
+ >
826
946
  <canvas
827
947
  bind:this={canvasEl}
828
948
  onmousedown={handleMouseDown}
@@ -54,11 +54,15 @@ interface Props {
54
54
  locate?: (ref: PlaybackRef) => PlaybackRect | null;
55
55
  /** Your own element for a playback ref, anywhere on the page; its box is mapped to document pixels. */
56
56
  locateElement?: (ref: PlaybackRef) => Element | null;
57
- /** Show a panel for an `open` step (default: click its `[data-image-tab]` under `chromeRoot`). */
57
+ /** Show a panel for an `open` step (default: click its `[data-image-tab]` or `[data-image-panel]` under `chromeRoot`). */
58
58
  open?: (ref: PlaybackRef) => void | Promise<void>;
59
+ /** Whether wheel zoom and middle/space drag panning are enabled (default true). */
60
+ viewportGestures?: boolean;
59
61
  class?: string;
60
62
  style?: string;
61
63
  }
62
- declare const ImageEditorCanvas: import("svelte").Component<Props, {}, "typeSession">;
64
+ declare const ImageEditorCanvas: import("svelte").Component<Props, {
65
+ fitToView: () => void;
66
+ }, "typeSession">;
63
67
  type ImageEditorCanvas = ReturnType<typeof ImageEditorCanvas>;
64
68
  export default ImageEditorCanvas;
@@ -0,0 +1,98 @@
1
+ <script lang="ts">
2
+ import type { ImageEditorController, ImageLayer } from '@pixerate/editor/image-editor';
3
+ import {
4
+ ADJUSTMENT_LAYER_OPACITY_CONTROL,
5
+ DEFAULT_SHAPE_TOOL_OPTIONS,
6
+ DEFAULT_TYPE_TOOL_OPTIONS,
7
+ DEFAULT_SELECTION_TOOL_OPTIONS,
8
+ } from '@pixerate/editor/image-editor';
9
+ import CanvasSizePanel from './CanvasSizePanel.svelte';
10
+ import ControlSlider from './ControlSlider.svelte';
11
+ import LayerEffectsPanel from './LayerEffectsPanel.svelte';
12
+ import BrushOptionsBar from './BrushOptionsBar.svelte';
13
+ import ShapeOptionsBar from './ShapeOptionsBar.svelte';
14
+ import TypeOptionsBar from './TypeOptionsBar.svelte';
15
+ import AdjustmentLayersPanel from './AdjustmentLayersPanel.svelte';
16
+ import SelectionOptionsBar from './SelectionOptionsBar.svelte';
17
+
18
+ interface Props {
19
+ controller: ImageEditorController;
20
+ panelId: string;
21
+ class?: string;
22
+ }
23
+
24
+ let { controller, panelId, class: className = '' }: Props = $props();
25
+
26
+ const activeLayer = $derived(controller.getActiveLayer());
27
+ let shapeOptions = $state({ ...DEFAULT_SHAPE_TOOL_OPTIONS });
28
+ let typeOptions = $state({ ...DEFAULT_TYPE_TOOL_OPTIONS });
29
+ let selectionOptions = $state({ ...DEFAULT_SELECTION_TOOL_OPTIONS });
30
+ </script>
31
+
32
+ <div class="layer-panel-container {className}" data-image-panel={panelId} data-testid="layer-panel-{panelId}">
33
+ {#if panelId === 'canvas-size'}
34
+ <CanvasSizePanel {controller} />
35
+ {:else if panelId === 'properties'}
36
+ <div class="flex flex-col gap-3 text-xs text-foreground">
37
+ <div class="font-medium text-sm">Properties</div>
38
+ {#if activeLayer}
39
+ <div class="flex flex-col gap-2">
40
+ <div class="flex items-center justify-between">
41
+ <span class="text-muted-foreground">Name</span>
42
+ <span class="font-medium">{activeLayer.name || activeLayer.id}</span>
43
+ </div>
44
+ <div class="flex items-center justify-between">
45
+ <span class="text-muted-foreground">Type</span>
46
+ <span class="capitalize">{activeLayer.type}</span>
47
+ </div>
48
+ <div class="flex items-center justify-between">
49
+ <span class="text-muted-foreground">Blend Mode</span>
50
+ <span class="capitalize">{activeLayer.blend}</span>
51
+ </div>
52
+ <ControlSlider
53
+ spec={ADJUSTMENT_LAYER_OPACITY_CONTROL}
54
+ label="Opacity"
55
+ value={Math.round(activeLayer.opacity * 100)}
56
+ onchange={(val) => controller.setLayerProperties(activeLayer.id, { opacity: val / 100 })}
57
+ />
58
+ </div>
59
+ {:else}
60
+ <div class="text-muted-foreground">No active layer selected.</div>
61
+ {/if}
62
+ </div>
63
+ {:else if panelId === 'paint'}
64
+ <div class="flex flex-col gap-3">
65
+ <div class="font-medium text-sm text-foreground">Brush & Paint</div>
66
+ <BrushOptionsBar {controller} />
67
+ </div>
68
+ {:else if panelId === 'effects'}
69
+ <div class="flex flex-col gap-3">
70
+ <div class="font-medium text-sm text-foreground">Layer Effects</div>
71
+ <LayerEffectsPanel {controller} />
72
+ </div>
73
+ {:else if panelId === 'adjustment'}
74
+ <AdjustmentLayersPanel {controller} />
75
+ {:else if panelId === 'type'}
76
+ <div class="flex flex-col gap-3">
77
+ <div class="font-medium text-sm text-foreground">Typography</div>
78
+ <TypeOptionsBar {controller} options={typeOptions} onchange={(o) => (typeOptions = o)} />
79
+ </div>
80
+ {:else if panelId === 'shape'}
81
+ <div class="flex flex-col gap-3">
82
+ <div class="font-medium text-sm text-foreground">Shape Options</div>
83
+ <ShapeOptionsBar {controller} options={shapeOptions} onchange={(o) => (shapeOptions = o)} />
84
+ </div>
85
+ {:else if panelId === 'smart-filters'}
86
+ <div class="flex flex-col gap-3">
87
+ <div class="font-medium text-sm text-foreground">Smart Filters</div>
88
+ <LayerEffectsPanel {controller} />
89
+ </div>
90
+ {:else if panelId === 'selection'}
91
+ <div class="flex flex-col gap-3">
92
+ <div class="font-medium text-sm text-foreground">Selection</div>
93
+ <SelectionOptionsBar {controller} options={selectionOptions} onOptionsChange={(o) => (selectionOptions = o)} />
94
+ </div>
95
+ {:else}
96
+ <div class="text-xs text-muted-foreground">Panel {panelId}</div>
97
+ {/if}
98
+ </div>
@@ -0,0 +1,9 @@
1
+ import type { ImageEditorController } from '@pixerate/editor/image-editor';
2
+ interface Props {
3
+ controller: ImageEditorController;
4
+ panelId: string;
5
+ class?: string;
6
+ }
7
+ declare const LayerPanel: import("svelte").Component<Props, {}, "">;
8
+ type LayerPanel = ReturnType<typeof LayerPanel>;
9
+ export default LayerPanel;
@@ -1,4 +1,4 @@
1
1
  /** What pointer gestures on `ImageEditorCanvas` do (and whether the crop overlay shows). */
2
- export type ImageEditorCanvasMode = 'crop' | 'select' | 'paint' | 'annotate' | 'none';
2
+ export type ImageEditorCanvasMode = 'crop' | 'select' | 'paint' | 'annotate' | 'transform' | 'none';
3
3
  /** Which gesture an `ImageEditorCanvas` `onerror` report comes from. */
4
- export type ImageEditorCanvasErrorSource = 'paint' | 'select' | 'shape' | 'type';
4
+ export type ImageEditorCanvasErrorSource = 'paint' | 'select' | 'shape' | 'type' | 'transform';
@@ -16,8 +16,12 @@ import type FreeTransformOptionsBarComponent from "./FreeTransformOptionsBar.sve
16
16
  import type ShapeOptionsBarComponent from "./ShapeOptionsBar.svelte";
17
17
  import type TypeOptionsBarComponent from "./TypeOptionsBar.svelte";
18
18
  import type ImageProviderEffectsPanelComponent from "./ImageProviderEffectsPanel.svelte";
19
+ import type CanvasSizePanelComponent from "./CanvasSizePanel.svelte";
20
+ import type LayerPanelComponent from "./LayerPanel.svelte";
19
21
  export { default as ImageEditor } from './ImageEditor.svelte';
20
22
  export { default as ImageEditorCanvas } from './ImageEditorCanvas.svelte';
23
+ export { default as CanvasSizePanel } from './CanvasSizePanel.svelte';
24
+ export { default as LayerPanel } from './LayerPanel.svelte';
21
25
  export { default as ImageEffectControls } from './ImageEffectControls.svelte';
22
26
  export type { ImageEditorCanvasMode, ImageEditorCanvasErrorSource } from './canvasTypes.js';
23
27
  export { default as PaintGradientEditor } from './PaintGradientEditor.svelte';
@@ -38,6 +42,8 @@ export { default as ImageProviderEffectsPanel } from './ImageProviderEffectsPane
38
42
  export * from './imageEditorState.svelte.js';
39
43
  export type ImageEditorProps = ComponentProps<typeof ImageEditorComponent>;
40
44
  export type ImageEditorCanvasProps = ComponentProps<typeof ImageEditorCanvasComponent>;
45
+ export type CanvasSizePanelProps = ComponentProps<typeof CanvasSizePanelComponent>;
46
+ export type LayerPanelPropsComponent = ComponentProps<typeof LayerPanelComponent>;
41
47
  export type ImageEffectControlsProps = ComponentProps<typeof ImageEffectControlsComponent>;
42
48
  export type HistogramProps = ComponentProps<typeof HistogramComponent>;
43
49
  export type AdjustmentLayersPanelProps = ComponentProps<typeof AdjustmentLayersPanelComponent>;
@@ -1,5 +1,7 @@
1
1
  export { default as ImageEditor } from './ImageEditor.svelte';
2
2
  export { default as ImageEditorCanvas } from './ImageEditorCanvas.svelte';
3
+ export { default as CanvasSizePanel } from './CanvasSizePanel.svelte';
4
+ export { default as LayerPanel } from './LayerPanel.svelte';
3
5
  export { default as ImageEffectControls } from './ImageEffectControls.svelte';
4
6
  export { default as PaintGradientEditor } from './PaintGradientEditor.svelte';
5
7
  export { default as Histogram } from './Histogram.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor-svelte",
3
- "version": "0.114.1",
3
+ "version": "0.116.0",
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.156.0"
163
+ "@pixerate/editor": "0.158.0"
164
164
  },
165
165
  "peerDependencies": {
166
166
  "@dagrejs/dagre": "^1.1.4",