5.9 KiB
| title | type | audience | authority | status | source |
|---|---|---|---|---|---|
| ActiveUix — the composition root | reference | human + agent | E1 architecture — how UIX boots, which services it owns, and how it degrades | current | 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. Executable contract:src/uix/contracts.ts, validated bycontracts.test.ts.
Two boot modes
createActiveUix(options) — standalone. Composes its own runtime:
- creates
logger,timers,busandprefs; - the
bususescreateSvelteEngineBus({ logger, clock: timers.clock }), same asActiveApp, so listeners run underuntrackand never create accidental reactive dependencies; - creates
langs,dom(ordisabledDomwhendom:false),clipboard,formatandeventsaccording to the options; - keeps
portalas a generic portal target, so each layer adapts it to its own API (portalTo) without couplingactive-uixto 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.clipboardandapp.formatwhen they exist; - requires
app.langsandapp.dom; if either is missing it throws a configuration error; - does not re-subscribe
langstoprefs.language(that connection belongs todefineActiveLangsinsideActiveApp); - 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.
| 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
- Only composition roots create shared services.
morfo,soma,sema,eidosand components never createdom,langs,prefs,format,clipboardor equivalents: they receive them fromActiveUix. dom:falseonly degrades in standalone.ActiveUixexposes a localdisabledDomand passes it toEngineSemantictoo; Sema/events never fall back to direct DOM writes. In attach there is no compensating creation: if the app has nodom,attachActiveUix(app)fails.disabledDomis 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— throwActiveUixDomDisabledError; a component that measures underdom:falsedies loudly instead of computing from a phantom layout.langsis notlocale.prefs.languagefeeds translations;prefs.localefeeds formats. They never mix.clipboardis a capability service, not visual DOM. Standalone creates it unlessclipboard:false; attach consumes it fromapp.clipboardwhen a layer asks for it, and fails with an explicit error if it was not declared.prefs.directionis the effective direction preference;html[dir]is only its DOM projection.ActiveUixdoes not auto-project preferences onto the DOM.arts/prefsprojects the cross-modal attrs (dir,data-motion,data-sound,data-haptic) viacreateActivePrefsDomProjection;ActiveEidosprojects the visual ones (data-theme,data-mode,data-density). Thefrontendartifact was retired and must not reappear as alocalesource.ActiveUixneither creates nor knowsSomaorEidos.Soma.create(...)creates its own scope andSoma.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:
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.