refactor: Change top bar layout and tools/thinking settings

This commit is contained in:
Petr Mironychev
2025-11-27 00:39:37 +01:00
parent b18ef4c400
commit 85a7bba90e
16 changed files with 274 additions and 241 deletions

View File

@ -49,6 +49,8 @@ qt_add_qml_module(QodeAssistChatView
icons/reject-changes-button.svg
icons/thinking-icon-on.svg
icons/thinking-icon-off.svg
icons/tools-icon-on.svg
icons/tools-icon-off.svg
SOURCES
ChatWidget.hpp ChatWidget.cpp

View File

@ -37,14 +37,14 @@
#include "ChatSerializer.hpp"
#include "ConfigurationManager.hpp"
#include "GeneralSettings.hpp"
#include "ToolsSettings.hpp"
#include "Logger.hpp"
#include "ProjectSettings.hpp"
#include "ProvidersManager.hpp"
#include "ToolsSettings.hpp"
#include "context/ChangesManager.h"
#include "context/ContextManager.hpp"
#include "context/TokenUtils.hpp"
#include "llmcore/RulesLoader.hpp"
#include "ProvidersManager.hpp"
namespace QodeAssist::Chat {
@ -69,15 +69,15 @@ ChatRootView::ChatRootView(QQuickItem *parent)
connect(&settings.caProvider, &Utils::BaseAspect::changed, this, [this]() {
auto &settings = Settings::generalSettings();
m_currentConfiguration = QString("%1 - %2").arg(settings.caProvider.value(),
settings.caModel.value());
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());
m_currentConfiguration
= QString("%1 - %2").arg(settings.caProvider.value(), settings.caModel.value());
emit currentConfigurationChanged();
});
@ -164,7 +164,8 @@ ChatRootView::ChatRootView(QQuickItem *parent)
connect(m_clientInterface, &ClientInterface::requestStarted, this, [this](const QString &requestId) {
if (!m_currentMessageRequestId.isEmpty()) {
LOG_MESSAGE(QString("Clearing previous message requestId: %1").arg(m_currentMessageRequestId));
LOG_MESSAGE(
QString("Clearing previous message requestId: %1").arg(m_currentMessageRequestId));
}
m_currentMessageRequestId = requestId;
@ -212,21 +213,18 @@ ChatRootView::ChatRootView(QQuickItem *parent)
this,
&ChatRootView::refreshRules);
QSettings appSettings;
m_isAgentMode = appSettings.value("QodeAssist/Chat/AgentMode", false).toBool();
m_isThinkingMode = Settings::chatAssistantSettings().enableThinkingMode();
connect(
&Settings::chatAssistantSettings().enableChatTools,
&Utils::BaseAspect::changed,
this,
&ChatRootView::useToolsChanged);
connect(
&Settings::chatAssistantSettings().enableThinkingMode,
&Utils::BaseAspect::changed,
this,
[this]() { setIsThinkingMode(Settings::chatAssistantSettings().enableThinkingMode()); });
&ChatRootView::useThinkingChanged);
connect(
&Settings::toolsSettings().useTools,
&Utils::BaseAspect::changed,
this,
&ChatRootView::toolsSupportEnabledChanged);
connect(
&Settings::generalSettings().caProvider,
&Utils::BaseAspect::changed,
@ -265,7 +263,8 @@ void ChatRootView::sendMessage(const QString &message)
}
}
m_clientInterface->sendMessage(message, m_attachmentFiles, m_linkedFiles, m_isAgentMode);
m_clientInterface
->sendMessage(message, m_attachmentFiles, m_linkedFiles, useTools(), useThinking());
clearAttachmentFiles();
setRequestProgressStatus(true);
}
@ -410,7 +409,8 @@ QString ChatRootView::getSuggestedFileName() const
shortMessage = firstMessage.split('\n').first().simplified().left(30);
if (shortMessage.isEmpty()) {
QVariantList images = m_chatModel->data(m_chatModel->index(0), ChatModel::Images).toList();
QVariantList images
= m_chatModel->data(m_chatModel->index(0), ChatModel::Images).toList();
if (!images.isEmpty()) {
shortMessage = "image_chat";
}
@ -447,7 +447,8 @@ QString ChatRootView::getAutosaveFilePath() const
return QDir(dir).filePath(getSuggestedFileName() + ".json");
}
QString ChatRootView::getAutosaveFilePath(const QString &firstMessage, const QStringList &attachments) const
QString ChatRootView::getAutosaveFilePath(
const QString &firstMessage, const QStringList &attachments) const
{
if (!m_recentFilePath.isEmpty()) {
return m_recentFilePath;
@ -557,7 +558,8 @@ void ChatRootView::addFilesToLinkList(const QStringList &filePaths)
if (!imageFiles.isEmpty()) {
addFilesToAttachList(imageFiles);
m_lastInfoMessage = tr("Images automatically moved to Attach zone (%n file(s))", "", imageFiles.size());
m_lastInfoMessage
= tr("Images automatically moved to Attach zone (%n file(s))", "", imageFiles.size());
emit lastInfoMessageChanged();
}
@ -867,43 +869,26 @@ void ChatRootView::refreshRules()
emit activeRulesCountChanged();
}
bool ChatRootView::isAgentMode() const
bool ChatRootView::useTools() const
{
return m_isAgentMode;
return Settings::chatAssistantSettings().enableChatTools();
}
void ChatRootView::setIsAgentMode(bool newIsAgentMode)
void ChatRootView::setUseTools(bool enabled)
{
if (m_isAgentMode != newIsAgentMode) {
m_isAgentMode = newIsAgentMode;
QSettings settings;
settings.setValue("QodeAssist/Chat/AgentMode", newIsAgentMode);
emit isAgentModeChanged();
}
}
bool ChatRootView::isThinkingMode() const
{
return m_isThinkingMode;
}
void ChatRootView::setIsThinkingMode(bool newIsThinkingMode)
{
if (m_isThinkingMode != newIsThinkingMode) {
m_isThinkingMode = newIsThinkingMode;
Settings::chatAssistantSettings().enableThinkingMode.setValue(newIsThinkingMode);
Settings::chatAssistantSettings().enableChatTools.setValue(enabled);
Settings::chatAssistantSettings().writeSettings();
emit isThinkingModeChanged();
}
}
bool ChatRootView::toolsSupportEnabled() const
bool ChatRootView::useThinking() const
{
return Settings::toolsSettings().useTools();
return Settings::chatAssistantSettings().enableThinkingMode();
}
void ChatRootView::setUseThinking(bool enabled)
{
Settings::chatAssistantSettings().enableThinkingMode.setValue(enabled);
Settings::chatAssistantSettings().writeSettings();
}
void ChatRootView::applyFileEdit(const QString &editId)
@ -986,8 +971,7 @@ void ChatRootView::openFileEditInEditor(const QString &editId)
if (edit.status == Context::ChangesManager::Applied && !edit.newContent.isEmpty()) {
position = currentContent.indexOf(edit.newContent);
}
else if (!edit.oldContent.isEmpty()) {
} else if (!edit.oldContent.isEmpty()) {
position = currentContent.indexOf(edit.oldContent);
}
@ -1028,7 +1012,9 @@ void ChatRootView::updateFileEditStatus(const QString &editId, const QString &st
obj["status_message"] = edit.statusMessage;
}
QString updatedContent = marker + QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
QString updatedContent = marker
+ QString::fromUtf8(
QJsonDocument(obj).toJson(QJsonDocument::Compact));
m_chatModel->updateMessageContent(editId, updatedContent);
LOG_MESSAGE(QString("Updated file edit status to: %1").arg(status));
}
@ -1057,7 +1043,8 @@ void ChatRootView::applyAllFileEditsForCurrentMessage()
m_lastInfoMessage = QString("All file edits applied successfully");
emit lastInfoMessageChanged();
auto edits = Context::ChangesManager::instance().getEditsForRequest(m_currentMessageRequestId);
auto edits = Context::ChangesManager::instance().getEditsForRequest(
m_currentMessageRequestId);
for (const auto &edit : edits) {
if (edit.status == Context::ChangesManager::Applied) {
updateFileEditStatus(edit.editId, "applied");
@ -1069,7 +1056,8 @@ void ChatRootView::applyAllFileEditsForCurrentMessage()
: QString("Failed to apply some file edits:\n%1").arg(errorMsg);
emit lastErrorMessageChanged();
auto edits = Context::ChangesManager::instance().getEditsForRequest(m_currentMessageRequestId);
auto edits = Context::ChangesManager::instance().getEditsForRequest(
m_currentMessageRequestId);
for (const auto &edit : edits) {
if (edit.status == Context::ChangesManager::Applied) {
updateFileEditStatus(edit.editId, "applied");
@ -1098,7 +1086,8 @@ void ChatRootView::undoAllFileEditsForCurrentMessage()
m_lastInfoMessage = QString("All file edits undone successfully");
emit lastInfoMessageChanged();
auto edits = Context::ChangesManager::instance().getEditsForRequest(m_currentMessageRequestId);
auto edits = Context::ChangesManager::instance().getEditsForRequest(
m_currentMessageRequestId);
for (const auto &edit : edits) {
if (edit.status == Context::ChangesManager::Rejected) {
updateFileEditStatus(edit.editId, "rejected");
@ -1110,7 +1099,8 @@ void ChatRootView::undoAllFileEditsForCurrentMessage()
: QString("Failed to undo some file edits:\n%1").arg(errorMsg);
emit lastErrorMessageChanged();
auto edits = Context::ChangesManager::instance().getEditsForRequest(m_currentMessageRequestId);
auto edits = Context::ChangesManager::instance().getEditsForRequest(
m_currentMessageRequestId);
for (const auto &edit : edits) {
if (edit.status == Context::ChangesManager::Rejected) {
updateFileEditStatus(edit.editId, "rejected");
@ -1124,8 +1114,8 @@ void ChatRootView::undoAllFileEditsForCurrentMessage()
void ChatRootView::updateCurrentMessageEditsStats()
{
if (m_currentMessageRequestId.isEmpty()) {
if (m_currentMessageTotalEdits != 0 || m_currentMessageAppliedEdits != 0 ||
m_currentMessagePendingEdits != 0 || m_currentMessageRejectedEdits != 0) {
if (m_currentMessageTotalEdits != 0 || m_currentMessageAppliedEdits != 0
|| m_currentMessagePendingEdits != 0 || m_currentMessageRejectedEdits != 0) {
m_currentMessageTotalEdits = 0;
m_currentMessageAppliedEdits = 0;
m_currentMessagePendingEdits = 0;
@ -1178,8 +1168,12 @@ void ChatRootView::updateCurrentMessageEditsStats()
}
if (changed) {
LOG_MESSAGE(QString("Updated message edits stats: total=%1, applied=%2, pending=%3, rejected=%4")
.arg(total).arg(applied).arg(pending).arg(rejected));
LOG_MESSAGE(
QString("Updated message edits stats: total=%1, applied=%2, pending=%3, rejected=%4")
.arg(total)
.arg(applied)
.arg(pending)
.arg(rejected));
emit currentMessageEditsStatsChanged();
}
}
@ -1268,9 +1262,7 @@ bool ChatRootView::hasImageAttachments(const QStringList &attachments) const
bool ChatRootView::isImageFile(const QString &filePath) const
{
static const QSet<QString> imageExtensions = {
"png", "jpg", "jpeg", "gif", "webp", "bmp", "svg"
};
static const QSet<QString> imageExtensions = {"png", "jpg", "jpeg", "gif", "webp", "bmp", "svg"};
QFileInfo fileInfo(filePath);
return imageExtensions.contains(fileInfo.suffix().toLower());
@ -1318,7 +1310,8 @@ void ChatRootView::applyConfiguration(const QString &configName)
settings.caModel.setValue(config.model);
settings.caTemplate.setValue(config.templateName);
settings.caUrl.setValue(config.url);
settings.caEndpointMode.setValue(settings.caEndpointMode.indexForDisplay(config.endpointMode));
settings.caEndpointMode.setValue(
settings.caEndpointMode.indexForDisplay(config.endpointMode));
settings.caCustomEndpoint.setValue(config.customEndpoint);
settings.writeSettings();

View File

@ -48,10 +48,8 @@ class ChatRootView : public QQuickItem
Q_PROPERTY(QString lastInfoMessage READ lastInfoMessage NOTIFY lastInfoMessageChanged FINAL)
Q_PROPERTY(QVariantList activeRules READ activeRules NOTIFY activeRulesChanged FINAL)
Q_PROPERTY(int activeRulesCount READ activeRulesCount NOTIFY activeRulesCountChanged FINAL)
Q_PROPERTY(bool isAgentMode READ isAgentMode WRITE setIsAgentMode NOTIFY isAgentModeChanged FINAL)
Q_PROPERTY(bool isThinkingMode READ isThinkingMode WRITE setIsThinkingMode NOTIFY isThinkingModeChanged FINAL)
Q_PROPERTY(
bool toolsSupportEnabled READ toolsSupportEnabled NOTIFY toolsSupportEnabledChanged FINAL)
Q_PROPERTY(bool useTools READ useTools WRITE setUseTools NOTIFY useToolsChanged FINAL)
Q_PROPERTY(bool useThinking READ useThinking WRITE setUseThinking NOTIFY useThinkingChanged FINAL)
Q_PROPERTY(int currentMessageTotalEdits READ currentMessageTotalEdits NOTIFY currentMessageEditsStatsChanged FINAL)
Q_PROPERTY(int currentMessageAppliedEdits READ currentMessageAppliedEdits NOTIFY currentMessageEditsStatsChanged FINAL)
@ -127,11 +125,10 @@ public:
Q_INVOKABLE QString getRuleContent(int index);
Q_INVOKABLE void refreshRules();
bool isAgentMode() const;
void setIsAgentMode(bool newIsAgentMode);
bool isThinkingMode() const;
void setIsThinkingMode(bool newIsThinkingMode);
bool toolsSupportEnabled() const;
bool useTools() const;
void setUseTools(bool enabled);
bool useThinking() const;
void setUseThinking(bool enabled);
Q_INVOKABLE void applyFileEdit(const QString &editId);
Q_INVOKABLE void rejectFileEdit(const QString &editId);
@ -184,9 +181,8 @@ signals:
void activeRulesChanged();
void activeRulesCountChanged();
void isAgentModeChanged();
void isThinkingModeChanged();
void toolsSupportEnabledChanged();
void useToolsChanged();
void useThinkingChanged();
void currentMessageEditsStatsChanged();
void isThinkingSupportChanged();
@ -214,8 +210,6 @@ private:
bool m_isRequestInProgress;
QString m_lastErrorMessage;
QVariantList m_activeRules;
bool m_isAgentMode;
bool m_isThinkingMode;
QString m_currentMessageRequestId;
int m_currentMessageTotalEdits{0};

View File

@ -43,12 +43,12 @@
#include "ChatAssistantSettings.hpp"
#include "ChatSerializer.hpp"
#include "GeneralSettings.hpp"
#include "ToolsSettings.hpp"
#include "Logger.hpp"
#include "ProvidersManager.hpp"
#include "RequestConfig.hpp"
#include <context/ChangesManager.h>
#include "ToolsSettings.hpp"
#include <RulesLoader.hpp>
#include <context/ChangesManager.h>
namespace QodeAssist::Chat {
@ -69,7 +69,8 @@ void ClientInterface::sendMessage(
const QString &message,
const QList<QString> &attachments,
const QList<QString> &linkedFiles,
bool useAgentMode)
bool useTools,
bool useThinking)
{
cancelRequest();
m_accumulatedResponses.clear();
@ -99,7 +100,8 @@ void ClientInterface::sendMessage(
QString storedPath;
QFileInfo fileInfo(imagePath);
if (ChatSerializer::saveImageToStorage(m_chatFilePath, fileInfo.fileName(), base64Data, storedPath)) {
if (ChatSerializer::saveImageToStorage(
m_chatFilePath, fileInfo.fileName(), base64Data, storedPath)) {
ChatModel::ImageAttachment imageAttachment;
imageAttachment.fileName = fileInfo.fileName();
imageAttachment.storedPath = storedPath;
@ -110,7 +112,8 @@ void ClientInterface::sendMessage(
}
}
} else if (!imageFiles.isEmpty()) {
LOG_MESSAGE(QString("Warning: Chat file path not set, cannot save %1 image(s)").arg(imageFiles.size()));
LOG_MESSAGE(QString("Warning: Chat file path not set, cannot save %1 image(s)")
.arg(imageFiles.size()));
}
m_chatModel->addMessage(message, ChatModel::ChatRole::User, "", attachFiles, imageAttachments);
@ -135,7 +138,7 @@ void ClientInterface::sendMessage(
LLMCore::ContextData context;
const bool isToolsEnabled = Settings::toolsSettings().useTools() && useAgentMode;
const bool isToolsEnabled = useTools;
if (chatAssistantSettings.useSystemPrompt()) {
QString systemPrompt = chatAssistantSettings.systemPrompt();
@ -144,7 +147,8 @@ void ClientInterface::sendMessage(
if (project) {
systemPrompt += QString("\n# Active project name: %1").arg(project->displayName());
systemPrompt += QString("\n# Active Project path: %1").arg(project->projectDirectory().toUrlishString());
systemPrompt += QString("\n# Active Project path: %1")
.arg(project->projectDirectory().toUrlishString());
if (auto target = project->activeTarget()) {
if (auto buildConfig = target->activeBuildConfiguration()) {
@ -224,8 +228,8 @@ void ClientInterface::sendMessage(
promptTemplate,
context,
LLMCore::RequestType::Chat,
isToolsEnabled,
Settings::chatAssistantSettings().enableThinkingMode());
useTools,
useThinking);
QString requestId = QUuid::createUuid().toString();
QJsonObject request{{"id", requestId}};
@ -397,8 +401,8 @@ void ClientInterface::handleFullResponse(const QString &requestId, const QString
QString finalText = !fullText.isEmpty() ? fullText : m_accumulatedResponses[requestId];
QString applyError;
bool applySuccess = Context::ChangesManager::instance()
.applyPendingEditsForRequest(requestId, &applyError);
bool applySuccess
= Context::ChangesManager::instance().applyPendingEditsForRequest(requestId, &applyError);
if (!applySuccess) {
LOG_MESSAGE(QString("Some edits for request %1 were not auto-applied: %2")
@ -443,9 +447,7 @@ void ClientInterface::handleCleanAccumulatedData(const QString &requestId)
bool ClientInterface::isImageFile(const QString &filePath) const
{
static const QSet<QString> imageExtensions = {
"png", "jpg", "jpeg", "gif", "webp", "bmp", "svg"
};
static const QSet<QString> imageExtensions = {"png", "jpg", "jpeg", "gif", "webp", "bmp", "svg"};
QFileInfo fileInfo(filePath);
QString extension = fileInfo.suffix().toLower();
@ -455,15 +457,14 @@ bool ClientInterface::isImageFile(const QString &filePath) const
QString ClientInterface::getMediaTypeForImage(const QString &filePath) const
{
static const QHash<QString, QString> mediaTypes = {
{"png", "image/png"},
static const QHash<QString, QString> mediaTypes
= {{"png", "image/png"},
{"jpg", "image/jpeg"},
{"jpeg", "image/jpeg"},
{"gif", "image/gif"},
{"webp", "image/webp"},
{"bmp", "image/bmp"},
{"svg", "image/svg+xml"}
};
{"svg", "image/svg+xml"}};
QFileInfo fileInfo(filePath);
QString extension = fileInfo.suffix().toLower();
@ -491,12 +492,14 @@ QString ClientInterface::encodeImageToBase64(const QString &filePath) const
return imageData.toBase64();
}
QVector<LLMCore::ImageAttachment> ClientInterface::loadImagesFromStorage(const QList<ChatModel::ImageAttachment> &storedImages) const
QVector<LLMCore::ImageAttachment> ClientInterface::loadImagesFromStorage(
const QList<ChatModel::ImageAttachment> &storedImages) const
{
QVector<LLMCore::ImageAttachment> apiImages;
for (const auto &storedImage : storedImages) {
QString base64Data = ChatSerializer::loadImageFromStorage(m_chatFilePath, storedImage.storedPath);
QString base64Data
= ChatSerializer::loadImageFromStorage(m_chatFilePath, storedImage.storedPath);
if (base64Data.isEmpty()) {
LOG_MESSAGE(QString("Warning: Failed to load image: %1").arg(storedImage.storedPath));
continue;

View File

@ -43,7 +43,8 @@ public:
const QString &message,
const QList<QString> &attachments = {},
const QList<QString> &linkedFiles = {},
bool useAgentMode = false);
bool useTools = false,
bool useThinking = false);
void clearMessages();
void cancelRequest();

View File

@ -1,4 +1,4 @@
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.4445 9.32233C17.7036 7.28556 21.8559 7.75441 25.8713 9.68854C27.428 9.4057 30.1744 8.91006 31.6477 9.47565C34.5351 10.5309 36.6339 12.7385 37.0285 14.9805C37.81 15.3756 38.4502 15.9932 38.8635 16.751C39.7282 18.3354 39.8498 19.9232 39.2678 21.2061C39.8159 22.277 39.9974 23.4913 39.7844 24.67C39.663 25.4561 39.3556 26.2047 38.8869 26.8555C38.4183 27.5062 37.8013 28.0419 37.0842 28.42C36.8857 28.5274 34.5887 28.6167 34.3713 28.6885C34.6443 32.2168 30.9868 33.5005 27.8889 32.6602L29.0403 36.586L26.0803 36.6885L23.8713 31.6885L21.8713 29.6885C20.125 30.1697 17.0919 30.168 15.76 28.0831C15.639 27.8916 15.5299 27.693 15.4319 27.4893C15.0931 27.5567 14.7474 27.5909 14.4016 27.5919C13.415 27.5918 11.771 27.3037 10.9358 26.7393C10.2736 26.3112 9.74862 25.7095 9.42014 25.004C7.64097 25.2413 6.13134 24.8334 5.14474 23.8262C3.8951 22.5721 3.72021 18.9738 4.37131 16.751C5.22965 13.7841 7.6818 12.9427 12.8713 11.6885C13.3214 11.1426 13.8387 9.69851 14.4445 9.32233ZM21.2551 15.0001L20.9358 16.1114L19.8723 16.4444L19.3401 15.5557L18.4895 16.3331L19.0217 17.2217L18.383 18.2217L17.2131 18.0001L17.0002 18.8887L18.0637 19.4444V20.5557L17.0002 21.1114L17.2131 22.0001L18.383 21.7774L19.0217 22.7774L18.4895 23.6671L19.3401 24.4444L19.8723 23.5557L20.9358 23.8887L21.2551 25.0001H22.7444L23.0637 23.8887L24.1272 23.5557L24.6594 24.4444L25.511 23.6671L24.9787 22.7774L25.6174 21.7774L26.7873 22.0001L27.0002 21.1114L25.9358 20.5557V19.4444L27.0002 18.8887L26.7873 18.0001L25.6174 18.2217L24.9787 17.2217L25.6174 16.4444L24.6594 15.5557L24.1272 16.4444L23.0637 16.1114L22.7444 15.0001H21.2551Z" fill="black"/>
<path d="M6 35L38 6" stroke="black" stroke-width="4" stroke-linecap="round"/>
<path d="M14.4445 9.32233C17.7036 7.28556 21.8559 7.75441 25.8713 9.68854C27.428 9.4057 30.1744 8.91006 31.6477 9.47565C34.5351 10.5309 36.6339 12.7385 37.0285 14.9805C37.81 15.3756 38.4502 15.9932 38.8635 16.751C39.7282 18.3354 39.8498 19.9232 39.2678 21.2061C39.8159 22.277 39.9974 23.4913 39.7844 24.67C39.663 25.4561 39.3556 26.2047 38.8869 26.8555C38.4183 27.5062 37.8013 28.0419 37.0842 28.42C36.8857 28.5274 34.5887 28.6167 34.3713 28.6885C34.6443 32.2168 30.9868 33.5005 27.8889 32.6602L29.0403 36.586L26.0803 36.6885L23.8713 31.6885L21.8713 29.6885C20.125 30.1697 17.0919 30.168 15.76 28.0831C15.639 27.8916 15.5299 27.693 15.4319 27.4893C15.0931 27.5567 14.7474 27.5909 14.4016 27.5919C13.415 27.5918 11.771 27.3037 10.9358 26.7393C10.2736 26.3112 9.74862 25.7095 9.42014 25.004C7.64097 25.2413 6.13134 24.8334 5.14474 23.8262C3.8951 22.5721 3.72021 18.9738 4.37131 16.751C5.22965 13.7841 7.6818 12.9427 12.8713 11.6885C13.3214 11.1426 13.8387 9.69851 14.4445 9.32233ZM21.2551 15.0001L20.9358 16.1114L19.8723 16.4444L19.3401 15.5557L18.4895 16.3331L19.0217 17.2217L18.383 18.2217L17.2131 18.0001L17.0002 18.8887L18.0637 19.4444V20.5557L17.0002 21.1114L17.2131 22.0001L18.383 21.7774L19.0217 22.7774L18.4895 23.6671L19.3401 24.4444L19.8723 23.5557L20.9358 23.8887L21.2551 25.0001H22.7444L23.0637 23.8887L24.1272 23.5557L24.6594 24.4444L25.511 23.6671L24.9787 22.7774L25.6174 21.7774L26.7873 22.0001L27.0002 21.1114L25.9358 20.5557V19.4444L27.0002 18.8887L26.7873 18.0001L25.6174 18.2217L24.9787 17.2217L25.6174 16.4444L24.6594 15.5557L24.1272 16.4444L23.0637 16.1114L22.7444 15.0001H21.2551Z" fill="black" fill-opacity="0.6"/>
<path d="M6 35L38 6" stroke="black" stroke-opacity="0.6" stroke-width="4" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,11 @@
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_82_71)">
<path d="M10.7777 0.0435181C14.2316 -0.253961 17.6161 0.979215 20.0629 3.42633C23.4139 6.77767 24.3012 11.6719 22.7299 15.8433C22.9016 15.988 23.0706 16.1419 23.2377 16.3072L42.2221 34.2203C42.2288 34.2268 42.2353 34.2344 42.2426 34.2408C44.4752 36.4735 44.4752 40.1064 42.2426 42.3394C40.0096 44.5717 36.4035 44.5446 34.1713 42.3121C34.1617 42.3031 34.1528 42.2937 34.144 42.2838L16.3871 23.1519C16.2254 22.9894 16.0746 22.8196 15.933 22.6451C11.7604 24.2194 6.86327 23.3335 3.50919 19.98C1.06298 17.5327 -0.171482 14.1483 0.126373 10.6949C0.160109 10.3034 0.41818 9.96685 0.78653 9.83258C1.15602 9.69759 1.57009 9.78945 1.84805 10.067L7.53555 15.7535L13.8402 13.7574L15.8363 7.4527L10.1488 1.7652C9.87057 1.48716 9.77945 1.07345 9.91348 0.704651C10.0489 0.335072 10.3852 0.0774496 10.7777 0.0435181ZM37.3656 34.7496L37.3129 34.9302L37.1586 35.4673L36.8363 35.5679L36.6195 35.2047L36.4623 34.942L36.2357 35.148L35.725 35.6148L35.5746 35.7525L35.6791 35.9283L35.9184 36.3287L35.7104 36.6548L35.1742 36.5543L34.9408 36.5093L34.8852 36.7418L34.7572 37.275L34.7123 37.4644L34.8842 37.5543L35.3891 37.8179V38.1802L34.8842 38.4449L34.7123 38.5347L34.7572 38.7242L34.8852 39.2574L34.9408 39.4898L35.1742 39.4449L35.7104 39.3433L35.9184 39.6695L35.6791 40.0709L35.5746 40.2466L35.725 40.3843L36.2357 40.8511L36.4623 41.0572L36.6195 40.7945L36.8363 40.4302L37.1586 40.5308L37.3129 41.0689L37.3656 41.2496H38.6352L38.6879 41.0689L38.8412 40.5308L39.1635 40.4302L39.3813 40.7945L39.5385 41.0572L39.765 40.8511L40.2758 40.3843L40.4262 40.2466L40.3217 40.0709L40.0815 39.6695L40.2895 39.3433L40.8266 39.4449L41.06 39.4898L41.1156 39.2574L41.2436 38.7242L41.2885 38.5347L41.1166 38.4449L40.6117 38.1802V37.8179L41.1166 37.5543L41.2885 37.4644L41.2436 37.275L41.1156 36.7418L41.06 36.5093L40.8266 36.5543L40.2895 36.6548L40.0815 36.3287L40.3217 35.9283L40.4262 35.7525L40.2758 35.6148L39.765 35.148L39.5385 34.942L39.3813 35.2047L39.1635 35.5679L38.8412 35.4673L38.6879 34.9302L38.6352 34.7496H37.3656Z" fill="black" fill-opacity="0.6"/>
<path d="M6 36L38 7" stroke="black" stroke-opacity="0.6" stroke-width="4" stroke-linecap="round"/>
</g>
<defs>
<clipPath id="clip0_82_71">
<rect width="44" height="44" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,10 @@
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_82_50)">
<path d="M10.7775 0.0441895C14.2315 -0.253375 17.6159 0.979824 20.0627 3.427C23.4135 6.77842 24.3011 11.6726 22.7297 15.844C22.9013 15.9886 23.0714 16.1416 23.2385 16.3069L42.2219 34.2209C42.2285 34.2274 42.2352 34.2341 42.2424 34.2405C44.475 36.4732 44.475 40.1061 42.2424 42.3391C40.0094 44.5715 36.4033 44.5444 34.1711 42.3118C34.1615 42.3028 34.1525 42.2934 34.1437 42.2834L16.3869 23.1516C16.2251 22.9891 16.0745 22.8193 15.9328 22.6448C11.7602 24.2191 6.86304 23.3333 3.50897 19.9797C1.06276 17.5324 -0.171773 14.148 0.12616 10.6946C0.159908 10.3029 0.418723 9.96644 0.787292 9.83228C1.15667 9.69748 1.56997 9.78926 1.84784 10.0667L7.53534 15.7532L13.84 13.7571L15.8361 7.45239L10.1486 1.76489C9.87052 1.48684 9.78022 1.07306 9.91425 0.704346C10.0498 0.334991 10.3852 0.0781082 10.7775 0.0441895ZM37.3654 34.7502L37.3127 34.9309L37.1584 35.468L36.8361 35.5686L36.6193 35.2053L36.4621 34.9426L36.2355 35.1487L35.7248 35.6155L35.5744 35.7532L35.6789 35.929L35.9182 36.3293L35.7101 36.6555L35.174 36.5549L34.9406 36.51L34.8849 36.7424L34.757 37.2756L34.7121 37.4651L34.884 37.5549L35.3889 37.8186V38.1809L34.884 38.4456L34.7121 38.5354L34.757 38.7249L34.8849 39.2581L34.9406 39.4905L35.174 39.4456L35.7101 39.344L35.9182 39.6702L35.6789 40.0715L35.5744 40.2473L35.7248 40.385L36.2355 40.8518L36.4621 41.0579L36.6193 40.7952L36.8361 40.4309L37.1584 40.5315L37.3127 41.0696L37.3654 41.2502H38.6349L38.6877 41.0696L38.841 40.5315L39.1633 40.4309L39.381 40.7952L39.5383 41.0579L39.7648 40.8518L40.2756 40.385L40.426 40.2473L40.3215 40.0715L40.0812 39.6702L40.2892 39.344L40.8264 39.4456L41.0598 39.4905L41.1154 39.2581L41.2433 38.7249L41.2883 38.5354L41.1164 38.4456L40.6115 38.1809V37.8186L41.1164 37.5549L41.2883 37.4651L41.2433 37.2756L41.1154 36.7424L41.0598 36.51L40.8264 36.5549L40.2892 36.6555L40.0812 36.3293L40.3215 35.929L40.426 35.7532L40.2756 35.6155L39.7648 35.1487L39.5383 34.9426L39.381 35.2053L39.1633 35.5686L38.841 35.468L38.6877 34.9309L38.6349 34.7502H37.3654Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_82_50">
<rect width="44" height="44" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -103,18 +103,17 @@ ChatRootView {
checked: typeof _chatview !== 'undefined' ? _chatview.isPin : false
onCheckedChanged: _chatview.isPin = topBar.pinButton.checked
}
agentModeSwitch {
checked: root.isAgentMode
enabled: root.toolsSupportEnabled
onToggled: {
root.isAgentMode = agentModeSwitch.checked
toolsButton {
checked: root.useTools
onCheckedChanged: {
root.useTools = toolsButton.checked
}
}
thinkingMode {
checked: root.isThinkingMode
checked: root.useThinking
enabled: root.isThinkingSupport
onCheckedChanged: {
root.isThinkingMode = thinkingMode.checked
root.useThinking = thinkingMode.checked
}
}
configSelector {

View File

@ -34,7 +34,7 @@ Rectangle {
property alias openChatHistory: openChatHistoryId
property alias pinButton: pinButtonId
property alias rulesButton: rulesButtonId
property alias agentModeSwitch: agentModeSwitchId
property alias toolsButton: toolsButtonId
property alias thinkingMode: thinkingModeId
property alias activeRulesCount: activeRulesCountId.text
property alias configSelector: configSelectorId
@ -53,7 +53,8 @@ Rectangle {
spacing: 10
Row {
height: agentModeSwitchId.height
id: firstRow
spacing: 10
QoAButton {
@ -75,23 +76,44 @@ Rectangle {
: qsTr("Pin chat window to the top")
}
QoATextSlider {
id: agentModeSwitchId
QoAComboBox {
id: configSelectorId
implicitHeight: 25
model: []
currentIndex: 0
ToolTip.visible: hovered
ToolTip.delay: 250
ToolTip.text: qsTr("Switch AI configuration")
}
QoAButton {
id: toolsButtonId
anchors.verticalCenter: parent.verticalCenter
leftText: "chat"
rightText: "AI Agent"
checkable: true
opacity: enabled ? 1.0 : 0.2
icon {
source: checked ? "qrc:/qt/qml/ChatView/icons/tools-icon-on.svg"
: "qrc:/qt/qml/ChatView/icons/tools-icon-off.svg"
color: palette.window.hslLightness > 0.5 ? "#000000" : "#FFFFFF"
height: 15
width: 15
}
ToolTip.visible: hovered
ToolTip.delay: 250
ToolTip.text: {
if (!agentModeSwitchId.enabled) {
if (!toolsButtonId.enabled) {
return qsTr("Tools are disabled in General Settings")
}
return checked
? qsTr("Agent Mode: AI can use tools to read files, search project, and build code")
: qsTr("Chat Mode: Simple conversation without tool access")
? qsTr("Tools enabled: AI can use tools to read files, search project, and build code")
: qsTr("Tools disabled: Simple conversation without tool access")
}
}
@ -120,7 +142,7 @@ Rectangle {
}
Item {
height: agentModeSwitchId.height
height: firstRow.height
width: recentPathId.width
Text {
@ -144,7 +166,10 @@ Rectangle {
}
RowLayout {
id: secondRow
Layout.preferredWidth: root.width
Layout.preferredHeight: firstRow.height
spacing: 10
@ -239,17 +264,6 @@ Rectangle {
ToolTip.delay: 250
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")
}
}
}
}

View File

@ -29,8 +29,10 @@ Basic.ComboBox {
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
@ -101,6 +103,8 @@ Basic.ComboBox {
implicitHeight: contentHeight
model: control.popup.visible ? control.delegateModel : null
currentIndex: control.highlightedIndex
boundsBehavior: ListView.StopAtBounds
highlightMoveDuration: 0
ScrollBar.vertical: ScrollBar {
policy: ScrollBar.AsNeeded

View File

@ -68,6 +68,10 @@ ChatAssistantSettings::ChatAssistantSettings()
enableChatInNavigationPanel.setLabelText(Tr::tr("Enable chat in navigation panel"));
enableChatInNavigationPanel.setDefaultValue(false);
enableChatTools.setSettingsKey(Constants::CA_ENABLE_CHAT_TOOLS);
enableChatTools.setLabelText(Tr::tr("Enable tools/function calling"));
enableChatTools.setToolTip(Tr::tr("When enabled, AI can use tools to read files, search project, and build code"));
enableChatTools.setDefaultValue(false);
// General Parameters Settings
temperature.setSettingsKey(Constants::CA_TEMPERATURE);
@ -146,10 +150,11 @@ ChatAssistantSettings::ChatAssistantSettings()
// Extended Thinking Settings
enableThinkingMode.setSettingsKey(Constants::CA_ENABLE_THINKING_MODE);
enableThinkingMode.setLabelText(Tr::tr("Enable extended thinking mode (Claude, Ollama).\n Temperature is 1.0 accordingly API requirement for Claude"));
enableThinkingMode.setLabelText(Tr::tr("Enable extended thinking mode."));
enableThinkingMode.setToolTip(
Tr::tr("Enable extended thinking mode for complex reasoning tasks."
"This provides step-by-step reasoning before the final answer. "));
"This provides step-by-step reasoning before the final answer."
"Temperature is 1.0 accordingly API requirement"));
enableThinkingMode.setDefaultValue(false);
thinkingBudgetTokens.setSettingsKey(Constants::CA_THINKING_BUDGET_TOKENS);
@ -283,6 +288,14 @@ ChatAssistantSettings::ChatAssistantSettings()
enableChatInBottomToolBar,
enableChatInNavigationPanel}},
Space{8},
Group{
title(Tr::tr("Tools")),
Column{enableChatTools}},
Space{8},
Group{
title(Tr::tr("Extended Thinking (if provider/model supports)")),
Column{enableThinkingMode, Row{thinkingGrid, Stretch{1}}}},
Space{8},
Group{
title(Tr::tr("General Parameters")),
Row{genGrid, Stretch{1}},
@ -297,9 +310,6 @@ ChatAssistantSettings::ChatAssistantSettings()
systemPrompt,
}},
Group{title(Tr::tr("Ollama Settings")), Column{Row{ollamaGrid, Stretch{1}}}},
Group{
title(Tr::tr("Extended Thinking (Claude, Ollama)")),
Column{enableThinkingMode, Row{thinkingGrid, Stretch{1}}}},
Group{title(Tr::tr("Chat Settings")), Row{chatViewSettingsGrid, Stretch{1}}},
Stretch{1}};
});
@ -343,6 +353,7 @@ void ChatAssistantSettings::resetSettingsToDefaults()
resetAspect(thinkingBudgetTokens);
resetAspect(thinkingMaxTokens);
resetAspect(linkOpenFiles);
resetAspect(enableChatTools);
resetAspect(textFontFamily);
resetAspect(codeFontFamily);
resetAspect(textFontSize);

View File

@ -38,6 +38,7 @@ public:
Utils::BoolAspect autosave{this};
Utils::BoolAspect enableChatInBottomToolBar{this};
Utils::BoolAspect enableChatInNavigationPanel{this};
Utils::BoolAspect enableChatTools{this};
// General Parameters Settings
Utils::DoubleAspect temperature{this};

View File

@ -102,6 +102,7 @@ const char CC_CUSTOM_LANGUAGES[] = "QodeAssist.ccCustomLanguages";
const char CA_ENABLE_CHAT_IN_BOTTOM_TOOLBAR[] = "QodeAssist.caEnableChatInBottomToolbar";
const char CA_ENABLE_CHAT_IN_NAVIGATION_PANEL[] = "QodeAssist.caEnableChatInNavigationPanel";
const char CA_ENABLE_CHAT_TOOLS[] = "QodeAssist.caEnableChatTools";
const char CA_USE_TOOLS[] = "QodeAssist.caUseTools";
const char CA_ALLOW_FILE_SYSTEM_READ[] = "QodeAssist.caAllowFileSystemRead";
const char CA_ALLOW_FILE_SYSTEM_WRITE[] = "QodeAssist.caAllowFileSystemWrite";

View File

@ -42,13 +42,6 @@ ToolsSettings::ToolsSettings()
setDisplayName(Tr::tr("Tools"));
useTools.setSettingsKey(Constants::CA_USE_TOOLS);
useTools.setLabelText(Tr::tr("Enable tools"));
useTools.setToolTip(Tr::tr(
"Enable tool use capabilities for the assistant (OpenAI function calling, Claude tools "
"and etc) if plugin and provider support"));
useTools.setDefaultValue(true);
allowFileSystemRead.setSettingsKey(Constants::CA_ALLOW_FILE_SYSTEM_READ);
allowFileSystemRead.setLabelText(Tr::tr("Allow File System Read Access for tools"));
allowFileSystemRead.setToolTip(
@ -120,8 +113,6 @@ ToolsSettings::ToolsSettings()
Group{
title(Tr::tr("Tool Settings")),
Column{
useTools,
Space{8},
allowFileSystemRead,
allowFileSystemWrite,
allowAccessOutsideProject
@ -158,7 +149,6 @@ void ToolsSettings::resetSettingsToDefaults()
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
resetAspect(useTools);
resetAspect(allowFileSystemRead);
resetAspect(allowFileSystemWrite);
resetAspect(allowAccessOutsideProject);

View File

@ -32,7 +32,6 @@ public:
ButtonAspect resetToDefaults{this};
Utils::BoolAspect useTools{this};
Utils::BoolAspect allowFileSystemRead{this};
Utils::BoolAspect allowFileSystemWrite{this};
Utils::BoolAspect allowAccessOutsideProject{this};