{"owner":"tonhowtf","repo":"omniget","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md","claude.md"],"skills":{"AGENTS.md":"# OmniGet\n\nDesktop download manager built with Tauri 2.0 (Rust backend) + SvelteKit (frontend). Modern design principles (2025-2026): clarity over features, immediate feedback, and universal accessibility. Monorepo: `src-tauri/` is Rust, `src/` is SvelteKit + TypeScript. Run `cargo tauri dev` to start.\n\n## Commands\n```bash\npnpm install          # install frontend deps\npnpm dev              # SvelteKit dev server only\ncargo tauri dev       # full app (Rust + frontend)\ncargo check           # typecheck Rust without building\npnpm check            # svelte-check + tsc\n```\n\n## Tech Stack\n\n- **Backend:** Rust, Tauri 2.x, tokio, reqwest, serde, sqlx (SQLite), chromiumoxide\n- **Frontend:** SvelteKit 2, Svelte 5 (runes: `$state`, `$derived`, `$effect`, `$props`), TypeScript strict\n- **Styling:** Scoped CSS with CSS custom properties. NO Tailwind. NO CSS-in-JS.\n- **Icons:** `@tabler/icons-svelte` — import individually: `import IconDownload from \"@tabler/icons-svelte/IconDownload.svelte\"`\n- **Fonts:** System fonts as default; IBM Plex Mono for code and technical content only\n- **i18n:** `sveltekit-i18n` with JSON locale files in `i18n/{lang}/`\n- **Bundler:** Vite 5, adapter-static, pnpm as package manager\n\n## Project Layout\nsrc-tauri/src/\ncommands/       # Tauri IPC commands (invoked from frontend)\nplatforms/      # Download platform implementations (trait-based plugin system)\ntraits.rs     # PlatformDownloader trait — all platforms implement this\nhotmart/      # Hotmart-specific: auth, api, parser, downloader\ncore/           # Shared engine: registry, hls_downloader, media_processor, queue\nmodels/         # Data structs: media.rs, download.rs, settings.rs\nstorage/        # Persistence: config, database (SQLite), cache\nsrc/\nroutes/         # SvelteKit file-based routing (+page.svelte, +layout.svelte)\ncomponents/     # Reusable UI, organized by domain (see Component Organization)\nlib/            # Shared logic: stores/, i18n/\n\n## Design System\n\n### Core Principles\n\n**Minimalism with Purpose**: Every element has a function. No decoration.\n\n**Immediate Feedback**: Progress bars show percent + bytes + speed. Errors are explicit (\"Missing API key\" not \"Error\").\n\n**Color as Guidance**: Accent for primary actions, error for destructive. Never communicate through color alone (pair with icon + text).\n\n**Accessibility First**: WCAG 2.2 AA baseline. 4.5:1 text contrast, 3:1 focus indicators, all elements keyboard accessible.\n\n### Color Architecture\n\nCSS custom properties exclusively. NEVER hardcode colors. Theme via `[data-theme=\"dark\"]` / `[data-theme=\"light\"]`. Every token defined in both `:root` and `[data-theme=\"dark\"]`.\n\n**Tokens** (see app.css for values): `--primary`, `--secondary`, `--tertiary`, `--accent`, `--success`, `--error`, `--warning`, `--button`, `--button-hover`, `--button-press`, `--button-text`, `--button-stroke`, `--button-elevated`, `--sidebar-bg`, `--sidebar-highlight`, `--content-border`, `--input-border`, `--input-bg`, `--popup-bg`, `--dialog-backdrop`.\n\n**Contrast tokens** for text on colored backgrounds: `--on-primary`, `--on-accent`, `--on-success`, `--on-error`, `--on-button`, `--on-button-elevated`.\n\n**Layout constants**: `--padding: 12px`, `--border-radius: 11px`, `--sidebar-width: 80px`.\n\n### Typography\n\nSystem fonts for UI, monospace for code only:\n- `--font-system: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif`\n- `--font-mono: \"IBM Plex Mono\", \"Courier New\", monospace`\n\nHeadings: `font-weight: 500`, `margin-block: 0`. Scale: h1=24px, h2=20px, h3=16px, h4=14.5px, h5=12px, h6=11px.\n\nBody: buttons=14.5px/500, .label=13px/500, .body=14px/400/1.6, .subtext=12.5px/500, .caption=11.5px/400, code=13px/mono.\n\n### Spacing & Radius\n\nBase unit: `var(--padding)` (12px). Derive: `calc(var(--padding) / 2)` (6px), `calc(var(--padding) * 2)` (24px). Border radius: `var(--border-radius)` (11px).\n\n## Component Patterns\n\n### File Organization\ncomponents/\nbuttons/       # ActionButton, SettingsButton, SettingsToggle, Switcher\ndialog/        # DialogContainer, DialogButton, SmallDialog, PickerDialog\nhints/         # Contextual hints UI\nhotmart/       # Hotmart-specific components\nicons/         # Custom SVG icon components (only when tabler doesn't have it)\nmascot/        # Loop mascot animations\nmisc/          # Toggle, Skeleton, Placeholder, SectionHeading, OuterLink\nomnibox/       # OmniboxInput, MediaPreview, FormatSelector, QualityPicker, DownloadModeSelector\nonboarding/    # First-run onboarding flow\nservices/      # Platform service components\nsettings/      # SettingsCategory, SettingsDropdown, SettingsInput\ntoast/         # Toast notification components\n\n### Button System\n\nClasses: `.button` (default), `.button.elevated`, `.button.active`, `.button.active.color`. Hover via `@media (hover: hover)`. Focus: `outline: var(--focus-ring)` on `:focus-visible` only.\n\n### Custom Controls\n\n- **Toggle**: `<input type=\"checkbox\" role=\"switch\" aria-checked>` with CSS transform animation. RTL-aware via `:dir(rtl)`.\n- **Select**: Native `<select>` overlaid on styled button (position: absolute, transparent). Native provides a11y, button provides visuals.\n- **Input**: Always pair with `<label>` + `aria-describedby` for helper text.\n\n### Switcher\n\nJoined button row. Middle children get `border-radius: 0`. Negative margin eliminates double borders. `.big` variant with container bg.\n\n### Settings Components\n\n- **SettingsToggle**: label + description + Toggle. Generic TS for type-safe setting context/id.\n- **SettingsDropdown**: native select overlay pattern.\n- **SettingsCategory**: section with `id` for hash-linking + focus highlight animation.\n\nAll call `updateSetting({ [context]: { [id]: value } })`.\n\n### Dialog System\n\nNative `<dialog>` + `showModal()`. ARIA: `role=\"dialog\"`, `aria-modal=\"true\"`, `aria-labelledby`. First element gets `autofocus`. ESC/backdrop closes. 150ms entry/exit animation.\n\n### Progress Bar\n\nOuter: `--button-elevated`, 6px, rounded. Inner: status-colored (`--blue` downloading, `--green` complete, `--red` error, `--gray` paused), width=percent, `transition: width 0.3s`. Indeterminate: Skeleton shimmer. Always determinate for downloads: `45% • 234 MB / 512 MB • 2.5 MB/s`.\n\nTO-DO: Add ETA display (`~3m 20s` via `(total - downloaded) / speed`).\n\n### Processing Queue Items\n\nDiscriminated union: `waiting | running | done | error`. Icons: spinner, check, X, exclamation. File type map: `{ video: IconMovie, audio: IconMusic, image: IconPhoto, file: IconFile }`.\n\n## Layout Architecture\n\n### Grid System\n\nTwo-column CSS Grid: sidebar + content. Content scrolls, sidebar fixed. Shadow border via `--content-border`.\n\n### Mobile Breakpoint: 535px\n\nGrid flips to rows. Sidebar becomes fixed bottom tab bar (horizontal scroll). Do NOT force dark theme on mobile.\n\n### Sub-Navigation\n\nTwo-column: 250px nav + content (max 600px). Collapses at 750px to single column with back button.\n\n### Safe Area\n\n`env(safe-area-inset-top/bottom/left)` on all fixed/sticky elements.\n\n## Download UX Principles\n\n### Phase Indicators\n\nShow download phase explicitly (never \"is it hung?\"):\n1. \"Preparing\" — Item enqueued, building config\n2. \"Fetching Info\" — Running yt-dlp info extraction (only if cache miss)\n3. \"Starting\" — Download engine initializing (via -1.0 sentinel)\n4. \"Connecting\" — yt-dlp spawned, awaiting first response (via -2.0 sentinel)\n5. \"Downloading\" — Data flowing (percent > 0%)\n\nTO-DO: Add explicit \"Merging\" phase when yt-dlp emits `[Merger]` line (currently shows ~99% with no phase label).\n\n### Determinate Progress\n\nALWAYS determinate bars: `234 MB / 512 MB (45%) • 2.5 MB/s`. ETA = (total - downloaded) / speed. Smooth speed with rolling average.\n\nTO-DO: ETA display not yet implemented. Speed rolling average not yet implemented (raw yt-dlp speed used directly).\n\n### Optimistic UI\n\nPause/Resume/Remove: update UI instantly, send command in background, revert on failure.\n\n### Error Feedback\n\nNever vague. Always: what happened + why + what to do.\n- ❌ \"Error\" → ✅ \"HTTP 429 - Retrying in 15s...\"\n- ❌ \"Loading...\" → ✅ \"Cannot access cookies - Continuing without auth\"\n\nToast for transient (5s auto-dismiss). Inline for persistent.\n\n### Completion Feedback\n\nColor → --success, icon → checkmark, haptic → hapticConfirm(), toast → \"Downloaded: file.mp4 (123 MB)\" + \"Open folder\".\n\nTO-DO: \"Open folder\" button not yet implemented on completed items (i18n key `open_folder` exists but is unused).\n\n### Rate Limiting (429)\n\nDetect → show \"Retrying in 10s (2/3)\" → rotate player_client/cookies → after 3: detailed error + manual retry button.\n\nTO-DO: Retry status (\"Retrying in 10s (2/3)\") is only logged server-side via `tracing::warn!`, not shown to the user. Need to emit retry events to frontend.\n\n### Batch Operations\n\nAggregate: \"3 Downloading, 2 Paused\". Sidebar badge (red circle). Peek panel: top 3 + total.\n\n## State Management\n\n### Settings Store\n\nPartial-merge: store holds user-changed values only. Derived store merges with defaults via `ts-deepmerge`.\n\n### Schema Versioning\n\n`schemaVersion: number` + migration functions keyed by version. Increment version + add migrator when changing settings.\n\n### Theme System\n\n`auto | light | dark`. Applied as `data-theme` on wrapper div (not html/body).\n\n### Other Stores\n\nAll stores in `$lib/stores/`: `download-store` (queue state), `download-listener` (Tauri event listeners), `media-preview-store` (URL preview), `toast-store`, `settings-store`, `update-store`, `convert-store`, `changelog-store`, `onboarding-store`, `clipboard-monitor`.\n\n## Accessibility\n\nWCAG 2.2 AA compliance required.\n\n### ARIA Rules\n\nSemantic HTML first. `role=\"dialog\"` + `aria-modal`. `aria-label`/`aria-labelledby` for unlabeled buttons. `role=\"switch\"` for toggles. `aria-live=\"polite\"` for dynamic updates. `aria-valuenow/min/max` for progress. No `role=\"button\"` on `<div>`.\n\n### Preferences\n\n`[data-reduce-motion=\"true\"]` → disable all animation. `[data-reduce-transparency=\"true\"]` → solid backdrops. Settings: `reduceMotion`, `reduceTransparency`, `disableHaptics`.\n\n### Contrast\n\nText: 4.5:1. Focus: 3:1 + min 6px area. Never color alone. Use `--on-*` tokens. Test in forced-colors mode.\n\n### Haptics\n\n`hapticSwitch()`, `hapticConfirm()`, `hapticError()`. Gated by `device.supports.haptics && !disableHaptics`.\n\n### Focus & Keyboard\n\nTab order: natural DOM. `:focus-visible` only. Focus ring: 2px --accent, offset 2px. ESC closes dialogs. Enter/Space activates buttons. Arrow keys navigate lists.\n\n### RTL\n\n`:dir(rtl)` for overrides. Logical properties preferred.\n\n## Loop Mascot\n\nExpressive creature reacting to download state (idle → downloading → paused → complete → error). Pure visual feedback — never required for understanding. IP protected.\n\n## i18n\n\nLazy per route. `i18n/{lang}/{namespace}.json`. `$t(\"namespace.key\")`. Default: `en`. All visible strings via `$t()`.\n\n## Coding Rules\n\n### Svelte & TypeScript\n\nSvelte 5 runes only (`$state`, `$derived`, `$effect`, `$props`). TypeScript strict. Discriminated unions for state.\n\n### Styling\n\nScoped `<style>`. `$components/`, `$lib/`, `$i18n/` aliases. `@media (hover: hover)` + `:active` fallback. `pointer-events: none` on images/SVGs. Scrollbar hidden. No `!important` except a11y. Safe area insets.\n\n### Feedback\n\nImmediate UI on click (optimistic). Progress: phase + percent + bytes + speed. Errors: explicit + actionable. Tooltips: 5-10 words + shortcut.\n\n### Focus & Animations\n\n`:focus-visible` ring (2px --accent, offset 2px). `prefers-reduced-motion` alternative. Dialog: autofocus first element, trap, ESC closes.\n\n### Downloads\n\nDeterminate progress with ARIA. Phase icon + text (no color-alone). Error → detail + Retry button. Batch → sidebar badge + aggregate.\n","claude.md":"# OmniGet\n\nDesktop download manager built with Tauri 2.0 (Rust backend) + SvelteKit (frontend). Modern design principles (2025-2026): clarity over features, immediate feedback, and universal accessibility. Monorepo: `src-tauri/` is Rust, `src/` is SvelteKit + TypeScript. Run `cargo tauri dev` to start.\n\n## Commands\n```bash\npnpm install          # install frontend deps\npnpm dev              # SvelteKit dev server only\ncargo tauri dev       # full app (Rust + frontend)\ncargo check           # typecheck Rust without building\npnpm check            # svelte-check + tsc\n```\n\n## Tech Stack\n\n- **Backend:** Rust, Tauri 2.x, tokio, reqwest, serde, sqlx (SQLite), chromiumoxide\n- **Frontend:** SvelteKit 2, Svelte 5 (runes: `$state`, `$derived`, `$effect`, `$props`), TypeScript strict\n- **Styling:** Scoped CSS with CSS custom properties. NO Tailwind. NO CSS-in-JS.\n- **Icons:** `@tabler/icons-svelte` — import individually: `import IconDownload from \"@tabler/icons-svelte/IconDownload.svelte\"`\n- **Fonts:** System fonts as default; IBM Plex Mono for code and technical content only\n- **i18n:** `sveltekit-i18n` with JSON locale files in `i18n/{lang}/`\n- **Bundler:** Vite 5, adapter-static, pnpm as package manager\n\n## Project Layout\nsrc-tauri/src/\ncommands/       # Tauri IPC commands (invoked from frontend)\nplatforms/      # Download platform implementations (trait-based plugin system)\ntraits.rs     # PlatformDownloader trait — all platforms implement this\nhotmart/      # Hotmart-specific: auth, api, parser, downloader\ncore/           # Shared engine: registry, hls_downloader, media_processor, queue\nmodels/         # Data structs: media.rs, download.rs, settings.rs\nstorage/        # Persistence: config, database (SQLite), cache\nsrc/\nroutes/         # SvelteKit file-based routing (+page.svelte, +layout.svelte)\ncomponents/     # Reusable UI, organized by domain (see Component Organization)\nlib/            # Shared logic: stores/, i18n/\n\n## Design System\n\n### Core Principles\n\n**Minimalism with Purpose**: Every element has a function. No decoration.\n\n**Immediate Feedback**: Progress bars show percent + bytes + speed. Errors are explicit (\"Missing API key\" not \"Error\").\n\n**Color as Guidance**: Accent for primary actions, error for destructive. Never communicate through color alone (pair with icon + text).\n\n**Accessibility First**: WCAG 2.2 AA baseline. 4.5:1 text contrast, 3:1 focus indicators, all elements keyboard accessible.\n\n### Color Architecture\n\nCSS custom properties exclusively. NEVER hardcode colors. Theme via `[data-theme=\"dark\"]` / `[data-theme=\"light\"]`. Every token defined in both `:root` and `[data-theme=\"dark\"]`.\n\n**Tokens** (see app.css for values): `--primary`, `--secondary`, `--tertiary`, `--accent`, `--success`, `--error`, `--warning`, `--button`, `--button-hover`, `--button-press`, `--button-text`, `--button-stroke`, `--button-elevated`, `--sidebar-bg`, `--sidebar-highlight`, `--content-border`, `--input-border`, `--input-bg`, `--popup-bg`, `--dialog-backdrop`.\n\n**Contrast tokens** for text on colored backgrounds: `--on-primary`, `--on-accent`, `--on-success`, `--on-error`, `--on-button`, `--on-button-elevated`.\n\n**Layout constants**: `--padding: 12px`, `--border-radius: 11px`, `--sidebar-width: 80px`.\n\n### Typography\n\nSystem fonts for UI, monospace for code only:\n- `--font-system: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif`\n- `--font-mono: \"IBM Plex Mono\", \"Courier New\", monospace`\n\nHeadings: `font-weight: 500`, `margin-block: 0`. Scale: h1=24px, h2=20px, h3=16px, h4=14.5px, h5=12px, h6=11px.\n\nBody: buttons=14.5px/500, .label=13px/500, .body=14px/400/1.6, .subtext=12.5px/500, .caption=11.5px/400, code=13px/mono.\n\n### Spacing & Radius\n\nBase unit: `var(--padding)` (12px). Derive: `calc(var(--padding) / 2)` (6px), `calc(var(--padding) * 2)` (24px). Border radius: `var(--border-radius)` (11px).\n\n## Component Patterns\n\n### File Organization\ncomponents/\nbuttons/       # ActionButton, SettingsButton, SettingsToggle, Switcher\ndialog/        # DialogContainer, DialogButton, SmallDialog, PickerDialog\nhints/         # Contextual hints UI\nhotmart/       # Hotmart-specific components\nicons/         # Custom SVG icon components (only when tabler doesn't have it)\nmascot/        # Loop mascot animations\nmisc/          # Toggle, Skeleton, Placeholder, SectionHeading, OuterLink\nomnibox/       # OmniboxInput, MediaPreview, FormatSelector, QualityPicker, DownloadModeSelector\nonboarding/    # First-run onboarding flow\nservices/      # Platform service components\nsettings/      # SettingsCategory, SettingsDropdown, SettingsInput\ntoast/         # Toast notification components\n\n### Button System\n\nClasses: `.button` (default), `.button.elevated`, `.button.active`, `.button.active.color`. Hover via `@media (hover: hover)`. Focus: `outline: var(--focus-ring)` on `:focus-visible` only.\n\n### Custom Controls\n\n- **Toggle**: `<input type=\"checkbox\" role=\"switch\" aria-checked>` with CSS transform animation. RTL-aware via `:dir(rtl)`.\n- **Select**: Native `<select>` overlaid on styled button (position: absolute, transparent). Native provides a11y, button provides visuals.\n- **Input**: Always pair with `<label>` + `aria-describedby` for helper text.\n\n### Switcher\n\nJoined button row. Middle children get `border-radius: 0`. Negative margin eliminates double borders. `.big` variant with container bg.\n\n### Settings Components\n\n- **SettingsToggle**: label + description + Toggle. Generic TS for type-safe setting context/id.\n- **SettingsDropdown**: native select overlay pattern.\n- **SettingsCategory**: section with `id` for hash-linking + focus highlight animation.\n\nAll call `updateSetting({ [context]: { [id]: value } })`.\n\n### Dialog System\n\nNative `<dialog>` + `showModal()`. ARIA: `role=\"dialog\"`, `aria-modal=\"true\"`, `aria-labelledby`. First element gets `autofocus`. ESC/backdrop closes. 150ms entry/exit animation.\n\n### Progress Bar\n\nOuter: `--button-elevated`, 6px, rounded. Inner: status-colored (`--blue` downloading, `--green` complete, `--red` error, `--gray` paused), width=percent, `transition: width 0.3s`. Indeterminate: Skeleton shimmer. Always determinate for downloads: `45% • 234 MB / 512 MB • 2.5 MB/s`.\n\nTO-DO: Add ETA display (`~3m 20s` via `(total - downloaded) / speed`).\n\n### Processing Queue Items\n\nDiscriminated union: `waiting | running | done | error`. Icons: spinner, check, X, exclamation. File type map: `{ video: IconMovie, audio: IconMusic, image: IconPhoto, file: IconFile }`.\n\n## Layout Architecture\n\n### Grid System\n\nTwo-column CSS Grid: sidebar + content. Content scrolls, sidebar fixed. Shadow border via `--content-border`.\n\n### Mobile Breakpoint: 535px\n\nGrid flips to rows. Sidebar becomes fixed bottom tab bar (horizontal scroll). Do NOT force dark theme on mobile.\n\n### Sub-Navigation\n\nTwo-column: 250px nav + content (max 600px). Collapses at 750px to single column with back button.\n\n### Safe Area\n\n`env(safe-area-inset-top/bottom/left)` on all fixed/sticky elements.\n\n## Download UX Principles\n\n### Phase Indicators\n\nShow download phase explicitly (never \"is it hung?\"):\n1. \"Preparing\" — Item enqueued, building config\n2. \"Fetching Info\" — Running yt-dlp info extraction (only if cache miss)\n3. \"Starting\" — Download engine initializing (via -1.0 sentinel)\n4. \"Connecting\" — yt-dlp spawned, awaiting first response (via -2.0 sentinel)\n5. \"Downloading\" — Data flowing (percent > 0%)\n\nTO-DO: Add explicit \"Merging\" phase when yt-dlp emits `[Merger]` line (currently shows ~99% with no phase label).\n\n### Determinate Progress\n\nALWAYS determinate bars: `234 MB / 512 MB (45%) • 2.5 MB/s`. ETA = (total - downloaded) / speed. Smooth speed with rolling average.\n\nTO-DO: ETA display not yet implemented. Speed rolling average not yet implemented (raw yt-dlp speed used directly).\n\n### Optimistic UI\n\nPause/Resume/Remove: update UI instantly, send command in background, revert on failure.\n\n### Error Feedback\n\nNever vague. Always: what happened + why + what to do.\n- ❌ \"Error\" → ✅ \"HTTP 429 - Retrying in 15s...\"\n- ❌ \"Loading...\" → ✅ \"Cannot access cookies - Continuing without auth\"\n\nToast for transient (5s auto-dismiss). Inline for persistent.\n\n### Completion Feedback\n\nColor → --success, icon → checkmark, haptic → hapticConfirm(), toast → \"Downloaded: file.mp4 (123 MB)\" + \"Open folder\".\n\nTO-DO: \"Open folder\" button not yet implemented on completed items (i18n key `open_folder` exists but is unused).\n\n### Rate Limiting (429)\n\nDetect → show \"Retrying in 10s (2/3)\" → rotate player_client/cookies → after 3: detailed error + manual retry button.\n\nTO-DO: Retry status (\"Retrying in 10s (2/3)\") is only logged server-side via `tracing::warn!`, not shown to the user. Need to emit retry events to frontend.\n\n### Batch Operations\n\nAggregate: \"3 Downloading, 2 Paused\". Sidebar badge (red circle). Peek panel: top 3 + total.\n\n## State Management\n\n### Settings Store\n\nPartial-merge: store holds user-changed values only. Derived store merges with defaults via `ts-deepmerge`.\n\n### Schema Versioning\n\n`schemaVersion: number` + migration functions keyed by version. Increment version + add migrator when changing settings.\n\n### Theme System\n\n`auto | light | dark`. Applied as `data-theme` on wrapper div (not html/body).\n\n### Other Stores\n\nAll stores in `$lib/stores/`: `download-store` (queue state), `download-listener` (Tauri event listeners), `media-preview-store` (URL preview), `toast-store`, `settings-store`, `update-store`, `convert-store`, `changelog-store`, `onboarding-store`, `clipboard-monitor`.\n\n## Accessibility\n\nWCAG 2.2 AA compliance required.\n\n### ARIA Rules\n\nSemantic HTML first. `role=\"dialog\"` + `aria-modal`. `aria-label`/`aria-labelledby` for unlabeled buttons. `role=\"switch\"` for toggles. `aria-live=\"polite\"` for dynamic updates. `aria-valuenow/min/max` for progress. No `role=\"button\"` on `<div>`.\n\n### Preferences\n\n`[data-reduce-motion=\"true\"]` → disable all animation. `[data-reduce-transparency=\"true\"]` → solid backdrops. Settings: `reduceMotion`, `reduceTransparency`, `disableHaptics`.\n\n### Contrast\n\nText: 4.5:1. Focus: 3:1 + min 6px area. Never color alone. Use `--on-*` tokens. Test in forced-colors mode.\n\n### Haptics\n\n`hapticSwitch()`, `hapticConfirm()`, `hapticError()`. Gated by `device.supports.haptics && !disableHaptics`.\n\n### Focus & Keyboard\n\nTab order: natural DOM. `:focus-visible` only. Focus ring: 2px --accent, offset 2px. ESC closes dialogs. Enter/Space activates buttons. Arrow keys navigate lists.\n\n### RTL\n\n`:dir(rtl)` for overrides. Logical properties preferred.\n\n## Loop Mascot\n\nExpressive creature reacting to download state (idle → downloading → paused → complete → error). Pure visual feedback — never required for understanding. IP protected.\n\n## i18n\n\nLazy per route. `i18n/{lang}/{namespace}.json`. `$t(\"namespace.key\")`. Default: `en`. All visible strings via `$t()`.\n\n## Coding Rules\n\n### Svelte & TypeScript\n\nSvelte 5 runes only (`$state`, `$derived`, `$effect`, `$props`). TypeScript strict. Discriminated unions for state.\n\n### Styling\n\nScoped `<style>`. `$components/`, `$lib/`, `$i18n/` aliases. `@media (hover: hover)` + `:active` fallback. `pointer-events: none` on images/SVGs. Scrollbar hidden. No `!important` except a11y. Safe area insets.\n\n### Feedback\n\nImmediate UI on click (optimistic). Progress: phase + percent + bytes + speed. Errors: explicit + actionable. Tooltips: 5-10 words + shortcut.\n\n### Focus & Animations\n\n`:focus-visible` ring (2px --accent, offset 2px). `prefers-reduced-motion` alternative. Dialog: autofocus first element, trap, ESC closes.\n\n### Downloads\n\nDeterminate progress with ARIA. Phase icon + text (no color-alone). Error → detail + Retry button. Batch → sidebar badge + aggregate.\n"},"files":{"AGENTS.md":"# OmniGet\n\nDesktop download manager built with Tauri 2.0 (Rust backend) + SvelteKit (frontend). Modern design principles (2025-2026): clarity over features, immediate feedback, and universal accessibility. Monorepo: `src-tauri/` is Rust, `src/` is SvelteKit + TypeScript. Run `cargo tauri dev` to start.\n\n## Commands\n```bash\npnpm install          # install frontend deps\npnpm dev              # SvelteKit dev server only\ncargo tauri dev       # full app (Rust + frontend)\ncargo check           # typecheck Rust without building\npnpm check            # svelte-check + tsc\n```\n\n## Tech Stack\n\n- **Backend:** Rust, Tauri 2.x, tokio, reqwest, serde, sqlx (SQLite), chromiumoxide\n- **Frontend:** SvelteKit 2, Svelte 5 (runes: `$state`, `$derived`, `$effect`, `$props`), TypeScript strict\n- **Styling:** Scoped CSS with CSS custom properties. NO Tailwind. NO CSS-in-JS.\n- **Icons:** `@tabler/icons-svelte` — import individually: `import IconDownload from \"@tabler/icons-svelte/IconDownload.svelte\"`\n- **Fonts:** System fonts as default; IBM Plex Mono for code and technical content only\n- **i18n:** `sveltekit-i18n` with JSON locale files in `i18n/{lang}/`\n- **Bundler:** Vite 5, adapter-static, pnpm as package manager\n\n## Project Layout\nsrc-tauri/src/\ncommands/       # Tauri IPC commands (invoked from frontend)\nplatforms/      # Download platform implementations (trait-based plugin system)\ntraits.rs     # PlatformDownloader trait — all platforms implement this\nhotmart/      # Hotmart-specific: auth, api, parser, downloader\ncore/           # Shared engine: registry, hls_downloader, media_processor, queue\nmodels/         # Data structs: media.rs, download.rs, settings.rs\nstorage/        # Persistence: config, database (SQLite), cache\nsrc/\nroutes/         # SvelteKit file-based routing (+page.svelte, +layout.svelte)\ncomponents/     # Reusable UI, organized by domain (see Component Organization)\nlib/            # Shared logic: stores/, i18n/\n\n## Design System\n\n### Core Principles\n\n**Minimalism with Purpose**: Every element has a function. No decoration.\n\n**Immediate Feedback**: Progress bars show percent + bytes + speed. Errors are explicit (\"Missing API key\" not \"Error\").\n\n**Color as Guidance**: Accent for primary actions, error for destructive. Never communicate through color alone (pair with icon + text).\n\n**Accessibility First**: WCAG 2.2 AA baseline. 4.5:1 text contrast, 3:1 focus indicators, all elements keyboard accessible.\n\n### Color Architecture\n\nCSS custom properties exclusively. NEVER hardcode colors. Theme via `[data-theme=\"dark\"]` / `[data-theme=\"light\"]`. Every token defined in both `:root` and `[data-theme=\"dark\"]`.\n\n**Tokens** (see app.css for values): `--primary`, `--secondary`, `--tertiary`, `--accent`, `--success`, `--error`, `--warning`, `--button`, `--button-hover`, `--button-press`, `--button-text`, `--button-stroke`, `--button-elevated`, `--sidebar-bg`, `--sidebar-highlight`, `--content-border`, `--input-border`, `--input-bg`, `--popup-bg`, `--dialog-backdrop`.\n\n**Contrast tokens** for text on colored backgrounds: `--on-primary`, `--on-accent`, `--on-success`, `--on-error`, `--on-button`, `--on-button-elevated`.\n\n**Layout constants**: `--padding: 12px`, `--border-radius: 11px`, `--sidebar-width: 80px`.\n\n### Typography\n\nSystem fonts for UI, monospace for code only:\n- `--font-system: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif`\n- `--font-mono: \"IBM Plex Mono\", \"Courier New\", monospace`\n\nHeadings: `font-weight: 500`, `margin-block: 0`. Scale: h1=24px, h2=20px, h3=16px, h4=14.5px, h5=12px, h6=11px.\n\nBody: buttons=14.5px/500, .label=13px/500, .body=14px/400/1.6, .subtext=12.5px/500, .caption=11.5px/400, code=13px/mono.\n\n### Spacing & Radius\n\nBase unit: `var(--padding)` (12px). Derive: `calc(var(--padding) / 2)` (6px), `calc(var(--padding) * 2)` (24px). Border radius: `var(--border-radius)` (11px).\n\n## Component Patterns\n\n### File Organization\ncomponents/\nbuttons/       # ActionButton, SettingsButton, SettingsToggle, Switcher\ndialog/        # DialogContainer, DialogButton, SmallDialog, PickerDialog\nhints/         # Contextual hints UI\nhotmart/       # Hotmart-specific components\nicons/         # Custom SVG icon components (only when tabler doesn't have it)\nmascot/        # Loop mascot animations\nmisc/          # Toggle, Skeleton, Placeholder, SectionHeading, OuterLink\nomnibox/       # OmniboxInput, MediaPreview, FormatSelector, QualityPicker, DownloadModeSelector\nonboarding/    # First-run onboarding flow\nservices/      # Platform service components\nsettings/      # SettingsCategory, SettingsDropdown, SettingsInput\ntoast/         # Toast notification components\n\n### Button System\n\nClasses: `.button` (default), `.button.elevated`, `.button.active`, `.button.active.color`. Hover via `@media (hover: hover)`. Focus: `outline: var(--focus-ring)` on `:focus-visible` only.\n\n### Custom Controls\n\n- **Toggle**: `<input type=\"checkbox\" role=\"switch\" aria-checked>` with CSS transform animation. RTL-aware via `:dir(rtl)`.\n- **Select**: Native `<select>` overlaid on styled button (position: absolute, transparent). Native provides a11y, button provides visuals.\n- **Input**: Always pair with `<label>` + `aria-describedby` for helper text.\n\n### Switcher\n\nJoined button row. Middle children get `border-radius: 0`. Negative margin eliminates double borders. `.big` variant with container bg.\n\n### Settings Components\n\n- **SettingsToggle**: label + description + Toggle. Generic TS for type-safe setting context/id.\n- **SettingsDropdown**: native select overlay pattern.\n- **SettingsCategory**: section with `id` for hash-linking + focus highlight animation.\n\nAll call `updateSetting({ [context]: { [id]: value } })`.\n\n### Dialog System\n\nNative `<dialog>` + `showModal()`. ARIA: `role=\"dialog\"`, `aria-modal=\"true\"`, `aria-labelledby`. First element gets `autofocus`. ESC/backdrop closes. 150ms entry/exit animation.\n\n### Progress Bar\n\nOuter: `--button-elevated`, 6px, rounded. Inner: status-colored (`--blue` downloading, `--green` complete, `--red` error, `--gray` paused), width=percent, `transition: width 0.3s`. Indeterminate: Skeleton shimmer. Always determinate for downloads: `45% • 234 MB / 512 MB • 2.5 MB/s`.\n\nTO-DO: Add ETA display (`~3m 20s` via `(total - downloaded) / speed`).\n\n### Processing Queue Items\n\nDiscriminated union: `waiting | running | done | error`. Icons: spinner, check, X, exclamation. File type map: `{ video: IconMovie, audio: IconMusic, image: IconPhoto, file: IconFile }`.\n\n## Layout Architecture\n\n### Grid System\n\nTwo-column CSS Grid: sidebar + content. Content scrolls, sidebar fixed. Shadow border via `--content-border`.\n\n### Mobile Breakpoint: 535px\n\nGrid flips to rows. Sidebar becomes fixed bottom tab bar (horizontal scroll). Do NOT force dark theme on mobile.\n\n### Sub-Navigation\n\nTwo-column: 250px nav + content (max 600px). Collapses at 750px to single column with back button.\n\n### Safe Area\n\n`env(safe-area-inset-top/bottom/left)` on all fixed/sticky elements.\n\n## Download UX Principles\n\n### Phase Indicators\n\nShow download phase explicitly (never \"is it hung?\"):\n1. \"Preparing\" — Item enqueued, building config\n2. \"Fetching Info\" — Running yt-dlp info extraction (only if cache miss)\n3. \"Starting\" — Download engine initializing (via -1.0 sentinel)\n4. \"Connecting\" — yt-dlp spawned, awaiting first response (via -2.0 sentinel)\n5. \"Downloading\" — Data flowing (percent > 0%)\n\nTO-DO: Add explicit \"Merging\" phase when yt-dlp emits `[Merger]` line (currently shows ~99% with no phase label).\n\n### Determinate Progress\n\nALWAYS determinate bars: `234 MB / 512 MB (45%) • 2.5 MB/s`. ETA = (total - downloaded) / speed. Smooth speed with rolling average.\n\nTO-DO: ETA display not yet implemented. Speed rolling average not yet implemented (raw yt-dlp speed used directly).\n\n### Optimistic UI\n\nPause/Resume/Remove: update UI instantly, send command in background, revert on failure.\n\n### Error Feedback\n\nNever vague. Always: what happened + why + what to do.\n- ❌ \"Error\" → ✅ \"HTTP 429 - Retrying in 15s...\"\n- ❌ \"Loading...\" → ✅ \"Cannot access cookies - Continuing without auth\"\n\nToast for transient (5s auto-dismiss). Inline for persistent.\n\n### Completion Feedback\n\nColor → --success, icon → checkmark, haptic → hapticConfirm(), toast → \"Downloaded: file.mp4 (123 MB)\" + \"Open folder\".\n\nTO-DO: \"Open folder\" button not yet implemented on completed items (i18n key `open_folder` exists but is unused).\n\n### Rate Limiting (429)\n\nDetect → show \"Retrying in 10s (2/3)\" → rotate player_client/cookies → after 3: detailed error + manual retry button.\n\nTO-DO: Retry status (\"Retrying in 10s (2/3)\") is only logged server-side via `tracing::warn!`, not shown to the user. Need to emit retry events to frontend.\n\n### Batch Operations\n\nAggregate: \"3 Downloading, 2 Paused\". Sidebar badge (red circle). Peek panel: top 3 + total.\n\n## State Management\n\n### Settings Store\n\nPartial-merge: store holds user-changed values only. Derived store merges with defaults via `ts-deepmerge`.\n\n### Schema Versioning\n\n`schemaVersion: number` + migration functions keyed by version. Increment version + add migrator when changing settings.\n\n### Theme System\n\n`auto | light | dark`. Applied as `data-theme` on wrapper div (not html/body).\n\n### Other Stores\n\nAll stores in `$lib/stores/`: `download-store` (queue state), `download-listener` (Tauri event listeners), `media-preview-store` (URL preview), `toast-store`, `settings-store`, `update-store`, `convert-store`, `changelog-store`, `onboarding-store`, `clipboard-monitor`.\n\n## Accessibility\n\nWCAG 2.2 AA compliance required.\n\n### ARIA Rules\n\nSemantic HTML first. `role=\"dialog\"` + `aria-modal`. `aria-label`/`aria-labelledby` for unlabeled buttons. `role=\"switch\"` for toggles. `aria-live=\"polite\"` for dynamic updates. `aria-valuenow/min/max` for progress. No `role=\"button\"` on `<div>`.\n\n### Preferences\n\n`[data-reduce-motion=\"true\"]` → disable all animation. `[data-reduce-transparency=\"true\"]` → solid backdrops. Settings: `reduceMotion`, `reduceTransparency`, `disableHaptics`.\n\n### Contrast\n\nText: 4.5:1. Focus: 3:1 + min 6px area. Never color alone. Use `--on-*` tokens. Test in forced-colors mode.\n\n### Haptics\n\n`hapticSwitch()`, `hapticConfirm()`, `hapticError()`. Gated by `device.supports.haptics && !disableHaptics`.\n\n### Focus & Keyboard\n\nTab order: natural DOM. `:focus-visible` only. Focus ring: 2px --accent, offset 2px. ESC closes dialogs. Enter/Space activates buttons. Arrow keys navigate lists.\n\n### RTL\n\n`:dir(rtl)` for overrides. Logical properties preferred.\n\n## Loop Mascot\n\nExpressive creature reacting to download state (idle → downloading → paused → complete → error). Pure visual feedback — never required for understanding. IP protected.\n\n## i18n\n\nLazy per route. `i18n/{lang}/{namespace}.json`. `$t(\"namespace.key\")`. Default: `en`. All visible strings via `$t()`.\n\n## Coding Rules\n\n### Svelte & TypeScript\n\nSvelte 5 runes only (`$state`, `$derived`, `$effect`, `$props`). TypeScript strict. Discriminated unions for state.\n\n### Styling\n\nScoped `<style>`. `$components/`, `$lib/`, `$i18n/` aliases. `@media (hover: hover)` + `:active` fallback. `pointer-events: none` on images/SVGs. Scrollbar hidden. No `!important` except a11y. Safe area insets.\n\n### Feedback\n\nImmediate UI on click (optimistic). Progress: phase + percent + bytes + speed. Errors: explicit + actionable. Tooltips: 5-10 words + shortcut.\n\n### Focus & Animations\n\n`:focus-visible` ring (2px --accent, offset 2px). `prefers-reduced-motion` alternative. Dialog: autofocus first element, trap, ESC closes.\n\n### Downloads\n\nDeterminate progress with ARIA. Phase icon + text (no color-alone). Error → detail + Retry button. Batch → sidebar badge + aggregate.\n","claude.md":"# OmniGet\n\nDesktop download manager built with Tauri 2.0 (Rust backend) + SvelteKit (frontend). Modern design principles (2025-2026): clarity over features, immediate feedback, and universal accessibility. Monorepo: `src-tauri/` is Rust, `src/` is SvelteKit + TypeScript. Run `cargo tauri dev` to start.\n\n## Commands\n```bash\npnpm install          # install frontend deps\npnpm dev              # SvelteKit dev server only\ncargo tauri dev       # full app (Rust + frontend)\ncargo check           # typecheck Rust without building\npnpm check            # svelte-check + tsc\n```\n\n## Tech Stack\n\n- **Backend:** Rust, Tauri 2.x, tokio, reqwest, serde, sqlx (SQLite), chromiumoxide\n- **Frontend:** SvelteKit 2, Svelte 5 (runes: `$state`, `$derived`, `$effect`, `$props`), TypeScript strict\n- **Styling:** Scoped CSS with CSS custom properties. NO Tailwind. NO CSS-in-JS.\n- **Icons:** `@tabler/icons-svelte` — import individually: `import IconDownload from \"@tabler/icons-svelte/IconDownload.svelte\"`\n- **Fonts:** System fonts as default; IBM Plex Mono for code and technical content only\n- **i18n:** `sveltekit-i18n` with JSON locale files in `i18n/{lang}/`\n- **Bundler:** Vite 5, adapter-static, pnpm as package manager\n\n## Project Layout\nsrc-tauri/src/\ncommands/       # Tauri IPC commands (invoked from frontend)\nplatforms/      # Download platform implementations (trait-based plugin system)\ntraits.rs     # PlatformDownloader trait — all platforms implement this\nhotmart/      # Hotmart-specific: auth, api, parser, downloader\ncore/           # Shared engine: registry, hls_downloader, media_processor, queue\nmodels/         # Data structs: media.rs, download.rs, settings.rs\nstorage/        # Persistence: config, database (SQLite), cache\nsrc/\nroutes/         # SvelteKit file-based routing (+page.svelte, +layout.svelte)\ncomponents/     # Reusable UI, organized by domain (see Component Organization)\nlib/            # Shared logic: stores/, i18n/\n\n## Design System\n\n### Core Principles\n\n**Minimalism with Purpose**: Every element has a function. No decoration.\n\n**Immediate Feedback**: Progress bars show percent + bytes + speed. Errors are explicit (\"Missing API key\" not \"Error\").\n\n**Color as Guidance**: Accent for primary actions, error for destructive. Never communicate through color alone (pair with icon + text).\n\n**Accessibility First**: WCAG 2.2 AA baseline. 4.5:1 text contrast, 3:1 focus indicators, all elements keyboard accessible.\n\n### Color Architecture\n\nCSS custom properties exclusively. NEVER hardcode colors. Theme via `[data-theme=\"dark\"]` / `[data-theme=\"light\"]`. Every token defined in both `:root` and `[data-theme=\"dark\"]`.\n\n**Tokens** (see app.css for values): `--primary`, `--secondary`, `--tertiary`, `--accent`, `--success`, `--error`, `--warning`, `--button`, `--button-hover`, `--button-press`, `--button-text`, `--button-stroke`, `--button-elevated`, `--sidebar-bg`, `--sidebar-highlight`, `--content-border`, `--input-border`, `--input-bg`, `--popup-bg`, `--dialog-backdrop`.\n\n**Contrast tokens** for text on colored backgrounds: `--on-primary`, `--on-accent`, `--on-success`, `--on-error`, `--on-button`, `--on-button-elevated`.\n\n**Layout constants**: `--padding: 12px`, `--border-radius: 11px`, `--sidebar-width: 80px`.\n\n### Typography\n\nSystem fonts for UI, monospace for code only:\n- `--font-system: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif`\n- `--font-mono: \"IBM Plex Mono\", \"Courier New\", monospace`\n\nHeadings: `font-weight: 500`, `margin-block: 0`. Scale: h1=24px, h2=20px, h3=16px, h4=14.5px, h5=12px, h6=11px.\n\nBody: buttons=14.5px/500, .label=13px/500, .body=14px/400/1.6, .subtext=12.5px/500, .caption=11.5px/400, code=13px/mono.\n\n### Spacing & Radius\n\nBase unit: `var(--padding)` (12px). Derive: `calc(var(--padding) / 2)` (6px), `calc(var(--padding) * 2)` (24px). Border radius: `var(--border-radius)` (11px).\n\n## Component Patterns\n\n### File Organization\ncomponents/\nbuttons/       # ActionButton, SettingsButton, SettingsToggle, Switcher\ndialog/        # DialogContainer, DialogButton, SmallDialog, PickerDialog\nhints/         # Contextual hints UI\nhotmart/       # Hotmart-specific components\nicons/         # Custom SVG icon components (only when tabler doesn't have it)\nmascot/        # Loop mascot animations\nmisc/          # Toggle, Skeleton, Placeholder, SectionHeading, OuterLink\nomnibox/       # OmniboxInput, MediaPreview, FormatSelector, QualityPicker, DownloadModeSelector\nonboarding/    # First-run onboarding flow\nservices/      # Platform service components\nsettings/      # SettingsCategory, SettingsDropdown, SettingsInput\ntoast/         # Toast notification components\n\n### Button System\n\nClasses: `.button` (default), `.button.elevated`, `.button.active`, `.button.active.color`. Hover via `@media (hover: hover)`. Focus: `outline: var(--focus-ring)` on `:focus-visible` only.\n\n### Custom Controls\n\n- **Toggle**: `<input type=\"checkbox\" role=\"switch\" aria-checked>` with CSS transform animation. RTL-aware via `:dir(rtl)`.\n- **Select**: Native `<select>` overlaid on styled button (position: absolute, transparent). Native provides a11y, button provides visuals.\n- **Input**: Always pair with `<label>` + `aria-describedby` for helper text.\n\n### Switcher\n\nJoined button row. Middle children get `border-radius: 0`. Negative margin eliminates double borders. `.big` variant with container bg.\n\n### Settings Components\n\n- **SettingsToggle**: label + description + Toggle. Generic TS for type-safe setting context/id.\n- **SettingsDropdown**: native select overlay pattern.\n- **SettingsCategory**: section with `id` for hash-linking + focus highlight animation.\n\nAll call `updateSetting({ [context]: { [id]: value } })`.\n\n### Dialog System\n\nNative `<dialog>` + `showModal()`. ARIA: `role=\"dialog\"`, `aria-modal=\"true\"`, `aria-labelledby`. First element gets `autofocus`. ESC/backdrop closes. 150ms entry/exit animation.\n\n### Progress Bar\n\nOuter: `--button-elevated`, 6px, rounded. Inner: status-colored (`--blue` downloading, `--green` complete, `--red` error, `--gray` paused), width=percent, `transition: width 0.3s`. Indeterminate: Skeleton shimmer. Always determinate for downloads: `45% • 234 MB / 512 MB • 2.5 MB/s`.\n\nTO-DO: Add ETA display (`~3m 20s` via `(total - downloaded) / speed`).\n\n### Processing Queue Items\n\nDiscriminated union: `waiting | running | done | error`. Icons: spinner, check, X, exclamation. File type map: `{ video: IconMovie, audio: IconMusic, image: IconPhoto, file: IconFile }`.\n\n## Layout Architecture\n\n### Grid System\n\nTwo-column CSS Grid: sidebar + content. Content scrolls, sidebar fixed. Shadow border via `--content-border`.\n\n### Mobile Breakpoint: 535px\n\nGrid flips to rows. Sidebar becomes fixed bottom tab bar (horizontal scroll). Do NOT force dark theme on mobile.\n\n### Sub-Navigation\n\nTwo-column: 250px nav + content (max 600px). Collapses at 750px to single column with back button.\n\n### Safe Area\n\n`env(safe-area-inset-top/bottom/left)` on all fixed/sticky elements.\n\n## Download UX Principles\n\n### Phase Indicators\n\nShow download phase explicitly (never \"is it hung?\"):\n1. \"Preparing\" — Item enqueued, building config\n2. \"Fetching Info\" — Running yt-dlp info extraction (only if cache miss)\n3. \"Starting\" — Download engine initializing (via -1.0 sentinel)\n4. \"Connecting\" — yt-dlp spawned, awaiting first response (via -2.0 sentinel)\n5. \"Downloading\" — Data flowing (percent > 0%)\n\nTO-DO: Add explicit \"Merging\" phase when yt-dlp emits `[Merger]` line (currently shows ~99% with no phase label).\n\n### Determinate Progress\n\nALWAYS determinate bars: `234 MB / 512 MB (45%) • 2.5 MB/s`. ETA = (total - downloaded) / speed. Smooth speed with rolling average.\n\nTO-DO: ETA display not yet implemented. Speed rolling average not yet implemented (raw yt-dlp speed used directly).\n\n### Optimistic UI\n\nPause/Resume/Remove: update UI instantly, send command in background, revert on failure.\n\n### Error Feedback\n\nNever vague. Always: what happened + why + what to do.\n- ❌ \"Error\" → ✅ \"HTTP 429 - Retrying in 15s...\"\n- ❌ \"Loading...\" → ✅ \"Cannot access cookies - Continuing without auth\"\n\nToast for transient (5s auto-dismiss). Inline for persistent.\n\n### Completion Feedback\n\nColor → --success, icon → checkmark, haptic → hapticConfirm(), toast → \"Downloaded: file.mp4 (123 MB)\" + \"Open folder\".\n\nTO-DO: \"Open folder\" button not yet implemented on completed items (i18n key `open_folder` exists but is unused).\n\n### Rate Limiting (429)\n\nDetect → show \"Retrying in 10s (2/3)\" → rotate player_client/cookies → after 3: detailed error + manual retry button.\n\nTO-DO: Retry status (\"Retrying in 10s (2/3)\") is only logged server-side via `tracing::warn!`, not shown to the user. Need to emit retry events to frontend.\n\n### Batch Operations\n\nAggregate: \"3 Downloading, 2 Paused\". Sidebar badge (red circle). Peek panel: top 3 + total.\n\n## State Management\n\n### Settings Store\n\nPartial-merge: store holds user-changed values only. Derived store merges with defaults via `ts-deepmerge`.\n\n### Schema Versioning\n\n`schemaVersion: number` + migration functions keyed by version. Increment version + add migrator when changing settings.\n\n### Theme System\n\n`auto | light | dark`. Applied as `data-theme` on wrapper div (not html/body).\n\n### Other Stores\n\nAll stores in `$lib/stores/`: `download-store` (queue state), `download-listener` (Tauri event listeners), `media-preview-store` (URL preview), `toast-store`, `settings-store`, `update-store`, `convert-store`, `changelog-store`, `onboarding-store`, `clipboard-monitor`.\n\n## Accessibility\n\nWCAG 2.2 AA compliance required.\n\n### ARIA Rules\n\nSemantic HTML first. `role=\"dialog\"` + `aria-modal`. `aria-label`/`aria-labelledby` for unlabeled buttons. `role=\"switch\"` for toggles. `aria-live=\"polite\"` for dynamic updates. `aria-valuenow/min/max` for progress. No `role=\"button\"` on `<div>`.\n\n### Preferences\n\n`[data-reduce-motion=\"true\"]` → disable all animation. `[data-reduce-transparency=\"true\"]` → solid backdrops. Settings: `reduceMotion`, `reduceTransparency`, `disableHaptics`.\n\n### Contrast\n\nText: 4.5:1. Focus: 3:1 + min 6px area. Never color alone. Use `--on-*` tokens. Test in forced-colors mode.\n\n### Haptics\n\n`hapticSwitch()`, `hapticConfirm()`, `hapticError()`. Gated by `device.supports.haptics && !disableHaptics`.\n\n### Focus & Keyboard\n\nTab order: natural DOM. `:focus-visible` only. Focus ring: 2px --accent, offset 2px. ESC closes dialogs. Enter/Space activates buttons. Arrow keys navigate lists.\n\n### RTL\n\n`:dir(rtl)` for overrides. Logical properties preferred.\n\n## Loop Mascot\n\nExpressive creature reacting to download state (idle → downloading → paused → complete → error). Pure visual feedback — never required for understanding. IP protected.\n\n## i18n\n\nLazy per route. `i18n/{lang}/{namespace}.json`. `$t(\"namespace.key\")`. Default: `en`. All visible strings via `$t()`.\n\n## Coding Rules\n\n### Svelte & TypeScript\n\nSvelte 5 runes only (`$state`, `$derived`, `$effect`, `$props`). TypeScript strict. Discriminated unions for state.\n\n### Styling\n\nScoped `<style>`. `$components/`, `$lib/`, `$i18n/` aliases. `@media (hover: hover)` + `:active` fallback. `pointer-events: none` on images/SVGs. Scrollbar hidden. No `!important` except a11y. Safe area insets.\n\n### Feedback\n\nImmediate UI on click (optimistic). Progress: phase + percent + bytes + speed. Errors: explicit + actionable. Tooltips: 5-10 words + shortcut.\n\n### Focus & Animations\n\n`:focus-visible` ring (2px --accent, offset 2px). `prefers-reduced-motion` alternative. Dialog: autofocus first element, trap, ESC closes.\n\n### Downloads\n\nDeterminate progress with ARIA. Phase icon + text (no color-alone). Error → detail + Retry button. Batch → sidebar badge + aggregate.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","title":"AGENTS.md","content":"# OmniGet\n\nDesktop download manager built with Tauri 2.0 (Rust backend) + SvelteKit (frontend). Modern design principles (2025-2026): clarity over features, immediate feedback, and universal accessibility. Monorepo: `src-tauri/` is Rust, `src/` is SvelteKit + TypeScript. Run `cargo tauri dev` to start.\n\n## Commands\n```bash\npnpm install          # install frontend deps\npnpm dev              # SvelteKit dev server only\ncargo tauri dev       # full app (Rust + frontend)\ncargo check           # typecheck Rust without building\npnpm check            # svelte-check + tsc\n```\n\n## Tech Stack\n\n- **Backend:** Rust, Tauri 2.x, tokio, reqwest, serde, sqlx (SQLite), chromiumoxide\n- **Frontend:** SvelteKit 2, Svelte 5 (runes: `$state`, `$derived`, `$effect`, `$props`), TypeScript strict\n- **Styling:** Scoped CSS with CSS custom properties. NO Tailwind. NO CSS-in-JS.\n- **Icons:** `@tabler/icons-svelte` — import individually: `import IconDownload from \"@tabler/icons-svelte/IconDownload.svelte\"`\n- **Fonts:** System fonts as default; IBM Plex Mono for code and technical content only\n- **i18n:** `sveltekit-i18n` with JSON locale files in `i18n/{lang}/`\n- **Bundler:** Vite 5, adapter-static, pnpm as package manager\n\n## Project Layout\nsrc-tauri/src/\ncommands/       # Tauri IPC commands (invoked from frontend)\nplatforms/      # Download platform implementations (trait-based plugin system)\ntraits.rs     # PlatformDownloader trait — all platforms implement this\nhotmart/      # Hotmart-specific: auth, api, parser, downloader\ncore/           # Shared engine: registry, hls_downloader, media_processor, queue\nmodels/         # Data structs: media.rs, download.rs, settings.rs\nstorage/        # Persistence: config, database (SQLite), cache\nsrc/\nroutes/         # SvelteKit file-based routing (+page.svelte, +layout.svelte)\ncomponents/     # Reusable UI, organized by domain (see Component Organization)\nlib/            # Shared logic: stores/, i18n/\n\n## Design System\n\n### Core Principles\n\n**Minimalism with Purpose**: Every element has a function. No decoration.\n\n**Immediate Feedback**: Progress bars show percent + bytes + speed. Errors are explicit (\"Missing API key\" not \"Error\").\n\n**Color as Guidance**: Accent for primary actions, error for destructive. Never communicate through color alone (pair with icon + text).\n\n**Accessibility First**: WCAG 2.2 AA baseline. 4.5:1 text contrast, 3:1 focus indicators, all elements keyboard accessible.\n\n### Color Architecture\n\nCSS custom properties exclusively. NEVER hardcode colors. Theme via `[data-theme=\"dark\"]` / `[data-theme=\"light\"]`. Every token defined in both `:root` and `[data-theme=\"dark\"]`.\n\n**Tokens** (see app.css for values): `--primary`, `--secondary`, `--tertiary`, `--accent`, `--success`, `--error`, `--warning`, `--button`, `--button-hover`, `--button-press`, `--button-text`, `--button-stroke`, `--button-elevated`, `--sidebar-bg`, `--sidebar-highlight`, `--content-border`, `--input-border`, `--input-bg`, `--popup-bg`, `--dialog-backdrop`.\n\n**Contrast tokens** for text on colored backgrounds: `--on-primary`, `--on-accent`, `--on-success`, `--on-error`, `--on-button`, `--on-button-elevated`.\n\n**Layout constants**: `--padding: 12px`, `--border-radius: 11px`, `--sidebar-width: 80px`.\n\n### Typography\n\nSystem fonts for UI, monospace for code only:\n- `--font-system: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif`\n- `--font-mono: \"IBM Plex Mono\", \"Courier New\", monospace`\n\nHeadings: `font-weight: 500`, `margin-block: 0`. Scale: h1=24px, h2=20px, h3=16px, h4=14.5px, h5=12px, h6=11px.\n\nBody: buttons=14.5px/500, .label=13px/500, .body=14px/400/1.6, .subtext=12.5px/500, .caption=11.5px/400, code=13px/mono.\n\n### Spacing & Radius\n\nBase unit: `var(--padding)` (12px). Derive: `calc(var(--padding) / 2)` (6px), `calc(var(--padding) * 2)` (24px). Border radius: `var(--border-radius)` (11px).\n\n## Component Patterns\n\n### File Organization\ncomponents/\nbuttons/       # ActionButton, SettingsButton, SettingsToggle, Switcher\ndialog/        # DialogContainer, DialogButton, SmallDialog, PickerDialog\nhints/         # Contextual hints UI\nhotmart/       # Hotmart-specific components\nicons/         # Custom SVG icon components (only when tabler doesn't have it)\nmascot/        # Loop mascot animations\nmisc/          # Toggle, Skeleton, Placeholder, SectionHeading, OuterLink\nomnibox/       # OmniboxInput, MediaPreview, FormatSelector, QualityPicker, DownloadModeSelector\nonboarding/    # First-run onboarding flow\nservices/      # Platform service components\nsettings/      # SettingsCategory, SettingsDropdown, SettingsInput\ntoast/         # Toast notification components\n\n### Button System\n\nClasses: `.button` (default), `.button.elevated`, `.button.active`, `.button.active.color`. Hover via `@media (hover: hover)`. Focus: `outline: var(--focus-ring)` on `:focus-visible` only.\n\n### Custom Controls\n\n- **Toggle**: `<input type=\"checkbox\" role=\"switch\" aria-checked>` with CSS transform animation. RTL-aware via `:dir(rtl)`.\n- **Select**: Native `<select>` overlaid on styled button (position: absolute, transparent). Native provides a11y, button provides visuals.\n- **Input**: Always pair with `<label>` + `aria-describedby` for helper text.\n\n### Switcher\n\nJoined button row. Middle children get `border-radius: 0`. Negative margin eliminates double borders. `.big` variant with container bg.\n\n### Settings Components\n\n- **SettingsToggle**: label + description + Toggle. Generic TS for type-safe setting context/id.\n- **SettingsDropdown**: native select overlay pattern.\n- **SettingsCategory**: section with `id` for hash-linking + focus highlight animation.\n\nAll call `updateSetting({ [context]: { [id]: value } })`.\n\n### Dialog System\n\nNative `<dialog>` + `showModal()`. ARIA: `role=\"dialog\"`, `aria-modal=\"true\"`, `aria-labelledby`. First element gets `autofocus`. ESC/backdrop closes. 150ms entry/exit animation.\n\n### Progress Bar\n\nOuter: `--button-elevated`, 6px, rounded. Inner: status-colored (`--blue` downloading, `--green` complete, `--red` error, `--gray` paused), width=percent, `transition: width 0.3s`. Indeterminate: Skeleton shimmer. Always determinate for downloads: `45% • 234 MB / 512 MB • 2.5 MB/s`.\n\nTO-DO: Add ETA display (`~3m 20s` via `(total - downloaded) / speed`).\n\n### Processing Queue Items\n\nDiscriminated union: `waiting | running | done | error`. Icons: spinner, check, X, exclamation. File type map: `{ video: IconMovie, audio: IconMusic, image: IconPhoto, file: IconFile }`.\n\n## Layout Architecture\n\n### Grid System\n\nTwo-column CSS Grid: sidebar + content. Content scrolls, sidebar fixed. Shadow border via `--content-border`.\n\n### Mobile Breakpoint: 535px\n\nGrid flips to rows. Sidebar becomes fixed bottom tab bar (horizontal scroll). Do NOT force dark theme on mobile.\n\n### Sub-Navigation\n\nTwo-column: 250px nav + content (max 600px). Collapses at 750px to single column with back button.\n\n### Safe Area\n\n`env(safe-area-inset-top/bottom/left)` on all fixed/sticky elements.\n\n## Download UX Principles\n\n### Phase Indicators\n\nShow download phase explicitly (never \"is it hung?\"):\n1. \"Preparing\" — Item enqueued, building config\n2. \"Fetching Info\" — Running yt-dlp info extraction (only if cache miss)\n3. \"Starting\" — Download engine initializing (via -1.0 sentinel)\n4. \"Connecting\" — yt-dlp spawned, awaiting first response (via -2.0 sentinel)\n5. \"Downloading\" — Data flowing (percent > 0%)\n\nTO-DO: Add explicit \"Merging\" phase when yt-dlp emits `[Merger]` line (currently shows ~99% with no phase label).\n\n### Determinate Progress\n\nALWAYS determinate bars: `234 MB / 512 MB (45%) • 2.5 MB/s`. ETA = (total - downloaded) / speed. Smooth speed with rolling average.\n\nTO-DO: ETA display not yet implemented. Speed rolling average not yet implemented (raw yt-dlp speed used directly).\n\n### Optimistic UI\n\nPause/Resume/Remove: update UI instantly, send command in background, revert on failure.\n\n### Error Feedback\n\nNever vague. Always: what happened + why + what to do.\n- ❌ \"Error\" → ✅ \"HTTP 429 - Retrying in 15s...\"\n- ❌ \"Loading...\" → ✅ \"Cannot access cookies - Continuing without auth\"\n\nToast for transient (5s auto-dismiss). Inline for persistent.\n\n### Completion Feedback\n\nColor → --success, icon → checkmark, haptic → hapticConfirm(), toast → \"Downloaded: file.mp4 (123 MB)\" + \"Open folder\".\n\nTO-DO: \"Open folder\" button not yet implemented on completed items (i18n key `open_folder` exists but is unused).\n\n### Rate Limiting (429)\n\nDetect → show \"Retrying in 10s (2/3)\" → rotate player_client/cookies → after 3: detailed error + manual retry button.\n\nTO-DO: Retry status (\"Retrying in 10s (2/3)\") is only logged server-side via `tracing::warn!`, not shown to the user. Need to emit retry events to frontend.\n\n### Batch Operations\n\nAggregate: \"3 Downloading, 2 Paused\". Sidebar badge (red circle). Peek panel: top 3 + total.\n\n## State Management\n\n### Settings Store\n\nPartial-merge: store holds user-changed values only. Derived store merges with defaults via `ts-deepmerge`.\n\n### Schema Versioning\n\n`schemaVersion: number` + migration functions keyed by version. Increment version + add migrator when changing settings.\n\n### Theme System\n\n`auto | light | dark`. Applied as `data-theme` on wrapper div (not html/body).\n\n### Other Stores\n\nAll stores in `$lib/stores/`: `download-store` (queue state), `download-listener` (Tauri event listeners), `media-preview-store` (URL preview), `toast-store`, `settings-store`, `update-store`, `convert-store`, `changelog-store`, `onboarding-store`, `clipboard-monitor`.\n\n## Accessibility\n\nWCAG 2.2 AA compliance required.\n\n### ARIA Rules\n\nSemantic HTML first. `role=\"dialog\"` + `aria-modal`. `aria-label`/`aria-labelledby` for unlabeled buttons. `role=\"switch\"` for toggles. `aria-live=\"polite\"` for dynamic updates. `aria-valuenow/min/max` for progress. No `role=\"button\"` on `<div>`.\n\n### Preferences\n\n`[data-reduce-motion=\"true\"]` → disable all animation. `[data-reduce-transparency=\"true\"]` → solid backdrops. Settings: `reduceMotion`, `reduceTransparency`, `disableHaptics`.\n\n### Contrast\n\nText: 4.5:1. Focus: 3:1 + min 6px area. Never color alone. Use `--on-*` tokens. Test in forced-colors mode.\n\n### Haptics\n\n`hapticSwitch()`, `hapticConfirm()`, `hapticError()`. Gated by `device.supports.haptics && !disableHaptics`.\n\n### Focus & Keyboard\n\nTab order: natural DOM. `:focus-visible` only. Focus ring: 2px --accent, offset 2px. ESC closes dialogs. Enter/Space activates buttons. Arrow keys navigate lists.\n\n### RTL\n\n`:dir(rtl)` for overrides. Logical properties preferred.\n\n## Loop Mascot\n\nExpressive creature reacting to download state (idle → downloading → paused → complete → error). Pure visual feedback — never required for understanding. IP protected.\n\n## i18n\n\nLazy per route. `i18n/{lang}/{namespace}.json`. `$t(\"namespace.key\")`. Default: `en`. All visible strings via `$t()`.\n\n## Coding Rules\n\n### Svelte & TypeScript\n\nSvelte 5 runes only (`$state`, `$derived`, `$effect`, `$props`). TypeScript strict. Discriminated unions for state.\n\n### Styling\n\nScoped `<style>`. `$components/`, `$lib/`, `$i18n/` aliases. `@media (hover: hover)` + `:active` fallback. `pointer-events: none` on images/SVGs. Scrollbar hidden. No `!important` except a11y. Safe area insets.\n\n### Feedback\n\nImmediate UI on click (optimistic). Progress: phase + percent + bytes + speed. Errors: explicit + actionable. Tooltips: 5-10 words + shortcut.\n\n### Focus & Animations\n\n`:focus-visible` ring (2px --accent, offset 2px). `prefers-reduced-motion` alternative. Dialog: autofocus first element, trap, ESC closes.\n\n### Downloads\n\nDeterminate progress with ARIA. Phase icon + text (no color-alone). Error → detail + Retry button. Batch → sidebar badge + aggregate.\n","category":"root","tokens":2934},{"name":"claude.md","path":"claude.md","title":"claude.md","content":"# OmniGet\n\nDesktop download manager built with Tauri 2.0 (Rust backend) + SvelteKit (frontend). Modern design principles (2025-2026): clarity over features, immediate feedback, and universal accessibility. Monorepo: `src-tauri/` is Rust, `src/` is SvelteKit + TypeScript. Run `cargo tauri dev` to start.\n\n## Commands\n```bash\npnpm install          # install frontend deps\npnpm dev              # SvelteKit dev server only\ncargo tauri dev       # full app (Rust + frontend)\ncargo check           # typecheck Rust without building\npnpm check            # svelte-check + tsc\n```\n\n## Tech Stack\n\n- **Backend:** Rust, Tauri 2.x, tokio, reqwest, serde, sqlx (SQLite), chromiumoxide\n- **Frontend:** SvelteKit 2, Svelte 5 (runes: `$state`, `$derived`, `$effect`, `$props`), TypeScript strict\n- **Styling:** Scoped CSS with CSS custom properties. NO Tailwind. NO CSS-in-JS.\n- **Icons:** `@tabler/icons-svelte` — import individually: `import IconDownload from \"@tabler/icons-svelte/IconDownload.svelte\"`\n- **Fonts:** System fonts as default; IBM Plex Mono for code and technical content only\n- **i18n:** `sveltekit-i18n` with JSON locale files in `i18n/{lang}/`\n- **Bundler:** Vite 5, adapter-static, pnpm as package manager\n\n## Project Layout\nsrc-tauri/src/\ncommands/       # Tauri IPC commands (invoked from frontend)\nplatforms/      # Download platform implementations (trait-based plugin system)\ntraits.rs     # PlatformDownloader trait — all platforms implement this\nhotmart/      # Hotmart-specific: auth, api, parser, downloader\ncore/           # Shared engine: registry, hls_downloader, media_processor, queue\nmodels/         # Data structs: media.rs, download.rs, settings.rs\nstorage/        # Persistence: config, database (SQLite), cache\nsrc/\nroutes/         # SvelteKit file-based routing (+page.svelte, +layout.svelte)\ncomponents/     # Reusable UI, organized by domain (see Component Organization)\nlib/            # Shared logic: stores/, i18n/\n\n## Design System\n\n### Core Principles\n\n**Minimalism with Purpose**: Every element has a function. No decoration.\n\n**Immediate Feedback**: Progress bars show percent + bytes + speed. Errors are explicit (\"Missing API key\" not \"Error\").\n\n**Color as Guidance**: Accent for primary actions, error for destructive. Never communicate through color alone (pair with icon + text).\n\n**Accessibility First**: WCAG 2.2 AA baseline. 4.5:1 text contrast, 3:1 focus indicators, all elements keyboard accessible.\n\n### Color Architecture\n\nCSS custom properties exclusively. NEVER hardcode colors. Theme via `[data-theme=\"dark\"]` / `[data-theme=\"light\"]`. Every token defined in both `:root` and `[data-theme=\"dark\"]`.\n\n**Tokens** (see app.css for values): `--primary`, `--secondary`, `--tertiary`, `--accent`, `--success`, `--error`, `--warning`, `--button`, `--button-hover`, `--button-press`, `--button-text`, `--button-stroke`, `--button-elevated`, `--sidebar-bg`, `--sidebar-highlight`, `--content-border`, `--input-border`, `--input-bg`, `--popup-bg`, `--dialog-backdrop`.\n\n**Contrast tokens** for text on colored backgrounds: `--on-primary`, `--on-accent`, `--on-success`, `--on-error`, `--on-button`, `--on-button-elevated`.\n\n**Layout constants**: `--padding: 12px`, `--border-radius: 11px`, `--sidebar-width: 80px`.\n\n### Typography\n\nSystem fonts for UI, monospace for code only:\n- `--font-system: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif`\n- `--font-mono: \"IBM Plex Mono\", \"Courier New\", monospace`\n\nHeadings: `font-weight: 500`, `margin-block: 0`. Scale: h1=24px, h2=20px, h3=16px, h4=14.5px, h5=12px, h6=11px.\n\nBody: buttons=14.5px/500, .label=13px/500, .body=14px/400/1.6, .subtext=12.5px/500, .caption=11.5px/400, code=13px/mono.\n\n### Spacing & Radius\n\nBase unit: `var(--padding)` (12px). Derive: `calc(var(--padding) / 2)` (6px), `calc(var(--padding) * 2)` (24px). Border radius: `var(--border-radius)` (11px).\n\n## Component Patterns\n\n### File Organization\ncomponents/\nbuttons/       # ActionButton, SettingsButton, SettingsToggle, Switcher\ndialog/        # DialogContainer, DialogButton, SmallDialog, PickerDialog\nhints/         # Contextual hints UI\nhotmart/       # Hotmart-specific components\nicons/         # Custom SVG icon components (only when tabler doesn't have it)\nmascot/        # Loop mascot animations\nmisc/          # Toggle, Skeleton, Placeholder, SectionHeading, OuterLink\nomnibox/       # OmniboxInput, MediaPreview, FormatSelector, QualityPicker, DownloadModeSelector\nonboarding/    # First-run onboarding flow\nservices/      # Platform service components\nsettings/      # SettingsCategory, SettingsDropdown, SettingsInput\ntoast/         # Toast notification components\n\n### Button System\n\nClasses: `.button` (default), `.button.elevated`, `.button.active`, `.button.active.color`. Hover via `@media (hover: hover)`. Focus: `outline: var(--focus-ring)` on `:focus-visible` only.\n\n### Custom Controls\n\n- **Toggle**: `<input type=\"checkbox\" role=\"switch\" aria-checked>` with CSS transform animation. RTL-aware via `:dir(rtl)`.\n- **Select**: Native `<select>` overlaid on styled button (position: absolute, transparent). Native provides a11y, button provides visuals.\n- **Input**: Always pair with `<label>` + `aria-describedby` for helper text.\n\n### Switcher\n\nJoined button row. Middle children get `border-radius: 0`. Negative margin eliminates double borders. `.big` variant with container bg.\n\n### Settings Components\n\n- **SettingsToggle**: label + description + Toggle. Generic TS for type-safe setting context/id.\n- **SettingsDropdown**: native select overlay pattern.\n- **SettingsCategory**: section with `id` for hash-linking + focus highlight animation.\n\nAll call `updateSetting({ [context]: { [id]: value } })`.\n\n### Dialog System\n\nNative `<dialog>` + `showModal()`. ARIA: `role=\"dialog\"`, `aria-modal=\"true\"`, `aria-labelledby`. First element gets `autofocus`. ESC/backdrop closes. 150ms entry/exit animation.\n\n### Progress Bar\n\nOuter: `--button-elevated`, 6px, rounded. Inner: status-colored (`--blue` downloading, `--green` complete, `--red` error, `--gray` paused), width=percent, `transition: width 0.3s`. Indeterminate: Skeleton shimmer. Always determinate for downloads: `45% • 234 MB / 512 MB • 2.5 MB/s`.\n\nTO-DO: Add ETA display (`~3m 20s` via `(total - downloaded) / speed`).\n\n### Processing Queue Items\n\nDiscriminated union: `waiting | running | done | error`. Icons: spinner, check, X, exclamation. File type map: `{ video: IconMovie, audio: IconMusic, image: IconPhoto, file: IconFile }`.\n\n## Layout Architecture\n\n### Grid System\n\nTwo-column CSS Grid: sidebar + content. Content scrolls, sidebar fixed. Shadow border via `--content-border`.\n\n### Mobile Breakpoint: 535px\n\nGrid flips to rows. Sidebar becomes fixed bottom tab bar (horizontal scroll). Do NOT force dark theme on mobile.\n\n### Sub-Navigation\n\nTwo-column: 250px nav + content (max 600px). Collapses at 750px to single column with back button.\n\n### Safe Area\n\n`env(safe-area-inset-top/bottom/left)` on all fixed/sticky elements.\n\n## Download UX Principles\n\n### Phase Indicators\n\nShow download phase explicitly (never \"is it hung?\"):\n1. \"Preparing\" — Item enqueued, building config\n2. \"Fetching Info\" — Running yt-dlp info extraction (only if cache miss)\n3. \"Starting\" — Download engine initializing (via -1.0 sentinel)\n4. \"Connecting\" — yt-dlp spawned, awaiting first response (via -2.0 sentinel)\n5. \"Downloading\" — Data flowing (percent > 0%)\n\nTO-DO: Add explicit \"Merging\" phase when yt-dlp emits `[Merger]` line (currently shows ~99% with no phase label).\n\n### Determinate Progress\n\nALWAYS determinate bars: `234 MB / 512 MB (45%) • 2.5 MB/s`. ETA = (total - downloaded) / speed. Smooth speed with rolling average.\n\nTO-DO: ETA display not yet implemented. Speed rolling average not yet implemented (raw yt-dlp speed used directly).\n\n### Optimistic UI\n\nPause/Resume/Remove: update UI instantly, send command in background, revert on failure.\n\n### Error Feedback\n\nNever vague. Always: what happened + why + what to do.\n- ❌ \"Error\" → ✅ \"HTTP 429 - Retrying in 15s...\"\n- ❌ \"Loading...\" → ✅ \"Cannot access cookies - Continuing without auth\"\n\nToast for transient (5s auto-dismiss). Inline for persistent.\n\n### Completion Feedback\n\nColor → --success, icon → checkmark, haptic → hapticConfirm(), toast → \"Downloaded: file.mp4 (123 MB)\" + \"Open folder\".\n\nTO-DO: \"Open folder\" button not yet implemented on completed items (i18n key `open_folder` exists but is unused).\n\n### Rate Limiting (429)\n\nDetect → show \"Retrying in 10s (2/3)\" → rotate player_client/cookies → after 3: detailed error + manual retry button.\n\nTO-DO: Retry status (\"Retrying in 10s (2/3)\") is only logged server-side via `tracing::warn!`, not shown to the user. Need to emit retry events to frontend.\n\n### Batch Operations\n\nAggregate: \"3 Downloading, 2 Paused\". Sidebar badge (red circle). Peek panel: top 3 + total.\n\n## State Management\n\n### Settings Store\n\nPartial-merge: store holds user-changed values only. Derived store merges with defaults via `ts-deepmerge`.\n\n### Schema Versioning\n\n`schemaVersion: number` + migration functions keyed by version. Increment version + add migrator when changing settings.\n\n### Theme System\n\n`auto | light | dark`. Applied as `data-theme` on wrapper div (not html/body).\n\n### Other Stores\n\nAll stores in `$lib/stores/`: `download-store` (queue state), `download-listener` (Tauri event listeners), `media-preview-store` (URL preview), `toast-store`, `settings-store`, `update-store`, `convert-store`, `changelog-store`, `onboarding-store`, `clipboard-monitor`.\n\n## Accessibility\n\nWCAG 2.2 AA compliance required.\n\n### ARIA Rules\n\nSemantic HTML first. `role=\"dialog\"` + `aria-modal`. `aria-label`/`aria-labelledby` for unlabeled buttons. `role=\"switch\"` for toggles. `aria-live=\"polite\"` for dynamic updates. `aria-valuenow/min/max` for progress. No `role=\"button\"` on `<div>`.\n\n### Preferences\n\n`[data-reduce-motion=\"true\"]` → disable all animation. `[data-reduce-transparency=\"true\"]` → solid backdrops. Settings: `reduceMotion`, `reduceTransparency`, `disableHaptics`.\n\n### Contrast\n\nText: 4.5:1. Focus: 3:1 + min 6px area. Never color alone. Use `--on-*` tokens. Test in forced-colors mode.\n\n### Haptics\n\n`hapticSwitch()`, `hapticConfirm()`, `hapticError()`. Gated by `device.supports.haptics && !disableHaptics`.\n\n### Focus & Keyboard\n\nTab order: natural DOM. `:focus-visible` only. Focus ring: 2px --accent, offset 2px. ESC closes dialogs. Enter/Space activates buttons. Arrow keys navigate lists.\n\n### RTL\n\n`:dir(rtl)` for overrides. Logical properties preferred.\n\n## Loop Mascot\n\nExpressive creature reacting to download state (idle → downloading → paused → complete → error). Pure visual feedback — never required for understanding. IP protected.\n\n## i18n\n\nLazy per route. `i18n/{lang}/{namespace}.json`. `$t(\"namespace.key\")`. Default: `en`. All visible strings via `$t()`.\n\n## Coding Rules\n\n### Svelte & TypeScript\n\nSvelte 5 runes only (`$state`, `$derived`, `$effect`, `$props`). TypeScript strict. Discriminated unions for state.\n\n### Styling\n\nScoped `<style>`. `$components/`, `$lib/`, `$i18n/` aliases. `@media (hover: hover)` + `:active` fallback. `pointer-events: none` on images/SVGs. Scrollbar hidden. No `!important` except a11y. Safe area insets.\n\n### Feedback\n\nImmediate UI on click (optimistic). Progress: phase + percent + bytes + speed. Errors: explicit + actionable. Tooltips: 5-10 words + shortcut.\n\n### Focus & Animations\n\n`:focus-visible` ring (2px --accent, offset 2px). `prefers-reduced-motion` alternative. Dialog: autofocus first element, trap, ESC closes.\n\n### Downloads\n\nDeterminate progress with ARIA. Phase icon + text (no color-alone). Error → detail + Retry button. Batch → sidebar badge + aggregate.\n","category":"root","tokens":2934}]}