mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-05-30 10:59:30 -04:00
fix: Change getting focus to chat in editor and name of title
This commit is contained in:
@@ -44,9 +44,11 @@ qt_add_qml_module(QodeAssistChatView
|
|||||||
icons/window-unlock.svg
|
icons/window-unlock.svg
|
||||||
icons/chat-icon.svg
|
icons/chat-icon.svg
|
||||||
icons/chat-pause-icon.svg
|
icons/chat-pause-icon.svg
|
||||||
|
icons/new-chat-icon.svg
|
||||||
icons/rules-icon.svg
|
icons/rules-icon.svg
|
||||||
icons/context-icon.svg
|
icons/context-icon.svg
|
||||||
icons/open-in-editor.svg
|
icons/open-in-editor.svg
|
||||||
|
icons/open-in-window.svg
|
||||||
icons/apply-changes-button.svg
|
icons/apply-changes-button.svg
|
||||||
icons/undo-changes-button.svg
|
icons/undo-changes-button.svg
|
||||||
icons/reject-changes-button.svg
|
icons/reject-changes-button.svg
|
||||||
|
|||||||
@@ -112,6 +112,18 @@ ChatRootView::ChatRootView(QQuickItem *parent)
|
|||||||
setRecentFilePath(QString{});
|
setRecentFilePath(QString{});
|
||||||
m_fileEditController->clearCurrentRequestId();
|
m_fileEditController->clearCurrentRequestId();
|
||||||
});
|
});
|
||||||
|
auto maybeEmitTitle = [this] {
|
||||||
|
const QString newTitle = computeChatTitle();
|
||||||
|
if (newTitle == m_cachedChatTitle)
|
||||||
|
return;
|
||||||
|
m_cachedChatTitle = newTitle;
|
||||||
|
emit chatTitleChanged();
|
||||||
|
};
|
||||||
|
connect(m_chatModel, &ChatModel::modelReseted, this, maybeEmitTitle);
|
||||||
|
connect(m_chatModel, &QAbstractItemModel::modelReset, this, maybeEmitTitle);
|
||||||
|
connect(m_chatModel, &QAbstractItemModel::rowsInserted, this, maybeEmitTitle);
|
||||||
|
connect(m_chatModel, &QAbstractItemModel::rowsRemoved, this, maybeEmitTitle);
|
||||||
|
connect(m_chatModel, &QAbstractItemModel::dataChanged, this, maybeEmitTitle);
|
||||||
connect(this, &ChatRootView::attachmentFilesChanged, this, [this]() {
|
connect(this, &ChatRootView::attachmentFilesChanged, this, [this]() {
|
||||||
m_tokenCounter->setAttachments(m_attachmentFiles);
|
m_tokenCounter->setAttachments(m_attachmentFiles);
|
||||||
});
|
});
|
||||||
@@ -792,6 +804,51 @@ void ChatRootView::triggerOpenChatCommand(Utils::Id commandId)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ChatRootView::isInEditor() const
|
||||||
|
{
|
||||||
|
return m_isInEditor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatRootView::setInEditor(bool value)
|
||||||
|
{
|
||||||
|
if (m_isInEditor == value)
|
||||||
|
return;
|
||||||
|
m_isInEditor = value;
|
||||||
|
emit isInEditorChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatRootView::requestNewChat()
|
||||||
|
{
|
||||||
|
triggerOpenChatCommand(Constants::QODE_ASSIST_NEW_CHAT_ACTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatRootView::chatTitle() const
|
||||||
|
{
|
||||||
|
if (m_cachedChatTitle.isEmpty())
|
||||||
|
m_cachedChatTitle = computeChatTitle();
|
||||||
|
return m_cachedChatTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatRootView::computeChatTitle() const
|
||||||
|
{
|
||||||
|
if (!m_chatModel)
|
||||||
|
return {};
|
||||||
|
const auto history = m_chatModel->getChatHistory();
|
||||||
|
for (const auto &msg : history) {
|
||||||
|
if (msg.role != ChatModel::User)
|
||||||
|
continue;
|
||||||
|
const QString content = msg.content.trimmed();
|
||||||
|
if (content.isEmpty())
|
||||||
|
continue;
|
||||||
|
const QString firstLine = content.section(QChar('\n'), 0, 0).trimmed();
|
||||||
|
constexpr int maxLen = 60;
|
||||||
|
if (firstLine.length() > maxLen)
|
||||||
|
return firstLine.left(maxLen - 1) + QChar(0x2026);
|
||||||
|
return firstLine;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void ChatRootView::handOffSession()
|
void ChatRootView::handOffSession()
|
||||||
{
|
{
|
||||||
if (m_chatModel->rowCount() > 0) {
|
if (m_chatModel->rowCount() > 0) {
|
||||||
@@ -824,7 +881,7 @@ void ChatRootView::consumePendingChatFile()
|
|||||||
void ChatRootView::relocateToSplit()
|
void ChatRootView::relocateToSplit()
|
||||||
{
|
{
|
||||||
handOffSession();
|
handOffSession();
|
||||||
triggerOpenChatCommand(Constants::QODE_ASSIST_SHOW_CHAT_ACTION);
|
triggerOpenChatCommand(Constants::QODE_ASSIST_NEW_CHAT_ACTION);
|
||||||
clearMessages();
|
clearMessages();
|
||||||
clearAttachmentFiles();
|
clearAttachmentFiles();
|
||||||
emit closeHostRequested();
|
emit closeHostRequested();
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ class ChatRootView : public QQuickItem
|
|||||||
Q_PROPERTY(QString currentAgentRoleDescription READ currentAgentRoleDescription NOTIFY currentAgentRoleChanged FINAL)
|
Q_PROPERTY(QString currentAgentRoleDescription READ currentAgentRoleDescription NOTIFY currentAgentRoleChanged FINAL)
|
||||||
Q_PROPERTY(QString currentAgentRoleSystemPrompt READ currentAgentRoleSystemPrompt NOTIFY currentAgentRoleChanged FINAL)
|
Q_PROPERTY(QString currentAgentRoleSystemPrompt READ currentAgentRoleSystemPrompt NOTIFY currentAgentRoleChanged FINAL)
|
||||||
Q_PROPERTY(bool isCompressing READ isCompressing NOTIFY isCompressingChanged FINAL)
|
Q_PROPERTY(bool isCompressing READ isCompressing NOTIFY isCompressingChanged FINAL)
|
||||||
|
Q_PROPERTY(bool isInEditor READ isInEditor NOTIFY isInEditorChanged FINAL)
|
||||||
|
Q_PROPERTY(QString chatTitle READ chatTitle NOTIFY chatTitleChanged FINAL)
|
||||||
|
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
@@ -183,6 +185,13 @@ public:
|
|||||||
|
|
||||||
bool isCompressing() const;
|
bool isCompressing() const;
|
||||||
|
|
||||||
|
bool isInEditor() const;
|
||||||
|
void setInEditor(bool value);
|
||||||
|
|
||||||
|
QString chatTitle() const;
|
||||||
|
|
||||||
|
Q_INVOKABLE void requestNewChat();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void sendMessage(const QString &message);
|
void sendMessage(const QString &message);
|
||||||
void copyToClipboard(const QString &text);
|
void copyToClipboard(const QString &text);
|
||||||
@@ -228,11 +237,15 @@ signals:
|
|||||||
void compressionCompleted(const QString &compressedChatPath);
|
void compressionCompleted(const QString &compressedChatPath);
|
||||||
void compressionFailed(const QString &error);
|
void compressionFailed(const QString &error);
|
||||||
|
|
||||||
|
void isInEditorChanged();
|
||||||
|
void chatTitleChanged();
|
||||||
|
|
||||||
void openFilesChanged();
|
void openFilesChanged();
|
||||||
|
|
||||||
void closeHostRequested();
|
void closeHostRequested();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QString computeChatTitle() const;
|
||||||
void triggerOpenChatCommand(Utils::Id commandId);
|
void triggerOpenChatCommand(Utils::Id commandId);
|
||||||
void handOffSession();
|
void handOffSession();
|
||||||
bool deferSendForAutoCompress(
|
bool deferSendForAutoCompress(
|
||||||
@@ -271,6 +284,8 @@ private:
|
|||||||
};
|
};
|
||||||
PendingSend m_pendingSend;
|
PendingSend m_pendingSend;
|
||||||
bool m_isSyncOpenFiles;
|
bool m_isSyncOpenFiles;
|
||||||
|
bool m_isInEditor = false;
|
||||||
|
mutable QString m_cachedChatTitle;
|
||||||
QList<Core::IEditor *> m_currentEditors;
|
QList<Core::IEditor *> m_currentEditors;
|
||||||
bool m_isRequestInProgress;
|
bool m_isRequestInProgress;
|
||||||
QString m_lastErrorMessage;
|
QString m_lastErrorMessage;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ ChatWidget::ChatWidget(
|
|||||||
QQmlEngine *engine,
|
QQmlEngine *engine,
|
||||||
SessionFileRegistry *sessionFileRegistry,
|
SessionFileRegistry *sessionFileRegistry,
|
||||||
Skills::SkillsManager *skillsManager,
|
Skills::SkillsManager *skillsManager,
|
||||||
|
bool registerOwnContext,
|
||||||
QWidget *parent)
|
QWidget *parent)
|
||||||
: QQuickWidget{engine, parent}
|
: QQuickWidget{engine, parent}
|
||||||
{
|
{
|
||||||
@@ -37,10 +38,19 @@ ChatWidget::ChatWidget(
|
|||||||
setResizeMode(QQuickWidget::SizeRootObjectToView);
|
setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||||
setFocusPolicy(Qt::StrongFocus);
|
setFocusPolicy(Qt::StrongFocus);
|
||||||
|
|
||||||
|
if (registerOwnContext) {
|
||||||
auto ideContext = new Core::IContext{this};
|
auto ideContext = new Core::IContext{this};
|
||||||
ideContext->setWidget(this);
|
ideContext->setWidget(this);
|
||||||
ideContext->setContext(Core::Context{Constants::QODE_ASSIST_CHAT_CONTEXT});
|
ideContext->setContext(Core::Context{Constants::QODE_ASSIST_CHAT_CONTEXT});
|
||||||
Core::ICore::addContextObject(ideContext);
|
Core::ICore::addContextObject(ideContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWidget::focusInEvent(QFocusEvent *event)
|
||||||
|
{
|
||||||
|
QQuickWidget::focusInEvent(event);
|
||||||
|
if (rootObject())
|
||||||
|
QMetaObject::invokeMethod(rootObject(), "focusInput");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatWidget::clear()
|
void ChatWidget::clear()
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public:
|
|||||||
QQmlEngine *engine,
|
QQmlEngine *engine,
|
||||||
SessionFileRegistry *sessionFileRegistry,
|
SessionFileRegistry *sessionFileRegistry,
|
||||||
Skills::SkillsManager *skillsManager,
|
Skills::SkillsManager *skillsManager,
|
||||||
|
bool registerOwnContext = true,
|
||||||
QWidget *parent = nullptr);
|
QWidget *parent = nullptr);
|
||||||
~ChatWidget() = default;
|
~ChatWidget() = default;
|
||||||
|
|
||||||
@@ -38,6 +39,9 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void clearPressed();
|
void clearPressed();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void focusInEvent(QFocusEvent *event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Chat
|
} // namespace QodeAssist::Chat
|
||||||
|
|||||||
5
ChatView/icons/new-chat-icon.svg
Normal file
5
ChatView/icons/new-chat-icon.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M21.6 0H2.4C1.08 0 0 1.08 0 2.4V16.8C0 18.12 1.08 19.2 2.4 19.2H7.2V22.8C7.2 23.46 7.74 24 8.4 24H9C9.3 24 9.6 23.88 9.84 23.652L14.28 19.2H21.6C22.92 19.2 24 18.12 24 16.8V2.4C24 1.08 22.92 0 21.6 0ZM21.6 16.8H13.44L8.76 21.48L8.4 21.6V16.8H2.4V2.4H21.6V16.8Z" fill="black"/>
|
||||||
|
<rect x="11" y="5" width="2" height="9" rx="0.5" fill="black"/>
|
||||||
|
<rect x="7.5" y="8.5" width="9" height="2" rx="0.5" fill="black"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 526 B |
@@ -1,17 +1,6 @@
|
|||||||
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="44" height="44" viewBox="0 0 44 44" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g clip-path="url(#clip0_74_52)">
|
<g transform="translate(10 8) skewX(-15)" stroke="black" stroke-width="2" stroke-linejoin="round">
|
||||||
<mask id="mask0_74_52" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="44" height="44">
|
<rect x="10" y="0" width="22" height="15" rx="3" ry="3" fill="black"/>
|
||||||
<path d="M44 0H0V44H44V0Z" fill="white"/>
|
<rect x="0" y="12" width="22" height="15" rx="3" ry="3" fill="none"/>
|
||||||
</mask>
|
</g>
|
||||||
<g mask="url(#mask0_74_52)">
|
|
||||||
<path d="M18 31C25.1797 31 31 25.1797 31 18C31 10.8203 25.1797 5 18 5C10.8203 5 5 10.8203 5 18C5 25.1797 10.8203 31 18 31Z" stroke="black" stroke-width="3.5"/>
|
|
||||||
<path d="M27 27L38 38" stroke="black" stroke-width="3.5" stroke-linecap="round"/>
|
|
||||||
<path d="M16.375 23L18.2841 11.3636H20.1023L18.1932 23H16.375ZM11.1648 20.1136L11.4659 18.2955H20.5568L20.2557 20.1136H11.1648ZM12.2841 23L14.1932 11.3636H16.0114L14.1023 23H12.2841ZM11.8295 16.0682L12.1364 14.25H21.2273L20.9205 16.0682H11.8295Z" fill="black"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<clipPath id="clip0_74_52">
|
|
||||||
<rect width="44" height="44" fill="white"/>
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 943 B After Width: | Height: | Size: 348 B |
6
ChatView/icons/open-in-window.svg
Normal file
6
ChatView/icons/open-in-window.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="44" height="44" viewBox="0 0 44 44" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g transform="translate(10 8) skewX(-15)" stroke="black" stroke-width="2" stroke-linejoin="round">
|
||||||
|
<rect x="10" y="0" width="22" height="15" rx="3" ry="3" fill="none"/>
|
||||||
|
<rect x="0" y="12" width="22" height="15" rx="3" ry="3" fill="black"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 348 B |
@@ -87,21 +87,25 @@ ChatRootView {
|
|||||||
Layout.preferredWidth: parent.width
|
Layout.preferredWidth: parent.width
|
||||||
Layout.preferredHeight: childrenRect.height + 10
|
Layout.preferredHeight: childrenRect.height + 10
|
||||||
|
|
||||||
|
isInEditor: root.isInEditor
|
||||||
saveButton.onClicked: root.showSaveDialog()
|
saveButton.onClicked: root.showSaveDialog()
|
||||||
loadButton.onClicked: root.showLoadDialog()
|
loadButton.onClicked: root.showLoadDialog()
|
||||||
clearButton.onClicked: root.clearChat()
|
clearButton.onClicked: root.clearChat()
|
||||||
|
newChatButton.onClicked: root.requestNewChat()
|
||||||
tokensBadge {
|
tokensBadge {
|
||||||
readonly property int sessionCached: root.chatModel.sessionCachedPromptTokens
|
readonly property int sessionPrompt: root.chatModel.sessionPromptTokens || 0
|
||||||
|
readonly property int sessionCompletion: root.chatModel.sessionCompletionTokens || 0
|
||||||
|
readonly property int sessionCached: root.chatModel.sessionCachedPromptTokens || 0
|
||||||
text: sessionCached > 0
|
text: sessionCached > 0
|
||||||
? qsTr("next ~%1 · session ↑%2 ↓%3 ↻%4")
|
? qsTr("next ~%1 · session ↑%2 ↓%3 ↻%4")
|
||||||
.arg(root.inputTokensCount)
|
.arg(root.inputTokensCount)
|
||||||
.arg(root.chatModel.sessionPromptTokens)
|
.arg(sessionPrompt)
|
||||||
.arg(root.chatModel.sessionCompletionTokens)
|
.arg(sessionCompletion)
|
||||||
.arg(sessionCached)
|
.arg(sessionCached)
|
||||||
: qsTr("next ~%1 · session ↑%2 ↓%3")
|
: qsTr("next ~%1 · session ↑%2 ↓%3")
|
||||||
.arg(root.inputTokensCount)
|
.arg(root.inputTokensCount)
|
||||||
.arg(root.chatModel.sessionPromptTokens)
|
.arg(sessionPrompt)
|
||||||
.arg(root.chatModel.sessionCompletionTokens)
|
.arg(sessionCompletion)
|
||||||
ToolTip.text: sessionCached > 0
|
ToolTip.text: sessionCached > 0
|
||||||
? qsTr("next request (estimate) · session prompt ↑ / completion ↓ / cached ↻ (provider cache hits)")
|
? qsTr("next request (estimate) · session prompt ↑ / completion ↓ / cached ↻ (provider cache hits)")
|
||||||
: qsTr("next request (estimate) · session prompt ↑ / completion ↓")
|
: qsTr("next request (estimate) · session prompt ↑ / completion ↓")
|
||||||
@@ -117,8 +121,11 @@ ChatRootView {
|
|||||||
onCheckedChanged: _chatview.isPin = topBar.pinButton.checked
|
onCheckedChanged: _chatview.isPin = topBar.pinButton.checked
|
||||||
}
|
}
|
||||||
relocateButton {
|
relocateButton {
|
||||||
|
icon.source: (typeof _chatview !== 'undefined')
|
||||||
|
? "qrc:/qt/qml/ChatView/icons/open-in-editor.svg"
|
||||||
|
: "qrc:/qt/qml/ChatView/icons/open-in-window.svg"
|
||||||
ToolTip.text: (typeof _chatview !== 'undefined')
|
ToolTip.text: (typeof _chatview !== 'undefined')
|
||||||
? qsTr("Move this chat to an editor split")
|
? qsTr("Move this chat to an editor tab")
|
||||||
: qsTr("Move this chat to a separate window")
|
: qsTr("Move this chat to a separate window")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (typeof _chatview !== 'undefined')
|
if (typeof _chatview !== 'undefined')
|
||||||
|
|||||||
@@ -10,9 +10,12 @@ import UIControls
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property bool isInEditor: false
|
||||||
|
|
||||||
property alias saveButton: saveButtonId
|
property alias saveButton: saveButtonId
|
||||||
property alias loadButton: loadButtonId
|
property alias loadButton: loadButtonId
|
||||||
property alias clearButton: clearButtonId
|
property alias clearButton: clearButtonId
|
||||||
|
property alias newChatButton: newChatButtonId
|
||||||
property alias tokensBadge: tokensBadgeId
|
property alias tokensBadge: tokensBadgeId
|
||||||
property alias recentPath: recentPathId
|
property alias recentPath: recentPathId
|
||||||
property alias openChatHistory: openChatHistoryId
|
property alias openChatHistory: openChatHistoryId
|
||||||
@@ -77,6 +80,43 @@ Rectangle {
|
|||||||
ToolTip.delay: 250
|
ToolTip.delay: 250
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QoASeparator {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
QoAButton {
|
||||||
|
id: clearButtonId
|
||||||
|
|
||||||
|
icon {
|
||||||
|
source: "qrc:/qt/qml/ChatView/icons/clean-icon-dark.svg"
|
||||||
|
height: 15
|
||||||
|
width: 8
|
||||||
|
}
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.delay: 250
|
||||||
|
ToolTip.text: qsTr("Clean chat")
|
||||||
|
}
|
||||||
|
|
||||||
|
QoASeparator {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
QoAButton {
|
||||||
|
id: newChatButtonId
|
||||||
|
|
||||||
|
visible: root.isInEditor
|
||||||
|
|
||||||
|
icon {
|
||||||
|
source: "qrc:/qt/qml/ChatView/icons/new-chat-icon.svg"
|
||||||
|
color: palette.window.hslLightness > 0.5 ? "#000000" : "#FFFFFF"
|
||||||
|
height: 15
|
||||||
|
width: 15
|
||||||
|
}
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.delay: 250
|
||||||
|
ToolTip.text: qsTr("Open new chat in a new tab")
|
||||||
|
}
|
||||||
|
|
||||||
QoAComboBox {
|
QoAComboBox {
|
||||||
id: configSelectorId
|
id: configSelectorId
|
||||||
|
|
||||||
@@ -276,21 +316,6 @@ 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")
|
||||||
}
|
}
|
||||||
|
|
||||||
QoASeparator {}
|
|
||||||
|
|
||||||
QoAButton {
|
|
||||||
id: clearButtonId
|
|
||||||
|
|
||||||
icon {
|
|
||||||
source: "qrc:/qt/qml/ChatView/icons/clean-icon-dark.svg"
|
|
||||||
height: 15
|
|
||||||
width: 8
|
|
||||||
}
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.delay: 250
|
|
||||||
ToolTip.text: qsTr("Clean chat")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const char QODE_ASSIST_CHAT_EDITOR_ID[] = "QodeAssist.ChatEditor";
|
|||||||
|
|
||||||
const char QODE_ASSIST_SHOW_CHAT_ACTION[] = "QodeAssist.ShowChatView";
|
const char QODE_ASSIST_SHOW_CHAT_ACTION[] = "QodeAssist.ShowChatView";
|
||||||
const char QODE_ASSIST_OPEN_CHAT_WINDOW_ACTION[] = "QodeAssist.OpenChatWindow";
|
const char QODE_ASSIST_OPEN_CHAT_WINDOW_ACTION[] = "QodeAssist.OpenChatWindow";
|
||||||
|
const char QODE_ASSIST_NEW_CHAT_ACTION[] = "QodeAssist.NewChat";
|
||||||
|
|
||||||
const char QODE_ASSIST_CHAT_SEND_MESSAGE[] = "QodeAssist.Chat.SendMessage";
|
const char QODE_ASSIST_CHAT_SEND_MESSAGE[] = "QodeAssist.Chat.SendMessage";
|
||||||
const char QODE_ASSIST_CHAT_CLEAR_SESSION[] = "QodeAssist.Chat.ClearSession";
|
const char QODE_ASSIST_CHAT_CLEAR_SESSION[] = "QodeAssist.Chat.ClearSession";
|
||||||
|
|||||||
@@ -3,17 +3,13 @@
|
|||||||
|
|
||||||
#include "ChatEditor.hpp"
|
#include "ChatEditor.hpp"
|
||||||
|
|
||||||
#include <coreplugin/actionmanager/actionmanager.h>
|
|
||||||
#include <coreplugin/actionmanager/command.h>
|
|
||||||
#include <coreplugin/coreconstants.h>
|
|
||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
|
|
||||||
#include <QAction>
|
|
||||||
|
|
||||||
#include "ChatDocument.hpp"
|
#include "ChatDocument.hpp"
|
||||||
#include "ChatView/ChatRootView.hpp"
|
#include "ChatView/ChatRootView.hpp"
|
||||||
#include "ChatView/ChatWidget.hpp"
|
#include "ChatView/ChatWidget.hpp"
|
||||||
#include "QodeAssistConstants.hpp"
|
#include "QodeAssistConstants.hpp"
|
||||||
|
#include "QodeAssisttr.h"
|
||||||
|
|
||||||
namespace QodeAssist::Chat {
|
namespace QodeAssist::Chat {
|
||||||
|
|
||||||
@@ -25,26 +21,28 @@ ChatEditor::ChatEditor(
|
|||||||
, m_sessionFileRegistry(sessionFileRegistry)
|
, m_sessionFileRegistry(sessionFileRegistry)
|
||||||
, m_skillsManager(skillsManager)
|
, m_skillsManager(skillsManager)
|
||||||
, m_document(new ChatDocument(this))
|
, m_document(new ChatDocument(this))
|
||||||
, m_chatWidget(new ChatWidget(engine, sessionFileRegistry, skillsManager))
|
, m_chatWidget(new ChatWidget(engine, sessionFileRegistry, skillsManager, false))
|
||||||
{
|
{
|
||||||
setWidget(m_chatWidget);
|
setWidget(m_chatWidget);
|
||||||
setContext(Core::Context(Constants::QODE_ASSIST_CHAT_CONTEXT));
|
setContext(Core::Context(Constants::QODE_ASSIST_CHAT_CONTEXT));
|
||||||
setDuplicateSupported(true);
|
setDuplicateSupported(false);
|
||||||
|
|
||||||
if (auto rootView = qobject_cast<ChatRootView *>(m_chatWidget->rootObject())) {
|
if (auto rootView = qobject_cast<ChatRootView *>(m_chatWidget->rootObject())) {
|
||||||
|
rootView->setInEditor(true);
|
||||||
connect(
|
connect(
|
||||||
rootView,
|
rootView,
|
||||||
&ChatRootView::closeHostRequested,
|
&ChatRootView::closeHostRequested,
|
||||||
this,
|
this,
|
||||||
[this] {
|
[this] { Core::EditorManager::closeEditors({this}); },
|
||||||
Core::EditorManager::closeEditors({this});
|
|
||||||
if (auto command
|
|
||||||
= Core::ActionManager::command(Core::Constants::REMOVE_CURRENT_SPLIT)) {
|
|
||||||
if (auto action = command->action(); action && action->isEnabled())
|
|
||||||
action->trigger();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
|
|
||||||
|
auto syncTitle = [this, rootView] {
|
||||||
|
const QString title = rootView->chatTitle();
|
||||||
|
m_document->setPreferredDisplayName(
|
||||||
|
title.isEmpty() ? Tr::tr("QodeAssist Chat") : QStringLiteral("QodeAssist - ") + title);
|
||||||
|
};
|
||||||
|
connect(rootView, &ChatRootView::chatTitleChanged, this, syncTitle);
|
||||||
|
syncTitle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +69,7 @@ QWidget *ChatEditor::toolBar()
|
|||||||
|
|
||||||
Core::IEditor *ChatEditor::duplicate()
|
Core::IEditor *ChatEditor::duplicate()
|
||||||
{
|
{
|
||||||
return new ChatEditor(m_engine, m_sessionFileRegistry, m_skillsManager);
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist::Chat
|
} // namespace QodeAssist::Chat
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ class ChatDocument;
|
|||||||
class ChatWidget;
|
class ChatWidget;
|
||||||
class SessionFileRegistry;
|
class SessionFileRegistry;
|
||||||
|
|
||||||
// Editor-area host for the chat. Each editor (including a split duplicate) owns its own
|
|
||||||
// ChatWidget and therefore its own independent chat session.
|
|
||||||
class ChatEditor : public Core::IEditor
|
class ChatEditor : public Core::IEditor
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include <coreplugin/actionmanager/actionmanager.h>
|
#include <coreplugin/actionmanager/actionmanager.h>
|
||||||
#include <coreplugin/actionmanager/command.h>
|
#include <coreplugin/actionmanager/command.h>
|
||||||
#include <coreplugin/coreconstants.h>
|
#include <coreplugin/coreconstants.h>
|
||||||
|
#include <coreplugin/editormanager/documentmodel.h>
|
||||||
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
#include <coreplugin/icontext.h>
|
#include <coreplugin/icontext.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/messagemanager.h>
|
#include <coreplugin/messagemanager.h>
|
||||||
@@ -228,10 +230,10 @@ public:
|
|||||||
ActionBuilder showChatViewAction(this, Constants::QODE_ASSIST_SHOW_CHAT_ACTION);
|
ActionBuilder showChatViewAction(this, Constants::QODE_ASSIST_SHOW_CHAT_ACTION);
|
||||||
const QKeySequence showChatViewShortcut = QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_W);
|
const QKeySequence showChatViewShortcut = QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_W);
|
||||||
showChatViewAction.setDefaultKeySequence(showChatViewShortcut);
|
showChatViewAction.setDefaultKeySequence(showChatViewShortcut);
|
||||||
showChatViewAction.setToolTip(Tr::tr("Open QodeAssist Chat in an editor split"));
|
showChatViewAction.setToolTip(Tr::tr("Open QodeAssist Chat as an editor tab"));
|
||||||
showChatViewAction.setText(Tr::tr("Show QodeAssist Chat"));
|
showChatViewAction.setText(Tr::tr("Show QodeAssist Chat"));
|
||||||
showChatViewAction.setIcon(QCODEASSIST_CHAT_ICON.icon());
|
showChatViewAction.setIcon(QCODEASSIST_CHAT_ICON.icon());
|
||||||
showChatViewAction.addOnTriggered(this, [this] { openChatInSplit(); });
|
showChatViewAction.addOnTriggered(this, [this] { openChatInEditor(); });
|
||||||
m_statusWidget->setChatButtonAction(showChatViewAction.contextAction());
|
m_statusWidget->setChatButtonAction(showChatViewAction.contextAction());
|
||||||
|
|
||||||
m_chatButtonMenu = new QMenu(m_statusWidget);
|
m_chatButtonMenu = new QMenu(m_statusWidget);
|
||||||
@@ -260,6 +262,12 @@ public:
|
|||||||
openChatWindowAction.setIcon(QCODEASSIST_CHAT_ICON.icon());
|
openChatWindowAction.setIcon(QCODEASSIST_CHAT_ICON.icon());
|
||||||
openChatWindowAction.addOnTriggered(this, [this] { openChatInWindow(); });
|
openChatWindowAction.addOnTriggered(this, [this] { openChatInWindow(); });
|
||||||
|
|
||||||
|
ActionBuilder newChatAction(this, Constants::QODE_ASSIST_NEW_CHAT_ACTION);
|
||||||
|
newChatAction.setText(Tr::tr("New QodeAssist Chat"));
|
||||||
|
newChatAction.setToolTip(Tr::tr("Open a fresh chat in a new editor tab"));
|
||||||
|
newChatAction.setIcon(QCODEASSIST_CHAT_ICON.icon());
|
||||||
|
newChatAction.addOnTriggered(this, [this] { openNewChatInEditor(); });
|
||||||
|
|
||||||
ActionBuilder sendMessageAction(this, Constants::QODE_ASSIST_CHAT_SEND_MESSAGE);
|
ActionBuilder sendMessageAction(this, Constants::QODE_ASSIST_CHAT_SEND_MESSAGE);
|
||||||
sendMessageAction.setContext(Core::Context(Constants::QODE_ASSIST_CHAT_CONTEXT));
|
sendMessageAction.setContext(Core::Context(Constants::QODE_ASSIST_CHAT_CONTEXT));
|
||||||
sendMessageAction.setText(Tr::tr("Send QodeAssist Chat Message"));
|
sendMessageAction.setText(Tr::tr("Send QodeAssist Chat Message"));
|
||||||
@@ -323,13 +331,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void openChatInSplit()
|
void openChatInEditor()
|
||||||
{
|
{
|
||||||
if (auto splitCommand
|
if (auto existing = findExistingChatEditor()) {
|
||||||
= Core::ActionManager::command(Core::Constants::SPLIT_SIDE_BY_SIDE)) {
|
Core::EditorManager::activateEditor(existing);
|
||||||
if (auto splitAction = splitCommand->action())
|
existing->consumePendingChatFile();
|
||||||
splitAction->trigger();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString title = Tr::tr("QodeAssist Chat");
|
QString title = Tr::tr("QodeAssist Chat");
|
||||||
Core::IEditor *editor = Core::EditorManager::openEditorWithContents(
|
Core::IEditor *editor = Core::EditorManager::openEditorWithContents(
|
||||||
Constants::QODE_ASSIST_CHAT_EDITOR_ID, &title, {}, QUuid::createUuid().toString());
|
Constants::QODE_ASSIST_CHAT_EDITOR_ID, &title, {}, QUuid::createUuid().toString());
|
||||||
@@ -337,6 +346,34 @@ private:
|
|||||||
chatEditor->consumePendingChatFile();
|
chatEditor->consumePendingChatFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void openNewChatInEditor()
|
||||||
|
{
|
||||||
|
QString title = Tr::tr("QodeAssist Chat");
|
||||||
|
Core::IEditor *editor = Core::EditorManager::openEditorWithContents(
|
||||||
|
Constants::QODE_ASSIST_CHAT_EDITOR_ID, &title, {}, QUuid::createUuid().toString());
|
||||||
|
// For the "New Chat" button pending is empty (no-op). For relocate-to-editor it
|
||||||
|
// carries the handed-off chat file and gets loaded into the freshly opened tab.
|
||||||
|
if (auto chatEditor = qobject_cast<Chat::ChatEditor *>(editor))
|
||||||
|
chatEditor->consumePendingChatFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
Chat::ChatEditor *findExistingChatEditor() const
|
||||||
|
{
|
||||||
|
const auto entries = Core::DocumentModel::entries();
|
||||||
|
for (auto *entry : entries) {
|
||||||
|
if (!entry || !entry->document)
|
||||||
|
continue;
|
||||||
|
if (entry->document->id() != Constants::QODE_ASSIST_CHAT_EDITOR_ID)
|
||||||
|
continue;
|
||||||
|
const auto editors = Core::DocumentModel::editorsForDocument(entry->document);
|
||||||
|
for (auto *editor : editors) {
|
||||||
|
if (auto chatEditor = qobject_cast<Chat::ChatEditor *>(editor))
|
||||||
|
return chatEditor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void openChatInWindow()
|
void openChatInWindow()
|
||||||
{
|
{
|
||||||
if (!m_chatView)
|
if (!m_chatView)
|
||||||
@@ -400,11 +437,11 @@ private:
|
|||||||
m_chatButtonMenu->addSeparator();
|
m_chatButtonMenu->addSeparator();
|
||||||
|
|
||||||
if (m_chatView && m_chatView->isVisible()) {
|
if (m_chatView && m_chatView->isVisible()) {
|
||||||
QAction *splitAction = m_chatButtonMenu->addAction(Tr::tr("Open Chat in Split"));
|
QAction *editorAction = m_chatButtonMenu->addAction(Tr::tr("Open Chat in Editor"));
|
||||||
connect(splitAction, &QAction::triggered, this, [this] {
|
connect(editorAction, &QAction::triggered, this, [this] {
|
||||||
if (m_chatView)
|
if (m_chatView)
|
||||||
m_chatView->close();
|
m_chatView->close();
|
||||||
openChatInSplit();
|
openChatInEditor();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
QAction *windowAction
|
QAction *windowAction
|
||||||
|
|||||||
Reference in New Issue
Block a user