docs(web): la pagina del sonido explica el sistema entero, no solo su uso

Segunda correccion del autor, y mas dura que la primera: «para ser una
pagina de documentacion me parece ridicula, no habla de los morfos, ni
nada de nada, ni la generacion de sonido, las utilidades, nada, la teoria,
el que se persigue... mezcla lo complejo con lo simple, como si el que la
lee supiera de donde viene todo».

Tenia razon: era una guia rapida disfrazada de documentacion. Enseñaba a
usar el sistema presuponiendo el ecosistema entero — mostraba un bloque
`semantic: { family, verb, intent }` sin haber dicho nunca que es un
morfo, ni donde vive, ni quien dispara el evento, ni como se convierte en
sonido audible.

REESCRITA COMO DOCUMENTO, ocho secciones que van de la teoria al oscilador:

1. QUE SE PERSIGUE — lo que faltaba entero. Un suceso es UNA cosa
   expresada por varios canales; de ahi los tres objetivos que explican
   todo lo demas: que la lectura sobreviva a perder un canal, que no
   fatigue, y que sea coherente — y por que eso ultimo obliga a quitarle
   al componente la libertad de elegir.
2. EL RECORRIDO — morfo declara → soma dispara → sema elige → $sound
   fabrica, con la ignorancia mutua entre capas explicada como decision.
3. EL MORFO — que es, donde vive, para que sirve, con la declaracion REAL
   del Toggle entera y las tres palabras (familia/verbo/intent) definidas
   con sus conjuntos cerrados completos.
4. SEMA — los tres canales (visual estampa atributos, sound, haptic) y las
   dos busquedas.
5. COMO SE FABRICA EL SONIDO — lo que no estaba y era la mitad del tema:
   dos osciladores a una quinta mezclados al 30%, el filtro paso-bajo que
   ES el brillo, la envolvente con sus topes y por que existen, el barrido
   de contorno de ±400 cents, y la aspereza como trémolo en serie. Mas
   como encaja un fichero de audio y su reserva.
6. INTERACTIVO — ahora muestra tambien la firma que recibe el motor.
7. LAS UTILIDADES — preferencias (full/reduce 0.4/off), memoria de
   frecuencia (umbral 3, −15% hasta suelo 25%, ventana 2s, exenciones),
   SILENT como retirada del canal, buses ui vs content, y la escala de
   ventanas perceptuales.
8. CUANDO UN COMPONENTE QUIERE OTRA COSA — el caso practico, al final.

Los datos siguen saliendo del codigo (SEMA_MAP, el resolver); las
constantes del motor citadas en prosa se verificaron leyendo la voz de
sema y las constantes del canal, no de memoria.

⚠️ Bug propio de la escritura: backticks anidados dentro de un literal de
plantilla rompian el SSR (500). Cazado al verificar, no despues.

VERIFICADO: las cuatro paginas responden 200 en SSR · check sin errores en
ninguna de ellas.

⚠️ Sigo sin poder MIRARLAS — el panel del navegador no compone en esta
sesion. La composicion visual queda pendiente de tu ojo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
alpha-0.1-dir-prefs
dev 2 months ago
parent 05d422ef4a
commit 4f1b9d4236

@ -1,11 +1,12 @@
<script lang="ts">
/**
* `/uix/docs/sound` — the entry point for the sound channel.
* `/uix/docs/sound` — the sound channel, end to end.
*
* Written for someone who has never seen this framework: it introduces the
* vocabulary (family, verb, intent) BEFORE using it, and only names the
* machinery once the reader needs the word. Tables are derived from the
* live `SEMA_MAP` and the real resolver, so they cannot go stale.
* For a developer who has never seen this framework: it goes from WHY a UI
* makes sound at all, through where an event is declared (morfo), who fires
* it (soma), who chooses the sound (sema) and how the sound is actually
* MADE (the Web Audio graph), to the policies that decide whether it is
* heard at all. Values come from the live map and the real resolver.
*/
import { getActiveUix } from '$active-uix';
import { SEMA_MAP, resolveSignature } from '$uix/sema';
@ -56,111 +57,259 @@
).sound;
});
function play(name?: string) {
function play() {
if (!stage) return;
void uix.events?.emit({
name: 'probe',
family: family as never,
target: stage,
...(verb ? { verb } : {}),
...(name ? { overrides: { sound: name } } : {}),
...(intent !== 'neutral' && !name ? { intent: intent as never } : {})
...(intent !== 'neutral' ? { intent: intent as never } : {})
});
}
</script>
<div data-uix-canvas-inner>
<div data-uix-eyebrow>Sema · el canal del sonido</div>
<h1 data-uix-page-title>Cómo suena una interfaz.</h1>
<h1 data-uix-page-title>El sonido, de principio a fin.</h1>
<p data-uix-page-lede>
Guardas un documento y oyes un pequeño repique. Lo borras y oyes algo más grave. Nadie escribió
«reproduce este fichero» en el botón de guardar: el componente sólo
<strong>describió lo que estaba pasando</strong>, y el sistema eligió el sonido. Esta página
explica cómo, desde cero.
«reproduce este fichero» en el botón de guardar. Esta página recorre el camino entero: por qué
una interfaz suena, dónde se declara lo que ocurre, quién elige el sonido, cómo se fabrica ese
sonido, y qué decide si llega a oírse.
</p>
<div bind:this={stage} data-uix-sound-stage aria-hidden="true"></div>
<!-- ── 1 ─────────────────────────────────────────────────────────────── -->
<section data-uix-section>
<h2 data-uix-section-title>1 · Un componente describe, no decide</h2>
<h2 data-uix-section-title>1 · Qué se persigue</h2>
<p data-uix-section-desc>
Cuando algo ocurre en un componente —se pulsa, se guarda, se abre un panel— ese componente <strong
>declara qué clase de suceso es</strong
>. No dice cómo suena, ni de qué color se pone, ni cómo se anima. Sólo lo describe, con tres
palabras:
El sonido aquí no es un adorno ni una recompensa. Parte de una idea sencilla: cuando algo
ocurre en una interfaz, ocurre <strong>una sola cosa</strong>, y esa cosa se puede expresar
por varios canales a la vez — se ve, se oye, se siente. El color, el movimiento, el sonido y
la vibración no son cuatro decisiones independientes: son cuatro caras del mismo suceso.
</p>
<p data-uix-section-desc>
De ahí salen tres objetivos concretos, y conviene tenerlos delante porque explican casi todas
las decisiones raras que verás después:
</p>
<ul data-uix-prose-list>
<li>
<strong>La familia</strong> — a qué clase de suceso pertenece. Hay
{FAMILIES.length}, y son un conjunto cerrado: <code>contact</code> (algo se ha tocado),
<code>commit</code>
(algo ha quedado hecho), <code>signal</code> (el sistema avisa), <code>emerge</code> (algo
aparece o desaparece),
<code>shift</code> (el marco cambia), <code>handle</code> (un gesto continuo), y dos más que no
suenan.
<strong>Que la lectura sobreviva a perder un canal.</strong> Si alguien tiene el sonido
apagado, o no puede ver bien, o usa un dispositivo sin vibración, la interfaz tiene que
seguir siendo comprensible. Ningún canal lleva información que no esté en otro sitio. El
sonido <em>refuerza</em>; no informa en exclusiva.
</li>
<li>
<strong>El verbo</strong> — qué se hizo exactamente dentro de esa familia:
<code>save</code>, <code>delete</code>, <code>open</code>, <code>close</code>…
<strong>Que no fatigue.</strong> Un sonido que se repite mucho deja de informar y empieza a molestar.
El sistema baja el volumen de lo repetitivo por su cuenta, y muchos sucesos frecuentes directamente
no suenan.
</li>
<li>
<strong>El intent</strong> — la carga: ¿salió bien, es arriesgado, es peligroso, se ha
perdido algo? Hay {INTENTS.length}, empezando por <code>neutral</code>.
<strong>Que sea coherente en toda la aplicación.</strong> Guardar debería sonar igual en cualquier
pantalla. Eso no se consigue pidiendo disciplina: se consigue quitando a los componentes la posibilidad
de elegir libremente.
</li>
</ul>
<p data-uix-section-desc>
Ese último punto es el que da forma a todo lo demás. Un componente <strong>no puede </strong>
decidir cómo suena, igual que no decide de qué color es un botón primario. Sólo declara
<em>qué está pasando</em>.
</p>
</section>
<!-- ── 2 ─────────────────────────────────────────────────────────────── -->
<section data-uix-section>
<h2 data-uix-section-title>2 · El recorrido completo</h2>
<p data-uix-section-desc>
El framework está en capas, y cada una hace una sola cosa. Un sonido atraviesa cuatro. Antes
de entrar en cada una, así se ve el camino entero:
</p>
<pre data-uix-code><code
>{`// El contrato de un componente. No hay ni un sonido aquí.
{
name: 'commit-save',
semantic: { family: 'commit', verb: 'save', intent: 'neutral' }
}`}</code
>{`morfo declara «este componente puede emitir un commit-toggle» (TypeScript, sin runtime)
soma dispara runtime.trigger('commit-toggle') (el proveedor, al pulsar)
sema elige familia+verbo → un nombre → el intent → un sonido (el motor semántico)
$sound fabrica osciladores → filtro → envolvente → altavoces (Web Audio)`}</code
></pre>
<p data-uix-section-desc>
Esa separación es la idea entera: el componente sabe <em>qué pasó</em>, y el sistema sabe
<em>a qué suena eso</em>. Cambiar la voz de toda la aplicación no obliga a tocar ni un
componente.
Cada capa ignora a la siguiente. El morfo no sabe que existe el sonido; sema no sabe qué es un
oscilador; el motor de audio no sabe qué es un «commit». Esa ignorancia mutua es deliberada:
es lo que permite cambiar la voz de la aplicación entera sin tocar un componente, y lo que
permite que el mismo suceso pinte un color en CSS sin que nadie coordine nada a mano.
</p>
</section>
<!-- ── 3 ─────────────────────────────────────────────────────────────── -->
<section data-uix-section>
<h2 data-uix-section-title>2 · El sistema elige — dos pasos</h2>
<h2 data-uix-section-title>3 · El morfo — dónde se declara lo que ocurre</h2>
<p data-uix-section-desc>
Cada componente de este framework tiene un <strong>morfo</strong>: un fichero de TypeScript
plano, sin runtime ni dependencias, que describe su contrato — sus partes, sus atributos ARIA,
su mapa de teclado y <strong>los sucesos que puede emitir</strong>. Vive en
<code>src/uix/morfo/components/&#123;nombre&#125;.ts</code>
y es la única fuente de verdad: soma, sema y la capa visual lo leen, nunca al revés.
</p>
<p data-uix-section-desc>
Esta es la declaración real del suceso de un <code>Toggle</code>, entera:
</p>
<pre data-uix-code><code
>{`// src/uix/morfo/components/toggle.ts
events: [
{
name: 'commit-toggle',
semantic: {
family: 'commit', // qué clase de suceso es
verb: 'toggle', // qué se hizo exactamente
target: v.partRef('provider'), // sobre qué parte del DOM ocurre
sequence: 'post', // antes o después del cambio de estado
intent: { // la carga, ligada a una prop pública
fromProp: 'intent',
default: 'neutral',
supported: ['neutral', 'affirm', 'risk', 'threat']
}
}
}
]`}</code
></pre>
<p data-uix-section-desc>Las tres palabras que importan para el sonido:</p>
<ul data-uix-prose-list>
<li>
<strong><code>family</code> — a qué clase de suceso pertenece.</strong> Son
{FAMILIES.length} y no se pueden inventar más:
<code>contact</code> (algo se ha tocado), <code>commit</code> (algo ha quedado hecho),
<code>signal</code>
(el sistema avisa por su cuenta), <code>emerge</code>
(algo aparece o desaparece), <code>shift</code> (cambia el marco: una pestaña, un paso),
<code>handle</code>
(un gesto continuo), <code>sustain</code> (algo está en curso) y <code>delegate</code> (reparto
de iniciativa con el sistema). Las dos últimas no suenan.
</li>
<li>
<strong><code>verb</code> — qué se hizo dentro de esa familia.</strong> También es un
conjunto cerrado por familia: un <code>commit</code> puede
<code>save</code>, <code>delete</code>, <code>submit</code>, <code>cancel</code>… pero no
<code>open</code>, que es de <code>emerge</code>.
</li>
<li>
<strong><code>intent</code> — la carga evaluativa.</strong> Son {INTENTS.length}:
<code>neutral</code>, <code>affirm</code> (va bien), <code>fulfill</code> (salió bien del
todo), <code>risk</code> (cuidado), <code>threat</code> (peligro) y
<code>loss</code> (se perdió algo). Fíjate en que aquí no es un valor fijo:
<code>fromProp: 'intent'</code> significa que lo decide quien usa el componente, con una prop.
</li>
</ul>
<p data-uix-section-desc>
Con esa descripción en la mano, el sistema hace <strong>dos búsquedas</strong> y nada más.
<strong>En ningún sitio de esa declaración aparece un sonido.</strong> Ni un fichero, ni un volumen,
ni una nota. El morfo describe el suceso; qué se oye es asunto de otra capa. Esa es exactamente
la separación que hace posible todo lo demás.
</p>
</section>
<!-- ── 4 ─────────────────────────────────────────────────────────────── -->
<section data-uix-section>
<h2 data-uix-section-title>4 · Sema — el motor que elige</h2>
<p data-uix-section-desc>
<strong>Primero, el nombre.</strong> Cada familia tiene una tabla que dice a qué suena, y
puede afinar por verbo. Un <code>commit</code> suena a
<code>tick</code>; un <code>emerge</code> suena a <code>open</code>, salvo que el verbo sea
<code>close</code>, en cuyo caso suena a <code>close</code>. Un componente normal no escribe
nada de esto: ya está en el sistema.
Cuando el usuario pulsa, el proveedor del componente (la capa <strong>soma</strong>) dispara
el suceso declarado: <code>runtime.trigger('commit-toggle')</code>. Eso llega a
<strong>sema</strong>, el motor semántico, que reparte la señal entre sus
<strong>canales</strong>:
</p>
<ul data-uix-prose-list>
<li>
<strong>visual</strong> — no pinta nada: estampa unos atributos (<code>data-event</code>,
<code>data-event-family</code>,
<code>data-event-intent</code>…) en el elemento durante un rato. La capa visual reacciona a
esos atributos desde CSS. Sema no sabe de colores.
</li>
<li><strong>sound</strong> — el que nos ocupa.</li>
<li>
<strong>haptic</strong> — la vibración, que funciona igual pero con un vocabulario
categórico (<code>tick</code>, <code>tap</code>, <code>pulse</code>…).
</li>
</ul>
<p data-uix-section-desc>
<strong>Después, el intent.</strong> Con el nombre ya elegido, se busca si existe una versión
de ese sonido para este intent. <code>tick</code> + <code>threat</code>
encuentra <code>tick.threat</code>, que es un sonido distinto: más grave, más rasposo y
descendente. Si esa versión <em>no</em> existe —por ejemplo
<code>tick.affirm</code>— el intent simplemente se ignora y suena el
<code>tick</code> normal. Y si no hubiera ni siquiera <code>tick</code>, no sonaría nada; el
silencio es una respuesta válida.
Para el sonido, sema hace <strong>dos búsquedas</strong> y nada más. Primero el
<strong>nombre</strong>: cada familia tiene una tabla que dice a qué suena, y puede afinarla
por verbo — un <code>commit</code> suena a <code>tick</code>; un
<code>emerge</code> suena a <code>open</code>, salvo que el verbo sea
<code>close</code>. Después el <strong>intent</strong>: se busca si existe una versión de ese
nombre para esta carga.
</p>
<pre data-uix-code><code
>{`nombre = la tabla de la familia, afinada por verbo
>{`nombre = familia[verbo] ?? familia.default
sonido = catálogo[nombre.intent] ?? catálogo[nombre] ?? nada`}</code
></pre>
<p data-uix-section-desc>
Lo importante de este diseño: <strong>nada se deforma</strong>. Un intent no «hace más grave»
un sonido — <em>elige otro</em>, diseñado entero por separado. Es como funciona el
reconocimiento: distinguimos sonidos, no desplazamientos de parámetro.
<code>tick</code> + <code>threat</code> encuentra <code>tick.threat</code>, que es
<strong>otro sonido</strong>: más grave, más áspero y descendente. Si esa versión no existe —<code
>tick.affirm</code
>, por ejemplo— el intent se ignora y suena el
<code>tick</code> normal. Si no hubiera ni <code>tick</code>, no sonaría nada.
</p>
<p data-uix-section-desc>
El detalle importante: <strong>nada se deforma</strong>. Un intent no «hace más grave» un
sonido, <em>elige otro</em>, diseñado entero por separado. Reconocemos sonidos —una puerta, un
cristal, un timbre—, no desplazamientos de parámetro; un sistema que modula un mismo tono
produce variantes que sobre el papel son distintas y al oído son la misma.
</p>
</section>
<!-- ── 5 ─────────────────────────────────────────────────────────────── -->
<section data-uix-section>
<h2 data-uix-section-title>3 · Míralo funcionar</h2>
<h2 data-uix-section-title>5 · Cómo se fabrica el sonido</h2>
<p data-uix-section-desc>
Elige una familia, un verbo y un intent, y observa las dos búsquedas. Para oírlo, enciende <em
>Sound</em
>
en el menú <em>Semantics</em> de la barra superior.
Hasta aquí sema ha elegido una <strong>firma</strong>: seis números y un contorno. El motor de
audio (<code>$sound</code>, un artefacto aparte que no sabe nada de semántica) los convierte
en sonido con Web Audio. El grafo es siempre el mismo:
</p>
<pre data-uix-code><code
>{`oscilador 1 (seno, a la altura "pitch") ─┐
oscilador 2 (seno, una quinta arriba) ─┴─→ filtro paso-bajo → envolvente → bus
mezclado al 30 % corte = "centroid" ADSR`}</code
></pre>
<ul data-uix-prose-list>
<li>
<strong>Dos osciladores, no uno.</strong> Un seno solo suena a pitido de test. El segundo,
una quinta por encima y mezclado al 30 %, le da cuerpo sin volverlo musical. Esa relación es
la <em>voz</em> del sistema y se puede cambiar entera.
</li>
<li>
<strong>El filtro es el brillo.</strong> <code>centroid</code> es la frecuencia de corte de un
paso-bajo: cuanto más alta, más armónicos pasan y más «cristalino» suena. Es lo que diferencia
un toque de cristal de un golpe sordo, con la misma nota.
</li>
<li>
<strong>La envolvente da la forma.</strong> Ataque, mantenimiento y caída. Está limitada a propósito
—el ataque nunca pasa del 15 % de la nota, la caída del 40 %— porque una nota muy corta con envolvente
cuadrada suena a clic digital y borra las diferencias de altura.
</li>
<li>
<strong>El contorno mueve la altura mientras suena.</strong> Subiendo, bajando o en arco, con
un barrido de ±400 cents (unos cuatro semitonos). Es suficiente para que un «abre» y un «cierra»
se distingan aunque duren 150 ms.
</li>
<li>
<strong>La aspereza es un trémolo.</strong> A partir de 0,2 se enciende una modulación de
amplitud, entre 30 y 180 Hz según lo áspero que sea. Va
<em>en serie</em>, multiplicando: por eso puede raspar sin subir el nivel. Es lo que hace
que un aviso se oiga como aviso y no como confirmación.
</li>
</ul>
<p data-uix-section-desc>
Una entrada del catálogo también puede ser un <strong>fichero</strong> (wav, mp3, ogg o aac) en
vez de una receta. Entonces el motor lo reproduce y sólo respeta su volumen — de un fichero no se
puede cambiar el tono. Si el fichero falla al cargar, suena la receta de reserva que acompaña a
toda entrada de ese tipo, así que un 404 no deja nada mudo.
</p>
</section>
<!-- ── 6 ─────────────────────────────────────────────────────────────── -->
<section data-uix-section>
<h2 data-uix-section-title>6 · Míralo funcionar</h2>
<p data-uix-section-desc>
Elige familia, verbo e intent y observa las dos búsquedas y la firma que sale. Para oírlo,
enciende <em>Sound</em> en el menú <em>Semantics</em> de la barra superior.
</p>
<div data-uix-controls>
@ -183,7 +332,7 @@ sonido = catálogo[nombre.intent] ?? catálogo[nombre] ?? nada`}</code
{#each INTENTS as i}<option value={i}>{i}</option>{/each}
</select>
</label>
<button type="button" data-uix-sound-chip onclick={() => play()}>
<button type="button" data-uix-sound-chip onclick={play}>
<span data-uix-sound-chip-name>oírlo</span>
<span data-uix-sound-chip-play>▶</span>
</button>
@ -195,7 +344,7 @@ sonido = catálogo[nombre.intent] ?? catálogo[nombre] ?? nada`}</code
<tr>
<th scope="row">
<span data-uix-sound-layer>Paso 1 — el nombre</span>
<span data-uix-sound-layer-note>lo dice la tabla de la familia</span>
<span data-uix-sound-layer-note>la tabla de la familia, afinada por verbo</span>
</th>
<td>{chosenName ?? 'esta familia no suena'}</td>
</tr>
@ -208,18 +357,22 @@ sonido = catálogo[nombre.intent] ?? catálogo[nombre] ?? nada`}</code
{#if !chosenEntry}
no suena nada
{:else if chosenEntry.includes('.')}
{chosenEntry} — sí, hay versión propia
{chosenEntry} — sí, versión propia
{:else}
{chosenEntry} — no hay versión, se ignora el intent
{/if}
</td>
</tr>
<tr>
<th scope="row"><span data-uix-sound-layer>Lo que se oye</span></th>
<th scope="row">
<span data-uix-sound-layer>La firma</span>
<span data-uix-sound-layer-note>lo que recibe el motor de audio</span>
</th>
<td>
{#if resolved}
{resolved.pitch} Hz · {resolved.duration} ms · {resolved.contour}
{#if resolved.roughness >= 0.2}· áspero{/if}
{resolved.pitch} Hz · brillo {resolved.centroid} · {resolved.duration} ms ·
{resolved.contour}
{#if resolved.roughness >= 0.2}· áspero ({resolved.roughness}){/if}
{:else}
silencio
{/if}
@ -230,19 +383,61 @@ sonido = catálogo[nombre.intent] ?? catálogo[nombre] ?? nada`}</code
</div>
<p data-uix-section-desc>
Tres combinaciones que enseñan la regla entera: <code>commit</code> +
<code>threat</code> (hay versión, suena otra cosa) · <code>commit</code> +
<code>affirm</code> (no hay versión, se ignora el intent) · <code>sustain</code>
(esa familia no suena, y está bien).
Tres casos que enseñan la regla entera: <code>commit</code> + <code>threat</code>
(hay versión, suena otra cosa) · <code>commit</code> + <code>affirm</code> (no hay versión, se
ignora el intent) · <code>sustain</code> (esa familia no suena, y está bien así).
</p>
</section>
<!-- ── 7 ─────────────────────────────────────────────────────────────── -->
<section data-uix-section>
<h2 data-uix-section-title>7 · Lo que decide si llega a oírse</h2>
<p data-uix-section-desc>
Elegir un sonido no significa reproducirlo. Entre la firma y el altavoz hay varias políticas,
y todas existen por el objetivo de no fatigar:
</p>
<ul data-uix-prose-list>
<li>
<strong>Preferencias.</strong> El canal admite tres niveles: <code>full</code>,
<code>reduce</code> (multiplica el volumen por 0,4) y <code>off</code> (no suena nada, y el
motor de audio ni se abre). Lo controla la aplicación — en esta documentación, el
interruptor <em>Sound</em> de la barra superior.
</li>
<li>
<strong>Memoria de frecuencia.</strong> A partir de la tercera repetición del mismo suceso
en dos segundos, el volumen baja un 15 % cada vez, hasta un suelo del 25 %. Una pausa lo
reinicia. Los sucesos de <code>threat</code> están exentos —una alarma no se cansa— y los
gestos continuos también, porque en ellos la repetición
<em>es</em> el mensaje.
</li>
<li>
<strong>Silencio declarado.</strong> Un componente puede decir <code>SILENT</code>
en lugar de un nombre. No es «volumen cero»: el canal se retira, no se abre contexto de audio,
no se calcula nada. Es una decisión de diseño explícita, y varios componentes muy frecuentes la
usan.
</li>
<li>
<strong>Buses.</strong> Los sonidos de interfaz van por un bus separado del audio de contenido
(un vídeo, un reproductor). Bajar el volumen de la interfaz no toca el trabajo del usuario, y
cuando suena contenido la interfaz se aparta sola.
</li>
<li>
<strong>Ventana perceptual.</strong> Cada suceso tiene una duración durante la que se
considera «vivo» — una escala con nombres (<code>glimpse</code> 120 ms,
<code>brief</code> 240, <code>settled</code> 400, <code>noticed</code> 600…). Es lo que mantiene
los atributos puestos para que el CSS reaccione, y coordina el sonido con lo que se ve.
</li>
</ul>
</section>
<!-- ── 8 ─────────────────────────────────────────────────────────────── -->
<section data-uix-section>
<h2 data-uix-section-title>4 · Cuando un componente quiere otra cosa</h2>
<h2 data-uix-section-title>8 · Cuando un componente quiere otra cosa</h2>
<p data-uix-section-desc>
A veces un componente necesita apartarse del sonido por defecto de su familia. Lo hace <strong
>nombrando otro</strong
>, en una línea:
>, en su fichero de sema (<code>src/uix/sema/components/&#123;nombre&#125;.ts</code>), con una
línea:
</p>
<pre data-uix-code><code
>{`// El botón de este componente no hace 'touch', hace 'snap'
@ -250,26 +445,27 @@ sonido = catálogo[nombre.intent] ?? catálogo[nombre] ?? nada`}</code
></pre>
<p data-uix-section-desc>
Lo que <strong>nunca</strong> hace —ni él, ni un tema, ni la aplicación— es escribir un
parámetro. No existe «súbele el tono 200 Hz». El tipo acepta un nombre del catálogo o
<code>SILENT</code>, y nada más. Si hace falta un sonido que no está, se
parámetro. No existe «súbele el tono 200 Hz»: el tipo acepta un nombre del catálogo o
<code>SILENT</code>, y el compilador rechaza lo demás. Si hace falta un sonido que no está, se
<a href="/uix/docs/sound/packs">añade al catálogo</a> con su nombre y luego se nombra.
</p>
<p data-uix-section-desc>
Por eso la mayoría de componentes del framework no tienen ninguna regla de sonido: el sistema
ya dice lo correcto, y sólo se escribe una línea cuando hay una decisión de verdad que tomar.
Por eso la mayoría de componentes no tienen ninguna regla de sonido: el sistema ya dice lo
correcto por familia y verbo, y sólo se escribe una línea cuando hay una decisión real que
tomar.
</p>
</section>
<section data-uix-section>
<h2 data-uix-section-title>Y a partir de aquí</h2>
<h2 data-uix-section-title>Seguir por aquí</h2>
<ul data-uix-prose-list>
<li>
<a href="/uix/docs/sound/catalogo"><strong>El catálogo</strong></a> — cada sonido que existe,
por qué suena así, y audible.
audible, y por qué suena así.
</li>
<li>
<a href="/uix/docs/sound/packs"><strong>Sound packs</strong></a> — traer tu propia voz:
síntesis, <code>.wav</code>, <code>.mp3</code>, y cómo registrar nombres nuevos.
síntesis, <code>.wav</code>, <code>.mp3</code>, y registrar nombres nuevos.
</li>
<li>
<a href="/uix/docs/sound/gestos"><strong>Gestos</strong></a> — por qué un arrastre suena por repetición

Loading…
Cancel
Save

Powered by TurnKey Linux.