2024-04-25 13:16:52 -04:00
|
|
|
import Qt5Compat.GraphicalEffects
|
2024-03-13 19:57:05 -04:00
|
|
|
import QtCore
|
|
|
|
import QtQuick
|
|
|
|
import QtQuick.Controls
|
|
|
|
import QtQuick.Controls.Basic
|
|
|
|
import QtQuick.Layouts
|
2024-04-25 13:16:52 -04:00
|
|
|
|
2024-03-13 19:57:05 -04:00
|
|
|
import chatlistmodel
|
|
|
|
import download
|
|
|
|
import gpt4all
|
2024-04-25 13:16:52 -04:00
|
|
|
import llm
|
|
|
|
import localdocs
|
|
|
|
import modellist
|
2024-03-13 19:57:05 -04:00
|
|
|
import mysettings
|
2024-04-25 13:16:52 -04:00
|
|
|
import network
|
2024-03-13 19:57:05 -04:00
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
id: window
|
|
|
|
|
|
|
|
Theme {
|
|
|
|
id: theme
|
|
|
|
}
|
|
|
|
|
|
|
|
property var currentChat: ChatListModel.currentChat
|
|
|
|
property var chatModel: currentChat.chatModel
|
2024-06-24 18:49:23 -04:00
|
|
|
signal addCollectionViewRequested()
|
|
|
|
signal addModelViewRequested()
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
color: theme.viewBackground
|
2024-03-13 19:57:05 -04:00
|
|
|
|
|
|
|
Connections {
|
|
|
|
target: currentChat
|
|
|
|
function onResponseInProgressChanged() {
|
|
|
|
if (MySettings.networkIsActive && !currentChat.responseInProgress)
|
|
|
|
Network.sendConversation(currentChat.id, getConversationJson());
|
|
|
|
}
|
|
|
|
function onModelLoadingErrorChanged() {
|
|
|
|
if (currentChat.modelLoadingError !== "")
|
|
|
|
modelLoadingErrorPopup.open()
|
|
|
|
}
|
|
|
|
function onModelLoadingWarning(warning) {
|
|
|
|
modelLoadingWarningPopup.open_(warning)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function currentModelName() {
|
|
|
|
return ModelList.modelInfo(currentChat.modelInfo.id).name;
|
|
|
|
}
|
|
|
|
|
|
|
|
PopupDialog {
|
2024-06-24 18:49:23 -04:00
|
|
|
id: modelLoadingErrorPopup
|
2024-03-13 19:57:05 -04:00
|
|
|
anchors.centerIn: parent
|
|
|
|
shouldTimeOut: false
|
2024-06-24 18:49:23 -04:00
|
|
|
text: qsTr("<h3>Encountered an error loading model:</h3><br>")
|
|
|
|
+ "<i>\"" + currentChat.modelLoadingError + "\"</i>"
|
|
|
|
+ qsTr("<br><br>Model loading failures can happen for a variety of reasons, but the most common "
|
|
|
|
+ "causes include a bad file format, an incomplete or corrupted download, the wrong file "
|
|
|
|
+ "type, not enough system RAM or an incompatible model type. Here are some suggestions for resolving the problem:"
|
|
|
|
+ "<br><ul>"
|
|
|
|
+ "<li>Ensure the model file has a compatible format and type"
|
|
|
|
+ "<li>Check the model file is complete in the download folder"
|
|
|
|
+ "<li>You can find the download folder in the settings dialog"
|
|
|
|
+ "<li>If you've sideloaded the model ensure the file is not corrupt by checking md5sum"
|
|
|
|
+ "<li>Read more about what models are supported in our <a href=\"https://docs.gpt4all.io/gpt4all_chat.html\">documentation</a> for the gui"
|
|
|
|
+ "<li>Check out our <a href=\"https://discord.gg/4M2QFmTt2k\">discord channel</a> for help")
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
PopupDialog {
|
2024-06-24 18:49:23 -04:00
|
|
|
id: modelLoadingWarningPopup
|
|
|
|
property string message
|
2024-03-13 19:57:05 -04:00
|
|
|
anchors.centerIn: parent
|
|
|
|
shouldTimeOut: false
|
2024-06-24 18:49:23 -04:00
|
|
|
text: qsTr("<h3>Warning</h3><p>%1</p>").arg(message)
|
|
|
|
function open_(msg) { message = msg; open(); }
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
SwitchModelDialog {
|
|
|
|
id: switchModelDialog
|
2024-03-13 19:57:05 -04:00
|
|
|
anchors.centerIn: parent
|
2024-06-24 18:49:23 -04:00
|
|
|
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")
|
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
PopupDialog {
|
|
|
|
id: copyMessage
|
2024-03-13 19:57:05 -04:00
|
|
|
anchors.centerIn: parent
|
2024-06-24 18:49:23 -04:00
|
|
|
text: qsTr("Conversation copied to clipboard.")
|
|
|
|
font.pixelSize: theme.fontSizeLarge
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
PopupDialog {
|
|
|
|
id: copyCodeMessage
|
2024-04-18 14:52:29 -04:00
|
|
|
anchors.centerIn: parent
|
2024-06-24 18:49:23 -04:00
|
|
|
text: qsTr("Code copied to clipboard.")
|
|
|
|
font.pixelSize: theme.fontSizeLarge
|
2024-04-18 14:52:29 -04:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
function getConversation() {
|
|
|
|
var conversation = "";
|
|
|
|
for (var i = 0; i < chatModel.count; i++) {
|
|
|
|
var item = chatModel.get(i)
|
|
|
|
var string = item.name;
|
|
|
|
var isResponse = item.name === qsTr("Response: ")
|
|
|
|
string += chatModel.get(i).value
|
|
|
|
if (isResponse && item.stopped)
|
|
|
|
string += " <stopped>"
|
|
|
|
string += "\n"
|
|
|
|
conversation += string
|
|
|
|
}
|
|
|
|
return conversation
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
function getConversationJson() {
|
|
|
|
var str = "{\"conversation\": [";
|
|
|
|
for (var i = 0; i < chatModel.count; i++) {
|
|
|
|
var item = chatModel.get(i)
|
|
|
|
var isResponse = item.name === qsTr("Response: ")
|
|
|
|
str += "{\"content\": ";
|
|
|
|
str += JSON.stringify(item.value)
|
|
|
|
str += ", \"role\": \"" + (isResponse ? "assistant" : "user") + "\"";
|
|
|
|
if (isResponse && item.thumbsUpState !== item.thumbsDownState)
|
|
|
|
str += ", \"rating\": \"" + (item.thumbsUpState ? "positive" : "negative") + "\"";
|
|
|
|
if (isResponse && item.newResponse !== "")
|
|
|
|
str += ", \"edited_content\": " + JSON.stringify(item.newResponse);
|
|
|
|
if (isResponse && item.stopped)
|
|
|
|
str += ", \"stopped\": \"true\""
|
|
|
|
if (!isResponse)
|
|
|
|
str += "},"
|
|
|
|
else
|
|
|
|
str += ((i < chatModel.count - 1) ? "}," : "}")
|
|
|
|
}
|
|
|
|
return str + "]}"
|
|
|
|
}
|
|
|
|
|
|
|
|
ChatDrawer {
|
|
|
|
id: chatDrawer
|
|
|
|
anchors.left: parent.left
|
|
|
|
anchors.top: parent.top
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
width: Math.max(180, Math.min(600, 0.23 * window.width))
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
PopupDialog {
|
2024-06-24 18:49:23 -04:00
|
|
|
id: referenceContextDialog
|
2024-03-13 19:57:05 -04:00
|
|
|
anchors.centerIn: parent
|
|
|
|
shouldTimeOut: false
|
2024-06-24 18:49:23 -04:00
|
|
|
shouldShowBusy: false
|
|
|
|
modal: true
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
Item {
|
|
|
|
id: mainArea
|
|
|
|
anchors.left: chatDrawer.right
|
2024-03-13 19:57:05 -04:00
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.top: parent.top
|
2024-06-24 18:49:23 -04:00
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
state: "expanded"
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
states: [
|
|
|
|
State {
|
|
|
|
name: "expanded"
|
|
|
|
AnchorChanges {
|
|
|
|
target: mainArea
|
|
|
|
anchors.left: chatDrawer.right
|
|
|
|
}
|
|
|
|
},
|
|
|
|
State {
|
|
|
|
name: "collapsed"
|
|
|
|
AnchorChanges {
|
|
|
|
target: mainArea
|
|
|
|
anchors.left: parent.left
|
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
2024-06-24 18:49:23 -04:00
|
|
|
]
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
function toggleLeftPanel() {
|
|
|
|
if (mainArea.state === "expanded") {
|
|
|
|
mainArea.state = "collapsed";
|
|
|
|
} else {
|
|
|
|
mainArea.state = "expanded";
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
transitions: Transition {
|
|
|
|
AnchorAnimation {
|
|
|
|
easing.type: Easing.InOutQuad
|
|
|
|
duration: 200
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
Rectangle {
|
|
|
|
id: header
|
|
|
|
anchors.left: parent.left
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.top: parent.top
|
|
|
|
height: 100
|
|
|
|
color: theme.conversationBackground
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-04-18 14:52:29 -04:00
|
|
|
RowLayout {
|
|
|
|
id: comboLayout
|
2024-06-24 18:49:23 -04:00
|
|
|
height: 80
|
|
|
|
anchors.left: parent.left
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
2024-06-29 18:00:52 -04:00
|
|
|
spacing: 0
|
2024-04-18 14:52:29 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
Rectangle {
|
|
|
|
Layout.alignment: Qt.AlignLeft
|
|
|
|
Layout.leftMargin: 30
|
|
|
|
Layout.fillWidth: true
|
|
|
|
color: "transparent"
|
|
|
|
Layout.preferredHeight: childrenRect.height
|
|
|
|
MyToolButton {
|
|
|
|
id: drawerButton
|
|
|
|
anchors.left: parent.left
|
|
|
|
backgroundColor: theme.iconBackgroundLight
|
|
|
|
width: 40
|
|
|
|
height: 40
|
|
|
|
imageWidth: 40
|
|
|
|
imageHeight: 40
|
|
|
|
padding: 15
|
|
|
|
source: mainArea.state === "expanded" ? "qrc:/gpt4all/icons/left_panel_open.svg" : "qrc:/gpt4all/icons/left_panel_closed.svg"
|
|
|
|
Accessible.role: Accessible.ButtonMenu
|
|
|
|
Accessible.name: qsTr("Chat panel")
|
|
|
|
Accessible.description: qsTr("Chat panel with options")
|
|
|
|
onClicked: {
|
|
|
|
mainArea.toggleLeftPanel()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-18 14:52:29 -04:00
|
|
|
MyComboBox {
|
|
|
|
id: comboBox
|
2024-06-24 18:49:23 -04:00
|
|
|
Layout.alignment: Qt.AlignHCenter
|
2024-04-18 14:52:29 -04:00
|
|
|
Layout.fillHeight: true
|
2024-06-29 18:00:52 -04:00
|
|
|
Layout.preferredWidth: 550
|
|
|
|
Layout.leftMargin: {
|
|
|
|
// This function works in tandem with the preferredWidth and the layout to
|
|
|
|
// provide the maximum size combobox we can have at the smallest window width
|
|
|
|
// we allow with the largest font size we allow. It is unfortunately based
|
|
|
|
// upon a magic number that was produced through trial and error for something
|
|
|
|
// I don't fully understand.
|
|
|
|
return -Math.max(0, comboBox.width / 2 + collectionsButton.width + 110 /*magic*/ - comboLayout.width / 2);
|
|
|
|
}
|
2024-04-18 14:52:29 -04:00
|
|
|
enabled: !currentChat.isServer
|
2024-05-15 14:07:03 -04:00
|
|
|
&& !currentChat.trySwitchContextInProgress
|
|
|
|
&& !currentChat.isCurrentlyLoading
|
2024-06-28 20:34:03 -04:00
|
|
|
&& ModelList.selectableModels.count !== 0
|
|
|
|
model: ModelList.selectableModels
|
2024-04-18 14:52:29 -04:00
|
|
|
valueRole: "id"
|
|
|
|
textRole: "name"
|
|
|
|
|
|
|
|
function changeModel(index) {
|
|
|
|
currentChat.stopGenerating()
|
|
|
|
currentChat.reset();
|
|
|
|
currentChat.modelInfo = ModelList.modelInfo(comboBox.valueAt(index))
|
2024-03-20 11:09:59 -04:00
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-04-18 14:52:29 -04:00
|
|
|
Connections {
|
|
|
|
target: switchModelDialog
|
|
|
|
function onAccepted() {
|
|
|
|
comboBox.changeModel(switchModelDialog.index)
|
|
|
|
}
|
2024-03-20 11:09:59 -04:00
|
|
|
}
|
2024-04-18 14:52:29 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
background: Rectangle {
|
|
|
|
color: theme.mainComboBackground
|
|
|
|
radius: 10
|
|
|
|
ProgressBar {
|
|
|
|
id: modelProgress
|
|
|
|
anchors.bottom: parent.bottom
|
2024-06-29 18:00:52 -04:00
|
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
|
|
width: contentRow.width + 20
|
2024-06-24 18:49:23 -04:00
|
|
|
visible: currentChat.isCurrentlyLoading
|
|
|
|
height: 10
|
|
|
|
value: currentChat.modelLoadingPercentage
|
|
|
|
background: Rectangle {
|
|
|
|
color: theme.progressBackground
|
|
|
|
radius: 10
|
|
|
|
}
|
|
|
|
contentItem: Item {
|
|
|
|
Rectangle {
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
width: modelProgress.visualPosition * parent.width
|
|
|
|
height: 10
|
|
|
|
radius: 2
|
|
|
|
color: theme.progressForeground
|
|
|
|
}
|
2024-04-18 14:52:29 -04:00
|
|
|
}
|
|
|
|
}
|
2024-03-20 11:09:59 -04:00
|
|
|
}
|
2024-06-29 18:00:52 -04:00
|
|
|
|
|
|
|
contentItem: Item {
|
|
|
|
RowLayout {
|
|
|
|
id: contentRow
|
|
|
|
anchors.centerIn: parent
|
|
|
|
spacing: 0
|
2024-06-30 19:26:53 -04:00
|
|
|
Layout.maximumWidth: 550
|
2024-06-29 18:00:52 -04:00
|
|
|
RowLayout {
|
|
|
|
id: miniButtonsRow
|
|
|
|
clip: true
|
2024-06-30 19:26:53 -04:00
|
|
|
Layout.maximumWidth: 550
|
2024-06-29 18:00:52 -04:00
|
|
|
Behavior on Layout.preferredWidth {
|
|
|
|
NumberAnimation {
|
|
|
|
duration: 300
|
|
|
|
easing.type: Easing.InOutQuad
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Layout.preferredWidth: {
|
2024-06-30 19:26:53 -04:00
|
|
|
if (!(comboBox.hovered || reloadButton.hovered || ejectButton.hovered))
|
2024-06-29 18:00:52 -04:00
|
|
|
return 0
|
|
|
|
return (reloadButton.visible ? reloadButton.width : 0) + (ejectButton.visible ? ejectButton.width : 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMiniButton {
|
|
|
|
id: reloadButton
|
|
|
|
Layout.alignment: Qt.AlignCenter
|
|
|
|
visible: currentChat.modelLoadingError === ""
|
|
|
|
&& !currentChat.trySwitchContextInProgress
|
|
|
|
&& !currentChat.isCurrentlyLoading
|
|
|
|
&& (currentChat.isModelLoaded || currentModelName() !== "")
|
|
|
|
source: "qrc:/gpt4all/icons/regenerate.svg"
|
|
|
|
backgroundColor: theme.textColor
|
|
|
|
backgroundColorHovered: theme.styledTextColor
|
|
|
|
onClicked: {
|
|
|
|
if (currentChat.isModelLoaded)
|
|
|
|
currentChat.forceReloadModel();
|
|
|
|
else
|
|
|
|
currentChat.reloadModel();
|
|
|
|
}
|
|
|
|
ToolTip.text: qsTr("Reload the currently loaded model")
|
|
|
|
ToolTip.visible: hovered
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMiniButton {
|
|
|
|
id: ejectButton
|
|
|
|
Layout.alignment: Qt.AlignCenter
|
|
|
|
visible: currentChat.isModelLoaded && !currentChat.isCurrentlyLoading
|
|
|
|
source: "qrc:/gpt4all/icons/eject.svg"
|
|
|
|
backgroundColor: theme.textColor
|
|
|
|
backgroundColorHovered: theme.styledTextColor
|
|
|
|
onClicked: {
|
|
|
|
currentChat.forceUnloadModel();
|
|
|
|
}
|
|
|
|
ToolTip.text: qsTr("Eject the currently loaded model")
|
|
|
|
ToolTip.visible: hovered
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Text {
|
2024-06-30 19:26:53 -04:00
|
|
|
Layout.maximumWidth: 520
|
2024-06-29 18:00:52 -04:00
|
|
|
id: comboBoxText
|
|
|
|
leftPadding: 10
|
|
|
|
rightPadding: 10
|
|
|
|
text: {
|
|
|
|
if (ModelList.selectableModels.count === 0)
|
|
|
|
return qsTr("No model installed...")
|
|
|
|
if (currentChat.modelLoadingError !== "")
|
|
|
|
return qsTr("Model loading error...")
|
|
|
|
if (currentChat.trySwitchContextInProgress === 1)
|
|
|
|
return qsTr("Waiting for model...")
|
|
|
|
if (currentChat.trySwitchContextInProgress === 2)
|
|
|
|
return qsTr("Switching context...")
|
|
|
|
if (currentModelName() === "")
|
|
|
|
return qsTr("Choose a model...")
|
|
|
|
if (currentChat.modelLoadingPercentage === 0.0)
|
|
|
|
return qsTr("Reload \u00B7 ") + currentModelName()
|
|
|
|
if (currentChat.isCurrentlyLoading)
|
|
|
|
return qsTr("Loading \u00B7 ") + currentModelName()
|
|
|
|
return currentModelName()
|
|
|
|
}
|
|
|
|
font.pixelSize: theme.fontSizeLarger
|
|
|
|
color: theme.iconBackgroundLight
|
|
|
|
verticalAlignment: Text.AlignVCenter
|
|
|
|
horizontalAlignment: Text.AlignHCenter
|
|
|
|
elide: Text.ElideRight
|
|
|
|
}
|
|
|
|
Item {
|
|
|
|
Layout.minimumWidth: updown.width
|
|
|
|
Layout.minimumHeight: updown.height
|
|
|
|
Image {
|
|
|
|
id: updown
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
sourceSize.width: comboBoxText.font.pixelSize
|
|
|
|
sourceSize.height: comboBoxText.font.pixelSize
|
|
|
|
mipmap: true
|
|
|
|
visible: false
|
|
|
|
source: "qrc:/gpt4all/icons/up_down.svg"
|
|
|
|
}
|
|
|
|
|
|
|
|
ColorOverlay {
|
|
|
|
anchors.fill: updown
|
|
|
|
source: updown
|
|
|
|
color: comboBoxText.color
|
|
|
|
}
|
|
|
|
}
|
2024-04-18 14:52:29 -04:00
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
2024-04-18 14:52:29 -04:00
|
|
|
delegate: ItemDelegate {
|
|
|
|
id: comboItemDelegate
|
2024-06-26 14:39:39 -04:00
|
|
|
width: comboItemPopup.width
|
2024-04-18 14:52:29 -04:00
|
|
|
contentItem: Text {
|
|
|
|
text: name
|
|
|
|
color: theme.textColor
|
|
|
|
font: comboBox.font
|
|
|
|
elide: Text.ElideRight
|
|
|
|
verticalAlignment: Text.AlignVCenter
|
|
|
|
}
|
|
|
|
background: Rectangle {
|
2024-06-29 18:00:52 -04:00
|
|
|
color: highlighted ? theme.lightContrast : theme.darkContrast
|
2024-04-18 14:52:29 -04:00
|
|
|
}
|
|
|
|
highlighted: comboBox.highlightedIndex === index
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
2024-06-29 18:00:52 -04:00
|
|
|
indicator: Item {
|
|
|
|
}
|
2024-06-26 14:39:39 -04:00
|
|
|
popup: Popup {
|
|
|
|
id: comboItemPopup
|
|
|
|
y: comboBox.height - 1
|
|
|
|
width: comboBox.width
|
|
|
|
implicitHeight: Math.min(window.height - y, contentItem.implicitHeight)
|
|
|
|
padding: 0
|
|
|
|
contentItem: Rectangle {
|
|
|
|
implicitWidth: comboBox.width
|
|
|
|
implicitHeight: comboItemPopupListView.implicitHeight
|
|
|
|
ScrollView {
|
|
|
|
anchors.fill: parent
|
|
|
|
clip: true
|
|
|
|
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
|
|
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
|
|
|
ListView {
|
|
|
|
id: comboItemPopupListView
|
|
|
|
implicitHeight: contentHeight
|
|
|
|
model: comboBox.popup.visible ? comboBox.delegateModel : null
|
|
|
|
currentIndex: comboBox.highlightedIndex
|
|
|
|
ScrollIndicator.vertical: ScrollIndicator { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
background: Rectangle {
|
|
|
|
color: theme.black
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-18 14:52:29 -04:00
|
|
|
Accessible.name: currentModelName()
|
|
|
|
Accessible.description: qsTr("The top item is the current model")
|
|
|
|
onActivated: function (index) {
|
|
|
|
var newInfo = ModelList.modelInfo(comboBox.valueAt(index));
|
|
|
|
if (newInfo === currentChat.modelInfo) {
|
|
|
|
currentChat.reloadModel();
|
|
|
|
} else if (currentModelName() !== "" && chatModel.count !== 0) {
|
|
|
|
switchModelDialog.index = index;
|
|
|
|
switchModelDialog.open();
|
|
|
|
} else {
|
|
|
|
comboBox.changeModel(index);
|
|
|
|
}
|
2024-03-20 11:09:59 -04:00
|
|
|
}
|
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
Rectangle {
|
|
|
|
color: "transparent"
|
|
|
|
Layout.alignment: Qt.AlignRight
|
|
|
|
Layout.rightMargin: 30
|
|
|
|
Layout.fillWidth: true
|
|
|
|
Layout.preferredHeight: childrenRect.height
|
2024-06-29 18:00:52 -04:00
|
|
|
clip: true
|
2024-03-20 11:09:59 -04:00
|
|
|
|
2024-06-29 18:00:52 -04:00
|
|
|
MyButton {
|
|
|
|
id: collectionsButton
|
|
|
|
clip: true
|
2024-06-24 18:49:23 -04:00
|
|
|
anchors.right: parent.right
|
2024-06-29 18:00:52 -04:00
|
|
|
borderWidth: 0
|
|
|
|
backgroundColor: theme.collectionsButtonBackground
|
|
|
|
backgroundColorHovered: theme.collectionsButtonBackgroundHovered
|
|
|
|
backgroundRadius: 5
|
|
|
|
padding: 15
|
|
|
|
topPadding: 8
|
|
|
|
bottomPadding: 8
|
|
|
|
|
|
|
|
contentItem: RowLayout {
|
|
|
|
spacing: 10
|
|
|
|
Item {
|
|
|
|
visible: currentChat.collectionModel.count === 0
|
|
|
|
Layout.minimumWidth: collectionsImage.width
|
|
|
|
Layout.minimumHeight: collectionsImage.height
|
|
|
|
Image {
|
|
|
|
id: collectionsImage
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
sourceSize.width: 24
|
|
|
|
sourceSize.height: 24
|
|
|
|
mipmap: true
|
|
|
|
visible: false
|
|
|
|
source: "qrc:/gpt4all/icons/db.svg"
|
2024-06-24 18:49:23 -04:00
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-29 18:00:52 -04:00
|
|
|
ColorOverlay {
|
|
|
|
anchors.fill: collectionsImage
|
|
|
|
source: collectionsImage
|
2024-06-24 18:49:23 -04:00
|
|
|
color: theme.collectionsButtonForeground
|
|
|
|
}
|
2024-06-29 18:00:52 -04:00
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-29 18:00:52 -04:00
|
|
|
MyBusyIndicator {
|
|
|
|
visible: currentChat.collectionModel.updatingCount !== 0
|
|
|
|
color: theme.collectionsButtonProgress
|
|
|
|
size: 24
|
|
|
|
Layout.minimumWidth: 24
|
|
|
|
Layout.minimumHeight: 24
|
2024-06-24 18:49:23 -04:00
|
|
|
Text {
|
2024-06-29 18:00:52 -04:00
|
|
|
anchors.centerIn: parent
|
|
|
|
text: currentChat.collectionModel.updatingCount
|
2024-06-24 18:49:23 -04:00
|
|
|
color: theme.collectionsButtonForeground
|
2024-06-29 18:00:52 -04:00
|
|
|
font.pixelSize: 14 // fixed regardless of theme
|
2024-06-24 18:49:23 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-29 18:00:52 -04:00
|
|
|
Rectangle {
|
|
|
|
visible: currentChat.collectionModel.count !== 0
|
|
|
|
radius: 6
|
|
|
|
color: theme.collectionsButtonForeground
|
|
|
|
Layout.minimumWidth: collectionsImage.width
|
|
|
|
Layout.minimumHeight: collectionsImage.height
|
|
|
|
Text {
|
|
|
|
anchors.centerIn: parent
|
|
|
|
text: currentChat.collectionModel.count
|
|
|
|
color: theme.collectionsButtonText
|
|
|
|
font.pixelSize: 14 // fixed regardless of theme
|
|
|
|
}
|
|
|
|
}
|
2024-06-24 18:49:23 -04:00
|
|
|
|
2024-06-29 18:00:52 -04:00
|
|
|
Text {
|
|
|
|
text: qsTr("LocalDocs")
|
|
|
|
color: theme.collectionsButtonForeground
|
|
|
|
font.pixelSize: theme.fontSizeLarge
|
2024-06-24 18:49:23 -04:00
|
|
|
}
|
2024-06-29 18:00:52 -04:00
|
|
|
}
|
2024-06-24 18:49:23 -04:00
|
|
|
|
2024-06-29 18:00:52 -04:00
|
|
|
fontPixelSize: theme.fontSizeLarge
|
2024-06-24 18:49:23 -04:00
|
|
|
|
2024-06-29 18:00:52 -04:00
|
|
|
background: Rectangle {
|
|
|
|
radius: collectionsButton.backgroundRadius
|
|
|
|
// TODO(jared): either use collectionsButton-specific theming, or don't - this is inconsistent
|
|
|
|
color: conversation.state === "expanded" ? (
|
|
|
|
collectionsButton.hovered ? theme.lightButtonBackgroundHovered : theme.lightButtonBackground
|
|
|
|
) : (
|
|
|
|
collectionsButton.hovered ? theme.lighterButtonBackground : theme.lighterButtonBackgroundHovered
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
Accessible.name: qsTr("Add documents")
|
|
|
|
Accessible.description: qsTr("add collections of documents to the chat")
|
|
|
|
|
|
|
|
onClicked: {
|
|
|
|
conversation.toggleRightPanel()
|
2024-06-24 18:49:23 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-04-18 14:52:29 -04:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
Rectangle {
|
|
|
|
id: conversationDivider
|
|
|
|
anchors.top: header.bottom
|
|
|
|
anchors.left: parent.left
|
|
|
|
anchors.right: parent.right
|
|
|
|
color: theme.conversationDivider
|
2024-06-27 07:16:11 -04:00
|
|
|
height: 1
|
2024-04-18 14:52:29 -04:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
CollectionsDrawer {
|
|
|
|
id: collectionsDrawer
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.top: conversationDivider.bottom
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
width: Math.max(180, Math.min(600, 0.23 * window.width))
|
|
|
|
color: theme.conversationBackground
|
|
|
|
onAddDocsClicked: {
|
|
|
|
addCollectionViewRequested()
|
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
Rectangle {
|
|
|
|
id: conversation
|
|
|
|
color: theme.conversationBackground
|
|
|
|
anchors.left: parent.left
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
anchors.top: conversationDivider.bottom
|
|
|
|
state: "collapsed"
|
|
|
|
|
|
|
|
states: [
|
|
|
|
State {
|
|
|
|
name: "expanded"
|
|
|
|
AnchorChanges {
|
|
|
|
target: conversation
|
|
|
|
anchors.right: collectionsDrawer.left
|
|
|
|
}
|
|
|
|
},
|
|
|
|
State {
|
|
|
|
name: "collapsed"
|
|
|
|
AnchorChanges {
|
|
|
|
target: conversation
|
|
|
|
anchors.right: parent.right
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
function toggleRightPanel() {
|
|
|
|
if (conversation.state === "expanded") {
|
|
|
|
conversation.state = "collapsed";
|
|
|
|
} else {
|
|
|
|
conversation.state = "expanded";
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
transitions: Transition {
|
|
|
|
AnchorAnimation {
|
|
|
|
easing.type: Easing.InOutQuad
|
|
|
|
duration: 300
|
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
ScrollView {
|
|
|
|
id: scrollView
|
|
|
|
anchors.left: parent.left
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.top: parent.top
|
|
|
|
anchors.bottom: !currentChat.isServer ? textInputView.top : parent.bottom
|
|
|
|
anchors.bottomMargin: !currentChat.isServer ? 30 : 0
|
|
|
|
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
|
2024-03-13 19:57:05 -04:00
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
anchors.fill: parent
|
2024-06-24 18:49:23 -04:00
|
|
|
color: currentChat.isServer ? theme.black : theme.conversationBackground
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
Rectangle {
|
|
|
|
id: homePage
|
|
|
|
color: "transparent"
|
|
|
|
anchors.fill: parent
|
2024-06-28 20:34:03 -04:00
|
|
|
visible: !currentChat.isModelLoaded && (ModelList.selectableModels.count === 0 || currentModelName() === "") && !currentChat.isServer
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
ColumnLayout {
|
2024-06-28 20:34:03 -04:00
|
|
|
visible: ModelList.selectableModels.count !== 0
|
2024-06-24 18:49:23 -04:00
|
|
|
id: modelInstalledLabel
|
|
|
|
anchors.centerIn: parent
|
|
|
|
spacing: 0
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
Rectangle {
|
|
|
|
Layout.alignment: Qt.AlignCenter
|
|
|
|
Layout.preferredWidth: image.width
|
|
|
|
Layout.preferredHeight: image.height
|
|
|
|
color: "transparent"
|
|
|
|
|
|
|
|
Image {
|
|
|
|
id: image
|
|
|
|
anchors.centerIn: parent
|
|
|
|
sourceSize.width: 160
|
|
|
|
sourceSize.height: 110
|
|
|
|
fillMode: Image.PreserveAspectFit
|
|
|
|
mipmap: true
|
|
|
|
visible: false
|
|
|
|
source: "qrc:/gpt4all/icons/nomic_logo.svg"
|
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
ColorOverlay {
|
|
|
|
anchors.fill: image
|
|
|
|
source: image
|
|
|
|
color: theme.containerBackground
|
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
MyButton {
|
2024-06-28 14:19:20 -04:00
|
|
|
id: loadDefaultModelButton
|
2024-06-28 20:34:03 -04:00
|
|
|
visible: ModelList.selectableModels.count !== 0
|
2024-06-24 18:49:23 -04:00
|
|
|
anchors.top: modelInstalledLabel.bottom
|
|
|
|
anchors.topMargin: 50
|
|
|
|
anchors.horizontalCenter: modelInstalledLabel.horizontalCenter
|
|
|
|
rightPadding: 60
|
|
|
|
leftPadding: 60
|
2024-06-28 14:29:31 -04:00
|
|
|
property string defaultModel: ""
|
2024-06-28 14:19:20 -04:00
|
|
|
function updateDefaultModel() {
|
|
|
|
var i = comboBox.find(MySettings.userDefaultModel)
|
|
|
|
if (i !== -1) {
|
|
|
|
defaultModel = comboBox.valueAt(i);
|
|
|
|
} else {
|
|
|
|
defaultModel = comboBox.valueAt(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
text: qsTr("Load \u00B7 ") + defaultModel + qsTr(" (default) \u2192");
|
2024-06-24 18:49:23 -04:00
|
|
|
onClicked: {
|
|
|
|
var i = comboBox.find(MySettings.userDefaultModel)
|
|
|
|
if (i !== -1) {
|
|
|
|
comboBox.changeModel(i);
|
|
|
|
} else {
|
|
|
|
comboBox.changeModel(0);
|
|
|
|
}
|
|
|
|
}
|
2024-06-28 14:19:20 -04:00
|
|
|
|
|
|
|
// This requires a bit of work because apparently the combobox valueAt
|
|
|
|
// function only works after the combobox component is loaded so we have
|
|
|
|
// to use our own component loaded to make this work along with a signal
|
|
|
|
// from MySettings for when the setting for user default model changes
|
|
|
|
Connections {
|
|
|
|
target: MySettings
|
|
|
|
function onUserDefaultModelChanged() {
|
|
|
|
loadDefaultModelButton.updateDefaultModel()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
|
|
loadDefaultModelButton.updateDefaultModel()
|
|
|
|
}
|
2024-06-24 18:49:23 -04:00
|
|
|
Accessible.role: Accessible.Button
|
|
|
|
Accessible.name: qsTr("Load the default model")
|
|
|
|
Accessible.description: qsTr("Loads the default model which can be changed in settings")
|
2024-03-30 14:02:27 -04:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
ColumnLayout {
|
|
|
|
id: noModelInstalledLabel
|
2024-06-28 20:34:03 -04:00
|
|
|
visible: ModelList.selectableModels.count === 0
|
2024-06-24 18:49:23 -04:00
|
|
|
anchors.centerIn: parent
|
|
|
|
spacing: 0
|
2024-03-30 14:02:27 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
Text {
|
|
|
|
Layout.alignment: Qt.AlignCenter
|
|
|
|
text: qsTr("No Model Installed")
|
|
|
|
color: theme.mutedLightTextColor
|
|
|
|
font.pixelSize: theme.fontSizeBannerSmall
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
2024-06-24 18:49:23 -04:00
|
|
|
|
|
|
|
Text {
|
|
|
|
Layout.topMargin: 15
|
|
|
|
horizontalAlignment: Qt.AlignHCenter
|
|
|
|
color: theme.mutedLighterTextColor
|
|
|
|
text: qsTr("GPT4All requires that you install at least one\nmodel to get started")
|
|
|
|
font.pixelSize: theme.fontSizeLarge
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
2024-06-24 18:49:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
MyButton {
|
2024-06-28 20:34:03 -04:00
|
|
|
visible: ModelList.selectableModels.count === 0
|
2024-06-24 18:49:23 -04:00
|
|
|
anchors.top: noModelInstalledLabel.bottom
|
|
|
|
anchors.topMargin: 50
|
|
|
|
anchors.horizontalCenter: noModelInstalledLabel.horizontalCenter
|
|
|
|
rightPadding: 60
|
|
|
|
leftPadding: 60
|
|
|
|
text: qsTr("Install a Model")
|
2024-03-13 19:57:05 -04:00
|
|
|
onClicked: {
|
2024-06-24 18:49:23 -04:00
|
|
|
addModelViewRequested();
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
2024-06-24 18:49:23 -04:00
|
|
|
Accessible.role: Accessible.Button
|
|
|
|
Accessible.name: qsTr("Shows the add model view")
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
ColumnLayout {
|
|
|
|
anchors.fill: parent
|
2024-06-28 20:34:03 -04:00
|
|
|
visible: ModelList.selectableModels.count !== 0 && chatModel.count !== 0
|
2024-06-24 18:49:23 -04:00
|
|
|
ListView {
|
|
|
|
id: listView
|
|
|
|
Layout.maximumWidth: 1280
|
|
|
|
Layout.fillHeight: true
|
|
|
|
Layout.fillWidth: true
|
|
|
|
Layout.margins: 20
|
|
|
|
Layout.leftMargin: 50
|
|
|
|
Layout.rightMargin: 50
|
|
|
|
Layout.alignment: Qt.AlignHCenter
|
|
|
|
spacing: 25
|
|
|
|
model: chatModel
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
ScrollBar.vertical: ScrollBar {
|
|
|
|
policy: ScrollBar.AsNeeded
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
Accessible.role: Accessible.List
|
|
|
|
Accessible.name: qsTr("Conversation with the model")
|
|
|
|
Accessible.description: qsTr("prompt / response pairs from the conversation")
|
|
|
|
|
|
|
|
delegate: GridLayout {
|
2024-06-27 00:36:45 -04:00
|
|
|
width: listView.contentItem.width - 15
|
2024-06-26 22:00:48 -04:00
|
|
|
rows: 3
|
2024-06-24 18:49:23 -04:00
|
|
|
columns: 2
|
|
|
|
|
|
|
|
Item {
|
|
|
|
Layout.row: 0
|
|
|
|
Layout.column: 0
|
|
|
|
Layout.alignment: Qt.AlignTop
|
|
|
|
Layout.preferredWidth: 38
|
|
|
|
Layout.preferredHeight: 38
|
|
|
|
Image {
|
|
|
|
id: logo
|
|
|
|
width: 38
|
|
|
|
height: 38
|
|
|
|
sourceSize.width: 64
|
|
|
|
sourceSize.height: 64
|
|
|
|
fillMode: Image.PreserveAspectFit
|
|
|
|
mipmap: true
|
|
|
|
visible: false
|
|
|
|
source: name !== qsTr("Response: ") ? "qrc:/gpt4all/icons/you.svg" : "qrc:/gpt4all/icons/alt_logo.svg"
|
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
ColorOverlay {
|
|
|
|
anchors.fill: logo
|
|
|
|
source: logo
|
|
|
|
color: theme.conversationHeader
|
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
Item {
|
|
|
|
Layout.row: 0
|
|
|
|
Layout.column: 1
|
|
|
|
Layout.fillWidth: true
|
|
|
|
Layout.preferredHeight: 38
|
|
|
|
RowLayout {
|
|
|
|
spacing: 5
|
|
|
|
anchors.left: parent.left
|
|
|
|
anchors.top: parent.top
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
|
|
|
|
TextArea {
|
|
|
|
text: name === qsTr("Response: ") ? qsTr("GPT4All") : qsTr("You")
|
|
|
|
padding: 0
|
|
|
|
font.pixelSize: theme.fontSizeLarger
|
|
|
|
font.bold: true
|
|
|
|
color: theme.conversationHeader
|
|
|
|
readOnly: true
|
|
|
|
}
|
|
|
|
Text {
|
|
|
|
visible: name === qsTr("Response: ")
|
|
|
|
font.pixelSize: theme.fontSizeLarger
|
|
|
|
text: currentModelName()
|
|
|
|
color: theme.mutedTextColor
|
|
|
|
}
|
|
|
|
RowLayout {
|
|
|
|
visible: (currentResponse ? true : false) && ((value === "" && currentChat.responseInProgress) || currentChat.isRecalc)
|
|
|
|
MyBusyIndicator {
|
|
|
|
size: 24
|
|
|
|
color: theme.conversationProgress
|
|
|
|
Accessible.role: Accessible.Animation
|
|
|
|
Accessible.name: qsTr("Busy indicator")
|
|
|
|
Accessible.description: qsTr("The model is thinking")
|
|
|
|
}
|
|
|
|
Text {
|
|
|
|
color: theme.mutedTextColor
|
|
|
|
font.pixelSize: theme.fontSizeLarger
|
|
|
|
text: {
|
|
|
|
if (currentChat.isRecalc)
|
|
|
|
return qsTr("recalculating context ...");
|
|
|
|
switch (currentChat.responseState) {
|
|
|
|
case Chat.ResponseStopped: return qsTr("response stopped ...");
|
|
|
|
case Chat.LocalDocsRetrieval: return qsTr("retrieving localdocs: ") + currentChat.collectionList.join(", ") + " ...";
|
|
|
|
case Chat.LocalDocsProcessing: return qsTr("searching localdocs: ") + currentChat.collectionList.join(", ") + " ...";
|
|
|
|
case Chat.PromptProcessing: return qsTr("processing ...")
|
|
|
|
case Chat.ResponseGeneration: return qsTr("generating response ...");
|
|
|
|
default: return ""; // handle unexpected values
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-05-09 09:35:54 -04:00
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
ColumnLayout {
|
2024-06-26 22:00:48 -04:00
|
|
|
Layout.row: 1
|
2024-06-24 18:49:23 -04:00
|
|
|
Layout.column: 1
|
|
|
|
Layout.fillWidth: true
|
|
|
|
TextArea {
|
|
|
|
id: myTextArea
|
|
|
|
text: value
|
|
|
|
Layout.fillWidth: true
|
|
|
|
padding: 0
|
|
|
|
color: {
|
|
|
|
if (!currentChat.isServer)
|
|
|
|
return theme.textColor
|
|
|
|
if (name === qsTr("Response: "))
|
|
|
|
return theme.white
|
|
|
|
return theme.black
|
|
|
|
}
|
|
|
|
wrapMode: Text.WordWrap
|
|
|
|
textFormat: TextEdit.PlainText
|
|
|
|
focus: false
|
|
|
|
readOnly: true
|
|
|
|
font.pixelSize: theme.fontSizeLarge
|
|
|
|
cursorVisible: currentResponse ? currentChat.responseInProgress : false
|
|
|
|
cursorPosition: text.length
|
|
|
|
TapHandler {
|
|
|
|
id: tapHandler
|
|
|
|
onTapped: function(eventPoint, button) {
|
|
|
|
var clickedPos = myTextArea.positionAt(eventPoint.position.x, eventPoint.position.y);
|
2024-06-28 11:10:20 -04:00
|
|
|
var success = textProcessor.tryCopyAtPosition(clickedPos);
|
2024-06-24 18:49:23 -04:00
|
|
|
if (success)
|
|
|
|
copyCodeMessage.open();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
id: conversationMouseArea
|
|
|
|
anchors.fill: parent
|
|
|
|
acceptedButtons: Qt.RightButton
|
|
|
|
|
|
|
|
onClicked: (mouse) => {
|
|
|
|
if (mouse.button === Qt.RightButton) {
|
|
|
|
conversationContextMenu.x = conversationMouseArea.mouseX
|
|
|
|
conversationContextMenu.y = conversationMouseArea.mouseY
|
|
|
|
conversationContextMenu.open()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-28 12:54:12 -04:00
|
|
|
onLinkActivated: function(link) {
|
|
|
|
if (!currentResponse || !currentChat.responseInProgress)
|
|
|
|
Qt.openUrlExternally(link)
|
|
|
|
}
|
|
|
|
|
|
|
|
onLinkHovered: function (link) {
|
|
|
|
if (!currentResponse || !currentChat.responseInProgress)
|
2024-06-28 13:34:26 -04:00
|
|
|
statusBar.externalHoveredLink = link
|
2024-06-28 12:54:12 -04:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
Menu {
|
|
|
|
id: conversationContextMenu
|
|
|
|
MenuItem {
|
|
|
|
text: qsTr("Copy")
|
|
|
|
enabled: myTextArea.selectedText !== ""
|
|
|
|
height: enabled ? implicitHeight : 0
|
|
|
|
onTriggered: myTextArea.copy()
|
|
|
|
}
|
|
|
|
MenuItem {
|
|
|
|
text: qsTr("Copy Message")
|
|
|
|
enabled: myTextArea.selectedText === ""
|
|
|
|
height: enabled ? implicitHeight : 0
|
|
|
|
onTriggered: {
|
|
|
|
myTextArea.selectAll()
|
|
|
|
myTextArea.copy()
|
|
|
|
myTextArea.deselect()
|
|
|
|
}
|
|
|
|
}
|
2024-06-28 11:10:20 -04:00
|
|
|
MenuItem {
|
|
|
|
text: textProcessor.shouldProcessText ? qsTr("Disable markdown") : qsTr("Enable markdown")
|
|
|
|
height: enabled ? implicitHeight : 0
|
|
|
|
onTriggered: {
|
|
|
|
textProcessor.shouldProcessText = !textProcessor.shouldProcessText;
|
|
|
|
myTextArea.text = value
|
|
|
|
}
|
|
|
|
}
|
2024-06-24 18:49:23 -04:00
|
|
|
}
|
|
|
|
|
2024-06-28 11:10:20 -04:00
|
|
|
ChatViewTextProcessor {
|
|
|
|
id: textProcessor
|
2024-06-30 19:15:01 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function resetChatViewTextProcessor() {
|
|
|
|
textProcessor.fontPixelSize = myTextArea.font.pixelSize
|
|
|
|
textProcessor.codeColors.defaultColor = theme.codeDefaultColor
|
|
|
|
textProcessor.codeColors.keywordColor = theme.codeKeywordColor
|
|
|
|
textProcessor.codeColors.functionColor = theme.codeFunctionColor
|
|
|
|
textProcessor.codeColors.functionCallColor = theme.codeFunctionCallColor
|
|
|
|
textProcessor.codeColors.commentColor = theme.codeCommentColor
|
|
|
|
textProcessor.codeColors.stringColor = theme.codeStringColor
|
|
|
|
textProcessor.codeColors.numberColor = theme.codeNumberColor
|
|
|
|
textProcessor.codeColors.headerColor = theme.codeHeaderColor
|
|
|
|
textProcessor.codeColors.backgroundColor = theme.codeBackgroundColor
|
|
|
|
textProcessor.textDocument = textDocument
|
2024-06-30 20:55:42 -04:00
|
|
|
chatModel.forceUpdate(index); // called to trigger a reprocessing of the text
|
2024-06-24 18:49:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
Component.onCompleted: {
|
2024-06-30 19:15:01 -04:00
|
|
|
resetChatViewTextProcessor();
|
|
|
|
}
|
|
|
|
|
|
|
|
Connections {
|
|
|
|
target: MySettings
|
|
|
|
function onFontSizeChanged() {
|
|
|
|
myTextArea.resetChatViewTextProcessor();
|
|
|
|
}
|
|
|
|
function onChatThemeChanged() {
|
|
|
|
myTextArea.resetChatViewTextProcessor();
|
|
|
|
}
|
2024-06-24 18:49:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
Accessible.role: Accessible.Paragraph
|
|
|
|
Accessible.name: text
|
|
|
|
Accessible.description: name === qsTr("Response: ") ? "The response by the model" : "The prompt by the user"
|
|
|
|
}
|
|
|
|
|
|
|
|
ThumbsDownDialog {
|
|
|
|
id: thumbsDownDialog
|
|
|
|
property point globalPoint: mapFromItem(window,
|
|
|
|
window.width / 2 - width / 2,
|
|
|
|
window.height / 2 - height / 2)
|
|
|
|
x: globalPoint.x
|
|
|
|
y: globalPoint.y
|
2024-06-26 16:36:00 -04:00
|
|
|
width: 640
|
|
|
|
height: 300
|
2024-06-24 18:49:23 -04:00
|
|
|
property string text: value
|
|
|
|
response: newResponse === undefined || newResponse === "" ? text : newResponse
|
|
|
|
onAccepted: {
|
|
|
|
var responseHasChanged = response !== text && response !== newResponse
|
|
|
|
if (thumbsDownState && !thumbsUpState && !responseHasChanged)
|
|
|
|
return
|
|
|
|
|
|
|
|
chatModel.updateNewResponse(index, response)
|
|
|
|
chatModel.updateThumbsUpState(index, false)
|
|
|
|
chatModel.updateThumbsDownState(index, true)
|
|
|
|
Network.sendConversation(currentChat.id, getConversationJson());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Column {
|
|
|
|
Layout.alignment: Qt.AlignRight
|
|
|
|
Layout.rightMargin: 15
|
|
|
|
visible: name === qsTr("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
|
2024-06-30 15:10:19 -04:00
|
|
|
anchors.topMargin: 3
|
2024-06-24 18:49:23 -04:00
|
|
|
anchors.left: thumbsUp.right
|
2024-06-30 15:10:19 -04:00
|
|
|
anchors.leftMargin: 3
|
2024-06-24 18:49:23 -04:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
}
|
2024-06-26 22:00:48 -04:00
|
|
|
|
|
|
|
Item {
|
|
|
|
Layout.row: 2
|
|
|
|
Layout.column: 1
|
|
|
|
Layout.topMargin: 5
|
|
|
|
Layout.alignment: Qt.AlignVCenter
|
|
|
|
Layout.preferredWidth: childrenRect.width
|
|
|
|
Layout.preferredHeight: childrenRect.height
|
|
|
|
visible: consolidatedSources.length !== 0 && MySettings.localDocsShowReferences && (!currentResponse || !currentChat.responseInProgress)
|
|
|
|
|
|
|
|
MyButton {
|
|
|
|
backgroundColor: theme.sourcesBackground
|
|
|
|
backgroundColorHovered: theme.sourcesBackgroundHovered
|
|
|
|
contentItem: RowLayout {
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
|
|
|
Item {
|
|
|
|
Layout.preferredWidth: 24
|
|
|
|
Layout.preferredHeight: 24
|
|
|
|
|
|
|
|
Image {
|
|
|
|
id: sourcesIcon
|
|
|
|
visible: false
|
|
|
|
anchors.fill: parent
|
|
|
|
sourceSize.width: 24
|
|
|
|
sourceSize.height: 24
|
|
|
|
mipmap: true
|
|
|
|
source: "qrc:/gpt4all/icons/db.svg"
|
|
|
|
}
|
|
|
|
|
|
|
|
ColorOverlay {
|
|
|
|
anchors.fill: sourcesIcon
|
|
|
|
source: sourcesIcon
|
|
|
|
color: theme.textColor
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-28 20:34:03 -04:00
|
|
|
Text {
|
|
|
|
text: qsTr("%1 Sources").arg(consolidatedSources.length)
|
2024-06-26 22:00:48 -04:00
|
|
|
padding: 0
|
|
|
|
font.pixelSize: theme.fontSizeLarge
|
|
|
|
font.bold: true
|
|
|
|
color: theme.styledTextColor
|
|
|
|
}
|
|
|
|
|
|
|
|
Item {
|
|
|
|
Layout.preferredWidth: caret.width
|
|
|
|
Layout.preferredHeight: caret.height
|
|
|
|
Image {
|
|
|
|
id: caret
|
|
|
|
anchors.centerIn: parent
|
|
|
|
visible: false
|
|
|
|
sourceSize.width: theme.fontSizeLarge
|
|
|
|
sourceSize.height: theme.fontSizeLarge
|
|
|
|
mipmap: true
|
|
|
|
source: {
|
|
|
|
if (sourcesLayout.state === "collapsed")
|
|
|
|
return "qrc:/gpt4all/icons/caret_right.svg";
|
|
|
|
else
|
|
|
|
return "qrc:/gpt4all/icons/caret_down.svg";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ColorOverlay {
|
|
|
|
anchors.fill: caret
|
|
|
|
source: caret
|
|
|
|
color: theme.textColor
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onClicked: {
|
|
|
|
if (sourcesLayout.state === "collapsed")
|
|
|
|
sourcesLayout.state = "expanded";
|
|
|
|
else
|
|
|
|
sourcesLayout.state = "collapsed";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ColumnLayout {
|
|
|
|
id: sourcesLayout
|
|
|
|
Layout.row: 3
|
|
|
|
Layout.column: 1
|
2024-06-28 20:34:03 -04:00
|
|
|
Layout.topMargin: 5
|
2024-06-26 22:00:48 -04:00
|
|
|
visible: consolidatedSources.length !== 0 && MySettings.localDocsShowReferences && (!currentResponse || !currentChat.responseInProgress)
|
|
|
|
clip: true
|
|
|
|
Layout.fillWidth: true
|
|
|
|
Layout.preferredHeight: 0
|
|
|
|
state: "collapsed"
|
|
|
|
states: [
|
|
|
|
State {
|
|
|
|
name: "expanded"
|
|
|
|
PropertyChanges { target: sourcesLayout; Layout.preferredHeight: childrenRect.height }
|
|
|
|
},
|
|
|
|
State {
|
|
|
|
name: "collapsed"
|
|
|
|
PropertyChanges { target: sourcesLayout; Layout.preferredHeight: 0 }
|
|
|
|
}
|
|
|
|
]
|
|
|
|
|
|
|
|
transitions: [
|
|
|
|
Transition {
|
|
|
|
SequentialAnimation {
|
|
|
|
PropertyAnimation {
|
|
|
|
target: sourcesLayout
|
|
|
|
property: "Layout.preferredHeight"
|
|
|
|
duration: 300
|
|
|
|
easing.type: Easing.InOutQuad
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
|
|
|
|
Flow {
|
|
|
|
Layout.fillWidth: true
|
|
|
|
spacing: 10
|
|
|
|
visible: consolidatedSources.length !== 0
|
|
|
|
Repeater {
|
|
|
|
model: consolidatedSources
|
|
|
|
|
|
|
|
delegate: Rectangle {
|
|
|
|
radius: 10
|
|
|
|
color: ma.containsMouse ? theme.sourcesBackgroundHovered : theme.sourcesBackground
|
|
|
|
width: 200
|
|
|
|
height: 75
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
id: ma
|
|
|
|
enabled: modelData.path !== ""
|
|
|
|
anchors.fill: parent
|
|
|
|
hoverEnabled: true
|
|
|
|
onClicked: function() {
|
|
|
|
Qt.openUrlExternally(modelData.fileUri)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
id: debugTooltip
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
width: 24
|
|
|
|
height: 24
|
|
|
|
color: "transparent"
|
|
|
|
ToolTip {
|
|
|
|
parent: debugTooltip
|
|
|
|
visible: debugMouseArea.containsMouse
|
|
|
|
text: modelData.text
|
|
|
|
contentWidth: 900
|
|
|
|
delay: 500
|
|
|
|
}
|
|
|
|
MouseArea {
|
|
|
|
id: debugMouseArea
|
|
|
|
anchors.fill: parent
|
|
|
|
hoverEnabled: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ColumnLayout {
|
|
|
|
anchors.left: parent.left
|
|
|
|
anchors.top: parent.top
|
|
|
|
anchors.margins: 10
|
|
|
|
spacing: 0
|
|
|
|
RowLayout {
|
|
|
|
id: title
|
|
|
|
spacing: 5
|
|
|
|
Layout.maximumWidth: 180
|
|
|
|
Item {
|
|
|
|
Layout.preferredWidth: 24
|
|
|
|
Layout.preferredHeight: 24
|
|
|
|
Image {
|
|
|
|
id: fileIcon
|
|
|
|
anchors.fill: parent
|
|
|
|
visible: false
|
|
|
|
sourceSize.width: 24
|
|
|
|
sourceSize.height: 24
|
|
|
|
mipmap: true
|
|
|
|
source: {
|
|
|
|
if (modelData.file.endsWith(".txt"))
|
|
|
|
return "qrc:/gpt4all/icons/file-txt.svg"
|
|
|
|
else if (modelData.file.endsWith(".pdf"))
|
|
|
|
return "qrc:/gpt4all/icons/file-pdf.svg"
|
|
|
|
else if (modelData.file.endsWith(".md"))
|
|
|
|
return "qrc:/gpt4all/icons/file-md.svg"
|
|
|
|
else
|
|
|
|
return "qrc:/gpt4all/icons/file.svg"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ColorOverlay {
|
|
|
|
anchors.fill: fileIcon
|
|
|
|
source: fileIcon
|
|
|
|
color: theme.textColor
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Text {
|
|
|
|
Layout.maximumWidth: 156
|
|
|
|
text: modelData.collection !== "" ? modelData.collection : qsTr("LocalDocs")
|
|
|
|
font.pixelSize: theme.fontSizeLarge
|
|
|
|
font.bold: true
|
|
|
|
color: theme.styledTextColor
|
|
|
|
elide: Qt.ElideRight
|
|
|
|
}
|
|
|
|
Rectangle {
|
|
|
|
Layout.fillWidth: true
|
|
|
|
color: "transparent"
|
|
|
|
height: 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Text {
|
|
|
|
Layout.fillHeight: true
|
|
|
|
Layout.maximumWidth: 180
|
|
|
|
Layout.maximumHeight: 55 - title.height
|
|
|
|
text: modelData.file
|
|
|
|
color: theme.textColor
|
|
|
|
font.pixelSize: theme.fontSizeSmall
|
|
|
|
elide: Qt.ElideRight
|
|
|
|
wrapMode: Text.WrapAnywhere
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-06-24 18:49:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
property bool shouldAutoScroll: true
|
|
|
|
property bool isAutoScrolling: false
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
Connections {
|
|
|
|
target: currentChat
|
|
|
|
function onResponseChanged() {
|
2024-06-26 22:00:48 -04:00
|
|
|
listView.scrollToEnd()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function scrollToEnd() {
|
|
|
|
if (listView.shouldAutoScroll) {
|
|
|
|
listView.isAutoScrolling = true
|
|
|
|
listView.positionViewAtEnd()
|
|
|
|
listView.isAutoScrolling = false
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
onContentYChanged: {
|
|
|
|
if (!isAutoScrolling)
|
|
|
|
shouldAutoScroll = atYEnd
|
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
Component.onCompleted: {
|
|
|
|
shouldAutoScroll = true
|
|
|
|
positionViewAtEnd()
|
|
|
|
}
|
|
|
|
|
|
|
|
footer: Item {
|
|
|
|
id: bottomPadding
|
|
|
|
width: parent.width
|
2024-06-30 23:27:30 -04:00
|
|
|
height: 0
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-06-24 18:49:23 -04:00
|
|
|
}
|
2024-06-30 15:10:19 -04:00
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-30 15:10:19 -04:00
|
|
|
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
|
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-30 15:10:19 -04:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-30 15:10:19 -04:00
|
|
|
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);
|
2024-06-24 18:49:23 -04:00
|
|
|
currentChat.regenerateResponse()
|
|
|
|
if (chatModel.count) {
|
|
|
|
if (listElement.name === qsTr("Response: ")) {
|
|
|
|
chatModel.updateCurrentResponse(index, true);
|
|
|
|
chatModel.updateStopped(index, false);
|
|
|
|
chatModel.updateThumbsUpState(index, false);
|
|
|
|
chatModel.updateThumbsDownState(index, false);
|
|
|
|
chatModel.updateNewResponse(index, "");
|
|
|
|
currentChat.prompt(listElement.prompt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-06-30 15:10:19 -04:00
|
|
|
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")
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
}
|
2024-06-30 15:10:19 -04:00
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
|
2024-06-30 15:10:19 -04:00
|
|
|
Item {
|
|
|
|
id: conversationTrayButton
|
|
|
|
anchors.bottom: textInputView.top
|
|
|
|
anchors.horizontalCenter: textInputView.horizontalCenter
|
2024-06-30 19:15:01 -04:00
|
|
|
width: 40
|
2024-06-30 15:10:19 -04:00
|
|
|
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
|
2024-06-30 19:15:01 -04:00
|
|
|
anchors.centerIn: parent
|
2024-06-30 15:10:19 -04:00
|
|
|
horizontalAlignment: Qt.AlignHCenter
|
|
|
|
leftPadding: 5
|
|
|
|
rightPadding: 5
|
|
|
|
text: "\u00B7\u00B7\u00B7"
|
|
|
|
color: theme.textColor
|
2024-06-30 19:15:01 -04:00
|
|
|
font.pixelSize: 30 // fixed size
|
|
|
|
font.bold: true
|
2024-06-30 15:10:19 -04:00
|
|
|
}
|
|
|
|
}
|
2024-06-24 18:49:23 -04:00
|
|
|
|
2024-06-30 15:10:19 -04:00
|
|
|
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();
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
2024-06-30 15:10:19 -04:00
|
|
|
|
|
|
|
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")
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
Text {
|
2024-06-28 12:54:12 -04:00
|
|
|
id: statusBar
|
2024-06-28 13:34:26 -04:00
|
|
|
property string externalHoveredLink: ""
|
2024-06-24 18:49:23 -04:00
|
|
|
anchors.top: textInputView.bottom
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.rightMargin: 30
|
|
|
|
anchors.left: parent.left
|
|
|
|
anchors.leftMargin: 30
|
|
|
|
horizontalAlignment: Qt.AlignRight
|
|
|
|
verticalAlignment: Qt.AlignVCenter
|
|
|
|
color: theme.mutedTextColor
|
2024-06-28 13:34:26 -04:00
|
|
|
visible: currentChat.tokenSpeed !== "" || externalHoveredLink !== ""
|
2024-06-24 18:49:23 -04:00
|
|
|
elide: Text.ElideRight
|
|
|
|
wrapMode: Text.WordWrap
|
2024-06-26 15:26:27 -04:00
|
|
|
text: {
|
2024-06-28 13:34:26 -04:00
|
|
|
if (externalHoveredLink !== "")
|
|
|
|
return externalHoveredLink
|
2024-06-28 12:54:12 -04:00
|
|
|
|
2024-06-26 15:26:27 -04:00
|
|
|
const segments = [currentChat.tokenSpeed];
|
|
|
|
const device = currentChat.device;
|
|
|
|
const backend = currentChat.deviceBackend;
|
|
|
|
if (device !== null) { // device is null if we have no model loaded
|
|
|
|
var deviceSegment = device;
|
|
|
|
if (backend === "CUDA" || backend === "Vulkan")
|
|
|
|
deviceSegment += ` (${backend})`;
|
|
|
|
segments.push(deviceSegment);
|
|
|
|
}
|
|
|
|
const fallbackReason = currentChat.fallbackReason;
|
|
|
|
if (fallbackReason !== null && fallbackReason !== "")
|
|
|
|
segments.push(fallbackReason);
|
|
|
|
return segments.join(" \u00B7 ");
|
|
|
|
}
|
2024-06-24 18:49:23 -04:00
|
|
|
font.pixelSize: theme.fontSizeSmaller
|
|
|
|
font.bold: true
|
|
|
|
}
|
|
|
|
|
|
|
|
RectangularGlow {
|
|
|
|
id: effect
|
2024-06-28 20:34:03 -04:00
|
|
|
visible: !currentChat.isServer && ModelList.selectableModels.count !== 0
|
2024-06-24 18:49:23 -04:00
|
|
|
anchors.fill: textInputView
|
|
|
|
glowRadius: 50
|
|
|
|
spread: 0
|
|
|
|
color: theme.sendGlow
|
|
|
|
cornerRadius: 10
|
|
|
|
opacity: 0.1
|
|
|
|
}
|
|
|
|
|
|
|
|
ScrollView {
|
|
|
|
id: textInputView
|
|
|
|
anchors.left: parent.left
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
anchors.margins: 30
|
|
|
|
anchors.leftMargin: Math.max((parent.width - 1310) / 2, 30)
|
|
|
|
anchors.rightMargin: Math.max((parent.width - 1310) / 2, 30)
|
|
|
|
height: Math.min(contentHeight, 200)
|
2024-06-28 20:34:03 -04:00
|
|
|
visible: !currentChat.isServer && ModelList.selectableModels.count !== 0
|
2024-06-24 18:49:23 -04:00
|
|
|
MyTextArea {
|
|
|
|
id: textInput
|
|
|
|
color: theme.textColor
|
2024-06-29 18:00:52 -04:00
|
|
|
topPadding: 15
|
|
|
|
bottomPadding: 15
|
2024-06-24 18:49:23 -04:00
|
|
|
leftPadding: 20
|
|
|
|
rightPadding: 40
|
|
|
|
enabled: currentChat.isModelLoaded && !currentChat.isServer
|
|
|
|
onEnabledChanged: {
|
|
|
|
if (textInput.enabled)
|
|
|
|
textInput.forceActiveFocus();
|
|
|
|
}
|
|
|
|
font.pixelSize: theme.fontSizeLarger
|
|
|
|
placeholderText: currentChat.isModelLoaded ? qsTr("Send a message...") : qsTr("Load a model to continue...")
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function sendMessage() {
|
|
|
|
if (textInput.text === "")
|
|
|
|
return
|
2024-03-13 19:57:05 -04:00
|
|
|
|
|
|
|
currentChat.stopGenerating()
|
2024-06-24 18:49:23 -04:00
|
|
|
currentChat.newPromptResponsePair(textInput.text);
|
|
|
|
currentChat.prompt(textInput.text,
|
|
|
|
MySettings.promptTemplate,
|
|
|
|
MySettings.maxLength,
|
|
|
|
MySettings.topK,
|
|
|
|
MySettings.topP,
|
|
|
|
MySettings.minP,
|
|
|
|
MySettings.temperature,
|
|
|
|
MySettings.promptBatchSize,
|
|
|
|
MySettings.repeatPenalty,
|
|
|
|
MySettings.repeatPenaltyTokens)
|
|
|
|
textInput.text = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
id: textInputMouseArea
|
|
|
|
anchors.fill: parent
|
|
|
|
acceptedButtons: Qt.RightButton
|
|
|
|
|
|
|
|
onClicked: (mouse) => {
|
|
|
|
if (mouse.button === Qt.RightButton) {
|
|
|
|
textInputContextMenu.x = textInputMouseArea.mouseX
|
|
|
|
textInputContextMenu.y = textInputMouseArea.mouseY
|
|
|
|
textInputContextMenu.open()
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
Menu {
|
|
|
|
id: textInputContextMenu
|
|
|
|
MenuItem {
|
|
|
|
text: qsTr("Cut")
|
|
|
|
enabled: textInput.selectedText !== ""
|
|
|
|
height: enabled ? implicitHeight : 0
|
|
|
|
onTriggered: textInput.cut()
|
|
|
|
}
|
|
|
|
MenuItem {
|
|
|
|
text: qsTr("Copy")
|
|
|
|
enabled: textInput.selectedText !== ""
|
|
|
|
height: enabled ? implicitHeight : 0
|
|
|
|
onTriggered: textInput.copy()
|
|
|
|
}
|
|
|
|
MenuItem {
|
|
|
|
text: qsTr("Paste")
|
|
|
|
onTriggered: textInput.paste()
|
|
|
|
}
|
|
|
|
MenuItem {
|
|
|
|
text: qsTr("Select All")
|
|
|
|
onTriggered: textInput.selectAll()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
|
2024-06-24 18:49:23 -04:00
|
|
|
MyToolButton {
|
|
|
|
id: sendButton
|
|
|
|
backgroundColor: theme.sendButtonBackground
|
|
|
|
backgroundColorHovered: theme.sendButtonBackgroundHovered
|
|
|
|
anchors.right: textInputView.right
|
|
|
|
anchors.verticalCenter: textInputView.verticalCenter
|
|
|
|
anchors.rightMargin: 15
|
2024-06-30 19:15:01 -04:00
|
|
|
imageWidth: theme.fontSizeLargest
|
|
|
|
imageHeight: theme.fontSizeLargest
|
2024-06-28 20:34:03 -04:00
|
|
|
visible: !currentChat.isServer && ModelList.selectableModels.count !== 0
|
2024-06-24 18:49:23 -04:00
|
|
|
enabled: !currentChat.responseInProgress
|
|
|
|
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")
|
|
|
|
|
|
|
|
onClicked: {
|
|
|
|
textInput.sendMessage()
|
|
|
|
}
|
2024-03-13 19:57:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|