pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts import QtQuick.Controls import Quickshell import ZShell.Blobs import ZShell.Config import ZShell.Llm import qs.Components import qs.Modules.Notifications.Sidebar.Chat.Content import qs.Services Item { id: root property int breakpoint: Math.round(Config.sidebar.sizes.width * 2) + Tokens.spacing.medium * 2 + Tokens.padding.medium * 3 property color blobColor: Colors.tPalette.m3surfaceContainerLow readonly property int iconSize: Math.round(Tokens.font.size.extraLarge * (96 / 72)) + Tokens.padding.large * 2 property alias conversationModel: sidebar.model property bool chatOpen: ChatState.chatSession !== null readonly property bool isWide: root.width >= root.breakpoint readonly property bool showList: isWide || !chatOpen readonly property bool showContent: !isWide && chatOpen function openConversation(conv: ChatSession, index: int): void { } BlobGroup { id: blobGroup color: root.blobColor smoothing: Tokens.rounding.medium } BlobInvertedRect { id: invertedRect anchors.fill: parent borderBottom: Tokens.padding.small borderLeft: sidebar.implicitWidth + sidebar.anchors.margins + Tokens.spacing.medium borderRight: Tokens.padding.small borderTop: Tokens.padding.small group: blobGroup opacity: root.blobColor.a radius: Tokens.rounding.largeIncreased } ChatList { id: sidebar anchors.top: parent.top anchors.left: parent.left anchors.bottom: parent.bottom anchors.margins: Tokens.padding.medium implicitWidth: root.isWide || ChatState.narrowSidebarExpanded ? Config.sidebar.sizes.width : root.iconSize highlight: true slim: !root.isWide && !ChatState.narrowSidebarExpanded z: 1 Behavior on implicitWidth { Anim { type: Anim.DefaultEffects } } model: ScriptModel { values: Chat.chats.values } onLoadChatRequest: (chat, index) => { ChatState.currentIdx = index; ChatState.chatSession = chat; } onDeleteChatRequest: chat => { if (chat === ChatState.chatSession) { ChatState.chatSession = null; ChatState.currentIdx = -1; } Chat.chats.remove(chat); } onNewChatRequest: { const data = Chat.chats.insert(); ChatState.currentIdx = 0; ChatState.chatSession = data; } onRequestExpand: { ChatState.narrowSidebarExpanded = true; } } CustomClippingRect { anchors.fill: parent anchors.leftMargin: invertedRect.borderLeft anchors.topMargin: invertedRect.borderTop anchors.bottomMargin: invertedRect.borderBottom anchors.rightMargin: invertedRect.borderRight radius: invertedRect.radius children: [contentArea, dim] } CustomRect { id: dim anchors.fill: parent color: Colors.palette.m3shadow opacity: ChatState.narrowSidebarExpanded && !root.isWide ? 0.3 : 0 z: 1 Behavior on opacity { Anim { type: Anim.DefaultEffects } } } Item { id: contentArea property int leftMargin: sidebar.implicitWidth z: 0 anchors.top: parent.top anchors.bottom: parent.bottom anchors.margins: Tokens.padding.medium anchors.right: parent.right implicitWidth: root.width - leftMargin - sidebar.anchors.margins - Tokens.spacing.medium * 2 - anchors.margins * 2 states: State { name: "wide" when: !root.isWide PropertyChanges { contentArea.leftMargin: root.iconSize } } transitions: Transition { to: "wide" Anim { type: Anim.DefaultEffects property: "leftMargin" } } ChatHost { id: convHost anchors.top: parent.top anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter implicitWidth: Math.max(Math.min(parent.width, 800), Config.sidebar.sizes.width) clip: true onLoadingChanged: if (!loading) root.chatOpen = true } ColumnLayout { id: noChatLayout anchors.centerIn: parent opacity: ChatState.chatSession ? 0 : 1 visible: opacity > 0 Behavior on opacity { Anim {} } MaterialIcon { Layout.alignment: Qt.AlignCenter text: "chat_bubble_off" font.pointSize: Tokens.font.size.extraLarge color: Colors.tPalette.m3onSurfaceVariant } CustomText { Layout.alignment: Qt.AlignCenter text: qsTr("Start or create a chat") color: Colors.tPalette.m3onSurfaceVariant } } MouseArea { anchors.fill: parent preventStealing: true enabled: !root.isWide && ChatState.narrowSidebarExpanded onClicked: ChatState.narrowSidebarExpanded = false } } Binding { target: ChatState property: "windowIsWide" value: root.showList } Binding { target: ChatState property: "isWide" value: root.isWide } Binding { target: ChatState value: contentArea.leftMargin === root.iconSize property: "settledSlim" } Binding { target: ChatState value: contentArea.leftMargin === sidebar.implicitWidth property: "settledWide" } Component { id: conversationView ChatContent {} } }