from __future__ import annotations import subprocess from pathlib import Path from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QPlainTextEdit, QTextBrowser, ) from PyQt6.QtCore import pyqtSignal from core.models import Article from core.frontmatter import parse_frontmatter from workers.translation_worker import TranslationWorker import re def _strip_shortcodes(text: str) -> str: return re.sub(r'\{\{[^}]+\}\}', '[shortcode]', text) class TranslationView(QWidget): push_master = pyqtSignal() publish = pyqtSignal() def __init__(self, transart_script: str, typora_bin: str, parent=None): super().__init__(parent) self._transart_script = transart_script self._typora_bin = typora_bin self._article: Article | None = None self._worker: TranslationWorker | None = None self._output_path: str = "" self._build_ui() def _build_ui(self): layout = QVBoxLayout(self) self._header = QLabel("") self._header.setStyleSheet("color:#fff;font-weight:bold;font-size:13px;padding:10px;") layout.addWidget(self._header) log_label = QLabel("Output traduzione:") log_label.setStyleSheet("color:#a855f7;font-size:9px;text-transform:uppercase;letter-spacing:1px;padding:0 10px;") layout.addWidget(log_label) self._log = QPlainTextEdit() self._log.setReadOnly(True) self._log.setStyleSheet("background:#0a0a12;color:#00ff88;font-family:monospace;font-size:11px;") self._log.setMaximumHeight(180) layout.addWidget(self._log) preview_label = QLabel("Preview traduzione:") preview_label.setStyleSheet("color:#a855f7;font-size:9px;text-transform:uppercase;letter-spacing:1px;padding:0 10px;") layout.addWidget(preview_label) self._preview = QTextBrowser() self._preview.setStyleSheet("background:#0d0d1a;color:#ccc;border:none;") layout.addWidget(self._preview, stretch=1) self._action_bar = QWidget() btns = QHBoxLayout(self._action_bar) btns.setContentsMargins(10, 6, 10, 6) self._btn_typora = QPushButton("โœ๏ธ Apri in Typora") self._btn_typora.clicked.connect(self._open_typora) self._btn_retry = QPushButton("๐Ÿ”„ Rigenera") self._btn_retry.clicked.connect(self._retry) self._btn_master = QPushButton("๐Ÿงช Push master") self._btn_master.clicked.connect(self.push_master.emit) self._btn_publish = QPushButton("๐Ÿšข Pubblica") self._btn_publish.clicked.connect(self.publish.emit) for btn in (self._btn_typora, self._btn_retry, self._btn_master, self._btn_publish): btns.addWidget(btn) btns.addStretch() self._action_bar.setVisible(False) layout.addWidget(self._action_bar) def start_translation(self, article: Article): self._article = article self._log.clear() self._preview.clear() self._action_bar.setVisible(False) direction = "it-en" if article.lang == "it" else "en-it" self._header.setText(f"Traduzione: {article.slug} ({article.lang.upper()} โ†’ {('EN' if article.lang == 'it' else 'IT')})") self._worker = TranslationWorker( Path(self._transart_script), article.path, direction, parent=self, ) self._worker.log_line.connect(self._log.appendPlainText) self._worker.finished.connect(self._on_finished) self._worker.error.connect(lambda e: self._log.appendPlainText(f"[ERRORE] {e}")) self._worker.start() def _on_finished(self, success: bool, output_path: str): self._output_path = output_path if success and Path(output_path).exists(): try: _, body = parse_frontmatter(Path(output_path)) self._preview.setMarkdown(_strip_shortcodes(body)) except Exception as e: self._preview.setPlainText(f"[Errore preview: {e}]") self._action_bar.setVisible(True) def _open_typora(self): if self._output_path: subprocess.Popen([self._typora_bin, self._output_path]) def _retry(self): if self._article: self.start_translation(self._article)