diff options
Diffstat (limited to 'docs/superpowers/specs')
| -rw-r--r-- | docs/superpowers/specs/2026-05-01-my-publisher-design.md | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-05-01-my-publisher-design.md b/docs/superpowers/specs/2026-05-01-my-publisher-design.md new file mode 100644 index 0000000..8b831f0 --- /dev/null +++ b/docs/superpowers/specs/2026-05-01-my-publisher-design.md @@ -0,0 +1,223 @@ +# my-publisher — Design Spec + +**Date:** 2026-05-01 +**Status:** Approved + +--- + +## Context + +Workflow attuale per pubblicare articoli sul blog danix.xyz è frammentato: git manuale da terminale, hugo server lanciato a mano, traduzione via script CLI, Typora aperto separatamente, frontmatter editato a mano. L'obiettivo è centralizzare tutto in una GUI desktop Linux che automatizzi le operazioni ripetitive e riduca il friction nel publishing workflow. + +Blog: Hugo bilingual IT/EN, repo `danix.xyz-hacker-theme`, due branch (`master` = staging, `production` = live via post-receive hook). Script traduzione: `/home/danix/bin/transart.py` (RunPod + TranslateGemma 27b). + +--- + +## Architettura + +**Stack:** Python 3 + PyQt6. Processo singolo con `QThread`/`QProcess` per operazioni async — UI non blocca mai. + +``` +my-publisher/ +├── main.py +├── ui/ +│ ├── main_window.py # finestra principale, sidebar + QStackedWidget +│ ├── articles_view.py # lista articoli tab IT/EN + voce "Senza Traduzione" +│ ├── article_detail.py # pannello dettaglio (frontmatter + preview + azioni) +│ ├── frontmatter_editor.py # dialog editing TOML frontmatter +│ ├── translation_view.py # progress log + preview + azioni post-traduzione +│ ├── git_view.py # stato repo + operazioni git +│ ├── media_view.py # upload file in static/uppies/YYYY/MM/ +│ └── hugo_panel.py # start/stop hugo server + log +├── workers/ +│ ├── git_worker.py # QThread: pull, push master, push production, git rm, restore +│ ├── hugo_worker.py # QProcess: hugo server start/stop, streaming log +│ └── translation_worker.py # QProcess: transart.py, streaming stdout +├── core/ +│ ├── article_scanner.py # scansiona content/it/ e content/en/, costruisce ArticleModel +│ ├── frontmatter.py # parse/write TOML frontmatter (tomlkit) +│ └── config.py # percorsi configurabili +└── docs/superpowers/specs/ +``` + +**Configurazione:** `~/.config/my-publisher/config.toml` +```toml +blog_repo = "/home/danix/Programming/GIT/danix.xyz-hacker-theme" +transart_script = "/home/danix/bin/transart.py" +typora_bin = "typora" +``` +Prima apertura → dialog setup se config mancante. + +--- + +## Layout UI + +**Struttura:** sidebar sinistra con gruppi + area contenuto destra (QStackedWidget). + +### Sidebar + +``` +📰 my-publisher [🔄] + +CONTENUTO + 📋 Articoli + ⚠️ Senza Traduzione [4] + ➕ Nuovo articolo + 🏷 Tassonomia + 🖼 Media + +WORKFLOW + 🌍 Traduzioni + 🔧 Git ops + 🚀 Hugo server + +DEPLOY + 🧪 Test (master) + 🚢 Production +``` + +Icona 🔄 in cima → refresh manuale `article_scanner`. `QFileSystemWatcher` su `content/it/` e `content/en/` → re-scan silenzioso automatico su modifiche esterne (Typora, terminale). + +--- + +## Componenti + +### Lista Articoli (`articles_view`) + +Tab IT / EN. In ogni tab: lista articoli con badge stato traduzione. +- Articolo con traduzione: `🇬🇧 ✓` verde +- Articolo senza traduzione: `🇬🇧 ✗` rosso + bordo sinistro rosso + +Voce sidebar "Senza Traduzione" → lista cross-lingua di tutti gli articoli privi di traduzione, con badge "manca 🇬🇧" / "manca 🇮🇹" + pulsante "Traduci" diretto. + +Click articolo → emette `article_selected(Article)` → carica `article_detail`. + +### Pannello Dettaglio (`article_detail`) + +Layout: header + split colonne. + +**Header:** path articolo + stato traduzione + 5 pulsanti: +| Pulsante | Azione | +|---|---| +| ✏️ Typora | `subprocess.Popen(["typora", path])` | +| 🔧 Frontmatter | apre `frontmatter_editor` dialog | +| 🌍 Traduci | apre `translation_view` | +| 🧪 Push master | `git_worker` → push master | +| 🚢 Pubblica | `git_worker` → push production | + +**Colonna sinistra:** frontmatter leggibile (campi + valori) + bottone "Modifica frontmatter". + +**Colonna destra:** preview markdown (`QTextBrowser`, renderer Qt built-in). Shortcodes Hugo strippati via regex prima del render. + +### Editor Frontmatter (`frontmatter_editor`) + +Dialog modale. Form generato dinamicamente dai campi TOML dell'articolo. Salva con `tomlkit` per preservare commenti e formato originale. + +Il campo `type` è un `QComboBox` con i 5 tipi predefiniti del sito (non editabile liberamente): + +```python +ARTICLE_TYPES = ["Life", "Photo", "Link", "Quote", "Tech"] +``` + +Tutti gli altri campi sono field testuali liberi. Tags e categorie mostrano i valori esistenti come chip con possibilità di aggiungere/rimuovere — solo termini già presenti nella tassonomia sono accettati (autocompletamento dal file `tags-{lang}.txt`). + +### Vista Traduzione (`translation_view`) + +Sostituisce `article_detail` durante e dopo la traduzione. + +1. **In corso:** `QProcess` lancia `transart.py`, ogni riga stdout → append in `QPlainTextEdit` (log streaming in tempo reale). +2. **Completata:** preview markdown della traduzione + 4 pulsanti: + - ✏️ Apri in Typora + - 🔄 Rigenera traduzione + - 🧪 Push master + - 🚢 Pubblica + +### Git ops (`git_view`) + +Stato repo: branch corrente, `git status` (file modificati), `git log --oneline -5`. + +Pulsanti: Pull, Push master, Push production. Output in `QPlainTextEdit`. + +**Eliminazione articoli:** pulsante "Elimina" in `article_detail` → `git rm` + commit automatico `"remove: <slug> (<lang>)"`. Sezione "Articoli eliminati" in `git_view` → `git log --diff-filter=D --pretty=...` → pulsante "Ripristina" → `git checkout <hash> -- <path>`. + +Refresh automatico all'apertura della scheda. + +Le voci sidebar "🧪 Test (master)" e "🚢 Production" nel gruppo DEPLOY aprono `git_view` con focus sul branch corrispondente e pulsante azione primario evidenziato. Equivalenti ai pulsanti "Push master" / "Pubblica" nell'`article_detail` — shortcut rapidi senza dover aprire un articolo. + +### Tassonomia (`taxonomy_view`) + +Scheda dedicata nella sidebar sotto CONTENUTO. Gestisce tag e categorie in modo bilingue. + +**Visualizzazione:** due tab — Tags / Categorie. Ogni tab mostra lista di termini con colonne IT | EN. Evidenziati in rosso i termini privi di corrispondenza nell'altra lingua (non presenti in `docs/tags-it.txt` / `docs/tags-en.txt`). + +**Operazioni:** +- Aggiungere nuovo termine (IT + EN insieme) +- Modificare termine esistente +- Proposta traduzione automatica: pulsante "Traduci mancanti" → lancia `transart.py` o chiamata diretta all'API Ollama per tradurre i termini orfani in batch +- Salva → aggiorna `docs/tags-it.txt` e `docs/tags-en.txt` mantenendo ordine alfabetico e allineamento 1:1 tra i due file + +**Integrità:** `article_scanner` segnala nel dettaglio articolo se un tag usato non è presente nel file tassonomia. + +### Media (`media_view`) + +Upload file in `static/uppies/YYYY/MM/` (anno/mese corrente automatico). +- Drag & drop nella finestra +- Pulsante "Aggiungi file" → file dialog +- Post-copia: path relativo copiato in clipboard automaticamente (`/uppies/YYYY/MM/file.ext`) +- Lista file presenti nella cartella del mese corrente + +### Hugo Server (`hugo_panel`) + +`QProcess` per `hugo server -D`. Start/stop dalla sidebar con icona status (🟢 running / 🔴 stopped). Log accessibile nel pannello. URL `http://localhost:1313` cliccabile. + +--- + +## Modello Dati + +```python +@dataclass +class Article: + slug: str + lang: str # "it" | "en" + path: Path # path assoluto a index.md + frontmatter: dict + has_translation: bool + translation_path: Path | None +``` + +`ArticleModel` = lista di `Article`. `article_scanner` scansiona `content/{it,en}/articles/*/index.md`, costruisce pairs per slug, determina `has_translation`. + +```python +ARTICLE_TYPES = ["Life", "Photo", "Link", "Quote", "Tech"] +``` + +`TaxonomyModel`: dizionario `{term_it: term_en}` caricato da `docs/tags-it.txt` e `docs/tags-en.txt` (file allineati riga per riga). Stesso schema per categorie. + +--- + +## Gestione Errori + +Ogni worker emette `error(str)` → statusbar + dialog non-bloccante. Git push fallito → stderr visibile. Traduzione fallita → log completo + pulsante "Riprova". + +--- + +## Dipendenze Python + +``` +PyQt6 +tomlkit # parse/write TOML preservando formato +mistune # markdown → HTML per preview (fallback a QTextBrowser nativo) +``` + +--- + +## Verifica (test end-to-end) + +1. Avviare app → sidebar popolata con articoli del blog reale +2. Click articolo → frontmatter corretto + preview markdown visibile +3. Modificare frontmatter → salvare → rileggere file su disco con `tomlkit` +4. Aprire Typora → modificare articolo → tornare in app → refresh automatico aggiorna preview +5. Tradurre articolo → log streaming visibile → preview traduzione al termine +6. Push master → `git log` sul repo mostra commit +7. Upload media → file in `static/uppies/YYYY/MM/` → path in clipboard +8. Eliminare articolo → scompare dalla lista → ripristino da Git ops funziona |
