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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
from __future__ import annotations
from PyQt6.QtWidgets import (
QMainWindow, QWidget, QHBoxLayout, QVBoxLayout,
QLabel, QPushButton, QStackedWidget, QFrame,
)
from PyQt6.QtCore import Qt, QFileSystemWatcher
from pathlib import Path
from core.config import Config
from core.article_scanner import scan_articles
from core.models import Article
class SidebarButton(QPushButton):
def __init__(self, text: str, parent=None):
super().__init__(text, parent)
self.setFlat(True)
self.setCheckable(True)
self.setStyleSheet("""
QPushButton { text-align: left; padding: 5px 10px; border-radius: 4px; color: #888; }
QPushButton:checked { background: #2a2a4e; color: #a855f7; }
QPushButton:hover:!checked { background: #1a1a2e; color: #ccc; }
""")
class MainWindow(QMainWindow):
def __init__(self, config: Config, parent=None):
super().__init__(parent)
self.config = config
self._articles: list[Article] = []
self._watcher = QFileSystemWatcher(self)
self._watcher.directoryChanged.connect(self._on_fs_change)
self.setWindowTitle("my-publisher")
self.setMinimumSize(1100, 700)
self._build_ui()
self._refresh_articles()
self._setup_watcher()
def _build_ui(self):
central = QWidget()
self.setCentralWidget(central)
root = QHBoxLayout(central)
root.setContentsMargins(0, 0, 0, 0)
root.setSpacing(0)
# Sidebar
sidebar = self._build_sidebar()
root.addWidget(sidebar)
# Divider
line = QFrame()
line.setFrameShape(QFrame.Shape.VLine)
line.setStyleSheet("color: #2a2a4e;")
root.addWidget(line)
# Content stack
self._stack = QStackedWidget()
root.addWidget(self._stack, stretch=1)
# Placeholder pages (replaced in later tasks)
for name in ["articles", "no_translation", "new_article", "taxonomy",
"media", "translations", "git", "hugo"]:
w = QLabel(f"[{name}]")
w.setAlignment(Qt.AlignmentFlag.AlignCenter)
self._stack.addWidget(w)
setattr(self, f"_page_{name}", self._stack.count() - 1)
def _build_sidebar(self) -> QWidget:
w = QWidget()
w.setFixedWidth(210)
w.setStyleSheet("background: #1a1a2e;")
layout = QVBoxLayout(w)
layout.setContentsMargins(8, 12, 8, 12)
layout.setSpacing(2)
header = QHBoxLayout()
title = QLabel("๐ฐ my-publisher")
title.setStyleSheet("color: #fff; font-weight: bold; font-size: 13px; padding: 4px 8px;")
header.addWidget(title)
self._refresh_btn = QPushButton("๐")
self._refresh_btn.setFlat(True)
self._refresh_btn.setFixedSize(28, 28)
self._refresh_btn.setStyleSheet("color: #555; border: none;")
self._refresh_btn.clicked.connect(self._refresh_articles)
header.addWidget(self._refresh_btn)
layout.addLayout(header)
def section(text):
lbl = QLabel(text)
lbl.setStyleSheet("color: #a855f7; font-size: 9px; text-transform: uppercase; letter-spacing: 1px; padding: 8px 8px 2px;")
layout.addWidget(lbl)
self._btn_group: list[SidebarButton] = []
def btn(icon_text: str, page_attr: str) -> SidebarButton:
b = SidebarButton(icon_text)
b.clicked.connect(lambda: self._show_page(page_attr, b))
layout.addWidget(b)
self._btn_group.append(b)
return b
section("CONTENUTO")
btn("๐ Articoli", "_page_articles")
no_trans_row = QHBoxLayout()
no_trans_row.setContentsMargins(0, 0, 0, 0)
self._btn_no_trans = SidebarButton("โ ๏ธ Senza Traduzione")
self._btn_no_trans.clicked.connect(lambda: self._show_page("_page_no_translation", self._btn_no_trans))
self._btn_group.append(self._btn_no_trans)
no_trans_row.addWidget(self._btn_no_trans, stretch=1)
self._badge_no_trans = QLabel("0")
self._badge_no_trans.setStyleSheet("background:#3a1a1a;color:#ff6b6b;border-radius:8px;padding:1px 6px;font-size:10px;")
no_trans_row.addWidget(self._badge_no_trans)
layout.addLayout(no_trans_row)
btn("โ Nuovo articolo", "_page_new_article")
btn("๐ท Tassonomia", "_page_taxonomy")
btn("๐ผ Media", "_page_media")
section("WORKFLOW")
btn("๐ Traduzioni", "_page_translations")
btn("๐ง Git ops", "_page_git")
btn("๐ Hugo server", "_page_hugo")
section("DEPLOY")
btn("๐งช Test (master)", "_page_git")
btn("๐ข Production", "_page_git")
layout.addStretch()
return w
def _show_page(self, page_attr: str, active_btn: SidebarButton):
for b in self._btn_group:
b.setChecked(False)
active_btn.setChecked(True)
self._stack.setCurrentIndex(getattr(self, page_attr))
def _refresh_articles(self):
if not self.config.blog_repo:
return
self._articles = scan_articles(Path(self.config.blog_repo))
missing = [a for a in self._articles if not a.has_translation]
self._badge_no_trans.setText(str(len(missing)))
def _setup_watcher(self):
if not self.config.blog_repo:
return
root = Path(self.config.blog_repo)
for lang in ("it", "en"):
d = root / "content" / lang / "articles"
if d.exists():
self._watcher.addPath(str(d))
def _on_fs_change(self, _path: str):
self._refresh_articles()
|