diff --git a/app/frontend/src/App.css b/app/frontend/src/App.css
index 26b3263..43efee7 100644
--- a/app/frontend/src/App.css
+++ b/app/frontend/src/App.css
@@ -2271,27 +2271,461 @@ html[data-theme="dark"] .m3-segment-item:hover:not(.active):not(.disabled) {
overflow-y: auto;
}
-/* DeepThink button animations and interactions */
-.chat-composer-deepthink-btn:hover {
- background: rgba(99, 102, 241, 0.08) !important;
- color: var(--md-sys-color-primary) !important;
- border-color: color-mix(in srgb, var(--md-sys-color-primary) 50%, transparent) !important;
+/* ============================================================
+ PROFESSIONAL SETTINGS UI - Collapsible Cards, Tabs, Hardware Badge
+ ============================================================ */
+
+/* --- Section Tabs (Image | Text) --- */
+.settings-tabs-container {
+ display: flex;
+ justify-content: center;
+ margin-bottom: 24px;
}
-.chat-composer-deepthink-btn.active:hover {
- background: rgba(99, 102, 241, 0.22) !important;
+.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;
}
-@keyframes spin-slow {
+.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(--border-color);
+ backdrop-filter: var(--glass-blur);
+ -webkit-backdrop-filter: var(--glass-blur);
+ background: var(--md-sys-color-surface);
+ box-shadow: var(--glass-shadow);
+}
+
+.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);
+}
+
+.hardware-tier-badge.tier-mid::before {
+ background: linear-gradient(90deg, #3b82f6, #0ea5e9);
+}
+
+.hardware-tier-badge.tier-low::before {
+ background: linear-gradient(90deg, #f59e0b, #fbbf24);
+}
+
+.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;
+}
+
+.tier-high .hardware-tier-icon {
+ background: rgba(34, 197, 94, 0.12);
+}
+
+.tier-mid .hardware-tier-icon {
+ background: rgba(59, 130, 246, 0.12);
+}
+
+.tier-low .hardware-tier-icon {
+ background: rgba(245, 158, 11, 0.12);
+}
+
+.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;
+}
+
+.hardware-tier-specs {
+ font-size: 0.8rem;
+ color: var(--md-sys-color-on-surface-variant);
+ line-height: 1.4;
+}
+
+.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.75rem;
+ 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;
+}
+
+.hardware-tier-chip:hover {
+ background: var(--md-sys-color-primary-container);
+ color: var(--md-sys-color-on-primary-container);
+ border-color: var(--md-sys-color-primary);
+}
+
+/* --- 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: box-shadow 0.3s ease, border-color 0.3s ease;
+}
+
+.collapsible-card:hover {
+ box-shadow: 0 12px 40px rgba(15, 23, 42, 0.08);
+ border-color: rgba(15, 23, 42, 0.12);
+}
+
+html[data-theme="dark"] .collapsible-card:hover {
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25);
+ border-color: rgba(255, 255, 255, 0.08);
+}
+
+.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: var(--md-sys-color-primary-container);
+ color: var(--md-sys-color-primary);
+ flex-shrink: 0;
+}
+
+.collapsible-header-title {
+ font-size: 0.95rem;
+ font-weight: 600;
+ color: var(--md-sys-color-on-surface);
+}
+
+.collapsible-header-subtitle {
+ font-size: 0.75rem;
+ color: var(--md-sys-color-outline);
+ margin-top: 2px;
+}
+
+.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 {
- transform: rotate(0deg);
+ opacity: 0;
+ transform: translateY(-8px);
}
to {
- transform: rotate(360deg);
+ opacity: 1;
+ transform: translateY(0);
}
}
-.rotate-anim {
- animation: spin-slow 8s linear infinite;
+/* --- Settings Section Header --- */
+.settings-section-header {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 20px;
+ padding-bottom: 12px;
+ border-bottom: 1px solid var(--border-color);
+}
+
+.settings-section-title {
+ font-size: 1.1rem;
+ font-weight: 700;
+ color: var(--md-sys-color-on-surface);
+}
+
+.settings-section-count {
+ font-size: 0.75rem;
+ padding: 3px 10px;
+ border-radius: 999px;
+ background: var(--md-sys-color-primary-container);
+ color: var(--md-sys-color-on-primary-container);
+ font-weight: 600;
+}
+
+/* --- Settings Grid Layout --- */
+.settings-grid {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 0;
+ max-width: 800px;
+ margin: 0 auto;
+}
+
+@media (min-width: 900px) {
+ .settings-grid {
+ grid-template-columns: 1fr 1fr;
+ gap: 20px;
+ }
+}
+
+/* --- Premium Toggle Switch --- */
+.premium-toggle {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 12px 16px;
+ border-radius: 12px;
+ background: var(--md-sys-color-surface-variant);
+ border: 1px solid var(--border-color);
+ transition: all 0.2s ease;
+ cursor: pointer;
+}
+
+.premium-toggle:hover {
+ border-color: var(--md-sys-color-primary);
+ background: var(--md-sys-color-primary-container);
+}
+
+.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);
+}
+
+.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: 2px;
+}
+
+/* --- Settings Value Badge --- */
+.settings-value-badge {
+ font-size: 0.75rem;
+ padding: 3px 10px;
+ border-radius: 8px;
+ background: var(--md-sys-color-primary-container);
+ color: var(--md-sys-color-on-primary-container);
+ font-weight: 700;
+ font-variant-numeric: tabular-nums;
+}
+
+/* --- 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;
+}
+
+/* --- 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-tab {
+ padding: 8px 16px;
+ font-size: 0.85rem;
+ }
+}
+
+/* --- Settings Page Header --- */
+.settings-page-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 24px;
+ padding-bottom: 16px;
+ border-bottom: 1px solid var(--border-color);
+}
+
+.settings-page-title {
+ font-size: 1.4rem;
+ font-weight: 700;
+ color: var(--md-sys-color-on-surface);
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.settings-page-subtitle {
+ font-size: 0.85rem;
+ color: var(--md-sys-color-outline);
+ margin-top: 4px;
}
diff --git a/app/frontend/src/components/Settings.jsx b/app/frontend/src/components/Settings.jsx
index b918fda..2976d1a 100644
--- a/app/frontend/src/components/Settings.jsx
+++ b/app/frontend/src/components/Settings.jsx
@@ -1,5 +1,9 @@
-import React, { memo, useEffect, useState } from "react";
-import { Crop, Sliders, Cpu, Info, MessageSquare, SlidersHorizontal, Zap } from "lucide-react";
+import React, { memo, useEffect, useState, useCallback } from "react";
+import {
+ Crop, Sliders, Cpu, Info, MessageSquare, SlidersHorizontal, Zap,
+ ChevronDown, Image, Type, Settings2, Gauge, Brain, Sparkles,
+ Monitor, HardDrive, MemoryStick, Thermometer, Hash, Layers
+} from "lucide-react";
import { stopServer, formatBytes, getLlmStatus } from "../services/api";
const ASPECT_RATIOS = [
@@ -18,6 +22,146 @@ const isSD15OrCustomModel = (modelName) => {
return true;
};
+// ─── Collapsible Card Component ───
+function CollapsibleCard({ icon: Icon, title, subtitle, children, defaultExpanded = true, id, onToggle }) {
+ const [isExpanded, setIsExpanded] = useState(() => {
+ const saved = localStorage.getItem(`settings_card_${id}`);
+ return saved !== null ? saved === "true" : defaultExpanded;
+ });
+
+ const toggle = useCallback(() => {
+ const newState = !isExpanded;
+ setIsExpanded(newState);
+ localStorage.setItem(`settings_card_${id}`, String(newState));
+ onToggle?.(id, newState);
+ }, [isExpanded, id, onToggle]);
+
+ return (
+
+
+
+
+
+
+
+
{title}
+ {subtitle &&
{subtitle}
}
+
+
+
+
+ {isExpanded && (
+
+ {children}
+
+ )}
+
+ );
+}
+
+// ─── Hardware Tier Badge Component ───
+function HardwareTierBadge({ specs }) {
+ if (!specs?.tier) return null;
+
+ const tierConfig = {
+ high: { icon: "🚀", label: "High-End PC", color: "tier-high" },
+ mid: { icon: "⚖️", label: "Balanced PC", color: "tier-mid" },
+ low: { icon: "🥔", label: "Potato PC", color: "tier-low" },
+ };
+
+ const tier = tierConfig[specs.tier] || tierConfig.low;
+ const rec = specs.recommended_text_settings;
+
+ return (
+
+
+
{tier.icon}
+
+
{tier.label}
+
+ {specs.cpu_name} • {specs.cpu_cores_physical} cores • {specs.ram_total_gb}GB RAM
+ {specs.gpu_name && specs.gpu_name !== "Loading..." && ` • ${specs.gpu_name}`}
+ {specs.gpu_vram_gb > 0 && ` • ${specs.gpu_vram_gb}GB VRAM`}
+
+
+
+ {rec && (
+ <>
+
+
+ Ctx: {rec.contextSize}
+ Threads: {rec.threads}
+ KV: {rec.cacheTypeK}
+ Batch: {rec.batchSize}
+ Profile: {rec.performanceProfile}
+
+ >
+ )}
+
+ );
+}
+
+// ─── Section Tabs Component ───
+function SectionTabs({ activeTab, onTabChange }) {
+ const tabs = [
+ { id: "image", label: "Image Generation", icon: Image },
+ { id: "text", label: "Text Generation", icon: Type },
+ ];
+
+ return (
+
+
+ {tabs.map((tab) => {
+ const Icon = tab.icon;
+ const isActive = activeTab === tab.id;
+ return (
+ onTabChange(tab.id)}
+ role="tab"
+ aria-selected={isActive}
+ type="button"
+ >
+
+ {tab.label}
+
+ );
+ })}
+
+
+ );
+}
+
+// ─── Premium Toggle Component ───
+function PremiumToggle({ checked, onChange, label, description, icon: Icon }) {
+ return (
+
+ onChange(!checked)}
+ role="checkbox"
+ aria-checked={checked}
+ >
+ {checked && }
+
+
+
{label}
+ {description &&
{description}
}
+
+
+ );
+}
+
+// ─── Main Settings Component ───
function Settings({
constraints,
setConstraints,
@@ -39,24 +183,15 @@ function Settings({
cleanupSafeItems,
diagnosticsCopied,
}) {
- const isSD15OrCustom = activeModel ? isSD15OrCustomModel(activeModel) : false;
- const isOpenVinoNpu = constraints.backendType === "openvino-npu";
- const forceStandardMode = isSD15OrCustom && !isOpenVinoNpu;
- const availableBackends = backendOptions?.options?.length
- ? backendOptions.options
- : [{ id: "cpu", label: "CPU", available: true }];
- const isMac = availableBackends.some(b => b.id === "metal" || b.id === "apple-npu") ||
- (specs?.os_name && (specs.os_name.toLowerCase().includes("darwin") || specs.os_name.toLowerCase().includes("mac")));
-
- 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 && cleanupItems.length > 0)));
-
+ const [activeTab, setActiveTab] = useState(() => {
+ return localStorage.getItem("settings_active_tab") || "image";
+ });
const [llmStatus, setLlmStatus] = useState({ ready: false, settings: {} });
+ useEffect(() => {
+ localStorage.setItem("settings_active_tab", activeTab);
+ }, [activeTab]);
+
useEffect(() => {
let cancelled = false;
const fetchLlmStatus = async () => {
@@ -73,13 +208,12 @@ function Settings({
};
}, []);
+ const isSD15OrCustom = activeModel ? isSD15OrCustomModel(activeModel) : false;
+ const isOpenVinoNpu = constraints.backendType === "openvino-npu";
const supportsThinking = Boolean(llmStatus.ready && llmStatus.settings?.supportsThinking);
-
- useEffect(() => {
- if (isOpenVinoNpu && constraints.steps > 8) {
- setConstraints((prev) => ({ ...prev, steps: 8, npuSteps: 8 }));
- }
- }, [constraints.steps, isOpenVinoNpu, setConstraints]);
+ const availableBackends = backendOptions?.options?.length
+ ? backendOptions.options
+ : [{ id: "cpu", label: "CPU", available: true }];
const updateConstraint = (key, value) => {
setConstraints((prev) => ({
@@ -178,910 +312,563 @@ function Settings({
}));
};
- return (
-
-
-
Settings & Parameters
-
- Configure size, quality, and performance controls for local image and text models.
-
-
-
- {showReadinessPanel && (
-
0 ? "readiness-card-warning" : ""}`} style={{ marginBottom: "20px" }}>
-
-
-
- {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)}).`}
-
+ // ─── Image Settings Tab Content ───
+ const ImageSettingsContent = () => (
+ <>
+
+
+ {/* Size Mode Toggle */}
+
+
+ Resolution Mode
+
+ {constraints.width >= 1024 ? "SDXL" : "SD 1.5"}
+
-
-
- {isReadinessBusy ? "Checking" : "Refresh"}
-
-
- {diagnosticsCopied ? "Copied" : "Copy Diagnostics"}
-
- {(cleanupItems || []).length > 0 && (
-
- Clean {formatBytes(cleanupBytes)}
+
+ {["sd15", "sdxl"].map((mode) => (
+ = 1024 ? "sdxl" : "sd15") === mode ? "active" : ""}`}
+ onClick={() => {
+ const ratio = ASPECT_RATIOS.find(r => {
+ const rw = constraints.width >= 1024 ? r.sdxl_width : r.width;
+ const rh = constraints.height >= 1024 ? r.sdxl_height : r.height;
+ return Math.abs(rw - constraints.width) < 10 && Math.abs(rh - constraints.height) < 10;
+ })?.id || "1:1";
+ handleAspectRatioChange(ratio, mode);
+ }}
+ disabled={isSD15OrCustom && mode === "sdxl"}
+ >
+ {mode === "sd15" ? "512px Base" : "1024px HD"}
- )}
+ ))}
+
+ {isSD15OrCustom && (
+
+ Custom models use 512px base resolution for compatibility.
+
+ )}
+
+
+ {/* Aspect Ratio Buttons */}
+
+
+ Aspect Ratio
+
+
+ {ASPECT_RATIOS.map((ratio) => {
+ const isSDXL = constraints.width >= 1024 && !isSD15OrCustom;
+ const rw = isSDXL ? ratio.sdxl_width : ratio.width;
+ const rh = isSDXL ? ratio.sdxl_height : ratio.height;
+ const isActive = Math.abs(constraints.width - rw) < 10 && Math.abs(constraints.height - rh) < 10;
+ return (
+
handleAspectRatioChange(ratio.id, isSDXL ? "sdxl" : "sd15")}
+ disabled={isOpenVinoNpu && ratio.id !== "1:1"}
+ style={{ fontSize: "0.8rem", padding: "10px 4px", height: "auto" }}
+ >
+ {ratio.id}
+
+ {rw}×{rh}
+
+
+ );
+ })}
- {readinessIssues.length > 0 && (
-
- {readinessIssues.slice(0, 4).map((issue) => (
-
{issue}
+
+ {/* Custom Dimensions */}
+
+
+
+
+
+
+ {/* Steps Slider */}
+
+
+ Detail Steps
+ {constraints.steps}
+
+
updateConstraint("steps", parseInt(e.target.value))}
+ min="1"
+ max={isOpenVinoNpu ? "8" : "60"}
+ />
+
+ {isOpenVinoNpu
+ ? "LCM OpenVINO uses 1-8 fast inference steps."
+ : "More steps = sharper details, but takes longer."}
+
+
+
+ {/* Seed */}
+
+
Random Seed
+
+ updateConstraint("seed", parseInt(e.target.value) || -1)}
+ placeholder="-1 for random..."
+ style={{ flex: 1 }}
+ />
+ updateConstraint("seed", -1)}
+ >
+ Random
+
+
+
+
+
+
+
+
+
updateConstraint("vaeTiling", v)}
+ label="VAE Tiling"
+ description="Process image in tiles to save VRAM"
+ icon={Layers}
+ />
+ updateConstraint("vaeOnCpu", v)}
+ label="VAE on CPU"
+ description="Run decoder on CPU if GPU runs out of memory"
+ icon={Cpu}
+ />
+ updateConstraint("useFlashAttn", v)}
+ label="Flash Attention"
+ description="Faster attention with less memory"
+ icon={Zap}
+ />
+
+
+
+
+
+
+
+ Accelerator
+
+
+ {availableBackends.map((b) => (
+ handleBackendChange(b.id)}
+ style={{ flex: "1 1 auto", minWidth: "80px" }}
+ >
+ {b.label}
+
))}
- )}
- {(cleanupItems || []).length > 0 && (
-
- {cleanupItems.slice(0, 3).map((item) => (
-
- {item.name} · {item.size} · {item.reason}
-
+
+
+
+ >
+ );
+
+ // ─── Text Settings Tab Content ───
+ const TextSettingsContent = () => (
+ <>
+
+
+
+ System Prompt
+
+
+
+
+ Context Size
+ {textSettings.contextSize || 0}
+
+
updateTextSetting("contextSize", parseInt(e.target.value))}
+ min="0"
+ max="32768"
+ step="512"
+ />
+
+ 0 = Auto (model default). Larger = more memory used.
+
+
+
+
+
+
+
+
+
+ Temperature
+ {textSettings.temperature}
+
+
updateTextSetting("temperature", parseFloat(e.target.value))}
+ min="0"
+ max="2"
+ step="0.1"
+ />
+
+
+
+
+ Top P
+ {textSettings.topP}
+
+
updateTextSetting("topP", parseFloat(e.target.value))}
+ min="0"
+ max="1"
+ step="0.05"
+ />
+
+
+
+
+ Top K
+ {textSettings.topK}
+
+
updateTextSetting("topK", parseInt(e.target.value))}
+ min="1"
+ max="100"
+ step="1"
+ />
+
+
+
+
+ Min P
+ {textSettings.minP}
+
+
updateTextSetting("minP", parseFloat(e.target.value))}
+ min="0"
+ max="1"
+ step="0.01"
+ />
+
+
+
+
+ Repeat Penalty
+ {textSettings.repeatPenalty}
+
+
updateTextSetting("repeatPenalty", parseFloat(e.target.value))}
+ min="1"
+ max="2"
+ step="0.05"
+ />
+
+
+
+ Seed (-1 = Random)
+ updateTextSetting("seed", parseInt(e.target.value) || -1)}
+ placeholder="-1"
+ />
+
+
+
+
+
+
+
+
+ Profile
+
+
+ {["potato", "balanced", "high", "custom"].map((profile) => (
+ updateTextSetting("performanceProfile", profile)}
+ >
+ {profile.charAt(0).toUpperCase() + profile.slice(1)}
+
))}
+
+
+
+
+ CPU Threads
+ {textSettings.threads || 4}
+
+
updateTextSetting("threads", parseInt(e.target.value))}
+ min="1"
+ max={specs?.cpu_cores_logical || 16}
+ />
+
+
+
+
+ GPU Layers
+ {textSettings.gpuLayers === -1 ? "All" : textSettings.gpuLayers}
+
+
{
+ const val = parseInt(e.target.value);
+ updateTextSetting("gpuLayers", val >= 50 ? -1 : val);
+ }}
+ min="0"
+ max="50"
+ />
+
+ 50 = All layers on GPU. 0 = CPU only.
+
+
+
+
+
+ Batch Size
+ {textSettings.batchSize || 512}
+
+
updateTextSetting("batchSize", parseInt(e.target.value))}
+ min="64"
+ max="2048"
+ step="64"
+ />
+
+
+
+
+ KV Cache Type
+
+
+ {["q4_0", "q8_0", "f16"].map((type) => (
+ {
+ updateTextSetting("cacheTypeK", type);
+ updateTextSetting("cacheTypeV", type);
+ }}
+ >
+ {type.toUpperCase()}
+
+ ))}
+
+
+
+
+
+
+
+
updateTextSetting("enableThinking", v)}
+ label="DeepThink"
+ description={supportsThinking
+ ? "Show model's reasoning process"
+ : "Model does not support thinking"
+ }
+ icon={Brain}
+ />
+ {!supportsThinking && (
+
+
+ Current model does not support thinking. Load a reasoning model (e.g., DeepSeek-R1, QwQ) to enable.
+
)}
- )}
+
-
- {/* Left Column: Image Settings */}
-
- {/* Card 1: Picture Size & Shape */}
-
-
-
- 1. Image Size & Shape
-
-
- {isOpenVinoNpu ? (
-
-
-
- OpenVINO NPU Resolution: The NPU generates at its stable 512x512 resolution. HD mode produces a 1024x1024 output using high-quality Lanczos upscaling without recompiling the model.
-
-
- ) : isSD15OrCustom && (
-
-
-
- Graphics Memory Protection Active: Locked at 512x512 size for this model ({activeModel}). Creating larger images (like 1024x1024) is disabled to prevent your {specs && specs.gpu_name && !specs.gpu_name.includes("Loading") ? specs.gpu_name : "graphics processor"} from running out of memory and crashing.
-
+
+
+
+
+
+
+ {llmStatus.ready ? "Running" : "Stopped"}
+
+
+ {llmStatus.ready && (
+
+ Model: {llmStatus.settings?.model || "Unknown"}
+ Backend: {llmStatus.settings?.backendMode || "Unknown"}
+ Threads: {llmStatus.settings?.threads || "-"}
+ GPU Layers: {llmStatus.settings?.gpuLayers === -1 ? "All" : llmStatus.settings?.gpuLayers}
)}
-
-
- {/* Size switcher */}
-
-
AI Generation Engine Optimization
-
- {forceStandardMode ? (
-
- {isOpenVinoNpu ? "HD Upscale (1024px)" : "High Quality Mode (1024px)"}
-
- ) : (
-
= 1024 ? "active" : ""}`}
- onClick={() => {
- updateConstraint("width", 1024);
- updateConstraint("height", 1024);
- }}
- >
- {isOpenVinoNpu ? "HD Upscale (1024px)" : "High Quality Mode (1024px)"}
-
- )}
-
{
- updateConstraint("width", 512);
- updateConstraint("height", 512);
- }}
- >
- Standard Speed Mode (512px)
-
-
-
- {isOpenVinoNpu
- ? "*HD mode generates at 512x512 on the NPU, then upscales to 1024x1024. It improves output size, but does not add native diffusion detail."
- : isSD15OrCustom
- ? "*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."}
-
-
-
- {/* Aspect Ratio Buttons */}
-
-
Select Shape (Aspect Ratio)
-
- {ASPECT_RATIOS.map((ratio) => {
- const isActive = Math.abs((constraints.width / constraints.height) - (ratio.width / ratio.height)) < 0.1;
- const isUnsupportedOpenVinoShape = isOpenVinoNpu && ratio.id !== "1:1";
- return (
-
handleAspectRatioChange(ratio.id, forceStandardMode ? "sd15" : (constraints.width >= 1024 ? "sdxl" : "sd15"))}
- >
-
- {ratio.label}
-
- {isUnsupportedOpenVinoShape
- ? "Not yet supported on NPU"
- : forceStandardMode
- ? `Max: ${ratio.id === "1:1" ? "512x512" : ratio.id === "4:3" ? "512x384" : ratio.id === "16:9" ? "512x288" : "288x512"}`
- : ratio.desc}
-
-
- );
- })}
-
-
-
-
-
- {/* Card 2: Quality & Logic */}
-
-
-
- 2. Quality, Speed & Logic
-
-
-
- {/* Quality Steps Slider */}
-
-
- Detail Steps (Inference speed)
- {constraints.steps} steps
-
-
updateConstraint("steps", parseInt(e.target.value))}
- min="1"
- max={isOpenVinoNpu ? "8" : "60"}
- />
-
- {isOpenVinoNpu
- ? "LCM OpenVINO uses 1-8 fast inference steps. Progress is reported after each completed NPU step."
- : "The number of times the AI cleans up the image. More steps = sharper details, but takes longer."}
-
-
-
- {/* Random Seed DNA */}
-
-
Image Blueprint (DNA Seed)
-
- updateConstraint("seed", parseInt(e.target.value) || -1)}
- placeholder="-1 for a brand new image..."
- style={{ flex: 1, height: "40px" }}
- />
- updateConstraint("seed", -1)}
- style={{ height: "40px" }}
- >
- Random (-1)
-
-
-
-
-
-
- {/* Hardware Tier Badge */}
- {specs?.tier && (
-
-
-
-
-
- {specs.tier === "high" ? "🚀 High-End PC" :
- specs.tier === "mid" ? "⚖️ Balanced PC" :
- "🥔 Potato PC"}
-
-
- {specs.cpu_name} • {specs.cpu_cores_physical} cores • {specs.ram_total_gb}GB RAM
- {specs.gpu_name && specs.gpu_name !== "Loading..." && ` • ${specs.gpu_name}`}
- {specs.gpu_vram_gb > 0 && ` • ${specs.gpu_vram_gb}GB VRAM`}
-
-
-
- {specs.recommended_text_settings && (
-
-
- Recommended text settings:
-
-
-
- Ctx: {specs.recommended_text_settings.contextSize}
-
-
- Threads: {specs.recommended_text_settings.threads}
-
-
- KV: {specs.recommended_text_settings.cacheTypeK}
-
-
- Batch: {specs.recommended_text_settings.batchSize}
-
-
-
- )}
-
- )}
-
- {/* Card 3: Image Memory Optimizations */}
-
-
-
- 3. Image Optimizations
-
-
-
-
-
- updateConstraint("vaeTiling", e.target.checked)}
- style={{
- width: "16px",
- height: "16px",
- marginTop: "3px",
- accentColor: "var(--md-sys-color-primary)",
- cursor: "pointer"
- }}
- />
-
-
Enable VAE Tiling
-
- Builds the image in smaller, bite-sized sections. This heavily reduces graphics memory usage with no loss in speed. Highly recommended for computers with standard graphics cards.
-
-
-
-
-
- updateConstraint("vaeOnCpu", e.target.checked)}
- style={{
- width: "16px",
- height: "16px",
- marginTop: "3px",
- accentColor: "var(--md-sys-color-primary)",
- cursor: "pointer"
- }}
- />
-
-
Run VAE on CPU
-
- Performs the final image rendering step using your computer's main memory (RAM) instead of graphics memory. Saves graphics memory, but makes the final stage of creating the image slightly slower.
-
-
-
-
-
- updateConstraint("useFlashAttn", e.target.checked)}
- style={{
- width: "16px",
- height: "16px",
- marginTop: "3px",
- accentColor: "var(--md-sys-color-primary)",
- cursor: "pointer"
- }}
- />
-
-
Enable Flash Attention
-
- Accelerates generation using memory-efficient attention. On some specific Mac models or GPUs, this may cause a slight slowdown, so you can disable it if needed.
-
-
-
-
-
+
+ >
+ );
- {/* Right Column: Text Settings & System */}
+ return (
+
+ {/* Page Header */}
+
- {/* Card 4: Text Generation Settings (llama.cpp) */}
-
-
-
- 4. Text Generation Settings (GGUF)
-
-
-
- {/* System Prompt instructions */}
-
- Default System Instructions
-
-
- {/* Context size selector */}
-
- Context Window Limit
- updateTextSetting("contextSize", Number(e.target.value))}
- className="m3-input"
- style={{ marginTop: "6px", height: "40px", cursor: "pointer" }}
- >
- Auto-detect (Recommended)
- 2,048 tokens (Fast/Low Memory)
- 4,096 tokens (Balanced)
- 8,192 tokens (More history)
- 16,384 tokens (Deep context)
-
-
- Controls the maximum memory allocated for conversation history. Larger limits allow longer chats but use more RAM/VRAM.
-
-
-
- {/* Show Thinking Process toggle - only for models that support it */}
- {supportsThinking && (
-
-
- updateTextSetting("enableThinking", e.target.checked)}
- style={{
- width: "16px",
- height: "16px",
- marginTop: "3px",
- accentColor: "var(--md-sys-color-primary)",
- cursor: "pointer"
- }}
- />
-
-
Show Thinking Process
-
- Enables the collapsible dropdown for models that support reasoning/thinking process (e.g. DeepSeek-R1, Gemma-4-E2B). If turned off, reasoning is hidden and stripped from the conversation view.
-
-
-
-
- )}
- {!supportsThinking && llmStatus.ready && (
-
- Thinking not supported. The loaded model does not support reasoning/thinking output. DeepThink is only available for models like DeepSeek-R1, Qwen3, Gemma-4-E2B, and other reasoning-capable models.
-
- )}
-
- {/* Temperature slider */}
-
-
- Temperature (Creativity)
- {textSettings.temperature}
-
-
updateTextSetting("temperature", parseFloat(e.target.value))}
- min="0.1"
- max="2.0"
- step="0.05"
- />
-
- Lower temperature produces focused and coherent replies; higher temperature allows more creative and diverse responses.
-
-
-
- {/* CPU/GPU Mode Toggle */}
- {(specs?.gpu_name && !specs.gpu_name.includes("Loading") && specs.gpu_name !== "Unavailable") && (
-
-
- Text Generation Backend
-
- {(textSettings.gpuLayers === 0 || textSettings.gpuLayers === undefined) ? "CPU" : "GPU"}
-
-
-
-
updateTextSetting("gpuLayers", 0)}
- title="Run model entirely on CPU. Best for Intel Arc GPUs which have poor Vulkan performance."
- style={{ cursor: "pointer" }}
- >
-
- CPU
-
-
updateTextSetting("gpuLayers", -1)}
- title="Offload model layers to GPU for faster generation."
- style={{ cursor: "pointer" }}
- >
-
- GPU
-
-
-
- {(textSettings.gpuLayers === 0 || textSettings.gpuLayers === undefined) ? (
-
- CPU mode: Uses processor only. Recommended for Intel Arc GPUs which currently perform better on CPU with llama.cpp.
-
- ) : (
-
- GPU mode: Offloads model layers to graphics card. Best for NVIDIA/AMD GPUs. Intel Arc may be slower due to Vulkan driver limitations.
-
- )}
-
-
- )}
-
- {/* GPU Layers slider - only show when GPU mode is selected */}
- {(specs?.gpu_name && !specs.gpu_name.includes("Loading") && specs.gpu_name !== "Unavailable") && (textSettings.gpuLayers !== 0 && textSettings.gpuLayers !== undefined) && (
-
-
- GPU Layer Offload
-
- {textSettings.gpuLayers === -1 ? "All layers" : `${textSettings.gpuLayers} layers`}
-
-
-
{
- const val = parseInt(e.target.value);
- updateTextSetting("gpuLayers", val === 999 ? -1 : val);
- }}
- min="0"
- max="999"
- step="1"
- />
-
- 0 (CPU only)
- All (-1)
-
-
- More layers = faster but uses more VRAM. Start with "All" and reduce if you get out-of-memory errors.
- {textSettings.gpuLayers === -1 && " Currently all layers will be loaded into GPU memory."}
- {textSettings.gpuLayers !== -1 && textSettings.gpuLayers !== 0 && ` Currently ${textSettings.gpuLayers} layers on GPU, rest on CPU.`}
-
-
- )}
-
- {/* CPU-only fallback message when no GPU detected */}
- {(!specs?.gpu_name || specs.gpu_name.includes("Loading") || specs.gpu_name === "Unavailable") && (
-
- No GPU detected. Text generation will use CPU only. Install GPU drivers or restart if you have a dedicated graphics card.
-
- )}
-
- {/* Performance Profile Preset */}
-
- Performance Profile
- {
- const profile = e.target.value;
- updateTextSetting("performanceProfile", profile);
- // Auto-apply recommended settings for the profile
- if (profile === "potato") {
- updateTextSetting("contextSize", 2048);
- updateTextSetting("cacheTypeK", "q4_0");
- updateTextSetting("cacheTypeV", "q4_0");
- updateTextSetting("mlock", true);
- updateTextSetting("gpuLayers", 0);
- } else if (profile === "balanced") {
- updateTextSetting("contextSize", 4096);
- updateTextSetting("cacheTypeK", "q8_0");
- updateTextSetting("cacheTypeV", "q8_0");
- updateTextSetting("mlock", false);
- updateTextSetting("gpuLayers", -1);
- } else if (profile === "high") {
- updateTextSetting("contextSize", 8192);
- updateTextSetting("cacheTypeK", "q8_0");
- updateTextSetting("cacheTypeV", "q8_0");
- updateTextSetting("mlock", false);
- updateTextSetting("gpuLayers", -1);
- }
- }}
- className="m3-input"
- style={{ marginTop: "6px", height: "40px", cursor: "pointer" }}
- >
- 🥔 Potato PC (Low-end CPU, no GPU)
- ⚖️ Balanced (Mid-range GPU/CPU)
- 🚀 High-End (Fast GPU, 16GB+ VRAM)
- 🔧 Custom (Manual settings)
-
-
- One-click optimization for your hardware tier. Select "Custom" to fine-tune individual settings.
-
-
-
- {/* Advanced Settings Toggle */}
-
-
- Advanced Settings
-
-
-
- {/* CPU Threads */}
-
-
- CPU Threads
- {textSettings.threads || specs?.cpu_cores_physical || 4}
-
-
updateTextSetting("threads", parseInt(e.target.value))}
- min="1"
- max="64"
- step="1"
- />
-
- Lower = less oversaturation on GPU mode. Auto-detected: {specs?.cpu_cores_physical || 4} physical cores.
-
-
-
- {/* Max Tokens */}
-
-
- Max Tokens
- {textSettings.maxTokens || 1024}
-
-
updateTextSetting("maxTokens", parseInt(e.target.value))}
- min="128"
- max="4096"
- step="128"
- />
-
- Maximum tokens to generate per response.
-
-
-
- {/* Top P */}
-
-
- Top P
- {textSettings.topP || 0.95}
-
-
updateTextSetting("topP", parseFloat(e.target.value))}
- min="0"
- max="1"
- step="0.05"
- />
-
-
- {/* Top K */}
-
-
- Top K
- {textSettings.topK || 40}
-
-
updateTextSetting("topK", parseInt(e.target.value))}
- min="1"
- max="100"
- step="1"
- />
-
-
- {/* Min P */}
-
-
- Min P
- {textSettings.minP || 0.05}
-
-
updateTextSetting("minP", parseFloat(e.target.value))}
- min="0"
- max="1"
- step="0.01"
- />
-
-
- {/* Repeat Penalty */}
-
-
- Repeat Penalty
- {textSettings.repeatPenalty || 1.1}
-
-
updateTextSetting("repeatPenalty", parseFloat(e.target.value))}
- min="0.5"
- max="2.0"
- step="0.05"
- />
-
-
- {/* KV Cache Type K */}
-
- KV Cache Type K
- updateTextSetting("cacheTypeK", e.target.value)}
- className="m3-input"
- style={{ marginTop: "6px", height: "40px", cursor: "pointer" }}
- >
- f16 (Highest quality, most memory)
- q8_0 (Balanced, 2x memory reduction)
- q4_0 (Aggressive, 4x memory reduction)
-
-
-
- {/* KV Cache Type V */}
-
- KV Cache Type V
- updateTextSetting("cacheTypeV", e.target.value)}
- className="m3-input"
- style={{ marginTop: "6px", height: "40px", cursor: "pointer" }}
- >
- f16 (Highest quality, most memory)
- q8_0 (Balanced, 2x memory reduction)
- q4_0 (Aggressive, 4x memory reduction)
-
-
-
- {/* Flash Attention Toggle */}
-
-
- updateTextSetting("flashAttn", e.target.checked)}
- style={{ width: "16px", height: "16px", marginTop: "3px", accentColor: "var(--md-sys-color-primary)", cursor: "pointer" }}
- />
-
-
Flash Attention
-
- Memory-efficient attention for faster generation and lower VRAM usage.
-
-
-
-
-
- {/* Memory Lock (mlock) Toggle */}
-
-
- updateTextSetting("mlock", e.target.checked)}
- style={{ width: "16px", height: "16px", marginTop: "3px", accentColor: "var(--md-sys-color-primary)", cursor: "pointer" }}
- />
-
-
Memory Lock (mlock)
-
- Prevent OS from paging model to disk. Recommended for CPU mode.
-
-
-
-
-
- {/* Memory Map (mmap) Toggle */}
-
-
- updateTextSetting("mmap", e.target.checked)}
- style={{ width: "16px", height: "16px", marginTop: "3px", accentColor: "var(--md-sys-color-primary)", cursor: "pointer" }}
- />
-
-
Memory Map (mmap)
-
- Faster model loading via memory-mapped files.
-
-
-
-
-
- {/* Seed */}
-
- Seed (optional)
- updateTextSetting("seed", e.target.value ? parseInt(e.target.value) : null)}
- placeholder="Random"
- className="m3-input"
- style={{ marginTop: "6px", height: "40px" }}
- />
-
- Set for reproducible outputs. Leave empty for random.
-
-
-
-
-
- {/* NPU Detection Badge */}
- {specs?.npu?.detected && (
-
- Intel NPU detected. NPU text generation via OpenVINO/IPEX-LLM is on the roadmap for future updates.
-
- )}
-
+
+
+ Settings & Parameters
-
- {/* Card 5: System Settings (Generation Backend) */}
-
-
-
- 5. Image Generation Backend
-
-
-
-
-
Active Image Accelerator
-
- {availableBackends.map((backend) => (
-
handleBackendChange(backend.id)}
- title={backend.id === "cuda" ? "CUDA appears only when an NVIDIA CUDA backend is available." : undefined}
- style={{ cursor: "pointer" }}
- >
- {backend.label}
-
- ))}
-
-
- {isMac ? (
- "CPU is slow but safest. Metal uses your Mac's graphics card. Apple Neural Engine (NPU) is highly power-efficient and fast."
- ) : (
- "CPU is slow but safest. Vulkan works on supported GPUs. CUDA is shown only when NVIDIA CUDA support is available."
- )}
-
-
- {constraints.backendType === "cuda" && specs?.gpu_name && String(specs.gpu_name).toLowerCase().includes("gtx") && (
-
- Performance Alert: Your graphics card ({specs.gpu_name}) is a GTX-series GPU which lacks hardware Tensor Cores . Running in CUDA mode will be up to 3x slower. We strongly recommend switching to Vulkan GPU for optimal generation speed.
-
- )}
-
- {backendOptions?.unavailable?.length > 0 && (
-
- {backendOptions.unavailable.map((backend) => (
-
- {backend.label} unavailable: {backend.reason}
-
- ))}
-
- )}
-
-
+
+ Configure your AI models for optimal performance
+
+ {/* Tabs */}
+
+
+ {/* Hardware Tier Badge */}
+
+
+ {/* Tab Content */}
+
+ {activeTab === "image" && }
+ {activeTab === "text" && }
+
);
}