12 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Branch note (
active-uix): the legacy layerssrc/lib/,src/uix/terra/,src/uix/air/and the oldsrc/routes/test tree were removed during the UIX refactor. The new ecosystem lives insrc/arts/,src/libs/,src/svrs/, andsrc/uix/{active-uix,soma,sema,eidos,morfo}. Demos are being re-authored underweb/routes/from scratch.
Build / Test Commands
npm run dev # Start dev server (routes resolve from web/routes)
npm run build # Production build (static via adapter-static)
npm run test # Run all tests once
npm run test:unit # Run tests in watch mode
npx vitest run src/uix/morfo/compile.test.ts # Run a single test file
npx vitest run -t "describe name" # Run tests matching a pattern
npm run check # Type check with svelte-check
npm run lint # Check formatting (Prettier)
npm run format # Auto-format
Code Style
- Tabs for indentation, single quotes, no trailing commas, 100 char print width (Prettier).
- Comments in English. (Some legacy Spanish comments remain — don't add new ones.)
- Svelte 5 runes mode is enforced globally (
svelte.config.jsdynamicCompileOptions). - DOM event handlers: lowercase (
onclick,onfocus). User callbacks: camelCase (onComplete,onInteractOutside). - State:
$state()without#. Derived:readonly x = $derived.by(...).
Path Aliases
Aliases are kept in sync between svelte.config.js and vite.config.ts. Source
of truth is vite.config.ts (single aliases const reused by both top-level
resolve and the server-test project).
| Alias | Target |
|---|---|
@ |
src/ |
$active-app, $adom, $auth, $bus, $cache, $connection, $format, $frontend, $http, $lang, $logger, $orca, $perm, $prefs, $session, $sium, $storage, $timer |
src/arts/{name} |
$libs, $locale, $reactive |
src/libs, src/libs/locale, src/libs/reactive |
$svrs |
src/svrs/ |
$uix, $active-uix, $soma |
src/uix, src/uix/active-uix, src/uix/soma |
Vitest Two-Project Structure
vite.config.ts defines two test projects:
- client: Browser tests via Playwright for
*.svelte.{test,spec}.{js,ts}. - server: Node environment for
*.{test,spec}.{js,ts}(excludes svelte tests).
Architecture
The UIX is layered around a declarative contract (morfo) that the other layers
consume. Each consumer reads the morfo via compileMorfo(morfo), never by
walking the raw declaration.
arts/ → runtime artifacts: active-app, adom, perm, prefs, ...
libs/ → pure helpers (zero-dep): reactive, days, dom, locale, ...
svrs/ → server-authoritative engines
uix/morfo → declarative contract: parts, data-attrs, ARIA, keyboard,
events with semantic family/intent. Compiler at compile.ts
emits CompiledMorfo (cached by WeakMap).
uix/sema → semantic engine + channels (visual / sound / vibra).
The visual channel writes data-event* during a hold window.
State attrs (data-state, data-disabled, ...) are NEVER
touched by sema — they belong to the morfo runtime.
uix/soma → headless component layer. Each provider consumes morfo +
sema via `runtime.trigger(eventName)` which sequences:
prewrite → semantic.emit → handler → effect-driven state attrs.
uix/eidos → CSS layer that selects against the data-attrs the morfo
contract promises. lint.ts validates a CSS file against
compileMorfo(morfo).contracts.cssSelectors.
uix/active-uix → composition root. Two boot modes:
- createActiveUix(options) — standalone (owns services)
- attachActiveUix(activeApp) — attach to external app
morfo (src/uix/morfo/)
Single source of truth for a component. A Morfo declares:
parts[]— kebab + archetype +data+aria+ optionalkeyboardevents[]— name +targetpartRef +semantic+ optionalprewrite+commitsfocus— initial / trap / return / restore (for overlay components)
compileMorfo(morfo) returns a CompiledMorfo with pre-resolved AttrPlan[],
KeyboardPlan[], ActionPlan[], and contracts.cssSelectors (the closed set
of selectors eidos is allowed to use). Cached by morfo identity.
eidos linter
scripts/eidos-lint.ts <component> and scripts/eidos-lint-all.ts classify every
[data-*] selector in eidos CSS as morfo-backed / eidos-only / invalid. Use
this to detect drift between the morfo contract and the eidos rules.
Key Conventions
- Never modify the morfo declaration files without updating the eidos selectors and provider sources that depend on them.
- Read before acting. When told to read a file, read it. Don't paraphrase.
- Verify before reporting done. Run
npm run checkand the relevant vitest scope. UI claims need a browser check; if you can't run a browser, say so. - No backward-compat shims when relocating code. Update consumers and delete the old path; don't leave a re-export.
- No re-export façades between layers (e.g. soma must not re-export dias —
consume
$libs/daysdirectly). - Provider naming: child providers reference the parent as
provider, neverroot. The exported component is<Xxx.Provider>.createAttrsstill uses the part kebab'provider'internally. - Data-attr naming:
data-{component}(provider) anddata-{component}-{kebab}(sub-parts). Neverdata-soma-*. The compiler emits these viacompiled.parts.attrs; runtime queries match exactly.
Critical Rules
- NEVER delete files without explicit instruction ("delete", "remove", "borra", "elimina"). If ambiguous, ASK first.
- NEVER create fake translators / mocked services in tests for components.
Use real instances composed via
createActiveUixorattachActiveUix. - NEVER skip hooks (
--no-verify) or bypass signing unless explicitly asked. If a hook fails, fix the root cause. $reactivesurvives,$libdoes not. The$reactivealias points to the currentsrc/libs/reactive; the singular$libalias was removed in the cleanup phase and any code that resurfaces it is a regression.
Reference Documents
Architecture overview and motivations: src/uix/active_architecture.md.
Per-layer references:
src/uix/morfo/README.mdsrc/uix/sema/README.mdsrc/uix/soma/SOMA_ARCHITECTURE.md+src/uix/soma/COMPONENT_GUIDE.mdsrc/uix/eidos/README.md+src/uix/eidos/components/README.md
The doctrinal API conventions (intent ↔ color resolution, per-component
subset, soma compound vs eidos flat, sound eager-init, etc.) live in
src/docs/sema-implementation-guide.md
Parte IV — Convenciones del API. That section is authoritative for any
new component or migration. Read it before designing a wrapper, declaring
intents, or amplifying motion in a docs preview.
Session hand-off — 2026-05-08
Where we left off:
- Toggle is the eidos pilot (
src/uix/eidos/components/toggle/): recipe + Svelte wrapper + types + index + README. The pattern is documented insrc/uix/eidos/components/README.md. Use it as the template to migrate the next component. - Eidos has a shared lib at
src/uix/eidos/lib/types.ts. First type isSize('xxs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl' | 'full'). Components narrow withExtract<Size, 'sm' | 'md' | 'lg'>per the per-component-subset doctrine. - No
Eidosprefix on types. The path$uix/eidos/components/{x}already conveys layer. Public types areToggleProps,ToggleVariant,ToggleSize. - API doctrine: soma stays compound (
Toggle.Provider); eidos exports bothdefaultandProviderfor single-part components so<Toggle>works for the ergonomic case while<Toggle.Provider>stays available. - SoundChannel eager-init landed in
src/uix/sema/chans/sound.tsto fix the autoplay race: the AudioContext is created + resumed synchronously on the first user gesture (capture-phase listener ondocument). - Demo page (
web/routes/toggle/+page.svelte) has the live preview rendered ALWAYS above the tablist (so Sema-tab Play buttons can fire on the real toggle) and the motion preview amplifies scale ×8 visually only — doctrinal values stay in the<dl>.
Next concrete steps:
- Migrate the next eidos component from flat CSS to the
subdirectory wrapper pattern. Candidates in priority order:
switch→collapsible→dialog→drawer→popover→toast→avatar. Each will need its own demo page underweb/routes/{name}/+page.sveltemirroring toggle's tab structure. - The docs site's topbar prefs (sound mute toggle, theme switcher)
are wired up but the sound mute does NOT yet drive
SoundChannel.masterGain. Wiring belongs on layout.svelte's$effect. - Theme global token rename — long-deferred. The 8 doctrinal tokens
(
primary, secondary, neutral, affirm, fulfill, risk, threat, loss) are referenced by thedata-colorrecipes via fallback aliases (success → fulfill,warning → risk,danger → threat). When the theme files insrc/uix/eidos/themes/base/{light,dark}.cssadopt the doctrinal names directly, drop the fallback aliases in the per-component recipes. EidosTogglePropswas renamed; grep for any staleEidos*Propsreference before adding new ones — there should be none.
Baseline npm run check shows 39 pre-existing errors in test files
unrelated to this work. Sema tests are green (53/53).
Behavioral Guidelines
These reduce common LLM coding mistakes. They bias toward caution over speed — for trivial tasks, use judgment.
1. Think before coding
Don't assume. Don't hide confusion. Surface tradeoffs.
- State assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them — don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
2. Simplicity first
Minimum code that solves the problem. Nothing speculative.
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
3. Surgical changes
Touch only what you must. Clean up only your own mess.
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it — don't delete it.
When your changes create orphans, remove the imports / variables / functions your changes made unused. Don't remove pre-existing dead code unless asked.
4. Goal-driven execution
Define success criteria. Loop until verified.
- "Add validation" → "Write tests for invalid inputs, then make them pass."
- "Fix the bug" → "Write a test that reproduces it, then make it pass."
- "Refactor X" → "Ensure tests pass before and after."
Strong success criteria let you loop independently. Weak criteria require constant clarification.