From: Danilo M. Date: Sun, 3 May 2026 08:33:08 +0000 (+0200) Subject: feat: frontmatter editor dialog with type dropdown and tag autocomplete X-Git-Tag: v1.0~15 X-Git-Url: https://git.danix.xyz/?a=commitdiff_plain;h=ccbefa7b40f883e85c2b16e48519bece2b909c0c;p=publisher.git feat: frontmatter editor dialog with type dropdown and tag autocomplete --- diff --git a/ui/frontmatter_editor.py b/ui/frontmatter_editor.py new file mode 100644 index 0000000..218bbac --- /dev/null +++ b/ui/frontmatter_editor.py @@ -0,0 +1,85 @@ +from __future__ import annotations +from PyQt6.QtWidgets import ( + QDialog, QVBoxLayout, QHBoxLayout, QFormLayout, + QLabel, QLineEdit, QComboBox, QPushButton, + QCompleter, QWidget, QScrollArea, +) +from PyQt6.QtCore import Qt +from pathlib import Path +from core.models import Article, ARTICLE_TYPES +from core.frontmatter import parse_frontmatter, write_frontmatter +from core.taxonomy import load_taxonomy + +class FrontmatterEditor(QDialog): + def __init__(self, article: Article, blog_root: Path, parent=None): + super().__init__(parent) + self.setWindowTitle(f"Frontmatter — {article.slug}") + self.setMinimumWidth(520) + self._article = article + self._blog_root = blog_root + self._fields: dict[str, QWidget] = {} + self._build_ui() + + def _build_ui(self): + layout = QVBoxLayout(self) + + scroll = QScrollArea() + scroll.setWidgetResizable(True) + inner = QWidget() + form = QFormLayout(inner) + scroll.setWidget(inner) + layout.addWidget(scroll) + + lang = self._article.lang + tags_it = self._blog_root / "docs" / "tags-it.txt" + tags_en = self._blog_root / "docs" / "tags-en.txt" + taxonomy = load_taxonomy(tags_it, tags_en) + known_tags = list(taxonomy.it_to_en.keys()) if lang == "it" else list(taxonomy.it_to_en.values()) + + for key, val in self._article.frontmatter.items(): + if key == "type": + widget = QComboBox() + widget.addItems(ARTICLE_TYPES) + if str(val) in ARTICLE_TYPES: + widget.setCurrentText(str(val)) + self._fields[key] = widget + elif key == "tags": + widget = QLineEdit(", ".join(str(v) for v in val) if isinstance(val, list) else str(val)) + completer = QCompleter(known_tags) + completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive) + widget.setCompleter(completer) + self._fields[key] = widget + else: + widget = QLineEdit(str(val)) + self._fields[key] = widget + form.addRow(QLabel(key), widget) + + btns = QHBoxLayout() + save_btn = QPushButton("Salva") + save_btn.clicked.connect(self._save) + cancel_btn = QPushButton("Annulla") + cancel_btn.clicked.connect(self.reject) + btns.addStretch() + btns.addWidget(cancel_btn) + btns.addWidget(save_btn) + layout.addLayout(btns) + + def _save(self): + updated = dict(self._article.frontmatter) + for key, widget in self._fields.items(): + if isinstance(widget, QComboBox): + updated[key] = widget.currentText() + elif isinstance(widget, QLineEdit): + val = widget.text().strip() + if key == "tags": + updated[key] = [t.strip() for t in val.split(",") if t.strip()] + else: + updated[key] = val + try: + _, body = parse_frontmatter(self._article.path) + write_frontmatter(self._article.path, updated, body) + self._article.frontmatter.update(updated) + self.accept() + except Exception as e: + from PyQt6.QtWidgets import QMessageBox + QMessageBox.critical(self, "Errore", str(e))