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.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.icon.extraLarge.pointSize * (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 wideExpandedSidebar: isWide && !ChatState.shrinkSidebarOverride readonly property bool narrowExpandedSidebar: !isWide && ChatState.narrowSidebarExpanded 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.wideExpandedSidebar || root.narrowExpandedSidebar ? Config.sidebar.sizes.width : root.iconSize highlight: true slim: !root.wideExpandedSidebar && !root.narrowExpandedSidebar z: 1 Behavior on implicitWidth { Anim { type: Anim.DefaultEffects } } model: LlmClient.chats onLoadChatRequest: (chat, index) => { ChatState.currentIdx = index; ChatState.chatSession = chat; } onDeleteChatRequest: chat => { const wasCurrent = chat === ChatState.chatSession; LlmClient.chats.remove(chat); if (wasCurrent) ChatState.openDraft(); } onNewChatRequest: ChatState.openDraft() onRequestExpand: { ChatState.narrowSidebarExpanded = true; } } Item { anchors.fill: parent anchors.leftMargin: invertedRect.borderLeft anchors.topMargin: invertedRect.borderTop anchors.bottomMargin: invertedRect.borderBottom anchors.rightMargin: invertedRect.borderRight clip: true children: [contentArea, dim] } CustomRect { id: dim anchors.fill: parent radius: invertedRect.radius color: Colors.palette.m3shadow opacity: root.narrowExpandedSidebar ? 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.right: parent.right implicitWidth: root.width - leftMargin - sidebar.anchors.margins - Tokens.spacing.medium * 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.fill: parent clip: true onLoadingChanged: if (!loading) root.chatOpen = true } MouseArea { id: dimArea anchors.fill: parent preventStealing: true enabled: root.narrowExpandedSidebar cursorShape: undefined onClicked: { const x = mouseX; const margin = root.width - contentArea.implicitWidth - contentArea.anchors.margins * 2; if (x > invertedRect.borderLeft - margin) { ChatState.narrowSidebarExpanded = false; ChatState.fabExpanded = false; } } HoverHandler { cursorShape: dimArea.enabled ? Qt.ArrowCursor : undefined enabled: dimArea.enabled } } } 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 {} } }