Merge with noctalia main

This commit is contained in:
atheeq-rhxn
2025-11-14 14:14:21 +05:30
47 changed files with 882 additions and 342 deletions
+14 -4
View File
@@ -652,7 +652,8 @@
"logout": "Abmelden",
"reboot": "Neu starten",
"shutdown": "Herunterfahren",
"suspend": "Ruhezustand",
"suspend": "Energiesparmodus",
"hibernate": "Ruhezustand",
"title": "Sitzungsmenü"
},
"settings": {
@@ -879,7 +880,7 @@
"programs": {
"code": {
"description": "Schreibe {Dateipfad}. Das Hyprluna-Theme muss manuell installiert und aktiviert werden",
"description-missing": "Benötigt die Installation von {app}"
"description-missing": "Kein Code-Client erkannt. Installieren Sie VSCode oder VSCodium."
},
"description": "Anwendungsspezifisches Theming.",
"discord": {
@@ -1441,8 +1442,8 @@
"label": "Immer im Vordergrund"
},
"background-opacity": {
"description": "Controls the transparency of the OSD background.",
"label": "Background opacity"
"description": "Kontrolliert die Transparenz des OSD Hintergrunds.",
"label": "Hintergrund-Transparenz"
},
"description": "Bildschirm-Overlays wie Lautstärke- und Helligkeitsanzeigen konfigurieren.",
"duration": {
@@ -1567,6 +1568,15 @@
},
"title": "Sitzungsmenü"
},
"system-monitor": {
"general": {
"section": {
"description": "Konfigurieren Sie das Verhalten und Aussehen des Systemmonitors.",
"label": "Allgemein"
}
},
"title": "Systemmonitor"
},
"user-interface": {
"animation-disable": {
"description": "Deaktivieren Sie alle Animationen für eine schnellere und reaktionsfreudigere Erfahrung.",
+11 -1
View File
@@ -653,6 +653,7 @@
"reboot": "Reboot",
"shutdown": "Shutdown",
"suspend": "Suspend",
"hibernate": "Hibernate",
"title": "Session Menu"
},
"settings": {
@@ -879,7 +880,7 @@
"programs": {
"code": {
"description": "Write {filepath}. Hyprluna theme needs to be installed and activated manually.",
"description-missing": "Requires {app} to be installed"
"description-missing": "No Code client detected. Install VSCode or VSCodium."
},
"description": "Application-specific theming.",
"discord": {
@@ -1567,6 +1568,15 @@
},
"title": "Session Menu"
},
"system-monitor": {
"general": {
"section": {
"description": "Configure the System Monitor behavior and appearance.",
"label": "General"
}
},
"title": "System Monitor"
},
"user-interface": {
"animation-disable": {
"description": "Disable all animations for a faster, more responsive experience.",
+11 -1
View File
@@ -653,6 +653,7 @@
"reboot": "Reiniciar",
"shutdown": "Apagar",
"suspend": "Suspender",
"hibernate": "Hibernación",
"title": "Menú de sesión"
},
"settings": {
@@ -879,7 +880,7 @@
"programs": {
"code": {
"description": "Escribe {filepath}. El tema Hyprluna debe ser instalado y activado manualmente.",
"description-missing": "Requiere que {app} esté instalado/a."
"description-missing": "No se detectó cliente de Code. Instala VSCode o VSCodium."
},
"description": "Tematización específica de aplicaciones.",
"discord": {
@@ -1567,6 +1568,15 @@
},
"title": "Menú de sesión"
},
"system-monitor": {
"general": {
"section": {
"description": "Configurar el comportamiento y la apariencia del Monitor del sistema.",
"label": "General"
}
},
"title": "Monitor del sistema"
},
"user-interface": {
"animation-disable": {
"description": "Desactiva todas las animaciones para una experiencia más rápida y con mayor capacidad de respuesta.",
+11 -1
View File
@@ -653,6 +653,7 @@
"reboot": "Redémarrer",
"shutdown": "Éteindre",
"suspend": "Mettre en veille",
"hibernate": "Mise en veille prolongée",
"title": "Menu de session"
},
"settings": {
@@ -879,7 +880,7 @@
"programs": {
"code": {
"description": "Écrire {filepath}. Le thème Hyprluna doit être installé et activé manuellement.",
"description-missing": "Nécessite l'installation de {app}"
"description-missing": "Aucun client Code détecté. Installez VSCode ou VSCodium."
},
"description": "Thématisation spécifique aux applications.",
"discord": {
@@ -1567,6 +1568,15 @@
},
"title": "Menu de session"
},
"system-monitor": {
"general": {
"section": {
"description": "Configurer le comportement et l'apparence du Moniteur Système.",
"label": "Général"
}
},
"title": "Moniteur système"
},
"user-interface": {
"animation-disable": {
"description": "Désactiver toutes les animations pour une expérience plus rapide et plus réactive.",
+11 -1
View File
@@ -653,6 +653,7 @@
"reboot": "Opnieuw opstarten",
"shutdown": "Uitschakelen",
"suspend": "Onderbreken",
"hibernate": "Slaperstand",
"title": "Sessie-menu"
},
"settings": {
@@ -879,7 +880,7 @@
"programs": {
"code": {
"description": "Schrijf {filepath}. Het Hyprluna-thema moet handmatig worden geïnstalleerd en geactiveerd.",
"description-missing": "Vereist dat {app} is geïnstalleerd."
"description-missing": "Geen Code-client gedetecteerd. Installeer VSCode of VSCodium."
},
"description": "Toepassingsspecifieke theming.",
"discord": {
@@ -1567,6 +1568,15 @@
},
"title": "Sessiemenu"
},
"system-monitor": {
"general": {
"section": {
"description": "Configureer het gedrag en de weergave van de Systeemmonitor.",
"label": "Algemeen"
}
},
"title": "Systeemmonitor"
},
"user-interface": {
"animation-disable": {
"description": "Schakel alle animaties uit voor een snellere, responsievere ervaring.",
+11 -1
View File
@@ -653,6 +653,7 @@
"reboot": "Reiniciar",
"shutdown": "Desligar",
"suspend": "Suspender",
"hibernate": "Hibernação",
"title": "Menu da Sessão"
},
"settings": {
@@ -879,7 +880,7 @@
"programs": {
"code": {
"description": "Escreva em {filepath}. O tema Hyprluna precisa ser instalado e ativado manualmente.",
"description-missing": "Requer que o {app} esteja instalado."
"description-missing": "Nenhum cliente Code detectado. Instale VSCode ou VSCodium."
},
"description": "Tematização específica de aplicativos.",
"discord": {
@@ -1567,6 +1568,15 @@
},
"title": "Menu de sessão"
},
"system-monitor": {
"general": {
"section": {
"description": "Configure o comportamento e a aparência do Monitor do Sistema.",
"label": "Geral"
}
},
"title": "Monitor do Sistema"
},
"user-interface": {
"animation-disable": {
"description": "Desative todas as animações para uma experiência mais rápida e responsiva.",
+11 -1
View File
@@ -653,6 +653,7 @@
"reboot": "Перезагрузить",
"shutdown": "Выключить",
"suspend": "Приостановить",
"hibernate": "Гибернация",
"title": "Меню сеанса"
},
"settings": {
@@ -879,7 +880,7 @@
"programs": {
"code": {
"description": "Записать {filepath}. Тему Hyprluna нужно установить и активировать вручную.",
"description-missing": "Требуется установка {app}"
"description-missing": "Клиент Code не обнаружен. Установите VSCode или VSCodium."
},
"description": "Тематика для конкретных приложений.",
"discord": {
@@ -1567,6 +1568,15 @@
},
"title": "Меню сеанса"
},
"system-monitor": {
"general": {
"section": {
"description": "Настроить поведение и внешний вид системного монитора.",
"label": "Общее"
}
},
"title": "Системный монитор"
},
"user-interface": {
"animation-disable": {
"description": "Отключить все анимации для более быстрого и отзывчивого взаимодействия.",
+11 -1
View File
@@ -653,6 +653,7 @@
"reboot": "Yeniden başlat",
"shutdown": "Bilgisayarı kapat",
"suspend": "Askıya al",
"hibernate": "Hazırda Bekletme",
"title": "Oturum Menüsü"
},
"settings": {
@@ -879,7 +880,7 @@
"programs": {
"code": {
"description": "{filepath} dosyasına yaz. Hyprluna temasının kurulu ve manuel olarak etkinleştirilmiş olması gerekir.",
"description-missing": "Kurulum için {app} gereklidir"
"description-missing": "Code istemcisi tespit edilmedi. VSCode veya VSCodium kurun."
},
"description": "Uygulamaya özel temalandırma.",
"discord": {
@@ -1567,6 +1568,15 @@
},
"title": "Oturum Menüsü"
},
"system-monitor": {
"general": {
"section": {
"description": "Sistem İzleyicisinin davranışını ve görünümünü yapılandırın.",
"label": "Genel"
}
},
"title": "Sistem İzleme"
},
"user-interface": {
"animation-disable": {
"description": "Daha hızlı ve daha duyarlı bir deneyim için tüm animasyonları devre dışı bırakın.",
+11 -1
View File
@@ -653,6 +653,7 @@
"reboot": "Перезавантажити",
"shutdown": "Вимкнути",
"suspend": "Призупинити",
"hibernate": "Гібернація",
"title": "Меню сеансу"
},
"settings": {
@@ -879,7 +880,7 @@
"programs": {
"code": {
"description": "Записати {filepath}. Тему Hyprluna потрібно встановити та активувати вручну.",
"description-missing": "Потрібна установка {app}"
"description-missing": "Клієнт Code не виявлено. Встановіть VSCode або VSCodium."
},
"description": "Оформлення окремих програм.",
"discord": {
@@ -1567,6 +1568,15 @@
},
"title": "Меню сеансу"
},
"system-monitor": {
"general": {
"section": {
"description": "Налаштуйте поведінку та зовнішній вигляд Системного монітора.",
"label": "Загальне"
}
},
"title": "Системний монітор"
},
"user-interface": {
"animation-disable": {
"description": "Вимкнути всі анімації для швидшого, більш відгукового досвіду.",
+11 -2
View File
@@ -653,6 +653,7 @@
"reboot": "重启",
"shutdown": "关机",
"suspend": "挂起",
"hibernate": "休眠",
"title": "会话菜单"
},
"settings": {
@@ -879,7 +880,7 @@
"programs": {
"code": {
"description": "写入 {filepath}。Hyprluna 主题需要手动安装和激活。",
"description-missing": "需要安装 {app}"
"description-missing": "未检测到 Code 客户端。请安装 VSCode 或 VSCodium。"
},
"description": "应用程序特定主题。",
"discord": {
@@ -1474,7 +1475,6 @@
"description": "配置屏显菜单(OSD)的可见性与行为。",
"label": "常规"
}
},
"title": "屏显菜单"
},
@@ -1568,6 +1568,15 @@
},
"title": "会话菜单"
},
"system-monitor": {
"general": {
"section": {
"description": "配置系统监视器的行为和外观。",
"label": "通用"
}
},
"title": "系统监视器"
},
"user-interface": {
"animation-disable": {
"description": "禁用所有动画以获得更快、更流畅的体验。",
+4
View File
@@ -219,6 +219,10 @@
"action": "suspend",
"enabled": true
},
{
"action": "hibernate",
"enabled": true
},
{
"action": "reboot",
"enabled": true
+3
View File
@@ -42,6 +42,7 @@ Singleton {
"powersaver": "leaf",
"storage": "database",
"ethernet": "sitemap",
"ethernet-off": "sitemap-off",
"keyboard": "keyboard",
"shutdown": "power",
"lock": "lock",
@@ -49,6 +50,7 @@ Singleton {
"logout": "logout",
"reboot": "refresh",
"suspend": "player-pause",
"hibernate": "zzz",
"nightlight-on": "moon",
"nightlight-off": "moon-off",
"nightlight-forced": "moon-stars",
@@ -123,6 +125,7 @@ Singleton {
"settings-about": "info-square-rounded",
"settings-lock-screen": "lock",
"settings-session-menu": "power",
"settings-system-monitor": "activity",
"bluetooth": "bluetooth",
"bt-device-generic": "bluetooth",
"bt-device-headphones": "headphones",
+8 -1
View File
@@ -352,6 +352,9 @@ Singleton {
}, {
"action": "suspend",
"enabled": true
}, {
"action": "hibernate",
"enabled": true
}, {
"action": "reboot",
"enabled": true
@@ -436,12 +439,16 @@ Singleton {
property bool spicetify: false
property bool enableUserTemplates: false
property bool code_code: false
property bool code_codium: false
property bool discord_vesktop: false // To be deleted soon
property bool discord_webcord: false // To be deleted soon
property bool discord_armcord: false // To be deleted soon
property bool discord_equibop: false // To be deleted soon
property bool discord_lightcord: false // To be deleted soon
property bool discord_dorion: false // To be deleted soon
property bool discord_vencord: false // To be deleted soon
}
// night light
@@ -654,7 +661,7 @@ Singleton {
var anyDiscordEnabled = false
// Check if any Discord client was enabled
const discordClients = ["discord_vesktop", "discord_webcord", "discord_armcord", "discord_equibop", "discord_lightcord", "discord_dorion"]
const discordClients = ["discord_vesktop", "discord_webcord", "discord_armcord", "discord_equibop", "discord_lightcord", "discord_dorion", "discord_vencord"]
for (var i = 0; i < discordClients.length; i++) {
if (adapter.templates[discordClients[i]]) {
+4 -2
View File
@@ -8,13 +8,15 @@ import qs.Widgets
PopupWindow {
id: root
property ShellScreen screen
property var trayItem: null
property var anchorItem: null
property real anchorX
property real anchorY
property bool isSubMenu: false
property bool isHovered: rootMouseArea.containsMouse
property ShellScreen screen
property string widgetSection: ""
property int widgetIndex: -1
@@ -45,7 +47,7 @@ PopupWindow {
implicitWidth: menuWidth
// Use the content height of the Flickable for implicit height
implicitHeight: Math.min(screen.height * 0.9, flickable.contentHeight + (Style.marginS * 2))
implicitHeight: Math.min(screen?.height * 0.9, flickable.contentHeight + (Style.marginS * 2))
visible: false
color: Color.transparent
anchor.item: anchorItem
+1 -1
View File
@@ -71,7 +71,7 @@ NIconButton {
if (customIconPath !== "")
return customIconPath.startsWith("file://") ? customIconPath : "file://" + customIconPath
if (useDistroLogo)
return DistroService.osLogo
return HostService.osLogo
return ""
}
visible: source !== ""
+80 -4
View File
@@ -33,14 +33,21 @@ Item {
}
return {}
}
readonly property bool hideUnoccupied: (widgetSettings.hideUnoccupied !== undefined) ? widgetSettings.hideUnoccupied : false
readonly property string labelMode: (widgetSettings.labelMode !== undefined) ? widgetSettings.labelMode : widgetMetadata.labelMode
readonly property bool hideUnoccupied: (widgetSettings.hideUnoccupied !== undefined) ? widgetSettings.hideUnoccupied : widgetMetadata.hideUnoccupied
readonly property int characterCount: 2
readonly property bool showWorkspaceNumbers: (widgetSettings.showWorkspaceNumbers !== undefined) ? widgetSettings.showWorkspaceNumbers : true
readonly property bool showNumbersOnlyWhenOccupied: (widgetSettings.showNumbersOnlyWhenOccupied !== undefined) ? widgetSettings.showNumbersOnlyWhenOccupied : true
readonly property bool colorizeIcons: (widgetSettings.colorizeIcons !== undefined) ? widgetSettings.colorizeIcons : widgetMetadata.colorizeIcons
property ListModel localWorkspaces: ListModel {}
property real masterProgress: 0.0
property bool effectsActive: false
property color effectColor: Color.mPrimary
// Wheel scroll handling
property int wheelAccumulatedDelta: 0
property bool wheelCooldown: false
function refreshWorkspaces() {
localWorkspaces.clear()
if (!screen)
@@ -80,11 +87,34 @@ Item {
}
}
function getFocusedLocalIndex() {
for (var i = 0; i < localWorkspaces.count; i++) {
if (localWorkspaces.get(i).isFocused === true)
return i
}
return -1
}
function switchByOffset(offset) {
if (localWorkspaces.count === 0)
return
var current = getFocusedLocalIndex()
if (current < 0)
current = 0
var next = (current + offset) % localWorkspaces.count
if (next < 0)
next = localWorkspaces.count - 1
const ws = localWorkspaces.get(next)
if (ws && ws.idx !== undefined)
CompositorService.switchToWorkspace(ws)
}
Component.onCompleted: {
refreshWorkspaces()
}
onScreenChanged: refreshWorkspaces()
onHideUnoccupiedChanged: refreshWorkspaces()
implicitWidth: isVerticalBar ? taskbarGrid.implicitWidth + Style.marginM * 2 : Math.round(taskbarGrid.implicitWidth + Style.marginM * 2)
implicitHeight: isVerticalBar ? Math.round(taskbarGrid.implicitHeight + Style.marginM * 2) : Style.barHeight
@@ -128,6 +158,46 @@ Item {
}
}
// Debounce timer for wheel interactions
Timer {
id: wheelDebounce
interval: 150
repeat: false
onTriggered: {
root.wheelCooldown = false
root.wheelAccumulatedDelta = 0
}
}
// Scroll to switch workspaces
WheelHandler {
id: wheelHandler
target: root
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
onWheel: function (event) {
if (root.wheelCooldown)
return
// Prefer vertical delta, fall back to horizontal if needed
var dy = event.angleDelta.y
var dx = event.angleDelta.x
var useDy = Math.abs(dy) >= Math.abs(dx)
var delta = useDy ? dy : dx
// One notch is typically 120
root.wheelAccumulatedDelta += delta
var step = 120
if (Math.abs(root.wheelAccumulatedDelta) >= step) {
var direction = root.wheelAccumulatedDelta > 0 ? -1 : 1
// For vertical layout, natural mapping: wheel up -> previous, down -> next (already handled by sign)
// For horizontal layout, same mapping using vertical wheel
root.switchByOffset(direction)
root.wheelCooldown = true
wheelDebounce.restart()
root.wheelAccumulatedDelta = 0
event.accepted = true
}
}
}
Component {
id: workspaceRepeaterDelegate
@@ -192,7 +262,7 @@ Item {
smooth: true
asynchronous: true
opacity: model.isFocused ? Style.opacityFull : 0.6
layer.enabled: widgetSettings.colorizeIcons === true
layer.enabled: root.colorizeIcons && !model.isFocused
Behavior on opacity {
NumberAnimation {
@@ -252,7 +322,7 @@ Item {
Item {
id: workspaceNumberContainer
visible: root.showWorkspaceNumbers && (!root.showNumbersOnlyWhenOccupied || container.hasWindows)
visible: root.labelMode !== "none" && root.showWorkspaceNumbers && (!root.showNumbersOnlyWhenOccupied || container.hasWindows)
anchors {
left: parent.left
@@ -318,7 +388,13 @@ Item {
anchors.centerIn: parent
text: workspaceModel.idx.toString()
text: {
if (root.labelMode === "name" && workspaceModel.name && workspaceModel.name.length > 0) {
return workspaceModel.name.substring(0, root.characterCount)
} else {
return workspaceModel.idx.toString()
}
}
family: Settings.data.ui.fontFixed
font {
+2 -2
View File
@@ -396,8 +396,8 @@ Rectangle {
density: Settings.data.bar.density
baseSize: Style.capsuleHeight
applyUiScale: false
colorBg: Settings.data.colorSchemes.darkMode ? (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) : Color.mPrimary
colorFg: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mOnPrimary
colorBg: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent
colorFg: Color.mOnSurface
colorBorder: Color.transparent
colorBorderHover: Color.transparent
icon: {
+2 -2
View File
@@ -41,7 +41,7 @@ Item {
icon: {
try {
if (NetworkService.ethernetConnected) {
return "ethernet"
return NetworkService.internetConnectivity ? "ethernet" : "ethernet-off"
}
let connected = false
let signalStrength = 0
@@ -55,7 +55,7 @@ Item {
return connected ? NetworkService.signalIcon(signalStrength, true) : "wifi-off"
} catch (error) {
Logger.e("Wi-Fi", "Error getting icon:", error)
return "signal_wifi_bad"
return "wifi-off"
}
}
text: {
+48
View File
@@ -1188,6 +1188,54 @@ Loader {
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: Settings.data.general.compactLockScreen ? 36 : 48
radius: Settings.data.general.compactLockScreen ? 18 : 24
color: hibernateButtonArea.containsMouse ? Color.mHover : "transparent"
border.color: Color.mOutline
border.width: 1
RowLayout {
anchors.centerIn: parent
spacing: 6
NIcon {
icon: "hibernate"
pointSize: Settings.data.general.compactLockScreen ? Style.fontSizeM : Style.fontSizeL
color: hibernateButtonArea.containsMouse ? Color.mOnHover : Color.mOnSurfaceVariant
}
NText {
text: I18n.tr("session-menu.hibernate")
pointSize: Settings.data.general.compactLockScreen ? Style.fontSizeS : Style.fontSizeM
color: hibernateButtonArea.containsMouse ? Color.mOnHover : Color.mOnSurfaceVariant
font.weight: Font.Medium
}
}
MouseArea {
id: hibernateButtonArea
anchors.fill: parent
hoverEnabled: true
onClicked: CompositorService.hibernate()
}
Behavior on color {
ColorAnimation {
duration: 200
easing.type: Easing.OutCubic
}
}
Behavior on border.color {
ColorAnimation {
duration: 200
easing.type: Easing.OutCubic
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: Settings.data.general.compactLockScreen ? 36 : 48
@@ -23,6 +23,8 @@ Item {
// Reference to MainScreen (for panel access)
required property var windowRoot
readonly property color panelBackgroundColor: Qt.alpha(Color.mSurface, Settings.data.ui.panelBackgroundOpacity)
anchors.fill: parent
// Wrapper with layer caching for better shadow performance
@@ -69,91 +71,91 @@ Item {
PanelBackground {
panel: root.windowRoot.audioPanelPlaceholder
shapeContainer: backgroundsShape
backgroundColor: Qt.alpha(Color.mSurface, Settings.data.ui.panelBackgroundOpacity)
backgroundColor: panelBackgroundColor
}
// Battery
PanelBackground {
panel: root.windowRoot.batteryPanelPlaceholder
shapeContainer: backgroundsShape
backgroundColor: Qt.alpha(Color.mSurface, Settings.data.ui.panelBackgroundOpacity)
backgroundColor: panelBackgroundColor
}
// Bluetooth
PanelBackground {
panel: root.windowRoot.bluetoothPanelPlaceholder
shapeContainer: backgroundsShape
backgroundColor: Qt.alpha(Color.mSurface, Settings.data.ui.panelBackgroundOpacity)
backgroundColor: panelBackgroundColor
}
// Calendar
PanelBackground {
panel: root.windowRoot.calendarPanelPlaceholder
shapeContainer: backgroundsShape
backgroundColor: Qt.alpha(Color.mSurface, Settings.data.ui.panelBackgroundOpacity)
backgroundColor: panelBackgroundColor
}
// Control Center
PanelBackground {
panel: root.windowRoot.controlCenterPanelPlaceholder
shapeContainer: backgroundsShape
backgroundColor: Qt.alpha(Color.mSurface, Settings.data.ui.panelBackgroundOpacity)
backgroundColor: panelBackgroundColor
}
// Launcher
PanelBackground {
panel: root.windowRoot.launcherPanelPlaceholder
shapeContainer: backgroundsShape
backgroundColor: Qt.alpha(Color.mSurface, Settings.data.ui.panelBackgroundOpacity)
backgroundColor: panelBackgroundColor
}
// Notification History
PanelBackground {
panel: root.windowRoot.notificationHistoryPanelPlaceholder
shapeContainer: backgroundsShape
backgroundColor: Qt.alpha(Color.mSurface, Settings.data.ui.panelBackgroundOpacity)
backgroundColor: panelBackgroundColor
}
// Session Menu
PanelBackground {
panel: root.windowRoot.sessionMenuPanelPlaceholder
shapeContainer: backgroundsShape
backgroundColor: Qt.alpha(Color.mSurface, Settings.data.ui.panelBackgroundOpacity)
backgroundColor: panelBackgroundColor
}
// Settings
PanelBackground {
panel: root.windowRoot.settingsPanelPlaceholder
shapeContainer: backgroundsShape
backgroundColor: Qt.alpha(Color.mSurface, Settings.data.ui.panelBackgroundOpacity)
backgroundColor: panelBackgroundColor
}
// Setup Wizard
PanelBackground {
panel: root.windowRoot.setupWizardPanelPlaceholder
shapeContainer: backgroundsShape
backgroundColor: Qt.alpha(Color.mSurface, Settings.data.ui.panelBackgroundOpacity)
backgroundColor: panelBackgroundColor
}
// TrayDrawer
PanelBackground {
panel: root.windowRoot.trayDrawerPanelPlaceholder
shapeContainer: backgroundsShape
backgroundColor: Qt.alpha(Color.mSurface, Settings.data.ui.panelBackgroundOpacity)
backgroundColor: panelBackgroundColor
}
// Wallpaper
PanelBackground {
panel: root.windowRoot.wallpaperPanelPlaceholder
shapeContainer: backgroundsShape
backgroundColor: Qt.alpha(Color.mSurface, Settings.data.ui.panelBackgroundOpacity)
backgroundColor: panelBackgroundColor
}
// WiFi
PanelBackground {
panel: root.windowRoot.wifiPanelPlaceholder
shapeContainer: backgroundsShape
backgroundColor: Qt.alpha(Color.mSurface, Settings.data.ui.panelBackgroundOpacity)
backgroundColor: panelBackgroundColor
}
}
+1 -1
View File
@@ -67,7 +67,7 @@ PanelWindow {
// Wayland
WlrLayershell.layer: WlrLayer.Top
WlrLayershell.namespace: "noctalia-screen-" + (screen?.name || "unknown")
WlrLayershell.namespace: "noctalia-background-" + (screen?.name || "unknown")
WlrLayershell.exclusionMode: ExclusionMode.Ignore // Don't reserve space - BarExclusionZone handles that
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
+1 -3
View File
@@ -15,8 +15,6 @@ import qs.Services.UI
Item {
id: root
// PUBLIC API - unchanged for backward compatibility
// Screen property provided by MainScreen
property ShellScreen screen: null
@@ -63,7 +61,7 @@ Item {
// Note: SmartPanelWindow directly calls these functions via panelWrapper reference
function onEscapePressed() {}
function onTabPressed() {}
function onShiftTabPressed() {}
function onBackTabPressed() {}
function onUpPressed() {}
function onDownPressed() {}
function onLeftPressed() {}
+5 -6
View File
@@ -191,12 +191,11 @@ PanelWindow {
} else if (event.key === Qt.Key_Right && panelWrapper.onRightPressed) {
panelWrapper.onRightPressed()
event.accepted = true
} else if (event.key === Qt.Key_Tab) {
if (event.modifiers & Qt.ShiftModifier && panelWrapper.onShiftTabPressed) {
panelWrapper.onShiftTabPressed()
} else if (panelWrapper.onTabPressed) {
panelWrapper.onTabPressed()
}
} else if (event.key === Qt.Key_Tab && panelWrapper.onTabPressed) {
panelWrapper.onTabPressed()
event.accepted = true
} else if (event.key === Qt.Key_Backtab && panelWrapper.onBackTabPressed) {
panelWrapper.onBackTabPressed()
event.accepted = true
} else if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && panelWrapper.onReturnPressed) {
panelWrapper.onReturnPressed()
+112 -112
View File
@@ -49,83 +49,83 @@ Variants {
function getIcon() {
if (currentOSDType === "volume") {
if (AudioService.muted) {
return "volume-mute";
return "volume-mute"
}
return (AudioService.volume <= Number.EPSILON) ? "volume-zero" : (AudioService.volume <= 0.5) ? "volume-low" : "volume-high";
return (AudioService.volume <= Number.EPSILON) ? "volume-zero" : (AudioService.volume <= 0.5) ? "volume-low" : "volume-high"
} else if (currentOSDType === "inputVolume") {
if (AudioService.inputMuted) {
return "microphone-off";
return "microphone-off"
}
return "microphone";
return "microphone"
} else if (currentOSDType === "brightness") {
return currentBrightness <= 0.5 ? "brightness-low" : "brightness-high";
return currentBrightness <= 0.5 ? "brightness-low" : "brightness-high"
}
return "";
return ""
}
// Get current value (0-1 range)
function getCurrentValue() {
if (currentOSDType === "volume") {
return isMuted ? 0 : currentVolume;
return isMuted ? 0 : currentVolume
} else if (currentOSDType === "inputVolume") {
return isInputMuted ? 0 : currentInputVolume;
return isInputMuted ? 0 : currentInputVolume
} else if (currentOSDType === "brightness") {
return currentBrightness;
return currentBrightness
}
return 0;
return 0
}
// Get maximum value for current OSD type
function getMaxValue() {
if (currentOSDType === "volume" || currentOSDType === "inputVolume") {
return Settings.data.audio.volumeOverdrive ? 1.5 : 1.0;
return Settings.data.audio.volumeOverdrive ? 1.5 : 1.0
} else if (currentOSDType === "brightness") {
return 1.0;
return 1.0
}
return 1.0;
return 1.0
}
// Get display percentage
function getDisplayPercentage() {
if (currentOSDType === "volume") {
if (isMuted)
return "0%";
const max = getMaxValue();
const pct = Math.round(Math.min(max, currentVolume) * 100);
return pct + "%";
return "0%"
const max = getMaxValue()
const pct = Math.round(Math.min(max, currentVolume) * 100)
return pct + "%"
} else if (currentOSDType === "inputVolume") {
if (isInputMuted)
return "0%";
const max = getMaxValue();
const pct = Math.round(Math.min(max, currentInputVolume) * 100);
return pct + "%";
return "0%"
const max = getMaxValue()
const pct = Math.round(Math.min(max, currentInputVolume) * 100)
return pct + "%"
} else if (currentOSDType === "brightness") {
const pct = Math.round(Math.min(1.0, currentBrightness) * 100);
return pct + "%";
const pct = Math.round(Math.min(1.0, currentBrightness) * 100)
return pct + "%"
}
return "";
return ""
}
// Get progress bar color
function getProgressColor() {
if (currentOSDType === "volume") {
if (isMuted)
return Color.mError;
return Color.mPrimary;
return Color.mError
return Color.mPrimary
} else if (currentOSDType === "inputVolume") {
if (isInputMuted)
return Color.mError;
return Color.mPrimary;
return Color.mError
return Color.mPrimary
}
return Color.mPrimary;
return Color.mPrimary
}
// Get icon color
function getIconColor() {
if ((currentOSDType === "volume" && isMuted) || (currentOSDType === "inputVolume" && isInputMuted)) {
return Color.mError;
return Color.mError
}
return Color.mOnSurface;
return Color.mOnSurface
}
sourceComponent: PanelWindow {
@@ -146,8 +146,8 @@ Variants {
// Ensure an even width to keep the vertical bar perfectly centered
readonly property int barThickness: {
const base = Math.max(8, Math.round(8 * Style.uiScaleRatio));
return (base % 2 === 0) ? base : base + 1;
const base = Math.max(8, Math.round(8 * Style.uiScaleRatio))
return (base % 2 === 0) ? base : base + 1
}
// Anchor selection based on location (window edges)
@@ -159,46 +159,46 @@ Variants {
// Margins depending on bar position and chosen location
margins.top: {
if (!(anchors.top))
return 0;
var base = Style.marginM;
return 0
var base = Style.marginM
if (Settings.data.bar.position === "top") {
var floatExtraV = Settings.data.bar.floating ? Settings.data.bar.marginVertical * Style.marginXL : 0;
return Style.barHeight + base + floatExtraV;
var floatExtraV = Settings.data.bar.floating ? Settings.data.bar.marginVertical * Style.marginXL : 0
return Style.barHeight + base + floatExtraV
}
return base;
return base
}
margins.bottom: {
if (!(anchors.bottom))
return 0;
var base = Style.marginM;
return 0
var base = Style.marginM
if (Settings.data.bar.position === "bottom") {
var floatExtraV = Settings.data.bar.floating ? Settings.data.bar.marginVertical * Style.marginXL : 0;
return Style.barHeight + base + floatExtraV;
var floatExtraV = Settings.data.bar.floating ? Settings.data.bar.marginVertical * Style.marginXL : 0
return Style.barHeight + base + floatExtraV
}
return base;
return base
}
margins.left: {
if (!(anchors.left))
return 0;
var base = Style.marginM;
return 0
var base = Style.marginM
if (Settings.data.bar.position === "left") {
var floatExtraH = Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0;
return Style.barHeight + base + floatExtraH;
var floatExtraH = Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
return Style.barHeight + base + floatExtraH
}
return base;
return base
}
margins.right: {
if (!(anchors.right))
return 0;
var base = Style.marginM;
return 0
var base = Style.marginM
if (Settings.data.bar.position === "right") {
var floatExtraH = Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0;
return Style.barHeight + base + floatExtraH;
var floatExtraH = Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
return Style.barHeight + base + floatExtraH
}
return base;
return base
}
implicitWidth: verticalMode ? vWidth : hWidth
@@ -249,10 +249,10 @@ Variants {
id: visibilityTimer
interval: Style.animationNormal + 50 // Add small buffer
onTriggered: {
osdItem.visible = false;
root.currentOSDType = "";
osdItem.visible = false
root.currentOSDType = ""
// Deactivate the loader when done
root.active = false;
root.active = false
}
}
@@ -265,8 +265,8 @@ Variants {
color: Qt.alpha(Color.mSurface, Settings.data.osd.backgroundOpacity || 1.0)
border.color: Qt.alpha(Color.mOutline, Settings.data.osd.backgroundOpacity || 1.0)
border.width: {
const bw = Math.max(2, Style.borderM);
return (bw % 2 === 0) ? bw : bw + 1;
const bw = Math.max(2, Style.borderM)
return (bw % 2 === 0) ? bw : bw + 1
}
}
@@ -365,9 +365,9 @@ Variants {
ColumnLayout {
// Ensure inner padding respects the rounded corners; avoid clipping the icon/text
property int vMargin: {
const styleMargin = Style.marginL;
const cornerGuard = Math.round(osdItem.radius);
return Math.max(styleMargin, cornerGuard);
const styleMargin = Style.marginL
const cornerGuard = Math.round(osdItem.radius)
return Math.max(styleMargin, cornerGuard)
}
property int vMarginTop: Math.round(Math.max(osdItem.radius, Style.marginS))
property int balanceDelta: Style.marginS
@@ -449,46 +449,46 @@ Variants {
function show() {
// Cancel any pending hide operations
hideTimer.stop();
visibilityTimer.stop();
hideTimer.stop()
visibilityTimer.stop()
// Make visible and animate in
osdItem.visible = true;
osdItem.visible = true
// Use Qt.callLater to ensure the visible change is processed before animation
Qt.callLater(() => {
osdItem.opacity = 1;
osdItem.scale = 1.0;
});
osdItem.opacity = 1
osdItem.scale = 1.0
})
// Start the auto-hide timer
hideTimer.start();
hideTimer.start()
}
function hide() {
hideTimer.stop();
visibilityTimer.stop();
hideTimer.stop()
visibilityTimer.stop()
// Start fade out animation
osdItem.opacity = 0;
osdItem.scale = 0.85; // Less dramatic scale change for smoother effect
osdItem.opacity = 0
osdItem.scale = 0.85 // Less dramatic scale change for smoother effect
// Delay hiding the element until after animation completes
visibilityTimer.start();
visibilityTimer.start()
}
function hideImmediately() {
hideTimer.stop();
visibilityTimer.stop();
osdItem.opacity = 0;
osdItem.scale = 0.85;
osdItem.visible = false;
root.currentOSDType = "";
root.active = false;
hideTimer.stop()
visibilityTimer.stop()
osdItem.opacity = 0
osdItem.scale = 0.85
osdItem.visible = false
root.currentOSDType = ""
root.active = false
}
}
function showOSD() {
osdItem.show();
osdItem.show()
}
}
@@ -498,34 +498,34 @@ Variants {
function onVolumeChanged() {
if (volumeInitialized) {
showOSD("volume");
showOSD("volume")
}
}
function onMutedChanged() {
if (muteInitialized) {
showOSD("volume");
showOSD("volume")
}
}
function onInputVolumeChanged() {
if (!inputAudioInitialized) {
return;
return
}
if (!AudioService.hasInput) {
return;
return
}
showOSD("inputVolume");
showOSD("inputVolume")
}
function onInputMutedChanged() {
if (!inputAudioInitialized) {
return;
return
}
if (!AudioService.hasInput) {
return;
return
}
showOSD("inputVolume");
showOSD("inputVolume")
}
}
@@ -535,11 +535,11 @@ Variants {
interval: 500
running: true
onTriggered: {
volumeInitialized = true;
muteInitialized = true;
inputAudioInitialized = true;
volumeInitialized = true
muteInitialized = true
inputAudioInitialized = true
// Brightness initializes on first change to avoid showing OSD on startup
connectBrightnessMonitors();
connectBrightnessMonitors()
}
}
@@ -548,65 +548,65 @@ Variants {
target: BrightnessService
function onMonitorsChanged() {
connectBrightnessMonitors();
connectBrightnessMonitors()
}
}
function disconnectBrightnessMonitors() {
for (var i = 0; i < BrightnessService.monitors.length; i++) {
let monitor = BrightnessService.monitors[i];
monitor.brightnessUpdated.disconnect(onBrightnessChanged);
let monitor = BrightnessService.monitors[i]
monitor.brightnessUpdated.disconnect(onBrightnessChanged)
}
}
function connectBrightnessMonitors() {
for (var i = 0; i < BrightnessService.monitors.length; i++) {
let monitor = BrightnessService.monitors[i];
let monitor = BrightnessService.monitors[i]
// Disconnect first to avoid duplicate connections
monitor.brightnessUpdated.disconnect(onBrightnessChanged);
monitor.brightnessUpdated.connect(onBrightnessChanged);
monitor.brightnessUpdated.disconnect(onBrightnessChanged)
monitor.brightnessUpdated.connect(onBrightnessChanged)
}
}
function onBrightnessChanged(newBrightness) {
root.lastUpdatedBrightness = newBrightness;
root.lastUpdatedBrightness = newBrightness
if (!brightnessInitialized) {
brightnessInitialized = true;
return;
brightnessInitialized = true
return
}
showOSD("brightness");
showOSD("brightness")
}
function showOSD(type) {
// Update the current OSD type
currentOSDType = type;
currentOSDType = type
// Activate the loader if not already active
if (!root.active) {
root.active = true;
root.active = true
}
// Show the OSD (may need to wait for loader to create the item)
if (root.item) {
root.item.showOSD();
root.item.showOSD()
} else {
// If item not ready yet, wait for it
Qt.callLater(() => {
if (root.item) {
root.item.showOSD();
}
});
if (root.item) {
root.item.showOSD()
}
})
}
}
function hideOSD() {
if (root.item && root.item.osdItem) {
root.item.osdItem.hideImmediately();
root.item.osdItem.hideImmediately()
} else if (root.active) {
// If loader is active but item isn't ready, just deactivate
root.active = false;
root.active = false
}
}
}
@@ -11,7 +11,7 @@ NIconButtonHot {
icon: {
try {
if (NetworkService.ethernetConnected) {
return "ethernet"
return NetworkService.internetConnectivity ? "ethernet" : "ethernet-off"
}
let connected = false
let signalStrength = 0
@@ -25,7 +25,7 @@ NIconButtonHot {
return connected ? NetworkService.signalIcon(signalStrength, true) : "wifi-off"
} catch (error) {
Logger.e("Wi-Fi", "Error getting icon:", error)
return "signal_wifi_bad"
return "wifi-off"
}
}
+15 -2
View File
@@ -50,12 +50,15 @@ SmartPanel {
readonly property int badgeSize: Math.round(Style.baseWidgetSize * 1.6)
readonly property int entryHeight: Math.round(badgeSize + Style.marginM * 2)
// Override keyboard handlers from SmartPanel for navigation
// Override keyboard handlers from SmartPanel for navigation.
// Launcher specific: onTabPressed() and onBackTabPressed() are special here.
// They are not coming from SmartPanelWindow as they are consumed by the search field before reaching the panel.
// They are instead being forwared from the search field NTextInput below.
function onTabPressed() {
selectNextWrapped()
}
function onShiftTabPressed() {
function onBackTabPressed() {
selectPreviousWrapped()
}
@@ -346,6 +349,16 @@ SmartPanel {
Component.onCompleted: {
if (searchInput.inputItem) {
searchInput.inputItem.forceActiveFocus()
// Intercept Tab keys before TextField handles them
searchInput.inputItem.Keys.onPressed.connect(function (event) {
if (event.key === Qt.Key_Tab) {
root.onTabPressed()
event.accepted = true
} else if (event.key === Qt.Key_Backtab) {
root.onBackTabPressed()
event.accepted = true
}
})
}
}
}
+9 -1
View File
@@ -65,6 +65,11 @@ SmartPanel {
"title": I18n.tr("session-menu.suspend"),
"isShutdown": false
},
"hibernate": {
"icon": "hibernate",
"title": I18n.tr("session-menu.hibernate"),
"isShutdown": false
},
"reboot": {
"icon": "reboot",
"title": I18n.tr("session-menu.reboot"),
@@ -159,6 +164,9 @@ SmartPanel {
CompositorService.suspend()
}
break
case "hibernate":
CompositorService.hibernate()
break
case "reboot":
CompositorService.reboot()
break
@@ -220,7 +228,7 @@ SmartPanel {
selectNextWrapped()
}
function onShiftTabPressed() {
function onBackTabPressed() {
selectPreviousWrapped()
}
@@ -12,16 +12,56 @@ ColumnLayout {
property var widgetData: null
property var widgetMetadata: null
property bool valueShowWorkspaceNumbers: widgetData.showWorkspaceNumbers !== undefined ? widgetData.showWorkspaceNumbers : widgetMetadata.showWorkspaceNumbers
property bool valueShowNumbersOnlyWhenOccupied: widgetData.showNumbersOnlyWhenOccupied !== undefined ? widgetData.showNumbersOnlyWhenOccupied : widgetMetadata.showNumbersOnlyWhenOccupied
property string valueLabelMode: widgetData.labelMode !== undefined ? widgetData.labelMode : (widgetMetadata ? widgetMetadata.labelMode : "index")
property bool valueHideUnoccupied: widgetData.hideUnoccupied !== undefined ? widgetData.hideUnoccupied : (widgetMetadata ? widgetMetadata.hideUnoccupied : false)
property bool valueShowWorkspaceNumbers: widgetData.showWorkspaceNumbers !== undefined ? widgetData.showWorkspaceNumbers : (widgetMetadata ? widgetMetadata.showWorkspaceNumbers : true)
property bool valueShowNumbersOnlyWhenOccupied: widgetData.showNumbersOnlyWhenOccupied !== undefined ? widgetData.showNumbersOnlyWhenOccupied : (widgetMetadata ? widgetMetadata.showNumbersOnlyWhenOccupied : true)
property bool valueColorizeIcons: widgetData.colorizeIcons !== undefined ? widgetData.colorizeIcons : (widgetMetadata ? widgetMetadata.colorizeIcons : false)
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.labelMode = valueLabelMode
settings.hideUnoccupied = valueHideUnoccupied
settings.showWorkspaceNumbers = valueShowWorkspaceNumbers
settings.showNumbersOnlyWhenOccupied = valueShowNumbersOnlyWhenOccupied
settings.colorizeIcons = valueColorizeIcons
return settings
}
NComboBox {
id: labelModeCombo
label: I18n.tr("bar.widget-settings.workspace.label-mode.label")
description: I18n.tr("bar.widget-settings.workspace.label-mode.description")
model: [{
"key": "none",
"name": I18n.tr("options.workspace-labels.none")
}, {
"key": "index",
"name": I18n.tr("options.workspace-labels.index")
}, {
"key": "name",
"name": I18n.tr("options.workspace-labels.name")
}]
currentKey: widgetData.labelMode || widgetMetadata.labelMode
onSelected: key => valueLabelMode = key
minimumWidth: 200
}
NToggle {
label: I18n.tr("bar.widget-settings.workspace.hide-unoccupied.label")
description: I18n.tr("bar.widget-settings.workspace.hide-unoccupied.description")
checked: valueHideUnoccupied
onToggled: checked => valueHideUnoccupied = checked
}
NToggle {
Layout.fillWidth: true
label: I18n.tr("bar.widget-settings.active-window.colorize-icons.label")
description: I18n.tr("bar.widget-settings.active-window.colorize-icons.description")
checked: root.valueColorizeIcons
onToggled: checked => root.valueColorizeIcons = checked
}
NToggle {
Layout.fillWidth: true
label: I18n.tr("bar.widget-settings.taskbar-grouped.show-workspace-numbers.label")
+49 -33
View File
@@ -77,6 +77,7 @@ SmartPanel {
Notifications,
ScreenRecorder,
SessionMenu,
SystemMonitor,
UserInterface,
Wallpaper
}
@@ -166,6 +167,10 @@ SmartPanel {
id: sessionMenuTab
SessionMenuTab {}
}
Component {
id: systemMonitorTab
SystemMonitorTab {}
}
// Order *DOES* matter
function updateTabsModel() {
@@ -179,31 +184,56 @@ SmartPanel {
"label": "settings.user-interface.title",
"icon": "settings-user-interface",
"source": userInterfaceTab
}, {
"id": SettingsPanel.Tab.ColorScheme,
"label": "settings.color-scheme.title",
"icon": "settings-color-scheme",
"source": colorSchemeTab
}, {
"id": SettingsPanel.Tab.Wallpaper,
"label": "settings.wallpaper.title",
"icon": "settings-wallpaper",
"source": wallpaperTab
}, {
"id": SettingsPanel.Tab.Bar,
"label": "settings.bar.title",
"icon": "settings-bar",
"source": barTab
}, {
"id": SettingsPanel.Tab.ControlCenter,
"label": "settings.control-center.title",
"icon": "settings-control-center",
"source": controlCenterTab
}, {
"id": SettingsPanel.Tab.Dock,
"label": "settings.dock.title",
"icon": "settings-dock",
"source": dockTab
}, {
"id": SettingsPanel.Tab.ControlCenter,
"label": "settings.control-center.title",
"icon": "settings-control-center",
"source": controlCenterTab
}, {
"id": SettingsPanel.Tab.Launcher,
"label": "settings.launcher.title",
"icon": "settings-launcher",
"source": launcherTab
}, {
"id": SettingsPanel.Tab.Notifications,
"label": "settings.notifications.title",
"icon": "settings-notifications",
"source": notificationsTab
}, {
"id": SettingsPanel.Tab.OSD,
"label": "settings.osd.title",
"icon": "settings-osd",
"source": osdTab
}, {
"id": SettingsPanel.Tab.LockScreen,
"label": "settings.lock-screen.title",
"icon": "settings-lock-screen",
"source": lockScreenTab
}, {
"id": SettingsPanel.Tab.SessionMenu,
"label": "settings.session-menu.title",
"icon": "settings-session-menu",
"source": sessionMenuTab
}, {
"id": SettingsPanel.Tab.Audio,
"label": "settings.audio.title",
@@ -214,16 +244,6 @@ SmartPanel {
"label": "settings.display.title",
"icon": "settings-display",
"source": displayTab
}, {
"id": SettingsPanel.Tab.OSD,
"label": "settings.osd.title",
"icon": "settings-osd",
"source": osdTab
}, {
"id": SettingsPanel.Tab.Notifications,
"label": "settings.notifications.title",
"icon": "settings-notifications",
"source": notificationsTab
}, {
"id": SettingsPanel.Tab.Network,
"label": "settings.network.title",
@@ -234,31 +254,27 @@ SmartPanel {
"label": "settings.location.title",
"icon": "settings-location",
"source": locationTab
}, {
"id": SettingsPanel.Tab.ColorScheme,
"label": "settings.color-scheme.title",
"icon": "settings-color-scheme",
"source": colorSchemeTab
}, {
"id": SettingsPanel.Tab.Wallpaper,
"label": "settings.wallpaper.title",
"icon": "settings-wallpaper",
"source": wallpaperTab
}, {
"id": SettingsPanel.Tab.ScreenRecorder,
"label": "settings.screen-recorder.title",
"icon": "settings-screen-recorder",
"source": screenRecorderTab
}, {
"id": SettingsPanel.Tab.Hooks,
"label": "settings.hooks.title",
"icon": "settings-hooks",
"source": hooksTab
}, {
"id": SettingsPanel.Tab.SessionMenu,
"label": "settings.session-menu.title",
"icon": "settings-session-menu",
"source": sessionMenuTab
}, // {
// "id": SettingsPanel.Tab.SystemMonitor,
// "label": "settings.system-monitor.title",
// "icon": "settings-system-monitor",
// "source": systemMonitorTab
// },
{
"id": SettingsPanel.Tab.Hooks,
"label": "settings.hooks.title",
"icon": "settings-hooks",
"source": hooksTab
}, {
"id": SettingsPanel.Tab.About,
"label": "settings.about.title",
@@ -340,7 +356,7 @@ SmartPanel {
selectNextTab()
}
function onShiftTabPressed() {
function onBackTabPressed() {
selectPreviousTab()
}
@@ -401,14 +417,14 @@ SmartPanel {
anchors.fill: parent
anchors.margins: Style.marginS
model: root.tabsModel
spacing: Style.marginXS
spacing: Style.marginXXS
currentIndex: root.currentTabIndex
verticalPolicy: ScrollBar.AsNeeded
delegate: Rectangle {
id: tabItem
width: sidebarList.verticalScrollBarActive ? sidebarList.width - sidebarList.scrollBarWidth - Style.marginXS : sidebarList.width
height: tabEntryRow.implicitHeight + Style.marginM * 2
height: tabEntryRow.implicitHeight + Style.marginS * 2
radius: Style.radiusS
color: selected ? Color.mPrimary : (tabItem.hovering ? Color.mHover : Color.transparent)
readonly property bool selected: index === root.currentTabIndex
-22
View File
@@ -24,22 +24,6 @@ ColumnLayout {
})
}
// Handler for drag start - disables panel background clicks
function handleDragStart() {
var panel = PanelService.getPanel("settingsPanel", screen)
if (panel && panel.disableBackgroundClick) {
panel.disableBackgroundClick()
}
}
// Handler for drag end - re-enables panel background clicks
function handleDragEnd() {
var panel = PanelService.getPanel("settingsPanel", screen)
if (panel && panel.enableBackgroundClick) {
panel.enableBackgroundClick()
}
}
NHeader {
label: I18n.tr("settings.bar.appearance.section.label")
description: I18n.tr("settings.bar.appearance.section.description")
@@ -234,8 +218,6 @@ ColumnLayout {
onReorderWidget: (section, fromIndex, toIndex) => _reorderWidgetInSection(section, fromIndex, toIndex)
onUpdateWidgetSettings: (section, index, settings) => _updateWidgetSettingsInSection(section, index, settings)
onMoveWidget: (fromSection, index, toSection) => _moveWidgetBetweenSections(fromSection, index, toSection)
onDragPotentialStarted: root.handleDragStart()
onDragPotentialEnded: root.handleDragEnd()
}
// Center Section
@@ -251,8 +233,6 @@ ColumnLayout {
onReorderWidget: (section, fromIndex, toIndex) => _reorderWidgetInSection(section, fromIndex, toIndex)
onUpdateWidgetSettings: (section, index, settings) => _updateWidgetSettingsInSection(section, index, settings)
onMoveWidget: (fromSection, index, toSection) => _moveWidgetBetweenSections(fromSection, index, toSection)
onDragPotentialStarted: root.handleDragStart()
onDragPotentialEnded: root.handleDragEnd()
}
// Right Section
@@ -268,8 +248,6 @@ ColumnLayout {
onReorderWidget: (section, fromIndex, toIndex) => _reorderWidgetInSection(section, fromIndex, toIndex)
onUpdateWidgetSettings: (section, index, settings) => _updateWidgetSettingsInSection(section, index, settings)
onMoveWidget: (fromSection, index, toSection) => _moveWidgetBetweenSections(fromSection, index, toSection)
onDragPotentialStarted: root.handleDragStart()
onDragPotentialEnded: root.handleDragEnd()
}
}
}
+43 -10
View File
@@ -677,6 +677,8 @@ ColumnLayout {
enabled: ProgramCheckerService.availableDiscordClients.length > 0
opacity: ProgramCheckerService.availableDiscordClients.length > 0 ? 1.0 : 0.6
onToggled: checked => {
// Set unified discord property
Settings.data.templates.discord = checked
// Enable/disable all detected Discord clients
for (var i = 0; i < ProgramCheckerService.availableDiscordClients.length; i++) {
var client = ProgramCheckerService.availableDiscordClients[i]
@@ -740,19 +742,50 @@ ColumnLayout {
}
}
// Code clients - single toggle with dynamic description
NCheckbox {
id: codeToggle
label: "Code"
description: ProgramCheckerService.codeAvailable ? I18n.tr("settings.color-scheme.templates.programs.code.description", {
"filepath": "~/.vscode/extensions/hyprluna.hyprluna-theme-1.0.2/themes/hyprluna.json"
}) : I18n.tr("settings.color-scheme.templates.programs.code.description-missing", {
"app": "code"
})
checked: Settings.data.templates.code
enabled: ProgramCheckerService.codeAvailable
opacity: ProgramCheckerService.codeAvailable ? 1.0 : 0.6
description: {
if (ProgramCheckerService.availableCodeClients.length === 0) {
return I18n.tr("settings.color-scheme.templates.programs.code.description-missing")
} else {
// Show detected clients
var clientInfo = []
for (var i = 0; i < ProgramCheckerService.availableCodeClients.length; i++) {
var client = ProgramCheckerService.availableCodeClients[i]
// Capitalize first letter and format nicely
var clientName = client.name === "code" ? "VSCode" : "VSCodium"
clientInfo.push(clientName)
}
return "Detected: " + clientInfo.join(", ")
}
}
Layout.fillWidth: true
Layout.preferredWidth: -1
checked: {
// Check if any Code client template is enabled
var anyEnabled = false
for (var i = 0; i < ProgramCheckerService.availableCodeClients.length; i++) {
var client = ProgramCheckerService.availableCodeClients[i]
if (Settings.data.templates["code_" + client.name]) {
anyEnabled = true
break
}
}
return anyEnabled
}
enabled: ProgramCheckerService.availableCodeClients.length > 0
opacity: ProgramCheckerService.availableCodeClients.length > 0 ? 1.0 : 0.6
onToggled: checked => {
if (ProgramCheckerService.codeAvailable) {
Settings.data.templates.code = checked
// Set unified code property
Settings.data.templates.code = checked
// Enable/disable all detected Code clients
for (var i = 0; i < ProgramCheckerService.availableCodeClients.length; i++) {
var client = ProgramCheckerService.availableCodeClients[i]
Settings.data.templates["code_" + client.name] = checked
}
if (ProgramCheckerService.availableCodeClients.length > 0) {
AppThemeService.generate()
}
}
@@ -38,22 +38,6 @@ ColumnLayout {
"required": false
}]
// Handler for drag start - disables panel background clicks
function handleDragStart() {
var panel = PanelService.getPanel("settingsPanel", screen)
if (panel && panel.disableBackgroundClick) {
panel.disableBackgroundClick()
}
}
// Handler for drag end - re-enables panel background clicks
function handleDragEnd() {
var panel = PanelService.getPanel("settingsPanel", screen)
if (panel && panel.enableBackgroundClick) {
panel.enableBackgroundClick()
}
}
function saveCards() {
var toSave = []
for (var i = 0; i < cardsModel.length; i++) {
@@ -202,12 +186,6 @@ ColumnLayout {
Layout.fillWidth: true
model: cardsModel
disabledIds: Settings.data.location.weatherEnabled ? [] : ["weather-card"]
onDragPotentialStarted: {
root.handleDragStart()
}
onDragPotentialEnded: {
root.handleDragEnd()
}
onItemToggled: function (index, enabled) {
//Logger.i("ControlCenterTab", "Item", index, "toggled to", enabled)
var newModel = cardsModel.slice()
@@ -266,8 +244,6 @@ ColumnLayout {
onReorderWidget: (section, fromIndex, toIndex) => _reorderWidgetInSection(section, fromIndex, toIndex)
onUpdateWidgetSettings: (section, index, settings) => _updateWidgetSettingsInSection(section, index, settings)
onMoveWidget: (fromSection, index, toSection) => _moveWidgetBetweenSections(fromSection, index, toSection)
onDragPotentialStarted: root.handleDragStart()
onDragPotentialEnded: root.handleDragEnd()
}
// Right
@@ -285,8 +261,6 @@ ColumnLayout {
onReorderWidget: (section, fromIndex, toIndex) => _reorderWidgetInSection(section, fromIndex, toIndex)
onUpdateWidgetSettings: (section, index, settings) => _updateWidgetSettingsInSection(section, index, settings)
onMoveWidget: (fromSection, index, toSection) => _moveWidgetBetweenSections(fromSection, index, toSection)
onDragPotentialStarted: root.handleDragStart()
onDragPotentialEnded: root.handleDragEnd()
}
}
}
+1 -1
View File
@@ -233,7 +233,7 @@ ColumnLayout {
}
NButton {
visible: !DistroService.isNixOS
visible: !HostService.isNixOS
text: I18n.tr("settings.general.launch-setup-wizard")
onClicked: {
var targetScreen = PanelService.openedPanel ? PanelService.openedPanel.screen : (Quickshell.screens.length > 0 ? Quickshell.screens[0] : null)
+26 -35
View File
@@ -11,15 +11,15 @@ ColumnLayout {
// Helper functions to update arrays immutably
function addMonitor(list, name) {
const arr = (list || []).slice();
const arr = (list || []).slice()
if (!arr.includes(name))
arr.push(name);
return arr;
arr.push(name)
return arr
}
function removeMonitor(list, name) {
return (list || []).filter(function (n) {
return n !== name;
});
return n !== name
})
}
// Display
@@ -30,40 +30,31 @@ ColumnLayout {
NComboBox {
label: I18n.tr("settings.osd.location.label")
description: I18n.tr("settings.osd.location.description")
model: [
{
model: [{
"key": "top",
"name": I18n.tr("options.osd.position.top_center")
},
{
}, {
"key": "top_left",
"name": I18n.tr("options.osd.position.top_left")
},
{
}, {
"key": "top_right",
"name": I18n.tr("options.osd.position.top_right")
},
{
}, {
"key": "bottom",
"name": I18n.tr("options.osd.position.bottom_center")
},
{
}, {
"key": "bottom_left",
"name": I18n.tr("options.osd.position.bottom_left")
},
{
}, {
"key": "bottom_right",
"name": I18n.tr("options.osd.position.bottom_right")
},
{
}, {
"key": "left",
"name": I18n.tr("options.osd.position.center_left")
},
{
}, {
"key": "right",
"name": I18n.tr("options.osd.position.center_right")
}
]
}]
currentKey: Settings.data.osd.location || "top_right"
onSelected: key => Settings.data.osd.location = key
}
@@ -152,22 +143,22 @@ ColumnLayout {
Layout.fillWidth: true
label: modelData.name || I18n.tr("system.unknown")
description: {
const compositorScale = CompositorService.getDisplayScale(modelData.name);
const compositorScale = CompositorService.getDisplayScale(modelData.name)
I18n.tr("system.monitor-description", {
"model": modelData.model,
"width": modelData.width * compositorScale,
"height": modelData.height * compositorScale,
"scale": compositorScale
});
"model": modelData.model,
"width": modelData.width * compositorScale,
"height": modelData.height * compositorScale,
"scale": compositorScale
})
}
checked: (Settings.data.osd.monitors || []).indexOf(modelData.name) !== -1
onToggled: checked => {
if (checked) {
Settings.data.osd.monitors = addMonitor(Settings.data.osd.monitors, modelData.name);
} else {
Settings.data.osd.monitors = removeMonitor(Settings.data.osd.monitors, modelData.name);
}
}
if (checked) {
Settings.data.osd.monitors = addMonitor(Settings.data.osd.monitors, modelData.name)
} else {
Settings.data.osd.monitors = removeMonitor(Settings.data.osd.monitors, modelData.name)
}
}
}
}
}
@@ -22,6 +22,11 @@ ColumnLayout {
"text": I18n.tr("session-menu.suspend"),
"enabled": true,
"required": false
}, {
"id": "hibernate",
"text": I18n.tr("session-menu.hibernate"),
"enabled": true,
"required": false
}, {
"id": "reboot",
"text": I18n.tr("session-menu.reboot"),
@@ -39,28 +44,6 @@ ColumnLayout {
"required": false
}]
// Handler for drag start - disables panel background clicks
function handleDragStart() {
var currentScreen = Quickshell.screens && Quickshell.screens.length > 0 ? Quickshell.screens[0] : null
if (currentScreen) {
var panel = PanelService.getPanel("settingsPanel", currentScreen)
if (panel && panel.disableBackgroundClick) {
panel.disableBackgroundClick()
}
}
}
// Handler for drag end - re-enables panel background clicks
function handleDragEnd() {
var currentScreen = Quickshell.screens && Quickshell.screens.length > 0 ? Quickshell.screens[0] : null
if (currentScreen) {
var panel = PanelService.getPanel("settingsPanel", currentScreen)
if (panel && panel.enableBackgroundClick) {
panel.enableBackgroundClick()
}
}
}
function saveEntries() {
var toSave = []
for (var i = 0; i < entriesModel.length; i++) {
@@ -201,12 +184,6 @@ ColumnLayout {
Layout.fillWidth: true
model: entriesModel
disabledIds: []
onDragPotentialStarted: {
root.handleDragStart()
}
onDragPotentialEnded: {
root.handleDragEnd()
}
onItemToggled: function (index, enabled) {
var newModel = entriesModel.slice()
newModel[index] = Object.assign({}, newModel[index], {
@@ -0,0 +1,22 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
import qs.Services.UI
import qs.Widgets
ColumnLayout {
id: root
spacing: Style.marginL
Component.onCompleted: {
}
NHeader {
label: I18n.tr("settings.system-monitor.general.section.label")
description: I18n.tr("settings.system-monitor.general.section.description")
}
}
@@ -368,6 +368,11 @@ Singleton {
Quickshell.execDetached(["sh", "-c", "systemctl suspend || loginctl suspend"])
}
function hibernate() {
Logger.i("Compositor", "Hibernate requested")
Quickshell.execDetached(["sh", "-c", "systemctl hibernate || loginctl hibernate"])
}
function lockAndSuspend() {
Logger.i("Compositor", "Lock and suspend requested")
try {
+1 -1
View File
@@ -16,7 +16,7 @@ Singleton {
* - LockScreen is opened
* - A control center is open
*/
property bool shouldRun: BarService.hasAudioVisualizer || PanelService.lockScreen?.active || (PanelService.openedPanel && PanelService.openedPanel.objectName.startsWith("controlCenterPanel"))
property bool shouldRun: BarService.hasAudioVisualizer || PanelService.lockScreen?.active || (PanelService.openedPanel && PanelService.openedPanel.panelWrapper.objectName.startsWith("controlCenterPanel"))
property var values: Array(barsCount).fill(0)
property int barsCount: 32
+83
View File
@@ -18,6 +18,8 @@ Singleton {
property bool ethernetConnected: false
property string disconnectingFrom: ""
property string forgettingNetwork: ""
property string networkConnectivity: "unknown"
property bool internetConnectivity: true
property bool ignoreScanResults: false
property bool scanPending: false
@@ -98,6 +100,16 @@ Singleton {
onTriggered: ethernetStateProcess.running = true
}
// Internet connectivity check timer
// Always running every 15s
Timer {
id: connectivityCheckTimer
interval: 15000
running: true
repeat: true
onTriggered: connectivityCheckProcess.running = true
}
// Core functions
function syncWifiState() {
wifiStateProcess.running = true
@@ -211,6 +223,8 @@ Singleton {
// Helper functions
function signalIcon(signal, isConnected = false) {
if (isConnected && !root.internetConnectivity)
return "world-off"
if (signal >= 80)
return "wifi"
if (signal >= 50)
@@ -285,6 +299,75 @@ Singleton {
}
}
// Process to check the internet connectivity of the connected network
Process {
id: connectivityCheckProcess
running: false
command: ["nmcli", "networking", "connectivity", "check"]
property int failedChecks: 0
stdout: StdioCollector {
onStreamFinished: {
const result = text.trim()
if (!result) {
return
}
if (result === "none" && root.networkConnectivity !== result) {
root.networkConnectivity = result
connectivityCheckProcess.failedChecks = 0
root.scan()
}
if (result === "full" && root.networkConnectivity !== result) {
root.networkConnectivity = result
root.internetConnectivity = true
connectivityCheckProcess.failedChecks = 0
root.scan()
}
if ((result === "limited" || result === "portal") && root.networkConnectivity !== result) {
connectivityCheckProcess.failedChecks++
if (connectivityCheckProcess.failedChecks === 3) {
root.networkConnectivity = result
pingCheckProcess.running = true
}
}
if (result === "unknown" && root.networkConnectivity !== result) {
root.networkConnectivity = result
connectivityCheckProcess.failedChecks = 0
}
}
}
stderr: StdioCollector {
onStreamFinished: {
if (text.trim()) {
Logger.w("Network", "Connectivity check error: " + text)
}
}
}
}
Process {
id: pingCheckProcess
command: ["sh", "-c", "ping -c1 -W2 ping.archlinux.org >/dev/null 2>&1 || " + "ping -c1 -W2 1.1.1.1 >/dev/null 2>&1 || " + "curl -fsI --max-time 5 https://cloudflare.com/cdn-cgi/trace >/dev/null 2>&1"]
onExited: (exitCode, exitStatus) => {
if (exitCode === 0) {
connectivityCheckProcess.failedChecks = 0
} else {
root.internetConnectivity = false
Logger.i("Network", "No internet connectivity")
ToastService.showWarning(root.cachedLastConnected, I18n.tr("toast.internet.limited"))
connectivityCheckProcess.failedChecks = 0
}
root.scan()
}
}
// Helper process to get existing profiles
Process {
id: profileCheckProcess
@@ -15,7 +15,7 @@ Singleton {
property bool isReady: false
function init() {
Logger.i("DistroService", "Service started")
Logger.i("HostService", "Service started")
}
// Internal helpers
@@ -81,7 +81,7 @@ Singleton {
return l ? l.split("=")[1].replace(/"/g, "") : ""
}
root.osPretty = val("PRETTY_NAME") || val("NAME")
Logger.i("DistroService", root.osPretty)
Logger.i("HostService", root.osPretty)
const osId = (val("ID") || "").toLowerCase()
root.isNixOS = osId === "nixos" || (root.osPretty || "").toLowerCase().includes("nixos")
@@ -91,7 +91,7 @@ Singleton {
}
root.isReady = true
} catch (e) {
Logger.w("DistroService", "failed to read os-release", e)
Logger.w("HostService", "failed to read os-release", e)
}
}
}
@@ -102,10 +102,10 @@ Singleton {
const p = String(stdout.text || "").trim()
if (code === 0 && p) {
root.osLogo = `file://${p}`
Logger.i("DistroService", "found", root.osLogo)
Logger.i("HostService", "found", root.osLogo)
} else {
root.osLogo = ""
Logger.w("DistroService", "none found")
Logger.w("HostService", "none found")
}
}
stdout: StdioCollector {}
+71 -3
View File
@@ -31,6 +31,9 @@ Singleton {
// Discord client auto-detection
property var availableDiscordClients: []
// Code client auto-detection
property var availableCodeClients: []
// Signal emitted when all checks are complete
signal checksCompleted
@@ -42,12 +45,16 @@ Singleton {
for (var i = 0; i < TemplateRegistry.discordClients.length; i++) {
var client = TemplateRegistry.discordClients[i]
var clientName = client.name
var configPath = client.configPath
// Use the actual config path from the client, removing ~ prefix
var checkPath = configPath.startsWith("~") ? configPath.substring(2) : configPath.substring(1)
// Check if this client requires themes folder to exist
if (client.requiresThemesFolder) {
scriptParts.push("if [ -d \"$HOME/.config/" + clientName + "/themes\" ]; then available_clients=\"$available_clients " + clientName + "\"; fi;")
scriptParts.push("if [ -d \"$HOME/" + checkPath + "/themes\" ]; then available_clients=\"$available_clients " + clientName + "\"; fi;")
} else {
scriptParts.push("if [ -d \"$HOME/.config/" + clientName + "\" ]; then available_clients=\"$available_clients " + clientName + "\"; fi;")
scriptParts.push("if [ -d \"$HOME/" + checkPath + "\" ]; then available_clients=\"$available_clients " + clientName + "\"; fi;")
}
}
@@ -97,6 +104,66 @@ Singleton {
stderr: StdioCollector {}
}
// Function to detect Code client by checking config directories
function detectCodeClient() {
// Build shell script to check each client
var scriptParts = ["available_clients=\"\";"]
for (var i = 0; i < TemplateRegistry.codeClients.length; i++) {
var client = TemplateRegistry.codeClients[i]
var clientName = client.name
var configPath = client.configPath
// Check if the config directory exists
scriptParts.push("if [ -d \"$HOME" + configPath.substring(1) + "\" ]; then available_clients=\"$available_clients " + clientName + "\"; fi;")
}
scriptParts.push("echo \"$available_clients\"")
// Use a Process to check directory existence for all clients
codeDetector.command = ["sh", "-c", scriptParts.join(" ")]
codeDetector.running = true
}
// Process to detect Code client directories
Process {
id: codeDetector
running: false
onExited: function (exitCode) {
availableCodeClients = []
if (exitCode === 0) {
var detectedClients = stdout.text.trim().split(/\s+/).filter(function (client) {
return client.length > 0
})
if (detectedClients.length > 0) {
// Build list of available clients
for (var i = 0; i < detectedClients.length; i++) {
var clientName = detectedClients[i]
for (var j = 0; j < TemplateRegistry.codeClients.length; j++) {
var client = TemplateRegistry.codeClients[j]
if (client.name === clientName) {
availableCodeClients.push(client)
break
}
}
}
Logger.i("ProgramChecker", "Detected Code clients:", detectedClients.join(", "))
}
}
if (availableCodeClients.length === 0) {
Logger.d("ProgramChecker", "No Code clients detected")
}
}
stdout: StdioCollector {}
stderr: StdioCollector {}
}
// Programs to check - maps property names to commands
readonly property var programsToCheck: ({
"matugenAvailable": ["which", "matugen"],
@@ -140,8 +207,9 @@ Singleton {
// Check next program or emit completion signal
if (root.completedChecks >= root.totalChecks) {
// Run Discord client detection after all checks are complete
// Run Discord and Code client detection after all checks are complete
root.detectDiscordClient()
root.detectCodeClient()
root.checksCompleted()
} else {
root.checkNextProgram()
+59
View File
@@ -114,6 +114,18 @@ Singleton {
}
})
}
} else if (app.id === "code") {
// Handle Code clients specially
if (Settings.data.templates.code) {
app.clients.forEach(client => {
// Check if this specific client is detected
if (isCodeClientEnabled(client.name)) {
lines.push(`\n[templates.code_${client.name}]`)
lines.push(`input_path = "${Quickshell.shellDir}/Assets/MatugenTemplates/${app.input}"`)
lines.push(`output_path = "${client.path}"`)
}
})
}
} else {
// Handle regular apps
if (Settings.data.templates[app.id]) {
@@ -140,6 +152,16 @@ Singleton {
return false
}
function isCodeClientEnabled(clientName) {
// Check ProgramCheckerService to see if client is detected
for (var i = 0; i < ProgramCheckerService.availableCodeClients.length; i++) {
if (ProgramCheckerService.availableCodeClients[i].name === clientName) {
return true
}
}
return false
}
function buildMatugenScript(content, wallpaper, mode) {
const delimiter = "MATUGEN_CONFIG_EOF_" + Math.random().toString(36).substr(2, 9)
const pathEsc = dynamicConfigPath.replace(/'/g, "'\\''")
@@ -163,6 +185,10 @@ Singleton {
if (Settings.data.templates.discord) {
script += processDiscordClients(app, colors, mode, homeDir)
}
} else if (app.id === "code") {
if (Settings.data.templates.code) {
script += processCodeClients(app, colors, mode, homeDir)
}
} else {
if (Settings.data.templates[app.id]) {
script += processTemplate(app, colors, mode, homeDir)
@@ -198,6 +224,39 @@ Singleton {
return script
}
function processCodeClients(codeApp, colors, mode, homeDir) {
let script = ""
const palette = ColorPaletteGenerator.generatePalette(colors, Settings.data.colorSchemes.darkMode, false)
codeApp.clients.forEach(client => {
if (!isCodeClientEnabled(client.name))
return
const templatePath = `${Quickshell.shellDir}/Assets/MatugenTemplates/${codeApp.input}`
const outputPath = client.path.replace("~", homeDir)
const outputDir = outputPath.substring(0, outputPath.lastIndexOf('/'))
// Extract base config directory for checking
var baseConfigDir = ""
if (client.name === "code") {
baseConfigDir = "~/.vscode".replace("~", homeDir)
} else if (client.name === "codium") {
baseConfigDir = "~/.vscode-oss".replace("~", homeDir)
}
script += `\n`
script += `if [ -d "${baseConfigDir}" ]; then\n`
script += ` mkdir -p ${outputDir}\n`
script += ` cp '${templatePath}' '${outputPath}'\n`
script += ` ${replaceColorsInFile(outputPath, palette)}`
script += `else\n`
script += ` echo "Code client ${client.name} not found at ${baseConfigDir}, skipping"\n`
script += `fi\n`
})
return script
}
function processTemplate(app, colors, mode, homeDir) {
const palette = ColorPaletteGenerator.generatePalette(colors, Settings.data.colorSchemes.darkMode, app.strict || false)
let script = ""
+35 -4
View File
@@ -139,17 +139,21 @@ Singleton {
"requiresThemesFolder": false
}, {
"name": "vencord",
"path": "~/.config/discord",
"requiresThemesFolder": true
"path": "~/.config/Vencord",
"requiresThemesFolder": false
}]
}, // VSCode with hardcoded path (requirement #5)
},
{
"id": "code",
"name": "VSCode",
"category": "applications",
"input": "code.json",
"outputs": [{
"clients": [{
"name": "code",
"path": "~/.vscode/extensions/hyprluna.hyprluna-theme-1.0.2/themes/hyprluna.json"
}, {
"name": "codium",
"path": "~/.vscode-oss/extensions/hyprluna.hyprluna-theme-1.0.2/themes/hyprluna.json"
}]
}, {
"id": "spicetify",
@@ -179,6 +183,33 @@ Singleton {
return clients
}
// Extract Code clients for ProgramCheckerService compatibility
readonly property var codeClients: {
var clients = []
var codeApp = applications.find(app => app.id === "code")
if (codeApp && codeApp.clients) {
codeApp.clients.forEach(client => {
// Extract base config directory from theme path
var themePath = client.path
var baseConfigDir = ""
if (client.name === "code") {
// For VSCode: ~/.vscode/extensions/... -> ~/.vscode
baseConfigDir = "~/.vscode"
} else if (client.name === "codium") {
// For VSCodium: ~/.vscode-oss/extensions/... -> ~/.vscode-oss
baseConfigDir = "~/.vscode-oss"
}
clients.push({
"name": client.name,
"configPath": baseConfigDir,
"themePath": themePath,
"requiresThemesFolder": false
})
})
}
return clients
}
// Build user templates TOML content
function buildUserTemplatesToml() {
var lines = []
+5 -1
View File
@@ -162,7 +162,11 @@ Singleton {
"TaskbarGrouped": {
"allowUserSettings": true,
"showWorkspaceNumbers": true,
"showNumbersOnlyWhenOccupied": true
"showNumbersOnlyWhenOccupied": true,
"labelMode": "index",
"hideUnoccupied": false,
"characterCount": 2,
"colorizeIcons": false
},
"Tray": {
"allowUserSettings": true,
+1 -1
View File
@@ -15,7 +15,7 @@ ColumnLayout {
NText {
text: root.label
pointSize: Style.fontSizeXL
font.weight: Style.fontWeightBold
font.weight: Style.fontWeightSemiBold
color: Color.mSecondary
visible: root.label !== ""
}
+1 -1
View File
@@ -16,7 +16,7 @@ ColumnLayout {
NText {
text: label
pointSize: Style.fontSizeL
font.weight: Style.fontWeightBold
font.weight: Style.fontWeightSemiBold
color: labelColor
visible: label !== ""
Layout.fillWidth: true
+3 -3
View File
@@ -81,7 +81,7 @@ ShellRoot {
BatteryService.init()
IdleInhibitorService.init()
PowerProfileService.init()
DistroService.init()
HostService.init()
FontService.init()
// Only open the setup wizard for new users
@@ -128,13 +128,13 @@ ShellRoot {
function checkSetupWizard() {
// Wait for distro service
if (!DistroService.isReady) {
if (!HostService.isReady) {
Qt.callLater(checkSetupWizard)
return
}
// No setup wizard on NixOS
if (DistroService.isNixOS) {
if (HostService.isNixOS) {
Settings.data.setupCompleted = true
return
}