import { UIX_REQUIRED_SERVICES } from './active-uix/services'; import type { ActiveApp } from '$active-app'; import type { ActiveClipboard } from '$clipboard'; import type { ActiveDom } from '$adom'; import type { EngineBus } from '$bus'; import type { ActiveEidos } from './eidos/active-eidos.svelte'; import type { ActiveFormat } from '$format'; import type { ActiveLangs } from '$langs'; import type { EngineLogger } from '$logger'; import type { ActivePrefs } from '$prefs'; import type { ActiveTimers } from '$timer'; import type { EngineSemantic } from './sema'; import type { EngineMotion } from '$motion'; import type { EngineScene } from '$scene'; import type { EngineSound } from '$sound'; import type { Morfo } from './morfo'; import type { SomaRuntime } from './soma/runtime.svelte'; import type { ActiveUix } from './active-uix/types'; import type { UixRequiredService } from './active-uix/services'; export interface ActiveUixServiceContract { readonly langs: ActiveLangs; readonly format: ActiveFormat | undefined; readonly dom: ActiveDom; readonly motion: EngineMotion; readonly scene: EngineScene | undefined; readonly sound: EngineSound; readonly clipboard: ActiveClipboard; readonly events: EngineSemantic | undefined; readonly logger: EngineLogger; readonly bus: EngineBus; readonly timers: ActiveTimers; readonly prefs: ActivePrefs; readonly portal: string | HTMLElement | undefined; readonly app: ActiveApp | undefined; readonly ownsApp: boolean; announce(message: string, priority?: 'polite' | 'assertive', timeout?: number): void; dispose(): void; } export interface SomaRuntimeContract { // Widened on purpose: this table pins the SHAPE of the runtime surface // across every morfo, not one component's names — the one boundary where // `SomaRuntime` is the honest instantiation. part: SomaRuntime['part']; partProps: SomaRuntime['partProps']; keydown: SomaRuntime['keydown']; trigger: SomaRuntime['trigger']; } export interface EventsServiceContract { emit: EngineSemantic['emit']; dispose: EngineSemantic['dispose']; } export interface ActiveEidosRuntimeContract { readonly dom: ActiveDom; readonly langs: ActiveLangs; readonly format: ActiveFormat | undefined; readonly prefs: ActivePrefs; getThemeId(): string; renderStaticCss(): string; apply(): void; dispose(): void; } export interface UixLayerContractTable { readonly activeUix: { readonly standaloneCreates: readonly string[]; readonly attachRequires: readonly UixRequiredService[]; readonly publicSurface: readonly (keyof ActiveUixServiceContract)[]; readonly forbiddenPublicSurface: readonly string[]; }; readonly prefsDomProjection: { readonly role: 'cross-modal-prefs-projection'; readonly implementation: '$prefs.createActivePrefsDomProjection'; readonly requires: readonly ['prefs', 'dom']; readonly ownsAttrs: readonly ['dir', 'lang', 'data-motion', 'data-sound', 'data-haptic']; readonly forbiddenAttrs: readonly ['data-theme', 'data-mode', 'data-density']; readonly createsServices: readonly []; }; readonly morfo: { readonly role: 'declarative-contract'; readonly owns: readonly string[]; readonly createsServices: readonly []; readonly translationNamespacePrefix: 'components'; }; readonly somaRuntime: { readonly role: 'morfo-interpreter'; readonly requires: readonly ['dom']; readonly optional: readonly ['eventEngine', 'translate']; readonly publicSurface: readonly (keyof SomaRuntimeContract)[]; readonly forbiddenPublicSurface: readonly string[]; readonly createsServices: readonly []; }; readonly events: { readonly role: 'perceptual-dispatch'; readonly publicServiceName: 'events'; readonly morfoPayloadName: 'semantic'; readonly requiresOneOfForVisualProjection: readonly ['dom', 'projector']; /** * What `defineEngineSemantic()` takes from the app schema. Both degrade to * `undefined`: without `dom` there is no visual projection, and without * `sound` the sound channel owns a private engine — which is why `sound` * is listed here at all: it is how the row below keeps its * `singleContextPerDocument` promise in attach mode. */ readonly serviceDependencies: readonly ['dom', 'sound']; readonly createsServices: readonly []; }; /** * The Web Audio runtime (`$sound`). Requires nothing: both dependencies are * optional ports and both degrade — without `dom` a context suspended by the * autoplay policy resumes on the next `prime()` instead of on the next * gesture; without `timers` the earcon's completion falls back to * `setTimeout`. Without an `AudioContext` at all it is silently inert, which * is why it can be created unconditionally. * * `singleContextPerDocument` is the invariant the art exists for: browsers * cap concurrent contexts and the unlock gesture is per-context, so every * consumer takes THIS engine (sema via `soundEngine`, everyone else via * `uix.sound.context`) instead of opening its own. */ readonly sound: { readonly role: 'web-audio-runtime'; readonly publicServiceName: 'sound'; readonly optional: readonly ['dom', 'timers']; readonly degradation: 'silent'; readonly singleContextPerDocument: true; readonly createsServices: readonly []; }; readonly activeEidos: { readonly role: 'visual-runtime'; readonly publicRuntime: 'ActiveEidos'; readonly forbiddenRuntime: 'EngineEidos'; readonly requiresForDomApply: readonly ['dom']; readonly constructionOptionalServices: readonly ['langs', 'format', 'prefs']; readonly requiredAccessors: readonly ['langs', 'prefs']; readonly optionalAccessors: readonly ['format']; readonly createsServices: readonly []; }; } export const UIX_LAYER_CONTRACTS = { activeUix: { standaloneCreates: [ 'logger', 'bus', 'timers', 'prefs', 'langs', 'dom', 'clipboard', 'format', 'events', 'sound' ], attachRequires: UIX_REQUIRED_SERVICES, publicSurface: [ 'langs', 'format', 'dom', 'motion', 'scene', 'sound', 'clipboard', 'events', 'logger', 'bus', 'timers', 'prefs', 'portal', 'app', 'ownsApp', 'announce', 'dispose' ], forbiddenPublicSurface: [ 'lang', 'semantic', 'frontend', 'soma', 'eidos', 'somaPortalTo', 'settings', 'presentation' ] }, prefsDomProjection: { role: 'cross-modal-prefs-projection', implementation: '$prefs.createActivePrefsDomProjection', requires: ['prefs', 'dom'], ownsAttrs: ['dir', 'lang', 'data-motion', 'data-sound', 'data-haptic'], forbiddenAttrs: ['data-theme', 'data-mode', 'data-density'], createsServices: [] }, morfo: { role: 'declarative-contract', owns: ['parts', 'states', 'props', 'events', 'translations'], createsServices: [], translationNamespacePrefix: 'components' }, somaRuntime: { role: 'morfo-interpreter', requires: ['dom'], optional: ['eventEngine', 'translate'], publicSurface: ['part', 'partProps', 'keydown', 'trigger'], forbiddenPublicSurface: ['registerPart', 'createAttrs', 'registerContract'], createsServices: [] }, events: { role: 'perceptual-dispatch', publicServiceName: 'events', morfoPayloadName: 'semantic', requiresOneOfForVisualProjection: ['dom', 'projector'], serviceDependencies: ['dom', 'sound'], createsServices: [] }, sound: { role: 'web-audio-runtime', publicServiceName: 'sound', optional: ['dom', 'timers'], degradation: 'silent', singleContextPerDocument: true, createsServices: [] }, activeEidos: { role: 'visual-runtime', publicRuntime: 'ActiveEidos', forbiddenRuntime: 'EngineEidos', requiresForDomApply: ['dom'], constructionOptionalServices: ['langs', 'format', 'prefs'], requiredAccessors: ['langs', 'prefs'], optionalAccessors: ['format'], createsServices: [] } } as const satisfies UixLayerContractTable; export type UixLayerContracts = typeof UIX_LAYER_CONTRACTS; type Extends = [T] extends [U] ? true : false; export const UIX_TYPE_CONTRACTS = { activeUix: true, somaRuntime: true, events: true, activeEidos: true } satisfies { activeUix: Extends; somaRuntime: Extends, SomaRuntimeContract>; events: Extends; activeEidos: Extends; };