Merge branch 'end-4:main' into main

This commit is contained in:
jwihardi
2025-10-12 17:16:03 -04:00
committed by GitHub
42 changed files with 450 additions and 270 deletions
+3
View File
@@ -74,6 +74,9 @@ windowrulev2 = immediate, title:.*\.exe
windowrulev2 = immediate, title:.*minecraft.* windowrulev2 = immediate, title:.*minecraft.*
windowrulev2 = immediate, class:^(steam_app).* windowrulev2 = immediate, class:^(steam_app).*
# Fix Jetbrain IDEs focus/rerendering problem
windowrulev2=noinitialfocus,class:^jetbrains-.*$,floating:1,title:^$|^\s$|^win\d+$
# No shadow for tiled windows (matches windows that are not floating). # No shadow for tiled windows (matches windows that are not floating).
windowrulev2 = noshadow, floating:0 windowrulev2 = noshadow, floating:0
+9 -1
View File
@@ -1,5 +1,6 @@
import qs.modules.common
import qs import qs
import qs.modules.common
import qs.services
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Hyprland import Quickshell.Hyprland
@@ -44,6 +45,13 @@ Singleton {
} }
} }
onSidebarRightOpenChanged: {
if (GlobalStates.sidebarRightOpen) {
Notifications.timeoutAll();
Notifications.markAllRead();
}
}
property real screenZoom: 1 property real screenZoom: 1
onScreenZoomChanged: { onScreenZoomChanged: {
Quickshell.execDetached(["hyprctl", "keyword", "cursor:zoom_factor", root.screenZoom.toString()]); Quickshell.execDetached(["hyprctl", "keyword", "cursor:zoom_factor", root.screenZoom.toString()]);
+1 -1
View File
@@ -58,7 +58,7 @@ Scope {
MouseArea { MouseArea {
id: mouseArea id: mouseArea
anchors.fill: parent anchors.fill: parent
onClicked: { onPressed: {
popupLoader.active = false popupLoader.active = false
} }
@@ -19,7 +19,10 @@ Item {
Behavior on rotation { Behavior on rotation {
enabled: Config.options.background.clock.cookie.constantlyRotate // Animating every second is expensive... enabled: Config.options.background.clock.cookie.constantlyRotate // Animating every second is expensive...
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) animation: NumberAnimation {
duration: 1000 // 1 second
easing.type: Easing.InOutQuad
}
} }
Rectangle { Rectangle {
@@ -295,6 +295,48 @@ Item { // Bar content region
Layout.rightMargin: indicatorsRowLayout.realSpacing Layout.rightMargin: indicatorsRowLayout.realSpacing
color: rightSidebarButton.colText color: rightSidebarButton.colText
} }
Revealer {
reveal: Notifications.silent || Notifications.unread > 0
Layout.fillHeight: true
Layout.rightMargin: reveal ? indicatorsRowLayout.realSpacing : 0
implicitHeight: reveal ? notificationIcon.implicitHeight : 0
implicitWidth: reveal ? notificationIcon.implicitWidth : 0
Behavior on Layout.rightMargin {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
Rectangle {
id: notifPing
visible: !Notifications.silent && Notifications.unread > 0
property bool showUnreadCount: Config.options.bar.indicators.notifications.showUnreadCount
anchors {
right: parent.right
top: parent.top
rightMargin: showUnreadCount ? 0 : 1
topMargin: showUnreadCount ? 0 : 3
}
radius: Appearance.rounding.full
color: Appearance.colors.colOnLayer0
z: 1
implicitHeight: showUnreadCount ? Math.max(notificationCounterText.implicitWidth, notificationCounterText.implicitHeight) : 8
implicitWidth: implicitHeight
StyledText {
id: notificationCounterText
visible: notifPing.showUnreadCount
anchors.centerIn: parent
font.pixelSize: Appearance.font.pixelSize.smallest
color: Appearance.colors.colLayer0
text: Notifications.unread
}
}
MaterialSymbol {
id: notificationIcon
text: Notifications.silent ? "notifications_paused" : "notifications"
iconSize: Appearance.font.pixelSize.larger
color: rightSidebarButton.colText
}
}
MaterialSymbol { MaterialSymbol {
Layout.rightMargin: indicatorsRowLayout.realSpacing Layout.rightMargin: indicatorsRowLayout.realSpacing
text: Network.materialSymbol text: Network.materialSymbol
@@ -302,6 +344,7 @@ Item { // Bar content region
color: rightSidebarButton.colText color: rightSidebarButton.colText
} }
MaterialSymbol { MaterialSymbol {
visible: BluetoothStatus.available
text: BluetoothStatus.connected ? "bluetooth_connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled" text: BluetoothStatus.connected ? "bluetooth_connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled"
iconSize: Appearance.font.pixelSize.larger iconSize: Appearance.font.pixelSize.larger
color: rightSidebarButton.colText color: rightSidebarButton.colText
@@ -75,7 +75,7 @@ Item {
toggled: root.trayOverflowOpen toggled: root.trayOverflowOpen
property bool containsMouse: hovered property bool containsMouse: hovered
onClicked: root.trayOverflowOpen = !root.trayOverflowOpen downAction: () => root.trayOverflowOpen = !root.trayOverflowOpen
Layout.fillHeight: !root.vertical Layout.fillHeight: !root.vertical
Layout.fillWidth: root.vertical Layout.fillWidth: root.vertical
@@ -48,7 +48,7 @@ PopupWindow {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.BackButton | Qt.RightButton acceptedButtons: Qt.BackButton | Qt.RightButton
onClicked: event => { onPressed: event => {
if ((event.button === Qt.BackButton || event.button === Qt.RightButton) && stackView.depth > 1) if ((event.button === Qt.BackButton || event.button === Qt.RightButton) && stackView.depth > 1)
stackView.pop(); stackView.pop();
} }
@@ -152,7 +152,7 @@ PopupWindow {
implicitWidth: contentItem.implicitWidth + horizontalPadding * 2 implicitWidth: contentItem.implicitWidth + horizontalPadding * 2
implicitHeight: 36 implicitHeight: 36
onClicked: stackView.pop() downAction: () => stackView.pop()
contentItem: RowLayout { contentItem: RowLayout {
anchors { anchors {
@@ -15,7 +15,7 @@ MouseArea {
hoverEnabled: true hoverEnabled: true
onClicked: { onPressed: {
Weather.getData(); Weather.getData();
Quickshell.execDetached(["notify-send", Quickshell.execDetached(["notify-send",
Translation.tr("Weather"), Translation.tr("Weather"),
@@ -18,7 +18,9 @@ Singleton {
// Transparency. The quadratic functions were derived from analysis of hand-picked transparency values. // Transparency. The quadratic functions were derived from analysis of hand-picked transparency values.
ColorQuantizer { ColorQuantizer {
id: wallColorQuant id: wallColorQuant
source: Qt.resolvedUrl(Config.options.background.wallpaperPath) property string wallpaperPath: Config.options.background.wallpaperPath
property bool wallpaperIsVideo: wallpaperPath.endsWith(".mp4") || wallpaperPath.endsWith(".webm") || wallpaperPath.endsWith(".mkv") || wallpaperPath.endsWith(".avi") || wallpaperPath.endsWith(".mov")
source: Qt.resolvedUrl(wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath)
depth: 0 // 2^0 = 1 color depth: 0 // 2^0 = 1 color
rescaleSize: 10 rescaleSize: 10
} }
@@ -303,7 +305,7 @@ Singleton {
} }
property QtObject elementResize: QtObject { property QtObject elementResize: QtObject {
property int duration: 400 property int duration: 300
property int type: Easing.BezierSpline property int type: Easing.BezierSpline
property list<real> bezierCurve: animationCurves.emphasized property list<real> bezierCurve: animationCurves.emphasized
property int velocity: 650 property int velocity: 650
@@ -137,7 +137,7 @@ Singleton {
property string secondHandStyle: "dot" // Options: "dot", "line" , "hide" property string secondHandStyle: "dot" // Options: "dot", "line" , "hide"
property string dateStyle: "bubble" // Options: "border", "rect", "bubble" , "hide" property string dateStyle: "bubble" // Options: "border", "rect", "bubble" , "hide"
property bool timeIndicators: true property bool timeIndicators: true
property bool hourMarks: true property bool hourMarks: false
property bool dateInClock: true property bool dateInClock: true
property bool constantlyRotate: false property bool constantlyRotate: false
} }
@@ -212,6 +212,11 @@ Singleton {
property bool useUSCS: false // Instead of metric (SI) units property bool useUSCS: false // Instead of metric (SI) units
property int fetchInterval: 10 // minutes property int fetchInterval: 10 // minutes
} }
property JsonObject indicators: JsonObject {
property JsonObject notifications: JsonObject {
property bool showUnreadCount: false
}
}
} }
property JsonObject battery: JsonObject { property JsonObject battery: JsonObject {
@@ -341,8 +346,12 @@ Singleton {
property JsonObject sidebar: JsonObject { property JsonObject sidebar: JsonObject {
property bool keepRightSidebarLoaded: true property bool keepRightSidebarLoaded: true
property JsonObject translator: JsonObject { property JsonObject translator: JsonObject {
property bool enable: false
property int delay: 300 // Delay before sending request. Reduces (potential) rate limits and lag. property int delay: 300 // Delay before sending request. Reduces (potential) rate limits and lag.
} }
property JsonObject ai: JsonObject {
property bool textFadeIn: true
}
property JsonObject booru: JsonObject { property JsonObject booru: JsonObject {
property bool allowNsfw: false property bool allowNsfw: false
property string defaultProvider: "yandere" property string defaultProvider: "yandere"
@@ -35,7 +35,7 @@ Rectangle {
RippleButton { RippleButton {
id: parentDirButton id: parentDirButton
onClicked: root.navigateToDirectory(FileUtils.parentDirectory(root.directory)) downAction: () => root.navigateToDirectory(FileUtils.parentDirectory(root.directory))
contentItem: MaterialSymbol { contentItem: MaterialSymbol {
text: "drive_folder_upload" text: "drive_folder_upload"
iconSize: Appearance.font.pixelSize.larger iconSize: Appearance.font.pixelSize.larger
@@ -105,7 +105,7 @@ Rectangle {
RippleButton { RippleButton {
id: dirEditButton id: dirEditButton
toggled: !root.showBreadcrumb toggled: !root.showBreadcrumb
onClicked: root.showBreadcrumb = !root.showBreadcrumb downAction: () => root.showBreadcrumb = !root.showBreadcrumb
contentItem: MaterialSymbol { contentItem: MaterialSymbol {
text: "edit" text: "edit"
iconSize: Appearance.font.pixelSize.larger iconSize: Appearance.font.pixelSize.larger
@@ -23,6 +23,7 @@ ColumnLayout {
text: root.title text: root.title
font.pixelSize: Appearance.font.pixelSize.larger font.pixelSize: Appearance.font.pixelSize.larger
font.weight: Font.Medium font.weight: Font.Medium
color: Appearance.colors.colOnSecondaryContainer
} }
} }
@@ -0,0 +1,25 @@
import QtQuick
import qs.modules.common
import qs.modules.common.widgets
MaterialCookie {
id: root
property alias text: symbol.text
property alias iconSize: symbol.iconSize
property alias font: symbol.font
property alias colSymbol: symbol.color
property real padding: 6
color: Appearance.colors.colSecondaryContainer
colSymbol: Appearance.colors.colOnSecondaryContainer
sides: 5
implicitSize: Math.max(symbol.implicitWidth, symbol.implicitHeight) + padding * 2
MaterialSymbol {
id: symbol
anchors.centerIn: parent
}
}
@@ -32,7 +32,7 @@ Button {
implicitWidth: (root.down && bounce) ? clickedWidth : baseWidth implicitWidth: (root.down && bounce) ? clickedWidth : baseWidth
implicitHeight: (root.down && bounce) ? clickedHeight : baseHeight implicitHeight: (root.down && bounce) ? clickedHeight : baseHeight
property color colBackground: ColorUtils.transparentize(Appearance?.colors.colLayer1Hover, 1) || "transparent" property color colBackground: ColorUtils.transparentize(colBackgroundHover, 1) || "transparent"
property color colBackgroundHover: Appearance?.colors.colLayer1Hover ?? "#E5DFED" property color colBackgroundHover: Appearance?.colors.colLayer1Hover ?? "#E5DFED"
property color colBackgroundActive: Appearance?.colors.colLayer1Active ?? "#D6CEE2" property color colBackgroundActive: Appearance?.colors.colLayer1Active ?? "#D6CEE2"
property color colBackgroundToggled: Appearance?.colors.colPrimary ?? "#65558F" property color colBackgroundToggled: Appearance?.colors.colPrimary ?? "#65558F"
@@ -9,7 +9,7 @@ RippleButton {
implicitWidth: 40 implicitWidth: 40
implicitHeight: 40 implicitHeight: 40
Layout.leftMargin: 8 Layout.leftMargin: 8
onClicked: { downAction: () => {
parent.expanded = !parent.expanded; parent.expanded = !parent.expanded;
} }
buttonRadius: Appearance.rounding.full buttonRadius: Appearance.rounding.full
@@ -6,25 +6,25 @@ import Quickshell
import Quickshell.Widgets import Quickshell.Widgets
import Quickshell.Services.Notifications import Quickshell.Services.Notifications
Rectangle { // App icon MaterialCookie { // App icon
id: root id: root
property var appIcon: "" property var appIcon: ""
property var summary: "" property var summary: ""
property var urgency: NotificationUrgency.Normal property var urgency: NotificationUrgency.Normal
property bool isUrgent: urgency === NotificationUrgency.Critical
property var image: "" property var image: ""
property real scale: 1
property real size: 38 * scale
property real materialIconScale: 0.57 property real materialIconScale: 0.57
property real appIconScale: 0.8 property real appIconScale: 0.8
property real smallAppIconScale: 0.49 property real smallAppIconScale: 0.49
property real materialIconSize: size * materialIconScale property real materialIconSize: implicitSize * materialIconScale
property real appIconSize: size * appIconScale property real appIconSize: implicitSize * appIconScale
property real smallAppIconSize: size * smallAppIconScale property real smallAppIconSize: implicitSize * smallAppIconScale
implicitWidth: size implicitSize: 38 * scale
implicitHeight: size sides: isUrgent ? 10 : 0
radius: Appearance.rounding.full amplitude: implicitSize / 24
color: Appearance.colors.colSecondaryContainer
color: isUrgent ? Appearance.colors.colPrimary : Appearance.colors.colSecondaryContainer
Loader { Loader {
id: materialSymbolLoader id: materialSymbolLoader
active: root.appIcon == "" active: root.appIcon == ""
@@ -34,12 +34,10 @@ Rectangle { // App icon
const defaultIcon = NotificationUtils.findSuitableMaterialSymbol("") const defaultIcon = NotificationUtils.findSuitableMaterialSymbol("")
const guessedIcon = NotificationUtils.findSuitableMaterialSymbol(root.summary) const guessedIcon = NotificationUtils.findSuitableMaterialSymbol(root.summary)
return (root.urgency == NotificationUrgency.Critical && guessedIcon === defaultIcon) ? return (root.urgency == NotificationUrgency.Critical && guessedIcon === defaultIcon) ?
"release_alert" : guessedIcon "priority_high" : guessedIcon
} }
anchors.fill: parent anchors.fill: parent
color: (root.urgency == NotificationUrgency.Critical) ? color: isUrgent ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSecondaryContainer
ColorUtils.mix(Appearance.m3colors.m3onSecondary, Appearance.m3colors.m3onSecondaryContainer, 0.1) :
Appearance.m3colors.m3onSecondaryContainer
iconSize: root.materialIconSize iconSize: root.materialIconSize
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
@@ -6,6 +6,7 @@ import "./notification_utils.js" as NotificationUtils
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell import Quickshell
import Quickshell.Services.Notifications
/** /**
* A group of notifications from the same app. * A group of notifications from the same app.
@@ -154,6 +155,8 @@ MouseArea { // Notification group area
image: root?.multipleNotifications ? "" : notificationGroup?.notifications[0]?.image ?? "" image: root?.multipleNotifications ? "" : notificationGroup?.notifications[0]?.image ?? ""
appIcon: notificationGroup?.appIcon appIcon: notificationGroup?.appIcon
summary: notificationGroup?.notifications[root.notificationCount - 1]?.summary summary: notificationGroup?.notifications[root.notificationCount - 1]?.summary
urgency: root.notifications.some(n => n.urgency === NotificationUrgency.Critical.toString()) ?
NotificationUrgency.Critical : NotificationUrgency.Normal
} }
ColumnLayout { // Content ColumnLayout { // Content
@@ -33,6 +33,7 @@ TabButton {
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onPressed: (event) => { onPressed: (event) => {
button.click() // Because the MouseArea already consumed the event
const {x,y} = event const {x,y} = event
const stateY = buttonBackground.y; const stateY = buttonBackground.y;
rippleAnim.x = x; rippleAnim.x = x;
@@ -46,7 +47,6 @@ TabButton {
rippleAnim.restart(); rippleAnim.restart();
} }
onReleased: (event) => { onReleased: (event) => {
button.click() // Because the MouseArea already consumed the event
rippleFadeAnim.restart(); rippleFadeAnim.restart();
} }
} }
@@ -31,6 +31,7 @@ TabButton {
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onPressed: (event) => { onPressed: (event) => {
root.click() // Because the MouseArea already consumed the event
const {x,y} = event const {x,y} = event
const stateY = buttonBackground.y; const stateY = buttonBackground.y;
rippleAnim.x = x; rippleAnim.x = x;
@@ -44,7 +45,6 @@ TabButton {
rippleAnim.restart(); rippleAnim.restart();
} }
onReleased: (event) => { onReleased: (event) => {
root.click() // Because the MouseArea already consumed the event
rippleFadeAnim.restart(); rippleFadeAnim.restart();
} }
} }
@@ -234,7 +234,7 @@ Item { // Player instance
} }
TrackChangeButton { TrackChangeButton {
iconName: "skip_previous" iconName: "skip_previous"
onClicked: root.player?.previous() downAction: () => root.player?.previous()
} }
Item { Item {
id: progressBarContainer id: progressBarContainer
@@ -277,7 +277,7 @@ Item { // Player instance
} }
TrackChangeButton { TrackChangeButton {
iconName: "skip_next" iconName: "skip_next"
onClicked: root.player?.next() downAction: () => root.player?.next()
} }
} }
@@ -289,7 +289,7 @@ Item { // Player instance
property real size: 44 property real size: 44
implicitWidth: size implicitWidth: size
implicitHeight: size implicitHeight: size
onClicked: root.player.togglePlaying(); downAction: () => root.player.togglePlaying();
buttonRadius: root.player?.isPlaying ? Appearance?.rounding.normal : size / 2 buttonRadius: root.player?.isPlaying ? Appearance?.rounding.normal : size / 2
colBackground: root.player?.isPlaying ? blendedColors.colPrimary : blendedColors.colSecondaryContainer colBackground: root.player?.isPlaying ? blendedColors.colPrimary : blendedColors.colSecondaryContainer
@@ -18,7 +18,7 @@ Scope { // Scope
baseWidth: 40 baseWidth: 40
baseHeight: 40 baseHeight: 40
clickedWidth: baseWidth clickedWidth: baseWidth
clickedHeight: baseHeight + 20 clickedHeight: baseHeight + 10
buttonRadius: Appearance.rounding.normal buttonRadius: Appearance.rounding.normal
} }
@@ -84,7 +84,7 @@ Scope { // Scope
VerticalButtonGroup { VerticalButtonGroup {
OskControlButton { // Pin button OskControlButton { // Pin button
toggled: root.pinned toggled: root.pinned
onClicked: root.pinned = !root.pinned downAction: () => root.pinned = !root.pinned
contentItem: MaterialSymbol { contentItem: MaterialSymbol {
text: "keep" text: "keep"
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
@@ -111,7 +111,7 @@ Item {
id: workspaceArea id: workspaceArea
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
onClicked: { onPressed: {
if (root.draggingTargetWorkspace === -1) { if (root.draggingTargetWorkspace === -1) {
GlobalStates.overviewOpen = false GlobalStates.overviewOpen = false
Hyprland.dispatch(`workspace ${workspaceValue}`) Hyprland.dispatch(`workspace ${workspaceValue}`)
@@ -144,41 +144,13 @@ ContentPage {
title: Translation.tr("Policies") title: Translation.tr("Policies")
ConfigRow { ConfigRow {
ColumnLayout {
// Weeb policy
ContentSubsectionLabel {
text: Translation.tr("Weeb")
}
ConfigSelectionArray {
currentValue: Config.options.policies.weeb
onSelected: newValue => {
Config.options.policies.weeb = newValue;
}
options: [
{
displayName: Translation.tr("No"),
icon: "close",
value: 0
},
{
displayName: Translation.tr("Yes"),
icon: "check",
value: 1
},
{
displayName: Translation.tr("Closet"),
icon: "ev_shadow",
value: 2
}
]
}
}
// AI policy
ColumnLayout { ColumnLayout {
// AI policy
ContentSubsectionLabel { ContentSubsectionLabel {
text: Translation.tr("AI") text: Translation.tr("AI")
} }
ConfigSelectionArray { ConfigSelectionArray {
currentValue: Config.options.policies.ai currentValue: Config.options.policies.ai
onSelected: newValue => { onSelected: newValue => {
@@ -203,6 +175,38 @@ ContentPage {
] ]
} }
} }
// Weeb policy
ColumnLayout {
ContentSubsectionLabel {
text: Translation.tr("Weeb")
}
ConfigSelectionArray {
currentValue: Config.options.policies.weeb
onSelected: newValue => {
Config.options.policies.weeb = newValue;
}
options: [
{
displayName: Translation.tr("No"),
icon: "close",
value: 0
},
{
displayName: Translation.tr("Yes"),
icon: "check",
value: 1
},
{
displayName: Translation.tr("Closet"),
icon: "ev_shadow",
value: 2
}
]
}
}
} }
} }
@@ -64,8 +64,8 @@ ContentPage {
icon: "add_triangle" icon: "add_triangle"
text: Translation.tr("Sides") text: Translation.tr("Sides")
value: Config.options.background.clock.cookie.sides value: Config.options.background.clock.cookie.sides
from: 1 from: 0
to: 36 to: 40
stepSize: 1 stepSize: 1
onValueChanged: { onValueChanged: {
Config.options.background.clock.cookie.sides = value; Config.options.background.clock.cookie.sides = value;
@@ -569,6 +569,15 @@ ContentPage {
} }
} }
ConfigSwitch {
buttonIcon: "translate"
text: Translation.tr('Enable translator')
checked: Config.options.sidebar.translator.enable
onCheckedChanged: {
Config.options.sidebar.translator.enable = checked;
}
}
ContentSubsection { ContentSubsection {
title: Translation.tr("Corner open") title: Translation.tr("Corner open")
tooltip: Translation.tr("Allows you to open sidebars by clicking or hovering screen corners regardless of bar position") tooltip: Translation.tr("Allows you to open sidebars by clicking or hovering screen corners regardless of bar position")
@@ -336,10 +336,10 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
property int lastResponseLength: 0 property int lastResponseLength: 0
onContentHeightChanged: { onContentHeightChanged: {
if (atYEnd) positionViewAtEnd(); if (atYEnd) Qt.callLater(positionViewAtEnd);
} }
onCountChanged: { // Auto-scroll when new messages are added onCountChanged: { // Auto-scroll when new messages are added
if (atYEnd) positionViewAtEnd(); if (atYEnd) Qt.callLater(positionViewAtEnd);
} }
add: null // Prevent function calls from being janky add: null // Prevent function calls from being janky
@@ -374,10 +374,9 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
anchors.centerIn: parent anchors.centerIn: parent
spacing: 5 spacing: 5
MaterialSymbol { CookieWrappedMaterialSymbol {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
iconSize: 60 iconSize: 60
color: Appearance.m3colors.m3outline
text: "neurology" text: "neurology"
} }
StyledText { StyledText {
@@ -757,8 +756,8 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
delegate: ApiCommandButton { delegate: ApiCommandButton {
property string commandRepresentation: `${root.commandPrefix}${modelData.name}` property string commandRepresentation: `${root.commandPrefix}${modelData.name}`
buttonText: commandRepresentation buttonText: commandRepresentation
onClicked: { downAction: () => {
if(modelData.sendDirectly) { if (modelData.sendDirectly) {
root.handleInput(commandRepresentation) root.handleInput(commandRepresentation)
} else { } else {
messageInputField.text = commandRepresentation + (modelData.dontAddSpace ? "" : " ") messageInputField.text = commandRepresentation + (modelData.dontAddSpace ? "" : " ")
@@ -198,10 +198,9 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
spacing: 5 spacing: 5
MaterialSymbol { CookieWrappedMaterialSymbol {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
iconSize: 60 iconSize: 60
color: Appearance.m3colors.m3outline
text: "bookmark_heart" text: "bookmark_heart"
} }
StyledText { StyledText {
@@ -525,9 +524,10 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
MouseArea { MouseArea {
anchors.fill: parent
hoverEnabled: true hoverEnabled: true
PointingHandInteraction {} PointingHandInteraction {}
onClicked: { onPressed: {
nsfwSwitch.checked = !nsfwSwitch.checked nsfwSwitch.checked = !nsfwSwitch.checked
} }
} }
@@ -566,8 +566,8 @@ Item {
buttonText: commandRepresentation buttonText: commandRepresentation
colBackground: Appearance.colors.colLayer2 colBackground: Appearance.colors.colLayer2
onClicked: { downAction: () => {
if(modelData.sendDirectly) { if (modelData.sendDirectly) {
root.handleInput(commandRepresentation) root.handleInput(commandRepresentation)
} else { } else {
tagInputField.text = commandRepresentation + " " tagInputField.text = commandRepresentation + " "
@@ -10,7 +10,7 @@ GroupButton {
verticalPadding: 6 verticalPadding: 6
baseWidth: contentItem.implicitWidth + horizontalPadding * 2 baseWidth: contentItem.implicitWidth + horizontalPadding * 2
clickedWidth: baseWidth + 20 clickedWidth: baseWidth + 14
baseHeight: contentItem.implicitHeight + verticalPadding * 2 baseHeight: contentItem.implicitHeight + verticalPadding * 2
buttonRadius: down ? Appearance.rounding.verysmall : Appearance.rounding.small buttonRadius: down ? Appearance.rounding.verysmall : Appearance.rounding.small
@@ -12,12 +12,17 @@ Item {
id: root id: root
required property var scopeRoot required property var scopeRoot
anchors.fill: parent anchors.fill: parent
property bool aiChatEnabled: Config.options.policies.ai !== 0
property bool translatorEnabled: Config.options.sidebar.translator.enable
property bool animeEnabled: Config.options.policies.weeb !== 0
property bool animeCloset: Config.options.policies.weeb === 2
property var tabButtonList: [ property var tabButtonList: [
...(Config.options.policies.ai !== 0 ? [{"icon": "neurology", "name": Translation.tr("Intelligence")}] : []), ...(root.aiChatEnabled ? [{"icon": "neurology", "name": Translation.tr("Intelligence")}] : []),
{"icon": "translate", "name": Translation.tr("Translator")}, ...(root.translatorEnabled ? [{"icon": "translate", "name": Translation.tr("Translator")}] : []),
...(Config.options.policies.weeb === 1 ? [{"icon": "bookmark_heart", "name": Translation.tr("Anime")}] : []) ...((root.animeEnabled && !root.animeCloset) ? [{"icon": "bookmark_heart", "name": Translation.tr("Anime")}] : [])
] ]
property int selectedTab: 0 property int selectedTab: 0
property int tabCount: swipeView.count
function focusActiveItem() { function focusActiveItem() {
swipeView.currentItem.forceActiveFocus() swipeView.currentItem.forceActiveFocus()
@@ -26,7 +31,7 @@ Item {
Keys.onPressed: (event) => { Keys.onPressed: (event) => {
if (event.modifiers === Qt.ControlModifier) { if (event.modifiers === Qt.ControlModifier) {
if (event.key === Qt.Key_PageDown) { if (event.key === Qt.Key_PageDown) {
root.selectedTab = Math.min(root.selectedTab + 1, root.tabButtonList.length - 1) root.selectedTab = Math.min(root.selectedTab + 1, root.tabCount - 1)
event.accepted = true; event.accepted = true;
} }
else if (event.key === Qt.Key_PageUp) { else if (event.key === Qt.Key_PageUp) {
@@ -34,11 +39,11 @@ Item {
event.accepted = true; event.accepted = true;
} }
else if (event.key === Qt.Key_Tab) { else if (event.key === Qt.Key_Tab) {
root.selectedTab = (root.selectedTab + 1) % root.tabButtonList.length; root.selectedTab = (root.selectedTab + 1) % root.tabCount;
event.accepted = true; event.accepted = true;
} }
else if (event.key === Qt.Key_Backtab) { else if (event.key === Qt.Key_Backtab) {
root.selectedTab = (root.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length; root.selectedTab = (root.selectedTab - 1 + root.tabCount) % root.tabCount;
event.accepted = true; event.accepted = true;
} }
} }
@@ -52,6 +57,7 @@ Item {
PrimaryTabBar { // Tab strip PrimaryTabBar { // Tab strip
id: tabBar id: tabBar
visible: root.tabButtonList.length > 1
tabButtonList: root.tabButtonList tabButtonList: root.tabButtonList
externalTrackedTab: root.selectedTab externalTrackedTab: root.selectedTab
function onCurrentIndexChanged(currentIndex) { function onCurrentIndexChanged(currentIndex) {
@@ -83,9 +89,9 @@ Item {
} }
contentChildren: [ contentChildren: [
...(Config.options.policies.ai !== 0 ? [aiChat.createObject()] : []), ...((root.aiChatEnabled || (!root.translatorEnabled && !root.animeEnabled)) ? [aiChat.createObject()] : []),
translator.createObject(), ...(root.translatorEnabled ? [translator.createObject()] : []),
...(Config.options.policies.weeb === 0 ? [] : [anime.createObject()]) ...(root.animeEnabled ? [anime.createObject()] : [])
] ]
} }
@@ -79,155 +79,164 @@ Rectangle {
anchors.margins: messagePadding anchors.margins: messagePadding
spacing: root.contentSpacing spacing: root.contentSpacing
RowLayout { // Header Rectangle {
spacing: 15
Layout.fillWidth: true Layout.fillWidth: true
implicitWidth: headerRowLayout.implicitWidth + 4 * 2
implicitHeight: headerRowLayout.implicitHeight + 4 * 2
color: Appearance.colors.colSecondaryContainer
radius: Appearance.rounding.small
Rectangle { // Name RowLayout { // Header
id: nameWrapper id: headerRowLayout
color: Appearance.colors.colSecondaryContainer anchors {
// color: "transparent" fill: parent
radius: Appearance.rounding.small margins: 4
implicitHeight: Math.max(nameRowLayout.implicitHeight + 5 * 2, 30) }
Layout.fillWidth: true spacing: 18
Layout.alignment: Qt.AlignVCenter
RowLayout { Item { // Name
id: nameRowLayout id: nameWrapper
anchors.verticalCenter: parent.verticalCenter implicitHeight: Math.max(nameRowLayout.implicitHeight + 5 * 2, 30)
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right Layout.alignment: Qt.AlignVCenter
anchors.leftMargin: 10
anchors.rightMargin: 10
spacing: 7
Item { RowLayout {
Layout.alignment: Qt.AlignVCenter id: nameRowLayout
Layout.fillHeight: true anchors.verticalCenter: parent.verticalCenter
implicitWidth: messageData?.role == 'assistant' ? modelIcon.width : roleIcon.implicitWidth anchors.left: parent.left
implicitHeight: messageData?.role == 'assistant' ? modelIcon.height : roleIcon.implicitHeight anchors.right: parent.right
anchors.leftMargin: 10
anchors.rightMargin: 10
spacing: 7
CustomIcon { Item {
id: modelIcon Layout.alignment: Qt.AlignVCenter
anchors.centerIn: parent Layout.fillHeight: true
visible: messageData?.role == 'assistant' && Ai.models[messageData?.model].icon implicitWidth: messageData?.role == 'assistant' ? modelIcon.width : roleIcon.implicitWidth
width: Appearance.font.pixelSize.large implicitHeight: messageData?.role == 'assistant' ? modelIcon.height : roleIcon.implicitHeight
height: Appearance.font.pixelSize.large
source: messageData?.role == 'assistant' ? Ai.models[messageData?.model].icon :
messageData?.role == 'user' ? 'linux-symbolic' : 'desktop-symbolic'
colorize: true CustomIcon {
id: modelIcon
anchors.centerIn: parent
visible: messageData?.role == 'assistant' && Ai.models[messageData?.model].icon
width: Appearance.font.pixelSize.large
height: Appearance.font.pixelSize.large
source: messageData?.role == 'assistant' ? Ai.models[messageData?.model].icon :
messageData?.role == 'user' ? 'linux-symbolic' : 'desktop-symbolic'
colorize: true
color: Appearance.m3colors.m3onSecondaryContainer
}
MaterialSymbol {
id: roleIcon
anchors.centerIn: parent
visible: !modelIcon.visible
iconSize: Appearance.font.pixelSize.larger
color: Appearance.m3colors.m3onSecondaryContainer
text: messageData?.role == 'user' ? 'person' :
messageData?.role == 'interface' ? 'settings' :
messageData?.role == 'assistant' ? 'neurology' :
'computer'
}
}
StyledText {
id: providerName
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
elide: Text.ElideRight
font.pixelSize: Appearance.font.pixelSize.normal
color: Appearance.m3colors.m3onSecondaryContainer color: Appearance.m3colors.m3onSecondaryContainer
} text: messageData?.role == 'assistant' ? Ai.models[messageData?.model].name :
(messageData?.role == 'user' && SystemInfo.username) ? SystemInfo.username :
MaterialSymbol { Translation.tr("Interface")
id: roleIcon
anchors.centerIn: parent
visible: !modelIcon.visible
iconSize: Appearance.font.pixelSize.larger
color: Appearance.m3colors.m3onSecondaryContainer
text: messageData?.role == 'user' ? 'person' :
messageData?.role == 'interface' ? 'settings' :
messageData?.role == 'assistant' ? 'neurology' :
'computer'
} }
} }
}
StyledText { Button { // Not visible to model
id: providerName id: modelVisibilityIndicator
Layout.alignment: Qt.AlignVCenter visible: messageData?.role == 'interface'
Layout.fillWidth: true implicitWidth: 16
elide: Text.ElideRight implicitHeight: 30
font.pixelSize: Appearance.font.pixelSize.normal Layout.alignment: Qt.AlignVCenter
color: Appearance.m3colors.m3onSecondaryContainer
text: messageData?.role == 'assistant' ? Ai.models[messageData?.model].name : background: Item
(messageData?.role == 'user' && SystemInfo.username) ? SystemInfo.username :
Translation.tr("Interface") MaterialSymbol {
id: notVisibleToModelText
anchors.centerIn: parent
iconSize: Appearance.font.pixelSize.small
color: Appearance.colors.colSubtext
text: "visibility_off"
}
StyledToolTip {
text: Translation.tr("Not visible to model")
} }
} }
}
Button { // Not visible to model ButtonGroup {
id: modelVisibilityIndicator spacing: 5
visible: messageData?.role == 'interface'
implicitWidth: 16
implicitHeight: 30
Layout.alignment: Qt.AlignVCenter
background: Item AiMessageControlButton {
id: copyButton
buttonIcon: activated ? "inventory" : "content_copy"
MaterialSymbol { onClicked: {
id: notVisibleToModelText Quickshell.clipboardText = root.messageData?.content
anchors.centerIn: parent copyButton.activated = true
iconSize: Appearance.font.pixelSize.small copyIconTimer.restart()
color: Appearance.colors.colSubtext }
text: "visibility_off"
}
StyledToolTip {
text: Translation.tr("Not visible to model")
}
}
ButtonGroup { Timer {
spacing: 5 id: copyIconTimer
interval: 1500
repeat: false
onTriggered: {
copyButton.activated = false
}
}
AiMessageControlButton { StyledToolTip {
id: copyButton text: Translation.tr("Copy")
buttonIcon: activated ? "inventory" : "content_copy"
onClicked: {
Quickshell.clipboardText = root.messageData?.content
copyButton.activated = true
copyIconTimer.restart()
}
Timer {
id: copyIconTimer
interval: 1500
repeat: false
onTriggered: {
copyButton.activated = false
} }
} }
AiMessageControlButton {
StyledToolTip { id: editButton
text: Translation.tr("Copy") activated: root.editing
} enabled: root.messageData?.done ?? false
} buttonIcon: "edit"
AiMessageControlButton { onClicked: {
id: editButton root.editing = !root.editing
activated: root.editing if (!root.editing) { // Save changes
enabled: root.messageData?.done ?? false root.saveMessage()
buttonIcon: "edit" }
onClicked: { }
root.editing = !root.editing StyledToolTip {
if (!root.editing) { // Save changes text: root.editing ? Translation.tr("Save") : Translation.tr("Edit")
root.saveMessage()
} }
} }
StyledToolTip { AiMessageControlButton {
text: root.editing ? Translation.tr("Save") : Translation.tr("Edit") id: toggleMarkdownButton
activated: !root.renderMarkdown
buttonIcon: "code"
onClicked: {
root.renderMarkdown = !root.renderMarkdown
}
StyledToolTip {
text: Translation.tr("View Markdown source")
}
} }
} AiMessageControlButton {
AiMessageControlButton { id: deleteButton
id: toggleMarkdownButton buttonIcon: "close"
activated: !root.renderMarkdown onClicked: {
buttonIcon: "code" Ai.removeMessage(root.messageIndex)
onClicked: { }
root.renderMarkdown = !root.renderMarkdown StyledToolTip {
} text: Translation.tr("Delete")
StyledToolTip { }
text: Translation.tr("View Markdown source")
}
}
AiMessageControlButton {
id: deleteButton
buttonIcon: "close"
onClicked: {
Ai.removeMessage(root.messageIndex)
}
StyledToolTip {
text: Translation.tr("Delete")
} }
} }
} }
@@ -263,6 +272,8 @@ Rectangle {
property bool done: root.messageData?.done ?? false property bool done: root.messageData?.done ?? false
property bool completed: thisBlock.completed ?? false property bool completed: thisBlock.completed ?? false
property bool forceDisableChunkSplitting: root.messageData.content.includes("```")
source: thisBlock.type === "code" ? "MessageCodeBlock.qml" : source: thisBlock.type === "code" ? "MessageCodeBlock.qml" :
thisBlock.type === "think" ? "MessageThinkBlock.qml" : thisBlock.type === "think" ? "MessageThinkBlock.qml" :
"MessageTextBlock.qml" "MessageTextBlock.qml"
@@ -8,8 +8,9 @@ GroupButton {
property string buttonIcon property string buttonIcon
property bool activated: false property bool activated: false
toggled: activated toggled: activated
baseWidth: height baseWidth: height
colBackgroundHover: Appearance.colors.colSecondaryContainerHover
colBackgroundActive: Appearance.colors.colSecondaryContainerActive
contentItem: MaterialSymbol { contentItem: MaterialSymbol {
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
@@ -8,6 +8,7 @@ import qs.modules.common.functions
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland import Quickshell.Hyprland
ColumnLayout { ColumnLayout {
@@ -22,6 +23,9 @@ ColumnLayout {
property list<string> renderedLatexHashes: [] property list<string> renderedLatexHashes: []
property string renderedSegmentContent: "" property string renderedSegmentContent: ""
property string shownText: ""
property bool forceDisableChunkSplitting: parent?.forceDisableChunkSplitting ?? false
property bool fadeChunkSplitting: !forceDisableChunkSplitting && !editing && !/\n\|/.test(shownText) && Config.options.sidebar.ai.textFadeIn
Layout.fillWidth: true Layout.fillWidth: true
@@ -73,7 +77,7 @@ ColumnLayout {
renderLatex() renderLatex()
} else { } else {
// console.log("Editing mode enabled", segmentContent) // console.log("Editing mode enabled", segmentContent)
textArea.text = segmentContent root.shownText = segmentContent
} }
} }
@@ -88,7 +92,7 @@ ColumnLayout {
onRenderedSegmentContentChanged: { onRenderedSegmentContentChanged: {
// console.log("Rendered segment content changed: " + renderedSegmentContent); // console.log("Rendered segment content changed: " + renderedSegmentContent);
if (renderedSegmentContent) { if (renderedSegmentContent) {
textArea.text = renderedSegmentContent; root.shownText = renderedSegmentContent;
} }
} }
@@ -104,39 +108,85 @@ ColumnLayout {
} }
} }
TextArea { spacing: 0
id: textArea Repeater {
id: textLinesRepeater
Layout.fillWidth: true property list<real> textLineOpacities: []
readOnly: !editing model: ScriptModel {
selectByMouse: enableMouseSelection || editing // Split by either double newlines or single newlines in a list
renderType: Text.NativeRendering values: root.fadeChunkSplitting ? root.shownText.split(/\n\n(?= {0,2})|\n(?= {0,2}[-\*])/g).filter(line => line.trim() !== "") : [root.shownText]
font.family: Appearance.font.family.reading onValuesChanged: {
font.hintingPreference: Font.PreferNoHinting // Prevent weird bold text while (textLinesRepeater.textLineOpacities.length < values.length) {
font.pixelSize: Appearance.font.pixelSize.small textLinesRepeater.textLineOpacities.push(root.messageData.done ? 1 : 0);
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer }
selectionColor: Appearance.colors.colSecondaryContainer }
wrapMode: TextEdit.Wrap
color: messageData.thinking ? Appearance.colors.colSubtext : Appearance.colors.colOnLayer1
textFormat: renderMarkdown ? TextEdit.MarkdownText : TextEdit.PlainText
text: Translation.tr("Waiting for response...")
onTextChanged: {
if (!root.editing) return
segmentContent = text
} }
delegate: TextArea {
id: textArea
required property int index
required property string modelData
onLinkActivated: (link) => { // Fade in animation
Qt.openUrlExternally(link) visible: opacity > 0
GlobalStates.sidebarLeftOpen = false opacity: fadeChunkSplitting ? (textLinesRepeater.textLineOpacities[index] ?? (root.messageData.done ? 1 : 0)) : 1
} Connections {
target: root.messageData
function onDoneChanged() {
if (root.messageData.done) {
textLinesRepeater.textLineOpacities[textArea.index] = 1
}
}
}
Connections {
target: textLinesRepeater.model
function onValuesChanged() {
if (textLinesRepeater.model.values.length > textArea.index + 1) {
textLinesRepeater.textLineOpacities[textArea.index] = 1
}
}
}
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
MouseArea { // Pointing hand for links Layout.fillWidth: true
anchors.fill: parent readOnly: !editing
acceptedButtons: Qt.NoButton // Only for hover selectByMouse: enableMouseSelection || editing
hoverEnabled: true renderType: Text.NativeRendering
cursorShape: parent.hoveredLink !== "" ? Qt.PointingHandCursor : font.family: Appearance.font.family.reading
(enableMouseSelection || editing) ? Qt.IBeamCursor : Qt.ArrowCursor font.hintingPreference: Font.PreferNoHinting // Prevent weird bold text
font.pixelSize: Appearance.font.pixelSize.small
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
selectionColor: Appearance.colors.colSecondaryContainer
wrapMode: TextEdit.Wrap
color: messageData.thinking ? Appearance.colors.colSubtext : Appearance.colors.colOnLayer1
textFormat: renderMarkdown ? TextEdit.MarkdownText : TextEdit.PlainText
text: modelData
onTextChanged: {
if (!root.editing) return
segmentContent = text
}
onLinkActivated: (link) => {
Qt.openUrlExternally(link)
GlobalStates.sidebarLeftOpen = false
}
MouseArea { // Pointing hand for links
anchors.fill: parent
acceptedButtons: Qt.NoButton // Only for hover
hoverEnabled: true
cursorShape: parent.hoveredLink !== "" ? Qt.PointingHandCursor :
(enableMouseSelection || editing) ? Qt.IBeamCursor : Qt.ArrowCursor
}
// Rectangle {
// anchors.fill: parent
// color: "#22786378"
// border.width: 1
// border.color: "#7E7E7E"
// }
} }
} }
} }
@@ -83,7 +83,7 @@ Rectangle {
Layout.margins: 10 Layout.margins: 10
Layout.rightMargin: 0 Layout.rightMargin: 0
forceCircle: true forceCircle: true
onClicked: { downAction: () => {
root.setCollapsed(false) root.setCollapsed(false)
} }
contentItem: MaterialSymbol { contentItem: MaterialSymbol {
@@ -146,7 +146,7 @@ Rectangle {
toggled: root.selectedTab == index toggled: root.selectedTab == index
buttonText: modelData.name buttonText: modelData.name
buttonIcon: modelData.icon buttonIcon: modelData.icon
onClicked: { onPressed: {
root.selectedTab = index root.selectedTab = index
Persistent.states.sidebar.bottomGroup.tab = index Persistent.states.sidebar.bottomGroup.tab = index
} }
@@ -158,7 +158,7 @@ Rectangle {
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
forceCircle: true forceCircle: true
onClicked: { downAction: () => {
root.setCollapsed(true) root.setCollapsed(true)
} }
contentItem: MaterialSymbol { contentItem: MaterialSymbol {
@@ -70,7 +70,6 @@ Scope {
function toggle(): void { function toggle(): void {
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen; GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen;
if(GlobalStates.sidebarRightOpen) Notifications.timeoutAll();
} }
function close(): void { function close(): void {
@@ -79,7 +78,6 @@ Scope {
function open(): void { function open(): void {
GlobalStates.sidebarRightOpen = true; GlobalStates.sidebarRightOpen = true;
Notifications.timeoutAll();
} }
} }
@@ -89,7 +87,6 @@ Scope {
onPressed: { onPressed: {
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen; GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen;
if(GlobalStates.sidebarRightOpen) Notifications.timeoutAll();
} }
} }
GlobalShortcut { GlobalShortcut {
@@ -98,7 +95,6 @@ Scope {
onPressed: { onPressed: {
GlobalStates.sidebarRightOpen = true; GlobalStates.sidebarRightOpen = true;
Notifications.timeoutAll();
} }
} }
GlobalShortcut { GlobalShortcut {
@@ -50,7 +50,7 @@ Item {
clip: true clip: true
buttonText: `${monthShift != 0 ? "• " : ""}${viewingDate.toLocaleDateString(Qt.locale(), "MMMM yyyy")}` buttonText: `${monthShift != 0 ? "• " : ""}${viewingDate.toLocaleDateString(Qt.locale(), "MMMM yyyy")}`
tooltipText: (monthShift === 0) ? "" : Translation.tr("Jump to current month") tooltipText: (monthShift === 0) ? "" : Translation.tr("Jump to current month")
onClicked: { downAction: () => {
monthShift = 0; monthShift = 0;
} }
} }
@@ -60,7 +60,7 @@ Item {
} }
CalendarHeaderButton { CalendarHeaderButton {
forceCircle: true forceCircle: true
onClicked: { downAction: () => {
monthShift--; monthShift--;
} }
contentItem: MaterialSymbol { contentItem: MaterialSymbol {
@@ -72,7 +72,7 @@ Item {
} }
CalendarHeaderButton { CalendarHeaderButton {
forceCircle: true forceCircle: true
onClicked: { downAction: () => {
monthShift++; monthShift++;
} }
contentItem: MaterialSymbol { contentItem: MaterialSymbol {
@@ -11,6 +11,7 @@ import Quickshell.Hyprland
QuickToggleButton { QuickToggleButton {
id: root id: root
visible: BluetoothStatus.available
toggled: BluetoothStatus.enabled toggled: BluetoothStatus.enabled
buttonIcon: BluetoothStatus.connected ? "bluetooth_connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled" buttonIcon: BluetoothStatus.connected ? "bluetooth_connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled"
onClicked: { onClicked: {
@@ -109,12 +109,12 @@ Item {
AudioDeviceSelectorButton { AudioDeviceSelectorButton {
Layout.fillWidth: true Layout.fillWidth: true
input: false input: false
onClicked: root.showDeviceSelectorDialog(input) downAction: () => root.showDeviceSelectorDialog(input)
} }
AudioDeviceSelectorButton { AudioDeviceSelectorButton {
Layout.fillWidth: true Layout.fillWidth: true
input: true input: true
onClicked: root.showDeviceSelectorDialog(input) downAction: () => root.showDeviceSelectorDialog(input)
} }
} }
} }
@@ -280,6 +280,7 @@ Item { // Bar content region
color: rightSidebarButton.colText color: rightSidebarButton.colText
} }
MaterialSymbol { MaterialSymbol {
visible: BluetoothStatus.available
text: BluetoothStatus.connected ? "bluetooth_connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled" text: BluetoothStatus.connected ? "bluetooth_connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled"
iconSize: Appearance.font.pixelSize.larger iconSize: Appearance.font.pixelSize.larger
color: rightSidebarButton.colText color: rightSidebarButton.colText
@@ -12,6 +12,7 @@ import QtQuick
Singleton { Singleton {
id: root id: root
readonly property bool available: Bluetooth.adapters.values.length > 0
readonly property bool enabled: Bluetooth.defaultAdapter?.enabled ?? false readonly property bool enabled: Bluetooth.defaultAdapter?.enabled ?? false
readonly property BluetoothDevice firstActiveDevice: Bluetooth.defaultAdapter?.devices.values.find(device => device.connected) ?? null readonly property BluetoothDevice firstActiveDevice: Bluetooth.defaultAdapter?.devices.values.find(device => device.connected) ?? null
readonly property int activeDeviceCount: Bluetooth.defaultAdapter?.devices.values.filter(device => device.connected).length ?? 0 readonly property int activeDeviceCount: Bluetooth.defaultAdapter?.devices.values.filter(device => device.connected).length ?? 0
@@ -40,7 +40,7 @@ Singleton {
id: resetFilePathNextWallpaperChange id: resetFilePathNextWallpaperChange
enabled: false enabled: false
target: Config.options.background target: Config.options.background
onWallpaperPathChanged: { function onWallpaperPathChanged() {
root.filePath = "" root.filePath = ""
root.filePath = Directories.generatedMaterialThemePath root.filePath = Directories.generatedMaterialThemePath
resetFilePathNextWallpaperChange.enabled = false resetFilePathNextWallpaperChange.enabled = false
@@ -69,6 +69,7 @@ Singleton {
} }
property bool silent: false property bool silent: false
property int unread: 0
property var filePath: Directories.notificationsPath property var filePath: Directories.notificationsPath
property list<Notif> list: [] property list<Notif> list: []
property var popupList: list.filter((notif) => notif.popup); property var popupList: list.filter((notif) => notif.popup);
@@ -173,12 +174,17 @@ Singleton {
} }
} }
root.unread++;
root.notify(newNotifObject); root.notify(newNotifObject);
// console.log(notifToString(newNotifObject)); // console.log(notifToString(newNotifObject));
notifFileView.setText(stringifyList(root.list)); notifFileView.setText(stringifyList(root.list));
} }
} }
function markAllRead() {
root.unread = 0;
}
function discardNotification(id) { function discardNotification(id) {
console.log("[Notifications] Discarding notification with ID: " + id); console.log("[Notifications] Discarding notification with ID: " + id);
const index = root.list.findIndex((notif) => notif.notificationId === id); const index = root.list.findIndex((notif) => notif.notificationId === id);
+2 -2
View File
@@ -172,7 +172,7 @@ ApplicationWindow {
iconText: "edit" iconText: "edit"
buttonText: Translation.tr("Config file") buttonText: Translation.tr("Config file")
expanded: navRail.expanded expanded: navRail.expanded
onClicked: { downAction: () => {
Qt.openUrlExternally(`${Directories.config}/illogical-impulse/config.json`); Qt.openUrlExternally(`${Directories.config}/illogical-impulse/config.json`);
} }
@@ -190,7 +190,7 @@ ApplicationWindow {
required property var index required property var index
required property var modelData required property var modelData
toggled: root.currentPage === index toggled: root.currentPage === index
onClicked: root.currentPage = index; onPressed: root.currentPage = index;
expanded: navRail.expanded expanded: navRail.expanded
buttonIcon: modelData.icon buttonIcon: modelData.icon
buttonIconRotation: modelData.iconRotation || 0 buttonIconRotation: modelData.iconRotation || 0
+1 -1
View File
@@ -63,7 +63,7 @@ install-local-pkgbuild() {
x pushd $location x pushd $location
source ./PKGBUILD source ./PKGBUILD
x yay -S $installflags --asdeps "${depends[@]}" x yay -S --sudoloop $installflags --asdeps "${depends[@]}"
x makepkg -Asi --noconfirm x makepkg -Asi --noconfirm
x popd x popd