import QtQuick import ZShell.Config import ZShell.Llm import qs.Components import qs.Services Item { id: root required property ChatGeneration current property bool expanded: false readonly property bool isReasoning: root.current.reasoningActive || root.current.content === "" implicitHeight: expandedRect.implicitHeight + collapsedText.implicitHeight + expandedRect.anchors.topMargin LoadingIndicator { id: spinnerReasoning anchors.left: parent.left anchors.margins: Tokens.padding.medium anchors.verticalCenter: parent.verticalCenter implicitSize: collapsedText.implicitHeight opacity: root.isReasoning ? 1 : 0 visible: opacity > 0 Behavior on opacity { Anim { } } } IconButton { id: expandBtn anchors.left: parent.left anchors.margins: Tokens.padding.medium anchors.verticalCenter: collapsedText.verticalCenter font.pointSize: Tokens.font.size.large icon: "keyboard_arrow_down" inactiveOnColor: hovered ? Colors.palette.m3onSurface : Colors.palette.m3outline opacity: root.isReasoning ? 0 : 1 rotation: root.expanded ? 180 : 0 type: IconButton.Text Behavior on rotation { Anim { } } onClicked: root.expanded = !root.expanded } CustomText { id: collapsedText anchors.left: root.current.reasoningActive ? spinnerReasoning.right : expandBtn.right anchors.margins: Tokens.padding.medium anchors.right: parent.right anchors.top: parent.top anchors.topMargin: 0 color: Colors.palette.m3outline font.pointSize: Tokens.font.size.small text: root.current.reasoningActive ? qsTr("Thinking...") : qsTr("Thought for %1s").arg((root.current.reasoningElapsedMs / 1000).toFixed(1)) Behavior on opacity { Anim { } } } CustomClippingRect { id: expandedRect anchors.left: parent.left anchors.right: parent.right anchors.top: collapsedText.bottom anchors.topMargin: Tokens.spacing.medium color: Colors.palette.m3surfaceContainerLow implicitHeight: root.expanded ? expandedText.implicitHeight + expandedText.anchors.margins * 2 : -anchors.topMargin implicitWidth: expandedText.implicitWidth + expandedText.anchors.margins * 2 opacity: root.expanded ? 1 : 0 radius: Tokens.rounding.medium visible: opacity > 0 Behavior on implicitHeight { Anim { type: Anim.DefaultEffects } } Behavior on opacity { Anim { } } CustomText { id: expandedText anchors.fill: parent anchors.margins: Tokens.padding.medium color: Colors.palette.m3onSurface font.pointSize: Tokens.font.size.small text: root.current.reasoning textFormat: CustomText.MarkdownText wrapMode: Text.WordWrap } } }