diff options
Diffstat (limited to 'desktop/assistant/Card.qml')
| -rw-r--r-- | desktop/assistant/Card.qml | 79 |
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 } |
