repo: organize sources, headers, and deps into subdirectories (#2917)

Signed-off-by: Jared Van Bortel <jared@nomic.ai>
This commit is contained in:
Jared Van Bortel
2024-08-27 17:22:40 -04:00
committed by GitHub
parent ed8bd4ceda
commit ca151f3519
97 changed files with 112 additions and 91 deletions

View File

@@ -1,206 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import QtQuick.Dialogs
import Qt.labs.folderlistmodel
import Qt5Compat.GraphicalEffects
import llm
import chatlistmodel
import download
import modellist
import network
import gpt4all
import mysettings
import localdocs
Rectangle {
id: addCollectionView
Theme {
id: theme
}
color: theme.viewBackground
signal localDocsViewRequested()
ColumnLayout {
id: mainArea
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.margins: 30
spacing: 20
RowLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
spacing: 50
MyButton {
id: backButton
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
text: qsTr("\u2190 Existing Collections")
borderWidth: 0
backgroundColor: theme.lighterButtonBackground
backgroundColorHovered: theme.lighterButtonBackgroundHovered
backgroundRadius: 5
padding: 15
topPadding: 8
bottomPadding: 8
textColor: theme.lighterButtonForeground
fontPixelSize: theme.fontSizeLarge
fontPixelBold: true
onClicked: {
localDocsViewRequested()
}
}
}
Text {
id: addDocBanner
Layout.alignment: Qt.AlignBottom | Qt.AlignHCenter
horizontalAlignment: Qt.AlignHCenter
text: qsTr("Add Document Collection")
font.pixelSize: theme.fontSizeBanner
color: theme.titleTextColor
}
Text {
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
Layout.maximumWidth: addDocBanner.width
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignJustify
text: qsTr("Add a folder containing plain text files, PDFs, or Markdown. Configure additional extensions in Settings.")
font.pixelSize: theme.fontSizeLarger
color: theme.titleInfoTextColor
}
GridLayout {
id: root
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
rowSpacing: 50
columnSpacing: 20
property alias collection: collection.text
property alias folder_path: folderEdit.text
FolderDialog {
id: folderDialog
title: qsTr("Please choose a directory")
}
function openFolderDialog(currentFolder, onAccepted) {
folderDialog.currentFolder = currentFolder;
folderDialog.accepted.connect(function() { onAccepted(folderDialog.selectedFolder); });
folderDialog.open();
}
Label {
Layout.row: 2
Layout.column: 0
text: qsTr("Name")
font.bold: true
font.pixelSize: theme.fontSizeLarger
color: theme.settingsTitleTextColor
}
MyTextField {
id: collection
Layout.row: 2
Layout.column: 1
Layout.minimumWidth: 400
Layout.alignment: Qt.AlignRight
horizontalAlignment: Text.AlignJustify
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
placeholderText: qsTr("Collection name...")
placeholderTextColor: theme.mutedTextColor
ToolTip.text: qsTr("Name of the collection to add (Required)")
ToolTip.visible: hovered
Accessible.role: Accessible.EditableText
Accessible.name: collection.text
Accessible.description: ToolTip.text
function showError() {
collection.placeholderTextColor = theme.textErrorColor
}
onTextChanged: {
collection.placeholderTextColor = theme.mutedTextColor
}
}
Label {
Layout.row: 3
Layout.column: 0
text: qsTr("Folder")
font.bold: true
font.pixelSize: theme.fontSizeLarger
color: theme.settingsTitleTextColor
}
RowLayout {
Layout.row: 3
Layout.column: 1
Layout.minimumWidth: 400
Layout.maximumWidth: 400
Layout.alignment: Qt.AlignRight
spacing: 10
MyDirectoryField {
id: folderEdit
Layout.fillWidth: true
text: root.folder_path
placeholderText: qsTr("Folder path...")
font.pixelSize: theme.fontSizeLarge
placeholderTextColor: theme.mutedTextColor
ToolTip.text: qsTr("Folder path to documents (Required)")
ToolTip.visible: hovered
function showError() {
folderEdit.placeholderTextColor = theme.textErrorColor
}
onTextChanged: {
folderEdit.placeholderTextColor = theme.mutedTextColor
}
}
MySettingsButton {
id: browseButton
text: qsTr("Browse")
onClicked: {
root.openFolderDialog(StandardPaths.writableLocation(StandardPaths.HomeLocation), function(selectedFolder) {
root.folder_path = selectedFolder
})
}
}
}
MyButton {
Layout.row: 4
Layout.column: 1
Layout.alignment: Qt.AlignRight
text: qsTr("Create Collection")
onClicked: {
var isError = false;
if (root.collection === "") {
isError = true;
collection.showError();
}
if (root.folder_path === "" || !folderEdit.isValid) {
isError = true;
folderEdit.showError();
}
if (isError)
return;
LocalDocs.addFolder(root.collection, root.folder_path)
root.collection = ""
root.folder_path = ""
collection.clear()
localDocsViewRequested()
}
}
}
}
}

View File

@@ -1,816 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import QtQuick.Dialogs
import Qt.labs.folderlistmodel
import Qt5Compat.GraphicalEffects
import llm
import chatlistmodel
import download
import modellist
import network
import gpt4all
import mysettings
import localdocs
Rectangle {
id: addModelView
Theme {
id: theme
}
color: theme.viewBackground
signal modelsViewRequested()
ToastManager {
id: messageToast
}
PopupDialog {
id: downloadingErrorPopup
anchors.centerIn: parent
shouldTimeOut: false
}
ColumnLayout {
id: mainArea
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.margins: 30
spacing: 30
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
spacing: 30
MyButton {
id: backButton
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
text: qsTr("\u2190 Existing Models")
borderWidth: 0
backgroundColor: theme.lighterButtonBackground
backgroundColorHovered: theme.lighterButtonBackgroundHovered
backgroundRadius: 5
padding: 15
topPadding: 8
bottomPadding: 8
textColor: theme.lighterButtonForeground
fontPixelSize: theme.fontSizeLarge
fontPixelBold: true
onClicked: {
modelsViewRequested()
}
}
Text {
id: welcome
text: qsTr("Explore Models")
font.pixelSize: theme.fontSizeBanner
color: theme.titleTextColor
}
RowLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignCenter
Layout.margins: 0
spacing: 10
MyTextField {
id: discoverField
property string textBeingSearched: ""
readOnly: ModelList.discoverInProgress
Layout.alignment: Qt.AlignCenter
Layout.fillWidth: true
font.pixelSize: theme.fontSizeLarger
placeholderText: qsTr("Discover and download models by keyword search...")
Accessible.role: Accessible.EditableText
Accessible.name: placeholderText
Accessible.description: qsTr("Text field for discovering and filtering downloadable models")
Connections {
target: ModelList
function onDiscoverInProgressChanged() {
if (ModelList.discoverInProgress) {
discoverField.textBeingSearched = discoverField.text;
discoverField.text = qsTr("Searching \u00B7 %1").arg(discoverField.textBeingSearched);
} else {
discoverField.text = discoverField.textBeingSearched;
discoverField.textBeingSearched = "";
}
}
}
background: ProgressBar {
id: discoverProgressBar
indeterminate: ModelList.discoverInProgress && ModelList.discoverProgress === 0.0
value: ModelList.discoverProgress
background: Rectangle {
color: theme.controlBackground
border.color: theme.controlBorder
radius: 10
}
contentItem: Item {
Rectangle {
visible: ModelList.discoverInProgress
anchors.bottom: parent.bottom
width: discoverProgressBar.visualPosition * parent.width
height: 10
radius: 2
color: theme.progressForeground
}
}
}
Keys.onReturnPressed: (event)=> {
if (event.modifiers & Qt.ControlModifier || event.modifiers & Qt.ShiftModifier)
event.accepted = false;
else {
editingFinished();
sendDiscovery()
}
}
function sendDiscovery() {
ModelList.downloadableModels.discoverAndFilter(discoverField.text);
}
RowLayout {
spacing: 0
anchors.right: discoverField.right
anchors.verticalCenter: discoverField.verticalCenter
anchors.rightMargin: 15
visible: !ModelList.discoverInProgress
MyMiniButton {
id: clearDiscoverButton
backgroundColor: theme.textColor
backgroundColorHovered: theme.iconBackgroundDark
visible: discoverField.text !== ""
source: "qrc:/gpt4all/icons/close.svg"
onClicked: {
discoverField.text = ""
discoverField.sendDiscovery() // should clear results
}
}
MyMiniButton {
backgroundColor: theme.textColor
backgroundColorHovered: theme.iconBackgroundDark
source: "qrc:/gpt4all/icons/settings.svg"
onClicked: {
discoveryTools.visible = !discoveryTools.visible
}
}
MyMiniButton {
id: sendButton
enabled: !ModelList.discoverInProgress
backgroundColor: theme.textColor
backgroundColorHovered: theme.iconBackgroundDark
source: "qrc:/gpt4all/icons/send_message.svg"
Accessible.name: qsTr("Initiate model discovery and filtering")
Accessible.description: qsTr("Triggers discovery and filtering of models")
onClicked: {
discoverField.sendDiscovery()
}
}
}
}
}
RowLayout {
id: discoveryTools
Layout.fillWidth: true
Layout.alignment: Qt.AlignCenter
Layout.margins: 0
spacing: 20
visible: false
MyComboBox {
id: comboSort
model: ListModel {
ListElement { name: qsTr("Default") }
ListElement { name: qsTr("Likes") }
ListElement { name: qsTr("Downloads") }
ListElement { name: qsTr("Recent") }
}
currentIndex: ModelList.discoverSort
contentItem: Text {
anchors.horizontalCenter: parent.horizontalCenter
rightPadding: 30
color: theme.textColor
text: {
return qsTr("Sort by: %1").arg(comboSort.displayText)
}
font.pixelSize: theme.fontSizeLarger
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
onActivated: function (index) {
ModelList.discoverSort = index;
}
}
MyComboBox {
id: comboSortDirection
model: ListModel {
ListElement { name: qsTr("Asc") }
ListElement { name: qsTr("Desc") }
}
currentIndex: {
if (ModelList.discoverSortDirection === 1)
return 0
else
return 1;
}
contentItem: Text {
anchors.horizontalCenter: parent.horizontalCenter
rightPadding: 30
color: theme.textColor
text: {
return qsTr("Sort dir: %1").arg(comboSortDirection.displayText)
}
font.pixelSize: theme.fontSizeLarger
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
onActivated: function (index) {
if (index === 0)
ModelList.discoverSortDirection = 1;
else
ModelList.discoverSortDirection = -1;
}
}
MyComboBox {
id: comboLimit
model: ListModel {
ListElement { name: "5" }
ListElement { name: "10" }
ListElement { name: "20" }
ListElement { name: "50" }
ListElement { name: "100" }
ListElement { name: qsTr("None") }
}
currentIndex: {
if (ModelList.discoverLimit === 5)
return 0;
else if (ModelList.discoverLimit === 10)
return 1;
else if (ModelList.discoverLimit === 20)
return 2;
else if (ModelList.discoverLimit === 50)
return 3;
else if (ModelList.discoverLimit === 100)
return 4;
else if (ModelList.discoverLimit === -1)
return 5;
}
contentItem: Text {
anchors.horizontalCenter: parent.horizontalCenter
rightPadding: 30
color: theme.textColor
text: {
return qsTr("Limit: %1").arg(comboLimit.displayText)
}
font.pixelSize: theme.fontSizeLarger
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
onActivated: function (index) {
switch (index) {
case 0:
ModelList.discoverLimit = 5; break;
case 1:
ModelList.discoverLimit = 10; break;
case 2:
ModelList.discoverLimit = 20; break;
case 3:
ModelList.discoverLimit = 50; break;
case 4:
ModelList.discoverLimit = 100; break;
case 5:
ModelList.discoverLimit = -1; break;
}
}
}
}
}
Label {
visible: !ModelList.downloadableModels.count && !ModelList.asyncModelRequestOngoing
Layout.fillWidth: true
Layout.fillHeight: true
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
text: qsTr("Network error: could not retrieve %1").arg("http://gpt4all.io/models/models3.json")
font.pixelSize: theme.fontSizeLarge
color: theme.mutedTextColor
}
MyBusyIndicator {
visible: !ModelList.downloadableModels.count && ModelList.asyncModelRequestOngoing
running: ModelList.asyncModelRequestOngoing
Accessible.role: Accessible.Animation
Layout.alignment: Qt.AlignCenter
Accessible.name: qsTr("Busy indicator")
Accessible.description: qsTr("Displayed when the models request is ongoing")
}
ScrollView {
id: scrollView
ScrollBar.vertical.policy: ScrollBar.AsNeeded
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
ListView {
id: modelListView
model: ModelList.downloadableModels
boundsBehavior: Flickable.StopAtBounds
spacing: 30
delegate: Rectangle {
id: delegateItem
width: modelListView.width
height: childrenRect.height + 60
color: theme.conversationBackground
radius: 10
border.width: 1
border.color: theme.controlBorder
ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 30
Text {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
text: name
elide: Text.ElideRight
color: theme.titleTextColor
font.pixelSize: theme.fontSizeLargest
font.bold: true
Accessible.role: Accessible.Paragraph
Accessible.name: qsTr("Model file")
Accessible.description: qsTr("Model file to be downloaded")
}
Rectangle {
Layout.fillWidth: true
height: 1
color: theme.dividerColor
}
RowLayout {
Layout.topMargin: 10
Layout.fillWidth: true
Text {
id: descriptionText
text: description
font.pixelSize: theme.fontSizeLarge
Layout.fillWidth: true
wrapMode: Text.WordWrap
textFormat: Text.StyledText
color: theme.textColor
linkColor: theme.textColor
Accessible.role: Accessible.Paragraph
Accessible.name: qsTr("Description")
Accessible.description: qsTr("File description")
onLinkActivated: function(link) { Qt.openUrlExternally(link); }
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton // pass clicks to parent
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
// FIXME Need to overhaul design here which must take into account
// features not present in current figma including:
// * Ability to cancel a current download
// * Ability to resume a download
// * The presentation of an error if encountered
// * Whether to show already installed models
// * Install of remote models with API keys
// * The presentation of the progress bar
Rectangle {
id: actionBox
width: childrenRect.width + 20
color: "transparent"
border.width: 1
border.color: theme.dividerColor
radius: 10
Layout.rightMargin: 20
Layout.bottomMargin: 20
Layout.minimumHeight: childrenRect.height + 20
Layout.alignment: Qt.AlignRight | Qt.AlignTop
ColumnLayout {
spacing: 0
MySettingsButton {
id: downloadButton
text: isDownloading ? qsTr("Cancel") : isIncomplete ? qsTr("Resume") : qsTr("Download")
font.pixelSize: theme.fontSizeLarge
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.minimumWidth: 200
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
visible: !isOnline && !installed && !calcHash && downloadError === ""
Accessible.description: qsTr("Stop/restart/start the download")
onClicked: {
if (!isDownloading) {
Download.downloadModel(filename);
} else {
Download.cancelDownload(filename);
}
}
}
MySettingsDestructiveButton {
id: removeButton
text: qsTr("Remove")
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.minimumWidth: 200
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
visible: !isDownloading && (installed || isIncomplete)
Accessible.description: qsTr("Remove model from filesystem")
onClicked: {
Download.removeModel(filename);
}
}
MySettingsButton {
id: installButton
visible: !installed && isOnline
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.minimumWidth: 200
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
text: qsTr("Install")
font.pixelSize: theme.fontSizeLarge
onClicked: {
var apiKeyText = apiKey.text.trim(),
baseUrlText = baseUrl.text.trim(),
modelNameText = modelName.text.trim();
var apiKeyOk = apiKeyText !== "",
baseUrlOk = !isCompatibleApi || baseUrlText !== "",
modelNameOk = !isCompatibleApi || modelNameText !== "";
if (!apiKeyOk)
apiKey.showError();
if (!baseUrlOk)
baseUrl.showError();
if (!modelNameOk)
modelName.showError();
if (!apiKeyOk || !baseUrlOk || !modelNameOk)
return;
if (!isCompatibleApi)
Download.installModel(
filename,
apiKeyText,
);
else
Download.installCompatibleModel(
modelNameText,
apiKeyText,
baseUrlText,
);
}
Accessible.role: Accessible.Button
Accessible.name: qsTr("Install")
Accessible.description: qsTr("Install online model")
}
ColumnLayout {
spacing: 0
Label {
Layout.topMargin: 20
Layout.leftMargin: 20
visible: downloadError !== ""
textFormat: Text.StyledText
text: qsTr("<strong><font size=\"1\"><a href=\"#error\">Error</a></strong></font>")
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
linkColor: theme.textErrorColor
Accessible.role: Accessible.Paragraph
Accessible.name: text
Accessible.description: qsTr("Describes an error that occurred when downloading")
onLinkActivated: {
downloadingErrorPopup.text = downloadError;
downloadingErrorPopup.open();
}
}
Label {
visible: LLM.systemTotalRAMInGB() < ramrequired
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.maximumWidth: 300
textFormat: Text.StyledText
text: qsTr("<strong><font size=\"2\">WARNING: Not recommended for your hardware. Model requires more memory (%1 GB) than your system has available (%2).</strong></font>").arg(ramrequired).arg(LLM.systemTotalRAMInGBString())
color: theme.textErrorColor
font.pixelSize: theme.fontSizeLarge
wrapMode: Text.WordWrap
Accessible.role: Accessible.Paragraph
Accessible.name: text
Accessible.description: qsTr("Error for incompatible hardware")
onLinkActivated: {
downloadingErrorPopup.text = downloadError;
downloadingErrorPopup.open();
}
}
}
ColumnLayout {
visible: isDownloading && !calcHash
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.minimumWidth: 200
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
spacing: 20
ProgressBar {
id: itemProgressBar
Layout.fillWidth: true
width: 200
value: bytesReceived / bytesTotal
background: Rectangle {
implicitHeight: 45
color: theme.progressBackground
radius: 3
}
contentItem: Item {
implicitHeight: 40
Rectangle {
width: itemProgressBar.visualPosition * parent.width
height: parent.height
radius: 2
color: theme.progressForeground
}
}
Accessible.role: Accessible.ProgressBar
Accessible.name: qsTr("Download progressBar")
Accessible.description: qsTr("Shows the progress made in the download")
}
Label {
id: speedLabel
color: theme.textColor
Layout.alignment: Qt.AlignRight
text: speed
font.pixelSize: theme.fontSizeLarge
Accessible.role: Accessible.Paragraph
Accessible.name: qsTr("Download speed")
Accessible.description: qsTr("Download speed in bytes/kilobytes/megabytes per second")
}
}
RowLayout {
visible: calcHash
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.minimumWidth: 200
Layout.maximumWidth: 200
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
clip: true
Label {
id: calcHashLabel
color: theme.textColor
text: qsTr("Calculating...")
font.pixelSize: theme.fontSizeLarge
Accessible.role: Accessible.Paragraph
Accessible.name: text
Accessible.description: qsTr("Whether the file hash is being calculated")
}
MyBusyIndicator {
id: busyCalcHash
running: calcHash
Accessible.role: Accessible.Animation
Accessible.name: qsTr("Busy indicator")
Accessible.description: qsTr("Displayed when the file hash is being calculated")
}
}
MyTextField {
id: apiKey
visible: !installed && isOnline
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.minimumWidth: 200
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
wrapMode: Text.WrapAnywhere
function showError() {
messageToast.show(qsTr("ERROR: $API_KEY is empty."));
apiKey.placeholderTextColor = theme.textErrorColor;
}
onTextChanged: {
apiKey.placeholderTextColor = theme.mutedTextColor;
}
placeholderText: qsTr("enter $API_KEY")
Accessible.role: Accessible.EditableText
Accessible.name: placeholderText
Accessible.description: qsTr("Whether the file hash is being calculated")
}
MyTextField {
id: baseUrl
visible: !installed && isOnline && isCompatibleApi
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.minimumWidth: 200
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
wrapMode: Text.WrapAnywhere
function showError() {
messageToast.show(qsTr("ERROR: $BASE_URL is empty."));
baseUrl.placeholderTextColor = theme.textErrorColor;
}
onTextChanged: {
baseUrl.placeholderTextColor = theme.mutedTextColor;
}
placeholderText: qsTr("enter $BASE_URL")
Accessible.role: Accessible.EditableText
Accessible.name: placeholderText
Accessible.description: qsTr("Whether the file hash is being calculated")
}
MyTextField {
id: modelName
visible: !installed && isOnline && isCompatibleApi
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.minimumWidth: 200
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
wrapMode: Text.WrapAnywhere
function showError() {
messageToast.show(qsTr("ERROR: $MODEL_NAME is empty."))
modelName.placeholderTextColor = theme.textErrorColor;
}
onTextChanged: {
modelName.placeholderTextColor = theme.mutedTextColor;
}
placeholderText: qsTr("enter $MODEL_NAME")
Accessible.role: Accessible.EditableText
Accessible.name: placeholderText
Accessible.description: qsTr("Whether the file hash is being calculated")
}
}
}
}
Item {
Layout.minimumWidth: childrenRect.width
Layout.minimumHeight: childrenRect.height
Layout.bottomMargin: 10
RowLayout {
id: paramRow
anchors.centerIn: parent
ColumnLayout {
Layout.topMargin: 10
Layout.bottomMargin: 10
Layout.leftMargin: 20
Layout.rightMargin: 20
Text {
text: qsTr("File size")
font.pixelSize: theme.fontSizeSmall
color: theme.mutedDarkTextColor
}
Text {
text: filesize
color: theme.textColor
font.pixelSize: theme.fontSizeSmall
font.bold: true
}
}
Rectangle {
width: 1
Layout.fillHeight: true
color: theme.dividerColor
}
ColumnLayout {
Layout.topMargin: 10
Layout.bottomMargin: 10
Layout.leftMargin: 20
Layout.rightMargin: 20
Text {
text: qsTr("RAM required")
font.pixelSize: theme.fontSizeSmall
color: theme.mutedDarkTextColor
}
Text {
text: ramrequired >= 0 ? qsTr("%1 GB").arg(ramrequired) : qsTr("?")
color: theme.textColor
font.pixelSize: theme.fontSizeSmall
font.bold: true
}
}
Rectangle {
width: 1
Layout.fillHeight: true
color: theme.dividerColor
}
ColumnLayout {
Layout.topMargin: 10
Layout.bottomMargin: 10
Layout.leftMargin: 20
Layout.rightMargin: 20
Text {
text: qsTr("Parameters")
font.pixelSize: theme.fontSizeSmall
color: theme.mutedDarkTextColor
}
Text {
text: parameters !== "" ? parameters : qsTr("?")
color: theme.textColor
font.pixelSize: theme.fontSizeSmall
font.bold: true
}
}
Rectangle {
width: 1
Layout.fillHeight: true
color: theme.dividerColor
}
ColumnLayout {
Layout.topMargin: 10
Layout.bottomMargin: 10
Layout.leftMargin: 20
Layout.rightMargin: 20
Text {
text: qsTr("Quant")
font.pixelSize: theme.fontSizeSmall
color: theme.mutedDarkTextColor
}
Text {
text: quant
color: theme.textColor
font.pixelSize: theme.fontSizeSmall
font.bold: true
}
}
Rectangle {
width: 1
Layout.fillHeight: true
color: theme.dividerColor
}
ColumnLayout {
Layout.topMargin: 10
Layout.bottomMargin: 10
Layout.leftMargin: 20
Layout.rightMargin: 20
Text {
text: qsTr("Type")
font.pixelSize: theme.fontSizeSmall
color: theme.mutedDarkTextColor
}
Text {
text: type
color: theme.textColor
font.pixelSize: theme.fontSizeSmall
font.bold: true
}
}
}
Rectangle {
color: "transparent"
anchors.fill: paramRow
border.color: theme.dividerColor
border.width: 1
radius: 10
}
}
Rectangle {
Layout.fillWidth: true
height: 1
color: theme.dividerColor
}
}
}
}
}
}
Connections {
target: Download
function onToastMessage(message) {
messageToast.show(message);
}
}
}

View File

@@ -1,602 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import QtQuick.Dialogs
import modellist
import mysettings
import network
import llm
MySettingsTab {
onRestoreDefaultsClicked: {
MySettings.restoreApplicationDefaults();
}
title: qsTr("Application")
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")
}
}
Dialog {
id: checkForUpdatesError
anchors.centerIn: parent
modal: false
padding: 20
Text {
horizontalAlignment: Text.AlignJustify
text: qsTr("ERROR: Update system could not find the MaintenanceTool used<br>
to check for updates!<br><br>
Did you install this application using the online installer? If so,<br>
the MaintenanceTool executable should be located one directory<br>
above where this application resides on your filesystem.<br><br>
If you can't start it manually, then I'm afraid you'll have to<br>
reinstall.")
color: theme.textErrorColor
font.pixelSize: theme.fontSizeLarge
Accessible.role: Accessible.Dialog
Accessible.name: text
Accessible.description: qsTr("Error dialog")
}
background: Rectangle {
anchors.fill: parent
color: theme.containerBackground
border.width: 1
border.color: theme.dialogBorder
radius: 10
}
}
contentItem: GridLayout {
id: applicationSettingsTabInner
columns: 3
rowSpacing: 30
columnSpacing: 10
Label {
Layout.row: 0
Layout.column: 0
Layout.bottomMargin: 10
color: theme.settingsTitleTextColor
font.pixelSize: theme.fontSizeBannerSmall
font.bold: true
text: qsTr("Application Settings")
}
ColumnLayout {
Layout.row: 1
Layout.column: 0
Layout.columnSpan: 3
Layout.fillWidth: true
spacing: 10
Label {
color: theme.styledTextColor
font.pixelSize: theme.fontSizeLarge
font.bold: true
text: qsTr("General")
}
Rectangle {
Layout.fillWidth: true
height: 1
color: theme.settingsDivider
}
}
MySettingsLabel {
id: themeLabel
text: qsTr("Theme")
helpText: qsTr("The application color scheme.")
Layout.row: 2
Layout.column: 0
}
MyComboBox {
id: themeBox
Layout.row: 2
Layout.column: 2
Layout.minimumWidth: 200
Layout.maximumWidth: 200
Layout.fillWidth: false
Layout.alignment: Qt.AlignRight
// NOTE: indices match values of ChatTheme enum, keep them in sync
model: ListModel {
ListElement { name: qsTr("Light") }
ListElement { name: qsTr("Dark") }
ListElement { name: qsTr("LegacyDark") }
}
Accessible.name: themeLabel.text
Accessible.description: themeLabel.helpText
function updateModel() {
themeBox.currentIndex = MySettings.chatTheme;
}
Component.onCompleted: {
themeBox.updateModel()
}
Connections {
target: MySettings
function onChatThemeChanged() {
themeBox.updateModel()
}
}
onActivated: {
MySettings.chatTheme = themeBox.currentIndex
}
}
MySettingsLabel {
id: fontLabel
text: qsTr("Font Size")
helpText: qsTr("The size of text in the application.")
Layout.row: 3
Layout.column: 0
}
MyComboBox {
id: fontBox
Layout.row: 3
Layout.column: 2
Layout.minimumWidth: 200
Layout.maximumWidth: 200
Layout.fillWidth: false
Layout.alignment: Qt.AlignRight
// NOTE: indices match values of FontSize enum, keep them in sync
model: ListModel {
ListElement { name: qsTr("Small") }
ListElement { name: qsTr("Medium") }
ListElement { name: qsTr("Large") }
}
Accessible.name: fontLabel.text
Accessible.description: fontLabel.helpText
function updateModel() {
fontBox.currentIndex = MySettings.fontSize;
}
Component.onCompleted: {
fontBox.updateModel()
}
Connections {
target: MySettings
function onFontSizeChanged() {
fontBox.updateModel()
}
}
onActivated: {
MySettings.fontSize = fontBox.currentIndex
}
}
MySettingsLabel {
id: languageLabel
visible: MySettings.uiLanguages.length > 1
text: qsTr("Language and Locale")
helpText: qsTr("The language and locale you wish to use.")
Layout.row: 4
Layout.column: 0
}
MyComboBox {
id: languageBox
visible: MySettings.uiLanguages.length > 1
Layout.row: 4
Layout.column: 2
Layout.minimumWidth: 200
Layout.maximumWidth: 200
Layout.fillWidth: false
Layout.alignment: Qt.AlignRight
model: ListModel {
Component.onCompleted: {
for (var i = 0; i < MySettings.uiLanguages.length; ++i)
append({"text": MySettings.uiLanguages[i]});
languageBox.updateModel();
}
ListElement { text: qsTr("System Locale") }
}
Accessible.name: languageLabel.text
Accessible.description: languageLabel.helpText
function updateModel() {
// This usage of 'System Locale' should not be translated
// FIXME: Make this refer to a string literal variable accessed by both QML and C++
if (MySettings.languageAndLocale === "System Locale")
languageBox.currentIndex = 0
else
languageBox.currentIndex = languageBox.indexOfValue(MySettings.languageAndLocale);
}
Component.onCompleted: {
languageBox.updateModel()
}
onActivated: {
// This usage of 'System Locale' should not be translated
// FIXME: Make this refer to a string literal variable accessed by both QML and C++
if (languageBox.currentIndex === 0)
MySettings.languageAndLocale = "System Locale";
else
MySettings.languageAndLocale = languageBox.currentText;
}
}
MySettingsLabel {
id: deviceLabel
text: qsTr("Device")
helpText: qsTr('The compute device used for text generation.')
Layout.row: 5
Layout.column: 0
}
MyComboBox {
id: deviceBox
Layout.row: 5
Layout.column: 2
Layout.minimumWidth: 400
Layout.maximumWidth: 400
Layout.fillWidth: false
Layout.alignment: Qt.AlignRight
model: ListModel {
Component.onCompleted: {
for (var i = 0; i < MySettings.deviceList.length; ++i)
append({"text": MySettings.deviceList[i]});
deviceBox.updateModel();
}
ListElement { text: qsTr("Application default") }
}
Accessible.name: deviceLabel.text
Accessible.description: deviceLabel.helpText
function updateModel() {
// This usage of 'Auto' should not be translated
// FIXME: Make this refer to a string literal variable accessed by both QML and C++
if (MySettings.device === "Auto")
deviceBox.currentIndex = 0
else
deviceBox.currentIndex = deviceBox.indexOfValue(MySettings.device);
}
Component.onCompleted: {
deviceBox.updateModel();
}
Connections {
target: MySettings
function onDeviceChanged() {
deviceBox.updateModel();
}
}
onActivated: {
// This usage of 'Auto' should not be translated
// FIXME: Make this refer to a string literal variable accessed by both QML and C++
if (deviceBox.currentIndex === 0)
MySettings.device = "Auto";
else
MySettings.device = deviceBox.currentText;
}
}
MySettingsLabel {
id: defaultModelLabel
text: qsTr("Default Model")
helpText: qsTr("The preferred model for new chats. Also used as the local server fallback.")
Layout.row: 6
Layout.column: 0
}
MyComboBox {
id: defaultModelBox
Layout.row: 6
Layout.column: 2
Layout.minimumWidth: 400
Layout.maximumWidth: 400
Layout.alignment: Qt.AlignRight
model: ListModel {
id: defaultModelBoxModel
Component.onCompleted: {
defaultModelBox.rebuildModel()
}
}
Accessible.name: defaultModelLabel.text
Accessible.description: defaultModelLabel.helpText
function rebuildModel() {
defaultModelBoxModel.clear();
defaultModelBoxModel.append({"text": qsTr("Application default")});
for (var i = 0; i < ModelList.selectableModelList.length; ++i)
defaultModelBoxModel.append({"text": ModelList.selectableModelList[i].name});
defaultModelBox.updateModel();
}
function updateModel() {
// This usage of 'Application default' should not be translated
// FIXME: Make this refer to a string literal variable accessed by both QML and C++
if (MySettings.userDefaultModel === "Application default")
defaultModelBox.currentIndex = 0
else
defaultModelBox.currentIndex = defaultModelBox.indexOfValue(MySettings.userDefaultModel);
}
onActivated: {
// This usage of 'Application default' should not be translated
// FIXME: Make this refer to a string literal variable accessed by both QML and C++
if (defaultModelBox.currentIndex === 0)
MySettings.userDefaultModel = "Application default";
else
MySettings.userDefaultModel = defaultModelBox.currentText;
}
Connections {
target: MySettings
function onUserDefaultModelChanged() {
defaultModelBox.updateModel()
}
}
Connections {
target: MySettings
function onLanguageAndLocaleChanged() {
defaultModelBox.rebuildModel()
}
}
Connections {
target: ModelList
function onSelectableModelListChanged() {
defaultModelBox.rebuildModel()
}
}
}
MySettingsLabel {
id: suggestionModeLabel
text: qsTr("Suggestion Mode")
helpText: qsTr("Generate suggested follow-up questions at the end of responses.")
Layout.row: 7
Layout.column: 0
}
MyComboBox {
id: suggestionModeBox
Layout.row: 7
Layout.column: 2
Layout.minimumWidth: 400
Layout.maximumWidth: 400
Layout.alignment: Qt.AlignRight
// NOTE: indices match values of SuggestionMode enum, keep them in sync
model: ListModel {
ListElement { name: qsTr("When chatting with LocalDocs") }
ListElement { name: qsTr("Whenever possible") }
ListElement { name: qsTr("Never") }
}
Accessible.name: suggestionModeLabel.text
Accessible.description: suggestionModeLabel.helpText
onActivated: {
MySettings.suggestionMode = suggestionModeBox.currentIndex;
}
Component.onCompleted: {
suggestionModeBox.currentIndex = MySettings.suggestionMode;
}
}
MySettingsLabel {
id: modelPathLabel
text: qsTr("Download Path")
helpText: qsTr("Where to store local models and the LocalDocs database.")
Layout.row: 8
Layout.column: 0
}
RowLayout {
Layout.row: 8
Layout.column: 2
Layout.alignment: Qt.AlignRight
Layout.minimumWidth: 400
Layout.maximumWidth: 400
spacing: 10
MyDirectoryField {
id: modelPathDisplayField
text: MySettings.modelPath
font.pixelSize: theme.fontSizeLarge
implicitWidth: 300
Layout.fillWidth: true
Accessible.name: modelPathLabel.text
Accessible.description: modelPathLabel.helpText
onEditingFinished: {
if (isValid) {
MySettings.modelPath = modelPathDisplayField.text
} else {
text = MySettings.modelPath
}
}
}
MySettingsButton {
text: qsTr("Browse")
Accessible.description: qsTr("Choose where to save model files")
onClicked: {
openFolderDialog("file://" + MySettings.modelPath, function(selectedFolder) {
MySettings.modelPath = selectedFolder
})
}
}
}
MySettingsLabel {
id: dataLakeLabel
text: qsTr("Enable Datalake")
helpText: qsTr("Send chats and feedback to the GPT4All Open-Source Datalake.")
Layout.row: 9
Layout.column: 0
}
MyCheckBox {
id: dataLakeBox
Layout.row: 9
Layout.column: 2
Layout.alignment: Qt.AlignRight
Component.onCompleted: { dataLakeBox.checked = MySettings.networkIsActive; }
Connections {
target: MySettings
function onNetworkIsActiveChanged() { dataLakeBox.checked = MySettings.networkIsActive; }
}
onClicked: {
if (MySettings.networkIsActive)
MySettings.networkIsActive = false;
else
networkDialog.open();
dataLakeBox.checked = MySettings.networkIsActive;
}
}
ColumnLayout {
Layout.row: 10
Layout.column: 0
Layout.columnSpan: 3
Layout.fillWidth: true
spacing: 10
Label {
color: theme.styledTextColor
font.pixelSize: theme.fontSizeLarge
font.bold: true
text: qsTr("Advanced")
}
Rectangle {
Layout.fillWidth: true
height: 1
color: theme.settingsDivider
}
}
MySettingsLabel {
id: nThreadsLabel
text: qsTr("CPU Threads")
helpText: qsTr("The number of CPU threads used for inference and embedding.")
Layout.row: 11
Layout.column: 0
}
MyTextField {
text: MySettings.threadCount
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
Layout.alignment: Qt.AlignRight
Layout.row: 11
Layout.column: 2
Layout.minimumWidth: 200
Layout.maximumWidth: 200
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.threadCount = val
focus = false
} else {
text = MySettings.threadCount
}
}
Accessible.role: Accessible.EditableText
Accessible.name: nThreadsLabel.text
Accessible.description: ToolTip.text
}
MySettingsLabel {
id: saveChatsContextLabel
text: qsTr("Save Chat Context")
helpText: qsTr("Save the chat model's state to disk for faster loading. WARNING: Uses ~2GB per chat.")
Layout.row: 12
Layout.column: 0
}
MyCheckBox {
id: saveChatsContextBox
Layout.row: 12
Layout.column: 2
Layout.alignment: Qt.AlignRight
checked: MySettings.saveChatsContext
onClicked: {
MySettings.saveChatsContext = !MySettings.saveChatsContext
}
}
MySettingsLabel {
id: serverChatLabel
text: qsTr("Enable Local Server")
helpText: qsTr("Expose an OpenAI-Compatible server to localhost. WARNING: Results in increased resource usage.")
Layout.row: 13
Layout.column: 0
}
MyCheckBox {
id: serverChatBox
Layout.row: 13
Layout.column: 2
Layout.alignment: Qt.AlignRight
checked: MySettings.serverChat
onClicked: {
MySettings.serverChat = !MySettings.serverChat
}
}
MySettingsLabel {
id: serverPortLabel
text: qsTr("API Server Port")
helpText: qsTr("The port to use for the local server. Requires restart.")
Layout.row: 14
Layout.column: 0
}
MyTextField {
id: serverPortField
text: MySettings.networkPort
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
Layout.row: 14
Layout.column: 2
Layout.minimumWidth: 200
Layout.maximumWidth: 200
Layout.alignment: Qt.AlignRight
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.networkPort = val
focus = false
} else {
text = MySettings.networkPort
}
}
Accessible.role: Accessible.EditableText
Accessible.name: serverPortLabel.text
Accessible.description: serverPortLabel.helpText
}
/*MySettingsLabel {
id: gpuOverrideLabel
text: qsTr("Force Metal (macOS+arm)")
Layout.row: 13
Layout.column: 0
}
MyCheckBox {
id: gpuOverrideBox
Layout.row: 13
Layout.column: 2
Layout.alignment: Qt.AlignRight
checked: MySettings.forceMetal
onClicked: {
MySettings.forceMetal = !MySettings.forceMetal
}
ToolTip.text: qsTr("WARNING: On macOS with arm (M1+) this setting forces usage of the GPU. Can cause crashes if the model requires more RAM than the system supports. Because of crash possibility the setting will not persist across restarts of the application. This has no effect on non-macs or intel.")
ToolTip.visible: hovered
}*/
MySettingsLabel {
id: updatesLabel
text: qsTr("Check For Updates")
helpText: qsTr("Manually check for an update to GPT4All.");
Layout.row: 15
Layout.column: 0
}
MySettingsButton {
Layout.row: 15
Layout.column: 2
Layout.alignment: Qt.AlignRight
text: qsTr("Updates");
onClicked: {
if (!LLM.checkForUpdates())
checkForUpdatesError.open()
}
}
Rectangle {
Layout.row: 16
Layout.column: 0
Layout.columnSpan: 3
Layout.fillWidth: true
height: 1
color: theme.settingsDivider
}
}
}

View File

@@ -1,322 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import chatlistmodel
import llm
import download
import network
import mysettings
Rectangle {
id: chatDrawer
Theme {
id: theme
}
color: theme.viewBackground
Rectangle {
id: borderRight
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
width: 1
color: theme.dividerColor
}
Item {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: borderRight.left
Accessible.role: Accessible.Pane
Accessible.name: qsTr("Drawer")
Accessible.description: qsTr("Main navigation drawer")
MySettingsButton {
id: newChat
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 20
font.pixelSize: theme.fontSizeLarger
topPadding: 24
bottomPadding: 24
text: qsTr("\uFF0B New Chat")
Accessible.description: qsTr("Create a new chat")
onClicked: {
ChatListModel.addChat()
conversationList.positionViewAtIndex(0, ListView.Beginning)
Network.trackEvent("new_chat", {"number_of_chats": ChatListModel.count})
}
}
Rectangle {
id: divider
anchors.top: newChat.bottom
anchors.margins: 20
anchors.topMargin: 14
anchors.left: parent.left
anchors.right: parent.right
height: 1
color: theme.dividerColor
}
ScrollView {
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 15
anchors.top: divider.bottom
anchors.bottom: parent.bottom
anchors.bottomMargin: 15
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
clip: true
ListView {
id: conversationList
anchors.fill: parent
anchors.leftMargin: 10
anchors.rightMargin: 10
model: ChatListModel
Component.onCompleted: ChatListModel.loadChats()
ScrollBar.vertical: ScrollBar {
parent: conversationList.parent
anchors.top: conversationList.top
anchors.left: conversationList.right
anchors.bottom: conversationList.bottom
}
Component {
id: sectionHeading
Rectangle {
width: ListView.view.width
height: childrenRect.height
color: "transparent"
property bool isServer: ChatListModel.get(parent.index) && ChatListModel.get(parent.index).isServer
visible: !isServer || MySettings.serverChat
required property string section
Text {
leftPadding: 10
rightPadding: 10
topPadding: 15
bottomPadding: 5
text: parent.section
color: theme.chatDrawerSectionHeader
font.pixelSize: theme.fontSizeSmallest
}
}
}
section.property: "section"
section.criteria: ViewSection.FullString
section.delegate: sectionHeading
delegate: Rectangle {
id: chatRectangle
width: conversationList.width
height: chatNameBox.height + 20
property bool isCurrent: ChatListModel.currentChat === ChatListModel.get(index)
property bool isServer: ChatListModel.get(index) && ChatListModel.get(index).isServer
property bool trashQuestionDisplayed: false
visible: !isServer || MySettings.serverChat
z: isCurrent ? 199 : 1
color: isCurrent ? theme.selectedBackground : "transparent"
border.width: isCurrent
border.color: theme.dividerColor
radius: 10
Rectangle {
id: chatNameBox
height: chatName.height
anchors.left: parent.left
anchors.right: trashButton.left
anchors.verticalCenter: chatRectangle.verticalCenter
anchors.leftMargin: 5
anchors.rightMargin: 5
radius: 5
color: chatName.readOnly ? "transparent" : theme.chatNameEditBgColor
TextField {
id: chatName
anchors.left: parent.left
anchors.right: editButton.left
anchors.verticalCenter: chatNameBox.verticalCenter
topPadding: 5
bottomPadding: 5
color: theme.styledTextColor
focus: false
readOnly: true
wrapMode: Text.NoWrap
hoverEnabled: false // Disable hover events on the TextArea
selectByMouse: false // Disable text selection in the TextArea
font.pixelSize: theme.fontSizeLarge
font.bold: true
text: readOnly ? metrics.elidedText : name
horizontalAlignment: TextInput.AlignLeft
opacity: trashQuestionDisplayed ? 0.5 : 1.0
TextMetrics {
id: metrics
font: chatName.font
text: name
elide: Text.ElideRight
elideWidth: chatName.width - 15
}
background: Rectangle {
color: "transparent"
}
onEditingFinished: {
// Work around a bug in qml where we're losing focus when the whole window
// goes out of focus even though this textfield should be marked as not
// having focus
if (chatName.readOnly)
return;
changeName();
}
function changeName() {
Network.trackChatEvent("rename_chat");
ChatListModel.get(index).name = chatName.text;
chatName.focus = false;
chatName.readOnly = true;
chatName.selectByMouse = false;
}
TapHandler {
onTapped: {
if (isCurrent)
return;
ChatListModel.currentChat = ChatListModel.get(index);
}
}
Accessible.role: Accessible.Button
Accessible.name: text
Accessible.description: qsTr("Select the current chat or edit the chat when in edit mode")
}
MyToolButton {
id: editButton
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 5
imageWidth: 24
imageHeight: 24
visible: isCurrent && !isServer && chatName.readOnly
opacity: trashQuestionDisplayed ? 0.5 : 1.0
source: "qrc:/gpt4all/icons/edit.svg"
onClicked: {
chatName.focus = true;
chatName.readOnly = false;
chatName.selectByMouse = true;
}
Accessible.name: qsTr("Edit chat name")
}
MyToolButton {
id: okButton
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 5
imageWidth: 24
imageHeight: 24
visible: isCurrent && !isServer && !chatName.readOnly
opacity: trashQuestionDisplayed ? 0.5 : 1.0
source: "qrc:/gpt4all/icons/check.svg"
onClicked: chatName.changeName()
Accessible.name: qsTr("Save chat name")
}
}
MyToolButton {
id: trashButton
anchors.verticalCenter: chatNameBox.verticalCenter
anchors.right: chatRectangle.right
anchors.rightMargin: 10
imageWidth: 24
imageHeight: 24
visible: isCurrent && !isServer
source: "qrc:/gpt4all/icons/trash.svg"
onClicked: {
trashQuestionDisplayed = true
timer.start()
}
Accessible.name: qsTr("Delete chat")
}
Rectangle {
id: trashSureQuestion
anchors.top: trashButton.bottom
anchors.topMargin: 10
anchors.right: trashButton.right
width: childrenRect.width
height: childrenRect.height
color: chatRectangle.color
visible: isCurrent && trashQuestionDisplayed
opacity: 1.0
radius: 10
z: 200
Row {
spacing: 10
Button {
id: checkMark
width: 30
height: 30
contentItem: Text {
color: theme.textErrorColor
text: "\u2713"
font.pixelSize: theme.fontSizeLarger
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
width: 30
height: 30
color: "transparent"
}
onClicked: {
Network.trackChatEvent("remove_chat")
ChatListModel.removeChat(ChatListModel.get(index))
}
Accessible.role: Accessible.Button
Accessible.name: qsTr("Confirm chat deletion")
}
Button {
id: cancel
width: 30
height: 30
contentItem: Text {
color: theme.textColor
text: "\u2715"
font.pixelSize: theme.fontSizeLarger
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
width: 30
height: 30
color: "transparent"
}
onClicked: {
trashQuestionDisplayed = false
}
Accessible.role: Accessible.Button
Accessible.name: qsTr("Cancel chat deletion")
}
}
}
Timer {
id: timer
interval: 3000; running: false; repeat: false
onTriggered: trashQuestionDisplayed = false
}
}
Accessible.role: Accessible.List
Accessible.name: qsTr("List of chats")
Accessible.description: qsTr("List of chats in the drawer dialog")
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,146 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import QtQuick.Dialogs
import chatlistmodel
import localdocs
import llm
Rectangle {
id: collectionsDrawer
color: "transparent"
signal addDocsClicked
property var currentChat: ChatListModel.currentChat
Rectangle {
id: borderLeft
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
width: 1
color: theme.dividerColor
}
ScrollView {
id: scrollView
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: borderLeft.right
anchors.right: parent.right
anchors.margins: 2
anchors.bottomMargin: 10
clip: true
contentHeight: 300
ScrollBar.vertical.policy: ScrollBar.AsNeeded
ListView {
id: listView
model: LocalDocs.localDocsModel
anchors.fill: parent
anchors.margins: 13
anchors.bottomMargin: 5
boundsBehavior: Flickable.StopAtBounds
spacing: 15
delegate: Rectangle {
width: listView.width
height: childrenRect.height + 15
color: checkBox.checked ? theme.collectionsButtonBackground : "transparent"
RowLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 7.5
MyCheckBox {
id: checkBox
Layout.alignment: Qt.AlignLeft
checked: currentChat.hasCollection(collection)
onClicked: {
if (checkBox.checked) {
currentChat.addCollection(collection)
} else {
currentChat.removeCollection(collection)
}
}
ToolTip.text: qsTr("Warning: searching collections while indexing can return incomplete results")
ToolTip.visible: hovered && model.indexing
}
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
Text {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
text: collection
font.pixelSize: theme.fontSizeLarger
elide: Text.ElideRight
color: theme.textColor
}
Text {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
text: "%1 %2".arg(qsTr("%n file(s)", "", model.totalDocs)).arg(qsTr("%n word(s)", "", model.totalWords))
elide: Text.ElideRight
color: theme.mutedTextColor
font.pixelSize: theme.fontSizeSmall
}
RowLayout {
visible: model.updating
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
MyBusyIndicator {
color: theme.accentColor
size: 24
Layout.minimumWidth: 24
Layout.minimumHeight: 24
}
Text {
text: qsTr("Updating")
elide: Text.ElideRight
color: theme.accentColor
font.pixelSize: theme.fontSizeSmall
font.bold: true
}
}
}
}
}
footer: ColumnLayout {
width: listView.width
spacing: 30
Rectangle {
visible: listView.count !== 0
Layout.topMargin: 30
Layout.fillWidth: true
height: 1
color: theme.dividerColor
}
MySettingsButton {
id: collectionSettings
enabled: LocalDocs.databaseValid
Layout.alignment: Qt.AlignCenter
text: qsTr("\uFF0B Add Docs")
font.pixelSize: theme.fontSizeLarger
onClicked: {
addDocsClicked()
}
}
Text {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
text: qsTr("Select a collection to make it available to the chat model.")
font.pixelSize: theme.fontSizeLarger
wrapMode: Text.WordWrap
elide: Text.ElideRight
color: theme.mutedTextColor
}
}
}
}
}

View File

@@ -1,288 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import llm
import chatlistmodel
import download
import modellist
import network
import gpt4all
import mysettings
Rectangle {
id: homeView
Theme {
id: theme
}
color: theme.viewBackground
signal chatViewRequested()
signal localDocsViewRequested()
signal settingsViewRequested(int page)
signal addModelViewRequested()
property bool shouldShowFirstStart: false
ColumnLayout {
id: mainArea
anchors.fill: parent
anchors.margins: 30
spacing: 30
ColumnLayout {
Layout.fillWidth: true
Layout.maximumWidth: 1530
Layout.alignment: Qt.AlignCenter
Layout.topMargin: 20
spacing: 30
ColumnLayout {
Layout.alignment: Qt.AlignHCenter
spacing: 5
Text {
id: welcome
Layout.alignment: Qt.AlignHCenter
text: qsTr("Welcome to GPT4All")
font.pixelSize: theme.fontSizeBanner
color: theme.titleTextColor
}
Text {
Layout.alignment: Qt.AlignHCenter
text: qsTr("The privacy-first LLM chat application")
font.pixelSize: theme.fontSizeLarge
color: theme.titleInfoTextColor
}
}
MyButton {
id: startChat
visible: shouldShowFirstStart
Layout.alignment: Qt.AlignHCenter
text: qsTr("Start chatting")
onClicked: {
chatViewRequested()
}
}
RowLayout {
spacing: 15
visible: !startChat.visible
Layout.alignment: Qt.AlignHCenter
MyWelcomeButton {
Layout.fillWidth: true
Layout.maximumWidth: 500
Layout.preferredHeight: 150
text: qsTr("Start Chatting")
description: qsTr("Chat with any LLM")
imageSource: "qrc:/gpt4all/icons/chat.svg"
onClicked: {
chatViewRequested()
}
}
MyWelcomeButton {
Layout.fillWidth: true
Layout.maximumWidth: 500
Layout.preferredHeight: 150
text: qsTr("LocalDocs")
description: qsTr("Chat with your local files")
imageSource: "qrc:/gpt4all/icons/db.svg"
onClicked: {
localDocsViewRequested()
}
}
MyWelcomeButton {
Layout.fillWidth: true
Layout.maximumWidth: 500
Layout.preferredHeight: 150
text: qsTr("Find Models")
description: qsTr("Explore and download models")
imageSource: "qrc:/gpt4all/icons/models.svg"
onClicked: {
addModelViewRequested()
}
}
}
Item {
visible: !startChat.visible && Download.latestNews !== ""
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: 120
Layout.maximumHeight: textAreaNews.height
Rectangle {
id: roundedFrameNews // latest news
anchors.fill: parent
z: 299
radius: 10
border.width: 1
border.color: theme.controlBorder
color: "transparent"
clip: true
}
Item {
anchors.fill: parent
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
width: roundedFrameNews.width
height: roundedFrameNews.height
radius: 10
}
}
RowLayout {
spacing: 0
anchors.fill: parent
Rectangle {
color: "transparent"
width: 82
height: 100
Image {
id: newsImg
anchors.centerIn: parent
sourceSize: Qt.size(48, 48)
mipmap: true
visible: false
source: "qrc:/gpt4all/icons/gpt4all_transparent.svg"
}
ColorOverlay {
anchors.fill: newsImg
source: newsImg
color: theme.styledTextColor
}
}
Item {
id: myItem
Layout.fillWidth: true
Layout.fillHeight: true
Rectangle {
anchors.fill: parent
color: theme.conversationBackground
}
ScrollView {
id: newsScroll
anchors.fill: parent
clip: true
ScrollBar.vertical.policy: ScrollBar.AsNeeded
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
Text {
id: textAreaNews
width: myItem.width
padding: 20
color: theme.styledTextColor
font.pixelSize: theme.fontSizeLarger
textFormat: TextEdit.MarkdownText
wrapMode: Text.WordWrap
text: Download.latestNews
focus: false
Accessible.role: Accessible.Paragraph
Accessible.name: qsTr("Latest news")
Accessible.description: qsTr("Latest news from GPT4All")
onLinkActivated: function(link) {
Qt.openUrlExternally(link);
}
}
}
}
}
}
}
}
Rectangle {
id: linkBar
Layout.alignment: Qt.AlignBottom
Layout.fillWidth: true
border.width: 1
border.color: theme.dividerColor
radius: 6
z: 200
height: 30
color: theme.conversationBackground
RowLayout {
anchors.fill: parent
spacing: 0
RowLayout {
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
spacing: 4
MyFancyLink {
text: qsTr("Release Notes")
imageSource: "qrc:/gpt4all/icons/notes.svg"
onClicked: { Qt.openUrlExternally("https://github.com/nomic-ai/gpt4all/releases") }
}
MyFancyLink {
text: qsTr("Documentation")
imageSource: "qrc:/gpt4all/icons/info.svg"
onClicked: { Qt.openUrlExternally("https://docs.gpt4all.io/") }
}
MyFancyLink {
text: qsTr("Discord")
imageSource: "qrc:/gpt4all/icons/discord.svg"
onClicked: { Qt.openUrlExternally("https://discord.gg/4M2QFmTt2k") }
}
MyFancyLink {
text: qsTr("X (Twitter)")
imageSource: "qrc:/gpt4all/icons/twitter.svg"
onClicked: { Qt.openUrlExternally("https://twitter.com/nomic_ai") }
}
MyFancyLink {
text: qsTr("Github")
imageSource: "qrc:/gpt4all/icons/github.svg"
onClicked: { Qt.openUrlExternally("https://github.com/nomic-ai/gpt4all") }
}
}
RowLayout {
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
spacing: 40
MyFancyLink {
text: qsTr("nomic.ai")
imageSource: "qrc:/gpt4all/icons/globe.svg"
onClicked: { Qt.openUrlExternally("https://www.nomic.ai/gpt4all") }
rightPadding: 15
}
}
}
}
}
Rectangle {
anchors.top: mainArea.top
anchors.right: mainArea.right
border.width: 1
border.color: theme.dividerColor
radius: 6
z: 200
height: 30
color: theme.conversationBackground
width: subscribeLink.width
RowLayout {
anchors.centerIn: parent
MyFancyLink {
id: subscribeLink
Layout.alignment: Qt.AlignCenter
text: qsTr("Subscribe to Newsletter")
imageSource: "qrc:/gpt4all/icons/email.svg"
onClicked: { Qt.openUrlExternally("https://forms.nomic.ai/gpt4all-release-notes-signup") }
}
}
}
}

View File

@@ -1,321 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import QtQuick.Dialogs
import localdocs
import modellist
import mysettings
import network
MySettingsTab {
onRestoreDefaultsClicked: {
MySettings.restoreLocalDocsDefaults();
}
showRestoreDefaultsButton: true
title: qsTr("LocalDocs")
contentItem: ColumnLayout {
id: root
spacing: 30
Label {
Layout.bottomMargin: 10
color: theme.settingsTitleTextColor
font.pixelSize: theme.fontSizeBannerSmall
font.bold: true
text: qsTr("LocalDocs Settings")
}
ColumnLayout {
spacing: 10
Label {
color: theme.styledTextColor
font.pixelSize: theme.fontSizeLarge
font.bold: true
text: qsTr("Indexing")
}
Rectangle {
Layout.fillWidth: true
height: 1
color: theme.settingsDivider
}
}
RowLayout {
MySettingsLabel {
id: extsLabel
text: qsTr("Allowed File Extensions")
helpText: qsTr("Comma-separated list. LocalDocs will only attempt to process files with these extensions.")
}
MyTextField {
id: extsField
text: MySettings.localDocsFileExtensions.join(',')
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
Layout.alignment: Qt.AlignRight
Layout.minimumWidth: 200
validator: RegularExpressionValidator {
regularExpression: /([^ ,\/"']+,?)*/
}
onEditingFinished: {
// split and remove empty elements
var exts = text.split(',').filter(e => e);
// normalize and deduplicate
exts = exts.map(e => e.toLowerCase());
exts = Array.from(new Set(exts));
/* Blacklist common unsupported file extensions. We only support plain text and PDFs, and although we
* reject binary data, we don't want to waste time trying to index files that we don't support. */
exts = exts.filter(e => ![
/* Microsoft documents */ "rtf", "docx", "ppt", "pptx", "xls", "xlsx",
/* OpenOffice */ "odt", "ods", "odp", "odg",
/* photos */ "jpg", "jpeg", "png", "gif", "bmp", "tif", "tiff", "webp",
/* audio */ "mp3", "wma", "m4a", "wav", "flac",
/* videos */ "mp4", "mov", "webm", "mkv", "avi", "flv", "wmv",
/* executables */ "exe", "com", "dll", "so", "dylib", "msi",
/* binary images */ "iso", "img", "dmg",
/* archives */ "zip", "jar", "apk", "rar", "7z", "tar", "gz", "xz", "bz2", "tar.gz",
"tgz", "tar.xz", "tar.bz2",
/* misc */ "bin",
].includes(e));
MySettings.localDocsFileExtensions = exts;
extsField.text = exts.join(',');
focus = false;
}
Accessible.role: Accessible.EditableText
Accessible.name: extsLabel.text
Accessible.description: extsLabel.helpText
}
}
ColumnLayout {
spacing: 10
Label {
color: theme.grayRed900
font.pixelSize: theme.fontSizeLarge
font.bold: true
text: qsTr("Embedding")
}
Rectangle {
Layout.fillWidth: true
height: 1
color: theme.grayRed500
}
}
RowLayout {
MySettingsLabel {
text: qsTr("Use Nomic Embed API")
helpText: qsTr("Embed documents using the fast Nomic API instead of a private local model. Requires restart.")
}
MyCheckBox {
id: useNomicAPIBox
Component.onCompleted: {
useNomicAPIBox.checked = MySettings.localDocsUseRemoteEmbed;
}
onClicked: {
MySettings.localDocsUseRemoteEmbed = useNomicAPIBox.checked && MySettings.localDocsNomicAPIKey !== "";
}
}
}
RowLayout {
MySettingsLabel {
id: apiKeyLabel
text: qsTr("Nomic API Key")
helpText: qsTr('API key to use for Nomic Embed. Get one from the Atlas <a href="https://atlas.nomic.ai/cli-login">API keys page</a>. Requires restart.')
onLinkActivated: function(link) { Qt.openUrlExternally(link) }
}
MyTextField {
id: apiKeyField
property bool isValid: validate()
onTextChanged: { isValid = validate(); }
function validate() { return /^(nk-[a-zA-Z0-9_-]{43})?$/.test(apiKeyField.text); }
placeholderText: "nk-" + "X".repeat(43)
text: MySettings.localDocsNomicAPIKey
color: apiKeyField.isValid ? theme.textColor : theme.textErrorColor
font.pixelSize: theme.fontSizeLarge
Layout.alignment: Qt.AlignRight
Layout.minimumWidth: 200
enabled: useNomicAPIBox.checked
onEditingFinished: {
if (apiKeyField.isValid) {
MySettings.localDocsNomicAPIKey = apiKeyField.text;
MySettings.localDocsUseRemoteEmbed = useNomicAPIBox.checked && MySettings.localDocsNomicAPIKey !== "";
}
focus = false;
}
Accessible.role: Accessible.EditableText
Accessible.name: apiKeyLabel.text
Accessible.description: apiKeyLabel.helpText
}
}
RowLayout {
MySettingsLabel {
id: deviceLabel
text: qsTr("Embeddings Device")
helpText: qsTr('The compute device used for embeddings. Requires restart.')
}
MyComboBox {
id: deviceBox
enabled: !useNomicAPIBox.checked
Layout.minimumWidth: 400
Layout.maximumWidth: 400
Layout.fillWidth: false
Layout.alignment: Qt.AlignRight
model: ListModel {
ListElement { text: qsTr("Application default") }
Component.onCompleted: {
MySettings.embeddingsDeviceList.forEach(d => append({"text": d}));
}
}
Accessible.name: deviceLabel.text
Accessible.description: deviceLabel.helpText
function updateModel() {
var device = MySettings.localDocsEmbedDevice;
// This usage of 'Auto' should not be translated
deviceBox.currentIndex = device === "Auto" ? 0 : deviceBox.indexOfValue(device);
}
Component.onCompleted: {
deviceBox.updateModel();
}
Connections {
target: MySettings
function onDeviceChanged() {
deviceBox.updateModel();
}
}
onActivated: {
// This usage of 'Auto' should not be translated
MySettings.localDocsEmbedDevice = deviceBox.currentIndex === 0 ? "Auto" : deviceBox.currentText;
}
}
}
ColumnLayout {
spacing: 10
Label {
color: theme.grayRed900
font.pixelSize: theme.fontSizeLarge
font.bold: true
text: qsTr("Display")
}
Rectangle {
Layout.fillWidth: true
height: 1
color: theme.grayRed500
}
}
RowLayout {
MySettingsLabel {
id: showReferencesLabel
text: qsTr("Show Sources")
helpText: qsTr("Display the sources used for each response.")
}
MyCheckBox {
id: showReferencesBox
checked: MySettings.localDocsShowReferences
onClicked: {
MySettings.localDocsShowReferences = !MySettings.localDocsShowReferences
}
}
}
ColumnLayout {
spacing: 10
Label {
color: theme.styledTextColor
font.pixelSize: theme.fontSizeLarge
font.bold: true
text: qsTr("Advanced")
}
Rectangle {
Layout.fillWidth: true
height: 1
color: theme.settingsDivider
}
}
MySettingsLabel {
id: warningLabel
Layout.bottomMargin: 15
Layout.fillWidth: true
color: theme.textErrorColor
wrapMode: Text.WordWrap
text: qsTr("Warning: Advanced usage only.")
helpText: qsTr("Values too large may cause localdocs failure, extremely slow responses or failure to respond at all. Roughly speaking, the {N chars x N snippets} are added to the model's context window. More info <a href=\"https://docs.gpt4all.io/gpt4all_desktop/localdocs.html\">here</a>.")
onLinkActivated: function(link) { Qt.openUrlExternally(link) }
}
RowLayout {
MySettingsLabel {
id: chunkLabel
Layout.fillWidth: true
text: qsTr("Document snippet size (characters)")
helpText: qsTr("Number of characters per document snippet. Larger numbers increase likelihood of factual responses, but also result in slower generation.")
}
MyTextField {
id: chunkSizeTextField
text: MySettings.localDocsChunkSize
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.localDocsChunkSize = val
focus = false
} else {
text = MySettings.localDocsChunkSize
}
}
}
}
RowLayout {
Layout.topMargin: 15
MySettingsLabel {
id: contextItemsPerPrompt
text: qsTr("Max document snippets per prompt")
helpText: qsTr("Max best N matches of retrieved document snippets to add to the context for prompt. Larger numbers increase likelihood of factual responses, but also result in slower generation.")
}
MyTextField {
text: MySettings.localDocsRetrievalSize
validator: IntValidator {
bottom: 1
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.localDocsRetrievalSize = val
focus = false
} else {
text = MySettings.localDocsRetrievalSize
}
}
}
}
Rectangle {
Layout.topMargin: 15
Layout.fillWidth: true
height: 1
color: theme.settingsDivider
}
}
}

View File

@@ -1,443 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import llm
import chatlistmodel
import download
import modellist
import network
import gpt4all
import mysettings
import localdocs
Rectangle {
id: localDocsView
Theme {
id: theme
}
color: theme.viewBackground
signal chatViewRequested()
signal localDocsViewRequested()
signal settingsViewRequested(int page)
signal addCollectionViewRequested()
ColumnLayout {
id: mainArea
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.margins: 30
spacing: 50
RowLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
visible: LocalDocs.databaseValid && LocalDocs.localDocsModel.count !== 0
spacing: 50
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
Layout.minimumWidth: 200
spacing: 5
Text {
id: welcome
text: qsTr("LocalDocs")
font.pixelSize: theme.fontSizeBanner
color: theme.titleTextColor
}
Text {
text: qsTr("Chat with your local files")
font.pixelSize: theme.fontSizeLarge
color: theme.titleInfoTextColor
}
}
Rectangle {
Layout.fillWidth: true
height: 0
}
MyButton {
Layout.alignment: Qt.AlignTop | Qt.AlignRight
text: qsTr("\uFF0B Add Collection")
onClicked: {
addCollectionViewRequested()
}
}
}
Rectangle {
id: warning
Layout.fillWidth: true
Layout.fillHeight: true
visible: !LocalDocs.databaseValid
Text {
anchors.centerIn: parent
text: qsTr("<h3>ERROR: The LocalDocs database cannot be accessed or is not valid.</h3><br>"
+ "<i>Note: You will need to restart after trying any of the following suggested fixes.</i><br>"
+ "<ul><li>Make sure that the folder set as <b>Download Path</b> exists on the file system.</li>"
+ "<li>Check ownership as well as read and write permissions of the <b>Download Path</b>.</li>"
+ "<li>If there is a <b>localdocs_v2.db</b> file, check its ownership and read/write "
+ "permissions, too.</li></ul><br>"
+ "If the problem persists and there are any 'localdocs_v*.db' files present, as a last resort you can<br>"
+ "try backing them up and removing them. You will have to recreate your collections, however.")
color: theme.textErrorColor
font.pixelSize: theme.fontSizeLarger
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
visible: LocalDocs.databaseValid && LocalDocs.localDocsModel.count === 0
ColumnLayout {
id: noInstalledLabel
anchors.centerIn: parent
spacing: 0
Text {
Layout.alignment: Qt.AlignCenter
text: qsTr("No Collections Installed")
color: theme.mutedLightTextColor
font.pixelSize: theme.fontSizeBannerSmall
}
Text {
Layout.topMargin: 15
horizontalAlignment: Qt.AlignHCenter
color: theme.mutedLighterTextColor
text: qsTr("Install a collection of local documents to get started using this feature")
font.pixelSize: theme.fontSizeLarge
}
}
MyButton {
anchors.top: noInstalledLabel.bottom
anchors.topMargin: 50
anchors.horizontalCenter: noInstalledLabel.horizontalCenter
rightPadding: 60
leftPadding: 60
text: qsTr("\uFF0B Add Doc Collection")
onClicked: {
addCollectionViewRequested()
}
Accessible.role: Accessible.Button
Accessible.name: qsTr("Shows the add model view")
}
}
ScrollView {
id: scrollView
ScrollBar.vertical.policy: ScrollBar.AsNeeded
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
visible: LocalDocs.databaseValid && LocalDocs.localDocsModel.count !== 0
ListView {
id: collectionListView
model: LocalDocs.localDocsModel
boundsBehavior: Flickable.StopAtBounds
spacing: 30
delegate: Rectangle {
width: collectionListView.width
height: childrenRect.height + 60
color: theme.conversationBackground
radius: 10
border.width: 1
border.color: theme.controlBorder
property bool removing: false
ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 30
spacing: 10
RowLayout {
Layout.fillWidth: true
Text {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
text: collection
elide: Text.ElideRight
color: theme.titleTextColor
font.pixelSize: theme.fontSizeLargest
font.bold: true
}
Item {
Layout.alignment: Qt.AlignRight
Layout.preferredWidth: state.contentWidth + 50
Layout.preferredHeight: state.contentHeight + 10
ProgressBar {
id: itemProgressBar
anchors.fill: parent
value: {
if (model.error !== "")
return 0
if (model.indexing)
return (model.totalBytesToIndex - model.currentBytesToIndex) / model.totalBytesToIndex
if (model.currentEmbeddingsToIndex !== 0)
return (model.totalEmbeddingsToIndex - model.currentEmbeddingsToIndex) / model.totalEmbeddingsToIndex
return 0
}
background: Rectangle {
implicitHeight: 45
color: {
if (model.error !== "")
return "transparent"
if (model.indexing)
return theme.altProgressBackground
if (model.currentEmbeddingsToIndex !== 0)
return theme.altProgressBackground
if (model.forceIndexing)
return theme.red200
return theme.lightButtonBackground
}
radius: 6
}
contentItem: Item {
implicitHeight: 40
Rectangle {
width: itemProgressBar.visualPosition * parent.width
height: parent.height
radius: 2
color: theme.altProgressForeground
}
}
Accessible.role: Accessible.ProgressBar
Accessible.name: qsTr("Indexing progressBar")
Accessible.description: qsTr("Shows the progress made in the indexing")
ToolTip.text: model.error
ToolTip.visible: hovered && model.error !== ""
}
Label {
id: state
anchors.centerIn: itemProgressBar
horizontalAlignment: Text.AlignHCenter
color: {
if (model.error !== "")
return theme.textErrorColor
if (model.indexing)
return theme.altProgressText
if (model.currentEmbeddingsToIndex !== 0)
return theme.altProgressText
if (model.forceIndexing)
return theme.textErrorColor
return theme.lighterButtonForeground
}
text: {
if (model.error !== "")
return qsTr("ERROR")
// indicates extracting snippets from documents
if (model.indexing)
return qsTr("INDEXING")
// indicates generating the embeddings for any outstanding snippets
if (model.currentEmbeddingsToIndex !== 0)
return qsTr("EMBEDDING")
if (model.forceIndexing)
return qsTr("REQUIRES UPDATE")
if (model.installed)
return qsTr("READY")
return qsTr("INSTALLING")
}
elide: Text.ElideRight
font.bold: true
font.pixelSize: theme.fontSizeSmaller
}
}
}
RowLayout {
Layout.fillWidth: true
Text {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
text: folder_path
elide: Text.ElideRight
color: theme.titleTextColor2
font.pixelSize: theme.fontSizeSmall
}
Text {
Layout.alignment: Qt.AlignRight
text: {
if (model.error !== "")
return model.error
if (model.indexing)
return qsTr("Indexing in progress")
if (model.currentEmbeddingsToIndex !== 0)
return qsTr("Embedding in progress")
if (model.forceIndexing)
return qsTr("This collection requires an update after version change")
if (model.installed)
return qsTr("Automatically reindexes upon changes to the folder")
return qsTr("Installation in progress")
}
elide: Text.ElideRight
color: theme.mutedDarkTextColor
font.pixelSize: theme.fontSizeSmall
}
Text {
visible: {
return model.indexing || model.currentEmbeddingsToIndex !== 0
}
Layout.alignment: Qt.AlignRight
text: {
var percentComplete = Math.round(itemProgressBar.value * 100);
var formattedPercent = percentComplete < 10 ? " " + percentComplete : percentComplete.toString();
return formattedPercent + qsTr("%")
}
elide: Text.ElideRight
color: theme.mutedDarkTextColor
font.family: "monospace"
font.pixelSize: theme.fontSizeSmall
}
}
RowLayout {
spacing: 7
Text {
text: "%1 %2".arg(qsTr("%n file(s)", "", model.totalDocs)).arg(qsTr("%n word(s)", "", model.totalWords))
elide: Text.ElideRight
color: theme.styledTextColor2
font.pixelSize: theme.fontSizeSmall
}
Text {
text: model.embeddingModel
elide: Text.ElideRight
color: theme.mutedDarkTextColor
font.bold: true
font.pixelSize: theme.fontSizeSmall
}
Text {
visible: Qt.formatDateTime(model.lastUpdate) !== ""
text: Qt.formatDateTime(model.lastUpdate)
elide: Text.ElideRight
color: theme.mutedTextColor
font.pixelSize: theme.fontSizeSmall
}
Text {
visible: model.currentEmbeddingsToIndex !== 0
text: (model.totalEmbeddingsToIndex - model.currentEmbeddingsToIndex) + " of "
+ model.totalEmbeddingsToIndex + " embeddings"
elide: Text.ElideRight
color: theme.mutedTextColor
font.pixelSize: theme.fontSizeSmall
}
}
Rectangle {
Layout.fillWidth: true
height: 1
color: theme.dividerColor
}
RowLayout {
id: fileProcessingRow
Layout.topMargin: 15
Layout.bottomMargin: 15
visible: model.fileCurrentlyProcessing !== "" && (model.indexing || model.currentEmbeddingsToIndex !== 0)
MyBusyIndicator {
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: 12
Layout.preferredHeight: 12
running: true
size: 12
color: theme.textColor
}
Text {
id: filename
Layout.alignment: Qt.AlignCenter
text: model.fileCurrentlyProcessing
elide: Text.ElideRight
color: theme.textColor
font.bold: true
font.pixelSize: theme.fontSizeLarge
}
}
Rectangle {
visible: fileProcessingRow.visible
Layout.fillWidth: true
height: 1
color: theme.dividerColor
}
RowLayout {
Layout.fillWidth: true
spacing: 30
MySettingsButton {
text: qsTr("Remove")
textColor: theme.red500
onClicked: LocalDocs.removeFolder(collection, folder_path)
backgroundColor: "transparent"
backgroundColorHovered: theme.lighterButtonBackgroundHoveredRed
}
Item {
Layout.fillWidth: true
}
MySettingsButton {
id: rebuildButton
visible: !model.forceIndexing && !model.indexing && model.currentEmbeddingsToIndex === 0
text: qsTr("Rebuild")
textColor: theme.green500
onClicked: LocalDocs.forceRebuildFolder(folder_path)
toolTip: qsTr("Reindex this folder from scratch. This is slow and usually not needed.")
backgroundColor: "transparent"
backgroundColorHovered: theme.lighterButtonBackgroundHovered
}
MySettingsButton {
id: updateButton
visible: model.forceIndexing
text: qsTr("Update")
textColor: theme.green500
onClicked: LocalDocs.forceIndexing(collection)
toolTip: qsTr("Update the collection to the new version. This is a slow operation.")
backgroundColor: "transparent"
backgroundColorHovered: theme.lighterButtonBackgroundHovered
}
}
}
}
}
}
}
}

View File

@@ -1,845 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import modellist
import mysettings
import chatlistmodel
MySettingsTab {
onRestoreDefaultsClicked: {
MySettings.restoreModelDefaults(root.currentModelInfo);
}
title: qsTr("Model")
contentItem: GridLayout {
id: root
columns: 3
rowSpacing: 10
columnSpacing: 10
enabled: ModelList.selectableModels.count !== 0
property var currentModelName: comboBox.currentText
property var currentModelId: comboBox.currentValue
property var currentModelInfo: ModelList.modelInfo(root.currentModelId)
Label {
Layout.row: 1
Layout.column: 0
Layout.bottomMargin: 10
color: theme.settingsTitleTextColor
font.pixelSize: theme.fontSizeBannerSmall
font.bold: true
text: qsTr("Model Settings")
}
RowLayout {
Layout.fillWidth: true
Layout.row: 2
Layout.column: 0
Layout.columnSpan: 2
spacing: 10
MyComboBox {
id: comboBox
Layout.fillWidth: true
model: ModelList.selectableModels
valueRole: "id"
textRole: "name"
currentIndex: {
var i = comboBox.indexOfValue(ChatListModel.currentChat.modelInfo.id);
if (i >= 0)
return i;
return 0;
}
contentItem: Text {
leftPadding: 10
rightPadding: 20
text: comboBox.currentText
font: comboBox.font
color: theme.textColor
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
delegate: ItemDelegate {
width: comboBox.width -20
contentItem: Text {
text: name
color: theme.textColor
font: comboBox.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
radius: 10
color: highlighted ? theme.menuHighlightColor : theme.menuBackgroundColor
}
highlighted: comboBox.highlightedIndex === index
}
}
MySettingsButton {
id: cloneButton
text: qsTr("Clone")
onClicked: {
var id = ModelList.clone(root.currentModelInfo);
comboBox.currentIndex = comboBox.indexOfValue(id);
}
}
MySettingsDestructiveButton {
id: removeButton
enabled: root.currentModelInfo.isClone
text: qsTr("Remove")
onClicked: {
ModelList.removeClone(root.currentModelInfo);
comboBox.currentIndex = 0;
}
}
}
RowLayout {
Layout.row: 3
Layout.column: 0
Layout.topMargin: 15
spacing: 10
MySettingsLabel {
text: qsTr("Name")
}
}
MyTextField {
id: uniqueNameField
text: root.currentModelName
font.pixelSize: theme.fontSizeLarge
enabled: root.currentModelInfo.isClone || root.currentModelInfo.description === ""
Layout.row: 4
Layout.column: 0
Layout.columnSpan: 2
Layout.fillWidth: true
Connections {
target: MySettings
function onNameChanged() {
uniqueNameField.text = root.currentModelInfo.name;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
uniqueNameField.text = root.currentModelInfo.name;
}
}
onTextChanged: {
if (text !== "" && ModelList.isUniqueName(text)) {
MySettings.setModelName(root.currentModelInfo, text);
}
}
}
MySettingsLabel {
text: qsTr("Model File")
Layout.row: 5
Layout.column: 0
Layout.topMargin: 15
}
MyTextField {
text: root.currentModelInfo.filename
font.pixelSize: theme.fontSizeLarge
enabled: false
Layout.row: 6
Layout.column: 0
Layout.columnSpan: 2
Layout.fillWidth: true
}
MySettingsLabel {
visible: !root.currentModelInfo.isOnline
text: qsTr("System Prompt")
helpText: qsTr("Prefixed at the beginning of every conversation. Must contain the appropriate framing tokens.")
Layout.row: 7
Layout.column: 0
Layout.topMargin: 15
}
Rectangle {
id: systemPrompt
visible: !root.currentModelInfo.isOnline
Layout.row: 8
Layout.column: 0
Layout.columnSpan: 2
Layout.fillWidth: true
color: "transparent"
Layout.minimumHeight: Math.max(100, systemPromptArea.contentHeight + 20)
MyTextArea {
id: systemPromptArea
anchors.fill: parent
text: root.currentModelInfo.systemPrompt
Connections {
target: MySettings
function onSystemPromptChanged() {
systemPromptArea.text = root.currentModelInfo.systemPrompt;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
systemPromptArea.text = root.currentModelInfo.systemPrompt;
}
}
onTextChanged: {
MySettings.setModelSystemPrompt(root.currentModelInfo, text)
}
Accessible.role: Accessible.EditableText
}
}
RowLayout {
Layout.row: 9
Layout.column: 0
Layout.columnSpan: 2
Layout.topMargin: 15
spacing: 10
MySettingsLabel {
id: promptTemplateLabel
text: qsTr("Prompt Template")
helpText: qsTr("The template that wraps every prompt.")
}
MySettingsLabel {
id: promptTemplateLabelHelp
text: qsTr("Must contain the string \"%1\" to be replaced with the user's input.")
color: theme.textErrorColor
visible: templateTextArea.text.indexOf("%1") === -1
wrapMode: TextArea.Wrap
}
}
Rectangle {
id: promptTemplate
Layout.row: 10
Layout.column: 0
Layout.columnSpan: 2
Layout.fillWidth: true
Layout.minimumHeight: Math.max(100, templateTextArea.contentHeight + 20)
color: "transparent"
clip: true
MyTextArea {
id: templateTextArea
anchors.fill: parent
text: root.currentModelInfo.promptTemplate
Connections {
target: MySettings
function onPromptTemplateChanged() {
templateTextArea.text = root.currentModelInfo.promptTemplate;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
templateTextArea.text = root.currentModelInfo.promptTemplate;
}
}
onTextChanged: {
if (templateTextArea.text.indexOf("%1") !== -1) {
MySettings.setModelPromptTemplate(root.currentModelInfo, text)
}
}
Accessible.role: Accessible.EditableText
Accessible.name: promptTemplateLabel.text
Accessible.description: promptTemplateLabelHelp.text
}
}
MySettingsLabel {
id: chatNamePromptLabel
text: qsTr("Chat Name Prompt")
helpText: qsTr("Prompt used to automatically generate chat names.")
Layout.row: 11
Layout.column: 0
Layout.topMargin: 15
}
Rectangle {
id: chatNamePrompt
Layout.row: 12
Layout.column: 0
Layout.columnSpan: 2
Layout.fillWidth: true
Layout.minimumHeight: Math.max(100, chatNamePromptTextArea.contentHeight + 20)
color: "transparent"
clip: true
MyTextArea {
id: chatNamePromptTextArea
anchors.fill: parent
text: root.currentModelInfo.chatNamePrompt
Connections {
target: MySettings
function onChatNamePromptChanged() {
chatNamePromptTextArea.text = root.currentModelInfo.chatNamePrompt;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
chatNamePromptTextArea.text = root.currentModelInfo.chatNamePrompt;
}
}
onTextChanged: {
MySettings.setModelChatNamePrompt(root.currentModelInfo, text)
}
Accessible.role: Accessible.EditableText
Accessible.name: chatNamePromptLabel.text
Accessible.description: chatNamePromptLabel.text
}
}
MySettingsLabel {
id: suggestedFollowUpPromptLabel
text: qsTr("Suggested FollowUp Prompt")
helpText: qsTr("Prompt used to generate suggested follow-up questions.")
Layout.row: 13
Layout.column: 0
Layout.topMargin: 15
}
Rectangle {
id: suggestedFollowUpPrompt
Layout.row: 14
Layout.column: 0
Layout.columnSpan: 2
Layout.fillWidth: true
Layout.minimumHeight: Math.max(100, suggestedFollowUpPromptTextArea.contentHeight + 20)
color: "transparent"
clip: true
MyTextArea {
id: suggestedFollowUpPromptTextArea
anchors.fill: parent
text: root.currentModelInfo.suggestedFollowUpPrompt
Connections {
target: MySettings
function onSuggestedFollowUpPromptChanged() {
suggestedFollowUpPromptTextArea.text = root.currentModelInfo.suggestedFollowUpPrompt;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
suggestedFollowUpPromptTextArea.text = root.currentModelInfo.suggestedFollowUpPrompt;
}
}
onTextChanged: {
MySettings.setModelSuggestedFollowUpPrompt(root.currentModelInfo, text)
}
Accessible.role: Accessible.EditableText
Accessible.name: suggestedFollowUpPromptLabel.text
Accessible.description: suggestedFollowUpPromptLabel.text
}
}
GridLayout {
Layout.row: 15
Layout.column: 0
Layout.columnSpan: 2
Layout.topMargin: 15
Layout.fillWidth: true
columns: 4
rowSpacing: 30
columnSpacing: 10
MySettingsLabel {
id: contextLengthLabel
visible: !root.currentModelInfo.isOnline
text: qsTr("Context Length")
helpText: qsTr("Number of input and output tokens the model sees.")
Layout.row: 0
Layout.column: 0
Layout.maximumWidth: 300 * theme.fontScale
}
Item {
Layout.row: 0
Layout.column: 1
Layout.fillWidth: true
Layout.maximumWidth: 200
Layout.margins: 0
height: contextLengthField.height
MyTextField {
id: contextLengthField
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
visible: !root.currentModelInfo.isOnline
text: root.currentModelInfo.contextLength
font.pixelSize: theme.fontSizeLarge
color: theme.textColor
ToolTip.text: qsTr("Maximum combined prompt/response tokens before information is lost.\nUsing more context than the model was trained on will yield poor results.\nNOTE: Does not take effect until you reload the model.")
ToolTip.visible: hovered
Connections {
target: MySettings
function onContextLengthChanged() {
contextLengthField.text = root.currentModelInfo.contextLength;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
contextLengthField.text = root.currentModelInfo.contextLength;
}
}
onEditingFinished: {
var val = parseInt(text)
if (isNaN(val)) {
text = root.currentModelInfo.contextLength
} else {
if (val < 8) {
val = 8
contextLengthField.text = val
} else if (val > root.currentModelInfo.maxContextLength) {
val = root.currentModelInfo.maxContextLength
contextLengthField.text = val
}
MySettings.setModelContextLength(root.currentModelInfo, val)
focus = false
}
}
Accessible.role: Accessible.EditableText
Accessible.name: contextLengthLabel.text
Accessible.description: ToolTip.text
}
}
MySettingsLabel {
id: tempLabel
text: qsTr("Temperature")
helpText: qsTr("Randomness of model output. Higher -> more variation.")
Layout.row: 1
Layout.column: 2
Layout.maximumWidth: 300 * theme.fontScale
}
MyTextField {
id: temperatureField
text: root.currentModelInfo.temperature
font.pixelSize: theme.fontSizeLarge
color: theme.textColor
ToolTip.text: qsTr("Temperature increases the chances of choosing less likely tokens.\nNOTE: Higher temperature gives more creative but less predictable outputs.")
ToolTip.visible: hovered
Layout.row: 1
Layout.column: 3
validator: DoubleValidator {
locale: "C"
}
Connections {
target: MySettings
function onTemperatureChanged() {
temperatureField.text = root.currentModelInfo.temperature;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
temperatureField.text = root.currentModelInfo.temperature;
}
}
onEditingFinished: {
var val = parseFloat(text)
if (!isNaN(val)) {
MySettings.setModelTemperature(root.currentModelInfo, val)
focus = false
} else {
text = root.currentModelInfo.temperature
}
}
Accessible.role: Accessible.EditableText
Accessible.name: tempLabel.text
Accessible.description: ToolTip.text
}
MySettingsLabel {
id: topPLabel
text: qsTr("Top-P")
helpText: qsTr("Nucleus Sampling factor. Lower -> more predictable.")
Layout.row: 2
Layout.column: 0
Layout.maximumWidth: 300 * theme.fontScale
}
MyTextField {
id: topPField
text: root.currentModelInfo.topP
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
ToolTip.text: qsTr("Only the most likely tokens up to a total probability of top_p can be chosen.\nNOTE: Prevents choosing highly unlikely tokens.")
ToolTip.visible: hovered
Layout.row: 2
Layout.column: 1
validator: DoubleValidator {
locale: "C"
}
Connections {
target: MySettings
function onTopPChanged() {
topPField.text = root.currentModelInfo.topP;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
topPField.text = root.currentModelInfo.topP;
}
}
onEditingFinished: {
var val = parseFloat(text)
if (!isNaN(val)) {
MySettings.setModelTopP(root.currentModelInfo, val)
focus = false
} else {
text = root.currentModelInfo.topP
}
}
Accessible.role: Accessible.EditableText
Accessible.name: topPLabel.text
Accessible.description: ToolTip.text
}
MySettingsLabel {
id: minPLabel
text: qsTr("Min-P")
helpText: qsTr("Minimum token probability. Higher -> more predictable.")
Layout.row: 3
Layout.column: 0
Layout.maximumWidth: 300 * theme.fontScale
}
MyTextField {
id: minPField
text: root.currentModelInfo.minP
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
ToolTip.text: qsTr("Sets the minimum relative probability for a token to be considered.")
ToolTip.visible: hovered
Layout.row: 3
Layout.column: 1
validator: DoubleValidator {
locale: "C"
}
Connections {
target: MySettings
function onMinPChanged() {
minPField.text = root.currentModelInfo.minP;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
minPField.text = root.currentModelInfo.minP;
}
}
onEditingFinished: {
var val = parseFloat(text)
if (!isNaN(val)) {
MySettings.setModelMinP(root.currentModelInfo, val)
focus = false
} else {
text = root.currentModelInfo.minP
}
}
Accessible.role: Accessible.EditableText
Accessible.name: minPLabel.text
Accessible.description: ToolTip.text
}
MySettingsLabel {
id: topKLabel
visible: !root.currentModelInfo.isOnline
text: qsTr("Top-K")
helpText: qsTr("Size of selection pool for tokens.")
Layout.row: 2
Layout.column: 2
Layout.maximumWidth: 300 * theme.fontScale
}
MyTextField {
id: topKField
visible: !root.currentModelInfo.isOnline
text: root.currentModelInfo.topK
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
ToolTip.text: qsTr("Only the top K most likely tokens will be chosen from.")
ToolTip.visible: hovered
Layout.row: 2
Layout.column: 3
validator: IntValidator {
bottom: 1
}
Connections {
target: MySettings
function onTopKChanged() {
topKField.text = root.currentModelInfo.topK;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
topKField.text = root.currentModelInfo.topK;
}
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.setModelTopK(root.currentModelInfo, val)
focus = false
} else {
text = root.currentModelInfo.topK
}
}
Accessible.role: Accessible.EditableText
Accessible.name: topKLabel.text
Accessible.description: ToolTip.text
}
MySettingsLabel {
id: maxLengthLabel
visible: !root.currentModelInfo.isOnline
text: qsTr("Max Length")
helpText: qsTr("Maximum response length, in tokens.")
Layout.row: 0
Layout.column: 2
Layout.maximumWidth: 300 * theme.fontScale
}
MyTextField {
id: maxLengthField
visible: !root.currentModelInfo.isOnline
text: root.currentModelInfo.maxLength
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
Layout.row: 0
Layout.column: 3
validator: IntValidator {
bottom: 1
}
Connections {
target: MySettings
function onMaxLengthChanged() {
maxLengthField.text = root.currentModelInfo.maxLength;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
maxLengthField.text = root.currentModelInfo.maxLength;
}
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.setModelMaxLength(root.currentModelInfo, val)
focus = false
} else {
text = root.currentModelInfo.maxLength
}
}
Accessible.role: Accessible.EditableText
Accessible.name: maxLengthLabel.text
Accessible.description: ToolTip.text
}
MySettingsLabel {
id: batchSizeLabel
visible: !root.currentModelInfo.isOnline
text: qsTr("Prompt Batch Size")
helpText: qsTr("The batch size used for prompt processing.")
Layout.row: 1
Layout.column: 0
Layout.maximumWidth: 300 * theme.fontScale
}
MyTextField {
id: batchSizeField
visible: !root.currentModelInfo.isOnline
text: root.currentModelInfo.promptBatchSize
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
ToolTip.text: qsTr("Amount of prompt tokens to process at once.\nNOTE: Higher values can speed up reading prompts but will use more RAM.")
ToolTip.visible: hovered
Layout.row: 1
Layout.column: 1
validator: IntValidator {
bottom: 1
}
Connections {
target: MySettings
function onPromptBatchSizeChanged() {
batchSizeField.text = root.currentModelInfo.promptBatchSize;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
batchSizeField.text = root.currentModelInfo.promptBatchSize;
}
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.setModelPromptBatchSize(root.currentModelInfo, val)
focus = false
} else {
text = root.currentModelInfo.promptBatchSize
}
}
Accessible.role: Accessible.EditableText
Accessible.name: batchSizeLabel.text
Accessible.description: ToolTip.text
}
MySettingsLabel {
id: repeatPenaltyLabel
visible: !root.currentModelInfo.isOnline
text: qsTr("Repeat Penalty")
helpText: qsTr("Repetition penalty factor. Set to 1 to disable.")
Layout.row: 4
Layout.column: 2
Layout.maximumWidth: 300 * theme.fontScale
}
MyTextField {
id: repeatPenaltyField
visible: !root.currentModelInfo.isOnline
text: root.currentModelInfo.repeatPenalty
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
Layout.row: 4
Layout.column: 3
validator: DoubleValidator {
locale: "C"
}
Connections {
target: MySettings
function onRepeatPenaltyChanged() {
repeatPenaltyField.text = root.currentModelInfo.repeatPenalty;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
repeatPenaltyField.text = root.currentModelInfo.repeatPenalty;
}
}
onEditingFinished: {
var val = parseFloat(text)
if (!isNaN(val)) {
MySettings.setModelRepeatPenalty(root.currentModelInfo, val)
focus = false
} else {
text = root.currentModelInfo.repeatPenalty
}
}
Accessible.role: Accessible.EditableText
Accessible.name: repeatPenaltyLabel.text
Accessible.description: ToolTip.text
}
MySettingsLabel {
id: repeatPenaltyTokensLabel
visible: !root.currentModelInfo.isOnline
text: qsTr("Repeat Penalty Tokens")
helpText: qsTr("Number of previous tokens used for penalty.")
Layout.row: 3
Layout.column: 2
Layout.maximumWidth: 300 * theme.fontScale
}
MyTextField {
id: repeatPenaltyTokenField
visible: !root.currentModelInfo.isOnline
text: root.currentModelInfo.repeatPenaltyTokens
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
Layout.row: 3
Layout.column: 3
validator: IntValidator {
bottom: 1
}
Connections {
target: MySettings
function onRepeatPenaltyTokensChanged() {
repeatPenaltyTokenField.text = root.currentModelInfo.repeatPenaltyTokens;
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
repeatPenaltyTokenField.text = root.currentModelInfo.repeatPenaltyTokens;
}
}
onEditingFinished: {
var val = parseInt(text)
if (!isNaN(val)) {
MySettings.setModelRepeatPenaltyTokens(root.currentModelInfo, val)
focus = false
} else {
text = root.currentModelInfo.repeatPenaltyTokens
}
}
Accessible.role: Accessible.EditableText
Accessible.name: repeatPenaltyTokensLabel.text
Accessible.description: ToolTip.text
}
MySettingsLabel {
id: gpuLayersLabel
visible: !root.currentModelInfo.isOnline
text: qsTr("GPU Layers")
helpText: qsTr("Number of model layers to load into VRAM.")
Layout.row: 4
Layout.column: 0
Layout.maximumWidth: 300 * theme.fontScale
}
MyTextField {
id: gpuLayersField
visible: !root.currentModelInfo.isOnline
text: root.currentModelInfo.gpuLayers
font.pixelSize: theme.fontSizeLarge
color: theme.textColor
ToolTip.text: qsTr("How many model layers to load into VRAM. Decrease this if GPT4All runs out of VRAM while loading this model.\nLower values increase CPU load and RAM usage, and make inference slower.\nNOTE: Does not take effect until you reload the model.")
ToolTip.visible: hovered
Layout.row: 4
Layout.column: 1
Connections {
target: MySettings
function onGpuLayersChanged() {
gpuLayersField.text = root.currentModelInfo.gpuLayers
}
}
Connections {
target: root
function onCurrentModelInfoChanged() {
if (root.currentModelInfo.gpuLayers === 100) {
gpuLayersField.text = root.currentModelInfo.maxGpuLayers
} else {
gpuLayersField.text = root.currentModelInfo.gpuLayers
}
}
}
onEditingFinished: {
var val = parseInt(text)
if (isNaN(val)) {
gpuLayersField.text = root.currentModelInfo.gpuLayers
} else {
if (val < 1) {
val = 1
gpuLayersField.text = val
} else if (val > root.currentModelInfo.maxGpuLayers) {
val = root.currentModelInfo.maxGpuLayers
gpuLayersField.text = val
}
MySettings.setModelGpuLayers(root.currentModelInfo, val)
focus = false
}
}
Accessible.role: Accessible.EditableText
Accessible.name: gpuLayersLabel.text
Accessible.description: ToolTip.text
}
}
Rectangle {
Layout.row: 16
Layout.column: 0
Layout.columnSpan: 2
Layout.topMargin: 15
Layout.fillWidth: true
height: 1
color: theme.settingsDivider
}
}
}

View File

@@ -1,596 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Dialogs
import QtQuick.Layouts
import chatlistmodel
import download
import llm
import modellist
import network
import mysettings
Rectangle {
id: modelsView
color: theme.viewBackground
signal addModelViewRequested()
ToastManager {
id: messageToast
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 30
Item {
Layout.fillWidth: true
Layout.fillHeight: true
visible: ModelList.installedModels.count === 0
ColumnLayout {
id: noInstalledLabel
anchors.centerIn: parent
spacing: 0
Text {
Layout.alignment: Qt.AlignCenter
text: qsTr("No Models Installed")
color: theme.mutedLightTextColor
font.pixelSize: theme.fontSizeBannerSmall
}
Text {
Layout.topMargin: 15
horizontalAlignment: Qt.AlignHCenter
color: theme.mutedLighterTextColor
text: qsTr("Install a model to get started using GPT4All")
font.pixelSize: theme.fontSizeLarge
}
}
MyButton {
anchors.top: noInstalledLabel.bottom
anchors.topMargin: 50
anchors.horizontalCenter: noInstalledLabel.horizontalCenter
rightPadding: 60
leftPadding: 60
text: qsTr("\uFF0B Add Model")
onClicked: {
addModelViewRequested()
}
Accessible.role: Accessible.Button
Accessible.name: qsTr("Shows the add model view")
}
}
RowLayout {
visible: ModelList.installedModels.count !== 0
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
spacing: 50
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
Layout.minimumWidth: 200
spacing: 5
Text {
id: welcome
text: qsTr("Installed Models")
font.pixelSize: theme.fontSizeBanner
color: theme.titleTextColor
}
Text {
text: qsTr("Locally installed chat models")
font.pixelSize: theme.fontSizeLarge
color: theme.titleInfoTextColor
}
}
Rectangle {
Layout.fillWidth: true
height: 0
}
MyButton {
Layout.alignment: Qt.AlignTop | Qt.AlignRight
text: qsTr("\uFF0B Add Model")
onClicked: {
addModelViewRequested()
}
}
}
ScrollView {
id: scrollView
visible: ModelList.installedModels.count !== 0
ScrollBar.vertical.policy: ScrollBar.AsNeeded
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
ListView {
id: modelListView
model: ModelList.installedModels
boundsBehavior: Flickable.StopAtBounds
spacing: 30
delegate: Rectangle {
id: delegateItem
width: modelListView.width
height: childrenRect.height + 60
color: theme.conversationBackground
radius: 10
border.width: 1
border.color: theme.controlBorder
ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 30
Text {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
text: name
elide: Text.ElideRight
color: theme.titleTextColor
font.pixelSize: theme.fontSizeLargest
font.bold: true
Accessible.role: Accessible.Paragraph
Accessible.name: qsTr("Model file")
Accessible.description: qsTr("Model file to be downloaded")
}
Rectangle {
Layout.fillWidth: true
height: 1
color: theme.dividerColor
}
RowLayout {
Layout.topMargin: 10
Layout.fillWidth: true
Text {
id: descriptionText
text: description
font.pixelSize: theme.fontSizeLarge
Layout.fillWidth: true
wrapMode: Text.WordWrap
textFormat: Text.StyledText
color: theme.textColor
linkColor: theme.textColor
Accessible.role: Accessible.Paragraph
Accessible.name: qsTr("Description")
Accessible.description: qsTr("File description")
onLinkActivated: function(link) { Qt.openUrlExternally(link); }
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton // pass clicks to parent
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
Rectangle {
id: actionBox
width: childrenRect.width + 20
color: "transparent"
border.width: 1
border.color: theme.dividerColor
radius: 10
Layout.rightMargin: 20
Layout.bottomMargin: 20
Layout.minimumHeight: childrenRect.height + 20
Layout.alignment: Qt.AlignRight | Qt.AlignTop
ColumnLayout {
spacing: 0
MySettingsButton {
id: downloadButton
text: isDownloading ? qsTr("Cancel") : qsTr("Resume")
font.pixelSize: theme.fontSizeLarge
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.minimumWidth: 200
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
visible: (isDownloading || isIncomplete) && downloadError === "" && !isOnline && !calcHash
Accessible.description: qsTr("Stop/restart/start the download")
onClicked: {
if (!isDownloading) {
Download.downloadModel(filename);
} else {
Download.cancelDownload(filename);
}
}
}
MySettingsDestructiveButton {
id: removeButton
text: qsTr("Remove")
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.minimumWidth: 200
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
visible: !isDownloading && (installed || isIncomplete)
Accessible.description: qsTr("Remove model from filesystem")
onClicked: {
Download.removeModel(filename);
}
}
MySettingsButton {
id: installButton
visible: !installed && isOnline
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.minimumWidth: 200
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
text: qsTr("Install")
font.pixelSize: theme.fontSizeLarge
onClicked: {
var apiKeyText = apiKey.text.trim(),
baseUrlText = baseUrl.text.trim(),
modelNameText = modelName.text.trim();
var apiKeyOk = apiKeyText !== "",
baseUrlOk = !isCompatibleApi || baseUrlText !== "",
modelNameOk = !isCompatibleApi || modelNameText !== "";
if (!apiKeyOk)
apiKey.showError();
if (!baseUrlOk)
baseUrl.showError();
if (!modelNameOk)
modelName.showError();
if (!apiKeyOk || !baseUrlOk || !modelNameOk)
return;
if (!isCompatibleApi)
Download.installModel(
filename,
apiKeyText,
);
else
Download.installCompatibleModel(
modelNameText,
apiKeyText,
baseUrlText,
);
}
Accessible.role: Accessible.Button
Accessible.name: qsTr("Install")
Accessible.description: qsTr("Install online model")
}
ColumnLayout {
spacing: 0
Label {
Layout.topMargin: 20
Layout.leftMargin: 20
visible: downloadError !== ""
textFormat: Text.StyledText
text: qsTr("<strong><font size=\"1\"><a href=\"#error\">Error</a></strong></font>")
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
linkColor: theme.textErrorColor
Accessible.role: Accessible.Paragraph
Accessible.name: text
Accessible.description: qsTr("Describes an error that occurred when downloading")
onLinkActivated: {
downloadingErrorPopup.text = downloadError;
downloadingErrorPopup.open();
}
}
Label {
visible: LLM.systemTotalRAMInGB() < ramrequired
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.maximumWidth: 300
textFormat: Text.StyledText
text: qsTr("<strong><font size=\"2\">WARNING: Not recommended for your hardware. Model requires more memory (%1 GB) than your system has available (%2).</strong></font>").arg(ramrequired).arg(LLM.systemTotalRAMInGBString())
color: theme.textErrorColor
font.pixelSize: theme.fontSizeLarge
wrapMode: Text.WordWrap
Accessible.role: Accessible.Paragraph
Accessible.name: text
Accessible.description: qsTr("Error for incompatible hardware")
onLinkActivated: {
downloadingErrorPopup.text = downloadError;
downloadingErrorPopup.open();
}
}
}
ColumnLayout {
visible: isDownloading && !calcHash
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.minimumWidth: 200
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
spacing: 20
ProgressBar {
id: itemProgressBar
Layout.fillWidth: true
width: 200
value: bytesReceived / bytesTotal
background: Rectangle {
implicitHeight: 45
color: theme.progressBackground
radius: 3
}
contentItem: Item {
implicitHeight: 40
Rectangle {
width: itemProgressBar.visualPosition * parent.width
height: parent.height
radius: 2
color: theme.progressForeground
}
}
Accessible.role: Accessible.ProgressBar
Accessible.name: qsTr("Download progressBar")
Accessible.description: qsTr("Shows the progress made in the download")
}
Label {
id: speedLabel
color: theme.textColor
Layout.alignment: Qt.AlignRight
text: speed
font.pixelSize: theme.fontSizeLarge
Accessible.role: Accessible.Paragraph
Accessible.name: qsTr("Download speed")
Accessible.description: qsTr("Download speed in bytes/kilobytes/megabytes per second")
}
}
RowLayout {
visible: calcHash
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.minimumWidth: 200
Layout.maximumWidth: 200
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
clip: true
Label {
id: calcHashLabel
color: theme.textColor
text: qsTr("Calculating...")
font.pixelSize: theme.fontSizeLarge
Accessible.role: Accessible.Paragraph
Accessible.name: text
Accessible.description: qsTr("Whether the file hash is being calculated")
}
MyBusyIndicator {
id: busyCalcHash
running: calcHash
Accessible.role: Accessible.Animation
Accessible.name: qsTr("Busy indicator")
Accessible.description: qsTr("Displayed when the file hash is being calculated")
}
}
MyTextField {
id: apiKey
visible: !installed && isOnline
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.minimumWidth: 200
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
wrapMode: Text.WrapAnywhere
function showError() {
messageToast.show(qsTr("ERROR: $API_KEY is empty."));
apiKey.placeholderTextColor = theme.textErrorColor;
}
onTextChanged: {
apiKey.placeholderTextColor = theme.mutedTextColor;
}
placeholderText: qsTr("enter $API_KEY")
Accessible.role: Accessible.EditableText
Accessible.name: placeholderText
Accessible.description: qsTr("Whether the file hash is being calculated")
}
MyTextField {
id: baseUrl
visible: !installed && isOnline && isCompatibleApi
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.minimumWidth: 200
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
wrapMode: Text.WrapAnywhere
function showError() {
messageToast.show(qsTr("ERROR: $BASE_URL is empty."));
baseUrl.placeholderTextColor = theme.textErrorColor;
}
onTextChanged: {
baseUrl.placeholderTextColor = theme.mutedTextColor;
}
placeholderText: qsTr("enter $BASE_URL")
Accessible.role: Accessible.EditableText
Accessible.name: placeholderText
Accessible.description: qsTr("Whether the file hash is being calculated")
}
MyTextField {
id: modelName
visible: !installed && isOnline && isCompatibleApi
Layout.topMargin: 20
Layout.leftMargin: 20
Layout.minimumWidth: 200
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
wrapMode: Text.WrapAnywhere
function showError() {
messageToast.show(qsTr("ERROR: $MODEL_NAME is empty."))
modelName.placeholderTextColor = theme.textErrorColor;
}
onTextChanged: {
modelName.placeholderTextColor = theme.mutedTextColor;
}
placeholderText: qsTr("enter $MODEL_NAME")
Accessible.role: Accessible.EditableText
Accessible.name: placeholderText
Accessible.description: qsTr("Whether the file hash is being calculated")
}
}
}
}
Item {
Layout.minimumWidth: childrenRect.width
Layout.minimumHeight: childrenRect.height
Layout.bottomMargin: 10
RowLayout {
id: paramRow
anchors.centerIn: parent
ColumnLayout {
Layout.topMargin: 10
Layout.bottomMargin: 10
Layout.leftMargin: 20
Layout.rightMargin: 20
Text {
text: qsTr("File size")
font.pixelSize: theme.fontSizeSmall
color: theme.mutedDarkTextColor
}
Text {
text: filesize
color: theme.textColor
font.pixelSize: theme.fontSizeSmall
font.bold: true
}
}
Rectangle {
width: 1
Layout.fillHeight: true
color: theme.dividerColor
}
ColumnLayout {
Layout.topMargin: 10
Layout.bottomMargin: 10
Layout.leftMargin: 20
Layout.rightMargin: 20
Text {
text: qsTr("RAM required")
font.pixelSize: theme.fontSizeSmall
color: theme.mutedDarkTextColor
}
Text {
text: ramrequired >= 0 ? qsTr("%1 GB").arg(ramrequired) : qsTr("?")
color: theme.textColor
font.pixelSize: theme.fontSizeSmall
font.bold: true
}
}
Rectangle {
width: 1
Layout.fillHeight: true
color: theme.dividerColor
}
ColumnLayout {
Layout.topMargin: 10
Layout.bottomMargin: 10
Layout.leftMargin: 20
Layout.rightMargin: 20
Text {
text: qsTr("Parameters")
font.pixelSize: theme.fontSizeSmall
color: theme.mutedDarkTextColor
}
Text {
text: parameters !== "" ? parameters : "?"
color: theme.textColor
font.pixelSize: theme.fontSizeSmall
font.bold: true
}
}
Rectangle {
width: 1
Layout.fillHeight: true
color: theme.dividerColor
}
ColumnLayout {
Layout.topMargin: 10
Layout.bottomMargin: 10
Layout.leftMargin: 20
Layout.rightMargin: 20
Text {
text: qsTr("Quant")
font.pixelSize: theme.fontSizeSmall
color: theme.mutedDarkTextColor
}
Text {
text: quant
color: theme.textColor
font.pixelSize: theme.fontSizeSmall
font.bold: true
}
}
Rectangle {
width: 1
Layout.fillHeight: true
color: theme.dividerColor
}
ColumnLayout {
Layout.topMargin: 10
Layout.bottomMargin: 10
Layout.leftMargin: 20
Layout.rightMargin: 20
Text {
text: qsTr("Type")
font.pixelSize: theme.fontSizeSmall
color: theme.mutedDarkTextColor
}
Text {
text: type
color: theme.textColor
font.pixelSize: theme.fontSizeSmall
font.bold: true
}
}
}
Rectangle {
color: "transparent"
anchors.fill: paramRow
border.color: theme.dividerColor
border.width: 1
radius: 10
}
}
Rectangle {
Layout.fillWidth: true
height: 1
color: theme.dividerColor
}
}
}
}
}
}
Connections {
target: Download
function onToastMessage(message) {
messageToast.show(message);
}
}
}

View File

@@ -1,67 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
BusyIndicator {
id: control
property real size: 48
property color color: theme.accentColor
contentItem: Item {
implicitWidth: control.size
implicitHeight: control.size
Item {
id: item
x: parent.width / 2 - width / 2
y: parent.height / 2 - height / 2
width: control.size
height: control.size
opacity: control.running ? 1 : 0
Behavior on opacity {
OpacityAnimator {
duration: 250
}
}
RotationAnimator {
target: item
running: control.visible && control.running
from: 0
to: 360
loops: Animation.Infinite
duration: 1750
}
Repeater {
id: repeater
model: 6
Rectangle {
id: delegate
x: item.width / 2 - width / 2
y: item.height / 2 - height / 2
implicitWidth: control.size * .2
implicitHeight: control.size * .2
radius: control.size * .1
color: control.color
required property int index
transform: [
Translate {
y: -Math.min(item.width, item.height) * 0.5 + delegate.radius
},
Rotation {
angle: delegate.index / repeater.count * 360
origin.x: delegate.radius
origin.y: delegate.radius
}
]
}
}
}
}
}

View File

@@ -1,43 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import mysettings
import mysettingsenums
Button {
id: myButton
padding: 10
rightPadding: 18
leftPadding: 18
property color textColor: theme.oppositeTextColor
property color mutedTextColor: theme.oppositeMutedTextColor
property color backgroundColor: theme.buttonBackground
property color backgroundColorHovered: theme.buttonBackgroundHovered
property real backgroundRadius: 10
property real borderWidth: MySettings.chatTheme === MySettingsEnums.ChatTheme.LegacyDark ? 1 : 0
property color borderColor: theme.buttonBorder
property real fontPixelSize: theme.fontSizeLarge
property bool fontPixelBold: false
property alias textAlignment: textContent.horizontalAlignment
contentItem: Text {
id: textContent
text: myButton.text
horizontalAlignment: myButton.textAlignment
color: myButton.enabled ? textColor : mutedTextColor
font.pixelSize: fontPixelSize
font.bold: fontPixelBold
Accessible.role: Accessible.Button
Accessible.name: text
}
background: Rectangle {
radius: myButton.backgroundRadius
border.width: myButton.borderWidth
border.color: myButton.borderColor
color: !myButton.enabled ? theme.mutedTextColor : myButton.hovered ? backgroundColorHovered : backgroundColor
}
Accessible.role: Accessible.Button
Accessible.name: text
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
}

View File

@@ -1,42 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
CheckBox {
id: myCheckBox
background: Rectangle {
color: "transparent"
}
indicator: Rectangle {
implicitWidth: 26
implicitHeight: 26
x: myCheckBox.leftPadding
y: parent.height / 2 - height / 2
border.color: theme.checkboxBorder
color: "transparent"
radius: 3
Rectangle {
width: 14
height: 14
x: 6
y: 6
radius: 2
color: theme.checkboxForeground
visible: myCheckBox.checked
}
}
contentItem: Text {
text: myCheckBox.text
font: myCheckBox.font
opacity: enabled ? 1.0 : 0.3
color: theme.textColor
verticalAlignment: Text.AlignVCenter
leftPadding: myCheckBox.indicator.width + myCheckBox.spacing
}
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
}

View File

@@ -1,103 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
ComboBox {
id: comboBox
font.pixelSize: theme.fontSizeLarge
spacing: 0
padding: 10
Accessible.role: Accessible.ComboBox
contentItem: RowLayout {
id: contentRow
spacing: 0
Text {
id: text
Layout.fillWidth: true
leftPadding: 10
rightPadding: 20
text: comboBox.displayText
font: comboBox.font
color: theme.textColor
verticalAlignment: Text.AlignLeft
elide: Text.ElideRight
}
Item {
Layout.preferredWidth: updown.width
Layout.preferredHeight: updown.height
Image {
id: updown
anchors.verticalCenter: parent.verticalCenter
sourceSize.width: comboBox.font.pixelSize
sourceSize.height: comboBox.font.pixelSize
mipmap: true
visible: false
source: "qrc:/gpt4all/icons/up_down.svg"
}
ColorOverlay {
anchors.fill: updown
source: updown
color: theme.textColor
}
}
}
delegate: ItemDelegate {
width: comboBox.width -20
contentItem: Text {
text: modelData
color: theme.textColor
font: comboBox.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
radius: 10
color: highlighted ? theme.menuHighlightColor : theme.menuBackgroundColor
}
highlighted: comboBox.highlightedIndex === index
}
popup: Popup {
// FIXME This should be made much nicer to take into account lists that are very long so
// that it is scrollable and also sized optimally taking into account the x,y and the content
// width and height as well as the window width and height
y: comboBox.height - 1
width: comboBox.width
implicitHeight: contentItem.implicitHeight + 20
padding: 0
contentItem: Rectangle {
implicitWidth: myListView.contentWidth
implicitHeight: myListView.contentHeight
color: "transparent"
ListView {
id: myListView
anchors.fill: parent
anchors.margins: 10
clip: true
implicitHeight: contentHeight
model: comboBox.popup.visible ? comboBox.delegateModel : null
currentIndex: comboBox.highlightedIndex
ScrollIndicator.vertical: ScrollIndicator { }
}
}
background: Rectangle {
color: theme.menuBackgroundColor//theme.controlBorder
border.color: theme.menuBorderColor //theme.controlBorder
border.width: 1
radius: 10
}
}
indicator: Item {
}
background: Rectangle {
color: theme.controlBackground
border.width: 1
border.color: theme.controlBorder
radius: 10
}
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
}

View File

@@ -1,48 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Dialogs
import QtQuick.Layouts
Dialog {
id: myDialog
parent: Overlay.overlay
property alias closeButtonVisible: myCloseButton.visible
background: Rectangle {
width: parent.width
height: parent.height
color: theme.containerBackground
border.width: 1
border.color: theme.dialogBorder
radius: 10
}
Rectangle {
id: closeBackground
visible: myCloseButton.visible
z: 299
anchors.centerIn: myCloseButton
width: myCloseButton.width + 10
height: myCloseButton.height + 10
color: theme.containerBackground
}
MyToolButton {
id: myCloseButton
x: 0 + myDialog.width - myDialog.padding - width - 15
y: 0 - myDialog.padding + 15
z: 300
visible: myDialog.closePolicy != Popup.NoAutoClose
width: 24
height: 24
imageWidth: 24
imageHeight: 24
padding: 0
source: "qrc:/gpt4all/icons/close.svg"
fillMode: Image.PreserveAspectFit
onClicked: {
myDialog.close();
}
}
}

View File

@@ -1,20 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import llm
TextField {
id: myDirectoryField
padding: 10
property bool isValid: LLM.directoryExists(text)
color: text === "" || isValid ? theme.textColor : theme.textErrorColor
background: Rectangle {
implicitWidth: 150
color: theme.controlBackground
border.width: 1
border.color: theme.controlBorder
radius: 10
}
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
}

View File

@@ -1,44 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import Qt5Compat.GraphicalEffects
import mysettings
MyButton {
id: fancyLink
property alias imageSource: myimage.source
Image {
id: myimage
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 12
sourceSize: Qt.size(15, 15)
mipmap: true
visible: false
}
ColorOverlay {
anchors.fill: myimage
source: myimage
color: fancyLink.hovered ? theme.fancyLinkTextHovered : theme.fancyLinkText
}
borderWidth: 0
backgroundColor: "transparent"
backgroundColorHovered: "transparent"
fontPixelBold: true
leftPadding: 35
rightPadding: 8
topPadding: 1
bottomPadding: 1
textColor: fancyLink.hovered ? theme.fancyLinkTextHovered : theme.fancyLinkText
fontPixelSize: theme.fontSizeSmall
background: Rectangle {
color: "transparent"
}
Accessible.name: qsTr("Fancy link")
Accessible.description: qsTr("A stylized link")
}

View File

@@ -1,62 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
Menu {
id: menu
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
contentWidth + leftPadding + rightPadding + 20)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding + 20)
background: Rectangle {
implicitWidth: 220
implicitHeight: 40
color: theme.menuBackgroundColor
border.color: theme.menuBorderColor
border.width: 1
radius: 10
}
contentItem: Rectangle {
implicitWidth: myListView.contentWidth
implicitHeight: myListView.contentHeight
color: "transparent"
ListView {
id: myListView
anchors.margins: 10
anchors.fill: parent
implicitHeight: contentHeight
model: menu.contentModel
interactive: Window.window
? contentHeight + menu.topPadding + menu.bottomPadding > menu.height
: false
clip: true
currentIndex: menu.currentIndex
ScrollIndicator.vertical: ScrollIndicator {}
}
}
enter: Transition {
NumberAnimation {
property: "opacity"
from: 0
to: 1
easing.type: Easing.InOutQuad
duration: 100
}
}
exit: Transition {
NumberAnimation {
property: "opacity"
from: 1
to: 0
easing.type: Easing.InOutQuad
duration: 100
}
}
}

View File

@@ -1,22 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
MenuItem {
id: item
background: Rectangle {
radius: 10
width: parent.width -20
color: item.highlighted ? theme.menuHighlightColor : theme.menuBackgroundColor
}
contentItem: Text {
leftPadding: 10
rightPadding: 10
padding: 5
text: item.text
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
}
}

View File

@@ -1,48 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import Qt5Compat.GraphicalEffects
Button {
id: myButton
padding: 0
property color backgroundColor: theme.iconBackgroundDark
property color backgroundColorHovered: theme.iconBackgroundHovered
property alias source: image.source
property alias fillMode: image.fillMode
implicitWidth: 30
implicitHeight: 30
contentItem: Text {
text: myButton.text
horizontalAlignment: Text.AlignHCenter
color: myButton.enabled ? theme.textColor : theme.mutedTextColor
font.pixelSize: theme.fontSizeLarge
Accessible.role: Accessible.Button
Accessible.name: text
}
background: Item {
anchors.fill: parent
Rectangle {
anchors.fill: parent
color: "transparent"
}
Image {
id: image
anchors.centerIn: parent
visible: false
mipmap: true
sourceSize.width: 16
sourceSize.height: 16
}
ColorOverlay {
anchors.fill: image
source: image
color: myButton.hovered ? backgroundColorHovered : backgroundColor
}
}
Accessible.role: Accessible.Button
Accessible.name: text
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
}

View File

@@ -1,41 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import mysettings
Button {
id: myButton
padding: 10
rightPadding: 18
leftPadding: 18
property color textColor: theme.lightButtonText
property color mutedTextColor: theme.lightButtonMutedText
property color backgroundColor: theme.lightButtonBackground
property color backgroundColorHovered: enabled ? theme.lightButtonBackgroundHovered : backgroundColor
property real borderWidth: 0
property color borderColor: "transparent"
property real fontPixelSize: theme.fontSizeLarge
property string toolTip
contentItem: Text {
text: myButton.text
horizontalAlignment: Text.AlignHCenter
color: myButton.enabled ? textColor : mutedTextColor
font.pixelSize: fontPixelSize
font.bold: true
Accessible.role: Accessible.Button
Accessible.name: text
}
background: Rectangle {
radius: 10
border.width: borderWidth
border.color: borderColor
color: myButton.hovered ? backgroundColorHovered : backgroundColor
}
Accessible.role: Accessible.Button
Accessible.name: text
ToolTip.text: toolTip
ToolTip.visible: toolTip !== "" && myButton.hovered
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
}

View File

@@ -1,38 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import mysettings
Button {
id: myButton
padding: 10
rightPadding: 18
leftPadding: 18
font.pixelSize: theme.fontSizeLarge
property color textColor: theme.darkButtonText
property color mutedTextColor: theme.darkButtonMutedText
property color backgroundColor: theme.darkButtonBackground
property color backgroundColorHovered: enabled ? theme.darkButtonBackgroundHovered : backgroundColor
property real borderWidth: 0
property color borderColor: "transparent"
contentItem: Text {
text: myButton.text
horizontalAlignment: Text.AlignHCenter
color: myButton.enabled ? textColor : mutedTextColor
font.pixelSize: theme.fontSizeLarge
font.bold: true
Accessible.role: Accessible.Button
Accessible.name: text
}
background: Rectangle {
radius: 10
border.width: borderWidth
border.color: borderColor
color: myButton.hovered ? backgroundColorHovered : backgroundColor
}
Accessible.role: Accessible.Button
Accessible.name: text
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
}

View File

@@ -1,48 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
ColumnLayout {
id: root
property alias text: mainTextLabel.text
property alias helpText: helpTextLabel.text
property alias textFormat: mainTextLabel.textFormat
property alias wrapMode: mainTextLabel.wrapMode
property alias font: mainTextLabel.font
property alias horizontalAlignment: mainTextLabel.horizontalAlignment
signal linkActivated(link : url);
property alias color: mainTextLabel.color
property alias linkColor: mainTextLabel.linkColor
Label {
id: mainTextLabel
color: theme.settingsTitleTextColor
font.pixelSize: theme.fontSizeLarger
font.bold: true
onLinkActivated: function(link) {
root.linkActivated(link);
}
}
Label {
id: helpTextLabel
visible: text !== ""
Layout.fillWidth: true
wrapMode: Text.Wrap
color: theme.settingsTitleTextColor
font.pixelSize: theme.fontSizeLarge
font.bold: false
onLinkActivated: function(link) {
root.linkActivated(link);
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton // pass clicks to parent
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
}

View File

@@ -1,96 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import QtQuick.Dialogs
import Qt.labs.folderlistmodel
import mysettings
Item {
id: settingsStack
Theme {
id: theme
}
property ListModel tabTitlesModel: ListModel { }
property list<Component> tabs: [ ]
TabBar {
id: settingsTabBar
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width / 1.75
z: 200
visible: tabTitlesModel.count > 1
background: Rectangle {
color: "transparent"
}
Repeater {
model: settingsStack.tabTitlesModel
TabButton {
id: tabButton
padding: 10
contentItem: IconLabel {
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
font.bold: tabButton.checked
text: model.title
}
background: Rectangle {
color: "transparent"
}
Accessible.role: Accessible.Button
Accessible.name: model.title
}
}
}
Rectangle {
id: dividerTabBar
visible: tabTitlesModel.count > 1
anchors.top: settingsTabBar.bottom
anchors.topMargin: 15
anchors.bottomMargin: 15
anchors.leftMargin: 15
anchors.rightMargin: 15
anchors.left: parent.left
anchors.right: parent.right
height: 1
color: theme.settingsDivider
}
FolderDialog {
id: folderDialog
title: qsTr("Please choose a directory")
}
function openFolderDialog(currentFolder, onAccepted) {
folderDialog.currentFolder = currentFolder;
folderDialog.accepted.connect(function() { onAccepted(folderDialog.selectedFolder); });
folderDialog.open();
}
StackLayout {
id: stackLayout
anchors.top: tabTitlesModel.count > 1 ? dividerTabBar.bottom : parent.top
anchors.topMargin: 5
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
currentIndex: settingsTabBar.currentIndex
Repeater {
model: settingsStack.tabs
delegate: Loader {
id: loader
sourceComponent: model.modelData
onLoaded: {
settingsStack.tabTitlesModel.append({ "title": loader.item.title });
item.openFolderDialog = settingsStack.openFolderDialog;
}
}
}
}
}

View File

@@ -1,74 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
Item {
id: root
property string title: ""
property Item contentItem: null
property bool showRestoreDefaultsButton: true
property var openFolderDialog
signal restoreDefaultsClicked
onContentItemChanged: function() {
if (contentItem) {
contentItem.parent = contentInner;
contentItem.anchors.left = contentInner.left;
contentItem.anchors.right = contentInner.right;
}
}
ScrollView {
id: scrollView
width: parent.width
height: parent.height
topPadding: 15
leftPadding: 5
contentWidth: availableWidth
contentHeight: innerColumn.height
ScrollBar.vertical: ScrollBar {
parent: scrollView.parent
anchors.top: scrollView.top
anchors.left: scrollView.right
anchors.bottom: scrollView.bottom
}
Theme {
id: theme
}
ColumnLayout {
id: innerColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 15
spacing: 10
Column {
id: contentInner
Layout.fillWidth: true
}
Item {
Layout.fillWidth: true
Layout.topMargin: 20
height: restoreDefaultsButton.height
MySettingsButton {
id: restoreDefaultsButton
anchors.left: parent.left
visible: showRestoreDefaultsButton
width: implicitWidth
text: qsTr("Restore Defaults")
font.pixelSize: theme.fontSizeLarge
Accessible.role: Accessible.Button
Accessible.name: text
Accessible.description: qsTr("Restores settings dialog to a default state")
onClicked: {
root.restoreDefaultsClicked();
}
}
}
}
}
}

View File

@@ -1,24 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
Label {
id: mySlug
padding: 3
rightPadding: 9
leftPadding: 9
font.pixelSize: theme.fontSizeSmall
background: Rectangle {
radius: 6
border.width: 1
border.color: mySlug.color
color: theme.slugBackground
}
ToolTip.visible: ma.containsMouse && ToolTip.text !== ""
MouseArea {
id: ma
anchors.fill: parent
hoverEnabled: true
}
}

View File

@@ -1,22 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
TextArea {
id: myTextArea
color: enabled ? theme.textColor : theme.mutedTextColor
placeholderTextColor: theme.mutedTextColor
font.pixelSize: theme.fontSizeLarge
background: Rectangle {
implicitWidth: 150
color: theme.controlBackground
border.width: 1
border.color: theme.controlBorder
radius: 10
}
padding: 10
wrapMode: TextArea.Wrap
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
}

View File

@@ -1,19 +0,0 @@
import QtQuick
import QtQuick.Controls
Text {
id: text
signal click()
property string tooltip
HoverHandler { id: hoverHandler }
TapHandler { onTapped: { click() } }
font.bold: true
font.underline: hoverHandler.hovered
font.pixelSize: theme.fontSizeSmall
ToolTip.text: tooltip
ToolTip.visible: tooltip !== "" && hoverHandler.hovered
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
}

View File

@@ -1,19 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
TextField {
id: myTextField
padding: 10
placeholderTextColor: theme.mutedTextColor
background: Rectangle {
implicitWidth: 150
color: myTextField.enabled ? theme.controlBackground : theme.disabledControlBackground
border.width: 1
border.color: theme.controlBorder
radius: 10
}
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
color: enabled ? theme.textColor : theme.mutedTextColor
}

View File

@@ -1,56 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import Qt5Compat.GraphicalEffects
Button {
id: myButton
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
property alias imageWidth: image.sourceSize.width
property alias imageHeight: image.sourceSize.height
contentItem: Text {
text: myButton.text
horizontalAlignment: Text.AlignHCenter
color: myButton.enabled ? theme.textColor : theme.mutedTextColor
font.pixelSize: theme.fontSizeLarge
Accessible.role: Accessible.Button
Accessible.name: text
}
background: Item {
anchors.fill: parent
Rectangle {
anchors.fill: parent
color: myButton.toggledColor
visible: myButton.toggled
border.color: myButton.toggledColor
border.width: myButton.toggledWidth
radius: 8
}
Image {
id: image
anchors.centerIn: parent
visible: false
fillMode: Image.PreserveAspectFit
mipmap: true
sourceSize.width: 32
sourceSize.height: 32
}
ColorOverlay {
anchors.fill: image
source: image
color: myButton.hovered ? backgroundColorHovered : backgroundColor
}
}
Accessible.role: Accessible.Button
Accessible.name: text
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
}

View File

@@ -1,77 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import Qt5Compat.GraphicalEffects
import QtQuick.Layouts
import mysettings
Button {
id: myButton
property alias imageSource: myimage.source
property alias description: description.text
contentItem: Item {
id: item
anchors.centerIn: parent
RowLayout {
anchors.fill: parent
Rectangle {
id: rec
color: "transparent"
Layout.preferredWidth: item.width * 1/5.5
Layout.preferredHeight: item.width * 1/5.5
Layout.alignment: Qt.AlignCenter
Image {
id: myimage
anchors.centerIn: parent
sourceSize.width: rec.width
sourceSize.height: rec.height
mipmap: true
visible: false
}
ColorOverlay {
anchors.fill: myimage
source: myimage
color: theme.welcomeButtonBorder
}
}
ColumnLayout {
Layout.preferredWidth: childrenRect.width
Text {
text: myButton.text
horizontalAlignment: Text.AlignHCenter
color: myButton.hovered ? theme.welcomeButtonTextHovered : theme.welcomeButtonText
font.pixelSize: theme.fontSizeBannerSmall
font.bold: true
Accessible.role: Accessible.Button
Accessible.name: text
}
Text {
id: description
horizontalAlignment: Text.AlignHCenter
color: myButton.hovered ? theme.welcomeButtonTextHovered : theme.welcomeButtonText
font.pixelSize: theme.fontSizeSmall
font.bold: false
Accessible.role: Accessible.Button
Accessible.name: text
}
}
}
}
background: Rectangle {
radius: 10
border.width: 1
border.color: myButton.hovered ? theme.welcomeButtonBorderHovered : theme.welcomeButtonBorder
color: theme.welcomeButtonBackground
}
Accessible.role: Accessible.Button
Accessible.name: text
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
}

View File

@@ -1,109 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import download
import network
import llm
import mysettings
MyDialog {
id: networkDialog
anchors.centerIn: parent
modal: true
padding: 20
Theme {
id: theme
}
Column {
id: column
spacing: 20
Item {
width: childrenRect.width
height: childrenRect.height
Image {
id: img
anchors.top: parent.top
anchors.left: parent.left
width: 60
height: 60
source: "qrc:/gpt4all/icons/gpt4all.svg"
}
Text {
anchors.left: img.right
anchors.leftMargin: 30
anchors.verticalCenter: img.verticalCenter
text: qsTr("Contribute data to the GPT4All Opensource Datalake.")
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
}
}
ScrollView {
clip: true
height: 300
width: 1024 - 40
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
MyTextArea {
id: textOptIn
width: 1024 - 40
text: qsTr("By enabling this feature, you will be able to participate in the democratic process of training a large language model by contributing data for future model improvements.
When a GPT4All model responds to you and you have opted-in, your conversation will be sent to the GPT4All Open Source Datalake. Additionally, you can like/dislike its response. If you dislike a response, you can suggest an alternative response. This data will be collected and aggregated in the GPT4All Datalake.
NOTE: By turning on this feature, you will be sending your data to the GPT4All Open Source Datalake. You should have no expectation of chat privacy when this feature is enabled. You should; however, have an expectation of an optional attribution if you wish. Your chat data will be openly available for anyone to download and will be used by Nomic AI to improve future GPT4All models. Nomic AI will retain all attribution information attached to your data and you will be credited as a contributor to any GPT4All model release that uses your data!")
focus: false
readOnly: true
Accessible.role: Accessible.Paragraph
Accessible.name: qsTr("Terms for opt-in")
Accessible.description: qsTr("Describes what will happen when you opt-in")
}
}
MyTextField {
id: attribution
width: parent.width
text: MySettings.networkAttribution
placeholderText: qsTr("Please provide a name for attribution (optional)")
Accessible.role: Accessible.EditableText
Accessible.name: qsTr("Attribution (optional)")
Accessible.description: qsTr("Provide attribution")
onEditingFinished: {
MySettings.networkAttribution = attribution.text;
}
}
}
footer: DialogButtonBox {
id: dialogBox
padding: 20
alignment: Qt.AlignRight
spacing: 10
MySettingsButton {
text: qsTr("Enable")
Accessible.description: qsTr("Enable opt-in")
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
}
MySettingsButton {
text: qsTr("Cancel")
Accessible.description: qsTr("Cancel opt-in")
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
}
background: Rectangle {
color: "transparent"
}
}
onAccepted: {
MySettings.networkIsActive = true
}
onRejected: {
MySettings.networkIsActive = false
}
}

View File

@@ -1,55 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import download
import network
import llm
MyDialog {
id: newVerionDialog
anchors.centerIn: parent
modal: true
width: contentItem.width
height: contentItem.height
padding: 20
closeButtonVisible: false
Theme {
id: theme
}
Item {
id: contentItem
width: childrenRect.width + 40
height: childrenRect.height + 40
Label {
id: label
anchors.top: parent.top
anchors.left: parent.left
topPadding: 20
bottomPadding: 20
text: qsTr("New version is available")
color: theme.titleTextColor
font.pixelSize: theme.fontSizeLarge
font.bold: true
}
MySettingsButton {
id: button
anchors.left: label.right
anchors.leftMargin: 10
anchors.verticalCenter: label.verticalCenter
padding: 20
text: qsTr("Update")
font.pixelSize: theme.fontSizeLarge
Accessible.description: qsTr("Update to new version")
onClicked: {
if (!LLM.checkForUpdates())
checkForUpdatesError.open()
}
}
}
}

View File

@@ -1,75 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
Dialog {
id: popupDialog
anchors.centerIn: parent
padding: 20
property alias text: textField.text
property bool shouldTimeOut: true
property bool shouldShowBusy: false
modal: shouldShowBusy
closePolicy: shouldShowBusy ? Popup.NoAutoClose : (Popup.CloseOnEscape | Popup.CloseOnPressOutside)
Theme {
id: theme
}
Row {
anchors.centerIn: parent
spacing: 20
Label {
id: textField
width: Math.min(1024, implicitWidth)
height: Math.min(600, implicitHeight)
anchors.verticalCenter: shouldShowBusy ? busyIndicator.verticalCenter : parent.verticalCenter
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
textFormat: Text.StyledText
wrapMode: Text.WordWrap
color: theme.textColor
linkColor: theme.linkColor
Accessible.role: Accessible.HelpBalloon
Accessible.name: text
Accessible.description: qsTr("Reveals a shortlived help balloon")
onLinkActivated: function(link) { Qt.openUrlExternally(link) }
}
MyBusyIndicator {
id: busyIndicator
visible: shouldShowBusy
running: shouldShowBusy
Accessible.role: Accessible.Animation
Accessible.name: qsTr("Busy indicator")
Accessible.description: qsTr("Displayed when the popup is showing busy")
}
}
background: Rectangle {
anchors.fill: parent
color: theme.containerBackground
border.width: 1
border.color: theme.dialogBorder
radius: 10
}
exit: Transition {
NumberAnimation { duration: 500; property: "opacity"; from: 1.0; to: 0.0 }
}
onOpened: {
if (shouldTimeOut)
timer.start()
}
Timer {
id: timer
interval: 500; running: false; repeat: false
onTriggered: popupDialog.close()
}
}

View File

@@ -1,158 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Dialogs
import QtQuick.Layouts
import Qt.labs.folderlistmodel
import download
import modellist
import network
import llm
import mysettings
Rectangle {
id: settingsDialog
color: theme.viewBackground
property alias pageToDisplay: listView.currentIndex
Item {
Accessible.role: Accessible.Dialog
Accessible.name: qsTr("Settings")
Accessible.description: qsTr("Contains various application settings")
}
ListModel {
id: stacksModel
ListElement {
title: qsTr("Application")
}
ListElement {
title: qsTr("Model")
}
ListElement {
title: qsTr("LocalDocs")
}
}
ColumnLayout {
id: mainArea
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.margins: 30
spacing: 50
RowLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
spacing: 50
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
Layout.minimumWidth: 200
spacing: 5
Text {
id: welcome
text: qsTr("Settings")
font.pixelSize: theme.fontSizeBanner
color: theme.titleTextColor
}
}
Rectangle {
Layout.fillWidth: true
height: 0
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
Rectangle {
id: stackList
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
width: 220
color: theme.viewBackground
radius: 10
ScrollView {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 10
ScrollBar.vertical.policy: ScrollBar.AsNeeded
clip: true
ListView {
id: listView
anchors.fill: parent
model: stacksModel
delegate: Rectangle {
id: item
width: listView.width
height: titleLabel.height + 10
color: "transparent"
MyButton {
id: titleLabel
backgroundColor: index === listView.currentIndex ? theme.selectedBackground : theme.viewBackground
backgroundColorHovered: backgroundColor
borderColor: "transparent"
borderWidth: 0
textColor: theme.titleTextColor
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 10
font.bold: index === listView.currentIndex
text: title
textAlignment: Qt.AlignLeft
font.pixelSize: theme.fontSizeLarge
onClicked: {
listView.currentIndex = index
}
}
}
}
}
}
StackLayout {
id: stackLayout
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: stackList.right
anchors.right: parent.right
currentIndex: listView.currentIndex
MySettingsStack {
tabs: [
Component { ApplicationSettings { } }
]
}
MySettingsStack {
tabs: [
Component { ModelSettings { } }
]
}
MySettingsStack {
tabs: [
Component { LocalDocsSettings { } }
]
}
}
}
}
}

View File

@@ -1,346 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import download
import network
import llm
import mysettings
MyDialog {
id: startupDialog
anchors.centerIn: parent
modal: true
padding: 10
width: 1024
height: column.height + 20
closePolicy: !optInStatisticsRadio.choiceMade || !optInNetworkRadio.choiceMade ? Popup.NoAutoClose : (Popup.CloseOnEscape | Popup.CloseOnPressOutside)
Theme {
id: theme
}
Column {
id: column
spacing: 20
Item {
width: childrenRect.width
height: childrenRect.height
Image {
id: img
anchors.top: parent.top
anchors.left: parent.left
sourceSize.width: 60
sourceSize.height: 60
mipmap: true
visible: false
source: "qrc:/gpt4all/icons/globe.svg"
}
ColorOverlay {
anchors.fill: img
source: img
color: theme.titleTextColor
}
Text {
anchors.left: img.right
anchors.leftMargin: 10
anchors.verticalCenter: img.verticalCenter
text: qsTr("Welcome!")
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
}
}
ScrollView {
clip: true
height: 200
width: 1024 - 40
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
MyTextArea {
id: welcome
width: 1024 - 40
textFormat: TextEdit.MarkdownText
text: qsTr("### Release notes\n%1### Contributors\n%2").arg(Download.releaseInfo.notes).arg(Download.releaseInfo.contributors)
focus: false
readOnly: true
Accessible.role: Accessible.Paragraph
Accessible.name: qsTr("Release notes")
Accessible.description: qsTr("Release notes for this version")
}
}
ScrollView {
clip: true
height: 150
width: 1024 - 40
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
MyTextArea {
id: optInTerms
width: 1024 - 40
textFormat: TextEdit.MarkdownText
text: qsTr(
"### Opt-ins for anonymous usage analytics and datalake
By enabling these features, you will be able to participate in the democratic process of training a
large language model by contributing data for future model improvements.
When a GPT4All model responds to you and you have opted-in, your conversation will be sent to the GPT4All
Open Source Datalake. Additionally, you can like/dislike its response. If you dislike a response, you
can suggest an alternative response. This data will be collected and aggregated in the GPT4All Datalake.
NOTE: By turning on this feature, you will be sending your data to the GPT4All Open Source Datalake.
You should have no expectation of chat privacy when this feature is enabled. You should; however, have
an expectation of an optional attribution if you wish. Your chat data will be openly available for anyone
to download and will be used by Nomic AI to improve future GPT4All models. Nomic AI will retain all
attribution information attached to your data and you will be credited as a contributor to any GPT4All
model release that uses your data!")
focus: false
readOnly: true
Accessible.role: Accessible.Paragraph
Accessible.name: qsTr("Terms for opt-in")
Accessible.description: qsTr("Describes what will happen when you opt-in")
}
}
GridLayout {
columns: 2
rowSpacing: 10
columnSpacing: 10
anchors.right: parent.right
Label {
id: optInStatistics
text: "Opt-in to anonymous usage analytics used to improve GPT4All"
Layout.row: 0
Layout.column: 0
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
Accessible.role: Accessible.Paragraph
Accessible.name: qsTr("Opt-in for anonymous usage statistics")
}
ButtonGroup {
buttons: optInStatisticsRadio.children
onClicked: {
MySettings.networkUsageStatsActive = optInStatisticsRadio.checked
if (optInNetworkRadio.choiceMade && optInStatisticsRadio.choiceMade)
startupDialog.close();
}
}
RowLayout {
id: optInStatisticsRadio
Layout.alignment: Qt.AlignVCenter
Layout.row: 0
Layout.column: 1
property alias checked: optInStatisticsRadioYes.checked
property bool choiceMade: optInStatisticsRadioYes.checked || optInStatisticsRadioNo.checked
RadioButton {
id: optInStatisticsRadioYes
checked: MySettings.networkUsageStatsActive
text: qsTr("Yes")
font.pixelSize: theme.fontSizeLarge
Accessible.role: Accessible.RadioButton
Accessible.name: qsTr("Opt-in for anonymous usage statistics")
Accessible.description: qsTr("Allow opt-in for anonymous usage statistics")
background: Rectangle {
color: "transparent"
}
indicator: Rectangle {
implicitWidth: 26
implicitHeight: 26
x: optInStatisticsRadioYes.leftPadding
y: parent.height / 2 - height / 2
radius: 13
border.color: theme.dialogBorder
color: "transparent"
Rectangle {
width: 14
height: 14
x: 6
y: 6
radius: 7
color: theme.textColor
visible: optInStatisticsRadioYes.checked
}
}
contentItem: Text {
text: optInStatisticsRadioYes.text
font: optInStatisticsRadioYes.font
opacity: enabled ? 1.0 : 0.3
color: theme.textColor
verticalAlignment: Text.AlignVCenter
leftPadding: optInStatisticsRadioYes.indicator.width + optInStatisticsRadioYes.spacing
}
}
RadioButton {
id: optInStatisticsRadioNo
checked: MySettings.isNetworkUsageStatsActiveSet() && !MySettings.networkUsageStatsActive
text: qsTr("No")
font.pixelSize: theme.fontSizeLarge
Accessible.role: Accessible.RadioButton
Accessible.name: qsTr("Opt-out for anonymous usage statistics")
Accessible.description: qsTr("Allow opt-out for anonymous usage statistics")
background: Rectangle {
color: "transparent"
}
indicator: Rectangle {
implicitWidth: 26
implicitHeight: 26
x: optInStatisticsRadioNo.leftPadding
y: parent.height / 2 - height / 2
radius: 13
border.color: theme.dialogBorder
color: "transparent"
Rectangle {
width: 14
height: 14
x: 6
y: 6
radius: 7
color: theme.textColor
visible: optInStatisticsRadioNo.checked
}
}
contentItem: Text {
text: optInStatisticsRadioNo.text
font: optInStatisticsRadioNo.font
opacity: enabled ? 1.0 : 0.3
color: theme.textColor
verticalAlignment: Text.AlignVCenter
leftPadding: optInStatisticsRadioNo.indicator.width + optInStatisticsRadioNo.spacing
}
}
}
Label {
id: optInNetwork
text: "Opt-in to anonymous sharing of chats to the GPT4All Datalake"
Layout.row: 1
Layout.column: 0
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
Accessible.role: Accessible.Paragraph
Accessible.name: qsTr("Opt-in for network")
Accessible.description: qsTr("Allow opt-in for network")
}
ButtonGroup {
buttons: optInNetworkRadio.children
onClicked: {
MySettings.networkIsActive = optInNetworkRadio.checked
if (optInNetworkRadio.choiceMade && optInStatisticsRadio.choiceMade)
startupDialog.close();
}
}
RowLayout {
id: optInNetworkRadio
Layout.alignment: Qt.AlignVCenter
Layout.row: 1
Layout.column: 1
property alias checked: optInNetworkRadioYes.checked
property bool choiceMade: optInNetworkRadioYes.checked || optInNetworkRadioNo.checked
RadioButton {
id: optInNetworkRadioYes
checked: MySettings.networkIsActive
text: qsTr("Yes")
font.pixelSize: theme.fontSizeLarge
Accessible.role: Accessible.RadioButton
Accessible.name: qsTr("Opt-in for network")
Accessible.description: qsTr("Allow opt-in anonymous sharing of chats to the GPT4All Datalake")
background: Rectangle {
color: "transparent"
}
indicator: Rectangle {
implicitWidth: 26
implicitHeight: 26
x: optInNetworkRadioYes.leftPadding
y: parent.height / 2 - height / 2
radius: 13
border.color: theme.dialogBorder
color: "transparent"
Rectangle {
width: 14
height: 14
x: 6
y: 6
radius: 7
color: theme.textColor
visible: optInNetworkRadioYes.checked
}
}
contentItem: Text {
text: optInNetworkRadioYes.text
font: optInNetworkRadioYes.font
opacity: enabled ? 1.0 : 0.3
color: theme.textColor
verticalAlignment: Text.AlignVCenter
leftPadding: optInNetworkRadioYes.indicator.width + optInNetworkRadioYes.spacing
}
}
RadioButton {
id: optInNetworkRadioNo
checked: MySettings.isNetworkIsActiveSet() && !MySettings.networkIsActive
text: qsTr("No")
font.pixelSize: theme.fontSizeLarge
Accessible.role: Accessible.RadioButton
Accessible.name: qsTr("Opt-out for network")
Accessible.description: qsTr("Allow opt-out anonymous sharing of chats to the GPT4All Datalake")
background: Rectangle {
color: "transparent"
}
indicator: Rectangle {
implicitWidth: 26
implicitHeight: 26
x: optInNetworkRadioNo.leftPadding
y: parent.height / 2 - height / 2
radius: 13
border.color: theme.dialogBorder
color: "transparent"
Rectangle {
width: 14
height: 14
x: 6
y: 6
radius: 7
color: theme.textColor
visible: optInNetworkRadioNo.checked
}
}
contentItem: Text {
text: optInNetworkRadioNo.text
font: optInNetworkRadioNo.font
opacity: enabled ? 1.0 : 0.3
color: theme.textColor
verticalAlignment: Text.AlignVCenter
leftPadding: optInNetworkRadioNo.indicator.width + optInNetworkRadioNo.spacing
}
}
}
}
}
}

View File

@@ -1,46 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import llm
import mysettings
MyDialog {
id: switchModelDialog
anchors.centerIn: parent
modal: true
padding: 20
property int index: -1
Theme {
id: theme
}
contentItem: Text {
textFormat: Text.StyledText
text: qsTr("<b>Warning:</b> changing the model will erase the current conversation. Do you wish to continue?")
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
}
footer: DialogButtonBox {
id: dialogBox
padding: 20
alignment: Qt.AlignRight
spacing: 10
MySettingsButton {
text: qsTr("Continue")
Accessible.description: qsTr("Continue with model loading")
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
}
MySettingsButton {
text: qsTr("Cancel")
Accessible.description: qsTr("Cancel")
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
}
background: Rectangle {
color: "transparent"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,77 +0,0 @@
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
import download
import network
import llm
MyDialog {
id: thumbsDownDialog
modal: true
padding: 20
Theme {
id: theme
}
property alias response: thumbsDownNewResponse.text
Column {
anchors.fill: parent
spacing: 20
Item {
width: childrenRect.width
height: childrenRect.height
Image {
id: img
anchors.top: parent.top
anchors.left: parent.left
width: 60
height: 60
source: "qrc:/gpt4all/icons/thumbs_down.svg"
}
Text {
anchors.left: img.right
anchors.leftMargin: 30
anchors.verticalCenter: img.verticalCenter
text: qsTr("Please edit the text below to provide a better response. (optional)")
color: theme.textColor
font.pixelSize: theme.fontSizeLarge
}
}
ScrollView {
clip: true
height: 120
width: parent.width
ScrollBar.vertical.policy: ScrollBar.AsNeeded
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
MyTextArea {
id: thumbsDownNewResponse
placeholderText: qsTr("Please provide a better response...")
}
}
}
footer: DialogButtonBox {
padding: 20
alignment: Qt.AlignRight
spacing: 10
MySettingsButton {
text: qsTr("Submit")
Accessible.description: qsTr("Submits the user's response")
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
}
MySettingsButton {
text: qsTr("Cancel")
Accessible.description: qsTr("Closes the response dialog")
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
}
background: Rectangle {
color: "transparent"
}
}
}

View File

@@ -1,98 +0,0 @@
/*
* SPDX-License-Identifier: MIT
* Source: https://gist.github.com/jonmcclung/bae669101d17b103e94790341301c129
* Adapted from StackOverflow: http://stackoverflow.com/questions/26879266/make-toast-in-android-by-qml
*/
import QtQuick 2.0
/**
* @brief An Android-like timed message text in a box that self-destroys when finished if desired
*/
Rectangle {
/**
* Public
*/
/**
* @brief Shows this Toast
*
* @param {string} text Text to show
* @param {real} duration Duration to show in milliseconds, defaults to 3000
*/
function show(text, duration=3000) {
message.text = text;
if (typeof duration !== "undefined") { // checks if parameter was passed
time = Math.max(duration, 2 * fadeTime);
}
else {
time = defaultTime;
}
animation.start();
}
property bool selfDestroying: false // whether this Toast will self-destroy when it is finished
/**
* Private
*/
id: root
readonly property real defaultTime: 3000
property real time: defaultTime
readonly property real fadeTime: 300
property real margin: 10
anchors {
left: parent.left
right: parent.right
margins: margin
}
height: message.height + margin
radius: margin
opacity: 0
color: "#222222"
Text {
id: message
color: "white"
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
anchors {
top: parent.top
left: parent.left
right: parent.right
margins: margin / 2
}
}
SequentialAnimation on opacity {
id: animation
running: false
NumberAnimation {
to: .9
duration: fadeTime
}
PauseAnimation {
duration: time - 2 * fadeTime
}
NumberAnimation {
to: 0
duration: fadeTime
}
onRunningChanged: {
if (!running && selfDestroying) {
root.destroy();
}
}
}
}

View File

@@ -1,60 +0,0 @@
/*
* SPDX-License-Identifier: MIT
* Source: https://gist.github.com/jonmcclung/bae669101d17b103e94790341301c129
* Adapted from StackOverflow: http://stackoverflow.com/questions/26879266/make-toast-in-android-by-qml
*/
import QtQuick 2.0
/**
* @brief Manager that creates Toasts dynamically
*/
ListView {
/**
* Public
*/
/**
* @brief Shows a Toast
*
* @param {string} text Text to show
* @param {real} duration Duration to show in milliseconds, defaults to 3000
*/
function show(text, duration=3000) {
model.insert(0, {text: text, duration: duration});
}
/**
* Private
*/
id: root
z: Infinity
spacing: 5
anchors.fill: parent
anchors.bottomMargin: 10
verticalLayoutDirection: ListView.BottomToTop
interactive: false
displaced: Transition {
NumberAnimation {
properties: "y"
easing.type: Easing.InOutQuad
}
}
delegate: Toast {
Component.onCompleted: {
if (typeof duration === "undefined") {
show(text);
}
else {
show(text, duration);
}
}
}
model: ListModel {id: model}
}