# 075 Agents 455a8400

> Supervertaler - AI Agent Documentation

- Skill: `tools-only/075-agents-455a8400` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add tools-only/075-agents-455a8400`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/075-agents-455a8400/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/tools-only/075-agents-455a8400

---

# Supervertaler - AI Agent Documentation

> **This is the single source of truth for AI coding assistants working on this project.**
> **Last Updated:** February 7, 2026 | **Version:** v1.9.236

---

## ⚡ QUICK START FOR AI AGENTS

**IMPORTANT: If you're continuing from a previous session or ran out of context:**

1. **Skip to the end of this file** - The most recent development context is in the **"🔄 Recent Development History"** section (search for the latest date)
2. **Current version: v1.9.236** - Custom OpenAI-Compatible API provider, version fix, multi-file export
3. **Read only what you need** - The Project Overview and Architecture sections are reference material; the dated history entries contain the actual working context

**Quick Navigation:**
- **Latest context:** See CHANGELOG.md for v1.9.236 (Custom OpenAI-Compatible API)
- **Module list:** Search for `## 🔌 Complete Module List`
- **Architecture:** Search for `## 🏗️ Architecture Patterns`
- **Common pitfalls:** Search for `## ⚠️ Common Pitfalls`

---

## 🎯 Project Overview

**Supervertaler** is a professional desktop translation application built with Python and PyQt6. It serves as a companion tool for translators, integrating AI-powered translation with traditional CAT (Computer-Assisted Translation) tool workflows.

| Property | Value |
|----------|-------|
| **Name** | Supervertaler |
| **Version** | v1.9.236 (February 2026) |
| **Framework** | PyQt6 (Qt for Python) |
| **Language** | Python 3.10+ |
| **Platform** | Windows (primary), Linux compatible |
| **Repository** | https://github.com/michaelbeijer/Supervertaler |
| **Website** | https://supervertaler.com |
| **Main File** | `Supervertaler.py` (~38,000+ lines) |
| **Modules** | 60+ specialized modules in `modules/` directory |

### Key Capabilities

- **Multi-LLM AI Translation**: OpenAI GPT-4, Anthropic Claude, Google Gemini, Local Ollama, Custom OpenAI-Compatible API (Volcengine/Doubao, Tongyi/Qwen, DeepSeek, etc.)
- **CAT Tool Integration**: Trados SDLPPX/SDLRPX, memoQ XLIFF/DOCX/RTF, Phrase/Memsource DOCX, CafeTran DOCX, Déjà Vu X3 RTF
- **Translation Memory**: Fuzzy matching TM with TMX import/export + Supermemory (ChromaDB vector search)
- **Terminology Management**: SQLite-based termbases with priority highlighting and automatic extraction
- **Document Handling**: DOCX, bilingual DOCX, PDF (via OCR), simple TXT, **Markdown (MD)**, **Multi-file folder import**
- **Quality Assurance**: Spellcheck, tag validation, consistency checking
- **Superlookup**: Unified concordance hub with TM, Termbase, Supermemory, MT, and Web Resources
- **Batteries Included**: Default install includes all major features and dependencies, except heavy optional components (Supermemory + offline Local Whisper)

---

## 📦 Installation (Simplified)

Supervertaler uses a batteries-included *core* install: `pip install supervertaler` pulls in everything needed for the major features, while keeping heavy optional components out of the default install.

**Note:** Offline dictation via Local Whisper is optional because it pulls in very large dependencies (PyTorch). The default/recommended voice path uses the OpenAI Whisper API.

**Legacy note:** older “extras” (e.g. `supervertaler[all]`) are still accepted for backward compatibility, but they no longer change what gets installed.

### Installation Options

| Command | Notes |
|---------|-------|
| `pip install supervertaler` | Recommended core install (excludes heavy optional Local Whisper) |
| `pip install supervertaler[local-whisper]` | Adds offline Local Whisper (heavy; pulls PyTorch) |
| `pip install supervertaler[all]` | Legacy alias (no-op; kept for compatibility) |

### Feature Modules

| Module | Included by default | Size | Description |
|--------|----------------------|------|-------------|
| **Supervoice** | ✅ | ~150 MB | Voice dictation & commands (OpenAI Whisper API recommended) |
| **Web Browser** | ✅ | ~100 MB | Built-in browser for Superlookup |
| **PDF Rescue** | ✅ | ~30 MB | PDF text extraction/OCR |
| **MT Providers** | ✅ | ~30 MB | DeepL, Amazon Translate |
| **Hunspell** | ✅ | ~20 MB | Advanced spellcheck |
| **AutoFingers** | ✅ (Windows) | ~10 MB | Windows automation |

### Settings → Features Tab

Users can enable/disable features in **Settings → 📦 Features**.

### Feature Manager Module

The `modules/feature_manager.py` provides:
- `FeatureManager` class for checking feature availability
- `FEATURE_MODULES` dict defining all optional features
- `lazy_import_*()` functions for conditional imports
- `check_feature(id)` quick availability check

---

## 🪟 Windows EXE Releases (CORE + FULL)

Supervertaler Windows releases are published as **two separate ZIP assets** on the same GitHub Release:

1) **CORE** (recommended for most users)
- Smaller download
- Does **not** bundle the heavy ML stack (Supermemory + offline Local Whisper)

2) **FULL** ("batteries included")
- Larger download
- Bundles **Supermemory** + **offline Local Whisper** (PyTorch / sentence-transformers / ChromaDB)

### Key Rule (PyInstaller one-folder builds)

The EXE must be run from the extracted distribution folder. Do **not** separate `Supervertaler.exe` from `_internal/`.

If users see an error like missing `python312.dll`, they are almost always:
- running the wrong EXE (from an intermediate build folder), or
- moving the EXE away from `_internal/`.

### How to Build (recommended)

Use the automated build script:

```powershell
# Build BOTH core + full and create both ZIP assets
powershell -NoProfile -ExecutionPolicy Bypass -File .\build_windows_release.ps1

# Build only core
powershell -NoProfile -ExecutionPolicy Bypass -File .\build_windows_release.ps1 -CoreOnly

# Build only full
powershell -NoProfile -ExecutionPolicy Bypass -File .\build_windows_release.ps1 -FullOnly

# Clean build venvs + rebuild (keeps dist/ so multi-flavor builds can coexist)
powershell -NoProfile -ExecutionPolicy Bypass -File .\build_windows_release.ps1 -Clean
```

What it does:
- Uses two isolated build environments: `.venv-build-core` and `.venv-build-full`
- Runs PyInstaller with the corresponding spec:
  - `Supervertaler.core.spec` → `dist\Supervertaler-core\...`
  - `Supervertaler.full.spec` → `dist\Supervertaler-full\...`
- Zips each output using `create_release_zip.py` and writes a `README_FIRST.txt` into each dist folder.

### Output Files

After a successful run, you should have:
- `dist\Supervertaler-v<version>-Windows-CORE.zip`
- `dist\Supervertaler-v<version>-Windows-FULL.zip`

### GitHub Release Posting

Attach **both** ZIP files to the same GitHub Release tag.

### VS Code Tasks

There are build tasks wired up in `.vscode/tasks.json`:
- "Build Windows EXE (core)"
- "Build Windows EXE (full)"
- "Build Windows EXE (core + full)"

---

## 📁 Project Structure

```
Supervertaler/
├── Supervertaler.py          # Main application (~38,000+ lines)
├── modules/                   # 60+ specialized modules
│   ├── feature_manager.py    # Modular feature management (NEW)
│   ├── llm_clients.py        # OpenAI, Anthropic, Google Gemini, Ollama
│   ├── translation_memory.py # TM matching and storage
│   ├── termbase_manager.py   # Terminology management
│   ├── docx_handler.py       # DOCX import/export
│   ├── sdlppx_handler.py     # Trados Studio packages
│   ├── phrase_docx_handler.py# Phrase/Memsource bilingual
│   ├── cafetran_docx_handler.py # CafeTran bilingual
│   ├── supermemory.py        # Vector-indexed semantic TM (ChromaDB)
│   ├── spellcheck_manager.py # Spellcheck with pyspellchecker/Hunspell
│   ├── prompt_library.py     # AI prompt management
│   └── ...                   # See module list below
├── user_data/                 # User content (gitignored)
│   ├── prompts/              # .svprompt files
│   ├── termbases/            # .db termbase files
│   ├── translation_memories/ # .db TM files
│   ├── dictionaries/         # Custom spellcheck words
│   └── supermemory/          # ChromaDB vector database
├── assets/                    # Icons, images
├── docs/                      # Documentation site
├── beijerterm/                # 🔗 GIT SUBMODULE - Beijerterm glossary website
├── tests/                     # Test files
└── legacy_versions/           # Historical Tkinter version
```

---

## 🔗 Beijerterm Website Submodule (IMPORTANT FOR AI AGENTS)

The `beijerterm/` folder is a **Git submodule** - a separate repository embedded inside Supervertaler.

### What is a Submodule?

- It's a **pointer** to a specific commit in another repository
- Regular `git clone` does NOT download submodule contents (saves ~15 MB)
- The submodule has its own `.git` and tracks `michaelbeijer/beijerterm` separately

### Two Separate Projects

| Project | Location | Purpose |
|---------|----------|---------|
| **Supervertaler** | Root folder | PyQt6 translation app |
| **Beijerterm** | `beijerterm/` subfolder | Static glossary website (beijerterm.com) |

**Note**: The "Superlookup" name refers to the in-app lookup panel inside Supervertaler - don't confuse it with the Beijerterm website!

### Working with the Submodule

**To make changes to Beijerterm website:**
```bash
cd beijerterm/
# Edit files...
git add .
git commit -m "your message"
git push origin main          # Pushes to michaelbeijer/beijerterm
```

**Then update the parent reference:**
```bash
cd ..                          # Back to Supervertaler root
git add beijerterm             # Stage the new submodule commit pointer
git commit -m "chore: Update beijerterm submodule"
git push origin main           # Pushes to michaelbeijer/Supervertaler
```

### ⚠️ Common Pitfalls

1. **Commits in submodule aren't automatically tracked** - After committing inside `beijerterm/`, you must also commit in the parent repo to update the pointer.

2. **Changelogs are separate** - Beijerterm website changes go in `beijerterm/CHANGELOG.md`, Supervertaler changes go in `CHANGELOG.md` at root.

3. **Features in the wrong repo** - The "Superlookup" panel inside Supervertaler.py is part of **Supervertaler**, not the Beijerterm website. Don't confuse them!

4. **Building the website** - Run `python scripts/build_site.py` from inside `beijerterm/`, not from root.

---

## 🔧 Key Technical Details

### Main Application (`Supervertaler.py`)

The main file is a large monolithic PyQt6 application. Key sections:

| Line Range | Purpose |
|------------|---------|
| 1-700 | Imports, constants, Project dataclass |
| 700-2000 | Custom widgets (grid editors, checkboxes) |
| 2000-4500 | MainWindow initialization, UI setup |
| 4500-8000 | Menu actions, file operations |
| 8000-12000 | Settings dialogs |
| 12000-18000 | Grid operations, navigation |
| 18000-25000 | Import/Export handlers |
| 25000-32000 | AI translation, batch operations |

### Key Classes

```python
@dataclass
class Project:
    segments: List[Segment]
    source_lang: str
    target_lang: str
    original_docx_path: Optional[str] = None
    memoq_source_path: Optional[str] = None
    sdlppx_source_path: Optional[str] = None
    phrase_source_path: Optional[str] = None
    original_txt_path: Optional[str] = None
    # ... 20+ fields total

@dataclass
class Segment:
    source: str
    target: str = ""
    status: str = "Not Started"
    notes: str = ""
    segment_type: str = "text"
    # ... additional fields
```

### File Extensions

| Extension | Purpose |
|-----------|---------|
| `.svproj` | Supervertaler project files (JSON) |
| `.svprompt` | Prompt files (JSON) |
| `.svntl` | Non-translatables lists (JSON) |

---

## 🔌 Complete Module List

### AI & LLM (`modules/`)
- `llm_clients.py` - OpenAI, Anthropic Claude, Google Gemini, Ollama, Custom OpenAI-Compatible API integration
- `model_version_checker.py` - Auto-detect new LLM models from providers
- `model_update_dialog.py` - UI for selecting new models
- `prompt_library.py` - Prompt management and favorites
- `prompt_assistant.py` - AI-powered prompt generation
- `unified_prompt_library.py` - Unified prompt system
- `unified_prompt_manager_qt.py` - Prompt manager UI
- `voice_dictation.py` - Whisper-based voice input
- `voice_commands.py` - Talon-style voice command system (NEW)
- `ai_actions.py` - AI action system for prompt library
- `ai_attachment_manager.py` - File attachment persistence
- `ai_file_viewer_dialog.py` - File viewing dialog

### Translation Memory & Terminology
- `translation_memory.py` - Fuzzy matching TM system
- `supermemory.py` - ChromaDB vector semantic search (2100+ lines)
- `termbase_manager.py` - SQLite-based terminology
- `term_extractor.py` - Automatic term extraction
- `termbase_entry_editor.py` - Term editing UI
- `termbase_import_export.py` - TMX/TBX import/export
- `tm_manager_qt.py` - TM management UI
- `tm_metadata_manager.py` - TM metadata handling
- `tm_editor_dialog.py` - TM editing dialog
- `tmx_editor.py` / `tmx_editor_qt.py` - TMX file editing
- `tmx_generator.py` - TMX file generation

### File Handlers
- `docx_handler.py` - Standard DOCX import/export
- `sdlppx_handler.py` - Trados Studio SDLPPX/SDLRPX packages (767+ lines)
- `phrase_docx_handler.py` - Phrase/Memsource bilingual DOCX
- `cafetran_docx_handler.py` - CafeTran bilingual DOCX
- `trados_docx_handler.py` - Trados bilingual review DOCX
- `dejavurtf_handler.py` - Déjà Vu X3 bilingual RTF (NEW in v1.9.91)
- `mqxliff_handler.py` - memoQ XLIFF files
- `simple_segmenter.py` - Text segmentation

### Spellcheck & QA
- `spellcheck_manager.py` - Dual-backend spellcheck (pyspellchecker + Hunspell)
- `non_translatables_manager.py` - Non-translatable term management
- `tag_cleaner.py` - CAT tool tag removal
- `tag_manager.py` - Tag handling

### UI Components
- `ribbon_widget.py` - Ribbon-style toolbar
- `translation_results_panel.py` - Match display panel
- `termview_widget.py` - Inline term display
- `superlookup.py` - Unified lookup window
- `superbrowser.py` - Multi-chat AI browser
- `quick_access_sidebar.py` - Quick access panel
- `keyboard_shortcuts_widget.py` - Shortcut management
- `project_home_panel.py` - Project home UI

### Utilities
- `database_manager.py` - SQLite database operations
- `database_migrations.py` - Database schema migrations
- `config_manager.py` - Settings management
- `file_dialog_helper.py` - File dialog utilities
- `find_replace.py` - Find and replace functionality
- `shortcut_manager.py` - Keyboard shortcut handling
- `theme_manager.py` - UI theme management
- `statuses.py` - Segment status definitions

### Specialized Tools
- `pdf_rescue_Qt.py` - AI OCR for PDF extraction
- `image_extractor.py` - Extract images from DOCX
- `figure_context_manager.py` - Image context for AI
- `document_analyzer.py` - Document analysis
- `encoding_repair.py` / `encoding_repair_Qt.py` - Fix encoding issues
- `autofingers_engine.py` - memoQ AutoFingers automation
- `tracked_changes.py` - Track changes analysis
- `supercleaner.py` / `supercleaner_ui.py` - Text cleaning

### Benchmarking
- `llm_leaderboard.py` - LLM quality benchmarking
- `superbench_ui.py` - Benchmark UI
- `local_llm_setup.py` - Ollama setup wizard

---

## 🏗️ Architecture Patterns

### UI Pattern
- PyQt6 with custom styled widgets
- Consistent styling:
  - Checkboxes: `CheckmarkCheckBox` (Standard), `PinkCheckmarkCheckBox` (Project), `BlueCheckmarkCheckBox` (Global)
  - Radio Buttons: `CheckmarkRadioButton` (Standard Green)
- Tag-based text formatting (`<b>`, `<i>`, `<u>`, `<li-o>`, `<li-b>`)
- Grid-based segment editor with source (read-only) and target (editable) columns

### Data Flow
1. Import file → Parse to segments → Display in grid
2. User translates/edits → Status updates → Grid refreshes
3. Export → Reconstruct original format with translations

### Settings Storage
- `user_data/general_settings.json` - App preferences
- `user_data/ui_preferences.json` - Window geometry, button states, LLM settings (`llm_settings`, `provider_enabled_states`)
- `user_data/api_keys.txt` - API keys for LLM and MT providers (key=value format)
- `.svproj` files - Per-project settings

---

## 📝 Development Guidelines

### When Editing Supervertaler.py
1. The file is large (~38K+ lines) - use line ranges when reading
2. Search for method names with `grep_search` before editing
3. Follow existing patterns for new features
4. `__version__` auto-reads from `pyproject.toml` (dev) or `importlib.metadata` (pip) — no manual update needed
5. To bump version: edit `pyproject.toml`, `CHANGELOG.md`, and `docs/index.html` (see Version Bump Checklist below)

### When Adding New Modules
1. Create in `modules/` directory
2. Add import in main file where needed
3. Follow existing module patterns (docstrings, type hints)

### Documentation Updates
**Always update these files after changes:**
1. `AGENTS.md` - Add dated entry to development history
2. `CHANGELOG.md` - Add version entry
3. `README.md` - Update version badge if needed

### Commit Messages
Use semantic prefixes:
- `feat:` - New feature
- `fix:` - Bug fix
- `docs:` - Documentation
- `refactor:` - Code restructuring
- `style:` - Formatting

---

## ⚠️ Common Pitfalls

1. **ElementTree namespaces** - Always use namespace dict when working with SDLXLIFF:
   ```python
   NAMESPACES = {'sdl': 'http://sdl.com/FileTypes/SdlXliff/1.0'}
   element.find('.//sdl:seg', NAMESPACES)
   ```

2. **Grid widget access** - Use `table.cellWidget()` for QTextEdit, `table.item()` for QTableWidgetItem

3. **File paths** - Store absolute paths, use `os.path.exists()` before accessing

4. **Status updates** - Remember to update both internal data and grid display

5. **Signal blocking** - When setting text programmatically, use `blockSignals(True/False)` to prevent cascading events

6. **Qt event queue** - Be aware that `setPlainText()` queues events even when signals are blocked

7. **Hidden widget styling** - Qt may not apply stylesheets to hidden widgets. If theme colors aren't appearing, apply styles when the widget becomes visible, not just at creation time

8. **Race conditions & timing** - When debugging UI issues, consider whether the problem might be timing-related:
   - Widgets created before theme manager is initialized
   - Styles applied while widgets are hidden
   - Signals firing before handlers are connected
   - Use `QTimer.singleShot()` for deferred initialization when needed

---

## 📋 Planned Features & Refactoring

### 🔑 Inline API Key Editing in Settings UI (Planned)

**Identified:** February 7, 2026
**Status:** Planned, not yet implemented
**Complexity:** Low-Medium

**Current Problem:**
Users must manually edit `api_keys.txt` in a text editor. Several users have reported this is inconvenient. The Settings UI currently only has an "Open API Keys File" button.

**Proposed Solution:**
Add password-masked `QLineEdit` fields in Settings > AI Settings for each provider's API key, with a show/hide toggle button (eye icon). Fields read from and write to the same `api_keys.txt` file, preserving backward compatibility.

**Implementation Notes:**
- Add `QLineEdit(echoMode=Password)` + toggle button for each provider (openai, claude, google/gemini, custom_openai, deepl, google_translate, etc.)
- Read initial values from `load_api_keys()` on settings dialog open
- Save back to `api_keys.txt` using same key=value format on "Save"
- Keep the "Open API Keys File" button for power users
- Consider grouping: LLM keys at top, MT keys below

---

### 🔧 Configuration File Consolidation (PRIORITY - Scheduled)

**Identified:** January 9, 2026  
**Status:** Documented, not yet implemented  
**Complexity:** Medium (30-40 file edits + migration script)

**Current Problem: Configuration File Sprawl**

Settings are currently scattered across multiple JSON files in `user_data/`:
- `general_settings.json` - General application preferences
- `ui_preferences.json` - Window geometry, button states (DEPRECATED, migrated to general_settings.json)
- `themes.json` - Theme definitions
- `recent_projects.json` - Recent project list
- `feature_settings.json` - Optional feature toggles
- `find_replace_history.json` - Find & Replace history
- `superlookup_history.json` - Superlookup search history
- `voice_commands.json` - Voice command library
- Plus module-specific configs...

**Issues:**
1. **Scattered settings** - Hard to find where a setting is stored
2. **Code complexity** - Multiple file read/write operations throughout codebase
3. **Inconsistent structure** - Each file has different patterns (nested vs flat, etc.)
4. **Migration headaches** - Changing structure requires updating multiple files
5. **User confusion** - Manual editing requires knowing which file to look in
6. **Backup/restore complexity** - Must backup multiple files to preserve all settings

**Proposed Solution: Single `config.json`**

Consolidate all settings into one well-structured file:

```json
{
  "config_version": "1.0.0",
  "general": {
    "restore_last_project": false,
    "auto_propagate_exact_matches": true,
    "auto_center_active_segment": true,
    "auto_insert_100_percent_matches": true,
    "auto_confirm_100_percent_matches": false,
    "tm_save_mode": "latest",
    "enable_smart_word_selection": true,
    "enable_auto_backup": true,
    "backup_interval_minutes": 5
  },
  "ui": {
    "theme": "light",
    "window_geometry": {
      "x": 100,
      "y": 100,
      "width": 1200,
      "height": 800
    },
    "fonts": {
      "grid_family": "Segoe UI",
      "grid_size": 11,
      "results_match_size": 9,
      "results_compare_size": 9,
      "termview_family": "Segoe UI",
      "termview_size": 10,
      "termview_bold": false
    },
    "colors": {
      "tag_color": "#7f0001",
      "focus_border_color": "#2196F3",
      "focus_border_thickness": 2,
      "badge_text_color": "#333333",
      "invisible_char_color": "#999999",
      "even_row_color": "#FFFFFF",
      "odd_row_color": "#F0F0F0"
    },
    "layout": {
      "tabs_above_grid": false,
      "enable_alternating_row_colors": true,
      "show_invisibles": false
    }
  },
  "features": {
    "supermemory_enabled": true,
    "supermemory_auto_init": false,
    "voice_enabled": false,
    "web_browser_enabled": true,
    "pdf_rescue_enabled": true,
    "autofingers_enabled": false
  },
  "match_limits": {
    "LLM": 3,
    "MT": 5,
    "TM": 10,
    "Termbases": 10
  },
  "termbase": {
    "enable_grid_highlighting": true,
    "highlight_style": "semibold",
    "display_order": "appearance",
    "hide_shorter_matches": false,
    "dotted_color": "#808080"
  },
  "precision_scroll": {
    "divisor": 3
  },
  "recent_projects": [
    "/path/to/project1.svproj",
    "/path/to/project2.svproj",
    "/path/to/project3.svproj"
  ],
  "custom_themes": [
    {
      "name": "Dark Blue",
      "is_dark": true,
      "colors": {
        "background": "#1e1e1e",
        "foreground": "#d4d4d4",
        ...
      }
    }
  ],
  "history": {
    "find_replace": [
      {"find": "example", "replace": "sample"}
    ],
    "superlookup_searches": [
      "translation memory",
      "terminology"
    ]
  },
  "voice_commands": {
    "enabled": false,
    "recognition_engine": "openai_whisper",
    "custom_commands": [
      {
        "phrase": "next segment",
        "aliases": ["go next", "move forward"],
        "action_type": "internal",
        "action": "next_segment"
      }
    ]
  },
  "ollama": {
    "keepwarm": false
  },
  "autohotkey": {
    "path": "C:\\Program Files\\AutoHotkey\\v2\\AutoHotkey64.exe"
  }
}
```

**Benefits:**
- ✅ **Single source of truth** - All settings in one place
- ✅ **Clear hierarchy** - Logical grouping by functionality
- ✅ **Easy to read/edit** - Clear structure for manual editing
- ✅ **Atomic saves** - All settings updated together (no partial updates)
- ✅ **Simple backup/restore** - One file to backup
- ✅ **Version tracking** - `config_version` field for migrations
- ✅ **Better defaults** - Easy to see all default values at once

**Implementation Plan:**

1. **Create `modules/config_manager_v2.py`** (new unified config manager):
   - `ConfigManager` class with section accessors
   - `get(section, key, default)` method
   - `set(section, key, value)` method
   - `save()` method (atomic write)
   - `load()` method with validation

2. **Migration script** (`scripts/migrate_config.py`):
   - Read all existing JSON files
   - Map to new structure
   - Write `config.json`
   - Backup old files to `user_data/config_backup/`
   - Run automatically on first startup after update

3. **Update all load/save calls** (~30-40 locations):
   - Replace `_load_general_settings_from_file()` → `config_manager.get('general', key)`
   - Replace `save_general_settings()` → `config_manager.set('general', key, value)` + `config_manager.save()`
   - Update theme manager
   - Update recent projects
   - Update find/replace history
   - Update voice command loader

4. **Backward compatibility**:
   - Keep old loaders as fallback for 1-2 versions
   - Log migration warnings
   - Auto-migrate on first run

5. **Testing checklist**:
   - [ ] Fresh install (no config files)
   - [ ] Migration from old files
   - [ ] Settings persist correctly
   - [ ] All features still work
   - [ ] No performance regression

**Files to modify:**
- `Supervertaler.py` - Replace all config load/save calls
- `modules/config_manager_v2.py` - NEW unified config manager
- `modules/theme_manager.py` - Use new config manager
- `modules/voice_commands.py` - Use new config manager
- `modules/find_replace_qt.py` - Use new config manager
- `scripts/migrate_config.py` - NEW migration script

**Estimated effort:** 3-4 hours (careful testing needed)

**Priority:** High (improves maintainability significantly)

---

## � Future Investigation: TMX Tag Export Format


**Issue discovered December 22, 2025**: Our TMX Editor stores formatting tags (like `<b>`, `<i>`) as escaped text (`&lt;b&gt;`) in the `<seg>` element. This is valid XML but may not be the optimal approach.

**Three approaches exist:**

| Approach | Example in TMX file | Pros | Cons |
|----------|---------------------|------|------|
| **Escaped text** | `<seg>&lt;b&gt;text&lt;/b&gt;</seg>` | Simple, valid XML | Tags treated as plain text, lost semantic meaning |
| **TMX inline elements** | `<seg><bpt i="1" type="bold">&lt;b&gt;</bpt>text<ept i="1">&lt;/b&gt;</ept></seg>` | TMX 1.4 compliant, preserves tag semantics | More complex to implement |
| **Raw XML** (invalid) | `<seg><b>text</b></seg>` | Human readable | Invalid XML unless DTD defines `<b>` |

**What other tools do:**
- **memoQ**: Uses TMX inline elements (`<bpt>`, `<ept>`) for proper tag preservation
- **Trados Studio**: Similar, uses inline elements with type attributes
- **OmegaT**: Generally uses escaped text for simplicity
- **Many web tools**: Just escape everything

**Recommendation for future**: Consider implementing proper TMX inline elements (`<bpt>`, `<ept>`, `<ph>`) when exporting. This would:
1. Maintain compatibility with professional CAT tools
2. Preserve tag type information (bold, italic, etc.)
3. Allow round-tripping without tag loss

**Files to modify**: `modules/tmx_editor.py` - `TmxParser.save_file()` method

---

## �💡 Problem-Solving Tips for AI Agents

When stuck on a difficult bug, consider these approaches:

1. **Think about timing**: Is this a race condition? Are things happening in the wrong order?
   - Widget creation vs. theme application timing
   - Signal connections vs. signal emissions
   - Hidden vs. visible widget state changes

2. **Think outside the box**: The obvious solution may not work
   - If stylesheets aren't applying, try QPalette as an alternative
   - If a method isn't being called, check if the widget is even visible
   - If changes aren't reflected, check if there's caching involved

3. **Add debug output**: When behavior is mysterious, add logging to trace execution flow
   - Print method entry/exit with timestamps
   - Log parameter values and state
   - Write to a debug file if console output is too fast

4. **Question assumptions**: What do you THINK is happening vs. what is ACTUALLY happening?
   - The code might be running but not having the expected effect
   - A different code path might be executing
   - Something else might be overriding your changes

---

## 🧪 Testing

### Running Tests
```bash
pytest tests/
```

### Manual Testing Checklist
- [ ] Import DOCX, translate segment, export
- [ ] Save/load .svproj project
- [ ] TM matching works
- [ ] Termbase highlighting works
- [ ] AI translation (if API keys configured)
- [ ] Spellcheck toggles correctly
- [ ] SDLPPX import/export round-trip

---

## 🔑 API Keys

**Unified Loading System with Persistent Storage:**

API keys are stored in the persistent user data location (see Installation section).

**User Data Locations (v1.9.148+):**

| Platform | Default Data Folder | API Keys File |
|----------|---------------------|---------------|
| **All Users (default)** | `~/Supervertaler/` | `~/Supervertaler/api_keys.txt` |
| **Windows (default)** | `C:\Users\Username\Supervertaler\` | `...\Supervertaler\api_keys.txt` |
| **Development** | `user_data_private\` (git-ignored) | `user_data_private\api_keys.txt` |

**Note:** Users can choose their own data folder location on first run or via Settings → General → Data Folder Location.

**For Developers (running from source):**
- Store keys in: `user_data_private/api_keys.txt`
- This location is fully gitignored and will **never** be uploaded to GitHub
- All features (translation, AI Assistant, tests) will find keys here

**For End Users (pip install):**
- On first run, choose where to store your data (default: `~/Supervertaler/`)
- Store API keys in: `[your-data-folder]/api_keys.txt`
- The app prints the exact path on startup: `[Data Paths] User data: ...`
- Keys persist across pip upgrades!

**For Windows EXE Users:**
- Same as pip users - choose your data folder on first run
- Default is visible in your home folder: `C:\Users\Username\Supervertaler\`

**Format:**
```
openai=sk-...
claude=sk-ant-...
google=AI...
gemini=AI...
custom_openai=sk-...
deepl=...
```

**Notes:**
- `google=` and `gemini=` are aliases - both work identically (v1.9.146+)
- `custom_openai=` is for any OpenAI-compatible endpoint (v1.9.236+). Configure the endpoint URL and model in Settings > AI Settings.
- `ollama_endpoint=` can override the default Ollama URL (default: `http://localhost:11434`)

**Implementation Details:**
- `Supervertaler.py`: `load_api_keys()` method reads from user_data_path
- `modules/llm_clients.py`: `load_api_keys()` also reads keys (same format)
- Automatic normalization: if `google` is set, `gemini` is also populated (and vice versa)
- All API key loading now unified through main app's method
- See `user_data_private/api_keys.example.txt` for full documented template

---

## 🔄 Recent Development History


### February 7, 2026 - Custom OpenAI-Compatible API Provider (v1.9.236)

**✨ Feature Summary**

Added a generic "Custom (OpenAI-Compatible API)" provider that enables any OpenAI SDK-compatible endpoint — Volcengine (ByteDance Doubao), Alibaba Tongyi (Qwen), DeepSeek, Mistral, Groq, and more. This addresses GitHub issue #155 requesting Chinese AI services that handle Chinese translation better.

**Status:** Implementation complete, version 1.9.236 released to PyPI

**Issue Addressed:**
- GitHub #155 - Feature request for Volcengine (Doubao) and Tongyi (Qwen) support

**Approach:**
Rather than adding each Chinese AI provider individually, implemented a single `custom_openai` provider that reuses `_call_openai()` with a user-specified `base_url`. Users configure: endpoint URL, API key (in `api_keys.txt` as `custom_openai = <key>`), and model name (free-text input since model names vary by provider).

**Files Modified:**

1. **`modules/llm_clients.py`**:
   - Added `base_url: Optional[str] = None` parameter to `__init__`
   - `translate()` routes `"custom_openai"` to `_call_openai()` (same as `"openai"`)
   - `_call_openai()` passes `base_url` to `OpenAI()` constructor when set
   - `DEFAULT_MODELS` includes `"custom_openai": "custom-model"`
   - `load_api_keys()` includes `"custom_openai": ""` in defaults
   - API key validation skipped for `custom_openai` (some endpoints don't need one)

2. **`Supervertaler.py`** — Settings UI:
   - Added `custom_radio = CustomRadioButton("🔌 Custom (OpenAI-Compatible API)")` in `_create_ai_settings_tab()`
   - Added `custom_endpoint_input` (QLineEdit for URL) and `custom_model_input` (QLineEdit for model name)
   - Added `custom_enable_cb = CheckmarkCheckBox("Enable Custom (OpenAI-Compatible)")`
   - Save handler (`_save_ai_settings_from_ui()`) saves `custom_openai_model`, `custom_openai_endpoint`
   - `load_llm_settings()` includes `custom_openai_model` and `custom_openai_endpoint` defaults
   - `load_provider_enabled_states()` includes `llm_custom_openai: True`

3. **`Supervertaler.py`** — Helper method:
   - Added `create_llm_client(provider, model, api_keys, settings)` method that handles `base_url` logic in one place
   - Used at ~12 `LLMClient()` instantiation points to avoid duplicating custom_openai logic
   - `PreTranslationWorker` accepts `base_url` parameter for batch translation

4. **`modules/quicktrans.py`**:
   - Added `("Custom", "CUS", "custom_openai", "mtql_custom_openai")` to `llm_defs`
   - `_call_llm_translation()` handles base_url for custom_openai, reads endpoint from parent_app settings

5. **`user_data_private/api_keys.example.txt`**: Added `custom_openai` section with example endpoints

**LLM Provider Architecture (5 providers total):**
- `openai` — OpenAI GPT models
- `claude` — Anthropic Claude models
- `gemini` — Google Gemini models
- `ollama` — Local Ollama models (no API key needed)
- `custom_openai` — Any OpenAI-compatible endpoint (reuses `_call_openai()` with custom `base_url`)

**Settings stored in `ui_preferences.json`:**
- `llm_settings.provider` — active provider name
- `llm_settings.custom_openai_model` — model name or endpoint ID
- `llm_settings.custom_openai_endpoint` — base URL (e.g., `https://ark.cn-beijing.volces.com/api/v3/`)
- `provider_enabled_states.llm_custom_openai` — enabled toggle

---

### February 7, 2026 - Version Display Fix for pip Users (v1.9.235)

**🐛 Bug Fix**

Fixed `_read_version()` which caused all pip-installed users to see version 1.9.227 regardless of the actual installed version. The function only tried `pyproject.toml` (which isn't included in pip wheels) and had a hardcoded fallback. Now uses a two-step approach:
1. Try `pyproject.toml` via `tomllib` (works in dev/source checkout)
2. Try `importlib.metadata.version("supervertaler")` (works after pip install)
3. Fallback to `"0.0.0"` instead of a misleading real version number

---

### February 7, 2026 - Multi-file Export & Batch Fixes (v1.9.232-234)

**✨ Features:**
- **Multi-file export "Original Format" option** (v1.9.234): Exports each file back to its source format (`.txt`, `.md`, or `.docx`), useful for mixed-format projects
- **Saved Views** (v1.9.232): Named views that filter the grid to selected files, persisted in project file
- **File boundary separators** (v1.9.232): Blue separator lines between files in multi-file projects
- **Markdown in multi-file import** (v1.9.232): `.md` files recognized alongside `.docx` and `.txt`
- **Tabbed Project Info dialog** (v1.9.232): Overview and File Progress tabs merged into one dialog

**🐛 Bug Fixes:**
- **Batch pre-translation SQLite thread error** (v1.9.233): Worker thread was calling main thread's SQLite connection for AI-inject glossary terms. Terms now pre-fetched on main thread and passed to worker.

---

### February 7, 2026 - memoQ RTF Fixes & UI Improvements (v1.9.228-231)

**🐛 Bug Fixes:**
- **memoQ RTF Unicode/formatting loss** (v1.9.231): Unicode escapes and RTF character control words were stripped by the generic cleanup regex. All Unicode escapes, hex escapes, and named character control words now decoded before the generic strip.
- **memoQ RTF combined formatting** (v1.9.231): Segments with bold+underline lost all formatting. Replaced pair-matching regex with direct marker-to-tag conversion.
- **memoQ RTF missing import options dialog** (v1.9.231): RTF import now shows the same formatting options dialog as DOCX import.
- **TM Read/Write settings persistence** (v1.9.228): Stale global TM activations could override project-specific settings on restart. Project-specific settings now always take priority.

**🎨 UI Improvements:**
- **Settings panel reorganized** (v1.9.227): AI Translation Preferences section reorganized with sub-headings
- **Version auto-read from pyproject.toml** (v1.9.227): `__version__` no longer needs manual updates

---

### February 6, 2026 - WYSIWYG Fix & Import Language Memory (v1.9.224-226)

**🐛 Bug Fixes:**
- **WYSIWYG/Tags toggle corrupted target text** (v1.9.225, #142): View mode toggle permanently destroyed whitespace/indentation. Fixed with `white-space: pre-wrap` CSS and `_suppress_target_change_handlers` guard.
- **Import dialogs ignored saved language pair** (v1.9.226, #143): Text/Markdown and multi-file import dialogs always defaulted to English → Dutch. Now all three import dialogs share language memory via `general_settings.json`.

---

### February 6, 2026 - memoQ Bilingual RTF Support (v1.9.223)

**✨ Feature Summary**

Added full import/export support for memoQ bilingual RTF files, addressing GitHub issue #145. This enables users with older memoQ versions (or those who prefer RTF format) to use the same bilingual table workflow as DOCX.

**Status:** Implementation complete, version 1.9.223 released

**Issue Addressed:**
- GitHub #145 - Feature request for memoQ RTF bilingual file support

**Implementation Details:**

The memoQ RTF bilingual format uses the identical 5-column table structure as memoQ DOCX:
- Column 1: Segment ID (number + GUID)
- Column 2: Source text (with formatting)
- Column 3: Target text
- Column 4: Comments
- Column 5: Status ("Not started", "Edited", "Confirmed", etc.)

**New Module - `modules/memoqrtf_handler.py`:**

```python
class MemoQRTFHandler:
    """Handler for memoQ bilingual RTF files."""

    def load(self, file_path: str) -> bool:
        """Load and parse memoQ bilingual RTF."""

    def save(self, output_path: str) -> bool:
        """Save RTF with updated translations."""

    def get_source_texts(self) -> List[str]:
        """Get source segments for translation."""
```

**Key Features:**
- Parses RTF table structure using `\cell` and `\row` markers
- Handles RTF formatting codes (`\b` bold, `\i` italic, `\ul` underline)
- Decodes Unicode escapes (`\uNNNN?`) and hex character codes (`'XX`)
- Preserves RTF structure for clean round-trip export
- Auto-detects source/target languages from header row

**Menu Integration:**
- Import: File → Import → memoQ Bilingual Table (RTF)...
- Export: File → Export → memoQ Bilingual Table - Translated (RTF)...

**Files Modified:**

1. **`modules/memoqrtf_handler.py`** (NEW):
   - `MemoQRTFHandler` class for parsing/saving memoQ RTF
   - `MemoQSegment` dataclass for segment representation
   - RTF escape/decode utilities
   - Language detection from header row

2. **`Supervertaler.py`**:
   - Added menu actions for memoQ RTF import/export (lines ~8165, ~8226)
   - Added `import_memoq_rtf()` method (lines ~27679-27805)
   - Added `export_memoq_rtf()` method (lines ~28407-28515)
   - Stores `memoq_rtf_source_path` in project for persistence

**Technical Notes:**
- RTF parsing uses regex to find cell content between `\cell` markers
- Unicode handling: `\uc0\uNNNN` and `\uNNNN?` patterns
- Negative Unicode values converted per RTF spec: `code + 65536`
- Target cell replacement done in reverse order to preserve positions

---

### February 5, 2026 - Custom Tooltips & Clean Slate Project Imports (v1.9.222)

**✨ Feature Summary**

Fixed black tooltip rendering issue on certain systems by implementing custom tooltip widgets. Also clarified and restored the "clean slate" behavior for new project impor

…(truncated)
