Блоки кода: отдельный компонент CodeBlock + MarkdownView (Telegram-пузырь)
- CodeBlock.qml: отдельный Rectangle #2e4d6a radius 12, header с языком и кнопкой 📋 Копировать (hover rgba 0.22/active 0.30, border, radius 11), код в rgba(0,0,0,0.22) radius 8, JetBrains Mono 13/tab 4, syntax highlight тёмной темой, selectByMouse
- MarkdownView.qml: ScrollView+Column+Repeater, parseBlocks() разбивает markdown на html/code блоки, html через Text RichText, code через CodeBlock, copyRequested → mdParser.copyToClipboard
- MdParser: parseBlocks() regex ```lang\n.*?\n``` + render для html частей, codeId/highlighted, highlightedCode(), highlight(...,true) forceDark, QTime удалён, QRegularExpression добавлен
- EditorPanel: viewArea/livePreview теперь MarkdownView (markdown property), liveDebounce/markdown, onLinkActivated → handleLink, viewMode переключает markdown
- CMake: добавлены CodeBlock.qml/MarkdownView.qml в qml модуль
- Исправлены qmllint: rgba → Qt.rgba, lineHeight убран, model.type → required property
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Flora
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string markdown: ""
|
||||
property string vaultRoot: ""
|
||||
property string fileDir: ""
|
||||
implicitHeight: column.implicitHeight + 24
|
||||
|
||||
signal linkActivated(string href)
|
||||
|
||||
// Для обновления
|
||||
function update() {
|
||||
// Триггерим пересборку блоков
|
||||
blocksModel.clear()
|
||||
const blocks = mdParser.parseBlocks(markdown, vaultRoot, fileDir)
|
||||
for (let i = 0; i < blocks.length; i++) {
|
||||
blocksModel.append(blocks[i])
|
||||
}
|
||||
// Позиция скролла в начало
|
||||
// contentItem.contentY = 0
|
||||
}
|
||||
|
||||
onMarkdownChanged: update()
|
||||
onVaultRootChanged: update()
|
||||
onFileDirChanged: update()
|
||||
|
||||
Component.onCompleted: update()
|
||||
|
||||
ListModel { id: blocksModel }
|
||||
|
||||
Column {
|
||||
id: column
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
// padding via anchors
|
||||
anchors.fill: parent
|
||||
anchors.margins: 12
|
||||
|
||||
Repeater {
|
||||
model: blocksModel
|
||||
delegate: Item {
|
||||
required property int index
|
||||
required property string type
|
||||
required property string html
|
||||
required property string code
|
||||
required property string language
|
||||
required property string codeId
|
||||
required property string highlighted
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
// HTML block
|
||||
Text {
|
||||
visible: type === "html" && html
|
||||
width: parent.width
|
||||
text: html || ""
|
||||
textFormat: Text.RichText
|
||||
wrapMode: Text.Wrap
|
||||
color: Theme.text
|
||||
font.family: "Menlo, Consolas, 'DejaVu Sans Mono', monospace"
|
||||
font.pixelSize: 14
|
||||
onLinkActivated: (href) => root.linkActivated(href)
|
||||
}
|
||||
// Code block - separate component
|
||||
CodeBlock {
|
||||
visible: type === "code"
|
||||
width: parent.width
|
||||
code: code || ""
|
||||
language: language || "text"
|
||||
codeId: codeId || ""
|
||||
highlightedText: highlighted || ""
|
||||
onCopyRequested: (id) => {
|
||||
const txt = mdParser.getCodeBlock(id)
|
||||
if (txt.length > 0) {
|
||||
mdParser.copyToClipboard(txt)
|
||||
root.linkActivated("flora://copycode/" + id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: markdown.trim().length === 0 ? qsTr("Пустой файл — начните писать в режиме редактирования") : ""
|
||||
color: Theme.textFaint
|
||||
wrapMode: Text.Wrap
|
||||
font.italic: true
|
||||
visible: markdown.trim().length === 0 && blocksModel.count === 0
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user