# Continue
Fecha de corte: 2026-05-19. Rama: `active-uix`.
## Guia vigente: migracion de componentes Soma -> Eidos
Objetivo: migrar componentes uno a uno desde Soma a Eidos sin crear una API
inferior a Air ni a las librerias de referencia, y sin romper fronteras de
capa. Esta es la guia que debe leerse antes de tocar cualquier componente.
### Flujo obligatorio por componente
1. Leer Air en la rama anterior:
`git show gita/glm-5:src/uix/air/components/{name}/...` cuando exista.
Air es la primera baseline visual local.
2. Leer Morfo/Soma actuales del componente:
- `src/uix/morfo/components/{name}.ts`
- `src/uix/soma/components/{name}/...`
- README y tests de Soma si existen.
3. Auditar Morfo/Sema antes de tocar Eidos:
- Clasificar el componente como pasivo, interactivo o mixto.
- Justificar explicitamente cualquier `0 events`. No se acepta por omision.
- Para cada accion de usuario real definir si debe existir evento semantico:
`family`, `verb`, `sequence`, `intent`, `target`, `prewrite` y `commit`.
- Verificar que Soma usa `runtime.trigger(...)` para esas acciones, no solo
callbacks locales (`onValueChange`, `onValueCommit`, etc.).
- No emitir eventos continuos por cada frame/pixel si el evento semantico es
el inicio, el commit o un cambio discreto.
4. Comparar con referentes externos oficiales relevantes:
Radix/Radix Themes, Ark UI, Bits UI, shadcn-svelte, React Aria, Base UI,
Vaul u otros solo si aplican al componente.
5. Crear o actualizar `src/uix/eidos/components/{name}/README.md` antes de
cerrar el componente. Debe incluir:
- baseline Air,
- tabla Soma/Morfo,
- tabla Morfo/Sema con eventos, targets y decisiones,
- tabla comparativa externa,
- decisiones explicitas,
- gaps con decision: implementar, diferir a Soma/Morfo, diferir a v2 o
descartar por no pertenecer a Eidos.
6. Solo despues tocar wrapper, recipe, tokens o contrato.
### Fronteras de capa
- Morfo declara partes, `data-*`, ARIA, eventos y translations.
- Soma posee comportamiento, estado, teclado, foco, formularios, portal,
floating, timers, imagenes, validacion y escrituras DOM headless via
`ActiveDom`.
- Sema/events posee ocurrencias perceptivas; no decide CSS ni tokens.
- Eidos posee wrapper visual, props visuales, recipe CSS, tokens y
documentacion visual.
- Morfo no se da por correcto por existir: cada componente nuevo o revisado
debe auditar si sus `events` representan bien la capa semantica. Componentes
pasivos como `progress`/`meter` pueden tener `0 events`; componentes
interactivos como `slider`, `pagination`, `rating-group`, `number-field` o
`search-field` requieren decision semantica explicita.
- Si una funcionalidad nueva necesita estado, ARIA, DOM imperative o lifecycle,
no se mete en Eidos: se define primero en Morfo/Soma.
- Si una parte publica aparece en Eidos, debe estar respaldada por Morfo/Soma
salvo que sea puramente visual y se documente como excepcion deliberada.
### API Eidos canonica
- Opcion C: root visual + partes attached en el namespace:
`import { Component } from '$uix/eidos/components/component'`.
- Sin `Provider` publico en Eidos.
- Sin APIs flat con snippets como alternativa paralela.
- Sin alias duplicados para compatibilidad: no hay compatibilidad hacia atras.
- Un solo entry mental por componente. No crear dos ficheros root para lo mismo.
- El root vive en `{name}.svelte`; partes en `{name}-{part}.svelte`.
- `index.ts` exporta named namespace y default del mismo namespace.
- Para single-part, el default/root es el componente completo y no se inventan
partes vacias.
### Naming y contratos
- No prefijar tipos con `Eidos` dentro del modulo del componente:
usar `FieldProps`, `FieldSize`, `FieldVariant`, etc.
- No usar nombres legacy `danger/success/warning/info`; el sistema usa
intents canonicos cuando aplica.
- No usar `--eidos-*`, `--soma-*`, `data-soma-*` ni marcadores privados de otra
capa en CSS Eidos.
- Los `data-*` publicos deben venir de Morfo o de props visuales Eidos
documentadas (`data-size`, `data-variant`, `data-color`, etc.).
- Si el CSS necesita un selector nuevo de estado/comportamiento, primero mirar
si debe declararse en Morfo/Soma.
- Los sizes son canonicos y responsive mediante `ResponsiveProp` +
`ActiveEidos.resolve(...)`. No inventar escalas locales sin razon.
- Las props visuales comunes deben mantenerse pequenas: normalmente `size`,
`variant`, `color`, `radius`, `orientation` solo cuando el componente lo
justifica.
### CSS y tokens
- Cada componente tiene `components/{name}/{name}.css` e import explicito en
`src/uix/eidos/index.css`.
- Los valores authorables viven en `THEME_BASE_RECIPE_TOKENS` y se publican en
`src/uix/eidos/generated/base.css` mediante `npm run generate:eidos-css`.
- No introducir colores raw, `font-size` literal en px/rem ni magic numbers si
deben ser tokens.
- El CSS de componente consume `var(--{component}-...)`; los valores salen del
config generado.
- No tocar `web/routes` ni demos salvo orden explicita.
### Decisions fijadas durante esta tanda
- `Popover.side="auto"` se descarta. Soma Floating usa `side` como preferencia
(`top/right/bottom/left`) y `avoidCollisions`/flip/shift resuelven el ajuste.
- `Field.RequiredIndicator` se define en Morfo/Soma antes de Eidos porque tiene
contrato publico y `aria-hidden`; Eidos solo renderiza el `*` por defecto.
- Air `Field.Error` se migra como `Field.ErrorText`, alineado con Soma/Morfo y
Ark UI. No se crea alias `Error`.
- Los comportamientos compartidos de imagen deben modelarse como
`ImageProvider`/provider headless reutilizable en Soma, no como helper suelto
en Eidos ni dentro de `layers`.
- Los tipos/utilidades de color compartidos pertenecen a `src/libs/color` si
tambien los consumen SIUM u otras capas; Soma no debe ser la libreria de
colores transversal.
- `Switch` conserva `Switch.Thumb` como parte publica, pero el root de Eidos
monta un thumb visual por defecto cuando no recibe children. Motivo:
`` es el uso minimo documentado en la demo y no puede renderizar un
track vacio. No es una API flat paralela: el compound sigue siendo la forma
avanzada para sustituir el thumb o sus iconos.
### Verificacion minima antes de cerrar un componente
- Regenerar CSS si se tocan recipe tokens:
`npm run generate:eidos-css`.
- Ejecutar tests focales del componente Soma si se toca Morfo/Soma.
- Ejecutar:
- `node --import tsx/esm scripts/eidos-lint-all.ts`
- `npm run check`
- Ejecutar vitest focal:
`npx vitest run src/uix/eidos src/uix/morfo/compile.test.ts src/uix/morfo/schema.test.ts`
y anadir tests Soma concretos si hubo cambios de comportamiento.
- `npm run lint` no es fiable como validacion global ahora mismo: el repo tiene
deuda de formato previa y snippets legacy no parseables. Formatear solo los
archivos tocados con `npx prettier --write ...`.
### Estado actual de la tanda Eidos
- Ultimo componente corregido/verificado: `Switch`.
- Correccion 2026-05-19:
- `Soma SwitchThumb` expone `{ checked }` al snippet del thumb.
- `Eidos Switch` monta `SwitchThumb` por defecto si el root no recibe
children.
- `Eidos SwitchThumb` pinta `SvgCheck` por defecto cuando `checked=true`.
- Bug corregido: recursividad accidental de snippets en
`switch-thumb.svelte`, que provocaba `500` en `/uix/components/switch`.
- Token anadido: `--switch-thumb-icon-color`, publicado desde
`THEME_BASE_RECIPE_TOKENS` y regenerado en `generated/base.css`.
- Validado despues de la correccion de `Switch`:
- `npx vitest run src/uix/soma/components/switch/switch-provider.svelte.test.ts src/uix/eidos/component-visual-attrs.test.ts src/uix/eidos/recipe-css-contract.test.ts`
-> 3 archivos, 12 tests OK.
- `npm run check` -> 0 errores, 0 warnings.
- Navegador `/uix/components/switch`: status 200; el switch monta thumb,
cambia a `checked` y renderiza `svg[data-svg="check"]` dentro del thumb.
- Ultimo componente cerrado antes de esta correccion: `Field`.
- `Field` anadido en Eidos con `Label`, `RequiredIndicator`, `Control`,
`Input`, `HelperText`, `ErrorText`, `Prefix` y `Suffix`.
- `RequiredIndicator` anadido a Morfo/Soma y cubierto por test del provider.
- Validado despues de `Field`:
- `npx vitest run src/uix/soma/components/field/field-provider.svelte.test.ts src/uix/morfo/compile.test.ts src/uix/morfo/schema.test.ts src/uix/eidos`
-> 11 archivos, 129 tests OK.
- `node --import tsx/esm scripts/eidos-lint-all.ts` -> 0 invalid.
- `npm run check` -> 0 errores, 0 warnings.
- Siguientes candidatos recomendados: `toolbar`, `tag-group`, despues
`file-upload`, `tags-input`, `editable`, `stepper`. Los grandes
(`select`, `combobox`, `calendar`, `date-field`, `date-picker`,
`date-range-picker`, `time-field`, `time-picker`) requieren tabla previa mas
detallada antes de tocar codigo.
### Archivos temporales
- No commitear logs temporales:
`.codex-vite-*.log`, `.codex-vite-*.err.log`, `debug.log`, `md`.
## Corte para continuar manana
- Rama `active-uix` queda con la tanda Soma -> Eidos commitada por fases.
- No se tocaron demos ni rutas; el trabajo fue en `src/uix/eidos/components`,
`src/uix/eidos/lib/recipes/base.ts`, `src/uix/eidos/index.css`, CSS generado
y documentacion.
- Validacion de la ultima tanda:
- `npx vitest run src/uix/eidos` -> 7 archivos, 84 tests OK.
- `npm run check` -> 0 errores, 0 warnings.
- `node --import tsx/esm scripts/eidos-lint-all.ts` -> 0 invalid, sin drift
hotspots.
- Regla nueva para continuar Eidos componente por componente:
- La primera referencia es Air en la rama anterior
(`glm-5:src/uix/air/components/{name}`) cuando exista.
- Despues se compara con Soma/Morfo actuales y con referentes externos
relevantes (Radix/Radix Themes, Ark UI, Bits UI, shadcn-svelte y React Aria
si aplica).
- Cada componente Eidos debe tener tabla en `components/{name}/README.md`
con gaps y decisiones antes de tocar wrapper/recipe/tokens.
- Objetivo: no dejar Eidos por debajo de Air ni de las plataformas de
referencia en funcionalidades reales.
- Primer componente auditado con el nuevo protocolo:
- `Dialog`: comparado contra Air, Radix, Ark UI, Bits UI y shadcn-svelte.
- Decision: no se crea `Dialog.Positioner`; `Content.position` cubre esa
responsabilidad visual sobre la grid canonica 3x3.
- Decision: se anaden overrides visuales `width`, `minWidth`, `maxWidth`,
`height`, `minHeight`, `maxHeight` en `Dialog.Content`, serializados como
variables privadas del recipe. Soma sigue siendo dueno de comportamiento.
- Segundo componente auditado con el nuevo protocolo:
- `Drawer`: comparado contra Air (`glm-5`), Soma/Morfo actuales, Vaul,
shadcn-svelte y Ark UI Dialog como referencia de dialog/layer.
- Decision: no se crea `Drawer.Positioner`; la geometria edge-anchored vive
en `data-side` + `data-variant` y en la receta.
- Decision: no se implementa `shouldScaleBackground` de Vaul en Drawer local;
es un efecto global sobre el wrapper de app y requiere contrato de shell.
- Decision: se anaden overrides visuales `width`, `minWidth`, `maxWidth`,
`height`, `minHeight`, `maxHeight` en `Drawer.Content`, serializados como
variables privadas del recipe. El size canonico `sm/md/lg/full` sigue como
preset principal.
- Tercer componente auditado con el nuevo protocolo:
- `Popover`: Air no tenia equivalente en `glm-5`, por tanto la referencia
local pasa a ser Soma/Morfo y la comparativa externa Radix, Ark UI, Bits UI
y shadcn-svelte.
- Decision: no se crea `Popover.Positioner`; la posicion pertenece a Soma
Floating y Eidos solo consume `data-side`, `data-align` y variables CSS.
- Decision: no se crean `Popover.Title`/`Description` hasta que exista caso
real; Popover suele contener UI arbitraria y el etiquetado puede vivir en
contenido consumidor.
- Decision: se anaden `matchAnchorWidth`, `width`, `minWidth`, `maxWidth`,
`height`, `minHeight`, `maxHeight` en `Popover.Content`, serializados como
variables privadas. La receta usa transform-origin y available-height de
Soma Floating.
- Cuarto componente auditado con el nuevo protocolo:
- `Tooltip`: Air no tenia equivalente en `glm-5`; referencia local
Soma/Morfo, comparativa externa Radix, Ark UI, Bits UI y shadcn-svelte.
- Decision: no se crea `Tooltip.Positioner` ni `Tooltip.ArrowTip`; Soma
Floating y Soma Arrow son los duenos de esa geometria.
- Decision: se anaden `matchAnchorWidth`, `width`, `minWidth`, `maxWidth`,
`height`, `minHeight`, `maxHeight` en `Tooltip.Content`, serializados como
variables privadas. La receta usa transform-origin y available-height de
Soma Floating.
- Decision: se alinea Soma con Radix/Bits: el tooltip cierra al activar/clicar
el trigger por defecto. `disableCloseOnTriggerClick` queda como escape hatch
en `Tooltip.Group` y `Tooltip`.
- Componentes Eidos nuevos desde Soma:
- `meter`
- `progress`
- `slider`
- `pagination`
- `rating-group`
- `search-field`
- `number-field`
- `breadcrumb`
- Siguiente decision antes de seguir migrando:
- Continuar con componentes de riesgo bajo/medio (`field`, `toolbar`,
`tag-group`) o parar a definir una tabla de migracion por componente para
los grandes (`select`, `combobox`, `calendar`, `date-field`).
- Para cada componente nuevo conviene escribir primero: partes Soma,
props visuales Eidos, `data-*` que estampa el wrapper, tokens de recipe y
attrs morfo-backed que consumira el CSS.
Actualizacion Eidos component migration 2026-05-17:
- Inicio de la migracion Soma -> Eidos siguiendo `soma/COMPONENT_GUIDE.md`
y `eidos/components/README.md`.
- Componentes migrados:
- `Progress`: root visual `progress.svelte`, `Progress.Indicator`,
`progress.css`, `types.ts` e `index.ts` con disciplined option C.
- `Meter`: root visual `meter.svelte`, `Meter.Indicator`, `meter.css`,
`types.ts` e `index.ts` con disciplined option C.
- Decisiones aplicadas:
- Eidos no crea comportamiento: ambos wrappers envuelven `Provider` e
`Indicator` publicos de Soma.
- La unica prop visual anadida es `size?: ResponsiveProp<'sm' | 'md' | 'lg'>`,
resuelta con `ActiveEidos.resolve(...)` y proyectada como `data-size`.
- `Progress` usa el `data-state` de Soma (`indeterminate`, `loading`,
`loaded`) para cargar/terminar/indeterminado.
- `Meter` usa el `data-state` de Soma (`below`, `optimum`, `above`) para
zonas visuales; no se anade intent ni color en Eidos.
- Los tokens base viven en `THEME_BASE_RECIPE_TOKENS` y se regenero
`src/uix/eidos/generated/base.css`.
- Guardias actualizadas:
- `component-visual-attrs.test.ts` protege `data-size` en `Progress` y
`Meter`.
- `scripts/eidos-lint-all.ts` reconoce `data-floating-wrapper` como marcador
publico actual y retira el sufijo legacy `flat`.
- `components/README.md` refleja `meter` y `progress` como migrados.
- Validado:
- `npx vitest run src/uix/eidos` -> 7 archivos, 84 tests OK.
- `npm run check` -> 0 errores, 0 warnings.
- `node --import tsx/esm scripts/eidos-lint-all.ts` -> 0 invalid, sin drift
hotspots.
- Segunda tanda del mismo bloque:
- `Slider`: root visual `slider.svelte`, partes `Range`, `Thumb`, `Tick`,
`slider.css`, `types.ts` e `index.ts` con disciplined option C.
- La receta usa las posiciones inline calculadas por Soma y solo define
track, range, thumb, ticks, foco, disabled y size visual.
- `component-visual-attrs.test.ts`, `components/README.md`,
`THEME_BASE_RECIPE_TOKENS`, `index.css` y `generated/base.css` quedan
actualizados.
- Validado despues de `Slider`:
- `npx vitest run src/uix/eidos` -> 7 archivos, 84 tests OK.
- `npm run check` -> 0 errores, 0 warnings.
- `node --import tsx/esm scripts/eidos-lint-all.ts` -> 0 invalid, sin drift
hotspots.
- Tercera tanda del mismo bloque:
- `Pagination`: root visual `pagination.svelte`, partes `PrevTrigger`,
`NextTrigger`, `Item`, `Ellipsis`, `pagination.css`, `types.ts` e
`index.ts`.
- El root conserva el snippet de Soma con `{ pages, totalPages }` y solo
anade `size` visual resuelto por `ActiveEidos`.
- La receta cubre controles, estado selected, disabled, ellipsis y focus.
- Validado despues de `Pagination`:
- `npx vitest run src/uix/eidos` -> 7 archivos, 84 tests OK.
- `npm run check` -> 0 errores, 0 warnings.
- `node --import tsx/esm scripts/eidos-lint-all.ts` -> 0 invalid, sin drift
hotspots.
- Cuarta tanda del mismo bloque:
- `RatingGroup`: root visual `rating-group.svelte`, parte `Item`,
`rating-group.css`, `types.ts` e `index.ts`.
- No se inventa icono/star por defecto ni API nueva; el wrapper conserva los
snippets de Soma (`items` en root y `state` en item) y la receta estiliza
`active`, `partial`, `inactive`, foco, vertical/horizontal y disabled.
- Validado despues de `RatingGroup`:
- `npx vitest run src/uix/eidos` -> 7 archivos, 84 tests OK.
- `npm run check` -> 0 errores, 0 warnings.
- `node --import tsx/esm scripts/eidos-lint-all.ts` -> 0 invalid, sin drift
hotspots.
- Quinta tanda del mismo bloque:
- `SearchField`: root visual `search-field.svelte`, partes `Input`,
`ClearTrigger`, `search-field.css`, `types.ts` e `index.ts`.
- El wrapper conserva el snippet de Soma (`value`, `isEmpty`, `isFocused`,
`clear`) y solo anade `size`.
- La receta cubre contenedor, input, clear trigger, focused, invalid, empty
y disabled.
- Validado despues de `SearchField`:
- `npx vitest run src/uix/eidos` -> 7 archivos, 84 tests OK.
- `npm run check` -> 0 errores, 0 warnings.
- `node --import tsx/esm scripts/eidos-lint-all.ts` -> 0 invalid, sin drift
hotspots.
- Sexta tanda del mismo bloque:
- `NumberField`: root visual `number-field.svelte`, partes `Input`,
`IncrementTrigger`, `DecrementTrigger`, `Scrubber`, `number-field.css`,
`types.ts` e `index.ts`.
- El wrapper conserva formato/parseo/spinbutton/scrub de Soma y solo anade
`size`.
- La receta cubre contenedor, input, triggers, scrubber, focused, scrubbing,
invalid y disabled.
- Validado despues de `NumberField`:
- `npx vitest run src/uix/eidos` -> 7 archivos, 84 tests OK.
- `npm run check` -> 0 errores, 0 warnings.
- `node --import tsx/esm scripts/eidos-lint-all.ts` -> 0 invalid, sin drift
hotspots.
- Septima tanda del mismo bloque:
- `Breadcrumb`: root visual `breadcrumb.svelte`, partes `List`, `Item`,
`Link`, `Separator`, `Ellipsis`, `breadcrumb.css`, `types.ts` e `index.ts`.
- El wrapper conserva semantica, current item/link, ellipsis interactivo y
ARIA de Soma; Eidos solo anade `size`.
- La receta consume `data-current` y `data-interactive` declarados por Morfo
y define tokens para gaps, tipografia, links, separador, ellipsis, foco y
transiciones.
- Validado despues de `Breadcrumb`:
- `npx vitest run src/uix/eidos` -> 7 archivos, 84 tests OK.
- `npm run check` -> 0 errores, 0 warnings.
- `node --import tsx/esm scripts/eidos-lint-all.ts` -> 0 invalid, sin drift
hotspots.
- Siguiente bloque sugerido:
- Migrar otro grupo de bajo riesgo desde Soma a Eidos: candidatos reales por
morfo/soma existentes son `field`, `toolbar` o `tag-group`.
- Antes de tocar componentes grandes (`select`, `combobox`, `calendar`,
`date-field`) conviene fijar una tabla por componente: partes Soma,
props visuales Eidos, recipe tokens y attrs esperados.
Actualizacion Eidos components audit 2026-05-17:
- Se acometieron los hallazgos autorizados sobre `src/uix/eidos/components`
sin tocar demos ni rutas `web/routes/uix`:
- `Tabs` instala su contexto visual de forma sincronica durante init, no en
`$effect`; los hijos `Tabs.Content` ya no pueden inicializar sin contexto.
- `Tabs.Indicator` deja de observarse a si mismo con `ResizeObserver`, para
evitar loops de medida provocados por sus propias escrituras inline.
- `Avatar.Image` ya no arranca oculto cuando hay `src`; la visibilidad queda
derivada de `src` y de la ultima URL fallida.
- `Accordion.Trigger` ya no contiene `