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/CLAUDE.md

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 layers src/lib/, src/uix/terra/, src/uix/air/ and the old src/routes/ test tree were removed during the UIX refactor. The new ecosystem lives in src/arts/, src/libs/, src/svrs/, and src/uix/{active-uix,soma,sema,eidos,morfo}. Demos are being re-authored under web/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.js dynamicCompileOptions).
  • 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 + optional keyboard
  • events[] — name + target partRef + semantic + optional prewrite + commits
  • focus — 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 check and 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/days directly).
  • Provider naming: child providers reference the parent as provider, never root. The exported component is <Xxx.Provider>. createAttrs still uses the part kebab 'provider' internally.
  • Data-attr naming: data-{component} (provider) and data-{component}-{kebab} (sub-parts). Never data-soma-*. The compiler emits these via compiled.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 createActiveUix or attachActiveUix.
  • NEVER skip hooks (--no-verify) or bypass signing unless explicitly asked. If a hook fails, fix the root cause.
  • $reactive survives, $lib does not. The $reactive alias points to the current src/libs/reactive; the singular $lib alias 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:

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 in src/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 is Size ('xxs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl' | 'full'). Components narrow with Extract<Size, 'sm' | 'md' | 'lg'> per the per-component-subset doctrine.
  • No Eidos prefix on types. The path $uix/eidos/components/{x} already conveys layer. Public types are ToggleProps, ToggleVariant, ToggleSize.
  • API doctrine: soma stays compound (Toggle.Provider); eidos exports both default and Provider for 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.ts to fix the autoplay race: the AudioContext is created + resumed synchronously on the first user gesture (capture-phase listener on document).
  • 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:

  1. 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 under web/routes/{name}/+page.svelte mirroring toggle's tab structure.
  2. 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.
  3. Theme global token rename — long-deferred. The 8 doctrinal tokens (primary, secondary, neutral, affirm, fulfill, risk, threat, loss) are referenced by the data-color recipes via fallback aliases (success → fulfill, warning → risk, danger → threat). When the theme files in src/uix/eidos/themes/base/{light,dark}.css adopt the doctrinal names directly, drop the fallback aliases in the per-component recipes.
  4. EidosToggleProps was renamed; grep for any stale Eidos*Props reference 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.

Powered by TurnKey Linux.