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/uix/sema/README.md

210 lines
6.3 KiB

# Sema
`Sema` define el dominio semántico canónico de UIX y orquesta la emisión de
señales perceptivas.
## Qué es
- familias canónicas: `contact`, `commit`, `alert`, `handle`, `emerge`, `sustain`
- intents canónicos: `neutral`, `affirm`, `fulfill`, `risk`, `threat`
- normalización entre shape estructurado y label canónico
- validación mínima del dominio
- `EngineSemantic` como canalizador de ocurrencias
`Sema` no decide qué evento ocurrió. El provider lo decide. `EngineSemantic`
recibe la ocurrencia y orquesta su materialización en el canal perceptivo
visual (DOM).
## Qué ya no es
`Sema` ya no es un runtime multimodal.
No contiene:
- resolver de canales
- sound/motion/color/presence engines
- mapa perceptivo por canal
- política global de accesibilidad por canal
Eso pertenece a capas futuras y separadas:
- `EngineSemantic` publica ocurrencias semánticas
- `ActiveDom` materializa esas ocurrencias como `data-event*` en el DOM
- `SoundEngine`, `VibraEngine` y otros engines modales se suscribirán al engine
## El contrato `emit`
```ts
semantic.emit(event: SemanticEvent): Promise<void>
```
Una sola firma. Cubre los tres escenarios cuando se compone con `dom.apply`:
```ts
// Cambio estructural sin señal
dom.apply(change)
// Cambio estructural con señal
await semantic.emit(event)
dom.apply(change)
// Señal sin cambio estructural
void semantic.emit(event)
```
### Semántica de la Promise
`emit(event)` resuelve cuando:
- la señal `data-event*` ya fue escrita al DOM
- ha pasado **un rAF** para que CSS pueda observarla y arrancar transitions
- todavía está visible en el DOM
No resuelve antes (no hay frame para que CSS reaccione) ni después de la
limpieza (la señal ya no estaría visible cuando el commit estructural entre).
### Ciclo de vida interno de `emit`
```
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
1. Engine genera id/session de la ocurrencia
2. Engine despacha la señal a TODOS los canales registrados
- canales no-visuales (sound, vibra) → fire-and-forget (no awaited)
- canal visual → awaited
3. Engine resuelve la Promise cuando el visual ha terminado
(semántica secuencial estricta: cleanup ANTES del resolve)
```
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
### Canales como módulos
sema: perceptually-anchored hold + sequential strict emit semantics Two architectural fixes surfaced by the eidos demo: 1. The previous default hold (1 rAF + 1 hold frame, ~30ms) was a technical timing decision — invisible to humans. Signals must be anchored to perceptual scales so they can actually be perceived. 2. The previous lifecycle resolved emit AFTER 1 rAF (caller's structural change ran in parallel with cleanup, ~30ms later). That's an implicit parallelism between event and state. The author has no way to say "wait for the announcement to be perceived before changing state". Sequential strict semantics (NEW) - emit writes the signal, awaits the full author-specified hold, cleans up, then resolves. State change happens AFTER cleanup — strictly sequential. - `signal.hold` is in milliseconds anchored to `SEMA_PERCEPTION` scales: subliminal 50ms below conscious threshold glimpse 120ms minimum perceptible without effort brief 240ms comfortable acknowledgement noticed 600ms sustained signal insistent 1200ms demands attention persistent 3000ms until acknowledged - Defaults per family in `SEMA_HOLD_DEFAULTS`: emerge: brief sustain: noticed contact: glimpse commit: brief alert: noticed handle: brief - Authors override per event via `SemanticSignal.hold` based on: - their CSS animation duration (so it completes before cleanup) - the perceptual scale they want - critical attention requirements (use insistent/persistent) Files - src/uix/sema/perception.ts (new) — SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS constants with rationale anchored on Bloch's law, Material/Apple HIG motion guidelines, toast UX literature. - src/uix/sema/signal.ts — `hold` JSDoc updated to explain ms semantics + defaults + override pattern. - src/uix/sema/engine.ts — emit() refactored from rAF chain to setTimeout(holdMs); cleanup happens BEFORE resolve (sequential strict); `nextFrame()` helper removed; `resolveHoldMs(signal)` added with the 3-tier fallback (signal.hold → family default → SEMA_PERCEPTION.brief). - src/uix/sema/exports.ts — re-exports SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS + SemaPerception type. - src/uix/sema/emit.test.ts — rewritten around fakeTimers + setTimeout (no more rAF queue); new test verifies cleanup runs BEFORE resolve. 7/7 tests green. - src/uix/sema/README.md — new "Duración del signal" section with the full perception scale + per-family defaults tables + override pattern. - src/uix/eidos/events.css — comment updated to explain the new hold windows; rule `[data-event^='dismiss']` etc. clarified. - src/uix/active_architecture.md — replaced "~13ms" prose with explicit perception-anchored scales; added explanation of "perceptual by design, not framerate". - src/uix/soma/components/toast/toast-provider.svelte.ts — new $effect that fires `present` + `announce` events when item ref attaches (one-shot guarded), so toasts emit signals on appear. The flash on /test/eidos is now visible thanks to (a) the perceptual hold and (b) the wired events. Channel parallelism note - Hold is the DOM channel duration only. - Future sound / vibra / motion engines subscribe to sema events independently; each owns its own timing relative to the event id. - Sema does not coordinate cross-channel sync — that's an integrator decision per app. Verification - 22/22 sema tests green. - 22/22 morfo runtime tests green. - 66/66 morfo-check + 68/68 smoke (verified before commit). - Probe shows announce signal on toast appear lasting ~600ms (alert family default) — visible in DevTools attribute panel. - svelte-check unchanged at 155. Pre-existing failure in src/uix/air/internal/canvas/layout.test.ts (`bun:test` import) is in the dead-branch air layer and unrelated.
6 months ago
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
Sema está organizada en canales perceptivos simétricos:
sema: perceptually-anchored hold + sequential strict emit semantics Two architectural fixes surfaced by the eidos demo: 1. The previous default hold (1 rAF + 1 hold frame, ~30ms) was a technical timing decision — invisible to humans. Signals must be anchored to perceptual scales so they can actually be perceived. 2. The previous lifecycle resolved emit AFTER 1 rAF (caller's structural change ran in parallel with cleanup, ~30ms later). That's an implicit parallelism between event and state. The author has no way to say "wait for the announcement to be perceived before changing state". Sequential strict semantics (NEW) - emit writes the signal, awaits the full author-specified hold, cleans up, then resolves. State change happens AFTER cleanup — strictly sequential. - `signal.hold` is in milliseconds anchored to `SEMA_PERCEPTION` scales: subliminal 50ms below conscious threshold glimpse 120ms minimum perceptible without effort brief 240ms comfortable acknowledgement noticed 600ms sustained signal insistent 1200ms demands attention persistent 3000ms until acknowledged - Defaults per family in `SEMA_HOLD_DEFAULTS`: emerge: brief sustain: noticed contact: glimpse commit: brief alert: noticed handle: brief - Authors override per event via `SemanticSignal.hold` based on: - their CSS animation duration (so it completes before cleanup) - the perceptual scale they want - critical attention requirements (use insistent/persistent) Files - src/uix/sema/perception.ts (new) — SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS constants with rationale anchored on Bloch's law, Material/Apple HIG motion guidelines, toast UX literature. - src/uix/sema/signal.ts — `hold` JSDoc updated to explain ms semantics + defaults + override pattern. - src/uix/sema/engine.ts — emit() refactored from rAF chain to setTimeout(holdMs); cleanup happens BEFORE resolve (sequential strict); `nextFrame()` helper removed; `resolveHoldMs(signal)` added with the 3-tier fallback (signal.hold → family default → SEMA_PERCEPTION.brief). - src/uix/sema/exports.ts — re-exports SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS + SemaPerception type. - src/uix/sema/emit.test.ts — rewritten around fakeTimers + setTimeout (no more rAF queue); new test verifies cleanup runs BEFORE resolve. 7/7 tests green. - src/uix/sema/README.md — new "Duración del signal" section with the full perception scale + per-family defaults tables + override pattern. - src/uix/eidos/events.css — comment updated to explain the new hold windows; rule `[data-event^='dismiss']` etc. clarified. - src/uix/active_architecture.md — replaced "~13ms" prose with explicit perception-anchored scales; added explanation of "perceptual by design, not framerate". - src/uix/soma/components/toast/toast-provider.svelte.ts — new $effect that fires `present` + `announce` events when item ref attaches (one-shot guarded), so toasts emit signals on appear. The flash on /test/eidos is now visible thanks to (a) the perceptual hold and (b) the wired events. Channel parallelism note - Hold is the DOM channel duration only. - Future sound / vibra / motion engines subscribe to sema events independently; each owns its own timing relative to the event id. - Sema does not coordinate cross-channel sync — that's an integrator decision per app. Verification - 22/22 sema tests green. - 22/22 morfo runtime tests green. - 66/66 morfo-check + 68/68 smoke (verified before commit). - Probe shows announce signal on toast appear lasting ~600ms (alert family default) — visible in DevTools attribute panel. - svelte-check unchanged at 155. Pre-existing failure in src/uix/air/internal/canvas/layout.test.ts (`bun:test` import) is in the dead-branch air layer and unrelated.
6 months ago
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
```
src/uix/sema/
├── engine.ts registry + dispatch
└── chans/
├── types.ts interfaz Channel
├── visual.ts VisualChannel (built-in, escribe data-event* al DOM)
├── sound.ts SoundChannel (placeholder V1)
└── vibra.ts VibraChannel (placeholder V1)
```
El engine no conoce DOM ni hold ni atributos. Cada canal materializa la
señal en su modalidad. Solo el canal visual bloquea al caller (comparte
plano DOM con el commit estructural posterior); los demás son
fire-and-forget.
### Hold — política técnica del canal visual
sema: perceptually-anchored hold + sequential strict emit semantics Two architectural fixes surfaced by the eidos demo: 1. The previous default hold (1 rAF + 1 hold frame, ~30ms) was a technical timing decision — invisible to humans. Signals must be anchored to perceptual scales so they can actually be perceived. 2. The previous lifecycle resolved emit AFTER 1 rAF (caller's structural change ran in parallel with cleanup, ~30ms later). That's an implicit parallelism between event and state. The author has no way to say "wait for the announcement to be perceived before changing state". Sequential strict semantics (NEW) - emit writes the signal, awaits the full author-specified hold, cleans up, then resolves. State change happens AFTER cleanup — strictly sequential. - `signal.hold` is in milliseconds anchored to `SEMA_PERCEPTION` scales: subliminal 50ms below conscious threshold glimpse 120ms minimum perceptible without effort brief 240ms comfortable acknowledgement noticed 600ms sustained signal insistent 1200ms demands attention persistent 3000ms until acknowledged - Defaults per family in `SEMA_HOLD_DEFAULTS`: emerge: brief sustain: noticed contact: glimpse commit: brief alert: noticed handle: brief - Authors override per event via `SemanticSignal.hold` based on: - their CSS animation duration (so it completes before cleanup) - the perceptual scale they want - critical attention requirements (use insistent/persistent) Files - src/uix/sema/perception.ts (new) — SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS constants with rationale anchored on Bloch's law, Material/Apple HIG motion guidelines, toast UX literature. - src/uix/sema/signal.ts — `hold` JSDoc updated to explain ms semantics + defaults + override pattern. - src/uix/sema/engine.ts — emit() refactored from rAF chain to setTimeout(holdMs); cleanup happens BEFORE resolve (sequential strict); `nextFrame()` helper removed; `resolveHoldMs(signal)` added with the 3-tier fallback (signal.hold → family default → SEMA_PERCEPTION.brief). - src/uix/sema/exports.ts — re-exports SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS + SemaPerception type. - src/uix/sema/emit.test.ts — rewritten around fakeTimers + setTimeout (no more rAF queue); new test verifies cleanup runs BEFORE resolve. 7/7 tests green. - src/uix/sema/README.md — new "Duración del signal" section with the full perception scale + per-family defaults tables + override pattern. - src/uix/eidos/events.css — comment updated to explain the new hold windows; rule `[data-event^='dismiss']` etc. clarified. - src/uix/active_architecture.md — replaced "~13ms" prose with explicit perception-anchored scales; added explanation of "perceptual by design, not framerate". - src/uix/soma/components/toast/toast-provider.svelte.ts — new $effect that fires `present` + `announce` events when item ref attaches (one-shot guarded), so toasts emit signals on appear. The flash on /test/eidos is now visible thanks to (a) the perceptual hold and (b) the wired events. Channel parallelism note - Hold is the DOM channel duration only. - Future sound / vibra / motion engines subscribe to sema events independently; each owns its own timing relative to the event id. - Sema does not coordinate cross-channel sync — that's an integrator decision per app. Verification - 22/22 sema tests green. - 22/22 morfo runtime tests green. - 66/66 morfo-check + 68/68 smoke (verified before commit). - Probe shows announce signal on toast appear lasting ~600ms (alert family default) — visible in DevTools attribute panel. - svelte-check unchanged at 155. Pre-existing failure in src/uix/air/internal/canvas/layout.test.ts (`bun:test` import) is in the dead-branch air layer and unrelated.
6 months ago
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
El `VisualChannel` mantiene los atributos `data-event*` en el DOM durante
un `hold` configurable. Defaults internos del canal por familia:
sema: perceptually-anchored hold + sequential strict emit semantics Two architectural fixes surfaced by the eidos demo: 1. The previous default hold (1 rAF + 1 hold frame, ~30ms) was a technical timing decision — invisible to humans. Signals must be anchored to perceptual scales so they can actually be perceived. 2. The previous lifecycle resolved emit AFTER 1 rAF (caller's structural change ran in parallel with cleanup, ~30ms later). That's an implicit parallelism between event and state. The author has no way to say "wait for the announcement to be perceived before changing state". Sequential strict semantics (NEW) - emit writes the signal, awaits the full author-specified hold, cleans up, then resolves. State change happens AFTER cleanup — strictly sequential. - `signal.hold` is in milliseconds anchored to `SEMA_PERCEPTION` scales: subliminal 50ms below conscious threshold glimpse 120ms minimum perceptible without effort brief 240ms comfortable acknowledgement noticed 600ms sustained signal insistent 1200ms demands attention persistent 3000ms until acknowledged - Defaults per family in `SEMA_HOLD_DEFAULTS`: emerge: brief sustain: noticed contact: glimpse commit: brief alert: noticed handle: brief - Authors override per event via `SemanticSignal.hold` based on: - their CSS animation duration (so it completes before cleanup) - the perceptual scale they want - critical attention requirements (use insistent/persistent) Files - src/uix/sema/perception.ts (new) — SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS constants with rationale anchored on Bloch's law, Material/Apple HIG motion guidelines, toast UX literature. - src/uix/sema/signal.ts — `hold` JSDoc updated to explain ms semantics + defaults + override pattern. - src/uix/sema/engine.ts — emit() refactored from rAF chain to setTimeout(holdMs); cleanup happens BEFORE resolve (sequential strict); `nextFrame()` helper removed; `resolveHoldMs(signal)` added with the 3-tier fallback (signal.hold → family default → SEMA_PERCEPTION.brief). - src/uix/sema/exports.ts — re-exports SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS + SemaPerception type. - src/uix/sema/emit.test.ts — rewritten around fakeTimers + setTimeout (no more rAF queue); new test verifies cleanup runs BEFORE resolve. 7/7 tests green. - src/uix/sema/README.md — new "Duración del signal" section with the full perception scale + per-family defaults tables + override pattern. - src/uix/eidos/events.css — comment updated to explain the new hold windows; rule `[data-event^='dismiss']` etc. clarified. - src/uix/active_architecture.md — replaced "~13ms" prose with explicit perception-anchored scales; added explanation of "perceptual by design, not framerate". - src/uix/soma/components/toast/toast-provider.svelte.ts — new $effect that fires `present` + `announce` events when item ref attaches (one-shot guarded), so toasts emit signals on appear. The flash on /test/eidos is now visible thanks to (a) the perceptual hold and (b) the wired events. Channel parallelism note - Hold is the DOM channel duration only. - Future sound / vibra / motion engines subscribe to sema events independently; each owns its own timing relative to the event id. - Sema does not coordinate cross-channel sync — that's an integrator decision per app. Verification - 22/22 sema tests green. - 22/22 morfo runtime tests green. - 66/66 morfo-check + 68/68 smoke (verified before commit). - Probe shows announce signal on toast appear lasting ~600ms (alert family default) — visible in DevTools attribute panel. - svelte-check unchanged at 155. Pre-existing failure in src/uix/air/internal/canvas/layout.test.ts (`bun:test` import) is in the dead-branch air layer and unrelated.
6 months ago
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
| Family | Hold |
|---|---|
| `emerge` | 240ms |
| `sustain` | 600ms |
| `contact` | 120ms |
| `commit` | 240ms |
| `alert` | 600ms |
| `handle` | 240ms |
sema: perceptually-anchored hold + sequential strict emit semantics Two architectural fixes surfaced by the eidos demo: 1. The previous default hold (1 rAF + 1 hold frame, ~30ms) was a technical timing decision — invisible to humans. Signals must be anchored to perceptual scales so they can actually be perceived. 2. The previous lifecycle resolved emit AFTER 1 rAF (caller's structural change ran in parallel with cleanup, ~30ms later). That's an implicit parallelism between event and state. The author has no way to say "wait for the announcement to be perceived before changing state". Sequential strict semantics (NEW) - emit writes the signal, awaits the full author-specified hold, cleans up, then resolves. State change happens AFTER cleanup — strictly sequential. - `signal.hold` is in milliseconds anchored to `SEMA_PERCEPTION` scales: subliminal 50ms below conscious threshold glimpse 120ms minimum perceptible without effort brief 240ms comfortable acknowledgement noticed 600ms sustained signal insistent 1200ms demands attention persistent 3000ms until acknowledged - Defaults per family in `SEMA_HOLD_DEFAULTS`: emerge: brief sustain: noticed contact: glimpse commit: brief alert: noticed handle: brief - Authors override per event via `SemanticSignal.hold` based on: - their CSS animation duration (so it completes before cleanup) - the perceptual scale they want - critical attention requirements (use insistent/persistent) Files - src/uix/sema/perception.ts (new) — SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS constants with rationale anchored on Bloch's law, Material/Apple HIG motion guidelines, toast UX literature. - src/uix/sema/signal.ts — `hold` JSDoc updated to explain ms semantics + defaults + override pattern. - src/uix/sema/engine.ts — emit() refactored from rAF chain to setTimeout(holdMs); cleanup happens BEFORE resolve (sequential strict); `nextFrame()` helper removed; `resolveHoldMs(signal)` added with the 3-tier fallback (signal.hold → family default → SEMA_PERCEPTION.brief). - src/uix/sema/exports.ts — re-exports SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS + SemaPerception type. - src/uix/sema/emit.test.ts — rewritten around fakeTimers + setTimeout (no more rAF queue); new test verifies cleanup runs BEFORE resolve. 7/7 tests green. - src/uix/sema/README.md — new "Duración del signal" section with the full perception scale + per-family defaults tables + override pattern. - src/uix/eidos/events.css — comment updated to explain the new hold windows; rule `[data-event^='dismiss']` etc. clarified. - src/uix/active_architecture.md — replaced "~13ms" prose with explicit perception-anchored scales; added explanation of "perceptual by design, not framerate". - src/uix/soma/components/toast/toast-provider.svelte.ts — new $effect that fires `present` + `announce` events when item ref attaches (one-shot guarded), so toasts emit signals on appear. The flash on /test/eidos is now visible thanks to (a) the perceptual hold and (b) the wired events. Channel parallelism note - Hold is the DOM channel duration only. - Future sound / vibra / motion engines subscribe to sema events independently; each owns its own timing relative to the event id. - Sema does not coordinate cross-channel sync — that's an integrator decision per app. Verification - 22/22 sema tests green. - 22/22 morfo runtime tests green. - 66/66 morfo-check + 68/68 smoke (verified before commit). - Probe shows announce signal on toast appear lasting ~600ms (alert family default) — visible in DevTools attribute panel. - svelte-check unchanged at 155. Pre-existing failure in src/uix/air/internal/canvas/layout.test.ts (`bun:test` import) is in the dead-branch air layer and unrelated.
6 months ago
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
Default global (cuando ni signal.hold ni la familia lo proporcionan): 240ms.
Estos números reflejan rangos típicos de CSS transitions para cada tipo
de feedback. El integrador puede subir el default global vía
`new SemanticEngine({ visual: { defaultHold: ... } })` o per signal vía
`signal.hold`.
sema: perceptually-anchored hold + sequential strict emit semantics Two architectural fixes surfaced by the eidos demo: 1. The previous default hold (1 rAF + 1 hold frame, ~30ms) was a technical timing decision — invisible to humans. Signals must be anchored to perceptual scales so they can actually be perceived. 2. The previous lifecycle resolved emit AFTER 1 rAF (caller's structural change ran in parallel with cleanup, ~30ms later). That's an implicit parallelism between event and state. The author has no way to say "wait for the announcement to be perceived before changing state". Sequential strict semantics (NEW) - emit writes the signal, awaits the full author-specified hold, cleans up, then resolves. State change happens AFTER cleanup — strictly sequential. - `signal.hold` is in milliseconds anchored to `SEMA_PERCEPTION` scales: subliminal 50ms below conscious threshold glimpse 120ms minimum perceptible without effort brief 240ms comfortable acknowledgement noticed 600ms sustained signal insistent 1200ms demands attention persistent 3000ms until acknowledged - Defaults per family in `SEMA_HOLD_DEFAULTS`: emerge: brief sustain: noticed contact: glimpse commit: brief alert: noticed handle: brief - Authors override per event via `SemanticSignal.hold` based on: - their CSS animation duration (so it completes before cleanup) - the perceptual scale they want - critical attention requirements (use insistent/persistent) Files - src/uix/sema/perception.ts (new) — SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS constants with rationale anchored on Bloch's law, Material/Apple HIG motion guidelines, toast UX literature. - src/uix/sema/signal.ts — `hold` JSDoc updated to explain ms semantics + defaults + override pattern. - src/uix/sema/engine.ts — emit() refactored from rAF chain to setTimeout(holdMs); cleanup happens BEFORE resolve (sequential strict); `nextFrame()` helper removed; `resolveHoldMs(signal)` added with the 3-tier fallback (signal.hold → family default → SEMA_PERCEPTION.brief). - src/uix/sema/exports.ts — re-exports SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS + SemaPerception type. - src/uix/sema/emit.test.ts — rewritten around fakeTimers + setTimeout (no more rAF queue); new test verifies cleanup runs BEFORE resolve. 7/7 tests green. - src/uix/sema/README.md — new "Duración del signal" section with the full perception scale + per-family defaults tables + override pattern. - src/uix/eidos/events.css — comment updated to explain the new hold windows; rule `[data-event^='dismiss']` etc. clarified. - src/uix/active_architecture.md — replaced "~13ms" prose with explicit perception-anchored scales; added explanation of "perceptual by design, not framerate". - src/uix/soma/components/toast/toast-provider.svelte.ts — new $effect that fires `present` + `announce` events when item ref attaches (one-shot guarded), so toasts emit signals on appear. The flash on /test/eidos is now visible thanks to (a) the perceptual hold and (b) the wired events. Channel parallelism note - Hold is the DOM channel duration only. - Future sound / vibra / motion engines subscribe to sema events independently; each owns its own timing relative to the event id. - Sema does not coordinate cross-channel sync — that's an integrator decision per app. Verification - 22/22 sema tests green. - 22/22 morfo runtime tests green. - 66/66 morfo-check + 68/68 smoke (verified before commit). - Probe shows announce signal on toast appear lasting ~600ms (alert family default) — visible in DevTools attribute panel. - svelte-check unchanged at 155. Pre-existing failure in src/uix/air/internal/canvas/layout.test.ts (`bun:test` import) is in the dead-branch air layer and unrelated.
6 months ago
```ts
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
// Override per signal
semantic.emit({ ..., hold: 1200 })
sema: perceptually-anchored hold + sequential strict emit semantics Two architectural fixes surfaced by the eidos demo: 1. The previous default hold (1 rAF + 1 hold frame, ~30ms) was a technical timing decision — invisible to humans. Signals must be anchored to perceptual scales so they can actually be perceived. 2. The previous lifecycle resolved emit AFTER 1 rAF (caller's structural change ran in parallel with cleanup, ~30ms later). That's an implicit parallelism between event and state. The author has no way to say "wait for the announcement to be perceived before changing state". Sequential strict semantics (NEW) - emit writes the signal, awaits the full author-specified hold, cleans up, then resolves. State change happens AFTER cleanup — strictly sequential. - `signal.hold` is in milliseconds anchored to `SEMA_PERCEPTION` scales: subliminal 50ms below conscious threshold glimpse 120ms minimum perceptible without effort brief 240ms comfortable acknowledgement noticed 600ms sustained signal insistent 1200ms demands attention persistent 3000ms until acknowledged - Defaults per family in `SEMA_HOLD_DEFAULTS`: emerge: brief sustain: noticed contact: glimpse commit: brief alert: noticed handle: brief - Authors override per event via `SemanticSignal.hold` based on: - their CSS animation duration (so it completes before cleanup) - the perceptual scale they want - critical attention requirements (use insistent/persistent) Files - src/uix/sema/perception.ts (new) — SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS constants with rationale anchored on Bloch's law, Material/Apple HIG motion guidelines, toast UX literature. - src/uix/sema/signal.ts — `hold` JSDoc updated to explain ms semantics + defaults + override pattern. - src/uix/sema/engine.ts — emit() refactored from rAF chain to setTimeout(holdMs); cleanup happens BEFORE resolve (sequential strict); `nextFrame()` helper removed; `resolveHoldMs(signal)` added with the 3-tier fallback (signal.hold → family default → SEMA_PERCEPTION.brief). - src/uix/sema/exports.ts — re-exports SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS + SemaPerception type. - src/uix/sema/emit.test.ts — rewritten around fakeTimers + setTimeout (no more rAF queue); new test verifies cleanup runs BEFORE resolve. 7/7 tests green. - src/uix/sema/README.md — new "Duración del signal" section with the full perception scale + per-family defaults tables + override pattern. - src/uix/eidos/events.css — comment updated to explain the new hold windows; rule `[data-event^='dismiss']` etc. clarified. - src/uix/active_architecture.md — replaced "~13ms" prose with explicit perception-anchored scales; added explanation of "perceptual by design, not framerate". - src/uix/soma/components/toast/toast-provider.svelte.ts — new $effect that fires `present` + `announce` events when item ref attaches (one-shot guarded), so toasts emit signals on appear. The flash on /test/eidos is now visible thanks to (a) the perceptual hold and (b) the wired events. Channel parallelism note - Hold is the DOM channel duration only. - Future sound / vibra / motion engines subscribe to sema events independently; each owns its own timing relative to the event id. - Sema does not coordinate cross-channel sync — that's an integrator decision per app. Verification - 22/22 sema tests green. - 22/22 morfo runtime tests green. - 66/66 morfo-check + 68/68 smoke (verified before commit). - Probe shows announce signal on toast appear lasting ~600ms (alert family default) — visible in DevTools attribute panel. - svelte-check unchanged at 155. Pre-existing failure in src/uix/air/internal/canvas/layout.test.ts (`bun:test` import) is in the dead-branch air layer and unrelated.
6 months ago
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
// Override default global del canal visual
const semantic = new SemanticEngine({ visual: { defaultHold: 400 } })
sema: perceptually-anchored hold + sequential strict emit semantics Two architectural fixes surfaced by the eidos demo: 1. The previous default hold (1 rAF + 1 hold frame, ~30ms) was a technical timing decision — invisible to humans. Signals must be anchored to perceptual scales so they can actually be perceived. 2. The previous lifecycle resolved emit AFTER 1 rAF (caller's structural change ran in parallel with cleanup, ~30ms later). That's an implicit parallelism between event and state. The author has no way to say "wait for the announcement to be perceived before changing state". Sequential strict semantics (NEW) - emit writes the signal, awaits the full author-specified hold, cleans up, then resolves. State change happens AFTER cleanup — strictly sequential. - `signal.hold` is in milliseconds anchored to `SEMA_PERCEPTION` scales: subliminal 50ms below conscious threshold glimpse 120ms minimum perceptible without effort brief 240ms comfortable acknowledgement noticed 600ms sustained signal insistent 1200ms demands attention persistent 3000ms until acknowledged - Defaults per family in `SEMA_HOLD_DEFAULTS`: emerge: brief sustain: noticed contact: glimpse commit: brief alert: noticed handle: brief - Authors override per event via `SemanticSignal.hold` based on: - their CSS animation duration (so it completes before cleanup) - the perceptual scale they want - critical attention requirements (use insistent/persistent) Files - src/uix/sema/perception.ts (new) — SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS constants with rationale anchored on Bloch's law, Material/Apple HIG motion guidelines, toast UX literature. - src/uix/sema/signal.ts — `hold` JSDoc updated to explain ms semantics + defaults + override pattern. - src/uix/sema/engine.ts — emit() refactored from rAF chain to setTimeout(holdMs); cleanup happens BEFORE resolve (sequential strict); `nextFrame()` helper removed; `resolveHoldMs(signal)` added with the 3-tier fallback (signal.hold → family default → SEMA_PERCEPTION.brief). - src/uix/sema/exports.ts — re-exports SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS + SemaPerception type. - src/uix/sema/emit.test.ts — rewritten around fakeTimers + setTimeout (no more rAF queue); new test verifies cleanup runs BEFORE resolve. 7/7 tests green. - src/uix/sema/README.md — new "Duración del signal" section with the full perception scale + per-family defaults tables + override pattern. - src/uix/eidos/events.css — comment updated to explain the new hold windows; rule `[data-event^='dismiss']` etc. clarified. - src/uix/active_architecture.md — replaced "~13ms" prose with explicit perception-anchored scales; added explanation of "perceptual by design, not framerate". - src/uix/soma/components/toast/toast-provider.svelte.ts — new $effect that fires `present` + `announce` events when item ref attaches (one-shot guarded), so toasts emit signals on appear. The flash on /test/eidos is now visible thanks to (a) the perceptual hold and (b) the wired events. Channel parallelism note - Hold is the DOM channel duration only. - Future sound / vibra / motion engines subscribe to sema events independently; each owns its own timing relative to the event id. - Sema does not coordinate cross-channel sync — that's an integrator decision per app. Verification - 22/22 sema tests green. - 22/22 morfo runtime tests green. - 66/66 morfo-check + 68/68 smoke (verified before commit). - Probe shows announce signal on toast appear lasting ~600ms (alert family default) — visible in DevTools attribute panel. - svelte-check unchanged at 155. Pre-existing failure in src/uix/air/internal/canvas/layout.test.ts (`bun:test` import) is in the dead-branch air layer and unrelated.
6 months ago
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
// Desactivar visual (entornos sin DOM)
const semantic = new SemanticEngine({ visual: false })
// Registrar canales adicionales (cuando estén implementados)
import { SoundChannel } from '$uix/sema'
semantic.register(new SoundChannel())
```
sema: perceptually-anchored hold + sequential strict emit semantics Two architectural fixes surfaced by the eidos demo: 1. The previous default hold (1 rAF + 1 hold frame, ~30ms) was a technical timing decision — invisible to humans. Signals must be anchored to perceptual scales so they can actually be perceived. 2. The previous lifecycle resolved emit AFTER 1 rAF (caller's structural change ran in parallel with cleanup, ~30ms later). That's an implicit parallelism between event and state. The author has no way to say "wait for the announcement to be perceived before changing state". Sequential strict semantics (NEW) - emit writes the signal, awaits the full author-specified hold, cleans up, then resolves. State change happens AFTER cleanup — strictly sequential. - `signal.hold` is in milliseconds anchored to `SEMA_PERCEPTION` scales: subliminal 50ms below conscious threshold glimpse 120ms minimum perceptible without effort brief 240ms comfortable acknowledgement noticed 600ms sustained signal insistent 1200ms demands attention persistent 3000ms until acknowledged - Defaults per family in `SEMA_HOLD_DEFAULTS`: emerge: brief sustain: noticed contact: glimpse commit: brief alert: noticed handle: brief - Authors override per event via `SemanticSignal.hold` based on: - their CSS animation duration (so it completes before cleanup) - the perceptual scale they want - critical attention requirements (use insistent/persistent) Files - src/uix/sema/perception.ts (new) — SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS constants with rationale anchored on Bloch's law, Material/Apple HIG motion guidelines, toast UX literature. - src/uix/sema/signal.ts — `hold` JSDoc updated to explain ms semantics + defaults + override pattern. - src/uix/sema/engine.ts — emit() refactored from rAF chain to setTimeout(holdMs); cleanup happens BEFORE resolve (sequential strict); `nextFrame()` helper removed; `resolveHoldMs(signal)` added with the 3-tier fallback (signal.hold → family default → SEMA_PERCEPTION.brief). - src/uix/sema/exports.ts — re-exports SEMA_PERCEPTION + SEMA_HOLD_DEFAULTS + SEMA_OBSERVE_FRAME_MS + SemaPerception type. - src/uix/sema/emit.test.ts — rewritten around fakeTimers + setTimeout (no more rAF queue); new test verifies cleanup runs BEFORE resolve. 7/7 tests green. - src/uix/sema/README.md — new "Duración del signal" section with the full perception scale + per-family defaults tables + override pattern. - src/uix/eidos/events.css — comment updated to explain the new hold windows; rule `[data-event^='dismiss']` etc. clarified. - src/uix/active_architecture.md — replaced "~13ms" prose with explicit perception-anchored scales; added explanation of "perceptual by design, not framerate". - src/uix/soma/components/toast/toast-provider.svelte.ts — new $effect that fires `present` + `announce` events when item ref attaches (one-shot guarded), so toasts emit signals on appear. The flash on /test/eidos is now visible thanks to (a) the perceptual hold and (b) the wired events. Channel parallelism note - Hold is the DOM channel duration only. - Future sound / vibra / motion engines subscribe to sema events independently; each owns its own timing relative to the event id. - Sema does not coordinate cross-channel sync — that's an integrator decision per app. Verification - 22/22 sema tests green. - 22/22 morfo runtime tests green. - 66/66 morfo-check + 68/68 smoke (verified before commit). - Probe shows announce signal on toast appear lasting ~600ms (alert family default) — visible in DevTools attribute panel. - svelte-check unchanged at 155. Pre-existing failure in src/uix/air/internal/canvas/layout.test.ts (`bun:test` import) is in the dead-branch air layer and unrelated.
6 months ago
### Política de errores
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
- Errores en canales NO-visuales se loguean pero no propagan. Sema es
ornamental: un fallo de audio context o vibration API no debe abortar
la operación del provider.
- Si el canal visual lanza, la Promise de `emit` rechaza. El caller decide.
- En fire-and-forget (`void semantic.emit(...)`), una rejection del visual
se propaga como unhandled promise — política consciente.
docs: cross-layer articulation — eidos README + 2-of-3 rule across all layer docs Closes the documentation loop on the cross-layer extension pass: morfo now articulates between soma, sema, and (future) eidos. The "2-of-3 rule" formalizes when an extension to morfo is justified vs when it should stay as provider logic. src/uix/eidos/README.md (new) - Documents eidos's role and what it consumes from morfo + sema BEFORE any code exists, so the contract is preparedly clean when implementation starts. - Catalogs which morfo fields eidos reads (parts, archetype, states, data values, events, prewrite, focus, supportsNesting) and which it ignores (computed state, runtime internals, layers). - Documents the DOM-as-channel pattern: sema writes data-event* on emit; eidos reacts to selectors like `[data-event^="dismiss"]`. - Establishes the boundary with `air` (dead branch reference, not base). src/uix/README.md (top-level) - §8 Reglas de dependencia: adds the 2-of-3 rule table making the morfo-extension contract explicit, plus a list of canonical vocabularies (archetypes, verbs). - §10 Reading order: includes eidos README + lib/dom + clarifies which layers are dead branches. src/uix/morfo/README.md - New "Archetypes" section documenting the 24-verb vocabulary, the Provider-as-trigger vs Provider-as-container distinction, and the rule for adding new archetypes (≥2 components share the role). - New "The 2-of-3 rule" section with the same table as the top-level, listing which extensions did/didn't make it past the rule and why. - `parts[].archetype` mentioned in the "What morfo contains" list. src/uix/sema/README.md - New "Vocabulario canónico de verbs" section listing SEMA_VERBS by family and the `{verb}-{variant}` composite naming convention. - Documents `validateEventName()` as advisory tooling. src/uix/soma/SOMA_ARCHITECTURE.md - partProps documentation now mentions data-archetype emission. - New "Cross-layer hooks que soma emite por la regla 2-de-3" section listing the data-* attrs soma writes that sema and eidos consume. - Reading-order links updated. No code changes — all docs.
6 months ago
## Vocabulario canónico de verbs (`SEMA_VERBS`)
Cross-component action verbs. `morfo.events[].name` debería alinear con
este vocabulario para que sema/sound/vibra puedan suscribir por verb y
eidos pueda escribir selectores transversales (`[data-event^=dismiss]`).
```
emerge: present · dismiss · open · close · expand · collapse
commit: commit · cancel · confirm · submit · reset · fail
alert: announce · alert
contact: activate · select · toggle
handle: acknowledge · edit · drag · resize
sustain: tick · progress
```
Definido en [`verbs.ts:SEMA_VERBS`](./verbs.ts).
### Composite event names
Los eventos pueden tener variantes con la convención `{verb}-{variant}`:
```ts
'dismiss' // bare verb
'dismiss-outside' // verb + variant
'commit-save'
'commit-cancel'
'close-after-fail'
```
`validateEventName(name)` extrae head + variant y reporta si el head es
canonical. Advisory — no rechaza morfos, solo flagea drift para tooling
y revisión.
```ts
import { validateEventName } from '$uix/sema'
validateEventName('commit-save')
// { name: 'commit-save', head: 'commit', matchesCanonical: true, variant: 'save' }
validateEventName('frob-glob')
// { name: 'frob-glob', head: 'frob', matchesCanonical: false, variant: 'glob' }
```
## Relación con Morfo y Soma
- `Morfo` declara los eventos semánticos del componente en `morfo.events`
- `Provider` decide cuándo ocurren y llama a `semantic.emit(...)`
- `MorfoRuntime` orquesta la secuencia `prewrite -> emit -> handler -> effects`
- `Sema` aporta el vocabulario, la normalización y la validación del dominio,
y publica las ocurrencias
## Dependencias
- `Sema` puede usar `Dom` (`semantic.emit` llama a `dom.apply` para escribir
`data-event*`). Dependencia hacia abajo, legítima.
- `Dom` no conoce `Sema`.
- `Sema` recibe `dom` por construcción, no lo importa duro de `$uix/adom`.
## Regla de arquitectura
`Morfo` autoriza la semántica del componente.
`Sema` define el vocabulario canónico y orquesta la señal perceptiva.
`Provider` decide cuándo emitir.
`Dom` aplica.
Ver [src/uix/README.md](../README.md) §2.bis para la vista cross-layer.

Powered by TurnKey Linux.