Prevent traceback on trying to edit a tag with the same name as another tag. Various other tweaks. Bump version
All checks were successful
CI / test (push) Successful in 3m31s
Lint / test (push) Successful in 16s
Trivy / test (push) Successful in 21s

This commit is contained in:
Miguel Jacq 2025-11-14 17:30:58 +11:00
parent 02a60ca656
commit 1becb7900e
Signed by: mig5
GPG key ID: 59B3F0C24135C6A9
15 changed files with 153 additions and 83 deletions

View file

@ -23,6 +23,20 @@ _TAG_COLORS = [
"#BAFFC9", # soft green
"#BAE1FF", # soft blue
"#E0BAFF", # soft purple
"#FFC4B3", # soft coral
"#FFD8B1", # soft peach
"#FFF1BA", # soft light yellow
"#E9FFBA", # soft lime
"#CFFFE5", # soft mint
"#BAFFF5", # soft aqua
"#BAF0FF", # soft cyan
"#C7E9FF", # soft sky blue
"#C7CEFF", # soft periwinkle
"#F0BAFF", # soft lavender pink
"#FFBAF2", # soft magenta
"#FFD1F0", # soft pink
"#EBD5C7", # soft beige
"#EAEAEA", # soft gray
]
@ -554,16 +568,22 @@ class DBManager:
name = name.strip()
color = color.strip() or "#CCCCCC"
with self.conn:
cur = self.conn.cursor()
cur.execute(
"""
UPDATE tags
SET name = ?, color = ?
WHERE id = ?;
""",
(name, color, tag_id),
)
try:
with self.conn:
cur = self.conn.cursor()
cur.execute(
"""
UPDATE tags
SET name = ?, color = ?
WHERE id = ?;
""",
(name, color, tag_id),
)
except sqlite.IntegrityError as e:
if "UNIQUE constraint failed: tags.name" in str(e):
raise sqlite.IntegrityError(
strings._("tag_already_exists_with_that_name")
) from e
def delete_tag(self, tag_id: int) -> None:
"""

View file

@ -112,13 +112,14 @@
"toolbar_heading": "Heading",
"toolbar_toggle_checkboxes": "Toggle checkboxes",
"tags": "Tags",
"tag": "Tag",
"manage_tags": "Manage tags",
"add_tag_placeholder": "Add a tag and press Enter",
"tag_browser_title": "Tag Browser",
"tag_browser_instructions": "Click a tag to expand and see all pages with that tag. Click a date to open it. Select a tag to edit its name, change its color, or delete it globally.",
"tag_name": "Tag name",
"tag_color_hex": "Hex colour",
"color_hex": "Color",
"color_hex": "Colour",
"date": "Date",
"pick_color": "Pick colour",
"invalid_color_title": "Invalid colour",
@ -130,5 +131,6 @@
"new_tag_name": "New tag name:",
"change_color": "Change colour",
"delete_tag": "Delete tag",
"delete_tag_confirm": "Are you sure you want to delete the tag '{name}'? This will remove it from all pages."
"delete_tag_confirm": "Are you sure you want to delete the tag '{name}'? This will remove it from all pages.",
"tag_already_exists_with_that_name": "A tag already exists with that name"
}

View file

@ -111,12 +111,13 @@
"toolbar_code_block": "Bloc de code",
"toolbar_heading": "Titre",
"toolbar_toggle_checkboxes": "Cocher/Décocher les cases",
"tags": "Tags",
"manage_tags": "Gérer les tags",
"add_tag_placeholder": "Ajouter un tag et appuyez sur Entrée",
"tag_browser_title": "Navigateur de tags",
"tag_browser_instructions": "Cliquez sur un tag pour l'étendre et voir toutes les pages avec ce tag. Cliquez sur une date pour l'ouvrir. Sélectionnez un tag pour modifier son nom, changer sa couleur ou le supprimer globalement.",
"tag_name": "Nom du tag",
"tags": "Étiquettes",
"tag": "Étiquette",
"manage_tags": "Gérer les étiquettes",
"add_tag_placeholder": "Ajouter une étiquette et appuyez sur Entrée",
"tag_browser_title": "Navigateur de étiquettes",
"tag_browser_instructions": "Cliquez sur une étiquette pour l'étendre et voir toutes les pages avec cette étiquette. Cliquez sur une date pour l'ouvrir. Sélectionnez une étiquette pour modifier son nom, changer sa couleur ou la supprimer globalement.",
"tag_name": "Nom de l'étiquette",
"tag_color_hex": "Couleur hexadécimale",
"color_hex": "Couleur",
"date": "Date",
@ -126,9 +127,10 @@
"add": "Ajouter",
"remove": "Supprimer",
"ok": "OK",
"edit_tag_name": "Modifier le nom du tag",
"new_tag_name": "Nouveau nom du tag :",
"edit_tag_name": "Modifier le nom de l'étiquette",
"new_tag_name": "Nouveau nom de l'étiquette :",
"change_color": "Changer la couleur",
"delete_tag": "Supprimer le tag",
"delete_tag_confirm": "Êtes-vous sûr de vouloir supprimer le tag '{name}' ? Cela le supprimera de toutes les pages."
"delete_tag": "Supprimer l'étiquette",
"delete_tag_confirm": "Êtes-vous sûr de vouloir supprimer l'étiquette '{name}' ? Cela la supprimera de toutes les pages.",
"tag_already_exists_with_that_name": "Une étiquette portant ce nom existe déjà"
}

View file

@ -130,5 +130,6 @@
"new_tag_name": "Nuovo nome tag:",
"change_color": "Cambia colore",
"delete_tag": "Elimina tag",
"delete_tag_confirm": "Sei sicuro di voler eliminare il tag '{name}'? Questo lo rimuoverà da tutte le pagine."
"delete_tag_confirm": "Sei sicuro di voler eliminare il tag '{name}'? Questo lo rimuoverà da tutte le pagine.",
"tag_already_exists_with_that_name": "Esiste già un tag con questo nome"
}

View file

@ -1077,8 +1077,17 @@ class MainWindow(QMainWindow):
dlg = TagBrowserDialog(self.db, self, focus_tag=tag_name_or_date)
dlg.openDateRequested.connect(self._load_selected_date)
dlg.tagsModified.connect(self._refresh_current_page_tags)
dlg.exec()
def _refresh_current_page_tags(self):
"""Refresh the tag chips for the current page (after tag browser changes)"""
if hasattr(self, "tags") and hasattr(self.editor, "current_date"):
date_iso = self.editor.current_date.toString("yyyy-MM-dd")
self.tags.set_current_date(date_iso)
if self.tags.toggle_btn.isChecked():
self.tags._reload_tags()
# ----------- Settings handler ------------#
def _open_settings(self):
dlg = SettingsDialog(self.cfg, self.db, self)

View file

@ -199,7 +199,7 @@ class MarkdownHighlighter(QSyntaxHighlighter):
self.setFormat(end - 2, 2, self.syntax_format)
self.setFormat(content_start, content_end - content_start, self.bold_format)
# --- Italic (*) or (_): skip if it overlaps any triple, keep your guards
# --- Italic (*) or (_): skip if it overlaps any triple
for m in re.finditer(
r"(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)|(?<!_)_(?!_)(.+?)(?<!_)_(?!_)", text
):

View file

@ -14,11 +14,13 @@ from PySide6.QtWidgets import (
)
from .db import DBManager
from sqlcipher3.dbapi2 import IntegrityError
from . import strings
class TagBrowserDialog(QDialog):
openDateRequested = Signal(str)
tagsModified = Signal()
def __init__(self, db: DBManager, parent=None, focus_tag: str | None = None):
super().__init__(parent)
@ -43,6 +45,8 @@ class TagBrowserDialog(QDialog):
self.tree.setColumnWidth(1, 100)
self.tree.itemActivated.connect(self._on_item_activated)
self.tree.itemClicked.connect(self._on_item_clicked)
self.tree.setSortingEnabled(True)
self.tree.sortByColumn(0, Qt.AscendingOrder)
layout.addWidget(self.tree)
# Tag management buttons
@ -77,6 +81,9 @@ class TagBrowserDialog(QDialog):
self._populate(focus_tag)
def _populate(self, focus_tag: str | None):
# Disable sorting during population for better performance
was_sorting = self.tree.isSortingEnabled()
self.tree.setSortingEnabled(False)
self.tree.clear()
tags = self._db.list_tags()
focus_item = None
@ -91,7 +98,18 @@ class TagBrowserDialog(QDialog):
)
# Set background color for the second column to show the tag color
root.setBackground(1, QColor(color))
bg_color = QColor(color)
root.setBackground(1, bg_color)
# Calculate luminance and set contrasting text color
# Using relative luminance formula (ITU-R BT.709)
luminance = (
0.2126 * bg_color.red()
+ 0.7152 * bg_color.green()
+ 0.0722 * bg_color.blue()
) / 255.0
text_color = QColor(0, 0, 0) if luminance > 0.5 else QColor(255, 255, 255)
root.setForeground(1, text_color)
root.setText(1, color) # Also show the hex code
root.setTextAlignment(1, Qt.AlignCenter)
@ -112,6 +130,9 @@ class TagBrowserDialog(QDialog):
self.tree.expandItem(focus_item)
self.tree.setCurrentItem(focus_item)
# Re-enable sorting after population
self.tree.setSortingEnabled(was_sorting)
def _on_item_clicked(self, item: QTreeWidgetItem, column: int):
"""Enable/disable buttons based on selection"""
data = item.data(0, Qt.ItemDataRole.UserRole)
@ -156,8 +177,12 @@ class TagBrowserDialog(QDialog):
)
if ok and new_name and new_name != old_name:
self._db.update_tag(tag_id, new_name, color)
self._populate(None)
try:
self._db.update_tag(tag_id, new_name, color)
self._populate(None)
self.tagsModified.emit()
except IntegrityError as e:
QMessageBox.critical(self, strings._("db_database_error"), str(e))
def _change_tag_color(self):
"""Change the color of the selected tag"""
@ -175,8 +200,12 @@ class TagBrowserDialog(QDialog):
color = QColorDialog.getColor(QColor(current_color), self)
if color.isValid():
self._db.update_tag(tag_id, name, color.name())
self._populate(None)
try:
self._db.update_tag(tag_id, name, color.name())
self._populate(None)
self.tagsModified.emit()
except IntegrityError as e:
QMessageBox.critical(self, strings._("db_database_error"), str(e))
def _delete_tag(self):
"""Delete the selected tag"""
@ -203,3 +232,4 @@ class TagBrowserDialog(QDialog):
if reply == QMessageBox.Yes:
self._db.delete_tag(tag_id)
self._populate(None)
self.tagsModified.emit()

View file

@ -70,7 +70,10 @@ class TagChip(QFrame):
def mouseReleaseEvent(self, ev):
if ev.button() == Qt.LeftButton:
self.clicked.emit(self._name)
super().mouseReleaseEvent(ev)
try:
super().mouseReleaseEvent(ev)
except RuntimeError:
pass
class PageTagsWidget(QFrame):

View file

@ -204,7 +204,7 @@ class ThemeManager(QObject):
)
if is_dark:
# Use the link color as the accent (you set this to ORANGE in dark palette)
# Use the link color as the accent
accent = pal.color(QPalette.Link)
r, g, b = accent.red(), accent.green(), accent.blue()
accent_hex = accent.name()