新增对话历史面板
This commit is contained in:
@@ -41,6 +41,8 @@ qt_add_executable(QtDesktopPet
|
|||||||
src/tray/TrayController.cpp
|
src/tray/TrayController.cpp
|
||||||
src/ui/ChatBubble.h
|
src/ui/ChatBubble.h
|
||||||
src/ui/ChatBubble.cpp
|
src/ui/ChatBubble.cpp
|
||||||
|
src/ui/ChatHistoryPanel.h
|
||||||
|
src/ui/ChatHistoryPanel.cpp
|
||||||
src/ui/ChatInputDialog.h
|
src/ui/ChatInputDialog.h
|
||||||
src/ui/ChatInputDialog.cpp
|
src/ui/ChatInputDialog.cpp
|
||||||
src/ui/PetView.h
|
src/ui/PetView.h
|
||||||
|
|||||||
@@ -0,0 +1,349 @@
|
|||||||
|
#include "ChatHistoryPanel.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QEvent>
|
||||||
|
#include <QFrame>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QRect>
|
||||||
|
#include <QScreen>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QTextEdit>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr int PanelWidth = 460;
|
||||||
|
constexpr int PanelHeight = 360;
|
||||||
|
constexpr int PanelGap = 16;
|
||||||
|
constexpr int ScreenMargin = 8;
|
||||||
|
|
||||||
|
int boundedCoordinate(int value, int minimum, int maximum)
|
||||||
|
{
|
||||||
|
if (maximum < minimum)
|
||||||
|
{
|
||||||
|
return minimum;
|
||||||
|
}
|
||||||
|
|
||||||
|
return qBound(minimum, value, maximum);
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 overlapArea(const QRect &first, const QRect &second)
|
||||||
|
{
|
||||||
|
const QRect intersection = first.intersected(second);
|
||||||
|
if (intersection.isEmpty())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<qint64>(intersection.width()) * static_cast<qint64>(intersection.height());
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 squaredDistance(const QPoint &first, const QPoint &second)
|
||||||
|
{
|
||||||
|
const qint64 dx = first.x() - second.x();
|
||||||
|
const qint64 dy = first.y() - second.y();
|
||||||
|
return dx * dx + dy * dy;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString roleLabel(const QString &role)
|
||||||
|
{
|
||||||
|
if (role == QStringLiteral("user"))
|
||||||
|
{
|
||||||
|
return QStringLiteral("你");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == QStringLiteral("assistant"))
|
||||||
|
{
|
||||||
|
return QStringLiteral("桌宠");
|
||||||
|
}
|
||||||
|
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString roleColor(const QString &role)
|
||||||
|
{
|
||||||
|
if (role == QStringLiteral("user"))
|
||||||
|
{
|
||||||
|
return QStringLiteral("#1769aa");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == QStringLiteral("assistant"))
|
||||||
|
{
|
||||||
|
return QStringLiteral("#3f6b34");
|
||||||
|
}
|
||||||
|
|
||||||
|
return QStringLiteral("#5f6368");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString htmlEscapedContent(QString content)
|
||||||
|
{
|
||||||
|
content = content.trimmed().toHtmlEscaped();
|
||||||
|
content.replace(QStringLiteral("\n"), QStringLiteral("<br>"));
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatHistoryPanel::ChatHistoryPanel(QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
, m_textEdit(new QTextEdit(this))
|
||||||
|
{
|
||||||
|
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
|
||||||
|
setAttribute(Qt::WA_TranslucentBackground);
|
||||||
|
setModal(false);
|
||||||
|
resize(PanelWidth, PanelHeight);
|
||||||
|
|
||||||
|
m_textEdit->setReadOnly(true);
|
||||||
|
m_textEdit->setFrameShape(QFrame::NoFrame);
|
||||||
|
m_textEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
m_textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||||
|
m_textEdit->setLineWrapMode(QTextEdit::WidgetWidth);
|
||||||
|
m_textEdit->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||||
|
m_textEdit->installEventFilter(this);
|
||||||
|
m_textEdit->viewport()->installEventFilter(this);
|
||||||
|
m_textEdit->verticalScrollBar()->installEventFilter(this);
|
||||||
|
if (qApp != nullptr)
|
||||||
|
{
|
||||||
|
qApp->installEventFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
setStyleSheet(QStringLiteral(
|
||||||
|
"QDialog {"
|
||||||
|
"background: transparent;"
|
||||||
|
"}"
|
||||||
|
"QTextEdit {"
|
||||||
|
"background: rgba(246, 249, 253, 232);"
|
||||||
|
"color: #202124;"
|
||||||
|
"border: 1px solid rgba(32, 33, 36, 46);"
|
||||||
|
"border-radius: 12px;"
|
||||||
|
"padding: 14px 16px;"
|
||||||
|
"font-size: 14px;"
|
||||||
|
"selection-background-color: rgba(66, 133, 244, 90);"
|
||||||
|
"}"
|
||||||
|
"QScrollBar:vertical {"
|
||||||
|
"background: transparent;"
|
||||||
|
"width: 9px;"
|
||||||
|
"margin: 12px 5px 12px 0;"
|
||||||
|
"}"
|
||||||
|
"QScrollBar::handle:vertical {"
|
||||||
|
"background: rgba(32, 33, 36, 88);"
|
||||||
|
"border-radius: 4px;"
|
||||||
|
"min-height: 28px;"
|
||||||
|
"}"
|
||||||
|
"QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {"
|
||||||
|
"height: 0;"
|
||||||
|
"}"));
|
||||||
|
|
||||||
|
auto *layout = new QVBoxLayout(this);
|
||||||
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
layout->addWidget(m_textEdit);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatHistoryPanel::~ChatHistoryPanel()
|
||||||
|
{
|
||||||
|
if (qApp != nullptr)
|
||||||
|
{
|
||||||
|
qApp->removeEventFilter(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatHistoryPanel::setMessages(const QVector<ChatMessage> &messages)
|
||||||
|
{
|
||||||
|
m_messages = messages;
|
||||||
|
updateContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatHistoryPanel::showAt(const QPoint &anchorPosition)
|
||||||
|
{
|
||||||
|
move(constrainedTopLeft(anchorPosition, availableGeometryForPoint(anchorPosition)));
|
||||||
|
show();
|
||||||
|
raise();
|
||||||
|
activateWindow();
|
||||||
|
m_textEdit->setFocus();
|
||||||
|
m_textEdit->verticalScrollBar()->setValue(m_textEdit->verticalScrollBar()->maximum());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatHistoryPanel::showNear(const QRect &avoidRect)
|
||||||
|
{
|
||||||
|
move(smartTopLeftNear(avoidRect));
|
||||||
|
show();
|
||||||
|
raise();
|
||||||
|
activateWindow();
|
||||||
|
m_textEdit->setFocus();
|
||||||
|
m_textEdit->verticalScrollBar()->setValue(m_textEdit->verticalScrollBar()->maximum());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatHistoryPanel::event(QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::WindowDeactivate)
|
||||||
|
{
|
||||||
|
hide();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QDialog::event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatHistoryPanel::eventFilter(QObject *watched, QEvent *event)
|
||||||
|
{
|
||||||
|
if (watched == qApp && isVisible() && event->type() == QEvent::ApplicationDeactivate)
|
||||||
|
{
|
||||||
|
hide();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isVisible() && event->type() == QEvent::MouseButtonPress && !isOwnObject(watched))
|
||||||
|
{
|
||||||
|
hide();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QDialog::eventFilter(watched, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatHistoryPanel::isOwnObject(QObject *object) const
|
||||||
|
{
|
||||||
|
while (object != nullptr)
|
||||||
|
{
|
||||||
|
if (object == this || object == m_textEdit || object == m_textEdit->viewport() || object == m_textEdit->verticalScrollBar())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
object = object->parent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect ChatHistoryPanel::availableGeometryForPoint(const QPoint &point) const
|
||||||
|
{
|
||||||
|
const QScreen *screen = QGuiApplication::screenAt(point);
|
||||||
|
if (screen == nullptr)
|
||||||
|
{
|
||||||
|
screen = QGuiApplication::primaryScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (screen == nullptr)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return screen->availableGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect ChatHistoryPanel::availableGeometryForRect(const QRect &rect) const
|
||||||
|
{
|
||||||
|
const QScreen *screen = QGuiApplication::screenAt(rect.center());
|
||||||
|
if (screen == nullptr)
|
||||||
|
{
|
||||||
|
screen = QGuiApplication::screenAt(rect.topLeft());
|
||||||
|
}
|
||||||
|
if (screen == nullptr)
|
||||||
|
{
|
||||||
|
screen = QGuiApplication::primaryScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (screen == nullptr)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return screen->availableGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint ChatHistoryPanel::constrainedTopLeft(const QPoint &preferredTopLeft, const QRect &availableGeometry) const
|
||||||
|
{
|
||||||
|
if (!availableGeometry.isValid())
|
||||||
|
{
|
||||||
|
return preferredTopLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int x = boundedCoordinate(
|
||||||
|
preferredTopLeft.x(),
|
||||||
|
availableGeometry.left() + ScreenMargin,
|
||||||
|
availableGeometry.right() - width() - ScreenMargin);
|
||||||
|
const int y = boundedCoordinate(
|
||||||
|
preferredTopLeft.y(),
|
||||||
|
availableGeometry.top() + ScreenMargin,
|
||||||
|
availableGeometry.bottom() - height() - ScreenMargin);
|
||||||
|
|
||||||
|
return QPoint(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint ChatHistoryPanel::smartTopLeftNear(const QRect &avoidRect) const
|
||||||
|
{
|
||||||
|
if (!avoidRect.isValid())
|
||||||
|
{
|
||||||
|
return constrainedTopLeft(pos(), availableGeometryForPoint(pos()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const QRect availableGeometry = availableGeometryForRect(avoidRect);
|
||||||
|
const QVector<QPoint> preferredTopLefts = {
|
||||||
|
QPoint(avoidRect.right() + 1 + PanelGap, avoidRect.top()),
|
||||||
|
QPoint(avoidRect.left() - PanelGap - width(), avoidRect.top()),
|
||||||
|
QPoint(avoidRect.center().x() - width() / 2, avoidRect.top() - PanelGap - height()),
|
||||||
|
QPoint(avoidRect.center().x() - width() / 2, avoidRect.bottom() + 1 + PanelGap),
|
||||||
|
};
|
||||||
|
|
||||||
|
QPoint bestTopLeft = constrainedTopLeft(preferredTopLefts.first(), availableGeometry);
|
||||||
|
qint64 bestOverlap = std::numeric_limits<qint64>::max();
|
||||||
|
qint64 bestDistance = std::numeric_limits<qint64>::max();
|
||||||
|
int bestIndex = preferredTopLefts.size();
|
||||||
|
|
||||||
|
for (int index = 0; index < preferredTopLefts.size(); ++index)
|
||||||
|
{
|
||||||
|
const QPoint candidateTopLeft = constrainedTopLeft(preferredTopLefts.at(index), availableGeometry);
|
||||||
|
const QRect candidateRect(candidateTopLeft, size());
|
||||||
|
const qint64 candidateOverlap = overlapArea(candidateRect, avoidRect);
|
||||||
|
const qint64 candidateDistance = squaredDistance(candidateTopLeft, preferredTopLefts.at(index));
|
||||||
|
|
||||||
|
if (candidateOverlap < bestOverlap
|
||||||
|
|| (candidateOverlap == bestOverlap && candidateDistance < bestDistance)
|
||||||
|
|| (candidateOverlap == bestOverlap && candidateDistance == bestDistance && index < bestIndex))
|
||||||
|
{
|
||||||
|
bestTopLeft = candidateTopLeft;
|
||||||
|
bestOverlap = candidateOverlap;
|
||||||
|
bestDistance = candidateDistance;
|
||||||
|
bestIndex = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestTopLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatHistoryPanel::updateContent()
|
||||||
|
{
|
||||||
|
QString html = QStringLiteral(
|
||||||
|
"<html><body style=\"font-family:'Microsoft YaHei','Segoe UI',sans-serif; margin:0;\">");
|
||||||
|
|
||||||
|
bool hasContent = false;
|
||||||
|
for (const ChatMessage &message : m_messages)
|
||||||
|
{
|
||||||
|
const QString content = htmlEscapedContent(message.content);
|
||||||
|
if (content.isEmpty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasContent = true;
|
||||||
|
html += QStringLiteral(
|
||||||
|
"<div style=\"margin:0 0 14px 0;\">"
|
||||||
|
"<div style=\"font-size:12px; font-weight:600; color:%1; margin-bottom:5px;\">%2</div>"
|
||||||
|
"<div style=\"font-size:14px; line-height:1.45; color:#202124;\">%3</div>"
|
||||||
|
"</div>")
|
||||||
|
.arg(roleColor(message.role), roleLabel(message.role).toHtmlEscaped(), content);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasContent)
|
||||||
|
{
|
||||||
|
html += QStringLiteral(
|
||||||
|
"<div style=\"font-size:14px; line-height:1.45; color:#5f6368;\">暂无对话记录。</div>");
|
||||||
|
}
|
||||||
|
|
||||||
|
html += QStringLiteral("</body></html>");
|
||||||
|
m_textEdit->setHtml(html);
|
||||||
|
m_textEdit->verticalScrollBar()->setValue(m_textEdit->verticalScrollBar()->maximum());
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../ai/LLMTypes.h"
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QPoint>
|
||||||
|
#include <QRect>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
class QEvent;
|
||||||
|
class QTextEdit;
|
||||||
|
|
||||||
|
class ChatHistoryPanel : public QDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ChatHistoryPanel(QWidget *parent = nullptr);
|
||||||
|
~ChatHistoryPanel() override;
|
||||||
|
|
||||||
|
void setMessages(const QVector<ChatMessage> &messages);
|
||||||
|
void showAt(const QPoint &anchorPosition);
|
||||||
|
void showNear(const QRect &avoidRect);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool event(QEvent *event) override;
|
||||||
|
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool isOwnObject(QObject *object) const;
|
||||||
|
QRect availableGeometryForPoint(const QPoint &point) const;
|
||||||
|
QRect availableGeometryForRect(const QRect &rect) const;
|
||||||
|
QPoint constrainedTopLeft(const QPoint &preferredTopLeft, const QRect &availableGeometry) const;
|
||||||
|
QPoint smartTopLeftNear(const QRect &avoidRect) const;
|
||||||
|
void updateContent();
|
||||||
|
|
||||||
|
QTextEdit *m_textEdit;
|
||||||
|
QVector<ChatMessage> m_messages;
|
||||||
|
};
|
||||||
+21
-38
@@ -7,6 +7,7 @@
|
|||||||
#include "../config/SecretStore.h"
|
#include "../config/SecretStore.h"
|
||||||
#include "../util/Logger.h"
|
#include "../util/Logger.h"
|
||||||
#include "ChatBubble.h"
|
#include "ChatBubble.h"
|
||||||
|
#include "ChatHistoryPanel.h"
|
||||||
#include "ChatInputDialog.h"
|
#include "ChatInputDialog.h"
|
||||||
#include "PetView.h"
|
#include "PetView.h"
|
||||||
#include "SettingsDialog.h"
|
#include "SettingsDialog.h"
|
||||||
@@ -122,47 +123,12 @@ QString userVisibleErrorMessage(const ChatResponse &response)
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString roleLabel(const QString &role)
|
|
||||||
{
|
|
||||||
if (role == QStringLiteral("user"))
|
|
||||||
{
|
|
||||||
return QStringLiteral("你");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (role == QStringLiteral("assistant"))
|
|
||||||
{
|
|
||||||
return QStringLiteral("桌宠");
|
|
||||||
}
|
|
||||||
|
|
||||||
return role;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString formattedConversationHistory(const QVector<ChatMessage> &history)
|
|
||||||
{
|
|
||||||
if (history.isEmpty())
|
|
||||||
{
|
|
||||||
return QStringLiteral("暂无对话记录。");
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList lines;
|
|
||||||
for (const ChatMessage &message : history)
|
|
||||||
{
|
|
||||||
const QString content = message.content.trimmed();
|
|
||||||
if (content.isEmpty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
lines.append(roleLabel(message.role) + QStringLiteral(":") + content);
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines.isEmpty() ? QStringLiteral("暂无对话记录。") : lines.join(QStringLiteral("\n\n"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PetWindow::PetWindow(QWidget *parent)
|
PetWindow::PetWindow(QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, m_chatBubble(std::make_unique<ChatBubble>())
|
, m_chatBubble(std::make_unique<ChatBubble>())
|
||||||
|
, m_chatHistoryPanel(std::make_unique<ChatHistoryPanel>(this))
|
||||||
, m_chatInputDialog(std::make_unique<ChatInputDialog>(MaxUserMessageLength, this))
|
, m_chatInputDialog(std::make_unique<ChatInputDialog>(MaxUserMessageLength, this))
|
||||||
, m_conversationManager(std::make_unique<ConversationManager>())
|
, m_conversationManager(std::make_unique<ConversationManager>())
|
||||||
, m_petView(new PetView(this))
|
, m_petView(new PetView(this))
|
||||||
@@ -467,6 +433,7 @@ bool PetWindow::submitChatMessage(const QString &message)
|
|||||||
{
|
{
|
||||||
window->playState(QStringLiteral("talk"), false);
|
window->playState(QStringLiteral("talk"), false);
|
||||||
window->showBubbleMessage(response.content);
|
window->showBubbleMessage(response.content);
|
||||||
|
window->refreshChatHistoryPanel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,6 +458,7 @@ void PetWindow::clearConversation()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_conversationManager->clear();
|
m_conversationManager->clear();
|
||||||
|
refreshChatHistoryPanel();
|
||||||
showBubbleMessage(hadActiveRequest
|
showBubbleMessage(hadActiveRequest
|
||||||
? QStringLiteral("已取消 AI 请求,并清空对话。")
|
? QStringLiteral("已取消 AI 请求,并清空对话。")
|
||||||
: QStringLiteral("对话已清空。"));
|
: QStringLiteral("对话已清空。"));
|
||||||
@@ -531,8 +499,19 @@ bool PetWindow::hasActiveAIRequest() const
|
|||||||
void PetWindow::showConversationHistory()
|
void PetWindow::showConversationHistory()
|
||||||
{
|
{
|
||||||
const QVector<ChatMessage> history = m_conversationManager ? m_conversationManager->history() : QVector<ChatMessage>();
|
const QVector<ChatMessage> history = m_conversationManager ? m_conversationManager->history() : QVector<ChatMessage>();
|
||||||
m_chatBubble->showMessage(formattedConversationHistory(history), bubbleAnchorPosition(), 30000);
|
m_chatHistoryPanel->setMessages(history);
|
||||||
m_chatBubble->setDismissOnExternalInteraction(true);
|
m_chatHistoryPanel->showNear(frameGeometry());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PetWindow::refreshChatHistoryPanel()
|
||||||
|
{
|
||||||
|
if (!m_chatHistoryPanel || !m_chatHistoryPanel->isVisible())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVector<ChatMessage> history = m_conversationManager ? m_conversationManager->history() : QVector<ChatMessage>();
|
||||||
|
m_chatHistoryPanel->setMessages(history);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PetWindow::resetBubbleAutoHideTimer()
|
void PetWindow::resetBubbleAutoHideTimer()
|
||||||
@@ -558,6 +537,10 @@ void PetWindow::hideEvent(QHideEvent *event)
|
|||||||
{
|
{
|
||||||
m_chatInputDialog->hide();
|
m_chatInputDialog->hide();
|
||||||
}
|
}
|
||||||
|
if (m_chatHistoryPanel)
|
||||||
|
{
|
||||||
|
m_chatHistoryPanel->hide();
|
||||||
|
}
|
||||||
|
|
||||||
QWidget::hideEvent(event);
|
QWidget::hideEvent(event);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class QHideEvent;
|
|||||||
class QMoveEvent;
|
class QMoveEvent;
|
||||||
class QPixmap;
|
class QPixmap;
|
||||||
class ChatBubble;
|
class ChatBubble;
|
||||||
|
class ChatHistoryPanel;
|
||||||
class ChatInputDialog;
|
class ChatInputDialog;
|
||||||
class ConversationManager;
|
class ConversationManager;
|
||||||
class LLMProvider;
|
class LLMProvider;
|
||||||
@@ -54,6 +55,7 @@ private:
|
|||||||
void clearConversation();
|
void clearConversation();
|
||||||
void cancelActiveAIRequest();
|
void cancelActiveAIRequest();
|
||||||
void showConversationHistory();
|
void showConversationHistory();
|
||||||
|
void refreshChatHistoryPanel();
|
||||||
bool hasActiveAIRequest() const;
|
bool hasActiveAIRequest() const;
|
||||||
void resetBubbleAutoHideTimer();
|
void resetBubbleAutoHideTimer();
|
||||||
QPoint chatInputAnchorPosition() const;
|
QPoint chatInputAnchorPosition() const;
|
||||||
@@ -70,6 +72,7 @@ private:
|
|||||||
void setAlwaysOnTop(bool enabled);
|
void setAlwaysOnTop(bool enabled);
|
||||||
|
|
||||||
std::unique_ptr<ChatBubble> m_chatBubble;
|
std::unique_ptr<ChatBubble> m_chatBubble;
|
||||||
|
std::unique_ptr<ChatHistoryPanel> m_chatHistoryPanel;
|
||||||
std::unique_ptr<ChatInputDialog> m_chatInputDialog;
|
std::unique_ptr<ChatInputDialog> m_chatInputDialog;
|
||||||
std::unique_ptr<ConversationManager> m_conversationManager;
|
std::unique_ptr<ConversationManager> m_conversationManager;
|
||||||
std::unique_ptr<LLMProvider> m_aiTestProvider;
|
std::unique_ptr<LLMProvider> m_aiTestProvider;
|
||||||
|
|||||||
Reference in New Issue
Block a user