Files
z-bar-qt/Modules/Notifications/Sidebar/Chat/ChatContent.qml
T

184 lines
3.7 KiB
QML

import ZShell.Config
import ZShell.Llm
import QtQuick
import QtQuick.Layouts
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(list.positionViewAtBeginning);
}
function send(text: string): void {
if (text.trim() === "")
return;
following = true;
chatData.sendMessage(text);
input.text = "";
}
Component.onCompleted: input.focus = true
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)
}
}
VerticalFadeListView {
id: list
property bool userScrolledUp: false
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
cacheBuffer: height * 20
clip: true
fadeAmount: 0.05
fadeThreshold: Tokens.padding.medium
model: root.chatData.messagesModel
spacing: 0
verticalLayoutDirection: VerticalFadeListView.BottomToTop
add: Transition {
Anim {
from: list.width
property: "x"
to: 0
}
}
delegate: MessageDelegate {}
displaced: Transition {
Anim {
property: "y"
}
}
move: Transition {
Anim {
property: "y"
}
}
Component.onCompleted: positionViewAtBeginning()
onAtYEndChanged: {
if (atYEnd)
userScrolledUp = false;
}
onContentHeightChanged: {
if (!userScrolledUp && atYEnd)
root.scrollToBottom();
}
onCountChanged: {
if (!userScrolledUp)
root.scrollToBottom();
}
onMovingChanged: {
if (moving)
userScrolledUp = !atYEnd;
}
Anim {
id: scrollAnim
property: "contentY"
target: list
type: Anim.DefaultEffects
}
}
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: list.visibleArea.yPosition + list.visibleArea.heightRatio < 1 && list.contentHeight > list.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: {
list.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
onAccepted: root.send(text)
onSendPressed: root.send(text)
}
}