fix(media-player): el boton de velocidad no decia su velocidad, y los sliders ignoraban la direccion

Dos defectos que solo aparecieron al MIRAR la pagina en un navegador real
(el panel embebido va a innerWidth 0 y sus medidas mienten: durante esta
misma sesion invento tres "overflow" inexistentes).

1) EL BOTON "x". El usuario pregunto que era ese boton que "no hace nada".
Si hacia: playbackRate llegaba a 1.5. Lo que no hacia era DECIRLO. El morfo
declaraba `data-rate` con propRef pero sin `emit: 'value'`, y sin enum el
compilador lo trata como bandera de presencia (compile.ts: hasEnum false y
emit !== 'value' -> 'html-presence'), asi que emitia data-rate="" para
siempre. El wrapper eidos pinta `{data-rate ?? 1}x` y `??` NO atrapa la
cadena vacia, de modo que el control mostraba un "x" pelado mientras la
velocidad cambiaba por debajo. Un boton que parece muerto. Anadido
`emit: 'value'` (el patron que chronos.ts ya practicaba). Verificado en
Chrome: "1x" -> click -> "1.25x" con playbackRate 1.25.

2) LA DIRECCION. Ni el TimeSlider ni el VolumeSlider recibian `dir`: cero
menciones en ambos ficheros. Caian al default del Slider, que resuelve de
la config de soma — que es GLOBAL de la app. Resultado medido: cromo del
player en ltr con sus DOS sliders en rtl, pulgares anclados al borde
equivocado. Es el mismo defecto que el Waveform tenia hoy ("dir no se
reenviaba al Slider embebido") y el mismo arreglo: el player gana `dir` y
lo reenvia a los dos. Verificado en Chrome: stage/player/ambos sliders
coinciden, tiempo en left:0% con valor 0 y volumen en left:100% al maximo.

La demo reenvia ahora su eje `dir` a los dos roots (AudioPlayer y
MediaPlayer), asi que el RTL del player es comprobable POR PRIMERA VEZ: el
arnes fijaba el stage y los roots no lo recibian.

Verificado: check 75 = baseline, 0 propios; 19 tests; component:audit
--only media-player PASS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
alpha-0.1-dir-prefs
dev 2 months ago
parent 4356020e37
commit 3ceda5b593

@ -606,7 +606,14 @@ export const mediaPlayerMorfo = {
role: 'button',
optional: true,
data: [
{ attr: 'data-rate', value: v.propRef('rate'), severity: 'optional' },
// `emit: 'value'` is REQUIRED here: with no `values` enum the compiler
// defaults an attr to a presence flag (compile.ts — `hasEnum` false and
// `emit !== 'value'` → 'html-presence'), which emitted `data-rate=""`
// forever. The eidos RateButton renders `{data-rate ?? 1}×`, and `??`
// does not catch an empty string — so the control painted a bare "×"
// while the rate really was changing underneath. Caught by eye, not by
// any guard: the button looked dead.
{ attr: 'data-rate', value: v.propRef('rate'), emit: 'value', severity: 'optional' },
{ attr: 'data-disabled', severity: 'optional' }
],
aria: [

@ -34,7 +34,11 @@
{@render child({ props: mergedProps })}
{:else}
<div {...mergedProps}>
<!-- `dir` comes from the PLAYER, not from the Slider's own config default:
otherwise a player inside an LTR subtree of an RTL app renders LTR chrome
with RTL-anchored sliders. -->
<Slider.Provider
dir={provider.opts.dir?.current}
value={[provider.currentTime]}
min={0}
max={Math.max(provider.duration, 1)}

@ -34,7 +34,11 @@
{@render child({ props: mergedProps })}
{:else}
<div {...mergedProps}>
<!-- `dir` comes from the PLAYER, not from the Slider's own config default:
otherwise a player inside an LTR subtree of an RTL app renders LTR chrome
with RTL-anchored sliders. -->
<Slider.Provider
dir={provider.opts.dir?.current}
value={[Math.round(provider.volume * 100)]}
min={0}
max={100}

@ -12,6 +12,7 @@
id = createId(uid, 'media-player'),
media = 'video',
seekStep = 10,
dir = undefined,
ariaLabel = undefined,
metadata = undefined,
createProvider,
@ -28,6 +29,7 @@
),
media: readableActive(() => media),
seekStep: readableActive(() => seekStep),
dir: readableActive(() => dir),
ariaLabel: readableActive(() => ariaLabel),
metadata: readableActive(() => metadata),
// svelte-ignore state_referenced_locally

@ -3,6 +3,7 @@ import { boolToEmptyStrOrUndef } from '$adom';
import type { Active, ActiveProps } from '$libs/reactive';
import type { TimerHandle } from '$timer';
import { KEYS } from '../../keyboard';
import type { Direction } from '../../types/component';
import { mediaPlayerMorfo } from '../../../morfo/components/media-player';
import { Soma } from '../../core/soma.svelte';
@ -39,6 +40,8 @@ interface MediaPlayerOpts
media: 'video' | 'audio';
/** Seconds the ±seek buttons and arrow keys move. */
seekStep: number;
/** Forwarded to the embedded Sliders so they anchor like their surface. */
dir?: Direction;
ariaLabel: string | undefined;
}> {
/**

@ -7,6 +7,7 @@ import type {
PrimitiveElementAttributes,
PrimitiveSpanAttributes
} from '../../types';
import type { Direction } from '../../types/component';
import type { MediaProvider } from './media-provider';
import type { SoundMediaMetadata } from '$sound';
@ -19,6 +20,16 @@ export type MediaPlayerProps = WithChild<{
media?: 'video' | 'audio';
/** Seconds the ±seek buttons and arrow keys move. @default 10 */
seekStep?: number;
/**
* Text direction, forwarded to the EMBEDDED Sliders (time + volume) so their
* value anchoring matches the surface they live on.
*
* Without this the Sliders fell back to the soma config's direction, which is
* app-global: a player inside an LTR subtree of an RTL app rendered LTR chrome
* with RTL sliders — thumbs anchored to the wrong edge. Same defect the
* Waveform had (its `dir` was not forwarded either) and the same fix.
*/
dir?: Direction;
/** Accessible name for the player region. @default localized "Media player" */
ariaLabel?: string;
/**

@ -173,6 +173,7 @@
<!-- The 90% door: one tag, identity = the OS session (D-AP2.5). -->
<AudioPlayer
{variant}
{dir}
{size}
{seekStep}
src={AUDIO_SRC}
@ -184,6 +185,7 @@
{:else}
<MediaPlayer
{media}
{dir}
{seekStep}
{size}
metadata={videoMetadata}

Loading…
Cancel
Save

Powered by TurnKey Linux.