История: diff прямо в Просмотре версии + код не в одну строку

- Подсветка: 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 без подсветки
This commit is contained in:
2026-09-06 19:26:53 +03:00
parent 3d04ac0095
commit beb1eb2956
7 changed files with 307 additions and 12 deletions
+15 -6
View File
@@ -30,6 +30,7 @@ Dialog {
property string selectedHash: ""
property string diffHtml: ""
property string versionContent: ""
property string currentContent: ""
property bool busy: false
function fileDirOf(p) {
@@ -59,6 +60,8 @@ Dialog {
function reload() {
if (vaultPath.length===0 || filePath.length===0) {
historyModel = []
versionContent = ""
currentContent = ""
diffHtml = "<p style='color:" + Theme.textFaint + "'>" + qsTr("Файл не выбран") + "</p>"
return
}
@@ -75,6 +78,7 @@ Dialog {
selectedIndex = -1
selectedHash = ""
versionContent = ""
currentContent = ""
const hasFile = filePath.length>0
if (hasFile) {
const st = gitManager.fileGitStatus(vaultPath, filePath)
@@ -94,12 +98,16 @@ Dialog {
if (selectedHash.length===0) {
diffHtml = ""
versionContent = ""
currentContent = ""
return
}
// свежее содержимое с диска — diff всегда актуален, даже если файл правили при открытом диалоге
currentContent = gitManager.workdirContent(filePath)
const html = gitManager.fileDiffHtml(vaultPath, filePath, selectedHash)
if (html.length===0) diffHtml = "<p style='color:" + Theme.textFaint + "'>" + qsTr("Нет изменений") + "</p>"
else diffHtml = html
// содержимое версии — сырой markdown, отрисуется через MarkdownView как в «Просмотре»
// содержимое версии — сырой markdown, отрисуется через MarkdownView как в «Просмотре»,
// с подсветкой отличий от текущего файла прямо там
versionContent = gitManager.fileContentAt(vaultPath, filePath, selectedHash)
}
@@ -342,6 +350,7 @@ Dialog {
id: versionPreview
width: parent.width
markdown: root.versionContent
compareMarkdown: root.currentContent
vaultRoot: root.vaultPath
fileDir: root.fileDirOf(root.filePath)
emptyText: qsTr("Нет версии для просмотра")
@@ -352,18 +361,18 @@ Dialog {
}
}
// Легенда — только для вкладки «Изменения»
// Легенда — цвета отличий от текущего файла (обе вкладки)
Row {
visible: tabBar.currentIndex === 0
Layout.fillWidth: true
Layout.preferredHeight: tabBar.currentIndex === 0 ? implicitHeight : 0
Layout.margins: 6
spacing: 12
Row { spacing: 4; Rectangle { width:12; height:12; color:"#e6ffec"; border.color:"#1a7f37"; radius:2; anchors.verticalCenter: parent.verticalCenter } Label { text: qsTr("добавлено"); color: Theme.textMuted; font.pixelSize:10 } }
Row { spacing: 4; Rectangle { width:12; height:12; color:"#ffebe9"; border.color:"#cf222e"; radius:2; anchors.verticalCenter: parent.verticalCenter } Label { text: qsTr("удалено"); color: Theme.textMuted; font.pixelSize:10 } }
Row { spacing: 4; Rectangle { width:12; height:12; color:"#ddf4ff"; border.color:"#0969da"; radius:2; anchors.verticalCenter: parent.verticalCenter } Label { text: qsTr("заголовок"); color: Theme.textMuted; font.pixelSize:10 } }
Row { visible: tabBar.currentIndex === 0; spacing: 4; Rectangle { width:12; height:12; color:"#ddf4ff"; border.color:"#0969da"; radius:2; anchors.verticalCenter: parent.verticalCenter } Label { text: qsTr("заголовок"); color: Theme.textMuted; font.pixelSize:10 } }
Label {
text: qsTr("Сравнение выбранной версии с текущим файлом")
text: tabBar.currentIndex === 0
? qsTr("Сравнение выбранной версии с текущим файлом")
: qsTr("Версия с подсветкой отличий от текущего файла")
color: Theme.textFaint
font.pixelSize: 10
Layout.fillWidth: true
+7 -1
View File
@@ -9,6 +9,9 @@ Item {
property string vaultRoot: ""
property string fileDir: ""
property string emptyText: qsTr("Пустой файл — начните писать в режиме редактирования")
// Если задан — markdown считается старой версией и подсвечиваются отличия от него
// (блоки del — только в markdown, add — только в compareMarkdown)
property string compareMarkdown: ""
implicitHeight: column.implicitHeight + 24
signal linkActivated(string href)
@@ -17,7 +20,9 @@ Item {
function update() {
// Триггерим пересборку блоков
blocksModel.clear()
const blocks = mdParser.parseBlocks(markdown, vaultRoot, fileDir)
const blocks = compareMarkdown.length > 0
? mdParser.parseBlocksWithDiff(markdown, compareMarkdown, vaultRoot, fileDir)
: mdParser.parseBlocks(markdown, vaultRoot, fileDir)
for (let i = 0; i < blocks.length; i++) {
blocksModel.append(blocks[i])
}
@@ -26,6 +31,7 @@ Item {
}
onMarkdownChanged: update()
onCompareMarkdownChanged: update()
onVaultRootChanged: update()
onFileDirChanged: update()