From a191844a3f5a165c3985f059239940955225d843 Mon Sep 17 00:00:00 2001 From: Jared Van Bortel Date: Fri, 28 Jun 2024 20:34:03 -0400 Subject: [PATCH] new UI fixes, part 5 (#2485) additional new ui changes, part 5 (#2485) Signed-off-by: Jared Van Bortel --- gpt4all-chat/database.cpp | 5 +- gpt4all-chat/main.qml | 2 +- gpt4all-chat/metadata/models3.json | 1 + gpt4all-chat/modellist.cpp | 17 +- gpt4all-chat/modellist.h | 9 +- gpt4all-chat/qml/AddModelView.qml | 9 +- gpt4all-chat/qml/ChatView.qml | 31 ++- gpt4all-chat/qml/CollectionsDrawer.qml | 2 +- gpt4all-chat/qml/HomeView.qml | 2 +- gpt4all-chat/qml/ModelSettings.qml | 4 +- gpt4all-chat/qml/ModelsView.qml | 260 ++++++++++++++++++++++--- 11 files changed, 286 insertions(+), 56 deletions(-) diff --git a/gpt4all-chat/database.cpp b/gpt4all-chat/database.cpp index 8deb1175..b24e99ef 100644 --- a/gpt4all-chat/database.cpp +++ b/gpt4all-chat/database.cpp @@ -1493,7 +1493,7 @@ void Database::start() } else if (!initDb(modelPath, oldCollections)) { m_databaseValid = false; } else { - //cleanDB(); + cleanDB(); addCurrentFolders(); } @@ -2118,7 +2118,8 @@ void Database::changeFileExtensions(const QStringList &extensions) m_scannedFileExtensions = extensions; - cleanDB(); + if (cleanDB()) + updateCollectionStatistics(); QSqlQuery q(m_db); QList collections; diff --git a/gpt4all-chat/main.qml b/gpt4all-chat/main.qml index dda8b795..1de8bc47 100644 --- a/gpt4all-chat/main.qml +++ b/gpt4all-chat/main.qml @@ -413,7 +413,7 @@ Window { ColorOverlay { 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 source: antennaImage color: theme.styledTextColor diff --git a/gpt4all-chat/metadata/models3.json b/gpt4all-chat/metadata/models3.json index d96d5737..e0543937 100644 --- a/gpt4all-chat/metadata/models3.json +++ b/gpt4all-chat/metadata/models3.json @@ -312,6 +312,7 @@ "filename": "all-MiniLM-L6-v2.gguf2.f16.gguf", "filesize": "45949216", "requires": "2.7.4", + "removedIn": "3.0.0", "ramrequired": "1", "parameters": "40 million", "quant": "f16", diff --git a/gpt4all-chat/modellist.cpp b/gpt4all-chat/modellist.cpp index 359f29a4..ed8cd63c 100644 --- a/gpt4all-chat/modellist.cpp +++ b/gpt4all-chat/modellist.cpp @@ -367,8 +367,9 @@ QVariantMap ModelInfo::getFields() const }; } -InstalledModels::InstalledModels(QObject *parent) +InstalledModels::InstalledModels(QObject *parent, bool selectable) : QSortFilterProxyModel(parent) + , m_selectable(selectable) { connect(this, &InstalledModels::rowsInserted, this, &InstalledModels::countChanged); connect(this, &InstalledModels::rowsRemoved, this, &InstalledModels::countChanged); @@ -379,11 +380,15 @@ InstalledModels::InstalledModels(QObject *parent) bool InstalledModels::filterAcceptsRow(int sourceRow, 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); bool isInstalled = sourceModel()->data(index, ModelList::InstalledRole).toBool(); + bool isDownloading = sourceModel()->data(index, ModelList::DownloadingRole).toBool(); bool isEmbeddingModel = sourceModel()->data(index, ModelList::IsEmbeddingModelRole).toBool(); // list installed chat models - return isInstalled && !isEmbeddingModel; + return (isInstalled || (!m_selectable && isDownloading)) && !isEmbeddingModel; } 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 bool withinLimit = sourceRow < (m_expanded ? sourceModel()->rowCount() : m_limit); QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); - bool isDownloadable = !sourceModel()->data(index, ModelList::DescriptionRole).toString().isEmpty(); - bool isInstalled = sourceModel()->data(index, ModelList::InstalledRole).toBool(); - bool isIncomplete = sourceModel()->data(index, ModelList::IncompleteRole).toBool(); + bool hasDescription = !sourceModel()->data(index, ModelList::DescriptionRole).toString().isEmpty(); bool isClone = sourceModel()->data(index, ModelList::IsCloneRole).toBool(); - return withinLimit && !isClone && !isInstalled && (isDownloadable || isIncomplete); + return withinLimit && hasDescription && !isClone; } int DownloadableModels::count() const @@ -446,6 +449,7 @@ ModelList *ModelList::globalInstance() ModelList::ModelList() : QAbstractListModel(nullptr) , m_installedModels(new InstalledModels(this)) + , m_selectableModels(new InstalledModels(this, /*selectable*/ true)) , m_downloadableModels(new DownloadableModels(this)) , m_asyncModelRequestOngoing(false) , m_discoverLimit(20) @@ -456,6 +460,7 @@ ModelList::ModelList() , m_discoverInProgress(false) { m_installedModels->setSourceModel(this); + m_selectableModels->setSourceModel(this); m_downloadableModels->setSourceModel(this); connect(MySettings::globalInstance(), &MySettings::modelPathChanged, this, &ModelList::updateModelsFromDirectory); diff --git a/gpt4all-chat/modellist.h b/gpt4all-chat/modellist.h index a7df583c..8c81622f 100644 --- a/gpt4all-chat/modellist.h +++ b/gpt4all-chat/modellist.h @@ -208,7 +208,7 @@ class InstalledModels : public QSortFilterProxyModel Q_OBJECT Q_PROPERTY(int count READ count NOTIFY countChanged) public: - explicit InstalledModels(QObject *parent); + explicit InstalledModels(QObject *parent, bool selectable = false); int count() const { return rowCount(); } Q_SIGNALS: @@ -216,6 +216,9 @@ Q_SIGNALS: protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; + +private: + bool m_selectable; }; class DownloadableModels : public QSortFilterProxyModel @@ -252,6 +255,7 @@ class ModelList : public QAbstractListModel Q_OBJECT Q_PROPERTY(int count READ count NOTIFY countChanged) 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(QList userDefaultModelList READ userDefaultModelList NOTIFY userDefaultModelListChanged) Q_PROPERTY(bool asyncModelRequestOngoing READ asyncModelRequestOngoing NOTIFY asyncModelRequestOngoingChanged) @@ -396,6 +400,7 @@ public: const QList userDefaultModelList() const; InstalledModels *installedModels() const { return m_installedModels; } + InstalledModels *selectableModels() const { return m_selectableModels; } DownloadableModels *downloadableModels() const { return m_downloadableModels; } static inline QString toFileSize(quint64 sz) { @@ -433,6 +438,7 @@ public: Q_SIGNALS: void countChanged(); void installedModelsChanged(); + void selectableModelsChanged(); void downloadableModelsChanged(); void userDefaultModelListChanged(); void asyncModelRequestOngoingChanged(); @@ -471,6 +477,7 @@ private: mutable QMutex m_mutex; QNetworkAccessManager m_networkManager; InstalledModels *m_installedModels; + InstalledModels *m_selectableModels; DownloadableModels *m_downloadableModels; QList m_models; QHash m_modelMap; diff --git a/gpt4all-chat/qml/AddModelView.qml b/gpt4all-chat/qml/AddModelView.qml index d5a943aa..360c6231 100644 --- a/gpt4all-chat/qml/AddModelView.qml +++ b/gpt4all-chat/qml/AddModelView.qml @@ -365,7 +365,12 @@ Rectangle { Accessible.role: Accessible.Paragraph Accessible.name: qsTr("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 @@ -418,7 +423,7 @@ Rectangle { Layout.minimumWidth: 200 Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - visible: installed || downloadError !== "" + visible: !isDownloading && (installed || isIncomplete) Accessible.description: qsTr("Remove model from filesystem") onClicked: { Download.removeModel(filename); diff --git a/gpt4all-chat/qml/ChatView.qml b/gpt4all-chat/qml/ChatView.qml index d9fe4ea6..5b12c838 100644 --- a/gpt4all-chat/qml/ChatView.qml +++ b/gpt4all-chat/qml/ChatView.qml @@ -242,8 +242,8 @@ Rectangle { enabled: !currentChat.isServer && !currentChat.trySwitchContextInProgress && !currentChat.isCurrentlyLoading - && ModelList.installedModels.count !== 0 - model: ModelList.installedModels + && ModelList.selectableModels.count !== 0 + model: ModelList.selectableModels valueRole: "id" textRole: "name" @@ -297,7 +297,7 @@ Rectangle { return 25 } text: { - if (ModelList.installedModels.count === 0) + if (ModelList.selectableModels.count === 0) return qsTr("No model installed...") if (currentChat.modelLoadingError !== "") return qsTr("Model loading error...") @@ -602,10 +602,10 @@ Rectangle { id: homePage color: "transparent" anchors.fill: parent - visible: !currentChat.isModelLoaded && (ModelList.installedModels.count === 0 || currentModelName() === "") && !currentChat.isServer + visible: !currentChat.isModelLoaded && (ModelList.selectableModels.count === 0 || currentModelName() === "") && !currentChat.isServer ColumnLayout { - visible: ModelList.installedModels.count !== 0 + visible: ModelList.selectableModels.count !== 0 id: modelInstalledLabel anchors.centerIn: parent spacing: 0 @@ -637,7 +637,7 @@ Rectangle { MyButton { id: loadDefaultModelButton - visible: ModelList.installedModels.count !== 0 + visible: ModelList.selectableModels.count !== 0 anchors.top: modelInstalledLabel.bottom anchors.topMargin: 50 anchors.horizontalCenter: modelInstalledLabel.horizontalCenter @@ -683,7 +683,7 @@ Rectangle { ColumnLayout { id: noModelInstalledLabel - visible: ModelList.installedModels.count === 0 + visible: ModelList.selectableModels.count === 0 anchors.centerIn: parent spacing: 0 @@ -704,7 +704,7 @@ Rectangle { } MyButton { - visible: ModelList.installedModels.count === 0 + visible: ModelList.selectableModels.count === 0 anchors.top: noModelInstalledLabel.bottom anchors.topMargin: 50 anchors.horizontalCenter: noModelInstalledLabel.horizontalCenter @@ -721,7 +721,7 @@ Rectangle { ColumnLayout { anchors.fill: parent - visible: ModelList.installedModels.count !== 0 && chatModel.count !== 0 + visible: ModelList.selectableModels.count !== 0 && chatModel.count !== 0 ListView { id: listView Layout.maximumWidth: 1280 @@ -1047,10 +1047,9 @@ Rectangle { } } - TextArea { - text: consolidatedSources.length + " " + qsTr("Sources") + Text { + text: qsTr("%1 Sources").arg(consolidatedSources.length) padding: 0 - readOnly: true font.pixelSize: theme.fontSizeLarge font.bold: true color: theme.styledTextColor @@ -1095,6 +1094,7 @@ Rectangle { id: sourcesLayout Layout.row: 3 Layout.column: 1 + Layout.topMargin: 5 visible: consolidatedSources.length !== 0 && MySettings.localDocsShowReferences && (!currentResponse || !currentChat.responseInProgress) clip: true Layout.fillWidth: true @@ -1126,7 +1126,6 @@ Rectangle { Flow { Layout.fillWidth: true - Layout.topMargin: 5 spacing: 10 visible: consolidatedSources.length !== 0 Repeater { @@ -1397,7 +1396,7 @@ Rectangle { RectangularGlow { id: effect - visible: !currentChat.isServer && ModelList.installedModels.count !== 0 + visible: !currentChat.isServer && ModelList.selectableModels.count !== 0 anchors.fill: textInputView glowRadius: 50 spread: 0 @@ -1415,7 +1414,7 @@ Rectangle { anchors.leftMargin: Math.max((parent.width - 1310) / 2, 30) anchors.rightMargin: Math.max((parent.width - 1310) / 2, 30) height: Math.min(contentHeight, 200) - visible: !currentChat.isServer && ModelList.installedModels.count !== 0 + visible: !currentChat.isServer && ModelList.selectableModels.count !== 0 MyTextArea { id: textInput color: theme.textColor @@ -1509,7 +1508,7 @@ Rectangle { anchors.rightMargin: 15 width: 30 height: 30 - visible: !currentChat.isServer && ModelList.installedModels.count !== 0 + visible: !currentChat.isServer && ModelList.selectableModels.count !== 0 enabled: !currentChat.responseInProgress source: "qrc:/gpt4all/icons/send_message.svg" Accessible.name: qsTr("Send message") diff --git a/gpt4all-chat/qml/CollectionsDrawer.qml b/gpt4all-chat/qml/CollectionsDrawer.qml index 62106a81..48ebd6a8 100644 --- a/gpt4all-chat/qml/CollectionsDrawer.qml +++ b/gpt4all-chat/qml/CollectionsDrawer.qml @@ -136,7 +136,7 @@ Rectangle { Text { Layout.fillWidth: true 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 wrapMode: Text.WordWrap elide: Text.ElideRight diff --git a/gpt4all-chat/qml/HomeView.qml b/gpt4all-chat/qml/HomeView.qml index 2ac03ad5..a9c6b402 100644 --- a/gpt4all-chat/qml/HomeView.qml +++ b/gpt4all-chat/qml/HomeView.qml @@ -53,7 +53,7 @@ Rectangle { Text { Layout.alignment: Qt.AlignHCenter - text: qsTr("the privacy-first LLM chat application") + text: qsTr("The privacy-first LLM chat application") font.pixelSize: theme.fontSizeLarge color: theme.titleInfoTextColor } diff --git a/gpt4all-chat/qml/ModelSettings.qml b/gpt4all-chat/qml/ModelSettings.qml index a20f1ef2..4c3f6dca 100644 --- a/gpt4all-chat/qml/ModelSettings.qml +++ b/gpt4all-chat/qml/ModelSettings.qml @@ -17,7 +17,7 @@ MySettingsTab { columns: 3 rowSpacing: 10 columnSpacing: 10 - enabled: ModelList.installedModels.count !== 0 + enabled: ModelList.selectableModels.count !== 0 property var currentModelName: comboBox.currentText property var currentModelId: comboBox.currentValue @@ -43,7 +43,7 @@ MySettingsTab { MyComboBox { id: comboBox Layout.fillWidth: true - model: ModelList.installedModels + model: ModelList.selectableModels valueRole: "id" textRole: "name" currentIndex: { diff --git a/gpt4all-chat/qml/ModelsView.qml b/gpt4all-chat/qml/ModelsView.qml index 7a3b8427..0eb74a11 100644 --- a/gpt4all-chat/qml/ModelsView.qml +++ b/gpt4all-chat/qml/ModelsView.qml @@ -82,7 +82,7 @@ Rectangle { } Text { - text: qsTr("Locally installed large language models") + text: qsTr("Locally installed chat models") font.pixelSize: theme.fontSizeLarge color: theme.titleInfoTextColor } @@ -150,20 +150,240 @@ Rectangle { color: theme.dividerColor } - Text { - id: descriptionText - text: description - font.pixelSize: theme.fontSizeLarge - Layout.row: 1 + RowLayout { Layout.topMargin: 10 - 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: Qt.openUrlExternally(link) + 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: { + 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: "" + + qsTr("Error") + + "" + 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("WARNING: Not recommended for your hardware.") + + qsTr(" Model requires more memory (") + ramrequired + + qsTr(" GB) than your system has available (") + + 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() { + 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 { @@ -206,7 +426,7 @@ Rectangle { color: theme.mutedDarkTextColor } Text { - text: ramrequired + qsTr(" GB") + text: ramrequired >= 0 ? ramrequired + qsTr(" GB") : "?" color: theme.textColor font.pixelSize: theme.fontSizeSmall font.bold: true @@ -228,7 +448,7 @@ Rectangle { color: theme.mutedDarkTextColor } Text { - text: parameters + text: parameters !== "" ? parameters : "?" color: theme.textColor font.pixelSize: theme.fontSizeSmall font.bold: true @@ -294,14 +514,6 @@ Rectangle { height: 1 color: theme.dividerColor } - - MySettingsButton { - text: qsTr("Remove") - textColor: theme.red500 - onClicked: Download.removeModel(filename) - backgroundColor: "transparent" - backgroundColorHovered: theme.lighterButtonBackgroundHoveredRed - } } } }