# whisper_app/log_window.py import os import queue import tkinter as tk from whisper_app import app BG = "#18181f" BG2 = "#22222c" BG3 = "#2c2c38" BORDER = "#38384a" FG = "#e8e8f0" FG2 = "#7878a0" AMBER = "#f5a623" GREEN = "#4ade80" RED = "#f87171" YELLOW = "#facc15" _sans = "Segoe UI" if os.name == "nt" else "sans-serif" _mono = "Consolas" if os.name == "nt" else "monospace" _window: tk.Toplevel | None = None _text: tk.Text | None = None _log_q: queue.Queue | None = None _MAX_LINES = 200 def create(root: tk.Tk, log_queue: queue.Queue, on_settings, on_vocab) -> tk.Toplevel: global _window, _text, _log_q _log_q = log_queue win = tk.Toplevel(root) win.title("Whisper Dictation") win.configure(bg=BG) win.resizable(True, True) win.minsize(400, 300) win.protocol("WM_DELETE_WINDOW", win.withdraw) # ── Header ── hdr = tk.Frame(win, bg=BG2) hdr.pack(fill="x") tk.Frame(hdr, bg=AMBER, height=2).pack(fill="x") hdr_inner = tk.Frame(hdr, bg=BG2, padx=12, pady=6) hdr_inner.pack(fill="x") tk.Label(hdr_inner, text="WHISPER DICTATION", font=(_sans, 11, "bold"), bg=BG2, fg=FG).pack(side="left") tk.Button(hdr_inner, text="✕", command=win.withdraw, bg=BG2, fg=FG2, relief="flat", bd=0, font=(_sans, 11), cursor="hand2").pack(side="right") # ── Log text ── txt = tk.Text(win, bg=BG, fg=FG, font=(_mono, 10), relief="flat", bd=0, padx=10, pady=6, state="disabled", wrap="none", width=48, height=10, highlightthickness=0) txt.pack(fill="both", expand=True) txt.tag_config("green", foreground=GREEN) txt.tag_config("red", foreground=RED) txt.tag_config("yellow", foreground=YELLOW) txt.tag_config("grey", foreground=FG2) _text = txt # ── Button bar ── bar = tk.Frame(win, bg=BG2, pady=6, padx=10) bar.pack(fill="x") for label, cmd in [("⚙ Einstellungen", on_settings), ("📚 Vokabular", on_vocab)]: b = tk.Button(bar, text=label, command=cmd, bg=BG3, fg=FG, relief="flat", bd=0, font=(_sans, 10), padx=10, pady=4, cursor="hand2") b.pack(side="left", padx=(0, 4)) tk.Button(bar, text="🗑", command=_clear_log, bg=BG3, fg=FG2, relief="flat", bd=0, font=(_sans, 10), padx=8, pady=4, cursor="hand2").pack(side="right") _window = win win.withdraw() root.after(100, _poll) return win def show() -> None: if _window: _window.deiconify() _window.lift() def _clear_log() -> None: if _text: _text.config(state="normal") _text.delete("1.0", "end") _text.config(state="disabled") def _tag_for(msg: str) -> str: low = msg.lower() if any(x in low for x in ("recording", "aufnahme")): return "red" if any(x in low for x in ("transcrib", "loading", "laden")): return "yellow" if any(x in low for x in ("result:", "ready", "bereit")): return "green" return "grey" def _poll() -> None: if _log_q and _text: try: while True: msg = _log_q.get_nowait() _append(msg) except queue.Empty: pass if app.overlay_tk: app.overlay_tk.after(100, _poll) def _append(msg: str) -> None: _text.config(state="normal") # Trim to MAX_LINES lines = int(_text.index("end-1c").split(".")[0]) if lines >= _MAX_LINES: _text.delete("1.0", "2.0") _text.insert("end", msg + "\n", _tag_for(msg)) _text.see("end") _text.config(state="disabled")