pyright/ruff error fixes. Autoinstall check of autocomplete

This commit is contained in:
2026-05-25 19:03:00 +02:00
parent 17fcf1a02c
commit 32acfa6b9f
4 changed files with 61 additions and 41 deletions
+34
View File
@@ -1,5 +1,10 @@
from __future__ import annotations
import sys
from pathlib import Path
import click
import typer
from typer._completion_shared import install, _get_shell_name
from zshell.subcommands import shell, scheme, screenshot, wallpaper, record
app = typer.Typer(name="zshell-cli")
@@ -11,5 +16,34 @@ app.add_typer(wallpaper.app, name="wallpaper")
app.add_typer(record.app, name="record")
def _completion_installed() -> bool:
shell = _get_shell_name()
match shell:
case "zsh":
return (Path.home() / ".zfunc" / "_zshell-cli").exists()
case "bash":
return (Path.home() / ".bash_completions" / "zshell-cli.sh").exists()
case "fish":
return (Path.home() / ".config" / "fish" / "completions" / "zshell-cli.fish").exists()
return False
def _auto_install_completion() -> None:
if not sys.stdout.isatty():
return
if _completion_installed():
return
shell = _get_shell_name()
if shell is None:
return
try:
_, path = install(prog_name="zshell-cli")
click.secho(f"zshell-cli: Shell completion installed ({shell}: {path})", fg="green")
click.echo("zshell-cli: Restart your shell or source the file to enable tab-completion.")
except Exception:
pass
def main() -> None:
_auto_install_completion()
app()
+20 -24
View File
@@ -18,8 +18,7 @@ TEMP_RECORDING = STATE_DIR / "recording.mp4"
REPLAY_RECORDING = STATE_DIR / "replay.mp4"
NOTIF_ID_FILE = STATE_DIR / "notifid.txt"
RECORDINGS_DIR = os.getenv("ZSHELL_RECORDINGS_DIR",
str(Path(HOME) / "Videos/Recordings"))
RECORDINGS_DIR = os.getenv("ZSHELL_RECORDINGS_DIR", str(Path(HOME) / "Videos/Recordings"))
def _read_extra_args() -> list[str]:
@@ -36,7 +35,7 @@ def _is_recording() -> bool:
return subprocess.run(["pidof", RECORDER], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0
def _notify(summary: str, body: str = "", actions: list = None, timeout: int = 5000) -> Optional[int]:
def _notify(summary: str, body: str = "", actions: list | None = None, timeout: int = 5000) -> Optional[int]:
args = ["notify-send", summary, body, "-t", str(timeout), "-p"]
if actions:
for action in actions:
@@ -49,14 +48,12 @@ def _notify(summary: str, body: str = "", actions: list = None, timeout: int = 5
def _close_notification(notif_id: int):
subprocess.run(["notify-send", "--close", str(notif_id)],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
subprocess.run(["notify-send", "--close", str(notif_id)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
def _get_monitors() -> list[dict]:
try:
res = subprocess.run(["hyprctl", "monitors", "-j"],
capture_output=True, text=True)
res = subprocess.run(["hyprctl", "monitors", "-j"], capture_output=True, text=True)
return json.loads(res.stdout)
except Exception:
return []
@@ -92,6 +89,7 @@ def _slurp_region() -> Optional[str]:
def _parse_geometry(geometry: str) -> Optional[tuple[int, int, int, int]]:
import re
match = re.match(r"(\d+)x(\d+)\+(\d+)\+(\d+)", geometry)
if match:
return int(match.group(3)), int(match.group(4)), int(match.group(1)), int(match.group(2))
@@ -139,8 +137,7 @@ def start_recording(region: Optional[str], sound: bool):
cmd.extend(extra_args)
cmd.extend(["-o", str(TEMP_RECORDING)])
subprocess.Popen(cmd, start_new_session=True,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
subprocess.Popen(cmd, start_new_session=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
notif_id = _notify("Recording started", f"Saving to {TEMP_RECORDING}")
if notif_id is not None:
@@ -148,14 +145,12 @@ def start_recording(region: Optional[str], sound: bool):
time.sleep(1)
if not _is_recording():
_notify("Recording failed",
"Check gpu-screen-recorder output.", timeout=5000)
_notify("Recording failed", "Check gpu-screen-recorder output.", timeout=5000)
raise typer.Exit(code=1)
def stop_recording(clipboard: bool):
subprocess.run(["pkill", "-f", RECORDER],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
subprocess.run(["pkill", "-f", RECORDER], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
for _ in range(50):
if not _is_recording():
@@ -178,30 +173,31 @@ def stop_recording(clipboard: bool):
NOTIF_ID_FILE.unlink()
if clipboard:
subprocess.run(["wl-copy", "--type", "text/uri-list", f"file://{final_path}"],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
subprocess.run(
["wl-copy", "--type", "text/uri-list", f"file://{final_path}"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
_notify("Recording stopped", f"Saved to {final_path}", timeout=5000)
def toggle_pause():
subprocess.run(["pkill", "-USR2", "-f", RECORDER],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
subprocess.run(["pkill", "-USR2", "-f", RECORDER], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
typer.echo("Toggled pause.")
@app.command()
def record(
region: Optional[str] = typer.Option(
None, "--region", "-r",
None,
"--region",
"-r",
help="Record a region. Use 'slurp' (or omit value) to select interactively, or give 'WxH+X+Y'.",
),
sound: bool = typer.Option(
False, "--sound", "-s", help="Record audio from default output."),
pause: bool = typer.Option(
False, "--pause", "-p", help="Toggle pause/resume."),
clipboard: bool = typer.Option(
False, "--clipboard", "-c", help="Copy the final recording path to clipboard."),
sound: bool = typer.Option(False, "--sound", "-s", help="Record audio from default output."),
pause: bool = typer.Option(False, "--pause", "-p", help="Toggle pause/resume."),
clipboard: bool = typer.Option(False, "--clipboard", "-c", help="Copy the final recording path to clipboard."),
):
"""Start or stop a screen recording with gpu-screen-recorder."""
if pause:
+5 -15
View File
@@ -105,9 +105,7 @@ def list_presets(
@app.command()
def generate(
image_path: Optional[Path] = typer.Option(
None, help="Path to source image. Required for image mode."
),
image_path: Optional[Path] = typer.Option(None, help="Path to source image. Required for image mode."),
scheme: Optional[str] = typer.Option(
None,
help="Color scheme algorithm to use for image mode. Ignored in preset mode.",
@@ -266,15 +264,11 @@ def generate(
def harmonize(from_hct: Hct, to_hct: Hct, tone_boost: float) -> Hct:
diff = difference_degrees(from_hct.hue, to_hct.hue)
rotation = min(diff * 0.8, 100)
output_hue = sanitize_degrees_double(
from_hct.hue + rotation * rotation_direction(from_hct.hue, to_hct.hue)
)
output_hue = sanitize_degrees_double(from_hct.hue + rotation * rotation_direction(from_hct.hue, to_hct.hue))
tone = max(0.0, min(100.0, from_hct.tone * (1 + tone_boost)))
return Hct.from_hct(output_hue, from_hct.chroma, tone)
def terminal_palette(
colors: dict[str, str], mode: str, variant: str
) -> dict[str, str]:
def terminal_palette(colors: dict[str, str], mode: str, variant: str) -> dict[str, str]:
light = mode.lower() == "light"
key_hex = (
@@ -576,17 +570,13 @@ def generate(
schemes = list_schemes()
if accent and p_scheme in schemes:
meta = schemes[p_scheme]
var_accents = next(
(v.accents for v in meta.variants if v.id == p_variant), ()
)
var_accents = next((v.accents for v in meta.variants if v.id == p_variant), ())
if accent not in var_accents:
available = ", ".join(var_accents) if var_accents else "none"
raise typer.BadParameter(
f"Accent '{accent}' not available for '{p_scheme}:{p_variant}'. Available accents: {available}"
)
palette_obj = get_palette(
p_scheme, p_variant, mode or config_mode, accent=accent
)
palette_obj = get_palette(p_scheme, p_variant, mode or config_mode, accent=accent)
colors = palette_obj.colors
effective_mode = palette_obj.mode
name = palette_obj.scheme
+2 -2
View File
@@ -34,9 +34,9 @@ def lockscreen(
return
if size[0] < 3840 or size[1] < 2160:
img = img.resize((size[0] // 2, size[1] // 2), Image.NEAREST)
img = img.resize((size[0] // 2, size[1] // 2), Image.Resampling.NEAREST)
else:
img = img.resize((size[0] // 4, size[1] // 4), Image.NEAREST)
img = img.resize((size[0] // 4, size[1] // 4), Image.Resampling.NEAREST)
img = img.filter(ImageFilter.GaussianBlur(blur_amount))