rotated view + wheel inverter
This commit is contained in:
@@ -33,6 +33,12 @@ Item {
|
||||
implicitHeight: root.isUser ? msgText.contentHeight + Tokens.padding.medium * 2 : blocks.implicitHeight + blocks.anchors.topMargin * 2
|
||||
anchors.right: root.isUser ? parent.right : undefined
|
||||
|
||||
Behavior on implicitHeight {
|
||||
// enabled: root.segment.running
|
||||
|
||||
Anim {}
|
||||
}
|
||||
|
||||
// User messages stay a plain editable text field.
|
||||
TextEditBase {
|
||||
id: msgText
|
||||
@@ -103,12 +109,13 @@ Item {
|
||||
Actions {
|
||||
id: actionsRow
|
||||
|
||||
readonly property bool shouldBeActive: (root.segment.type === LlmSegment.Type.Content) && !root.segment.running && root.repeater.count === (root.index + 1)
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: (implicitWidth > bubble.implicitWidth) ? undefined : bubble.right
|
||||
anchors.top: bubble.bottom
|
||||
visible: {
|
||||
return (root.segment.type === LlmSegment.Type.Content) && root.segment.status !== LlmSegment.Status.Running && root.repeater.count === (root.index + 1);
|
||||
}
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
visible: opacity > 0
|
||||
anchors.topMargin: Tokens.spacing.medium
|
||||
edit: msgText
|
||||
hovered: root.hovered
|
||||
@@ -116,5 +123,9 @@ Item {
|
||||
segment: root.segment
|
||||
current: root.current
|
||||
message: root.message
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import qs.Services
|
||||
MouseArea {
|
||||
id: root
|
||||
|
||||
readonly property ChatGeneration current: modelData.activeGeneration
|
||||
property ChatGeneration current: modelData.activeGeneration
|
||||
required property int index
|
||||
readonly property bool isUser: modelData.role === ChatMessage.Role.User
|
||||
required property ChatMessage modelData
|
||||
@@ -60,22 +60,79 @@ MouseArea {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
implicitWidth: ListView.view.width
|
||||
hoverEnabled: true
|
||||
preventStealing: true
|
||||
preventStealing: false
|
||||
implicitHeight: layout.implicitHeight + Tokens.padding.medium
|
||||
|
||||
Binding {
|
||||
property: "contentY"
|
||||
restoreMode: Binding.RestoreNone
|
||||
target: root.ListView.view
|
||||
value: root.y - Tokens.padding.large * 2
|
||||
when: root.reasoningExpanded && root.ListView.view && ((root.y - Tokens.padding.large * 2) < root.ListView.view.contentY)
|
||||
Behavior on current {
|
||||
id: changeAnim
|
||||
|
||||
property var object: targetProperty
|
||||
readonly property bool next: {
|
||||
const cond = root.modelData.generations.indexOf(current) < root.modelData.generations.indexOf(changeAnim.targetValue);
|
||||
return cond;
|
||||
}
|
||||
|
||||
animation: SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: root
|
||||
property: "x"
|
||||
to: changeAnim.next ? -root.width / 2 : root.width / 2
|
||||
from: 0
|
||||
}
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "opacity"
|
||||
from: 1
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
|
||||
PropertyAction {}
|
||||
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: root
|
||||
property: "x"
|
||||
from: changeAnim.next ? root.width / 2 : -root.width / 2
|
||||
to: 0
|
||||
}
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Behavior on implicitHeight {
|
||||
// enabled: !root.isUser && root.current.streaming
|
||||
//
|
||||
// Anim {}
|
||||
// }
|
||||
|
||||
onCurrentChanged: {
|
||||
console.log(modelData.generations.indexOf(current));
|
||||
}
|
||||
|
||||
// Binding {
|
||||
// property: "contentY"
|
||||
// restoreMode: Binding.RestoreNone
|
||||
// target: root.ListView.view
|
||||
// value: root.y - Tokens.padding.large * 2
|
||||
// when: root.reasoningExpanded && root.ListView.view && ((root.y - Tokens.padding.large * 2) < root.ListView.view.contentY)
|
||||
// }
|
||||
|
||||
Anim {
|
||||
id: restoreAnim
|
||||
|
||||
@@ -91,6 +148,20 @@ MouseArea {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
// onImplicitHeightChanged: console.log("LAYOUT:", layout.implicitHeight)
|
||||
|
||||
// add: Transition {
|
||||
// Anim {
|
||||
// from: 10
|
||||
// property: "y"
|
||||
// }
|
||||
// }
|
||||
// move: Transition {
|
||||
// Anim {
|
||||
// property: "y"
|
||||
// }
|
||||
// }
|
||||
|
||||
Repeater {
|
||||
id: segmentRep
|
||||
|
||||
@@ -105,12 +176,8 @@ MouseArea {
|
||||
roleValue: "process"
|
||||
|
||||
delegate: ProcessBlock {
|
||||
required property int index
|
||||
required property var modelData
|
||||
|
||||
segments: modelData.segments
|
||||
isActive: index === root.blocks.length - 1
|
||||
width: root.width
|
||||
blocks: root.blocks
|
||||
|
||||
onExpandedChanged: root.handleReasoningToggle(expanded)
|
||||
}
|
||||
|
||||
@@ -5,92 +5,106 @@ import ZShell.Llm
|
||||
import qs.Components
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
CustomClippingRect {
|
||||
id: root
|
||||
|
||||
required property var segments
|
||||
required property bool isActive
|
||||
required property int index
|
||||
required property var modelData
|
||||
required property var blocks
|
||||
readonly property var segments: modelData.segments
|
||||
readonly property bool isActive: index === blocks.length - 1
|
||||
property bool expanded: false
|
||||
readonly property LlmSegment lastSegment: root.segments[root.segments.length - 1]
|
||||
readonly property real totalElapsedMs: root.segments.reduce((sum, s) => sum + (s.elapsedMs ?? 0), 0)
|
||||
|
||||
implicitHeight: expandedRect.implicitHeight + collapsedText.implicitHeight + expandedRect.anchors.topMargin
|
||||
implicitHeight: expanded ? expandedRect.implicitHeight + layout.implicitHeight + expandedRect.anchors.topMargin * 2 : layout.implicitHeight + Tokens.spacing.small
|
||||
|
||||
LoadingIndicator {
|
||||
id: spinnerReasoning
|
||||
|
||||
anchors.centerIn: expandBtn
|
||||
implicitSize: collapsedText.implicitHeight
|
||||
opacity: root.isActive ? 1 : 0
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
id: expandBtn
|
||||
RowLayout {
|
||||
id: layout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: collapsedText.verticalCenter
|
||||
font.pointSize: Tokens.font.size.large
|
||||
anchors.topMargin: 0
|
||||
icon: "keyboard_arrow_down"
|
||||
inactiveOnColor: hovered ? Colors.palette.m3onSurface : Colors.palette.m3outline
|
||||
opacity: root.isActive ? 0 : 1
|
||||
rotation: root.expanded ? 180 : 0
|
||||
type: IconButton.Text
|
||||
|
||||
Behavior on rotation {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
onClicked: root.expanded = !root.expanded
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: collapsedText
|
||||
|
||||
anchors.left: 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: {
|
||||
if (root.isActive) {
|
||||
if (root.lastSegment.type === LlmSegment.Type.ToolCall)
|
||||
return qsTr("Using %1...").arg(root.lastSegment.name);
|
||||
return qsTr("Thinking...");
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Item {
|
||||
implicitHeight: expandBtn.implicitHeight
|
||||
implicitWidth: expandBtn.implicitWidth
|
||||
|
||||
LoadingIndicator {
|
||||
id: spinnerReasoning
|
||||
|
||||
implicitSize: collapsedText.implicitHeight
|
||||
anchors.centerIn: parent
|
||||
opacity: root.isActive ? 1 : 0
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
id: expandBtn
|
||||
|
||||
font.pointSize: Tokens.font.size.large
|
||||
icon: "keyboard_arrow_down"
|
||||
inactiveOnColor: hovered ? Colors.palette.m3onSurface : Colors.palette.m3outline
|
||||
anchors.centerIn: parent
|
||||
opacity: root.isActive ? 0 : 1
|
||||
rotation: root.expanded ? 180 : 0
|
||||
type: IconButton.Text
|
||||
|
||||
Behavior on rotation {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
onClicked: root.expanded = !root.expanded
|
||||
}
|
||||
return qsTr("Worked for %1s").arg((root.totalElapsedMs / 1000).toFixed(1));
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
CustomText {
|
||||
id: collapsedText
|
||||
|
||||
color: Colors.palette.m3outline
|
||||
font.pointSize: Tokens.font.size.small
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Tokens.spacing.medium
|
||||
text: {
|
||||
if (root.isActive) {
|
||||
if (root.lastSegment.type === LlmSegment.Type.ToolCall)
|
||||
return qsTr("Using %1...").arg(root.lastSegment.name);
|
||||
return qsTr("Thinking...");
|
||||
}
|
||||
return qsTr("Worked for %1s").arg((root.totalElapsedMs / 1000).toFixed(1));
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
CustomRect {
|
||||
id: expandedRect
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: collapsedText.bottom
|
||||
anchors.topMargin: Tokens.spacing.medium
|
||||
anchors.top: layout.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.topMargin: Tokens.spacing.small
|
||||
anchors.bottomMargin: Tokens.spacing.small
|
||||
color: Colors.palette.m3surfaceContainerLow
|
||||
implicitHeight: root.expanded ? expandedContent.contentHeight + expandedContent.anchors.margins * 2 : 0
|
||||
implicitHeight: expandedContent.contentHeight + expandedContent.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 {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user