From 2daa683fa4b5ae7e3303004a31300731e29c90ea Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 24 Aug 2026 22:00:55 +0200 Subject: [PATCH] fix(theming)!: file-upload pinta lo SUYO - muere el barrido de descendientes `[data-file-upload] [data-disabled]` (0,2,0) vestia todo nodo disabled bajo el provider, HUESPEDES incluidos, y ganaba el empate por orden de import - el accidente que B' condena. Medido en la composicion documentada del composer: el textarea huesped recibia la atenuacion TRES veces (0.4^3 = 0.064, el placeholder casi ilegible) con el file-upload NI SIQUIERA disabled, y `--chat-composer-disabled-opacity` no alcanzaba nunca. - La mitad descendiente se sustituye por los CINCO selectores por parte (label, dropzone, trigger, item-remove, clear-trigger; el provider ya estaba). El morfo declara `data-disabled` exactamente en esas partes y soma lo estampa siempre junto al atributo de parte: el barrido era 100 % redundante hacia dentro - solo anadia alcance hacia fuera. - Unicidad re-censada HOY: 171 recetas, unica ocurrencia con `data-disabled`. - Medido: file-upload CERO ESTRICTO (4.608 valores x 8 estados + pase disabled propio 0/35); composer recupera EXACTAMENTE 6 valores (opacity y alpha de textarea/input, cursor x2) y nada mas - la excepcion a D-TH.5 firmada, con capturas antes/despues. - Centinela: composer 29 -> 30/33 (revive `disabled-opacity`; su excepcion EXACTA salio STALE por si sola - el reverso de la ceguera a patrones); file-upload 61/98, exit 0 ambos. - README del composer corregido: su adjudicacion "no se ve hasta que un tema mueva" era FALSA para el textarea (triple atenuacion en el tema default). - Registro: PLAN 8 entrada fechada - CONTINUE item 3 EJECUTADA - notas en las fichas de ambos. Aviso de instrumento (en el registro): la sonda sancionada es CIEGA a esta firma (sin paso disabled; su filtro del composer no ve [data-textarea]) - su cero vale como regresion, no como testigo; el gate se cumplio con instrumento dedicado, exacto y sin extras. Co-Authored-By: Claude Fable 5 --- docs/audit/theming/chat-composer.md | 26 +++++++++++ docs/audit/theming/file-upload.md | 20 +++++++++ docs/process/CONTINUE-theming.md | 10 ++++- docs/process/PLAN-theming.md | 43 +++++++++++++++++++ scripts/theming-sentinel-exceptions.ts | 15 ++++--- .../eidos/components/chat-composer/README.md | 29 +++++++++---- .../components/file-upload/file-upload.css | 16 ++++++- 7 files changed, 141 insertions(+), 18 deletions(-) diff --git a/docs/audit/theming/chat-composer.md b/docs/audit/theming/chat-composer.md index 869e01936..c176ec09f 100644 --- a/docs/audit/theming/chat-composer.md +++ b/docs/audit/theming/chat-composer.md @@ -147,4 +147,30 @@ del servidor de desarrollo. Los 2.204 valores restantes, idénticos. **Lo que queda fuera y por qué**: una identidad firmada (§1.4) y cinco knobs de sistema transversal (§2). El componente está CERRADO en su número. +**Nota 2026-08-24 — la adjudicación de `disabled-opacity` de aquí arriba era +FALSA, y la firma la retira.** `file-upload` acotó su barrido a sus cinco partes +propias, así que la colisión ya no existe. Pero además la cuenta que la +justificaba estaba incompleta: «invisible hasta que un tema mueva uno» es cierto +para las dos reglas PROPIAS del composer —`[data-chat-composer][data-disabled]` +y `[data-chat-composer-send][data-disabled]` empataban a 0.4 y ahí se quedaban— +y **falso para el textarea**, que esa lectura no miró. `[data-textarea]` y +`[data-textarea-input]` **no declaran atenuación propia** (y esta ficha ya +declara el wrapper «celda neutra»), así que el barrido les imponía 0.4 **dos +veces más** sobre el 0.4 legítimo del provider. La opacidad multiplica: +**0.4³ = 0.064 de alpha efectiva en el TEMA POR DEFECTO**, seis veces más +apagado de lo diseñado, con el placeholder casi ilegible — **925 de 25.568 px, +3,6 % de la superficie**, sin tocar un tema. + +**Recuperado, medido**: exactamente **6 de 25** valores del estado `disabled` +—`[data-textarea]` opacity 0.4 → 1, alpha 0.16 → 0.4 y cursor `default` → `auto`; +`[data-textarea-input]` opacity 0.4 → 1 y alpha **0.064 → 0.4**; y el cursor +`default` → `auto` del provider del composer—. Nada más: `send` y el provider +siguen en 0.4 porque ambos defaults son `--opacity-disabled`; lo que recuperan es +que su `--chat-composer-disabled-opacity` **vuelve a alcanzar**. Centinela +**29 → 30/33** y la entrada del ledger sale como STALE (retirada de +`theming-sentinel-exceptions.ts`). La sonda estándar da 0 diffs reales — es +CIEGA a esto: no tiene paso `disabled` y su filtro de nodos no incluye +`[data-textarea]`; los 4 crudos son otra vez el `blockSize` flotante del +`attach` ya documentado arriba, reproducido sobre código idéntico. + diff --git a/docs/audit/theming/file-upload.md b/docs/audit/theming/file-upload.md index 27f0c2ae3..216ebcdb4 100644 --- a/docs/audit/theming/file-upload.md +++ b/docs/audit/theming/file-upload.md @@ -157,4 +157,24 @@ control (gana por orden a igual especificidad): 0 diffs sobre 6.840 valores. El instrumento pasó su muta-prueba: alterando el CSS de control acusa 19 diffs, y 1 si sólo se altera el borde inválido. +**Nota 2026-08-24 — FIRMA: la receta deja de BARRER DESCENDIENTES.** La regla de +disabled (`file-upload.css:200-218`) escribía +`[data-file-upload][data-disabled], [data-file-upload] [data-disabled]`. La +segunda mitad, (0,2,0), vestía TODO nodo deshabilitado bajo el provider —los +ajenos incluidos— y ganaba el empate por orden de emisión. **Única ocurrencia de +ese patrón en las 171 recetas del árbol.** Pasa a cinco selectores por parte +propia (`label`, `dropzone`, `trigger`, `item-remove`, `clear-trigger`), con la +mitad del provider intacta y la misma especificidad. + +Se pudo retirar entera porque el morfo declara `data-disabled` en 6 partes y +soma lo escribe en esos 6 nodos exactos, **siempre junto a su +`data-file-upload-{parte}`**: el barrido no alcanzaba ni un nodo propio que un +selector por parte no alcance. Verificado en runtime — las 6 partes conservan +`opacity: 0.4` y `cursor: default`. **Este componente NO se mueve**: sonda +estándar 0 diffs (4.608 valores · 8 estados) y **0 de 35** valores en el estado +`disabled` medido aparte (la sonda compartida no tiene paso `disabled`). +`image-picker`, el otro consumidor de sus partes, **0 de 15**. Lo que se movió +es el huésped: ver `chat-composer.md`. Centinela 61/98, exit 0. Detalle en §8 de +`PLAN-theming.md`. + diff --git a/docs/process/CONTINUE-theming.md b/docs/process/CONTINUE-theming.md index 49a9a97f3..4cde4cd77 100644 --- a/docs/process/CONTINUE-theming.md +++ b/docs/process/CONTINUE-theming.md @@ -119,8 +119,14 @@ guarda `active-eidos-config.test.ts` · ledger `scripts/theming-sentinel-excepti y que reaparece por tercera vez en `prose.code-bg` / `code-radius` / `code-font-family` (`:862-865`). Lo que cambia es el ALCANCE de la firma: de radio / padding / tipografía **a incluir también la tinta**. - 3. **`file-upload` barre DESCENDIENTES** (`[data-file-upload] [data-disabled]`) - y se come el token de sus huéspedes. Única ocurrencia en 162 recetas. + 3. ~~**`file-upload` barre DESCENDIENTES**~~ — **✅ FIRMADA Y EJECUTADA + 2026-08-24**: la mitad descendiente pasa a cinco selectores por parte propia + (el morfo declara `data-disabled` en 6 nodos y los 6 llevan su attr de + parte, así que el barrido era redundante hacia dentro). Única ocurrencia en + **171** recetas, no 162. `file-upload` e `image-picker` con diff CERO; + `chat-composer` recupera 6 valores —el textarea salía a **0.4³ = 0.064** de + alpha en el tema POR DEFECTO, 925 px— y su entrada del ledger se RETIRA. + Detalle en §8 del PLAN. 4. **Las cuatro claves de `avatar` fuera de gramática** (`*-color-custom*`, la ranura de tinta es `fg`). Son contrato público: renombrar es romper. 5. **`tags-input`** sella `aria-selected='true'` como LITERAL en cada etiqueta, diff --git a/docs/process/PLAN-theming.md b/docs/process/PLAN-theming.md index 561769442..66399f3ea 100644 --- a/docs/process/PLAN-theming.md +++ b/docs/process/PLAN-theming.md @@ -521,6 +521,49 @@ excepción abre la mano para MEDIR, no para contabilizar. ## 8. Registro +- 2026-08-24 — **FIRMA: `file-upload` deja de BARRER DESCENDIENTES.** Su receta + escribía `[data-file-upload] [data-disabled]` (0,2,0) —única ocurrencia de + «combinador descendiente hacia un atributo de estado desnudo» en las **171** + recetas del árbol (el claim decía 162; el censo creció)— y vestía TODO nodo + deshabilitado bajo el provider, huéspedes incluidos. Pasa a **cinco selectores + por parte propia** (`label`, `dropzone`, `trigger`, `item-remove`, + `clear-trigger`), conservando la mitad del provider. Misma especificidad, así + que ninguna otra regla se mueve. + + **Por qué se podía retirar entero**: el morfo declara `data-disabled` en 6 + partes y soma lo escribe en esos 6 nodos exactos + (`file-upload-provider.svelte.ts:265, 300, 399, 450, 751, 838`), **siempre + junto a su propio `data-file-upload-{parte}`**. El barrido era 100 % + redundante hacia dentro y sólo añadía alcance hacia fuera. Censo por demo: + `/file-upload` 6 propios / 0 ajenos · `/image-picker` 2 / 0 · + **`/chat-composer` 0 propios / 4 AJENOS** — allí no pintaba ni un nodo suyo, y + `[data-file-upload]` ni siquiera estaba deshabilitado. + + **Los números**: `file-upload` diff **CERO ESTRICTO** (4.608 valores × 8 + estados) y **0/35** en el estado disabled; `image-picker` **0/15**; + `chat-composer` exactamente **6 de 25** valores, todos recuperación — + `[data-textarea]` opacity 0.4 → 1 y alpha efectiva 0.16 → 0.4, + `[data-textarea-input]` opacity 0.4 → 1 y alpha **0.064 → 0.4**, y el `cursor` + `default` → `auto` del provider del composer y del wrapper del textarea. Nada + extra. **925 de 25.568 px (3,6 %)** en el TEMA POR DEFECTO. Centinela + `file-upload` 61/98 y `chat-composer` **29 → 30/33**, los dos exit 0. + + **Lo que enseñó**: (1) **una adjudicación puede ser falsa por no mirar al + huésped ENTERO.** El ledger daba `chat-composer.disabled-opacity` por + invisible «hasta que un tema mueva uno» — cierto para las dos reglas propias + del composer, que empataban a 0.4, y **falso para el textarea**, que no + declara atenuación y recibía el 0.4 dos veces más sobre el legítimo del + provider: la opacidad MULTIPLICA y 0.4³ = 0.064 se ve sin tocar el tema. La + entrada se RETIRA (`theming-sentinel-exceptions.ts`), y esta vez **el guard sí + la cazó** —era clave EXACTA, no patrón—, que es justo el reverso de la ley 1 + de B′. (2) **La sonda compartida no tiene paso `disabled`** (rest / size / + open / hover / focus) y su filtro de nodos para `chat-composer` no incluye + `[data-textarea]`: era CIEGA a esta firma, y su cero no es prueba. La + recuperación se midió con instrumento propio; el cero de la sonda vale como + regresión, no como testigo. (3) **Un empate resuelto por orden de emisión no + es una decisión, es una que aún no se ha tomado** — ley hermana de B′, tercera + aplicación de la misma doctrina tras §12.9. + - 2026-08-24 — **`toast` 98 % → 100 %** (`b8eccc1c4`), contrato 85 → **85** claves —ninguna acuñada—, centinela **51/85** con 34 adjudicadas. Llegaba con `global`, `private` y `sistema` a CERO y **un** knob fuera de alcance: el diff --git a/scripts/theming-sentinel-exceptions.ts b/scripts/theming-sentinel-exceptions.ts index 91eb2e12f..e83536d97 100644 --- a/scripts/theming-sentinel-exceptions.ts +++ b/scripts/theming-sentinel-exceptions.ts @@ -159,17 +159,20 @@ export const SENTINEL_EXCEPTIONS: Record> = { 'pause-z': 'the pause control is not mounted in the demo (0 nodes on the page)' }, 'chat-composer': { - // 2026-08-23. Four, and three of them are the guard's own two habits. + // 2026-08-23, narrowed 2026-08-24. Three, and ALL of them are the guard's + // own two habits. `disabled-opacity` was the fourth and is RETIRED: it was + // adjudicated on file-upload's descendant sweep `[data-file-upload] + // [data-disabled]`, which the firma of 2026-08-24 narrowed to its five own + // parts. The entry's own premise ("invisible until a theme moves one") was + // false: the sweep also hit `[data-textarea]` and `[data-textarea-input]`, + // which declare no dim, stacking 0.4 three deep (0.064 effective) in the + // DEFAULT theme. The token moves again — the guard reads it STALE. 'focus-border': 'only paints under :focus-within, and the guard blurs the active element after opening; focused the REAL textarea with transitions frozen -> reaches (color(srgb 0.745 0.577 0.894 / 0.48) -> rgb(1, 2, 3) on the shell border)', 'transition-duration': 'the guard freezes transitions and this token IS the transition; measured unfrozen -> reaches (0.12s -> 4.321s on the shell AND on the send)', 'transition-ease': - 'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(3))', - // NOT the guard: a cross-recipe collision, and the composer is on the losing - // side of it BY DESIGN — its documented composition puts it inside FileUpload. - 'disabled-opacity': - 'file-upload sweeps DESCENDANTS: `[data-file-upload] [data-disabled]` is (0,2,0), the same weight as `[data-chat-composer-send][data-disabled]`, and its recipe is emitted LATER, so it wins over every disabled node inside a FileUpload — which is where the demo (and the documented composition) puts the composer; measured on the REAL nodes with the ancestor attr removed -> reaches (0.4 -> 0.123 on the send AND on the shell). Both defaults are --opacity-disabled, so the collision is invisible until a theme moves one' + 'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(3))' }, 'chat-message': { // 2026-08-24. Four, and every one of them is a limit of the INSTRUMENT — diff --git a/src/uix/eidos/components/chat-composer/README.md b/src/uix/eidos/components/chat-composer/README.md index 67e7a99b8..ec6f275f1 100644 --- a/src/uix/eidos/components/chat-composer/README.md +++ b/src/uix/eidos/components/chat-composer/README.md @@ -61,17 +61,28 @@ no tema. **Identidad firmada** (fuera del ratio): el `inline-size: 100%` del textarea, que ocupa su celda de la rejilla. -**Lo que el guard da por silencioso** (4, todas medidas): +**Lo que el guard da por silencioso** (3, todas medidas): `focus-border` (sólo bajo `:focus-within`, y el centinela hace blur tras abrir) · `transition-duration` / `transition-ease` (el centinela CONGELA la -transición para poder medir lo demás; sin congelar, 0,12 s → 4,321 s) · -`disabled-opacity`, que es lo único que no es una limitación del instrumento: -`file-upload` barre DESCENDIENTES (`[data-file-upload] [data-disabled]`, (0,2,0), -el mismo peso que `[data-chat-composer-send][data-disabled]`) y su receta se -emite MÁS TARDE, así que gana sobre cualquier nodo deshabilitado dentro de un -FileUpload — que es justo donde vive el composer en su composición documentada. -Fuera de ese envoltorio alcanza (0.4 → 0.123). Los dos defaults son -`--opacity-disabled`, así que la colisión no se ve hasta que un tema mueve uno. +transición para poder medir lo demás; sin congelar, 0,12 s → 4,321 s). + +**`disabled-opacity` ya NO está en esa lista — firma 2026-08-24.** Lo que +había escrito aquí («los dos defaults son `--opacity-disabled`, así que la +colisión no se ve hasta que un tema mueva uno») era una **adjudicación falsa**, +y la re-medida la retiró. Cierto para las dos reglas PROPIAS del composer — +`[data-chat-composer][data-disabled]` y `[data-chat-composer-send][data-disabled]` +empataban a (0,2,0) con el barrido y quedaban en 0.4 de todas formas—; **falso +para el textarea**, que esa cuenta no miró: `[data-textarea]` y +`[data-textarea-input]` **no declaran atenuación propia en esta página**, así +que el barrido de `file-upload` les imponía 0.4 **dos veces más** sobre el 0.4 +legítimo del provider. La opacidad multiplica: **0.4³ = 0.064 de alpha efectiva +en el TEMA POR DEFECTO**, seis veces más apagado de lo diseñado, con el +placeholder casi ilegible. **925 de 25 568 px (3,6 % del composer).** + +Desde la firma el barrido no existe: `file-upload` acota su regla a sus cinco +partes propias (`file-upload.css:200-218`), el composer recupera su +`--chat-composer-disabled-opacity` y el textarea vuelve a 0.4 de alpha. Fuera +de cualquier envoltorio la clave siempre alcanzó (0.4 → 0.123). ## Tokens (`--chat-composer-*`) diff --git a/src/uix/eidos/components/file-upload/file-upload.css b/src/uix/eidos/components/file-upload/file-upload.css index 539e1f385..0a845271d 100644 --- a/src/uix/eidos/components/file-upload/file-upload.css +++ b/src/uix/eidos/components/file-upload/file-upload.css @@ -197,8 +197,22 @@ transition: inline-size var(--file-upload-transition-duration) var(--file-upload-transition-ease); } +/* A component paints what is ITS OWN. The second half used to read + `[data-file-upload] [data-disabled]` — a descendant sweep that dressed every + disabled node under the provider, HOSTS included, and won the (0,2,0) tie by + emission order alone. Measured 2026-08-24 on the documented `chat-composer` + composition: the composer's textarea took this 0.4 twice on top of the + composer's own (0.4³ = 0.064 effective) in the DEFAULT theme, and + `--chat-composer-disabled-opacity` never painted. The five parts below are + exactly the ones the morfo declares with `data-disabled` — the sweep never + reached an own node they do not. Sibling law of firma B′: precedence belongs + in the artefact, not in the emission order. Firma 2026-08-24. */ [data-file-upload][data-disabled], -[data-file-upload] [data-disabled] { +[data-file-upload-label][data-disabled], +[data-file-upload-dropzone][data-disabled], +[data-file-upload-trigger][data-disabled], +[data-file-upload-item-remove][data-disabled], +[data-file-upload-clear-trigger][data-disabled] { cursor: default; opacity: var(--file-upload-disabled-opacity); }