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/src/arts/prefs/README.md

6.8 KiB

Prefs

prefs es el artefacto activo de preferencias. Su trabajo es resolver, de forma generica, la relacion entre intencion de usuario, entorno detectado y valor efectivo para un esquema declarado por la app.

No traduce, no formatea, no persiste por si mismo y no escribe el DOM salvo cuando el composition root cablea explicitamente createActivePrefsDomProjection(...).

Estado 2026-05-14

Decisiones vigentes:

  • prefs.language alimenta langs.
  • prefs.locale alimenta format.
  • prefs.direction resuelve direccion efectiva.
  • prefs.motion, prefs.sound y prefs.haptic son preferencias transversales de percepcion/interaccion.
  • createActivePrefsDomProjection(...) proyecta solo dir, data-motion, data-sound y data-haptic.
  • theme, mode y density visuales pertenecen a ActiveEidos, no al preset core de prefs, ActiveApp ni ActiveUix.
  • No hay themeDimension(...) ni densityDimension(...) en el catalogo publico de prefs: si una app necesita dimensiones custom, usa las primitivas genericas (enumDimension, stringDimension, etc.) o una PrefsDimension propia.

Composition Rule

Solo los composition roots crean ActivePrefs:

  • ActiveApp crea o recibe prefs.
  • createActiveUix(...) crea prefs cuando UIX arranca standalone.
  • attachActiveUix(app) reutiliza app.prefs.

Las capas consumidoras leen slots concretos o reciben vistas acotadas. No deben crear otra instancia compensatoria de preferencias.

ActiveApp/createActiveUix -> ActivePrefs
langs                     -> prefs.language
format                    -> prefs.locale, currency, timezone, unitSystem
ActivePrefsDomProjection  -> direction, motion, sound, haptic
ActiveEidos               -> theme/mode/density visuales propios

Schema Model

La implementacion actual es schema-based:

type PrefsSchema = Record<string, PrefsDimension<TIntent, TEffective>>;

Cada dimension declara:

  • defaultValue: valor de fallback.
  • validate(value): valida intencion de usuario.
  • resolve(intent, env): opcional; convierte intencion + entorno en valor efectivo.
  • catalog(): opcional; lista de valores seleccionables.

El motor mantiene tres planos:

intent      = lo que el usuario eligio explicitamente
environment = lo que servidor/browser/sistema sugieren
effective   = valor total que leen los consumidores

Solo intent se persiste. environment se recalcula y effective se deriva.

Standard Preset

standardPrefsDimensions(catalog) compone el preset transversal:

const schema = {
	...standardPrefsDimensions({
		languages: ['es', 'en'],
		locales: ['es-ES', 'en-US'],
		currencies: ['EUR', 'USD'],
		defaults: {
			language: 'es',
			locale: 'es-ES',
			currency: 'EUR'
		}
	}),
	sidebarCollapsed: booleanDimension({ default: false })
};

Incluye:

language
locale
currency
timezone
unitSystem
motion
sound
haptic
direction

No incluye:

theme
mode
density

Esos valores son visuales en UIX. Una shell debe pasarlos a ActiveEidos mediante theme, modeSource y densitySource.

Active Surface

createActivePrefs({ schema }) devuelve una superficie reactiva con un slot por dimension:

const prefs = createActivePrefs({ schema });

prefs.locale.get();
prefs.locale.set('en-US');
prefs.locale.clear();
prefs.locale.onChange((locale) => {});
prefs.locale.catalog();

Tambien expone metodos genericos para adaptadores:

prefs.setIntent('locale', 'en-US');
prefs.clearIntent('locale');
prefs.resetIntent();
prefs.patchEnvironment({ reducedMotion: true });
prefs.refreshEnvironment(nextEnvironment);
prefs.subscribe((event) => {});
prefs.dispose();

Los servicios que reciben un ActivePrefs abierto y no conocen su schema en tiempo de compilacion deben leer defensivamente:

const slot = readActivePrefsSlot<Locale>(prefs, 'locale');
const locale = slot?.get();

Si el slot no existe, el consumidor decide si puede degradar o debe lanzar su propio error de configuracion.

Environment

El entorno entra por adaptadores. Ninguna dimension lee window, cookies, headers, localStorage o DOM directamente.

Adaptadores disponibles:

  • detectServerEnvironment(input)
  • detectBrowserEnvironment(overrides?)
  • applyBrowserEnvironment(prefs, overrides?)
  • watchBrowserEnvironment(prefs, overrides?)

Ejemplos de entorno:

Accept-Language -> language/locale candidates
Intl timezone   -> timezone
matchMedia      -> reducedMotion/colorScheme
navigator       -> languages, reduced sound/haptics when available

colorScheme puede existir en el entorno porque el browser lo expone, pero UIX no lo convierte en prefs.theme; ActiveEidos puede leer el sistema por su propia modeSource.

DOM Projection

ActivePrefs no escribe el DOM por si mismo. Si la app quiere atributos globales, cablea el proyector:

const prefsProjection = createActivePrefsDomProjection({
	prefs: App.prefs,
	dom: App.dom
});

El proyector es idempotente, se suscribe a los slots disponibles y limpia los atributos que gestiono en dispose().

Contrato de atributos:

prefs.direction -> dir
prefs.motion    -> data-motion
prefs.sound     -> data-sound
prefs.haptic    -> data-haptic

No proyecta data-theme, data-mode ni data-density.

Eidos Boundary

Para una shell visual:

const uix = createActiveUix({ langs, prefs: { schema } });
const prefsProjection = createActivePrefsDomProjection({
	prefs: uix.prefs,
	dom: uix.dom
});

const eidos = ActiveEidos.create({
	theme: 'base',
	modeSource,
	densitySource,
	applyDom: true
});

Regla practica:

NO: uix.prefs.setIntent('theme', 'dark')
SI: modeSource notifica 'dark' a ActiveEidos

Si una app no UIX decide declarar una dimension visual propia en prefs, es un contrato local de esa app. No debe filtrarse a ActiveUix, Soma, Sema ni Morfo.

Storage

createPrefsStorageBridge(...) persiste intenciones, no valores efectivos:

const bridge = createPrefsStorageBridge({
	prefs,
	storage,
	key: 'active:prefs'
});

Reglas:

  • Persistir solo intent.
  • No persistir environment.
  • No persistir effective.
  • No escribir durante hydrate salvo configuracion explicita.
  • Un fallo de storage no debe corromper preferencias en memoria.

Errors

Los errores publicos usan la familia prefs::*:

  • prefs::unknown_dimension
  • prefs::intent_invalid
  • prefs::reserved_key
  • prefs::disposed

Ejemplo conocido:

prefs::unknown_dimension: [prefs] no such dimension in schema: theme

En UIX ese error normalmente significa que una shell intento escribir prefs.theme. La correccion es pasar el modo visual a ActiveEidos.

Tests

npx vitest run src/arts/prefs/test/engine-prefs.test.ts
npx vitest run src/arts/prefs/test/active-prefs.svelte.test.ts
npx vitest run src/arts/prefs/test/dom-projection.test.ts

Powered by TurnKey Linux.