|
|
5 months ago | |
|---|---|---|
| .. | ||
| active-uix | 5 months ago | |
| eidos | 5 months ago | |
| morfo | 5 months ago | |
| sema | 5 months ago | |
| soma | 5 months ago | |
| README.md | 5 months ago | |
| active_architecture.md | 5 months ago | |
| types.ts | 5 months ago | |
README.md
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. 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.mdParte 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.
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
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, futuroVibraChannel) - semántica secuencial estricta del
VisualChannel: escribedata-event-*, mantiene durante elhold, 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
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/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
active-uix (src/uix/active-uix/)
Composition root. Dos modos de boot:
createActiveUix(options)— standalone, instancia las services internamenteattachActiveUix(activeApp)— adjunta a unActiveAppya 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.
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
// 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.applyescribe, Svelte no lo renderiza.partPropssolo emite identidad estática (id, marker, ref). - Los handlers de
eventsson 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. SemayADomson capas hermanas: ninguna depende de la otra.morfo.events.commitses 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
EventEmitterglobal 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
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:
Morfono conoceSomani código de runtimeMorfoRuntimeleeMorfoy depende deSemaProviderno escribe attrs mutables al DOM directamente; los aporta como sources al runtimeSemanticEngineno conoce DOM. Solo registry + dispatchVisualChannelaccede al DOM directamente (no víaADom); el commit estructural posterior va porADomSemayADomson capas hermanasADomno conoceMorfo, niSema, niSoma, niEidosEidosconsume DOM ydata-*, no internals deSomaniSema- Lo que
dom.applyescribe, 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 comodata-archetype="..."porruntime.partProps. - Verbs: action verbs canónicos para
morfo.events[].name. Definida ensrc/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
- morfo/README.md — declaración, archetypes, regla 2-de-3
- sema/README.md —
emitcontract, verbs canónicos, canales - eidos/README.md — qué consume eidos del DOM + wrappers
- soma/SOMA_ARCHITECTURE.md — runtime que transcribe morfo
- soma/COMPONENT_GUIDE.md — guía operativa para crear/migrar componentes
src/arts/adom/README.md—dom.applysrc/docs/sema-implementation-guide.md— convenciones doctrinales del API