mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2025-08-05 01:53:13 +00:00
new UI fixes, part 5 (#2485)
additional new ui changes, part 5 (#2485) Signed-off-by: Jared Van Bortel <jared@nomic.ai>
This commit is contained in:
parent
22396a6fa1
commit
a191844a3f
@ -1493,7 +1493,7 @@ void Database::start()
|
|||||||
} else if (!initDb(modelPath, oldCollections)) {
|
} else if (!initDb(modelPath, oldCollections)) {
|
||||||
m_databaseValid = false;
|
m_databaseValid = false;
|
||||||
} else {
|
} else {
|
||||||
//cleanDB();
|
cleanDB();
|
||||||
addCurrentFolders();
|
addCurrentFolders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2118,7 +2118,8 @@ void Database::changeFileExtensions(const QStringList &extensions)
|
|||||||
|
|
||||||
m_scannedFileExtensions = extensions;
|
m_scannedFileExtensions = extensions;
|
||||||
|
|
||||||
cleanDB();
|
if (cleanDB())
|
||||||
|
updateCollectionStatistics();
|
||||||
|
|
||||||
QSqlQuery q(m_db);
|
QSqlQuery q(m_db);
|
||||||
QList<CollectionItem> collections;
|
QList<CollectionItem> collections;
|
||||||
|
@ -413,7 +413,7 @@ Window {
|
|||||||
|
|
||||||
ColorOverlay {
|
ColorOverlay {
|
||||||
id: antennaColored
|
id: antennaColored
|
||||||
visible: ModelList.installedModels.count !== 0 && (currentChat.isServer || currentChat.modelInfo.isOnline || MySettings.networkIsActive)
|
visible: ModelList.selectableModels.count !== 0 && (currentChat.isServer || currentChat.modelInfo.isOnline || MySettings.networkIsActive)
|
||||||
anchors.fill: antennaImage
|
anchors.fill: antennaImage
|
||||||
source: antennaImage
|
source: antennaImage
|
||||||
color: theme.styledTextColor
|
color: theme.styledTextColor
|
||||||
|
@ -312,6 +312,7 @@
|
|||||||
"filename": "all-MiniLM-L6-v2.gguf2.f16.gguf",
|
"filename": "all-MiniLM-L6-v2.gguf2.f16.gguf",
|
||||||
"filesize": "45949216",
|
"filesize": "45949216",
|
||||||
"requires": "2.7.4",
|
"requires": "2.7.4",
|
||||||
|
"removedIn": "3.0.0",
|
||||||
"ramrequired": "1",
|
"ramrequired": "1",
|
||||||
"parameters": "40 million",
|
"parameters": "40 million",
|
||||||
"quant": "f16",
|
"quant": "f16",
|
||||||
|
@ -367,8 +367,9 @@ QVariantMap ModelInfo::getFields() const
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
InstalledModels::InstalledModels(QObject *parent)
|
InstalledModels::InstalledModels(QObject *parent, bool selectable)
|
||||||
: QSortFilterProxyModel(parent)
|
: QSortFilterProxyModel(parent)
|
||||||
|
, m_selectable(selectable)
|
||||||
{
|
{
|
||||||
connect(this, &InstalledModels::rowsInserted, this, &InstalledModels::countChanged);
|
connect(this, &InstalledModels::rowsInserted, this, &InstalledModels::countChanged);
|
||||||
connect(this, &InstalledModels::rowsRemoved, this, &InstalledModels::countChanged);
|
connect(this, &InstalledModels::rowsRemoved, this, &InstalledModels::countChanged);
|
||||||
@ -379,11 +380,15 @@ InstalledModels::InstalledModels(QObject *parent)
|
|||||||
bool InstalledModels::filterAcceptsRow(int sourceRow,
|
bool InstalledModels::filterAcceptsRow(int sourceRow,
|
||||||
const QModelIndex &sourceParent) const
|
const QModelIndex &sourceParent) const
|
||||||
{
|
{
|
||||||
|
/* TODO(jared): We should list incomplete models alongside installed models on the
|
||||||
|
* Models page. Simply replacing isDownloading with isIncomplete here doesn't work for
|
||||||
|
* some reason - the models show up as something else. */
|
||||||
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||||
bool isInstalled = sourceModel()->data(index, ModelList::InstalledRole).toBool();
|
bool isInstalled = sourceModel()->data(index, ModelList::InstalledRole).toBool();
|
||||||
|
bool isDownloading = sourceModel()->data(index, ModelList::DownloadingRole).toBool();
|
||||||
bool isEmbeddingModel = sourceModel()->data(index, ModelList::IsEmbeddingModelRole).toBool();
|
bool isEmbeddingModel = sourceModel()->data(index, ModelList::IsEmbeddingModelRole).toBool();
|
||||||
// list installed chat models
|
// list installed chat models
|
||||||
return isInstalled && !isEmbeddingModel;
|
return (isInstalled || (!m_selectable && isDownloading)) && !isEmbeddingModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadableModels::DownloadableModels(QObject *parent)
|
DownloadableModels::DownloadableModels(QObject *parent)
|
||||||
@ -403,11 +408,9 @@ bool DownloadableModels::filterAcceptsRow(int sourceRow,
|
|||||||
// FIXME We can eliminate the 'expanded' code as the UI no longer uses this
|
// FIXME We can eliminate the 'expanded' code as the UI no longer uses this
|
||||||
bool withinLimit = sourceRow < (m_expanded ? sourceModel()->rowCount() : m_limit);
|
bool withinLimit = sourceRow < (m_expanded ? sourceModel()->rowCount() : m_limit);
|
||||||
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||||
bool isDownloadable = !sourceModel()->data(index, ModelList::DescriptionRole).toString().isEmpty();
|
bool hasDescription = !sourceModel()->data(index, ModelList::DescriptionRole).toString().isEmpty();
|
||||||
bool isInstalled = sourceModel()->data(index, ModelList::InstalledRole).toBool();
|
|
||||||
bool isIncomplete = sourceModel()->data(index, ModelList::IncompleteRole).toBool();
|
|
||||||
bool isClone = sourceModel()->data(index, ModelList::IsCloneRole).toBool();
|
bool isClone = sourceModel()->data(index, ModelList::IsCloneRole).toBool();
|
||||||
return withinLimit && !isClone && !isInstalled && (isDownloadable || isIncomplete);
|
return withinLimit && hasDescription && !isClone;
|
||||||
}
|
}
|
||||||
|
|
||||||
int DownloadableModels::count() const
|
int DownloadableModels::count() const
|
||||||
@ -446,6 +449,7 @@ ModelList *ModelList::globalInstance()
|
|||||||
ModelList::ModelList()
|
ModelList::ModelList()
|
||||||
: QAbstractListModel(nullptr)
|
: QAbstractListModel(nullptr)
|
||||||
, m_installedModels(new InstalledModels(this))
|
, m_installedModels(new InstalledModels(this))
|
||||||
|
, m_selectableModels(new InstalledModels(this, /*selectable*/ true))
|
||||||
, m_downloadableModels(new DownloadableModels(this))
|
, m_downloadableModels(new DownloadableModels(this))
|
||||||
, m_asyncModelRequestOngoing(false)
|
, m_asyncModelRequestOngoing(false)
|
||||||
, m_discoverLimit(20)
|
, m_discoverLimit(20)
|
||||||
@ -456,6 +460,7 @@ ModelList::ModelList()
|
|||||||
, m_discoverInProgress(false)
|
, m_discoverInProgress(false)
|
||||||
{
|
{
|
||||||
m_installedModels->setSourceModel(this);
|
m_installedModels->setSourceModel(this);
|
||||||
|
m_selectableModels->setSourceModel(this);
|
||||||
m_downloadableModels->setSourceModel(this);
|
m_downloadableModels->setSourceModel(this);
|
||||||
|
|
||||||
connect(MySettings::globalInstance(), &MySettings::modelPathChanged, this, &ModelList::updateModelsFromDirectory);
|
connect(MySettings::globalInstance(), &MySettings::modelPathChanged, this, &ModelList::updateModelsFromDirectory);
|
||||||
|
@ -208,7 +208,7 @@ class InstalledModels : public QSortFilterProxyModel
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||||
public:
|
public:
|
||||||
explicit InstalledModels(QObject *parent);
|
explicit InstalledModels(QObject *parent, bool selectable = false);
|
||||||
int count() const { return rowCount(); }
|
int count() const { return rowCount(); }
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
@ -216,6 +216,9 @@ Q_SIGNALS:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_selectable;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DownloadableModels : public QSortFilterProxyModel
|
class DownloadableModels : public QSortFilterProxyModel
|
||||||
@ -252,6 +255,7 @@ class ModelList : public QAbstractListModel
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||||
Q_PROPERTY(InstalledModels* installedModels READ installedModels NOTIFY installedModelsChanged)
|
Q_PROPERTY(InstalledModels* installedModels READ installedModels NOTIFY installedModelsChanged)
|
||||||
|
Q_PROPERTY(InstalledModels* selectableModels READ selectableModels NOTIFY selectableModelsChanged)
|
||||||
Q_PROPERTY(DownloadableModels* downloadableModels READ downloadableModels NOTIFY downloadableModelsChanged)
|
Q_PROPERTY(DownloadableModels* downloadableModels READ downloadableModels NOTIFY downloadableModelsChanged)
|
||||||
Q_PROPERTY(QList<QString> userDefaultModelList READ userDefaultModelList NOTIFY userDefaultModelListChanged)
|
Q_PROPERTY(QList<QString> userDefaultModelList READ userDefaultModelList NOTIFY userDefaultModelListChanged)
|
||||||
Q_PROPERTY(bool asyncModelRequestOngoing READ asyncModelRequestOngoing NOTIFY asyncModelRequestOngoingChanged)
|
Q_PROPERTY(bool asyncModelRequestOngoing READ asyncModelRequestOngoing NOTIFY asyncModelRequestOngoingChanged)
|
||||||
@ -396,6 +400,7 @@ public:
|
|||||||
const QList<QString> userDefaultModelList() const;
|
const QList<QString> userDefaultModelList() const;
|
||||||
|
|
||||||
InstalledModels *installedModels() const { return m_installedModels; }
|
InstalledModels *installedModels() const { return m_installedModels; }
|
||||||
|
InstalledModels *selectableModels() const { return m_selectableModels; }
|
||||||
DownloadableModels *downloadableModels() const { return m_downloadableModels; }
|
DownloadableModels *downloadableModels() const { return m_downloadableModels; }
|
||||||
|
|
||||||
static inline QString toFileSize(quint64 sz) {
|
static inline QString toFileSize(quint64 sz) {
|
||||||
@ -433,6 +438,7 @@ public:
|
|||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void countChanged();
|
void countChanged();
|
||||||
void installedModelsChanged();
|
void installedModelsChanged();
|
||||||
|
void selectableModelsChanged();
|
||||||
void downloadableModelsChanged();
|
void downloadableModelsChanged();
|
||||||
void userDefaultModelListChanged();
|
void userDefaultModelListChanged();
|
||||||
void asyncModelRequestOngoingChanged();
|
void asyncModelRequestOngoingChanged();
|
||||||
@ -471,6 +477,7 @@ private:
|
|||||||
mutable QMutex m_mutex;
|
mutable QMutex m_mutex;
|
||||||
QNetworkAccessManager m_networkManager;
|
QNetworkAccessManager m_networkManager;
|
||||||
InstalledModels *m_installedModels;
|
InstalledModels *m_installedModels;
|
||||||
|
InstalledModels *m_selectableModels;
|
||||||
DownloadableModels *m_downloadableModels;
|
DownloadableModels *m_downloadableModels;
|
||||||
QList<ModelInfo*> m_models;
|
QList<ModelInfo*> m_models;
|
||||||
QHash<QString, ModelInfo*> m_modelMap;
|
QHash<QString, ModelInfo*> m_modelMap;
|
||||||
|
@ -365,7 +365,12 @@ Rectangle {
|
|||||||
Accessible.role: Accessible.Paragraph
|
Accessible.role: Accessible.Paragraph
|
||||||
Accessible.name: qsTr("Description")
|
Accessible.name: qsTr("Description")
|
||||||
Accessible.description: qsTr("File description")
|
Accessible.description: qsTr("File description")
|
||||||
onLinkActivated: Qt.openUrlExternally(link)
|
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
|
// FIXME Need to overhaul design here which must take into account
|
||||||
@ -418,7 +423,7 @@ Rectangle {
|
|||||||
Layout.minimumWidth: 200
|
Layout.minimumWidth: 200
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
|
||||||
visible: installed || downloadError !== ""
|
visible: !isDownloading && (installed || isIncomplete)
|
||||||
Accessible.description: qsTr("Remove model from filesystem")
|
Accessible.description: qsTr("Remove model from filesystem")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Download.removeModel(filename);
|
Download.removeModel(filename);
|
||||||
|
@ -242,8 +242,8 @@ Rectangle {
|
|||||||
enabled: !currentChat.isServer
|
enabled: !currentChat.isServer
|
||||||
&& !currentChat.trySwitchContextInProgress
|
&& !currentChat.trySwitchContextInProgress
|
||||||
&& !currentChat.isCurrentlyLoading
|
&& !currentChat.isCurrentlyLoading
|
||||||
&& ModelList.installedModels.count !== 0
|
&& ModelList.selectableModels.count !== 0
|
||||||
model: ModelList.installedModels
|
model: ModelList.selectableModels
|
||||||
valueRole: "id"
|
valueRole: "id"
|
||||||
textRole: "name"
|
textRole: "name"
|
||||||
|
|
||||||
@ -297,7 +297,7 @@ Rectangle {
|
|||||||
return 25
|
return 25
|
||||||
}
|
}
|
||||||
text: {
|
text: {
|
||||||
if (ModelList.installedModels.count === 0)
|
if (ModelList.selectableModels.count === 0)
|
||||||
return qsTr("No model installed...")
|
return qsTr("No model installed...")
|
||||||
if (currentChat.modelLoadingError !== "")
|
if (currentChat.modelLoadingError !== "")
|
||||||
return qsTr("Model loading error...")
|
return qsTr("Model loading error...")
|
||||||
@ -602,10 +602,10 @@ Rectangle {
|
|||||||
id: homePage
|
id: homePage
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: !currentChat.isModelLoaded && (ModelList.installedModels.count === 0 || currentModelName() === "") && !currentChat.isServer
|
visible: !currentChat.isModelLoaded && (ModelList.selectableModels.count === 0 || currentModelName() === "") && !currentChat.isServer
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
visible: ModelList.installedModels.count !== 0
|
visible: ModelList.selectableModels.count !== 0
|
||||||
id: modelInstalledLabel
|
id: modelInstalledLabel
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
@ -637,7 +637,7 @@ Rectangle {
|
|||||||
|
|
||||||
MyButton {
|
MyButton {
|
||||||
id: loadDefaultModelButton
|
id: loadDefaultModelButton
|
||||||
visible: ModelList.installedModels.count !== 0
|
visible: ModelList.selectableModels.count !== 0
|
||||||
anchors.top: modelInstalledLabel.bottom
|
anchors.top: modelInstalledLabel.bottom
|
||||||
anchors.topMargin: 50
|
anchors.topMargin: 50
|
||||||
anchors.horizontalCenter: modelInstalledLabel.horizontalCenter
|
anchors.horizontalCenter: modelInstalledLabel.horizontalCenter
|
||||||
@ -683,7 +683,7 @@ Rectangle {
|
|||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: noModelInstalledLabel
|
id: noModelInstalledLabel
|
||||||
visible: ModelList.installedModels.count === 0
|
visible: ModelList.selectableModels.count === 0
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
@ -704,7 +704,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MyButton {
|
MyButton {
|
||||||
visible: ModelList.installedModels.count === 0
|
visible: ModelList.selectableModels.count === 0
|
||||||
anchors.top: noModelInstalledLabel.bottom
|
anchors.top: noModelInstalledLabel.bottom
|
||||||
anchors.topMargin: 50
|
anchors.topMargin: 50
|
||||||
anchors.horizontalCenter: noModelInstalledLabel.horizontalCenter
|
anchors.horizontalCenter: noModelInstalledLabel.horizontalCenter
|
||||||
@ -721,7 +721,7 @@ Rectangle {
|
|||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: ModelList.installedModels.count !== 0 && chatModel.count !== 0
|
visible: ModelList.selectableModels.count !== 0 && chatModel.count !== 0
|
||||||
ListView {
|
ListView {
|
||||||
id: listView
|
id: listView
|
||||||
Layout.maximumWidth: 1280
|
Layout.maximumWidth: 1280
|
||||||
@ -1047,10 +1047,9 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextArea {
|
Text {
|
||||||
text: consolidatedSources.length + " " + qsTr("Sources")
|
text: qsTr("%1 Sources").arg(consolidatedSources.length)
|
||||||
padding: 0
|
padding: 0
|
||||||
readOnly: true
|
|
||||||
font.pixelSize: theme.fontSizeLarge
|
font.pixelSize: theme.fontSizeLarge
|
||||||
font.bold: true
|
font.bold: true
|
||||||
color: theme.styledTextColor
|
color: theme.styledTextColor
|
||||||
@ -1095,6 +1094,7 @@ Rectangle {
|
|||||||
id: sourcesLayout
|
id: sourcesLayout
|
||||||
Layout.row: 3
|
Layout.row: 3
|
||||||
Layout.column: 1
|
Layout.column: 1
|
||||||
|
Layout.topMargin: 5
|
||||||
visible: consolidatedSources.length !== 0 && MySettings.localDocsShowReferences && (!currentResponse || !currentChat.responseInProgress)
|
visible: consolidatedSources.length !== 0 && MySettings.localDocsShowReferences && (!currentResponse || !currentChat.responseInProgress)
|
||||||
clip: true
|
clip: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@ -1126,7 +1126,6 @@ Rectangle {
|
|||||||
|
|
||||||
Flow {
|
Flow {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 5
|
|
||||||
spacing: 10
|
spacing: 10
|
||||||
visible: consolidatedSources.length !== 0
|
visible: consolidatedSources.length !== 0
|
||||||
Repeater {
|
Repeater {
|
||||||
@ -1397,7 +1396,7 @@ Rectangle {
|
|||||||
|
|
||||||
RectangularGlow {
|
RectangularGlow {
|
||||||
id: effect
|
id: effect
|
||||||
visible: !currentChat.isServer && ModelList.installedModels.count !== 0
|
visible: !currentChat.isServer && ModelList.selectableModels.count !== 0
|
||||||
anchors.fill: textInputView
|
anchors.fill: textInputView
|
||||||
glowRadius: 50
|
glowRadius: 50
|
||||||
spread: 0
|
spread: 0
|
||||||
@ -1415,7 +1414,7 @@ Rectangle {
|
|||||||
anchors.leftMargin: Math.max((parent.width - 1310) / 2, 30)
|
anchors.leftMargin: Math.max((parent.width - 1310) / 2, 30)
|
||||||
anchors.rightMargin: Math.max((parent.width - 1310) / 2, 30)
|
anchors.rightMargin: Math.max((parent.width - 1310) / 2, 30)
|
||||||
height: Math.min(contentHeight, 200)
|
height: Math.min(contentHeight, 200)
|
||||||
visible: !currentChat.isServer && ModelList.installedModels.count !== 0
|
visible: !currentChat.isServer && ModelList.selectableModels.count !== 0
|
||||||
MyTextArea {
|
MyTextArea {
|
||||||
id: textInput
|
id: textInput
|
||||||
color: theme.textColor
|
color: theme.textColor
|
||||||
@ -1509,7 +1508,7 @@ Rectangle {
|
|||||||
anchors.rightMargin: 15
|
anchors.rightMargin: 15
|
||||||
width: 30
|
width: 30
|
||||||
height: 30
|
height: 30
|
||||||
visible: !currentChat.isServer && ModelList.installedModels.count !== 0
|
visible: !currentChat.isServer && ModelList.selectableModels.count !== 0
|
||||||
enabled: !currentChat.responseInProgress
|
enabled: !currentChat.responseInProgress
|
||||||
source: "qrc:/gpt4all/icons/send_message.svg"
|
source: "qrc:/gpt4all/icons/send_message.svg"
|
||||||
Accessible.name: qsTr("Send message")
|
Accessible.name: qsTr("Send message")
|
||||||
|
@ -136,7 +136,7 @@ Rectangle {
|
|||||||
Text {
|
Text {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: qsTr("Chat privately with local files using on-device Large Language Models (LLMs). Keeps data private and secure. Best results with Llama 3 Instruct.")
|
text: qsTr("Select a collection to make it available to the chat model.")
|
||||||
font.pixelSize: theme.fontSizeLarger
|
font.pixelSize: theme.fontSizeLarger
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
|
@ -53,7 +53,7 @@ Rectangle {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("the privacy-first LLM chat application")
|
text: qsTr("The privacy-first LLM chat application")
|
||||||
font.pixelSize: theme.fontSizeLarge
|
font.pixelSize: theme.fontSizeLarge
|
||||||
color: theme.titleInfoTextColor
|
color: theme.titleInfoTextColor
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ MySettingsTab {
|
|||||||
columns: 3
|
columns: 3
|
||||||
rowSpacing: 10
|
rowSpacing: 10
|
||||||
columnSpacing: 10
|
columnSpacing: 10
|
||||||
enabled: ModelList.installedModels.count !== 0
|
enabled: ModelList.selectableModels.count !== 0
|
||||||
|
|
||||||
property var currentModelName: comboBox.currentText
|
property var currentModelName: comboBox.currentText
|
||||||
property var currentModelId: comboBox.currentValue
|
property var currentModelId: comboBox.currentValue
|
||||||
@ -43,7 +43,7 @@ MySettingsTab {
|
|||||||
MyComboBox {
|
MyComboBox {
|
||||||
id: comboBox
|
id: comboBox
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
model: ModelList.installedModels
|
model: ModelList.selectableModels
|
||||||
valueRole: "id"
|
valueRole: "id"
|
||||||
textRole: "name"
|
textRole: "name"
|
||||||
currentIndex: {
|
currentIndex: {
|
||||||
|
@ -82,7 +82,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: qsTr("Locally installed large language models")
|
text: qsTr("Locally installed chat models")
|
||||||
font.pixelSize: theme.fontSizeLarge
|
font.pixelSize: theme.fontSizeLarge
|
||||||
color: theme.titleInfoTextColor
|
color: theme.titleInfoTextColor
|
||||||
}
|
}
|
||||||
@ -150,20 +150,240 @@ Rectangle {
|
|||||||
color: theme.dividerColor
|
color: theme.dividerColor
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
RowLayout {
|
||||||
id: descriptionText
|
|
||||||
text: description
|
|
||||||
font.pixelSize: theme.fontSizeLarge
|
|
||||||
Layout.row: 1
|
|
||||||
Layout.topMargin: 10
|
Layout.topMargin: 10
|
||||||
wrapMode: Text.WordWrap
|
Layout.fillWidth: true
|
||||||
textFormat: Text.StyledText
|
Text {
|
||||||
color: theme.textColor
|
id: descriptionText
|
||||||
linkColor: theme.textColor
|
text: description
|
||||||
Accessible.role: Accessible.Paragraph
|
font.pixelSize: theme.fontSizeLarge
|
||||||
Accessible.name: qsTr("Description")
|
Layout.fillWidth: true
|
||||||
Accessible.description: qsTr("File description")
|
wrapMode: Text.WordWrap
|
||||||
onLinkActivated: Qt.openUrlExternally(link)
|
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: {
|
||||||
|
if (apiKey.text === "")
|
||||||
|
apiKey.showError();
|
||||||
|
else
|
||||||
|
Download.installModel(filename, apiKey.text);
|
||||||
|
}
|
||||||
|
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: "<strong><font size=\"1\">"
|
||||||
|
+ qsTr("<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.")
|
||||||
|
+ qsTr(" Model requires more memory (") + ramrequired
|
||||||
|
+ qsTr(" GB) than your system has available (")
|
||||||
|
+ LLM.systemTotalRAMInGBString() + ").</strong></font>"
|
||||||
|
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() {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@ -206,7 +426,7 @@ Rectangle {
|
|||||||
color: theme.mutedDarkTextColor
|
color: theme.mutedDarkTextColor
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
text: ramrequired + qsTr(" GB")
|
text: ramrequired >= 0 ? ramrequired + qsTr(" GB") : "?"
|
||||||
color: theme.textColor
|
color: theme.textColor
|
||||||
font.pixelSize: theme.fontSizeSmall
|
font.pixelSize: theme.fontSizeSmall
|
||||||
font.bold: true
|
font.bold: true
|
||||||
@ -228,7 +448,7 @@ Rectangle {
|
|||||||
color: theme.mutedDarkTextColor
|
color: theme.mutedDarkTextColor
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
text: parameters
|
text: parameters !== "" ? parameters : "?"
|
||||||
color: theme.textColor
|
color: theme.textColor
|
||||||
font.pixelSize: theme.fontSizeSmall
|
font.pixelSize: theme.fontSizeSmall
|
||||||
font.bold: true
|
font.bold: true
|
||||||
@ -294,14 +514,6 @@ Rectangle {
|
|||||||
height: 1
|
height: 1
|
||||||
color: theme.dividerColor
|
color: theme.dividerColor
|
||||||
}
|
}
|
||||||
|
|
||||||
MySettingsButton {
|
|
||||||
text: qsTr("Remove")
|
|
||||||
textColor: theme.red500
|
|
||||||
onClicked: Download.removeModel(filename)
|
|
||||||
backgroundColor: "transparent"
|
|
||||||
backgroundColorHovered: theme.lighterButtonBackgroundHoveredRed
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user