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

@ -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 {
@ -66,18 +66,18 @@ ChatRootView::ChatRootView(QQuickItem *parent)
connect(
&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());
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();
});
@ -97,7 +97,7 @@ ChatRootView::ChatRootView(QQuickItem *parent)
this,
&ChatRootView::updateInputTokensCount);
connect(m_chatModel, &ChatModel::modelReseted, this, [this]() {
connect(m_chatModel, &ChatModel::modelReseted, this, [this]() {
setRecentFilePath(QString{});
m_currentMessageRequestId.clear();
updateCurrentMessageEditsStats();
@ -161,41 +161,42 @@ ChatRootView::ChatRootView(QQuickItem *parent)
m_lastErrorMessage = error;
emit lastErrorMessageChanged();
});
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;
LOG_MESSAGE(QString("New message request started: %1").arg(requestId));
updateCurrentMessageEditsStats();
});
connect(
&Context::ChangesManager::instance(),
&Context::ChangesManager::fileEditAdded,
this,
[this](const QString &) { updateCurrentMessageEditsStats(); });
connect(
&Context::ChangesManager::instance(),
&Context::ChangesManager::fileEditApplied,
this,
[this](const QString &) { updateCurrentMessageEditsStats(); });
connect(
&Context::ChangesManager::instance(),
&Context::ChangesManager::fileEditRejected,
this,
[this](const QString &) { updateCurrentMessageEditsStats(); });
connect(
&Context::ChangesManager::instance(),
&Context::ChangesManager::fileEditUndone,
this,
[this](const QString &) { updateCurrentMessageEditsStats(); });
connect(
&Context::ChangesManager::instance(),
&Context::ChangesManager::fileEditArchived,
@ -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);
}
@ -342,7 +341,7 @@ void ChatRootView::loadHistory(const QString &filePath)
} else {
setRecentFilePath(filePath);
}
m_currentMessageRequestId.clear();
updateInputTokensCount();
updateCurrentMessageEditsStats();
@ -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;
@ -505,7 +506,7 @@ void ChatRootView::addFilesToAttachList(const QStringList &filePaths)
filesAdded = true;
}
}
if (filesAdded) {
emit attachmentFilesChanged();
}
@ -541,26 +542,27 @@ void ChatRootView::addFilesToLinkList(const QStringList &filePaths)
bool filesAdded = false;
QStringList imageFiles;
for (const QString &filePath : filePaths) {
if (isImageFile(filePath)) {
imageFiles.append(filePath);
continue;
}
if (!m_linkedFiles.contains(filePath)) {
m_linkedFiles.append(filePath);
filesAdded = true;
}
}
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();
}
if (filesAdded) {
emit linkedFilesChanged();
}
@ -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();
}
Settings::chatAssistantSettings().enableChatTools.setValue(enabled);
Settings::chatAssistantSettings().writeSettings();
}
bool ChatRootView::isThinkingMode() const
bool ChatRootView::useThinking() const
{
return m_isThinkingMode;
return Settings::chatAssistantSettings().enableThinkingMode();
}
void ChatRootView::setIsThinkingMode(bool newIsThinkingMode)
void ChatRootView::setUseThinking(bool enabled)
{
if (m_isThinkingMode != newIsThinkingMode) {
m_isThinkingMode = newIsThinkingMode;
Settings::chatAssistantSettings().enableThinkingMode.setValue(newIsThinkingMode);
Settings::chatAssistantSettings().writeSettings();
emit isThinkingModeChanged();
}
}
bool ChatRootView::toolsSupportEnabled() const
{
return Settings::toolsSettings().useTools();
Settings::chatAssistantSettings().enableThinkingMode.setValue(enabled);
Settings::chatAssistantSettings().writeSettings();
}
void ChatRootView::applyFileEdit(const QString &editId)
@ -912,13 +897,13 @@ void ChatRootView::applyFileEdit(const QString &editId)
if (Context::ChangesManager::instance().applyFileEdit(editId)) {
m_lastInfoMessage = QString("File edit applied successfully");
emit lastInfoMessageChanged();
updateFileEditStatus(editId, "applied");
} else {
auto edit = Context::ChangesManager::instance().getFileEdit(editId);
m_lastErrorMessage = edit.statusMessage.isEmpty()
? QString("Failed to apply file edit")
: QString("Failed to apply file edit: %1").arg(edit.statusMessage);
m_lastErrorMessage = edit.statusMessage.isEmpty()
? QString("Failed to apply file edit")
: QString("Failed to apply file edit: %1").arg(edit.statusMessage);
emit lastErrorMessageChanged();
}
}
@ -929,13 +914,13 @@ void ChatRootView::rejectFileEdit(const QString &editId)
if (Context::ChangesManager::instance().rejectFileEdit(editId)) {
m_lastInfoMessage = QString("File edit rejected");
emit lastInfoMessageChanged();
updateFileEditStatus(editId, "rejected");
} else {
auto edit = Context::ChangesManager::instance().getFileEdit(editId);
m_lastErrorMessage = edit.statusMessage.isEmpty()
? QString("Failed to reject file edit")
: QString("Failed to reject file edit: %1").arg(edit.statusMessage);
m_lastErrorMessage = edit.statusMessage.isEmpty()
? QString("Failed to reject file edit")
: QString("Failed to reject file edit: %1").arg(edit.statusMessage);
emit lastErrorMessageChanged();
}
}
@ -946,13 +931,13 @@ void ChatRootView::undoFileEdit(const QString &editId)
if (Context::ChangesManager::instance().undoFileEdit(editId)) {
m_lastInfoMessage = QString("File edit undone successfully");
emit lastInfoMessageChanged();
updateFileEditStatus(editId, "rejected");
} else {
auto edit = Context::ChangesManager::instance().getFileEdit(editId);
m_lastErrorMessage = edit.statusMessage.isEmpty()
? QString("Failed to undo file edit")
: QString("Failed to undo file edit: %1").arg(edit.statusMessage);
m_lastErrorMessage = edit.statusMessage.isEmpty()
? QString("Failed to undo file edit")
: QString("Failed to undo file edit: %1").arg(edit.statusMessage);
emit lastErrorMessageChanged();
}
}
@ -960,37 +945,36 @@ void ChatRootView::undoFileEdit(const QString &editId)
void ChatRootView::openFileEditInEditor(const QString &editId)
{
LOG_MESSAGE(QString("Opening file edit in editor: %1").arg(editId));
auto edit = Context::ChangesManager::instance().getFileEdit(editId);
if (edit.editId.isEmpty()) {
m_lastErrorMessage = QString("File edit not found: %1").arg(editId);
emit lastErrorMessageChanged();
return;
}
Utils::FilePath filePath = Utils::FilePath::fromString(edit.filePath);
Core::IEditor *editor = Core::EditorManager::openEditor(filePath);
if (!editor) {
m_lastErrorMessage = QString("Failed to open file in editor: %1").arg(edit.filePath);
emit lastErrorMessageChanged();
return;
}
auto *textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor);
if (textEditor && textEditor->editorWidget()) {
QTextDocument *doc = textEditor->editorWidget()->document();
if (doc) {
QString currentContent = doc->toPlainText();
int position = -1;
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);
}
if (position >= 0) {
QTextCursor cursor(doc);
cursor.setPosition(position);
@ -999,7 +983,7 @@ void ChatRootView::openFileEditInEditor(const QString &editId)
}
}
}
LOG_MESSAGE(QString("Opened file in editor: %1").arg(edit.filePath));
}
@ -1009,33 +993,35 @@ void ChatRootView::updateFileEditStatus(const QString &editId, const QString &st
for (int i = 0; i < messages.size(); ++i) {
if (messages[i].role == Chat::ChatModel::FileEdit && messages[i].id == editId) {
QString content = messages[i].content;
const QString marker = "QODEASSIST_FILE_EDIT:";
int markerPos = content.indexOf(marker);
QString jsonStr = content;
if (markerPos >= 0) {
jsonStr = content.mid(markerPos + marker.length());
}
QJsonDocument doc = QJsonDocument::fromJson(jsonStr.toUtf8());
if (doc.isObject()) {
QJsonObject obj = doc.object();
obj["status"] = status;
auto edit = Context::ChangesManager::instance().getFileEdit(editId);
if (!edit.statusMessage.isEmpty()) {
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));
}
break;
}
}
updateCurrentMessageEditsStats();
}
@ -1046,37 +1032,39 @@ void ChatRootView::applyAllFileEditsForCurrentMessage()
emit lastErrorMessageChanged();
return;
}
LOG_MESSAGE(QString("Applying all file edits for message: %1").arg(m_currentMessageRequestId));
QString errorMsg;
bool success = Context::ChangesManager::instance()
.reapplyAllEditsForRequest(m_currentMessageRequestId, &errorMsg);
if (success) {
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");
}
}
} else {
m_lastErrorMessage = errorMsg.isEmpty()
? QString("Failed to apply some file edits")
: QString("Failed to apply some file edits:\n%1").arg(errorMsg);
m_lastErrorMessage = errorMsg.isEmpty()
? QString("Failed to apply some file edits")
: 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");
}
}
}
updateCurrentMessageEditsStats();
}
@ -1087,45 +1075,47 @@ void ChatRootView::undoAllFileEditsForCurrentMessage()
emit lastErrorMessageChanged();
return;
}
LOG_MESSAGE(QString("Undoing all file edits for message: %1").arg(m_currentMessageRequestId));
QString errorMsg;
bool success = Context::ChangesManager::instance()
.undoAllEditsForRequest(m_currentMessageRequestId, &errorMsg);
if (success) {
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");
}
}
} else {
m_lastErrorMessage = errorMsg.isEmpty()
? QString("Failed to undo some file edits")
: QString("Failed to undo some file edits:\n%1").arg(errorMsg);
m_lastErrorMessage = errorMsg.isEmpty()
? QString("Failed to undo some file edits")
: 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");
}
}
}
updateCurrentMessageEditsStats();
}
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;
@ -1134,14 +1124,14 @@ void ChatRootView::updateCurrentMessageEditsStats()
}
return;
}
auto edits = Context::ChangesManager::instance().getEditsForRequest(m_currentMessageRequestId);
int total = edits.size();
int applied = 0;
int pending = 0;
int rejected = 0;
for (const auto &edit : edits) {
switch (edit.status) {
case Context::ChangesManager::Applied:
@ -1158,7 +1148,7 @@ void ChatRootView::updateCurrentMessageEditsStats()
break;
}
}
bool changed = false;
if (m_currentMessageTotalEdits != total) {
m_currentMessageTotalEdits = total;
@ -1176,10 +1166,14 @@ void ChatRootView::updateCurrentMessageEditsStats()
m_currentMessageRejectedEdits = rejected;
changed = true;
}
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();