|
|
4 months ago | |
|---|---|---|
| .. | ||
| active-uix | 4 months ago | |
| eidos | 4 months ago | |
| langs | 4 months ago | |
| morfo | 4 months ago | |
| sema | 4 months ago | |
| soma | 4 months ago | |
| words | 4 months ago | |
| AUDIT_REPORT_2026-05-27.md | 5 months ago | |
| COMPONENT_COMPLETION_CHECKLIST.md | 5 months ago | |
| PENDIENTES.md | 5 months ago | |
| README.md | 5 months ago | |
| active_architecture.md | 4 months ago | |
| contracts.test.ts | 4 months ago | |
| contracts.ts | 4 months ago | |
| intent.ts | 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
continue.md. Handoff 2026-05-14: pausa deliberada antes de seguir programando. Los contratos mínimos entreactive-uix,morfo,soma,sema,eidos,adom,langs,formatyprefsquedan descritos en active_architecture.md, sección "Handoff 2026-05-14". Ya queda fijada la regla principal de ownership: soloActiveAppyActiveUixstandalone crean servicios compartidos. La tabla ejecutable de contratos vive en contracts.ts y se valida en contracts.test.ts. La tabla de naming canonico vive en active_architecture.md#01-naming-canonico. Ownership DOM P1 queda cerrado: las escrituras gestionadas por UIX pasan porActiveDom.Convenciones doctrinales del API (intent ↔ color, subset por componente, root visual con partes attached en eidos, sound prepare-time priming): viven en
src/docs/GUIA_IMPLEMENTACION_SEMAUIX.mdAutoritativo 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, haptic, 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, langs, 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 y, cuando el texto pertenece al contrato,
translations del componente. No es prose ni runtime — es la forma canónica
pública.
Ver: morfo/README.md
Sema / Events (src/uix/sema/)
Vocabulario semántico y orquestación de eventos perceptivos. sema queda como
nombre historico de la carpeta; la superficie publica de ActiveUix usa
events.
- familias canónicas:
contact,commit,signal,handle,emerge,shift,sustain - intents canónicos:
neutral,affirm,fulfill,risk,threat,loss uix.events.emit(signal)para que el provider publique ocurrencias- registry de canales (
VisualChannel,SoundChannel,HapticChannel) src/uix/sema/channels.tscomo fuente unica de ids, signatures y overrides de canalessrc/uix/sema/sounds.tscomo repositorio nominal unico de sonidos sintéticos,.wavexternos y recetas dinámicas- semántica secuencial estricta del
VisualChannel: preparadata-event-*mediante un proyector DOM, mantiene durante elholdy 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 y wrappers Svelte sobre los providers headless de soma con las props visuales (variant, size, block, iconOnly, icon, checkMark).
El modulo general queda aplanado en ActiveEidos:
ActiveEidos— runtime activo/contexto visual creado porActiveEidos.create(...). Gestiona configuracion visual, primitivas, roles canonicos, themes, validacion, generacion de CSS y persistencia. Con servicios deActiveUix, recibedom,langs,prefsyformat;modeydensityllegan por fuentes explicitas o defaults propios. Inyecta<style data-uix-eidos>solo cuando la app quiere CSS runtime en vez de CSS precompilado.
La app puede aportar un EidosConfig completo o un themeBase patch
parcial desde ActiveEidos.create({ config/themeBase }), o delegar los valores
de theme a CSS externo con themeSource: 'css'. getCssContract() expone la
lista typed de custom properties y renderContractCss() la publica como CSS
vacío para themes externos. Para editores o preferencias en vivo,
ActiveEidos puede inyectar un mapa de variables runtime en un <style>
propio validado contra ese contrato. Cuando la app quiera persistir una
configuracion completa, guarda un EidosConfigDocument versionado
(kind + version + options) y lo puede pasar de vuelta como
ActiveEidos.create({ config: document }).
El size de Eidos es canonico y discreto: xxs..xxl/full. ActiveEidos genera
tokens fisicos coordinados para xxs..xxl; full queda reservado para
layout responsivo.
El contrato generado tambien incluye alpha color scales (--scale-blue-a1..a12
y --primitive-primary-a1..a12), borde (width/style + aliases), layout
(containerWidth, contentWidth, aspectRatio), density scalars conectados
a data-density, opacidad, z-index y sombras con escala fisica 1..6 más
aliases semanticos por theme. Los aliases de
recipe activos (--toast-*, --dialog-*, etc.) ya viven en
EidosConfig.recipes; el artefacto estatico generado vive en
src/uix/eidos/generated/base.css y
src/uix/eidos/index.css lo importa como foundation para SSR/docs con
ActiveEidos.create({ applyDom: false }). Los antiguos CSS de contracts/
y themes/base/ se retiraron del arbol activo; el contrato se obtiene desde
ActiveEidos.getCssContract() / renderContractCss(). El directorio
tokens/ ya no forma parte del runtime. ActiveEidos.listRecipes() y
getRecipeTokens(component) exponen esos aliases para editores de theme sin
leer CSS.
La migración de componentes Soma -> Eidos se retoma por tandas pequeñas. A fecha
2026-05-17, además de los pilotos previos, ya están migrados meter,
progress, slider, pagination, rating-group, search-field y
number-field, y breadcrumb; cada uno envuelve partes públicas de Soma y
añade únicamente superficie visual.
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. Solo ActiveApp y ActiveUix pueden crear servicios
compartidos:
createActiveUix(options)— standalone, instancia services yprefsinternamente.attachActiveUix(activeApp)— adjunta a unActiveAppya compuesto por la aplicación; valida los servicios requeridos y no crea sustitutos.
Las capas activas consumen ActiveUix vía getActiveUix() y exponen la
superficie minima que necesitan sus componentes. Los componentes Soma leen
Soma; los componentes Eidos leen ActiveEidos. Así no saben qué ruta de boot
se usó ni dependen de la superficie completa de ActiveUix.
ActiveUix y prefs
ActiveUix no exige ni conoce arts/frontend; ese artefacto fue retirado.
El patron sigue a ActiveApp: prefs es core y las capas consumidoras se
sincronizan desde sus dimensiones, pero cada capa proyecta solo lo que posee.
prefs.languagesincroniza el idioma activo deuix.langs.prefs.localealimentauix.format.prefs.directiones la unica fuente de direccion efectiva; deriva delanguagecuando no hay override.prefs.motion,prefs.soundyprefs.hapticson preferencias transversales de percepcion/interaccion.theme,modeydensitypertenecen a Eidos:themenombra la familia visual,moderesuelvelight | darkydensityajusta ergonomia visual.
La escritura global va por el ActiveDom que se inyecte, no por mutaciones
directas:
ActivePrefsDomProjection -> dir, data-motion, data-sound, data-haptic
ActiveEidos -> data-theme, data-mode, data-density
ActiveUix no crea automaticamente ActivePrefsDomProjection. Ese proyector
pertenece a arts/prefs y lo cablea el composition root cuando quiere esos
attrs globales. Eidos se crea aparte mediante ActiveEidos cuando se necesita
runtime visual.
Ejemplo de shell UIX standalone (como la ruta /uix de docs):
const uix = createActiveUix({ langs, prefs: { schema } });
setActiveUix(uix);
Soma.create();
const prefsProjection = createActivePrefsDomProjection({ prefs: uix.prefs, dom: uix.dom });
const eidos = ActiveEidos.create({
theme: 'base',
modeSource,
applyDom: true
});
El toggle de modo claro/oscuro alimenta ActiveEidos.modeSource, no
uix.prefs.theme. prefs.theme no forma parte del preset core de UIX.
En modo standalone, createActiveUix() instancia ActivePrefs con el preset
estandar de UIX y crea los servicios configurados. En modo attach,
attachActiveUix(app) reutiliza app.prefs, app.langs, app.dom,
app.clipboard, app.format y el motor perceptivo cuando existen. UIX lo
expone como uix.events; en attach mode el servicio de ActiveApp se llama
events. langs y dom son requeridos para attach; si faltan, se lanza
error temprano. clipboard, format y events son servicios opcionales:
si una capa los pide y ActiveUix no los expone, fallan con error explicito
en vez de crear sustitutos.
soma.prefs es la vista de preferencias que necesitan los providers
headless, respaldada por uix.prefs, no por frontend.
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
SomaRuntime transcribe
Provider aporta sources, targets y handlers
Effects sincronizan attrs derivados
EngineSemantic orquesta prepare + dispatch a canales perceptivos
VisualChannel prepara data-event* via SignalProjector + mantiene hold
SignalProjector proyecta data-event* via uix.dom
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.
SomaRuntime (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.
EngineSemantic recibe la ocurrencia desde runtime.trigger. Genera el id,
ejecuta los prepare de canales y despacha la señal a TODOS los canales
registrados. El VisualChannel (built-in) proyecta data-event-* mediante
SignalProjector, mantiene los attrs durante el hold configurado, los
limpia y solo entonces resuelve la Promise (semántica secuencial estricta).
Los canales no-visuales (sound, haptic) 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 events.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 events.emit(event); dom.apply(change)
// Señal sin cambio estructural
provider.emitEvent(event);
// internamente: void events.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 ni feedback háptico,
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.
La regla no es "todo acceso DOM pasa por ADom". La regla es mas precisa:
escrituras gestionadas por UIX, listeners de document/window, consultas
globales, observers (ResizeObserver, MutationObserver,
IntersectionObserver) y acciones imperativas transversales pasan por
ActiveDom. Esto incluye foco y scroll imperativo (focus, scrollTo,
scrollIntoView, scrollWindowTo). Las lecturas locales de un elemento que
el componente ya posee (contains, closest, getBoundingClientRect,
scrollTop) se quedan en el componente.
3.5 La app compone servicios, no "super componentes"
active-app permite que la aplicación componga langs, dom,
clipboard, format, events y que ActiveUix los entregue a scopes de capa
(Soma, Eidos) en lugar de obligar a cada componente a conocer la raíz
activa completa.
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 servicio monolitico de eventos 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
SomaRuntime → interpreta morfo dentro de Soma
Provider → aporta sources, targets, handlers
Effects → sincronizan state → attrs
EngineSemantic → registry + prepare/dispatch de señales a canales
VisualChannel → prepara data-event* via SignalProjector + mantiene hold
SignalProjector → proyecta data-event* via uix.dom
ADom → aplica mutaciones DOM (commit estructural)
Eidos → materializa visualmente leyendo DOM
App → compone servicios
Reglas duras:
Morfono conoceSomani código de runtimeSomaRuntimeleeMorfoy depende deSemaProviderno escribe attrs mutables al DOM directamente; los aporta como sources al runtimeEngineSemanticno muta el DOM directamente; solo orquesta hooks de canalesVisualChannel.prepare()delega la proyeccion DOM a unSignalProjectorDomSignalProjectorescribe mediante elActiveDomrecibido desdeActiveUixSemayADomson capas hermanasADomno conoceMorfo, niSema, niSoma, niEidos; solo ejecuta mutaciones, listeners y acciones DOM transversales que recibeActiveEidosadaptaActiveUixpara componentes visuales; los wrappers no importangetActiveUix()directamenteEidosconsume 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, SomaRuntime transcribe, Provider aporta, Effects sincronizan, Sema emite, ADom aplica, Eidos lee.
Siete piezas, siete 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/GUIA_IMPLEMENTACION_SEMAUIX.md— convenciones doctrinales del API