Splitter previously had no sema bindings — handle-pick / handle-drag /
commit-set fired silently. Closes the asymmetry with Slider and Drawer.
Sema (src/uix/sema/components/splitter.ts):
- handle-pick → sound('handle.pickup.air', gain 0.10) + tick haptic
- handle-drag → channels: ['sound', 'haptic'] (channels-only; per-emit
signal.overrides from soma own the dynamic curve so cascade doesn't
clobber them)
- commit-set → sound('handle.snap.chime', gain 0.14) + tap haptic
Sound resolver (src/uix/sema/sounds.ts):
- resolveSplitterDragSound — heavier sonic register than slider since
splitters move whole panels, not a thumb:
pitch 480..820 Hz (slider: 760..1280)
centroid 2400..4800 (slider: 3600..6800) — less brilliant
roughness 0.006 (slider: 0.003) — woody texture, not metallic
envelope attack 16 / decay 72 / duration 76 — perceptual mass
gain 0.025 + velocity*0.03 — restrained ceiling, long swipes ok
Soma drag-signal infrastructure (splitter-provider.svelte.ts):
- Mirrors slider's frame-throttled signal pipeline:
dragSignalFrame / pendingDragPercent / lastDragSignalAt / lastDragPercent
DRAG_SIGNAL_MS = 72ms throttle (≈14 Hz emit cadence)
- dragSignalOverrides(percent01) computes velocity01 from frame delta,
builds SemaSignatureOverride with sound (via resolveSplitterDragSound)
and haptic (tick scaled by velocity).
- queueHandleDrag / cancelQueuedHandleDrag wrap requestFrame so each
pointermove only schedules one trigger per throttle window.
- onpointerdown calls resetDragSignal(startSizeA/100) to anchor the
velocity baseline at grab time.
- onpointerup / onlostpointercapture / unmount $effect all cancel the
queued frame so we don't fire post-release.
Verified in browser at /uix/components/splitter:
handle-pick → 1 event (family: handle)
handle-drag → frame-throttled across 6 pointermoves
commit-set → 1 event (family: commit, intent: neutral)
Checks: svelte-check 0 errors, 133/133 vitest (splitter + sema),
component:audit 91/91 PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
active-uix
parent
b504c1c4b7
commit
59505e6da2
@ -0,0 +1,55 @@
|
|||||||
|
import { semaSelector } from '$uix/morfo';
|
||||||
|
import { splitterMorfo } from '$uix/morfo/components/splitter';
|
||||||
|
import { sound } from '../sounds';
|
||||||
|
import type { Sema } from '../sema-map';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splitter perceptual defaults — mirrors the Slider's three-event shape
|
||||||
|
* (handle-pick → handle-drag → commit-set) with a heavier sonic register
|
||||||
|
* appropriate to moving whole panels rather than a thumb on a track.
|
||||||
|
*
|
||||||
|
* Sound:
|
||||||
|
* - `handle-pick` → `handle.pickup.air` (gain 0.10, slightly softer
|
||||||
|
* than slider's 0.12 — splitters get grabbed more often)
|
||||||
|
* - `handle-drag` → channel-only rule. Per-emit `signal.overrides`
|
||||||
|
* are computed by soma (`SplitterResizeTriggerProvider`) via
|
||||||
|
* `resolveSplitterDragSound({ value01, velocity01, direction })`
|
||||||
|
* so the dynamic curve survives the cascade.
|
||||||
|
* - `commit-set` → `handle.snap.chime` + tap haptic for the
|
||||||
|
* "I committed to this layout" landing cue.
|
||||||
|
*
|
||||||
|
* Haptic mirrors slider: tick on pick, tick driven by velocity during
|
||||||
|
* drag (also from soma overrides), tap on commit.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const onResizeTrigger = (
|
||||||
|
matchers?: Parameters<typeof semaSelector<typeof splitterMorfo>>[2]
|
||||||
|
) => semaSelector(splitterMorfo, 'resize-trigger', matchers);
|
||||||
|
|
||||||
|
const onProvider = (matchers?: Parameters<typeof semaSelector<typeof splitterMorfo>>[2]) =>
|
||||||
|
semaSelector(splitterMorfo, 'provider', matchers);
|
||||||
|
|
||||||
|
export const splitterSema: Sema = {
|
||||||
|
name: 'splitter',
|
||||||
|
cascade: [
|
||||||
|
{
|
||||||
|
selector: onResizeTrigger({ eventName: 'handle-pick' }),
|
||||||
|
channels: ['sound', 'haptic'],
|
||||||
|
sound: sound('handle.pickup.air', { gain: 0.1 }),
|
||||||
|
haptic: { kind: 'tick', intensity: 0.2, duration: 6 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Channels-only — the per-emit `signal.overrides` from soma
|
||||||
|
// own the dynamic pitch/centroid/gain. Adding a `sound:` here
|
||||||
|
// would clobber them.
|
||||||
|
selector: onResizeTrigger({ eventName: 'handle-drag' }),
|
||||||
|
channels: ['sound', 'haptic']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: onProvider({ eventName: 'commit-set' }),
|
||||||
|
channels: ['sound', 'haptic'],
|
||||||
|
sound: sound('handle.snap.chime', { gain: 0.14 }),
|
||||||
|
haptic: { kind: 'tap', intensity: 0.5, duration: 22 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
Loading…
Reference in new issue