aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/assistant/Card.qml
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/assistant/Card.qml')
-rw-r--r--desktop/assistant/Card.qml79
1 files changed, 79 insertions, 0 deletions
diff --git a/desktop/assistant/Card.qml b/desktop/assistant/Card.qml
index 437f0ad..2f9af69 100644
--- a/desktop/assistant/Card.qml
+++ b/desktop/assistant/Card.qml
@@ -11,6 +11,8 @@
import Quickshell
import QtQuick
+import QtQuick.Effects
+import QtQuick.Shapes
import ".."
import "Files.js" as Files
import "Presets.js" as Presets
@@ -45,6 +47,82 @@ Rectangle {
border.width: 1
border.color: Qt.alpha(Theme.text, 0.12)
+ // deck-it's Assistant tab backdrop (Assistant.qml, backdrop): the
+ // picture's subject in the right part of the tall card, desaturated and
+ // faded out toward the transcript through an elliptic mask; stronger
+ // while the chat is empty. First child, so everything draws over it.
+ Item {
+ id: backdrop
+ anchors.fill: parent
+ visible: card.tall && card.canvas.backdrop !== "" && art.status === Image.Ready
+ opacity: list.count === 0 ? 0.24 : 0.07
+ Behavior on opacity { NumberAnimation { duration: 600 } }
+
+ // 108% of the card's height, top-aligned; the face (or, without
+ // one, the picture's centre) sits 74% across, under the mask.
+ // ponytail: decoded at full size while the card is open; the face
+ // avatars share this decode through Qt's pixmap cache.
+ Image {
+ id: art
+ readonly property real k: art.implicitHeight > 0 ? card.height * 1.08 / art.implicitHeight : 0
+ readonly property real fx: card.canvas.face.length ? card.canvas.face[0] : art.implicitWidth / 2
+ visible: false
+ source: card.canvas.fileUrl(card.canvas.backdrop)
+ height: card.height * 1.08
+ width: art.implicitWidth * art.k
+ x: card.width * 0.74 - art.fx * art.k
+ }
+ Item {
+ id: mask
+ anchors.fill: parent
+ visible: false
+ layer.enabled: true
+ Shape {
+ // A circle scaled into deck-it's ellipse (27% x 56% at 74%, 40%).
+ width: parent.width
+ height: parent.height
+ transform: Scale {
+ origin { x: mask.width * 0.74; y: mask.height * 0.40 }
+ yScale: mask.width > 0 ? mask.height * 0.56 / (mask.width * 0.27) : 1
+ }
+ ShapePath {
+ strokeWidth: -1
+ fillGradient: RadialGradient {
+ centerX: mask.width * 0.74; centerY: mask.height * 0.40
+ focalX: centerX; focalY: centerY
+ centerRadius: mask.width * 0.27
+ GradientStop { position: 0.3; color: "black" }
+ GradientStop { position: 1.0; color: "transparent" }
+ }
+ PathRectangle { x: 0; y: -mask.height; width: mask.width; height: mask.height * 3 }
+ }
+ }
+ }
+ MultiEffect {
+ x: art.x
+ width: art.width
+ height: art.height
+ source: art
+ autoPaddingEnabled: false
+ saturation: -0.25
+ brightness: -0.1
+ maskEnabled: true
+ maskSource: maskView
+ // The gradient's alpha as a soft edge; the default is a hard cut.
+ maskThresholdMin: 0.0
+ maskSpreadAtMin: 1.0
+ }
+ // The mask in the picture's own coordinates, since the effect is.
+ ShaderEffectSource {
+ id: maskView
+ visible: false
+ width: art.width
+ height: art.height
+ sourceItem: mask
+ sourceRect: Qt.rect(art.x, 0, art.width, art.height)
+ }
+ }
+
// Keys reach a focused item, never the window (AGENTS.md); the card is
// the ancestor of every text field, so their Escape bubbles here.
Keys.onEscapePressed: card.canvas.back()
@@ -178,6 +256,7 @@ Rectangle {
delegate: Message {
width: list.width
+ canvas: card.canvas
running: card.canvas.runningTool
}