spinner animat i comptador de clones en curs
This commit is contained in:
+45
-19
@@ -9,6 +9,8 @@ import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import tomllib
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
@@ -23,7 +25,7 @@ from rich.live import Live
|
||||
from rich.table import Table
|
||||
from rich.text import Text
|
||||
|
||||
__version__ = "1.0.3"
|
||||
__version__ = "1.0.4"
|
||||
|
||||
console = Console()
|
||||
|
||||
@@ -258,6 +260,13 @@ def build_entries(remote_repos: list[RemoteRepo], local_index: dict[str, Path])
|
||||
|
||||
# --- render -----------------------------------------------------------------
|
||||
|
||||
SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
||||
|
||||
|
||||
def spinner_frame() -> str:
|
||||
return SPINNER_FRAMES[int(time.monotonic() * 10) % len(SPINNER_FRAMES)]
|
||||
|
||||
|
||||
def compute_page(cursor: int, total: int) -> tuple[int, int, int, int, int]:
|
||||
"""Calcula (start, end, page_idx, total_pages, page_size) per a la finestra visible."""
|
||||
h = max(10, console.size.height)
|
||||
@@ -294,7 +303,7 @@ def render(entries: list[RepoEntry], cursor: int, base: Path, owner: str, status
|
||||
|
||||
# Estado
|
||||
if e.cloning:
|
||||
state = Text("⟳ clonando…", style="bold blue")
|
||||
state = Text(f"{spinner_frame()} clonant…", style="bold blue")
|
||||
elif e.cloned_ok is True:
|
||||
state = Text("✓ clonado", style="bold green")
|
||||
elif e.cloned_ok is False:
|
||||
@@ -364,27 +373,44 @@ def clone_one(entry: RepoEntry, base: Path, cfg: Config) -> tuple[RepoEntry, boo
|
||||
def run_clone_queue(entries: list[RepoEntry], base: Path, cfg: Config, live: Live, cursor: int, owner: str) -> int:
|
||||
pending = [e for e in entries if e.selected and e.local_path is None and e.remote is not None]
|
||||
if not pending:
|
||||
live.update(render(entries, cursor, base, owner, "Nada marcado para clonar."))
|
||||
live.update(render(entries, cursor, base, owner, "Res marcat per clonar."), refresh=True)
|
||||
return 0
|
||||
for e in pending:
|
||||
e.cloning = True
|
||||
e.selected = False
|
||||
live.update(render(entries, cursor, base, owner, f"Clonando {len(pending)} repo(s)…"))
|
||||
ok = 0
|
||||
max_workers = min(4, len(pending))
|
||||
with ThreadPoolExecutor(max_workers=max_workers) as ex:
|
||||
futures = {ex.submit(clone_one, e, base, cfg): e for e in pending}
|
||||
for fut in as_completed(futures):
|
||||
entry, success, msg = fut.result()
|
||||
entry.cloning = False
|
||||
entry.cloned_ok = success
|
||||
entry.error = "" if success else msg
|
||||
if success:
|
||||
entry.local_path = base / entry.name
|
||||
ok += 1
|
||||
live.update(render(entries, cursor, base, owner, f"Clonando… {ok}/{len(pending)} listos"))
|
||||
live.update(render(entries, cursor, base, owner, f"Hecho: {ok}/{len(pending)} clonados."))
|
||||
return ok
|
||||
total = len(pending)
|
||||
|
||||
stop = threading.Event()
|
||||
state = {"ok": 0, "done": 0}
|
||||
|
||||
def animate() -> None:
|
||||
while not stop.is_set():
|
||||
in_progress = total - state["done"]
|
||||
msg = f"Clonant {in_progress} en curs · {state['done']}/{total} acabats"
|
||||
live.update(render(entries, cursor, base, owner, msg), refresh=True)
|
||||
stop.wait(0.1)
|
||||
|
||||
anim = threading.Thread(target=animate, daemon=True)
|
||||
anim.start()
|
||||
try:
|
||||
max_workers = min(4, total)
|
||||
with ThreadPoolExecutor(max_workers=max_workers) as ex:
|
||||
futures = {ex.submit(clone_one, e, base, cfg): e for e in pending}
|
||||
for fut in as_completed(futures):
|
||||
entry, success, msg = fut.result()
|
||||
entry.cloning = False
|
||||
entry.cloned_ok = success
|
||||
entry.error = "" if success else msg
|
||||
if success:
|
||||
entry.local_path = base / entry.name
|
||||
state["ok"] += 1
|
||||
state["done"] += 1
|
||||
finally:
|
||||
stop.set()
|
||||
anim.join()
|
||||
|
||||
live.update(render(entries, cursor, base, owner, f"Fet: {state['ok']}/{total} clonats."), refresh=True)
|
||||
return state["ok"]
|
||||
|
||||
|
||||
# --- TUI loop ---------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user