Каталогизатор документов: разбор папки, тезисы, поиск, GUI

Программа принимает папку с документами, извлекает из них текст и
метаданные и строит каталог: краткое описание, тезисы и ключевые слова
для каждого файла плюс поиск по всему собранному.

Ядро:
- extract: PDF, DJVU, DOC(X), RTF, ODT, CHM, EPUB, FB2, PPT(X), XLS(X), TXT;
  PDF передаётся MuPDF потоком в память (работают длинные пути) и ограничен
  по времени — повреждённый файл иначе чинится минутами;
- summarize: экстрактивное реферирование по TF-IDF, посчитанному на самой
  библиотеке, с лёгким стеммером для русского и английского;
- db: SQLite с полнотекстовым индексом FTS5, морфологический поиск с BM25;
- scanner: инкрементальный многопоточный обход, счётчик попыток защищает
  от файла, обрывающего разбор;
- report: автономный HTML с поиском, Markdown, JSON, CSV.

Интерфейс:
- окно PySide6 в тёмной теме: вкладки разбора и поиска, фоновый поток,
  карточка документа, правка своих ключевых слов;
- командная строка: build, scan, analyze, report, search, show, tag, stats;
- у каждой папки свой каталог, последняя обработанная запоминается.

Сборка: scripts/build_exe.py даёт Catalogizer.exe в Windows и
Catalogizer.app в macOS, иконки .ico и .icns рисуются кодом.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-30 19:20:29 +03:00
commit 49f91247c7
30 changed files with 3934 additions and 0 deletions

140
catalogizer/ui/theme.py Normal file
View File

@@ -0,0 +1,140 @@
"""@file theme.py
@brief Единая спокойная тёмная тема каталогизатора без внешних ресурсов.
Палитра и правила оформления совпадают со стилем SETGUI, чтобы обе
программы выглядели частями одного инструментария.
"""
APP_STYLESHEET = """
QWidget {
background: #10151d;
color: #dce6f2;
font-family: "Segoe UI", "SF Pro Text", "Helvetica Neue", "Noto Sans",
sans-serif;
font-size: 10pt;
}
QMainWindow { background: #0d1219; }
QFrame#header {
background: #151d28;
border-bottom: 1px solid #263345;
}
QFrame[panel="true"] {
background: #151d28;
border: 1px solid #263345;
border-radius: 8px;
}
QLabel[muted="true"] { color: #8291a5; }
QLabel[title="true"] {
color: #f4f8fc;
font-size: 15pt;
font-weight: 600;
}
QLabel[section="true"] {
color: #f4f8fc;
font-size: 11pt;
font-weight: 600;
}
QPushButton {
background: #243247;
border: 1px solid #344762;
border-radius: 5px;
padding: 7px 14px;
}
QPushButton:hover { background: #2d405b; }
QPushButton:pressed { background: #1c293b; }
QPushButton:disabled { color: #627086; background: #192230; }
QPushButton[primary="true"] {
background: #1570d8;
border-color: #2584ec;
font-weight: 600;
}
QPushButton[primary="true"]:hover { background: #2584ec; }
QLineEdit, QComboBox, QSpinBox, QDoubleSpinBox {
background: #0d131c;
border: 1px solid #34445a;
border-radius: 5px;
padding: 6px;
selection-background-color: #1570d8;
}
QComboBox::drop-down { border: 0; width: 24px; }
QComboBox QAbstractItemView {
background: #0d131c;
border: 1px solid #34445a;
selection-background-color: #1570d8;
}
QTabWidget::pane {
border: 1px solid #263345;
background: #10151d;
}
QTabBar::tab {
background: #151d28;
color: #91a0b4;
border: 1px solid #263345;
padding: 9px 15px;
}
QTabBar::tab:selected {
color: #ffffff;
background: #1c2838;
border-bottom: 2px solid #2f91ff;
}
QHeaderView::section {
background: #1b2635;
color: #b9c5d5;
border: 0;
border-right: 1px solid #2b394c;
padding: 6px;
}
QTableWidget, QTreeWidget {
background: #0d131c;
alternate-background-color: #111a25;
gridline-color: #263345;
border: 1px solid #263345;
selection-background-color: #1570d8;
selection-color: #ffffff;
}
QTextBrowser, QPlainTextEdit {
background: #090e14;
color: #aabbd0;
border: 1px solid #263345;
font-family: "Cascadia Mono", "SF Mono", Menlo, "DejaVu Sans Mono",
monospace;
font-size: 9pt;
}
QProgressBar {
background: #0d131c;
border: 1px solid #34445a;
border-radius: 5px;
text-align: center;
}
QProgressBar::chunk { background: #1570d8; border-radius: 4px; }
QScrollBar:vertical {
background: #10151d;
width: 11px;
margin: 0;
}
QScrollBar::handle:vertical {
background: #34445a;
min-height: 24px;
border-radius: 5px;
}
QSplitter::handle { background: #1b2635; }
QStatusBar { background: #151d28; color: #8291a5; }
"""
#: Цвета карточки документа: HTML-разметка QTextBrowser темы не наследует.
CARD_CSS = """
body { color: #dce6f2;
font-family: "Segoe UI", "SF Pro Text", "Helvetica Neue", sans-serif;
font-size: 10pt; }
h2 { color: #f4f8fc; font-size: 12pt; margin: 0 0 4px 0; }
.facts { color: #8291a5; font-size: 9pt; margin-bottom: 10px; }
.desc { margin-bottom: 10px; }
.label { color: #2f91ff; font-weight: 600; }
li { margin-bottom: 4px; }
.kw { color: #b9c5d5; }
.mine { color: #2f91ff; font-weight: 600; }
.path { color: #627086;
font-family: "Cascadia Mono", "SF Mono", Menlo, monospace;
font-size: 8pt; }
mark { background: #1570d8; color: #ffffff; }
"""