diff --git a/src/uix/morfo/components/color-picker.ts b/src/uix/morfo/components/color-picker.ts index ee4edc9d8..3c58b8978 100644 --- a/src/uix/morfo/components/color-picker.ts +++ b/src/uix/morfo/components/color-picker.ts @@ -7,6 +7,30 @@ export const colorPickerMorfo = { scope: ['soma', 'sema'], apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/', events: [ + // Area drag — same shape as the slider's `handle-*` events so sema + // consumers can hook the family/verb uniformly (sounds, haptics, + // transient styling) regardless of which dragger emitted. + { + name: 'handle-pick', + semantic: { + family: 'handle', + verb: 'pick', + target: v.partRef('area'), + sequence: 'coincident' + } + }, + { + name: 'handle-drag', + semantic: { + family: 'handle', + verb: 'drag', + target: v.partRef('area'), + sequence: 'coincident' + } + }, + // Value commit — fires on pointer release (area thumb / channel + // slider / swatch click / eyedropper pick). `commit-set` is the + // canonical "save" signal. { name: 'commit-set', semantic: { diff --git a/src/uix/soma/components/color-picker/color-picker-provider.svelte.ts b/src/uix/soma/components/color-picker/color-picker-provider.svelte.ts index 1164a96dc..9212e01c9 100644 --- a/src/uix/soma/components/color-picker/color-picker-provider.svelte.ts +++ b/src/uix/soma/components/color-picker/color-picker-provider.svelte.ts @@ -692,6 +692,8 @@ export class ColorPickerAreaProvider { const withX = setColorChannel(this.provider.effectiveColor, this.xChannel, xValue); const withBoth = setColorChannel(withX, this.yChannel, yValue); this.provider.opts.value.current = withBoth; + // Sema: emit `handle-drag` so consumers can stream tactile feedback. + void this.provider.runtime.trigger('handle-drag'); } readonly onpointerdown = (e: PointerEvent): void => { @@ -699,6 +701,8 @@ export class ColorPickerAreaProvider { const target = e.currentTarget as HTMLElement; target.setPointerCapture(e.pointerId); this.pointerId = e.pointerId; + // Sema: emit `handle-pick` at drag start (mirrors slider semantics). + void this.provider.runtime.trigger('handle-pick'); this.handlePointerMove(e.clientX, e.clientY, target.getBoundingClientRect()); e.preventDefault(); }; @@ -712,6 +716,8 @@ export class ColorPickerAreaProvider { readonly onpointerup = (e: PointerEvent): void => { if (this.pointerId !== e.pointerId) return; this.pointerId = null; + // Sema: fire `commit-set` on release (canonical save signal). + void this.provider.runtime.trigger('commit-set'); this.provider.commitChange(); }; @@ -837,6 +843,11 @@ export class ColorPickerAreaThumbProvider { if (nextX !== x) next = setColorChannel(next, this.area.xChannel, nextX); if (nextY !== y) next = setColorChannel(next, this.area.yChannel, nextY); this.provider.opts.value.current = next; + + // Sema: each keypress is one drag-step + commit (slider parity). + void this.provider.runtime.trigger('handle-drag'); + void this.provider.runtime.trigger('commit-set'); + this.provider.commitChange(); }; readonly props = $derived.by(() => { diff --git a/web/routes/uix/components/color-picker/+page.svelte b/web/routes/uix/components/color-picker/+page.svelte index 0bf0270f7..fc06f332a 100644 --- a/web/routes/uix/components/color-picker/+page.svelte +++ b/web/routes/uix/components/color-picker/+page.svelte @@ -131,6 +131,7 @@ [ "