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 }
1716 Commits (a82e8ebb3e6d33c8cdb98cab81cc73390d322e7b)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
a82e8ebb3e |
fix(carousel): las flechas apuntaban hacia adentro en RTL
Reportado por el usuario mirando la demo. La POSICION de los triggers si se
espejaba —los insets del recipe son logicos— pero el GLIFO no: `chevronDir`
solo mira `orientation`, nunca la direccion. Resultado: prev acababa a la
derecha apuntando a la izquierda y next a la izquierda apuntando a la
derecha, las dos flechas al centro. Medido:
LTR RTL (antes)
prev izq, apunta izq ✓ der, apunta izq ✗
next der, apunta der ✓ izq, apunta der ✗
`SvgChevron` escribe `transform: rotate()` INLINE, asi que ninguna regla lo
re-apunta sin `!important`. Se voltea con la propiedad independiente
`rotate`, que COMPONE con ese transform en vez de reemplazarlo — el mismo
motivo por el que el centrado de los triggers usa `translate` y no
`transform`, cosa que el propio recipe ya documentaba dos reglas mas arriba.
Se lee `[data-dir='rtl']`, el atributo PROPIO del componente que soma estampa
desde `resolvedDir` — no el `dir` del DOM, asi que no es el `[dir='rtl']`
prohibido. Ademas garantiza que el glifo coincida con la matematica del
arrastre y del teclado, que resuelven de esa misma fuente; con `:dir()`
podrian discrepar si DOM y prefs divergen. `eidos-lint carousel`: invalid 0,
y la regla sale clasificada morfo-backed.
Solo el eje inline: en vertical los chevrons son up/down y no giran.
Es lo que hace shadcn (`rtl:rotate-180`); nuxt/ui tuvo este bug exacto
(nuxt/ui#1354), donde ademas fallaban las posiciones y el signo de la
navegacion — aqui ambos ya estaban bien.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
07515eedf5 |
fix(motion,demos): dos desviaciones propias, encontradas auditando la sesion
1. La regla `:dir()` de `3c3d8e1ac` nacia SIN ACOTAR, o sea matcheaba cada elemento del documento para declarar una custom property que solo lee el nodo animado. Acotada a `[data-animation-style]:dir(...)`: `:dir()` se resuelve contra ese nodo, asi que el caso del subarbol anidado sigue cubierto y el coste deja de ser global. Medido igual que antes — origin 65.05px en RTL / 0px en LTR, con el borde de anclaje clavado en 0. 2. `releasePosition()` en la demo del float-panel (`1857c7854`) soltaba la posicion SIEMPRE, tambien con `useAnchor` apagado: cambiar `side` en modo libre re-centraba un panel colocado a mano. Condicionado al ancla, que es lo unico con lo que la posicion fijada es excluyente. Medido: con ancla off el panel se queda en (24,24); con ancla on sigue re-sembrando (start x=234 · end x=61). Y el hueco de metodo que las destapo: habia tocado `render-css.ts` —el generador de CSS de TODO eidos— corriendo solo los tests del ambito. La pasada COMPLETA da 8 fallos en 4 ficheros, y verifique que ninguno es mio cruzando lo que señalan contra `git diff --name-only 40db0b981..HEAD`: audio-player (sonido), aura + menubar (eje agentico), orca (hook timeout), soma-attr-audit (pasa en solitario: flaky bajo carga). Queda anotado en 9.7 un cabo suelto AJENO: 6.7 retiro la entrada de `data-ready` de `component-visual-attrs.test.ts` al migrar tabs, pero `contracts.test.ts` sigue marcando `data-ready` en radio-group y tabs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
8ec1095545 |
docs(direction): las tres piezas de graficas, y el tercer punto ciego de RTL-1
Seccion 9.6: cierre de metrics / bar-segment / chart-legend, el defecto del preset `grow-x` con sus medidas, y el renombrado de `placement`. Lo que merece quedar escrito: los tres se dieron por sanos en 7.2 mirando el CSS, y el CSS ERA correcto en los tres. Lo que fallaba era la ANIMACION, que no vive en el recipe sino en un preset compartido. Revisar una grafica en RTL no es solo leer su recipe — hay que disparar su entrada y mirar de donde crece. Y el tercer punto ciego de RTL-1, tras el transform inline del 6.7: un `transform-origin` en un preset de TypeScript. El guard lee CSS estatico; los presets y lo que pinta el JS siguen necesitando el ojo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
b0f995a3bc |
refactor(metrics)!: placement 'right' pasa a 'end', que es lo que siempre hizo
El CSS de `metrics` es integramente logico (`inset-inline-end`, `padding-inline-end`), asi que `placement="right"` ya ponia el chart a la IZQUIERDA en RTL. Medido: fromRight 0 en LTR, fromLeft 0 en RTL. El comportamiento era el bueno; mentia el nombre. Y era una ISLA: `layout: 'icon-start'`, `align: 'start'` y el resto del vocabulario del componente son logicos — `right` era el unico termino fisico de toda su superficie. No es el caso legitimo del `toast`, que es fisico y coherente CONSIGO MISMO; aqui la incoherencia estaba dentro del componente. BREAKING: `MetricsChartPlacement` pasa de `'below' | 'right' | 'full'` a `'below' | 'end' | 'full'`. Sin shim (doctrina del proyecto): actualizados el tipo, los dos selectores del recipe, el README y la demo. Fuera de ellos no habia consumidores. No tocado y señalado: el token `--metrics-chart-right-width` conserva el nombre fisico. Describe un ANCHO, no un lado, y es superficie publica de theming — su renombrado es otra decision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
3c3d8e1ac8 |
fix(motion): las barras horizontales crecian desde su punta en RTL
`grow-x` clavaba `transform-origin: 0% 50%` — el borde fisico izquierdo. En RTL la barra se dispone contra el borde DERECHO, asi que la entrada la hacia crecer desde su punta de vuelta a su base, despegandose de su ancla. Medido en los dos consumidores, con la direccion movida por el toggle del topbar: bar-segment borde izq. clavado en 296, el derecho avanza 108 -> 0 bar-list borde izq. clavado en 239, y el de ANCLAJE se aleja 65 -> 3 `transform-origin` no tiene forma logica en CSS, asi que el preset lee ahora `--motion-origin-inline-start`, que `render-css.ts` emite por `:dir()` — el mismo patron que `scale-fade` ya usa con `--floating-transform-origin`. La var HEREDA (al reves que el indice de stagger, que es `inherits: false`): es lo que permite que un `:dir()` de un ancestro alcance al nodo animado. Se declaran las DOS direcciones para que un subarbol que redeclare la suya tambien acierte — la misma razon por la que `[dir='rtl']` esta prohibido. Tras el arreglo el borde de anclaje queda CLAVADO en 0 y la punta avanza, en ambas direcciones. LTR intacto (`origin: 0px`, ancla en 0). Verificado a ojo congelando la animacion al 45%: las cinco barras del bar-list nacen pegadas a su ancla —derecha en RTL, izquierda en LTR— con el stagger escalonado. RTL-1 no podia verlo: es `transform-origin` en un preset de TypeScript, ni siquiera CSS estatico. `metrics-progress` NO usa `grow-x`; los afectados eran solo esos dos. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
3812cd196e |
docs(direction): cierre de 8.1 y 8.2, y la leccion de por que una demo no deja ver
Seccion 9 nueva: el eje horizontal del scroll-area (9.1-9.4), los tres
agujeros de observabilidad (9.5) y los sueltos (9.6). 8.1 y 8.2 marcadas
CERRADAS con puntero.
La leccion que se repitio tres veces y merece quedar escrita: una demo es
inobservable por motivos DISTINTOS, y hay que averiguar cual antes de
declarar el bloqueo —
1. el control existe y basta usarlo — "scroll-area no tiene eje horizontal
(maxScroll: 0)" era cierto solo en el valor por defecto; el chip
scrollbars="both" daba 258. El bloqueo no existia y no hubo que
construir nada.
2. falta el control — el drawer solo listaba los cuatro fisicos.
3. el control existe pero otro estado lo anula — float-panel tenia
useAnchor, pero una posicion ligada ganaba siempre.
Recorre los controles y mira QUE GANA.
Queda anotado, señalado y no tocado: 2.4 sigue viva (parts[2] de la demo del
slider indexa SecondaryRange en vez de Thumb, ahora linea 430) y float-panel
no expone prop dir — lee soma.prefs.getDir() directo, que es la cadena
documentada con el eslabon de prop ausente.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
1857c7854d |
fix(demos): drawer, slider y float-panel no dejaban ver su mitad logica en RTL
Los tres eran agujeros de OBSERVABILIDAD — el componente estaba bien, la demo
no permitia mirarlo. Y cada uno lo era por un motivo distinto:
drawer — faltaba el control: solo listaba top/right/bottom/left, y
start/end son justo los que ejercitan RTL. Medido: start da
left en LTR y right en RTL; left fisico NO voltea, correcto.
slider — faltaba el control: secondaryValue solo se veia en la demo del
waveform, que es horizontal, asi que vertical+RTL no era
observable. Cableado como un mando de porcentaje, con
SecondaryRange montado siempre (sin valor pinta a tamano cero).
Horizontal espeja; vertical sale IDENTICO en las dos
direcciones, que es lo correcto: el eje de bloque no gira.
float-panel — el control existia (useAnchor, con side y align) pero otro
estado lo anulaba: posA arranca en {x:24,y:24} y seedRect()
solo siembra con position vacia, asi que la semilla anclada
nunca corria y el align era inerte. releasePosition() la suelta
al tocar useAnchor / side / align, que son excluyentes con una
posicion fija por diseno.
Y aun asi seguia sin verse, por un CLAMP: el disparador nacia
pegado al borde del stage, asi que align=end pedia x=-173 y
volvia a 0. Centrada la fila de disparadores. Entonces si:
start->dLeft 0 / end->dRight 0 en LTR, espejado en RTL, y
center no gira.
Con esto el arreglo de
|
2 months ago |
|
|
ae756ee578 |
fix(scroll-area): el eje horizontal no se espejaba en RTL — el thumb salia del carril y aria-valuenow era negativo
En RTL el navegador DESCANSA scrollLeft en 0 con el contenido pegado a la derecha y lo lleva NEGATIVO hacia el final de lectura (modelo negativo del CSSOM). Nadie lo traducia, asi que resolvedDir era codigo muerto y toda la matematica del eje inline estaba rota: aria-valuenow -50 / -99 con aria-valuemin="0" -> 0 / 50 / 100 thumb left: -167px, FUERA del carril -> dentro y espejado data-at-left siempre puesto -> solo en el borde izq. data-at-right nunca -> en reposo click canalon al 10% desde la izquierda -> 0 -> -232, el 90% logico Dos traducciones privadas en el provider y cada consumidor toma la suya: lo que sigue el eje de LECTURA (offset del thumb, aria-valuenow, click en el canalon) y lo que se queda FISICO (data-at-left / data-at-right, cuyos nombres lo son y cuyo uso documentado —sombras de scroll— tambien). El ancla del thumb pasa a inset-inline-start porque el offset es progreso de lectura: es la decision opuesta a tabs / sliding-indicator, donde el JS media una coordenada fisica. La regla no cambia: mira de que lado esta la medida. El arrastre no necesito nada, y no por suerte: subir scrollLeft mueve el viewport a la derecha en los dos marcos. Verificado, no supuesto. Ambos extremos llevan ahora la misma holgura de 1px, porque solo uno cae en un cero exacto y cual de los dos depende de la direccion. De paso LTR gana el data-at-right del extremo, que tampoco se encendia. Medido en Chrome en las dos direcciones, con el toggle del topbar. El test nuevo sale ROJO con el provider anterior. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
40db0b9816 |
docs(direction): cierre del barrido de graficas, la regla de RTL en SVG, y la cola para manana
LAS 11 GRAFICAS DEL CATALOGO, REVISADAS. Ninguna de las siete que quedaban
necesitaba cambios, y por razones distintas que conviene dejar escritas:
bar-list ya lo resuelve con CSS logico; sparkline y bubble-chart heredan el
frame de <Chart> arreglado en 55070c688; y gauge, pie-chart, polar-area y
smith-chart son RADIALES — sus posiciones son angulos, no tienen eje de lectura
y es correcto que no espejen.
LA REGLA, ahora en la fila "RTL · SVG" del contrato de construccion y
desarrollada en eidos/components/chart/README.md §Direction. Son DOS decisiones
y no son la misma:
1) ¿espeja la composicion? solo si hay EJE DE LECTURA. Se invierte el rango de
pixeles de la escala, o se refleja la x. Los valores de Y no se espejan
nunca — un grafico es una secuencia leida como lee el lector, no un espejo.
2) `text-anchor` es LOGICO: si la composicion espeja se deja tal cual (voltear
ademas lo cancela); si no espeja se fuerza el fisico.
TRAMPAS DE MEDICION que me engañaron durante toda la sesion, en §7.3: el bbox de
un path espejado es identico al del original —mi "fingerprint" declaro sparkline
sin espejar cuando si lo hacia—; un test de "se sale del SVG" no ve una invasion
hacia DENTRO; el hueco del eje Y hay que medirlo contra el TICK y no contra la
linea; verificar en LTR no revela NADA de un defecto de RTL; y la pagina
"heatmap" tiene dos modos tras un control, asi que capturar el primer svg solo
enseñaba uno.
HANDOFF. La cabecera apunta a la §8 nueva, que es la cola priorizada: scroll-area
(dos señales, sin medir, y le falta una demo con eje horizontal), los agujeros de
OBSERVABILIDAD que impiden verificar drawer y float-panel, las verificaciones
pendientes, y la deuda ajena por gravedad —encabezada por los 91 ficheros de test
que falsifican Soma.require()—. Y una §8.5 con lo que NO hay que rehacer, para
que nadie repita el trabajo medido.
check 74 = linea base exacta.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
01b891b645 |
fix(chart,docs): el radar volcaba sus etiquetas sobre el poligono en RTL, y los bloques de codigo se volvian ilegibles
RADAR-CHART. No debe espejarse: sus etiquetas se colocan en ANGULOS alrededor del poligono, no sobre un eje de lectura. Pero el anchor de cada una se elige por el coseno —`start` en la mitad derecha, `end` en la izquierda— y `text-anchor` es logico, asi que bajo RTL todos se invertian y las etiquetas caminaban hacia dentro: medido, "Reliability" pasaba de [254,308] a [201,255] y "Economy" de [-6,47] a [46,99], las dos encima del grafico. Anchor fisico con el layout sin espejar, que es el caso del eje Y. De paso, el ternario del anchor se anota como union en vez de inferirse `string`. BLOQUES DE CODIGO DE LAS DEMOS. En RTL el algoritmo bidi reordenaba la puntuacion neutra y las muestras salian ilegibles: `<script lang='ts'>` se leia `<'script lang='ts>`, el `;` final de una linea saltaba al principio y `color="affirm"` salia `"color="affirm`. El codigo fuente es LTR sea cual sea la direccion de lectura — no es prosa —, asi que `pre/code/kbd/samp` lo declaran. Es la version ESTRECHA del pin que llevaba `<main data-uix-canvas>` y que quite en ec533845d: acotado al CODIGO, que es inequivocamente LTR, en vez de a todo el canvas, que tambien contenia las demos vivas y las congelaba a las 51 en LTR. Verificado MIRANDO capturas en RTL: el radar con las seis etiquetas fuera del poligono, y el snippet alineado a la izquierda con la puntuacion en su sitio. check 74 = linea base exacta. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
6751b4c3d8 |
fix(heatmap): la matriz no se espejaba en RTL y sus etiquetas de fila invadian las celdas
El usuario tenia razon: de la pagina "heatmap" yo solo habia tocado el modo CALENDARIO. El modo MATRIZ —otro componente, el que se elige con el control `mode (data)`— seguia intacto, y mi barrido no lo vio porque capturaba el primer SVG de la pagina en vez de la pagina entera. Medido en RTL antes: las etiquetas de fila iban de x=66 a x=113 con las celdas empezando en x=80, o sea metidas dentro; y las columnas no se movian (Mon en x=101 en las dos direcciones). Mismo tratamiento que el calendario y el eje X del chart: se refleja la coordenada x, con lo que celdas, etiquetas de columna y canalon de filas se espejan juntos, y los `text-anchor` LOGICOS se dejan como estan porque al espejar ya caen bien. Verificado MIRANDO la captura en RTL: Mon pasa de x=101 a x=314 —primera columna a la derecha—, las celdas de [80,362] a [12,294], y Morning/Midday/Evening quedan a la derecha, fuera de la rejilla. check 74 = linea base exacta. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
79ed5cdc2b |
fix(calendar-heatmap): el calendario no se espejaba en RTL — enero seguia a la izquierda
Lo habia dejado anotado como pendiente en |
2 months ago |
|
|
2636b40eaf |
fix(chart): funnel y calendar-heatmap tambien rompian en RTL, y la regla no es "voltea el anchor"
Revisadas las demas graficas del catalogo tras el eje Y. Dos rotas, y arreglarlas enseño donde estaba mi error de criterio. FUNNEL. En RTL las etiquetas y sus lineas guia se dibujaban ENCIMA del embudo. Su `labelPosition` ofrece `'inside' | 'right' | 'none'`: `'right'` es el UNICO valor lateral —no existe `'left'`—, asi que ese nombre no es una eleccion fisica del consumidor (a diferencia del `side` de drawer, que ofrece los dos) sino una suposicion LTR. Bajo RTL la columna de etiquetas va al lado de lectura contrario, asi que se espeja la composicion entera: embudo a la derecha, etiquetas a la izquierda. El cuerpo del embudo es simetrico respecto a `cx`, de modo que espejarlo es solo mover ese centro. CALENDAR-HEATMAP. Sus etiquetas de mes y de dia viven en canalones FIJOS y sus anchors `start`/`end` se volteaban, dejando "lun/mié/vie" pegadas a las celdas. Ahi si hay que forzar el anchor fisico, porque el canalon no se mueve. LA REGLA, que me costo dos intentos en el funnel: si la composicion SE ESPEJA, el anchor LOGICO ya es el correcto y voltearlo ademas deshace el espejo —lo hice y las etiquetas volvieron sobre el embudo—. Si la composicion NO se espeja (el canalon del eje Y, los de este calendario), hay que forzar el fisico. Queda escrito en rtl.svelte.ts, que centraliza las dos piezas: `physicalAnchor()` y un `createChartRtl()` que lee la direccion RESUELTA del elemento —las graficas sueltas no tienen el contexto de <Chart>, y `prefs.getDir()` devuelve undefined en este arbol mientras el elemento computa rtl bien—. Verificado MIRANDO capturas en oscuro, en RTL y en la pagina, antes y despues de cada cambio, no midiendo en LTR como hice las tres primeras veces. QUEDA, y no esta hecho: el calendario no se espeja —enero sigue a la izquierda en RTL, igual que le pasaba al eje X del chart antes de 55070c688—. Y de la misma clase sin revisar: heatmap.svelte:145 (`end` en el canalon izquierdo) y los anchors calculados de radar-chart. check 74 = linea base exacta. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
6e8d2d9223 |
fix(chart): en RTL las etiquetas del eje Y cruzaban el eje — text-anchor es logico y hay que voltearlo a mano
Cuarto aviso del usuario sobre lo mismo. Las tres veces anteriores verifique en LTR, donde no pasa nada; su captura estaba en RTL, que es justo donde ocurre. `text-anchor="end"` es LOGICO. Bajo `direction: rtl` el ancla `end` es el lado IZQUIERDO, asi que la etiqueta se fijaba por su borde izquierdo en x=-14 y crecia hacia la DERECHA, cruzando el eje y metiendose en el area del grafico. Medido: con el eje en x=46, la etiqueta llegaba a x=59.9. En LTR el mismo codigo daba 4.7 -> 32.6 y por eso mis comprobaciones lo daban por bueno. El canalon del eje Y esta a la izquierda fisica sea cual sea la direccion, asi que su ancla tambien tiene que ser fisica: `start` en RTL (que es el lado derecho) y `end` en LTR. El contexto expone `rtl` para que el eje pueda decidir. Verificado en RTL, no en LTR: la etiqueta pasa a 4.7 -> 32.6, identica a LTR, y crossesAxis de true a false. Zoom del canalon (deviceScaleFactor 4, oscuro, en la pagina) antes y despues. El eje X no esta afectado: usa text-anchor="middle", que no se voltea. MISMA CLASE, NO TOCADO: heatmap.svelte:145 usa `end` para las etiquetas de fila del margen izquierdo y funnel.svelte:111 usa `start` para las suyas a la derecha. Los dos tienen el mismo defecto latente en RTL. check 74 = linea base exacta. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
97f0278ed2 |
fix(chart): las etiquetas del eje Y quedaban a 3.4px del tick y se leian pegadas al eje
Reportado tres veces por el usuario. Las dos primeras veces mire numeros —gapToAxis: 7— y di por bueno que no habia solape; lo que faltaba era MIRAR un zoom del canalon, y ahi se ve al instante. La cuenta real no era contra el eje sino contra el TICK, que sale 4px hacia la etiqueta: con el texto en x=-8 quedaban 3.4px de aire entre glifo y tick a un font-size de 12px. A esa escala no es holgura, es contacto. El texto pasa a x=-14, que deja ~9.4px, y el canalon reservado crece en la misma medida (+18 en vez de +12) para que el label no se salga por el otro lado. Verificado con una captura ampliada (deviceScaleFactor 4) del canalon, en oscuro y en la pagina, antes y despues: gapToTick 3.4 -> 9.4 y el texto visiblemente despegado del eje. check 74 = linea base exacta. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
55070c688c |
fix(chart): el eje X no seguia la direccion de lectura — en RTL la serie empezaba por la izquierda igual que en LTR
Lo que el usuario pedia desde el principio, y que yo no arregle en los dos
commits anteriores porque me fui a otra cosa.
En RTL el chart NO se adaptaba en nada: medido, Jan en x=34 y Dec en x=843,
exactamente las mismas coordenadas que en LTR, con el eje Y a la izquierda y la
curva identica. Lo unico que cambiaba era `direction: rtl`, que afecta al texto y
a nada mas.
Las cuatro escalas X se construian con `range: [0, plot.width]`, siempre de
izquierda a derecha. Ahora el rango se invierte en RTL. Invertir el RANGO hace
todo el trabajo: linea, area, barras, scatter, crosshair y ancla del tooltip leen
su x de esa escala, asi que se espejan juntos y ninguno necesita saber de
direccion. scaleBand esta escrito para esto ("work in ascending pixel space, then
reflect if the range descends") y scalePoint delega en el.
Solo se voltea el ORDEN, nunca los valores de Y: un grafico no es un espejo, es
una secuencia leida como lee el lector. El eje Y y sus numeros se quedan como
estan, que es la linea que tambien trazan las librerias de referencia.
DE DONDE SALE LA DIRECCION. Primer intento con `eidos.prefs.getDir()`: no
funciono, y medirlo lo explico — devuelve undefined en este arbol mientras el
elemento computa `rtl` perfectamente, porque el ActiveEidos que montan las demos
no lleva las mismas prefs que escribe la shell. Se lee la direccion RESUELTA del
frame con getComputedStyle, que ademas es el dato correcto (un chart puede vivir
en un subarbol con su propio dir) y es como lo hace $ethereal/compute.ts.
`direction` es estilo computado, no geometria, asi que no fuerza layout. Un
cambio de direccion no dispara resize ni nada, asi que se observa el atributo
`dir` donde la shell lo escribe.
Verificado MIRANDO la captura, en oscuro y en la pagina, no un svg aislado en
claro como hice antes: en RTL el eje va Dec Nov Oct ... Feb Jan de izquierda a
derecha, con Enero a la DERECHA, y la curva espejada con el. Medido: Jan pasa de
x=34 a x=844 y Dec de 843 a 33. LTR intacto.
QUEDA: el eje Y sigue dibujandose a la izquierda en RTL; lo canonico seria
llevarlo al lado derecho. Es el siguiente paso, no esta hecho.
check 74 = linea base exacta.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
ea88414f3e |
fix(chart): el margen izquierdo era fijo, asi que toda etiqueta mas ancha que "100k" se salia del SVG
El usuario reporto que los valores solapan al eje, y en el commit anterior arregle
otra cosa —el formato del tooltip— sin tocar esto. Tenia razon: seguia igual.
const margins = { ..., left: margin?.left ?? 44 }
44px fijos, sin relacion con lo que midan las etiquetas del eje Y. Con "100k"
(28px) cabe por 7px y por eso la demo se salvaba; con cualquier cosa mas ancha se
sale por el borde IZQUIERDO del SVG. Medido con el formateador de la demo puesto
en toLocaleString('es-ES'): de seis etiquetas, CINCO desbordaban —"100.000"
empezaba en x=-11— y se veian cortadas.
Solo el eje conoce su `format`, asi que es el eje quien declara cuanto necesita:
nuevo `reserveYGutter(px)` en el contexto, con el mismo patron de disposer que
enableTooltip/enableLegend. El frame toma Math.max(44, gutter) y un `margin.left`
explicito sigue ganando sobre ambos.
El ancho se ESTIMA por numero de caracteres (~7px) en vez de medirse con getBBox:
mantiene esto fuera de la ruta de lecturas de layout, solo puede HACER CRECER el
margen porque el frame lo suela en 44, y el texto de las etiquetas depende del
dominio Y y nunca del margen, asi que no puede realimentarse.
Verificado en Chrome con el formateador ancho de verdad —no reescribiendo el DOM
despues, que fue mi primer intento y no probaba nada porque el componente nunca
veia las etiquetas largas—: clipped 5 -> 0, "100.000" entero dentro del SVG y el
eje corrido para dejarle sitio. Y con el formato corto restaurado, axisX sigue
en 44: cero cambio en el caso actual.
check 74 = linea base exacta.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
8086f21d6e |
fix(chart): el formato del eje Y no llegaba al tooltip, asi que ambos etiquetaban distinto el mismo punto
Reportado por el usuario con una captura: el eje decia "55k" y el tooltip "55"
para el mismo punto de Marzo.
El chart NO calcula mal. Los datos de la demo son decenas (42, 48, 55...) que
representan miles, el trazado es correcto y el punto cae donde debe. Lo que
fallaba es que el formateador del valor Y llegaba SOLO al eje.
EN EL FRAMEWORK, y afecta a los cinco presets. line-chart, area-chart,
bar-chart, bubble-chart y scatter-chart aceptan `formatY` y lo pasan a
<YAxis format={formatY}> y a nadie mas. El tooltip lo activan con la prop
booleana `tooltip` del Chart, que deja tooltipConfig en null, asi que el valor
cae al formatNumber por defecto. Resultado: `<LineChart formatY={money} />` da
eje "51k" y tooltip "51", y el consumidor NO puede arreglarlo porque el preset
no expone el formato del tooltip. Ahora cada preset declara
`{#if tooltip}<Tooltip format={formatY} />{/if}`: los dos rotulan el MISMO valor
y deben decir lo mismo.
EN LA DEMO. El chart compuesto —el de la captura— pasaba `format={money}` al
YAxis y dejaba `<Chart.Tooltip />` sin formato. Corregido, y tambien el snippet
de ejemplo que la pagina muestra, para que no ensene el patron incompleto.
Medido en Chrome disparando el pointermove sobre los tres SVG a la vez. Antes:
compuesto "May revenue 67 expenses 44", LineChart "Apr revenue 51", AreaChart
"Apr revenue 51" — todos contra un eje 0k..100k. Despues: "67k / 44k",
"51k / 35k" y "51k", coherentes con su eje.
Verificado: check 74 = linea base exacta, 54 warnings tambien. No hay tests de
chart en el repo. Prettier: 19 avisos en esos .svelte ANTES de tocarlos, 15
despues — ninguno nuevo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
714a1ddb59 |
fix(carousel): en RTL el rail se movia AL CONTRARIO del dedo durante el arrastre
El usuario vio a mano lo que mi simulacion de puntero no consiguio disparar en cinco intentos. El swipe estaba mal, y no donde yo habia mirado. La decision de finishDrag SI voltea (isRtlHorizontal ? -offset : offset) y por eso la daba por buena. El defecto estaba en el PINTADO durante el arrastre: translate = base + dragOffset // base = indice, dragOffset = dedo itemGroupTransform = translate * flip // el flip caia sobre LOS DOS El recorrido por indice es LOGICO —N->N+1 mueve el rail a la izquierda en LTR y a la derecha en RTL— y debe voltear. `dragOffset` es el desplazamiento FISICO del puntero y no debe. Al voltear ambos, en RTL el rail se movia en sentido contrario al dedo: arrastras a la derecha y el contenido huye a la izquierda. Separados: solo baseTranslate lleva el signo. A/B con git stash, midiendo el transform A MITAD del arrastre —que no exige superar el umbral de gesto, y por eso ahora si es medible—: sin el arreglo, con el dedo a +100px el rail da Δ-100 (huye); con el arreglo, Δ+100 (lo sigue). LTR intacto en los dos casos. NOTA DE METODO, anotada en el handoff: para un gesto con umbral de distancia y velocidad, no intentes completar el swipe con page.mouse; mide el estado INTERMEDIO con el puntero aun abajo. Es lo que me faltaba en §6.8. DESCARTADOS CON MEDIDA, no se tocan: scroll-area FUNCIONA. Confirmado por el usuario y medido: con el toggle en auto, ES->ltr y AR->rtl, y el elemento pasa a direction:rtl. Su resolvedDir sigue siendo codigo muerto y el uso de scrollLeft sin verificar en un eje horizontal real, pero el componente responde a la direccion. chart NO NECESITA ARREGLO y las referencias lo respaldan. Medido: chartDir, legendDir y el texto de los ejes pasan de ltr a rtl; el eje numerico no se voltea. Es el consenso: Chart.js implemento RTL SOLO para leyendas y tooltips y mantiene abierto el issue de ejes/etiquetas (#11937, PR #6460), y el patron documentado es eje numerico LTR con textos RTL. amCharts es de los pocos con RTL integral. Y el sintoma «la demo no reacciona al idioma», reportado tres veces (virtual-list, carousel, scroll-area): medido en las tres, con el toggle en AUTO las tres reaccionan. Si el toggle esta en ltr/rtl explicito el idioma no manda, porque el intent gana sobre la derivacion. No es defecto, pero despista de forma sistematica: parece un interruptor de dos posiciones y es un ciclo de tres donde solo uno cede el mando al idioma, y el estado solo se lee en el title. Cerrarlo seria trabajo de la shell, no del eje. Verificado: check 74 = linea base exacta. 4/4 en carousel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
f420c74920 |
fix(rating-group,splitter): dos wrappers se quedaron fuera de 013ceac57, y el arrastre del splitter no volteaba
Verificados los tres componentes que "tenian codigo de direccion" pero nadie habia comprobado que acertara. Dos estaban rotos. RATING-GROUP, roto por DOS motivos encadenados. 1) calcFromPointer medía (clientX - rect.left) / rect.width, una fraccion desde el borde FISICO izquierdo. En RTL los items van de derecha a izquierda, asi que la mitad que EMPIEZA una estrella es la derecha: pinchar la primera mitad visual daba la estrella entera. Volteado con el mismo `1 - pos` que ya usa el slider. 2) Y aun asi seguia sin funcionar, porque su WRAPPER se quedo fuera de la normalizacion de 013ceac57: tenia `dir = 'ltr'` como default duro —que ademas incumple "el atributo nunca tiene defecto"— y readableActive(() => dir) en vez de activeDir(() => dir, soma). Nunca consultaba prefs, asi que resolvedDir valia 'ltr' pasara lo que pasara. NATURAL-TIME-PICKER tenia el mismo agujero (dir ?? 'ltr'). Son los DOS unicos que quedaron fuera; los otros 36 si usan activeDir. Medido tras arreglar ambos: en RTL la mitad derecha da 2.5 y la izquierda la entera — espejo correcto. SPLITTER: el arrastre estaba roto, el teclado no. resizePanels toma un delta LOGICO —positivo agranda el panel de ANTES del handle— y recibia el delta fisico del puntero sin voltear. En RTL arrastrar a la derecha agrandaba el panel que debia encoger, y el arrastre CONTRADECIA a las flechas, que si pasan por getDirectionalKeys. Tras el arreglo: LTR arrastre +118 / ArrowRight +6; RTL arrastre -118 / ArrowRight -6 — espejo exacto y los dos de acuerdo. NOTA DE METODO, anotada en el handoff: al ver `resolvedDir = opts.dir.current ?? 'ltr'` en unos 30 providers di por hecho que a todos les faltaba el eslabon de prefs. Es FALSO — la cadena vive en el WRAPPER (activeDir), y el provider recibe la prop ya resuelta. El slider, que tiene ese mismo resolvedDir, voltea perfectamente. Antes de "arreglar" 30 componentes, mirar el wrapper. SIN VERIFICAR: scroll-area. Su resolvedDir aparece UNA sola vez —la declaracion—, o sea es codigo muerto, y usa scrollLeft en cuatro sitios sin tratar que en RTL los navegadores lo devuelven NEGATIVO (isAtLeft seria siempre cierto, el ratio del thumb saldria negativo). No esta medido porque el scroll-area de la demo no tiene eje horizontal. Verificado: check 74 = linea base exacta. 20/20 en splitter + rating-group + natural-time-picker. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
b550c8cd4b |
fix(virtual-grid,virtual-list): en RTL el contenido desaparecia — el ancla de celda era fisica sobre un sizer logico
Reportado por el usuario: en la demo del virtual-grid, voltear la direccion deja
la rejilla EN BLANCO. Reproducido y medido en Chrome.
LTR : 108 celdas, 70 visibles, primera en x=381 (borde del viewport)
RTL : 108 celdas, 0 visibles, primera en x=-5101 (fuera de la pantalla)
No era scrollLeft —esta en 0 en ambos casos—, era el anclaje de la celda:
position:absolute con top:0, left:0 y translate3d(columnStart). Los dos terminos
son fisicos y coherentes ENTRE SI, pero el sizer interno es
`inline-size: 6000px`, o sea LOGICO: en RTL desborda hacia la izquierda y ocupa
[-5101, 899], asi que su borde fisico izquierdo es donde el contenido TERMINA.
`left: 0` mandaba las 108 celdas justo ahi.
Arreglado con ancla logica (inset-inline-start) y el signo en el offset, de modo
que el recorrido se queda en translate3d: un virtualizador recoloca miles de
celdas y conviene que siga en el compositor en vez de animar layout. Medido tras
el arreglo: 70 visibles en RTL, primera en x=779 = borde derecho del viewport
menos el ancho de celda, que es el inicio logico.
virtual-list tenia el MISMO patron (left:0 + translateX) y por tanto el mismo
defecto en modo horizontal. Mismo arreglo. El modo vertical no se ve afectado:
inset-inline-start:0 con width:100% es exactamente lo que left:0 era. Verificado:
12 items visibles en RTL, primero en x=755.
EL OTRO SINTOMA REPORTADO NO ES UN DEFECTO. «El cambio de idioma no le afecta»:
con el toggle en `auto`, elegir arabe voltea la lista correctamente —medido,
htmlDir y el direction computado del componente pasan a rtl—. Si el toggle esta
en ltr/rtl EXPLICITO el idioma no manda, porque el intent gana sobre la
derivacion; es lo ratificado en
|
2 months ago |
|
|
caf3cfd884 |
fix(float-panel): la semilla anclada usa la matematica de $ethereal en vez de una copia sin direccion
Verificados uno a uno los cinco componentes de la §2.2 del handoff, y despues barrido el punto ciego del guard. Ningun cambio de comportamiento salvo el de float-panel. LOS CINCO DE §2.2 ESTAN SANOS. slider: click al 25% del ancho FISICO da 75 en RTL, arrastrar a la derecha sube en LTR y baja en RTL, y ArrowRight igual. number-field y css-field: el mismo arrastre fisico sube en LTR y baja en RTL. dropdown-menu: el panel raiz se alinea al inline-start y el submenu abre a la derecha en LTR y a la izquierda en RTL. carousel: el track voltea (Next mueve -622px en LTR y +622px en RTL). Ninguno necesito arreglo. EL SWIPE DEL CAROUSEL NO ESTA MEDIDO. Cinco intentos de simulacion de puntero y no consegui disparar el gesto de forma reproducible ni en LTR: la capa tiene umbral de distancia y de velocidad. El signo esta bien por LECTURA (isRtlHorizontal ? -offset : offset). Son diez segundos con un raton de verdad. UN MOTOR CUBRE NUEVE COMPONENTES. soma/layers/floating lo consumen combobox, context-menu, dropdown-menu, link-preview, menubar, popover, select, sidebar y tooltip. Verificado a traves de popover (align=end: derecha en LTR, izquierda en RTL) y menubar (align=start, al reves). Los nueve quedan cubiertos. FLOAT-PANEL NO USA ESE MOTOR, Y HACE BIEN: useFloating mantiene el panel PEGADO a su ancla, y un float-panel se arrastra y redimensiona libremente. Pero su SEMILLA anclada si era redundante: computeInitialPosition() re-derivaba side + align contra el rect a mano, y esa copia resolvia align:'start' a r.left SIEMPRE — una alineacion LOGICA clavada a un borde FISICO, que nunca voltea. $ethereal ya exporta computeCoordsFromPlacement(rects, placement, rtl): pura, sincrona, sin ciclo de vida, y es la misma que usa el motor compartido. Migrada la semilla a ella. Se comparte la semilla, NO useFloating. anchored-seed.test.ts fija la migracion, porque la demo NO ancla ningun panel y el defecto no era observable ahi: en LTR el resultado es IDENTICO al algoritmo anterior en las 12 combinaciones side x align (cero regresion); en RTL con side vertical start y end se espejan; con side horizontal el align no cambia, que es el eje de bloque; y center sigue centrado en ambas. La demo, medida antes y despues, no se mueve. NO TOCADO, hace falta criterio: Home/End en el eje X llevan el panel a bounds.left / bounds.right. Para un panel que se arrastra libremente es defendible que sean extremos fisicos y no principio/final de lectura. Descartados como falsos positivos tras mirar el uso real: container (prop de margenes), text-focus, text-scramble y path-trace (miden donde esta algo para un efecto), drag-drop (sueltas donde ves), cropper (paneas una imagen que no se voltea) y gradient-builder (arrastras la parada donde la ves). Y drawer, que no aparecia en el primer cruce, esta BIEN y es el ejemplo a imitar: traduce start/end a left/right una sola vez en resolveDirection() y desde ahi todo es fisico y coherente — aunque su demo solo expone valores fisicos, asi que su mitad logica no es observable. Verificado: check 74 = linea base exacta. 12/12 en float-panel + 4/4 del test nuevo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
76f18a5e87 |
refactor(tabs): el indicador pasa a MeasuredIndicator, y aparece un punto ciego del guard
Buscados los indicadores que comparten mecanismo con el de |
2 months ago |
|
|
dc5e956658 |
fix(direction): el indicador deslizante no se remedia al voltear la direccion
Verificar los tres arreglos que quedaron sin ver en
|
2 months ago |
|
|
ec533845d1 |
feat(direction): un guard para el eje logico-fisico, y el <main> que congelaba las 51 demos
Cierra la §2.1 del handoff. El guard nacio en rojo con 24 hallazgos y al arreglarlos aparecieron dos defectos mas grandes que el que buscaba. 1) EL GUARD RTL-1. Nada cruzaba un ancla LOGICA con un desplazamiento FISICO en la misma regla CSS: eidos-lint clasifica SELECTORES, no declaraciones, y la fila RTL del contrato de construccion decia "rule (LIVE)" — revision humana. Por eso el defecto del slider llego a produccion y lo cazo el ojo del usuario. Ahora `npm run rtl:check`, con la logica en src/uix/eidos/rtl-lint.ts (14 tests) para que sea testeable, calcando el par lint.ts / scripts/eidos-lint.ts. El test ancla el caso historico: slider.css ANTES de |
2 months ago |
|
|
86b6381388 |
docs(process): handoff del eje de direccion
Que esta cerrado (3 commits), que queda por orden de valor, y las trampas de metodo que costaron horas. Lo primero de la cola NO es codigo de componente: es el guard que no existe. Nada cruza `transform: translate*` con `inset-inline*` — hay 39 transforms en el CSS de eidos y ningun script los mira, porque eidos-lint solo clasifica selectores y el guard de la fila RTL del contrato es literalmente revision humana. Por eso el bug llego a produccion y lo cazo el ojo del usuario. Incluye la investigacion de las cinco librerias de referencia ya hecha (25 agentes, 5 hallazgos tumbados por refutacion) para que nadie la repita, y los dos datos que mas costaron: `unicode-bidi: isolate` NO arregla el interrogante desplazado, y `dir="ltr"` si — porque la hoja de agente del WHATWG le da isolate de propina. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
538f4932bc |
docs(process): el handoff decia que el Slider RTL estaba roto, y ya no lo esta
Cerrado en |
2 months ago |
|
|
5469e05df9 |
feat(direction): las demos dejan de clavar ltr, y la prosa inglesa se declara inglesa
Cierra el eje de direccion abierto en
|
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 |
|
|
013ceac574 |
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado
El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos
independientes, y el segundo era del ecosistema entero.
1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform
FISICO en las tres reglas verticales (inset-inline-start: 50% +
translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y
el relleno quedaban fuera de eje un ancho entero. Medido en Chrome:
raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba
porque ya usaba margin-inline-start — ese era el idioma correcto del
propio fichero y es el que aplico. Mismo defecto en accordion.css:
text-align: left en el trigger, que era el UNICO text-align fisico de
todo eidos; ahora quedan cero.
2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la
celda $state, asi que prefs.<dim>.get() — y con ella el
soma.prefs.getDir() que la guia manda usar — era una lectura sin
tracking. Los ~64 componentes que resuelven su direccion desde prefs la
congelaban al montarse. La proyeccion DOM se salvaba porque usa
onChange: misma dimension, dos caminos de lectura, solo uno reactivo.
Arreglado con un contador de cambios POR dimension, bombeado desde el
effectiveDiff que el motor ya calcula, de modo que cubre tambien el
cambio por DERIVACION (direction siguiendo a language) y no solo el
intent. Un contador por clave y no una celda global: con una sola celda,
tocar motion despertaria las 64 derivaciones de direccion. El test
negativo lo clava.
3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz
con un valor siempre concreto, asi que una app que ponga html dir=rtl sin
registrar la preferencia se encontraba 33 islas volteadas del reves.
Investigadas las cinco librerias de referencia: MUI y react-aria no
estampan nunca, Radix estampa siempre (sus mantenedores arrastran
discussions/1405 por esto), y Zag estampa prop("dir") — presente si
alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme:
el atributo NUNCA tiene defecto. La herencia la hace el navegador por
AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena
prop -> prefs -> 'ltr' se queda intacta.
4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los
wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico
—heredar del menu padre— enterrado en dos expresiones sueltas.
Normalizado a activeDir(getter, soma) en los 36, un solo tipo
Direction | undefined, y el escalon de los submenus declarado en la
llamada, donde se ve.
5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace
clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe
en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era
disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar
el control — la cadena documentada funcionando de punta a punta por
primera vez.
Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en
101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en
vivo, y el arabe voltea por derivacion.
QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del
accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es
un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL);
html lang no sigue al idioma; smoke y perm:check sin ejecutar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
3ceda5b593 |
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>
|
2 months ago |
|
|
4356020e37 |
feat(audio-player): la caratula de `card` sale a token, como sus dos hermanas
`row` y `bar` tenian tope publico (--audio-player-artwork-size-{row,bar}) y
`card` lo llevaba clavado en la receta como `calc(var(--space-8) * 8)`: un
tema podia retocar dos de las tres caratulas y la tercera no. Ahora son tres.
El valor se preserva EXACTO — 256px a densidad 1x / zoom 1x, verificado
resolviendo la custom prop en el navegador (el viewport del panel es 0, asi
que se mide la cascada y no el valor usado). Cero cambio visual.
Se conserva tambien la asimetria de FORMA, que no es un descuido y queda
escrita: `card` va sobre la escala de espacios (--space-8 ya trae densidad x
zoom compuestos) para que la caratula heroica respire con la fundacion,
mientras `row` y `bar` son rem planos a proposito — una fila de lista y una
barra de app no deben crecer con la densidad o dejan de casar con las filas
y barras de alrededor.
Verificado: recipe-css-contract 31 verdes (el guard de huerfanos exige que un
token declarado se consuma), eidos-lint media-player 0 invalidos,
component:audit --only media-player PASS, docs:check 0, check 75 = baseline.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
77f8a4e46f |
docs(waveform): la demo carga solo la pista real, y `sample` deja de fingir que es una prop
Fuera los earcons de sema del selector: eran material de INTERFAZ (blips de 80-220 ms; `pop` pintaba casi plano) en la demo de un componente de CONTENIDO, y estaban ahi solo porque eran el unico audio del repo. Con una pista real disponible, ensenaban lo que el componente no es. Y con una sola fuente, un radiogroup de una opcion es ruido: `sample` deja de ser un control y pasa a ser un dato en modo lectura. Eso hace visible en la pagina la frontera que el usuario pregunto explicitamente — debajo (`shape`, `value`, `step`, `secondaryValue`, `readonly`, `disabled`, `dir`, `size`) son PROPS del componente; `sample` y `buckets` NO lo son: son como la demo fabrica los `peaks`. El contrato del waveform es `peaks: number[]` y nada mas (D-WF.2): la app provee los datos, el componente nunca descarga ni decodifica. Verificado: check 75 = baseline, 0 propios; sonda en vivo — la onda sigue pintando los picos reales de la pista (path de 4031 chars), shape/bars vivo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
58acb778ee |
docs(waveform): F6 firmada — la iniciativa queda completa
El usuario dio por bueno el waveform contrastandolo con el reproductor de Suno AI, que valida dos decisiones del gate: barras (D-WF.5, la lectura SoundCloud) y los dos colores reproducido/pendiente como lo esencial — Suno ni siquiera pinta buffered, que en el nuestro es opcional y arranca apagado. Queda anotado lo que costo la mirada, porque ninguna sonda lo habia visto: el anillo del commit enmarcando el control entero ( |
2 months ago |
|
|
9050125076 |
fix(waveform): el buffered vuelve a ser banda recta, y la demo estrena material real
BUFFERED — segunda correccion por MIRADA, y la definitiva.
Las dos formas se construyeron y se miraron. Primero fue el SecondaryRange del
Slider con un velo neutro al 14%: rechazado ("el secundario no puede ser del
mismo color"), y la medicion le dio la razon — velo y onda eran alfas del MISMO
token (--color-content-primary al 14% vs 28%), o sea el mismo gris. Se paso
entonces a una tercera copia recortada del path, con la idea de que "el buffered
ES la onda un escalon mas alla". TAMBIEN rechazado, y por un motivo mas fuerte:
"se hace inapreciable y confunde visualmente".
La leccion, que queda escrita en los dos README: al compartir silueta con la
senal, el ojo lee la banda COMO la senal — es ilegible POR CONSTRUCCION y ningun
color la salva. La forma recta es legible precisamente porque NO es la onda. Asi
que el buffered vuelve al Slider.SecondaryRange (G-2), la parte canonica que ya
existia para esto, en vez de que el waveform pinte lo suyo.
El COLOR si sobrevive de la iteracion anterior: acento lavado
(color-mix del primary-solid con content-primary), que se separa del gris en
TONO y no solo en luminosidad. Tokens de paleta, cero literales. Los tres
colores de la onda son publicos y retunables por el disenador.
Nota de metodo: la distancia RGB avalo un gris que el ojo rechazo al instante
(esa metrica esta dominada por la luminosidad y es ciega al croma), y ademas un
bug propio en la alfa del color-mix dio numeros inflados que se corrigieron. Dos
recordatorios de que la medicion informa pero no decide.
DEMO — material real. El control se llamaba `signal`, que es una de las 8
FAMILIAS del canon, en un componente que declara CERO eventos
(expression: 'delegated'): ensenaba lo contrario de la verdad. Renombrado a
`sample`. Y el material eran los earcons de sema (pitidos de 80-220 ms, `pop`
salia casi plano) porque eran el unico audio del repo; ahora arranca en una
pista real de 2:45 (static/music/t2.wav, aportada por el usuario).
Y un defecto que 30 MB destaparon: el efecto de carga dependia de `source` Y de
`buckets`, asi que mover el slider de buckets volvia a DESCARGAR el fichero
entero. Separado en dos: decode una vez por fuente (caro), rebucketing puro y
barato. Medido: t2 (5 MB, 86 ms) frente al master de 48 kHz/16 bit (30 MB,
186 ms); sus envolventes NO son identicas — delta maximo 0.298 por cubo, medio
0.048, porque los 8 bits suben el suelo de ruido y aplanan los pasajes suaves.
Se elige t2 igualmente: 6x mas ligero por una envolvente marginalmente mas fiel.
Verificado: check 75 = baseline, 0 propios; 35 tests; eidos-lint 0 invalidos;
recipe-css-contract verde; component:audit --only waveform PASS; docs:check 0;
sonda en vivo — 2 paths en la onda, banda recta al 66% en rgb(130,95,161).
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 |
|
|
4e4f1b612d |
feat(waveform): F4 eidos + F5 demo — y el buffered pasa a ser la onda
F4 (eidos): wrapper raiz que carga slider.css + waveform.css y resuelve
data-size por ActiveEidos; Wave passthrough; recipe con bars = stroke en
UNIDADES DE VIEWBOX (el svg estira con preserveAspectRatio="none", asi el
ciclo barra/hueco sobrevive) y wave = fill sin stroke; espejo RTL en CSS;
el Slider embebido en scope con track/range transparentes y el thumb
convertido en playhead de 2px a alto completo. Tokens waveform:{height-*,
bar-width, color, color-buffered, color-played, playhead-width} —
playhead-width y color-buffered van ANADIDOS sobre la lista firmada en el
gate, ambos declarados en el README.
F5 (demo + dossier): /uix/components/waveform con picos REALES (fetch de
/sounds/*.wav -> uix.sound.decode -> uix.sound.peaks), cada prop como
control vivo, READMEs de soma y eidos. Fix arrastrado de F3: `dir` no se
reenviaba al Slider embebido.
BUFFERED — cambio de forma (decision del usuario tras ver el render).
El primer intento re-tintaba el SecondaryRange del Slider; el usuario lo
rechazo ("el secundario no puede ser del mismo color") y la auditoria
encontro por que ningun color lo arreglaba del todo: ese part es una BARRA
de 6px con la geometria del track, y sobre una pintura de barras al ~58%
de ciclo el 42% de su area cae sobre fondo desnudo, ademas de tenir el
tramo ya reproducido. Ahora el buffered es una TERCERA COPIA del mismo
path, recortada a su fraccion — es la onda un escalon mas alla, la lectura
de SoundCloud/wavesurfer. El waveform ya no monta Slider.SecondaryRange.
Escalera: color 28% -> color-buffered 55% -> color-played acento.
Guard nuevo (bufferedFraction: ausente sin valor, clampado, dominio
degenerado) VISTO EN ROJO desconectando el cableado antes de darlo por
bueno.
Verificado: check 75 = baseline, 0 propios; 5/5 soma + 4/4 morfo;
component:audit --only waveform PASS; eidos-lint 0 invalidos;
recipe-css-contract y motion.test verdes; docs:check 0; prettier limpio.
Queda F6 (mirada del usuario): el panel del navegador no composita, asi
que la verificacion fue por sonda del DOM, sin captura.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
cd9b06fa2f |
docs(blocks): auditoria del tier congelada — 29 confirmados, 50 sin verificar
Auditoria de los 15 blocks con 50 agentes: dos dimensiones en paralelo —doctrina
(contrato B + regla de coordinacion, por lectura) y percepcion (sema en vivo en
navegador, interaccion por interaccion)— y despues un verificador ESCEPTICO por
hallazgo, con el encargo de refutarlo y la lista de falsos positivos conocidos
del repo.
90 en bruto → 40 verificados → 29 CONFIRMADOS + 11 refutados (28%)
Ese 28% es la razon de existir de la fase adversarial: sin ella habrian entrado
once acusaciones falsas.
⚠️ 50 hallazgos quedaron SIN VERIFICAR, 18 de ellos marcados ALTA. El tope de 40
lo puso mi script, no el trabajo. No son menos graves: estan sin comprobar.
`AUDIT-blocks-2026-08-01.md` es la UNICA copia — el journal del workflow era de
sesion y ya no existe. Incluye los refutados con su motivo para que nadie los
vuelva a reportar.
Lo gordo confirmado:
- `contact` importa `$libs/forms`, fuera de la lista de la frontera dura 1.
- El nombre accesible del submit de `contact` esta congelado en «Enviar» en los
cinco estados: `Form.Submit` impone su `aria-label`, asi que las palabras de
estado que el block existe para poseer NO llegan al lector de pantalla. El
block cumple su promesa solo a la vista.
- `site-header` congela la pagina: con el cajon abierto, cruzar el `breakpoint`
esconde cajon/overlay/trigger con `display:none` pero deja el Drawer abierto y
el scroll del body bloqueado. Reproducido con swipe tactil real rotando un
telefono; la unica salida es Escape, que en tactil no existe.
- `hero` en `layout="background"`: CTA secundaria a 1.78:1, bajo AA, y el token
que la demo escribe a mano para arreglarlo es inerte.
- `height="100%"` sobre `Card` es INERTE (atributo HTML, no prop) en `pricing` y
`testimonials`: las tarjetas no igualan alto mientras README y demo afirman lo
contrario.
- B-8 sin declarar (landmark + jerarquia de encabezados) en seis READMEs.
La dimension CRUZADA —los 15 en una pagina compuesta— NO se ejecuto, y es ademas
la condicion de cierre de F2 del plan.
La auditoria confirmo su propia premisa: la capa perceptiva no se habia ejercido
NUNCA porque la galeria arrancaba sin packs de sema desde F0, y ahi esta lo mas
feo (todavia sin verificar): el nav del header emitiendo `commit-select`+`affirm`
con earcon audible al pasar el RATON por encima, un `Enter` en `newsletter`
emitiendo dos `commit-submit` con intents contradictorios, y 4 de 7 elementos del
header mudos.
El handoff `CONTINUE-blocks.md` arranca ahora por la auditoria, con el orden
recomendado (verificar los 50 → arreglar por severidad → pagina compuesta →
catalogo → v2) y la deuda declarada del arnes, que triplica la cadena de arranque.
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 |
|
|
6036560ff0 |
fix(blocks): la galeria emite — packs de sema y canales perceptivos en el arnes
El motor SI emitia: pulsar un boton de un block estampa `data-event=contact-activate` con `family=contact` y `phase=active`, porque el morfo del `Button` declara ese evento (verbo `activate`, secuencia `pre`, sin intent a proposito — una pulsacion no carga intencion evaluativa). Lo que faltaba era el otro lado: la seccion arrancaba UIX **sin `events` y con cero packs de sema**, frente a los 69 de `/uix`. El evento salia a una sala sin altavoces. Viene de F0, cuando el arranque se monto como «espejo minimo» de `/uix` y sema se quedo fuera. Un framework semantico cuya propia galeria no suena no esta demostrando lo que existe para demostrar, asi que los canales van ENCENDIDOS aqui: `sound` y `haptic` a true en las dos cadenas de arranque (galeria y previews). La lista de packs es DERIVADA del barrel (`Object.values(packs)`), no escrita a mano, y eso es deliberado: la lista a mano del otro arnes se quedo atras dos veces — `69c108d2a` tuvo que anadir «los 25 packs que faltaban en la composicion de demos». Una lista derivada no puede quedarse atras. El barrel recomienda importar solo los que usas por tree-shaking; aqui no aplica, porque esto no es un producto sino la galeria del framework y un block compone lo que quiera del canon. Verificado espiando la Web Audio API en un clic real dentro del block: al cargar 1 AudioContext y 0 fuentes; tras pulsar, 2 fuentes y 3 nodos de ganancia mas. Y las 15 demos cargan limpias con los 69 packs, cero errores de pagina. Gates: blocks:check verde (15) · svelte-check sin errores en lo tocado · prettier limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
96393e4305 |
docs(blocks): cada block declara su posicion bajo la doctrina de coordinacion
Y `content-section.Media` pasa a seguir al texto por defecto. DEFAULT: `.Media` era `width='wide'`, asi que una figura se salia de la columna SALVO que dijeras lo contrario. Al reves de lo que hacen todas las referencias (en `@tailwindcss/typography` una imagen dentro de `prose` queda capada a la medida y salirse exige el truco del `translate`; en `markdown-body` de GitHub, `max-width: 100%` de la columna). Ahora el default es `measure` — la figura sigue al texto y el escape se PIDE. Cambiado en los cuatro sitios que decian lo contrario: la parte, la ruta `preview`, el estado inicial del control de la demo (una demo que pisa el default en silencio ensena algo que el block no hace solo) y la doc. Verificado: 528 = ancho del texto exacto a 1011/1280/1920, y `wide` / `full` siguen funcionando cuando se piden. DOCUMENTACION: los 15 READMEs declaran ahora su posicion bajo §«Coordination», y el mapa real no es uniforme — - Coordinan: `contact` (maquina + esquema + palabras) y `pricing` (el periodo por contexto). Pero en pricing nada se puede BLOQUEAR, asi que no necesita maquina ni palabras: coordinar no es siempre una maquina. - Comparten CONFIGURACION, no estado: `team` (`align`) y `content-section` (la medida). - Costura bindable hacia el canon: `faq` (`value` → `Accordion`) y `site-header` (`mobileOpen` → `Drawer`). Reenviar no es poseer. - No poseen nada: banner · cta · feature-grid · feature-split · hero · site-footer · stats-band · testimonials. Decirlo explicitamente es el punto: inventarle estado a un block que no coordina nada es el error contrario. - `newsletter` queda como CANDIDATO ABIERTO: coloca un envio que puede quedar bloqueado sin que nada diga por que — el `disabled` lo monta el app en el punto de uso — que es exactamente el hueco que cerro `contact`. Es anterior a la doctrina (block del 30-07, doctrina del 31-07). Registrado, no asumido: se toca con decision del usuario. El README del tier apunta a la doctrina y el handoff recoge el mapa completo. Gates: blocks:check verde (15) · docs:check 0 · svelte-check sin errores en lo tocado · prettier limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
378457f46c |
feat(blocks): content-section cierra F2 — la rejilla de escape, y las escalas tipograficas salen de Text
El ultimo block de sitio, y el que mas cerca estuvo de ser un envoltorio:
`Section` + `Container` + `Prose` no habria anadido nada, porque `Prose` ya trae
su medida de lectura. Lo que falta en el ecosistema es el ESCAPE — dentro de un
`Container` nada puede ser mas ancho que el, asi que una figura a sangre hay que
sacarla del articulo y ponerla de hermana, y el orden de lectura se rompe.
La seccion es una rejilla de 5 pistas (canalon · flanco · MEDIDA · flanco ·
canalon) y cada parte dice hasta donde llega: `measure` col 3, `wide` col 2/5,
`full` col 1/-1. Todas las longitudes son tokens del sistema (`--measure-*`,
`--container-width-*`, `--container-padding-inline`). `.Body` y `.Media` son
compound porque SE REPITEN; la cabecera son slots de snippet. `.Body` apaga la
medida de `Prose`: dos duenos del mismo ancho se pelean y ganaria el mas
estrecho en silencio.
Los tres ejes son RESPONSIVE (`measure`, `wide`, y el `width` de cada parte),
resueltos con `eidos.resolve()` — el mismo camino que usan los primitivos de
eidos, asi que los unicos breakpoints en juego son los canonicos. Es lo que un
valor unico no puede decir: `{ base: 'full', md: 'wide' }` = foto a sangre en
movil y contenida de `md` arriba.
CANON: un componente no es libreria de otro. `Heading` importaba CINCO escalas
tipograficas de `Text` (`TextTracking`, `TextLeading`, `TextWrap`,
`TextNumeric`, `TextMeasure`) y al tipar este block repeti la violacion. Las
cinco se mueven a `eidos/lib/types.ts`, que es donde su propio encabezado dice
que van y donde ya esta documentado el precedente del mismo fallo (`ColorRole`
re-escrito a mano que derivo a 8 miembros contra 9). Sin shim de compatibilidad:
`text` y `heading` las importan de la libreria.
Cuatro defectos que solo dijo el navegador:
1. `gap` separa tambien las COLUMNAS y una parte que las cruza se lleva esos
huecos encima — `wide` media 1088 en vez de los 1024 del contenedor con el
que debe alinearse, y a 420px `full` llegaba a 532 dentro de una rejilla de
404 y hacia scrollear la pagina. Van `rowGap` + `columnGap={0}`.
2. Una pista `min(medida, 100%)` ignora sus propios canalones: a 420px la
central se quedaba los 404 enteros y la rejilla se iba a 436.
3. Un token de container es un ancho EXTERIOR: con `--container-width-lg` a
pelo la figura `wide` sobresalia 16px por cada lado respecto al texto de la
seccion hermana de debajo, a 1280/1440/1920. La pista ancha resta ahora
`2 * --container-padding-inline` (992 en `lg`) y el desfase es 0. Lo cazo el
usuario mirando la pantalla: yo habia medido el block contra si mismo, no
contra la pagina.
4. `100%` significa algo distinto dentro de cada parte — en `measure`/`wide` ya
es una pista, en `full` es la rejilla entera con canalones —, asi que capar
el pie igual en los dos casos lo dejaba a 340 contra una columna de 372 en
uno y a 404 en el otro.
Verificado en navegador: barrido 375→1920 sin desbordamiento; alineacion
pie↔prosa identica en 3 viewports × 3 medidas × 3 anchuras (18/18); figura
`wide` a ras del texto de la seccion hermana en 1280/1440/1920; y el valor
responsive cambia exactamente en el `md` canonico (a sangre a 767, contenida a
768). Claro, oscuro, 420px y 1920 mirados, y la geometria confirmada tambien en
el Chrome del usuario.
Semantica propia `figure`/`figcaption` (no interactivos → estructura de
documento, B-8) con el hueco senalado: el canon no tiene primitiva `Figure`.
Contexto de CONFIGURACION, no de estado — es un block de layout y no coordina
nada.
Gates: blocks:check verde (15 blocks) · svelte-check sin errores en lo tocado ·
eidos 28/29 suites (la que falla es `audio-player` sin morfo, del hilo de audio,
ajena) · docs:check 0 · prettier limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
69c108d2a0 |
fix(demo): cargar los 25 packs sema que faltaban en la composicion de demos
Cierra la CLASE del hueco que destapo el reporte del media-player
(
|
2 months ago |
|
|
5d2816a0c4 |
fix(sema): silencio del player ampliado a los sliders — pick/drag por canal
Escucha real del usuario tras D-AP2.7 v2: los botones ya callan, los sliders no — sonaban el pickup del agarre y el feedback continuo del drag (el commit-set del soltar ya estaba restado). Dos reglas de descendientes con channels: ['haptic'] para handle-pick y handle-drag de los Sliders compuestos (scrubber y volumen): ambos son eventos ESTRUCTURALES sin intent — el libro concentra la evaluacion en el drop, cubierto por la resta de commit-set — asi que desactivar su canal sonoro dentro del player no borra carga evaluativa (la regla que prohibe tirar canales protege intents, y handle no lleva ninguno). El resolver por-emit del drag sigue poseyendo las primitivas del sonido, pero una senal con el canal inactivo no suena; el tick tactil se conserva. El pickup lleva primitivas absolutas de libreria (gain 0.12), por lo que una resta se acoplaria a su valor — el canal es la via limpia. Corrige la nota del pack («no callable desde el pack era conservadora de mas») y el plan v2 (D-AP2.7 v2 ampliada). Fuera del player los sliders conservan su firma completa (selectores de descendientes, especificidad 30 sobre 20 del pack del slider). Verificado: morfo:vocabulary limpio - sema 192/192 - check 74 = baseline, 0 propios. El verify final es la escucha: agarrar y arrastrar el scrubber debe ser mudo (solo tacto), y fuera del player un Slider suelto debe seguir sonando como siempre. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
2 months ago |
|
|
63f20f3451 |
fix(demo): cargar mediaPlayerSema en la composicion de la app de demos
Reporte del usuario en vivo: el transporte del player seguia sonando en la
demo pese al pack silencioso (D-AP2.7 v2). Causa raiz: los packs de sema son
OPT-IN por app (defineEngineSemantic({ components: [...] }), tree-shaking) y
`mediaPlayerSema` NUNCA estuvo en la lista del layout de demos — ni el
silencio nuevo, ni el tap sutil v1, ni el fix del selector (H-1) llegaron a
aplicarse alli jamas: lo que sonaba era el default crudo de familia (los dos
earcons por clic que H-10 midio).
Dos lineas: import + entrada en `components` de
web/routes/uix/+layout@.svelte. Verificado: check 74 = baseline, 0 propios.
Observacion reportada (fuera de alcance, no tocada): otros packs del indice
tampoco estan en esa lista (dropdown-menu, context-menu, menubar, switch,
toggle, tooltip, tree-view...) — la misma clase de hueco para sus demos.
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 |
|
|
b1b568e32f |
feat(blocks): contact CERRADO — la coordinacion entra en el contrato B
El estado se le pregunta al ESQUEMA, no al Form. `form.isValid` con `validationBehaviour: 'progressive'` significa «aun no se ha encontrado nada mal»: un formulario vacio e intacto no tiene errores, asi que se declaraba valido y `incomplete` era INALCANZABLE al cargar — la seccion pedia resolver la verificacion con los tres campos vacios, justo el hueco que la doctrina existe para cerrar. Ahora la raiz valida los valores con `validateSync` de SIUM: sincrono y sin escribir errores (`form.validate()` habria encendido los tres campos en rojo en el primer frame). `state.test.ts` fija la maquina con 9 casos — primer test unitario del tier, porque `state.ts` es su primera logica pura: el orden de prioridad, que solo `ready` deja enviar, y que todo estado bloqueado tiene frase. Contrato B (architecture/blocks.md): seccion «Coordination» con las tres cosas que posee un block que coordina (estado, forma de sus datos, palabras), B-5 admite el esquema por defecto, B-7 enmendado (posee las palabras de SUS estados como idlangref con fallback ingles) y la convencion de servicios acotada: el traductor es el UNICO servicio sancionado. Dice tambien a quien NO aplica — los blocks de layout no coordinan nada. i18n por la via real: el arnes registra el namespace `blocks` en las DOS cadenas de arranque (galeria y previews, que duplican el boot) y la demo se lee en castellano en vez de caer al fallback ingles. Ademas: README y pestanas de doc rehechos al reparto nuevo, ficha F2.13 y bitacora en PLAN-blocks.md, y el control vivo que faltaba para un prop publico — `verification` con reto / sin reto, porque omitir el prop no es lo mismo que pasar un estado que nunca verifica. Verificado en navegador, arco completo y las dos ramas: sin reto vacio → un campo → tres campos (se desbloquea) → «Enviando…» → «Enviado»; con reto, con los tres campos llenos el boton sigue bloqueado y la razon pasa a «Resuelve la verificacion de arriba». `verifying` y `rejected` quedan cubiertos por test unitario, no por el reto en vivo: el provider de ProofOfHuman escribe `status` el mismo y el veredicto del app no se sostiene (hilo de canon abierto). Gates: blocks:check verde (14 blocks) · vitest 9/9 · docs:check 0 · svelte-check sin errores propios · prettier limpio en los ficheros propios. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |