diff --git a/src-tauri/src/models/settings.rs b/src-tauri/src/models/settings.rs index 74c62fe..5953d4c 100644 --- a/src-tauri/src/models/settings.rs +++ b/src-tauri/src/models/settings.rs @@ -57,6 +57,7 @@ pub struct AppSettings { pub default_thinking: bool, pub first_run_completed: bool, pub check_for_updates: bool, + pub high_contrast: bool, pub language: Option, pub model_directory: Option, pub backend_working_directory: Option, @@ -93,6 +94,7 @@ impl Default for AppSettings { default_thinking: true, first_run_completed: false, check_for_updates: true, + high_contrast: false, language: None, model_directory: None, backend_working_directory: None, @@ -168,6 +170,7 @@ pub enum SettingKey { DefaultThinking, FirstRunCompleted, CheckForUpdates, + HighContrast, Language, ModelDirectory, BackendWorkingDirectory, @@ -188,6 +191,7 @@ impl SettingKey { "defaultThinking" => Ok(Self::DefaultThinking), "firstRunCompleted" => Ok(Self::FirstRunCompleted), "checkForUpdates" => Ok(Self::CheckForUpdates), + "highContrast" => Ok(Self::HighContrast), "language" => Ok(Self::Language), "modelDirectory" => Ok(Self::ModelDirectory), "backendWorkingDirectory" => Ok(Self::BackendWorkingDirectory), @@ -211,6 +215,7 @@ impl SettingKey { Self::DefaultThinking => "defaultThinking", Self::FirstRunCompleted => "firstRunCompleted", Self::CheckForUpdates => "checkForUpdates", + Self::HighContrast => "highContrast", Self::Language => "language", Self::ModelDirectory => "modelDirectory", Self::BackendWorkingDirectory => "backendWorkingDirectory", @@ -289,6 +294,11 @@ impl AppSettings { AppError::validation_failed(format!("invalid checkForUpdates value: {error}")) })?; } + SettingKey::HighContrast => { + self.high_contrast = serde_json::from_value(value).map_err(|error| { + AppError::validation_failed(format!("invalid highContrast value: {error}")) + })?; + } SettingKey::Language => { self.language = serde_json::from_value(value).map_err(|error| { AppError::validation_failed(format!("invalid language value: {error}")) @@ -353,6 +363,7 @@ impl AppSettings { "checkForUpdates", serde_json::to_string(&self.check_for_updates), ), + ("highContrast", serde_json::to_string(&self.high_contrast)), ("language", serde_json::to_string(&self.language)), ( "modelDirectory", @@ -404,6 +415,7 @@ mod tests { "defaultThinking", "firstRunCompleted", "checkForUpdates", + "highContrast", "language", "modelDirectory", "backendWorkingDirectory", diff --git a/src/app/App.tsx b/src/app/App.tsx index a4bff09..ddfc244 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -8,6 +8,13 @@ import * as api from "@/app/lib/api"; import { useGenerationStore } from "@/app/lib/store"; import { useAppMenuRuntime } from "@/app/runtime/menu-runtime"; +function useHighContrast() { + const highContrast = useGenerationStore((s) => s.highContrast); + useEffect(() => { + document.documentElement.toggleAttribute("data-high-contrast", highContrast); + }, [highContrast]); +} + function BootShell() { return (
@@ -17,6 +24,7 @@ function BootShell() { } function App() { + useHighContrast(); const { t } = useTranslation(); const { addToast } = useToast(); const hydrateFromPersistence = useGenerationStore((state) => state.hydrateFromPersistence); diff --git a/src/app/components/generation/GenerationPanel.tsx b/src/app/components/generation/GenerationPanel.tsx index ff6aabb..9198b37 100644 --- a/src/app/components/generation/GenerationPanel.tsx +++ b/src/app/components/generation/GenerationPanel.tsx @@ -59,9 +59,13 @@ type ToggleField = | "useCotLanguage" | "constrainedDecoding"; -function FieldError({ message }: { message?: string }) { +function FieldError({ message, id }: { message?: string; id?: string }) { if (!message) return null; - return

{message}

; + return ( +

+ {message} +

+ ); } function FieldLabel({ children }: { children: React.ReactNode }) { @@ -396,8 +400,9 @@ export function GenerationPanel() { value={form.prompt} onChange={handleTextFieldChange("prompt")} disabled={isBusy} + aria-describedby={validationErrors.prompt ? "gen-error-prompt" : undefined} /> - + {/* Model info - compact row */} diff --git a/src/app/components/generation/GenerationPanel/FormBody.tsx b/src/app/components/generation/GenerationPanel/FormBody.tsx index 40903ca..a4d5810 100644 --- a/src/app/components/generation/GenerationPanel/FormBody.tsx +++ b/src/app/components/generation/GenerationPanel/FormBody.tsx @@ -128,8 +128,9 @@ export function FormBody({ value={form.prompt} onChange={handleTextFieldChange("prompt")} disabled={isBusy} + aria-describedby={validationErrors.prompt ? "error-prompt" : undefined} /> - + {/* Model info - compact row */} @@ -223,8 +224,9 @@ export function FormBody({ value={form.lyrics} onChange={handleTextFieldChange("lyrics")} disabled={isBusy || form.instrumental} + aria-describedby={validationErrors.lyrics ? "error-lyrics" : undefined} /> - +
{/* Basic parameters - grouped */} @@ -244,8 +246,9 @@ export function FormBody({ value={form.durationSeconds} onChange={handleTextFieldChange("durationSeconds")} disabled={isBusy} + aria-describedby={validationErrors.durationSeconds ? "error-duration" : undefined} /> - +