diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfdb8b7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sh text eol=lf diff --git a/.gitignore b/.gitignore index 35a19d6..7cb43f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,27 +1,48 @@ -# Ignore large Stable Diffusion model weights (exceeds GitHub file limits) -app/models/*.safetensors -app/models/*.gguf -app/models/*.ckpt -app/models -app/outputs/ +# Downloaded AI models +app/models/ +app/openvino-models/ +app/llm-models/ +app/speech-models/ +app/tts-models/ -# Ignore portable Node.js binaries and packages -app/tools/ -**/node_modules/ -node_modules/ - -# Ignore backend folder completely (contains massive DLLs, EXEs, and ZIPs >1GB) +# Downloaded portable runtimes/backends app/backend/ +app/llm-backend/ +app/speech-backend/ +app/tts-runtime/ +app/tools/ -# Ignore local IDE and OS temp files +# Generated files +app/config/ +app/cache/ +app/dist/ +app/outputs/ +app/transcriptions/ +app/tts-outputs/ +app/tts-cache/ + +# Dependencies and platform-specific copies +**/node_modules/ +**/node_modules_*/ +**/node_modules_windows/ +**/node_modules_linux/ +**/node_modules_mac/ +**/.active_modules_os + +# Partial downloads and archives +*.part +*.tmp +app/tools/*.zip +app/tools/*.tar.gz +app/tools/*.tar.xz + +# Logs and caches +*.log +__pycache__/ +*.pyc + +# OS/editor files .vscode/ .idea/ .DS_Store Thumbs.db -*.log -*.tmp - - - -# output images folder -/app/outputs \ No newline at end of file diff --git a/README.md b/README.md index f02ba37..3638f91 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,146 @@ -# πŸ–ΌοΈ Local AI Image Generator +# πŸ–ΌοΈ Uncensored AI Studio -### An easy, zero-setup Stable Diffusion GUI for Windows. Run GGUF & Safetensors models offline without Python configuration. +

+ A premium, zero-configuration local AI studio and offline GUI for Stable Diffusion (Image Generation), LLMs (Chat), Whisper (Speech-to-Text), and Kokoro (Text-to-Speech). Powered by hardware-accelerated GPU and NPU execution on Windows, Linux, and macOS. +

+

+ 100% Offline + Platforms + License +

+

+ πŸŽ₯ Watch the Setup & Demo Video: https://youtu.be/ESELhY-G_9w +

-| **Generation Workspace** | **Model Library** | **Image Constraints** | -| :---: | :---: | :---: | -| | | | - ---- - - - -
-

πŸŽ₯ Watch the Setup & Demo Video: https://youtu.be/ESELhY-G_9w

+

- Local AI Image Generator Video Tutorial + Uncensored AI Studio Video Tutorial -

+

--- -## πŸ“– Overview -**Local AI Image Generator** is a zero-configuration, portable desktop environment for running Stable Diffusion (Safetensors/GGUF/CKPT) offline on Windows. Double-clicking `start.bat` automatically handles dependency setup, GPU backend matching (CUDA/Vulkan), and launches a high-performance local web workspace. + +## πŸ“– Table of Contents +* [What is Uncensored AI Studio?](#what-is-uncensored-ai-studio) +* [Key Features](#key-features) +* [Workspace & Engine Architecture](#workspace-architecture) +* [Folder Architecture](#folder-architecture) +* [Getting Started](#getting-started) + * [Windows Setup](#windows-setup) + * [Linux Setup](#linux-setup) + * [macOS Setup](#macos-setup) +* [Hardware Compatibility & Acceleration](#hardware-compatibility-acceleration) +* [Troubleshooting & FAQ](#troubleshooting-faq) +* [Building From Source](#building-from-source) +* [Licensing](#licensing) --- -## ⚑ Quick Start -1. **Launch:** Double-click **`start.bat`** (downloads portable Node.js and pre-compiled GPU backend binaries on first run). -2. **Add Models:** Drop `.safetensors`, `.gguf`, or `.ckpt` weights into `app/models/` (or download them via the **Model Manager** tab in the UI). -3. **Generate:** Open `http://localhost:1420` in your browser, select your model, and write a prompt. +## πŸ“– What is Uncensored AI Studio? + +**Uncensored AI Studio** is a completely offline, zero-setup, self-contained AI studio for Windows, Linux, and macOS. Unlike cloud-based AI systems, it runs entirely on your own hardware with no censorship, tracking, subscriptions, or login requirements. + +It unifies four major local AI capabilities into one high-performance desktop interface: +1. **🎨 Image Generation (Stable Diffusion):** Generate and edit high-quality images offline using `.safetensors`, `.gguf`, or `.ckpt` model weights. +2. **πŸ’¬ Text Chat (LLMs):** Converse privately with open-source language models (GGUF format) powered by official, high-performance `llama.cpp` backends. +3. **πŸŽ™οΈ Speech-to-Text (Whisper):** Transcribe voice recordings and speech to text in real-time with an integrated `whisper.cpp` engine. +4. **πŸ—£οΈ Text-to-Speech (Kokoro TTS):** Convert text outputs into highly natural, lifelike vocal audio offline using the `Kokoro-82M` ONNX model. --- -## ✨ Features -* **100% Offline & Private:** Inference runs completely locally on your hardware. -* **Auto-Detected GPU Acceleration:** Configures **CUDA** for Nvidia cards, and **Vulkan** for AMD or Intel Arc GPUs. -* **Zero System Footprint:** Node.js is sandboxed inside the folder. No global environment paths are altered. -* **Integrated Model Manager:** Paste a Hugging Face URL to download weights directly, or drag-and-drop local weight files to import them. -* **Real-time Telemetry:** Monitor RAM, VRAM, CPU, and GPU load directly in the UI. -* **Local Gallery:** Saves generated PNGs alongside prompt metadata JSONs to `app/outputs/`. +## 🌟 Key Features + +* **100% Offline & Private:** Run inferences locally. No internet, telemetry, cloud logging, or API keys required. +* **Zero-Install Portability:** Entire runtime (Node.js, models, GPU backends) is self-contained. Zero global system environment changes. +* **Auto-Configured Acceleration:** Auto-detects hardware specs to load CUDA (Nvidia), ROCm (AMD), Vulkan (Intel/AMD/NVIDIA), Metal (macOS), or OpenVINO (Intel NPU) backends. +* **Integrated Model Manager:** Paste Hugging Face URLs to download weights directly, or drag-and-drop local weights to import them. +* **Live Performance Monitor:** Track CPU, RAM, GPU, and VRAM utilization in real-time directly inside the web UI. +* **Local Output Gallery:** Saves generated images side-by-side with prompt parameters and metadata JSON files. --- -## πŸ“ Repository Structure +## βš™οΈ Workspace & Engine Architecture + +To avoid exhausting system RAM or VRAM, text and image engines are mutually exclusive by default. You can switch between workspaces inside the UI: + +* **Image Generation Workspace:** Uses a dedicated `stable-diffusion.cpp` backend node. Model weights are stored in `app/models/`. +* **Text Chat Workspace:** Uses a portable `llama.cpp` server backend. Model weights (.gguf) are stored in `app/llm-models/`. A small Qwen2.5 Coder starter model can be downloaded directly from the Text Chat panel. +* **Speech Worker (Whisper):** Runs a localized `whisper-cli` process to convert your vocal input to text. +* **Audio Output (Kokoro TTS):** Utilizes `kokoro-js` locally on the server side to read responses in natural voices. + +--- + +## πŸ“ Folder Architecture + ``` -local-ai-image-generator/ -β”œβ”€β”€ start.bat # Main double-click entrypoint -β”œβ”€β”€ LICENSE # MIT Open Source license -β”œβ”€β”€ .gitignore -β”œβ”€β”€ README.md +Uncensored-AI-Studio/ +β”œβ”€β”€ windows.bat # Windows Launcher (Double-click entrypoint) +β”œβ”€β”€ linux.sh # Linux Launcher (Terminal entrypoint) +β”œβ”€β”€ mac.sh # macOS Launcher (Terminal entrypoint) +β”œβ”€β”€ LICENSE # MIT Open Source License +β”œβ”€β”€ .gitignore # Excludes models and output images from version control +β”œβ”€β”€ README.md # Detailed system documentation β”œβ”€β”€ scripts/ -β”‚ β”œβ”€β”€ setup.ps1 # Automated GPU-detect and environment installer -β”‚ β”œβ”€β”€ reset.ps1 # Cleans runtime environments (keeps models & outputs) -β”‚ └── serve.cjs # UI web server and backend lifecycle manager +β”‚ β”œβ”€β”€ setup/ # Platform setup and backend installers +β”‚ β”œβ”€β”€ reset/ # Clean install & environment repair +β”‚ β”œβ”€β”€ server/ # UI web server and backend lifecycle manager +β”‚ β”œβ”€β”€ workers/ # Local worker processes +β”‚ β”œβ”€β”€ build/ # Optional source build helpers +β”‚ └── config/ # Runtime configuration catalogs └── app/ β”œβ”€β”€ frontend/ # UI source code (Vite + React) - β”œβ”€β”€ models/ # Place weights here (.safetensors, .gguf, .ckpt) + β”œβ”€β”€ models/ # Place image weights here (.safetensors, .gguf, .ckpt) + β”œβ”€β”€ llm-models/ # Place text GGUF weights here └── outputs/ # Saved images and parameters metadata ``` --- -## πŸ–₯️ GPU Compatibility Matrix +## πŸš€ Getting Started + +Ensure you have a modern web browser installed. Follow the quick guide below for your platform: + +### Windows Setup + +1. **Launch:** Double-click **`windows.bat`**. + > [!NOTE] + > On the first run, the script will automatically download a portable Node.js runtime and configure pre-compiled GPU/CPU backend binaries. +2. **Add Models:** Drop `.safetensors`, `.gguf`, or `.ckpt` weights into `app/models/` (or download them via the **Model Manager** tab in the UI). +3. **Generate:** Open `http://localhost:1420` in your browser, select your model, and write a prompt. + +### Linux Setup + +1. **Make executable:** Open a terminal in the project folder and make the script executable: + ```bash + chmod +x linux.sh + ``` +2. **Launch:** Run **`./linux.sh`**. + - **NVIDIA GPU Users:** You will be prompted to set up the high-performance **CUDA** backend (downloads prebuilt or automatically compiles from source as a fallback). + - **AMD Radeon Performance:** Run with **`./linux.sh --max-perf`** to add the ROCm backend (~1.3 GB download). + - **Intel Core Ultra NPU:** Run with **`./linux.sh --setup-openvino`** to configure Intel NPU support (requires Intel Linux NPU driver). +3. **Add Models:** Drop your weights into `app/models/` or download them via the **Model Manager** tab. +4. **Generate:** Open `http://localhost:1420` in your browser. + +### macOS Setup + +1. **Make executable:** Open a terminal in the project folder and make the script executable: + ```bash + chmod +x mac.sh + ``` +2. **Launch:** Run **`./mac.sh`**. + > [!IMPORTANT] + > The prebuilt macOS backend is optimized for **Apple Silicon (M1 or newer)** and uses **Metal** GPU acceleration. *(macOS Intel hardware is completely unsupported)*. +3. **Add Models:** Drop your weights into `app/models/` or download them via the **Model Manager** tab. +4. **Generate:** Open `http://localhost:1420` in your browser. + +--- + +## πŸ–₯️ Hardware Compatibility & Acceleration + +### Windows | GPU Vendor | Tech | Status | Notes | | :--- | :--- | :--- | :--- | @@ -71,24 +149,120 @@ local-ai-image-generator/ | **Intel Arc** | Vulkan | βœ… Native | Maps `sd-vulkan.exe` for Intel hardware. | | **Integrated / None** | CPU | ⚠️ Fallback | Runs on logical CPU threads (slow). | ---- +### Linux -## ⏱️ Performance Benchmarks +| GPU Vendor | Primary | Fallback | Notes | +| :--- | :--- | :--- | :--- | +| **NVIDIA** | CUDA / Vulkan | Vulkan / CPU | Auto-detects NVIDIA. Prompt-driven CUDA setup downloads prebuilt or compiles from source. Falls back to Vulkan for GTX. | +| **AMD Radeon** | ROCm | Vulkan | ROCm provides best AMD performance when host ROCm drivers are available. | +| **Intel Arc / integrated** | Vulkan | CPU | Cross-vendor Vulkan support. | +| **Intel Core Ultra NPU** | OpenVINO NPU | CPU | Requires the Intel Linux NPU driver, kernel 6.6+, Python 3, and `./linux.sh --setup-openvino`. | +| **Integrated / None** | CPU | β€” | Runs on logical CPU threads (slow). | -Typical generation times for an image with **20 steps** (e.g. 512x512 resolution; actual times can vary depending on specific hardware specifications, clock speeds, and system load): +### macOS -* **CUDA GPU (Nvidia RTX):** ~10 seconds. -* **Vulkan GPU (AMD / Intel Arc):** ~89 seconds. -* **GTX Vulkan Fallback (Nvidia GTX):** ~30 seconds (Vulkan runs significantly faster on legacy GTX series cards since they lack Tensor Cores). -* **CPU (Fallback):** ~150 - 300+ seconds (highly dependent on processor core count, speed, and AVX instruction sets). +| Hardware | Primary | Fallback | Notes | +| :--- | :--- | :--- | :--- | +| **Apple Silicon (M1 or newer)** | Metal | CPU | Uses the official Darwin arm64 stable-diffusion.cpp backend. | + +> [!IMPORTANT] +> **System Requirements & Notes:** +> - **64-bit Windows 10 or Windows 11** is required for the portable Node.js 22 runtime used by the Windows launcher. +> - **glibc 2.38 or newer** is required for the prebuilt Linux backends (Ubuntu 24.04, Fedora 40+, etc.). The setup script will warn you if your glibc is older. +> - **Linux OpenVINO NPU:** Intel Core Ultra, x86_64 Linux, kernel 6.6+, a working `/dev/accel/accel0` device, Python 3 with `venv`, and the Intel Linux NPU driver are required. --- -## πŸ› οΈ Troubleshooting -* **Reset Environment:** If a build fails or you want to clear dependencies, run `scripts/reset.ps1`. (This preserves your models and generated images). -* **Port Conflicts:** The frontend uses `1420` by default. The backend tries `8080` first, then automatically falls back to a free port if `8080` is already busy. +## πŸ› οΈ Troubleshooting & FAQ + +
+ Reset Environment: If a build fails or you want to clear dependencies +

Run scripts/reset/reset.ps1 (Windows) or scripts/reset/reset.sh (Linux/macOS). This will clear temporary compilation and package caches to repair your environment. (Note: This preserves your model weights and generated output images).

+
+ +
+ Linux backends fail to start with GLIBC_2.38 not found +

The prebuilt binaries require glibc 2.38+ (e.g. Ubuntu 24.04). If your distribution uses an older glibc version, you can upgrade your operating system or compile the backend from source (see the Building From Source guide below).

+
+ +
+ Port Conflicts: Default port address already busy +

The web user interface runs on port 1420 by default. The GPU backend manager attempts to bind to port 8080 first, then automatically detects and falls back to a free system port if 8080 is already occupied.

+
+ +
+ Linux ROCm not loading for AMD Radeon GPUs +

Ensure your AMD GPU hardware and host kernel are fully compatible with ROCm 7.13. If ROCm fails to initialize correctly, the application will automatically fall back to Vulkan acceleration.

+
+ +
+ Windows exits with code 3221225781 (0xC0000135) +

This code means Windows could not locate a required backend DLL:

+ +
+ +
+ Generation shows "server is not responding or crashed" +

This indicates that the local backend engine process terminated. Check your launch terminal (where you executed windows.bat, ./linux.sh, or ./mac.sh) for the exact console error. Common causes include glibc version mismatches, missing Vulkan drivers, or system out-of-memory (OOM) issues.

+
--- -## πŸ“ License +## πŸ”¨ Building From Source + +The setup script (`scripts/setup/setup.sh`) now automates building and setting up the CUDA backend from source when selected. If you want to manually build all backends (CPU, Vulkan, and CUDA) at once, you can run the included `scripts/build/build_from_source.sh` script. + +For macOS, the included `scripts/build/build_from_source.sh` builds the Metal backend and copies it to `app/backend/mac/sd`. + +### Requirements +- `git`, `cmake`, `make` (or `ninja`), and a C++17 compiler (`g++` / `clang++`). +- For **CUDA**: the NVIDIA CUDA toolkit (`nvcc`) must be on your `PATH`. +- For **Vulkan**: the Vulkan SDK / loader and a compatible driver. +- For **ROCm**: AMD ROCm development libraries. +- For **macOS Metal**: Apple Command Line Tools or Xcode. + +### Build commands + +```bash +# 1. Clone upstream +git clone https://github.com/leejet/stable-diffusion.cpp.git +cd stable-diffusion.cpp +mkdir build && cd build + +# 2. Configure for your backend (pick ONE) +# CPU only +cmake .. -DSD_BUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release + +# CUDA +cmake .. -DSD_CUDA=ON -DSD_BUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release + +# Vulkan +cmake .. -DSD_VULKAN=ON -DSD_BUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release + +# ROCm +cmake .. -DSD_HIPBLAS=ON -DSD_BUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release + +# macOS Metal +cmake .. -DSD_METAL=ON -DSD_BUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release + +# 3. Build +cmake --build . --config Release -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu) + +# 4. Copy the binaries into this project +cp bin/sd* /path/to/Uncensored-AI-Studio/app/backend/linux// +``` + +After copying, rename the server binary to match what `scripts/server/serve.cjs` expects: +- Vulkan: `sd` β†’ `sd-vulkan` +- ROCm: `sd` β†’ `sd-rocm` + +Then restart the app with `./linux.sh` (Linux) or `./mac.sh` (macOS). + +--- + +## πŸ“ License + This project is licensed under the MIT License - see the [LICENSE](LICENSE) file. Bundles [stable-diffusion.cpp](https://github.com/leejet/stable-diffusion.cpp) (MIT License). Model weights are subject to their respective creators' licenses. diff --git a/app/dist/assets/event-DeDJUvqC.js b/app/dist/assets/event-DeDJUvqC.js deleted file mode 100644 index 30180c4..0000000 --- a/app/dist/assets/event-DeDJUvqC.js +++ /dev/null @@ -1 +0,0 @@ -import{i as D,t as i}from"./index-jXnaCwmK.js";var _;(function(r){r.WINDOW_RESIZED="tauri://resize",r.WINDOW_MOVED="tauri://move",r.WINDOW_CLOSE_REQUESTED="tauri://close-requested",r.WINDOW_DESTROYED="tauri://destroyed",r.WINDOW_FOCUS="tauri://focus",r.WINDOW_BLUR="tauri://blur",r.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",r.WINDOW_THEME_CHANGED="tauri://theme-changed",r.WINDOW_CREATED="tauri://window-created",r.WINDOW_SUSPENDED="tauri://suspended",r.WINDOW_RESUMED="tauri://resumed",r.WEBVIEW_CREATED="tauri://webview-created",r.DRAG_ENTER="tauri://drag-enter",r.DRAG_OVER="tauri://drag-over",r.DRAG_DROP="tauri://drag-drop",r.DRAG_LEAVE="tauri://drag-leave"})(_||(_={}));async function l(r,a){window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener(r,a),await D("plugin:event|unlisten",{event:r,eventId:a})}async function n(r,a,e){var t;const W=typeof e?.target=="string"?{kind:"AnyLabel",label:e.target}:(t=e?.target)!==null&&t!==void 0?t:{kind:"Any"};return D("plugin:event|listen",{event:r,target:W,handler:i(a)}).then(d=>async()=>l(r,d))}export{_ as TauriEvent,n as listen}; diff --git a/app/dist/assets/index-DW6G7wc2.css b/app/dist/assets/index-DW6G7wc2.css deleted file mode 100644 index d6e6769..0000000 --- a/app/dist/assets/index-DW6G7wc2.css +++ /dev/null @@ -1 +0,0 @@ -:root{font-family:Outfit,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;--md-shape-corner-small: 8px;--md-shape-corner-medium: 12px;--md-shape-corner-large: 16px;--md-shape-corner-extra-large: 22px;--md-shape-corner-full: 9999px;--md-transition-duration: .25s;--md-transition-easing: cubic-bezier(.4, 0, .2, 1);--md-sys-color-primary: #4f46e5;--md-sys-color-on-primary: #ffffff;--md-sys-color-primary-container: rgba(79, 70, 229, .06);--md-sys-color-on-primary-container: #4338ca;--md-sys-color-secondary: #0ea5e9;--md-sys-color-on-secondary: #ffffff;--md-sys-color-secondary-container: rgba(14, 165, 233, .06);--md-sys-color-on-secondary-container: #0369a1;--md-sys-color-tertiary: #d946ef;--md-sys-color-on-tertiary: #ffffff;--md-sys-color-background: #f8fafc;--md-sys-color-on-background: #0f172a;--md-sys-color-surface: rgba(255, 255, 255, .75);--md-sys-color-on-surface: #0f172a;--md-sys-color-surface-variant: rgba(241, 245, 249, .8);--md-sys-color-on-surface-variant: #475569;--md-sys-color-outline: #64748b;--md-sys-color-outline-variant: #94a3b8;--border-color: rgba(15, 23, 42, .08);--border-color-muted: rgba(15, 23, 42, .05);--md-sys-color-error: #ef4444;--md-sys-color-on-error: #ffffff;--md-sys-color-success: #10b981;--md-sys-color-on-success: #ffffff;--md-sys-color-card-border: rgba(15, 23, 42, .06);--glass-shadow: 0 8px 32px 0 rgba(15, 23, 42, .04);--glass-blur: blur(16px)}html[data-theme=dark]{--md-sys-color-primary: #6366f1;--md-sys-color-on-primary: #ffffff;--md-sys-color-primary-container: rgba(99, 102, 241, .12);--md-sys-color-on-primary-container: #c7d2fe;--md-sys-color-secondary: #38bdf8;--md-sys-color-on-secondary: #0f172a;--md-sys-color-secondary-container: rgba(56, 189, 248, .08);--md-sys-color-on-secondary-container: #bae6fd;--md-sys-color-tertiary: #f472b6;--md-sys-color-on-tertiary: #0f172a;--md-sys-color-background: #090a0f;--md-sys-color-on-background: #f4f4f5;--md-sys-color-surface: rgba(18, 20, 29, .65);--md-sys-color-on-surface: #f4f4f5;--md-sys-color-surface-variant: rgba(26, 29, 41, .55);--md-sys-color-on-surface-variant: #a1a1aa;--md-sys-color-outline: #a1a1aa;--md-sys-color-outline-variant: #71717a;--border-color: rgba(255, 255, 255, .09);--border-color-muted: rgba(255, 255, 255, .05);--md-sys-color-error: #f87171;--md-sys-color-on-error: #ffffff;--md-sys-color-success: #34d399;--md-sys-color-on-success: #090a0f;--md-sys-color-card-border: rgba(255, 255, 255, .04);--glass-shadow: 0 8px 32px 0 rgba(0, 0, 0, .3);--glass-blur: blur(16px)}*{box-sizing:border-box;margin:0;padding:0}body{background-color:var(--md-sys-color-background);background-image:radial-gradient(at 0% 0%,var(--md-sys-color-primary-container) 0px,transparent 50%),radial-gradient(at 100% 0%,var(--md-sys-color-secondary-container) 0px,transparent 40%);color:var(--md-sys-color-on-background);overflow:hidden;height:100vh;width:100vw;font-size:16px;transition:background-color var(--md-transition-duration) ease}.app-container{display:flex;flex-direction:row;height:100vh;width:100vw;overflow:hidden}.sidebar{width:280px;background-color:var(--md-sys-color-surface);backdrop-filter:var(--glass-blur);-webkit-backdrop-filter:var(--glass-blur);border-right:1px solid var(--border-color);display:flex;flex-direction:column;padding:28px 18px;justify-content:space-between;box-shadow:var(--glass-shadow);z-index:11;transition:all var(--md-transition-duration) var(--md-transition-easing)}.sidebar-logo{display:flex;align-items:center;gap:12px;padding:0 12px 28px}.sidebar-logo-icon{color:var(--md-sys-color-primary);width:26px;height:26px;filter:drop-shadow(0 2px 8px rgba(99,102,241,.3))}.sidebar-logo-text{font-size:1.15rem;font-weight:700;letter-spacing:-.02em;color:var(--md-sys-color-on-background)}.nav-list{display:flex;flex-direction:column;gap:6px;margin-bottom:auto}.nav-item{display:flex;align-items:center;gap:12px;padding:10px 16px;height:48px;border-radius:var(--md-shape-corner-medium);color:var(--md-sys-color-on-surface-variant);font-size:.9rem;font-weight:500;cursor:pointer;transition:all var(--md-transition-duration) var(--md-transition-easing);-webkit-user-select:none;user-select:none;text-decoration:none;position:relative}.nav-item:hover{background-color:var(--md-sys-color-surface-variant);color:var(--md-sys-color-primary);transform:translate(2px)}.nav-item.active{background-color:var(--md-sys-color-primary-container);color:var(--md-sys-color-primary);font-weight:600}.nav-item.active:before{content:"";position:absolute;left:0;top:14px;bottom:14px;width:4px;background-color:var(--md-sys-color-primary);border-radius:0 4px 4px 0}.sidebar-footer{padding:18px 12px 0;border-top:1px solid var(--border-color);font-size:.78rem;color:var(--md-sys-color-on-surface-variant);display:flex;flex-direction:column;gap:6px;opacity:.95}.main-content{flex:1;display:flex;flex-direction:column;background-color:transparent;overflow:hidden}.top-status-bar{height:64px;background-color:var(--md-sys-color-surface);backdrop-filter:var(--glass-blur);-webkit-backdrop-filter:var(--glass-blur);border-bottom:1px solid var(--border-color);display:flex;align-items:center;justify-content:space-between;padding:0 24px;z-index:10;box-shadow:var(--glass-shadow)}.current-model-info{display:flex;align-items:center;gap:10px;font-size:.88rem}.status-indicator{width:8px;height:8px;border-radius:50%;background-color:var(--md-sys-color-success);box-shadow:0 0 8px var(--md-sys-color-success)}.status-indicator.busy{background-color:var(--md-sys-color-primary);box-shadow:0 0 8px var(--md-sys-color-primary);animation:pulse 1.8s infinite}.status-indicator.offline{background-color:var(--md-sys-color-on-surface-variant);box-shadow:none;opacity:.6}.telemetry-group{display:flex;align-items:center;gap:8px}.telemetry-chip{display:flex;align-items:center;gap:6px;background-color:var(--md-sys-color-surface-variant);border:1px solid var(--border-color);border-radius:var(--md-shape-corner-medium);padding:6px 12px;font-size:.8rem;font-weight:500;color:var(--md-sys-color-on-surface-variant);transition:all var(--md-transition-duration)}.telemetry-chip-icon{width:14px;height:14px}.readiness-card{margin:0 0 18px;border-left:4px solid var(--md-sys-color-primary)}.readiness-card-warning{border-left-color:var(--md-sys-color-error)}.readiness-header{display:flex;align-items:flex-start;justify-content:space-between;gap:14px}.readiness-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap}.readiness-actions .m3-btn{height:34px;padding:0 14px}.readiness-issues,.readiness-cleanup-list{display:flex;flex-direction:column;gap:6px;margin-top:12px;font-size:.82rem;color:var(--md-sys-color-on-surface-variant)}.readiness-issues span{color:var(--md-sys-color-error)}.theme-toggle-btn{background:var(--md-sys-color-surface-variant);border:1px solid var(--border-color);color:var(--md-sys-color-on-surface-variant);cursor:pointer;display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:var(--md-shape-corner-medium);transition:all var(--md-transition-duration) var(--md-transition-easing);margin-right:6px}.theme-toggle-btn:hover{color:var(--md-sys-color-primary);border-color:var(--md-sys-color-primary);background-color:var(--md-sys-color-primary-container);transform:scale(1.05)}.workspace-area{flex:1;overflow-y:auto;padding:28px}.workspace-title-section{margin-bottom:24px}.workspace-title{font-size:1.6rem;font-weight:700;color:var(--md-sys-color-on-background);letter-spacing:-.02em}.workspace-subtitle{font-size:.9rem;color:var(--md-sys-color-on-surface-variant);margin-top:4px;opacity:.8}.m3-card{background-color:var(--md-sys-color-surface);backdrop-filter:var(--glass-blur);-webkit-backdrop-filter:var(--glass-blur);border:1px solid var(--md-sys-color-card-border);border-radius:var(--md-shape-corner-large);padding:24px;margin-bottom:24px;box-shadow:var(--glass-shadow);transition:all var(--md-transition-duration) var(--md-transition-easing)}.m3-card:hover{border-color:#6366f126}.m3-card-title{font-size:1.05rem;font-weight:600;margin-bottom:20px;color:var(--md-sys-color-on-surface);display:flex;align-items:center;gap:8px}.m3-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;height:38px;padding:0 20px;border-radius:var(--md-shape-corner-medium);font-size:.88rem;font-weight:550;font-family:inherit;border:none;cursor:pointer;transition:all var(--md-transition-duration) var(--md-transition-easing);-webkit-user-select:none;user-select:none;line-height:1;white-space:nowrap}.m3-btn svg{flex:0 0 auto}.m3-btn .progress-spinner{width:14px;height:14px;border-width:2px;border-color:currentColor;border-right-color:transparent;border-bottom-color:transparent}.m3-btn:disabled{opacity:.45;cursor:not-allowed}.m3-btn-filled{background:linear-gradient(135deg,var(--md-sys-color-primary),#8b5cf6);color:var(--md-sys-color-on-primary);box-shadow:0 4px 12px #6366f133}.m3-btn-filled:hover:not(:disabled){box-shadow:0 6px 20px #6366f159;transform:translateY(-1px);filter:brightness(1.04)}.m3-btn-tonal{background-color:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.m3-btn-tonal:hover:not(:disabled){background-color:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary);transform:translateY(-1px)}.m3-btn-outlined{background-color:transparent;border:1px solid var(--border-color);color:var(--md-sys-color-on-surface)}.m3-btn-outlined:hover:not(:disabled){background-color:var(--md-sys-color-surface-variant);border-color:var(--md-sys-color-primary);color:var(--md-sys-color-primary)}.m3-btn-error{background-color:#f43f5e14;border:1px solid rgba(244,63,94,.18);color:#e11d48;box-shadow:0 2px 8px #f43f5e0d}.m3-btn-error:hover:not(:disabled){background-color:#f43f5e26;border-color:#f43f5e4d;transform:translateY(-1px)}html[data-theme=dark] .m3-btn-error{background-color:#fb718517;border:1px solid rgba(251,113,133,.22);color:#fda4af;box-shadow:0 2px 8px #fb71850a}html[data-theme=dark] .m3-btn-error:hover:not(:disabled){background-color:#fb718529;border-color:#fb718559}.m3-field-group{display:flex;flex-direction:column;gap:18px}.m3-text-field{display:flex;flex-direction:column;gap:6px}.m3-text-field-label{font-size:.82rem;font-weight:600;letter-spacing:.01em;color:var(--md-sys-color-on-surface-variant)}.m3-input,.m3-textarea{width:100%;background-color:var(--md-sys-color-surface-variant);border:1px solid var(--border-color);border-radius:var(--md-shape-corner-medium);padding:10px 14px;font-family:inherit;font-size:.92rem;color:var(--md-sys-color-on-surface);transition:all var(--md-transition-duration) var(--md-transition-easing);outline:none}.m3-input{height:42px}.m3-textarea{min-height:90px;resize:vertical}.m3-input:focus,.m3-textarea:focus{border-color:var(--md-sys-color-primary);background-color:var(--md-sys-color-surface);box-shadow:0 0 0 3px #6366f11f}.m3-slider-group{display:flex;flex-direction:column;gap:4px}.m3-slider-header{display:flex;justify-content:space-between;align-items:center}.m3-slider-label{font-size:.82rem;font-weight:600;color:var(--md-sys-color-on-surface-variant)}.m3-slider-value{font-size:.78rem;font-weight:700;color:var(--md-sys-color-primary);background-color:var(--md-sys-color-primary-container);padding:2px 8px;border-radius:var(--md-shape-corner-full)}.m3-slider{-webkit-appearance:none;appearance:none;width:100%;height:4px;border-radius:var(--md-shape-corner-full);background:var(--border-color);outline:none;margin:12px 0;transition:background .25s}.m3-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:16px;height:16px;border-radius:50%;background:var(--md-sys-color-primary);border:2px solid var(--md-sys-color-surface);box-shadow:0 0 8px #6366f159;cursor:pointer;transition:all var(--md-transition-duration) var(--md-transition-easing)}.m3-slider::-webkit-slider-thumb:hover{transform:scale(1.25);box-shadow:0 0 12px #6366f199}.m3-switch-row{display:flex;justify-content:space-between;align-items:center;padding:8px 0}.m3-switch-info{display:flex;flex-direction:column;gap:2px}.m3-switch-title{font-size:.9rem;font-weight:600;color:var(--md-sys-color-on-surface)}.m3-switch-description{font-size:.78rem;color:var(--md-sys-color-on-surface-variant);opacity:.8}.m3-switch{position:relative;display:inline-block;width:46px;height:26px}.m3-switch input{opacity:0;width:0;height:0}.m3-switch-slider{position:absolute;cursor:pointer;inset:0;background-color:var(--md-sys-color-surface-variant);border:1px solid var(--border-color);border-radius:26px;transition:.25s}.m3-switch-slider:before{position:absolute;content:"";height:18px;width:18px;left:3px;bottom:3px;background-color:var(--md-sys-color-on-surface-variant);border-radius:50%;transition:.25s}.m3-switch input:checked+.m3-switch-slider{background-color:var(--md-sys-color-primary);border-color:var(--md-sys-color-primary)}.m3-switch input:checked+.m3-switch-slider:before{transform:translate(20px);background-color:var(--md-sys-color-on-primary)}.m3-segmented-button{display:flex;background-color:var(--md-sys-color-surface-variant);border:1px solid var(--border-color);border-radius:var(--md-shape-corner-medium);padding:3px;overflow:hidden;width:100%}.m3-segment-item{flex:1;text-align:center;padding:8px 0;font-size:.82rem;font-weight:600;cursor:pointer;background-color:transparent;color:var(--md-sys-color-on-surface-variant);transition:all var(--md-transition-duration) var(--md-transition-easing);border-radius:calc(var(--md-shape-corner-medium) - 2px);-webkit-user-select:none;user-select:none;border:none}.m3-segment-item:hover:not(.active):not(.disabled){color:var(--md-sys-color-on-surface);background-color:#00000008}html[data-theme=dark] .m3-segment-item:hover:not(.active):not(.disabled){background-color:#ffffff0a}.m3-segment-item.active{background-color:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary);box-shadow:0 2px 8px #6366f133}.m3-segment-item.disabled{opacity:.45;cursor:not-allowed;text-decoration:line-through}.generator-layout{display:grid;grid-template-columns:1fr;gap:24px}@media(min-width:1000px){.generator-layout{grid-template-columns:1.15fr .85fr}}.chips-container{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}.status-chip{display:inline-flex;align-items:center;gap:6px;background-color:var(--md-sys-color-surface-variant);border:1px solid var(--border-color);color:var(--md-sys-color-on-surface-variant);padding:5px 12px;border-radius:var(--md-shape-corner-full);font-size:.78rem;font-weight:550;cursor:pointer;transition:all var(--md-transition-duration)}.status-chip:hover{background-color:var(--md-sys-color-primary-container);border-color:var(--md-sys-color-primary);color:var(--md-sys-color-primary)}.prompt-actions{display:flex;justify-content:space-between;align-items:center;margin-top:16px}.aspect-ratio-btn{position:relative;transition:all var(--md-transition-duration) var(--md-transition-easing)}.aspect-ratio-btn.active{background-color:var(--md-sys-color-primary-container)!important;border-color:var(--md-sys-color-primary)!important;color:var(--md-sys-color-primary)!important;box-shadow:0 0 0 2px #6366f126}.aspect-ratio-preview{border:1.5px solid currentColor;border-radius:2px;opacity:.6;background-color:transparent;transition:all .2s ease;margin-bottom:6px}.aspect-ratio-btn:hover .aspect-ratio-preview,.aspect-ratio-btn.active .aspect-ratio-preview{opacity:1;background-color:#6366f10f}.ratio-1-1{width:16px;height:16px}.ratio-4-3{width:20px;height:15px}.ratio-16-9{width:24px;height:13.5px}.ratio-9-16{width:13.5px;height:24px}.output-panel{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:380px}.output-image-container{width:100%;aspect-ratio:1;background-color:var(--md-sys-color-surface-variant);border-radius:var(--md-shape-corner-large);border:1px solid var(--border-color);display:flex;align-items:center;justify-content:center;overflow:hidden;position:relative;box-shadow:inset 0 2px 8px #0000000d}.output-image{width:100%;height:100%;object-fit:contain;transition:transform .3s}.output-image:hover{transform:scale(1.015)}.progress-overlay{position:absolute;inset:0;background-color:#090a0fd9;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);display:flex;flex-direction:column;align-items:center;justify-content:center;color:#fff;gap:14px;padding:24px;z-index:5}.progress-spinner{width:44px;height:44px;border:3px solid rgba(255,255,255,.15);border-top-color:var(--md-sys-color-primary);border-radius:50%;animation:spin .9s cubic-bezier(.5,.1,.4,.9) infinite;box-shadow:0 0 12px #6366f133}.loading-dot{width:8px;height:8px;border-radius:50%;background:var(--md-sys-color-primary);opacity:.9;animation:pulse 1.2s ease-in-out infinite;box-shadow:0 0 8px var(--md-sys-color-primary)}.progress-text{font-size:1.05rem;font-weight:700;letter-spacing:-.01em}.progress-bar-container{width:80%;height:5px;background-color:#ffffff1f;border-radius:var(--md-shape-corner-full);overflow:hidden;margin:4px 0}.progress-bar-fill{height:100%;background:linear-gradient(90deg,var(--md-sys-color-primary),#d946ef);width:0%;transition:width .4s cubic-bezier(.1,.8,.25,1);box-shadow:0 0 8px #6366f166}.output-placeholder-icon{width:48px;height:48px;color:var(--md-sys-color-on-surface-variant);opacity:.5;filter:drop-shadow(0 2px 10px rgba(0,0,0,.05))}.output-placeholder-text{font-size:.88rem;color:var(--md-sys-color-on-surface-variant);margin-top:12px;text-align:center;line-height:1.5;opacity:.8}.image-actions{display:flex;gap:10px;margin-top:16px;width:100%}.image-actions button{flex:1}.models-grid{display:grid;grid-template-columns:1fr;gap:20px}@media(min-width:800px){.models-grid{grid-template-columns:1fr 1fr}}.model-card{display:flex;flex-direction:column;justify-content:space-between;border:1px solid var(--border-color-muted)}.model-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:12px}.model-name{font-size:1.05rem;font-weight:600}.model-badge{font-size:.72rem;font-weight:700;padding:3px 8px;border-radius:var(--md-shape-corner-full)}.model-badge-gguf{background-color:#f59e0b26;color:#d97706}.model-badge-safetensors{background-color:#0ea5e926;color:#0284c7}.model-description{font-size:.82rem;color:var(--md-sys-color-on-surface-variant);margin-bottom:16px;line-height:1.45;opacity:.9}.model-status-info{margin-top:auto;padding-top:12px;border-top:1px solid var(--border-color-muted);display:flex;align-items:center;justify-content:space-between}.model-size{font-size:.8rem;font-weight:550;color:var(--md-sys-color-on-surface-variant)}.model-progress-section{width:100%;margin-top:12px}.model-progress-label{display:flex;justify-content:space-between;font-size:.78rem;font-weight:600;margin-bottom:4px}.model-progress-bar{width:100%;height:6px;background-color:var(--border-color-muted);border-radius:var(--md-shape-corner-full);overflow:hidden}.model-progress-fill{height:100%;background:linear-gradient(90deg,var(--md-sys-color-primary),var(--md-sys-color-secondary));width:0%;transition:width .25s ease}.import-box{border:2px dashed var(--border-color);border-radius:var(--md-shape-corner-large);padding:32px;text-align:center;cursor:pointer;transition:all var(--md-transition-duration);background-color:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface-variant);display:flex;flex-direction:column;align-items:center;gap:10px}.import-box:hover{background-color:var(--md-sys-color-primary-container);border-color:var(--md-sys-color-primary);color:var(--md-sys-color-primary)}.import-icon{width:40px;height:40px;color:var(--md-sys-color-primary);filter:drop-shadow(0 2px 6px rgba(99,102,241,.15))}.gallery-section{margin-top:36px}.gallery-header{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.gallery-selection-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.gallery-selection-actions .m3-btn{height:34px;padding:0 14px}.gallery-selection-count{font-size:.82rem;font-weight:700;color:var(--md-sys-color-primary);padding:7px 10px;border-radius:var(--md-shape-corner-full);background:var(--md-sys-color-primary-container)}.gallery-hint{margin-top:8px;font-size:.78rem;color:var(--md-sys-color-outline)}.gallery-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:12px;margin-top:16px}.gallery-item{aspect-ratio:1;border-radius:var(--md-shape-corner-medium);overflow:hidden;border:1px solid var(--border-color-muted);cursor:pointer;position:relative;contain:layout paint style;transition:border-color var(--md-transition-duration),box-shadow var(--md-transition-duration),transform var(--md-transition-duration)}.gallery-item.selected{border-color:var(--md-sys-color-primary);box-shadow:0 0 0 3px #4f46e540}.gallery-thumb{width:100%;height:100%;object-fit:cover;transition:transform var(--md-transition-duration) var(--md-transition-easing)}.gallery-item:hover .gallery-thumb{transform:scale(1.05)}.gallery-select-badge{position:absolute;top:8px;right:8px;width:26px;height:26px;border-radius:50%;display:grid;place-items:center;color:var(--md-sys-color-on-primary);background:var(--md-sys-color-primary);box-shadow:0 2px 8px #00000047}.app-dialog-backdrop{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;padding:24px;background:#080c18a3;-webkit-backdrop-filter:blur(4px) saturate(.86);backdrop-filter:blur(4px) saturate(.86)}.app-dialog{width:min(460px,100%);background:color-mix(in srgb,var(--md-sys-color-surface) 96%,white 4%);color:var(--md-sys-color-on-surface);border:1px solid color-mix(in srgb,var(--md-sys-color-outline) 55%,transparent);border-radius:var(--md-shape-corner-large);box-shadow:0 22px 60px #0000006b,0 0 0 1px #ffffff14 inset;padding:24px}.app-dialog-title{font-size:1.15rem;font-weight:800;margin:0 0 10px}.app-dialog-message{font-size:.92rem;line-height:1.45;color:var(--md-sys-color-on-surface-variant);margin:0;white-space:pre-wrap}.app-dialog-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:22px;flex-wrap:wrap}.gallery-empty{text-align:center;padding:36px;color:var(--md-sys-color-on-surface-variant);font-size:.85rem;opacity:.8}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.55;transform:scale(1.06)}}.sidebar-specs-header{display:flex;align-items:center;gap:6px;margin-bottom:6px;font-weight:600;color:var(--md-sys-color-on-background)}.sidebar-specs-item{text-overflow:ellipsis;overflow:hidden;white-space:nowrap;opacity:.85;margin-bottom:2px;color:var(--md-sys-color-on-background)}.sidebar-specs-os{color:var(--md-sys-color-primary);font-weight:600;margin-top:4px} diff --git a/app/dist/assets/index-jXnaCwmK.js b/app/dist/assets/index-jXnaCwmK.js deleted file mode 100644 index bd6788e..0000000 --- a/app/dist/assets/index-jXnaCwmK.js +++ /dev/null @@ -1,121 +0,0 @@ -(function(){const b=document.createElement("link").relList;if(b&&b.supports&&b.supports("modulepreload"))return;for(const x of document.querySelectorAll('link[rel="modulepreload"]'))r(x);new MutationObserver(x=>{for(const N of x)if(N.type==="childList")for(const G of N.addedNodes)G.tagName==="LINK"&&G.rel==="modulepreload"&&r(G)}).observe(document,{childList:!0,subtree:!0});function E(x){const N={};return x.integrity&&(N.integrity=x.integrity),x.referrerPolicy&&(N.referrerPolicy=x.referrerPolicy),x.crossOrigin==="use-credentials"?N.credentials="include":x.crossOrigin==="anonymous"?N.credentials="omit":N.credentials="same-origin",N}function r(x){if(x.ep)return;x.ep=!0;const N=E(x);fetch(x.href,N)}})();function fm(f){return f&&f.__esModule&&Object.prototype.hasOwnProperty.call(f,"default")?f.default:f}var Bs={exports:{}},ai={};var Zd;function E0(){if(Zd)return ai;Zd=1;var f=Symbol.for("react.transitional.element"),b=Symbol.for("react.fragment");function E(r,x,N){var G=null;if(N!==void 0&&(G=""+N),x.key!==void 0&&(G=""+x.key),"key"in x){N={};for(var H in x)H!=="key"&&(N[H]=x[H])}else N=x;return x=N.ref,{$$typeof:f,type:r,key:G,ref:x!==void 0?x:null,props:N}}return ai.Fragment=b,ai.jsx=E,ai.jsxs=E,ai}var Kd;function N0(){return Kd||(Kd=1,Bs.exports=E0()),Bs.exports}var c=N0(),Rs={exports:{}},le={};var Jd;function A0(){if(Jd)return le;Jd=1;var f=Symbol.for("react.transitional.element"),b=Symbol.for("react.portal"),E=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),x=Symbol.for("react.profiler"),N=Symbol.for("react.consumer"),G=Symbol.for("react.context"),H=Symbol.for("react.forward_ref"),w=Symbol.for("react.suspense"),h=Symbol.for("react.memo"),A=Symbol.for("react.lazy"),U=Symbol.for("react.activity"),V=Symbol.iterator;function fe(d){return d===null||typeof d!="object"?null:(d=V&&d[V]||d["@@iterator"],typeof d=="function"?d:null)}var X={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},L=Object.assign,te={};function W(d,j,B){this.props=d,this.context=j,this.refs=te,this.updater=B||X}W.prototype.isReactComponent={},W.prototype.setState=function(d,j){if(typeof d!="object"&&typeof d!="function"&&d!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,d,j,"setState")},W.prototype.forceUpdate=function(d){this.updater.enqueueForceUpdate(this,d,"forceUpdate")};function Oe(){}Oe.prototype=W.prototype;function de(d,j,B){this.props=d,this.context=j,this.refs=te,this.updater=B||X}var Ee=de.prototype=new Oe;Ee.constructor=de,L(Ee,W.prototype),Ee.isPureReactComponent=!0;var ke=Array.isArray;function Ae(){}var P={H:null,A:null,T:null,S:null},ve=Object.prototype.hasOwnProperty;function Ke(d,j,B){var q=B.ref;return{$$typeof:f,type:d,key:j,ref:q!==void 0?q:null,props:B}}function yt(d,j){return Ke(d.type,j,d.props)}function me(d){return typeof d=="object"&&d!==null&&d.$$typeof===f}function Je(d){var j={"=":"=0",":":"=2"};return"$"+d.replace(/[=:]/g,function(B){return j[B]})}var st=/\/+/g;function $e(d,j){return typeof d=="object"&&d!==null&&d.key!=null?Je(""+d.key):j.toString(36)}function Xe(d){switch(d.status){case"fulfilled":return d.value;case"rejected":throw d.reason;default:switch(typeof d.status=="string"?d.then(Ae,Ae):(d.status="pending",d.then(function(j){d.status==="pending"&&(d.status="fulfilled",d.value=j)},function(j){d.status==="pending"&&(d.status="rejected",d.reason=j)})),d.status){case"fulfilled":return d.value;case"rejected":throw d.reason}}throw d}function _(d,j,B,q,K){var ee=typeof d;(ee==="undefined"||ee==="boolean")&&(d=null);var se=!1;if(d===null)se=!0;else switch(ee){case"bigint":case"string":case"number":se=!0;break;case"object":switch(d.$$typeof){case f:case b:se=!0;break;case A:return se=d._init,_(se(d._payload),j,B,q,K)}}if(se)return K=K(d),se=q===""?"."+$e(d,0):q,ke(K)?(B="",se!=null&&(B=se.replace(st,"$&/")+"/"),_(K,j,B,"",function(gt){return gt})):K!=null&&(me(K)&&(K=yt(K,B+(K.key==null||d&&d.key===K.key?"":(""+K.key).replace(st,"$&/")+"/")+se)),j.push(K)),1;se=0;var Ze=q===""?".":q+":";if(ke(d))for(var Ne=0;Ne>>1,ce=_[he];if(0>>1;hex(B,J))qx(K,B)?(_[he]=K,_[q]=J,he=q):(_[he]=B,_[j]=J,he=j);else if(qx(K,J))_[he]=K,_[q]=J,he=q;else break e}}return C}function x(_,C){var J=_.sortIndex-C.sortIndex;return J!==0?J:_.id-C.id}if(f.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var N=performance;f.unstable_now=function(){return N.now()}}else{var G=Date,H=G.now();f.unstable_now=function(){return G.now()-H}}var w=[],h=[],A=1,U=null,V=3,fe=!1,X=!1,L=!1,te=!1,W=typeof setTimeout=="function"?setTimeout:null,Oe=typeof clearTimeout=="function"?clearTimeout:null,de=typeof setImmediate<"u"?setImmediate:null;function Ee(_){for(var C=E(h);C!==null;){if(C.callback===null)r(h);else if(C.startTime<=_)r(h),C.sortIndex=C.expirationTime,b(w,C);else break;C=E(h)}}function ke(_){if(L=!1,Ee(_),!X)if(E(w)!==null)X=!0,Ae||(Ae=!0,Je());else{var C=E(h);C!==null&&Xe(ke,C.startTime-_)}}var Ae=!1,P=-1,ve=5,Ke=-1;function yt(){return te?!0:!(f.unstable_now()-Ke_&&yt());){var he=U.callback;if(typeof he=="function"){U.callback=null,V=U.priorityLevel;var ce=he(U.expirationTime<=_);if(_=f.unstable_now(),typeof ce=="function"){U.callback=ce,Ee(_),C=!0;break t}U===E(w)&&r(w),Ee(_)}else r(w);U=E(w)}if(U!==null)C=!0;else{var d=E(h);d!==null&&Xe(ke,d.startTime-_),C=!1}}break e}finally{U=null,V=J,fe=!1}C=void 0}}finally{C?Je():Ae=!1}}}var Je;if(typeof de=="function")Je=function(){de(me)};else if(typeof MessageChannel<"u"){var st=new MessageChannel,$e=st.port2;st.port1.onmessage=me,Je=function(){$e.postMessage(null)}}else Je=function(){W(me,0)};function Xe(_,C){P=W(function(){_(f.unstable_now())},C)}f.unstable_IdlePriority=5,f.unstable_ImmediatePriority=1,f.unstable_LowPriority=4,f.unstable_NormalPriority=3,f.unstable_Profiling=null,f.unstable_UserBlockingPriority=2,f.unstable_cancelCallback=function(_){_.callback=null},f.unstable_forceFrameRate=function(_){0>_||125<_?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):ve=0<_?Math.floor(1e3/_):5},f.unstable_getCurrentPriorityLevel=function(){return V},f.unstable_next=function(_){switch(V){case 1:case 2:case 3:var C=3;break;default:C=V}var J=V;V=C;try{return _()}finally{V=J}},f.unstable_requestPaint=function(){te=!0},f.unstable_runWithPriority=function(_,C){switch(_){case 1:case 2:case 3:case 4:case 5:break;default:_=3}var J=V;V=_;try{return C()}finally{V=J}},f.unstable_scheduleCallback=function(_,C,J){var he=f.unstable_now();switch(typeof J=="object"&&J!==null?(J=J.delay,J=typeof J=="number"&&0he?(_.sortIndex=J,b(h,_),E(w)===null&&_===E(h)&&(L?(Oe(P),P=-1):L=!0,Xe(ke,J-he))):(_.sortIndex=ce,b(w,_),X||fe||(X=!0,Ae||(Ae=!0,Je()))),_},f.unstable_shouldYield=yt,f.unstable_wrapCallback=function(_){var C=V;return function(){var J=V;V=C;try{return _.apply(this,arguments)}finally{V=J}}}})(qs)),qs}var Fd;function O0(){return Fd||(Fd=1,Gs.exports=C0()),Gs.exports}var Ls={exports:{}},Mt={};var Id;function U0(){if(Id)return Mt;Id=1;var f=Zs();function b(w){var h="https://react.dev/errors/"+w;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(f)}catch(b){console.error(b)}}return f(),Ls.exports=U0(),Ls.exports}var em;function B0(){if(em)return ni;em=1;var f=O0(),b=Zs(),E=w0();function r(e){var t="https://react.dev/errors/"+e;if(1ce||(e.current=he[ce],he[ce]=null,ce--)}function B(e,t){ce++,he[ce]=e.current,e.current=t}var q=d(null),K=d(null),ee=d(null),se=d(null);function Ze(e,t){switch(B(ee,t),B(K,e),B(q,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?yd(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=yd(t),e=gd(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}j(q),B(q,e)}function Ne(){j(q),j(K),j(ee)}function gt(e){e.memoizedState!==null&&B(se,e);var t=q.current,l=gd(t,e.type);t!==l&&(B(K,e),B(q,l))}function Et(e){K.current===e&&(j(q),j(K)),se.current===e&&(j(se),Pn._currentValue=J)}var We,Ht;function Tt(e){if(We===void 0)try{throw Error()}catch(l){var t=l.stack.trim().match(/\n( *(at )?)/);We=t&&t[1]||"",Ht=-1)":-1n||o[a]!==p[n]){var z=` -`+o[a].replace(" at new "," at ");return e.displayName&&z.includes("")&&(z=z.replace("",e.displayName)),z}while(1<=a&&0<=n);break}}}finally{Z=!1,Error.prepareStackTrace=l}return(l=e?e.displayName||e.name:"")?Tt(l):""}function D(e,t){switch(e.tag){case 26:case 27:case 5:return Tt(e.type);case 16:return Tt("Lazy");case 13:return e.child!==t&&t!==null?Tt("Suspense Fallback"):Tt("Suspense");case 19:return Tt("SuspenseList");case 0:case 15:return ie(e.type,!1);case 11:return ie(e.type.render,!1);case 1:return ie(e.type,!0);case 31:return Tt("Activity");default:return""}}function R(e){try{var t="",l=null;do t+=D(e,l),l=e,e=e.return;while(e);return t}catch(a){return` -Error generating stack: `+a.message+` -`+a.stack}}var F=Object.prototype.hasOwnProperty,ae=f.unstable_scheduleCallback,At=f.unstable_cancelCallback,Gt=f.unstable_shouldYield,dl=f.unstable_requestPaint,Re=f.unstable_now,al=f.unstable_getCurrentPriorityLevel,Fe=f.unstable_ImmediatePriority,za=f.unstable_UserBlockingPriority,Ta=f.unstable_NormalPriority,Y=f.unstable_LowPriority,ye=f.unstable_IdlePriority,He=f.log,pt=f.unstable_setDisableYieldValue,ft=null,tt=null;function qt(e){if(typeof He=="function"&&pt(e),tt&&typeof tt.setStrictMode=="function")try{tt.setStrictMode(ft,e)}catch{}}var rt=Math.clz32?Math.clz32:xe,vt=Math.log,Se=Math.LN2;function xe(e){return e>>>=0,e===0?32:31-(vt(e)/Se|0)|0}var Ge=256,_e=262144,Ie=4194304;function je(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function dt(e,t,l){var a=e.pendingLanes;if(a===0)return 0;var n=0,i=e.suspendedLanes,u=e.pingedLanes;e=e.warmLanes;var s=a&134217727;return s!==0?(a=s&~i,a!==0?n=je(a):(u&=s,u!==0?n=je(u):l||(l=s&~e,l!==0&&(n=je(l))))):(s=a&~i,s!==0?n=je(s):u!==0?n=je(u):l||(l=a&~e,l!==0&&(n=je(l)))),n===0?0:t!==0&&t!==n&&(t&i)===0&&(i=n&-n,l=t&-t,i>=l||i===32&&(l&4194048)!==0)?t:n}function bt(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function cl(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function ci(){var e=Ie;return Ie<<=1,(Ie&62914560)===0&&(Ie=4194304),e}function ju(e){for(var t=[],l=0;31>l;l++)t.push(e);return t}function mn(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function ym(e,t,l,a,n,i){var u=e.pendingLanes;e.pendingLanes=l,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=l,e.entangledLanes&=l,e.errorRecoveryDisabledLanes&=l,e.shellSuspendCounter=0;var s=e.entanglements,o=e.expirationTimes,p=e.hiddenUpdates;for(l=u&~l;0"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var xm=/[\n"\\]/g;function Jt(e){return e.replace(xm,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function Au(e,t,l,a,n,i,u,s){e.name="",u!=null&&typeof u!="function"&&typeof u!="symbol"&&typeof u!="boolean"?e.type=u:e.removeAttribute("type"),t!=null?u==="number"?(t===0&&e.value===""||e.value!=t)&&(e.value=""+Kt(t)):e.value!==""+Kt(t)&&(e.value=""+Kt(t)):u!=="submit"&&u!=="reset"||e.removeAttribute("value"),t!=null?Du(e,u,Kt(t)):l!=null?Du(e,u,Kt(l)):a!=null&&e.removeAttribute("value"),n==null&&i!=null&&(e.defaultChecked=!!i),n!=null&&(e.checked=n&&typeof n!="function"&&typeof n!="symbol"),s!=null&&typeof s!="function"&&typeof s!="symbol"&&typeof s!="boolean"?e.name=""+Kt(s):e.removeAttribute("name")}function sf(e,t,l,a,n,i,u,s){if(i!=null&&typeof i!="function"&&typeof i!="symbol"&&typeof i!="boolean"&&(e.type=i),t!=null||l!=null){if(!(i!=="submit"&&i!=="reset"||t!=null)){Nu(e);return}l=l!=null?""+Kt(l):"",t=t!=null?""+Kt(t):l,s||t===e.value||(e.value=t),e.defaultValue=t}a=a??n,a=typeof a!="function"&&typeof a!="symbol"&&!!a,e.checked=s?e.checked:!!a,e.defaultChecked=!!a,u!=null&&typeof u!="function"&&typeof u!="symbol"&&typeof u!="boolean"&&(e.name=u),Nu(e)}function Du(e,t,l){t==="number"&&oi(e.ownerDocument)===e||e.defaultValue===""+l||(e.defaultValue=""+l)}function Ca(e,t,l,a){if(e=e.options,t){t={};for(var n=0;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Bu=!1;if(yl)try{var pn={};Object.defineProperty(pn,"passive",{get:function(){Bu=!0}}),window.addEventListener("test",pn,pn),window.removeEventListener("test",pn,pn)}catch{Bu=!1}var Ul=null,Ru=null,di=null;function yf(){if(di)return di;var e,t=Ru,l=t.length,a,n="value"in Ul?Ul.value:Ul.textContent,i=n.length;for(e=0;e=Sn),xf=" ",_f=!1;function jf(e,t){switch(e){case"keyup":return $m.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function zf(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Ba=!1;function Fm(e,t){switch(e){case"compositionend":return zf(t);case"keypress":return t.which!==32?null:(_f=!0,xf);case"textInput":return e=t.data,e===xf&&_f?null:e;default:return null}}function Im(e,t){if(Ba)return e==="compositionend"||!Yu&&jf(e,t)?(e=yf(),di=Ru=Ul=null,Ba=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:l,offset:t-e};e=a}e:{for(;l;){if(l.nextSibling){l=l.nextSibling;break e}l=l.parentNode}l=void 0}l=Of(l)}}function wf(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?wf(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Bf(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=oi(e.document);t instanceof e.HTMLIFrameElement;){try{var l=typeof t.contentWindow.location.href=="string"}catch{l=!1}if(l)e=t.contentWindow;else break;t=oi(e.document)}return t}function Qu(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}var uh=yl&&"documentMode"in document&&11>=document.documentMode,Ra=null,Vu=null,zn=null,Zu=!1;function Rf(e,t,l){var a=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;Zu||Ra==null||Ra!==oi(a)||(a=Ra,"selectionStart"in a&&Qu(a)?a={start:a.selectionStart,end:a.selectionEnd}:(a=(a.ownerDocument&&a.ownerDocument.defaultView||window).getSelection(),a={anchorNode:a.anchorNode,anchorOffset:a.anchorOffset,focusNode:a.focusNode,focusOffset:a.focusOffset}),zn&&jn(zn,a)||(zn=a,a=iu(Vu,"onSelect"),0>=u,n-=u,sl=1<<32-rt(t)+n|l<ue?(pe=Q,Q=null):pe=Q.sibling;var Te=v(y,Q,g[ue],T);if(Te===null){Q===null&&(Q=pe);break}e&&Q&&Te.alternate===null&&t(y,Q),m=i(Te,m,ue),ze===null?$=Te:ze.sibling=Te,ze=Te,Q=pe}if(ue===g.length)return l(y,Q),be&&pl(y,ue),$;if(Q===null){for(;ueue?(pe=Q,Q=null):pe=Q.sibling;var ta=v(y,Q,Te.value,T);if(ta===null){Q===null&&(Q=pe);break}e&&Q&&ta.alternate===null&&t(y,Q),m=i(ta,m,ue),ze===null?$=ta:ze.sibling=ta,ze=ta,Q=pe}if(Te.done)return l(y,Q),be&&pl(y,ue),$;if(Q===null){for(;!Te.done;ue++,Te=g.next())Te=M(y,Te.value,T),Te!==null&&(m=i(Te,m,ue),ze===null?$=Te:ze.sibling=Te,ze=Te);return be&&pl(y,ue),$}for(Q=a(Q);!Te.done;ue++,Te=g.next())Te=S(Q,y,ue,Te.value,T),Te!==null&&(e&&Te.alternate!==null&&Q.delete(Te.key===null?ue:Te.key),m=i(Te,m,ue),ze===null?$=Te:ze.sibling=Te,ze=Te);return e&&Q.forEach(function(M0){return t(y,M0)}),be&&pl(y,ue),$}function Be(y,m,g,T){if(typeof g=="object"&&g!==null&&g.type===L&&g.key===null&&(g=g.props.children),typeof g=="object"&&g!==null){switch(g.$$typeof){case fe:e:{for(var $=g.key;m!==null;){if(m.key===$){if($=g.type,$===L){if(m.tag===7){l(y,m.sibling),T=n(m,g.props.children),T.return=y,y=T;break e}}else if(m.elementType===$||typeof $=="object"&&$!==null&&$.$$typeof===ve&&ga($)===m.type){l(y,m.sibling),T=n(m,g.props),Dn(T,g),T.return=y,y=T;break e}l(y,m);break}else t(y,m);m=m.sibling}g.type===L?(T=ra(g.props.children,y.mode,T,g.key),T.return=y,y=T):(T=_i(g.type,g.key,g.props,null,y.mode,T),Dn(T,g),T.return=y,y=T)}return u(y);case X:e:{for($=g.key;m!==null;){if(m.key===$)if(m.tag===4&&m.stateNode.containerInfo===g.containerInfo&&m.stateNode.implementation===g.implementation){l(y,m.sibling),T=n(m,g.children||[]),T.return=y,y=T;break e}else{l(y,m);break}else t(y,m);m=m.sibling}T=Pu(g,y.mode,T),T.return=y,y=T}return u(y);case ve:return g=ga(g),Be(y,m,g,T)}if(Xe(g))return k(y,m,g,T);if(Je(g)){if($=Je(g),typeof $!="function")throw Error(r(150));return g=$.call(g),I(y,m,g,T)}if(typeof g.then=="function")return Be(y,m,Ai(g),T);if(g.$$typeof===de)return Be(y,m,Ti(y,g),T);Di(y,g)}return typeof g=="string"&&g!==""||typeof g=="number"||typeof g=="bigint"?(g=""+g,m!==null&&m.tag===6?(l(y,m.sibling),T=n(m,g),T.return=y,y=T):(l(y,m),T=Iu(g,y.mode,T),T.return=y,y=T),u(y)):l(y,m)}return function(y,m,g,T){try{An=0;var $=Be(y,m,g,T);return Ka=null,$}catch(Q){if(Q===Za||Q===Ei)throw Q;var ze=Yt(29,Q,null,y.mode);return ze.lanes=T,ze.return=y,ze}}}var va=io(!0),uo=io(!1),Gl=!1;function rc(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function dc(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function ql(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function Ll(e,t,l){var a=e.updateQueue;if(a===null)return null;if(a=a.shared,(Me&2)!==0){var n=a.pending;return n===null?t.next=t:(t.next=n.next,n.next=t),a.pending=t,t=xi(e),Xf(e,null,l),t}return Si(e,a,t,l),xi(e)}function Cn(e,t,l){if(t=t.updateQueue,t!==null&&(t=t.shared,(l&4194048)!==0)){var a=t.lanes;a&=e.pendingLanes,l|=a,t.lanes=l,Ws(e,l)}}function mc(e,t){var l=e.updateQueue,a=e.alternate;if(a!==null&&(a=a.updateQueue,l===a)){var n=null,i=null;if(l=l.firstBaseUpdate,l!==null){do{var u={lane:l.lane,tag:l.tag,payload:l.payload,callback:null,next:null};i===null?n=i=u:i=i.next=u,l=l.next}while(l!==null);i===null?n=i=t:i=i.next=t}else n=i=t;l={baseState:a.baseState,firstBaseUpdate:n,lastBaseUpdate:i,shared:a.shared,callbacks:a.callbacks},e.updateQueue=l;return}e=l.lastBaseUpdate,e===null?l.firstBaseUpdate=t:e.next=t,l.lastBaseUpdate=t}var hc=!1;function On(){if(hc){var e=Va;if(e!==null)throw e}}function Un(e,t,l,a){hc=!1;var n=e.updateQueue;Gl=!1;var i=n.firstBaseUpdate,u=n.lastBaseUpdate,s=n.shared.pending;if(s!==null){n.shared.pending=null;var o=s,p=o.next;o.next=null,u===null?i=p:u.next=p,u=o;var z=e.alternate;z!==null&&(z=z.updateQueue,s=z.lastBaseUpdate,s!==u&&(s===null?z.firstBaseUpdate=p:s.next=p,z.lastBaseUpdate=o))}if(i!==null){var M=n.baseState;u=0,z=p=o=null,s=i;do{var v=s.lane&-536870913,S=v!==s.lane;if(S?(ge&v)===v:(a&v)===v){v!==0&&v===Qa&&(hc=!0),z!==null&&(z=z.next={lane:0,tag:s.tag,payload:s.payload,callback:null,next:null});e:{var k=e,I=s;v=t;var Be=l;switch(I.tag){case 1:if(k=I.payload,typeof k=="function"){M=k.call(Be,M,v);break e}M=k;break e;case 3:k.flags=k.flags&-65537|128;case 0:if(k=I.payload,v=typeof k=="function"?k.call(Be,M,v):k,v==null)break e;M=U({},M,v);break e;case 2:Gl=!0}}v=s.callback,v!==null&&(e.flags|=64,S&&(e.flags|=8192),S=n.callbacks,S===null?n.callbacks=[v]:S.push(v))}else S={lane:v,tag:s.tag,payload:s.payload,callback:s.callback,next:null},z===null?(p=z=S,o=M):z=z.next=S,u|=v;if(s=s.next,s===null){if(s=n.shared.pending,s===null)break;S=s,s=S.next,S.next=null,n.lastBaseUpdate=S,n.shared.pending=null}}while(!0);z===null&&(o=M),n.baseState=o,n.firstBaseUpdate=p,n.lastBaseUpdate=z,i===null&&(n.shared.lanes=0),Vl|=u,e.lanes=u,e.memoizedState=M}}function co(e,t){if(typeof e!="function")throw Error(r(191,e));e.call(t)}function so(e,t){var l=e.callbacks;if(l!==null)for(e.callbacks=null,e=0;ei?i:8;var u=_.T,s={};_.T=s,Uc(e,!1,t,l);try{var o=n(),p=_.S;if(p!==null&&p(s,o),o!==null&&typeof o=="object"&&typeof o.then=="function"){var z=yh(o,a);Rn(e,t,z,Zt(e))}else Rn(e,t,a,Zt(e))}catch(M){Rn(e,t,{then:function(){},status:"rejected",reason:M},Zt())}finally{C.p=i,u!==null&&s.types!==null&&(u.types=s.types),_.T=u}}function xh(){}function Cc(e,t,l,a){if(e.tag!==5)throw Error(r(476));var n=Yo(e).queue;Lo(e,n,t,J,l===null?xh:function(){return ko(e),l(a)})}function Yo(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:J,baseState:J,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:xl,lastRenderedState:J},next:null};var l={};return t.next={memoizedState:l,baseState:l,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:xl,lastRenderedState:l},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function ko(e){var t=Yo(e);t.next===null&&(t=e.alternate.memoizedState),Rn(e,t.next.queue,{},Zt())}function Oc(){return _t(Pn)}function Xo(){return at().memoizedState}function Qo(){return at().memoizedState}function _h(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var l=Zt();e=ql(l);var a=Ll(t,e,l);a!==null&&(Rt(a,t,l),Cn(a,t,l)),t={cache:cc()},e.payload=t;return}t=t.return}}function jh(e,t,l){var a=Zt();l={lane:a,revertLane:0,gesture:null,action:l,hasEagerState:!1,eagerState:null,next:null},Li(e)?Zo(t,l):(l=Wu(e,t,l,a),l!==null&&(Rt(l,e,a),Ko(l,t,a)))}function Vo(e,t,l){var a=Zt();Rn(e,t,l,a)}function Rn(e,t,l,a){var n={lane:a,revertLane:0,gesture:null,action:l,hasEagerState:!1,eagerState:null,next:null};if(Li(e))Zo(t,n);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var u=t.lastRenderedState,s=i(u,l);if(n.hasEagerState=!0,n.eagerState=s,Lt(s,u))return Si(e,t,n,0),qe===null&&bi(),!1}catch{}if(l=Wu(e,t,n,a),l!==null)return Rt(l,e,a),Ko(l,t,a),!0}return!1}function Uc(e,t,l,a){if(a={lane:2,revertLane:rs(),gesture:null,action:a,hasEagerState:!1,eagerState:null,next:null},Li(e)){if(t)throw Error(r(479))}else t=Wu(e,l,a,2),t!==null&&Rt(t,e,2)}function Li(e){var t=e.alternate;return e===ne||t!==null&&t===ne}function Zo(e,t){$a=Ui=!0;var l=e.pending;l===null?t.next=t:(t.next=l.next,l.next=t),e.pending=t}function Ko(e,t,l){if((l&4194048)!==0){var a=t.lanes;a&=e.pendingLanes,l|=a,t.lanes=l,Ws(e,l)}}var Hn={readContext:_t,use:Ri,useCallback:Pe,useContext:Pe,useEffect:Pe,useImperativeHandle:Pe,useLayoutEffect:Pe,useInsertionEffect:Pe,useMemo:Pe,useReducer:Pe,useRef:Pe,useState:Pe,useDebugValue:Pe,useDeferredValue:Pe,useTransition:Pe,useSyncExternalStore:Pe,useId:Pe,useHostTransitionStatus:Pe,useFormState:Pe,useActionState:Pe,useOptimistic:Pe,useMemoCache:Pe,useCacheRefresh:Pe};Hn.useEffectEvent=Pe;var Jo={readContext:_t,use:Ri,useCallback:function(e,t){return Nt().memoizedState=[e,t===void 0?null:t],e},useContext:_t,useEffect:Co,useImperativeHandle:function(e,t,l){l=l!=null?l.concat([e]):null,Gi(4194308,4,Bo.bind(null,t,e),l)},useLayoutEffect:function(e,t){return Gi(4194308,4,e,t)},useInsertionEffect:function(e,t){Gi(4,2,e,t)},useMemo:function(e,t){var l=Nt();t=t===void 0?null:t;var a=e();if(ba){qt(!0);try{e()}finally{qt(!1)}}return l.memoizedState=[a,t],a},useReducer:function(e,t,l){var a=Nt();if(l!==void 0){var n=l(t);if(ba){qt(!0);try{l(t)}finally{qt(!1)}}}else n=t;return a.memoizedState=a.baseState=n,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:n},a.queue=e,e=e.dispatch=jh.bind(null,ne,e),[a.memoizedState,e]},useRef:function(e){var t=Nt();return e={current:e},t.memoizedState=e},useState:function(e){e=Mc(e);var t=e.queue,l=Vo.bind(null,ne,t);return t.dispatch=l,[e.memoizedState,l]},useDebugValue:Ac,useDeferredValue:function(e,t){var l=Nt();return Dc(l,e,t)},useTransition:function(){var e=Mc(!1);return e=Lo.bind(null,ne,e.queue,!0,!1),Nt().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,l){var a=ne,n=Nt();if(be){if(l===void 0)throw Error(r(407));l=l()}else{if(l=t(),qe===null)throw Error(r(349));(ge&127)!==0||yo(a,t,l)}n.memoizedState=l;var i={value:l,getSnapshot:t};return n.queue=i,Co(po.bind(null,a,i,e),[e]),a.flags|=2048,Fa(9,{destroy:void 0},go.bind(null,a,i,l,t),null),l},useId:function(){var e=Nt(),t=qe.identifierPrefix;if(be){var l=fl,a=sl;l=(a&~(1<<32-rt(a)-1)).toString(32)+l,t="_"+t+"R_"+l,l=wi++,0<\/script>",i=i.removeChild(i.firstChild);break;case"select":i=typeof a.is=="string"?u.createElement("select",{is:a.is}):u.createElement("select"),a.multiple?i.multiple=!0:a.size&&(i.size=a.size);break;default:i=typeof a.is=="string"?u.createElement(n,{is:a.is}):u.createElement(n)}}i[St]=t,i[Dt]=a;e:for(u=t.child;u!==null;){if(u.tag===5||u.tag===6)i.appendChild(u.stateNode);else if(u.tag!==4&&u.tag!==27&&u.child!==null){u.child.return=u,u=u.child;continue}if(u===t)break e;for(;u.sibling===null;){if(u.return===null||u.return===t)break e;u=u.return}u.sibling.return=u.return,u=u.sibling}t.stateNode=i;e:switch(zt(i,n,a),n){case"button":case"input":case"select":case"textarea":a=!!a.autoFocus;break e;case"img":a=!0;break e;default:a=!1}a&&jl(t)}}return Ye(t),Kc(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,l),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==a&&jl(t);else{if(typeof a!="string"&&t.stateNode===null)throw Error(r(166));if(e=ee.current,ka(t)){if(e=t.stateNode,l=t.memoizedProps,a=null,n=xt,n!==null)switch(n.tag){case 27:case 5:a=n.memoizedProps}e[St]=t,e=!!(e.nodeValue===l||a!==null&&a.suppressHydrationWarning===!0||md(e.nodeValue,l)),e||Rl(t,!0)}else e=uu(e).createTextNode(a),e[St]=t,t.stateNode=e}return Ye(t),null;case 31:if(l=t.memoizedState,e===null||e.memoizedState!==null){if(a=ka(t),l!==null){if(e===null){if(!a)throw Error(r(318));if(e=t.memoizedState,e=e!==null?e.dehydrated:null,!e)throw Error(r(557));e[St]=t}else da(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;Ye(t),e=!1}else l=ac(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=l),e=!0;if(!e)return t.flags&256?(Xt(t),t):(Xt(t),null);if((t.flags&128)!==0)throw Error(r(558))}return Ye(t),null;case 13:if(a=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(n=ka(t),a!==null&&a.dehydrated!==null){if(e===null){if(!n)throw Error(r(318));if(n=t.memoizedState,n=n!==null?n.dehydrated:null,!n)throw Error(r(317));n[St]=t}else da(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;Ye(t),n=!1}else n=ac(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=n),n=!0;if(!n)return t.flags&256?(Xt(t),t):(Xt(t),null)}return Xt(t),(t.flags&128)!==0?(t.lanes=l,t):(l=a!==null,e=e!==null&&e.memoizedState!==null,l&&(a=t.child,n=null,a.alternate!==null&&a.alternate.memoizedState!==null&&a.alternate.memoizedState.cachePool!==null&&(n=a.alternate.memoizedState.cachePool.pool),i=null,a.memoizedState!==null&&a.memoizedState.cachePool!==null&&(i=a.memoizedState.cachePool.pool),i!==n&&(a.flags|=2048)),l!==e&&l&&(t.child.flags|=8192),Vi(t,t.updateQueue),Ye(t),null);case 4:return Ne(),e===null&&ys(t.stateNode.containerInfo),Ye(t),null;case 10:return bl(t.type),Ye(t),null;case 19:if(j(lt),a=t.memoizedState,a===null)return Ye(t),null;if(n=(t.flags&128)!==0,i=a.rendering,i===null)if(n)qn(a,!1);else{if(et!==0||e!==null&&(e.flags&128)!==0)for(e=t.child;e!==null;){if(i=Oi(e),i!==null){for(t.flags|=128,qn(a,!1),e=i.updateQueue,t.updateQueue=e,Vi(t,e),t.subtreeFlags=0,e=l,l=t.child;l!==null;)Qf(l,e),l=l.sibling;return B(lt,lt.current&1|2),be&&pl(t,a.treeForkCount),t.child}e=e.sibling}a.tail!==null&&Re()>Wi&&(t.flags|=128,n=!0,qn(a,!1),t.lanes=4194304)}else{if(!n)if(e=Oi(i),e!==null){if(t.flags|=128,n=!0,e=e.updateQueue,t.updateQueue=e,Vi(t,e),qn(a,!0),a.tail===null&&a.tailMode==="hidden"&&!i.alternate&&!be)return Ye(t),null}else 2*Re()-a.renderingStartTime>Wi&&l!==536870912&&(t.flags|=128,n=!0,qn(a,!1),t.lanes=4194304);a.isBackwards?(i.sibling=t.child,t.child=i):(e=a.last,e!==null?e.sibling=i:t.child=i,a.last=i)}return a.tail!==null?(e=a.tail,a.rendering=e,a.tail=e.sibling,a.renderingStartTime=Re(),e.sibling=null,l=lt.current,B(lt,n?l&1|2:l&1),be&&pl(t,a.treeForkCount),e):(Ye(t),null);case 22:case 23:return Xt(t),gc(),a=t.memoizedState!==null,e!==null?e.memoizedState!==null!==a&&(t.flags|=8192):a&&(t.flags|=8192),a?(l&536870912)!==0&&(t.flags&128)===0&&(Ye(t),t.subtreeFlags&6&&(t.flags|=8192)):Ye(t),l=t.updateQueue,l!==null&&Vi(t,l.retryQueue),l=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(l=e.memoizedState.cachePool.pool),a=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(a=t.memoizedState.cachePool.pool),a!==l&&(t.flags|=2048),e!==null&&j(ya),null;case 24:return l=null,e!==null&&(l=e.memoizedState.cache),t.memoizedState.cache!==l&&(t.flags|=2048),bl(nt),Ye(t),null;case 25:return null;case 30:return null}throw Error(r(156,t.tag))}function Nh(e,t){switch(tc(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return bl(nt),Ne(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return Et(t),null;case 31:if(t.memoizedState!==null){if(Xt(t),t.alternate===null)throw Error(r(340));da()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(Xt(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(r(340));da()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return j(lt),null;case 4:return Ne(),null;case 10:return bl(t.type),null;case 22:case 23:return Xt(t),gc(),e!==null&&j(ya),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return bl(nt),null;case 25:return null;default:return null}}function vr(e,t){switch(tc(t),t.tag){case 3:bl(nt),Ne();break;case 26:case 27:case 5:Et(t);break;case 4:Ne();break;case 31:t.memoizedState!==null&&Xt(t);break;case 13:Xt(t);break;case 19:j(lt);break;case 10:bl(t.type);break;case 22:case 23:Xt(t),gc(),e!==null&&j(ya);break;case 24:bl(nt)}}function Ln(e,t){try{var l=t.updateQueue,a=l!==null?l.lastEffect:null;if(a!==null){var n=a.next;l=n;do{if((l.tag&e)===e){a=void 0;var i=l.create,u=l.inst;a=i(),u.destroy=a}l=l.next}while(l!==n)}}catch(s){Ce(t,t.return,s)}}function Xl(e,t,l){try{var a=t.updateQueue,n=a!==null?a.lastEffect:null;if(n!==null){var i=n.next;a=i;do{if((a.tag&e)===e){var u=a.inst,s=u.destroy;if(s!==void 0){u.destroy=void 0,n=t;var o=l,p=s;try{p()}catch(z){Ce(n,o,z)}}}a=a.next}while(a!==i)}}catch(z){Ce(t,t.return,z)}}function br(e){var t=e.updateQueue;if(t!==null){var l=e.stateNode;try{so(t,l)}catch(a){Ce(e,e.return,a)}}}function Sr(e,t,l){l.props=Sa(e.type,e.memoizedProps),l.state=e.memoizedState;try{l.componentWillUnmount()}catch(a){Ce(e,t,a)}}function Yn(e,t){try{var l=e.ref;if(l!==null){switch(e.tag){case 26:case 27:case 5:var a=e.stateNode;break;case 30:a=e.stateNode;break;default:a=e.stateNode}typeof l=="function"?e.refCleanup=l(a):l.current=a}}catch(n){Ce(e,t,n)}}function ol(e,t){var l=e.ref,a=e.refCleanup;if(l!==null)if(typeof a=="function")try{a()}catch(n){Ce(e,t,n)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof l=="function")try{l(null)}catch(n){Ce(e,t,n)}else l.current=null}function xr(e){var t=e.type,l=e.memoizedProps,a=e.stateNode;try{e:switch(t){case"button":case"input":case"select":case"textarea":l.autoFocus&&a.focus();break e;case"img":l.src?a.src=l.src:l.srcSet&&(a.srcset=l.srcSet)}}catch(n){Ce(e,e.return,n)}}function Jc(e,t,l){try{var a=e.stateNode;Wh(a,e.type,l,t),a[Dt]=t}catch(n){Ce(e,e.return,n)}}function _r(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&Wl(e.type)||e.tag===4}function $c(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||_r(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&Wl(e.type)||e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Wc(e,t,l){var a=e.tag;if(a===5||a===6)e=e.stateNode,t?(l.nodeType===9?l.body:l.nodeName==="HTML"?l.ownerDocument.body:l).insertBefore(e,t):(t=l.nodeType===9?l.body:l.nodeName==="HTML"?l.ownerDocument.body:l,t.appendChild(e),l=l._reactRootContainer,l!=null||t.onclick!==null||(t.onclick=hl));else if(a!==4&&(a===27&&Wl(e.type)&&(l=e.stateNode,t=null),e=e.child,e!==null))for(Wc(e,t,l),e=e.sibling;e!==null;)Wc(e,t,l),e=e.sibling}function Zi(e,t,l){var a=e.tag;if(a===5||a===6)e=e.stateNode,t?l.insertBefore(e,t):l.appendChild(e);else if(a!==4&&(a===27&&Wl(e.type)&&(l=e.stateNode),e=e.child,e!==null))for(Zi(e,t,l),e=e.sibling;e!==null;)Zi(e,t,l),e=e.sibling}function jr(e){var t=e.stateNode,l=e.memoizedProps;try{for(var a=e.type,n=t.attributes;n.length;)t.removeAttributeNode(n[0]);zt(t,a,l),t[St]=e,t[Dt]=l}catch(i){Ce(e,e.return,i)}}var zl=!1,ct=!1,Fc=!1,zr=typeof WeakSet=="function"?WeakSet:Set,ht=null;function Ah(e,t){if(e=e.containerInfo,vs=mu,e=Bf(e),Qu(e)){if("selectionStart"in e)var l={start:e.selectionStart,end:e.selectionEnd};else e:{l=(l=e.ownerDocument)&&l.defaultView||window;var a=l.getSelection&&l.getSelection();if(a&&a.rangeCount!==0){l=a.anchorNode;var n=a.anchorOffset,i=a.focusNode;a=a.focusOffset;try{l.nodeType,i.nodeType}catch{l=null;break e}var u=0,s=-1,o=-1,p=0,z=0,M=e,v=null;t:for(;;){for(var S;M!==l||n!==0&&M.nodeType!==3||(s=u+n),M!==i||a!==0&&M.nodeType!==3||(o=u+a),M.nodeType===3&&(u+=M.nodeValue.length),(S=M.firstChild)!==null;)v=M,M=S;for(;;){if(M===e)break t;if(v===l&&++p===n&&(s=u),v===i&&++z===a&&(o=u),(S=M.nextSibling)!==null)break;M=v,v=M.parentNode}M=S}l=s===-1||o===-1?null:{start:s,end:o}}else l=null}l=l||{start:0,end:0}}else l=null;for(bs={focusedElem:e,selectionRange:l},mu=!1,ht=t;ht!==null;)if(t=ht,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,ht=e;else for(;ht!==null;){switch(t=ht,i=t.alternate,e=t.flags,t.tag){case 0:if((e&4)!==0&&(e=t.updateQueue,e=e!==null?e.events:null,e!==null))for(l=0;l title"))),zt(i,a,l),i[St]=e,mt(i),a=i;break e;case"link":var u=Dd("link","href",n).get(a+(l.href||""));if(u){for(var s=0;sBe&&(u=Be,Be=I,I=u);var y=Uf(s,I),m=Uf(s,Be);if(y&&m&&(S.rangeCount!==1||S.anchorNode!==y.node||S.anchorOffset!==y.offset||S.focusNode!==m.node||S.focusOffset!==m.offset)){var g=M.createRange();g.setStart(y.node,y.offset),S.removeAllRanges(),I>Be?(S.addRange(g),S.extend(m.node,m.offset)):(g.setEnd(m.node,m.offset),S.addRange(g))}}}}for(M=[],S=s;S=S.parentNode;)S.nodeType===1&&M.push({element:S,left:S.scrollLeft,top:S.scrollTop});for(typeof s.focus=="function"&&s.focus(),s=0;sl?32:l,_.T=null,l=ns,ns=null;var i=Kl,u=Al;if(ot=0,ln=Kl=null,Al=0,(Me&6)!==0)throw Error(r(331));var s=Me;if(Me|=4,Br(i.current),Or(i,i.current,u,l),Me=s,Kn(0,!1),tt&&typeof tt.onPostCommitFiberRoot=="function")try{tt.onPostCommitFiberRoot(ft,i)}catch{}return!0}finally{C.p=n,_.T=a,Pr(e,t)}}function td(e,t,l){t=Wt(l,t),t=Hc(e.stateNode,t,2),e=Ll(e,t,2),e!==null&&(mn(e,2),rl(e))}function Ce(e,t,l){if(e.tag===3)td(e,e,l);else for(;t!==null;){if(t.tag===3){td(t,e,l);break}else if(t.tag===1){var a=t.stateNode;if(typeof t.type.getDerivedStateFromError=="function"||typeof a.componentDidCatch=="function"&&(Zl===null||!Zl.has(a))){e=Wt(l,e),l=lr(2),a=Ll(t,l,2),a!==null&&(ar(l,a,t,e),mn(a,2),rl(a));break}}t=t.return}}function ss(e,t,l){var a=e.pingCache;if(a===null){a=e.pingCache=new Oh;var n=new Set;a.set(t,n)}else n=a.get(t),n===void 0&&(n=new Set,a.set(t,n));n.has(l)||(es=!0,n.add(l),e=Hh.bind(null,e,t,l),t.then(e,e))}function Hh(e,t,l){var a=e.pingCache;a!==null&&a.delete(t),e.pingedLanes|=e.suspendedLanes&l,e.warmLanes&=~l,qe===e&&(ge&l)===l&&(et===4||et===3&&(ge&62914560)===ge&&300>Re()-$i?(Me&2)===0&&an(e,0):ts|=l,tn===ge&&(tn=0)),rl(e)}function ld(e,t){t===0&&(t=ci()),e=oa(e,t),e!==null&&(mn(e,t),rl(e))}function Gh(e){var t=e.memoizedState,l=0;t!==null&&(l=t.retryLane),ld(e,l)}function qh(e,t){var l=0;switch(e.tag){case 31:case 13:var a=e.stateNode,n=e.memoizedState;n!==null&&(l=n.retryLane);break;case 19:a=e.stateNode;break;case 22:a=e.stateNode._retryCache;break;default:throw Error(r(314))}a!==null&&a.delete(t),ld(e,l)}function Lh(e,t){return ae(e,t)}var lu=null,un=null,fs=!1,au=!1,os=!1,$l=0;function rl(e){e!==un&&e.next===null&&(un===null?lu=un=e:un=un.next=e),au=!0,fs||(fs=!0,kh())}function Kn(e,t){if(!os&&au){os=!0;do for(var l=!1,a=lu;a!==null;){if(e!==0){var n=a.pendingLanes;if(n===0)var i=0;else{var u=a.suspendedLanes,s=a.pingedLanes;i=(1<<31-rt(42|e)+1)-1,i&=n&~(u&~s),i=i&201326741?i&201326741|1:i?i|2:0}i!==0&&(l=!0,ud(a,i))}else i=ge,i=dt(a,a===qe?i:0,a.cancelPendingCommit!==null||a.timeoutHandle!==-1),(i&3)===0||bt(a,i)||(l=!0,ud(a,i));a=a.next}while(l);os=!1}}function Yh(){ad()}function ad(){au=fs=!1;var e=0;$l!==0&&Ih()&&(e=$l);for(var t=Re(),l=null,a=lu;a!==null;){var n=a.next,i=nd(a,t);i===0?(a.next=null,l===null?lu=n:l.next=n,n===null&&(un=l)):(l=a,(e!==0||(i&3)!==0)&&(au=!0)),a=n}ot!==0&&ot!==5||Kn(e),$l!==0&&($l=0)}function nd(e,t){for(var l=e.suspendedLanes,a=e.pingedLanes,n=e.expirationTimes,i=e.pendingLanes&-62914561;0s)break;var z=o.transferSize,M=o.initiatorType;z&&hd(M)&&(o=o.responseEnd,u+=z*(o"u"?null:document;function Md(e,t,l){var a=cn;if(a&&typeof t=="string"&&t){var n=Jt(t);n='link[rel="'+e+'"][href="'+n+'"]',typeof l=="string"&&(n+='[crossorigin="'+l+'"]'),Td.has(n)||(Td.add(n),e={rel:e,crossOrigin:l,href:t},a.querySelector(n)===null&&(t=a.createElement("link"),zt(t,"link",e),mt(t),a.head.appendChild(t)))}}function c0(e){Dl.D(e),Md("dns-prefetch",e,null)}function s0(e,t){Dl.C(e,t),Md("preconnect",e,t)}function f0(e,t,l){Dl.L(e,t,l);var a=cn;if(a&&e&&t){var n='link[rel="preload"][as="'+Jt(t)+'"]';t==="image"&&l&&l.imageSrcSet?(n+='[imagesrcset="'+Jt(l.imageSrcSet)+'"]',typeof l.imageSizes=="string"&&(n+='[imagesizes="'+Jt(l.imageSizes)+'"]')):n+='[href="'+Jt(e)+'"]';var i=n;switch(t){case"style":i=sn(e);break;case"script":i=fn(e)}ll.has(i)||(e=U({rel:"preload",href:t==="image"&&l&&l.imageSrcSet?void 0:e,as:t},l),ll.set(i,e),a.querySelector(n)!==null||t==="style"&&a.querySelector(Fn(i))||t==="script"&&a.querySelector(In(i))||(t=a.createElement("link"),zt(t,"link",e),mt(t),a.head.appendChild(t)))}}function o0(e,t){Dl.m(e,t);var l=cn;if(l&&e){var a=t&&typeof t.as=="string"?t.as:"script",n='link[rel="modulepreload"][as="'+Jt(a)+'"][href="'+Jt(e)+'"]',i=n;switch(a){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":i=fn(e)}if(!ll.has(i)&&(e=U({rel:"modulepreload",href:e},t),ll.set(i,e),l.querySelector(n)===null)){switch(a){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(l.querySelector(In(i)))return}a=l.createElement("link"),zt(a,"link",e),mt(a),l.head.appendChild(a)}}}function r0(e,t,l){Dl.S(e,t,l);var a=cn;if(a&&e){var n=Aa(a).hoistableStyles,i=sn(e);t=t||"default";var u=n.get(i);if(!u){var s={loading:0,preload:null};if(u=a.querySelector(Fn(i)))s.loading=5;else{e=U({rel:"stylesheet",href:e,"data-precedence":t},l),(l=ll.get(i))&&Ms(e,l);var o=u=a.createElement("link");mt(o),zt(o,"link",e),o._p=new Promise(function(p,z){o.onload=p,o.onerror=z}),o.addEventListener("load",function(){s.loading|=1}),o.addEventListener("error",function(){s.loading|=2}),s.loading|=4,su(u,t,a)}u={type:"stylesheet",instance:u,count:1,state:s},n.set(i,u)}}}function d0(e,t){Dl.X(e,t);var l=cn;if(l&&e){var a=Aa(l).hoistableScripts,n=fn(e),i=a.get(n);i||(i=l.querySelector(In(n)),i||(e=U({src:e,async:!0},t),(t=ll.get(n))&&Es(e,t),i=l.createElement("script"),mt(i),zt(i,"link",e),l.head.appendChild(i)),i={type:"script",instance:i,count:1,state:null},a.set(n,i))}}function m0(e,t){Dl.M(e,t);var l=cn;if(l&&e){var a=Aa(l).hoistableScripts,n=fn(e),i=a.get(n);i||(i=l.querySelector(In(n)),i||(e=U({src:e,async:!0,type:"module"},t),(t=ll.get(n))&&Es(e,t),i=l.createElement("script"),mt(i),zt(i,"link",e),l.head.appendChild(i)),i={type:"script",instance:i,count:1,state:null},a.set(n,i))}}function Ed(e,t,l,a){var n=(n=ee.current)?cu(n):null;if(!n)throw Error(r(446));switch(e){case"meta":case"title":return null;case"style":return typeof l.precedence=="string"&&typeof l.href=="string"?(t=sn(l.href),l=Aa(n).hoistableStyles,a=l.get(t),a||(a={type:"style",instance:null,count:0,state:null},l.set(t,a)),a):{type:"void",instance:null,count:0,state:null};case"link":if(l.rel==="stylesheet"&&typeof l.href=="string"&&typeof l.precedence=="string"){e=sn(l.href);var i=Aa(n).hoistableStyles,u=i.get(e);if(u||(n=n.ownerDocument||n,u={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},i.set(e,u),(i=n.querySelector(Fn(e)))&&!i._p&&(u.instance=i,u.state.loading=5),ll.has(e)||(l={rel:"preload",as:"style",href:l.href,crossOrigin:l.crossOrigin,integrity:l.integrity,media:l.media,hrefLang:l.hrefLang,referrerPolicy:l.referrerPolicy},ll.set(e,l),i||h0(n,e,l,u.state))),t&&a===null)throw Error(r(528,""));return u}if(t&&a!==null)throw Error(r(529,""));return null;case"script":return t=l.async,l=l.src,typeof l=="string"&&t&&typeof t!="function"&&typeof t!="symbol"?(t=fn(l),l=Aa(n).hoistableScripts,a=l.get(t),a||(a={type:"script",instance:null,count:0,state:null},l.set(t,a)),a):{type:"void",instance:null,count:0,state:null};default:throw Error(r(444,e))}}function sn(e){return'href="'+Jt(e)+'"'}function Fn(e){return'link[rel="stylesheet"]['+e+"]"}function Nd(e){return U({},e,{"data-precedence":e.precedence,precedence:null})}function h0(e,t,l,a){e.querySelector('link[rel="preload"][as="style"]['+t+"]")?a.loading=1:(t=e.createElement("link"),a.preload=t,t.addEventListener("load",function(){return a.loading|=1}),t.addEventListener("error",function(){return a.loading|=2}),zt(t,"link",l),mt(t),e.head.appendChild(t))}function fn(e){return'[src="'+Jt(e)+'"]'}function In(e){return"script[async]"+e}function Ad(e,t,l){if(t.count++,t.instance===null)switch(t.type){case"style":var a=e.querySelector('style[data-href~="'+Jt(l.href)+'"]');if(a)return t.instance=a,mt(a),a;var n=U({},l,{"data-href":l.href,"data-precedence":l.precedence,href:null,precedence:null});return a=(e.ownerDocument||e).createElement("style"),mt(a),zt(a,"style",n),su(a,l.precedence,e),t.instance=a;case"stylesheet":n=sn(l.href);var i=e.querySelector(Fn(n));if(i)return t.state.loading|=4,t.instance=i,mt(i),i;a=Nd(l),(n=ll.get(n))&&Ms(a,n),i=(e.ownerDocument||e).createElement("link"),mt(i);var u=i;return u._p=new Promise(function(s,o){u.onload=s,u.onerror=o}),zt(i,"link",a),t.state.loading|=4,su(i,l.precedence,e),t.instance=i;case"script":return i=fn(l.src),(n=e.querySelector(In(i)))?(t.instance=n,mt(n),n):(a=l,(n=ll.get(i))&&(a=U({},l),Es(a,n)),e=e.ownerDocument||e,n=e.createElement("script"),mt(n),zt(n,"link",a),e.head.appendChild(n),t.instance=n);case"void":return null;default:throw Error(r(443,t.type))}else t.type==="stylesheet"&&(t.state.loading&4)===0&&(a=t.instance,t.state.loading|=4,su(a,l.precedence,e));return t.instance}function su(e,t,l){for(var a=l.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),n=a.length?a[a.length-1]:null,i=n,u=0;u title"):null)}function y0(e,t,l){if(l===1||t.itemProp!=null)return!1;switch(e){case"meta":case"title":return!0;case"style":if(typeof t.precedence!="string"||typeof t.href!="string"||t.href==="")break;return!0;case"link":if(typeof t.rel!="string"||typeof t.href!="string"||t.href===""||t.onLoad||t.onError)break;return t.rel==="stylesheet"?(e=t.disabled,typeof t.precedence=="string"&&e==null):!0;case"script":if(t.async&&typeof t.async!="function"&&typeof t.async!="symbol"&&!t.onLoad&&!t.onError&&t.src&&typeof t.src=="string")return!0}return!1}function Od(e){return!(e.type==="stylesheet"&&(e.state.loading&3)===0)}function g0(e,t,l,a){if(l.type==="stylesheet"&&(typeof a.media!="string"||matchMedia(a.media).matches!==!1)&&(l.state.loading&4)===0){if(l.instance===null){var n=sn(a.href),i=t.querySelector(Fn(n));if(i){t=i._p,t!==null&&typeof t=="object"&&typeof t.then=="function"&&(e.count++,e=ou.bind(e),t.then(e,e)),l.state.loading|=4,l.instance=i,mt(i);return}i=t.ownerDocument||t,a=Nd(a),(n=ll.get(n))&&Ms(a,n),i=i.createElement("link"),mt(i);var u=i;u._p=new Promise(function(s,o){u.onload=s,u.onerror=o}),zt(i,"link",a),l.instance=i}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(l,t),(t=l.state.preload)&&(l.state.loading&3)===0&&(e.count++,l=ou.bind(e),t.addEventListener("load",l),t.addEventListener("error",l))}}var Ns=0;function p0(e,t){return e.stylesheets&&e.count===0&&du(e,e.stylesheets),0Ns?50:800)+t);return e.unsuspend=l,function(){e.unsuspend=null,clearTimeout(a),clearTimeout(n)}}:null}function ou(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)du(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var ru=null;function du(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,ru=new Map,t.forEach(v0,e),ru=null,ou.call(e))}function v0(e,t){if(!(t.state.loading&4)){var l=ru.get(e);if(l)var a=l.get(null);else{l=new Map,ru.set(e,l);for(var n=e.querySelectorAll("link[data-precedence],style[data-precedence]"),i=0;i"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(f)}catch(b){console.error(b)}}return f(),Hs.exports=B0(),Hs.exports}var H0=R0();const G0=fm(H0);const om=(...f)=>f.filter((b,E,r)=>!!b&&b.trim()!==""&&r.indexOf(b)===E).join(" ").trim();const q0=f=>f.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase();const L0=f=>f.replace(/^([A-Z])|[\s-_]+(\w)/g,(b,E,r)=>r?r.toUpperCase():E.toLowerCase());const lm=f=>{const b=L0(f);return b.charAt(0).toUpperCase()+b.slice(1)};var Ys={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};const Y0=f=>{for(const b in f)if(b.startsWith("aria-")||b==="role"||b==="title")return!0;return!1},k0=O.createContext({}),X0=()=>O.useContext(k0),Q0=O.forwardRef(({color:f,size:b,strokeWidth:E,absoluteStrokeWidth:r,className:x="",children:N,iconNode:G,...H},w)=>{const{size:h=24,strokeWidth:A=2,absoluteStrokeWidth:U=!1,color:V="currentColor",className:fe=""}=X0()??{},X=r??U?Number(E??A)*24/Number(b??h):E??A;return O.createElement("svg",{ref:w,...Ys,width:b??h??Ys.width,height:b??h??Ys.height,stroke:f??V,strokeWidth:X,className:om("lucide",fe,x),...!N&&!Y0(H)&&{"aria-hidden":"true"},...H},[...G.map(([L,te])=>O.createElement(L,te)),...Array.isArray(N)?N:[N]])});const Ve=(f,b)=>{const E=O.forwardRef(({className:r,...x},N)=>O.createElement(Q0,{ref:N,iconNode:b,className:om(`lucide-${q0(lm(f))}`,`lucide-${f}`,r),...x}));return E.displayName=lm(f),E};const V0=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],ks=Ve("check",V0);const Z0=[["path",{d:"M12 13v8l-4-4",key:"1f5nwf"}],["path",{d:"m12 21 4-4",key:"1lfcce"}],["path",{d:"M4.393 15.269A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.436 8.284",key:"ui1hmy"}]],am=Ve("cloud-download",Z0);const K0=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]],J0=Ve("copy",K0);const $0=[["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M17 20v2",key:"1rnc9c"}],["path",{d:"M17 2v2",key:"11trls"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M2 17h2",key:"7oei6x"}],["path",{d:"M2 7h2",key:"asdhe0"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"M20 17h2",key:"1fpfkl"}],["path",{d:"M20 7h2",key:"1o8tra"}],["path",{d:"M7 20v2",key:"4gnj0m"}],["path",{d:"M7 2v2",key:"1i4yhu"}],["rect",{x:"4",y:"4",width:"16",height:"16",rx:"2",key:"1vbyd7"}],["rect",{x:"8",y:"8",width:"8",height:"8",rx:"1",key:"z9xiuo"}]],rm=Ve("cpu",$0);const W0=[["path",{d:"M6 2v14a2 2 0 0 0 2 2h14",key:"ron5a4"}],["path",{d:"M18 22V8a2 2 0 0 0-2-2H2",key:"7s9ehn"}]],F0=Ve("crop",W0);const I0=[["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3",key:"msslwz"}],["path",{d:"M3 5V19A9 3 0 0 0 21 19V5",key:"1wlel7"}],["path",{d:"M3 12A9 3 0 0 0 21 12",key:"mv7ke4"}]],Xs=Ve("database",I0);const P0=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],nm=Ve("download",P0);const ey=[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",key:"1kt360"}],["path",{d:"M12 10v6",key:"1bos4e"}],["path",{d:"m15 13-3 3-3-3",key:"6j2sf0"}]],ty=Ve("folder-down",ey);const ly=[["path",{d:"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2",key:"usdka0"}]],im=Ve("folder-open",ly);const ay=[["path",{d:"M10 16h.01",key:"1bzywj"}],["path",{d:"M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z",key:"18tbho"}],["path",{d:"M21.946 12.013H2.054",key:"zqlbp7"}],["path",{d:"M6 16h.01",key:"1pmjb7"}]],xu=Ve("hard-drive",ay);const ny=[["path",{d:"M16 5h6",key:"1vod17"}],["path",{d:"M19 2v6",key:"4bpg5p"}],["path",{d:"M21 11.5V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7.5",key:"1ue2ih"}],["path",{d:"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21",key:"1xmnt7"}],["circle",{cx:"9",cy:"9",r:"2",key:"af1f0g"}]],iy=Ve("image-plus",ny);const uy=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2",key:"1m3agn"}],["circle",{cx:"9",cy:"9",r:"2",key:"af1f0g"}],["path",{d:"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21",key:"1xmnt7"}]],cy=Ve("image",uy);const sy=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]],fy=Ve("info",sy);const oy=[["path",{d:"m16 6 4 14",key:"ji33uf"}],["path",{d:"M12 6v14",key:"1n7gus"}],["path",{d:"M8 8v12",key:"1gg7y9"}],["path",{d:"M4 4v16",key:"6qkkli"}]],ry=Ve("library",oy);const dy=[["path",{d:"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401",key:"kfwtm"}]],my=Ve("moon",dy);const hy=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]],aa=Ve("refresh-cw",hy);const yy=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]],gy=Ve("shield",yy);const py=[["path",{d:"M10 8h4",key:"1sr2af"}],["path",{d:"M12 21v-9",key:"17s77i"}],["path",{d:"M12 8V3",key:"13r4qs"}],["path",{d:"M17 16h4",key:"h1uq16"}],["path",{d:"M19 12V3",key:"o1uvq1"}],["path",{d:"M19 21v-5",key:"qua636"}],["path",{d:"M3 14h4",key:"bcjad9"}],["path",{d:"M5 10V3",key:"cb8scm"}],["path",{d:"M5 21v-7",key:"1w1uti"}]],Ks=Ve("sliders-vertical",py);const vy=[["path",{d:"M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z",key:"1s2grr"}],["path",{d:"M20 2v4",key:"1rf3ol"}],["path",{d:"M22 4h-4",key:"gwowj6"}],["circle",{cx:"4",cy:"20",r:"2",key:"6kqj1y"}]],Su=Ve("sparkles",vy);const by=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}]],Qs=Ve("square",by);const Sy=[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]],xy=Ve("sun",Sy);const _y=[["path",{d:"M12 19h8",key:"baeox8"}],["path",{d:"m4 17 6-6-6-6",key:"1yngyt"}]],jy=Ve("terminal",_y);const zy=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],rn=Ve("trash-2",zy);const Ty=[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]],My=Ve("triangle-alert",Ty);function Ey({activeTab:f,setActiveTab:b,specs:E}){return c.jsxs("div",{className:"sidebar",children:[c.jsxs("div",{children:[c.jsxs("div",{className:"sidebar-logo",children:[c.jsx(gy,{className:"sidebar-logo-icon"}),c.jsx("span",{className:"sidebar-logo-text",children:"Local AI Studio"})]}),c.jsxs("div",{className:"nav-list",children:[c.jsxs("div",{className:`nav-item ${f==="generator"?"active":""}`,onClick:()=>b("generator"),children:[c.jsx(cy,{size:20}),c.jsx("span",{children:"Image Generator"})]}),c.jsxs("div",{className:`nav-item ${f==="models"?"active":""}`,onClick:()=>b("models"),children:[c.jsx(ty,{size:20}),c.jsx("span",{children:"Model Manager"})]}),c.jsxs("div",{className:`nav-item ${f==="constraints"?"active":""}`,onClick:()=>b("constraints"),children:[c.jsx(Ks,{size:20}),c.jsx("span",{children:"Image Constraints"})]})]})]}),c.jsxs("div",{className:"sidebar-footer",children:[c.jsxs("div",{className:"sidebar-specs-header",children:[c.jsx(jy,{size:12}),c.jsx("span",{children:"Host Specifications"})]}),c.jsxs("div",{className:"sidebar-specs-item",title:E.cpu_name,children:["CPU: ",E.cpu_name]}),c.jsxs("div",{className:"sidebar-specs-item",title:E.gpu_name,children:["GPU: ",E.gpu_name]}),c.jsxs("div",{className:"sidebar-specs-item",children:["Memory: ",E.ram_total_gb.toFixed(0)," GB RAM (",E.cpu_cores_physical," Cores)"]}),c.jsxs("div",{className:"sidebar-specs-os",children:["OS: ",E.os_name]})]})]})}const Ny=O.memo(Ey);function Ay({telemetry:f,serverRunning:b,activeModel:E,onStopServer:r,isStoppingServer:x=!1,theme:N,setTheme:G}){const H=A=>{const U=Number(A);return Number.isFinite(U)&&U>0?U.toFixed(U>=10?0:1):"--"},w=()=>b?"Server Active":"Local Mode",h=()=>b?"status-indicator busy":"status-indicator offline";return c.jsxs("div",{className:"top-status-bar",children:[c.jsxs("div",{className:"current-model-info",children:[c.jsx("div",{className:h()}),c.jsx("span",{style:{fontWeight:600,fontSize:"0.95rem"},children:w()}),E&&c.jsxs(c.Fragment,{children:[c.jsx("span",{style:{color:"var(--md-sys-color-outline-variant)"},children:"|"}),c.jsx("span",{style:{color:"var(--md-sys-color-primary)",fontWeight:700},children:E})]})]}),c.jsxs("div",{className:"telemetry-group",children:[c.jsx("button",{className:"theme-toggle-btn",onClick:()=>G(N==="dark"?"light":"dark"),title:`Switch to ${N==="dark"?"light":"dark"} theme`,children:N==="dark"?c.jsx(xy,{size:18}):c.jsx(my,{size:18})}),b&&c.jsxs("button",{className:"m3-btn m3-btn-error",style:{height:"34px",padding:"0 14px"},onClick:r,disabled:x,title:"Stop local model server",children:[x?c.jsx(aa,{className:"progress-spinner",size:14}):c.jsx(Qs,{size:14}),c.jsx("span",{children:x?"Stopping":"Stop Server"})]}),c.jsxs("div",{className:"telemetry-chip",title:"CPU Utilization",children:[c.jsx(rm,{className:"telemetry-chip-icon",style:{color:"var(--md-sys-color-primary)"}}),c.jsxs("span",{children:["CPU: ",Number.isFinite(Number(f.cpu_usage))?f.cpu_usage:"--","%"]})]}),c.jsxs("div",{className:"telemetry-chip",title:"System Memory Usage",children:[c.jsx(xu,{className:"telemetry-chip-icon",style:{color:"var(--md-sys-color-secondary)"}}),c.jsxs("span",{children:["RAM: ",H(f.ram_used_gb)," / ",H(f.ram_total_gb)," GB"]})]}),f.vram_total_gb>0&&c.jsxs("div",{className:"telemetry-chip",title:`${f.gpu_name} VRAM`,children:[c.jsx(Xs,{className:"telemetry-chip-icon",style:{color:"var(--md-sys-color-tertiary)"}}),c.jsxs("span",{children:["VRAM: ",H(f.vram_used_gb)," / ",H(f.vram_total_gb)," GB"]})]})]})]})}const Dy=O.memo(Ay),Cy="modulepreload",Oy=function(f){return"/"+f},um={},Uy=function(b,E,r){let x=Promise.resolve();if(E&&E.length>0){let w=function(h){return Promise.all(h.map(A=>Promise.resolve(A).then(U=>({status:"fulfilled",value:U}),U=>({status:"rejected",reason:U}))))};document.getElementsByTagName("link");const G=document.querySelector("meta[property=csp-nonce]"),H=G?.nonce||G?.getAttribute("nonce");x=w(E.map(h=>{if(h=Oy(h),h in um)return;um[h]=!0;const A=h.endsWith(".css"),U=A?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${h}"]${U}`))return;const V=document.createElement("link");if(V.rel=A?"stylesheet":Cy,A||(V.as="script"),V.crossOrigin="",V.href=h,H&&V.setAttribute("nonce",H),document.head.appendChild(V),A)return new Promise((fe,X)=>{V.addEventListener("load",fe),V.addEventListener("error",()=>X(new Error(`Unable to preload CSS for ${h}`)))})}))}function N(G){const H=new Event("vite:preloadError",{cancelable:!0});if(H.payload=G,window.dispatchEvent(H),!H.defaultPrevented)throw G}return x.then(G=>{for(const H of G||[])H.status==="rejected"&&N(H.reason);return b().catch(N)})};function rg(f,b=!1){return window.__TAURI_INTERNALS__.transformCallback(f,b)}async function ja(f,b={},E){return window.__TAURI_INTERNALS__.invoke(f,b,E)}const ia=()=>typeof window<"u"&&window.__TAURI_INTERNALS__!==void 0;let la=null,_u=null;const wy="polish-setup-v1",na=()=>typeof window<"u"&&(window.location.hostname==="localhost"||window.location.hostname==="127.0.0.1");async function dm(){if(na())try{const f=await Cl(),b=Number(f?.port);if(Number.isInteger(b)&&b>0)return _u=b,b}catch{}return _u||8080}async function mm(){return`http://127.0.0.1:${await dm()}`}function ii(f){const b=Number(f)||0;if(b<=0)return"0 B";const E=["B","KB","MB","GB","TB"],r=Math.min(E.length-1,Math.floor(Math.log(b)/Math.log(1024)));return`${(b/1024**r).toFixed(r===0?0:1)} ${E[r]}`}function ui(f){return typeof f=="string"?{filename:f,sizeBytes:0,size:"Unknown"}:{filename:f?.filename||f?.name||"",sizeBytes:Number(f?.sizeBytes||0),size:f?.size||(f?.sizeBytes?ii(f.sizeBytes):"Unknown")}}async function dn(f,b="The local server returned an invalid response."){const E=await f.text();let r={};try{r=JSON.parse(E||"{}")}catch{const N=E.trim().startsWith("0&&(_u=Number(G.port)),console.log("Backend restart:",G.message),G.message}catch(x){if(console.warn("Could not reach management API:",x.message),na())throw x;return"Backend management unavailable"}}async function Js(){if(ia())return await ja("stop_server");try{await fetch("/api/stop-backend",{method:"POST"})}catch{}return"Backend stopped"}async function Cl(){try{return await(await fetch("/api/backend-status")).json()}catch{return{ready:!1,settings:{}}}}async function Xy(){try{return(await(await fetch("/api/outputs")).json()).outputs||[]}catch{return[]}}async function Qy(f,b){const E=await fetch("/api/save-output",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:f,metadata:b})});return(await dn(E,"The local server returned an invalid save response.")).output}async function Vy(f){const b=await fetch("/api/delete-outputs",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({outputs:f})});return await dn(b,"The local server returned an invalid delete response.")}async function Zy(){try{return((await(await fetch("/api/models")).json()).models||[]).map(ui)}catch{return[]}}async function Ky(f,b,E,r,x,N,G){console.log("Initiating image generation:",{prompt:f,negativePrompt:b,constraints:E,activeModelName:r});const H=Date.now(),w=await mm(),h={prompt:f,negative_prompt:b||"",width:E.width||512,height:E.height||512,steps:E.steps||20,cfg_scale:E.cfgScale||7,seed:E.seed===-1?Math.floor(Math.random()*1e6):E.seed,sampler:E.sampler||"euler_a",image:x||null,denoising_strength:E.denoisingStrength||.7},A=async()=>{const X=h.steps;for(let Ke=1;Ke<=X;Ke++){if(G&&G.aborted)throw new DOMException("The user aborted a request.","AbortError");await new Promise(yt=>setTimeout(yt,3e3/X)),N&&N(Math.round(Ke/X*100))}const L=f.toLowerCase(),te=Math.abs(h.seed)||Math.floor(Math.random()*1e3);let W="#3B82F6",Oe="#8B5CF6",de="",Ee="Abstract Generation";L.includes("moon")||L.includes("space")||L.includes("astronomy")||L.includes("sky")?(W="#1E293B",Oe="#0F172A",Ee="Luminous Moon in Starry Sky",de=` - - - - - - - - - - - - `):L.includes("cat")||L.includes("kitten")||L.includes("feline")?(W="#4F46E5",Oe="#7C3AED",Ee="Stylized Cat Portrait",de=` - - - - - - - - - - - - - - - - `):L.includes("dog")||L.includes("puppy")||L.includes("canine")?(W="#D97706",Oe="#B45309",Ee="Friendly Dog Portrait",de=` - - - - - - - - - - - - - `):L.includes("city")||L.includes("cyberpunk")||L.includes("tokyo")||L.includes("neon")?(W="#111827",Oe="#311042",Ee="Neon Cyberpunk Skyline",de=` - - - - - - - - - - - - - `):L.includes("car")||L.includes("vehicle")||L.includes("supercar")?(W="#DC2626",Oe="#991B1B",Ee="Futuristic Concept Car",de=` - - - - - - - - - - - - - `):(W="#06B6D4",Oe="#0891B2",Ee="Abstract Creative Concept",de=` - - - - `);const ke=` - - - - - - - - - - - - - - - - - ${de} - - - - - - - ${Ee} - Seed: ${te} β€’ Steps: ${h.steps} β€’ Scale: ${h.cfg_scale} - - `,P=`data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(ke)))}`,ve=parseFloat(((Date.now()-H)/1e3).toFixed(1));return{image:P,seed:h.seed,duration_sec:ve}},U=!!h.image;let V=`${w}/v1/images/generations`,fe={prompt:h.prompt,negative_prompt:h.negative_prompt||"",n:1,size:`${h.width}x${h.height}`,response_format:"b64_json",steps:h.steps,cfg_scale:h.cfg_scale,seed:h.seed,sample_method:h.sampler};if(U){const X=String(h.image).replace(/^data:[^;]+;base64,/,"");V=`${w}/sdapi/v1/img2img`,fe={init_images:[X],prompt:h.prompt,negative_prompt:h.negative_prompt||"",denoising_strength:h.denoising_strength,steps:h.steps,cfg_scale:h.cfg_scale,seed:h.seed,width:h.width,height:h.height,sampler_name:h.sampler,sample_method:h.sampler,batch_size:1,n_iter:1,send_images:!0,save_images:!1}}try{const X=await fetch(V,{method:"POST",headers:{"Content-Type":"application/json"},signal:G,body:JSON.stringify(fe)});if(X.ok){const L=await X.json(),te=L?.data?.[0]?.b64_json??L?.images?.[0];if(te){const W=parseFloat(((Date.now()-H)/1e3).toFixed(1));return{image:`data:image/png;base64,${te}`,seed:L.data?.[0]?.seed??h.seed,duration_sec:W}}}else{let L=`Generation failed (HTTP ${X.status})`;try{const te=await X.json();(te?.detail||te?.error?.message)&&(L=te.detail||te.error.message)}catch{}throw new Error(L)}}catch(X){if(X.name==="AbortError"||X.message.startsWith("Generation failed"))throw X;console.warn("Could not reach local server. Falling back to simulation mode.",X)}return await A()}async function Jy(f,b,E){if(f instanceof File){const H=f;if(!na())throw new Error("File import requires the local image generator server.");return await $y(H,b,E),{response:"Model imported successfully"}}if(ia()){const{listen:H}=await Uy(async()=>{const{listen:h}=await import("./event-DeDJUvqC.js");return{listen:h}},[]);let w=null;b&&(w=await H("import-progress",h=>{b(h.payload)}));try{return{response:await ja("import_model_file",{sourcePath:f}),unlisten:w}}catch(h){throw w&&w(),h}}const r=f.split(/[\\/]/).pop()||"imported_model.gguf";console.log(`Web Mode: Simulating copying ${r} to USB models folder`);const x=40;for(let H=1;H<=x;H++){if(E?.aborted)throw new DOMException("The user aborted a request.","AbortError");await new Promise(U=>setTimeout(U,150));const w=H/x*100,h=40+Math.sin(H)*5+Math.random()*2,A=(x-H)*.15;b&&b({filename:r,progress:w,speed_mb_s:h,eta_secs:A,status:"Copying to USB..."})}const N=localStorage.getItem("imported-models"),G=N?JSON.parse(N):[];return G.includes(r)||(G.push({filename:r,sizeBytes:0,size:"Unknown"}),localStorage.setItem("imported-models",JSON.stringify(G))),{response:"Model imported successfully"}}function $y(f,b,E){return new Promise((r,x)=>{const N=new XMLHttpRequest,G=Date.now();let H=!1;N.open("POST",`/api/import-model?filename=${encodeURIComponent(f.name)}`),N.setRequestHeader("Content-Type","application/octet-stream");const w=()=>{H=!0,N.abort()};E&&(E.aborted?w():E.addEventListener("abort",w,{once:!0})),N.upload.onprogress=h=>{if(!h.lengthComputable||!b)return;const A=Math.max(.1,(Date.now()-G)/1e3),U=h.loaded/A,V=Math.max(0,h.total-h.loaded);b({filename:f.name,progress:h.loaded/h.total*100,speed_mb_s:U/(1024*1024),eta_secs:V/Math.max(1,U),status:"Copying to models folder..."})},N.onload=()=>{let h={};try{h=JSON.parse(N.responseText||"{}")}catch{}N.status>=200&&N.status<300&&h.ok!==!1?(b&&b({filename:f.name,progress:100,speed_mb_s:0,eta_secs:0,status:"Import complete"}),r(h)):x(new Error(h.error||`Import failed (HTTP ${N.status})`))},N.onerror=()=>x(new Error("Import failed while uploading the file.")),N.onabort=()=>x(new DOMException(H?"Import cancelled by user.":"Import aborted.","AbortError")),N.send(f)})}async function Wy(f){if(ia())return await ja("delete_model_file",{filename:f});if(typeof window<"u"&&(window.location.hostname==="localhost"||window.location.hostname==="127.0.0.1"))try{const x=await fetch("/api/delete-model",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({filename:f})}),N=await x.json();if(!x.ok)throw new Error(N.error||"Failed to delete model file");return N}catch(x){throw console.error("Failed to delete model via local API:",x),x}const E=localStorage.getItem("imported-models");let r=E?JSON.parse(E):[];return r=r.filter(x=>ui(x).filename!==f),localStorage.setItem("imported-models",JSON.stringify(r)),`Simulated deletion of ${f} from browser session`}async function hm(){const f=await mm();try{return(await fetch(`${f}/v1/models`,{method:"GET"})).ok}catch{return!1}}async function Fy(f=30){const b=ia()||na()?f:3;for(let E=0;EsetTimeout(x,500))}return!1}async function Iy(f){try{return await(await fetch("/api/download-model",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:f})})).json()}catch(b){return console.error("Failed to start model download:",b),{ok:!1,error:b.message}}}async function Py(){try{return await(await fetch("/api/cancel-download",{method:"POST"})).json()}catch(f){return console.error("Failed to cancel model download:",f),{ok:!1,error:f.message}}}async function cm(){try{return await(await fetch("/api/download-progress")).json()}catch(f){return console.error("Failed to get download progress:",f),{active:!1,error:f.message}}}async function eg(){try{return await(await fetch("/api/generation-progress")).json()}catch(f){return console.error("Failed to get generation progress:",f),{active:!1,error:f.message}}}const tg=O.memo(({img:f,idx:b,isSelected:E,onClick:r})=>{const x=N=>{r(b,N)};return c.jsxs("div",{className:`gallery-item ${E?"selected":""}`,onClick:x,title:`Prompt: ${f.prompt} -Seed: ${f.seed}${f.duration_sec?` -Time: ${f.duration_sec.toFixed(1)}s`:""}`,children:[c.jsx("img",{src:f.url,className:"gallery-thumb",alt:"Thumbnail",loading:"lazy",decoding:"async"}),E&&c.jsx("div",{className:"gallery-select-badge",children:c.jsx(ks,{size:14})})]})});function lg({prompt:f,setPrompt:b,negativePrompt:E,setNegativePrompt:r,constraints:x,setConstraints:N,activeModel:G,generatedImages:H,setGeneratedImages:w,isGenerating:h,setIsGenerating:A,generationProgress:U,setGenerationProgress:V,setActiveTab:fe,showAlert:X=async({message:te})=>window.alert(te),showConfirm:L=async({message:te})=>window.confirm(te)}){const[te,W]=O.useState(null),[Oe,de]=O.useState(null),[Ee,ke]=O.useState(null),[Ae,P]=O.useState(!1),[ve,Ke]=O.useState(0),[yt,me]=O.useState(0),[Je,st]=O.useState(0),[$e,Xe]=O.useState(""),[_,C]=O.useState(!1),[J,he]=O.useState(!1),[ce,d]=O.useState(!1),[j,B]=O.useState(null),[q,K]=O.useState(null),[ee,se]=O.useState(!1),[Ze,Ne]=O.useState([]),[gt,Et]=O.useState(null),We=O.useRef(null),Ht=O.useRef(null),Tt=O.useRef(!1),Z=Y=>{const ye=Y.target.files?.[0];if(Y.target.value="",!ye)return;if(!ye.type.startsWith("image/")){K("Please choose an image file (PNG, JPG, or WEBP).");return}const He=new FileReader;He.onload=()=>Et(He.result),He.onerror=()=>K("Could not read the selected image file."),He.readAsDataURL(ye)},ie=()=>Et(null),D=async()=>{if(!f.trim())return;K(null),d(!0),B(null);let Y=!1;try{const Se=await Cl();if(Se&&Se.settings){const xe=Se.settings,Ge=xe.model?xe.model.split(/[\\/]/).pop():null,_e=G?G.split(/[\\/]/).pop():null;(Ge!==_e||parseInt(xe.steps)!==parseInt(x.steps)||Math.abs(parseFloat(xe.cfgScale)-parseFloat(x.cfgScale))>.05||xe.sampler!==x.sampler||parseInt(xe.threads)!==parseInt(x.threads)||!!xe.useGpu!=(x.useGpu!==!1)||(xe.backendType||(xe.useGpu===!1?"cpu":"auto"))!==(x.backendType||(x.useGpu===!1?"cpu":"auto")))&&(Y=!0)}else Y=!0}catch{Y=!0}if(Y)try{console.log("Settings out of sync, restarting backend..."),await Vs(G,x);let Se=!1,xe=null;for(let Ge=0;Ge<240;Ge++){const _e=await Cl();if(_e.loading&&B({progress:_e.loading.progress||0,phase:_e.loading.phase||"Loading model...",speed:_e.loading.speed||"",current:_e.loading.current||0,total:_e.loading.total||0,backendMode:_e.loading.backendMode||_e.settings?.backendMode||"",device:_e.loading.device||_e.settings?.backendDevice||""}),_e.ready){Se=!0;break}if(_e.error){xe=_e.error;break}if(!_e.running&&Ge>3){xe="The backend process terminated immediately on startup.";break}await new Promise(Ie=>setTimeout(Ie,500))}if(!Se)throw new Error(xe||"Backend server failed to start after applying configuration changes.")}catch(Se){console.error("Failed to restart backend:",Se),K(Se.message||"Failed to sync settings with backend."),d(!1);return}d(!1),B(null),We.current&&clearInterval(We.current),A(!0),V(0),Ke(0),st(0),Xe(""),Tt.current=!1,C(!1),se(!1),ke(null),W(null);const ye=(x.width>=1024?140:35)*.04,He=x.width>=1024?140:35,pt=(x.backendType||(x.useGpu===!1?"cpu":"auto"))!=="cpu";let ft=pt?ye:He,tt=!1,qt=0,rt=0,vt=0;me(Math.round(x.steps*ft)),We.current=setInterval(async()=>{vt++,Ke(vt);try{const Se=await eg();if(Se&&Se.active){const xe=Se.decoding||!1;se(xe);const Ge=xe?Se.steps||x.steps||20:Se.step||0,_e=Se.steps||x.steps||20,Ie=xe?"":Se.speed||"";(xe||Se.steps>0)&&(Tt.current=!0,st(Ge)),Xe(xe?"decoding":Ie);let je=0;xe?je=99:_e>0&&(je=Math.round(Ge/_e*100)),je=Math.min(99,je),xe||Se.steps>0?V(je):V(bt=>Math.max(bt,je));let dt=0;if(xe)dt=3;else{const bt=parseFloat(Ie);if(!isNaN(bt)&&bt>0){const cl=Math.max(0,_e-Ge);Ie.includes("it/s")?dt=cl/bt:Ie.includes("s/it")&&(dt=cl*bt)}else{const cl=pt?ye:He;dt=Math.max(0,(_e-Ge)*cl)}}if(pt&&!tt){const bt=Ie.includes("it/s")&&numericSpeed<.2,cl=Ie.includes("s/it")&&numericSpeed>5;(bt||cl)&&(tt=!0,C(!0))}me(Math.round(dt))}else V(xe=>Math.min(15,xe+1)),me(xe=>Math.max(1,xe-1))}catch(Se){console.warn("Failed to get generation progress, running simulation fallback:",Se),pt&&!tt&&vt>x.steps*ye*1.3&&(tt=!0,C(!0),ft=He,qt=vt,rt=Math.min(95,Math.round(vt/(x.steps*ye)*100)));let xe=Math.min(x.steps-1,Math.floor(vt/ft)),Ge=x.steps*ft;if(vt>=Ge){const je=Math.ceil(vt/Math.max(.5,xe));ft=Math.max(ft+15,je+15),Ge=x.steps*ft,xe=Math.min(x.steps-1,Math.floor(vt/ft))}const _e=Math.max(1,Ge-vt);me(Math.round(_e));let Ie=0;if(tt){const je=99-rt,dt=Ge-qt;if(dt>0){const bt=Math.min(1,(vt-qt)/dt);if(bt<.85)Ie=Math.round(rt+bt*je);else{const cl=(vt-(qt+dt*.85))/(ft*2),ci=1-Math.exp(-cl);Ie=Math.min(99,Math.round(rt+.85*je+.14*je*ci))}}else Ie=99}else if(vtMath.max(je,Ie))}},1e3);try{Ht.current=new AbortController;const Se=await Ky(f,E,x,G,gt,je=>V(dt=>Math.max(dt,je)),Ht.current.signal);We.current&&clearInterval(We.current),V(100),st(x.steps),me(0),Xe("");const xe={prompt:f,negativePrompt:E,seed:Se.seed,steps:x.steps,cfgScale:x.cfgScale,width:x.width,height:x.height,sampler:x.sampler,model:G,mode:gt?"img2img":"txt2img",denoisingStrength:gt?x.denoisingStrength:null,duration_sec:Se.duration_sec,timestamp:new Date().toLocaleTimeString()};let Ge=null,_e=null;try{if(Ge=await Qy(Se.image,xe),_e=Ge.url,!_e)throw new Error("The local server saved the image but did not return a file URL.")}catch(je){throw console.error("Generated image could not be saved:",je),new Error(`Image generated but could not be saved to USB: ${je.message||je}`)}W(_e),de(Se.seed),ke(Se.duration_sec);const Ie={...xe,...Ge||{},url:_e};w(je=>[Ie,...je])}catch(Se){Se.name==="AbortError"?console.log("Generation request was aborted by the user"):(console.error("Generation failed:",Se),K(Se.message||"Generation failed. Please try again.")),We.current&&clearInterval(We.current)}finally{A(!1),Ht.current=null}},R=async()=>{if(await L({title:"Stop Generation?",message:"The current image generation will be cancelled and the local model server may restart.",confirmLabel:"Stop",danger:!0})){We.current&&clearInterval(We.current),he(!0),Ht.current&&Ht.current.abort();try{console.log("Cancelling: Stopping server to kill generation threads..."),await Js(),st(0),Xe(""),se(!1),Tt.current=!1,G&&(console.log("Cancelling: Restarting server for future generations..."),await Vs(G,x),await Fy()||console.warn("Server failed to restart properly."))}catch(Y){console.error("Failed to restart server on cancellation:",Y)}finally{he(!1),A(!1),V(0),me(0)}}},F=async()=>{if(te)try{if(te.startsWith("data:image")){const ye=await(await fetch(te)).blob();await navigator.clipboard.write([new ClipboardItem({[ye.type]:ye})])}else{const Y=new Image;Y.crossOrigin="anonymous",Y.src=te,Y.onload=async()=>{const ye=document.createElement("canvas");ye.width=Y.width,ye.height=Y.height,ye.getContext("2d").drawImage(Y,0,0),ye.toBlob(async pt=>{await navigator.clipboard.write([new ClipboardItem({[pt.type]:pt})])},"image/png")}}P(!0),setTimeout(()=>P(!1),2e3)}catch(Y){console.error("Failed to copy image:",Y)}},ae=()=>{if(!te)return;const Y=document.createElement("a");Y.href=te,Y.download=`diffusion-${Oe||Date.now()}.png`,document.body.appendChild(Y),Y.click(),document.body.removeChild(Y)},At=(Y,ye=`diffusion-${Date.now()}.png`)=>{if(!Y)return;const He=document.createElement("a");He.href=Y,He.download=ye,document.body.appendChild(He),He.click(),document.body.removeChild(He)},Gt=O.useCallback(Y=>{b(Y.prompt),r(Y.negativePrompt),N(ye=>({...ye,steps:Y.steps,cfgScale:Y.cfgScale,width:Y.width,height:Y.height,sampler:Y.sampler,seed:Y.seed})),W(Y.url),de(Y.seed),ke(Y.duration_sec??Y.durationSec??null)},[b,r,N]),dl=O.useCallback((Y,ye)=>{const He=ye.ctrlKey||ye.metaKey||ye.shiftKey;Ne(pt=>He?pt.includes(Y)?pt.filter(ft=>ft!==Y):[...pt,Y]:[Y]),H[Y]&&Gt(H[Y])},[H,Gt]),Re=Ze.map(Y=>H[Y]).filter(Boolean),al=()=>{Ne([])},Fe=()=>{Re[0]&&Gt(Re[0])},za=()=>{Re.forEach((Y,ye)=>{const He=Y.seed??Date.now();At(Y.url,`diffusion-${He}-${ye+1}.png`)})},Ta=async()=>{if(Re.length===0)return;const Y=Re.length;if(await L({title:"Delete Selected Outputs?",message:`Delete ${Y} selected output${Y===1?"":"s"} from USB? This removes the image and metadata files.`,confirmLabel:"Delete",danger:!0}))try{await Vy(Re);const He=new Set(Ze);w(pt=>pt.filter((ft,tt)=>!He.has(tt))),Ne([]),Re.some(pt=>pt.url===te)&&(W(null),de(null),ke(null))}catch(He){console.error("Failed to delete selected outputs:",He),X({title:"Delete Failed",message:He.message||String(He),danger:!0})}};return c.jsxs("div",{className:"workspace-area",children:[c.jsxs("div",{className:"workspace-title-section",children:[c.jsx("h2",{className:"workspace-title",children:"Generator Dashboard"}),c.jsx("p",{className:"workspace-subtitle",children:"Create high-fidelity images completely locally on your external drive."})]}),c.jsxs("div",{className:"generator-layout",children:[c.jsx("div",{children:c.jsxs("div",{className:"m3-card",children:[c.jsxs("h3",{className:"m3-card-title",children:[c.jsx(Su,{size:18,style:{color:"var(--md-sys-color-primary)"}}),"Generation Workspace"]}),c.jsxs("div",{className:"m3-field-group",children:[c.jsxs("div",{className:"m3-text-field",children:[c.jsx("label",{className:"m3-text-field-label",children:"Positive Prompt"}),c.jsx("textarea",{className:"m3-textarea",value:f,onChange:Y=>b(Y.target.value),placeholder:"Enter a descriptive prompt (e.g. 'A futuristic city in cyberpunk aesthetic, high details, cinematic lighting')...",disabled:h})]}),c.jsxs("div",{className:"m3-text-field",children:[c.jsx("label",{className:"m3-text-field-label",children:"Negative Prompt (Optional)"}),c.jsx("input",{type:"text",className:"m3-input",value:E,onChange:Y=>r(Y.target.value),placeholder:"Items to avoid (e.g. 'blurry, low quality, deformed hands, texts')...",disabled:h})]}),c.jsxs("div",{className:"m3-text-field",children:[c.jsx("label",{className:"m3-text-field-label",children:"Base Image (Optional)"}),gt?c.jsxs("div",{style:{display:"flex",gap:"12px",alignItems:"center",padding:"12px",background:"var(--md-sys-color-surface-variant)",borderRadius:"var(--md-shape-corner-medium)",border:"1px solid var(--md-sys-color-outline-variant)"},children:[c.jsx("img",{src:gt,alt:"Base",style:{width:"64px",height:"64px",objectFit:"cover",borderRadius:"8px",flexShrink:0}}),c.jsxs("div",{style:{flex:1,minWidth:0},children:[c.jsx("div",{style:{fontWeight:600,fontSize:"0.85rem"},children:"Base image ready"}),c.jsx("div",{style:{fontSize:"0.75rem",color:"var(--md-sys-color-outline)"},children:"The AI will redraw this image guided by your prompt."})]}),c.jsxs("button",{type:"button",className:"m3-btn m3-btn-error",style:{height:"34px",flexShrink:0},onClick:ie,disabled:h,children:[c.jsx(rn,{size:14}),c.jsx("span",{children:"Remove"})]})]}):c.jsxs("label",{className:"import-box",style:{margin:0,padding:"16px",cursor:h?"not-allowed":"pointer"},children:[c.jsx("input",{type:"file",accept:"image/png,image/jpeg,image/webp",style:{display:"none"},onChange:Z,disabled:h}),c.jsx(iy,{className:"import-icon"}),c.jsx("span",{style:{fontWeight:600},children:"Upload a base image"}),c.jsx("span",{style:{fontSize:"0.75rem",color:"var(--md-sys-color-outline)",textAlign:"center"},children:"Optional. Generate a new image based on one of your own."})]}),gt&&c.jsxs("div",{style:{marginTop:"12px"},children:[c.jsxs("div",{style:{display:"flex",justifyContent:"space-between",fontSize:"0.8rem",marginBottom:"4px"},children:[c.jsx("span",{children:"Transformation strength"}),c.jsx("span",{children:x.denoisingStrength.toFixed(2)})]}),c.jsx("input",{type:"range",min:"0.1",max:"1",step:"0.05",value:x.denoisingStrength,onChange:Y=>N(ye=>({...ye,denoisingStrength:parseFloat(Y.target.value)})),disabled:h,style:{width:"100%"}}),c.jsx("div",{style:{fontSize:"0.72rem",color:"var(--md-sys-color-outline)",marginTop:"2px"},children:"Lower = closer to your image; higher = more creative freedom. 0.7 is a good start."})]})]}),c.jsxs("div",{className:"m3-text-field",children:[c.jsx("label",{className:"m3-text-field-label",children:"Active Image Constraints"}),c.jsxs("div",{className:"chips-container",children:[c.jsxs("div",{className:"status-chip",onClick:()=>fe("constraints"),children:[c.jsx(Ks,{size:14}),c.jsxs("span",{children:["Resolution: ",x.width,"x",x.height]})]}),c.jsxs("div",{className:"status-chip",onClick:()=>fe("constraints"),children:[c.jsx(aa,{size:14}),c.jsxs("span",{children:["Steps: ",x.steps]})]}),c.jsxs("div",{className:"status-chip",onClick:()=>fe("constraints"),children:[c.jsx(Su,{size:14}),c.jsxs("span",{children:["Sampler: ",x.sampler]})]}),c.jsx("div",{className:"status-chip",onClick:()=>fe("constraints"),children:c.jsxs("span",{children:["Seed: ",x.seed===-1?"Random":x.seed]})})]})]}),c.jsxs("button",{className:"m3-btn m3-btn-filled",style:{height:"48px",marginTop:"8px"},onClick:D,disabled:h||!f.trim()||!G,children:[c.jsx(Su,{size:18}),c.jsx("span",{children:G?gt?"Generate from Base Image":"Generate Local Image":"Select Model to Generate"})]})]})]})}),c.jsxs("div",{className:"m3-card output-panel",children:[c.jsx("div",{className:"output-image-container",children:h||J||ce?c.jsx("div",{className:"progress-overlay",children:ce?c.jsxs("div",{style:{display:"flex",flexDirection:"column",alignItems:"center",gap:"16px",padding:"20px"},children:[c.jsx("div",{className:"progress-spinner"}),c.jsx("div",{className:"progress-text",style:{fontSize:"1.1rem",fontWeight:700,textAlign:"center"},children:"Syncing Settings & Initializing..."}),j?c.jsxs("div",{style:{width:"min(360px, 90%)"},children:[c.jsxs("div",{style:{display:"flex",justifyContent:"space-between",gap:"12px",fontSize:"0.82rem",fontWeight:700,marginBottom:"6px"},children:[c.jsxs("span",{children:[j.phase,j.speed?` (${j.speed})`:""]}),c.jsx("span",{children:j.total>0?`Loaded ${j.current} / ${j.total} tensors`:`${Math.round(j.progress)}%`})]}),c.jsx("div",{className:"progress-bar-container",style:{margin:0},children:c.jsx("div",{className:"progress-bar-fill",style:{width:`${Math.min(100,Math.max(0,j.progress))}%`,transition:"width 0.25s ease"}})}),(j.backendMode||j.device)&&c.jsxs("div",{style:{fontSize:"0.78rem",opacity:.8,textAlign:"center",marginTop:"8px"},children:["Loading on ",j.backendMode||"backend",j.device?` β€’ ${j.device}`:""]})]}):c.jsx("p",{style:{fontSize:"0.85rem",opacity:.8,textAlign:"center",maxWidth:"280px",margin:0},children:"Adjusting inference settings and restarting the local model server."})]}):J?c.jsxs("div",{style:{display:"flex",flexDirection:"column",alignItems:"center",gap:"16px",padding:"20px"},children:[c.jsx("div",{className:"progress-spinner"}),c.jsx("div",{className:"progress-text",style:{fontSize:"1.1rem",fontWeight:700,textAlign:"center"},children:"Cancelling Generation..."}),c.jsx("p",{style:{fontSize:"0.85rem",opacity:.8,textAlign:"center",maxWidth:"280px",margin:0},children:"Stopping CPU/GPU execution threads and reloading model weights. Please wait a moment."})]}):c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"progress-spinner"}),c.jsx("div",{className:"progress-text",style:{fontSize:"1.1rem",fontWeight:700},children:ee?"Decoding Latents...":"Generating Locally..."}),_&&c.jsx("div",{style:{fontSize:"0.8rem",color:"var(--md-sys-color-primary)",fontWeight:600,background:"var(--md-sys-color-primary-container)",padding:"4px 12px",borderRadius:"12px",margin:"4px 0 8px 0",animation:"pulse 2s infinite"},children:"⚠️ CPU Fallback Detected (Running Slower)"}),c.jsxs("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"16px",width:"80%",margin:"8px 0 0 0",fontSize:"0.85rem",opacity:.9},children:[c.jsxs("div",{style:{textAlign:"left"},children:[c.jsx("strong",{children:"Step:"})," ",Je," / ",x.steps," (",Math.max(0,x.steps-Je)," left)"]}),c.jsxs("div",{style:{textAlign:"right"},children:[c.jsx("strong",{children:"Elapsed:"})," ",ve,"s",$e&&` (${$e==="decoding"?"decoding":$e})`]})]}),c.jsx("div",{className:"progress-bar-container",style:{margin:"4px 0 10px 0"},children:c.jsx("div",{className:"progress-bar-fill",style:{width:`${U}%`,transition:"width 1s linear"}})}),c.jsxs("div",{style:{fontSize:"0.85rem",opacity:.8,display:"flex",gap:"8px",alignItems:"center"},children:[c.jsxs("span",{children:["Progress: ",U,"%"]}),c.jsx("span",{children:"β€’"}),c.jsxs("span",{children:["Est. remaining: ",yt,"s"]})]}),c.jsx("button",{className:"m3-btn m3-btn-error",style:{marginTop:"16px",height:"36px",padding:"0 24px"},onClick:R,children:"Stop Generating"})]})}):te?c.jsx("img",{src:te,className:"output-image",alt:"Generated Output"}):q?c.jsxs("div",{style:{textAlign:"center",padding:"24px",color:"var(--md-sys-color-error)",display:"flex",flexDirection:"column",alignItems:"center",gap:"16px"},children:[c.jsx("div",{style:{background:"rgba(239, 68, 68, 0.1)",padding:"16px",borderRadius:"50%",display:"flex",alignItems:"center",justifyContent:"center"},children:c.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",width:"48",height:"48",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2",children:c.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"})})}),c.jsx("h3",{style:{margin:0,fontSize:"1.2rem",fontWeight:"bold"},children:"Generation Failed"}),c.jsx("p",{style:{margin:0,fontSize:"0.95rem",opacity:.9,maxWidth:"320px",lineHeight:"1.4"},children:q}),c.jsxs("div",{style:{marginTop:"8px",fontSize:"0.85rem",opacity:.8,color:"var(--md-sys-color-on-surface-variant)"},children:["Hint: Try lowering the resolution in the ",c.jsx("strong",{children:"Image Constraints"})," settings (e.g., to 512x512)."]})]}):c.jsxs("div",{style:{textAlign:"center"},children:[c.jsx(Su,{className:"output-placeholder-icon"}),c.jsxs("p",{className:"output-placeholder-text",children:["Your generated image will appear here.",c.jsx("br",{}),"Enter a prompt on the left and click Generate."]})]})}),te&&!h&&c.jsxs(c.Fragment,{children:[c.jsxs("div",{style:{width:"100%",display:"flex",justifyContent:"space-between",marginTop:"12px",fontSize:"0.8rem",color:"var(--md-sys-color-outline)"},children:[c.jsxs("span",{children:["Seed: ",Oe]}),Ee&&c.jsxs("span",{children:["Inference Time: ",Ee.toFixed(1),"s"]})]}),c.jsxs("div",{className:"image-actions",children:[c.jsxs("button",{className:"m3-btn m3-btn-tonal",onClick:F,children:[Ae?c.jsx(ks,{size:16}):c.jsx(J0,{size:16}),c.jsx("span",{children:Ae?"Copied":"Copy"})]}),c.jsxs("button",{className:"m3-btn m3-btn-outlined",onClick:ae,children:[c.jsx(nm,{size:16}),c.jsx("span",{children:"Save to USB"})]})]})]})]})]}),c.jsxs("div",{className:"gallery-section",children:[c.jsxs("div",{className:"gallery-header",children:[c.jsx("h3",{className:"m3-card-title",style:{marginBottom:0},children:"Recent Outputs Gallery"}),Re.length>0&&c.jsxs("div",{className:"gallery-selection-actions",children:[c.jsxs("span",{className:"gallery-selection-count",children:[Re.length," selected"]}),c.jsxs("button",{className:"m3-btn m3-btn-tonal",onClick:Fe,children:[c.jsx(ks,{size:14}),c.jsx("span",{children:"Use"})]}),c.jsxs("button",{className:"m3-btn m3-btn-outlined",onClick:za,children:[c.jsx(nm,{size:14}),c.jsx("span",{children:"Download"})]}),c.jsxs("button",{className:"m3-btn m3-btn-error",onClick:Ta,children:[c.jsx(rn,{size:14}),c.jsx("span",{children:"Delete"})]}),c.jsx("button",{className:"m3-btn m3-btn-outlined",onClick:al,children:"Clear"})]})]}),H.length===0?c.jsx("div",{className:"m3-card gallery-empty",children:"No images generated in this session yet. Everything will be saved directly on your external drive."}):c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"gallery-hint",children:"Click to select one image. Ctrl-click or Shift-click to select multiple."}),c.jsx("div",{className:"gallery-grid",children:H.map((Y,ye)=>c.jsx(tg,{img:Y,idx:ye,isSelected:Ze.includes(ye),onClick:dl},Y.image||ye))})]})]})]})}const ag=O.memo(lg),ng=[{group:"SDXL - Best Quality",items:[{name:"Juggernaut XL v9 Lightning",filename:"Juggernaut_RunDiffusionPhoto2_Lightning_4Steps.safetensors",format:"Safetensors",approxSize:"6.6 GB",resolution:"1024x1024",notes:"Photorealistic SDXL model tuned for 4-8 step Lightning generation.",url:"https://huggingface.co/RunDiffusion/Juggernaut-XL-Lightning/resolve/main/Juggernaut_RunDiffusionPhoto2_Lightning_4Steps.safetensors"},{name:"DreamShaper XL Lightning",filename:"DreamShaperXL_Lightning.safetensors",format:"Safetensors",approxSize:"6.6 GB",resolution:"1024x1024",notes:"All-around SDXL model for fantasy art, renders, illustration, and stylized images.",url:"https://huggingface.co/Lykon/dreamshaper-xl-lightning/resolve/main/DreamShaperXL_Lightning.safetensors"}]},{group:"SD 1.5 - Ultra Fast / Low VRAM",items:[{name:"DreamShaper 8",filename:"DreamShaper_8_pruned.safetensors",format:"Safetensors",approxSize:"2.1 GB",resolution:"512x512",notes:"General-purpose SD 1.5 model for illustration, anime, and semi-realistic portraits.",url:"https://huggingface.co/Lykon/DreamShaper/resolve/main/DreamShaper_8_pruned.safetensors"},{name:"CyberRealistic V8",filename:"CyberRealistic_V8_FP16.safetensors",format:"Safetensors",approxSize:"2.1 GB",resolution:"512x512",notes:"High-fidelity realism and photorealism in the SD 1.5 ecosystem.",url:"https://huggingface.co/cyberdelia/CyberRealistic/resolve/main/CyberRealistic_V8_FP16.safetensors"},{name:"ReV Animated v1.2.2",filename:"rev-animated-v1-2-2.safetensors",format:"Safetensors",approxSize:"2.1 GB",resolution:"512x512",notes:"Semi-realistic, 2.5D, anime, fantasy, and stylized digital art.",url:"https://huggingface.co/danbrown/RevAnimated-v1-2-2/resolve/main/rev-animated-v1-2-2.safetensors"}]}];function ig({activeModel:f,setActiveModel:b,serverRunning:E,setServerRunning:r,constraints:x,showAlert:N=async({message:H})=>window.alert(H),showConfirm:G=async({message:H})=>window.confirm(H)}){const[H,w]=O.useState([]),[h,A]=O.useState(null),[U,V]=O.useState(0),[fe,X]=O.useState(""),[L,te]=O.useState(null),[W,Oe]=O.useState(null),[de,Ee]=O.useState(null),[ke,Ae]=O.useState({filename:"",speed:"0.0",eta:0,status:""}),[P,ve]=O.useState(""),[Ke,yt]=O.useState(null),[me,Je]=O.useState(!1),[st,$e]=O.useState({progress:0,phase:""}),[Xe,_]=O.useState(null),[C,J]=O.useState({backendMode:"",backendBinary:"",backendDevice:""}),he=H.map(D=>ui(D).filename),ce=L!==null||me;O.useEffect(()=>{B(),d()},[]),O.useEffect(()=>{if(!E&&!L){J({backendMode:"",backendBinary:"",backendDevice:""});return}let D=!1;const R=async()=>{try{const ae=await Cl();if(D)return;J({backendMode:ae.settings?.backendMode||ae.loading?.backendMode||"",backendBinary:ae.settings?.backendBinary||ae.loading?.backendBinary||"",backendDevice:ae.settings?.backendDevice||ae.loading?.device||""})}catch{}};R();const F=setInterval(R,2e3);return()=>{D=!0,clearInterval(F)}},[E,L]);const d=async()=>{try{const D=await cm();D.active&&D.filename&&j(D.filename)}catch(D){console.error("Check active download failed:",D)}},j=D=>{A(D);const R=setInterval(async()=>{try{const F=await cm();F.active?(V(F.progress===-1?0:F.progress),X(`${F.speed} β€’ ETA ${F.eta}s`)):(clearInterval(R),A(null),F.error?String(F.error).toLowerCase().includes("cancelled")||N({title:"Download Failed",message:F.error,danger:!0}):B())}catch(F){console.error("Polling error:",F)}},1e3)},B=async()=>{try{const D=await ky();w(D.map(ui).filter(R=>R.filename))}catch(D){console.error(D)}},q=async(D,R)=>{if(h||he.includes(R)&&!await G({title:"Overwrite Model?",message:`"${R}" is already downloaded. Do you want to download and overwrite it?`,confirmLabel:"Overwrite"}))return;const F=typeof window<"u"&&window.__TAURI_INTERNALS__!==void 0,ae=typeof window<"u"&&(window.location.hostname==="localhost"||window.location.hostname==="127.0.0.1");if(!F&&!ae){N({title:"Server Required",message:"Model download requires the local image generator server.",danger:!0});return}try{const At=await Iy(D);At.ok?j(R):N({title:"Download Failed",message:At.error||"Unknown error",danger:!0})}catch(At){N({title:"Download Error",message:At.message,danger:!0})}},K=async D=>{if(D.preventDefault(),!P.trim())return;let R="model.gguf";try{const F=new URL(P.trim());if(F.protocol!=="http:"&&F.protocol!=="https:")throw new Error("URL protocol must be http or https");if(R=F.pathname.split("/").pop()||"",!R)throw new Error("Could not extract filename from URL");const ae=R.toLowerCase();if(!ae.endsWith(".gguf")&&!ae.endsWith(".safetensors")&&!ae.endsWith(".ckpt"))throw new Error("URL must point to a .gguf, .safetensors, or .ckpt weights file")}catch(F){N({title:"Invalid URL",message:F.message,danger:!0});return}await q(P.trim(),R),ve("")},ee=D=>{q(D.url,D.filename)},se=async()=>{await Py(),A(null),V(0),X(""),await B()},Ze=async D=>{if(f&&f!==D&&E){_(D);return}await Ne(D)},Ne=async D=>{const R=typeof window<"u"&&window.__TAURI_INTERNALS__!==void 0,F=typeof window<"u"&&(window.location.hostname==="localhost"||window.location.hostname==="127.0.0.1"),At=(await Cl())?.port||8080;if(!R&&!F){const Gt=await hm(),dl=`.\\backend\\win\\vulkan\\sd-vulkan.exe --listen-port ${At} --model .\\models\\${D} --threads 8`;Gt?await G({title:"Web Browser Mode",message:`To load "${D}" on the active GPU server, restart your terminal backend with: - -${dl} - -Update the UI status anyway?`,confirmLabel:"Update UI"})&&(b(D),r(!0)):await G({title:"Backend Not Running",message:`C++ backend server is not running on port ${At}. - -To run locally, start: - -${dl} - -Proceed in Simulation Mode instead?`,confirmLabel:"Proceed"})&&(b(D),r(!1));return}te(D),Oe({progress:0,phase:"Starting backend...",speed:"",current:0,total:0,model:D,backendMode:"",backendBinary:"",device:""});try{const Gt=await Vs(D,x);console.log(Gt);let dl=!1,Re=null;for(let al=0;al<240;al++){const Fe=await Cl();if(Fe.loading&&Oe({progress:Fe.loading.progress||0,phase:Fe.loading.phase||"Loading model...",speed:Fe.loading.speed||"",current:Fe.loading.current||0,total:Fe.loading.total||0,model:Fe.loading.model||D,backendMode:Fe.loading.backendMode||Fe.settings?.backendMode||"",backendBinary:Fe.loading.backendBinary||Fe.settings?.backendBinary||"",device:Fe.loading.device||Fe.settings?.backendDevice||""}),Fe.ready){dl=!0;break}if(Fe.error){Re=Fe.error;break}if(!Fe.running&&al>3){Re="The backend process terminated immediately on startup.";break}await new Promise(za=>setTimeout(za,500))}if(dl)Oe(al=>({...al||{},progress:100,phase:"Model ready",speed:"",current:al?.current||0,total:al?.total||0,model:D})),b(D),r(!0);else throw new Error(Re||`Model server failed to respond on port ${At}.`)}catch(Gt){console.error("Failed to load model:",Gt),N({title:"Model Load Failed",message:Gt.message||String(Gt),danger:!0})}finally{setTimeout(()=>Oe(null),800),te(null)}},gt=async()=>{const D=Xe;_(null),await Et(),D&&await Ne(D)},Et=async()=>{if(me)return;Je(!0),$e({progress:10,phase:"Stopping backend process..."});const D=setInterval(async()=>{try{const R=await Cl();R.unloading?.active?$e({progress:R.unloading.progress||50,phase:R.unloading.phase||"Unloading model..."}):$e(F=>({progress:Math.min(95,F.progress+10),phase:F.phase||"Unloading model..."}))}catch{}},300);try{await Js(),$e({progress:100,phase:"Model unloaded"}),b(null),_(null),r(!1)}catch(R){console.error(R),N({title:"Unload Failed",message:R.message||String(R),danger:!0})}finally{clearInterval(D),setTimeout(()=>{Je(!1),$e({progress:0,phase:""})},500)}},We=async D=>{if(await G({title:"Delete Model?",message:`Delete "${D}" from your drive?`,confirmLabel:"Delete",danger:!0}))try{await Wy(D),await B(),f===D&&await Et()}catch(R){console.error("Failed to delete model:",R),N({title:"Delete Failed",message:R.message||String(R),danger:!0})}},Ht=async(D,R)=>{const F=new AbortController;yt(F),Ee(0),Ae({filename:R,speed:"0.0",eta:0,status:"Initiating file copy..."});try{await Jy(D,ae=>{Ee(Math.round(ae.progress)),Ae({filename:ae.filename,speed:ae.speed_mb_s.toFixed(1),eta:Math.round(ae.eta_secs),status:ae.status})},F.signal),await B()}catch(ae){console.error("Import failed:",ae),ae.name!=="AbortError"&&N({title:"Import Failed",message:ae.message||String(ae),danger:!0})}finally{Ee(null),yt(null)}},Tt=()=>{Ke&&Ke.abort()},Z=D=>{const R=D.target.files[0];R&&Ht(R,R.name),D.target.value=""},ie=!!h;return c.jsxs("div",{className:"workspace-area",children:[c.jsxs("div",{className:"workspace-title-section",children:[c.jsx("h2",{className:"workspace-title",children:"Model Manager"}),c.jsx("p",{className:"workspace-subtitle",children:"Manage local files and download recommended weights directly to your local models folder."})]}),f&&c.jsx("div",{className:"m3-card",style:{borderLeft:"4px solid var(--md-sys-color-primary)",background:"var(--md-sys-color-primary-container)",color:"var(--md-sys-color-on-primary-container)"},children:c.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[c.jsxs("div",{children:[c.jsxs("h4",{style:{fontWeight:700},children:["Active Model: ",f]}),c.jsx("p",{style:{fontSize:"0.85rem",marginTop:"2px",opacity:.9},children:"The local C++ stable-diffusion server is running. Real-time telemetry is displaying performance status in the top bar."}),(C.backendMode||C.backendDevice||C.backendBinary)&&c.jsxs("div",{style:{display:"flex",flexWrap:"wrap",gap:"8px",marginTop:"10px"},children:[C.backendMode&&c.jsxs("span",{className:"status-chip",style:{cursor:"default"},children:[c.jsx(xu,{size:14}),c.jsxs("span",{children:["Loaded on ",C.backendMode]})]}),C.backendDevice&&c.jsx("span",{className:"status-chip",style:{cursor:"default"},children:c.jsx("span",{children:C.backendDevice})}),C.backendBinary&&c.jsx("span",{className:"status-chip",style:{cursor:"default"},children:c.jsx("span",{children:C.backendBinary})})]})]}),c.jsxs("button",{className:"m3-btn m3-btn-error",onClick:Et,disabled:me||L!==null,children:[me?c.jsx(aa,{className:"progress-spinner",size:16}):c.jsx(rn,{size:16}),c.jsx("span",{children:me?"Unloading":"Unload Server"})]})]})}),me&&c.jsxs("div",{className:"m3-card",style:{borderLeft:"4px solid var(--md-sys-color-error)",marginTop:"16px"},children:[c.jsx("h4",{style:{fontWeight:700,marginBottom:"8px"},children:"Unloading Model"}),c.jsxs("div",{className:"model-progress-section",style:{margin:"8px 0 0 0"},children:[c.jsxs("div",{className:"model-progress-label",children:[c.jsx("span",{children:st.phase||"Unloading model..."}),c.jsxs("span",{children:[Math.round(st.progress),"%"]})]}),c.jsx("div",{className:"model-progress-bar",children:c.jsx("div",{className:"model-progress-fill",style:{width:`${Math.min(100,Math.max(0,st.progress))}%`,transition:"width 0.25s ease"}})})]})]}),W&&c.jsxs("div",{className:"m3-card",style:{borderLeft:"4px solid var(--md-sys-color-secondary)",marginTop:"16px"},children:[c.jsxs("h4",{style:{fontWeight:700,marginBottom:"8px"},children:["Loading Model: ",W.model||L]}),(W.backendMode||W.backendBinary||W.device)&&c.jsxs("div",{style:{display:"flex",flexWrap:"wrap",gap:"8px",marginBottom:"10px"},children:[W.backendMode&&c.jsxs("span",{className:"status-chip",style:{cursor:"default"},children:[c.jsx(xu,{size:14}),c.jsxs("span",{children:["Loading on ",W.backendMode]})]}),W.device&&c.jsx("span",{className:"status-chip",style:{cursor:"default"},children:c.jsx("span",{children:W.device})}),W.backendBinary&&c.jsx("span",{className:"status-chip",style:{cursor:"default"},children:c.jsx("span",{children:W.backendBinary})})]}),c.jsxs("div",{className:"model-progress-section",style:{margin:"8px 0 0 0"},children:[c.jsxs("div",{className:"model-progress-label",children:[c.jsxs("span",{children:[W.phase,W.speed?` (${W.speed})`:""]}),c.jsx("span",{children:W.total>0?`Loaded ${W.current} / ${W.total} tensors`:`${Math.round(W.progress)}%`})]}),c.jsx("div",{className:"model-progress-bar",children:c.jsx("div",{className:"model-progress-fill",style:{width:`${Math.min(100,Math.max(0,W.progress))}%`,transition:"width 0.25s ease"}})})]})]}),c.jsxs("div",{className:"m3-card",style:{marginTop:"24px"},children:[c.jsxs("h3",{className:"m3-card-title",children:[c.jsx(Xs,{size:18,style:{color:"var(--md-sys-color-primary)"}}),"Local Models (",H.length,")"]}),H.length===0?c.jsx("p",{style:{fontSize:"0.9rem",color:"var(--md-sys-color-outline)",textAlign:"center",padding:"16px 0"},children:"No models detected in app/models/. Download from the library below or import a file."}):c.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"10px"},children:H.map(D=>{const R=D.filename,F=f===R;return c.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"12px 16px",background:F?"var(--md-sys-color-primary-container)":"var(--md-sys-color-surface-variant)",color:F?"var(--md-sys-color-on-primary-container)":"var(--md-sys-color-on-surface-variant)",borderRadius:"var(--md-shape-corner-medium)",border:"1px solid var(--md-sys-color-outline-variant)"},children:[c.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"2px"},children:[c.jsx("span",{style:{fontWeight:600,fontSize:"0.95rem"},children:R}),c.jsxs("span",{style:{fontSize:"0.75rem",opacity:.8},children:["Local Weights File β€’ ",D.size||ii(D.sizeBytes)]})]}),c.jsx("div",{style:{display:"flex",gap:"8px"},children:F?c.jsxs("button",{className:"m3-btn m3-btn-error",style:{height:"36px",padding:"0 16px"},onClick:Et,disabled:me,children:[me?c.jsx(aa,{className:"progress-spinner",size:14}):c.jsx(rn,{size:14}),c.jsx("span",{children:me?"Unloading":"Unload"})]}):c.jsxs(c.Fragment,{children:[c.jsxs("button",{className:"m3-btn m3-btn-filled",style:{height:"36px",padding:"0 16px"},onClick:()=>Ze(R),disabled:ce,children:[L===R?c.jsx(aa,{className:"progress-spinner",size:14}):c.jsx(Xs,{size:14}),c.jsx("span",{children:L===R?"Loading":"Load"})]}),c.jsx("button",{className:"m3-btn m3-btn-error",style:{height:"36px",width:"36px",padding:0,minWidth:"36px"},onClick:()=>We(R),disabled:ce,title:"Delete from models folder",children:c.jsx(rn,{size:14})})]})})]},R)})})]}),c.jsx("div",{className:"workspace-title-section",style:{marginTop:"32px",marginBottom:"16px"},children:c.jsxs("h3",{className:"m3-card-title",children:[c.jsx(ry,{size:20,style:{color:"var(--md-sys-color-primary)"}}),"Model Library"]})}),c.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"16px"},children:ng.map(D=>c.jsxs("div",{className:"m3-card",style:{margin:0},children:[c.jsx("h4",{style:{fontWeight:700,marginBottom:"12px"},children:D.group}),c.jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fit, minmax(280px, 1fr))",gap:"12px"},children:D.items.map(R=>{const F=he.includes(R.filename),ae=h===R.filename;return c.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"10px",padding:"14px",background:"var(--md-sys-color-surface-variant)",border:"1px solid var(--md-sys-color-outline-variant)",borderRadius:"var(--md-shape-corner-medium)"},children:[c.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"4px"},children:[c.jsx("span",{style:{fontWeight:700},children:R.name}),c.jsxs("span",{style:{fontSize:"0.75rem",color:"var(--md-sys-color-outline)"},children:[R.format," β€’ approx. ",R.approxSize," β€’ ",R.resolution]})]}),c.jsx("p",{style:{fontSize:"0.78rem",color:"var(--md-sys-color-on-surface-variant)",lineHeight:1.35,margin:0},children:R.notes}),ae&&c.jsxs("div",{className:"model-progress-section",style:{marginTop:"2px"},children:[c.jsxs("div",{className:"model-progress-label",children:[c.jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:"8px"},children:[c.jsx("span",{className:"loading-dot"}),"Downloading"]}),c.jsx("span",{children:U>0?`${U}%`:"Preparing"})]}),c.jsx("div",{className:"model-progress-bar",children:c.jsx("div",{className:"model-progress-fill",style:{width:`${Math.min(100,Math.max(0,U))}%`,transition:"width 0.2s ease"}})}),fe&&c.jsx("div",{style:{fontSize:"0.7rem",color:"var(--md-sys-color-outline)",marginTop:"4px"},children:fe})]}),c.jsxs("button",{className:F?"m3-btn m3-btn-tonal":"m3-btn m3-btn-filled",style:{height:"36px",marginTop:"auto"},onClick:()=>ee(R),disabled:F||h!==null,children:[F?c.jsx(xu,{size:14}):ae?c.jsx(aa,{className:"progress-spinner",size:14}):c.jsx(am,{size:14}),c.jsx("span",{children:F?"Downloaded":ae?"Downloading":"Download"})]}),c.jsx("a",{href:R.url,target:"_blank",rel:"noreferrer",style:{alignSelf:"center",color:"var(--md-sys-color-primary)",fontSize:"0.75rem",fontWeight:600,lineHeight:1.2,textDecoration:"none"},children:"Save to Downloads folder"})]},R.filename)})})]},D.group))}),c.jsx("div",{className:"workspace-title-section",style:{marginTop:"32px",marginBottom:"16px"},children:c.jsxs("h3",{className:"m3-card-title",children:[c.jsx(im,{size:20,style:{color:"var(--md-sys-color-primary)"}}),"Local Model Import"]})}),de!==null&&c.jsxs("div",{className:"m3-card",style:{borderLeft:"4px solid var(--md-sys-color-primary)",marginTop:"24px"},children:[c.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",gap:"12px"},children:[c.jsxs("h4",{style:{fontWeight:600},children:["Copying Model to local folder: ",ke.filename]}),c.jsxs("button",{className:"m3-btn m3-btn-error",style:{height:"34px",padding:"0 14px"},onClick:Tt,children:[c.jsx(Qs,{size:14}),c.jsx("span",{children:"Stop Import"})]})]}),c.jsxs("div",{className:"model-progress-section",style:{margin:"12px 0 6px 0"},children:[c.jsxs("div",{className:"model-progress-label",children:[c.jsxs("span",{children:[ke.status," (",ke.speed," MB/s)"]}),c.jsxs("span",{children:[de,"% (",ke.eta,"s remaining)"]})]}),c.jsx("div",{className:"model-progress-bar",children:c.jsx("div",{className:"model-progress-fill",style:{width:`${de}%`,transition:"width 0.15s ease"}})})]})]}),c.jsxs("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fit, minmax(300px, 1fr))",gap:"16px",marginTop:"16px"},children:[c.jsxs("label",{className:"import-box",style:{margin:0,height:"100%",justifyContent:"center"},children:[c.jsx("input",{type:"file",style:{display:"none"},accept:".gguf,.safetensors,.ckpt",onChange:Z,disabled:de!==null}),c.jsx(im,{className:"import-icon"}),c.jsx("span",{style:{fontWeight:600},children:"Choose weights file"}),c.jsx("span",{style:{fontSize:"0.75rem",color:"var(--md-sys-color-outline)",textAlign:"center"},children:"Select `.gguf`, `.safetensors` or `.ckpt` weights."})]}),c.jsxs("div",{className:"m3-card",style:{display:"flex",flexDirection:"column",justifyContent:"center",margin:0,padding:"20px"},children:[c.jsxs("h4",{style:{fontWeight:600,marginBottom:"8px",display:"flex",alignItems:"center",gap:"8px"},children:[c.jsx(am,{size:16}),"Download Model from URL"]}),c.jsx("p",{style:{fontSize:"0.75rem",color:"var(--md-sys-color-outline)",marginBottom:"12px"},children:"Download any GGUF or Safetensors model from Hugging Face or other sites directly to your models folder."}),ie?c.jsxs("div",{className:"model-progress-section",style:{marginTop:"0px"},children:[c.jsxs("div",{className:"model-progress-label",children:[c.jsx("span",{style:{fontSize:"0.75rem",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis",maxWidth:"200px"},title:h,children:h}),c.jsxs("span",{children:[U,"%"]})]}),c.jsx("div",{className:"model-progress-bar",style:{margin:"4px 0"},children:c.jsx("div",{className:"model-progress-fill",style:{width:`${U}%`,transition:"width 0.15s ease"}})}),c.jsx("div",{style:{fontSize:"0.7rem",color:"var(--md-sys-color-outline)",marginTop:"2px"},children:fe}),c.jsxs("button",{className:"m3-btn m3-btn-error",style:{height:"34px",padding:"0 14px",marginTop:"10px"},onClick:se,children:[c.jsx(Qs,{size:14}),c.jsx("span",{children:"Stop Download"})]})]}):c.jsxs("form",{onSubmit:K,style:{display:"flex",gap:"8px"},children:[c.jsx("input",{type:"text",className:"m3-input",style:{flex:1,height:"40px"},placeholder:"e.g. https://huggingface.co/...",value:P,onChange:D=>ve(D.target.value),disabled:h!==null}),c.jsx("button",{type:"submit",className:"m3-btn m3-btn-filled",style:{height:"40px"},disabled:h!==null,children:"Download"})]})]})]}),Xe&&c.jsx("div",{style:{position:"fixed",inset:0,zIndex:50,background:"rgba(0,0,0,0.55)",display:"flex",alignItems:"center",justifyContent:"center",padding:"24px"},children:c.jsxs("div",{className:"m3-card",style:{maxWidth:"460px",width:"100%",margin:0,border:"1px solid var(--md-sys-color-outline-variant)"},children:[c.jsxs("div",{style:{display:"flex",alignItems:"center",gap:"10px",marginBottom:"10px"},children:[c.jsx(My,{size:22,style:{color:"var(--md-sys-color-error)"}}),c.jsx("h3",{style:{fontSize:"1.05rem",fontWeight:700},children:"Unload Current Model?"})]}),c.jsxs("p",{style:{fontSize:"0.9rem",color:"var(--md-sys-color-on-surface-variant)",lineHeight:1.45,marginBottom:"16px"},children:['"',f,'" is already loaded. Unload it before loading "',Xe,'".']}),c.jsxs("div",{style:{display:"flex",justifyContent:"flex-end",gap:"10px"},children:[c.jsx("button",{className:"m3-btn m3-btn-outlined",onClick:()=>_(null),disabled:me,children:"Cancel"}),c.jsxs("button",{className:"m3-btn m3-btn-error",onClick:Et,disabled:me,children:[c.jsx(rn,{size:14}),c.jsx("span",{children:"Unload Only"})]}),c.jsxs("button",{className:"m3-btn m3-btn-filled",onClick:gt,disabled:me,children:[me?c.jsx(aa,{className:"progress-spinner",size:14}):c.jsx(aa,{size:14}),c.jsx("span",{children:"Unload and Load"})]})]})]})})]})}const ug=O.memo(ig),sm=[{id:"1:1",label:"1:1 Square",width:512,height:512,sdxl_width:1024,sdxl_height:1024,desc:"Social posts & avatars"},{id:"4:3",label:"4:3 Photo",width:640,height:480,sdxl_width:1152,sdxl_height:864,desc:"Classic photo look"},{id:"16:9",label:"16:9 Landscape",width:768,height:432,sdxl_width:1216,sdxl_height:684,desc:"Widescreen landscape"},{id:"9:16",label:"9:16 Portrait",width:432,height:768,sdxl_width:684,sdxl_height:1216,desc:"Tall phone screen"}],cg=f=>{if(!f)return!0;const b=f.toLowerCase();return!(b.includes("flux")||b.includes("schnell")||b.includes("sdxl")||b.includes("lightning")||b.includes("turbo")||b.includes("sd3"))};function sg({constraints:f,setConstraints:b,activeModel:E,specs:r,backendOptions:x}){const N=E?cg(E):!1,G=x?.options?.length?x.options:[{id:"cpu",label:"CPU",available:!0}],H=(A,U)=>{b(V=>({...V,[A]:U}))},w=(A,U)=>{const V=U==="sdxl"&&!N,fe=sm.find(X=>X.id===A);if(fe){let X=V?fe.sdxl_width:fe.width,L=V?fe.sdxl_height:fe.height;N&&(X>L?(L=Math.round(L*512/X),X=512):(X=Math.round(X*512/L),L=512),X=Math.round(X/64)*64,L=Math.round(L/64)*64),H("width",X),H("height",L)}},h=A=>{b(U=>({...U,backendType:A,useGpu:A!=="cpu"}))};return c.jsxs("div",{className:"workspace-area",children:[c.jsxs("div",{className:"workspace-title-section",children:[c.jsx("h2",{className:"workspace-title",children:"Image Constraints"}),c.jsx("p",{className:"workspace-subtitle",children:"Configure size, quality, and performance controls for image generation."})]}),c.jsxs("div",{className:"generator-layout",children:[c.jsxs("div",{children:[c.jsxs("div",{className:"m3-card",children:[c.jsxs("h3",{className:"m3-card-title",children:[c.jsx(F0,{size:18,style:{color:"var(--md-sys-color-primary)"}}),"1. Picture Size & Shape"]}),N&&c.jsxs("div",{style:{background:"rgba(251, 191, 36, 0.1)",border:"1px solid rgb(251, 191, 36)",color:"var(--md-sys-color-on-surface)",padding:"12px",borderRadius:"8px",marginBottom:"16px",fontSize:"0.85rem",display:"flex",gap:"8px",alignItems:"flex-start"},children:[c.jsx(fy,{size:16,style:{color:"rgb(251, 191, 36)",flexShrink:0,marginTop:"2px"}}),c.jsxs("div",{children:[c.jsx("strong",{children:"Low-VRAM Protection Active:"})," Capped at 512x512 for the loaded SD 1.x model (",c.jsx("code",{children:E}),"). Generating at larger sizes (like 1024x1024) is disabled to prevent out-of-memory crashes on your ",r&&r.gpu_name&&!r.gpu_name.includes("Loading")?r.gpu_name:"graphics processor","."]})]}),c.jsxs("div",{className:"m3-field-group",children:[c.jsxs("div",{className:"m3-text-field",children:[c.jsx("label",{className:"m3-text-field-label",children:"AI Generation Engine Optimization"}),c.jsxs("div",{className:"m3-segmented-button",children:[N?c.jsx("div",{className:"m3-segment-item disabled",style:{opacity:.5,cursor:"not-allowed",textDecoration:"line-through",backgroundColor:"var(--md-sys-color-surface-variant)"},title:"High Quality Mode is disabled for SD 1.x models.",children:"High Quality Mode (1024px)"}):c.jsx("div",{className:`m3-segment-item ${f.width>=1024?"active":""}`,onClick:()=>{H("width",1024),H("height",1024)},children:"High Quality Mode (1024px)"}),c.jsx("div",{className:`m3-segment-item ${f.width<1024||N?"active":""}`,onClick:()=>{H("width",512),H("height",512)},children:"Standard Speed Mode (512px)"})]}),c.jsx("span",{style:{fontSize:"0.75rem",color:"var(--md-sys-color-outline)",marginTop:"4px"},children:N?"*Standard Speed Mode (512px) is forced for SD 1.5 / custom models to ensure stable local generations.":"*Use High Quality (1024px) for Flux or SDXL models. Use Standard (512px) for SD 1.5."})]}),c.jsxs("div",{className:"m3-text-field",children:[c.jsx("label",{className:"m3-text-field-label",children:"Select Shape (Aspect Ratio)"}),c.jsx("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"8px"},children:sm.map(A=>{const U=Math.abs(f.width/f.height-A.width/A.height)<.1;return c.jsxs("button",{className:`m3-btn m3-btn-outlined aspect-ratio-btn ${U?"active":""}`,style:{height:"86px",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",padding:"8px",borderRadius:"var(--md-shape-corner-medium)",borderColor:"var(--md-sys-color-outline-variant)"},onClick:()=>w(A.id,N?"sd15":f.width>=1024?"sdxl":"sd15"),children:[c.jsx("div",{className:`aspect-ratio-preview ratio-${A.id.replace(":","-")}`}),c.jsx("span",{style:{fontWeight:600,fontSize:"0.85rem",marginTop:"2px"},children:A.label}),c.jsx("span",{style:{fontSize:"0.68rem",opacity:.7},children:N?`Max: ${A.id==="1:1"?"512x512":A.id==="4:3"?"512x384":A.id==="16:9"?"512x288":"288x512"}`:A.desc})]},A.id)})})]})]})]}),c.jsxs("div",{className:"m3-card",children:[c.jsxs("h3",{className:"m3-card-title",children:[c.jsx(Ks,{size:18,style:{color:"var(--md-sys-color-primary)"}}),"2. Quality, Speed & Logic"]}),c.jsxs("div",{className:"m3-field-group",children:[c.jsxs("div",{className:"m3-slider-group",children:[c.jsxs("div",{className:"m3-slider-header",children:[c.jsx("span",{className:"m3-slider-label",children:"Detail Steps (Inference speed)"}),c.jsxs("span",{className:"m3-slider-value",children:[f.steps," steps"]})]}),c.jsx("input",{type:"range",className:"m3-slider",value:f.steps,onChange:A=>H("steps",parseInt(A.target.value)),min:"1",max:"60"}),c.jsx("span",{style:{fontSize:"0.75rem",color:"var(--md-sys-color-outline)",lineHeight:"1.3"},children:"The number of times the AI cleans up the image. More steps = sharper details, but takes longer."})]}),c.jsxs("div",{className:"m3-text-field",style:{marginTop:"8px"},children:[c.jsx("label",{className:"m3-text-field-label",children:"Image Blueprint (DNA Seed)"}),c.jsxs("div",{style:{display:"flex",gap:"8px"},children:[c.jsx("input",{type:"number",className:"m3-input",value:f.seed,onChange:A=>H("seed",parseInt(A.target.value)||-1),placeholder:"-1 for a brand new image...",style:{flex:1,height:"40px"}}),c.jsx("button",{className:"m3-btn m3-btn-tonal",onClick:()=>H("seed",-1),style:{height:"40px"},children:"Random (-1)"})]})]})]})]})]}),c.jsx("div",{children:c.jsxs("div",{className:"m3-card",children:[c.jsxs("h3",{className:"m3-card-title",children:[c.jsx(rm,{size:18,style:{color:"var(--md-sys-color-primary)"}}),"3. System Settings"]}),c.jsxs("div",{className:"m3-field-group",children:[c.jsxs("div",{className:"m3-text-field",children:[c.jsx("label",{className:"m3-text-field-label",children:"Generation Backend"}),c.jsx("div",{className:"m3-segmented-button",children:G.map(A=>c.jsx("div",{className:`m3-segment-item ${f.backendType===A.id||!f.backendType&&A.id==="cpu"?"active":""}`,onClick:()=>h(A.id),title:A.id==="cuda"?"CUDA appears only when an NVIDIA CUDA backend is available.":void 0,children:A.label},A.id))}),c.jsx("span",{style:{fontSize:"0.75rem",color:"var(--md-sys-color-outline)",marginTop:"4px",lineHeight:1.35},children:"CPU is slow but safest. Vulkan works on supported GPUs. CUDA is shown only when NVIDIA CUDA support is available."}),f.backendType==="cuda"&&r?.gpu_name&&String(r.gpu_name).toLowerCase().includes("gtx")&&c.jsxs("div",{style:{marginTop:"12px",padding:"10px 14px",background:"rgba(239, 68, 68, 0.08)",border:"1px dashed rgb(239, 68, 68)",borderRadius:"8px",fontSize:"0.75rem",color:"var(--md-sys-color-on-surface)",lineHeight:"1.45"},children:[c.jsx("strong",{children:"Performance Alert:"})," Your graphics card (",c.jsx("code",{children:r.gpu_name}),") is a GTX-series GPU which lacks hardware ",c.jsx("strong",{children:"Tensor Cores"}),". Running in CUDA mode will be up to 3x slower. We strongly recommend switching to ",c.jsx("strong",{children:"Vulkan GPU"})," for optimal generation speed."]}),x?.unavailable?.length>0&&c.jsx("div",{style:{marginTop:"8px",display:"flex",flexDirection:"column",gap:"4px"},children:x.unavailable.map(A=>c.jsxs("span",{style:{fontSize:"0.75rem",color:"var(--md-sys-color-error)",lineHeight:1.35},children:[A.label," unavailable: ",A.reason]},A.id))})]}),c.jsxs("div",{className:"m3-text-field",style:{marginTop:"20px"},children:[c.jsx("label",{className:"m3-text-field-label",children:"Memory Optimization (GPU VRAM)"}),c.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"14px",marginTop:"10px"},children:[c.jsxs("label",{style:{display:"flex",alignItems:"flex-start",gap:"10px",cursor:"pointer",fontSize:"0.85rem"},children:[c.jsx("input",{type:"checkbox",checked:f.vaeTiling!==!1,onChange:A=>H("vaeTiling",A.target.checked),style:{width:"16px",height:"16px",marginTop:"3px",accentColor:"var(--md-sys-color-primary)",cursor:"pointer"}}),c.jsxs("div",{children:[c.jsx("strong",{style:{color:"var(--md-sys-color-on-surface)"},children:"Enable VAE Tiling"}),c.jsx("div",{style:{fontSize:"0.75rem",color:"var(--md-sys-color-outline)",marginTop:"2px",lineHeight:1.35},children:"Processes VAE decoding in smaller tiles. Drastically reduces VRAM usage (from 2GB+ down to ~100MB) with no speed loss. Highly recommended for GPUs with 4GB-6GB VRAM."})]})]}),c.jsxs("label",{style:{display:"flex",alignItems:"flex-start",gap:"10px",cursor:"pointer",fontSize:"0.85rem"},children:[c.jsx("input",{type:"checkbox",checked:f.vaeOnCpu===!0,onChange:A=>H("vaeOnCpu",A.target.checked),style:{width:"16px",height:"16px",marginTop:"3px",accentColor:"var(--md-sys-color-primary)",cursor:"pointer"}}),c.jsxs("div",{children:[c.jsx("strong",{style:{color:"var(--md-sys-color-on-surface)"},children:"Run VAE on CPU"}),c.jsx("div",{style:{fontSize:"0.75rem",color:"var(--md-sys-color-outline)",marginTop:"2px",lineHeight:1.35},children:"Offloads the heavy VAE decoder computation from GPU VRAM to system memory (RAM). Saves ~2GB of VRAM, but makes the final decoding stage slightly slower."})]})]})]})]})]})]})})]})]})}const fg=O.memo(sg);function og(){const f=O.useRef(null),[b,E]=O.useState(null),r=O.useCallback(Z=>{const ie=f.current;f.current=null,E(null),ie&&ie(Z)},[]),x=O.useCallback(({title:Z="Confirm Action",message:ie,confirmLabel:D="OK",cancelLabel:R="Cancel",danger:F=!1})=>new Promise(ae=>{f.current=ae,E({type:"confirm",title:Z,message:ie,confirmLabel:D,cancelLabel:R,danger:F})}),[]),N=O.useCallback(({title:Z="Notice",message:ie,confirmLabel:D="OK",danger:R=!1})=>new Promise(F=>{f.current=F,E({type:"alert",title:Z,message:ie,confirmLabel:D,danger:R})}),[]),[G,H]=O.useState(()=>{const Z=localStorage.getItem("theme");return Z||(window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light")});O.useEffect(()=>{document.documentElement.setAttribute("data-theme",G),localStorage.setItem("theme",G)},[G]);const[w,h]=O.useState("generator"),[A,U]=O.useState(""),[V,fe]=O.useState(""),[X,L]=O.useState(null),[te,W]=O.useState(!1),[Oe,de]=O.useState(!1),[Ee,ke]=O.useState({options:[{id:"cpu",label:"CPU",available:!0}],cudaAvailable:!1,vulkanAvailable:!1,defaultBackendType:"cpu"}),[Ae,P]=O.useState(null),[ve,Ke]=O.useState([]),[yt,me]=O.useState(!1),[Je,st]=O.useState(!1),[$e,Xe]=O.useState([]),[_,C]=O.useState(!1),[J,he]=O.useState(0),[ce,d]=O.useState({os_name:"Loading Specs...",cpu_name:"Loading Specs...",cpu_cores_physical:4,cpu_cores_logical:8,ram_total_gb:0,gpu_name:"Loading Specs..."}),[j,B]=O.useState({cpu_usage:0,ram_used_gb:0,ram_total_gb:0,gpu_name:"Detecting...",vram_used_gb:0,vram_total_gb:0}),[q,K]=O.useState({width:1024,height:1024,steps:4,cfgScale:1,sampler:"euler_a",seed:-1,denoisingStrength:.7,useGpu:!0,useTaesd:!0,useFlashAttn:!0,useTiling:!1,vaeTiling:!0,vaeOnCpu:!1,threads:4,backendType:"auto"});O.useEffect(()=>{async function Z(){try{const ie=await qy(),D=await Yy();ke(D),d(ie),K(R=>({...R,threads:ie.cpu_cores_physical||4,backendType:R.backendType==="auto"?D.defaultBackendType||"cpu":R.backendType,useGpu:(R.backendType==="auto"?D.defaultBackendType:R.backendType)!=="cpu"}))}catch(ie){console.error("Error fetching hardware specs:",ie)}}Z()},[]);const ee=O.useCallback(async()=>{me(!0);try{const[Z,ie]=await Promise.all([By(),Hy().catch(()=>[])]);P(Z),Ke(ie)}finally{me(!1)}},[]);O.useEffect(()=>{ee()},[ee]);const se=O.useCallback(async()=>{try{const Z=await Ry();await navigator.clipboard.writeText(JSON.stringify(Z,null,2)),st(!0),setTimeout(()=>st(!1),1800)}catch(Z){N({title:"Diagnostics Failed",message:Z.message||String(Z),danger:!0})}},[N]),Ze=O.useCallback(async()=>{if(ve.length===0)return;const Z=ve.reduce((D,R)=>D+Number(R.sizeBytes||0),0);if(await x({title:"Clean Safe Files?",message:`Delete ${ve.length} temporary/cache item${ve.length===1?"":"s"} and free about ${ii(Z)}? - -Models and generated outputs are not included.`,confirmLabel:"Clean",danger:!0})){me(!0);try{await Gy(ve.map(D=>D.id)),await ee()}catch(D){N({title:"Cleanup Failed",message:D.message||String(D),danger:!0})}finally{me(!1)}}},[ve,ee,N,x]);O.useEffect(()=>{async function Z(){const ie=await Xy();Xe(ie)}Z()},[]),O.useEffect(()=>{X?localStorage.setItem("active-model",X):localStorage.removeItem("active-model")},[X]),O.useEffect(()=>{let Z=null;async function ie(){const D=await Cl();if(!!D.ready){Z&&clearInterval(Z);const F=localStorage.getItem("active-model");if(F)L(F),W(!0);else{try{if(D&&D.settings&&D.settings.model){const ae=D.settings.model.split(/[\\/]/).pop();L(ae)}}catch{}W(!0)}}else if(!(typeof window<"u"&&window.__TAURI_INTERNALS__!==void 0)){const ae=localStorage.getItem("active-model");ae&&(L(ae),W(!1))}}return ie(),Z=setInterval(ie,2e3),()=>{Z&&clearInterval(Z)}},[]),O.useEffect(()=>{async function Z(){try{const D=await Ly();B(R=>R.cpu_usage===D.cpu_usage&&R.ram_used_gb===D.ram_used_gb&&R.ram_total_gb===D.ram_total_gb&&R.gpu_name===D.gpu_name&&R.vram_used_gb===D.vram_used_gb&&R.vram_total_gb===D.vram_total_gb?R:D)}catch{}}Z();const ie=setInterval(Z,1500);return()=>clearInterval(ie)},[]),O.useEffect(()=>{if(X){const Z=X.toLowerCase();Z.includes("flux")||Z.includes("schnell")?K(ie=>({...ie,steps:4,cfgScale:1,width:1024,height:1024})):Z.includes("lightning")||Z.includes("turbo")?K(ie=>({...ie,steps:4,cfgScale:1.5,width:1024,height:1024})):Z.includes("sd15")?K(ie=>({...ie,steps:25,cfgScale:7,width:512,height:512})):Z.includes("sd35")?K(ie=>({...ie,steps:20,cfgScale:4.5,width:1024,height:1024})):K(ie=>({...ie,steps:20,cfgScale:7,width:512,height:512}))}},[X]);const Ne=O.useMemo(()=>{switch(w){case"generator":return c.jsx(ag,{prompt:A,setPrompt:U,negativePrompt:V,setNegativePrompt:fe,constraints:q,setConstraints:K,activeModel:X,generatedImages:$e,setGeneratedImages:Xe,isGenerating:_,setIsGenerating:C,generationProgress:J,setGenerationProgress:he,setActiveTab:h,showAlert:N,showConfirm:x});case"models":return c.jsx(ug,{activeModel:X,setActiveModel:L,serverRunning:te,setServerRunning:W,constraints:q,showAlert:N,showConfirm:x});case"constraints":return c.jsx(fg,{constraints:q,setConstraints:K,activeModel:X,specs:ce,backendOptions:Ee});default:return null}},[w,A,V,q,X,$e,_,J,te,ce,Ee,N,x]),gt=O.useMemo(()=>c.jsx(Ny,{activeTab:w,setActiveTab:h,specs:ce}),[w,ce]),Et=O.useCallback(async()=>{if(!(!te||Oe)){de(!0);try{await Js(),W(!1),L(null)}catch(Z){console.error("Failed to stop server:",Z),N({title:"Stop Server Failed",message:Z.message||String(Z),danger:!0})}finally{de(!1)}}},[te,Oe,N]),We=[...Ae?.stale?["Restart Local AI Image Generator so the local server loads the latest API."]:[],...Ae?.issues||[]],Ht=ve.reduce((Z,ie)=>Z+Number(ie.sizeBytes||0),0),Tt=!!(Ae&&(We.length>0||ve.length>0));return c.jsxs("div",{className:"app-container",children:[gt,c.jsxs("div",{className:"main-content",children:[c.jsx(Dy,{telemetry:j,serverRunning:te,activeModel:X,onStopServer:Et,isStoppingServer:Oe,theme:G,setTheme:H}),Tt&&c.jsxs("div",{className:`m3-card readiness-card ${Ae?.stale||We.length>0?"readiness-card-warning":""}`,children:[c.jsxs("div",{className:"readiness-header",children:[c.jsxs("div",{children:[c.jsx("h3",{className:"m3-card-title",style:{marginBottom:"4px"},children:Ae?.stale?"Restart Required":We.length>0?"System Readiness":"Safe Cleanup Available"}),c.jsx("p",{className:"m3-card-subtitle",style:{margin:0},children:Ae?.stale?`Running server build: ${Ae.build||"unknown"}`:We.length>0?"Local AI Image Generator found setup items that may need attention.":`${ve.length} temporary item${ve.length===1?"":"s"} can be cleaned (${ii(Ht)}).`})]}),c.jsxs("div",{className:"readiness-actions",children:[c.jsx("button",{className:"m3-btn m3-btn-outlined",onClick:ee,disabled:yt,children:yt?"Checking":"Refresh"}),c.jsx("button",{className:"m3-btn m3-btn-tonal",onClick:se,children:Je?"Copied":"Copy Diagnostics"}),ve.length>0&&c.jsxs("button",{className:"m3-btn m3-btn-error",onClick:Ze,disabled:yt,children:["Clean ",ii(Ht)]})]})]}),We.length>0&&c.jsx("div",{className:"readiness-issues",children:We.slice(0,4).map(Z=>c.jsx("span",{children:Z},Z))}),ve.length>0&&c.jsx("div",{className:"readiness-cleanup-list",children:ve.slice(0,3).map(Z=>c.jsxs("span",{title:Z.path,children:[Z.name," Β· ",Z.size," Β· ",Z.reason]},Z.id))})]}),Ne]}),b&&c.jsx("div",{className:"app-dialog-backdrop",role:"presentation",children:c.jsxs("div",{className:"app-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"app-dialog-title",children:[c.jsx("h3",{id:"app-dialog-title",className:"app-dialog-title",children:b.title}),c.jsx("p",{className:"app-dialog-message",children:b.message}),c.jsxs("div",{className:"app-dialog-actions",children:[b.type==="confirm"&&c.jsx("button",{className:"m3-btn m3-btn-outlined",onClick:()=>r(!1),children:b.cancelLabel}),c.jsx("button",{className:`m3-btn ${b.danger?"m3-btn-error":"m3-btn-filled"}`,onClick:()=>r(!0),children:b.confirmLabel})]})]})})]})}G0.createRoot(document.getElementById("root")).render(c.jsx(D0.StrictMode,{children:c.jsx(og,{})}));export{ja as i,rg as t}; diff --git a/app/dist/index.html b/app/dist/index.html deleted file mode 100644 index 26edf65..0000000 --- a/app/dist/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - Local AI Image Generator - - - - - -
- - diff --git a/app/frontend/.agents/task.md b/app/frontend/.agents/task.md new file mode 100644 index 0000000..9d32aed --- /dev/null +++ b/app/frontend/.agents/task.md @@ -0,0 +1,13 @@ +- [x] Rearrange composer JSX in `TextChat.jsx` to stack the textarea at the top and the action buttons in a separate toolbar at the bottom. +- [x] Refactor composer styles in `App.css` to align with the vertical stack layout, ensuring full-width textarea and proper spacing. +- [x] Remove obsolete `margin-bottom: 2px` properties from action buttons and center them vertically in the toolbar. +- [x] Implement sidebar visibility toggle state in `App.jsx` and persist state to `localStorage`. +- [x] Add sidebar collapse transitions and `.sidebar.collapsed` styles in `App.css`. +- [x] Integrate left sidebar toggle button in `TopStatusBar.jsx` using `PanelLeft`/`PanelLeftClose` Lucide icons. +- [x] Set AI/assistant chat bubble wrap max-width to `80%` in `App.css` to improve text readability and prevent wide stretching. +- [x] Implement global font size state in `App.jsx` and synchronize it to a CSS custom variable `--app-font-size` on the html element. +- [x] Refactor body font-size to use relative `1rem` in `App.css` to enable page-wide scaling. +- [x] Add a Font Size range slider to the "Appearance & Themes" section in `Settings.jsx` to adjust text size from 12px to 24px. +- [x] Fix dropdown overlay menu readability issues in `App.css` by using a solid color-mix base (blending transparent surface with solid background) and forcing strong `backdrop-filter: blur(20px)` to prevent underlying text/icon bleeding. +- [x] Merge "Color Themes" and "Text & Interface Size" subsections in `Settings.jsx` into a single unified card container separated by a horizontal divider line. +- [x] Test production build using `npm run build` to verify there are no compilation or syntax errors. diff --git a/app/frontend/index.html b/app/frontend/index.html index aade2a9..4b2d673 100644 --- a/app/frontend/index.html +++ b/app/frontend/index.html @@ -7,7 +7,7 @@ - Local AI Image Generator + Uncensored AI Studio diff --git a/app/frontend/package-lock.json b/app/frontend/package-lock.json index ed289ac..fd395ab 100644 --- a/app/frontend/package-lock.json +++ b/app/frontend/package-lock.json @@ -21,21 +21,21 @@ } }, "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, "license": "MIT", "optional": true, @@ -44,9 +44,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, "license": "MIT", "optional": true, @@ -55,14 +55,14 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", - "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.2" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", @@ -74,9 +74,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "dev": true, "license": "MIT", "funding": { @@ -84,9 +84,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", - "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", + "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", "cpu": [ "arm64" ], @@ -101,9 +101,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", - "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", + "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", "cpu": [ "arm64" ], @@ -118,9 +118,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", - "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", + "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", "cpu": [ "x64" ], @@ -135,9 +135,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", - "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", + "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", "cpu": [ "x64" ], @@ -152,9 +152,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", - "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", + "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", "cpu": [ "arm" ], @@ -169,9 +169,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", - "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", + "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", "cpu": [ "arm64" ], @@ -186,9 +186,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", - "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", + "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", "cpu": [ "arm64" ], @@ -203,9 +203,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", - "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", + "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", "cpu": [ "ppc64" ], @@ -220,9 +220,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", - "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", + "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", "cpu": [ "s390x" ], @@ -237,9 +237,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", - "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", + "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", "cpu": [ "x64" ], @@ -254,9 +254,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", - "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", + "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", "cpu": [ "x64" ], @@ -271,9 +271,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", - "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", + "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", "cpu": [ "arm64" ], @@ -288,9 +288,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", - "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", + "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", "cpu": [ "wasm32" ], @@ -298,18 +298,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", - "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", + "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", "cpu": [ "arm64" ], @@ -324,9 +324,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", - "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz", + "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==", "cpu": [ "x64" ], @@ -348,9 +348,9 @@ "license": "MIT" }, "node_modules/@tauri-apps/api": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.0.tgz", - "integrity": "sha512-7CinYODhky9lmO23xHnUFv0Xt43fbtWMyxZcLcRBlFkcgXKuEirBvHpmtJ89YMhyeGcq20Wuc47Fa4XjyniywA==", + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.1.tgz", + "integrity": "sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA==", "license": "Apache-2.0 OR MIT", "funding": { "type": "opencollective", @@ -358,9 +358,9 @@ } }, "node_modules/@tauri-apps/cli": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.2.tgz", - "integrity": "sha512-bk3HemqvGRoy+5D/dVMUQHKMYLglD0jVnMm/0iGMH6ufZ+p8r14m6BpIixwij3PBvZdvORUp1YifTD8QxVZ1Nw==", + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.3.tgz", + "integrity": "sha512-EElQe8z8uD7Pi5++tJ/UfEwWuK08rd3oCDYdeIbJAb6pZRrxlqmoF5gh5H5YvzmUPhS4IRCaLSsQhvWkrfK+GQ==", "dev": true, "license": "Apache-2.0 OR MIT", "bin": { @@ -374,23 +374,23 @@ "url": "https://opencollective.com/tauri" }, "optionalDependencies": { - "@tauri-apps/cli-darwin-arm64": "2.11.2", - "@tauri-apps/cli-darwin-x64": "2.11.2", - "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.2", - "@tauri-apps/cli-linux-arm64-gnu": "2.11.2", - "@tauri-apps/cli-linux-arm64-musl": "2.11.2", - "@tauri-apps/cli-linux-riscv64-gnu": "2.11.2", - "@tauri-apps/cli-linux-x64-gnu": "2.11.2", - "@tauri-apps/cli-linux-x64-musl": "2.11.2", - "@tauri-apps/cli-win32-arm64-msvc": "2.11.2", - "@tauri-apps/cli-win32-ia32-msvc": "2.11.2", - "@tauri-apps/cli-win32-x64-msvc": "2.11.2" + "@tauri-apps/cli-darwin-arm64": "2.11.3", + "@tauri-apps/cli-darwin-x64": "2.11.3", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.3", + "@tauri-apps/cli-linux-arm64-gnu": "2.11.3", + "@tauri-apps/cli-linux-arm64-musl": "2.11.3", + "@tauri-apps/cli-linux-riscv64-gnu": "2.11.3", + "@tauri-apps/cli-linux-x64-gnu": "2.11.3", + "@tauri-apps/cli-linux-x64-musl": "2.11.3", + "@tauri-apps/cli-win32-arm64-msvc": "2.11.3", + "@tauri-apps/cli-win32-ia32-msvc": "2.11.3", + "@tauri-apps/cli-win32-x64-msvc": "2.11.3" } }, "node_modules/@tauri-apps/cli-darwin-arm64": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.2.tgz", - "integrity": "sha512-+4UZzLt+eOAEQCwgd+TqKgyUJMrvx+BgdXLLaqJYmPqzP+nE6YZr/hY6CWLYGQb8jFn99jEkmC6uA3tNvamA1w==", + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.3.tgz", + "integrity": "sha512-BxpaM8bsCoXs3wd4WKYhas/G1gs7+r7B+e4WnyRk2GEoVOouJB1hoL6E6YLXZDXbYci6VFdrNnobQwd2uVL4ew==", "cpu": [ "arm64" ], @@ -405,9 +405,9 @@ } }, "node_modules/@tauri-apps/cli-darwin-x64": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.2.tgz", - "integrity": "sha512-VjYYtZUPqDMLutSfJEyxFE3Bz+DPi7c8wC3imckgvciLDZLq4qwKJxBicg0BXGhXjJsl8vKWgWRFNMPELQ+Xyg==", + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.3.tgz", + "integrity": "sha512-DbZYuPB1ZEzcAHYeyCvo3ltzM27+aXwPloCrtexPnmgPgulYJm3TOq6aC4S+wPhSXteddg8zImtNkvx/gQzmwg==", "cpu": [ "x64" ], @@ -422,9 +422,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.2.tgz", - "integrity": "sha512-yMemD6f4i95AQriS8EazyOFzbE34yjnP16i3IOzpHGQvBoy2DjypFMFBq0NtPuITURv/cOGguRtHR5d79/9CSA==", + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.3.tgz", + "integrity": "sha512-741NduqBmz1XkdU8yz3OI/kBZtqHbvxo9F9ytIeWYU69/Ba9dcZEbqOU++Dp0G/XU8vAI0TfTywEl+p+BbLvaA==", "cpu": [ "arm" ], @@ -439,9 +439,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-gnu": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.2.tgz", - "integrity": "sha512-cgI91D2wL8GSgoWwZXDqt+DwnuZCP2/bz03QAE4TrhgAKIsrB4hX26W/H1EONPUUNkqrsgeCD0wU6pcNjV/5kw==", + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.3.tgz", + "integrity": "sha512-RWAXT8pTqIczXcoic+LXlo6uEbAXGB0cgh6Pg7Y9xVnEbzryQ1JHtRGj9SxzrKSemBIDBH6Qc24kK2G69i8ofA==", "cpu": [ "arm64" ], @@ -456,9 +456,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-musl": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.2.tgz", - "integrity": "sha512-X1rm0BERqAAggtYTESSgXrS3sz4Sb/OiPiz54UqISlXW+GkR3vNIGnsy/lejNmoXGVqri3Q53BCfQiclOIyRPw==", + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.3.tgz", + "integrity": "sha512-qomqYS+yAkd0gXMRmhguWXc7RfVN+XKKXaEwbf5QmKURwydLFOTldd6F8/WoZDSsBMrV8dpNxz0YneGLmobiSA==", "cpu": [ "arm64" ], @@ -473,9 +473,9 @@ } }, "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.2.tgz", - "integrity": "sha512-usbMLJbT3KtkOrBMDVeGYNM35aTHXx38SJSzTMSqqjeUIOQ+iVPjb2yAGNAE+KqmBbAx4FOFIyMeKXx2M/JKGQ==", + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.3.tgz", + "integrity": "sha512-jOCXbDqeDj5XcclsOBAaXjtTgwZCVg8zEZ+dbPUCoADOgljFgL0rOkYTc96vUYgOrYEfuHYihWMxIDGaD6GwJw==", "cpu": [ "riscv64" ], @@ -490,9 +490,9 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-gnu": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.2.tgz", - "integrity": "sha512-Ru4gwJKPG0ctVGchRGpRup4Y4lW2SSfFnrbQcyHhCliKy4g8Qz97TrUgCur4CbWyAgKxvGh3SjrkA0LDYzDGiw==", + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.3.tgz", + "integrity": "sha512-+u3HO/F3gHwL48t9gWN/urqZvpaEJzBFmTaq5eSIhvy8TOvnhb+LgJr3Q3BG+5JxuBrCUjqtOEz6gMttdJFSBA==", "cpu": [ "x64" ], @@ -507,9 +507,9 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-musl": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.2.tgz", - "integrity": "sha512-eUm7T6clN1MMmNSRQ9gaWsQdyehQx2Gmn5hht/QUlqZQI/qcP2OJK5dnaxqwFzCr2HdsEo9ydxaqcS1oJzMvUw==", + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.3.tgz", + "integrity": "sha512-spr5Jpr6KF/vehkLwJ0YmdGv8QwpWU+uw7J8bgijO0sox6ZCYsSNMbcsQjTqPi4xl+p0woIYpWXgChgHYpAc8g==", "cpu": [ "x64" ], @@ -524,9 +524,9 @@ } }, "node_modules/@tauri-apps/cli-win32-arm64-msvc": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.2.tgz", - "integrity": "sha512-HeeZW80jU+gVTOEX4X/hC6NVSAdDVXajwP5fxIZ/3z9WvUC7qrudX2GMTilYq6Dg0e0sk0XgsAJD1hZ5wPBXUA==", + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.3.tgz", + "integrity": "sha512-abkoRQih5xBa3vz2spWaex0kP/MzVzVPQHom2f8jnCq46R/luOD6Uy85EMU9/bfzf6ZzdorWJsgO+OMX90Fx2w==", "cpu": [ "arm64" ], @@ -541,9 +541,9 @@ } }, "node_modules/@tauri-apps/cli-win32-ia32-msvc": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.2.tgz", - "integrity": "sha512-YhjQNZcXfbkCLyazSv1nPnJ9iRFE1wm6kc51FDbU10/Dk09io+6PAGMLjkxnX2GdM0qMnDmTjstY8mTDVvtKeA==", + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.3.tgz", + "integrity": "sha512-Vy6AvzFm1G40hg3r+OYDB3jkuu7R4wnMzbQBKuun9v6Cgg8IierpLL7toMzrZKs/8NlG8Sg4x1iLFR52oknyHg==", "cpu": [ "ia32" ], @@ -558,9 +558,9 @@ } }, "node_modules/@tauri-apps/cli-win32-x64-msvc": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.2.tgz", - "integrity": "sha512-d2JchlFIpZevZVReyqhQOekJmb1UH3rhZ5VX6sH3ty9ETE0TKQavpihvoScUXfKKpW6HZC0MrFGRU0ZtD+w3gA==", + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.3.tgz", + "integrity": "sha512-GlciF75GdbseajOyib2aCHwE3BXIqZ1liGKWLFRvCdN5wm8h8hFssEVKQ/6E+2jsMLg9v7LCTb983YFnn0QSww==", "cpu": [ "x64" ], @@ -584,9 +584,9 @@ } }, "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "license": "MIT", "optional": true, @@ -595,13 +595,13 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz", - "integrity": "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.3.tgz", + "integrity": "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "^1.0.0" + "@rolldown/pluginutils": "^1.0.1" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -925,18 +925,18 @@ } }, "node_modules/lucide-react": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.17.0.tgz", - "integrity": "sha512-9FA9evdox/JQL5PT57fdA1x/yg8T7knJ98+zjTL3UfKza6pflQUUh3XtaQIHKvnsJw1lmsEyHVlt5jchYxOQ5w==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.21.0.tgz", + "integrity": "sha512-reEZMXq8Qdd5jg5XYkQ5TR1fB/GiQ7ih4vcrthYDtgjSDwh0i6/YLiGjsWsIwgN49gpAnd4J2elSNzncMEEUUQ==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", "dev": true, "funding": [ { @@ -1023,13 +1023,13 @@ } }, "node_modules/rolldown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", - "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz", + "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.133.0", + "@oxc-project/types": "=0.137.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -1039,21 +1039,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" + "@rolldown/binding-android-arm64": "1.1.3", + "@rolldown/binding-darwin-arm64": "1.1.3", + "@rolldown/binding-darwin-x64": "1.1.3", + "@rolldown/binding-freebsd-x64": "1.1.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", + "@rolldown/binding-linux-arm64-gnu": "1.1.3", + "@rolldown/binding-linux-arm64-musl": "1.1.3", + "@rolldown/binding-linux-ppc64-gnu": "1.1.3", + "@rolldown/binding-linux-s390x-gnu": "1.1.3", + "@rolldown/binding-linux-x64-gnu": "1.1.3", + "@rolldown/binding-linux-x64-musl": "1.1.3", + "@rolldown/binding-openharmony-arm64": "1.1.3", + "@rolldown/binding-wasm32-wasi": "1.1.3", + "@rolldown/binding-win32-arm64-msvc": "1.1.3", + "@rolldown/binding-win32-x64-msvc": "1.1.3" } }, "node_modules/scheduler": { @@ -1098,16 +1098,16 @@ "optional": true }, "node_modules/vite": { - "version": "8.0.16", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", - "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.0.tgz", + "integrity": "sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", - "rolldown": "1.0.3", + "rolldown": "~1.1.2", "tinyglobby": "^0.2.17" }, "bin": { @@ -1124,7 +1124,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", + "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", diff --git a/app/frontend/src/App.css b/app/frontend/src/App.css index e592830..0527bee 100644 --- a/app/frontend/src/App.css +++ b/app/frontend/src/App.css @@ -1,5 +1,6 @@ /* Modern Minimalist & Glassmorphism Design System */ :root { + --app-font-size: 16px; font-family: 'Outfit', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-synthesis: none; text-rendering: optimizeLegibility; @@ -50,6 +51,10 @@ --glass-blur: blur(16px); } +html { + font-size: var(--app-font-size); +} + /* Dark Theme Variables */ html[data-theme="dark"] { --md-sys-color-primary: #6366f1; @@ -86,6 +91,289 @@ html[data-theme="dark"] { --glass-blur: blur(16px); } +/* Nord Light Theme */ +html[data-theme="light-nord"] { + --md-sys-color-primary: #5e81ac; + --md-sys-color-on-primary: #ffffff; + --md-sys-color-primary-container: rgba(94, 129, 172, 0.08); + --md-sys-color-on-primary-container: #4c566a; + --md-sys-color-secondary: #88c0d0; + --md-sys-color-on-secondary: #2e3440; + --md-sys-color-secondary-container: rgba(136, 192, 208, 0.08); + --md-sys-color-on-secondary-container: #3b4252; + --md-sys-color-tertiary: #b48ead; + --md-sys-color-on-tertiary: #ffffff; + --md-sys-color-background: #eceff4; + --md-sys-color-on-background: #2e3440; + --md-sys-color-surface: rgba(242, 244, 248, 0.85); + --md-sys-color-on-surface: #2e3440; + --md-sys-color-surface-variant: rgba(229, 233, 240, 0.85); + --md-sys-color-on-surface-variant: #4c566a; + --md-sys-color-outline: #4c566a; + --md-sys-color-outline-variant: #d8dee9; + --border-color: rgba(46, 52, 64, 0.09); + --border-color-muted: rgba(46, 52, 64, 0.05); + --md-sys-color-error: #bf616a; + --md-sys-color-on-error: #ffffff; + --md-sys-color-success: #a3be8c; + --md-sys-color-on-success: #2e3440; + --md-sys-color-card-border: rgba(46, 52, 64, 0.06); + --glass-shadow: 0 8px 32px 0 rgba(46, 52, 64, 0.03); + --glass-blur: blur(16px); +} + +/* Sakura Blossom Light Theme */ +html[data-theme="light-sakura"] { + --md-sys-color-primary: #d05a74; + --md-sys-color-on-primary: #ffffff; + --md-sys-color-primary-container: rgba(208, 90, 116, 0.06); + --md-sys-color-on-primary-container: #8c354a; + --md-sys-color-secondary: #e597a7; + --md-sys-color-on-secondary: #ffffff; + --md-sys-color-secondary-container: rgba(229, 151, 167, 0.08); + --md-sys-color-on-secondary-container: #8c354a; + --md-sys-color-tertiary: #a278b5; + --md-sys-color-on-tertiary: #ffffff; + --md-sys-color-background: #fdf6f6; + --md-sys-color-on-background: #5c2a39; + --md-sys-color-surface: rgba(255, 250, 250, 0.85); + --md-sys-color-on-surface: #5c2a39; + --md-sys-color-surface-variant: rgba(250, 235, 235, 0.9); + --md-sys-color-on-surface-variant: #8c5264; + --md-sys-color-outline: #8c5264; + --md-sys-color-outline-variant: #dbb2bc; + --border-color: rgba(92, 42, 57, 0.08); + --border-color-muted: rgba(92, 42, 57, 0.04); + --md-sys-color-error: #e05c5c; + --md-sys-color-on-error: #ffffff; + --md-sys-color-success: #5cbe8a; + --md-sys-color-on-success: #ffffff; + --md-sys-color-card-border: rgba(92, 42, 57, 0.06); + --glass-shadow: 0 8px 32px 0 rgba(92, 42, 57, 0.03); + --glass-blur: blur(16px); +} + +/* Cyberpunk Neon Dark Theme */ +html[data-theme="dark-cyberpunk"] { + --md-sys-color-primary: #ff007f; + --md-sys-color-on-primary: #ffffff; + --md-sys-color-primary-container: rgba(255, 0, 127, 0.15); + --md-sys-color-on-primary-container: #ff9ebb; + --md-sys-color-secondary: #00f0ff; + --md-sys-color-on-secondary: #0d0714; + --md-sys-color-secondary-container: rgba(0, 240, 255, 0.1); + --md-sys-color-on-secondary-container: #a6f5ff; + --md-sys-color-tertiary: #9d4edd; + --md-sys-color-on-tertiary: #ffffff; + --md-sys-color-background: #0d0714; + --md-sys-color-on-background: #e0e0ff; + --md-sys-color-surface: rgba(26, 12, 38, 0.7); + --md-sys-color-on-surface: #e0e0ff; + --md-sys-color-surface-variant: rgba(37, 18, 54, 0.6); + --md-sys-color-on-surface-variant: #b5a8cc; + --md-sys-color-outline: #00f0ff; + --md-sys-color-outline-variant: #ff007f; + --border-color: rgba(0, 240, 255, 0.2); + --border-color-muted: rgba(255, 0, 127, 0.15); + --md-sys-color-error: #ff3333; + --md-sys-color-on-error: #ffffff; + --md-sys-color-success: #00ff66; + --md-sys-color-on-success: #0d0714; + --md-sys-color-card-border: rgba(0, 240, 255, 0.1); + --glass-shadow: 0 8px 32px 0 rgba(255, 0, 127, 0.15); + --glass-blur: blur(16px); +} + +/* Emerald Forest Dark Theme */ +html[data-theme="dark-forest"] { + --md-sys-color-primary: #10b981; + --md-sys-color-on-primary: #040a09; + --md-sys-color-primary-container: rgba(16, 185, 129, 0.12); + --md-sys-color-on-primary-container: #a7f3d0; + --md-sys-color-secondary: #34d399; + --md-sys-color-on-secondary: #040a09; + --md-sys-color-secondary-container: rgba(52, 211, 153, 0.08); + --md-sys-color-on-secondary-container: #d1fae5; + --md-sys-color-tertiary: #6ee7b7; + --md-sys-color-on-tertiary: #040a09; + --md-sys-color-background: #060f0e; + --md-sys-color-on-background: #e2ebe9; + --md-sys-color-surface: rgba(13, 28, 25, 0.65); + --md-sys-color-on-surface: #e2ebe9; + --md-sys-color-surface-variant: rgba(20, 41, 37, 0.55); + --md-sys-color-on-surface-variant: #94a8a4; + --md-sys-color-outline: #86efac; + --md-sys-color-outline-variant: #15803d; + --border-color: rgba(52, 211, 153, 0.15); + --border-color-muted: rgba(52, 211, 153, 0.08); + --md-sys-color-error: #f87171; + --md-sys-color-on-error: #ffffff; + --md-sys-color-success: #34d399; + --md-sys-color-on-success: #040a09; + --md-sys-color-card-border: rgba(52, 211, 153, 0.06); + --glass-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.4); + --glass-blur: blur(16px); +} + +/* Dracula Classic Dark Theme */ +html[data-theme="dark-dracula"] { + --md-sys-color-primary: #bd93f9; + --md-sys-color-on-primary: #282a36; + --md-sys-color-primary-container: rgba(189, 147, 249, 0.15); + --md-sys-color-on-primary-container: #d6bbfb; + --md-sys-color-secondary: #ff79c6; + --md-sys-color-on-secondary: #282a36; + --md-sys-color-secondary-container: rgba(255, 121, 198, 0.1); + --md-sys-color-on-secondary-container: #ffb3db; + --md-sys-color-tertiary: #50fa7b; + --md-sys-color-on-tertiary: #282a36; + --md-sys-color-background: #282a36; + --md-sys-color-on-background: #f8f8f2; + --md-sys-color-surface: rgba(33, 34, 44, 0.7); + --md-sys-color-on-surface: #f8f8f2; + --md-sys-color-surface-variant: rgba(40, 42, 54, 0.6); + --md-sys-color-on-surface-variant: #6272a4; + --md-sys-color-outline: #ff79c6; + --md-sys-color-outline-variant: #6272a4; + --border-color: rgba(189, 147, 249, 0.18); + --border-color-muted: rgba(255, 121, 198, 0.1); + --md-sys-color-error: #ff5555; + --md-sys-color-on-error: #ffffff; + --md-sys-color-success: #50fa7b; + --md-sys-color-on-success: #282a36; + --md-sys-color-card-border: rgba(189, 147, 249, 0.08); + --glass-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.35); + --glass-blur: blur(16px); +} + +/* OLED Pitch Black Theme */ +html[data-theme="dark-oled"] { + --md-sys-color-primary: #3b82f6; + --md-sys-color-on-primary: #ffffff; + --md-sys-color-primary-container: rgba(59, 130, 246, 0.15); + --md-sys-color-on-primary-container: #93c5fd; + --md-sys-color-secondary: #60a5fa; + --md-sys-color-on-secondary: #000000; + --md-sys-color-secondary-container: rgba(96, 165, 250, 0.1); + --md-sys-color-on-secondary-container: #bfdbfe; + --md-sys-color-tertiary: #38bdf8; + --md-sys-color-on-tertiary: #000000; + --md-sys-color-background: #000000; + --md-sys-color-on-background: #ffffff; + --md-sys-color-surface: rgba(10, 10, 10, 0.95); + --md-sys-color-on-surface: #ffffff; + --md-sys-color-surface-variant: rgba(20, 20, 20, 0.98); + --md-sys-color-on-surface-variant: #cccccc; + --md-sys-color-outline: #888888; + --md-sys-color-outline-variant: #444444; + --border-color: rgba(255, 255, 255, 0.15); + --border-color-muted: rgba(255, 255, 255, 0.08); + --md-sys-color-error: #ef4444; + --md-sys-color-on-error: #ffffff; + --md-sys-color-success: #10b981; + --md-sys-color-on-success: #ffffff; + --md-sys-color-card-border: rgba(255, 255, 255, 0.1); + --glass-shadow: 0 0 0 1px rgba(255, 255, 255, 0.1); + --glass-blur: none; +} + +html[data-theme="dark-oled"] body { + background-image: none !important; +} + +/* Theme Dropdown & Selection Styling */ +.theme-dropdown-menu { + position: absolute; + top: 40px; + right: 0; + width: 190px; + background: color-mix(in srgb, var(--md-sys-color-surface) 40%, var(--md-sys-color-background)); + backdrop-filter: blur(20px) !important; + -webkit-backdrop-filter: blur(20px) !important; + border: 1px solid var(--border-color); + border-radius: var(--md-shape-corner-medium); + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.25); + padding: 6px; + display: flex; + flex-direction: column; + gap: 2px; + z-index: 9999; + animation: theme-dropdown-fade 0.2s cubic-bezier(0.4, 0, 0.2, 1); +} + +@keyframes theme-dropdown-fade { + from { + opacity: 0; + transform: translateY(-8px) scale(0.95); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +.theme-dropdown-header { + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--md-sys-color-outline); + padding: 6px 10px 4px; +} + +.theme-dropdown-divider { + height: 1px; + background: var(--border-color-muted); + margin: 4px 0; +} + +.theme-dropdown-item { + background: transparent; + border: none; + border-radius: calc(var(--md-shape-corner-medium) - 4px); + padding: 8px 10px; + font-family: inherit; + font-size: 0.8rem; + font-weight: 500; + color: var(--md-sys-color-on-surface-variant); + cursor: pointer; + display: flex; + align-items: center; + text-align: left; + transition: all 0.15s ease; + width: 100%; +} + +.theme-dropdown-item:hover { + background: var(--md-sys-color-primary-container); + color: var(--md-sys-color-on-primary-container); +} + +.theme-dropdown-item.active { + background: var(--md-sys-color-secondary-container); + color: var(--md-sys-color-on-secondary-container); + font-weight: 600; +} + +.theme-card-btn { + transform: translateY(0); + transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease !important; +} + +.theme-card-btn:hover { + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08) !important; + border-color: var(--md-sys-color-outline) !important; +} + +.theme-card-btn.active:hover { + border-color: var(--md-sys-color-primary) !important; +} + +.theme-card-btn.active { + box-shadow: 0 4px 16px color-mix(in srgb, var(--md-sys-color-primary) 25%, transparent) !important; +} + /* Global Reset & Base Styles */ * { box-sizing: border-box; @@ -102,7 +390,7 @@ body { overflow: hidden; height: 100vh; width: 100vw; - font-size: 16px; + font-size: 1rem; transition: background-color var(--md-transition-duration) ease; } @@ -131,6 +419,16 @@ body { transition: all var(--md-transition-duration) var(--md-transition-easing); } +.sidebar.collapsed { + width: 0 !important; + padding-left: 0 !important; + padding-right: 0 !important; + border-right-color: transparent !important; + opacity: 0; + pointer-events: none; + overflow: hidden; +} + .sidebar-logo { display: flex; align-items: center; @@ -219,6 +517,874 @@ body { overflow: hidden; } +.text-chat-layout { + flex: 1; + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 0px; + padding: 10px 12px; + min-height: 0; +} + +.text-chat-main, +.text-chat-settings { + min-height: 0; +} + +.text-chat-main { + display: flex; + flex-direction: column; + overflow: hidden; + border: 1px solid var(--border-color); + border-radius: 12px; + background: var(--md-sys-color-surface); + box-shadow: var(--glass-shadow); +} + +.text-chat-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 10px 14px; + border-bottom: 1px solid var(--border-color); +} + +.text-chat-header h2 { + display: flex; + align-items: center; + gap: 9px; + font-size: 1.2rem; +} + +.text-chat-header p, +.chat-empty p { + margin-top: 5px; + color: var(--md-sys-color-on-surface-variant); + font-size: 0.86rem; +} + +.text-engine-badge { + max-width: 240px; + padding: 7px 10px; + border-radius: var(--md-shape-corner-full); + background: var(--md-sys-color-surface-variant); + color: var(--md-sys-color-on-surface-variant); + font-size: 0.75rem; + text-align: center; +} + +.text-engine-badge.ready { + color: var(--md-sys-color-success); + border: 1px solid color-mix(in srgb, var(--md-sys-color-success) 45%, transparent); +} + +/* ─── Chat Messages Container ─────────────────────────────────── */ +.chat-messages { + flex: 1; + overflow-y: auto; + overflow-x: hidden; + padding: 14px 12px 8px; + display: flex; + flex-direction: column; + gap: 0; + scroll-behavior: smooth; +} + +.chat-messages::-webkit-scrollbar { + width: 5px; +} +.chat-messages::-webkit-scrollbar-track { + background: transparent; +} +.chat-messages::-webkit-scrollbar-thumb { + background: color-mix(in srgb, var(--md-sys-color-outline) 35%, transparent); + border-radius: 99px; +} + +/* ─── Empty State ─────────────────────────────────────────────── */ +.chat-empty { + margin: auto; + max-width: 460px; + text-align: center; + color: var(--md-sys-color-on-surface-variant); + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + padding: 40px 24px; +} + +.chat-empty-icon { + width: 68px; + height: 68px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, + color-mix(in srgb, var(--md-sys-color-primary) 18%, transparent), + color-mix(in srgb, var(--md-sys-color-tertiary, #7c3aed) 12%, transparent)); + border: 1.5px solid color-mix(in srgb, var(--md-sys-color-primary) 25%, transparent); + color: var(--md-sys-color-primary); + animation: emptyPulse 3s ease-in-out infinite; +} + +@keyframes emptyPulse { + 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--md-sys-color-primary) 25%, transparent); } + 50% { box-shadow: 0 0 0 12px color-mix(in srgb, var(--md-sys-color-primary) 0%, transparent); } +} + +.chat-empty h3 { + margin: 0; + font-size: 1.15rem; + font-weight: 700; + color: var(--md-sys-color-on-surface); + letter-spacing: -0.01em; +} + +.chat-empty p { + margin: 0; + font-size: 0.875rem; + line-height: 1.6; + max-width: 340px; +} + +.chat-suggestions { + display: flex; + flex-direction: column; + gap: 8px; + width: 100%; + max-width: 380px; + margin-top: 8px; +} + +.chat-suggestion-chip { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 14px; + border-radius: var(--md-shape-corner-medium); + border: 1px solid var(--border-color); + background: var(--md-sys-color-surface-variant); + color: var(--md-sys-color-on-surface-variant); + font-size: 0.82rem; + font-weight: 500; + text-align: left; + cursor: pointer; + transition: all 0.18s ease; + line-height: 1.4; +} + +.chat-suggestion-chip:hover { + border-color: var(--md-sys-color-primary); + background: color-mix(in srgb, var(--md-sys-color-primary) 8%, var(--md-sys-color-surface-variant)); + color: var(--md-sys-color-on-surface); + transform: translateX(3px); +} + +/* ─── Message Row (avatar + bubble) ──────────────────────────── */ +.chat-message-row { + display: flex; + align-items: flex-end; + gap: 8px; + margin-bottom: 4px; + animation: msgSlideIn 0.22s cubic-bezier(0.22, 1, 0.36, 1) both; +} + +.chat-message-row.ai { + align-items: flex-start; +} + +@keyframes msgSlideIn { + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } +} + +.chat-message-row.user { + flex-direction: row-reverse; +} + +.chat-message-row + .chat-message-row { + margin-top: 2px; +} + +/* Group spacing: bigger gap between userβ†’ai switches */ +.chat-message-row.ai + .chat-message-row.user, +.chat-message-row.user + .chat-message-row.ai { + margin-top: 10px; +} + +/* ─── Avatar Chip ─────────────────────────────────────────────── */ +.chat-avatar { + width: 30px; + height: 30px; + border-radius: 50%; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.72rem; + font-weight: 800; + letter-spacing: 0.02em; + user-select: none; +} + +.chat-avatar.ai { + background: linear-gradient(135deg, var(--md-sys-color-primary), color-mix(in srgb, var(--md-sys-color-primary) 70%, #7c3aed)); + color: var(--md-sys-color-on-primary); + box-shadow: + 0 0 0 3px color-mix(in srgb, var(--md-sys-color-primary) 14%, transparent), + 0 8px 20px color-mix(in srgb, var(--md-sys-color-primary) 28%, transparent); +} + +.chat-avatar.user { + background: color-mix(in srgb, var(--md-sys-color-primary-container) 80%, var(--md-sys-color-surface-variant)); + color: var(--md-sys-color-on-primary-container); + border: 1.5px solid color-mix(in srgb, var(--md-sys-color-primary) 30%, transparent); +} + +/* ─── Bubble ──────────────────────────────────────────────────── */ +.chat-bubble-wrap { + display: flex; + flex-direction: column; + max-width: min(1440px, calc(100% - 42px)); + gap: 5px; +} + +.chat-message-row.user .chat-bubble-wrap { + align-items: flex-end; + max-width: min(920px, 72%); +} + +.chat-message-row.ai .chat-bubble-wrap { + align-items: flex-start; + max-width: 80%; +} + +.chat-bubble { + padding: 10px 14px; + border-radius: 14px; + line-height: 1.5; + overflow-wrap: anywhere; + position: relative; + font-size: 0.88rem; +} + +/* AI bubble */ +.chat-message-row.ai .chat-bubble { + background: var(--md-sys-color-surface-variant); + color: var(--md-sys-color-on-surface); + border: 1px solid var(--border-color); + border-bottom-left-radius: 4px; + max-width: 100%; +} + +/* User bubble */ +.chat-message-row.user .chat-bubble { + background: linear-gradient(135deg, + var(--md-sys-color-primary), + color-mix(in srgb, var(--md-sys-color-primary) 80%, #7c3aed 20%)); + color: var(--md-sys-color-on-primary); + border: none; + border-bottom-right-radius: 4px; + box-shadow: 0 2px 12px color-mix(in srgb, var(--md-sys-color-primary) 30%, transparent); +} + +.chat-bubble.error { + background: color-mix(in srgb, var(--md-sys-color-error) 12%, var(--md-sys-color-surface-variant)); + border-color: color-mix(in srgb, var(--md-sys-color-error) 35%, transparent); + color: var(--md-sys-color-error); +} + +/* Sender label */ +.chat-sender-label { + font-size: 0.66rem; + font-weight: 800; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--md-sys-color-outline); + padding: 0 2px; + line-height: 1.1; +} + +/* ─── Generation Stats ────────────────────────────────────────── */ +.chat-generation-warning { + margin-top: 4px; + padding: 7px 10px; + border-radius: var(--md-shape-corner-small); + border: 1px solid color-mix(in srgb, var(--md-sys-color-warning, #f59e0b) 35%, transparent); + background: color-mix(in srgb, var(--md-sys-color-warning, #f59e0b) 10%, transparent); + color: var(--md-sys-color-on-surface-variant); + font-size: 0.76rem; + line-height: 1.35; + max-width: min(720px, 84vw); +} + +.chat-generation-stats { + display: inline-flex; + align-items: center; + gap: 6px; + margin-top: 2px; + padding: 4px 10px; + border-radius: 99px; + background: color-mix(in srgb, var(--md-sys-color-surface-variant) 82%, transparent); + border: 1px solid color-mix(in srgb, var(--border-color) 88%, var(--md-sys-color-primary) 12%); + color: var(--md-sys-color-on-surface-variant); + font-size: 0.68rem; + font-weight: 800; + backdrop-filter: blur(8px); + line-height: 1.4; +} + +.chat-generation-stats.streaming { + color: var(--md-sys-color-primary); + border-color: color-mix(in srgb, var(--md-sys-color-primary) 38%, transparent); + background: color-mix(in srgb, var(--md-sys-color-primary) 10%, var(--md-sys-color-surface-variant)); + box-shadow: 0 6px 18px color-mix(in srgb, var(--md-sys-color-primary) 10%, transparent); +} + +@keyframes statsGlow { + 0%, 100% { box-shadow: none; } + 50% { box-shadow: 0 0 8px color-mix(in srgb, var(--md-sys-color-primary) 30%, transparent); } +} + +.chat-generation-stats.complete { + color: var(--md-sys-color-primary); +} + +.chat-generation-stats.starting { + animation: statsBlink 1.1s ease-in-out infinite; +} + +@keyframes statsBlink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.45; } +} + +/* ─── Thinking / Progress ─────────────────────────────────────── */ +.chat-thinking, +.text-progress { + display: flex; + align-items: center; + gap: 7px; + color: var(--md-sys-color-on-surface-variant); + font-size: 0.8rem; +} + +.text-progress.error { + color: var(--md-sys-color-error); +} + +/* ─── Composer ────────────────────────────────────────────────── */ +.chat-composer { + display: flex; + flex-direction: column; + gap: 0; + padding: 10px 14px 12px; + border-top: 1px solid var(--border-color); + background: color-mix(in srgb, var(--md-sys-color-surface) 85%, transparent); + backdrop-filter: blur(16px); +} + +.chat-composer-inner { + display: flex; + flex-direction: column; + align-items: stretch; + background: var(--md-sys-color-surface-variant); + border: 1px solid var(--border-color); + border-radius: 18px; + padding: 10px 12px 8px; + transition: border-color 0.25s cubic-bezier(0.4, 0, 0.2, 1), + box-shadow 0.25s cubic-bezier(0.4, 0, 0.2, 1), + background-color 0.25s cubic-bezier(0.4, 0, 0.2, 1); +} + +.chat-composer-inner:hover { + border-color: color-mix(in srgb, var(--md-sys-color-primary) 30%, var(--border-color)); +} + +.chat-composer-inner:focus-within { + border-color: var(--md-sys-color-primary); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--md-sys-color-primary) 15%, transparent), + 0 4px 16px rgba(0, 0, 0, 0.08); + background: color-mix(in srgb, var(--md-sys-color-surface-variant) 97%, var(--md-sys-color-primary)); +} + +.chat-composer textarea, +.system-prompt, +.text-chat-settings input, +.text-chat-settings select { + width: 100%; + border: 1px solid var(--border-color); + border-radius: var(--md-shape-corner-medium); + background: var(--md-sys-color-surface-variant); + color: var(--md-sys-color-on-surface); + padding: 10px 12px; + font: inherit; + outline: none; +} + +.chat-composer-textarea-container { + width: 100%; + display: flex; + min-width: 0; +} + +.chat-composer-textarea { + flex: 1; + border: none !important; + background: transparent !important; + color: var(--md-sys-color-on-surface); + padding: 2px 4px 6px 4px; + font: inherit; + font-size: 0.92rem; + outline: none; + resize: none !important; + min-height: 28px; + max-height: 140px; + line-height: 1.45; + overflow-y: auto; + margin-bottom: 0; +} + +.chat-composer-textarea::placeholder { + color: var(--md-sys-color-outline); + font-weight: 400; + opacity: 0.8; +} + +.chat-composer textarea { + min-height: 28px; + max-height: 140px; + resize: none; +} + +.chat-composer-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + border-top: 1px solid color-mix(in srgb, var(--border-color) 35%, transparent); + padding-top: 8px; + margin-top: 4px; +} + +.chat-composer-toolbar-left { + display: flex; + align-items: center; + gap: 8px; +} + +.chat-composer-toolbar-right { + display: flex; + align-items: center; +} + +.chat-composer-send-btn { + width: 32px; + height: 32px; + border-radius: 50%; + border: none; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + background: linear-gradient(135deg, var(--md-sys-color-primary), color-mix(in srgb, var(--md-sys-color-primary) 75%, #7c3aed)); + color: var(--md-sys-color-on-primary); + box-shadow: 0 2px 8px color-mix(in srgb, var(--md-sys-color-primary) 30%, transparent); + margin-bottom: 0; +} + +.chat-composer-send-btn:hover:not(:disabled) { + transform: scale(1.08) translateY(-1px); + box-shadow: 0 4px 14px color-mix(in srgb, var(--md-sys-color-primary) 45%, transparent); +} + +.chat-composer-send-btn:active:not(:disabled) { + transform: scale(0.96) translateY(0); +} + +.chat-composer-send-btn:disabled { + background: color-mix(in srgb, var(--md-sys-color-surface-variant) 80%, transparent); + color: var(--md-sys-color-outline); + opacity: 0.5; + box-shadow: none; + cursor: not-allowed; +} + +.chat-composer-stop-btn { + width: 32px; + height: 32px; + border-radius: 50%; + border: none; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + background: color-mix(in srgb, var(--md-sys-color-error) 15%, var(--md-sys-color-surface-variant)); + color: var(--md-sys-color-error); + border: 1.5px solid color-mix(in srgb, var(--md-sys-color-error) 40%, transparent); + animation: stopPulse 1.5s ease-in-out infinite; + margin-bottom: 0; +} + +@keyframes stopPulse { + 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--md-sys-color-error) 30%, transparent); } + 50% { box-shadow: 0 0 0 5px color-mix(in srgb, var(--md-sys-color-error) 0%, transparent); } +} + +.chat-composer-stop-btn:hover { + background: color-mix(in srgb, var(--md-sys-color-error) 22%, var(--md-sys-color-surface-variant)); + transform: scale(1.08) translateY(-1px); +} + +.chat-composer-attach-btn { + width: 32px; + height: 32px; + border-radius: 50%; + border: none; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + background: transparent; + color: var(--md-sys-color-outline); + margin-bottom: 0; +} + +.chat-composer-attach-btn:hover:not(:disabled) { + background: color-mix(in srgb, var(--md-sys-color-primary) 10%, transparent); + color: var(--md-sys-color-primary); + transform: translateY(-1px); +} + +.chat-composer-attach-btn:disabled { + opacity: 0.35; + cursor: not-allowed; +} + +.chat-composer-hint { + display: none; +} + +.chat-composer-deepthink-btn { + height: 32px; + display: inline-flex; + align-items: center; + gap: 6px; + padding: 0 12px; + border-radius: 999px; + border: 1px solid var(--border-color); + background: color-mix(in srgb, var(--md-sys-color-surface-variant) 40%, transparent); + color: var(--md-sys-color-outline); + font-family: Outfit, sans-serif; + font-size: 0.78rem; + font-weight: 600; + cursor: pointer; + flex-shrink: 0; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + margin-bottom: 0; +} + +.chat-composer-deepthink-btn:hover:not(:disabled) { + border-color: color-mix(in srgb, var(--md-sys-color-primary) 40%, transparent); + background: color-mix(in srgb, var(--md-sys-color-primary) 8%, transparent); + color: var(--md-sys-color-on-surface); + transform: translateY(-1px); +} + +.chat-composer-deepthink-btn.active { + background: color-mix(in srgb, var(--md-sys-color-primary) 12%, transparent); + border-color: color-mix(in srgb, var(--md-sys-color-primary) 40%, transparent); + color: var(--md-sys-color-primary); + box-shadow: 0 2px 8px color-mix(in srgb, var(--md-sys-color-primary) 10%, transparent); +} + +.chat-composer-deepthink-btn.deepthink-btn.active { + background: color-mix(in srgb, var(--md-sys-color-primary) 14%, transparent); + border-color: color-mix(in srgb, var(--md-sys-color-primary) 45%, transparent); + color: var(--md-sys-color-primary); + box-shadow: 0 0 12px color-mix(in srgb, var(--md-sys-color-primary) 15%, transparent); +} + +.chat-composer-deepthink-btn.active:hover:not(:disabled) { + background: color-mix(in srgb, var(--md-sys-color-primary) 18%, transparent); + transform: translateY(-1px); +} + +/* Custom Dropdown select */ +.chat-composer-select { + height: 32px; + width: 105px; + border-radius: 999px; + border: 1px solid var(--border-color); + background-color: color-mix(in srgb, var(--md-sys-color-surface-variant) 40%, transparent); + color: var(--md-sys-color-outline); + font-family: Outfit, sans-serif; + font-size: 0.78rem; + font-weight: 600; + padding: 0 26px 0 12px; + cursor: pointer; + outline: none; + margin-bottom: 2px; + flex-shrink: 0; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23888888' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 10px center; +} + +.chat-composer-select:hover:not(:disabled) { + border-color: color-mix(in srgb, var(--md-sys-color-primary) 40%, transparent); + background-color: color-mix(in srgb, var(--md-sys-color-primary) 8%, transparent); + color: var(--md-sys-color-on-surface); + transform: translateY(-1px); +} + +.chat-composer-select:focus { + border-color: var(--md-sys-color-primary); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--md-sys-color-primary) 15%, transparent); +} + +.chat-composer-select option { + background-color: var(--md-sys-color-surface); + color: var(--md-sys-color-on-surface); + padding: 8px; +} + +/* Custom Chat Model Dropdown Selection */ +.chat-model-select-trigger { + height: 34px; + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + border-radius: var(--md-shape-corner-medium); + background: var(--md-sys-color-surface-variant); + border: 1px solid var(--border-color); + color: var(--md-sys-color-on-surface); + padding: 0 12px; + font-family: Outfit, sans-serif; + font-size: 0.9rem; + font-weight: 600; + cursor: pointer; + outline: none; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); +} + +.chat-model-select-trigger:hover:not(:disabled) { + border-color: color-mix(in srgb, var(--md-sys-color-primary) 40%, var(--border-color)); + background: color-mix(in srgb, var(--md-sys-color-surface-variant) 95%, var(--md-sys-color-primary)); +} + +.chat-model-select-trigger.active { + border-color: var(--md-sys-color-primary); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--md-sys-color-primary) 15%, transparent); + background: color-mix(in srgb, var(--md-sys-color-surface-variant) 95%, var(--md-sys-color-primary)); +} + +.chat-model-select-trigger:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.chat-model-select-label { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; + text-align: left; +} + +.chat-model-select-arrow { + transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1); + color: var(--md-sys-color-outline); + flex-shrink: 0; +} + +.chat-model-select-arrow.open { + transform: rotate(180deg); + color: var(--md-sys-color-primary); +} + +.chat-model-dropdown-menu { + position: absolute; + top: 40px; + left: 0; + width: 100%; + min-width: 280px; + max-height: 280px; + overflow-y: auto; + background: color-mix(in srgb, var(--md-sys-color-surface) 40%, var(--md-sys-color-background)); + backdrop-filter: blur(20px) !important; + -webkit-backdrop-filter: blur(20px) !important; + border: 1px solid var(--border-color); + border-radius: 12px; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.25); + padding: 6px; + display: flex; + flex-direction: column; + gap: 2px; + z-index: 9999; + animation: theme-dropdown-fade 0.2s cubic-bezier(0.4, 0, 0.2, 1); +} + +.chat-model-dropdown-item { + width: 100%; + padding: 8px 12px; + border: none; + background: transparent; + color: var(--md-sys-color-on-surface); + font-family: Outfit, sans-serif; + font-size: 0.85rem; + font-weight: 500; + text-align: left; + border-radius: 8px; + cursor: pointer; + transition: all 0.15s ease; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.chat-model-dropdown-item:hover { + background: color-mix(in srgb, var(--md-sys-color-primary) 10%, transparent); + color: var(--md-sys-color-primary); +} + +.chat-model-dropdown-item.selected { + background: color-mix(in srgb, var(--md-sys-color-primary) 8%, var(--md-sys-color-surface-variant)); + font-weight: 600; +} + +.chat-model-dropdown-item.active { + border-left: 3px solid var(--md-sys-color-primary); + padding-left: 9px; +} + +.chat-model-name-text { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; +} + +.chat-model-active-badge { + font-size: 0.65rem; + font-weight: 700; + text-transform: uppercase; + background: var(--md-sys-color-primary-container); + color: var(--md-sys-color-primary); + padding: 2px 6px; + border-radius: 4px; + letter-spacing: 0.05em; +} + +@keyframes rotateThinking { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.rotate-anim { + animation: rotateThinking 7s linear infinite; +} + +.text-chat-settings { + display: flex; + flex-direction: column; + gap: 14px; + overflow: auto; + padding-right: 3px; +} + +.text-chat-settings .m3-card { + padding: 16px; +} + +.text-model-actions, +.text-url-row { + display: flex; + gap: 8px; + margin-top: 10px; +} + +.text-local-model, +.text-download-option { + width: 100%; + display: flex; + align-items: center; + gap: 8px; + margin-top: 8px; + padding: 9px 10px; + border: 1px solid var(--border-color); + border-radius: var(--md-shape-corner-small); + background: transparent; + color: var(--md-sys-color-on-surface); + text-align: left; +} + +.text-local-model span, +.text-download-option span { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; +} + +.text-local-model button { + border: 0; + background: transparent; + color: var(--md-sys-color-error); + cursor: pointer; +} + +.text-download-option { + cursor: pointer; +} + +.text-download-option:hover { + background: var(--md-sys-color-surface-variant); +} + +.text-download-option small { + color: var(--md-sys-color-on-surface-variant); +} + +.system-prompt { + min-height: 85px; + resize: vertical; + margin-top: 9px; +} + +@media (max-width: 1050px) { + .text-chat-layout { + grid-template-columns: 1fr; + overflow: auto; + } + + .text-chat-main { + min-height: 620px; + } + + .text-chat-settings { + overflow: visible; + } +} + /* Persistent Top Status Bar */ .top-status-bar { height: 64px; @@ -234,6 +1400,28 @@ body { box-shadow: var(--glass-shadow); } +.sidebar-toggle-btn { + background: var(--md-sys-color-surface-variant); + border: 1px solid var(--border-color); + color: var(--md-sys-color-on-surface-variant); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: var(--md-shape-corner-medium); + transition: all var(--md-transition-duration) var(--md-transition-easing); + flex-shrink: 0; +} + +.sidebar-toggle-btn:hover { + color: var(--md-sys-color-primary); + border-color: var(--md-sys-color-primary); + background-color: var(--md-sys-color-primary-container); + transform: scale(1.05); +} + .current-model-info { display: flex; align-items: center; @@ -357,6 +1545,24 @@ body { flex: 1; overflow-y: auto; padding: 28px; + scroll-behavior: smooth; +} + +.workspace-area::-webkit-scrollbar { + width: 6px; +} + +.workspace-area::-webkit-scrollbar-track { + background: transparent; +} + +.workspace-area::-webkit-scrollbar-thumb { + background: color-mix(in srgb, var(--md-sys-color-outline) 25%, transparent); + border-radius: 99px; +} + +.workspace-area::-webkit-scrollbar-thumb:hover { + background: color-mix(in srgb, var(--md-sys-color-outline) 45%, transparent); } .workspace-title-section { @@ -377,6 +1583,136 @@ body { opacity: 0.8; } +.speech-workspace { + display: flex; + flex-direction: column; + gap: 18px; +} + +.speech-grid { + display: grid; + grid-template-columns: minmax(280px, 0.9fr) minmax(320px, 1.1fr); + gap: 18px; + align-items: stretch; +} + +.speech-panel, +.speech-result-panel { + display: flex; + flex-direction: column; + gap: 16px; + padding: 18px; +} + +.speech-panel-header, +.speech-button-row, +.speech-controls-row { + display: flex; + align-items: center; + gap: 10px; +} + +.speech-panel-header { + justify-content: space-between; +} + +.speech-panel-header h3 { + margin: 0; + font-size: 1rem; +} + +.speech-button-row { + flex-wrap: wrap; +} + +.speech-controls-row { + align-items: flex-end; +} + +.speech-label { + display: flex; + flex: 1; + min-width: 0; + flex-direction: column; + gap: 6px; + color: var(--md-sys-color-on-surface-variant); + font-size: 0.82rem; + font-weight: 600; +} + +.speech-starter-list { + display: grid; + grid-template-columns: 1fr; + gap: 8px; +} + +.speech-capture-zone { + display: flex; + align-items: center; + gap: 14px; + min-height: 120px; + border: 1px dashed var(--md-sys-color-outline-variant); + border-radius: var(--md-shape-corner-medium); + padding: 18px; + background: var(--md-sys-color-surface-container-low); +} + +.speech-capture-zone p { + margin: 4px 0 0 0; + color: var(--md-sys-color-on-surface-variant); + font-size: 0.86rem; + line-height: 1.4; +} + +.m3-textarea.speech-result-textarea { + min-height: 420px; + height: clamp(420px, 46vh, 720px); + resize: vertical; + line-height: 1.55; + font-size: 0.98rem; +} + +.speech-history-list { + display: flex; + flex-direction: column; + gap: 8px; +} + +.speech-history-item { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 10px 12px; + border: 1px solid var(--md-sys-color-outline-variant); + border-radius: var(--md-shape-corner-small); +} + +.speech-history-item div { + display: flex; + min-width: 0; + flex-direction: column; + gap: 2px; +} + +.speech-history-item strong, +.speech-history-item span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.speech-history-item span { + color: var(--md-sys-color-on-surface-variant); + font-size: 0.78rem; +} + +@media (max-width: 980px) { + .speech-grid { + grid-template-columns: 1fr; + } +} + /* Glassmorphic UI Cards */ .m3-card { background-color: var(--md-sys-color-surface); @@ -490,14 +1826,14 @@ body { transform: translateY(-1px); } -html[data-theme="dark"] .m3-btn-error { +html[data-theme^="dark"] .m3-btn-error { background-color: rgba(251, 113, 133, 0.09); border: 1px solid rgba(251, 113, 133, 0.22); color: #fda4af; box-shadow: 0 2px 8px rgba(251, 113, 133, 0.04); } -html[data-theme="dark"] .m3-btn-error:hover:not(:disabled) { +html[data-theme^="dark"] .m3-btn-error:hover:not(:disabled) { background-color: rgba(251, 113, 133, 0.16); border-color: rgba(251, 113, 133, 0.35); } @@ -525,7 +1861,7 @@ html[data-theme="dark"] .m3-btn-error:hover:not(:disabled) { .m3-input, .m3-textarea { width: 100%; background-color: var(--md-sys-color-surface-variant); - border: 1px solid var(--border-color); + border: 1.5px solid var(--border-color); border-radius: var(--md-shape-corner-medium); padding: 10px 14px; font-family: inherit; @@ -547,26 +1883,28 @@ html[data-theme="dark"] .m3-btn-error:hover:not(:disabled) { .m3-input:focus, .m3-textarea:focus { border-color: var(--md-sys-color-primary); background-color: var(--md-sys-color-surface); - box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.12); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--md-sys-color-primary) 15%, transparent), 0 2px 8px color-mix(in srgb, var(--md-sys-color-primary) 10%, transparent); } /* Custom Range Sliders */ .m3-slider-group { display: flex; flex-direction: column; - gap: 4px; + gap: 6px; } .m3-slider-header { display: flex; justify-content: space-between; align-items: center; + margin-bottom: 2px; } .m3-slider-label { font-size: 0.82rem; font-weight: 600; color: var(--md-sys-color-on-surface-variant); + letter-spacing: 0.01em; } .m3-slider-value { @@ -582,30 +1920,45 @@ html[data-theme="dark"] .m3-btn-error:hover:not(:disabled) { -webkit-appearance: none; appearance: none; width: 100%; - height: 4px; + height: 5px; border-radius: var(--md-shape-corner-full); - background: var(--border-color); + background: linear-gradient(90deg, + var(--md-sys-color-primary) var(--value, 50%), + var(--border-color) var(--value, 50%) + ); outline: none; - margin: 12px 0; - transition: background 0.25s; + margin: 10px 0; + transition: all 0.25s; + cursor: pointer; } .m3-slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; - width: 16px; - height: 16px; + width: 18px; + height: 18px; border-radius: 50%; background: var(--md-sys-color-primary); - border: 2px solid var(--md-sys-color-surface); - box-shadow: 0 0 8px rgba(99, 102, 241, 0.35); + border: 2.5px solid var(--md-sys-color-surface); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--md-sys-color-primary) 30%, transparent), 0 2px 8px color-mix(in srgb, var(--md-sys-color-primary) 40%, transparent); cursor: pointer; transition: all var(--md-transition-duration) var(--md-transition-easing); } .m3-slider::-webkit-slider-thumb:hover { - transform: scale(1.25); - box-shadow: 0 0 12px rgba(99, 102, 241, 0.6); + transform: scale(1.2); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--md-sys-color-primary) 25%, transparent), 0 4px 12px color-mix(in srgb, var(--md-sys-color-primary) 50%, transparent); +} + +.m3-slider::-moz-range-thumb { + width: 18px; + height: 18px; + border-radius: 50%; + background: var(--md-sys-color-primary); + border: 2.5px solid var(--md-sys-color-surface); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--md-sys-color-primary) 30%, transparent), 0 2px 8px color-mix(in srgb, var(--md-sys-color-primary) 40%, transparent); + cursor: pointer; + transition: all var(--md-transition-duration) var(--md-transition-easing); } /* Modern Switch Styles */ @@ -713,14 +2066,15 @@ html[data-theme="dark"] .m3-btn-error:hover:not(:disabled) { background-color: rgba(0, 0, 0, 0.03); } -html[data-theme="dark"] .m3-segment-item:hover:not(.active):not(.disabled) { +html[data-theme^="dark"] .m3-segment-item:hover:not(.active):not(.disabled) { background-color: rgba(255, 255, 255, 0.04); } .m3-segment-item.active { - background-color: var(--md-sys-color-primary); + background: linear-gradient(135deg, var(--md-sys-color-primary), color-mix(in srgb, var(--md-sys-color-primary) 85%, var(--md-sys-color-secondary))); color: var(--md-sys-color-on-primary); - box-shadow: 0 2px 8px rgba(99, 102, 241, 0.2); + box-shadow: 0 2px 10px color-mix(in srgb, var(--md-sys-color-primary) 35%, transparent); + font-weight: 700; } .m3-segment-item.disabled { @@ -760,14 +2114,6 @@ html[data-theme="dark"] .m3-segment-item:hover:not(.active):not(.disabled) { border-radius: var(--md-shape-corner-full); font-size: 0.78rem; font-weight: 550; - cursor: pointer; - transition: all var(--md-transition-duration); -} - -.status-chip:hover { - background-color: var(--md-sys-color-primary-container); - border-color: var(--md-sys-color-primary); - color: var(--md-sys-color-primary); } .prompt-actions { @@ -784,10 +2130,14 @@ html[data-theme="dark"] .m3-segment-item:hover:not(.active):not(.disabled) { } .aspect-ratio-btn.active { - background-color: var(--md-sys-color-primary-container) !important; + background: linear-gradient(135deg, + color-mix(in srgb, var(--md-sys-color-primary-container) 80%, transparent), + color-mix(in srgb, var(--md-sys-color-secondary-container) 50%, transparent) + ) !important; border-color: var(--md-sys-color-primary) !important; color: var(--md-sys-color-primary) !important; - box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.15); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--md-sys-color-primary) 15%, transparent), 0 4px 12px color-mix(in srgb, var(--md-sys-color-primary) 15%, transparent); + font-weight: 700; } .aspect-ratio-preview { @@ -802,7 +2152,7 @@ html[data-theme="dark"] .m3-segment-item:hover:not(.active):not(.disabled) { .aspect-ratio-btn:hover .aspect-ratio-preview, .aspect-ratio-btn.active .aspect-ratio-preview { opacity: 1; - background-color: rgba(99, 102, 241, 0.06); + background-color: color-mix(in srgb, var(--md-sys-color-primary) 8%, transparent); } .ratio-1-1 { width: 16px; height: 16px; } @@ -1035,6 +2385,115 @@ html[data-theme="dark"] .m3-segment-item:hover:not(.active):not(.disabled) { transition: width 0.25s ease; } +.model-discovery-controls { + display: flex; + flex-direction: column; + gap: 12px; + margin-bottom: 16px; + padding: 16px; + border: 1px solid var(--border-color-muted); + border-radius: var(--md-shape-corner-large); + background: var(--md-sys-color-surface); +} + +.model-search-box { + display: flex; + align-items: center; + gap: 10px; + min-height: 44px; + padding: 0 12px; + border: 1px solid var(--border-color); + border-radius: var(--md-shape-corner-medium); + background: var(--md-sys-color-surface-variant); + color: var(--md-sys-color-outline); +} + +.model-search-box:focus-within { + border-color: var(--md-sys-color-primary); +} + +.model-search-box input { + flex: 1; + min-width: 0; + border: 0; + outline: 0; + background: transparent; + color: var(--md-sys-color-on-surface); + font: inherit; +} + +.model-search-box button { + display: inline-flex; + padding: 4px; + border: 0; + background: transparent; + color: inherit; + cursor: pointer; +} + +.model-filter-row { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 8px; +} + +.model-filter-chip { + display: inline-flex; + align-items: center; + gap: 6px; + min-height: 34px; + padding: 0 12px; + border: 1px solid var(--border-color); + border-radius: var(--md-shape-corner-full); + background: transparent; + color: var(--md-sys-color-on-surface-variant); + font-weight: 600; + cursor: pointer; +} + +.model-filter-chip.selected { + border-color: var(--md-sys-color-primary); + background: var(--md-sys-color-primary-container); + color: var(--md-sys-color-on-primary-container); +} + +.model-filter-clear { + border: 0; + background: transparent; + color: var(--md-sys-color-primary); + font-weight: 600; + cursor: pointer; +} + +.model-discovery-loading { + display: flex; + align-items: center; + gap: 12px; + color: var(--md-sys-color-outline); + font-size: 0.82rem; + font-weight: 600; +} + +.model-search-warning { + padding: 10px 12px; + border-radius: var(--md-shape-corner-small); + background: var(--md-sys-color-error-container); + color: var(--md-sys-color-on-error-container); + font-size: 0.8rem; +} + +.model-search-empty { + color: var(--md-sys-color-on-surface-variant); + text-align: center; +} + +.model-load-more { + align-self: center; + min-width: 180px; + margin: 4px 0 8px; +} + /* Importer & Library */ .import-box { border: 2px dashed var(--border-color); @@ -1241,3 +2700,1141 @@ html[data-theme="dark"] .m3-segment-item:hover:not(.active):not(.disabled) { font-weight: 600; margin-top: 4px; } + +/* Chat History Sidebar styling */ +.chat-history-sidebar { + width: 240px; + flex-shrink: 0; + display: flex; + flex-direction: column; + background: var(--md-sys-color-surface-variant); + opacity: 0.95; + border: 1px solid var(--border-color); + border-radius: var(--md-shape-corner-large); + padding: 16px; + height: 100%; + overflow: hidden; + box-shadow: var(--glass-shadow); + transition: all 0.3s ease; +} + +.history-items-list { + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 14px; + overflow-y: auto; + flex: 1; +} + +.history-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 12px; + border-radius: var(--md-shape-corner-medium); + cursor: pointer; + background: transparent; + transition: background-color 0.2s ease, border-color 0.2s ease; + border: 1px solid transparent; +} + +.history-item:hover { + background: rgba(120, 120, 120, 0.1); +} + +.history-item.active { + background: var(--md-sys-color-secondary-container); + color: var(--md-sys-color-on-secondary-container); + border-color: var(--md-sys-color-outline-variant); +} + +.history-item-info { + display: flex; + flex-direction: column; + min-width: 0; + flex: 1; +} + +.history-item-title { + font-size: 0.85rem; + font-weight: 600; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--md-sys-color-on-surface); +} + +.history-item-meta { + font-size: 0.68rem; + opacity: 0.7; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-top: 2px; + color: var(--md-sys-color-on-surface-variant); +} + +.history-item-delete { + background: transparent; + border: none; + color: var(--md-sys-color-outline); + cursor: pointer; + padding: 4px; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + transition: transform var(--md-transition-duration) var(--md-transition-easing); +} + +.gallery-item:hover .gallery-thumb { + transform: scale(1.05); +} + +.gallery-select-badge { + position: absolute; + top: 8px; + right: 8px; + width: 26px; + height: 26px; + border-radius: 50%; + display: grid; + place-items: center; + color: var(--md-sys-color-on-primary); + background: var(--md-sys-color-primary); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28); +} + +.app-dialog-backdrop { + position: fixed; + inset: 0; + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; + padding: 24px; + background: rgba(8, 12, 24, 0.64); + backdrop-filter: blur(4px) saturate(0.86); +} + +.app-dialog { + width: min(460px, 100%); + background: color-mix(in srgb, var(--md-sys-color-surface) 96%, white 4%); + color: var(--md-sys-color-on-surface); + border: 1px solid color-mix(in srgb, var(--md-sys-color-outline) 55%, transparent); + border-radius: var(--md-shape-corner-large); + box-shadow: + 0 22px 60px rgba(0, 0, 0, 0.42), + 0 0 0 1px rgba(255, 255, 255, 0.08) inset; + padding: 24px; +} + +.app-dialog-title { + font-size: 1.15rem; + font-weight: 800; + margin: 0 0 10px 0; +} + +.app-dialog-message { + font-size: 0.92rem; + line-height: 1.45; + color: var(--md-sys-color-on-surface-variant); + margin: 0; + white-space: pre-wrap; +} + +.app-dialog-actions { + display: flex; + justify-content: flex-end; + gap: 10px; + margin-top: 22px; + flex-wrap: wrap; +} + +.gallery-empty { + text-align: center; + padding: 36px; + color: var(--md-sys-color-on-surface-variant); + font-size: 0.85rem; + opacity: 0.8; +} + +/* Animations */ +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +@keyframes pulse { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.55; transform: scale(1.06); } +} + +/* Sidebar specs classes */ +.sidebar-specs-header { + display: flex; + align-items: center; + gap: 6px; + margin-bottom: 6px; + font-weight: 600; + color: var(--md-sys-color-on-background); +} + +.sidebar-specs-item { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + opacity: 0.85; + margin-bottom: 2px; + color: var(--md-sys-color-on-background); +} + +.sidebar-specs-os { + color: var(--md-sys-color-primary); + font-weight: 600; + margin-top: 4px; +} + +/* Chat History Sidebar styling */ +.chat-history-sidebar { + width: 240px; + flex-shrink: 0; + display: flex; + flex-direction: column; + background: var(--md-sys-color-surface-variant); + opacity: 0.95; + border: 1px solid var(--border-color); + border-radius: var(--md-shape-corner-large); + padding: 16px; + height: 100%; + overflow: hidden; + box-shadow: var(--glass-shadow); + transition: all 0.3s ease; +} + +.history-items-list { + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 14px; + overflow-y: auto; + flex: 1; +} + +.history-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 12px; + border-radius: var(--md-shape-corner-medium); + cursor: pointer; + background: transparent; + transition: background-color 0.2s ease, border-color 0.2s ease; + border: 1px solid transparent; +} + +.history-item:hover { + background: rgba(120, 120, 120, 0.1); +} + +.history-item.active { + background: var(--md-sys-color-secondary-container); + color: var(--md-sys-color-on-secondary-container); + border-color: var(--md-sys-color-outline-variant); +} + +.history-item-info { + display: flex; + flex-direction: column; + min-width: 0; + flex: 1; +} + +.history-item-title { + font-size: 0.85rem; + font-weight: 600; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--md-sys-color-on-surface); +} + +.history-item-meta { + font-size: 0.68rem; + opacity: 0.7; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-top: 2px; + color: var(--md-sys-color-on-surface-variant); +} + +.history-item-delete { + background: transparent; + border: none; + color: var(--md-sys-color-outline); + cursor: pointer; + padding: 4px; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + transition: color 0.2s, background-color 0.2s; + flex-shrink: 0; + margin-left: 6px; +} + +.history-item-delete:hover { + color: var(--md-sys-color-error); + background: rgba(239, 68, 68, 0.1); +} + +/* Chat Thinking Block (Reasoning Process) */ +.chat-thinking-container { + margin-bottom: 4px; + width: fit-content; + max-width: min(420px, 100%); + border-radius: 999px; + border: 1px solid color-mix(in srgb, var(--md-sys-color-primary) 24%, var(--border-color)); + background: + linear-gradient(135deg, + color-mix(in srgb, var(--md-sys-color-primary) 10%, var(--md-sys-color-surface-variant)), + color-mix(in srgb, var(--md-sys-color-surface-variant) 88%, transparent)); + box-shadow: 0 8px 24px color-mix(in srgb, var(--md-sys-color-primary) 9%, transparent); + overflow: hidden; + backdrop-filter: blur(10px); +} + +.chat-thinking-container:has(.chat-thinking-content) { + border-radius: 13px; + max-width: min(720px, 92vw); +} + +.chat-thinking-header { + width: auto; + min-width: 0; + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + padding: 7px 11px; + background: transparent; + border: none; + color: var(--md-sys-color-on-surface-variant); + font-size: 0.75rem; + font-weight: 800; + cursor: pointer; + text-align: left; + transition: background 0.18s ease, color 0.18s ease; + outline: none; +} + +.chat-thinking-header:hover { + background: color-mix(in srgb, var(--md-sys-color-primary) 7%, transparent); + color: var(--md-sys-color-on-surface); +} + +.chat-thinking-title { + display: flex; + align-items: center; + gap: 8px; + opacity: 0.92; + white-space: nowrap; +} + +.chat-thinking-title::before { + content: ""; + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--md-sys-color-primary); + box-shadow: 0 0 0 4px color-mix(in srgb, var(--md-sys-color-primary) 14%, transparent); + animation: thinkingPulse 1.35s ease-in-out infinite; +} + +@keyframes thinkingPulse { + 0%, 100% { + transform: scale(0.9); + opacity: 0.72; + } + 50% { + transform: scale(1.12); + opacity: 1; + } +} + +.chat-thinking-content { + padding: 9px 12px 10px; + font-size: 0.76rem; + line-height: 1.45; + color: var(--md-sys-color-on-surface-variant); + opacity: 0.78; + border-top: 1px solid color-mix(in srgb, var(--border-color) 78%, transparent); + white-space: pre-wrap; + font-family: var(--font-family-monospace, monospace); + background: color-mix(in srgb, var(--md-sys-color-surface) 28%, transparent); + max-height: 150px; + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: + color-mix(in srgb, var(--md-sys-color-primary) 38%, var(--md-sys-color-outline)) + color-mix(in srgb, var(--md-sys-color-surface) 65%, transparent); +} + +.chat-thinking-content::-webkit-scrollbar { + width: 10px; +} + +.chat-thinking-content::-webkit-scrollbar-track { + background: color-mix(in srgb, var(--md-sys-color-surface) 60%, transparent); + border-left: 1px solid color-mix(in srgb, var(--border-color) 70%, transparent); +} + +.chat-thinking-content::-webkit-scrollbar-thumb { + min-height: 34px; + border-radius: 999px; + border: 2px solid transparent; + background: + linear-gradient( + 180deg, + color-mix(in srgb, var(--md-sys-color-primary) 62%, #ffffff 8%), + color-mix(in srgb, var(--md-sys-color-primary) 38%, var(--md-sys-color-outline)) + ) + border-box; + background-clip: padding-box; +} + +.chat-thinking-content::-webkit-scrollbar-thumb:hover { + background: + linear-gradient( + 180deg, + color-mix(in srgb, var(--md-sys-color-primary) 78%, #ffffff 10%), + color-mix(in srgb, var(--md-sys-color-primary) 52%, var(--md-sys-color-outline)) + ) + border-box; + background-clip: padding-box; +} + +/* ============================================================ + PROFESSIONAL SETTINGS UI - Collapsible Cards, Tabs, Hardware Badge + ============================================================ */ + +/* --- Section Tabs (Image | Text) --- */ +.settings-tabs-container { + display: flex; + justify-content: center; + margin-bottom: 24px; +} + +.settings-tabs { + display: flex; + gap: 4px; + padding: 4px; + background: var(--md-sys-color-surface-variant); + border-radius: 14px; + border: 1px solid var(--border-color); + width: fit-content; +} + +.settings-tab { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 24px; + border-radius: 12px; + border: none; + font-weight: 600; + font-size: 0.9rem; + cursor: pointer; + transition: all 0.25s var(--md-transition-easing); + color: var(--md-sys-color-on-surface-variant); + background: transparent; + position: relative; + overflow: hidden; +} + +.settings-tab:hover { + background: var(--md-sys-color-surface); + color: var(--md-sys-color-on-surface); +} + +.settings-tab.active { + background: var(--md-sys-color-primary); + color: var(--md-sys-color-on-primary); + box-shadow: 0 4px 16px rgba(79, 70, 229, 0.25); +} + +.settings-tab .tab-icon { + transition: transform 0.3s ease; +} + +.settings-tab.active .tab-icon { + transform: scale(1.1); +} + +/* --- Hardware Tier Badge --- */ +.hardware-tier-badge { + border-radius: 20px; + padding: 24px; + margin-bottom: 28px; + position: relative; + overflow: hidden; + border: 1px solid var(--md-sys-color-card-border); + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); + background: var(--md-sys-color-surface); + box-shadow: var(--glass-shadow); + transition: all 0.3s ease; +} + +.hardware-tier-badge:hover { + box-shadow: 0 12px 40px rgba(15, 23, 42, 0.1); + border-color: color-mix(in srgb, var(--md-sys-color-primary) 15%, var(--md-sys-color-card-border)); +} + +html[data-theme^="dark"] .hardware-tier-badge:hover { + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3); +} + +.hardware-tier-badge::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + border-radius: 20px 20px 0 0; +} + +.hardware-tier-badge.tier-high::before { + background: linear-gradient(90deg, #22c55e, #10b981); + box-shadow: 0 2px 8px rgba(34, 197, 94, 0.3); +} + +.hardware-tier-badge.tier-mid::before { + background: linear-gradient(90deg, #3b82f6, #0ea5e9); + box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3); +} + +.hardware-tier-badge.tier-low::before { + background: linear-gradient(90deg, #f59e0b, #fbbf24); + box-shadow: 0 2px 8px rgba(245, 158, 11, 0.3); +} + +.hardware-tier-header { + display: flex; + align-items: center; + gap: 16px; + margin-bottom: 12px; +} + +.hardware-tier-icon { + width: 48px; + height: 48px; + border-radius: 14px; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.5rem; + flex-shrink: 0; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); +} + +.tier-high .hardware-tier-icon { + background: linear-gradient(135deg, rgba(34, 197, 94, 0.15), rgba(16, 185, 129, 0.1)); + border: 1px solid rgba(34, 197, 94, 0.2); +} + +.tier-mid .hardware-tier-icon { + background: linear-gradient(135deg, rgba(59, 130, 246, 0.15), rgba(14, 165, 233, 0.1)); + border: 1px solid rgba(59, 130, 246, 0.2); +} + +.tier-low .hardware-tier-icon { + background: linear-gradient(135deg, rgba(245, 158, 11, 0.15), rgba(251, 191, 36, 0.1)); + border: 1px solid rgba(245, 158, 11, 0.2); +} + +.hardware-tier-info { + flex: 1; +} + +.hardware-tier-name { + font-size: 1.15rem; + font-weight: 700; + color: var(--md-sys-color-on-surface); + margin-bottom: 4px; + letter-spacing: -0.01em; +} + +.hardware-tier-specs { + font-size: 0.8rem; + color: var(--md-sys-color-on-surface-variant); + line-height: 1.4; + font-weight: 500; +} + +.hardware-tier-divider { + height: 1px; + background: var(--border-color); + margin: 14px 0; +} + +.hardware-tier-chips { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.hardware-tier-chip { + font-size: 0.72rem; + padding: 5px 14px; + border-radius: 20px; + background: var(--md-sys-color-surface-variant); + font-weight: 600; + color: var(--md-sys-color-on-surface-variant); + border: 1px solid var(--border-color); + transition: all 0.2s ease; + letter-spacing: 0.01em; +} + +.hardware-tier-chip:hover { + background: var(--md-sys-color-primary-container); + color: var(--md-sys-color-on-primary-container); + border-color: color-mix(in srgb, var(--md-sys-color-primary) 25%, var(--border-color)); + transform: translateY(-1px); + box-shadow: 0 2px 8px color-mix(in srgb, var(--md-sys-color-primary) 15%, transparent); +} + +/* --- Collapsible Card --- */ +.collapsible-card { + border: 1px solid var(--md-sys-color-card-border); + border-radius: 16px; + overflow: hidden; + margin-bottom: 16px; + background: var(--md-sys-color-surface); + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); + box-shadow: var(--glass-shadow); + transition: all 0.3s ease; +} + +.collapsible-card:hover { + box-shadow: 0 12px 40px rgba(15, 23, 42, 0.08); + border-color: color-mix(in srgb, var(--md-sys-color-primary) 15%, var(--md-sys-color-card-border)); + transform: translateY(-1px); +} + +html[data-theme^="dark"] .collapsible-card:hover { + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25); + border-color: color-mix(in srgb, var(--md-sys-color-primary) 20%, var(--md-sys-color-card-border)); +} + +.collapsible-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 18px 22px; + cursor: pointer; + user-select: none; + transition: background 0.25s ease; + border: none; + width: 100%; + background: transparent; + color: inherit; + font-family: inherit; + text-align: left; +} + +.collapsible-header:hover { + background: var(--md-sys-color-surface-variant); +} + +.collapsible-header-left { + display: flex; + align-items: center; + gap: 14px; +} + +.collapsible-header-icon { + width: 36px; + height: 36px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, + color-mix(in srgb, var(--md-sys-color-primary) 12%, transparent), + color-mix(in srgb, var(--md-sys-color-secondary) 8%, transparent) + ); + color: var(--md-sys-color-primary); + flex-shrink: 0; + border: 1px solid color-mix(in srgb, var(--md-sys-color-primary) 10%, transparent); +} + +.collapsible-header-title { + font-size: 0.95rem; + font-weight: 700; + color: var(--md-sys-color-on-surface); + letter-spacing: -0.01em; +} + +.collapsible-header-subtitle { + font-size: 0.75rem; + color: var(--md-sys-color-outline); + margin-top: 2px; + font-weight: 500; +} + +.collapsible-chevron { + transition: transform 0.35s var(--md-transition-easing); + color: var(--md-sys-color-outline); + flex-shrink: 0; +} + +.collapsible-chevron.expanded { + transform: rotate(180deg); + color: var(--md-sys-color-primary); +} + +.collapsible-content { + padding: 0 22px 22px; + animation: fadeInSlide 0.3s var(--md-transition-easing); +} + +@keyframes fadeInSlide { + from { + opacity: 0; + transform: translateY(-8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* --- Settings Section Header --- */ +.settings-section-header { + display: flex; + align-items: center; + gap: 14px; + margin: 36px 0 20px 0; + padding: 14px 20px; + border-radius: 14px; + background: var(--md-sys-color-surface); + border: 1px solid var(--md-sys-color-card-border); + border-left: 3px solid; + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); + box-shadow: var(--glass-shadow); + transition: all 0.3s ease; +} + +.settings-section-header:hover { + border-color: color-mix(in srgb, var(--md-sys-color-primary) 20%, var(--md-sys-color-card-border)); + box-shadow: 0 8px 24px rgba(15, 23, 42, 0.06); +} + +html[data-theme^="dark"] .settings-section-header:hover { + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2); +} + +.settings-section-icon { + width: 40px; + height: 40px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + background: linear-gradient(135deg, + color-mix(in srgb, var(--md-sys-color-primary) 15%, transparent), + color-mix(in srgb, var(--md-sys-color-secondary) 10%, transparent) + ); + border: 1px solid color-mix(in srgb, var(--md-sys-color-primary) 12%, transparent); +} + +.settings-section-title { + font-size: 1.1rem; + font-weight: 700; + color: var(--md-sys-color-on-surface); + flex: 1; + letter-spacing: -0.01em; +} + +.settings-section-count { + font-size: 0.72rem; + padding: 4px 12px; + border-radius: 999px; + background: var(--md-sys-color-primary-container); + color: var(--md-sys-color-on-primary-container); + font-weight: 700; + border: 1px solid color-mix(in srgb, var(--md-sys-color-primary) 15%, transparent); + letter-spacing: 0.02em; +} + +/* --- Collapsible Header Badge --- */ +.collapsible-header-badge { + font-size: 0.7rem; + padding: 2px 10px; + border-radius: 999px; + margin-left: 10px; + font-weight: 700; + vertical-align: middle; +} + +.collapsible-header-right { + display: flex; + align-items: center; + gap: 12px; +} + +/* --- Settings Grid (Single Column) --- */ +.settings-grid { + display: flex; + flex-direction: column; + gap: 0; + max-width: 900px; + margin: 0 auto; +} + +/* --- Premium Toggle Switch --- */ +.premium-toggle { + display: flex; + align-items: center; + gap: 14px; + padding: 14px 18px; + border-radius: 14px; + background: var(--md-sys-color-surface-variant); + border: 1px solid var(--border-color); + transition: all 0.25s ease; + cursor: pointer; +} + +.premium-toggle:hover { + border-color: color-mix(in srgb, var(--md-sys-color-primary) 25%, var(--border-color)); + background: color-mix(in srgb, var(--md-sys-color-primary-container) 30%, var(--md-sys-color-surface-variant)); + transform: translateX(2px); +} + +.premium-toggle-checkbox { + width: 22px; + height: 22px; + border-radius: 6px; + border: 2px solid var(--md-sys-color-outline); + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + flex-shrink: 0; + background: transparent; + cursor: pointer; +} + +.premium-toggle-checkbox.checked { + background: var(--md-sys-color-primary); + border-color: var(--md-sys-color-primary); + color: var(--md-sys-color-on-primary); + box-shadow: 0 2px 8px color-mix(in srgb, var(--md-sys-color-primary) 35%, transparent); +} + +.premium-toggle-label { + font-size: 0.85rem; + font-weight: 600; + color: var(--md-sys-color-on-surface); +} + +.premium-toggle-desc { + font-size: 0.75rem; + color: var(--md-sys-color-outline); + margin-top: 3px; + line-height: 1.4; +} + +/* --- Settings Value Badge --- */ +.settings-value-badge { + font-size: 0.72rem; + padding: 3px 10px; + border-radius: 8px; + background: linear-gradient(135deg, + var(--md-sys-color-primary-container), + color-mix(in srgb, var(--md-sys-color-secondary-container) 50%, var(--md-sys-color-primary-container)) + ); + color: var(--md-sys-color-on-primary-container); + font-weight: 700; + font-variant-numeric: tabular-nums; + border: 1px solid color-mix(in srgb, var(--md-sys-color-primary) 12%, transparent); + box-shadow: 0 1px 4px color-mix(in srgb, var(--md-sys-color-primary) 10%, transparent); + letter-spacing: 0.02em; +} + +/* --- Empty State for Settings --- */ +.settings-empty-state { + text-align: center; + padding: 60px 20px; + color: var(--md-sys-color-outline); +} + +.settings-empty-state-icon { + font-size: 3rem; + margin-bottom: 16px; + opacity: 0.5; +} + +.settings-empty-state-title { + font-size: 1.1rem; + font-weight: 600; + color: var(--md-sys-color-on-surface); + margin-bottom: 8px; +} + +.settings-empty-state-desc { + font-size: 0.85rem; + max-width: 400px; + margin: 0 auto; + line-height: 1.5; +} + +/* --- Settings Two Column Layout --- */ +.settings-two-column { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + margin-bottom: 20px; +} + +.settings-column { + display: flex; + flex-direction: column; + gap: 16px; +} + +.settings-subsection { + background: var(--md-sys-color-surface); + border: 1px solid var(--md-sys-color-card-border); + border-radius: 16px; + padding: 20px; + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); + box-shadow: var(--glass-shadow); + transition: all 0.3s ease; +} + +.settings-option-desc { + font-size: 0.75rem; + color: var(--md-sys-color-outline); + font-weight: 300; + opacity: 0.85; + line-height: 1.4; +} + +.settings-subsection:hover { + box-shadow: 0 12px 40px rgba(15, 23, 42, 0.08); + border-color: color-mix(in srgb, var(--md-sys-color-primary) 15%, var(--md-sys-color-card-border)); + transform: translateY(-1px); +} + +html[data-theme^="dark"] .settings-subsection:hover { + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25); + border-color: color-mix(in srgb, var(--md-sys-color-primary) 20%, var(--md-sys-color-card-border)); +} + +.settings-subsection-title { + display: flex; + align-items: center; + gap: 10px; + font-size: 0.88rem; + font-weight: 700; + color: var(--md-sys-color-on-surface); + margin-bottom: 16px; + padding-bottom: 12px; + border-bottom: 1px solid var(--border-color); + letter-spacing: -0.01em; +} + +.settings-subsection-title svg { + color: var(--md-sys-color-primary); + opacity: 0.85; +} + +/* --- Settings Entrance Animation --- */ +@keyframes settingsFadeIn { + from { + opacity: 0; + transform: translateY(12px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.settings-two-column { + animation: settingsFadeIn 0.4s cubic-bezier(0.22, 1, 0.36, 1) both; +} + +.settings-subsection { + animation: settingsFadeIn 0.4s cubic-bezier(0.22, 1, 0.36, 1) both; +} + +.settings-subsection:nth-child(1) { animation-delay: 0.05s; } +.settings-subsection:nth-child(2) { animation-delay: 0.1s; } +.settings-subsection:nth-child(3) { animation-delay: 0.15s; } +.settings-subsection:nth-child(4) { animation-delay: 0.2s; } + +/* --- Responsive: Stack on mobile --- */ +@media (max-width: 900px) { + .settings-two-column { + grid-template-columns: 1fr; + } +} + +/* --- Responsive Adjustments --- */ +@media (max-width: 600px) { + .hardware-tier-badge { + padding: 16px; + border-radius: 16px; + } + + .hardware-tier-header { + gap: 12px; + } + + .hardware-tier-icon { + width: 40px; + height: 40px; + font-size: 1.2rem; + } + + .hardware-tier-name { + font-size: 1rem; + } + + .collapsible-header { + padding: 14px 16px; + } + + .collapsible-content { + padding: 0 16px 16px; + } + + .settings-section-header { + padding: 12px 16px; + margin: 24px 0 16px 0; + } + + .settings-section-icon { + width: 36px; + height: 36px; + } + + .settings-section-title { + font-size: 1rem; + } + + .settings-page-header { + padding: 18px 20px; + margin-bottom: 20px; + } + + .settings-page-title { + font-size: 1.2rem; + } +} + +/* --- Settings Page Header --- */ +.settings-page-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 28px; + padding: 24px 28px; + border-radius: var(--md-shape-corner-large); + background: linear-gradient(135deg, + color-mix(in srgb, var(--md-sys-color-primary-container) 60%, transparent), + color-mix(in srgb, var(--md-sys-color-secondary-container) 40%, transparent) + ); + border: 1px solid var(--md-sys-color-card-border); + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); + box-shadow: var(--glass-shadow); + position: relative; + overflow: hidden; +} + +.settings-page-header::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: linear-gradient(90deg, var(--md-sys-color-primary), var(--md-sys-color-secondary)); + opacity: 0.6; +} + +.settings-page-title { + font-size: 1.5rem; + font-weight: 800; + color: var(--md-sys-color-on-surface); + display: flex; + align-items: center; + gap: 14px; + letter-spacing: -0.02em; +} + +.settings-page-title svg { + filter: drop-shadow(0 2px 8px color-mix(in srgb, var(--md-sys-color-primary) 40%, transparent)); +} + +.settings-page-subtitle { + font-size: 0.85rem; + color: var(--md-sys-color-on-surface-variant); + margin-top: 6px; + font-weight: 500; + opacity: 0.85; +} + +.sidebar-history-delete { + transition: color 0.2s, background-color 0.2s, opacity 0.2s; + border-radius: 4px; + opacity: 0.6; +} + +.sidebar-history-delete:hover { + color: var(--md-sys-color-error) !important; + background-color: rgba(239, 68, 68, 0.1) !important; + opacity: 1 !important; +} + +.sidebar-history-item:hover .sidebar-history-delete { + opacity: 1; +} + +/* Global modern scrollbars */ +::-webkit-scrollbar { + width: 6px; + height: 6px; +} +::-webkit-scrollbar-track { + background: transparent; +} +::-webkit-scrollbar-thumb { + background: color-mix(in srgb, var(--md-sys-color-outline) 25%, transparent); + border-radius: 99px; +} +::-webkit-scrollbar-thumb:hover { + background: color-mix(in srgb, var(--md-sys-color-outline) 45%, transparent); +} + +/* Firefox */ +* { + scrollbar-width: thin; + scrollbar-color: color-mix(in srgb, var(--md-sys-color-outline) 25%, transparent) transparent; +} + +.settings-expanded-content { + animation: fadeInSlide 0.3s var(--md-transition-easing); +} + diff --git a/app/frontend/src/App.jsx b/app/frontend/src/App.jsx index 817bfb1..dc92a01 100644 --- a/app/frontend/src/App.jsx +++ b/app/frontend/src/App.jsx @@ -3,8 +3,11 @@ import Sidebar from "./components/Sidebar"; import TopStatusBar from "./components/TopStatusBar"; import Generator from "./components/Generator"; import ModelManager from "./components/ModelManager"; -import ImageConstraints from "./components/ImageConstraints"; -import { cleanupCandidates, formatBytes, getCleanupCandidates, getDiagnostics, getHardwareSpecs, getHealth, getTelemetry, getBackendOptions, getBackendStatus, listGeneratedOutputs, stopServer } from "./services/api"; +import Settings from "./components/Settings"; +import TextChat from "./components/TextChat"; +import SpeechTranscriber from "./components/SpeechTranscriber"; +import TextToSpeech from "./components/TextToSpeech"; +import { cleanupCandidates, formatBytes, getCleanupCandidates, getDiagnostics, getHardwareSpecs, getHealth, getTelemetry, getBackendOptions, getBackendStatus, listGeneratedOutputs, listSpeechTranscriptions, deleteSpeechTranscription, listTtsOutputs, deleteTtsOutput, stopServer } from "./services/api"; import "./App.css"; function App() { @@ -55,6 +58,7 @@ function App() { const [activeModel, setActiveModel] = useState(null); const [serverRunning, setServerRunning] = useState(false); const [isStoppingServer, setIsStoppingServer] = useState(false); + const [isLlmLoaded, setIsLlmLoaded] = useState(false); const [backendOptions, setBackendOptions] = useState({ options: [{ id: "cpu", label: "CPU", available: true }], cudaAvailable: false, @@ -97,6 +101,8 @@ function App() { width: 1024, height: 1024, steps: 4, // Recommended default steps for Flux.1 Schnell/Lightning + npuSteps: 4, + standardSteps: 20, cfgScale: 1.0, // Recommended default CFG for Flux.1 Schnell sampler: "euler_a", seed: -1, @@ -111,6 +117,296 @@ function App() { backendType: "auto", }); + const [textSettings, setTextSettings] = useState(() => { + const saved = localStorage.getItem("textSettings"); + if (saved) { + try { + const parsed = JSON.parse(saved); + return { + ...parsed, + enableThinking: parsed.enableThinking === true, + maxTokens: !parsed.maxTokens || parsed.maxTokens === 384 ? 1024 : parsed.maxTokens, + responseTokenMode: parsed.responseTokenMode || "auto", + }; + } catch (_) {} + } + return { + contextSize: 0, + temperature: 0.7, + systemPrompt: "You are a helpful local AI assistant.", + threads: Math.max(4, Math.min(16, (navigator.hardwareConcurrency || 4) - 2)), + enableThinking: false, + // New performance settings + gpuLayers: -1, + maxTokens: 1024, + responseTokenMode: "auto", + topP: 0.95, + topK: 40, + minP: 0.05, + repeatPenalty: 1.1, + seed: null, + performanceProfile: "balanced", + flashAttn: true, + cacheTypeK: "q8_0", + cacheTypeV: "q8_0", + mlock: false, + mmap: true, + cachePrompt: true, + defragThold: 0.1, + batchSize: 512, + ubatchSize: 512, + preferredBackend: "auto", + }; + }); + + useEffect(() => { + localStorage.setItem("textSettings", JSON.stringify(textSettings)); + }, [textSettings]); + + const [speechSettings, setSpeechSettings] = useState(() => { + const saved = localStorage.getItem("speechSettings"); + if (saved) { + try { + const parsed = JSON.parse(saved); + return { + language: parsed.language || "auto", + threads: Math.max(1, Math.min(32, Number(parsed.threads) || Math.max(1, Math.min(8, navigator.hardwareConcurrency || 4)))), + backendPreference: ["auto", "vulkan", "metal", "cpu"].includes(parsed.backendPreference) ? parsed.backendPreference : "auto", + translate: parsed.translate === true, + }; + } catch (_) {} + } + return { + language: "auto", + threads: Math.max(1, Math.min(8, navigator.hardwareConcurrency || 4)), + backendPreference: "auto", + translate: false, + }; + }); + + useEffect(() => { + localStorage.setItem("speechSettings", JSON.stringify(speechSettings)); + }, [speechSettings]); + + const [ttsSettings, setTtsSettings] = useState(() => { + const saved = localStorage.getItem("ttsSettings"); + if (saved) { + try { + const parsed = JSON.parse(saved); + return { + model: parsed.model || "", + voice: parsed.voice || "af_heart", + speed: Math.max(0.5, Math.min(2, Number(parsed.speed) || 1)), + }; + } catch (_) {} + } + return { + model: "", + voice: "af_heart", + speed: 1, + }; + }); + + useEffect(() => { + localStorage.setItem("ttsSettings", JSON.stringify(ttsSettings)); + }, [ttsSettings]); + + // Lifted Chat History States + const [conversations, setConversations] = useState([]); + const [activeConversationId, setActiveConversationId] = useState(null); + const [showHistory, setShowHistory] = useState(false); // Default hide + const [sidebarVisible, setSidebarVisible] = useState(() => { + const saved = localStorage.getItem("sidebarVisible"); + return saved !== "false"; + }); + + useEffect(() => { + localStorage.setItem("sidebarVisible", String(sidebarVisible)); + }, [sidebarVisible]); + + const [fontSize, setFontSize] = useState(() => { + const saved = localStorage.getItem("fontSize"); + return saved ? Number(saved) : 16; + }); + + useEffect(() => { + document.documentElement.style.setProperty("--app-font-size", `${fontSize}px`); + localStorage.setItem("fontSize", String(fontSize)); + }, [fontSize]); + const [speechTranscriptions, setSpeechTranscriptions] = useState([]); + const [selectedSpeechTranscript, setSelectedSpeechTranscript] = useState(null); + const [showSpeechHistory, setShowSpeechHistory] = useState(false); + const [ttsOutputs, setTtsOutputs] = useState([]); + const [selectedTtsOutput, setSelectedTtsOutput] = useState(null); + const [showTtsHistory, setShowTtsHistory] = useState(false); + + useEffect(() => { + const saved = localStorage.getItem("chat_conversations"); + if (saved) { + try { + setConversations(JSON.parse(saved)); + } catch (_) { + localStorage.removeItem("chat_conversations"); + } + } + }, []); + + const sanitizeMessageForStorage = (message) => { + if (!Array.isArray(message?.content)) return message; + return { + ...message, + content: message.content.map((item) => { + if (item?.type !== "image_url") return item; + return { + type: "text", + text: "[Attached image omitted from saved chat history]", + }; + }), + }; + }; + + const sanitizeConversationForStorage = (conversation) => ({ + ...conversation, + messages: Array.isArray(conversation.messages) + ? conversation.messages.map(sanitizeMessageForStorage) + : [], + }); + + const persistConversations = (list) => { + const compactList = list.map(sanitizeConversationForStorage); + try { + localStorage.setItem("chat_conversations", JSON.stringify(compactList)); + } catch (err) { + console.warn("Could not save chat history:", err); + } + }; + + const saveConversationState = useCallback((id, msgs, modelName, newTitle = null) => { + setConversations((prev) => { + const list = [...prev]; + const idx = list.findIndex(c => c.id === id); + if (idx !== -1) { + list[idx] = { + ...list[idx], + messages: msgs, + timestamp: Date.now(), + model: modelName, + ...(newTitle ? { title: newTitle } : {}) + }; + } else { + list.unshift({ + id, + title: newTitle || "Chat Session", + model: modelName, + messages: msgs, + timestamp: Date.now() + }); + } + persistConversations(list); + return list; + }); + }, []); + + const handleDeleteConversation = useCallback((id, e) => { + if (e) e.stopPropagation(); + setConversations((prev) => { + const filtered = prev.filter((c) => c.id !== id); + persistConversations(filtered); + return filtered; + }); + if (activeConversationId === id) { + setActiveConversationId(null); + } + }, [activeConversationId]); + + const refreshSpeechTranscriptions = useCallback(async () => { + try { + const list = await listSpeechTranscriptions(); + setSpeechTranscriptions(list); + } catch (err) { + console.warn("Could not load speech transcriptions:", err); + setSpeechTranscriptions([]); + } + }, []); + + const handleDeleteSpeechTranscription = useCallback(async (item, e) => { + if (e) e.stopPropagation(); + const itemId = item.filename || item.metadata || item.textFile; + if (!itemId) return; + + const ok = await showConfirm({ + title: "Delete Transcription", + message: `Are you sure you want to delete "${item.displayName || item.sourceFilename || "this transcription"}"? This action cannot be undone.`, + confirmLabel: "Delete", + danger: true + }); + if (!ok) return; + + try { + await deleteSpeechTranscription(itemId); + + setSelectedSpeechTranscript((prev) => { + if (prev) { + const prevId = prev.filename || prev.metadata || prev.textFile; + if (prevId === itemId) return null; + } + return prev; + }); + + refreshSpeechTranscriptions(); + } catch (err) { + console.error("Failed to delete speech transcription:", err); + showAlert({ title: "Delete Failed", message: err.message || String(err), danger: true }); + } + }, [showConfirm, showAlert, setSelectedSpeechTranscript, refreshSpeechTranscriptions]); + + useEffect(() => { + refreshSpeechTranscriptions(); + }, [refreshSpeechTranscriptions]); + + const refreshTtsOutputs = useCallback(async () => { + try { + const list = await listTtsOutputs(); + setTtsOutputs(list); + } catch (err) { + console.warn("Could not load TTS outputs:", err); + setTtsOutputs([]); + } + }, []); + + const handleDeleteTtsOutput = useCallback(async (item, e) => { + if (e) e.stopPropagation(); + const itemId = item.filename || item.metadata; + if (!itemId) return; + + const ok = await showConfirm({ + title: "Delete TTS Output", + message: `Are you sure you want to delete "${item.displayName || item.audioFile || "this audio"}"? This action cannot be undone.`, + confirmLabel: "Delete", + danger: true + }); + if (!ok) return; + + try { + await deleteTtsOutput(itemId); + setSelectedTtsOutput((prev) => { + if (prev) { + const prevId = prev.filename || prev.metadata; + if (prevId === itemId) return null; + } + return prev; + }); + refreshTtsOutputs(); + } catch (err) { + console.error("Failed to delete TTS output:", err); + showAlert({ title: "Delete Failed", message: err.message || String(err), danger: true }); + } + }, [showConfirm, showAlert, refreshTtsOutputs]); + + useEffect(() => { + refreshTtsOutputs(); + }, [refreshTtsOutputs]); + // Load hardware specifications on mount useEffect(() => { async function loadSpecs() { @@ -126,6 +422,10 @@ function App() { backendType: prev.backendType === "auto" ? backendInfo.defaultBackendType || "cpu" : prev.backendType, useGpu: (prev.backendType === "auto" ? backendInfo.defaultBackendType : prev.backendType) !== "cpu", })); + setTextSettings((prev) => ({ + ...prev, + threads: prev.threads || hardware.cpu_cores_physical || 4 + })); } catch (err) { console.error("Error fetching hardware specs:", err); } @@ -277,10 +577,22 @@ function App() { useEffect(() => { if (activeModel) { const name = activeModel.toLowerCase(); - if (name.includes("flux") || name.includes("schnell")) { + if (name.includes("lcm-dreamshaper-v7-fp16")) { setConstraints((prev) => ({ ...prev, steps: 4, + npuSteps: 4, + cfgScale: 1.0, + width: 512, + height: 512, + backendType: "openvino-npu", + useGpu: true, + })); + } else if (name.includes("flux") || name.includes("schnell")) { + setConstraints((prev) => ({ + ...prev, + steps: 4, + standardSteps: 4, cfgScale: 1.0, width: 1024, height: 1024, @@ -289,6 +601,7 @@ function App() { setConstraints((prev) => ({ ...prev, steps: 4, + standardSteps: 4, cfgScale: 1.5, width: 1024, height: 1024, @@ -297,6 +610,7 @@ function App() { setConstraints((prev) => ({ ...prev, steps: 25, + standardSteps: 25, cfgScale: 7.0, width: 512, height: 512, @@ -305,6 +619,7 @@ function App() { setConstraints((prev) => ({ ...prev, steps: 20, + standardSteps: 20, cfgScale: 4.5, width: 1024, height: 1024, @@ -314,6 +629,7 @@ function App() { setConstraints((prev) => ({ ...prev, steps: 20, + standardSteps: 20, cfgScale: 7.0, width: 512, height: 512, @@ -322,10 +638,73 @@ function App() { } }, [activeModel]); - const activeTabContent = useMemo(() => { - switch (activeTab) { - case "generator": - return ( + // Tab contents are kept mounted to preserve loading/generation state when switching tabs + + const sidebarContent = useMemo(() => ( + + ), [sidebarVisible, activeTab, specs, conversations, activeConversationId, showHistory, handleDeleteConversation, speechTranscriptions, selectedSpeechTranscript, showSpeechHistory, handleDeleteSpeechTranscription, ttsOutputs, selectedTtsOutput, showTtsHistory, handleDeleteTtsOutput]); + + const handleStopServer = useCallback(async () => { + if (!serverRunning || isStoppingServer) return; + setIsStoppingServer(true); + try { + await stopServer(); + setServerRunning(false); + setActiveModel(null); + } catch (err) { + console.error("Failed to stop server:", err); + showAlert({ title: "Stop Server Failed", message: err.message || String(err), danger: true }); + } finally { + setIsStoppingServer(false); + } + }, [serverRunning, isStoppingServer, showAlert]); + + return ( +
+ {/* Sidebar with Navigation Rail & Specs */} + {sidebarContent} + + {/* Main Panel */} +
+ {/* Top telemetry specs chips bar */} + setSidebarVisible(!sidebarVisible)} + /> + + {/* Dynamic Workspace Container */} +
- ); - case "models": - return ( +
+ +
- ); - case "constraints": - return ( - + +
+ +
+ +
+ +
+ +
+ +
+ +
+ - ); - default: - return null; - } - }, [ - activeTab, - prompt, - negativePrompt, - constraints, - activeModel, - generatedImages, - isGenerating, - generationProgress, - serverRunning, - specs, - backendOptions, - showAlert, - showConfirm, - ]); - - const sidebarContent = useMemo(() => ( - - ), [activeTab, specs]); - - const handleStopServer = useCallback(async () => { - if (!serverRunning || isStoppingServer) return; - setIsStoppingServer(true); - try { - await stopServer(); - setServerRunning(false); - setActiveModel(null); - } catch (err) { - console.error("Failed to stop server:", err); - showAlert({ title: "Stop Server Failed", message: err.message || String(err), danger: true }); - } finally { - setIsStoppingServer(false); - } - }, [serverRunning, isStoppingServer, showAlert]); - - const readinessIssues = [ - ...(health?.stale ? ["Restart Local AI Image Generator so the local server loads the latest API."] : []), - ...(health?.issues || []), - ]; - const cleanupBytes = cleanupItems.reduce((sum, item) => sum + Number(item.sizeBytes || 0), 0); - const showReadinessPanel = Boolean(health && (readinessIssues.length > 0 || cleanupItems.length > 0)); - - return ( -
- {/* Sidebar with Navigation Rail & Specs */} - {sidebarContent} - - {/* Main Panel */} -
- {/* Top telemetry specs chips bar */} - - - {showReadinessPanel && ( -
0 ? "readiness-card-warning" : ""}`}> -
-
-

- {health?.stale ? "Restart Required" : readinessIssues.length > 0 ? "System Readiness" : "Safe Cleanup Available"} -

-

- {health?.stale - ? `Running server build: ${health.build || "unknown"}` - : readinessIssues.length > 0 - ? "Local AI Image Generator found setup items that may need attention." - : `${cleanupItems.length} temporary item${cleanupItems.length === 1 ? "" : "s"} can be cleaned (${formatBytes(cleanupBytes)}).`} -

-
-
- - - {cleanupItems.length > 0 && ( - - )} -
-
- {readinessIssues.length > 0 && ( -
- {readinessIssues.slice(0, 4).map((issue) => ( - {issue} - ))} -
- )} - {cleanupItems.length > 0 && ( -
- {cleanupItems.slice(0, 3).map((item) => ( - - {item.name} Β· {item.size} Β· {item.reason} - - ))} -
- )} -
- )} - - {/* Dynamic Workspace Container */} - {activeTabContent} +
{dialog && ( diff --git a/app/frontend/src/components/Generator.jsx b/app/frontend/src/components/Generator.jsx index e9e8e68..b0967ab 100644 --- a/app/frontend/src/components/Generator.jsx +++ b/app/frontend/src/components/Generator.jsx @@ -1,6 +1,19 @@ import React, { memo, useState, useRef, useCallback } from "react"; import { Sparkles, Download, Copy, RefreshCw, Check, Sliders, Trash2, ImagePlus } from "lucide-react"; -import { generateImage, startServer, stopServer, waitForServerReady, getBackendStatus, getGenerationProgress, saveGeneratedOutput, deleteGeneratedOutputs } from "../services/api"; +import { + generateImage, + startServer, + stopServer, + waitForServerReady, + getBackendStatus, + getGenerationProgress, + saveGeneratedOutput, + deleteGeneratedOutputs, + getLlmStatus, + listLlmModels, + startLlm, + chatWithLlm +} from "../services/api"; const GalleryItem = memo(({ img, idx, isSelected, onClick }) => { const handleClick = (e) => { @@ -39,6 +52,8 @@ function Generator({ setActiveTab, showAlert = async ({ message }) => window.alert(message), showConfirm = async ({ message }) => window.confirm(message), + specs, + textSettings, }) { const [outputImage, setOutputImage] = useState(null); const [outputSeed, setOutputSeed] = useState(null); @@ -56,10 +71,88 @@ function Generator({ const [isDecoding, setIsDecoding] = useState(false); const [selectedGalleryIndexes, setSelectedGalleryIndexes] = useState([]); const [baseImage, setBaseImage] = useState(null); + const [isEnhancing, setIsEnhancing] = useState(false); const timerRef = useRef(null); const abortControllerRef = useRef(null); const hasRealGenerationStepRef = useRef(false); + const handleEnhancePrompt = async () => { + if (!prompt.trim() || isEnhancing || isGenerating) return; + setIsEnhancing(true); + try { + const status = await getLlmStatus(); + let targetModel = status.settings?.model; + + if (!status.ready) { + const models = await listLlmModels(); + if (models.length === 0) { + showAlert({ + title: "No Text Models", + message: "You haven't downloaded any text models yet. Please go to the 'Model Manager' or 'Text Chat' tab to download a GGUF model first.", + danger: true + }); + setIsEnhancing(false); + return; + } + + const savedLlm = localStorage.getItem("selectedLlmModel") || models[0]?.filename; + targetModel = models.some(m => m.filename === savedLlm) ? savedLlm : models[0]?.filename; + + const confirm = await showConfirm({ + title: "Load Text Model?", + message: `Enhancing your prompt requires loading the local text model "${targetModel}". This will temporarily unload the image model from memory. Do you want to proceed?`, + confirmLabel: "Load & Enhance", + cancelLabel: "Cancel" + }); + + if (!confirm) { + setIsEnhancing(false); + return; + } + + const cores = textSettings?.threads || specs?.cpu_cores_physical || 4; + const context = textSettings?.contextSize || 2048; + await startLlm(targetModel, { + threads: cores, + contextSize: context, + gpuLayers: -1, + preferredBackend: textSettings?.preferredBackend + }); + } + + const systemPrompt = "You are a helpful assistant. Rewrite the user's image prompt to be more descriptive and detailed for image generation. Keep it under 80 words. Output ONLY the rewritten prompt, no explanation or introductory text."; + const messages = [ + { role: "system", content: systemPrompt }, + { role: "user", content: `Please expand this prompt: "${prompt}"` } + ]; + + const temp = textSettings?.temperature || 0.7; + const response = await chatWithLlm(messages, { temperature: temp, maxTokens: 128 }); + let cleanResponse = response.content.trim(); + if (cleanResponse.startsWith('"') && cleanResponse.endsWith('"')) { + cleanResponse = cleanResponse.slice(1, -1); + } + cleanResponse = cleanResponse.replace(/^Here is the expanded prompt:?\s*/i, ""); + cleanResponse = cleanResponse.replace(/^Enhanced prompt:?\s*/i, ""); + + setPrompt(cleanResponse); + } catch (err) { + showAlert({ + title: "Prompt Enhancement Failed", + message: err.message || String(err), + danger: true + }); + } finally { + setIsEnhancing(false); + } + }; + + React.useEffect(() => { + if (constraints.backendType === "apple-npu" || constraints.backendType === "openvino-npu") { + setBaseImage(null); + } + }, [constraints.backendType]); + const handleBaseImageSelect = (e) => { const file = e.target.files?.[0]; e.target.value = ""; @@ -91,14 +184,20 @@ function Generator({ const settings = status.settings; const currentModelName = settings.model ? settings.model.split(/[\\/]/).pop() : null; const targetModelName = activeModel ? activeModel.split(/[\\/]/).pop() : null; + const requestedBackend = constraints.backendType || (constraints.useGpu === false ? "cpu" : "auto"); + const currentBackend = settings.backendType || (settings.useGpu === false ? "cpu" : "auto"); + const shouldCompareModel = requestedBackend !== "openvino-npu"; + const isCoreMLBackend = requestedBackend === "apple-npu"; - if (currentModelName !== targetModelName || - parseInt(settings.steps) !== parseInt(constraints.steps) || - Math.abs(parseFloat(settings.cfgScale) - parseFloat(constraints.cfgScale)) > 0.05 || - settings.sampler !== constraints.sampler || - parseInt(settings.threads) !== parseInt(constraints.threads) || + if ((shouldCompareModel && currentModelName !== targetModelName) || + (!isCoreMLBackend && parseInt(settings.steps) !== parseInt(constraints.steps)) || + (!isCoreMLBackend && Math.abs(parseFloat(settings.cfgScale) - parseFloat(constraints.cfgScale)) > 0.05) || + (!isCoreMLBackend && settings.sampler !== constraints.sampler) || + (!isCoreMLBackend && parseInt(settings.threads) !== parseInt(constraints.threads)) || Boolean(settings.useGpu) !== (constraints.useGpu !== false) || - (settings.backendType || (settings.useGpu === false ? "cpu" : "auto")) !== (constraints.backendType || (constraints.useGpu === false ? "cpu" : "auto"))) { + parseInt(settings.width || 512) !== parseInt(constraints.width || 512) || + parseInt(settings.height || 512) !== parseInt(constraints.height || 512) || + currentBackend !== requestedBackend) { needsRestart = true; } } else { @@ -112,10 +211,10 @@ function Generator({ try { console.log("Settings out of sync, restarting backend..."); await startServer(activeModel, constraints); - - let isReady = false; + let isReady = false; let crashError = null; - for (let i = 0; i < 240; i++) { // Poll for up to 120 seconds (240 * 500ms) + const maxStartupPolls = (constraints.backendType === "openvino-npu" || constraints.backendType === "apple-npu") ? 1200 : 240; + for (let i = 0; i < maxStartupPolls; i++) { const status = await getBackendStatus(); if (status.loading) { setRestartLoadProgress({ @@ -136,7 +235,7 @@ function Generator({ crashError = status.error; break; } - if (!status.running && i > 3) { + if (!status.running && !status.loading?.active && i > 3) { crashError = "The backend process terminated immediately on startup."; break; } @@ -584,7 +683,7 @@ function Generator({
{/* Prompt Textarea */}
- +