Skip to content

Commit 6ecebfe

Browse files
fmt done
1 parent bd0b60e commit 6ecebfe

5 files changed

Lines changed: 496 additions & 348 deletions

File tree

autosave_manager.py

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,12 @@
1111
- Respects user's last saved version as fallback
1212
"""
1313

14-
import hashlib
1514
import logging
1615
from typing import Optional, Callable
17-
from pathlib import Path
1816

1917
try:
2018
from PySide6.QtCore import QTimer, QObject, Signal
19+
2120
HAS_PYSIDE = True
2221
except ImportError:
2322
HAS_PYSIDE = False
@@ -34,51 +33,51 @@
3433
class AutosaveManager(QObject if HAS_PYSIDE else object):
3534
"""
3635
Manages automatic saving of projects with intelligent change detection.
37-
36+
3837
Never saves unnecessarily — only when state actually changes.
3938
Uses debouncing to prevent rapid successive saves.
4039
"""
41-
40+
4241
if HAS_PYSIDE:
4342
autosave_triggered = Signal(str) # (reason)
44-
43+
4544
def __init__(self):
4645
if HAS_PYSIDE:
4746
super().__init__()
48-
47+
4948
self._save_callback: Optional[Callable[[], None]] = None
5049
self._timer: Optional[QTimer] = None
51-
50+
5251
# Hash tracking
5352
self._last_code_hash = ""
5453
self._last_graph_hash = ""
5554
self._last_assets_hash = ""
5655
self._last_keybindings_hash = ""
57-
56+
5857
# Compute functions (will be set by EfficientManimWindow)
5958
self._compute_code_hash: Optional[Callable[[], str]] = None
6059
self._compute_graph_hash: Optional[Callable[[], str]] = None
6160
self._compute_assets_hash: Optional[Callable[[], str]] = None
6261
self._compute_keybindings_hash: Optional[Callable[[], str]] = None
63-
62+
6463
self._enabled = False
65-
64+
6665
if HAS_PYSIDE:
6766
self._setup_timer()
68-
67+
6968
def _setup_timer(self) -> None:
7069
"""Initialize the autosave timer."""
7170
self._timer = QTimer()
7271
self._timer.setSingleShot(True) # Debounce: only fire once per interval
7372
self._timer.setInterval(AUTOSAVE_INTERVAL_MS)
7473
self._timer.timeout.connect(self._on_timer_timeout)
7574
LOGGER.debug(f"Autosave timer configured: {AUTOSAVE_INTERVAL_MS}ms interval")
76-
75+
7776
def set_save_callback(self, callback: Callable[[], None]) -> None:
7877
"""Register the save function to call on autosave."""
7978
self._save_callback = callback
8079
LOGGER.debug("Autosave callback registered")
81-
80+
8281
def set_hash_computers(
8382
self,
8483
code_fn: Callable[[], str],
@@ -92,56 +91,64 @@ def set_hash_computers(
9291
self._compute_assets_hash = assets_fn
9392
self._compute_keybindings_hash = keybindings_fn
9493
LOGGER.debug("Hash computer functions registered")
95-
94+
9695
def enable(self) -> None:
9796
"""Enable autosave."""
9897
self._enabled = True
9998
LOGGER.info("Autosave enabled")
100-
99+
101100
def disable(self) -> None:
102101
"""Disable autosave."""
103102
self._enabled = False
104103
if HAS_PYSIDE and self._timer:
105104
self._timer.stop()
106105
LOGGER.info("Autosave disabled")
107-
106+
108107
def trigger_autosave(self, reason: str = "change detected") -> None:
109108
"""
110109
Request autosave with debouncing.
111-
110+
112111
Won't save immediately — will wait for quiet period,
113112
then check if state actually changed before saving.
114113
"""
115114
if not self._enabled:
116115
return
117-
116+
118117
if HAS_PYSIDE and self._timer:
119118
self._timer.stop()
120119
self._timer.start() # Reset debounce timer
121120
LOGGER.debug(f"Autosave scheduled ({reason})")
122-
121+
123122
def _on_timer_timeout(self) -> None:
124123
"""Timer fired — check if state changed, then save."""
125124
if not self._enabled or not self._save_callback:
126125
return
127-
126+
128127
try:
129128
# Compute current hashes
130129
code_hash = self._compute_code_hash() if self._compute_code_hash else ""
131130
graph_hash = self._compute_graph_hash() if self._compute_graph_hash else ""
132-
assets_hash = self._compute_assets_hash() if self._compute_assets_hash else ""
133-
keybindings_hash = self._compute_keybindings_hash() if self._compute_keybindings_hash else ""
134-
131+
assets_hash = (
132+
self._compute_assets_hash() if self._compute_assets_hash else ""
133+
)
134+
keybindings_hash = (
135+
self._compute_keybindings_hash()
136+
if self._compute_keybindings_hash
137+
else ""
138+
)
139+
135140
# Check if anything changed
136-
code_changed = (code_hash != self._last_code_hash)
137-
graph_changed = (graph_hash != self._last_graph_hash)
138-
assets_changed = (assets_hash != self._last_assets_hash)
139-
keybindings_changed = (keybindings_hash != self._last_keybindings_hash)
140-
141-
if not (code_changed or graph_changed or assets_changed or keybindings_changed):
141+
code_changed = code_hash != self._last_code_hash
142+
graph_changed = graph_hash != self._last_graph_hash
143+
assets_changed = assets_hash != self._last_assets_hash
144+
keybindings_changed = keybindings_hash != self._last_keybindings_hash
145+
146+
if not (
147+
code_changed or graph_changed or assets_changed or keybindings_changed
148+
):
142149
LOGGER.debug("No changes detected, skipping autosave")
143150
return
144-
151+
145152
# State changed — save now
146153
reason_parts = []
147154
if code_changed:
@@ -152,26 +159,26 @@ def _on_timer_timeout(self) -> None:
152159
reason_parts.append("assets")
153160
if keybindings_changed:
154161
reason_parts.append("keybindings")
155-
162+
156163
reason = f"autosave ({', '.join(reason_parts)})"
157-
164+
158165
# Update hashes for next check
159166
self._last_code_hash = code_hash
160167
self._last_graph_hash = graph_hash
161168
self._last_assets_hash = assets_hash
162169
self._last_keybindings_hash = keybindings_hash
163-
170+
164171
# Perform the save
165172
self._save_callback()
166-
173+
167174
if HAS_PYSIDE:
168175
self.autosave_triggered.emit(reason)
169-
176+
170177
LOGGER.info(f"✓ Autosave completed ({', '.join(reason_parts)})")
171-
178+
172179
except Exception as e:
173180
LOGGER.error(f"Autosave failed: {e}")
174-
181+
175182
def force_save(self) -> None:
176183
"""Force immediate save (bypass debounce)."""
177184
if HAS_PYSIDE and self._timer:

0 commit comments

Comments
 (0)