From 4f797a9741c4bc775a96126bfe192bf845092323 Mon Sep 17 00:00:00 2001 From: zach Date: Wed, 30 Sep 2026 15:11:51 +0200 Subject: [PATCH] separate dashboard and settings changes to new branch --- .gitignore | 1 + Components/GeminiGlow.qml | 42 +++++++++ .../Sidebar/Chat/ChatContent.qml | 12 +-- .../Notifications/Sidebar/Chat/ChatInput.qml | 53 +++++++++++ assets/shaders/geminiglow.frag | 82 ++++++++++++++++++ assets/shaders/geminiglow.frag.qsb | Bin 0 -> 3721 bytes 6 files changed, 185 insertions(+), 5 deletions(-) create mode 100644 Components/GeminiGlow.qml create mode 100644 assets/shaders/geminiglow.frag create mode 100644 assets/shaders/geminiglow.frag.qsb diff --git a/.gitignore b/.gitignore index 8b588fe..c6ff3ec 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ network-dev/ **/zshell.dist/ .opencode/ run-agent.sh +AGENTS.md diff --git a/Components/GeminiGlow.qml b/Components/GeminiGlow.qml new file mode 100644 index 0000000..443ef07 --- /dev/null +++ b/Components/GeminiGlow.qml @@ -0,0 +1,42 @@ +import QtQuick +import Quickshell + +ShaderEffect { + id: root + + property Item target: parent + + property real glowSize: 40 + property real innerGlow: 0 + property real lineWidth: 2 + property real intensity: 1.0 + property real speed: 1.0 + property int period: 6000 + property bool running: true + + property color color1: "#4285F4" + property color color2: "#9B72CB" + property color color3: "#D96570" + property color color4: "#F2A54A" + + readonly property size itemSize: Qt.size(width, height) + readonly property size rectSize: target ? Qt.size(target.width, target.height) : Qt.size(0, 0) + readonly property real radius: (target as Rectangle).radius ?? 0 + property real phase: 0 + + anchors.fill: target + anchors.margins: -glowSize + z: -1 + + enabled: false + + fragmentShader: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/shaders/geminiglow.frag.qsb`) + + FrameAnimation { + running: root.running && root.visible + onTriggered: { + const dt = frameTime * 1000; + root.phase = (root.phase + dt * root.speed / root.period) % 1.0; + } + } +} diff --git a/Modules/Notifications/Sidebar/Chat/ChatContent.qml b/Modules/Notifications/Sidebar/Chat/ChatContent.qml index 88a4c2d..e4cbaf2 100644 --- a/Modules/Notifications/Sidebar/Chat/ChatContent.qml +++ b/Modules/Notifications/Sidebar/Chat/ChatContent.qml @@ -170,7 +170,7 @@ Item { Layout.fillWidth: ChatState.isWindow ? false : true Layout.alignment: ChatState.isWindow ? Qt.AlignCenter : Qt.AlignLeft | Qt.AlignVCenter elide: Text.ElideRight - font.pointSize: Tokens.font.size.larger + font: Tokens.font.title.medium text: qsTr(root.chatData.title) } } @@ -346,7 +346,7 @@ Item { id: scrollToBottom anchors.centerIn: parent - font.pointSize: Tokens.font.size.large + font: Tokens.font.icon.large icon: "arrow_downward" isRound: true padding: Tokens.padding.extraSmall @@ -455,7 +455,7 @@ Item { MaterialIcon { anchors.centerIn: parent - font.pointSize: Tokens.font.size.extraLarge + font.pointSize: Tokens.font.size.large text: thumb.fileIcon(thumb.modelData?.mimeType ?? "") visible: !thumb.modelData?.isImage ?? false } @@ -537,13 +537,15 @@ Item { anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right + anchors.margins: Tokens.padding.medium bg.border.color: Colors.palette.m3outlineVariant bg.color: Colors.tPalette.m3surfaceContainerLowest - font.pointSize: Tokens.font.size.normal + fastGlow: Chat.busy + font: Tokens.font.body.small implicitHeight: Math.min(root.height / 5, contentHeight + topPadding + bottomPadding) focus: true placeholderText: qsTr("Send a message") - sendIcon.font.pointSize: Tokens.font.size.large + sendIcon.font: Tokens.font.icon.medium sendIcon.icon: "arrow_upward" sendIcon.padding: Tokens.padding.extraSmall diff --git a/Modules/Notifications/Sidebar/Chat/ChatInput.qml b/Modules/Notifications/Sidebar/Chat/ChatInput.qml index 058ac31..34d2275 100644 --- a/Modules/Notifications/Sidebar/Chat/ChatInput.qml +++ b/Modules/Notifications/Sidebar/Chat/ChatInput.qml @@ -9,6 +9,8 @@ TextAreaBase { readonly property alias bg: bg readonly property alias sendIcon: sendIcon + property bool fastGlow: false + signal sendPressed bottomPadding: Tokens.padding.large @@ -17,6 +19,16 @@ TextAreaBase { topPadding: Tokens.padding.large wrapMode: TextAreaBase.Wrap + onFastGlowChanged: { + if (fastGlow) { + resetAnim.stop(); + boostAnim.restart(); + } else { + boostAnim.stop(); + resetAnim.restart(); + } + } + background: CustomRect { id: bg @@ -24,6 +36,47 @@ TextAreaBase { color: Colors.tPalette.m3surfaceContainer radius: Tokens.rounding.extraLarge + GeminiGlow { + id: glow + glowSize: 32 + + opacity: root.activeFocus ? 1 : 0 + visible: opacity > 0 + + Behavior on opacity { + Anim {} + } + + SequentialAnimation { + id: boostAnim + + NumberAnimation { + target: glow + property: "speed" + to: 4.0 + duration: 250 + easing.type: Easing.OutCubic + } + NumberAnimation { + target: glow + property: "speed" + to: 2.25 + duration: 1500 + easing.type: Easing.InOutQuad + } + } + + // Ease back down to normal once fastGlow is false + NumberAnimation { + id: resetAnim + target: glow + property: "speed" + to: 1.0 + duration: 800 + easing.type: Easing.InOutQuad + } + } + StateLayer { id: stateLayer diff --git a/assets/shaders/geminiglow.frag b/assets/shaders/geminiglow.frag new file mode 100644 index 0000000..ea19795 --- /dev/null +++ b/assets/shaders/geminiglow.frag @@ -0,0 +1,82 @@ +#version 440 + +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + vec2 itemSize; + vec2 rectSize; + float radius; + float glowSize; + float innerGlow; + float lineWidth; + float phase; + float intensity; + vec4 color1; + vec4 color2; + vec4 color3; + vec4 color4; +}; + +const float TAU = 6.28318530718; + +float sdRoundBox(vec2 p, vec2 halfSize, float r) +{ + vec2 q = abs(p) - (halfSize - r); + return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r; +} + +vec3 palette(float t) +{ + t = fract(t) * 4.0; + float f = smoothstep(0.0, 1.0, fract(t)); + if (t < 1.0) return mix(color1.rgb, color2.rgb, f); + if (t < 2.0) return mix(color2.rgb, color3.rgb, f); + if (t < 3.0) return mix(color3.rgb, color4.rgb, f); + return mix(color4.rgb, color1.rgb, f); +} + +float hash(vec2 p) +{ + return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); +} + +void main() +{ + vec2 p = (qt_TexCoord0 - 0.5) * itemSize; + + float r = clamp(radius, 0.0, min(rectSize.x, rectSize.y) * 0.5); + float d = sdRoundBox(p, rectSize * 0.5, r); + + float ang = atan(p.y, p.x); + float a2 = phase * TAU; + float u = ang / TAU + 0.5 + phase; + u += 0.07 * sin(ang * 2.0 + a2) + 0.04 * sin(ang * 3.0 - a2 * 2.0); + vec3 col = palette(u); + + float g = 0.0; + if (d > 0.0) { + float t = clamp(d / max(glowSize, 0.001), 0.0, 1.0); + g = pow(1.0 - t, 3.0); + } else if (innerGlow > 0.0) { + g = exp(d / innerGlow * 3.0); + } + + float pulse = 0.9 + 0.1 * sin(a2 * 2.0 + ang); + g *= pulse; + + float line = 0.0; + if (lineWidth > 0.0) + line = 1.0 - smoothstep(0.0, lineWidth, abs(d)); + + float a = clamp(g * intensity + line * 0.9, 0.0, 1.0); + + col = mix(col, vec3(1.0), line * 0.25); + + a += (hash(gl_FragCoord.xy) - 0.5) / 255.0; + a = clamp(a, 0.0, 1.0); + + fragColor = vec4(col * a, a) * qt_Opacity; +} diff --git a/assets/shaders/geminiglow.frag.qsb b/assets/shaders/geminiglow.frag.qsb new file mode 100644 index 0000000000000000000000000000000000000000..233240e6fd7552d50ef537e097b4a5c3c2609215 GIT binary patch literal 3721 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 literal 0 HcmV?d00001