astra
alpha-0.1-background
alpha-0.1-dir-prefs
alpha-0.1-sec-dom
menubar-v4-safe
active-uix
morfo-runtime
morfo-driven-soma
semantuix
glm-5
main
sium-v1.0
${ noResults }
26 Commits (b01646cfd8462dcd61e746d89dcc19dd3847ea17)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
5f024b1733 |
docs(eidos,process): el ancho minimo de bar queda escrito, y el handoff deja de deber tres decisiones
README de eidos: bar tiene ~900px de ancho minimo de diseño (medido
2026-08-04) — valor de la variante, como los tamaños de caratula — con la
disposicion firmada del arreglo real: ORDEN DE DESCARTE (bajo el umbral cae el
slider de volumen y el mute recupera sus 36px) via container query, el primer
caso concreto para reabrir D-AP2.9.
CONTINUE-sound-engine adjudicado en tres puntos: el rojo de audio-player ya
estaba RESUELTO en el codigo (categoria COMPETING_ROOTS, lint.test.ts:70, con
la razon escrita — verificado 18/18); el aria-pressed del caption-button queda
FIRMADO (b) y ejecutado (
|
2 months ago |
|
|
653eb5fa3c |
docs(process): el switch Sound del topbar estaba cableado desde d161cedcc — medido hoy, y los handoffs dejan de decir «no tocado»
El hallazgo del 2026-08-04 quedo rancio dos dias despues: |
2 months ago |
|
|
4a1a0c3907 |
refactor(sema): los 3 resolvers de gesto se retiran — esta vez desde el eje, y el registro se hace verdad
El rediseno del 2026-08-06 (gestos por REPETICION, sound:'step' por emision)
los dejo sin llamador, y book-deviations D.7 escribio en pasado un borrado que
nunca ocurrio. Hoy ocurre, firmado y desde una sesion del eje que empezo por
su handoff — la condicion que la retirada revertida de
|
2 months ago |
|
|
1d442c59dc |
fix(media-player): `c` ya no descarta el idioma elegido, y el menu deja de robarle teclas al player
Cierra los cuatro que la revision adversarial dejo vivos y que dependian del
provider. La otra sesion commiteo su refactor (
|
2 months ago |
|
|
482cea3bee |
docs(media-player): las cuatro variantes de audio estan aprobadas, no dos
row e inline las dio por buenas el usuario y yo las seguia listando como pendientes de su mirada. El eje visual del modo audio queda CERRADO. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
35ef18ed02 |
docs(media-player): los cuatro arreglos que bloquea el refactor en vuelo
La revision adversarial dejo cuatro hallazgos ciertos que viven todos en media-player-provider.svelte.ts, que la otra sesion tiene modificado sin commitear. Tocarlo significaria llevarme su trabajo en mi commit, asi que quedan escritos con el arreglo concreto de cada uno. Incluye una observacion sobre SU arbol que no he tocado: con el refactor en vuelo el player deja de estampar el atributo dir crudo aunque la prop lo afirme. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
b6caaaa178 |
docs(media-player): el handoff recoge el selector de pista y lo que NO pude verificar
Anade las entregas 5 y 6 (CaptionFloat y la especificacion de la calidad) y, mas importante, una seccion nueva de limites. El panel de navegador embebido dejo de componer frames a media tarde (`document.visibilityState === 'hidden'`) y con el dejaron de llegar las teclas. Llegue a creer que habia un defecto —«Escape no cierra el float dentro del player»— y era FALSO: instrumente un listener y una pulsacion real registro cero eventos. Lo roto era mi via de entrada, no el componente. Queda escrito porque es la misma familia de trampa que las transiciones CSS congeladas, y porque la proxima sesion no debe salir a cazar ese fantasma. Lo que si quedo verificado con eventos despachados: keydown de Enter/Espacio abre el menu, y el click que el navegador sintetiza detras no toca los subtitulos — el preventDefault() de la costura aguanta. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
6f0f438f30 |
docs(media-player): el handoff recoge las cuatro entregas de la tarde
Waveform visible, velocidad bajable, API de eventos y transporte de lista. Con la leccion reutilizable: componer un primitivo desde soma deja su receta fuera, la carga el componente que lo compone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
bd7853b59e |
docs(sound-engine): mutear antes de reproducir, y parar al terminar
Medir el escenario mixto y los earcons exige reproducir de verdad, pero el Chrome que suena es el del usuario. Dejar una pista de 6 minutos a volumen 1 mientras se hacen otras cosas es una molestia real, y paso hoy. Casi todo lo que se mide —contextos, stamps, envolventes, geometria— no depende de que se oiga, asi que muted=true basta; el volumen solo hace falta para la escucha, que es del usuario. Y pause() al acabar cada medicion. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
6de53519d1 |
fix(audio-player): el track de la barra era un punto — el suelo estaba anulado
El usuario lo vio: en la variante `bar` el scrubber quedaba reducido a un pulgar entre 0:00 y 6:12, mientras el volumen conservaba sus 64px al lado. La causa era de una linea. media-player.css le da a todo scrubber un suelo (min-width: --space-8) y la regla de `bar` lo anulaba EXPLICITAMENTE con min-inline-size: 0 y mayor especificidad, asi que flex podia encogerlo hasta la nada. Un control que desaparece en silencio es peor que una fila que admite que no tiene sitio. Dos decisiones de diseno del usuario, aplicadas: - El scrubber nunca se dibuja mas estrecho que el volumen. El ancho del volumen sale a token (--_mp-volume-width) y el scrubber de `bar` se apoya en el como suelo, para que no puedan divergir cuando alguien retoque uno. - El artista se oculta en `bar`; `card` y `row` lo conservan, que tienen sitio. Medido tras el arreglo (variante verificada en la misma pasada): scrubber 64 a 560/720px contra volumen 64, y 229/529 a 900/1200px. Sin desborde en ninguno. Visto en Chrome: los dos sliders se leen del mismo tamano y el titulo gana el espacio del artista. Queda vivo y declarado: el mute-button se estruja a 20-25px bajo 800px. No se toca porque ponerle flex:none empuja el problema a otro control, y esa es una decision de diseno. check 0 errores propios · player 14/14 · eidos-lint invalid 0 · docs:check 0 · prettier limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
92c1ee91a9 |
docs(sound-engine): bar solo colapsa bajo 800px, y la mezcla la elige el disenador
El barrido de anchos reencuadra el defecto: bar esta sano de 900px en adelante (scrubber 147/347/1047) y solo colapsa a 720px, que es el ancho al que la demo encierra al player, no un ancho propio de una barra de app persistente. Corrige ademas la pregunta que yo habia planteado. El usuario zanjo que la mezcla es composicion y la elige el disenador, lo que encaja con la doctrina que audio-player.css ya declara (un variant decide que se muestra y como fluye, nunca comportamiento) y con la salida que ya existe: componer MediaPlayer media=audio a mano mantiene el flujo sin opinion, porque el CSS de variantes cuelga de data-variant y eso solo lo estampa AudioPlayer. Lo que no se disuelve en composicion es que el preset colapse EN SILENCIO. El arreglo pendiente no arbitra entre scrubber y volumen: min-inline-size en el scrubber para que nunca llegue a 0, y el ancho minimo documentado. Y registra dos veredictos del usuario: waveform F6 APROBADA (cierra el pendiente del 2026-08-01) y variante card APROBADA. Quedan row e inline. docs:check 0 sobre 564 documentos. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
5ff5202038 |
docs(sound-engine): el stamp de sema y la matriz del modo audio, medidos
Cierra los dos pendientes del hilo que no exigían los ojos del usuario, y sanea el documento, que afirmaba cosas ya cumplidas. STAMP DE SEMA — cerrado por las dos mitades. Dónde aterriza: con un MutationObserver sobre el subárbol del player, un clic en play estampa contact-activate y commit-toggle-play (commit · neutral) sobre el PROPIO play-button, no sobre el provider, y los limpia al cerrarse el hold. Que la regla del pack casa: A/B del pico de la envolvente dentro del mismo componente — transporte (play, mute) 0.0001, o sea silenciado; commit-complete 0.05, audible. Los osciladores sí se construyen, la ganancia nunca sube: es la resta de gain de D-AP2.7 funcionando. Queda anotada la trampa en la que caí, comparar ese pico con el 0.25 de un Button de otra página y creer ver una contradicción: el A/B tiene que ser dentro del mismo componente. F6, MATRIZ DEL MODO AUDIO — las 16 combinaciones (4 variantes × claro/oscuro × LTR/RTL) recorridas buscando partes colapsadas, botones estrujados y desbordes. Un solo defecto: en `bar` el time-slider colapsa a ancho 0 y el mute a 20px, en las CUATRO combinaciones, así que no es del eje ni del tema. Visto: el scrubber queda reducido a un punto ámbar entre 0:00 y 6:12 — una barra de app sin barra de progreso — mientras el volumen conserva su ancho completo. `card` y `row` limpias; lo que oculta `inline` es lo que su receta declara. HIGIENE — el documento decía «sin commitear» del rediseño y del waveform, y pedía un re-plan del player que ya está hecho. Verificado contra git: rediseño |
2 months ago |
|
|
f9e372e6fe |
feat(media-player): captions propias + el eje de dirección, canonizado
Dos hilos que comparten ficheros, así que van juntos. ## Captions — el player pinta las cues La parte estaba declarada en el morfo desde el principio y nunca implementada. El render nativo no servía: con la pista en mode='showing' el navegador dibuja su caja de cues en bottom:0 del <video> — la misma franja de 49px que la barra de controles — y contra el z-index:4 de la barra al 82% de opacidad llegaba el 18% del texto. ::cue no puede moverlo (la spec no admite propiedades de posición ahí). Ahora la pista se mantiene en 'hidden': la spec deja la pista activa —cues parseadas, activeCues mantenida, cuechange disparándose— y el navegador no pinta nada, así que el texto se monta en la parte Captions, por encima de la barra. Es el movimiento de Plyr, y por el mismo motivo. Tres trampas de la especificación, encaradas por escrito: - Pasar una pista a 'disabled' desactiva sus cues SIN disparar cuechange. Nadie avisa de que hay que borrar, así que se limpia a mano — si no, el último subtítulo se queda congelado sobre el vídeo. - Los eventos de cue no corren mientras está puesto el show-poster flag, así que el primer pintado se siembra leyendo activeCues. - cues/activeCues son null en 'disabled'. De paso cierra un defecto latente: captionsOn era un espejo de una sola dirección que nunca releía track.mode, así que si el SO activaba subtítulos salían cues mientras el botón decía que estaban apagados. Ahora se DERIVA de los modos reales, y una pista que el navegador encienda se adopta (degradada a 'hidden', para que no pinte encima de la nuestra). Decisiones declaradas: el marcado inline se conserva (se monta el DocumentFragment de getCueAsHTML como nodos, nunca por innerHTML — el sumidero de XSS que abre Plyr al serializarlo de vuelta a cadena); la metadata de posición (line/position/align/vertical/snapToLines/regiones) se DESCARTA. Esto no es una implementación de WebVTT y el README lo dice. ## Dirección — el player estaba partido por la mitad Con dir="rtl" por prop y sin dir ambiental, el cromo se disponía en LTR y sus dos Sliders en RTL: el wrapper pasaba la prop cruda y sólo la reenviaba a los hijos, sin resolver la cadena ni estampar. En la demo estaba enmascarado porque el stage estampa dir además de pasar la prop. Canonizado con el patrón de CONTINUE-direction.md §9.16: activeDir en el wrapper, resolvedDir en el provider, dir crudo + data-dir resuelto en los props. El censo del §9.15 no lo tenía — era un cuarto caso que su búsqueda no cubre (acepta dir y no lo escribe en ningún sitio). Y los glifos direccionales no se volteaban: en RTL el transporte se leía ▶▶ ▶ ◀◀, con las dos flechas exteriores apuntando HACIA DENTRO — el síntoma que carousel.css ya documentaba. Se voltean con scale:-1 1 (no rotate:180deg: sólo coinciden en glifos verticalmente simétricos, y rotar el PiP le llevaría el recuadro a la esquina de arriba), anclado a data-dir, el atributo propio del componente. Alcance elegido: seek, play, volumen y PiP; captions y fullscreen NO. El teclado pasa a resolver por getDirectionalKeys, así que las flechas concuerdan con el glifo y con el Slider embebido. ## Verificación check 0 errores propios · vitest del player 14/14, con los tres guards nuevos vistos en ROJO por mutación · eidos-lint invalid 0 (morfo-backed 25 → 29) · morfo:check sin fallos del player · component:audit PASS · smoke PASS · docs:check 0 · prettier limpio en lo propio. A ojo en Chrome real: claro y oscuro, LTR y RTL, y las dos regresiones (el espejo del SO y el subtítulo congelado). Nota: el README de soma se lleva una línea ajena — la fila `dir` de la tabla de props, del barrido de otra sesión, que describe justamente esta cadena. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
42d58c3940 |
docs(process): handoff del RTL — el Slider esta roto y es la prioridad de manana
Nace CONTINUE-player-rtl.md con lo que la MIRADA del usuario abrio el 2026-08-01 y quedo sin cerrar, mas el enlace cruzado desde el handoff del hilo de audio. Lo que lleva: 1. PRIORIDAD — el Slider esta roto en RTL, confirmado por el usuario con el componente SUELTO. No es del media-player: es el canonico del que cuelgan waveform, media-player, color-picker y los dos time-pickers. Se deja descartado por lectura que getValueFromPointer y rangeStyle estan bien en aislamiento, y se deja una HIPOTESIS comprobable en dos minutos: el Slider estampa `dir` como atributo HTML, lo que activa `direction: rtl` en CSS, y slider.css mezcla propiedades logicas con los estilos fisicos en linea que calcula el provider — doble volteo. El arreglo sera elegir UN dueno de la inversion. 2. La disposicion de los botones del player, que el usuario insiste en que es un problema APARTE. Las sondas de la sesion decian que mirroriza, pero esas sondas fallaron varias veces ese dia (dos midieron video creyendo audio): quedan marcadas como NO fiables. 3. Iconos de seek: decision tomada (voltearlos, con su razon), sin aplicar. 4. La medicion del escenario mixto quedo a medias y se explica por que: se instrumento DESPUES del arranque, asi que el "0 contextos" no prueba lo que parecia. Lo que si queda establecido es que la media no abre ningun AudioContext. 5. Captions y el cableado del waveform, con su diagnostico completo. Y las lecciones de metodo del dia, que son el motivo de que este documento exista: medir el contrato NO es verificar la experiencia (se afirmo tres veces lo contrario y las tres las caza el usuario); el panel embebido no sirve para nada visual (innerWidth 0 — llego a inventar tres "overflow" inexistentes); comprobar que el control se pulso de verdad antes de creerse la medida; la distancia RGB es ciega al croma; y la FORMA manda sobre el color. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
e096f65e39 |
docs(sound): MediaSession verificada por el usuario — el pendiente de tres sesiones, cerrado
El usuario abrio el media hub de Chrome con la demo de audio sonando: caratula + titulo + artista + transporte, origen localhost:5173. Cierra "prueba manual de teclas de medios / pantalla de bloqueo", pendiente desde que se escribio el rediseno porque no habia pagina que consumiera sound.media(). Se anotan tres cosas para no repetir el camino: - Las teclas de VOLUMEN no sirven (muestran el OSD de Windows; en Win11 Microsoft retiro el panel de medios que se pegaba a ese OSD). La superficie fiable es el boton de nota musical de la barra de Chrome. - La sospecha sobre la caratula era INFUNDADA: se temio que artwork sin sizes/type y sin CORS fuera descartado por el MediaImageManager. Carga perfectamente. Queda registrado para que nadie "arregle" un no-problema. - Anterior/siguiente apagados es CORRECTO: setActive() registra play, pause y seekto; un reproductor de una sola fuente no tiene lista. Y la leccion de metodo: se afirmo "el audio ya funcionaba" apoyandose en una medicion JS (metadata poblada) como si zanjara lo que el usuario VE. Una medicion del contrato no es una verificacion de la experiencia. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
df86a4e509 |
fix(slider,media-player): el anillo del commit no enmarca contenedores, y saltar al final no es completar
Cuatro defectos que solo aparecieron cuando el usuario MIRO la pagina.
1) EL BORDE DE LOS SLIDERS ("se viene arrastrando desde el principio").
No era del arquetipo ni del slider: es la firma GLOBAL de la familia
commit. BUILTIN_SIGNATURES.commit monta commit-settle, cuyo 30% pinta
box-shadow 0 0 0 3px. El commit-set del Slider apunta al PROVIDER, asi que
cada paso de flecha y cada release dibujaban un aro alrededor de la caja
entera (medido en vivo: 544x40). Un aro lee como feedback en un boton y
como borde parasito en un rail ancho y transparente. Exencion acotada en
slider.css — el patron que gradient-builder.css ya practicaba. Alcanza a
todo slider embebido (waveform, media-player, color-picker, time-picker)
porque todos llevan [data-slider]. `animation` es la palanca, no
`box-shadow`: las declaraciones de keyframe ganan a las normales, asi que
solo reiniciar el NOMBRE de la animacion detiene la pintura.
2) EL MISMO ANILLO EN EL PLAYER: commit-complete / commit-fail apuntan al
provider -> borde alrededor de los ~700px del reproductor. Misma exencion
en media-player.css. Los botones conservan su aro pequeno (20x36), que es
donde el feedback pertenece.
3) SALTAR AL FINAL ANUNCIABA UNA COMPLECION FALSA. Los botones de salto no
tienen evento propio: seek() delega a proposito (D-AP2.13, "el release del
scrubber ya ES el commit-set del Slider") — cierto para el scrubber y
VACIO para los botones, que no tienen Slider detras. Al saltar mas alla del
final el elemento dispara `ended` -> commit-complete en el provider ->
sonido (es audible por D-AP2.7) + anillo, mientras el boton de retroceso
quedaba mudo. Saltar al final no es completar: es abandonar la obra en el
final. seek()/scrubTo() marcan el salto que aterriza en el final y `ended`
no anuncia; un `play` posterior limpia la marca, asi que el final natural
se sigue anunciando. Los dos botones quedan simetricos y mudos, que es la
doctrina del transporte.
4) MEDIASESSION SIN OVERLAY DEL SO: el modo audio YA funcionaba (metadata
completa, medida en vivo); el roto era VIDEO, el modo por defecto de la
demo. <AudioPlayer> deriva metadata de title/artist/artwork; el
<MediaPlayer> compositivo no puede leer su hijo Title sin volverse
data-driven, asi que la declara el consumidor — y la demo no lo hacia.
El arreglo es de la demo; el art se comporta como D-SR.6/D-AP2.5 lo firmo.
Guard nuevo (no anunciar compleccion al saltar al final; si al final
natural) VISTO EN ROJO antes de darlo por bueno.
Nota de doctrina, tras leer el corpus a peticion del usuario: la exencion
acotada en la receta ES la puerta, no un olor. motion.md §4 fija que la
firma es generica por decision estructural ("un commit asienta igual en
todo el sistema"), eidos es el UNICO dueno del canal visual, los packs de
sema solo transportan sound/haptic, y el VisualChannel no consulta
activeChannels — no hay palanca declarativa para callar solo lo visual.
Queda anotado, menor: commit-settle escribe 3px a pelo donde sus hermanos
announce-pulse-* usan var(--focus-ring-width).
Verificado: check 75 = baseline, 0 propios; 11/11 media-player;
motion.test.ts verde (la firma global intacta); sonda en Chrome vivo —
slider real -> animation none, elemento no-slider con el mismo sello ->
sigue commit-settle, aro de foco intacto.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
0ab6cc822c |
feat(waveform): F1-F3 — sound.peaks, morfo delegado y soma componiendo Slider
Waveform (gate D-WF.1-8 firmado, PLAN-waveform.md): la visualizacion de audio cuyo contrato es el DATO y su pintura — la interaccion ES el Slider canonico. - F1: uix.sound.peaks(buffer, buckets) en el art — max absoluto por bucket sobre todos los canales, normalizado por el bucket mas alto (documentado); puro, sin contexto. El flujo natural decode -> peaks en la misma puerta. - F2: morfo con DOS partes (provider con data-shape/data-readonly; wave svg presentacional aria-hidden), SIN campo events y expression 'delegated' — re-declarar drag/commit seria la duplicacion que D-AP2.13 retiro del player. El part Cursor del borrador, PODADO como especulativo (el thumb del Slider embebido ES el playhead). - F3: WaveformProvider con la geometria testeable (buildWavePath: bars = trazos verticales con piso perceptivo 0.5 — el silencio pinta linea fina, no hueco — y ancho de barra via stroke-width de la recipe; wave = area() de $libs/plots espejada) + progressFraction clampada; wrapper que compone el Slider COMPLETO (SecondaryRange G-2 + Range + Thumb, con valueText G-1 y readonly/disabled/step reenviados); Wave con el clip de progreso (dos copias del mismo path, hooks eidos-only -played/-remaining). Verificacion: art+port 42/42 - morfo 4/4 - soma 4/4 - check 0 propios en los arboles del waveform - vocabulario limpio. Quedan F4 (recipe eidos + tokens, disenada en el CONTINUE), F5 (demo+dossier+audit) y F6 (mirada del usuario). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
2 months ago |
|
|
85b269f745 |
feat(sema): transporte del player silencioso por defecto — D-AP2.7 v2
Firmado 2026-07-31: el player es el unico componente cuyo output ES audio — un tick de UI en su transporte compite con la obra por definicion (D.7). El pack aplica el patron D.5 (resta exacta del gain de familia; las primitivas del intent intactas, threat/fulfill afloran) a todo lo que dispara un gesto: - commit-toggle-play / commit-toggle-mute / commit-set-rate en sus botones; - el contact-activate de los Buttons compuestos (regla de descendientes, H-10; tuning nuevo `contact.silent` -0.25 en sounds.ts) — IconButton compone Button sin morfo propio, asi que [data-button] casa (verificado); - el commit-set de los Sliders compuestos (scrubber y volumen). Audibles se quedan: commit-fail (el risk aflora sobre la resta) y commit-complete (informa de la obra; revisitar con escucha real). El handle-drag continuo conserva su resolver por MECANISMO (el payload por-emit posee las primitivas — el propio pack del slider lo declara); callarlo seria iniciativa de resolver/canal. La voz se reactiva en la capa de app (cascada 5b), sin prop nueva. Complementario con duckUiWhileContent: el pack calla AL PLAYER, el duck atenua al RESTO de la app. Haptica se queda. Selectores 100% del builder tipado (solo el combinador descendiente es literal). Verificado: morfo:vocabulary limpio - sema 192/192 - check 74 = baseline, 0 propios. Auditoria posterior: regla de descendientes confirmada viva (IconButton -> Button), sin regla rival de commit-set en el pack del slider, deltas componen por el mecanismo D.5. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
2 months ago |
|
|
c4d64ba98a |
feat(media-player): modo audio de referencia — AudioLayout, <AudioPlayer> con 4 variantes y buffer real (F4-F7 parcial)
El modo audio deja de ser "video colapsado" (las 3 reglas CSS de v1):
- F4 sema: el docblock del pack escribe la doctrina D-AP2.7 — el silencio
mientras suena la obra lo posee el SERVICIO (duckUiWhileContent, receta
documentada); la resta-de-gain queda disenada sin cablear a proposito.
- F5 buffer real (G-2 consumido): el TimeSlider soma pasa bufferedTime como
secondaryValue del Slider compuesto y monta Slider.SecondaryRange; se
retiran el div .mp-buffered, la var --media-buffered (y su writeProperty),
--_mp-buffered y --_mp-rail. Look intacto: el 42% pasa a
--slider-secondary-bg en scope.
- F5 wrappers eidos de las 6 partes: Artwork compone AspectRatio+Image del
catalogo via child; RateButton compone Button ghost mostrando {data-rate}x;
Artist/Identity/Transport/LiveIndicator passthrough.
- F5 AudioLayout: espejo de DefaultControls para audio — identidad
(artwork+title+artist+live) + transporte + fila de scrubber + secundarios.
- F5 raiz competidora <AudioPlayer> (eidos/components/audio-player/, el
precedente Toast/Toaster): una etiqueta monta todo, y la metadata del SO se
DERIVA de la identidad visible (title/artist/artworkSrc) salvo override —
lo que ves es lo que muestra el lock screen (D-AP2.5).
- F5 recipe de 4 variantes (card/row/bar/inline) scoped a [data-variant]
(componer a mano sigue sin opinar) + tokens publicos --audio-player-*
(gap, artwork-radius, artwork-size-row/bar), regenerados.
- F7 parcial: el badge live usa el text localizado del morfo via soma (fuera
el "LIVE" hardcodeado) · demo con el modo audio real (<AudioPlayer> +
selector de variante + snippet con paridad) · README eidos con la seccion
del modo audio · component:audit --only media-player PASS.
Verificacion: player 18/18 · recipe-css-contract + component-api-contract
37/37 · eidos-lint media-player 0 invalidos · check 74 = baseline, 0 propios
(demo incluida). Declarado: audio-player.css es recipe de composicion sobre
el morfo del player (el lint per-component no la cubre — gap del tooling);
los internos --_mp-* los comparte la recipe hermana del MISMO morfo.
Quedan F6 (verificacion VISUAL de las 4 variantes — exige ojos) y el resto
de F7 (medicion Chrome del escenario mixto + teclas de medios + stamp sema).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
2 months ago |
|
|
2f872ef864 |
feat(sound): $sound orquestador (buses+voces+media) y player v2 F1-F3 sobre el servicio
Rediseno clean-room del sonido (gate D-SR.1-12 firmado; la doctrina "la voz
es del art" de PLAN-sound-engine s16.1 queda revocada):
- Mezcla: master -> buses ui/content (gain/mute/duck refcount, gana la mas
fuerte; politica pre-contexto). prefs de UI jamas tocan la obra.
- Voces registradas: la calibracion es DATO del consumidor (sema registra
'sema'; el default conserva la calibracion historica - cero cambio audible,
A/B del usuario: "suena igual").
- Ciudadania de media: sound.media(el) PROVEE el transporte (la forma exacta
del puerto MediaProvider) + foco mixed/exclusive/duck PERSISTENTE (estado
derivado; el ducker mas reciente retiene la palabra) + duckUiWhileContent +
MediaSession de la fuente activa (play/pause/seekto + posicion, liberacion
y promocion) + visibilidad content-aware + attach() opt-in (irreversible,
CORS declarado). Re-registro idempotente por elemento.
- diagnostics.ts con catalogo tipado (contrato de arts) y guards post-dispose
(ningun camino abre un contexto huerfano). Auditoria AU-1..9 resuelta;
bundle medido: 16,2 KB min / 5,6 KB gz.
Player v2 (gate D-AP2.1-13 firmado) F1-F3:
- F1 gaps de framework: aria-valuetext en Slider (valueText por thumb) +
parte SecondaryRange con token --slider-secondary-bg (buffer/clip/capitulos)
+ formatDuration en $libs/days + fix del selector del pack (H-1: la regla
de play/pause construia sobre provider y el stamp aterriza en play-button).
- F2 morfo: 6 partes audio-only (artwork/artist/identity/transport/
rate-button/live-indicator), commit-set-rate, commit-set-time RETIRADO
(D-AP2.13: el scrubber delega en el commit-set del Slider compuesto),
sustain-loading cableado (H-9), teclado j/l, </> y 0-9 (con guard de
modificadores para no pisar atajos del navegador).
- F3 la migracion: el transporte default es uix.sound.media(el, {metadata})
(pineado con el motor real); nativeMediaProvider RETIRADO (media-provider.ts
queda como contrato puro del puerto, sin shim); G-5: los commits de
play/mute/rate cabalgan el evento de RESULTADO (play/pause saltando el
pause de ended, volumechange con transicion de muted, ratechange);
formatTime -> formatDuration; sub-providers + wrappers + exports de las 6
partes; prop metadata publica (captura al registrar, documentado).
Verificacion: 45 suites / 458 tests verdes en el alcance (sound, sema,
active-uix, active-app, morfo, slider, media-player, days) - check 74 =
baseline de la rama, 0 propios - docs:check 0 - invariante de UN AudioContext
intacta (e2e sin tocar). Planes de proceso y handoff al dia
(PLAN-sound-redesign, PLAN-audio-player-v2, CONTINUE-sound-engine).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
2 months ago |
|
|
b48264902f |
docs(process): el gate del reproductor, consolidado — se presenta una vez y no se mueve
La sesión perdió convergencia: tres presentaciones del gate lo movieron tres veces (D-AP.7 corregida dos veces, D-AP.12 → v2, D-AP.13 apareció al final), y de sus commits los últimos corrigieron a los anteriores. Este pase consolida TODO en el cuerpo del plan para que la sesión siguiente firme en frío, sin re-excavar. Consolidación pura: sin código y sin hallazgos nuevos. - **D-AP.7, criterio final: incongruencia, no frecuencia** (D.7: «si el contenido ES audio, un tick de UI compite con la obra»). Con frecuencia como criterio, play/pausa —2 a 10 veces por escucha— quedaría fuera y la línea sería arbitraria. Se cancela el gain de lo que suena MIENTRAS suena la obra; se conserva lo que informa del estado de la obra (`commit-complete`, `commit-fail`), con `commit-complete` anotado como el discutible de los dos. - **D-AP.13, nueva**: `commit-set-time` duplica el `commit-set` del `Slider` embebido (`onValueCommit` → `seek()` → `trigger`, dos líneas leídas; el del Slider, medido en navegador). El rationale de radio-cards aplica literal, y el propio provider ya practica la delegación en `setVolume`. Recomendación: retirar y delegar; el coste va a F2 como cambio de contrato declarado. - **§8.7 — la postura de participación, resuelta**: `expression: 'pack'` es correcta (los eventos de ciclo de vida no los posee ningún hijo, y el guard S11d la exige mientras exista el pack); el player es un híbrido legítimo con UNA duplicación (D-AP.13) y UN defecto de timing (G-5). La premisa de D-AP.1 queda intacta — la pregunta que la última pasada dejó como abismo tenía suelo en la doctrina. - El clip queda condicionado a D-AP.12(b) en §5, F3 y F5, coherente con la recomendación v2; F2 recoge D-AP.13 y la ejecución de H-9. - La cabecera fija la regla de cierre: la cuarta presentación **se firma o se rechaza, no se re-excava** — y el kickoff de sesión nueva lo dice explícito. - El handoff (`CONTINUE-sound-engine.md`) queda en «firmar, sólo firmar», con la trampa que costó la sesión escrita donde las demás: **una medición no es un diagnóstico** — leer el contrato de la pieza (README → CANON §8 → book-deviations → código) antes de acusarla. `docs:check` 0 errores. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
d2e841c65d |
fix(sound): la auditoría del art, cerrada — niveles por reproducción y opciones que no mienten
Los tres hallazgos que quedaban de `PLAN-sound-engine` §16, firmados y aplicados.
Dos con código, uno con una medición que cambia la doctrina.
**A-5 — `gainScale` es de la reproducción, no del documento.**
`play()` escribía `master × gainScale` en el nodo master, del que cuelgan TODOS
los earcons. Un parámetro transitorio corrompía un mando de política: el nivel
se filtraba entre reproducciones, una nota en vuelo saltaba de volumen, y los
dos consumidores que motivan el motor compartido —la reducción de sema y el
ducking de un reproductor— se pisaban sobre un único valor. Ahora el factor
multiplica el pico de la envolvente de ESA reproducción, y **también la
profundidad del AM**: dejarla absoluta habría hecho que un earcon atenuado
sonara relativamente más áspero, cambiando el timbre en vez del volumen, que es
lo contrario de lo que significa una reducción.
**RC-5 se reescribe, y es legítimo**: un contrato de regresión protege
comportamiento, no defectos. Lo que pineaba —«la ganancia se fija ANTES de que
la síntesis reviente»— sólo importaba porque el valor iba a un nodo compartido.
La cobertura no se pierde, se coloca donde vive cada responsabilidad:
`sound-port.test.ts` ya pineaba que el canal resuelve el NIVEL y lo entrega;
`sound.test.ts` pinea ahora que el master **no se mueve**; y el art estrena un
contexto falso que sí construye el grafo, para ver el pico de la envolvente.
**A-4 — las opciones del canal, imposibles de equivocar.**
`SoundChannelOptions` pasa a unión discriminada: `{ engine, preferences?,
logger? }` **o** la forma de construcción (`audioContextFactory` / `fetcher` /
`dom` / `masterGain` / `timers`). Nunca ambas — un motor llega ya construido, así
que sus opciones de construcción no significan nada a su lado, y hasta hoy se
aceptaban y se tiraban en silencio. La defensa primaria es el tipo (doctrina de
la casa), con un guard `@ts-expect-error` que hace fallar `check` el día que la
unión deje de rechazar la mezcla; el aviso por logger es la red para JS.
`engine.ts` pasa de un spread a tres ramas en orden de precedencia.
Y **`fetchFn` → `fetcher`**, para alinearse con `$perm`: un nombre por concepto.
`audioContextFactory` **se queda** — es el patrón `idFactory` que ya usan
`$logger` y `$bus`, y nombra el tipo exacto que fabrica (`context` a secas
colisiona con el contexto GL de `$scene`).
**A-3 — S-1 aceptado, medido: 2,2 KB.**
El art entero son 6.410 bytes minificados / 2.177 gzip. Y mi propia propuesta
para cerrarlo era falsa: quitar el fallback del canal no saca el art de ningún
bundle, porque `createActiveUix` y `defineEngineSound` lo importan
incondicionalmente. Cerrarlo de verdad exige carga diferida, que choca con que
`prime()` deba ser síncrono dentro del gesto, y volver `uix.sound` perezoso —
cambio de superficie pública por 2,2 KB. Se acepta: el art es servicio de
núcleo, como `motion` y `scene`. Queda corregida la doctrina: S-1 era el más
débil de los cuatro síntomas; los que justificaban la extracción eran S-2 y S-4.
**La voz, declarada como diseño.** La quinta a 1.5×, el ADSR recortado a
15 %/40 %, el contour de ±400 cents y el mapeo del AM son decisiones perceptivas
calibradas contra el vocabulario de sema, no maquinaria. Escrito en el README
del art y en la cabecera de `engine-sound.ts`, con la regla: el día que un
segundo consumidor quiera otra voz, ése es el momento de partir el art en dos
—gobierno de contexto / voz—, no antes.
Verificación: art + sema **17 suites / 204 tests** · `contracts.test.ts` 35/38
(los 3 rojos, ajenos) · `check` en la baseline exacta (73, 0 propios) ·
`docs:check` **0 errores** · los guards de A-5 **vistos fallar** al reintroducir
la escritura al master · navegador `/temas/sema`, 4 ciclos de editar→disparar:
**1 contexto, 16 osciladores, master en 1, cero errores de consola**.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
cf37aee002 |
docs(sound): el aviso de segundo contexto dice lo que hace, no lo que promete
A-2 de la auditoría (`PLAN-sound-engine` §16). El contador de contextos vivos sólo ve **los que crea el art**, así que un `new AudioContext()` en cualquier otro sitio le es invisible: detecta cableado doble de ENGINES, no el antipatrón del contexto crudo que su propio mensaje describe. Ese hueco es exactamente lo que dejó al estudio de sema abrir un segundo contexto en silencio hasta ayer (A-1) — y lo que hizo inútil el plan del handoff anterior, que era «abre la página y mira el warn». Cerrarlo de verdad exigiría enganchar el constructor global, es decir parchear la plataforma. El art no lo va a hacer. Así que se escribe donde prometía de más, en `consts.ts` y en el README, y se apoya en la salida real: los consumidores que necesitaban su propio contexto ahora tienen puertas —`decode` para las muestras, `context` para el grafo— que no les obligan a abrirlo. Sin cambios de comportamiento. Art 13/13 · `check` en la baseline exacta (73). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
08ee18a8e1 |
fix(sound): `decode()` — la puerta que faltaba, y el estudio deja de abrir su contexto
A-1 de la auditoría (`PLAN-sound-engine` §16): `analyze.ts` hacía `new Ctor()` propio para decodificar el `.wav` importado. Medido inyectando un WAV real por el `input[type=file]`: **1 → 2 contextos**. Transitorio —se cerraba al acabar el decode— pero es exactamente el antipatrón que el art existe para impedir, en la misma página que usé como prueba de la invariante. Y el aviso de «segundo contexto» no lo vio, porque sólo cuenta los contextos que crea el art (A-2). No era un despiste de esa página: **la superficie del art no ofrecía la operación**. `preload(urls)` decodifica y esconde los `AudioBuffer` en una caché privada, así que quien quiere las MUESTRAS —un waveform leyendo picos, un analizador midiendo un fichero— no tenía puerta y se abría la suya. - **`EngineSound.decode(data): Promise<AudioBuffer | null>`**. Segunda puerta, simétrica de `context`: una para quien quiere un GRAFO, otra para quien quiere las MUESTRAS. Las dos llevan al mismo contexto. - **No pasa por `getOrCreateContext()`**: decodificar funciona sobre un contexto suspendido, y `resume()` fuera de un gesto puede quedarse pendiente para siempre — un `decode()` que se espera desde la UI no puede colgar de eso. Se añade `ensureContext()`, crear sin resumir. - **No cachea** (los bytes crudos no tienen clave; la caché por URL es de `preload`) y **devuelve `null` en vez de lanzar**. - **Desviación declarada** de lo que §16.2 proponía: NO se añade el acceso a los buffers cacheados. Hoy no tiene consumidor —`Waveform` no existe— y añadir superficie sin consumidor es justo lo que §16.1 le reprocha al art. - `analyze.ts` toma el motor por puerto estructural; `AnalyzePanel` lo lee de `getActiveUix()`, que es el idioma de la casa, no prop-drilling. MEDIDO en Chrome: importar un WAV crea ahora **1 contexto** en vez de 2, y el earcon posterior reutiliza ése. Y sigue analizando bien: un tono de 440 Hz a 8 kHz, decodificado sobre el contexto compartido a 44,1 kHz, se mide como **441 Hz** — el resampleo no falsea la medida. Guards nuevos en `engine-sound.test.ts`: decodifica sobre el contexto compartido SIN llamar a `resume()`, un segundo `decode` no crea otro contexto, y devuelve `null` con bytes indecodificables o sin contexto. Verificación: art + sema 17 suites / 201 tests · `check` en la baseline exacta (73 errores, 0 propios). Quedan abiertos A-2 (el aviso es ciego a los contextos crudos), A-3 (S-1 no está cerrado), A-4 (opciones del canal tiradas en silencio) y A-5 (`gainScale` en el master compartido) — los tres últimos cambian comportamiento público y esperan decisión. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
b27f2345fe |
docs(process): el motor cierra sus fases y abre 5 hallazgos; el player, al gate
`PLAN-sound-engine` pasa a CERRADO (F0…F5) y `PLAN-audio-player` a DESBLOQUEADO. Pero cerrar las fases no es dejar el diseño incuestionado, así que el plan gana una auditoría propia que lo contradice donde toca. - **§15 — la invariante, cerrada y medida.** Incluye lo que la medición DESMINTIÓ: el handoff anterior predecía un aviso por consola en `/temas/sema` que no podía aparecer, porque el aviso cuenta contextos VIVOS y allí sólo había uno. Y **E-8**, el agujero de attach que E-1 dio por cableado sin estarlo. - **§16 — 5 hallazgos ABIERTOS sobre el art**, tres con decisión pendiente: `analyze.ts` sigue abriendo su contexto (medido 1 → 2 al importar un `.wav`); el aviso de segundo contexto es ciego a los contextos crudos; **S-1 NO está cerrado** y §7 afirmaba lo contrario —queda tachado en su sitio—; el canal tira en silencio `masterGain` y compañía cuando le inyectan el motor; y `gainScale`, que es de UNA reproducción, se escribe en el master compartido. - **§16.1 — la pregunta de fondo**: el corte separa módulos, **no conocimiento**. La quinta a 1.5×, el ADSR recortado a 15 %/40 %, el contour de ±400 cents y el mapeo de `roughness` son diseño sonoro calibrado contra el vocabulario de sema, viviendo dentro de lo que el plan llama «maquinaria». El precedente de `$motion` —datos en la capa, mecanismos en el art— no se replica tan de cerca como §3 afirma. Y la superficie no sirve a dos de los tres consumidores que la justificaban: `Waveform` y el analizador necesitan los `AudioBuffer`, y `preload(urls)` los esconde en una caché privada. - **`sema.md` corregido**: «sema no importa el art» era cierto para la INSTANCIA y falso para el grafo de módulos. - **`PLAN-audio-player` con las 5 correcciones de su cabecera incorporadas al cuerpo**, que era la condición para presentar nada: segmentos en la matriz §2 y en el contrato §7.5; **G-2 sube a bloqueante** (buffer, ventana de clip, capítulos); las dos invariantes de §7.6 (`prefs.sound` NUNCA toca el volumen del contenido; el silencio de UI debe alcanzar al `Slider` compuesto). **D-AP.11 escrita contra el art** —su premisa, «no hay motor», era falsa— y **D-AP.12** (segmentos `clip`) añadida; las dos existían como referencia sin fila en §4. **D-AP.7 reescrita** como ducking. - El handoff añade dos reglas que costaron caro: **un guard que no has visto fallar no vale**, y acoplarse al dev server vivo del usuario en vez de levantar otro. D-AP.1…D-AP.12 siguen SIN FIRMAR: el gate es lo primero de F0 y no se escribe una línea de código del player antes. `docs:check` deja 1 error ajeno (`blocks/banner/README.md:15`, «8 roles» por 9). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
6920684d09 |
refactor(sound): extraer el motor Web Audio de sema al art `$sound`
`sema/chans/sound.ts` tenía 407 líneas de las que ~255 (≈63%) eran maquinaria Web Audio —ciclo de vida del AudioContext, síntesis, samples, desbloqueo por gesto— mezclada con doctrina perceptiva. Deuda arrastrada, con tres síntomas medidos: el import era estático, así que toda app con sema metía el sintetizador en el bundle aunque el sonido estuviera apagado (que es el default); faltaba ciudadanía que `$scene` ya resuelve; y la costura de inyección (`audioContextFactory`) llevaba ahí sin usar desde el principio. Mismo movimiento que `$motion` hizo desde eidos: el art se lleva el RUNTIME, la capa conserva sus DATOS y su doctrina. - `$sound` / `EngineSound`: UN AudioContext por documento (los navegadores los limitan y el gesto de desbloqueo es por contexto), síntesis de earcon, samples con caché y fallback a síntesis, `autoSuspend` OPT-IN —suspender con la pestaña oculta es correcto para earcons y erróneo para contenido, así que es decisión de quien compone— y aviso cuando un segundo contexto va vivo. Puertos `SoundDom` / `SoundTimers` inyectados; no importa ningún otro art ni nada de `$uix/sema`, que es la prueba objetiva del corte. - `SoundChannel`: 407 → 136 líneas. Solo doctrina: el gate de `prepare`, la política de reducción y el reparto «el canal resuelve el NIVEL, el motor aplica la ganancia». Sema no gana ni un import: recibe el motor por puerto. - `uix.sound` en standalone y attach con `ownsSound` (idioma ya shipped: `ownsMotion` / `ownsScene`), fila `sound` en la tabla ejecutable `contracts.ts`, y `defineEngineSound()` para el camino de app. - Tests nuevos: `engine-sound.test.ts` (11), `sound-port.test.ts` (guard de deriva de tipos + la regla de propiedad) y `sound-e2e.test.ts`, que recorre `emit -> cascada -> canal -> art -> grafo real`: el camino que las 16 suites previas no cubrían porque paraban en canales falsos. Sin `diagnostics.ts` ni `errors.ts`, y es decisión: espejo de `$scene`, aquí todo fallo es degradación documentada, no error de programador. Verificación: 17 suites / 198 tests · `check` en la baseline exacta (73 errores, 0 propios) · `sound.test.ts` verde SIN tocar un solo assert, que era el criterio de que el movimiento fue value-preserving. Planes: `PLAN-sound-engine.md` (completo, con el registro de la revisión adversarial E-1..E-7), `PLAN-audio-player.md` (aparcado tras el análisis del reproductor, con sus correcciones en cabecera) y `CONTINUE-sound-engine.md` (handoff). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |