# Sema `Sema` define el dominio semántico canónico de UIX y orquesta la emisión de señales perceptivas. ## Qué es - familias canónicas: `contact`, `commit`, `alert`, `handle`, `emerge`, `sustain` - intents canónicos: `neutral`, `affirm`, `fulfill`, `risk`, `threat` - normalización entre shape estructurado y label canónico - validación mínima del dominio - `EngineSemantic` como canalizador de ocurrencias `Sema` no decide qué evento ocurrió. El provider lo decide. `EngineSemantic` recibe la ocurrencia y orquesta su materialización en el canal perceptivo visual (DOM). ## Qué ya no es `Sema` ya no es un runtime multimodal. No contiene: - resolver de canales - sound/motion/color/presence engines - mapa perceptivo por canal - política global de accesibilidad por canal Eso pertenece a capas futuras y separadas: - `EngineSemantic` publica ocurrencias semánticas - `ActiveDom` materializa esas ocurrencias como `data-event*` en el DOM - `SoundEngine`, `VibraEngine` y otros engines modales se suscribirán al engine ## El contrato `emit` ```ts semantic.emit(event: SemanticEvent): Promise ``` Una sola firma. Cubre los tres escenarios cuando se compone con `dom.apply`: ```ts // Cambio estructural sin señal dom.apply(change) // Cambio estructural con señal await semantic.emit(event) dom.apply(change) // Señal sin cambio estructural void semantic.emit(event) ``` ### Semántica de la Promise `emit(event)` resuelve cuando: - la señal `data-event*` ya fue escrita al DOM - ha pasado **un rAF** para que CSS pueda observarla y arrancar transitions - todavía está visible en el DOM No resuelve antes (no hay frame para que CSS reaccione) ni después de la limpieza (la señal ya no estaría visible cuando el commit estructural entre). ### Ciclo de vida interno de `emit` ``` 1. Engine genera id/session de la ocurrencia 2. Engine despacha la señal a TODOS los canales registrados - canales no-visuales (sound, vibra) → fire-and-forget (no awaited) - canal visual → awaited 3. Engine resuelve la Promise cuando el visual ha terminado (semántica secuencial estricta: cleanup ANTES del resolve) ``` ### Canales como módulos Sema está organizada en canales perceptivos simétricos: ``` src/uix/sema/ ├── engine.ts registry + dispatch └── chans/ ├── types.ts interfaz Channel ├── visual.ts VisualChannel (built-in, escribe data-event* al DOM) ├── sound.ts SoundChannel (placeholder V1) └── vibra.ts VibraChannel (placeholder V1) ``` El engine no conoce DOM ni hold ni atributos. Cada canal materializa la señal en su modalidad. Solo el canal visual bloquea al caller (comparte plano DOM con el commit estructural posterior); los demás son fire-and-forget. ### Hold — política técnica del canal visual El `VisualChannel` mantiene los atributos `data-event*` en el DOM durante un `hold` configurable. Defaults internos del canal por familia: | Family | Hold | |---|---| | `emerge` | 240ms | | `sustain` | 600ms | | `contact` | 120ms | | `commit` | 240ms | | `alert` | 600ms | | `handle` | 240ms | Default global (cuando ni signal.hold ni la familia lo proporcionan): 240ms. Estos números reflejan rangos típicos de CSS transitions para cada tipo de feedback. El integrador puede subir el default global vía `new SemanticEngine({ visual: { defaultHold: ... } })` o per signal vía `signal.hold`. ```ts // Override per signal semantic.emit({ ..., hold: 1200 }) // Override default global del canal visual const semantic = new SemanticEngine({ visual: { defaultHold: 400 } }) // Desactivar visual (entornos sin DOM) const semantic = new SemanticEngine({ visual: false }) // Registrar canales adicionales (cuando estén implementados) import { SoundChannel } from '$uix/sema' semantic.register(new SoundChannel()) ``` ### Política de errores - Errores en canales NO-visuales se loguean pero no propagan. Sema es ornamental: un fallo de audio context o vibration API no debe abortar la operación del provider. - Si el canal visual lanza, la Promise de `emit` rechaza. El caller decide. - En fire-and-forget (`void semantic.emit(...)`), una rejection del visual se propaga como unhandled promise — política consciente. ## Vocabulario canónico de verbs (`SEMA_VERBS`) Cross-component action verbs. `morfo.events[].name` debería alinear con este vocabulario para que sema/sound/vibra puedan suscribir por verb y eidos pueda escribir selectores transversales (`[data-event^=dismiss]`). ``` emerge: present · dismiss · open · close · expand · collapse commit: commit · cancel · confirm · submit · reset · fail alert: announce · alert contact: activate · select · toggle handle: acknowledge · edit · drag · resize sustain: tick · progress ``` Definido en [`verbs.ts:SEMA_VERBS`](./verbs.ts). ### Composite event names Los eventos pueden tener variantes con la convención `{verb}-{variant}`: ```ts 'dismiss' // bare verb 'dismiss-outside' // verb + variant 'commit-save' 'commit-cancel' 'close-after-fail' ``` `validateEventName(name)` extrae head + variant y reporta si el head es canonical. Advisory — no rechaza morfos, solo flagea drift para tooling y revisión. ```ts import { validateEventName } from '$uix/sema' validateEventName('commit-save') // { name: 'commit-save', head: 'commit', matchesCanonical: true, variant: 'save' } validateEventName('frob-glob') // { name: 'frob-glob', head: 'frob', matchesCanonical: false, variant: 'glob' } ``` ## Relación con Morfo y Soma - `Morfo` declara los eventos semánticos del componente en `morfo.events` - `Provider` decide cuándo ocurren y llama a `semantic.emit(...)` - `MorfoRuntime` orquesta la secuencia `prewrite -> emit -> handler -> effects` - `Sema` aporta el vocabulario, la normalización y la validación del dominio, y publica las ocurrencias ## Dependencias - `Sema` puede usar `Dom` (`semantic.emit` llama a `dom.apply` para escribir `data-event*`). Dependencia hacia abajo, legítima. - `Dom` no conoce `Sema`. - `Sema` recibe `dom` por construcción, no lo importa duro de `$uix/adom`. ## Regla de arquitectura `Morfo` autoriza la semántica del componente. `Sema` define el vocabulario canónico y orquesta la señal perceptiva. `Provider` decide cuándo emitir. `Dom` aplica. Ver [src/uix/README.md](../README.md) §2.bis para la vista cross-layer.