chore: restructure llm plugin to be more coherent & fix various bugs in llm ui
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 30s
Rust / build (pull_request) Successful in 49s
Python / verify (pull_request) Successful in 1m35s
Rust / fmt (pull_request) Successful in 36s
Rust / clippy (pull_request) Successful in 54s
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 30s
Rust / build (pull_request) Successful in 49s
Python / verify (pull_request) Successful in 1m35s
Rust / fmt (pull_request) Successful in 36s
Rust / clippy (pull_request) Successful in 54s
This commit is contained in:
@@ -0,0 +1,400 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import ZShell.Config
|
||||
import ZShell.Components
|
||||
import ZShell.Llm
|
||||
import ZShell.Blobs
|
||||
import qs.Components
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property Item rootParent
|
||||
required property ChatGeneration current
|
||||
|
||||
implicitHeight: approvalLayout.implicitHeight + approvalLayout.anchors.margins * 2
|
||||
|
||||
CustomRect {
|
||||
id: approvalBg
|
||||
|
||||
anchors.fill: parent
|
||||
radius: Tokens.rounding.medium
|
||||
color: Colors.palette.m3surface
|
||||
border.width: 1
|
||||
border.color: Colors.palette.m3outlineVariant
|
||||
|
||||
ColumnLayout {
|
||||
id: approvalLayout
|
||||
anchors.fill: parent
|
||||
anchors.margins: Tokens.padding.medium
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
CustomText {
|
||||
id: requestHeader
|
||||
text: qsTr("Tool call request")
|
||||
font: Tokens.font.headline.medium
|
||||
color: Colors.palette.m3onSurface
|
||||
}
|
||||
|
||||
Item {
|
||||
id: toolsBg
|
||||
|
||||
property bool open: false
|
||||
property int maxHeight: root.rootParent.height - Tokens.padding.medium * 2
|
||||
property int openHeight: Math.min(toolsContent.item?.implicitHeight, maxHeight)
|
||||
property int openWidth: root.rootParent.width - Tokens.padding.medium * 2
|
||||
property real openHeightProg: openHeight / maxHeight
|
||||
property int collapsedY: 0
|
||||
|
||||
implicitHeight: toolList.implicitHeight + toolList.anchors.margins * 2
|
||||
z: open || dialogTransition.running ? 2 : 0
|
||||
Layout.fillWidth: true
|
||||
|
||||
function reparentWrapper(): void {
|
||||
const newParent = open ? root.rootParent : toolsBg;
|
||||
const c = toolsWrapper.parent.mapToItem(newParent, toolsWrapper.x + toolsWrapper.width / 2, toolsWrapper.y + toolsWrapper.height / 2);
|
||||
toolsBg.collapsedY = toolsWrapper.y;
|
||||
toolsWrapper.parent = newParent;
|
||||
toolsWrapper.x = c.x - toolsWrapper.width / 2;
|
||||
toolsWrapper.y = c.y - toolsWrapper.height / 2;
|
||||
}
|
||||
|
||||
onOpenChanged: {
|
||||
if (open)
|
||||
root.rootParent.interactive = false;
|
||||
else
|
||||
root.rootParent.interactive = true;
|
||||
}
|
||||
|
||||
BlobGroup {
|
||||
id: blobGroup
|
||||
|
||||
color: toolsBg.open ? Colors.palette.m3surfaceContainerHighest : Colors.tPalette.m3primaryContainer
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: backdrop
|
||||
|
||||
anchors.fill: parent
|
||||
enabled: false
|
||||
hoverEnabled: enabled
|
||||
preventStealing: true
|
||||
parent: toolsBg.open ? root.rootParent : toolsBg
|
||||
|
||||
onClicked: toolsBg.open = false
|
||||
}
|
||||
|
||||
Item {
|
||||
id: toolsWrapper
|
||||
|
||||
width: toolsBg.width
|
||||
height: toolBox.implicitHeight
|
||||
rotation: parent === root.rootParent ? 180 : 0
|
||||
z: 1
|
||||
|
||||
states: State {
|
||||
name: "open"
|
||||
when: toolsBg.open
|
||||
|
||||
PropertyChanges {
|
||||
backdrop.enabled: true
|
||||
toolsBackground.bottomLeftRadius: Tokens.rounding.largeIncreased
|
||||
toolsBackground.bottomRightRadius: Tokens.rounding.largeIncreased
|
||||
toolsBackground.topRightRadius: Tokens.rounding.largeIncreased
|
||||
toolsBackground.topLeftRadius: Tokens.rounding.largeIncreased
|
||||
toolsContent.opacity: 1
|
||||
toolsWrapper.height: toolsBg.openHeight
|
||||
toolsWrapper.width: toolsBg.openWidth
|
||||
toolsWrapper.x: (root.rootParent.width - toolsBg.openWidth) / 2
|
||||
toolsWrapper.y: (confirmationRow.implicitHeight + Tokens.spacing.small + Tokens.padding.medium) * (1 - toolsBg.openHeightProg) + Tokens.padding.medium
|
||||
elevation.opacity: 1
|
||||
toolBox.opacity: 0
|
||||
}
|
||||
}
|
||||
transitions: Transition {
|
||||
id: dialogTransition
|
||||
|
||||
SequentialAnimation {
|
||||
ScriptAction {
|
||||
script: toolsBg.reparentWrapper()
|
||||
}
|
||||
|
||||
Anim {
|
||||
properties: "x,y"
|
||||
}
|
||||
}
|
||||
|
||||
PropertyAction {
|
||||
property: "enabled"
|
||||
}
|
||||
|
||||
Anim {
|
||||
properties: "opacity,topLeftRadius,topRightRadius,bottomLeftRadius,bottomRightRadius"
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
|
||||
Anim {
|
||||
properties: "width,height"
|
||||
}
|
||||
}
|
||||
|
||||
Elevation {
|
||||
id: elevation
|
||||
|
||||
anchors.fill: parent
|
||||
bottomLeftRadius: toolsBackground.bottomLeftRadius
|
||||
bottomRightRadius: toolsBackground.bottomRightRadius
|
||||
level: 4
|
||||
opacity: 0
|
||||
radius: toolsBackground.radius
|
||||
|
||||
transform: Matrix4x4 {
|
||||
matrix: toolsBackground.deformMatrix
|
||||
}
|
||||
}
|
||||
|
||||
BlobRect {
|
||||
id: toolsBackground
|
||||
|
||||
anchors.fill: parent
|
||||
bottomLeftRadius: toolsBg.open ? Tokens.rounding.largeIncreased : Tokens.rounding.small
|
||||
bottomRightRadius: toolsBg.open ? Tokens.rounding.largeIncreased : Tokens.rounding.small
|
||||
topRightRadius: toolsBg.open ? Tokens.rounding.largeIncreased : Tokens.rounding.small
|
||||
topLeftRadius: toolsBg.open ? Tokens.rounding.largeIncreased : Tokens.rounding.small
|
||||
deformScale: 0
|
||||
group: blobGroup
|
||||
opacity: blobGroup.color.a * (root.enabled ? 1 : 0.5)
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: toolBox
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: Math.min(implicitHeight, parent.height)
|
||||
implicitHeight: toolList.implicitHeight + Tokens.padding.medium * 2
|
||||
radius: Tokens.rounding.small
|
||||
|
||||
StateLayer {
|
||||
onClicked: {
|
||||
toolsBg.open = true;
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: toolList
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: Tokens.padding.medium
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
model: root.current.pendingToolCalls?.slice(0, 4) ?? []
|
||||
|
||||
delegate: RowLayout {
|
||||
id: toolText
|
||||
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
function toolTarget(seg: var): string {
|
||||
var a = null;
|
||||
try {
|
||||
a = JSON.parse(seg.arguments);
|
||||
} catch (e) {}
|
||||
if (a && typeof a === "object") {
|
||||
const v = a.url ?? a.path;
|
||||
if (v !== undefined && v !== null)
|
||||
return String(v);
|
||||
}
|
||||
return String(seg.arguments);
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
color: Colors.palette.m3onPrimaryContainer
|
||||
text: toolText.modelData.name === "webfetch" ? "web" : "text_snippet"
|
||||
fill: 1
|
||||
}
|
||||
|
||||
CustomText {
|
||||
color: Colors.palette.m3onPrimaryContainer
|
||||
text: qsTr("%1").arg(toolText.modelData.name)
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
color: Colors.palette.m3onPrimaryContainer
|
||||
text: "arrow_right_alt"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
color: Colors.palette.m3onPrimaryContainer
|
||||
text: qsTr("%1").arg(toolText.toolTarget(toolText.modelData))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
color: Colors.palette.m3onPrimaryContainer
|
||||
text: qsTr("And %1 more").arg(root.current.pendingToolCalls.slice(4).length)
|
||||
visible: repeater.count < root.current.pendingToolCalls.length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: toolsContent
|
||||
|
||||
active: opacity > 0
|
||||
anchors.fill: parent
|
||||
asynchronous: false
|
||||
opacity: 0
|
||||
|
||||
sourceComponent: MouseArea {
|
||||
onWheel: event => event.accepted = true
|
||||
preventStealing: true
|
||||
implicitHeight: innerLayout.contentHeight + innerLayout.anchors.margins * 2
|
||||
|
||||
VerticalFadeListView {
|
||||
id: innerLayout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Tokens.padding.small
|
||||
cacheBuffer: height * 10
|
||||
fadeAmount: 0.1
|
||||
fadeEnabled: interactive
|
||||
spacing: 0
|
||||
clip: true
|
||||
interactive: contentHeight > toolsBg.maxHeight
|
||||
|
||||
model: root.current.pendingToolCalls ?? []
|
||||
|
||||
delegate: Item {
|
||||
id: toolCall
|
||||
|
||||
required property var modelData
|
||||
property bool expanded: false
|
||||
|
||||
implicitWidth: CustomListView.view.width
|
||||
implicitHeight: collapsedRow.implicitHeight + collapsedRow.anchors.margins + (expanded ? expandedTool.implicitHeight + expandedTool.anchors.margins + expandedTool.anchors.topMargin : collapsedRow.anchors.margins)
|
||||
|
||||
function toolTarget(seg: var): string {
|
||||
var a = null;
|
||||
try {
|
||||
a = JSON.parse(seg.arguments);
|
||||
} catch (e) {}
|
||||
if (a && typeof a === "object") {
|
||||
const v = a.url ?? a.path;
|
||||
if (v !== undefined && v !== null)
|
||||
return String(v);
|
||||
}
|
||||
return String(seg.arguments);
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: collapsedRow
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Tokens.padding.medium
|
||||
|
||||
IconButton {
|
||||
id: toolExpandBtn
|
||||
|
||||
font: Tokens.font.icon.medium
|
||||
icon: "keyboard_arrow_down"
|
||||
inactiveOnColor: hovered ? Colors.palette.m3onSurface : Colors.palette.m3outline
|
||||
rotation: toolCall.expanded ? 180 : 0
|
||||
type: IconButton.Text
|
||||
|
||||
Behavior on rotation {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
onClicked: toolCall.expanded = !toolCall.expanded
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: preview
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: toolCall.modelData.name
|
||||
}
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
id: expandedTool
|
||||
|
||||
anchors.top: collapsedRow.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: Tokens.padding.medium
|
||||
anchors.topMargin: Tokens.spacing.medium
|
||||
|
||||
implicitHeight: fullToolCall.implicitHeight + fullToolCall.anchors.margins * 2
|
||||
radius: Tokens.rounding.medium
|
||||
color: Colors.palette.m3surfaceContainerLow
|
||||
|
||||
CustomText {
|
||||
id: fullToolCall
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Tokens.padding.medium
|
||||
|
||||
text: toolCall.toolTarget(toolCall.modelData)
|
||||
wrapMode: CustomText.WrapAtWordBoundaryOrAnywhere
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ButtonRow {
|
||||
id: confirmationRow
|
||||
Layout.alignment: Qt.AlignRight
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
IconTextButton {
|
||||
icon: "check_circle"
|
||||
text: qsTr("Allow")
|
||||
horizontalPadding: Tokens.padding.large
|
||||
verticalPadding: Tokens.padding.small
|
||||
isRound: true
|
||||
shapeMorph: true
|
||||
onClicked: root.current.approveTools()
|
||||
}
|
||||
|
||||
IconTextButton {
|
||||
icon: "cancel"
|
||||
text: qsTr("Deny")
|
||||
isRound: true
|
||||
shapeMorph: true
|
||||
inactiveColor: Colors.palette.m3error
|
||||
inactiveOnColor: Colors.palette.m3onError
|
||||
horizontalPadding: Tokens.padding.large
|
||||
verticalPadding: Tokens.padding.small
|
||||
onClicked: root.current.denyTools()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user