diff --git a/README.de.md b/README.de.md
index 7eab48c..29a9b5e 100644
--- a/README.de.md
+++ b/README.de.md
@@ -155,7 +155,7 @@ Blitztext registriert globale Hotkeys via `evdev`. Mit diesen Kombinationen hast
Die KI-Workflows helfen bei Formulierung, Ton und Emojis. Die passenden Einstellungen findest du unter **Einstellungen → KI-Workflows**:
-

+
@@ -195,6 +195,12 @@ Für den Workflow **Blitztext+** (Text-Verbesserer) gibt es vorgefertigte Schrei
Das **Compose-Fenster** (`✍ Compose…` im Tray-Kontextmenü) ermöglicht das Umschreiben beliebiger Texte mit der KI — ganz ohne Sprachaufnahme. Es eignet sich ideal zum Überarbeiten fertiger Entwürfe, E-Mails oder Notizen.
+
+
+

+
+
+
**Öffnen:** Klick auf das Tray-Icon → **✍ Compose…**
**Was du im Compose-Fenster tun kannst:**
@@ -251,7 +257,7 @@ Das Tray-Kontextmenü gibt dir schnellen Zugriff auf alle Workflows, das Compose
-

+
@@ -266,11 +272,12 @@ Das Hauptfenster ist dein grafisches Kontrollzentrum — nützlich, wenn Hotkeys
-

+
- **Workflow-Dropdown:** Alle 5 Aufnahmemodi zur Auswahl.
+- **Schreibstil-Vorlage:** Sichtbar wenn **Blitztext+** gewählt ist — Preset direkt im Hauptfenster wählen. Änderungen werden sofort mit dem Tray synchronisiert.
- **Start/Stopp-Button:** Klick zum Starten oder Beenden einer Aufnahme.
- **Abbruch:** Bricht die aktuelle Aufnahme ohne Transkription ab.
- **Diktat / Verlauf:** Schnellzugriff auf den Diktat-Modus und den Transkript-Verlauf.
@@ -286,8 +293,8 @@ Zusätzlich zu den Workflows bietet das Tool drei Komfort-Funktionen:
@@ -320,11 +327,11 @@ Alles wird lokal und sicher unter `~/.config/blitztext-linux/config.json` gespei
Der Einstellungs-Dialog hat drei Tabs:
-

+
Spracherkennung — Whisper-Modell, Backend, Sprache, Hotkey-Modus und Aufnahmetaste.
-

+
KI-Workflows — LLM-Anbieter, API-Key, Base-URL, Modell, Tonfall und Schreibstil-Vorlage.
-

+
Allgemein — Auto-Paste, Diktat-Ordner, Verlaufsgröße, Sprache der Oberfläche und Signatur.
diff --git a/README.md b/README.md
index e1ea984..09b2f34 100644
--- a/README.md
+++ b/README.md
@@ -157,7 +157,7 @@ Blitztext registers global hotkeys via `evdev`. With these combinations you have
The AI workflows help with phrasing, tone, and emojis. You'll find the relevant settings under **Settings → AI Workflows**:
-

+
@@ -197,6 +197,12 @@ For the **Blitztext+** workflow (text improver) there are ready-made writing-sty
The **Compose window** (`✍ Compose…` in the tray menu) lets you rewrite any text using the AI — without recording your voice. It is ideal for editing existing drafts, emails, or notes.
+
+
+

+
+
+
**How to open:** Click the tray icon → **✍ Compose…**
**What you can do in the Compose window:**
@@ -253,7 +259,7 @@ The tray context menu gives you quick access to all workflows, the compose windo
-

+
@@ -268,11 +274,12 @@ The main window is your graphical control center — useful when hotkeys are blo
-

+
- **Workflow dropdown:** Select from all 5 recording modes.
+- **Writing-style preset:** Visible when **Blitztext+** is selected — pick your preset directly in the main window. Changes sync to the tray instantly.
- **Start/Stop button:** Click to begin or end a recording.
- **Discard:** Cancels the current recording without transcription.
- **Dictation / History:** Quick access to dictation mode and the transcript history.
@@ -288,8 +295,8 @@ In addition to the workflows, the tool offers three convenience functions:
@@ -323,11 +330,11 @@ Everything is stored locally and securely under `~/.config/blitztext-linux/confi
The settings dialog has three tabs:
-

+
Speech Recognition — Whisper model, backend, language, hotkey mode, and recording key.
-

+
AI Workflows — LLM provider, API key, base URL, model, tone, and writing-style preset.
-

+
General — Auto-Paste, dictation folder, history size, interface language, and signature.
diff --git a/docs/screenshots/linux/Banner-de.png b/docs/screenshots/linux/Banner-de.png
new file mode 100644
index 0000000..8506a2e
Binary files /dev/null and b/docs/screenshots/linux/Banner-de.png differ
diff --git a/docs/screenshots/linux/Banner-en.png b/docs/screenshots/linux/Banner-en.png
new file mode 100644
index 0000000..72097fb
Binary files /dev/null and b/docs/screenshots/linux/Banner-en.png differ
diff --git a/docs/screenshots/linux/compose-de.png b/docs/screenshots/linux/compose-de.png
new file mode 100644
index 0000000..b8b08ee
Binary files /dev/null and b/docs/screenshots/linux/compose-de.png differ
diff --git a/docs/screenshots/linux/compose-en.png b/docs/screenshots/linux/compose-en.png
new file mode 100644
index 0000000..d7ccaa5
Binary files /dev/null and b/docs/screenshots/linux/compose-en.png differ
diff --git a/docs/screenshots/linux/history-de.png b/docs/screenshots/linux/history-de.png
new file mode 100644
index 0000000..673b661
Binary files /dev/null and b/docs/screenshots/linux/history-de.png differ
diff --git a/docs/screenshots/linux/history-en.png b/docs/screenshots/linux/history-en.png
new file mode 100644
index 0000000..c879f45
Binary files /dev/null and b/docs/screenshots/linux/history-en.png differ
diff --git a/docs/screenshots/linux/main-window-de.png b/docs/screenshots/linux/main-window-de.png
new file mode 100644
index 0000000..acb8504
Binary files /dev/null and b/docs/screenshots/linux/main-window-de.png differ
diff --git a/docs/screenshots/linux/main-window-en.png b/docs/screenshots/linux/main-window-en.png
new file mode 100644
index 0000000..403ae42
Binary files /dev/null and b/docs/screenshots/linux/main-window-en.png differ
diff --git a/docs/screenshots/linux/main-window-recording-de.png b/docs/screenshots/linux/main-window-recording-de.png
new file mode 100644
index 0000000..6d2e96f
Binary files /dev/null and b/docs/screenshots/linux/main-window-recording-de.png differ
diff --git a/docs/screenshots/linux/main-window-recording-en.png b/docs/screenshots/linux/main-window-recording-en.png
new file mode 100644
index 0000000..7d58a58
Binary files /dev/null and b/docs/screenshots/linux/main-window-recording-en.png differ
diff --git a/docs/screenshots/linux/settings-ai-workflows-compose-en.png b/docs/screenshots/linux/settings-ai-workflows-compose-en.png
new file mode 100644
index 0000000..7e9d9bd
Binary files /dev/null and b/docs/screenshots/linux/settings-ai-workflows-compose-en.png differ
diff --git a/docs/screenshots/linux/settings-ai-workflows-de.png b/docs/screenshots/linux/settings-ai-workflows-de.png
new file mode 100644
index 0000000..e8c0d1e
Binary files /dev/null and b/docs/screenshots/linux/settings-ai-workflows-de.png differ
diff --git a/docs/screenshots/linux/settings-ai-workflows-en.png b/docs/screenshots/linux/settings-ai-workflows-en.png
new file mode 100644
index 0000000..cca4a19
Binary files /dev/null and b/docs/screenshots/linux/settings-ai-workflows-en.png differ
diff --git a/docs/screenshots/linux/settings-general-de.png b/docs/screenshots/linux/settings-general-de.png
new file mode 100644
index 0000000..ae362b2
Binary files /dev/null and b/docs/screenshots/linux/settings-general-de.png differ
diff --git a/docs/screenshots/linux/settings-general-en.png b/docs/screenshots/linux/settings-general-en.png
new file mode 100644
index 0000000..bf04760
Binary files /dev/null and b/docs/screenshots/linux/settings-general-en.png differ
diff --git a/docs/screenshots/linux/settings-speech-de.png b/docs/screenshots/linux/settings-speech-de.png
new file mode 100644
index 0000000..4d2b468
Binary files /dev/null and b/docs/screenshots/linux/settings-speech-de.png differ
diff --git a/docs/screenshots/linux/settings-speech-en.png b/docs/screenshots/linux/settings-speech-en.png
new file mode 100644
index 0000000..feef9a2
Binary files /dev/null and b/docs/screenshots/linux/settings-speech-en.png differ
diff --git a/docs/screenshots/linux/tray-menu-de.png b/docs/screenshots/linux/tray-menu-de.png
new file mode 100644
index 0000000..e98ed19
Binary files /dev/null and b/docs/screenshots/linux/tray-menu-de.png differ
diff --git a/docs/screenshots/linux/tray-menu-en.png b/docs/screenshots/linux/tray-menu-en.png
new file mode 100644
index 0000000..0a26a7f
Binary files /dev/null and b/docs/screenshots/linux/tray-menu-en.png differ
diff --git a/docs/screenshots/linux/tts-de.png b/docs/screenshots/linux/tts-de.png
new file mode 100644
index 0000000..2c04cfe
Binary files /dev/null and b/docs/screenshots/linux/tts-de.png differ
diff --git a/docs/screenshots/linux/tts-en.png b/docs/screenshots/linux/tts-en.png
new file mode 100644
index 0000000..dbd15fb
Binary files /dev/null and b/docs/screenshots/linux/tts-en.png differ
diff --git a/scripts/_make_screenshots.py b/scripts/_make_screenshots.py
index 0f89caf..b83c395 100644
--- a/scripts/_make_screenshots.py
+++ b/scripts/_make_screenshots.py
@@ -22,12 +22,15 @@
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QApplication
+from app.blitztext_linux import BlitztextApp, Config, SettingsDialog
+from app.compose_window import ComposeWindow
from app.config import BlitztextConfig
from app.history_panel import HistoryPanel
from app.i18n import set_language, t
+from app.llm_service import LLMService, WorkflowType
from app.main_window import MainWindow
+from app.paste_service import PasteService
from app.tts_window import TtsWindow
-from app.blitztext_linux import BlitztextApp, Config, SettingsDialog
SCREENSHOT_DIR = Path("docs/screenshots/linux")
CANVAS_SIZE = (1280, 640)
@@ -47,20 +50,22 @@
"hero": "Your local AI voice assistant for KDE Plasma & Wayland",
"sub": "Record speech, transcribe locally or online, optionally rewrite it with AI, and paste it directly into the active app.",
"flow": "Record • Transcribe • Rewrite • Paste",
- "feature_title": "What is new in v0.4.0",
+ "feature_title": "What is new in v0.8.0",
"tag_new": "NEW",
"chips": [
- ("Multilingual UI", "Switch the whole app between English and German."),
- ("Writing-style presets", "Ready-made Blitztext+ presets for common writing tasks."),
- ("Offline-ready", "Local Whisper and privacy-friendly workflows stay available."),
- ("Global hotkeys", "Capture dictation from anywhere in KDE Plasma."),
+ ("Compose window", "Draft, refine and compare AI-rewritten text before pasting it anywhere."),
+ ("Preset in main window", "Pick your writing style in the main window — changes sync to the tray instantly."),
+ ("Tone & template control", "Choose tone and writing template directly inside the Compose window."),
+ ("Prompt transparency", "Inspect and edit the AI system prompt before running a rewrite."),
],
"labels": {
"main": "Main window",
- "general": "Settings → General",
+ "compose": "Compose window",
"workflows": "Settings → AI Workflows",
"tray": "Tray presets",
},
+ "compose_input": "Please help me write a concise follow-up email for our product meeting earlier today.",
+ "compose_output": "Hi team,\n\nThank you for the productive discussion today. Here are the key action items we agreed on:\n\n• Finalise the API contract by Friday\n• Schedule a follow-up review for next Tuesday\n• Share the updated roadmap with stakeholders\n\nLet me know if I missed anything.\n\nBest,",
"history_entries": [
("Please move tomorrow's team sync to 10:00.", False),
("Could you send me the updated rollout plan afterwards?", True),
@@ -74,20 +79,22 @@
"hero": "Dein lokaler KI-Sprachassistent für KDE Plasma & Wayland",
"sub": "Sprache aufnehmen, lokal oder online transkribieren, optional mit KI umformulieren und direkt in die aktive Anwendung einfügen.",
"flow": "Aufnehmen • Transkribieren • Umformulieren • Direkt einfügen",
- "feature_title": "Neu im Stand v0.4.0",
+ "feature_title": "Neu in v0.8.0",
"tag_new": "NEU",
"chips": [
- ("Mehrsprachige Oberfläche", "Die komplette App lässt sich zwischen Deutsch und Englisch umschalten."),
- ("Schreibstil-Vorlagen", "Vorgefertigte Blitztext+-Presets für typische Schreibaufgaben."),
- ("Offline-fähig", "Lokale Whisper-Workflows bleiben für datensparsame Nutzung verfügbar."),
- ("Globale Hotkeys", "Diktat direkt aus jeder Anwendung unter KDE Plasma starten."),
+ ("Entwurfsfenster", "Text entwerfen, verfeinern und KI-Varianten vergleichen, bevor du einfügst."),
+ ("Preset im Hauptfenster", "Schreibstil direkt im Hauptfenster wählen – Änderungen bleiben mit dem Tray synchron."),
+ ("Tonfall & Vorlage", "Tonfall und Schreibvorlage direkt im Entwurfsfenster steuern."),
+ ("Prompt-Transparenz", "KI-Systemprompt vor der Ausführung einsehen und anpassen."),
],
"labels": {
"main": "Hauptfenster",
- "general": "Einstellungen → Allgemein",
+ "compose": "Entwurfsfenster",
"workflows": "Einstellungen → KI-Workflows",
"tray": "Tray-Presets",
},
+ "compose_input": "Bitte hilf mir, eine knappe Nachfass-E-Mail zu unserem heutigen Produktmeeting zu schreiben.",
+ "compose_output": "Hallo zusammen,\n\nvielen Dank für die produktive Diskussion heute. Hier die vereinbarten Aufgaben:\n\n• API-Vertrag bis Freitag finalisieren\n• Review-Termin für nächsten Dienstag eintragen\n• Aktualisierte Roadmap an Stakeholder verteilen\n\nBitte meldet euch, falls ich etwas vergessen habe.\n\nViele Grüße,",
"history_entries": [
("Bitte verschiebe das Team-Meeting morgen auf 10 Uhr.", False),
("Kannst du mir danach den aktualisierten Rollout-Plan schicken?", True),
@@ -277,14 +284,14 @@ def _make_banner(lang: str, out_dir: Path) -> None:
screenshots = {
"main": out_dir / f"main-window-{lang}.png",
- "general": out_dir / f"settings-general-{lang}.png",
+ "compose": out_dir / f"compose-{lang}.png",
"workflows": out_dir / f"settings-ai-workflows-{lang}.png",
"tray": out_dir / f"tray-menu-{lang}.png",
}
placements = [
(screenshots["main"], (824, 74), (220, 260), copy["labels"]["main"]),
- (screenshots["general"], (1046, 74), (194, 260), copy["labels"]["general"]),
+ (screenshots["compose"], (1046, 74), (194, 260), copy["labels"]["compose"]),
(screenshots["workflows"], (790, 350), (250, 236), copy["labels"]["workflows"]),
(screenshots["tray"], (1054, 332), (186, 254), copy["labels"]["tray"]),
]
@@ -307,6 +314,36 @@ def _make_banner(lang: str, out_dir: Path) -> None:
print(f" ✓ {social_path.name}")
+class _FakeLLMService(LLMService):
+ """Minimal LLMService stub for offscreen rendering — never calls any API."""
+
+ def __init__(self) -> None:
+ self.api_key = "SCREENSHOT_DUMMY_TOKEN"
+ self.writing_preset = "standard"
+
+ def is_configured(self) -> bool:
+ return True
+
+ def rewrite_text(self, *args, **kwargs) -> str: # type: ignore[override]
+ return ""
+
+ def build_system_prompt(self, *args, **kwargs) -> str: # type: ignore[override]
+ return ""
+
+ def rewrite_raw(self, *args, **kwargs) -> str: # type: ignore[override]
+ return ""
+
+
+class _FakePasteService(PasteService):
+ """Minimal PasteService stub — suppresses all clipboard / xdotool calls."""
+
+ def __init__(self) -> None:
+ pass
+
+ def paste(self, text: str, force_autopaste=None) -> None:
+ pass
+
+
def _tab_index(tabs, key: str) -> int:
"""Resolve a settings tab by its i18n key, independent of tab order or language."""
target = t(key)
@@ -336,9 +373,16 @@ def _render_language_set(out_dir: Path, lang: str) -> None:
show_history_panel=lambda *a, **k: None,
show_settings_dialog=lambda *a, **k: None,
show_tts_window=lambda *a, **k: None,
+ main_window_preset_changed=lambda *a, **k: None,
)
main_window = MainWindow(controller)
+ # Switch to TEXT_IMPROVER so the writing-style preset combo is visible
+ for i in range(main_window._workflow_combo.count()):
+ if main_window._workflow_combo.itemData(i) == WorkflowType.TEXT_IMPROVER:
+ main_window._workflow_combo.setCurrentIndex(i)
+ _process_events()
+ break
_grab(main_window, out_dir / f"main-window-{lang}.png")
main_window.update_state("RECORDING", None, None)
_grab(main_window, out_dir / f"main-window-recording-{lang}.png")
@@ -347,6 +391,8 @@ def _render_language_set(out_dir: Path, lang: str) -> None:
settings = SettingsDialog(config)
settings.tabs.setCurrentIndex(_tab_index(settings.tabs, "settings.tab.general"))
_grab(settings, out_dir / f"settings-general-{lang}.png")
+ settings.tabs.setCurrentIndex(_tab_index(settings.tabs, "settings.tab.speech"))
+ _grab(settings, out_dir / f"settings-speech-{lang}.png")
settings.tabs.setCurrentIndex(_tab_index(settings.tabs, "settings.tab.workflows"))
_grab(settings, out_dir / f"settings-ai-workflows-{lang}.png")
settings.close()
@@ -366,6 +412,12 @@ def _render_language_set(out_dir: Path, lang: str) -> None:
_grab(tts, out_dir / f"tts-{lang}.png")
tts.close()
+ compose = ComposeWindow(_FakeLLMService(), _FakePasteService(), config)
+ compose.set_input_text(copy["compose_input"])
+ compose.txtOutput.setPlainText(copy["compose_output"])
+ _grab(compose, out_dir / f"compose-{lang}.png")
+ compose.close()
+
_capture_tray_menu(config, lang, out_dir / f"tray-menu-{lang}.png")
_make_banner(lang, out_dir)