mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-04-05 04:12:49 -04:00
feat: Add configuration manager (#270)
This commit is contained in:
@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
#include "ChatAssistantSettings.hpp"
|
#include "ChatAssistantSettings.hpp"
|
||||||
#include "ChatSerializer.hpp"
|
#include "ChatSerializer.hpp"
|
||||||
|
#include "ConfigurationManager.hpp"
|
||||||
#include "GeneralSettings.hpp"
|
#include "GeneralSettings.hpp"
|
||||||
#include "ToolsSettings.hpp"
|
#include "ToolsSettings.hpp"
|
||||||
#include "Logger.hpp"
|
#include "Logger.hpp"
|
||||||
@ -44,7 +45,6 @@
|
|||||||
#include "context/TokenUtils.hpp"
|
#include "context/TokenUtils.hpp"
|
||||||
#include "llmcore/RulesLoader.hpp"
|
#include "llmcore/RulesLoader.hpp"
|
||||||
#include "ProvidersManager.hpp"
|
#include "ProvidersManager.hpp"
|
||||||
#include "GeneralSettings.hpp"
|
|
||||||
|
|
||||||
namespace QodeAssist::Chat {
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
@ -66,6 +66,20 @@ ChatRootView::ChatRootView(QQuickItem *parent)
|
|||||||
|
|
||||||
connect(
|
connect(
|
||||||
&settings.caModel, &Utils::BaseAspect::changed, this, &ChatRootView::currentTemplateChanged);
|
&settings.caModel, &Utils::BaseAspect::changed, this, &ChatRootView::currentTemplateChanged);
|
||||||
|
|
||||||
|
connect(&settings.caProvider, &Utils::BaseAspect::changed, this, [this]() {
|
||||||
|
auto &settings = Settings::generalSettings();
|
||||||
|
m_currentConfiguration = QString("%1 - %2").arg(settings.caProvider.value(),
|
||||||
|
settings.caModel.value());
|
||||||
|
emit currentConfigurationChanged();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&settings.caModel, &Utils::BaseAspect::changed, this, [this]() {
|
||||||
|
auto &settings = Settings::generalSettings();
|
||||||
|
m_currentConfiguration = QString("%1 - %2").arg(settings.caProvider.value(),
|
||||||
|
settings.caModel.value());
|
||||||
|
emit currentConfigurationChanged();
|
||||||
|
});
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
m_clientInterface,
|
m_clientInterface,
|
||||||
@ -190,6 +204,7 @@ ChatRootView::ChatRootView(QQuickItem *parent)
|
|||||||
|
|
||||||
updateInputTokensCount();
|
updateInputTokensCount();
|
||||||
refreshRules();
|
refreshRules();
|
||||||
|
loadAvailableConfigurations();
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
ProjectExplorer::ProjectManager::instance(),
|
ProjectExplorer::ProjectManager::instance(),
|
||||||
@ -1246,4 +1261,69 @@ bool ChatRootView::isImageFile(const QString &filePath) const
|
|||||||
return imageExtensions.contains(fileInfo.suffix().toLower());
|
return imageExtensions.contains(fileInfo.suffix().toLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatRootView::loadAvailableConfigurations()
|
||||||
|
{
|
||||||
|
auto &manager = Settings::ConfigurationManager::instance();
|
||||||
|
manager.loadConfigurations(Settings::ConfigurationType::Chat);
|
||||||
|
|
||||||
|
QVector<Settings::AIConfiguration> configs = manager.configurations(
|
||||||
|
Settings::ConfigurationType::Chat);
|
||||||
|
|
||||||
|
m_availableConfigurations.clear();
|
||||||
|
m_availableConfigurations.append(QObject::tr("Current Settings"));
|
||||||
|
|
||||||
|
for (const Settings::AIConfiguration &config : configs) {
|
||||||
|
m_availableConfigurations.append(config.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &settings = Settings::generalSettings();
|
||||||
|
QString currentProvider = settings.caProvider.value();
|
||||||
|
QString currentModel = settings.caModel.value();
|
||||||
|
m_currentConfiguration = QString("%1 - %2").arg(currentProvider, currentModel);
|
||||||
|
|
||||||
|
emit availableConfigurationsChanged();
|
||||||
|
emit currentConfigurationChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatRootView::applyConfiguration(const QString &configName)
|
||||||
|
{
|
||||||
|
if (configName == QObject::tr("Current Settings")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &manager = Settings::ConfigurationManager::instance();
|
||||||
|
QVector<Settings::AIConfiguration> configs = manager.configurations(
|
||||||
|
Settings::ConfigurationType::Chat);
|
||||||
|
|
||||||
|
for (const Settings::AIConfiguration &config : configs) {
|
||||||
|
if (config.name == configName) {
|
||||||
|
auto &settings = Settings::generalSettings();
|
||||||
|
|
||||||
|
settings.caProvider.setValue(config.provider);
|
||||||
|
settings.caModel.setValue(config.model);
|
||||||
|
settings.caTemplate.setValue(config.templateName);
|
||||||
|
settings.caUrl.setValue(config.url);
|
||||||
|
settings.caEndpointMode.setValue(settings.caEndpointMode.indexForDisplay(config.endpointMode));
|
||||||
|
settings.caCustomEndpoint.setValue(config.customEndpoint);
|
||||||
|
|
||||||
|
settings.writeSettings();
|
||||||
|
|
||||||
|
m_currentConfiguration = QString("%1 - %2").arg(config.provider, config.model);
|
||||||
|
emit currentConfigurationChanged();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList ChatRootView::availableConfigurations() const
|
||||||
|
{
|
||||||
|
return m_availableConfigurations;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatRootView::currentConfiguration() const
|
||||||
|
{
|
||||||
|
return m_currentConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist::Chat
|
} // namespace QodeAssist::Chat
|
||||||
|
|||||||
@ -58,6 +58,8 @@ class ChatRootView : public QQuickItem
|
|||||||
Q_PROPERTY(int currentMessagePendingEdits READ currentMessagePendingEdits NOTIFY currentMessageEditsStatsChanged FINAL)
|
Q_PROPERTY(int currentMessagePendingEdits READ currentMessagePendingEdits NOTIFY currentMessageEditsStatsChanged FINAL)
|
||||||
Q_PROPERTY(int currentMessageRejectedEdits READ currentMessageRejectedEdits NOTIFY currentMessageEditsStatsChanged FINAL)
|
Q_PROPERTY(int currentMessageRejectedEdits READ currentMessageRejectedEdits NOTIFY currentMessageEditsStatsChanged FINAL)
|
||||||
Q_PROPERTY(bool isThinkingSupport READ isThinkingSupport NOTIFY isThinkingSupportChanged FINAL)
|
Q_PROPERTY(bool isThinkingSupport READ isThinkingSupport NOTIFY isThinkingSupportChanged FINAL)
|
||||||
|
Q_PROPERTY(QStringList availableConfigurations READ availableConfigurations NOTIFY availableConfigurationsChanged FINAL)
|
||||||
|
Q_PROPERTY(QString currentConfiguration READ currentConfiguration NOTIFY currentConfigurationChanged FINAL)
|
||||||
|
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
@ -138,6 +140,11 @@ public:
|
|||||||
Q_INVOKABLE void applyAllFileEditsForCurrentMessage();
|
Q_INVOKABLE void applyAllFileEditsForCurrentMessage();
|
||||||
Q_INVOKABLE void undoAllFileEditsForCurrentMessage();
|
Q_INVOKABLE void undoAllFileEditsForCurrentMessage();
|
||||||
Q_INVOKABLE void updateCurrentMessageEditsStats();
|
Q_INVOKABLE void updateCurrentMessageEditsStats();
|
||||||
|
|
||||||
|
Q_INVOKABLE void loadAvailableConfigurations();
|
||||||
|
Q_INVOKABLE void applyConfiguration(const QString &configName);
|
||||||
|
QStringList availableConfigurations() const;
|
||||||
|
QString currentConfiguration() const;
|
||||||
|
|
||||||
int currentMessageTotalEdits() const;
|
int currentMessageTotalEdits() const;
|
||||||
int currentMessageAppliedEdits() const;
|
int currentMessageAppliedEdits() const;
|
||||||
@ -182,6 +189,8 @@ signals:
|
|||||||
void currentMessageEditsStatsChanged();
|
void currentMessageEditsStatsChanged();
|
||||||
|
|
||||||
void isThinkingSupportChanged();
|
void isThinkingSupportChanged();
|
||||||
|
void availableConfigurationsChanged();
|
||||||
|
void currentConfigurationChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateFileEditStatus(const QString &editId, const QString &status);
|
void updateFileEditStatus(const QString &editId, const QString &status);
|
||||||
@ -213,6 +222,9 @@ private:
|
|||||||
int m_currentMessagePendingEdits{0};
|
int m_currentMessagePendingEdits{0};
|
||||||
int m_currentMessageRejectedEdits{0};
|
int m_currentMessageRejectedEdits{0};
|
||||||
QString m_lastInfoMessage;
|
QString m_lastInfoMessage;
|
||||||
|
|
||||||
|
QStringList m_availableConfigurations;
|
||||||
|
QString m_currentConfiguration;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Chat
|
} // namespace QodeAssist::Chat
|
||||||
|
|||||||
@ -111,6 +111,19 @@ ChatRootView {
|
|||||||
root.isThinkingMode = thinkingMode.checked
|
root.isThinkingMode = thinkingMode.checked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
configSelector {
|
||||||
|
model: root.availableConfigurations
|
||||||
|
displayText: root.currentConfiguration
|
||||||
|
onActivated: function(index) {
|
||||||
|
if (index > 0) {
|
||||||
|
root.applyConfiguration(root.availableConfigurations[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
popup.onAboutToShow: {
|
||||||
|
root.loadAvailableConfigurations()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
|
|||||||
@ -37,6 +37,7 @@ Rectangle {
|
|||||||
property alias agentModeSwitch: agentModeSwitchId
|
property alias agentModeSwitch: agentModeSwitchId
|
||||||
property alias thinkingMode: thinkingModeId
|
property alias thinkingMode: thinkingModeId
|
||||||
property alias activeRulesCount: activeRulesCountId.text
|
property alias activeRulesCount: activeRulesCountId.text
|
||||||
|
property alias configSelector: configSelectorId
|
||||||
|
|
||||||
color: palette.window.hslLightness > 0.5 ?
|
color: palette.window.hslLightness > 0.5 ?
|
||||||
Qt.darker(palette.window, 1.1) :
|
Qt.darker(palette.window, 1.1) :
|
||||||
@ -238,6 +239,17 @@ Rectangle {
|
|||||||
ToolTip.delay: 250
|
ToolTip.delay: 250
|
||||||
ToolTip.text: qsTr("Current amount tokens in chat and LLM limit threshold")
|
ToolTip.text: qsTr("Current amount tokens in chat and LLM limit threshold")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QoAComboBox {
|
||||||
|
id: configSelectorId
|
||||||
|
|
||||||
|
model: []
|
||||||
|
currentIndex: 0
|
||||||
|
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.delay: 250
|
||||||
|
ToolTip.text: qsTr("Switch AI configuration")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2024-2025 Petr Mironychev
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
*
|
*
|
||||||
* This file is part of QodeAssist.
|
* This file is part of QodeAssist.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2024-2025 Petr Mironychev
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
*
|
*
|
||||||
* This file is part of QodeAssist.
|
* This file is part of QodeAssist.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -11,7 +11,12 @@ qt_add_qml_module(QodeAssistUIControls
|
|||||||
qml/Badge.qml
|
qml/Badge.qml
|
||||||
qml/QoAButton.qml
|
qml/QoAButton.qml
|
||||||
qml/QoATextSlider.qml
|
qml/QoATextSlider.qml
|
||||||
|
qml/QoAComboBox.qml
|
||||||
qml/FadeListItemAnimation.qml
|
qml/FadeListItemAnimation.qml
|
||||||
|
|
||||||
|
RESOURCES
|
||||||
|
icons/dropdown-arrow-light.svg
|
||||||
|
icons/dropdown-arrow-dark.svg
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(QodeAssistUIControls
|
target_link_libraries(QodeAssistUIControls
|
||||||
|
|||||||
4
UIControls/icons/dropdown-arrow-dark.svg
Normal file
4
UIControls/icons/dropdown-arrow-dark.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="12" height="8" viewBox="0 0 12 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M6 8L0 0H12L6 8Z" fill="#FFFFFF"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 148 B |
4
UIControls/icons/dropdown-arrow-light.svg
Normal file
4
UIControls/icons/dropdown-arrow-light.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="12" height="8" viewBox="0 0 12 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M6 8L0 0H12L6 8Z" fill="#000000"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 148 B |
157
UIControls/qml/QoAComboBox.qml
Normal file
157
UIControls/qml/QoAComboBox.qml
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic as Basic
|
||||||
|
|
||||||
|
Basic.ComboBox {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
implicitWidth: Math.min(contentItem.implicitWidth + 8, 300)
|
||||||
|
implicitHeight: 30
|
||||||
|
|
||||||
|
indicator: Image {
|
||||||
|
id: dropdownIcon
|
||||||
|
x: control.width - width - 10
|
||||||
|
y: control.topPadding + (control.availableHeight - height) / 2
|
||||||
|
width: 12
|
||||||
|
height: 8
|
||||||
|
source: palette.window.hslLightness > 0.5
|
||||||
|
? "qrc:/qt/qml/UIControls/icons/dropdown-arrow-light.svg"
|
||||||
|
: "qrc:/qt/qml/UIControls/icons/dropdown-arrow-dark.svg"
|
||||||
|
sourceSize: Qt.size(width, height)
|
||||||
|
|
||||||
|
rotation: control.popup.visible ? 180 : 0
|
||||||
|
|
||||||
|
Behavior on rotation {
|
||||||
|
NumberAnimation { duration: 150; easing.type: Easing.OutQuad }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
id: bg
|
||||||
|
|
||||||
|
implicitWidth: control.implicitWidth
|
||||||
|
implicitHeight: 30
|
||||||
|
color: !control.enabled || !control.down ? palette.button : palette.dark
|
||||||
|
border.color: !control.enabled || (!control.hovered && !control.visualFocus)
|
||||||
|
? palette.mid
|
||||||
|
: palette.highlight
|
||||||
|
border.width: 1
|
||||||
|
radius: 5
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: 150 }
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on border.color {
|
||||||
|
ColorAnimation { duration: 150 }
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: bg
|
||||||
|
radius: bg.radius
|
||||||
|
gradient: Gradient {
|
||||||
|
GradientStop { position: 0.0; color: Qt.alpha(palette.highlight, 0.4) }
|
||||||
|
GradientStop { position: 1.0; color: Qt.alpha(palette.highlight, 0.2) }
|
||||||
|
}
|
||||||
|
opacity: control.hovered ? 0.3 : 0.01
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation { duration: 250 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Text {
|
||||||
|
leftPadding: 10
|
||||||
|
rightPadding: 30
|
||||||
|
text: control.displayText
|
||||||
|
font.pixelSize: 12
|
||||||
|
color: palette.text
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
elide: Text.ElideNone
|
||||||
|
}
|
||||||
|
|
||||||
|
popup: Popup {
|
||||||
|
y: control.height + 2
|
||||||
|
width: control.width
|
||||||
|
implicitHeight: Math.min(contentItem.implicitHeight, 300)
|
||||||
|
padding: 4
|
||||||
|
|
||||||
|
contentItem: ListView {
|
||||||
|
clip: true
|
||||||
|
implicitHeight: contentHeight
|
||||||
|
model: control.popup.visible ? control.delegateModel : null
|
||||||
|
currentIndex: control.highlightedIndex
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
policy: ScrollBar.AsNeeded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: palette.base
|
||||||
|
border.color: Qt.lighter(palette.mid, 1.1)
|
||||||
|
border.width: 1
|
||||||
|
radius: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
enter: Transition {
|
||||||
|
NumberAnimation { property: "opacity"; from: 0.0; to: 1.0; duration: 150 }
|
||||||
|
}
|
||||||
|
|
||||||
|
exit: Transition {
|
||||||
|
NumberAnimation { property: "opacity"; from: 1.0; to: 0.0; duration: 100 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: ItemDelegate {
|
||||||
|
width: control.width - 8
|
||||||
|
height: 32
|
||||||
|
|
||||||
|
contentItem: Text {
|
||||||
|
text: modelData
|
||||||
|
color: palette.text
|
||||||
|
font.pixelSize: 12
|
||||||
|
elide: Text.ElideRight
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
leftPadding: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
highlighted: control.highlightedIndex === index
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
radius: 4
|
||||||
|
color: highlighted
|
||||||
|
? Qt.alpha(palette.highlight, 0.2)
|
||||||
|
: parent.hovered
|
||||||
|
? (palette.window.hslLightness > 0.5
|
||||||
|
? Qt.darker(palette.base, 1.05)
|
||||||
|
: Qt.lighter(palette.base, 1.15))
|
||||||
|
: "transparent"
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: 100 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,6 +1,7 @@
|
|||||||
add_library(QodeAssistSettings STATIC
|
add_library(QodeAssistSettings STATIC
|
||||||
GeneralSettings.hpp GeneralSettings.cpp
|
GeneralSettings.hpp GeneralSettings.cpp
|
||||||
CustomPromptSettings.hpp CustomPromptSettings.cpp
|
CustomPromptSettings.hpp CustomPromptSettings.cpp
|
||||||
|
ConfigurationManager.hpp ConfigurationManager.cpp
|
||||||
SettingsUtils.hpp
|
SettingsUtils.hpp
|
||||||
SettingsConstants.hpp
|
SettingsConstants.hpp
|
||||||
ButtonAspect.hpp
|
ButtonAspect.hpp
|
||||||
|
|||||||
237
settings/ConfigurationManager.cpp
Normal file
237
settings/ConfigurationManager.cpp
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024-2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ConfigurationManager.hpp"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
|
#include "Logger.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Settings {
|
||||||
|
|
||||||
|
ConfigurationManager::ConfigurationManager(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
ConfigurationManager &ConfigurationManager::instance()
|
||||||
|
{
|
||||||
|
static ConfigurationManager instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ConfigurationManager::configurationTypeToString(ConfigurationType type) const
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case ConfigurationType::CodeCompletion:
|
||||||
|
return "code_completion";
|
||||||
|
case ConfigurationType::Chat:
|
||||||
|
return "chat";
|
||||||
|
case ConfigurationType::QuickRefactor:
|
||||||
|
return "quick_refactor";
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ConfigurationManager::getConfigurationDirectory(ConfigurationType type) const
|
||||||
|
{
|
||||||
|
QString path = QString("%1/qodeassist/configurations/%2")
|
||||||
|
.arg(Core::ICore::userResourcePath().toFSPathString(),
|
||||||
|
configurationTypeToString(type));
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigurationManager::ensureDirectoryExists(ConfigurationType type) const
|
||||||
|
{
|
||||||
|
QDir dir(getConfigurationDirectory(type));
|
||||||
|
if (!dir.exists()) {
|
||||||
|
return dir.mkpath(".");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigurationManager::loadConfigurations(ConfigurationType type)
|
||||||
|
{
|
||||||
|
QVector<AIConfiguration> *configs = nullptr;
|
||||||
|
switch (type) {
|
||||||
|
case ConfigurationType::CodeCompletion:
|
||||||
|
configs = &m_ccConfigurations;
|
||||||
|
break;
|
||||||
|
case ConfigurationType::Chat:
|
||||||
|
configs = &m_caConfigurations;
|
||||||
|
break;
|
||||||
|
case ConfigurationType::QuickRefactor:
|
||||||
|
configs = &m_qrConfigurations;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!configs) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
configs->clear();
|
||||||
|
|
||||||
|
if (!ensureDirectoryExists(type)) {
|
||||||
|
LOG_MESSAGE("Failed to create configuration directory");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir dir(getConfigurationDirectory(type));
|
||||||
|
QStringList filters;
|
||||||
|
filters << "*.json";
|
||||||
|
QFileInfoList files = dir.entryInfoList(filters, QDir::Files);
|
||||||
|
|
||||||
|
for (const QFileInfo &fileInfo : files) {
|
||||||
|
QFile file(fileInfo.absoluteFilePath());
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
LOG_MESSAGE(QString("Failed to open configuration file: %1").arg(fileInfo.fileName()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
if (!doc.isObject()) {
|
||||||
|
LOG_MESSAGE(QString("Invalid configuration file: %1").arg(fileInfo.fileName()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject obj = doc.object();
|
||||||
|
AIConfiguration config;
|
||||||
|
config.id = obj["id"].toString();
|
||||||
|
config.name = obj["name"].toString();
|
||||||
|
config.provider = obj["provider"].toString();
|
||||||
|
config.model = obj["model"].toString();
|
||||||
|
config.templateName = obj["template"].toString();
|
||||||
|
config.url = obj["url"].toString();
|
||||||
|
config.endpointMode = obj["endpointMode"].toString();
|
||||||
|
config.customEndpoint = obj["customEndpoint"].toString();
|
||||||
|
config.type = type;
|
||||||
|
config.formatVersion = obj.value("formatVersion").toInt(1);
|
||||||
|
|
||||||
|
if (config.id.isEmpty() || config.name.isEmpty()) {
|
||||||
|
LOG_MESSAGE(QString("Invalid configuration data in file: %1").arg(fileInfo.fileName()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
configs->append(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit configurationsChanged(type);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigurationManager::saveConfiguration(const AIConfiguration &config)
|
||||||
|
{
|
||||||
|
if (!ensureDirectoryExists(config.type)) {
|
||||||
|
LOG_MESSAGE("Failed to create configuration directory");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject obj;
|
||||||
|
obj["formatVersion"] = config.formatVersion;
|
||||||
|
obj["id"] = config.id;
|
||||||
|
obj["name"] = config.name;
|
||||||
|
obj["provider"] = config.provider;
|
||||||
|
obj["model"] = config.model;
|
||||||
|
obj["template"] = config.templateName;
|
||||||
|
obj["url"] = config.url;
|
||||||
|
obj["endpointMode"] = config.endpointMode;
|
||||||
|
obj["customEndpoint"] = config.customEndpoint;
|
||||||
|
|
||||||
|
QString sanitizedName = config.name;
|
||||||
|
sanitizedName.replace(" ", "_");
|
||||||
|
sanitizedName.replace(QRegularExpression("[^a-zA-Z0-9_-]"), "");
|
||||||
|
|
||||||
|
QString fileName = QString("%1/%2_%3.json")
|
||||||
|
.arg(getConfigurationDirectory(config.type), sanitizedName, config.id);
|
||||||
|
|
||||||
|
QFile file(fileName);
|
||||||
|
if (!file.open(QIODevice::WriteOnly)) {
|
||||||
|
LOG_MESSAGE(QString("Failed to create configuration file: %1").arg(fileName));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument doc(obj);
|
||||||
|
file.write(doc.toJson(QJsonDocument::Indented));
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
loadConfigurations(config.type);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigurationManager::deleteConfiguration(const QString &id, ConfigurationType type)
|
||||||
|
{
|
||||||
|
QDir dir(getConfigurationDirectory(type));
|
||||||
|
QStringList filters;
|
||||||
|
filters << QString("*_%1.json").arg(id);
|
||||||
|
QFileInfoList files = dir.entryInfoList(filters, QDir::Files);
|
||||||
|
|
||||||
|
if (files.isEmpty()) {
|
||||||
|
LOG_MESSAGE(QString("Configuration file not found for id: %1").arg(id));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const QFileInfo &fileInfo : files) {
|
||||||
|
QFile file(fileInfo.absoluteFilePath());
|
||||||
|
if (!file.remove()) {
|
||||||
|
LOG_MESSAGE(QString("Failed to delete configuration file: %1")
|
||||||
|
.arg(fileInfo.absoluteFilePath()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadConfigurations(type);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<AIConfiguration> ConfigurationManager::configurations(ConfigurationType type) const
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case ConfigurationType::CodeCompletion:
|
||||||
|
return m_ccConfigurations;
|
||||||
|
case ConfigurationType::Chat:
|
||||||
|
return m_caConfigurations;
|
||||||
|
case ConfigurationType::QuickRefactor:
|
||||||
|
return m_qrConfigurations;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AIConfiguration ConfigurationManager::getConfigurationById(const QString &id,
|
||||||
|
ConfigurationType type) const
|
||||||
|
{
|
||||||
|
const QVector<AIConfiguration> &configs = configurations(type);
|
||||||
|
for (const AIConfiguration &config : configs) {
|
||||||
|
if (config.id == id) {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AIConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Settings
|
||||||
|
|
||||||
78
settings/ConfigurationManager.hpp
Normal file
78
settings/ConfigurationManager.hpp
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024-2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
namespace QodeAssist::Settings {
|
||||||
|
|
||||||
|
enum class ConfigurationType { CodeCompletion, Chat, QuickRefactor };
|
||||||
|
|
||||||
|
inline constexpr int CONFIGURATION_FORMAT_VERSION = 1;
|
||||||
|
|
||||||
|
struct AIConfiguration
|
||||||
|
{
|
||||||
|
QString id;
|
||||||
|
QString name;
|
||||||
|
QString provider;
|
||||||
|
QString model;
|
||||||
|
QString templateName;
|
||||||
|
QString url;
|
||||||
|
QString endpointMode;
|
||||||
|
QString customEndpoint;
|
||||||
|
ConfigurationType type;
|
||||||
|
int formatVersion = CONFIGURATION_FORMAT_VERSION;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConfigurationManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
static ConfigurationManager &instance();
|
||||||
|
|
||||||
|
bool loadConfigurations(ConfigurationType type);
|
||||||
|
bool saveConfiguration(const AIConfiguration &config);
|
||||||
|
bool deleteConfiguration(const QString &id, ConfigurationType type);
|
||||||
|
|
||||||
|
QVector<AIConfiguration> configurations(ConfigurationType type) const;
|
||||||
|
AIConfiguration getConfigurationById(const QString &id, ConfigurationType type) const;
|
||||||
|
|
||||||
|
QString getConfigurationDirectory(ConfigurationType type) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void configurationsChanged(ConfigurationType type);
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit ConfigurationManager(QObject *parent = nullptr);
|
||||||
|
~ConfigurationManager() override = default;
|
||||||
|
|
||||||
|
bool ensureDirectoryExists(ConfigurationType type) const;
|
||||||
|
QString configurationTypeToString(ConfigurationType type) const;
|
||||||
|
|
||||||
|
QVector<AIConfiguration> m_ccConfigurations;
|
||||||
|
QVector<AIConfiguration> m_caConfigurations;
|
||||||
|
QVector<AIConfiguration> m_qrConfigurations;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Settings
|
||||||
|
|
||||||
@ -23,11 +23,16 @@
|
|||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <utils/layoutbuilder.h>
|
#include <utils/layoutbuilder.h>
|
||||||
#include <utils/utilsicons.h>
|
#include <utils/utilsicons.h>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QDir>
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QPushButton>
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QUuid>
|
||||||
#include <QtWidgets/qboxlayout.h>
|
#include <QtWidgets/qboxlayout.h>
|
||||||
#include <QtWidgets/qcompleter.h>
|
#include <QtWidgets/qcompleter.h>
|
||||||
#include <QtWidgets/qgroupbox.h>
|
#include <QtWidgets/qgroupbox.h>
|
||||||
@ -35,6 +40,7 @@
|
|||||||
#include <QtWidgets/qstackedwidget.h>
|
#include <QtWidgets/qstackedwidget.h>
|
||||||
|
|
||||||
#include "../Version.hpp"
|
#include "../Version.hpp"
|
||||||
|
#include "ConfigurationManager.hpp"
|
||||||
#include "Logger.hpp"
|
#include "Logger.hpp"
|
||||||
#include "SettingsConstants.hpp"
|
#include "SettingsConstants.hpp"
|
||||||
#include "SettingsDialog.hpp"
|
#include "SettingsDialog.hpp"
|
||||||
@ -119,6 +125,12 @@ GeneralSettings::GeneralSettings()
|
|||||||
ccTemplateDescription.setReadOnly(true);
|
ccTemplateDescription.setReadOnly(true);
|
||||||
ccTemplateDescription.setDefaultValue("");
|
ccTemplateDescription.setDefaultValue("");
|
||||||
|
|
||||||
|
ccSaveConfig.m_buttonText = TrConstants::SAVE_CONFIG;
|
||||||
|
ccLoadConfig.m_buttonText = TrConstants::LOAD_CONFIG;
|
||||||
|
ccOpenConfigFolder.m_buttonText = TrConstants::OPEN_CONFIG_FOLDER;
|
||||||
|
ccOpenConfigFolder.m_icon = Utils::Icons::OPENFILE.icon();
|
||||||
|
ccOpenConfigFolder.m_isCompact = true;
|
||||||
|
|
||||||
// preset1
|
// preset1
|
||||||
specifyPreset1.setSettingsKey(Constants::CC_SPECIFY_PRESET1);
|
specifyPreset1.setSettingsKey(Constants::CC_SPECIFY_PRESET1);
|
||||||
specifyPreset1.setLabelText(TrConstants::ADD_NEW_PRESET);
|
specifyPreset1.setLabelText(TrConstants::ADD_NEW_PRESET);
|
||||||
@ -204,6 +216,12 @@ GeneralSettings::GeneralSettings()
|
|||||||
caTemplateDescription.setReadOnly(true);
|
caTemplateDescription.setReadOnly(true);
|
||||||
caTemplateDescription.setDefaultValue("");
|
caTemplateDescription.setDefaultValue("");
|
||||||
|
|
||||||
|
caSaveConfig.m_buttonText = TrConstants::SAVE_CONFIG;
|
||||||
|
caLoadConfig.m_buttonText = TrConstants::LOAD_CONFIG;
|
||||||
|
caOpenConfigFolder.m_buttonText = TrConstants::OPEN_CONFIG_FOLDER;
|
||||||
|
caOpenConfigFolder.m_icon = Utils::Icons::OPENFILE.icon();
|
||||||
|
caOpenConfigFolder.m_isCompact = true;
|
||||||
|
|
||||||
// quick refactor settings
|
// quick refactor settings
|
||||||
initStringAspect(qrProvider, Constants::QR_PROVIDER, TrConstants::PROVIDER, "Ollama");
|
initStringAspect(qrProvider, Constants::QR_PROVIDER, TrConstants::PROVIDER, "Ollama");
|
||||||
qrProvider.setReadOnly(true);
|
qrProvider.setReadOnly(true);
|
||||||
@ -242,6 +260,12 @@ GeneralSettings::GeneralSettings()
|
|||||||
qrTemplateDescription.setReadOnly(true);
|
qrTemplateDescription.setReadOnly(true);
|
||||||
qrTemplateDescription.setDefaultValue("");
|
qrTemplateDescription.setDefaultValue("");
|
||||||
|
|
||||||
|
qrSaveConfig.m_buttonText = TrConstants::SAVE_CONFIG;
|
||||||
|
qrLoadConfig.m_buttonText = TrConstants::LOAD_CONFIG;
|
||||||
|
qrOpenConfigFolder.m_buttonText = TrConstants::OPEN_CONFIG_FOLDER;
|
||||||
|
qrOpenConfigFolder.m_icon = Utils::Icons::OPENFILE.icon();
|
||||||
|
qrOpenConfigFolder.m_isCompact = true;
|
||||||
|
|
||||||
ccShowTemplateInfo.m_icon = Utils::Icons::INFO.icon();
|
ccShowTemplateInfo.m_icon = Utils::Icons::INFO.icon();
|
||||||
ccShowTemplateInfo.m_tooltip = Tr::tr("Show template information");
|
ccShowTemplateInfo.m_tooltip = Tr::tr("Show template information");
|
||||||
ccShowTemplateInfo.m_isCompact = true;
|
ccShowTemplateInfo.m_isCompact = true;
|
||||||
@ -300,15 +324,18 @@ GeneralSettings::GeneralSettings()
|
|||||||
auto ccGroup = Group{
|
auto ccGroup = Group{
|
||||||
title(TrConstants::CODE_COMPLETION),
|
title(TrConstants::CODE_COMPLETION),
|
||||||
Column{
|
Column{
|
||||||
|
Row{ccSaveConfig, ccLoadConfig, ccOpenConfigFolder, Stretch{1}},
|
||||||
ccGrid,
|
ccGrid,
|
||||||
Row{specifyPreset1, preset1Language, Stretch{1}},
|
Row{specifyPreset1, preset1Language, Stretch{1}},
|
||||||
ccPreset1Grid}};
|
ccPreset1Grid}};
|
||||||
|
|
||||||
auto caGroup = Group{
|
auto caGroup = Group{
|
||||||
title(TrConstants::CHAT_ASSISTANT), Column{caGrid}};
|
title(TrConstants::CHAT_ASSISTANT),
|
||||||
|
Column{Row{caSaveConfig, caLoadConfig, caOpenConfigFolder, Stretch{1}}, caGrid}};
|
||||||
|
|
||||||
auto qrGroup = Group{
|
auto qrGroup = Group{
|
||||||
title(TrConstants::QUICK_REFACTOR), Column{qrGrid}};
|
title(TrConstants::QUICK_REFACTOR),
|
||||||
|
Column{Row{qrSaveConfig, qrLoadConfig, qrOpenConfigFolder, Stretch{1}}, qrGrid}};
|
||||||
|
|
||||||
auto rootLayout = Column{
|
auto rootLayout = Column{
|
||||||
Row{enableQodeAssist, Stretch{1}, Row{checkUpdate, resetToDefaults}},
|
Row{enableQodeAssist, Stretch{1}, Row{checkUpdate, resetToDefaults}},
|
||||||
@ -417,7 +444,7 @@ void GeneralSettings::showModelsNotSupportedDialog(Utils::StringAspect &aspect)
|
|||||||
|
|
||||||
QString key = QString("CompleterHistory/")
|
QString key = QString("CompleterHistory/")
|
||||||
.append(
|
.append(
|
||||||
(&aspect == &ccModel) ? Constants::CC_MODEL_HISTORY
|
(&aspect == &ccModel) ? Constants::CC_MODEL_HISTORY
|
||||||
: (&aspect == &caModel) ? Constants::CA_MODEL_HISTORY
|
: (&aspect == &caModel) ? Constants::CA_MODEL_HISTORY
|
||||||
: Constants::QR_MODEL_HISTORY);
|
: Constants::QR_MODEL_HISTORY);
|
||||||
#if QODEASSIST_QT_CREATOR_VERSION >= QT_VERSION_CHECK(18, 0, 0)
|
#if QODEASSIST_QT_CREATOR_VERSION >= QT_VERSION_CHECK(18, 0, 0)
|
||||||
@ -494,7 +521,8 @@ void GeneralSettings::showUrlSelectionDialog(
|
|||||||
dialog.exec();
|
dialog.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeneralSettings::showTemplateInfoDialog(const Utils::StringAspect &descriptionAspect, const QString &templateName)
|
void GeneralSettings::showTemplateInfoDialog(
|
||||||
|
const Utils::StringAspect &descriptionAspect, const QString &templateName)
|
||||||
{
|
{
|
||||||
SettingsDialog dialog(Tr::tr("Template Information"));
|
SettingsDialog dialog(Tr::tr("Template Information"));
|
||||||
dialog.addLabel(QString("<b>%1:</b> %2").arg(Tr::tr("Template"), templateName));
|
dialog.addLabel(QString("<b>%1:</b> %2").arg(Tr::tr("Template"), templateName));
|
||||||
@ -575,6 +603,48 @@ void GeneralSettings::setupConnections()
|
|||||||
connect(&qrShowTemplateInfo, &ButtonAspect::clicked, this, [this]() {
|
connect(&qrShowTemplateInfo, &ButtonAspect::clicked, this, [this]() {
|
||||||
showTemplateInfoDialog(qrTemplateDescription, qrTemplate.value());
|
showTemplateInfoDialog(qrTemplateDescription, qrTemplate.value());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(&ccSaveConfig, &ButtonAspect::clicked, this, [this]() { onSaveConfiguration("cc"); });
|
||||||
|
connect(&ccLoadConfig, &ButtonAspect::clicked, this, [this]() { onLoadConfiguration("cc"); });
|
||||||
|
|
||||||
|
connect(&caSaveConfig, &ButtonAspect::clicked, this, [this]() { onSaveConfiguration("ca"); });
|
||||||
|
connect(&caLoadConfig, &ButtonAspect::clicked, this, [this]() { onLoadConfiguration("ca"); });
|
||||||
|
|
||||||
|
connect(&qrSaveConfig, &ButtonAspect::clicked, this, [this]() { onSaveConfiguration("qr"); });
|
||||||
|
connect(&qrLoadConfig, &ButtonAspect::clicked, this, [this]() { onLoadConfiguration("qr"); });
|
||||||
|
|
||||||
|
connect(&ccOpenConfigFolder, &ButtonAspect::clicked, this, [this]() {
|
||||||
|
auto &manager = ConfigurationManager::instance();
|
||||||
|
QString path = manager.getConfigurationDirectory(ConfigurationType::CodeCompletion);
|
||||||
|
QDir dir(path);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
dir.mkpath(".");
|
||||||
|
}
|
||||||
|
QUrl url = QUrl::fromLocalFile(dir.absolutePath());
|
||||||
|
QDesktopServices::openUrl(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&caOpenConfigFolder, &ButtonAspect::clicked, this, [this]() {
|
||||||
|
auto &manager = ConfigurationManager::instance();
|
||||||
|
QString path = manager.getConfigurationDirectory(ConfigurationType::Chat);
|
||||||
|
QDir dir(path);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
dir.mkpath(".");
|
||||||
|
}
|
||||||
|
QUrl url = QUrl::fromLocalFile(dir.absolutePath());
|
||||||
|
QDesktopServices::openUrl(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&qrOpenConfigFolder, &ButtonAspect::clicked, this, [this]() {
|
||||||
|
auto &manager = ConfigurationManager::instance();
|
||||||
|
QString path = manager.getConfigurationDirectory(ConfigurationType::QuickRefactor);
|
||||||
|
QDir dir(path);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
dir.mkpath(".");
|
||||||
|
}
|
||||||
|
QUrl url = QUrl::fromLocalFile(dir.absolutePath());
|
||||||
|
QDesktopServices::openUrl(url);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeneralSettings::resetPageToDefaults()
|
void GeneralSettings::resetPageToDefaults()
|
||||||
@ -620,6 +690,176 @@ void GeneralSettings::resetPageToDefaults()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GeneralSettings::onSaveConfiguration(const QString &prefix)
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
QString configName = QInputDialog::getText(
|
||||||
|
Core::ICore::dialogParent(),
|
||||||
|
TrConstants::SAVE_CONFIGURATION,
|
||||||
|
TrConstants::CONFIGURATION_NAME,
|
||||||
|
QLineEdit::Normal,
|
||||||
|
QString(),
|
||||||
|
&ok);
|
||||||
|
|
||||||
|
if (!ok || configName.trimmed().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AIConfiguration config;
|
||||||
|
config.id = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
||||||
|
config.name = configName.trimmed();
|
||||||
|
|
||||||
|
if (prefix == "cc") {
|
||||||
|
config.provider = ccProvider.value();
|
||||||
|
config.model = ccModel.value();
|
||||||
|
config.templateName = ccTemplate.value();
|
||||||
|
config.url = ccUrl.value();
|
||||||
|
config.endpointMode = ccEndpointMode.stringValue();
|
||||||
|
config.customEndpoint = ccCustomEndpoint.value();
|
||||||
|
config.type = ConfigurationType::CodeCompletion;
|
||||||
|
} else if (prefix == "ca") {
|
||||||
|
config.provider = caProvider.value();
|
||||||
|
config.model = caModel.value();
|
||||||
|
config.templateName = caTemplate.value();
|
||||||
|
config.url = caUrl.value();
|
||||||
|
config.endpointMode = caEndpointMode.stringValue();
|
||||||
|
config.customEndpoint = caCustomEndpoint.value();
|
||||||
|
config.type = ConfigurationType::Chat;
|
||||||
|
} else if (prefix == "qr") {
|
||||||
|
config.provider = qrProvider.value();
|
||||||
|
config.model = qrModel.value();
|
||||||
|
config.templateName = qrTemplate.value();
|
||||||
|
config.url = qrUrl.value();
|
||||||
|
config.endpointMode = qrEndpointMode.stringValue();
|
||||||
|
config.customEndpoint = qrCustomEndpoint.value();
|
||||||
|
config.type = ConfigurationType::QuickRefactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &manager = ConfigurationManager::instance();
|
||||||
|
if (manager.saveConfiguration(config)) {
|
||||||
|
QMessageBox::information(
|
||||||
|
Core::ICore::dialogParent(),
|
||||||
|
TrConstants::SAVE_CONFIGURATION,
|
||||||
|
TrConstants::CONFIGURATION_SAVED);
|
||||||
|
} else {
|
||||||
|
QMessageBox::warning(
|
||||||
|
Core::ICore::dialogParent(),
|
||||||
|
TrConstants::SAVE_CONFIGURATION,
|
||||||
|
Tr::tr("Failed to save configuration. Check logs for details."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeneralSettings::onLoadConfiguration(const QString &prefix)
|
||||||
|
{
|
||||||
|
ConfigurationType type;
|
||||||
|
if (prefix == "cc") {
|
||||||
|
type = ConfigurationType::CodeCompletion;
|
||||||
|
} else if (prefix == "ca") {
|
||||||
|
type = ConfigurationType::Chat;
|
||||||
|
} else if (prefix == "qr") {
|
||||||
|
type = ConfigurationType::QuickRefactor;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &manager = ConfigurationManager::instance();
|
||||||
|
manager.loadConfigurations(type);
|
||||||
|
|
||||||
|
QVector<AIConfiguration> configs = manager.configurations(type);
|
||||||
|
if (configs.isEmpty()) {
|
||||||
|
QMessageBox::information(
|
||||||
|
Core::ICore::dialogParent(),
|
||||||
|
TrConstants::LOAD_CONFIGURATION,
|
||||||
|
TrConstants::NO_CONFIGURATIONS_FOUND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsDialog dialog(TrConstants::LOAD_CONFIGURATION);
|
||||||
|
dialog.addLabel(TrConstants::SELECT_CONFIGURATION);
|
||||||
|
dialog.addSpacing();
|
||||||
|
|
||||||
|
QStringList configNames;
|
||||||
|
for (const AIConfiguration &config : configs) {
|
||||||
|
configNames.append(config.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto configList = dialog.addComboBox(configNames, QString());
|
||||||
|
dialog.addSpacing();
|
||||||
|
|
||||||
|
auto *deleteButton = new QPushButton(TrConstants::DELETE_CONFIGURATION);
|
||||||
|
auto *okButton = new QPushButton(TrConstants::OK);
|
||||||
|
auto *cancelButton = new QPushButton(TrConstants::CANCEL);
|
||||||
|
|
||||||
|
connect(deleteButton, &QPushButton::clicked, &dialog, [&]() {
|
||||||
|
int currentIndex = configList->currentIndex();
|
||||||
|
if (currentIndex >= 0 && currentIndex < configs.size()) {
|
||||||
|
QMessageBox::StandardButton reply = QMessageBox::question(
|
||||||
|
&dialog,
|
||||||
|
TrConstants::DELETE_CONFIGURATION,
|
||||||
|
TrConstants::CONFIRM_DELETE_CONFIG,
|
||||||
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
|
if (reply == QMessageBox::Yes) {
|
||||||
|
const AIConfiguration &configToDelete = configs[currentIndex];
|
||||||
|
if (manager.deleteConfiguration(configToDelete.id, type)) {
|
||||||
|
dialog.accept();
|
||||||
|
onLoadConfiguration(prefix);
|
||||||
|
} else {
|
||||||
|
QMessageBox::warning(
|
||||||
|
&dialog,
|
||||||
|
TrConstants::DELETE_CONFIGURATION,
|
||||||
|
Tr::tr("Failed to delete configuration."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(okButton, &QPushButton::clicked, &dialog, [&]() {
|
||||||
|
int currentIndex = configList->currentIndex();
|
||||||
|
if (currentIndex >= 0 && currentIndex < configs.size()) {
|
||||||
|
const AIConfiguration &config = configs[currentIndex];
|
||||||
|
|
||||||
|
if (prefix == "cc") {
|
||||||
|
ccProvider.setValue(config.provider);
|
||||||
|
ccModel.setValue(config.model);
|
||||||
|
ccTemplate.setValue(config.templateName);
|
||||||
|
ccUrl.setValue(config.url);
|
||||||
|
ccEndpointMode.setValue(ccEndpointMode.indexForDisplay(config.endpointMode));
|
||||||
|
ccCustomEndpoint.setValue(config.customEndpoint);
|
||||||
|
} else if (prefix == "ca") {
|
||||||
|
caProvider.setValue(config.provider);
|
||||||
|
caModel.setValue(config.model);
|
||||||
|
caTemplate.setValue(config.templateName);
|
||||||
|
caUrl.setValue(config.url);
|
||||||
|
caEndpointMode.setValue(caEndpointMode.indexForDisplay(config.endpointMode));
|
||||||
|
caCustomEndpoint.setValue(config.customEndpoint);
|
||||||
|
} else if (prefix == "qr") {
|
||||||
|
qrProvider.setValue(config.provider);
|
||||||
|
qrModel.setValue(config.model);
|
||||||
|
qrTemplate.setValue(config.templateName);
|
||||||
|
qrUrl.setValue(config.url);
|
||||||
|
qrEndpointMode.setValue(qrEndpointMode.indexForDisplay(config.endpointMode));
|
||||||
|
qrCustomEndpoint.setValue(config.customEndpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSettings();
|
||||||
|
QMessageBox::information(
|
||||||
|
Core::ICore::dialogParent(),
|
||||||
|
TrConstants::LOAD_CONFIGURATION,
|
||||||
|
TrConstants::CONFIGURATION_LOADED);
|
||||||
|
dialog.accept();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(cancelButton, &QPushButton::clicked, &dialog, &QDialog::reject);
|
||||||
|
|
||||||
|
dialog.buttonLayout()->addWidget(deleteButton);
|
||||||
|
addDialogButtons(dialog.buttonLayout(), okButton, cancelButton);
|
||||||
|
|
||||||
|
configList->setFocus();
|
||||||
|
dialog.exec();
|
||||||
|
}
|
||||||
|
|
||||||
class GeneralSettingsPage : public Core::IOptionsPage
|
class GeneralSettingsPage : public Core::IOptionsPage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@ -66,6 +66,10 @@ public:
|
|||||||
|
|
||||||
Utils::StringAspect ccTemplateDescription{this};
|
Utils::StringAspect ccTemplateDescription{this};
|
||||||
|
|
||||||
|
ButtonAspect ccSaveConfig{this};
|
||||||
|
ButtonAspect ccLoadConfig{this};
|
||||||
|
ButtonAspect ccOpenConfigFolder{this};
|
||||||
|
|
||||||
// TODO create dynamic presets system
|
// TODO create dynamic presets system
|
||||||
// preset1 for code completion settings
|
// preset1 for code completion settings
|
||||||
Utils::BoolAspect specifyPreset1{this};
|
Utils::BoolAspect specifyPreset1{this};
|
||||||
@ -107,6 +111,10 @@ public:
|
|||||||
|
|
||||||
Utils::StringAspect caTemplateDescription{this};
|
Utils::StringAspect caTemplateDescription{this};
|
||||||
|
|
||||||
|
ButtonAspect caSaveConfig{this};
|
||||||
|
ButtonAspect caLoadConfig{this};
|
||||||
|
ButtonAspect caOpenConfigFolder{this};
|
||||||
|
|
||||||
// quick refactor settings
|
// quick refactor settings
|
||||||
Utils::StringAspect qrProvider{this};
|
Utils::StringAspect qrProvider{this};
|
||||||
ButtonAspect qrSelectProvider{this};
|
ButtonAspect qrSelectProvider{this};
|
||||||
@ -128,6 +136,10 @@ public:
|
|||||||
|
|
||||||
Utils::StringAspect qrTemplateDescription{this};
|
Utils::StringAspect qrTemplateDescription{this};
|
||||||
|
|
||||||
|
ButtonAspect qrSaveConfig{this};
|
||||||
|
ButtonAspect qrLoadConfig{this};
|
||||||
|
ButtonAspect qrOpenConfigFolder{this};
|
||||||
|
|
||||||
ButtonAspect ccShowTemplateInfo{this};
|
ButtonAspect ccShowTemplateInfo{this};
|
||||||
ButtonAspect caShowTemplateInfo{this};
|
ButtonAspect caShowTemplateInfo{this};
|
||||||
ButtonAspect qrShowTemplateInfo{this};
|
ButtonAspect qrShowTemplateInfo{this};
|
||||||
@ -148,6 +160,9 @@ public:
|
|||||||
|
|
||||||
void updatePreset1Visiblity(bool state);
|
void updatePreset1Visiblity(bool state);
|
||||||
|
|
||||||
|
void onSaveConfiguration(const QString &prefix);
|
||||||
|
void onLoadConfiguration(const QString &prefix);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupConnections();
|
void setupConnections();
|
||||||
void resetPageToDefaults();
|
void resetPageToDefaults();
|
||||||
|
|||||||
@ -99,6 +99,19 @@ inline const char AUTO_COMPLETION_SETTINGS[]
|
|||||||
inline const char ADD_NEW_PRESET[]
|
inline const char ADD_NEW_PRESET[]
|
||||||
= QT_TRANSLATE_NOOP("QtC::QodeAssist", "Add new preset for language");
|
= QT_TRANSLATE_NOOP("QtC::QodeAssist", "Add new preset for language");
|
||||||
|
|
||||||
|
inline const char SAVE_CONFIG[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Save Config...");
|
||||||
|
inline const char LOAD_CONFIG[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Load Config...");
|
||||||
|
inline const char OPEN_CONFIG_FOLDER[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Open Folder");
|
||||||
|
inline const char SAVE_CONFIGURATION[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Save Configuration");
|
||||||
|
inline const char LOAD_CONFIGURATION[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Load Configuration");
|
||||||
|
inline const char CONFIGURATION_NAME[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Configuration name:");
|
||||||
|
inline const char SELECT_CONFIGURATION[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Select Configuration");
|
||||||
|
inline const char NO_CONFIGURATIONS_FOUND[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "No saved configurations found.");
|
||||||
|
inline const char CONFIGURATION_SAVED[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Configuration saved successfully.");
|
||||||
|
inline const char CONFIGURATION_LOADED[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Configuration loaded successfully.");
|
||||||
|
inline const char DELETE_CONFIGURATION[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Delete");
|
||||||
|
inline const char CONFIRM_DELETE_CONFIG[] = QT_TRANSLATE_NOOP("QtC::QodeAssist", "Are you sure you want to delete this configuration?");
|
||||||
|
|
||||||
} // namespace TrConstants
|
} // namespace TrConstants
|
||||||
|
|
||||||
struct Tr
|
struct Tr
|
||||||
|
|||||||
Reference in New Issue
Block a user