From 6e13790a733afebfba32ab0cd44d2a9100059035 Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 3 Jul 2026 21:26:24 +0200 Subject: [PATCH] =?UTF-8?q?docs(arts):=20A2=20ES->EN=20=E2=80=94=20prefs?= =?UTF-8?q?=20(full=20translation)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Full Spanish -> English translation of prefs/README.md (faithful; all code / text blocks and the `prefs::*` error strings kept verbatim). Carries the A1 signature fixes already landed (applyBrowserEnvironment/watchBrowserEnvironment, createPrefsStorageBridge `engine`). Co-Authored-By: Claude Opus 4.8 --- src/arts/prefs/README.md | 177 ++++++++++++++++++++------------------- 1 file changed, 90 insertions(+), 87 deletions(-) diff --git a/src/arts/prefs/README.md b/src/arts/prefs/README.md index 287a97905..f27c372fb 100644 --- a/src/arts/prefs/README.md +++ b/src/arts/prefs/README.md @@ -1,78 +1,80 @@ # 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. +`prefs` is the active preferences artifact. Its job is to resolve, generically, +the relationship between user intent, detected environment and effective value +for a schema declared by the app. + +It does not translate, does not format, does not persist by itself, and does not +write the DOM except when the composition root explicitly wires +`createActivePrefsDomProjection(...)`. + +## Status 2026-05-14 + +Current decisions: + +- `prefs.language` feeds `langs`. +- `prefs.locale` feeds `format`. +- `prefs.direction` resolves the effective direction. +- `prefs.motion`, `prefs.sound` and `prefs.haptic` are cross-cutting + perception/interaction preferences. +- `createActivePrefsDomProjection(...)` projects only `dir`, `data-motion`, + `data-sound` and `data-haptic`. +- Visual `theme`, `mode` and `density` belong to `ActiveEidos`, not to the core + preset of `prefs`, `ActiveApp` or `ActiveUix`. +- There is no `themeDimension(...)` or `densityDimension(...)` in the public + prefs catalog: if an app needs custom dimensions, it uses the generic + primitives (`enumDimension`, `stringDimension`, etc.) or its own + `PrefsDimension`. ## Composition Rule -Solo los composition roots crean `ActivePrefs`: +Only composition roots create `ActivePrefs`: -- `ActiveApp` crea o recibe `prefs`. -- `createActiveUix(...)` crea `prefs` cuando UIX arranca standalone. -- `attachActiveUix(app)` reutiliza `app.prefs`. +- `ActiveApp` creates or receives `prefs`. +- `createActiveUix(...)` creates `prefs` when UIX boots standalone. +- `attachActiveUix(app)` reuses `app.prefs`. -Las capas consumidoras leen slots concretos o reciben vistas acotadas. No -deben crear otra instancia compensatoria de preferencias. +Consuming layers read specific slots or receive scoped views. They must not +create another compensatory preferences instance. ```text ActiveApp/createActiveUix -> ActivePrefs langs -> prefs.language format -> prefs.locale, currency, timezone, unitSystem ActivePrefsDomProjection -> direction, motion, sound, haptic -ActiveEidos -> theme/mode/density visuales propios +ActiveEidos -> its own visual theme/mode/density ``` ## Schema Model -La implementacion actual es schema-based: +The current implementation is schema-based: ```ts type PrefsSchema = Record>; ``` -Cada dimension declara: +Each dimension declares: -- `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. +- `defaultValue`: fallback value. +- `validate(value)`: validates user intent. +- `resolve(intent, env)`: optional; turns intent + environment into the + effective value. +- `catalog()`: optional; list of selectable values. -El motor mantiene tres planos: +The engine keeps three planes: ```text -intent = lo que el usuario eligio explicitamente -environment = lo que servidor/browser/sistema sugieren -effective = valor total que leen los consumidores +intent = what the user explicitly chose +environment = what the server/browser/system suggest +effective = the final value consumers read ``` -Solo `intent` se persiste. `environment` se recalcula y `effective` se deriva. +Only `intent` is persisted. `environment` is recomputed and `effective` is +derived. ## Standard Preset -`standardPrefsDimensions(catalog)` compone el preset transversal: +`standardPrefsDimensions(catalog)` composes the cross-cutting preset: ```ts const schema = { @@ -90,7 +92,7 @@ const schema = { }; ``` -Incluye: +Includes: ```text language @@ -104,7 +106,7 @@ haptic direction ``` -No incluye: +Does not include: ```text theme @@ -112,13 +114,13 @@ mode density ``` -Esos valores son visuales en UIX. Una shell debe pasarlos a `ActiveEidos` -mediante `theme`, `modeSource` y `densitySource`. +Those values are visual in UIX. A shell must pass them to `ActiveEidos` via +`theme`, `modeSource` and `densitySource`. ## Active Surface -`createActivePrefs({ schema })` devuelve una superficie reactiva con un slot -por dimension: +`createActivePrefs({ schema })` returns a reactive surface with one slot per +dimension: ```ts const prefs = createActivePrefs({ schema }); @@ -130,7 +132,7 @@ prefs.locale.onChange((locale) => {}); prefs.locale.catalog(); ``` -Tambien expone metodos genericos para adaptadores: +It also exposes generic methods for adapters: ```ts prefs.setIntent('locale', 'en-US'); @@ -142,30 +144,31 @@ prefs.subscribe((event) => {}); prefs.dispose(); ``` -Los servicios que reciben un `ActivePrefs` abierto y no conocen su schema en -tiempo de compilacion deben leer defensivamente: +Services that receive an open `ActivePrefs` and do not know its schema at +compile time should read defensively: ```ts const slot = readActivePrefsSlot(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. +If the slot does not exist, the consumer decides whether it can degrade or must +throw its own configuration error. ## Environment -El entorno entra por adaptadores. Ninguna dimension lee `window`, cookies, -headers, `localStorage` o DOM directamente. +The environment comes in through adapters. No dimension reads `window`, cookies, +headers, `localStorage` or the DOM directly. -Adaptadores disponibles: +Available adapters: - `detectServerEnvironment(input)` - `detectBrowserEnvironment(overrides?)` - `applyBrowserEnvironment(engine, overrides?)` -- `watchBrowserEnvironment(apply, overrides?)` — `apply` recibe el patch de entorno (`(patch) => void`); `overrides` solo `{ matchMedia }` +- `watchBrowserEnvironment(apply, overrides?)` — `apply` receives the environment + patch (`(patch) => void`); `overrides` is only `{ matchMedia }` -Ejemplos de entorno: +Environment examples: ```text Accept-Language -> language/locale candidates @@ -174,14 +177,14 @@ 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`. +`colorScheme` can exist in the environment because the browser exposes it, but +UIX does not turn it into `prefs.theme`; `ActiveEidos` can read the system +through its own `modeSource`. ## DOM Projection -`ActivePrefs` no escribe el DOM por si mismo. Si la app quiere atributos -globales, cablea el proyector: +`ActivePrefs` does not write the DOM by itself. If the app wants global +attributes, it wires the projector: ```ts const prefsProjection = createActivePrefsDomProjection({ @@ -190,10 +193,10 @@ const prefsProjection = createActivePrefsDomProjection({ }); ``` -El proyector es idempotente, se suscribe a los slots disponibles y limpia los -atributos que gestiono en `dispose()`. +The projector is idempotent, subscribes to the available slots and clears the +attributes it manages on `dispose()`. -Contrato de atributos: +Attribute contract: ```text prefs.direction -> dir @@ -202,11 +205,11 @@ prefs.sound -> data-sound prefs.haptic -> data-haptic ``` -No proyecta `data-theme`, `data-mode` ni `data-density`. +It does not project `data-theme`, `data-mode` or `data-density`. ## Eidos Boundary -Para una shell visual: +For a visual shell: ```ts const uix = createActiveUix({ langs, prefs: { schema } }); @@ -223,55 +226,55 @@ const eidos = ActiveEidos.create({ }); ``` -Regla practica: +Practical rule: ```text NO: uix.prefs.setIntent('theme', 'dark') -SI: modeSource notifica 'dark' a ActiveEidos +YES: modeSource notifies 'dark' to 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 +If a non-UIX app decides to declare its own visual dimension in `prefs`, that is +a local contract of that app. It must not leak into `ActiveUix`, Soma, Sema or Morfo. ## Storage -`createPrefsStorageBridge(...)` persiste intenciones, no valores efectivos: +`createPrefsStorageBridge(...)` persists intents, not effective values: ```ts const bridge = createPrefsStorageBridge({ engine, // EnginePrefs storage, // PrefsIntentStorage - onError: (error, op) => report(error, op), // opcional - skipHydrate: false // opcional (default false) + onError: (error, op) => report(error, op), // optional + skipHydrate: false // optional (default false) }); ``` -Reglas: +Rules: -- 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. +- Persist only `intent`. +- Do not persist `environment`. +- Do not persist `effective`. +- Do not write during hydrate unless explicitly configured. +- A storage failure must not corrupt in-memory preferences. ## Errors -Los errores publicos usan la familia `prefs::*`: +Public errors use the `prefs::*` family: - `prefs::unknown_dimension` - `prefs::intent_invalid` - `prefs::reserved_key` - `prefs::disposed` -Ejemplo conocido: +Known example: ```text 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`. +In UIX that error usually means a shell tried to write `prefs.theme`. The fix is +to pass the visual mode to `ActiveEidos`. ## Tests