Preliminary redesign of the UI. This has no major regression.

Signed-off-by: Adam Treat <treat.adam@gmail.com>
This commit is contained in:
Adam Treat
2024-03-20 11:09:59 -04:00
committed by AT
parent 0455b80b7f
commit f571e7e450
10 changed files with 499 additions and 389 deletions

View File

@@ -98,4 +98,17 @@ MyDialog {
Accessible.description: qsTr("Contains embedded link to https://home.nomic.ai")
}
}
MyButton {
id: checkForUpdatesButton
anchors.right: parent.right
anchors.bottom: parent.bottom
text: qsTr("Check for updates...")
font.pixelSize: theme.fontSizeLarge
Accessible.description: qsTr("Launch an external application that will check for updates to the installer")
onClicked: {
if (!LLM.checkForUpdates())
checkForUpdatesError.open()
}
}
}

View File

@@ -16,13 +16,22 @@ Rectangle {
id: theme
}
signal downloadClicked
signal aboutClicked
color: theme.containerBackground
Rectangle {
id: borderRight
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
width: 2
color: theme.containerForeground
}
Item {
anchors.fill: parent
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: borderRight.left
anchors.margins: 10
Accessible.role: Accessible.Pane
@@ -50,7 +59,7 @@ Rectangle {
anchors.rightMargin: -10
anchors.topMargin: 10
anchors.top: newChat.bottom
anchors.bottom: checkForUpdatesButton.top
anchors.bottom: parent.bottom
anchors.bottomMargin: 10
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
clip: true
@@ -236,45 +245,5 @@ Rectangle {
Accessible.description: qsTr("List of chats in the drawer dialog")
}
}
MyButton {
id: checkForUpdatesButton
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: downloadButton.top
anchors.bottomMargin: 10
text: qsTr("Updates")
font.pixelSize: theme.fontSizeLarge
Accessible.description: qsTr("Launch an external application that will check for updates to the installer")
onClicked: {
if (!LLM.checkForUpdates())
checkForUpdatesError.open()
}
}
MyButton {
id: downloadButton
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: aboutButton.top
anchors.bottomMargin: 10
text: qsTr("Downloads")
Accessible.description: qsTr("Launch a dialog to download new models")
onClicked: {
downloadClicked()
}
}
MyButton {
id: aboutButton
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
text: qsTr("About")
Accessible.description: qsTr("Launch a dialog to show the about page")
onClicked: {
aboutClicked()
}
}
}
}

View File

@@ -153,13 +153,6 @@ Rectangle {
anchors.centerIn: parent
}
AboutDialog {
id: aboutDialog
anchors.centerIn: parent
width: Math.min(1024, window.width - (window.width * .2))
height: Math.min(600, window.height - (window.height * .2))
}
Item {
Accessible.role: Accessible.Window
Accessible.name: title
@@ -294,190 +287,260 @@ Rectangle {
anchors.top: parent.top
height: 100
color: theme.mainHeader
Item {
anchors.centerIn: parent
height: childrenRect.height
visible: true
Label {
id: modelLabel
color: theme.textColor
padding: 20
font.pixelSize: theme.fontSizeLarger
text: ""
background: Rectangle {
color: theme.mainHeader
RowLayout {
id: comboLayout
height: 80
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 20
MyToolButton {
id: drawerButton
Layout.alignment: Qt.AlignLeft
Layout.leftMargin: 30
backgroundColor: theme.iconBackgroundLight
width: 40
height: 40
scale: 1.5
z: 200
padding: 15
source: conversation.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: {
conversation.toggleLeftPanel()
}
}
MyComboBox {
id: comboBox
Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: true
Layout.fillWidth: true
Layout.minimumWidth: 375
Layout.maximumWidth: 675
enabled: !currentChat.isServer
&& !window.trySwitchContextInProgress
&& !window.isCurrentlyLoading
model: ModelList.installedModels
valueRole: "id"
textRole: "name"
function changeModel(index) {
window.modelLoadingPercentage = 0.0;
window.isCurrentlyLoading = true;
currentChat.stopGenerating()
currentChat.reset();
currentChat.modelInfo = ModelList.modelInfo(comboBox.valueAt(index))
}
Connections {
target: currentChat
function onModelLoadingPercentageChanged() {
window.modelLoadingPercentage = currentChat.modelLoadingPercentage;
window.isCurrentlyLoading = currentChat.modelLoadingPercentage !== 0.0
&& currentChat.modelLoadingPercentage !== 1.0;
}
function onTrySwitchContextOfLoadedModelAttempted() {
window.trySwitchContextInProgress = true;
}
function onTrySwitchContextOfLoadedModelCompleted() {
window.trySwitchContextInProgress = false;
}
}
Connections {
target: switchModelDialog
function onAccepted() {
comboBox.changeModel(switchModelDialog.index)
}
}
background: ProgressBar {
id: modelProgress
value: window.modelLoadingPercentage
background: Rectangle {
color: theme.mainComboBackground
radius: 10
}
contentItem: Item {
Rectangle {
visible: window.isCurrentlyLoading
anchors.bottom: parent.bottom
width: modelProgress.visualPosition * parent.width
height: 10
radius: 2
color: theme.progressForeground
}
}
}
contentItem: Text {
anchors.horizontalCenter: parent.horizontalCenter
leftPadding: 10
rightPadding: {
if (ejectButton.visible && reloadButton)
return 105;
if (reloadButton.visible)
return 65
return 25
}
text: {
if (currentChat.modelLoadingError !== "")
return qsTr("Model loading error...")
if (window.trySwitchContextInProgress)
return qsTr("Switching context...")
if (currentModelName() === "")
return qsTr("Choose a model...")
if (currentChat.modelLoadingPercentage === 0.0)
return qsTr("Reload \u00B7 ") + currentModelName()
if (window.isCurrentlyLoading)
return qsTr("Loading \u00B7 ") + currentModelName()
return currentModelName()
}
font.pixelSize: theme.fontSizeLarger
color: theme.white
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
delegate: ItemDelegate {
id: comboItemDelegate
width: comboBox.width
contentItem: Text {
text: name
color: theme.textColor
font: comboBox.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
color: (index % 2 === 0 ? theme.darkContrast : theme.lightContrast)
border.width: highlighted
border.color: theme.accentColor
}
highlighted: comboBox.highlightedIndex === index
}
Accessible.role: Accessible.ComboBox
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);
}
}
MyMiniButton {
id: ejectButton
visible: currentChat.isModelLoaded && !window.isCurrentlyLoading
z: 500
anchors.right: parent.right
anchors.rightMargin: 50
anchors.verticalCenter: parent.verticalCenter
source: "qrc:/gpt4all/icons/eject.svg"
backgroundColor: theme.gray300
backgroundColorHovered: theme.iconBackgroundLight
onClicked: {
currentChat.forceUnloadModel();
}
ToolTip.text: qsTr("Eject the currently loaded model")
ToolTip.visible: hovered
}
MyMiniButton {
id: reloadButton
visible: currentChat.modelLoadingError === ""
&& !window.trySwitchContextInProgress
&& !window.isCurrentlyLoading
&& (currentChat.isModelLoaded || currentModelName() !== "")
z: 500
anchors.right: ejectButton.visible ? ejectButton.left : parent.right
anchors.rightMargin: ejectButton.visible ? 10 : 50
anchors.verticalCenter: parent.verticalCenter
source: "qrc:/gpt4all/icons/regenerate.svg"
backgroundColor: theme.gray300
backgroundColorHovered: theme.iconBackgroundLight
onClicked: {
if (currentChat.isModelLoaded)
currentChat.forceReloadModel();
else
currentChat.reloadModel();
}
ToolTip.text: qsTr("Reload the currently loaded model")
ToolTip.visible: hovered
}
horizontalAlignment: TextInput.AlignRight
}
RowLayout {
id: comboLayout
anchors.top: modelLabel.top
anchors.bottom: modelLabel.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenterOffset: window.width >= 950 ? 0 : Math.max(-((950 - window.width) / 2), -99.5)
Layout.alignment: Qt.AlignRight
Layout.rightMargin: 30
spacing: 20
MyComboBox {
id: comboBox
Layout.fillWidth: true
Layout.fillHeight: true
implicitWidth: 575
width: window.width >= 750 ? implicitWidth : implicitWidth - (750 - window.width)
enabled: !currentChat.isServer
&& !window.trySwitchContextInProgress
&& !window.isCurrentlyLoading
model: ModelList.installedModels
valueRole: "id"
textRole: "name"
MyToolButton {
id: resetContextButton
backgroundColor: theme.iconBackgroundLight
width: 40
height: 40
z: 200
padding: 15
source: "qrc:/gpt4all/icons/regenerate.svg"
function changeModel(index) {
window.modelLoadingPercentage = 0.0;
window.isCurrentlyLoading = true;
currentChat.stopGenerating()
Accessible.name: text
Accessible.description: qsTr("Reset the context and erase current conversation")
onClicked: {
Network.sendResetContext(chatModel.count)
currentChat.reset();
currentChat.modelInfo = ModelList.modelInfo(comboBox.valueAt(index))
currentChat.processSystemPrompt();
}
}
MyToolButton {
id: copyButton
backgroundColor: theme.iconBackgroundLight
width: 40
height: 40
z: 200
padding: 15
source: "qrc:/gpt4all/icons/copy.svg"
Accessible.name: qsTr("Copy")
Accessible.description: qsTr("Copy the conversation to the clipboard")
TextEdit{
id: copyEdit
visible: false
}
Connections {
target: currentChat
function onModelLoadingPercentageChanged() {
window.modelLoadingPercentage = currentChat.modelLoadingPercentage;
window.isCurrentlyLoading = currentChat.modelLoadingPercentage !== 0.0
&& currentChat.modelLoadingPercentage !== 1.0;
}
function onTrySwitchContextOfLoadedModelAttempted() {
window.trySwitchContextInProgress = true;
}
function onTrySwitchContextOfLoadedModelCompleted() {
window.trySwitchContextInProgress = false;
}
}
Connections {
target: switchModelDialog
function onAccepted() {
comboBox.changeModel(switchModelDialog.index)
}
onClicked: {
var conversation = getConversation()
copyEdit.text = conversation
copyEdit.selectAll()
copyEdit.copy()
copyMessage.open()
}
}
background: ProgressBar {
id: modelProgress
value: window.modelLoadingPercentage
background: Rectangle {
color: theme.mainComboBackground
radius: 10
}
contentItem: Item {
Rectangle {
visible: window.isCurrentlyLoading
anchors.bottom: parent.bottom
width: modelProgress.visualPosition * parent.width
height: 10
radius: 2
color: theme.progressForeground
}
}
}
contentItem: Text {
anchors.horizontalCenter: parent.horizontalCenter
leftPadding: 10
rightPadding: {
if (ejectButton.visible && reloadButton)
return 105;
if (reloadButton.visible)
return 65
return 25
}
text: {
if (currentChat.modelLoadingError !== "")
return qsTr("Model loading error...")
if (window.trySwitchContextInProgress)
return qsTr("Switching context...")
if (currentModelName() === "")
return qsTr("Choose a model...")
if (currentChat.modelLoadingPercentage === 0.0)
return qsTr("Reload \u00B7 ") + currentModelName()
if (window.isCurrentlyLoading)
return qsTr("Loading \u00B7 ") + currentModelName()
return currentModelName()
}
font.pixelSize: theme.fontSizeLarger
color: theme.white
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
delegate: ItemDelegate {
id: comboItemDelegate
width: comboBox.width
contentItem: Text {
text: name
color: theme.textColor
font: comboBox.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
color: (index % 2 === 0 ? theme.darkContrast : theme.lightContrast)
border.width: highlighted
border.color: theme.accentColor
}
highlighted: comboBox.highlightedIndex === index
}
Accessible.role: Accessible.ComboBox
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);
}
}
MyToolButton {
id: collectionsButton
backgroundColor: theme.iconBackgroundLight
width: 40
height: 42.5
z: 200
padding: 15
toggled: currentChat.collectionList.length
source: "qrc:/gpt4all/icons/db.svg"
Accessible.name: qsTr("Add documents")
Accessible.description: qsTr("add collections of documents to the chat")
MyMiniButton {
id: ejectButton
visible: currentChat.isModelLoaded && !window.isCurrentlyLoading
z: 500
anchors.right: parent.right
anchors.rightMargin: 50
anchors.verticalCenter: parent.verticalCenter
source: "qrc:/gpt4all/icons/eject.svg"
backgroundColor: theme.gray300
backgroundColorHovered: theme.iconBackgroundLight
onClicked: {
currentChat.forceUnloadModel();
}
ToolTip.text: qsTr("Eject the currently loaded model")
ToolTip.visible: hovered
}
MyMiniButton {
id: reloadButton
visible: currentChat.modelLoadingError === ""
&& !window.trySwitchContextInProgress
&& !window.isCurrentlyLoading
&& (currentChat.isModelLoaded || currentModelName() !== "")
z: 500
anchors.right: ejectButton.visible ? ejectButton.left : parent.right
anchors.rightMargin: ejectButton.visible ? 10 : 50
anchors.verticalCenter: parent.verticalCenter
source: "qrc:/gpt4all/icons/regenerate.svg"
backgroundColor: theme.gray300
backgroundColorHovered: theme.iconBackgroundLight
onClicked: {
if (currentChat.isModelLoaded)
currentChat.forceReloadModel();
else
currentChat.reloadModel();
}
ToolTip.text: qsTr("Reload the currently loaded model")
ToolTip.visible: hovered
onClicked: {
collectionsDialog.open()
}
}
}
@@ -495,64 +558,6 @@ Rectangle {
}
}
MyToolButton {
id: drawerButton
backgroundColor: theme.iconBackgroundLight
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: 42.5
anchors.leftMargin: 30
width: 40
height: 40
scale: 1.5
z: 200
padding: 15
source: conversation.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: {
conversation.toggleLeftPanel()
}
}
NetworkDialog {
id: networkDialog
anchors.centerIn: parent
width: Math.min(1024, window.width - (window.width * .2))
height: Math.min(600, window.height - (window.height * .2))
Item {
Accessible.role: Accessible.Dialog
Accessible.name: qsTr("Network dialog")
Accessible.description: qsTr("opt-in to share feedback/conversations")
}
}
MyToolButton {
id: networkButton
backgroundColor: theme.iconBackgroundLight
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: 42.5
anchors.rightMargin: 30
width: 40
height: 40
z: 200
padding: 15
toggled: MySettings.networkIsActive
source: "qrc:/gpt4all/icons/network.svg"
Accessible.name: qsTr("Network")
Accessible.description: qsTr("Reveals a dialogue where you can opt-in for sharing data over network")
onClicked: {
if (MySettings.networkIsActive) {
MySettings.networkIsActive = false
Network.sendNetworkToggled(false);
} else
networkDialog.open()
}
}
Connections {
target: Network
function onHealthCheckFailed(code) {
@@ -569,47 +574,6 @@ Rectangle {
}
}
MyToolButton {
id: collectionsButton
backgroundColor: theme.iconBackgroundLight
anchors.right: networkButton.left
anchors.top: parent.top
anchors.topMargin: 42.5
anchors.rightMargin: 10
width: 40
height: 42.5
z: 200
padding: 15
toggled: currentChat.collectionList.length
source: "qrc:/gpt4all/icons/db.svg"
Accessible.name: qsTr("Add documents")
Accessible.description: qsTr("add collections of documents to the chat")
onClicked: {
collectionsDialog.open()
}
}
MyToolButton {
id: settingsButton
backgroundColor: theme.iconBackgroundLight
anchors.right: collectionsButton.left
anchors.top: parent.top
anchors.topMargin: 42.5
anchors.rightMargin: 10
width: 40
height: 40
z: 200
padding: 15
source: "qrc:/gpt4all/icons/settings.svg"
Accessible.name: qsTr("Settings")
Accessible.description: qsTr("Reveals a dialogue with settings")
onClicked: {
settingsDialog.open()
}
}
PopupDialog {
id: copyMessage
anchors.centerIn: parent
@@ -650,35 +614,6 @@ Rectangle {
}
}
MyToolButton {
id: copyButton
backgroundColor: theme.iconBackgroundLight
anchors.right: settingsButton.left
anchors.top: parent.top
anchors.topMargin: 42.5
anchors.rightMargin: 10
width: 40
height: 40
z: 200
padding: 15
source: "qrc:/gpt4all/icons/copy.svg"
Accessible.name: qsTr("Copy")
Accessible.description: qsTr("Copy the conversation to the clipboard")
TextEdit{
id: copyEdit
visible: false
}
onClicked: {
var conversation = getConversation()
copyEdit.text = conversation
copyEdit.selectAll()
copyEdit.copy()
copyMessage.open()
}
}
function getConversation() {
var conversation = "";
for (var i = 0; i < chatModel.count; i++) {
@@ -716,29 +651,6 @@ Rectangle {
return str + "]}"
}
MyToolButton {
id: resetContextButton
backgroundColor: theme.iconBackgroundLight
anchors.right: copyButton.left
anchors.top: parent.top
anchors.topMargin: 42.5
anchors.rightMargin: 10
width: 40
height: 40
z: 200
padding: 15
source: "qrc:/gpt4all/icons/regenerate.svg"
Accessible.name: text
Accessible.description: qsTr("Reset the context and erase current conversation")
onClicked: {
Network.sendResetContext(chatModel.count)
currentChat.reset();
currentChat.processSystemPrompt();
}
}
Dialog {
id: checkForUpdatesError
anchors.centerIn: parent
@@ -785,14 +697,7 @@ Rectangle {
anchors.left: parent.left
anchors.top: accentRibbon.bottom
anchors.bottom: parent.bottom
width: Math.min(600, 0.2 * window.width)
onDownloadClicked: {
downloadNewModels.showEmbeddingModels = false
downloadNewModels.open()
}
onAboutClicked: {
aboutDialog.open()
}
width: Math.max(180, Math.min(600, 0.2 * window.width))
}
PopupDialog {
@@ -970,10 +875,7 @@ Rectangle {
ListView {
id: listView
visible: ModelList.installedModels.count !== 0 && chatModel.count !== 0
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: Math.min(1280, parent.width)
anchors.fill: parent
model: chatModel
ScrollBar.vertical: ScrollBar {
@@ -987,7 +889,8 @@ Rectangle {
delegate: TextArea {
id: myTextArea
text: value + references
width: listView.width
anchors.horizontalCenter: listView.contentItem.horizontalCenter
width: Math.min(1280, listView.contentItem.width)
color: theme.textColor
wrapMode: Text.WordWrap
textFormat: TextEdit.PlainText

View File

@@ -9,6 +9,8 @@ Button {
padding: 10
property color backgroundColor: theme.iconBackgroundDark
property color backgroundColorHovered: theme.iconBackgroundHovered
property color toggledColor: theme.accentColor
property real toggledWidth: 1
property bool toggled: false
property alias source: image.source
property alias fillMode: image.fillMode
@@ -27,8 +29,8 @@ Button {
anchors.fill: parent
color: "transparent"
visible: myButton.toggled
border.color: theme.accentColor
border.width: 1
border.color: myButton.toggledColor
border.width: myButton.toggledWidth
radius: 10
}
Image {

View File

@@ -200,6 +200,17 @@ QtObject {
}
}
property color viewBarBackground: {
switch (MySettings.chatTheme) {
case "LegacyDark":
return blue950;
case "Dark":
return darkgray300;
default:
return gray300;
}
}
property color progressForeground: {
switch (MySettings.chatTheme) {
case "LegacyDark":
@@ -376,6 +387,39 @@ QtObject {
}
}
property color iconBackgroundViewBar: {
switch (MySettings.chatTheme) {
case "LegacyDark":
return blue200;
case "Dark":
return green400;
default:
return green700;
}
}
property color iconBackgroundViewBarToggled: {
switch (MySettings.chatTheme) {
case "LegacyDark":
return purple400;
case "Dark":
return accentColor;
default:
return black;
}
}
property color iconBackgroundViewBarHovered: {
switch (MySettings.chatTheme) {
case "LegacyDark":
return blue400;
case "Dark":
return green600;
default:
return green500;
}
}
property color slugBackground: {
switch (MySettings.chatTheme) {
case "LegacyDark":