uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
<script lang="ts">
|
|
|
|
|
|
import { onDestroy } from 'svelte';
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
import { createActiveUix, setActiveUix } from '$active-uix';
|
uix(skip-link): el tier decidió en F2.1 que los shells lo poseen, y no había nada que poseer
Canon nuevo, eidos-native (1 parte, 0 eventos): la pieza de WCAG 2.4.1 que
`site-header` difirió a los shells hace tres semanas y que `app-shell` (F3.1)
necesita para existir. Nace de su fase 0, decisión Q4 firmada.
Lo que decide, y por qué:
- **Las palabras son suyas.** «Ir al contenido principal» no es copy: es el
nombre de un contrato. La regla del tier (B-7 / D-BLK.5) apunta a esta puerta
— si un string parece inevitable, lo posee el componente canónico vía
`texts:` + langs. Por eso el prop es `region`, no `label`, y el vocabulario
es la lista de landmarks de la APG. Cada clave es una FRASE ENTERA: en
castellano el artículo se contrae con la región, así que un «Ir a» +
sustantivo saldría mal en media catálogo.
- **`region` no se estampa**: elige un texto y ninguna capa lo lee. Declararlo
repetiría el error que `Affix` corrigió el 2026-08-15.
- **No compone `Link`**: `Link` es tinta en línea y todo su API describe texto
dentro de un párrafo. Esto es cromo que aparece de la nada, con su geometría
y su suelo; envolverlo sería pisar todos sus props y aun así dejarlos en la
API pública. Los componentes del canon renderizan sus nativos; son los blocks
los que no pueden.
- **El handler mueve el FOCO**, que es lo que el fragmento no hace: un `href`
desplaza la vista y deja el foco en el enlace, y el siguiente Tab vuelve al
cromo que el lector pidió saltar. El destino se hace enfocable sólo mientras
dura el salto. El `href` se queda: es el camino sin JS.
- **Peldaño propio de z (950), el más alto de la escalera estática.** No puede
compartir el de `affix`: un empate lo rompe el orden del DOM y este enlace es
por definición el primer elemento del documento, así que perdería contra
cualquier aviso fijado debajo — el fallo medido en A-95.
Medido con Playwright (el pane del navegador no tiene el foco del SO, así que
`:focus` nunca casa ahí): en reposo 1x1 con clip-path; enfocado `position:
fixed`, z 950, píldora de 181x36 sobre `primary-solid` con tinta blanca y
anillo de foco, igual en claro y en oscuro; el salto deja el foco en
`#demo-main` y el `tabindex` temporal se limpia en el blur; en RTL la píldora
espeja al otro borde (x 338 -> 761) sin una segunda regla.
Y la demo se corrigió a sí misma: decía «Tab desde el botón de abajo» y la
medición enseñó que desde ahí el foco va hacia delante. El botón sube encima
del marco, y de paso salió la otra mitad de la lección — por tabulación NUNCA
se aterriza en el `main`, porque no tiene nada enfocable. Justo por eso el
destino necesita el `tabindex` prestado.
Guards: `component:audit --only skip-link` PASS · `morfo:check` PASS ·
`eidos-lint` 2 selectores morfo-backed, 0 inválidos · `docs:check` 0/0 ·
`svelte-check` 72, ninguno aquí.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
import { readActivePrefsSlot, standardPrefsDimensions } from '$prefs';
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
import { page } from '$app/state';
|
|
|
|
|
|
import { Soma } from '$soma/core/soma.svelte';
|
|
|
|
|
|
import { siumLangs } from '$sium/langs/langs';
|
fix(uix audit): apply Kim audit remediations across morfo/soma/eidos/recipe layers
Resolves 34 of 40 failures from src/uix/audit-uix-kimi-1.md. The remaining
6 are all Words component (excluded per project owner — actively developed
on a separate track) or unrelated infra (cookie-adapter-client jsdom timeout).
Key changes:
- soma/provider/context.ts: swallow Svelte's `lifecycle_outside_component`
in `context.set()` so unit tests can instantiate providers without a
component tree (unblocked 18 picker tests in one line).
- soma/textarea: route inline style writes through `dom.apply({attrs})`
instead of direct `el.style.X = Y` (contract regex compliance).
- soma/picker-shell: add minimal Provider component + morfo so the shared
handle utility passes the morfo/component contract.
- eidos/_layout: lift `shared.ts` to `eidos/lib/layout-helpers.ts` (the
`_layout` directory wasn't a real component) and update 8 layout
primitive imports.
- eidos/command: replace `onMount` with `$effect` (visual layer can't use
Svelte 4 lifecycle hooks per component-api contract); restructure
`Item.Icon`/`Item.Shortcut` namespace so contract regex matches.
- eidos/color-picker: extract hue spectrum hsl literals to sidecar
`color-picker-spectrum.css`; keep main recipe color-literal-free.
- eidos/lib/recipes/base.ts: declare 196 layout-primitive + chrome CSS
vars (box, flex, grid, float, group, banner, container, section,
separator, toggle-group, pin-input, scroll-area, splitter,
navigation-menu, dropdown-menu, context-menu, date-range-field,
time-range-field, plus per-component additions to date-field,
color-picker, button, select). 197 missing → 0 non-Words.
- morfo: add `data-dir` to carousel, `data-kind` to date-picker, add
StrengthMeterLabel sub-part to password-field, register picker-shell.
- 5 pickers (date/color/date-range/time/time-range): add `internals.ts`
indirection so `exports.ts` doesn't re-export `*provider.svelte.ts`
directly (contract D); rewrite virtual root types to plain object
literals (no `WithChild`/`PrimitiveDivAttributes`).
- 4 new READMEs (button, textarea, password-field, picker-shell)
following the search-field canonical template.
Test result: 2391/2397 passing (99.75%). Remaining 6 failures are all
Words-scope or jsdom infra timeout — explicitly out of scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
import { secsLangs } from '$libs/secs';
|
|
|
|
|
|
import { ActiveEidos } from '$uix/eidos';
|
|
|
|
|
|
import type { Gradient } from '$libs/gradient';
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
import { dialogSema } from '$uix/sema/components/dialog';
|
|
|
|
|
|
import { drawerSema } from '$uix/sema/components/drawer';
|
|
|
|
|
|
import { editableSema } from '$uix/sema/components/editable';
|
|
|
|
|
|
import { fileUploadSema } from '$uix/sema/components/file-upload';
|
|
|
|
|
|
import { floatPanelSema } from '$uix/sema/components/float-panel';
|
|
|
|
|
|
import { formSema } from '$uix/sema/components/form';
|
|
|
|
|
|
import { gradientBuilderSema } from '$uix/sema/components/gradient-builder';
|
|
|
|
|
|
import { gradientPickerSema } from '$uix/sema/components/gradient-picker';
|
feat(uix): cropper, image-picker, image-adjustments + reusable zoom-pan layer
Three new components across all 4 layers (morfo/soma/sema/eidos) + demos + langs + READMEs:
- ImageAdjustments — reusable image-filter sliders subcomponent.
- ImagePicker — composes file-upload + image + image-adjustments (fill modes, rotation).
- Cropper — Ark image-cropper anatomy: normalized 0-1 crop rect, rect + round shapes,
8 resize handles, fixedSize move-only (avatar), canvas Blob output (5-arg drawImage
for SVG safety), wheel/pan zoom, two-zone gesture (selection moves the area, the
background pans to set focus; img draggable=false guards native DnD per zag).
New reusable soma/layers/zoom-pan: cursor-centered zoom, clamped pan, transform +
viewport-frac-to-content-frac mapping. Image-viewer / diagram-pan / map can reuse it.
sema: add 'zoom' verb to the handle family (direct manipulation, documented extension).
slider: track visible at zero (gray, not white), thumb lifts on drag.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
import { imageAdjustmentsSema } from '$uix/sema/components/image-adjustments';
|
|
|
|
|
|
import { imagePickerSema } from '$uix/sema/components/image-picker';
|
|
|
|
|
|
import { knobSema } from '$uix/sema/components/knob';
|
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
import { maskFieldSema } from '$uix/sema/components/mask-field';
|
feat(web): /uix/docs/sound — la pagina que explica el canal del sonido
Primera de las ocho del plan, en castellano y DERIVADA del codigo vivo: las
tablas leen SEMA_MAP y el resolver real, nunca una transcripcion. Una pagina
que copiara el catalogo envejeceria al primer gain que alguien tocara — que
es exactamente la deriva que este subsistema se reconstruyo para quitar.
SIETE SECCIONES, ordenadas para responder «no se como funciona esto despues
de tantas correcciones»: (1) en una frase — un componente dice una palabra;
(2) el catalogo, los 16 nombres sonando al pulsarlos, agrupados por la FORMA
de cada entrada y no por una lista a mano; (3) el orden con los numeros de
verdad, tabla viva de familia x nombre x intent capa a capa; (4) que puede
llevar un .wav; (5) que no te toca escribir; (6) las correcciones y por que
—la historia ES la explicacion, ocultarla es lo que dejo al autor sin saber
como funciona—; (7) el tema.
EL BLOQUEANTE, PRIMERO. El interruptor Sound del topbar no silenciaba NADA:
el layout no pasaba `preferences`, asi que los canales se quedaban en 'full'
y el switch solo movia el intent de prefs (que gobierna el lado visual).
Publicar paginas que suenan con un mute que miente es peor que no
publicarlas. Ahora pasa un objeto VIVO — ambos canales leen
`preferences.{sound,haptic}` por emision, asi que mutarlo silencia al
instante sin re-registrar nada. Cierra §3.3 de la cola.
MEDIDO EN NAVEGADOR, no supuesto: 7 secciones, 17 chips, la rejilla y los
chips con su CSS aplicado, y la tabla de capas moviendose de verdad — con
`fulfill` el gain pasa de 0.05 a 0.1, el pitch de 700 a 1000 y el contour de
flat a ascending. Es decir: la pagina DEMUESTRA en vivo que el intent
sobrevive al nombre, que es la tesis del rediseño entero.
⚠️ NO he podido hacer captura: el panel del navegador no esta visible en esta
sesion. Verificado por arbol de accesibilidad, consola y estilos computados
— pero no lo he MIRADO. Queda pendiente tu ojo.
⚠️ Corregido de paso un fallo propio: la sonda del resolver usaba `document`
en el render de servidor y la pagina daba 500. Ahora cae a un stub en SSR.
El nav estrena seccion «El sistema perceptual». Quedan las otras siete
paginas, y los seis enlaces muertos que Foundations ya declaraba
(/uix/architecture, /uix/tokens, /uix/morfo, /uix/sema, /uix/eidos,
/uix/getting-started) siguen sin pagina.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
import type { SemaPreferences } from '$uix/sema';
|
|
|
|
|
|
import { navTreeSema } from '$uix/sema/components/nav-tree';
|
uix(sidebar): F1.7 eidos + demo · raíl, modo icono y submenú flotante
Segunda tanda del sidebar: la capa visual, la demo canónica de 9 pestañas y
la verificación en navegador real. Con esto el componente está vivo; queda el
review adversarial.
Eidos
- Recipe con los dos ejes: off-canvas por `transform` (trabajo de compositor,
el inset no reflowea) y `width` animado SOLO en modo icono, donde el inset
sí tiene que seguir al raíl. El raíl NO lleva `will-change` (una franja
fina tiembla a DPR fraccionario — memoria NavMenu).
- Anchos como TOKENS: `--sidebar-width` 16rem · `--sidebar-width-icon` 3rem ·
`--sidebar-width-mobile` 18rem, más raíl, gaps, altura de fila y colores.
- Tres composiciones que la capa headless no puede hacer: `Drawer` para la
presentación móvil, `Tooltip` para nombrar filas en modo icono y `Badge`
para los contadores. El trigger compone `Button` por el patrón `child`.
- Convención de icono documentada: sobrevive el PRIMER elemento de la fila.
Arreglos que destapó el navegador
- **`mobile` en SSR**: sin viewport, `isAtLeast` responde «por debajo de todo»
→ el HTML salía con `data-mobile` y la hidratación NO lo corrige (no diffea
atributos y el valor ya no cambia). Viewport desconocido = ESCRITORIO.
- **Estado móvil propio** (arranca cerrado): en escritorio `open` es
preferencia de layout persistida; en móvil sería un overlay tapando la
página, que jamás puede ser el estado inicial. `onOpenChange` solo se
dispara con el estado de escritorio.
- **`open` controlado/no controlado**: se adopta el patrón de la casa
(`$bindable` con default, el provider escribe siempre) — mi versión previa
con estado interno no propagaba `bind:open`.
- **El submenú flotante no se posicionaba**: `FloatingAnchor` resuelve su
provider por CONTEXTO y el eidos envuelve la fila en un `Tooltip`, que
publica el suyo → el ancla caía en el tooltip y el submenú se quedaba en
`translate(0,-200%)`. Ahora el ancla se cablea directamente al provider
flotante del item. Además el `<ul>` ya no se posiciona a sí mismo (colapsaba
el wrapper a tamaño cero) y toma elevación del plano `overlay` por
`data-depth`, como cualquier panel flotante del ecosistema.
- **Tooltip vs submenú**: una fila con submenú no lleva tooltip en modo icono
(dos superficies sobre el mismo ancla se pisan) — nuevo `useSidebarItemOr()`.
- El item envuelve (`flex-wrap`) para que el submenú caiga en su propia línea
en vez de ponerse al lado de la fila.
Demo: shell de app real (grupos, badge, acción, submenú, fila deshabilitada),
controles vivos de los tres props y las 9 pestañas del canon.
Verificado en navegador real (Playwright): dos landmarks nombrados en el
idioma activo · `aria-current="page"` en la fila activa · cada menú etiquetado
por SU grupo · raíl enfocable que alterna con Enter · colapso 256→48px ·
off-canvas por transform · submenú flotante anclado a su fila y Escape que
devuelve el foco · sema `emerge-expand/collapse` estampando en `panel` ·
móvil 600px = Drawer cerrado con el landmark dentro · RTL espeja el shell
entero · oscuro · 0 errores de consola.
Gates: `component:audit --only sidebar` PASS 0E/0W · eidos-lint 45
morfo-backed / 0 invalid / 0 class-hooks · `svelte-check` 0 errores propios ·
`vitest src/uix/eidos` 353/353 · `contracts.test` solo con fallos ajenos
(menubar, radio-group y las claves camelCase de aura).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
|
|
|
|
import { sidebarSema } from '$uix/sema/components/sidebar';
|
|
|
|
|
|
import { numberFieldSema } from '$uix/sema/components/number-field';
|
|
|
|
|
|
import { paginationSema } from '$uix/sema/components/pagination';
|
feat: build PasswordField (full stack)
Dedicated password input component — matches the Ark UI / Chakra v3 /
Mantine / PrimeReact / Ant Design pattern of treating PasswordField as
its own first-class component (not a TextField variant). Includes one
feature that NO surveyed library ships: a CapsLock indicator driven by
KeyboardEvent.getModifierState('CapsLock').
Anatomy (5 parts, 4 events):
- Provider (root, value + visibility state)
- Input (the textbox; type flips between password / text)
- VisibilityTrigger (eye toggle button; aria-pressed + aria-controls)
- CapsLockIndicator (status live region; data-active toggles via soma
listening to keydown/keyup on Input)
- StrengthMeter (composable; consumer provides 0-4 score; UIX bundles
no zxcvbn-style algorithm — dependency-free by design)
Events:
- commit-input (every keystroke)
- commit-submit (Enter)
- shift-toggle-visibility (eye click)
- shift-caps-state (Caps Lock state flips while focused)
Key behaviors:
- `purpose: 'current' | 'new'` drives native autocomplete attribute
('current-password' for login / 'new-password' for registration).
- Field integration: disabled/readonly/required/invalid OR-merged.
- VisibilityTrigger gets tabindex=-1 so it doesn't break the form's
natural tab order — mouse / programmatic focus only, matching Ark UI.
- Caps Lock indicator is aria-live="polite" — announces on activation
while focused, silent when inactive.
- Letter-spacing increased on the input for legibility while masked.
Reference library survey performed (10 libraries) before implementation —
all consensus around dedicated component, all have built-in toggle,
ZERO have caps lock warning. Documented in the demo's API tab with a
feature comparison table.
Demo follows DEMO_AUTHORING_GUIDE canonical 6-tab depth: Live (controls
grouped by layer + code snippets at the bottom), API (props + reference
comparison), Morfo (field/value + Parts + Events tables), Sema (events
table with ▶ play), Recipe (selectors with morfo/eidos owner tags), A11y
(concern/contract table).
Also includes external work in words/ from a separate session
(unrelated to PasswordField) — not touching Words is the standing rule,
but the diff was already in the working tree.
svelte-check: 0 errors in PasswordField files (Words errors are tracked
by its own development branch).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
import { passwordFieldSema } from '$uix/sema/components/password-field';
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
import { popoverSema } from '$uix/sema/components/popover';
|
|
|
|
|
|
import { radioGroupSema } from '$uix/sema/components/radio-group';
|
|
|
|
|
|
import { ratingGroupSema } from '$uix/sema/components/rating-group';
|
|
|
|
|
|
import { searchFieldSema } from '$uix/sema/components/search-field';
|
|
|
|
|
|
import { selectSema } from '$uix/sema/components/select';
|
|
|
|
|
|
import { sliderSema } from '$uix/sema/components/slider';
|
|
|
|
|
|
import { splitterSema } from '$uix/sema/components/splitter';
|
|
|
|
|
|
import { stepperSema } from '$uix/sema/components/stepper';
|
|
|
|
|
|
import { tabsSema } from '$uix/sema/components/tabs';
|
|
|
|
|
|
import { tagGroupSema } from '$uix/sema/components/tag-group';
|
|
|
|
|
|
import { tagsInputSema } from '$uix/sema/components/tags-input';
|
Build TextArea component + fix locale propagation bug across soma providers
Major work in this session:
1. NumberField demo refactored to expose Intl.NumberFormatOptions live
- 9 canonical presets (currency/unit/percent/compact/plain)
- Locale/currency/unit lists derived from $libs/langs, $libs/currency,
$format/units (no more hardcoded arrays)
- Default locale from uix.format.numbers.getLocale()
2. Bug fix: locale prop ignored when soma.format service is active
- soma/NumberField: 5 bypass points via new `hasOwnLocale` getter
(decimalSeparator, groupSeparator, formatValue, formatEditValue, parseValue)
- soma/Calendar: dateOrder respects locale prop (matches DateField precedence)
- soma/RangeCalendar: same dateOrder fix
- When the consumer passes `locale` prop, soma.nums/dates is bypassed
and Intl is used directly with the prop locale
3. TextArea — new component, full 4-layer stack
- morfo: 3 parts (provider/input/count), 3 events
(commit-input/commit-submit/shift-count-overflow), APG textbox
- soma: TextAreaProvider with autosize (scrollHeight measurement),
submitOn modes (false|enter|shift+enter|mod+enter), maxLength soft clamp,
Field integration (OR-merge), counter overflow tracking
- eidos: 5 sizes × 3 variants × 8 colors, autosize suppresses CSS resize
- sema: commit-submit + shift-count-overflow perceptual cues
- demo: canonical 6-tab depth, all props exposed as live controls
- PrimitiveTextareaAttributes added to soma/types/html.ts
4. Also includes accumulated unreleased work from prior sessions:
- Eidos ports: announce, clipboard, drag-drop, feed, table,
tree-grid, tree-view, virtual-grid, virtual-list, words
- Their canonical demos under web/routes/uix/components/
- Semantic events defined for utility morfos
(announce/clipboard/virtual-list/virtual-grid)
- Tree indent guides fixed (TreeView ::before, TreeGrid stacked gradients)
- Sidebar reorganized with Data + Utilities groups
- Eidos words content layer and primitives
Reference library survey performed before TextArea API design (Mantine,
Radix Themes, Chakra v3, React Aria, Ark UI, Bits UI, shadcn, MUI, Prime).
Decision documented: no separate CurrencyField/UnitField — single
NumberField with formatOptions is the canon (matches all surveyed
headless libs except PrimeReact's mode enum, which still uses 1 component).
svelte-check: 0 errors in modified/created files.
See CONTINUE.md for full session notes and next steps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
import { textareaSema } from '$uix/sema/components/textarea';
|
fix(demo): cargar mediaPlayerSema en la composicion de la app de demos
Reporte del usuario en vivo: el transporte del player seguia sonando en la
demo pese al pack silencioso (D-AP2.7 v2). Causa raiz: los packs de sema son
OPT-IN por app (defineEngineSemantic({ components: [...] }), tree-shaking) y
`mediaPlayerSema` NUNCA estuvo en la lista del layout de demos — ni el
silencio nuevo, ni el tap sutil v1, ni el fix del selector (H-1) llegaron a
aplicarse alli jamas: lo que sonaba era el default crudo de familia (los dos
earcons por clic que H-10 midio).
Dos lineas: import + entrada en `components` de
web/routes/uix/+layout@.svelte. Verificado: check 74 = baseline, 0 propios.
Observacion reportada (fuera de alcance, no tocada): otros packs del indice
tampoco estan en esa lista (dropdown-menu, context-menu, menubar, switch,
toggle, tooltip, tree-view...) — la misma clase de hueco para sus demos.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
import { mediaPlayerSema } from '$uix/sema/components/media-player';
|
|
|
|
|
|
import { timelineSema } from '$uix/sema/components/timeline';
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
import { toastSema } from '$uix/sema/components/toast';
|
|
|
|
|
|
import { toolbarSema } from '$uix/sema/components/toolbar';
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
import { accordionSema } from '$uix/sema/components/accordion';
|
uix(chat): bloque de mensajería chat-* v1 (log · message · composer · typing)
Familia chat-* completa, componiendo el ecosistema (Feed + VirtualList
anclado, Textarea, FileUpload, Avatar, Popover, Icon). Transporte-agnóstica:
el modelo de datos es de la app, los componentes reciben props + snippets.
- chat-log: Feed + VirtualList(anchor:'end') en triple registro; day
separators, unread divider, filas de sistema (Feed.Article), pill "N
nuevos" + notifyIncoming (announce a live-region), to-latest distance-aware.
- chat-message: raíz Feed.Article; runs ($libs/chat), ticks de entrega,
reply embebido, reacciones (chip pressed + tapback panel), acciones hover.
- chat-composer: Textarea (doble registro) + FileUpload; teclado
Enter/Shift+Enter/Esc/ArrowUp; typing saliente debounced (uix.timers);
barra reply/edit.
- chat-typing: sustain-streaming (visual-only, loop del sistema);
aggregateTypers 1/2/3+ con plantillas plurales.
- $libs/chat: groupIntoRuns (ventana 8 min estilo Discord) + aggregateTypers.
- VirtualList: extensión anchor:'end' (followOutput, prepend por clave
estable, sticky monótono, onAtEndChange) — sin scroller paralelo.
Diseño de referencia (WhatsApp/Telegram, pase lado-a-lado): burbujas suaves
(out = primary-element + texto normal; in = surface card + sombra) sobre
lienzo muted del log, meta dentro de la burbuja, tapback bar horizontal,
iconos lucide del ecosistema (nada de SVG a mano). Contrato de tamaños:
size="md" = 16px canónico.
Fixes de framework (afectaban más allá de chat):
- EngineLangs.t() ahora pela el prefijo '#?': los idlangrefs imperativos
(pagination/file-upload/chronos/chat) caían al fallback EN en locale es.
Fix parseLangRef ?? parsePathFallback + test de regresión (132/132).
- virtual-list commit-set-resize con channels:[]: es bookkeeping de
ResizeObserver, no gesto → dejaba de emitir beep/vibrate pre-interacción
(interventions de navigator.vibrate / AudioContext).
Escaparate messenger en /uix/demos/chat (bot de eco con uix.timers;
variante $connection señalada). Gaps v2 registrados en next-features §7.
Pase: component:audit 4×PASS · eidos-lint invalid=0 · recipe-contract
29/29 · Playwright escaparate PASS end-to-end · consola limpia ·
npm run check en baseline (76, cero errores nuevos).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
import { chatComposerSema } from '$uix/sema/components/chat-composer';
|
|
|
|
|
|
import { chatLogSema } from '$uix/sema/components/chat-log';
|
|
|
|
|
|
import { chatMessageSema } from '$uix/sema/components/chat-message';
|
|
|
|
|
|
import { checkboxSema } from '$uix/sema/components/checkbox';
|
|
|
|
|
|
import { calendarSema } from '$uix/sema/components/calendar';
|
|
|
|
|
|
import { colorPickerSema } from '$uix/sema/components/color-picker';
|
|
|
|
|
|
import { comboboxSema } from '$uix/sema/components/combobox';
|
feat(uix): cropper, image-picker, image-adjustments + reusable zoom-pan layer
Three new components across all 4 layers (morfo/soma/sema/eidos) + demos + langs + READMEs:
- ImageAdjustments — reusable image-filter sliders subcomponent.
- ImagePicker — composes file-upload + image + image-adjustments (fill modes, rotation).
- Cropper — Ark image-cropper anatomy: normalized 0-1 crop rect, rect + round shapes,
8 resize handles, fixedSize move-only (avatar), canvas Blob output (5-arg drawImage
for SVG safety), wheel/pan zoom, two-zone gesture (selection moves the area, the
background pans to set focus; img draggable=false guards native DnD per zag).
New reusable soma/layers/zoom-pan: cursor-centered zoom, clamped pan, transform +
viewport-frac-to-content-frac mapping. Image-viewer / diagram-pan / map can reuse it.
sema: add 'zoom' verb to the handle family (direct manipulation, documented extension).
slider: track visible at zero (gray, not white), thumb lifts on drag.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
import { cropperSema } from '$uix/sema/components/cropper';
|
|
|
|
|
|
import { cssFieldSema } from '$uix/sema/components/css-field';
|
|
|
|
|
|
import { dateFieldSema } from '$uix/sema/components/date-field';
|
fix(demo): cargar los 25 packs sema que faltaban en la composicion de demos
Cierra la CLASE del hueco que destapo el reporte del media-player
(63f20f345): los packs sema son OPT-IN por app y la lista del layout de
demos habia crecido por goteo — 25 de los 68 packs del indice no estaban, y
sus demos sonaban con defaults crudos de familia en vez de sus firmas
autoradas: chronos, clipboard, collapsible, color-field, context-menu,
drag-drop, dropdown-menu, listbox, menubar, menu-dial, month-grid,
navigation-menu, onion-menu, path-trace, proof-of-human, range-calendar,
rotate-align, switch, time-field, toggle, toggle-group, tooltip, tree-view,
tree-grid, year-grid.
Entre ellos, los packs silenciosos de D.5/D.6 (switch/toggle/toggle-group,
tooltip, menus/arboles): esas demos ahora CALLAN o suenan sutil como su
doctrina prescribe — el cambio audible es hacia lo autorado.
Bloque contiguo y comentado (sin reordenar lo existente). Verificado:
check 74 = baseline, 0 propios.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
// The rest of the pack index, loaded 2026-07-31. The media-player report
|
|
|
|
|
|
// (63f20f345) exposed the class: packs are OPT-IN per app, and every demo
|
|
|
|
|
|
// whose pack was missing here played raw family defaults instead of its
|
|
|
|
|
|
// authored signature.
|
|
|
|
|
|
import { chronosSema } from '$uix/sema/components/chronos';
|
|
|
|
|
|
import { clipboardSema } from '$uix/sema/components/clipboard';
|
|
|
|
|
|
import { collapsibleSema } from '$uix/sema/components/collapsible';
|
|
|
|
|
|
import { colorFieldSema } from '$uix/sema/components/color-field';
|
|
|
|
|
|
import { contextMenuSema } from '$uix/sema/components/context-menu';
|
|
|
|
|
|
import { dragDropSema } from '$uix/sema/components/drag-drop';
|
|
|
|
|
|
import { dropdownMenuSema } from '$uix/sema/components/dropdown-menu';
|
|
|
|
|
|
import { listboxSema } from '$uix/sema/components/listbox';
|
|
|
|
|
|
import { menubarSema } from '$uix/sema/components/menubar';
|
|
|
|
|
|
import { menuDialSema } from '$uix/sema/components/menu-dial';
|
|
|
|
|
|
import { monthGridSema } from '$uix/sema/components/month-grid';
|
|
|
|
|
|
import { navigationMenuSema } from '$uix/sema/components/navigation-menu';
|
|
|
|
|
|
import { onionMenuSema } from '$uix/sema/components/onion-menu';
|
|
|
|
|
|
import { pathTraceSema } from '$uix/sema/components/path-trace';
|
|
|
|
|
|
import { proofOfHumanSema } from '$uix/sema/components/proof-of-human';
|
|
|
|
|
|
import { rangeCalendarSema } from '$uix/sema/components/range-calendar';
|
|
|
|
|
|
import { rotateAlignSema } from '$uix/sema/components/rotate-align';
|
|
|
|
|
|
import { switchSema } from '$uix/sema/components/switch';
|
|
|
|
|
|
import { timeFieldSema } from '$uix/sema/components/time-field';
|
|
|
|
|
|
import { toggleSema } from '$uix/sema/components/toggle';
|
|
|
|
|
|
import { toggleGroupSema } from '$uix/sema/components/toggle-group';
|
|
|
|
|
|
import { tooltipSema } from '$uix/sema/components/tooltip';
|
|
|
|
|
|
import { treeViewSema } from '$uix/sema/components/tree-view';
|
|
|
|
|
|
import { treeGridSema } from '$uix/sema/components/tree-grid';
|
|
|
|
|
|
import { yearGridSema } from '$uix/sema/components/year-grid';
|
|
|
|
|
|
import { Popover } from '$uix/eidos/components/popover';
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
import '@/uix/eidos/index.css';
|
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
// Opt-in full 31-scale donor palette — lets component `color="teal"` /
|
|
|
|
|
|
// `color="plum"` (THEMING §25.5, the Radix per-instance override) resolve
|
|
|
|
|
|
// the raw `--scale-{name}-*` tokens in the docs. The slim foundation
|
|
|
|
|
|
// (`generated/base.css`) ships only role-referenced scales; the demos need
|
|
|
|
|
|
// the whole palette so the System axis can exercise every scale.
|
|
|
|
|
|
import '@/uix/eidos/generated/palette.css';
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
import './uix.css';
|
|
|
|
|
|
|
|
|
|
|
|
let { children } = $props();
|
|
|
|
|
|
|
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado
El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos
independientes, y el segundo era del ecosistema entero.
1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform
FISICO en las tres reglas verticales (inset-inline-start: 50% +
translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y
el relleno quedaban fuera de eje un ancho entero. Medido en Chrome:
raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba
porque ya usaba margin-inline-start — ese era el idioma correcto del
propio fichero y es el que aplico. Mismo defecto en accordion.css:
text-align: left en el trigger, que era el UNICO text-align fisico de
todo eidos; ahora quedan cero.
2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la
celda $state, asi que prefs.<dim>.get() — y con ella el
soma.prefs.getDir() que la guia manda usar — era una lectura sin
tracking. Los ~64 componentes que resuelven su direccion desde prefs la
congelaban al montarse. La proyeccion DOM se salvaba porque usa
onChange: misma dimension, dos caminos de lectura, solo uno reactivo.
Arreglado con un contador de cambios POR dimension, bombeado desde el
effectiveDiff que el motor ya calcula, de modo que cubre tambien el
cambio por DERIVACION (direction siguiendo a language) y no solo el
intent. Un contador por clave y no una celda global: con una sola celda,
tocar motion despertaria las 64 derivaciones de direccion. El test
negativo lo clava.
3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz
con un valor siempre concreto, asi que una app que ponga html dir=rtl sin
registrar la preferencia se encontraba 33 islas volteadas del reves.
Investigadas las cinco librerias de referencia: MUI y react-aria no
estampan nunca, Radix estampa siempre (sus mantenedores arrastran
discussions/1405 por esto), y Zag estampa prop("dir") — presente si
alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme:
el atributo NUNCA tiene defecto. La herencia la hace el navegador por
AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena
prop -> prefs -> 'ltr' se queda intacta.
4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los
wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico
—heredar del menu padre— enterrado en dos expresiones sueltas.
Normalizado a activeDir(getter, soma) en los 36, un solo tipo
Direction | undefined, y el escalon de los submenus declarado en la
llamada, donde se ve.
5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace
clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe
en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era
disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar
el control — la cadena documentada funcionando de punta a punta por
primera vez.
Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en
101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en
vivo, y el arabe voltea por derivacion.
QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del
accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es
un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL);
html lang no sigue al idioma; smoke y perm:check sin ejecutar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
type Language = 'es' | 'en' | 'fr' | 'de' | 'ar';
|
|
|
|
|
|
type Currency = 'EUR' | 'USD' | 'GBP' | 'JPY';
|
|
|
|
|
|
type UnitSystem = 'metric' | 'imperial';
|
|
|
|
|
|
type NumberFormat = 'es-ES' | 'en-US' | 'en-GB' | 'de-DE';
|
|
|
|
|
|
|
|
|
|
|
|
const docsPrefsSchema = standardPrefsDimensions({
|
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado
El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos
independientes, y el segundo era del ecosistema entero.
1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform
FISICO en las tres reglas verticales (inset-inline-start: 50% +
translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y
el relleno quedaban fuera de eje un ancho entero. Medido en Chrome:
raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba
porque ya usaba margin-inline-start — ese era el idioma correcto del
propio fichero y es el que aplico. Mismo defecto en accordion.css:
text-align: left en el trigger, que era el UNICO text-align fisico de
todo eidos; ahora quedan cero.
2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la
celda $state, asi que prefs.<dim>.get() — y con ella el
soma.prefs.getDir() que la guia manda usar — era una lectura sin
tracking. Los ~64 componentes que resuelven su direccion desde prefs la
congelaban al montarse. La proyeccion DOM se salvaba porque usa
onChange: misma dimension, dos caminos de lectura, solo uno reactivo.
Arreglado con un contador de cambios POR dimension, bombeado desde el
effectiveDiff que el motor ya calcula, de modo que cubre tambien el
cambio por DERIVACION (direction siguiendo a language) y no solo el
intent. Un contador por clave y no una celda global: con una sola celda,
tocar motion despertaria las 64 derivaciones de direccion. El test
negativo lo clava.
3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz
con un valor siempre concreto, asi que una app que ponga html dir=rtl sin
registrar la preferencia se encontraba 33 islas volteadas del reves.
Investigadas las cinco librerias de referencia: MUI y react-aria no
estampan nunca, Radix estampa siempre (sus mantenedores arrastran
discussions/1405 por esto), y Zag estampa prop("dir") — presente si
alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme:
el atributo NUNCA tiene defecto. La herencia la hace el navegador por
AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena
prop -> prefs -> 'ltr' se queda intacta.
4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los
wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico
—heredar del menu padre— enterrado en dos expresiones sueltas.
Normalizado a activeDir(getter, soma) en los 36, un solo tipo
Direction | undefined, y el escalon de los submenus declarado en la
llamada, donde se ve.
5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace
clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe
en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era
disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar
el control — la cadena documentada funcionando de punta a punta por
primera vez.
Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en
101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en
vivo, y el arabe voltea por derivacion.
QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del
accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es
un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL);
html lang no sigue al idioma; smoke y perm:check sin ejecutar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
// `ar` is here to make the language→direction derivation exercisable:
|
|
|
|
|
|
// picking it flips `prefs.direction` to rtl with no direction intent set.
|
|
|
|
|
|
// Untranslated keys fall back to the default locale (engine-langs.ts:51-56),
|
|
|
|
|
|
// so it degrades to Spanish copy rather than breaking.
|
|
|
|
|
|
languages: ['es', 'en', 'fr', 'de', 'ar'] satisfies readonly Language[],
|
|
|
|
|
|
locales: ['es-ES', 'en-US', 'en-GB', 'de-DE'] satisfies readonly NumberFormat[],
|
|
|
|
|
|
currencies: ['EUR', 'USD', 'GBP', 'JPY'] satisfies readonly Currency[],
|
|
|
|
|
|
defaults: {
|
|
|
|
|
|
language: 'es',
|
|
|
|
|
|
locale: 'es-ES',
|
|
|
|
|
|
currency: 'EUR'
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
feat(web): /uix/docs/sound — la pagina que explica el canal del sonido
Primera de las ocho del plan, en castellano y DERIVADA del codigo vivo: las
tablas leen SEMA_MAP y el resolver real, nunca una transcripcion. Una pagina
que copiara el catalogo envejeceria al primer gain que alguien tocara — que
es exactamente la deriva que este subsistema se reconstruyo para quitar.
SIETE SECCIONES, ordenadas para responder «no se como funciona esto despues
de tantas correcciones»: (1) en una frase — un componente dice una palabra;
(2) el catalogo, los 16 nombres sonando al pulsarlos, agrupados por la FORMA
de cada entrada y no por una lista a mano; (3) el orden con los numeros de
verdad, tabla viva de familia x nombre x intent capa a capa; (4) que puede
llevar un .wav; (5) que no te toca escribir; (6) las correcciones y por que
—la historia ES la explicacion, ocultarla es lo que dejo al autor sin saber
como funciona—; (7) el tema.
EL BLOQUEANTE, PRIMERO. El interruptor Sound del topbar no silenciaba NADA:
el layout no pasaba `preferences`, asi que los canales se quedaban en 'full'
y el switch solo movia el intent de prefs (que gobierna el lado visual).
Publicar paginas que suenan con un mute que miente es peor que no
publicarlas. Ahora pasa un objeto VIVO — ambos canales leen
`preferences.{sound,haptic}` por emision, asi que mutarlo silencia al
instante sin re-registrar nada. Cierra §3.3 de la cola.
MEDIDO EN NAVEGADOR, no supuesto: 7 secciones, 17 chips, la rejilla y los
chips con su CSS aplicado, y la tabla de capas moviendose de verdad — con
`fulfill` el gain pasa de 0.05 a 0.1, el pitch de 700 a 1000 y el contour de
flat a ascending. Es decir: la pagina DEMUESTRA en vivo que el intent
sobrevive al nombre, que es la tesis del rediseño entero.
⚠️ NO he podido hacer captura: el panel del navegador no esta visible en esta
sesion. Verificado por arbol de accesibilidad, consola y estilos computados
— pero no lo he MIRADO. Queda pendiente tu ojo.
⚠️ Corregido de paso un fallo propio: la sonda del resolver usaba `document`
en el render de servidor y la pagina daba 500. Ahora cae a un stub en SSR.
El nav estrena seccion «El sistema perceptual». Quedan las otras siete
paginas, y los seis enlaces muertos que Foundations ya declaraba
(/uix/architecture, /uix/tokens, /uix/morfo, /uix/sema, /uix/eidos,
/uix/getting-started) siguen sin pagina.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
/**
|
|
|
|
|
|
* The perceptual reduction levels the sema channels read. A LIVE object:
|
|
|
|
|
|
* both channels read `preferences.{sound,haptic}` on every emit, so
|
|
|
|
|
|
* mutating it silences immediately with no channel re-registration.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Until 2026-08-06 this was not passed at all, so the channels defaulted to
|
|
|
|
|
|
* `'full'` and the topbar's Sound switch silenced NOTHING — it set the prefs
|
|
|
|
|
|
* intent (which drives the visual/CSS side) and stopped there. A mute that
|
|
|
|
|
|
* does not mute is worse on a page that teaches sound than no mute at all.
|
|
|
|
|
|
*/
|
|
|
|
|
|
const semaPreferences: SemaPreferences = $state({ sound: 'full', haptic: 'full' });
|
|
|
|
|
|
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
const uix = createActiveUix({
|
fix(uix audit): apply Kim audit remediations across morfo/soma/eidos/recipe layers
Resolves 34 of 40 failures from src/uix/audit-uix-kimi-1.md. The remaining
6 are all Words component (excluded per project owner — actively developed
on a separate track) or unrelated infra (cookie-adapter-client jsdom timeout).
Key changes:
- soma/provider/context.ts: swallow Svelte's `lifecycle_outside_component`
in `context.set()` so unit tests can instantiate providers without a
component tree (unblocked 18 picker tests in one line).
- soma/textarea: route inline style writes through `dom.apply({attrs})`
instead of direct `el.style.X = Y` (contract regex compliance).
- soma/picker-shell: add minimal Provider component + morfo so the shared
handle utility passes the morfo/component contract.
- eidos/_layout: lift `shared.ts` to `eidos/lib/layout-helpers.ts` (the
`_layout` directory wasn't a real component) and update 8 layout
primitive imports.
- eidos/command: replace `onMount` with `$effect` (visual layer can't use
Svelte 4 lifecycle hooks per component-api contract); restructure
`Item.Icon`/`Item.Shortcut` namespace so contract regex matches.
- eidos/color-picker: extract hue spectrum hsl literals to sidecar
`color-picker-spectrum.css`; keep main recipe color-literal-free.
- eidos/lib/recipes/base.ts: declare 196 layout-primitive + chrome CSS
vars (box, flex, grid, float, group, banner, container, section,
separator, toggle-group, pin-input, scroll-area, splitter,
navigation-menu, dropdown-menu, context-menu, date-range-field,
time-range-field, plus per-component additions to date-field,
color-picker, button, select). 197 missing → 0 non-Words.
- morfo: add `data-dir` to carousel, `data-kind` to date-picker, add
StrengthMeterLabel sub-part to password-field, register picker-shell.
- 5 pickers (date/color/date-range/time/time-range): add `internals.ts`
indirection so `exports.ts` doesn't re-export `*provider.svelte.ts`
directly (contract D); rewrite virtual root types to plain object
literals (no `WithChild`/`PrimitiveDivAttributes`).
- 4 new READMEs (button, textarea, password-field, picker-shell)
following the search-field canonical template.
Test result: 2391/2397 passing (99.75%). Remaining 6 failures are all
Words-scope or jsdom infra timeout — explicitly out of scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
langs: { schema: { sium: siumLangs, secs: secsLangs }, defaultLocale: 'es' as const },
|
|
|
|
|
|
prefs: { schema: docsPrefsSchema },
|
|
|
|
|
|
events: {
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
sound: true,
|
|
|
|
|
|
haptic: true,
|
feat(web): /uix/docs/sound — la pagina que explica el canal del sonido
Primera de las ocho del plan, en castellano y DERIVADA del codigo vivo: las
tablas leen SEMA_MAP y el resolver real, nunca una transcripcion. Una pagina
que copiara el catalogo envejeceria al primer gain que alguien tocara — que
es exactamente la deriva que este subsistema se reconstruyo para quitar.
SIETE SECCIONES, ordenadas para responder «no se como funciona esto despues
de tantas correcciones»: (1) en una frase — un componente dice una palabra;
(2) el catalogo, los 16 nombres sonando al pulsarlos, agrupados por la FORMA
de cada entrada y no por una lista a mano; (3) el orden con los numeros de
verdad, tabla viva de familia x nombre x intent capa a capa; (4) que puede
llevar un .wav; (5) que no te toca escribir; (6) las correcciones y por que
—la historia ES la explicacion, ocultarla es lo que dejo al autor sin saber
como funciona—; (7) el tema.
EL BLOQUEANTE, PRIMERO. El interruptor Sound del topbar no silenciaba NADA:
el layout no pasaba `preferences`, asi que los canales se quedaban en 'full'
y el switch solo movia el intent de prefs (que gobierna el lado visual).
Publicar paginas que suenan con un mute que miente es peor que no
publicarlas. Ahora pasa un objeto VIVO — ambos canales leen
`preferences.{sound,haptic}` por emision, asi que mutarlo silencia al
instante sin re-registrar nada. Cierra §3.3 de la cola.
MEDIDO EN NAVEGADOR, no supuesto: 7 secciones, 17 chips, la rejilla y los
chips con su CSS aplicado, y la tabla de capas moviendose de verdad — con
`fulfill` el gain pasa de 0.05 a 0.1, el pitch de 700 a 1000 y el contour de
flat a ascending. Es decir: la pagina DEMUESTRA en vivo que el intent
sobrevive al nombre, que es la tesis del rediseño entero.
⚠️ NO he podido hacer captura: el panel del navegador no esta visible en esta
sesion. Verificado por arbol de accesibilidad, consola y estilos computados
— pero no lo he MIRADO. Queda pendiente tu ojo.
⚠️ Corregido de paso un fallo propio: la sonda del resolver usaba `document`
en el render de servidor y la pagina daba 500. Ahora cae a un stub en SSR.
El nav estrena seccion «El sistema perceptual». Quedan las otras siete
paginas, y los seis enlaces muertos que Foundations ya declaraba
(/uix/architecture, /uix/tokens, /uix/morfo, /uix/sema, /uix/eidos,
/uix/getting-started) siguen sin pagina.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
preferences: semaPreferences,
|
|
|
|
|
|
components: [
|
|
|
|
|
|
accordionSema,
|
|
|
|
|
|
calendarSema,
|
uix(chat): bloque de mensajería chat-* v1 (log · message · composer · typing)
Familia chat-* completa, componiendo el ecosistema (Feed + VirtualList
anclado, Textarea, FileUpload, Avatar, Popover, Icon). Transporte-agnóstica:
el modelo de datos es de la app, los componentes reciben props + snippets.
- chat-log: Feed + VirtualList(anchor:'end') en triple registro; day
separators, unread divider, filas de sistema (Feed.Article), pill "N
nuevos" + notifyIncoming (announce a live-region), to-latest distance-aware.
- chat-message: raíz Feed.Article; runs ($libs/chat), ticks de entrega,
reply embebido, reacciones (chip pressed + tapback panel), acciones hover.
- chat-composer: Textarea (doble registro) + FileUpload; teclado
Enter/Shift+Enter/Esc/ArrowUp; typing saliente debounced (uix.timers);
barra reply/edit.
- chat-typing: sustain-streaming (visual-only, loop del sistema);
aggregateTypers 1/2/3+ con plantillas plurales.
- $libs/chat: groupIntoRuns (ventana 8 min estilo Discord) + aggregateTypers.
- VirtualList: extensión anchor:'end' (followOutput, prepend por clave
estable, sticky monótono, onAtEndChange) — sin scroller paralelo.
Diseño de referencia (WhatsApp/Telegram, pase lado-a-lado): burbujas suaves
(out = primary-element + texto normal; in = surface card + sombra) sobre
lienzo muted del log, meta dentro de la burbuja, tapback bar horizontal,
iconos lucide del ecosistema (nada de SVG a mano). Contrato de tamaños:
size="md" = 16px canónico.
Fixes de framework (afectaban más allá de chat):
- EngineLangs.t() ahora pela el prefijo '#?': los idlangrefs imperativos
(pagination/file-upload/chronos/chat) caían al fallback EN en locale es.
Fix parseLangRef ?? parsePathFallback + test de regresión (132/132).
- virtual-list commit-set-resize con channels:[]: es bookkeeping de
ResizeObserver, no gesto → dejaba de emitir beep/vibrate pre-interacción
(interventions de navigator.vibrate / AudioContext).
Escaparate messenger en /uix/demos/chat (bot de eco con uix.timers;
variante $connection señalada). Gaps v2 registrados en next-features §7.
Pase: component:audit 4×PASS · eidos-lint invalid=0 · recipe-contract
29/29 · Playwright escaparate PASS end-to-end · consola limpia ·
npm run check en baseline (76, cero errores nuevos).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
chatComposerSema,
|
|
|
|
|
|
chatLogSema,
|
|
|
|
|
|
chatMessageSema,
|
|
|
|
|
|
checkboxSema,
|
|
|
|
|
|
colorPickerSema,
|
|
|
|
|
|
comboboxSema,
|
feat(uix): cropper, image-picker, image-adjustments + reusable zoom-pan layer
Three new components across all 4 layers (morfo/soma/sema/eidos) + demos + langs + READMEs:
- ImageAdjustments — reusable image-filter sliders subcomponent.
- ImagePicker — composes file-upload + image + image-adjustments (fill modes, rotation).
- Cropper — Ark image-cropper anatomy: normalized 0-1 crop rect, rect + round shapes,
8 resize handles, fixedSize move-only (avatar), canvas Blob output (5-arg drawImage
for SVG safety), wheel/pan zoom, two-zone gesture (selection moves the area, the
background pans to set focus; img draggable=false guards native DnD per zag).
New reusable soma/layers/zoom-pan: cursor-centered zoom, clamped pan, transform +
viewport-frac-to-content-frac mapping. Image-viewer / diagram-pan / map can reuse it.
sema: add 'zoom' verb to the handle family (direct manipulation, documented extension).
slider: track visible at zero (gray, not white), thumb lifts on drag.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
cropperSema,
|
|
|
|
|
|
cssFieldSema,
|
|
|
|
|
|
dateFieldSema,
|
|
|
|
|
|
dialogSema,
|
|
|
|
|
|
drawerSema,
|
|
|
|
|
|
editableSema,
|
|
|
|
|
|
fileUploadSema,
|
|
|
|
|
|
floatPanelSema,
|
|
|
|
|
|
formSema,
|
|
|
|
|
|
gradientBuilderSema,
|
|
|
|
|
|
gradientPickerSema,
|
feat(uix): cropper, image-picker, image-adjustments + reusable zoom-pan layer
Three new components across all 4 layers (morfo/soma/sema/eidos) + demos + langs + READMEs:
- ImageAdjustments — reusable image-filter sliders subcomponent.
- ImagePicker — composes file-upload + image + image-adjustments (fill modes, rotation).
- Cropper — Ark image-cropper anatomy: normalized 0-1 crop rect, rect + round shapes,
8 resize handles, fixedSize move-only (avatar), canvas Blob output (5-arg drawImage
for SVG safety), wheel/pan zoom, two-zone gesture (selection moves the area, the
background pans to set focus; img draggable=false guards native DnD per zag).
New reusable soma/layers/zoom-pan: cursor-centered zoom, clamped pan, transform +
viewport-frac-to-content-frac mapping. Image-viewer / diagram-pan / map can reuse it.
sema: add 'zoom' verb to the handle family (direct manipulation, documented extension).
slider: track visible at zero (gray, not white), thumb lifts on drag.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
imageAdjustmentsSema,
|
|
|
|
|
|
imagePickerSema,
|
|
|
|
|
|
knobSema,
|
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
maskFieldSema,
|
fix(demo): cargar mediaPlayerSema en la composicion de la app de demos
Reporte del usuario en vivo: el transporte del player seguia sonando en la
demo pese al pack silencioso (D-AP2.7 v2). Causa raiz: los packs de sema son
OPT-IN por app (defineEngineSemantic({ components: [...] }), tree-shaking) y
`mediaPlayerSema` NUNCA estuvo en la lista del layout de demos — ni el
silencio nuevo, ni el tap sutil v1, ni el fix del selector (H-1) llegaron a
aplicarse alli jamas: lo que sonaba era el default crudo de familia (los dos
earcons por clic que H-10 midio).
Dos lineas: import + entrada en `components` de
web/routes/uix/+layout@.svelte. Verificado: check 74 = baseline, 0 propios.
Observacion reportada (fuera de alcance, no tocada): otros packs del indice
tampoco estan en esa lista (dropdown-menu, context-menu, menubar, switch,
toggle, tooltip, tree-view...) — la misma clase de hueco para sus demos.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
mediaPlayerSema,
|
|
|
|
|
|
navTreeSema,
|
uix(sidebar): F1.7 eidos + demo · raíl, modo icono y submenú flotante
Segunda tanda del sidebar: la capa visual, la demo canónica de 9 pestañas y
la verificación en navegador real. Con esto el componente está vivo; queda el
review adversarial.
Eidos
- Recipe con los dos ejes: off-canvas por `transform` (trabajo de compositor,
el inset no reflowea) y `width` animado SOLO en modo icono, donde el inset
sí tiene que seguir al raíl. El raíl NO lleva `will-change` (una franja
fina tiembla a DPR fraccionario — memoria NavMenu).
- Anchos como TOKENS: `--sidebar-width` 16rem · `--sidebar-width-icon` 3rem ·
`--sidebar-width-mobile` 18rem, más raíl, gaps, altura de fila y colores.
- Tres composiciones que la capa headless no puede hacer: `Drawer` para la
presentación móvil, `Tooltip` para nombrar filas en modo icono y `Badge`
para los contadores. El trigger compone `Button` por el patrón `child`.
- Convención de icono documentada: sobrevive el PRIMER elemento de la fila.
Arreglos que destapó el navegador
- **`mobile` en SSR**: sin viewport, `isAtLeast` responde «por debajo de todo»
→ el HTML salía con `data-mobile` y la hidratación NO lo corrige (no diffea
atributos y el valor ya no cambia). Viewport desconocido = ESCRITORIO.
- **Estado móvil propio** (arranca cerrado): en escritorio `open` es
preferencia de layout persistida; en móvil sería un overlay tapando la
página, que jamás puede ser el estado inicial. `onOpenChange` solo se
dispara con el estado de escritorio.
- **`open` controlado/no controlado**: se adopta el patrón de la casa
(`$bindable` con default, el provider escribe siempre) — mi versión previa
con estado interno no propagaba `bind:open`.
- **El submenú flotante no se posicionaba**: `FloatingAnchor` resuelve su
provider por CONTEXTO y el eidos envuelve la fila en un `Tooltip`, que
publica el suyo → el ancla caía en el tooltip y el submenú se quedaba en
`translate(0,-200%)`. Ahora el ancla se cablea directamente al provider
flotante del item. Además el `<ul>` ya no se posiciona a sí mismo (colapsaba
el wrapper a tamaño cero) y toma elevación del plano `overlay` por
`data-depth`, como cualquier panel flotante del ecosistema.
- **Tooltip vs submenú**: una fila con submenú no lleva tooltip en modo icono
(dos superficies sobre el mismo ancla se pisan) — nuevo `useSidebarItemOr()`.
- El item envuelve (`flex-wrap`) para que el submenú caiga en su propia línea
en vez de ponerse al lado de la fila.
Demo: shell de app real (grupos, badge, acción, submenú, fila deshabilitada),
controles vivos de los tres props y las 9 pestañas del canon.
Verificado en navegador real (Playwright): dos landmarks nombrados en el
idioma activo · `aria-current="page"` en la fila activa · cada menú etiquetado
por SU grupo · raíl enfocable que alterna con Enter · colapso 256→48px ·
off-canvas por transform · submenú flotante anclado a su fila y Escape que
devuelve el foco · sema `emerge-expand/collapse` estampando en `panel` ·
móvil 600px = Drawer cerrado con el landmark dentro · RTL espeja el shell
entero · oscuro · 0 errores de consola.
Gates: `component:audit --only sidebar` PASS 0E/0W · eidos-lint 45
morfo-backed / 0 invalid / 0 class-hooks · `svelte-check` 0 errores propios ·
`vitest src/uix/eidos` 353/353 · `contracts.test` solo con fallos ajenos
(menubar, radio-group y las claves camelCase de aura).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
|
|
|
|
sidebarSema,
|
|
|
|
|
|
numberFieldSema,
|
|
|
|
|
|
paginationSema,
|
feat: build PasswordField (full stack)
Dedicated password input component — matches the Ark UI / Chakra v3 /
Mantine / PrimeReact / Ant Design pattern of treating PasswordField as
its own first-class component (not a TextField variant). Includes one
feature that NO surveyed library ships: a CapsLock indicator driven by
KeyboardEvent.getModifierState('CapsLock').
Anatomy (5 parts, 4 events):
- Provider (root, value + visibility state)
- Input (the textbox; type flips between password / text)
- VisibilityTrigger (eye toggle button; aria-pressed + aria-controls)
- CapsLockIndicator (status live region; data-active toggles via soma
listening to keydown/keyup on Input)
- StrengthMeter (composable; consumer provides 0-4 score; UIX bundles
no zxcvbn-style algorithm — dependency-free by design)
Events:
- commit-input (every keystroke)
- commit-submit (Enter)
- shift-toggle-visibility (eye click)
- shift-caps-state (Caps Lock state flips while focused)
Key behaviors:
- `purpose: 'current' | 'new'` drives native autocomplete attribute
('current-password' for login / 'new-password' for registration).
- Field integration: disabled/readonly/required/invalid OR-merged.
- VisibilityTrigger gets tabindex=-1 so it doesn't break the form's
natural tab order — mouse / programmatic focus only, matching Ark UI.
- Caps Lock indicator is aria-live="polite" — announces on activation
while focused, silent when inactive.
- Letter-spacing increased on the input for legibility while masked.
Reference library survey performed (10 libraries) before implementation —
all consensus around dedicated component, all have built-in toggle,
ZERO have caps lock warning. Documented in the demo's API tab with a
feature comparison table.
Demo follows DEMO_AUTHORING_GUIDE canonical 6-tab depth: Live (controls
grouped by layer + code snippets at the bottom), API (props + reference
comparison), Morfo (field/value + Parts + Events tables), Sema (events
table with ▶ play), Recipe (selectors with morfo/eidos owner tags), A11y
(concern/contract table).
Also includes external work in words/ from a separate session
(unrelated to PasswordField) — not touching Words is the standing rule,
but the diff was already in the working tree.
svelte-check: 0 errors in PasswordField files (Words errors are tracked
by its own development branch).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
passwordFieldSema,
|
|
|
|
|
|
popoverSema,
|
|
|
|
|
|
radioGroupSema,
|
|
|
|
|
|
ratingGroupSema,
|
|
|
|
|
|
searchFieldSema,
|
|
|
|
|
|
selectSema,
|
|
|
|
|
|
sliderSema,
|
|
|
|
|
|
splitterSema,
|
|
|
|
|
|
stepperSema,
|
|
|
|
|
|
tabsSema,
|
|
|
|
|
|
tagGroupSema,
|
|
|
|
|
|
tagsInputSema,
|
Build TextArea component + fix locale propagation bug across soma providers
Major work in this session:
1. NumberField demo refactored to expose Intl.NumberFormatOptions live
- 9 canonical presets (currency/unit/percent/compact/plain)
- Locale/currency/unit lists derived from $libs/langs, $libs/currency,
$format/units (no more hardcoded arrays)
- Default locale from uix.format.numbers.getLocale()
2. Bug fix: locale prop ignored when soma.format service is active
- soma/NumberField: 5 bypass points via new `hasOwnLocale` getter
(decimalSeparator, groupSeparator, formatValue, formatEditValue, parseValue)
- soma/Calendar: dateOrder respects locale prop (matches DateField precedence)
- soma/RangeCalendar: same dateOrder fix
- When the consumer passes `locale` prop, soma.nums/dates is bypassed
and Intl is used directly with the prop locale
3. TextArea — new component, full 4-layer stack
- morfo: 3 parts (provider/input/count), 3 events
(commit-input/commit-submit/shift-count-overflow), APG textbox
- soma: TextAreaProvider with autosize (scrollHeight measurement),
submitOn modes (false|enter|shift+enter|mod+enter), maxLength soft clamp,
Field integration (OR-merge), counter overflow tracking
- eidos: 5 sizes × 3 variants × 8 colors, autosize suppresses CSS resize
- sema: commit-submit + shift-count-overflow perceptual cues
- demo: canonical 6-tab depth, all props exposed as live controls
- PrimitiveTextareaAttributes added to soma/types/html.ts
4. Also includes accumulated unreleased work from prior sessions:
- Eidos ports: announce, clipboard, drag-drop, feed, table,
tree-grid, tree-view, virtual-grid, virtual-list, words
- Their canonical demos under web/routes/uix/components/
- Semantic events defined for utility morfos
(announce/clipboard/virtual-list/virtual-grid)
- Tree indent guides fixed (TreeView ::before, TreeGrid stacked gradients)
- Sidebar reorganized with Data + Utilities groups
- Eidos words content layer and primitives
Reference library survey performed before TextArea API design (Mantine,
Radix Themes, Chakra v3, React Aria, Ark UI, Bits UI, shadcn, MUI, Prime).
Decision documented: no separate CurrencyField/UnitField — single
NumberField with formatOptions is the canon (matches all surveyed
headless libs except PrimeReact's mode enum, which still uses 1 component).
svelte-check: 0 errors in modified/created files.
See CONTINUE.md for full session notes and next steps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
textareaSema,
|
|
|
|
|
|
timelineSema,
|
|
|
|
|
|
toastSema,
|
fix(demo): cargar los 25 packs sema que faltaban en la composicion de demos
Cierra la CLASE del hueco que destapo el reporte del media-player
(63f20f345): los packs sema son OPT-IN por app y la lista del layout de
demos habia crecido por goteo — 25 de los 68 packs del indice no estaban, y
sus demos sonaban con defaults crudos de familia en vez de sus firmas
autoradas: chronos, clipboard, collapsible, color-field, context-menu,
drag-drop, dropdown-menu, listbox, menubar, menu-dial, month-grid,
navigation-menu, onion-menu, path-trace, proof-of-human, range-calendar,
rotate-align, switch, time-field, toggle, toggle-group, tooltip, tree-view,
tree-grid, year-grid.
Entre ellos, los packs silenciosos de D.5/D.6 (switch/toggle/toggle-group,
tooltip, menus/arboles): esas demos ahora CALLAN o suenan sutil como su
doctrina prescribe — el cambio audible es hacia lo autorado.
Bloque contiguo y comentado (sin reordenar lo existente). Verificado:
check 74 = baseline, 0 propios.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
toolbarSema,
|
|
|
|
|
|
// The rest of the index (2026-07-31) — see the import note above.
|
|
|
|
|
|
chronosSema,
|
|
|
|
|
|
clipboardSema,
|
|
|
|
|
|
collapsibleSema,
|
|
|
|
|
|
colorFieldSema,
|
|
|
|
|
|
contextMenuSema,
|
|
|
|
|
|
dragDropSema,
|
|
|
|
|
|
dropdownMenuSema,
|
|
|
|
|
|
listboxSema,
|
|
|
|
|
|
menubarSema,
|
|
|
|
|
|
menuDialSema,
|
|
|
|
|
|
monthGridSema,
|
|
|
|
|
|
navigationMenuSema,
|
|
|
|
|
|
onionMenuSema,
|
|
|
|
|
|
pathTraceSema,
|
|
|
|
|
|
proofOfHumanSema,
|
|
|
|
|
|
rangeCalendarSema,
|
|
|
|
|
|
rotateAlignSema,
|
|
|
|
|
|
switchSema,
|
|
|
|
|
|
timeFieldSema,
|
|
|
|
|
|
toggleSema,
|
|
|
|
|
|
toggleGroupSema,
|
|
|
|
|
|
tooltipSema,
|
|
|
|
|
|
treeViewSema,
|
|
|
|
|
|
treeGridSema,
|
|
|
|
|
|
yearGridSema
|
|
|
|
|
|
]
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
setActiveUix(uix);
|
|
|
|
|
|
Soma.create();
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
|
|
|
|
|
|
type Section =
|
|
|
|
|
|
| { kind: 'top'; slug: string; label: string }
|
|
|
|
|
|
| { kind: 'group'; heading: string; items: { slug: string; label: string; tag?: string }[] };
|
|
|
|
|
|
|
|
|
|
|
|
// Information architecture — single source of truth for sidebar +
|
|
|
|
|
|
// breadcrumb. Each leaf maps to a route under `/uix/...`.
|
|
|
|
|
|
const nav: Section[] = [
|
|
|
|
|
|
{
|
|
|
|
|
|
kind: 'group',
|
|
|
|
|
|
heading: 'Get started',
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{ slug: '/uix', label: 'Overview' },
|
|
|
|
|
|
{ slug: '/uix/architecture', label: 'Architecture' },
|
|
|
|
|
|
{ slug: '/uix/getting-started', label: 'Installation' }
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
kind: 'group',
|
|
|
|
|
|
heading: 'Foundations',
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{ slug: '/uix/tokens', label: 'Design tokens' },
|
|
|
|
|
|
{ slug: '/uix/morfo', label: 'Morfo · contracts' },
|
|
|
|
|
|
{ slug: '/uix/sema', label: 'Sema · perception' },
|
|
|
|
|
|
{ slug: '/uix/eidos', label: 'Eidos · visual layer' }
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
feat(web): /uix/docs/sound — la pagina que explica el canal del sonido
Primera de las ocho del plan, en castellano y DERIVADA del codigo vivo: las
tablas leen SEMA_MAP y el resolver real, nunca una transcripcion. Una pagina
que copiara el catalogo envejeceria al primer gain que alguien tocara — que
es exactamente la deriva que este subsistema se reconstruyo para quitar.
SIETE SECCIONES, ordenadas para responder «no se como funciona esto despues
de tantas correcciones»: (1) en una frase — un componente dice una palabra;
(2) el catalogo, los 16 nombres sonando al pulsarlos, agrupados por la FORMA
de cada entrada y no por una lista a mano; (3) el orden con los numeros de
verdad, tabla viva de familia x nombre x intent capa a capa; (4) que puede
llevar un .wav; (5) que no te toca escribir; (6) las correcciones y por que
—la historia ES la explicacion, ocultarla es lo que dejo al autor sin saber
como funciona—; (7) el tema.
EL BLOQUEANTE, PRIMERO. El interruptor Sound del topbar no silenciaba NADA:
el layout no pasaba `preferences`, asi que los canales se quedaban en 'full'
y el switch solo movia el intent de prefs (que gobierna el lado visual).
Publicar paginas que suenan con un mute que miente es peor que no
publicarlas. Ahora pasa un objeto VIVO — ambos canales leen
`preferences.{sound,haptic}` por emision, asi que mutarlo silencia al
instante sin re-registrar nada. Cierra §3.3 de la cola.
MEDIDO EN NAVEGADOR, no supuesto: 7 secciones, 17 chips, la rejilla y los
chips con su CSS aplicado, y la tabla de capas moviendose de verdad — con
`fulfill` el gain pasa de 0.05 a 0.1, el pitch de 700 a 1000 y el contour de
flat a ascending. Es decir: la pagina DEMUESTRA en vivo que el intent
sobrevive al nombre, que es la tesis del rediseño entero.
⚠️ NO he podido hacer captura: el panel del navegador no esta visible en esta
sesion. Verificado por arbol de accesibilidad, consola y estilos computados
— pero no lo he MIRADO. Queda pendiente tu ojo.
⚠️ Corregido de paso un fallo propio: la sonda del resolver usaba `document`
en el render de servidor y la pagina daba 500. Ahora cae a un stub en SSR.
El nav estrena seccion «El sistema perceptual». Quedan las otras siete
paginas, y los seis enlaces muertos que Foundations ya declaraba
(/uix/architecture, /uix/tokens, /uix/morfo, /uix/sema, /uix/eidos,
/uix/getting-started) siguen sin pagina.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
{
|
|
|
|
|
|
kind: 'group',
|
docs(web): cuatro paginas del sonido, escritas para quien llega de nuevas
Correccion del autor sobre la primera version: «la redaccion esta mal,
empieza como si la gente supiera ya que es lo que has pasado, la redaccion
tiene que ser didactica para un nuevo desarrollador». Tenia razon — estaban
escritas como notas de version para quien vivio el rediseño: se abrian
presumiendo el vocabulario y presumian de cuantas reglas se habian borrado,
que es mi historia, no documentacion.
Reescritas desde cero, en ese orden: primero la escena («guardas un
documento y oyes un repique»), luego el vocabulario construido pieza a
pieza (familia, verbo, intent) y solo entonces la maquinaria. Cada
concepto se explica antes de usar su palabra.
/uix/docs/sound como suena una interfaz — el modelo, con la
resolucion en vivo (familia + verbo + intent)
/uix/docs/sound/catalogo cada sonido que existe, audible, con la razon
de por que suena asi y que significa cada eje
/uix/docs/sound/packs traer tu propia voz: sintesis, wav, mp3,
registrar nombres nuevos, cambiar en caliente
/uix/docs/sound/gestos por que un arrastre suena por repeticion, con
un simulador de tres velocidades para oirlo
DERIVADAS DEL CODIGO: los sonidos, familias, intents y verbos salen de
`SEMA_MAP` y del resolver real. Solo la prosa esta escrita a mano, asi que
añadir una entrada al catalogo la hace aparecer con sus medidas.
Lo historico no desaparece, cambia de sitio: donde aporta —por que los
sonidos son tan distintos entre si, que se gana y que se pierde con el
trinquete— va al final de su seccion como razon, nunca como apertura.
VERIFICADO: las cuatro responden 200 en SSR con sus secciones · check sin
errores nuevos en las paginas.
⚠️ Sigo sin poder mirarlas: el panel del navegador no compone en esta
sesion, asi que la composicion visual queda pendiente de tu ojo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
heading: 'El sonido',
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{ slug: '/uix/docs/sound', label: 'Cómo suena una interfaz' },
|
|
|
|
|
|
{ slug: '/uix/docs/sound/catalogo', label: 'El catálogo' },
|
|
|
|
|
|
{ slug: '/uix/docs/sound/packs', label: 'Sound packs' },
|
|
|
|
|
|
{ slug: '/uix/docs/sound/gestos', label: 'Gestos' }
|
|
|
|
|
|
]
|
feat(web): /uix/docs/sound — la pagina que explica el canal del sonido
Primera de las ocho del plan, en castellano y DERIVADA del codigo vivo: las
tablas leen SEMA_MAP y el resolver real, nunca una transcripcion. Una pagina
que copiara el catalogo envejeceria al primer gain que alguien tocara — que
es exactamente la deriva que este subsistema se reconstruyo para quitar.
SIETE SECCIONES, ordenadas para responder «no se como funciona esto despues
de tantas correcciones»: (1) en una frase — un componente dice una palabra;
(2) el catalogo, los 16 nombres sonando al pulsarlos, agrupados por la FORMA
de cada entrada y no por una lista a mano; (3) el orden con los numeros de
verdad, tabla viva de familia x nombre x intent capa a capa; (4) que puede
llevar un .wav; (5) que no te toca escribir; (6) las correcciones y por que
—la historia ES la explicacion, ocultarla es lo que dejo al autor sin saber
como funciona—; (7) el tema.
EL BLOQUEANTE, PRIMERO. El interruptor Sound del topbar no silenciaba NADA:
el layout no pasaba `preferences`, asi que los canales se quedaban en 'full'
y el switch solo movia el intent de prefs (que gobierna el lado visual).
Publicar paginas que suenan con un mute que miente es peor que no
publicarlas. Ahora pasa un objeto VIVO — ambos canales leen
`preferences.{sound,haptic}` por emision, asi que mutarlo silencia al
instante sin re-registrar nada. Cierra §3.3 de la cola.
MEDIDO EN NAVEGADOR, no supuesto: 7 secciones, 17 chips, la rejilla y los
chips con su CSS aplicado, y la tabla de capas moviendose de verdad — con
`fulfill` el gain pasa de 0.05 a 0.1, el pitch de 700 a 1000 y el contour de
flat a ascending. Es decir: la pagina DEMUESTRA en vivo que el intent
sobrevive al nombre, que es la tesis del rediseño entero.
⚠️ NO he podido hacer captura: el panel del navegador no esta visible en esta
sesion. Verificado por arbol de accesibilidad, consola y estilos computados
— pero no lo he MIRADO. Queda pendiente tu ojo.
⚠️ Corregido de paso un fallo propio: la sonda del resolver usaba `document`
en el render de servidor y la pagina daba 500. Ahora cae a un stub en SSR.
El nav estrena seccion «El sistema perceptual». Quedan las otras siete
paginas, y los seis enlaces muertos que Foundations ya declaraba
(/uix/architecture, /uix/tokens, /uix/morfo, /uix/sema, /uix/eidos,
/uix/getting-started) siguen sin pagina.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
},
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{
|
|
|
|
|
|
kind: 'group',
|
|
|
|
|
|
heading: 'Controls',
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{ slug: '/uix/components/toggle', label: 'Toggle' },
|
|
|
|
|
|
{ slug: '/uix/components/toggle-group', label: 'Toggle group' },
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/switch', label: 'Switch' },
|
|
|
|
|
|
{ slug: '/uix/components/checkbox', label: 'Checkbox' },
|
|
|
|
|
|
{ slug: '/uix/components/radio-group', label: 'Radio group' },
|
feat(uix): RadioCards — card-style single-select reusing RadioGroup
New eidos component built ON TOP of RadioGroup with zero duplicated
behaviour. RadioCards reuses RadioGroup's soma wholesale (selection,
roving focus, role=radio, native <form> via HiddenInput) and adds only
the card chrome: icon · title · description + an always-present corner
radio. Vertical, horizontal and grid layouts.
- morfo: scope:['eidos'], thin contract for presentation parts only
(Provider marker, Item-card, Indicator, Icon, Content, Title,
Description). The interactive contract + commit-select stay in
radio-group, reused via its soma. No new soma, no new sema.
- eidos: `radio-cards` recipe (card chrome) + wrappers + radio-cards.css.
radio-group.css is NOT loaded here, only its foundation tokens — the
card owns its visual, reusing --_radio-group-palette-* (inlined, since a
:root token referencing the palette is invalid-at-computed-value).
- demo: canonical structure (always-on stage + trace, tabs, chip/switch
controls, Untitled-UI example patterns) + nav entry + README.
check 0 · eidos-lint 0 invalid. Browser-verified: select (click + keyboard
roving), disabled, the three layouts, controls drive the preview.
Follow-up: icon/circle sizes are still px (mirroring radio-group's own size
tokens) — making the control-size scale density-aware is a separate,
family-wide change deferred per request.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
{ slug: '/uix/components/radio-cards', label: 'Radio cards' },
|
|
|
|
|
|
{ slug: '/uix/components/rating-group', label: 'Rating group' },
|
|
|
|
|
|
{ slug: '/uix/components/slider', label: 'Slider' },
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/collapsible', label: 'Collapsible' },
|
|
|
|
|
|
{ slug: '/uix/components/accordion', label: 'Accordion' },
|
|
|
|
|
|
{ slug: '/uix/components/tabs', label: 'Tabs' },
|
|
|
|
|
|
{ slug: '/uix/components/toolbar', label: 'Toolbar' },
|
|
|
|
|
|
{ slug: '/uix/components/button', label: 'Button' },
|
|
|
|
|
|
{ slug: '/uix/components/button-group', label: 'Button group' },
|
|
|
|
|
|
{ slug: '/uix/components/split-button', label: 'Split button' },
|
|
|
|
|
|
{ slug: '/uix/components/fab', label: 'Fab' }
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
kind: 'group',
|
|
|
|
|
|
heading: 'Forms',
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{ slug: '/uix/components/form', label: 'Form' },
|
|
|
|
|
|
{ slug: '/uix/components/field', label: 'Field' },
|
|
|
|
|
|
{ slug: '/uix/components/field-langs', label: 'Field langs' },
|
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
{ slug: '/uix/components/label', label: 'Label' },
|
|
|
|
|
|
{ slug: '/uix/components/editable', label: 'Editable' },
|
palabras: retirar el chrome words deprecado (FW)
Decision de usuario 2026-07-14: words (chrome eidos + demo) quedo
deprecado en pos de palabras; existia como referencia historica.
- Migradas las 4 piezas que palabras importaba (git mv, historia
conservada, sin shims): words-activate -> palabras-activate,
words-block-drag -> palabras-block-drag, words-block-drag-type ->
palabras-block-drag-type, words-image-file -> palabras-image-file;
imports actualizados a rutas locales.
- Borrados src/uix/eidos/components/words/** (wrapper, menubar, bubble,
inspector, gutter, rail, words.css, langs-menu/inspector, AUDIT
visual) y la demo web/routes/uix/components/words/.
- Sidebar: fuera el link Words, entra Palabras (la demo era
inalcanzable desde la nav).
- Podado el bloque `words:` de lib/recipes/base.ts + regeneracion
(generated/base.css: -84 lineas, solo bajas --words-*).
- Docs del track: referencias al AUDIT visual de words apuntan a git
history.
NO muere: soma/components/words (motor + provider + tests), morfo y
langs de words — el fundamento vivo de palabras.
Verificacion: engine 493/493 - guards eidos (recipe-css-contract +
api-contract + visual-attrs) 35/35 - grep eidos/components/words en
codigo = 0 - navegador (server propio): demo palabras monta sin
errores de consola, activate fija bloque, grip abre panel (tabs +
pills H1-H6/HC + texto/enlace).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
{ slug: '/uix/components/palabras', label: 'Palabras' },
|
|
|
|
|
|
{ slug: '/uix/components/number-field', label: 'Number field' },
|
|
|
|
|
|
{ slug: '/uix/components/css-field', label: 'CSS field' },
|
|
|
|
|
|
{ slug: '/uix/components/search-field', label: 'Search field' },
|
feat: build PasswordField (full stack)
Dedicated password input component — matches the Ark UI / Chakra v3 /
Mantine / PrimeReact / Ant Design pattern of treating PasswordField as
its own first-class component (not a TextField variant). Includes one
feature that NO surveyed library ships: a CapsLock indicator driven by
KeyboardEvent.getModifierState('CapsLock').
Anatomy (5 parts, 4 events):
- Provider (root, value + visibility state)
- Input (the textbox; type flips between password / text)
- VisibilityTrigger (eye toggle button; aria-pressed + aria-controls)
- CapsLockIndicator (status live region; data-active toggles via soma
listening to keydown/keyup on Input)
- StrengthMeter (composable; consumer provides 0-4 score; UIX bundles
no zxcvbn-style algorithm — dependency-free by design)
Events:
- commit-input (every keystroke)
- commit-submit (Enter)
- shift-toggle-visibility (eye click)
- shift-caps-state (Caps Lock state flips while focused)
Key behaviors:
- `purpose: 'current' | 'new'` drives native autocomplete attribute
('current-password' for login / 'new-password' for registration).
- Field integration: disabled/readonly/required/invalid OR-merged.
- VisibilityTrigger gets tabindex=-1 so it doesn't break the form's
natural tab order — mouse / programmatic focus only, matching Ark UI.
- Caps Lock indicator is aria-live="polite" — announces on activation
while focused, silent when inactive.
- Letter-spacing increased on the input for legibility while masked.
Reference library survey performed (10 libraries) before implementation —
all consensus around dedicated component, all have built-in toggle,
ZERO have caps lock warning. Documented in the demo's API tab with a
feature comparison table.
Demo follows DEMO_AUTHORING_GUIDE canonical 6-tab depth: Live (controls
grouped by layer + code snippets at the bottom), API (props + reference
comparison), Morfo (field/value + Parts + Events tables), Sema (events
table with ▶ play), Recipe (selectors with morfo/eidos owner tags), A11y
(concern/contract table).
Also includes external work in words/ from a separate session
(unrelated to PasswordField) — not touching Words is the standing rule,
but the diff was already in the working tree.
svelte-check: 0 errors in PasswordField files (Words errors are tracked
by its own development branch).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/password-field', label: 'Password field' },
|
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
{ slug: '/uix/components/mask-field', label: 'Mask field' },
|
|
|
|
|
|
{ slug: '/uix/components/prose', label: 'Prose' },
|
Build TextArea component + fix locale propagation bug across soma providers
Major work in this session:
1. NumberField demo refactored to expose Intl.NumberFormatOptions live
- 9 canonical presets (currency/unit/percent/compact/plain)
- Locale/currency/unit lists derived from $libs/langs, $libs/currency,
$format/units (no more hardcoded arrays)
- Default locale from uix.format.numbers.getLocale()
2. Bug fix: locale prop ignored when soma.format service is active
- soma/NumberField: 5 bypass points via new `hasOwnLocale` getter
(decimalSeparator, groupSeparator, formatValue, formatEditValue, parseValue)
- soma/Calendar: dateOrder respects locale prop (matches DateField precedence)
- soma/RangeCalendar: same dateOrder fix
- When the consumer passes `locale` prop, soma.nums/dates is bypassed
and Intl is used directly with the prop locale
3. TextArea — new component, full 4-layer stack
- morfo: 3 parts (provider/input/count), 3 events
(commit-input/commit-submit/shift-count-overflow), APG textbox
- soma: TextAreaProvider with autosize (scrollHeight measurement),
submitOn modes (false|enter|shift+enter|mod+enter), maxLength soft clamp,
Field integration (OR-merge), counter overflow tracking
- eidos: 5 sizes × 3 variants × 8 colors, autosize suppresses CSS resize
- sema: commit-submit + shift-count-overflow perceptual cues
- demo: canonical 6-tab depth, all props exposed as live controls
- PrimitiveTextareaAttributes added to soma/types/html.ts
4. Also includes accumulated unreleased work from prior sessions:
- Eidos ports: announce, clipboard, drag-drop, feed, table,
tree-grid, tree-view, virtual-grid, virtual-list, words
- Their canonical demos under web/routes/uix/components/
- Semantic events defined for utility morfos
(announce/clipboard/virtual-list/virtual-grid)
- Tree indent guides fixed (TreeView ::before, TreeGrid stacked gradients)
- Sidebar reorganized with Data + Utilities groups
- Eidos words content layer and primitives
Reference library survey performed before TextArea API design (Mantine,
Radix Themes, Chakra v3, React Aria, Ark UI, Bits UI, shadcn, MUI, Prime).
Decision documented: no separate CurrencyField/UnitField — single
NumberField with formatOptions is the canon (matches all surveyed
headless libs except PrimeReact's mode enum, which still uses 1 component).
svelte-check: 0 errors in modified/created files.
See CONTINUE.md for full session notes and next steps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/textarea', label: 'Text area' },
|
|
|
|
|
|
{ slug: '/uix/components/select', label: 'Select' },
|
|
|
|
|
|
{ slug: '/uix/components/combobox', label: 'Combobox' },
|
feat(eidos): port Listbox (embedded selection list)
#131. Eidos wrapper + types + recipe + canonical-depth demo. Soma owns
single/multi selection (value as string[]), roving-tabindex keyboard
nav (Arrow + Home/End + typeahead + Ctrl+A), Field-merged flags
(disabled/readonly/required/invalid), hidden form inputs. Eidos adds
the visual surface and exposes variant + size + color.
Reference parity (no air baseline — port directly from soma):
| Feature | soma | react-aria | Headless UI | bits-ui | this port |
|---|---|---|---|---|---|
| Single + multiple selection | ✓ | ✓ | ✓ | ✓ | ✓ |
| bind:value (controlled) | ✓ | ✓ | ✓ | ✓ | ✓ |
| Typeahead + Arrow / Home / End | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop nav | ✓ | ✗ | ✗ | ✗ | ✓ |
| Section / Group + label | ✓ | ✓ | ✗ | ✓ | ✓ |
| Ctrl+A select-all (multi) | ✓ | ✓ | ✗ | ✗ | ✓ |
| Form participation (hidden inputs) | ✓ | ✗ | ✓ | ✗ | ✓ |
| Field.Provider merge (disabled/invalid/…) | ✓ | ✗ | ✗ | ✗ | ✓ |
| Horizontal orientation | ✓ | ✓ | ✓ | ✓ | ✓ |
| Item indicator (✓ glyph) | ✓ | ✓ | ✗ | ✓ | ✓ shared partial |
| Drag-to-reorder | ✗ | ✓ | ✗ | ✗ | ⚠ deferred |
| Async / infinite loading | ✗ | ✓ | ✗ | ✗ | ⚠ deferred |
| selectionBehavior (toggle/replace) | ⚠ implicit | ✓ | ✗ | ✓ | ⚠ deferred |
| PageUp/Down nav | ✗ | ✓ | ✗ | ✗ | ⚠ deferred |
Parts (5): Listbox (Root, replaces soma's Provider name), Item,
ItemIndicator, Group, GroupLabel.
Variant set:
- variant: ControlVariant (`'surface' | 'outline' | 'ghost'`), default 'surface'
- size: xs..xl (5-step canonical form-control scale)
- color: ColorRole (primary/secondary/neutral/affirm/fulfill/risk/threat/loss)
- block: stretch to container width
Cross-component reuse:
- `ItemIndicator` shares the menu-indicator visual (`a2709476`) — ✓ glyph
identical to standalone Checkbox and Dropdown/Context Menu
CheckboxItems. CSS-only difference: shown only inside
`[data-listbox-item][data-selected]`.
- Item row hover / highlight tokens mirror Combobox.Item /
Command.Item — the form-control surface family stays coherent.
Disambiguation note (in index.ts comment):
- Listbox: atomic options, text rows, no inner interactive elements
(ARIA forbids interactive descendants inside role="listbox")
- GridList: rows with buttons / links / checkboxes inside (2D nav)
- Select / Combobox: closed picker surface (Listbox-like dropdown)
Gotcha: listbox.svelte and listbox-item.svelte rename `children` to
`bodyContent` on destructure to avoid shadowing the inner
`{#snippet children}` block (same recursion trap documented in
`feedback_button_consumer_pattern.md` Rule 3).
Demo: 6 tabs at canonical depth — Live wires all soma + eidos props,
all 3 ControlVariant chips, 5 size chips, 8 ColorRole chips, single +
multi mode toggle, RTL/horizontal toggle. Sidebar entry added under
Forms (between Combobox and Calendar).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/listbox', label: 'Listbox' },
|
feat(eidos): port GridList (selectable list w/ interactive row children)
Closes #133. Eidos wrapper + types + recipe + canonical-depth demo.
4 parts: Provider, Row, Cell, SelectionCheckbox. Soma owns selection
(none/single/multiple), 2D keyboard nav (Up/Down rows + Left/Right
cells), Shift/Ctrl range/toggle, Ctrl+A select-all, Escape-clear,
typeahead, hidden form input, Field merge. Eidos adds variant + size +
color + defaultValue (uncontrolled init).
Reference parity (no air baseline):
| Feature | soma | react-aria GridList | this port |
|---|---|---|---|
| selectionMode none/single/multiple | ✓ | ✓ | ✓ |
| bind:value (controlled) | ✓ | ✓ | ✓ |
| defaultValue (uncontrolled init) | ✗ | ✓ | ✓ added (parity with Combobox/Select) |
| Item with interactive descendants | ✓ | ✓ | ✓ |
| 2D keyboard nav (Up/Down rows, L/R cells) | ✓ | ✓ | ✓ |
| Shift/Ctrl range/toggle | ✓ | ✓ | ✓ |
| Ctrl+A select-all (multiple) | ✓ | ✓ | ✓ |
| Escape-clear | ✓ | ✓ | ✓ |
| Typeahead | ✓ | ✓ | ✓ |
| ARIA grid/row/gridcell | ✓ | ✓ | ✓ |
| Field merge (disabled/invalid/required/readonly) | ✓ | ✗ | ✓ |
| Form participation (hidden input) | ✓ | ✗ | ✓ |
| Drag-to-reorder | ✗ | ✓ | ⚠ deferred (compose w/ future DragDrop) |
| onAction (row click → navigate) | ✗ | ✓ | ⚠ deferred (use button/link inside Cell) |
| selectionBehavior toggle/replace | ✗ implicit | ✓ | ⚠ deferred |
| disabledBehavior 'selection'/'all' | ✗ skip-only | ✓ | ⚠ deferred |
| Async / infinite loading | ✗ | ✓ | ⚠ deferred |
| Empty state slot | ✗ | ✓ | ⚠ document `data-empty` styling |
Naming: keeps soma's part names (Provider, Row, Cell, SelectionCheckbox).
react-aria's "GridListItem" maps to `Row`; soma's vocabulary is more
precise and consistent with future Table / TreeGrid in the same family.
Variant set:
- variant: ControlVariant (surface/outline/ghost), default 'surface'
- size: xs..xl (5-step, mirrors Combobox/Listbox)
- color: ColorRole (primary/secondary/neutral/affirm/fulfill/risk/threat/loss)
- block: stretch to container width
Disambiguation (in index.ts comment): use GridList for rows with
interactive children; use Listbox for atomic text options (ARIA
forbids interactive descendants inside role="listbox"); use
Select/Combobox for closed-picker patterns; use Table for tabular
data with columns + sort.
Visual reuse:
- SelectionCheckbox uses the same `mask-image: url(svg-check)` pattern
as the shared menu-indicator partial (commit a2709476). Filled
accent box → white ✓ on `[data-state='checked']`.
- Row hover/highlight tokens mirror Combobox.Item / Listbox.Item so
the form-control surface family stays coherent.
- Same `max-block-size: min(60vh, 22rem) + overflow-y: auto` scroll
contract as Listbox (commit 830025b4).
Sidebar entry under Forms (between Listbox and Calendar).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/grid-list', label: 'Grid list' },
|
|
|
|
|
|
{ slug: '/uix/components/calendar', label: 'Calendar' },
|
feat(eidos): port range-calendar from soma + extract shared partial
Standalone eidos `<RangeCalendar>` wrapper over the soma range-calendar
primitive. Adds size / variant / color cascades; otherwise pass-through
14 compound parts (Provider, Header, Heading, PrevButton, NextButton,
MonthSelect, YearSelect, Grid, GridHead, GridBody, GridRow, HeadCell,
Cell, Day).
## Feature comparison
| Feature | bits-ui | ark-ui | react-aria | Eidos |
|----------------------|---------|--------|------------|-------|
| Range start/end | ✓ | ✓ | ✓ | ✓ |
| Hover preview state | ✓ | ✓ | ✓ | ✓ |
| minDays / maxDays | — | — | ✓ | ✓ |
| allowSingleDay | ✓ | ✓ | ✓ | ✓ |
| preventDeselect | ✓ | ✓ | — | ✓ |
| Multi-month display | ✓ | ✓ | ✓ | ✓ |
| Week-number column | — | — | ✓ | ✓ |
| Month/Year selects | — | ✓ | — | ✓ |
| Disabled / readonly | ✓ | ✓ | ✓ | ✓ |
| isDateUnavailable | ✓ | ✓ | ✓ | ✓ |
| Holiday markers | — | — | — | ✓ |
| Locale / dir | ✓ | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 4 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
## Range-cells partial extracted
The recipe at `src/uix/eidos/components/range-calendar/range-calendar.css`
now owns ALL `[data-range-calendar-*]` selectors — the shared rules
that previously lived inside `date-range-picker.css` (lines 537-834)
moved here. Both consumers (the standalone `<RangeCalendar>` and
`<DateRangePicker>`'s popover content) stamp the same data-attrs, so the
same selectors paint without duplication.
`date-range-picker.css` drops 298 lines of duplication; it keeps only
the picker-specific scope overrides (`[data-date-range-picker-calendar]`
with no chrome, max-content sizing, transparent bg).
## Morfo
Extended `rangeCalendarMorfo.scope` to include `'eidos'` so the new
wrapper appears in the eidos-layer inventory and the demo Morfo tab
declares it correctly.
## Demo
Canonical 6-tab depth (~600L) at `/uix/components/range-calendar`. Live
tab exposes every range-specific prop: minDays / maxDays sliders,
allowSingleDay, preventDeselect, numberOfMonths chips (1 / 2 / 3),
pagedNavigation, fixedWeeks, showWeekNumbers, disableDaysOutsideMonth,
weekStartsOn, disabled, readonly, locale, plus size / variant / color
cascades.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/range-calendar', label: 'Range calendar' },
|
feat(month-grid, year-grid): standalone primitives parallel to date-picker views
DatePicker.MonthView / YearView require a DatePickerProvider context and
can't be used standalone. This commit introduces MonthGrid and YearGrid
as independent primitives that accept value / placeholder / minValue /
maxValue as props and work anywhere.
morfo
- New morfo/components/month-grid.ts and year-grid.ts.
- scope: ['soma','sema'], apg: grid pattern.
- 7 parts each: Provider (role=application) / Header / Heading /
PrevButton / NextButton / Grid (role=grid) / Cell (role=gridcell).
- Events: commit-set (cell click) + nav-step (prev/next).
- Cell data-attrs: data-value, data-selected, data-current, data-disabled,
data-focused.
- Keyboard map covers arrows / Home / End / PageUp/Down / Enter / Space.
soma
- New soma/components/{month-grid,year-grid}/ with provider class +
langs + types + exports + 7 thin svelte wrappers each.
- MonthGridProvider: 12-cell grid keyed to placeholder.year; localized
month names via Intl.DateTimeFormat with configurable monthFormat
('long'|'short'|'narrow'). Prev/Next step ±1 year.
- YearGridProvider: decadeSize-cell grid (default 12); page anchored at
placeholder.year - (placeholder.year % decadeSize). Prev/Next step ±
decadeSize.
- Both expose Cell sub-provider with snippetProps {selected, disabled,
current, value, label} + roving tabindex + RTL-aware keyboard nav.
eidos
- New eidos/components/{month-grid,year-grid}/ — namespace-style index
with size / variant / color visual props + dedicated CSS reusing the
--calendar-* token family.
- Each grid renders Header (Prev/Heading/Next) + 3-col Grid.
demo
- New /uix/components/month-grid and /uix/components/year-grid demo
pages with the standard live/api/morfo/sema/recipe/a11y tabs,
interactive controls for every public prop, soma+eidos snippets that
reactively rebuild from controls, and sema event play buttons.
Verified: svelte-check --threshold error -> 0 errors / 0 warnings /
4572 files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/month-grid', label: 'Month grid' },
|
|
|
|
|
|
{ slug: '/uix/components/year-grid', label: 'Year grid' },
|
|
|
|
|
|
{ slug: '/uix/components/date-field', label: 'Date field' },
|
|
|
|
|
|
{ slug: '/uix/components/date-range-field', label: 'Date range field' },
|
uix: date-picker + date-range-picker components + component audit infra
Two new Eidos components shipped end-to-end (wrapper + recipe + demo +
Soma provider hardening) plus a checklist-driven audit pipeline that
scores all 67 morfo components against doctrinal completion criteria.
New components:
- date-picker: full popover-anchored picker over date-field + calendar,
with calendar/content/trigger parts and demo route.
- date-range-picker: standalone wrapper with own calendar/grid/segment
surface, demo route, and recipe CSS.
- Both wrappers follow Option C disciplined (root + parts attached via
explicit assignment, no Object.assign).
Supporting Soma changes:
- range-calendar provider tightened (211 LOC of behavior, 167 LOC of
tests), README brought up to component doctrine.
- date-field, date-picker, date-range-field, date-range-picker Soma
providers + READMEs updated for new wrappers.
- popover provider/close gain props needed by the picker wrappers.
Morfo updates:
- date-picker / date-range-picker / range-calendar morfos refined for
the new APIs (parts, events, ARIA).
Audit infrastructure (new):
- src/uix/COMPONENT_COMPLETION_CHECKLIST.md — 81 doctrinal rules across
morfo / eidos wrapper / recipe CSS / demo / README / cross-layer
scripts. Each rule keyed to active_architecture.md, GUIA_IMPLEMENTACION
and DEMO_AUTHORING_GUIDE.
- scripts/component-audit.ts + `npm run component:audit` — regex parser
over all 67 components, emits tmp/component-audit.md with summary
scoreboard + per-component findings. Validates against canonical
SEMA_FAMILIES / SEMA_VERBS / ARCHETYPE_VOCABULARY / INTENTS.
- Initial baseline: 1 PASS, 64 NEEDS-WORK, 2 BROKEN (tooltip,
date-range-picker). Top systemic gaps: translations.label (49),
README Gaps/Comparativa/Baseline sections (87 combined), keyboard
/event ratio under-declaration (15), apg URL absent (19).
Misc:
- src/uix/kimi-audit-eidos.md — supplementary audit notes.
- .gitignore: ignore .codex-* agent scratch artifacts at repo root.
- continue.md + READMEs updated through the migration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/date-picker', label: 'Date picker' },
|
|
|
|
|
|
{ slug: '/uix/components/date-range-picker', label: 'Date range picker' },
|
|
|
|
|
|
{ slug: '/uix/components/time-field', label: 'Time field' },
|
|
|
|
|
|
{ slug: '/uix/components/time-range-field', label: 'Time range field' },
|
feat(eidos): add TimePicker wrapper + demo following P-1..P-5 picker contract
Builds on the existing soma TimePicker slider-based clock (HourSlider /
MinuteSlider / SecondSlider + DayPeriodToggle). Adds the canonical
picker shell:
- mode='inline' | 'modal' on the Provider, propagated to popover.modal
- Provider helpers commit() / cancel() / clear() with watch(open)
snapshot for cancel-revert
- Footer composition (TimePicker.Footer / Clear / Cancel / Close as
pure-composition Eidos parts; no *Button root props)
- New TimePicker.Clock soma component that carries the
data-time-picker-clock contract attr, wraps the slider stack
- Eidos shell: Provider visual root, Trigger (clock icon), Content
(popover content), Clock, Footer + action parts, slider/toggle
pass-throughs
Renews the time-picker morfo to declare Provider / Trigger / Clock /
DayPeriodToggle / DayPeriodItem / Footer / Clear / Cancel / Close,
plus all 6 commit/emerge events (open + close-commit/cancel/dismiss/
dismiss-outside + commit-clear). apg=dialog-modal.
Demo at /uix/components/time-picker exercises all controls, Footer
composition switches, soma + eidos code snippets, morfo / sema / recipe
/ a11y tabs. README documents the picker contract.
67/67 component-audit PASS, 39/39 morfo:check PASS, svelte-check
0 errors / 0 warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/time-picker', label: 'Time picker' },
|
|
|
|
|
|
{ slug: '/uix/components/time-range-picker', label: 'Time range picker' },
|
|
|
|
|
|
{ slug: '/uix/components/color-field', label: 'Color field' },
|
feat(eidos): add ColorPicker wrapper + demo
Composite popover: 2D saturation/brightness area, hue + alpha channel
sliders, preset swatches, eye-dropper, and a segmented channel input
composed from ColorField. Builds on the existing soma color-picker
(23 parts) — eidos layer is mostly thin pass-throughs adding visual
chrome.
What landed
-----------
- New `src/uix/eidos/components/color-picker/`:
- Root visual (Provider) + visual-context (size/variant/color)
- Trigger with a default swatch dot tinted from
`--cp-trigger-color` (the air pattern from morfo-runtime)
- Content (popover wrapper), Area (+ Background + Thumb),
TransparencyGrid
- ChannelSlider (+ Track + Thumb) — generic per-channel slider;
default-composes Track+Thumb when no children
- SwatchGroup + SwatchTrigger + Swatch + SwatchIndicator
- EyeDropper (Pipette icon by default)
- ChannelInput + ChannelSegment + FormatSelect (composed from
ColorField parts; same provider injected automatically)
- Label, Control, ValueSwatch, ValueText, HiddenInput
- Recipe section `color-picker` in `recipes/base.ts` (regenerated
`generated/base.css`) — trigger, content, area, channel slider,
swatches, eye-dropper tokens.
- CSS reuses color-field tokens for the segmented input portion and
adds picker-specific visuals. Transparency checker on trigger swatch
+ value swatch + alpha slider track via conic-gradient pattern.
- Morfo updates: scope `['soma','sema']`, `apg: dialog-modal`, added
`commit-set` event so audit classifies it as interactive.
- Soma exports: ColorPickerProvider class so eidos Trigger can read
the current value for the swatch tint.
- Demo at `/uix/components/color-picker` with the standard 6 tabs;
live stage exercises area drag + channel sliders + preset swatches
+ eye-dropper + format switch.
- README documents the surface, comparativa vs air/Ark/Chakra, and
flags the picker contract (mode/Footer/commit/cancel) as gap to
align with date/time-picker in a follow-up.
Checks
------
- 67/67 component-audit PASS
- 40/40 morfo:check PASS (new color-picker demo validates against
morfo DOM)
- svelte-check 0 errors / 0 warnings
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/color-picker', label: 'Color picker' },
|
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
{ slug: '/uix/components/picker', label: 'Picker (deferValue)' },
|
|
|
|
|
|
{ slug: '/uix/components/natural-time-picker', label: 'Natural time picker' },
|
|
|
|
|
|
{ slug: '/uix/components/stepper', label: 'Stepper' },
|
|
|
|
|
|
{ slug: '/uix/components/tag-group', label: 'Tag group' },
|
|
|
|
|
|
{ slug: '/uix/components/tags-input', label: 'Tags input' },
|
feat(pin-input): port from soma to eidos (Tier 1 sprint, 5/5 — DONE)
Multi-cell OTP / verification code input. Two parts (Provider, Cell)
with a snippet-driven cell iteration.
Architecture:
- A transparent absolutely-positioned <input> captures every
keystroke, paste and IME event (soma).
- Visible bordered cells follow the hidden input's caret via
data-active / data-filled emitted by the morfo.
- Single-input approach (shadcn / Bits family) — preserves paste
distribution, autocomplete='one-time-code' for SMS autofill on
iOS/Android, and IME quality.
Eidos surface:
- size: 'sm' | 'md' | 'lg' | 'xl' (responsive), cascades cell
dimension + glyph size tokens to all cells
- Recipe paints square bordered cells with focus ring tracking
data-active, subtle bg lift on data-filled, threat-toned border
when [data-invalid] cascades from the provider
Morfo:
- scope: ['soma'] → ['soma', 'sema', 'eidos']
- apg URL added (Textbox pattern — OTP has no formal ARIA pattern)
- Passive at the morfo layer (no events array; perceptual feedback
via the commit family base when onComplete fires)
- README documents the passive classification
Comparativa: aligned with shadcn / Bits UI (single hidden input).
Ark and Chakra use one input per cell which loses paste / IME
quality.
Sidebar nav: 'Pin input' added under Forms (next to Tags input).
=== Sprint Tier 1 — DONE ===
1. toggle-group 61ffbade
2. alert-dialog d5630a52
3. dropdown-menu 4e477e63
4. context-menu 21eb8a62
5. pin-input (this)
Checks: svelte-check 0 errors, component:audit 91/91 PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/pin-input', label: 'Pin input' },
|
|
|
|
|
|
{ slug: '/uix/components/file-upload', label: 'File upload' }
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
kind: 'group',
|
|
|
|
|
|
heading: 'Navigation',
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{ slug: '/uix/components/anchor-nav', label: 'Anchor nav' },
|
|
|
|
|
|
{ slug: '/uix/components/breadcrumb', label: 'Breadcrumb' },
|
feat(eidos): port NavigationMenu (top-level site navigation w/ mega-menus)
#132. Eidos wrapper + types + recipe + canonical-depth demo. 7 parts:
Provider, List, Item, Trigger, Content, Link, Indicator. Soma owns
active-item state, hover delays (openDelay/skipDelayDuration/closeDelay),
hoverEnabled gate, loop arrow nav, data-motion directional swap,
Indicator rect CSS vars. Eidos paints the bar + trigger chrome +
Content panel + slide animations.
Reference parity (no air baseline — port directly from soma):
| Feature | soma | Radix | bits-ui | this port |
|---|---|---|---|---|
| Provider + List + Item + Trigger + Content + Link + Indicator | ✓ | ✓ | ✓ | ✓ |
| Hover open (openDelay/skipDelayDuration/closeDelay) | ✓ | ✓ | ✓ | ✓ |
| hoverEnabled (click-only mode) | ✓ | ⚠ | ✓ | ✓ |
| orientation horizontal + vertical | ✓ | ✓ | ⚠ | ✓ |
| dir / RTL | ✓ | ✓ | ✓ | ✓ |
| Indicator CSS vars (x/y/w/h rect) | ✓ | ✗ | ✗ | ✓ |
| data-motion directional swap (from-/to-start/end) | ✓ | ✓ | ⚠ | ✓ |
| forceMount on Content + Indicator | ✓ | ✓ | ✓ | ✓ |
| ARIA <nav> + translated aria-label | ✓ | ⚠ | ⚠ | ✓ |
| Keyboard nav (Arrow/Home/End/Enter/Space/Esc, ArrowDown → Content) | ✓ | ✓ | ✓ | ✓ |
| Sub (nested viewports) | ✗ | ✓ | ✓ | ⚠ deferred (matches soma) |
| Viewport (shared swap pane) | ✗ | ✓ | ✓ | ⚠ deferred |
| Modal variant (focus trap) | ✗ | ✓ | ✗ | ⚠ deferred |
| SafePolygon hover bridge | ✗ | ✗ | ✗ | ⚠ deferred |
Naming: matches soma's part names exactly. Both Trigger (button) and
Link (anchor) stay native — NOT routed through <Button> — because
top-nav has flat/borderless chrome distinct from action buttons. The
Link automatically picks up `aria-current="page"` styling for the
active route.
Variant set: only `size: 'sm' | 'md' | 'lg'` (no xs/xl). No
variant/color — NavigationMenu is chrome surface, not actionable
intent.
Fresh recipe — does NOT reuse dropdown-menu (floating click-driven)
or menubar (roving tabindex bar). NavigationMenu lives inline in the
page; per-item Content panels render via Floating but conceptually
attach to the bar. Indicator is a 2px underline that slides via CSS
vars (--navigation-menu-indicator-{x,y,w,h}) written by soma. Trigger
chevron rotates on [data-state='open']. Content animates via
data-motion directional slide on sibling swap.
Sidebar entry added under Navigation (between Pagination and a future
NavigationMenu entry).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/pagination', label: 'Pagination' },
|
uix(nav-tree): F1.8 CERRADA · demo + navegador + review adversarial
Cierra el árbol de navegación data-driven (E-1) de F1: demo canónica de 9
pestañas con el mapa real de docs (43 nodos, 3 niveles), verificación en
navegador real y review adversarial (5 dimensiones × 3 verificadores
escépticos; 22 hallazgos brutos, 10 confirmados) con todos los confirmados
arreglados.
Arreglos del review
- sema: la parte `group` —target de los eventos emerge— se registraba SIN
`ref`, así que `runtime.trigger` lanzaba `SomaRuntimeTargetError` en
silencio y el pack no sonaba nunca (cero `data-event-*` en el grupo frente
a los de collapsible). El provider posee ahora el ref del `<ul>`.
- eidos: en una fila navegable el chevron resolvía `inline-size: 100%` como
flex-basis y ocupaba media fila (101 de 231 px en «Soma»), robándole clics
al enlace. Toggle compacto con suelo de diana de 24 px (WCAG 2.5.8).
- soma: una clave duplicada podía volver cíclico `parentByKey` y colgar la
pestaña dentro de `trailKeys` (deriva en render) → clave sufijada + aviso
del logger + guarda de ciclo en el paseo.
- soma: el colapso es CONTEXTUAL (recuerda el `activeKey` bajo el que se
hizo): cerrar la sección que lees se respeta, pero caduca al navegar
DENTRO del grupo, para que la página actual nunca quede sin fila visible.
Sigue siendo query pura, sin `$effect` que escriba estado.
- soma: `child` recibe también `children` (el árbol renderizado); antes
dejaba el landmark vacío, porque un árbol data-driven no lo puede
reautorar el consumidor.
- morfo + langs: el nombre accesible del chevron se declara en el contrato y
se localiza («Alternar sección {label}»); ya no duplica el del enlace.
- eidos: RTL completo — el glyph espeja solo (bordes lógicos), lo que no
espeja es el giro, así que bajo `[dir='rtl']` las dos rotaciones se
intercambian. El Gap «dirección del chevron en RTL» queda RESUELTO.
- demo: paridad de snippet con los controles vivos; fuera el token fantasma
`--nav-tree-rail-width` del docblock del recipe.
badge en v1 (decisión del usuario, delegada)
Está en el suelo de paridad del dossier §P5 (los 6 refs lo llevan). Se
resuelve con SNIPPET, no con recursión a nivel de eidos: el morfo declara la
parte `badge`, soma renderiza el snippet recibido (sin él, el valor crudo —
sigue siendo headless) y el wrapper de eidos pasa el `Badge` canónico. Va
DENTRO del control de la fila, así su texto entra en el nombre accesible
(«TSC, New, enlace»). `disabled` se descarta en v1 (fuera del suelo, y un
enlace de navegación deshabilitado es semánticamente dudoso); ambos quedan
registrados en la tabla de Gaps.
Verificado: `component:audit` PASS · eidos-lint 26 morfo-backed / 0 invalid ·
`svelte-check` 0 errores en estos archivos · `vitest src/uix/eidos` 353/353 ·
navegador real (Playwright): trail auto-expandido, sema estampando en el
grupo, teclado nativo, foco visible, claro y oscuro, RTL, 375 px sin
desbordes, 0 errores de consola.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
|
|
|
|
{ slug: '/uix/components/navigation-menu', label: 'Navigation menu' },
|
uix(sidebar): F1.7 eidos + demo · raíl, modo icono y submenú flotante
Segunda tanda del sidebar: la capa visual, la demo canónica de 9 pestañas y
la verificación en navegador real. Con esto el componente está vivo; queda el
review adversarial.
Eidos
- Recipe con los dos ejes: off-canvas por `transform` (trabajo de compositor,
el inset no reflowea) y `width` animado SOLO en modo icono, donde el inset
sí tiene que seguir al raíl. El raíl NO lleva `will-change` (una franja
fina tiembla a DPR fraccionario — memoria NavMenu).
- Anchos como TOKENS: `--sidebar-width` 16rem · `--sidebar-width-icon` 3rem ·
`--sidebar-width-mobile` 18rem, más raíl, gaps, altura de fila y colores.
- Tres composiciones que la capa headless no puede hacer: `Drawer` para la
presentación móvil, `Tooltip` para nombrar filas en modo icono y `Badge`
para los contadores. El trigger compone `Button` por el patrón `child`.
- Convención de icono documentada: sobrevive el PRIMER elemento de la fila.
Arreglos que destapó el navegador
- **`mobile` en SSR**: sin viewport, `isAtLeast` responde «por debajo de todo»
→ el HTML salía con `data-mobile` y la hidratación NO lo corrige (no diffea
atributos y el valor ya no cambia). Viewport desconocido = ESCRITORIO.
- **Estado móvil propio** (arranca cerrado): en escritorio `open` es
preferencia de layout persistida; en móvil sería un overlay tapando la
página, que jamás puede ser el estado inicial. `onOpenChange` solo se
dispara con el estado de escritorio.
- **`open` controlado/no controlado**: se adopta el patrón de la casa
(`$bindable` con default, el provider escribe siempre) — mi versión previa
con estado interno no propagaba `bind:open`.
- **El submenú flotante no se posicionaba**: `FloatingAnchor` resuelve su
provider por CONTEXTO y el eidos envuelve la fila en un `Tooltip`, que
publica el suyo → el ancla caía en el tooltip y el submenú se quedaba en
`translate(0,-200%)`. Ahora el ancla se cablea directamente al provider
flotante del item. Además el `<ul>` ya no se posiciona a sí mismo (colapsaba
el wrapper a tamaño cero) y toma elevación del plano `overlay` por
`data-depth`, como cualquier panel flotante del ecosistema.
- **Tooltip vs submenú**: una fila con submenú no lleva tooltip en modo icono
(dos superficies sobre el mismo ancla se pisan) — nuevo `useSidebarItemOr()`.
- El item envuelve (`flex-wrap`) para que el submenú caiga en su propia línea
en vez de ponerse al lado de la fila.
Demo: shell de app real (grupos, badge, acción, submenú, fila deshabilitada),
controles vivos de los tres props y las 9 pestañas del canon.
Verificado en navegador real (Playwright): dos landmarks nombrados en el
idioma activo · `aria-current="page"` en la fila activa · cada menú etiquetado
por SU grupo · raíl enfocable que alterna con Enter · colapso 256→48px ·
off-canvas por transform · submenú flotante anclado a su fila y Escape que
devuelve el foco · sema `emerge-expand/collapse` estampando en `panel` ·
móvil 600px = Drawer cerrado con el landmark dentro · RTL espeja el shell
entero · oscuro · 0 errores de consola.
Gates: `component:audit --only sidebar` PASS 0E/0W · eidos-lint 45
morfo-backed / 0 invalid / 0 class-hooks · `svelte-check` 0 errores propios ·
`vitest src/uix/eidos` 353/353 · `contracts.test` solo con fallos ajenos
(menubar, radio-group y las claves camelCase de aura).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
|
|
|
|
{ slug: '/uix/components/nav-tree', label: 'Nav tree' },
|
uix(skip-link): el tier decidió en F2.1 que los shells lo poseen, y no había nada que poseer
Canon nuevo, eidos-native (1 parte, 0 eventos): la pieza de WCAG 2.4.1 que
`site-header` difirió a los shells hace tres semanas y que `app-shell` (F3.1)
necesita para existir. Nace de su fase 0, decisión Q4 firmada.
Lo que decide, y por qué:
- **Las palabras son suyas.** «Ir al contenido principal» no es copy: es el
nombre de un contrato. La regla del tier (B-7 / D-BLK.5) apunta a esta puerta
— si un string parece inevitable, lo posee el componente canónico vía
`texts:` + langs. Por eso el prop es `region`, no `label`, y el vocabulario
es la lista de landmarks de la APG. Cada clave es una FRASE ENTERA: en
castellano el artículo se contrae con la región, así que un «Ir a» +
sustantivo saldría mal en media catálogo.
- **`region` no se estampa**: elige un texto y ninguna capa lo lee. Declararlo
repetiría el error que `Affix` corrigió el 2026-08-15.
- **No compone `Link`**: `Link` es tinta en línea y todo su API describe texto
dentro de un párrafo. Esto es cromo que aparece de la nada, con su geometría
y su suelo; envolverlo sería pisar todos sus props y aun así dejarlos en la
API pública. Los componentes del canon renderizan sus nativos; son los blocks
los que no pueden.
- **El handler mueve el FOCO**, que es lo que el fragmento no hace: un `href`
desplaza la vista y deja el foco en el enlace, y el siguiente Tab vuelve al
cromo que el lector pidió saltar. El destino se hace enfocable sólo mientras
dura el salto. El `href` se queda: es el camino sin JS.
- **Peldaño propio de z (950), el más alto de la escalera estática.** No puede
compartir el de `affix`: un empate lo rompe el orden del DOM y este enlace es
por definición el primer elemento del documento, así que perdería contra
cualquier aviso fijado debajo — el fallo medido en A-95.
Medido con Playwright (el pane del navegador no tiene el foco del SO, así que
`:focus` nunca casa ahí): en reposo 1x1 con clip-path; enfocado `position:
fixed`, z 950, píldora de 181x36 sobre `primary-solid` con tinta blanca y
anillo de foco, igual en claro y en oscuro; el salto deja el foco en
`#demo-main` y el `tabindex` temporal se limpia en el blur; en RTL la píldora
espeja al otro borde (x 338 -> 761) sin una segunda regla.
Y la demo se corrigió a sí misma: decía «Tab desde el botón de abajo» y la
medición enseñó que desde ahí el foco va hacia delante. El botón sube encima
del marco, y de paso salió la otra mitad de la lección — por tabulación NUNCA
se aterriza en el `main`, porque no tiene nada enfocable. Justo por eso el
destino necesita el `tabindex` prestado.
Guards: `component:audit --only skip-link` PASS · `morfo:check` PASS ·
`eidos-lint` 2 selectores morfo-backed, 0 inválidos · `docs:check` 0/0 ·
`svelte-check` 72, ninguno aquí.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
{ slug: '/uix/components/sidebar', label: 'Sidebar' },
|
|
|
|
|
|
{ slug: '/uix/components/skip-link', label: 'Skip link' }
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
kind: 'group',
|
|
|
|
|
|
heading: 'Media',
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{ slug: '/uix/components/avatar', label: 'Avatar' },
|
feat(eidos): Card, Avatar ext, Image, SText, Badge, service components + dates engine fixes
Sprint completo:
- Card (eidos + sema): elevation/intent/interactive, eventos select+activate.
- Avatar: ring, AvatarBadge, AvatarGroup separados; demo Avatar standalone.
- Image: status surface (idle/loading/loaded/error) consumiendo soma ImageProvider.
- SText + STextVirtualList: canvas-text engine portado de morfo-driven-soma como
componentes separados para optimizar bundle (no se importa canvas si no se usa).
- Badge: primitivo eidos puro.
- Trans, FormatNumber, FormatDate, RelativeTime: 4 service components greppables
alrededor de uix.langs y uix.format. Nav group "Utilities".
Bugs fix arts/format/dates (engine-dates.ts):
1. Preset / per-axis conflict: Intl.DateTimeFormat rechaza dateStyle/timeStyle
con cualquier per-axis (weekday/era/year/month/day/dayPeriod/hour/minute/
second/fractionalSecondDigits) o con timeZoneName. Engine drop-ea ambos
presets por defecto cuando detecta conflict (regla estricta — un solo
per-axis hace ilegales los dos presets).
2. Per-call locale override: formatDate/Time/DateTime aceptan tercer arg
locale?: string. No muta engine state. Cache (getCachedDateFormat) keyea
por (locale x options). resolveCallHourCycle: cuando hay locale override
y pref auto, el ciclo horario se resuelve desde el locale OVERRIDE.
3. withHourCycle precedence: caller hourCycle/hour12 ahora gana sobre
preferencia del engine. Antes spread mal ordenado sobrescribia 'h12' del
caller con 'h23' del engine cuando locale resolvia a 24h.
FormatDate component vuelve a rutear siempre por uix.format.dates
(cache + hourCycle + locale reactivo), pasando locale como tercer arg.
Demos service components autonomos del toolbar global: cada uno owns su
demoLocale state con 7 chips (es-ES/en-US/en-GB/de-DE/fr-FR/ja-JP/ar-EG).
FormatDate stage filtra dateStyle/timeStyle por mode + chip hourCycle anadido.
Tests: 14/14 passing en engine-dates.test.ts (+6 nuevos: conflict handling,
locale override, hourCycle precedence).
Docs: arts/format/README.md (handoff 2026-05-25 con las 3 reglas),
README.md por service component, project_service_components_sprint_2026-05-25.md,
feedback_service_demos_own_locale.md (pattern reusable).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/avatar-group', label: 'AvatarGroup' },
|
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/icon', label: 'Icon' },
|
feat(eidos): Card, Avatar ext, Image, SText, Badge, service components + dates engine fixes
Sprint completo:
- Card (eidos + sema): elevation/intent/interactive, eventos select+activate.
- Avatar: ring, AvatarBadge, AvatarGroup separados; demo Avatar standalone.
- Image: status surface (idle/loading/loaded/error) consumiendo soma ImageProvider.
- SText + STextVirtualList: canvas-text engine portado de morfo-driven-soma como
componentes separados para optimizar bundle (no se importa canvas si no se usa).
- Badge: primitivo eidos puro.
- Trans, FormatNumber, FormatDate, RelativeTime: 4 service components greppables
alrededor de uix.langs y uix.format. Nav group "Utilities".
Bugs fix arts/format/dates (engine-dates.ts):
1. Preset / per-axis conflict: Intl.DateTimeFormat rechaza dateStyle/timeStyle
con cualquier per-axis (weekday/era/year/month/day/dayPeriod/hour/minute/
second/fractionalSecondDigits) o con timeZoneName. Engine drop-ea ambos
presets por defecto cuando detecta conflict (regla estricta — un solo
per-axis hace ilegales los dos presets).
2. Per-call locale override: formatDate/Time/DateTime aceptan tercer arg
locale?: string. No muta engine state. Cache (getCachedDateFormat) keyea
por (locale x options). resolveCallHourCycle: cuando hay locale override
y pref auto, el ciclo horario se resuelve desde el locale OVERRIDE.
3. withHourCycle precedence: caller hourCycle/hour12 ahora gana sobre
preferencia del engine. Antes spread mal ordenado sobrescribia 'h12' del
caller con 'h23' del engine cuando locale resolvia a 24h.
FormatDate component vuelve a rutear siempre por uix.format.dates
(cache + hourCycle + locale reactivo), pasando locale como tercer arg.
Demos service components autonomos del toolbar global: cada uno owns su
demoLocale state con 7 chips (es-ES/en-US/en-GB/de-DE/fr-FR/ja-JP/ar-EG).
FormatDate stage filtra dateStyle/timeStyle por mode + chip hourCycle anadido.
Tests: 14/14 passing en engine-dates.test.ts (+6 nuevos: conflict handling,
locale override, hourCycle precedence).
Docs: arts/format/README.md (handoff 2026-05-25 con las 3 reglas),
README.md por service component, project_service_components_sprint_2026-05-25.md,
feedback_service_demos_own_locale.md (pattern reusable).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/image', label: 'Image' },
|
feat(uix): cropper, image-picker, image-adjustments + reusable zoom-pan layer
Three new components across all 4 layers (morfo/soma/sema/eidos) + demos + langs + READMEs:
- ImageAdjustments — reusable image-filter sliders subcomponent.
- ImagePicker — composes file-upload + image + image-adjustments (fill modes, rotation).
- Cropper — Ark image-cropper anatomy: normalized 0-1 crop rect, rect + round shapes,
8 resize handles, fixedSize move-only (avatar), canvas Blob output (5-arg drawImage
for SVG safety), wheel/pan zoom, two-zone gesture (selection moves the area, the
background pans to set focus; img draggable=false guards native DnD per zag).
New reusable soma/layers/zoom-pan: cursor-centered zoom, clamped pan, transform +
viewport-frac-to-content-frac mapping. Image-viewer / diagram-pan / map can reuse it.
sema: add 'zoom' verb to the handle family (direct manipulation, documented extension).
slider: track visible at zero (gray, not white), thumb lifts on drag.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
{ slug: '/uix/components/image-adjustments', label: 'Image adjustments' },
|
|
|
|
|
|
{ slug: '/uix/components/image-picker', label: 'Image picker' },
|
|
|
|
|
|
{ slug: '/uix/components/cropper', label: 'Cropper' },
|
|
|
|
|
|
{ slug: '/uix/components/carousel', label: 'Carousel' },
|
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
{ slug: '/uix/components/qr-code', label: 'QR code' },
|
feat(eidos): `Barcode` — el código de barras 1D, con encoder propio y cero dependencias
Ocho simbologías desde su estándar ISO publicado (Code 128 con auto-switching
A/B/C, EAN-13/8, UPC-A/E, Code 39, ITF/ITF-14) en `$libs/barcode`: tablas
transcritas de la especificación o derivadas de su regla de construcción, sin
una sola dependencia npm añadida. JsBarcode y bwip-js son referencia de
corrección, jamás import — la misma doctrina con la que `$libs/qr` usó a Nayuki.
Ningún headless del mercado trae barcode: el terreno lo ocupan encoders
imperativos sin anatomía ni a11y.
- **La física manda sobre la ergonomía visual.** La dimensión X (`moduleWidth`)
es el ancla de escaneabilidad y la altura de barra un eje libre — un escáner
lee una franja horizontal. Por eso la geometría es px absolutos en el viewBox
(precedente: la familia `chart`) y no el token `size` cuadrado del QR:
colapsar los dos ejes en uno haría que la X dependiera de la altura, que es
justo lo que no puede flotar. La zona muda va en módulos, con el mínimo de
cada estándar.
- **Lo que hace reconocible a la familia.** Las guardas EAN/UPC bajan hasta la
línea base y los dígitos se imprimen dígito-bajo-dígito sobre su celda de 7
módulos, en mono (la HRI del estándar es OCR-B, de ancho fijo). ITF-14 lleva
su barra portadora por fuera de la zona muda, que no puede comerse.
- **Un valor no codificable es un ESTADO, no una excepción.** `encode` lanza un
`BarcodeError` tipado; el wrapper lo captura, marca `data-invalid`, avisa por
`onInvalid` (deduplicado por razón: `encoded` es un objeto nuevo por
pulsación) y deja un marco placeholder decorativo. Un EAN a medio teclear es
un paso normal de edición; dejarlo reventar dentro de un `$derived` tumbaría
la página.
- **`'text'` entra en `MorfoElement`** — y también en el schema de sium, que
tiene su propia lista literal. Añadir solo la unión de TypeScript compila y
falla en runtime; lo cazó `morfo:check`, no `npm run check`.
Verificación: 48 tests de encoder en tres capas (invariantes estructurales de
cada tabla · vectores publicados transcritos aparte · decoders independientes
que releen la retícula), la lectura real confirmada con escáner por el usuario,
y la retícula decodificada desde el SVG ya pintado en navegador. `component:audit`
PASS con 0 errores; los 2 warnings (D-1.5 / D-4.3) son los mismos que da
`button`, la demo canaria: sus regex buscan la forma v1 inline del
MutationObserver y del `emit`, que la v2 movió al harness compartido.
Demo v2 canónica de 9 pestañas. El alcance diferido (Codabar/MSI/Pharmacode,
GS1-128, add-ons EAN-2/5, y las 2D como componentes aparte) queda registrado en
`next-features.md` §10 — el hueco de numeración se cierra cuando el track de
blocks commitee sus §8/§9, hoy sin commitear.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
{ slug: '/uix/components/barcode', label: 'Barcode' },
|
refactor(skin-media-player): un cableado y cuatro pinturas, con el movimiento del sistema
Reescritura completa de la versión A (`bf12b8a57`), que el autor valoró como
duplicación. La medición le daba la razón: `fit()` idéntico en dos ficheros,
`hold()` y `stop()` en otros dos, y `MediaPlayerProvider.require()` en los
cuatro. Eran cuatro componentes compartiendo carpeta, no un skin.
## La forma
Un componente cablea, los aparatos sólo pintan.
- `skin-media-player.svelte` — EL punto de cableado: un `require()`, una
derivación de todo, y las teclas generadas recorriendo un mapa.
- `controls.ts` — datos por aparato: lienzo, teclas `[{id,x,y,w,h,action}]` y la
ranura del volumen.
- `actions.ts` — el vocabulario único (`play·pause·toggle·stop·seek-*·rpm·none`),
con `stop()` y el veto por `preventDefault()` en un solo sitio.
- `type-fit.svelte.ts` — el ajuste de `<text>`, una vez.
- Los cuatro cuerpos: props entran, SVG sale. Cero provider, cero `$effect`,
cero contexto. Añadir un quinto aparato es un SVG y una fila de datos.
`<SkinMediaPlayer>` deja de montar player: es una PIEZA dentro de uno. No monta
`Media`, no monta barra y no decide la forma — `inline/bar/row/card` es el eje
`variant` de `AudioPlayer`, y la piel viaja dentro como hija.
## Movimiento: el del sistema, nada a mano
La versión A traía tres `@keyframes` locales y siete transiciones con números
crudos, ignorando `docs/theming/motion.md` (leída ahora, entera).
- Todo giro continuo es el preset **`spin`**; el respirar de la cinta, **`pulse`**.
El periodo va en la ranura del propio loop (`--motion-loop-spin`: 1.333s a 45
rpm, 1.8s a 33).
- Las transiciones consumen `--duration-*` / `--ease-*` por una ranura `--_smp-*`
por eje.
- DEFECTO FUNCIONAL corregido: el generador apaga los loops por
`[data-motion='reduce']` **y** por la media query; el bloque a mano sólo tenía
la segunda, así que la preferencia UIX en `reduce` sin ajuste del SO no paraba
el plato. Medido en navegador: ahora `animation-name: none`.
- Queda un `@keyframes` local, la aguja del vúmetro, anotado `functional:` y con
el periodo en token: ningún preset representa un instrumento analógico.
## Tres defectos que sólo se ven en movimiento
Los tres pasaban toda comprobación de atributos y sólo aparecieron mirando la
imagen moverse:
1. **La sombra rotaba con el disco.** El `feDropShadow` colgaba del grupo que
gira, así que su desplazamiento orbitaba (se leía como la sombra subiendo y
bajando) y la penumbra de la región del filtro barría las marcas del plato,
que parecían parpadear. El filtro pasa a un padre quieto — un disco es un
círculo, su silueta no cambia. Igual en el brazo.
2. **Las marcas del plato salían disparadas en arco.** Al rehacer se perdió la
regla del estrobo; sin `transform-origin` el preset gira alrededor del centro
del viewBox (500, 440) en vez del centro del plato (406, 468.5).
3. **El tamaño no llegaba.** `max-inline-size` sólo encoge, así que `lg` y `xl`
eran inertes en cuanto la columna era más estrecha (medido: md/lg/xl los tres
a 720px). Ahora `inline-size: min(100%, …)`, base por objeto y factor por
tamaño, una sola fuente en el recipe — la base estaba declarada dos veces,
inline y en CSS, y ganaba la inline.
## Volumen
Primero se compuso el `Knob` del catálogo y era inmanejable: mapea el valor al
ángulo ABSOLUTO del puntero alrededor del centro (`Gesture.rotate`), o sea
orbitar el ratón en torno a un dial de 40px. Sustituido por el `VolumeSlider`
del propio player corriendo en una guía pintada por la máquina. El re-tinte va
sobre el MISMO descendiente que retinta el player (`media-player.css:420`) o su
regla gana por estar más cerca del thumb.
La banda inferior de la pletina se reordenó para alojarlo: vúmetro a media
anchura con el dial recentrado (aguja intacta), deslizador a 42 unidades del
bisel (20px a `md`), contador y piloto +20 a la derecha, la etiqueta REPROD del
piloto fuera, y «MODELO CR-77» al mismo ancho que «Levante» por `textLength` +
`lengthAdjust="spacing"`.
## Prop `labels` y la decisión de idioma
`labels` (default `true`) apaga la serigrafía entera de la máquina. Los rótulos
NO se traducen, y es decisión firmada: son parte del OBJETO, como la marca del
badge. Los nombres accesibles sí siguen el idioma, porque salen del morfo de las
partes compuestas.
## Demo canónica
`web/routes/uix/components/skin-media-player/+page.svelte` con la plantilla v2
de 9 pestañas + su entrada en el sidebar. El banco provisional de
`/otros/players` se borra: duplicaba la demo con DOS players independientes, que
es lo que produjo dos lecturas falsas («no está sincronizado», «fondo negro»).
Los cuatro bocetos del autor se quedan en la carpeta, ya sin ruta.
`preload="none"` en la demo a propósito: un preload de metadata abre una
petición de rango que el navegador aborta, y `smoke` cuenta ese aborto como
fallo same-origin.
## Auditoría — el hueco que queda
`component-guide.md` §5 dice que incluso un átomo pasivo es morfo-first, con un
morfo mínimo `scope: ['eidos']` y `## Passive justification`. Este componente no
lo tiene porque el alcance acordado excluía `src/uix/morfo/`, y la consecuencia
no es cosmética: **tres máquinas no lo ven** — `component:audit` enumera por
ficheros de morfo y no le da fila, `eidos-lint` lo salta («no morfo file») y
`morfo:check` no visita su demo. Los guards transversales sí pasan; los
específicos de componente no se han ejecutado sobre él ni una vez.
Pendiente además: `data-perm-step` en la demo (A37), sin el cual `perm:check` la
salta en silencio.
Verificado: `npm run check` sin errores nuevos (base 72/59) · 77 tests de eidos
(recipe-css-contract · component-api-contract · component-visual-attrs · motion)
· `rtl:check` 0/179 · `docs:check` 0/0 · `layer:check` 0 · `smoke` PASS ·
`morfo:check` 159/159. Y en Chrome, con el ratón: transporte en los dos
sentidos, brazo siguiendo el surco, bobinas por conservación de área, contador
de cinta, y el volumen moviendo `audio.volume` de verdad.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
{ slug: '/uix/components/media-player', label: 'Media player' },
|
|
|
|
|
|
{ slug: '/uix/components/skin-media-player', label: 'Media player skin' }
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
kind: 'group',
|
|
|
|
|
|
heading: 'Status',
|
|
|
|
|
|
items: [
|
feat(eidos): Card, Avatar ext, Image, SText, Badge, service components + dates engine fixes
Sprint completo:
- Card (eidos + sema): elevation/intent/interactive, eventos select+activate.
- Avatar: ring, AvatarBadge, AvatarGroup separados; demo Avatar standalone.
- Image: status surface (idle/loading/loaded/error) consumiendo soma ImageProvider.
- SText + STextVirtualList: canvas-text engine portado de morfo-driven-soma como
componentes separados para optimizar bundle (no se importa canvas si no se usa).
- Badge: primitivo eidos puro.
- Trans, FormatNumber, FormatDate, RelativeTime: 4 service components greppables
alrededor de uix.langs y uix.format. Nav group "Utilities".
Bugs fix arts/format/dates (engine-dates.ts):
1. Preset / per-axis conflict: Intl.DateTimeFormat rechaza dateStyle/timeStyle
con cualquier per-axis (weekday/era/year/month/day/dayPeriod/hour/minute/
second/fractionalSecondDigits) o con timeZoneName. Engine drop-ea ambos
presets por defecto cuando detecta conflict (regla estricta — un solo
per-axis hace ilegales los dos presets).
2. Per-call locale override: formatDate/Time/DateTime aceptan tercer arg
locale?: string. No muta engine state. Cache (getCachedDateFormat) keyea
por (locale x options). resolveCallHourCycle: cuando hay locale override
y pref auto, el ciclo horario se resuelve desde el locale OVERRIDE.
3. withHourCycle precedence: caller hourCycle/hour12 ahora gana sobre
preferencia del engine. Antes spread mal ordenado sobrescribia 'h12' del
caller con 'h23' del engine cuando locale resolvia a 24h.
FormatDate component vuelve a rutear siempre por uix.format.dates
(cache + hourCycle + locale reactivo), pasando locale como tercer arg.
Demos service components autonomos del toolbar global: cada uno owns su
demoLocale state con 7 chips (es-ES/en-US/en-GB/de-DE/fr-FR/ja-JP/ar-EG).
FormatDate stage filtra dateStyle/timeStyle por mode + chip hourCycle anadido.
Tests: 14/14 passing en engine-dates.test.ts (+6 nuevos: conflict handling,
locale override, hourCycle precedence).
Docs: arts/format/README.md (handoff 2026-05-25 con las 3 reglas),
README.md por service component, project_service_components_sprint_2026-05-25.md,
feedback_service_demos_own_locale.md (pattern reusable).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/badge', label: 'Badge' },
|
uix(empty-state): F1.2 · componente base display (morfo+eidos) al suelo del dossier
Primera pieza F1 del plan blocks (PLAN-blocks.md; alcance E-2 = suelo de
paridad, dossier §P3). Ruta de 9 fases completa:
- morfo: 5 partes display (provider/media/title/description/actions),
scope ['eidos'], 0 eventos con justificacion pasiva (patron
renderEmptyState de las refs headless), Title role:'heading', texts.label
- langs: components.empty-state.label (es/en) — el indice tambien recoge la
retirada foranea del import de words (inseparable por staging de archivo;
coherente con la migracion palabras ya enviada; words.ts sigue en su arbol)
- eidos: compound EmptyState + Media(kind icon|media, placa 2x glifo) +
Title(level 2-6, default h3 — modelo Atlaskit, tamano visual desacoplado) +
Description(measure 45ch) + Actions(label -> role=group +aria-label,
buttonGroupLabel); recipe sobre el bundle --size-* (titulo un paso
discreto arriba; sm=in-collection, lg=hero); tokens publicos minimos
(gap/actions-gap/media-bg/media-fg/media-radius/description-measure)
- demo v2 9 tabs (harness: SystemAxes/MotionPanel/SemaPanel; snippet con
paridad; escena in-collection via Card) + entrada nav (grupo Status)
- README: Baseline · Comparativa (shadcn/Chakra/Atlaskit/AntD/Polaris) ·
Decisiones · Passive justification · Gaps con disposicion
Verificacion: component:audit PASS · eidos-lint 5 morfo-backed + 4
eidos-only sancionados · morfo:check + morfo:vocabulary verdes ·
recipe-css-contract/api-contract/visual-attrs 37/37 · svelte-check
76E/51W = baseline exacto (cero regresion) · navegador claro Y oscuro por
estilos computados (titulo 18->24px, placa 40->64px, chips vivos, cero
errores de consola).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
{ slug: '/uix/components/empty-state', label: 'Empty state' },
|
uix(result): F1.3 · estado terminal de flujo/pagina (morfo+eidos) al suelo del dossier
Segunda pieza F1 del plan blocks (alcance E-2 = suelo de paridad, dossier
§P3). Comparte el esqueleto de empty-state a proposito: EmptyState describe
AUSENCIA de datos, Result reporta un RESULTADO — un lenguaje de layout, dos
contratos (duplicacion consciente registrada en el README; revisable al 3er
consumidor).
- morfo: 6 partes display (provider/media/title/description/actions/extra;
extra SIN archetype — parte genuinamente propia), scope ['eidos'],
0 eventos justificados (el resultado ya OCURRIO antes de renderizar)
- status: enum de 7 = paridad AntD CON `warning` (decision firme del
dossier) y HTTP renombrados semanticos (forbidden/not-found/server-error,
nunca '404' stringly); default 'info'; data-status = attr eidos-only
- media default: compone el mapa doctrinal IntentIcon (success→fulfill ·
error→threat · warning→risk) + Info de catalogo; HTTP = codigo mono
grande NEUTRO aria-hidden (situaciones, no fallos — AntD jamas pinta 404
de rojo); children reemplazan el default entero (context local eidos-only
con getter reactivo)
- Title default h2 (vs h3 de empty-state — Result suele SER la pagina);
Actions con label→role=group; Extra alineado a inicio (detalle que lee)
- recipe: glifo display 3× xl bundle; tokens publicos
gap/actions-gap/measures + {status}-color como forwarders de rol
retintables; sin eje size (paridad AntD, gap diferido)
- demo v2 9 tabs con copy por status + entrada nav (Status) + README
(Comparativa · Decisiones · Passive justification · Gaps con disposicion)
Verificacion: component:audit PASS 0E/0W · eidos-lint 6 morfo-backed + 5
eidos-only sancionados · recipe/api/visual-attrs 37/37 · morfo:check verde
(los 7 fallos listados son deuda foranea preexistente) · svelte-check
76E/51W = baseline exacto · navegador: success=fulfill verde 84px ·
error=threat rojo · not-found=«404» mono neutro, copy conmutando, cero
errores de consola.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
{ slug: '/uix/components/result', label: 'Result' },
|
|
|
|
|
|
{ slug: '/uix/components/callout', label: 'Callout' },
|
|
|
|
|
|
{ slug: '/uix/components/progress', label: 'Progress' },
|
feat(eidos): add Skeleton and Spinner loader primitives
Two pure-eidos loader components with morfos at scope `['eidos']`
(no soma backing, no semantic events).
Skeleton — content placeholder:
- 3 shapes: rect (default), circle, text (multi-line with staggered widths)
- 5 sizes (xs..xl), 8 color accents
- Optional shimmer animation via CSS `background-position` keyframes
- Single `<div role="status" aria-busy="true">` with translated aria-label
- Reduced-motion freezes animation, keeps visual
Spinner — indeterminate progress:
- 3 variants: ring (rotating arc), dots (bouncing), bars (stretching)
- 5 sizes, 8 colors
- Optional inline label via children snippet
- `role="status"` + `aria-live="polite"` for SR announcements
- Reduced-motion freezes animation, keeps visual
Files:
- morfo: skeleton.ts, spinner.ts (scope eidos, no events)
- eidos: full component + types + css + langs + index per component
- langs: skeleton/spinner under componentLangs barrel
- demos: canonical 6-tab depth + composition examples (avatar+text card)
- layout: sidebar additions under Status group
Note: also includes responsive shell additions to web/routes/uix/uix.css
that surfaced from an external session — same file, leaving them in.
Reference libraries reviewed (Mantine, Chakra v3, Radix Themes, MUI):
all expose the same shape/variant/size/color surface. shadcn Skeleton uses
the same pulse animation. No surprises in our API.
svelte-check: 0 errors in skeleton/spinner files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/meter', label: 'Meter' },
|
|
|
|
|
|
{ slug: '/uix/components/spinner', label: 'Spinner' },
|
|
|
|
|
|
{ slug: '/uix/components/skeleton', label: 'Skeleton' }
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
Build TextArea component + fix locale propagation bug across soma providers
Major work in this session:
1. NumberField demo refactored to expose Intl.NumberFormatOptions live
- 9 canonical presets (currency/unit/percent/compact/plain)
- Locale/currency/unit lists derived from $libs/langs, $libs/currency,
$format/units (no more hardcoded arrays)
- Default locale from uix.format.numbers.getLocale()
2. Bug fix: locale prop ignored when soma.format service is active
- soma/NumberField: 5 bypass points via new `hasOwnLocale` getter
(decimalSeparator, groupSeparator, formatValue, formatEditValue, parseValue)
- soma/Calendar: dateOrder respects locale prop (matches DateField precedence)
- soma/RangeCalendar: same dateOrder fix
- When the consumer passes `locale` prop, soma.nums/dates is bypassed
and Intl is used directly with the prop locale
3. TextArea — new component, full 4-layer stack
- morfo: 3 parts (provider/input/count), 3 events
(commit-input/commit-submit/shift-count-overflow), APG textbox
- soma: TextAreaProvider with autosize (scrollHeight measurement),
submitOn modes (false|enter|shift+enter|mod+enter), maxLength soft clamp,
Field integration (OR-merge), counter overflow tracking
- eidos: 5 sizes × 3 variants × 8 colors, autosize suppresses CSS resize
- sema: commit-submit + shift-count-overflow perceptual cues
- demo: canonical 6-tab depth, all props exposed as live controls
- PrimitiveTextareaAttributes added to soma/types/html.ts
4. Also includes accumulated unreleased work from prior sessions:
- Eidos ports: announce, clipboard, drag-drop, feed, table,
tree-grid, tree-view, virtual-grid, virtual-list, words
- Their canonical demos under web/routes/uix/components/
- Semantic events defined for utility morfos
(announce/clipboard/virtual-list/virtual-grid)
- Tree indent guides fixed (TreeView ::before, TreeGrid stacked gradients)
- Sidebar reorganized with Data + Utilities groups
- Eidos words content layer and primitives
Reference library survey performed before TextArea API design (Mantine,
Radix Themes, Chakra v3, React Aria, Ark UI, Bits UI, shadcn, MUI, Prime).
Decision documented: no separate CurrencyField/UnitField — single
NumberField with formatOptions is the canon (matches all surveyed
headless libs except PrimeReact's mode enum, which still uses 1 component).
svelte-check: 0 errors in modified/created files.
See CONTINUE.md for full session notes and next steps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{
|
|
|
|
|
|
kind: 'group',
|
|
|
|
|
|
heading: 'Data',
|
|
|
|
|
|
items: [
|
feat(charts): unified Heatmap (matrix + calendar) + 5 new chart types
Heatmap is now one component, two modes by data shape: matrix {x,y,value}
(columns × rows) and calendar {date,value} (GitHub-style weeks × weekdays,
with granularity year/month, orientation, locale-aware week start, fade).
New standalone charts: Gauge, Funnel (multicolor + labelPosition), Bubble
(size-encoded, axis-padded), PolarArea (Nightingale, padAngle gap between
sectors), CalendarHeatmap (internal renderer behind Heatmap).
Funnel/Bubble/Heatmap/PolarArea gain hover tooltips + dim. CalendarHeatmap
week start + month/weekday labels follow the uix langs locale reactively.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
{ slug: '/uix/components/metrics', label: 'Metrics' },
|
Build TextArea component + fix locale propagation bug across soma providers
Major work in this session:
1. NumberField demo refactored to expose Intl.NumberFormatOptions live
- 9 canonical presets (currency/unit/percent/compact/plain)
- Locale/currency/unit lists derived from $libs/langs, $libs/currency,
$format/units (no more hardcoded arrays)
- Default locale from uix.format.numbers.getLocale()
2. Bug fix: locale prop ignored when soma.format service is active
- soma/NumberField: 5 bypass points via new `hasOwnLocale` getter
(decimalSeparator, groupSeparator, formatValue, formatEditValue, parseValue)
- soma/Calendar: dateOrder respects locale prop (matches DateField precedence)
- soma/RangeCalendar: same dateOrder fix
- When the consumer passes `locale` prop, soma.nums/dates is bypassed
and Intl is used directly with the prop locale
3. TextArea — new component, full 4-layer stack
- morfo: 3 parts (provider/input/count), 3 events
(commit-input/commit-submit/shift-count-overflow), APG textbox
- soma: TextAreaProvider with autosize (scrollHeight measurement),
submitOn modes (false|enter|shift+enter|mod+enter), maxLength soft clamp,
Field integration (OR-merge), counter overflow tracking
- eidos: 5 sizes × 3 variants × 8 colors, autosize suppresses CSS resize
- sema: commit-submit + shift-count-overflow perceptual cues
- demo: canonical 6-tab depth, all props exposed as live controls
- PrimitiveTextareaAttributes added to soma/types/html.ts
4. Also includes accumulated unreleased work from prior sessions:
- Eidos ports: announce, clipboard, drag-drop, feed, table,
tree-grid, tree-view, virtual-grid, virtual-list, words
- Their canonical demos under web/routes/uix/components/
- Semantic events defined for utility morfos
(announce/clipboard/virtual-list/virtual-grid)
- Tree indent guides fixed (TreeView ::before, TreeGrid stacked gradients)
- Sidebar reorganized with Data + Utilities groups
- Eidos words content layer and primitives
Reference library survey performed before TextArea API design (Mantine,
Radix Themes, Chakra v3, React Aria, Ark UI, Bits UI, shadcn, MUI, Prime).
Decision documented: no separate CurrencyField/UnitField — single
NumberField with formatOptions is the canon (matches all surveyed
headless libs except PrimeReact's mode enum, which still uses 1 component).
svelte-check: 0 errors in modified/created files.
See CONTINUE.md for full session notes and next steps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/table', label: 'Table' },
|
|
|
|
|
|
{ slug: '/uix/components/tree-view', label: 'Tree view' },
|
|
|
|
|
|
{ slug: '/uix/components/tree-grid', label: 'Tree grid' },
|
uix(chat): bloque de mensajería chat-* v1 (log · message · composer · typing)
Familia chat-* completa, componiendo el ecosistema (Feed + VirtualList
anclado, Textarea, FileUpload, Avatar, Popover, Icon). Transporte-agnóstica:
el modelo de datos es de la app, los componentes reciben props + snippets.
- chat-log: Feed + VirtualList(anchor:'end') en triple registro; day
separators, unread divider, filas de sistema (Feed.Article), pill "N
nuevos" + notifyIncoming (announce a live-region), to-latest distance-aware.
- chat-message: raíz Feed.Article; runs ($libs/chat), ticks de entrega,
reply embebido, reacciones (chip pressed + tapback panel), acciones hover.
- chat-composer: Textarea (doble registro) + FileUpload; teclado
Enter/Shift+Enter/Esc/ArrowUp; typing saliente debounced (uix.timers);
barra reply/edit.
- chat-typing: sustain-streaming (visual-only, loop del sistema);
aggregateTypers 1/2/3+ con plantillas plurales.
- $libs/chat: groupIntoRuns (ventana 8 min estilo Discord) + aggregateTypers.
- VirtualList: extensión anchor:'end' (followOutput, prepend por clave
estable, sticky monótono, onAtEndChange) — sin scroller paralelo.
Diseño de referencia (WhatsApp/Telegram, pase lado-a-lado): burbujas suaves
(out = primary-element + texto normal; in = surface card + sombra) sobre
lienzo muted del log, meta dentro de la burbuja, tapback bar horizontal,
iconos lucide del ecosistema (nada de SVG a mano). Contrato de tamaños:
size="md" = 16px canónico.
Fixes de framework (afectaban más allá de chat):
- EngineLangs.t() ahora pela el prefijo '#?': los idlangrefs imperativos
(pagination/file-upload/chronos/chat) caían al fallback EN en locale es.
Fix parseLangRef ?? parsePathFallback + test de regresión (132/132).
- virtual-list commit-set-resize con channels:[]: es bookkeeping de
ResizeObserver, no gesto → dejaba de emitir beep/vibrate pre-interacción
(interventions de navigator.vibrate / AudioContext).
Escaparate messenger en /uix/demos/chat (bot de eco con uix.timers;
variante $connection señalada). Gaps v2 registrados en next-features §7.
Pase: component:audit 4×PASS · eidos-lint invalid=0 · recipe-contract
29/29 · Playwright escaparate PASS end-to-end · consola limpia ·
npm run check en baseline (76, cero errores nuevos).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
{ slug: '/uix/components/chat-composer', label: 'Chat composer' },
|
uix(chat): v2 · chat-list — panel de conversaciones (compone Listbox)
Componente nuevo del bloque chat v2: el panel de lista de salas/DMs.
Compone el Listbox del ecosistema (doble registro id/ref compartido):
selección única + roving-tabindex + typeahead + role=option vienen de
Listbox; chat-list aporta la disposición de fila (avatar · título/preview ·
meta) y el estado visual data-unread (negrita + Badge compuesto).
- morfo: expression 'delegated', sin eventos propios (D.1 — el actor es el
Listbox compuesto); parts provider/item(data-unread)/avatar/title/preview/meta.
- soma: ChatListProvider crea ListboxProvider dentro (value string <-> string[]);
ChatListItemProvider per-instance (unread = boolean crudo); 4 clases de
contenido explicitas.
- eidos: rejilla avatar | (titulo/preview) | meta, avatar y meta abarcan las
dos filas; truncado; unread -> titulo en negrita. No neutraliza el arquetipo
item (la conversacion ES una opcion seleccionable). Sin @keyframes; ejes
logicos.
- demo: 5 salas, seleccion limpia unread, typing-en-lista (compone ChatTyping),
Badge de no-leidos; tabs live/system/api/morfo/a11y.
- READMEs dobles (eidos con Passive justification, delegated); next-features 7
marca chat-list SHIPPED.
Verificacion: check baseline 76 (0 nuevos) · component:audit PASS ·
eidos-lint invalid=0 · recipe-contract 30/30 · morfo:vocabulary 0 · Playwright
PASS (role=listbox, 5 salas, aria-selected, unread=2, badges=2, typing-en-lista,
click + teclado seleccionan).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
|
|
|
|
{ slug: '/uix/components/chat-list', label: 'Chat list' },
|
uix(chat): bloque de mensajería chat-* v1 (log · message · composer · typing)
Familia chat-* completa, componiendo el ecosistema (Feed + VirtualList
anclado, Textarea, FileUpload, Avatar, Popover, Icon). Transporte-agnóstica:
el modelo de datos es de la app, los componentes reciben props + snippets.
- chat-log: Feed + VirtualList(anchor:'end') en triple registro; day
separators, unread divider, filas de sistema (Feed.Article), pill "N
nuevos" + notifyIncoming (announce a live-region), to-latest distance-aware.
- chat-message: raíz Feed.Article; runs ($libs/chat), ticks de entrega,
reply embebido, reacciones (chip pressed + tapback panel), acciones hover.
- chat-composer: Textarea (doble registro) + FileUpload; teclado
Enter/Shift+Enter/Esc/ArrowUp; typing saliente debounced (uix.timers);
barra reply/edit.
- chat-typing: sustain-streaming (visual-only, loop del sistema);
aggregateTypers 1/2/3+ con plantillas plurales.
- $libs/chat: groupIntoRuns (ventana 8 min estilo Discord) + aggregateTypers.
- VirtualList: extensión anchor:'end' (followOutput, prepend por clave
estable, sticky monótono, onAtEndChange) — sin scroller paralelo.
Diseño de referencia (WhatsApp/Telegram, pase lado-a-lado): burbujas suaves
(out = primary-element + texto normal; in = surface card + sombra) sobre
lienzo muted del log, meta dentro de la burbuja, tapback bar horizontal,
iconos lucide del ecosistema (nada de SVG a mano). Contrato de tamaños:
size="md" = 16px canónico.
Fixes de framework (afectaban más allá de chat):
- EngineLangs.t() ahora pela el prefijo '#?': los idlangrefs imperativos
(pagination/file-upload/chronos/chat) caían al fallback EN en locale es.
Fix parseLangRef ?? parsePathFallback + test de regresión (132/132).
- virtual-list commit-set-resize con channels:[]: es bookkeeping de
ResizeObserver, no gesto → dejaba de emitir beep/vibrate pre-interacción
(interventions de navigator.vibrate / AudioContext).
Escaparate messenger en /uix/demos/chat (bot de eco con uix.timers;
variante $connection señalada). Gaps v2 registrados en next-features §7.
Pase: component:audit 4×PASS · eidos-lint invalid=0 · recipe-contract
29/29 · Playwright escaparate PASS end-to-end · consola limpia ·
npm run check en baseline (76, cero errores nuevos).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
{ slug: '/uix/components/chat-log', label: 'Chat log' },
|
|
|
|
|
|
{ slug: '/uix/components/chat-message', label: 'Chat message' },
|
|
|
|
|
|
{ slug: '/uix/components/chat-typing', label: 'Chat typing' },
|
uix(chat): v2 · emoji-picker (picker completo) + escaparate rico del bloque
Nuevo componente emoji-picker y rediseño del escaparate de mensajeria.
emoji-picker (ruta 9 fases):
- Compone Popover (open/close/foco/portal) + Command (busqueda + grid + nav de
teclado + a11y combobox/listbox, su "grid mode" documentado) + dos ToggleGroup
(categorias + tono de piel). Delegated: sin eventos propios (D.1); el grid y
las celdas doble-registran Command.List/Item, conservando la a11y pero con
contrato visual propio (gana especificidad con selectores de 2 niveles, sin
tocar attrs [data-command-*] ajenos — eidos-lint limpio).
- Datos: $libs/emoji, dataset vendorizado de emojibase (MIT) horneado por
scripts/generate-emoji-data.ts (precedente lucide: JSON crudo en static/ +
script generador). ~1906 emoji con nombre CLDR + keywords, 316 con tono de
piel single-person. Grid acotado (categoria activa o resultados de busqueda),
sin virtualizacion (refinamiento v2). Helpers puros + test (12/12).
- morfo delegated (7 partes + header/footer de layout); soma (root + trigger +
content + grid + option + preview, provider-test 4/4); eidos (panel
empaquetado, receta con tokens --emoji-picker-*); langs es/en; demo 5 tabs
(excepcion delegated documentada); READMEs dobles.
Escaparate del bloque (web/routes/uix/demos/chat) — rehecho rico y correcto:
- Avatares por-autor con color solido (Avatar del ecosistema): antes gris
neutro indefinido, ahora teal/ambar/verde/indigo/primary bien contrastados.
- Dos columnas: chat-list (sidebar con badges + typing-en-lista) + sala.
- Conversacion rica: separador de dia, run agrupado, reaccion, reply citado,
recibos de lectura, typing con avatar, y el emoji-picker integrado en el
composer (inserta en el draft).
- Solape de burbujas (transitorio, estimateSize=48 con mensajes ~90-126px)
mitigado subiendo el estimate a 76.
Auditoria de tamanos/espacios: recipes del bloque limpios (todo con tokens
--space-*/--size-*/--color-*); unico literal (punto tono-capable) tokenizado.
Verificacion: check baseline 76 (0 nuevos) · component:audit emoji-picker PASS ·
eidos-lint invalid=0 · recipe-contract 30/30 · morfo:vocabulary 0 · emoji lib
12/12 · provider-test 4/4 · Playwright PASS (busqueda 'rocket'->rocket+
astronautas, click->onSelect+cierra, teclado, tono oscuro 👋->👋🏿, picker abre
en el composer). Capturas claro miradas.
next-features §7 marca emoji-picker SHIPPED.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
|
|
|
|
{ slug: '/uix/components/emoji-picker', label: 'Emoji picker' },
|
Build TextArea component + fix locale propagation bug across soma providers
Major work in this session:
1. NumberField demo refactored to expose Intl.NumberFormatOptions live
- 9 canonical presets (currency/unit/percent/compact/plain)
- Locale/currency/unit lists derived from $libs/langs, $libs/currency,
$format/units (no more hardcoded arrays)
- Default locale from uix.format.numbers.getLocale()
2. Bug fix: locale prop ignored when soma.format service is active
- soma/NumberField: 5 bypass points via new `hasOwnLocale` getter
(decimalSeparator, groupSeparator, formatValue, formatEditValue, parseValue)
- soma/Calendar: dateOrder respects locale prop (matches DateField precedence)
- soma/RangeCalendar: same dateOrder fix
- When the consumer passes `locale` prop, soma.nums/dates is bypassed
and Intl is used directly with the prop locale
3. TextArea — new component, full 4-layer stack
- morfo: 3 parts (provider/input/count), 3 events
(commit-input/commit-submit/shift-count-overflow), APG textbox
- soma: TextAreaProvider with autosize (scrollHeight measurement),
submitOn modes (false|enter|shift+enter|mod+enter), maxLength soft clamp,
Field integration (OR-merge), counter overflow tracking
- eidos: 5 sizes × 3 variants × 8 colors, autosize suppresses CSS resize
- sema: commit-submit + shift-count-overflow perceptual cues
- demo: canonical 6-tab depth, all props exposed as live controls
- PrimitiveTextareaAttributes added to soma/types/html.ts
4. Also includes accumulated unreleased work from prior sessions:
- Eidos ports: announce, clipboard, drag-drop, feed, table,
tree-grid, tree-view, virtual-grid, virtual-list, words
- Their canonical demos under web/routes/uix/components/
- Semantic events defined for utility morfos
(announce/clipboard/virtual-list/virtual-grid)
- Tree indent guides fixed (TreeView ::before, TreeGrid stacked gradients)
- Sidebar reorganized with Data + Utilities groups
- Eidos words content layer and primitives
Reference library survey performed before TextArea API design (Mantine,
Radix Themes, Chakra v3, React Aria, Ark UI, Bits UI, shadcn, MUI, Prime).
Decision documented: no separate CurrencyField/UnitField — single
NumberField with formatOptions is the canon (matches all surveyed
headless libs except PrimeReact's mode enum, which still uses 1 component).
svelte-check: 0 errors in modified/created files.
See CONTINUE.md for full session notes and next steps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/feed', label: 'Feed' },
|
|
|
|
|
|
{ slug: '/uix/components/timeline', label: 'Timeline' },
|
Build TextArea component + fix locale propagation bug across soma providers
Major work in this session:
1. NumberField demo refactored to expose Intl.NumberFormatOptions live
- 9 canonical presets (currency/unit/percent/compact/plain)
- Locale/currency/unit lists derived from $libs/langs, $libs/currency,
$format/units (no more hardcoded arrays)
- Default locale from uix.format.numbers.getLocale()
2. Bug fix: locale prop ignored when soma.format service is active
- soma/NumberField: 5 bypass points via new `hasOwnLocale` getter
(decimalSeparator, groupSeparator, formatValue, formatEditValue, parseValue)
- soma/Calendar: dateOrder respects locale prop (matches DateField precedence)
- soma/RangeCalendar: same dateOrder fix
- When the consumer passes `locale` prop, soma.nums/dates is bypassed
and Intl is used directly with the prop locale
3. TextArea — new component, full 4-layer stack
- morfo: 3 parts (provider/input/count), 3 events
(commit-input/commit-submit/shift-count-overflow), APG textbox
- soma: TextAreaProvider with autosize (scrollHeight measurement),
submitOn modes (false|enter|shift+enter|mod+enter), maxLength soft clamp,
Field integration (OR-merge), counter overflow tracking
- eidos: 5 sizes × 3 variants × 8 colors, autosize suppresses CSS resize
- sema: commit-submit + shift-count-overflow perceptual cues
- demo: canonical 6-tab depth, all props exposed as live controls
- PrimitiveTextareaAttributes added to soma/types/html.ts
4. Also includes accumulated unreleased work from prior sessions:
- Eidos ports: announce, clipboard, drag-drop, feed, table,
tree-grid, tree-view, virtual-grid, virtual-list, words
- Their canonical demos under web/routes/uix/components/
- Semantic events defined for utility morfos
(announce/clipboard/virtual-list/virtual-grid)
- Tree indent guides fixed (TreeView ::before, TreeGrid stacked gradients)
- Sidebar reorganized with Data + Utilities groups
- Eidos words content layer and primitives
Reference library survey performed before TextArea API design (Mantine,
Radix Themes, Chakra v3, React Aria, Ark UI, Bits UI, shadcn, MUI, Prime).
Decision documented: no separate CurrencyField/UnitField — single
NumberField with formatOptions is the canon (matches all surveyed
headless libs except PrimeReact's mode enum, which still uses 1 component).
svelte-check: 0 errors in modified/created files.
See CONTINUE.md for full session notes and next steps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/virtual-list', label: 'Virtual list' },
|
|
|
|
|
|
{ slug: '/uix/components/virtual-grid', label: 'Virtual grid' }
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
feat(charts): unified Heatmap (matrix + calendar) + 5 new chart types
Heatmap is now one component, two modes by data shape: matrix {x,y,value}
(columns × rows) and calendar {date,value} (GitHub-style weeks × weekdays,
with granularity year/month, orientation, locale-aware week start, fade).
New standalone charts: Gauge, Funnel (multicolor + labelPosition), Bubble
(size-encoded, axis-padded), PolarArea (Nightingale, padAngle gap between
sectors), CalendarHeatmap (internal renderer behind Heatmap).
Funnel/Bubble/Heatmap/PolarArea gain hover tooltips + dim. CalendarHeatmap
week start + month/weekday labels follow the uix langs locale reactively.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
{
|
|
|
|
|
|
kind: 'group',
|
|
|
|
|
|
heading: 'Charts',
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{ slug: '/uix/components/chart', label: 'Chart · overview' },
|
|
|
|
|
|
{ slug: '/uix/components/line-chart', label: 'Line chart' },
|
|
|
|
|
|
{ slug: '/uix/components/area-chart', label: 'Area chart' },
|
|
|
|
|
|
{ slug: '/uix/components/bar-chart', label: 'Bar chart' },
|
|
|
|
|
|
{ slug: '/uix/components/scatter-chart', label: 'Scatter chart' },
|
|
|
|
|
|
{ slug: '/uix/components/sparkline', label: 'Sparkline' },
|
|
|
|
|
|
{ slug: '/uix/components/pie-chart', label: 'Pie chart' },
|
|
|
|
|
|
{ slug: '/uix/components/bar-list', label: 'Bar list' },
|
|
|
|
|
|
{ slug: '/uix/components/bar-segment', label: 'Bar segment' },
|
|
|
|
|
|
{ slug: '/uix/components/radar-chart', label: 'Radar chart' },
|
|
|
|
|
|
{ slug: '/uix/components/gauge', label: 'Gauge' },
|
|
|
|
|
|
{ slug: '/uix/components/funnel', label: 'Funnel' },
|
|
|
|
|
|
{ slug: '/uix/components/bubble-chart', label: 'Bubble chart' },
|
|
|
|
|
|
{ slug: '/uix/components/polar-area', label: 'Polar area' },
|
|
|
|
|
|
{ slug: '/uix/components/heatmap', label: 'Heatmap' },
|
|
|
|
|
|
{ slug: '/uix/components/smith-chart', label: 'Smith chart' }
|
feat(charts): unified Heatmap (matrix + calendar) + 5 new chart types
Heatmap is now one component, two modes by data shape: matrix {x,y,value}
(columns × rows) and calendar {date,value} (GitHub-style weeks × weekdays,
with granularity year/month, orientation, locale-aware week start, fade).
New standalone charts: Gauge, Funnel (multicolor + labelPosition), Bubble
(size-encoded, axis-padded), PolarArea (Nightingale, padAngle gap between
sectors), CalendarHeatmap (internal renderer behind Heatmap).
Funnel/Bubble/Heatmap/PolarArea gain hover tooltips + dim. CalendarHeatmap
week start + month/weekday labels follow the uix langs locale reactively.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
|
|
|
|
]
|
|
|
|
|
|
},
|
Build TextArea component + fix locale propagation bug across soma providers
Major work in this session:
1. NumberField demo refactored to expose Intl.NumberFormatOptions live
- 9 canonical presets (currency/unit/percent/compact/plain)
- Locale/currency/unit lists derived from $libs/langs, $libs/currency,
$format/units (no more hardcoded arrays)
- Default locale from uix.format.numbers.getLocale()
2. Bug fix: locale prop ignored when soma.format service is active
- soma/NumberField: 5 bypass points via new `hasOwnLocale` getter
(decimalSeparator, groupSeparator, formatValue, formatEditValue, parseValue)
- soma/Calendar: dateOrder respects locale prop (matches DateField precedence)
- soma/RangeCalendar: same dateOrder fix
- When the consumer passes `locale` prop, soma.nums/dates is bypassed
and Intl is used directly with the prop locale
3. TextArea — new component, full 4-layer stack
- morfo: 3 parts (provider/input/count), 3 events
(commit-input/commit-submit/shift-count-overflow), APG textbox
- soma: TextAreaProvider with autosize (scrollHeight measurement),
submitOn modes (false|enter|shift+enter|mod+enter), maxLength soft clamp,
Field integration (OR-merge), counter overflow tracking
- eidos: 5 sizes × 3 variants × 8 colors, autosize suppresses CSS resize
- sema: commit-submit + shift-count-overflow perceptual cues
- demo: canonical 6-tab depth, all props exposed as live controls
- PrimitiveTextareaAttributes added to soma/types/html.ts
4. Also includes accumulated unreleased work from prior sessions:
- Eidos ports: announce, clipboard, drag-drop, feed, table,
tree-grid, tree-view, virtual-grid, virtual-list, words
- Their canonical demos under web/routes/uix/components/
- Semantic events defined for utility morfos
(announce/clipboard/virtual-list/virtual-grid)
- Tree indent guides fixed (TreeView ::before, TreeGrid stacked gradients)
- Sidebar reorganized with Data + Utilities groups
- Eidos words content layer and primitives
Reference library survey performed before TextArea API design (Mantine,
Radix Themes, Chakra v3, React Aria, Ark UI, Bits UI, shadcn, MUI, Prime).
Decision documented: no separate CurrencyField/UnitField — single
NumberField with formatOptions is the canon (matches all surveyed
headless libs except PrimeReact's mode enum, which still uses 1 component).
svelte-check: 0 errors in modified/created files.
See CONTINUE.md for full session notes and next steps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{
|
|
|
|
|
|
kind: 'group',
|
|
|
|
|
|
heading: 'Utilities',
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{ slug: '/uix/components/announce', label: 'Announce' },
|
|
|
|
|
|
{ slug: '/uix/components/clipboard', label: 'Clipboard' },
|
feat(eidos): Card, Avatar ext, Image, SText, Badge, service components + dates engine fixes
Sprint completo:
- Card (eidos + sema): elevation/intent/interactive, eventos select+activate.
- Avatar: ring, AvatarBadge, AvatarGroup separados; demo Avatar standalone.
- Image: status surface (idle/loading/loaded/error) consumiendo soma ImageProvider.
- SText + STextVirtualList: canvas-text engine portado de morfo-driven-soma como
componentes separados para optimizar bundle (no se importa canvas si no se usa).
- Badge: primitivo eidos puro.
- Trans, FormatNumber, FormatDate, RelativeTime: 4 service components greppables
alrededor de uix.langs y uix.format. Nav group "Utilities".
Bugs fix arts/format/dates (engine-dates.ts):
1. Preset / per-axis conflict: Intl.DateTimeFormat rechaza dateStyle/timeStyle
con cualquier per-axis (weekday/era/year/month/day/dayPeriod/hour/minute/
second/fractionalSecondDigits) o con timeZoneName. Engine drop-ea ambos
presets por defecto cuando detecta conflict (regla estricta — un solo
per-axis hace ilegales los dos presets).
2. Per-call locale override: formatDate/Time/DateTime aceptan tercer arg
locale?: string. No muta engine state. Cache (getCachedDateFormat) keyea
por (locale x options). resolveCallHourCycle: cuando hay locale override
y pref auto, el ciclo horario se resuelve desde el locale OVERRIDE.
3. withHourCycle precedence: caller hourCycle/hour12 ahora gana sobre
preferencia del engine. Antes spread mal ordenado sobrescribia 'h12' del
caller con 'h23' del engine cuando locale resolvia a 24h.
FormatDate component vuelve a rutear siempre por uix.format.dates
(cache + hourCycle + locale reactivo), pasando locale como tercer arg.
Demos service components autonomos del toolbar global: cada uno owns su
demoLocale state con 7 chips (es-ES/en-US/en-GB/de-DE/fr-FR/ja-JP/ar-EG).
FormatDate stage filtra dateStyle/timeStyle por mode + chip hourCycle anadido.
Tests: 14/14 passing en engine-dates.test.ts (+6 nuevos: conflict handling,
locale override, hourCycle precedence).
Docs: arts/format/README.md (handoff 2026-05-25 con las 3 reglas),
README.md por service component, project_service_components_sprint_2026-05-25.md,
feedback_service_demos_own_locale.md (pattern reusable).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/drag-drop', label: 'Drag & drop' },
|
feat(proof-of-human): re-architect to orchestrator + pluggable challenge (PathTrace)
Split the anti-bot component into a purpose- and interaction-agnostic
orchestrator that owns the lifecycle stages + verdict, and a pluggable
challenge that runs the interaction and reports up via
ProofOfHumanChallengeHost. The primary challenge, PathTrace, has the user
follow a random curved path by pointer OR arrow keys.
This fixes the drag version's two real defects:
- keyboard bypass: the accessible path now yields genuine, non-replayable
behavioural signal (pathAdherence over a per-instance random curve); a
two-key script can't solve it.
- over-coupled abstraction: interaction + animation are decoupled from the
component identity (per-stage consumer slots; challenge is pluggable).
Layers:
- morfo: proof-of-human slimmed to orchestrator (Provider + 3 commits); new
path-trace morfo (Provider + Track + Token; handle-*).
- soma: orchestrator + ProofOfHumanChallengeHost; new path-trace (seeded
Catmull-Rom curve, corridor-gated monotonic progress, adherence telemetry,
pointer + arrow-key input). Old drag Token/Target wrappers retired.
- sema: verdict commits (proof-of-human) + trace handle earcons (path-trace).
- eidos: orchestrator + PathTrace that paints the curve/corridor/trail.
- demo rebuilt: carrier-pigeon skin over the per-stage slots.
Auto-focuses the token when the challenge is armed so the arrow keys start the
trace immediately (gated on the disabled->enabled transition).
Verified: 32/32 tests (incl. arrow trace completes with real signal + two-key
script can't solve, in real chromium) + visual in real Chrome (curve, failed,
verified; scorer rejects instant traces).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
|
|
|
|
{ slug: '/uix/components/proof-of-human', label: 'Proof of human' },
|
feat(eidos): Card, Avatar ext, Image, SText, Badge, service components + dates engine fixes
Sprint completo:
- Card (eidos + sema): elevation/intent/interactive, eventos select+activate.
- Avatar: ring, AvatarBadge, AvatarGroup separados; demo Avatar standalone.
- Image: status surface (idle/loading/loaded/error) consumiendo soma ImageProvider.
- SText + STextVirtualList: canvas-text engine portado de morfo-driven-soma como
componentes separados para optimizar bundle (no se importa canvas si no se usa).
- Badge: primitivo eidos puro.
- Trans, FormatNumber, FormatDate, RelativeTime: 4 service components greppables
alrededor de uix.langs y uix.format. Nav group "Utilities".
Bugs fix arts/format/dates (engine-dates.ts):
1. Preset / per-axis conflict: Intl.DateTimeFormat rechaza dateStyle/timeStyle
con cualquier per-axis (weekday/era/year/month/day/dayPeriod/hour/minute/
second/fractionalSecondDigits) o con timeZoneName. Engine drop-ea ambos
presets por defecto cuando detecta conflict (regla estricta — un solo
per-axis hace ilegales los dos presets).
2. Per-call locale override: formatDate/Time/DateTime aceptan tercer arg
locale?: string. No muta engine state. Cache (getCachedDateFormat) keyea
por (locale x options). resolveCallHourCycle: cuando hay locale override
y pref auto, el ciclo horario se resuelve desde el locale OVERRIDE.
3. withHourCycle precedence: caller hourCycle/hour12 ahora gana sobre
preferencia del engine. Antes spread mal ordenado sobrescribia 'h12' del
caller con 'h23' del engine cuando locale resolvia a 24h.
FormatDate component vuelve a rutear siempre por uix.format.dates
(cache + hourCycle + locale reactivo), pasando locale como tercer arg.
Demos service components autonomos del toolbar global: cada uno owns su
demoLocale state con 7 chips (es-ES/en-US/en-GB/de-DE/fr-FR/ja-JP/ar-EG).
FormatDate stage filtra dateStyle/timeStyle por mode + chip hourCycle anadido.
Tests: 14/14 passing en engine-dates.test.ts (+6 nuevos: conflict handling,
locale override, hourCycle precedence).
Docs: arts/format/README.md (handoff 2026-05-25 con las 3 reglas),
README.md por service component, project_service_components_sprint_2026-05-25.md,
feedback_service_demos_own_locale.md (pattern reusable).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/trans', label: 'Trans' },
|
|
|
|
|
|
{ slug: '/uix/components/format-number', label: 'FormatNumber' },
|
|
|
|
|
|
{ slug: '/uix/components/format-date', label: 'FormatDate' },
|
|
|
|
|
|
{ slug: '/uix/components/relative-time', label: 'RelativeTime' }
|
Build TextArea component + fix locale propagation bug across soma providers
Major work in this session:
1. NumberField demo refactored to expose Intl.NumberFormatOptions live
- 9 canonical presets (currency/unit/percent/compact/plain)
- Locale/currency/unit lists derived from $libs/langs, $libs/currency,
$format/units (no more hardcoded arrays)
- Default locale from uix.format.numbers.getLocale()
2. Bug fix: locale prop ignored when soma.format service is active
- soma/NumberField: 5 bypass points via new `hasOwnLocale` getter
(decimalSeparator, groupSeparator, formatValue, formatEditValue, parseValue)
- soma/Calendar: dateOrder respects locale prop (matches DateField precedence)
- soma/RangeCalendar: same dateOrder fix
- When the consumer passes `locale` prop, soma.nums/dates is bypassed
and Intl is used directly with the prop locale
3. TextArea — new component, full 4-layer stack
- morfo: 3 parts (provider/input/count), 3 events
(commit-input/commit-submit/shift-count-overflow), APG textbox
- soma: TextAreaProvider with autosize (scrollHeight measurement),
submitOn modes (false|enter|shift+enter|mod+enter), maxLength soft clamp,
Field integration (OR-merge), counter overflow tracking
- eidos: 5 sizes × 3 variants × 8 colors, autosize suppresses CSS resize
- sema: commit-submit + shift-count-overflow perceptual cues
- demo: canonical 6-tab depth, all props exposed as live controls
- PrimitiveTextareaAttributes added to soma/types/html.ts
4. Also includes accumulated unreleased work from prior sessions:
- Eidos ports: announce, clipboard, drag-drop, feed, table,
tree-grid, tree-view, virtual-grid, virtual-list, words
- Their canonical demos under web/routes/uix/components/
- Semantic events defined for utility morfos
(announce/clipboard/virtual-list/virtual-grid)
- Tree indent guides fixed (TreeView ::before, TreeGrid stacked gradients)
- Sidebar reorganized with Data + Utilities groups
- Eidos words content layer and primitives
Reference library survey performed before TextArea API design (Mantine,
Radix Themes, Chakra v3, React Aria, Ark UI, Bits UI, shadcn, MUI, Prime).
Decision documented: no separate CurrencyField/UnitField — single
NumberField with formatOptions is the canon (matches all surveyed
headless libs except PrimeReact's mode enum, which still uses 1 component).
svelte-check: 0 errors in modified/created files.
See CONTINUE.md for full session notes and next steps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
]
|
|
|
|
|
|
},
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{
|
|
|
|
|
|
kind: 'group',
|
|
|
|
|
|
heading: 'Overlays',
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{ slug: '/uix/components/dialog', label: 'Dialog' },
|
|
|
|
|
|
{ slug: '/uix/components/alert-dialog', label: 'Alert dialog' },
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/drawer', label: 'Drawer' },
|
|
|
|
|
|
{ slug: '/uix/components/popover', label: 'Popover' },
|
|
|
|
|
|
{ slug: '/uix/components/float-panel', label: 'Float panel' },
|
|
|
|
|
|
{ slug: '/uix/components/dropdown-menu', label: 'Dropdown menu' },
|
|
|
|
|
|
{ slug: '/uix/components/context-menu', label: 'Context menu' },
|
|
|
|
|
|
{ slug: '/uix/components/menubar', label: 'Menubar' },
|
|
|
|
|
|
{ slug: '/uix/components/onion-menu', label: 'Onion menu' },
|
|
|
|
|
|
{ slug: '/uix/components/menu-dial', label: 'Menu dial' },
|
|
|
|
|
|
{ slug: '/uix/components/command', label: 'Command' },
|
|
|
|
|
|
{ slug: '/uix/components/link-preview', label: 'Link preview' },
|
|
|
|
|
|
{ slug: '/uix/components/toast', label: 'Toast' },
|
|
|
|
|
|
{ slug: '/uix/components/tooltip', label: 'Tooltip' }
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
]
|
|
|
|
|
|
},
|
feat(typography): Text — POC consuming new --style-* foundation tokens
First typography primitive consuming the named-style tokens added in
`015bd761`. Sets the canon for the remaining typography ports
(Heading, Display, Code, CodeBlock, Kbd, Mark, Highlight, Link).
API:
- `style: 'body' | 'prose' | 'label' | 'caption'` (default `body`).
Selects which `--style-{name}-*` token set the recipe consumes.
- Per-axis overrides via `size`, `family`, `weight`, `color`, `align`.
Each writes a local `--_text-{axis}` CSS variable that wins over
the named style's default via the cascade.
- `italic`, `underline` shorthand booleans.
- `truncate` (single-line ellipsis) + `clamp={N}` (multi-line via
CSS `-webkit-line-clamp`).
- `as` prop switches the tag (default `'span'` — inline composable).
- `tagStyle` escape hatch for one-off inline CSS that the prop
surface doesn't cover (separate from the `style` named-style prop).
Architectural choices documented in README:
- Sizes map to eidos `xxs..xxxl` (not air's `1..12`).
- Families use the eidos slot names (`primary/secondary/mono/display`)
not air's domain aliases (`ui/prose/code`) — coherence with the
foundation contract.
- Colors are restricted to the content-role slots
(`--color-content-*`) to preserve contrast guarantees. Free colors
via `tagStyle`.
- Canvas line-count measurement (air's `text` + bindable `lineCount`)
deferred; CSS line-clamp covers the visual-truncate use case.
Sidebar nav gains a `Typography` group with Text as its first entry.
`npm run component:audit`: 82 / 82 PASS, 0 NEEDS-WORK. svelte-check 0
errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{
|
|
|
|
|
|
kind: 'group',
|
|
|
|
|
|
heading: 'Typography',
|
feat(typography): 8 primitives — Heading, Display, Code, CodeBlock, Kbd, Mark, Highlight, Link
Full canon for the rest of the typography family, following Text
(commit `03531709`) as template. Each primitive ships morfo (passive,
scope eidos, 0 events) + full eidos set (svelte/css/types/index +
README with required sections) + canon 6-tab demo (≥400 lines each).
**Heading** — semantic h1..h6 via `level` prop, visual style decoupled
via `style` prop (defaults to match level but `<Heading level={3}
style="h1">` allowed). Demo 594 lines.
**Display** — always-h1 page-title primitive. Always uses
`style="hero"`. Tracking via literal em values until foundation emits
hero-scale tracking tokens. Demo 546 lines.
**Code** — inline code via `<code>`. `variant: 'plain' | 'soft' |
'outline'` for subtle background chrome. Demo 423 lines.
**CodeBlock** — multi-line `<pre><code>` with optional title +
copy button (button utility, NOT a sema verb — kept out of morfo per
2-of-3 rule). Demo 402 lines.
**Kbd** — keyboard shortcut chrome over `style="code"`. Demo 418 lines.
**Mark** — text highlight via `<mark>`. Palette uses canonical
`ColorRole` intents (default `fulfill` ≈ legacy yellow). Demo 402 lines.
**Highlight** — substring highlighter inside Text. Scans children for
`query` and wraps matches. Same intent palette as Mark. Demo 407 lines.
**Link** — text link via `<a>`. `variant: 'default' | 'subtle' |
'plain'` for underline style, `intent: ColorRole` for color. Passive
on the morfo — hover/active is CSS-only, navigation belongs to the
consumer (router). Demo 448 lines.
**Architectural deviations from air documented per README's
Decisiones section:**
- Heading: semantic level vs visual style decoupling
- Code split into inline `<Code>` + block `<CodeBlock>` (was one
component with `variant='block'` in air)
- Mark/Highlight palette swapped from literal hue names to canonical
ColorRole intents (theme contract)
- Link palette opened to full ColorRole; `quiet`/`emphasized` air
variants dropped in favour of composable patterns
`npm run component:audit`: **90 / 90 PASS**, 0 NEEDS-WORK.
svelte-check: 0 errors, 0 new warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
items: [
|
|
|
|
|
|
{ slug: '/uix/components/text', label: 'Text' },
|
feat(eidos): Card, Avatar ext, Image, SText, Badge, service components + dates engine fixes
Sprint completo:
- Card (eidos + sema): elevation/intent/interactive, eventos select+activate.
- Avatar: ring, AvatarBadge, AvatarGroup separados; demo Avatar standalone.
- Image: status surface (idle/loading/loaded/error) consumiendo soma ImageProvider.
- SText + STextVirtualList: canvas-text engine portado de morfo-driven-soma como
componentes separados para optimizar bundle (no se importa canvas si no se usa).
- Badge: primitivo eidos puro.
- Trans, FormatNumber, FormatDate, RelativeTime: 4 service components greppables
alrededor de uix.langs y uix.format. Nav group "Utilities".
Bugs fix arts/format/dates (engine-dates.ts):
1. Preset / per-axis conflict: Intl.DateTimeFormat rechaza dateStyle/timeStyle
con cualquier per-axis (weekday/era/year/month/day/dayPeriod/hour/minute/
second/fractionalSecondDigits) o con timeZoneName. Engine drop-ea ambos
presets por defecto cuando detecta conflict (regla estricta — un solo
per-axis hace ilegales los dos presets).
2. Per-call locale override: formatDate/Time/DateTime aceptan tercer arg
locale?: string. No muta engine state. Cache (getCachedDateFormat) keyea
por (locale x options). resolveCallHourCycle: cuando hay locale override
y pref auto, el ciclo horario se resuelve desde el locale OVERRIDE.
3. withHourCycle precedence: caller hourCycle/hour12 ahora gana sobre
preferencia del engine. Antes spread mal ordenado sobrescribia 'h12' del
caller con 'h23' del engine cuando locale resolvia a 24h.
FormatDate component vuelve a rutear siempre por uix.format.dates
(cache + hourCycle + locale reactivo), pasando locale como tercer arg.
Demos service components autonomos del toolbar global: cada uno owns su
demoLocale state con 7 chips (es-ES/en-US/en-GB/de-DE/fr-FR/ja-JP/ar-EG).
FormatDate stage filtra dateStyle/timeStyle por mode + chip hourCycle anadido.
Tests: 14/14 passing en engine-dates.test.ts (+6 nuevos: conflict handling,
locale override, hourCycle precedence).
Docs: arts/format/README.md (handoff 2026-05-25 con las 3 reglas),
README.md por service component, project_service_components_sprint_2026-05-25.md,
feedback_service_demos_own_locale.md (pattern reusable).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/s-text', label: 'SText' },
|
|
|
|
|
|
{ slug: '/uix/components/s-text-virtual-list', label: 'STextVirtualList' },
|
feat(typography): 8 primitives — Heading, Display, Code, CodeBlock, Kbd, Mark, Highlight, Link
Full canon for the rest of the typography family, following Text
(commit `03531709`) as template. Each primitive ships morfo (passive,
scope eidos, 0 events) + full eidos set (svelte/css/types/index +
README with required sections) + canon 6-tab demo (≥400 lines each).
**Heading** — semantic h1..h6 via `level` prop, visual style decoupled
via `style` prop (defaults to match level but `<Heading level={3}
style="h1">` allowed). Demo 594 lines.
**Display** — always-h1 page-title primitive. Always uses
`style="hero"`. Tracking via literal em values until foundation emits
hero-scale tracking tokens. Demo 546 lines.
**Code** — inline code via `<code>`. `variant: 'plain' | 'soft' |
'outline'` for subtle background chrome. Demo 423 lines.
**CodeBlock** — multi-line `<pre><code>` with optional title +
copy button (button utility, NOT a sema verb — kept out of morfo per
2-of-3 rule). Demo 402 lines.
**Kbd** — keyboard shortcut chrome over `style="code"`. Demo 418 lines.
**Mark** — text highlight via `<mark>`. Palette uses canonical
`ColorRole` intents (default `fulfill` ≈ legacy yellow). Demo 402 lines.
**Highlight** — substring highlighter inside Text. Scans children for
`query` and wraps matches. Same intent palette as Mark. Demo 407 lines.
**Link** — text link via `<a>`. `variant: 'default' | 'subtle' |
'plain'` for underline style, `intent: ColorRole` for color. Passive
on the morfo — hover/active is CSS-only, navigation belongs to the
consumer (router). Demo 448 lines.
**Architectural deviations from air documented per README's
Decisiones section:**
- Heading: semantic level vs visual style decoupling
- Code split into inline `<Code>` + block `<CodeBlock>` (was one
component with `variant='block'` in air)
- Mark/Highlight palette swapped from literal hue names to canonical
ColorRole intents (theme contract)
- Link palette opened to full ColorRole; `quiet`/`emphasized` air
variants dropped in favour of composable patterns
`npm run component:audit`: **90 / 90 PASS**, 0 NEEDS-WORK.
svelte-check: 0 errors, 0 new warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/heading', label: 'Heading' },
|
|
|
|
|
|
{ slug: '/uix/components/display', label: 'Display' },
|
|
|
|
|
|
{ slug: '/uix/components/code', label: 'Code' },
|
|
|
|
|
|
{ slug: '/uix/components/code-block', label: 'CodeBlock' },
|
|
|
|
|
|
{ slug: '/uix/components/kbd', label: 'Kbd' },
|
|
|
|
|
|
{ slug: '/uix/components/mark', label: 'Mark' },
|
|
|
|
|
|
{ slug: '/uix/components/highlight', label: 'Highlight' },
|
|
|
|
|
|
{ slug: '/uix/components/link', label: 'Link' }
|
|
|
|
|
|
]
|
feat(typography): Text — POC consuming new --style-* foundation tokens
First typography primitive consuming the named-style tokens added in
`015bd761`. Sets the canon for the remaining typography ports
(Heading, Display, Code, CodeBlock, Kbd, Mark, Highlight, Link).
API:
- `style: 'body' | 'prose' | 'label' | 'caption'` (default `body`).
Selects which `--style-{name}-*` token set the recipe consumes.
- Per-axis overrides via `size`, `family`, `weight`, `color`, `align`.
Each writes a local `--_text-{axis}` CSS variable that wins over
the named style's default via the cascade.
- `italic`, `underline` shorthand booleans.
- `truncate` (single-line ellipsis) + `clamp={N}` (multi-line via
CSS `-webkit-line-clamp`).
- `as` prop switches the tag (default `'span'` — inline composable).
- `tagStyle` escape hatch for one-off inline CSS that the prop
surface doesn't cover (separate from the `style` named-style prop).
Architectural choices documented in README:
- Sizes map to eidos `xxs..xxxl` (not air's `1..12`).
- Families use the eidos slot names (`primary/secondary/mono/display`)
not air's domain aliases (`ui/prose/code`) — coherence with the
foundation contract.
- Colors are restricted to the content-role slots
(`--color-content-*`) to preserve contrast guarantees. Free colors
via `tagStyle`.
- Canvas line-count measurement (air's `text` + bindable `lineCount`)
deferred; CSS line-clamp covers the visual-truncate use case.
Sidebar nav gains a `Typography` group with Text as its first entry.
`npm run component:audit`: 82 / 82 PASS, 0 NEEDS-WORK. svelte-check 0
errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
},
|
fix(demos): reachable nav + real interactive Ambient testbed + visible fog/mesh
The catalog work shipped but was unreachable and impoverished:
- **Nav**: the sidebar IA (web/routes/uix/+layout@.svelte) is a manual list;
the new components were never added, so nothing linked to them. Add a
"Text effects" group (TextGradient/Circular/Blur/Focus/Scramble), CountUp
under Utilities, and a "Packs" group (Ambient · backgrounds).
- **Ambient demo → real testbed**: rebuild web/routes/uix/packs/ambient as a
proper interactive page — effect selector (32, grouped Agentic/Tier A/B),
live param controls AUTO-GENERATED from each effect's `defaults`
(number→slider, boolean→toggle, colors→editable token list, string→text),
a stage-background toggle (surface/black/white/transparent), reset, and a
copyable usage snippet. The old page only switched effect, no tuning.
- **fog + mesh painted nothing**: their seeded colors were near-invisible on
the dark base theme — fog used neutral-bg2 (L 0.21) + neutral-element
(L 0.25), mesh used four -element slots (L ~0.26) against surface L 0.18.
Reseed with high-contrast tokens (fog: surface bg + neutral-text; mesh:
-solid stops). Verified in a real browser: aurora/mesh/fog all paint.
- Repoint the six demo ledes from the ephemeral plan to
docs/decisions/design-text-effects.md (matches the READMEs).
check 0 own errors. Verified live (real Chrome): all effects paint, controls
tune params, nav reaches every new page.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
{
|
|
|
|
|
|
kind: 'group',
|
|
|
|
|
|
heading: 'Text effects',
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{ slug: '/uix/components/count-up', label: 'CountUp' },
|
fix(demos): reachable nav + real interactive Ambient testbed + visible fog/mesh
The catalog work shipped but was unreachable and impoverished:
- **Nav**: the sidebar IA (web/routes/uix/+layout@.svelte) is a manual list;
the new components were never added, so nothing linked to them. Add a
"Text effects" group (TextGradient/Circular/Blur/Focus/Scramble), CountUp
under Utilities, and a "Packs" group (Ambient · backgrounds).
- **Ambient demo → real testbed**: rebuild web/routes/uix/packs/ambient as a
proper interactive page — effect selector (32, grouped Agentic/Tier A/B),
live param controls AUTO-GENERATED from each effect's `defaults`
(number→slider, boolean→toggle, colors→editable token list, string→text),
a stage-background toggle (surface/black/white/transparent), reset, and a
copyable usage snippet. The old page only switched effect, no tuning.
- **fog + mesh painted nothing**: their seeded colors were near-invisible on
the dark base theme — fog used neutral-bg2 (L 0.21) + neutral-element
(L 0.25), mesh used four -element slots (L ~0.26) against surface L 0.18.
Reseed with high-contrast tokens (fog: surface bg + neutral-text; mesh:
-solid stops). Verified in a real browser: aurora/mesh/fog all paint.
- Repoint the six demo ledes from the ephemeral plan to
docs/decisions/design-text-effects.md (matches the READMEs).
check 0 own errors. Verified live (real Chrome): all effects paint, controls
tune params, nav reaches every new page.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
{ slug: '/uix/components/text-gradient', label: 'TextGradient' },
|
|
|
|
|
|
{ slug: '/uix/components/text-circular', label: 'TextCircular' },
|
|
|
|
|
|
{ slug: '/uix/components/text-blur', label: 'TextBlur' },
|
|
|
|
|
|
{ slug: '/uix/components/text-focus', label: 'TextFocus' },
|
|
|
|
|
|
{ slug: '/uix/components/text-scramble', label: 'TextScramble' }
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
kind: 'group',
|
|
|
|
|
|
heading: 'Packs',
|
|
|
|
|
|
items: [{ slug: '/uix/packs/ambient', label: 'Ambient · backgrounds', tag: 'pack' }]
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
kind: 'group',
|
|
|
|
|
|
heading: 'Layout',
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{ slug: '/uix/components/box', label: 'Box' },
|
|
|
|
|
|
{ slug: '/uix/components/flex', label: 'Flex' },
|
|
|
|
|
|
{ slug: '/uix/components/grid', label: 'Grid' },
|
|
|
|
|
|
{ slug: '/uix/components/stack', label: 'Stack' },
|
|
|
|
|
|
{ slug: '/uix/components/group', label: 'Group' },
|
|
|
|
|
|
{ slug: '/uix/components/wrap', label: 'Wrap' },
|
|
|
|
|
|
{ slug: '/uix/components/container', label: 'Container' },
|
feat(layout): Layout Batch 2 — aspect-ratio, auto-grid, banner, float
4 new layout primitives, full canon (morfo + eidos full set + canon
demo + README). `npm run component:audit`: 81 / 81 PASS, 0 NEEDS-WORK.
**AspectRatio** — composes through Box; emits single `--aspect-ratio`
CSS var (uses modern CSS `aspect-ratio` property, drops legacy
padding-bottom hack). Accepts numeric (16/9 → 1.777…) or string
("16/9") values.
**AutoGrid** — composes through Grid. Resolves `templateColumns` from
`minItemWidth` to `repeat(auto-fill, minmax(MIN, 1fr))` in JS so the
recipe stays declarative. Uses `auto-fill` (not auto-fit) to preserve
empty tracks when item count is low.
**Banner** — `<header role="banner">` announcement strip with intent
(full 8-role UIX `ColorRole`), variant (`ChipVariant` soft/solid/
outline/ghost), size (sm/md/lg). Dismissal is composition-driven:
consumer wraps in {#if show} and adds `<Banner.Close>`. No
`dismissible` boolean.
**Float** — CSS `float` primitive with logical `inline-start` /
`inline-end` sides. Redefined from air's 9-zone overlay primitive
(which becomes a future `<Positioned>` component if real demand
surfaces). Useful for inline images / pull-quotes / drop caps.
Each component ships morfo (`scope: ['eidos']`, 1 part, 0 events,
justified) + eidos set + README (Baseline / Comparativa /
Decisiones / Eventos Sema / Gaps with disposition markers /
Referencias / Passive justification) + canon demo with 6 tabs.
Sidebar nav appends the 4 entries to the existing Layout group.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/section', label: 'Section' },
|
|
|
|
|
|
{ slug: '/uix/components/aspect-ratio', label: 'AspectRatio' },
|
|
|
|
|
|
{ slug: '/uix/components/auto-grid', label: 'AutoGrid' },
|
uix(background): el fondo se mueve con el scroll, y con lo que el lector no pidió no
F3 (parallax) + F4 (demo y registro documental) + las correcciones de sus dos
auditorías, en un commit porque viven en los mismos ficheros: la demo enseña los
ejes que F3 añade, y separarlas dejaría un estado que nunca se probó.
Cinco maneras de que una capa deje de estarse quieta: `speed` (cuánto del token
de travel cubre mientras el anfitrión cruza el viewport), `bleed` (crece más
allá del anfitrión para que el viaje no arrastre su propio borde), `depth` (la
deriva contra el puntero), `spotlight` y `attach='fixed'`.
El travel es CSS: `animation-timeline: view()` lo gobierna desde la posición de
scroll, sin listener ni rAF. Sólo donde el motor no lo trae, la pila arranca
`ScrollProgress` y escribe `--background-progress`, que la rama `@supports not`
mete en la MISMA declaración; los dos caminos no pueden estar vivos a la vez
porque el JS comprueba la condición idéntica con `CSS.supports`, y ambos se
paran bajo `prefers-reduced-motion` — el parallax es movimiento atado al scroll
del propio lector, que es justo la clase que provoca síntomas vestibulares.
**La decisión que no estaba prevista.** El scroll y el puntero quieren mover la
MISMA capa, y una animación sobre `translate` gana a cualquier declaración
estática: el puntero habría dejado de existir sin más. Así que el scroll anima
una custom property REGISTRADA (`@property`, o interpolaría a saltos) y un único
`translate` compone los dos términos. Medido: parallax solo → `0px 30px`; con el
puntero arriba-derecha y `depth: 20px` → `20px 10px`. Es `translate` y nunca el
shorthand `transform`, la misma ley que sigue el lift del draggable con `scale`.
El precio, dicho porque en la primera redacción escribí lo contrario tres veces:
una custom property NO se puede compositar, así que el navegador recalcula
estilo cada frame. Para una decoración es el intercambio correcto —una property
por capa que viaja, ninguna bajo reduced motion— pero «va en el compositor» era
falso y ahora el código dice lo que ocurre.
**Dos footguns cerrados por forma, no por disciplina:**
- `attach='fixed'` se DECLARA desde la capa y la pila se recorta sola. Antes
había que escribirlo también en la pila, y olvidarlo dejaba la capa
`position: fixed` pintando a sangre por todo el viewport, detrás de todo y sin
error (un hijo fijo se escapa de `overflow: clip`; sólo un `clip-path` lo trae
de vuelta). La prop de la pila desaparece: no hay nada que olvidar.
- Un `speed` negativo —una capa que se mueve contra el scroll— invertía el
bleed: la capa ENCOGÍA y enseñaba justo los bordes que el bleed tapa. Ahora
usa la magnitud.
**Lo que costó medición**: el shorthand `animation` pone `duration: 0s` y una
línea de tiempo de progreso necesita el `auto` inicial, así que con el shorthand
la capa no se movía nunca (van longhands, con el porqué escrito) · mi listener
de puntero pedía un frame y no lo liberaba si el rect salía degenerado, matando
el puntero para el resto de la sesión (reescrito sin frame, con el rect cacheado
e invalidado por `pointerenter` y `observeResize`) · las cuatro registraciones
—`animated`, `pointer`, `scroll`, `fixed`— comparten un solo sitio,
`declare.svelte.ts`, donde vive la regla A30 y su segunda mitad: registrar desde
el init, y seguir el prop sin escribir en la primera pasada.
**La demo** (`/uix/components/background`, v2, nueve pestañas) monta un
ANFITRIÓN de verdad en el escenario, porque este componente es invisible por sí
solo y sin padre no se puede enseñar lo único que importa: que el padre se
adopta y el layout no se mueve. Los chips son uniones completas verificadas por
el TIPO (`Record<Union, 0>`): un miembro que falte es error de compilación.
Y fue la demo la que destapó que, con A30, encender `animate` en caliente no
hacía aparecer el control de pausa — el registro era un hecho de montaje.
Invisible en una sonda, obvio con un interruptor.
Registro documental (D-BG.11): `next-features.md` §11 · la frase en
`design-text-effects.md` (el mismo corte canon/pack leído desde el otro lado) ·
`PLAN-blocks-quality.md` Q0.3 → sucesor · `surface/README.md` §Gaps «scrim de
autoría» CERRADO por `Background.Scrim` · glosario con entrada `Background` y
`Aura` corregida (decía «Not built yet» y está construido) · y en
`motion-guide.md` §8 + el RFC: el travel ligado al scroll no es un preset —un
preset nombra una transición discreta CON duración, y esto es modulación
continua sin ninguna— y sólo se replantea como dominio con un segundo consumidor.
Verificado en Chrome real: el puntero mueve `depth` y `spotlight` con los
valores exactos y vuelven al centro al salir · `attach='fixed'` estampa y
retira el recorte de la pila · el bleed aguanta el speed negativo · RTL: el
`translate` del puntero se mantiene FÍSICO y el bleed en el eje de bloque · cada
control de la demo cambia algo (los de `spotlight` y `depth` no llegaban a tres
de las cuatro clases de capa hasta la segunda auditoría).
⚠️ SIN VERIFICAR, y no lo doy por bueno: el travel real al hacer scroll, los 60
fps y el detector de reflow. El panel del navegador va oculto con viewport 0×0 y
ahí las animaciones scroll-driven declaradas en CSS no se activan — comprobado
que es del ENTORNO con un caso mínimo inyectado (un `div` pelado con
`animation-timeline: view()` sale inactivo mientras una `ViewTimeline` creada
por API sobre el mismo sujeto marca 68%). Necesita una pasada con Chrome
visible.
Gates: audit `--only background` PASS 0 errores · eidos-lint invalid 0 ·
`rtl:check` 0/180 · `docs:check` 0/0 en 634 docs · `blocks:check` 0/18 ·
`morfo:check` PASS · smoke PASS · 441/442 (el fallo es el `skin-media-player` de
siempre) · `check` 0 errores propios.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
{ slug: '/uix/components/background', label: 'Background' },
|
feat(layout): Layout Batch 2 — aspect-ratio, auto-grid, banner, float
4 new layout primitives, full canon (morfo + eidos full set + canon
demo + README). `npm run component:audit`: 81 / 81 PASS, 0 NEEDS-WORK.
**AspectRatio** — composes through Box; emits single `--aspect-ratio`
CSS var (uses modern CSS `aspect-ratio` property, drops legacy
padding-bottom hack). Accepts numeric (16/9 → 1.777…) or string
("16/9") values.
**AutoGrid** — composes through Grid. Resolves `templateColumns` from
`minItemWidth` to `repeat(auto-fill, minmax(MIN, 1fr))` in JS so the
recipe stays declarative. Uses `auto-fill` (not auto-fit) to preserve
empty tracks when item count is low.
**Banner** — `<header role="banner">` announcement strip with intent
(full 8-role UIX `ColorRole`), variant (`ChipVariant` soft/solid/
outline/ghost), size (sm/md/lg). Dismissal is composition-driven:
consumer wraps in {#if show} and adds `<Banner.Close>`. No
`dismissible` boolean.
**Float** — CSS `float` primitive with logical `inline-start` /
`inline-end` sides. Redefined from air's 9-zone overlay primitive
(which becomes a future `<Positioned>` component if real demand
surfaces). Useful for inline images / pull-quotes / drop caps.
Each component ships morfo (`scope: ['eidos']`, 1 part, 0 events,
justified) + eidos set + README (Baseline / Comparativa /
Decisiones / Eventos Sema / Gaps with disposition markers /
Referencias / Passive justification) + canon demo with 6 tabs.
Sidebar nav appends the 4 entries to the existing Layout group.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/banner', label: 'Banner' },
|
feat(eidos): Card, Avatar ext, Image, SText, Badge, service components + dates engine fixes
Sprint completo:
- Card (eidos + sema): elevation/intent/interactive, eventos select+activate.
- Avatar: ring, AvatarBadge, AvatarGroup separados; demo Avatar standalone.
- Image: status surface (idle/loading/loaded/error) consumiendo soma ImageProvider.
- SText + STextVirtualList: canvas-text engine portado de morfo-driven-soma como
componentes separados para optimizar bundle (no se importa canvas si no se usa).
- Badge: primitivo eidos puro.
- Trans, FormatNumber, FormatDate, RelativeTime: 4 service components greppables
alrededor de uix.langs y uix.format. Nav group "Utilities".
Bugs fix arts/format/dates (engine-dates.ts):
1. Preset / per-axis conflict: Intl.DateTimeFormat rechaza dateStyle/timeStyle
con cualquier per-axis (weekday/era/year/month/day/dayPeriod/hour/minute/
second/fractionalSecondDigits) o con timeZoneName. Engine drop-ea ambos
presets por defecto cuando detecta conflict (regla estricta — un solo
per-axis hace ilegales los dos presets).
2. Per-call locale override: formatDate/Time/DateTime aceptan tercer arg
locale?: string. No muta engine state. Cache (getCachedDateFormat) keyea
por (locale x options). resolveCallHourCycle: cuando hay locale override
y pref auto, el ciclo horario se resuelve desde el locale OVERRIDE.
3. withHourCycle precedence: caller hourCycle/hour12 ahora gana sobre
preferencia del engine. Antes spread mal ordenado sobrescribia 'h12' del
caller con 'h23' del engine cuando locale resolvia a 24h.
FormatDate component vuelve a rutear siempre por uix.format.dates
(cache + hourCycle + locale reactivo), pasando locale como tercer arg.
Demos service components autonomos del toolbar global: cada uno owns su
demoLocale state con 7 chips (es-ES/en-US/en-GB/de-DE/fr-FR/ja-JP/ar-EG).
FormatDate stage filtra dateStyle/timeStyle por mode + chip hourCycle anadido.
Tests: 14/14 passing en engine-dates.test.ts (+6 nuevos: conflict handling,
locale override, hourCycle precedence).
Docs: arts/format/README.md (handoff 2026-05-25 con las 3 reglas),
README.md por service component, project_service_components_sprint_2026-05-25.md,
feedback_service_demos_own_locale.md (pattern reusable).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/card', label: 'Card' },
|
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
{ slug: '/uix/components/card-group', label: 'CardGroup' },
|
feat(layout): Layout Batch 3 — scroll-area, separator, splitter + splitter sema rewire
3 new layout primitives close the air port set (15 / 15 done).
`npm run component:audit`: 91 / 91 PASS, all 3 new PASS with 0 errors.
**scroll-area** — eidos compound wrapper over the existing soma:
`<ScrollArea><ScrollArea.Viewport>{content}</ScrollArea.Viewport>
<ScrollArea.Scrollbar orientation="vertical"><ScrollArea.Thumb />
</ScrollArea.Scrollbar><ScrollArea.Corner /></ScrollArea>`. Mirrors
Radix Primitives / Bits UI shape. Demo 667 lines.
**separator** — visual primitive (`scope: ['eidos']`) with conditional
ARIA: `role="separator"` + `aria-orientation` when not decorative,
`aria-hidden="true"` when decorative. New morfo
`src/uix/morfo/components/separator.ts`. Demo 645 lines.
**splitter** — eidos compound over existing soma:
`<Splitter orientation="horizontal"><Splitter.Panel>{a}</Splitter.Panel>
<Splitter.ResizeTrigger /><Splitter.Panel>{b}</Splitter.Panel>
</Splitter>`. Demo 824 lines.
**Splitter sema rewire — match Slider's handle/drag/commit shape:**
The morfo previously declared only `commit-resize`. Direct-manipulation
primitives (Slider, Splitter) should share the same event vocabulary so
audio/haptic projections feel coherent across the family. Renamed +
expanded to mirror Slider:
- `handle-pick` (family: handle, verb: pick, sequence: coincident,
target: resize-trigger) — fires on pointerdown when user grabs the
trigger.
- `handle-drag` (family: handle, verb: drag, sequence: coincident,
target: resize-trigger) — fires on each pointermove frame.
- `commit-set` (family: commit, verb: set, intent: neutral,
sequence: post, target: provider) — fires on pointerup. The runtime
`commit-set` handler is wired to call the consumer's `onResizeEnd`
callback, so sema and JS callback land at the same moment (parity
with Slider's `onValueCommit`).
Soma provider triggers these via `this.runtime.trigger(name)` in
`onpointerdown` / `onpointermove` / `notifyResizeEnd` (the latter
unchanged externally — internally now routes through the runtime
trigger).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{ slug: '/uix/components/float', label: 'Float' },
|
|
|
|
|
|
{ slug: '/uix/components/scroll-area', label: 'ScrollArea' },
|
|
|
|
|
|
{ slug: '/uix/components/separator', label: 'Separator' },
|
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado
El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos
independientes, y el segundo era del ecosistema entero.
1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform
FISICO en las tres reglas verticales (inset-inline-start: 50% +
translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y
el relleno quedaban fuera de eje un ancho entero. Medido en Chrome:
raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba
porque ya usaba margin-inline-start — ese era el idioma correcto del
propio fichero y es el que aplico. Mismo defecto en accordion.css:
text-align: left en el trigger, que era el UNICO text-align fisico de
todo eidos; ahora quedan cero.
2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la
celda $state, asi que prefs.<dim>.get() — y con ella el
soma.prefs.getDir() que la guia manda usar — era una lectura sin
tracking. Los ~64 componentes que resuelven su direccion desde prefs la
congelaban al montarse. La proyeccion DOM se salvaba porque usa
onChange: misma dimension, dos caminos de lectura, solo uno reactivo.
Arreglado con un contador de cambios POR dimension, bombeado desde el
effectiveDiff que el motor ya calcula, de modo que cubre tambien el
cambio por DERIVACION (direction siguiendo a language) y no solo el
intent. Un contador por clave y no una celda global: con una sola celda,
tocar motion despertaria las 64 derivaciones de direccion. El test
negativo lo clava.
3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz
con un valor siempre concreto, asi que una app que ponga html dir=rtl sin
registrar la preferencia se encontraba 33 islas volteadas del reves.
Investigadas las cinco librerias de referencia: MUI y react-aria no
estampan nunca, Radix estampa siempre (sus mantenedores arrastran
discussions/1405 por esto), y Zag estampa prop("dir") — presente si
alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme:
el atributo NUNCA tiene defecto. La herencia la hace el navegador por
AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena
prop -> prefs -> 'ltr' se queda intacta.
4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los
wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico
—heredar del menu padre— enterrado en dos expresiones sueltas.
Normalizado a activeDir(getter, soma) en los 36, un solo tipo
Direction | undefined, y el escalon de los submenus declarado en la
llamada, donde se ve.
5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace
clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe
en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era
disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar
el control — la cadena documentada funcionando de punta a punta por
primera vez.
Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en
101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en
vivo, y el arabe voltea por derivacion.
QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del
accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es
un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL);
html lang no sigue al idioma; smoke y perm:check sin ejecutar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
{ slug: '/uix/components/splitter', label: 'Splitter' },
|
feat(eidos): Affix — la pieza que Sticky no puede ser, y la capa que Fab y MenuDial ya habian copiado dos veces
Un aviso al principio del documento se va con la pagina: `position: sticky` se
despega en cuanto termina su contenedor. Medido el 2026-08-10 sobre el block
`banner` — `Sticky edge="bottom"` acaba en `top: -8` sin `data-stuck`. No es un
cableado roto: es que un elemento anclado al viewport es `fixed`, y eso es otro
componente.
Pero no es un componente nuevo: la capacidad YA existia dos veces. `fab.css`
fija 4 esquinas, `menu-dial.css` nueve, y las dos ya divergian (`--fab-offset`
publico vs `--_menu-dial-offset` interno; `var(--fab-z)` vs
`var(--z-index-sticky, 1100)`, un fallback de 1100 sobre un token que vale 100).
Y antes de eso existio entera: `air/layout/float`, el primitivo de 9 zonas que
el refactor retiro sin sustituto — lo dicen los README de `float` y de `Fab`.
Asi que `affix.css` no es un tercer ejemplar: es la CAPA, enganchada en
`data-affix-placement` y no en `data-affix`. El split es portante —
`morfo-check` selecciona `[data-affix]` en toda la pagina y valida cada match
contra `affixMorfo`, asi que si `Fab` estampara la identidad quedaria soldado a
este contrato. Estampando solo el gancho de capa, no. Es el patron de
`lib/list-surface.css`: una fuente, cero nodos extra.
Lo que salio midiendo, no razonando:
- `--z-index-affix: 150`, peldano nuevo. Con la tira en `top` empataba con
`--sticky-z-index` (100 los dos) y el empate lo rompe el ORDEN DEL DOM: el
aviso va antes que la cabecera en el fuente, luego perdia. 30,7px de solape con
el cromo encima — el fallo original reproducido por su sustituto. Un peldano
toca DOS sitios: `STATIC_Z_INDEX` y la lista cerrada `Z_INDEX_KEYS`.
- NO compone `<Box>`, aunque sea el patron de los primitivos de layout:
`box.css` declara `position: var(--box-position, revert-layer)` a la misma
especificidad que el gancho, y eidos no usa `@layer`. En el orden de carga
equivocado, `position: static` — un `fixed` que no hace nada.
- `stretch` es solo de los bordes de bloque. El rail del eje inline se envio y se
retiro el mismo dia: `Affix` no dimensiona a su hijo, asi que era una caja
invisible de 380px con el hijo de 47px arriba, identica a `top-start` en
pantalla. El marco medido decia otra cosa; la pintura mandaba.
- `env(safe-area-inset-*)` es fisico y los anclajes logicos: remapeado bajo
`:dir(rtl)`. Emparejar `inset-inline-start` con `safe-area-inset-left` despeja
la muesca equivocada en RTL apaisado — que es lo que hacen hoy `fab` y
`menu-dial`, registrado.
`banner` recupera lo que su README daba por imposible: `affix="top" | "bottom"`.
La disposicion anterior decia «app-land, el canon ya trae Sticky» y nombraba un
componente que no puede hacerlo.
Verificado con recorrido real (342px en la demo, 1247px en la preview del
block): desplazamiento 0 en los dos bordes, nueve zonas exactas, RTL espejando,
el ancestro transformado derivando -342px como esta documentado, y
`elementFromPoint` sobre la tira devolviendo el aviso y no el cromo.
component:audit PASS 0/0 · morfo:check PASS · rtl 0/178 · smoke 311/311.
Queda: migrar `Fab` y `MenuDial` a la capa.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
{ slug: '/uix/components/sticky', label: 'Sticky' },
|
|
|
|
|
|
{ slug: '/uix/components/affix', label: 'Affix' }
|
|
|
|
|
|
]
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
}
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const componentCount = $derived(
|
|
|
|
|
|
nav
|
|
|
|
|
|
.filter((s): s is Extract<Section, { kind: 'group' }> => s.kind === 'group')
|
|
|
|
|
|
.filter((g) => ['Controls', 'Media', 'Overlays'].includes(g.heading))
|
|
|
|
|
|
.reduce((acc, g) => acc + g.items.length, 0)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const current = $derived(page.url.pathname.replace(/\/$/, '') || '/uix');
|
|
|
|
|
|
|
|
|
|
|
|
// ── Theme ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
let theme = $state<'light' | 'dark'>('light');
|
|
|
|
|
|
const eidosModeListeners = new Set<(value: 'light' | 'dark') => void>();
|
|
|
|
|
|
const activeEidos = ActiveEidos.create({
|
|
|
|
|
|
applyDom: true,
|
|
|
|
|
|
theme: 'base',
|
|
|
|
|
|
modeSource: {
|
|
|
|
|
|
get: () => theme,
|
|
|
|
|
|
onChange(handler) {
|
|
|
|
|
|
eidosModeListeners.add(handler);
|
|
|
|
|
|
return () => eidosModeListeners.delete(handler);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// A small canonical named-gradient set for the docs site — role-derived, so
|
|
|
|
|
|
// they re-tint with the seed and flip light/dark for free (the whole point of
|
|
|
|
|
|
// the gradient axis). Components name them: `<TextGradient colors="brand">`,
|
|
|
|
|
|
// `background: var(--gradient-brand)`. Both are LINEAR (a single `<image>`),
|
|
|
|
|
|
// so they're valid `background-image` values and paint through `background-clip:
|
|
|
|
|
|
// text` — a role-derived MESH (aurora) carries a base color and would not.
|
|
|
|
|
|
const brandGradient: Gradient = {
|
|
|
|
|
|
kind: 'linear',
|
|
|
|
|
|
angle: 110,
|
|
|
|
|
|
interpolation: { space: 'oklch' },
|
|
|
|
|
|
stops: [
|
|
|
|
|
|
{ color: { kind: 'role', role: 'primary', slot: 'solid' } },
|
|
|
|
|
|
{ color: { kind: 'role', role: 'secondary', slot: 'solid' } },
|
|
|
|
|
|
{ color: { kind: 'role', role: 'tertiary', slot: 'solid' } }
|
|
|
|
|
|
]
|
|
|
|
|
|
};
|
|
|
|
|
|
const spectrumGradient: Gradient = {
|
|
|
|
|
|
kind: 'linear',
|
|
|
|
|
|
angle: 90,
|
|
|
|
|
|
interpolation: { space: 'oklch', hue: 'longer' },
|
|
|
|
|
|
stops: [
|
|
|
|
|
|
{ color: { kind: 'role', role: 'primary', slot: 'solid' } },
|
|
|
|
|
|
{ color: { kind: 'role', role: 'affirm', slot: 'solid' } },
|
|
|
|
|
|
{ color: { kind: 'role', role: 'secondary', slot: 'solid' } },
|
|
|
|
|
|
{ color: { kind: 'role', role: 'risk', slot: 'solid' } },
|
|
|
|
|
|
{ color: { kind: 'role', role: 'loss', slot: 'solid' } }
|
|
|
|
|
|
]
|
|
|
|
|
|
};
|
|
|
|
|
|
activeEidos.applyGradients({ brand: brandGradient, spectrum: spectrumGradient });
|
|
|
|
|
|
|
|
|
|
|
|
onDestroy(() => {
|
|
|
|
|
|
activeEidos.dispose();
|
|
|
|
|
|
uix.dispose();
|
|
|
|
|
|
});
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
|
if (typeof document === 'undefined') return;
|
|
|
|
|
|
const stored = localStorage.getItem('uix-docs-theme');
|
|
|
|
|
|
if (stored === 'dark' || stored === 'light') {
|
|
|
|
|
|
theme = stored;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const prefersDark = window.matchMedia?.('(prefers-color-scheme: dark)').matches;
|
|
|
|
|
|
theme = prefersDark ? 'dark' : 'light';
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
|
if (typeof localStorage === 'undefined') return;
|
|
|
|
|
|
localStorage.setItem('uix-docs-theme', theme);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
|
const currentTheme = theme;
|
|
|
|
|
|
for (const listener of eidosModeListeners) listener(currentTheme);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
let search = $state('');
|
|
|
|
|
|
|
|
|
|
|
|
// ── Topbar prefs ──────────────────────────────────────────────────────
|
|
|
|
|
|
// All persisted to localStorage. The sema toggles will wire to the
|
|
|
|
|
|
// engine's channels once those expose runtime setEnabled APIs; today
|
|
|
|
|
|
// they store preference + reflect it in the UI.
|
|
|
|
|
|
|
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado
El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos
independientes, y el segundo era del ecosistema entero.
1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform
FISICO en las tres reglas verticales (inset-inline-start: 50% +
translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y
el relleno quedaban fuera de eje un ancho entero. Medido en Chrome:
raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba
porque ya usaba margin-inline-start — ese era el idioma correcto del
propio fichero y es el que aplico. Mismo defecto en accordion.css:
text-align: left en el trigger, que era el UNICO text-align fisico de
todo eidos; ahora quedan cero.
2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la
celda $state, asi que prefs.<dim>.get() — y con ella el
soma.prefs.getDir() que la guia manda usar — era una lectura sin
tracking. Los ~64 componentes que resuelven su direccion desde prefs la
congelaban al montarse. La proyeccion DOM se salvaba porque usa
onChange: misma dimension, dos caminos de lectura, solo uno reactivo.
Arreglado con un contador de cambios POR dimension, bombeado desde el
effectiveDiff que el motor ya calcula, de modo que cubre tambien el
cambio por DERIVACION (direction siguiendo a language) y no solo el
intent. Un contador por clave y no una celda global: con una sola celda,
tocar motion despertaria las 64 derivaciones de direccion. El test
negativo lo clava.
3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz
con un valor siempre concreto, asi que una app que ponga html dir=rtl sin
registrar la preferencia se encontraba 33 islas volteadas del reves.
Investigadas las cinco librerias de referencia: MUI y react-aria no
estampan nunca, Radix estampa siempre (sus mantenedores arrastran
discussions/1405 por esto), y Zag estampa prop("dir") — presente si
alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme:
el atributo NUNCA tiene defecto. La herencia la hace el navegador por
AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena
prop -> prefs -> 'ltr' se queda intacta.
4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los
wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico
—heredar del menu padre— enterrado en dos expresiones sueltas.
Normalizado a activeDir(getter, soma) en los 36, un solo tipo
Direction | undefined, y el escalon de los submenus declarado en la
llamada, donde se ve.
5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace
clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe
en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era
disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar
el control — la cadena documentada funcionando de punta a punta por
primera vez.
Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en
101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en
vivo, y el arabe voltea por derivacion.
QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del
accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es
un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL);
html lang no sigue al idioma; smoke y perm:check sin ejecutar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
// Three states, not two. `auto` CLEARS the direction intent so the built-in
|
|
|
|
|
|
// `directionDimension` can derive it from `language` — the chain the
|
|
|
|
|
|
// architecture documents ("if the user sets no intent, it derives from
|
|
|
|
|
|
// prefs.language"). With a two-state toggle the shell set an intent from the
|
|
|
|
|
|
// first frame, which permanently masked that derivation: picking Arabic
|
|
|
|
|
|
// would have changed nothing.
|
|
|
|
|
|
let dirMode = $state<'auto' | 'ltr' | 'rtl'>('auto');
|
|
|
|
|
|
/** Effective direction after prefs resolves intent → derivation → default. */
|
|
|
|
|
|
const dirSlot = readActivePrefsSlot<'ltr' | 'rtl'>(uix.prefs, 'direction');
|
|
|
|
|
|
const effectiveDir = $derived(dirSlot?.get() ?? 'ltr');
|
|
|
|
|
|
let language = $state<Language>('es');
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
let currency = $state<Currency>('EUR');
|
|
|
|
|
|
let units = $state<UnitSystem>('metric');
|
|
|
|
|
|
let numberFormat = $state<NumberFormat>('es-ES');
|
|
|
|
|
|
let soundOn = $state(true);
|
|
|
|
|
|
let motionOn = $state(true);
|
|
|
|
|
|
let hapticOn = $state(true);
|
|
|
|
|
|
|
|
|
|
|
|
// ── Display axes — the interrelated theming knobs (density · scaling · border
|
|
|
|
|
|
// width). Projected onto :root (below) via a managed stylesheet so EVERY demo —
|
|
|
|
|
|
// including portaled overlays (dialog/popover) — reflects the change live.
|
|
|
|
|
|
type Density = 'compact' | 'comfortable' | 'spacious';
|
|
|
|
|
|
const DENSITIES: readonly Density[] = ['compact', 'comfortable', 'spacious'];
|
|
|
|
|
|
const DENSITY_SCALES: Record<Density, { space: number; control: number }> = {
|
|
|
|
|
|
compact: { space: 0.84, control: 0.9 },
|
|
|
|
|
|
comfortable: { space: 1, control: 1 },
|
|
|
|
|
|
spacious: { space: 1.16, control: 1.12 }
|
|
|
|
|
|
};
|
|
|
|
|
|
let density = $state<Density>('comfortable');
|
|
|
|
|
|
let scaling = $state(1);
|
|
|
|
|
|
let borderWidth = $state(1);
|
|
|
|
|
|
|
|
|
|
|
|
const LANGUAGE_LABELS: Record<Language, string> = {
|
|
|
|
|
|
es: 'Español',
|
|
|
|
|
|
en: 'English',
|
|
|
|
|
|
fr: 'Français',
|
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado
El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos
independientes, y el segundo era del ecosistema entero.
1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform
FISICO en las tres reglas verticales (inset-inline-start: 50% +
translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y
el relleno quedaban fuera de eje un ancho entero. Medido en Chrome:
raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba
porque ya usaba margin-inline-start — ese era el idioma correcto del
propio fichero y es el que aplico. Mismo defecto en accordion.css:
text-align: left en el trigger, que era el UNICO text-align fisico de
todo eidos; ahora quedan cero.
2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la
celda $state, asi que prefs.<dim>.get() — y con ella el
soma.prefs.getDir() que la guia manda usar — era una lectura sin
tracking. Los ~64 componentes que resuelven su direccion desde prefs la
congelaban al montarse. La proyeccion DOM se salvaba porque usa
onChange: misma dimension, dos caminos de lectura, solo uno reactivo.
Arreglado con un contador de cambios POR dimension, bombeado desde el
effectiveDiff que el motor ya calcula, de modo que cubre tambien el
cambio por DERIVACION (direction siguiendo a language) y no solo el
intent. Un contador por clave y no una celda global: con una sola celda,
tocar motion despertaria las 64 derivaciones de direccion. El test
negativo lo clava.
3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz
con un valor siempre concreto, asi que una app que ponga html dir=rtl sin
registrar la preferencia se encontraba 33 islas volteadas del reves.
Investigadas las cinco librerias de referencia: MUI y react-aria no
estampan nunca, Radix estampa siempre (sus mantenedores arrastran
discussions/1405 por esto), y Zag estampa prop("dir") — presente si
alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme:
el atributo NUNCA tiene defecto. La herencia la hace el navegador por
AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena
prop -> prefs -> 'ltr' se queda intacta.
4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los
wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico
—heredar del menu padre— enterrado en dos expresiones sueltas.
Normalizado a activeDir(getter, soma) en los 36, un solo tipo
Direction | undefined, y el escalon de los submenus declarado en la
llamada, donde se ve.
5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace
clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe
en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era
disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar
el control — la cadena documentada funcionando de punta a punta por
primera vez.
Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en
101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en
vivo, y el arabe voltea por derivacion.
QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del
accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es
un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL);
html lang no sigue al idioma; smoke y perm:check sin ejecutar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
de: 'Deutsch',
|
|
|
|
|
|
ar: 'العربية'
|
|
|
|
|
|
};
|
|
|
|
|
|
const CURRENCY_LABELS: Record<Currency, string> = {
|
|
|
|
|
|
EUR: 'Euro',
|
|
|
|
|
|
USD: 'US Dollar',
|
|
|
|
|
|
GBP: 'British Pound',
|
|
|
|
|
|
JPY: 'Japanese Yen'
|
|
|
|
|
|
};
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
const UNIT_LABELS: Record<UnitSystem, string> = { metric: 'Metric', imperial: 'Imperial' };
|
|
|
|
|
|
const NUMBER_LABELS: Record<NumberFormat, string> = {
|
|
|
|
|
|
'es-ES': 'Spain · 1.234,56',
|
|
|
|
|
|
'en-US': 'US · 1,234.56',
|
|
|
|
|
|
'en-GB': 'UK · 1,234.56',
|
|
|
|
|
|
'de-DE': 'Germany · 1.234,56'
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Persist all topbar prefs in one bag.
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
|
if (typeof localStorage === 'undefined') return;
|
|
|
|
|
|
const stored = localStorage.getItem('uix-docs-prefs');
|
|
|
|
|
|
if (!stored) return;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const p = JSON.parse(stored);
|
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado
El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos
independientes, y el segundo era del ecosistema entero.
1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform
FISICO en las tres reglas verticales (inset-inline-start: 50% +
translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y
el relleno quedaban fuera de eje un ancho entero. Medido en Chrome:
raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba
porque ya usaba margin-inline-start — ese era el idioma correcto del
propio fichero y es el que aplico. Mismo defecto en accordion.css:
text-align: left en el trigger, que era el UNICO text-align fisico de
todo eidos; ahora quedan cero.
2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la
celda $state, asi que prefs.<dim>.get() — y con ella el
soma.prefs.getDir() que la guia manda usar — era una lectura sin
tracking. Los ~64 componentes que resuelven su direccion desde prefs la
congelaban al montarse. La proyeccion DOM se salvaba porque usa
onChange: misma dimension, dos caminos de lectura, solo uno reactivo.
Arreglado con un contador de cambios POR dimension, bombeado desde el
effectiveDiff que el motor ya calcula, de modo que cubre tambien el
cambio por DERIVACION (direction siguiendo a language) y no solo el
intent. Un contador por clave y no una celda global: con una sola celda,
tocar motion despertaria las 64 derivaciones de direccion. El test
negativo lo clava.
3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz
con un valor siempre concreto, asi que una app que ponga html dir=rtl sin
registrar la preferencia se encontraba 33 islas volteadas del reves.
Investigadas las cinco librerias de referencia: MUI y react-aria no
estampan nunca, Radix estampa siempre (sus mantenedores arrastran
discussions/1405 por esto), y Zag estampa prop("dir") — presente si
alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme:
el atributo NUNCA tiene defecto. La herencia la hace el navegador por
AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena
prop -> prefs -> 'ltr' se queda intacta.
4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los
wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico
—heredar del menu padre— enterrado en dos expresiones sueltas.
Normalizado a activeDir(getter, soma) en los 36, un solo tipo
Direction | undefined, y el escalon de los submenus declarado en la
llamada, donde se ve.
5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace
clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe
en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era
disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar
el control — la cadena documentada funcionando de punta a punta por
primera vez.
Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en
101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en
vivo, y el arabe voltea por derivacion.
QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del
accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es
un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL);
html lang no sigue al idioma; smoke y perm:check sin ejecutar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
if (p.dir === 'auto' || p.dir === 'ltr' || p.dir === 'rtl') dirMode = p.dir;
|
|
|
|
|
|
if (p.language) language = p.language;
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
if (p.currency) currency = p.currency;
|
|
|
|
|
|
if (p.units) units = p.units;
|
|
|
|
|
|
if (p.numberFormat) numberFormat = p.numberFormat;
|
|
|
|
|
|
if (typeof p.soundOn === 'boolean') soundOn = p.soundOn;
|
|
|
|
|
|
if (typeof p.motionOn === 'boolean') motionOn = p.motionOn;
|
|
|
|
|
|
if (typeof p.hapticOn === 'boolean') hapticOn = p.hapticOn;
|
|
|
|
|
|
if (p.density === 'compact' || p.density === 'comfortable' || p.density === 'spacious')
|
|
|
|
|
|
density = p.density;
|
|
|
|
|
|
if (typeof p.scaling === 'number') scaling = p.scaling;
|
|
|
|
|
|
if (typeof p.borderWidth === 'number') borderWidth = p.borderWidth;
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
} catch {
|
|
|
|
|
|
/* ignore corrupt prefs blob */
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
|
if (typeof localStorage === 'undefined') return;
|
|
|
|
|
|
const blob = {
|
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado
El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos
independientes, y el segundo era del ecosistema entero.
1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform
FISICO en las tres reglas verticales (inset-inline-start: 50% +
translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y
el relleno quedaban fuera de eje un ancho entero. Medido en Chrome:
raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba
porque ya usaba margin-inline-start — ese era el idioma correcto del
propio fichero y es el que aplico. Mismo defecto en accordion.css:
text-align: left en el trigger, que era el UNICO text-align fisico de
todo eidos; ahora quedan cero.
2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la
celda $state, asi que prefs.<dim>.get() — y con ella el
soma.prefs.getDir() que la guia manda usar — era una lectura sin
tracking. Los ~64 componentes que resuelven su direccion desde prefs la
congelaban al montarse. La proyeccion DOM se salvaba porque usa
onChange: misma dimension, dos caminos de lectura, solo uno reactivo.
Arreglado con un contador de cambios POR dimension, bombeado desde el
effectiveDiff que el motor ya calcula, de modo que cubre tambien el
cambio por DERIVACION (direction siguiendo a language) y no solo el
intent. Un contador por clave y no una celda global: con una sola celda,
tocar motion despertaria las 64 derivaciones de direccion. El test
negativo lo clava.
3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz
con un valor siempre concreto, asi que una app que ponga html dir=rtl sin
registrar la preferencia se encontraba 33 islas volteadas del reves.
Investigadas las cinco librerias de referencia: MUI y react-aria no
estampan nunca, Radix estampa siempre (sus mantenedores arrastran
discussions/1405 por esto), y Zag estampa prop("dir") — presente si
alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme:
el atributo NUNCA tiene defecto. La herencia la hace el navegador por
AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena
prop -> prefs -> 'ltr' se queda intacta.
4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los
wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico
—heredar del menu padre— enterrado en dos expresiones sueltas.
Normalizado a activeDir(getter, soma) en los 36, un solo tipo
Direction | undefined, y el escalon de los submenus declarado en la
llamada, donde se ve.
5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace
clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe
en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era
disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar
el control — la cadena documentada funcionando de punta a punta por
primera vez.
Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en
101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en
vivo, y el arabe voltea por derivacion.
QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del
accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es
un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL);
html lang no sigue al idioma; smoke y perm:check sin ejecutar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
dir: dirMode,
|
|
|
|
|
|
language,
|
|
|
|
|
|
currency,
|
|
|
|
|
|
units,
|
|
|
|
|
|
numberFormat,
|
|
|
|
|
|
soundOn,
|
|
|
|
|
|
motionOn,
|
|
|
|
|
|
hapticOn,
|
|
|
|
|
|
density,
|
|
|
|
|
|
scaling,
|
|
|
|
|
|
borderWidth
|
|
|
|
|
|
};
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
localStorage.setItem('uix-docs-prefs', JSON.stringify(blob));
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
|
uix.prefs.setIntent('language', language);
|
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado
El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos
independientes, y el segundo era del ecosistema entero.
1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform
FISICO en las tres reglas verticales (inset-inline-start: 50% +
translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y
el relleno quedaban fuera de eje un ancho entero. Medido en Chrome:
raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba
porque ya usaba margin-inline-start — ese era el idioma correcto del
propio fichero y es el que aplico. Mismo defecto en accordion.css:
text-align: left en el trigger, que era el UNICO text-align fisico de
todo eidos; ahora quedan cero.
2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la
celda $state, asi que prefs.<dim>.get() — y con ella el
soma.prefs.getDir() que la guia manda usar — era una lectura sin
tracking. Los ~64 componentes que resuelven su direccion desde prefs la
congelaban al montarse. La proyeccion DOM se salvaba porque usa
onChange: misma dimension, dos caminos de lectura, solo uno reactivo.
Arreglado con un contador de cambios POR dimension, bombeado desde el
effectiveDiff que el motor ya calcula, de modo que cubre tambien el
cambio por DERIVACION (direction siguiendo a language) y no solo el
intent. Un contador por clave y no una celda global: con una sola celda,
tocar motion despertaria las 64 derivaciones de direccion. El test
negativo lo clava.
3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz
con un valor siempre concreto, asi que una app que ponga html dir=rtl sin
registrar la preferencia se encontraba 33 islas volteadas del reves.
Investigadas las cinco librerias de referencia: MUI y react-aria no
estampan nunca, Radix estampa siempre (sus mantenedores arrastran
discussions/1405 por esto), y Zag estampa prop("dir") — presente si
alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme:
el atributo NUNCA tiene defecto. La herencia la hace el navegador por
AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena
prop -> prefs -> 'ltr' se queda intacta.
4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los
wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico
—heredar del menu padre— enterrado en dos expresiones sueltas.
Normalizado a activeDir(getter, soma) en los 36, un solo tipo
Direction | undefined, y el escalon de los submenus declarado en la
llamada, donde se ve.
5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace
clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe
en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era
disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar
el control — la cadena documentada funcionando de punta a punta por
primera vez.
Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en
101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en
vivo, y el arabe voltea por derivacion.
QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del
accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es
un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL);
html lang no sigue al idioma; smoke y perm:check sin ejecutar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
// `auto` must CLEAR, not set: intent beats derivation, so setting any
|
|
|
|
|
|
// value here would stop `direction` from following `language`.
|
|
|
|
|
|
if (dirMode === 'auto') uix.prefs.clearIntent('direction');
|
|
|
|
|
|
else uix.prefs.setIntent('direction', dirMode);
|
|
|
|
|
|
uix.prefs.setIntent('locale', numberFormat);
|
|
|
|
|
|
uix.prefs.setIntent('currency', currency);
|
|
|
|
|
|
uix.prefs.setIntent('unitSystem', units);
|
|
|
|
|
|
uix.prefs.setIntent('sound', soundOn ? 'allow' : 'reduce');
|
|
|
|
|
|
uix.prefs.setIntent('motion', motionOn ? 'allow' : 'reduce');
|
|
|
|
|
|
uix.prefs.setIntent('haptic', hapticOn ? 'allow' : 'reduce');
|
feat(web): /uix/docs/sound — la pagina que explica el canal del sonido
Primera de las ocho del plan, en castellano y DERIVADA del codigo vivo: las
tablas leen SEMA_MAP y el resolver real, nunca una transcripcion. Una pagina
que copiara el catalogo envejeceria al primer gain que alguien tocara — que
es exactamente la deriva que este subsistema se reconstruyo para quitar.
SIETE SECCIONES, ordenadas para responder «no se como funciona esto despues
de tantas correcciones»: (1) en una frase — un componente dice una palabra;
(2) el catalogo, los 16 nombres sonando al pulsarlos, agrupados por la FORMA
de cada entrada y no por una lista a mano; (3) el orden con los numeros de
verdad, tabla viva de familia x nombre x intent capa a capa; (4) que puede
llevar un .wav; (5) que no te toca escribir; (6) las correcciones y por que
—la historia ES la explicacion, ocultarla es lo que dejo al autor sin saber
como funciona—; (7) el tema.
EL BLOQUEANTE, PRIMERO. El interruptor Sound del topbar no silenciaba NADA:
el layout no pasaba `preferences`, asi que los canales se quedaban en 'full'
y el switch solo movia el intent de prefs (que gobierna el lado visual).
Publicar paginas que suenan con un mute que miente es peor que no
publicarlas. Ahora pasa un objeto VIVO — ambos canales leen
`preferences.{sound,haptic}` por emision, asi que mutarlo silencia al
instante sin re-registrar nada. Cierra §3.3 de la cola.
MEDIDO EN NAVEGADOR, no supuesto: 7 secciones, 17 chips, la rejilla y los
chips con su CSS aplicado, y la tabla de capas moviendose de verdad — con
`fulfill` el gain pasa de 0.05 a 0.1, el pitch de 700 a 1000 y el contour de
flat a ascending. Es decir: la pagina DEMUESTRA en vivo que el intent
sobrevive al nombre, que es la tesis del rediseño entero.
⚠️ NO he podido hacer captura: el panel del navegador no esta visible en esta
sesion. Verificado por arbol de accesibilidad, consola y estilos computados
— pero no lo he MIRADO. Queda pendiente tu ojo.
⚠️ Corregido de paso un fallo propio: la sonda del resolver usaba `document`
en el render de servidor y la pagina daba 500. Ahora cae a un stub en SSR.
El nav estrena seccion «El sistema perceptual». Quedan las otras siete
paginas, y los seis enlaces muertos que Foundations ya declaraba
(/uix/architecture, /uix/tokens, /uix/morfo, /uix/sema, /uix/eidos,
/uix/getting-started) siguen sin pagina.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
// And the channels themselves: the prefs intent drives the visual side,
|
|
|
|
|
|
// this drives whether anything is heard or felt at all.
|
|
|
|
|
|
semaPreferences.sound = soundOn ? 'full' : 'off';
|
|
|
|
|
|
semaPreferences.haptic = hapticOn ? 'full' : 'off';
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Project the display axes onto :root through a managed <style> (id-keyed, so
|
|
|
|
|
|
// it replaces in place). :root — not the docs shell — so portaled overlays,
|
|
|
|
|
|
// which render outside the shell, inherit the same density/scaling/border.
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
|
const d = DENSITY_SCALES[density];
|
|
|
|
|
|
uix.dom.writeStyle(
|
|
|
|
|
|
'uix-docs-display',
|
|
|
|
|
|
`:root{--density-space-scale:${d.space};--density-control-scale:${d.control};--scaling:${scaling};--border-width:${borderWidth}px;}`
|
|
|
|
|
|
);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
const filteredNav = $derived.by(() => {
|
|
|
|
|
|
const q = search.trim().toLowerCase();
|
|
|
|
|
|
if (!q) return nav;
|
|
|
|
|
|
return nav
|
|
|
|
|
|
.map((s) => {
|
|
|
|
|
|
if (s.kind !== 'group') return s;
|
|
|
|
|
|
const items = s.items.filter((i) => i.label.toLowerCase().includes(q));
|
|
|
|
|
|
return items.length ? { ...s, items } : null;
|
|
|
|
|
|
})
|
|
|
|
|
|
.filter((s): s is Section => s !== null);
|
|
|
|
|
|
});
|
feat: build PasswordField (full stack)
Dedicated password input component — matches the Ark UI / Chakra v3 /
Mantine / PrimeReact / Ant Design pattern of treating PasswordField as
its own first-class component (not a TextField variant). Includes one
feature that NO surveyed library ships: a CapsLock indicator driven by
KeyboardEvent.getModifierState('CapsLock').
Anatomy (5 parts, 4 events):
- Provider (root, value + visibility state)
- Input (the textbox; type flips between password / text)
- VisibilityTrigger (eye toggle button; aria-pressed + aria-controls)
- CapsLockIndicator (status live region; data-active toggles via soma
listening to keydown/keyup on Input)
- StrengthMeter (composable; consumer provides 0-4 score; UIX bundles
no zxcvbn-style algorithm — dependency-free by design)
Events:
- commit-input (every keystroke)
- commit-submit (Enter)
- shift-toggle-visibility (eye click)
- shift-caps-state (Caps Lock state flips while focused)
Key behaviors:
- `purpose: 'current' | 'new'` drives native autocomplete attribute
('current-password' for login / 'new-password' for registration).
- Field integration: disabled/readonly/required/invalid OR-merged.
- VisibilityTrigger gets tabindex=-1 so it doesn't break the form's
natural tab order — mouse / programmatic focus only, matching Ark UI.
- Caps Lock indicator is aria-live="polite" — announces on activation
while focused, silent when inactive.
- Letter-spacing increased on the input for legibility while masked.
Reference library survey performed (10 libraries) before implementation —
all consensus around dedicated component, all have built-in toggle,
ZERO have caps lock warning. Documented in the demo's API tab with a
feature comparison table.
Demo follows DEMO_AUTHORING_GUIDE canonical 6-tab depth: Live (controls
grouped by layer + code snippets at the bottom), API (props + reference
comparison), Morfo (field/value + Parts + Events tables), Sema (events
table with ▶ play), Recipe (selectors with morfo/eidos owner tags), A11y
(concern/contract table).
Also includes external work in words/ from a separate session
(unrelated to PasswordField) — not touching Words is the standing rule,
but the diff was already in the working tree.
svelte-check: 0 errors in PasswordField files (Words errors are tracked
by its own development branch).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
|
|
|
|
|
|
const currentBreakpoint = $derived(uix.dom.currentBreakpoint.current);
|
|
|
|
|
|
const compactShell = $derived(
|
|
|
|
|
|
currentBreakpoint === 'base' || currentBreakpoint === 'sm' || currentBreakpoint === 'md'
|
|
|
|
|
|
);
|
|
|
|
|
|
const phoneShell = $derived(currentBreakpoint === 'base');
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
</script>
|
|
|
|
|
|
|
feat: build PasswordField (full stack)
Dedicated password input component — matches the Ark UI / Chakra v3 /
Mantine / PrimeReact / Ant Design pattern of treating PasswordField as
its own first-class component (not a TextField variant). Includes one
feature that NO surveyed library ships: a CapsLock indicator driven by
KeyboardEvent.getModifierState('CapsLock').
Anatomy (5 parts, 4 events):
- Provider (root, value + visibility state)
- Input (the textbox; type flips between password / text)
- VisibilityTrigger (eye toggle button; aria-pressed + aria-controls)
- CapsLockIndicator (status live region; data-active toggles via soma
listening to keydown/keyup on Input)
- StrengthMeter (composable; consumer provides 0-4 score; UIX bundles
no zxcvbn-style algorithm — dependency-free by design)
Events:
- commit-input (every keystroke)
- commit-submit (Enter)
- shift-toggle-visibility (eye click)
- shift-caps-state (Caps Lock state flips while focused)
Key behaviors:
- `purpose: 'current' | 'new'` drives native autocomplete attribute
('current-password' for login / 'new-password' for registration).
- Field integration: disabled/readonly/required/invalid OR-merged.
- VisibilityTrigger gets tabindex=-1 so it doesn't break the form's
natural tab order — mouse / programmatic focus only, matching Ark UI.
- Caps Lock indicator is aria-live="polite" — announces on activation
while focused, silent when inactive.
- Letter-spacing increased on the input for legibility while masked.
Reference library survey performed (10 libraries) before implementation —
all consensus around dedicated component, all have built-in toggle,
ZERO have caps lock warning. Documented in the demo's API tab with a
feature comparison table.
Demo follows DEMO_AUTHORING_GUIDE canonical 6-tab depth: Live (controls
grouped by layer + code snippets at the bottom), API (props + reference
comparison), Morfo (field/value + Parts + Events tables), Sema (events
table with ▶ play), Recipe (selectors with morfo/eidos owner tags), A11y
(concern/contract table).
Also includes external work in words/ from a separate session
(unrelated to PasswordField) — not touching Words is the standing rule,
but the diff was already in the working tree.
svelte-check: 0 errors in PasswordField files (Words errors are tracked
by its own development branch).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
<div
|
|
|
|
|
|
data-uix-docs
|
|
|
|
|
|
data-theme={theme}
|
|
|
|
|
|
data-mode={theme}
|
|
|
|
|
|
data-uix-breakpoint={currentBreakpoint}
|
|
|
|
|
|
data-uix-compact={compactShell ? 'true' : 'false'}
|
|
|
|
|
|
data-uix-phone={phoneShell ? 'true' : 'false'}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div data-uix-shell>
|
|
|
|
|
|
<header data-uix-topbar>
|
uix: date-picker + date-range-picker components + component audit infra
Two new Eidos components shipped end-to-end (wrapper + recipe + demo +
Soma provider hardening) plus a checklist-driven audit pipeline that
scores all 67 morfo components against doctrinal completion criteria.
New components:
- date-picker: full popover-anchored picker over date-field + calendar,
with calendar/content/trigger parts and demo route.
- date-range-picker: standalone wrapper with own calendar/grid/segment
surface, demo route, and recipe CSS.
- Both wrappers follow Option C disciplined (root + parts attached via
explicit assignment, no Object.assign).
Supporting Soma changes:
- range-calendar provider tightened (211 LOC of behavior, 167 LOC of
tests), README brought up to component doctrine.
- date-field, date-picker, date-range-field, date-range-picker Soma
providers + READMEs updated for new wrappers.
- popover provider/close gain props needed by the picker wrappers.
Morfo updates:
- date-picker / date-range-picker / range-calendar morfos refined for
the new APIs (parts, events, ARIA).
Audit infrastructure (new):
- src/uix/COMPONENT_COMPLETION_CHECKLIST.md — 81 doctrinal rules across
morfo / eidos wrapper / recipe CSS / demo / README / cross-layer
scripts. Each rule keyed to active_architecture.md, GUIA_IMPLEMENTACION
and DEMO_AUTHORING_GUIDE.
- scripts/component-audit.ts + `npm run component:audit` — regex parser
over all 67 components, emits tmp/component-audit.md with summary
scoreboard + per-component findings. Validates against canonical
SEMA_FAMILIES / SEMA_VERBS / ARCHETYPE_VOCABULARY / INTENTS.
- Initial baseline: 1 PASS, 64 NEEDS-WORK, 2 BROKEN (tooltip,
date-range-picker). Top systemic gaps: translations.label (49),
README Gaps/Comparativa/Baseline sections (87 combined), keyboard
/event ratio under-declaration (15), apg URL absent (19).
Misc:
- src/uix/kimi-audit-eidos.md — supplementary audit notes.
- .gitignore: ignore .codex-* agent scratch artifacts at repo root.
- continue.md + READMEs updated through the migration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
<a
|
|
|
|
|
|
href="/uix"
|
|
|
|
|
|
data-uix-brand
|
|
|
|
|
|
data-sveltekit-preload-code="hover"
|
|
|
|
|
|
data-sveltekit-preload-data="hover"
|
|
|
|
|
|
>
|
|
|
|
|
|
<span data-uix-brand-mark>U</span>
|
|
|
|
|
|
<span>UIX</span>
|
|
|
|
|
|
<span data-uix-brand-version>v0.1</span>
|
|
|
|
|
|
</a>
|
|
|
|
|
|
|
|
|
|
|
|
<nav data-uix-topbar-nav aria-label="Primary">
|
|
|
|
|
|
<a
|
|
|
|
|
|
href="/uix"
|
|
|
|
|
|
data-sveltekit-preload-code="hover"
|
|
|
|
|
|
data-sveltekit-preload-data="hover"
|
|
|
|
|
|
data-active={current === '/uix' ||
|
|
|
|
|
|
current.startsWith('/uix/architecture') ||
|
|
|
|
|
|
current.startsWith('/uix/getting-started')
|
|
|
|
|
|
? 'true'
|
|
|
|
|
|
: 'false'}>Docs</a
|
|
|
|
|
|
>
|
|
|
|
|
|
<a
|
|
|
|
|
|
href="/uix/components"
|
|
|
|
|
|
data-sveltekit-preload-code="hover"
|
|
|
|
|
|
data-sveltekit-preload-data="hover"
|
|
|
|
|
|
data-active={current.startsWith('/uix/components') ? 'true' : 'false'}>Components</a
|
|
|
|
|
|
>
|
|
|
|
|
|
<a href="https://github.com/" target="_blank" rel="noreferrer">GitHub ↗</a>
|
|
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
|
|
|
|
<div data-uix-topbar-tools>
|
|
|
|
|
|
<!-- DIR: simple toggle. RTL flips the entire document direction
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
so all eidos components (which use logical properties)
|
|
|
|
|
|
reverse correctly. -->
|
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado
El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos
independientes, y el segundo era del ecosistema entero.
1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform
FISICO en las tres reglas verticales (inset-inline-start: 50% +
translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y
el relleno quedaban fuera de eje un ancho entero. Medido en Chrome:
raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba
porque ya usaba margin-inline-start — ese era el idioma correcto del
propio fichero y es el que aplico. Mismo defecto en accordion.css:
text-align: left en el trigger, que era el UNICO text-align fisico de
todo eidos; ahora quedan cero.
2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la
celda $state, asi que prefs.<dim>.get() — y con ella el
soma.prefs.getDir() que la guia manda usar — era una lectura sin
tracking. Los ~64 componentes que resuelven su direccion desde prefs la
congelaban al montarse. La proyeccion DOM se salvaba porque usa
onChange: misma dimension, dos caminos de lectura, solo uno reactivo.
Arreglado con un contador de cambios POR dimension, bombeado desde el
effectiveDiff que el motor ya calcula, de modo que cubre tambien el
cambio por DERIVACION (direction siguiendo a language) y no solo el
intent. Un contador por clave y no una celda global: con una sola celda,
tocar motion despertaria las 64 derivaciones de direccion. El test
negativo lo clava.
3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz
con un valor siempre concreto, asi que una app que ponga html dir=rtl sin
registrar la preferencia se encontraba 33 islas volteadas del reves.
Investigadas las cinco librerias de referencia: MUI y react-aria no
estampan nunca, Radix estampa siempre (sus mantenedores arrastran
discussions/1405 por esto), y Zag estampa prop("dir") — presente si
alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme:
el atributo NUNCA tiene defecto. La herencia la hace el navegador por
AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena
prop -> prefs -> 'ltr' se queda intacta.
4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los
wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico
—heredar del menu padre— enterrado en dos expresiones sueltas.
Normalizado a activeDir(getter, soma) en los 36, un solo tipo
Direction | undefined, y el escalon de los submenus declarado en la
llamada, donde se ve.
5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace
clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe
en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era
disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar
el control — la cadena documentada funcionando de punta a punta por
primera vez.
Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en
101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en
vivo, y el arabe voltea por derivacion.
QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del
accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es
un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL);
html lang no sigue al idioma; smoke y perm:check sin ejecutar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
<!-- Instrumented for `perm:check`: this is the ONLY control in the
|
|
|
|
|
|
shell that commits a preference, so without it the permutation
|
|
|
|
|
|
runner exercises component state transitions but never a prefs
|
|
|
|
|
|
commit — and the reactive path from `uix.prefs` into the ~64
|
|
|
|
|
|
components that resolve `dir` from it stays untested.
|
|
|
|
|
|
Index 90 so it runs AFTER each demo's own steps (0..N).
|
|
|
|
|
|
Two consequences a future reader should know: the runner shares
|
|
|
|
|
|
one browser context across routes, so this flip persists via
|
|
|
|
|
|
localStorage and routes alternate ltr/rtl through a run; and
|
|
|
|
|
|
because the scan is page-wide, EVERY route under this layout now
|
|
|
|
|
|
carries a step, so the runner no longer skips uninstrumented
|
|
|
|
|
|
demos. Both are deliberate — morfo contracts are direction
|
|
|
|
|
|
agnostic, and a prefs commit on every route is the coverage
|
|
|
|
|
|
this instrumentation exists for. -->
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
data-uix-icon-btn
|
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado
El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos
independientes, y el segundo era del ecosistema entero.
1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform
FISICO en las tres reglas verticales (inset-inline-start: 50% +
translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y
el relleno quedaban fuera de eje un ancho entero. Medido en Chrome:
raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba
porque ya usaba margin-inline-start — ese era el idioma correcto del
propio fichero y es el que aplico. Mismo defecto en accordion.css:
text-align: left en el trigger, que era el UNICO text-align fisico de
todo eidos; ahora quedan cero.
2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la
celda $state, asi que prefs.<dim>.get() — y con ella el
soma.prefs.getDir() que la guia manda usar — era una lectura sin
tracking. Los ~64 componentes que resuelven su direccion desde prefs la
congelaban al montarse. La proyeccion DOM se salvaba porque usa
onChange: misma dimension, dos caminos de lectura, solo uno reactivo.
Arreglado con un contador de cambios POR dimension, bombeado desde el
effectiveDiff que el motor ya calcula, de modo que cubre tambien el
cambio por DERIVACION (direction siguiendo a language) y no solo el
intent. Un contador por clave y no una celda global: con una sola celda,
tocar motion despertaria las 64 derivaciones de direccion. El test
negativo lo clava.
3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz
con un valor siempre concreto, asi que una app que ponga html dir=rtl sin
registrar la preferencia se encontraba 33 islas volteadas del reves.
Investigadas las cinco librerias de referencia: MUI y react-aria no
estampan nunca, Radix estampa siempre (sus mantenedores arrastran
discussions/1405 por esto), y Zag estampa prop("dir") — presente si
alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme:
el atributo NUNCA tiene defecto. La herencia la hace el navegador por
AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena
prop -> prefs -> 'ltr' se queda intacta.
4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los
wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico
—heredar del menu padre— enterrado en dos expresiones sueltas.
Normalizado a activeDir(getter, soma) en los 36, un solo tipo
Direction | undefined, y el escalon de los submenus declarado en la
llamada, donde se ve.
5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace
clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe
en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era
disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar
el control — la cadena documentada funcionando de punta a punta por
primera vez.
Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en
101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en
vivo, y el arabe voltea por derivacion.
QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del
accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es
un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL);
html lang no sigue al idioma; smoke y perm:check sin ejecutar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
data-perm-step="90"
|
|
|
|
|
|
data-perm-label="prefs: flip direction"
|
|
|
|
|
|
data-perm-settle="600"
|
|
|
|
|
|
title="Direction: {dirMode}{dirMode === 'auto'
|
|
|
|
|
|
? ` (from language → ${effectiveDir})`
|
|
|
|
|
|
: ''} — click to cycle auto → ltr → rtl"
|
|
|
|
|
|
aria-label="Toggle direction"
|
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado
El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos
independientes, y el segundo era del ecosistema entero.
1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform
FISICO en las tres reglas verticales (inset-inline-start: 50% +
translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y
el relleno quedaban fuera de eje un ancho entero. Medido en Chrome:
raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba
porque ya usaba margin-inline-start — ese era el idioma correcto del
propio fichero y es el que aplico. Mismo defecto en accordion.css:
text-align: left en el trigger, que era el UNICO text-align fisico de
todo eidos; ahora quedan cero.
2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la
celda $state, asi que prefs.<dim>.get() — y con ella el
soma.prefs.getDir() que la guia manda usar — era una lectura sin
tracking. Los ~64 componentes que resuelven su direccion desde prefs la
congelaban al montarse. La proyeccion DOM se salvaba porque usa
onChange: misma dimension, dos caminos de lectura, solo uno reactivo.
Arreglado con un contador de cambios POR dimension, bombeado desde el
effectiveDiff que el motor ya calcula, de modo que cubre tambien el
cambio por DERIVACION (direction siguiendo a language) y no solo el
intent. Un contador por clave y no una celda global: con una sola celda,
tocar motion despertaria las 64 derivaciones de direccion. El test
negativo lo clava.
3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz
con un valor siempre concreto, asi que una app que ponga html dir=rtl sin
registrar la preferencia se encontraba 33 islas volteadas del reves.
Investigadas las cinco librerias de referencia: MUI y react-aria no
estampan nunca, Radix estampa siempre (sus mantenedores arrastran
discussions/1405 por esto), y Zag estampa prop("dir") — presente si
alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme:
el atributo NUNCA tiene defecto. La herencia la hace el navegador por
AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena
prop -> prefs -> 'ltr' se queda intacta.
4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los
wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico
—heredar del menu padre— enterrado en dos expresiones sueltas.
Normalizado a activeDir(getter, soma) en los 36, un solo tipo
Direction | undefined, y el escalon de los submenus declarado en la
llamada, donde se ve.
5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace
clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe
en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era
disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar
el control — la cadena documentada funcionando de punta a punta por
primera vez.
Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en
101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en
vivo, y el arabe voltea por derivacion.
QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del
accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es
un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL);
html lang no sigue al idioma; smoke y perm:check sin ejecutar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
onclick={() =>
|
|
|
|
|
|
(dirMode = dirMode === 'auto' ? 'ltr' : dirMode === 'ltr' ? 'rtl' : 'auto')}
|
|
|
|
|
|
>
|
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado
El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos
independientes, y el segundo era del ecosistema entero.
1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform
FISICO en las tres reglas verticales (inset-inline-start: 50% +
translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y
el relleno quedaban fuera de eje un ancho entero. Medido en Chrome:
raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba
porque ya usaba margin-inline-start — ese era el idioma correcto del
propio fichero y es el que aplico. Mismo defecto en accordion.css:
text-align: left en el trigger, que era el UNICO text-align fisico de
todo eidos; ahora quedan cero.
2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la
celda $state, asi que prefs.<dim>.get() — y con ella el
soma.prefs.getDir() que la guia manda usar — era una lectura sin
tracking. Los ~64 componentes que resuelven su direccion desde prefs la
congelaban al montarse. La proyeccion DOM se salvaba porque usa
onChange: misma dimension, dos caminos de lectura, solo uno reactivo.
Arreglado con un contador de cambios POR dimension, bombeado desde el
effectiveDiff que el motor ya calcula, de modo que cubre tambien el
cambio por DERIVACION (direction siguiendo a language) y no solo el
intent. Un contador por clave y no una celda global: con una sola celda,
tocar motion despertaria las 64 derivaciones de direccion. El test
negativo lo clava.
3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz
con un valor siempre concreto, asi que una app que ponga html dir=rtl sin
registrar la preferencia se encontraba 33 islas volteadas del reves.
Investigadas las cinco librerias de referencia: MUI y react-aria no
estampan nunca, Radix estampa siempre (sus mantenedores arrastran
discussions/1405 por esto), y Zag estampa prop("dir") — presente si
alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme:
el atributo NUNCA tiene defecto. La herencia la hace el navegador por
AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena
prop -> prefs -> 'ltr' se queda intacta.
4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los
wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico
—heredar del menu padre— enterrado en dos expresiones sueltas.
Normalizado a activeDir(getter, soma) en los 36, un solo tipo
Direction | undefined, y el escalon de los submenus declarado en la
llamada, donde se ve.
5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace
clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe
en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era
disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar
el control — la cadena documentada funcionando de punta a punta por
primera vez.
Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en
101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en
vivo, y el arabe voltea por derivacion.
QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del
accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es
un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL);
html lang no sigue al idioma; smoke y perm:check sin ejecutar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
{#if effectiveDir === 'ltr'}
|
|
|
|
|
|
<svg
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path d="M5 12h14M13 6l6 6-6 6" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
{:else}
|
|
|
|
|
|
<svg
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path d="M19 12H5M11 6l-6 6 6 6" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
{/if}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- LANG: short uppercase code + popover with full language list -->
|
|
|
|
|
|
<Popover>
|
|
|
|
|
|
<Popover.Trigger>
|
|
|
|
|
|
<span data-uix-label-btn>
|
|
|
|
|
|
<svg
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
>
|
|
|
|
|
|
<circle cx="12" cy="12" r="10" />
|
|
|
|
|
|
<path d="M2 12h20M12 2a15 15 0 0 1 0 20M12 2a15 15 0 0 0 0 20" />
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
</svg>
|
|
|
|
|
|
{language.toUpperCase()}
|
|
|
|
|
|
<svg
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
data-caret
|
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path d="m6 9 6 6 6-6" />
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
</svg>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</Popover.Trigger>
|
|
|
|
|
|
<Popover.Content side="bottom" align="end" sideOffset={6}>
|
|
|
|
|
|
<div data-uix-menu>
|
|
|
|
|
|
<div data-uix-menu-section>
|
|
|
|
|
|
<div data-uix-menu-label>Language</div>
|
|
|
|
|
|
{#each Object.entries(LANGUAGE_LABELS) as [code, label]}
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
data-uix-menu-item
|
|
|
|
|
|
data-checked={language === code ? 'true' : 'false'}
|
|
|
|
|
|
onclick={() => (language = code as Language)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<span>{label}</span>
|
|
|
|
|
|
<span data-uix-menu-item-meta>{code.toUpperCase()}</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
{/each}
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Popover.Content>
|
|
|
|
|
|
</Popover>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- FORMATS: currency + units + number formatting in one popover -->
|
|
|
|
|
|
<Popover>
|
|
|
|
|
|
<Popover.Trigger>
|
|
|
|
|
|
<span data-uix-label-btn>
|
|
|
|
|
|
<svg
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path d="M12 1v22M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
Formats
|
|
|
|
|
|
<svg
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
data-caret
|
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path d="m6 9 6 6 6-6" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</Popover.Trigger>
|
|
|
|
|
|
<Popover.Content side="bottom" align="end" sideOffset={6}>
|
|
|
|
|
|
<div data-uix-menu style="min-inline-size: 240px;">
|
|
|
|
|
|
<div data-uix-menu-section>
|
|
|
|
|
|
<div data-uix-menu-label>Currency</div>
|
|
|
|
|
|
{#each Object.entries(CURRENCY_LABELS) as [code, label]}
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
data-uix-menu-item
|
|
|
|
|
|
data-checked={currency === code ? 'true' : 'false'}
|
|
|
|
|
|
onclick={() => (currency = code as Currency)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<span>{label}</span>
|
|
|
|
|
|
<span data-uix-menu-item-meta>{code}</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
{/each}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div data-uix-menu-section>
|
|
|
|
|
|
<div data-uix-menu-label>Units</div>
|
|
|
|
|
|
{#each Object.entries(UNIT_LABELS) as [code, label]}
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
data-uix-menu-item
|
|
|
|
|
|
data-checked={units === code ? 'true' : 'false'}
|
|
|
|
|
|
onclick={() => (units = code as UnitSystem)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<span>{label}</span>
|
|
|
|
|
|
<span data-uix-menu-item-meta
|
|
|
|
|
|
>{code === 'metric' ? 'KM · KG · °C' : 'MI · LB · °F'}</span
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
{/each}
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
</div>
|
|
|
|
|
|
<div data-uix-menu-section>
|
|
|
|
|
|
<div data-uix-menu-label>Numbers</div>
|
|
|
|
|
|
{#each Object.entries(NUMBER_LABELS) as [code, label]}
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
data-uix-menu-item
|
|
|
|
|
|
data-checked={numberFormat === code ? 'true' : 'false'}
|
|
|
|
|
|
onclick={() => (numberFormat = code as NumberFormat)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<span>{label}</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
{/each}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Popover.Content>
|
|
|
|
|
|
</Popover>
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
|
|
|
|
|
|
<!-- DISPLAY: density / scaling / border width — the interrelated
|
|
|
|
|
|
theming axes, applied to :root so every demo (including
|
|
|
|
|
|
portaled overlays) reflects the change live. -->
|
|
|
|
|
|
<Popover>
|
|
|
|
|
|
<Popover.Trigger>
|
|
|
|
|
|
<span data-uix-label-btn>
|
|
|
|
|
|
<svg
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
>
|
|
|
|
|
|
<line x1="4" y1="7" x2="20" y2="7" />
|
|
|
|
|
|
<circle cx="9" cy="7" r="2.5" />
|
|
|
|
|
|
<line x1="4" y1="17" x2="20" y2="17" />
|
|
|
|
|
|
<circle cx="15" cy="17" r="2.5" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
Display
|
|
|
|
|
|
<svg
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
data-caret
|
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path d="m6 9 6 6 6-6" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</Popover.Trigger>
|
|
|
|
|
|
<Popover.Content side="bottom" align="end" sideOffset={6}>
|
|
|
|
|
|
<div data-uix-menu style="min-inline-size: 252px;">
|
|
|
|
|
|
<div data-uix-menu-section>
|
|
|
|
|
|
<div data-uix-menu-label>Density</div>
|
|
|
|
|
|
{#each DENSITIES as d}
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
data-uix-menu-item
|
|
|
|
|
|
data-checked={density === d ? 'true' : 'false'}
|
|
|
|
|
|
onclick={() => (density = d)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<span style="text-transform: capitalize;">{d}</span>
|
|
|
|
|
|
<span data-uix-menu-item-meta
|
|
|
|
|
|
>{DENSITY_SCALES[d].space}× · {DENSITY_SCALES[d].control}×</span
|
|
|
|
|
|
>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
{/each}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div data-uix-menu-section>
|
|
|
|
|
|
<div data-uix-menu-label>Scaling · {scaling.toFixed(2)}×</div>
|
|
|
|
|
|
<input
|
|
|
|
|
|
data-uix-range
|
|
|
|
|
|
type="range"
|
|
|
|
|
|
min="0.85"
|
|
|
|
|
|
max="1.25"
|
|
|
|
|
|
step="0.05"
|
|
|
|
|
|
bind:value={scaling}
|
|
|
|
|
|
aria-label="Scaling factor"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div data-uix-menu-section>
|
|
|
|
|
|
<div data-uix-menu-label>Border width · {borderWidth}px</div>
|
|
|
|
|
|
<input
|
|
|
|
|
|
data-uix-range
|
|
|
|
|
|
type="range"
|
|
|
|
|
|
min="0"
|
|
|
|
|
|
max="4"
|
|
|
|
|
|
step="0.5"
|
|
|
|
|
|
bind:value={borderWidth}
|
|
|
|
|
|
aria-label="Border width"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div data-uix-menu-section>
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
data-uix-menu-item
|
|
|
|
|
|
onclick={() => {
|
|
|
|
|
|
density = 'comfortable';
|
|
|
|
|
|
scaling = 1;
|
|
|
|
|
|
borderWidth = 1;
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<span>Reset</span>
|
|
|
|
|
|
<span data-uix-menu-item-meta>comfortable · 1× · 1px</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Popover.Content>
|
|
|
|
|
|
</Popover>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- SEMANTICS: sound / motion / haptic toggles. These will hook
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
into the sema engine channels' enabled state once the
|
|
|
|
|
|
channels expose runtime setEnabled APIs; today they
|
|
|
|
|
|
persist preference + reflect in the UI. -->
|
|
|
|
|
|
<Popover>
|
|
|
|
|
|
<Popover.Trigger>
|
|
|
|
|
|
<span data-uix-label-btn>
|
|
|
|
|
|
<svg
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path
|
|
|
|
|
|
d="M12 2a4 4 0 0 1 4 4v6a4 4 0 0 1-8 0V6a4 4 0 0 1 4-4zM19 10v2a7 7 0 0 1-14 0v-2M12 19v3"
|
|
|
|
|
|
/>
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
</svg>
|
|
|
|
|
|
Semantics
|
|
|
|
|
|
<svg
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
data-caret
|
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path d="m6 9 6 6 6-6" />
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
</svg>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</Popover.Trigger>
|
|
|
|
|
|
<Popover.Content side="bottom" align="end" sideOffset={6}>
|
|
|
|
|
|
<div data-uix-menu style="min-inline-size: 240px;">
|
|
|
|
|
|
<div data-uix-menu-section>
|
|
|
|
|
|
<div data-uix-menu-label>Perceptual channels</div>
|
|
|
|
|
|
<label data-uix-menu-toggle>
|
|
|
|
|
|
<input type="checkbox" bind:checked={soundOn} />
|
|
|
|
|
|
Sound
|
|
|
|
|
|
<span data-uix-menu-toggle-meta>{soundOn ? 'on' : 'off'}</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<label data-uix-menu-toggle>
|
|
|
|
|
|
<input type="checkbox" bind:checked={motionOn} />
|
|
|
|
|
|
Motion
|
|
|
|
|
|
<span data-uix-menu-toggle-meta>{motionOn ? 'on' : 'off'}</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<label data-uix-menu-toggle>
|
|
|
|
|
|
<input type="checkbox" bind:checked={hapticOn} />
|
|
|
|
|
|
Haptic
|
|
|
|
|
|
<span data-uix-menu-toggle-meta>{hapticOn ? 'on' : 'off'}</span>
|
|
|
|
|
|
</label>
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Popover.Content>
|
|
|
|
|
|
</Popover>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- THEME -->
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
data-uix-icon-btn
|
|
|
|
|
|
title={theme === 'light' ? 'Switch to dark theme' : 'Switch to light theme'}
|
|
|
|
|
|
aria-label="Toggle theme"
|
|
|
|
|
|
onclick={() => (theme = theme === 'light' ? 'dark' : 'light')}
|
|
|
|
|
|
>
|
|
|
|
|
|
{#if theme === 'light'}
|
|
|
|
|
|
<svg
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
{:else}
|
|
|
|
|
|
<svg
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
>
|
|
|
|
|
|
<circle cx="12" cy="12" r="4" />
|
|
|
|
|
|
<path
|
|
|
|
|
|
d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</svg>
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{/if}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
|
|
<aside data-uix-rail>
|
|
|
|
|
|
<div data-uix-rail-search>
|
|
|
|
|
|
<input type="search" placeholder="Search…" bind:value={search} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{#each filteredNav as section}
|
|
|
|
|
|
{#if section.kind === 'group'}
|
|
|
|
|
|
<div data-uix-rail-group>
|
|
|
|
|
|
<div data-uix-rail-group-head>
|
|
|
|
|
|
<span>{section.heading}</span>
|
|
|
|
|
|
<span data-uix-rail-group-count>{section.items.length}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<ul>
|
|
|
|
|
|
{#each section.items as item}
|
|
|
|
|
|
<li>
|
|
|
|
|
|
<a
|
|
|
|
|
|
href={item.slug}
|
|
|
|
|
|
data-uix-rail-link
|
|
|
|
|
|
data-sveltekit-preload-code="hover"
|
|
|
|
|
|
data-sveltekit-preload-data="hover"
|
|
|
|
|
|
data-active={current === item.slug ? 'true' : 'false'}
|
|
|
|
|
|
>
|
|
|
|
|
|
{item.label}
|
|
|
|
|
|
{#if item.tag}
|
|
|
|
|
|
<span data-uix-rail-link-tag>{item.tag}</span>
|
|
|
|
|
|
{/if}
|
|
|
|
|
|
</a>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
{/each}
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
</div>
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
{/if}
|
|
|
|
|
|
{/each}
|
|
|
|
|
|
|
|
|
|
|
|
{#if filteredNav.length === 0}
|
|
|
|
|
|
<div data-uix-rail-group-head>No matches for "{search}"</div>
|
|
|
|
|
|
{/if}
|
|
|
|
|
|
</aside>
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
|
feat(direction): un guard para el eje logico-fisico, y el <main> que congelaba las 51 demos
Cierra la §2.1 del handoff. El guard nacio en rojo con 24 hallazgos y al
arreglarlos aparecieron dos defectos mas grandes que el que buscaba.
1) EL GUARD RTL-1. Nada cruzaba un ancla LOGICA con un desplazamiento FISICO
en la misma regla CSS: eidos-lint clasifica SELECTORES, no declaraciones, y la
fila RTL del contrato de construccion decia "rule (LIVE)" — revision humana.
Por eso el defecto del slider llego a produccion y lo cazo el ojo del usuario.
Ahora `npm run rtl:check`, con la logica en src/uix/eidos/rtl-lint.ts (14
tests) para que sea testeable, calcando el par lint.ts / scripts/eidos-lint.ts.
El test ancla el caso historico: slider.css ANTES de 013ceac57 sale rojo y el
arreglo que se commiteo sale verde.
Dos desviaciones del enunciado, deliberadas. AMPLIA a la propiedad
independiente `translate:`, que se usa MAS que `transform: translate*` (87
frente a 36) y tiene el mismo defecto — ocho de los 24 hallazgos eran de esa
forma. RECORTA `padding-inline*`: no mueve la caja contra su ancla, asi que no
puede pelear con un translate; solo anadia ruido.
2) LA COSECHA, 24 -> 1. El unico que queda es palabras, auditado y no tocado.
Los 23 caen en tres familias: CENTRADO (10) donde el resultado deseado es
simetrico y basta dejar de descentrar; DIRECCIONAL (6) donde el signo debe
voltear; y FISICO (7) donde lo que sobraba era el ancla logica — handles de
brujula del cropper sobre una imagen que no se voltea, y el arco polar del
menu-dial. Medido en Chrome el switch: el thumb salia a +24px en un carril de
38px, colgando fuera. Verificados tambien carousel, avatar, cropper, menu-dial
y chat-log.
3) :dir() ES LA DOCTRINA, [dir='rtl'] QUEDA PROHIBIDO. Los 12 selectores que
habia en eidos estaban TODOS rotos, en los dos sentidos opuestos del mismo
error, y ambos medidos en Chrome. Los 5 descendientes se aplican DE MAS: el
sidebar tenia direction:ltr de verdad y aun asi matcheaba, porque el selector
capta cualquier ancestro RTL e ignora un dir mas cercano que redeclare. Los 7
de mismo-elemento NO se aplican NUNCA: el tree-view era rtl de verdad y no
matcheaba, porque desde 013ceac57 el atributo solo se estampa si alguien lo
afirmo. :dir() acierta los tres casos porque mira la direccion RESUELTA.
Baseline desde 2023.
4) EL PIN DEL <main>. 5469e05df hizo dos cosas que se anulan: puso las demos en
`auto` —sin prop dir, o sea el componente no estampa atributo y HEREDA del
DOM— y a la vez clavo <main data-uix-canvas dir="ltr"> sobre ellas. Ese <main>
era la herencia, asi que el toggle del topbar y el arabe llegaban a <html> y
morian ahi: las 51 demos congeladas en LTR. Su comentario afirmaba que "los
componentes siguen las prefs, este contenedor no les afecta" — cierto para la
LOGICA, falso para el CSS, que lee el direction COMPUTADO. Medido: html
dir="rtl" con el panel del sidebar todavia a la izquierda, y quitar ese
atributo y nada mas lo movia. Se queda el lang="en". Coste aceptado por el
usuario: vuelve la puntuacion desplazada en la prosa inglesa al elegir RTL, que
es cosmetico y solo en un modo de inspeccion; demos que no pueden demostrar,
no. Si hay que fijar la prosa otra vez, el pin va alrededor de la PROSA.
Verificado: check 74 = linea base exacta en las tres pasadas. rtl-lint 14/14.
Suite de eidos 375 pasan; el fallo de lint.test.ts por audio-player es
PREEXISTENTE, comprobado con stash, y es del hilo del sonido. Los 9 CSS tocados
ya fallaban prettier antes de tocarlos, asi que no se formatean.
SIN VERIFICAR EN NAVEGADOR, anotado en el handoff: archetypes y proof-of-human
(sus reglas viven en @media (pointer: coarse), invisible en escritorio) y
sliding-indicator (la demo del radio-group segmented no llegaba a medir).
QUEDA: hay wrappers estampando dir="" vacio en vez de omitir el atributo —
visto en sidebar y tree-view; no rompe porque un valor invalido hereda, pero
contradice la letra del contrato.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
<!-- The docs copy is written in English, so it is DECLARED as English via
|
|
|
|
|
|
`lang` — but NOT via `dir`. A `dir="ltr"` here used to pin the prose
|
|
|
|
|
|
against the bidi artefact ("What is morfo?" rendering as "?What is
|
|
|
|
|
|
morfo"), and it worked, but it pinned the DEMOS too: a component in
|
|
|
|
|
|
`auto` mode passes no `dir` prop, so it stamps no attribute and
|
|
|
|
|
|
resolves its direction by INHERITANCE from the DOM. This container
|
|
|
|
|
|
was that inheritance, so the shell's RTL switch — and picking
|
|
|
|
|
|
Arabic — reached `<html>` and stopped here, leaving all 51 demos
|
|
|
|
|
|
frozen in LTR. Measured on the sidebar: `html dir="rtl"` with the
|
|
|
|
|
|
panel still on the left, and dropping this attribute alone moved it.
|
|
|
|
|
|
The prose artefact is cosmetic and only shows in RTL, which is an
|
|
|
|
|
|
inspection mode; demos that cannot demonstrate are not. If the
|
|
|
|
|
|
prose needs pinning again, it has to happen AROUND THE PROSE, not
|
|
|
|
|
|
around the canvas that also holds the live examples. -->
|
|
|
|
|
|
<main data-uix-canvas lang="en">
|
|
|
|
|
|
{@render children?.()}
|
|
|
|
|
|
</main>
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|