Compare commits

...

102 Commits

Author SHA1 Message Date
ItsLemmy
53d885a8ae v2.21.1 2025-10-31 17:19:59 -04:00
ItsLemmy
82c6578a9f CalendarPanel: better auto sizing. 2025-10-31 16:45:27 -04:00
ItsLemmy
691dcc9c97 NPanel: better logic for auto detaching. 2025-10-31 16:20:38 -04:00
ItsLemmy
c036ff0d7a NPanel: if bar opacity < 33%, detach panel from bar and dont match bg opacity. 2025-10-31 16:12:41 -04:00
ItsLemmy
87144df024 translations: more accurate volume/microphone tooltips. 2025-10-31 15:48:56 -04:00
Ly-sec
aad8cd46b5 Set version to dev 2025-10-31 20:41:30 +01:00
ItsLemmy
c3cf3dcf32 v2.21.0 2025-10-31 15:34:07 -04:00
ItsLemmy
c16ee69de6 translations 2025-10-31 15:32:20 -04:00
Ly-sec
8e6e110447 Merge branch 'main' of https://github.com/noctalia-dev/noctalia-shell 2025-10-31 20:26:29 +01:00
Ly-sec
17bd4e79f8 Volume & Input widget: switch left & right click logic 2025-10-31 20:26:09 +01:00
ItsLemmy
698be35791 BatteryPanel: NBox wrapping 2025-10-31 15:17:44 -04:00
ItsLemmy
c0d50b87c0 Wifit: NBox wrapping 2025-10-31 15:07:06 -04:00
ItsLemmy
2b6bcdc570 autofmt 2025-10-31 15:06:57 -04:00
ItsLemmy
13f82d10e2 Bluetooth: NBox wrapping 2025-10-31 14:58:41 -04:00
Lysec
56203e1a07 Merge pull request #632 from lonerOrz/fix/mediaCard
fix: Media player auto-switching in Control Center
2025-10-31 19:39:29 +01:00
Ly-sec
7316695aac AudioPanel: wrap header, input and output in separate NBox 2025-10-31 19:32:21 +01:00
Ly-sec
640ed729e5 Merge branch 'main' of https://github.com/noctalia-dev/noctalia-shell 2025-10-31 19:17:13 +01:00
Ly-sec
08191678df NotificationHistoryPanel: wrap header area in NBox 2025-10-31 19:17:09 +01:00
ItsLemmy
9680dd83fd VSCode: minor improvements to template. 2025-10-31 14:16:42 -04:00
loner
3b166bd270 fix: Media player auto-switching in Control Center
Detailed Explanation:
  Addresses an issue where the media player would automatically
switch,overriding user's manual selections when the Control Center was
open.

  The core problem was that the automatic player detection logic was too
aggressive and didn't respect explicit user choices. Previous attempts
to pause auto-switching based on UI visibility were unreliable due to
incorrect event handling for the custom NPanel component.
2025-11-01 02:15:44 +08:00
ItsLemmy
cf12b98351 Dim Desktop: more cleanup 2025-10-31 11:56:26 -04:00
ItsLemmy
066d6f2e2f DimDesktop: removed in favor of nice curvy corners on panels 2025-10-31 10:53:59 -04:00
ItsLemmy
81edc14e63 CalendarPanel: improved the look 2025-10-31 10:38:36 -04:00
ItsLemmy
5303a88003 Calendar weather: hide double location 2025-10-31 10:08:22 -04:00
ItsLemmy
3a534f8f72 autofmt 2025-10-31 10:08:09 -04:00
Lemmy
1bacf397ed Merge pull request #630 from MrDowntempo/fix/color-convert-refactor
Using ColorsConvert.js instead of adding functions to ClockLoader.qml
2025-10-31 10:04:54 -04:00
ItsLemmy
18501a5b9e Screencorners: disabled if bar is non floating and transparent with attached panels 2025-10-31 09:48:39 -04:00
Corey Woodworth
d6cc4660dd Using ColorsConvert.js instead of adding functions to ClockLoader.qml 2025-10-31 09:17:53 -04:00
ItsLemmy
1152453d84 Settings: ShowCalendarWeather - true by default 2025-10-31 08:37:08 -04:00
Lysec
436ff56c93 Merge pull request #629 from MrDowntempo/feat/lock-keys-tweaks
Feat/lock keys tweaks
2025-10-31 13:27:11 +01:00
ItsLemmy
b7dc1aed84 Calendar Panel: improve look when bar is transparent + NBox simplification. 2025-10-31 08:17:46 -04:00
ItsLemmy
336deba554 NPanel: improve look at zero opacity 2025-10-31 06:56:28 -04:00
Corey Woodworth
0af85721b3 Finished Translation 2025-10-31 01:25:48 -04:00
Corey Woodworth
b047837543 Configurable Icons for the indications, and I18n. 2025-10-31 01:17:50 -04:00
ItsLemmy
aa30e90ec7 NPanel: use bar bg color when attached. 2025-10-31 01:01:55 -04:00
ItsLemmy
3b63384a51 Floating bar: better look when attachedPanels 2025-10-31 00:54:02 -04:00
ItsLemmy
cf36389fa6 NSectionEditor: Easier to access right click to move widgets around 2025-10-31 00:30:17 -04:00
ItsLemmy
abf346e485 feat: NPanel + Calendar - added dynamic resizing 2025-10-31 00:17:36 -04:00
ItsLemmy
d4be3a2cc2 qmlfmt 2025-10-30 23:46:00 -04:00
ItsLemmy
84e058fb07 Wallpaper: is never attached to a button 2025-10-30 23:34:05 -04:00
ItsLemmy
833a9c1a8f Merge branch 'main' of github.com:noctalia-dev/noctalia-shell 2025-10-30 22:49:38 -04:00
ItsLemmy
0cdc5bd518 NPanel: restore outter border on panels without sexy borders + minor polishing. 2025-10-30 22:49:36 -04:00
Lemmy
f955e2c87d Merge pull request #626 from MrDowntempo/feat/lock-keys-widget
Feat/lock keys widget
2025-10-30 22:48:36 -04:00
ItsLemmy
64dcb0d34e Panels: beautifull NRectangleCurved shape - conditionnal with a new settings, default is true. 2025-10-30 22:42:12 -04:00
MrDowntempo
75acc2fd82 Increase poll timer interval from 125 to 200 ms
I had THOUGHT I had increased this. Now I have.
2025-10-30 21:04:44 -04:00
Corey Woodworth
a6d7d077f1 I18n and translations done. Increased Timer to 200, removed unneeded import (i think) 2025-10-30 20:41:56 -04:00
Corey Woodworth
6a74924e04 Resolved broken settings. Utilized Singleton more effectively 2025-10-30 20:13:12 -04:00
Corey Woodworth
c955db20b7 Add settings to toggle which LockKeys to watch. Doesn't work yet. My settings are coming back undefined 2025-10-30 20:13:12 -04:00
Corey Woodworth
928b64e64a Add LockKeysService. It unfortunetly requires polling. I've not been able to find an event driven way to accomplish this. 2025-10-30 20:12:32 -04:00
Corey Woodworth
5f79dac0f2 Add LockKeys to BarWidgetRegistry 2025-10-30 20:12:32 -04:00
ItsLemmy
129609ec2c vscode: app theming, courtesy of TUI!
https://github.com/tuibird/
2025-10-30 20:06:52 -04:00
ItsLemmy
f42bcef239 CalendarService: moved some log to debug to reduce spam 2025-10-30 19:14:38 -04:00
ItsLemmy
9f62eacf27 WidgetSettings: fixed centering 2025-10-30 19:06:55 -04:00
ItsLemmy
c5cb1e6500 BarWidgetSettings: always open centered. (avoid potential warning) 2025-10-30 18:09:01 -04:00
ItsLemmy
9a02f58d29 Workspace Settings: dont set your own state or you break bindings. 2025-10-30 18:08:42 -04:00
ItsLemmy
29ad654a58 Locale: factorized usage in I18n. 2025-10-30 17:43:35 -04:00
ItsLemmy
e1d39f3bbc Locale: fixed all Qt.locale calls to respect the user selected locale. 2025-10-30 17:20:54 -04:00
ItsLemmy
bc9fe06fd8 Sysmon: disabled right click until I get back to this feat. 2025-10-30 17:20:03 -04:00
ItsLemmy
77e004566c Calendar: fix layout. 2025-10-30 16:15:16 -04:00
ItsLemmy
4377637790 Calendar: support for custom first day of the week 2025-10-30 15:44:56 -04:00
Lemmy
691b2e3a7d Merge pull request #624 from lonerOrz/feat/ipc-color-scheme
feat: Add IPC command to set color scheme
2025-10-30 14:58:52 -04:00
ItsLemmy
e60e2b5eb1 LocationTab: fix ntoggle opacity 2025-10-30 14:11:20 -04:00
ItsLemmy
8db8913bd3 NToggle: no mouse interaction when componend is disabled 2025-10-30 14:11:04 -04:00
ItsLemmy
db8803d137 Calendar weather: respect global weather enabled flag. 2025-10-30 14:06:36 -04:00
loner
cd5b48f26d feat: Add IPC command to set color scheme 2025-10-31 02:05:45 +08:00
ItsLemmy
ca72a5ca8f Calendar: conditional weather card - wip 2025-10-30 14:02:42 -04:00
Michael Buckley
114cbc9f9f Nix: updated nix inputs 2025-10-30 18:58:51 +01:00
Lemmy
6e156c3ae5 Merge pull request #623 from lonerOrz/fix/clock
Fix: Add clockItem ID to ClockLoader in CalendarPanel.qml
2025-10-30 13:26:28 -04:00
loner
4cc4c364d4 Fix: Add clockItem ID to ClockLoader in CalendarPanel.qml 2025-10-31 01:19:23 +08:00
Lemmy
344b5f9a8c Merge pull request #622 from MrDowntempo/fix/smarter-colors
Fix/smarter colors
2025-10-30 13:19:07 -04:00
Corey Woodworth
1a1ef85fa5 Undeleted a comment 2025-10-30 12:44:46 -04:00
Corey Woodworth
49e2bc6905 cleanup 2025-10-30 12:43:15 -04:00
Corey Woodworth
fba4bf6b74 Fix: Even smarter secondHand color picker. 2025-10-30 12:19:40 -04:00
ItsLemmy
b7ff9e73e4 dock: improve translations to explain the new behavior 2025-10-30 09:47:51 -04:00
ItsLemmy
732f58b967 Merge branch 'main' of github.com:noctalia-dev/noctalia-shell 2025-10-30 09:47:36 -04:00
ItsLemmy
916c2d67ea autofmt 2025-10-30 09:47:33 -04:00
Ly-sec
6ec3a61157 Merge branch 'main' of https://github.com/noctalia-dev/noctalia-shell 2025-10-30 14:38:13 +01:00
Ly-sec
2c0062390d SetupWizard: small dock layout fix 2025-10-30 14:38:10 +01:00
Lemmy
66db74eb71 Merge pull request #617 from MrDowntempo/feature/split-clocks
Feature/split clocks
2025-10-30 09:32:50 -04:00
Ly-sec
8a78d8cb34 Dock: behave the same as bar (shows on all displays if no display is selected)
DockTab: add Enable toggle (default true)
SetupDockStep: add Dock settings to setup wizard
SetupWizard: add SetupDockStep
i18n: add dock translations
2025-10-30 14:15:23 +01:00
Ly-sec
c0e5d7d419 SetupWallpaperStep: add scrollwheel support 2025-10-30 12:42:42 +01:00
Ly-sec
3dd02b8367 SettingsWindow: run program availability checks on open 2025-10-30 12:39:00 +01:00
Lysec
3c04fddcf1 Merge pull request #618 from MrDowntempo/feature/vicinae-appimage-fix
Feature/vicinae appimage fix
2025-10-30 08:09:41 +01:00
Lysec
890c86ac68 Merge pull request #615 from lonerOrz/fix/vicinae
fix(vicinae): Include vicinae into the hasEnabledTemplates() check.
2025-10-30 08:01:42 +01:00
Corey Woodworth
d4a73e05ee Fix: More robust search for Vicinae. Should match even if uses release name like Vicinae-6206ca757-x86_64.AppImage 2025-10-30 01:34:02 -04:00
Corey Woodworth
b57e77df9b Fix: Detect Vicinae, even if its an appimage 2025-10-30 00:47:53 -04:00
Corey Woodworth
ddb0b90ef7 Split secondHandColor into progressColor for DigitalClock so that it can be defined from CalendarPanel 2025-10-30 00:00:42 -04:00
Corey Woodworth
f39ea9e704 Change secondHandColor on LockScreen back to mPrimary 2025-10-29 23:28:22 -04:00
Corey Woodworth
9c66d64d85 Removed more useless comments 2025-10-29 22:53:56 -04:00
Corey Woodworth
475f4a6bda Removed commented out block 2025-10-29 22:51:59 -04:00
Corey Woodworth
376dedeb6f Move color logic to ClockLoader 2025-10-29 22:43:28 -04:00
Corey Woodworth
b3cddc1ede Lock Screen also uses this clock 2025-10-29 21:51:45 -04:00
ItsLemmy
f75a056550 Wallpaper: fix bug where folder would not be rescanned if they did not exists when noctalia started. 2025-10-29 21:25:39 -04:00
loner
0cf9de0fc4 fix(vicinae): Include vicinae into the hasEnabledTemplates() check. 2025-10-30 09:01:30 +08:00
Corey Woodworth
04d89905cf Split clocks out into multiple files 2025-10-29 21:00:15 -04:00
ItsLemmy
6fbbf38ffa Settings: refined the 12h clock display option to clearly explains it does not apply to the bar's clock. 2025-10-29 20:55:54 -04:00
ItsLemmy
3db3226b6f foot: fix theming when no config exists - foot does not create a config by default. 2025-10-29 20:47:55 -04:00
ItsLemmy
94d3ea9c94 Walker theming: replace custom theme injection code by the proper bash implementation. 2025-10-29 20:44:17 -04:00
Lysec
d171e81be1 Merge pull request #614 from damian-ds7/toggle-calendar-on-date-click
CalendarPanel: toggle panel after date click
2025-10-29 22:26:42 +01:00
Damian D'Souza
05ea9af4db CalendarPanel: toggle panel after date click 2025-10-29 21:25:01 +01:00
Ly-sec
10adaf955b AudioVisualizer: add setting to auto hide if no media is playing 2025-10-29 20:30:16 +01:00
Ly-sec
7fbfcfd9ef Set version to dev 2025-10-29 19:55:01 +01:00
70 changed files with 4869 additions and 1486 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -257,6 +257,10 @@
},
"dock": {
"title": "Dock",
"enabled": {
"label": "Dock aktivieren",
"description": "Dock vollständig anzeigen oder ausblenden"
},
"appearance": {
"section": {
"label": "Erscheinungsbild",
@@ -289,7 +293,7 @@
"monitors": {
"section": {
"label": "Monitor-Anzeige",
"description": "Monitor auswählen, auf dem das Dock angezeigt werden soll."
"description": "Dock auf bestimmten Monitoren anzeigen. Standardmäßig werden alle angezeigt, wenn keine ausgewählt wurden."
},
"only-same-output": {
"description": "Zeige nur Apps von dem Bildschirm, auf dem sich das Dock befindet.",
@@ -604,6 +608,10 @@
"pywalfox": {
"description": "Schreibt {filepath} und führt pywalfox update aus",
"description-missing": "Erfordert die Installation von {app}"
},
"code": {
"description": "Schreibe {Dateipfad}. Das Hyprluna-Theme muss manuell installiert und aktiviert werden.",
"description-missing": "Benötigt die Installation von {app}"
}
},
"misc": {
@@ -645,6 +653,10 @@
"fahrenheit": {
"label": "Temperatur in Fahrenheit (°F) anzeigen",
"description": "Temperatur in Fahrenheit statt Celsius anzeigen."
},
"show-in-calendar": {
"description": "Zeige die tägliche Wettervorhersage direkt in deiner Kalenderansicht an.",
"label": "Wetter im Kalender anzeigen"
}
},
"date-time": {
@@ -653,8 +665,8 @@
"description": "Anpassen, wie Datum und Zeit erscheinen."
},
"12hour-format": {
"description": "An für AM/PM-Format (z.B. 8:00 PM), aus für 24-Stunden-Format (z.B. 20:00).",
"label": "12-Stunden-Zeitformat benutzen"
"label": "12-Stunden-Zeitformat benutzen",
"description": "Zeigt die Uhrzeit im 12-Stunden-Format auf dem Sperrbildschirm und im Kalender an. Die Uhr in der Statusleiste hat eigene Einstellungen."
},
"week-numbers": {
"label": "Wochennummern anzeigen",
@@ -667,6 +679,11 @@
"use-analog": {
"description": "Eine Analoguhr auf dem Kalenderbildschirm anzeigen.",
"label": "Analoge Uhr verwenden"
},
"first-day-of-week": {
"automatic": "Automatisch (Systemgebietsschema verwenden)",
"description": "Wähle, welcher Tag die Woche im Kalender beginnen soll.",
"label": "Erster Tag der Woche"
}
}
},
@@ -838,10 +855,6 @@
"label": "Eckenradius",
"reset": "Rahmenradius zurücksetzen"
},
"dim-desktop": {
"description": "Den Desktop abdunkeln, wenn Bedienfelder oder Menüs geöffnet sind.",
"label": "Desktop abdunkeln"
},
"scaling": {
"description": "Ändert die Größe der allgemeinen Benutzeroberfläche, mit Ausnahme der Leiste.",
"label": "Oberflächenskalierung",
@@ -855,6 +868,10 @@
"tooltips": {
"description": "Tooltips in der gesamten Benutzeroberfläche aktivieren oder deaktivieren.",
"label": "Tooltips anzeigen"
},
"panels-attached-to-bar": {
"description": "Wenn aktiviert, werden die Panels mit einem schönen, umgekehrten Eckdesign an der Leiste befestigt.",
"label": "Paneele an Stange befestigen"
}
},
"lock-screen": {
@@ -1251,6 +1268,36 @@
"width": {
"label": "Breite",
"description": "Benutzerdefinierte Komponentenbreite."
},
"hide-when-idle": {
"label": "Ausblenden, wenn keine Medien wiedergegeben werden",
"description": "Wenn aktiviert, wird der Visualizer ausgeblendet, sofern keine Wiedergabe läuft."
}
},
"lock-keys": {
"show-caps-lock": {
"description": "Caps Lock Status anzeigen.",
"label": "Feststelltaste"
},
"show-num-lock": {
"description": "Num-Lock-Status anzeigen.",
"label": "Num-Taste"
},
"show-scroll-lock": {
"description": "Scroll-Lock-Status anzeigen.",
"label": "Rollenfeststelltaste"
},
"indicator-style": {
"circle": "Kreisbuchstaben",
"circle-dash": "Gestrichelte Kreisbuchstaben",
"circle-dot": "Punktierte Kreisbuchstaben",
"description": "Symbolstil für die Feststelltastenanzeigen",
"hex": "Sechseck-Buchstaben",
"label": "Indikatorstil",
"large": "Große Buchstaben",
"small": "Kleinbuchstaben",
"square": "Quadratische Buchstaben",
"square-round": "Abgerundete quadratische Buchstaben"
}
}
}
@@ -1372,8 +1419,6 @@
"click-to-start-recording": "Klicken zum Starten einer Bildschirmaufnahme",
"click-to-stop-recording": "Klicken zum Stoppen einer Bildschirmaufnahme",
"open-control-center": "Kontrollzentrum öffnen",
"volume-at": "Lautstärke bei {volume}%\nLinksklick zum Stumm-/Lautschalten. Rechtsklick für Einstellungen.\nScrollen zum Ändern der Lautstärke.",
"microphone-volume-at": "Mikrofon-Lautstärke bei {volume}%\nLinksklick zum Stumm-/Lautschalten. Rechtsklick für Einstellungen.\nScrollen zum Ändern der Lautstärke.",
"brightness-at": "Helligkeit: {brightness}%\nRechtsklick für Einstellungen.\nScrollen zum Ändern der Helligkeit.",
"manage-wifi": "WLAN verwalten",
"bluetooth-devices": "Bluetooth-Geräte",
@@ -1387,7 +1432,9 @@
"power-profile": "'{profile}' Energieprofil",
"keyboard-layout": "{layout} Tastaturlayout",
"output-muted": "Audio-Ausgabe stummschalten",
"input-muted": "Audio-Eingabe stummschalten"
"input-muted": "Audio-Eingabe stummschalten",
"microphone-volume-at": "Mikrofonlautstärke bei {volume}%.\nLinksklick für Einstellungen. Rechtsklick zum Stummschalten.\nScrollen zum Ändern der Lautstärke.",
"volume-at": "Lautstärke bei {volume}%.\nLinksklick für Einstellungen. Rechtsklick zum Stummschalten.\nScrollen zum Ändern der Lautstärke."
},
"clock": {
"tooltip": "Kalender öffnen"

View File

@@ -257,6 +257,10 @@
},
"dock": {
"title": "Dock",
"enabled": {
"label": "Enable dock",
"description": "Show or hide the dock entirely"
},
"appearance": {
"section": {
"label": "Appearance",
@@ -289,7 +293,7 @@
"monitors": {
"section": {
"label": "Monitor display",
"description": "Choose which monitor to display the dock on."
"description": "Show dock on specific monitors. Defaults to all if none are chosen."
},
"only-same-output": {
"label": "Only apps from same output",
@@ -602,12 +606,16 @@
"description-missing": "Requires {app} to be installed"
},
"discord": {
"description": "Write {filepath} for {client}",
"description": "Write {filepath} for {client}. Hyprluna theme needs to be activated manually.",
"description-missing": "No Discord client detected. Install vencord, vesktop, webcord, armcord, equibop, lightcord, or dorion."
},
"pywalfox": {
"description": "Write {filepath} and run pywalfox update",
"description-missing": "Requires {app} to be installed"
},
"code": {
"description": "Write {filepath}. Hyprluna theme needs to be installed and activated manually.",
"description-missing": "Requires {app} to be installed"
}
},
"misc": {
@@ -645,6 +653,10 @@
"fahrenheit": {
"label": "Display temperature in Fahrenheit (°F)",
"description": "Display temperature in Fahrenheit instead of Celsius."
},
"show-in-calendar": {
"label": "Display weather in calendar",
"description": "Show the daily weather forecast directly in your calendar view."
}
},
"date-time": {
@@ -654,12 +666,17 @@
},
"12hour-format": {
"label": "Use 12-hour time format",
"description": "On for AM/PM format (e.g., 8:00 PM), off for 24-hour format (e.g., 20:00)."
"description": "Displays time in 12-hour format on the lock screen and calendar. The bar clock has its own settings."
},
"week-numbers": {
"label": "Show week numbers",
"description": "Displays the week of the year (e.g., Week 38) in the calendar."
},
"first-day-of-week": {
"label": "First day of week",
"description": "Choose which day starts the week in the calendar.",
"automatic": "Automatic (use system locale)"
},
"show-events": {
"label": "Show calendar events",
"description": "Display events in the calendar panel."
@@ -834,10 +851,6 @@
"description": "Changes the size of the general user interface, excluding the bar.",
"reset-scaling": "Reset interface scaling"
},
"dim-desktop": {
"label": "Dim desktop",
"description": "Dim the desktop when panels or menus are open."
},
"border-radius": {
"label": "Border radius",
"description": "Controls the corner roundness of windows, buttons, and other elements.",
@@ -852,6 +865,10 @@
"label": "Disable UI Animations",
"description": "Disable all animations for a faster, more responsive experience."
},
"panels-attached-to-bar": {
"label": "Attach panels to bar",
"description": "When enabled, panels will be attached to the bar with beautiful inverted corner design"
},
"panels-overlay": {
"label": "Open panels in overlay layer",
"description": "Panels will appear above fullscreen windows"
@@ -1234,6 +1251,36 @@
"width": {
"label": "Width",
"description": "Custom component width."
},
"hide-when-idle": {
"label": "Hide when no media is playing",
"description": "When enabled, the visualizer is hidden unless a player is actively playing."
}
},
"lock-keys": {
"indicator-style": {
"label": "Indicator Style",
"description": "Icon style for the lock key indicators",
"large": "Large Letters",
"small": "Small Letters",
"square": "Square Letters",
"square-round": "Rounded Squre Letters",
"circle": "Circle Letters",
"circle-dash": "Dashed Circle Letters",
"circle-dot": "Dotted Circle Letters",
"hex": "Hexagon Letters"
},
"show-caps-lock": {
"label": "Caps Lock",
"description": "Display caps lock status."
},
"show-num-lock": {
"label": "Num Lock",
"description": "Display num lock status."
},
"show-scroll-lock": {
"label": "Scroll Lock",
"description": "Display scroll lock status."
}
}
}
@@ -1347,8 +1394,8 @@
"click-to-start-recording": "Click to start recording",
"click-to-stop-recording": "Click to stop recording",
"open-control-center": "Open control center",
"volume-at": "Volume at {volume}%\nLeft click to toggle mute. Right click for settings.\nScroll to modify volume.",
"microphone-volume-at": "Microphone volume at {volume}%\nLeft click to toggle mute. Right click for settings.\nScroll to modify volume.",
"volume-at": "Output volume at {volume}%\nLeft click for settings. Right click to toggle mute.\nScroll to modify volume.",
"microphone-volume-at": "Microphone volume at {volume}%\nLeft click for settings. Right click to toggle mute.\nScroll to modify volume.",
"brightness-at": "Brightness: {brightness}%\nRight click for settings.\nScroll to modify brightness.",
"manage-wifi": "Manage Wi-Fi",
"bluetooth-devices": "Bluetooth devices",

View File

@@ -257,6 +257,10 @@
},
"dock": {
"title": "Dock",
"enabled": {
"label": "Habilitar dock",
"description": "Mostrar u ocultar el dock por completo"
},
"appearance": {
"section": {
"label": "Apariencia",
@@ -289,7 +293,7 @@
"monitors": {
"section": {
"label": "Visualización en monitor",
"description": "Elige en qué monitor mostrar el dock."
"description": "Mostrar el dock en monitores específicos. Por defecto, se muestra en todos si no se elige ninguno."
},
"only-same-output": {
"description": "Mostrar solo las aplicaciones de la salida donde se encuentra el dock.",
@@ -604,6 +608,10 @@
"pywalfox": {
"description": "Escribir {filepath} y ejecutar pywalfox update",
"description-missing": "Requiere que {app} esté instalado"
},
"code": {
"description": "Escribe {filepath}. El tema Hyprluna debe ser instalado y activado manualmente.",
"description-missing": "Requiere que {app} esté instalado/a."
}
},
"misc": {
@@ -645,6 +653,10 @@
"fahrenheit": {
"label": "Mostrar temperatura en Fahrenheit (°F)",
"description": "Muestra la temperatura en Fahrenheit en lugar de Celsius."
},
"show-in-calendar": {
"description": "Muestra el pronóstico del tiempo diario directamente en la vista de tu calendario.",
"label": "Mostrar el clima en el calendario"
}
},
"date-time": {
@@ -653,8 +665,8 @@
"description": "Personaliza cómo aparecen la fecha y la hora."
},
"12hour-format": {
"description": "Activado para formato AM/PM (ej., 8:00 PM), desactivado para formato de 24 horas (ej., 20:00).",
"label": "Utilice el formato de hora de 12 horas"
"label": "Utilice el formato de hora de 12 horas",
"description": "Muestra la hora en formato de 12 horas en la pantalla de bloqueo y el calendario. El reloj de la barra tiene su propia configuración."
},
"week-numbers": {
"label": "Mostrar números de semana",
@@ -667,6 +679,11 @@
"use-analog": {
"description": "Mostrar un reloj de estilo analógico en la pantalla del calendario.",
"label": "Usar reloj de estilo analógico"
},
"first-day-of-week": {
"automatic": "Automático (usar la configuración regional del sistema)",
"description": "Elige qué día empieza la semana en el calendario.",
"label": "Primer día de la semana"
}
}
},
@@ -838,10 +855,6 @@
"label": "Radio de borde",
"reset": "Restablecer el radio del borde"
},
"dim-desktop": {
"description": "Atenuar el escritorio cuando los paneles o menús estén abiertos.",
"label": "Dim escritorio"
},
"scaling": {
"description": "Cambia el tamaño de la interfaz de usuario general, excluyendo la barra.",
"label": "Escalado de la interfaz",
@@ -855,6 +868,10 @@
"tooltips": {
"description": "Activar o desactivar los avisos emergentes en toda la interfaz.",
"label": "Mostrar sugerencias"
},
"panels-attached-to-bar": {
"description": "Cuando está habilitado, los paneles se adjuntarán a la barra con un hermoso diseño de esquina invertida.",
"label": "Adjuntar paneles a la barra"
}
},
"lock-screen": {
@@ -1234,6 +1251,36 @@
"width": {
"label": "Ancho",
"description": "Ancho del componente personalizado."
},
"hide-when-idle": {
"label": "Ocultar cuando no se reproduce",
"description": "Si está activado, el visualizador se oculta salvo que haya reproducción activa."
}
},
"lock-keys": {
"show-caps-lock": {
"description": "Mostrar el estado de Bloq Mayús.",
"label": "Bloq Mayús"
},
"show-num-lock": {
"description": "Mostrar el estado de bloqueo numérico.",
"label": "Bloq Num"
},
"show-scroll-lock": {
"description": "Mostrar el estado de Bloq Despl.",
"label": "Bloq Despl"
},
"indicator-style": {
"circle": "Letras circulares",
"circle-dash": "Letras de círculo discontinuo",
"circle-dot": "Letras de círculo punteado",
"description": "Estilo de icono para los indicadores de las teclas de bloqueo",
"hex": "Letras hexagonales",
"label": "Estilo del indicador",
"large": "Letras grandes",
"small": "Letras minúsculas",
"square": "Letras cuadradas",
"square-round": "Letras cuadradas redondeadas"
}
}
}
@@ -1355,8 +1402,6 @@
"click-to-start-recording": "Haz clic para iniciar la grabación",
"click-to-stop-recording": "Haz clic para detener la grabación",
"open-control-center": "Abrir el centro de control",
"volume-at": "Volumen al {volume}%\nClic izquierdo para silenciar. Clic derecho para configuración.\nDesplaza para modificar el volumen.",
"microphone-volume-at": "Volumen del micrófono al {volume}%\nClic izquierdo para silenciar. Clic derecho para configuración.\nDesplaza para modificar el volumen.",
"brightness-at": "Brillo: {brightness}%\nClic derecho para configuración.\nDesplaza para modificar el brillo.",
"manage-wifi": "Gestionar Wi-Fi",
"bluetooth-devices": "Dispositivos Bluetooth",
@@ -1370,7 +1415,9 @@
"power-profile": "Perfil de energía '{profile}'",
"keyboard-layout": "Distribución de teclado {layout}",
"output-muted": "Silenciar salida de audio",
"input-muted": "Silenciar entrada de audio"
"input-muted": "Silenciar entrada de audio",
"microphone-volume-at": "Volumen del micrófono al {volume}%.\nClic izquierdo para ajustes. Clic derecho para activar/desactivar el silencio.\nDesplázate para modificar el volumen.",
"volume-at": "Volumen de salida al {volume}%.\nClic izquierdo para ajustes. Clic derecho para activar/desactivar el silencio.\nDesplázate para modificar el volumen."
},
"clock": {
"tooltip": "Abrir calendario"

View File

@@ -257,6 +257,10 @@
},
"dock": {
"title": "Dock",
"enabled": {
"label": "Activer le dock",
"description": "Afficher ou masquer complètement le dock"
},
"appearance": {
"section": {
"label": "Apparence",
@@ -289,7 +293,7 @@
"monitors": {
"section": {
"label": "Affichage sur les moniteur",
"description": "Choisissez sur quels moniteurs afficher le dock."
"description": "Afficher le dock sur des écrans spécifiques. Par défaut, il s'affiche sur tous les écrans si aucun n'est sélectionné."
},
"only-same-output": {
"description": "Afficher uniquement les applications de la sortie où le dock est situé.",
@@ -604,6 +608,10 @@
"pywalfox": {
"description": "Écrire ~/.cache/wal/colors.json et exécuter pywalfox update",
"description-missing": "Nécessite que le paquet pywalfox soit installé"
},
"code": {
"description": "Écrire {filepath}. Le thème Hyprluna doit être installé et activé manuellement.",
"description-missing": "Nécessite l'installation de {app}"
}
},
"misc": {
@@ -645,6 +653,10 @@
"fahrenheit": {
"label": "Afficher la température en Fahrenheit (°F)",
"description": "Afficher la température en Fahrenheit au lieu de Celsius."
},
"show-in-calendar": {
"description": "Afficher les prévisions météo quotidiennes directement dans votre vue calendrier.",
"label": "Afficher la météo dans le calendrier"
}
},
"date-time": {
@@ -654,7 +666,7 @@
},
"12hour-format": {
"label": "Utiliser le format horaire de 12 heures",
"description": "Activé pour le format AM/PM (ex: 8:00 PM), désactivé pour le format 24 heures (ex: 20:00)."
"description": "Affiche l'heure au format 12 heures sur l'écran de verrouillage et dans le calendrier. L'horloge de la barre possède ses propres paramètres."
},
"week-numbers": {
"label": "Afficher les numéros de semaine",
@@ -667,6 +679,11 @@
"use-analog": {
"description": "Afficher une horloge de style analogique sur l'écran du calendrier.",
"label": "Utiliser une horloge de style analogique."
},
"first-day-of-week": {
"automatic": "Automatique (utiliser les paramètres régionaux du système)",
"description": "Choisissez quel jour commence la semaine dans le calendrier.",
"label": "Premier jour de la semaine"
}
}
},
@@ -838,10 +855,6 @@
"label": "Rayon de bordure",
"reset": "Réinitialiser le rayon de la bordure"
},
"dim-desktop": {
"description": "Atténuer le bureau lorsque des panneaux ou des menus sont ouverts.",
"label": "Dim bureau"
},
"scaling": {
"description": "Modifie la taille de l'interface utilisateur générale, à l'exception de la barre.",
"label": "Mise à l'échelle de l'interface",
@@ -855,6 +868,10 @@
"tooltips": {
"description": "Activer ou désactiver les info-bulles dans toute l'interface.",
"label": "Afficher les infobulles"
},
"panels-attached-to-bar": {
"description": "Lorsque cette option est activée, les panneaux seront attachés à la barre avec un design élégant de coin inversé.",
"label": "Fixer les panneaux à la barre."
}
},
"lock-screen": {
@@ -1234,6 +1251,36 @@
"width": {
"label": "Largeur",
"description": "Largeur personnalisée du composant."
},
"hide-when-idle": {
"label": "Masquer lorsqu'aucun média n'est en lecture",
"description": "Si activé, le visualiseur est masqué sauf lorsqu'un lecteur est en lecture."
}
},
"lock-keys": {
"show-caps-lock": {
"description": "Afficher l'état du verrouillage majuscule.",
"label": "Verr Maj"
},
"show-num-lock": {
"description": "Afficher l'état du verrouillage numérique.",
"label": "Verr Num"
},
"show-scroll-lock": {
"description": "Afficher l'état du verrouillage du défilement.",
"label": "Verr Maj"
},
"indicator-style": {
"circle": "Lettres circulaires",
"circle-dash": "Lettres en cercle pointillé",
"circle-dot": "Lettres en pointillés dans un cercle",
"description": "Style d'icône pour les indicateurs de la touche de verrouillage",
"hex": "Lettres hexagonales",
"label": "Style d'indicateur",
"large": "Grandes lettres",
"small": "Petites lettres",
"square": "Lettres carrées",
"square-round": "Lettres carrées arrondies"
}
}
}
@@ -1355,8 +1402,6 @@
"click-to-start-recording": "Cliquez pour démarrer l'enregistrement",
"click-to-stop-recording": "Cliquez pour arrêter l'enregistrement",
"open-control-center": "Ouvrir le centre de contrôle",
"volume-at": "Volume à {volume}%\nClic gauche pour couper/rétablir le son. Clic droit pour les paramètres.\nFaites défiler pour modifier le volume.",
"microphone-volume-at": "Volume du microphone à {volume}%\nClic gauche pour couper/rétablir le son. Clic droit pour les paramètres.\nFaites défiler pour modifier le volume.",
"brightness-at": "Luminosité : {brightness}%\nClic droit pour les paramètres.\nFaites défiler pour modifier la luminosité.",
"manage-wifi": "Gérer le Wi-Fi",
"bluetooth-devices": "Appareils Bluetooth",
@@ -1370,7 +1415,9 @@
"power-profile": "Profil d'alimentation '{profile}'",
"keyboard-layout": "Disposition du clavier {layout}",
"output-muted": "Couper la sortie audio",
"input-muted": "Couper l'entrée audio"
"input-muted": "Couper l'entrée audio",
"microphone-volume-at": "Volume du microphone à {volume}%.\nClic gauche pour les paramètres. Clic droit pour activer/désactiver le mode muet.\nFaites défiler pour modifier le volume.",
"volume-at": "Volume de sortie à {volume}%.\nClic gauche pour les paramètres. Clic droit pour activer/désactiver le mode muet.\nFaites défiler pour modifier le volume."
},
"clock": {
"tooltip": "Ouvrir le calendrier"

View File

@@ -257,6 +257,10 @@
},
"dock": {
"title": "Dock",
"enabled": {
"label": "Ativar dock",
"description": "Mostrar ou ocultar o dock completamente"
},
"appearance": {
"section": {
"label": "Aparência",
@@ -289,7 +293,7 @@
"monitors": {
"section": {
"label": "Exibição no monitor",
"description": "Escolha em qual monitor exibir a dock."
"description": "Mostrar dock em monitores específicos. O padrão é todos se nenhum for escolhido."
},
"only-same-output": {
"description": "Mostrar apenas aplicativos da saída onde o dock está localizado.",
@@ -566,6 +570,10 @@
"pywalfox": {
"description": "Escrever {filepath} e executar pywalfox update",
"description-missing": "Requer que o {app} esteja instalado"
},
"code": {
"description": "Escreva em {filepath}. O tema Hyprluna precisa ser instalado e ativado manualmente.",
"description-missing": "Requer que o {app} esteja instalado."
}
},
"misc": {
@@ -607,6 +615,10 @@
"fahrenheit": {
"label": "Exibir temperatura em Fahrenheit (°F)",
"description": "Exibe a temperatura em Fahrenheit em vez de Celsius."
},
"show-in-calendar": {
"description": "Mostre a previsão do tempo diária diretamente na sua visualização de calendário.",
"label": "Exibir clima no calendário"
}
},
"date-time": {
@@ -615,8 +627,8 @@
"description": "Personalize como a data e a hora aparecem."
},
"12hour-format": {
"description": "Ativado para formato AM/PM (ex., 8:00 PM), desativado para formato de 24 horas (ex., 20:00).",
"label": "Use o formato de hora de 12 horas"
"label": "Use o formato de hora de 12 horas",
"description": "Exibe a hora no formato de 12 horas na tela de bloqueio e no calendário. O relógio da barra tem suas próprias configurações."
},
"week-numbers": {
"label": "Mostrar números da semana",
@@ -629,6 +641,11 @@
"use-analog": {
"description": "Mostrar um relógio estilo analógico na tela do calendário.",
"label": "Use um relógio de estilo analógico."
},
"first-day-of-week": {
"automatic": "Automático (usar localização do sistema)",
"description": "Escolha qual dia começa a semana no calendário.",
"label": "Primeiro dia da semana"
}
}
},
@@ -838,10 +855,6 @@
"label": "Raio da borda",
"reset": "Redefinir raio da borda"
},
"dim-desktop": {
"description": "Escurecer a área de trabalho quando painéis ou menus estiverem abertos.",
"label": "Dim área de trabalho"
},
"scaling": {
"description": "Altera o tamanho da interface geral do usuário, excluindo a barra.",
"label": "Escalonamento da interface",
@@ -855,6 +868,10 @@
"tooltips": {
"description": "Ativar ou desativar dicas de ferramentas em toda a interface.",
"label": "Mostrar dicas de ferramenta"
},
"panels-attached-to-bar": {
"description": "Quando ativado, os painéis serão anexados à barra com um belo design de canto invertido.",
"label": "Anexar painéis à barra"
}
},
"lock-screen": {
@@ -1234,6 +1251,36 @@
"width": {
"label": "Largura",
"description": "Largura do componente personalizado."
},
"hide-when-idle": {
"label": "Ocultar quando não houver reprodução",
"description": "Quando ativado, o visualizador fica oculto a menos que haja reprodução ativa."
}
},
"lock-keys": {
"show-caps-lock": {
"description": "Exibir o status do Caps Lock.",
"label": "Caps Lock"
},
"show-num-lock": {
"description": "Exibir o status do Num Lock.",
"label": "Bloq Num"
},
"show-scroll-lock": {
"description": "Exibir o status do Scroll Lock.",
"label": "Scroll Lock"
},
"indicator-style": {
"circle": "Letras Circulares",
"circle-dash": "Letras em Círculo Tracejadas",
"circle-dot": "Letras de Círculo Pontilhado",
"description": "Estilo de ícone para os indicadores da tecla de bloqueio.",
"hex": "Letras Hexagonais",
"label": "Estilo do Indicador",
"large": "Letras grandes",
"small": "Letras minúsculas",
"square": "Letras Quadradas",
"square-round": "Letras Quadradas Arredondadas"
}
}
}
@@ -1355,8 +1402,6 @@
"click-to-start-recording": "Clique para iniciar a gravação",
"click-to-stop-recording": "Clique para parar a gravação",
"open-control-center": "Abrir central de controle",
"volume-at": "Volume em {volume}%\nClique esquerdo para silenciar. Clique direito para configurações.\nRole para modificar o volume.",
"microphone-volume-at": "Volume do microfone em {volume}%\nClique esquerdo para silenciar. Clique direito para configurações.\nRole para modificar o volume.",
"brightness-at": "Brilho: {brightness}%\nClique direito para configurações.\nRole para modificar o brilho.",
"manage-wifi": "Gerenciar Wi-Fi",
"bluetooth-devices": "Dispositivos Bluetooth",
@@ -1370,7 +1415,9 @@
"power-profile": "Perfil de energia '{profile}'",
"keyboard-layout": "Layout de teclado {layout}",
"output-muted": "Silenciar saída de áudio",
"input-muted": "Silenciar entrada de áudio"
"input-muted": "Silenciar entrada de áudio",
"microphone-volume-at": "Volume do microfone em {volume}%.\nClique esquerdo para configurações. Clique direito para ativar/desativar o mudo.\nRole para modificar o volume.",
"volume-at": "Volume de saída em {volume}%.\nClique esquerdo para configurações. Clique direito para alternar o mudo.\nRole para modificar o volume."
},
"clock": {
"tooltip": "Abrir calendário"

View File

@@ -257,6 +257,10 @@
},
"dock": {
"title": "Dock",
"enabled": {
"label": "启用 Dock",
"description": "完全显示或隐藏 Dock"
},
"appearance": {
"section": {
"label": "外观",
@@ -289,7 +293,7 @@
"monitors": {
"section": {
"label": "显示器显示",
"description": "选择在哪个显示器上显示 Dock。"
"description": "在特定显示器上显示 Dock。如果未选择任何显示器,则默认为全部。"
},
"only-same-output": {
"description": "仅显示输出结果中dock所在位置的应用。",
@@ -604,6 +608,10 @@
"pywalfox": {
"description": "写入 {filepath} 并运行 pywalfox update",
"description-missing": "需要安装 {app}"
},
"code": {
"description": "写入 {filepath}。Hyprluna 主题需要手动安装和激活。",
"description-missing": "需要安装 {app}"
}
},
"misc": {
@@ -645,6 +653,10 @@
"fahrenheit": {
"label": "以华氏度显示温度 (°F)",
"description": "以华氏度而非摄氏度显示温度。"
},
"show-in-calendar": {
"description": "直接在您的日历视图中显示每日天气预报。",
"label": "在日历中显示天气"
}
},
"date-time": {
@@ -653,8 +665,8 @@
"description": "自定义日期和时间的显示方式。"
},
"12hour-format": {
"description": "开启为 AM/PM 格式例如8:00 PM关闭为 24 小时制例如20:00",
"label": "使用12小时制时间格式"
"label": "使用12小时制时间格式",
"description": "在锁屏和日历上以 12 小时制格式显示时间。状态栏时钟有其自己的设置。"
},
"week-numbers": {
"label": "显示周数",
@@ -667,6 +679,11 @@
"use-analog": {
"description": "在日历屏幕上显示一个模拟时钟。",
"label": "使用模拟时钟样式"
},
"first-day-of-week": {
"automatic": "自动(使用系统区域设置)",
"description": "选择日历中一周的起始日。",
"label": "一周的第一天"
}
}
},
@@ -838,10 +855,6 @@
"label": "边框半径",
"reset": "重置边框半径"
},
"dim-desktop": {
"description": "当面板或菜单打开时,桌面变暗。",
"label": "昏暗的桌面"
},
"scaling": {
"description": "更改通用用户界面大小,不包括栏。",
"label": "界面缩放",
@@ -855,6 +868,10 @@
"tooltips": {
"description": "启用或禁用整个界面的工具提示。",
"label": "显示工具提示"
},
"panels-attached-to-bar": {
"description": "启用后,面板将以美观的倒角设计附加到栏上。",
"label": "将面板连接到杆上"
}
},
"lock-screen": {
@@ -1234,6 +1251,36 @@
"width": {
"label": "宽度",
"description": "自定义组件的宽度。"
},
"hide-when-idle": {
"label": "无媒体播放时隐藏",
"description": "启用后,除非正在播放媒体,否则隐藏可视化显示。"
}
},
"lock-keys": {
"show-caps-lock": {
"description": "显示大写锁定状态。",
"label": "大写锁定"
},
"show-num-lock": {
"description": "显示数字锁定键状态。",
"label": "数字锁定"
},
"show-scroll-lock": {
"description": "显示滚动锁定状态。",
"label": "滚动锁定"
},
"indicator-style": {
"hex": "六边形字母",
"large": "大写字母",
"small": "小写字母",
"square": "方块字",
"square-round": "圆角方形字母",
"circle": "圆圈字母",
"circle-dash": "虚线圆圈字母",
"circle-dot": "虚线圆圈字母",
"description": "锁键指示器的图标样式",
"label": "指标样式"
}
}
}
@@ -1355,8 +1402,6 @@
"click-to-start-recording": "点击开始录制",
"click-to-stop-recording": "点击停止录制",
"open-control-center": "打开控制中心",
"volume-at": "音量 {volume}%\n左键点击切换静音。右键点击进入设置。\n滚动调整音量。",
"microphone-volume-at": "麦克风音量 {volume}%\n左键点击切换静音。右键点击进入设置。\n滚动调整音量。",
"brightness-at": "亮度:{brightness}%\n右键点击进入设置。\n滚动调整亮度。",
"manage-wifi": "管理 Wi-Fi",
"bluetooth-devices": "蓝牙设备",
@@ -1370,7 +1415,9 @@
"power-profile": "'{profile}' 电源模式",
"keyboard-layout": "{layout} 键盘布局",
"output-muted": "静音输出设备",
"input-muted": "静音输入设备"
"input-muted": "静音输入设备",
"microphone-volume-at": "麦克风音量 {volume}%\n左键点击进入设置。右键点击切换静音。\n滚动滚轮调节音量。",
"volume-at": "音量设为 {volume}%\n\n左键点击进入设置。右键点击切换静音。\n\n滚动滚轮调节音量。"
},
"clock": {
"tooltip": "打开日历"

View File

@@ -57,7 +57,6 @@
},
"general": {
"avatarImage": "",
"dimDesktop": true,
"showScreenCorners": false,
"forceBlackScreenCorners": false,
"scaleRatio": 1,
@@ -76,7 +75,9 @@
"use12hourFormat": false,
"showWeekNumberInCalendar": false,
"showCalendarEvents": true,
"analogClockInCalendar": false
"showCalendarWeather": true,
"analogClockInCalendar": false,
"firstDayOfWeek": -1
},
"screenRecorder": {
"directory": "",
@@ -172,6 +173,7 @@
]
},
"dock": {
"enabled": true,
"displayMode": "always_visible",
"backgroundOpacity": 1,
"floatingRatio": 1,
@@ -216,6 +218,7 @@
"fontDefaultScale": 1,
"fontFixedScale": 1,
"tooltipsEnabled": true,
"panelsAttachedToBar": true,
"panelsOverlayLayer": true
},
"brightness": {
@@ -250,6 +253,7 @@
"pywalfox": false,
"vicinae": false,
"walker": false,
"code": false,
"enableUserTemplates": false
},
"nightLight": {

View File

@@ -5,7 +5,7 @@
if [ "$#" -ne 1 ]; then
# Print usage information to standard error.
echo "Error: No application specified." >&2
echo "Usage: $0 {kitty|ghostty|foot|fuzzel|pywalfox}" >&2
echo "Usage: $0 {kitty|ghostty|foot|fuzzel|walker|pywalfox}" >&2
exit 1
fi
@@ -43,8 +43,18 @@ case "$APP_NAME" in
echo "🎨 Applying 'noctalia' theme to foot..."
CONFIG_FILE="$HOME/.config/foot/foot.ini"
# Check if the config file exists before trying to modify it.
if [ -f "$CONFIG_FILE" ]; then
# Check if the config file exists, create it if it doesn't.
if [ ! -f "$CONFIG_FILE" ]; then
echo "Config file not found, creating $CONFIG_FILE..."
# Create the config directory if it doesn't exist
mkdir -p "$(dirname "$CONFIG_FILE")"
# Create the config file with the noctalia theme
cat > "$CONFIG_FILE" << 'EOF'
[main]
include=~/.config/foot/themes/noctalia
EOF
echo "Created new config file with noctalia theme."
else
# Check if theme is already set to noctalia
if grep -q "include=~/.config/foot/themes/noctalia" "$CONFIG_FILE"; then
echo "Theme already set to noctalia, skipping modification."
@@ -59,9 +69,6 @@ case "$APP_NAME" in
sed -i '1i [main]\ninclude=~/.config/foot/themes/noctalia\n' "$CONFIG_FILE"
fi
fi
else
echo "Error: foot config file not found at $CONFIG_FILE" >&2
exit 1
fi
;;
@@ -86,6 +93,29 @@ case "$APP_NAME" in
fi
;;
walker)
echo "🎨 Applying 'noctalia' theme to walker..."
CONFIG_FILE="$HOME/.config/walker/config.toml"
# Check if the config file exists.
if [ -f "$CONFIG_FILE" ]; then
# Check if theme is already set to noctalia
if grep -q '^theme = "noctalia"' "$CONFIG_FILE"; then
echo "Theme already set to noctalia, skipping modification."
else
# Check if a theme line exists and replace it, otherwise append
if grep -q '^theme = ' "$CONFIG_FILE"; then
sed -i 's/^theme = .*/theme = "noctalia"/' "$CONFIG_FILE"
else
echo 'theme = "noctalia"' >> "$CONFIG_FILE"
fi
fi
else
echo "Error: walker config file not found at $CONFIG_FILE" >&2
exit 1
fi
;;
vicinae)
echo "🎨 Applying 'matugen' theme to vicinae..."
# Apply the theme

View File

@@ -10,6 +10,7 @@ Singleton {
property bool isLoaded: false
property string langCode: ""
property var locale: Qt.locale()
property string systemDetectedLangCode: ""
property var availableLanguages: []
property var translations: ({})
@@ -198,6 +199,7 @@ Singleton {
function setLanguage(newLangCode) {
if (newLangCode !== langCode && availableLanguages.includes(newLangCode)) {
langCode = newLangCode
locale = Qt.locale(langCode)
Logger.i("I18n", `Language set to "${langCode}"`)
languageChanged(langCode)
loadTranslations()

View File

@@ -55,7 +55,7 @@ Singleton {
// Then it should be commented out again, regular users don't need to generate
// default settings on every start
// TODO: automate this someday!
// generateDefaultSettings()
//generateDefaultSettings()
// Patch-in the local default, resolved to user's home
adapter.general.avatarImage = defaultAvatar
@@ -182,7 +182,6 @@ Singleton {
// general
property JsonObject general: JsonObject {
property string avatarImage: ""
property bool dimDesktop: true
property bool showScreenCorners: false
property bool forceBlackScreenCorners: false
property real scaleRatio: 1.0
@@ -203,7 +202,9 @@ Singleton {
property bool use12hourFormat: false
property bool showWeekNumberInCalendar: false
property bool showCalendarEvents: true
property bool showCalendarWeather: true
property bool analogClockInCalendar: false
property int firstDayOfWeek: -1 // -1 = auto (use locale), 0 = Sunday, 1 = Monday, 6 = Saturday
}
// screen recorder
@@ -296,6 +297,7 @@ Singleton {
// dock
property JsonObject dock: JsonObject {
property bool enabled: true
property string displayMode: "always_visible" // "always_visible", "auto_hide", "exclusive"
property real backgroundOpacity: 1.0
property real floatingRatio: 1.0
@@ -351,6 +353,7 @@ Singleton {
property real fontDefaultScale: 1.0
property real fontFixedScale: 1.0
property bool tooltipsEnabled: true
property bool panelsAttachedToBar: true
property bool panelsOverlayLayer: true
}
@@ -390,6 +393,7 @@ Singleton {
property bool pywalfox: false
property bool vicinae: false
property bool walker: false
property bool code: false
property bool enableUserTemplates: false
}

View File

@@ -7,7 +7,7 @@ import qs.Services
import qs.Widgets
Loader {
active: Settings.data.general.showScreenCorners
active: Settings.data.general.showScreenCorners && (!Settings.data.ui.panelsAttachedToBar || Settings.data.bar.backgroundOpacity >= 1 || Settings.data.bar.floating)
sourceComponent: Variants {
model: Quickshell.screens

View File

@@ -63,54 +63,57 @@ NPanel {
spacing: Style.marginM
// HEADER
RowLayout {
NBox {
Layout.fillWidth: true
spacing: Style.marginM
implicitHeight: headerRow.implicitHeight + (Style.marginM * 2)
NIcon {
icon: "settings-audio"
pointSize: Style.fontSizeXXL
color: Color.mPrimary
}
RowLayout {
id: headerRow
anchors.fill: parent
anchors.margins: Style.marginM
spacing: Style.marginM
NText {
text: I18n.tr("settings.audio.title")
pointSize: Style.fontSizeL
font.weight: Style.fontWeightBold
color: Color.mOnSurface
Layout.fillWidth: true
}
NIcon {
icon: "settings-audio"
pointSize: Style.fontSizeXXL
color: Color.mPrimary
}
NIconButton {
icon: AudioService.getOutputIcon()
tooltipText: I18n.tr("tooltips.output-muted")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
AudioService.setOutputMuted(!AudioService.muted)
NText {
text: I18n.tr("settings.audio.title")
pointSize: Style.fontSizeL
font.weight: Style.fontWeightBold
color: Color.mOnSurface
Layout.fillWidth: true
}
NIconButton {
icon: AudioService.getOutputIcon()
tooltipText: I18n.tr("tooltips.output-muted")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
AudioService.setOutputMuted(!AudioService.muted)
}
}
NIconButton {
icon: AudioService.getInputIcon()
tooltipText: I18n.tr("tooltips.input-muted")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
AudioService.setInputMuted(!AudioService.inputMuted)
}
}
NIconButton {
icon: "close"
tooltipText: I18n.tr("tooltips.close")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
root.close()
}
}
}
NIconButton {
icon: AudioService.getInputIcon()
tooltipText: I18n.tr("tooltips.input-muted")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
AudioService.setInputMuted(!AudioService.inputMuted)
}
}
NIconButton {
icon: "close"
tooltipText: I18n.tr("tooltips.close")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
root.close()
}
}
}
NDivider {
Layout.fillWidth: true
}
NScrollView {
@@ -123,8 +126,8 @@ NPanel {
// AudioService Devices
ColumnLayout {
spacing: Style.marginS
Layout.fillWidth: true
spacing: Style.marginM
width: parent.width
// -------------------------------
// Output Devices
@@ -132,26 +135,27 @@ NPanel {
id: sinks
}
ColumnLayout {
spacing: 0
NBox {
Layout.fillWidth: true
Layout.bottomMargin: Style.marginL
Layout.preferredHeight: outputColumn.implicitHeight + (Style.marginM * 2)
RowLayout {
spacing: Style.spacingM * Style.uiScaling
Layout.bottomMargin: Style.marginL
ColumnLayout {
id: outputColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: Style.marginM
spacing: Style.marginS
NText {
text: I18n.tr("settings.audio.devices.output-device.label")
pointSize: Style.fontSizeL
color: Color.mPrimary
Layout.preferredWidth: root.preferredWidth * 0.3
}
// Output Volume Slider
NValueSlider {
Layout.fillWidth: true
Layout.maximumWidth: root.preferredWidth * 0.6
from: 0
to: Settings.data.audio.volumeOverdrive ? 1.5 : 1.0
value: localOutputVolume
@@ -160,55 +164,55 @@ NPanel {
onMoved: value => localOutputVolume = value
onPressedChanged: (pressed, value) => localOutputVolumeChanging = pressed
text: Math.round(localOutputVolume * 100) + "%"
Layout.bottomMargin: Style.marginM
}
}
Repeater {
model: AudioService.sinks
NRadioButton {
ButtonGroup.group: sinks
required property PwNode modelData
pointSize: Style.fontSizeS
text: modelData.description
checked: AudioService.sink?.id === modelData.id
onClicked: {
AudioService.setAudioSink(modelData)
localOutputVolume = AudioService.volume
Repeater {
model: AudioService.sinks
NRadioButton {
ButtonGroup.group: sinks
required property PwNode modelData
pointSize: Style.fontSizeS
text: modelData.description
checked: AudioService.sink?.id === modelData.id
onClicked: {
AudioService.setAudioSink(modelData)
localOutputVolume = AudioService.volume
}
Layout.fillWidth: true
}
Layout.fillWidth: true
}
}
}
NDivider {
Layout.fillWidth: true
}
// -------------------------------
// Input Devices
ButtonGroup {
id: sources
}
ColumnLayout {
spacing: 0
NBox {
Layout.fillWidth: true
Layout.preferredHeight: inputColumn.implicitHeight + (Style.marginM * 2)
RowLayout {
spacing: Style.spacingM * Style.uiScaling
Layout.bottomMargin: Style.marginL
ColumnLayout {
id: inputColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.margins: Style.marginM
spacing: Style.marginS
NText {
text: I18n.tr("settings.audio.devices.input-device.label")
pointSize: Style.fontSizeL
color: Color.mPrimary
Layout.preferredWidth: root.preferredWidth * 0.3
}
// Input Volume Slider
NValueSlider {
Layout.fillWidth: true
Layout.maximumWidth: root.preferredWidth * 0.6
from: 0
to: Settings.data.audio.volumeOverdrive ? 1.5 : 1.0
value: localInputVolume
@@ -217,23 +221,24 @@ NPanel {
onMoved: value => localInputVolume = value
onPressedChanged: (pressed, value) => localInputVolumeChanging = pressed
text: Math.round(localInputVolume * 100) + "%"
Layout.bottomMargin: Style.marginM
}
}
Repeater {
model: AudioService.sources
//Layout.fillWidth: true
NRadioButton {
ButtonGroup.group: sources
required property PwNode modelData
pointSize: Style.fontSizeS
text: modelData.description
checked: AudioService.source?.id === modelData.id
onClicked: AudioService.setAudioSource(modelData)
Layout.fillWidth: true
Repeater {
model: AudioService.sources
NRadioButton {
ButtonGroup.group: sources
required property PwNode modelData
pointSize: Style.fontSizeS
text: modelData.description
checked: AudioService.source?.id === modelData.id
onClicked: AudioService.setAudioSource(modelData)
Layout.fillWidth: true
}
}
}
}
Item {
Layout.fillHeight: true
}

View File

@@ -34,59 +34,62 @@ NPanel {
updateOptionsModel()
}
panelContent: Rectangle {
color: Color.transparent
panelContent: Item {
anchors.fill: parent
ColumnLayout {
anchors.fill: parent
anchors.margins: Style.marginL
anchors.margins: Style.marginM
spacing: Style.marginM
// HEADER
RowLayout {
NBox {
Layout.fillWidth: true
spacing: Style.marginM
Layout.preferredHeight: header.implicitHeight + Style.marginM * 2
NText {
text: I18n.tr("battery.panel.title")
pointSize: Style.fontSizeL
font.weight: Style.fontWeightBold
color: Color.mOnSurface
Layout.fillWidth: true
}
RowLayout {
id: header
anchors.fill: parent
anchors.margins: Style.marginM
spacing: Style.marginM
NToggle {
id: batteryManagerSwitch
checked: BatteryService.chargingMode !== BatteryService.ChargingMode.Disabled
onToggled: checked => BatteryService.toggleEnabled(checked)
baseSize: Style.baseWidgetSize * 0.65
}
NText {
text: I18n.tr("battery.panel.title")
pointSize: Style.fontSizeL
font.weight: Style.fontWeightBold
color: Color.mOnSurface
Layout.fillWidth: true
}
NIconButton {
icon: "close"
tooltipText: I18n.tr("tooltips.close")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
root.close()
NToggle {
id: batteryManagerSwitch
checked: BatteryService.chargingMode !== BatteryService.ChargingMode.Disabled
onToggled: checked => BatteryService.toggleEnabled(checked)
baseSize: Style.baseWidgetSize * 0.65
}
NIconButton {
icon: "close"
tooltipText: I18n.tr("tooltips.close")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
root.close()
}
}
}
}
NDivider {
Layout.fillWidth: true
}
ButtonGroup {
id: batteryGroup
}
Rectangle {
NBox {
Layout.fillWidth: true
Layout.fillHeight: true
color: Color.transparent
ColumnLayout {
anchors.fill: parent
anchors.margins: Style.marginM
spacing: Style.marginM
Repeater {

View File

@@ -8,7 +8,7 @@ import qs.Commons
import qs.Services
import qs.Widgets
ColumnLayout {
NBox {
id: root
property string label: ""
@@ -18,161 +18,170 @@ ColumnLayout {
}
Layout.fillWidth: true
spacing: Style.marginM
Layout.preferredHeight: column.implicitHeight + Style.marginM * 2
NText {
text: root.label
pointSize: Style.fontSizeL
color: Color.mSecondary
font.weight: Style.fontWeightMedium
Layout.fillWidth: true
visible: root.model.length > 0
}
ColumnLayout {
id: column
anchors.fill: parent
anchors.margins: Style.marginM
Repeater {
id: deviceList
Layout.fillWidth: true
model: root.model
visible: BluetoothService.adapter && BluetoothService.adapter.enabled
Rectangle {
id: device
readonly property bool canConnect: BluetoothService.canConnect(modelData)
readonly property bool canDisconnect: BluetoothService.canDisconnect(modelData)
readonly property bool isBusy: BluetoothService.isDeviceBusy(modelData)
function getContentColor(defaultColor = Color.mOnSurface) {
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
return Color.mPrimary
if (modelData.blocked)
return Color.mError
return defaultColor
}
spacing: Style.marginM
NText {
text: root.label
pointSize: Style.fontSizeL
color: Color.mSecondary
font.weight: Style.fontWeightMedium
visible: root.model.length > 0
Layout.fillWidth: true
Layout.preferredHeight: deviceLayout.implicitHeight + (Style.marginM * 2)
radius: Style.radiusM
color: Color.mSurface
border.width: Style.borderS
border.color: getContentColor(Color.mOutline)
Layout.leftMargin: Style.marginM
}
RowLayout {
id: deviceLayout
anchors.fill: parent
anchors.margins: Style.marginM
spacing: Style.marginM
Layout.alignment: Qt.AlignVCenter
Repeater {
id: deviceList
Layout.fillWidth: true
model: root.model
visible: BluetoothService.adapter && BluetoothService.adapter.enabled
// One device BT icon
NIcon {
icon: BluetoothService.getDeviceIcon(modelData)
pointSize: Style.fontSizeXXL
color: getContentColor(Color.mOnSurface)
Layout.alignment: Qt.AlignVCenter
Rectangle {
id: device
readonly property bool canConnect: BluetoothService.canConnect(modelData)
readonly property bool canDisconnect: BluetoothService.canDisconnect(modelData)
readonly property bool isBusy: BluetoothService.isDeviceBusy(modelData)
function getContentColor(defaultColor = Color.mOnSurface) {
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
return Color.mPrimary
if (modelData.blocked)
return Color.mError
return defaultColor
}
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXXS
Layout.fillWidth: true
Layout.preferredHeight: deviceLayout.implicitHeight + (Style.marginM * 2)
radius: Style.radiusM
color: Color.mSurface
border.width: Style.borderS
border.color: getContentColor(Color.mOutline)
// Device name
NText {
text: modelData.name || modelData.deviceName
pointSize: Style.fontSizeM
font.weight: Style.fontWeightMedium
elide: Text.ElideRight
RowLayout {
id: deviceLayout
anchors.fill: parent
anchors.margins: Style.marginM
spacing: Style.marginM
Layout.alignment: Qt.AlignVCenter
// One device BT icon
NIcon {
icon: BluetoothService.getDeviceIcon(modelData)
pointSize: Style.fontSizeXXL
color: getContentColor(Color.mOnSurface)
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
}
// Status
NText {
text: BluetoothService.getStatusString(modelData)
visible: text !== ""
pointSize: Style.fontSizeXS
color: getContentColor(Color.mOnSurfaceVariant)
}
// Signal Strength
RowLayout {
visible: modelData.signalStrength !== undefined
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXS
spacing: Style.marginXXS
// Device signal strength - "Unknown" when not connected
// Device name
NText {
text: BluetoothService.getSignalStrength(modelData)
text: modelData.name || modelData.deviceName
pointSize: Style.fontSizeM
font.weight: Style.fontWeightMedium
elide: Text.ElideRight
color: getContentColor(Color.mOnSurface)
Layout.fillWidth: true
}
// Status
NText {
text: BluetoothService.getStatusString(modelData)
visible: text !== ""
pointSize: Style.fontSizeXS
color: getContentColor(Color.mOnSurfaceVariant)
}
NIcon {
visible: modelData.signalStrength > 0 && !modelData.pairing && !modelData.blocked
icon: BluetoothService.getSignalIcon(modelData)
pointSize: Style.fontSizeXS
color: getContentColor(Color.mOnSurface)
// Signal Strength
RowLayout {
visible: modelData.signalStrength !== undefined
Layout.fillWidth: true
spacing: Style.marginXS
// Device signal strength - "Unknown" when not connected
NText {
text: BluetoothService.getSignalStrength(modelData)
pointSize: Style.fontSizeXS
color: getContentColor(Color.mOnSurfaceVariant)
}
NIcon {
visible: modelData.signalStrength > 0 && !modelData.pairing && !modelData.blocked
icon: BluetoothService.getSignalIcon(modelData)
pointSize: Style.fontSizeXS
color: getContentColor(Color.mOnSurface)
}
NText {
visible: modelData.signalStrength > 0 && !modelData.pairing && !modelData.blocked
text: (modelData.signalStrength !== undefined && modelData.signalStrength > 0) ? modelData.signalStrength + "%" : ""
pointSize: Style.fontSizeXS
color: getContentColor(Color.mOnSurface)
}
}
// Battery
NText {
visible: modelData.signalStrength > 0 && !modelData.pairing && !modelData.blocked
text: (modelData.signalStrength !== undefined && modelData.signalStrength > 0) ? modelData.signalStrength + "%" : ""
visible: modelData.batteryAvailable
text: BluetoothService.getBattery(modelData)
pointSize: Style.fontSizeXS
color: getContentColor(Color.mOnSurface)
color: getContentColor(Color.mOnSurfaceVariant)
}
}
// Battery
NText {
visible: modelData.batteryAvailable
text: BluetoothService.getBattery(modelData)
pointSize: Style.fontSizeXS
color: getContentColor(Color.mOnSurfaceVariant)
// Spacer to push connect button to the right
Item {
Layout.fillWidth: true
}
}
// Spacer to push connect button to the right
Item {
Layout.fillWidth: true
}
// Call to action
NButton {
id: button
visible: (modelData.state !== BluetoothDeviceState.Connecting)
enabled: (canConnect || canDisconnect) && !isBusy
outlined: !button.hovered
fontSize: Style.fontSizeXS
fontWeight: Style.fontWeightMedium
backgroundColor: {
if (device.canDisconnect && !isBusy) {
return Color.mError
// Call to action
NButton {
id: button
visible: (modelData.state !== BluetoothDeviceState.Connecting)
enabled: (canConnect || canDisconnect) && !isBusy
outlined: !button.hovered
fontSize: Style.fontSizeXS
fontWeight: Style.fontWeightMedium
backgroundColor: {
if (device.canDisconnect && !isBusy) {
return Color.mError
}
return Color.mPrimary
}
return Color.mPrimary
}
tooltipText: root.tooltipText
text: {
if (modelData.pairing) {
return "Pairing..."
tooltipText: root.tooltipText
text: {
if (modelData.pairing) {
return "Pairing..."
}
if (modelData.blocked) {
return "Blocked"
}
if (modelData.connected) {
return "Disconnect"
}
return "Connect"
}
if (modelData.blocked) {
return "Blocked"
icon: (isBusy ? "busy" : null)
onClicked: {
if (modelData.connected) {
BluetoothService.disconnectDevice(modelData)
} else {
BluetoothService.connectDeviceWithTrust(modelData)
}
}
if (modelData.connected) {
return "Disconnect"
onRightClicked: {
BluetoothService.forgetDevice(modelData)
}
return "Connect"
}
icon: (isBusy ? "busy" : null)
onClicked: {
if (modelData.connected) {
BluetoothService.disconnectDevice(modelData)
} else {
BluetoothService.connectDeviceWithTrust(modelData)
}
}
onRightClicked: {
BluetoothService.forgetDevice(modelData)
}
}
}

View File

@@ -11,7 +11,7 @@ import qs.Widgets
NPanel {
id: root
preferredWidth: 380 * Style.uiScaleRatio
preferredWidth: 420 * Style.uiScaleRatio
preferredHeight: 500 * Style.uiScaleRatio
panelKeyboardFocus: true
@@ -23,63 +23,67 @@ NPanel {
anchors.margins: Style.marginL
spacing: Style.marginM
// HEADER
RowLayout {
// Header
NBox {
Layout.fillWidth: true
spacing: Style.marginM
implicitHeight: headerRow.implicitHeight + (Style.marginM * 2)
NIcon {
icon: "bluetooth"
pointSize: Style.fontSizeXXL
color: Color.mPrimary
}
RowLayout {
id: headerRow
anchors.fill: parent
anchors.leftMargin: Style.marginM
anchors.rightMargin: Style.marginM
spacing: Style.marginM
NText {
text: I18n.tr("bluetooth.panel.title")
pointSize: Style.fontSizeL
font.weight: Style.fontWeightBold
color: Color.mOnSurface
Layout.fillWidth: true
}
NIcon {
icon: "bluetooth"
pointSize: Style.fontSizeXXL
color: Color.mPrimary
}
NToggle {
id: bluetoothSwitch
checked: BluetoothService.enabled
onToggled: checked => BluetoothService.setBluetoothEnabled(checked)
baseSize: Style.baseWidgetSize * 0.65
}
NText {
text: I18n.tr("bluetooth.panel.title")
pointSize: Style.fontSizeL
font.weight: Style.fontWeightBold
color: Color.mOnSurface
Layout.fillWidth: true
}
NIconButton {
enabled: BluetoothService.enabled
icon: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop" : "refresh"
tooltipText: I18n.tr("tooltips.refresh-devices")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
if (BluetoothService.adapter) {
BluetoothService.adapter.discovering = !BluetoothService.adapter.discovering
NToggle {
id: bluetoothSwitch
checked: BluetoothService.enabled
onToggled: checked => BluetoothService.setBluetoothEnabled(checked)
baseSize: Style.baseWidgetSize * 0.65
}
NIconButton {
enabled: BluetoothService.enabled
icon: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop" : "refresh"
tooltipText: I18n.tr("tooltips.refresh-devices")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
if (BluetoothService.adapter) {
BluetoothService.adapter.discovering = !BluetoothService.adapter.discovering
}
}
}
NIconButton {
icon: "close"
tooltipText: I18n.tr("tooltips.close")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
root.close()
}
}
}
NIconButton {
icon: "close"
tooltipText: I18n.tr("tooltips.close")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
root.close()
}
}
}
NDivider {
Layout.fillWidth: true
}
Rectangle {
// Adapter not available of disabled
NBox {
visible: !(BluetoothService.adapter && BluetoothService.adapter.enabled)
Layout.fillWidth: true
Layout.fillHeight: true
color: Color.transparent
// Center the content within this rectangle
ColumnLayout {
@@ -166,9 +170,9 @@ NPanel {
}
// Fallback - No devices, scanning
ColumnLayout {
Layout.alignment: Qt.AlignHCenter
spacing: Style.marginM
NBox {
Layout.fillWidth: true
Layout.preferredHeight: columnScanning.implicitHeight + Style.marginM * 2
visible: {
if (!BluetoothService.adapter || !BluetoothService.adapter.discovering || !Bluetooth.devices) {
return false
@@ -180,37 +184,45 @@ NPanel {
return (availableCount === 0)
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: Style.marginXS
ColumnLayout {
id: columnScanning
anchors.fill: parent
anchors.margins: Style.marginM
NIcon {
icon: "refresh"
pointSize: Style.fontSizeXXL * 1.5
color: Color.mPrimary
spacing: Style.marginM
RotationAnimation on rotation {
running: true
loops: Animation.Infinite
from: 0
to: 360
duration: Style.animationSlow * 4
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: Style.marginXS
NIcon {
icon: "refresh"
pointSize: Style.fontSizeXXL * 1.5
color: Color.mPrimary
RotationAnimation on rotation {
running: true
loops: Animation.Infinite
from: 0
to: 360
duration: Style.animationSlow * 4
}
}
NText {
text: I18n.tr("bluetooth.panel.scanning")
pointSize: Style.fontSizeL
color: Color.mOnSurface
}
}
NText {
text: I18n.tr("bluetooth.panel.scanning")
pointSize: Style.fontSizeL
color: Color.mOnSurface
text: I18n.tr("bluetooth.panel.pairing-mode")
pointSize: Style.fontSizeM
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
}
NText {
text: I18n.tr("bluetooth.panel.pairing-mode")
pointSize: Style.fontSizeM
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
}
Item {

View File

@@ -0,0 +1,106 @@
import QtQuick
import qs.Commons
import Quickshell
Item {
property var now
property color backgroundColor: Color.mPrimary
property color clockColor: Color.mOnPrimary
property color secondHandColor: Color.mError
anchors.fill: parent
Canvas {
id: clockCanvas
anchors.fill: parent
property int hours: now.getHours()
property int minutes: now.getMinutes()
property int seconds: now.getSeconds()
onPaint: {
const markAlpha = 0.7
var ctx = getContext("2d")
ctx.reset()
ctx.translate(width / 2, height / 2)
var radius = Math.min(width, height) / 2
// Hour marks
ctx.strokeStyle = Qt.alpha(clockColor, markAlpha)
ctx.lineWidth = 2 * Style.uiScaleRatio
var scaleFactor = 0.7
for (var i = 0; i < 12; i++) {
var scaleFactor = 0.8
if (i % 3 === 0) {
scaleFactor = 0.65
}
ctx.save()
ctx.rotate(i * Math.PI / 6)
ctx.beginPath()
ctx.moveTo(0, -radius * scaleFactor)
ctx.lineTo(0, -radius)
ctx.stroke()
ctx.restore()
}
// Hour hand
ctx.save()
var hourAngle = (hours % 12 + minutes / 60) * Math.PI / 6
ctx.rotate(hourAngle)
ctx.strokeStyle = clockColor
ctx.lineWidth = 3 * Style.uiScaleRatio
ctx.lineCap = "round"
ctx.beginPath()
ctx.moveTo(0, 0)
ctx.lineTo(0, -radius * 0.6)
ctx.stroke()
ctx.restore()
// Minute hand
ctx.save()
var minuteAngle = (minutes + seconds / 60) * Math.PI / 30
ctx.rotate(minuteAngle)
ctx.strokeStyle = clockColor
ctx.lineWidth = 2 * Style.uiScaleRatio
ctx.lineCap = "round"
ctx.beginPath()
ctx.moveTo(0, 0)
ctx.lineTo(0, -radius * 0.9)
ctx.stroke()
ctx.restore()
// Second hand
ctx.save()
var secondAngle = seconds * Math.PI / 30
ctx.rotate(secondAngle)
ctx.strokeStyle = secondHandColor
ctx.lineWidth = 1.6 * Style.uiScaleRatio
ctx.lineCap = "round"
ctx.beginPath()
ctx.moveTo(0, 0)
ctx.lineTo(0, -radius)
ctx.stroke()
ctx.restore()
// Center dot
ctx.beginPath()
ctx.arc(0, 0, 3 * Style.uiScaleRatio, 0, 2 * Math.PI)
ctx.fillStyle = clockColor
ctx.fill()
}
Timer {
interval: 1000
running: true
repeat: true
onTriggered: {
clockCanvas.hours = now.getHours()
clockCanvas.minutes = now.getMinutes()
clockCanvas.seconds = now.getSeconds()
clockCanvas.requestPaint()
}
}
Component.onCompleted: requestPaint()
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
import QtQuick
import qs.Commons
import qs.Services
import Quickshell
import "../../../Helpers/ColorsConvert.js" as ColorsConvert
Item {
id: clockRoot
property var now
// Default colors
property color backgroundColor: Color.mPrimary
property color clockColor: Color.mOnPrimary
property color secondHandColor: {
var defaultColor = Color.mError
var bestContrast = 1.0 // 1.0 is "no contrast"
var bestColor = defaultColor
var candidates = [Color.mSecondary, Color.mTertiary, Color.mError]
const minContrast = 1.149
for (var i = 0; i < candidates.length; i++) {
var candidate = candidates[i]
var contrastClock = ColorsConvert.getContrastRatio(candidate.toString(), clockColor.toString())
if (contrastClock < minContrast) {
continue
}
var contrastBg = ColorsConvert.getContrastRatio(candidate.toString(), backgroundColor.toString())
if (contrastBg < minContrast) {
continue
}
var currentWorstContrast = Math.min(contrastBg, contrastClock)
if (currentWorstContrast > bestContrast) {
bestContrast = currentWorstContrast
bestColor = candidate
}
}
return bestColor
}
property color progressColor: clockRoot.secondHandColor
height: Math.round((Style.fontSizeXXXL * 1.9) / 2 * Style.uiScaleRatio) * 2
width: clockRoot.height
Loader {
id: clockLoader
anchors.fill: parent
source: Settings.data.location.analogClockInCalendar ? "AnalogClock.qml" : "DigitalClock.qml"
onLoaded: {
item.now = Qt.binding(function () {
return clockRoot.now
})
item.backgroundColor = Qt.binding(function () {
return clockRoot.backgroundColor
})
item.clockColor = Qt.binding(function () {
return clockRoot.clockColor
})
if (item.hasOwnProperty("secondHandColor")) {
item.secondHandColor = Qt.binding(function () {
return clockRoot.secondHandColor
})
}
if (item.hasOwnProperty("progressColor")) {
item.progressColor = Qt.binding(function () {
return clockRoot.progressColor
})
}
}
}
}

View File

@@ -0,0 +1,80 @@
import QtQuick
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import Quickshell
Item {
property var now
property color backgroundColor: Color.mPrimary
property color clockColor: Color.mOnPrimary
property color progressColor: Color.mError
anchors.fill: parent
// Digital clock's seconds circular progress
Canvas {
id: secondsProgress
anchors.fill: parent
property real progress: now.getSeconds() / 60
onProgressChanged: requestPaint()
Connections {
target: Time
function onDateChanged() {
const total = now.getSeconds() * 1000 + now.getMilliseconds()
secondsProgress.progress = total / 60000
}
}
onPaint: {
var ctx = getContext("2d")
var centerX = width / 2
var centerY = height / 2
var radius = Math.min(width, height) / 2 - 3
ctx.reset()
// Background circle
ctx.beginPath()
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI)
ctx.lineWidth = 2.5
ctx.strokeStyle = Qt.alpha(clockColor, 0.15)
ctx.stroke()
// Progress arc
ctx.beginPath()
ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + progress * 2 * Math.PI)
ctx.lineWidth = 2.5
ctx.strokeStyle = progressColor
ctx.lineCap = "round"
ctx.stroke()
}
}
// Digital clock
ColumnLayout {
anchors.centerIn: parent
spacing: -Style.marginXXS
NText {
text: {
var t = Settings.data.location.use12hourFormat ? I18n.locale.toString(now, "hh AP") : I18n.locale.toString(now, "HH")
return t.split(" ")[0]
}
pointSize: Style.fontSizeXS
font.weight: Style.fontWeightBold
color: clockColor
family: Settings.data.ui.fontFixed
Layout.alignment: Qt.AlignHCenter
}
NText {
text: Qt.formatTime(now, "mm")
pointSize: Style.fontSizeXXS
font.weight: Style.fontWeightBold
color: clockColor
family: Settings.data.ui.fontFixed
Layout.alignment: Qt.AlignHCenter
}
}
}

View File

@@ -10,7 +10,7 @@ import qs.Widgets
NPanel {
id: root
preferredWidth: 400 * Style.uiScaleRatio
preferredWidth: 420 * Style.uiScaleRatio
preferredHeight: 500 * Style.uiScaleRatio
panelKeyboardFocus: true
@@ -29,51 +29,53 @@ NPanel {
spacing: Style.marginM
// Header
RowLayout {
NBox {
Layout.fillWidth: true
spacing: Style.marginM
Layout.preferredHeight: headerRow.implicitHeight + Style.marginM * 2
NIcon {
icon: Settings.data.network.wifiEnabled ? "wifi" : "wifi-off"
pointSize: Style.fontSizeXXL
color: Settings.data.network.wifiEnabled ? Color.mPrimary : Color.mOnSurfaceVariant
}
RowLayout {
id: headerRow
anchors.fill: parent
anchors.margins: Style.marginM
spacing: Style.marginM
NText {
text: I18n.tr("wifi.panel.title")
pointSize: Style.fontSizeL
font.weight: Style.fontWeightBold
color: Color.mOnSurface
Layout.fillWidth: true
}
NIcon {
icon: Settings.data.network.wifiEnabled ? "wifi" : "wifi-off"
pointSize: Style.fontSizeXXL
color: Settings.data.network.wifiEnabled ? Color.mPrimary : Color.mOnSurfaceVariant
}
NToggle {
id: wifiSwitch
checked: Settings.data.network.wifiEnabled
onToggled: checked => NetworkService.setWifiEnabled(checked)
baseSize: Style.baseWidgetSize * 0.65
}
NText {
text: I18n.tr("wifi.panel.title")
pointSize: Style.fontSizeL
font.weight: Style.fontWeightBold
color: Color.mOnSurface
Layout.fillWidth: true
}
NIconButton {
icon: "refresh"
tooltipText: I18n.tr("tooltips.refresh")
baseSize: Style.baseWidgetSize * 0.8
enabled: Settings.data.network.wifiEnabled && !NetworkService.scanning
onClicked: NetworkService.scan()
}
NToggle {
id: wifiSwitch
checked: Settings.data.network.wifiEnabled
onToggled: checked => NetworkService.setWifiEnabled(checked)
baseSize: Style.baseWidgetSize * 0.65
}
NIconButton {
icon: "close"
tooltipText: I18n.tr("tooltips.close")
baseSize: Style.baseWidgetSize * 0.8
onClicked: root.close()
NIconButton {
icon: "refresh"
tooltipText: I18n.tr("tooltips.refresh")
baseSize: Style.baseWidgetSize * 0.8
enabled: Settings.data.network.wifiEnabled && !NetworkService.scanning
onClicked: NetworkService.scan()
}
NIconButton {
icon: "close"
tooltipText: I18n.tr("tooltips.close")
baseSize: Style.baseWidgetSize * 0.8
onClicked: root.close()
}
}
}
NDivider {
Layout.fillWidth: true
}
// Error message
Rectangle {
visible: NetworkService.lastError.length > 0
@@ -113,16 +115,15 @@ NPanel {
}
// Main content area
Rectangle {
NBox {
Layout.fillWidth: true
Layout.fillHeight: true
color: Color.transparent
// WiFi disabled state
ColumnLayout {
visible: !Settings.data.network.wifiEnabled
anchors.fill: parent
spacing: Style.marginM
anchors.margins: Style.marginM
Item {
Layout.fillHeight: true
@@ -158,6 +159,7 @@ NPanel {
ColumnLayout {
visible: Settings.data.network.wifiEnabled && NetworkService.scanning && Object.keys(NetworkService.networks).length === 0
anchors.fill: parent
anchors.margins: Style.marginM
spacing: Style.marginL
Item {
@@ -187,6 +189,7 @@ NPanel {
NScrollView {
visible: Settings.data.network.wifiEnabled && (!NetworkService.scanning || Object.keys(NetworkService.networks).length > 0)
anchors.fill: parent
anchors.margins: Style.marginM
horizontalPolicy: ScrollBar.AlwaysOff
verticalPolicy: ScrollBar.AsNeeded
clip: true

View File

@@ -29,9 +29,25 @@ Item {
// Resolve settings: try user settings or defaults from BarWidgetRegistry
readonly property int visualizerWidth: widgetSettings.width !== undefined ? widgetSettings.width : widgetMetadata.width
readonly property bool hideWhenIdle: widgetSettings.hideWhenIdle !== undefined ? widgetSettings.hideWhenIdle : (widgetMetadata.hideWhenIdle !== undefined ? widgetMetadata.hideWhenIdle : false)
readonly property bool shouldShow: (currentVisualizerType !== "" && currentVisualizerType !== "none") && (!hideWhenIdle || MediaService.isPlaying)
implicitWidth: visualizerWidth
implicitHeight: Style.capsuleHeight
implicitWidth: shouldShow ? visualizerWidth : 0
implicitHeight: shouldShow ? Style.capsuleHeight : 0
visible: shouldShow
Behavior on implicitWidth {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.InOutCubic
}
}
Behavior on implicitHeight {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.InOutCubic
}
}
Rectangle {
id: background
@@ -43,27 +59,14 @@ Item {
// Store visualizer type to force re-evaluation
readonly property string currentVisualizerType: Settings.data.audio.visualizerType
// Timer to delay reload
Timer {
id: reloadTimer
interval: 50
onTriggered: {
visualizerLoader.active = true
}
}
// Force reload when visualizer type changes
onCurrentVisualizerTypeChanged: {
visualizerLoader.active = false
reloadTimer.restart()
}
// When visualizer type or playback changes, shouldShow updates automatically
// The Loader dynamically loads the appropriate visualizer based on settings
Loader {
id: visualizerLoader
anchors.fill: parent
anchors.margins: Style.marginS
active: false
active: shouldShow
asynchronous: false
sourceComponent: {
@@ -99,13 +102,7 @@ Item {
}
}
// Initial activation on component complete
Component.onCompleted: {
if (currentVisualizerType !== "" && currentVisualizerType !== "none") {
visualizerLoader.active = true
}
}
// No imperative activation needed; bound to shouldShow
Component {
id: linearComponent
LinearSpectrum {

View File

@@ -62,7 +62,7 @@ Rectangle {
spacing: Settings.data.bar.showCapsule ? -4 : -2
Repeater {
id: repeater
model: Qt.locale().toString(now, formatHorizontal.trim()).split("\\n")
model: I18n.locale.toString(now, formatHorizontal.trim()).split("\\n")
NText {
visible: text !== ""
text: modelData
@@ -95,7 +95,7 @@ Rectangle {
anchors.centerIn: parent
spacing: -2
Repeater {
model: Qt.locale().toString(now, formatVertical.trim()).split(" ")
model: I18n.locale.toString(now, formatVertical.trim()).split(" ")
delegate: NText {
visible: text !== ""
text: modelData

View File

@@ -0,0 +1,123 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Modules.Settings
import qs.Services
import qs.Widgets
//import qs.Modules.Bar.Extras
Rectangle {
id: root
property string widgetId: ""
property string section: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.data.bar.widgets[section]
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex]
}
}
return {}
}
readonly property string barPosition: Settings.data.bar.position
readonly property bool isVertical: barPosition === "left" || barPosition === "right"
readonly property string iconStyle: (widgetSettings.indicatorStyle !== undefined) ? widgetSettings.indicatorStyle : widgetMetadata.indicatorStyle
readonly property bool showCaps: (widgetSettings.showCapsLock !== undefined) ? widgetSettings.showCapsLock : widgetMetadata.showCapsLock
readonly property bool showNum: (widgetSettings.showNumLock !== undefined) ? widgetSettings.showNumLock : widgetMetadata.showNumLock
readonly property bool showScroll: (widgetSettings.showScrollLock !== undefined) ? widgetSettings.showScrollLock : widgetMetadata.showScrollLock
implicitWidth: isVertical ? Style.capsuleHeight : Math.round(layout.implicitWidth + Style.marginM * 2)
implicitHeight: isVertical ? Math.round(layout.implicitHeight + Style.marginM * 2) : Style.capsuleHeight
Layout.alignment: Qt.AlignVCenter
radius: Style.radiusM
color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent
Item {
id: layout
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
implicitWidth: rowLayout.visible ? rowLayout.implicitWidth : colLayout.implicitWidth
implicitHeight: rowLayout.visible ? rowLayout.implicitHeight : colLayout.implicitHeight
readonly property var indicatorStyle: root.getIndicatorStyle(root.iconStyle)
RowLayout {
id: rowLayout
visible: !root.isVertical
spacing: 0
NIcon {
visible: root.showCaps
icon: layout.indicatorStyle[0]
color: LockKeysService.capsLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
NIcon {
visible: root.showNum
icon: layout.indicatorStyle[1]
color: LockKeysService.numLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
NIcon {
visible: root.showScroll
icon: layout.indicatorStyle[2]
color: LockKeysService.scrollLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
}
ColumnLayout {
id: colLayout
visible: root.isVertical
spacing: 0
NIcon {
visible: root.showCaps
icon: layout.indicatorStyle[0]
color: LockKeysService.capsLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
NIcon {
visible: root.showNum
icon: layout.indicatorStyle[1]
color: LockKeysService.numLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
NIcon {
visible: root.showScroll
icon: layout.indicatorStyle[2]
color: LockKeysService.scrollLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
}
}
function getIndicatorStyle(styleName) {
switch (styleName) {
case "large":
return ["letter-c", "letter-n", "letter-s"]
case "small":
return ["letter-c-small", "letter-n-small", "letter-s-small"]
case "square":
return ["square-letter-c", "square-letter-n", "square-letter-s"]
case "square-round":
return ["square-rounded-letter-c", "square-rounded-letter-n", "square-rounded-letter-s"]
case "circle":
return ["circle-letter-c", "circle-letter-n", "circle-letter-s"]
case "circle-dash":
return ["circle-dashed-letter-c", "circle-dashed-letter-n", "circle-dashed-letter-s"]
case "circle-dot":
return ["circle-dotted-letter-c", "circle-dotted-letter-n", "circle-dotted-letter-s"]
case "hex":
return ["hexagon-letter-c", "hexagon-letter-n", "hexagon-letter-s"]
default:
return ["letter-c", "letter-n", "letter-s"]
}
}
}

View File

@@ -105,10 +105,10 @@ Item {
}
}
onClicked: {
AudioService.setInputMuted(!AudioService.inputMuted)
PanelService.getPanel("audioPanel")?.toggle(this)
}
onRightClicked: {
PanelService.getPanel("audioPanel")?.toggle(this)
AudioService.setInputMuted(!AudioService.inputMuted)
}
onMiddleClicked: {
Quickshell.execDetached(["pwvucontrol"])

View File

@@ -347,12 +347,12 @@ Rectangle {
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: {
var directPanel = PanelService.getPanel("directWidgetSettingsPanel")
directPanel.openWidgetSettings(root.section, root.sectionWidgetIndex, root.widgetId, root.widgetSettings)
}
}
// MouseArea {
// anchors.fill: parent
// acceptedButtons: Qt.RightButton
// onClicked: {
// var directPanel = PanelService.getPanel("directWidgetSettingsPanel")
// directPanel.openWidgetSettings(root.section, root.sectionWidgetIndex, root.widgetId, root.widgetSettings)
// }
// }
}

View File

@@ -90,10 +90,10 @@ Item {
}
}
onClicked: {
AudioService.setOutputMuted(!AudioService.muted)
PanelService.getPanel("audioPanel")?.toggle(this)
}
onRightClicked: {
PanelService.getPanel("audioPanel")?.toggle(this)
AudioService.setOutputMuted(!AudioService.muted)
}
onMiddleClicked: {
Quickshell.execDetached(["sh", "-c", "pwvucontrol || pavucontrol"])

View File

@@ -179,8 +179,7 @@ NBox {
onTriggered: function (action) {
var index = parseInt(action)
if (!isNaN(index)) {
MediaService.selectedPlayerIndex = index
MediaService.updateCurrentPlayer()
MediaService.switchToPlayer(index)
}
}
}

View File

@@ -9,8 +9,13 @@ import qs.Widgets
NBox {
id: root
property int forecastDays: 7
property bool showLocation: true
readonly property bool weatherReady: Settings.data.location.weatherEnabled && (LocationService.data.weather !== null)
visible: Settings.data.location.weatherEnabled
implicitHeight: Math.max(100 * Style.uiScaleRatio, content.implicitHeight + (Style.marginXL * 2))
ColumnLayout {
id: content
anchors.left: parent.left
@@ -43,6 +48,7 @@ NBox {
}
pointSize: Style.fontSizeL
font.weight: Style.fontWeightBold
visible: showLocation
}
RowLayout {
@@ -61,7 +67,7 @@ NBox {
temp = Math.round(temp)
return `${temp}°${suffix}`
}
pointSize: Style.fontSizeXL
pointSize: showLocation ? Style.fontSizeXL : Style.fontSizeXL * 1.6
font.weight: Style.fontWeightBold
}
@@ -69,7 +75,7 @@ NBox {
text: weatherReady ? `(${LocationService.data.weather.timezone_abbreviation})` : ""
pointSize: Style.fontSizeXS
color: Color.mOnSurfaceVariant
visible: LocationService.data.weather
visible: LocationService.data.weather && showLocation
}
}
}
@@ -87,7 +93,7 @@ NBox {
spacing: Style.marginM
Repeater {
model: weatherReady ? LocationService.data.weather.daily.time : []
model: weatherReady ? Math.min(root.forecastDays, LocationService.data.weather.daily.time.length) : 0
delegate: ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXS
@@ -98,7 +104,7 @@ NBox {
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
text: {
var weatherDate = new Date(LocationService.data.weather.daily.time[index].replace(/-/g, "/"))
return Qt.locale().toString(weatherDate, "ddd")
return I18n.locale.toString(weatherDate, "ddd")
}
color: Color.mOnSurface
}

View File

@@ -63,6 +63,14 @@ NPanel {
readonly property int weatherHeight: Math.round(190 * Style.uiScaleRatio)
readonly property int mediaSysMonHeight: Math.round(260 * Style.uiScaleRatio)
onOpened: {
MediaService.autoSwitchingPaused = true
}
onClosed: {
MediaService.autoSwitchingPaused = false
}
panelContent: Item {
id: content

View File

@@ -10,6 +10,6 @@ NIconButtonHot {
enabled: Settings.data.wallpaper.enabled
icon: "wallpaper-selector"
tooltipText: I18n.tr("quickSettings.wallpaperSelector.tooltip.action")
onClicked: PanelService.getPanel("wallpaperPanel")?.toggle(this)
onClicked: PanelService.getPanel("wallpaperPanel")?.toggle()
onRightClicked: WallpaperService.setRandomWallpaper()
}

View File

@@ -186,7 +186,7 @@ Variants {
// PEEK WINDOW - Always visible when auto-hide is enabled
Loader {
active: (barIsReady || !hasBar) && modelData && Settings.data.dock.monitors.includes(modelData.name) && autoHide
active: Settings.data.dock.enabled && (barIsReady || !hasBar) && modelData && (Settings.data.dock.monitors.length === 0 || Settings.data.dock.monitors.includes(modelData.name)) && autoHide
sourceComponent: PanelWindow {
id: peekWindow
@@ -226,7 +226,7 @@ Variants {
// DOCK WINDOW
Loader {
active: (barIsReady || !hasBar) && modelData && Settings.data.dock.monitors.includes(modelData.name) && dockLoaded && ToplevelManager && (dockApps.length > 0)
active: Settings.data.dock.enabled && (barIsReady || !hasBar) && modelData && (Settings.data.dock.monitors.length === 0 || Settings.data.dock.monitors.includes(modelData.name)) && dockLoaded && ToplevelManager && (dockApps.length > 0)
sourceComponent: PanelWindow {
id: dockWindow

View File

@@ -12,6 +12,7 @@ import qs.Commons
import qs.Services
import qs.Widgets
import qs.Modules.Audio
import qs.Modules.Bar.Calendar
Loader {
id: lockScreen
@@ -341,7 +342,7 @@ Loader {
// Date below
NText {
text: {
var lang = Qt.locale().name.split("_")[0]
var lang = I18n.locale.name.split("_")[0]
var formats = {
"de": "dddd, d. MMMM",
"es": "dddd, d 'de' MMMM",
@@ -349,7 +350,7 @@ Loader {
"pt": "dddd, d 'de' MMMM",
"zh": "yyyy年M月d日 dddd"
}
return Qt.locale().toString(Time.date, formats[lang] || "dddd, MMMM d")
return I18n.locale.toString(Time.date, formats[lang] || "dddd, MMMM d")
}
pointSize: Style.fontSizeXL
font.weight: Font.Medium
@@ -363,80 +364,15 @@ Loader {
Layout.fillWidth: true
}
Item {
// Clock
ClockLoader {
now: Time.date
Layout.preferredWidth: 70
Layout.preferredHeight: 70
Layout.alignment: Qt.AlignVCenter
// Seconds circular progress
Canvas {
id: secondsProgress
anchors.fill: parent
property real progress: Time.date.getSeconds() / 60
onProgressChanged: requestPaint()
Connections {
target: Time
function onDateChanged() {
const total = Time.date.getSeconds() * 1000 + Time.date.getMilliseconds()
secondsProgress.progress = total / 60000
}
}
onPaint: {
var ctx = getContext("2d")
var centerX = width / 2
var centerY = height / 2
var radius = Math.min(width, height) / 2 - 3
ctx.reset()
// Background circle
ctx.beginPath()
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI)
ctx.lineWidth = 2.5
ctx.strokeStyle = Qt.alpha(Color.mOnSurface, 0.15)
ctx.stroke()
// Progress arc
ctx.beginPath()
ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + progress * 2 * Math.PI)
ctx.lineWidth = 2.5
ctx.strokeStyle = Color.mPrimary
ctx.lineCap = "round"
ctx.stroke()
}
}
// Digital clock
ColumnLayout {
anchors.centerIn: parent
spacing: 0
NText {
text: {
var t = Settings.data.location.use12hourFormat ? Qt.locale().toString(Time.date, "hh AP") : Qt.locale().toString(Time.date, "HH")
return t
}
pointSize: Style.fontSizeM
font.weight: Style.fontWeightBold
family: Settings.data.ui.fontFixed
color: Color.mOnSurface
horizontalAlignment: Text.AlignHCenter
Layout.alignment: Qt.AlignHCenter
}
NText {
text: Qt.formatTime(Time.date, "mm")
pointSize: Style.fontSizeM
font.weight: Style.fontWeightBold
family: Settings.data.ui.fontFixed
color: Color.mOnSurfaceVariant
horizontalAlignment: Text.AlignHCenter
Layout.alignment: Qt.AlignHCenter
}
}
backgroundColor: Color.mSurface
clockColor: Color.mOnSurface
secondHandColor: Color.mPrimary
}
}
}
@@ -830,7 +766,7 @@ Loader {
NText {
text: {
var weatherDate = new Date(LocationService.data.weather.daily.time[index].replace(/-/g, "/"))
return Qt.locale().toString(weatherDate, "ddd")
return I18n.locale.toString(weatherDate, "ddd")
}
pointSize: Style.fontSizeM
color: Color.mOnSurfaceVariant

View File

@@ -30,52 +30,55 @@ NPanel {
spacing: Style.marginM
// Header section
RowLayout {
NBox {
Layout.fillWidth: true
spacing: Style.marginM
implicitHeight: headerRow.implicitHeight + (Style.marginM * 2)
NIcon {
icon: "bell"
pointSize: Style.fontSizeXXL
color: Color.mPrimary
}
RowLayout {
id: headerRow
anchors.fill: parent
anchors.margins: Style.marginM
spacing: Style.marginM
NText {
text: I18n.tr("notifications.panel.title")
pointSize: Style.fontSizeL
font.weight: Style.fontWeightBold
color: Color.mOnSurface
Layout.fillWidth: true
}
NIcon {
icon: "bell"
pointSize: Style.fontSizeXXL
color: Color.mPrimary
}
NIconButton {
icon: Settings.data.notifications.doNotDisturb ? "bell-off" : "bell"
tooltipText: Settings.data.notifications.doNotDisturb ? I18n.tr("tooltips.do-not-disturb-enabled") : I18n.tr("tooltips.do-not-disturb-disabled")
baseSize: Style.baseWidgetSize * 0.8
onClicked: Settings.data.notifications.doNotDisturb = !Settings.data.notifications.doNotDisturb
}
NText {
text: I18n.tr("notifications.panel.title")
pointSize: Style.fontSizeL
font.weight: Style.fontWeightBold
color: Color.mOnSurface
Layout.fillWidth: true
}
NIconButton {
icon: "trash"
tooltipText: I18n.tr("tooltips.clear-history")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
NotificationService.clearHistory()
// Close panel as there is nothing more to see.
root.close()
NIconButton {
icon: Settings.data.notifications.doNotDisturb ? "bell-off" : "bell"
tooltipText: Settings.data.notifications.doNotDisturb ? I18n.tr("tooltips.do-not-disturb-enabled") : I18n.tr("tooltips.do-not-disturb-disabled")
baseSize: Style.baseWidgetSize * 0.8
onClicked: Settings.data.notifications.doNotDisturb = !Settings.data.notifications.doNotDisturb
}
NIconButton {
icon: "trash"
tooltipText: I18n.tr("tooltips.clear-history")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
NotificationService.clearHistory()
// Close panel as there is nothing more to see.
root.close()
}
}
NIconButton {
icon: "close"
tooltipText: I18n.tr("tooltips.close")
baseSize: Style.baseWidgetSize * 0.8
onClicked: root.close()
}
}
NIconButton {
icon: "close"
tooltipText: I18n.tr("tooltips.close")
baseSize: Style.baseWidgetSize * 0.8
onClicked: root.close()
}
}
NDivider {
Layout.fillWidth: true
}
// Empty state when no notifications

View File

@@ -17,14 +17,11 @@ Popup {
signal updateWidgetSettings(string section, int index, var settings)
// Center popup in parent
x: (parent.width - width) * 0.5
y: (parent.height - height) * 0.5
width: Math.max(content.implicitWidth + padding * 2, 500)
height: content.implicitHeight + padding * 2
padding: Style.marginXL
modal: true
anchors.centerIn: parent
onOpened: {
// Mark this popup has opened in the PanelService
@@ -131,6 +128,7 @@ Popup {
"ControlCenter": "WidgetSettings/ControlCenterSettings.qml",
"CustomButton": "WidgetSettings/CustomButtonSettings.qml",
"KeyboardLayout": "WidgetSettings/KeyboardLayoutSettings.qml",
"LockKeys": "WidgetSettings/LockKeysSettings.qml",
"MediaMini": "WidgetSettings/MediaMiniSettings.qml",
"Microphone": "WidgetSettings/MicrophoneSettings.qml",
"NotificationHistory": "WidgetSettings/NotificationHistorySettings.qml",

View File

@@ -13,9 +13,13 @@ ColumnLayout {
property var widgetData: null
property var widgetMetadata: null
// Local state
property bool valueHideWhenIdle: widgetData.hideWhenIdle !== undefined ? widgetData.hideWhenIdle : (widgetMetadata.hideWhenIdle !== undefined ? widgetMetadata.hideWhenIdle : false)
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.width = parseInt(widthInput.text) || widgetMetadata.width
settings.hideWhenIdle = valueHideWhenIdle
return settings
}
@@ -27,4 +31,11 @@ ColumnLayout {
text: widgetData.width || widgetMetadata.width
placeholderText: I18n.tr("placeholders.enter-width-pixels")
}
NToggle {
label: I18n.tr("bar.widget-settings.audio-visualizer.hide-when-idle.label")
description: I18n.tr("bar.widget-settings.audio-visualizer.hide-when-idle.description")
checked: valueHideWhenIdle
onToggled: checked => valueHideWhenIdle = checked
}
}

View File

@@ -200,7 +200,7 @@ ColumnLayout {
// Horizontal
Repeater {
Layout.topMargin: Style.marginM
model: Qt.locale().toString(now, valueFormatHorizontal.trim()).split("\\n")
model: I18n.locale.toString(now, valueFormatHorizontal.trim()).split("\\n")
delegate: NText {
visible: text !== ""
text: modelData
@@ -231,7 +231,7 @@ ColumnLayout {
Repeater {
Layout.topMargin: Style.marginM
model: Qt.locale().toString(now, valueFormatVertical.trim()).split(" ")
model: I18n.locale.toString(now, valueFormatVertical.trim()).split(" ")
delegate: NText {
visible: text !== ""
text: modelData

View File

@@ -0,0 +1,84 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
ColumnLayout {
id: root
spacing: Style.marginM
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
// Local state
property string valueIndicatorStyle: widgetData.indicatorStyle !== undefined ? widgetData.indicatorStyle : widgetMetadata.indicatorStyle
property bool valueShowCapsLock: widgetData.showCapsLock !== undefined ? widgetData.showCapsLock : widgetMetadata.showCapsLock
property bool valueShowNumLock: widgetData.showNumLock !== undefined ? widgetData.showNumLock : widgetMetadata.showNumLock
property bool valueShowScrollLock: widgetData.showScrollLock !== undefined ? widgetData.showScrollLock : widgetMetadata.showScrollLock
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.indicatorStyle = valueIndicatorStyle
settings.showCapsLock = valueShowCapsLock
settings.showNumLock = valueShowNumLock
settings.showScrollLock = valueShowScrollLock
return settings
}
NComboBox {
Layout.fillWidth: true
label: I18n.tr("bar.widget-settings.lock-keys.indicator-style.label")
description: I18n.tr("bar.widget-settings.lock-keys.indicator-style.description")
model: [{
"key": "large",
"name": I18n.tr("bar.widget-settings.lock-keys.indicator-style.large")
}, {
"key": "small",
"name": I18n.tr("bar.widget-settings.lock-keys.indicator-style.small")
}, {
"key": "square",
"name": I18n.tr("bar.widget-settings.lock-keys.indicator-style.square")
}, {
"key": "square-round",
"name": I18n.tr("bar.widget-settings.lock-keys.indicator-style.square-round")
}, {
"key": "circle",
"name": I18n.tr("bar.widget-settings.lock-keys.indicator-style.circle")
}, {
"key": "circle-dash",
"name": I18n.tr("bar.widget-settings.lock-keys.indicator-style.circle-dash")
}, {
"key": "circle-dot",
"name": I18n.tr("bar.widget-settings.lock-keys.indicator-style.circle-dot")
}, {
"key": "hex",
"name": I18n.tr("bar.widget-settings.lock-keys.indicator-style.hex")
}]
currentKey: valueIndicatorStyle
onSelected: key => valueIndicatorStyle = key
}
NToggle {
label: I18n.tr("bar.widget-settings.lock-keys.show-caps-lock.label")
description: I18n.tr("bar.widget-settings.lock-keys.show-caps-lock.description")
checked: valueShowCapsLock
onToggled: checked => valueShowCapsLock = checked
}
NToggle {
label: I18n.tr("bar.widget-settings.lock-keys.show-num-lock.label")
description: I18n.tr("bar.widget-settings.lock-keys.show-num-lock.description")
checked: valueShowNumLock
onToggled: checked => valueShowNumLock = checked
}
NToggle {
label: I18n.tr("bar.widget-settings.lock-keys.show-scroll-lock.label")
description: I18n.tr("bar.widget-settings.lock-keys.show-scroll-lock.description")
checked: valueShowScrollLock
onToggled: checked => valueShowScrollLock = checked
}
}

View File

@@ -13,17 +13,20 @@ ColumnLayout {
property var widgetData: null
property var widgetMetadata: null
property string valueLabelMode: widgetData.labelMode !== undefined ? widgetData.labelMode : widgetMetadata.labelMode
property bool valueHideUnoccupied: widgetData.hideUnoccupied !== undefined ? widgetData.hideUnoccupied : widgetMetadata.hideUnoccupied
property int valueCharacterCount: widgetData.characterCount !== undefined ? widgetData.characterCount : widgetMetadata.characterCount
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.labelMode = labelModeCombo.currentKey
settings.hideUnoccupied = hideUnoccupiedToggle.checked
settings.characterCount = characterCountSpinBox.value
settings.labelMode = valueLabelMode
settings.hideUnoccupied = valueHideUnoccupied
settings.characterCount = valueCharacterCount
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: [{
@@ -37,33 +40,24 @@ ColumnLayout {
"name": I18n.tr("options.workspace-labels.name")
}]
currentKey: widgetData.labelMode || widgetMetadata.labelMode
onSelected: key => labelModeCombo.currentKey = key
onSelected: key => valueLabelMode = key
minimumWidth: 200
}
NToggle {
id: hideUnoccupiedToggle
label: I18n.tr("bar.widget-settings.workspace.hide-unoccupied.label")
description: I18n.tr("bar.widget-settings.workspace.hide-unoccupied.description")
checked: widgetData.hideUnoccupied
onToggled: checked => hideUnoccupiedToggle.checked = checked
checked: valueHideUnoccupied
onToggled: checked => valueHideUnoccupied = checked
}
NSpinBox {
id: characterCountSpinBox
label: I18n.tr("bar.widget-settings.workspace.character-count.label")
description: I18n.tr("bar.widget-settings.workspace.character-count.description")
from: 1
to: 10
value: {
if (widgetData && widgetData.characterCount !== undefined) {
return widgetData.characterCount
}
if (widgetMetadata && widgetMetadata.characterCount !== undefined) {
return widgetMetadata.characterCount
}
return 2
}
visible: labelModeCombo.currentKey === "name"
value: valueCharacterCount
onValueChanged: valueCharacterCount = value
visible: valueLabelMode === "name"
}
}

View File

@@ -223,6 +223,8 @@ NPanel {
// When the panel opens, choose the appropriate tab
onOpened: {
// Run program availability checks every time settings opens
ProgramCheckerService.checkAllPrograms()
updateTabsModel()
var initialIndex = SettingsPanel.Tab.General

View File

@@ -697,51 +697,24 @@ ColumnLayout {
onToggled: checked => {
if (ProgramCheckerService.walkerAvailable) {
Settings.data.templates.walker = checked
if (checked) {
// Update walker config.toml to use noctalia theme
var configFile = Quickshell.env("HOME") + "/.config/walker/config.toml"
var configDir = Quickshell.env("HOME") + "/.config/walker"
var configFileEsc = configFile.replace(/'/g, "'\\''")
var configDirEsc = configDir.replace(/'/g, "'\\''")
AppThemeService.generate()
}
}
}
Logger.i("ColorSchemeTab", "Updating walker config:", configFile)
Logger.d("ColorSchemeTab", "Config file path:", configFileEsc)
Logger.d("ColorSchemeTab", "Config dir path:", configDirEsc)
// Remove existing theme line and insert new one at the top
var script = "echo '[Walker Config] Starting update...' && "
script += "mkdir -p '" + configDirEsc + "' && "
script += "echo '[Walker Config] Directory created/verified' && "
script += "if [ -f '" + configFileEsc + "' ]; then "
script += "echo '[Walker Config] File exists, removing old theme line' && "
script += "(grep -v '^theme[[:space:]]*=' '" + configFileEsc + "' > '" + configFileEsc + ".tmp' 2>/dev/null || cat '" + configFileEsc + "' > '" + configFileEsc + ".tmp') && "
script += "mv '" + configFileEsc + ".tmp' '" + configFileEsc + "' && "
script += "echo '[Walker Config] Removed old theme line'; "
script += "else echo '[Walker Config] File does not exist, will create'; "
script += "fi && "
script += "echo 'theme = \"noctalia\"' | cat - '" + configFileEsc + "' > '" + configFileEsc + ".tmp' && "
script += "mv '" + configFileEsc + ".tmp' '" + configFileEsc + "' && "
script += "echo '[Walker Config] Inserted theme at top' && "
script += "FINAL_THEME=$(grep '^theme' '" + configFileEsc + "' | head -1 2>/dev/null) && "
script += "echo '[Walker Config] Final theme line: '$FINAL_THEME || echo '[Walker Config] ERROR: Theme line not found after update'"
Logger.d("ColorSchemeTab", "Executing script:", script)
// Execute script using execDetached
Quickshell.execDetached(["sh", "-c", script])
// Verify the update after a short delay
Qt.callLater(function () {
var verifyScript = "grep '^theme' '" + configFileEsc + "' | head -1 2>/dev/null || echo 'NOT_FOUND'"
var verifyProcessStr = "import QtQuick; import Quickshell.Io; Process { command: [\"sh\", \"-c\", \"" + verifyScript.replace(/"/g, '\\"') + "\"]; stdout: StdioCollector {} }"
var verifyProcess = Qt.createQmlObject(verifyProcessStr, root, "walkerVerify")
verifyProcess.exited.connect(function (exitCode) {
Logger.i("ColorSchemeTab", "Walker theme verification:", verifyProcess.stdout.text || "NOT FOUND")
verifyProcess.destroy()
})
verifyProcess.running = true
})
}
NCheckbox {
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
onToggled: checked => {
if (ProgramCheckerService.codeAvailable) {
Settings.data.templates.code = checked
AppThemeService.generate()
}
}

View File

@@ -29,7 +29,16 @@ ColumnLayout {
description: I18n.tr("settings.dock.appearance.section.description")
}
NToggle {
Layout.fillWidth: true
label: I18n.tr("settings.dock.enabled.label")
description: I18n.tr("settings.dock.enabled.description")
checked: Settings.data.dock.enabled
onToggled: checked => Settings.data.dock.enabled = checked
}
NComboBox {
visible: Settings.data.dock.enabled
Layout.fillWidth: true
label: I18n.tr("settings.dock.appearance.display.label")
description: I18n.tr("settings.dock.appearance.display.description")
@@ -50,6 +59,7 @@ ColumnLayout {
}
ColumnLayout {
visible: Settings.data.dock.enabled
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
@@ -68,6 +78,7 @@ ColumnLayout {
}
ColumnLayout {
visible: Settings.data.dock.enabled
spacing: Style.marginXXS
Layout.fillWidth: true
@@ -88,6 +99,7 @@ ColumnLayout {
}
ColumnLayout {
visible: Settings.data.dock.enabled
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
@@ -106,6 +118,7 @@ ColumnLayout {
}
NToggle {
visible: Settings.data.dock.enabled
label: I18n.tr("settings.dock.monitors.only-same-output.label")
description: I18n.tr("settings.dock.monitors.only-same-output.description")
checked: Settings.data.dock.onlySameOutput
@@ -113,6 +126,7 @@ ColumnLayout {
}
NToggle {
visible: Settings.data.dock.enabled
Layout.fillWidth: true
label: I18n.tr("settings.dock.appearance.colorize-icons.label")
description: I18n.tr("settings.dock.appearance.colorize-icons.description")
@@ -121,6 +135,7 @@ ColumnLayout {
}
NDivider {
visible: Settings.data.dock.enabled
Layout.fillWidth: true
Layout.topMargin: Style.marginXL
Layout.bottomMargin: Style.marginXL
@@ -128,6 +143,7 @@ ColumnLayout {
// Monitor Configuration
ColumnLayout {
visible: Settings.data.dock.enabled
spacing: Style.marginM
Layout.fillWidth: true
@@ -163,6 +179,7 @@ ColumnLayout {
}
NDivider {
visible: Settings.data.dock.enabled
Layout.fillWidth: true
Layout.topMargin: Style.marginXL
Layout.bottomMargin: Style.marginXL

View File

@@ -84,7 +84,14 @@ ColumnLayout {
checked: Settings.data.location.useFahrenheit
onToggled: checked => Settings.data.location.useFahrenheit = checked
enabled: Settings.data.location.weatherEnabled
opacity: Settings.data.location.weatherEnabled ? 1.0 : 0.5
}
NToggle {
label: I18n.tr("settings.location.weather.show-in-calendar.label")
description: I18n.tr("settings.location.weather.show-in-calendar.description")
checked: Settings.data.location.showCalendarWeather
onToggled: checked => Settings.data.location.showCalendarWeather = checked
enabled: Settings.data.location.weatherEnabled
}
}
@@ -118,6 +125,30 @@ ColumnLayout {
onToggled: checked => Settings.data.location.showWeekNumberInCalendar = checked
}
NComboBox {
label: I18n.tr("settings.location.date-time.first-day-of-week.label")
description: I18n.tr("settings.location.date-time.first-day-of-week.description")
currentKey: Settings.data.location.firstDayOfWeek.toString()
minimumWidth: 260 * Style.uiScaleRatio
model: [{
"key": "-1",
"name": I18n.tr("settings.location.date-time.first-day-of-week.automatic")
}, {
"key": "6",
"name": I18n.locale.dayName(6, Locale.LongFormat)
}, // Saturday
{
"key": "0",
"name": I18n.locale.dayName(0, Locale.LongFormat)
}, // Sunday
{
"key": "1",
"name": I18n.locale.dayName(1, Locale.LongFormat)
} // Monday
]
onSelected: key => Settings.data.location.firstDayOfWeek = parseInt(key)
}
NToggle {
label: I18n.tr("settings.location.date-time.show-events.label")
description: I18n.tr("settings.location.date-time.show-events.description")

View File

@@ -19,13 +19,6 @@ ColumnLayout {
description: I18n.tr("settings.user-interface.section.description")
}
NToggle {
label: I18n.tr("settings.user-interface.dim-desktop.label")
description: I18n.tr("settings.user-interface.dim-desktop.description")
checked: Settings.data.general.dimDesktop
onToggled: checked => Settings.data.general.dimDesktop = checked
}
NToggle {
label: I18n.tr("settings.user-interface.tooltips.label")
description: I18n.tr("settings.user-interface.tooltips.description")
@@ -33,6 +26,13 @@ ColumnLayout {
onToggled: checked => Settings.data.ui.tooltipsEnabled = checked
}
NToggle {
label: I18n.tr("settings.user-interface.panels-attached-to-bar.label")
description: I18n.tr("settings.user-interface.panels-attached-to-bar.description")
checked: Settings.data.ui.panelsAttachedToBar
onToggled: checked => Settings.data.ui.panelsAttachedToBar = checked
}
NToggle {
label: I18n.tr("settings.user-interface.panels-overlay.label")
description: I18n.tr("settings.user-interface.panels-overlay.description")

View File

@@ -65,7 +65,7 @@ ColumnLayout {
Layout.fillWidth: true
radius: Style.radiusM
color: Color.mSurfaceVariant
color: Color.mSurface
border.color: Color.mOutline
border.width: Style.borderS
implicitHeight: contentCol.implicitHeight + Style.marginL * 2

View File

@@ -11,11 +11,9 @@ ColumnLayout {
property real selectedScaleRatio: 1.0
property string selectedBarPosition: "top"
property bool selectedDimDesktop: true
signal scaleRatioChanged(real ratio)
signal barPositionChanged(string position)
signal dimDesktopChanged(bool dim)
spacing: Style.marginM
@@ -197,61 +195,6 @@ ColumnLayout {
Layout.bottomMargin: Style.marginS
}
// Dim Desktop section
RowLayout {
Layout.fillWidth: true
spacing: Style.marginM
Rectangle {
width: 32
height: 32
radius: Style.radiusM
color: Color.mSurface
NIcon {
icon: "moon"
pointSize: Style.fontSizeL
color: Color.mPrimary
anchors.centerIn: parent
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: 2
NText {
text: I18n.tr("settings.user-interface.dim-desktop.label")
pointSize: Style.fontSizeL
font.weight: Style.fontWeightBold
color: Color.mOnSurface
}
NText {
text: I18n.tr("settings.user-interface.dim-desktop.description")
pointSize: Style.fontSizeS
color: Color.mOnSurfaceVariant
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
}
NToggle {
checked: selectedDimDesktop
onToggled: function (checked) {
selectedDimDesktop = checked
dimDesktopChanged(checked)
}
}
}
// Divider
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Color.mOutline
opacity: 0.2
Layout.topMargin: Style.marginS
Layout.bottomMargin: Style.marginS
}
// Bar Density section
RowLayout {
Layout.fillWidth: true

View File

@@ -0,0 +1,203 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import qs.Commons
import qs.Services
import qs.Widgets
ColumnLayout {
id: root
spacing: Style.marginM
// Header
RowLayout {
Layout.fillWidth: true
Layout.bottomMargin: Style.marginL
spacing: Style.marginM
Rectangle {
width: 40
height: 40
radius: Style.radiusL
color: Color.mSurfaceVariant
opacity: 0.6
NIcon {
icon: "device-desktop"
pointSize: Style.fontSizeL
color: Color.mPrimary
anchors.centerIn: parent
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXS
NText {
text: I18n.tr("settings.dock.title") || "Dock"
pointSize: Style.fontSizeXL
font.weight: Style.fontWeightBold
color: Color.mPrimary
}
NText {
text: I18n.tr("settings.dock.monitors.section.description")
pointSize: Style.fontSizeM
color: Color.mOnSurfaceVariant
}
}
}
// Options
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginL
NToggle {
Layout.fillWidth: true
label: I18n.tr("settings.dock.enabled.label")
description: I18n.tr("settings.dock.enabled.description")
checked: Settings.data.dock.enabled
onToggled: checked => Settings.data.dock.enabled = checked
}
// Display behavior
NComboBox {
visible: Settings.data.dock.enabled
Layout.fillWidth: true
label: I18n.tr("settings.dock.appearance.display.label")
description: I18n.tr("settings.dock.appearance.display.description")
model: [{
"key": "always_visible",
"name": I18n.tr("settings.dock.appearance.display.always-visible")
}, {
"key": "auto_hide",
"name": I18n.tr("settings.dock.appearance.display.auto-hide")
}, {
"key": "exclusive",
"name": I18n.tr("settings.dock.appearance.display.exclusive")
}]
currentKey: Settings.data.dock.displayMode
onSelected: key => Settings.data.dock.displayMode = key
}
// Background opacity
ColumnLayout {
visible: Settings.data.dock.enabled
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("settings.dock.appearance.background-opacity.label")
description: I18n.tr("settings.dock.appearance.background-opacity.description")
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: Settings.data.dock.backgroundOpacity
onMoved: value => Settings.data.dock.backgroundOpacity = value
text: Math.floor(Settings.data.dock.backgroundOpacity * 100) + "%"
}
}
// Floating distance
ColumnLayout {
visible: Settings.data.dock.enabled
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("settings.dock.appearance.floating-distance.label")
description: I18n.tr("settings.dock.appearance.floating-distance.description")
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 4
stepSize: 0.01
value: Settings.data.dock.floatingRatio
onMoved: value => Settings.data.dock.floatingRatio = value
text: Math.floor(Settings.data.dock.floatingRatio * 100) + "%"
}
}
// Icon size
ColumnLayout {
visible: Settings.data.dock.enabled
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("settings.dock.appearance.icon-size.label")
description: I18n.tr("settings.dock.appearance.icon-size.description")
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 2
stepSize: 0.01
value: Settings.data.dock.size
onMoved: value => Settings.data.dock.size = value
text: Math.floor(Settings.data.dock.size * 100) + "%"
}
}
NToggle {
visible: Settings.data.dock.enabled
Layout.fillWidth: true
label: I18n.tr("settings.dock.monitors.only-same-output.label")
description: I18n.tr("settings.dock.monitors.only-same-output.description")
checked: Settings.data.dock.onlySameOutput
onToggled: checked => Settings.data.dock.onlySameOutput = checked
}
NToggle {
visible: Settings.data.dock.enabled
Layout.fillWidth: true
label: I18n.tr("settings.dock.appearance.colorize-icons.label")
description: I18n.tr("settings.dock.appearance.colorize-icons.description")
checked: Settings.data.dock.colorizeIcons
onToggled: checked => Settings.data.dock.colorizeIcons = checked
}
NHeader {
visible: Settings.data.dock.enabled
label: I18n.tr("settings.dock.monitors.section.label")
description: I18n.tr("settings.dock.monitors.section.description")
}
Repeater {
visible: Settings.data.dock.enabled
model: Quickshell.screens || []
delegate: NCheckbox {
Layout.fillWidth: true
label: modelData.name || "Unknown"
visible: Settings.data.dock.enabled
description: {
const compositorScale = CompositorService.getDisplayScale(modelData.name)
I18n.tr("system.monitor-description", {
"model": modelData.model,
"width": modelData.width * compositorScale,
"height": modelData.height * compositorScale,
"scale": compositorScale
})
}
checked: (Settings.data.dock.monitors || []).indexOf(modelData.name) !== -1
onToggled: checked => {
if (checked) {
const arr = (Settings.data.dock.monitors || []).slice()
if (arr.indexOf(modelData.name) === -1)
arr.push(modelData.name)
Settings.data.dock.monitors = arr
} else {
Settings.data.dock.monitors = (Settings.data.dock.monitors || []).filter(function (n) {
return n !== modelData.name
})
}
}
}
}
}
}

View File

@@ -199,11 +199,34 @@ ColumnLayout {
visible: filteredWallpapers.length > 0
ScrollView {
id: galleryScroll
anchors.fill: parent
clip: true
ScrollBar.horizontal.policy: ScrollBar.AsNeeded
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
// Enable vertical mouse wheel to scroll the horizontal strip by moving contentX
WheelHandler {
target: galleryScroll.contentItem
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
onWheel: event => {
const flick = galleryScroll.contentItem
if (!flick)
return
const delta = event.pixelDelta.x !== 0 || event.pixelDelta.y !== 0 ? (event.pixelDelta.y !== 0 ? event.pixelDelta.y : event.pixelDelta.x) : (event.angleDelta.y !== 0 ? event.angleDelta.y : event.angleDelta.x)
// Move opposite of wheel to scroll content to the right for wheel down
const step = -delta
const maxX = Math.max(0, flick.contentWidth - flick.width)
let newX = flick.contentX + step
if (newX < 0)
newX = 0
if (newX > maxX)
newX = maxX
flick.contentX = newX
event.accepted = true
}
}
RowLayout {
spacing: Style.marginM
height: parent.height

View File

@@ -23,14 +23,13 @@ NPanel {
draggable: false
property int currentStep: 0
property int totalSteps: 4
property int totalSteps: 5
// Setup wizard data
property string selectedWallpaperDirectory: Settings.defaultWallpapersDirectory
property string selectedWallpaper: ""
property real selectedScaleRatio: 1.0
property string selectedBarPosition: "top"
property bool selectedDimDesktop: true
panelContent: Component {
Item {
@@ -199,7 +198,6 @@ NPanel {
id: step2
selectedScaleRatio: root.selectedScaleRatio
selectedBarPosition: root.selectedBarPosition
selectedDimDesktop: root.selectedDimDesktop
onScaleRatioChanged: function (ratio) {
root.selectedScaleRatio = ratio
root.applyUISettings()
@@ -208,13 +206,14 @@ NPanel {
root.selectedBarPosition = position
root.applyUISettings()
}
onDimDesktopChanged: function (dim) {
root.selectedDimDesktop = dim
root.applyUISettings()
}
}
// Step 3: Appearance - Dark mode and color source
// Step 3: Dock Setup
SetupDockStep {
id: stepDock
}
// Step 4: Appearance - Dark mode and color source
SetupAppearanceStep {
id: step3
}
@@ -248,6 +247,9 @@ NPanel {
}, {
"icon": "settings",
"label": "Customize"
}, {
"icon": "device-desktop",
"label": "Dock"
}, {
"icon": "palette",
"label": "Appearance"
@@ -376,7 +378,6 @@ NPanel {
Settings.data.general.scaleRatio = selectedScaleRatio
Settings.data.bar.position = selectedBarPosition
Settings.data.general.dimDesktop = selectedDimDesktop
Settings.data.setupCompleted = true
Settings.saveImmediate()
@@ -398,7 +399,6 @@ NPanel {
function applyUISettings() {
Settings.data.general.scaleRatio = selectedScaleRatio
Settings.data.bar.position = selectedBarPosition
Settings.data.general.dimDesktop = selectedDimDesktop
}
Component.onCompleted: {
@@ -407,7 +407,6 @@ NPanel {
if (Settings && Settings.data) {
selectedScaleRatio = Settings.data.general.scaleRatio
selectedBarPosition = Settings.data.bar.position
selectedDimDesktop = Settings.data.general.dimDesktop
selectedWallpaperDirectory = Settings.data.wallpaper.directory || Settings.defaultWallpapersDirectory
}
}

View File

@@ -292,9 +292,6 @@ NPanel {
}
wallpapersList = WallpaperService.getWallpapersList(targetScreen.name)
Logger.i("WallpaperPanel", "Got", wallpapersList.length, "wallpapers for screen", targetScreen.name)
if (wallpapersList.length > 0) {
Logger.d("WallpaperPanel", "First 5 wallpapers:", wallpapersList.slice(0, 5))
}
// Pre-compute basenames once for better performance
wallpapersWithNames = wallpapersList.map(function (p) {

View File

@@ -115,7 +115,14 @@ Singleton {
"outputs": [{
"path": "~/.config/walker/themes/noctalia/style.css"
}],
"postProcess": () => `${colorsApplyScript} walker\n`,
"strict": true
},
"code": {
"input": "code.json",
"outputs": [{
"path": "~/.vscode/extensions/hyprluna.hyprluna-theme-1.0.2/themes/hyprluna.json"
}]
}
})

View File

@@ -21,6 +21,7 @@ Singleton {
"DarkMode": darkMode,
"KeepAwake": keepAwakeComponent,
"KeyboardLayout": keyboardLayoutComponent,
"LockKeys": lockKeysComponent,
"MediaMini": mediaMiniComponent,
"Microphone": microphoneComponent,
"NightLight": nightLightComponent,
@@ -52,7 +53,8 @@ Singleton {
},
"AudioVisualizer": {
"allowUserSettings": true,
"width": 200
"width": 200,
"hideWhenIdle": false
},
"Battery": {
"allowUserSettings": true,
@@ -97,6 +99,13 @@ Singleton {
"allowUserSettings": true,
"displayMode": "onhover"
},
"LockKeys": {
"allowUserSettings": true,
"indicatorStyle": "large",
"showCapsLock": true,
"showNumLock": true,
"showScrollLock": true
},
"MediaMini": {
"allowUserSettings": true,
"hideMode": "hidden",
@@ -192,6 +201,9 @@ Singleton {
property Component keepAwakeComponent: Component {
KeepAwake {}
}
property Component lockKeysComponent: Component {
LockKeys {}
}
property Component mediaMiniComponent: Component {
MediaMini {}
}

View File

@@ -68,16 +68,16 @@ Singleton {
function loadFromCache() {
if (cacheAdapter.cachedEvents && cacheAdapter.cachedEvents.length > 0) {
root.events = cacheAdapter.cachedEvents
Logger.i("Calendar", `Loaded ${cacheAdapter.cachedEvents.length} cached event(s)`)
Logger.d("Calendar", `Loaded ${cacheAdapter.cachedEvents.length} cached event(s)`)
}
if (cacheAdapter.cachedCalendars && cacheAdapter.cachedCalendars.length > 0) {
root.calendars = cacheAdapter.cachedCalendars
Logger.i("Calendar", `Loaded ${cacheAdapter.cachedCalendars.length} cached calendar(s)`)
Logger.d("Calendar", `Loaded ${cacheAdapter.cachedCalendars.length} cached calendar(s)`)
}
if (cacheAdapter.lastUpdate) {
Logger.i("Calendar", `Cache last updated: ${cacheAdapter.lastUpdate}`)
Logger.d("Calendar", `Cache last updated: ${cacheAdapter.lastUpdate}`)
}
}
@@ -123,7 +123,7 @@ Singleton {
loadEventsProcess.endTime = Math.floor(endDate.getTime() / 1000)
loadEventsProcess.running = true
Logger.i("Calendar", `Loading events (${daysBehind} days behind, ${daysAhead} days ahead): ${startDate.toLocaleDateString()} to ${endDate.toLocaleDateString()}`)
Logger.d("Calendar", `Loading events (${daysBehind} days behind, ${daysAhead} days ahead): ${startDate.toLocaleDateString()} to ${endDate.toLocaleDateString()}`)
}
// Helper to format date/time
@@ -178,7 +178,7 @@ Singleton {
cacheAdapter.cachedCalendars = result
saveCache()
Logger.i("Calendar", `Found ${result.length} calendar(s)`)
Logger.d("Calendar", `Found ${result.length} calendar(s)`)
// Auto-load events after discovering calendars
// Only load if we have calendars and no cached events
@@ -225,7 +225,7 @@ Singleton {
cacheAdapter.lastUpdate = new Date().toISOString()
saveCache()
Logger.i("Calendar", `Loaded ${result.length} event(s)`)
Logger.d("Calendar", `Loaded ${result.length} event(s)`)
} catch (e) {
Logger.d("Calendar", "Failed to parse events: " + e)
root.lastError = "Failed to parse events"
@@ -233,7 +233,7 @@ Singleton {
// Fall back to cached events if available
if (cacheAdapter.cachedEvents.length > 0) {
root.events = cacheAdapter.cachedEvents
Logger.i("Calendar", "Using cached events")
Logger.d("Calendar", "Using cached events")
}
}
}
@@ -250,7 +250,7 @@ Singleton {
// Fall back to cached events if available
if (cacheAdapter.cachedEvents.length > 0) {
root.events = cacheAdapter.cachedEvents
Logger.i("Calendar", "Using cached events due to error")
Logger.d("Calendar", "Using cached events due to error")
}
}
}

View File

@@ -91,6 +91,31 @@ Singleton {
schemeReader.path = filePath
}
function setPredefinedScheme(schemeName) {
Logger.i("ColorScheme", "Attempting to set predefined scheme to:", schemeName)
var resolvedPath = resolveSchemePath(schemeName)
var basename = getBasename(schemeName)
// Check if the scheme actually exists in the loaded schemes list
var schemeExists = false
for (var i = 0; i < schemes.length; i++) {
if (getBasename(schemes[i]) === basename) {
schemeExists = true
break
}
}
if (schemeExists) {
Settings.data.colorSchemes.predefinedScheme = basename
applyScheme(schemeName)
ToastService.showNotice("Color Scheme", `Set to ${basename}`)
} else {
Logger.e("ColorScheme", "Scheme not found:", schemeName)
ToastService.showError("Color Scheme", `Scheme '${basename}' not found!`)
}
}
Process {
id: findProcess
running: false
@@ -162,7 +187,7 @@ Singleton {
// Check if any templates are enabled
function hasEnabledTemplates() {
return Settings.data.templates.gtk || Settings.data.templates.qt || Settings.data.templates.kitty || Settings.data.templates.ghostty || Settings.data.templates.foot || Settings.data.templates.fuzzel || Settings.data.templates.discord || Settings.data.templates.discord_vesktop || Settings.data.templates.discord_webcord
|| Settings.data.templates.discord_armcord || Settings.data.templates.discord_equibop || Settings.data.templates.discord_lightcord || Settings.data.templates.discord_dorion || Settings.data.templates.pywalfox
|| Settings.data.templates.discord_armcord || Settings.data.templates.discord_equibop || Settings.data.templates.discord_lightcord || Settings.data.templates.discord_dorion || Settings.data.templates.pywalfox || Settings.data.templates.vicinae || Settings.data.templates.walker
}
// Writer to colors.json using a JsonAdapter for safety

View File

@@ -126,6 +126,13 @@ Item {
}
}
IpcHandler {
target: "colorScheme"
function set(schemeName: string) {
ColorSchemeService.setPredefinedScheme(schemeName)
}
}
IpcHandler {
target: "volume"
function increase() {

View File

@@ -0,0 +1,85 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
Singleton {
id: root
property bool capsLockOn: false
property bool numLockOn: false
property bool scrollLockOn: false
signal capsLockChanged(bool active)
signal numLockChanged(bool active)
signal scrollLockChanged(bool active)
Process {
id: stateCheckProcess
property string checkCommand: " \
caps=0; cat /sys/class/leds/input*::capslock/brightness 2>/dev/null | grep -q 1 && caps=1; echo \"caps:${caps}\"; \
num=0; cat /sys/class/leds/input*::numlock/brightness 2>/dev/null | grep -q 1 && num=1; echo \"num:${num}\"; \
scroll=0; cat /sys/class/leds/input*::scrolllock/brightness 2>/dev/null | grep -q 1 && scroll=1; echo \"scroll:${scroll}\"; \
"
command: ["sh", "-c", stateCheckProcess.checkCommand]
stdout: StdioCollector {
onStreamFinished: {
var lines = this.text.trim().split('\n')
for (var i = 0; i < lines.length; i++) {
var parts = lines[i].split(':')
if (parts.length === 2) {
var key = parts[0]
var newState = (parts[1] === '1')
if (key === "caps") {
if (root.capsLockOn !== newState) {
root.capsLockOn = newState
root.capsLockChanged(newState)
Logger.i("LockKeysService", "Caps Lock:", capsLockOn)
}
} else if (key === "num") {
if (root.numLockOn !== newState) {
root.numLockOn = newState
root.numLockChanged(newState)
Logger.i("LockKeysService", "Num Lock:", numLockOn)
}
} else if (key === "scroll") {
if (root.scrollLockOn !== newState) {
root.scrollLockOn = newState
root.scrollLockChanged(newState)
Logger.i("LockKeysService", "Scroll Lock:", scrollLockOn)
}
}
}
}
}
}
stderr: StdioCollector {
onStreamFinished: {
if (this.text.trim().length > 0)
Logger.i("LockKeysService", "Error running state check:", this.text.trim())
}
}
}
Timer {
id: pollTimer
interval: 200
running: true
repeat: true
onTriggered: {
if (!stateCheckProcess.running) {
stateCheckProcess.running = true
}
}
}
Component.onCompleted: {
Logger.i("LockKeysService", "Service started, performing initial state check.")
stateCheckProcess.running = true
}
}

View File

@@ -225,6 +225,13 @@ Singleton {
}],
"input": "vesktop.css",
"requiresThemesFolder": true
}, {
"name": "code",
"templates": [{
"version": "code",
"output": "~/.vscode/extensions/hyprluna.hyprluna-theme-1.0.2/themes/hyprluna.json"
}],
"input": "code.json"
}]
// --------------------------------

View File

@@ -171,6 +171,21 @@ Singleton {
}
}
property bool autoSwitchingPaused: false
function switchToPlayer(index) {
let availablePlayers = getAvailablePlayers()
if (index >= 0 && index < availablePlayers.length) {
let newPlayer = availablePlayers[index]
if (newPlayer !== currentPlayer) {
currentPlayer = newPlayer
selectedPlayerIndex = index
currentPosition = currentPlayer ? currentPlayer.position : 0
Logger.d("Media", "Manually switched to player " + currentPlayer.identity)
}
}
}
// Switch to the most recently active player
function updateCurrentPlayer() {
let newPlayer = findActivePlayer()
@@ -289,6 +304,9 @@ Singleton {
repeat: true
running: true
onTriggered: {
Logger.d("MediaService", "playerStateMonitor triggered. autoSwitchingPaused: " + root.autoSwitchingPaused)
if (autoSwitchingPaused)
return
// Only update if we don't have a playing player or if current player is paused
if (!currentPlayer || !currentPlayer.isPlaying || currentPlayer.playbackState !== MprisPlaybackState.Playing) {
updateCurrentPlayer()

View File

@@ -21,6 +21,7 @@ Singleton {
property bool gpuScreenRecorderAvailable: false
property bool wlsunsetAvailable: false
property bool app2unitAvailable: false
property bool codeAvailable: false
// Discord client auto-detection
property var availableDiscordClients: []
@@ -99,11 +100,12 @@ Singleton {
"ghosttyAvailable": ["which", "ghostty"],
"footAvailable": ["which", "foot"],
"fuzzelAvailable": ["which", "fuzzel"],
"vicinaeAvailable": ["which", "vicinae"],
"vicinaeAvailable": ["sh", "-c", "command -v vicinae >/dev/null 2>&1 || (IFS=:; find $PATH -maxdepth 1 -iname 'vicinae*.appimage' -type f -executable 2>/dev/null | grep -q .)"],
"walkerAvailable": ["which", "walker"],
"app2unitAvailable": ["which", "app2unit"],
"gpuScreenRecorderAvailable": ["sh", "-c", "command -v gpu-screen-recorder >/dev/null 2>&1 || (command -v flatpak >/dev/null 2>&1 && flatpak list --app | grep -q 'com.dec05eba.gpu_screen_recorder')"],
"wlsunsetAvailable": ["which", "wlsunset"]
"wlsunsetAvailable": ["which", "wlsunset"],
"codeAvailable": ["which", "code"]
})
// Internal tracking

View File

@@ -8,7 +8,7 @@ Singleton {
id: root
// Public properties
property string baseVersion: "2.20.0"
property string baseVersion: "2.21.1"
property bool isDevelopment: false
property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}`

View File

@@ -372,12 +372,20 @@ Singleton {
}
} else {
// Use FolderListModel (non-recursive)
// Force refresh by toggling each scanner's currentDirectory
for (var i = 0; i < wallpaperScanners.count; i++) {
var scanner = wallpaperScanners.objectAt(i)
if (scanner) {
var currentFolder = scanner.folder
scanner.folder = ""
scanner.folder = currentFolder
// Capture scanner in closure
(function (s) {
var directory = root.getMonitorDirectory(s.screenName)
// Trigger a change by setting to /tmp (always exists) then back to the actual directory
// Note: This causes harmless Qt warnings (QTBUG-52262) but is necessary to force FolderListModel to re-scan
s.currentDirectory = "/tmp"
Qt.callLater(function () {
s.currentDirectory = directory
})
})(scanner)
}
}
}
@@ -395,6 +403,15 @@ Singleton {
return
}
// Cancel any existing scan for this screen
if (recursiveProcesses[screenName]) {
Logger.d("Wallpaper", "Cancelling existing scan for", screenName)
recursiveProcesses[screenName].running = false
recursiveProcesses[screenName].destroy()
delete recursiveProcesses[screenName]
scanningCount--
}
scanningCount++
Logger.i("Wallpaper", "Starting recursive scan for", screenName, "in", directory)
@@ -434,7 +451,7 @@ Singleton {
Logger.i("Wallpaper", "Recursive scan completed for", screenName, "found", files.length, "files")
wallpaperListChanged(screenName, files.length)
} else {
Logger.e("Wallpaper", "Recursive scan failed for", screenName, "exit code:", exitCode, "stderr:", processObject.stderr.text)
Logger.w("Wallpaper", "Recursive scan failed for", screenName, "exit code:", exitCode, "(directory might not exist)")
wallpaperLists[screenName] = []
wallpaperListChanged(screenName, 0)
}

View File

@@ -8,9 +8,6 @@ import qs.Services
Rectangle {
id: root
implicitWidth: childrenRect.width
implicitHeight: childrenRect.height
color: Color.mSurfaceVariant
radius: Style.radiusM
border.color: Color.mOutline

View File

@@ -5,16 +5,16 @@ import qs.Commons
Rectangle {
id: root
property date sampleDate: new Date() // Dec 25, 2023, 2:30:45.123 PM
signal tokenClicked(string token)
color: Color.mSurface
border.color: Color.mOutline
border.width: Style.borderS
radius: Style.radiusM
property date sampleDate: new Date() // Dec 25, 2023, 2:30:45.123 PM
// Signal emitted when a token is clicked
signal tokenClicked(string token)
ColumnLayout {
id: column
anchors.fill: parent
@@ -312,7 +312,7 @@ Rectangle {
NText {
anchors.centerIn: parent
text: Qt.locale().toString(root.sampleDate, modelData.token)
text: I18n.locale.toString(root.sampleDate, modelData.token)
color: tokenMouseArea.containsMouse ? Color.mOnPrimary : Color.mSurfaceVariant
pointSize: Style.fontSizeS

View File

@@ -9,9 +9,16 @@ Loader {
property ShellScreen screen
readonly property real opacityThreshold: 0.33
property bool attachedToBar: (Settings.data.ui.panelsAttachedToBar && Settings.data.bar.backgroundOpacity > opacityThreshold)
property bool useOverlay: Settings.data.ui.panelsOverlayLayer
property Component panelContent: null
// Panel size properties. Can be set directly on NPanel, or dynamically by the content.
// For dynamic sizing, the content should expose contentPreferredWidth, contentPreferredHeight,
// contentPreferredWidthRatio, or contentPreferredHeightRatio properties.
// Changes to these properties will be animated smoothly (except during panel dragging).
property real preferredWidth: 700
property real preferredHeight: 900
property real preferredWidthRatio
@@ -39,10 +46,20 @@ Loader {
property bool backgroundClickEnabled: true
// Animation properties
readonly property real originalScale: 0.0
property real scaleValue: originalScale
property real panelBackgroundOpacity: 0
property real panelContentOpacity: 0
property real dimmingOpacity: 0
readonly property string barPosition: Settings.data.bar.position
readonly property bool barIsVertical: barPosition === "left" || barPosition === "right"
readonly property real verticalBarWidth: Style.barHeight
// Effective anchor properties - combines explicit anchors with implicit anchoring from useButtonPosition
readonly property bool effectivePanelAnchorTop: panelAnchorTop || (useButtonPosition && barPosition === "top")
readonly property bool effectivePanelAnchorBottom: panelAnchorBottom || (useButtonPosition && barPosition === "bottom")
readonly property bool effectivePanelAnchorLeft: panelAnchorLeft || (useButtonPosition && barPosition === "left")
readonly property bool effectivePanelAnchorRight: panelAnchorRight || (useButtonPosition && barPosition === "right")
signal opened
signal closed
@@ -97,7 +114,8 @@ Loader {
// -----------------------------------------
function close() {
dimmingOpacity = 0
scaleValue = originalScale
panelBackgroundOpacity = 0
panelContentOpacity = 0
root.closed()
active = false
useButtonPosition = false
@@ -132,10 +150,7 @@ Loader {
PanelWindow {
id: panelWindow
readonly property string barPosition: Settings.data.bar.position
readonly property bool isVertical: barPosition === "left" || barPosition === "right"
readonly property bool barIsVisible: (screen !== null) && (Settings.data.bar.monitors.includes(screen.name) || (Settings.data.bar.monitors.length === 0))
readonly property real verticalBarWidth: Style.barHeight
Component.onCompleted: {
Logger.d("NPanel", "Opened", root.objectName, "on", screen.name)
@@ -155,8 +170,7 @@ Loader {
}
}
visible: true
color: Settings.data.general.dimDesktop ? Qt.alpha(Color.mShadow, dimmingOpacity) : Color.transparent
color: Color.transparent
WlrLayershell.exclusionMode: ExclusionMode.Ignore
WlrLayershell.namespace: "noctalia-panel"
@@ -195,12 +209,77 @@ Loader {
}
// The actual panel's content
Rectangle {
NShapedRectangle {
id: panelBackground
color: panelBackgroundColor
radius: Style.radiusL
border.color: panelBorderColor
border.width: Style.borderS
backgroundColor: (attachedToBar && (topLeftInverted || topRightInverted || bottomLeftInverted || bottomRightInverted)) ? Qt.alpha(panelBackgroundColor, Settings.data.bar.backgroundOpacity) : panelBackgroundColor
topLeftRadius: Style.radiusL
topRightRadius: Style.radiusL
bottomLeftRadius: Style.radiusL
bottomRightRadius: Style.radiusL
// Set inverted corners based on panel anchors and bar position
// Top-left corner
topLeftInverted: {
if (!attachedToBar)
return false
// Inverted if panel is anchored to top edge (bar is at top)
if (effectivePanelAnchorTop)
return true
// Or if panel is anchored to left edge (bar is at left)
if (effectivePanelAnchorLeft)
return true
return false
}
topLeftInvertedDirection: effectivePanelAnchorTop ? "horizontal" : "vertical"
// Top-right corner
topRightInverted: {
if (!attachedToBar)
return false
// Inverted if panel is anchored to top edge (bar is at top)
if (effectivePanelAnchorTop)
return true
// Or if panel is anchored to right edge (bar is at right)
if (effectivePanelAnchorRight)
return true
return false
}
topRightInvertedDirection: effectivePanelAnchorTop ? "horizontal" : "vertical"
// Bottom-left corner
bottomLeftInverted: {
if (!attachedToBar)
return false
// Inverted if panel is anchored to bottom edge (bar is at bottom)
if (effectivePanelAnchorBottom)
return true
// Or if panel is anchored to left edge (bar is at left)
if (effectivePanelAnchorLeft)
return true
return false
}
bottomLeftInvertedDirection: effectivePanelAnchorBottom ? "horizontal" : "vertical"
// Bottom-right corner
bottomRightInverted: {
if (!attachedToBar)
return false
// Inverted if panel is anchored to bottom edge (bar is at bottom)
if (effectivePanelAnchorBottom)
return true
// Or if panel is anchored to right edge (bar is at right)
if (effectivePanelAnchorRight)
return true
return false
}
bottomRightInvertedDirection: effectivePanelAnchorBottom ? "horizontal" : "vertical"
// Dragging support
property bool draggable: root.draggable
@@ -209,30 +288,47 @@ Loader {
property real manualY: 0
width: {
var w
if (preferredWidthRatio !== undefined) {
w = Math.round(Math.max(screen?.width * preferredWidthRatio, preferredWidth))
if (root.preferredWidthRatio !== undefined) {
w = Math.round(Math.max(screen?.width * root.preferredWidthRatio, root.preferredWidth))
} else {
w = preferredWidth
w = root.preferredWidth
}
// Clamp width so it is never bigger than the screen
return Math.min(w, screen?.width - Style.marginL * 2)
}
height: {
var h
if (preferredHeightRatio !== undefined) {
h = Math.round(Math.max(screen?.height * preferredHeightRatio, preferredHeight))
if (root.preferredHeightRatio !== undefined) {
h = Math.round(Math.max(screen?.height * root.preferredHeightRatio, root.preferredHeight))
} else {
h = preferredHeight
h = root.preferredHeight
}
// Clamp width so it is never bigger than the screen
// Clamp height so it is never bigger than the screen
return Math.min(h, screen?.height - Style.barHeight - Style.marginL * 2)
}
scale: root.scaleValue
opacity: root.panelBackgroundOpacity
x: isDragged ? manualX : calculatedX
y: isDragged ? manualY : calculatedY
// Animate width and height changes smoothly
Behavior on width {
enabled: !panelBackground.isDragged
NumberAnimation {
duration: Style.animationFast
easing.type: Easing.InOutQuad
}
}
Behavior on height {
enabled: !panelBackground.isDragged
NumberAnimation {
duration: Style.animationFast
easing.type: Easing.InOutQuad
}
}
// ---------------------------------------------
// Does not account for corners are they are negligible and helps keep the code clean.
// ---------------------------------------------
@@ -240,11 +336,12 @@ Loader {
if (!barIsVisible) {
return 0
}
switch (barPosition) {
case "top":
return (Style.barHeight + Style.marginS) + (Settings.data.bar.floating ? Settings.data.bar.marginVertical * Style.marginXL : 0)
return (Style.barHeight + (attachedToBar ? 0 : Style.marginS)) + (Settings.data.bar.floating ? Math.round(Settings.data.bar.marginVertical * Style.marginXL) : 0)
default:
return Style.marginS
return attachedToBar ? 0 : Style.marginS
}
}
@@ -254,9 +351,9 @@ Loader {
}
switch (barPosition) {
case "bottom":
return (Style.barHeight + Style.marginS) + (Settings.data.bar.floating ? Settings.data.bar.marginVertical * Style.marginXL : 0)
return (Style.barHeight + (attachedToBar ? 0 : Style.marginS)) + (Settings.data.bar.floating ? Math.round(Settings.data.bar.marginVertical * Style.marginXL) : 0)
default:
return Style.marginS
return attachedToBar ? 0 : Style.marginS
}
}
@@ -266,9 +363,9 @@ Loader {
}
switch (barPosition) {
case "left":
return (Style.barHeight + Style.marginS) + (Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0)
return (Style.barHeight + (attachedToBar ? 0 : Style.marginS)) + (Settings.data.bar.floating ? Math.round(Settings.data.bar.marginHorizontal * Style.marginXL) : 0)
default:
return Style.marginS
return attachedToBar ? 0 : Style.marginS
}
}
@@ -278,9 +375,9 @@ Loader {
}
switch (barPosition) {
case "right":
return (Style.barHeight + Style.marginS) + (Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0)
return (Style.barHeight + (attachedToBar ? 0 : Style.marginS)) + (Settings.data.bar.floating ? Math.round(Settings.data.bar.marginHorizontal * Style.marginXL) : 0)
default:
return Style.marginS
return attachedToBar ? 0 : Style.marginS
}
}
@@ -300,7 +397,7 @@ Loader {
}
// No fixed anchoring
if (isVertical) {
if (barIsVertical) {
// Vertical bar
if (barPosition === "right") {
// To the left of the right bar
@@ -317,6 +414,11 @@ Loader {
// Keep panel within screen bounds
var maxX = panelWindow.width - panelBackground.width - marginRight
var minX = marginLeft
if (Settings.data.bar.floating) {
maxX -= Settings.data.bar.marginHorizontal * Style.marginXL * 10
minX += Settings.data.bar.marginHorizontal * Style.marginXL * 10
}
return Math.round(Math.max(minX, Math.min(targetX, maxX)))
} else {
// Fallback to center horizontally
@@ -341,7 +443,7 @@ Loader {
}
// No fixed anchoring
if (isVertical) {
if (barIsVertical) {
// Vertical bar
if (useButtonPosition) {
// Position panel relative to button
@@ -349,6 +451,12 @@ Loader {
// Keep panel within screen bounds
var maxY = panelWindow.height - panelBackground.height - marginBottom
var minY = marginTop
if (Settings.data.bar.floating) {
maxY -= Settings.data.bar.marginHorizontal * Style.marginXL * 10
minY += Settings.data.bar.marginHorizontal * Style.marginXL * 10
}
return Math.round(Math.max(minY, Math.min(targetY, maxY)))
} else {
// Fallback to center vertically
@@ -368,7 +476,28 @@ Loader {
// Animate in when component is completed
Component.onCompleted: {
root.scaleValue = 1.0
// Start invisible
// Use a timer to delay the animation start, allowing QML to properly set up initial state
fadeInTimer.start()
}
Timer {
id: fadeInTimer
interval: 1
repeat: false
onTriggered: {
// Fade in background
root.panelBackgroundOpacity = 1.0
}
}
// Timer to fade in content after slide animation completes
Timer {
id: contentFadeInTimer
interval: Style.animationFast
repeat: false
running: true
onTriggered: root.panelContentOpacity = 1.0
}
// Reset drag position when panel closes
@@ -384,17 +513,10 @@ Loader {
anchors.fill: parent
}
// Animation behaviors
Behavior on scale {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutExpo
}
}
// Animation behavior
Behavior on opacity {
NumberAnimation {
duration: Style.animationNormal
duration: Style.animationFast
easing.type: Easing.OutQuad
}
}
@@ -403,6 +525,33 @@ Loader {
id: panelContentLoader
anchors.fill: parent
sourceComponent: root.panelContent
opacity: root.panelContentOpacity
Behavior on opacity {
NumberAnimation {
duration: Style.animationFast
easing.type: Easing.OutQuad
}
}
// Allow content to dynamically resize the panel
onItemChanged: {
if (item) {
// Bind to content's preferredWidth/Height if they exist
if (item.hasOwnProperty('contentPreferredWidth')) {
root.preferredWidth = Qt.binding(() => item.contentPreferredWidth)
}
if (item.hasOwnProperty('contentPreferredHeight')) {
root.preferredHeight = Qt.binding(() => item.contentPreferredHeight)
}
if (item.hasOwnProperty('contentPreferredWidthRatio')) {
root.preferredWidthRatio = Qt.binding(() => item.contentPreferredWidthRatio)
}
if (item.hasOwnProperty('contentPreferredHeightRatio')) {
root.preferredHeightRatio = Qt.binding(() => item.contentPreferredHeightRatio)
}
}
}
}
// Handle drag move on the whole panel area
@@ -459,9 +608,9 @@ Loader {
anchors.fill: parent
anchors.margins: 0
color: Color.transparent
border.color: Color.mPrimary
border.width: Style.borderL
radius: parent.radius
border.color: Color.mTertiary
border.width: Style.borderM
radius: Style.radiusL
visible: panelBackground.isDragged && dragHandler.active
opacity: 0.8
z: 3000
@@ -471,9 +620,9 @@ Loader {
anchors.fill: parent
anchors.margins: 0
color: Color.transparent
border.color: Color.mPrimary
border.color: Color.mTertiary
border.width: Style.borderS
radius: parent.radius
radius: Style.radiusL
opacity: 0.3
}
}

View File

@@ -236,12 +236,9 @@ RowLayout {
Repeater {
model: typeof badgeLocations !== 'undefined' ? badgeLocations : []
delegate: NBox {
delegate: Item {
width: Style.baseWidgetSize * 0.7
height: Style.baseWidgetSize * 0.7
color: "transparent"
radius: Style.radiusS
border.width: 0
NText {
anchors.centerIn: parent

View File

@@ -233,7 +233,7 @@ NBox {
acceptedButtons: Qt.RightButton
z: -1 // Below the buttons but above background
onClicked: mouse => {
onPressed: mouse => {
if (mouse.button === Qt.RightButton) {
// Check if click is not on the buttons area
const localX = mouse.x

View File

@@ -0,0 +1,189 @@
import QtQuick
import qs.Commons
Item {
id: root
// Corner radius properties
property real topLeftRadius: 0
property real topRightRadius: 0
property real bottomLeftRadius: 0
property real bottomRightRadius: 0
// Inverted corner properties (concave instead of convex)
property bool topLeftInverted: false
property bool topRightInverted: false
property bool bottomLeftInverted: false
property bool bottomRightInverted: false
// Direction for inverted corners: "horizontal" or "vertical"
// horizontal: curves left/right (extends beyond left/right edges)
// vertical: curves up/down (extends beyond top/bottom edges)
property string topLeftInvertedDirection: "horizontal" // default: curves left
property string topRightInvertedDirection: "horizontal" // default: curves right
property string bottomLeftInvertedDirection: "horizontal" // default: curves left
property string bottomRightInvertedDirection: "horizontal" // default: curves right
// Background color
property color backgroundColor: "white"
// Check if any corner is inverted
readonly property bool hasInvertedCorners: topLeftInverted || topRightInverted || bottomLeftInverted || bottomRightInverted
// Calculate padding needed for inverted corners based on their direction
readonly property real topPadding: Math.max((topLeftInverted && topLeftInvertedDirection === "vertical") ? topLeftRadius : 0, (topRightInverted && topRightInvertedDirection === "vertical") ? topRightRadius : 0)
readonly property real bottomPadding: Math.max((bottomLeftInverted && bottomLeftInvertedDirection === "vertical") ? bottomLeftRadius : 0, (bottomRightInverted && bottomRightInvertedDirection === "vertical") ? bottomRightRadius : 0)
readonly property real leftPadding: Math.max((topLeftInverted && topLeftInvertedDirection === "horizontal") ? topLeftRadius : 0, (bottomLeftInverted && bottomLeftInvertedDirection === "horizontal") ? bottomLeftRadius : 0)
readonly property real rightPadding: Math.max((topRightInverted && topRightInvertedDirection === "horizontal") ? topRightRadius : 0, (bottomRightInverted && bottomRightInvertedDirection === "horizontal") ? bottomRightRadius : 0)
// Simple rectangle for non-inverted corners (better performance)
Rectangle {
id: simpleBackground
anchors.fill: parent
color: root.backgroundColor
radius: topLeftRadius // Use topLeftRadius as default
border.width: Style.borderS
border.color: Color.mOutline
visible: !root.hasInvertedCorners
topLeftRadius: root.topLeftRadius
topRightRadius: root.topRightRadius
bottomLeftRadius: root.bottomLeftRadius
bottomRightRadius: root.bottomRightRadius
}
// Background with custom corners (for inverted corners)
Canvas {
id: background
anchors.fill: parent
anchors.topMargin: -root.topPadding
anchors.bottomMargin: -root.bottomPadding
anchors.leftMargin: -root.leftPadding
anchors.rightMargin: -root.rightPadding
visible: root.hasInvertedCorners
onPaint: {
var ctx = getContext("2d")
ctx.reset()
// Adjust coordinates to account for inverted corner padding
var x = root.leftPadding
var y = root.topPadding
var w = width - root.leftPadding - root.rightPadding
var h = height - root.topPadding - root.bottomPadding
ctx.fillStyle = root.backgroundColor
ctx.beginPath()
// Start from top left
if (topLeftInverted) {
if (topLeftInvertedDirection === "vertical") {
ctx.moveTo(x, y)
} else {
ctx.moveTo(x + topLeftRadius, y)
}
} else {
ctx.moveTo(x + topLeftRadius, y)
}
// Top edge and top right corner
if (topRightInverted) {
if (topRightInvertedDirection === "horizontal") {
// Curves to the right
ctx.lineTo(x + w, y)
ctx.lineTo(x + w + topRightRadius, y)
ctx.quadraticCurveTo(x + w, y, x + w, y + topRightRadius)
} else {
// Curves upward
ctx.lineTo(x + w, y)
ctx.lineTo(x + w, y - topRightRadius)
ctx.quadraticCurveTo(x + w, y, x + w - topRightRadius, y)
ctx.lineTo(x + w, y)
ctx.lineTo(x + w, y + topRightRadius)
}
} else {
ctx.lineTo(x + w - topRightRadius, y)
ctx.arcTo(x + w, y, x + w, y + topRightRadius, topRightRadius)
}
// Right edge and bottom right corner
if (bottomRightInverted) {
if (bottomRightInvertedDirection === "horizontal") {
// Curves to the right
ctx.lineTo(x + w, y + h - bottomRightRadius)
ctx.quadraticCurveTo(x + w, y + h, x + w + bottomRightRadius, y + h)
ctx.lineTo(x + w, y + h)
ctx.lineTo(x + w - bottomRightRadius, y + h)
} else {
// Curves downward
ctx.lineTo(x + w, y + h)
ctx.lineTo(x + w, y + h + bottomRightRadius)
ctx.quadraticCurveTo(x + w, y + h, x + w - bottomRightRadius, y + h)
}
} else {
ctx.lineTo(x + w, y + h - bottomRightRadius)
ctx.arcTo(x + w, y + h, x + w - bottomRightRadius, y + h, bottomRightRadius)
}
// Bottom edge and bottom left corner
if (bottomLeftInverted) {
if (bottomLeftInvertedDirection === "horizontal") {
// Curves to the left
ctx.lineTo(x + bottomLeftRadius, y + h)
ctx.lineTo(x - bottomLeftRadius, y + h)
ctx.quadraticCurveTo(x, y + h, x, y + h - bottomLeftRadius)
} else {
// Curves downward
ctx.lineTo(x, y + h)
ctx.lineTo(x, y + h + bottomLeftRadius)
ctx.quadraticCurveTo(x, y + h, x + bottomLeftRadius, y + h)
ctx.lineTo(x, y + h)
ctx.lineTo(x, y + h - bottomLeftRadius)
}
} else {
ctx.lineTo(x + bottomLeftRadius, y + h)
ctx.arcTo(x, y + h, x, y + h - bottomLeftRadius, bottomLeftRadius)
}
// Left edge and back to top left corner
if (topLeftInverted) {
if (topLeftInvertedDirection === "horizontal") {
// Curves to the left
ctx.lineTo(x, y + topLeftRadius)
ctx.quadraticCurveTo(x, y, x - topLeftRadius, y)
ctx.lineTo(x, y)
ctx.lineTo(x + topLeftRadius, y)
} else {
// Curves upward
ctx.lineTo(x, y + topLeftRadius)
ctx.lineTo(x, y)
ctx.lineTo(x, y - topLeftRadius)
ctx.quadraticCurveTo(x, y, x + topLeftRadius, y)
}
} else {
ctx.lineTo(x, y + topLeftRadius)
ctx.arcTo(x, y, x + topLeftRadius, y, topLeftRadius)
}
ctx.closePath()
ctx.fill()
}
}
// Trigger repaint when properties change
onTopLeftRadiusChanged: background.requestPaint()
onTopRightRadiusChanged: background.requestPaint()
onBottomLeftRadiusChanged: background.requestPaint()
onBottomRightRadiusChanged: background.requestPaint()
onTopLeftInvertedChanged: background.requestPaint()
onTopRightInvertedChanged: background.requestPaint()
onBottomLeftInvertedChanged: background.requestPaint()
onBottomRightInvertedChanged: background.requestPaint()
onTopLeftInvertedDirectionChanged: background.requestPaint()
onTopRightInvertedDirectionChanged: background.requestPaint()
onBottomLeftInvertedDirectionChanged: background.requestPaint()
onBottomRightInvertedDirectionChanged: background.requestPaint()
onBackgroundColorChanged: background.requestPaint()
onWidthChanged: background.requestPaint()
onHeightChanged: background.requestPaint()
}

View File

@@ -69,6 +69,7 @@ RowLayout {
}
MouseArea {
enabled: root.enabled
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true

14
flake.lock generated
View File

@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1756125398,
"narHash": "sha256-XexyKZpf46cMiO5Vbj+dWSAXOnr285GHsMch8FBoHbc=",
"lastModified": 1761672384,
"narHash": "sha256-o9KF3DJL7g7iYMZq9SWgfS1BFlNbsm6xplRjVlOCkXI=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "3b9f00d7a7bf68acd4c4abb9d43695afb04e03a5",
"rev": "08dacfca559e1d7da38f3cf05f1f45ee9bfd213c",
"type": "github"
},
"original": {
@@ -23,11 +23,11 @@
]
},
"locked": {
"lastModified": 1753595452,
"narHash": "sha256-vqkSDvh7hWhPvNjMjEDV4KbSCv2jyl2Arh73ZXe274k=",
"lastModified": 1761821581,
"narHash": "sha256-nLuc6jA7z+H/6bHPEBSOYPbz7RtvNCZiTKmYItJuBmM=",
"ref": "refs/heads/master",
"rev": "a5431dd02dc23d9ef1680e67777fed00fe5f7cda",
"revCount": 665,
"rev": "db1777c20b936a86528c1095cbcb1ebd92801402",
"revCount": 699,
"type": "git",
"url": "https://git.outfoxxed.me/outfoxxed/quickshell"
},

View File

@@ -178,6 +178,7 @@ ShellRoot {
id: wallpaperPanel
objectName: "wallpaperPanel"
}
BatteryPanel {
id: batteryPanel
objectName: "batteryPanel"