summaryrefslogtreecommitdiffstats
path: root/ui/frontmatter_editor.py
blob: 6c53ac3e5c4cc626c2186a84e5ea39c4488c7399 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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 MultiTokenCompleter(QCompleter):
    """QCompleter that completes only the last comma-separated token."""

    def __init__(self, tags: list[str], parent=None):
        super().__init__(tags, parent)
        self.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)

    def attach(self, line_edit):
        self.setWidget(line_edit)
        line_edit.textEdited.connect(self._on_text_edited)
        self.activated[str].connect(self._on_activated)

    def _on_text_edited(self, text: str):
        token = text.split(",")[-1].lstrip()
        if token:
            self.setCompletionPrefix(token)
            self.complete()
        else:
            self.popup().hide()

    def _on_activated(self, completion: str):
        widget = self.widget()
        if widget is None:
            return
        current = widget.text()
        if "," in current:
            prefix = current.rsplit(",", 1)[0]
            new_text = prefix + ", " + completion + ", "
        else:
            new_text = completion + ", "
        widget.setText(new_text)
        widget.setCursorPosition(len(new_text))
        self.popup().hide()


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))
                MultiTokenCompleter(known_tags, widget).attach(widget)
                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))