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

8.2 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 by contracts.test.ts.

Two boot modes

createActiveUix(options) — standalone. Composes its own runtime:

  • creates logger, timers, bus and prefs — and the prefs schema ALWAYS carries the four visual axes: uixVisualPrefsDimensions() is merged under whatever prefs.schema the app passes, so the app may redefine mode / theme / density / scaling but never omit them;
  • 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;
  • does not merge the visual axes. The prefs engine already exists and belongs to the app, so nothing can be composed into it after the fact: an attaching app spreads uixVisualPrefsDimensions() (from $active-uix) into its own prefs schema. Otherwise eidos warns once per absent slot (eidos.prefs) and falls back to its own default — mode: 'light' on a dark-mode machine included;
  • 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.

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. Standalone projects the cross-modal preferences by default. createActiveUix builds createActivePrefsDomProjection unless projectPrefs: false (types.ts → projectPrefs, @default true), and it owns dir, lang, data-motion, data-sound and data-haptic; components stamp only ASSERTED directions, so the page must reflect the ambient preference or nothing does. In attach it is the opposite: opt-in with projectPrefs: true, because the host app may own <html>. The visual axes (data-theme, data-mode, data-density, data-scaling) are ActiveEidos' job, never this one. The frontend artifact was retired and must not reappear as a locale source. What UIX writes into <html dir> carries an ownership mark — canon/direction-contract.md §6.
  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:

const uix = createActiveUix({ langs, prefs: { schema } });

setActiveUix(uix);
Soma.create();

const eidos = ActiveEidos.create({ applyDom: true });

The cross-modal projection comes with the root (rule 6): uix.prefsProjection owns dir, lang, data-motion, data-sound and data-haptic. Building createActivePrefsDomProjection by hand here would be a SECOND projection over the same attributes. ActiveEidos owns data-theme, data-mode and data-density. Light/dark mode is a prefs dimension like the rest (2026-09-14): the toggle writes uix.prefs.setIntent('mode', 'dark') and eidos, which reads the slot, re-applies the attributes.

Powered by TurnKey Linux.