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 }
1837 Commits (6f42eebfe80bc9ac5656be2bbf621c6d62e768fb)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
dee2c9e3a6 |
fix(direction): dos reglas :dir(rtl) volteaban lo que ya estaba volteado
Salieron documentando el eje, no auditandolo, que es lo que las hace utiles:
son una forma que yo no habia visto y que el guard no ve.
EL DOBLE VOLTEO — una regla `:dir(rtl)` cuyo cuerpo solo reasigna propiedades
LOGICAS. Cuando esa regla matchea, la propiedad YA se ha espejado, asi que
moverla de `inline-start` a `inline-end` la devuelve al punto de partida; y como
las declaraciones hermanas se quedan quietas, aterriza en el borde OPUESTO al de
la cosa a la que pertenece.
- `feed.css` — el hilo anidado sangraba por un borde y dibujaba su rail en el
otro. El rail marca el borde DESDE el que se sangra, asi que viaja en el mismo
lado logico que el padding y se espeja con el.
- `tree-view.css` — la guia de indentacion quedaba aparcada al otro lado del
subarbol que marca.
El arreglo es RETIRAR las dos reglas: las propiedades logicas ya hacian lo
correcto solas. En su lugar queda un comentario, porque el proximo que lea el
recipe va a tener la misma tentacion.
Medido en Chrome: `feed` da padding-left:20px + border-left:2px en LTR y
padding-right:20px + border-right:2px en RTL — los dos en el mismo borde. La
guia de `tree-view` resuelve a left:16px en LTR y right:16px en RTL.
⚠️ NO pude ver los pixeles: el panel del navegador no estaba visible.
COMO SE COLARON: la migracion de §6.3 (`[dir='rtl']` → `:dir(rtl)`, `ec533845d`)
cambio el SELECTOR de las 12 reglas sin preguntarse si el CUERPO era correcto.
Una migracion mecanica hereda los defectos que traduce. RTL-1 tampoco las ve —
busca un desplazamiento FISICO contra un ancla logica, y aqui todo es logico.
Extender el guard a esta forma queda pendiente.
Ademas: el JSDoc de `dir` en waveform prometia «@default from Soma config». Es
falso — waveform es el caso delegante, no llama a `activeDir`, y sin prop no hay
atributo ni lectura de prefs: hereda.
`eidos-lint` feed/tree-view: invalid 0. Ningun test afirma sobre las dos reglas.
`rtl:check` sigue en 1 error, el de `palabras`, preexistente y excluido.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
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 |
|
|
76c807d3c7 |
docs(direction): 9.19, el alias y los cuatro de solo-CSS — el eje queda cerrado
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
d490f53e95 |
feat(direction): los cuatro que pintaban con :dir() no dejaban afirmarla
feed, nav-tree, sidebar y switch tienen reglas `:dir(rtl)` en su receta pero
no aceptaban `dir`. Funcionaban —`:dir()` lee la direccion HEREDADA, y la
proyeccion de prefs pone `dir` en el `<html>`— pero solo para la pagina
entera: no habia forma de voltear UNO. Ahora corren la cadena canonica
`prop → soma.prefs.getDir() → 'ltr'` con `activeDir` en el envoltorio, como
los otros 49.
Estampan el CRUDO (`opts.dir.current`, no `resolvedDir`) por la razon de
siempre: `:dir()` lee la direccion RESUELTA del elemento, asi que una
afirmacion que no llegue al DOM movería el contrato y dejaria la pintura
atras — el mismo partido-por-la-mitad que costo el arreglo de tree-grid y
float-panel.
`switch` no pudo ir por `OptsFromProps`: ese helper quita el `undefined` del
prop publico y da `Active<'ltr' | 'rtl'>`, cuando «nadie afirmo nada» es
justo el valor que el estampado crudo necesita distinguir. Va aparte, con
`ActiveProps<{ dir: Direction | undefined }>`, y en el envoltorio fuera del
`bindProps` (que toma getters, no Actives).
sidebar tenia ademas un resolutor propio en el motor flotante
(`soma.prefs?.getDir() ?? 'ltr'` a pelo, linea 662) que se saltaba el prop:
ahora consume `resolvedDir`. Ese si necesita valor concreto —el motor espeja
la colocacion del flyout— a diferencia de la receta, que lee `:dir()`.
Verificado: `check` sin errores nuevos en los cuatro (77 total = la base con
los ficheros de media-player, que son de otra sesion). Tests de feed y switch
en verde tras anadir `dir` a sus helpers de opts. En SSR los cuatro estampan
`dir="ltr"`; en Chrome el switch pasa a `dir="rtl"` al girar la preferencia y
la regla dispara —`--_switch-thumb-offset` va de `16px` a `calc(-1 * 16px)`.
La transicion del pulgar no se pudo medir: el panel del navegador estaba
oculto y las transiciones CSS quedan congeladas ahi.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
1884500725 |
refactor(direction): ocho componentes escribian el union a mano en vez del alias
`Direction` existe en `soma/types.ts` y es lo que consume el resto de la cadena — `activeDir`, las opts de los proveedores, `resolvedDir`. Estos ocho declaraban `dir?: 'ltr' | 'rtl'` literal, que compila igual pero deja el contrato fuera del alias: renombrar o ensanchar `Direction` no los tocaria y la deriva no rompe en tipos, que es justo lo que el alias esta para garantizar. Son calendar, command, emoji-picker, field-langs (props Y el spec de lengua del proveedor), month-grid, range-calendar y year-grid. Cambio de tipo puro, sin efecto en runtime. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
bfb58b9c78 |
docs(direction): 9.18, la auditoria de la canonizacion
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
3b4340d2ad |
fix(direction): auditoria de la canonizacion — 2 defectos propios y 1 artefacto
Repaso critico del refactor de los 11, con el criterio CORREGIDO que la otra sesion demostro que faltaba: buscar tambien quien ACEPTA `dir` y cruzarlo con quien lo resuelve. DEFECTO 1 — anadi la prop pero NO el estampado. `tree-grid` y `float-panel` usan `:dir()` en su recipe y no estampaban el atributo, asi que un consumidor que afirmase `dir` por prop movia la MATEMATICA y dejaba la PINTURA atras: el `:dir()` lee la direccion efectiva, que sin atributo es la heredada. Es el mismo partido-por-la-mitad que la otra sesion hallo en `media-player`. Verificado tras el arreglo: `tree-grid` estampa ltr/rtl/auto correctamente y el grip del `float-panel` pasa de `nwse-resize` a `nesw-resize` con el atributo. Los otros 5 del grupo B no llevan `:dir()` en su recipe, asi que no estampar no rompe nada; se dejan como estan para no ampliar el DOM sin motivo. DEFECTO 2 — artefacto de mi regex: cinco ficheros quedaron con un espacio antes de la coma (`OnChangeFn , Direction`). Prettier no lo corrigio porque ya estaban sin formatear en HEAD, asi que no pasaba por su escritura. Corregidos a mano. FALSO POSITIVO revisado y NO tocado — `waveform` acepta `dir` y no lo resuelve, pero **no esta roto**: no tiene matematica direccional propia. El recipe espeja con `:dir(rtl)` (medido: la onda pasa a `scaleX(-1)`) y el Slider que compone corre la cadena por su cuenta; el wrapper solo reenvia el crudo, que es lo que mantiene ambas mitades de acuerdo. Lo unico corregido ahi es el COMENTARIO, que afirmaba que el recipe se apoya en `[dir='rtl']` —prohibido desde 6.3— cuando usa `:dir(rtl)`. `check` sin errores en ninguno de los componentes tocados. 16/16. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
5269c4f6a9 |
feat(direction): `dir` pasa a ser prop en los 3 ultimos del grupo B
`float-panel` · `virtual-grid` · `virtual-list`. Cierra el censo: los 49 componentes con logica direccional resuelven ya por la MISMA cadena, en el MISMO sitio. Mismo patron que 9.16 y que los cuatro anteriores: `dir?: Direction` en types, `activeDir(() => dir, soma)` en el wrapper, `resolvedDir = opts.dir.current ?? 'ltr'` en el provider. Las lecturas sueltas de `soma.prefs.getDir()` que quedaban dentro de la matematica —el calculo de la semilla anclada y el edge fisico del resize en float-panel, el signo del translate de las celdas en los dos virtualizadores— pasan a leer ese `resolvedDir`. Tres factorias de test recibieron el campo; el compilador enumero el trabajo en cada paso, que es justo para lo que el handoff recomienda ensanchar el tipo primero. `check` sin errores en ninguno de los 11 componentes tocados. 22/22. NOTA: el conteo global fluctua entre 74 y 77 durante la sesion por cambios de `media-player`, que pertenecen a OTRA sesion sobre esta misma rama y quedan fuera de todos estos commits. |
2 months ago |
|
|
23315b711e |
feat(direction): `dir` pasa a ser prop en 4 de los 7 que no la exponian
Grupo B del censo (9.17). No usaban otro mecanismo: usaban el MISMO con el
primer eslabon ausente —leian `soma.prefs.getDir()` porque no habia prop que
consultar—, que es lo que el contrato prescribe sin prop. Lo que se subsana es
la ASIMETRIA de superficie: 42 componentes dejaban fijar la direccion de su
subarbol y estos no.
Hechos aqui: `tag-group` · `grid-list` · `tree-grid` · `drawer`.
El patron, identico en los cuatro y ya probado en 9.16:
types.ts `dir?: Direction` documentado con la cadena canonica
wrapper `dir: activeDir(() => dir, soma)`
provider `resolvedDir = opts.dir.current ?? 'ltr'` (solo el fallback)
atributo el CRUDO (`opts.dir.current`), omitido si nadie afirmo
⚠️ `drawer` merece cuidado: ya exponia `direction` (el BORDE al que se pega:
top/right/bottom/left/start/end). Ahora convive con `dir` (la direccion de
LECTURA, ltr/rtl), que es justo lo que resuelve `start`/`end` a un lado fisico.
Ambas quedan documentadas apuntandose la una a la otra para que nadie las
confunda.
Cinco tests apoyaban su expectativa en el mecanismo viejo (el harness devolvia
una direccion por `prefs.getDir()` y esperaban verla resuelta dentro del
provider). Actualizados: el provider recibe la direccion YA resuelta, que es lo
que el wrapper le entrega. El del drawer gana ademas la comprobacion de que un
borde FISICO (`left`) ignora la direccion de lectura.
`check` = 74 = linea base (medido con el arbol limpio; los 2 errores extra que
aparecen en el conteo son de `media-player`, tocado por otra sesion y ajeno a
este trabajo). 15/15.
QUEDAN 3 del grupo B: `float-panel`, `virtual-grid`, `virtual-list`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
3c73af90a3 |
docs(direction): 9.16 grupo A canonizado, 9.17 el grupo B es decision de API
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
b73d6baf8c |
refactor(direction): la cadena de `dir` baja al wrapper en los 4 que la resolvian en el provider
`013ceac57` unifico DIEZ formas de resolver la direccion en una: la cadena `prop -> soma.prefs.getDir() -> 'ltr'` vive en el WRAPPER (`activeDir`) y el provider solo pone el ultimo fallback. El censo de 6.12 dio por hecho que los 36 restantes ya la seguian, pero contaba los wrappers con default duro y no los que resuelven en OTRO sitio. Cuatro se habian quedado fuera: css-field · listbox · navigation-menu · number-field Todos pasaban la prop CRUDA (`readableActive(() => dir)`) y duplicaban la cadena dentro del provider. Dos de ellos —`listbox` y `navigation-menu`— lo documentaban como desviacion consciente («Unlike the other components…»). Funcionalmente eran equivalentes HOY: verificado que `number-field` y el canonico `scroll-area` estampan lo mismo con el toggle en `auto`. Pero la divergencia era real y latente en dos: css-field y number-field estampaban el valor RESUELTO en el atributo. Con un esquema de prefs sin dimension de direccion, `getDir()` devuelve undefined: los canonicos omiten el atributo y esos dos estampaban `'ltr'` — el mismo defecto que `013ceac57` corrigio en 33 componentes, superviviente en 2. Ahora estampan el crudo (`opts.dir.current`), como el resto. Tres tests se apoyaban en el mecanismo viejo (el harness devolvia una direccion por `prefs.getDir()` y esperaban verla en el provider). Actualizados para reflejar el contrato: sin afirmacion, el atributo se OMITE; con ella, se estampa. `listbox` gana ademas la comprobacion explicita de ese par. Verificado en Chrome en las tres posiciones del toggle (`ltr` / `rtl` / `auto`+arabe): `number-field` y `listbox` estampan y computan igual que antes. `check` 74 = linea base, 25/25. QUEDA (grupo B, 7 componentes): `drawer`, `float-panel`, `grid-list`, `tag-group`, `tree-grid`, `virtual-grid`, `virtual-list` NO exponen prop `dir` y leen prefs directamente. No estan rotos —es la cadena con el eslabon de prop ausente— pero son una asimetria de API: 42 componentes dejan fijar la direccion de su subarbol y esos 7 no. Anadir la prop es superficie publica. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
8cda5fd252 |
docs(direction): 9.15, censo de canonizacion — 38 canonicos, 11 fuera de la norma
El censo de 6.12 estaba incompleto: contaba los wrappers con default duro pero no los que resuelven en el provider (4) ni los que no exponen dir (7). Ninguno esta roto hoy — verificado que number-field y scroll-area estampan lo mismo — pero quedan dos decisiones: mover la cadena al wrapper, y si los 7 sin prop deben exponerla. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
7b306ae5d7 |
docs(direction): 9.13 decidida (teclado fisico) y 9.14, el resize del float-panel en RTL
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
552f4f2c77 |
fix(float-panel): el resize se calculaba al reves en RTL, y se salia de sus bounds
Reportado por el usuario con una captura del grip en la esquina inferior
IZQUIERDA. Ahi es donde debe estar en RTL —el recipe lo coloca con
`inset-inline-end`, logico— pero la matematica no acompanaba.
`onResizeMove` leia el edge como si fuera FISICO:
if (edge.includes('e')) width = start.width + dx; // dx es fisico
Los handles se colocan con insets LOGICOS, asi que el marcado `e` es el borde
inline-END: fisicamente derecho en LTR y fisicamente IZQUIERDO en RTL. El
resultado, medido: arrastrar el grip hacia FUERA encogia el panel — 436 -> 327
donde debia crecer a 545. El eje de bloque si estaba bien (292 -> 346).
Arreglado resolviendo el edge logico a su lado FISICO una sola vez
(`physicalResizeEdge`), de modo que toda la geometria de abajo sigue en
pixeles fisicos — la misma forma que usa `drawer.resolveDirection()`, que 6.9
senalo como el ejemplo a imitar.
Y eso destapo un SEGUNDO defecto, latente: crecer desde `w`/`n` mantiene el
lado opuesto fijo, asi que el origen del panel viaja hacia fuera, y ese caso
no se acotaba contra los bounds — el panel se salia del contenedor (medido
`POS -85` con el stage empezando en 0). En LTR la rama era inalcanzable: solo
montan `e`, `s` y el grip `se`. Acotado por el hueco disponible hasta el
limite.
El grip ademas mentia visualmente en RTL: `cursor: nwse-resize` (flecha ↘↖) y
los chevrones a `-45deg / bottom right` son fisicos y no tienen forma logica.
Par `:dir(rtl)` con `nesw-resize` y `45deg / bottom left`.
Verificado con raton real y a ojo en las dos direcciones:
RTL arrastre hacia fuera 300 -> 409 de ancho, borde DERECHO fijo en 803
RTL arrastre largo se detiene exacto en el borde del stage (left 0)
LTR sin cambios 300 -> 436, borde izquierdo fijo en 24
DECISION documentada (el usuario la dejo a mi eleccion): el movimiento por
teclado del panel —flechas y `Home`/`End`— se queda FISICO. No hay patron APG
para paneles flotantes movibles; el analogo, el Window Splitter, define las
teclas por VALOR («la posicion que da al panel primario su menor/mayor
tamano») y no menciona RTL, pero un panel que se arrastra libre por un plano
no tiene valor, solo posicion. Sin semantica que respetar, el vocabulario
fisico es coherente CONSIGO MISMO —`bounds.left`/`bounds.right` ya lo son— y
esa es la misma excepcion legitima que dejo intacto al `toast` en 6.10.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
013af67d7b |
docs(direction): 9.12 color-picker cerrado por las referencias, 9.13 float-panel sin precedente
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
46ffe1c74b |
fix(color-picker): el area 2D y las rampas de canal no se espejaban en RTL
El eje X del area lleva un CANAL de color repartido por la dimension inline —
un eje de lectura— asi que espeja, igual que el `ColorArea` de react-aria
(«orientation of the gradient background, positioning of the thumb, and
dragging behavior is automatically mirrored»). Una rueda radial no lo haria:
es el mismo corte lectura-vs-radial que ya usa la regla de las graficas. Mi
analogia previa con el `cropper` era mala y queda retirada — alli paneas una
imagen, aqui recorres un canal.
Medido antes: pinchar el MISMO punto fisico daba el mismo valor en las dos
direcciones, y el degradado no seguia al thumb.
Lo que faltaba, por pieza:
area · puntero la fraccion fisica se espeja sobre el canal
area · thumb ancla PHYSICA (`left`) alimentada ya espejada
area · flechas ArrowRight mueve el thumb a la derecha ⇒ BAJA el canal
area · degradados saturacion y arcoiris de hue voltean con `:dir(rtl)`
sliders de canal solo el degradado
⚠️ El thumb conserva `left` FISICO a proposito: el recipe lo centra con
`translate(-50%,-50%)`, y ancla logica + translate fisico es justo la trampa
que el contrato de RTL prohibe. Soma le pasa la fraccion ya espejada
(`physicalXProgress`).
Los sliders de canal casi no necesitaban nada: desde el refactor de 2026-05-21
componen el `SliderProvider` generico, que ya espeja thumb, puntero y teclado.
Pero su rampa estaba clavada a `to right`, asi que el color BAJO el thumb
dejaba de ser el valor — medido con hue 210: thumb a 123px del borde derecho y
degradado contando aun desde la izquierda.
Verificado en Chrome en las dos direcciones: pinchando al 10% del borde
izquierdo fisico, LTR da saturation 10 y RTL da 90; el blanco del area y el
rojo del hue quedan a la derecha en RTL; ArrowRight mueve el thumb a la derecha
en ambas. Los dos tests nuevos salen ROJOS con el provider anterior.
`Home`/`End` se quedan en min/max del CANAL (un valor, no un borde fisico),
igual que en un slider.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
ac2ca97444 |
docs(direction): 9.11 ejecutada — el lang del documento ya sigue al idioma
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
41e1f28306 |
feat(prefs): el `lang` del documento sigue al idioma, como ya hacia el `dir`
`src/app.html` trae `<html lang="en">` hardcodeado y NADIE lo actualizaba: con arabe seleccionado la pagina quedaba correctamente espejada (`dir="rtl"`, que la proyeccion si escribe) pero anunciada en el idioma equivocado. El `lang` gobierna seleccion de fuentes, corte de palabras, glifos de comilla y lo que lee CUALQUIER lector de pantalla. `lang` viaja con `dir` porque responden a la misma pregunta sobre el documento y el navegador lee LOS DOS del DOM. La dimension `language` ya estaba en el esquema —es de donde `prefs` deriva la direccion— asi que el slot ya estaba disponible: el valor efectivo es un tag BCP-47, que es justo lo que el atributo toma. Un esquema SIN la dimension `language` no produce slot y no se proyecta nada, asi que una app que gobierne el `lang` desde el servidor (i18n por routing) queda intacta. Cambia el contrato declarado: `prefsDomProjection.ownsAttrs` pasa a `['dir','lang','data-motion','data-sound','data-haptic']`, con su guard en `contracts.test.ts` y el README de prefs. Medido en Chrome por el camino real (el selector de idioma de la shell): ar -> lang=ar/dir=rtl · en -> lang=en/dir=ltr · es -> lang=es/dir=ltr. Un solo `language.set` mueve los dos: el `dir` por derivacion, el `lang` directo — y eso queda fijado en `dom-projection.test.ts`. Sin tocar el `<main lang="en">` de las demos: NO es el defecto, es correcto — la prosa de las demos esta en ingles (decision de 6.4). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
23d2e94783 |
docs(direction): el dir vacio era el shorthand {dir} de Svelte, y el diagnostico del html lang
9.10 cierra el dir=\\` que 6.5 dejo sin investigar: el shorthand emite una asignacion de PROPIEDAD ademas del atributo, y con undefined queda la cadena vacia. No estaba en el SSR — lo escribia el cliente al hidratar. 9.11 deja el html lang diagnosticado pero NO ejecutado: la causa es app.html con lang=en hardcodeado, el arreglo seria corto (la shell ya registra language en prefs) pero cambia el contrato ownsAttrs de la proyeccion, y hay un argumento en contra — en apps con i18n por routing el lang lo fija el servidor. Decision de arquitectura, pendiente de criterio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
b906dd4077 |
fix(direction): el atributo `dir` se estampaba VACIO cuando nadie lo afirmaba
El contrato de `013ceac57` dice que `dir` se OMITE cuando nadie ha afirmado una
direccion. `6.5` habia visto `dir=""` en sidebar y tree-view sin averiguar de
donde salia. Es el shorthand `{dir}`: para `dir` Svelte emite una asignacion de
PROPIEDAD ademas del atributo, y con `undefined` el resultado es la cadena
vacia en vez de la omision. Atrapado con un trap sobre `setAttribute` + el
descriptor de la propiedad: dos escrituras, la segunda por propiedad.
No estaba en el SSR (`dir=""` no aparece ni una vez en el HTML servido) — lo
escribia el cliente al hidratar.
Un valor invalido HEREDA, asi que nada pintaba mal; pero contradice la letra
del contrato y hace que `[dir]` matchee.
Arreglado con spread condicional, que si omite:
{dir} -> {...dir ? { dir } : {}}
- `tree-view` (eidos): el wrapper LO NECESITA cuando el valor es concreto — el
recipe selecciona con `[data-tree-view-root]:dir(rtl)` y ese div es el
ANCESTRO del provider que estampa el attr. Con el valor ausente hereda, que
es lo correcto.
- 26 demos: el arnes estandar (`data-uix-stage-area` / `data-uix-canvas-inner`).
Verificado: en `auto` solo quedan `<html>` (la proyeccion de prefs) y el
provider con su valor resuelto — cero `dir=""`; en `rtl` los tres elementos lo
reciben concreto; al volver a `auto` desaparece limpiamente. Las 26 rutas
sirven 200 y ninguna trae `dir=""`.
METODO: mi primer regex tambien matcheaba dentro de `dir={dir}` y dejo
`dir={...}` — dos ficheros con parse error que `check` cazo (76 vs 74). Contar
ficheros modificados NO basta; hay que mirar el diff.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
3bfe2c6527 |
docs(direction): 8.3 cerrada — coarse eran 6 media queries, no 2, y el swipe con raton real
Seccion 9.9. Dos cierres y un hallazgo del usuario: - `pointer: coarse`: el catalogo tiene SEIS media queries, no las dos que 8.3 daba por unicas. Las cuatro sin mirar (list-surface, chat-message, radio-group, rotate-align) no tienen ninguna geometria de eje inline — `--list-item-height`, `opacity`, `min-block-size` e `inset: 0` + `min-*` logicos. Las dos que si la tenian son justo las que 6.2 midio con hit-test. Cerrado por lectura, que aqui basta porque lo que se comprueba es una AUSENCIA. - El swipe del carousel con el raton REAL del navegador: las 4 combinaciones dan espejo exacto. Cierra lo que 6.8 no consiguio disparar por simulacion. MÉTODO: el raton real avanza 2 posiciones de golpe (momentum) y `left_click_drag` no deja fijar la velocidad — lo que se comprueba es el SIGNO, no la magnitud. Y verifica la direccion ANTES de cada gesto: mi primera pasada midio creyendo estar en LTR con el toggle en RTL, y el resultado parecia un defecto inexistente. - Las flechas del carousel en RTL (arreglo en el commit anterior). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
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 |