You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/web/routes/uix/+layout@.svelte

1069 lines
37 KiB

<script lang="ts">
import { onDestroy } from 'svelte';
import { createActiveUix, setActiveUix } from '$active-uix';
import { createActivePrefsDomProjection, standardPrefsDimensions } from '$prefs';
import { page } from '$app/state';
import { Soma } from '$soma/core/soma.svelte';
fix(password-field): visual issues — double border, broken layout, stale lang refs Three visual bugs spotted on /uix/components/password-field: 1. **Double-bordered inputs.** The demo wrapped `<PasswordField>` inside `<Field.Control>`, which adds its own border + padding. PasswordField IS the field's input shell — it ships its own chrome. Removed the five offending Field.Control wrappers from login/registration/change forms; PasswordField now sits directly inside `<Field>`. 2. **Icon wrapping to a new row + StrengthMeter sharing the bordered box.** Replaced `display: inline-flex; flex-wrap: wrap` on the root with `display: grid; grid-template-columns: minmax(0, 1fr) auto auto` so Input + CapsLockIndicator + VisibilityTrigger stay on the first row. When StrengthMeter is composed, `:has()` switches the root to a 2-row grid where the bordered chrome lifts onto a `::before` pseudo-element that only spans the first row, and the meter sits below as a sibling. State chrome (`data-focused`, `data-invalid`) propagates to the pseudo via the same `:has()` selectors. 3. **CapsLock pill claiming space even when inactive.** Was `color: transparent; background: transparent` which still consumed width in the grid. Now `display: none` by default and `display: inline-flex` only when `[data-active]` is set. Also fixed: `#?sium.errors.min_length|…` and friends rendered literal in the demo because the docs shell didn't register `$sium/langs/langs`. Registered `siumLangs` under the `sium` namespace in the layout's `createActiveUix({ langs: { schema: { sium: siumLangs } } })`. svelte-check: 0 errors. Page now loads at 200. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
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';
feat(text-gradient): `colors` discriminates canonical name vs stop list (folds `preset`) Per request: one `colors` prop that discriminates by shape instead of two rival props. A `string[]` is an explicit stop list (unchanged); a bare `string` names a canonical gradient from the theming's named set, resolved as `var(--gradient-{name})` — exactly what the removed `preset` prop did. The set is an open cage (`EidosConfig.gradients` / `applyGradients`), so the type stays `string | readonly string[]`, not a closed union. Removed `preset` outright (no back-compat shim, per project rule); the canonical S9 vocabulary and raw stops are now the SAME input. Docs shell: register a small canonical named set on `uix/+layout@` via `applyGradients` (`brand`, `spectrum`) — role-derived, so they re-tint with the seed and flip light/dark. Demo gains a live stops|named toggle so both branches are a real testbed. Finding worth recording (types + README): a named gradient painted through `background-clip: text` must be a single `<image>` (linear/radial/conic). A role-derived MESH serializes to radials + a base COLOR, and a bare color is not a valid `background-image` (only the `background` shorthand accepts it) — so a mesh name computes to `none` and never paints. The docs set is linear-only for this reason. Verified in real Chrome: stops branch paints the token gradient; `colors="spectrum"` paints `var(--gradient-spectrum)` through the text. svelte-check 0 own errors, component:audit 141/0/0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
import type { Gradient } from '$libs/gradient';
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';
feat(gradient-picker): rebuild on the generic Picker core + presets panel + result-commit sema Rebuilds the GradientPicker on the generic Picker.Provider<Gradient> transaction core (open / commit / cancel / clear / workingValue + a composed Popover), like the natural-time-picker — the bespoke coordinator is now a thin domain provider (morfo runtime + working-value CSS + the presets state). - deferValue prop (live vs deferred draft-until-Listo). The trigger chip reflects the COMMITTED value, so a deferred picker's chip stays put while you edit the working draft. - Clear restores the INITIAL bound value the picker was given, not a hardcoded default gradient. - No tabs: the editor IS the popover body; the presets grid opens in a panel from a Layers button next to "Añadir degradado" (the Add <-> Remove toggle) — ColorPicker-style "select a saved preset -> Remove", and it reclaims the tab-header height. - Add / Remove presets fire commit.fulfill / commit.loss firmas (new gradient-picker sema pack, registered in the demo). Per the Button doctrine the press is a neutral contact.activate and the consumer emits the RESULT commit; the events target the REGISTERED `provider` part (targeting the unregistered `content` part was also why the Clear commit-reset was silent). - Stop color editor hides the remove-stop button for the endpoint stops (first / last by position — they anchor 0% / 100%). - maxPresets cap (default 10). StopList in a ScrollArea (autosize, slim scrollbar with a gap so the rows never sit under it). Composes the existing components AS-IS (Popover / Button / ScrollArea / the GradientBuilder parts) without overriding their styles. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
import { gradientBuilderSema } from '$uix/sema/components/gradient-builder';
import { gradientPickerSema } from '$uix/sema/components/gradient-picker';
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';
import { navTreeSema } from '$uix/sema/components/nav-tree';
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';
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';
fix(splitter): register sema scope + cascade so handle-pick/drag actually sound Two missing registrations meant the splitter sema cascade from the previous commit never reached the engine — handle-pick and handle-drag were silent, only commit-set sounded (because the `commit` family has a sound payload baked into its SEMA_MAP base, while `handle` only has haptic and depends on the cascade for sound). Fixes: 1. morfo scope: ['soma'] → ['soma', 'sema', 'eidos']. Without 'sema' the per-component cascade rules in src/uix/sema/components/splitter.ts are never honored. 2. web/routes/uix/+layout@.svelte: add splitterSema to the `components: [...]` array passed to defineEngineSemantic. The sema engine only loads cascade packs it's explicitly given, so forgetting this line silently drops all splitter cascade rules. Verified in browser at /uix/components/splitter via Live tab (real pointer interaction, AudioContext.createOscillator counter): handle-pick → 2 oscillators (handle.pickup.air, gain 0.10) handle-drag → 16 oscillators across 8 throttled emits — the dynamic resolveSplitterDragSound curve is playing per-frame as expected commit-set → 2 oscillators (handle.snap.chime, gain 0.14) Note: the Sema tab's static "▶ play" button still shows 0 oscillators for handle-drag — same as Slider's demo. By design: that button calls engine.emit() directly without the per-emit `signal.overrides` that soma injects during real drag. The dynamic curve IS the payload. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
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';
import { timelineSema } from '$uix/sema/components/timeline';
import { toastSema } from '$uix/sema/components/toast';
import { toolbarSema } from '$uix/sema/components/toolbar';
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';
import { cropperSema } from '$uix/sema/components/cropper';
import { cssFieldSema } from '$uix/sema/components/css-field';
import { dateFieldSema } from '$uix/sema/components/date-field';
import { Popover } from '$uix/eidos/components/popover';
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';
import './uix.css';
let { children } = $props();
type Language = 'es' | 'en' | 'fr' | 'de';
type Currency = 'EUR' | 'USD' | 'GBP' | 'JPY';
type UnitSystem = 'metric' | 'imperial';
type NumberFormat = 'es-ES' | 'en-US' | 'en-GB' | 'de-DE';
const docsPrefsSchema = standardPrefsDimensions({
languages: ['es', 'en', 'fr', 'de'] 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'
}
});
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: {
sound: true,
haptic: true,
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,
cropperSema,
cssFieldSema,
dateFieldSema,
dialogSema,
drawerSema,
editableSema,
fileUploadSema,
floatPanelSema,
formSema,
feat(gradient-picker): rebuild on the generic Picker core + presets panel + result-commit sema Rebuilds the GradientPicker on the generic Picker.Provider<Gradient> transaction core (open / commit / cancel / clear / workingValue + a composed Popover), like the natural-time-picker — the bespoke coordinator is now a thin domain provider (morfo runtime + working-value CSS + the presets state). - deferValue prop (live vs deferred draft-until-Listo). The trigger chip reflects the COMMITTED value, so a deferred picker's chip stays put while you edit the working draft. - Clear restores the INITIAL bound value the picker was given, not a hardcoded default gradient. - No tabs: the editor IS the popover body; the presets grid opens in a panel from a Layers button next to "Añadir degradado" (the Add <-> Remove toggle) — ColorPicker-style "select a saved preset -> Remove", and it reclaims the tab-header height. - Add / Remove presets fire commit.fulfill / commit.loss firmas (new gradient-picker sema pack, registered in the demo). Per the Button doctrine the press is a neutral contact.activate and the consumer emits the RESULT commit; the events target the REGISTERED `provider` part (targeting the unregistered `content` part was also why the Clear commit-reset was silent). - Stop color editor hides the remove-stop button for the endpoint stops (first / last by position — they anchor 0% / 100%). - maxPresets cap (default 10). StopList in a ScrollArea (autosize, slim scrollbar with a gap so the rows never sit under it). Composes the existing components AS-IS (Popover / Button / ScrollArea / the GradientBuilder parts) without overriding their styles. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
gradientBuilderSema,
gradientPickerSema,
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,
navTreeSema,
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,
fix(splitter): register sema scope + cascade so handle-pick/drag actually sound Two missing registrations meant the splitter sema cascade from the previous commit never reached the engine — handle-pick and handle-drag were silent, only commit-set sounded (because the `commit` family has a sound payload baked into its SEMA_MAP base, while `handle` only has haptic and depends on the cascade for sound). Fixes: 1. morfo scope: ['soma'] → ['soma', 'sema', 'eidos']. Without 'sema' the per-component cascade rules in src/uix/sema/components/splitter.ts are never honored. 2. web/routes/uix/+layout@.svelte: add splitterSema to the `components: [...]` array passed to defineEngineSemantic. The sema engine only loads cascade packs it's explicitly given, so forgetting this line silently drops all splitter cascade rules. Verified in browser at /uix/components/splitter via Live tab (real pointer interaction, AudioContext.createOscillator counter): handle-pick → 2 oscillators (handle.pickup.air, gain 0.10) handle-drag → 16 oscillators across 8 throttled emits — the dynamic resolveSplitterDragSound curve is playing per-frame as expected commit-set → 2 oscillators (handle.snap.chime, gain 0.14) Note: the Sema tab's static "▶ play" button still shows 0 oscillators for handle-drag — same as Slider's demo. By design: that button calls engine.emit() directly without the per-emit `signal.overrides` that soma injects during real drag. The dynamic curve IS the payload. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
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,
toolbarSema
]
}
});
setActiveUix(uix);
Soma.create();
const prefsProjection = createActivePrefsDomProjection({ prefs: uix.prefs, dom: uix.dom });
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' }
]
},
{
kind: 'group',
heading: 'Controls',
items: [
{ slug: '/uix/components/toggle', label: 'Toggle' },
{ slug: '/uix/components/toggle-group', label: 'Toggle group' },
{ slug: '/uix/components/switch', label: 'Switch' },
{ slug: '/uix/components/checkbox', label: 'Checkbox' },
{ slug: '/uix/components/radio-group', label: 'Radio group' },
{ slug: '/uix/components/radio-cards', label: 'Radio cards' },
{ slug: '/uix/components/rating-group', label: 'Rating group' },
{ slug: '/uix/components/slider', label: 'Slider' },
{ 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' },
{ 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' },
feat(eidos): port time-range-field from soma Eidos wrapper for time range selection — start/end TimeField inputs sharing a single ZonedDateTime range value. Mirrors DateRangeField pattern with `data-time-field` overlay trick so item segments inherit the time-field recipe. ## Feature comparison | Feature | react-aria | Mantine | Eidos | |----------------------|------------|---------|-------| | Range value | ✓ | ✓ | ✓ | | Per-endpoint Input | ✓ | ✓ | ✓ | | Segment editing | ✓ | — | ✓ | | Granularity prop | ✓ | ✓ | ✓ | | Hour cycle (12/24) | ✓ | ✓ | ✓ | | Min/max value | ✓ | ✓ | ✓ | | Placeholder per-end | ✓ | — | ✓ | | Invalid signalling | ✓ | ✓ | ✓ | | Disabled / readOnly | ✓ | ✓ | ✓ | | Form integration | ✓ | ✓ | ✓ | | Size cascade | — | ✓ | ✓ xs–xl | | Variant cascade | — | ✓ | ✓ 3 | | Color cascade | — | — | ✓ 6 | ## Parts Root · Label · Input (type='start' | 'end') · Segment (re-exported from TimeField). Each Input scopes its own TimeField context so segment editing routes to the correct endpoint. ## Recipe Grid `1fr · auto · 1fr` with em-dash separator. Specificity bump on `[data-time-range-field][data-time-field]` to win over the overlay data-time-field rules. ## Demo Canonical 6-tab depth (~530L): Live / API / Morfo / Sema / Recipe / A11y. Variant / size / color / granularity / hour-cycle / min-max / disabled / readOnly / required controls. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
{ slug: '/uix/components/time-range-field', label: 'Time range field' },
{ slug: '/uix/components/time-picker', label: 'Time picker' },
feat(time-range-picker): eidos wrapper + canonical picker-shell parity Builds the Eidos time-range-picker layer end-to-end and brings color-picker's morfo into parity with the other pickers. time-range-picker - soma: `mode` prop, `pickerShellContext` registration with commit/cancel/clear and `valueOnOpen` snapshot for revert; Provider now renders a div with data-state/disabled/readonly/required + aria mirrors. - soma: new `Clock` part (mirrors TimePicker.Clock with role="dialog" and data-state). - morfo: scope ['soma','sema'] + apg dialog-modal; promoted Provider to public div with states/data-state; added Clock part; declared the 6 canonical picker events (open / close-commit / close-cancel / close-dismiss / close-dismiss-outside / commit-clear); slider parts declare data-endpoint + data-disabled. - eidos: full namespace (Trigger / Content / Clock / HourSlider / MinuteSlider / SecondSlider / DayPeriodToggle + re-exported PickerShell Footer / Clear / Cancel / Close). Sliders compose generic SliderProvider (N-8) and ship with optional rulers above hour / below minute+second. - eidos css: 2-column endpoint grid in the clock; per-endpoint range tint; AM/PM toggle styling; reuses time-field tokens. - demo: interactive testbed at /uix/components/time-range-picker mirroring the time-picker demo — every soma + eidos prop has a live control, closeOnRangeComplete + start/endReadonlySegments toggles, footer composition switches, sema play buttons, soma+eidos snippets. - nav: added entry between Time picker and Color field. color-picker (audit fixes) - morfo: Provider declares states ['open','closed'] + data-state attr; added the same 6 canonical picker events targeting the `content` part (handle-pick / handle-drag / commit-set preserved). - soma: runtime now feeds open state + disabled/readonly/required/invalid props to the morfo runtime so data-state actually emits. Verified: svelte-check --threshold error → 0 errors / 0 warnings / 4524 files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
{ 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' },
{ slug: '/uix/components/nav-tree', label: 'Nav tree' }
]
},
{
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' },
{ 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' },
{ slug: '/uix/components/media-player', label: 'Media player' }
]
},
{
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' },
uix(callout): F1.4 · admonicion inline (morfo+eidos) al suelo del dossier Tercera pieza F1 del plan blocks (alcance E-2, dossier §P3). Banner = tira de anuncio de pagina; Callout = el aside del documento (split deliberado). - morfo: 4 partes (provider role=note / icon / title / content) + texts con TITULOS DEFAULT LOCALIZADOS por intent (note/tip/warning/caution — patron GitHub de label visible: la semantica nunca viaja solo en color/icono); 0 eventos justificados (dismissible DIFERIDO a pasada soma+sema por la regla de admision — dismiss ES un evento real) - el hueco IMPORTANT resuelto con el modelo Radix, sin 5o enum: `intent` (neutral|affirm|risk|threat — mapea NOTE/TIP/WARNING/CAUTION) + `color` override SOLO bajo neutral (doctrina §4: intent evaluativo gana); IMPORTANT = intent neutral + color + titulo propio (preset en la demo) - pintura sobre la maquinaria C6/THM-2 (patron badge): forwarders por color + slots `_palette-track/text/solid` → el generador emite la cascada de 8 roles Y el forward presence-guarded al shared layer — 33 escalas y colores custom con CERO CSS extra - a11y: role=note NOMBRADO via aria-labelledby → Title SOLO mientras esta montado (registro reactivo por context local); escalacion tipada role=status|alert|none (el role=alert estatico de shadcn = bug de referencia que NO copiamos); Title NO es heading (protege el outline que escaneara anchor-nav); icono decorativo = mapa doctrinal IntentIcon - fix cazado en navegador: el `+=` del registro leia el estado dentro del tracking del $effect del hijo → effect_update_depth silencioso (contador a -997, attr nunca estampado) — untrack() en register/cleanup; leccion registrada en memoria (incidente 2 de la clase) - recipe: grid con acento logico border-inline-start (RTL-correcto), nesting tolerado (Docusaurus); demo v2 9 tabs con preset IMPORTANT + PalettePicker showIntent=false; README completo Verificacion: component:audit PASS 0E/0W · eidos-lint 8 morfo-backed/0 invalid · guards 37/37 · svelte-check 76E/51W = baseline exacto · navegador: «Nota»/«Atencion» localizados · risk=ambar hue 45-60 · IMPORTANT=plum hue 326 resuelto por shared layer · labelledby=titleId · cero errores de consola. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
{ 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: [
{ 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' }
]
},
{
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' }
]
},
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
]
},
{
kind: 'group',
heading: 'Overlays',
items: [
{ slug: '/uix/components/dialog', label: 'Dialog' },
{ slug: '/uix/components/alert-dialog', label: 'Alert dialog' },
{ 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' }
]
},
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' },
{ 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' },
{ slug: '/uix/components/splitter', label: 'Splitter' } ,
{ slug: '/uix/components/sticky', label: 'Sticky' }
]
}
];
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);
}
}
});
feat(text-gradient): `colors` discriminates canonical name vs stop list (folds `preset`) Per request: one `colors` prop that discriminates by shape instead of two rival props. A `string[]` is an explicit stop list (unchanged); a bare `string` names a canonical gradient from the theming's named set, resolved as `var(--gradient-{name})` — exactly what the removed `preset` prop did. The set is an open cage (`EidosConfig.gradients` / `applyGradients`), so the type stays `string | readonly string[]`, not a closed union. Removed `preset` outright (no back-compat shim, per project rule); the canonical S9 vocabulary and raw stops are now the SAME input. Docs shell: register a small canonical named set on `uix/+layout@` via `applyGradients` (`brand`, `spectrum`) — role-derived, so they re-tint with the seed and flip light/dark. Demo gains a live stops|named toggle so both branches are a real testbed. Finding worth recording (types + README): a named gradient painted through `background-clip: text` must be a single `<image>` (linear/radial/conic). A role-derived MESH serializes to radials + a base COLOR, and a bare color is not a valid `background-image` (only the `background` shorthand accepts it) — so a mesh name computes to `none` and never paints. The docs set is linear-only for this reason. Verified in real Chrome: stops branch paints the token gradient; `colors="spectrum"` paints `var(--gradient-spectrum)` through the text. svelte-check 0 own errors, component:audit 141/0/0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
// 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(() => {
prefsProjection.dispose();
activeEidos.dispose();
uix.dispose();
});
$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);
});
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.
let dir = $state<'ltr' | 'rtl'>('ltr');
let language = $state<Language>('es');
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',
de: 'Deutsch'
};
const CURRENCY_LABELS: Record<Currency, string> = {
EUR: 'Euro',
USD: 'US Dollar',
GBP: 'British Pound',
JPY: 'Japanese Yen'
};
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);
if (p.dir === 'ltr' || p.dir === 'rtl') dir = p.dir;
if (p.language) language = p.language;
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;
} catch {
/* ignore corrupt prefs blob */
}
});
$effect(() => {
if (typeof localStorage === 'undefined') return;
const blob = {
dir,
language,
currency,
units,
numberFormat,
soundOn,
motionOn,
hapticOn,
density,
scaling,
borderWidth
};
localStorage.setItem('uix-docs-prefs', JSON.stringify(blob));
});
$effect(() => {
uix.prefs.setIntent('language', language);
uix.prefs.setIntent('direction', dir);
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');
});
// 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;}`
);
});
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');
</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
so all eidos components (which use logical properties)
reverse correctly. -->
<button
type="button"
data-uix-icon-btn
title={dir === 'ltr' ? 'Switch to RTL' : 'Switch to LTR'}
aria-label="Toggle direction"
aria-pressed={dir === 'rtl'}
onclick={() => (dir = dir === 'ltr' ? 'rtl' : 'ltr')}
>
{#if dir === '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" />
</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" />
</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}
</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
>
</button>
{/each}
</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>
<!-- 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
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"
/>
</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" />
</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>
</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>
{/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>
{/if}
{/each}
{#if filteredNav.length === 0}
<div data-uix-rail-group-head>No matches for "{search}"</div>
{/if}
</aside>
<main data-uix-canvas>
{@render children?.()}
</main>
</div>
</div>

Powered by TurnKey Linux.