diff --git a/.gitignore b/.gitignore index efb8eb3..ab658d6 100644 --- a/.gitignore +++ b/.gitignore @@ -10,13 +10,16 @@ env/ # Distribution / Build dist/ build/ -*.spec -_unused_files/ runtime/ +# Trash / Unused +_trash/ +_unused_files/ + # IDEs .vscode/ .idea/ +.claude/ # Application Specific models/ diff --git a/app_icon.ico b/app_icon.ico deleted file mode 100644 index cc5cc26..0000000 Binary files a/app_icon.ico and /dev/null differ diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..8856b33 --- /dev/null +++ b/build.bat @@ -0,0 +1,31 @@ +@echo off +echo ============================================ +echo Building WhisperVoice Portable EXE +echo ============================================ +echo. + +if not exist venv ( + echo ERROR: venv not found. Run run_source.bat first. + pause + exit /b 1 +) + +call venv\Scripts\activate + +echo Running PyInstaller (single-file bootstrapper)... +pyinstaller build.spec --clean --noconfirm + +if %ERRORLEVEL% NEQ 0 ( + echo. + echo BUILD FAILED! Check errors above. + pause + exit /b 1 +) + +echo. +echo Build complete! +echo. +echo Output: dist\WhisperVoice.exe +echo. +echo This single exe will download all dependencies on first run. +pause diff --git a/build.spec b/build.spec new file mode 100644 index 0000000..8740b55 --- /dev/null +++ b/build.spec @@ -0,0 +1,95 @@ +# -*- mode: python ; coding: utf-8 -*- +# WhisperVoice — Single-file portable bootstrapper +# +# This builds a TINY exe that contains only: +# - The bootstrapper (downloads Python + deps on first run) +# - The app source code (bundled as data, extracted to runtime/app/) +# +# NO heavy dependencies (torch, PySide6, etc.) are bundled. + +import os +import glob + +block_cipher = None + +# ── Collect app source as data (goes into app_source/ inside the bundle) ── + +app_datas = [] + +# main.py +app_datas.append(('main.py', 'app_source')) + +# requirements.txt +app_datas.append(('requirements.txt', 'app_source')) + +# src/**/*.py (core, ui, utils — preserving directory structure) +for py in glob.glob('src/**/*.py', recursive=True): + dest = os.path.join('app_source', os.path.dirname(py)) + app_datas.append((py, dest)) + +# src/ui/qml/** (QML files, shaders, SVGs, fonts, qmldir) +qml_dir = os.path.join('src', 'ui', 'qml') +for pattern in ('*.qml', '*.qsb', '*.frag', '*.svg', '*.ico', '*.png', + 'qmldir', 'AUTHORS.txt', 'OFL.txt'): + for f in glob.glob(os.path.join(qml_dir, pattern)): + app_datas.append((f, os.path.join('app_source', qml_dir))) + +# Fonts +for f in glob.glob(os.path.join(qml_dir, 'fonts', 'ttf', '*.ttf')): + app_datas.append((f, os.path.join('app_source', qml_dir, 'fonts', 'ttf'))) + +# assets/ +if os.path.exists(os.path.join('assets', 'icon.ico')): + app_datas.append((os.path.join('assets', 'icon.ico'), os.path.join('app_source', 'assets'))) + +# ── Analysis — only the bootstrapper, NO heavy imports ──────────────────── + +a = Analysis( + ['bootstrapper.py'], + pathex=[], + binaries=[], + datas=app_datas, + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[ + # Exclude everything heavy — the bootstrapper only uses stdlib + 'torch', 'numpy', 'scipy', 'PySide6', 'shiboken6', + 'faster_whisper', 'ctranslate2', 'llama_cpp', + 'sounddevice', 'soundfile', 'keyboard', 'pyperclip', + 'psutil', 'pynvml', 'pystray', 'PIL', 'Pillow', + 'darkdetect', 'huggingface_hub', 'requests', + 'tqdm', 'onnxruntime', 'av', + 'tkinter', 'matplotlib', 'notebook', 'IPython', + ], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False, +) + +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) + +# ── Single-file EXE (--onefile) ────────────────────────────────────────── + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name='WhisperVoice', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=False, # No console — bootstrapper allocates one when needed + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon='assets/icon.ico', +) diff --git a/build_bootstrapper.py b/build_bootstrapper.py deleted file mode 100644 index b89ffac..0000000 --- a/build_bootstrapper.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -Build the Lightweight Bootstrapper -================================== - -This creates a small (~15-20MB) .exe that downloads Python + dependencies on first run. -""" - -import os -import shutil -import PyInstaller.__main__ -from pathlib import Path - -def build_bootstrapper(): - project_root = Path(__file__).parent.absolute() - dist_path = project_root / "dist" - - # Collect all app source files to bundle - # These will be extracted and used when setting up - app_source_files = [ - ("src", "app_source/src"), - ("assets", "app_source/assets"), # Include icon etc - ("main.py", "app_source"), - ("requirements.txt", "app_source"), - ] - - add_data_args = [] - for src, dst in app_source_files: - src_path = project_root / src - if src_path.exists(): - add_data_args.extend(["--add-data", f"{src}{os.pathsep}{dst}"]) - - # Use absolute project root for copying - shutil.copy2(project_root / "assets" / "icon.ico", project_root / "app_icon.ico") - - print("🚀 Building Lightweight Bootstrapper...") - print("⏳ This creates a small .exe that downloads dependencies on first run.\n") - - PyInstaller.__main__.run([ - "bootstrapper.py", - "--name=WhisperVoice", - "--onefile", - "--noconsole", # Re-enabled! Error handling in bootstrapper is ready. - "--clean", - "--icon=app_icon.ico", # Simplified path at root - *add_data_args, - ]) - - exe_path = dist_path / "WhisperVoice.exe" - if exe_path.exists(): - size_mb = exe_path.stat().st_size / (1024 * 1024) - print("\n" + "="*60) - print("✅ BOOTSTRAPPER BUILD COMPLETE!") - print("="*60) - print(f"\n📍 Output: {exe_path}") - print(f"📦 Size: {size_mb:.1f} MB") - print("\n📋 How it works:") - print(" 1. User runs WhisperVoice.exe") - print(" 2. First run: Downloads Python + packages (~2-3GB)") - print(" 3. Subsequent runs: Launches instantly") - print("\n💡 The 'runtime/' folder will be created next to the .exe") - else: - print("\n❌ Build failed. Check the output above for errors.") - -if __name__ == "__main__": - os.chdir(Path(__file__).parent) - build_bootstrapper() diff --git a/build_exe.bat b/build_exe.bat deleted file mode 100644 index 336ec27..0000000 --- a/build_exe.bat +++ /dev/null @@ -1,17 +0,0 @@ -@echo off -echo Building Whisper Voice Portable EXE... -if not exist venv ( - echo Please run run_source.bat first to setup environment! - pause - exit /b -) - -call venv\Scripts\activate -pip install pyinstaller - -echo Running PyInstaller... -pyinstaller build.spec --clean --noconfirm - -echo. -echo Build Complete! Check dist/WhisperVoice.exe -pause diff --git a/convert_icon.py b/convert_icon.py deleted file mode 100644 index 5584f90..0000000 --- a/convert_icon.py +++ /dev/null @@ -1,14 +0,0 @@ -from PIL import Image -import os - -# Path from the generate_image tool output -src = r"C:/Users/lashman/.gemini/antigravity/brain/9a183770-2481-475b-b748-03f4910f9a8e/app_icon_1769195450659.png" -dst = r"d:\!!! SYSTEM DATA !!!\Desktop\python crap\whisper_voice\assets\icon.ico" - -if os.path.exists(src): - img = Image.open(src) - # Resize to standard icon sizes - img.save(dst, format='ICO', sizes=[(256, 256)]) - print(f"Icon saved to {dst}") -else: - print(f"Source image not found: {src}") diff --git a/download_icons.py b/download_icons.py deleted file mode 100644 index 2e3b237..0000000 --- a/download_icons.py +++ /dev/null @@ -1,43 +0,0 @@ -import requests -import os - -ICONS = { - "settings.svg": "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/6.x/svgs/solid/gear.svg", - "visibility.svg": "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/6.x/svgs/solid/eye.svg", - "smart_toy.svg": "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/6.x/svgs/solid/brain.svg", - "microphone.svg": "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/6.x/svgs/solid/microphone.svg" -} - -TARGET_DIR = r"d:\!!! SYSTEM DATA !!!\Desktop\python crap\whisper_voice\src\ui\qml" - -def download_icons(): - if not os.path.exists(TARGET_DIR): - print(f"Directory not found: {TARGET_DIR}") - return - - for filename, url in ICONS.items(): - try: - print(f"Downloading {filename} from {url}...") - response = requests.get(url, timeout=10) - response.raise_for_status() - - # Force white fill - content = response.text - if " 0: - painter = QPainter(self) - painter.setRenderHint(QPainter.Antialiasing) - - # Subtle Glow Border - color = QColor(self.accent) - color.setAlphaF(self._hover_opacity * 0.5) - painter.setPen(QPen(color, 1.5)) - painter.setBrush(Qt.NoBrush) - painter.drawRoundedRect(self.rect().adjusted(1,1,-1,-1), 8, 8) - - # Text Glow color shift - self.setStyleSheet(f""" - QPushButton {{ - background-color: rgba(255, 255, 255, {0.05 + (self._hover_opacity * 0.05)}); - border: 1px solid {Theme.BORDER_SUBTLE}; - color: white; - border-radius: 8px; - padding: 0 20px; - font-size: 13px; - font-weight: 600; - }} - """) - -class ModernSwitch(QAbstractButton): - """A sleek iOS-style toggle switch.""" - - def __init__(self, parent=None, active_color=Theme.ACCENT_GREEN): - super().__init__(parent) - self.setCheckable(True) - self.setFixedSize(44, 24) - self._thumb_pos = 3.0 - self.active_color = QColor(active_color) - - self.anim = QPropertyAnimation(self, b"thumb_pos") - self.anim.setDuration(200) - self.anim.setEasingCurve(QEasingCurve.InOutCubic) - - @Property(float) - def thumb_pos(self): return self._thumb_pos - - @thumb_pos.setter - def thumb_pos(self, value): - self._thumb_pos = value - self.update() - - def nextCheckState(self): - super().nextCheckState() - self.anim.stop() - if self.isChecked(): - self.anim.setEndValue(23.0) - else: - self.anim.setEndValue(3.0) - self.anim.start() - - def paintEvent(self, event): - painter = QPainter(self) - painter.setRenderHint(QPainter.Antialiasing) - - # Background - bg_color = QColor("#2d2d3d") - if self.isChecked(): - bg_color = self.active_color - - painter.setBrush(bg_color) - painter.setPen(Qt.NoPen) - painter.drawRoundedRect(self.rect(), 12, 12) - - # Thumb - painter.setBrush(Qt.white) - painter.drawEllipse(QPoint(self._thumb_pos + 9, 12), 9, 9) - -class ModernFrame(QFrame): - """A base frame with rounded corners and a shadow.""" - def __init__(self, parent=None): - super().__init__(parent) - self.setObjectName("premiumFrame") - self.setStyleSheet(f""" - #premiumFrame {{ - background-color: {Theme.BG_CARD}; - border: 1px solid {Theme.BORDER_SUBTLE}; - border-radius: 12px; - }} - """) - - self.shadow = QGraphicsDropShadowEffect(self) - self.shadow.setBlurRadius(25) - self.shadow.setXOffset(0) - self.shadow.setYOffset(8) - self.shadow.setColor(QColor(0, 0, 0, 180)) - self.setGraphicsEffect(self.shadow) - -from PySide6.QtWidgets import ( - QPushButton, QWidget, QVBoxLayout, QHBoxLayout, - QLabel, QGraphicsDropShadowEffect, QFrame, QAbstractButton, QSlider -) - -class ModernSlider(QSlider): - """A custom painted modern slider with a glowing knob.""" - def __init__(self, orientation=Qt.Horizontal, parent=None): - super().__init__(orientation, parent) - self.setStyleSheet(f""" - QSlider::groove:horizontal {{ - border: 1px solid {Theme.BG_DARK}; - height: 4px; - background: {Theme.BG_DARK}; - margin: 2px 0; - border-radius: 2px; - }} - QSlider::handle:horizontal {{ - background: {Theme.ACCENT_CYAN}; - border: 2px solid white; - width: 16px; - height: 16px; - margin: -7px 0; - border-radius: 8px; - }} - QSlider::add-page:horizontal {{ - background: {Theme.BG_DARK}; - }} - QSlider::sub-page:horizontal {{ - background: {Theme.ACCENT_CYAN}; - border-radius: 2px; - }} - """) - -class FramelessWindow(QWidget): - """Base class for all premium windows to handle dragging and frameless logic.""" - def __init__(self, parent=None): - super().__init__(parent) - self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.NoDropShadowWindowHint) - self.setAttribute(Qt.WA_TranslucentBackground) - self._drag_pos = None - - def mousePressEvent(self, event): - if event.button() == Qt.LeftButton: - self._drag_pos = event.globalPosition().toPoint() - self.frameGeometry().topLeft() - event.accept() - - def mouseMoveEvent(self, event): - if event.buttons() & Qt.LeftButton: - self.move(event.globalPosition().toPoint() - self._drag_pos) - event.accept() diff --git a/src/ui/loader.py b/src/ui/loader.py deleted file mode 100644 index 9f7efc4..0000000 --- a/src/ui/loader.py +++ /dev/null @@ -1,109 +0,0 @@ -""" -Loader Widget Module. -===================== - -Handles the application initialization and model checks. -Refactored for 2026 Premium Aesthetics. -""" - -from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel, QProgressBar -from PySide6.QtCore import Qt, QThread, Signal -from PySide6.QtGui import QFont -import os -import logging -from faster_whisper import download_model - -from src.core.paths import get_models_path -from src.ui.styles import Theme, StyleGenerator, load_modern_fonts -from src.ui.components import FramelessWindow, ModernFrame - -class DownloadWorker(QThread): - """Background worker for model downloads.""" - progress = Signal(str, int) - download_finished = Signal() - error = Signal(str) - - def run(self): - try: - model_path = get_models_path() - self.progress.emit("Verifying AI Core...", 10) - os.environ["HF_HOME"] = str(model_path) - - self.progress.emit("Downloading Model...", 30) - download_model("small", output_dir=str(model_path)) - - self.progress.emit("System Ready!", 100) - self.download_finished.emit() - except Exception as e: - logging.error(f"Loader failed: {e}") - self.error.emit(str(e)) - -class LoaderWidget(FramelessWindow): - """ - Premium bootstrapper UI. - Inherits from FramelessWindow for rounded glass look. - """ - ready_signal = Signal() - - def __init__(self): - super().__init__() - self.setFixedSize(400, 180) - - # Main Layout - self.root = QVBoxLayout(self) - self.root.setContentsMargins(10, 10, 10, 10) - - # Glass Card - self.card = ModernFrame() - self.card.setStyleSheet(StyleGenerator.get_glass_card(radius=20)) - self.root.addWidget(self.card) - - # Content Layout - self.layout = QVBoxLayout(self.card) - self.layout.setContentsMargins(30,30,30,30) - self.layout.setSpacing(15) - - # App Title/Brand - self.brand = QLabel("WHISPER VOICE") - self.brand.setFont(load_modern_fonts()) - self.brand.setStyleSheet(f"color: {Theme.ACCENT_CYAN}; font-weight: 900; letter-spacing: 4px; font-size: 14px;") - self.brand.setAlignment(Qt.AlignCenter) - self.layout.addWidget(self.brand) - - # Status Label - self.status_label = QLabel("INITIALIZING...") - self.status_label.setStyleSheet(f"color: {Theme.TEXT_SECONDARY}; font-weight: 600; font-size: 11px;") - self.status_label.setAlignment(Qt.AlignCenter) - self.layout.addWidget(self.status_label) - - # Progress Bar (Modern Slim style) - self.progress_bar = QProgressBar() - self.progress_bar.setFixedHeight(4) - self.progress_bar.setStyleSheet(f""" - QProgressBar {{ - background-color: {Theme.BG_DARK}; - border-radius: 2px; - border: none; - text-align: center; - color: transparent; - }} - QProgressBar::chunk {{ - background-color: {Theme.ACCENT_CYAN}; - border-radius: 2px; - }} - """) - self.layout.addWidget(self.progress_bar) - - # Start Worker - self.worker = DownloadWorker() - self.worker.progress.connect(self.update_progress) - self.worker.download_finished.connect(self.on_finished) - self.worker.start() - - def update_progress(self, text: str, percent: int): - self.status_label.setText(text.upper()) - self.progress_bar.setValue(percent) - - def on_finished(self): - self.ready_signal.emit() - self.close() diff --git a/src/ui/overlay.py b/src/ui/overlay.py deleted file mode 100644 index 87fb0af..0000000 --- a/src/ui/overlay.py +++ /dev/null @@ -1,105 +0,0 @@ -""" -Overlay Window Module. -====================== - -Premium High-Fidelity Overlay for Whisper Voice. -Features glassmorphism, pulsating status indicators, and smart positioning. -""" - -from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel -from PySide6.QtCore import Qt, Slot, QPoint, QPropertyAnimation, QEasingCurve -from PySide6.QtGui import QColor, QFont, QGuiApplication - -from src.ui.visualizer import AudioVisualizer -from src.ui.styles import Theme, StyleGenerator, load_modern_fonts -from src.ui.components import FramelessWindow, ModernFrame - -class OverlayWindow(FramelessWindow): - """ - The main transparent overlay (The Pill). - Refactored for 2026 Premium Aesthetics. - """ - - def __init__(self): - super().__init__() - self.setFixedSize(320, 95) - - # Main Layout - self.master_layout = QVBoxLayout(self) - self.master_layout.setContentsMargins(10, 10, 10, 10) - - # The Glass Pill Container - self.pill = ModernFrame() - self.pill.setStyleSheet(StyleGenerator.get_glass_card(radius=24)) - self.master_layout.addWidget(self.pill) - - # Layout inside the pill - self.layout = QHBoxLayout(self.pill) - self.layout.setContentsMargins(20, 10, 20, 10) - self.layout.setSpacing(15) - - # Status Visualization (Left Dot) - self.status_dot = QWidget() - self.status_dot.setFixedSize(14, 14) - self.status_dot.setStyleSheet(f"background-color: {Theme.ACCENT_CYAN}; border-radius: 7px; border: 2px solid white;") - self.layout.addWidget(self.status_dot) - - # Text/Visualizer Stack - self.content_stack = QVBoxLayout() - self.content_stack.setSpacing(2) - self.content_stack.setContentsMargins(0, 0, 0, 0) - - self.status_label = QLabel("READY") - self.status_label.setFont(load_modern_fonts()) - self.status_label.setStyleSheet(f"color: white; font-weight: 800; font-size: 11px; letter-spacing: 2px;") - self.content_stack.addWidget(self.status_label) - - self.visualizer = AudioVisualizer() - self.visualizer.setFixedHeight(30) - self.content_stack.addWidget(self.visualizer) - - self.layout.addLayout(self.content_stack) - - # Animations - self.pulse_timer = None # Use style-based pulsing to avoid window flags issues - - # Initial State - self.hide() - self.first_show = True - - def showEvent(self, event): - """Handle positioning and config updates.""" - from src.core.config import ConfigManager - config = ConfigManager() - self.setWindowOpacity(config.get("opacity")) - - if self.first_show: - self.center_above_taskbar() - self.first_show = False - super().showEvent(event) - - def center_above_taskbar(self): - screen = QGuiApplication.primaryScreen() - if not screen: return - avail_rect = screen.availableGeometry() - x = avail_rect.x() + (avail_rect.width() - self.width()) // 2 - y = avail_rect.bottom() - self.height() - 15 - self.move(x, y) - - @Slot(str) - def update_status(self, text: str): - """Updates the status text and visual indicator.""" - self.status_label.setText(text.upper()) - - if "RECORDING" in text.upper(): - color = Theme.ACCENT_GREEN - elif "THINKING" in text.upper(): - color = Theme.ACCENT_PURPLE - else: - color = Theme.ACCENT_CYAN - - self.status_dot.setStyleSheet(f"background-color: {color}; border-radius: 7px; border: 2px solid white;") - - @Slot(float) - def update_visualizer(self, amp: float): - self.visualizer.set_amplitude(amp) diff --git a/src/ui/qml/JetBrainsMono.zip b/src/ui/qml/JetBrainsMono.zip deleted file mode 100644 index 1d05731..0000000 Binary files a/src/ui/qml/JetBrainsMono.zip and /dev/null differ diff --git a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Bold.ttf b/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Bold.ttf deleted file mode 100644 index f78f84f..0000000 Binary files a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Bold.ttf and /dev/null differ diff --git a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-BoldItalic.ttf b/src/ui/qml/fonts/ttf/JetBrainsMonoNL-BoldItalic.ttf deleted file mode 100644 index 9fb8c83..0000000 Binary files a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-BoldItalic.ttf and /dev/null differ diff --git a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-ExtraBold.ttf b/src/ui/qml/fonts/ttf/JetBrainsMonoNL-ExtraBold.ttf deleted file mode 100644 index fe5be6a..0000000 Binary files a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-ExtraBold.ttf and /dev/null differ diff --git a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-ExtraBoldItalic.ttf b/src/ui/qml/fonts/ttf/JetBrainsMonoNL-ExtraBoldItalic.ttf deleted file mode 100644 index 59fc980..0000000 Binary files a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-ExtraBoldItalic.ttf and /dev/null differ diff --git a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-ExtraLight.ttf b/src/ui/qml/fonts/ttf/JetBrainsMonoNL-ExtraLight.ttf deleted file mode 100644 index 6da7b75..0000000 Binary files a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-ExtraLight.ttf and /dev/null differ diff --git a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-ExtraLightItalic.ttf b/src/ui/qml/fonts/ttf/JetBrainsMonoNL-ExtraLightItalic.ttf deleted file mode 100644 index 5733efc..0000000 Binary files a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-ExtraLightItalic.ttf and /dev/null differ diff --git a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Italic.ttf b/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Italic.ttf deleted file mode 100644 index 4e9c380..0000000 Binary files a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Italic.ttf and /dev/null differ diff --git a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Light.ttf b/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Light.ttf deleted file mode 100644 index 0b79b0c..0000000 Binary files a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Light.ttf and /dev/null differ diff --git a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-LightItalic.ttf b/src/ui/qml/fonts/ttf/JetBrainsMonoNL-LightItalic.ttf deleted file mode 100644 index b5e0842..0000000 Binary files a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-LightItalic.ttf and /dev/null differ diff --git a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Medium.ttf b/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Medium.ttf deleted file mode 100644 index 1454372..0000000 Binary files a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Medium.ttf and /dev/null differ diff --git a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-MediumItalic.ttf b/src/ui/qml/fonts/ttf/JetBrainsMonoNL-MediumItalic.ttf deleted file mode 100644 index 8d63c6c..0000000 Binary files a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-MediumItalic.ttf and /dev/null differ diff --git a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Regular.ttf b/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Regular.ttf deleted file mode 100644 index 70d2ec9..0000000 Binary files a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Regular.ttf and /dev/null differ diff --git a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-SemiBold.ttf b/src/ui/qml/fonts/ttf/JetBrainsMonoNL-SemiBold.ttf deleted file mode 100644 index ce60a88..0000000 Binary files a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-SemiBold.ttf and /dev/null differ diff --git a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-SemiBoldItalic.ttf b/src/ui/qml/fonts/ttf/JetBrainsMonoNL-SemiBoldItalic.ttf deleted file mode 100644 index 3b3f8f6..0000000 Binary files a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-SemiBoldItalic.ttf and /dev/null differ diff --git a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Thin.ttf b/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Thin.ttf deleted file mode 100644 index bea837e..0000000 Binary files a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-Thin.ttf and /dev/null differ diff --git a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-ThinItalic.ttf b/src/ui/qml/fonts/ttf/JetBrainsMonoNL-ThinItalic.ttf deleted file mode 100644 index f0bfed7..0000000 Binary files a/src/ui/qml/fonts/ttf/JetBrainsMonoNL-ThinItalic.ttf and /dev/null differ diff --git a/src/ui/qml/fonts/variable/JetBrainsMono-Italic[wght].ttf b/src/ui/qml/fonts/variable/JetBrainsMono-Italic[wght].ttf deleted file mode 100644 index 5414835..0000000 Binary files a/src/ui/qml/fonts/variable/JetBrainsMono-Italic[wght].ttf and /dev/null differ diff --git a/src/ui/qml/fonts/variable/JetBrainsMono[wght].ttf b/src/ui/qml/fonts/variable/JetBrainsMono[wght].ttf deleted file mode 100644 index b60e77f..0000000 Binary files a/src/ui/qml/fonts/variable/JetBrainsMono[wght].ttf and /dev/null differ diff --git a/src/ui/qml/fonts/webfonts/JetBrainsMono-Bold.woff2 b/src/ui/qml/fonts/webfonts/JetBrainsMono-Bold.woff2 deleted file mode 100644 index 4917f43..0000000 Binary files a/src/ui/qml/fonts/webfonts/JetBrainsMono-Bold.woff2 and /dev/null differ diff --git a/src/ui/qml/fonts/webfonts/JetBrainsMono-BoldItalic.woff2 b/src/ui/qml/fonts/webfonts/JetBrainsMono-BoldItalic.woff2 deleted file mode 100644 index 536d3f7..0000000 Binary files a/src/ui/qml/fonts/webfonts/JetBrainsMono-BoldItalic.woff2 and /dev/null differ diff --git a/src/ui/qml/fonts/webfonts/JetBrainsMono-ExtraBold.woff2 b/src/ui/qml/fonts/webfonts/JetBrainsMono-ExtraBold.woff2 deleted file mode 100644 index 8f88c54..0000000 Binary files a/src/ui/qml/fonts/webfonts/JetBrainsMono-ExtraBold.woff2 and /dev/null differ diff --git a/src/ui/qml/fonts/webfonts/JetBrainsMono-ExtraBoldItalic.woff2 b/src/ui/qml/fonts/webfonts/JetBrainsMono-ExtraBoldItalic.woff2 deleted file mode 100644 index d1478ba..0000000 Binary files a/src/ui/qml/fonts/webfonts/JetBrainsMono-ExtraBoldItalic.woff2 and /dev/null differ diff --git a/src/ui/qml/fonts/webfonts/JetBrainsMono-ExtraLight.woff2 b/src/ui/qml/fonts/webfonts/JetBrainsMono-ExtraLight.woff2 deleted file mode 100644 index b97239f..0000000 Binary files a/src/ui/qml/fonts/webfonts/JetBrainsMono-ExtraLight.woff2 and /dev/null differ diff --git a/src/ui/qml/fonts/webfonts/JetBrainsMono-ExtraLightItalic.woff2 b/src/ui/qml/fonts/webfonts/JetBrainsMono-ExtraLightItalic.woff2 deleted file mode 100644 index be01aac..0000000 Binary files a/src/ui/qml/fonts/webfonts/JetBrainsMono-ExtraLightItalic.woff2 and /dev/null differ diff --git a/src/ui/qml/fonts/webfonts/JetBrainsMono-Italic.woff2 b/src/ui/qml/fonts/webfonts/JetBrainsMono-Italic.woff2 deleted file mode 100644 index d60c270..0000000 Binary files a/src/ui/qml/fonts/webfonts/JetBrainsMono-Italic.woff2 and /dev/null differ diff --git a/src/ui/qml/fonts/webfonts/JetBrainsMono-Light.woff2 b/src/ui/qml/fonts/webfonts/JetBrainsMono-Light.woff2 deleted file mode 100644 index 6538498..0000000 Binary files a/src/ui/qml/fonts/webfonts/JetBrainsMono-Light.woff2 and /dev/null differ diff --git a/src/ui/qml/fonts/webfonts/JetBrainsMono-LightItalic.woff2 b/src/ui/qml/fonts/webfonts/JetBrainsMono-LightItalic.woff2 deleted file mode 100644 index 66ca3d2..0000000 Binary files a/src/ui/qml/fonts/webfonts/JetBrainsMono-LightItalic.woff2 and /dev/null differ diff --git a/src/ui/qml/fonts/webfonts/JetBrainsMono-Medium.woff2 b/src/ui/qml/fonts/webfonts/JetBrainsMono-Medium.woff2 deleted file mode 100644 index 669d04c..0000000 Binary files a/src/ui/qml/fonts/webfonts/JetBrainsMono-Medium.woff2 and /dev/null differ diff --git a/src/ui/qml/fonts/webfonts/JetBrainsMono-MediumItalic.woff2 b/src/ui/qml/fonts/webfonts/JetBrainsMono-MediumItalic.woff2 deleted file mode 100644 index 80cfd15..0000000 Binary files a/src/ui/qml/fonts/webfonts/JetBrainsMono-MediumItalic.woff2 and /dev/null differ diff --git a/src/ui/qml/fonts/webfonts/JetBrainsMono-Regular.woff2 b/src/ui/qml/fonts/webfonts/JetBrainsMono-Regular.woff2 deleted file mode 100644 index 40da427..0000000 Binary files a/src/ui/qml/fonts/webfonts/JetBrainsMono-Regular.woff2 and /dev/null differ diff --git a/src/ui/qml/fonts/webfonts/JetBrainsMono-SemiBold.woff2 b/src/ui/qml/fonts/webfonts/JetBrainsMono-SemiBold.woff2 deleted file mode 100644 index 5ead7b0..0000000 Binary files a/src/ui/qml/fonts/webfonts/JetBrainsMono-SemiBold.woff2 and /dev/null differ diff --git a/src/ui/qml/fonts/webfonts/JetBrainsMono-SemiBoldItalic.woff2 b/src/ui/qml/fonts/webfonts/JetBrainsMono-SemiBoldItalic.woff2 deleted file mode 100644 index c5dd294..0000000 Binary files a/src/ui/qml/fonts/webfonts/JetBrainsMono-SemiBoldItalic.woff2 and /dev/null differ diff --git a/src/ui/qml/fonts/webfonts/JetBrainsMono-Thin.woff2 b/src/ui/qml/fonts/webfonts/JetBrainsMono-Thin.woff2 deleted file mode 100644 index 17270e4..0000000 Binary files a/src/ui/qml/fonts/webfonts/JetBrainsMono-Thin.woff2 and /dev/null differ diff --git a/src/ui/qml/fonts/webfonts/JetBrainsMono-ThinItalic.woff2 b/src/ui/qml/fonts/webfonts/JetBrainsMono-ThinItalic.woff2 deleted file mode 100644 index a643215..0000000 Binary files a/src/ui/qml/fonts/webfonts/JetBrainsMono-ThinItalic.woff2 and /dev/null differ diff --git a/src/ui/qml/glass.frag b/src/ui/qml/glass.frag deleted file mode 100644 index 8b9c45f..0000000 --- a/src/ui/qml/glass.frag +++ /dev/null @@ -1,50 +0,0 @@ -#version 440 - -layout(location = 0) in vec2 qt_TexCoord0; -layout(location = 0) out vec4 fragColor; - -layout(std140, binding = 0) uniform buf { - mat4 qt_Matrix; - float qt_Opacity; - float time; - float aberration; // 0.0 to 1.0, controlled by Audio Amplitude -}; - -float rand(vec2 co) { - return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); -} - -void main() { - // 1. Calculate Distortion Offset based on Amplitude (aberration) - // We warp the UVs slightly away from center - vec2 uv = qt_TexCoord0; - vec2 dist = uv - 0.5; - - // 2. Chromatic Aberration - // Red Channel shifts OUT - // Blue Channel shifts IN - float strength = aberration * 0.02; // Max shift 2% of texture size - - vec2 rUV = uv + (dist * strength); - vec2 bUV = uv - (dist * strength); - - // Sample texture? We don't have a texture input (source is empty Item), we are generating visuals. - // Wait, ShaderEffect usually works on sourceItem. - // Here we are generating NOISE on top of a gradient. - // So we apply Aberration to the NOISE function? - // Or do we want to aberrate the pixels UNDERNEATH? - // ShaderEffect with no source property renders purely procedural content. - - // Let's create layered procedural noise with channel offsets - float nR = rand(rUV + vec2(time * 0.01, 0.0)); - float nG = rand(uv + vec2(time * 0.01, 0.0)); // Green is anchor - float nB = rand(bUV + vec2(time * 0.01, 0.0)); - - // Also modulate alpha by aberration - higher volume = more intense grain? - // Or maybe just pure glitch. - - vec4 grainColor = vec4(nR, nG, nB, 1.0); - - // Mix it with opacity - fragColor = grainColor * qt_Opacity; -} diff --git a/src/ui/qml/glass.qsb b/src/ui/qml/glass.qsb deleted file mode 100644 index 09ab52f..0000000 Binary files a/src/ui/qml/glass.qsb and /dev/null differ diff --git a/src/ui/qml/noise.frag b/src/ui/qml/noise.frag deleted file mode 100644 index 713646a..0000000 --- a/src/ui/qml/noise.frag +++ /dev/null @@ -1,25 +0,0 @@ -#version 440 - -layout(location = 0) in vec2 qt_TexCoord0; -layout(location = 0) out vec4 fragColor; - -layout(std140, binding = 0) uniform buf { - mat4 qt_Matrix; - float qt_Opacity; - float time; -}; - -// High-quality pseudo-random function -float rand(vec2 co) { - return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); -} - -void main() { - // Dynamic Noise based on Time - // We add 'time' to the coordinate to animate the grain - float noise = rand(qt_TexCoord0 + vec2(time * 0.01, time * 0.02)); - - // Output grayscale noise with alpha modulation - // We want white noise, applied with qt_Opacity - fragColor = vec4(noise, noise, noise, 1.0) * qt_Opacity; -} diff --git a/src/ui/qml/noise.qsb b/src/ui/qml/noise.qsb deleted file mode 100644 index 70a1ff5..0000000 Binary files a/src/ui/qml/noise.qsb and /dev/null differ diff --git a/src/ui/qml/settings.png b/src/ui/qml/settings.png deleted file mode 100644 index d8dbd4c..0000000 Binary files a/src/ui/qml/settings.png and /dev/null differ diff --git a/src/ui/qml/smart_toy.png b/src/ui/qml/smart_toy.png deleted file mode 100644 index 274d80d..0000000 Binary files a/src/ui/qml/smart_toy.png and /dev/null differ diff --git a/src/ui/qml/visibility.png b/src/ui/qml/visibility.png deleted file mode 100644 index d8db75f..0000000 Binary files a/src/ui/qml/visibility.png and /dev/null differ diff --git a/src/ui/settings.py b/src/ui/settings.py deleted file mode 100644 index c603343..0000000 --- a/src/ui/settings.py +++ /dev/null @@ -1,236 +0,0 @@ -""" -Settings Window Module. -======================= - -Manages the application configuration UI. -Refactored for 2026 Premium Aesthetics with Sidebar navigation. -""" - -from PySide6.QtWidgets import ( - QWidget, QVBoxLayout, QHBoxLayout, QStackedWidget, - QLabel, QComboBox, QFormLayout, QFrame, QMessageBox, QScrollArea -) -from PySide6.QtCore import Qt, Signal, Slot, QSize -from PySide6.QtGui import QFont, QIcon - -from src.core.config import ConfigManager -from src.ui.styles import Theme, StyleGenerator, load_modern_fonts -from src.ui.components import FramelessWindow, ModernFrame, GlassButton, ModernSwitch, ModernSlider -import sounddevice as sd - -class SettingsWindow(FramelessWindow): - """ - The main settings dialog. - Refactored with 2026 Premium Sidebar Layout. - """ - settings_changed = Signal() - - def __init__(self, parent=None): - super().__init__(parent) - self.config = ConfigManager() - self.setFixedSize(700, 500) - - # Main Container - self.bg_frame = ModernFrame() - self.bg_frame.setStyleSheet(StyleGenerator.get_glass_card(radius=20)) - - self.root_layout = QVBoxLayout(self) - self.root_layout.setContentsMargins(10, 10, 10, 10) - self.root_layout.addWidget(self.bg_frame) - - # Title Bar Area (Inside glass card) - self.title_layout = QHBoxLayout() - self.title_layout.setContentsMargins(20, 15, 20, 0) - - title_lbl = QLabel("PREMIUM SETTINGS") - title_lbl.setFont(load_modern_fonts()) - title_lbl.setStyleSheet(f"color: white; font-weight: 900; font-size: 14px; letter-spacing: 2px;") - self.title_layout.addWidget(title_lbl) - - self.title_layout.addStretch() - - self.btn_close = GlassButton("×", accent_color="#ff4b4b") - self.btn_close.setFixedSize(30, 30) - self.btn_close.clicked.connect(self.close) - self.title_layout.addWidget(self.btn_close) - - # Central Layout (Sidebar + Content) - self.content_layout = QHBoxLayout() - self.content_layout.setContentsMargins(10, 10, 10, 10) - self.content_layout.setSpacing(10) - - # 1. SIDEBAR - self.sidebar = QWidget() - self.sidebar.setFixedWidth(160) - self.sidebar_layout = QVBoxLayout(self.sidebar) - self.sidebar_layout.setContentsMargins(0, 10, 0, 10) - self.sidebar_layout.setSpacing(8) - - self.nav_general = GlassButton("General") - self.nav_audio = GlassButton("Audio") - self.nav_visuals = GlassButton("Visuals") - self.nav_advanced = GlassButton("Advanced/AI") - - self.sidebar_layout.addWidget(self.nav_general) - self.sidebar_layout.addWidget(self.nav_audio) - self.sidebar_layout.addWidget(self.nav_visuals) - self.sidebar_layout.addWidget(self.nav_advanced) - self.sidebar_layout.addStretch() - - self.btn_save = GlassButton("SAVE CHANGES", accent_color=Theme.ACCENT_GREEN) - self.btn_save.clicked.connect(self.save_settings) - self.sidebar_layout.addWidget(self.btn_save) - - # 2. CONTENT STACK - self.stack = QStackedWidget() - self.stack.setStyleSheet("background: transparent;") - - # Connect sidebar to stack - self.nav_general.clicked.connect(lambda: self.stack.setCurrentIndex(0)) - self.nav_audio.clicked.connect(lambda: self.stack.setCurrentIndex(1)) - self.nav_visuals.clicked.connect(lambda: self.stack.setCurrentIndex(2)) - self.nav_advanced.clicked.connect(lambda: self.stack.setCurrentIndex(3)) - - # Main Layout Assembly - self.inner_layout = QVBoxLayout(self.bg_frame) - self.inner_layout.addLayout(self.title_layout) - self.inner_layout.addLayout(self.content_layout) - - self.content_layout.addWidget(self.sidebar) - self.content_layout.addWidget(self.stack) - - self.setup_pages() - self.load_values() - - def setup_pages(self): - """Creates the settings pages.""" - # --- GENERAL --- - self.page_general = QWidget() - l1 = QFormLayout(self.page_general) - l1.setVerticalSpacing(20) - - self.inp_hotkey = QComboBox() - self.inp_hotkey.addItems(["f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "caps lock"]) - self.inp_hotkey.setStyleSheet(f"background: {Theme.BG_DARK}; border-radius: 4px; padding: 5px; color: white;") - l1.addRow(self.create_lbl("Global Hotkey:"), self.inp_hotkey) - - self.chk_top = ModernSwitch() - l1.addRow(self.create_lbl("Always on Top:"), self.chk_top) - - self.stack.addWidget(self.page_general) - - # --- AUDIO --- - self.page_audio = QWidget() - l2 = QFormLayout(self.page_audio) - l2.setVerticalSpacing(15) - - self.inp_device = QComboBox() - self.inp_device.setStyleSheet(f"background: {Theme.BG_DARK}; border-radius: 4px; padding: 5px; color: white;") - self.populate_audio_devices() - l2.addRow(self.create_lbl("Input Device:"), self.inp_device) - - self.sld_threshold = ModernSlider(Qt.Horizontal) - self.sld_threshold.setRange(1, 25) - self.lbl_threshold = self.create_lbl("2%") - self.sld_threshold.valueChanged.connect(lambda v: self.lbl_threshold.setText(f"{v}%")) - l2.addRow(self.create_lbl("Noise Gate:"), self.sld_threshold) - l2.addRow("", self.lbl_threshold) - - self.sld_duration = ModernSlider(Qt.Horizontal) - self.sld_duration.setRange(5, 50) - self.lbl_duration = self.create_lbl("1.0s") - self.sld_duration.valueChanged.connect(lambda v: self.lbl_duration.setText(f"{v/10}s")) - l2.addRow(self.create_lbl("Auto-Submit:"), self.sld_duration) - l2.addRow("", self.lbl_duration) - - self.stack.addWidget(self.page_audio) - - # --- VISUALS --- - self.page_visuals = QWidget() - l3 = QFormLayout(self.page_visuals) - l3.setVerticalSpacing(20) - - self.inp_style = QComboBox() - self.inp_style.addItem("Neon Line (Recommended)", "line") - self.inp_style.addItem("Classic Bars", "bar") - self.inp_style.setStyleSheet(f"background: {Theme.BG_DARK}; border-radius: 4px; padding: 5px; color: white;") - l3.addRow(self.create_lbl("Visualizer:"), self.inp_style) - - self.sld_opacity = ModernSlider(Qt.Horizontal) - self.sld_opacity.setRange(40, 100) - self.lbl_opacity = self.create_lbl("100%") - self.sld_opacity.valueChanged.connect(lambda v: self.lbl_opacity.setText(f"{v}%")) - l3.addRow(self.create_lbl("Opacity:"), self.sld_opacity) - l3.addRow("", self.lbl_opacity) - - self.stack.addWidget(self.page_visuals) - - # --- ADVANCED --- - self.page_adv = QWidget() - l4 = QFormLayout(self.page_adv) - l4.setVerticalSpacing(15) - - self.inp_model = QComboBox() - self.inp_model.setStyleSheet(f"background: {Theme.BG_DARK}; border-radius: 4px; padding: 5px; color: white;") - for id, name in [("tiny", "Tiny (Fast)"), ("base", "Base"), ("small", "Small (Default)"), ("medium", "Medium"), ("large-v3", "Large V3")]: - self.inp_model.addItem(name, id) - l4.addRow(self.create_lbl("Model:"), self.inp_model) - - info = QLabel("Large models provide higher accuracy but require significant RAM/VRAM.") - info.setWordWrap(True) - info.setStyleSheet(f"color: {Theme.TEXT_SECONDARY}; font-style: italic; font-size: 11px;") - l4.addRow("", info) - - self.stack.addWidget(self.page_adv) - - def create_lbl(self, text): - lbl = QLabel(text) - lbl.setStyleSheet(f"color: {Theme.TEXT_SECONDARY}; font-weight: 600; font-size: 13px;") - return lbl - - def populate_audio_devices(self): - try: - self.inp_device.addItem("System Default", -1) - for i, dev in enumerate(sd.query_devices()): - if dev['max_input_channels'] > 0: - self.inp_device.addItem(dev['name'], i) - except: pass - - def load_values(self): - self.inp_hotkey.setCurrentText(self.config.get("hotkey")) - self.chk_top.setChecked(self.config.get("always_on_top")) - - dev_id = self.config.get("input_device") - idx = self.inp_device.findData(dev_id if dev_id is not None else -1) - if idx >= 0: self.inp_device.setCurrentIndex(idx) - - self.sld_threshold.setValue(int(self.config.get("silence_threshold") * 100)) - self.sld_duration.setValue(int(self.config.get("silence_duration") * 10)) - - idx = self.inp_style.findData(self.config.get("visualizer_style")) - if idx >= 0: self.inp_style.setCurrentIndex(idx) - - self.sld_opacity.setValue(int(self.config.get("opacity") * 100)) - - idx = self.inp_model.findData(self.config.get("model_size")) - if idx >= 0: self.inp_model.setCurrentIndex(idx) - - def save_settings(self): - updates = { - "hotkey": self.inp_hotkey.currentText(), - "always_on_top": self.chk_top.isChecked(), - "input_device": self.inp_device.currentData() if self.inp_device.currentData() != -1 else None, - "silence_threshold": self.sld_threshold.value() / 100.0, - "silence_duration": self.sld_duration.value() / 10.0, - "visualizer_style": self.inp_style.currentData(), - "opacity": self.sld_opacity.value() / 100.0, - "model_size": self.inp_model.currentData() - } - - new_model = updates["model_size"] - if new_model != self.config.get("model_size"): - QMessageBox.information(self, "Model Updated", f"Downloaded {new_model} on next launch.") - - self.config.set_bulk(updates) - self.settings_changed.emit() - self.close() diff --git a/src/ui/styles.py b/src/ui/styles.py deleted file mode 100644 index 436f82a..0000000 --- a/src/ui/styles.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -Style Engine Module. -==================== - -Centralized design system for the 2026 Premium UI. -Defines color palettes, glassmorphism templates, and modern font loading. -""" - -from PySide6.QtGui import QColor, QFont, QFontDatabase -import os - -class Theme: - """Premium Dark Theme Palette (2026 Edition).""" - # Backgrounds - BG_DARK = "#0d0d12" # Deep cosmic black - BG_CARD = "#16161e" # Slightly lighter for components - BG_GLASS = "rgba(22, 22, 30, 0.7)" # Semi-transparent for glass effect - - # Neons & Accents - ACCENT_CYAN = "#00f2ff" # Electric cyan - ACCENT_PURPLE = "#7000ff" # Deep cyber purple - ACCENT_GREEN = "#00ff88" # Mint neon - - # Text - TEXT_PRIMARY = "#ffffff" # Pure white - TEXT_SECONDARY = "#9499b0" # Muted blue-gray - TEXT_MUTED = "#565f89" # Darker blue-gray - - # Borders - BORDER_SUBTLE = "rgba(100, 100, 150, 0.2)" - BORDER_GLOW = "rgba(0, 242, 255, 0.5)" - -class StyleGenerator: - """Generates QSS strings for complex effects.""" - - @staticmethod - def get_glass_card(radius=12, border=True): - """Returns QSS for a glassmorphism card.""" - border_css = f"border: 1px solid {Theme.BORDER_SUBTLE};" if border else "border: none;" - return f""" - background-color: {Theme.BG_GLASS}; - border-radius: {radius}px; - {border_css} - """ - - @staticmethod - def get_glow_border(color=Theme.ACCENT_CYAN): - """Returns QSS for a glowing border state.""" - return f"border: 1px solid {color};" - -def load_modern_fonts(): - """Attempts to load a modern font stack for the 2026 look.""" - # Preferred order: Segoe UI Variable, Inter, Segoe UI, sans-serif - families = ["Segoe UI Variable Text", "Inter", "Segoe UI", "sans-serif"] - - for family in families: - font = QFont(family, 10) - if QFontDatabase.families().count(family) > 0: - return font - - # Absolute fallback - return QFont("Arial", 10) diff --git a/src/ui/visualizer.py b/src/ui/visualizer.py deleted file mode 100644 index 1e71772..0000000 --- a/src/ui/visualizer.py +++ /dev/null @@ -1,117 +0,0 @@ -""" -Audio Visualizer Module. -======================== - -High-Fidelity rendering for the 2026 Premium UI. -Supports 'Classic Bars' and 'Neon Line' with smooth curves and glows. -""" - -from PySide6.QtWidgets import QWidget -from PySide6.QtCore import Qt, QTimer, Slot, QRectF, QPointF -from PySide6.QtGui import QPainter, QBrush, QColor, QPainterPath, QPen, QLinearGradient -import random - -from src.ui.styles import Theme - -class AudioVisualizer(QWidget): - """ - A premium audio visualizer with smooth physics and neon aesthetics. - """ - - def __init__(self, parent=None): - super().__init__(parent) - self.amplitude = 0.0 - self.bars = 12 - self.history = [0.0] * self.bars - - # High-refresh timer for silky smooth motion - self.timer = QTimer(self) - self.timer.timeout.connect(self.update_animation) - self.timer.start(16) # ~60 FPS - - @Slot(float) - def set_amplitude(self, amp: float): - self.amplitude = amp - - def update_animation(self): - self.history.pop(0) - # Smooth interpolation + noise - jitter = random.uniform(0.01, 0.03) - # Decay logic: Gravity-like pull - self.history.append(max(self.amplitude, jitter)) - self.update() - - def paintEvent(self, event): - from src.core.config import ConfigManager - style = ConfigManager().get("visualizer_style") - - painter = QPainter(self) - painter.setRenderHint(QPainter.Antialiasing) - - w, h = self.width(), self.height() - painter.translate(0, h / 2) - - if style == "bar": - self._draw_bars(painter, w, h) - else: - self._draw_line(painter, w, h) - - def _draw_bars(self, painter, w, h): - bar_w = w / self.bars - spacing = 3 - - for i, val in enumerate(self.history): - bar_h = val * (h * 0.9) - x = i * bar_w - - # Gradient Bar - grad = QLinearGradient(0, -bar_h/2, 0, bar_h/2) - grad.setColorAt(0, QColor(Theme.ACCENT_PURPLE)) - grad.setColorAt(1, QColor(Theme.ACCENT_CYAN)) - - painter.setBrush(grad) - painter.setPen(Qt.NoPen) - painter.drawRoundedRect(QRectF(x + spacing, -bar_h/2, bar_w - spacing*2, bar_h), 3, 3) - - def _draw_line(self, painter, w, h): - path = QPainterPath() - points = len(self.history) - dx = w / (points - 1) - - path.moveTo(0, 0) - - def get_path(multi): - p = QPainterPath() - p.moveTo(0, 0) - for i in range(points): - curr_x = i * dx - curr_y = -self.history[i] * (h * 0.45) * multi - if i == 0: - p.moveTo(curr_x, curr_y) - else: - prev_x = (i-1) * dx - # Simple lerp or quadTo for smoothness - p.lineTo(curr_x, curr_y) - return p - - # Draw Top & Bottom - p_top = get_path(1) - p_bot = get_path(-1) - - # Glow layer - glow_pen = QPen(QColor(Theme.ACCENT_CYAN)) - glow_pen.setWidth(4) - glow_alpha = QColor(Theme.ACCENT_CYAN) - glow_alpha.setAlpha(60) - glow_pen.setColor(glow_alpha) - - painter.setPen(glow_pen) - painter.drawPath(p_top) - painter.drawPath(p_bot) - - # Core layer - core_pen = QPen(Qt.white) - core_pen.setWidth(2) - painter.setPen(core_pen) - painter.drawPath(p_top) - painter.drawPath(p_bot) diff --git a/test_m2m.py b/test_m2m.py deleted file mode 100644 index 466c185..0000000 --- a/test_m2m.py +++ /dev/null @@ -1,38 +0,0 @@ - -import sys -from transformers import M2M100ForConditionalGeneration, M2M100Tokenizer - -def test_m2m(): - model_name = "facebook/m2m100_418M" - print(f"Loading {model_name}...") - - tokenizer = M2M100Tokenizer.from_pretrained(model_name) - model = M2M100ForConditionalGeneration.from_pretrained(model_name) - - # Test cases: (Language Code, Input) - test_cases = [ - ("en", "he go to school yesterday"), - ("pl", "on iść do szkoła wczoraj"), # Intentional broken grammar in Polish - ] - - print("\nStarting M2M Tests (Self-Translation):\n") - - for lang, input_text in test_cases: - tokenizer.src_lang = lang - encoded = tokenizer(input_text, return_tensors="pt") - - # Translate to SAME language - generated_tokens = model.generate( - **encoded, - forced_bos_token_id=tokenizer.get_lang_id(lang) - ) - - corrected = tokenizer.batch_decode(generated_tokens, skip_special_tokens=True)[0] - - print(f"[{lang}]") - print(f"Input: {input_text}") - print(f"Output: {corrected}") - print("-" * 20) - -if __name__ == "__main__": - test_m2m() diff --git a/test_mt0.py b/test_mt0.py deleted file mode 100644 index 1ff54ae..0000000 --- a/test_mt0.py +++ /dev/null @@ -1,40 +0,0 @@ - -import sys -from transformers import AutoTokenizer, AutoModelForSeq2SeqLM - -def test_mt0(): - model_name = "bigscience/mt0-base" - print(f"Loading {model_name}...") - - tokenizer = AutoTokenizer.from_pretrained(model_name) - model = AutoModelForSeq2SeqLM.from_pretrained(model_name) - - # Test cases: (Language, Prompt, Input) - # MT0 is instruction tuned, so we should prompt it in the target language or English. - # Cross-lingual prompting (English prompt -> Target tasks) is usually supported. - - test_cases = [ - ("English", "Correct grammar:", "he go to school yesterday"), - ("Polish", "Popraw gramatykę:", "to jest testowe zdanie bez kropki"), - ("Finnish", "Korjaa kielioppi:", "tämä on testilause ilman pistettä"), - ("Russian", "Исправь грамматику:", "это тестовое предложение без точки"), - ("Japanese", "文法を直してください:", "これは点のないテスト文です"), - ("Spanish", "Corrige la gramática:", "esta es una oración de prueba sin punto"), - ] - - print("\nStarting MT0 Tests:\n") - - for lang, prompt_text, input_text in test_cases: - full_input = f"{prompt_text} {input_text}" - inputs = tokenizer(full_input, return_tensors="pt") - - outputs = model.generate(inputs.input_ids, max_length=128) - corrected = tokenizer.decode(outputs[0], skip_special_tokens=True) - - print(f"[{lang}]") - print(f"Input: {full_input}") - print(f"Output: {corrected}") - print("-" * 20) - -if __name__ == "__main__": - test_mt0() diff --git a/test_punctuation.py b/test_punctuation.py deleted file mode 100644 index 7127c0a..0000000 --- a/test_punctuation.py +++ /dev/null @@ -1,34 +0,0 @@ - -import sys -import os - -# Add src to path -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -from src.core.grammar_assistant import GrammarAssistant - -def test_punctuation(): - assistant = GrammarAssistant() - assistant.load_model() - - samples = [ - # User's example (verbatim) - "If the voice recognition doesn't recognize that I like stopped Or something would that would it also correct that", - - # Generic run-on - "hello how are you doing today i am doing fine thanks for asking", - - # Missing commas/periods - "well i think its valid however we should probably check the logs first" - ] - - print("\nStarting Punctuation Tests:\n") - - for sample in samples: - print(f"Original: {sample}") - corrected = assistant.correct(sample) - print(f"Corrected: {corrected}") - print("-" * 20) - -if __name__ == "__main__": - test_punctuation()