from __future__ import annotations import subprocess import re from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QTextBrowser, QScrollArea, QSizePolicy, QFrame, ) from PyQt6.QtCore import pyqtSignal, Qt from core.models import Article from core.frontmatter import parse_frontmatter def _strip_shortcodes(text: str) -> str: return re.sub(r'\{\{[^}]+\}\}', '[shortcode]', text) class ArticleDetailView(QWidget): open_typora = pyqtSignal(object) # Article open_frontmatter = pyqtSignal(object) # Article translate = pyqtSignal(object) # Article push_master = pyqtSignal(object) # Article publish = pyqtSignal(object) # Article delete_article = pyqtSignal(object) # Article def __init__(self, parent=None): super().__init__(parent) self._article: Article | None = None self._build_ui() def _build_ui(self): layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) # Header header = QWidget() header.setStyleSheet("background: #0f0f1a; border-bottom: 1px solid #2a2a4e;") h_layout = QHBoxLayout(header) h_layout.setContentsMargins(12, 8, 12, 8) self._title_label = QLabel("") self._title_label.setStyleSheet("color: #fff; font-weight: bold; font-size: 13px;") self._path_label = QLabel("") self._path_label.setStyleSheet("color: #555; font-size: 10px;") info = QVBoxLayout() info.addWidget(self._title_label) info.addWidget(self._path_label) h_layout.addLayout(info, stretch=1) for (icon, signal_name, style) in [ ("โœ๏ธ Typora", "open_typora", "color:#a855f7;border:1px solid #a855f7;"), ("๐Ÿ”ง Frontmatter", "open_frontmatter", "color:#888;border:1px solid #444;"), ("๐ŸŒ Traduci", "translate", "color:#f59e0b;border:1px solid #f59e0b;"), ("๐Ÿงช Push master", "push_master", "color:#60a5fa;border:1px solid #60a5fa;"), ("๐Ÿšข Pubblica", "publish", "color:#00ff88;border:1px solid #00ff88;"), ("๐Ÿ—‘ Elimina", "delete_article", "color:#ff6b6b;border:1px solid #ff6b6b;"), ]: btn = QPushButton(icon) btn.setStyleSheet(f"background:#1a1a2e;{style}padding:4px 10px;border-radius:4px;font-size:10px;") btn.clicked.connect(lambda _, s=signal_name: getattr(self, s).emit(self._article)) h_layout.addWidget(btn) layout.addWidget(header) # Body: split columns body = QHBoxLayout() body.setContentsMargins(0, 0, 0, 0) body.setSpacing(0) # Frontmatter column self._fm_widget = QWidget() self._fm_widget.setFixedWidth(230) self._fm_widget.setStyleSheet("background:#0a0a12;") self._fm_layout = QVBoxLayout(self._fm_widget) self._fm_layout.setContentsMargins(10, 10, 10, 10) fm_title = QLabel("Frontmatter") fm_title.setStyleSheet("color:#a855f7;font-size:9px;text-transform:uppercase;letter-spacing:1px;") self._fm_layout.addWidget(fm_title) self._fm_content = QVBoxLayout() self._fm_layout.addLayout(self._fm_content) self._fm_layout.addStretch() body.addWidget(self._fm_widget) divider = QFrame() divider.setFrameShape(QFrame.Shape.VLine) divider.setStyleSheet("color:#1a1a2e;") body.addWidget(divider) # Preview column preview_container = QWidget() preview_layout = QVBoxLayout(preview_container) preview_layout.setContentsMargins(12, 10, 12, 10) preview_title = QLabel("Preview markdown") preview_title.setStyleSheet("color:#a855f7;font-size:9px;text-transform:uppercase;letter-spacing:1px;") preview_layout.addWidget(preview_title) self._preview = QTextBrowser() self._preview.setStyleSheet("background:#0d0d1a;color:#ccc;border:none;") self._preview.setOpenExternalLinks(True) preview_layout.addWidget(self._preview) body.addWidget(preview_container, stretch=1) layout.addLayout(body, stretch=1) def set_article(self, article: Article): self._article = article self._title_label.setText(article.slug) rel = str(article.path).replace(str(article.path.parent.parent.parent.parent), "") self._path_label.setText(rel) # Populate frontmatter while self._fm_content.count(): item = self._fm_content.takeAt(0) if item.widget(): item.widget().deleteLater() for key, val in article.frontmatter.items(): row = QVBoxLayout() key_lbl = QLabel(key) key_lbl.setStyleSheet("color:#555;font-size:9px;") val_lbl = QLabel(str(val) if not isinstance(val, list) else ", ".join(str(v) for v in val)) val_lbl.setStyleSheet("color:#e2e8f0;font-size:11px;") val_lbl.setWordWrap(True) row.addWidget(key_lbl) row.addWidget(val_lbl) container = QWidget() container.setLayout(row) self._fm_content.addWidget(container) # Load and render markdown preview try: _, body = parse_frontmatter(article.path) clean = _strip_shortcodes(body) self._preview.setMarkdown(clean) except Exception as e: self._preview.setPlainText(f"[Errore caricamento: {e}]")