diff --git a/.gitignore b/.gitignore deleted file mode 100644 index ab658d6..0000000 --- a/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# Python -__pycache__/ -*.py[cod] -*$py.class - -# Virtual Environment -venv/ -env/ - -# Distribution / Build -dist/ -build/ -runtime/ - -# Trash / Unused -_trash/ -_unused_files/ - -# IDEs -.vscode/ -.idea/ -.claude/ - -# Application Specific -models/ -recordings/ -*.log -settings.json diff --git a/docs/plans/2026-02-18-wcag-aaa-design.md b/docs/plans/2026-02-18-wcag-aaa-design.md deleted file mode 100644 index 72850b6..0000000 --- a/docs/plans/2026-02-18-wcag-aaa-design.md +++ /dev/null @@ -1,204 +0,0 @@ -# WCAG 2.2 AAA Compliance Design - -**Date:** 2026-02-18 -**Approach:** Style-System-First (cascade from SettingsStyle.qml, then per-component fixes) -**Goal:** Full WCAG 2.2 AAA compliance while preserving the current visual identity - ---- - -## Section 1: Color System Overhaul (SettingsStyle.qml) - -Update the central design token singleton to meet AAA contrast ratios. - -| Token | Current | New | Rationale | -|---|---|---|---| -| `accentPurple` | `#7000FF` (1.6:1) | `#B794F6` (7.2:1) | AAA normal text on #121212 | -| `textSecondary` | `#999999` (6.3:1) | `#ABABAB` (8.1:1) | AAA normal text | -| `borderSubtle` | `rgba(1,1,1,0.08)` | `rgba(1,1,1,0.22)` | 3:1 non-text contrast | - -New tokens to add: -- `textDisabled: "#808080"` (4.0:1 minimum for disabled states) -- `focusRingWidth: 2` (consistent focus indicator width) -- `minTargetSize: 24` (WCAG 2.5.8 minimum target) - ---- - -## Section 2: Component Default Fixes - -Bulk fixes that cascade from component-level defaults. - -**ModernSlider.qml:** -- Handle: 18x18 -> 24x24 (meets 24px target size) -- Track: 4px -> 6px height -- Add 2px focus ring on `activeFocus` -- Add `Accessible.role: Accessible.Slider` - -**ModernSwitch.qml:** -- Add "I" (on) / "O" (off) pip marks inside the thumb for non-color state indication -- Switch border to `borderSubtle` token -- Add `Accessible.role: Accessible.CheckBox` with state in name - -**ModernTextField.qml:** -- Placeholder: `#606060` -> `#808080` (3.7:1 minimum) -- Focus border: 1px -> 2px -- Add `Accessible.role: Accessible.EditableText` - -**ModernComboBox.qml:** -- Border -> `borderSubtle` token -- Focus border: 1px -> 2px -- Arrow icon: `#888888` -> `#ABABAB` -- Add `Accessible.role: Accessible.ComboBox` - -**ModernKeySequenceRecorder.qml:** -- Border -> `borderSubtle` token -- Focus border: 1px -> 2px -- "None" text: `#808080` -> `#ABABAB` -- Add `activeFocusOnTab: true` - -**GlowButton.qml:** -- Default text: `#9499b0` -> `#ABABAB` -- Border -> `borderSubtle` token - ---- - -## Section 3: Individual Component Attention - -Per-file hardcoded color fixes that can't be solved by token changes alone. - -**Settings.qml:** -- StatBox accent colors: `#bd93f9` -> `#CAA9FF`, `#ff79c6` -> `#FF8FD0`, `#ff5555` -> `#FF8A8A` -- Close button hover: `#ff4b4b` -> `#FF8A8A` -- Model info text: 10px -> 11px, remove `opacity: 0.6` -- INSTALLED tag: verify contrast of green-on-dark - -**Loader.qml:** -- Subtitle: `#80ffffff` -> `#ABABAB` -- Status text: remove `opacity: 0.8`, use full `textSecondary` -- Border: `#40ffffff` -> `rgba(1,1,1,0.22)` - -**Overlay.qml:** -- Border: `#40ffffff` -> `rgba(1,1,1,0.22)` (non-text contrast) - ---- - -## Section 4: Accessibility Properties - -Add `Accessible.name` and `Accessible.role` to every interactive and informational element. - -**Window-level:** -- Overlay.qml: `Accessible.name: "WhisperVoice Overlay"`, `title: "WhisperVoice"` -- Settings.qml: `Accessible.name: "WhisperVoice Settings"`, `title: "WhisperVoice Settings"` -- Loader.qml: `Accessible.name: "WhisperVoice Loading"`, `title: "WhisperVoice"` - -**Overlay.qml elements:** -- Mic button: `Accessible.name: ui.isRecording ? "Stop recording" : "Start recording"`, `Accessible.role: Accessible.Button` -- Status text: `Accessible.name: statusText.text`, `Accessible.role: Accessible.AlertMessage` -- Transcription text: `Accessible.name: transcriptionText.text`, `Accessible.role: Accessible.StaticText` - -**Settings.qml elements:** -- Nav items: `Accessible.name: modelData`, `Accessible.role: Accessible.Tab` -- Close button: `Accessible.name: "Close settings"`, `Accessible.role: Accessible.Button` -- Each settings section: delegate to ModernSettingsSection -- Tab content areas: `Accessible.role: Accessible.PageTab` - -**Shared components:** -- ModernSettingsSection: `Accessible.name: root.title + " settings group"`, `Accessible.role: Accessible.Grouping` -- ModernSettingsItem: `Accessible.name: root.label`, `Accessible.role: Accessible.Row` - ---- - -## Section 5: Keyboard Navigation - -Make every interactive element operable via keyboard alone. - -**Overlay.qml mic button:** -- Add `activeFocusOnTab: true` -- Add `Keys.onReturnPressed` / `Keys.onSpacePressed` -> toggle recording -- Add visible 2px focus ring using `activeFocus` state - -**Settings.qml nav sidebar:** -- Add `activeFocusOnTab: true` to each nav item -- Add `Keys.onReturnPressed` / `Keys.onSpacePressed` -> select tab -- Add `Keys.onUpPressed` / `Keys.onDownPressed` -> move between tabs -- Visible focus ring on each nav item - -**Settings.qml close button:** -- Add `activeFocusOnTab: true` -- Add `Keys.onReturnPressed` / `Keys.onSpacePressed` -> close - -**All custom controls:** -- Ensure `activeFocusOnTab: true` on ModernSlider, ModernSwitch, ModernComboBox, ModernKeySequenceRecorder, GlowButton -- 2px focus ring visible when `activeFocus` is true - ---- - -## Section 6: Reduced Motion Support - -Three-layer system: OS detection, user toggle, animation conditionals. - -**Config layer (`config.py`):** -- Add `"reduce_motion": false` to DEFAULT_SETTINGS - -**OS detection (`main.py`):** -- On Windows startup, call `SystemParametersInfo(SPI_GETCLIENTAREAANIMATION)` via ctypes -- If OS says reduce motion, set config `reduce_motion: true` (user can override) - -**Bridge layer (`bridge.py`):** -- Add `reduceMotion` Q_PROPERTY (bool, notify signal) -- Reads from config, exposed to QML as `ui.reduceMotion` - -**QML conditionals:** - -Overlay.qml animations (6 total): -- Shimmer: `running: !ui.reduceMotion` -- Gradient blobs shader: `visible: !ui.reduceMotion`, static fallback color -- Glow shader: `visible: !ui.reduceMotion` -- CRT shader: `visible: !ui.reduceMotion` -- Particle emitter: `enabled: !ui.reduceMotion` -- Rainbow wave shader: `visible: !ui.reduceMotion` - -Loader.qml animations (2 total): -- Icon pulse: `running: !ui.reduceMotion` -- Progress shimmer: `running: !ui.reduceMotion` - -**UI toggle:** -- Add "Reduce Motion" toggle in Settings.qml Visuals tab -- Bound to config `reduce_motion` via bridge - ---- - -## Section 7: Remaining WCAG AAA Criteria - -**7a. Status Messages (4.1.3):** -- Overlay status label gets `Accessible.role: Accessible.AlertMessage` (covered in Section 4) - -**7b. Text Scaling (1.4.4, 1.4.10):** -- Already handled by existing `ui_scale` config property. No changes needed. - -**7c. Page Titles (2.4.2):** -- Window titles set in Section 4 window-level properties. - -**7d. Error Identification (3.3.1):** -- Not applicable. No user-submitted forms with validation. - ---- - -## Files Changed (Summary) - -| File | Changes | -|---|---| -| `src/ui/qml/SettingsStyle.qml` | Token updates + new tokens | -| `src/ui/qml/Overlay.qml` | Colors, accessibility, keyboard, reduced motion | -| `src/ui/qml/Settings.qml` | Colors, accessibility, keyboard, reduce motion toggle | -| `src/ui/qml/Loader.qml` | Colors, accessibility, reduced motion | -| `src/ui/qml/ModernSlider.qml` | Size, focus ring, accessibility | -| `src/ui/qml/ModernSwitch.qml` | I/O marks, border, accessibility | -| `src/ui/qml/ModernTextField.qml` | Placeholder color, focus, accessibility | -| `src/ui/qml/ModernComboBox.qml` | Border, arrow color, focus, accessibility | -| `src/ui/qml/ModernKeySequenceRecorder.qml` | Border, colors, focus, accessibility | -| `src/ui/qml/GlowButton.qml` | Text color, border, accessibility | -| `src/ui/qml/ModernSettingsSection.qml` | Accessibility properties | -| `src/ui/qml/ModernSettingsItem.qml` | Accessibility properties | -| `src/core/config.py` | Add `reduce_motion` default | -| `src/ui/bridge.py` | Add `reduceMotion` property | -| `main.py` | Windows reduced-motion OS detection | diff --git a/docs/plans/2026-02-18-wcag-aaa-implementation.md b/docs/plans/2026-02-18-wcag-aaa-implementation.md deleted file mode 100644 index 85d64c6..0000000 --- a/docs/plans/2026-02-18-wcag-aaa-implementation.md +++ /dev/null @@ -1,1087 +0,0 @@ -# WCAG 2.2 AAA Implementation Plan - -> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. - -**Goal:** Bring WhisperVoice to full WCAG 2.2 AAA compliance while preserving the existing visual identity. - -**Architecture:** Style-system-first approach. Update the central `SettingsStyle.qml` design token singleton first so color/contrast fixes cascade to all consumers. Then fix per-component issues (sizes, focus rings, I/O marks). Then add accessibility properties, keyboard navigation, and reduced-motion support through the Python bridge layer. - -**Tech Stack:** PySide6/QML (Qt 6), Python 3.x, ctypes (Windows API for reduced motion detection) - ---- - -### Task 1: Update SettingsStyle.qml Design Tokens - -**Files:** -- Modify: `src/ui/qml/SettingsStyle.qml` (entire file, 25 lines) - -**Step 1: Edit SettingsStyle.qml** - -Replace the entire content with updated AAA-compliant tokens: - -```qml -import QtQuick - -pragma Singleton - -QtObject { - // Colors - readonly property color background: "#F2121212" // Deep Obsidian with 95% opacity - readonly property color surfaceCard: "#1A1A1A" // Layer 1 - readonly property color surfaceHover: "#2A2A2A" // Layer 2 - readonly property color borderSubtle: Qt.rgba(1, 1, 1, 0.22) // WCAG 3:1 non-text contrast - - readonly property color textPrimary: "#FAFAFA" - readonly property color textSecondary: "#ABABAB" // WCAG AAA 8.1:1 on #121212 - readonly property color textDisabled: "#808080" // 4.0:1 minimum for disabled states - - readonly property color accentPurple: "#B794F6" // WCAG AAA 7.2:1 on #121212 - readonly property color accentCyan: "#00F2FF" - - // Configurable active accent - property color accent: accentPurple - - // Dimensions - readonly property int cardRadius: 16 - readonly property int itemRadius: 8 - readonly property int itemHeight: 60 - - // Accessibility - readonly property int focusRingWidth: 2 - readonly property int minTargetSize: 24 -} -``` - -**What changed:** -- `accentPurple`: `#7000FF` -> `#B794F6` (1.6:1 -> 7.2:1 contrast ratio) -- `textSecondary`: `#999999` -> `#ABABAB` (6.3:1 -> 8.1:1) -- `borderSubtle`: `rgba(1,1,1,0.08)` -> `rgba(1,1,1,0.22)` (3:1 non-text) -- New: `textDisabled`, `focusRingWidth`, `minTargetSize` - -**Step 2: Verify the app launches without errors** - -Run: `cd "D:/!!! SYSTEM DATA !!!/Desktop/python crap/whisper_voice" && python main.py` - -Expected: App loads, Loader appears with updated accent colors. Settings opens with lighter purple accents. No QML errors in console. - -**Step 3: Commit** - -```bash -git add src/ui/qml/SettingsStyle.qml -git commit -m "WCAG: Update design tokens for AAA contrast compliance" -``` - ---- - -### Task 2: Fix ModernSlider.qml (Size + Focus Ring + Accessibility) - -**Files:** -- Modify: `src/ui/qml/ModernSlider.qml` (entire file, 61 lines) - -**Step 1: Edit ModernSlider.qml** - -Replace the entire content: - -```qml -import QtQuick -import QtQuick.Controls -import QtQuick.Effects - -Slider { - id: control - - Accessible.role: Accessible.Slider - Accessible.name: control.value.toString() - activeFocusOnTab: true - - background: Rectangle { - x: control.leftPadding - y: control.topPadding + control.availableHeight / 2 - height / 2 - implicitWidth: 200 - implicitHeight: 6 - width: control.availableWidth - height: implicitHeight - radius: 3 - color: "#2d2d3d" - - Rectangle { - width: control.visualPosition * parent.width - height: parent.height - color: SettingsStyle.accent - radius: 3 - } - } - - handle: Item { - x: control.leftPadding + control.visualPosition * (control.availableWidth - width) - y: control.topPadding + control.availableHeight / 2 - height / 2 - implicitWidth: SettingsStyle.minTargetSize - implicitHeight: SettingsStyle.minTargetSize - - // Focus ring - Rectangle { - anchors.centerIn: parent - width: parent.width + SettingsStyle.focusRingWidth * 2 + 2 - height: width - radius: width / 2 - color: "transparent" - border.width: SettingsStyle.focusRingWidth - border.color: SettingsStyle.accent - visible: control.activeFocus - } - - Rectangle { - anchors.fill: parent - radius: width / 2 - color: "white" - border.color: SettingsStyle.accent - border.width: 2 - - layer.enabled: control.pressed - layer.effect: MultiEffect { - blurEnabled: true - blur: 0.5 - shadowEnabled: true - shadowColor: SettingsStyle.accent - } - } - } - - // Value Readout - Text { - anchors.right: parent.left - anchors.rightMargin: 12 - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignRight - - text: { - var val = control.value - return (val % 1 === 0) ? val.toFixed(0) : val.toFixed(1) - } - - color: SettingsStyle.textSecondary - font.family: "JetBrains Mono" - font.pixelSize: 12 - font.weight: Font.Medium - } -} -``` - -**What changed:** -- Handle: `implicitWidth/Height: 18` -> `SettingsStyle.minTargetSize` (24px) -- Track: `implicitHeight: 4` -> `6`, radius `2` -> `3` -- Added `Accessible.role: Accessible.Slider`, `Accessible.name` -- Added `activeFocusOnTab: true` -- Added focus ring Rectangle that shows on `control.activeFocus` -- Handle is now an Item wrapping the circle + focus ring (avoids clipping) - -**Step 2: Verify** - -Run the app, open Settings, check sliders render with larger handles. Tab to a slider - focus ring should appear. - -**Step 3: Commit** - -```bash -git add src/ui/qml/ModernSlider.qml -git commit -m "WCAG: Slider - 24px target, focus ring, accessible role" -``` - ---- - -### Task 3: Fix ModernSwitch.qml (I/O Marks + Border + Accessibility) - -**Files:** -- Modify: `src/ui/qml/ModernSwitch.qml` (entire file, 40 lines) - -**Step 1: Edit ModernSwitch.qml** - -Replace the entire content: - -```qml -import QtQuick -import QtQuick.Controls - -Switch { - id: control - - Accessible.role: Accessible.CheckBox - Accessible.name: control.text + (control.checked ? " on" : " off") - activeFocusOnTab: true - - indicator: Rectangle { - implicitWidth: 44 - implicitHeight: 24 - x: control.leftPadding - y: parent.height / 2 - height / 2 - radius: 12 - color: control.checked ? SettingsStyle.accent : "#2d2d3d" - border.color: control.checked ? SettingsStyle.accent : SettingsStyle.borderSubtle - border.width: control.activeFocus ? SettingsStyle.focusRingWidth : 1 - - Behavior on color { ColorAnimation { duration: 200 } } - Behavior on border.color { ColorAnimation { duration: 200 } } - - Rectangle { - x: control.checked ? parent.width - width - 3 : 3 - y: 3 - width: 18 - height: 18 - radius: 9 - color: "white" - - Behavior on x { - NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } - } - - // I/O pip marks for non-color state indication - Text { - anchors.centerIn: parent - text: control.checked ? "I" : "O" - font.pixelSize: 9 - font.bold: true - color: control.checked ? SettingsStyle.accent : "#666666" - } - } - } - - contentItem: Text { - text: control.text - font: control.font - opacity: enabled ? 1.0 : 0.3 - color: "white" - verticalAlignment: Text.AlignVCenter - leftPadding: control.indicator.width + control.spacing - } -} -``` - -**What changed:** -- Added `Accessible.role: Accessible.CheckBox` with state in name -- Added `activeFocusOnTab: true` -- Border: `"#3d3d4d"` -> `SettingsStyle.borderSubtle` token -- Focus: `border.width` switches to `focusRingWidth` on `activeFocus` -- Added "I"/"O" Text inside the thumb circle for non-color state indication - -**Step 2: Verify** - -Open Settings, toggle switches. "I" shows when on, "O" when off. Tab to switches - border thickens. - -**Step 3: Commit** - -```bash -git add src/ui/qml/ModernSwitch.qml -git commit -m "WCAG: Switch - I/O marks, border token, focus ring, accessible role" -``` - ---- - -### Task 4: Fix ModernTextField.qml (Placeholder + Focus + Accessibility) - -**Files:** -- Modify: `src/ui/qml/ModernTextField.qml` (entire file, 27 lines) - -**Step 1: Edit ModernTextField.qml** - -Replace the entire content: - -```qml -import QtQuick -import QtQuick.Controls - -TextField { - id: control - - property color accentColor: "#00f2ff" - property color bgColor: "#1a1a20" - - Accessible.role: Accessible.EditableText - Accessible.name: control.placeholderText || "Text input" - - placeholderTextColor: SettingsStyle.textDisabled - color: "#ffffff" - font.family: "JetBrains Mono" - font.pixelSize: 14 - selectedTextColor: "#000000" - selectionColor: accentColor - - background: Rectangle { - implicitWidth: 200 - implicitHeight: 40 - color: control.bgColor - border.color: control.activeFocus ? control.accentColor : SettingsStyle.borderSubtle - border.width: control.activeFocus ? SettingsStyle.focusRingWidth : 1 - radius: 6 - - Behavior on border.color { ColorAnimation { duration: 150 } } - } -} -``` - -**What changed:** -- `placeholderTextColor`: `"#606060"` -> `SettingsStyle.textDisabled` (#808080, 3.7:1) -- Border default: `"#40ffffff"` -> `SettingsStyle.borderSubtle` -- Focus `border.width`: `1` -> `SettingsStyle.focusRingWidth` (2) -- Added `Accessible.role: Accessible.EditableText` - -**Step 2: Verify** - -Open Settings > AI Engine > Custom Prompt field. Placeholder text should be lighter. Focus border thicker. - -**Step 3: Commit** - -```bash -git add src/ui/qml/ModernTextField.qml -git commit -m "WCAG: TextField - placeholder contrast, focus ring, accessible role" -``` - ---- - -### Task 5: Fix ModernComboBox.qml (Border + Arrow + Focus + Accessibility) - -**Files:** -- Modify: `src/ui/qml/ModernComboBox.qml:88-98` (background section) -- Modify: `src/ui/qml/ModernComboBox.qml:65-73` (indicator paint) - -**Step 1: Update background border** - -In `ModernComboBox.qml`, change the background Rectangle (line 88-98): - -```qml - background: Rectangle { - implicitWidth: 140 - implicitHeight: 40 - color: control.bgColor - border.color: control.pressed || control.activeFocus ? control.accentColor : SettingsStyle.borderSubtle - border.width: control.activeFocus ? SettingsStyle.focusRingWidth : 1 - radius: 6 - - Behavior on border.color { ColorAnimation { duration: 150 } } - } -``` - -**What changed:** -- Default border: `"#40ffffff"` -> `SettingsStyle.borderSubtle` -- Focus `border.width`: `1` -> `SettingsStyle.focusRingWidth` (2) - -**Step 2: Update arrow indicator color** - -In the `onPaint` handler (line 71), change the non-pressed fill color: - -Old: `context.fillStyle = control.pressed ? control.accentColor : "#888888";` -New: `context.fillStyle = control.pressed ? control.accentColor : "#ABABAB";` - -**Step 3: Add accessibility properties** - -After line 11 (`property color popupColor: "#252530"`), add: - -```qml - Accessible.role: Accessible.ComboBox - Accessible.name: control.displayText -``` - -**Step 4: Also update popup border** - -In the popup background (line 115-119), change: - -Old: `border.color: "#40ffffff"` -New: `border.color: SettingsStyle.borderSubtle` - -**Step 5: Verify** - -Open Settings, check combo boxes render with visible borders. Arrow icon is brighter. - -**Step 6: Commit** - -```bash -git add src/ui/qml/ModernComboBox.qml -git commit -m "WCAG: ComboBox - border token, arrow contrast, focus ring, accessible role" -``` - ---- - -### Task 6: Fix ModernKeySequenceRecorder.qml (Border + Colors + Focus + Accessibility) - -**Files:** -- Modify: `src/ui/qml/ModernKeySequenceRecorder.qml` (lines 10-11, 29, plus add new properties) - -**Step 1: Update border color** - -Line 11, change: - -Old: `border.color: activeFocus || recording ? SettingsStyle.accent : "#40ffffff"` -New: `border.color: activeFocus || recording ? SettingsStyle.accent : SettingsStyle.borderSubtle` - -**Step 2: Update border width for focus** - -Add after `border.color` line: - -Add to existing line 10: change from `border.width: 1` to: -``` - border.width: (activeFocus || recording) ? SettingsStyle.focusRingWidth : 1 -``` - -**Step 3: Update "None" text color** - -Line 29, the color for empty state. Change: - -Old: `color: control.recording ? SettingsStyle.accent : (control.currentSequence ? "#ffffff" : "#808080")` -New: `color: control.recording ? SettingsStyle.accent : (control.currentSequence ? "#ffffff" : "#ABABAB")` - -**Step 4: Add accessibility and tab focus** - -After line 3 (`Rectangle {`), inside the root Rectangle, add: - -```qml - activeFocusOnTab: true - Accessible.role: Accessible.Button - Accessible.name: control.currentSequence ? "Hotkey: " + control.currentSequence + ". Click to change" : "No hotkey set. Click to record" -``` - -Note: `activeFocusOnTab: true` is critical since the recorder uses `forceActiveFocus()` on click but needs to also be reachable by Tab. - -**Step 5: Verify** - -Open Settings > General tab. Hotkey recorders should have visible borders, "None" text brighter. Tab to recorders. - -**Step 6: Commit** - -```bash -git add src/ui/qml/ModernKeySequenceRecorder.qml -git commit -m "WCAG: KeySequenceRecorder - border token, focus ring, accessible role" -``` - ---- - -### Task 7: Fix GlowButton.qml (Text Color + Border + Accessibility) - -**Files:** -- Modify: `src/ui/qml/GlowButton.qml` (lines 14, 28, plus add new properties) - -**Step 1: Update default text color** - -Line 14, change: - -Old: `color: control.hovered ? "white" : "#9499b0"` -New: `color: control.hovered ? "white" : "#ABABAB"` - -**Step 2: Update default border color** - -Line 28, change: - -Old: `border.color: control.hovered ? control.accentColor : Qt.rgba(1, 1, 1, 0.1)` -New: `border.color: control.hovered ? control.accentColor : SettingsStyle.borderSubtle` - -**Step 3: Add accessibility and focus ring** - -After line 8 (`property color accentColor: "#00f2ff"`), add: - -```qml - Accessible.role: Accessible.Button - Accessible.name: control.text - activeFocusOnTab: true -``` - -Also update the background `border.width` (line 29): - -Old: `border.width: 1` -New: `border.width: control.activeFocus ? SettingsStyle.focusRingWidth : 1` - -**Step 4: Verify** - -Check any GlowButton instances in the app (Download button uses Button directly, so GlowButton may only appear if used elsewhere — verify usage). - -**Step 5: Commit** - -```bash -git add src/ui/qml/GlowButton.qml -git commit -m "WCAG: GlowButton - text contrast, border token, focus ring, accessible role" -``` - ---- - -### Task 8: Fix ModernSettingsSection.qml + ModernSettingsItem.qml (Accessibility) - -**Files:** -- Modify: `src/ui/qml/ModernSettingsSection.qml` (add 2 lines) -- Modify: `src/ui/qml/ModernSettingsItem.qml` (add 2 lines) - -**Step 1: Add accessibility to ModernSettingsSection** - -After line 8 (`property string title: ""`), add: - -```qml - Accessible.name: root.title + " settings group" - Accessible.role: Accessible.Grouping -``` - -**Step 2: Add accessibility to ModernSettingsItem** - -After line 20 (`property bool showSeparator: true`), add: - -```qml - Accessible.name: root.label - Accessible.role: Accessible.Row -``` - -**Step 3: Verify** - -Open Settings. Use Windows Narrator or Accessibility Insights to confirm sections and items are announced with their labels. - -**Step 4: Commit** - -```bash -git add src/ui/qml/ModernSettingsSection.qml src/ui/qml/ModernSettingsItem.qml -git commit -m "WCAG: Add accessible name/role to settings sections and items" -``` - ---- - -### Task 9: Fix Settings.qml (Hardcoded Colors + Keyboard Nav + Accessibility) - -This is the largest file (1116 lines). Multiple targeted edits. - -**Files:** -- Modify: `src/ui/qml/Settings.qml` - -**Step 1: Update window title** - -Line 15, change: - -Old: `title: "Settings"` -New: `title: "WhisperVoice Settings"` - -**Step 2: Add window-level accessibility** - -After line 15, add: - -```qml - Accessible.name: "WhisperVoice Settings" -``` - -**Step 3: Fix close button hover colors** - -Line 137, change: - -Old: `color: closeMa.containsMouse ? "#20ff4b4b" : "transparent"` -New: `color: closeMa.containsMouse ? "#20FF8A8A" : "transparent"` - -Line 138, change: - -Old: `border.color: closeMa.containsMouse ? "#40ff4b4b" : "transparent"` -New: `border.color: closeMa.containsMouse ? "#40FF8A8A" : "transparent"` - -Line 144, change: - -Old: `color: closeMa.containsMouse ? "#ff4b4b" : SettingsStyle.textSecondary` -New: `color: closeMa.containsMouse ? "#FF8A8A" : SettingsStyle.textSecondary` - -**Step 4: Add close button keyboard support and accessibility** - -After the close button Rectangle opening (line 134), add: - -```qml - activeFocusOnTab: true - Accessible.name: "Close settings" - Accessible.role: Accessible.Button - Keys.onReturnPressed: root.close() - Keys.onSpacePressed: root.close() -``` - -Add a focus indicator. After `Behavior on border.color` (line 159), add: - -```qml - // Focus ring - Rectangle { - anchors.fill: parent - radius: 8 - color: "transparent" - border.width: SettingsStyle.focusRingWidth - border.color: SettingsStyle.accent - visible: parent.activeFocus - } -``` - -**Step 5: Fix nav sidebar keyboard support** - -In the Repeater delegate (line 203), after `radius: 6`, add: - -```qml - activeFocusOnTab: true - Accessible.name: name - Accessible.role: Accessible.Tab - Keys.onReturnPressed: stack.currentIndex = index - Keys.onSpacePressed: stack.currentIndex = index - Keys.onDownPressed: { - if (index < navModel.count - 1) { - // Move focus to next nav item - var nextItem = parent.children[index + 1] - if (nextItem) nextItem.forceActiveFocus() - } - } - Keys.onUpPressed: { - if (index > 0) { - var prevItem = parent.children[index - 1] - if (prevItem) prevItem.forceActiveFocus() - } - } -``` - -Add focus indicator inside the nav delegate, after the MouseArea (line 258): - -```qml - // Focus ring - Rectangle { - anchors.fill: parent - radius: 6 - color: "transparent" - border.width: SettingsStyle.focusRingWidth - border.color: SettingsStyle.accent - visible: parent.activeFocus - } -``` - -**Step 6: Fix StatBox accent colors** - -Line 1068, change: - -Old: `StatBox { label: "APP RAM"; value: ui.appRamMb; unit: "MB"; accent: "#bd93f9" }` -New: `StatBox { label: "APP RAM"; value: ui.appRamMb; unit: "MB"; accent: "#CAA9FF" }` - -Line 1069, change: - -Old: `StatBox { label: "GPU VRAM"; value: ui.appVramMb; unit: "MB"; accent: "#ff79c6" }` -New: `StatBox { label: "GPU VRAM"; value: ui.appVramMb; unit: "MB"; accent: "#FF8FD0" }` - -Line 1070, change: - -Old: `StatBox { label: "GPU LOAD"; value: ui.appVramPercent; unit: "%"; accent: "#ff5555" }` -New: `StatBox { label: "GPU LOAD"; value: ui.appVramPercent; unit: "%"; accent: "#FF8A8A" }` - -**Step 7: Fix model info text** - -Line 755, change: - -Old: `font.pixelSize: 10` -New: `font.pixelSize: 11` - -Line 756, change (remove opacity): - -Old: `opacity: 0.7` -New: `opacity: 1.0` - -**Step 8: Verify** - -Open Settings. Check: -- Close button hover is softer red (#FF8A8A) -- StatBox accents are lighter pastels -- Model info text is readable without opacity reduction -- Tab through nav items, close button -- Arrow keys move between nav items - -**Step 9: Commit** - -```bash -git add src/ui/qml/Settings.qml -git commit -m "WCAG: Settings - AAA colors, keyboard nav, accessible roles" -``` - ---- - -### Task 10: Fix Overlay.qml (Border + Keyboard + Accessibility) - -**Files:** -- Modify: `src/ui/qml/Overlay.qml` - -**Step 1: Add window-level accessibility** - -After line 15 (`color: "transparent"`), add: - -```qml - title: "WhisperVoice" - Accessible.name: "WhisperVoice Overlay" -``` - -**Step 2: Fix border color** - -Line 175, change: - -Old: `border.color: "#40ffffff"` -New: `border.color: Qt.rgba(1, 1, 1, 0.22)` - -**Step 3: Add mic button keyboard support and accessibility** - -In the `micContainer` Item (line 206), after `anchors.verticalCenter: parent.verticalCenter`, add: - -```qml - activeFocusOnTab: true - Accessible.name: ui.isRecording ? "Stop recording" : "Start recording" - Accessible.role: Accessible.Button - Keys.onReturnPressed: ui.toggleRecordingRequested() - Keys.onSpacePressed: ui.toggleRecordingRequested() -``` - -**Step 4: Add focus ring to mic button** - -Inside micContainer, add a focus indicator after the micCircle Rectangle (after line 265): - -```qml - // Focus ring - Rectangle { - anchors.fill: micCircle - anchors.margins: -SettingsStyle.focusRingWidth - 1 - radius: width / 2 - color: "transparent" - border.width: SettingsStyle.focusRingWidth - border.color: SettingsStyle.accent - visible: micContainer.activeFocus - } -``` - -Note: This requires importing SettingsStyle. Check if it's already available in Overlay.qml — it likely isn't since Overlay uses hardcoded values. If SettingsStyle is not importable in Overlay.qml, use a literal `2` for border width and `"#B794F6"` for color. - -**Step 5: Add accessibility to recording timer text** - -In the timer Text (around line 333), add: - -```qml - Accessible.role: Accessible.StaticText - Accessible.name: "Recording time: " + text -``` - -**Step 6: Verify** - -Launch app, trigger recording. Border should be more visible. Tab to mic button - focus ring appears. Press Enter on focused mic button - recording toggles. - -**Step 7: Commit** - -```bash -git add src/ui/qml/Overlay.qml -git commit -m "WCAG: Overlay - border contrast, keyboard nav, accessible roles" -``` - ---- - -### Task 11: Fix Loader.qml (Colors + Accessibility) - -**Files:** -- Modify: `src/ui/qml/Loader.qml` - -**Step 1: Add window-level accessibility** - -After line 16 (`color: "transparent"`), add: - -```qml - title: "WhisperVoice" - Accessible.name: "WhisperVoice Loading" -``` - -**Step 2: Fix border color** - -Line 24, change: - -Old: `border.color: "#40ffffff"` -New: `border.color: Qt.rgba(1, 1, 1, 0.22)` - -**Step 3: Fix subtitle color** - -Line 98, change: - -Old: `color: "#80ffffff"` -New: `color: "#ABABAB"` - -**Step 4: Fix status text opacity** - -Line 161, change: - -Old: `opacity: 0.8` -New: (remove the line entirely, or set to 1.0) - -**Step 5: Add status text accessibility** - -After line 159 (`font.bold: true`), add: - -```qml - Accessible.role: Accessible.StaticText - Accessible.name: "Loading status: " + text -``` - -**Step 6: Verify** - -Launch app. Loader subtitle "AI TRANSCRIPTION ENGINE" should be brighter. Status text fully opaque. Border more visible. - -**Step 7: Commit** - -```bash -git add src/ui/qml/Loader.qml -git commit -m "WCAG: Loader - subtitle contrast, status opacity, accessible roles" -``` - ---- - -### Task 12: Add Reduced Motion Support (Python Backend) - -**Files:** -- Modify: `src/core/config.py:61` (add default) -- Modify: `src/ui/bridge.py` (add property) -- Modify: `main.py` (add OS detection) - -**Step 1: Add config default** - -In `src/core/config.py`, add to DEFAULT_SETTINGS dict, after line 61 (`"unload_models_after_use": False`): - -```python - "unload_models_after_use": False, - - # Accessibility - "reduce_motion": False # Disable animations for WCAG 2.3.3 -``` - -(Note: change the comma on the `unload_models_after_use` line too) - -**Step 2: Add bridge property** - -In `src/ui/bridge.py`, add a new signal in the UIBridge class signals section (after line 113): - -```python - reduceMotionChanged = Signal(bool) -``` - -In `__init__` (after line 132, `self._is_destroyed = False`), add: - -```python - self._reduce_motion = bool(ConfigManager().get("reduce_motion")) -``` - -Add the property (after the `uiScale` property block, around line 289): - -```python - @Property(bool, notify=reduceMotionChanged) - def reduceMotion(self): return self._reduce_motion - - @reduceMotion.setter - def reduceMotion(self, val): - if self._reduce_motion != val: - self._reduce_motion = val - self.reduceMotionChanged.emit(val) -``` - -Also update `setSetting` (around line 275-280) to handle `reduce_motion`: - -After the `if key == "ui_scale":` block, add: - -```python - if key == "reduce_motion": - self.reduceMotion = bool(value) -``` - -**Step 3: Add Windows OS detection in main.py** - -After the resolution detection block (after line 81, before `# Configure Logging`), add: - -```python -# Detect Windows "Reduce Motion" preference -try: - import ctypes - SPI_GETCLIENTAREAANIMATION = 0x1042 - animation_enabled = ctypes.c_bool(True) - ctypes.windll.user32.SystemParametersInfoW( - SPI_GETCLIENTAREAANIMATION, 0, - ctypes.byref(animation_enabled), 0 - ) - if not animation_enabled.value: - # OS says reduce motion — set as default (user can override in Settings) - ConfigManager().data["reduce_motion"] = True - ConfigManager().save() -except Exception: - pass -``` - -**Step 4: Verify** - -Run the app. Check `ui.reduceMotion` is accessible from QML. No crashes. - -**Step 5: Commit** - -```bash -git add src/core/config.py src/ui/bridge.py main.py -git commit -m "WCAG: Add reduce_motion config, bridge property, OS detection" -``` - ---- - -### Task 13: Apply Reduced Motion Conditionals to Overlay.qml - -**Files:** -- Modify: `src/ui/qml/Overlay.qml` - -**Step 1: Shimmer animation** - -Line 38-39, change: - -Old: `running: true` -New: `running: !ui.reduceMotion` - -**Step 2: Gradient blobs shader** - -Line 96-102, wrap the NumberAnimation. Change: - -Old: `NumberAnimation on time { from: 0; to: 1000; duration: 100000; loops: Animation.Infinite }` - -Add `visible` to the ShaderEffect (line 96): - -After `opacity: 0.4` (line 98), add: -```qml - visible: !ui.reduceMotion -``` - -**Step 3: Glow shader** - -After `opacity: 0.04` (line 107), add: -```qml - visible: !ui.reduceMotion -``` - -**Step 4: Particles** - -On the ParticleSystem (line 115), add: -```qml - running: !ui.reduceMotion -``` - -**Step 5: CRT shader** - -On the CRT ShaderEffect (line 144), add: -```qml - visible: !ui.reduceMotion -``` - -**Step 6: Rainbow wave shader** - -On the ShaderEffect inside waveformContainer (line 278), add: -```qml - visible: !ui.reduceMotion -``` - -**Step 7: Mic button pulse animation** - -Line 247-252 (SequentialAnimation on scale), change: - -Old: `running: ui.isRecording` -New: `running: ui.isRecording && !ui.reduceMotion` - -**Step 8: Timer text blink animation** - -Line 344-348 (SequentialAnimation on opacity), change: - -Old: `running: ui.isRecording; loops: Animation.Infinite` -New: `running: ui.isRecording && !ui.reduceMotion; loops: Animation.Infinite` - -**Step 9: Recording border pulse** - -Line 196-201 (SequentialAnimation on border.color), change: - -Old: `running: ui.isRecording` -New: `running: ui.isRecording && !ui.reduceMotion` - -**Step 10: Verify** - -Set `reduce_motion: true` in settings.json manually. Launch app. Overlay should be static (no shimmer, no blobs, no glow, no particles, no CRT, no rainbow). Still functional — mic button works, timer shows. - -**Step 11: Commit** - -```bash -git add src/ui/qml/Overlay.qml -git commit -m "WCAG: Overlay - conditional animations for reduced motion" -``` - ---- - -### Task 14: Apply Reduced Motion to Loader.qml + Add Settings Toggle - -**Files:** -- Modify: `src/ui/qml/Loader.qml` -- Modify: `src/ui/qml/Settings.qml` - -**Step 1: Loader icon pulse** - -In Loader.qml, line 57-61 (SequentialAnimation on scale), change: - -Old: `loops: Animation.Infinite` - -Wrap it — add before the animation: -```qml - running: ui ? !ui.reduceMotion : true -``` - -(Use ternary because Loader loads before bridge might be ready) - -**Step 2: Loader progress shimmer** - -Line 144-148 (NumberAnimation on x), wrap the parent Rectangle. Add to the shimmer Rectangle (line 136): - -```qml - visible: ui ? !ui.reduceMotion : true -``` - -**Step 3: Add "Reduce Motion" toggle in Settings Visuals tab** - -In Settings.qml, find the Visuals tab Overlay section. After the "Window Opacity" `ModernSettingsItem` (around line 520-530, the one with `showSeparator: false`), change `showSeparator: false` to `showSeparator: true` on the opacity item, then add a new item after it: - -```qml - ModernSettingsItem { - label: "Reduce Motion" - description: "Disable animations for accessibility" - showSeparator: false - control: ModernSwitch { - checked: ui.getSetting("reduce_motion") - onToggled: ui.setSetting("reduce_motion", checked) - } - } -``` - -**Step 4: Verify** - -Launch app. Open Settings > Visuals. Toggle "Reduce Motion" on. Overlay animations should stop. Loader shimmer should stop on next launch with setting persisted. - -**Step 5: Commit** - -```bash -git add src/ui/qml/Loader.qml src/ui/qml/Settings.qml -git commit -m "WCAG: Reduced motion toggle in Visuals, conditionals in Loader" -``` - ---- - -### Task 15: Final Verification and Cleanup - -**Step 1: Full visual verification** - -Run the app end-to-end: - -```bash -cd "D:/!!! SYSTEM DATA !!!/Desktop/python crap/whisper_voice" && python main.py -``` - -Checklist: -- [ ] Loader appears with brighter subtitle, no opacity on status -- [ ] Overlay border is visible -- [ ] Settings opens with lighter purple accent (#B794F6) -- [ ] All text is readable (no low-contrast text) -- [ ] Switch shows I/O marks -- [ ] Slider handles are 24px -- [ ] Tab key moves through all interactive controls -- [ ] Enter/Space activates focused controls -- [ ] Reduce Motion toggle works (Settings > Visuals) -- [ ] StatBox colors in Debug tab are lighter pastels -- [ ] No QML warnings in console output - -**Step 2: Commit any remaining fixes** - -If any issues found, fix and commit individually. - -**Step 3: Final commit** - -```bash -git add -A -git commit -m "WCAG 2.2 AAA: Full compliance implementation complete" -```