From 4a1a3c48e83771ea640c5c401c6fd57deb28cbb6 Mon Sep 17 00:00:00 2001 From: AT Date: Sun, 30 Jun 2024 15:10:19 -0400 Subject: [PATCH] Latest v3.0.0 rc4 fixes (#2490) * Use the same font size for code blocks as we do for the rest of the chat text. * Add a conversation tray after discussion with Vincent and Andriy and gathering of feedback from some other users. This adds the reset context back as a recycle button and copy chat features back to the app for v3.0.0. Signed-off-by: Adam Treat --- gpt4all-chat/CMakeLists.txt | 1 + gpt4all-chat/chatviewtextprocessor.cpp | 17 +- gpt4all-chat/chatviewtextprocessor.h | 6 + gpt4all-chat/icons/close.svg | 6 +- gpt4all-chat/icons/copy.svg | 10 +- gpt4all-chat/icons/download.svg | 6 +- gpt4all-chat/icons/image.svg | 7 +- gpt4all-chat/icons/network.svg | 2 +- gpt4all-chat/icons/question.svg | 9 +- gpt4all-chat/icons/recycle.svg | 1 + gpt4all-chat/icons/redo.svg | 1 + gpt4all-chat/icons/regenerate.svg | 2 +- gpt4all-chat/icons/search.svg | 7 +- gpt4all-chat/icons/send_message.svg | 2 +- gpt4all-chat/icons/stop_generating.svg | 2 +- gpt4all-chat/icons/thumbs_down.svg | 6 +- gpt4all-chat/icons/thumbs_up.svg | 6 +- gpt4all-chat/icons/undo.svg | 1 + gpt4all-chat/qml/ChatView.qml | 260 ++++++++++++++++++------- gpt4all-chat/qml/Theme.qml | 6 +- 20 files changed, 235 insertions(+), 123 deletions(-) create mode 100644 gpt4all-chat/icons/recycle.svg create mode 100644 gpt4all-chat/icons/redo.svg create mode 100644 gpt4all-chat/icons/undo.svg diff --git a/gpt4all-chat/CMakeLists.txt b/gpt4all-chat/CMakeLists.txt index be503598..c9d20622 100644 --- a/gpt4all-chat/CMakeLists.txt +++ b/gpt4all-chat/CMakeLists.txt @@ -175,6 +175,7 @@ qt_add_qml_module(chat icons/antenna_3.svg icons/send_message.svg icons/stop_generating.svg + icons/recycle.svg icons/regenerate.svg icons/caret_down.svg icons/caret_right.svg diff --git a/gpt4all-chat/chatviewtextprocessor.cpp b/gpt4all-chat/chatviewtextprocessor.cpp index bade6c18..f4119276 100644 --- a/gpt4all-chat/chatviewtextprocessor.cpp +++ b/gpt4all-chat/chatviewtextprocessor.cpp @@ -888,6 +888,7 @@ ChatViewTextProcessor::ChatViewTextProcessor(QObject *parent) , m_syntaxHighlighter(new SyntaxHighlighter(this)) , m_isProcessingText(false) , m_shouldProcessText(true) + , m_fontPixelSize(QGuiApplication::font().pointSizeF()) { } @@ -933,6 +934,20 @@ void ChatViewTextProcessor::setShouldProcessText(bool b) handleTextChanged(); } +qreal ChatViewTextProcessor::fontPixelSize() const +{ + return m_fontPixelSize; +} + +void ChatViewTextProcessor::setFontPixelSize(qreal sz) +{ + if (m_fontPixelSize == sz) + return; + m_fontPixelSize = sz; + emit fontPixelSizeChanged(); + handleTextChanged(); +} + void traverseDocument(QTextDocument *doc, QTextFrame *frame) { QTextFrame *rootFrame = frame ? frame : doc->rootFrame(); @@ -1126,7 +1141,7 @@ void ChatViewTextProcessor::handleCodeBlocks() codeBlockCharFormat.setForeground(defaultColor); QFont monospaceFont("Courier"); - monospaceFont.setPointSize(QGuiApplication::font().pointSize() + 2); + monospaceFont.setPointSize(m_fontPixelSize); if (monospaceFont.family() != "Courier") { monospaceFont.setFamily("Monospace"); // Fallback if Courier isn't available } diff --git a/gpt4all-chat/chatviewtextprocessor.h b/gpt4all-chat/chatviewtextprocessor.h index 7f2cc73e..60207f44 100644 --- a/gpt4all-chat/chatviewtextprocessor.h +++ b/gpt4all-chat/chatviewtextprocessor.h @@ -42,6 +42,7 @@ class ChatViewTextProcessor : public QObject Q_OBJECT Q_PROPERTY(QQuickTextDocument* textDocument READ textDocument WRITE setTextDocument NOTIFY textDocumentChanged()) Q_PROPERTY(bool shouldProcessText READ shouldProcessText WRITE setShouldProcessText NOTIFY shouldProcessTextChanged()) + Q_PROPERTY(qreal fontPixelSize READ fontPixelSize WRITE setFontPixelSize NOTIFY fontPixelSizeChanged()) QML_ELEMENT public: explicit ChatViewTextProcessor(QObject *parent = nullptr); @@ -57,9 +58,13 @@ public: bool shouldProcessText() const; void setShouldProcessText(bool b); + qreal fontPixelSize() const; + void setFontPixelSize(qreal b); + Q_SIGNALS: void textDocumentChanged(); void shouldProcessTextChanged(); + void fontPixelSizeChanged(); private Q_SLOTS: void handleTextChanged(); @@ -75,6 +80,7 @@ private: QColor m_headerColor; bool m_shouldProcessText = false; bool m_isProcessingText = false; + qreal m_fontPixelSize; }; #endif // CHATVIEWTEXTPROCESSOR_H diff --git a/gpt4all-chat/icons/close.svg b/gpt4all-chat/icons/close.svg index 4074cd97..70772054 100644 --- a/gpt4all-chat/icons/close.svg +++ b/gpt4all-chat/icons/close.svg @@ -1,5 +1 @@ - - + \ No newline at end of file diff --git a/gpt4all-chat/icons/copy.svg b/gpt4all-chat/icons/copy.svg index 5ab45b5b..1b1334c7 100644 --- a/gpt4all-chat/icons/copy.svg +++ b/gpt4all-chat/icons/copy.svg @@ -1,9 +1 @@ - - - - - - - - - + \ No newline at end of file diff --git a/gpt4all-chat/icons/download.svg b/gpt4all-chat/icons/download.svg index 76f56c3f..60d202b4 100644 --- a/gpt4all-chat/icons/download.svg +++ b/gpt4all-chat/icons/download.svg @@ -1,5 +1 @@ - - + \ No newline at end of file diff --git a/gpt4all-chat/icons/image.svg b/gpt4all-chat/icons/image.svg index f4c46f14..25a7a765 100644 --- a/gpt4all-chat/icons/image.svg +++ b/gpt4all-chat/icons/image.svg @@ -1,6 +1 @@ - - - + \ No newline at end of file diff --git a/gpt4all-chat/icons/network.svg b/gpt4all-chat/icons/network.svg index 266f13d6..4b148276 100644 --- a/gpt4all-chat/icons/network.svg +++ b/gpt4all-chat/icons/network.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/gpt4all-chat/icons/question.svg b/gpt4all-chat/icons/question.svg index 7cf18fdd..7825afec 100644 --- a/gpt4all-chat/icons/question.svg +++ b/gpt4all-chat/icons/question.svg @@ -1,8 +1 @@ - - - - - - - - + \ No newline at end of file diff --git a/gpt4all-chat/icons/recycle.svg b/gpt4all-chat/icons/recycle.svg new file mode 100644 index 00000000..27cb1f08 --- /dev/null +++ b/gpt4all-chat/icons/recycle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gpt4all-chat/icons/redo.svg b/gpt4all-chat/icons/redo.svg new file mode 100644 index 00000000..6a0db9e6 --- /dev/null +++ b/gpt4all-chat/icons/redo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gpt4all-chat/icons/regenerate.svg b/gpt4all-chat/icons/regenerate.svg index 016e6a52..25cdeedf 100644 --- a/gpt4all-chat/icons/regenerate.svg +++ b/gpt4all-chat/icons/regenerate.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/gpt4all-chat/icons/search.svg b/gpt4all-chat/icons/search.svg index bae556fc..bf4e5058 100644 --- a/gpt4all-chat/icons/search.svg +++ b/gpt4all-chat/icons/search.svg @@ -1,6 +1 @@ - - - + \ No newline at end of file diff --git a/gpt4all-chat/icons/send_message.svg b/gpt4all-chat/icons/send_message.svg index d8650b66..ca4a8296 100644 --- a/gpt4all-chat/icons/send_message.svg +++ b/gpt4all-chat/icons/send_message.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/gpt4all-chat/icons/stop_generating.svg b/gpt4all-chat/icons/stop_generating.svg index c627ac0e..72261f00 100644 --- a/gpt4all-chat/icons/stop_generating.svg +++ b/gpt4all-chat/icons/stop_generating.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/gpt4all-chat/icons/thumbs_down.svg b/gpt4all-chat/icons/thumbs_down.svg index b01a82d3..4123d9ef 100644 --- a/gpt4all-chat/icons/thumbs_down.svg +++ b/gpt4all-chat/icons/thumbs_down.svg @@ -1,5 +1 @@ - - + \ No newline at end of file diff --git a/gpt4all-chat/icons/thumbs_up.svg b/gpt4all-chat/icons/thumbs_up.svg index cd5efcd2..7adc0b31 100644 --- a/gpt4all-chat/icons/thumbs_up.svg +++ b/gpt4all-chat/icons/thumbs_up.svg @@ -1,5 +1 @@ - - + \ No newline at end of file diff --git a/gpt4all-chat/icons/undo.svg b/gpt4all-chat/icons/undo.svg new file mode 100644 index 00000000..ced96b87 --- /dev/null +++ b/gpt4all-chat/icons/undo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gpt4all-chat/qml/ChatView.qml b/gpt4all-chat/qml/ChatView.qml index dca00017..8a9c268a 100644 --- a/gpt4all-chat/qml/ChatView.qml +++ b/gpt4all-chat/qml/ChatView.qml @@ -949,6 +949,7 @@ Rectangle { ChatViewTextProcessor { id: textProcessor + fontPixelSize: myTextArea.font.pixelSize } Component.onCompleted: { @@ -1019,9 +1020,9 @@ Rectangle { MyToolButton { id: thumbsDown anchors.top: thumbsUp.top - anchors.topMargin: 10 + anchors.topMargin: 3 anchors.left: thumbsUp.right - anchors.leftMargin: 2 + anchors.leftMargin: 3 width: 24 height: 24 imageWidth: width @@ -1307,33 +1308,122 @@ Rectangle { } } } + } - RowLayout { - id: conversationButtons - anchors.bottom: textInputView.top - anchors.horizontalCenter: textInputView.horizontalCenter - anchors.bottomMargin: 20 - spacing: 10 - MyButton { - textColor: theme.textColor - visible: chatModel.count && !currentChat.isServer && currentChat.isModelLoaded - Image { - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: 15 - source: currentChat.responseInProgress ? "qrc:/gpt4all/icons/stop_generating.svg" : "qrc:/gpt4all/icons/regenerate.svg" + Rectangle { + id: conversationTrayContent + anchors.bottom: conversationTrayButton.top + anchors.horizontalCenter: conversationTrayButton.horizontalCenter + width: conversationTrayContentLayout.width + height: conversationTrayContentLayout.height + color: theme.containerBackground + radius: 5 + opacity: 0 + visible: false + clip: true + z: 400 + + property bool isHovered: { + return conversationTrayButton.isHovered || + resetContextButton.hovered || copyChatButton.hovered || + regenerateButton.hovered || stopButton.hovered + } + + state: conversationTrayContent.isHovered ? "expanded" : "collapsed" + states: [ + State { + name: "expanded" + PropertyChanges { target: conversationTrayContent; opacity: 1 } + }, + State { + name: "collapsed" + PropertyChanges { target: conversationTrayContent; opacity: 0 } } + ] + transitions: [ + Transition { + from: "collapsed" + to: "expanded" + SequentialAnimation { + ScriptAction { + script: conversationTrayContent.visible = true + } + PropertyAnimation { + target: conversationTrayContent + property: "opacity" + duration: 300 + easing.type: Easing.InOutQuad + } + } + }, + Transition { + from: "expanded" + to: "collapsed" + SequentialAnimation { + PropertyAnimation { + target: conversationTrayContent + property: "opacity" + duration: 300 + easing.type: Easing.InOutQuad + } + ScriptAction { + script: conversationTrayContent.visible = false + } + } + } + ] - leftPadding: 50 - onClicked: { - var index = Math.max(0, chatModel.count - 1); - var listElement = chatModel.get(index); - - if (currentChat.responseInProgress) { - listElement.stopped = true - currentChat.stopGenerating() - } else { + RowLayout { + id: conversationTrayContentLayout + spacing: 0 + MyToolButton { + id: resetContextButton + Layout.preferredWidth: 40 + Layout.preferredHeight: 40 + source: "qrc:/gpt4all/icons/recycle.svg" + imageWidth: 20 + imageHeight: 20 + onClicked: { + Network.trackChatEvent("reset_context", { "length": chatModel.count }) + currentChat.reset(); + currentChat.processSystemPrompt(); + } + ToolTip.visible: resetContextButton.hovered + ToolTip.text: qsTr("Erase and reset chat session") + } + MyToolButton { + id: copyChatButton + Layout.preferredWidth: 40 + Layout.preferredHeight: 40 + source: "qrc:/gpt4all/icons/copy.svg" + imageWidth: 20 + imageHeight: 20 + TextEdit{ + id: copyEdit + visible: false + } + onClicked: { + var conversation = getConversation() + copyEdit.text = conversation + copyEdit.selectAll() + copyEdit.copy() + copyMessage.open() + } + 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: { + var index = Math.max(0, chatModel.count - 1); + var listElement = chatModel.get(index); currentChat.regenerateResponse() if (chatModel.count) { if (listElement.name === qsTr("Response: ")) { @@ -1346,51 +1436,89 @@ Rectangle { } } } + ToolTip.visible: regenerateButton.hovered + ToolTip.text: qsTr("Redo last chat response") } + MyToolButton { + id: stopButton + Layout.preferredWidth: 40 + Layout.preferredHeight: 40 + source: "qrc:/gpt4all/icons/stop_generating.svg" + imageWidth: 20 + imageHeight: 20 + visible: currentChat.responseInProgress + onClicked: { + var index = Math.max(0, chatModel.count - 1); + var listElement = chatModel.get(index); + listElement.stopped = true + currentChat.stopGenerating() + } + ToolTip.visible: stopButton.hovered + ToolTip.text: qsTr("Stop the current response generation") + } + } + } - borderWidth: 1 - backgroundColor: theme.conversationButtonBackground - backgroundColorHovered: theme.conversationButtonBackgroundHovered - backgroundRadius: 5 - padding: 15 - topPadding: 8 - bottomPadding: 8 - text: currentChat.responseInProgress ? qsTr("Stop generating") : qsTr("Regenerate response") - fontPixelSize: theme.fontSizeSmall - Accessible.description: qsTr("Controls generation of the response") + Item { + id: conversationTrayButton + anchors.bottom: textInputView.top + anchors.horizontalCenter: textInputView.horizontalCenter + width: 30 + height: 30 + visible: chatModel.count && !currentChat.isServer && currentChat.isModelLoaded + property bool isHovered: conversationTrayMouseAreaButton.containsMouse + MouseArea { + id: conversationTrayMouseAreaButton + anchors.fill: parent + hoverEnabled: true + } + Text { + id: conversationTrayTextButton + anchors.horizontalCenter: parent.horizontalCenter + horizontalAlignment: Qt.AlignHCenter + leftPadding: 5 + rightPadding: 5 + text: "\u00B7\u00B7\u00B7" + color: theme.textColor + font.pixelSize: 20 // fixed size + } + } + + MyButton { + anchors.bottom: textInputView.top + anchors.horizontalCenter: textInputView.horizontalCenter + anchors.bottomMargin: 20 + textColor: theme.textColor + visible: !currentChat.isServer + && !currentChat.isModelLoaded + && currentChat.modelLoadingError === "" + && !currentChat.trySwitchContextInProgress + && !currentChat.isCurrentlyLoading + && currentModelName() !== "" + + Image { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 15 + sourceSize.width: 15 + sourceSize.height: 15 + source: "qrc:/gpt4all/icons/regenerate.svg" + } + leftPadding: 40 + onClicked: { + currentChat.reloadModel(); } - MyButton { - textColor: theme.textColor - visible: !currentChat.isServer - && !currentChat.isModelLoaded - && currentChat.modelLoadingError === "" - && !currentChat.trySwitchContextInProgress - && !currentChat.isCurrentlyLoading - && currentModelName() !== "" - - Image { - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: 15 - source: "qrc:/gpt4all/icons/regenerate.svg" - } - leftPadding: 50 - onClicked: { - currentChat.reloadModel(); - } - - borderWidth: 1 - backgroundColor: theme.conversationButtonBackground - backgroundColorHovered: theme.conversationButtonBackgroundHovered - backgroundRadius: 5 - padding: 15 - topPadding: 8 - bottomPadding: 8 - text: qsTr("Reload \u00B7 ") + currentChat.modelInfo.name - fontPixelSize: theme.fontSizeSmall - Accessible.description: qsTr("Reloads the model") - } + borderWidth: 1 + backgroundColor: theme.conversationButtonBackground + backgroundColorHovered: theme.conversationButtonBackgroundHovered + backgroundRadius: 5 + padding: 15 + topPadding: 8 + bottomPadding: 8 + text: qsTr("Reload \u00B7 ") + currentChat.modelInfo.name + fontPixelSize: theme.fontSizeSmall + Accessible.description: qsTr("Reloads the model") } Text { diff --git a/gpt4all-chat/qml/Theme.qml b/gpt4all-chat/qml/Theme.qml index eab8aece..880d674f 100644 --- a/gpt4all-chat/qml/Theme.qml +++ b/gpt4all-chat/qml/Theme.qml @@ -518,7 +518,7 @@ QtObject { case "Dark": return accentColor; default: - return accentColor; + return black; } } @@ -529,7 +529,7 @@ QtObject { case "Dark": return darkwhite; default: - return black; + return accentColor; } } @@ -740,7 +740,7 @@ QtObject { case "Dark": return green400; default: - return green400; + return black; } }