Code cleanup/comments, more test coverage (92%)
This commit is contained in:
parent
66950eeff5
commit
74177f2cd3
19 changed files with 463 additions and 22 deletions
|
|
@ -296,7 +296,6 @@ class DBManager:
|
|||
) -> None:
|
||||
"""
|
||||
Point the page head (pages.current_version_id) to an existing version.
|
||||
Fast revert: no content is rewritten.
|
||||
"""
|
||||
if self.conn is None:
|
||||
raise RuntimeError("Database is not connected")
|
||||
|
|
@ -356,6 +355,9 @@ class DBManager:
|
|||
json.dump(data, f, ensure_ascii=False, separators=(",", ":"))
|
||||
|
||||
def export_csv(self, entries: Sequence[Entry], file_path: str) -> None:
|
||||
"""
|
||||
Export pages to CSV.
|
||||
"""
|
||||
# utf-8-sig adds a BOM so Excel opens as UTF-8 by default.
|
||||
with open(file_path, "w", encoding="utf-8-sig", newline="") as f:
|
||||
writer = csv.writer(f)
|
||||
|
|
@ -369,6 +371,10 @@ class DBManager:
|
|||
separator: str = "\n\n— — — — —\n\n",
|
||||
strip_html: bool = True,
|
||||
) -> None:
|
||||
"""
|
||||
Strip the HTML from the latest version of the pages
|
||||
and save to a text file.
|
||||
"""
|
||||
import re, html as _html
|
||||
|
||||
# Precompiled patterns
|
||||
|
|
@ -407,6 +413,9 @@ class DBManager:
|
|||
def export_html(
|
||||
self, entries: Sequence[Entry], file_path: str, title: str = "Bouquin export"
|
||||
) -> None:
|
||||
"""
|
||||
Export to HTML with a heading.
|
||||
"""
|
||||
parts = [
|
||||
"<!doctype html>",
|
||||
'<html lang="en">',
|
||||
|
|
@ -429,6 +438,10 @@ class DBManager:
|
|||
def export_markdown(
|
||||
self, entries: Sequence[Entry], file_path: str, title: str = "Bouquin export"
|
||||
) -> None:
|
||||
"""
|
||||
Export to HTML, similar to export_html, but then convert to Markdown
|
||||
using markdownify, and finally save to file.
|
||||
"""
|
||||
parts = [
|
||||
"<!doctype html>",
|
||||
'<html lang="en">',
|
||||
|
|
@ -469,6 +482,10 @@ class DBManager:
|
|||
cur.execute("DETACH DATABASE backup")
|
||||
|
||||
def export_by_extension(self, file_path: str) -> None:
|
||||
"""
|
||||
Fallback catch-all that runs one of the above functions based on
|
||||
the extension of the file name that was chosen by the user.
|
||||
"""
|
||||
entries = self.get_all_entries()
|
||||
ext = os.path.splitext(file_path)[1].lower()
|
||||
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ class HistoryDialog(QDialog):
|
|||
# Diff vs current (textual diff)
|
||||
cur = self._db.get_version(version_id=self._current_id)
|
||||
self.diff.setHtml(_colored_unified_diff_html(cur["content"], sel["content"]))
|
||||
# Enable revert only if selecting a non-current
|
||||
# Enable revert only if selecting a non-current version
|
||||
self.btn_revert.setEnabled(sel_id != self._current_id)
|
||||
|
||||
@Slot()
|
||||
|
|
@ -167,7 +167,7 @@ class HistoryDialog(QDialog):
|
|||
sel_id = item.data(Qt.UserRole)
|
||||
if sel_id == self._current_id:
|
||||
return
|
||||
# Flip head pointer
|
||||
# Flip head pointer to the older version
|
||||
try:
|
||||
self._db.revert_to_version(self._date, version_id=sel_id)
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@ class KeyPrompt(QDialog):
|
|||
title: str = "Enter key",
|
||||
message: str = "Enter key",
|
||||
):
|
||||
"""
|
||||
Prompt the user for the key required to decrypt the database.
|
||||
|
||||
Used when opening the app, unlocking the idle locked screen,
|
||||
or when rekeying.
|
||||
"""
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle(title)
|
||||
v = QVBoxLayout(self)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton
|
|||
|
||||
class LockOverlay(QWidget):
|
||||
def __init__(self, parent: QWidget, on_unlock: callable):
|
||||
"""
|
||||
Widget that 'locks' the screen after a configured idle time.
|
||||
"""
|
||||
super().__init__(parent)
|
||||
self.setObjectName("LockOverlay")
|
||||
self.setAttribute(Qt.WA_StyledBackground, True)
|
||||
|
|
@ -39,6 +42,9 @@ class LockOverlay(QWidget):
|
|||
self.hide()
|
||||
|
||||
def _is_dark(self, pal: QPalette) -> bool:
|
||||
"""
|
||||
Detect if dark mode is in use.
|
||||
"""
|
||||
c = pal.color(QPalette.Window)
|
||||
luma = 0.2126 * c.redF() + 0.7152 * c.greenF() + 0.0722 * c.blueF()
|
||||
return luma < 0.5
|
||||
|
|
@ -58,7 +64,7 @@ class LockOverlay(QWidget):
|
|||
|
||||
self.setStyleSheet(
|
||||
f"""
|
||||
#LockOverlay {{ background-color: rgb(0,0,0); }} /* opaque, no transparency */
|
||||
#LockOverlay {{ background-color: rgb(0,0,0); }}
|
||||
#LockOverlay QLabel#lockLabel {{ color: {accent_hex}; font-weight: 600; }}
|
||||
|
||||
#LockOverlay QPushButton#unlockButton {{
|
||||
|
|
@ -113,7 +119,7 @@ class LockOverlay(QWidget):
|
|||
|
||||
def changeEvent(self, ev):
|
||||
super().changeEvent(ev)
|
||||
# Only re-style on palette flips
|
||||
# Only re-style on palette flips (user changed theme)
|
||||
if ev.type() in (QEvent.PaletteChange, QEvent.ApplicationPaletteChange):
|
||||
self._apply_overlay_style()
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ class MainWindow(QMainWindow):
|
|||
split = QSplitter()
|
||||
split.addWidget(left_panel)
|
||||
split.addWidget(self.editor)
|
||||
split.setStretchFactor(1, 1) # editor grows
|
||||
split.setStretchFactor(1, 1)
|
||||
|
||||
container = QWidget()
|
||||
lay = QVBoxLayout(container)
|
||||
|
|
@ -281,7 +281,7 @@ class MainWindow(QMainWindow):
|
|||
if hasattr(self, "_lock_overlay"):
|
||||
self._lock_overlay._apply_overlay_style()
|
||||
self._apply_calendar_text_colors()
|
||||
self._apply_link_css() # Reapply link styles based on the current theme
|
||||
self._apply_link_css()
|
||||
self._apply_search_highlights(getattr(self, "_search_highlighted_dates", set()))
|
||||
self.calendar.update()
|
||||
self.editor.viewport().update()
|
||||
|
|
@ -298,7 +298,6 @@ class MainWindow(QMainWindow):
|
|||
css = "" # Default to no custom styling for links (system or light theme)
|
||||
|
||||
try:
|
||||
# Apply to the editor (QTextEdit or any other relevant widgets)
|
||||
self.editor.document().setDefaultStyleSheet(css)
|
||||
except Exception:
|
||||
pass
|
||||
|
|
@ -347,7 +346,6 @@ class MainWindow(QMainWindow):
|
|||
self.calendar.setPalette(app_pal)
|
||||
self.calendar.setStyleSheet("")
|
||||
|
||||
# Keep weekend text color in sync with the current palette
|
||||
self._apply_calendar_text_colors()
|
||||
self.calendar.update()
|
||||
|
||||
|
|
@ -855,6 +853,9 @@ If you want an encrypted backup, choose Backup instead of Export.
|
|||
return super().eventFilter(obj, event)
|
||||
|
||||
def _enter_lock(self):
|
||||
"""
|
||||
Trigger the lock overlay and disable widgets
|
||||
"""
|
||||
if self._locked:
|
||||
return
|
||||
self._locked = True
|
||||
|
|
@ -870,6 +871,10 @@ If you want an encrypted backup, choose Backup instead of Export.
|
|||
|
||||
@Slot()
|
||||
def _on_unlock_clicked(self):
|
||||
"""
|
||||
Prompt for key to unlock screen
|
||||
If successful, re-enable widgets
|
||||
"""
|
||||
try:
|
||||
ok = self._prompt_for_key_until_valid(first_time=False)
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ class SaveDialog(QDialog):
|
|||
title: str = "Enter a name for this version",
|
||||
message: str = "Enter a name for this version?",
|
||||
):
|
||||
"""
|
||||
Used for explicitly saving a new version of a page.
|
||||
"""
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle(title)
|
||||
v = QVBoxLayout(self)
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@ class Search(QWidget):
|
|||
try:
|
||||
rows: Iterable[Row] = self._db.search_entries(q)
|
||||
except Exception:
|
||||
# be quiet on DB errors here; caller can surface if desired
|
||||
rows = []
|
||||
|
||||
self._populate_results(q, rows)
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ class ThemeManager(QObject):
|
|||
scheme = getattr(hints, "colorScheme", None)
|
||||
if callable(scheme):
|
||||
scheme = hints.colorScheme()
|
||||
# 0=Light, 1=Dark in newer Qt; fall back to Light
|
||||
# 0=Light, 1=Dark; fall back to Light
|
||||
theme = Theme.DARK if scheme == 1 else Theme.LIGHT
|
||||
|
||||
# Always use Fusion so palette applies consistently cross-platform
|
||||
|
|
@ -58,7 +58,6 @@ class ThemeManager(QObject):
|
|||
if theme == Theme.DARK:
|
||||
pal = self._dark_palette()
|
||||
self._app.setPalette(pal)
|
||||
# keep stylesheet empty unless you need widget-specific tweaks
|
||||
self._app.setStyleSheet("")
|
||||
else:
|
||||
pal = self._light_palette()
|
||||
|
|
|
|||
|
|
@ -140,6 +140,11 @@ class ToolBar(QToolBar):
|
|||
for a in (self.actAlignL, self.actAlignC, self.actAlignR):
|
||||
a.setActionGroup(self.grpAlign)
|
||||
|
||||
self.grpLists = QActionGroup(self)
|
||||
self.grpLists.setExclusive(True)
|
||||
for a in (self.actBullets, self.actNumbers, self.actCheckboxes):
|
||||
a.setActionGroup(self.grpLists)
|
||||
|
||||
# Add actions
|
||||
self.addActions(
|
||||
[
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue