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/soma/SOMA_ARCHITECTURE.md

912 lines
41 KiB

# Soma Architecture
Documento de referencia arquitectonica para `src/uix/soma`.
docs: prune obsolete audits + studies, refresh remaining references Deleted 60 obsolete documents that no longer reflect the active-uix state: - 47 per-component audit logs (*-audit.md, audit_N.md, AUDIT.md) - 5 global soma audits (AUDIT_1, codex_audit, soma-audit-04-20/21, audit-prompt template) - 5 closed studies (soma/study, morfo/study, morfo/DESIGN, morfo/ PROVIDER_STUDY_2026-04-23, uix/CONTINUITY_2026-04-24) - 2 superseded refactor proposals (refactor_claude, refactor_code) - 1 stale bug log (form/BUG-onchange-onblur-hang — repro path src/routes/test/sium/kitchen-sink no longer exists) Updated the remaining operational references to drop dead links and reflect the post-cleanup architecture: - src/uix/README.md — rewritten links section, dropped pointers to terra/air/CONTINUITY/refactor docs, updated adom/dom paths - src/uix/active_architecture.md — header now dated 2026-05-08, lists the major shifts since 2026-04-26 (provider inheritance dropped, eidos in production, doctrina parte IV, sound eager-init); pendings section reflects the eidos migration roadmap; "see also" purged of deleted files - src/uix/morfo/README.md — file tree updated (removed DESIGN/study, added compile/runtime/PERMUTATION_RUNNER); see-also pruned - src/uix/sema/README.md — SoundChannel section now describes the eager-init pattern (constructor-time, not lazy); pointer to sema-implementation-guide added - src/uix/soma/README.md — drops terra/air mentions, declares the actual dependency direction (eidos -> soma, not the reverse) - src/uix/soma/SOMA_ARCHITECTURE.md — §15 rewritten as "Estado actual y deuda histórica" reflecting Provider-inheritance-drop; demo path updated to web/routes/ - src/uix/soma/COMPONENT_GUIDE.md — A23 dates section now points to $libs/days (no external/dates façade); demo paths swapped from /test/soma/* to web/routes/ - src/uix/morfo/PERMUTATION_RUNNER.md — paths swapped to web/routes/ with a relocation note - CLAUDE.md — Refactor Documents section replaced with Reference Documents pointing to per-layer READMEs npm run check holds at baseline (39 pre-existing errors, none new). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
`soma` es la capa de primitives headless del sistema UIX. Implementa
behavior, accesibilidad y composicion de partes; la presentacion visual
es responsabilidad de eidos. Cada componente se describe primero como
**morfo** (contrato declarativo) y soma lo materializa mediante clases
concretas de estado que registran sus partes en `SomaRuntime`.
## 1. Proposito
`soma` existe para dar una base comun sobre la que construir interfaces complejas sin repetir la misma logica de:
- contexto compartido
- control de foco
- teclado y puntero
- `aria-*`
- `data-*`
- sincronizacion de estado
- integracion con servicios transversales
- animaciones de entrada/salida
- posicionamiento flotante
Su objetivo no es ser una capa visual ni de producto. `soma` define primitives reutilizables y predecibles; la capa visual decide el look and feel.
## 2. Arquitectura de capas
```
soma → headless: behavior, accesibilidad, data-* contracts, context, servicios
eidos → visual: tokens, CSS, temas, recipes, reacciones a data-event-*
events → percepcion: sound/haptic/hold y dispatch de ocurrencias semanticas
app → producto: composicion final, contenido, logica de negocio
```
Cada capa tiene responsabilidades estrictas:
### soma aporta
- comportamiento (keyboard, focus, dismiss, scroll lock)
- accesibilidad (ARIA, roles, live regions)
- contratos `data-*` estables y validados
- contexto y composicion de partes
- servicios de runtime (`langs`, `format`, `logger`)
- sistema de animaciones (presence, data-starting/ending-style, onComplete)
- posicionamiento flotante (@floating-ui)
### La capa visual aporta
- apariencia (tokens, colores, tipografia, spacing)
- tono visual (temas light/dark, variantes)
- decisiones de diseno opinionated (sizes, recipes)
- motion CSS y reacciones visuales a `data-event-*`
- responsive design
### La capa visual NUNCA
- importa state classes internas de soma
- depende de estructura DOM incidental
- accede a propiedades privadas
- duplica behavior que soma ya resuelve
- usa `data-*` fuera de los contratos publicados
La frontera es los `data-*` attrs y las CSS variables que soma expone.
## 3. Principios de diseno
### 3.1 El desarrollador no necesita conocer los internos
Los layers, el sistema reactivo, el floating engine — son implementacion interna. El desarrollador de componentes interactua con:
- clases concretas de estado + `SomaRuntime`
- `Soma` class para servicios
- Barrel imports jerárquicos (`import { Dialog } from '$soma/components'`)
### 3.2 Un patron, no tres
Todo componente sigue el mismo patron:
1. State class concreta registra sus partes con `SomaRuntime.part(...)`
2. Wrapper `.svelte` fino convierte props → Active/State
3. Props derivados via `$derived.by` + `runtimePart.assert`
4. Contexto para comunicacion padre-hijo
Los roots sin DOM usan `ProviderOpts` (ref opcional), las partes con DOM usan
`WithRefOpts`. Las unicas excepciones son partes declarativas que pueden vivir
fuera de su provider (`AnnounceRegion`, `FeedSentinel`): si encuentran un
provider reutilizan su `runtime`; si no, crean un runtime propio desde el
`Soma` del scope actual.
### 3.3 Layers como behaviors, no como wrappers
Los layers se instancian en el constructor del Provider y exponen `.props` para merge. No hay nesting de componentes wrapper en template.
```ts
// Correcto: behaviors integrados
readonly focusScope = FocusScope.use({...});
readonly dismissal = Dismissal.use({...});
readonly props = $derived.by(() => this.runtimePart.assert({
...this.runtimePart.props,
...this.focusScope.props,
...this.dismissal.props,
}));
```
```svelte
<!-- Incorrecto: nesting de wrappers (patron terra) -->
<ScrollLock>
soma: gesture layer, drawer component, full audit fixes across 26 components Gesture Layer: - Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking - VelocityTracker with ring buffer, staleness check, projection - 52 tests (velocity + gesture integration) - setPointerCapture deferred until moveBuffer exceeded (prevents click stealing) Drawer Component: - 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close - Gesture.drag() integration for swipe dismiss - Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle - Modal/non-modal/non-dismissible modes - Logical directions (start/end) RTL-aware via soma presentation - CSS custom properties (--drawer-progress, --drawer-offset-x/y) Architecture: - T1: 219 files migrated from $soma/ alias to relative paths - T2: 35 wrappers — noop replaced with () => {} inline - Translation system: componentLangs + common.buttons.* + idlangref constants - COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns - contracts.ts: case-normalized registerContract/assertContract Audit Fixes (P0 critical): - DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super - TB1: data-table attr on wrapper - CH1/SW1/NF4: hidden input rendering fixes - SP2: panel cleanup on unmount - RG1: roving tabindex fallback to first item - CO1: aria-labelledby on collapsible content Audit Fixes (P1 high): - SafePolygon integrated in dropdown-menu SubProvider + tooltip - 9 langs.ts files with idlangref constants - Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards - Stepper: count decrement + private, aria-label on List - ToggleGroup: roving tabindex in multiple mode - Select: pure aria-activedescendant (no mixed focus), label registry - NumberField: startValue respects min, scrubber commit on pointerup only - Toolbar: element-based focus tracking (not index) Audit Fixes (P2 medium): - A6: timer/listener cleanup in 6 components - A12: getDirectionalKeys in 4 components - A10: DOM queries scoped to root in 3 components - Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern - Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup - Pagination: reactive page clamping, Set-based computePageRange - Scroll-area: thumbSize zero-guard, mount flash prevention - Splitter: collapse/expand respects adjacent constraints, immediate pointer capture 5 issues remain (design decisions): stepper registry, table sub-row IDs, tags-input ARIA model, tags-input paste, toast exit animation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
<FocusScope>
<DismissibleLayer>
{content}
</DismissibleLayer>
</FocusScope>
</ScrollLock>
```
### 3.4 Soma es una clase, no una configuracion
`Soma` es la identidad runtime del framework. No es un archivo de configuracion — es el objeto raiz que provee servicios via context.
```ts
// En un Provider:
readonly soma = Soma.require();
const dir = this.soma.prefs.getDir();
```
soma: gesture layer, drawer component, full audit fixes across 26 components Gesture Layer: - Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking - VelocityTracker with ring buffer, staleness check, projection - 52 tests (velocity + gesture integration) - setPointerCapture deferred until moveBuffer exceeded (prevents click stealing) Drawer Component: - 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close - Gesture.drag() integration for swipe dismiss - Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle - Modal/non-modal/non-dismissible modes - Logical directions (start/end) RTL-aware via soma presentation - CSS custom properties (--drawer-progress, --drawer-offset-x/y) Architecture: - T1: 219 files migrated from $soma/ alias to relative paths - T2: 35 wrappers — noop replaced with () => {} inline - Translation system: componentLangs + common.buttons.* + idlangref constants - COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns - contracts.ts: case-normalized registerContract/assertContract Audit Fixes (P0 critical): - DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super - TB1: data-table attr on wrapper - CH1/SW1/NF4: hidden input rendering fixes - SP2: panel cleanup on unmount - RG1: roving tabindex fallback to first item - CO1: aria-labelledby on collapsible content Audit Fixes (P1 high): - SafePolygon integrated in dropdown-menu SubProvider + tooltip - 9 langs.ts files with idlangref constants - Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards - Stepper: count decrement + private, aria-label on List - ToggleGroup: roving tabindex in multiple mode - Select: pure aria-activedescendant (no mixed focus), label registry - NumberField: startValue respects min, scrubber commit on pointerup only - Toolbar: element-based focus tracking (not index) Audit Fixes (P2 medium): - A6: timer/listener cleanup in 6 components - A12: getDirectionalKeys in 4 components - A10: DOM queries scoped to root in 3 components - Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern - Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup - Pagination: reactive page clamping, Set-based computePageRange - Scroll-area: thumbSize zero-guard, mount flash prevention - Splitter: collapse/expand respects adjacent constraints, immediate pointer capture 5 issues remain (design decisions): stepper registry, table sub-row IDs, tags-input ARIA model, tags-input paste, toast exit animation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
### 3.5 Los data-\* son contrato publico
Los `data-*` attrs son la frontera entre soma y la capa visual. Cambiarlos es breaking change.
soma: dias date library, Calendar + RangeCalendar, Command fixes, doc updates **Date library reorganisation** (no new third-party dependency) - New `src/lib/util/dias/` with `_vendor/` (Adobe Apache 2.0 vendored code, ATTRIBUTION.md preserved) and a public facade split into `types`, `values`, `calendars`, `queries`, `operations`, `parse`, `format`, `index`. Consumers import only from `$lib/util/dias`. - All date-related `Intl` consolidated in `dias/format.ts` — `DateFormatter`, cache, `resolveDateOrder`, `resolveHourCycle`, placeholders, defaults. - `ext/dates` now a thin reactive wrapper delegating to `dias`. - Aligned App-layer `HourCycle` to canonical numeric form `12 | 24` (matches `Intl.DateTimeFormat`'s `hour12`). Removed string form `'12h' | '24h'` from `ext/app/types`, `ext/dates`, `ext/presentation/locale-defaults`, and their tests. - Repointed every consumer: `soma/external/dates`, `terra/external/dates`, eight `routes/test/*` pages. - Legacy `src/lib/util/dates/` left untouched by request. **Tier 3 components** - `soma/components/calendar/` — 14 parts, single + multiple selection, `MonthSelect`/`YearSelect`, ISO/simple week numbers, `isDateHoliday` matcher, `data-weekend` flag, navigation live- region, RTL flip, full ARIA, 27-item checklist verified. - `soma/components/range-calendar/` — 14 parts, range selection with live hover preview (novel vs ark/bits/radix/react-aria), `minDays` / `maxDays` / `allowSingleDay`, third-click deselects only the clicked endpoint. - Test pages at `/test/soma/calendar` and `/test/soma/range-calendar` with seven demos each. Links added to the soma index under a new "Dates" section. - READMEs with anatomy, parts, props, ARIA, keyboard, data-attrs, and a competitive comparison table. **Command fixes** - Resolved O(N^2) reactive cascade: introduced `visibleSet: $derived<Set<string>>` on the root provider; per-item `isVisible` is now an O(1) `has()` lookup instead of calling back into the provider, which was re-running all items' derivations on every `itemsVersion` bump. - Added `dir` prop with fallback to `soma.presentation.getDir()`; horizontal arrows flip in RTL via `getDirectionalKeys`; root receives `dir` attribute. **Doc updates (session outcomes)** - `CLAUDE.md`: replaced obsolete `@/glob/lib` date references with `$lib/util/dias`; added canonical date-layer section; reinforced `Provider` export rule and `data-{component}[-{part}]` naming (never `data-soma-*`); clarified translation convention. - `src/uix/soma/COMPONENT_GUIDE.md`: new checklist items 26 (component README required) and 27 (grep for data-attr consistency); new Common Mistakes 11 (`data-soma-*` prefix), 12 (wrong date module), 13 (string `HourCycle`). - `src/uix/soma/SOMA_ARCHITECTURE.md`: clarified data-attr naming (canonical emitter is `createAttrs`; validator checks enum values only, not names); new section covering date types and formatting through `$soma/external/dates` → `$lib/util/dias`; documented numeric `HourCycle`. **Infrastructure** - `PrimitiveHeaderAttributes` and `PrimitiveSelectAttributes` added to `$soma/types`. - `getLastFirstDayOfWeek`, `getNextLastDayOfWeek` re-exported from `$soma/external/dates`. - `calendar.*` and `range-calendar.*` translation blocks added to `$soma/core/langs.ts`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
Convencion (obligatoria, sin excepciones):
soma: dias date library, Calendar + RangeCalendar, Command fixes, doc updates **Date library reorganisation** (no new third-party dependency) - New `src/lib/util/dias/` with `_vendor/` (Adobe Apache 2.0 vendored code, ATTRIBUTION.md preserved) and a public facade split into `types`, `values`, `calendars`, `queries`, `operations`, `parse`, `format`, `index`. Consumers import only from `$lib/util/dias`. - All date-related `Intl` consolidated in `dias/format.ts` — `DateFormatter`, cache, `resolveDateOrder`, `resolveHourCycle`, placeholders, defaults. - `ext/dates` now a thin reactive wrapper delegating to `dias`. - Aligned App-layer `HourCycle` to canonical numeric form `12 | 24` (matches `Intl.DateTimeFormat`'s `hour12`). Removed string form `'12h' | '24h'` from `ext/app/types`, `ext/dates`, `ext/presentation/locale-defaults`, and their tests. - Repointed every consumer: `soma/external/dates`, `terra/external/dates`, eight `routes/test/*` pages. - Legacy `src/lib/util/dates/` left untouched by request. **Tier 3 components** - `soma/components/calendar/` — 14 parts, single + multiple selection, `MonthSelect`/`YearSelect`, ISO/simple week numbers, `isDateHoliday` matcher, `data-weekend` flag, navigation live- region, RTL flip, full ARIA, 27-item checklist verified. - `soma/components/range-calendar/` — 14 parts, range selection with live hover preview (novel vs ark/bits/radix/react-aria), `minDays` / `maxDays` / `allowSingleDay`, third-click deselects only the clicked endpoint. - Test pages at `/test/soma/calendar` and `/test/soma/range-calendar` with seven demos each. Links added to the soma index under a new "Dates" section. - READMEs with anatomy, parts, props, ARIA, keyboard, data-attrs, and a competitive comparison table. **Command fixes** - Resolved O(N^2) reactive cascade: introduced `visibleSet: $derived<Set<string>>` on the root provider; per-item `isVisible` is now an O(1) `has()` lookup instead of calling back into the provider, which was re-running all items' derivations on every `itemsVersion` bump. - Added `dir` prop with fallback to `soma.presentation.getDir()`; horizontal arrows flip in RTL via `getDirectionalKeys`; root receives `dir` attribute. **Doc updates (session outcomes)** - `CLAUDE.md`: replaced obsolete `@/glob/lib` date references with `$lib/util/dias`; added canonical date-layer section; reinforced `Provider` export rule and `data-{component}[-{part}]` naming (never `data-soma-*`); clarified translation convention. - `src/uix/soma/COMPONENT_GUIDE.md`: new checklist items 26 (component README required) and 27 (grep for data-attr consistency); new Common Mistakes 11 (`data-soma-*` prefix), 12 (wrong date module), 13 (string `HourCycle`). - `src/uix/soma/SOMA_ARCHITECTURE.md`: clarified data-attr naming (canonical emitter is `createAttrs`; validator checks enum values only, not names); new section covering date types and formatting through `$soma/external/dates` → `$lib/util/dias`; documented numeric `HourCycle`. **Infrastructure** - `PrimitiveHeaderAttributes` and `PrimitiveSelectAttributes` added to `$soma/types`. - `getLastFirstDayOfWeek`, `getNextLastDayOfWeek` re-exported from `$soma/external/dates`. - `calendar.*` and `range-calendar.*` translation blocks added to `$soma/core/langs.ts`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
- provider: `data-{component}` (no `data-{component}-provider`, **no `data-soma-*`**)
- parte: `data-{component}-{part}`
- estado: `data-state`, `data-disabled`, `data-side`, `data-align`, `data-orientation`
- animacion: `data-starting-style`, `data-ending-style`
- nesting: `data-nested`, `data-nested-open`
Los nombres los emite el compilador de morfo que consume `SomaRuntime`.
`createAttrs(morfo)` queda como helper tipado para `querySelector` y tooling,
no como sistema de registro ni escritura DOM. Cualquier selector CSS, cadena
en README o snippet debe coincidir exactamente con esos nombres generados. El
validador de contratos (`assertContract`) solo verifica valores enumerados, no
nombres ni presencia — la consistencia de nombres es responsabilidad del autor
del componente (checklist item 27).
soma: dias date library, Calendar + RangeCalendar, Command fixes, doc updates **Date library reorganisation** (no new third-party dependency) - New `src/lib/util/dias/` with `_vendor/` (Adobe Apache 2.0 vendored code, ATTRIBUTION.md preserved) and a public facade split into `types`, `values`, `calendars`, `queries`, `operations`, `parse`, `format`, `index`. Consumers import only from `$lib/util/dias`. - All date-related `Intl` consolidated in `dias/format.ts` — `DateFormatter`, cache, `resolveDateOrder`, `resolveHourCycle`, placeholders, defaults. - `ext/dates` now a thin reactive wrapper delegating to `dias`. - Aligned App-layer `HourCycle` to canonical numeric form `12 | 24` (matches `Intl.DateTimeFormat`'s `hour12`). Removed string form `'12h' | '24h'` from `ext/app/types`, `ext/dates`, `ext/presentation/locale-defaults`, and their tests. - Repointed every consumer: `soma/external/dates`, `terra/external/dates`, eight `routes/test/*` pages. - Legacy `src/lib/util/dates/` left untouched by request. **Tier 3 components** - `soma/components/calendar/` — 14 parts, single + multiple selection, `MonthSelect`/`YearSelect`, ISO/simple week numbers, `isDateHoliday` matcher, `data-weekend` flag, navigation live- region, RTL flip, full ARIA, 27-item checklist verified. - `soma/components/range-calendar/` — 14 parts, range selection with live hover preview (novel vs ark/bits/radix/react-aria), `minDays` / `maxDays` / `allowSingleDay`, third-click deselects only the clicked endpoint. - Test pages at `/test/soma/calendar` and `/test/soma/range-calendar` with seven demos each. Links added to the soma index under a new "Dates" section. - READMEs with anatomy, parts, props, ARIA, keyboard, data-attrs, and a competitive comparison table. **Command fixes** - Resolved O(N^2) reactive cascade: introduced `visibleSet: $derived<Set<string>>` on the root provider; per-item `isVisible` is now an O(1) `has()` lookup instead of calling back into the provider, which was re-running all items' derivations on every `itemsVersion` bump. - Added `dir` prop with fallback to `soma.presentation.getDir()`; horizontal arrows flip in RTL via `getDirectionalKeys`; root receives `dir` attribute. **Doc updates (session outcomes)** - `CLAUDE.md`: replaced obsolete `@/glob/lib` date references with `$lib/util/dias`; added canonical date-layer section; reinforced `Provider` export rule and `data-{component}[-{part}]` naming (never `data-soma-*`); clarified translation convention. - `src/uix/soma/COMPONENT_GUIDE.md`: new checklist items 26 (component README required) and 27 (grep for data-attr consistency); new Common Mistakes 11 (`data-soma-*` prefix), 12 (wrong date module), 13 (string `HourCycle`). - `src/uix/soma/SOMA_ARCHITECTURE.md`: clarified data-attr naming (canonical emitter is `createAttrs`; validator checks enum values only, not names); new section covering date types and formatting through `$soma/external/dates` → `$lib/util/dias`; documented numeric `HourCycle`. **Infrastructure** - `PrimitiveHeaderAttributes` and `PrimitiveSelectAttributes` added to `$soma/types`. - `getLastFirstDayOfWeek`, `getNextLastDayOfWeek` re-exported from `$soma/external/dates`. - `calendar.*` and `range-calendar.*` translation blocks added to `$soma/core/langs.ts`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
### 3.6 La accesibilidad base no se delega
soma resuelve ARIA por defecto. El consumidor no necesita añadir `role`, `aria-modal`, `aria-expanded`, `aria-controls`, etc. — el Provider los genera.
Texto funcional se resuelve via `langs.ts()` con idlangref. El catalogo propio del componente vive en `morfo.translations` y se referencia con `v.translationRef(...)`; texto compartido como close/cancel/save vive en `common.*` y se referencia con `v.commonRef(...)` o un idlangref absoluto. `langs.ts` por componente queda como comodidad opcional para constantes imperativas, no como catalogo canonico.
### 3.7 DOM global via ActiveDom
Soma usa el `ActiveDom` del scope para escrituras gestionadas por UIX,
listeners de `document/window`, queries globales, foco imperativo y scroll de
ventana. No existe fachada `soma/events`: `dom.listen(...)` es la superficie
canonica para registrar listeners con cleanup.
Las lecturas locales de un elemento propio (`contains`, `closest`,
`getBoundingClientRect`, `clientWidth`, `scrollTop`) no se envuelven en
`ActiveDom`; son parte del comportamiento local del componente.
### 3.8 Props documentadas obligatoriamente
Todas las props de todos los componentes llevan JSDoc en `types.ts`. Cada prop: descripcion, `@default`, notas de comportamiento.
### 3.9 Comparacion con referencias
Cada componente se compara con ark-ui, bits-ui y radix-ui antes de implementar. Se documentan las props que otros tienen y soma no, con justificacion.
## 3.bis Arquitectura cerrada (post-2026-04-25)
El reparto de responsabilidades entre Morfo, Soma, Sema y ADom esta cerrado en
seis piezas con responsabilidades disjuntas:
```
Morfo declara
soma/morfo/active-uix: relocate runtime + rename SomaRuntime + sema opcional Architectural correction. Morfo was importing Svelte runes, $adom, and $uix/sema — all violations of "morfo is pure declarative DNA". The runtime that interprets a CompiledMorfo lives where it executes: soma. Move: - src/uix/morfo/runtime.svelte.ts → src/uix/soma/runtime.svelte.ts - src/uix/morfo/runtime.svelte.test.ts → src/uix/soma/runtime.svelte.test.ts Rename (consistent with the move; "soma's runtime", per the doctrine "morfo declares, soma executes"): - class MorfoRuntime → SomaRuntime - createMorfoRuntime() → createSomaRuntime() - type MorfoRuntimeSources → SomaRuntimeSources - error prefixes [morfo-runtime] → [soma-runtime] Sema is now genuinely ornamental: - ActiveUix.semantic returns EngineSemantic | undefined (no longer throws when no engine is configured). Components in SSR / headless tests / audio-disabled environments stay functional. - SomaRuntime.trigger() skips the emit step AND the target-resolution check when no engine is present — only requires a DOM target when something will actually be emitted. - Soma.semantic getter returns | undefined too (was non-optional). Consumers updated: - src/uix/active-uix/active-uix.svelte.ts — imports createSomaRuntime from $soma; semantic getter returns | undefined - src/uix/active-uix/types.ts — interface SomaRuntime, semantic | undefined - 8 providers (toggle, switch, collapsible, dialog, drawer, popover, toast, avatar) — type SomaRuntime, import path $soma - src/uix/soma/index.ts — re-exports createSomaRuntime + types - src/uix/morfo/index.ts — drops the runtime re-exports (morfo is pure) - src/uix/sema/signal.ts — JSDoc reference updated - src/uix/soma/core/soma.svelte.ts — semantic getter | undefined Test added: SomaRuntime.trigger() succeeds without a semantic engine (skips emit + target check, runs handler directly). Docs aligned to the new shape: - active_architecture.md — runtime locations, sema as ornamental - soma/SOMA_ARCHITECTURE.md, soma/README.md — name + import paths - morfo/README.md — file listing now flags morfo as PURE TS, points at soma/runtime.svelte.ts for the interpreter - sema/README.md — SomaRuntime references - README.md — root architecture diagram - CLAUDE.md — hand-off note Tests 218/218 green. npm run check holds at the 39-error baseline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
SomaRuntime transcribe (vive en soma/)
Provider aporta sources, targets y handlers
Effects sincronizan attrs derivados
EngineSemantic despacha senales a canales perceptivos
VisualChannel materializa la senal en el DOM (data-event*, hold, cleanup)
ADom aplica mutaciones DOM (commit estructural)
```
soma/morfo/active-uix: relocate runtime + rename SomaRuntime + sema opcional Architectural correction. Morfo was importing Svelte runes, $adom, and $uix/sema — all violations of "morfo is pure declarative DNA". The runtime that interprets a CompiledMorfo lives where it executes: soma. Move: - src/uix/morfo/runtime.svelte.ts → src/uix/soma/runtime.svelte.ts - src/uix/morfo/runtime.svelte.test.ts → src/uix/soma/runtime.svelte.test.ts Rename (consistent with the move; "soma's runtime", per the doctrine "morfo declares, soma executes"): - class MorfoRuntime → SomaRuntime - createMorfoRuntime() → createSomaRuntime() - type MorfoRuntimeSources → SomaRuntimeSources - error prefixes [morfo-runtime] → [soma-runtime] Sema is now genuinely ornamental: - ActiveUix.semantic returns EngineSemantic | undefined (no longer throws when no engine is configured). Components in SSR / headless tests / audio-disabled environments stay functional. - SomaRuntime.trigger() skips the emit step AND the target-resolution check when no engine is present — only requires a DOM target when something will actually be emitted. - Soma.semantic getter returns | undefined too (was non-optional). Consumers updated: - src/uix/active-uix/active-uix.svelte.ts — imports createSomaRuntime from $soma; semantic getter returns | undefined - src/uix/active-uix/types.ts — interface SomaRuntime, semantic | undefined - 8 providers (toggle, switch, collapsible, dialog, drawer, popover, toast, avatar) — type SomaRuntime, import path $soma - src/uix/soma/index.ts — re-exports createSomaRuntime + types - src/uix/morfo/index.ts — drops the runtime re-exports (morfo is pure) - src/uix/sema/signal.ts — JSDoc reference updated - src/uix/soma/core/soma.svelte.ts — semantic getter | undefined Test added: SomaRuntime.trigger() succeeds without a semantic engine (skips emit + target check, runs handler directly). Docs aligned to the new shape: - active_architecture.md — runtime locations, sema as ornamental - soma/SOMA_ARCHITECTURE.md, soma/README.md — name + import paths - morfo/README.md — file listing now flags morfo as PURE TS, points at soma/runtime.svelte.ts for the interpreter - sema/README.md — SomaRuntime references - README.md — root architecture diagram - CLAUDE.md — hand-off note Tests 218/218 green. npm run check holds at the 39-error baseline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
### SomaRuntime — la pieza nueva
soma/morfo/active-uix: relocate runtime + rename SomaRuntime + sema opcional Architectural correction. Morfo was importing Svelte runes, $adom, and $uix/sema — all violations of "morfo is pure declarative DNA". The runtime that interprets a CompiledMorfo lives where it executes: soma. Move: - src/uix/morfo/runtime.svelte.ts → src/uix/soma/runtime.svelte.ts - src/uix/morfo/runtime.svelte.test.ts → src/uix/soma/runtime.svelte.test.ts Rename (consistent with the move; "soma's runtime", per the doctrine "morfo declares, soma executes"): - class MorfoRuntime → SomaRuntime - createMorfoRuntime() → createSomaRuntime() - type MorfoRuntimeSources → SomaRuntimeSources - error prefixes [morfo-runtime] → [soma-runtime] Sema is now genuinely ornamental: - ActiveUix.semantic returns EngineSemantic | undefined (no longer throws when no engine is configured). Components in SSR / headless tests / audio-disabled environments stay functional. - SomaRuntime.trigger() skips the emit step AND the target-resolution check when no engine is present — only requires a DOM target when something will actually be emitted. - Soma.semantic getter returns | undefined too (was non-optional). Consumers updated: - src/uix/active-uix/active-uix.svelte.ts — imports createSomaRuntime from $soma; semantic getter returns | undefined - src/uix/active-uix/types.ts — interface SomaRuntime, semantic | undefined - 8 providers (toggle, switch, collapsible, dialog, drawer, popover, toast, avatar) — type SomaRuntime, import path $soma - src/uix/soma/index.ts — re-exports createSomaRuntime + types - src/uix/morfo/index.ts — drops the runtime re-exports (morfo is pure) - src/uix/sema/signal.ts — JSDoc reference updated - src/uix/soma/core/soma.svelte.ts — semantic getter | undefined Test added: SomaRuntime.trigger() succeeds without a semantic engine (skips emit + target check, runs handler directly). Docs aligned to the new shape: - active_architecture.md — runtime locations, sema as ornamental - soma/SOMA_ARCHITECTURE.md, soma/README.md — name + import paths - morfo/README.md — file listing now flags morfo as PURE TS, points at soma/runtime.svelte.ts for the interpreter - sema/README.md — SomaRuntime references - README.md — root architecture diagram - CLAUDE.md — hand-off note Tests 218/218 green. npm run check holds at the 39-error baseline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
`SomaRuntime` es la pieza que faltaba entre `Morfo` (declaracion) y
`Provider` (ejecucion). Lee el morfo y produce el comportamiento.
Una instancia por componente:
```ts
readonly soma = Soma.require();
readonly runtime = this.soma.runtime(morfo, {
states: { open: () => this.opts.open.current },
props: { disabled: () => this.opts.disabled.current },
parts: { content: () => this.contentId.current },
events: {
open: () => {
this.opts.open.current = true;
},
'close-cancel': () => {
this.opts.open.current = false;
}
}
});
```
API V1:
- `runtime.part(part, opts)` — unica API publica para registrar una parte.
Devuelve el handle (`props`, `resolveProps`, `assert`) y, con
`syncAttrs: true`, sincroniza attrs derivados por morfo via `dom.apply`.
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
- `runtime.partProps(part)` — devuelve `{ id, ref, marker, data-archetype? }`. Solo identidad estatica (el `data-archetype` es classification cross-component, nunca cambia).
- `runtime.keydown(part, event)` — dispatch de teclas declaradas en `morfo.keyboard`.
- `runtime.trigger(eventName)` — orquesta la secuencia perceptiva + state.
### Provider en el modelo nuevo
El provider deja de tener helpers locales de resolucion de morfo. Solo
aporta:
- getters reactivos para `states`, `props`, `parts`
- handlers sincronos para los `events`
- glue de layers ortogonales (Presence, Dismissal, ScrollLock)
Cada part-provider conserva el handle devuelto por `runtime.part(...)`:
```ts
readonly runtimePart = runtime.part('trigger', {
id: opts.id,
ref: opts.ref,
owner: this,
syncAttrs: true
});
readonly props = $derived.by(() => this.runtimePart.props);
```
### Tres operaciones que cubren todos los escenarios
```ts
// Cambio estructural sin senal
provider.commitState(change);
// Cambio estructural con senal
provider.commitState(change, event);
// Senal sin cambio estructural
provider.emitEvent(event);
```
Internamente:
```ts
async commitState(change, event?) {
if (event) await this.soma.events?.emit(event);
this.soma.dom.apply(change);
}
emitEvent(event) {
void this.soma.events?.emit(event);
}
```
### La secuencia de `runtime.trigger(eventName)`
```
1. prewrite imperativo (transient markers como data-last-action)
2. await events.emit(event)
3. handler sincrono del provider muta state
4. effects derivan y aplican attrs estructurales (data-state, aria-*)
```
Los effects del runtime escuchan los sources reactivos y reaplican attrs cada
vez que el estado cambia. ADom es el unico escritor de attrs mutables.
### Reglas operativas
- `partProps(part)` solo emite identidad estatica. Lo mutable lo escribe ADom.
- Lo que `dom.apply` escribe, Svelte no lo renderiza desde `partProps`.
- Event handlers son sincronos. Async va antes del trigger.
- Guards (`if (disabled) return`) van en el call-site, no dentro del handler.
- `events`/`VisualChannel` pueden usar `ActiveDom` a traves del projector
inyectado; `ActiveDom` no conoce `events`.
- `morfo.events.commits` es descriptivo, no ejecutable. El smoke valida.
### Pilotaje (orden incremental)
1. **Toggle** — primer caso, solo `partProps`.
2. **Collapsible** — anade `keydown`.
3. **Toast** — primer test real de `trigger()` con `intent`.
4. **Dialog** — al final, cuando layers + portal ya esten validados.
Ver tambien:
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
- [src/uix/morfo/README.md](../morfo/README.md) — declaracion, archetypes, regla 2-de-3
- [src/uix/sema/README.md](../sema/README.md) — contrato de `emit`, vocabulario de verbs
- [src/uix/adom/README.md](../adom/README.md) — `dom.apply`
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
- [src/uix/eidos/README.md](../eidos/README.md) — qué consume eidos del DOM
- [src/uix/README.md](../README.md) §2.bis — vista cross-layer
### Cross-layer hooks que soma emite por la regla 2-de-3
Soma escribe al DOM no solo lo que necesita; escribe también lo que sema y
eidos van a consumir. La regla "2-de-3" justifica qué entra al morfo y por
tanto qué emite el runtime:
- **`data-archetype`** — emitido por `partProps` cuando la parte declara
archetype. Eidos lo usa para selectores transversales
(`[data-archetype=trigger] { ... }`); sema puede asociar verbs por
archetype.
- **`data-event*`** — emitido por `events.emit` (a través del VisualChannel)
durante un hold configurable (240ms emerge/commit/handle, 600ms alert/sustain
por defecto). Eidos lo usa para tintar transiciones de eventos
(`[data-event^=dismiss]`).
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
- **`data-{component}` / `data-{component}-{part}`** — los markers
estructurales clásicos. Eidos los usa para selectores per-componente.
## 4. Modelo de componente
La forma base de soma es `Componente.Parte`:
```ts
import { Dialog } from '$soma/components';
soma: gesture layer, drawer component, full audit fixes across 26 components Gesture Layer: - Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking - VelocityTracker with ring buffer, staleness check, projection - 52 tests (velocity + gesture integration) - setPointerCapture deferred until moveBuffer exceeded (prevents click stealing) Drawer Component: - 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close - Gesture.drag() integration for swipe dismiss - Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle - Modal/non-modal/non-dismissible modes - Logical directions (start/end) RTL-aware via soma presentation - CSS custom properties (--drawer-progress, --drawer-offset-x/y) Architecture: - T1: 219 files migrated from $soma/ alias to relative paths - T2: 35 wrappers — noop replaced with () => {} inline - Translation system: componentLangs + common.buttons.* + idlangref constants - COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns - contracts.ts: case-normalized registerContract/assertContract Audit Fixes (P0 critical): - DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super - TB1: data-table attr on wrapper - CH1/SW1/NF4: hidden input rendering fixes - SP2: panel cleanup on unmount - RG1: roving tabindex fallback to first item - CO1: aria-labelledby on collapsible content Audit Fixes (P1 high): - SafePolygon integrated in dropdown-menu SubProvider + tooltip - 9 langs.ts files with idlangref constants - Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards - Stepper: count decrement + private, aria-label on List - ToggleGroup: roving tabindex in multiple mode - Select: pure aria-activedescendant (no mixed focus), label registry - NumberField: startValue respects min, scrubber commit on pointerup only - Toolbar: element-based focus tracking (not index) Audit Fixes (P2 medium): - A6: timer/listener cleanup in 6 components - A12: getDirectionalKeys in 4 components - A10: DOM queries scoped to root in 3 components - Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern - Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup - Pagination: reactive page clamping, Set-based computePageRange - Scroll-area: thumbSize zero-guard, mount flash prevention - Splitter: collapse/expand respects adjacent constraints, immediate pointer capture 5 issues remain (design decisions): stepper registry, table sub-row IDs, tags-input ARIA model, tags-input paste, toast exit animation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
Dialog.Provider; // root — crea contexto
Dialog.Trigger; // accion — abre/cierra
Dialog.Content; // contenido — layers integrados
Dialog.Overlay; // fondo — presence
Dialog.Title; // metadata ARIA
Dialog.Description; // metadata ARIA
Dialog.Close; // accion — cierra
```
### Provider (root)
Crea el estado central, lo registra en context, gestiona presence para content y overlay. Puede o no renderizar DOM:
- **Con DOM** (Collapsible, Accordion): usa `WithRefOpts`, renderiza `<div>`
- **Sin DOM** (Dialog, Popover): usa `ProviderOpts`, solo renderiza children
### Subcomponentes
soma: gesture layer, drawer component, full audit fixes across 26 components Gesture Layer: - Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking - VelocityTracker with ring buffer, staleness check, projection - 52 tests (velocity + gesture integration) - setPointerCapture deferred until moveBuffer exceeded (prevents click stealing) Drawer Component: - 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close - Gesture.drag() integration for swipe dismiss - Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle - Modal/non-modal/non-dismissible modes - Logical directions (start/end) RTL-aware via soma presentation - CSS custom properties (--drawer-progress, --drawer-offset-x/y) Architecture: - T1: 219 files migrated from $soma/ alias to relative paths - T2: 35 wrappers — noop replaced with () => {} inline - Translation system: componentLangs + common.buttons.* + idlangref constants - COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns - contracts.ts: case-normalized registerContract/assertContract Audit Fixes (P0 critical): - DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super - TB1: data-table attr on wrapper - CH1/SW1/NF4: hidden input rendering fixes - SP2: panel cleanup on unmount - RG1: roving tabindex fallback to first item - CO1: aria-labelledby on collapsible content Audit Fixes (P1 high): - SafePolygon integrated in dropdown-menu SubProvider + tooltip - 9 langs.ts files with idlangref constants - Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards - Stepper: count decrement + private, aria-label on List - ToggleGroup: roving tabindex in multiple mode - Select: pure aria-activedescendant (no mixed focus), label registry - NumberField: startValue respects min, scrubber commit on pointerup only - Toolbar: element-based focus tracking (not index) Audit Fixes (P2 medium): - A6: timer/listener cleanup in 6 components - A12: getDirectionalKeys in 4 components - A10: DOM queries scoped to root in 3 components - Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern - Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup - Pagination: reactive page clamping, Set-based computePageRange - Scroll-area: thumbSize zero-guard, mount flash prevention - Splitter: collapse/expand respects adjacent constraints, immediate pointer capture 5 issues remain (design decisions): stepper registry, table sub-row IDs, tags-input ARIA model, tags-input paste, toast exit animation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
Leen estado del root via `.require()`. No reimplementan logica — derivan props, ARIA, data-\*, events del estado del padre.
### Portal
Componente interno (`components/internal/portal.svelte`). Renderiza hijos en otro nodo DOM. El contexto de Svelte se preserva.
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs Tier 3 (complete): DateField, DatePicker, DateRangeField, DateRangePicker Tier 4 (partial): TimeField, TimePicker (TimeRangeField pending) Core: $lib/util/dias/segments.ts — pure domain primitives for segmented date/time inputs (constants, guards, initializers, reconciliation, content generation, Intl opts) soma/external/dates re-exports everything (no façades inside soma) soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav, SegmentState, KEYS predicates, description writers) Conventions established (A23–A29): A23 — never re-implement date helpers; extend dias A24 — readonlySegments without value anchor logs warning A25 — range components split readonly per-endpoint A26 — contenteditable segments use onbeforeinput to block IME/paste A27 — picker composition pattern with shared writableActive refs A28 — time placeholders render as hh/mm/ss via dias/segments A29 — demo pages are interactive testbeds, not static galleries Demo pages: Material Design 3 styled, every public prop wired to a live control, Field integration + state readout. Day-period uses real radio group in 12h mode. Auto-anchor on range pickers keeps the end month visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly. Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29, checklist items 28–31, picker composition section, dias boundary, directory structure note for soma/datetime/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
### Picker composition (shared state across providers)
Pickers (`DatePicker`, `DateRangePicker`, `TimePicker`, y futuros `TimeRangePicker`) no reimplementan Popover / Field / Calendar — los **componen** con estado compartido. El root wrapper crea tres (o más) Providers que apuntan a los mismos `writableActive` refs:
```ts
// Root wrapper
const sharedValue = writableActive(() => value, (v) => (value = v));
const sharedPlaceholder = writableActive(() => placeholder, (v) => (placeholder = v));
const sharedOpen = writableActive(() => open, (v) => (open = v));
{Name}PickerProvider.create({ value: sharedValue, placeholder: sharedPlaceholder, open: sharedOpen, ...config });
PopoverProvider.create({ open: sharedOpen, ... });
{Base}FieldProvider.create({ value: sharedValue, placeholder: sharedPlaceholder, ...config });
// Calendar/RangeCalendar/slider providers are created in their own wrapper (DatePicker.Calendar, TimePicker.HourSlider, …)
```
El picker expone **solo** wrappers únicos para `Provider`, `Trigger`, y el puente al calendario/slider. Los demás exports re-exportan desde los componentes compuestos — sus `data-*` nativos (`data-popover-*`, `data-date-field-*`, `data-calendar-*`, `data-slider-*`) siguen siendo la API de estilo autoritativa. El picker sólo añade atributos de identidad (`data-{picker}-trigger`, `data-{picker}-calendar`) en sus propios wrappers.
**Auto-close / auto-anchor**: el PickerProvider expone `handleSelect()` que el wrapper del calendario llama al completarse una selección. Los range pickers re-anclan `placeholder` para que el mes final caiga en la columna más a la derecha visible, evitando mostrar al usuario un mes que no contiene su selección.
Ver A27 en `COMPONENT_GUIDE.md` para el checklist completo.
## 5. Runtime parts
```ts
readonly soma = Soma.require();
readonly runtime = this.soma.runtime(accordionMorfo, {});
const runtimePart = this.runtime.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: AccordionProvider.ctx
});
readonly props = $derived.by(() =>
runtimePart.assert({
...runtimePart.props,
'data-state': this.state
})
);
```
`SomaRuntime.part()` centraliza lo mecanico de cada parte:
```ts
interface SomaRuntimePart {
readonly attachment: RefAttachment | undefined;
readonly props: Record<string, unknown>;
resolveProps(bindings?): Record<string, unknown>;
assert<P extends Record<string, unknown>>(props: P): P;
}
```
`syncAttrs: true` activa la escritura imperativa por `uix.dom` para partes
cuyos sources ya estan declarados en el runtime. Si un provider sigue
componiendo attrs en render props, no activa `syncAttrs`.
Dos interfaces de opts:
- `ProviderOpts` — `{ id: Active<string>; ref?: State<HTMLElement | null> }` — para roots sin DOM
- `WithRefOpts` — `{ id: Active<string>; ref: State<HTMLElement | null> }` — para parts con DOM
## 6. Layers
`layers/` contiene solo clases de comportamiento (`.svelte.ts`). Son infraestructura consumida por Providers, nunca directamente por el consumidor.
### Inventario
soma: gesture layer, drawer component, full audit fixes across 26 components Gesture Layer: - Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking - VelocityTracker with ring buffer, staleness check, projection - 52 tests (velocity + gesture integration) - setPointerCapture deferred until moveBuffer exceeded (prevents click stealing) Drawer Component: - 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close - Gesture.drag() integration for swipe dismiss - Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle - Modal/non-modal/non-dismissible modes - Logical directions (start/end) RTL-aware via soma presentation - CSS custom properties (--drawer-progress, --drawer-offset-x/y) Architecture: - T1: 219 files migrated from $soma/ alias to relative paths - T2: 35 wrappers — noop replaced with () => {} inline - Translation system: componentLangs + common.buttons.* + idlangref constants - COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns - contracts.ts: case-normalized registerContract/assertContract Audit Fixes (P0 critical): - DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super - TB1: data-table attr on wrapper - CH1/SW1/NF4: hidden input rendering fixes - SP2: panel cleanup on unmount - RG1: roving tabindex fallback to first item - CO1: aria-labelledby on collapsible content Audit Fixes (P1 high): - SafePolygon integrated in dropdown-menu SubProvider + tooltip - 9 langs.ts files with idlangref constants - Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards - Stepper: count decrement + private, aria-label on List - ToggleGroup: roving tabindex in multiple mode - Select: pure aria-activedescendant (no mixed focus), label registry - NumberField: startValue respects min, scrubber commit on pointerup only - Toolbar: element-based focus tracking (not index) Audit Fixes (P2 medium): - A6: timer/listener cleanup in 6 components - A12: getDirectionalKeys in 4 components - A10: DOM queries scoped to root in 3 components - Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern - Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup - Pagination: reactive page clamping, Set-based computePageRange - Scroll-area: thumbSize zero-guard, mount flash prevention - Splitter: collapse/expand respects adjacent constraints, immediate pointer capture 5 issues remain (design decisions): stepper registry, table sub-row IDs, tags-input ARIA model, tags-input paste, toast exit animation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
| Layer | API | Responsabilidad |
| ----------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `Presence` | `new Presence(opts)` | Mount/unmount con animaciones. `isPresent`, `transitionAttrs`, `onComplete`. |
| `FocusScope` | `FocusScope.use(opts)` | Focus trap, loop, auto-focus, restore. Singleton manager con stack. |
| `Dismissal` | `Dismissal.use(opts)` | Escape + click-outside. Registry global. Behaviors: close, ignore, defer. |
| `TextSelection` | `TextSelection.use(opts)` | Previene selection overflow durante drag. |
| `ScrollLock` | `new ScrollLock(initial?, delay?)` | Body scroll lock con refcount. Soporta delay para animaciones. |
| `ResizeObserver$` | `new ResizeObserver$(getter, cb)` | ResizeObserver con lifecycle Svelte. |
| `Floating*` | `FloatingProvider.create()`, `FloatingContent.create(opts)`, etc. | Posicionamiento relativo a anchor via @floating-ui. |
| `Gesture.base` | `Gesture.base(opts)` | Pointer tracking + axis lock + velocity. |
| `Gesture.drag` | `Gesture.drag(opts)` | Base + progress + snap points + dismiss. |
| `Gesture.resize` | `Gesture.resize(opts)` | Base + delta + min/max constraints. |
| `SafePolygon` | `new SafePolygon(opts)` | Hover-gap corridor between trigger↔content. |
`layers/floating/placement.ts` es la fuente unica para `Side`, `Align`,
`Boundary`, `SIDE_OPTIONS` y `ALIGN_OPTIONS`. `floating/types.ts` consume esa
fuente y no importa del runtime `floating.svelte.ts`, evitando ciclos entre
tipos y clases.
### Cobertura P0 actual
Los tests por componente estan creciendo desde las piezas de mayor riesgo. A
2026-05-15 existen tests directos para:
- `$libs/datagrid/table-core.svelte.test.ts` — motor de data grid.
- `$libs/forms/form-core.svelte.test.ts` — motor de formularios + Standard Schema.
- `dialog/dialog-provider.svelte.test.ts` — open/close, fallback target y
eventos morfo.
- `drawer/drawer-provider.svelte.test.ts` — direccion logica, dismiss y modo
persistent.
- `command/command-provider.svelte.test.ts` — filtro, visibilidad, navegación
y selección.
- `combobox/combobox-provider.svelte.test.ts` — selección, `inputValue` y
navegación sin items disabled.
- `select/select-provider.svelte.test.ts` — selección single/multiple y
navegación sin items disabled.
- `popover/popover-provider.svelte.test.ts` — toggle, hover timers y señales
runtime.
- `toast/toaster.svelte.test.ts` — overflow, dismiss/remove y promesas.
- `calendar/calendar-provider.svelte.test.ts` — placeholder, meses, selección
y flags.
- `range-calendar/range-calendar-provider.svelte.test.ts` — placeholder,
orden de endpoints y limites min/max.
- `date-field/date-field-provider.svelte.test.ts` — helpers UI de segmentos,
lectura DOM, navegación por `ActiveDom`, validación y commit segmentado.
- `date-picker/date-picker-provider.svelte.test.ts` — registro runtime y
cierre controlado por `closeOnDateSelect`.
- `date-range-picker/date-range-picker-provider.svelte.test.ts` — registro
runtime y cierre controlado por `closeOnRangeSelect`.
- `time-picker/time-picker-provider.svelte.test.ts` — registro runtime,
valores de placeholder y escritura de sliders al valor compartido.
- `time-range-picker/time-range-picker-provider.svelte.test.ts` — valores por
endpoint, escritura de sliders y cierre al completar rango.
- `time-range-field/time-range-field-provider.svelte.test.ts` — validación de
orden/min/max, validación custom y foco de label via `ActiveDom`.
- `time-field/time-field-provider.svelte.test.ts` — validación, sincronización
de segmentos 12h y commit solo cuando los segmentos renderizados estan
completos.
- `date-range-field/date-range-field-provider.svelte.test.ts` — validación de
orden/min/max, validación custom y foco de label via `ActiveDom`.
- `virtual-list/virtual-list-provider.svelte.test.ts` — cálculo de ventana,
`scrollToIndex` y compensación anti-jump via `ActiveDom`.
- `virtual-grid/virtual-grid-provider.svelte.test.ts` — cálculo de ventana 2D
y `scrollToCell` via `ActiveDom`.
- `number-field/number-field-provider.svelte.test.ts` — parsing localizable,
teclado spinbutton, triggers, props ARIA y scrubber.
- `file-upload/file-upload-provider.svelte.test.ts` — aceptación/rechazo,
dropzone, input oculto, items, progress y acciones remove/clear.
- `color-field/color-field-provider.svelte.test.ts` — commit por segmentos,
edición hex por teclado, selector de formato, input oculto y foco via
`ActiveDom`.
- `color-picker/color-picker-provider.svelte.test.ts` — helpers de canales,
trigger/value/hidden, area 2D, slider de canal y swatches.
- `navigation-menu/navigation-menu-provider.svelte.test.ts` — timers UIX,
enlace item/trigger/content, foco por teclado y props de list/link.
- `tree-grid/tree-grid-provider.svelte.test.ts` — expansión, selección/rango,
filas visibles, navegación y props de row/cell/header/expand trigger.
- `dropdown-menu/dropdown-menu-provider.svelte.test.ts` — trigger open/close,
scoping de items, selección, checkbox/radio groups, submenu y group/separator.
- `context-menu/context-menu-provider.svelte.test.ts` — apertura por
`contextmenu`, anchor virtual, scoping de items, selección, checkbox/radio,
submenu y group/separator.
- `menubar/menubar-provider.svelte.test.ts` — coordinación de menús hermanos,
hover-follow, navegación horizontal y cambio de menú desde content.
- `listbox/listbox-provider.svelte.test.ts` — props ARIA root, selección,
navegación, typeahead, indicador de item y grupos.
- `tree-view/tree-view-provider.svelte.test.ts` — expansión/selección,
navegación root, typeahead, props de ramas/hojas y partes auxiliares.
- `accordion/accordion-provider.svelte.test.ts` — modos single/multiple,
eventos `open/close` por item, navegación de triggers y header/content.
- `checkbox/checkbox-provider.svelte.test.ts` — commits check/uncheck,
indeterminate, hidden input y sincronización con `CheckboxGroup`.
- `radio-group/radio-group-provider.svelte.test.ts` — roving tabindex,
selección síncrona, navegación con auto-select, hidden input y guardas.
- `switch/switch-provider.svelte.test.ts` — `commit-toggle` runtime,
keyboard, hidden input, integración con `FieldProvider` y thumb.
- `toggle/toggle-provider.svelte.test.ts` — `commit-toggle` runtime, attrs DOM
via `ActiveDom`, integración con `FieldProvider` y guardas.
- `toggle-group/toggle-group-provider.svelte.test.ts` — modos single/multiple,
roving tabindex y navegación de items saltando disabled.
- `tabs/tabs-provider.svelte.test.ts` — registros trigger/content, activacion
automatic/manual, navegacion roving, fallback tab stop y partes auxiliares.
- `slider/slider-provider.svelte.test.ts` — contrato provider/range/thumb/tick,
snapping/clamping, drag pointer confirmado y teclado de thumb.
- `progress/progress-provider.svelte.test.ts` y
`meter/meter-provider.svelte.test.ts` — `data-value/data-min/data-max`
emitidos por morfo/runtime, estados derivados e indicador sincronizado.
- `collapsible/collapsible-provider.svelte.test.ts` — refs cruzadas
trigger/content, eventos `expand/collapse` y guardas disabled.
Pendiente: seguir ampliando cobertura por el resto del catalogo Soma, ya por
componentes de riesgo medio y familias menos centrales. `table-core`,
`form-core` y el scorer de Command ya no viven dentro de Soma: se consumen
desde `$libs/datagrid`, `$libs/forms` y `$libs/strings`.
### Convencion
- `.use(opts)` → lifecycle auto-gestionado (watch/$effect internos). Constructor privado.
- `new X(opts)` → lifecycle manual. El consumidor controla.
- `.props` → objeto para spread en el Provider.
### Animaciones (Presence)
Lifecycle:
```
OPENING:
open=true → shouldRender=true + data-starting-style
→ next rAF: data-starting-style removed (triggers CSS transition)
→ getAnimations().finished → onComplete(true)
CLOSING:
open=false → data-ending-style (element stays in DOM!)
→ getAnimations().finished
→ shouldRender=false + data-ending-style removed → onComplete(false)
```
- `forceMount` mantiene el elemento en DOM siempre (para transiciones CSS)
- `onComplete` usa `getAnimations()` API, no eventos `transitionend`/`animationend`
- Run ID cancellation previene callbacks stale en toggle rapido
soma: gesture layer, drawer component, full audit fixes across 26 components Gesture Layer: - Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking - VelocityTracker with ring buffer, staleness check, projection - 52 tests (velocity + gesture integration) - setPointerCapture deferred until moveBuffer exceeded (prevents click stealing) Drawer Component: - 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close - Gesture.drag() integration for swipe dismiss - Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle - Modal/non-modal/non-dismissible modes - Logical directions (start/end) RTL-aware via soma presentation - CSS custom properties (--drawer-progress, --drawer-offset-x/y) Architecture: - T1: 219 files migrated from $soma/ alias to relative paths - T2: 35 wrappers — noop replaced with () => {} inline - Translation system: componentLangs + common.buttons.* + idlangref constants - COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns - contracts.ts: case-normalized registerContract/assertContract Audit Fixes (P0 critical): - DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super - TB1: data-table attr on wrapper - CH1/SW1/NF4: hidden input rendering fixes - SP2: panel cleanup on unmount - RG1: roving tabindex fallback to first item - CO1: aria-labelledby on collapsible content Audit Fixes (P1 high): - SafePolygon integrated in dropdown-menu SubProvider + tooltip - 9 langs.ts files with idlangref constants - Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards - Stepper: count decrement + private, aria-label on List - ToggleGroup: roving tabindex in multiple mode - Select: pure aria-activedescendant (no mixed focus), label registry - NumberField: startValue respects min, scrubber commit on pointerup only - Toolbar: element-based focus tracking (not index) Audit Fixes (P2 medium): - A6: timer/listener cleanup in 6 components - A12: getDirectionalKeys in 4 components - A10: DOM queries scoped to root in 3 components - Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern - Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup - Pagination: reactive page clamping, Set-based computePageRange - Scroll-area: thumbSize zero-guard, mount flash prevention - Splitter: collapse/expand respects adjacent constraints, immediate pointer capture 5 issues remain (design decisions): stepper registry, table sub-row IDs, tags-input ARIA model, tags-input paste, toast exit animation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
## 7. Soma class (component runtime scope)
Soma reads `ActiveUix` from context and exposes services to components.
Components import from `$soma`, never from `$active-app`. Nestable: child
`<Soma portalTo="#modals">` overrides parent.
```ts
class Soma {
soma: gesture layer, drawer component, full audit fixes across 26 components Gesture Layer: - Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking - VelocityTracker with ring buffer, staleness check, projection - 52 tests (velocity + gesture integration) - setPointerCapture deferred until moveBuffer exceeded (prevents click stealing) Drawer Component: - 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close - Gesture.drag() integration for swipe dismiss - Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle - Modal/non-modal/non-dismissible modes - Logical directions (start/end) RTL-aware via soma presentation - CSS custom properties (--drawer-progress, --drawer-offset-x/y) Architecture: - T1: 219 files migrated from $soma/ alias to relative paths - T2: 35 wrappers — noop replaced with () => {} inline - Translation system: componentLangs + common.buttons.* + idlangref constants - COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns - contracts.ts: case-normalized registerContract/assertContract Audit Fixes (P0 critical): - DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super - TB1: data-table attr on wrapper - CH1/SW1/NF4: hidden input rendering fixes - SP2: panel cleanup on unmount - RG1: roving tabindex fallback to first item - CO1: aria-labelledby on collapsible content Audit Fixes (P1 high): - SafePolygon integrated in dropdown-menu SubProvider + tooltip - 9 langs.ts files with idlangref constants - Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards - Stepper: count decrement + private, aria-label on List - ToggleGroup: roving tabindex in multiple mode - Select: pure aria-activedescendant (no mixed focus), label registry - NumberField: startValue respects min, scrubber commit on pointerup only - Toolbar: element-based focus tracking (not index) Audit Fixes (P2 medium): - A6: timer/listener cleanup in 6 components - A12: getDirectionalKeys in 4 components - A10: DOM queries scoped to root in 3 components - Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern - Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup - Pagination: reactive page clamping, Set-based computePageRange - Scroll-area: thumbSize zero-guard, mount flash prevention - Splitter: collapse/expand respects adjacent constraints, immediate pointer capture 5 issues remain (design decisions): stepper registry, table sub-row IDs, tags-input ARIA model, tags-input paste, toast exit animation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
static create(opts?: SomaOptions): Soma; // factory + context set
static get(): Soma | undefined; // safe read
static require(): Soma; // throws if not found
readonly uix: ActiveUix;
soma: gesture layer, drawer component, full audit fixes across 26 components Gesture Layer: - Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking - VelocityTracker with ring buffer, staleness check, projection - 52 tests (velocity + gesture integration) - setPointerCapture deferred until moveBuffer exceeded (prevents click stealing) Drawer Component: - 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close - Gesture.drag() integration for swipe dismiss - Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle - Modal/non-modal/non-dismissible modes - Logical directions (start/end) RTL-aware via soma presentation - CSS custom properties (--drawer-progress, --drawer-offset-x/y) Architecture: - T1: 219 files migrated from $soma/ alias to relative paths - T2: 35 wrappers — noop replaced with () => {} inline - Translation system: componentLangs + common.buttons.* + idlangref constants - COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns - contracts.ts: case-normalized registerContract/assertContract Audit Fixes (P0 critical): - DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super - TB1: data-table attr on wrapper - CH1/SW1/NF4: hidden input rendering fixes - SP2: panel cleanup on unmount - RG1: roving tabindex fallback to first item - CO1: aria-labelledby on collapsible content Audit Fixes (P1 high): - SafePolygon integrated in dropdown-menu SubProvider + tooltip - 9 langs.ts files with idlangref constants - Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards - Stepper: count decrement + private, aria-label on List - ToggleGroup: roving tabindex in multiple mode - Select: pure aria-activedescendant (no mixed focus), label registry - NumberField: startValue respects min, scrubber commit on pointerup only - Toolbar: element-based focus tracking (not index) Audit Fixes (P2 medium): - A6: timer/listener cleanup in 6 components - A12: getDirectionalKeys in 4 components - A10: DOM queries scoped to root in 3 components - Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern - Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup - Pagination: reactive page clamping, Set-based computePageRange - Scroll-area: thumbSize zero-guard, mount flash prevention - Splitter: collapse/expand respects adjacent constraints, immediate pointer capture 5 issues remain (design decisions): stepper registry, table sub-row IDs, tags-input ARIA model, tags-input paste, toast exit animation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
readonly portalTo: string | HTMLElement | undefined;
// Service accessors (delegate to ActiveUix)
get langs(): ActiveLangs;
get nums(): ActiveNumbers | undefined;
get money(): ActiveCurrency | undefined;
get dates(): ActiveDates | undefined;
get units(): ActiveUnits | undefined;
get prefs(): ActiveUixPrefsView;
get logger(): EngineLogger;
}
```
soma: gesture layer, drawer component, full audit fixes across 26 components Gesture Layer: - Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking - VelocityTracker with ring buffer, staleness check, projection - 52 tests (velocity + gesture integration) - setPointerCapture deferred until moveBuffer exceeded (prevents click stealing) Drawer Component: - 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close - Gesture.drag() integration for swipe dismiss - Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle - Modal/non-modal/non-dismissible modes - Logical directions (start/end) RTL-aware via soma presentation - CSS custom properties (--drawer-progress, --drawer-offset-x/y) Architecture: - T1: 219 files migrated from $soma/ alias to relative paths - T2: 35 wrappers — noop replaced with () => {} inline - Translation system: componentLangs + common.buttons.* + idlangref constants - COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns - contracts.ts: case-normalized registerContract/assertContract Audit Fixes (P0 critical): - DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super - TB1: data-table attr on wrapper - CH1/SW1/NF4: hidden input rendering fixes - SP2: panel cleanup on unmount - RG1: roving tabindex fallback to first item - CO1: aria-labelledby on collapsible content Audit Fixes (P1 high): - SafePolygon integrated in dropdown-menu SubProvider + tooltip - 9 langs.ts files with idlangref constants - Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards - Stepper: count decrement + private, aria-label on List - ToggleGroup: roving tabindex in multiple mode - Select: pure aria-activedescendant (no mixed focus), label registry - NumberField: startValue respects min, scrubber commit on pointerup only - Toolbar: element-based focus tracking (not index) Audit Fixes (P2 medium): - A6: timer/listener cleanup in 6 components - A12: getDirectionalKeys in 4 components - A10: DOM queries scoped to root in 3 components - Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern - Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup - Pagination: reactive page clamping, Set-based computePageRange - Scroll-area: thumbSize zero-guard, mount flash prevention - Splitter: collapse/expand respects adjacent constraints, immediate pointer capture 5 issues remain (design decisions): stepper registry, table sub-row IDs, tags-input ARIA model, tags-input paste, toast exit animation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
### Service access from components
soma: gesture layer, drawer component, full audit fixes across 26 components Gesture Layer: - Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking - VelocityTracker with ring buffer, staleness check, projection - 52 tests (velocity + gesture integration) - setPointerCapture deferred until moveBuffer exceeded (prevents click stealing) Drawer Component: - 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close - Gesture.drag() integration for swipe dismiss - Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle - Modal/non-modal/non-dismissible modes - Logical directions (start/end) RTL-aware via soma presentation - CSS custom properties (--drawer-progress, --drawer-offset-x/y) Architecture: - T1: 219 files migrated from $soma/ alias to relative paths - T2: 35 wrappers — noop replaced with () => {} inline - Translation system: componentLangs + common.buttons.* + idlangref constants - COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns - contracts.ts: case-normalized registerContract/assertContract Audit Fixes (P0 critical): - DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super - TB1: data-table attr on wrapper - CH1/SW1/NF4: hidden input rendering fixes - SP2: panel cleanup on unmount - RG1: roving tabindex fallback to first item - CO1: aria-labelledby on collapsible content Audit Fixes (P1 high): - SafePolygon integrated in dropdown-menu SubProvider + tooltip - 9 langs.ts files with idlangref constants - Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards - Stepper: count decrement + private, aria-label on List - ToggleGroup: roving tabindex in multiple mode - Select: pure aria-activedescendant (no mixed focus), label registry - NumberField: startValue respects min, scrubber commit on pointerup only - Toolbar: element-based focus tracking (not index) Audit Fixes (P2 medium): - A6: timer/listener cleanup in 6 components - A12: getDirectionalKeys in 4 components - A10: DOM queries scoped to root in 3 components - Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern - Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup - Pagination: reactive page clamping, Set-based computePageRange - Scroll-area: thumbSize zero-guard, mount flash prevention - Splitter: collapse/expand respects adjacent constraints, immediate pointer capture 5 issues remain (design decisions): stepper registry, table sub-row IDs, tags-input ARIA model, tags-input paste, toast exit animation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
Components access services through Soma, never through App directly:
```ts
soma: gesture layer, drawer component, full audit fixes across 26 components Gesture Layer: - Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking - VelocityTracker with ring buffer, staleness check, projection - 52 tests (velocity + gesture integration) - setPointerCapture deferred until moveBuffer exceeded (prevents click stealing) Drawer Component: - 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close - Gesture.drag() integration for swipe dismiss - Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle - Modal/non-modal/non-dismissible modes - Logical directions (start/end) RTL-aware via soma presentation - CSS custom properties (--drawer-progress, --drawer-offset-x/y) Architecture: - T1: 219 files migrated from $soma/ alias to relative paths - T2: 35 wrappers — noop replaced with () => {} inline - Translation system: componentLangs + common.buttons.* + idlangref constants - COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns - contracts.ts: case-normalized registerContract/assertContract Audit Fixes (P0 critical): - DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super - TB1: data-table attr on wrapper - CH1/SW1/NF4: hidden input rendering fixes - SP2: panel cleanup on unmount - RG1: roving tabindex fallback to first item - CO1: aria-labelledby on collapsible content Audit Fixes (P1 high): - SafePolygon integrated in dropdown-menu SubProvider + tooltip - 9 langs.ts files with idlangref constants - Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards - Stepper: count decrement + private, aria-label on List - ToggleGroup: roving tabindex in multiple mode - Select: pure aria-activedescendant (no mixed focus), label registry - NumberField: startValue respects min, scrubber commit on pointerup only - Toolbar: element-based focus tracking (not index) Audit Fixes (P2 medium): - A6: timer/listener cleanup in 6 components - A12: getDirectionalKeys in 4 components - A10: DOM queries scoped to root in 3 components - Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern - Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup - Pagination: reactive page clamping, Set-based computePageRange - Scroll-area: thumbSize zero-guard, mount flash prevention - Splitter: collapse/expand respects adjacent constraints, immediate pointer capture 5 issues remain (design decisions): stepper registry, table sub-row IDs, tags-input ARIA model, tags-input paste, toast exit animation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
const soma = Soma.get();
soma?.langs.ts('#?common.buttons.close|Close'); // translation via idlangref
soma?.prefs.getDir(); // effective direction from uix.prefs.direction
soma?.money?.format(1099); // currency formatting
soma: dias date library, Calendar + RangeCalendar, Command fixes, doc updates **Date library reorganisation** (no new third-party dependency) - New `src/lib/util/dias/` with `_vendor/` (Adobe Apache 2.0 vendored code, ATTRIBUTION.md preserved) and a public facade split into `types`, `values`, `calendars`, `queries`, `operations`, `parse`, `format`, `index`. Consumers import only from `$lib/util/dias`. - All date-related `Intl` consolidated in `dias/format.ts` — `DateFormatter`, cache, `resolveDateOrder`, `resolveHourCycle`, placeholders, defaults. - `ext/dates` now a thin reactive wrapper delegating to `dias`. - Aligned App-layer `HourCycle` to canonical numeric form `12 | 24` (matches `Intl.DateTimeFormat`'s `hour12`). Removed string form `'12h' | '24h'` from `ext/app/types`, `ext/dates`, `ext/presentation/locale-defaults`, and their tests. - Repointed every consumer: `soma/external/dates`, `terra/external/dates`, eight `routes/test/*` pages. - Legacy `src/lib/util/dates/` left untouched by request. **Tier 3 components** - `soma/components/calendar/` — 14 parts, single + multiple selection, `MonthSelect`/`YearSelect`, ISO/simple week numbers, `isDateHoliday` matcher, `data-weekend` flag, navigation live- region, RTL flip, full ARIA, 27-item checklist verified. - `soma/components/range-calendar/` — 14 parts, range selection with live hover preview (novel vs ark/bits/radix/react-aria), `minDays` / `maxDays` / `allowSingleDay`, third-click deselects only the clicked endpoint. - Test pages at `/test/soma/calendar` and `/test/soma/range-calendar` with seven demos each. Links added to the soma index under a new "Dates" section. - READMEs with anatomy, parts, props, ARIA, keyboard, data-attrs, and a competitive comparison table. **Command fixes** - Resolved O(N^2) reactive cascade: introduced `visibleSet: $derived<Set<string>>` on the root provider; per-item `isVisible` is now an O(1) `has()` lookup instead of calling back into the provider, which was re-running all items' derivations on every `itemsVersion` bump. - Added `dir` prop with fallback to `soma.presentation.getDir()`; horizontal arrows flip in RTL via `getDirectionalKeys`; root receives `dir` attribute. **Doc updates (session outcomes)** - `CLAUDE.md`: replaced obsolete `@/glob/lib` date references with `$lib/util/dias`; added canonical date-layer section; reinforced `Provider` export rule and `data-{component}[-{part}]` naming (never `data-soma-*`); clarified translation convention. - `src/uix/soma/COMPONENT_GUIDE.md`: new checklist items 26 (component README required) and 27 (grep for data-attr consistency); new Common Mistakes 11 (`data-soma-*` prefix), 12 (wrong date module), 13 (string `HourCycle`). - `src/uix/soma/SOMA_ARCHITECTURE.md`: clarified data-attr naming (canonical emitter is `createAttrs`; validator checks enum values only, not names); new section covering date types and formatting through `$soma/external/dates` → `$lib/util/dias`; documented numeric `HourCycle`. **Infrastructure** - `PrimitiveHeaderAttributes` and `PrimitiveSelectAttributes` added to `$soma/types`. - `getLastFirstDayOfWeek`, `getNextLastDayOfWeek` re-exported from `$soma/external/dates`. - `calendar.*` and `range-calendar.*` translation blocks added to `$soma/core/langs.ts`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
soma?.dates?.getDateOrder(); // DMY / MDY / YMD
soma?.dates?.getHourCycle(); // 12 | 24 (numeric — not '12h' / '24h')
soma?.portalTo; // portal target
```
soma: dias date library, Calendar + RangeCalendar, Command fixes, doc updates **Date library reorganisation** (no new third-party dependency) - New `src/lib/util/dias/` with `_vendor/` (Adobe Apache 2.0 vendored code, ATTRIBUTION.md preserved) and a public facade split into `types`, `values`, `calendars`, `queries`, `operations`, `parse`, `format`, `index`. Consumers import only from `$lib/util/dias`. - All date-related `Intl` consolidated in `dias/format.ts` — `DateFormatter`, cache, `resolveDateOrder`, `resolveHourCycle`, placeholders, defaults. - `ext/dates` now a thin reactive wrapper delegating to `dias`. - Aligned App-layer `HourCycle` to canonical numeric form `12 | 24` (matches `Intl.DateTimeFormat`'s `hour12`). Removed string form `'12h' | '24h'` from `ext/app/types`, `ext/dates`, `ext/presentation/locale-defaults`, and their tests. - Repointed every consumer: `soma/external/dates`, `terra/external/dates`, eight `routes/test/*` pages. - Legacy `src/lib/util/dates/` left untouched by request. **Tier 3 components** - `soma/components/calendar/` — 14 parts, single + multiple selection, `MonthSelect`/`YearSelect`, ISO/simple week numbers, `isDateHoliday` matcher, `data-weekend` flag, navigation live- region, RTL flip, full ARIA, 27-item checklist verified. - `soma/components/range-calendar/` — 14 parts, range selection with live hover preview (novel vs ark/bits/radix/react-aria), `minDays` / `maxDays` / `allowSingleDay`, third-click deselects only the clicked endpoint. - Test pages at `/test/soma/calendar` and `/test/soma/range-calendar` with seven demos each. Links added to the soma index under a new "Dates" section. - READMEs with anatomy, parts, props, ARIA, keyboard, data-attrs, and a competitive comparison table. **Command fixes** - Resolved O(N^2) reactive cascade: introduced `visibleSet: $derived<Set<string>>` on the root provider; per-item `isVisible` is now an O(1) `has()` lookup instead of calling back into the provider, which was re-running all items' derivations on every `itemsVersion` bump. - Added `dir` prop with fallback to `soma.presentation.getDir()`; horizontal arrows flip in RTL via `getDirectionalKeys`; root receives `dir` attribute. **Doc updates (session outcomes)** - `CLAUDE.md`: replaced obsolete `@/glob/lib` date references with `$lib/util/dias`; added canonical date-layer section; reinforced `Provider` export rule and `data-{component}[-{part}]` naming (never `data-soma-*`); clarified translation convention. - `src/uix/soma/COMPONENT_GUIDE.md`: new checklist items 26 (component README required) and 27 (grep for data-attr consistency); new Common Mistakes 11 (`data-soma-*` prefix), 12 (wrong date module), 13 (string `HourCycle`). - `src/uix/soma/SOMA_ARCHITECTURE.md`: clarified data-attr naming (canonical emitter is `createAttrs`; validator checks enum values only, not names); new section covering date types and formatting through `$soma/external/dates` → `$lib/util/dias`; documented numeric `HourCycle`. **Infrastructure** - `PrimitiveHeaderAttributes` and `PrimitiveSelectAttributes` added to `$soma/types`. - `getLastFirstDayOfWeek`, `getNextLastDayOfWeek` re-exported from `$soma/external/dates`. - `calendar.*` and `range-calendar.*` translation blocks added to `$soma/core/langs.ts`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
### Date / time types and formatting
Soma imports date-related symbols from `$libs/days`, the canonical date library. Components **never** import from `$lib/util/dates` (legacy) or `@internationalized/date` directly. There is no Soma re-export façade for the date domain.
soma: dias date library, Calendar + RangeCalendar, Command fixes, doc updates **Date library reorganisation** (no new third-party dependency) - New `src/lib/util/dias/` with `_vendor/` (Adobe Apache 2.0 vendored code, ATTRIBUTION.md preserved) and a public facade split into `types`, `values`, `calendars`, `queries`, `operations`, `parse`, `format`, `index`. Consumers import only from `$lib/util/dias`. - All date-related `Intl` consolidated in `dias/format.ts` — `DateFormatter`, cache, `resolveDateOrder`, `resolveHourCycle`, placeholders, defaults. - `ext/dates` now a thin reactive wrapper delegating to `dias`. - Aligned App-layer `HourCycle` to canonical numeric form `12 | 24` (matches `Intl.DateTimeFormat`'s `hour12`). Removed string form `'12h' | '24h'` from `ext/app/types`, `ext/dates`, `ext/presentation/locale-defaults`, and their tests. - Repointed every consumer: `soma/external/dates`, `terra/external/dates`, eight `routes/test/*` pages. - Legacy `src/lib/util/dates/` left untouched by request. **Tier 3 components** - `soma/components/calendar/` — 14 parts, single + multiple selection, `MonthSelect`/`YearSelect`, ISO/simple week numbers, `isDateHoliday` matcher, `data-weekend` flag, navigation live- region, RTL flip, full ARIA, 27-item checklist verified. - `soma/components/range-calendar/` — 14 parts, range selection with live hover preview (novel vs ark/bits/radix/react-aria), `minDays` / `maxDays` / `allowSingleDay`, third-click deselects only the clicked endpoint. - Test pages at `/test/soma/calendar` and `/test/soma/range-calendar` with seven demos each. Links added to the soma index under a new "Dates" section. - READMEs with anatomy, parts, props, ARIA, keyboard, data-attrs, and a competitive comparison table. **Command fixes** - Resolved O(N^2) reactive cascade: introduced `visibleSet: $derived<Set<string>>` on the root provider; per-item `isVisible` is now an O(1) `has()` lookup instead of calling back into the provider, which was re-running all items' derivations on every `itemsVersion` bump. - Added `dir` prop with fallback to `soma.presentation.getDir()`; horizontal arrows flip in RTL via `getDirectionalKeys`; root receives `dir` attribute. **Doc updates (session outcomes)** - `CLAUDE.md`: replaced obsolete `@/glob/lib` date references with `$lib/util/dias`; added canonical date-layer section; reinforced `Provider` export rule and `data-{component}[-{part}]` naming (never `data-soma-*`); clarified translation convention. - `src/uix/soma/COMPONENT_GUIDE.md`: new checklist items 26 (component README required) and 27 (grep for data-attr consistency); new Common Mistakes 11 (`data-soma-*` prefix), 12 (wrong date module), 13 (string `HourCycle`). - `src/uix/soma/SOMA_ARCHITECTURE.md`: clarified data-attr naming (canonical emitter is `createAttrs`; validator checks enum values only, not names); new section covering date types and formatting through `$soma/external/dates` → `$lib/util/dias`; documented numeric `HourCycle`. **Infrastructure** - `PrimitiveHeaderAttributes` and `PrimitiveSelectAttributes` added to `$soma/types`. - `getLastFirstDayOfWeek`, `getNextLastDayOfWeek` re-exported from `$soma/external/dates`. - `calendar.*` and `range-calendar.*` translation blocks added to `$soma/core/langs.ts`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
- Value types: `CalendarDate`, `CalendarDateTime`, `Time`, `ZonedDateTime`
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs Tier 3 (complete): DateField, DatePicker, DateRangeField, DateRangePicker Tier 4 (partial): TimeField, TimePicker (TimeRangeField pending) Core: $lib/util/dias/segments.ts — pure domain primitives for segmented date/time inputs (constants, guards, initializers, reconciliation, content generation, Intl opts) soma/external/dates re-exports everything (no façades inside soma) soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav, SegmentState, KEYS predicates, description writers) Conventions established (A23–A29): A23 — never re-implement date helpers; extend dias A24 — readonlySegments without value anchor logs warning A25 — range components split readonly per-endpoint A26 — contenteditable segments use onbeforeinput to block IME/paste A27 — picker composition pattern with shared writableActive refs A28 — time placeholders render as hh/mm/ss via dias/segments A29 — demo pages are interactive testbeds, not static galleries Demo pages: Material Design 3 styled, every public prop wired to a live control, Field integration + state readout. Day-period uses real radio group in 12h mode. Auto-anchor on range pickers keeps the end month visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly. Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29, checklist items 28–31, picker composition section, dias boundary, directory structure note for soma/datetime/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
- Types: `DateValue`, `TimeValue`, `DateRange`, `DateMatcher`, `Month`, `WeekStartsOn`, `HourCycle`, `TimeGranularity`, `DateOrder`, `Granularity`, `SegmentPart`, `EditableTimeSegmentPart`, `TimeSegmentObj`, `SegmentValueObj`, `DayPeriod`, …
- Queries: `isSameDay`, `hasTime`, `isZonedDateTime`, `isTimeBefore`, `isTimeAfter`, `today`, `now`, `startOfMonth`, `endOfMonth`, `getLastFirstDayOfWeek`, `getNextLastDayOfWeek`, …
- Operations: `dateValueToDate`, `convertTimeValueToDateValue`, `convertTimeValueToTime`, `toCalendarDate`, `toZoned`, …
soma: dias date library, Calendar + RangeCalendar, Command fixes, doc updates **Date library reorganisation** (no new third-party dependency) - New `src/lib/util/dias/` with `_vendor/` (Adobe Apache 2.0 vendored code, ATTRIBUTION.md preserved) and a public facade split into `types`, `values`, `calendars`, `queries`, `operations`, `parse`, `format`, `index`. Consumers import only from `$lib/util/dias`. - All date-related `Intl` consolidated in `dias/format.ts` — `DateFormatter`, cache, `resolveDateOrder`, `resolveHourCycle`, placeholders, defaults. - `ext/dates` now a thin reactive wrapper delegating to `dias`. - Aligned App-layer `HourCycle` to canonical numeric form `12 | 24` (matches `Intl.DateTimeFormat`'s `hour12`). Removed string form `'12h' | '24h'` from `ext/app/types`, `ext/dates`, `ext/presentation/locale-defaults`, and their tests. - Repointed every consumer: `soma/external/dates`, `terra/external/dates`, eight `routes/test/*` pages. - Legacy `src/lib/util/dates/` left untouched by request. **Tier 3 components** - `soma/components/calendar/` — 14 parts, single + multiple selection, `MonthSelect`/`YearSelect`, ISO/simple week numbers, `isDateHoliday` matcher, `data-weekend` flag, navigation live- region, RTL flip, full ARIA, 27-item checklist verified. - `soma/components/range-calendar/` — 14 parts, range selection with live hover preview (novel vs ark/bits/radix/react-aria), `minDays` / `maxDays` / `allowSingleDay`, third-click deselects only the clicked endpoint. - Test pages at `/test/soma/calendar` and `/test/soma/range-calendar` with seven demos each. Links added to the soma index under a new "Dates" section. - READMEs with anatomy, parts, props, ARIA, keyboard, data-attrs, and a competitive comparison table. **Command fixes** - Resolved O(N^2) reactive cascade: introduced `visibleSet: $derived<Set<string>>` on the root provider; per-item `isVisible` is now an O(1) `has()` lookup instead of calling back into the provider, which was re-running all items' derivations on every `itemsVersion` bump. - Added `dir` prop with fallback to `soma.presentation.getDir()`; horizontal arrows flip in RTL via `getDirectionalKeys`; root receives `dir` attribute. **Doc updates (session outcomes)** - `CLAUDE.md`: replaced obsolete `@/glob/lib` date references with `$lib/util/dias`; added canonical date-layer section; reinforced `Provider` export rule and `data-{component}[-{part}]` naming (never `data-soma-*`); clarified translation convention. - `src/uix/soma/COMPONENT_GUIDE.md`: new checklist items 26 (component README required) and 27 (grep for data-attr consistency); new Common Mistakes 11 (`data-soma-*` prefix), 12 (wrong date module), 13 (string `HourCycle`). - `src/uix/soma/SOMA_ARCHITECTURE.md`: clarified data-attr naming (canonical emitter is `createAttrs`; validator checks enum values only, not names); new section covering date types and formatting through `$soma/external/dates` → `$lib/util/dias`; documented numeric `HourCycle`. **Infrastructure** - `PrimitiveHeaderAttributes` and `PrimitiveSelectAttributes` added to `$soma/types`. - `getLastFirstDayOfWeek`, `getNextLastDayOfWeek` re-exported from `$soma/external/dates`. - `calendar.*` and `range-calendar.*` translation blocks added to `$soma/core/langs.ts`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
- Parsing: `parseDate`, `parseDateTime`, `parseTime`
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs Tier 3 (complete): DateField, DatePicker, DateRangeField, DateRangePicker Tier 4 (partial): TimeField, TimePicker (TimeRangeField pending) Core: $lib/util/dias/segments.ts — pure domain primitives for segmented date/time inputs (constants, guards, initializers, reconciliation, content generation, Intl opts) soma/external/dates re-exports everything (no façades inside soma) soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav, SegmentState, KEYS predicates, description writers) Conventions established (A23–A29): A23 — never re-implement date helpers; extend dias A24 — readonlySegments without value anchor logs warning A25 — range components split readonly per-endpoint A26 — contenteditable segments use onbeforeinput to block IME/paste A27 — picker composition pattern with shared writableActive refs A28 — time placeholders render as hh/mm/ss via dias/segments A29 — demo pages are interactive testbeds, not static galleries Demo pages: Material Design 3 styled, every public prop wired to a live control, Field integration + state readout. Day-period uses real radio group in 12h mode. Auto-anchor on range pickers keeps the end month visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly. Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29, checklist items 28–31, picker composition section, dias boundary, directory structure note for soma/datetime/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
- Formatting: `DateFormatter`, `getCachedDateFormat`, `getPlaceholder`, `getDefaultDate`, `getDefaultTime`, `inferGranularity`, `inferTimeGranularity`, `getDefaultHourCycle`, `resolveDateOrder(locale)`, `resolveHourCycle(locale)`
- **Segments (dias/segments.ts)**: constants (`DATE_SEGMENT_PARTS`, `EDITABLE_TIME_SEGMENT_PARTS`, …), type guards (`isDateSegmentPart`, `isEditableTimeSegmentPart`, `isDateAndTimeSegmentObj`, …), pure helpers (`initializeSegmentValues`, `initializeTimeSegmentValues`, `getValueFromSegments`, `getTimeValueFromSegments`, `areAllSegmentsFilled`, `createSegmentContent`, `createTimeSegmentContent`, `getOptsByGranularity`, `getOptsByTimeGranularity`).
soma: dias date library, Calendar + RangeCalendar, Command fixes, doc updates **Date library reorganisation** (no new third-party dependency) - New `src/lib/util/dias/` with `_vendor/` (Adobe Apache 2.0 vendored code, ATTRIBUTION.md preserved) and a public facade split into `types`, `values`, `calendars`, `queries`, `operations`, `parse`, `format`, `index`. Consumers import only from `$lib/util/dias`. - All date-related `Intl` consolidated in `dias/format.ts` — `DateFormatter`, cache, `resolveDateOrder`, `resolveHourCycle`, placeholders, defaults. - `ext/dates` now a thin reactive wrapper delegating to `dias`. - Aligned App-layer `HourCycle` to canonical numeric form `12 | 24` (matches `Intl.DateTimeFormat`'s `hour12`). Removed string form `'12h' | '24h'` from `ext/app/types`, `ext/dates`, `ext/presentation/locale-defaults`, and their tests. - Repointed every consumer: `soma/external/dates`, `terra/external/dates`, eight `routes/test/*` pages. - Legacy `src/lib/util/dates/` left untouched by request. **Tier 3 components** - `soma/components/calendar/` — 14 parts, single + multiple selection, `MonthSelect`/`YearSelect`, ISO/simple week numbers, `isDateHoliday` matcher, `data-weekend` flag, navigation live- region, RTL flip, full ARIA, 27-item checklist verified. - `soma/components/range-calendar/` — 14 parts, range selection with live hover preview (novel vs ark/bits/radix/react-aria), `minDays` / `maxDays` / `allowSingleDay`, third-click deselects only the clicked endpoint. - Test pages at `/test/soma/calendar` and `/test/soma/range-calendar` with seven demos each. Links added to the soma index under a new "Dates" section. - READMEs with anatomy, parts, props, ARIA, keyboard, data-attrs, and a competitive comparison table. **Command fixes** - Resolved O(N^2) reactive cascade: introduced `visibleSet: $derived<Set<string>>` on the root provider; per-item `isVisible` is now an O(1) `has()` lookup instead of calling back into the provider, which was re-running all items' derivations on every `itemsVersion` bump. - Added `dir` prop with fallback to `soma.presentation.getDir()`; horizontal arrows flip in RTL via `getDirectionalKeys`; root receives `dir` attribute. **Doc updates (session outcomes)** - `CLAUDE.md`: replaced obsolete `@/glob/lib` date references with `$lib/util/dias`; added canonical date-layer section; reinforced `Provider` export rule and `data-{component}[-{part}]` naming (never `data-soma-*`); clarified translation convention. - `src/uix/soma/COMPONENT_GUIDE.md`: new checklist items 26 (component README required) and 27 (grep for data-attr consistency); new Common Mistakes 11 (`data-soma-*` prefix), 12 (wrong date module), 13 (string `HourCycle`). - `src/uix/soma/SOMA_ARCHITECTURE.md`: clarified data-attr naming (canonical emitter is `createAttrs`; validator checks enum values only, not names); new section covering date types and formatting through `$soma/external/dates` → `$lib/util/dias`; documented numeric `HourCycle`. **Infrastructure** - `PrimitiveHeaderAttributes` and `PrimitiveSelectAttributes` added to `$soma/types`. - `getLastFirstDayOfWeek`, `getNextLastDayOfWeek` re-exported from `$soma/external/dates`. - `calendar.*` and `range-calendar.*` translation blocks added to `$soma/core/langs.ts`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
`HourCycle` is canonically the numeric form `12 | 24` across the whole framework, matching `Intl.DateTimeFormat`'s `hour12` resolved option. String forms like `'12h'`/`'24h'` are legacy and must not appear in new code.
**`soma/datetime/` holds only UI-level helpers** (screen-reader announcer, DOM segment navigation, `SegmentState` shape with `lastKeyZero`/`hasLeftFocus`/`updating`, `isAcceptableSegmentKey` using KEYS, description-element DOM writers). It must not re-export `$libs/days` symbols — consumers import from `$libs/days` directly. Extending `$libs/days` is the default for new date/time helpers; adding to `soma/datetime/` is only correct when the helper is genuinely UI-specific.
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs Tier 3 (complete): DateField, DatePicker, DateRangeField, DateRangePicker Tier 4 (partial): TimeField, TimePicker (TimeRangeField pending) Core: $lib/util/dias/segments.ts — pure domain primitives for segmented date/time inputs (constants, guards, initializers, reconciliation, content generation, Intl opts) soma/external/dates re-exports everything (no façades inside soma) soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav, SegmentState, KEYS predicates, description writers) Conventions established (A23–A29): A23 — never re-implement date helpers; extend dias A24 — readonlySegments without value anchor logs warning A25 — range components split readonly per-endpoint A26 — contenteditable segments use onbeforeinput to block IME/paste A27 — picker composition pattern with shared writableActive refs A28 — time placeholders render as hh/mm/ss via dias/segments A29 — demo pages are interactive testbeds, not static galleries Demo pages: Material Design 3 styled, every public prop wired to a live control, Field integration + state readout. Day-period uses real radio group in 12h mode. Auto-anchor on range pickers keeps the end month visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly. Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29, checklist items 28–31, picker composition section, dias boundary, directory structure note for soma/datetime/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
soma: gesture layer, drawer component, full audit fixes across 26 components Gesture Layer: - Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking - VelocityTracker with ring buffer, staleness check, projection - 52 tests (velocity + gesture integration) - setPointerCapture deferred until moveBuffer exceeded (prevents click stealing) Drawer Component: - 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close - Gesture.drag() integration for swipe dismiss - Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle - Modal/non-modal/non-dismissible modes - Logical directions (start/end) RTL-aware via soma presentation - CSS custom properties (--drawer-progress, --drawer-offset-x/y) Architecture: - T1: 219 files migrated from $soma/ alias to relative paths - T2: 35 wrappers — noop replaced with () => {} inline - Translation system: componentLangs + common.buttons.* + idlangref constants - COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns - contracts.ts: case-normalized registerContract/assertContract Audit Fixes (P0 critical): - DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super - TB1: data-table attr on wrapper - CH1/SW1/NF4: hidden input rendering fixes - SP2: panel cleanup on unmount - RG1: roving tabindex fallback to first item - CO1: aria-labelledby on collapsible content Audit Fixes (P1 high): - SafePolygon integrated in dropdown-menu SubProvider + tooltip - 9 langs.ts files with idlangref constants - Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards - Stepper: count decrement + private, aria-label on List - ToggleGroup: roving tabindex in multiple mode - Select: pure aria-activedescendant (no mixed focus), label registry - NumberField: startValue respects min, scrubber commit on pointerup only - Toolbar: element-based focus tracking (not index) Audit Fixes (P2 medium): - A6: timer/listener cleanup in 6 components - A12: getDirectionalKeys in 4 components - A10: DOM queries scoped to root in 3 components - Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern - Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup - Pagination: reactive page clamping, Set-based computePageRange - Scroll-area: thumbSize zero-guard, mount flash prevention - Splitter: collapse/expand respects adjacent constraints, immediate pointer capture 5 issues remain (design decisions): stepper registry, table sub-row IDs, tags-input ARIA model, tags-input paste, toast exit animation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
### Static method convention (project-wide)
All classes that use Svelte context follow the same pattern:
| Method | Returns | Use when |
| ---------------- | ----------------------- | --------------------------------- |
| `X.create(opts)` | instance | Creating + registering in context |
| `X.get()` | instance or `undefined` | Parent/context is optional |
| `X.require()` | instance (throws) | Parent/context is required |
This applies to `App`, `Soma`, and all state classes that use context. No
standalone functions. No `from()`. No `ctx` exposed.
### Texto funcional
Las traducciones propias del componente se declaran en el morfo:
```ts
export const drawerMorfo = {
name: 'Drawer',
kebab: 'drawer',
translations: {
trigger: { es: 'Abrir cajon', en: 'Open drawer' }
},
parts: [
{
name: 'Trigger',
kebab: 'trigger',
aria: [{ attr: 'aria-label', value: v.translationRef('trigger', 'Open drawer') }]
}
]
} as const satisfies Morfo;
```
Las traducciones compartidas no se duplican por componente:
```ts
value: v.commonRef('buttons.close', 'Close'); // #?common.buttons.close|Close
```
`ActiveUix` conecta el registro de morfos con `ActiveLangs`. Cuando el
provider crea `createSomaRuntime(morfo, sources)` o `soma.runtime(morfo,
sources)`, `registerMorfo(morfo)` registra el contrato `data-*` y publica
`morfo.translations` bajo `components.{kebab}`.
`commonLangs` en `core/langs.ts` aporta los defaults de `common.*`.
`ActiveUix` los registra sin pisar hojas existentes, de modo que el
integrador puede pasar sus propias traducciones y UIX solo completa lo que
falte.
No existe catálogo global por componente. `ActiveUix` conecta el registro de
morfos; cada componente publica sus textos cuando su morfo se registra.
## 8. Sistema reactivo
Capa fina sobre runes de Svelte 5 que permite pasar estado reactivo por referencia entre clases.
- `state<T>(initial)` → `State<T>` (mutable, `.current`)
- `readableActive(() => value)` → `Active<T>` (readonly derived)
- `writableActive(getter, setter)` → `State<T>` (two-way binding)
Los wrappers `.svelte` convierten props normales a `Active`/`State` con estas funciones. Esta conversion es la frontera entre el mundo de props de Svelte y el mundo de clases reactivas de soma.
soma: gesture layer, drawer component, full audit fixes across 26 components Gesture Layer: - Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking - VelocityTracker with ring buffer, staleness check, projection - 52 tests (velocity + gesture integration) - setPointerCapture deferred until moveBuffer exceeded (prevents click stealing) Drawer Component: - 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close - Gesture.drag() integration for swipe dismiss - Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle - Modal/non-modal/non-dismissible modes - Logical directions (start/end) RTL-aware via soma presentation - CSS custom properties (--drawer-progress, --drawer-offset-x/y) Architecture: - T1: 219 files migrated from $soma/ alias to relative paths - T2: 35 wrappers — noop replaced with () => {} inline - Translation system: componentLangs + common.buttons.* + idlangref constants - COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns - contracts.ts: case-normalized registerContract/assertContract Audit Fixes (P0 critical): - DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super - TB1: data-table attr on wrapper - CH1/SW1/NF4: hidden input rendering fixes - SP2: panel cleanup on unmount - RG1: roving tabindex fallback to first item - CO1: aria-labelledby on collapsible content Audit Fixes (P1 high): - SafePolygon integrated in dropdown-menu SubProvider + tooltip - 9 langs.ts files with idlangref constants - Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards - Stepper: count decrement + private, aria-label on List - ToggleGroup: roving tabindex in multiple mode - Select: pure aria-activedescendant (no mixed focus), label registry - NumberField: startValue respects min, scrubber commit on pointerup only - Toolbar: element-based focus tracking (not index) Audit Fixes (P2 medium): - A6: timer/listener cleanup in 6 components - A12: getDirectionalKeys in 4 components - A10: DOM queries scoped to root in 3 components - Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern - Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup - Pagination: reactive page clamping, Set-based computePageRange - Scroll-area: thumbSize zero-guard, mount flash prevention - Splitter: collapse/expand respects adjacent constraints, immediate pointer capture 5 issues remain (design decisions): stepper registry, table sub-row IDs, tags-input ARIA model, tags-input paste, toast exit animation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
## 9. Contratos data-\*
Los `data-*` son API publica formal, validados con `assertContract()`.
Convencion:
```
data-dialog → provider (sin -provider, sin -root)
data-dialog-trigger → parte
data-dialog-content → parte
data-state="open|closed" → estado
data-disabled → flag
data-side="top|right|bottom|left" → posicion flotante
data-align="start|center|end" → alineacion
data-starting-style → animacion de entrada (1 frame)
data-ending-style → animacion de salida (persiste)
data-nested → es hijo de otro del mismo tipo
data-nested-open → tiene un hijo abierto
data-dragging → gesture drag activo
data-highlighted → item con virtual focus (aria-activedescendant)
data-resizing → splitter resize activo
```
CSS variables expuestas:
```
--soma-floating-transform-origin
--soma-floating-available-width
--soma-floating-available-height
--soma-floating-anchor-width
--soma-floating-anchor-height
--soma-dialog-depth
--soma-dialog-nested-count
--drawer-progress → 0-1 drag progress
--drawer-offset-x / y → drag offset in px
--soma-toast-swipe-move-x / y → toast swipe offset
```
## 10. IDs
Los IDs se generan con contexto de componente:
```
soma-dialog-c12
soma-dialog-trigger-c13
soma-dialog-content-c14
```
Pattern: `soma-{component}-{part}-{uid}`. Descriptivos e inspeccionables.
## 11. Barrel exports
### Componentes (jerárquico)
```ts
// $soma/components/index.ts
export * as Collapsible from './collapsible';
export * as Dialog from './dialog';
export * as Popover from './popover';
```
Consumo:
```ts
import { Dialog, Popover } from '$soma/components';
soma: gesture layer, drawer component, full audit fixes across 26 components Gesture Layer: - Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking - VelocityTracker with ring buffer, staleness check, projection - 52 tests (velocity + gesture integration) - setPointerCapture deferred until moveBuffer exceeded (prevents click stealing) Drawer Component: - 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close - Gesture.drag() integration for swipe dismiss - Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle - Modal/non-modal/non-dismissible modes - Logical directions (start/end) RTL-aware via soma presentation - CSS custom properties (--drawer-progress, --drawer-offset-x/y) Architecture: - T1: 219 files migrated from $soma/ alias to relative paths - T2: 35 wrappers — noop replaced with () => {} inline - Translation system: componentLangs + common.buttons.* + idlangref constants - COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns - contracts.ts: case-normalized registerContract/assertContract Audit Fixes (P0 critical): - DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super - TB1: data-table attr on wrapper - CH1/SW1/NF4: hidden input rendering fixes - SP2: panel cleanup on unmount - RG1: roving tabindex fallback to first item - CO1: aria-labelledby on collapsible content Audit Fixes (P1 high): - SafePolygon integrated in dropdown-menu SubProvider + tooltip - 9 langs.ts files with idlangref constants - Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards - Stepper: count decrement + private, aria-label on List - ToggleGroup: roving tabindex in multiple mode - Select: pure aria-activedescendant (no mixed focus), label registry - NumberField: startValue respects min, scrubber commit on pointerup only - Toolbar: element-based focus tracking (not index) Audit Fixes (P2 medium): - A6: timer/listener cleanup in 6 components - A12: getDirectionalKeys in 4 components - A10: DOM queries scoped to root in 3 components - Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern - Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup - Pagination: reactive page clamping, Set-based computePageRange - Scroll-area: thumbSize zero-guard, mount flash prevention - Splitter: collapse/expand respects adjacent constraints, immediate pointer capture 5 issues remain (design decisions): stepper registry, table sub-row IDs, tags-input ARIA model, tags-input paste, toast exit animation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
Dialog.Provider; // no SomaDialogProvider, no TerraDialogProvider
Dialog.Trigger;
```
### Internal
```ts
import { Portal, Arrow, VisuallyHidden, Soma } from '$soma/components/internal';
```
## 12. Fronteras externas
`soma` distingue entre:
- internos: `layers/`, `reactive/`, `dom/`, `provider/` — helpers propios
- externos: `@floating-ui/dom`, `runed`, `tabbable` — dependencias npm
Si una dependencia tiene API inestable o podria cambiar, se accede a traves de una frontera formal (como `layers/floating/` wrappea @floating-ui). Las dependencias estables (runed, svelte) se importan directamente.
## 13. Estructura del directorio
```
src/uix/soma/
soma: 12 components, architecture docs, naming fixes, form tier complete Components (12): - Accordion (keyboard nav, single/multiple, collapsible, loop) - Checkbox (indeterminate, group, readonly) - Collapsible - Combobox (input + floating listbox, consumer-side filtering) - Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount) - DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items) - Popover (floating, openOnHover, modal, custom anchor) - RadioGroup (roving tabindex, orientation, readonly) - Select (single/multiple, typeahead, scroll-into-view, hidden select) - Slider (drag, keyboard, single/range, ticks, vertical) - Switch (hidden input, data-checked) - Tooltip (skip-delay group, 3-state, hover/focus) Architecture: - State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts (avoids Vite module resolution ambiguity with .svelte wrappers) - Export name standardized: always Provider, never Root - COMPONENT_GUIDE.md: step-by-step implementation guide with checklist - SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table - README.md: updated naming, file structure, inventory - context.ts: error message uses Provider not Root - Demo index page at /test/soma/ Bug fixes: - DropdownMenu trigger missing onclick/onkeydown in props - DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent) - Checkbox getContext in event handler → captured in constructor - Select context not found → was Vite cache issue (not naming) - ScrollLock unlock path - Floating wrapper z-index: auto override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
├── SOMA_ARCHITECTURE.md ← this document
├── COMPONENT_GUIDE.md ← step-by-step implementation guide
├── README.md ← API reference
├── runtime.svelte.ts ← SomaRuntime (morfo interpreter)
├── errors.ts ← typed runtime/context errors
soma: 12 components, architecture docs, naming fixes, form tier complete Components (12): - Accordion (keyboard nav, single/multiple, collapsible, loop) - Checkbox (indeterminate, group, readonly) - Collapsible - Combobox (input + floating listbox, consumer-side filtering) - Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount) - DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items) - Popover (floating, openOnHover, modal, custom anchor) - RadioGroup (roving tabindex, orientation, readonly) - Select (single/multiple, typeahead, scroll-into-view, hidden select) - Slider (drag, keyboard, single/range, ticks, vertical) - Switch (hidden input, data-checked) - Tooltip (skip-delay group, 3-state, hover/focus) Architecture: - State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts (avoids Vite module resolution ambiguity with .svelte wrappers) - Export name standardized: always Provider, never Root - COMPONENT_GUIDE.md: step-by-step implementation guide with checklist - SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table - README.md: updated naming, file structure, inventory - context.ts: error message uses Provider not Root - Demo index page at /test/soma/ Bug fixes: - DropdownMenu trigger missing onclick/onkeydown in props - DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent) - Checkbox getContext in event handler → captured in constructor - Select context not found → was Vite cache issue (not naming) - ScrollLock unlock path - Floating wrapper z-index: auto override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
├── core/
│ ├── soma.svelte.ts ← Soma class (root instance)
│ └── langs.ts ← shared commonLangs defaults
soma: 12 components, architecture docs, naming fixes, form tier complete Components (12): - Accordion (keyboard nav, single/multiple, collapsible, loop) - Checkbox (indeterminate, group, readonly) - Collapsible - Combobox (input + floating listbox, consumer-side filtering) - Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount) - DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items) - Popover (floating, openOnHover, modal, custom anchor) - RadioGroup (roving tabindex, orientation, readonly) - Select (single/multiple, typeahead, scroll-into-view, hidden select) - Slider (drag, keyboard, single/range, ticks, vertical) - Switch (hidden input, data-checked) - Tooltip (skip-delay group, 3-state, hover/focus) Architecture: - State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts (avoids Vite module resolution ambiguity with .svelte wrappers) - Export name standardized: always Provider, never Root - COMPONENT_GUIDE.md: step-by-step implementation guide with checklist - SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table - README.md: updated naming, file structure, inventory - context.ts: error message uses Provider not Root - Demo index page at /test/soma/ Bug fixes: - DropdownMenu trigger missing onclick/onkeydown in props - DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent) - Checkbox getContext in event handler → captured in constructor - Select context not found → was Vite cache issue (not naming) - ScrollLock unlock path - Floating wrapper z-index: auto override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
├── reactive/ ← reactive system
├── provider/ ← context + opts bridge
├── props/ ← mergeProps, composeHandlers
├── keyboard/ ← KEYS, directional
├── dom/ ← DOM utilities, focus
├── css/ ← styleToString, cssToStyleObj
├── id/ ← createId, useId
├── types/ ← shared types + service interfaces
soma: 12 components, architecture docs, naming fixes, form tier complete Components (12): - Accordion (keyboard nav, single/multiple, collapsible, loop) - Checkbox (indeterminate, group, readonly) - Collapsible - Combobox (input + floating listbox, consumer-side filtering) - Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount) - DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items) - Popover (floating, openOnHover, modal, custom anchor) - RadioGroup (roving tabindex, orientation, readonly) - Select (single/multiple, typeahead, scroll-into-view, hidden select) - Slider (drag, keyboard, single/range, ticks, vertical) - Switch (hidden input, data-checked) - Tooltip (skip-delay group, 3-state, hover/focus) Architecture: - State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts (avoids Vite module resolution ambiguity with .svelte wrappers) - Export name standardized: always Provider, never Root - COMPONENT_GUIDE.md: step-by-step implementation guide with checklist - SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table - README.md: updated naming, file structure, inventory - context.ts: error message uses Provider not Root - Demo index page at /test/soma/ Bug fixes: - DropdownMenu trigger missing onclick/onkeydown in props - DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent) - Checkbox getContext in event handler → captured in constructor - Select context not found → was Vite cache issue (not naming) - ScrollLock unlock path - Floating wrapper z-index: auto override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
├── layers/ ← behavior layers (classes only)
│ ├── presence.svelte.ts
│ ├── focus-scope.svelte.ts
│ ├── dismissal.svelte.ts
│ ├── text-selection.svelte.ts
│ ├── scroll-lock.svelte.ts
│ ├── resize-observer.svelte.ts
│ └── floating/
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs Tier 3 (complete): DateField, DatePicker, DateRangeField, DateRangePicker Tier 4 (partial): TimeField, TimePicker (TimeRangeField pending) Core: $lib/util/dias/segments.ts — pure domain primitives for segmented date/time inputs (constants, guards, initializers, reconciliation, content generation, Intl opts) soma/external/dates re-exports everything (no façades inside soma) soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav, SegmentState, KEYS predicates, description writers) Conventions established (A23–A29): A23 — never re-implement date helpers; extend dias A24 — readonlySegments without value anchor logs warning A25 — range components split readonly per-endpoint A26 — contenteditable segments use onbeforeinput to block IME/paste A27 — picker composition pattern with shared writableActive refs A28 — time placeholders render as hh/mm/ss via dias/segments A29 — demo pages are interactive testbeds, not static galleries Demo pages: Material Design 3 styled, every public prop wired to a live control, Field integration + state readout. Day-period uses real radio group in 12h mode. Auto-anchor on range pickers keeps the end month visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly. Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29, checklist items 28–31, picker composition section, dias boundary, directory structure note for soma/datetime/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
├── datetime/ ← UI-only helpers (announcer, segment DOM nav,
│ segment UI-state shapes, segment-key predicates,
│ description-element writers). NO date math,
│ NO re-exports of days — import `$libs/days`
│ directly.
├── components/
soma: 12 components, architecture docs, naming fixes, form tier complete Components (12): - Accordion (keyboard nav, single/multiple, collapsible, loop) - Checkbox (indeterminate, group, readonly) - Collapsible - Combobox (input + floating listbox, consumer-side filtering) - Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount) - DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items) - Popover (floating, openOnHover, modal, custom anchor) - RadioGroup (roving tabindex, orientation, readonly) - Select (single/multiple, typeahead, scroll-into-view, hidden select) - Slider (drag, keyboard, single/range, ticks, vertical) - Switch (hidden input, data-checked) - Tooltip (skip-delay group, 3-state, hover/focus) Architecture: - State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts (avoids Vite module resolution ambiguity with .svelte wrappers) - Export name standardized: always Provider, never Root - COMPONENT_GUIDE.md: step-by-step implementation guide with checklist - SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table - README.md: updated naming, file structure, inventory - context.ts: error message uses Provider not Root - Demo index page at /test/soma/ Bug fixes: - DropdownMenu trigger missing onclick/onkeydown in props - DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent) - Checkbox getContext in event handler → captured in constructor - Select context not found → was Vite cache issue (not naming) - ScrollLock unlock path - Floating wrapper z-index: auto override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
│ ├── internal/ ← Portal, Arrow, VisuallyHidden, <Soma>
│ ├── {name}/ ← each headless component
│ │ ├── {name}-provider.svelte.ts ← state classes (NOT {name}.svelte.ts)
│ │ ├── types.ts ← public props + canonical field shapes
│ │ ├── langs.ts ← optional idlangref constants for imperative strings
soma: 12 components, architecture docs, naming fixes, form tier complete Components (12): - Accordion (keyboard nav, single/multiple, collapsible, loop) - Checkbox (indeterminate, group, readonly) - Collapsible - Combobox (input + floating listbox, consumer-side filtering) - Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount) - DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items) - Popover (floating, openOnHover, modal, custom anchor) - RadioGroup (roving tabindex, orientation, readonly) - Select (single/multiple, typeahead, scroll-into-view, hidden select) - Slider (drag, keyboard, single/range, ticks, vertical) - Switch (hidden input, data-checked) - Tooltip (skip-delay group, 3-state, hover/focus) Architecture: - State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts (avoids Vite module resolution ambiguity with .svelte wrappers) - Export name standardized: always Provider, never Root - COMPONENT_GUIDE.md: step-by-step implementation guide with checklist - SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table - README.md: updated naming, file structure, inventory - context.ts: error message uses Provider not Root - Demo index page at /test/soma/ Bug fixes: - DropdownMenu trigger missing onclick/onkeydown in props - DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent) - Checkbox getContext in event handler → captured in constructor - Select context not found → was Vite cache issue (not naming) - ScrollLock unlock path - Floating wrapper z-index: auto override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
│ │ ├── exports.ts
│ │ ├── index.ts
│ │ └── components/
│ │ ├── {name}.svelte ← root wrapper
│ │ ├── {name}-trigger.svelte
│ │ └── ...
│ └── index.ts ← hierarchical barrel
└── index.ts ← main barrel
```
soma: 12 components, architecture docs, naming fixes, form tier complete Components (12): - Accordion (keyboard nav, single/multiple, collapsible, loop) - Checkbox (indeterminate, group, readonly) - Collapsible - Combobox (input + floating listbox, consumer-side filtering) - Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount) - DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items) - Popover (floating, openOnHover, modal, custom anchor) - RadioGroup (roving tabindex, orientation, readonly) - Select (single/multiple, typeahead, scroll-into-view, hidden select) - Slider (drag, keyboard, single/range, ticks, vertical) - Switch (hidden input, data-checked) - Tooltip (skip-delay group, 3-state, hover/focus) Architecture: - State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts (avoids Vite module resolution ambiguity with .svelte wrappers) - Export name standardized: always Provider, never Root - COMPONENT_GUIDE.md: step-by-step implementation guide with checklist - SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table - README.md: updated naming, file structure, inventory - context.ts: error message uses Provider not Root - Demo index page at /test/soma/ Bug fixes: - DropdownMenu trigger missing onclick/onkeydown in props - DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent) - Checkbox getContext in event handler → captured in constructor - Select context not found → was Vite cache issue (not naming) - ScrollLock unlock path - Floating wrapper z-index: auto override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
### File naming convention
- State class: `{name}-provider.svelte.ts` — NOT `{name}.svelte.ts`
- Avoids Vite module resolution ambiguity with `{name}.svelte` wrapper
- Reflects what's inside: provider/state classes
soma: 12 components, architecture docs, naming fixes, form tier complete Components (12): - Accordion (keyboard nav, single/multiple, collapsible, loop) - Checkbox (indeterminate, group, readonly) - Collapsible - Combobox (input + floating listbox, consumer-side filtering) - Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount) - DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items) - Popover (floating, openOnHover, modal, custom anchor) - RadioGroup (roving tabindex, orientation, readonly) - Select (single/multiple, typeahead, scroll-into-view, hidden select) - Slider (drag, keyboard, single/range, ticks, vertical) - Switch (hidden input, data-checked) - Tooltip (skip-delay group, 3-state, hover/focus) Architecture: - State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts (avoids Vite module resolution ambiguity with .svelte wrappers) - Export name standardized: always Provider, never Root - COMPONENT_GUIDE.md: step-by-step implementation guide with checklist - SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table - README.md: updated naming, file structure, inventory - context.ts: error message uses Provider not Root - Demo index page at /test/soma/ Bug fixes: - DropdownMenu trigger missing onclick/onkeydown in props - DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent) - Checkbox getContext in event handler → captured in constructor - Select context not found → was Vite cache issue (not naming) - ScrollLock unlock path - Floating wrapper z-index: auto override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
- Root wrapper: `{name}.svelte` in `components/` subdirectory
- Export name: always `Provider`, never `Root`
## 14. Anti-patterns
Avoid in soma:
- Complex logic inside wrapper `.svelte` — belongs in Provider
- Props drilling when context is the correct pattern
- `data-*` attrs outside of contract
- Inventing part names without checking reference library anatomies (ark-ui, bits-ui, radix-ui)
- Nesting layers as component wrappers in templates
- Inline `z-index: auto` that overrides CSS
- Coupling primitives to app libraries
- Product copy inside the primitive
- Speculative abstractions ("just in case")
- One-line files that only re-export (merge into parent)
- Redundant naming prefixes (SomaDialog, DialogLayerState)
- Dummy refs to satisfy a type — use `ProviderOpts` for no-DOM roots
- **State class file named same as wrapper** — `select.svelte.ts` + `components/select.svelte` causes Vite module duplication. Always use `{name}-provider.svelte.ts`
- **Event handlers not in props** — defining onclick as a class method but not including it in the props derived object
- **getContext in event handlers** — getContext only works during initialization. Capture references in constructor
- **Exporting as Root** — always `Provider`, never `Root`
- **Skipping reference library comparison** — mandatory step, no exceptions
- **Comments in Spanish** — all code comments in English
soma: gesture layer, drawer component, full audit fixes across 26 components Gesture Layer: - Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking - VelocityTracker with ring buffer, staleness check, projection - 52 tests (velocity + gesture integration) - setPointerCapture deferred until moveBuffer exceeded (prevents click stealing) Drawer Component: - 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close - Gesture.drag() integration for swipe dismiss - Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle - Modal/non-modal/non-dismissible modes - Logical directions (start/end) RTL-aware via soma presentation - CSS custom properties (--drawer-progress, --drawer-offset-x/y) Architecture: - T1: 219 files migrated from $soma/ alias to relative paths - T2: 35 wrappers — noop replaced with () => {} inline - Translation system: componentLangs + common.buttons.* + idlangref constants - COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns - contracts.ts: case-normalized registerContract/assertContract Audit Fixes (P0 critical): - DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super - TB1: data-table attr on wrapper - CH1/SW1/NF4: hidden input rendering fixes - SP2: panel cleanup on unmount - RG1: roving tabindex fallback to first item - CO1: aria-labelledby on collapsible content Audit Fixes (P1 high): - SafePolygon integrated in dropdown-menu SubProvider + tooltip - 9 langs.ts files with idlangref constants - Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards - Stepper: count decrement + private, aria-label on List - ToggleGroup: roving tabindex in multiple mode - Select: pure aria-activedescendant (no mixed focus), label registry - NumberField: startValue respects min, scrubber commit on pointerup only - Toolbar: element-based focus tracking (not index) Audit Fixes (P2 medium): - A6: timer/listener cleanup in 6 components - A12: getDirectionalKeys in 4 components - A10: DOM queries scoped to root in 3 components - Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern - Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup - Pagination: reactive page clamping, Set-based computePageRange - Scroll-area: thumbSize zero-guard, mount flash prevention - Splitter: collapse/expand respects adjacent constraints, immediate pointer capture 5 issues remain (design decisions): stepper registry, table sub-row IDs, tags-input ARIA model, tags-input paste, toast exit animation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
- **Standalone context functions** — no `createX()`, `getX()`, `useX()` as loose functions. Use `X.create()`, `X.get()`, `X.require()` static methods
- **Importing from `$lib/ext/app`** in components — components access services through `Soma`, never App directly
- **`from()` as factory name** — use `create()` consistently
- **Re-implementing date/time helpers inside soma** — extend `$libs/days` (A23). Importing from `$lib/util/dates` (legacy vendored) or `@internationalized/date` directly is forbidden; use `$libs/days`.
- **Re-export façades over days** — a soma module whose only job is to forward `$libs/days` symbols is dead weight. Consumers import from `$libs/days` directly.
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs Tier 3 (complete): DateField, DatePicker, DateRangeField, DateRangePicker Tier 4 (partial): TimeField, TimePicker (TimeRangeField pending) Core: $lib/util/dias/segments.ts — pure domain primitives for segmented date/time inputs (constants, guards, initializers, reconciliation, content generation, Intl opts) soma/external/dates re-exports everything (no façades inside soma) soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav, SegmentState, KEYS predicates, description writers) Conventions established (A23–A29): A23 — never re-implement date helpers; extend dias A24 — readonlySegments without value anchor logs warning A25 — range components split readonly per-endpoint A26 — contenteditable segments use onbeforeinput to block IME/paste A27 — picker composition pattern with shared writableActive refs A28 — time placeholders render as hh/mm/ss via dias/segments A29 — demo pages are interactive testbeds, not static galleries Demo pages: Material Design 3 styled, every public prop wired to a live control, Field integration + state readout. Day-period uses real radio group in 12h mode. Auto-anchor on range pickers keeps the end month visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly. Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29, checklist items 28–31, picker composition section, dias boundary, directory structure note for soma/datetime/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
- **UI-level helpers in dias, or date math in `soma/datetime/`** — dias is pure (no DOM, no Svelte, no KEYS); `soma/datetime/` is UI-only (screen-reader announcer, DOM segment navigation, `SegmentState` shapes, KEYS-based predicates). No crossover
- **`readonlySegments` without a concrete value anchor** — A24: warn via `soma?.logger.warn` when `value` is undefined. Range components split into `startReadonlySegments` / `endReadonlySegments` (A25)
- **`keydown.preventDefault()` as the only guard on contenteditable segments** — IME/paste/drop bypass keydown. Always add `onbeforeinput: e => e.preventDefault()` (A26)
- **Time placeholders as `'––'`** — use `createSegmentContent` / `createTimeSegmentContent` from `dias/segments.ts`; time parts render as `hh`/`mm`/`ss` (A28)
- **Pickers that reimplement field/calendar/popover** — compose via shared `writableActive` refs (A27). Only `Provider`, `Trigger`, and the calendar/slider bridge are unique parts
- **Demo pages as galleries of canned snippets** — every soma demo must be an interactive testbed wiring every public prop to a live control, including a Field-integration section (A29)
- **`HourCycle` as `'12h' \| '24h'`** — canonical form is numeric `12 \| 24` (matches `Intl.DateTimeFormat.hour12`). String forms are legacy
soma: 12 components, architecture docs, naming fixes, form tier complete Components (12): - Accordion (keyboard nav, single/multiple, collapsible, loop) - Checkbox (indeterminate, group, readonly) - Collapsible - Combobox (input + floating listbox, consumer-side filtering) - Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount) - DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items) - Popover (floating, openOnHover, modal, custom anchor) - RadioGroup (roving tabindex, orientation, readonly) - Select (single/multiple, typeahead, scroll-into-view, hidden select) - Slider (drag, keyboard, single/range, ticks, vertical) - Switch (hidden input, data-checked) - Tooltip (skip-delay group, 3-state, hover/focus) Architecture: - State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts (avoids Vite module resolution ambiguity with .svelte wrappers) - Export name standardized: always Provider, never Root - COMPONENT_GUIDE.md: step-by-step implementation guide with checklist - SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table - README.md: updated naming, file structure, inventory - context.ts: error message uses Provider not Root - Demo index page at /test/soma/ Bug fixes: - DropdownMenu trigger missing onclick/onkeydown in props - DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent) - Checkbox getContext in event handler → captured in constructor - Select context not found → was Vite cache issue (not naming) - ScrollLock unlock path - Floating wrapper z-index: auto override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
docs: prune obsolete audits + studies, refresh remaining references Deleted 60 obsolete documents that no longer reflect the active-uix state: - 47 per-component audit logs (*-audit.md, audit_N.md, AUDIT.md) - 5 global soma audits (AUDIT_1, codex_audit, soma-audit-04-20/21, audit-prompt template) - 5 closed studies (soma/study, morfo/study, morfo/DESIGN, morfo/ PROVIDER_STUDY_2026-04-23, uix/CONTINUITY_2026-04-24) - 2 superseded refactor proposals (refactor_claude, refactor_code) - 1 stale bug log (form/BUG-onchange-onblur-hang — repro path src/routes/test/sium/kitchen-sink no longer exists) Updated the remaining operational references to drop dead links and reflect the post-cleanup architecture: - src/uix/README.md — rewritten links section, dropped pointers to terra/air/CONTINUITY/refactor docs, updated adom/dom paths - src/uix/active_architecture.md — header now dated 2026-05-08, lists the major shifts since 2026-04-26 (provider inheritance dropped, eidos in production, doctrina parte IV, sound eager-init); pendings section reflects the eidos migration roadmap; "see also" purged of deleted files - src/uix/morfo/README.md — file tree updated (removed DESIGN/study, added compile/runtime/PERMUTATION_RUNNER); see-also pruned - src/uix/sema/README.md — SoundChannel section now describes the eager-init pattern (constructor-time, not lazy); pointer to sema-implementation-guide added - src/uix/soma/README.md — drops terra/air mentions, declares the actual dependency direction (eidos -> soma, not the reverse) - src/uix/soma/SOMA_ARCHITECTURE.md — §15 rewritten as "Estado actual y deuda histórica" reflecting Provider-inheritance-drop; demo path updated to web/routes/ - src/uix/soma/COMPONENT_GUIDE.md — A23 dates section now points to $libs/days (no external/dates façade); demo paths swapped from /test/soma/* to web/routes/ - src/uix/morfo/PERMUTATION_RUNNER.md — paths swapped to web/routes/ with a relocation note - CLAUDE.md — Refactor Documents section replaced with Reference Documents pointing to per-layer READMEs npm run check holds at baseline (39 pre-existing errors, none new). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
## 15. Estado actual y deuda histórica
Soma ha pasado por varias fases. La forma actual (rama `active-uix`,
post-2026-05-08):
- **Provider inheritance dropped** — los providers ya no heredan de un
base abstracto; son clases concretas. La mecanica DOM comun vive en
`SomaRuntime.part(...)`, y los casos que necesitan eventos semanticos
usan el mismo `SomaRuntime` para `trigger`/`keydown`.
soma/morfo/active-uix: relocate runtime + rename SomaRuntime + sema opcional Architectural correction. Morfo was importing Svelte runes, $adom, and $uix/sema — all violations of "morfo is pure declarative DNA". The runtime that interprets a CompiledMorfo lives where it executes: soma. Move: - src/uix/morfo/runtime.svelte.ts → src/uix/soma/runtime.svelte.ts - src/uix/morfo/runtime.svelte.test.ts → src/uix/soma/runtime.svelte.test.ts Rename (consistent with the move; "soma's runtime", per the doctrine "morfo declares, soma executes"): - class MorfoRuntime → SomaRuntime - createMorfoRuntime() → createSomaRuntime() - type MorfoRuntimeSources → SomaRuntimeSources - error prefixes [morfo-runtime] → [soma-runtime] Sema is now genuinely ornamental: - ActiveUix.semantic returns EngineSemantic | undefined (no longer throws when no engine is configured). Components in SSR / headless tests / audio-disabled environments stay functional. - SomaRuntime.trigger() skips the emit step AND the target-resolution check when no engine is present — only requires a DOM target when something will actually be emitted. - Soma.semantic getter returns | undefined too (was non-optional). Consumers updated: - src/uix/active-uix/active-uix.svelte.ts — imports createSomaRuntime from $soma; semantic getter returns | undefined - src/uix/active-uix/types.ts — interface SomaRuntime, semantic | undefined - 8 providers (toggle, switch, collapsible, dialog, drawer, popover, toast, avatar) — type SomaRuntime, import path $soma - src/uix/soma/index.ts — re-exports createSomaRuntime + types - src/uix/morfo/index.ts — drops the runtime re-exports (morfo is pure) - src/uix/sema/signal.ts — JSDoc reference updated - src/uix/soma/core/soma.svelte.ts — semantic getter | undefined Test added: SomaRuntime.trigger() succeeds without a semantic engine (skips emit + target check, runs handler directly). Docs aligned to the new shape: - active_architecture.md — runtime locations, sema as ornamental - soma/SOMA_ARCHITECTURE.md, soma/README.md — name + import paths - morfo/README.md — file listing now flags morfo as PURE TS, points at soma/runtime.svelte.ts for the interpreter - sema/README.md — SomaRuntime references - README.md — root architecture diagram - CLAUDE.md — hand-off note Tests 218/218 green. npm run check holds at the 39-error baseline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
- **SomaRuntime cachea** la compilación del morfo (`compileMorfo` por
docs: prune obsolete audits + studies, refresh remaining references Deleted 60 obsolete documents that no longer reflect the active-uix state: - 47 per-component audit logs (*-audit.md, audit_N.md, AUDIT.md) - 5 global soma audits (AUDIT_1, codex_audit, soma-audit-04-20/21, audit-prompt template) - 5 closed studies (soma/study, morfo/study, morfo/DESIGN, morfo/ PROVIDER_STUDY_2026-04-23, uix/CONTINUITY_2026-04-24) - 2 superseded refactor proposals (refactor_claude, refactor_code) - 1 stale bug log (form/BUG-onchange-onblur-hang — repro path src/routes/test/sium/kitchen-sink no longer exists) Updated the remaining operational references to drop dead links and reflect the post-cleanup architecture: - src/uix/README.md — rewritten links section, dropped pointers to terra/air/CONTINUITY/refactor docs, updated adom/dom paths - src/uix/active_architecture.md — header now dated 2026-05-08, lists the major shifts since 2026-04-26 (provider inheritance dropped, eidos in production, doctrina parte IV, sound eager-init); pendings section reflects the eidos migration roadmap; "see also" purged of deleted files - src/uix/morfo/README.md — file tree updated (removed DESIGN/study, added compile/runtime/PERMUTATION_RUNNER); see-also pruned - src/uix/sema/README.md — SoundChannel section now describes the eager-init pattern (constructor-time, not lazy); pointer to sema-implementation-guide added - src/uix/soma/README.md — drops terra/air mentions, declares the actual dependency direction (eidos -> soma, not the reverse) - src/uix/soma/SOMA_ARCHITECTURE.md — §15 rewritten as "Estado actual y deuda histórica" reflecting Provider-inheritance-drop; demo path updated to web/routes/ - src/uix/soma/COMPONENT_GUIDE.md — A23 dates section now points to $libs/days (no external/dates façade); demo paths swapped from /test/soma/* to web/routes/ - src/uix/morfo/PERMUTATION_RUNNER.md — paths swapped to web/routes/ with a relocation note - CLAUDE.md — Refactor Documents section replaced with Reference Documents pointing to per-layer READMEs npm run check holds at baseline (39 pre-existing errors, none new). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
WeakMap) y registra los `effects` que sincronizan `state → attrs` vía
`dom.apply`.
- **Naming**: `Provider` (nunca `Root`); child providers referencian al
padre como `provider`, nunca `root`. El export de un componente
multi-parte sigue la forma compound `Toggle.Provider + Toggle.Trigger
- ...`.
docs: prune obsolete audits + studies, refresh remaining references Deleted 60 obsolete documents that no longer reflect the active-uix state: - 47 per-component audit logs (*-audit.md, audit_N.md, AUDIT.md) - 5 global soma audits (AUDIT_1, codex_audit, soma-audit-04-20/21, audit-prompt template) - 5 closed studies (soma/study, morfo/study, morfo/DESIGN, morfo/ PROVIDER_STUDY_2026-04-23, uix/CONTINUITY_2026-04-24) - 2 superseded refactor proposals (refactor_claude, refactor_code) - 1 stale bug log (form/BUG-onchange-onblur-hang — repro path src/routes/test/sium/kitchen-sink no longer exists) Updated the remaining operational references to drop dead links and reflect the post-cleanup architecture: - src/uix/README.md — rewritten links section, dropped pointers to terra/air/CONTINUITY/refactor docs, updated adom/dom paths - src/uix/active_architecture.md — header now dated 2026-05-08, lists the major shifts since 2026-04-26 (provider inheritance dropped, eidos in production, doctrina parte IV, sound eager-init); pendings section reflects the eidos migration roadmap; "see also" purged of deleted files - src/uix/morfo/README.md — file tree updated (removed DESIGN/study, added compile/runtime/PERMUTATION_RUNNER); see-also pruned - src/uix/sema/README.md — SoundChannel section now describes the eager-init pattern (constructor-time, not lazy); pointer to sema-implementation-guide added - src/uix/soma/README.md — drops terra/air mentions, declares the actual dependency direction (eidos -> soma, not the reverse) - src/uix/soma/SOMA_ARCHITECTURE.md — §15 rewritten as "Estado actual y deuda histórica" reflecting Provider-inheritance-drop; demo path updated to web/routes/ - src/uix/soma/COMPONENT_GUIDE.md — A23 dates section now points to $libs/days (no external/dates façade); demo paths swapped from /test/soma/* to web/routes/ - src/uix/morfo/PERMUTATION_RUNNER.md — paths swapped to web/routes/ with a relocation note - CLAUDE.md — Refactor Documents section replaced with Reference Documents pointing to per-layer READMEs npm run check holds at baseline (39 pre-existing errors, none new). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
- **Data-attr naming**: `data-{component}` (provider) y
`data-{component}-{kebab}` (sub-parts). Sin prefijo `data-soma-*`. El
compiler emite estos via `compiled.parts.attrs`.
- **State files**: `{name}-provider.svelte.ts` (explicit, no ambiguity).
- **IDs**: descriptive (`soma-dialog-trigger-c13`).
## 16. Regla de estabilidad
Un componente de soma se considera estable cuando:
- su API publica esta clara y documentada con JSDoc
- sus `data-*` estan registrados y validados con `assertContract`
- el wrapper y el Provider siguen el patron general
- su accesibilidad base esta resuelta (ARIA, roles, keyboard)
- sus props se han comparado con ark-ui, bits-ui y radix-ui
docs: prune obsolete audits + studies, refresh remaining references Deleted 60 obsolete documents that no longer reflect the active-uix state: - 47 per-component audit logs (*-audit.md, audit_N.md, AUDIT.md) - 5 global soma audits (AUDIT_1, codex_audit, soma-audit-04-20/21, audit-prompt template) - 5 closed studies (soma/study, morfo/study, morfo/DESIGN, morfo/ PROVIDER_STUDY_2026-04-23, uix/CONTINUITY_2026-04-24) - 2 superseded refactor proposals (refactor_claude, refactor_code) - 1 stale bug log (form/BUG-onchange-onblur-hang — repro path src/routes/test/sium/kitchen-sink no longer exists) Updated the remaining operational references to drop dead links and reflect the post-cleanup architecture: - src/uix/README.md — rewritten links section, dropped pointers to terra/air/CONTINUITY/refactor docs, updated adom/dom paths - src/uix/active_architecture.md — header now dated 2026-05-08, lists the major shifts since 2026-04-26 (provider inheritance dropped, eidos in production, doctrina parte IV, sound eager-init); pendings section reflects the eidos migration roadmap; "see also" purged of deleted files - src/uix/morfo/README.md — file tree updated (removed DESIGN/study, added compile/runtime/PERMUTATION_RUNNER); see-also pruned - src/uix/sema/README.md — SoundChannel section now describes the eager-init pattern (constructor-time, not lazy); pointer to sema-implementation-guide added - src/uix/soma/README.md — drops terra/air mentions, declares the actual dependency direction (eidos -> soma, not the reverse) - src/uix/soma/SOMA_ARCHITECTURE.md — §15 rewritten as "Estado actual y deuda histórica" reflecting Provider-inheritance-drop; demo path updated to web/routes/ - src/uix/soma/COMPONENT_GUIDE.md — A23 dates section now points to $libs/days (no external/dates façade); demo paths swapped from /test/soma/* to web/routes/ - src/uix/morfo/PERMUTATION_RUNNER.md — paths swapped to web/routes/ with a relocation note - CLAUDE.md — Refactor Documents section replaced with Reference Documents pointing to per-layer READMEs npm run check holds at baseline (39 pre-existing errors, none new). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
- tiene demo page funcional en `web/routes/{componente}/`
- no depende de hacks locales, z-index hardcodeados, ni demo CSS para sostenerse
- compila con 0 errores (`svelte-check`)
soma: 12 components, architecture docs, naming fixes, form tier complete Components (12): - Accordion (keyboard nav, single/multiple, collapsible, loop) - Checkbox (indeterminate, group, readonly) - Collapsible - Combobox (input + floating listbox, consumer-side filtering) - Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount) - DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items) - Popover (floating, openOnHover, modal, custom anchor) - RadioGroup (roving tabindex, orientation, readonly) - Select (single/multiple, typeahead, scroll-into-view, hidden select) - Slider (drag, keyboard, single/range, ticks, vertical) - Switch (hidden input, data-checked) - Tooltip (skip-delay group, 3-state, hover/focus) Architecture: - State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts (avoids Vite module resolution ambiguity with .svelte wrappers) - Export name standardized: always Provider, never Root - COMPONENT_GUIDE.md: step-by-step implementation guide with checklist - SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table - README.md: updated naming, file structure, inventory - context.ts: error message uses Provider not Root - Demo index page at /test/soma/ Bug fixes: - DropdownMenu trigger missing onclick/onkeydown in props - DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent) - Checkbox getContext in event handler → captured in constructor - Select context not found → was Vite cache issue (not naming) - ScrollLock unlock path - Floating wrapper z-index: auto override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
## 17. New component checklist
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs Tier 3 (complete): DateField, DatePicker, DateRangeField, DateRangePicker Tier 4 (partial): TimeField, TimePicker (TimeRangeField pending) Core: $lib/util/dias/segments.ts — pure domain primitives for segmented date/time inputs (constants, guards, initializers, reconciliation, content generation, Intl opts) soma/external/dates re-exports everything (no façades inside soma) soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav, SegmentState, KEYS predicates, description writers) Conventions established (A23–A29): A23 — never re-implement date helpers; extend dias A24 — readonlySegments without value anchor logs warning A25 — range components split readonly per-endpoint A26 — contenteditable segments use onbeforeinput to block IME/paste A27 — picker composition pattern with shared writableActive refs A28 — time placeholders render as hh/mm/ss via dias/segments A29 — demo pages are interactive testbeds, not static galleries Demo pages: Material Design 3 styled, every public prop wired to a live control, Field integration + state readout. Day-period uses real radio group in 12h mode. Auto-anchor on range pickers keeps the end month visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly. Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29, checklist items 28–31, picker composition section, dias boundary, directory structure note for soma/datetime/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
See `COMPONENT_GUIDE.md` for the full step-by-step process (27 general steps + 4 date/time specific, with rules A1–A29). Summary:
```
[ ] 1. Compare with ark-ui, bits-ui, radix-ui — feature table
[ ] 2. Verify membership criteria
[ ] 3. Define parts + attrs + morfo.translations when the component owns text
[ ] 4. Create types.ts (props + canonical field shapes)
[ ] 5. Create langs.ts only for imperative idlangref constants, not as the catalog
[ ] 6. Create {name}-provider.svelte.ts (concrete state classes, no Provider inheritance)
[ ] 7. Create wrapper .svelte files (thin)
[ ] 8. Create exports.ts + index.ts
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs Tier 3 (complete): DateField, DatePicker, DateRangeField, DateRangePicker Tier 4 (partial): TimeField, TimePicker (TimeRangeField pending) Core: $lib/util/dias/segments.ts — pure domain primitives for segmented date/time inputs (constants, guards, initializers, reconciliation, content generation, Intl opts) soma/external/dates re-exports everything (no façades inside soma) soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav, SegmentState, KEYS predicates, description writers) Conventions established (A23–A29): A23 — never re-implement date helpers; extend dias A24 — readonlySegments without value anchor logs warning A25 — range components split readonly per-endpoint A26 — contenteditable segments use onbeforeinput to block IME/paste A27 — picker composition pattern with shared writableActive refs A28 — time placeholders render as hh/mm/ss via dias/segments A29 — demo pages are interactive testbeds, not static galleries Demo pages: Material Design 3 styled, every public prop wired to a live control, Field integration + state readout. Day-period uses real radio group in 12h mode. Auto-anchor on range pickers keeps the end month visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly. Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29, checklist items 28–31, picker composition section, dias boundary, directory structure note for soma/datetime/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
[ ] 9. Create interactive demo page + link in index (A29)
[ ] 10. README.md with anatomy, ARIA, data-attrs, comparison table
[ ] 11. svelte-check + test in browser
[ ] 12. Date/time components: only consume date/time domain via `$libs/days`,
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs Tier 3 (complete): DateField, DatePicker, DateRangeField, DateRangePicker Tier 4 (partial): TimeField, TimePicker (TimeRangeField pending) Core: $lib/util/dias/segments.ts — pure domain primitives for segmented date/time inputs (constants, guards, initializers, reconciliation, content generation, Intl opts) soma/external/dates re-exports everything (no façades inside soma) soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav, SegmentState, KEYS predicates, description writers) Conventions established (A23–A29): A23 — never re-implement date helpers; extend dias A24 — readonlySegments without value anchor logs warning A25 — range components split readonly per-endpoint A26 — contenteditable segments use onbeforeinput to block IME/paste A27 — picker composition pattern with shared writableActive refs A28 — time placeholders render as hh/mm/ss via dias/segments A29 — demo pages are interactive testbeds, not static galleries Demo pages: Material Design 3 styled, every public prop wired to a live control, Field integration + state readout. Day-period uses real radio group in 12h mode. Auto-anchor on range pickers keeps the end month visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly. Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29, checklist items 28–31, picker composition section, dias boundary, directory structure note for soma/datetime/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
`onbeforeinput` on contenteditable, readonly-without-value warning,
picker composition pattern (A23–A28)
```

Powered by TurnKey Linux.