@pixerate/editor-svelte 0.130.3 → 0.132.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.
@@ -18,6 +18,7 @@
18
18
  FreeTransformTool,
19
19
  boxFrameHit,
20
20
  quadFrameOverlay,
21
+ TRANSFORM_ROTATE_HANDLE_OFFSET,
21
22
  getViewportProjection,
22
23
  measureCanvasViewport,
23
24
  watchCanvasViewport,
@@ -371,7 +372,8 @@
371
372
  if (!layer || layer.visible === false) return null;
372
373
  const corners = c.getLayerFrame(layer.id);
373
374
  if (!corners) return null;
374
- return quadFrameOverlay(corners, !(layer.locks?.all || layer.locks?.position));
375
+ const { scale } = getViewportProjection(c.getState(), view.width, view.height, false);
376
+ return quadFrameOverlay(corners, !(layer.locks?.all || layer.locks?.position), TRANSFORM_ROTATE_HANDLE_OFFSET / (scale || 1));
375
377
  }
376
378
 
377
379
  /**
@@ -503,6 +505,8 @@
503
505
  if (!canvasEl || !containerEl) return;
504
506
  const viewport = measureCanvasViewport(containerEl);
505
507
  view = { width: viewport.width, height: viewport.height };
508
+ // The transform overlay's rotate handle sits a fixed distance out on screen.
509
+ transformTool.setView(getViewportProjection(editor.controller.getState(), view.width, view.height, mode === 'crop').scale);
506
510
 
507
511
  await editor.controller.render(canvasEl, {
508
512
  viewportWidth: viewport.width,
@@ -1,14 +1,12 @@
1
1
  <script lang="ts">
2
2
  import { imageOutlineButtonClass } from './styles.js';
3
- import { EFFECT_LAYER_USES, LAYER_EFFECT_MENU, effectLayerTiming, findLayer, layerEffectsPanel, type ImageEditorController, type ImageLayer, type LayerEffectKind, type SmartObjectLayer } from '@pixerate/editor/image-editor';
3
+ import { EFFECT_LAYER_USES, effectLayerTiming, findLayer, type ImageEditorController, type ImageLayer, type SmartObjectLayer } from '@pixerate/editor/image-editor';
4
4
  import { untrack } from 'svelte';
5
5
  import EffectsBrowser from '../effects/EffectsBrowser.svelte';
6
6
  import { effectDescriptionOf } from '../effects/effects.svelte.js';
7
7
  import ImageEffectControls from './ImageEffectControls.svelte';
8
- import LayerEffectsPanel from './LayerEffectsPanel.svelte';
9
8
  import ImageCheckbox from './controls/ImageCheckbox.svelte';
10
9
  import ImageRange from './controls/ImageRange.svelte';
11
- import ImageSelect from './controls/ImageSelect.svelte';
12
10
  import type { ParameterComponents } from '../parameters/index.js';
13
11
 
14
12
  interface Props {
@@ -38,14 +36,9 @@
38
36
  const layer = $derived(snapshot.layer);
39
37
  const all = $derived(layer?.type === 'smart' ? ((layer as SmartObjectLayer).smartFilters?.filters ?? []) : []);
40
38
  const generator = $derived(layer?.type === 'pixel' ? layer.generator : undefined);
41
- const canFilter = $derived(!!layer && layer.type !== 'adjustment' && layer.type !== 'annotation');
42
- const styles = $derived(layerEffectsPanel(layer));
39
+ const canFilter = $derived(!!provider && !!layer && layer.type !== 'adjustment' && layer.type !== 'annotation');
43
40
  const described = effectDescriptionOf(() => provider, () => generator?.effect);
44
41
  const timing = $derived(generator ? effectLayerTiming(generator, described.description) : null);
45
- const addOptions = $derived([
46
- ...(provider && canFilter ? [{ value: 'filter', label: 'Filter…' }] : []),
47
- ...(styles.available && !styles.readOnly ? LAYER_EFFECT_MENU.map((m) => ({ value: `style:${m.kind}`, label: m.label })) : []),
48
- ]);
49
42
  const drags = new Set<string>();
50
43
  /** Changes during a drag skip the undo stack after the first, so the drag is one step. */
51
44
  function commit(gesture: string | null) {
@@ -60,12 +53,6 @@
60
53
  .then(work)
61
54
  .catch((e: unknown) => (message = e instanceof Error ? e.message : String(e)));
62
55
  }
63
- function add(value: string) {
64
- const id = layer?.id;
65
- if (!value || !id) return;
66
- if (value === 'filter') browse = browse === 'filter' ? null : 'filter';
67
- else run(() => controller.addLayerEffect(id, value.slice('style:'.length) as LayerEffectKind));
68
- }
69
56
 
70
57
  // Scrubbing the frame draws the effect again: one render at a time, the latest frame wins.
71
58
  let scrub: { layerId: string; time: number; commit: boolean } | null = null;
@@ -94,11 +81,12 @@
94
81
  </script>
95
82
 
96
83
  <!--
97
- Effects for the active layer, in one place. An effect layer first: its effect (change it or
84
+ Provider effects for the active layer. An effect layer first: its effect (change it or
98
85
  rasterize it), a still frame or an animation for an effect that moves, and its controls. Then
99
- what is applied to any layer: provider filters (smart filters; applying one converts a pixel
100
- layer to a smart object in the same undo step) and layer styles, added from one menu. New
101
- effect layers come from the layers panel. Without a provider it says why nothing can be added.
86
+ the provider filters applied to the layer (smart filters; applying one converts a pixel layer to
87
+ a smart object in the same undo step). Layer styles (drop shadow, stroke…) are not here: they
88
+ are part of the layer's properties (LayerEffectsPanel). New effect layers come from the layers
89
+ panel. Without a provider it says why nothing can be added.
102
90
  -->
103
91
  <section class="flex flex-col gap-2 text-xs {className}" aria-label="Effects" data-image-provider-effects="">
104
92
  {#if !provider}<p class="text-[11px] text-muted-foreground">No effects provider is registered. Effects in the document are kept.</p>{/if}
@@ -169,10 +157,8 @@
169
157
  {/if}
170
158
  {#if layer && canFilter}
171
159
  <div class="flex items-center gap-1" data-effect-part="applied">
172
- <span class="flex-1 text-[11px] font-semibold uppercase tracking-wider text-foreground">{generator ? 'On this layer' : 'Effects'}</span>
173
- {#if addOptions.length}
174
- <ImageSelect label="Add effect" value="" placeholder="Add…" options={addOptions} onchange={add} />
175
- {/if}
160
+ <span class="flex-1 text-[11px] font-semibold uppercase tracking-wider text-foreground">Filters</span>
161
+ <button type="button" class={imageOutlineButtonClass} aria-pressed={browse === 'filter'} onclick={() => (browse = browse === 'filter' ? null : 'filter')}>Add filter…</button>
176
162
  </div>
177
163
  {#if browse === 'filter'}
178
164
  {@const id = layer.id}
@@ -219,11 +205,8 @@
219
205
  {/each}
220
206
  </ol>
221
207
  {/if}
222
- {#if layer && styles.available && styles.rows.length}
223
- <LayerEffectsPanel {controller} layerId={layer.id} heading={false} />
224
- {/if}
225
- {#if layer && canFilter && !generator && !all.some((f) => f.kind === 'effect') && !styles.rows.length}
226
- <p class="text-[11px] text-muted-foreground">No effects yet. Add a filter, or a style such as a drop shadow. New effect layers are added from the layers panel.</p>
208
+ {#if layer && canFilter && !generator && !all.some((f) => f.kind === 'effect')}
209
+ <p class="text-[11px] text-muted-foreground">No filters yet. A filter changes the layer's pixels and stays editable. Layer styles such as a drop shadow are in the layer's properties; new effect layers are added from the layers panel.</p>
227
210
  {/if}
228
211
  {#if !layer}<p class="text-[11px] text-muted-foreground">Select a layer to apply effects to it.</p>{/if}
229
212
  {#if message}<p role="alert" class="text-[11px] text-destructive">{message}</p>{/if}
@@ -10,7 +10,7 @@
10
10
 
11
11
  /**
12
12
  * A layer's effects (Photoshop's layer styles): the master switch, Scale Effects, an "Add
13
- * effect" menu, and each effect with its enabled toggle, remove button and, when expanded, its
13
+ * style" menu, and each effect with its enabled toggle, remove button and, when expanded, its
14
14
  * settings. Every change is one undo step (a slider drag is one step too).
15
15
  */
16
16
  interface Props {
@@ -18,7 +18,7 @@
18
18
  /** The layer whose effects to show (default: the active layer). */
19
19
  layerId?: string | null;
20
20
  /**
21
- * Show the "fx Effects" title and the "Add effect" menu (default true). Without them (inside a
21
+ * Show the "Layer Style" title and the "Add style" menu (default true). Without them (inside a
22
22
  * panel that adds styles from its own menu) the master switch and Scale stay.
23
23
  */
24
24
  heading?: boolean;
@@ -87,9 +87,9 @@
87
87
  <div class="text-xs text-muted-foreground {className}" data-testid="layer-effects">{view.model.reason}</div>
88
88
  {:else}
89
89
  {@const id = view.id}
90
- <div class="flex flex-col gap-1.5 text-xs text-foreground {className}" data-testid="layer-effects" aria-label="Layer effects" role="group">
90
+ <div class="flex flex-col gap-1.5 text-xs text-foreground {className}" data-testid="layer-effects" aria-label="Layer style" role="group">
91
91
  <div class="flex flex-wrap items-center gap-2">
92
- {#if heading}<span class="font-semibold text-foreground">fx Effects</span>{:else}<span class="font-semibold text-foreground">Styles</span>{/if}
92
+ {#if heading}<span class="font-semibold text-foreground">Layer Style</span>{:else}<span class="font-semibold text-foreground">Styles</span>{/if}
93
93
  <label class="flex items-center gap-1 text-foreground">
94
94
  <ImageCheckbox
95
95
  label="Effects on"
@@ -116,9 +116,9 @@
116
116
  </label>
117
117
  {#if heading}
118
118
  <ImageSelect
119
- label="Add effect"
119
+ label="Add style"
120
120
  value=""
121
- placeholder="Add effect…"
121
+ placeholder="Add style…"
122
122
  disabled={view.model.readOnly}
123
123
  options={LAYER_EFFECT_MENU.map((m) => ({ value: m.kind, label: m.label }))}
124
124
  onchange={add}
@@ -1,7 +1,7 @@
1
1
  import type { ImageEditorController } from '@pixerate/editor/image-editor';
2
2
  /**
3
3
  * A layer's effects (Photoshop's layer styles): the master switch, Scale Effects, an "Add
4
- * effect" menu, and each effect with its enabled toggle, remove button and, when expanded, its
4
+ * style" menu, and each effect with its enabled toggle, remove button and, when expanded, its
5
5
  * settings. Every change is one undo step (a slider drag is one step too).
6
6
  */
7
7
  interface Props {
@@ -9,7 +9,7 @@ interface Props {
9
9
  /** The layer whose effects to show (default: the active layer). */
10
10
  layerId?: string | null;
11
11
  /**
12
- * Show the "fx Effects" title and the "Add effect" menu (default true). Without them (inside a
12
+ * Show the "Layer Style" title and the "Add style" menu (default true). Without them (inside a
13
13
  * panel that adds styles from its own menu) the master switch and Scale stay.
14
14
  */
15
15
  heading?: boolean;
@@ -68,6 +68,10 @@
68
68
  onchange={(val) => controller.setLayerProperties(activeLayer.id, { opacity: val })}
69
69
  />
70
70
  </div>
71
+ <!-- Layer Style (drop shadow, stroke, glow…) is part of the layer's properties; adjustment layers have none. -->
72
+ {#if activeLayer.type !== 'adjustment'}
73
+ <LayerEffectsPanel {controller} layerId={activeLayer.id} class="border-t border-border pt-2" />
74
+ {/if}
71
75
  {:else}
72
76
  <div class="text-muted-foreground">No active layer selected.</div>
73
77
  {/if}
@@ -78,10 +82,8 @@
78
82
  <BrushOptionsBar {controller} />
79
83
  </div>
80
84
  {:else if panelId === 'effects'}
81
- <div class="flex flex-col gap-3">
82
- <div class="font-medium text-sm text-foreground">Layer Effects</div>
83
- <LayerEffectsPanel {controller} />
84
- </div>
85
+ <!-- Kept for hosts that still ask for it by id: layerPanels() folds it into 'properties'. -->
86
+ <LayerEffectsPanel {controller} />
85
87
  {:else if panelId === 'adjustment'}
86
88
  <AdjustmentLayersPanel {controller} />
87
89
  {:else if panelId === 'type'}
@@ -95,10 +97,8 @@
95
97
  <ShapeOptionsBar {controller} options={shapeOptions} onchange={(o) => (shapeOptions = o)} />
96
98
  </div>
97
99
  {:else if panelId === 'smart-filters'}
98
- <div class="flex flex-col gap-3">
99
- <div class="font-medium text-sm text-foreground">Smart Filters</div>
100
- <LayerEffectsPanel {controller} />
101
- </div>
100
+ <!-- Kept for hosts that still ask for it by id: smart filters are under 'provider-effects'. -->
101
+ <ImageProviderEffectsPanel {controller} />
102
102
  {:else if panelId === 'provider-effects'}
103
103
  <ImageProviderEffectsPanel {controller} />
104
104
  {:else if panelId === 'selection'}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor-svelte",
3
- "version": "0.130.3",
3
+ "version": "0.132.0",
4
4
  "description": "Svelte bindings and prompt/rich-text components for @pixerate/editor",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -165,7 +165,7 @@
165
165
  "**/*.css"
166
166
  ],
167
167
  "dependencies": {
168
- "@pixerate/editor": "0.177.1"
168
+ "@pixerate/editor": "0.179.0"
169
169
  },
170
170
  "peerDependencies": {
171
171
  "@dagrejs/dagre": "^1.1.4",