From 0698f7f59c5061d68a480a7c0178003d0b65b6cf Mon Sep 17 00:00:00 2001 From: aichristabasco Date: Sat, 21 Feb 2026 14:44:10 +0000 Subject: [PATCH 1/2] fix: use python.exe -m pip to avoid red ERROR during pip upgrade in venv --- install.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install.bat b/install.bat index 8811194..aece315 100644 --- a/install.bat +++ b/install.bat @@ -71,8 +71,8 @@ echo. call venv\Scripts\activate.bat -pip install --upgrade pip --quiet --disable-pip-version-check -pip install -r requirements.txt --disable-pip-version-check +venv\Scripts\python.exe -m pip install --upgrade pip --quiet --disable-pip-version-check +venv\Scripts\python.exe -m pip install -r requirements.txt --disable-pip-version-check if %errorlevel% neq 0 ( echo. From b47eda1e201ed6954beca55e559afba0aeed6b8c Mon Sep 17 00:00:00 2001 From: aichristabasco Date: Sat, 21 Feb 2026 14:55:20 +0000 Subject: [PATCH 2/2] fix: 4 fixes - hotkey bug, overlay visible, editable transcript, 3 color themes 1. HOTKEY BUG: After stop(), _active=False so update_hotkey() never restarted the listener. Now directly set hotkey/is_combo and call start() explicitly - new hotkey works immediately after change. 2. OVERLAY TRANSPARENT: Removed transparentcolor trick (unreliable). Replaced _rrect arc-based method with smooth polygon approach. Solid dark bg (#0d0b1e) + layered glow border via outline polygons. Fade in/out still works via window alpha. 3. EDITABLE TRANSCRIPT: Textbox is no longer disabled. User can edit transcription text before copying. _copy_text no longer needs to toggle state. 4. COLOR THEMES in settings: 3 buttons - Fioletowy/Niebieski/Zielony Change accent color and window background on click. --- voxflow/app.py | 72 ++++++++++++++++++++++-------- voxflow/overlay.py | 109 +++++++++++++++++++++++++-------------------- 2 files changed, 115 insertions(+), 66 deletions(-) diff --git a/voxflow/app.py b/voxflow/app.py index 5c5c6d5..d7b643e 100644 --- a/voxflow/app.py +++ b/voxflow/app.py @@ -450,15 +450,14 @@ def _build_transcript(self): ).pack(side="right") self.textbox = ctk.CTkTextbox( - card, - font=ctk.CTkFont(family="Segoe UI", size=14), - fg_color="transparent", - text_color=C["txt"], - wrap="word", height=90, border_width=0, - ) - self.textbox.pack(fill="both", expand=True, padx=14, pady=(6, 10)) - self.textbox.insert("1.0", "Twój tekst pojawi się tutaj...") - self.textbox.configure(state="disabled") + card, + font=ctk.CTkFont(family="Segoe UI", size=14), + fg_color="transparent", + text_color=C["txt"], + wrap="word", height=90, border_width=0, + ) + self.textbox.pack(fill="both", expand=True, padx=14, pady=(6, 10)) + self.textbox.insert("1.0", "Twój tekst pojawi się tutaj — możesz go edytować przed skopiowaniem...") # ── Quick Controls (language / model / hotkey) ───────────────── @@ -638,7 +637,42 @@ def opt_row(parent, label, values, var, cmd, width=130): self.autostart_var = ctk.BooleanVar(value=is_autostart_enabled()) sw_row(inner, "🚀 Uruchamiaj z Windows", self.autostart_var, self._on_autostart_toggle) - ctk.CTkFrame(inner, fg_color=C["border"], height=1).pack(fill="x", pady=(8, 6)) + # ── Color theme selector ─────────────────────── + ctk.CTkFrame(inner, fg_color=C["border"], height=1).pack(fill="x", pady=(10, 8)) + + ctk.CTkLabel( + inner, text="🎨 Motyw kolorów", + font=ctk.CTkFont(size=12, weight="bold"), + text_color=C["txt2"], + ).pack(anchor="w", pady=(0, 6)) + + theme_row = ctk.CTkFrame(inner, fg_color="transparent") + theme_row.pack(fill="x") + + THEMES = [ + ("Fioletowy", "#7c3aed", "#09091a"), + ("Niebieski", "#1d4ed8", "#080e1a"), + ("Zielony", "#059669", "#08130f"), + ] + + def _apply_theme(accent, bg): + C["accent"] = accent + C["accent2"] = accent + C["accent_dim"] = accent + C["bg"] = bg + self.configure(fg_color=bg) + self.config.save() + + for label, accent, bg in THEMES: + ctk.CTkButton( + theme_row, text=label, width=100, height=30, + font=ctk.CTkFont(size=11), + fg_color=accent, hover_color=accent, + corner_radius=8, + command=lambda a=accent, b=bg: _apply_theme(a, b), + ).pack(side="left", padx=(0, 6)) + + ctk.CTkFrame(inner, fg_color=C["border"], height=1).pack(fill="x", pady=(10, 6)) ctk.CTkLabel( inner, @@ -649,6 +683,7 @@ def opt_row(parent, label, values, var, cmd, width=130): justify="left", ).pack(anchor="w") + # ── Footer ──────────────────────────────────────────────────── def _build_footer(self): @@ -736,7 +771,11 @@ def _finish_hotkey_capture(self, key: Optional[str]): if key: self.config.hotkey = key self.config.save() - self.hotkey_manager.update_hotkey(key) + # BUGFIX: update_hotkey checks was_active which is False after stop(), + # so we update the key manually and always call start(). + self.hotkey_manager.hotkey = key.lower() + self.hotkey_manager._is_combo = "+" in key + self.hotkey_manager.start() display = key.upper().replace("+", " + ") self.hotkey_btn.configure( text=display, @@ -753,7 +792,7 @@ def _finish_hotkey_capture(self, key: Optional[str]): # Reset border after 2s self.after(2000, lambda: self.hotkey_btn.configure(border_color=C["border2"])) else: - # Cancelled / timeout + # Cancelled / timeout — restart with old hotkey self.hotkey_btn.configure( text=self.config.hotkey.upper().replace("+", " + "), fg_color=C["bg_input"], @@ -900,11 +939,9 @@ def _on_done(self, result: dict): self.status.configure(text="🤔 Nie rozpoznano mowy", text_color=C["warn"]) return - # Update transcript box - self.textbox.configure(state="normal") + # Update transcript box (always editable — user can fix before copying) self.textbox.delete("1.0", "end") self.textbox.insert("1.0", text) - self.textbox.configure(state="disabled") # Auto-copy if self.config.auto_copy_to_clipboard: @@ -1077,10 +1114,9 @@ def _reload_model(self, sz): ) def _copy_text(self): - self.textbox.configure(state="normal") t = self.textbox.get("1.0", "end").strip() - self.textbox.configure(state="disabled") - if t and t != "Twój tekst pojawi się tutaj...": + placeholder = "Twój tekst pojawi się tutaj" + if t and not t.startswith(placeholder): pyperclip.copy(t) self.status.configure(text="📋 Skopiowano!", text_color=C["ok"]) diff --git a/voxflow/overlay.py b/voxflow/overlay.py index 26d0d10..60d8f0b 100644 --- a/voxflow/overlay.py +++ b/voxflow/overlay.py @@ -56,19 +56,20 @@ def _create_window(self): win = self._root win.overrideredirect(True) win.attributes("-topmost", True) - win.attributes("-alpha", 0.0) # start invisible, fade in - win.attributes("-transparentcolor", "#010101") # key-colour transparency + win.attributes("-alpha", 0.0) # start invisible, fade in sw = win.winfo_screenwidth() sh = win.winfo_screenheight() x = (sw - self.W) // 2 y = sh - self.H - self.MARGIN_BOTTOM win.geometry(f"{self.W}x{self.H}+{x}+{y}") - win.configure(bg="#010101") + + # Match canvas background to panel color — no transparent key needed + win.configure(bg="#0d0b1e") self._canvas = tk.Canvas( win, width=self.W, height=self.H, - bg="#010101", highlightthickness=0, + bg="#0d0b1e", highlightthickness=0, ) self._canvas.pack() self._animate() @@ -93,7 +94,7 @@ def _animate(self): # --- Fade in / fade out --- if self._fading_out: - self._alpha = max(0.0, self._alpha - 0.07) + self._alpha = max(0.0, self._alpha - 0.08) try: self._root.attributes("-alpha", self._alpha) except Exception: @@ -102,8 +103,8 @@ def _animate(self): if self._parent: self._parent.after(0, self._destroy_window) return - elif self._alpha < 0.95: - self._alpha = min(0.95, self._alpha + 0.06) + elif self._alpha < 0.93: + self._alpha = min(0.93, self._alpha + 0.07) try: self._root.attributes("-alpha", self._alpha) except Exception: @@ -122,29 +123,32 @@ def _draw(self): W, H = self.W, self.H c.delete("all") - # ── Outer glow (shadow layer) ───────────────────────────── - # Subtle purple glow border drawn as stacked rounded rects - for i in range(4, 0, -1): - alpha_colors = ["#1a0a2e", "#1e0d38", "#220f42", "#26114a"] - self._rrect(c, i, i, W - i, H - i, r=18, fill=alpha_colors[i-1], outline="") + # ── Border glow (layered rectangles, outer→inner) ──────── + glow_colors = ["#3b1f6a", "#4c2181", "#5d28a0", "#6d28d9"] + radii = [16, 14, 13, 12] + for col, r in zip(glow_colors, radii): + pad = radii[0] - r + self._rrect(c, pad, pad, W - pad, H - pad, r=r, fill="", outline=col, width=2) + + # ── Glass panel fill ────────────────────────────────────── + self._rrect(c, 3, 3, W - 3, H - 3, r=12, + fill="#100e28", outline="") - # ── Glass panel ─────────────────────────────────────────── - self._rrect(c, 3, 3, W - 3, H - 3, r=16, - fill="#0d0b1e", outline="#6d28d9", width=2) + # Inner top-shine strip + self._rrect(c, 4, 4, W - 4, 4 + H // 3, r=11, + fill="#ffffff0a", outline="") - # Inner highlight line (top edge shine) - self._rrect(c, 4, 4, W - 4, 18, r=14, - fill="#ffffff08", outline="") + # Thin bright border top-cap + self._rrect(c, 3, 3, W - 3, H - 3, r=12, + fill="", outline="#7c3aed", width=1) # ── Pulsing record dot (left) ───────────────────────────── pulse = (math.sin(self._phase * 3.0) + 1) / 2 cx, cy = 38, H // 2 # Outer glow rings - for ri, col, fade in [(22, "#ef444420", 1.0), - (17, "#ef444440", 1.0), - (13, "#ef444480", 1.0)]: - r = ri + pulse * 3 + for ri, col in [(22, "#ef444418"), (16, "#ef444438"), (12, "#ef444470")]: + r = ri + pulse * 4 c.create_oval(cx - r, cy - r, cx + r, cy + r, fill=col, outline="") @@ -153,38 +157,41 @@ def _draw(self): c.create_oval(cx - dr, cy - dr, cx + dr, cy + dr, fill="#ef4444", outline="#fca5a5", width=1) - # Tiny white mic icon inside dot + # Mic icon inside dot c.create_oval(cx - 3, cy - 6, cx + 3, cy, fill="white", outline="") c.create_line(cx, cy, cx, cy + 5, fill="white", width=2) c.create_line(cx - 4, cy + 5, cx + 4, cy + 5, fill="white", width=2) - # ── Text labels ─────────────────────────────────────────── + # ── Text ────────────────────────────────────────────────── c.create_text(62, cy - 9, text="Nagrywam...", fill="#fca5a5", font=("Segoe UI", 11, "bold"), anchor="w") - c.create_text(62, cy + 8, + c.create_text(62, cy + 9, text="VoxFlow \u2022 zwolnij klawisz aby zakonczyc", - fill="#7c6fa0", font=("Segoe UI", 8), + fill="#6d5fa0", font=("Segoe UI", 8), anchor="w") - # ── Waveform bars (right section) ───────────────────────── - n_bars = 30 + # Thin separator line + c.create_line(230, 12, 230, H - 12, fill="#3d2d70", width=1) + + # ── Waveform bars ───────────────────────────────────────── + n_bars = 32 bw = 4 gap = 2 - x0 = 230 + x0 = 238 max_h = 20 for i in range(n_bars): + if x0 + i * (bw + gap) > W - 8: + break t = self._phase * 2.6 + i * 0.38 wave = abs(math.sin(t)) * 0.6 + abs(math.sin(t * 0.7 + 1.1)) * 0.4 - amp = (wave * 0.5 + self._level * 0.65) * max_h + 3 + amp = (wave * 0.45 + self._level * 0.7) * max_h + 3 amp = min(amp, max_h) x = x0 + i * (bw + gap) - bar_cy = cy - # Gradient colour: low=purple, mid=violet, high=rose ratio = amp / max_h if ratio > 0.78: col = "#f43f5e" @@ -196,26 +203,32 @@ def _draw(self): col = "#4c1d95" # Bar body - c.create_rectangle(x, bar_cy - amp, x + bw, bar_cy + amp, + c.create_rectangle(x, cy - amp, x + bw, cy + amp, fill=col, outline="") - # Rounded cap - c.create_oval(x - 1, bar_cy - amp - 2, x + bw + 1, bar_cy - amp + 2, + # Rounded top cap + c.create_oval(x - 1, cy - amp - 2, x + bw + 1, cy - amp + 2, fill=col, outline="") - # ── Right accent line ───────────────────────────────────── - rx = x0 + n_bars * (bw + gap) + 6 - c.create_line(rx, 12, rx, H - 12, - fill="#6d28d950", width=1) - # ── Helper ──────────────────────────────────────────────────── @staticmethod def _rrect(canvas, x1, y1, x2, y2, r=12, **kw): - """Draw a rounded rectangle.""" - canvas.create_arc(x1, y1, x1+2*r, y1+2*r, start=90, extent=90, style="pieslice", **kw) - canvas.create_arc(x2 - 2*r, y1, x2, y1+2*r, start=0, extent=90, style="pieslice", **kw) - canvas.create_arc(x1, y2 - 2*r, x1+2*r, y2, start=180, extent=90, style="pieslice", **kw) - canvas.create_arc(x2 - 2*r, y2 - 2*r, x2, y2, start=270, extent=90, style="pieslice", **kw) - canvas.create_rectangle(x1 + r, y1, x2 - r, y2, **kw) - canvas.create_rectangle(x1, y1 + r, x1 + r, y2 - r, **kw) - canvas.create_rectangle(x2 - r, y1 + r, x2, y2 - r, **kw) + """Draw a rounded rectangle on canvas.""" + # Clamp radius + r = min(r, (x2 - x1) // 2, (y2 - y1) // 2) + points = [ + x1 + r, y1, + x2 - r, y1, + x2, y1, + x2, y1 + r, + x2, y2 - r, + x2, y2, + x2 - r, y2, + x1 + r, y2, + x1, y2, + x1, y2 - r, + x1, y1 + r, + x1, y1, + x1 + r, y1, + ] + canvas.create_polygon(points, smooth=True, **kw)