mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-07-23 19:51:05 -04:00
feat: add support acp in common chat (#369)
This commit is contained in:
29
sources/ChatView/qml/chatparts/BlockPayload.js
Normal file
29
sources/ChatView/qml/chatparts/BlockPayload.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2026 Petr Mironychev
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
|
||||
|
||||
.pragma library
|
||||
|
||||
function parseMarkerPayload(marker, content) {
|
||||
if (!content || !content.startsWith(marker))
|
||||
return null;
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(content.substring(marker.length));
|
||||
return (parsed && typeof parsed === "object") ? parsed : null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function parseFileEdit(content) {
|
||||
return parseMarkerPayload("QODEASSIST_FILE_EDIT:", content);
|
||||
}
|
||||
|
||||
function parsePermission(content) {
|
||||
return parseMarkerPayload("QODEASSIST_PERMISSION:", content);
|
||||
}
|
||||
|
||||
function parsePlan(content) {
|
||||
return parseMarkerPayload("QODEASSIST_PLAN:", content);
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import QtQuick.Layouts
|
||||
import UIControls
|
||||
import ChatView
|
||||
import Qt.labs.platform as Platform
|
||||
import "BlockPayload.js" as BlockPayload
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
@@ -39,13 +40,11 @@ Rectangle {
|
||||
readonly property bool isArchived: editStatus === "archived"
|
||||
|
||||
readonly property color appliedColor: Qt.rgba(0.2, 0.8, 0.2, 0.8)
|
||||
readonly property color revertedColor: Qt.rgba(0.8, 0.6, 0.2, 0.8)
|
||||
readonly property color rejectedColor: Qt.rgba(0.8, 0.2, 0.2, 0.8)
|
||||
readonly property color archivedColor: Qt.rgba(0.5, 0.5, 0.5, 0.8)
|
||||
readonly property color pendingColor: palette.highlight
|
||||
|
||||
readonly property color appliedBgColor: Qt.rgba(0.2, 0.8, 0.2, 0.3)
|
||||
readonly property color revertedBgColor: Qt.rgba(0.8, 0.6, 0.2, 0.3)
|
||||
readonly property color rejectedBgColor: Qt.rgba(0.8, 0.2, 0.2, 0.3)
|
||||
readonly property color archivedBgColor: Qt.rgba(0.5, 0.5, 0.5, 0.3)
|
||||
|
||||
@@ -84,23 +83,15 @@ Rectangle {
|
||||
readonly property int removedLines: countLines(oldContent)
|
||||
|
||||
function parseEditData(content) {
|
||||
try {
|
||||
const marker = "QODEASSIST_FILE_EDIT:";
|
||||
let jsonStr = content;
|
||||
if (content.indexOf(marker) >= 0) {
|
||||
jsonStr = content.substring(content.indexOf(marker) + marker.length);
|
||||
}
|
||||
return JSON.parse(jsonStr);
|
||||
} catch (e) {
|
||||
return {
|
||||
edit_id: "",
|
||||
file: "",
|
||||
old_content: "",
|
||||
new_content: "",
|
||||
status: "error",
|
||||
status_message: ""
|
||||
};
|
||||
}
|
||||
const parsed = BlockPayload.parseFileEdit(content);
|
||||
return parsed !== null ? parsed : {
|
||||
edit_id: "",
|
||||
file: "",
|
||||
old_content: "",
|
||||
new_content: "",
|
||||
status: "error",
|
||||
status_message: ""
|
||||
};
|
||||
}
|
||||
|
||||
function getFileName(path) {
|
||||
|
||||
208
sources/ChatView/qml/chatparts/PermissionBlock.qml
Normal file
208
sources/ChatView/qml/chatparts/PermissionBlock.qml
Normal file
@@ -0,0 +1,208 @@
|
||||
// Copyright (C) 2026 Petr Mironychev
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import UIControls
|
||||
import "BlockPayload.js" as BlockPayload
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string permissionContent: ""
|
||||
|
||||
readonly property var permissionData: parsePermissionData(permissionContent)
|
||||
readonly property bool isMalformed: permissionData === null
|
||||
readonly property string requestId: isMalformed ? "" : (permissionData.requestId || "")
|
||||
readonly property string title: isMalformed
|
||||
? qsTr("Unreadable permission request")
|
||||
: (permissionData.title || qsTr("The agent asks for permission"))
|
||||
readonly property string toolKind: isMalformed ? "" : (permissionData.toolKind || "")
|
||||
readonly property var options: isMalformed ? [] : (permissionData.options || [])
|
||||
readonly property string status: isMalformed ? "error" : (permissionData.status || "pending")
|
||||
readonly property string selectedOptionId: isMalformed ? "" : (permissionData.selectedOptionId || "")
|
||||
readonly property bool automatic: !isMalformed && permissionData.automatic === true
|
||||
|
||||
readonly property bool isPending: status === "pending" && options.length > 0
|
||||
readonly property bool isCancelled: status === "cancelled"
|
||||
readonly property bool wasDeclinedUnpresentable: isCancelled && options.length === 0
|
||||
|
||||
readonly property var selectedOption: findOption(selectedOptionId)
|
||||
readonly property bool wasAllowed: selectedOption !== null
|
||||
&& selectedOption.allows === true
|
||||
|
||||
readonly property int borderRadius: 6
|
||||
readonly property int contentMargin: 10
|
||||
readonly property int badgeRadius: 3
|
||||
readonly property int badgePaddingH: 12
|
||||
readonly property int badgePaddingV: 6
|
||||
readonly property int titleMaxLines: 4
|
||||
readonly property int rowSpacing: 8
|
||||
|
||||
readonly property color allowedColor: Qt.rgba(0.2, 0.8, 0.2, 0.8)
|
||||
readonly property color deniedColor: Qt.rgba(0.8, 0.2, 0.2, 0.8)
|
||||
readonly property color cancelledColor: Qt.rgba(0.5, 0.5, 0.5, 0.8)
|
||||
|
||||
readonly property color statusColor: {
|
||||
if (isMalformed)
|
||||
return deniedColor;
|
||||
if (isPending)
|
||||
return palette.highlight;
|
||||
if (isCancelled)
|
||||
return cancelledColor;
|
||||
return wasAllowed ? allowedColor : deniedColor;
|
||||
}
|
||||
|
||||
readonly property string statusText: {
|
||||
if (isMalformed)
|
||||
return qsTr("UNREADABLE");
|
||||
if (isPending)
|
||||
return qsTr("WAITING FOR YOU");
|
||||
if (wasDeclinedUnpresentable)
|
||||
return qsTr("DECLINED");
|
||||
if (isCancelled)
|
||||
return qsTr("NO LONGER AVAILABLE");
|
||||
if (automatic)
|
||||
return wasAllowed ? qsTr("ALLOWED AUTOMATICALLY") : qsTr("DENIED AUTOMATICALLY");
|
||||
return wasAllowed ? qsTr("ALLOWED") : qsTr("DENIED");
|
||||
}
|
||||
|
||||
readonly property string explanation: {
|
||||
if (isMalformed)
|
||||
return qsTr("This permission record could not be read, so it cannot be answered.");
|
||||
if (wasDeclinedUnpresentable)
|
||||
return qsTr("The agent did not offer a set of options this chat could present safely, so the request was declined.");
|
||||
if (isCancelled)
|
||||
return qsTr("This request ended before it was answered.");
|
||||
if (isPending)
|
||||
return "";
|
||||
if (selectedOption === null)
|
||||
return "";
|
||||
return automatic
|
||||
? qsTr("Answered automatically with \"%1\", because you chose that for this action type for the rest of the conversation.").arg(selectedOption.name)
|
||||
: qsTr("You answered \"%1\".").arg(selectedOption.name);
|
||||
}
|
||||
|
||||
signal respond(string requestId, string optionId)
|
||||
|
||||
implicitHeight: layout.implicitHeight + 2 * contentMargin
|
||||
radius: borderRadius
|
||||
color: palette.base
|
||||
border.width: 1
|
||||
border.color: statusColor
|
||||
|
||||
function parsePermissionData(content) {
|
||||
return BlockPayload.parsePermission(content);
|
||||
}
|
||||
|
||||
function findOption(optionId) {
|
||||
if (!optionId)
|
||||
return null;
|
||||
for (let i = 0; i < root.options.length; ++i) {
|
||||
if (root.options[i].id === optionId)
|
||||
return root.options[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
margins: root.contentMargin
|
||||
}
|
||||
spacing: root.rowSpacing
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: root.rowSpacing
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: statusLabel.implicitWidth + root.badgePaddingH
|
||||
Layout.preferredHeight: statusLabel.implicitHeight + root.badgePaddingV
|
||||
Layout.alignment: Qt.AlignTop
|
||||
radius: root.badgeRadius
|
||||
color: root.statusColor
|
||||
|
||||
Text {
|
||||
id: statusLabel
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: root.statusText
|
||||
textFormat: Text.PlainText
|
||||
font.pixelSize: 10
|
||||
font.bold: true
|
||||
color: palette.base
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: root.title
|
||||
textFormat: Text.PlainText
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: palette.text
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: root.titleMaxLines
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
visible: root.toolKind.length > 0
|
||||
text: qsTr("Action type: %1").arg(root.toolKind)
|
||||
textFormat: Text.PlainText
|
||||
font.pixelSize: 11
|
||||
color: palette.placeholderText
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
visible: text.length > 0
|
||||
text: root.explanation
|
||||
textFormat: Text.PlainText
|
||||
font.pixelSize: 11
|
||||
color: palette.placeholderText
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Flow {
|
||||
Layout.fillWidth: true
|
||||
visible: root.isPending
|
||||
spacing: root.rowSpacing
|
||||
|
||||
Repeater {
|
||||
model: root.options
|
||||
|
||||
delegate: QoAButton {
|
||||
id: optionButton
|
||||
|
||||
required property var modelData
|
||||
|
||||
text: optionButton.modelData.name || optionButton.modelData.id
|
||||
accentColor: optionButton.modelData.allows === true
|
||||
? root.allowedColor
|
||||
: root.deniedColor
|
||||
|
||||
contentItem: Text {
|
||||
text: optionButton.text
|
||||
textFormat: Text.PlainText
|
||||
color: palette.buttonText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
onClicked: root.respond(root.requestId, optionButton.modelData.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
151
sources/ChatView/qml/chatparts/PlanBlock.qml
Normal file
151
sources/ChatView/qml/chatparts/PlanBlock.qml
Normal file
@@ -0,0 +1,151 @@
|
||||
// Copyright (C) 2026 Petr Mironychev
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import "BlockPayload.js" as BlockPayload
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string planContent: ""
|
||||
property bool expanded: true
|
||||
|
||||
readonly property var planData: parsePlanData(planContent)
|
||||
readonly property var entries: planData === null ? [] : (planData.entries || [])
|
||||
readonly property int completedCount: countCompleted()
|
||||
readonly property bool isComplete: entries.length > 0 && completedCount === entries.length
|
||||
|
||||
readonly property int borderRadius: 6
|
||||
readonly property int contentMargin: 10
|
||||
readonly property int rowSpacing: 6
|
||||
|
||||
readonly property color completedColor: Qt.rgba(0.2, 0.8, 0.2, 0.9)
|
||||
readonly property color activeColor: palette.highlight
|
||||
readonly property color pendingColor: palette.placeholderText
|
||||
|
||||
implicitHeight: layout.implicitHeight + 2 * contentMargin
|
||||
radius: borderRadius
|
||||
color: palette.base
|
||||
border.width: 1
|
||||
border.color: isComplete ? completedColor : palette.mid
|
||||
|
||||
function parsePlanData(content) {
|
||||
return BlockPayload.parsePlan(content);
|
||||
}
|
||||
|
||||
function countCompleted() {
|
||||
let done = 0;
|
||||
for (let i = 0; i < root.entries.length; ++i) {
|
||||
if (root.entries[i].status === "completed")
|
||||
++done;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
function entryColor(status) {
|
||||
if (status === "completed")
|
||||
return root.completedColor;
|
||||
if (status === "in_progress")
|
||||
return root.activeColor;
|
||||
return root.pendingColor;
|
||||
}
|
||||
|
||||
function entryMarker(status) {
|
||||
if (status === "completed")
|
||||
return "✓";
|
||||
if (status === "in_progress")
|
||||
return "▶";
|
||||
return "○";
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
margins: root.contentMargin
|
||||
}
|
||||
spacing: root.rowSpacing
|
||||
|
||||
MouseArea {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: header.implicitHeight
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.expanded = !root.expanded
|
||||
|
||||
RowLayout {
|
||||
id: header
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: root.rowSpacing
|
||||
|
||||
Text {
|
||||
text: qsTr("Plan")
|
||||
textFormat: Text.PlainText
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: palette.text
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("%1 of %2 done").arg(root.completedCount).arg(root.entries.length)
|
||||
textFormat: Text.PlainText
|
||||
font.pixelSize: 11
|
||||
color: palette.placeholderText
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.expanded ? "▼" : "▶"
|
||||
font.pixelSize: 10
|
||||
color: palette.mid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.expanded ? root.entries : []
|
||||
|
||||
delegate: RowLayout {
|
||||
id: entryRow
|
||||
|
||||
required property var modelData
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: root.rowSpacing
|
||||
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
text: root.entryMarker(entryRow.modelData.status)
|
||||
textFormat: Text.PlainText
|
||||
font.pixelSize: 12
|
||||
color: root.entryColor(entryRow.modelData.status)
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: entryRow.modelData.content || ""
|
||||
textFormat: Text.PlainText
|
||||
font.pixelSize: 12
|
||||
font.strikeout: entryRow.modelData.status === "completed"
|
||||
color: entryRow.modelData.status === "completed" ? palette.placeholderText
|
||||
: palette.text
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
visible: entryRow.modelData.priority === "high"
|
||||
text: qsTr("high")
|
||||
textFormat: Text.PlainText
|
||||
font.pixelSize: 10
|
||||
color: palette.highlight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,14 +8,52 @@ import Qt.labs.platform as Platform
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string toolContent: ""
|
||||
property string toolName: ""
|
||||
property string toolResult: ""
|
||||
property string toolKind: ""
|
||||
property string toolStatus: ""
|
||||
property var toolDetails: ({})
|
||||
property bool expanded: false
|
||||
|
||||
property alias headerOpacity: headerRow.opacity
|
||||
|
||||
readonly property int firstNewline: toolContent.indexOf('\n')
|
||||
readonly property string toolName: firstNewline > 0 ? toolContent.substring(0, firstNewline) : toolContent
|
||||
readonly property string toolResult: firstNewline > 0 ? toolContent.substring(firstNewline + 1) : ""
|
||||
readonly property int legacyNewline: toolName === "" ? toolResult.indexOf('\n') : -1
|
||||
readonly property string headerName: {
|
||||
if (toolName !== "")
|
||||
return toolName;
|
||||
return legacyNewline > 0 ? toolResult.substring(0, legacyNewline) : toolResult;
|
||||
}
|
||||
readonly property string resultText: {
|
||||
if (toolName !== "")
|
||||
return toolResult;
|
||||
return legacyNewline > 0 ? toolResult.substring(legacyNewline + 1) : "";
|
||||
}
|
||||
|
||||
readonly property var locations: (toolDetails && toolDetails.locations) ? toolDetails.locations : []
|
||||
readonly property var diffs: (toolDetails && toolDetails.diffs) ? toolDetails.diffs : []
|
||||
|
||||
readonly property bool isRunning: toolStatus === "pending" || toolStatus === "in_progress"
|
||||
readonly property bool hasFailed: toolStatus === "failed"
|
||||
|
||||
readonly property color statusColor: {
|
||||
if (hasFailed)
|
||||
return Qt.rgba(0.8, 0.2, 0.2, 0.9);
|
||||
if (isRunning)
|
||||
return palette.highlight;
|
||||
if (toolStatus === "completed")
|
||||
return Qt.rgba(0.2, 0.8, 0.2, 0.9);
|
||||
return palette.mid;
|
||||
}
|
||||
|
||||
readonly property string statusMarker: {
|
||||
if (hasFailed)
|
||||
return "✗";
|
||||
if (toolStatus === "completed")
|
||||
return "✓";
|
||||
if (isRunning)
|
||||
return "…";
|
||||
return "";
|
||||
}
|
||||
|
||||
radius: 6
|
||||
color: palette.base
|
||||
@@ -45,10 +83,26 @@ Rectangle {
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
text: qsTr("Tool: %1").arg(root.toolName)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.statusMarker
|
||||
textFormat: Text.PlainText
|
||||
visible: text.length > 0
|
||||
font.pixelSize: 12
|
||||
color: root.statusColor
|
||||
}
|
||||
|
||||
Text {
|
||||
id: headerTitle
|
||||
|
||||
width: headerRow.width - x - 30
|
||||
text: root.toolKind.length > 0
|
||||
? root.toolKind + ": " + root.headerName
|
||||
: qsTr("Tool: %1").arg(root.headerName)
|
||||
textFormat: Text.PlainText
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: palette.text
|
||||
color: root.hasFailed ? root.statusColor : palette.text
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Text {
|
||||
@@ -60,8 +114,8 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: contentColumn
|
||||
Loader {
|
||||
id: contentLoader
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
@@ -69,20 +123,88 @@ Rectangle {
|
||||
top: header.bottom
|
||||
margins: 10
|
||||
}
|
||||
spacing: 8
|
||||
active: root.expanded
|
||||
sourceComponent: contentComponent
|
||||
}
|
||||
|
||||
TextEdit {
|
||||
id: resultText
|
||||
Component {
|
||||
id: contentComponent
|
||||
|
||||
width: parent.width
|
||||
text: root.toolResult
|
||||
readOnly: true
|
||||
selectByMouse: true
|
||||
color: palette.text
|
||||
wrapMode: Text.WordWrap
|
||||
font.family: "monospace"
|
||||
font.pixelSize: 11
|
||||
selectionColor: palette.highlight
|
||||
Column {
|
||||
property alias resultEditor: resultEditorItem
|
||||
|
||||
spacing: 8
|
||||
|
||||
Repeater {
|
||||
model: root.locations
|
||||
|
||||
delegate: Text {
|
||||
id: locationEntry
|
||||
|
||||
required property var modelData
|
||||
|
||||
width: contentLoader.width
|
||||
text: locationEntry.modelData.line !== undefined
|
||||
? "→ " + locationEntry.modelData.path + ":" + locationEntry.modelData.line
|
||||
: "→ " + locationEntry.modelData.path
|
||||
textFormat: Text.PlainText
|
||||
color: palette.placeholderText
|
||||
font.pixelSize: 11
|
||||
elide: Text.ElideMiddle
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.diffs
|
||||
|
||||
delegate: Column {
|
||||
id: diffEntry
|
||||
|
||||
required property var modelData
|
||||
|
||||
width: contentLoader.width
|
||||
spacing: 2
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
text: qsTr("Diff") + ": " + (diffEntry.modelData.path || "")
|
||||
textFormat: Text.PlainText
|
||||
color: palette.text
|
||||
font.pixelSize: 11
|
||||
font.bold: true
|
||||
elide: Text.ElideMiddle
|
||||
}
|
||||
|
||||
TextEdit {
|
||||
width: parent.width
|
||||
text: (diffEntry.modelData.oldText || "") + "\n" + (diffEntry.modelData.newText || "")
|
||||
textFormat: TextEdit.PlainText
|
||||
readOnly: true
|
||||
selectByMouse: true
|
||||
color: palette.text
|
||||
wrapMode: Text.WordWrap
|
||||
font.family: "monospace"
|
||||
font.pixelSize: 11
|
||||
selectionColor: palette.highlight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextEdit {
|
||||
id: resultEditorItem
|
||||
|
||||
width: parent.width
|
||||
visible: text.length > 0
|
||||
text: root.resultText
|
||||
textFormat: TextEdit.PlainText
|
||||
readOnly: true
|
||||
selectByMouse: true
|
||||
color: palette.text
|
||||
wrapMode: Text.WordWrap
|
||||
font.family: "monospace"
|
||||
font.pixelSize: 11
|
||||
selectionColor: palette.highlight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,14 +220,14 @@ Rectangle {
|
||||
|
||||
Platform.MenuItem {
|
||||
text: qsTr("Copy")
|
||||
enabled: resultText.selectedText.length > 0
|
||||
onTriggered: resultText.copy()
|
||||
enabled: contentLoader.item && contentLoader.item.resultEditor.selectedText.length > 0
|
||||
onTriggered: contentLoader.item.resultEditor.copy()
|
||||
}
|
||||
|
||||
Platform.MenuItem {
|
||||
text: qsTr("Select All")
|
||||
enabled: resultText.text.length > 0
|
||||
onTriggered: resultText.selectAll()
|
||||
enabled: contentLoader.item && contentLoader.item.resultEditor.text.length > 0
|
||||
onTriggered: contentLoader.item.resultEditor.selectAll()
|
||||
}
|
||||
|
||||
Platform.MenuSeparator {}
|
||||
@@ -140,7 +262,7 @@ Rectangle {
|
||||
when: root.expanded
|
||||
PropertyChanges {
|
||||
target: root
|
||||
implicitHeight: header.height + contentColumn.height + 20
|
||||
implicitHeight: header.height + contentLoader.height + 20
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user