# ActiveUix `active-uix` es la raíz de composición de UIX. Su responsabilidad no es ser otra capa de comportamiento, sino entregar a `morfo`, `soma`, `sema` y `eidos` los servicios mínimos que necesitan, sin que los componentes conozcan `ActiveApp` directamente. > **Arquitectura de conjunto**: [`../active_architecture.md`](../active_architecture.md). > **Contrato ejecutable**: [`contracts.ts`](../contracts.ts), validado en > [`contracts.test.ts`](../contracts.test.ts). ## Dos modos de arranque `createActiveUix(options)` — **standalone**. Compone un runtime propio: - crea `logger`, `timers`, `bus` y `prefs`; - el `bus` usa `createSvelteEngineBus({ logger, clock: timers.clock })`, igual que `ActiveApp`, para que los listeners corran bajo `untrack` y no creen dependencias reactivas accidentales; - crea `langs`, `dom` (o `disabledDom` si `dom:false`), `clipboard`, `format` y `events` según opciones; - conserva `portal` como target genérico de portales, para que cada capa lo adapte a su API (`portalTo`) sin acoplar `active-uix` a esa capa; - registra las traducciones comunes y las `morfo.translations`. `attachActiveUix(app, options)` — **attach** a un `ActiveApp` externo: - reutiliza `app.prefs`, `app.langs`, `app.dom`, `app.clipboard` y `app.format` cuando existen; - **exige** `app.langs` y `app.dom`; si faltan, lanza error de configuración; - no vuelve a suscribir `langs` a `prefs.language` (esa conexión pertenece a `defineActiveLangs` dentro de `ActiveApp`); - registra las traducciones comunes y las `morfo.translations`; - no posee el lifecycle del `app`. ## Contratos mínimos por módulo > Fuente ejecutable: [`contracts.ts`](../contracts.ts). | Módulo | Mínimo requerido | Opcional | Fallback si falta | Error si falta | | --- | --- | --- | --- | --- | | `ActiveUix` standalone | `langs` config | `clipboard:false`, `format`, `events`, `portal`, `dom:false` | crea `prefs`, core services y, si `dom:false`, `disabledDom` local | falta config `langs` | | `ActiveUix` attach | `ActiveApp` core + servicios `langs`, `dom` | `app.clipboard`, `app.format`, motor de eventos, `portal` | ninguno para servicios requeridos | falta `langs` o `dom` en app; el getter de un servicio opcional ausente falla explícito | | `SomaRuntime` | `dom` desde `ActiveUix` | motor de eventos, `langs`, `format` | ninguno propio | morfo/event/part inexistente | | `Sema` directo | `dom` o `projector` si `visual` activo | `sound`, `haptic`, `visual:false` | ninguno propio de servicios UIX | `SemaConfigError` sin `dom/projector` con visual activo | | `Eidos` | `dom` si `applyDom` | `langs`, `format`, `prefs`, fuentes mode/density | `applyDom:false` permite render/serializar sin DOM | falta `dom` con `applyDom` activo | | `ADom` directo | target/window/document del caller | breakpoints/window | `disabledDom` solo si el caller lo pide | errores propios de ADom sin DOM real | ## Reglas de ownership y degradación 1. **Solo los composition roots crean servicios compartidos.** `morfo`, `soma`, `sema`, `eidos` y los componentes nunca crean `dom`, `langs`, `prefs`, `format`, `clipboard` ni equivalentes: los reciben de `ActiveUix`. 2. **`dom:false` solo degrada en standalone.** `ActiveUix` expone un `disabledDom` local y se lo pasa también a `EngineSemantic`; Sema/events no caen a escrituras DOM directas. En attach no hay creación compensatoria: si la app no tiene `dom`, `attachActiveUix(app)` falla. 3. **`langs` no es `locale`.** `prefs.language` alimenta las traducciones; `prefs.locale` alimenta los formatos. No se mezclan. 4. **`clipboard` es un servicio de capacidad, no DOM visual.** Standalone lo crea salvo `clipboard:false`; attach lo consume de `app.clipboard` cuando una capa lo pide, y falla con error explícito si no se declaró. 5. **`prefs.direction` es la preferencia efectiva de dirección**; `html[dir]` es solo su proyección DOM. 6. **`ActiveUix` no auto-proyecta preferencias al DOM.** `arts/prefs` proyecta lo transversal (`dir`, `data-motion`, `data-sound`, `data-haptic`) vía `createActivePrefsDomProjection`; `ActiveEidos` proyecta lo visual (`data-theme`, `data-mode`, `data-density`). El artefacto `frontend` fue retirado y no debe reaparecer como fuente de `locale`. 7. **`ActiveUix` no crea ni conoce `Soma` ni `Eidos`.** `Soma.create(...)` crea su propio scope y `Soma.runtime(...)`; `ActiveEidos.create(...)` crea el scope visual cuando la app necesita CSS runtime. ## Arrancar una shell UIX Una shell que usa componentes visuales cablea tres piezas de forma explícita: ```ts 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 }); ``` `prefsProjection` es el dueño de `dir`, `data-motion`, `data-sound` y `data-haptic`. `ActiveEidos` es el dueño de `data-theme`, `data-mode` y `data-density`. El modo claro/oscuro se pasa a `ActiveEidos.modeSource`, no a `prefs.theme` — `theme` no forma parte del preset core de UIX y escribirlo provoca `prefs::unknown_dimension`.