mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-05-30 02:49:12 -04:00
feat: Add message navigator
This commit is contained in:
@@ -27,6 +27,7 @@ qt_add_qml_module(QodeAssistChatView
|
||||
qml/controls/Toast.qml
|
||||
qml/controls/TopBar.qml
|
||||
qml/controls/SplitDropZone.qml
|
||||
qml/controls/MessageNavigator.qml
|
||||
|
||||
RESOURCES
|
||||
icons/attach-file-light.svg
|
||||
|
||||
@@ -335,6 +335,16 @@ void ChatModel::resetModelTo(int index)
|
||||
}
|
||||
}
|
||||
|
||||
QVariantList ChatModel::userMessageIndices() const
|
||||
{
|
||||
QVariantList result;
|
||||
for (int i = 0; i < m_messages.size(); ++i) {
|
||||
if (m_messages[i].role == ChatRole::User)
|
||||
result.append(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ChatModel::addToolExecutionStatus(
|
||||
const QString &requestId,
|
||||
const QString &toolId,
|
||||
|
||||
@@ -94,6 +94,7 @@ public:
|
||||
QString lastMessageId() const;
|
||||
|
||||
Q_INVOKABLE void resetModelTo(int index);
|
||||
Q_INVOKABLE QVariantList userMessageIndices() const;
|
||||
|
||||
void addToolExecutionStatus(
|
||||
const QString &requestId,
|
||||
|
||||
@@ -175,6 +175,27 @@ ChatRootView {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: 2
|
||||
|
||||
MessageNavigator {
|
||||
id: messageNavigator
|
||||
|
||||
Layout.preferredWidth: 16
|
||||
Layout.fillHeight: true
|
||||
Layout.topMargin: 4
|
||||
Layout.bottomMargin: 4
|
||||
|
||||
chatModel: root.chatModel
|
||||
|
||||
onMessageClicked: function(messageIndex) {
|
||||
chatListView.userScrolledUp = true
|
||||
chatListView.positionViewAtIndex(messageIndex, ListView.Beginning)
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: chatListView
|
||||
|
||||
@@ -182,7 +203,7 @@ ChatRootView {
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
leftMargin: 5
|
||||
leftMargin: 3
|
||||
model: root.chatModel
|
||||
clip: true
|
||||
spacing: 0
|
||||
@@ -370,6 +391,7 @@ ChatRootView {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: view
|
||||
|
||||
103
ChatView/qml/controls/MessageNavigator.qml
Normal file
103
ChatView/qml/controls/MessageNavigator.qml
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright (C) 2024-2026 Petr Mironychev
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import ChatView
|
||||
|
||||
Item {
|
||||
id: nav
|
||||
|
||||
property var chatModel
|
||||
property var userIndices: []
|
||||
property int hoveredDotIndex: -1
|
||||
property color dotColor: "#92BD6C"
|
||||
|
||||
signal messageClicked(int messageIndex)
|
||||
|
||||
implicitWidth: 16
|
||||
|
||||
function rebuild() {
|
||||
if (chatModel)
|
||||
userIndices = chatModel.userMessageIndices()
|
||||
else
|
||||
userIndices = []
|
||||
}
|
||||
|
||||
onChatModelChanged: rebuild()
|
||||
Component.onCompleted: rebuild()
|
||||
|
||||
Connections {
|
||||
target: nav.chatModel
|
||||
ignoreUnknownSignals: true
|
||||
function onRowsInserted() { nav.rebuild() }
|
||||
function onRowsRemoved() { nav.rebuild() }
|
||||
function onModelReset() { nav.rebuild() }
|
||||
function onModelReseted() { nav.rebuild() }
|
||||
function onLayoutChanged() { nav.rebuild() }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: spine
|
||||
|
||||
visible: nav.userIndices.length > 1
|
||||
x: nav.width / 2 - width / 2
|
||||
y: 14
|
||||
width: 1
|
||||
height: Math.max(0, nav.height - 28)
|
||||
color: palette.mid
|
||||
opacity: 0.4
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: nav.userIndices
|
||||
|
||||
delegate: Item {
|
||||
id: dotItem
|
||||
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
width: nav.width
|
||||
height: 14
|
||||
x: 0
|
||||
y: {
|
||||
const count = nav.userIndices.length
|
||||
const dotH = height
|
||||
if (count <= 1)
|
||||
return (nav.height - dotH) / 2
|
||||
const top = 7
|
||||
const bottom = nav.height - dotH - 7
|
||||
return top + (bottom - top) * index / (count - 1)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: dot
|
||||
anchors.centerIn: parent
|
||||
width: dotArea.containsMouse ? 10 : 7
|
||||
height: width
|
||||
radius: width / 2
|
||||
color: dotArea.containsMouse ? Qt.lighter(nav.dotColor, 1.15) : nav.dotColor
|
||||
border.color: Qt.darker(nav.dotColor, 1.4)
|
||||
border.width: 1
|
||||
opacity: dotArea.containsMouse ? 1.0 : 0.9
|
||||
|
||||
Behavior on width { NumberAnimation { duration: 120 } }
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: dotArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: nav.messageClicked(dotItem.modelData)
|
||||
|
||||
ToolTip.visible: containsMouse
|
||||
ToolTip.delay: 400
|
||||
ToolTip.text: qsTr("Jump to message #%1").arg(dotItem.index + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user