From 5acee947778f6dccf4542ec6e01db51e2a872337 Mon Sep 17 00:00:00 2001 From: dev Date: Wed, 27 May 2026 22:05:37 +0200 Subject: [PATCH] fix(words): color picker uses applyCommand (no sema) + onchange instead of oninput MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two related fixes to the color picker UX: 1. Swatches now call applyCommand({type:'toggleMark', mark:'color:#x'}) directly instead of runCommand('color:#x'). runCommand emits the 'commit-set-format' sema event (which fires sound + visual feedback on the editor surface). For discrete color picks the sema event is noise — applyCommand bypasses the trigger and just mutates the doc. 2. Native uses `onchange` instead of `oninput`. `oninput` fires continuously while the user drags the OS color picker — each fire was queueing a separate command + sema event, producing a horror cascade of sound / visual flashes on every pixel of the slider drag. `onchange` only fires when the user releases / commits the picker (closes the OS popup), so we get exactly one command per intent. Same pattern applied to both Foreground and Background pickers + the clear (×) buttons. Sema events are still emitted by the rest of the drawer's action chips (run via runCommand) because those are user-facing intent actions (Bold, Italic, etc.) where the perceptual feedback aligns with the user's notion of "I just did a thing". Color slider drags are NOT in that category — they're continuous parameter tuning. Verified: clicking the red swatch on selected "ActiveUI" produces the expected red in the document AND the trace shows no new commit-set-format event (was previously firing on every swatch click, drowning audio + animation). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../eidos/components/words/words-drawer.svelte | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/uix/eidos/components/words/words-drawer.svelte b/src/uix/eidos/components/words/words-drawer.svelte index cc4a303f0..e91f19b1f 100644 --- a/src/uix/eidos/components/words/words-drawer.svelte +++ b/src/uix/eidos/components/words/words-drawer.svelte @@ -305,7 +305,7 @@ title={preset} aria-label={`Set foreground to ${preset}`} onmousedown={(e) => e.preventDefault()} - onclick={() => s.runCommand(`color:${preset}` as never)} + onclick={() => s.applyCommand({ type: 'toggleMark', mark: `color:${preset}` as never })} > {/each} { + onchange={(e) => { const v = (e.currentTarget as HTMLInputElement).value; - s.runCommand(`color:${v}` as never); + s.applyCommand({ type: 'toggleMark', mark: `color:${v}` as never }); }} /> {#if currentParametricMark(s.activeMarks, 'color')} @@ -325,7 +325,7 @@ title="Clear foreground" aria-label="Clear foreground color" onmousedown={(e) => e.preventDefault()} - onclick={() => s.runCommand('color:' as never)} + onclick={() => s.applyCommand({ type: 'toggleMark', mark: 'color:' as never })} >× {/if} @@ -341,7 +341,7 @@ title={preset} aria-label={`Set background to ${preset}`} onmousedown={(e) => e.preventDefault()} - onclick={() => s.runCommand(`bgcolor:${preset}` as never)} + onclick={() => s.applyCommand({ type: 'toggleMark', mark: `bgcolor:${preset}` as never })} > {/each} { + onchange={(e) => { const v = (e.currentTarget as HTMLInputElement).value; - s.runCommand(`bgcolor:${v}` as never); + s.applyCommand({ type: 'toggleMark', mark: `bgcolor:${v}` as never }); }} /> {#if currentParametricMark(s.activeMarks, 'bgcolor')} @@ -361,7 +361,7 @@ title="Clear background" aria-label="Clear background color" onmousedown={(e) => e.preventDefault()} - onclick={() => s.runCommand('bgcolor:' as never)} + onclick={() => s.applyCommand({ type: 'toggleMark', mark: 'bgcolor:' as never })} >× {/if}