213 lines
4.2 KiB
QML
213 lines
4.2 KiB
QML
import QtQuick
|
|
import QtQuick.Layouts
|
|
import ZShell.Components
|
|
import ZShell.Config
|
|
import ZShell.Llm
|
|
import qs.Modules.Notifications.Sidebar.Chat.Content
|
|
import qs.Components
|
|
import qs.Services
|
|
|
|
Item {
|
|
id: root
|
|
|
|
property ChatSession chatData
|
|
property bool following: true
|
|
|
|
signal requestClose
|
|
|
|
function scrollToBottom(): void {
|
|
Qt.callLater(() => listLoader.item?.positionViewAtBeginning());
|
|
}
|
|
|
|
function send(text: string): void {
|
|
if (text.trim() === "")
|
|
return;
|
|
following = true;
|
|
chatData.sendMessage(text);
|
|
input.text = "";
|
|
}
|
|
|
|
RowLayout {
|
|
id: header
|
|
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.top: parent.top
|
|
spacing: Tokens.spacing.large
|
|
|
|
IconButton {
|
|
icon: "arrow_back"
|
|
inactiveColor: Colors.tPalette.m3surfaceContainerHigh
|
|
inactiveOnColor: Colors.palette.m3onSurfaceVariant
|
|
isRound: true
|
|
type: IconButton.Tonal
|
|
|
|
onClicked: root.requestClose()
|
|
}
|
|
|
|
CustomText {
|
|
Layout.fillWidth: true
|
|
elide: Text.ElideRight
|
|
font.pointSize: Tokens.font.size.larger
|
|
text: qsTr(root.chatData.title)
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
id: listLoader
|
|
|
|
anchors.bottom: input.top
|
|
anchors.bottomMargin: Tokens.spacing.medium
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.top: header.bottom
|
|
anchors.topMargin: Tokens.spacing.medium
|
|
active: root.chatData && root.chatData.loaded
|
|
|
|
sourceComponent: ListView {
|
|
id: list
|
|
|
|
property bool userScrolledUp: false
|
|
|
|
function scrollToBottom(): void {
|
|
Qt.callLater(() => positionViewAtBeginning());
|
|
}
|
|
|
|
cacheBuffer: height * 20
|
|
clip: true
|
|
// fadeAmount: 0.05
|
|
// fadeThreshold: Tokens.padding.medium
|
|
model: root.chatData.messagesModel
|
|
spacing: 0
|
|
rotation: 180
|
|
|
|
// add: Transition {
|
|
// Anim {
|
|
// from: 10
|
|
// property: "y"
|
|
// }
|
|
// }
|
|
delegate: MessageDelegate {
|
|
rotation: 180
|
|
}
|
|
|
|
// displaced: Transition {
|
|
// Anim {
|
|
// property: "y"
|
|
// }
|
|
// }
|
|
// move: Transition {
|
|
// Anim {
|
|
// property: "y"
|
|
// }
|
|
// }
|
|
|
|
Component.onCompleted: {
|
|
positionViewAtBeginning();
|
|
forceActiveFocus();
|
|
}
|
|
onAtYEndChanged: {
|
|
if (atYEnd)
|
|
userScrolledUp = false;
|
|
}
|
|
onContentHeightChanged: {
|
|
if (!userScrolledUp && atYEnd)
|
|
scrollToBottom();
|
|
}
|
|
onCountChanged: {
|
|
if (!userScrolledUp)
|
|
scrollToBottom();
|
|
}
|
|
onMovingChanged: {
|
|
if (moving)
|
|
userScrolledUp = !atYEnd;
|
|
}
|
|
|
|
Anim {
|
|
id: scrollAnim
|
|
|
|
property: "contentY"
|
|
target: list
|
|
type: Anim.DefaultEffects
|
|
}
|
|
|
|
WheelInverter {
|
|
target: list
|
|
}
|
|
}
|
|
|
|
// Trigger the load even before we're ready to show the list.
|
|
Component.onCompleted: if (root.chatData)
|
|
root.chatData.messagesModel
|
|
}
|
|
|
|
EmptyBackground {
|
|
id: emptyState
|
|
|
|
anchors.fill: parent
|
|
spacing: Tokens.spacing.small
|
|
visible: !root.chatData
|
|
}
|
|
|
|
Item {
|
|
anchors.bottom: input.top
|
|
anchors.bottomMargin: Tokens.spacing.extraLarge
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
implicitHeight: scrollToBottom.implicitHeight
|
|
implicitWidth: scrollToBottom.implicitWidth
|
|
scale: !listLoader.item.atYBeginning && listLoader.item.contentHeight > listLoader.item.height ? 1 : 0
|
|
visible: scale > 0
|
|
|
|
Behavior on scale {
|
|
Anim {}
|
|
}
|
|
|
|
Elevation {
|
|
anchors.fill: parent
|
|
level: 2
|
|
radius: scrollToBottom.radius
|
|
}
|
|
|
|
IconButton {
|
|
id: scrollToBottom
|
|
|
|
anchors.centerIn: parent
|
|
font.pointSize: Tokens.font.size.large
|
|
icon: "arrow_downward"
|
|
isRound: true
|
|
padding: Tokens.padding.extraSmall
|
|
type: IconButton.Tonal
|
|
|
|
onClicked: {
|
|
listLoader.item.positionViewAtBeginning();
|
|
}
|
|
}
|
|
}
|
|
|
|
ChatInput {
|
|
id: input
|
|
|
|
anchors.bottom: parent.bottom
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
bg.border.color: Colors.palette.m3outlineVariant
|
|
bg.color: Colors.tPalette.m3surfaceContainerLowest
|
|
font.pointSize: Tokens.font.size.normal
|
|
implicitHeight: Math.min(root.height / 5, contentHeight + topPadding + bottomPadding)
|
|
placeholderText: qsTr("Send a message")
|
|
sendIcon.font.pointSize: Tokens.font.size.large
|
|
sendIcon.icon: "arrow_upward"
|
|
sendIcon.padding: Tokens.padding.extraSmall
|
|
|
|
Keys.onPressed: e => {
|
|
if (e.key == Qt.Key_Return) {
|
|
if (!(e.modifiers & Qt.ShiftModifier)) {
|
|
root.send(text);
|
|
e.accepted = true;
|
|
}
|
|
}
|
|
}
|
|
onSendPressed: root.send(text)
|
|
}
|
|
}
|