b31fdd023a
Product / UI / CLI / docs rebrand. Internal package, repo, and on-disk dir names stay 'cloud_sync' / 'cloud-sync' / '.cloud-sync/' to avoid breaking existing installs; a future commit can do the file-system rename when the cost is worth paying. User-facing changes: CLI prog name: cloud-sync -> instance-sync CLI description: cloud-svc URL -> Timemachine Network endpoint Dialog title: CLOUD SYNC -> INSTANCE SYNC Dialog title: CLOUD CONFLICT -> INSTANCE CONFLICT Dialog title: CONNECT CLOUD SAVE -> CONNECT TO THE NETWORK Card label: Cloud Save -> Remote Save Skip button: Skip cloud sync -> Skip instance sync Body copy: 'the cloud' -> 'the Timemachine Network' Window titles: Cloud sync — ... -> Instance sync — ... Log prefix: cloud-sync: -> instance-sync: Error prose: 'cloud-sync token' -> 'instance-sync token' Backend changes: restic --host tag: cloud-sync -> instance-sync State.host_tag dflt: cloud-sync -> instance-sync (Existing snapshots with the old tag still pull fine; we use 'latest'.) Drop tkinter fallback: ui.py now offers Qt OR Headless. tkinter is unnecessary given we already maintain Qt + headless; one less code path to keep styled, smaller pyz. make_progress() picks Qt first, falls through to HeadlessProgress on ImportError with a stderr hint to 'pip install PySide6'. README: rebrand title + prose; note repo/dir rename deferred; call out the PySide6 install step. Conflict/login dialogs are now Qt-only; without Qt, conflict aborts (defensive) and login tells the user to paste the token manually. 52 tests green; no test-file label changes needed since they only exercise internal APIs.
71 lines
2.2 KiB
Python
71 lines
2.2 KiB
Python
"""Progress UI for instance-sync operations.
|
|
|
|
Two implementations sharing the ``Progress`` protocol:
|
|
|
|
- :class:`HeadlessProgress` — no window; prints to stdout/stderr. Used when
|
|
``--no-gui`` is set OR when Qt isn't available (the only graphical path
|
|
is Qt; there is no tkinter fallback).
|
|
- :class:`QtProgressWindow` (in :mod:`cloud_sync.ui_qt`) — Qt modal window
|
|
with the Prism-dark Steam-style layout.
|
|
|
|
The factory :func:`make_progress` picks Qt → Headless. Qt requires
|
|
PySide6 or PyQt6 to be importable. Install via
|
|
``pip install 'cloud-sync[qt]'`` or directly
|
|
``pip install PySide6``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from typing import Callable, Protocol
|
|
|
|
|
|
class Progress(Protocol):
|
|
"""Interface for status + cancellation reporting during a sync run."""
|
|
|
|
def set_status(self, msg: str) -> None: ...
|
|
def is_cancelled(self) -> bool: ...
|
|
def run_with(self, worker: Callable[[], int], title: str) -> int: ...
|
|
|
|
|
|
class HeadlessProgress:
|
|
"""No-op progress. Status messages go to stdout, errors to stderr."""
|
|
|
|
def set_status(self, msg: str) -> None:
|
|
print(f"instance-sync: {msg}", flush=True)
|
|
|
|
def is_cancelled(self) -> bool:
|
|
return False
|
|
|
|
def run_with(self, worker: Callable[[], int], title: str) -> int:
|
|
self.set_status(title)
|
|
return worker()
|
|
|
|
|
|
def make_progress(headless: bool) -> Progress:
|
|
"""Return the best Progress impl for the runtime + flags.
|
|
|
|
Order:
|
|
1. Qt window (PySide6 or PyQt6) — preferred when available.
|
|
2. HeadlessProgress — fallback when ``--no-gui`` is set or Qt is
|
|
missing. Logs to stdout/stderr.
|
|
"""
|
|
if headless:
|
|
return HeadlessProgress()
|
|
try:
|
|
from .ui_qt import QtProgressWindow
|
|
return QtProgressWindow()
|
|
except ImportError:
|
|
print(
|
|
"instance-sync: Qt (PySide6/PyQt6) not installed; "
|
|
"running headless. Install with: pip install PySide6",
|
|
file=sys.stderr,
|
|
)
|
|
return HeadlessProgress()
|
|
except Exception as e: # noqa: BLE001
|
|
print(
|
|
f"instance-sync: Qt init failed ({e}); falling back to headless",
|
|
file=sys.stderr,
|
|
)
|
|
return HeadlessProgress()
|