rename: cloud sync -> instance sync; cloud -> Timemachine Network; drop Tk
CI / test (3.10) (push) Successful in 7s
CI / test (3.11) (push) Successful in 7s
CI / test (3.12) (push) Successful in 7s
CI / build-pyz (push) Successful in 4s
CI / release (push) Has been skipped

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.
This commit is contained in:
2026-06-05 01:14:02 +02:00
parent f1cb9f4b86
commit b31fdd023a
10 changed files with 99 additions and 232 deletions
+19 -18
View File
@@ -1,4 +1,4 @@
"""Qt progress UI for cloud-sync.
"""Qt UI for instance-sync.
Supports both PySide6 (preferred — LGPL, official Qt binding) and PyQt6
(fallback — GPL/commercial). Same code runs on both because their
@@ -6,7 +6,7 @@ QtWidgets / QtCore APIs are interchangeable for our subset.
This module never imports Qt at top level. ``import_qt()`` raises
ImportError if neither binding is available; the factory in ``ui.py``
catches that and falls back to the tkinter window.
catches that and falls back to :class:`HeadlessProgress`.
Threading model: ``QApplication`` runs on the main thread (started by
``run_with`` via ``QDialog.exec``); the restic worker runs on a daemon
@@ -35,7 +35,7 @@ def import_qt() -> tuple[Any, Any, Any]:
except ImportError as e:
raise ImportError(
"neither PySide6 nor PyQt6 is installed; "
"pip install 'cloud-sync[qt]' or pip install PySide6"
"pip install PySide6 (or pip install 'cloud-sync[qt]')"
) from e
@@ -143,7 +143,7 @@ class QtProgressWindow:
_apply_prism_dark(self._app)
self._dialog = QtWidgets.QDialog()
self._dialog.setWindowTitle("Cloud sync")
self._dialog.setWindowTitle("Instance sync")
self._dialog.setFixedSize(520, 240)
self._dialog.setStyleSheet(_PROGRESS_QSS)
self._dialog.setWindowFlag(
@@ -161,7 +161,7 @@ class QtProgressWindow:
badge.setPixmap(icons.svg_pixmap(icons.SYNC_BADGE_SVG, 32))
badge.setFixedSize(32, 32)
header.addWidget(badge)
self._title_label = QtWidgets.QLabel("CLOUD SYNC")
self._title_label = QtWidgets.QLabel("INSTANCE SYNC")
self._title_label.setObjectName("title")
header.addWidget(self._title_label)
header.addStretch(1)
@@ -326,7 +326,7 @@ def prompt_login_qt() -> str | None:
_apply_prism_dark(app)
dialog = QtWidgets.QDialog()
dialog.setWindowTitle("Cloud sync — connect account")
dialog.setWindowTitle("Instance sync — connect account")
dialog.setFixedSize(560, 360)
dialog.setStyleSheet(_LOGIN_QSS)
@@ -340,16 +340,16 @@ def prompt_login_qt() -> str | None:
badge.setPixmap(icons.svg_pixmap(icons.PLUS_BADGE_SVG, 32))
badge.setFixedSize(32, 32)
header.addWidget(badge)
title = QtWidgets.QLabel("CONNECT CLOUD SAVE")
title = QtWidgets.QLabel("CONNECT TO THE NETWORK")
title.setObjectName("title")
header.addWidget(title)
header.addStretch(1)
outer.addLayout(header)
body = QtWidgets.QLabel(
"To enable cross-machine save sync, message the Discord bot to "
"register this account. The bot will DM you a one-line token "
"paste it below."
"To sync this instance across machines, register on the Timemachine "
"Network. Message the Discord bot — it will DM you a one-line token. "
"Paste it below."
)
body.setObjectName("body")
body.setWordWrap(True)
@@ -380,7 +380,7 @@ def prompt_login_qt() -> str | None:
outer.addStretch(1)
foot = QtWidgets.QHBoxLayout()
skip = QtWidgets.QPushButton("Skip cloud sync")
skip = QtWidgets.QPushButton("Skip instance sync")
skip.setObjectName("secondary")
foot.addWidget(skip)
foot.addStretch(1)
@@ -479,7 +479,7 @@ def prompt_conflict_qt(
Args:
local_modified: human-readable "Saturday, February 12 at 12:28 AM"
remote_modified: same, but for the cloud snapshot
remote_modified: same, but for the Timemachine Network snapshot
save_label: noun phrase for body copy (e.g. "Minecraft save").
Returns one of: ``"keep_local"``, ``"use_remote"``, ``"cancel"``.
@@ -527,7 +527,7 @@ def prompt_conflict_qt(
self.clicked.emit()
dialog = QtWidgets.QDialog()
dialog.setWindowTitle("Cloud sync — conflict")
dialog.setWindowTitle("Instance sync — conflict")
dialog.setFixedSize(640, 460)
dialog.setStyleSheet(_CONFLICT_QSS)
@@ -541,16 +541,17 @@ def prompt_conflict_qt(
warning.setPixmap(icons.svg_pixmap(icons.WARNING_BADGE_SVG, 32))
warning.setFixedSize(32, 32)
header.addWidget(warning)
title = QtWidgets.QLabel("CLOUD CONFLICT")
title = QtWidgets.QLabel("INSTANCE CONFLICT")
title.setObjectName("title")
header.addWidget(title)
header.addStretch(1)
outer.addLayout(header)
body = QtWidgets.QLabel(
f"Your local {save_label} conflicts with what is stored in the cloud. "
f"Whichever save data you choose to keep will be synced to this device "
f"and the cloud. The option you choose not to keep will be overwritten."
f"Your local {save_label} conflicts with what is stored on the "
f"Timemachine Network. Whichever save data you choose to keep will "
f"be synced to this device and the network. The option you choose "
f"not to keep will be overwritten."
)
body.setObjectName("body")
body.setWordWrap(True)
@@ -560,7 +561,7 @@ def prompt_conflict_qt(
cloud_card = _Card(
icons.svg_pixmap(icons.CLOUD_SVG, 32),
"Cloud Save",
"Remote Save",
f"Modified {remote_modified}",
)
local_card = _Card(