Files
Flora/PLAN.md
T
2026-08-16 23:32:12 +03:00

240 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PLAN — Текстовый редактор/заметочник «Flora»
Минималистичный markdown-заметочник с git-историей, синхронизацией и графом связей.
Аналог Obsidian, но лёгкий: быстрый запуск на любом железе, мгновенное открытие заметок.
## Технологии
| Компонент | Решение |
|---|---|
| Язык | C++17 |
| UI | Qt 6.8 LTS, Qt Quick / QML |
| Сборка | CMake |
| Markdown-парсер | cmark-gfm (CommonMark + GFM) |
| Git | libgit2 (с поддержкой SSH через libssh2 + OpenSSL) |
| SSH | libssh2 + OpenSSL (генерация ключей, known_hosts) |
| Подсветка кода | KSyntaxHighlighting (KDE, LGPL — совместимо с GPL-3.0) |
| БД | SQLite (Qt SQL) — метаданные, теги, связи, свойства |
| Diff | libgit2 diff между blob версии и текущим файлом |
| PDF | QtPdf (предпросмотр) + QPdfWriter (экспорт) |
| Лицензия | GPL-3.0 |
Целевые платформы: macOS, Windows, Linux, Android.
## Архитектура
```
Flora/
├── CMakeLists.txt, LICENSE (GPL-3.0), README, .gitignore
├── src/
│ ├── main.cpp
│ ├── app/ (VaultManager, WindowController)
│ ├── core/ (FileStore, TreeModel, DocumentManager, SettingsStore)
│ ├── git/GitManager.{h,cpp} # libgit2
│ ├── markdown/ (MdParser@cmark-gfm, HtmlRenderer, SyntaxHighlight)
│ ├── db/Database.{h,cpp} # SQLite: теги, связи, свойства
│ ├── editor/ (DiffRenderer)
│ ├── sync/ (SyncManager, WebDavSync, SyncthingClient, GitHubSync,
│ │ SshManager, ErrorCatalog)
│ └── export/ (HtmlExporter, PdfExporter)
├── qml/
│ ├── MainWindow.qml, VaultWindow.qml
│ ├── wizard/WelcomeWizard.qml, ProviderSetupGuide.qml, SshSetupGuide.qml
│ ├── panels/ FileTreePanel, TabsBar, EditorPanel, FormatToolbar,
│ │ CalendarPanel, LinksTagsPanel, GraphPanel, TableEditor
│ └── dialogs/ NameDialog, ConfirmDialog, HistoryDiffDialog, SyncDialog,
│ VaultSwitcherPopup
├── third_party/ cmark/ libgit2/ libssh2/ openssl/ ksyntaxhighlighting/
├── tests/ (QtTest + Catch2)
└── packaging/ (macOS / Windows / Linux / Android)
```
## Функциональные требования (общие)
- Левая панель (файловое дерево) — 15% экрана, ширина меняется мышью; центральная — 70%;
правая — 15%. Размеры панелей запоминаются между сессиями.
- Автосохранение файла каждые 5 секунд.
- Хранилище = локальная папка, внутри которой git-репозиторий (создаётся автоматически).
- Все сохранения — автоматически, авто-коммиты в git.
- Редактор: режимы «редактирование» и «просмотр» (рендер как HTML в браузере).
- Файлы любого формата открываются корректно: md — редактор, html/pdf — предпросмотр.
## Фазы
### Фаза 0 — Каркас
1. Репозиторий, LICENSE (GPL-3.0), README, .gitignore.
2. Подключение cmark, libgit2 (с SSH), libssh2, OpenSSL, KSyntaxHighlighting как подпроектов
(статическая сборка).
3. Скелет: главное меню вверху (Файл / Правка / Вид / Справка), SplitView 15/70/15,
кнопка «+ хранилище» внизу слева.
### Фаза 1 — Первый запуск (мастер)
4. WelcomeWizard: выбор локальной папки (новая или с файлами) → предложение подключить
GitHub/GitLab/Gitea, Nextcloud/WebDAV, Syncthing → git-репозиторий создаётся
автоматически → открытие хранилища. Выбор запоминается.
5. Подключение git-провайдера — два режима на выбор:
- HTTPS + токен (через ProviderSetupGuide);
- SSH-ключ (через SshSetupGuide) — если токен создать невозможно.
Подключение можно пропустить; позже доступно через кнопку хранилища (внизу слева) →
«Настройки хранилища» → «Подключить синхронизацию».
#### 5.1. HTTPS + токен (ProviderSetupGuide)
Пошаговая инструкция для каждого провайдера:
- Шаг 1 — создать токен: точная ссылка на страницу токенов и необходимые права:
- GitHub: github.com/settings/tokens (Fine-grained: `Contents: Read and write` / классический scope `repo`);
- GitLab: Настройки → Access Tokens (scope `api` или `read_repository`+`write_repository`);
- Gitea: Настройки → Приложения → Personal Access Token (scope `repo`).
- Шаг 2 — создать репозиторий (пустой, без README/.gitignore — иначе конфликт истории).
- Шаг 3 — для self-hosted GitLab/Gitea: URL инстанса (включая http://localhost:3000), пометка про HTTPS.
- Шаг 4 — вставить токен, кнопка «Проверить соединение» (реальный API-запрос: проверка
токена и доступности репо), после успеха — авто-push и настройка автосинка.
- Кнопки: «Скопировать команды для терминала» (git remote add/push вручную),
«Назад/Пропустить».
#### 5.2. SSH-ключ (SshSetupGuide)
- Использовать существующий ключ (путь + опц. passphrase, хранится в системном keychain /
настройках с предупреждением) ИЛИ сгенерировать новый в приложении
(ed25519/RSA через OpenSSL, работает и на Android, без ssh-keygen).
- Добавить публичный ключ в аккаунт: инструкция + кнопка «Скопировать публичный ключ»:
- GitHub: Settings → SSH and GPG keys;
- GitLab: Preferences → SSH Keys;
- Gitea: Настройки → SSH-ключи.
- Параметры: хост (github.com, gitlab.com, custom), порт (22/другой), имя пользователя
(`git` по умолчанию), строка подключения вида `git@github.com:user/repo.git`.
- «Проверить соединение»: реальное подключение через libssh2; проверка host key:
при первом подключении показать отпечаток и предложить «Доверять и сохранить»
(known_hosts в `.flora/ssh/`); при несовпадении — блокировка с предупреждением «Возможен MITM».
- Успех → авто-push и настройка автосинка.
### Фаза 2 — Мульти-хранилища (несколько окон)
6. VaultManager: список известных хранилищ в настройках; последнее открытое = основное
(открывается при старте).
7. Кнопка внизу слева → поповер со списком хранилищ + «Добавить новое»: новое хранилище
открывается в новом окне, старое продолжает работать; закрытие окна не удаляет
хранилище; после перезапуска открывается только новое (основное), к старым — возврат
через ту же кнопку.
### Фаза 3 — Хранилище и файловое дерево
8. FileStore + QFileSystemWatcher, TreeModel для QML TreeView: подпапки с отступами,
стрелка «>» раскрытия, иконки пустой/заполненной папки и файлов по типу.
9. Кнопка «+» над деревом и ПКМ по пустому месту → меню «Файл»/«Папка» → диалог имени.
Переименование (F2/ПКМ), удаление (файл/пустая папка — одно подтверждение, непустая
папка — двойное), перемещение drag&drop между папками.
10. Двойной клик по файлу — открыть в новой вкладке (md — в редакторе, прочие — предпросмотром).
### Фаза 4 — Редактор, вкладки, автосохранение
11. DocumentManager: список открытых файлов, QTimer автосохранения каждые 5 сек
(только если есть изменения), восстановление открытых вкладок после перезапуска.
12. TabsBar с вкладками, закрытием, режимом «открыть рядом»: центральная область — две
колонки SplitView, в каждую открывается вкладка (одна активна в каждой колонке).
13. Режим редактирования: TextArea + моноширинный шрифт, путь/название файла в заголовке
вкладки. Переключение режимов — Ctrl+E / Ctrl+V.
### Фаза 5 — Панель форматирования (подменю вверху)
14. FormatToolbar над редактором, видна в режиме редактирования:
- Жирный, курсив, подчёркнутый, зачёркнутый, выделение (==mark==), цвет текста,
цвет выделения (палитры), верхний/нижний индекс (возведение в квадрат, подстрочное);
- Заголовки # 1–6 (выпадающий выбор);
- Списки: маркированный / нумерованный / задачи `- [ ]`;
- Цитата (`>`), Выноска (GFM `> [!NOTE/INFO/WARN/ERROR]`);
- Таблица: по умолчанию 2×1; в режиме редактирования таблицы по краям «+»-кнопки
справа и снизу (20px шириной), добавляют столбец/строку, раскладка растёт
по вертикали/горизонтали;
- Обтекание текста вокруг картинок и таблиц: вставка директивы
`![[img.png|float:left|width:40%]]`, рендер в обтекаемый блок.
Кнопки работают с выделенным текстом (вставка/оборачивание markdown-разметки).
### Фаза 6 — Просмотр Markdown
15. MdParser на cmark-gfm: CommonMark + GFM-таблицы, зачёркивание, автоссылки, выноски,
raw-HTML, [[wiki-ссылки]], директивы обтекания.
16. Подсветка кода: KSyntaxHighlighting — язык из ограждения (таблица алиасов: «С», «C»,
«С++», «C++», python, go, rust, js, …; при отсутствии — авто по расширению/дефолт).
Подсветка в режиме просмотра; в редакторе — по расширению файла через слоёный Text
(фоновый слой rich-текст + прозрачный TextArea сверху), обновление по таймеру-дебонсу.
17. Задачи: `- [ ]`/`- [x]` рендерятся чекбоксами; клик в просмотре переключает галочку
в исходнике и перерисовывает.
18. Ссылки в просмотре: клик по [[wiki]]/обычной ссылке открывает файл в новой вкладке.
### Фаза 7 — Правая панель: календарь, ссылки и теги, граф
19. Календарь: клик по дню создаёт/открывает Журнал/ГГГГ-ММ-ДД.md (папка журнала настраивается).
20. «Ссылки и теги» (секция над графом, для текущего файла): все ссылки и теги файла;
клик по ссылке → переход в файл; клик по тегу → список всех файлов с этим тегом,
клик по файлу → открыть.
21. Граф связей (Canvas): узлы-файлы и теги-кластеры, рёбра по ссылкам/тегам/связям из БД,
простая симуляция отталкивания/притяжения на JS, drag узлов, клик — открыть файл,
кнопка «Обновить».
### Фаза 8 — База данных (notion-подобные связи)
22. Database (SQLite, файл `.flora/flora.db` в хранилище): таблицы files, tags, file_tags,
links, properties (свойства страниц: приоритет, статус, даты, связи), relations
(родитель/ребёнок).
23. Синхронизация БД с файловой системой при каждом изменении. UI: диалог
«Свойства и связи» файла (ПКМ → Свойства), поиск по свойствам, backlinks.
Граф дополнительно рисует связи из БД.
### Фаза 9 — Git: история и откат
24. GitManager: репозиторий создаётся автоматически при создании хранилища, авто-коммиты,
.gitignore (.flora/, .stfolder, конфликт-копии).
25. Меню Правка → «Вернуться к предыдущей версии»: для открытого файла список версий →
diff-просмотр (строки «+»/«−» цветные, контекст), внизу кнопки «Применить» и «Отклонить».
Применение — замена файла на выбранную версию (через git) + автокоммит отката.
### Фаза 10 — Экспорт и предпросмотр
26. Меню Файл → Экспорт: HTML (картинки инлайн base64 или копией, с обтеканием)
и PDF (HTML → QTextDocument → QPdfWriter).
27. Предпросмотр .html (тот же QTextDocument-рендер, без JS) и .pdf (QtPdf) из дерева.
### Фаза 11 — Синхронизация
28. SyncManager (период в настройках):
- Nextcloud/WebDAV: загрузка/скачивание дерева, сравнение mtime+hash, конфликты →
имя.conflict.время.md, авторизация по логину/паролю (сохранение токена);
- GitHub/GitLab/Gitea: git push/pull по HTTPS (токен) или SSH, merge конфликтов;
- Syncthing: REST API (localhost:8384): сканирование папки, статус устройств, кнопка
«Синхронизировать». Файлы .stfolder игнорируются git-ом.
Подключение новых провайдеров — через кнопку хранилища (внизу слева) →
«Настройки хранилища».
#### Каталог ошибок (ErrorCatalog)
Каждой ошибке API/libgit2/SSH соответствует человеческое сообщение и рекомендация:
| Ошибка | Причина | Что показать пользователю |
|---|---|---|
| 401 Unauthorized | Неверный/просроченный/отозванный токен | «Токен не принят. Создайте новый» + ссылка на страницу токенов |
| 403 Forbidden | Недостаточно прав токена / rate limit | «Проверьте права токена (scope repo/api)»; при rate limit — «Подождите N минут» |
| 404 Not Found | Неверный URL инстанса или репо не существует / нет доступа (private) | «Проверьте адрес и что репозиторий создан», кнопка «Создать пустой репозиторий» (шаг 2) |
| 409 / non-fast-forward | В репозитории уже есть чужие коммиты | «Отключите «Синхронизировать» и выполните ручное слияние» + команды терминала |
| TLS/SSL (self-signed) | Self-hosted инстанс без сертификата | «Добавьте сертификат в доверенные» / опция «Разрешить небезопасное соединение» с предупреждением |
| Таймаут / нет сети | Недоступен хост, прокси, DNS | «Проверьте интернет и прокси», повтор с ретраями |
| Двухфакторка | GitHub требует OTP при пароле | «Используйте токен вместо пароля» |
| Пустой локальный репо | Хранилище ещё без коммитов | Авто-коммит инициализационный перед push |
| `Permission denied (publickey)` (SSH) | Ключ не добавлен в аккаунт / не тот ключ | «Добавьте публичный ключ в аккаунт» + ссылка на страницу ключей |
| Passphrase неверна (SSH) | Неверная парольная фраза | «Введите passphrase заново» |
| Host key mismatch (SSH) | Сервер подменился / ключ поменялся | «Подключение заблокировано: отпечаток изменился» + оба отпечатка, кнопка «Удалить старый и продолжить» (с предупреждением) |
| Таймаут/отказ порта (SSH) | Неверный порт/хост, файрвол, репо не существует | «Проверьте хост, порт и имя репозитория» |
| Неверный формат ключа (SSH) | Ключ повреждён/не тот формат | «Поддерживаются ed25519, RSA (PKCS#1/PKCS#8)» |
Ошибка не блокирует работу: всё хранится локально, подключение можно повторить позже.
### Фаза 12 — Настройки
29. QSettings: размеры панелей (запоминание), шрифты, интервалы автосохранения/автокоммита,
тема (светлая/тёмная), токены, основное хранилище. Горячие клавиши.
### Фаза 13 — Тесты
30. QtTest/Catch2: md→HTML (таблицы, выноски, обтекание, задачи), подсветка языков
(алиасы), GitManager (коммиты, откат, diff), Database, конфликты синка. Прогон через CTest.
### Фаза 14 — Android
31. Сборка cmark/libgit2/libssh2/OpenSSL/KSyntaxHighlighting под NDK (статически; libgit2
с SSH-транспортом). Манифест, разрешения, выбор хранилища системным диалогом,
ПКМ → долгое нажатие, адаптация панелей под экран телефона. SSH-ключи — в приватном
хранилище приложения.
### Фаза 15 — Упаковка и лицензия
32. Пакеты: macOS .app/.dmg, Windows NSIS, Linux AppImage, Android APK.
33. Лицензионные заголовки GPL-3.0 во всех файлах, README, чек-лист соответствия лицензии.
## Служебные файлы проекта
- PLAN.md — этот план (актуальная версия).
- CHANGELOG.md — история изменений проекта.
- done.md — журнал итераций: что планировалось за итерацию и что сделано.