You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/sema/components/media-player.ts

147 lines
6.4 KiB

import { semaSelector } from '$uix/morfo';
import { mediaPlayerMorfo } from '$uix/morfo/components/media-player';
import { buttonMorfo } from '$uix/morfo/components/button';
import { sliderMorfo } from '$uix/morfo/components/slider';
import { SILENT } from '../channels';
import type { Sema } from '../sema-map';
/**
* Media-player perceptual defaults — SILENT transport by definition (signed
* 2026-07-31, D-AP2.7 v2).
*
* The player is the one component whose OUTPUT is audio: a UI tick on its own
* audio, un tick de UI compite con la obra»; «el silencio es una firma
* válida»). So the pack subtracts the family gain (the D.5 pattern — never
* replacing intent primitives, so `threat`/`fulfill` deltas still surface)
* on everything a user gesture fires:
*
* - the transport commits (`commit-toggle-play` / `commit-toggle-mute` /
* `commit-set-rate`), which stamp on their own buttons;
* - the composed Buttons' `contact-activate` (descendant rule — H-10: the
* silence must cover descendants, or the press still ticks);
* - the composed Sliders' release `commit-set` (descendant rule — scrubber
* and volume alike).
*
* Deliberately NOT silenced: `commit-fail` (risk must be heard — exactly what
* the gain subtraction preserves) and `commit-complete` (informs about the
* work itself; revisit with real listening if loops make it fatiguing).
*
* The Sliders' `handle-pick` / `handle-drag` are silenced by CHANNEL
* (`channels: ['haptic']`, higher-specificity descendant rules): both are
* STRUCTURAL events with no intent — the book concentrates evaluation on the
* drop, which the `commit-set` subtraction already covers — so dropping their
* sound channel here erases no evaluative load (the rule that forbids
* channel-dropping guards intents, and handle carries none). The drag's
* per-emit resolver still owns the sound PRIMITIVES, but a signal whose sound
* channel is inactive plays nothing. Real listening asked for this
* (2026-07-31: «los botones ya no… pero los sliders sí»).
*
* Reactivating the voice is the app layer's job (cascade 5b / own tunings),
* which is what that layer exists for — no component prop duplicates sema.
* Complementary, not redundant: this pack silences THE PLAYER's transport;
* `duckUiWhileContent` (the sound service) attenuates the REST of the app
* while content plays. Haptics stay — a tactile tap does not compete with
* the work.
*
* Sound is chosen, never authored: the family's verb table names it and the
* intent selects a variant. A rule here only appears when this component
* DIFFERS from that default — see `architecture/sema.md` §the sound pack.
*/
const onProvider = (matchers?: Parameters<typeof semaSelector<typeof mediaPlayerMorfo>>[2]) =>
semaSelector(mediaPlayerMorfo, 'provider', matchers);
const onPlayButton = (matchers?: Parameters<typeof semaSelector<typeof mediaPlayerMorfo>>[2]) =>
semaSelector(mediaPlayerMorfo, 'play-button', matchers);
const onRateButton = (matchers?: Parameters<typeof semaSelector<typeof mediaPlayerMorfo>>[2]) =>
semaSelector(mediaPlayerMorfo, 'rate-button', matchers);
/**
* Descendant selectors: both halves come from the typed builder (renames still
* break at compile time); only the combinator is literal.
*/
const inPlayer = (descendant: string) => `${onProvider()} ${descendant}`;
/**
* Same silence, rooted at the volume PART instead of the player.
*
* A descendant rule hung off the provider stops matching the moment the control
* is PORTALLED out of the player — which is exactly what the floating volume
* does — and the slider gets its full voice back. The part's own marker travels
* with it wherever it is rendered, so this is the root that survives the portal.
* Same class of escape as the `--_mp-*` tokens, which do not cross it either.
*/
const inVolume = (descendant: string) =>
`${semaSelector(mediaPlayerMorfo, 'volume-slider')} ${descendant}`;
export const mediaPlayerSema: Sema = {
name: 'media-player',
cascade: [
{
// The event's morfo target is the play-button (the stamp lands THERE,
// measured 2026-07-30, H-1/G-4). The subtraction is the commit family
// base (-0.3) — the tuning name comes from D.5.
selector: onPlayButton({ eventName: 'commit-toggle-play' }),
sound: SILENT,
haptic: { kind: 'tap' }
},
{
selector: semaSelector(mediaPlayerMorfo, 'mute-button', {
eventName: 'commit-toggle-mute'
}),
sound: SILENT,
haptic: { kind: 'tap' }
},
{
selector: onRateButton({ eventName: 'commit-set-rate' }),
sound: SILENT,
haptic: { kind: 'tap' }
},
{
// H-10: the composed Button's contact still ticked under any
// provider-level rule — the silence must reach descendants.
selector: inPlayer(semaSelector(buttonMorfo, 'provider', { eventName: 'contact-activate' })),
sound: SILENT
},
{
// The composed Sliders' release (scrubber + volume): same subtraction.
selector: inPlayer(semaSelector(sliderMorfo, 'provider', { eventName: 'commit-set' })),
sound: SILENT
},
{
// The Sliders' grab cue: structural, no intent — silenced by channel
// (the pickup cue ships absolute primitives, so a subtraction would
// couple to its value). The tactile tick stays.
selector: inPlayer(semaSelector(sliderMorfo, 'provider', { eventName: 'handle-pick' })),
channels: ['haptic']
},
{
// The Sliders' continuous drag: structural, no intent. Its per-emit
// resolver owns the sound primitives, but with the sound channel
// inactive for this signal nothing plays — the scrub stays tactile.
selector: inPlayer(semaSelector(sliderMorfo, 'provider', { eventName: 'handle-drag' })),
channels: ['haptic']
},
// ── The same three, rooted at the volume part ────────────────────────
// Not redundant: the floating volume PORTALS its slider out of the
// player, so the three rules above stop matching it and the control
// recovers its full voice. These follow it out.
{
selector: inVolume(semaSelector(sliderMorfo, 'provider', { eventName: 'commit-set' })),
sound: SILENT
},
{
selector: inVolume(semaSelector(sliderMorfo, 'provider', { eventName: 'handle-pick' })),
channels: ['haptic']
},
{
selector: inVolume(semaSelector(sliderMorfo, 'provider', { eventName: 'handle-drag' })),
channels: ['haptic']
},
{
selector: onProvider({ eventName: 'commit-complete' })
},
{
selector: onProvider({ eventName: 'commit-fail' })
}
]
};

Powered by TurnKey Linux.