feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
---
title: Next features — initiative registry
type: registry
audience: human + agent
authority: backlog of user-decided future initiatives — records scope, sequencing and dependencies; it does NOT record verdicts (those live in the RFCs / changelog / book-deviations when executed)
status: living — items enter by explicit user decision during audits and sessions; remove an item when it ships (and link where it landed)
created: 2026-07-06
---
# Next features — initiative registry
Rules of this file: one entry per initiative, dated, with its origin (which
audit/session raised it), its scope in one paragraph, and its dependencies.
Entries are **registered, not started** — starting one is a session decision.
Detail lives in the linked doc, never copied here.
color: contraste Stage 2 — contrato §40 como datos + guard CI; solver descartado
La ejecución refutó la premisa del plan: el morph HEREDA el contraste del texto
(steps 11/12 = curva-L del donante verbatim; ~invariante al croma bajo
gamut-mapping), así que toda escala desde donantes §40-compliant cumple por
construcción. Medido en 3 bancos (base, leave-one-out, 45 semillas OOD): 0 fallos
del gate duro text-strong·12, min WCAG 9.7:1. El solver de luminancia no tenía
nada que resolver → descartado por especulativo. D2 = base verbatim (sin
migración base→seeds).
- $color: CONTRAST_PAIRS (arts/color/contrast-contract.ts) — tabla §40 como datos
compartidos: floor duro text-strong·12, banda text·11 (tope relacional, sin nº
mágico), border·7 exento
- scripts/contrast-audit.ts: muere la PAIRS pre-veredicto (4.5 duro contra §40),
consume CONTRAST_PAIRS + banco de regresión morph-generado
- eidos/lib/contrast-invariant.test.ts: guard de CI (4 tests) que bloquea la herencia
- docs: reference.md §40, next-features.md §1, changelog.md §45, plan (OUTCOME)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
## 1. Tonal-ramp contrast parity (measure, then construct) — 2026-07-06 · Stage 1 DONE 2026-07-19 · Stage 2 CLOSED 2026-07-20
> **Stage 1 SHIPPED (2026-07-19).** Measured with `scripts/contrast-audit.ts`
> (33 scales × 2 modes); drift + user verdicts ratified as doctrine in
> [`theming/reference.md §40`](./theming/reference.md) + [`changelog.md §44`](./theming/changelog.md).
> Outcome: `text·11` = secondary tier, `text-strong` = AA-guaranteed; the border
> vocabulary is Radix-subtle by design (only `solid·9` clears 3:1 by
> construction) with a decorative-vs-load-bearing split; the focus ring is a
> config axis (`primitives.focusRing` + `color.focus`, single `outline` per §32)
> whose shipped default is soft — hardening is a config-value choice, kept as-is.
color: contraste Stage 2 — contrato §40 como datos + guard CI; solver descartado
La ejecución refutó la premisa del plan: el morph HEREDA el contraste del texto
(steps 11/12 = curva-L del donante verbatim; ~invariante al croma bajo
gamut-mapping), así que toda escala desde donantes §40-compliant cumple por
construcción. Medido en 3 bancos (base, leave-one-out, 45 semillas OOD): 0 fallos
del gate duro text-strong·12, min WCAG 9.7:1. El solver de luminancia no tenía
nada que resolver → descartado por especulativo. D2 = base verbatim (sin
migración base→seeds).
- $color: CONTRAST_PAIRS (arts/color/contrast-contract.ts) — tabla §40 como datos
compartidos: floor duro text-strong·12, banda text·11 (tope relacional, sin nº
mágico), border·7 exento
- scripts/contrast-audit.ts: muere la PAIRS pre-veredicto (4.5 duro contra §40),
consume CONTRAST_PAIRS + banco de regresión morph-generado
- eidos/lib/contrast-invariant.test.ts: guard de CI (4 tests) que bloquea la herencia
- docs: reference.md §40, next-features.md §1, changelog.md §45, plan (OUTCOME)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
> **Stage 2 CLOSED (2026-07-20) — no solver needed.** The 6 decisions were
> locked (D1 band · D2 measured-pass · D3 flip-only · D4 opt-in · D5 runtime-first
> · D6 now), then **execution disproved the premise**: the template morph does not
> *compute* text contrast, it **inherits** it (steps 11/12 copy the donor's L-curve
> verbatim; L-driven contrast is ~chroma-invariant under gamut-mapping), so any
> scale generated from a §40-compliant donor library clears the ratified text
> floors **by construction**. Verified on three banks — authored base, leave-one-out
> regeneration, and 45 out-of-distribution seeds — **0 hard-gate failures**
> (min WCAG 9.7:1). The luminance solver would have had nothing to resolve for
> realistic inputs → dropped as speculative. **Delivered instead:** the ratified
> pair table as shared data (`$color` → `CONTRAST_PAIRS`), the audit refactored to
> consume it + a morph-generated regression bank (`scripts/contrast-audit.ts`), and
> a CI guard that locks the inheritance (`eidos/lib/contrast-invariant.test.ts` —
> fails if a future donor/generator breaks it). With D2, the base stays verbatim
> ground-truth; **no base→seeds migration**.
> **Plan + decision + outcome record:**
> [`process/contrast-stage2-plan-2026-07.md`](./process/contrast-stage2-plan-2026-07.md);
> chronicle [`changelog.md §45`](./theming/changelog.md).
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
**Origin**: A.7 follow-up (theming audit F). The on-solid pair is now computed
at generation with a single criterion ([`rfcs/rfc-color-engine.md`](./rfcs/rfc-color-engine.md)
§8, `eidos/lib/on-solid.ts` ). M3 still guarantees more: contrast **by
construction across its whole tonal ramp** (container/on-container pairs at
fixed tone deltas — ΔT 40 ≈ 3:1, ΔT 50 ≈ 4.5:1). Our equivalent promises
(text-11 on bg-2/element-3, border-7 vs bg-2 at the 3:1 non-text floor, text
over composited soft surfaces a2/a3) rest on Radix-style curation, unmeasured.
**Stage 1 — pair-contract verification** (the A.7 pattern generalized): a
canonical table of slot pairs + floors (a user-decided doctrine session),
a measurement pass over pairs × 33 scales × 2 modes at generation
(`on-solid.ts` generalizes; the compositing math exists in `$color` ), and the
resulting drift list goes to user verdicts (retune vs annotated exception).
Infrastructure is small; the fallout is the unknown.
color: contraste Stage 2 — contrato §40 como datos + guard CI; solver descartado
La ejecución refutó la premisa del plan: el morph HEREDA el contraste del texto
(steps 11/12 = curva-L del donante verbatim; ~invariante al croma bajo
gamut-mapping), así que toda escala desde donantes §40-compliant cumple por
construcción. Medido en 3 bancos (base, leave-one-out, 45 semillas OOD): 0 fallos
del gate duro text-strong·12, min WCAG 9.7:1. El solver de luminancia no tenía
nada que resolver → descartado por especulativo. D2 = base verbatim (sin
migración base→seeds).
- $color: CONTRAST_PAIRS (arts/color/contrast-contract.ts) — tabla §40 como datos
compartidos: floor duro text-strong·12, banda text·11 (tope relacional, sin nº
mágico), border·7 exento
- scripts/contrast-audit.ts: muere la PAIRS pre-veredicto (4.5 duro contra §40),
consume CONTRAST_PAIRS + banco de regresión morph-generado
- eidos/lib/contrast-invariant.test.ts: guard de CI (4 tests) que bloquea la herencia
- docs: reference.md §40, next-features.md §1, changelog.md §45, plan (OUTCOME)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
**Stage 2 — by-construction generator (CLOSED 2026-07-20, solver dropped).** The
plan proposed a luminance solver that resolves each step to satisfy Stage 1's
pair table for a seed. Execution disproved its premise: the morph **inherits**
text contrast from the donor (steps 11/12 copy the donor's L-curve verbatim;
L-driven contrast is ~chroma-invariant under gamut-mapping), so every scale from
a §40-compliant donor library clears the ratified floors by construction. Measured
across three banks (authored base, leave-one-out regeneration, 45 out-of-distribution
seeds): **0 hard-gate failures, min WCAG 9.7:1.** The solver had nothing to
resolve for realistic inputs → dropped (speculative). What shipped: the pair
table as shared data (`$color` `CONTRAST_PAIRS` ), the audit consuming it + a
morph-generated regression bank, and a CI guard (`eidos/lib/contrast-invariant.test.ts`)
that locks the inheritance. The BASE stays verbatim ground-truth (D2); no
base→seeds migration.
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
**Sequence**: Stage 1 first (it is the spec for Stage 2). No M3-style `on-*`
vocabulary expansion — our slots already encode the pairs; the table
formalizes them without inflating the namespace.
## 2. Per-theme palette-contrast cascade emission — 2026-07-06
**Origin**: A.7 execution. The per-instance `palette-contrast` cascade lives
in the STATIC foundation — one polarity for all themes. Today polarity is
computed per configured theme and unanimous (33/33 scales agree across
base-light/base-dark); on disagreement the `*-light` theme wins
(`computeLightSolidScales`, documented in
[`rfcs/rfc-color-engine.md` ](./rfcs/rfc-color-engine.md ) §8). **Trigger** : the
day a real configured theme disagrees on a scale's polarity, emit the cascade
per-theme instead of statically. Not before — no speculative machinery.
## 3. Component audit (post-system) — 2026-07-06
**Origin**: user decision during the theming audit — "de momento no tocamos
los componentes, merecen una auditoría adicional una vez que se haya resuelto
estas". Gate: the system-level audit (F/G) closes first. Registered items so
far, to seed its scope:
- **Named-styles adoption by controls** (from A.3): controls/recipes that
hand-declare typography instead of consuming `--style-*` /
size-bundle typography — likely by error, to be confirmed per component.
- **Letter-spacing bundle adoption**: ~34 recipes consume `font-size` /
`line-height` from the size bundle but not `letter-spacing` ; plus ~7 raw
letter-spacing literals in component CSS.
- **Tier-A `data-depth` stamping coverage**: overlays that should stamp their
plane (dialog / drawer / tooltip / menubar / combobox) per
[`rfcs/rfc-depth.md` ](./rfcs/rfc-depth.md ) §5.
- **Calendar family → shared layer/archetype** (user design verdict,
2026-07-07): the calendar-grid vocabulary (accent set, day geometry, the
holiday/event marks fixed in audit B.2) becomes a FIRST-CLASS shared layer
— today the family borrows it informally (measured: range-calendar
consumes 79 `--calendar-*` tokens, month-grid 65, year-grid 65,
date-range-picker 38, date-picker 4 — ~250 cross-component borrows), and
**chronos will draw from it too** when its track lands. Materialization to
decide at execution: a morfo part-archetype for the day cell (the 26-set
grows) and/or a `calendar-surface` shared recipe à la list-surface
(`--list-item-*` precedent). This supersedes "per-component composition
repairs" for the B.2 borrowers: the marks reach the whole family through
the shared layer instead.
- **Field family composition — segments under `[data-field]` ** (user design
verdict, 2026-07-07, audit B.1): segment height derives from the SIZE axis
at the FIELD family level — `field` recipe declares `segment-height` with
size-scoped declarations (`calc(var(--field-control-height-{k}) -
var(--space-2))`; the TSC `size:` scope axis exists, unused so far), and
the per-component copies die (each x-field today re-declares the whole
`height-{xs..xl}` family the field recipe already has). **MANDATED by the
B.1-(i) verdict**: date/time/color-field MUST incorporate the Field
wrapper (segments under `[data-field][data-size]` — probed live: today
they don't) as part of the component audit; the three broken
per-component `segment-height` tokens and their consumers were removed
(they had computed `auto` since birth — zero visual change).
- Whatever F/G registers as "component-level adoption gap" in
`docs/audit/theming-audit.md` when that report lands (deliverable G of the
2026-07 theming audit).
## 4. `architecture/sema.md` hold-sync — 2026-07-06
**Origin**: A.4/A.5 execution (holds single-source). `SEMA_HOLDS_BY_INTENT`
(`src/uix/sema/holds.ts`) is now THE only hold table (resolver + visual
channel consume it; `sema-map.ts` 's per-family hold column died). The chapter
[`architecture/sema.md` ](./architecture/sema.md ) still narrates per-family
holds in ~34 places. **Blocked** : the file is the author's WIP (uncommitted
edits) — sync it when the author's pass lands, not before.
## 6. THM-2 rollout — per-instance palette to all surfaces — SHIPPED 2026-07-12; scope boundaries REVERTED by the open-color-cage initiative (2026-07-18/19)
Shipped in `05f0031c` (media-player Batch-4 + 14 surfaces + vocab + 4 props)
and `cc7e9a16` (avatar root). Every per-component palette cascade now collapses
into the shared `--palette-*` layer (size win, behavior-preserving for roles);
the vocabulary gained the alpha `surface` /`surface-hover` + `hover` slots.
> **REVERSED (2026-07-18/19) — "abrir la jaula del color".** THM-2 originally
> kept semantic controls (checkbox/radio/stepper/editable/select/file-upload)
> and `Avatar.Badge` role-restricted "by design". A later user decision reverses
> that: `color` accepts the FULL system (role / intent / 33 scales / raw CSS =
> `ComponentColorProp`) on **every** component, no exceptions — content-ink
> primitives keep their axis as an additive union. Enforced by two guards in
> `recipe-css-contract.test.ts`. Full record + the closed queue (incl. the
> chart SVG resolver and the Card/Avatar custom-color fix):
> [`process/open-color-cage-2026-07.md`](./process/open-color-cage-2026-07.md).
> Only `qr-code` stays out (its `color` is the QR module ink, not a scale).
feat(theming+soma): media-player Batch-4 (SOM-4) + THM-2 shared-palette rollout to 14 surfaces
media-player Batch-4 (closes SOM-4): the 10 sub-parts migrate to
renderProps() (35 attr-dup hits -> 0). Register the sources the morfo
reads (label propRef, on stateRef, direction/type) so renderProps emits
type/role/aria-*/data-state; keep only contract-only soma-owned attrs.
Pin the soma-attr-audit ceiling to 0.
THM-2 rollout - collapse each per-component palette cascade into the
shared per-instance layer (size win, behavior-preserving for roles,
adds the 33 scales):
- Rename palette tokens to canonical _palette-{slot} + route via the
presence-guarded forward: tags-input, stepper, badge, card, select
(parts-aware trigger+content), file-upload, editable, tag-group.
- Extend the shared vocabulary with alpha surface(a2)/surface-hover(a3)
slots (soft variant of variant-aware recipes) and hover(4) - the
latter also fixes Toggle's latent scale-hover.
- tag-group: variant-aware migration (solid/outline -> canonical,
soft -> alpha surface; merge outline-fg into text, prune orphans).
- toggle-group: replace the 4-role cross-recipe composition with a
hand-authored presence-scoped route to the shared layer (the
composition DSL can't presence-scope); covers roles + 33 scales.
- Widen color to ComponentColor (roles + 33 scales) where arbitrary
color is meaningful: badge, tag-group, tags-input, toggle-group, and
add the scale picker to their demos. Semantic controls stay restricted.
Verified end-to-end with the REAL components (drive the demo color
control -> the component emits data-color -> the shared layer resolves
the exact scale). Gates: recipe-css-contract/config/contracts/
soma-attr-audit/media-player suites green; 0 orphan tokens.
avatar remains for a focused pass: its variant-resolved matrix and badge
sub-part don't fit a mechanical migration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
## 5. Book editorial note — "el hold es suelo, no tijera" — 2026-07-06
**Origin**: A.4/A.5 verdict. The mechanism (hold = floor for the expression,
never a scissor; expression waits animation-finish after the hold, capped by
an absolute `MAX_EXPRESSION_WAIT_MS` , never derived from the hold) is recorded
in [`decisions/book-deviations.md` ](./decisions/book-deviations.md ) D.12 as a
candidate **editorial appendix** for the book (*Diseñando lo que ocurre*,
HOMOGENEIZADO edition) — the book gives qualitative regions and this is the
materialization doctrine worth feeding back to the source.
uix(chat): bloque de mensajería chat-* v1 (log · message · composer · typing)
Familia chat-* completa, componiendo el ecosistema (Feed + VirtualList
anclado, Textarea, FileUpload, Avatar, Popover, Icon). Transporte-agnóstica:
el modelo de datos es de la app, los componentes reciben props + snippets.
- chat-log: Feed + VirtualList(anchor:'end') en triple registro; day
separators, unread divider, filas de sistema (Feed.Article), pill "N
nuevos" + notifyIncoming (announce a live-region), to-latest distance-aware.
- chat-message: raíz Feed.Article; runs ($libs/chat), ticks de entrega,
reply embebido, reacciones (chip pressed + tapback panel), acciones hover.
- chat-composer: Textarea (doble registro) + FileUpload; teclado
Enter/Shift+Enter/Esc/ArrowUp; typing saliente debounced (uix.timers);
barra reply/edit.
- chat-typing: sustain-streaming (visual-only, loop del sistema);
aggregateTypers 1/2/3+ con plantillas plurales.
- $libs/chat: groupIntoRuns (ventana 8 min estilo Discord) + aggregateTypers.
- VirtualList: extensión anchor:'end' (followOutput, prepend por clave
estable, sticky monótono, onAtEndChange) — sin scroller paralelo.
Diseño de referencia (WhatsApp/Telegram, pase lado-a-lado): burbujas suaves
(out = primary-element + texto normal; in = surface card + sombra) sobre
lienzo muted del log, meta dentro de la burbuja, tapback bar horizontal,
iconos lucide del ecosistema (nada de SVG a mano). Contrato de tamaños:
size="md" = 16px canónico.
Fixes de framework (afectaban más allá de chat):
- EngineLangs.t() ahora pela el prefijo '#?': los idlangrefs imperativos
(pagination/file-upload/chronos/chat) caían al fallback EN en locale es.
Fix parseLangRef ?? parsePathFallback + test de regresión (132/132).
- virtual-list commit-set-resize con channels:[]: es bookkeeping de
ResizeObserver, no gesto → dejaba de emitir beep/vibrate pre-interacción
(interventions de navigator.vibrate / AudioContext).
Escaparate messenger en /uix/demos/chat (bot de eco con uix.timers;
variante $connection señalada). Gaps v2 registrados en next-features §7.
Pase: component:audit 4×PASS · eidos-lint invalid=0 · recipe-contract
29/29 · Playwright escaparate PASS end-to-end · consola limpia ·
npm run check en baseline (76, cero errores nuevos).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
## 7. Chat block v2 — 2026-07-18
**Origin**: the `chat-*` v1 initiative (comparative study + plan, session
2026-07-18; user scoped v1 to the room core). v1 shipped `chat-log`
(Feed + end-anchored VirtualList), `chat-message` , `chat-composer`
(Textarea + FileUpload composition) and `chat-typing` , plus the
`$libs/chat` helpers and the VirtualList `anchor: 'end'` extension. The
deferred surface, registered with disposition (each item also lives in its
component README §Gaps):
uix(chat): v2 · chat-list — panel de conversaciones (compone Listbox)
Componente nuevo del bloque chat v2: el panel de lista de salas/DMs.
Compone el Listbox del ecosistema (doble registro id/ref compartido):
selección única + roving-tabindex + typeahead + role=option vienen de
Listbox; chat-list aporta la disposición de fila (avatar · título/preview ·
meta) y el estado visual data-unread (negrita + Badge compuesto).
- morfo: expression 'delegated', sin eventos propios (D.1 — el actor es el
Listbox compuesto); parts provider/item(data-unread)/avatar/title/preview/meta.
- soma: ChatListProvider crea ListboxProvider dentro (value string <-> string[]);
ChatListItemProvider per-instance (unread = boolean crudo); 4 clases de
contenido explicitas.
- eidos: rejilla avatar | (titulo/preview) | meta, avatar y meta abarcan las
dos filas; truncado; unread -> titulo en negrita. No neutraliza el arquetipo
item (la conversacion ES una opcion seleccionable). Sin @keyframes; ejes
logicos.
- demo: 5 salas, seleccion limpia unread, typing-en-lista (compone ChatTyping),
Badge de no-leidos; tabs live/system/api/morfo/a11y.
- READMEs dobles (eidos con Passive justification, delegated); next-features 7
marca chat-list SHIPPED.
Verificacion: check baseline 76 (0 nuevos) · component:audit PASS ·
eidos-lint invalid=0 · recipe-contract 30/30 · morfo:vocabulary 0 · Playwright
PASS (role=listbox, 5 salas, aria-selected, unread=2, badges=2, typing-en-lista,
click + teclado seleccionan).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
- ~~**`chat-list`** — the conversation-list pane (rooms, unread badges,
typing-in-list).~~ ** ✅ SHIPPED 2026-07-19** — composes `Listbox`
(single-select + keyboard) with an avatar/title/preview/meta row; unread
bold + `Badge` , typing-in-list composes `ChatTyping` . Virtualization for
very long lists deferred (pairs with the VirtualList bidirectional item).
uix(chat): bloque de mensajería chat-* v1 (log · message · composer · typing)
Familia chat-* completa, componiendo el ecosistema (Feed + VirtualList
anclado, Textarea, FileUpload, Avatar, Popover, Icon). Transporte-agnóstica:
el modelo de datos es de la app, los componentes reciben props + snippets.
- chat-log: Feed + VirtualList(anchor:'end') en triple registro; day
separators, unread divider, filas de sistema (Feed.Article), pill "N
nuevos" + notifyIncoming (announce a live-region), to-latest distance-aware.
- chat-message: raíz Feed.Article; runs ($libs/chat), ticks de entrega,
reply embebido, reacciones (chip pressed + tapback panel), acciones hover.
- chat-composer: Textarea (doble registro) + FileUpload; teclado
Enter/Shift+Enter/Esc/ArrowUp; typing saliente debounced (uix.timers);
barra reply/edit.
- chat-typing: sustain-streaming (visual-only, loop del sistema);
aggregateTypers 1/2/3+ con plantillas plurales.
- $libs/chat: groupIntoRuns (ventana 8 min estilo Discord) + aggregateTypers.
- VirtualList: extensión anchor:'end' (followOutput, prepend por clave
estable, sticky monótono, onAtEndChange) — sin scroller paralelo.
Diseño de referencia (WhatsApp/Telegram, pase lado-a-lado): burbujas suaves
(out = primary-element + texto normal; in = surface card + sombra) sobre
lienzo muted del log, meta dentro de la burbuja, tapback bar horizontal,
iconos lucide del ecosistema (nada de SVG a mano). Contrato de tamaños:
size="md" = 16px canónico.
Fixes de framework (afectaban más allá de chat):
- EngineLangs.t() ahora pela el prefijo '#?': los idlangrefs imperativos
(pagination/file-upload/chronos/chat) caían al fallback EN en locale es.
Fix parseLangRef ?? parsePathFallback + test de regresión (132/132).
- virtual-list commit-set-resize con channels:[]: es bookkeeping de
ResizeObserver, no gesto → dejaba de emitir beep/vibrate pre-interacción
(interventions de navigator.vibrate / AudioContext).
Escaparate messenger en /uix/demos/chat (bot de eco con uix.timers;
variante $connection señalada). Gaps v2 registrados en next-features §7.
Pase: component:audit 4×PASS · eidos-lint invalid=0 · recipe-contract
29/29 · Playwright escaparate PASS end-to-end · consola limpia ·
npm run check en baseline (76, cero errores nuevos).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
- **`emoji-picker`** — a real picker component (color-picker scale:
categories, search, skin tones). v1 ships the quick-set Popover in
`ChatMessage.ReactionAdd` .
- **Mention autocomplete (`@`)** — combobox-in-textarea for
`chat-composer` ; pairs with the future `words` /`palabras` integration.
- **Threads UI** — the side-panel composition over the already-shipped
`Feed.Thread` semantics.
- ~~**Jump-to-message highlight** — `signal-emphasize-target` on
`chat-message` (the morfo event was deliberately left out of v1).~~
** ✅ SHIPPED 2026-07-19** — `emphasized` prop → `data-emphasized` row flash
(visual-only, no sema event: reference apps flash silently; the app scrolls
via a reply's `onJump` then toggles `emphasized` for ~1.4s).
- ~~**Read-by list** — "seen by N" avatar row (Teams/Messenger pattern) on
`chat-message` .~~ ** ✅ SHIPPED 2026-07-19** — `ChatMessage.ReadBy` part
(the app composes `AvatarGroup` ; the sender's `onMessageSeen` feeds it).
uix(chat): bloque de mensajería chat-* v1 (log · message · composer · typing)
Familia chat-* completa, componiendo el ecosistema (Feed + VirtualList
anclado, Textarea, FileUpload, Avatar, Popover, Icon). Transporte-agnóstica:
el modelo de datos es de la app, los componentes reciben props + snippets.
- chat-log: Feed + VirtualList(anchor:'end') en triple registro; day
separators, unread divider, filas de sistema (Feed.Article), pill "N
nuevos" + notifyIncoming (announce a live-region), to-latest distance-aware.
- chat-message: raíz Feed.Article; runs ($libs/chat), ticks de entrega,
reply embebido, reacciones (chip pressed + tapback panel), acciones hover.
- chat-composer: Textarea (doble registro) + FileUpload; teclado
Enter/Shift+Enter/Esc/ArrowUp; typing saliente debounced (uix.timers);
barra reply/edit.
- chat-typing: sustain-streaming (visual-only, loop del sistema);
aggregateTypers 1/2/3+ con plantillas plurales.
- $libs/chat: groupIntoRuns (ventana 8 min estilo Discord) + aggregateTypers.
- VirtualList: extensión anchor:'end' (followOutput, prepend por clave
estable, sticky monótono, onAtEndChange) — sin scroller paralelo.
Diseño de referencia (WhatsApp/Telegram, pase lado-a-lado): burbujas suaves
(out = primary-element + texto normal; in = surface card + sombra) sobre
lienzo muted del log, meta dentro de la burbuja, tapback bar horizontal,
iconos lucide del ecosistema (nada de SVG a mano). Contrato de tamaños:
size="md" = 16px canónico.
Fixes de framework (afectaban más allá de chat):
- EngineLangs.t() ahora pela el prefijo '#?': los idlangrefs imperativos
(pagination/file-upload/chronos/chat) caían al fallback EN en locale es.
Fix parseLangRef ?? parsePathFallback + test de regresión (132/132).
- virtual-list commit-set-resize con channels:[]: es bookkeeping de
ResizeObserver, no gesto → dejaba de emitir beep/vibrate pre-interacción
(interventions de navigator.vibrate / AudioContext).
Escaparate messenger en /uix/demos/chat (bot de eco con uix.timers;
variante $connection señalada). Gaps v2 registrados en next-features §7.
Pase: component:audit 4×PASS · eidos-lint invalid=0 · recipe-contract
29/29 · Playwright escaparate PASS end-to-end · consola limpia ·
npm run check en baseline (76, cero errores nuevos).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
- **VirtualList chat-mode refinements** — bidirectional sparse ranges for
jump-into-history far from the loaded window.
- ~~**Typing avatars** — faces next to the `chat-typing` dots (Messenger).~~
** ✅ SHIPPED 2026-07-19** — `ChatTyping.Avatars` slot (compose `AvatarGroup` ).
uix(chat): bloque de mensajería chat-* v1 (log · message · composer · typing)
Familia chat-* completa, componiendo el ecosistema (Feed + VirtualList
anclado, Textarea, FileUpload, Avatar, Popover, Icon). Transporte-agnóstica:
el modelo de datos es de la app, los componentes reciben props + snippets.
- chat-log: Feed + VirtualList(anchor:'end') en triple registro; day
separators, unread divider, filas de sistema (Feed.Article), pill "N
nuevos" + notifyIncoming (announce a live-region), to-latest distance-aware.
- chat-message: raíz Feed.Article; runs ($libs/chat), ticks de entrega,
reply embebido, reacciones (chip pressed + tapback panel), acciones hover.
- chat-composer: Textarea (doble registro) + FileUpload; teclado
Enter/Shift+Enter/Esc/ArrowUp; typing saliente debounced (uix.timers);
barra reply/edit.
- chat-typing: sustain-streaming (visual-only, loop del sistema);
aggregateTypers 1/2/3+ con plantillas plurales.
- $libs/chat: groupIntoRuns (ventana 8 min estilo Discord) + aggregateTypers.
- VirtualList: extensión anchor:'end' (followOutput, prepend por clave
estable, sticky monótono, onAtEndChange) — sin scroller paralelo.
Diseño de referencia (WhatsApp/Telegram, pase lado-a-lado): burbujas suaves
(out = primary-element + texto normal; in = surface card + sombra) sobre
lienzo muted del log, meta dentro de la burbuja, tapback bar horizontal,
iconos lucide del ecosistema (nada de SVG a mano). Contrato de tamaños:
size="md" = 16px canónico.
Fixes de framework (afectaban más allá de chat):
- EngineLangs.t() ahora pela el prefijo '#?': los idlangrefs imperativos
(pagination/file-upload/chronos/chat) caían al fallback EN en locale es.
Fix parseLangRef ?? parsePathFallback + test de regresión (132/132).
- virtual-list commit-set-resize con channels:[]: es bookkeeping de
ResizeObserver, no gesto → dejaba de emitir beep/vibrate pre-interacción
(interventions de navigator.vibrate / AudioContext).
Escaparate messenger en /uix/demos/chat (bot de eco con uix.timers;
variante $connection señalada). Gaps v2 registrados en next-features §7.
Pase: component:audit 4×PASS · eidos-lint invalid=0 · recipe-contract
29/29 · Playwright escaparate PASS end-to-end · consola limpia ·
npm run check en baseline (76, cero errores nuevos).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
- **Delegate-family ↔ agent chat** — the Aura reservation (§F6 of the
scene/pack initiative) once agent conversations land.
**Deps**: none to start `chat-list` /`emoji-picker`; mentions depend on the
`words` integration decision. The transport stays app-land (`$connection`).