You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/docs/architecture/active-uix.md

116 lines
5.9 KiB

---
title: ActiveUix — the composition root
type: reference
audience: human + agent
authority: E1 architecture — how UIX boots, which services it owns, and how it degrades
status: current
source: migrated from src/uix/active-uix/README.md (2026-07-02, docs-book F7.1)
---
# ActiveUix
`active-uix` is UIX's composition root. Its responsibility is not to be another
behavior layer, but to hand `morfo`, `soma`, `sema` and `eidos` the minimal
services they need — without components ever knowing `ActiveApp` directly.
> **Whole-system architecture**: [`architecture/active-architecture.md`](./active-architecture.md).
> **Executable contract**: [`src/uix/contracts.ts`](../../src/uix/contracts.ts), validated by
> [`contracts.test.ts`](../../src/uix/contracts.test.ts).
## Two boot modes
`createActiveUix(options)` — **standalone**. Composes its own runtime:
- creates `logger`, `timers`, `bus` and `prefs`;
- the `bus` uses `createSvelteEngineBus({ logger, clock: timers.clock })`, same
as `ActiveApp`, so listeners run under `untrack` and never create accidental
reactive dependencies;
- creates `langs`, `dom` (or `disabledDom` when `dom:false`), `clipboard`,
`format` and `events` according to the options;
- keeps `portal` as a generic portal target, so each layer adapts it to its own
API (`portalTo`) without coupling `active-uix` to that layer;
- registers the common translations and the per-component catalogs from
`src/uix/langs/components/*`.
`attachActiveUix(app, options)` — **attach** to an external `ActiveApp`:
- reuses `app.prefs`, `app.langs`, `app.dom`, `app.clipboard` and `app.format`
when they exist;
- **requires** `app.langs` and `app.dom`; if either is missing it throws a
configuration error;
- does not re-subscribe `langs` to `prefs.language` (that connection belongs to
`defineActiveLangs` inside `ActiveApp`);
- registers the common translations and the per-component catalogs;
- does not own the `app`'s lifecycle.
## Minimum contracts per module
> Executable source: [`src/uix/contracts.ts`](../../src/uix/contracts.ts).
| Module | Minimum required | Optional | Fallback when missing | Error when missing |
| --- | --- | --- | --- | --- |
| `ActiveUix` standalone | `langs` config | `clipboard:false`, `format`, `events`, `portal`, `dom:false` | creates `prefs`, core services and, with `dom:false`, a local `disabledDom` | missing `langs` config |
| `ActiveUix` attach | `ActiveApp` core + `langs`, `dom` services | `app.clipboard`, `app.format`, event engine, `portal` | none for required services | missing `langs` or `dom` on the app; the getter of an absent optional service fails explicitly |
| `SomaRuntime` | `dom` from `ActiveUix` | event engine, `langs`, `format` | none of its own | nonexistent morfo/event/part |
| `Sema` direct | `dom` or `projector` when `visual` is active | `sound`, `haptic`, `visual:false` | none of its own for UIX services | `SemaConfigError` without `dom/projector` while visual is active |
| `Eidos` | `dom` when `applyDom` | `langs`, `format`, `prefs`, mode/density sources | `applyDom:false` allows render/serialize without DOM | missing `dom` with `applyDom` active |
| `ADom` direct | caller's target/window/document | breakpoints/window | `disabledDom` only when the caller asks for it | ADom's own errors without a real DOM |
## Ownership and degradation rules
1. **Only composition roots create shared services.** `morfo`, `soma`, `sema`,
`eidos` and components never create `dom`, `langs`, `prefs`, `format`,
`clipboard` or equivalents: they receive them from `ActiveUix`.
2. **`dom:false` only degrades in standalone.** `ActiveUix` exposes a local
`disabledDom` and passes it to `EngineSemantic` too; Sema/events never fall
back to direct DOM writes. In attach there is no compensating creation: if
the app has no `dom`, `attachActiveUix(app)` fails. **`disabledDom` is
deliberately MIXED** (AUX-2, 2026-07-11): write-shaped calls (`apply`,
`listen`, …) no-op silently, but reads that MUST return a value —
`measure`, `raf`, `getDocument` / `getWindow` — **throw
`ActiveUixDomDisabledError`**; a component that measures under
`dom:false` dies loudly instead of computing from a phantom layout.
3. **`langs` is not `locale`.** `prefs.language` feeds translations;
`prefs.locale` feeds formats. They never mix.
4. **`clipboard` is a capability service, not visual DOM.** Standalone creates
it unless `clipboard:false`; attach consumes it from `app.clipboard` when a
layer asks for it, and fails with an explicit error if it was not declared.
5. **`prefs.direction` is the effective direction preference**; `html[dir]` is
only its DOM projection.
6. **`ActiveUix` does not auto-project preferences onto the DOM.** `arts/prefs`
projects the cross-modal attrs (`dir`, `data-motion`, `data-sound`,
`data-haptic`) via `createActivePrefsDomProjection`; `ActiveEidos` projects
the visual ones (`data-theme`, `data-mode`, `data-density`). The `frontend`
artifact was retired and must not reappear as a `locale` source.
7. **`ActiveUix` neither creates nor knows `Soma` or `Eidos`.** `Soma.create(...)`
creates its own scope and `Soma.runtime(...)`; `ActiveEidos.create(...)`
creates the visual scope when the app needs runtime CSS.
## Booting a UIX shell
A shell that uses visual components wires three pieces explicitly:
```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` owns `dir`, `data-motion`, `data-sound` and `data-haptic`.
`ActiveEidos` owns `data-theme`, `data-mode` and `data-density`. Light/dark
mode goes to `ActiveEidos.modeSource`, not to `prefs.theme` — `theme` is not
part of UIX's core prefs preset and writing it raises
`prefs::unknown_dimension`.

Powered by TurnKey Linux.