diff --git a/AGENTS.md b/AGENTS.md index 9143d8c..b2c2ca4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -37,6 +37,37 @@ npm test -- -t "test name" # Run a single test (if using Vitest/Jest) - **Database**: SQLite via `@tauri-apps/plugin-sql` - **AI**: OpenAI-compatible APIs (OpenRouter, custom providers) +## UI Design & Component System + +### Component Library +- **Library**: [shadcn-svelte](https://www.shadcn-svelte.com/) +- **Location**: `src/lib/components/ui/` +- **Installation**: Use `npx shadcn-svelte@latest add [component]` to add new components. +- **Icons**: [Lucide Svelte](https://lucide.dev/icons/) (`lucide-svelte`) + +### Theming System +The application uses a sophisticated CSS variable-based theming system defined in `src/app.css`. It supports multiple distinct visual themes that override standard Tailwind/Shadcn tokens. + +**Available Themes**: +- **Default (Dark)**: Modern slate/blue dark mode. +- **Light (Paper)**: Warm, high-contrast, paper-like aesthetic. +- **Light (Solarized)**: Classic solarized light palette. +- **Retro Console**: CRT terminal aesthetic (green/amber on black) with scanline effects. +- **Fallen Down**: Undertale/Deltarune inspired high-contrast pixel art aesthetic (black/white/yellow). + +### CSS Variables & Tokens +- **Shadcn Tokens**: Standard tokens (`--background`, `--foreground`, `--primary`, `--muted`, etc.) are mapped to theme-specific colors in `app.css`. +- **Surface System**: Custom `surface-*` (50-950) and `accent-*` (50-950) scales are used for fine-grained control across themes. +- **Typography**: + - UI Font: System sans-serif. + - Story Text: Configurable via `--font-story` (Serif for default, Monospace for Retro/Fallen Down). + +### Usage Guidelines +1. **Prefer Shadcn Components**: Use components from `$lib/components/ui` whenever possible (e.g., `Button`, `Input`, `Card`). +2. **Tailwind Classes**: Use standard Tailwind classes. They will automatically adapt to the active theme via the CSS variables. +3. **Custom Styling**: If custom CSS is needed, use the CSS variables defined in `app.css` to ensure theme compatibility (e.g., `var(--bg-secondary)` instead of hardcoded hex). +4. **Icons**: Import icons from `lucide-svelte` (e.g., `import { Save } from 'lucide-svelte';`). + ## Current Refactor: Preset-Based Service Configuration **Status**: In Progress - Phase 3 (AgentProfiles UI) Complete diff --git a/components.json b/components.json new file mode 100644 index 0000000..779a9d1 --- /dev/null +++ b/components.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "tailwind": { + "css": "src/app.css", + "baseColor": "slate" + }, + "aliases": { + "components": "$lib/components", + "utils": "$lib/utils/cn", + "ui": "$lib/components/ui", + "hooks": "$lib/hooks", + "lib": "$lib" + }, + "typescript": true, + "registry": "https://tw3.shadcn-svelte.com/registry/default" +} diff --git a/package-lock.json b/package-lock.json index 43524d2..6f39b14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,24 +17,30 @@ "@tauri-apps/plugin-process": "^2.3.1", "@tauri-apps/plugin-sql": "^2", "@tauri-apps/plugin-updater": "^2.9.0", + "clsx": "^2.1.1", "gpt-tokenizer": "^3.4.0", "harper.js": "^1.2.0", "html5-qrcode": "^2.3.8", "jsonrepair": "^3.13.2", "lucide-svelte": "^0.468.0", - "marked": "^17.0.1" + "marked": "^17.0.1", + "tailwind-merge": "^3.4.0" }, "devDependencies": { + "@lucide/svelte": "^0.482.0", "@sveltejs/adapter-static": "^3.0.6", "@sveltejs/kit": "^2.9.0", "@sveltejs/vite-plugin-svelte": "^5.0.0", "@tauri-apps/cli": "^2", "@types/marked": "^5.0.2", "autoprefixer": "^10.4.20", + "bits-ui": "^1.8.0", "postcss": "^8.4.49", "svelte": "^5.0.0", "svelte-check": "^4.0.0", + "tailwind-variants": "^0.2.1", "tailwindcss": "^3.4.17", + "tailwindcss-animate": "^1.0.7", "typescript": "~5.6.2", "vite": "^6.0.3" } @@ -494,6 +500,44 @@ "node": ">=18" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@internationalized/date": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.10.1.tgz", + "integrity": "sha512-oJrXtQiAXLvT9clCf1K4kxp3eKsQhIaZqxEyowkBcsvZDdZkbWrVmnGknxs5flTD0VGsxrxKgBCZty1EzoiMzA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -539,6 +583,16 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lucide/svelte": { + "version": "0.482.0", + "resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-0.482.0.tgz", + "integrity": "sha512-n2ycHU9cNcleRDwwpEHBJ6pYzVhHIaL3a+9dQa8kns9hB2g05bY+v2p2KP8v0pZwtNhYTHk/F2o2uZ1bVtQGhw==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "svelte": "^5" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -999,6 +1053,16 @@ "vite": "^6.0.0" } }, + "node_modules/@swc/helpers": { + "version": "0.5.18", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.18.tgz", + "integrity": "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@tauri-apps/api": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.9.1.tgz", @@ -1441,6 +1505,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bits-ui": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-1.8.0.tgz", + "integrity": "sha512-CXD6Orp7l8QevNDcRPLXc/b8iMVgxDWT2LyTwsdLzJKh9CxesOmPuNePSPqAxKoT59FIdU4aFPS1k7eBdbaCxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.4", + "@floating-ui/dom": "^1.6.7", + "@internationalized/date": "^3.5.6", + "css.escape": "^1.5.1", + "esm-env": "^1.1.2", + "runed": "^0.23.2", + "svelte-toolbelt": "^0.7.1", + "tabbable": "^6.2.0" + }, + "engines": { + "node": ">=18", + "pnpm": ">=8.7.0" + }, + "funding": { + "url": "https://github.com/sponsors/huntabyte" + }, + "peerDependencies": { + "svelte": "^5.11.0" + } + }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -1565,6 +1656,13 @@ "node": ">= 0.6" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -1854,6 +1952,13 @@ "integrity": "sha512-jsr4vafJhwoLVEDW3n1KvPnCCXWaQfRng0/EEYk1vNcQGcG/htAdhJX0be8YyqMoSz7+hZvOZSTAepsabiuhiQ==", "license": "Apache-2.0" }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "dev": true, + "license": "MIT" + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2163,7 +2268,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2498,6 +2602,22 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/runed": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/runed/-/runed-0.23.4.tgz", + "integrity": "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte", + "https://github.com/sponsors/tglide" + ], + "dependencies": { + "esm-env": "^1.0.0" + }, + "peerDependencies": { + "svelte": "^5.7.0" + } + }, "node_modules/sade": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", @@ -2543,6 +2663,16 @@ "node": ">=0.10.0" } }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, "node_modules/sucrase": { "version": "3.35.1", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", @@ -2630,12 +2760,79 @@ "typescript": ">=5.0.0" } }, + "node_modules/svelte-toolbelt": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.7.1.tgz", + "integrity": "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte" + ], + "dependencies": { + "clsx": "^2.1.1", + "runed": "^0.23.2", + "style-to-object": "^1.0.8" + }, + "engines": { + "node": ">=18", + "pnpm": ">=8.7.0" + }, + "peerDependencies": { + "svelte": "^5.0.0" + } + }, + "node_modules/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tailwind-merge": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", + "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwind-variants": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-0.2.1.tgz", + "integrity": "sha512-2xmhAf4UIc3PijOUcJPA1LP4AbxhpcHuHM2C26xM0k81r0maAO6uoUSHl3APmvHZcY5cZCY/bYuJdfFa4eGoaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tailwind-merge": "^2.2.0" + }, + "engines": { + "node": ">=16.x", + "pnpm": ">=7.x" + }, + "peerDependencies": { + "tailwindcss": "*" + } + }, + "node_modules/tailwind-variants/node_modules/tailwind-merge": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", + "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "3.4.19", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -2668,6 +2865,16 @@ "node": ">=14.0.0" } }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, "node_modules/tailwindcss/node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -2802,6 +3009,13 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/typescript": { "version": "5.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", diff --git a/package.json b/package.json index f05c450..1f6ed4b 100644 --- a/package.json +++ b/package.json @@ -21,24 +21,30 @@ "@tauri-apps/plugin-process": "^2.3.1", "@tauri-apps/plugin-sql": "^2", "@tauri-apps/plugin-updater": "^2.9.0", + "clsx": "^2.1.1", "gpt-tokenizer": "^3.4.0", "harper.js": "^1.2.0", "html5-qrcode": "^2.3.8", "jsonrepair": "^3.13.2", "lucide-svelte": "^0.468.0", - "marked": "^17.0.1" + "marked": "^17.0.1", + "tailwind-merge": "^3.4.0" }, "devDependencies": { + "@lucide/svelte": "^0.482.0", "@sveltejs/adapter-static": "^3.0.6", "@sveltejs/kit": "^2.9.0", "@sveltejs/vite-plugin-svelte": "^5.0.0", "@tauri-apps/cli": "^2", "@types/marked": "^5.0.2", "autoprefixer": "^10.4.20", + "bits-ui": "^1.8.0", "postcss": "^8.4.49", "svelte": "^5.0.0", "svelte-check": "^4.0.0", + "tailwind-variants": "^0.2.1", "tailwindcss": "^3.4.17", + "tailwindcss-animate": "^1.0.7", "typescript": "~5.6.2", "vite": "^6.0.3" } diff --git a/src/app.css b/src/app.css index 023738a..c4b7085 100644 --- a/src/app.css +++ b/src/app.css @@ -7,6 +7,7 @@ ============================================= */ @layer base { + /* Default/Dark Theme (base) */ :root { /* Surface colors (backgrounds, borders) */ @@ -57,6 +58,28 @@ --crt-glow: none; --crt-flicker: none; --font-story: 'Georgia', 'Cambria', serif; + + /* Shadcn Variables (Dark Default) */ + --background: 222 47% 11%; + --foreground: 210 40% 98%; + --card: 217 33% 17%; + --card-foreground: 210 40% 98%; + --popover: 217 33% 17%; + --popover-foreground: 210 40% 98%; + --primary: 221.2 83.2% 53.3%; + --primary-foreground: 210 40% 98%; + --secondary: 217 33% 25%; + --secondary-foreground: 210 40% 98%; + --muted: 217 33% 25%; + --muted-foreground: 215 20.2% 65.1%; + --accent: 217 33% 25%; + --accent-foreground: 210 40% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + --border: 217 33% 25%; + --input: 217 33% 25%; + --ring: 221.2 83.2% 53.3%; + --radius: 0.5rem; } /* ============================================= @@ -65,27 +88,42 @@ ============================================= */ [data-theme="light"] { /* Paper-inspired surface colors */ - --color-surface-50: #faf9f7; /* Clean paper white */ - --color-surface-100: #f5f3f0; /* Slightly off-white */ - --color-surface-200: #eceae5; /* Light warm gray */ - --color-surface-300: #dedad3; /* Soft border gray */ - --color-surface-400: #b8b3a9; /* Muted gray for icons */ - --color-surface-500: #7d786e; /* Medium gray - muted text */ - --color-surface-600: #5c5850; /* Dark gray */ - --color-surface-700: #3d3a35; /* Very dark - body text */ - --color-surface-800: #2a2825; /* Near black */ - --color-surface-850: #1f1e1b; /* Darker */ - --color-surface-900: #161513; /* Rich black - headers */ - --color-surface-950: #0a0a09; /* Deepest black */ + --color-surface-50: #faf9f7; + /* Clean paper white */ + --color-surface-100: #f5f3f0; + /* Slightly off-white */ + --color-surface-200: #eceae5; + /* Light warm gray */ + --color-surface-300: #dedad3; + /* Soft border gray */ + --color-surface-400: #b8b3a9; + /* Muted gray for icons */ + --color-surface-500: #7d786e; + /* Medium gray - muted text */ + --color-surface-600: #5c5850; + /* Dark gray */ + --color-surface-700: #3d3a35; + /* Very dark - body text */ + --color-surface-800: #2a2825; + /* Near black */ + --color-surface-850: #1f1e1b; + /* Darker */ + --color-surface-900: #161513; + /* Rich black - headers */ + --color-surface-950: #0a0a09; + /* Deepest black */ /* Warm accent colors (amber/brown - ink on paper) */ --color-accent-50: #fef8ee; --color-accent-100: #fcf0dc; --color-accent-200: #f8ddb8; --color-accent-300: #f3c78a; - --color-accent-400: #e6a54a; /* Warm amber */ - --color-accent-500: #c4872d; /* Rich golden brown */ - --color-accent-600: #a66b1f; /* Deep amber */ + --color-accent-400: #e6a54a; + /* Warm amber */ + --color-accent-500: #c4872d; + /* Rich golden brown */ + --color-accent-600: #a66b1f; + /* Deep amber */ --color-accent-700: #8a5419; --color-accent-800: #724516; --color-accent-900: #5f3a14; @@ -104,6 +142,27 @@ --scrollbar-track: var(--color-surface-200); --scrollbar-thumb: var(--color-surface-400); --scrollbar-thumb-hover: var(--color-surface-500); + + /* Shadcn Variables (Light) */ + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 215 20.2% 65.1%; } /* ============================================= @@ -112,26 +171,40 @@ ============================================= */ [data-theme="light-solarized"] { /* Solarized base colors */ - --color-surface-50: #fdf6e3; /* base3 - main background */ - --color-surface-100: #f5eedb; /* between base3 and base2 */ - --color-surface-200: #eee8d5; /* base2 - background highlights */ - --color-surface-300: #e0d6c1; /* border color */ - --color-surface-400: #9a9181; /* muted UI elements */ - --color-surface-500: #6b6459; /* muted text - darker for visibility */ - --color-surface-600: #544f46; /* secondary text */ - --color-surface-700: #3d3933; /* body text - dark for contrast */ - --color-surface-800: #2a2721; /* emphasized text */ - --color-surface-850: #1e1c18; /* darker */ - --color-surface-900: #141310; /* headers - near black */ - --color-surface-950: #0a0908; /* deepest */ + --color-surface-50: #fdf6e3; + /* base3 - main background */ + --color-surface-100: #f5eedb; + /* between base3 and base2 */ + --color-surface-200: #eee8d5; + /* base2 - background highlights */ + --color-surface-300: #e0d6c1; + /* border color */ + --color-surface-400: #9a9181; + /* muted UI elements */ + --color-surface-500: #6b6459; + /* muted text - darker for visibility */ + --color-surface-600: #544f46; + /* secondary text */ + --color-surface-700: #3d3933; + /* body text - dark for contrast */ + --color-surface-800: #2a2721; + /* emphasized text */ + --color-surface-850: #1e1c18; + /* darker */ + --color-surface-900: #141310; + /* headers - near black */ + --color-surface-950: #0a0908; + /* deepest */ /* Solarized accent colors (cyan) */ --color-accent-50: #e8f5f4; --color-accent-100: #d1ebe9; --color-accent-200: #a3d7d3; --color-accent-300: #75c3bd; - --color-accent-400: #2aa198; /* Solarized cyan */ - --color-accent-500: #24897f; /* Slightly darker for contrast */ + --color-accent-400: #2aa198; + /* Solarized cyan */ + --color-accent-500: #24897f; + /* Slightly darker for contrast */ --color-accent-600: #1e7268; --color-accent-700: #185b52; --color-accent-800: #12443d; @@ -151,6 +224,35 @@ --scrollbar-track: var(--color-surface-200); --scrollbar-thumb: var(--color-surface-400); --scrollbar-thumb-hover: var(--color-surface-500); + + /* Shadcn Variables (Light - Solarized) */ + --background: 44 26% 94%; + /* base3 */ + --foreground: 204 11% 23%; + /* base00 */ + --card: 43 27% 91%; + /* base2 */ + --card-foreground: 204 11% 23%; + --popover: 43 27% 91%; + --popover-foreground: 204 11% 23%; + --primary: 175 59% 40%; + /* cyan */ + --primary-foreground: 44 26% 94%; + --secondary: 42 16% 87%; + /* base2ish */ + --secondary-foreground: 204 11% 23%; + --muted: 42 16% 87%; + --muted-foreground: 38 7% 40%; + /* base1 */ + --accent: 42 16% 87%; + --accent-foreground: 175 59% 40%; + --destructive: 1 71% 52%; + /* red */ + --destructive-foreground: 44 26% 94%; + --border: 38 15% 82%; + /* base2/3 mix */ + --input: 38 15% 82%; + --ring: 175 59% 40%; } /* ============================================= @@ -226,6 +328,35 @@ --retro-border: 2px solid var(--color-retro-green-muted); --retro-border-glow: 0 0 4px rgba(57, 255, 20, 0.3); + /* Shadcn Variables (Retro) */ + --background: 180 30% 2%; + /* retro-black */ + --foreground: 110 100% 54%; + /* retro-green */ + --card: 168 29% 5%; + /* retro-dark */ + --card-foreground: 110 100% 54%; + --popover: 168 29% 5%; + --popover-foreground: 110 100% 54%; + --primary: 186 100% 50%; + /* retro-cyan */ + --primary-foreground: 180 30% 2%; + --secondary: 158 44% 8%; + /* retro-green-dark */ + --secondary-foreground: 110 100% 54%; + --muted: 158 44% 8%; + --muted-foreground: 144 48% 20%; + /* retro-green-muted */ + --accent: 158 44% 8%; + --accent-foreground: 186 100% 50%; + --destructive: 0 100% 59%; + /* retro-red */ + --destructive-foreground: 180 30% 2%; + --border: 144 48% 20%; + --input: 144 48% 20%; + --ring: 186 100% 50%; + --radius: 0; + /* Typography - pixelated monospace feel */ --font-story: 'VT323', 'Share Tech Mono', 'JetBrains Mono', 'Fira Code', 'Courier New', monospace; } @@ -292,6 +423,36 @@ --ut-heart-color: var(--color-ut-red); --ut-action-color: var(--color-ut-orange); + /* Shadcn Variables (Fallen Down) */ + --background: 0 0% 0%; + /* Black */ + --foreground: 0 0% 100%; + /* White */ + --card: 0 0% 0%; + --card-foreground: 0 0% 100%; + --popover: 0 0% 0%; + --popover-foreground: 0 0% 100%; + --primary: 60 100% 50%; + /* Yellow */ + --primary-foreground: 0 0% 0%; + --secondary: 0 0% 10%; + /* Dark Gray */ + --secondary-foreground: 0 0% 100%; + --muted: 0 0% 20%; + --muted-foreground: 0 0% 75%; + /* Silver */ + --accent: 60 100% 50%; + /* Yellow */ + --accent-foreground: 0 0% 0%; + --destructive: 0 100% 50%; + /* Red */ + --destructive-foreground: 0 0% 100%; + --border: 0 0% 100%; + /* White Border */ + --input: 0 0% 100%; + --ring: 60 100% 50%; + --radius: 0; + /* Typography - VT323 pixel font */ --font-story: 'VT323', 'Share Tech Mono', monospace; } @@ -345,20 +506,16 @@ pointer-events: none; z-index: 9999; background: - repeating-linear-gradient( - 0deg, + repeating-linear-gradient(0deg, transparent 0px, transparent 1px, rgba(0, 0, 0, 0.15) 1px, - rgba(0, 0, 0, 0.15) 2px - ), - repeating-linear-gradient( - 0deg, + rgba(0, 0, 0, 0.15) 2px), + repeating-linear-gradient(0deg, var(--crt-scanline) 0px, var(--crt-scanline) 1px, transparent 1px, - transparent 3px - ); + transparent 3px); animation: scanlines 0.08s linear infinite; } @@ -372,12 +529,10 @@ height: 100%; pointer-events: none; z-index: 9998; - background: radial-gradient( - ellipse 120% 120% at 50% 50%, - transparent 50%, - rgba(0, 0, 0, 0.25) 80%, - rgba(0, 0, 0, 0.5) 100% - ); + background: radial-gradient(ellipse 120% 120% at 50% 50%, + transparent 50%, + rgba(0, 0, 0, 0.25) 80%, + rgba(0, 0, 0, 0.5) 100%); animation: crt-flicker 3s ease-in-out infinite; } @@ -392,18 +547,23 @@ 0% { background-position: 0 0; } + 100% { background-position: 0 3px; } } @keyframes crt-flicker { - 0%, 100% { + + 0%, + 100% { opacity: 0.96; } + 50% { opacity: 1; } + 75% { opacity: 0.98; } @@ -411,9 +571,12 @@ /* Phosphor bloom animation for text */ @keyframes phosphor-bloom { - 0%, 100% { + + 0%, + 100% { text-shadow: var(--crt-text-glow); } + 50% { text-shadow: 0 0 3px rgba(57, 255, 20, 0.9), @@ -496,11 +659,9 @@ /* Card styling with retro frame */ [data-theme="retro-console"] .card { border: 2px solid var(--color-retro-green-muted); - background: linear-gradient( - 180deg, - rgba(10, 18, 16, 0.95) 0%, - rgba(5, 8, 8, 0.98) 100% - ); + background: linear-gradient(180deg, + rgba(10, 18, 16, 0.95) 0%, + rgba(5, 8, 8, 0.98) 100%); box-shadow: var(--retro-border-glow), inset 0 0 40px rgba(57, 255, 20, 0.02); @@ -737,6 +898,7 @@ ============================================= */ .prose-content { + /* Paragraphs */ p { @apply mb-4 last:mb-0; @@ -758,18 +920,22 @@ color: var(--text-primary); } - h4, h5, h6 { + h4, + h5, + h6 { @apply text-base font-semibold mb-2 mt-3 first:mt-0; color: var(--text-primary); } /* Bold and italic */ - strong, b { + strong, + b { @apply font-bold; color: var(--text-primary); } - em, i { + em, + i { @apply italic; } @@ -792,8 +958,8 @@ @apply leading-relaxed; } - li > ul, - li > ol { + li>ul, + li>ol { @apply ml-4 mt-1 mb-0; } @@ -833,7 +999,8 @@ @apply w-full border-collapse my-4; } - th, td { + th, + td { @apply border px-3 py-2 text-left; border-color: var(--border-primary); } @@ -843,7 +1010,7 @@ @apply font-semibold; } -/* Images */ + /* Images */ img { @apply max-w-full h-auto rounded my-4; } @@ -871,7 +1038,8 @@ @apply mb-2 last:mb-0; } - ul, ol { + ul, + ol { @apply my-2; } @@ -879,7 +1047,12 @@ @apply mb-1; } - h1, h2, h3, h4, h5, h6 { + h1, + h2, + h3, + h4, + h5, + h6 { @apply mb-2 mt-3 first:mt-0; } @@ -909,13 +1082,26 @@ } @keyframes blink { - 0%, 100% { opacity: 0.7; } - 50% { opacity: 0; } + + 0%, + 100% { + opacity: 0.7; + } + + 50% { + opacity: 0; + } } /* Retro Console theme prose overrides */ [data-theme="retro-console"] .prose-content { - h1, h2, h3, h4, h5, h6 { + + h1, + h2, + h3, + h4, + h5, + h6 { text-shadow: var(--crt-text-glow); } @@ -937,7 +1123,8 @@ text-shadow: var(--crt-cyan-glow); } - strong, b { + strong, + b { color: var(--color-retro-green); text-shadow: var(--crt-text-glow); } @@ -959,21 +1146,27 @@ } @keyframes retro-glitch { - 0%, 100% { + + 0%, + 100% { transform: translateX(0); filter: none; } + 20% { transform: translateX(-1px); filter: hue-rotate(10deg); } + 40% { transform: translateX(1px); filter: hue-rotate(-10deg); } + 60% { transform: translateX(-1px); } + 80% { transform: translateX(1px); } @@ -987,45 +1180,147 @@ /* Blinking cursor animation */ @keyframes cursor-blink { - 0%, 50% { opacity: 1; } - 51%, 100% { opacity: 0; } + + 0%, + 50% { + opacity: 1; + } + + 51%, + 100% { + opacity: 0; + } } /* Override Tailwind classes for retro theme */ -[data-theme="retro-console"] .bg-surface-900 { background-color: var(--color-surface-900); } -[data-theme="retro-console"] .bg-surface-800 { background-color: var(--color-surface-800); } -[data-theme="retro-console"] .bg-surface-700 { background-color: var(--color-surface-700); } -[data-theme="retro-console"] .bg-surface-600 { background-color: var(--color-surface-600); } -[data-theme="retro-console"] .text-surface-100 { color: var(--color-surface-100); } -[data-theme="retro-console"] .text-surface-200 { color: var(--color-surface-200); } -[data-theme="retro-console"] .text-surface-300 { color: var(--color-surface-300); } -[data-theme="retro-console"] .text-surface-400 { color: var(--color-surface-400); } -[data-theme="retro-console"] .text-surface-500 { color: var(--color-surface-500); } -[data-theme="retro-console"] .border-surface-700 { border-color: var(--color-surface-700); } -[data-theme="retro-console"] .border-surface-600 { border-color: var(--color-surface-600); } -[data-theme="retro-console"] .bg-accent-600 { background-color: var(--color-accent-600); } -[data-theme="retro-console"] .bg-accent-700 { background-color: var(--color-accent-700); } -[data-theme="retro-console"] .text-accent-400 { color: var(--color-accent-400); } -[data-theme="retro-console"] .text-accent-500 { color: var(--color-accent-500); } -[data-theme="retro-console"] .text-accent-600 { color: var(--color-accent-600); } -[data-theme="retro-console"] .ring-accent-500 { --tw-ring-color: var(--color-accent-500); } -[data-theme="retro-console"] .focus\:ring-accent-500:focus { --tw-ring-color: var(--color-accent-500); } +[data-theme="retro-console"] .bg-surface-900 { + background-color: var(--color-surface-900); +} + +[data-theme="retro-console"] .bg-surface-800 { + background-color: var(--color-surface-800); +} + +[data-theme="retro-console"] .bg-surface-700 { + background-color: var(--color-surface-700); +} + +[data-theme="retro-console"] .bg-surface-600 { + background-color: var(--color-surface-600); +} + +[data-theme="retro-console"] .text-surface-100 { + color: var(--color-surface-100); +} + +[data-theme="retro-console"] .text-surface-200 { + color: var(--color-surface-200); +} + +[data-theme="retro-console"] .text-surface-300 { + color: var(--color-surface-300); +} + +[data-theme="retro-console"] .text-surface-400 { + color: var(--color-surface-400); +} + +[data-theme="retro-console"] .text-surface-500 { + color: var(--color-surface-500); +} + +[data-theme="retro-console"] .border-surface-700 { + border-color: var(--color-surface-700); +} + +[data-theme="retro-console"] .border-surface-600 { + border-color: var(--color-surface-600); +} + +[data-theme="retro-console"] .bg-accent-600 { + background-color: var(--color-accent-600); +} + +[data-theme="retro-console"] .bg-accent-700 { + background-color: var(--color-accent-700); +} + +[data-theme="retro-console"] .text-accent-400 { + color: var(--color-accent-400); +} + +[data-theme="retro-console"] .text-accent-500 { + color: var(--color-accent-500); +} + +[data-theme="retro-console"] .text-accent-600 { + color: var(--color-accent-600); +} + +[data-theme="retro-console"] .ring-accent-500 { + --tw-ring-color: var(--color-accent-500); +} + +[data-theme="retro-console"] .focus\:ring-accent-500:focus { + --tw-ring-color: var(--color-accent-500); +} /* Hover states */ -[data-theme="retro-console"] .hover\:bg-surface-600:hover { background-color: var(--color-surface-600); } -[data-theme="retro-console"] .hover\:bg-surface-700:hover { background-color: var(--color-surface-700); } -[data-theme="retro-console"] .hover\:bg-surface-800:hover { background-color: var(--color-surface-800); } -[data-theme="retro-console"] .hover\:bg-accent-700:hover { background-color: var(--color-accent-700); } -[data-theme="retro-console"] .hover\:text-surface-100:hover { color: var(--color-surface-100); } +[data-theme="retro-console"] .hover\:bg-surface-600:hover { + background-color: var(--color-surface-600); +} + +[data-theme="retro-console"] .hover\:bg-surface-700:hover { + background-color: var(--color-surface-700); +} + +[data-theme="retro-console"] .hover\:bg-surface-800:hover { + background-color: var(--color-surface-800); +} + +[data-theme="retro-console"] .hover\:bg-accent-700:hover { + background-color: var(--color-accent-700); +} + +[data-theme="retro-console"] .hover\:text-surface-100:hover { + color: var(--color-surface-100); +} /* Special retro status colors */ -[data-theme="retro-console"] .text-green-400 { color: var(--color-retro-green); text-shadow: var(--crt-text-glow); } -[data-theme="retro-console"] .text-amber-400 { color: var(--color-retro-amber); text-shadow: var(--crt-amber-glow); } -[data-theme="retro-console"] .text-red-400 { color: var(--color-retro-red); text-shadow: 0 0 4px rgba(255, 45, 45, 0.5); } -[data-theme="retro-console"] .text-blue-400 { color: var(--color-retro-cyan); text-shadow: var(--crt-cyan-glow); } -[data-theme="retro-console"] .text-purple-400 { color: var(--color-retro-purple); text-shadow: 0 0 4px rgba(191, 0, 255, 0.5); } -[data-theme="retro-console"] .text-yellow-400 { color: var(--color-retro-amber); text-shadow: var(--crt-amber-glow); } -[data-theme="retro-console"] .text-pink-400 { color: #ff69b4; text-shadow: 0 0 4px rgba(255, 105, 180, 0.5); } +[data-theme="retro-console"] .text-green-400 { + color: var(--color-retro-green); + text-shadow: var(--crt-text-glow); +} + +[data-theme="retro-console"] .text-amber-400 { + color: var(--color-retro-amber); + text-shadow: var(--crt-amber-glow); +} + +[data-theme="retro-console"] .text-red-400 { + color: var(--color-retro-red); + text-shadow: 0 0 4px rgba(255, 45, 45, 0.5); +} + +[data-theme="retro-console"] .text-blue-400 { + color: var(--color-retro-cyan); + text-shadow: var(--crt-cyan-glow); +} + +[data-theme="retro-console"] .text-purple-400 { + color: var(--color-retro-purple); + text-shadow: 0 0 4px rgba(191, 0, 255, 0.5); +} + +[data-theme="retro-console"] .text-yellow-400 { + color: var(--color-retro-amber); + text-shadow: var(--crt-amber-glow); +} + +[data-theme="retro-console"] .text-pink-400 { + color: #ff69b4; + text-shadow: 0 0 4px rgba(255, 105, 180, 0.5); +} /* Select styling for Retro theme */ [data-theme="retro-console"] select.input { @@ -1049,14 +1344,28 @@ /* Undertale soul float animation */ @keyframes ut-soul-float { - 0%, 100% { transform: translateY(0px); } - 50% { transform: translateY(-5px); } + + 0%, + 100% { + transform: translateY(0px); + } + + 50% { + transform: translateY(-5px); + } } /* Undertale heart blink animation */ @keyframes ut-heart-blink { - 0%, 100% { opacity: 1; } - 50% { opacity: 0; } + + 0%, + 100% { + opacity: 1; + } + + 50% { + opacity: 0; + } } [data-theme="fallen-down"] .ut-soul-float { @@ -1192,59 +1501,155 @@ [data-theme="fallen-down"] .bg-surface-700 { background-color: #000000 !important; } -[data-theme="fallen-down"] .bg-surface-600 { background-color: #0a0a0a !important; } -[data-theme="fallen-down"] .bg-surface-500 { background-color: #1a1a1a !important; } -[data-theme="fallen-down"] .text-surface-50 { color: #FFFFFF !important; } -[data-theme="fallen-down"] .text-surface-100 { color: #FFFFFF !important; } -[data-theme="fallen-down"] .text-surface-200 { color: #E0E0E0 !important; } -[data-theme="fallen-down"] .text-surface-300 { color: #C0C0C0 !important; } -[data-theme="fallen-down"] .text-surface-400 { color: #808080 !important; } -[data-theme="fallen-down"] .text-surface-500 { color: #606060 !important; } -[data-theme="fallen-down"] .border-surface-700 { border-color: #FFFFFF !important; } -[data-theme="fallen-down"] .border-surface-600 { border-color: #808080 !important; } + +[data-theme="fallen-down"] .bg-surface-600 { + background-color: #0a0a0a !important; +} + +[data-theme="fallen-down"] .bg-surface-500 { + background-color: #1a1a1a !important; +} + +[data-theme="fallen-down"] .text-surface-50 { + color: #FFFFFF !important; +} + +[data-theme="fallen-down"] .text-surface-100 { + color: #FFFFFF !important; +} + +[data-theme="fallen-down"] .text-surface-200 { + color: #E0E0E0 !important; +} + +[data-theme="fallen-down"] .text-surface-300 { + color: #C0C0C0 !important; +} + +[data-theme="fallen-down"] .text-surface-400 { + color: #808080 !important; +} + +[data-theme="fallen-down"] .text-surface-500 { + color: #606060 !important; +} + +[data-theme="fallen-down"] .border-surface-700 { + border-color: #FFFFFF !important; +} + +[data-theme="fallen-down"] .border-surface-600 { + border-color: #808080 !important; +} /* Accent colors - all yellow, no blue */ -[data-theme="fallen-down"] .bg-accent-500 { background-color: #FFFF00; } -[data-theme="fallen-down"] .bg-accent-600 { background-color: #FFFF00; } -[data-theme="fallen-down"] .bg-accent-700 { background-color: #E6E600; } -[data-theme="fallen-down"] .text-accent-400 { color: #FFFF00; } -[data-theme="fallen-down"] .text-accent-500 { color: #FFFF00; } -[data-theme="fallen-down"] .text-accent-600 { color: #FFFF00; } +[data-theme="fallen-down"] .bg-accent-500 { + background-color: #FFFF00; +} + +[data-theme="fallen-down"] .bg-accent-600 { + background-color: #FFFF00; +} + +[data-theme="fallen-down"] .bg-accent-700 { + background-color: #E6E600; +} + +[data-theme="fallen-down"] .text-accent-400 { + color: #FFFF00; +} + +[data-theme="fallen-down"] .text-accent-500 { + color: #FFFF00; +} + +[data-theme="fallen-down"] .text-accent-600 { + color: #FFFF00; +} /* Focus rings - yellow instead of blue */ -[data-theme="fallen-down"] .ring-accent-500 { --tw-ring-color: #FFFF00; } -[data-theme="fallen-down"] .focus\:ring-accent-500:focus { --tw-ring-color: #FFFF00; } -[data-theme="fallen-down"] *:focus { outline-color: #FFFF00; } -[data-theme="fallen-down"] [class*="focus:ring"] { --tw-ring-color: #FFFF00 !important; } +[data-theme="fallen-down"] .ring-accent-500 { + --tw-ring-color: #FFFF00; +} + +[data-theme="fallen-down"] .focus\:ring-accent-500:focus { + --tw-ring-color: #FFFF00; +} + +[data-theme="fallen-down"] *:focus { + outline-color: #FFFF00; +} + +[data-theme="fallen-down"] [class*="focus:ring"] { + --tw-ring-color: #FFFF00 !important; +} /* Override ALL blue colors to yellow (Undertale has no blue UI) */ [data-theme="fallen-down"] .text-blue-400, [data-theme="fallen-down"] .text-blue-500, -[data-theme="fallen-down"] .text-blue-600 { color: #FFFF00; } +[data-theme="fallen-down"] .text-blue-600 { + color: #FFFF00; +} + [data-theme="fallen-down"] .bg-blue-400, [data-theme="fallen-down"] .bg-blue-500, -[data-theme="fallen-down"] .bg-blue-600 { background-color: #FFFF00; color: #000000; } +[data-theme="fallen-down"] .bg-blue-600 { + background-color: #FFFF00; + color: #000000; +} + [data-theme="fallen-down"] .border-blue-400, [data-theme="fallen-down"] .border-blue-500, -[data-theme="fallen-down"] .border-blue-600 { border-color: #FFFF00; } +[data-theme="fallen-down"] .border-blue-600 { + border-color: #FFFF00; +} /* Override indigo/purple to yellow as well */ [data-theme="fallen-down"] .text-indigo-400, [data-theme="fallen-down"] .text-indigo-500, -[data-theme="fallen-down"] .bg-indigo-900 { color: #FFFF00; background-color: transparent; } -[data-theme="fallen-down"] .border-indigo-400 { border-color: #FFFF00; } +[data-theme="fallen-down"] .bg-indigo-900 { + color: #FFFF00; + background-color: transparent; +} + +[data-theme="fallen-down"] .border-indigo-400 { + border-color: #FFFF00; +} /* Links - yellow like Undertale's save points */ -[data-theme="fallen-down"] a { color: #FFFF00; } -[data-theme="fallen-down"] a:hover { color: #FFFFFF; } +[data-theme="fallen-down"] a { + color: #FFFF00; +} + +[data-theme="fallen-down"] a:hover { + color: #FFFFFF; +} /* Hover states */ -[data-theme="fallen-down"] .hover\:bg-surface-600:hover { background-color: #1a1a1a !important; } -[data-theme="fallen-down"] .hover\:bg-surface-700:hover { background-color: #0a0a0a !important; } -[data-theme="fallen-down"] .hover\:bg-surface-800:hover { background-color: #000000 !important; } -[data-theme="fallen-down"] .hover\:bg-accent-700:hover { background-color: #FFFF00 !important; color: #000000 !important; } -[data-theme="fallen-down"] .hover\:text-surface-100:hover { color: #FFFFFF !important; } -[data-theme="fallen-down"] .hover\:text-accent-400:hover { color: #FFFF00 !important; } +[data-theme="fallen-down"] .hover\:bg-surface-600:hover { + background-color: #1a1a1a !important; +} + +[data-theme="fallen-down"] .hover\:bg-surface-700:hover { + background-color: #0a0a0a !important; +} + +[data-theme="fallen-down"] .hover\:bg-surface-800:hover { + background-color: #000000 !important; +} + +[data-theme="fallen-down"] .hover\:bg-accent-700:hover { + background-color: #FFFF00 !important; + color: #000000 !important; +} + +[data-theme="fallen-down"] .hover\:text-surface-100:hover { + color: #FFFFFF !important; +} + +[data-theme="fallen-down"] .hover\:text-accent-400:hover { + color: #FFFF00 !important; +} /* Ensure body and root backgrounds are pure black */ [data-theme="fallen-down"], @@ -1288,10 +1693,21 @@ } /* Undertale status colors - limited palette */ -[data-theme="fallen-down"] .text-green-400 { color: #00FF00; } -[data-theme="fallen-down"] .text-amber-400 { color: #FFA500; } -[data-theme="fallen-down"] .text-red-400 { color: #FF0000; } -[data-theme="fallen-down"] .text-yellow-400 { color: #FFFF00; } +[data-theme="fallen-down"] .text-green-400 { + color: #00FF00; +} + +[data-theme="fallen-down"] .text-amber-400 { + color: #FFA500; +} + +[data-theme="fallen-down"] .text-red-400 { + color: #FF0000; +} + +[data-theme="fallen-down"] .text-yellow-400 { + color: #FFFF00; +} /* Primary button - yellow like FIGHT/ACT buttons */ [data-theme="fallen-down"] .btn-primary { @@ -1299,6 +1715,7 @@ border: 2px solid #FFFF00; color: #FFFF00; } + [data-theme="fallen-down"] .btn-primary:hover { background-color: #FFFF00; color: #000000; @@ -1323,6 +1740,7 @@ [data-theme="fallen-down"] input[type="range"]::-webkit-slider-thumb { background-color: #FFFF00; } + [data-theme="fallen-down"] input[type="range"]::-moz-range-thumb { background-color: #FFFF00; } @@ -1348,54 +1766,169 @@ ============================================= */ /* Background overrides - INVERT for light theme (dark numbers = light colors) */ -[data-theme="light"] .bg-surface-900 { background-color: var(--color-surface-50); } -[data-theme="light"] .bg-surface-850 { background-color: var(--color-surface-100); } -[data-theme="light"] .bg-surface-800 { background-color: var(--color-surface-100); } -[data-theme="light"] .bg-surface-700 { background-color: var(--color-surface-200); } -[data-theme="light"] .bg-surface-600 { background-color: var(--color-surface-300); } -[data-theme="light"] .bg-surface-500 { background-color: var(--color-surface-400); } -[data-theme="light"] .bg-surface-400 { background-color: var(--color-surface-400); } -[data-theme="light"] .bg-surface-300 { background-color: var(--color-surface-300); } -[data-theme="light"] .bg-surface-200 { background-color: var(--color-surface-200); } -[data-theme="light"] .bg-surface-100 { background-color: var(--color-surface-100); } -[data-theme="light"] .bg-surface-50 { background-color: var(--color-surface-50); } +[data-theme="light"] .bg-surface-900 { + background-color: var(--color-surface-50); +} + +[data-theme="light"] .bg-surface-850 { + background-color: var(--color-surface-100); +} + +[data-theme="light"] .bg-surface-800 { + background-color: var(--color-surface-100); +} + +[data-theme="light"] .bg-surface-700 { + background-color: var(--color-surface-200); +} + +[data-theme="light"] .bg-surface-600 { + background-color: var(--color-surface-300); +} + +[data-theme="light"] .bg-surface-500 { + background-color: var(--color-surface-400); +} + +[data-theme="light"] .bg-surface-400 { + background-color: var(--color-surface-400); +} + +[data-theme="light"] .bg-surface-300 { + background-color: var(--color-surface-300); +} + +[data-theme="light"] .bg-surface-200 { + background-color: var(--color-surface-200); +} + +[data-theme="light"] .bg-surface-100 { + background-color: var(--color-surface-100); +} + +[data-theme="light"] .bg-surface-50 { + background-color: var(--color-surface-50); +} /* Text color overrides - INVERT for light theme (light numbers = dark colors) */ -[data-theme="light"] .text-surface-50 { color: var(--color-surface-950); } -[data-theme="light"] .text-surface-100 { color: var(--color-surface-900); } -[data-theme="light"] .text-surface-200 { color: var(--color-surface-800); } -[data-theme="light"] .text-surface-300 { color: var(--color-surface-700); } -[data-theme="light"] .text-surface-400 { color: var(--color-surface-500); } -[data-theme="light"] .text-surface-500 { color: var(--color-surface-500); } -[data-theme="light"] .text-surface-600 { color: var(--color-surface-400); } -[data-theme="light"] .text-surface-700 { color: var(--color-surface-300); } -[data-theme="light"] .text-surface-800 { color: var(--color-surface-200); } -[data-theme="light"] .text-surface-900 { color: var(--color-surface-100); } +[data-theme="light"] .text-surface-50 { + color: var(--color-surface-950); +} + +[data-theme="light"] .text-surface-100 { + color: var(--color-surface-900); +} + +[data-theme="light"] .text-surface-200 { + color: var(--color-surface-800); +} + +[data-theme="light"] .text-surface-300 { + color: var(--color-surface-700); +} + +[data-theme="light"] .text-surface-400 { + color: var(--color-surface-500); +} + +[data-theme="light"] .text-surface-500 { + color: var(--color-surface-500); +} + +[data-theme="light"] .text-surface-600 { + color: var(--color-surface-400); +} + +[data-theme="light"] .text-surface-700 { + color: var(--color-surface-300); +} + +[data-theme="light"] .text-surface-800 { + color: var(--color-surface-200); +} + +[data-theme="light"] .text-surface-900 { + color: var(--color-surface-100); +} /* Border color overrides */ -[data-theme="light"] .border-surface-700 { border-color: var(--color-surface-300); } -[data-theme="light"] .border-surface-600 { border-color: var(--color-surface-400); } -[data-theme="light"] .border-surface-500 { border-color: var(--color-surface-400); } +[data-theme="light"] .border-surface-700 { + border-color: var(--color-surface-300); +} + +[data-theme="light"] .border-surface-600 { + border-color: var(--color-surface-400); +} + +[data-theme="light"] .border-surface-500 { + border-color: var(--color-surface-400); +} /* Accent color overrides */ -[data-theme="light"] .bg-accent-600 { background-color: var(--color-accent-600); } -[data-theme="light"] .bg-accent-700 { background-color: var(--color-accent-700); } -[data-theme="light"] .text-accent-400 { color: var(--color-accent-400); } -[data-theme="light"] .text-accent-500 { color: var(--color-accent-500); } -[data-theme="light"] .text-accent-600 { color: var(--color-accent-600); } -[data-theme="light"] .border-accent-500 { border-color: var(--color-accent-500); } -[data-theme="light"] .ring-accent-500 { --tw-ring-color: var(--color-accent-500); } -[data-theme="light"] .focus\:ring-accent-500:focus { --tw-ring-color: var(--color-accent-500); } +[data-theme="light"] .bg-accent-600 { + background-color: var(--color-accent-600); +} + +[data-theme="light"] .bg-accent-700 { + background-color: var(--color-accent-700); +} + +[data-theme="light"] .text-accent-400 { + color: var(--color-accent-400); +} + +[data-theme="light"] .text-accent-500 { + color: var(--color-accent-500); +} + +[data-theme="light"] .text-accent-600 { + color: var(--color-accent-600); +} + +[data-theme="light"] .border-accent-500 { + border-color: var(--color-accent-500); +} + +[data-theme="light"] .ring-accent-500 { + --tw-ring-color: var(--color-accent-500); +} + +[data-theme="light"] .focus\:ring-accent-500:focus { + --tw-ring-color: var(--color-accent-500); +} /* Hover state overrides - backgrounds get lighter, text gets darker */ -[data-theme="light"] .hover\:bg-surface-600:hover { background-color: var(--color-surface-300); } -[data-theme="light"] .hover\:bg-surface-700:hover { background-color: var(--color-surface-200); } -[data-theme="light"] .hover\:bg-surface-800:hover { background-color: var(--color-surface-200); } -[data-theme="light"] .hover\:bg-accent-700:hover { background-color: var(--color-accent-400); } -[data-theme="light"] .hover\:text-surface-100:hover { color: var(--color-surface-950); } -[data-theme="light"] .hover\:text-surface-200:hover { color: var(--color-surface-900); } -[data-theme="light"] .hover\:text-surface-300:hover { color: var(--color-surface-800); } -[data-theme="light"] .hover\:text-accent-400:hover { color: var(--color-accent-700); } +[data-theme="light"] .hover\:bg-surface-600:hover { + background-color: var(--color-surface-300); +} + +[data-theme="light"] .hover\:bg-surface-700:hover { + background-color: var(--color-surface-200); +} + +[data-theme="light"] .hover\:bg-surface-800:hover { + background-color: var(--color-surface-200); +} + +[data-theme="light"] .hover\:bg-accent-700:hover { + background-color: var(--color-accent-400); +} + +[data-theme="light"] .hover\:text-surface-100:hover { + color: var(--color-surface-950); +} + +[data-theme="light"] .hover\:text-surface-200:hover { + color: var(--color-surface-900); +} + +[data-theme="light"] .hover\:text-surface-300:hover { + color: var(--color-surface-800); +} + +[data-theme="light"] .hover\:text-accent-400:hover { + color: var(--color-accent-700); +} /* Nav tab active state */ [data-theme="light"] .nav-tab-active { @@ -1403,13 +1936,33 @@ } /* Light theme status colors (high contrast on paper) */ -[data-theme="light"] .text-green-400 { color: #15803d; } -[data-theme="light"] .text-amber-400 { color: #b45309; } -[data-theme="light"] .text-red-400 { color: #dc2626; } -[data-theme="light"] .text-blue-400 { color: #1d4ed8; } -[data-theme="light"] .text-purple-400 { color: #7c3aed; } -[data-theme="light"] .text-yellow-400 { color: #a16207; } -[data-theme="light"] .text-pink-400 { color: #be185d; } +[data-theme="light"] .text-green-400 { + color: #15803d; +} + +[data-theme="light"] .text-amber-400 { + color: #b45309; +} + +[data-theme="light"] .text-red-400 { + color: #dc2626; +} + +[data-theme="light"] .text-blue-400 { + color: #1d4ed8; +} + +[data-theme="light"] .text-purple-400 { + color: #7c3aed; +} + +[data-theme="light"] .text-yellow-400 { + color: #a16207; +} + +[data-theme="light"] .text-pink-400 { + color: #be185d; +} /* Light theme component overrides */ [data-theme="light"] .sidebar { @@ -1517,7 +2070,8 @@ /* Light theme story entry backgrounds (opacity variants) */ [data-theme="light"] .bg-surface-800\/50 { - background-color: rgba(222, 218, 211, 0.5) !important; /* warm paper with opacity */ + background-color: rgba(222, 218, 211, 0.5) !important; + /* warm paper with opacity */ } [data-theme="light"] .bg-surface-800\/30 { @@ -1583,54 +2137,169 @@ ============================================= */ /* Background overrides - INVERT for light theme */ -[data-theme="light-solarized"] .bg-surface-900 { background-color: var(--color-surface-50); } -[data-theme="light-solarized"] .bg-surface-850 { background-color: var(--color-surface-100); } -[data-theme="light-solarized"] .bg-surface-800 { background-color: var(--color-surface-100); } -[data-theme="light-solarized"] .bg-surface-700 { background-color: var(--color-surface-200); } -[data-theme="light-solarized"] .bg-surface-600 { background-color: var(--color-surface-300); } -[data-theme="light-solarized"] .bg-surface-500 { background-color: var(--color-surface-400); } -[data-theme="light-solarized"] .bg-surface-400 { background-color: var(--color-surface-400); } -[data-theme="light-solarized"] .bg-surface-300 { background-color: var(--color-surface-300); } -[data-theme="light-solarized"] .bg-surface-200 { background-color: var(--color-surface-200); } -[data-theme="light-solarized"] .bg-surface-100 { background-color: var(--color-surface-100); } -[data-theme="light-solarized"] .bg-surface-50 { background-color: var(--color-surface-50); } +[data-theme="light-solarized"] .bg-surface-900 { + background-color: var(--color-surface-50); +} + +[data-theme="light-solarized"] .bg-surface-850 { + background-color: var(--color-surface-100); +} + +[data-theme="light-solarized"] .bg-surface-800 { + background-color: var(--color-surface-100); +} + +[data-theme="light-solarized"] .bg-surface-700 { + background-color: var(--color-surface-200); +} + +[data-theme="light-solarized"] .bg-surface-600 { + background-color: var(--color-surface-300); +} + +[data-theme="light-solarized"] .bg-surface-500 { + background-color: var(--color-surface-400); +} + +[data-theme="light-solarized"] .bg-surface-400 { + background-color: var(--color-surface-400); +} + +[data-theme="light-solarized"] .bg-surface-300 { + background-color: var(--color-surface-300); +} + +[data-theme="light-solarized"] .bg-surface-200 { + background-color: var(--color-surface-200); +} + +[data-theme="light-solarized"] .bg-surface-100 { + background-color: var(--color-surface-100); +} + +[data-theme="light-solarized"] .bg-surface-50 { + background-color: var(--color-surface-50); +} /* Text color overrides - INVERT for light theme */ -[data-theme="light-solarized"] .text-surface-50 { color: var(--color-surface-950); } -[data-theme="light-solarized"] .text-surface-100 { color: var(--color-surface-900); } -[data-theme="light-solarized"] .text-surface-200 { color: var(--color-surface-800); } -[data-theme="light-solarized"] .text-surface-300 { color: var(--color-surface-700); } -[data-theme="light-solarized"] .text-surface-400 { color: var(--color-surface-500); } -[data-theme="light-solarized"] .text-surface-500 { color: var(--color-surface-500); } -[data-theme="light-solarized"] .text-surface-600 { color: var(--color-surface-400); } -[data-theme="light-solarized"] .text-surface-700 { color: var(--color-surface-300); } -[data-theme="light-solarized"] .text-surface-800 { color: var(--color-surface-200); } -[data-theme="light-solarized"] .text-surface-900 { color: var(--color-surface-100); } +[data-theme="light-solarized"] .text-surface-50 { + color: var(--color-surface-950); +} + +[data-theme="light-solarized"] .text-surface-100 { + color: var(--color-surface-900); +} + +[data-theme="light-solarized"] .text-surface-200 { + color: var(--color-surface-800); +} + +[data-theme="light-solarized"] .text-surface-300 { + color: var(--color-surface-700); +} + +[data-theme="light-solarized"] .text-surface-400 { + color: var(--color-surface-500); +} + +[data-theme="light-solarized"] .text-surface-500 { + color: var(--color-surface-500); +} + +[data-theme="light-solarized"] .text-surface-600 { + color: var(--color-surface-400); +} + +[data-theme="light-solarized"] .text-surface-700 { + color: var(--color-surface-300); +} + +[data-theme="light-solarized"] .text-surface-800 { + color: var(--color-surface-200); +} + +[data-theme="light-solarized"] .text-surface-900 { + color: var(--color-surface-100); +} /* Border color overrides */ -[data-theme="light-solarized"] .border-surface-700 { border-color: var(--color-surface-300); } -[data-theme="light-solarized"] .border-surface-600 { border-color: var(--color-surface-400); } -[data-theme="light-solarized"] .border-surface-500 { border-color: var(--color-surface-400); } +[data-theme="light-solarized"] .border-surface-700 { + border-color: var(--color-surface-300); +} + +[data-theme="light-solarized"] .border-surface-600 { + border-color: var(--color-surface-400); +} + +[data-theme="light-solarized"] .border-surface-500 { + border-color: var(--color-surface-400); +} /* Accent color overrides */ -[data-theme="light-solarized"] .bg-accent-600 { background-color: var(--color-accent-600); } -[data-theme="light-solarized"] .bg-accent-700 { background-color: var(--color-accent-700); } -[data-theme="light-solarized"] .text-accent-400 { color: var(--color-accent-400); } -[data-theme="light-solarized"] .text-accent-500 { color: var(--color-accent-500); } -[data-theme="light-solarized"] .text-accent-600 { color: var(--color-accent-600); } -[data-theme="light-solarized"] .border-accent-500 { border-color: var(--color-accent-500); } -[data-theme="light-solarized"] .ring-accent-500 { --tw-ring-color: var(--color-accent-500); } -[data-theme="light-solarized"] .focus\:ring-accent-500:focus { --tw-ring-color: var(--color-accent-500); } +[data-theme="light-solarized"] .bg-accent-600 { + background-color: var(--color-accent-600); +} + +[data-theme="light-solarized"] .bg-accent-700 { + background-color: var(--color-accent-700); +} + +[data-theme="light-solarized"] .text-accent-400 { + color: var(--color-accent-400); +} + +[data-theme="light-solarized"] .text-accent-500 { + color: var(--color-accent-500); +} + +[data-theme="light-solarized"] .text-accent-600 { + color: var(--color-accent-600); +} + +[data-theme="light-solarized"] .border-accent-500 { + border-color: var(--color-accent-500); +} + +[data-theme="light-solarized"] .ring-accent-500 { + --tw-ring-color: var(--color-accent-500); +} + +[data-theme="light-solarized"] .focus\:ring-accent-500:focus { + --tw-ring-color: var(--color-accent-500); +} /* Hover state overrides */ -[data-theme="light-solarized"] .hover\:bg-surface-600:hover { background-color: var(--color-surface-300); } -[data-theme="light-solarized"] .hover\:bg-surface-700:hover { background-color: var(--color-surface-200); } -[data-theme="light-solarized"] .hover\:bg-surface-800:hover { background-color: var(--color-surface-200); } -[data-theme="light-solarized"] .hover\:bg-accent-700:hover { background-color: var(--color-accent-400); } -[data-theme="light-solarized"] .hover\:text-surface-100:hover { color: var(--color-surface-950); } -[data-theme="light-solarized"] .hover\:text-surface-200:hover { color: var(--color-surface-900); } -[data-theme="light-solarized"] .hover\:text-surface-300:hover { color: var(--color-surface-800); } -[data-theme="light-solarized"] .hover\:text-accent-400:hover { color: var(--color-accent-700); } +[data-theme="light-solarized"] .hover\:bg-surface-600:hover { + background-color: var(--color-surface-300); +} + +[data-theme="light-solarized"] .hover\:bg-surface-700:hover { + background-color: var(--color-surface-200); +} + +[data-theme="light-solarized"] .hover\:bg-surface-800:hover { + background-color: var(--color-surface-200); +} + +[data-theme="light-solarized"] .hover\:bg-accent-700:hover { + background-color: var(--color-accent-400); +} + +[data-theme="light-solarized"] .hover\:text-surface-100:hover { + color: var(--color-surface-950); +} + +[data-theme="light-solarized"] .hover\:text-surface-200:hover { + color: var(--color-surface-900); +} + +[data-theme="light-solarized"] .hover\:text-surface-300:hover { + color: var(--color-surface-800); +} + +[data-theme="light-solarized"] .hover\:text-accent-400:hover { + color: var(--color-accent-700); +} /* Nav tab active state */ [data-theme="light-solarized"] .nav-tab-active { @@ -1638,13 +2307,41 @@ } /* Solarized status colors */ -[data-theme="light-solarized"] .text-green-400 { color: #859900; } /* Solarized green */ -[data-theme="light-solarized"] .text-amber-400 { color: #b58900; } /* Solarized yellow */ -[data-theme="light-solarized"] .text-red-400 { color: #dc322f; } /* Solarized red */ -[data-theme="light-solarized"] .text-blue-400 { color: #268bd2; } /* Solarized blue */ -[data-theme="light-solarized"] .text-purple-400 { color: #6c71c4; } /* Solarized violet */ -[data-theme="light-solarized"] .text-yellow-400 { color: #b58900; } /* Solarized yellow */ -[data-theme="light-solarized"] .text-pink-400 { color: #d33682; } /* Solarized magenta */ +[data-theme="light-solarized"] .text-green-400 { + color: #859900; +} + +/* Solarized green */ +[data-theme="light-solarized"] .text-amber-400 { + color: #b58900; +} + +/* Solarized yellow */ +[data-theme="light-solarized"] .text-red-400 { + color: #dc322f; +} + +/* Solarized red */ +[data-theme="light-solarized"] .text-blue-400 { + color: #268bd2; +} + +/* Solarized blue */ +[data-theme="light-solarized"] .text-purple-400 { + color: #6c71c4; +} + +/* Solarized violet */ +[data-theme="light-solarized"] .text-yellow-400 { + color: #b58900; +} + +/* Solarized yellow */ +[data-theme="light-solarized"] .text-pink-400 { + color: #d33682; +} + +/* Solarized magenta */ /* Solarized component overrides */ [data-theme="light-solarized"] .sidebar { @@ -1854,7 +2551,7 @@ scrollbar-width: none; -ms-overflow-style: none; } - + *::-webkit-scrollbar { display: none; } @@ -1923,6 +2620,7 @@ .pb-modal-safe { padding-bottom: calc(1rem + env(safe-area-inset-bottom, 0px)); } + @media (min-width: 640px) { .pb-modal-safe { padding-bottom: 1rem; @@ -1930,14 +2628,17 @@ } /* Edge-to-edge support for Android */ -html, body { +html, +body { /* Ensure the app fills the full viewport including system bars */ min-height: 100vh; - min-height: 100dvh; /* Dynamic viewport height for mobile */ + min-height: 100dvh; + /* Dynamic viewport height for mobile */ } /* Touch-friendly tap targets (minimum 44px per Apple/Google guidelines) */ @media (max-width: 768px) { + /* Larger buttons on mobile */ .btn { @apply min-h-[44px]; @@ -1965,7 +2666,9 @@ html, body { } /* Prevent text selection on interactive elements */ - button, .btn, a { + button, + .btn, + a { -webkit-tap-highlight-color: transparent; -webkit-touch-callout: none; user-select: none; @@ -1985,6 +2688,7 @@ html, body { /* Extra small screens (phones in portrait) */ @media (max-width: 475px) { + /* Slightly smaller text for very small screens */ .story-text { @apply text-[15px]; @@ -2000,6 +2704,7 @@ html, body { /* Touch feedback */ @media (hover: none) and (pointer: coarse) { + /* Add touch ripple effect */ .btn:active, button:active { @@ -2039,7 +2744,7 @@ html, body { pointer-events: none; } - .nav-tab > * { + .nav-tab>* { position: relative; z-index: 1; } @@ -2064,6 +2769,7 @@ html, body { /* Landscape mode adjustments */ @media (max-height: 500px) and (orientation: landscape) { + /* Reduce header height */ header { @apply h-10; @@ -2077,7 +2783,10 @@ html, body { /* Prevent zoom on input focus (iOS) */ @media screen and (-webkit-min-device-pixel-ratio: 0) { - input, textarea, select { + + input, + textarea, + select { font-size: 16px !important; } } @@ -2114,7 +2823,8 @@ body { .prose-content.visual-prose-container .visual-prose-entry p, .visual-prose-container .visual-prose-entry p { margin-top: 0; - margin-bottom: 0.75em; /* Tighter paragraph spacing */ + margin-bottom: 0.75em; + /* Tighter paragraph spacing */ } .prose-content.visual-prose-container .visual-prose-entry p:last-child, @@ -2123,19 +2833,19 @@ body { } /* Reset div margins - AI should use padding/margin in styles if needed */ -.prose-content.visual-prose-container .visual-prose-entry > div, -.visual-prose-container .visual-prose-entry > div { +.prose-content.visual-prose-container .visual-prose-entry>div, +.visual-prose-container .visual-prose-entry>div { margin-top: 0.75em; margin-bottom: 0.75em; } -.prose-content.visual-prose-container .visual-prose-entry > div:first-child, -.visual-prose-container .visual-prose-entry > div:first-child { +.prose-content.visual-prose-container .visual-prose-entry>div:first-child, +.visual-prose-container .visual-prose-entry>div:first-child { margin-top: 0; } -.prose-content.visual-prose-container .visual-prose-entry > div:last-child, -.visual-prose-container .visual-prose-entry > div:last-child { +.prose-content.visual-prose-container .visual-prose-entry>div:last-child, +.visual-prose-container .visual-prose-entry>div:last-child { margin-bottom: 0; } @@ -2162,8 +2872,16 @@ body { } @keyframes visual-prose-cursor { - 0%, 50% { opacity: 1; } - 51%, 100% { opacity: 0; } + + 0%, + 50% { + opacity: 1; + } + + 51%, + 100% { + opacity: 0; + } } /* ========================================== @@ -2218,7 +2936,9 @@ body { } @keyframes inline-spin { - to { transform: rotate(360deg); } + to { + transform: rotate(360deg); + } } /* Placeholder icon (for failed state) */ @@ -2260,8 +2980,15 @@ body { } @keyframes status-pulse { - 0%, 100% { opacity: 0.6; } - 50% { opacity: 1; } + + 0%, + 100% { + opacity: 0.6; + } + + 50% { + opacity: 1; + } } /* Generated inline image container */ @@ -2454,5 +3181,4 @@ body { .inline-image-edit-actions .generate-btn:hover { background: var(--color-accent-500); -} - +} \ No newline at end of file diff --git a/src/lib/components/intro/WelcomeScreen.svelte b/src/lib/components/intro/WelcomeScreen.svelte new file mode 100644 index 0000000..5c47ca2 --- /dev/null +++ b/src/lib/components/intro/WelcomeScreen.svelte @@ -0,0 +1,376 @@ + + +
+ +
+

Welcome to Aventuras

+

+ {#if step === "interface"} + Customize your reading environment + {:else if step === "select"} + Choose your AI provider to get started + {:else} + {@const p = getSelectedProviderInfo()} + Configure {p?.name ?? 'Provider'} + {/if} +

+
+ +
+ + {#if step === "interface"} +
+ + + + +
+ + settings.setTheme(v as ThemeId)}> + + {themes.find((t) => t.value === settings.uiSettings.theme)?.label ?? "Select a theme"} + + + {#each themes as theme} + {theme.label} + {/each} + + +
+ + +
+ +
+ {#each ["small", "medium", "large"] as size} + + {/each} +
+
+ + +
+
+ + settings.updateTranslationSettings({ enabled: v })} + /> +
+ + {#if settings.translationSettings.enabled} +
+
+ + settings.updateTranslationSettings({ targetLanguage: v })}> + + {getSupportedLanguages().find(l => l.code === settings.translationSettings.targetLanguage)?.name ?? "Select language"} + + + {#each getSupportedLanguages() as lang} + {lang.name} + {/each} + + +
+ +
+ + settings.updateTranslationSettings({ translateUserInput: v })} + /> +
+ +
+ + settings.updateTranslationSettings({ translateWorldState: v })} + /> +
+
+ {/if} +
+ +
+ + + +
+
+ + {:else if step === "select"} +
+
+ {#each providers as provider} + + {/each} +
+ +
+ +
+
+ + {:else} + + {@const provider = getSelectedProviderInfo()} + {#if provider} +
+ + +
+ +
+ + Setup +
+
+
+ + {#if provider.requiresKey} +
+
+ + {#if provider.signupUrl} + + Get a key + + {/if} +
+
+ + + +
+
+ {:else} +

+ {provider.description} +

+ {/if} + + {#if provider.note} +
+ Note: {provider.note} +
+ {/if} + + {#if error} +
+ {error} +
+ {/if} + + +
+
+
+ {/if} + {/if} +
+
diff --git a/src/lib/components/layout/Sidebar.svelte b/src/lib/components/layout/Sidebar.svelte index f50d0bc..861ec5b 100644 --- a/src/lib/components/layout/Sidebar.svelte +++ b/src/lib/components/layout/Sidebar.svelte @@ -1,6 +1,5 @@ - - - diff --git a/src/lib/components/settings/ProviderSetupModal.svelte b/src/lib/components/settings/ProviderSetupModal.svelte deleted file mode 100644 index 9bc8df8..0000000 --- a/src/lib/components/settings/ProviderSetupModal.svelte +++ /dev/null @@ -1,455 +0,0 @@ - - -{#if isOpen} - -{/if} - - diff --git a/src/lib/components/story/LibraryView.svelte b/src/lib/components/story/LibraryView.svelte index ae6afda..6c5c055 100644 --- a/src/lib/components/story/LibraryView.svelte +++ b/src/lib/components/story/LibraryView.svelte @@ -7,7 +7,6 @@ BookOpen, Trash2, Clock, - Sparkles, Upload, RefreshCw, Archive, @@ -15,6 +14,11 @@ } from "lucide-svelte"; import SetupWizard from "../wizard/SetupWizard.svelte"; + import { Button } from "$lib/components/ui/button"; + import * as Card from "$lib/components/ui/card"; + import { Badge } from "$lib/components/ui/badge"; + import { Input } from "$lib/components/ui/input"; + // File input for import (HTML-based for mobile compatibility) let importFileInput: HTMLInputElement; @@ -61,19 +65,19 @@ function getGenreColor(genre: string | null): string { switch (genre) { case "Fantasy": - return "bg-purple-500/20 text-purple-400"; + return "bg-purple-500/15 text-purple-700 dark:text-purple-400 border-purple-500/20"; case "Sci-Fi": - return "bg-cyan-500/20 text-cyan-400"; + return "bg-cyan-500/15 text-cyan-700 dark:text-cyan-400 border-cyan-500/20"; case "Mystery": - return "bg-amber-500/20 text-amber-400"; + return "bg-amber-500/15 text-amber-700 dark:text-amber-400 border-amber-500/20"; case "Horror": - return "bg-red-500/20 text-red-400"; + return "bg-red-500/15 text-red-700 dark:text-red-400 border-red-500/20"; case "Slice of Life": - return "bg-green-500/20 text-green-400"; + return "bg-green-500/15 text-green-700 dark:text-green-400 border-green-500/20"; case "Historical": - return "bg-orange-500/20 text-orange-400"; + return "bg-orange-500/15 text-orange-700 dark:text-orange-400 border-orange-500/20"; default: - return "bg-surface-700 text-surface-400"; + return "bg-secondary text-secondary-foreground border-border"; } } @@ -113,42 +117,53 @@ } -
-
+
+
-
-
-

+
+
+

Story Library

-

+

Your adventures await

-
- - + - + + + + - + + +
{#if importError} -
+
{importError}
{/if} @@ -178,24 +198,22 @@
- -

- No stories yet -

-

- Create your first adventure to get started +

+ +
+

No stories yet

+

+ Create your first adventure to get started. You can also import + existing stories.

- +
{:else}
{#each story.allStories as s (s.id)}
openStory(s.id)} onkeydown={(e) => e.key === "Enter" && openStory(s.id)} - class="card group cursor-pointer text-left transition-colors hover:border-accent-500/50 hover:bg-surface-700/50 active:bg-surface-700 min-h-[80px]" + class="h-full" > -
-
-

- {s.title} -

- {#if s.genre} - + +
+ - {s.genre} - + {s.title} + + +
+ {#if s.genre} +
+ + {s.genre} + +
{/if} -
- -
- {#if s.description} -

- {s.description} -

- {/if} -
- - Updated {formatDate(s.updatedAt)} -
+ + + {#if s.description} +

+ {s.description} +

+ {:else} +

+ No description +

+ {/if} +
+ +
+ + Updated {formatDate(s.updatedAt)} +
+
+
{/each}
@@ -250,7 +283,7 @@ href="https://discord.gg/DqVzhSPC46" target="_blank" rel="noopener noreferrer" - class="hidden sm:flex fixed bottom-safe-4 left-safe-4 items-center gap-2 rounded-lg bg-[#5865F2] px-3 py-2 text-sm text-white shadow-lg transition-all hover:bg-[#4752C4] hover:scale-105" + class="hidden sm:flex fixed bottom-6 left-6 items-center gap-2 rounded-lg bg-secondary px-3 py-2 text-sm text-secondary-foreground shadow-lg transition-all hover:bg-secondary/80 hover:scale-105 z-40" > + import { Avatar as AvatarPrimitive } from "bits-ui"; + import { cn } from "$lib/utils/cn.js"; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: AvatarPrimitive.FallbackProps = $props(); + + + diff --git a/src/lib/components/ui/avatar/avatar-image.svelte b/src/lib/components/ui/avatar/avatar-image.svelte new file mode 100644 index 0000000..18021d8 --- /dev/null +++ b/src/lib/components/ui/avatar/avatar-image.svelte @@ -0,0 +1,12 @@ + + + diff --git a/src/lib/components/ui/avatar/avatar.svelte b/src/lib/components/ui/avatar/avatar.svelte new file mode 100644 index 0000000..a904bca --- /dev/null +++ b/src/lib/components/ui/avatar/avatar.svelte @@ -0,0 +1,16 @@ + + + diff --git a/src/lib/components/ui/avatar/index.ts b/src/lib/components/ui/avatar/index.ts new file mode 100644 index 0000000..d06457b --- /dev/null +++ b/src/lib/components/ui/avatar/index.ts @@ -0,0 +1,13 @@ +import Root from "./avatar.svelte"; +import Image from "./avatar-image.svelte"; +import Fallback from "./avatar-fallback.svelte"; + +export { + Root, + Image, + Fallback, + // + Root as Avatar, + Image as AvatarImage, + Fallback as AvatarFallback, +}; diff --git a/src/lib/components/ui/badge/badge.svelte b/src/lib/components/ui/badge/badge.svelte new file mode 100644 index 0000000..f8bc841 --- /dev/null +++ b/src/lib/components/ui/badge/badge.svelte @@ -0,0 +1,50 @@ + + + + + + {@render children?.()} + diff --git a/src/lib/components/ui/badge/index.ts b/src/lib/components/ui/badge/index.ts new file mode 100644 index 0000000..64e0aa9 --- /dev/null +++ b/src/lib/components/ui/badge/index.ts @@ -0,0 +1,2 @@ +export { default as Badge } from "./badge.svelte"; +export { badgeVariants, type BadgeVariant } from "./badge.svelte"; diff --git a/src/lib/components/ui/button/button.svelte b/src/lib/components/ui/button/button.svelte new file mode 100644 index 0000000..7cb2ed6 --- /dev/null +++ b/src/lib/components/ui/button/button.svelte @@ -0,0 +1,74 @@ + + + + +{#if href} + + {@render children?.()} + +{:else} + +{/if} diff --git a/src/lib/components/ui/button/index.ts b/src/lib/components/ui/button/index.ts new file mode 100644 index 0000000..fb585d7 --- /dev/null +++ b/src/lib/components/ui/button/index.ts @@ -0,0 +1,17 @@ +import Root, { + type ButtonProps, + type ButtonSize, + type ButtonVariant, + buttonVariants, +} from "./button.svelte"; + +export { + Root, + type ButtonProps as Props, + // + Root as Button, + buttonVariants, + type ButtonProps, + type ButtonSize, + type ButtonVariant, +}; diff --git a/src/lib/components/ui/card/card-content.svelte b/src/lib/components/ui/card/card-content.svelte new file mode 100644 index 0000000..085b722 --- /dev/null +++ b/src/lib/components/ui/card/card-content.svelte @@ -0,0 +1,16 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/card/card-description.svelte b/src/lib/components/ui/card/card-description.svelte new file mode 100644 index 0000000..72d3620 --- /dev/null +++ b/src/lib/components/ui/card/card-description.svelte @@ -0,0 +1,16 @@ + + +

+ {@render children?.()} +

diff --git a/src/lib/components/ui/card/card-footer.svelte b/src/lib/components/ui/card/card-footer.svelte new file mode 100644 index 0000000..d413b4e --- /dev/null +++ b/src/lib/components/ui/card/card-footer.svelte @@ -0,0 +1,16 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/card/card-header.svelte b/src/lib/components/ui/card/card-header.svelte new file mode 100644 index 0000000..d635f37 --- /dev/null +++ b/src/lib/components/ui/card/card-header.svelte @@ -0,0 +1,16 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/card/card-title.svelte b/src/lib/components/ui/card/card-title.svelte new file mode 100644 index 0000000..db1bc7e --- /dev/null +++ b/src/lib/components/ui/card/card-title.svelte @@ -0,0 +1,25 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/card/card.svelte b/src/lib/components/ui/card/card.svelte new file mode 100644 index 0000000..a7f56e9 --- /dev/null +++ b/src/lib/components/ui/card/card.svelte @@ -0,0 +1,23 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/card/index.ts b/src/lib/components/ui/card/index.ts new file mode 100644 index 0000000..0f9084d --- /dev/null +++ b/src/lib/components/ui/card/index.ts @@ -0,0 +1,22 @@ +import Root from "./card.svelte"; +import Content from "./card-content.svelte"; +import Description from "./card-description.svelte"; +import Footer from "./card-footer.svelte"; +import Header from "./card-header.svelte"; +import Title from "./card-title.svelte"; + +export { + Root, + Content, + Description, + Footer, + Header, + Title, + // + Root as Card, + Content as CardContent, + Description as CardDescription, + Footer as CardFooter, + Header as CardHeader, + Title as CardTitle, +}; diff --git a/src/lib/components/ui/dialog/dialog-content.svelte b/src/lib/components/ui/dialog/dialog-content.svelte new file mode 100644 index 0000000..9f1a33b --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-content.svelte @@ -0,0 +1,38 @@ + + + + + + {@render children?.()} + + + Close + + + diff --git a/src/lib/components/ui/dialog/dialog-description.svelte b/src/lib/components/ui/dialog/dialog-description.svelte new file mode 100644 index 0000000..6b1eddc --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-description.svelte @@ -0,0 +1,16 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-footer.svelte b/src/lib/components/ui/dialog/dialog-footer.svelte new file mode 100644 index 0000000..0f53c76 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-footer.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/dialog/dialog-header.svelte b/src/lib/components/ui/dialog/dialog-header.svelte new file mode 100644 index 0000000..5c8baf6 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-header.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/dialog/dialog-overlay.svelte b/src/lib/components/ui/dialog/dialog-overlay.svelte new file mode 100644 index 0000000..c0a15e3 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-overlay.svelte @@ -0,0 +1,19 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-title.svelte b/src/lib/components/ui/dialog/dialog-title.svelte new file mode 100644 index 0000000..ee92c4e --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-title.svelte @@ -0,0 +1,16 @@ + + + diff --git a/src/lib/components/ui/dialog/index.ts b/src/lib/components/ui/dialog/index.ts new file mode 100644 index 0000000..3286ab7 --- /dev/null +++ b/src/lib/components/ui/dialog/index.ts @@ -0,0 +1,37 @@ +import { Dialog as DialogPrimitive } from "bits-ui"; + +import Title from "./dialog-title.svelte"; +import Footer from "./dialog-footer.svelte"; +import Header from "./dialog-header.svelte"; +import Overlay from "./dialog-overlay.svelte"; +import Content from "./dialog-content.svelte"; +import Description from "./dialog-description.svelte"; + +const Root = DialogPrimitive.Root; +const Trigger = DialogPrimitive.Trigger; +const Close = DialogPrimitive.Close; +const Portal = DialogPrimitive.Portal; + +export { + Root, + Title, + Portal, + Footer, + Header, + Trigger, + Overlay, + Content, + Description, + Close, + // + Root as Dialog, + Title as DialogTitle, + Portal as DialogPortal, + Footer as DialogFooter, + Header as DialogHeader, + Trigger as DialogTrigger, + Overlay as DialogOverlay, + Content as DialogContent, + Description as DialogDescription, + Close as DialogClose, +}; diff --git a/src/lib/components/ui/input/index.ts b/src/lib/components/ui/input/index.ts new file mode 100644 index 0000000..f47b6d3 --- /dev/null +++ b/src/lib/components/ui/input/index.ts @@ -0,0 +1,7 @@ +import Root from "./input.svelte"; + +export { + Root, + // + Root as Input, +}; diff --git a/src/lib/components/ui/input/input.svelte b/src/lib/components/ui/input/input.svelte new file mode 100644 index 0000000..a2f34d6 --- /dev/null +++ b/src/lib/components/ui/input/input.svelte @@ -0,0 +1,46 @@ + + +{#if type === "file"} + +{:else} + +{/if} diff --git a/src/lib/components/ui/label/index.ts b/src/lib/components/ui/label/index.ts new file mode 100644 index 0000000..8bfca0b --- /dev/null +++ b/src/lib/components/ui/label/index.ts @@ -0,0 +1,7 @@ +import Root from "./label.svelte"; + +export { + Root, + // + Root as Label, +}; diff --git a/src/lib/components/ui/label/label.svelte b/src/lib/components/ui/label/label.svelte new file mode 100644 index 0000000..f1e7b90 --- /dev/null +++ b/src/lib/components/ui/label/label.svelte @@ -0,0 +1,19 @@ + + + diff --git a/src/lib/components/ui/scroll-area/index.ts b/src/lib/components/ui/scroll-area/index.ts new file mode 100644 index 0000000..e86a25b --- /dev/null +++ b/src/lib/components/ui/scroll-area/index.ts @@ -0,0 +1,10 @@ +import Scrollbar from "./scroll-area-scrollbar.svelte"; +import Root from "./scroll-area.svelte"; + +export { + Root, + Scrollbar, + //, + Root as ScrollArea, + Scrollbar as ScrollAreaScrollbar, +}; diff --git a/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte b/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte new file mode 100644 index 0000000..a130b97 --- /dev/null +++ b/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte @@ -0,0 +1,29 @@ + + + + {@render children?.()} + + diff --git a/src/lib/components/ui/scroll-area/scroll-area.svelte b/src/lib/components/ui/scroll-area/scroll-area.svelte new file mode 100644 index 0000000..fed796a --- /dev/null +++ b/src/lib/components/ui/scroll-area/scroll-area.svelte @@ -0,0 +1,32 @@ + + + + + {@render children?.()} + + {#if orientation === "vertical" || orientation === "both"} + + {/if} + {#if orientation === "horizontal" || orientation === "both"} + + {/if} + + diff --git a/src/lib/components/ui/select/index.ts b/src/lib/components/ui/select/index.ts new file mode 100644 index 0000000..f31b8ae --- /dev/null +++ b/src/lib/components/ui/select/index.ts @@ -0,0 +1,34 @@ +import { Select as SelectPrimitive } from "bits-ui"; + +import GroupHeading from "./select-group-heading.svelte"; +import Item from "./select-item.svelte"; +import Content from "./select-content.svelte"; +import Trigger from "./select-trigger.svelte"; +import Separator from "./select-separator.svelte"; +import ScrollDownButton from "./select-scroll-down-button.svelte"; +import ScrollUpButton from "./select-scroll-up-button.svelte"; + +const Root = SelectPrimitive.Root; +const Group = SelectPrimitive.Group; + +export { + Root, + Group, + GroupHeading, + Item, + Content, + Trigger, + Separator, + ScrollDownButton, + ScrollUpButton, + // + Root as Select, + Group as SelectGroup, + GroupHeading as SelectGroupHeading, + Item as SelectItem, + Content as SelectContent, + Trigger as SelectTrigger, + Separator as SelectSeparator, + ScrollDownButton as SelectScrollDownButton, + ScrollUpButton as SelectScrollUpButton, +}; diff --git a/src/lib/components/ui/select/select-content.svelte b/src/lib/components/ui/select/select-content.svelte new file mode 100644 index 0000000..911d01e --- /dev/null +++ b/src/lib/components/ui/select/select-content.svelte @@ -0,0 +1,39 @@ + + + + + + + {@render children?.()} + + + + diff --git a/src/lib/components/ui/select/select-group-heading.svelte b/src/lib/components/ui/select/select-group-heading.svelte new file mode 100644 index 0000000..ce22413 --- /dev/null +++ b/src/lib/components/ui/select/select-group-heading.svelte @@ -0,0 +1,16 @@ + + + diff --git a/src/lib/components/ui/select/select-item.svelte b/src/lib/components/ui/select/select-item.svelte new file mode 100644 index 0000000..c8407e5 --- /dev/null +++ b/src/lib/components/ui/select/select-item.svelte @@ -0,0 +1,37 @@ + + + + {#snippet children({ selected, highlighted })} + + {#if selected} + + {/if} + + {#if childrenProp} + {@render childrenProp({ selected, highlighted })} + {:else} + {label || value} + {/if} + {/snippet} + diff --git a/src/lib/components/ui/select/select-scroll-down-button.svelte b/src/lib/components/ui/select/select-scroll-down-button.svelte new file mode 100644 index 0000000..e74c173 --- /dev/null +++ b/src/lib/components/ui/select/select-scroll-down-button.svelte @@ -0,0 +1,19 @@ + + + + + diff --git a/src/lib/components/ui/select/select-scroll-up-button.svelte b/src/lib/components/ui/select/select-scroll-up-button.svelte new file mode 100644 index 0000000..8a27992 --- /dev/null +++ b/src/lib/components/ui/select/select-scroll-up-button.svelte @@ -0,0 +1,19 @@ + + + + + diff --git a/src/lib/components/ui/select/select-separator.svelte b/src/lib/components/ui/select/select-separator.svelte new file mode 100644 index 0000000..e434b64 --- /dev/null +++ b/src/lib/components/ui/select/select-separator.svelte @@ -0,0 +1,13 @@ + + + diff --git a/src/lib/components/ui/select/select-trigger.svelte b/src/lib/components/ui/select/select-trigger.svelte new file mode 100644 index 0000000..3ef5469 --- /dev/null +++ b/src/lib/components/ui/select/select-trigger.svelte @@ -0,0 +1,24 @@ + + +span]:line-clamp-1", + className + )} + {...restProps} +> + {@render children?.()} + + diff --git a/src/lib/components/ui/separator/index.ts b/src/lib/components/ui/separator/index.ts new file mode 100644 index 0000000..82442d2 --- /dev/null +++ b/src/lib/components/ui/separator/index.ts @@ -0,0 +1,7 @@ +import Root from "./separator.svelte"; + +export { + Root, + // + Root as Separator, +}; diff --git a/src/lib/components/ui/separator/separator.svelte b/src/lib/components/ui/separator/separator.svelte new file mode 100644 index 0000000..f64938d --- /dev/null +++ b/src/lib/components/ui/separator/separator.svelte @@ -0,0 +1,22 @@ + + + diff --git a/src/lib/components/ui/skeleton/index.ts b/src/lib/components/ui/skeleton/index.ts new file mode 100644 index 0000000..186db21 --- /dev/null +++ b/src/lib/components/ui/skeleton/index.ts @@ -0,0 +1,7 @@ +import Root from "./skeleton.svelte"; + +export { + Root, + // + Root as Skeleton, +}; diff --git a/src/lib/components/ui/skeleton/skeleton.svelte b/src/lib/components/ui/skeleton/skeleton.svelte new file mode 100644 index 0000000..fa90e99 --- /dev/null +++ b/src/lib/components/ui/skeleton/skeleton.svelte @@ -0,0 +1,17 @@ + + +
diff --git a/src/lib/components/ui/switch/index.ts b/src/lib/components/ui/switch/index.ts new file mode 100644 index 0000000..f5533db --- /dev/null +++ b/src/lib/components/ui/switch/index.ts @@ -0,0 +1,7 @@ +import Root from "./switch.svelte"; + +export { + Root, + // + Root as Switch, +}; diff --git a/src/lib/components/ui/switch/switch.svelte b/src/lib/components/ui/switch/switch.svelte new file mode 100644 index 0000000..372e724 --- /dev/null +++ b/src/lib/components/ui/switch/switch.svelte @@ -0,0 +1,27 @@ + + + + + diff --git a/src/lib/components/ui/tabs/index.ts b/src/lib/components/ui/tabs/index.ts new file mode 100644 index 0000000..f1ab372 --- /dev/null +++ b/src/lib/components/ui/tabs/index.ts @@ -0,0 +1,18 @@ +import { Tabs as TabsPrimitive } from "bits-ui"; +import Content from "./tabs-content.svelte"; +import List from "./tabs-list.svelte"; +import Trigger from "./tabs-trigger.svelte"; + +const Root = TabsPrimitive.Root; + +export { + Root, + Content, + List, + Trigger, + // + Root as Tabs, + Content as TabsContent, + List as TabsList, + Trigger as TabsTrigger, +}; diff --git a/src/lib/components/ui/tabs/tabs-content.svelte b/src/lib/components/ui/tabs/tabs-content.svelte new file mode 100644 index 0000000..02aa7f8 --- /dev/null +++ b/src/lib/components/ui/tabs/tabs-content.svelte @@ -0,0 +1,19 @@ + + + diff --git a/src/lib/components/ui/tabs/tabs-list.svelte b/src/lib/components/ui/tabs/tabs-list.svelte new file mode 100644 index 0000000..ced23c5 --- /dev/null +++ b/src/lib/components/ui/tabs/tabs-list.svelte @@ -0,0 +1,19 @@ + + + diff --git a/src/lib/components/ui/tabs/tabs-trigger.svelte b/src/lib/components/ui/tabs/tabs-trigger.svelte new file mode 100644 index 0000000..3cd2366 --- /dev/null +++ b/src/lib/components/ui/tabs/tabs-trigger.svelte @@ -0,0 +1,19 @@ + + + diff --git a/src/lib/components/ui/textarea/index.ts b/src/lib/components/ui/textarea/index.ts new file mode 100644 index 0000000..ace797a --- /dev/null +++ b/src/lib/components/ui/textarea/index.ts @@ -0,0 +1,7 @@ +import Root from "./textarea.svelte"; + +export { + Root, + // + Root as Textarea, +}; diff --git a/src/lib/components/ui/textarea/textarea.svelte b/src/lib/components/ui/textarea/textarea.svelte new file mode 100644 index 0000000..2314c5e --- /dev/null +++ b/src/lib/components/ui/textarea/textarea.svelte @@ -0,0 +1,22 @@ + + + diff --git a/src/lib/components/vault/TagFilter.svelte b/src/lib/components/vault/TagFilter.svelte index 89b02c4..3bbade6 100644 --- a/src/lib/components/vault/TagFilter.svelte +++ b/src/lib/components/vault/TagFilter.svelte @@ -2,7 +2,11 @@ import { tagStore } from "$lib/stores/tags.svelte"; import type { VaultType } from "$lib/types"; import { Filter, Check, X } from "lucide-svelte"; - import { fade, slide } from "svelte/transition"; + import { fade } from "svelte/transition"; + import { Button } from "$lib/components/ui/button"; + import { Input } from "$lib/components/ui/input"; + import { Badge } from "$lib/components/ui/badge"; + import { cn } from "$lib/utils/cn"; interface Props { selectedTags: string[]; @@ -61,29 +65,25 @@
- + {#if isOpen}
(isOpen = false)} aria-hidden="true" @@ -92,58 +92,60 @@
- Filter Logic: - + +
-
-
+
{#each filteredTags as tag} {/each} {#if filteredTags.length === 0} -
+
No tags found
{/if} @@ -163,14 +165,16 @@ {#if selectedTags.length > 0} -
- +
{/if}
diff --git a/src/lib/components/vault/VaultCharacterBrowser.svelte b/src/lib/components/vault/VaultCharacterBrowser.svelte index 34243d4..ccb7655 100644 --- a/src/lib/components/vault/VaultCharacterBrowser.svelte +++ b/src/lib/components/vault/VaultCharacterBrowser.svelte @@ -1,8 +1,15 @@ -
+
{#if characterVault.characters.filter((c) => !filterType || c.characterType === filterType).length > 0}
-
{/if} -
- {#if !characterVault.isLoaded} -
- -
- {:else if filteredCharacters.length === 0} -
-
+
+ + {#if !characterVault.isLoaded} +
+ {#each Array(3) as _} +
+ +
+ + +
+
+ {/each} +
+ {:else if filteredCharacters.length === 0} +
{#if filterType === "protagonist"} - + {:else} - + {/if} -

+

{#if searchQuery} No characters match your search {:else} @@ -113,58 +126,66 @@ {/if}

{#if !searchQuery && onNavigateToVault} - + {/if}
-
- {:else} -
- {#each filteredCharacters as character (character.id)} - - {/each} -
- {/if} + + {/each} +
+ {/if} +
diff --git a/src/lib/components/vault/VaultCharacterCard.svelte b/src/lib/components/vault/VaultCharacterCard.svelte index 682b313..5a21ec5 100644 --- a/src/lib/components/vault/VaultCharacterCard.svelte +++ b/src/lib/components/vault/VaultCharacterCard.svelte @@ -1,9 +1,11 @@ -
{ if (e.key === 'Enter' || e.key === ' ') handleCardClick(); } : undefined} + onkeydown={selectable && !isImporting + ? (e) => { + if (e.key === "Enter" || e.key === " ") handleCardClick(); + } + : undefined} > {#if isImporting} -
- - Importing... +
+ + Importing...
{/if} -
- - {#if character.portrait} - {character.name} - {:else} -
- {#if character.characterType === 'protagonist'} - + +
+ +
+ {#if character.portrait} + {character.name} {:else} - - {/if} -
- {/if} - -
-
-

{character.name}

- {#if character.favorite} - +
+ {#if character.characterType === "protagonist"} + + {:else} + + {/if} +
{/if}
- - {character.characterType === 'protagonist' ? 'Protagonist' : character.role || 'Supporting'} - + +
+
+ +
+

+ {character.name} +

+
+ + {character.characterType === "protagonist" + ? "Protagonist" + : character.role || "Supporting"} + + {#if selectable && character.favorite} + + {/if} +
+
- {#if character.description} -

{character.description}

- {/if} - - {#if character.traits.length > 0} -
- {#each character.traits.slice(0, 3) as trait} - {trait} - {/each} - {#if character.traits.length > 3} - +{character.traits.length - 3} + + {#if !selectable && (onEdit || onDelete || onToggleFavorite)} +
+ {#if confirmingDelete} +
+ Delete? +
+ + +
+
+ {:else} + {#if onToggleFavorite} + + {/if} + {#if onEdit} + + {/if} + {#if onDelete} + + {/if} + {/if} +
{/if}
- {/if} - {#if character.tags.length > 0} -
- {#each character.tags.slice(0, 3) as tag} - - {/each} - {#if character.tags.length > 3} - +{character.tags.length - 3} - {/if} -
- {/if} -
-
+ {#if character.description} +

+ {character.description} +

+ {/if} - - {#if !selectable && (onEdit || onDelete || onToggleFavorite)} -
- {#if confirmingDelete} - - - {:else} - {#if onToggleFavorite} - + {#if character.traits.length > 0} +
+ {#each character.traits.slice(0, 3) as trait} + + {trait} + + {/each} + {#if character.traits.length > 3} + +{character.traits.length - 3} + {/if} +
{/if} - {#if onEdit} - - {/if} - {#if onDelete} - - {/if} - {/if} +
- {/if} -
+
+ diff --git a/src/lib/components/vault/VaultCharacterForm.svelte b/src/lib/components/vault/VaultCharacterForm.svelte index ce29a26..95b462a 100644 --- a/src/lib/components/vault/VaultCharacterForm.svelte +++ b/src/lib/components/vault/VaultCharacterForm.svelte @@ -4,6 +4,13 @@ import { X, User, Users, ImageUp, Loader2 } from 'lucide-svelte'; import { normalizeImageDataUrl } from '$lib/utils/image'; import TagInput from '$lib/components/tags/TagInput.svelte'; + import { cn } from '$lib/utils/cn'; + + import * as Dialog from '$lib/components/ui/dialog'; + import { Input } from '$lib/components/ui/input'; + import { Textarea } from '$lib/components/ui/textarea'; + import { Button } from '$lib/components/ui/button'; + import { Label } from '$lib/components/ui/label'; interface Props { character?: VaultCharacter | null; @@ -120,246 +127,219 @@ } - -
{ if (e.target === e.currentTarget) onClose(); }} - role="dialog" - aria-modal="true" -> -
- -
-

- {isEditing ? 'Edit Character' : 'New Character'} -

- -
+ { if (!open) onClose(); }}> + + + {isEditing ? 'Edit Character' : 'New Character'} + - -
{ e.preventDefault(); handleSubmit(); }} class="p-4 space-y-4"> + { e.preventDefault(); handleSubmit(); }} class="space-y-4 py-2"> {#if error} -
+
{error}
{/if} -
- +
+
- - +
-
- - + +
-
- - + rows={3} + class="resize-none" + />
{#if characterType === 'protagonist'} -
- - + rows={2} + class="resize-none" + />
-
- - + +
{/if} {#if characterType === 'supporting'} -
- - + +
-
- - + +
{/if} -
- - + + -

Comma-separated personality traits

+

Comma-separated personality traits

-
- - + + -

Used for portrait generation

+

Used for portrait generation

-
- +
+
{#if portrait} -
+
Portrait preview
{:else} -
+
{#if characterType === 'protagonist'} - + {:else} - + {/if}
{/if} - +
+ +
-
- +
+ tags = newTags} placeholder="Add tags..." /> -

For organizing your vault

+

For organizing your vault

-
- - -
+ + -
-
+ + diff --git a/src/lib/components/vault/VaultCharacterPicker.svelte b/src/lib/components/vault/VaultCharacterPicker.svelte index 4d35df7..cc9f7b7 100644 --- a/src/lib/components/vault/VaultCharacterPicker.svelte +++ b/src/lib/components/vault/VaultCharacterPicker.svelte @@ -1,8 +1,13 @@ - -
{ if (e.target === e.currentTarget) onClose(); }} - role="dialog" - aria-modal="true" -> -
- -
-
+ { if (!open) onClose(); }}> + + + {#if filterType === 'protagonist'} - + {:else} - + {/if} -

{title}

-
- -
+ {title} + + - -
+
- - +
-
{#if !characterVault.isLoaded}
- +
{:else if filteredCharacters.length === 0} -
+
{#if filterType === 'protagonist'} - + {:else} - + {/if} -

+

{#if searchQuery} No characters match your search {:else} {emptyMessage} {/if}

-

+

Create characters in the Character Vault first

@@ -144,14 +134,10 @@ {/if}
- -
- -
-
-
+ + + + diff --git a/src/lib/components/vault/VaultLorebookCard.svelte b/src/lib/components/vault/VaultLorebookCard.svelte index 103d146..f406e88 100644 --- a/src/lib/components/vault/VaultLorebookCard.svelte +++ b/src/lib/components/vault/VaultLorebookCard.svelte @@ -1,8 +1,10 @@ -
{ if (e.key === 'Enter' || e.key === ' ') handleCardClick(); } : undefined} > {#if isImporting} -
- - Processing... +
+ + Processing...
{/if} -
- -
- -
- -
-
-

{lorebook.name}

- {#if lorebook.favorite} - - {/if} + +
+ +
+
+ +
-
- - {lorebook.entries.length} entries - - {#if lorebook.source === 'story'} - • From Story - {:else if lorebook.source === 'import'} - • Imported - {/if} -
+ +
+
+ +
+

{lorebook.name}

+
+ + {lorebook.entries.length} entries + + {#if lorebook.source === 'story' || lorebook.source === 'import'} + + {lorebook.source === 'story' ? 'Story' : 'Imported'} + + {/if} + {#if selectable && lorebook.favorite} + + {/if} +
+
- {#if lorebook.description} -

{lorebook.description}

- {/if} - - - {#if entryCounts.length > 0} -
- {#each entryCounts.slice(0, 4) as { type, count }} - {@const Icon = typeIcons[type]} -
- - {count} + + {#if !selectable && (onEdit || onDelete || onToggleFavorite)} +
+ {#if confirmingDelete} +
+ Delete? +
+ + +
+
+ {:else} + {#if onToggleFavorite} + + {/if} + {#if onEdit} + + {/if} + {#if onDelete} + + {/if} + {/if}
- {/each} - {#if entryCounts.length > 4} - +{entryCounts.length - 4} {/if}
- {/if} - {#if lorebook.tags.length > 0} -
- {#each lorebook.tags.slice(0, 3) as tag} - - {/each} - {#if lorebook.tags.length > 3} - +{lorebook.tags.length - 3} - {/if} -
- {/if} -
-
+ {#if lorebook.description} +

{lorebook.description}

+ {/if} - - {#if !selectable && (onEdit || onDelete || onToggleFavorite)} -
- {#if confirmingDelete} - - - {:else} - {#if onToggleFavorite} - + + {#if entryCounts.length > 0} +
+ {#each entryCounts.slice(0, 4) as { type, count }} + {@const Icon = typeIcons[type]} +
+ {#if Icon} + + {/if} + {count} +
+ {/each} + {#if entryCounts.length > 4} + +{entryCounts.length - 4} + {/if} +
{/if} - {#if onEdit} - - {/if} - {#if onDelete} - - {/if} - {/if} +
- {/if} -
+
+ diff --git a/src/lib/components/vault/VaultPanel.svelte b/src/lib/components/vault/VaultPanel.svelte index b8c1c41..684c2b0 100644 --- a/src/lib/components/vault/VaultPanel.svelte +++ b/src/lib/components/vault/VaultPanel.svelte @@ -11,13 +11,12 @@ } from "$lib/types"; import { Plus, - Search, + Search as SearchIcon, Star, User, Users, ChevronLeft, Upload, - Loader2, Archive, Book, Globe, @@ -36,6 +35,20 @@ import { tagStore } from "$lib/stores/tags.svelte"; import { fade } from "svelte/transition"; + // Shadcn Components + import { Button, buttonVariants } from "$lib/components/ui/button"; + import { Input } from "$lib/components/ui/input"; + import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, + } from "$lib/components/ui/tabs"; + import { Badge } from "$lib/components/ui/badge"; + import { Skeleton } from "$lib/components/ui/skeleton"; + import { ScrollArea } from "$lib/components/ui/scroll-area"; + import { cn } from "$lib/utils/cn"; + // Types type VaultTab = "characters" | "lorebooks" | "scenarios"; @@ -81,10 +94,14 @@ } if (selectedTags.length > 0) { - if (filterLogic === 'AND') { - chars = chars.filter(c => selectedTags.every(tag => c.tags.includes(tag))); + if (filterLogic === "AND") { + chars = chars.filter((c) => + selectedTags.every((tag) => c.tags.includes(tag)), + ); } else { - chars = chars.filter(c => selectedTags.some(tag => c.tags.includes(tag))); + chars = chars.filter((c) => + selectedTags.some((tag) => c.tags.includes(tag)), + ); } } @@ -111,10 +128,14 @@ } if (selectedTags.length > 0) { - if (filterLogic === 'AND') { - books = books.filter(b => selectedTags.every(tag => b.tags.includes(tag))); + if (filterLogic === "AND") { + books = books.filter((b) => + selectedTags.every((tag) => b.tags.includes(tag)), + ); } else { - books = books.filter(b => selectedTags.some(tag => b.tags.includes(tag))); + books = books.filter((b) => + selectedTags.some((tag) => b.tags.includes(tag)), + ); } } @@ -140,10 +161,14 @@ } if (selectedTags.length > 0) { - if (filterLogic === 'AND') { - items = items.filter(s => selectedTags.every(tag => s.tags.includes(tag))); + if (filterLogic === "AND") { + items = items.filter((s) => + selectedTags.every((tag) => s.tags.includes(tag)), + ); } else { - items = items.filter(s => selectedTags.some(tag => s.tags.includes(tag))); + items = items.filter((s) => + selectedTags.some((tag) => s.tags.includes(tag)), + ); } } @@ -311,228 +336,260 @@ } -
+ (activeTab = v as VaultTab)} + class="flex h-full flex-col bg-background" +> -
+
-
-
- + +
- -

Vault

+ +

Vault

- -
- + Tags + + + {#if activeTab === "characters"} - -
-
- - - +
+ + + + + {characterVault.characters.length} + + + + + {lorebookVault.lorebooks.length} + + + + + {scenarioVault.scenarios.length} + +
- +
+ {#if activeTab === "characters" && importCharError}
{importCharError} - +
{/if} - {#if activeTab === "lorebooks"} - {#if importLorebookError} -
+ {importLorebookError} + -
- {/if} + Dismiss + × + +
{/if} - {#if activeTab === "scenarios"} - {#if importScenarioError} -
+ {importScenarioError} + -
- {/if} + Dismiss + × + +
{/if} -
+
- -
{#if activeTab === "characters"} -
- - - +
{/if} { selectedTags = tags; filterLogic = logic; }} /> - +
-
- {#if activeTab === "characters"} - - {#if !characterVault.isLoaded} -
- -
- {:else if filteredCharacters.length === 0} -
-
- -

+ + +

+ {#if !characterVault.isLoaded} +
+ {#each Array(6) as _} +
+ +
+ + +
+
+ {/each} +
+ {:else if filteredCharacters.length === 0} +
+
+ +
+

{#if searchQuery || showFavoritesOnly || charFilterType !== "all"} No characters match your filters {:else} @@ -626,128 +706,150 @@ {/if}

{#if !searchQuery && !showFavoritesOnly && charFilterType === "all"} -
- - +
{/if}
-
- {:else} -
- {#each filteredCharacters as character (character.id)} - openEditCharForm(character)} - onDelete={() => handleDeleteChar(character.id)} - onToggleFavorite={() => handleToggleFavoriteChar(character.id)} - /> - {/each} -
- {/if} - {:else if activeTab === "lorebooks"} - - {#if !lorebookVault.isLoaded} -
- -
- {:else if filteredLorebooks.length === 0} -
-
- -

+ {:else} +

+ {#each filteredCharacters as character (character.id)} + openEditCharForm(character)} + onDelete={() => handleDeleteChar(character.id)} + onToggleFavorite={() => handleToggleFavoriteChar(character.id)} + /> + {/each} +
+ {/if} +
+ + + + + +
+ {#if !lorebookVault.isLoaded} +
+ {#each Array(6) as _} + + {/each} +
+ {:else if filteredLorebooks.length === 0} +
+
+ +
+

{#if searchQuery || showFavoritesOnly} No lorebooks match your filters {:else} No lorebooks in vault yet {/if}

-

+

Create a new lorebook or import one from a file

{#if !searchQuery && !showFavoritesOnly} -
- +
{/if}
-
- {:else} -
- {#each filteredLorebooks as lorebook (lorebook.id)} - handleDeleteLorebook(lorebook.id)} - onToggleFavorite={() => handleToggleFavoriteLorebook(lorebook.id)} - onEdit={() => openEditLorebook(lorebook)} - /> - {/each} -
- {/if} - {:else if activeTab === "scenarios"} - - {#if !scenarioVault.isLoaded} -
- -
- {:else if filteredScenarios.length === 0} -
-
- -

+ {:else} +

+ {#each filteredLorebooks as lorebook (lorebook.id)} + handleDeleteLorebook(lorebook.id)} + onToggleFavorite={() => + handleToggleFavoriteLorebook(lorebook.id)} + onEdit={() => openEditLorebook(lorebook)} + /> + {/each} +
+ {/if} +
+ + + + + +
+ {#if !scenarioVault.isLoaded} +
+ {#each Array(6) as _} + + {/each} +
+ {:else if filteredScenarios.length === 0} +
+
+ +
+

{#if searchQuery || showFavoritesOnly} No scenarios match your filters {:else} No scenarios in vault yet {/if}

-

+

Import character cards to extract scenario settings

-
- {:else} -
- {#each filteredScenarios as scenario (scenario.id)} - handleDeleteScenario(scenario.id)} - onToggleFavorite={() => handleToggleFavoriteScenario(scenario.id)} - onEdit={() => openEditScenario(scenario)} - /> - {/each} -
- {/if} - {/if} -
-
+ {:else} +
+ {#each filteredScenarios as scenario (scenario.id)} + handleDeleteScenario(scenario.id)} + onToggleFavorite={() => + handleToggleFavoriteScenario(scenario.id)} + onEdit={() => openEditScenario(scenario)} + /> + {/each} +
+ {/if} +
+ + + {#if showCharForm} diff --git a/src/lib/utils/cn.ts b/src/lib/utils/cn.ts new file mode 100644 index 0000000..365058c --- /dev/null +++ b/src/lib/utils/cn.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 3faf543..289a70e 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -5,7 +5,7 @@ import { grammarService } from "$lib/services/grammar"; import { updaterService } from "$lib/services/updater"; import AppShell from "$lib/components/layout/AppShell.svelte"; - import ProviderSetupModal from "$lib/components/settings/ProviderSetupModal.svelte"; + import WelcomeScreen from "$lib/components/intro/WelcomeScreen.svelte"; let initialized = $state(false); let error = $state(null); @@ -77,12 +77,6 @@ } - - - {#if error}
{:else if showProviderSetup} - -
-
-

Welcome to Aventuras

-
-
+ {:else if !initialized}
)", + input: "hsl(var(--input) / )", + ring: "hsl(var(--ring) / )", + background: "hsl(var(--background) / )", + foreground: "hsl(var(--foreground) / )", + primary: { + DEFAULT: "hsl(var(--primary) / )", + foreground: "hsl(var(--primary-foreground) / )", + }, + secondary: { + DEFAULT: "hsl(var(--secondary) / )", + foreground: "hsl(var(--secondary-foreground) / )", + }, + destructive: { + DEFAULT: "hsl(var(--destructive) / )", + foreground: "hsl(var(--destructive-foreground) / )", + }, + muted: { + DEFAULT: "hsl(var(--muted) / )", + foreground: "hsl(var(--muted-foreground) / )", + }, + accent: { + DEFAULT: "hsl(var(--accent) / )", + foreground: "hsl(var(--accent-foreground) / )", + 50: '#eff6ff', + 100: '#dbeafe', + 200: '#bfdbfe', + 300: '#93c5fd', + 400: '#60a5fa', + 500: '#3b82f6', + 600: '#2563eb', + 700: '#1d4ed8', + 800: '#1e40af', + 900: '#1e3a8a', + 950: '#172554', + }, + popover: { + DEFAULT: "hsl(var(--popover) / )", + foreground: "hsl(var(--popover-foreground) / )", + }, + card: { + DEFAULT: "hsl(var(--card) / )", + foreground: "hsl(var(--card-foreground) / )", + }, surface: { 50: '#f8fafc', 100: '#f1f5f9', @@ -28,26 +82,24 @@ export default { 900: '#0f172a', 950: '#020617', }, - accent: { - 50: '#eff6ff', - 100: '#dbeafe', - 200: '#bfdbfe', - 300: '#93c5fd', - 400: '#60a5fa', - 500: '#3b82f6', - 600: '#2563eb', - 700: '#1d4ed8', - 800: '#1e40af', - 900: '#1e3a8a', - 950: '#172554', - }, + }, + borderColor: { + DEFAULT: "hsl(var(--border) / )", + }, + ringColor: { + DEFAULT: "hsl(var(--ring) / )", + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", }, fontFamily: { - sans: ['Inter', 'system-ui', 'sans-serif'], - mono: ['JetBrains Mono', 'Fira Code', 'monospace'], + sans: ['Inter', 'system-ui', 'sans-serif', ...fontFamily.sans], + mono: ['JetBrains Mono', 'Fira Code', 'monospace', ...fontFamily.mono], story: ['Georgia', 'Cambria', 'serif'], }, }, }, - plugins: [], + plugins: [tailwindcssAnimate], };