--- /dev/null
+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))