diff --git a/Components/CustomScrollBar.qml b/Components/CustomScrollBar.qml index 810dd2e..6df6539 100644 --- a/Components/CustomScrollBar.qml +++ b/Components/CustomScrollBar.qml @@ -12,7 +12,7 @@ ScrollBar { readonly property bool isHorizontal: flickable && flickable.ScrollBar.horizontal === root readonly property real axisSize: isHorizontal ? flickable.width : flickable.height readonly property real axisContentSize: isHorizontal ? flickable.contentWidth : flickable.contentHeight - readonly property real axisContentPos: isHorizontal ? flickable.contentX : flickable.contentY + readonly property real axisContentPos: isHorizontal ? (flickable.contentX - flickable.originX) : (flickable.contentY - flickable.originY) readonly property real axisLength: isHorizontal ? root.width : root.height readonly property real effectiveSize: Math.max(nonAnimHeight, root.minimumSize) readonly property real effectiveTravel: Math.max(0, 1 - root.effectiveSize) @@ -102,9 +102,9 @@ ScrollBar { var newPos = contentPosFromThumbStart(thumbStart); if (root.isHorizontal) - root.flickable.contentX = newPos; + root.flickable.contentX = newPos + root.flickable.originX; else - root.flickable.contentY = newPos; + root.flickable.contentY = newPos + root.flickable.originY; } function visualThumbStart() { diff --git a/Modules/Notifications/Sidebar/Chat/ChatContent.qml b/Modules/Notifications/Sidebar/Chat/ChatContent.qml index 2413981..85e2a6d 100644 --- a/Modules/Notifications/Sidebar/Chat/ChatContent.qml +++ b/Modules/Notifications/Sidebar/Chat/ChatContent.qml @@ -87,10 +87,6 @@ Item { signal requestClose - function scrollToBottom(): void { - Qt.callLater(() => listLoader.item?.positionViewAtBeginning()); - } - function send(text: string): void { if (text.trim() === "") return; @@ -129,11 +125,15 @@ Item { isRound: true type: IconButton.Tonal + enabled: !ChatState.isWindow + visible: enabled + onClicked: root.requestClose() } CustomText { - Layout.fillWidth: true + Layout.fillWidth: ChatState.isWindow ? false : true + Layout.alignment: ChatState.isWindow ? Qt.AlignCenter : Qt.AlignLeft | Qt.AlignVCenter elide: Text.ElideRight font.pointSize: Tokens.font.size.larger text: qsTr(root.chatData.title) @@ -151,21 +151,20 @@ Item { anchors.topMargin: Tokens.spacing.medium active: root.chatData && root.chatData.loaded - sourceComponent: ListView { + sourceComponent: VerticalFadeListView { id: list property bool userScrolledUp: false property real lastCHeight: 0.0 function scrollToBottom(): void { - scrollAnim.to = 0; scrollAnim.start(); } - cacheBuffer: height * 20 + cacheBuffer: Math.max(height * 20, 0) clip: true - // fadeAmount: 0.05 - // fadeThreshold: Tokens.padding.medium + fadeAmount: 0.05 + fadeThreshold: Tokens.padding.medium model: root.chatData.messagesModel spacing: 0 rotation: 180 @@ -238,6 +237,7 @@ Item { property: "contentY" target: list type: Anim.DefaultEffects + to: list.originY } WheelInverter { @@ -255,7 +255,7 @@ Item { anchors.fill: parent spacing: Tokens.spacing.small - visible: !root.chatData + visible: root.chatData.messageCount < 1 } Item { diff --git a/Modules/Notifications/Sidebar/Chat/ChatDelegate.qml b/Modules/Notifications/Sidebar/Chat/ChatDelegate.qml index 4784f31..1bcb78f 100644 --- a/Modules/Notifications/Sidebar/Chat/ChatDelegate.qml +++ b/Modules/Notifications/Sidebar/Chat/ChatDelegate.qml @@ -2,6 +2,7 @@ import QtQuick import ZShell.Components import ZShell.Config import ZShell.Llm +import qs.Effects import qs.Components import qs.Services @@ -9,12 +10,13 @@ CustomClippingRect { id: root property bool expanded: false + property bool highlighted: false required property ChatSession modelData signal clicked(content: ChatSession) signal remove(content: ChatSession) - color: Colors.tPalette.m3surfaceContainer + color: highlighted ? Qt.alpha(Colors.palette.m3primaryContainer, 0.3) : Qt.alpha(Colors.tPalette.m3surfaceContainer, 0.2) implicitHeight: { let h = 0; @@ -33,6 +35,20 @@ CustomClippingRect { return h; } radius: Tokens.rounding.large + clip: true + + CustomRect { + id: borderHighlight + + anchors.fill: parent + border.width: root.highlighted ? 2 : 0 + border.color: Colors.palette.m3secondary + radius: root.radius - border.width + 1 + + Behavior on border.width { + Anim {} + } + } Behavior on implicitHeight { enabled: titleText.readOnly @@ -49,6 +65,7 @@ CustomClippingRect { anchors.topMargin: Tokens.padding.large font.pointSize: Tokens.font.size.extraLarge text: root.modelData.icon || "check" + color: Colors.palette.m3onSurface } Item { @@ -85,31 +102,73 @@ CustomClippingRect { } } - TextEditBase { - id: titleText + Item { + id: titleWrapper anchors.left: parent.left - anchors.leftMargin: readOnly ? 0 : Tokens.padding.large anchors.top: parent.top - anchors.topMargin: readOnly ? Tokens.padding.small : Tokens.padding.extraSmall * 2 - font.pointSize: Tokens.font.size.normal - text: root.modelData.title - readOnly: true + anchors.right: parent.right + implicitHeight: titleText.implicitHeight + Tokens.padding.small * 2 - Behavior on anchors.leftMargin { - Anim {} - } - Behavior on anchors.topMargin { - Anim {} + layer.enabled: true + layer.effect: OpacityMask { + maskSource: fadeMask } - onEditingFinished: root.modelData.title = text - onReadOnlyChanged: { - if (!readOnly) { - this.forceActiveFocus(); - cursorPosition = text.length; - } else { - root.forceActiveFocus(); + TextFieldBase { + id: titleText + + anchors.left: parent.left + anchors.leftMargin: readOnly ? 0 : Tokens.padding.large + anchors.top: parent.top + anchors.topMargin: readOnly ? Tokens.padding.small : Tokens.padding.extraSmall * 2 + font.pointSize: Tokens.font.size.normal + text: root.modelData.title || qsTr("New chat") + readOnly: true + color: Colors.palette.m3onSurface + + Behavior on anchors.leftMargin { + Anim {} + } + Behavior on anchors.topMargin { + Anim {} + } + + onEditingFinished: root.modelData.title = text + onReadOnlyChanged: { + if (!readOnly) { + this.forceActiveFocus(); + cursorPosition = text.length; + } else { + root.forceActiveFocus(); + } + } + } + } + + CustomRect { + id: fadeMask + + // anchors.top: titleText.top + // anchors.bottom: titleText.bottom + // anchors.left: titleText.left + // anchors.right: parent.right + anchors.fill: titleWrapper + + layer.enabled: true + visible: false + + gradient: Gradient { + orientation: Gradient.Horizontal + + GradientStop { + color: Qt.rgba(1, 1, 1, 1.0) + position: 0.85 + } + + GradientStop { + color: Qt.rgba(1, 1, 1, 0) + position: 1.0 } } } @@ -151,7 +210,7 @@ CustomClippingRect { id: timestamp anchors.left: parent.left - anchors.top: titleText.bottom + anchors.top: title.bottom anchors.topMargin: Tokens.spacing.extraSmall color: Colors.palette.m3onSurfaceVariant font.pointSize: Tokens.font.size.small @@ -188,7 +247,7 @@ CustomClippingRect { font.pointSize: Tokens.font.size.small maximumLineCount: 1 opacity: !root.expanded ? 1 : 0 - text: root.modelData.messagesModel.lastMessage?.activeGeneration.content.replace(/\s+/g, " ").trim() ?? "" + text: root.modelData.messagesModel.lastMessage?.activeGeneration.content.replace(/\s+/g, " ").trim() ?? qsTr("No messages yet") Behavior on y { Anim {} @@ -213,7 +272,7 @@ CustomClippingRect { color: Colors.palette.m3onSurfaceVariant maximumLineCount: 5 opacity: root.expanded ? 1 : 0 - text: root.modelData.messagesModel.lastMessage?.activeGeneration.content ?? "" + text: root.modelData.messagesModel.lastMessage?.activeGeneration.content ?? qsTr("No messages yet") wrapMode: Text.WrapAtWordBoundaryOrAnywhere Behavior on y { @@ -286,7 +345,7 @@ CustomClippingRect { implicitHeight: expandIcon.implicitHeight implicitWidth: expandIcon.implicitHeight radius: Tokens.rounding.full - color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 3) + color: Colors.layer(Colors.palette.m3surfaceContainerHighest, 3) StateLayer { onClicked: root.expanded = !root.expanded @@ -298,6 +357,7 @@ CustomClippingRect { anchors.centerIn: parent anchors.verticalCenterOffset: root.expanded ? -1 : 1 rotation: root.expanded ? 180 : 0 + color: Colors.palette.m3onSurface text: "expand_more" Behavior on anchors.verticalCenterOffset { diff --git a/Modules/Notifications/Sidebar/Chat/ChatList.qml b/Modules/Notifications/Sidebar/Chat/ChatList.qml index 9ca55d9..78be0e3 100644 --- a/Modules/Notifications/Sidebar/Chat/ChatList.qml +++ b/Modules/Notifications/Sidebar/Chat/ChatList.qml @@ -13,9 +13,10 @@ Item { property alias list: list property alias model: list.model + property bool highlight: false signal deleteChatRequest(content: ChatSession) - signal loadChatRequest(content: ChatSession) + signal loadChatRequest(content: ChatSession, index: int) signal newChatRequest CustomText { @@ -40,16 +41,48 @@ Item { anchors.right: parent.right anchors.top: header.bottom anchors.topMargin: Tokens.spacing.medium - cacheBuffer: height * 2 clip: true spacing: Tokens.spacing.medium + add: Transition { + Anim { + from: list.width + property: "x" + } + } + remove: Transition { + Anim { + to: list.width + property: "x" + } + + Anim { + to: 0 + property: "opacity" + } + } + displaced: Transition { + Anim { + property: "y" + } + } + move: Transition { + Anim { + property: "y" + } + } + delegate: ChatDelegate { id: chat - implicitWidth: ListView.view.width + required property int index - onClicked: content => root.loadChatRequest(content) + implicitWidth: ListView.view.width + highlighted: root.highlight && ChatState.chatSession === modelData + + onHighlightedChanged: if (highlighted) + list.currentIndex = index + onClicked: content => root.loadChatRequest(content, index) onRemove: content => root.deleteChatRequest(content) } } @@ -58,12 +91,12 @@ Item { id: newChatBtn anchors.bottom: parent.bottom - anchors.margins: Tokens.padding.small anchors.right: parent.right + anchors.margins: Tokens.padding.small + padding: 8 font.pointSize: Math.round(18 * 1.2) icon: "add" - padding: 8 - radius: Tokens.rounding.full + isRound: true onClicked: { root.newChatRequest(); diff --git a/Modules/Notifications/Sidebar/Chat/ChatPanel.qml b/Modules/Notifications/Sidebar/Chat/ChatPanel.qml index 2c9c44c..ba17161 100644 --- a/Modules/Notifications/Sidebar/Chat/ChatPanel.qml +++ b/Modules/Notifications/Sidebar/Chat/ChatPanel.qml @@ -18,45 +18,35 @@ Item { Component.onCompleted: { if (ChatState.inChat) { - stackLoader.item?.push(chatList); - stackLoader.item?.push(chatContent, { + stack.push(chatList); + stack.push(chatContent, { "chatData": ChatState.chatSession }); } else - stackLoader.item?.push(chatList); + stack.push(chatList); } - Loader { - id: stackLoader - - active: !ChatState.isWindow || root.width <= (ChatState.screen.width / 4) + StackView { + id: stack anchors.fill: parent + } - sourceComponent: StackView { - id: stack + IconButton { + anchors.left: parent.left + anchors.bottom: parent.bottom + icon: "open_in_new" + anchors.margins: Tokens.padding.small + isRound: true + padding: 8 + font.pointSize: Math.round(18 * 1.2) + visible: !ChatState.isWindow + + onClicked: { + Detach.create(); + Visibilities.getForActive().sidebar = false; } } - Loader { - id: sidebarViewLoader - - active: ChatState.isWindow && root.width > (ChatState.screen.width / 4) - anchors.fill: parent - - sourceComponent: SidebarView {} - } - - // IconButton { - // anchors.right: parent.right - // icon: "close" - // visible: !ChatState.isWindow - // - // onClicked: { - // Detach.create(); - // Visibilities.getForActive().sidebar = false; - // } - // } - Component { id: chatList @@ -68,8 +58,8 @@ Item { onDeleteChatRequest: chat => { Chat.chats.remove(chat); } - onLoadChatRequest: chat => { - stackLoader.item?.push(chatContent, { + onLoadChatRequest: (chat, index) => { + stack.push(chatContent, { "chatData": chat }); ChatState.inChat = true; @@ -77,7 +67,7 @@ Item { } onNewChatRequest: { const data = Chat.chats.insert(); - stackLoader.item?.push(chatContent, { + stack.push(chatContent, { "chatData": data }); ChatState.inChat = true; @@ -91,7 +81,7 @@ Item { ChatContent { onRequestClose: { - stackLoader.item?.pop(); + stack.pop(); ChatState.inChat = false; } } diff --git a/Modules/Notifications/Sidebar/Chat/ChatState.qml b/Modules/Notifications/Sidebar/Chat/ChatState.qml index df96065..219a0b9 100644 --- a/Modules/Notifications/Sidebar/Chat/ChatState.qml +++ b/Modules/Notifications/Sidebar/Chat/ChatState.qml @@ -8,6 +8,8 @@ Singleton { id: root property ChatSession chatSession + readonly property string title: chatSession ? chatSession.title : "Chat" + property int currentIdx: -1 property bool inChat: false property bool isWindow: false property ShellScreen screen diff --git a/Modules/Notifications/Sidebar/Chat/Content/ChatHost.qml b/Modules/Notifications/Sidebar/Chat/Content/ChatHost.qml new file mode 100644 index 0000000..0a75bb3 --- /dev/null +++ b/Modules/Notifications/Sidebar/Chat/Content/ChatHost.qml @@ -0,0 +1,132 @@ +import QtQuick +import ZShell.Llm +import ZShell.Config +import qs.Components +import qs.Services +import qs.Modules.Notifications.Sidebar.Chat + +Item { + id: root + + property int animOff + property Item currentItem + property bool loading: false + property int lastIdx: -1 + + readonly property Component conversationComp: ChatContent {} + + function loadConversation(chat): void { + if (currentItem) { + currentItem.destroy(); + currentItem = null; + } + + if (!chat) + return; + + root.loading = true; + + const incubator = root.conversationComp.incubateObject(container, { + chatData: chat + }); + + const attach = () => { + incubator.object.anchors.fill = container; + currentItem = incubator.object; + root.loading = false; + enterAnim.start(); + }; + + if (incubator.status === Component.Ready) + attach(); + else + incubator.onStatusChanged = status => { + if (status === Component.Ready) + attach(); + }; + } + + Item { + id: container + + anchors.fill: parent + layer.enabled: opacity < 1 + objectName: "ConversationContainer" + + Component.onCompleted: { + if (ChatState.chatSession) + root.loadConversation(ChatState.chatSession); + } + } + + LoadingIndicator { + anchors.centerIn: parent + implicitSize: Tokens.font.size.extraLarge * 4 + opacity: root.loading ? 1 : 0 + visible: opacity > 0 + + Behavior on opacity { + Anim {} + } + } + + Connections { + function onChatSessionChanged(): void { + exitAnim.complete(); + enterAnim.complete(); + root.animOff = Tokens.padding.small * (ChatState.currentIdx > root.lastIdx ? 1 : -1); + root.lastIdx = ChatState.currentIdx; + exitAnim.start(); + } + + target: ChatState + } + + SequentialAnimation { + id: exitAnim + + Anim { + property: "opacity" + target: container + to: 0 + type: Anim.DefaultEffects + } + + ScriptAction { + script: root.loadConversation(ChatState.chatSession) + } + } + + SequentialAnimation { + id: enterAnim + + PropertyAction { + property: "topMargin" + target: container.anchors + value: root.animOff + } + + PropertyAction { + property: "bottomMargin" + target: container.anchors + value: -root.animOff + } + + ParallelAnimation { + Anim { + from: 0 + property: "opacity" + target: container + to: 1 + type: Anim.SlowEffects + } + + Anim { + properties: "topMargin,bottomMargin" + target: container.anchors + to: 0 + type: Anim.SlowEffects + } + } + } +} diff --git a/Modules/Notifications/Sidebar/Chat/Content/EmptyBackground.qml b/Modules/Notifications/Sidebar/Chat/Content/EmptyBackground.qml index ee21512..d3370e7 100644 --- a/Modules/Notifications/Sidebar/Chat/Content/EmptyBackground.qml +++ b/Modules/Notifications/Sidebar/Chat/Content/EmptyBackground.qml @@ -5,28 +5,28 @@ import qs.Components import qs.Services ColumnLayout { - id: root + id: root - spacing: Tokens.spacing.small + spacing: Tokens.spacing.small - Item { - Layout.fillHeight: true - } + Item { + Layout.fillHeight: true + } - MaterialIcon { - Layout.alignment: Qt.AlignHCenter - color: Colors.tPalette.m3outlineVariant - font.pointSize: 36 - text: "chat" - } + 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") - } + CustomText { + Layout.alignment: Qt.AlignHCenter + color: Colors.tPalette.m3onSurfaceVariant + text: qsTr("Send a message to get started") + } - Item { - Layout.fillHeight: true - } + Item { + Layout.fillHeight: true + } } diff --git a/Modules/Notifications/Sidebar/Chat/Content/MathBlockView.qml b/Modules/Notifications/Sidebar/Chat/Content/MathBlockView.qml index 856005d..0a8727d 100644 --- a/Modules/Notifications/Sidebar/Chat/Content/MathBlockView.qml +++ b/Modules/Notifications/Sidebar/Chat/Content/MathBlockView.qml @@ -11,7 +11,6 @@ Item { property color mathColor: Colors.palette.m3onSurface property real fontSize: Tokens.font.size.large - implicitWidth: Math.max(fallbackText.implicitWidth, root.width) implicitHeight: Math.max(equationImage.height, fallbackText.contentHeight) + Tokens.padding.small * 2 LlmMathText { diff --git a/Modules/Notifications/Sidebar/Chat/Content/MessageDelegate.qml b/Modules/Notifications/Sidebar/Chat/Content/MessageDelegate.qml index 036c061..7f267b1 100644 --- a/Modules/Notifications/Sidebar/Chat/Content/MessageDelegate.qml +++ b/Modules/Notifications/Sidebar/Chat/Content/MessageDelegate.qml @@ -17,7 +17,8 @@ MouseArea { required property ChatMessage modelData property bool reasoningExpanded: false readonly property var blocks: blockify(current.segments) - property real savedContentY: -1 + property real savedOffset: -1 + property bool stickActive: false function handleReasoningToggle(expanded: bool): void { const view = root.ListView.view; @@ -25,14 +26,16 @@ MouseArea { return; if (expanded) { - root.savedContentY = view.contentY; + root.savedOffset = view.contentY - root.y; root.reasoningExpanded = true; } else { root.reasoningExpanded = false; - if (root.savedContentY !== -1) { - restoreAnim.to = root.savedContentY; + root.stickActive = false; + settleTimer.stop(); + if (!isNaN(root.savedOffset)) { + restoreAnim.to = root.y + root.savedOffset; restoreAnim.restart(); - root.savedContentY = -1; + root.savedOffset = NaN; } } } @@ -69,6 +72,30 @@ MouseArea { preventStealing: false implicitHeight: layout.implicitHeight + Tokens.padding.medium + Timer { + id: settleTimer + interval: 16 + + onTriggered: { + root.stickActive = false; + } + } + + onImplicitHeightChanged: { + if (root.reasoningExpanded) { + root.stickActive = true; + settleTimer.restart(); + + const view = root.ListView.view; + if (!view) + return; + const bottomEdge = root.y + root.height + Tokens.padding.large * 2; + const viewportBottom = view.contentY + view.height; + if (bottomEdge > viewportBottom) + view.contentY = bottomEdge - view.height; + } + } + Behavior on current { id: changeAnim @@ -119,14 +146,6 @@ MouseArea { } } - Binding { - property: "contentY" - restoreMode: Binding.RestoreNone - target: root.ListView.view - value: root.y + root.height + Tokens.padding.large * 2 - root.ListView.view.height - when: root.reasoningExpanded && root.ListView.view && (root.y + root.height + Tokens.padding.large * 2 > root.ListView.view.contentY + root.ListView.view.height) - } - Anim { id: restoreAnim diff --git a/Modules/Notifications/Sidebar/Chat/Detach.qml b/Modules/Notifications/Sidebar/Chat/Detach.qml index 86fe6bd..1064dde 100644 --- a/Modules/Notifications/Sidebar/Chat/Detach.qml +++ b/Modules/Notifications/Sidebar/Chat/Detach.qml @@ -31,7 +31,7 @@ Singleton { minimumSize.height: Config.sidebar.sizes.width minimumSize.width: Config.sidebar.sizes.width surfaceFormat.opaque: false - title: qsTr("ZShell - Chat") + title: qsTr("ZShell - %1").arg(ChatState.title) Component.onCompleted: ChatState.screen = screen onVisibleChanged: { @@ -41,7 +41,7 @@ Singleton { } } - ChatPanel { + SidebarView { id: chat anchors.fill: parent diff --git a/Modules/Notifications/Sidebar/Chat/SidebarView.qml b/Modules/Notifications/Sidebar/Chat/SidebarView.qml index 9b83259..1ffdb0c 100644 --- a/Modules/Notifications/Sidebar/Chat/SidebarView.qml +++ b/Modules/Notifications/Sidebar/Chat/SidebarView.qml @@ -2,90 +2,109 @@ pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts +import QtQuick.Controls import Quickshell import ZShell.Config import ZShell.Llm import qs.Components +import qs.Modules.Notifications.Sidebar.Chat.Content import qs.Services -Item { +RowLayout { id: root property int breakpoint: 700 property alias conversationModel: sidebar.model - property var currentConversation: null // whatever model item is "open" readonly property bool isWide: root.width >= root.breakpoint property bool narrowShowsSidebar: true - function closeConversation() { - narrowShowsSidebar = true; - } - - function openConversation(conv) { - currentConversation = conv; - narrowShowsSidebar = false; // in narrow mode, jump to the chat + function openConversation(conv: ChatSession, index: int): void { } ChatList { id: sidebar - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: root.isWide ? undefined : parent.right - anchors.top: parent.top - implicitWidth: 20 - list.currentIndex: -1 - list.highlightFollowsCurrentItem: false + Layout.fillHeight: true + Layout.margins: Tokens.padding.medium + Layout.rightMargin: Tokens.spacing.medium + Layout.maximumWidth: Config.sidebar.sizes.width + Layout.preferredWidth: root.width / 4 + Layout.minimumWidth: Config.sidebar.sizes.width / 2 + highlight: true - Behavior on anchors.right { - AnchorAnim {} - } Behavior on implicitWidth { Anim {} } - list.highlight: CustomRect { - color: Colors.palette.m3primary - implicitHeight: sidebar.list.currentItem?.implicitHeight ?? 0 - implicitWidth: sidebar.list.width - radius: Tokens.rounding.medium - x: sidebar.list.currentItem?.chat.x ?? 0 - y: sidebar.list.currentItem?.y ?? 0 - - Behavior on y { - Anim { - duration: Tokens.anim.durations.small - easing: Tokens.anim.expressiveDefaultEffects - } - } - } model: ScriptModel { values: Chat.chats.values } - anchors.onRightChanged: { - if (anchors.right === undefined) - implicitWidth = 20; - } onLoadChatRequest: (chat, index) => { - sidebar.list.currentIndex = index; - root.openConversation(chat); + ChatState.currentIdx = index; + ChatState.chatSession = chat; + } + + onDeleteChatRequest: chat => { + Chat.chats.remove(chat); + ChatState.currentIdx = -1; + ChatState.chatSession = null; + } + onNewChatRequest: { + const data = Chat.chats.insert(); + ChatState.currentIdx = 0; + ChatState.chatSession = data; } } - CustomClippingWrapperRect { - anchors.bottom: parent.bottom - anchors.left: sidebar.right - anchors.right: parent.right - anchors.top: parent.top - child: conversationView + Item { + Layout.margins: Tokens.padding.extraLarge + Layout.leftMargin: Tokens.spacing.extraLarge + Layout.topMargin: Tokens.padding.large + Layout.preferredWidth: Config.sidebar.sizes.width * 2 + Layout.fillWidth: true + Layout.fillHeight: true + + ChatHost { + id: convHost + + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + implicitWidth: Math.min(parent.width, 800) + clip: true + + onImplicitWidthChanged: console.log(implicitWidth) + } + + 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 + } + } } - ChatContent { + Component { id: conversationView - anchors.fill: root - anchors.leftMargin: root.isWide ? Config.sidebar.sizes.width / 2 : 0 - chatData: root.currentConversation + ChatContent {} } } diff --git a/Modules/Settings/Common/TimeDialogRow.qml b/Modules/Settings/Common/TimeDialogRow.qml index 25bc7cb..79d6035 100644 --- a/Modules/Settings/Common/TimeDialogRow.qml +++ b/Modules/Settings/Common/TimeDialogRow.qml @@ -178,9 +178,6 @@ Item { sourceComponent: MouseArea { onWheel: event => event.accepted = true preventStealing: true - acceptedButtons: Qt.NoButton - cursorShape: undefined - enabled: dialogContent.opacity > 0 clip: true implicitWidth: innerLayout.implicitWidth + innerLayout.anchors.margins * 2 implicitHeight: innerLayout.implicitHeight + innerLayout.anchors.margins + innerLayout.anchors.bottomMargin diff --git a/Modules/Settings/Common/TimeDialogToggle.qml b/Modules/Settings/Common/TimeDialogToggle.qml index 0b540fa..0cd1555 100644 --- a/Modules/Settings/Common/TimeDialogToggle.qml +++ b/Modules/Settings/Common/TimeDialogToggle.qml @@ -16,7 +16,7 @@ ConnectedRect { property string iconText: "open_in_new" property int verticalPadding: Tokens.padding.small - signal clicked + signal clicked(event: MouseEvent) signal toggled(checked: bool) Layout.fillWidth: true @@ -56,7 +56,7 @@ ConnectedRect { } StateLayer { - onClicked: root.clicked() + onClicked: e => root.clicked(e) } CustomSwitch {