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

291 lines
15 KiB

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Build/Test Commands
```bash
npm run dev # Start dev server
npm run build # Production build (static site via adapter-static)
npm run test # Run all tests once
npm run test:unit # Run tests in watch mode
npx vitest run src/lib/ling/test/ling.test.ts # Run single test file
npx vitest run -t "describe name" # Run tests matching pattern
npm run check # Type check with svelte-check
npm run lint # Check formatting with Prettier
npm run format # Auto-format with Prettier
```
## Code Style
- Tabs for indentation, single quotes, no trailing commas, 100 char print width (Prettier)
- Spanish comments in code are acceptable
- Svelte 5 runes mode enforced globally (`svelte.config.js` `dynamicCompileOptions`)
- DOM event handlers: lowercase (`onclick`, `onfocus`). User callbacks: camelCase (`onComplete`, `onInteractOutside`)
- State fields: `$state()` without `#`. Derived: `readonly x = $derived.by(...)`
- Boolean attrs: always use helpers (`boolToTrueOrUndef`, `boolToEmptyStrOrUndef`, `boolToStr`) — never ternaries
## Path Aliases
Aliases must be synced in BOTH `svelte.config.js` AND `vite.config.ts`:
| Alias | Target |
| ----------------------- | ------------------------------------------------------ |
| `@/` | `src/` |
| `@/ling` | `src/lib/ling` (i18n) |
| `@/logr` | `src/lib/logr` (logging) |
| `@/glob` | `src/lib/glob` (legacy; do not use for new code) |
| `@/actx` | `src/lib/actx` (app context) |
| `@/uiux` | `src/lib/uiux` |
| `$uix` | `src/uix` (UI layers) |
| `$terra` | `src/uix/terra` (headless primitives) |
| `$terra/utils` | `src/uix/terra/utils/sound.svelte.ts` |
| `$terra/system` | `src/uix/terra/system/exports.ts` |
| `$terra/external/dates` | `src/uix/terra/external/dates/sound.svelte.ts` |
| `$lib` | `src/lib` |
| `$lib/util/dias` | `src/lib/util/dias` (date/time library — canonical) |
## 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: Three-Layer UI System
```
terra → headless primitives: behavior, accessibility, data-* contracts, context
air → visual layer on top of terra: tokens, themes, recipes, styled components
glob → domain logic: dates, numbers, currency, units, locale
```
The dependency flows one way: `air` → `terra`. Terra never imports from air.
### terra (`src/uix/terra/`)
Headless primitive library. Each component follows `Element.SubElement` pattern (`Dialog.Root`, `Dialog.Trigger`, `Dialog.Content`).
**Key patterns:**
- Thin `.svelte` wrappers + logic in `*.svelte.ts` State classes
- State classes use `static create()` for context registration, `createComposed()` for internal composition without context
- Reactive system: `Active<T>`, `State<T>`, `readableActive()`, `writableActive()` — values travel as boxes with `.current`
- Context via `createTerraContext()` (wraps runed's `Context`)
- Props merging: `mergeProps(restProps, state.props)` in every wrapper
- `data-*` attrs are formal public API, validated via `assertDataContract()` against `utils/contracts.ts`
- `child` snippet replaces the rendered node; `children` fills the interior
**Structure:**
- `[component]/components/` — Svelte wrappers
- `[component]/types.ts` — public props
- `[component]/*.svelte.ts` — State classes
- `[component]/exports.ts` + `sound.svelte.ts` — barrel exports
- `layers/` — shared infra (portal, floating, presence, focus-scope, dismissible, escape, scroll)
- `dom/` — browser-dependent utils (focus, roving focus, arrow navigation)
- `utils/` — pure helpers (state system, attrs, merge-props, events, keyboard, css, datetime)
- `system/` — `TerraConfig` for cross-cutting services (translator, formatters, logger, dir, portalTo)
- `external/dates/` — adapter boundary for date library
**Import rules within terra:** use `$terra/utils` or relative paths internally, never `$uix/terra`.
### air (`src/uix/air/`)
Visual layer built on terra. Consumes terra's public anatomy and `data-*` attrs — never internal classes or incidental DOM structure.
**Three-level token system:**
1. **Primitive tokens** (`tokens/primitive.css`) — absolute scale values, never used directly by components
2. **Semantic tokens** (`tokens/semantic.css`) — theme-aware, switch via `[data-theme]`
3. **Component tokens** (`tokens/components/*.css`) — scoped per component (`--air-button-*`), the only level component CSS writes
**Token naming:**
- Public: `--air-{component}-*` (stable, overridable)
- Private: `--_air-{component}-*` (implementation detail, may change)
**Contracts** (`contracts/`):
- `contract.css` — minimum semantic tokens any theme must implement
- `contract-categorical.css` — optional categorical color families
- `contract-primitives.css` — optional advanced 12-step scales
- `contracts/components/*.css` — per-component public token API
**CSS rules:**
- Tokens and `data-*` selectors must be global CSS (imported `.css` files), not `<style scoped>`
- Svelte scoped styles only for properties that don't depend on `data-*` or terra internals
- Components respond to terra states purely via `data-*` selectors: `[data-disabled]`, `[data-state='open']`, etc.
- Theme activation via `data-theme` attribute on any DOM container (supports nesting)
- Variant dimensions are orthogonal: `size` → dimensions, `variant` → appearance, `state` → transversal overrides
**Structure:**
- `components/` — styled wrappers over terra primitives
- `tokens/` — token implementation (primitive, semantic, per-component)
- `contracts/` — public token interfaces for theme authors
- `themes/` — theme implementations (base light/dark)
- `icons/` — icon system (`Icon` + per-icon components in `icons/lib/`)
### Internal Libraries (`src/lib/`)
- `ling` — i18n system (factory: `createLing`)
- `logr` — logging (factory: `createLogr`, depends on `ling`)
- `ext/` — App-layer reactive services: `app`, `dates`, `lang`, `money`, `nums`, `presentation`, `units`
- `util/dias` — canonical date/time library. Single public door at `$lib/util/dias`. `_vendor/` holds the internal implementation (Adobe Apache 2.0 vendored code) and MUST NOT be imported by consumers
- `glob` — legacy (pre-App-layer); do not use for new code
- `actx` — app context
- `vice` — app-level utilities
**Dates — canonical layout:**
- Value types (`CalendarDate`, `CalendarDateTime`, `Time`, `ZonedDateTime`), 9 calendars, queries, operations, parsing: all re-exported from `$lib/util/dias`
- All date-related `Intl` (`DateFormatter`, cache, `resolveDateOrder`, `resolveHourCycle`, placeholders, defaults) lives in `$lib/util/dias/format.ts`
- `ext/dates` is a thin reactive wrapper for App-level preferences (dateOrder, hourCycle) that delegates formatting to `dias`
- `HourCycle` is canonically numeric — `12 | 24` — matching `Intl.DateTimeFormat`'s `hour12` resolved option. Never `'12h' | '24h'`
- Soma consumes via `$soma/external/dates`; terra via `$terra/external/dates`. Both re-export from `dias`. Components never import `dias` directly
**Number formatting:** use `createNumr` from the App layer, NEVER raw `Intl.NumberFormat`.
### soma (`src/uix/soma/`)
Headless component library. Follows terra's architecture exactly — same patterns, same conventions. Uses `$soma` alias.
**Key patterns (must match terra):**
- Child providers reference parent as `provider`, NEVER `root`
- **The root export is always `Provider`, never `Root`**. Consumer writes `<Dialog.Provider>`, `<Calendar.Provider>`. `createAttrs` still uses the part name `'root'` internally — that is the DOM attribute part, not the export
- **Data-attr naming is canonical**: `data-{component}` (root) and `data-{component}-{part}` (sub-parts), emitted by `createAttrs`. **Never** `data-soma-*`. QuerySelectors, CSS selectors, README tables, and inline strings must all match exactly what `createAttrs` writes on the DOM — the contract validator does NOT check this (only enum values)
- `onChange` callbacks go in `writableActive` setter in the `.svelte` wrapper, NOT in provider Opts
- Translations: idlangref constants (`#?components.xxx.yyy|Fallback`) defined in the component's `langs.ts`. Root-level entries live in `src/uix/soma/core/langs.ts` under `components.{kebab-name}`. Providers call `soma?.langs.ts(IDLANGREF)` — no `resolveSomaTranslationPath` wrapper
- Props defined ONCE in `types.ts` (consumer API). Provider Opts only has reactive wrappers + internal fields
- `Soma.get()` returns the Soma config from context. `Soma.require()` throws when missing
- Test pages use shared layout at `src/routes/test/soma/+layout.svelte` with real `createLangs()` + `createPresentation()` + `App.create()`
**Structure:** mirrors terra exactly:
- `[component]/components/` — Svelte wrappers
- `[component]/types.ts` — public props with JSDoc
- `[component]/*-provider.svelte.ts` — Provider classes
- `[component]/exports.ts` + `index.ts` — barrel exports
- `core/` — Soma class, translator adapter, prop-resolvers, formatters
- `provider/` — base Provider class, context utility
- `layers/` — shared infra (floating, presence, focus-scope, dismissal, scroll-lock)
**Import rules within soma:** use `$soma/` alias paths, never relative paths across component boundaries.
### Test Pages
Component demos live in `src/routes/test/`. Each terra/air/soma component should have a test page demonstrating its states and variants.
Soma test pages share a layout at `src/routes/test/soma/+layout.svelte` that provides:
- Real ling instance with soma translations via `extendSomaTranslationModule`
- `createSomaTranslator` adapter connecting to ling
- Locale/dir switcher in a sticky bar
- `<Soma>` wrapper for all children
Test pages should NOT create their own `<Soma>` or translator — use the layout's.
## TerraConfig / Soma Integration
```svelte
<!-- Terra -->
<TerraConfig {translator} {dateTimeFormatter}>
<Dialog.Root />
</TerraConfig>
<!-- Soma -->
<Soma {translator} {presentation}>
<Dialog.Provider />
</Soma>
```
Priority chain for resolved props: explicit component prop → config context value → internal fallback. Both support nesting (child overrides parent partially).
## Key Conventions
- When creating terra components: follow the checklist in `src/uix/terra/README.md` §21
- When creating soma components: follow the 27-item checklist in `src/uix/soma/COMPONENT_GUIDE.md`. Walk every item explicitly before reporting done — this is mandatory, not optional. Compare features against ark-ui, bits-ui, radix-ui, react-aria and document the gap table in the component's README
- When reading existing modules: start with `exports.ts` → `types.ts` → root `.svelte` → `*-provider.svelte.ts` → child wrappers → test page → README
- `$bindable()` without fallback when parent might pass `undefined`; apply defaults via coalescing (see `src/uix/terra/README.md` §13.1)
- Components that use Portal: consumer must manage `z-index` explicitly (layers don't impose z-indices)
- Every soma component ships with its own `README.md` following the format of `dialog/README.md` — anatomy, parts, props, ARIA, data-attrs, keyboard, comparison table, example
## Critical Rules
- **NEVER modify terra source code.** Terra is the reference architecture. Soma copies its patterns.
- **NEVER delete files without explicit instruction** ("delete", "remove", "borra", "elimina"). If ambiguous, ASK first. Reading costs nothing, deleting can be irreversible.
- **NEVER create fake translators** in test pages. Use real ling instances with `createSomaTranslator` + `extendSomaTranslationModule`.
- **READ before acting.** When told to read a file, read it. Don't interpret "léete" as "eléte".
- **Verify before reporting done.** Run `npm run check`, test the UI in the browser.
- Soma audit: `src/uix/soma/AUDIT.md` — 4 pending issues (F3, F12, F13, F14). Read it before making architectural changes.
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
## 1. Think Before Coding
**Don't assume. Don't hide confusion. Surface tradeoffs.**
Before implementing:
- State your 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.
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
## 3. Surgical Changes
**Touch only what you must. Clean up only your own mess.**
When editing existing code:
- 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 imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
## 4. Goal-Driven Execution
**Define success criteria. Loop until verified.**
Transform tasks into verifiable goals:
- "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"
For multi-step tasks, state a brief plan:
```
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]
```
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
---
**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.

Powered by TurnKey Linux.