Remove the parallel motion-coordination service per the redesign: animation is a
channel of the EVENT's firma (sema owns it across all channels), not a parallel axis.
Code is gone; the RFC stays as historical record with a retirement banner.
- morfo: drop MorfoPart.animation + its schema/compile/types/exports
- arts/motion: drop CoordinatedPreset / MotionConfig.coordinated
- eidos: drop BUILTIN_COORDINATED_PRESETS / renderCoordinatedPresetRules / the
animation:none neutralization + registry block; regen generated/base.css
- soma: Presence reduced to a single-surface island; delete presence-group /
dom-cascade / coordination; dropdown-menu loses the cascade wiring
- delete Reveal / Rail (morfo + soma + demos)
Docs: MOTION_SERVICE_RFC gains the retirement banner + §D.8 (retirada) + §D.9
(verified model: morfo->soma->sema->eidos pipeline + the two hard rules — soma
never writes a visual --var; data-event-* is a single-target stamp, not a bus) +
§D.10 (Fase 1 prototype). Fix stale "5 canales" claim in GUIA §11; eidos-motion +
dropdown README aligned.
Fase 1 prototype (web/routes/temas/animations/panel-cascade): validates the model
end to end — panel->cards cascade (enter/exit), dynamic removal with Svelte out:
retention, nested cascade — all via :nth-child + custom-property inheritance, with
zero JS visual writes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El piloto M9 (dropdown-menu) destapó que el PresenceGroup coordina children
que son `Presence` REGISTRADOS (Reveal/Rail), pero un menú real es owner-
`Presence` (el content monta como unidad) + children-DOM (items por selector,
sin `Presence`). 3 grietas en 3 capas: registro, index per-item, y escribir
una CSS-var per-element (que ActiveDom no cubría).
Modo children-DOM (la 2ª coordinación): el owner PROPAGA su lifecycle a los
items DOM — escribe data-animation-style + data-starting/ending-style (espejo
del owner) + --motion-stagger-index/-count (por orden DOM), reutilizando el
preset coordinado de eidos SIN cambios (los items quedan con los mismos attrs
que un child-`Presence` produciría).
F1 (núcleo):
- morfo: `MorfoPart.animation.staggerChildren?: string` (types + schema +
invariante: requiere surface, excluye children).
- adom: `ActiveDom.writeProperty/removeProperty` per-element (la pieza que
faltaba; + stub disabled).
- soma: `DomCascade` (`dom-cascade.svelte.ts` — `sync`/`clear` puros y
testeables, `watch()` corre el $effect espejo) + barrel export.
F1b (validación aislada): demo `/temas/animations/dom-cascade` (owner Presence
+ items DOM + DomCascade.watch). Cazó un bug que el test no veía: DomCascade
usa $effect → debe ser `.svelte.ts`, no `.ts` (el test solo ejercita sync/clear,
nunca watch()). Cascada de ENTRADA verificada en navegador.
PENDIENTE (en la memoria del proyecto): F1c exit-heavy (el owner espera a los
items — getAnimations no ve subtree), F2 el dropdown-menu real (focus-trap/
dismissal/submenús), y volcar §M9 al RFC.
Tests: dom-cascade 5/5, morfo 70/70. svelte-check: 0 errores en mis archivos.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Continuación del servicio de motion-coordination (sobre M1–M6 ya commiteado).
- M5 — naming `animation` / `motion` SEPARADAS por rol (no unificar: tras M6
son sistemas distintos — `motion`=momento `--state`/`data-state`/eidos;
`animation`=coordinado/`data-starting-ending-style`/soma). `animation` gana
type-safety vía el registry augmentable `MotionCoordinatedPresets` +
`CoordinatedPresetName` en `$motion` (la capa compartida, para que soma lo
tipe sin importar eidos); eidos lo puebla con `cascade-slide/-fade/-scale`
por declaration-merging — espejo de `EidosMotionPresets`.
- Reversa fluida — JS handoff (§8.3). El motor expone `MotionHandle.peek()`
(posición + velocidad por propiedad) y la reinyecta en la fase inversa vía
`MotionContext.handoff`: el `spring` continúa desde la posición/velocidad
actual en vez de reiniciar desde el `from`. Orquestado enteramente en el
engine (`takeHandoff` — captura en `cancel`, consume en `run`, y cancela un
run en vuelo cuando el caller no pre-cancela): cubre el camino island
(overlay-spring) y elimina un apilamiento preexistente. La reversa de los
coordinados (transiciones CSS) ya era fluida.
- Demo `/temas/animations/compuesto` — un `Rail` de `<Button>`s que usa los
presets coordinados PREDEFINIDOS de eidos (cero CSS de animación en la
página); el selector cambia `animation` en vivo y cada Button compone su
press (firma sema) con la cascada (motion coordinado).
- Docs — RFC Apéndice B (convivencia de los tres sistemas visuales: firma
`--event` / state-preset `--state` / coordinado, con diagrama + ejemplo
Button-con-tokens), §5 reescrito (separadas por rol), §8.3/§15 al día, y
cross-link del tercer eje desde eidos-motion.md.
Tests: arts/motion 9/9 (handoff) + eidos/motion 24/24 (M5 paridad).
svelte-check: 0 errores en mis archivos.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Servicio de motion cross-layer: morfo declara superficies animables +
coordinación; soma coordina presencia/lifecycle; eidos posee lo visual;
arts/motion ejecuta por-nodo. RFC en eidos/MOTION_SERVICE_RFC.md.
- M1 — contrato MorfoPart.animation (types/schema/compile); children
como { enter?, exit? }.
- M2 — PresenceGroup (soma/layers/presence-group.ts), rune-free;
Presence.group descubre el coordinador por context.
- M3 — exit con retención de DOM (§8.1).
- M4 — interrupción/reversa (§8.3): token de generación + motion.cancel
en flip + toHandle resuelve finished en cancel (sin AbortError suelto).
- M5 — prop `animation` enrutada a las parts surface:true del morfo
compilado (routeAnimation); Panel/Item emiten data-animation-style.
- M6 — stagger auto-derivado del orden de registro (--motion-stagger-*,
inversa en exit) + presets coordinados en la librería de eidos
(MotionConfig.coordinated; cascade-slide/-fade/-scale) que reaccionan
a data-starting/ending-style, NO a data-state.
Helper Coordination (soma/layers/coordination.ts) extraído y validado
por DOS consumidores reales: Reveal (raíz virtual + Panel owner) y Rail
(raíz=owner, together). Demos en /temas/animations/{reveal,rail,
presence-group}.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
From the arts/ audit (docs/process/arts-audit-2026-06.md):
- connection: drop 'export * from helpers' (leaked internal builders) and name-export the public error surface; remove the redundant EngineConnections.close() alias (consolidated to closeConnection, consistent with openConnection/reconnectConnection/closeAll) and retarget its destructure-safety test.
- http: extract the inline ATTEMPT_COMPLETED diagnostic message into a named builder (attemptCompletedLogMessage); output preserved.
- color: drop stale 'Phase 0 / not consumed yet' comment (uix.color is consumed by Eidos).
Verified: vitest connection/http/color green, type-check clean, prettier clean. The QrCode morfo fix (literal('path')) is left in src/uix/morfo/schema.ts because that file also carries unrelated in-progress animation-morfo work — not swept in here.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There is no sium-specific Svelte adapter by design: forms (createForm / Form.Provider) consume any schema via ~standard, Form.AutoFields renders via ~sium introspection, and langs/logger inject through createEngineSium(). sium is page-scoped and not an ActiveUix service. Fixes README (Estado, dep rules, Integracion Svelte, troubleshooting) + core/types.ts comments.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Close the Phase 1b debt: the "## Handoff 2026-05-14" blocks embedded at
the top of arts/README, arts/adom/README and arts/format/README move to
docs/process/handoffs-2026-05.md, so the layer references read as
timeless. Same mechanical pattern already applied to the UIX READMEs in
41c3b0f9.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`timers.schedule/interval/scheduleAt` now accept `key: null` — the engine mints a
unique `auto:N` key via an internal counter (with a `has()` collision guard) and
the caller drives the timer through the returned handle. Naming a timer stays a
feature (cancel-by-name, `replace`, dedup, scoped `cancelAll`), not a tax on every
call. Anonymous timers live in the `auto` scope. +6 engine tests; the race-safety
path is unchanged (the key is resolved before the (id,key,version) machinery).
Uses it to close the raw `setTimeout`/`setInterval` and direct `getComputedStyle`
violations the audit flagged — instead of waiving the uix.timers / $adom rules:
- code-block, relative-time: raw setTimeout/setInterval -> eidos.timers (key:null)
- cropper: raw setTimeout throttle gate -> uix.timers (key:null)
- textarea, s-text, s-text-virtual-list: getComputedStyle(el) -> dom.getWindow(el)
The earlier audit call that these were "nil functional gain" was wrong: the rule
is the rule, and the friction (manual per-instance key invention) was removed at
the framework level rather than used as grounds to skip the rule.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Motor (words): ImageBlock gana `fit` (fill/fit/crop/tile) + 7 filtros CSS
(saturate/brightness/contrast/hueRotate/grayscale/sepia/blur) + `width`/`height`
number|string (CSS-length). Render: object-fit + branch tile bg + figcaption.
Fase A: caret-carry al insertar, guard columnas-en-columnas, deleteColumnAt
siembra parrafo cuando el superviviente queda vacio. block-path.test.ts (14).
- Panel de imagen reescrito con primitivas eidos (ToggleGroup/Switch/Slider/
NumberField/Accordion), labels xs sans arriba del slider, header con icono
definitorio del bloque, secciones pinned + accordion collapsible, footer band.
- sium: `cssLength()`/`isCssLength()`/`CSS_LENGTH_REGEX` en core/refines.ts,
exportados por la fachada EngineSium. El panel valida unidades CSS (data-invalid).
- accordion: tamano `xs` (types + recipe base.ts + accordion.css + generated).
- words-activate: guard atomico (lastAtomicAt + isActiveBlockAtomic) evita que el
caret-follower degrade el bloque imagen activo a heading.
- saturate/contrast max 200% (estandar, antes 300% inventado). Caption visible:
fit-frame movido de figure a img/tile.
Sin tocar la capa sema ni la animacion (restriccion del usuario).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Found stale color docs while verifying currency:
- arts/color/README.md: said "Status — Phase 0 ... nothing consumes it yet" (false —
consumed at build via render-css + runtime via applyColorScheme) and used
`ActiveEidos.setCssVariables` as the theme-builder mechanism (the real API is
applyColorScheme; setCssVariables is for contract knobs). Updated status, added
deriveScheme/temper/harmonize to the API table, documented temper as the canonical
intent-cohesion tool (vs harmonize for brand accents), fixed the builder pipeline to
buildScheme + applyColorScheme, and corrected the wide-gamut note (strategy A is
live + default-on, not "deferred").
- COLOR_ENGINE_RFC.md: 4 remaining `setCssVariables` references for the runtime
white-label builder -> applyColorScheme (only §6.2 was fixed earlier). Marked
Fase 4-bis (runtime generation) as IMPLEMENTED.
COLOR_MODEL_RFC.md verified current (RESUELTO; loss->plum correct; the anchor-hex
examples are the documented-discarded proposal = history). THEMING/audit/README were
already synced in their own commits.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per Gemini's sharp note: rotating an intent's HUE toward the brand (harmonize)
erodes its meaning — a red stops reading as "error". What coheres a palette is
sharing the chroma + lightness PROFILE, not the hue. New temper(color, reference,
amount) keeps the hue and lerps L+C toward the reference. The demo's intent
cohesion switches harmonize -> temper, and the slider MOVES to the "Roles
canonicos" section (next to the intents, dynamic). Verified in-browser: threat hue
stays 358 (red) at 0% and 40% temper, only chroma/lightness shift; affirm stays
teal. harmonize stays in the engine for brand accents. RFC §6.2 updated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Theme-builder core in uix.color: one brand seed -> hierarchy role seeds
(primary/secondary/tertiary/neutral/neutralVariant). Ports M3's HCT CorePalette
to OKLCH — secondary = same hue/low chroma, tertiary = hue+60deg, neutral =
near-gray; variants tonal/vibrant/monochrome (structured for more). harmonize()
nudges hues toward the brand (M3 blend.harmonize). The 6 canonical intents are
NOT derived (an error is always red); APCA replaces HCT's tone->contrast. Pure
+ isomorphic — produces values behind the frozen --color-{role}-{slot}
contract, so zero component impact.
Docs: COLOR_ENGINE_RFC.md §6.2 + color README theme-builder section.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First consumer of uix.color. The theme generator's on-solid text pick
(white vs dark) now decides by APCA (|Lc| >= 60) instead of WCAG 2
(< 3:1), with a WCAG 2 ratio kept as a conservative cross-check — white
must clear BOTH or the contrast slot flips to onSolidContrast. APCA is
accurate in the mid-tones where WCAG 2 mis-estimates (the risk=orange
case). Reproduces the documented base behavior (only risk flips) via a
better metric; generated/base.css unchanged (the pick lives in the
runtime theme block).
- render-css: import apcaLc / oklchToGammaRgb / safeParseColor /
wcagContrastRatio from $color; replace the local WCAG pick; drop the
now-orphaned local wcagRelativeLuminance + wcagContrastRatio.
- color: add safeParseColor (null instead of throw for var()/color-mix
theme values the engine can't introspect).
- wire $color alias (vite.config.ts + svelte.config.js + CLAUDE.md).
Verified: color 20/20, eidos 162/165 (3 pre-existing words failures),
active-eidos-config contrast asserts pass, npm run check 0 new errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New art `src/arts/color` — pure, DOM-free, deterministic color math that
runs identically at build (eidos render-css) and at runtime (white-label
theming). Because every decision is computed in JS before a value is
written, APCA introspection and compositing-inverse alpha are preserved
in every mode (COLOR_ENGINE_RFC §6.1).
- convert: OKLCH<->OKLab<->linear-sRGB<->gamma-sRGB<->hex (Ottosson),
chroma-reduction gamut mapping (no channel clip), parseColor, oklchToCss.
- apca: APCA-W3 0.1.9 Lc + WCAG2 ratio cross-check.
- generate: seed->12-step scale by template morph (re-hue, rescale chroma,
anchor solid to seed), pickOnSolid (APCA, prefer-onSolid policy per
THEMING §24.1), compositing-inverse alphaOverBackground.
Phase 0 only: module + 19 unit tests, NOT consumed yet — zero behavior
change. Full plan in src/uix/eidos/COLOR_ENGINE_RFC.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Relocate the motion runtime out of eidos into a runtime art (src/arts/motion,
$motion), exposed as uix.motion and consumed by BOTH soma (Presence.motion ->
motion.run) and eidos (delegates + registers presets) -- dissolving the
soma->eidos coupling. Remove DialogProps.runMotion / eidos.motionRunner; the
bridge is now EngineMotion.run (reads data-animation-style). Delete the 4
relocated dead files (lib/motion/{types,runtime,runner,presets/js}.ts); the
preset DATA (presets/css.ts) stays in eidos. Regenerate generated/base.css.
F6 - token rigor (Carbon): tokenize the raw firma durations (slower/deliberate/
emphatic/sustained holds, escalating by announce intent severity), add
--motion-distance-xl (30px shared-axis), --motion-scale-through (0.92), the
emphasized easing, and productive/expressive sets ([data-motion-set=expressive]).
F7 - extensibility + typegen: app-extensible, type-safe preset-name registry
(EidosMotionPresets, mirroring SemaChannelSignatures); MotionPresetName =
keyof EidosMotionPresets | none | (string & {}).
Also sweeps other in-progress working-tree edits (web/routes/temas/grafito).
Verify: npm run check -> 1 pre-existing error (grafito), 0 new; motion 22/22,
active-uix 25/25, Presence 2/2, Dialog 3/3. Pre-existing words-track failures
unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add `raf(callback, node?): ActiveDomFrameCleanup` to the ActiveDom surface —
a one-shot animation frame that returns an idempotent **disposer** (the same
`() => void` shape as `listen` / `observe*`), so an `$effect` can
`return dom.raf(...)` and Svelte cancels the pending frame on teardown. It
wraps the existing `requestFrame` / `cancelFrame` (which already resolve the
instance's `targetWindow` — iframe / popup / happy-dom safe), adding no new
scheduling logic. New `ActiveDomFrameCleanup` type exported from the barrel;
`raf` also implemented on the disabled-dom stub (throws, like `requestFrame`).
Why: the doctrine is "all DOM activity via ActiveDom". `listen`/`observe*`
already returned disposers; the animation frame was the gap — `requestFrame`
exposes a raw numeric handle (per-call bookkeeping + leak risk), and layout
components were falling back to the GLOBAL `requestAnimationFrame`, which
targets the wrong window in iframe/popup contexts (the exact bug getWindow/
getDocument fix elsewhere). `raf` closes it.
Migrated the 3 raw `requestAnimationFrame` sites the Words audit surfaced —
words-block-gutter (reposition), words-bubble-menu + words-slash-menu
(overlay position) — to `dom.raf(...)`. Bubble/slash keep their `tick()`
fallback for no-rAF environments.
Documented the decision + rationale as a dated Backlog entry at the end of
`src/arts/adom/README.md` (and listed `raf` in the API + imperative-actions
sections). Notes the kept distinction: `raf` is for layout frames, NOT the
`$timer` lifecycle scheduler; low-level requestFrame/cancelFrame stays for
consumers that already hold the handle (drawer/slider/splitter/floating/
focus-scope). Remaining raw rAF in other eidos components (tabs-indicator…)
left for when those are touched — flagged in the backlog.
Gates: npm run check 1 error (pre-existing grafito, not adom/Words) · soma
words + adom 479/479 · prettier clean · browser smoke: gutter repositions,
bubble menu positions, no console errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
$libs/days/format.ts:
- Nuevo `getCachedRelativeTimeFormat(locale, options)` — twin del cache
ya existente `getCachedDateFormat`. Key `${locale}:${JSON.stringify(opts)}`.
Sites que renderizan listas de timestamps relativos (feeds, activity
logs) hit identical pairs y mantienen el loop caliente.
arts/format/dates/engine-dates.ts + types.ts:
- Nuevo `formatRelative(value, unit, options?, locale?)` en EngineDates.
Defaults: `numeric: 'auto'` (colapsa 0/-1/+1 en "yesterday"/"today"),
`style: 'long'`. Per-call locale override no muta engine state.
arts/format/dates/active-dates.svelte.ts:
- formatRelative wired via `readFrom(runtime, engine.formatRelative)`
para que el $derived del componente sea reactivo al cambio de locale.
Componente relative-time.svelte:
- Drop del bypass directo a `new Intl.RelativeTimeFormat(locale, opts)`.
Ahora SIEMPRE rutea por `uix.format.dates.formatRelative(magnitude,
unit, { numeric, style }, locale)`. El componente sigue haciendo la
matematica del delta + auto-unit + magnitud — el engine solo expone
la operacion atomica.
- Fallback a Intl directo solo cuando no hay runtime (eidos fuera de
UixApp).
Tests: 17/17 en engine-dates.test.ts (+3 nuevos: formatRelative con
locale del engine, numeric/style options, per-call locale override sin
mutar state).
Docs: arts/format/README.md sub-seccion en Dates con la nueva API.
relative-time/README.md decisiones actualizadas (ya no es la excepcion).
Cierra el ultimo follow-up del sprint de service components — los 4
componentes (Trans, FormatNumber, FormatDate, RelativeTime) ahora
rutean uniformemente por el runtime UIX.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Engine (`arts/format/numbers/engine-numbers.ts` + `types.ts`):
- format(value, options?, locale?) acepta tercer arg locale como en
dates. Cache (getCachedNumberFormat) keyea por (locale x options).
- formatPercent / formatCompact / formatCurrency / formatUnit propagan
el locale al format() interno.
- No muta engine state (getLocale() sigue devolviendo el activo).
Component (`format-number.svelte`):
- Drop del bypass `new Intl.NumberFormat(locale, options)` cuando habia
locale prop. Ahora SIEMPRE rutea por numbers.format(inputValue,
options, locale). Preserva cache + separadores activos + defaultFormat
+ reactividad del locale.
- Fallback a Intl directo solo cuando no hay runtime (eidos fuera de
UixApp).
Tests: 9/9 en engine-numbers.test.ts (+4 cubriendo format/Percent/
Compact/Currency/Unit con locale override sin mutar state).
Docs: arts/format/README.md sub-seccion "Override de locale por
llamada" en Numbers (handoff 2026-05-25). format-number/README.md
decisiones actualizadas.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sprint completo:
- Card (eidos + sema): elevation/intent/interactive, eventos select+activate.
- Avatar: ring, AvatarBadge, AvatarGroup separados; demo Avatar standalone.
- Image: status surface (idle/loading/loaded/error) consumiendo soma ImageProvider.
- SText + STextVirtualList: canvas-text engine portado de morfo-driven-soma como
componentes separados para optimizar bundle (no se importa canvas si no se usa).
- Badge: primitivo eidos puro.
- Trans, FormatNumber, FormatDate, RelativeTime: 4 service components greppables
alrededor de uix.langs y uix.format. Nav group "Utilities".
Bugs fix arts/format/dates (engine-dates.ts):
1. Preset / per-axis conflict: Intl.DateTimeFormat rechaza dateStyle/timeStyle
con cualquier per-axis (weekday/era/year/month/day/dayPeriod/hour/minute/
second/fractionalSecondDigits) o con timeZoneName. Engine drop-ea ambos
presets por defecto cuando detecta conflict (regla estricta — un solo
per-axis hace ilegales los dos presets).
2. Per-call locale override: formatDate/Time/DateTime aceptan tercer arg
locale?: string. No muta engine state. Cache (getCachedDateFormat) keyea
por (locale x options). resolveCallHourCycle: cuando hay locale override
y pref auto, el ciclo horario se resuelve desde el locale OVERRIDE.
3. withHourCycle precedence: caller hourCycle/hour12 ahora gana sobre
preferencia del engine. Antes spread mal ordenado sobrescribia 'h12' del
caller con 'h23' del engine cuando locale resolvia a 24h.
FormatDate component vuelve a rutear siempre por uix.format.dates
(cache + hourCycle + locale reactivo), pasando locale como tercer arg.
Demos service components autonomos del toolbar global: cada uno owns su
demoLocale state con 7 chips (es-ES/en-US/en-GB/de-DE/fr-FR/ja-JP/ar-EG).
FormatDate stage filtra dateStyle/timeStyle por mode + chip hourCycle anadido.
Tests: 14/14 passing en engine-dates.test.ts (+6 nuevos: conflict handling,
locale override, hourCycle precedence).
Docs: arts/format/README.md (handoff 2026-05-25 con las 3 reglas),
README.md por service component, project_service_components_sprint_2026-05-25.md,
feedback_service_demos_own_locale.md (pattern reusable).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Toggle as the eidos pilot: subdirectory layout (recipe + Svelte wrapper +
types + index + README) replacing the flat CSS-only form. Pattern is
documented in eidos/components/README.md and the toggle README.
Shared types live in eidos/lib/types.ts. First export is `Size` (8 values
xxs..xxl + full); components narrow with `Extract<Size, ...>` per the
per-component-subset doctrine. No `Eidos` prefix on types — module path
already conveys the layer.
API doctrine:
- soma stays compound (Toggle.Provider) for symmetry with multi-part
- eidos exports both default + Provider so single-part components
accept both `<Toggle>` (ergonomic) and `<Toggle.Provider>`
(compound-style consumers)
SoundChannel eager-init fixes the autoplay race: AudioContext is created
+ resumed synchronously on the first user gesture (capture-phase
listener registered in the constructor), avoiding the previous race
where the first emit() scheduled the resume in a microtask outside the
gesture window.
Demo page (web/routes/toggle/+page.svelte) restructured so the live
preview renders ALWAYS above the tablist — Sema-tab Play buttons can
fire on the real toggle. Motion preview amplifies scale ×8 visually
only; doctrinal values stay in the <dl>.
Conventions 7-13 added to src/docs/sema-implementation-guide.md
covering: directory structure, wrapper composition, no Eidos prefix,
soma compound vs eidos flat, iconOnly sr-only body, sound eager-init,
docs-preview amplification.
CLAUDE.md gets a session hand-off block listing where things stand and
next concrete steps (migrate switch/collapsible/dialog/drawer/popover/
toast/avatar; wire topbar sound mute to masterGain; rename theme
tokens to drop the success/warning/danger fallback aliases).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Remove src/uix/soma/components/form/components/form.svelte.test.ts
— same casualty as the auto-fields test removed in phase B: imports
from \$langs and \$lib/sium/* (both deleted).
- Update arts/session/ssr.ts JSDoc example: \$lib/app → \$active-app.
Note: form.svelte and form-auto-fields.svelte themselves still import
from \$lib/sium/* and \$langs. They will not compile against the new
ecosystem and need a rewrite or deletion as a separate decision —
flagged but not changed here.
Also note: src/arts/cache/test/engine-cache.test.ts fails with
"defineActiveCache is not a function". The test file is untracked
(was never committed); the failure predates this branch and is not
caused by the cleanup.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>