initial commit for llm chat tab in sidebar
This commit is contained in:
@@ -0,0 +1,280 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import ZShell.Config
|
||||
import ZShell.Llm
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property ChatSession chatData
|
||||
property bool following: true
|
||||
readonly property int messageCount: chatData.messages.length
|
||||
|
||||
signal requestClose
|
||||
|
||||
function atBottom(): bool {
|
||||
return list.contentHeight - list.height - list.contentY < 48;
|
||||
}
|
||||
|
||||
function send(): void {
|
||||
if (Chat.busy || input.text.trim() === "")
|
||||
return;
|
||||
following = true;
|
||||
Chat.send(chatData.id, input.text);
|
||||
input.text = "";
|
||||
}
|
||||
|
||||
VerticalFadeListView {
|
||||
id: list
|
||||
|
||||
anchors.bottom: inputRow.top
|
||||
anchors.bottomMargin: Tokens.spacing.medium
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
cacheBuffer: height * 20
|
||||
clip: true
|
||||
currentIndex: messages.values.length - 1
|
||||
fadeAmount: 0.1
|
||||
highlightFollowsCurrentItem: true
|
||||
highlightRangeMode: ListView.ApplyRange
|
||||
preferredHighlightEnd: list.height
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
CustomScrollBar.vertical: CustomScrollBar {
|
||||
flickable: list
|
||||
}
|
||||
Behavior on contentY {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
delegate: ColumnLayout {
|
||||
id: messageRow
|
||||
|
||||
readonly property bool isUser: modelData.role === ChatMessage.Role.User
|
||||
required property var modelData
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: messageRow.isUser ? 0 : Tokens.spacing.extraSmall
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: messageRow.isUser ? Tokens.spacing.extraSmall : 0
|
||||
|
||||
Loader {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: Tokens.spacing.extraLarge
|
||||
active: !messageRow.isUser && messageRow.modelData.reasoning !== ""
|
||||
|
||||
sourceComponent: CustomRect {
|
||||
id: reasoning
|
||||
|
||||
property bool expanded: false
|
||||
|
||||
color: Colors.palette.m3surfaceContainer
|
||||
implicitHeight: expanded ? expandedText.implicitHeight : collapsedText.implicitHeight + Tokens.padding.medium * 2
|
||||
radius: Tokens.rounding.medium
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: spinnerReasoning
|
||||
|
||||
active: opacity > 0
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Tokens.padding.medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: messageRow.modelData.reasoningActive ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
sourceComponent: LoadingIndicator {
|
||||
implicitSize: collapsedText.implicitHeight
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: reasoningDone
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Tokens.padding.medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pointSize: Tokens.font.size.large
|
||||
opacity: messageRow.modelData.reasoningActive || reasoning.expanded ? 0 : 1
|
||||
text: "check"
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: collapsedText
|
||||
|
||||
anchors.left: messageRow.modelData.reasoningActive ? spinnerReasoning.right : reasoningDone.right
|
||||
anchors.margins: Tokens.padding.medium
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
opacity: reasoning.expanded ? 0 : 1
|
||||
text: messageRow.modelData.reasoningActive ? qsTr("Thinking...") : qsTr("Thought for %1s").arg((messageRow.modelData.reasoningElapsedMs / 1000).toFixed(1))
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: expandedText
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Tokens.padding.medium
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
color: Colors.palette.m3onSurface
|
||||
opacity: reasoning.expanded ? 1 : 0
|
||||
text: messageRow.modelData.reasoning
|
||||
visible: opacity > 0
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
onClicked: reasoning.expanded = !reasoning.expanded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.alignment: messageRow.isUser ? Qt.AlignRight : Qt.AlignLeft
|
||||
color: messageRow.isUser ? Colors.palette.m3primary : Colors.palette.m3surfaceContainer
|
||||
implicitHeight: Math.max(msgText.implicitHeight + Tokens.padding.medium * 2, spinnerLoader.implicitHeight)
|
||||
implicitWidth: Math.min(msgText.implicitWidth + Tokens.padding.medium * 2, messageRow.ListView.view.width - Tokens.spacing.extraSmall - Tokens.spacing.extraLarge)
|
||||
radius: Tokens.rounding.medium
|
||||
visible: !messageRow.modelData.reasoningActive
|
||||
|
||||
CustomText {
|
||||
id: msgText
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Tokens.padding.medium
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
color: messageRow.isUser ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
|
||||
text: messageRow.modelData.content
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: spinnerLoader
|
||||
|
||||
active: messageRow.modelData.streaming && msgText.text.length === 0
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
sourceComponent: Item {
|
||||
implicitHeight: 18
|
||||
implicitWidth: 18
|
||||
|
||||
LoadingIndicator {
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
model: ScriptModel {
|
||||
id: messages
|
||||
|
||||
values: root.chatData.messages
|
||||
}
|
||||
|
||||
Component.onCompleted: positionViewAtEnd()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: emptyState
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: Tokens.spacing.small
|
||||
visible: root.messageCount === 0 && !Chat.busy
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: Colors.tPalette.m3outlineVariant
|
||||
font.pointSize: 36
|
||||
text: "chat"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: Colors.tPalette.m3onSurfaceVariant
|
||||
text: qsTr("No messages yet")
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: inputRow
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: Tokens.spacing.extraSmall
|
||||
|
||||
CustomTextField {
|
||||
id: input
|
||||
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("Send a message...")
|
||||
type: CustomTextField.Filled
|
||||
|
||||
onAccepted: root.send()
|
||||
}
|
||||
|
||||
IconButton {
|
||||
id: actionBtn
|
||||
|
||||
enabled: Chat.busy || input.text.trim() !== ""
|
||||
font.pointSize: 20
|
||||
icon: Chat.busy ? "stop" : "send"
|
||||
type: IconButton.Filled
|
||||
|
||||
onClicked: Chat.busy ? Chat.stop() : root.send()
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
icon: "arrow_back"
|
||||
inactiveColor: Colors.tPalette.m3surfaceContainerHigh
|
||||
inactiveOnColor: Colors.palette.m3onSurfaceVariant
|
||||
isRound: true
|
||||
type: IconButton.Tonal
|
||||
|
||||
onClicked: root.requestClose()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Llm
|
||||
import ZShell.Config
|
||||
import qs.Services
|
||||
import qs.Components
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property alias model: list.model
|
||||
|
||||
signal loadChatRequest(content: ChatSession)
|
||||
signal newChatRequest
|
||||
|
||||
CustomListView {
|
||||
id: list
|
||||
|
||||
anchors.fill: parent
|
||||
cacheBuffer: height * 2
|
||||
clip: true
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
delegate: CustomRect {
|
||||
id: chatItem
|
||||
|
||||
required property var modelData
|
||||
|
||||
color: Colors.tPalette.m3surfaceContainer
|
||||
implicitHeight: layout.implicitHeight + layout.anchors.topMargin + layout.anchors.bottomMargin
|
||||
implicitWidth: ListView.view.width
|
||||
radius: Tokens.rounding.medium
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.bottomMargin: Tokens.padding.small
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Tokens.padding.medium
|
||||
anchors.rightMargin: Tokens.padding.medium
|
||||
anchors.topMargin: Tokens.padding.small
|
||||
spacing: 0
|
||||
|
||||
CustomText {
|
||||
id: title
|
||||
|
||||
text: chatItem.modelData.title
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: timestamp
|
||||
|
||||
color: Colors.palette.m3outline
|
||||
font.pointSize: Tokens.font.size.small
|
||||
text: chatItem.modelData.updatedAt.toLocaleString(Qt.locale("en_US"), "MMM d, yyyy - h:mm AP")
|
||||
}
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
onClicked: {
|
||||
root.loadChatRequest(chatItem.modelData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
id: newChatBtn
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: Tokens.padding.small
|
||||
anchors.right: parent.right
|
||||
font.pointSize: Math.round(18 * 1.2)
|
||||
icon: "add"
|
||||
padding: 8
|
||||
radius: Tokens.rounding.full
|
||||
|
||||
onClicked: {
|
||||
root.newChatRequest();
|
||||
}
|
||||
|
||||
Elevation {
|
||||
anchors.fill: parent
|
||||
level: newChatBtn.stateLayer.containsMouse ? 4 : 3
|
||||
radius: parent.radius
|
||||
z: -1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import ZShell.Config
|
||||
import ZShell.Llm
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import qs.Modules.Notifications.Sidebar
|
||||
import qs.Components
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property bool chatOpen: false
|
||||
required property Props props
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Tokens.padding.small
|
||||
|
||||
Component.onCompleted: {
|
||||
if (root.props.inChat) {
|
||||
stack.push(chatList);
|
||||
stack.push(chatContent, {
|
||||
"chatData": root.props.chatSession
|
||||
});
|
||||
} else
|
||||
stack.push(chatList);
|
||||
}
|
||||
|
||||
StackView {
|
||||
id: stack
|
||||
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Component {
|
||||
id: chatList
|
||||
|
||||
ChatList {
|
||||
model: ScriptModel {
|
||||
values: Chat.chats.values
|
||||
}
|
||||
|
||||
onLoadChatRequest: chat => {
|
||||
stack.push(chatContent, {
|
||||
"chatData": chat
|
||||
});
|
||||
root.props.inChat = true;
|
||||
root.props.chatSession = chat;
|
||||
}
|
||||
onNewChatRequest: {
|
||||
const data = Chat.chats.insert();
|
||||
stack.push(chatContent, {
|
||||
"chatData": data
|
||||
});
|
||||
root.props.inChat = true;
|
||||
root.props.chatSession = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: chatContent
|
||||
|
||||
ChatContent {
|
||||
onRequestClose: {
|
||||
stack.pop();
|
||||
root.props.inChat = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
import qs.Components
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import ZShell.Config
|
||||
import ZShell.Llm
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Modules.Notifications.Sidebar.Chat
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
@@ -16,15 +20,64 @@ Item {
|
||||
anchors.fill: parent
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
CustomRect {
|
||||
Tabs {
|
||||
Layout.fillWidth: true
|
||||
dashState: root.props
|
||||
nonAnimWidth: layout.width
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
color: Colors.tPalette.m3surfaceContainerLow
|
||||
radius: Tokens.rounding.small
|
||||
|
||||
NotifDock {
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
Item {
|
||||
id: pages
|
||||
|
||||
anchors.fill: parent
|
||||
opacity: root.props.currentTab === 0 ? 1 : 0
|
||||
visible: opacity > 0.01
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
color: Colors.tPalette.m3surfaceContainerLow
|
||||
radius: Tokens.rounding.small
|
||||
|
||||
NotifDock {
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: chatPage
|
||||
|
||||
anchors.fill: parent
|
||||
opacity: visible ? 1 : 0
|
||||
visible: root.props.currentTab === 1
|
||||
z: 1
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
color: Colors.tPalette.m3surfaceContainerLow
|
||||
radius: Tokens.rounding.small
|
||||
|
||||
ChatPanel {
|
||||
anchors.fill: parent
|
||||
props: root.props
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import Quickshell
|
||||
import ZShell.Llm
|
||||
|
||||
PersistentProperties {
|
||||
property ChatSession chatSession
|
||||
property int currentTab: 0
|
||||
property list<string> expandedNotifs: []
|
||||
property bool inChat: false
|
||||
|
||||
reloadableId: "sidebar"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Templates
|
||||
import Quickshell
|
||||
import ZShell.Config
|
||||
import qs.Components
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property alias count: bar.count
|
||||
required property PersistentProperties dashState
|
||||
required property real nonAnimWidth
|
||||
|
||||
implicitHeight: bar.implicitHeight + indicator.implicitHeight + indicator.anchors.topMargin + separator.implicitHeight
|
||||
|
||||
TabBar {
|
||||
id: bar
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
background: null
|
||||
currentIndex: root.dashState.currentTab
|
||||
implicitHeight: contentHeight
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: bar.contentModel
|
||||
}
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: root.state.currentTab = currentIndex
|
||||
|
||||
Tab {
|
||||
iconName: "notifications"
|
||||
text: qsTr("Notifications")
|
||||
}
|
||||
|
||||
Tab {
|
||||
iconName: "chat"
|
||||
text: qsTr("Chat")
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: indicator
|
||||
|
||||
anchors.top: bar.bottom
|
||||
clip: true
|
||||
implicitHeight: 3
|
||||
implicitWidth: bar.currentItem.implicitWidth
|
||||
x: {
|
||||
const tab = bar.currentItem;
|
||||
const width = (root.nonAnimWidth - bar.spacing * (bar.count - 1)) / bar.count;
|
||||
return width * tab.TabBar.index + (width - tab.implicitWidth) / 2;
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on x {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
color: Colors.palette.m3primary
|
||||
implicitHeight: parent.implicitHeight * 2
|
||||
radius: Tokens.rounding.full
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: separator
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: indicator.bottom
|
||||
color: Colors.palette.m3outlineVariant
|
||||
implicitHeight: 1
|
||||
}
|
||||
|
||||
component Tab: TabButton {
|
||||
id: tab
|
||||
|
||||
readonly property bool current: TabBar.tabBar.currentItem === this
|
||||
required property string iconName
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
background: null
|
||||
implicitHeight: implicitContentHeight
|
||||
implicitWidth: implicitContentWidth
|
||||
|
||||
contentItem: Item {
|
||||
implicitHeight: icon.height + label.height
|
||||
implicitWidth: Math.max(icon.width, label.width)
|
||||
|
||||
StateLayer {
|
||||
color: tab.current ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
|
||||
radius: Tokens.rounding.small
|
||||
|
||||
onClicked: root.dashState.currentTab = tab.TabBar.index
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.bottom: label.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurfaceVariant
|
||||
fill: tab.current ? 1 : 0
|
||||
font.pointSize: 18
|
||||
text: tab.iconName
|
||||
|
||||
Behavior on fill {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: tab.current ? Colors.palette.m3primary : Colors.palette.m3onSurfaceVariant
|
||||
text: tab.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,12 @@ Item {
|
||||
required property var visibilities
|
||||
|
||||
anchors.bottomMargin: (-implicitHeight - 5) * offsetScale
|
||||
implicitHeight: content.implicitHeight + 8 * 2
|
||||
clip: chatActive
|
||||
implicitHeight: chatActive ? 21 : content.implicitHeight + 8 * 2
|
||||
implicitWidth: sidebar.width * (1 - sidebar.offsetScale)
|
||||
opacity: 1 - offsetScale
|
||||
opacity: (1 - offsetScale) * chatFade
|
||||
readonly property bool chatActive: sidebar.chatActive
|
||||
property real chatFade: chatActive ? 0 : 1
|
||||
visible: offsetScale < 1
|
||||
|
||||
Behavior on offsetScale {
|
||||
@@ -35,6 +38,20 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on chatFade {
|
||||
Anim {
|
||||
duration: Tokens.anim.durations.expressiveDefaultSpatial
|
||||
easing: Tokens.anim.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: Tokens.anim.durations.expressiveDefaultSpatial
|
||||
easing: Tokens.anim.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: content
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.Components
|
||||
import qs.Components.Toast
|
||||
import ZShell
|
||||
import ZShell.Config
|
||||
import ZShell.Llm
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
@@ -12,6 +16,7 @@ Item {
|
||||
readonly property Props props: Props {
|
||||
}
|
||||
readonly property bool shouldBeActive: root.visibilities.sidebar && Config.sidebar.enabled
|
||||
readonly property bool chatActive: props.currentTab === 1
|
||||
required property var visibilities
|
||||
|
||||
anchors.rightMargin: (-implicitWidth - 5) * offsetScale
|
||||
@@ -26,6 +31,17 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Chat
|
||||
|
||||
function onErrorOccurred(message: string): void {
|
||||
if (root.shouldBeActive && root.chatActive)
|
||||
return;
|
||||
|
||||
Toaster.toast(qsTr("Chat"), message, "error_outline", Toast.Error);
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: content
|
||||
|
||||
|
||||
Reference in New Issue
Block a user