Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 3m36s
C++ / clang-tidy (pull_request) Failing after 3m34s
JS/TS / fmt (pull_request) Successful in 13s
JS/TS / lint (pull_request) Successful in 13s
Python / static (pull_request) Successful in 28s
Rust / build (pull_request) Successful in 51s
Python / verify (pull_request) Successful in 1m36s
Rust / fmt (pull_request) Successful in 33s
Rust / clippy (pull_request) Successful in 1m15s

This commit is contained in:
2026-10-24 16:28:54 +02:00
116 changed files with 2722 additions and 1223 deletions
-49
View File
@@ -1,49 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Effects
import ZShell.Config
import qs.Components
import qs.Services
Item {
id: root
required property Item bar
required property ScreenState screenState
anchors.fill: parent
CustomRect {
anchors.fill: parent
anchors.margins: -1
color: Colors.palette.m3surface
layer.enabled: true
layer.effect: MultiEffect {
maskEnabled: true
maskInverted: true
maskSource: mask
maskSpreadAtMin: 1
maskThresholdMin: 0.5
}
}
Item {
id: mask
anchors.fill: parent
layer.enabled: true
visible: false
Rectangle {
anchors.fill: parent
anchors.margins: Config.bar.border + 1
anchors.topMargin: root.bar.implicitHeight + 1
radius: Config.bar.border > 0 ? Config.bar.rounding : 0
topLeftRadius: Config.bar.rounding
topRightRadius: Config.bar.rounding
}
}
}
+3 -1
View File
@@ -19,7 +19,7 @@ Item {
required property Wrapper popouts
function resolveIcon(app: string, icon: string): string {
if (app === "chrome_status_icon_1") {
if (app === "discord_status_icon_1" && Config.bar.tray.replaceIcons) {
return Quickshell.iconPath("discord-tray");
} else if (app === "AyuGramDesktop") {
if (icon === Quickshell.iconPath("com.ayugram.desktop-attention-symbolic"))
@@ -50,6 +50,8 @@ Item {
radius: Tokens.rounding.full
StateLayer {
id: layer
acceptedButtons: Qt.LeftButton | Qt.RightButton
anchors.fill: parent
color: icon.layer.enabled && root.current ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
+32 -28
View File
@@ -33,8 +33,7 @@ Item {
y: parent.y
Behavior on implicitHeight {
Anim {
}
Anim {}
}
}
@@ -101,8 +100,7 @@ Item {
value: Audio.volume
Behavior on value {
Anim {
}
Anim {}
}
onInteraction: value => Audio.setVolume(value)
@@ -123,7 +121,7 @@ Item {
}
}
CustomClippingRect {
CustomRect {
Layout.fillWidth: true
Layout.preferredHeight: 65 + Tokens.spacing.small * 2
Layout.topMargin: root.topMargin
@@ -136,17 +134,21 @@ Item {
node: Audio.source
}
CustomRect {
id: sourcePeakFill
ClipRect {
anchors.fill: parent
radius: parent.radius
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
color: Qt.alpha(Colors.palette.m3primary, 0.15)
implicitWidth: parent.width * sourcePeak.peak
CustomRect {
id: sourcePeakFill
Behavior on implicitWidth {
Anim {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
color: Qt.alpha(Colors.palette.m3primary, 0.15)
implicitWidth: parent.width * sourcePeak.peak
Behavior on implicitWidth {
Anim {}
}
}
}
@@ -195,8 +197,7 @@ Item {
value: Audio.sourceVolume
Behavior on value {
Anim {
}
Anim {}
}
onInteraction: value => Audio.setSourceVolume(value)
@@ -230,7 +231,7 @@ Item {
model: Audio.streams.filter(s => s.isSink)
CustomClippingRect {
CustomRect {
id: appBox
required property int index
@@ -248,17 +249,21 @@ Item {
node: appBox.modelData
}
CustomRect {
id: peakFill
ClipRect {
anchors.fill: parent
radius: parent.radius
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
color: Qt.alpha(Colors.palette.m3primary, 0.15)
implicitWidth: parent.width * peak.peak
CustomRect {
id: peakFill
Behavior on implicitWidth {
Anim {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
color: Qt.alpha(Colors.palette.m3primary, 0.15)
implicitWidth: parent.width * peak.peak
Behavior on implicitWidth {
Anim {}
}
}
}
@@ -316,8 +321,7 @@ Item {
value: appBox.modelData.audio.volume
Behavior on value {
Anim {
}
Anim {}
}
onInteraction: value => Audio.setStreamVolume(appBox.modelData, value)
+2 -1
View File
@@ -9,7 +9,7 @@ import qs.Modules
import qs.Helpers
import qs.Services
CustomClippingRect {
CustomRect {
id: root
readonly property bool hasUpdates: Object.keys(Updates.updates)?.length > 0
@@ -73,6 +73,7 @@ CustomClippingRect {
implicitHeight: Math.min(contentHeight, (root.itemHeight + spacing) * 5 - spacing)
implicitWidth: contentWidth
spacing: Tokens.spacing.medium
clip: true
delegate: CustomRect {
id: update
-2
View File
@@ -1,6 +1,4 @@
import Quickshell
import Quickshell.Wayland
import Quickshell.Hyprland
import QtQuick
import qs.Components
import ZShell.Config
+3 -3
View File
@@ -25,7 +25,7 @@ Item {
searchField.forceActiveFocus();
}
CustomClippingRect {
ClipRect {
id: search
anchors.left: parent.left
@@ -54,7 +54,7 @@ Item {
}
}
CustomClippingRect {
ClipRect {
id: preview
anchors.bottom: parent.bottom
@@ -228,7 +228,7 @@ Item {
anchors.fill: parent
spacing: Tokens.spacing.small
CustomClippingRect {
ClipRect {
id: textRect
Layout.fillHeight: true
+1 -1
View File
@@ -14,7 +14,7 @@ Row {
padding: 20
spacing: 12
CustomClippingRect {
ClipRect {
color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2)
implicitHeight: info.implicitHeight
implicitWidth: info.implicitHeight
+1 -1
View File
@@ -16,7 +16,7 @@ Item {
implicitWidth: 1000
implicitHeight: 320
CustomClippingRect {
ClipRect {
anchors.fill: parent
radius: root.radius
@@ -46,7 +46,7 @@ CustomListView {
anchors.bottom: parent.bottom
spacing: Tokens.spacing.extraSmall / 2
CustomClippingRect {
ClipRect {
id: iconClip
Layout.fillHeight: true
@@ -25,7 +25,7 @@ Item {
onClicked: root.close()
}
CustomClippingRect {
ClipRect {
id: popupBackground
readonly property real padding: 4
@@ -40,7 +40,7 @@ Item {
visible: false
z: 999
CustomClippingRect {
ClipRect {
id: popupBackground
readonly property real padding: Tokens.padding.extraSmall
+1 -1
View File
@@ -19,7 +19,7 @@ Item {
implicitHeight: tileHeight + Tokens.padding.large * 2
implicitWidth: tileWidth
CustomClippingRect {
ClipRect {
id: image
anchors.horizontalCenter: parent.horizontalCenter
+1 -1
View File
@@ -55,7 +55,7 @@ ColumnLayout {
text: Time.format("dddd, d MMMM yyyy")
}
CustomClippingRect {
ClipRect {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Tokens.spacing.largeIncreased * 2
color: Colors.tPalette.m3surfaceContainer
+1 -1
View File
@@ -5,7 +5,7 @@ import qs.Helpers
import ZShell.Config
import qs.Services
CustomClippingRect {
ClipRect {
id: root
required property var lock
+2 -2
View File
@@ -2,12 +2,12 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import ZShell.Components
import qs.Components
import ZShell.Config
import qs.Components
import qs.Helpers
import qs.Services
CustomClippingRect {
ClipRect {
id: root
required property var lock
+2 -3
View File
@@ -24,12 +24,11 @@ ColumnLayout {
Layout.fillWidth: true
color: Colors.palette.m3outline
elide: Text.ElideRight
font.family: Config.appearance.font.family.mono
font.weight: 500
font: Tokens.font.mono.builders.small.weight(Font.Medium).build()
text: NotifServer.list.length > 0 ? qsTr("%1 notification%2").arg(NotifServer.list.length).arg(NotifServer.list.length === 1 ? "" : "s") : qsTr("Notifications")
}
ClippingRectangle {
ClipRect {
id: clipRect
Layout.fillHeight: true
+1 -1
View File
@@ -85,7 +85,7 @@ CustomRect {
}
}
ClippingRectangle {
ClipRect {
anchors.fill: parent
color: root.urgency === "critical" ? Colors.palette.m3error : root.urgency === "low" ? Colors.layer(Colors.palette.m3surfaceContainerHighest, 3) : Colors.palette.m3secondaryContainer
radius: Tokens.rounding.full
+2 -1
View File
@@ -1,12 +1,13 @@
import Quickshell
import Quickshell.Widgets
import QtQuick
import qs.Components
import qs.Paths
Item {
id: root
ClippingRectangle {
ClipRect {
anchors.fill: parent
radius: Tokens.rounding.full
+7 -11
View File
@@ -3,8 +3,9 @@ pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Widgets
import QtQuick
import qs.Components
import ZShell.Config
import ZShell.Components
import qs.Components
import qs.Daemons
import qs.Helpers
@@ -27,7 +28,7 @@ Item {
for (let i = 0; i < count; i++)
height += (list.itemAtIndex(i) as NotifWrapper)?.nonAnimHeight ?? 0;
if (panels.popouts.hasCurrent && (panels.popouts.currentCenter + (panels.popouts.current?.width / 2)) > panels.notifications.x)
if (panels.popouts.hasCurrent && (panels.popouts.currentCenter + (panels.popouts.current?.width / 2)) > panels.notifications.x || screenState.dashboard)
return 0;
if (screenState.osd) {
@@ -36,25 +37,20 @@ Item {
height = h;
}
if (screenState.session) {
const h = panels.session.y - Tokens.spacing.small * 2 - padding * 2;
if (height > h)
height = h;
}
return Math.min(((QsWindow.window as QsWindow)?.screen?.height ?? 0) - 1 * 2, height + padding * 2);
}
implicitWidth: Config.notifs.sizes.width + padding * 2
Behavior on implicitHeight {
Anim {}
}
ClippingWrapperRectangle {
Item {
anchors.fill: parent
anchors.margins: root.padding
color: "transparent"
radius: Tokens.rounding.large - root.padding
clip: true
// radius: Tokens.rounding.large - root.padding
CustomListView {
id: list
+1 -1
View File
@@ -108,7 +108,7 @@ CustomRect {
visible: root.hasImage || root.hasAppIcon
width: Config.notifs.sizes.image
sourceComponent: CustomClippingRect {
sourceComponent: ClipRect {
color: root.modelData.urgency === NotificationUrgency.Critical ? Colors.palette.m3error : root.modelData.urgency === NotificationUrgency.Low ? Colors.layer(Colors.palette.m3surfaceContainerHighest, 2) : Colors.palette.m3secondaryContainer
implicitHeight: Config.notifs.sizes.image
implicitWidth: Config.notifs.sizes.image
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -5,7 +5,7 @@ import ZShell.Config
import qs.Components
import qs.Services
CustomClippingRect {
ClipRect {
id: root
property real animPerc: UPower.displayDevice.percentage
+14 -9
View File
@@ -4,7 +4,7 @@ import ZShell.Config
import qs.Components
import qs.Services
CustomClippingRect {
CustomRect {
id: root
required property color accent
@@ -19,15 +19,20 @@ CustomClippingRect {
implicitWidth: 450
radius: Tokens.rounding.largeIncreased
CustomRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
color: Qt.alpha(root.accent, 0.05)
implicitWidth: parent.width * root.usage
ClipRect {
anchors.fill: parent
radius: parent.radius
Behavior on implicitWidth {
Anim {}
CustomRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
color: Qt.alpha(root.accent, 0.05)
implicitWidth: parent.width * root.usage
Behavior on implicitWidth {
Anim {}
}
}
}
+3 -2
View File
@@ -149,7 +149,7 @@ ColumnLayout {
}
}
VerticalFadeFlickable {
CustomFlickable {
id: flickable
Layout.fillHeight: true
@@ -158,7 +158,8 @@ ColumnLayout {
bottomMargin: Tokens.padding.extraLarge
topMargin: Tokens.padding.large
fadeAmount: 0.1
clip: true
// fadeAmount: 0.1
contentHeight: root.contentChild?.implicitHeight ?? 0
contentItem.children: [root.contentChild]
+1 -1
View File
@@ -25,7 +25,7 @@ Item {
anchors.fill: parent
spacing: Tokens.spacing.small
CustomClippingRect {
ClipRect {
id: imgWrapper
Layout.fillWidth: true
+30 -28
View File
@@ -100,16 +100,13 @@ Item {
visible: opacity > 0
Behavior on opacity {
Anim {
}
Anim {}
}
Behavior on scale {
Anim {
}
Anim {}
}
Behavior on y {
Anim {
}
Anim {}
}
Component.onCompleted: {
@@ -120,9 +117,8 @@ Item {
Qt.callLater(syncCropToScreen);
}
ButtonRow {
id: screenSelector
Loader {
id: selectorLoader
anchors.bottom: imageSlot.top
anchors.bottomMargin: Tokens.spacing.medium
anchors.left: parent.left
@@ -130,25 +126,32 @@ Item {
anchors.right: parent.right
anchors.rightMargin: Tokens.padding.extraLarge * 2
anchors.top: parent.top
spacing: Tokens.spacing.small
Repeater {
model: wrapper.screens
active: wrapper.screens.length > 1
delegate: TextButton {
required property int index
readonly property bool isCurrent: wrapper.selectedScreenIndex === index
required property var modelData
sourceComponent: ButtonRow {
id: screenSelector
fillWidth: true
font: Tokens.font.body.builders.large.weight(Font.DemiBold).build()
inactiveColor: isCurrent ? Colors.palette.m3primary : Qt.alpha(Colors.palette.m3surfaceContainer, 0.7)
inactiveOnColor: isCurrent ? Colors.palette.m3onPrimary : Qt.alpha(Colors.palette.m3onSurface, 0.7)
isRound: true
shapeMorph: true
text: modelData.name
spacing: Tokens.spacing.small
onClicked: wrapper.selectScreen(index)
Repeater {
model: wrapper.screens
delegate: TextButton {
required property int index
readonly property bool isCurrent: wrapper.selectedScreenIndex === index
required property var modelData
fillWidth: true
font: Tokens.font.body.builders.large.weight(Font.DemiBold).build()
inactiveColor: isCurrent ? Colors.palette.m3primary : Qt.alpha(Colors.palette.m3surfaceContainer, 0.7)
inactiveOnColor: isCurrent ? Colors.palette.m3onPrimary : Qt.alpha(Colors.palette.m3onSurface, 0.7)
isRound: true
shapeMorph: true
text: modelData.name
onClicked: wrapper.selectScreen(index)
}
}
}
}
@@ -222,9 +225,9 @@ Item {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: wrapper.buttonRowHeight + Tokens.spacing.medium
anchors.topMargin: selectorLoader.active ? wrapper.buttonRowHeight + Tokens.spacing.medium : Tokens.spacing.medium
CustomClippingRect {
ClipRect {
id: fitBox
readonly property real aspect: scaledImg.implicitHeight > 0 ? scaledImg.implicitWidth / scaledImg.implicitHeight : 1
@@ -357,8 +360,7 @@ Item {
width: Math.round(baseWidth / zoom)
Behavior on color {
CAnim {
}
CAnim {}
}
Component.onCompleted: {
+5 -2
View File
@@ -6,7 +6,7 @@ import qs.Components
import ZShell.Config
import qs.Services
CustomClippingRect {
Item {
id: root
property color blobColor: Colors.tPalette.m3surfaceContainerLow
@@ -21,7 +21,6 @@ CustomClippingRect {
implicitHeight: sState.screen.height * 0.7
implicitWidth: implicitHeight * ratio
radius: Tokens.rounding.largeIncreased + Tokens.padding.small
Behavior on blobColor {
CAnim {}
@@ -39,6 +38,7 @@ CustomClippingRect {
}
BlobInvertedRect {
id: blobRect
anchors.fill: parent
borderBottom: Tokens.padding.small
borderLeft: navPane.width + navPane.anchors.margins * 2
@@ -46,6 +46,8 @@ CustomClippingRect {
borderTop: Tokens.padding.small
group: blobGroup
opacity: root.blobColor.a
// outerRadius: 10
outerRadius: Tokens.rounding.largeIncreased + Tokens.padding.small
radius: Tokens.rounding.largeIncreased
}
@@ -64,6 +66,7 @@ CustomClippingRect {
anchors.bottom: parent.bottom
anchors.left: navPane.right
anchors.leftMargin: navPane.anchors.margins + anchors.margins
anchors.bottomMargin: blobRect.borderBottom
anchors.margins: Tokens.padding.extraLarge
anchors.right: parent.right
anchors.top: parent.top
+11 -14
View File
@@ -2,8 +2,9 @@ pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import qs.Components
import ZShell.Config
import ZShell.Components
import qs.Components
Item {
id: root
@@ -27,21 +28,17 @@ Item {
}
}
CustomClippingRect {
anchors.fill: parent
Loader {
id: content
Loader {
id: content
active: root.shouldBeActive || root.visible
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
active: root.shouldBeActive || root.visible
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
sourceComponent: Content {
sState.animatingContainer: content.opacity < 1
sState.currentPageIdx: ["wallpaper"].indexOf(root.queuedMode)
sState.screen: root.screen
}
sourceComponent: Content {
sState.animatingContainer: content.opacity < 1
sState.currentPageIdx: ["wallpaper"].indexOf(root.queuedMode)
sState.screen: root.screen
}
}
}
@@ -3,10 +3,11 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import QtQuick.Layouts
import ZShell.Services
import ZShell.Components
import ZShell.Config
import ZShell.Llm
import qs.Modules.Notifications.Sidebar.Chat.Content
import qs.Modules.Sidebar.Chat.Content
import qs.Components
import qs.Services
@@ -90,7 +91,9 @@ Item {
signal requestClose
readonly property bool atNewestEnd: !listLoader.item || listLoader.item.contentY < 4
readonly property bool importing: importIds.length > 0
property var importIds: []
property var pendingAttachments: []
function attachFiles(): void {
@@ -436,7 +439,7 @@ Item {
width: attachments.cellWidth
height: width
opacity: modelData ? 1 : 0
CustomClippingRect {
ClipRect {
anchors.fill: parent
anchors.margins: Tokens.spacing.extraSmall
@@ -541,7 +544,7 @@ Item {
bg.border.color: Colors.palette.m3outlineVariant
bg.color: Colors.tPalette.m3surfaceContainerLowest
fastGlow: LlmClient.busy
glow.opacity: input.activeFocus && (!LlmClient.busy || ChatState.isWindow)
glow.opacity: !ChatState.narrowSidebarExpanded ? (input.activeFocus && (!LlmClient.busy || ChatState.isWindow) ? 1 : 0) : 0
font: Tokens.font.body.small
implicitHeight: Math.min(root.height / 5, contentHeight + topPadding + bottomPadding)
focus: true
@@ -578,4 +581,52 @@ Item {
root.send(text);
}
}
Connections {
target: DropImporter
function onImportFinished(id: int, paths: var, errors: var): void {
if (!root.importIds.includes(id))
return;
root.importIds = root.importIds.filter(i => i !== id);
if (paths.length > 0) {
const added = root.chatData.addAttachments(paths);
root.pendingAttachments = root.pendingAttachments.concat(added);
}
for (const e of errors)
console.warn("Drop import:", e);
}
}
DropArea {
id: dropArea
anchors.fill: parent
onEntered: drag => {
drag.accepted = drag.hasUrls || drag.hasText || drag.hasHtml || drag.formats.some(f => f.startsWith("image/"));
}
onDropped: drop => {
const formats = drop.formats;
const urls = drop.hasUrls ? drop.urls : [];
const text = drop.hasText ? drop.text : "";
const html = drop.hasHtml ? drop.html : "";
const imgFormat = formats.find(f => f.startsWith("image/"));
const raw = imgFormat ? drop.getDataAsArrayBuffer(imgFormat) : new ArrayBuffer(0);
switch (DropImporter.decide(urls, text, html, raw.byteLength > 0)) {
case DropImporter.Attach:
root.importIds = [...root.importIds, DropImporter.importAttachments(urls, html, raw)];
break;
case DropImporter.InsertText:
input.insert(input.cursorPosition, text || urls.map(u => u.toString()).join("\n"));
root.focusInput();
break;
}
drop.acceptProposedAction();
}
}
}
@@ -6,7 +6,7 @@ import qs.Effects
import qs.Components
import qs.Services
CustomClippingRect {
CustomRect {
id: root
property bool expanded: false
@@ -91,6 +91,50 @@ CustomClippingRect {
opacity: root.slim ? 0 : 1
state: titleText.readOnly ? "static" : "editing"
states: [
State {
name: "static"
PropertyChanges {
staticText.opacity: 1
}
PropertyChanges {
titleText.opacity: 0
}
},
State {
name: "editing"
PropertyChanges {
staticText.opacity: 0
}
PropertyChanges {
titleText.opacity: 1
}
}
]
transitions: [
// Going static: titleText fades out (animated), staticText snaps in
Transition {
to: "static"
Anim {
target: titleText
property: "opacity"
type: Anim.SlowEffects
}
},
// Going editing: staticText fades out (animated), titleText snaps in
Transition {
to: "editing"
Anim {
target: staticText
property: "opacity"
type: Anim.SlowEffects
}
}
]
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
@@ -106,7 +150,7 @@ CustomClippingRect {
color: Colors.layer(Colors.palette.m3surfaceContainerHighest, 1)
implicitHeight: titleText.implicitHeight + Tokens.padding.extraSmall * 2
radius: Tokens.rounding.full
implicitWidth: titleText.implicitWidth + Tokens.padding.large * 2
implicitWidth: titleText.width + Tokens.padding.large * 2
opacity: titleText.readOnly ? 0 : 1
Behavior on opacity {
@@ -122,11 +166,6 @@ CustomClippingRect {
anchors.right: parent.right
implicitHeight: titleText.implicitHeight + Tokens.padding.small * 2
layer.enabled: true
layer.effect: OpacityMask {
maskSource: fadeMask
}
TextFieldBase {
id: titleText
@@ -134,10 +173,12 @@ CustomClippingRect {
anchors.leftMargin: readOnly ? 0 : Tokens.padding.large
anchors.top: parent.top
anchors.topMargin: readOnly ? Tokens.padding.small : Tokens.padding.extraSmall * 2
width: Math.min(implicitWidth, parent.width - Tokens.padding.large * 2)
font: Tokens.font.body.medium
text: root.modelData.title || qsTr("New chat")
readOnly: true
color: Colors.palette.m3onSurface
opacity: 0
Behavior on anchors.leftMargin {
Anim {}
@@ -146,6 +187,11 @@ CustomClippingRect {
Anim {}
}
function cancelTitleEdit(): void {
text = root.modelData.title;
readOnly = true;
}
onEditingFinished: root.modelData.title = text
onReadOnlyChanged: {
if (!readOnly) {
@@ -156,27 +202,24 @@ CustomClippingRect {
}
}
}
}
CustomRect {
id: fadeMask
CustomText {
id: staticText
anchors.fill: titleWrapper
anchors.left: parent.left
anchors.leftMargin: titleText.readOnly ? 0 : Tokens.padding.large
anchors.top: parent.top
anchors.topMargin: titleText.readOnly ? Tokens.padding.small : Tokens.padding.extraSmall * 2
anchors.right: parent.right
font: titleText.font
text: titleText.text
elide: CustomText.ElideRight
layer.enabled: true
visible: false
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop {
color: Qt.rgba(1, 1, 1, 1.0)
position: 0.85
Behavior on anchors.leftMargin {
Anim {}
}
GradientStop {
color: Qt.rgba(1, 1, 1, 0)
position: 1.0
Behavior on anchors.topMargin {
Anim {}
}
}
}
@@ -323,7 +366,13 @@ CustomClippingRect {
inactiveColor: Colors.layer(Colors.palette.m3surfaceContainerHighest, 3)
inactiveOnColor: Colors.palette.m3onSurfaceVariant
onClicked: root.remove(root.modelData)
onClicked: {
if (!titleText.readOnly) {
titleText.cancelTitleEdit();
return;
}
root.remove(root.modelData);
}
}
IconButton {
@@ -366,7 +415,10 @@ CustomClippingRect {
}
StateLayer {
onClicked: root.expanded = !root.expanded
onClicked: {
root.expanded = !root.expanded;
titleText.readOnly = true;
}
}
MaterialIcon {
@@ -5,7 +5,7 @@ import QtQuick
import QtQuick.Layouts
import ZShell.Llm
import ZShell.Config
import qs.Modules.Notifications.Sidebar.Chat.Content
import qs.Modules.Sidebar.Chat.Content
import qs.Services
import qs.Components
@@ -140,7 +140,7 @@ Item {
label.transform: Rotation {
origin.y: fabRoot.label.height / 2
origin.x: fabRoot.label.width / 2
angle: ChatState.fabExpanded && (ChatState.narrowSidebarExpanded && !ChatState.isWide || !ChatState.isWindow) ? 135 : 0
angle: ChatState.fabExpanded && (ChatState.narrowSidebarExpanded || !ChatState.isWide || !ChatState.isWindow) ? 135 : 0
Behavior on angle {
Anim {}
@@ -148,8 +148,9 @@ Item {
}
onClicked: {
if (root.slim && !ChatState.narrowSidebarExpanded) {
if ((root.slim && !ChatState.narrowSidebarExpanded) || ChatState.shrinkSidebarOverride) {
root.requestExpand();
ChatState.shrinkSidebarOverride = false;
return;
}
@@ -183,9 +184,13 @@ Item {
name: "pop out in window"
icon: "open_in_new"
}
ListElement {
name: "shrink sidebar"
icon: "left_panel_close"
}
}
CustomClippingRect {
CustomRect {
id: fabMenuItem
required property var modelData
@@ -196,7 +201,19 @@ Item {
radius: sLayer.pressed ? Tokens.rounding.small : implicitHeight / 2
color: Colors.palette.m3primaryContainer
visible: !(modelData.name === "pop out in window" && ChatState.isWindow)
visible: {
const n = modelData.name;
const w = ChatState.isWindow;
const wide = ChatState.isWide;
if (n === "pop out in window" && w)
return false;
if (n === "shrink sidebar" && (!w || !wide))
return false;
else
return true;
}
clip: true
Behavior on radius {
Anim {
@@ -296,6 +313,8 @@ Item {
root.newChatRequest();
else if (name === "pop out in window")
Detach.create();
else if (name === "shrink sidebar")
ChatState.shrinkSidebarOverride = true;
ChatState.fabExpanded = false;
}
@@ -6,7 +6,7 @@ import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Helpers
import qs.Modules.Notifications.Sidebar
import qs.Modules.Sidebar
import qs.Components
import qs.Services
@@ -19,7 +19,7 @@ Singleton {
property bool settledSlim: false
property bool settledWide: false
property bool narrowSidebarExpanded: false
property bool windowIsWide: false
property bool shrinkSidebarOverride: false
property ShellScreen screen
property bool sidebarVisible: ShellState.forActive().sidebar
@@ -28,6 +28,9 @@ Singleton {
root.currentIdx = -1;
}
onIsWideChanged: if (isWide)
narrowSidebarExpanded = false
onSidebarVisibleChanged: if (!sidebarVisible)
fabExpanded = false
}
@@ -337,7 +337,7 @@ Item {
}
}
CustomClippingRect {
ClipRect {
id: expandedTool
anchors.top: collapsedRow.bottom
@@ -3,7 +3,7 @@ import ZShell.Llm
import ZShell.Config
import qs.Components
import qs.Services
import qs.Modules.Notifications.Sidebar.Chat
import qs.Modules.Sidebar.Chat
Item {
id: root
@@ -220,9 +220,8 @@ CustomRect {
Layout.alignment: Qt.AlignVCenter
color: root.codeHeaderColor
font: Tokens.font.label.small
font: Tokens.font.label.builders.small.capitalization(Font.Capitalize).build()
text: CodeIcons.name(root.language)
visible: root.language.length > 0
}
Item {
@@ -252,7 +251,7 @@ CustomRect {
}
}
CustomClippingRect {
CustomRect {
id: codeRect
anchors.left: parent.left
@@ -265,6 +264,7 @@ CustomRect {
implicitWidth: codeText.implicitWidth + codeFlick.anchors.margins * 2
implicitHeight: codeText.implicitHeight + codeFlick.anchors.margins * 2
color: CodeColors.active.bg
clip: true
Flickable {
id: codeFlick
@@ -272,6 +272,9 @@ CustomRect {
anchors.fill: parent
anchors.margins: Tokens.padding.small
boundsBehavior: Flickable.StopAtBounds
contentWidth: codeText.implicitWidth
CustomScrollBar.horizontal: CustomScrollBar {
flickable: codeFlick
parent: codeFlick.parent
@@ -280,16 +283,13 @@ CustomRect {
anchors.right: parent.right
}
TextAreaBase.flickable: TextAreaBase {
CustomText {
id: codeText
color: CodeColors.active.normal
layer.enabled: true
clip: false
font: Tokens.font.mono.small
textFormat: root.colored ? Text.RichText : Text.PlainText
textFormat: Text.RichText
text: root.colored ? root.html : root.plainForDisplay(root.code)
readOnly: true
}
}
}
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@ import QtQuick
import QtQuick.Layouts
import ZShell.Llm
import ZShell.Config
import qs.Modules.Notifications.Sidebar.Chat
import qs.Modules.Sidebar.Chat
import qs.Components
import qs.Services
@@ -24,7 +24,7 @@ Item {
implicitHeight: bubble.implicitHeight + (actionsRow.shouldBeActive ? actionsRow.implicitHeight + actionsRow.anchors.topMargin : 0)
CustomClippingRect {
CustomRect {
id: bubble
property real liveTargetWidth: root.isUser ? Math.min(msgText.implicitWidth + Tokens.padding.medium * 2, root.contentMaxWidth) : root.contentMaxWidth
@@ -37,8 +37,7 @@ Item {
implicitWidth: layoutFrozen ? frozenWidth : layoutWidth
implicitHeight: root.isUser ? msgText.contentHeight + Tokens.padding.medium * 2 : markdown.implicitHeight + markdown.anchors.topMargin * 2
anchors.right: root.isUser ? parent.right : undefined
layer.enabled: false
layer.smooth: true
clip: true
transform: Scale {
id: stretchScale
@@ -6,7 +6,7 @@ import Quickshell
import ZShell.Components
import ZShell.Config
import ZShell.Llm
import qs.Modules.Notifications.Sidebar.Chat
import qs.Modules.Sidebar.Chat
import qs.Components
import qs.Services
@@ -223,12 +223,13 @@ MouseArea {
height: width
opacity: modelData ? 1 : 0
CustomClippingRect {
CustomRect {
anchors.fill: parent
anchors.margins: Tokens.spacing.extraSmall
radius: Tokens.rounding.medium
color: Colors.tPalette.m3surfaceContainer
clip: true
FadeImage {
id: attImage
@@ -6,7 +6,7 @@ import ZShell.Config
import ZShell.Components
import ZShell.Llm
import ZShell.Blobs
import qs.Modules.Notifications.Sidebar.Chat
import qs.Modules.Sidebar.Chat
import qs.Components
import qs.Services
@@ -125,7 +125,7 @@ Item {
current: root.current
}
CustomClippingRect {
ClipRect {
id: expandedRect
anchors.left: parent.left
@@ -8,7 +8,7 @@ import ZShell.Blobs
import ZShell.Config
import ZShell.Llm
import qs.Components
import qs.Modules.Notifications.Sidebar.Chat.Content
import qs.Modules.Sidebar.Chat.Content
import qs.Services
Item {
@@ -21,12 +21,8 @@ Item {
property bool chatOpen: ChatState.chatSession !== null
readonly property bool isWide: root.width >= root.breakpoint
readonly property bool showList: isWide || !chatOpen
readonly property bool showContent: !isWide && chatOpen
function openConversation(conv: ChatSession, index: int): void {
}
readonly property bool wideExpandedSidebar: isWide && !ChatState.shrinkSidebarOverride
readonly property bool narrowExpandedSidebar: !isWide && ChatState.narrowSidebarExpanded
BlobGroup {
id: blobGroup
@@ -54,9 +50,9 @@ Item {
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.margins: Tokens.padding.medium
implicitWidth: root.isWide || ChatState.narrowSidebarExpanded ? Config.sidebar.sizes.width : root.iconSize
implicitWidth: root.wideExpandedSidebar || root.narrowExpandedSidebar ? Config.sidebar.sizes.width : root.iconSize
highlight: true
slim: !root.isWide && !ChatState.narrowSidebarExpanded
slim: !root.wideExpandedSidebar && !root.narrowExpandedSidebar
z: 1
Behavior on implicitWidth {
@@ -86,13 +82,13 @@ Item {
}
}
CustomClippingRect {
Item {
anchors.fill: parent
anchors.leftMargin: invertedRect.borderLeft
anchors.topMargin: invertedRect.borderTop
anchors.bottomMargin: invertedRect.borderBottom
anchors.rightMargin: invertedRect.borderRight
radius: invertedRect.radius
clip: true
children: [contentArea, dim]
}
@@ -100,9 +96,10 @@ Item {
CustomRect {
id: dim
anchors.fill: parent
radius: invertedRect.radius
color: Colors.palette.m3shadow
opacity: ChatState.narrowSidebarExpanded && !root.isWide ? 0.3 : 0
opacity: root.narrowExpandedSidebar ? 0.3 : 0
z: 1
Behavior on opacity {
@@ -155,7 +152,7 @@ Item {
id: dimArea
anchors.fill: parent
preventStealing: true
enabled: !root.isWide && ChatState.narrowSidebarExpanded
enabled: root.narrowExpandedSidebar
cursorShape: undefined
onClicked: {
@@ -175,12 +172,6 @@ Item {
}
}
Binding {
target: ChatState
property: "windowIsWide"
value: root.showList
}
Binding {
target: ChatState
property: "isWide"
@@ -5,7 +5,7 @@ import ZShell.Llm
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Modules.Notifications.Sidebar.Chat
import qs.Modules.Sidebar.Chat
import qs.Services
Item {
@@ -17,76 +17,6 @@ Item {
Layout.fillWidth: true
implicitHeight: flickable.contentHeight
layer.enabled: true
layer.smooth: true
layer.effect: Mask {
maskSource: gradientMask
}
Item {
id: gradientMask
anchors.fill: parent
layer.enabled: true
visible: false
Rectangle {
anchors.fill: parent
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop {
color: Qt.rgba(0, 0, 0, 0)
position: 0
}
GradientStop {
color: Qt.rgba(0, 0, 0, 1)
position: 0.1
}
GradientStop {
color: Qt.rgba(0, 0, 0, 1)
position: 0.9
}
GradientStop {
color: Qt.rgba(0, 0, 0, 0)
position: 1
}
}
}
Rectangle {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
implicitWidth: parent.width / 2
opacity: flickable.contentX > 0 ? 0 : 1
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
}
Rectangle {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.top: parent.top
implicitWidth: parent.width / 2
opacity: flickable.contentX < flickable.contentWidth - parent.width ? 0 : 1
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
}
}
CustomFlickable {
id: flickable
@@ -63,7 +63,7 @@ Item {
}
}
ClippingRectangle {
Item {
id: clipRect
anchors.bottom: parent.bottom
@@ -71,8 +71,6 @@ Item {
anchors.right: parent.right
anchors.top: title.bottom
anchors.topMargin: 10
color: "transparent"
radius: 6
Loader {
active: opacity > 0
@@ -112,7 +112,7 @@ CustomRect {
}
}
CustomClippingRect {
ClipRect {
anchors.fill: parent
color: root.urgency === NotificationUrgency.Critical ? Colors.palette.m3error : root.urgency === NotificationUrgency.Low ? Colors.layer(Colors.palette.m3surfaceContainerHigh, 3) : Colors.palette.m3secondaryContainer
radius: Tokens.rounding.full
@@ -3,7 +3,7 @@ import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Components
import qs.Modules.Notifications.Sidebar.Utils.Cards
import qs.Modules.Sidebar.Utils.Cards
Item {
id: root