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/docs/process/CONTINUE-player-rtl.md

9.3 KiB

CONTINUE — RTL del Slider y del media-player (+ la cola del hilo de audio)

Kickoff: "Lee docs/process/CONTINUE-player-rtl.md y empieza por §2." Fecha: 2026-08-01 · actualizado 2026-08-02 · Rama alpha-0.1-dir-prefs (sale de alpha-0.1-sec-dom). §1 está CERRADO — no lo rehagas. Lo abierto empieza en §2. El hilo de audio propiamente dicho sigue en CONTINUE-sound-engine.md; este documento recoge lo que la mirada del usuario abrió el 2026-08-01 y quedó SIN cerrar.

Lo que se cerró el 2026-08-01 (7 commits, verificados)

Commit Qué
df86a4e50 El anillo del commit dejaba de enmarcar contenedores (slider + media-player) · saltar al final ya no anuncia compleción falsa
e096f65e3 MediaSession verificada por el usuario (carátula + título + artista en el hub de Chrome)
905012507 Waveform: el buffered vuelve a banda recta con acento lavado
58acb778e Waveform F6 firmada — iniciativa COMPLETA
77f8a4e46 La demo del waveform carga solo la pista real; sample deja de fingir que es una prop
4356020e3 La carátula de card sale a token (--audio-player-artwork-size-card)
3ceda5b59 El botón de velocidad ya dice su velocidad (emit: 'value') · el player reenvía dir a sus dos sliders

1. PRIORIDAD — el Slider está roto en RTL · CERRADO 2026-08-02

Cerrado en 013ceac57 + 5469e05df (rama alpha-0.1-dir-prefs). La hipótesis de este documento acertó las dos piezas visibles — el Slider sí estampaba dir y slider.css sí mezclaba lógicas con físicas — pero eran síntomas, no la causa. Lo que resultó ser, medido:

Del slider slider.css emparejaba inset-inline-start: 50% (lógico) con translateX(-50%) (FÍSICO, no se voltea) en las tres reglas verticales. Medido en Chrome: raíz/pulgar/ticks en x=961, raíl y relleno en x=955. El Tick se libraba porque ya usaba margin-inline-start — ése era el idioma correcto del propio fichero. Mismo defecto en accordion.css (text-align: left), el único físico que quedaba en todo eidos.
La causa raíz, que no estaba en las hipótesis makeDimension().get() (arts/prefs) leía engine.snapshot() esquivando la celda $state, así que soma.prefs.getDir() era una lectura sin tracking y los ~64 componentes que resuelven su dirección desde prefs la congelaban al montarse. La proyección DOM se salvaba porque usa onChange: misma dimensión, dos caminos de lectura, uno solo reactivo.
Y el estampado 33 de 37 componentes escribían dir con un valor SIEMPRE concreto → una app que ponga <html dir="rtl"> sin registrar la preferencia se encontraba 33 islas del revés. Ahora el atributo se omite cuando nadie afirmó nada (modelo de Zag, aplicado uniforme).

No hizo falta elegir «un solo dueño de la inversión»: el JS sigue calculando físico y el CSS sigue siendo lógico. No había doble volteo — había un transform físico donde tocaba uno lógico.

✅ La contradicción del pulgar del volumen queda explicada: con getDir() congelado, el mismo slider daba lecturas distintas según cuándo se midiera respecto al montaje. No era una sonda mintiendo.

Guards que nacieron en rojo y ahora pasan: arts/prefs/test/active-prefs-reactivity.svelte.test.ts (intent · derivación · granularidad por clave) y uix/active-uix/test/prefs-view.svelte.test.ts.

De regalo: elegir árabe voltea la dirección por derivación desde el idioma — la cadena que active-architecture.md:407 documenta y que en esta app era código muerto, porque la shell fijaba intent desde el primer frame y no había ningún idioma RTL en el catálogo.

⚠️ Sigue abierto de este eje: html lang no sigue al idioma (el dir sí), y perm:check sin ejecutar — el topbar ya lleva data-perm-step, lo que hace que toda ruta tenga paso y la pasada sea mucho más larga.

2. La disposición de los botones del player en RTL — APARTE y abierto

El usuario insiste en que no es lo mismo que §1: aun con el Slider arreglado, la disposición que él ve sigue mal. Las sondas de la sesión decían que el orden mirroriza (orden exactamente invertido en vídeo y en audio/card, y el transporte interno ⏪ ▶ ⏩ → ⏩ ▶ ⏪), pero esas sondas se equivocaron varias veces ese día — dos de ellas midieron vídeo creyendo que medían audio. Tratar sus números como NO fiables y volver a mirar con la página delante.

3. Iconos de seek en RTL — decisión tomada, sin aplicar

Medido: las posiciones mirrorizan pero los iconos no se voltean (transform: none en ambas direcciones). Recomendación dada al usuario y aceptada implícitamente al pedir «arregla el rtl/ltr»: voltearlos. El icono no dice «derecha», dice «adelante en el tiempo», y el eje temporal en RTL corre hacia la izquierda — igual que la barra de progreso, que ancla en right. Si el slider avanza a la izquierda y la flecha de avanzar apunta a la derecha, se contradicen dentro del mismo control. Es lo que hacen YouTube y Spotify en árabe.

Decidir al aplicarlo: ¿volteo del icono en el propio botón, o regla RTL transversal de eidos? (afecta a cualquier icono direccional del catálogo).

4. Medición del escenario mixto — NO concluyente, repetir bien

Lo que SÍ quedó establecido: la media no abre ningún AudioContext (con audio sonando de verdad, cero contextos creados) — el transporte va por la tubería nativa, no por Web Audio, porque attach() es opt-in y nadie lo activa. Esa es la mitad importante de la invariante.

Lo que NO: la mitad del earcon. Se instrumentó AudioContext después del arranque, así que un contexto ya existente no se ve; el 0 creados sólo prueba que no se creó uno NUEVO. soundOn: true está en prefs.

Para cerrarlo: instrumentar antes del boot y llegar a la página con una navegación client-side (sin recargar, o el parche se pierde).

5. Iniciativas dormidas (cada una con su gate)

  • Captions — parte declarada en el morfo desde el principio y nunca implementada. Diagnóstico completo y medido: el player pone las pistas en mode='showing', así que Chrome pinta su caja de cues en bottom: 0 del <video> — la MISMA franja de 49px que la barra de controles — y como el vídeo computa z-index: auto frente al z-index: 4 de la barra al 82% de opacidad, llega el 18% del texto («apenas imperceptible»). ::cue NO puede arreglarlo: la especificación no admite propiedades de posición. Arreglo real: mode='hidden' + pintar activeCues en la parte declarada. Defecto latente adicional: captionsOn es un espejo de una sola dirección (nunca relee track.mode), así que si Chrome activa subtítulos por preferencia del SO salen cues mientras el botón dice que están apagados.
  • Waveform como scrubber del player — cero cables hoy entre ambos, y es deliberado (D-AP2.4 + PLAN-waveform.md §0 lo aplazan a la v2 del player). El nudo NO es cambiar el componente: es de dónde salen los picos. El contrato del waveform (D-WF.2) dice que los provee la app, y un player streamea, así que no puede decodificar su fuente sin bajársela entera. Verificado además que fetch de media.w3.org y soundhelix.com falla por CORS mientras el <audio> las reproduce sin problema. Empezar por la variante inline (nota de voz): ahí el audio SÍ es local y corto.

Lecciones de método del 2026-08-01 — leer antes de repetirlas

  • Medir el contrato NO es verificar la experiencia. Se afirmó tres veces que algo estaba verificado midiendo JS: la MediaSession (metadata poblada ≠ overlay del SO visible), el botón de velocidad (cambiaba playbackRate perfectamente… y no lo mostraba) y el RTL (se comprobó LTR y se dio RTL por bueno). Las tres las cazó el usuario.
  • El panel del navegador embebido NO SIRVE para nada visual: innerWidth: 0, visibility: hidden, el stage mide 2px. Ese día inventó tres «overflow» inexistentes y un hueco de carátula que estaba bien. Screenshot falla con «not compositing frames». Para cualquier juicio visual, usar el Chrome real del usuario (mcp__claude-in-chrome__*).
  • Comprobar que el control se pulsó de verdad. Dos medidas de esa sesión midieron el modo vídeo creyendo que era audio porque el chip no cambió y nadie lo verificó. Leer siempre data-media / data-variant después de clicar.
  • La distancia RGB engaña: avaló un gris que el ojo del usuario rechazó al instante. Está dominada por la luminosidad y es ciega al croma.
  • La forma manda sobre el color: el buffered del waveform se rechazó dos veces; la segunda porque una copia de la onda es la onda y se camufla por construcción, por muy distinto que sea su color.

Powered by TurnKey Linux.