fix: Remove image dublicate (#271)

This commit is contained in:
Petr Mironychev
2025-11-20 23:25:00 +01:00
committed by GitHub
parent 24565dc81f
commit a3527e1442
8 changed files with 70 additions and 51 deletions

View File

@ -20,8 +20,11 @@
#include "ChatModel.hpp" #include "ChatModel.hpp"
#include <utils/aspects.h> #include <utils/aspects.h>
#include <QDateTime> #include <QDateTime>
#include <QDir>
#include <QFileInfo>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QUrl>
#include <QtQml> #include <QtQml>
#include "ChatAssistantSettings.hpp" #include "ChatAssistantSettings.hpp"
@ -91,6 +94,19 @@ QVariant ChatModel::data(const QModelIndex &index, int role) const
imageMap["fileName"] = image.fileName; imageMap["fileName"] = image.fileName;
imageMap["storedPath"] = image.storedPath; imageMap["storedPath"] = image.storedPath;
imageMap["mediaType"] = image.mediaType; imageMap["mediaType"] = image.mediaType;
// Generate proper file URL for cross-platform compatibility
if (!m_chatFilePath.isEmpty()) {
QFileInfo fileInfo(m_chatFilePath);
QString baseName = fileInfo.completeBaseName();
QString dirPath = fileInfo.absolutePath();
QString imagesFolder = QDir(dirPath).filePath(baseName + "_images");
QString fullPath = QDir(imagesFolder).filePath(image.storedPath);
imageMap["imageUrl"] = QUrl::fromLocalFile(fullPath).toString();
} else {
imageMap["imageUrl"] = QString();
}
imagesList.append(imageMap); imagesList.append(imageMap);
} }
return imagesList; return imagesList;
@ -549,4 +565,14 @@ void ChatModel::updateFileEditStatus(const QString &editId, const QString &statu
} }
} }
void ChatModel::setChatFilePath(const QString &filePath)
{
m_chatFilePath = filePath;
}
QString ChatModel::chatFilePath() const
{
return m_chatFilePath;
}
} // namespace QodeAssist::Chat } // namespace QodeAssist::Chat

View File

@ -101,6 +101,9 @@ public:
void setLoadingFromHistory(bool loading); void setLoadingFromHistory(bool loading);
bool isLoadingFromHistory() const; bool isLoadingFromHistory() const;
void setChatFilePath(const QString &filePath);
QString chatFilePath() const;
signals: signals:
void tokensThresholdChanged(); void tokensThresholdChanged();
@ -116,6 +119,7 @@ private:
QVector<Message> m_messages; QVector<Message> m_messages;
bool m_loadingFromHistory = false; bool m_loadingFromHistory = false;
QString m_chatFilePath;
}; };
} // namespace QodeAssist::Chat } // namespace QodeAssist::Chat

View File

@ -601,6 +601,21 @@ void ChatRootView::showAddImageDialog()
} }
} }
QStringList ChatRootView::convertUrlsToLocalPaths(const QVariantList &urls) const
{
QStringList localPaths;
for (const QVariant &urlVariant : urls) {
QUrl url(urlVariant.toString());
if (url.isLocalFile()) {
QString localPath = url.toLocalFile();
if (!localPath.isEmpty()) {
localPaths.append(localPath);
}
}
}
return localPaths;
}
void ChatRootView::calculateMessageTokensCount(const QString &message) void ChatRootView::calculateMessageTokensCount(const QString &message)
{ {
m_messageTokensCount = Context::TokenUtils::estimateTokens(message); m_messageTokensCount = Context::TokenUtils::estimateTokens(message);

View File

@ -88,6 +88,7 @@ public:
Q_INVOKABLE void showLinkFilesDialog(); Q_INVOKABLE void showLinkFilesDialog();
Q_INVOKABLE void addFilesToLinkList(const QStringList &filePaths); Q_INVOKABLE void addFilesToLinkList(const QStringList &filePaths);
Q_INVOKABLE void removeFileFromLinkList(int index); Q_INVOKABLE void removeFileFromLinkList(int index);
Q_INVOKABLE QStringList convertUrlsToLocalPaths(const QVariantList &urls) const;
Q_INVOKABLE void showAddImageDialog(); Q_INVOKABLE void showAddImageDialog();
Q_INVOKABLE bool isImageFile(const QString &filePath) const; Q_INVOKABLE bool isImageFile(const QString &filePath) const;
Q_INVOKABLE void calculateMessageTokensCount(const QString &message); Q_INVOKABLE void calculateMessageTokensCount(const QString &message);

View File

@ -183,22 +183,7 @@ void ClientInterface::sendMessage(
messages.append(apiMessage); messages.append(apiMessage);
} }
if (!imageAttachments.isEmpty() && provider->supportImage() && !messages.isEmpty()) { if (!imageFiles.isEmpty() && !provider->supportImage()) {
for (int i = messages.size() - 1; i >= 0; --i) {
if (messages[i].role == "user") {
auto newImages = loadImagesFromStorage(imageAttachments);
if (!newImages.isEmpty()) {
if (messages[i].images.has_value()) {
messages[i].images.value().append(newImages);
} else {
messages[i].images = newImages;
}
LOG_MESSAGE(QString("Added %1 new image(s) to message").arg(newImages.size()));
}
break;
}
}
} else if (!imageFiles.isEmpty() && !provider->supportImage()) {
LOG_MESSAGE(QString("Provider %1 doesn't support images, %2 ignored") LOG_MESSAGE(QString("Provider %1 doesn't support images, %2 ignored")
.arg(provider->name(), QString::number(imageFiles.size()))); .arg(provider->name(), QString::number(imageFiles.size())));
} }
@ -522,6 +507,7 @@ QVector<LLMCore::ImageAttachment> ClientInterface::loadImagesFromStorage(const Q
void ClientInterface::setChatFilePath(const QString &filePath) void ClientInterface::setChatFilePath(const QString &filePath)
{ {
m_chatFilePath = filePath; m_chatFilePath = filePath;
m_chatModel->setChatFilePath(filePath);
} }
QString ClientInterface::chatFilePath() const QString ClientInterface::chatFilePath() const

View File

@ -61,12 +61,18 @@ ChatRootView {
SplitDropZone { SplitDropZone {
anchors.fill: parent anchors.fill: parent
onFilesDroppedToAttach: (filePaths) => { onFilesDroppedToAttach: (urlStrings) => {
root.addFilesToAttachList(filePaths) var localPaths = root.convertUrlsToLocalPaths(urlStrings)
if (localPaths.length > 0) {
root.addFilesToAttachList(localPaths)
}
} }
onFilesDroppedToLink: (filePaths) => { onFilesDroppedToLink: (urlStrings) => {
root.addFilesToLinkList(filePaths) var localPaths = root.convertUrlsToLocalPaths(urlStrings)
if (localPaths.length > 0) {
root.addFilesToLinkList(localPaths)
}
} }
} }

View File

@ -263,12 +263,7 @@ Rectangle {
Layout.maximumWidth: parent.parent.maxImageWidth Layout.maximumWidth: parent.parent.maxImageWidth
Layout.maximumHeight: parent.parent.maxImageHeight Layout.maximumHeight: parent.parent.maxImageHeight
source: { source: itemData.imageUrl ? itemData.imageUrl : ""
if (!itemData.storedPath || !root.chatFilePath) return "";
var fileInfo = chatFileInfo(root.chatFilePath);
var imagesFolder = fileInfo.dir + "/" + fileInfo.baseName + "_images";
return "file://" + imagesFolder + "/" + itemData.storedPath;
}
sourceSize.width: parent.parent.maxImageWidth sourceSize.width: parent.parent.maxImageWidth
sourceSize.height: parent.parent.maxImageHeight sourceSize.height: parent.parent.maxImageHeight
@ -303,14 +298,4 @@ Rectangle {
} }
} }
} }
function chatFileInfo(filePath) {
if (!filePath) return {dir: "", baseName: ""};
var lastSlash = filePath.lastIndexOf("/");
var dir = lastSlash >= 0 ? filePath.substring(0, lastSlash) : "";
var fileName = lastSlash >= 0 ? filePath.substring(lastSlash + 1) : filePath;
var lastDot = fileName.lastIndexOf(".");
var baseName = lastDot >= 0 ? fileName.substring(0, lastDot) : fileName;
return {dir: dir, baseName: baseName};
}
} }

View File

@ -23,8 +23,8 @@ import QtQuick.Controls
Item { Item {
id: root id: root
signal filesDroppedToAttach(var filePaths) signal filesDroppedToAttach(var urlStrings) // Array of URL strings (file://...)
signal filesDroppedToLink(var filePaths) signal filesDroppedToLink(var urlStrings) // Array of URL strings (file://...)
property string activeZone: "" property string activeZone: ""
@ -216,21 +216,17 @@ Item {
splitDropOverlay.visible = false splitDropOverlay.visible = false
root.activeZone = "" root.activeZone = ""
if (drop.hasUrls) { if (drop.hasUrls && drop.urls.length > 0) {
var filePaths = [] // Convert URLs to array of strings for C++ processing
var urlStrings = []
for (var i = 0; i < drop.urls.length; i++) { for (var i = 0; i < drop.urls.length; i++) {
var url = drop.urls[i].toString() urlStrings.push(drop.urls[i].toString())
if (url.startsWith("file://")) {
filePaths.push(decodeURIComponent(url.replace(/^file:\/\//, '')))
}
} }
if (filePaths.length > 0) { if (targetZone === "right") {
if (targetZone === "right") { root.filesDroppedToLink(urlStrings)
root.filesDroppedToLink(filePaths) } else {
} else { root.filesDroppedToAttach(urlStrings)
root.filesDroppedToAttach(filePaths)
}
} }
} }
} }