mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2025-09-06 19:10:15 +00:00
Remove binary state from high-level API and use Jinja templates (#3147)
Signed-off-by: Jared Van Bortel <jared@nomic.ai> Signed-off-by: Adam Treat <treat.adam@gmail.com> Co-authored-by: Adam Treat <treat.adam@gmail.com>
This commit is contained in:
@@ -10,7 +10,7 @@ import network
|
||||
import llm
|
||||
|
||||
MySettingsTab {
|
||||
onRestoreDefaultsClicked: {
|
||||
onRestoreDefaults: {
|
||||
MySettings.restoreApplicationDefaults();
|
||||
}
|
||||
title: qsTr("Application")
|
||||
@@ -486,23 +486,6 @@ MySettingsTab {
|
||||
Accessible.name: nThreadsLabel.text
|
||||
Accessible.description: ToolTip.text
|
||||
}
|
||||
MySettingsLabel {
|
||||
id: saveChatsContextLabel
|
||||
text: qsTr("Save Chat Context")
|
||||
helpText: qsTr("Save the chat model's state to disk for faster loading. WARNING: Uses ~2GB per chat.")
|
||||
Layout.row: 12
|
||||
Layout.column: 0
|
||||
}
|
||||
MyCheckBox {
|
||||
id: saveChatsContextBox
|
||||
Layout.row: 12
|
||||
Layout.column: 2
|
||||
Layout.alignment: Qt.AlignRight
|
||||
checked: MySettings.saveChatsContext
|
||||
onClicked: {
|
||||
MySettings.saveChatsContext = !MySettings.saveChatsContext
|
||||
}
|
||||
}
|
||||
MySettingsLabel {
|
||||
id: trayLabel
|
||||
text: qsTr("Enable System Tray")
|
||||
|
@@ -8,8 +8,23 @@ import QtQuick.Layouts
|
||||
import gpt4all
|
||||
import mysettings
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
property var inputBoxText: null
|
||||
signal setInputBoxText(text: string)
|
||||
|
||||
Item {
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: parent.width
|
||||
Layout.preferredHeight: gridLayout.height
|
||||
|
||||
HoverHandler { id: hoverArea }
|
||||
|
||||
GridLayout {
|
||||
rows: 5
|
||||
id: gridLayout
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
columns: 2
|
||||
|
||||
Item {
|
||||
@@ -40,7 +55,7 @@ GridLayout {
|
||||
to: 360
|
||||
duration: 1000
|
||||
loops: Animation.Infinite
|
||||
running: currentResponse && (currentChat.responseInProgress || currentChat.restoringFromText)
|
||||
running: isCurrentResponse && currentChat.responseInProgress
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,13 +88,11 @@ GridLayout {
|
||||
color: theme.mutedTextColor
|
||||
}
|
||||
RowLayout {
|
||||
visible: currentResponse && ((value === "" && currentChat.responseInProgress) || currentChat.restoringFromText)
|
||||
visible: isCurrentResponse && (value === "" && currentChat.responseInProgress)
|
||||
Text {
|
||||
color: theme.mutedTextColor
|
||||
font.pixelSize: theme.fontSizeLarger
|
||||
text: {
|
||||
if (currentChat.restoringFromText)
|
||||
return qsTr("restoring from text ...");
|
||||
switch (currentChat.responseState) {
|
||||
case Chat.ResponseStopped: return qsTr("response stopped ...");
|
||||
case Chat.LocalDocsRetrieval: return qsTr("retrieving localdocs: %1 ...").arg(currentChat.collectionList.join(", "));
|
||||
@@ -99,10 +112,11 @@ GridLayout {
|
||||
Layout.row: 1
|
||||
Layout.column: 1
|
||||
Layout.fillWidth: true
|
||||
spacing: 20
|
||||
spacing: 10
|
||||
Flow {
|
||||
id: attachedUrlsFlow
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 10
|
||||
spacing: 10
|
||||
visible: promptAttachments.length !== 0
|
||||
Repeater {
|
||||
@@ -156,7 +170,7 @@ GridLayout {
|
||||
focus: false
|
||||
readOnly: true
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
cursorVisible: currentResponse ? currentChat.responseInProgress : false
|
||||
cursorVisible: isCurrentResponse ? currentChat.responseInProgress : false
|
||||
cursorPosition: text.length
|
||||
TapHandler {
|
||||
id: tapHandler
|
||||
@@ -183,12 +197,12 @@ GridLayout {
|
||||
}
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
if (!currentResponse || !currentChat.responseInProgress)
|
||||
if (!isCurrentResponse || !currentChat.responseInProgress)
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
|
||||
onLinkHovered: function (link) {
|
||||
if (!currentResponse || !currentChat.responseInProgress)
|
||||
if (!isCurrentResponse || !currentChat.responseInProgress)
|
||||
statusBar.externalHoveredLink = link
|
||||
}
|
||||
|
||||
@@ -239,13 +253,19 @@ GridLayout {
|
||||
textProcessor.setValue(value);
|
||||
}
|
||||
|
||||
property bool textProcessorReady: false
|
||||
|
||||
Component.onCompleted: {
|
||||
resetChatViewTextProcessor();
|
||||
chatModel.valueChanged.connect(function(i, value) {
|
||||
if (index === i)
|
||||
textProcessorReady = true;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: chatModel
|
||||
function onValueChanged(i, value) {
|
||||
if (myTextArea.textProcessorReady && index === i)
|
||||
textProcessor.setValue(value);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Connections {
|
||||
@@ -282,67 +302,6 @@ GridLayout {
|
||||
Network.sendConversation(currentChat.id, getConversationJson());
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.rightMargin: 15
|
||||
visible: name === "Response: " &&
|
||||
(!currentResponse || !currentChat.responseInProgress) && MySettings.networkIsActive
|
||||
spacing: 10
|
||||
|
||||
Item {
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
MyToolButton {
|
||||
id: thumbsUp
|
||||
width: 24
|
||||
height: 24
|
||||
imageWidth: width
|
||||
imageHeight: height
|
||||
opacity: thumbsUpState || thumbsUpState == thumbsDownState ? 1.0 : 0.2
|
||||
source: "qrc:/gpt4all/icons/thumbs_up.svg"
|
||||
Accessible.name: qsTr("Thumbs up")
|
||||
Accessible.description: qsTr("Gives a thumbs up to the response")
|
||||
onClicked: {
|
||||
if (thumbsUpState && !thumbsDownState)
|
||||
return
|
||||
|
||||
chatModel.updateNewResponse(index, "")
|
||||
chatModel.updateThumbsUpState(index, true)
|
||||
chatModel.updateThumbsDownState(index, false)
|
||||
Network.sendConversation(currentChat.id, getConversationJson());
|
||||
}
|
||||
}
|
||||
|
||||
MyToolButton {
|
||||
id: thumbsDown
|
||||
anchors.top: thumbsUp.top
|
||||
anchors.topMargin: 3
|
||||
anchors.left: thumbsUp.right
|
||||
anchors.leftMargin: 3
|
||||
width: 24
|
||||
height: 24
|
||||
imageWidth: width
|
||||
imageHeight: height
|
||||
checked: thumbsDownState
|
||||
opacity: thumbsDownState || thumbsUpState == thumbsDownState ? 1.0 : 0.2
|
||||
transform: [
|
||||
Matrix4x4 {
|
||||
matrix: Qt.matrix4x4(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
|
||||
},
|
||||
Translate {
|
||||
x: thumbsDown.width
|
||||
}
|
||||
]
|
||||
source: "qrc:/gpt4all/icons/thumbs_down.svg"
|
||||
Accessible.name: qsTr("Thumbs down")
|
||||
Accessible.description: qsTr("Opens thumbs down dialog")
|
||||
onClicked: {
|
||||
thumbsDownDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
@@ -353,11 +312,13 @@ GridLayout {
|
||||
Layout.preferredWidth: childrenRect.width
|
||||
Layout.preferredHeight: childrenRect.height
|
||||
visible: {
|
||||
if (name !== "Response: ")
|
||||
return false
|
||||
if (consolidatedSources.length === 0)
|
||||
return false
|
||||
if (!MySettings.localDocsShowReferences)
|
||||
return false
|
||||
if (currentResponse && currentChat.responseInProgress
|
||||
if (isCurrentResponse && currentChat.responseInProgress
|
||||
&& currentChat.responseState !== Chat.GeneratingQuestions )
|
||||
return false
|
||||
return true
|
||||
@@ -443,7 +404,7 @@ GridLayout {
|
||||
return false
|
||||
if (!MySettings.localDocsShowReferences)
|
||||
return false
|
||||
if (currentResponse && currentChat.responseInProgress
|
||||
if (isCurrentResponse && currentChat.responseInProgress
|
||||
&& currentChat.responseState !== Chat.GeneratingQuestions )
|
||||
return false
|
||||
return true
|
||||
@@ -566,8 +527,139 @@ GridLayout {
|
||||
}
|
||||
}
|
||||
|
||||
ConfirmationDialog {
|
||||
id: editPromptDialog
|
||||
dialogTitle: qsTr("Edit this prompt?")
|
||||
description: qsTr("The existing response and all later messages will be permanently erased.")
|
||||
onAccepted: {
|
||||
const msg = currentChat.popPrompt(index);
|
||||
if (msg !== null)
|
||||
setInputBoxText(msg);
|
||||
}
|
||||
}
|
||||
|
||||
ConfirmationDialog {
|
||||
id: redoResponseDialog
|
||||
dialogTitle: qsTr("Redo this response?")
|
||||
description: qsTr("The existing response and all later messages will be permanently erased.")
|
||||
onAccepted: currentChat.regenerateResponse(index)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: buttonRow
|
||||
Layout.row: 4
|
||||
Layout.column: 1
|
||||
Layout.maximumWidth: parent.width
|
||||
Layout.fillWidth: false
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
spacing: 3
|
||||
visible: !isCurrentResponse || !currentChat.responseInProgress
|
||||
enabled: opacity > 0
|
||||
opacity: hoverArea.hovered
|
||||
readonly property var canModify: !currentChat.isServer && currentChat.isModelLoaded && !currentChat.responseInProgress
|
||||
|
||||
Behavior on opacity {
|
||||
OpacityAnimator { duration: 30 }
|
||||
}
|
||||
|
||||
ChatMessageButton {
|
||||
visible: parent.canModify && model.name === "Prompt: "
|
||||
Layout.maximumWidth: 24
|
||||
Layout.maximumHeight: 24
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillWidth: false
|
||||
source: "qrc:/gpt4all/icons/edit.svg"
|
||||
onClicked: {
|
||||
if (inputBoxText === "")
|
||||
editPromptDialog.open();
|
||||
}
|
||||
name: qsTr("Edit")
|
||||
}
|
||||
|
||||
ChatMessageButton {
|
||||
visible: parent.canModify && model.name === "Response: "
|
||||
Layout.maximumWidth: 24
|
||||
Layout.maximumHeight: 24
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillWidth: false
|
||||
name: qsTr("Redo")
|
||||
source: "qrc:/gpt4all/icons/regenerate.svg"
|
||||
onClicked: redoResponseDialog.open()
|
||||
}
|
||||
|
||||
ChatMessageButton {
|
||||
Layout.maximumWidth: 24
|
||||
Layout.maximumHeight: 24
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillWidth: false
|
||||
name: qsTr("Copy")
|
||||
source: "qrc:/gpt4all/icons/copy.svg"
|
||||
onClicked: {
|
||||
myTextArea.selectAll();
|
||||
myTextArea.copy();
|
||||
myTextArea.deselect();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
visible: name === "Response: " && MySettings.networkIsActive
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.preferredWidth: childrenRect.width
|
||||
Layout.preferredHeight: childrenRect.height
|
||||
Layout.fillWidth: false
|
||||
|
||||
ChatMessageButton {
|
||||
id: thumbsUp
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: thumbsUpState || thumbsUpState == thumbsDownState ? 1.0 : 0.2
|
||||
source: "qrc:/gpt4all/icons/thumbs_up.svg"
|
||||
name: qsTr("Like response")
|
||||
onClicked: {
|
||||
if (thumbsUpState && !thumbsDownState)
|
||||
return
|
||||
|
||||
chatModel.updateNewResponse(index, "")
|
||||
chatModel.updateThumbsUpState(index, true)
|
||||
chatModel.updateThumbsDownState(index, false)
|
||||
Network.sendConversation(currentChat.id, getConversationJson());
|
||||
}
|
||||
}
|
||||
|
||||
ChatMessageButton {
|
||||
id: thumbsDown
|
||||
anchors.top: thumbsUp.top
|
||||
anchors.topMargin: buttonRow.spacing
|
||||
anchors.left: thumbsUp.right
|
||||
anchors.leftMargin: buttonRow.spacing
|
||||
checked: thumbsDownState
|
||||
opacity: thumbsDownState || thumbsUpState == thumbsDownState ? 1.0 : 0.2
|
||||
bgTransform: [
|
||||
Matrix4x4 {
|
||||
matrix: Qt.matrix4x4(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
|
||||
},
|
||||
Translate {
|
||||
x: thumbsDown.width
|
||||
}
|
||||
]
|
||||
source: "qrc:/gpt4all/icons/thumbs_down.svg"
|
||||
name: qsTr("Dislike response")
|
||||
onClicked: {
|
||||
thumbsDownDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // GridLayout
|
||||
|
||||
} // Item
|
||||
|
||||
GridLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: parent.width
|
||||
|
||||
function shouldShowSuggestions() {
|
||||
if (!currentResponse)
|
||||
if (!isCurrentResponse)
|
||||
return false;
|
||||
if (MySettings.suggestionMode === 2) // Off
|
||||
return false;
|
||||
@@ -577,8 +669,8 @@ GridLayout {
|
||||
}
|
||||
|
||||
Item {
|
||||
visible: shouldShowSuggestions()
|
||||
Layout.row: 4
|
||||
visible: parent.shouldShowSuggestions()
|
||||
Layout.row: 5
|
||||
Layout.column: 0
|
||||
Layout.topMargin: 20
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
@@ -601,8 +693,8 @@ GridLayout {
|
||||
}
|
||||
|
||||
Item {
|
||||
visible: shouldShowSuggestions()
|
||||
Layout.row: 4
|
||||
visible: parent.shouldShowSuggestions()
|
||||
Layout.row: 5
|
||||
Layout.column: 1
|
||||
Layout.topMargin: 20
|
||||
Layout.fillWidth: true
|
||||
@@ -627,8 +719,8 @@ GridLayout {
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
visible: shouldShowSuggestions()
|
||||
Layout.row: 5
|
||||
visible: parent.shouldShowSuggestions()
|
||||
Layout.row: 6
|
||||
Layout.column: 1
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: 1
|
||||
@@ -786,4 +878,7 @@ GridLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // GridLayout
|
||||
|
||||
} // ColumnLayout
|
||||
|
20
gpt4all-chat/qml/ChatMessageButton.qml
Normal file
20
gpt4all-chat/qml/ChatMessageButton.qml
Normal file
@@ -0,0 +1,20 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
import gpt4all
|
||||
|
||||
MyToolButton {
|
||||
property string name
|
||||
|
||||
width: 24
|
||||
height: 24
|
||||
imageWidth: width
|
||||
imageHeight: height
|
||||
ToolTip {
|
||||
visible: parent.hovered
|
||||
y: parent.height * 1.5
|
||||
text: name
|
||||
delay: Qt.styleHints.mousePressAndHoldInterval
|
||||
}
|
||||
Accessible.name: name
|
||||
}
|
@@ -24,6 +24,12 @@ Rectangle {
|
||||
|
||||
property var currentChat: ChatListModel.currentChat
|
||||
property var chatModel: currentChat.chatModel
|
||||
property var currentModelInfo: currentChat && currentChat.modelInfo
|
||||
property var currentModelId: null
|
||||
onCurrentModelInfoChanged: {
|
||||
const newId = currentModelInfo && currentModelInfo.id;
|
||||
if (currentModelId !== newId) { currentModelId = newId; }
|
||||
}
|
||||
signal addCollectionViewRequested()
|
||||
signal addModelViewRequested()
|
||||
|
||||
@@ -79,14 +85,11 @@ Rectangle {
|
||||
function open_(msg) { message = msg; open(); }
|
||||
}
|
||||
|
||||
SwitchModelDialog {
|
||||
ConfirmationDialog {
|
||||
id: switchModelDialog
|
||||
anchors.centerIn: parent
|
||||
Item {
|
||||
Accessible.role: Accessible.Dialog
|
||||
Accessible.name: qsTr("Switch model dialog")
|
||||
Accessible.description: qsTr("Warn the user if they switch models, then context will be erased")
|
||||
}
|
||||
property int index: -1
|
||||
dialogTitle: qsTr("Erase conversation?")
|
||||
description: qsTr("Changing the model will erase the current conversation.")
|
||||
}
|
||||
|
||||
PopupDialog {
|
||||
@@ -103,6 +106,16 @@ Rectangle {
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
}
|
||||
|
||||
ConfirmationDialog {
|
||||
id: resetContextDialog
|
||||
dialogTitle: qsTr("Erase conversation?")
|
||||
description: qsTr("The entire chat will be erased.")
|
||||
onAccepted: {
|
||||
Network.trackChatEvent("reset_context", { "length": chatModel.count });
|
||||
currentChat.reset();
|
||||
}
|
||||
}
|
||||
|
||||
function getConversation() {
|
||||
var conversation = "";
|
||||
for (var i = 0; i < chatModel.count; i++) {
|
||||
@@ -703,7 +716,7 @@ Rectangle {
|
||||
if (i !== -1) {
|
||||
defaultModel = comboBox.valueAt(i);
|
||||
} else {
|
||||
defaultModel = comboBox.valueAt(0);
|
||||
defaultModel = comboBox.count ? comboBox.valueAt(0) : "";
|
||||
}
|
||||
if (defaultModel !== "") {
|
||||
defaultModelName = ModelList.modelInfo(defaultModel).name;
|
||||
@@ -790,9 +803,9 @@ Rectangle {
|
||||
Layout.leftMargin: 50
|
||||
Layout.rightMargin: 50
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: 25
|
||||
spacing: 10
|
||||
model: chatModel
|
||||
cacheBuffer: Math.max(0, listView.contentHeight)
|
||||
cacheBuffer: 2147483647
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
policy: ScrollBar.AsNeeded
|
||||
@@ -804,6 +817,12 @@ Rectangle {
|
||||
|
||||
delegate: ChatItemView {
|
||||
width: listView.contentItem.width - 15
|
||||
inputBoxText: textInput.text
|
||||
onSetInputBoxText: text => {
|
||||
textInput.text = text;
|
||||
textInput.forceActiveFocus();
|
||||
textInput.cursorPosition = text.length;
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToEnd() {
|
||||
@@ -832,11 +851,9 @@ Rectangle {
|
||||
clip: true
|
||||
z: 400
|
||||
|
||||
property bool isHovered: {
|
||||
return conversationTrayButton.isHovered ||
|
||||
resetContextButton.hovered || copyChatButton.hovered ||
|
||||
regenerateButton.hovered
|
||||
}
|
||||
property bool isHovered: (
|
||||
conversationTrayButton.isHovered || resetContextButton.hovered || copyChatButton.hovered
|
||||
)
|
||||
|
||||
state: conversationTrayContent.isHovered ? "expanded" : "collapsed"
|
||||
states: [
|
||||
@@ -892,11 +909,7 @@ Rectangle {
|
||||
source: "qrc:/gpt4all/icons/recycle.svg"
|
||||
imageWidth: 20
|
||||
imageHeight: 20
|
||||
onClicked: {
|
||||
Network.trackChatEvent("reset_context", { "length": chatModel.count })
|
||||
currentChat.reset();
|
||||
currentChat.processSystemPrompt();
|
||||
}
|
||||
onClicked: resetContextDialog.open()
|
||||
ToolTip.visible: resetContextButton.hovered
|
||||
ToolTip.text: qsTr("Erase and reset chat session")
|
||||
}
|
||||
@@ -921,34 +934,6 @@ Rectangle {
|
||||
ToolTip.visible: copyChatButton.hovered
|
||||
ToolTip.text: qsTr("Copy chat session to clipboard")
|
||||
}
|
||||
MyToolButton {
|
||||
id: regenerateButton
|
||||
Layout.preferredWidth: 40
|
||||
Layout.preferredHeight: 40
|
||||
source: "qrc:/gpt4all/icons/regenerate.svg"
|
||||
imageWidth: 20
|
||||
imageHeight: 20
|
||||
visible: chatModel.count && !currentChat.isServer && currentChat.isModelLoaded && !currentChat.responseInProgress
|
||||
onClicked: {
|
||||
if (chatModel.count < 2)
|
||||
return
|
||||
var promptIndex = chatModel.count - 2
|
||||
var promptElement = chatModel.get(promptIndex)
|
||||
var responseIndex = chatModel.count - 1
|
||||
var responseElement = chatModel.get(responseIndex)
|
||||
if (promptElement.name !== "Prompt: " || responseElement.name !== "Response: ")
|
||||
return
|
||||
currentChat.regenerateResponse()
|
||||
chatModel.updateCurrentResponse(responseIndex, true)
|
||||
chatModel.updateStopped(responseIndex, false)
|
||||
chatModel.updateThumbsUpState(responseIndex, false)
|
||||
chatModel.updateThumbsDownState(responseIndex, false)
|
||||
chatModel.updateNewResponse(responseIndex, "")
|
||||
currentChat.prompt(promptElement.promptPlusAttachments)
|
||||
}
|
||||
ToolTip.visible: regenerateButton.hovered
|
||||
ToolTip.text: qsTr("Redo last chat response")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1026,13 +1011,15 @@ Rectangle {
|
||||
anchors.leftMargin: 30
|
||||
horizontalAlignment: Qt.AlignRight
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
color: theme.mutedTextColor
|
||||
visible: currentChat.tokenSpeed !== "" || externalHoveredLink !== ""
|
||||
color: textInputView.error !== null ? theme.textErrorColor : theme.mutedTextColor
|
||||
visible: currentChat.tokenSpeed !== "" || externalHoveredLink !== "" || textInputView.error !== null
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.WordWrap
|
||||
text: {
|
||||
if (externalHoveredLink !== "")
|
||||
return externalHoveredLink
|
||||
if (textInputView.error !== null)
|
||||
return textInputView.error;
|
||||
|
||||
const segments = [currentChat.tokenSpeed];
|
||||
const device = currentChat.device;
|
||||
@@ -1050,6 +1037,7 @@ Rectangle {
|
||||
}
|
||||
font.pixelSize: theme.fontSizeSmaller
|
||||
font.bold: true
|
||||
onLinkActivated: function(link) { Qt.openUrlExternally(link) }
|
||||
}
|
||||
|
||||
RectangularGlow {
|
||||
@@ -1079,8 +1067,8 @@ Rectangle {
|
||||
Rectangle {
|
||||
id: textInputView
|
||||
color: theme.controlBackground
|
||||
border.width: 1
|
||||
border.color: theme.controlBorder
|
||||
border.width: error === null ? 1 : 2
|
||||
border.color: error === null ? theme.controlBorder : theme.textErrorColor
|
||||
radius: 10
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
@@ -1091,6 +1079,41 @@ Rectangle {
|
||||
height: textInputViewLayout.implicitHeight
|
||||
visible: !currentChat.isServer && ModelList.selectableModels.count !== 0
|
||||
|
||||
property var error: null
|
||||
function checkError() {
|
||||
const info = currentModelInfo;
|
||||
if (info === null || !info.id) {
|
||||
error = null;
|
||||
} else if (info.chatTemplate.isLegacy) {
|
||||
error = qsTr("Legacy prompt template needs to be " +
|
||||
"<a href=\"https://docs.gpt4all.io/gpt4all_desktop/chat_templates.html\">updated" +
|
||||
"</a> in Settings.");
|
||||
} else if (!info.chatTemplate.isSet) {
|
||||
error = qsTr("No <a href=\"https://docs.gpt4all.io/gpt4all_desktop/chat_templates.html\">" +
|
||||
"chat template</a> configured.");
|
||||
} else if (/^\s*$/.test(info.chatTemplate.value)) {
|
||||
error = qsTr("The <a href=\"https://docs.gpt4all.io/gpt4all_desktop/chat_templates.html\">" +
|
||||
"chat template</a> cannot be blank.");
|
||||
} else if (info.systemMessage.isLegacy) {
|
||||
error = qsTr("Legacy system prompt needs to be " +
|
||||
"<a href=\"https://docs.gpt4all.io/gpt4all_desktop/chat_templates.html\">updated" +
|
||||
"</a> in Settings.");
|
||||
} else
|
||||
error = null;
|
||||
}
|
||||
Component.onCompleted: checkError()
|
||||
Connections {
|
||||
target: window
|
||||
function onCurrentModelIdChanged() { textInputView.checkError(); }
|
||||
}
|
||||
Connections {
|
||||
target: MySettings
|
||||
function onChatTemplateChanged(info)
|
||||
{ if (info.id === window.currentModelId) textInputView.checkError(); }
|
||||
function onSystemMessageChanged(info)
|
||||
{ if (info.id === window.currentModelId) textInputView.checkError(); }
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: textInputViewMouseArea
|
||||
anchors.fill: parent
|
||||
@@ -1214,16 +1237,16 @@ Rectangle {
|
||||
Accessible.role: Accessible.EditableText
|
||||
Accessible.name: placeholderText
|
||||
Accessible.description: qsTr("Send messages/prompts to the model")
|
||||
Keys.onReturnPressed: (event)=> {
|
||||
if (event.modifiers & Qt.ControlModifier || event.modifiers & Qt.ShiftModifier)
|
||||
event.accepted = false;
|
||||
else {
|
||||
editingFinished();
|
||||
sendMessage()
|
||||
}
|
||||
}
|
||||
Keys.onReturnPressed: event => {
|
||||
if (event.modifiers & Qt.ControlModifier || event.modifiers & Qt.ShiftModifier) {
|
||||
event.accepted = false;
|
||||
} else if (!chatModel.hasError && textInputView.error === null) {
|
||||
editingFinished();
|
||||
sendMessage();
|
||||
}
|
||||
}
|
||||
function sendMessage() {
|
||||
if ((textInput.text === "" && attachmentModel.count === 0) || currentChat.responseInProgress || currentChat.restoringFromText)
|
||||
if ((textInput.text === "" && attachmentModel.count === 0) || currentChat.responseInProgress)
|
||||
return
|
||||
|
||||
currentChat.stopGenerating()
|
||||
@@ -1338,6 +1361,7 @@ Rectangle {
|
||||
imageWidth: theme.fontSizeLargest
|
||||
imageHeight: theme.fontSizeLargest
|
||||
visible: !currentChat.responseInProgress && !currentChat.isServer && ModelList.selectableModels.count !== 0
|
||||
enabled: !chatModel.hasError && textInputView.error === null
|
||||
source: "qrc:/gpt4all/icons/send_message.svg"
|
||||
Accessible.name: qsTr("Send message")
|
||||
Accessible.description: qsTr("Sends the message/prompt contained in textfield to the model")
|
||||
|
59
gpt4all-chat/qml/ConfirmationDialog.qml
Normal file
59
gpt4all-chat/qml/ConfirmationDialog.qml
Normal file
@@ -0,0 +1,59 @@
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Controls.Basic
|
||||
import QtQuick.Layouts
|
||||
|
||||
MyDialog {
|
||||
id: confirmationDialog
|
||||
anchors.centerIn: parent
|
||||
modal: true
|
||||
padding: 20
|
||||
property alias dialogTitle: titleText.text
|
||||
property alias description: descriptionText.text
|
||||
|
||||
Theme { id: theme }
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
Text {
|
||||
id: titleText
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
textFormat: Text.StyledText
|
||||
color: theme.textColor
|
||||
font.pixelSize: theme.fontSizeLarger
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Text {
|
||||
id: descriptionText
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
textFormat: Text.StyledText
|
||||
color: theme.textColor
|
||||
font.pixelSize: theme.fontSizeMedium
|
||||
}
|
||||
}
|
||||
|
||||
footer: DialogButtonBox {
|
||||
id: dialogBox
|
||||
padding: 20
|
||||
alignment: Qt.AlignRight
|
||||
spacing: 10
|
||||
MySettingsButton {
|
||||
text: qsTr("OK")
|
||||
textColor: theme.mediumButtonText
|
||||
backgroundColor: theme.mediumButtonBackground
|
||||
backgroundColorHovered: theme.mediumButtonBackgroundHovered
|
||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||
}
|
||||
MySettingsButton {
|
||||
text: qsTr("Cancel")
|
||||
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
|
||||
}
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
}
|
||||
Keys.onEnterPressed: confirmationDialog.accept()
|
||||
Keys.onReturnPressed: confirmationDialog.accept()
|
||||
}
|
||||
Component.onCompleted: dialogBox.forceActiveFocus()
|
||||
}
|
@@ -10,7 +10,7 @@ import mysettings
|
||||
import network
|
||||
|
||||
MySettingsTab {
|
||||
onRestoreDefaultsClicked: {
|
||||
onRestoreDefaults: {
|
||||
MySettings.restoreLocalDocsDefaults();
|
||||
}
|
||||
|
||||
|
@@ -8,10 +8,34 @@ import mysettings
|
||||
import chatlistmodel
|
||||
|
||||
MySettingsTab {
|
||||
onRestoreDefaultsClicked: {
|
||||
onRestoreDefaults: {
|
||||
MySettings.restoreModelDefaults(root.currentModelInfo);
|
||||
}
|
||||
title: qsTr("Model")
|
||||
|
||||
ConfirmationDialog {
|
||||
id: resetSystemMessageDialog
|
||||
property var index: null
|
||||
property bool resetClears: false
|
||||
dialogTitle: qsTr("%1 system message?").arg(resetClears ? qsTr("Clear") : qsTr("Reset"))
|
||||
description: qsTr("The system message will be %1.").arg(resetClears ? qsTr("removed") : qsTr("reset to the default"))
|
||||
onAccepted: MySettings.resetModelSystemMessage(ModelList.modelInfo(index))
|
||||
function show(index_, resetClears_) { index = index_; resetClears = resetClears_; open(); }
|
||||
}
|
||||
|
||||
ConfirmationDialog {
|
||||
id: resetChatTemplateDialog
|
||||
property bool resetClears: false
|
||||
property var index: null
|
||||
dialogTitle: qsTr("%1 chat template?").arg(resetClears ? qsTr("Clear") : qsTr("Reset"))
|
||||
description: qsTr("The chat template will be %1.").arg(resetClears ? qsTr("erased") : qsTr("reset to the default"))
|
||||
onAccepted: {
|
||||
MySettings.resetModelChatTemplate(ModelList.modelInfo(index));
|
||||
templateTextArea.resetText();
|
||||
}
|
||||
function show(index_, resetClears_) { index = index_; resetClears = resetClears_; open(); }
|
||||
}
|
||||
|
||||
contentItem: GridLayout {
|
||||
id: root
|
||||
columns: 3
|
||||
@@ -35,6 +59,7 @@ MySettingsTab {
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: parent.width
|
||||
Layout.row: 2
|
||||
Layout.column: 0
|
||||
Layout.columnSpan: 2
|
||||
@@ -153,69 +178,154 @@ MySettingsTab {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
MySettingsLabel {
|
||||
visible: !root.currentModelInfo.isOnline
|
||||
text: qsTr("System Prompt")
|
||||
helpText: qsTr("Prefixed at the beginning of every conversation. Must contain the appropriate framing tokens.")
|
||||
RowLayout {
|
||||
Layout.row: 7
|
||||
Layout.column: 0
|
||||
Layout.columnSpan: 2
|
||||
Layout.topMargin: 15
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: parent.width
|
||||
spacing: 10
|
||||
MySettingsLabel {
|
||||
id: systemMessageLabel
|
||||
text: qsTr("System Message")
|
||||
helpText: qsTr("A message to set the context or guide the behavior of the model. Leave blank for " +
|
||||
"none. NOTE: Since GPT4All 3.5, this should not contain control tokens.")
|
||||
onReset: () => resetSystemMessageDialog.show(root.currentModelId, resetClears)
|
||||
function updateResetButton() {
|
||||
const info = root.currentModelInfo;
|
||||
// NOTE: checks if the *override* is set, regardless of whether there is a default
|
||||
canReset = !!info.id && MySettings.isModelSystemMessageSet(info);
|
||||
resetClears = !info.defaultSystemMessage;
|
||||
}
|
||||
Component.onCompleted: updateResetButton()
|
||||
Connections {
|
||||
target: root
|
||||
function onCurrentModelIdChanged() { systemMessageLabel.updateResetButton(); }
|
||||
}
|
||||
Connections {
|
||||
target: MySettings
|
||||
function onSystemMessageChanged(info)
|
||||
{ if (info.id === root.currentModelId) systemMessageLabel.updateResetButton(); }
|
||||
}
|
||||
}
|
||||
Label {
|
||||
id: systemMessageLabelHelp
|
||||
visible: systemMessageArea.errState !== "ok"
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 5
|
||||
Layout.maximumHeight: systemMessageLabel.height
|
||||
text: qsTr("System message is not " +
|
||||
"<a href=\"https://docs.gpt4all.io/gpt4all_desktop/chat_templates.html\">plain text</a>.")
|
||||
color: systemMessageArea.errState === "error" ? theme.textErrorColor : theme.textWarningColor
|
||||
font.pixelSize: theme.fontSizeLarger
|
||||
font.bold: true
|
||||
wrapMode: Text.Wrap
|
||||
elide: Text.ElideRight
|
||||
onLinkActivated: function(link) { Qt.openUrlExternally(link) }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: systemPrompt
|
||||
visible: !root.currentModelInfo.isOnline
|
||||
id: systemMessage
|
||||
Layout.row: 8
|
||||
Layout.column: 0
|
||||
Layout.columnSpan: 2
|
||||
Layout.fillWidth: true
|
||||
color: "transparent"
|
||||
Layout.minimumHeight: Math.max(100, systemPromptArea.contentHeight + 20)
|
||||
Layout.minimumHeight: Math.max(100, systemMessageArea.contentHeight + 20)
|
||||
MyTextArea {
|
||||
id: systemPromptArea
|
||||
id: systemMessageArea
|
||||
anchors.fill: parent
|
||||
text: root.currentModelInfo.systemPrompt
|
||||
property bool isBeingReset: false
|
||||
function resetText() {
|
||||
const info = root.currentModelInfo;
|
||||
isBeingReset = true;
|
||||
text = (info.id ? info.systemMessage.value : null) ?? "";
|
||||
isBeingReset = false;
|
||||
}
|
||||
Component.onCompleted: resetText()
|
||||
Connections {
|
||||
target: MySettings
|
||||
function onSystemPromptChanged() {
|
||||
systemPromptArea.text = root.currentModelInfo.systemPrompt;
|
||||
}
|
||||
function onSystemMessageChanged(info)
|
||||
{ if (info.id === root.currentModelId) systemMessageArea.resetText(); }
|
||||
}
|
||||
Connections {
|
||||
target: root
|
||||
function onCurrentModelInfoChanged() {
|
||||
systemPromptArea.text = root.currentModelInfo.systemPrompt;
|
||||
}
|
||||
function onCurrentModelIdChanged() { systemMessageArea.resetText(); }
|
||||
}
|
||||
// strict validation, because setModelSystemMessage clears isLegacy
|
||||
readonly property var reLegacyCheck: (
|
||||
/(?:^|\s)(?:### *System\b|S(?:ystem|YSTEM):)|<\|(?:im_(?:start|end)|(?:start|end)_header_id|eot_id|SYSTEM_TOKEN)\|>|<<SYS>>/m
|
||||
)
|
||||
onTextChanged: {
|
||||
MySettings.setModelSystemPrompt(root.currentModelInfo, text)
|
||||
const info = root.currentModelInfo;
|
||||
if (!info.id) {
|
||||
errState = "ok";
|
||||
} else if (info.systemMessage.isLegacy && (isBeingReset || reLegacyCheck.test(text))) {
|
||||
errState = "error";
|
||||
} else
|
||||
errState = reLegacyCheck.test(text) ? "warning" : "ok";
|
||||
if (info.id && errState !== "error" && !isBeingReset)
|
||||
MySettings.setModelSystemMessage(info, text);
|
||||
systemMessageLabel.updateResetButton();
|
||||
}
|
||||
Accessible.role: Accessible.EditableText
|
||||
Accessible.name: systemMessageLabel.text
|
||||
Accessible.description: systemMessageLabelHelp.text
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.row: 9
|
||||
Layout.column: 0
|
||||
Layout.columnSpan: 2
|
||||
Layout.topMargin: 15
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: parent.width
|
||||
spacing: 10
|
||||
MySettingsLabel {
|
||||
id: promptTemplateLabel
|
||||
text: qsTr("Prompt Template")
|
||||
helpText: qsTr("The template that wraps every prompt.")
|
||||
id: chatTemplateLabel
|
||||
text: qsTr("Chat Template")
|
||||
helpText: qsTr("This Jinja template turns the chat into input for the model.")
|
||||
onReset: () => resetChatTemplateDialog.show(root.currentModelId, resetClears)
|
||||
function updateResetButton() {
|
||||
const info = root.currentModelInfo;
|
||||
canReset = !!info.id && (
|
||||
MySettings.isModelChatTemplateSet(info)
|
||||
|| templateTextArea.text !== (info.chatTemplate.value ?? "")
|
||||
);
|
||||
resetClears = !info.defaultChatTemplate;
|
||||
}
|
||||
Component.onCompleted: updateResetButton()
|
||||
Connections {
|
||||
target: root
|
||||
function onCurrentModelIdChanged() { chatTemplateLabel.updateResetButton(); }
|
||||
}
|
||||
Connections {
|
||||
target: MySettings
|
||||
function onChatTemplateChanged(info)
|
||||
{ if (info.id === root.currentModelId) chatTemplateLabel.updateResetButton(); }
|
||||
}
|
||||
}
|
||||
MySettingsLabel {
|
||||
id: promptTemplateLabelHelp
|
||||
text: qsTr("Must contain the string \"%1\" to be replaced with the user's input.")
|
||||
color: theme.textErrorColor
|
||||
visible: templateTextArea.text.indexOf("%1") === -1
|
||||
wrapMode: TextArea.Wrap
|
||||
Label {
|
||||
id: chatTemplateLabelHelp
|
||||
visible: templateTextArea.errState !== "ok"
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 5
|
||||
Layout.maximumHeight: chatTemplateLabel.height
|
||||
text: templateTextArea.errMsg
|
||||
color: templateTextArea.errState === "error" ? theme.textErrorColor : theme.textWarningColor
|
||||
font.pixelSize: theme.fontSizeLarger
|
||||
font.bold: true
|
||||
wrapMode: Text.Wrap
|
||||
elide: Text.ElideRight
|
||||
onLinkActivated: function(link) { Qt.openUrlExternally(link) }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: promptTemplate
|
||||
id: chatTemplate
|
||||
Layout.row: 10
|
||||
Layout.column: 0
|
||||
Layout.columnSpan: 2
|
||||
@@ -226,27 +336,71 @@ MySettingsTab {
|
||||
MyTextArea {
|
||||
id: templateTextArea
|
||||
anchors.fill: parent
|
||||
text: root.currentModelInfo.promptTemplate
|
||||
font: fixedFont
|
||||
property bool isBeingReset: false
|
||||
property var errMsg: null
|
||||
function resetText() {
|
||||
const info = root.currentModelInfo;
|
||||
isBeingReset = true;
|
||||
text = (info.id ? info.chatTemplate.value : null) ?? "";
|
||||
isBeingReset = false;
|
||||
}
|
||||
Component.onCompleted: resetText()
|
||||
Connections {
|
||||
target: MySettings
|
||||
function onPromptTemplateChanged() {
|
||||
templateTextArea.text = root.currentModelInfo.promptTemplate;
|
||||
}
|
||||
function onChatTemplateChanged() { templateTextArea.resetText(); }
|
||||
}
|
||||
Connections {
|
||||
target: root
|
||||
function onCurrentModelInfoChanged() {
|
||||
templateTextArea.text = root.currentModelInfo.promptTemplate;
|
||||
}
|
||||
function onCurrentModelIdChanged() { templateTextArea.resetText(); }
|
||||
}
|
||||
function legacyCheck() {
|
||||
return /%[12]\b/.test(text) || !/\{%.*%\}.*\{\{.*\}\}.*\{%.*%\}/.test(text.replace(/\n/g, ''))
|
||||
|| !/\bcontent\b/.test(text);
|
||||
}
|
||||
onTextChanged: {
|
||||
if (templateTextArea.text.indexOf("%1") !== -1) {
|
||||
MySettings.setModelPromptTemplate(root.currentModelInfo, text)
|
||||
const info = root.currentModelInfo;
|
||||
let jinjaError;
|
||||
if (!info.id) {
|
||||
errMsg = null;
|
||||
errState = "ok";
|
||||
} else if (info.chatTemplate.isLegacy && (isBeingReset || legacyCheck())) {
|
||||
errMsg = null;
|
||||
errState = "error";
|
||||
} else if (text === "" && !info.chatTemplate.isSet) {
|
||||
errMsg = qsTr("No <a href=\"https://docs.gpt4all.io/gpt4all_desktop/chat_templates.html\">" +
|
||||
"chat template</a> configured.");
|
||||
errState = "error";
|
||||
} else if (/^\s*$/.test(text)) {
|
||||
errMsg = qsTr("The <a href=\"https://docs.gpt4all.io/gpt4all_desktop/chat_templates.html\">" +
|
||||
"chat template</a> cannot be blank.");
|
||||
errState = "error";
|
||||
} else if ((jinjaError = MySettings.checkJinjaTemplateError(text)) !== null) {
|
||||
errMsg = qsTr("<a href=\"https://docs.gpt4all.io/gpt4all_desktop/chat_templates.html\">Syntax" +
|
||||
" error</a>: %1").arg(jinjaError);
|
||||
errState = "error";
|
||||
} else if (legacyCheck()) {
|
||||
errMsg = qsTr("Chat template is not in " +
|
||||
"<a href=\"https://docs.gpt4all.io/gpt4all_desktop/chat_templates.html\">" +
|
||||
"Jinja format</a>.")
|
||||
errState = "warning";
|
||||
} else {
|
||||
errState = "ok";
|
||||
}
|
||||
if (info.id && errState !== "error" && !isBeingReset)
|
||||
MySettings.setModelChatTemplate(info, text);
|
||||
chatTemplateLabel.updateResetButton();
|
||||
}
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Tab) {
|
||||
const a = templateTextArea;
|
||||
event.accepted = true; // suppress tab
|
||||
a.insert(a.cursorPosition, ' '); // four spaces
|
||||
}
|
||||
}
|
||||
Accessible.role: Accessible.EditableText
|
||||
Accessible.name: promptTemplateLabel.text
|
||||
Accessible.description: promptTemplateLabelHelp.text
|
||||
Accessible.name: chatTemplateLabel.text
|
||||
Accessible.description: chatTemplateLabelHelp.text
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -17,6 +17,7 @@ Button {
|
||||
property color borderColor: "transparent"
|
||||
property real fontPixelSize: theme.fontSizeLarge
|
||||
property string toolTip
|
||||
property alias backgroundRadius: background.radius
|
||||
|
||||
contentItem: Text {
|
||||
text: myButton.text
|
||||
@@ -28,6 +29,7 @@ Button {
|
||||
Accessible.name: text
|
||||
}
|
||||
background: Rectangle {
|
||||
id: background
|
||||
radius: 10
|
||||
border.width: borderWidth
|
||||
border.color: borderColor
|
||||
|
@@ -17,13 +17,42 @@ ColumnLayout {
|
||||
property alias color: mainTextLabel.color
|
||||
property alias linkColor: mainTextLabel.linkColor
|
||||
|
||||
Label {
|
||||
id: mainTextLabel
|
||||
color: theme.settingsTitleTextColor
|
||||
font.pixelSize: theme.fontSizeLarger
|
||||
font.bold: true
|
||||
onLinkActivated: function(link) {
|
||||
root.linkActivated(link);
|
||||
property var onReset: null
|
||||
property alias canReset: resetButton.enabled
|
||||
property bool resetClears: false
|
||||
|
||||
Item {
|
||||
anchors.margins: 5
|
||||
width: childrenRect.width
|
||||
height: mainTextLabel.contentHeight
|
||||
|
||||
Label {
|
||||
id: mainTextLabel
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
color: theme.settingsTitleTextColor
|
||||
font.pixelSize: theme.fontSizeLarger
|
||||
font.bold: true
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
onLinkActivated: function(link) {
|
||||
root.linkActivated(link);
|
||||
}
|
||||
}
|
||||
|
||||
MySettingsButton {
|
||||
id: resetButton
|
||||
anchors.baseline: mainTextLabel.baseline
|
||||
anchors.left: mainTextLabel.right
|
||||
height: mainTextLabel.contentHeight
|
||||
anchors.leftMargin: 10
|
||||
padding: 2
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
backgroundRadius: 5
|
||||
text: resetClears ? qsTr("Clear") : qsTr("Reset")
|
||||
visible: root.onReset !== null
|
||||
onClicked: root.onReset()
|
||||
}
|
||||
}
|
||||
Label {
|
||||
|
@@ -9,7 +9,7 @@ Item {
|
||||
property string title: ""
|
||||
property Item contentItem: null
|
||||
property bool showRestoreDefaultsButton: true
|
||||
signal restoreDefaultsClicked
|
||||
signal restoreDefaults
|
||||
|
||||
onContentItemChanged: function() {
|
||||
if (contentItem) {
|
||||
@@ -19,6 +19,13 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
ConfirmationDialog {
|
||||
id: restoreDefaultsDialog
|
||||
dialogTitle: qsTr("Restore defaults?")
|
||||
description: qsTr("This page of settings will be reset to the defaults.")
|
||||
onAccepted: root.restoreDefaults()
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
width: parent.width
|
||||
@@ -47,6 +54,7 @@ Item {
|
||||
Column {
|
||||
id: contentInner
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: parent.width
|
||||
}
|
||||
|
||||
Item {
|
||||
@@ -63,9 +71,7 @@ Item {
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: text
|
||||
Accessible.description: qsTr("Restores settings dialog to a default state")
|
||||
onClicked: {
|
||||
root.restoreDefaultsClicked();
|
||||
}
|
||||
onClicked: restoreDefaultsDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,18 +5,27 @@ import QtQuick.Controls.Basic
|
||||
|
||||
TextArea {
|
||||
id: myTextArea
|
||||
|
||||
property string errState: "ok" // one of "ok", "error", "warning"
|
||||
|
||||
color: enabled ? theme.textColor : theme.mutedTextColor
|
||||
placeholderTextColor: theme.mutedTextColor
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
background: Rectangle {
|
||||
implicitWidth: 150
|
||||
color: theme.controlBackground
|
||||
border.width: 1
|
||||
border.color: theme.controlBorder
|
||||
border.width: errState === "ok" ? 1 : 2
|
||||
border.color: {
|
||||
switch (errState) {
|
||||
case "ok": return theme.controlBorder;
|
||||
case "warning": return theme.textWarningColor;
|
||||
case "error": return theme.textErrorColor;
|
||||
}
|
||||
}
|
||||
radius: 10
|
||||
}
|
||||
padding: 10
|
||||
wrapMode: TextArea.Wrap
|
||||
|
||||
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ Button {
|
||||
property alias fillMode: image.fillMode
|
||||
property alias imageWidth: image.sourceSize.width
|
||||
property alias imageHeight: image.sourceSize.height
|
||||
property alias bgTransform: background.transform
|
||||
contentItem: Text {
|
||||
text: myButton.text
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
@@ -26,6 +27,7 @@ Button {
|
||||
}
|
||||
|
||||
background: Item {
|
||||
id: background
|
||||
anchors.fill: parent
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
|
@@ -1,46 +0,0 @@
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Controls.Basic
|
||||
import QtQuick.Layouts
|
||||
import llm
|
||||
import mysettings
|
||||
|
||||
MyDialog {
|
||||
id: switchModelDialog
|
||||
anchors.centerIn: parent
|
||||
modal: true
|
||||
padding: 20
|
||||
property int index: -1
|
||||
|
||||
Theme {
|
||||
id: theme
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
textFormat: Text.StyledText
|
||||
text: qsTr("<b>Warning:</b> changing the model will erase the current conversation. Do you wish to continue?")
|
||||
color: theme.textColor
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
}
|
||||
|
||||
footer: DialogButtonBox {
|
||||
id: dialogBox
|
||||
padding: 20
|
||||
alignment: Qt.AlignRight
|
||||
spacing: 10
|
||||
MySettingsButton {
|
||||
text: qsTr("Continue")
|
||||
Accessible.description: qsTr("Continue with model loading")
|
||||
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
|
||||
}
|
||||
MySettingsButton {
|
||||
text: qsTr("Cancel")
|
||||
Accessible.description: qsTr("Cancel")
|
||||
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
|
||||
}
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
}
|
@@ -64,6 +64,9 @@ QtObject {
|
||||
property color green800: Qt.hsla(123/360, 0.17, 0.24)
|
||||
property color green900: Qt.hsla(124/360, 0.17, 0.20)
|
||||
property color green950: Qt.hsla(125/360, 0.22, 0.10)
|
||||
property color green300_sat: Qt.hsla(122/360, 0.24, 0.73)
|
||||
property color green400_sat: Qt.hsla(122/360, 0.23, 0.58)
|
||||
property color green450_sat: Qt.hsla(122/360, 0.23, 0.52)
|
||||
|
||||
// yellow
|
||||
property color yellow0: Qt.hsla(47/360, 0.90, 0.99)
|
||||
@@ -99,6 +102,7 @@ QtObject {
|
||||
property color purple200: Qt.hsla(279/360, 1.0, 0.91)
|
||||
property color purple300: Qt.hsla(279/360, 1.0, 0.84)
|
||||
property color purple400: Qt.hsla(279/360, 1.0, 0.73)
|
||||
property color purple450: Qt.hsla(279/360, 1.0, 0.68)
|
||||
property color purple500: Qt.hsla(279/360, 1.0, 0.63)
|
||||
property color purple600: Qt.hsla(279/360, 1.0, 0.53)
|
||||
property color purple700: Qt.hsla(279/360, 1.0, 0.47)
|
||||
@@ -408,6 +412,39 @@ QtObject {
|
||||
}
|
||||
}
|
||||
|
||||
property color mediumButtonBackground: {
|
||||
switch (MySettings.chatTheme) {
|
||||
case MySettingsEnums.ChatTheme.LegacyDark:
|
||||
return purple400
|
||||
case MySettingsEnums.ChatTheme.Dark:
|
||||
return green400_sat
|
||||
default:
|
||||
return green400_sat
|
||||
}
|
||||
}
|
||||
|
||||
property color mediumButtonBackgroundHovered: {
|
||||
switch (MySettings.chatTheme) {
|
||||
case MySettingsEnums.ChatTheme.LegacyDark:
|
||||
return purple450
|
||||
case MySettingsEnums.ChatTheme.Dark:
|
||||
return green450_sat
|
||||
default:
|
||||
return green300_sat
|
||||
}
|
||||
}
|
||||
|
||||
property color mediumButtonText: {
|
||||
switch (MySettings.chatTheme) {
|
||||
case MySettingsEnums.ChatTheme.LegacyDark:
|
||||
return textColor
|
||||
case MySettingsEnums.ChatTheme.Dark:
|
||||
return textColor
|
||||
default:
|
||||
return white
|
||||
}
|
||||
}
|
||||
|
||||
property color darkButtonText: {
|
||||
switch (MySettings.chatTheme) {
|
||||
case MySettingsEnums.ChatTheme.LegacyDark:
|
||||
@@ -922,16 +959,8 @@ QtObject {
|
||||
}
|
||||
}
|
||||
|
||||
property color textErrorColor: {
|
||||
switch (MySettings.chatTheme) {
|
||||
case MySettingsEnums.ChatTheme.LegacyDark:
|
||||
return red400
|
||||
case MySettingsEnums.ChatTheme.Dark:
|
||||
return red400
|
||||
default:
|
||||
return red400
|
||||
}
|
||||
}
|
||||
readonly property color textErrorColor: red400
|
||||
readonly property color textWarningColor: yellow400
|
||||
|
||||
property color settingsTitleTextColor: {
|
||||
switch (MySettings.chatTheme) {
|
||||
|
Reference in New Issue
Block a user