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/web/routes/blocks/+layout@.svelte

261 lines
8.5 KiB

<script lang="ts">
demo(blocks): la sección tiene cromo de verdad — tema, idioma, dirección, densidad y catálogo La crítica era exacta: no se podía cambiar a oscuro, ni de idioma, ni había forma de moverse entre blocks. La sección arrancaba UIX y renderizaba la página desnuda, con un comentario que decía que el modo seguía al sistema operativo «para verificar oscuro emulando prefers-color-scheme» — cómodo para mí, inútil para quien mira la página. Ahora hay shell: topbar pegajosa con los ejes que de verdad mueven el framework (idioma ES/EN/FR/DE y dirección como INTENTS de prefs, así que llegan a langs, formatos y al AGENTS.md debug.log routes tsconfig.json CLAUDE.md docs scripts tsconfig.tsbuildinfo README.md node_modules src vite.config.ts STUMBLES.md package-lock.json static web audit package.json svelte.config.js worker.js build plans tmp workers del documento por el camino del sistema; densidad proyectada en :root para que la hereden también los overlays porteados; y el conmutador de tema, que alimenta el del ActiveEidos) y un raíl con el catálogo entero de los 27 blocks planificados, marcando el actual y atenuando los que aún no existen. Todo persiste en localStorage, así que una recarga te deja donde estabas. El catálogo pasa a : raíl y galería leen la MISMA fuente, y un block que aterriza no hay que recordarlo en dos sitios. Las tres regiones las coloca — cromo de app-land, el papel que hace en las docs de componentes; la regla de «sin CSS» del contrato B es para el tier (), no para el sitio que lo enseña. Dentro de esas regiones todo son componentes del canon. Verificado en navegador: el botón conmuta a oscuro de verdad (fondo oklch(0.1776 0 0)), el idioma llega a , el raíl lista los 27 y desaparece por debajo de 64rem sin desbordes, cero errores de consola. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
/**
* Blocks section shell. Boots UIX (createActiveUix → Soma → prefs projection
* → ActiveEidos) and puts REAL chrome around it: a rail with the catalog and
* a topbar whose axes actually drive the framework — colour mode, language,
* direction and density. Without them a demo can only be looked at, never
* exercised, which is the whole point of a demo.
*
* The three regions are placed by `blocks.css` (app-land chrome, the role
* `uix.css` plays for the component docs); everything inside them is a canon
* component, so the section dogfoods the framework like the rest of the site.
*/
import { onDestroy } from 'svelte';
demo(blocks): la sección tiene cromo de verdad — tema, idioma, dirección, densidad y catálogo La crítica era exacta: no se podía cambiar a oscuro, ni de idioma, ni había forma de moverse entre blocks. La sección arrancaba UIX y renderizaba la página desnuda, con un comentario que decía que el modo seguía al sistema operativo «para verificar oscuro emulando prefers-color-scheme» — cómodo para mí, inútil para quien mira la página. Ahora hay shell: topbar pegajosa con los ejes que de verdad mueven el framework (idioma ES/EN/FR/DE y dirección como INTENTS de prefs, así que llegan a langs, formatos y al AGENTS.md debug.log routes tsconfig.json CLAUDE.md docs scripts tsconfig.tsbuildinfo README.md node_modules src vite.config.ts STUMBLES.md package-lock.json static web audit package.json svelte.config.js worker.js build plans tmp workers del documento por el camino del sistema; densidad proyectada en :root para que la hereden también los overlays porteados; y el conmutador de tema, que alimenta el del ActiveEidos) y un raíl con el catálogo entero de los 27 blocks planificados, marcando el actual y atenuando los que aún no existen. Todo persiste en localStorage, así que una recarga te deja donde estabas. El catálogo pasa a : raíl y galería leen la MISMA fuente, y un block que aterriza no hay que recordarlo en dos sitios. Las tres regiones las coloca — cromo de app-land, el papel que hace en las docs de componentes; la regla de «sin CSS» del contrato B es para el tier (), no para el sitio que lo enseña. Dentro de esas regiones todo son componentes del canon. Verificado en navegador: el botón conmuta a oscuro de verdad (fondo oklch(0.1776 0 0)), el idioma llega a , el raíl lista los 27 y desaparece por debajo de 64rem sin desbordes, cero errores de consola. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
import { page } from '$app/state';
import { createActiveUix, setActiveUix } from '$active-uix';
feat(direction): la proyeccion prefs->DOM es del boot, y el DOM la siembra La proyeccion cross-modal (dir · lang · data-motion/sound/haptic sobre <html>) era cableado manual de cada app: solo 4 de ~13 boots de la demo la tenian, y el estampado per-componente del valor de prefs actuaba de SUSTITUTO de la proyeccion que faltaba. Para que el flip de P3 pueda soltar prefs del atributo, el ambiente tiene que llegar al DOM una vez y siempre: - createActiveUix la crea por defecto (opt-out `projectPrefs: false` cuando la app posee <html>: i18n por routing, proyeccion propia). En attach es opt-IN — el inverso — porque el host puede gobernar el documento. - Se dispone la PRIMERA en dispose(), antes de que prefs/dom mueran debajo. - Los 3 cableados manuales commiteables se borran EN ESTE MISMO commit (uix, blocks, BootUix); el de web/routes/alpha se edita en arbol pero no se commitea nunca (regla de la rama). Borrarlos junto a la automatica evita el agujero medido del doble montaje: dispose() BORRA los attrs gestionados sin restaurar. Y LA SEMILLA, que es lo que hace la automatica segura: sin ella, el boot REESCRIBIRIA un <html dir="rtl"> puesto a mano (el escenario 013ceac57) con el valor derivado del idioma. `readPrefsEnvironmentFromDom` lee <html dir> UNA vez al boot (SSR-safe: {} sin document; auto/vacio = "la pagina no declaro") y la dimension direction la honra con la precedencia: intent del usuario > semilla del entorno > derivacion(idioma) > default Una declaracion a nivel de pagina es una AFIRMACION explicita; el enlace por idioma es una heuristica — la afirmacion gana. Lo que pase la app en options.prefs.environment pisa la semilla. Un guard viejo fijaba el comportamiento anterior ("does not auto-project…") — es exactamente lo que la decision D2 (firmada 2026-08-05) revierte. Pasa a fijar el nuevo contrato: se proyectan SOLO los attrs cross-modales; data-theme/ mode/density siguen prohibidos (son de eidos). Tests: semilla y precedencia 6/6 (dom-environment.test.ts) · proyeccion automatica, opt-out, adopcion del dir a mano y limpieza en dispose (4 nuevos en active-uix.svelte.test.ts) · suites active-uix + prefs 79/79. Verificado en Chrome por el camino real: el toggle del topbar mueve <html dir> via prefs->proyeccion AUTOMATICA (ltr -> rtl -> auto) con el cableado manual ya borrado, en /uix y en /blocks; <html lang> viaja con el. check 77 = linea base · rtl:check 1 (palabras) · docs:check 0/566. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
import { standardPrefsDimensions } from '$prefs';
import { Soma } from '$soma/core/soma.svelte';
import { siumLangs } from '$sium/langs/langs';
import { secsLangs } from '$libs/secs';
feat(blocks): contact CERRADO — la coordinacion entra en el contrato B El estado se le pregunta al ESQUEMA, no al Form. `form.isValid` con `validationBehaviour: 'progressive'` significa «aun no se ha encontrado nada mal»: un formulario vacio e intacto no tiene errores, asi que se declaraba valido y `incomplete` era INALCANZABLE al cargar — la seccion pedia resolver la verificacion con los tres campos vacios, justo el hueco que la doctrina existe para cerrar. Ahora la raiz valida los valores con `validateSync` de SIUM: sincrono y sin escribir errores (`form.validate()` habria encendido los tres campos en rojo en el primer frame). `state.test.ts` fija la maquina con 9 casos — primer test unitario del tier, porque `state.ts` es su primera logica pura: el orden de prioridad, que solo `ready` deja enviar, y que todo estado bloqueado tiene frase. Contrato B (architecture/blocks.md): seccion «Coordination» con las tres cosas que posee un block que coordina (estado, forma de sus datos, palabras), B-5 admite el esquema por defecto, B-7 enmendado (posee las palabras de SUS estados como idlangref con fallback ingles) y la convencion de servicios acotada: el traductor es el UNICO servicio sancionado. Dice tambien a quien NO aplica — los blocks de layout no coordinan nada. i18n por la via real: el arnes registra el namespace `blocks` en las DOS cadenas de arranque (galeria y previews, que duplican el boot) y la demo se lee en castellano en vez de caer al fallback ingles. Ademas: README y pestanas de doc rehechos al reparto nuevo, ficha F2.13 y bitacora en PLAN-blocks.md, y el control vivo que faltaba para un prop publico — `verification` con reto / sin reto, porque omitir el prop no es lo mismo que pasar un estado que nunca verifica. Verificado en navegador, arco completo y las dos ramas: sin reto vacio → un campo → tres campos (se desbloquea) → «Enviando…» → «Enviado»; con reto, con los tres campos llenos el boton sigue bloqueado y la razon pasa a «Resuelve la verificacion de arriba». `verifying` y `rejected` quedan cubiertos por test unitario, no por el reto en vivo: el provider de ProofOfHuman escribe `status` el mismo y el veredicto del app no se sostiene (hilo de canon abierto). Gates: blocks:check verde (14 blocks) · vitest 9/9 · docs:check 0 · svelte-check sin errores propios · prettier limpio en los ficheros propios. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
import { blocksLangs } from './_lib/blocks-langs';
import { ActiveEidos } from '$uix/eidos';
fix(blocks): la galeria emite — packs de sema y canales perceptivos en el arnes El motor SI emitia: pulsar un boton de un block estampa `data-event=contact-activate` con `family=contact` y `phase=active`, porque el morfo del `Button` declara ese evento (verbo `activate`, secuencia `pre`, sin intent a proposito — una pulsacion no carga intencion evaluativa). Lo que faltaba era el otro lado: la seccion arrancaba UIX **sin `events` y con cero packs de sema**, frente a los 69 de `/uix`. El evento salia a una sala sin altavoces. Viene de F0, cuando el arranque se monto como «espejo minimo» de `/uix` y sema se quedo fuera. Un framework semantico cuya propia galeria no suena no esta demostrando lo que existe para demostrar, asi que los canales van ENCENDIDOS aqui: `sound` y `haptic` a true en las dos cadenas de arranque (galeria y previews). La lista de packs es DERIVADA del barrel (`Object.values(packs)`), no escrita a mano, y eso es deliberado: la lista a mano del otro arnes se quedo atras dos veces — `69c108d2a` tuvo que anadir «los 25 packs que faltaban en la composicion de demos». Una lista derivada no puede quedarse atras. El barrel recomienda importar solo los que usas por tree-shaking; aqui no aplica, porque esto no es un producto sino la galeria del framework y un block compone lo que quiera del canon. Verificado espiando la Web Audio API en un clic real dentro del block: al cargar 1 AudioContext y 0 fuentes; tras pulsar, 2 fuentes y 3 nodos de ganancia mas. Y las 15 demos cargan limpias con los 69 packs, cero errores de pagina. Gates: blocks:check verde (15) · svelte-check sin errores en lo tocado · prettier limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
import { semaPacks } from './_lib/sema-packs';
demo(blocks): la sección tiene cromo de verdad — tema, idioma, dirección, densidad y catálogo La crítica era exacta: no se podía cambiar a oscuro, ni de idioma, ni había forma de moverse entre blocks. La sección arrancaba UIX y renderizaba la página desnuda, con un comentario que decía que el modo seguía al sistema operativo «para verificar oscuro emulando prefers-color-scheme» — cómodo para mí, inútil para quien mira la página. Ahora hay shell: topbar pegajosa con los ejes que de verdad mueven el framework (idioma ES/EN/FR/DE y dirección como INTENTS de prefs, así que llegan a langs, formatos y al AGENTS.md debug.log routes tsconfig.json CLAUDE.md docs scripts tsconfig.tsbuildinfo README.md node_modules src vite.config.ts STUMBLES.md package-lock.json static web audit package.json svelte.config.js worker.js build plans tmp workers del documento por el camino del sistema; densidad proyectada en :root para que la hereden también los overlays porteados; y el conmutador de tema, que alimenta el del ActiveEidos) y un raíl con el catálogo entero de los 27 blocks planificados, marcando el actual y atenuando los que aún no existen. Todo persiste en localStorage, así que una recarga te deja donde estabas. El catálogo pasa a : raíl y galería leen la MISMA fuente, y un block que aterriza no hay que recordarlo en dos sitios. Las tres regiones las coloca — cromo de app-land, el papel que hace en las docs de componentes; la regla de «sin CSS» del contrato B es para el tier (), no para el sitio que lo enseña. Dentro de esas regiones todo son componentes del canon. Verificado en navegador: el botón conmuta a oscuro de verdad (fondo oklch(0.1776 0 0)), el idioma llega a , el raíl lista los 27 y desaparece por debajo de 64rem sin desbordes, cero errores de consola. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
import { Stack } from '$uix/eidos/components/stack';
import { Group } from '$uix/eidos/components/group';
import { Text } from '$uix/eidos/components/text';
import { Link } from '$uix/eidos/components/link';
import { Badge } from '$uix/eidos/components/badge';
import { Separator } from '$uix/eidos/components/separator';
import { ToggleGroup } from '$uix/eidos/components/toggle-group';
import { Button } from '$uix/eidos/components/button';
import * as Icon from '$uix/eidos/components/icon';
import { BLOCK_CATALOG } from './_lib/catalog';
import '@/uix/eidos/index.css';
feat(blocks): `newsletter` — el alta al boletín, y el reset que el framework daba por hecho F2.9. Slots de snippet (eyebrow · title · description · field · submit · note · children), `center` y `justified`, y `panel` como interruptor real: el panel de marca es la cara de las referencias, pero el control, la ayuda y el error están calibrados para la superficie de página, así que apagarlo es un estado de primera y no un fallback. La fila es `Grid templateColumns={{ base: '1fr', sm: '1fr auto' }}`: el campo se queda la pista libre y la acción abraza su contenido —eso es lo que hace que un alta se lea como UN gesto— y bajo `sm` pasa a una columna, porque un botón al lado de un campo de correo deja inservibles a los dos. El block NO valida y NO emite sema. `Form` posee el runtime, el esquema, dirty/ touched, la agregación de errores y el foco al primer error; `Field` posee el cableado ARIA; el morfo del `Form` ya declara `commit-submit` y `signal-invalid`. El app posee esquema, valores y handler. No hay ni un `if` sobre un correo aquí. ## Superación del dossier: la etiqueta El único hueco que el dossier nombraba era la nota de privacidad, y está. Pero la diferencia de verdad es la etiqueta: las referencias shipean la fila escondiéndola con `sr-only` o dejando solo un placeholder. El canon tiene `Field floatingLabel` —arranca dentro del control y sube al borde al enfocar o rellenar—, así que la fila queda alineada CON etiqueta real y asociada. Verificado con pulsaciones de teclado de verdad: 10px dentro en reposo → −11px sobre el borde al enfocar y al rellenar. ## Tres hallazgos de canon más, medidos - **La fundación de eidos no trae reset de modelo de caja y lo asume del app.** `[data-field-control]` declara `inline-size: 100%` + padding, así que bajo `content-box` el control mide 30px más que su contenedor: el campo se metía por debajo del botón de envío. Campo 480 / control 510 en la galería frente a 502 / 502 en los docs de componentes, que sí resetean (igual que `web/routes/active/styles.css`). Arreglado en app-land con `web/routes/blocks/_lib/reset.css`, con A/B sobre los 10 previews y 5 páginas de shell: cambia el newsletter y NADA más. Hay que importarlo dos veces porque la galería arranca UIX en línea en vez de pasar por `BootUix` — deuda del arnés, anotada en el handoff. - **`onValidSubmit` es un no-op silencioso** cuando se pasa un `form` ya construido: el componente solo lo reenvía al `createForm` que hace él mismo. El envío validaba, limpiaba el error y no anunciaba nada. Por eso el block no expone el prop: el handler va en el `createForm` del app. - **Los mensajes de SIUM son idlangref.** La vía correcta es `uix.langs.t(issue.message, issue.params)` —verificado, sale «Debe ser una dirección de correo válida»—, pero la demo de docs del propio `Form` parte la cadena a mano tras el `|`, así que el único ejemplo del repo enseña el patrón equivocado y siempre muestra inglés. Los tres quedan en los gaps del README y en `PLAN-blocks-quality.md` §6 (F18/F19/F20), sin tocar nada fuera del tier. Verificado en navegador el arco completo: correo inválido → error traducido con `role="alert"`, `aria-invalid`, `aria-describedby` y foco al primer error; correo válido → confirmación del app (`Callout` afirmativo con la dirección) y error limpio. Claro/oscuro/RTL, tres colores, `panel` sí/no, 420px. Cero errores de página. Gates: `blocks:check` verde (10 blocks) · `svelte-check` sin errores propios · prettier limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
// The box-model reset the section needs. Imported HERE as well as in `BootUix`
// because this shell boots UIX inline instead of going through it — the
// previews get it from `BootUix`, the gallery from this line.
import './_lib/reset.css';
demo(blocks): la sección tiene cromo de verdad — tema, idioma, dirección, densidad y catálogo La crítica era exacta: no se podía cambiar a oscuro, ni de idioma, ni había forma de moverse entre blocks. La sección arrancaba UIX y renderizaba la página desnuda, con un comentario que decía que el modo seguía al sistema operativo «para verificar oscuro emulando prefers-color-scheme» — cómodo para mí, inútil para quien mira la página. Ahora hay shell: topbar pegajosa con los ejes que de verdad mueven el framework (idioma ES/EN/FR/DE y dirección como INTENTS de prefs, así que llegan a langs, formatos y al AGENTS.md debug.log routes tsconfig.json CLAUDE.md docs scripts tsconfig.tsbuildinfo README.md node_modules src vite.config.ts STUMBLES.md package-lock.json static web audit package.json svelte.config.js worker.js build plans tmp workers del documento por el camino del sistema; densidad proyectada en :root para que la hereden también los overlays porteados; y el conmutador de tema, que alimenta el del ActiveEidos) y un raíl con el catálogo entero de los 27 blocks planificados, marcando el actual y atenuando los que aún no existen. Todo persiste en localStorage, así que una recarga te deja donde estabas. El catálogo pasa a : raíl y galería leen la MISMA fuente, y un block que aterriza no hay que recordarlo en dos sitios. Las tres regiones las coloca — cromo de app-land, el papel que hace en las docs de componentes; la regla de «sin CSS» del contrato B es para el tier (), no para el sitio que lo enseña. Dentro de esas regiones todo son componentes del canon. Verificado en navegador: el botón conmuta a oscuro de verdad (fondo oklch(0.1776 0 0)), el idioma llega a , el raíl lista los 27 y desaparece por debajo de 64rem sin desbordes, cero errores de consola. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
import './blocks.css';
let { children } = $props();
demo(blocks): la sección tiene cromo de verdad — tema, idioma, dirección, densidad y catálogo La crítica era exacta: no se podía cambiar a oscuro, ni de idioma, ni había forma de moverse entre blocks. La sección arrancaba UIX y renderizaba la página desnuda, con un comentario que decía que el modo seguía al sistema operativo «para verificar oscuro emulando prefers-color-scheme» — cómodo para mí, inútil para quien mira la página. Ahora hay shell: topbar pegajosa con los ejes que de verdad mueven el framework (idioma ES/EN/FR/DE y dirección como INTENTS de prefs, así que llegan a langs, formatos y al AGENTS.md debug.log routes tsconfig.json CLAUDE.md docs scripts tsconfig.tsbuildinfo README.md node_modules src vite.config.ts STUMBLES.md package-lock.json static web audit package.json svelte.config.js worker.js build plans tmp workers del documento por el camino del sistema; densidad proyectada en :root para que la hereden también los overlays porteados; y el conmutador de tema, que alimenta el del ActiveEidos) y un raíl con el catálogo entero de los 27 blocks planificados, marcando el actual y atenuando los que aún no existen. Todo persiste en localStorage, así que una recarga te deja donde estabas. El catálogo pasa a : raíl y galería leen la MISMA fuente, y un block que aterriza no hay que recordarlo en dos sitios. Las tres regiones las coloca — cromo de app-land, el papel que hace en las docs de componentes; la regla de «sin CSS» del contrato B es para el tier (), no para el sitio que lo enseña. Dentro de esas regiones todo son componentes del canon. Verificado en navegador: el botón conmuta a oscuro de verdad (fondo oklch(0.1776 0 0)), el idioma llega a , el raíl lista los 27 y desaparece por debajo de 64rem sin desbordes, cero errores de consola. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
type Language = 'es' | 'en' | 'fr' | 'de';
type Density = 'compact' | 'comfortable' | 'spacious';
const blocksPrefsSchema = standardPrefsDimensions({
languages: ['es', 'en', 'fr', 'de'] satisfies readonly Language[],
locales: ['es-ES', 'en-US', 'en-GB', 'de-DE'] as const,
currencies: ['EUR', 'USD', 'GBP', 'JPY'] as const,
defaults: { language: 'es', locale: 'es-ES', currency: 'EUR' }
});
const uix = createActiveUix({
feat(blocks): contact CERRADO — la coordinacion entra en el contrato B El estado se le pregunta al ESQUEMA, no al Form. `form.isValid` con `validationBehaviour: 'progressive'` significa «aun no se ha encontrado nada mal»: un formulario vacio e intacto no tiene errores, asi que se declaraba valido y `incomplete` era INALCANZABLE al cargar — la seccion pedia resolver la verificacion con los tres campos vacios, justo el hueco que la doctrina existe para cerrar. Ahora la raiz valida los valores con `validateSync` de SIUM: sincrono y sin escribir errores (`form.validate()` habria encendido los tres campos en rojo en el primer frame). `state.test.ts` fija la maquina con 9 casos — primer test unitario del tier, porque `state.ts` es su primera logica pura: el orden de prioridad, que solo `ready` deja enviar, y que todo estado bloqueado tiene frase. Contrato B (architecture/blocks.md): seccion «Coordination» con las tres cosas que posee un block que coordina (estado, forma de sus datos, palabras), B-5 admite el esquema por defecto, B-7 enmendado (posee las palabras de SUS estados como idlangref con fallback ingles) y la convencion de servicios acotada: el traductor es el UNICO servicio sancionado. Dice tambien a quien NO aplica — los blocks de layout no coordinan nada. i18n por la via real: el arnes registra el namespace `blocks` en las DOS cadenas de arranque (galeria y previews, que duplican el boot) y la demo se lee en castellano en vez de caer al fallback ingles. Ademas: README y pestanas de doc rehechos al reparto nuevo, ficha F2.13 y bitacora en PLAN-blocks.md, y el control vivo que faltaba para un prop publico — `verification` con reto / sin reto, porque omitir el prop no es lo mismo que pasar un estado que nunca verifica. Verificado en navegador, arco completo y las dos ramas: sin reto vacio → un campo → tres campos (se desbloquea) → «Enviando…» → «Enviado»; con reto, con los tres campos llenos el boton sigue bloqueado y la razon pasa a «Resuelve la verificacion de arriba». `verifying` y `rejected` quedan cubiertos por test unitario, no por el reto en vivo: el provider de ProofOfHuman escribe `status` el mismo y el veredicto del app no se sostiene (hilo de canon abierto). Gates: blocks:check verde (14 blocks) · vitest 9/9 · docs:check 0 · svelte-check sin errores propios · prettier limpio en los ficheros propios. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
langs: {
schema: { sium: siumLangs, secs: secsLangs, blocks: blocksLangs },
defaultLocale: 'es' as const
},
fix(blocks): la galeria emite — packs de sema y canales perceptivos en el arnes El motor SI emitia: pulsar un boton de un block estampa `data-event=contact-activate` con `family=contact` y `phase=active`, porque el morfo del `Button` declara ese evento (verbo `activate`, secuencia `pre`, sin intent a proposito — una pulsacion no carga intencion evaluativa). Lo que faltaba era el otro lado: la seccion arrancaba UIX **sin `events` y con cero packs de sema**, frente a los 69 de `/uix`. El evento salia a una sala sin altavoces. Viene de F0, cuando el arranque se monto como «espejo minimo» de `/uix` y sema se quedo fuera. Un framework semantico cuya propia galeria no suena no esta demostrando lo que existe para demostrar, asi que los canales van ENCENDIDOS aqui: `sound` y `haptic` a true en las dos cadenas de arranque (galeria y previews). La lista de packs es DERIVADA del barrel (`Object.values(packs)`), no escrita a mano, y eso es deliberado: la lista a mano del otro arnes se quedo atras dos veces — `69c108d2a` tuvo que anadir «los 25 packs que faltaban en la composicion de demos». Una lista derivada no puede quedarse atras. El barrel recomienda importar solo los que usas por tree-shaking; aqui no aplica, porque esto no es un producto sino la galeria del framework y un block compone lo que quiera del canon. Verificado espiando la Web Audio API en un clic real dentro del block: al cargar 1 AudioContext y 0 fuentes; tras pulsar, 2 fuentes y 3 nodos de ganancia mas. Y las 15 demos cargan limpias con los 69 packs, cero errores de pagina. Gates: blocks:check verde (15) · svelte-check sin errores en lo tocado · prettier limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
// The blocks tier is shown, not shipped: this is the gallery of a SEMANTIC
// framework, so the perceptual channels are on. Without this the engine
// still stamps `data-event-*` correctly and nothing is ever audible —
// which is how the section shipped from F0.
events: { sound: true, haptic: true, components: semaPacks },
demo(blocks): la sección tiene cromo de verdad — tema, idioma, dirección, densidad y catálogo La crítica era exacta: no se podía cambiar a oscuro, ni de idioma, ni había forma de moverse entre blocks. La sección arrancaba UIX y renderizaba la página desnuda, con un comentario que decía que el modo seguía al sistema operativo «para verificar oscuro emulando prefers-color-scheme» — cómodo para mí, inútil para quien mira la página. Ahora hay shell: topbar pegajosa con los ejes que de verdad mueven el framework (idioma ES/EN/FR/DE y dirección como INTENTS de prefs, así que llegan a langs, formatos y al AGENTS.md debug.log routes tsconfig.json CLAUDE.md docs scripts tsconfig.tsbuildinfo README.md node_modules src vite.config.ts STUMBLES.md package-lock.json static web audit package.json svelte.config.js worker.js build plans tmp workers del documento por el camino del sistema; densidad proyectada en :root para que la hereden también los overlays porteados; y el conmutador de tema, que alimenta el del ActiveEidos) y un raíl con el catálogo entero de los 27 blocks planificados, marcando el actual y atenuando los que aún no existen. Todo persiste en localStorage, así que una recarga te deja donde estabas. El catálogo pasa a : raíl y galería leen la MISMA fuente, y un block que aterriza no hay que recordarlo en dos sitios. Las tres regiones las coloca — cromo de app-land, el papel que hace en las docs de componentes; la regla de «sin CSS» del contrato B es para el tier (), no para el sitio que lo enseña. Dentro de esas regiones todo son componentes del canon. Verificado en navegador: el botón conmuta a oscuro de verdad (fondo oklch(0.1776 0 0)), el idioma llega a , el raíl lista los 27 y desaparece por debajo de 64rem sin desbordes, cero errores de consola. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
prefs: { schema: blocksPrefsSchema }
});
setActiveUix(uix);
Soma.create();
demo(blocks): la sección tiene cromo de verdad — tema, idioma, dirección, densidad y catálogo La crítica era exacta: no se podía cambiar a oscuro, ni de idioma, ni había forma de moverse entre blocks. La sección arrancaba UIX y renderizaba la página desnuda, con un comentario que decía que el modo seguía al sistema operativo «para verificar oscuro emulando prefers-color-scheme» — cómodo para mí, inútil para quien mira la página. Ahora hay shell: topbar pegajosa con los ejes que de verdad mueven el framework (idioma ES/EN/FR/DE y dirección como INTENTS de prefs, así que llegan a langs, formatos y al AGENTS.md debug.log routes tsconfig.json CLAUDE.md docs scripts tsconfig.tsbuildinfo README.md node_modules src vite.config.ts STUMBLES.md package-lock.json static web audit package.json svelte.config.js worker.js build plans tmp workers del documento por el camino del sistema; densidad proyectada en :root para que la hereden también los overlays porteados; y el conmutador de tema, que alimenta el del ActiveEidos) y un raíl con el catálogo entero de los 27 blocks planificados, marcando el actual y atenuando los que aún no existen. Todo persiste en localStorage, así que una recarga te deja donde estabas. El catálogo pasa a : raíl y galería leen la MISMA fuente, y un block que aterriza no hay que recordarlo en dos sitios. Las tres regiones las coloca — cromo de app-land, el papel que hace en las docs de componentes; la regla de «sin CSS» del contrato B es para el tier (), no para el sitio que lo enseña. Dentro de esas regiones todo son componentes del canon. Verificado en navegador: el botón conmuta a oscuro de verdad (fondo oklch(0.1776 0 0)), el idioma llega a , el raíl lista los 27 y desaparece por debajo de 64rem sin desbordes, cero errores de consola. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
// ── Axes ──────────────────────────────────────────────────────────────
let mode = $state<'light' | 'dark'>('light');
demo(blocks): la sección tiene cromo de verdad — tema, idioma, dirección, densidad y catálogo La crítica era exacta: no se podía cambiar a oscuro, ni de idioma, ni había forma de moverse entre blocks. La sección arrancaba UIX y renderizaba la página desnuda, con un comentario que decía que el modo seguía al sistema operativo «para verificar oscuro emulando prefers-color-scheme» — cómodo para mí, inútil para quien mira la página. Ahora hay shell: topbar pegajosa con los ejes que de verdad mueven el framework (idioma ES/EN/FR/DE y dirección como INTENTS de prefs, así que llegan a langs, formatos y al AGENTS.md debug.log routes tsconfig.json CLAUDE.md docs scripts tsconfig.tsbuildinfo README.md node_modules src vite.config.ts STUMBLES.md package-lock.json static web audit package.json svelte.config.js worker.js build plans tmp workers del documento por el camino del sistema; densidad proyectada en :root para que la hereden también los overlays porteados; y el conmutador de tema, que alimenta el del ActiveEidos) y un raíl con el catálogo entero de los 27 blocks planificados, marcando el actual y atenuando los que aún no existen. Todo persiste en localStorage, así que una recarga te deja donde estabas. El catálogo pasa a : raíl y galería leen la MISMA fuente, y un block que aterriza no hay que recordarlo en dos sitios. Las tres regiones las coloca — cromo de app-land, el papel que hace en las docs de componentes; la regla de «sin CSS» del contrato B es para el tier (), no para el sitio que lo enseña. Dentro de esas regiones todo son componentes del canon. Verificado en navegador: el botón conmuta a oscuro de verdad (fondo oklch(0.1776 0 0)), el idioma llega a , el raíl lista los 27 y desaparece por debajo de 64rem sin desbordes, cero errores de consola. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
let language = $state<Language>('es');
let dir = $state<'ltr' | 'rtl'>('ltr');
let density = $state<Density>('comfortable');
const DENSITY_SCALES: Record<Density, { space: number; control: number }> = {
compact: { space: 0.85, control: 0.9 },
comfortable: { space: 1, control: 1 },
spacious: { space: 1.15, control: 1.1 }
};
const modeListeners = new Set<(value: 'light' | 'dark') => void>();
const activeEidos = ActiveEidos.create({
applyDom: true,
theme: 'base',
modeSource: {
get: () => mode,
onChange(handler) {
modeListeners.add(handler);
return () => modeListeners.delete(handler);
}
}
});
demo(blocks): la sección tiene cromo de verdad — tema, idioma, dirección, densidad y catálogo La crítica era exacta: no se podía cambiar a oscuro, ni de idioma, ni había forma de moverse entre blocks. La sección arrancaba UIX y renderizaba la página desnuda, con un comentario que decía que el modo seguía al sistema operativo «para verificar oscuro emulando prefers-color-scheme» — cómodo para mí, inútil para quien mira la página. Ahora hay shell: topbar pegajosa con los ejes que de verdad mueven el framework (idioma ES/EN/FR/DE y dirección como INTENTS de prefs, así que llegan a langs, formatos y al AGENTS.md debug.log routes tsconfig.json CLAUDE.md docs scripts tsconfig.tsbuildinfo README.md node_modules src vite.config.ts STUMBLES.md package-lock.json static web audit package.json svelte.config.js worker.js build plans tmp workers del documento por el camino del sistema; densidad proyectada en :root para que la hereden también los overlays porteados; y el conmutador de tema, que alimenta el del ActiveEidos) y un raíl con el catálogo entero de los 27 blocks planificados, marcando el actual y atenuando los que aún no existen. Todo persiste en localStorage, así que una recarga te deja donde estabas. El catálogo pasa a : raíl y galería leen la MISMA fuente, y un block que aterriza no hay que recordarlo en dos sitios. Las tres regiones las coloca — cromo de app-land, el papel que hace en las docs de componentes; la regla de «sin CSS» del contrato B es para el tier (), no para el sitio que lo enseña. Dentro de esas regiones todo son componentes del canon. Verificado en navegador: el botón conmuta a oscuro de verdad (fondo oklch(0.1776 0 0)), el idioma llega a , el raíl lista los 27 y desaparece por debajo de 64rem sin desbordes, cero errores de consola. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
// Restore once: a stored choice wins, otherwise the OS scheme decides.
$effect(() => {
if (typeof localStorage === 'undefined') return;
const stored = localStorage.getItem('uix-blocks-prefs');
if (stored) {
try {
const blob = JSON.parse(stored);
if (blob.mode === 'dark' || blob.mode === 'light') mode = blob.mode;
if (blob.language) language = blob.language;
if (blob.dir) dir = blob.dir;
if (blob.density) density = blob.density;
return;
} catch {
/* ignore a corrupt blob */
}
}
mode = window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
});
$effect(() => {
if (typeof localStorage === 'undefined') return;
localStorage.setItem('uix-blocks-prefs', JSON.stringify({ mode, language, dir, density }));
});
$effect(() => {
demo(blocks): la sección tiene cromo de verdad — tema, idioma, dirección, densidad y catálogo La crítica era exacta: no se podía cambiar a oscuro, ni de idioma, ni había forma de moverse entre blocks. La sección arrancaba UIX y renderizaba la página desnuda, con un comentario que decía que el modo seguía al sistema operativo «para verificar oscuro emulando prefers-color-scheme» — cómodo para mí, inútil para quien mira la página. Ahora hay shell: topbar pegajosa con los ejes que de verdad mueven el framework (idioma ES/EN/FR/DE y dirección como INTENTS de prefs, así que llegan a langs, formatos y al AGENTS.md debug.log routes tsconfig.json CLAUDE.md docs scripts tsconfig.tsbuildinfo README.md node_modules src vite.config.ts STUMBLES.md package-lock.json static web audit package.json svelte.config.js worker.js build plans tmp workers del documento por el camino del sistema; densidad proyectada en :root para que la hereden también los overlays porteados; y el conmutador de tema, que alimenta el del ActiveEidos) y un raíl con el catálogo entero de los 27 blocks planificados, marcando el actual y atenuando los que aún no existen. Todo persiste en localStorage, así que una recarga te deja donde estabas. El catálogo pasa a : raíl y galería leen la MISMA fuente, y un block que aterriza no hay que recordarlo en dos sitios. Las tres regiones las coloca — cromo de app-land, el papel que hace en las docs de componentes; la regla de «sin CSS» del contrato B es para el tier (), no para el sitio que lo enseña. Dentro de esas regiones todo son componentes del canon. Verificado en navegador: el botón conmuta a oscuro de verdad (fondo oklch(0.1776 0 0)), el idioma llega a , el raíl lista los 27 y desaparece por debajo de 64rem sin desbordes, cero errores de consola. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
const current = mode;
for (const listener of modeListeners) listener(current);
});
demo(blocks): la sección tiene cromo de verdad — tema, idioma, dirección, densidad y catálogo La crítica era exacta: no se podía cambiar a oscuro, ni de idioma, ni había forma de moverse entre blocks. La sección arrancaba UIX y renderizaba la página desnuda, con un comentario que decía que el modo seguía al sistema operativo «para verificar oscuro emulando prefers-color-scheme» — cómodo para mí, inútil para quien mira la página. Ahora hay shell: topbar pegajosa con los ejes que de verdad mueven el framework (idioma ES/EN/FR/DE y dirección como INTENTS de prefs, así que llegan a langs, formatos y al AGENTS.md debug.log routes tsconfig.json CLAUDE.md docs scripts tsconfig.tsbuildinfo README.md node_modules src vite.config.ts STUMBLES.md package-lock.json static web audit package.json svelte.config.js worker.js build plans tmp workers del documento por el camino del sistema; densidad proyectada en :root para que la hereden también los overlays porteados; y el conmutador de tema, que alimenta el del ActiveEidos) y un raíl con el catálogo entero de los 27 blocks planificados, marcando el actual y atenuando los que aún no existen. Todo persiste en localStorage, así que una recarga te deja donde estabas. El catálogo pasa a : raíl y galería leen la MISMA fuente, y un block que aterriza no hay que recordarlo en dos sitios. Las tres regiones las coloca — cromo de app-land, el papel que hace en las docs de componentes; la regla de «sin CSS» del contrato B es para el tier (), no para el sitio que lo enseña. Dentro de esas regiones todo son componentes del canon. Verificado en navegador: el botón conmuta a oscuro de verdad (fondo oklch(0.1776 0 0)), el idioma llega a , el raíl lista los 27 y desaparece por debajo de 64rem sin desbordes, cero errores de consola. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
// Language and direction are PREFS intents: they reach langs, formats and the
// document direction through the framework, not through local wiring.
$effect(() => {
uix.prefs.setIntent('language', language);
uix.prefs.setIntent('direction', dir);
});
// Density on :root, so portaled overlays get it too.
$effect(() => {
const d = DENSITY_SCALES[density];
uix.dom.writeStyle(
'uix-blocks-display',
`:root{--density-space-scale:${d.space};--density-control-scale:${d.control};}`
);
});
const currentSlug = $derived(page.url.pathname.replace(/^\/blocks\/?/, ''));
onDestroy(() => {
activeEidos.dispose();
uix.dispose();
});
</script>
<svelte:head>
<title>Blocks — UIX</title>
<meta name="color-scheme" content="light dark" />
</svelte:head>
demo(blocks): la sección tiene cromo de verdad — tema, idioma, dirección, densidad y catálogo La crítica era exacta: no se podía cambiar a oscuro, ni de idioma, ni había forma de moverse entre blocks. La sección arrancaba UIX y renderizaba la página desnuda, con un comentario que decía que el modo seguía al sistema operativo «para verificar oscuro emulando prefers-color-scheme» — cómodo para mí, inútil para quien mira la página. Ahora hay shell: topbar pegajosa con los ejes que de verdad mueven el framework (idioma ES/EN/FR/DE y dirección como INTENTS de prefs, así que llegan a langs, formatos y al AGENTS.md debug.log routes tsconfig.json CLAUDE.md docs scripts tsconfig.tsbuildinfo README.md node_modules src vite.config.ts STUMBLES.md package-lock.json static web audit package.json svelte.config.js worker.js build plans tmp workers del documento por el camino del sistema; densidad proyectada en :root para que la hereden también los overlays porteados; y el conmutador de tema, que alimenta el del ActiveEidos) y un raíl con el catálogo entero de los 27 blocks planificados, marcando el actual y atenuando los que aún no existen. Todo persiste en localStorage, así que una recarga te deja donde estabas. El catálogo pasa a : raíl y galería leen la MISMA fuente, y un block que aterriza no hay que recordarlo en dos sitios. Las tres regiones las coloca — cromo de app-land, el papel que hace en las docs de componentes; la regla de «sin CSS» del contrato B es para el tier (), no para el sitio que lo enseña. Dentro de esas regiones todo son componentes del canon. Verificado en navegador: el botón conmuta a oscuro de verdad (fondo oklch(0.1776 0 0)), el idioma llega a , el raíl lista los 27 y desaparece por debajo de 64rem sin desbordes, cero errores de consola. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
<div data-blocks-shell data-theme={mode} data-mode={mode}>
<header data-blocks-topbar>
<Group align="center" gap={3} justify="start">
<Link href="/blocks" variant="plain" underline="none">
<Group align="center" gap={2} justify="start">
<Icon.Boxes size="sm" />
<Text weight="semibold">Blocks</Text>
</Group>
</Link>
<Badge size="xs" variant="soft">tier</Badge>
</Group>
<Group align="center" gap={4} justify="end">
<Group align="center" gap={2} justify="end">
<Text size="xs" color="muted">idioma</Text>
<ToggleGroup
selectionMode="single"
size="sm"
attached
value={[language]}
onValueChange={(v) => (language = (v[0] ?? language) as Language)}
aria-label="Idioma"
>
{#each ['es', 'en', 'fr', 'de'] as value (value)}
<ToggleGroup.Item {value}>{value.toUpperCase()}</ToggleGroup.Item>
{/each}
</ToggleGroup>
</Group>
<Group align="center" gap={2} justify="end">
<Text size="xs" color="muted">densidad</Text>
<ToggleGroup
selectionMode="single"
size="sm"
attached
value={[density]}
onValueChange={(v) => (density = (v[0] ?? density) as Density)}
aria-label="Densidad"
>
<ToggleGroup.Item value="compact">−</ToggleGroup.Item>
<ToggleGroup.Item value="comfortable">·</ToggleGroup.Item>
<ToggleGroup.Item value="spacious">+</ToggleGroup.Item>
</ToggleGroup>
</Group>
<Group align="center" gap={2} justify="end">
<Text size="xs" color="muted">dir</Text>
<ToggleGroup
selectionMode="single"
size="sm"
attached
value={[dir]}
onValueChange={(v) => (dir = (v[0] ?? dir) as 'ltr' | 'rtl')}
aria-label="Dirección"
>
<ToggleGroup.Item value="ltr">LTR</ToggleGroup.Item>
<ToggleGroup.Item value="rtl">RTL</ToggleGroup.Item>
</ToggleGroup>
</Group>
<Button
size="sm"
variant="ghost"
aria-label={mode === 'light' ? 'Cambiar a modo oscuro' : 'Cambiar a modo claro'}
onclick={() => (mode = mode === 'light' ? 'dark' : 'light')}
>
{#if mode === 'light'}
<Icon.Moon size="sm" />
{:else}
<Icon.Sun size="sm" />
{/if}
</Button>
</Group>
</header>
<div data-blocks-body>
<nav data-blocks-rail aria-label="Catálogo de blocks">
<Stack gap={4}>
{#each BLOCK_CATALOG as group, i (group.title)}
<Stack gap={2}>
<Text size="xs" weight="medium" color="muted">{group.title}</Text>
<Stack gap={1}>
{#each group.items as item (item.slug)}
{#if item.shipped}
<Link href={`/blocks/${item.slug}`} variant="plain" underline="none">
<Text
size="sm"
weight={currentSlug === item.slug ? 'semibold' : 'regular'}
color={currentSlug === item.slug ? 'primary' : 'secondary'}
>
{item.label}
</Text>
</Link>
{:else}
<Text size="sm" color="disabled">{item.label}</Text>
{/if}
{/each}
</Stack>
</Stack>
{#if i < BLOCK_CATALOG.length - 1}
<Separator />
{/if}
{/each}
</Stack>
</nav>
<div data-blocks-content {dir}>
{@render children?.()}
</div>
</div>
</div>

Powered by TurnKey Linux.