- Текст del/add парой сливается в ОДИН блок (change=mod): словный LCS по токенам, плейсхолдеры U+E000/U+E001, рендер, подстановка цветных span только в текстовых узлах; ссылки/URL/код — fallback на два блока чтобы не рвать разметку
- Единые насыщенные цвета везде: rgba(244,63,94,.45)/rgba(34,197,94,.45) — html тинты, строки кода, слова, текстовый diff, легенда (hex с альфой в QML)
- Тесты: text merge, link fallback, цвета
- parseBlocksWithDiff: пара del/add кодовых блоков сливается в ОДИН блок (change=mod) с построчным LCS-diff внутри: удалённые строки — розовая подложка rgba(244,63,94,.45) с −, добавленные — зелёная rgba(34,197,94,.45) с +, неизменные — как были; копируется новое содержимое; fallback — два блока
- Цвета насыщеннее: html тинты #ffd6dd/#b7f5c6 (были бледные), текстовый diff тоже, легенда в тон; на тёмном пузыре светлый текст читается
- Тесты: merge в один блок, маркеры, цвета, copy-нового
- Подсветка: highlight() отдаёт <br/> вместо сырых \n (RichText их схлопывал — код был одной строкой); plainToRich для fallback-путей
- MdParser.parseBlocksWithDiff(old,new): поблочный LCS-diff с fence-aware разбивкой, change same/del/add, изменённые блоки в цветном div (красный/зелёный как в diff), codeId для копирования сохранены
- MarkdownView.compareMarkdown: при задании рендерит версию с подсветкой отличий
- История: Просмотр версии сравнивается со свежим содержимым с диска (workdirContent), легенда на обеих вкладках
- Тесты: <br/>, порядок del/add, тинты, identical без подсветки
- Вкладка Содержимое версии -> Просмотр версии: MarkdownView с markdown версии (vaultRoot/fileDir файла), как режим Просмотр в редакторе
- Ссылки в исторической версии только чтение: задачи/вики не трогаем, flora://open открывает в редакторе, внешние — в браузере, копирование кода с тостом Скопировано
- Легенда diff только на вкладке Изменения, emptyText для пустого состояния
- onWidth/HeightChanged теперь только clampPositions()+requestPaint, без force-симуляции и без сброса зума/панорамы/позиций
- nodes/edges меняются парой -> дебаунс 120мс (requestRelayout), добавлен onEdgesChanged
- relayout больше не трогает zoom/pan (resetView только по двойному клику), убран Math.random в пользу детерминированного сдвига, guard на нулевой размер
- CodeBlock: RowLayout для header (copy строго справа), MarkdownView в Column внутри ScrollView (availableWidth) — превью теперь скроллится, показывает все строки, код как ввёл (pre-wrap, tab 4)
- Vault: selectVault при выборе в переключателе + windowOpened -> setCurrentVault — запуск с последним выбранным, проверено на /Users/alex/Nextcloud/test и test-vault
- CodeBlock: <pre> white-space:pre-wrap + TextEdit RichText, без ScrollView, implicitHeight contentHeight+20 — весь код виден целиком, как ввёл, не в одну строку; только иконка 📋
- MainWindow: vaultSwitcher теперь vaultManager.selectVault(modelData) перед openWindow, main.cpp connect windowOpened -> setCurrentVault — запуск с последним выбранным, а не добавленным; проверено на /Users/alex/Nextcloud/test и test-vault
- Tests: vault_select_test + mdparser parseBlocks
- TextArea focus:true + Component.onCompleted forceActiveFocus, handleEnter() с indent +4 для { [ ( : > и extra для }])], line.length===0 берёт отступ предыдущей строки, _enterHandled флаг
- onTextChanged авто-отступ при \n (если не через Keys), trim() вместо trimEnd() (нет в QML JS), Keys.onPressed для Return/Enter Tab/Backspace/скобок, убран дубль onReturnPressed
- Проверено: ctest 21/21, offscreen/cocoa RUNNING OK, ручной тест ' hello' -> Enter -> ' hello\n '
- SplitView handle: implicit 12 (было 1/6), Rectangle fill parent + hit 16, cursor SplitH/VCursor, для файлового дерева/живого просмотра/графа-календаря — теперь легко попасть по всей высоте
- MonthCalendar: header 28x28 radius 14, month Label fill 70-110 + year 42/38, font 11, elide, spacing 4 — 'сентябрь 2026' не налезает, кнопки 28 круглые opaque (surface/hover/selected)
- RightPanel: удалён дубль 'Сегодня' ☷ из calBox header (оставлен только ⌂ в MonthCalendar)
- EditorPanel: Enter через Keys.onReturnPressed/onEnterPressed handleEnter() (сохранение indent), убран дубль в onPressed — теперь новая строка работает
- Убран ScrollView, теперь Rectangle + TextEdit с anchors.fill и implicitHeight: contentHeight+20 — весь код виден целиком, без колёсика
- Кнопка копирования: только 📋 (было '📋 Копировать'), при копировании ✓, hover/active Qt.rgba, размер под иконку
- TextEdit RichText Wrap, JetBrains Mono 13, selectByMouse, contentHeight
- Repeater delegate: убраны required property, заменены на property string blockType: model.type || "" и т.д. для html/code блоков (иначе QML Cannot create delegate когда у html нет codeId)
- parseBlocks тест в mdparser_test.cpp: проверка 3 блоков (html/code/html) и highlighted
- Убран console.log из MarkdownView::update
- GitManager: fileHistory (список версий с hash/time/message/author), fileContentAt, fileDiffHtml/Text (git_patch_from_buffers, цветной HTML), restoreFile (запись + автокоммит), lastModifiedTime/String, autoCommit, ensureGitignore (.flora/.stfolder)
- HistoryDialog.qml: список версий (время «только что/5 мин назад» + дата dd.MM.yyyy, сообщение, автор, shortHash), diff с легендой (зелёный +/красный -/синий заголовок), превью содержимого, кнопка «Восстановить эту версию» с подтверждением (объясняет что создастся новый коммит и можно вернуться вперёд), «Обновить/Закрыть»
- MainWindow: меню Правка → История файла (Ctrl+H) и Вернуться к предыдущей версии, FileTree ПКМ → История, передача vaultPath/filePath
- EditorPanel: индикатор «🕘» + lastModifiedStr (gitManager.lastModifiedTimeString), debounce autoCommit (2с) при documentSaved, shortcut Ctrl+H
- FileTreePanel: сигнал historyRequested
- CMake: добавлен HistoryDialog.qml в qml модуль
- Тесты: git_history_test — создание v1/v2, история 1→2, diff, восстановление назад к v1 (история 3), вперёд к v2 (4), v3, diff v1→v3, bad hash, other.md не влияет на историю note.md, autoCommit, lastModified, .gitignore; 21/21 зелёных
- Проверено вперёд/назад: v1→v2→восстановление v1→восстановление v2→v3→autoCommit, каждый шаг с проверкой содержимого и истории