From 79c94d85d5daee54b58f6a779ebbf99b1d1aab91 Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 22 May 2026 04:35:04 +0200 Subject: [PATCH] wip(popover): unified min-width = anchor + sideOffset 6 across pickers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Day-end checkpoint for the floating-layer normalization work. Documented in CONTINUE.md. Done: - popover.css: new `--_popover-min-width` defaults to `var(--floating-anchor-width, auto)` so popover layer is NEVER narrower than its anchor (Radix / React Aria convention). Consumers that should opt out (tooltip, dialog) override with `auto`. - soma popover-content default sideOffset 0 → 6. - 5 picker eidos contents: sideOffset 10 → 6 (matches the new default; per-component override removed visually but kept as explicit prop). - combobox provider: reverted the multi-on-close stamp-labels patch. Multi-mode selections will be rendered as inline tags via a future `Combobox.SelectedTags` part (Material UI Autocomplete pattern, decided with user). Pending (see CONTINUE.md): - Re-anchor pickers to their Input wrapper (currently anchored to the chevron trigger ~14px). Required for the min-width normalization to actually show — research vs Ark UI / Radix / React Aria confirmed composite-input pickers anchor to the control wrapper, not the trigger button. - Combobox `SelectedTags` part for multi-mode inline tags. - SearchField floating Results part (user opted to make it Combobox-like rather than reuse Combobox). svelte-check --threshold error → 0 errors / 0 warnings / 4584 files. Co-Authored-By: Claude Opus 4.7 (1M context) --- continue.md | 1929 +---------------- .../color-picker/color-picker-content.svelte | 2 +- .../date-picker/date-picker-content.svelte | 2 +- .../date-range-picker-content.svelte | 2 +- src/uix/eidos/components/popover/popover.css | 10 +- .../time-picker/time-picker-content.svelte | 2 +- .../time-range-picker-content.svelte | 2 +- .../combobox/combobox-provider.svelte.ts | 4 + .../popover/components/popover-content.svelte | 2 +- 9 files changed, 67 insertions(+), 1888 deletions(-) diff --git a/continue.md b/continue.md index 5801e4443..f7bbf7399 100644 --- a/continue.md +++ b/continue.md @@ -1,1906 +1,73 @@ -# Continue +# Continue tomorrow -Fecha de corte: 2026-05-19. Rama: `active-uix`. +Fecha de corte: 2026-05-22. Rama: `active-uix`. -## Guia vigente: migracion de componentes Soma -> Eidos +## Resumen del día -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. +Auditoría + mejoras a 6 componentes form (select, field, editable, number-field, search-field, combobox). +Commits clave: `ffdb4804` → `df948aa3`. Todos los componentes pasaron `svelte-check` 0/0 al cerrar. -### Flujo obligatorio por componente +## Pendiente / en curso -1. Leer Air en la rama anterior: - `git show morfo-runtime: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. +### 1. Normalización del width del popover (task #105, prioridad alta) -### Fronteras de capa +**Hecho:** +- `src/uix/eidos/components/popover/popover.css` emite `min-inline-size: var(--floating-anchor-width, auto)` como default → popover ≥ anchor para TODOS los popover-based. +- `sideOffset` unificado a 6 en soma popover-content y en los 5 pickers (date / date-range / time / time-range / color). -- 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. +**Pendiente:** los pickers anclan al chevron trigger (~14px), no al input wrapper (~448px). Por eso `--floating-anchor-width` resuelve a 14px y el `min-inline-size` no actúa visible. -### API Eidos canonica +**Patrón verificado vs Ark / Radix / shadcn / React Aria:** +- Select / Combobox → anchor = trigger button (ya correcto). +- Pickers compuestos (date/time/color + range) → anchor = **input wrapper** (falta). -- 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. -- Dentro de wrappers Eidos, las partes de Soma se importan como namespace con - el nombre real del componente: `import * as Collapsible from - '$soma/components/collapsible'`, y se usan como - ``, ``, etc. -- Prohibido en wrappers Eidos: `Parts`, `Base`, `SomaXxxProvider`, tags - sueltas ``/``/`` y cualquier alias `Root`. +**Implementación (Approach A, soma-side):** +- En cada picker provider (`src/uix/soma/components/{date,time,color}-picker/*-provider.svelte.ts`), exponer `inputRef`. +- El `*-input.svelte` wrapper registra `provider.inputRef.current = el` en mount. +- Donde el popover hace `FloatingContent.use({ customAnchor: ... })`, usar `inputRef.current` si existe, sino `triggerRef.current`. -### Naming y contratos +**Verificación:** abrir popover, comprobar que `getComputedStyle(wrapper).getPropertyValue('--floating-anchor-width')` == ancho del input wrapper, no del chevron. -- 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. +### 2. Combobox multi mode con tags inline -### CSS y tokens +Decisión del usuario: patrón Material UI Autocomplete — items seleccionados como pills DENTRO del input, caret al final. -- 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. +**Pendiente:** +- Añadir `Combobox.SelectedTags` part al morfo (kebab `selected-tags`, archetype `group`, optional). +- Soma: `ComboboxSelectedTagsProvider` que renderiza un pill por value con X para eliminar. +- Eidos: wrapper + CSS — tags + `` en flex container con `flex-wrap`. +- Backspace en input vacío ya elimina último tag (commit C4 del audit, ya en main). +- Demo: ejemplo en multi mode. -### Decisions fijadas durante esta tanda +**Notas técnicas:** +- El `$effect` actual del provider sync inputValue al label SOLO en single mode. Para multi NO se debe tocar inputValue (filter activo). +- Mi patch anterior de "stamp labels on close" en multi fue **revertido**. El sync queda en cargo de SelectedTags. -- `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. +### 3. SearchField como Combobox-like -### Verificacion minima antes de cerrar un componente +Decisión del usuario: SearchField debe tener layer flotante con results clickables. -- 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 ...`. +**Opciones:** +- **A** (elegida): añadir `SearchField.Results` part con popover propio (items clickables emiten `commit-select`). +- B (descartada): documentar y reusar Combobox. -### Estado actual de la tanda Eidos +Pendiente. -- Ultimo componente corregido/verificado: `FileUpload` demo. -- Correccion/demo 2026-05-19: - - Anadio ruta `web/routes/uix/components/file-upload/+page.svelte` siguiendo - `web/routes/uix/lib/DEMO_AUTHORING_GUIDE.md`. - - La demo incluye tabs `Live`, `API`, `Morfo`, `Sema`, `Recipe`, `A11y`, - traza por `MutationObserver`, botones Play de Sema y pasos - `data-perm-step` para `remove -> clear`. - - No se instrumenta el boton que abre el picker nativo porque bloquearia las - pruebas automaticas; el evento `trigger-picker` queda cubierto en el tab - Sema mediante Play. - - `web/routes/uix/+layout@.svelte` registra `fileUploadSema` y anade la - entrada de navegacion `File upload`. - - Bug real corregido en `Eidos FileUpload`: el root ahora declara - `files = $bindable([])` y reenvia `bind:files` a Soma. - - Bug real corregido en `Eidos FileUpload.Item`: el snippet argumentado ya no - usa `children` como nombre local; usa `bodyContent`. - - Bug real corregido en `Eidos FileUpload.ItemName` e `ItemSize`: si el - consumidor no pasa children, el wrapper ya no inyecta un snippet vacio que - anulaba el fallback de Soma (`file.name` y `formatBytes(file.size)`). - - Bug de contrato corregido en `Morfo FileUpload`: `ItemPreview.data-type` - pasa a ser opcional porque la prop `type` de Soma es un filtro opcional y - no se emite cuando no se usa. -- Validado despues de `FileUpload` demo: - - `npm run check` -> 0 errores, 0 warnings. - - `SMOKE_SCOPE=/uix/components/file-upload npm run smoke` -> ruta OK. - - `npm run morfo:check` -> `file-upload` PASS; 29 rutas validadas, - 38/67 morfos sin demo. - - `npm run perm:check` -> `file-upload` PASS; baseline elimina y limpia; - disabled queda pasivo; 17 demos instrumentadas OK. - - `node --import tsx/esm scripts/eidos-lint-all.ts` -> `file-upload` - invalid 0. Persisten solo hotspots conocidos de `progress`/`meter` por - `data-shape='circular'`. - - Navegador integrado: ruta carga sin errores de consola, monta root, - dropzone, trigger, list, dos items, dos progress bars, seis tabs y Sema - muestra cinco botones Play. -- Todos los componentes presentes hoy en `src/uix/eidos/components` salvo - `svg` tienen ruta demo en `web/routes/uix/components`. -- Ultimo componente corregido/verificado antes de esta demo: `TagsInput` demo. -- Correccion/demo 2026-05-19: - - Anadio ruta `web/routes/uix/components/tags-input/+page.svelte` siguiendo - `web/routes/uix/lib/DEMO_AUTHORING_GUIDE.md`. - - La demo incluye tabs `Live`, `API`, `Morfo`, `Sema`, `Recipe`, `A11y`, - traza por `MutationObserver`, botones Play de Sema y pasos - `data-perm-step` para `type delimiter -> remove -> clear`. - - `web/routes/uix/+layout@.svelte` registra `tagsInputSema` y anade la - entrada de navegacion `Tags input`. - - Bug real corregido en `Eidos TagsInput`: el root ahora declara - `value = $bindable([])` e `inputValue = $bindable('')`, reenviando ambos - estados bindables a Soma. -- Validado despues de `TagsInput` demo: - - `npm run check` -> 0 errores, 0 warnings. - - `SMOKE_SCOPE=/uix/components/tags-input npm run smoke` -> ruta OK. - - `npm run morfo:check` -> `tags-input` PASS; 28 rutas validadas, - 39/67 morfos sin demo. - - `npm run perm:check` -> `tags-input` PASS; baseline e invalid escriben, - eliminan y limpian; disabled/readonly quedan pasivos; 16 demos - instrumentadas OK. - - `node --import tsx/esm scripts/eidos-lint-all.ts` -> `tags-input` invalid 0. - - Navegador integrado: tags visibles, input presente, Sema con cuatro eventos - y sin errores de consola. -- Ultimo componente corregido/verificado antes de esta demo: `Editable` demo. -- Correccion/demo 2026-05-19: -- Correccion/demo 2026-05-19: - - Anadio ruta `web/routes/uix/components/editable/+page.svelte` siguiendo - `web/routes/uix/lib/DEMO_AUTHORING_GUIDE.md`. - - La demo incluye tabs `Live`, `API`, `Morfo`, `Sema`, `Recipe`, `A11y`, - traza por `MutationObserver`, botones Play de Sema y pasos - `data-perm-step` para `Edit -> type draft -> Save`. - - `web/routes/uix/+layout@.svelte` registra `editableSema` y anade la - entrada de navegacion `Editable`. - - Bug real corregido en `Eidos Editable`: el root ahora declara - `value = $bindable('')` y `editing = $bindable(false)`, reenviando ambos - estados bindables a Soma. - - Bug real corregido en `Eidos Editable.Preview`: si el consumidor no pasa - children, el wrapper ya no inyecta un snippet vacio que anulaba el fallback - `state.displayValue` de Soma. - - Correccion posterior de arquitectura: `Editable` no debe parecer un - `Field/Input` permanente. La baseline real de la rama anterior esta en - `src/uix/terra/editable` (no en Air components) y plantea contenido inline - editable. - - `Morfo/Soma Editable` anade `Control` para agrupar acciones. Los triggers - ya no fuerzan `hidden`; Soma expone `data-editing`, `data-disabled` y - `data-readonly`, y Eidos decide visualmente si muestra `Edit` o - `Save/Cancel`. - - La receta Eidos de `Editable` vuelve a preview inline: caja sutil solo en - hover/focus o al entrar en edicion. Si se quiere una caja permanente debe - usarse `Field`/`Input`. -- Validado despues de `Editable` demo: - - `npm run check` -> 0 errores, 0 warnings. - - `SMOKE_SCOPE=/uix/components/editable npm run smoke` -> ruta OK. - - `npm run morfo:check` -> `editable` PASS; 27 rutas validadas, - 40/67 morfos sin demo. - - `npm run perm:check` -> `editable` PASS; baseline edita, escribe y guarda; - disabled/readonly quedan pasivos; 15 demos instrumentadas OK. - - `node --import tsx/esm scripts/eidos-lint-all.ts` -> `editable` invalid 0. - - Navegador integrado: preview muestra el valor inicial, input oculto en - preview y sin errores de consola. -- Ultimo componente corregido/verificado antes de esta demo: `Stepper` demo. -- Correccion/demo 2026-05-19: -- Correccion/demo 2026-05-19: - - Anadio ruta `web/routes/uix/components/stepper/+page.svelte` siguiendo - `web/routes/uix/lib/DEMO_AUTHORING_GUIDE.md`. - - La demo incluye tabs `Live`, `API`, `Morfo`, `Sema`, `Recipe`, `A11y`, - traza por `MutationObserver`, botones Play de Sema y pasos - `data-perm-step` que avanzan hasta `commit-complete`. - - `web/routes/uix/+layout@.svelte` registra `stepperSema` y anade la entrada - de navegacion `Stepper`. - - Bug real corregido en `Eidos Stepper`: el root ahora declara - `value = $bindable(0)` y reenvia `bind:value` a Soma, tal como prometia - su API documentada. - - Bug real corregido en `Eidos Stepper.Item` y `Stepper.Indicator`: los - snippets argumentados `{ state }` ya no usan `children` como nombre local, - evitando la recursividad/`undefined is not a function` vista en SSR. -- Validado despues de `Stepper` demo: - - `npm run check` -> 0 errores, 0 warnings. - - `SMOKE_SCOPE=/uix/components/stepper npm run smoke` -> ruta OK. - - `npm run morfo:check` -> `stepper` PASS; 26 rutas validadas, - 41/67 morfos sin demo. - - `npm run perm:check` -> `stepper` PASS; baseline, RTL y vertical avanzan - los tres pasos hasta completar; 14 demos instrumentadas OK. - - `node --import tsx/esm scripts/eidos-lint-all.ts` -> `stepper` invalid 0. - - Navegador integrado: ruta carga sin errores de consola, seis tabs y Sema - muestra dos eventos con Play. -- Ultimo componente corregido/verificado antes de esta demo: `TagGroup` demo. -- Correccion/demo 2026-05-19: - - Anadio ruta `web/routes/uix/components/tag-group/+page.svelte` siguiendo - `web/routes/uix/lib/DEMO_AUTHORING_GUIDE.md`. - - La demo incluye tabs `Live`, `API`, `Morfo`, `Sema`, `Recipe`, `A11y`, - traza por `MutationObserver`, botones Play de Sema y pasos - `data-perm-step` para seleccionar y eliminar un tag. - - `web/routes/uix/+layout@.svelte` registra `tagGroupSema` en el shell de - docs y anade la entrada de navegacion `Tag group`. - - Bug real corregido en `Eidos TagGroup`: el root ahora declara - `value = $bindable([])` y reenvia `bind:value` a Soma, tal como prometia - su API documentada. - - Bug real corregido en `Eidos TagGroup.Item`: el snippet argumentado ya no - usa `children` como nombre local; usa `bodyContent` para evitar la - recursividad/`undefined is not a function` vista en SSR. -- Validado despues de `TagGroup` demo: - - `npm run check` -> 0 errores, 0 warnings. - - `SMOKE_SCOPE=/uix/components/tag-group npm run smoke` -> ruta OK. - - `npm run morfo:check` -> `tag-group` PASS; 25 rutas validadas, - 42/67 morfos sin demo. - - `npm run perm:check` -> `tag-group` PASS; baseline selecciona y elimina, - variante disabled queda pasiva; 13 demos instrumentadas OK. - - `node --import tsx/esm scripts/eidos-lint-all.ts` -> `tag-group` invalid 0. - - Navegador integrado: ruta carga sin errores de consola, seis tabs, Sema - muestra cuatro eventos y Recipe lista selectors morfo/eidos esperados. -- Ultimo componente corregido/verificado antes de esta demo: `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. -- Siguiente componente recomendado: iniciar la tabla previa de uno de los - componentes grandes aun no migrados a Eidos (`select`, `combobox`, - `calendar`, `date-field`, `date-picker`, `date-range-picker`, `time-field`, - `time-picker`). Antes de tocar codigo, comparar Air si existe, Soma/Morfo y - referencias externas, y decidir gaps reales. +### 4. Combobox: revisar effect de single-mode label sync al mezclar con tags inline -### Archivos temporales +El `$effect` en `combobox-provider.svelte.ts` (líneas ~145) sync inputValue al label en single mode. Al añadir SelectedTags (item 2), verificar que no haya loops en multi. -- No commitear logs temporales: - `.codex-vite-*.log`, `.codex-vite-*.err.log`, - `.codex-taggroup-dev.*.log`, `debug.log`, `md`. +## Estado actual del working tree (sin commit aún) -## Corte para continuar manana +- `src/uix/eidos/components/popover/popover.css` — `--_popover-min-width` añadido. +- `src/uix/eidos/components/{date,date-range,time,time-range,color}-picker/*-content.svelte` — sideOffset = 6. +- `src/uix/soma/components/popover/components/popover-content.svelte` — default sideOffset = 6. +- `src/uix/soma/components/combobox/combobox-provider.svelte.ts` — revertido patch multi-on-close + comentario "Multi mode renders selections as inline tags via a separate Combobox.SelectedTags part" como recordatorio. -- 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 - (`morfo-runtime: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 (`morfo-runtime`), 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 `morfo-runtime`, 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 `morfo-runtime`; 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. +## Comandos útiles -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 `