# UIX Documento corto de posicionamiento arquitectónico para `src/uix`. > **Visión de conjunto**: para entender las cuatro capas (morfo, soma, sema, > eidos) en una sola lectura, motivaciones y articulación incluidas, ir a > [active_architecture.md](./active_architecture.md). Este README mantiene la > introducción más narrativa. > > **Hand-off de continuación**: el estado actual de migración y los próximos > pasos viven en `CLAUDE.md` (raíz del repo), sección "Session hand-off". > > **Convenciones doctrinales del API** (intent ↔ color, subset por componente, > soma compound vs eidos flat, sound eager-init): viven en > [`src/docs/sema-implementation-guide.md`](../docs/sema-implementation-guide.md) > Parte IV. Autoritativo para todo wrapper / migración nueva. UIX no intenta ser "otra librería de componentes". La apuesta es más ambiciosa y estructural: **separar capas que casi todos los frameworks actuales mantienen mezcladas**. En la mayoría de sistemas de UI, estas cosas viven pegadas: - contrato público del DOM - comportamiento headless - accesibilidad - semántica del evento - capa visual - motores modales (sound, vibra, motion) - integración con servicios de app UIX intenta partir ese bloque en piezas con fronteras fuertes. --- ## 1. La idea central UIX modela la interfaz como varias capas cooperando, no como un único componente gigante que hace todo a la vez. ```text App ├─ servicios transversales (active-app) │ └─ adom, lang, frontend, format, ... └─ componentes └─ capa estructural / semántica / comportamental / visual ``` La intuición: - la estructura pública del componente no es lo mismo que su comportamiento - la semántica de un evento no es lo mismo que su materialización - el DOM activo no es lo mismo que utilidades DOM puras - la app no debería acoplar motores modales entre sí UIX pone nombres y contratos explícitos a esas separaciones. --- ## 2. Las capas de UIX ### `Morfo` (`src/uix/morfo/`) Contrato estructural cross-layer del componente. Define `parts`, `data-*`, ARIA, foco, teclado, eventos. No es prose ni runtime — es la forma canónica pública. Ver: [morfo/README.md](./morfo/README.md) ### `Sema` (`src/uix/sema/`) Vocabulario semántico y orquestación de señales perceptivas. - familias canónicas: `contact`, `commit`, `signal`, `handle`, `emerge`, `shift`, `sustain` - intents canónicos: `neutral`, `affirm`, `fulfill`, `risk`, `threat`, `loss` - `SemanticEngine.emit(signal)` para que el provider publique ocurrencias - registry de canales (`VisualChannel`, `SoundChannel`, futuro `VibraChannel`) - semántica secuencial estricta del `VisualChannel`: escribe `data-event-*`, mantiene durante el `hold`, limpia ANTES de resolver la Promise Sema no decide qué ocurrió (eso lo decide el provider). Solo orquesta la ocurrencia y la despacha a los canales registrados. Ver: [sema/README.md](./sema/README.md) ### `Soma` (`src/uix/soma/`) Capa headless de comportamiento: estado, contexto, a11y, keyboard / pointer / focus, emisión de eventos. Soma no conoce la implementación concreta de los engines modales. Su trabajo es emitir hechos del componente, no materializarlos. Ver: [soma/README.md](./soma/README.md), [soma/SOMA_ARCHITECTURE.md](./soma/SOMA_ARCHITECTURE.md) ### `Eidos` (`src/uix/eidos/`) Capa visual: tokens, themes, recipes CSS por componente, archetype rules, event reactions, **wrappers Svelte** sobre los providers headless de soma con las props visuales (variant, size, block, iconOnly, icon, checkMark). Eidos lee del DOM lo que las otras capas escriben (parts, data-attrs, ARIA, event signals) — nunca importa internals de soma ni de sema. Ver: [eidos/README.md](./eidos/README.md) ### `active-uix` (`src/uix/active-uix/`) Composition root. Dos modos de boot: - `createActiveUix(options)` — standalone, instancia las services internamente - `attachActiveUix(activeApp)` — adjunta a un `ActiveApp` ya compuesto por la aplicación Los componentes consumen `ActiveUix` vía `getActiveUix()` y no saben qué ruta de boot se usó. ### `adom` (`src/arts/adom/`, alias `$adom`) Runtime DOM activo de aplicación. No es semantic engine. Su única función es **coordinar y sincronizar mutaciones DOM** vía `app.dom.apply(change)` y `app.dom.remove(target, names)`. ADom recibe instrucciones ya resueltas. No las interpreta. Ver: [`src/arts/adom/README.md`](../arts/adom/README.md) ### `libs/dom` (`src/libs/dom/`) Utilidades DOM puras o casi puras (`contains`, `getDocument`, `getWindow`, foco, traversal, wrappers base de observers). No contiene runtime activo — ese papel es de `adom`. --- ## 2.bis Cómo se ejecuta un componente La arquitectura cerrada (post-2026-04-25) define seis piezas con responsabilidades disjuntas. Ninguna invade a la siguiente. ``` Morfo declara MorfoRuntime transcribe Provider aporta sources, targets y handlers Effects sincronizan attrs derivados SemanticEngine despacha señales a canales perceptivos VisualChannel materializa la señal en el DOM (data-event*, hold, cleanup) ADom aplica mutaciones DOM (commit estructural) ``` ### El reparto operativo `Morfo` es DNA: un fichero por componente que declara `parts`, `data-*`, `aria-*`, `role`, `keyboard`, `focus`, `events`. No ejecuta nada. `MorfoRuntime` (en `soma/`) interpreta el morfo. Una instancia por componente recibe del provider las fuentes de estado, los targets DOM y los handlers de eventos. Expone `partProps(part)`, `attachPart(part, target)`, `keydown(part, event)`, `trigger(eventName)`. `Provider` aporta lo que el morfo no puede inferir: getters reactivos para `states` y `props`, getters reactivos para `parts` (ids dinámicos), handlers síncronos para los `events`, glue de layers ortogonales (Presence, Dismissal, ScrollLock — no son morfo). `Effects` (registrados por el runtime al montar) escuchan cambios en los sources y aplican los attrs derivados vía `dom.apply`. `SemanticEngine` recibe la ocurrencia desde `runtime.trigger`. Genera el id, despacha la señal a TODOS los canales registrados. El `VisualChannel` (built-in) escribe `data-event-*` directamente en el DOM, mantiene los attrs durante el `hold` configurado, los limpia y solo entonces resuelve la Promise (semántica secuencial estricta). Los canales no-visuales (sound, vibra) son fire-and-forget. `ADom` solo aplica el commit estructural posterior. No interpreta. Sema y ADom son capas hermanas: el engine ya no depende de ADom. ### La secuencia de `runtime.trigger(eventName)` ``` 1. prewrite imperativo (transient markers como data-last-action) 2. await semantic.emit(event) 3. handler síncrono del provider muta state 4. effects derivan y aplican attrs estructurales (data-state, aria-*) ``` El handler muta state. Los effects ven el cambio y reescriben el DOM. ADom es el único escritor de attrs mutables. ### Tres escenarios de Soma ```ts // Cambio estructural sin señal provider.commitState(change) // internamente: dom.apply(change) // Cambio estructural con señal provider.commitState(change, event) // internamente: await semantic.emit(event); dom.apply(change) // Señal sin cambio estructural provider.emitEvent(event) // internamente: void semantic.emit(event) ``` ### Reglas operativas - Lo que `dom.apply` escribe, Svelte no lo renderiza. `partProps` solo emite identidad estática (id, marker, ref). - Los handlers de `events` son síncronos. Async va fuera del trigger. - Los guards (`if (disabled) return`) van en el call-site, no dentro del handler — si entran al handler, ya emitieron señal perceptiva. - `Sema` y `ADom` son capas hermanas: ninguna depende de la otra. - `morfo.events.commits` es descriptivo: documenta lo observable, no lo ejecuta. La cadena causal real es handler → state → effect. --- ## 3. Qué hace distinto a UIX ### 3.1 El contrato estructural es una capa propia En la mayoría de librerías la estructura pública del componente está dispersa: - atributos en el provider - roles en el render - partes en CSS - selector names en docs - contratos en tests UIX concentra eso en `Morfo`. Eso permite: - docs derivadas del contrato - validación cross-layer (linter de eidos) - menos drift entre headless y visual - tooling más fiable ### 3.2 La semántica no se mezcla con la ejecución UIX separa el **nombre de la ocurrencia** de su **materialización modal**. Sonido, vibración, CSS y motores futuros usan el mismo vocabulario sin quedar pegados entre sí. ### 3.3 El comportamiento headless no carga con toda la modalidad `Soma` no es un mega-engine que sabe de todo. No reproduce WAVs, no vibra, no decide la física perceptiva de cada canal. Soma emite. Los canales ejecutan. ### 3.4 El DOM activo es infraestructura de app UIX reconoce que hay hechos transversales del DOM que varios consumidores quieren escuchar, y por eso introduce `adom` como servicio de app. ### 3.5 La app compone servicios, no "super componentes" `active-app` permite que la aplicación componga `lang`, `frontend`, `dom`, `format`, `semantic` y los componentes los consuman vía `getActiveUix()`. --- ## 4. Lo que UIX no es UIX no es: - una colección plana de componentes visuales - un simple wrapper opinionated sobre primitives existentes - un design system clásico donde visual, comportamiento y contratos viven juntos - un semantic engine centralizado que ejecuta todas las modalidades - un `EventEmitter` global disfrazado de arquitectura La originalidad de UIX no está en inventar nombres exóticos, sino en **separar problemas reales** que otros sistemas suelen aceptar como un único bloque. --- ## 5. Reglas de dependencia ```text Morfo → declara contratos MorfoRuntime → interpreta morfo dentro de Soma Provider → aporta sources, targets, handlers Effects → sincronizan state → attrs SemanticEngine → registry + dispatch de señales a canales VisualChannel → materializa la señal en el DOM (data-event*, hold, cleanup) ADom → aplica mutaciones DOM (commit estructural) Eidos → materializa visualmente leyendo DOM App → compone servicios ``` Reglas duras: - `Morfo` no conoce `Soma` ni código de runtime - `MorfoRuntime` lee `Morfo` y depende de `Sema` - `Provider` no escribe attrs mutables al DOM directamente; los aporta como sources al runtime - `SemanticEngine` no conoce DOM. Solo registry + dispatch - `VisualChannel` accede al DOM directamente (no vía `ADom`); el commit estructural posterior va por `ADom` - `Sema` y `ADom` son capas hermanas - `ADom` no conoce `Morfo`, ni `Sema`, ni `Soma`, ni `Eidos` - `Eidos` consume DOM y `data-*`, no internals de `Soma` ni `Sema` - Lo que `dom.apply` escribe, Svelte no lo renderiza ### Regla 2-de-3 para extender Morfo Una extensión a Morfo solo se justifica si **al menos dos de las tres capas** (soma, sema, eidos) la consumen. Si solo soma se beneficia, el patrón es virtual prop en provider — no extender el contrato. | Extensión | Soma | Sema | Eidos | En morfo | |---|---|---|---|---| | `parts[].archetype` | ✅ emit | ✅ verbs por rol | ✅ selectores transversales | ✅ | | `events[].semantic` | ✅ payload | ✅ vocabulario | ✅ tinta | ✅ | | `events[].prewrite` | ✅ ejecuta | ✅ secuencia | ✅ tinta exit | ✅ | | `firstOf` value source | ✅ only | — | — | ❌ | | `prop-not-nullish` cond | ✅ only | — | — | ❌ | | Field-context OR | — virtual prop | — | — | ❌ | ### Vocabularios canónicos cross-layer - **Archetypes**: clasificación de partes cross-component. Definida en `src/uix/morfo/types.ts:ARCHETYPE_VOCABULARY`. Emitida como `data-archetype="..."` por `runtime.partProps`. - **Verbs**: action verbs canónicos para `morfo.events[].name`. Definida en `src/uix/sema/verbs.ts:SEMA_VERBS`. Convención para composite names: `{verb}-{variant}` (e.g. `commit-save`, `dismiss-outside`). --- ## 6. La diferencia en una frase > Morfo declara, MorfoRuntime transcribe, Provider aporta, Effects sincronizan, > Sema emite, ADom aplica. Seis piezas, seis responsabilidades, ninguna invade a la siguiente. --- ## 7. Orden de lectura sugerido 1. [morfo/README.md](./morfo/README.md) — declaración, archetypes, regla 2-de-3 2. [sema/README.md](./sema/README.md) — `emit` contract, verbs canónicos, canales 3. [eidos/README.md](./eidos/README.md) — qué consume eidos del DOM + wrappers 4. [soma/SOMA_ARCHITECTURE.md](./soma/SOMA_ARCHITECTURE.md) — runtime que transcribe morfo 5. [soma/COMPONENT_GUIDE.md](./soma/COMPONENT_GUIDE.md) — guía operativa para crear/migrar componentes 6. [`src/arts/adom/README.md`](../arts/adom/README.md) — `dom.apply` 7. [`src/docs/sema-implementation-guide.md`](../docs/sema-implementation-guide.md) — convenciones doctrinales del API