@pixerate/editor-svelte 0.78.0 → 0.79.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.
Files changed (35) hide show
  1. package/dist/paint-editor/PaintCanvas.svelte +6 -1
  2. package/dist/paint-editor/PaintCanvas.svelte.d.ts +3 -1
  3. package/dist/paint-editor/PaintCanvas.svelte.d.ts.map +1 -1
  4. package/dist/paint-editor/PaintEditor.svelte +5 -3
  5. package/dist/paint-editor/PaintEditor.svelte.d.ts +3 -1
  6. package/dist/paint-editor/PaintEditor.svelte.d.ts.map +1 -1
  7. package/dist/paint-editor/PaintLayersPanel.svelte +7 -3
  8. package/dist/paint-editor/PaintLayersPanel.svelte.d.ts.map +1 -1
  9. package/dist/paint-editor/PaintPresenceLayer.svelte +34 -0
  10. package/dist/paint-editor/PaintPresenceLayer.svelte.d.ts +18 -0
  11. package/dist/paint-editor/PaintPresenceLayer.svelte.d.ts.map +1 -0
  12. package/dist/paint-editor/index.d.ts +2 -1
  13. package/dist/paint-editor/index.d.ts.map +1 -1
  14. package/dist/paint-editor/index.js +2 -1
  15. package/dist/paint-editor/paintEditorRune.svelte.d.ts +10 -1
  16. package/dist/paint-editor/paintEditorRune.svelte.d.ts.map +1 -1
  17. package/dist/paint-editor/paintEditorRune.svelte.js +10 -1
  18. package/dist/vector/VectorCanvas.svelte +13 -0
  19. package/dist/vector/VectorCanvas.svelte.d.ts.map +1 -1
  20. package/dist/vector/VectorMaskPanel.svelte +125 -0
  21. package/dist/vector/VectorMaskPanel.svelte.d.ts +15 -0
  22. package/dist/vector/VectorMaskPanel.svelte.d.ts.map +1 -0
  23. package/dist/vector/VectorMaskedNodes.svelte +45 -0
  24. package/dist/vector/VectorMaskedNodes.svelte.d.ts +14 -0
  25. package/dist/vector/VectorMaskedNodes.svelte.d.ts.map +1 -0
  26. package/dist/vector/VectorSceneNodes.svelte +14 -2
  27. package/dist/vector/VectorSceneNodes.svelte.d.ts.map +1 -1
  28. package/dist/vector/VectorStylePanel.svelte +2 -0
  29. package/dist/vector/VectorStylePanel.svelte.d.ts.map +1 -1
  30. package/dist/vector/VectorToolbar.svelte +5 -1
  31. package/dist/vector/VectorToolbar.svelte.d.ts.map +1 -1
  32. package/dist/vector/index.d.ts +2 -0
  33. package/dist/vector/index.d.ts.map +1 -1
  34. package/dist/vector/index.js +2 -0
  35. package/package.json +2 -2
@@ -7,10 +7,12 @@
7
7
  handlePaintShortcut,
8
8
  viewCssTransform,
9
9
  type PaintEditorController,
10
+ type PaintPresence,
10
11
  type PaintTool,
11
12
  } from '@pixerate/editor/paint-editor';
12
13
  import { untrack } from 'svelte';
13
14
  import { paintEditorState } from './paintEditorRune.svelte.js';
15
+ import PaintPresenceLayer from './PaintPresenceLayer.svelte';
14
16
  import ReferencePanel from './ReferencePanel.svelte';
15
17
 
16
18
  interface Props {
@@ -23,9 +25,11 @@
23
25
  fitOnMount?: boolean;
24
26
  /** Called with a refusal (hidden or locked layer). */
25
27
  onerror?: (message: string | null) => void;
28
+ /** Agent cursors to draw over the canvas (see `PaintDirector` and `createPaintDirector`). */
29
+ presence?: PaintPresence;
26
30
  class?: string;
27
31
  }
28
- let { controller, tool = 'brush', showGuides = true, fitOnMount = true, onerror, class: className = '' }: Props = $props();
32
+ let { controller, tool = 'brush', showGuides = true, fitOnMount = true, onerror, presence, class: className = '' }: Props = $props();
29
33
 
30
34
  const editor = paintEditorState(() => controller);
31
35
  const snap = $derived(editor.current);
@@ -157,6 +161,7 @@
157
161
  {#each snap.guides.references.filter((r) => r.visible) as r (r.id)}
158
162
  <ReferencePanel {controller} reference={r} />
159
163
  {/each}
164
+ {#if presence}<PaintPresenceLayer {presence} view={snap.view} canvas={doc} viewport={size} />{/if}
160
165
  <div class="absolute bottom-1 right-2 text-[10px] text-slate-500 pointer-events-none" data-testid="paint-view-status">
161
166
  {Math.round(snap.view.zoom * 100)}% · {Math.round(snap.view.rotation)}°{snap.view.mirror ? ' · mirrored' : ''}
162
167
  </div>
@@ -1,4 +1,4 @@
1
- import { type PaintEditorController, type PaintTool } from '@pixerate/editor/paint-editor';
1
+ import { type PaintEditorController, type PaintPresence, type PaintTool } from '@pixerate/editor/paint-editor';
2
2
  interface Props {
3
3
  controller: PaintEditorController;
4
4
  /** The active tool (default brush). */
@@ -9,6 +9,8 @@ interface Props {
9
9
  fitOnMount?: boolean;
10
10
  /** Called with a refusal (hidden or locked layer). */
11
11
  onerror?: (message: string | null) => void;
12
+ /** Agent cursors to draw over the canvas (see `PaintDirector` and `createPaintDirector`). */
13
+ presence?: PaintPresence;
12
14
  class?: string;
13
15
  }
14
16
  declare const PaintCanvas: import("svelte").Component<Props, {}, "">;
@@ -1 +1 @@
1
- {"version":3,"file":"PaintCanvas.svelte.d.ts","sourceRoot":"","sources":["../../src/paint-editor/PaintCanvas.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAOH,KAAK,qBAAqB,EAC1B,KAAK,SAAS,EACf,MAAM,+BAA+B,CAAC;AAMvC,UAAU,KAAK;IACb,UAAU,EAAE,qBAAqB,CAAC;IAClC,uCAAuC;IACvC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,sDAAsD;IACtD,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAsHH,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"PaintCanvas.svelte.d.ts","sourceRoot":"","sources":["../../src/paint-editor/PaintCanvas.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAOH,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,+BAA+B,CAAC;AAOvC,UAAU,KAAK;IACb,UAAU,EAAE,qBAAqB,CAAC;IAClC,uCAAuC;IACvC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,sDAAsD;IACtD,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3C,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAwHH,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import type { PaintEditorController, PaintTool } from '@pixerate/editor/paint-editor';
2
+ import type { PaintEditorController, PaintPresence, PaintTool } from '@pixerate/editor/paint-editor';
3
3
  import BrushPanel from './BrushPanel.svelte';
4
4
  import ColorPanel from './ColorPanel.svelte';
5
5
  import PaintCanvas from './PaintCanvas.svelte';
@@ -14,9 +14,11 @@
14
14
  fileName?: string;
15
15
  /** Height of the editor (default 720 px). */
16
16
  height?: number | string;
17
+ /** Agent cursors to draw over the canvas (see `createPaintDirector`). */
18
+ presence?: PaintPresence;
17
19
  class?: string;
18
20
  }
19
- let { controller, fileName, height = 720, class: className = '' }: Props = $props();
21
+ let { controller, fileName, height = 720, presence, class: className = '' }: Props = $props();
20
22
  let tool = $state<PaintTool>('brush');
21
23
  let tab = $state<'color' | 'paper' | 'layers'>('color');
22
24
  let error = $state<string | null>(null);
@@ -28,7 +30,7 @@
28
30
  <div class="flex min-h-0 flex-1 gap-2">
29
31
  <BrushPanel {controller} class="w-60 shrink-0 overflow-y-auto pr-1" />
30
32
  <div class="flex min-w-0 flex-1 flex-col">
31
- <PaintCanvas {controller} {tool} onerror={(e) => (error = e)} class="flex-1 rounded-lg" />
33
+ <PaintCanvas {controller} {tool} {presence} onerror={(e) => (error = e)} class="flex-1 rounded-lg" />
32
34
  {#if error}<p role="alert" class="mt-1 text-[11px] text-rose-400">{error}</p>{/if}
33
35
  </div>
34
36
  <div class="flex w-64 shrink-0 flex-col gap-2 overflow-y-auto pr-1">
@@ -1,10 +1,12 @@
1
- import type { PaintEditorController } from '@pixerate/editor/paint-editor';
1
+ import type { PaintEditorController, PaintPresence } from '@pixerate/editor/paint-editor';
2
2
  interface Props {
3
3
  controller: PaintEditorController;
4
4
  /** Base name of saved and exported files (default `painting`). */
5
5
  fileName?: string;
6
6
  /** Height of the editor (default 720 px). */
7
7
  height?: number | string;
8
+ /** Agent cursors to draw over the canvas (see `createPaintDirector`). */
9
+ presence?: PaintPresence;
8
10
  class?: string;
9
11
  }
10
12
  declare const PaintEditor: import("svelte").Component<Props, {}, "">;
@@ -1 +1 @@
1
- {"version":3,"file":"PaintEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/paint-editor/PaintEditor.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAa,MAAM,+BAA+B,CAAC;AAUpF,UAAU,KAAK;IACb,UAAU,EAAE,qBAAqB,CAAC;IAClC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAyCH,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"PaintEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/paint-editor/PaintEditor.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,aAAa,EAAa,MAAM,+BAA+B,CAAC;AAUnG,UAAU,KAAK;IACb,UAAU,EAAE,qBAAqB,CAAC;IAClC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAyCH,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { BLEND_MODES, type BlendMode, type PaintEditorController, type PaintLayerInfo } from '@pixerate/editor/paint-editor';
2
+ import { BLEND_MENU_GROUPS, type BlendMode, type PaintEditorController, type PaintLayerInfo } from '@pixerate/editor/paint-editor';
3
3
  import { untrack } from 'svelte';
4
4
  import { attempt, button, chip, paintEditorState, panel } from './paintEditorRune.svelte.js';
5
5
 
@@ -91,8 +91,12 @@
91
91
  <span class="w-14 text-slate-400">Blend</span>
92
92
  <select aria-label="Layer blend" value={active.blend} onchange={(e) => run(() => controller.updateLayer(active.id, { blend: e.currentTarget.value as BlendMode }))} class="flex-1 rounded bg-slate-800 px-1 py-0.5">
93
93
  {#if active.type === 'group'}<option value="pass-through">Pass through</option>{/if}
94
- {#each BLEND_MODES as m (m.id)}
95
- <option value={m.id}>{m.label}</option>
94
+ {#each BLEND_MENU_GROUPS as g (g.label)}
95
+ <optgroup label={g.label}>
96
+ {#each g.modes as m (m.id)}
97
+ <option value={m.id} title={m.description}>{m.label}</option>
98
+ {/each}
99
+ </optgroup>
96
100
  {/each}
97
101
  </select>
98
102
  </label>
@@ -1 +1 @@
1
- {"version":3,"file":"PaintLayersPanel.svelte.d.ts","sourceRoot":"","sources":["../../src/paint-editor/PaintLayersPanel.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAA+B,KAAK,qBAAqB,EAAuB,MAAM,+BAA+B,CAAC;AAK3H,UAAU,KAAK;IACb,UAAU,EAAE,qBAAqB,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAwFH,QAAA,MAAM,gBAAgB,2CAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"PaintLayersPanel.svelte.d.ts","sourceRoot":"","sources":["../../src/paint-editor/PaintLayersPanel.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAqC,KAAK,qBAAqB,EAAuB,MAAM,+BAA+B,CAAC;AAKjI,UAAU,KAAK;IACb,UAAU,EAAE,qBAAqB,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA4FH,QAAA,MAAM,gBAAgB,2CAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,34 @@
1
+ <script lang="ts">
2
+ import { documentToScreen, type PaintPresence, type PaintPresenceState, type PaintView } from '@pixerate/editor/paint-editor';
3
+
4
+ interface Props {
5
+ /** Agent cursors to draw (see `PaintDirector`). */
6
+ presence: PaintPresence;
7
+ view: PaintView;
8
+ canvas: { width: number; height: number };
9
+ viewport: { width: number; height: number };
10
+ }
11
+
12
+ let { presence, view, canvas, viewport }: Props = $props();
13
+
14
+ let current: PaintPresenceState = $state.raw({ actors: [] });
15
+ $effect(() => {
16
+ current = presence.getState();
17
+ return presence.subscribe((next) => {
18
+ current = next;
19
+ });
20
+ });
21
+ </script>
22
+
23
+ <!-- Agent cursors in document pixels, placed through the view; the cursors stay upright and unscaled. -->
24
+ <div class="pointer-events-none absolute inset-0 overflow-hidden" data-paint-presence="" aria-hidden="true">
25
+ {#each current.actors as a (a.id)}
26
+ {@const p = documentToScreen(view, canvas, viewport, a.x, a.y)}
27
+ <div class="absolute" style={`left: ${p.x}px; top: ${p.y}px`} data-actor={a.id}>
28
+ <svg width="18" height="20" viewBox="0 0 18 20" class="drop-shadow-md" style={`transform-origin: 0 0; ${a.pressed ? 'transform: scale(0.85)' : ''}`}>
29
+ <path d="M0 0 L0 15 L4.5 10.8 L7.6 17 L10.2 15.8 L7.2 9.7 L13.5 9.4 Z" fill={a.color} stroke="white" stroke-width="1.4" stroke-linejoin="round" />
30
+ </svg>
31
+ <span class="absolute left-4 top-4 w-max max-w-[240px] rounded-md px-2 py-0.5 text-[11px] font-semibold text-white shadow" style={`background: ${a.color}`}>{a.message ?? a.name}</span>
32
+ </div>
33
+ {/each}
34
+ </div>
@@ -0,0 +1,18 @@
1
+ import { type PaintPresence, type PaintView } from '@pixerate/editor/paint-editor';
2
+ interface Props {
3
+ /** Agent cursors to draw (see `PaintDirector`). */
4
+ presence: PaintPresence;
5
+ view: PaintView;
6
+ canvas: {
7
+ width: number;
8
+ height: number;
9
+ };
10
+ viewport: {
11
+ width: number;
12
+ height: number;
13
+ };
14
+ }
15
+ declare const PaintPresenceLayer: import("svelte").Component<Props, {}, "">;
16
+ type PaintPresenceLayer = ReturnType<typeof PaintPresenceLayer>;
17
+ export default PaintPresenceLayer;
18
+ //# sourceMappingURL=PaintPresenceLayer.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PaintPresenceLayer.svelte.d.ts","sourceRoot":"","sources":["../../src/paint-editor/PaintPresenceLayer.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoB,KAAK,aAAa,EAA2B,KAAK,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAG5H,UAAU,KAAK;IACb,mDAAmD;IACnD,QAAQ,EAAE,aAAa,CAAC;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AA+BH,QAAA,MAAM,kBAAkB,2CAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
@@ -14,7 +14,8 @@ export { default as PaperPanel } from './PaperPanel.svelte';
14
14
  export { default as ColorPanel } from './ColorPanel.svelte';
15
15
  export { default as PaintLayersPanel } from './PaintLayersPanel.svelte';
16
16
  export { default as PaintSlider } from './PaintSlider.svelte';
17
- export { createReactivePaintEditor, paintEditorState } from './paintEditorRune.svelte.js';
17
+ export { default as PaintPresenceLayer } from './PaintPresenceLayer.svelte';
18
+ export { createPaintDirector, createReactivePaintEditor, paintEditorState } from './paintEditorRune.svelte.js';
18
19
  export type PaintEditorProps = ComponentProps<typeof PaintEditorComponent>;
19
20
  export type PaintCanvasProps = ComponentProps<typeof PaintCanvasComponent>;
20
21
  export type PaintToolbarProps = ComponentProps<typeof PaintToolbarComponent>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/paint-editor/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,oBAAoB,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,oBAAoB,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,mBAAmB,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,mBAAmB,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,mBAAmB,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,yBAAyB,MAAM,2BAA2B,CAAC;AAEvE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAE1F,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAC3E,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAC3E,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC7E,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACzE,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACzE,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACzE,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/paint-editor/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,oBAAoB,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,oBAAoB,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,mBAAmB,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,mBAAmB,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,mBAAmB,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,yBAAyB,MAAM,2BAA2B,CAAC;AAEvE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAE/G,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAC3E,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAC3E,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC7E,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACzE,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACzE,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACzE,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
@@ -6,4 +6,5 @@ export { default as PaperPanel } from './PaperPanel.svelte';
6
6
  export { default as ColorPanel } from './ColorPanel.svelte';
7
7
  export { default as PaintLayersPanel } from './PaintLayersPanel.svelte';
8
8
  export { default as PaintSlider } from './PaintSlider.svelte';
9
- export { createReactivePaintEditor, paintEditorState } from './paintEditorRune.svelte.js';
9
+ export { default as PaintPresenceLayer } from './PaintPresenceLayer.svelte';
10
+ export { createPaintDirector, createReactivePaintEditor, paintEditorState } from './paintEditorRune.svelte.js';
@@ -1,4 +1,4 @@
1
- import { PaintEditorController, type PaintEditorOptions, type PaintEditorState } from '@pixerate/editor/paint-editor';
1
+ import { PaintDirector, PaintEditorController, PaintPresence, type PaintDirectorOptions, type PaintEditorOptions, type PaintEditorState } from '@pixerate/editor/paint-editor';
2
2
  /**
3
3
  * A `PaintEditorController` (created from `options`, or the one given) with its state as a
4
4
  * rune: `editor.state` updates after every change. Call `destroy()` to unsubscribe.
@@ -12,6 +12,15 @@ export declare function createReactivePaintEditor(options?: PaintEditorOptions |
12
12
  export declare function paintEditorState(controller: () => PaintEditorController): {
13
13
  readonly current: PaintEditorState;
14
14
  };
15
+ /**
16
+ * A `PaintPresence` and `PaintDirector` for a paint editor: pass `presence` to `PaintEditor` (or
17
+ * `PaintCanvas`) and `director` to `createPaintEditorToolProvider`, so agent strokes are painted
18
+ * live by a cursor on the canvas.
19
+ */
20
+ export declare function createPaintDirector(controller: PaintEditorController, options?: Omit<PaintDirectorOptions, 'clock'>): {
21
+ presence: PaintPresence;
22
+ director: PaintDirector;
23
+ };
15
24
  export declare const chip: (on: boolean) => string;
16
25
  export declare const button = "px-2 py-1 rounded text-[11px] bg-slate-800 text-slate-200 hover:bg-slate-700 disabled:opacity-40";
17
26
  export declare const panel = "flex flex-col gap-2 text-xs text-slate-200";
@@ -1 +1 @@
1
- {"version":3,"file":"paintEditorRune.svelte.d.ts","sourceRoot":"","sources":["../../src/paint-editor/paintEditorRune.svelte.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,KAAK,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEtH;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,kBAAkB,GAAG,qBAA0B;;;;EAiBjG;AAED,8FAA8F;AAC9F,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,qBAAqB,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,CAoBhH;AAED,eAAO,MAAM,IAAI,GAAI,IAAI,OAAO,WAAwH,CAAC;AACzJ,eAAO,MAAM,MAAM,qGAAqG,CAAC;AACzH,eAAO,MAAM,KAAK,+CAA+C,CAAC;AAElE,qGAAqG;AACrG,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,OAAO,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAQtF"}
1
+ {"version":3,"file":"paintEditorRune.svelte.d.ts","sourceRoot":"","sources":["../../src/paint-editor/paintEditorRune.svelte.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,KAAK,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAE/K;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,kBAAkB,GAAG,qBAA0B;;;;EAiBjG;AAED,8FAA8F;AAC9F,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,qBAAqB,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,CAoBhH;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,qBAAqB,EAAE,OAAO,GAAE,IAAI,CAAC,oBAAoB,EAAE,OAAO,CAAM,GAAG;IAAE,QAAQ,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,aAAa,CAAA;CAAE,CAG9K;AAED,eAAO,MAAM,IAAI,GAAI,IAAI,OAAO,WAAwH,CAAC;AACzJ,eAAO,MAAM,MAAM,qGAAqG,CAAC;AACzH,eAAO,MAAM,KAAK,+CAA+C,CAAC;AAElE,qGAAqG;AACrG,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,OAAO,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAQtF"}
@@ -1,5 +1,5 @@
1
1
  import { untrack } from 'svelte';
2
- import { PaintEditorController } from '@pixerate/editor/paint-editor';
2
+ import { PaintDirector, PaintEditorController, PaintPresence } from '@pixerate/editor/paint-editor';
3
3
  /**
4
4
  * A `PaintEditorController` (created from `options`, or the one given) with its state as a
5
5
  * rune: `editor.state` updates after every change. Call `destroy()` to unsubscribe.
@@ -44,6 +44,15 @@ export function paintEditorState(controller) {
44
44
  },
45
45
  };
46
46
  }
47
+ /**
48
+ * A `PaintPresence` and `PaintDirector` for a paint editor: pass `presence` to `PaintEditor` (or
49
+ * `PaintCanvas`) and `director` to `createPaintEditorToolProvider`, so agent strokes are painted
50
+ * live by a cursor on the canvas.
51
+ */
52
+ export function createPaintDirector(controller, options = {}) {
53
+ const presence = new PaintPresence();
54
+ return { presence, director: new PaintDirector(controller, presence, options) };
55
+ }
47
56
  export const chip = (on) => `px-2 py-1 rounded text-[11px] ${on ? 'bg-indigo-600 text-white' : 'bg-slate-800 text-slate-300 hover:text-white'}`;
48
57
  export const button = 'px-2 py-1 rounded text-[11px] bg-slate-800 text-slate-200 hover:bg-slate-700 disabled:opacity-40';
49
58
  export const panel = 'flex flex-col gap-2 text-xs text-slate-200';
@@ -260,6 +260,19 @@
260
260
  <circle cx={w.x} cy={w.y} r={1.5} fill={accentColor} />
261
261
  </g>
262
262
  {/each}
263
+ {#each overlay.masks as m (m.id)}
264
+ <path
265
+ data-vector-mask-indicator={m.id}
266
+ data-mask-type={m.type}
267
+ data-inverted={m.invert || undefined}
268
+ d={m.d}
269
+ fill="none"
270
+ stroke={THEME.mask}
271
+ stroke-width={m.editing ? 1.5 : 1}
272
+ stroke-dasharray={m.editing ? undefined : THEME.maskDash}
273
+ opacity={m.enabled ? 1 : 0.5}
274
+ />
275
+ {/each}
263
276
  {#each overlay.blend.spines as sp (sp.id)}
264
277
  <g data-vector-blend-spine={sp.id} data-replaced={sp.replaced || undefined}>
265
278
  <path d={sp.d} fill="none" stroke={accentColor} stroke-dasharray="4 3" />
@@ -1 +1 @@
1
- {"version":3,"file":"VectorCanvas.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorCanvas.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,EAQH,KAAK,sBAAsB,EAC5B,MAAM,yBAAyB,CAAC;AASjC,UAAU,KAAK;IACb,UAAU,EAAE,sBAAsB,CAAC;IACnC,mFAAmF;IACnF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAsSH,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"VectorCanvas.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorCanvas.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,EAQH,KAAK,sBAAsB,EAC5B,MAAM,yBAAyB,CAAC;AASjC,UAAU,KAAK;IACb,UAAU,EAAE,sBAAsB,CAAC;IACnC,mFAAmF;IACnF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAySH,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,125 @@
1
+ <script lang="ts">
2
+ import { VECTOR_MASK_TYPE_OPTIONS, getVectorMaskPanel, type VectorEditorController, type VectorMaskType } from '@pixerate/editor/vector';
3
+ import { vectorStateOf } from './subscribe.svelte.js';
4
+
5
+ /**
6
+ * Masks of the selection: Make Clipping Mask, Make Opacity Mask (the topmost selected object
7
+ * masks the others) and Release; for a mask group (or something inside one) its type, Invert, a
8
+ * switch that turns the mask off by hiding it, and Edit Mask / Edit Contents, which select the
9
+ * mask (a path opens for anchor editing) or what it masks. Each change is one undo step.
10
+ */
11
+ interface Props {
12
+ controller: VectorEditorController;
13
+ class?: string;
14
+ }
15
+
16
+ let { controller, class: className = '' }: Props = $props();
17
+
18
+ const store = vectorStateOf(() => controller);
19
+ const view = $derived(store.current);
20
+ const panel = $derived(getVectorMaskPanel(view));
21
+ const g = $derived(panel?.group ?? null);
22
+ const ids = $derived(g ? [g.id] : undefined);
23
+
24
+ const buttonClass =
25
+ 'inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground';
26
+ const selectClass = 'h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs';
27
+ </script>
28
+
29
+ {#if panel}
30
+ <div class="space-y-2 {className}" data-vector-mask-panel="" aria-label="Mask">
31
+ <div class="flex flex-wrap items-center gap-1">
32
+ <span class="w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground">Mask</span>
33
+ <button
34
+ type="button"
35
+ data-vector-mask-action="clip"
36
+ class={buttonClass}
37
+ title={panel.makeHint ?? 'Make Clipping Mask: the topmost object clips the others'}
38
+ aria-label="Make Clipping Mask"
39
+ disabled={!panel.canMake}
40
+ onclick={() => controller.makeMask('clip')}
41
+ >
42
+ Clip
43
+ </button>
44
+ <button
45
+ type="button"
46
+ data-vector-mask-action="opacity"
47
+ class={buttonClass}
48
+ title={panel.makeHint ?? 'Make Opacity Mask: the topmost object fades the others by its brightness'}
49
+ aria-label="Make Opacity Mask"
50
+ disabled={!panel.canMake}
51
+ onclick={() => controller.makeMask('luminance')}
52
+ >
53
+ Opacity
54
+ </button>
55
+ <button
56
+ type="button"
57
+ data-vector-mask-action="release"
58
+ class={buttonClass}
59
+ title="Release: the mask goes back on top and draws as itself"
60
+ aria-label="Release Mask"
61
+ disabled={!g}
62
+ onclick={() => controller.releaseMask({ ids })}
63
+ >
64
+ Release
65
+ </button>
66
+ </div>
67
+ {#if g}
68
+ <div class="space-y-2" data-vector-mask-group={g.id} data-mask-type={g.type}>
69
+ <div class="flex items-center gap-1">
70
+ <select
71
+ aria-label="Mask type"
72
+ class={selectClass}
73
+ value={g.type}
74
+ title={VECTOR_MASK_TYPE_OPTIONS.find((o) => o.value === g.type)?.title}
75
+ onchange={(e) => controller.setMaskOptions({ type: e.currentTarget.value as VectorMaskType }, { ids })}
76
+ >
77
+ {#each VECTOR_MASK_TYPE_OPTIONS as o (o.value)}
78
+ <option value={o.value} title={o.title}>{o.label}</option>
79
+ {/each}
80
+ </select>
81
+ <label class="flex items-center gap-1 text-xs text-foreground" title="Show the contents where the mask hides them">
82
+ <input type="checkbox" aria-label="Invert mask" checked={g.invert} onchange={(e) => controller.setMaskOptions({ invert: e.currentTarget.checked }, { ids })} />
83
+ Invert
84
+ </label>
85
+ </div>
86
+ <div class="flex flex-wrap items-center gap-1">
87
+ <button
88
+ type="button"
89
+ data-vector-mask-action="edit-mask"
90
+ class={buttonClass}
91
+ aria-pressed={g.editing === 'mask'}
92
+ title="Edit Mask: select the mask (a path opens for anchor editing)"
93
+ aria-label="Edit Mask"
94
+ onclick={() => controller.editMask({ id: g.id })}
95
+ >
96
+ Edit mask
97
+ </button>
98
+ <button
99
+ type="button"
100
+ data-vector-mask-action="edit-contents"
101
+ class={buttonClass}
102
+ aria-pressed={g.editing === 'contents'}
103
+ title="Edit Contents: select what the mask masks"
104
+ aria-label="Edit Contents"
105
+ disabled={!g.contents.length}
106
+ onclick={() => controller.editMaskContents({ id: g.id })}
107
+ >
108
+ Contents
109
+ </button>
110
+ <button
111
+ type="button"
112
+ data-vector-mask-action="toggle"
113
+ class={buttonClass}
114
+ aria-pressed={g.enabled}
115
+ title={g.enabled ? 'Turn the mask off (hide it): the contents draw whole' : 'Turn the mask on'}
116
+ aria-label="Mask on"
117
+ onclick={() => controller.setMaskOptions({ enabled: !g.enabled }, { ids })}
118
+ >
119
+ {g.enabled ? 'On' : 'Off'}
120
+ </button>
121
+ </div>
122
+ </div>
123
+ {/if}
124
+ </div>
125
+ {/if}
@@ -0,0 +1,15 @@
1
+ import { type VectorEditorController } from '@pixerate/editor/vector';
2
+ /**
3
+ * Masks of the selection: Make Clipping Mask, Make Opacity Mask (the topmost selected object
4
+ * masks the others) and Release; for a mask group (or something inside one) its type, Invert, a
5
+ * switch that turns the mask off by hiding it, and Edit Mask / Edit Contents, which select the
6
+ * mask (a path opens for anchor editing) or what it masks. Each change is one undo step.
7
+ */
8
+ interface Props {
9
+ controller: VectorEditorController;
10
+ class?: string;
11
+ }
12
+ declare const VectorMaskPanel: import("svelte").Component<Props, {}, "">;
13
+ type VectorMaskPanel = ReturnType<typeof VectorMaskPanel>;
14
+ export default VectorMaskPanel;
15
+ //# sourceMappingURL=VectorMaskPanel.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VectorMaskPanel.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorMaskPanel.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgD,KAAK,sBAAsB,EAAuB,MAAM,yBAAyB,CAAC;AAIvI;;;;;GAKG;AACH,UAAU,KAAK;IACb,UAAU,EAAE,sBAAsB,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAgEH,QAAA,MAAM,eAAe,2CAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
@@ -0,0 +1,45 @@
1
+ <script lang="ts">
2
+ import type { VectorRenderMask, VectorRenderNode } from '@pixerate/editor/vector';
3
+ import VectorSceneNodes from './VectorSceneNodes.svelte';
4
+
5
+ /**
6
+ * A mask group's contents under its mask: the `<clipPath>` (clip) or `<mask>` (alpha, luminance;
7
+ * the mask drawn inside, through the inverting filter when inverted), and an inner `<g>` that
8
+ * references it. The group's own transform, opacity, blend mode and filter are on the outer `<g>`.
9
+ */
10
+ interface Props {
11
+ mask: VectorRenderMask;
12
+ nodes: readonly VectorRenderNode[];
13
+ }
14
+
15
+ let { mask, nodes }: Props = $props();
16
+ </script>
17
+
18
+ <defs>
19
+ {#if mask.clip}
20
+ <clipPath id={mask.id}>
21
+ {#each mask.clip as c, i (i)}
22
+ <path d={c.d} clip-rule={c.clipRule} />
23
+ {/each}
24
+ </clipPath>
25
+ {:else if mask.bounds}
26
+ <mask
27
+ id={mask.id}
28
+ maskUnits="userSpaceOnUse"
29
+ x={mask.bounds.x}
30
+ y={mask.bounds.y}
31
+ width={mask.bounds.width}
32
+ height={mask.bounds.height}
33
+ style={mask.type === 'alpha' ? 'mask-type: alpha' : undefined}
34
+ >
35
+ {#if mask.filter}
36
+ <g filter={mask.filter}><VectorSceneNodes nodes={mask.nodes ?? []} /></g>
37
+ {:else}
38
+ <VectorSceneNodes nodes={mask.nodes ?? []} />
39
+ {/if}
40
+ </mask>
41
+ {/if}
42
+ </defs>
43
+ <g data-vector-masked="" clip-path={mask.clip ? `url(#${mask.id})` : undefined} mask={mask.clip ? undefined : `url(#${mask.id})`}>
44
+ <VectorSceneNodes {nodes} />
45
+ </g>
@@ -0,0 +1,14 @@
1
+ import type { VectorRenderMask, VectorRenderNode } from '@pixerate/editor/vector';
2
+ /**
3
+ * A mask group's contents under its mask: the `<clipPath>` (clip) or `<mask>` (alpha, luminance;
4
+ * the mask drawn inside, through the inverting filter when inverted), and an inner `<g>` that
5
+ * references it. The group's own transform, opacity, blend mode and filter are on the outer `<g>`.
6
+ */
7
+ interface Props {
8
+ mask: VectorRenderMask;
9
+ nodes: readonly VectorRenderNode[];
10
+ }
11
+ declare const VectorMaskedNodes: import("svelte").Component<Props, {}, "">;
12
+ type VectorMaskedNodes = ReturnType<typeof VectorMaskedNodes>;
13
+ export default VectorMaskedNodes;
14
+ //# sourceMappingURL=VectorMaskedNodes.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VectorMaskedNodes.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorMaskedNodes.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAIhF;;;;GAIG;AACH,UAAU,KAAK;IACb,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAC;CACpC;AAgCH,QAAA,MAAM,iBAAiB,2CAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
@@ -2,6 +2,7 @@
2
2
  import type { VectorRenderNode } from '@pixerate/editor/vector';
3
3
  import Self from './VectorSceneNodes.svelte';
4
4
  import VectorPaintPath from './VectorPaintPath.svelte';
5
+ import VectorMaskedNodes from './VectorMaskedNodes.svelte';
5
6
 
6
7
  interface Props {
7
8
  nodes: readonly VectorRenderNode[];
@@ -13,8 +14,19 @@
13
14
 
14
15
  {#each nodes as node (node.id)}
15
16
  {#if node.kind === 'group'}
16
- <g data-vector-id={node.id} transform={node.transform} opacity={node.opacity < 1 ? node.opacity : undefined} filter={node.filter} style={blend(node.blendMode)}>
17
- <Self nodes={node.children} />
17
+ <g
18
+ data-vector-id={node.id}
19
+ data-vector-mask={node.mask?.type}
20
+ transform={node.transform}
21
+ opacity={node.opacity < 1 ? node.opacity : undefined}
22
+ filter={node.filter}
23
+ style={blend(node.blendMode)}
24
+ >
25
+ {#if node.mask}
26
+ <VectorMaskedNodes mask={node.mask} nodes={node.children} />
27
+ {:else}
28
+ <Self nodes={node.children} />
29
+ {/if}
18
30
  </g>
19
31
  {:else if node.layers}
20
32
  <!-- Fill and stroke stacks: each layer in order, bottom to top. -->
@@ -1 +1 @@
1
- {"version":3,"file":"VectorSceneNodes.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorSceneNodes.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAK9D,UAAU,KAAK;IACb,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAC;CACpC;AA8BH,QAAA,MAAM,gBAAgB,2CAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"VectorSceneNodes.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorSceneNodes.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAM9D,UAAU,KAAK;IACb,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAC;CACpC;AAmCH,QAAA,MAAM,gBAAgB,2CAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -20,6 +20,7 @@
20
20
  import VectorEffectsPanel from './VectorEffectsPanel.svelte';
21
21
  import VectorAppearancePanel from './VectorAppearancePanel.svelte';
22
22
  import VectorEnvelopePanel from './VectorEnvelopePanel.svelte';
23
+ import VectorMaskPanel from './VectorMaskPanel.svelte';
23
24
  import VectorGradientEditor from './VectorGradientEditor.svelte';
24
25
 
25
26
  interface Props {
@@ -225,5 +226,6 @@
225
226
  <VectorAppearancePanel {controller} />
226
227
  <VectorEffectsPanel {controller} />
227
228
  <VectorEnvelopePanel {controller} />
229
+ <VectorMaskPanel {controller} />
228
230
  {/if}
229
231
  </div>
@@ -1 +1 @@
1
- {"version":3,"file":"VectorStylePanel.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorStylePanel.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAYH,KAAK,sBAAsB,EAG5B,MAAM,yBAAyB,CAAC;AASjC,UAAU,KAAK;IACb,UAAU,EAAE,sBAAsB,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAiKH,QAAA,MAAM,gBAAgB,2CAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"VectorStylePanel.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorStylePanel.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAYH,KAAK,sBAAsB,EAG5B,MAAM,yBAAyB,CAAC;AAUjC,UAAU,KAAK;IACb,UAAU,EAAE,sBAAsB,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAmKH,QAAA,MAAM,gBAAgB,2CAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -8,6 +8,7 @@
8
8
  VECTOR_PATHFINDER_ACTIONS,
9
9
  VECTOR_FILL_GAP,
10
10
  vectorToolLabel,
11
+ isVectorMaskGroup,
11
12
  type VectorEditorController,
12
13
  type VectorTool
13
14
  } from '@pixerate/editor/vector';
@@ -61,6 +62,7 @@
61
62
  const count = $derived(view.selection.length);
62
63
  const points = $derived(view.tool === 'direct' ? view.selectedAnchors.length : 0);
63
64
  const groupSelected = $derived(view.selection.some((id) => controller.getNode(id)?.type === 'group'));
65
+ const maskSelected = $derived(view.selection.some((id) => isVectorMaskGroup(controller.getNode(id))));
64
66
 
65
67
  // Live booleans keep the operands editable; a lone live group or its operands take the operation too.
66
68
  let live = $state(false);
@@ -71,7 +73,9 @@
71
73
  { label: '∩', title: 'Intersect', run: () => controller.booleanOp('intersect', { live }), disabled: booleanDisabled },
72
74
  { label: '⊻', title: 'Exclude', run: () => controller.booleanOp('exclude', { live }), disabled: booleanDisabled },
73
75
  { label: 'G', title: 'Group', run: () => controller.group(), disabled: count < 1 },
74
- { label: 'U', title: 'Ungroup', run: () => controller.ungroup(), disabled: !groupSelected }
76
+ { label: 'U', title: 'Ungroup', run: () => controller.ungroup(), disabled: !groupSelected },
77
+ { label: '◐', title: `Make Clipping Mask (${modKey}+7)`, run: () => controller.makeMask('clip'), disabled: count < 2 },
78
+ { label: '◑', title: `Release Mask (${modKey}+Alt+7)`, run: () => controller.releaseMask(), disabled: !maskSelected }
75
79
  ]);
76
80
  const arrange = $derived([
77
81
  { label: '⇈', title: 'Bring to Front', run: () => controller.arrange('front'), disabled: count < 1 },
@@ -1 +1 @@
1
- {"version":3,"file":"VectorToolbar.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorToolbar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EASH,KAAK,sBAAsB,EAC3B,KAAK,UAAU,EAChB,MAAM,yBAAyB,CAAC;AAMjC,UAAU,KAAK;IACb,UAAU,EAAE,sBAAsB,CAAC;IACnC,oCAAoC;IACpC,KAAK,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAC9B,4CAA4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qEAAqE;IACrE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAiJH,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"VectorToolbar.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorToolbar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAUH,KAAK,sBAAsB,EAC3B,KAAK,UAAU,EAChB,MAAM,yBAAyB,CAAC;AAMjC,UAAU,KAAK;IACb,UAAU,EAAE,sBAAsB,CAAC;IACnC,oCAAoC;IACpC,KAAK,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAC9B,4CAA4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qEAAqE;IACrE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAoJH,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -8,6 +8,8 @@ export { default as VectorToolbar } from "./VectorToolbar.svelte";
8
8
  export { default as VectorStylePanel } from "./VectorStylePanel.svelte";
9
9
  export { default as VectorEffectsPanel } from "./VectorEffectsPanel.svelte";
10
10
  export { default as VectorEnvelopePanel } from "./VectorEnvelopePanel.svelte";
11
+ export { default as VectorMaskPanel } from "./VectorMaskPanel.svelte";
12
+ export { default as VectorMaskedNodes } from "./VectorMaskedNodes.svelte";
11
13
  export { default as VectorSceneNodes } from "./VectorSceneNodes.svelte";
12
14
  export { default as VectorGradientDefs } from "./VectorGradientDefs.svelte";
13
15
  export { default as VectorFilterDefs } from "./VectorFilterDefs.svelte";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vector/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAE/D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AACtF,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAChF,cAAc,gCAAgC,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,cAAc,aAAa,CAAC;AAE5B,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC7E,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vector/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAE/D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AACtF,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAChF,cAAc,gCAAgC,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,cAAc,aAAa,CAAC;AAE5B,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC7E,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
@@ -5,6 +5,8 @@ export { default as VectorToolbar } from "./VectorToolbar.svelte";
5
5
  export { default as VectorStylePanel } from "./VectorStylePanel.svelte";
6
6
  export { default as VectorEffectsPanel } from "./VectorEffectsPanel.svelte";
7
7
  export { default as VectorEnvelopePanel } from "./VectorEnvelopePanel.svelte";
8
+ export { default as VectorMaskPanel } from "./VectorMaskPanel.svelte";
9
+ export { default as VectorMaskedNodes } from "./VectorMaskedNodes.svelte";
8
10
  export { default as VectorSceneNodes } from "./VectorSceneNodes.svelte";
9
11
  export { default as VectorGradientDefs } from "./VectorGradientDefs.svelte";
10
12
  export { default as VectorFilterDefs } from "./VectorFilterDefs.svelte";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor-svelte",
3
- "version": "0.78.0",
3
+ "version": "0.79.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.106.0"
142
+ "@pixerate/editor": "0.108.0"
143
143
  },
144
144
  "peerDependencies": {
145
145
  "@dagrejs/dagre": "^1.1.4",