summaryrefslogtreecommitdiffstats
path: root/docs/superpowers/specs
diff options
context:
space:
mode:
Diffstat (limited to 'docs/superpowers/specs')
-rw-r--r--docs/superpowers/specs/2026-05-01-my-publisher-design.md223
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