@pixerate/editor-svelte 0.189.0 → 0.190.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.
@@ -29,6 +29,8 @@
29
29
  problems = r.problems.length ? r.problems.join(' ') : null;
30
30
  if (r.brush !== brush) onchange(r.brush);
31
31
  };
32
+ const setColour = (path: string, light: string, dark: string | null) => set(path, dark === null ? light : { light, dark });
33
+ const setAt = (path: string, list: Array<string | { light: string; dark: string }>, i: number, entry: string | { light: string; dark: string }) => set(path, list.map((x, j) => (j === i ? entry : x)));
32
34
  const save = async () => {
33
35
  try {
34
36
  await onsaveas?.(name.trim());
@@ -71,22 +73,44 @@
71
73
  <input aria-label={c.ariaLabel} value={c.value} placeholder={c.placeholder} onblur={(e) => e.currentTarget.value !== c.value && set(c.path, e.currentTarget.value.trim().toLowerCase())} class="w-28 rounded bg-slate-800 px-1.5 py-0.5" />
72
74
  </label>
73
75
  {:else if c.kind === 'colour'}
74
- <div class="flex items-center justify-between gap-2">
75
- <span class="text-slate-400">{c.label}</span>
76
- <span class="flex items-center gap-1">
77
- <input type="color" aria-label={c.ariaLabel} value={c.value ?? '#000000'} onchange={(e) => set(c.path, e.currentTarget.value)} class="h-5 w-8" />
78
- {#if c.value}<button type="button" class={button} onclick={() => set(c.path, undefined)}>None</button>{/if}
79
- </span>
76
+ <!-- One colour, or a light / dark pair resolved for the host's colour scheme. -->
77
+ <div class="flex flex-col gap-1">
78
+ <div class="flex items-center justify-between gap-2">
79
+ <span class="text-slate-400">{c.label}</span>
80
+ <span class="flex items-center gap-1">
81
+ <input type="color" aria-label={c.ariaLabel} title={c.dark !== null ? 'On light backgrounds' : undefined} value={c.value ?? '#000000'} onchange={(e) => setColour(c.path, e.currentTarget.value, c.dark)} class="h-5 w-8" />
82
+ {#if c.dark !== null}
83
+ <input type="color" aria-label={`${c.ariaLabel} (dark)`} title="On dark backgrounds" value={c.dark} onchange={(e) => setColour(c.path, c.value ?? '#000000', e.currentTarget.value)} class="h-5 w-8" />
84
+ {/if}
85
+ {#if c.value || c.gradient}<button type="button" class={button} onclick={() => set(c.path, undefined)}>None</button>{/if}
86
+ </span>
87
+ </div>
88
+ {#if c.gradient}
89
+ <span class="text-[10px] text-slate-500">A gradient (its own paint).</span>
90
+ {:else}
91
+ <label class="flex items-center gap-2">
92
+ <input type="checkbox" aria-label={`${c.ariaLabel}: light and dark`} checked={c.dark !== null} onchange={(e) => setColour(c.path, c.value ?? '#000000', e.currentTarget.checked ? (c.value ?? '#ffffff') : null)} /> Light / dark
93
+ </label>
94
+ {/if}
80
95
  </div>
81
96
  {:else if c.kind === 'palette'}
82
- <div class="flex flex-wrap items-center gap-1" role="group" aria-label={c.ariaLabel}>
97
+ <div class="flex flex-col gap-1" role="group" aria-label={c.ariaLabel}>
83
98
  <span class="text-slate-400">{c.label}</span>
84
- {#each c.value as col, i (`${col}-${i}`)}
85
- <button type="button" title={`Remove ${col}`} aria-label={`Remove ${col}`} class="h-4 w-4 rounded ring-1 ring-slate-700" style={`background: ${col}`} onclick={() => set(c.path, c.value.filter((_, j) => j !== i))}></button>
86
- {/each}
87
- {#if c.value.length < c.max}
88
- <input type="color" aria-label="Add a palette colour" onchange={(e) => set(c.path, [...c.value, e.currentTarget.value])} class="h-4 w-6" />
89
- {/if}
99
+ <div class="flex flex-wrap items-center gap-1">
100
+ {#each c.value as col, i (i)}
101
+ <span class="flex items-center gap-0.5 rounded bg-slate-900 p-0.5">
102
+ <input type="color" aria-label={`Palette colour ${i + 1}`} value={typeof col === 'string' ? col : col.light} onchange={(e) => setAt(c.path, c.value, i, typeof col === 'string' ? e.currentTarget.value : { ...col, light: e.currentTarget.value })} class="h-4 w-5" />
103
+ {#if typeof col !== 'string'}
104
+ <input type="color" aria-label={`Palette colour ${i + 1} (dark)`} value={col.dark} onchange={(e) => setAt(c.path, c.value, i, { ...col, dark: e.currentTarget.value })} class="h-4 w-5" />
105
+ {/if}
106
+ <button type="button" aria-label={`Remove palette colour ${i + 1}`} class="px-0.5 text-slate-400 hover:text-white" onclick={() => set(c.path, c.value.filter((_, j) => j !== i))}>×</button>
107
+ </span>
108
+ {/each}
109
+ {#if c.value.length < c.max}
110
+ <input type="color" aria-label="Add a palette colour" onchange={(e) => set(c.path, [...c.value, e.currentTarget.value])} class="h-4 w-6" />
111
+ <button type="button" class={button} onclick={() => set(c.path, [...c.value, { light: '#334155', dark: '#e2e8f0' }])}>Add light / dark pair</button>
112
+ {/if}
113
+ </div>
90
114
  </div>
91
115
  {:else if c.kind === 'stamps'}
92
116
  <div class="flex flex-wrap gap-1" role="group" aria-label={c.ariaLabel} title={`Up to ${c.max}`}>
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import StickerPicker from './StickerPicker.svelte';
3
3
  import { brushLoadColor, brushTakesPaint, downloadBytes, toggleStickerRef, DEFAULT_PAINT_GRADIENT, type PaintEditorController } from '@pixerate/editor/paint-editor';
4
- import { PAINT_PROFILE, type Brush, type BrushLibraryEntry } from '@pixerate/editor/brushes';
4
+ import { brushColourHex, PAINT_PROFILE, type Brush, type BrushLibraryEntry } from '@pixerate/editor/brushes';
5
5
  import { untrack } from 'svelte';
6
6
  import BrushEditor from './BrushEditor.svelte';
7
7
  import BrushLibraryBrowser from '../brushes/BrushLibraryBrowser.svelte';
@@ -49,6 +49,8 @@
49
49
  return untrack(() => controller.brushThumbnail(brush.id, { width: 240, height: 64, color }));
50
50
  });
51
51
  const carried = $derived(brushLoadColor(snap.mixerLoad));
52
+ // The brush's suggested colours, resolved for the colour scheme: one click makes one the paint colour.
53
+ const palette = $derived((spec.colour?.palette ?? []).flatMap((c) => brushColourHex(c, snap.colorScheme) ?? []));
52
54
  const loads = $derived(['dab', 'mixer', 'impasto'].includes(brush.engine) || (brush.engine === 'wet' && brush.wet.tool === 'paint'));
53
55
  const slug = (name: string) => name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 60) || 'brush';
54
56
  </script>
@@ -65,7 +67,18 @@
65
67
  {/if}
66
68
  <button type="button" class={button} onclick={() => controller.cleanMixerBrush(brush.engine === 'mixer')}>Clean</button>
67
69
  </div>
68
- {:else if section === 'colour' && brushTakesPaint(brush)}
70
+ {:else if section === 'colour'}
71
+ {#if palette.length}
72
+ <div class="flex flex-wrap items-center gap-1" role="group" aria-label="Paint with a palette colour">
73
+ {#each palette as hex, i (`${hex}-${i}`)}
74
+ <button type="button" aria-label={`Paint with ${hex}`} title={hex} aria-pressed={snap.color === hex} class={`h-5 w-5 rounded ring-1 ${snap.color === hex ? 'ring-white' : 'ring-slate-700'}`} style={`background: ${hex}`} onclick={() => controller.setColor(hex)}></button>
75
+ {/each}
76
+ </div>
77
+ {/if}
78
+ <label class="flex items-center gap-2" title="Choosing a brush with a default colour (the highlighter's yellow) makes it your colour unless this is on">
79
+ <input type="checkbox" aria-label="Keep my colour" checked={snap.colorLocked} onchange={(e) => controller.setColorLocked(e.currentTarget.checked)} /> Keep my colour when choosing a brush
80
+ </label>
81
+ {#if brushTakesPaint(brush)}
69
82
  <div class="flex flex-col gap-1.5" data-testid="brush-gradient">
70
83
  <div class="flex items-center justify-between gap-2">
71
84
  <span class="text-slate-400" title="A gradient of the brush's own, used instead of the paint (the multi-colour brush)">Brush gradient</span>
@@ -75,6 +88,7 @@
75
88
  <PaintGradientEditor value={brush.paint} colors={{ paint: snap.color, secondary: snap.secondaryColor }} onchange={(g) => run(() => controller.setBrush({ paint: g }))} />
76
89
  {/if}
77
90
  </div>
91
+ {/if}
78
92
  {/if}
79
93
  {/snippet}
80
94
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor-svelte",
3
- "version": "0.189.0",
3
+ "version": "0.190.0",
4
4
  "description": "Svelte bindings and prompt/rich-text components for @pixerate/editor",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -175,7 +175,7 @@
175
175
  "**/*.css"
176
176
  ],
177
177
  "dependencies": {
178
- "@pixerate/editor": "0.256.0"
178
+ "@pixerate/editor": "0.257.0"
179
179
  },
180
180
  "peerDependencies": {
181
181
  "@dagrejs/dagre": "^1.1.4",