mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2025-09-08 03:49:10 +00:00
repo: organize sources, headers, and deps into subdirectories (#2917)
Signed-off-by: Jared Van Bortel <jared@nomic.ai>
This commit is contained in:
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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")
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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()
|
||||
}
|
||||
}
|
@@ -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 { } }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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}
|
||||
}
|
Reference in New Issue
Block a user