diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..5963be51 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,9 @@ +# WASM targets for wasmtime. +# wasm32-unknown-unknown is used by default (available on stable). +# For full WASI support, install: rustup target add wasm32-wasi --toolchain nightly + +[target.wasm32-unknown-unknown] +rustflags = ["-C", "target-feature=+atomics,+bulk-memory,+mutable-globals,+simd128"] + +[target.wasm32-wasi] +rustflags = ["-C", "target-feature=+atomics,+bulk-memory,+mutable-globals,+simd128"] diff --git a/.claude/rules/cleanup-downloads.md b/.claude/rules/cleanup-downloads.md new file mode 100644 index 00000000..074a57a8 --- /dev/null +++ b/.claude/rules/cleanup-downloads.md @@ -0,0 +1,32 @@ +--- +description: Clean up any downloaded files after they are no longer needed. +glob: "*" +--- + +# Clean Up Downloaded Files + +After downloading any files (archives, packages, binaries, datasets, temporary assets), remove them once they have served their purpose. + +## Rules + +1. After extracting an archive, delete the archive. +2. After installing a package from a downloaded tarball, delete the tarball. +3. After copying needed data from a downloaded file, delete the original download. +4. After fetching a binary or tool for a one-time operation, delete it when done. + +## When to Clean + +Remove downloads immediately after: +- Extracting an archive (`tar`, `unzip`, etc.) +- Installing from a downloaded package +- Copying data from a temporary file +- Running a one-time tool or binary +- Generating a build artifact that's already committed + +## What to Keep + +Do **not** delete: +- Source code files +- Configuration files +- Build outputs that are part of the project +- Files tracked by git diff --git a/.claude/rules/commit-after-changes.md b/.claude/rules/commit-after-changes.md new file mode 100644 index 00000000..beaac3e6 --- /dev/null +++ b/.claude/rules/commit-after-changes.md @@ -0,0 +1,9 @@ +--- +description: Always commit after every meaningful code change before moving to the next task. +glob: "*" +--- + +# Commit After Changes + +After every meaningful code change (fix, feature, config update), stage and commit the files before moving to the next task. +Do not batch multiple changes into a single commit unless they are part of the same atomic fix. diff --git a/.claude/rules/escape-at-signs.md b/.claude/rules/escape-at-signs.md new file mode 100644 index 00000000..2b8a023e --- /dev/null +++ b/.claude/rules/escape-at-signs.md @@ -0,0 +1,15 @@ +--- +description: Escape @ symbols in commit messages and file content before committing to GitHub to prevent unwanted mentions. +glob: "*" +--- + +# Escape @ Symbols Before Committing + +Always escape `@` symbols with a backslash (`\@`) in commit messages and any file content before committing to GitHub, to prevent GitHub from interpreting them as user or team mentions. + +Apply this to: +- Commit messages +- Markdown files +- Any text that will be rendered by GitHub + +Example: `@username` → `\@username` diff --git a/.claude/rules/playwright-verify.md b/.claude/rules/playwright-verify.md new file mode 100644 index 00000000..9c908317 --- /dev/null +++ b/.claude/rules/playwright-verify.md @@ -0,0 +1,70 @@ +--- +description: Verify all visual and behavioral changes using Playwright MCP before claiming success. +glob: "*" +--- + +# Verify with Playwright MCP + +After making any visual or behavioral change, verify it works correctly using Playwright MCP browser tools. Never claim a feature works without confirming it in a real browser. + +## When to Verify + +Run Playwright verification after: +- CSS/style changes +- Component rewrites +- Layout modifications +- Adding or removing UI elements +- Changing responsive behavior +- Dark mode adjustments + +## Verification Steps + +1. **Navigate** to the affected page using `playwright_browser_navigate` +2. **Snapshot** the page using `playwright_browser_snapshot` to verify structure +3. **Screenshot** using `playwright_browser_take_screenshot` for visual confirmation +4. **Interact** with the changed elements (click, type, hover) to confirm behavior +5. **Compare** against expected appearance — if broken, fix immediately + +## Example Workflow + +``` +# After changing upload page styles +1. playwright_browser_navigate → http://localhost:5173/upload +2. playwright_browser_snapshot → verify component structure +3. playwright_browser_take_screenshot → visual check +4. playwright_browser_click → test interactive elements +5. Confirm no regressions +``` + +## Golden Path + Edge Cases + +Test both: +- **Golden path**: the main happy flow (e.g., select files → upload → share) +- **Edge cases**: empty states, error states, loading states, mobile viewports + +## Responsive Testing + +After CSS changes, verify at multiple viewports: + +``` +1. playwright_browser_resize → 1920x1080 (desktop) +2. playwright_browser_resize → 768x1024 (tablet) +3. playwright_browser_resize → 375x667 (mobile) +``` + +## Dark Mode Testing + +After style changes, toggle dark mode and verify: + +``` +1. playwright_browser_evaluate → toggle .dark class on +2. playwright_browser_snapshot → verify dark styles +3. Confirm no broken contrast or missing overrides +``` + +## What NOT to Do + +1. **Do NOT** claim a change works without browser verification +2. **Do NOT** skip verification for "small" CSS changes +3. **Do NOT** assume Tailwind classes work without confirming the output +4. **Do NOT** skip responsive verification after layout changes diff --git a/.claude/rules/readable-typescript.md b/.claude/rules/readable-typescript.md new file mode 100644 index 00000000..5cbdcc84 --- /dev/null +++ b/.claude/rules/readable-typescript.md @@ -0,0 +1,385 @@ +--- +description: Write verbose, readable TypeScript that prioritizes clarity, explicit intent, and a Python-like coding style. +glob: "*.ts" +--- + +# Code Style: Readable, Pythonic TypeScript + +Write code for humans first. + +Optimize for: +- Readability +- Maintainability +- Explicit intent +- Ease of modification + +Avoid writing code that is merely shorter. + +## Naming + +Use descriptive names. + +Prefer: + +```ts +const selectedFiles = files.filter(file => file.selected); +const totalDownloadSize = calculateTotalDownloadSize(files); +``` + +Over: + +```ts +const s = files.filter(f => f.selected); +const t = calc(files); +``` + +Names should explain: +- what a value contains +- why it exists +- how it is used + +Avoid unnecessary abbreviations. + +--- + +## Functions + +Functions should do one thing. + +Prefer: + +```ts +function getEnabledTrackers(trackers: Tracker[]): Tracker[] { + return trackers.filter(tracker => tracker.enabled); +} +``` + +Over: + +```ts +function process(trackers: Tracker[]) { + // 50 lines doing 7 different things +} +``` + +Extract logic into well-named helpers whenever doing so improves readability. + +--- + +## Prefer Expression-Oriented Code + +Favor transformations over step-by-step mutation. + +Prefer: + +```ts +const activePeerAddresses = peers + .filter(peer => peer.connected) + .map(peer => peer.address); +``` + +Over: + +```ts +const activePeerAddresses: string[] = []; + +for (const peer of peers) { + if (peer.connected) { + activePeerAddresses.push(peer.address); + } +} +``` + +The code should communicate *what* is happening, not *how*. + +--- + +## Iteration + +Prefer functional iteration methods: + +```ts +map() +filter() +find() +reduce() +some() +every() +flatMap() +forEach() +``` + +Prefer: + +```ts +const trackerUrls = trackers.map(tracker => tracker.url); +``` + +Over: + +```ts +const trackerUrls: string[] = []; + +for (const tracker of trackers) { + trackerUrls.push(tracker.url); +} +``` + +Use loops only when they genuinely improve clarity. + +--- + +## Object Operations + +Prefer object-oriented transformations. + +Use: + +```ts +Object.entries() +Object.keys() +Object.values() +Object.fromEntries() +``` + +Prefer: + +```ts +const enabledSettings = Object.fromEntries( + Object.entries(settings) + .filter(([, value]) => value.enabled) +); +``` + +Over manual property iteration. + +--- + +## Immutability + +Prefer immutable updates. + +Use: + +```ts +const updatedItems = items.with(index, newItem); +const filteredItems = items.toSpliced(index, 1); +``` + +Instead of mutating arrays directly. + +Prefer: + +```ts +return { + ...torrent, + progress: newProgress +}; +``` + +Over: + +```ts +torrent.progress = newProgress; +return torrent; +``` + +Mutation is acceptable when it clearly improves performance and the intent remains obvious. + +--- + +## Early Returns + +Avoid unnecessary nesting. + +Prefer: + +```ts +if (!torrent) { + return; +} + +if (!torrent.started) { + return; +} + +startDownload(torrent); +``` + +Over: + +```ts +if (torrent) { + if (torrent.started) { + startDownload(torrent); + } +} +``` + +Keep the happy path visible. + +--- + +## Conditionals + +Prefer positive conditions. + +Prefer: + +```ts +if (torrent.isComplete) { + return; +} +``` + +Over: + +```ts +if (!torrent.isComplete) { + // large block +} +``` + +Reduce mental negation whenever possible. + +--- + +## Destructuring + +Use destructuring when it improves readability. + +```ts +const { name, size, priority } = file; +``` + +Avoid excessive destructuring that obscures data origins. + +--- + +## Intermediate Variables + +Do not fear extra variables. + +Prefer: + +```ts +const selectedFiles = files.filter(file => file.selected); + +const totalSelectedSize = selectedFiles.reduce( + (totalSize, file) => totalSize + file.size, + 0 +); +``` + +Over: + +```ts +const totalSelectedSize = files + .filter(file => file.selected) + .reduce((a, b) => a + b.size, 0); +``` + +when intermediate names make the logic easier to understand. + +--- + +## Nesting + +Keep nesting shallow. + +Extract helpers instead of creating pyramids. + +Prefer: + +```ts +const downloadableFiles = files.filter(isDownloadable); + +return downloadableFiles.map(createDownloadTask); +``` + +Over deeply nested callbacks. + +--- + +## Explicitness + +Prefer code that explains itself. + +Prefer: + +```ts +const hasAnyHighPriorityFiles = files.some( + file => file.priority === DownloadPriority.High +); +``` + +Over: + +```ts +const hasHigh = files.some(f => f.priority === 7); +``` + +Magic numbers and cryptic values should be replaced with named constants. + +--- + +## Comments + +Use comments to explain *why*. + +Avoid comments that explain *what*. + +Good: + +```ts +// Metadata may be incomplete while magnet resolution is in progress. +``` + +Bad: + +```ts +// Increment index by one. +index++; +``` + +Well-written code should explain itself. + +--- + +## TypeScript + +Prefer explicit types for public APIs. + +```ts +function getTorrentInfo(hash: string): TorrentInfo { + ... +} +``` + +Allow local inference when the type is obvious. + +```ts +const trackerCount = trackers.length; +``` + +Do not add redundant type annotations. + +--- + +## Readability Rules + +When choosing between: +- shorter vs clearer +- clever vs obvious +- compact vs explicit + +Always choose: + +- clearer +- more explicit +- easier to modify +- easier to debug + +Future maintainers should understand the code without needing to mentally execute it. + +Write TypeScript as if it were clean Python with static types. diff --git a/.claude/rules/shadcn-svelte-exact.md b/.claude/rules/shadcn-svelte-exact.md new file mode 100644 index 00000000..1e451f84 --- /dev/null +++ b/.claude/rules/shadcn-svelte-exact.md @@ -0,0 +1,379 @@ +--- +description: Enforce exact shadcn-svelte documentation patterns for all Svelte component code. Never deviate from the official docs. +glob: "*.svelte" +--- + +# shadcn-svelte: Follow Docs Exactly + +Every shadcn-svelte component usage in this project **must match the official documentation exactly**. Never invent your own patterns. Never guess. Always follow the docs verbatim. + +## Source of Truth + +The official registry documentation is the only reference: +- **LLM docs**: `https://www.shadcn-svelte.com/llms.txt` +- **Main docs**: `https://www.shadcn-svelte.com/docs` +- **GitHub**: `https://github.com/huntabyte/shadcn-svelte` + +Before writing any shadcn-svelte component code, check the docs for the exact example. If unsure, look it up. + +--- + +## Core Principles + +shadcn-svelte is **not a component library** — it's a code distribution system. Components are cloned into the project via CLI, giving full ownership. Built on: +- **Bits UI** (headless accessible primitives) for ARIA + keyboard navigation +- **Tailwind CSS v4** with OKLCH color space +- **tailwind-variants** (`tv()`) for variant/size styling +- **Svelte 5 runes** (`$state`, `$derived`, `$props()`, `$bindable`, `{#snippet}`) + +--- + +## Import Patterns — EXACT + +### Single Components (named imports) + +```svelte + +``` + +### Compositional Components (namespace imports) + +```svelte + +``` + +**Rule**: Compositional components (Card, Dialog, DropdownMenu, Form, Tooltip, Select, Sidebar, Sheet, etc.) **always** use `import * as X`. Single components (Button, Input, Badge, Progress, etc.) **always** use `import { X }`. + +--- + +## Button Usage — EXACT + +```svelte +Default +Outline +Secondary +Ghost +Destructive +Link +Small +Large + + + +Dashboard +Rounded +``` + +**Variants**: `default`, `outline`, `secondary`, `ghost`, `destructive`, `link` +**Sizes**: `xs`, `sm`, `default`, `lg`, `icon`, `icon-xs`, `icon-sm`, `icon-lg` + +**Icons inside buttons need no margin** — spacing is automatic based on button size. + +**For non-button elements styled as buttons**, use `buttonVariants()`: + +```svelte + + +Link as button +``` + +--- + +## Card Usage — EXACT + +```svelte + + + Login + Enter your email below + + + + + + Login + + +``` + +**Parts**: `Card.Root`, `Card.Header`, `Card.Content`, `Card.Footer`, `Card.Title`, `Card.Description` + +--- + +## Dialog Usage — EXACT + +```svelte + + + Open Dialog + + + + Edit profile + Make changes here. + + + + + + Cancel + Save + + + +``` + +**Parts**: `Dialog.Root`, `Dialog.Trigger`, `Dialog.Content`, `Dialog.Header`, `Dialog.Footer`, `Dialog.Title`, `Dialog.Description`, `Dialog.Close`, `Dialog.CloseIcon` + +**Critical**: `Dialog.Trigger` and `Dialog.Close` use `buttonVariants()` — not `` directly. + +--- + +## Dropdown Menu Usage — EXACT + +```svelte + + + {#snippet child({ props })} + Open + {/snippet} + + + My Account + + Profile + Billing + + + Log out + + +``` + +**Critical**: `DropdownMenu.Trigger` uses `{#snippet child({ props })}` — **not** `{#snippet children({ props })}`. This is the exact pattern from the docs. + +--- + +## Form Usage — EXACT + +Forms use **Formsnap** + **sveltekit-superforms** + **Zod**. + +```svelte + + + + + + {#snippet children({ props })} + Username + + {/snippet} + + This is your public display name. + + + Submit + +``` + +**Structure**: `Form.Field` → `Form.Control` → `{#snippet children({ props })}` → input with `{...props}` and `bind:value` +**Validation**: Always use `Form.FieldErrors` for error display +**Helper text**: Always use `Form.Description` for helper text + +--- + +## Tooltip Usage — EXACT + +```svelte + + + + + + Hover + + + Tooltip text + + + +``` + +**Parts**: `Tooltip.Provider`, `Tooltip.Root`, `Tooltip.Trigger`, `Tooltip.Content`, `Tooltip.Arrow` + +--- + +## Select Usage — EXACT + +```svelte + + + + + {value} + + + Option 1 + Option 2 + + +``` + +**Parts**: `Select.Root` (with `type="single"` and `bind:value`), `Select.Trigger`, `Select.Content`, `Select.Item`, `Select.Group`, `Select.Label`, `Select.Separator`, `Select.Arrow` + +--- + +## Theming — EXACT + +All colors controlled via **CSS custom properties** in global CSS. Convention is `--name` for background and `--name-foreground` for text color. + +```css +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); +} +``` + +**Adding custom colors**: + +```css +:root { + --warning: oklch(0.84 0.16 84); + --warning-foreground: oklch(0.28 0.07 46); +} + +@theme inline { + --color-warning: var(--warning); + --color-warning-foreground: var(--warning-foreground); +} +``` + +Then use: `` + +--- + +## Dark Mode — EXACT + +Use `mode-watcher` package: + +```svelte + + +{@render children?.()} +``` + +Toggle button: + +```svelte + + + + + +``` + +Or dropdown selector with `setMode("light")`, `setMode("dark")`, `resetMode()`. + +--- + +## Class Merging — EXACT + +Always use the `cn()` utility from `$lib/utils.ts`: + +```ts +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} +``` + +--- + +## Icons — EXACT + +Always use `@lucide/svelte`: + +```svelte + +``` + +--- + +## What NOT to Do + +1. **Do NOT** manually copy component source — use CLI (`npx shadcn-svelte add`) +2. **Do NOT** try to override component styles via props — edit component source directly +3. **Do NOT** use `import Card from` for compositional components — always `import * as Card` +4. **Do NOT** use `` inside `Dialog.Trigger` — use `buttonVariants()` +5. **Do NOT** add margin classes to icons inside buttons — spacing is automatic +6. **Do NOT** use media queries for dark mode — use `.dark` class + `mode-watcher` +7. **Do NOT** change component class names directly — change CSS variables +8. **Do NOT** invent your own component patterns — follow the docs exactly + +--- + +## Enforcement + +When writing any shadcn-svelte component: +1. Check the docs for the exact example +2. Copy the pattern verbatim +3. Adapt only the content (text, values, bindings) +4. Never change the structure, props, or component hierarchy from the docs diff --git a/.claude/rules/smart-css.md b/.claude/rules/smart-css.md new file mode 100644 index 00000000..c5349e10 --- /dev/null +++ b/.claude/rules/smart-css.md @@ -0,0 +1,93 @@ +--- +description: Use smart CSS to reduce code while preserving existing styles. Verify changes with Playwright MCP. +glob: "*.css" +--- + +# Smart CSS Optimization + +Reduce CSS code by consolidating redundant rules, using modern features, and leveraging Tailwind utility patterns — without breaking existing visual styles. + +## Principles + +1. **Preserve visual output** — never change the rendered appearance +2. **Reduce duplication** — merge identical rules, extract shared patterns +3. **Use modern CSS** — nesting, `:has()`, `color-mix()`, `oklch()`, custom properties +4. **Leverage Tailwind** — replace custom CSS with Tailwind utilities where equivalent +5. **Verify with Playwright** — visually confirm no regressions after changes + +## Optimization Techniques + +### Consolidate duplicate selectors + +```css +/* BEFORE */ +.btn-primary { padding: 0.5rem 1rem; border-radius: 0.375rem; } +.btn-secondary { padding: 0.5rem 1rem; border-radius: 0.375rem; } + +/* AFTER */ +.btn { padding: 0.5rem 1rem; border-radius: 0.375rem; } +``` + +### Use CSS nesting + +```css +/* BEFORE */ +.card {} +.card .title {} +.card .description {} + +/* AFTER */ +.card { + .title {} + .description {} +} +``` + +### Replace with Tailwind utilities + +```css +/* BEFORE */ +.center-flex { + display: flex; + align-items: center; + justify-content: center; +} + +/* AFTER — use class="flex items-center justify-center" directly */ +``` + +### Use CSS custom properties for repetition + +```css +/* BEFORE */ +.header { --spacing: 1rem; padding: var(--spacing); } +.footer { --spacing: 1rem; padding: var(--spacing); } + +/* AFTER */ +:root { --spacing: 1rem; } +``` + +## Verify with Playwright MCP + +After every CSS change, verify visual correctness using Playwright MCP: + +1. Navigate to the affected page +2. Take a snapshot or screenshot +3. Compare against the expected appearance +4. If styles are broken, revert the change immediately + +``` +# Example verification steps +1. playwright_browser_navigate → affected page URL +2. playwright_browser_snapshot → capture current state +3. playwright_browser_take_screenshot → visual reference +4. Confirm no style regressions +``` + +## What NOT to Do + +1. **Do NOT** remove CSS that affects layout or spacing +2. **Do NOT** change colors or font sizes +3. **Do NOT** remove responsive breakpoints +4. **Do NOT** remove dark mode overrides +5. **Do NOT** optimize CSS you don't fully understand — verify first diff --git a/.claude/rules/track-plan-progress.md b/.claude/rules/track-plan-progress.md new file mode 100644 index 00000000..a11b6347 --- /dev/null +++ b/.claude/rules/track-plan-progress.md @@ -0,0 +1,42 @@ +--- +description: Update PLAN.md after completing any task by marking the finished step as DONE. +glob: "*" +--- + +# Track Plan Progress + +After completing any meaningful piece of work, update `PLAN.md` to reflect the change. + +## Rules + +1. After finishing a task, open `PLAN.md` and mark the completed step as **DONE**. +2. If a new task or discovery emerges during work, add it to the plan before starting. +3. Never leave `PLAN.md` stale — it is the single source of truth for what's done and what's next. +4. When a phase is fully complete, mark the entire phase as **DONE**. + +## Format + +Use the Status column in tables or inline markers: + +```markdown +| Task | Status | Detail | +|---|---|---| +| Create unified worker | DONE | Created chithi.worker.ts | +| Rewrite WASM bindings | TODO Phase 1 | Replace wasm-bindgen with C ABI | +``` + +Or for step lists: + +```markdown +#### Step 1.1: Create unified worker +- [x] Merge crypto + rust workers into chithi.worker.ts +- [ ] Add main-thread fallback +``` + +## When to Update + +Update `PLAN.md` immediately after: +- Finishing a code change +- Completing a refactoring +- Discovering a new requirement or blocker +- Changing the implementation approach diff --git a/.claude/rules/wasmtime-performance-rust.md b/.claude/rules/wasmtime-performance-rust.md new file mode 100644 index 00000000..01827669 --- /dev/null +++ b/.claude/rules/wasmtime-performance-rust.md @@ -0,0 +1,300 @@ +--- +description: Optimize all Rust code for wasmtime JIT performance when compiling to wasm32-unknown-unknown. +glob: "*.rs" +--- + +# Rust for Wasmtime Performance + +All Rust code compiled to `wasm32-unknown-unknown` must be optimized for wasmtime's JIT execution model. Wasmtime uses Cranelift to compile WASM to native machine code — write Rust that maximizes this pipeline. + +## Target + +```toml +[lib] +crate-type = ["cdylib"] + +[dependencies] +# NO wasm-bindgen for exports +# NO PyO3 +# Pure C ABI only +``` + +--- + +## C ABI Exports — EXACT + +All public exports use `#[no_mangle] pub extern "C"` with raw pointers and lengths. No Rust string/Vec crossing the boundary. + +```rust +#[no_mangle] +pub extern "C" fn encrypt_chunk( + data_ptr: *const u8, + data_len: u32, + key_ptr: *const u8, + key_len: u32, + nonce_ptr: *const u8, + nonce_len: u32, + out_ptr: *mut u8, + out_len: u32, +) -> u32 { + let data = unsafe { std::slice::from_raw_parts(data_ptr, data_len as usize) }; + let key = unsafe { std::slice::from_raw_parts(key_ptr, key_len as usize) }; + let nonce = unsafe { std::slice::from_raw_parts(nonce_ptr, nonce_len as usize) }; + let out = unsafe { std::slice::from_raw_parts_mut(out_ptr, out_len as usize) }; + + let written = do_encrypt(data, key, nonce, out); + written as u32 +} +``` + +**Rules**: +- All buffer I/O: `(*const u8, len: u32)` for input, `(*mut u8, len: u32)` for output +- Return `u32` for byte counts or status codes (0 = success, non-zero = error) +- Never return `String`, `Vec`, or `JsValue` across the boundary +- Caller pre-allocates output buffer, callee writes into it + +--- + +## Linear Memory Management + +WASM has a single flat linear memory. Manage it explicitly: + +```rust +static ALLOCATOR: std::sync::Mutex = std::sync::Mutex::new(SimpleAllocator::new()); + +#[no_mangle] +pub extern "C" fn alloc(len: u32) -> u32 { + let mut allocator = ALLOCATOR.lock().unwrap(); + allocator.allocate(len as usize) as u32 +} + +#[no_mangle] +pub extern "C" fn dealloc(ptr: u32, len: u32) { + let mut allocator = ALLOCATOR.lock().unwrap(); + allocator.deallocate(ptr as usize, len as usize) +} +``` + +**Rules**: +- Export `alloc` and `dealloc` for host-side memory management +- Use a bump allocator or static pool — no `std::alloc` in WASM target +- Caller is responsible for freeing allocated buffers +- Keep allocations small and short-lived to minimize memory growth + +--- + +## Minimize WASM-Boundary Crossings + +Each call across the WASM boundary has overhead (type conversion, trap checking, memory sync). Reduce crossings: + +```rust +// GOOD: One call processes entire batch +#[no_mangle] +pub extern "C" fn encrypt_all( + data_ptr: *const u8, + data_len: u32, + key_ptr: *const u8, + key_len: u32, + nonce_ptr: *const u8, + nonce_len: u32, + out_ptr: *mut u8, + out_len: u32, +) -> u32 { + // Internal chunking — host sees one call +} + +// BAD: Host calls encrypt_chunk in a loop +#[no_mangle] +pub extern "C" fn encrypt_chunk(/* ... */) -> u32 { + // Called N times by host for N chunks +} +``` + +**Rules**: +- Provide bulk operations (`encrypt_all`, `decrypt_all`) alongside per-chunk operations +- Batch internal work — minimize the number of exported function calls +- Keep hot paths inside WASM — don't round-trip to host for intermediate results + +--- + +## Optimize for Cranelift JIT + +Wasmtime's Cranelift compiler generates native code from WASM. Write code that Cranelift can optimize well: + +### Prefer tight loops + +```rust +// GOOD: Cranelift can vectorize +let mut output = [0u8; 1024]; +for i in 0..input.len() { + output[i] = input[i] ^ key[i % key.len()]; +} + +// BAD: Indirection prevents vectorization +let results: Vec = input.iter() + .map(|&b| transform(b, key)) + .collect(); +``` + +### Avoid dynamic dispatch in hot paths + +```rust +// GOOD: Monomorphized, inlinable +fn encrypt_aes_gcm(data: &[u8], key: &[u8], nonce: &[u8]) -> [u8; 16] { + aes_gcm_encrypt(data, key, nonce) +} + +// BAD: vtable call — Cranelift cannot inline +trait Cipher { + fn encrypt(&self, data: &[u8]) -> Vec; +} +``` + +### Keep stack usage low + +WASM has a limited stack (typically 5MB). Avoid large stack allocations: + +```rust +// GOOD: Heap-allocated via WASM linear memory +fn process_large_buffer(data_ptr: *const u8, len: u32) { + let data = unsafe { std::slice::from_raw_parts(data_ptr, len as usize) }; + // Process in place or use output buffer +} + +// BAD: Large stack allocation +fn process_large_buffer(data: &[u8]) { + let mut buffer = [0u8; 65536]; // 64KB on stack +} +``` + +--- + +## Parallelism Inside WASM + +Use Rayon for parallel chunk processing within WASM: + +```rust +use rayon::prelude::*; + +#[no_mangle] +pub extern "C" fn encrypt_chunks_parallel( + chunks_ptr: *const Chunk, + chunk_count: u32, + key_ptr: *const u8, + key_len: u32, + results_ptr: *mut ChunkResult, +) -> u32 { + let chunks = unsafe { std::slice::from_raw_parts(chunks_ptr, chunk_count as usize) }; + let key = unsafe { std::slice::from_raw_parts(key_ptr, key_len as usize) }; + let results = unsafe { std::slice::from_raw_parts_mut(results_ptr, chunk_count as usize) }; + + chunks.par_iter().enumerate().for_each(|(i, chunk)| { + let encrypted = encrypt_single_chunk(chunk.data(), key); + results[i] = ChunkResult::new(encrypted); + }); + + 0 // success +} +``` + +**Rules**: +- Use `par_iter()` for independent chunk operations +- Parallelism is most effective for large data sets (>1MB) +- Keep per-chunk work significant enough to justify thread overhead + +--- + +## Memory Layout Optimization + +WASM linear memory is contiguous. Optimize data layout: + +```rust +// GOOD: Compact, cache-friendly +#[repr(C)] +pub struct ChunkInfo { + offset: u32, + length: u32, +} + +// GOOD: Array of structs for sequential access +let chunks: [ChunkInfo; N] = /* ... */; + +// BAD: Padding and indirection +pub struct ChunkInfo { + name: String, // heap pointer + data: Vec, // heap pointer + metadata: Option>, // double heap pointer +} +``` + +**Rules**: +- Use `#[repr(C)]` on all structs that cross the FFI boundary +- Prefer arrays of structs (AoS) for sequential access +- Avoid heap allocations in tight loops — use output buffers +- Keep struct sizes small and aligned + +--- + +## Release Build Optimizations + +```toml +[profile.release] +opt-level = 3 # Aggressive optimization +lto = true # Link-time optimization +codegen-units = 1 # Single codegen unit for LTO +panic = "abort" # Smaller binary, no unwind tables +strip = "symbols" # Remove debug symbols +``` + +**Rules**: +- Always build with `--release` for production WASM +- LTO is critical for cross-crate inlining +- `panic = "abort"` reduces binary size significantly +- Test with debug builds, ship with release + +--- + +## Measure, Don't Guess + +Profile WASM execution with wasmtime: + +```bash +# Build +cargo build --target wasm32-unknown-unknown --release + +# Profile with wasmtime +wasmtime profile target/wasm32-unknown-unknown/release/chithi_wasm.wasm + +# Check binary size +wasm-opt --metrics chithi_wasm.wasm +``` + +**Rules**: +- Measure actual execution time, not Rust benchmark time +- Monitor WASM binary size — smaller = faster JIT compile +- Profile the critical path: key derivation → encryption → upload +- Track linear memory growth — unexpected growth indicates leaks + +--- + +## What NOT to Do + +1. **Do NOT** use `wasm-bindgen` for exported functions — pure C ABI only +2. **Do NOT** return `String` or `Vec` from exported functions +3. **Do NOT** use `println!` or `eprintln!` — no stdout in WASM +4. **Do NOT** allocate large buffers on the stack +5. **Do NOT** use `std::thread` — use Rayon for parallelism +6. **Do NOT** use `std::fs` or `std::net` — not available in `wasm32-unknown-unknown` +7. **Do NOT** use `lazy_static` or `once_cell` with non-`const` initializers — WASM has no dynamic init +8. **Do NOT** rely on system entropy (`/dev/urandom`) — use `getrandom` crate with `js` or WASI backend + +--- + +## Enforcement + +When writing Rust for the WASM target: +1. Use `#[no_mangle] pub extern "C"` for all exports +2. All buffer I/O via `(*const u8, len: u32)` pattern +3. Prefer bulk operations over per-item calls +4. Optimize for Cranelift: tight loops, no dynamic dispatch, low stack +5. Profile with wasmtime, not `criterion` diff --git a/.claude/scheduled_tasks.lock b/.claude/scheduled_tasks.lock new file mode 100644 index 00000000..f5b3a912 --- /dev/null +++ b/.claude/scheduled_tasks.lock @@ -0,0 +1 @@ +{"sessionId":"ae8e6001-63fe-46db-8e95-8a7b1893cd96","pid":34292,"acquiredAt":1784478433794} \ No newline at end of file diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..259a9591 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "playwright": { + "command": "npx", + "args": ["@playwright/mcp@latest"] + } + } +} diff --git a/.claude/skills/tailwind-4-docs/SKILL.md b/.claude/skills/tailwind-4-docs/SKILL.md new file mode 100644 index 00000000..46ffb188 --- /dev/null +++ b/.claude/skills/tailwind-4-docs/SKILL.md @@ -0,0 +1,77 @@ +--- +name: tailwind-4-docs +description: Comprehensive Tailwind CSS v4 documentation snapshot and workflow guidance. Use when answering Tailwind v4 questions, selecting utilities/variants, configuring Tailwind v4, or migrating projects from v3 to v4 with official docs and gotcha checks. +compatibility: Requires git, Python 3, and internet access to initialize the Tailwind docs snapshot from tailwindcss.com. +--- + +# Tailwind 4 Docs + +## Overview + +Use this skill to navigate a locally synced Tailwind CSS v4 documentation snapshot and answer development, configuration, migration, implementation, refactor, and review questions with official guidance. + +The docs snapshot is not bundled with this skill because the upstream repository is source-available but not open-source. Users must initialize the snapshot themselves and are responsible for complying with the upstream license. + +## Quick start + +1. Check whether the docs snapshot is initialized (`references/docs/` and `references/docs-index.tsx` exist). +2. If the snapshot is missing or older than one week, stop and ask to run the initialization step in "Initialization" before continuing. Do not answer the user's question until the snapshot is initialized. +3. Identify the topic (utility, variant, config, migration, compatibility, implementation, refactor, review). +4. Find the matching doc in `references/docs-index.tsx`. +5. Load only the relevant file from `references/docs/`. +6. For implementation, refactor, or review tasks, also load `references/engineering-playbook.md`. +7. Apply guidance and call out any breaking changes or constraints. + +## Initialization (required once per install) + +Run the sync script to download the Tailwind docs locally. This requires network access, git, and Python 3: + +``` +python skills/tailwind-4-docs/scripts/sync_tailwind_docs.py --accept-docs-license +``` + +This pulls content from `tailwindlabs/tailwindcss.com`. That repo is source-available and explicitly not open-source, so the user must accept its license before downloading and keep the snapshot local. + +If you cannot run tools or have no internet access, ask the user to run the exact command above in a terminal, then continue once `references/docs/` and `references/docs-index.tsx` exist. + +If the snapshot is missing or older than one week, you must ask for permission to run the command or ask the user to run it. Do not proceed with Tailwind guidance until the snapshot is initialized or refreshed. + +If initialization is blocked (no internet or no write access), use `references/gotchas.md` as a limited fallback and ask the user to consult the official docs. For implementation, refactor, or review tasks, `references/engineering-playbook.md` can also serve as a limited fallback. + +## References map + +- `references/docs/` is generated locally and contains the Tailwind v4 MDX docs snapshot. +- `references/docs-index.tsx` is generated locally and contains the category and slug map used by the docs sidebar. +- `references/docs-source.txt` captures the upstream repo, commit, and snapshot date (or reports that initialization is pending). +- `references/engineering-playbook.md` is the agent-oriented implementation, refactor, and review guide. +- `references/gotchas.md` provides a quick scan of common v4 migration pitfalls. + +## MDX handling + +- Treat `export const title` and `export const description` as metadata. +- Read JSX callouts like `` or `` as guidance text. + +## Common entry points + +- Migration: `references/docs/upgrade-guide.mdx`, `references/docs/compatibility.mdx`. +- Implementation/refactor/review: `references/engineering-playbook.md`. +- Gotchas overview: `references/gotchas.md`. +- Configuration and directives: `references/docs/functions-and-directives.mdx`, `references/docs/adding-custom-styles.mdx`, `references/docs/theme.mdx`. +- Variants and responsive patterns: `references/docs/hover-focus-and-other-states.mdx`, `references/docs/responsive-design.mdx`. +- Core behavior: `references/docs/preflight.mdx`, `references/docs/detecting-classes-in-source-files.mdx`. + +## Migration checklist + +When upgrading from v3 to v4, always confirm the following in the docs: + +- Browser support and compatibility expectations. +- Tooling changes: `@tailwindcss/postcss`, `@tailwindcss/cli`, `@tailwindcss/vite`. +- Import syntax: `@import "tailwindcss"` replaces `@tailwind` directives. +- Utility renames/removals, prefix format, and important modifier placement. +- Changes to variants, transforms, and arbitrary value syntax. + +## Update workflow + +Run `scripts/sync_tailwind_docs.py` to refresh the snapshot. Use `--local-repo` if you already have a local clone of `tailwindlabs/tailwindcss.com` to speed up syncs. Always pass `--accept-docs-license`. + +--- diff --git a/.claude/skills/tailwind-4-docs/references/docs-source.txt b/.claude/skills/tailwind-4-docs/references/docs-source.txt new file mode 100644 index 00000000..59892da8 --- /dev/null +++ b/.claude/skills/tailwind-4-docs/references/docs-source.txt @@ -0,0 +1,6 @@ +Status: Not initialized +Source: https://github.com/tailwindlabs/tailwindcss.com +Docs-Path: src/docs +Index-Path: src/app/(docs)/docs/index.tsx +Snapshot-Date: (none) +Note: Run skills/tailwind-4-docs/scripts/sync_tailwind_docs.py --accept-docs-license to initialize. diff --git a/.claude/skills/tailwind-4-docs/references/engineering-playbook.md b/.claude/skills/tailwind-4-docs/references/engineering-playbook.md new file mode 100644 index 00000000..cfe5eb61 --- /dev/null +++ b/.claude/skills/tailwind-4-docs/references/engineering-playbook.md @@ -0,0 +1,312 @@ +# Tailwind Engineering Playbook + +Use this reference for implementation, refactor, and review tasks where you need practical engineering judgment in addition to the official Tailwind docs. Its purpose is to help you make good architectural decisions quickly when you are writing, reviewing or refactoring Tailwind code. + +## Default workflow + +1. Inspect the repo first. +2. Find the Tailwind entrypoint CSS and any split files. +3. Identify existing theme tokens, breakpoints, component classes, custom utilities, and formatting conventions. +4. Prefer using the project's existing design language over inventing a new one. +5. Keep the implementation as close to markup as possible. +6. Only add new abstraction when repetition or lack of a design primitive actually justifies it. + +## Core mindset + +- The default move is to compose UI in markup with utilities. +- Custom CSS is still valuable for tokens, utilities, component classes, rich text, and third-party markup. + +## The abstraction ladder + +Use this order by default: + +1. Compose with existing utilities in markup. +2. If markup repeats, extract the markup into the project's native reusable abstraction, such as a component, partial, include, or template. +3. If repeated values are missing from the system, add tokens with `@theme`. +4. If a repeated low-level behavior is missing, add a custom utility with `@utility`. +5. If a stable named visual primitive is justified, add a small component class in `@layer components`. +6. Use `@apply` only as a narrow adapter, not as the main architecture. + +## What good reuse looks like + +The first level of reuse is the Tailwind design system itself: + +- spacing scale +- color system +- type scale +- radius scale +- shadow scale +- breakpoint scale +- container behavior + +The second level of reuse is markup reuse: + +- shared cards +- buttons +- hero sections +- CTA blocks +- pagination items +- list items +- nav items + +The third level of reuse is CSS abstraction: + +- tokens +- custom utilities +- custom variants +- small component classes + +## Tokens first + +Create a token when a value is part of the design language and should be reusable: + +- brand colors +- semantic surface or text colors +- typography scale +- font families +- radii +- shadows +- spacing decisions that should be global +- container widths +- breakpoints + +Do not create tokens for one-off values too early. + +Use an arbitrary value first when the value: + +- is a real exception +- appears only once +- is unlikely to become part of the system + +Promote it into a token when: + +- it appears repeatedly +- it has product meaning +- design wants it governed centrally +- a later redesign should update all usages together + +Prefer semantic token names where semantics matter, especially for colors: + +- `--color-primary` +- `--color-surface-muted` +- `--color-danger` + +Use `@theme` when the token should generate utilities or variants. +Use `:root` only for regular CSS variables that are not supposed to create utility classes. + +## Arbitrary values + +Use them for: + +- one-off alignment or layout tuning +- design details that are not system-level +- third-party or generated markup constraints + +Do not let repeated arbitrary values accumulate. If the same value shows up several times, it is usually time for a token or a custom utility. + +## Custom utilities + +Use `@utility` when you need a low-level reusable behavior that Tailwind does not already provide. + +Good candidates: + +- a project container helper +- a focus-ring preset +- a custom text wrap helper +- a low-level layout helper +- a transition preset + +A custom utility should still feel like a utility: + +- one job +- low level +- composable +- not semantic + +## Component classes + +Create component classes only for stable, intentional APIs: + +- `btn` +- `card` +- `badge` +- `field-input` +- `callout` +- `rich-text` + +They are also appropriate when you need to style markup you don't control: + +- CMS-rendered content +- third-party widgets +- generated framework markup + +Do not create component classes just to hide utilities from templates. + +Good component classes are: + +- small +- stable +- easy to override +- tied to real repeated primitives + +Bad component classes are: + +- page-specific +- giant +- bundles of unrelated concerns +- substitutes for component extraction + +## Variant strategy for component classes + +When a component has likely variants, keep the base class neutral where possible. + +Example of good separation: + +- base class owns layout, spacing, and shared behavior +- separate variant classes own tone or intent +- size variants are separate classes + +This avoids a common problem where the base component class hardcodes colors or state behavior, then every exception has to fight the CSS. + +If you create a shared component abstraction, it should own its shared behavior consistently: + +- hover transitions +- focus treatment +- icon motion +- spacing between label and icon +- disabled or active states + +Do not leave half the behavior in the abstraction and the other half duplicated ad hoc in templates. + +## `@apply` + +Use `@apply` sparingly. + +Good uses: + +- styling third-party classes you don't control +- adapting Tailwind styles to generated markup +- tiny repeated patterns where markup extraction would be worse + +Bad uses: + +- hiding all utilities in CSS +- creating giant semantic wrappers that are harder to reason about than the original markup + +If you are using `@apply` heavily to shorten templates, step back and reconsider the abstraction ladder. + +## Custom variants + +Use `@custom-variant` when a selector pattern is truly repeated and deserves to become a styling primitive. + +Good candidates: + +- app theme wrappers +- data-attribute driven states +- repeated container context selectors +- CMS-specific context wrappers (e.g. dark page section, highlighted block) + +Do not create custom variants for one-off selector tricks unless the repetition is real. + +## Rich text and uncontrolled markup + +Rich text is a special case because you usually do not control the inner HTML. + +Use one of these approaches: + +- a scoped wrapper such as `.rich-text` +- the official Typography plugin + +Do not globally style every `h1`, `p`, `ul`, or `table` in the whole app just to fix one content region. + +Scope the styling to the content container. + +## Generated DOM and JS-replaced markup + +When styling icons or widgets that are transformed by JavaScript, target the rendered DOM, not only the placeholder markup. + +Examples: + +- icon libraries that replace `` with `` +- component libraries that inject wrappers +- widgets that rewrite class names or structure + +If a shared interaction depends on a specific child element, verify that the final rendered DOM still matches the selector. + +This matters a lot for: + +- transitions +- hover effects +- focus styles +- icon animation +- nested selectors in component classes + +## Responsive strategy + +Prefer mobile-first styling: + +- define the base case first +- add larger breakpoint changes progressively + +Use breakpoint tokens intentionally. In v4, `--breakpoint-*` theme variables define which responsive variants exist. + +Do not blindly use every default breakpoint if the project intentionally removed or replaced some of them. + +## File organization + +For a CSS-first Tailwind v4 setup, this structure is usually sensible: + +- entrypoint CSS (`app.css` or `site.css`) +- `theme.css` for tokens +- `base.css` for minimal element defaults +- `utilities.css` for low-level project utilities and variants +- `components.css` for a small stable component API + - Bigger components, e.g. `rich-text.css` for scoped uncontrolled HTML + +## Refactor heuristics + +When refactoring an existing Tailwind codebase: + +1. Remove dead CSS before adding new CSS. +2. Remove redundant utilities before extracting abstractions. +3. Normalize obvious repeated primitives first. +4. Keep the number of component classes intentionally small. +5. Push page structure back into markup if CSS started owning too much layout. +6. Merge inconsistent implementations of the same affordance. + +Watch for these smells: + +- several versions of the same button or card +- repeated arbitrary shadows, radii, or spacing +- identical hover interactions implemented in different ways +- state logic split between a shared class and page-specific one-offs +- component classes that are really page fragments in disguise + +## Review checklist + +Before finalizing a Tailwind change, check: + +- Are utilities sufficient here, or did I abstract too early? +- If repetition exists, would extracting markup be better than adding CSS? +- Should repeated values become tokens? +- Are custom utilities truly low-level? +- Are component classes small, stable, and override-friendly? +- Are state and motion rules consistent across similar UI? +- Will Tailwind detect every class I used? +- Are dynamic classes mapped to full strings? +- Is uncontrolled markup scoped instead of styled globally? +- Is the responsive behavior mobile-first and intentional? +- Can any CSS be deleted now? + +## Practical defaults + +When in doubt: + +- keep styling in markup first +- reuse markup before reusing CSS +- use tokens before arbitrary repetition +- use custom utilities before semantic CSS wrappers +- keep component classes few and durable +- keep state behavior inside the shared abstraction if the abstraction exists +- verify that rendered DOM matches your selectors +- prefer deleting complexity over introducing a clever abstraction diff --git a/.claude/skills/tailwind-4-docs/references/gotchas.md b/.claude/skills/tailwind-4-docs/references/gotchas.md new file mode 100644 index 00000000..5a958769 --- /dev/null +++ b/.claude/skills/tailwind-4-docs/references/gotchas.md @@ -0,0 +1,22 @@ +# Tailwind CSS v4 gotchas (quick scan) + +- Browser support is modern-only: Safari 16.4+, Chrome 111+, Firefox 128+. +- PostCSS plugin moved to `@tailwindcss/postcss`. +- CLI moved to `@tailwindcss/cli`. +- Vite plugin `@tailwindcss/vite` is recommended. +- Import Tailwind with `@import "tailwindcss";` (no `@tailwind` directives). +- Prefix syntax is `@import "tailwindcss" prefix(tw);` and classes use `tw:` at the start. +- Important modifier goes at the end: `bg-red-500!`. +- Utility renames and removals: see `references/docs/upgrade-guide.mdx` for the full list. +- Default border and ring color now use `currentColor`; ring width default is 1px. +- `space-*` and `divide-*` selectors changed; use flex/grid with `gap` if layouts break. +- Custom utilities should use `@utility` instead of `@layer utilities` or `@layer components`. +- `@theme` is for design tokens that should create utilities or variants; use `:root` only for plain CSS variables that should not generate Tailwind APIs. +- `@theme` variables must be top-level, not nested under selectors or media queries. +- Stacked variants apply left-to-right (reverse order from v3). +- Arbitrary CSS variable syntax is `bg-(--brand-color)` (not `bg-[--brand-color]`). +- Transform reset uses `scale-none`, `rotate-none`, `translate-none` (not `transform-none`). +- `hover:` now only applies on devices that support hover; override if needed. +- Tailwind scans source files as plain text, so dynamically concatenated class fragments are not detected. +- Use `@source` for external or unusual source locations, and `@source inline()` only when safelisting is truly necessary. +- CSS modules and component `
Tooltip text