Merge branch 'end-4:main' into clean-testing

This commit is contained in:
jwihardi
2025-10-13 13:36:57 -04:00
committed by GitHub
63 changed files with 1041 additions and 519 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
+6 -15
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
@@ -27,20 +28,10 @@ Singleton {
property bool superReleaseMightTrigger: true property bool superReleaseMightTrigger: true
property bool workspaceShowNumbers: false property bool workspaceShowNumbers: false
Connections { onSidebarRightOpenChanged: {
target: Config if (GlobalStates.sidebarRightOpen) {
function onReadyChanged() { Notifications.timeoutAll();
if (Config.options.lock.launchOnStartup && Config.ready && Persistent.ready && Persistent.isNewHyprlandInstance) { Notifications.markAllRead();
GlobalStates.screenLocked = true;
}
}
}
Connections {
target: Persistent
function onReadyChanged() {
if (Config.options.lock.launchOnStartup && Config.ready && Persistent.ready && Persistent.isNewHyprlandInstance) {
GlobalStates.screenLocked = true;
}
} }
} }
+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
} }
+1 -1
View File
@@ -36,7 +36,7 @@ ApplicationWindow {
title: Translation.tr("Shell conflicts killer") title: Translation.tr("Shell conflicts killer")
Component.onCompleted: { Component.onCompleted: {
Config.ready // Just read to force init Config.readWriteDelay = 0;
MaterialThemeLoader.reapplyTheme(); MaterialThemeLoader.reapplyTheme();
} }
@@ -8,6 +8,7 @@ import qs.modules.common.functions
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import Quickshell.Io
import "./dateIndicator" import "./dateIndicator"
import "./minuteMarks" import "./minuteMarks"
@@ -35,12 +36,60 @@ Item {
implicitWidth: implicitSize implicitWidth: implicitSize
implicitHeight: implicitSize implicitHeight: implicitSize
function applyStyle(sides, dialStyle, hourHandStyle, minuteHandStyle, secondHandStyle, dateStyle) {
Config.options.background.clock.cookie.sides = sides
Config.options.background.clock.cookie.dialNumberStyle = dialStyle
Config.options.background.clock.cookie.hourHandStyle = hourHandStyle
Config.options.background.clock.cookie.minuteHandStyle = minuteHandStyle
Config.options.background.clock.cookie.secondHandStyle = secondHandStyle
Config.options.background.clock.cookie.dateStyle = dateStyle
}
function setClockPreset(category) {
if (!Config.options.background.clock.cookie.aiStyling) return;
if (category === "") return;
print("[Cookie clock] Setting clock preset for category: " + category)
// "abstract", "anime", "city", "minimalist", "landscape", "plants", "person", "space"
if (category == "abstract") {
applyStyle(10, "dots", "fill", "medium", "dot", "bubble")
} else if (category == "anime") {
applyStyle(12, "dots", "fill", "bold", "dot", "bubble")
} else if (category == "city" || category == "space") {
applyStyle(23, "full", "hollow", "medium", "classic", "bubble")
} else if (category == "minimalist") {
applyStyle(6, "none", "fill", "bold", "dot", "hide")
} else if (category == "landscape") {
applyStyle(14, "full", "hollow", "medium", "classic", "bubble")
} else if (category == "plants") {
applyStyle(9, "dots", "fill", "bold", "dot", "border")
} else if (category == "person") {
applyStyle(14, "full", "classic", "classic", "classic", "rect")
}
}
Connections {
target: Config
function onReadyChanged() {
categoryFileView.path = Directories.generatedWallpaperCategoryPath
}
}
FileView {
id: categoryFileView
path: ""
watchChanges: true
onFileChanged: reload()
onLoaded: {
root.setClockPreset(categoryFileView.text().trim())
}
}
DropShadow { DropShadow {
source: cookie source: cookie
anchors.fill: source anchors.fill: source
horizontalOffset: 0 horizontalOffset: 0
verticalOffset: 2 verticalOffset: 1
radius: 12 radius: 8
samples: radius * 2 + 1 samples: radius * 2 + 1
color: root.colShadow color: root.colShadow
transparentBorder: true transparentBorder: true
@@ -54,102 +103,102 @@ Item {
sides: Config.options.background.clock.cookie.sides sides: Config.options.background.clock.cookie.sides
color: root.colBackground color: root.colBackground
constantlyRotate: Config.options.background.clock.cookie.constantlyRotate constantlyRotate: Config.options.background.clock.cookie.constantlyRotate
}
// Hour/minutes numbers/dots/lines
// Hour/minutes numbers/dots/lines MinuteMarks {
MinuteMarks {
anchors.fill: parent
color: root.colOnBackground
}
// Stupid extra hour marks in the middle
FadeLoader {
id: hourMarksLoader
anchors.centerIn: parent
shown: Config.options.background.clock.cookie.hourMarks
sourceComponent: HourMarks {
implicitSize: 135 * (1.75 - 0.75 * hourMarksLoader.opacity)
color: root.colOnBackground
colOnBackground: ColorUtils.mix(root.colBackgroundInfo, root.colOnBackground, 0.5)
}
}
// Number column in the middle
FadeLoader {
id: timeColumnLoader
anchors.centerIn: parent
shown: Config.options.background.clock.cookie.timeIndicators
scale: 1.4 - 0.4 * timeColumnLoader.shown
Behavior on scale {
animation: Appearance.animation.elementResize.numberAnimation.createObject(this)
}
sourceComponent: TimeColumn {
color: root.colBackgroundInfo
}
}
// Hour hand
FadeLoader {
anchors.fill: parent
z: 1
shown: Config.options.background.clock.cookie.hourHandStyle !== "hide"
sourceComponent: HourHand {
clockHour: root.clockHour
clockMinute: root.clockMinute
style: Config.options.background.clock.cookie.hourHandStyle
color: root.colHourHand
}
}
// Minute hand
FadeLoader {
anchors.fill: parent
z: 2
shown: Config.options.background.clock.cookie.minuteHandStyle !== "hide"
sourceComponent: MinuteHand {
anchors.fill: parent anchors.fill: parent
clockMinute: root.clockMinute color: root.colOnBackground
style: Config.options.background.clock.cookie.minuteHandStyle
color: root.colMinuteHand
} }
}
// Second hand // Stupid extra hour marks in the middle
FadeLoader { FadeLoader {
id: secondHandLoader id: hourMarksLoader
z: (Config.options.background.clock.cookie.secondHandStyle === "line") ? 2 : 3 anchors.centerIn: parent
shown: Config.options.time.secondPrecision && Config.options.background.clock.cookie.secondHandStyle !== "hide" shown: Config.options.background.clock.cookie.hourMarks
anchors.fill: parent sourceComponent: HourMarks {
sourceComponent: SecondHand { implicitSize: 135 * (1.75 - 0.75 * hourMarksLoader.opacity)
id: secondHand color: root.colOnBackground
clockSecond: root.clockSecond colOnBackground: ColorUtils.mix(root.colBackgroundInfo, root.colOnBackground, 0.5)
style: Config.options.background.clock.cookie.secondHandStyle }
color: root.colSecondHand
} }
}
// Center dot // Number column in the middle
FadeLoader { FadeLoader {
z: 4 id: timeColumnLoader
anchors.centerIn: parent anchors.centerIn: parent
shown: Config.options.background.clock.cookie.minuteHandStyle !== "bold" shown: Config.options.background.clock.cookie.timeIndicators
sourceComponent: Rectangle { scale: 1.4 - 0.4 * timeColumnLoader.shown
color: Config.options.background.clock.cookie.minuteHandStyle === "medium" ? root.colBackground : root.colMinuteHand Behavior on scale {
implicitWidth: 6 animation: Appearance.animation.elementResize.numberAnimation.createObject(this)
implicitHeight: implicitWidth }
radius: width / 2
sourceComponent: TimeColumn {
color: root.colBackgroundInfo
}
} }
}
// Date // Hour hand
FadeLoader { FadeLoader {
anchors.fill: parent anchors.fill: parent
shown: Config.options.background.clock.cookie.dateStyle !== "hide" z: 1
shown: Config.options.background.clock.cookie.hourHandStyle !== "hide"
sourceComponent: HourHand {
clockHour: root.clockHour
clockMinute: root.clockMinute
style: Config.options.background.clock.cookie.hourHandStyle
color: root.colHourHand
}
}
sourceComponent: DateIndicator { // Minute hand
color: root.colBackgroundInfo FadeLoader {
style: Config.options.background.clock.cookie.dateStyle anchors.fill: parent
z: 2
shown: Config.options.background.clock.cookie.minuteHandStyle !== "hide"
sourceComponent: MinuteHand {
anchors.fill: parent
clockMinute: root.clockMinute
style: Config.options.background.clock.cookie.minuteHandStyle
color: root.colMinuteHand
}
}
// Second hand
FadeLoader {
id: secondHandLoader
z: (Config.options.background.clock.cookie.secondHandStyle === "line") ? 2 : 3
shown: Config.options.time.secondPrecision && Config.options.background.clock.cookie.secondHandStyle !== "hide"
anchors.fill: parent
sourceComponent: SecondHand {
id: secondHand
clockSecond: root.clockSecond
style: Config.options.background.clock.cookie.secondHandStyle
color: root.colSecondHand
}
}
// Center dot
FadeLoader {
z: 4
anchors.centerIn: parent
shown: Config.options.background.clock.cookie.minuteHandStyle !== "bold"
sourceComponent: Rectangle {
color: Config.options.background.clock.cookie.minuteHandStyle === "medium" ? root.colBackground : root.colMinuteHand
implicitWidth: 6
implicitHeight: implicitWidth
radius: width / 2
}
}
// Date
FadeLoader {
anchors.fill: parent
shown: Config.options.background.clock.cookie.dateStyle !== "hide"
sourceComponent: DateIndicator {
color: root.colBackgroundInfo
style: Config.options.background.clock.cookie.dateStyle
}
} }
} }
} }
@@ -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 {
@@ -7,27 +7,31 @@ import qs.modules.common.widgets
import QtQuick import QtQuick
Item { Item {
property int bubbleIndex: 0 property bool isMonth: false
property real targetSize: 0 property real targetSize: 0
property alias text: bubbleText.text
text: Qt.locale().toString(DateTime.clock.date, isMonth ? "MM" : "d")
MaterialCookie { MaterialCookie {
z: 5 z: 5
sides: bubbleIndex === 0 ? 4 : 1 sides: isMonth ? 1 : 4
anchors.centerIn: parent anchors.centerIn: parent
color: bubbleIndex === 0.0 ? Appearance.colors.colTertiaryContainer : Appearance.colors.colPrimaryContainer color: isMonth ? Appearance.colors.colPrimaryContainer : Appearance.colors.colTertiaryContainer
implicitSize: targetSize implicitSize: targetSize
constantlyRotate: Config.options.background.clock.cookie.constantlyRotate constantlyRotate: Config.options.background.clock.cookie.constantlyRotate
} }
StyledText { StyledText {
id: bubbleText
z: 6 z: 6
anchors.centerIn: parent anchors.centerIn: parent
text: bubbleIndex === 0.0 ? DateTime.date.substring(5, 7) : DateTime.date.substring(8, 10) color: isMonth ? Appearance.colors.colPrimary : Appearance.colors.colTertiary
color: bubbleIndex === 0.0 ? Appearance.colors.colTertiary : Appearance.colors.colPrimary opacity: root.style === "bubble" ? 1 : 0
opacity: root.style === "bubble" ? 1.0 : 0
font { font {
family: Appearance.font.family.expressive family: Appearance.font.family.expressive
pixelSize: 30 pixelSize: 30
weight: 1000 weight: Font.Black
} }
Behavior on opacity { Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
@@ -53,7 +53,7 @@ Item {
sourceComponent: BubbleDate { sourceComponent: BubbleDate {
implicitWidth: dayBubbleLoader.targetSize implicitWidth: dayBubbleLoader.targetSize
implicitHeight: dayBubbleLoader.targetSize implicitHeight: dayBubbleLoader.targetSize
bubbleIndex: 0 isMonth: false
targetSize: dayBubbleLoader.targetSize targetSize: dayBubbleLoader.targetSize
} }
} }
@@ -71,7 +71,7 @@ Item {
sourceComponent: BubbleDate { sourceComponent: BubbleDate {
implicitWidth: monthBubbleLoader.targetSize implicitWidth: monthBubbleLoader.targetSize
implicitHeight: monthBubbleLoader.targetSize implicitHeight: monthBubbleLoader.targetSize
bubbleIndex: 1 isMonth: true
targetSize: monthBubbleLoader.targetSize targetSize: monthBubbleLoader.targetSize
} }
} }
@@ -295,6 +295,19 @@ 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 ? notificationUnreadCount.implicitHeight : 0
implicitWidth: reveal ? notificationUnreadCount.implicitWidth : 0
Behavior on Layout.rightMargin {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
NotificationUnreadCount {
id: notificationUnreadCount
}
}
MaterialSymbol { MaterialSymbol {
Layout.rightMargin: indicatorsRowLayout.realSpacing Layout.rightMargin: indicatorsRowLayout.realSpacing
text: Network.materialSymbol text: Network.materialSymbol
@@ -302,6 +315,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
@@ -0,0 +1,39 @@
import QtQuick
import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
MaterialSymbol {
id: root
readonly property bool showUnreadCount: Config.options.bar.indicators.notifications.showUnreadCount
text: Notifications.silent ? "notifications_paused" : "notifications"
iconSize: Appearance.font.pixelSize.larger
color: rightSidebarButton.colText
Rectangle {
id: notifPing
visible: !Notifications.silent && Notifications.unread > 0
anchors {
right: parent.right
top: parent.top
rightMargin: root.showUnreadCount ? 0 : 1
topMargin: root.showUnreadCount ? 0 : 3
}
radius: Appearance.rounding.full
color: Appearance.colors.colOnLayer0
z: 1
implicitHeight: root.showUnreadCount ? Math.max(notificationCounterText.implicitWidth, notificationCounterText.implicitHeight) : 8
implicitWidth: implicitHeight
StyledText {
id: notificationCounterText
visible: root.showUnreadCount
anchors.centerIn: parent
font.pixelSize: Appearance.font.pixelSize.smallest
color: Appearance.colors.colLayer0
text: Notifications.unread
}
}
}
@@ -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
@@ -9,6 +9,7 @@ Singleton {
property string filePath: Directories.shellConfigPath property string filePath: Directories.shellConfigPath
property alias options: configOptionsJsonAdapter property alias options: configOptionsJsonAdapter
property bool ready: false property bool ready: false
property int readWriteDelay: 50 // milliseconds
function setNestedValue(nestedKey, value) { function setNestedValue(nestedKey, value) {
let keys = nestedKey.split("."); let keys = nestedKey.split(".");
@@ -40,11 +41,30 @@ Singleton {
obj[keys[keys.length - 1]] = convertedValue; obj[keys[keys.length - 1]] = convertedValue;
} }
Timer {
id: fileReloadTimer
interval: root.readWriteDelay
repeat: false
onTriggered: {
configFileView.reload()
}
}
Timer {
id: fileWriteTimer
interval: root.readWriteDelay
repeat: false
onTriggered: {
configFileView.writeAdapter()
}
}
FileView { FileView {
id: configFileView
path: root.filePath path: root.filePath
watchChanges: true watchChanges: true
onFileChanged: reload() onFileChanged: fileReloadTimer.restart()
onAdapterUpdated: writeAdapter() onAdapterUpdated: fileWriteTimer.restart()
onLoaded: root.ready = true onLoaded: root.ready = true
onLoadFailed: error => { onLoadFailed: error => {
if (error == FileViewError.FileNotFound) { if (error == FileViewError.FileNotFound) {
@@ -130,14 +150,15 @@ Singleton {
property string style: "cookie" // Options: "cookie", "digital" property string style: "cookie" // Options: "cookie", "digital"
property real scale: 1 property real scale: 1
property JsonObject cookie: JsonObject { property JsonObject cookie: JsonObject {
property bool aiStyling: false
property int sides: 14 property int sides: 14
property string dialNumberStyle: "full" // Options: "dots" , "numbers", "full" , "none" property string dialNumberStyle: "full" // Options: "dots" , "numbers", "full" , "none"
property string hourHandStyle: "fill" // Options: "classic", "fill", "hollow", "hide" property string hourHandStyle: "fill" // Options: "classic", "fill", "hollow", "hide"
property string minuteHandStyle: "medium" // Options "classic", "thin", "medium", "bold", "hide" property string minuteHandStyle: "medium" // Options "classic", "thin", "medium", "bold", "hide"
property string secondHandStyle: "dot" // Options: "dot", "line" , "hide" property string secondHandStyle: "dot" // Options: "dot", "line", "classic", "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 +233,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 {
@@ -274,6 +300,7 @@ Singleton {
} }
property JsonObject lock: JsonObject { property JsonObject lock: JsonObject {
property bool useHyprlock: false
property bool launchOnStartup: false property bool launchOnStartup: false
property JsonObject blur: JsonObject { property JsonObject blur: JsonObject {
property bool enable: false property bool enable: false
@@ -341,8 +368,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"
@@ -401,7 +432,7 @@ Singleton {
} }
property JsonObject triggerCondition: JsonObject { property JsonObject triggerCondition: JsonObject {
property list<string> networkNameKeywords: ["airport", "cafe", "college", "company", "eduroam", "free", "guest", "public", "school", "university"] property list<string> networkNameKeywords: ["airport", "cafe", "college", "company", "eduroam", "free", "guest", "public", "school", "university"]
property list<string> fileKeywords: ["anime", "ecchi", "hentai", "yande.re", "konachan", "breast", "nipples", "pussy", "nsfw", "spoiler", "girl"] property list<string> fileKeywords: ["anime", "booru", "ecchi", "hentai", "yande.re", "konachan", "breast", "nipples", "pussy", "nsfw", "spoiler", "girl"]
property list<string> linkKeywords: ["hentai", "porn", "sukebei", "hitomi.la", "rule34", "gelbooru", "fanbox", "dlsite"] property list<string> linkKeywords: ["hentai", "porn", "sukebei", "hitomi.la", "rule34", "gelbooru", "fanbox", "dlsite"]
} }
} }
@@ -34,6 +34,7 @@ Singleton {
property string todoPath: FileUtils.trimFileProtocol(`${Directories.state}/user/todo.json`) property string todoPath: FileUtils.trimFileProtocol(`${Directories.state}/user/todo.json`)
property string notificationsPath: FileUtils.trimFileProtocol(`${Directories.cache}/notifications/notifications.json`) property string notificationsPath: FileUtils.trimFileProtocol(`${Directories.cache}/notifications/notifications.json`)
property string generatedMaterialThemePath: FileUtils.trimFileProtocol(`${Directories.state}/user/generated/colors.json`) property string generatedMaterialThemePath: FileUtils.trimFileProtocol(`${Directories.state}/user/generated/colors.json`)
property string generatedWallpaperCategoryPath: FileUtils.trimFileProtocol(`${Directories.state}/user/generated/wallpaper/category.txt`)
property string cliphistDecode: FileUtils.trimFileProtocol(`/tmp/quickshell/media/cliphist`) property string cliphistDecode: FileUtils.trimFileProtocol(`/tmp/quickshell/media/cliphist`)
property string screenshotTemp: "/tmp/quickshell/media/screenshot" property string screenshotTemp: "/tmp/quickshell/media/screenshot"
property string wallpaperSwitchScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/colors/switchwall.sh`) property string wallpaperSwitchScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/colors/switchwall.sh`)
@@ -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
@@ -32,7 +32,8 @@ TabButton {
MouseArea { MouseArea {
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();
} }
} }
@@ -30,7 +30,8 @@ TabButton {
MouseArea { MouseArea {
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();
} }
} }
@@ -112,6 +112,10 @@ Scope {
description: "Locks the screen" description: "Locks the screen"
onPressed: { onPressed: {
if (Config.options.lock.useHyprlock) {
Quickshell.execDetached(["hyprlock"])
return;
}
GlobalStates.screenLocked = true; GlobalStates.screenLocked = true;
} }
} }
@@ -125,4 +129,23 @@ Scope {
lockContext.shouldReFocus(); lockContext.shouldReFocus();
} }
} }
Connections {
target: Config
function onReadyChanged() {
print("lock after config")
if (Config.options.lock.launchOnStartup && Config.ready && Persistent.ready && Persistent.isNewHyprlandInstance) {
Hyprland.dispatch("global quickshell:lock")
}
}
}
Connections {
target: Persistent
function onReadyChanged() {
print("lock after persistent")
if (Config.options.lock.launchOnStartup && Config.ready && Persistent.ready && Persistent.isNewHyprlandInstance) {
Hyprland.dispatch("global quickshell:lock")
}
}
}
} }
@@ -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}`)
@@ -7,6 +7,19 @@ import qs.modules.common.widgets
ContentPage { ContentPage {
forceWidth: true forceWidth: true
ContentSection {
icon: "notifications"
title: Translation.tr("Notifications")
ConfigSwitch {
buttonIcon: "counter_2"
text: Translation.tr("Unread indicator: show count")
checked: Config.options.bar.indicators.notifications.showUnreadCount
onCheckedChanged: {
Config.options.bar.indicators.notifications.showUnreadCount = checked;
}
}
}
ContentSection { ContentSection {
icon: "spoke" icon: "spoke"
@@ -130,57 +143,24 @@ ContentPage {
} }
ContentSection { ContentSection {
icon: "workspaces" icon: "shelf_auto_hide"
title: Translation.tr("Workspaces") title: Translation.tr("Tray")
ConfigSwitch { ConfigSwitch {
buttonIcon: "counter_1" buttonIcon: "keep"
text: Translation.tr('Always show numbers') text: Translation.tr('Make icons pinned by default')
checked: Config.options.bar.workspaces.alwaysShowNumbers checked: Config.options.bar.tray.invertPinnedItems
onCheckedChanged: { onCheckedChanged: {
Config.options.bar.workspaces.alwaysShowNumbers = checked; Config.options.bar.tray.invertPinnedItems = checked;
} }
} }
ConfigSwitch {
buttonIcon: "award_star"
text: Translation.tr('Show app icons')
checked: Config.options.bar.workspaces.showAppIcons
onCheckedChanged: {
Config.options.bar.workspaces.showAppIcons = checked;
}
}
ConfigSwitch { ConfigSwitch {
buttonIcon: "colors" buttonIcon: "colors"
text: Translation.tr('Tint app icons') text: Translation.tr('Tint icons')
checked: Config.options.bar.workspaces.monochromeIcons checked: Config.options.bar.tray.monochromeIcons
onCheckedChanged: { onCheckedChanged: {
Config.options.bar.workspaces.monochromeIcons = checked; Config.options.bar.tray.monochromeIcons = checked;
}
}
ConfigSpinBox {
icon: "view_column"
text: Translation.tr("Workspaces shown")
value: Config.options.bar.workspaces.shown
from: 1
to: 30
stepSize: 1
onValueChanged: {
Config.options.bar.workspaces.shown = value;
}
}
ConfigSpinBox {
icon: "touch_long"
text: Translation.tr("Number show delay when pressing Super (ms)")
value: Config.options.bar.workspaces.showNumberDelay
from: 0
to: 1000
stepSize: 50
onValueChanged: {
Config.options.bar.workspaces.showNumberDelay = value;
} }
} }
} }
@@ -248,29 +228,6 @@ ContentPage {
} }
} }
ContentSection {
icon: "shelf_auto_hide"
title: Translation.tr("Tray")
ConfigSwitch {
buttonIcon: "keep"
text: Translation.tr('Make icons pinned by default')
checked: Config.options.bar.tray.invertPinnedItems
onCheckedChanged: {
Config.options.bar.tray.invertPinnedItems = checked;
}
}
ConfigSwitch {
buttonIcon: "colors"
text: Translation.tr('Tint icons')
checked: Config.options.bar.tray.monochromeIcons
onCheckedChanged: {
Config.options.bar.tray.monochromeIcons = checked;
}
}
}
ContentSection { ContentSection {
icon: "cloud" icon: "cloud"
title: Translation.tr("Weather") title: Translation.tr("Weather")
@@ -283,4 +240,60 @@ ContentPage {
} }
} }
} }
ContentSection {
icon: "workspaces"
title: Translation.tr("Workspaces")
ConfigSwitch {
buttonIcon: "counter_1"
text: Translation.tr('Always show numbers')
checked: Config.options.bar.workspaces.alwaysShowNumbers
onCheckedChanged: {
Config.options.bar.workspaces.alwaysShowNumbers = checked;
}
}
ConfigSwitch {
buttonIcon: "award_star"
text: Translation.tr('Show app icons')
checked: Config.options.bar.workspaces.showAppIcons
onCheckedChanged: {
Config.options.bar.workspaces.showAppIcons = checked;
}
}
ConfigSwitch {
buttonIcon: "colors"
text: Translation.tr('Tint app icons')
checked: Config.options.bar.workspaces.monochromeIcons
onCheckedChanged: {
Config.options.bar.workspaces.monochromeIcons = checked;
}
}
ConfigSpinBox {
icon: "view_column"
text: Translation.tr("Workspaces shown")
value: Config.options.bar.workspaces.shown
from: 1
to: 30
stepSize: 1
onValueChanged: {
Config.options.bar.workspaces.shown = value;
}
}
ConfigSpinBox {
icon: "touch_long"
text: Translation.tr("Number show delay when pressing Super (ms)")
value: Config.options.bar.workspaces.showNumberDelay
from: 0
to: 1000
stepSize: 50
onValueChanged: {
Config.options.bar.workspaces.showNumberDelay = value;
}
}
}
} }
@@ -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
}
]
}
}
} }
} }
@@ -59,13 +59,25 @@ ContentPage {
ContentSubsection { ContentSubsection {
visible: Config.options.background.clock.style === "cookie" visible: Config.options.background.clock.style === "cookie"
title: Translation.tr("Cookie clock settings") title: Translation.tr("Cookie clock settings")
ConfigSwitch {
buttonIcon: "wand_stars"
text: Translation.tr("Auto styling with Gemini")
checked: Config.options.background.clock.cookie.aiStyling
onCheckedChanged: {
Config.options.background.clock.cookie.aiStyling = checked;
}
StyledToolTip {
text: Translation.tr("Uses Gemini to categorize the wallpaper then picks a preset based on it.\nYou'll need to set Gemini API key on the left sidebar first.\nImages are downscaled for performance, but just to be safe,\ndo not select wallpapers with sensitive information.")
}
}
ConfigSpinBox { ConfigSpinBox {
visible: Config.options.background.clock.style === "cookie"
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;
@@ -73,7 +85,6 @@ ContentPage {
} }
ConfigSwitch { ConfigSwitch {
visible: Config.options.background.clock.style === "cookie"
buttonIcon: "autoplay" buttonIcon: "autoplay"
text: Translation.tr("Constantly rotate") text: Translation.tr("Constantly rotate")
checked: Config.options.background.clock.cookie.constantlyRotate checked: Config.options.background.clock.cookie.constantlyRotate
@@ -86,7 +97,6 @@ ContentPage {
} }
ConfigRow { ConfigRow {
visible: Config.options.background.clock.style === "cookie"
ConfigSwitch { ConfigSwitch {
enabled: Config.options.background.clock.style === "cookie" && Config.options.background.clock.cookie.dialNumberStyle === "dots" || Config.options.background.clock.cookie.dialNumberStyle === "full" enabled: Config.options.background.clock.style === "cookie" && Config.options.background.clock.cookie.dialNumberStyle === "dots" || Config.options.background.clock.cookie.dialNumberStyle === "full"
@@ -452,6 +462,18 @@ ContentPage {
icon: "lock" icon: "lock"
title: Translation.tr("Lock screen") title: Translation.tr("Lock screen")
ConfigSwitch {
buttonIcon: "water_drop"
text: Translation.tr('Use Hyprlock (instead of Quickshell)')
checked: Config.options.lock.useHyprlock
onCheckedChanged: {
Config.options.lock.useHyprlock = checked;
}
StyledToolTip {
text: Translation.tr("If you want to somehow use fingerprint unlock...")
}
}
ConfigSwitch { ConfigSwitch {
buttonIcon: "account_circle" buttonIcon: "account_circle"
text: Translation.tr('Launch on startup') text: Translation.tr('Launch on startup')
@@ -569,6 +591,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 ? "" : " ")
@@ -778,4 +777,4 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
} }
} }
@@ -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 {
@@ -514,24 +513,22 @@ Item {
text: "•" text: "•"
} }
Item { // NSFW toggle MouseArea { // NSFW toggle
visible: width > 0 visible: width > 0
implicitWidth: switchesRow.implicitWidth implicitWidth: switchesRow.implicitWidth
Layout.fillHeight: true Layout.fillHeight: true
hoverEnabled: true
PointingHandInteraction {}
onPressed: {
nsfwSwitch.checked = !nsfwSwitch.checked
}
RowLayout { RowLayout {
id: switchesRow id: switchesRow
spacing: 5 spacing: 5
anchors.centerIn: parent anchors.centerIn: parent
MouseArea {
hoverEnabled: true
PointingHandInteraction {}
onClicked: {
nsfwSwitch.checked = !nsfwSwitch.checked
}
}
StyledText { StyledText {
Layout.fillHeight: true Layout.fillHeight: true
Layout.leftMargin: 10 Layout.leftMargin: 10
@@ -552,6 +549,7 @@ Item {
} }
} }
} }
} }
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
@@ -566,8 +564,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()] : [])
] ]
} }
@@ -78,156 +78,165 @@ Rectangle {
anchors.top: parent.top anchors.top: parent.top
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
RowLayout { // Header
id: headerRowLayout
anchors {
fill: parent
margins: 4
}
spacing: 18
Rectangle { // Name Item { // Name
id: nameWrapper id: nameWrapper
color: Appearance.colors.colSecondaryContainer implicitHeight: Math.max(nameRowLayout.implicitHeight + 5 * 2, 30)
// color: "transparent" Layout.fillWidth: true
radius: Appearance.rounding.small Layout.alignment: Qt.AlignVCenter
implicitHeight: Math.max(nameRowLayout.implicitHeight + 5 * 2, 30)
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
RowLayout { RowLayout {
id: nameRowLayout id: nameRowLayout
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.leftMargin: 10 anchors.leftMargin: 10
anchors.rightMargin: 10 anchors.rightMargin: 10
spacing: 7 spacing: 7
Item { Item {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Layout.fillHeight: true Layout.fillHeight: true
implicitWidth: messageData?.role == 'assistant' ? modelIcon.width : roleIcon.implicitWidth implicitWidth: messageData?.role == 'assistant' ? modelIcon.width : roleIcon.implicitWidth
implicitHeight: messageData?.role == 'assistant' ? modelIcon.height : roleIcon.implicitHeight implicitHeight: messageData?.role == 'assistant' ? modelIcon.height : roleIcon.implicitHeight
CustomIcon { CustomIcon {
id: modelIcon id: modelIcon
anchors.centerIn: parent anchors.centerIn: parent
visible: messageData?.role == 'assistant' && Ai.models[messageData?.model].icon visible: messageData?.role == 'assistant' && Ai.models[messageData?.model].icon
width: Appearance.font.pixelSize.large width: Appearance.font.pixelSize.large
height: Appearance.font.pixelSize.large height: Appearance.font.pixelSize.large
source: messageData?.role == 'assistant' ? Ai.models[messageData?.model].icon : source: messageData?.role == 'assistant' ? Ai.models[messageData?.model].icon :
messageData?.role == 'user' ? 'linux-symbolic' : 'desktop-symbolic' messageData?.role == 'user' ? 'linux-symbolic' : 'desktop-symbolic'
colorize: true 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
AiMessageControlButton { repeat: false
id: copyButton onTriggered: {
buttonIcon: activated ? "inventory" : "content_copy" copyButton.activated = false
}
onClicked: { }
Quickshell.clipboardText = root.messageData?.content
copyButton.activated = true StyledToolTip {
copyIconTimer.restart() text: Translation.tr("Copy")
}
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")
} }
} }
} }
@@ -262,6 +271,8 @@ Rectangle {
property bool thinking: root.messageData?.thinking ?? true property bool thinking: root.messageData?.thinking ?? true
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" :
@@ -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: {
@@ -10,7 +10,7 @@ import Quickshell.Io
import Quickshell.Hyprland import Quickshell.Hyprland
QuickToggleButton { QuickToggleButton {
toggled: Network.wifiEnabled toggled: Network.wifiStatus !== "disabled"
buttonIcon: Network.materialSymbol buttonIcon: Network.materialSymbol
onClicked: Network.toggleWifi() onClicked: Network.toggleWifi()
altAction: () => { altAction: () => {
@@ -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)
} }
} }
} }
@@ -273,6 +273,20 @@ Item { // Bar content region
Layout.bottomMargin: indicatorsColumnLayout.realSpacing Layout.bottomMargin: indicatorsColumnLayout.realSpacing
color: rightSidebarButton.colText color: rightSidebarButton.colText
} }
Revealer {
vertical: true
reveal: Notifications.silent || Notifications.unread > 0
Layout.fillWidth: true
Layout.bottomMargin: reveal ? indicatorsColumnLayout.realSpacing : 0
implicitHeight: reveal ? notificationUnreadCount.implicitHeight : 0
implicitWidth: reveal ? notificationUnreadCount.implicitWidth : 0
Behavior on Layout.bottomMargin {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
Bar.NotificationUnreadCount {
id: notificationUnreadCount
}
}
MaterialSymbol { MaterialSymbol {
Layout.bottomMargin: indicatorsColumnLayout.realSpacing Layout.bottomMargin: indicatorsColumnLayout.realSpacing
text: Network.materialSymbol text: Network.materialSymbol
@@ -280,6 +294,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
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
if [[ -z "$1" ]]; then
echo "Usage: $0 <image_path>"
exit 1
fi
SOURCE_IMG_PATH="$1"
WALLPAPER_NAME="$(basename "$SOURCE_IMG_PATH")"
RESIZED_IMG_PATH="/tmp/quickshell/ai/wallpaper.jpg"
magick "$SOURCE_IMG_PATH" -resize 200x -quality 50 "$RESIZED_IMG_PATH"
API_KEY=$(secret-tool lookup 'application' 'illogical-impulse' | jq -r '.apiKeys.gemini')
if [[ "$(base64 --version 2>&1)" = *"FreeBSD"* ]]; then
B64FLAGS="--input"
else
B64FLAGS="-w0"
fi
payload='{
"contents": [{
"parts":[
{
"inline_data": {
"mime_type":"image/jpeg",
"data": "'"$(base64 $B64FLAGS $RESIZED_IMG_PATH)"'"
}
},
{"text": "Categorize the wallpaper. Its file name is '"$WALLPAPER_NAME"'"}
]
}],
"generationConfig": {
"responseMimeType": "text/x.enum",
"responseSchema": {
"type": "string",
"enum": [ "abstract", "anime", "city", "minimalist", "landscape", "plants", "person", "space" ]
},
"temperature": 0,
}
}'
# echo "$payload" | jq
response=$(curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent" \
-H "x-goog-api-key: $API_KEY" \
-H 'Content-Type: application/json' \
-X POST \
-d "$payload" 2> /dev/null)
echo "$response" | jq -r '.candidates[0].content.parts[0].text'
@@ -171,6 +171,13 @@ switch() {
type_flag="$3" type_flag="$3"
color_flag="$4" color_flag="$4"
color="$5" color="$5"
# Start Gemini auto-categorization if enabled
aiStylingEnabled=$(jq -r '.background.clock.cookie.aiStyling' "$SHELL_CONFIG_FILE")
if [[ "$aiStylingEnabled" == "true" ]]; then
"$SCRIPT_DIR/../ai/gemini-categorize-wallpaper.sh" "$imgpath" > "$STATE_DIR/user/generated/wallpaper/category.txt" &
fi
read scale screenx screeny screensizey < <(hyprctl monitors -j | jq '.[] | select(.focused) | .scale, .x, .y, .height' | xargs) read scale screenx screeny screensizey < <(hyprctl monitors -j | jq '.[] | select(.focused) | .scale, .x, .y, .height' | xargs)
cursorposx=$(hyprctl cursorpos -j | jq '.x' 2>/dev/null) || cursorposx=960 cursorposx=$(hyprctl cursorpos -j | jq '.x' 2>/dev/null) || cursorposx=960
cursorposx=$(bc <<< "scale=0; ($cursorposx - $screenx) * $scale / 1") cursorposx=$(bc <<< "scale=0; ($cursorposx - $screenx) * $scale / 1")
@@ -294,7 +294,8 @@ def get_dominant_color(image_path, x, y, w, h, screen_width=None, screen_height=
_, labels, centers = cv2.kmeans(region, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS) _, labels, centers = cv2.kmeans(region, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
counts = np.bincount(labels.flatten()) counts = np.bincount(labels.flatten())
dominant = centers[np.argmax(counts)] dominant = centers[np.argmax(counts)]
return [int(x) for x in dominant] # Reverse from BGR to RGB
return [int(x) for x in reversed(dominant)]
def main(): def main():
parser = argparse.ArgumentParser(description="Find least busy region in an image and output a JSON. Made for determining a suitable position for a wallpaper widget.") parser = argparse.ArgumentParser(description="Find least busy region in an image and output a JSON. Made for determining a suitable position for a wallpaper widget.")
@@ -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
@@ -21,6 +21,9 @@ Singleton {
entry: a entry: a
})) }))
function fuzzyQuery(search: string): var { function fuzzyQuery(search: string): var {
if (search.trim() === "") {
return entries;
}
if (root.sloppySearch) { if (root.sloppySearch) {
const results = entries.slice(0, 100).map(str => ({ const results = entries.slice(0, 100).map(str => ({
entry: str, entry: str,
@@ -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
+45 -13
View File
@@ -16,24 +16,35 @@ Singleton {
property bool wifi: true property bool wifi: true
property bool ethernet: false property bool ethernet: false
property bool wifiEnabled: false property bool wifiEnabled: false
property bool wifiScanning: false property bool wifiScanning: false
property bool wifiConnecting: connectProc.running property bool wifiConnecting: connectProc.running
property WifiAccessPoint wifiConnectTarget property WifiAccessPoint wifiConnectTarget
readonly property list<WifiAccessPoint> wifiNetworks: [] readonly property list<WifiAccessPoint> wifiNetworks: []
readonly property WifiAccessPoint active: wifiNetworks.find(n => n.active) ?? null readonly property WifiAccessPoint active: wifiNetworks.find(n => n.active) ?? null
property string wifiStatus: "disconnected"
property string networkName: "" property string networkName: ""
property int networkStrength property int networkStrength
property string materialSymbol: ethernet ? "lan" : property string materialSymbol: root.ethernet
wifiEnabled ? ( ? "lan"
Network.networkStrength > 80 ? "signal_wifi_4_bar" : : root.wifiEnabled
Network.networkStrength > 60 ? "network_wifi_3_bar" : ? (
Network.networkStrength > 40 ? "network_wifi_2_bar" : Network.networkStrength > 83 ? "signal_wifi_4_bar" :
Network.networkStrength > 20 ? "network_wifi_1_bar" : Network.networkStrength > 67 ? "network_wifi" :
"signal_wifi_0_bar" Network.networkStrength > 50 ? "network_wifi_3_bar" :
) : "signal_wifi_off" Network.networkStrength > 33 ? "network_wifi_2_bar" :
Network.networkStrength > 17 ? "network_wifi_1_bar" :
"signal_wifi_0_bar"
)
: (root.wifiStatus === "connecting")
? "signal_wifi_statusbar_not_connected"
: (root.wifiStatus === "disconnected")
? "wifi_find"
: (root.wifiStatus === "disabled")
? "signal_wifi_off"
: "signal_wifi_bad"
// Control // Control
function enableWifi(enabled = true): void { function enableWifi(enabled = true): void {
@@ -153,7 +164,7 @@ Singleton {
Process { Process {
id: updateConnectionType id: updateConnectionType
property string buffer property string buffer
command: ["sh", "-c", "nmcli -t -f NAME,TYPE,DEVICE c show --active"] command: ["sh", "-c", "nmcli -t -f TYPE,STATE d status && nmcli -t -f CONNECTIVITY g"]
running: true running: true
function startCheck() { function startCheck() {
buffer = ""; buffer = "";
@@ -166,14 +177,35 @@ Singleton {
} }
onExited: (exitCode, exitStatus) => { onExited: (exitCode, exitStatus) => {
const lines = updateConnectionType.buffer.trim().split('\n'); const lines = updateConnectionType.buffer.trim().split('\n');
const connectivity = lines.pop() // none, limited, full
let hasEthernet = false; let hasEthernet = false;
let hasWifi = false; let hasWifi = false;
let wifiStatus = "disconnected";
lines.forEach(line => { lines.forEach(line => {
if (line.includes("ethernet")) if (line.includes("ethernet") && line.includes("connected"))
hasEthernet = true; hasEthernet = true;
else if (line.includes("wireless")) else if (line.includes("wifi:")) {
hasWifi = true; if (line.includes("disconnected")) {
wifiStatus = "disconnected"
}
else if (line.includes("connected")) {
hasWifi = true;
wifiStatus = "connected"
if (connectivity === "limited") {
hasWifi = false;
wifiStatus = "limited"
}
}
else if (line.includes("connecting")) {
wifiStatus = "connecting"
}
else if (line.includes("unavailable")) {
wifiStatus = "disabled"
}
}
}); });
root.wifiStatus = wifiStatus;
root.ethernet = hasEthernet; root.ethernet = hasEthernet;
root.wifi = hasWifi; root.wifi = hasWifi;
} }
@@ -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);
+3 -2
View File
@@ -69,6 +69,7 @@ ApplicationWindow {
Component.onCompleted: { Component.onCompleted: {
MaterialThemeLoader.reapplyTheme() MaterialThemeLoader.reapplyTheme()
Config.readWriteDelay = 0 // Settings app always only sets one var at a time so delay isn't needed
} }
minimumWidth: 750 minimumWidth: 750
@@ -172,7 +173,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 +191,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
View File
@@ -33,6 +33,7 @@ ApplicationWindow {
Component.onCompleted: { Component.onCompleted: {
MaterialThemeLoader.reapplyTheme(); MaterialThemeLoader.reapplyTheme();
Config.readWriteDelay = 0 // Welcome app always only sets one var at a time so delay isn't needed
} }
minimumWidth: 600 minimumWidth: 600
+129 -35
View File
@@ -38,10 +38,8 @@
"Unfinished": "לא הושלם", "Unfinished": "לא הושלם",
"Add": "הוסף", "Add": "הוסף",
"Nothing here!": "אין תזכורות", "Nothing here!": "אין תזכורות",
"Center title": "כותרת במרכז",
"Elements": "אלמנטים", "Elements": "אלמנטים",
"Color picker": "בוחר צבעים", "Color picker": "בוחר צבעים",
"Title bar": "שורת כותרת",
"Sleep": "שינה", "Sleep": "שינה",
"Transparency": "שקיפות", "Transparency": "שקיפות",
"Bluetooth": "בלוטות׳", "Bluetooth": "בלוטות׳",
@@ -61,19 +59,15 @@
"Wind": "רוח", "Wind": "רוח",
"Humidity": "לחות", "Humidity": "לחות",
"Select Language": "בחר שפה", "Select Language": "בחר שפה",
"Wallpaper": "טפט",
"Copy code": "העתק קוד", "Copy code": "העתק קוד",
"Allow NSFW": "הצג NSFW", "Allow NSFW": "הצג NSFW",
"Colors & Wallpaper": "צבעים וטפט",
"Shutdown": "כיבוי", "Shutdown": "כיבוי",
"Decorations & Effects": "קישוטים ואפקטים",
"Translation goes here...": "תרגום יגיע לכאן...", "Translation goes here...": "תרגום יגיע לכאן...",
"Polling interval (ms)": "מרווח סקר (מילישניות)", "Polling interval (ms)": "מרווח סקר (מילישניות)",
"System prompt": "פרומפט מערכת", "System prompt": "פרומפט מערכת",
"Base URL": "כתובת בסיס", "Base URL": "כתובת בסיס",
"Always show numbers": "הצג מספרים תמיד", "Always show numbers": "הצג מספרים תמיד",
"Wallpaper parallax": "אפקט parallax לטפט", "Wallpaper parallax": "אפקט parallax לטפט",
"Plain rectangle": "מלבן פשוט",
"illogical-impulse Welcome": "ברוך הבא ל-illogical-impulse", "illogical-impulse Welcome": "ברוך הבא ל-illogical-impulse",
"Local only": "מקומי בלבד", "Local only": "מקומי בלבד",
"Dark/Light toggle": "מצב כהה/בהיר", "Dark/Light toggle": "מצב כהה/בהיר",
@@ -82,10 +76,8 @@
"Show app icons": "הצג אייקוני אפליקציות", "Show app icons": "הצג אייקוני אפליקציות",
"Useless buttons": "כפתורים מיותרים", "Useless buttons": "כפתורים מיותרים",
"Scale (%)": "קנה מידה (%)", "Scale (%)": "קנה מידה (%)",
"Show background": "הצג רקע",
"Intelligence": "בינה מלאכותית", "Intelligence": "בינה מלאכותית",
"Enable": "אפשר", "Enable": "אפשר",
"Borderless": "ללא גבולות",
"Random: Konachan": "אקראי: Konachan", "Random: Konachan": "אקראי: Konachan",
"Yes": "כן", "Yes": "כן",
"Documentation": "תיעוד", "Documentation": "תיעוד",
@@ -94,7 +86,6 @@
"Report a Bug": "דווח על באג", "Report a Bug": "דווח על באג",
"Sunset": "שקיעה", "Sunset": "שקיעה",
"Weeb": "אנימה", "Weeb": "אנימה",
"Shell windows": "חלונות",
"Workspaces shown": "מספר שטחי עבודה מוצגים", "Workspaces shown": "מספר שטחי עבודה מוצגים",
"Screenshot tool": "כלי צילום מסך", "Screenshot tool": "כלי צילום מסך",
"Enter text to translate...": "הכנס טקסט לתרגום", "Enter text to translate...": "הכנס טקסט לתרגום",
@@ -103,7 +94,6 @@
"Scroll to change brightness": "גלול לשינוי בהירות", "Scroll to change brightness": "גלול לשינוי בהירות",
"Privacy Policy": "מדיניות פרטיות", "Privacy Policy": "מדיניות פרטיות",
"12h AM/PM": "12 שעות (AM/PM)", "12h AM/PM": "12 שעות (AM/PM)",
"Material palette": "פלטת צבעים",
"No audio source": "אין מקור שמע", "No audio source": "אין מקור שמע",
"Download": "הורד", "Download": "הורד",
"Prefixes": "קידומות", "Prefixes": "קידומות",
@@ -140,14 +130,11 @@
"Donate": "תרום", "Donate": "תרום",
"Resources": "משאבים", "Resources": "משאבים",
"Float": "צף", "Float": "צף",
"Fake screen rounding": "עיגול מסך מזויף",
"Hibernate": "שינה עמוקה", "Hibernate": "שינה עמוקה",
"Visibility": "ראות", "Visibility": "ראות",
"Delete": "מחק", "Delete": "מחק",
"Style": "סגנון",
"Page %1": "עמוד %1", "Page %1": "עמוד %1",
"Keyboard toggle": "מקלדת וירטואלית", "Keyboard toggle": "מקלדת וירטואלית",
"Buttons": "כפתורים",
"24h": "24 שעות", "24h": "24 שעות",
"Time": "שעה", "Time": "שעה",
"Clipboard": "לוח העתקה", "Clipboard": "לוח העתקה",
@@ -165,7 +152,6 @@
"Not visible to model": "לא נראה למודל", "Not visible to model": "לא נראה למודל",
"Local Ollama model | %1": "מודל Ollama מקומי | %1", "Local Ollama model | %1": "מודל Ollama מקומי | %1",
"Open file link": "פתח קישור לקובץ", "Open file link": "פתח קישור לקובץ",
"Waiting for response...": "ממתין לתשובה...",
"Cheat sheet": "דף עזר", "Cheat sheet": "דף עזר",
"Allow NSFW content": "אפשר תוכן NSFW", "Allow NSFW content": "אפשר תוכן NSFW",
"%1 characters": "%1 תווים", "%1 characters": "%1 תווים",
@@ -180,8 +166,6 @@
"Color generation": "יצירת צבעים", "Color generation": "יצירת צבעים",
"Temperature must be between 0 and 2": "ערך הטמפרטורה חייב להיות בין 0 ל-2", "Temperature must be between 0 and 2": "ערך הטמפרטורה חייב להיות בין 0 ל-2",
"Earbang protection": "הגנת אוזניים", "Earbang protection": "הגנת אוזניים",
"Alternatively use /dark, /light, /img in the launcher": "באופן חלופי השתמש /dark, /light, /img במנוע",
"Change any time later with /dark, /light, /img in the launcher": "שנה בכל זמן עם /dark, /light, /img במנוע",
"Current model: %1\nSet it with %2model MODEL": "מודל נוכחי: %1\nשנה עם %2model", "Current model: %1\nSet it with %2model MODEL": "מודל נוכחי: %1\nשנה עם %2model",
"Waifus only | Excellent quality, limited quantity": "וואייפוס בלבד | איכות מעולה, כמות מוגבלת", "Waifus only | Excellent quality, limited quantity": "וואייפוס בלבד | איכות מעולה, כמות מוגבלת",
"Save to Downloads": "שמור בהורדות", "Save to Downloads": "שמור בהורדות",
@@ -195,7 +179,6 @@
"Prevents abrupt increments and restricts volume limit": "מונע עליות חדות ומגביל עוצמת קול", "Prevents abrupt increments and restricts volume limit": "מונע עליות חדות ומגביל עוצמת קול",
"Preferred wallpaper zoom (%)": "זום טפט מועדף (%)", "Preferred wallpaper zoom (%)": "זום טפט מועדף (%)",
"Depends on workspace": "תלוי במקום העבודה", "Depends on workspace": "תלוי במקום העבודה",
"Note: turning off can hurt readability": "הערה: כיבוי עלול לפגוע בקריאות",
"Pick wallpaper image on your system": "בחר טפט מהמחשב", "Pick wallpaper image on your system": "בחר טפט מהמחשב",
"Invalid API provider. Supported: \n-": "ספק API לא חוקי. נתמכים: \n-", "Invalid API provider. Supported: \n-": "ספק API לא חוקי. נתמכים: \n-",
"The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "הנטאי | כמות רבה, NSFW, איכות משתנה", "The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "הנטאי | כמות רבה, NSFW, איכות משתנה",
@@ -218,9 +201,7 @@
"Refreshing (manually triggered)": "מרענן (הופעל ידנית)", "Refreshing (manually triggered)": "מרענן (הופעל ידנית)",
"Kill conflicting programs?": "להפסיק תוכנות מתנגשות?", "Kill conflicting programs?": "להפסיק תוכנות מתנגשות?",
"System uptime:": "משך זמן פעילות המערכת:", "System uptime:": "משך זמן פעילות המערכת:",
"Place at the bottom/right": "הצב בתחתית/ימין",
"Monochrome": "מונוכרום", "Monochrome": "מונוכרום",
"Bar layout": "פריסת סרגל",
"Issues": "בעיות", "Issues": "בעיות",
"Online | Google's model\nNewer model that's slower than its predecessor but should deliver higher quality answers": "מקוון | מודל של גוגל\nמודל חדש יותר, איטי יותר מקודמו אך אמור לספק תשובות איכותיות יותר", "Online | Google's model\nNewer model that's slower than its predecessor but should deliver higher quality answers": "מקוון | מודל של גוגל\nמודל חדש יותר, איטי יותר מקודמו אך אמור לספק תשובות איכותיות יותר",
"Temperature set to %1": "הטמפרטורה הוגדרה ל-%1", "Temperature set to %1": "הטמפרטורה הוגדרה ל-%1",
@@ -230,7 +211,6 @@
"Set the tool to use for the model.": "הגדר את הכלי לשימוש עבור המודל.", "Set the tool to use for the model.": "הגדר את הכלי לשימוש עבור המודל.",
"Cannot find a GPS service. Using the fallback method instead.": "לא נמצא שירות GPS. משתמש בשיטה חלופית.", "Cannot find a GPS service. Using the fallback method instead.": "לא נמצא שירות GPS. משתמש בשיטה חלופית.",
"API key:\n\n```txt\n%1\n```": "מפתח API:\n\n```txt\n%1\n```", "API key:\n\n```txt\n%1\n```": "מפתח API:\n\n```txt\n%1\n```",
"Friday": "יום שישי",
"Large images | God tier quality, no NSFW.": "תמונות גדולות | איכות גבוהה מאוד, ללא NSFW.", "Large images | God tier quality, no NSFW.": "תמונות גדולות | איכות גבוהה מאוד, ללא NSFW.",
"Language": "שפה", "Language": "שפה",
"Weather Service": "שירות מזג אוויר", "Weather Service": "שירות מזג אוויר",
@@ -243,7 +223,6 @@
"The current system prompt is\n\n---\n\n%1": "הפרומפט הנוכחי של המערכת הוא\n\n---\n\n%1", "The current system prompt is\n\n---\n\n%1": "הפרומפט הנוכחי של המערכת הוא\n\n---\n\n%1",
"Save chat": "שמור שיחה", "Save chat": "שמור שיחה",
"Conflicts with the shell's notification implementation": "מתנגש עם מימוש ההתראות של ה-shell", "Conflicts with the shell's notification implementation": "מתנגש עם מימוש ההתראות של ה-shell",
"Saturday": "יום שבת",
"Critically low battery": "סוללה נמוכה מאוד", "Critically low battery": "סוללה נמוכה מאוד",
"Shell conflicts killer": "מנטרל התנגשויות shell", "Shell conflicts killer": "מנטרל התנגשויות shell",
"Number show delay when pressing Super (ms)": "השהיית הצגת מספרים בלחיצת Super (מילישניות)", "Number show delay when pressing Super (ms)": "השהיית הצגת מספרים בלחיצת Super (מילישניות)",
@@ -252,7 +231,6 @@
"☕ Break: %1 minutes": "☕ הפסקה: %1 דקות", "☕ Break: %1 minutes": "☕ הפסקה: %1 דקות",
". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!": ". הערות ל-Zerochan:\n- חובה להזין צבע\n- הגדר את שם המשתמש שלך ב-zerochan באופציית הקונפיג `sidebar.booru.zerochan.username`. ייתכן שתחסם אם לא תעשה זאת!", ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!": ". הערות ל-Zerochan:\n- חובה להזין צבע\n- הגדר את שם המשתמש שלך ב-zerochan באופציית הקונפיג `sidebar.booru.zerochan.username`. ייתכן שתחסם אם לא תעשה זאת!",
"Used:": "בשימוש:", "Used:": "בשימוש:",
"Suspend at": "השהה ב-",
"Hi there! First things first...": "שלום! קודם כל...", "Hi there! First things first...": "שלום! קודם כל...",
"Registration failed. Please inspect manually with the <tt>warp-cli</tt> command": "ההרשמה נכשלה. נא לבדוק ידנית עם הפקודה <tt>warp-cli</tt>", "Registration failed. Please inspect manually with the <tt>warp-cli</tt> command": "ההרשמה נכשלה. נא לבדוק ידנית עם הפקודה <tt>warp-cli</tt>",
"Usage": "שימוש", "Usage": "שימוש",
@@ -300,7 +278,6 @@
"Anime": "אנימה", "Anime": "אנימה",
"Online models disallowed\n\nControlled by `policies.ai` config option": "מודלים מקוונים אינם מותרים\n\nנשלט על ידי אפשרות הקונפיג `policies.ai`", "Online models disallowed\n\nControlled by `policies.ai` config option": "מודלים מקוונים אינם מותרים\n\nנשלט על ידי אפשרות הקונפיג `policies.ai`",
"Thought": "מחשבה", "Thought": "מחשבה",
"Wednesday": "יום רביעי",
"GitHub": "גיטהאב", "GitHub": "גיטהאב",
"Clear chat history": "נקה היסטוריית שיחות", "Clear chat history": "נקה היסטוריית שיחות",
"Resume": "המשך", "Resume": "המשך",
@@ -321,9 +298,7 @@
"Break": "הפסקה", "Break": "הפסקה",
"App": "אפליקציה", "App": "אפליקציה",
"Temperature\nChange with /temp VALUE": "טמפרטורה\nשנה עם /temp VALUE", "Temperature\nChange with /temp VALUE": "טמפרטורה\nשנה עם /temp VALUE",
"Horizontal": "אופקי",
"Unknown command:": "פקודה לא ידועה:", "Unknown command:": "פקודה לא ידועה:",
"Language setting saved. Please restart Quickshell (Ctrl+Super+R) to apply the new language.": "הגדרת השפה נשמרה. נא להפעיל מחדש את Quickshell (Ctrl+Super+R) כדי להחיל את השפה החדשה.",
"There might be a download in progress": "ייתכן שיש הורדה בתהליך", "There might be a download in progress": "ייתכן שיש הורדה בתהליך",
"Go to source (%1)": "עבור למקור (%1)", "Go to source (%1)": "עבור למקור (%1)",
"Networking": "רשת", "Networking": "רשת",
@@ -343,7 +318,6 @@
"Choose model": "בחר מודל", "Choose model": "בחר מודל",
"Large language models": "מודלי שפה גדולים", "Large language models": "מודלי שפה גדולים",
"%1 Safe Storage": "%1 אחסון בטוח", "%1 Safe Storage": "%1 אחסון בטוח",
"Deadline": "דדליין",
"No API key\nSet it with /key YOUR_API_KEY": "אין מפתח API\nהגדר אותו עם /key YOUR_API_KEY", "No API key\nSet it with /key YOUR_API_KEY": "אין מפתח API\nהגדר אותו עם /key YOUR_API_KEY",
"Logout": "התנתק", "Logout": "התנתק",
"Clean stuff | Excellent quality, no NSFW": "תמונות נקיות | איכות מעולה, ללא NSFW", "Clean stuff | Excellent quality, no NSFW": "תמונות נקיות | איכות מעולה, ללא NSFW",
@@ -353,7 +327,6 @@
"Output": "פלט", "Output": "פלט",
"**Instructions**: Log into Mistral account, go to Keys on the sidebar, click Create new key": "**הוראות**: התחבר לחשבון Mistral, עבור ל-Keys בסרגל הצד, לחץ על Create new key", "**Instructions**: Log into Mistral account, go to Keys on the sidebar, click Create new key": "**הוראות**: התחבר לחשבון Mistral, עבור ל-Keys בסרגל הצד, לחץ על Create new key",
"Unknown function call: %1": "קריאת פונקציה לא ידועה: %1", "Unknown function call: %1": "קריאת פונקציה לא ידועה: %1",
"Sunday": "יום ראשון",
"Interface Language": "שפת ממשק", "Interface Language": "שפת ממשק",
"Fully charged": "טעון במלואו", "Fully charged": "טעון במלואו",
"Free:": "פנוי:", "Free:": "פנוי:",
@@ -368,21 +341,15 @@
"Hover to trigger": "עבור עםְ העכבר להפעלה", "Hover to trigger": "עבור עםְ העכבר להפעלה",
"Enjoy! You can reopen the welcome app any time with <tt>Super+Shift+Alt+/</tt>. To open the settings app, hit <tt>Super+I</tt>": "תהנה! תוכל לפתוח מחדש את אפליקציית הברוכים הבאים בכל עת עם <tt>Super+Shift+Alt+/</tt>. כדי לפתוח את אפליקציית ההגדרות, לחץ <tt>Super+I</tt>", "Enjoy! You can reopen the welcome app any time with <tt>Super+Shift+Alt+/</tt>. To open the settings app, hit <tt>Super+I</tt>": "תהנה! תוכל לפתוח מחדש את אפליקציית הברוכים הבאים בכל עת עם <tt>Super+Shift+Alt+/</tt>. כדי לפתוח את אפליקציית ההגדרות, לחץ <tt>Super+I</tt>",
"Timeout (ms)": "פסק זמן (מילישניות)", "Timeout (ms)": "פסק זמן (מילישניות)",
"Monday": "יום שני",
"Tuesday": "יום שלישי",
"Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.": "אזורים כאלה יכולים להיות תמונות או חלקים מהמסך שיש בהם תוכן.\nייתכן שלא תמיד יהיה מדויק. זה נעשה עם אלגוריתם עיבוד תמונה מקומי וללא שימוש בבינה מלאכותית.", "Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.": "אזורים כאלה יכולים להיות תמונות או חלקים מהמסך שיש בהם תוכן.\nייתכן שלא תמיד יהיה מדויק. זה נעשה עם אלגוריתם עיבוד תמונה מקומי וללא שימוש בבינה מלאכותית.",
"Long break": "הפסקה ארוכה", "Long break": "הפסקה ארוכה",
"Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel": "מקשי החצים לניווט, Enter לבחירה\nEsc או לחיצה בכל מקום לביטול", "Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel": "מקשי החצים לניווט, Enter לבחירה\nEsc או לחיצה בכל מקום לביטול",
"Load prompt from %1": "טען פרומפט מ-%1", "Load prompt from %1": "טען פרומפט מ-%1",
"That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "זה לא עבד. טיפים:\n- בדוק את התגיות והגדרות NSFW\n- אם אין לך תגית, הקלד מספר עמוד", "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "זה לא עבד. טיפים:\n- בדוק את התגיות והגדרות NSFW\n- אם אין לך תגית, הקלד מספר עמוד",
"Tip: Hide icons and always show numbers for\nthe classic illogical-impulse experience": "טיפ: הסתר אייקונים והצג תמיד מספרים לחוויית illogical-impulse הקלאסית",
"Corner open": "פינה פתוחה", "Corner open": "פינה פתוחה",
"Select Deadline": "בחר דדליין",
"Overall appearance": "מראה כללי",
"Disable tools": "השבת כלים", "Disable tools": "השבת כלים",
"Reject": "דחה", "Reject": "דחה",
"Visualize region": "הדמיית אזור", "Visualize region": "הדמיית אזור",
"Thursday": "יום חמישי",
"**Pricing**: Free tier available with limited rates. See https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**Instructions**: Generate a GitHub personal access token with Models permission, then set as API key here\n\n**Note**: To use this you will have to set the temperature parameter to 1": "**מחיר**: יש פרופיל חינם עם תקצב מוגבל. ראה https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**הוראות**: צור אסימון גישה אישי ב-GitHub עם הרשאת Models, ואז הגדר אותו כאן כמפתח API\n\n**הערה**: כדי להשתמש בזה תצטרך להגדיר את פרמטר הטמפרטורה ל-1", "**Pricing**: Free tier available with limited rates. See https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**Instructions**: Generate a GitHub personal access token with Models permission, then set as API key here\n\n**Note**: To use this you will have to set the temperature parameter to 1": "**מחיר**: יש פרופיל חינם עם תקצב מוגבל. ראה https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**הוראות**: צור אסימון גישה אישי ב-GitHub עם הרשאת Models, ואז הגדר אותו כאן כמפתח API\n\n**הערה**: כדי להשתמש בזה תצטרך להגדיר את פרמטר הטמפרטורה ל-1",
"Select the language for the user interface.\n\"Auto\" will use your system's locale.": "בחר את השפה לממשק המשתמש. \"אוטומטי\" ישתמש בשפת המערכת שלך.", "Select the language for the user interface.\n\"Auto\" will use your system's locale.": "בחר את השפה לממשק המשתמש. \"אוטומטי\" ישתמש בשפת המערכת שלך.",
"Automatically suspends the system when battery is low": "משהה אוטומטית את המערכת כאשר הסוללה נמוכה", "Automatically suspends the system when battery is low": "משהה אוטומטית את המערכת כאשר הסוללה נמוכה",
@@ -392,7 +359,6 @@
"Fidelity": "דיוק", "Fidelity": "דיוק",
"Load chat": "טען שיחה", "Load chat": "טען שיחה",
"All-rounder | Good quality, decent quantity": "כללי | איכות טובה, כמות אדירה", "All-rounder | Good quality, decent quantity": "כללי | איכות טובה, כמות אדירה",
"TimeTable": "לוח זמנים",
"Info": "מידע", "Info": "מידע",
"Corner style": "סגנון פינות", "Corner style": "סגנון פינות",
"Online | Google's model\nFast, can perform searches for up-to-date information": "מקוון | מודל של גוגל\nמהיר, יכול לבצע חיפושים למידע עדכני", "Online | Google's model\nFast, can perform searches for up-to-date information": "מקוון | מודל של גוגל\nמהיר, יכול לבצע חיפושים למידע עדכני",
@@ -407,5 +373,133 @@
"🔴 Focus: %1 minutes": "🔴 פוקוס: %1 דקות", "🔴 Focus: %1 minutes": "🔴 פוקוס: %1 דקות",
"Set API key": "הגדר מפתח API", "Set API key": "הגדר מפתח API",
"Load chat from %1": "טען שיחה מ-%1", "Load chat from %1": "טען שיחה מ-%1",
"Code saved to file": "הקוד נשמר לקובץ" "Code saved to file": "הקוד נשמר לקובץ",
"Show clock": "הצגת שעון",
"Use the system file picker instead\nRight-click to make this the default behavior": "השתמש בבוחר הקבצים של המערכת במקום\nלחיצה ימנית תהפוך את התנהגות זו לברירת המחדל",
"Hour marks": "סימוני שעה",
"Unread indicator: show count": "מתריע על אי-קריאה: הצגת כמות",
"Cookie clock settings": "הגדרות שעון עוגיה",
"Not all options are available in this app. You should also check the config file by hitting the \"Config file\" button on the topleft corner or opening %1 manually.": "לא כל האפשרויות נמצאות באפליקציה הזו. מומלץ לבדוק את קובץ ההגדרות על ידי לחיצה על הכפתור \"קובץ הגדרות\" הממוקם למעלה בפינה השמאלית. ניתן גם לפתוח את %1 באופן ידני.",
"Border": "גבול",
"Classic": "קלאסי",
"Connected": "מחובר",
"Hit \"/\" to search": "לחץ \"/\" לחיפוש",
"Wallpaper & Colors": "רקע וצבעים",
"Change any time later with /dark, /light, /wallpaper in the launcher\nIf the shell's colors aren't changing:\n 1. Open the right sidebar with Super+N\n 2. Click \"Reload Hyprland & Quickshell\" in the top-right corner": "ניתן לשנות בכל עת עם /dark, /light, /wallpaper במפעיל\nאם צבעי ה-shell לא משתנים:\n 1. פתח את הסרגל הצדדי הימני עם Super+N\n 2. לחץ על \"טען מחדש Hyprland & Quickshell\" בפינה הימנית העליונה",
"Launch on startup": "הפעל בעת הפעלה",
"Terminal: Harmonize threshold": "טרמינל: Harmonize threshold",
"Quick": "מהיר",
"Pills": "גלולות",
"Terminal: Harmony (%)": "טרמינל: הרמוניה (%)",
"Bar position": "מיקום הסרגל",
"Connect to Wi-Fi": "התחבר ל-WiFi",
"Force dark mode in terminal": "כפה על מצב כהה בטרמינל",
"Wallpaper safety enforced": "בטיחות הרקע נאכפה",
"Click to toggle light/dark mode\n(applied when wallpaper is chosen)": "לחץ לשינוי בין מצב בהיר/כהה\n(מיושם לאחר בחירת רקע)",
"Dots": "Dots",
"Minute hand": "מחוג הדקות",
"Open editor": "פתח עורך",
"This is usually safe and needed for your browser and AI sidebar anyway\nMostly useful for those who use lock on startup instead of a display manager that does it (GDM, SDDM, etc.)": "זה בדרך כלל בטוח ונדרש עבור הדפדפן שלך וסרגל הצד של הבינה המלאכותית.\nבעיקר שימושי עבור מי שמשתמש בנעילה בעת ההפעלה במקום מנהל תצוגה שעושה זאת (GDM, SDDM וכו').",
"Security": "בטיחות",
"Work safety": "בטיחות עבודה",
"Bar & screen": "סרגל ומסך",
"Crosshair overlay": "שכבת כוונת",
"Make sure your player has MPRIS support\nor try turning off duplicate player filtering": "וודא שהנגן שלך תומך ב-MPRIS\nאו נסה לכבות סינון נגנים כפולים",
"Bottom": "מטה",
"Press Super+G to toggle appearance": "לחץ סופר+G לתצוגה או הסתרה",
"Thin": "דק",
"Sides": "צדדים",
"Hollow": "חלול",
"Usage: <tt>%1superpaste NUM_OF_ENTRIES[i]</tt>\nSupply <tt>i</tt> when you want images\nExamples:\n<tt>%1superpaste 4i</tt> for the last 4 images\n<tt>%1superpaste 7</tt> for the last 7 entries": "שימוש: <tt>%1superpaste NUM_OF_ENTRIES[i]</tt>\nהוסף <tt>i</tt> כאשר תרצה תמונות\nדוגמאות:\n<tt>%1superpaste 4i</tt> עבור 4 התמונות האחרונות\n<tt>%1superpaste 7</tt> עבור 7 הערכים האחרונים",
"Center clock": "מרכוז השעון",
"Tip: Close a window with Super+Q": "טיפ: סגור חלון עם Super+Q",
"Clock style": "סגנון השעון",
"Right": "ימינה",
"Exceeded max allowed": "חריגה מהמקסימום המותר",
"Bold": "מודגש",
"Group style": "סגנון קבוצה",
"Numbers": "מספרים",
"Disconnect": "התנתקות",
"Ignored if terminal theming is not enabled": "מתעלם אם ערכת נושא לטרמינל אינה מופעלת",
"Dial style": "סגנון חוגה",
"Auto styling with Gemini": "עיצוב אוטומטי עם Gemini",
"Screen round corner": "עיגול פינות מסך",
"When this is off you'll have to click": "כאשר אפשרות זו כבויה, תצטרך ללחוץ",
"Paired": "מחובר",
"Details": "פרטים",
"Crosshair code (in Valorant's format)": "קוד כוונת (בפורמט של Valorant)",
"Positioning": "מיקום",
"Also unlock keyring": "פתח גם את שרשרת המפתחות (keyring)",
"Make icons pinned by default": "הצמד אייקונים כברירת מחדל",
"Background": "רקע",
"Random osu! seasonal background\nImage is saved to ~/Pictures/Wallpapers": "רקע osu! עונתי רנדומלי\nהתמונה נשמרת ב- ~/Pictures/Wallpapers",
"Cancel wallpaper selection": "בטל בחירת רקע",
"Enable if you want clocks to show seconds accurately": "הפעל כדי שהשעון יראה שניות במדויק",
"at": "ב",
"Quote settings": "הגדרות ציטוט",
"Timeout duration (if not defined by notification) (ms)": "משך זמן (אם לא מוגדר על ידי ההתראה) (באלפיות השנייה)",
"Connect": "התחבר",
"Digits in the middle": "ספרות באמצע",
"Rect": "מלבן",
"Unknown device": "מכשיר לא ידוע",
"Back": "אחורה",
"General": "כללי",
"Bluetooth devices": "מכשירי Bluetooth",
"Bubble": "בועה",
"Hour hand": "מחוג השעות",
"Enable translator": "הפעלת מתרגם",
"Utility buttons": "כפתורי תועלת",
"Value scroll": "ערך גלילה",
"Remember that on most devices one can always hold the power button to force shutdown\nThis only makes it a tiny bit harder for accidents to happen": "זכור כי ברוב המכשירים תמיד ניתן להחזיק את כפתור ההפעלה כדי לכבות בכוח\nזה רק מקשה מעט על טעויות מקריות לקרות",
"Material cookie": "עוגיה",
"Locked": "נעול",
"Left": "שמאלה",
"Place at bottom": "מקם למטה",
"Lock screen": "מסך נעילה",
"Show \"Locked\" text": "הצג את הטקסט \"נעול\"",
"Open network portal": "פתח את פורטל הרשת",
"No active player": "אין נגן פעיל",
"Simple digital": "דיגיטלי פשוט",
"Tip: right-clicking a group\nalso expands it": "טיפ: לחיצה ימנית על קבוצה\nמרחיבה אותה גם כן",
"Pick random from this folder": "בחר באקראיות מהתיקייה",
"Illegal increment": "הוספה לא חוקית",
"Style: Blurred": "סגנון: חיבור",
"Full": "מלא",
"Style: general": "סגנון: כללי",
"Use Hyprlock (instead of Quickshell)": "השתמש ב-Hyprlock (במקום ב-Quickshell)",
"Top": "מעלה",
"Random: osu! seasonal": "רנדומלי: osu! עונתי",
"Require password to power off/restart": "דרוש סיסמה לכיבוי/הפעלה מחדש",
"Fill": "מלא",
"Constantly rotate": "הסתובב לעד",
"Forget": "שכח",
"Pick a wallpaper": "בחירת רקע",
"Show quote": "הצג ציטוט",
"Line-separated": "הפרדת קו",
"Shell command": "פקודת של (shell)",
"Extra wallpaper zoom (%)": "הגדלת רקע (%)",
"Search wallpapers": "חיפוש רקעים",
"Bar style": "סגנון הסרגל",
"Dot": "נקודה",
"Second precision": "דיוק בשנייה",
"Line": "קו",
"Place the corners to trigger at the bottom": "מקם את הפינות להפעלה בתחתית",
"Second hand": "מחוג השניות",
"Math": "מתמטיקה",
"Edit directory": "ערוך תיקייה",
"Password": "סיסמה",
"Terminal: Foreground boost (%)": "Terminal: Foreground boost (%)",
"If you want to somehow use fingerprint unlock...": "אם תרצה איכשהו לפתוח בעזרת טביעת אצבע...",
"Superpaste": "Superpaste",
"Date style": "סגנון תאריך",
"Enable blur": "הפעל טשטוש",
"Brightness and volume": "בהירות ועוצמת שמע",
"Quote": "ציטוט",
"Mo": "Mo/*keep*/",
"Su": "Su/*keep*/",
"Sa": "Sa/*keep*/",
"Fr": "Fr/*keep*/",
"Tu": "Tu/*keep*/",
"Th": "Th/*keep*/",
"We": "We/*keep*/"
} }
+4
View File
@@ -6,6 +6,10 @@
# Code details # Code details
## Contributing to i18n
For contributing in translation (i18n) for Quickshell, see also `.config/quickshell/translations/tools`.
## Dynamic loading ## Dynamic loading
- If something's not always necessary, especially when guarded by a config option to enable/disable, put it in a `Loader`. One tip with `Loader`s is sometimes you will need to declare positioning properties (like `anchors`) in the `Loader`, not the `sourceComponent`. - If something's not always necessary, especially when guarded by a config option to enable/disable, put it in a `Loader`. One tip with `Loader`s is sometimes you will need to declare positioning properties (like `anchors`) in the `Loader`, not the `sourceComponent`.
+3 -2
View File
@@ -28,8 +28,9 @@
<details> <details>
<summary>Installation (illogical-impulse Quickshell)</summary> <summary>Installation (illogical-impulse Quickshell)</summary>
- See the [Wiki](https://ii.clsty.link/en/ii-qs/01setup/) <sub>(idempotent script provided)</sub> - Just run `bash <(curl -s https://ii.clsty.link/setup)`
- In case you can't access the wiki, you can always manually clone this repo and run `install.sh` - Or, clone this repo and run `./install.sh`
- See [document](https://ii.clsty.link/en/ii-qs/01setup/) for details.
- **Default keybinds**: Should be somewhat familiar to Windows or GNOME users. Important ones: - **Default keybinds**: Should be somewhat familiar to Windows or GNOME users. Important ones:
- `Super`+`/` = keybind list - `Super`+`/` = keybind list
- `Super`+`Enter` = terminal - `Super`+`Enter` = terminal
+5
View File
@@ -1,7 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# #
# This script is for quickly generate helpful info # This script is for quickly generate helpful info
#
# It should be as independent as possible and should not source other files unless it has to # It should be as independent as possible and should not source other files unless it has to
#
# TODO: Add quickshell and Qt version check.
# Pay attention to the version of Qt which quickshell built against.
STY_RED='\e[31m' STY_RED='\e[31m'
STY_RESET='\e[00m' STY_RESET='\e[00m'
+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
+5 -1
View File
@@ -27,5 +27,9 @@ fi
##################################################################################### #####################################################################################
if [[ "${SKIP_ALLFILES}" != true ]]; then if [[ "${SKIP_ALLFILES}" != true ]]; then
printf "${STY_CYAN}[$0]: 3. Copying config files\n${STY_RESET}" printf "${STY_CYAN}[$0]: 3. Copying config files\n${STY_RESET}"
source ./scriptdata/step/3.install-files.sh if [[ "${EXPERIMENTAL_FILES_SCRIPT}" != true ]]; then
source ./scriptdata/step/3.install-files.sh
else
source ./scriptdata/step/3.install-files.experimental.sh
fi
fi fi
+3 -1
View File
@@ -22,6 +22,7 @@ If no option is specified, run default install process.
--skip-plasmaintg Skip installing plasma-browser-integration --skip-plasmaintg Skip installing plasma-browser-integration
--skip-miscconf Skip copying the dirs and files to \".configs\" except for --skip-miscconf Skip copying the dirs and files to \".configs\" except for
AGS, Fish and Hyprland AGS, Fish and Hyprland
--exp-files Use experimental script for the third step copying files
--fontset <set> (Unavailable yet) Use a set of pre-defined font and config --fontset <set> (Unavailable yet) Use a set of pre-defined font and config
--via-nix (Unavailable yet) Use Nix to install dependencies --via-nix (Unavailable yet) Use Nix to install dependencies
" "
@@ -34,7 +35,7 @@ cleancache(){
# `man getopt` to see more # `man getopt` to see more
para=$(getopt \ para=$(getopt \
-o hfk:cs \ -o hfk:cs \
-l help,force,fontset:,clean,skip-allgreeting,skip-alldeps,skip-allsetups,skip-allfiles,skip-sysupdate,skip-fish,skip-hyprland,skip-plasmaintg,skip-miscconf,via-nix \ -l help,force,fontset:,clean,skip-allgreeting,skip-alldeps,skip-allsetups,skip-allfiles,skip-sysupdate,skip-fish,skip-hyprland,skip-plasmaintg,skip-miscconf,exp-files,via-nix \
-n "$0" -- "$@") -n "$0" -- "$@")
[ $? != 0 ] && echo "$0: Error when getopt, please recheck parameters." && exit 1 [ $? != 0 ] && echo "$0: Error when getopt, please recheck parameters." && exit 1
##################################################################################### #####################################################################################
@@ -68,6 +69,7 @@ while true ; do
--skip-fish) SKIP_FISH=true;shift;; --skip-fish) SKIP_FISH=true;shift;;
--skip-miscconf) SKIP_MISCCONF=true;shift;; --skip-miscconf) SKIP_MISCCONF=true;shift;;
--skip-plasmaintg) SKIP_PLASMAINTG=true;shift;; --skip-plasmaintg) SKIP_PLASMAINTG=true;shift;;
--exp-files) EXPERIMENTAL_FILES_SCRIPT=true;shift;;
--via-nix) INSTALL_VIA_NIX=true;shift;; --via-nix) INSTALL_VIA_NIX=true;shift;;
## Ones with parameter ## Ones with parameter