waffles: polkit

This commit is contained in:
end-4
2025-12-08 10:34:05 +01:00
parent 1c8339df10
commit 8b8ac44852
19 changed files with 394 additions and 79 deletions
@@ -0,0 +1,4 @@
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#000000">
<path d="M3 5.75C3 5.33579 3.33579 5 3.75 5C6.41341 5 9.00797 4.05652 11.55 2.15C11.8167 1.95 12.1833 1.95 12.45 2.15C14.992 4.05652 17.5866 5 20.25 5C20.6642 5 21 5.33579 21 5.75V11C21 16.0012 18.0424 19.6757 12.2749 21.9478C12.0982 22.0174 11.9018 22.0174 11.7251 21.9478C5.95756 19.6757 3 16.0012 3 11V5.75Z" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 473 B

@@ -0,0 +1,4 @@
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#000000">
<path d="M3 5.75C3 5.33579 3.33579 5 3.75 5C6.41341 5 9.00797 4.05652 11.55 2.15C11.8167 1.95 12.1833 1.95 12.45 2.15C14.992 4.05652 17.5866 5 20.25 5C20.6642 5 21 5.33579 21 5.75V11C21 11.1813 20.9961 11.3609 20.9883 11.5387C20.3539 10.8973 19.4734 10.5 18.5 10.5C16.567 10.5 15 12.067 15 14V14.05C13.8589 14.2816 13 15.2905 13 16.5V21.5C13 21.5492 13.0014 21.598 13.0042 21.6465C12.7663 21.7496 12.5232 21.85 12.2749 21.9478C12.0982 22.0174 11.9018 22.0174 11.7251 21.9478C5.95756 19.6757 3 16.0012 3 11V5.75ZM16 15V14C16 12.6193 17.1193 11.5 18.5 11.5C19.8807 11.5 21 12.6193 21 14V15H21.5C22.3284 15 23 15.6716 23 16.5V21.5C23 22.3284 22.3284 23 21.5 23H15.5C14.6716 23 14 22.3284 14 21.5V16.5C14 15.6716 14.6716 15 15.5 15H16ZM17.5 14V15H19.5V14C19.5 13.4477 19.0523 13 18.5 13C17.9477 13 17.5 13.4477 17.5 14ZM19.5 19C19.5 18.4477 19.0523 18 18.5 18C17.9477 18 17.5 18.4477 17.5 19C17.5 19.5523 17.9477 20 18.5 20C19.0523 20 19.5 19.5523 19.5 19Z" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,4 @@
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#000000">
<path d="M3 5.75C3 5.33579 3.33579 5 3.75 5C6.41341 5 9.00797 4.05652 11.55 2.15C11.8167 1.95 12.1833 1.95 12.45 2.15C14.992 4.05652 17.5866 5 20.25 5C20.6642 5 21 5.33579 21 5.75V11C21 11.1813 20.9961 11.3609 20.9883 11.5387C20.5804 11.1263 20.0707 10.8148 19.5 10.6449V6.47793C16.9227 6.32585 14.4192 5.38829 12 3.67782C9.58084 5.38829 7.07735 6.32585 4.5 6.47793V11C4.5 15.2556 6.95337 18.3789 12 20.4419C12.3455 20.3007 12.6788 20.1545 13 20.0033V21.5C13 21.5492 13.0014 21.598 13.0042 21.6465C12.7663 21.7496 12.5232 21.85 12.2749 21.9478C12.0982 22.0174 11.9018 22.0174 11.7251 21.9478C5.95756 19.6757 3 16.0012 3 11V5.75ZM16 15V14C16 12.6193 17.1193 11.5 18.5 11.5C19.8807 11.5 21 12.6193 21 14V15H21.5C22.3284 15 23 15.6716 23 16.5V21.5C23 22.3284 22.3284 23 21.5 23H15.5C14.6716 23 14 22.3284 14 21.5V16.5C14 15.6716 14.6716 15 15.5 15H16ZM17.5 14V15H19.5V14C19.5 13.4477 19.0523 13 18.5 13C17.9477 13 17.5 13.4477 17.5 14ZM19.5 19C19.5 18.4477 19.0523 18 18.5 18C17.9477 18 17.5 18.4477 17.5 19C17.5 19.5523 17.9477 20 18.5 20C19.0523 20 19.5 19.5523 19.5 19Z" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,4 @@
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#000000">
<path d="M3 5.75C3 5.33579 3.33579 5 3.75 5C6.41341 5 9.00797 4.05652 11.55 2.15C11.8167 1.95 12.1833 1.95 12.45 2.15C14.992 4.05652 17.5866 5 20.25 5C20.6642 5 21 5.33579 21 5.75V11C21 16.0012 18.0424 19.6757 12.2749 21.9478C12.0982 22.0174 11.9018 22.0174 11.7251 21.9478C5.95756 19.6757 3 16.0012 3 11V5.75ZM4.5 6.47793V11C4.5 15.2556 6.95337 18.3789 12 20.4419C17.0466 18.3789 19.5 15.2556 19.5 11V6.47793C16.9227 6.32585 14.4192 5.38829 12 3.67782C9.58084 5.38829 7.07735 6.32585 4.5 6.47793Z" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 660 B

@@ -0,0 +1,4 @@
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#000000">
<path d="M3 6.25C3 4.45507 4.45507 3 6.25 3H17.75C19.5449 3 21 4.45507 21 6.25V11.9759C20.433 11.7979 19.9204 11.4957 19.5 11.1854V8.5H4.5V17.75C4.5 18.7165 5.2835 19.5 6.25 19.5H12.7324C13.0228 20.04 13.3977 20.5424 13.854 21H6.25C4.45507 21 3 19.5449 3 17.75V6.25ZM17.9896 11.1945C18.6423 11.8454 19.8965 12.8805 21.439 13.0928C21.7466 13.1352 22 13.3779 22 13.6817V16.5203C22 20.3363 18.4206 21.7212 17.6467 21.9761C17.5498 22.008 17.4509 22.008 17.354 21.9761C16.5801 21.7213 13.0003 20.3363 13.0003 16.5203L13 13.6818C13 13.378 13.2534 13.1352 13.561 13.0928C15.1032 12.8804 16.3575 11.8454 17.0103 11.1945C17.2704 10.9351 17.7295 10.9352 17.9896 11.1945Z" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 823 B

@@ -0,0 +1,4 @@
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#000000">
<path d="M6.25 3C4.45507 3 3 4.45507 3 6.25V17.75C3 19.5449 4.45507 21 6.25 21H13.854C13.3977 20.5424 13.0228 20.04 12.7324 19.5H6.25C5.2835 19.5 4.5 18.7165 4.5 17.75V8.5H19.5V11.1854C19.9204 11.4957 20.433 11.7979 21 11.9759V6.25C21 4.45507 19.5449 3 17.75 3H6.25ZM19.5 7H4.5V6.25C4.5 5.2835 5.2835 4.5 6.25 4.5H17.75C18.7165 4.5 19.5 5.2835 19.5 6.25V7ZM17.9896 11.1945C18.6423 11.8454 19.8965 12.8805 21.439 13.0928C21.7466 13.1352 22 13.3779 22 13.6817V16.5203C22 20.3363 18.4206 21.7212 17.6467 21.9761C17.5498 22.008 17.4509 22.008 17.354 21.9761C16.5801 21.7213 13.0003 20.3363 13.0003 16.5203L13 13.6818C13 13.378 13.2534 13.1352 13.561 13.0928C15.1032 12.8804 16.3575 11.8454 17.0103 11.1945C17.2704 10.9351 17.7295 10.9352 17.9896 11.1945Z" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 913 B

@@ -0,0 +1,44 @@
pragma ComponentBehavior: Bound
import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
import QtQuick
import Quickshell
import Quickshell.Wayland
Scope {
id: root
required property Component contentComponent
Loader {
active: PolkitService.active
sourceComponent: Variants {
model: Quickshell.screens
delegate: PanelWindow {
id: panelWindow
required property var modelData
screen: modelData
anchors {
top: true
left: true
right: true
bottom: true
}
color: "transparent"
WlrLayershell.namespace: "quickshell:polkit"
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
WlrLayershell.layer: WlrLayer.Overlay
exclusionMode: ExclusionMode.Ignore
Loader {
anchors.fill: parent
sourceComponent: root.contentComponent
}
}
}
}
}
@@ -6,37 +6,10 @@ import qs.modules.common.functions
import QtQuick
import Quickshell
import Quickshell.Wayland
import Quickshell.Hyprland
Scope {
FullscreenPolkitWindow {
id: root
Loader {
active: PolkitService.active
sourceComponent: Variants {
model: Quickshell.screens
delegate: PanelWindow {
id: panelWindow
required property var modelData
screen: modelData
anchors {
top: true
left: true
right: true
bottom: true
}
color: "transparent"
WlrLayershell.namespace: "quickshell:polkit"
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
WlrLayershell.layer: WlrLayer.Overlay
exclusionMode: ExclusionMode.Ignore
PolkitContent {
anchors.fill: parent
}
}
}
contentComponent: Component {
PolkitContent {}
}
}
@@ -66,12 +66,7 @@ Item {
WindowDialogParagraph {
Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft
text: {
if (!PolkitService.flow) return;
return PolkitService.flow.message.endsWith(".")
? PolkitService.flow.message.slice(0, -1)
: PolkitService.flow.message
}
text: PolkitService.cleanMessage
}
MaterialTextField {
@@ -79,11 +74,7 @@ Item {
Layout.fillWidth: true
focus: true
enabled: PolkitService.interactionAvailable
placeholderText: {
const inputPrompt = PolkitService.flow?.inputPrompt.trim() ?? "";
const cleanedInputPrompt = inputPrompt.endsWith(":") ? inputPrompt.slice(0, -1) : inputPrompt;
return cleanedInputPrompt || (root.usePasswordChars ? Translation.tr("Password") : Translation.tr("Input"))
}
placeholderText: PolkitService.cleanPrompt
echoMode: root.usePasswordChars ? TextInput.Password : TextInput.Normal
onAccepted: root.submit();
@@ -67,7 +67,7 @@ Button {
}
}
CloseButton {
WindowCloseButton {
id: closeButton
}
}
@@ -91,46 +91,14 @@ Button {
}
}
component CloseButton: Button {
id: reusableCloseButton
component WindowCloseButton: CloseButton {
visible: root.hovered
Layout.leftMargin: 4
implicitHeight: 30
implicitWidth: 30
radius: Looks.radius.large - root.padding
onClicked: {
root.toplevel.close();
}
Rectangle {
z: 0
color: "transparent"
anchors.fill: closeButtonBg
anchors.margins: -1
opacity: closeButtonBg.opacity
border.width: 1
radius: closeButtonBg.radius + 1
border.color: Looks.colors.bg2Border
}
background: Rectangle {
id: closeButtonBg
z: 1
opacity: reusableCloseButton.hovered ? 1 : 0
radius: Looks.radius.large - root.padding
color: reusableCloseButton.pressed ? Looks.colors.dangerActive : Looks.colors.danger
Behavior on opacity {
animation: Looks.transition.opacity.createObject(this)
}
Behavior on color {
animation: Looks.transition.color.createObject(this)
}
}
contentItem: FluentIcon {
z: 2
anchors.centerIn: parent
icon: "dismiss"
implicitSize: 10
}
}
}
@@ -0,0 +1,48 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
import qs.modules.waffle.bar
import Quickshell
Button {
id: reusableCloseButton
implicitHeight: 30
implicitWidth: 30
property alias radius: closeButtonBg.radius
Rectangle {
z: 0
color: "transparent"
anchors.fill: closeButtonBg
anchors.margins: -1
opacity: closeButtonBg.opacity
border.width: 1
radius: closeButtonBg.radius + 1
border.color: Looks.colors.bg2Border
}
background: Rectangle {
id: closeButtonBg
z: 1
opacity: reusableCloseButton.hovered ? 1 : 0
color: reusableCloseButton.pressed ? Looks.colors.dangerActive : Looks.colors.danger
Behavior on opacity {
animation: Looks.transition.opacity.createObject(this)
}
Behavior on color {
animation: Looks.transition.color.createObject(this)
}
}
contentItem: FluentIcon {
z: 2
anchors.centerIn: parent
icon: "dismiss"
implicitSize: 10
}
}
@@ -101,7 +101,7 @@ Singleton {
property color bg1Hover: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Hover : root.lightColors.bg1Hover, root.contentTransparency)
property color bg1Active: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Active : root.lightColors.bg1Active, root.contentTransparency)
property color bg1Border: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Border : root.lightColors.bg1Border, root.contentTransparency)
property color bg2Base: ColorUtils.transparentize(root.dark ? root.darkColors.bg2Base : root.lightColors.bg2Base, root.backgroundTransparency)
property color bg2Base: root.dark ? root.darkColors.bg2Base : root.lightColors.bg2Base
property color bg2: ColorUtils.transparentize(root.dark ? root.darkColors.bg2 : root.lightColors.bg2, root.contentTransparency)
property color bg2Hover: ColorUtils.transparentize(root.dark ? root.darkColors.bg2Hover : root.lightColors.bg2Hover, root.contentTransparency)
property color bg2Active: ColorUtils.transparentize(root.dark ? root.darkColors.bg2Active : root.lightColors.bg2Active, root.contentTransparency)
@@ -147,6 +147,7 @@ Singleton {
property int regular: Font.Medium
property int strong: Font.DemiBold
property int stronger: (Font.DemiBold + 2*Font.Bold) / 3
property int strongest: Font.Bold
}
property QtObject pixelSize: QtObject {
property real normal: 11
@@ -17,4 +17,6 @@ Kirigami.Icon {
roundToIconSize: false
fallback: root.iconName
source: tryCustomIcon ? `${Looks.iconsPath}/${root.iconName}${!root.separateLightDark ? "" : Looks.dark ? "-dark" : "-light"}.svg` : fallback
color: Looks.colors.fg
}
@@ -8,7 +8,7 @@ Text {
color: Looks.colors.fg
font {
hintingPreference: Font.PreferFullHinting
hintingPreference: Font.PreferDefaultHinting
family: Looks.font.family.ui
pixelSize: Looks.font.pixelSize.normal
weight: Looks.font.weight.regular
@@ -0,0 +1,27 @@
import qs.modules.common
import QtQuick
import QtQuick.Controls.FluentWinUI3
import QtQuick.Controls
TextField {
id: root
clip: true
renderType: Text.NativeRendering
verticalAlignment: Text.AlignVCenter
color: Looks.colors.fg
font {
hintingPreference: Font.PreferDefaultHinting
family: Looks.font.family.ui
pixelSize: Looks.font.pixelSize.normal
weight: Looks.font.weight.regular
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
hoverEnabled: true
cursorShape: Qt.IBeamCursor
}
}
@@ -0,0 +1,208 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
import qs.modules.waffle.looks
Rectangle {
id: root
color: "#000000"
readonly property bool usePasswordChars: !PolkitService.flow?.responseVisible ?? true
Keys.onPressed: event => { // Esc to close
if (event.key === Qt.Key_Escape) {
PolkitService.cancel();
}
}
StyledImage {
anchors.fill: parent
source: Config.options.background.wallpaperPath
fillMode: Image.PreserveAspectCrop
Rectangle {
anchors.fill: parent
color: ColorUtils.transparentize("#000000", 0.31)
PolkitDialog {
id: dialog
DragHandler {
target: null
property real startX: dialog.x
property real startY: dialog.y
onActiveChanged: {
if (!active) return;
startX = dialog.x;
startY = dialog.y;
}
xAxis.onActiveValueChanged: {
dialog.x = Math.round(startX + xAxis.activeValue);
}
yAxis.onActiveValueChanged: {
dialog.y = Math.round(startY + yAxis.activeValue);
}
}
x: Math.round((parent.width - width) / 2)
y: Math.round((parent.height - height) / 2)
}
}
}
component PolkitDialog: WPane {
borderColor: Looks.colors.ambientShadow
contentItem: WPanelPageColumn {
PolkitDialogHeader {
Layout.fillWidth: true
}
BodyRectangle {
id: dialogBody
implicitHeight: bodyContent.implicitHeight + 48
implicitWidth: 434
color: Looks.colors.bg1Base
ColumnLayout {
id: bodyContent
anchors.fill: parent
anchors.margins: 24
spacing: 20
RowLayout {
Layout.fillWidth: true
spacing: 15
WAppIcon {
iconName: PolkitService.flow?.iconName ?? "window-shield"
fallback: PolkitService.flow?.iconName == "" ? `${Looks.iconsPath}/window-shield` : PolkitService.flow.iconName
isMask: PolkitService.flow?.iconName === ""
tryCustomIcon: false
}
WText {
Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft
font.pixelSize: Looks.font.pixelSize.larger
font.weight: Looks.font.weight.strongest
text: {
const iconName = PolkitService.flow?.iconName ?? "";
if (iconName === "")
return Translation.tr("Command-line-invoked Action");
const desktopEntry = DesktopEntries.applications.values.find(entry => {
return entry.icon == iconName;
});
return desktopEntry ? desktopEntry.name : Translation.tr("Unknown Application");
}
}
}
WText {
Layout.fillWidth: true
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignLeft
text: PolkitService.cleanMessage
}
WTextField {
id: inputField
Layout.fillWidth: true
focus: true
enabled: PolkitService.interactionAvailable
placeholderText: PolkitService.cleanPrompt
echoMode: root.usePasswordChars ? TextInput.Password : TextInput.Normal
onAccepted: PolkitService.submit(inputField.text)
Keys.onPressed: event => { // Esc to close
if (event.key === Qt.Key_Escape) {
PolkitService.cancel();
}
}
Component.onCompleted: forceActiveFocus()
Connections {
target: PolkitService
function onInteractionAvailableChanged() {
if (!PolkitService.interactionAvailable)
return;
inputField.text = "";
inputField.forceActiveFocus();
}
}
}
}
}
BodyRectangle {
implicitHeight: 80
color: Looks.colors.bgPanelFooterBase
RowLayout {
anchors.fill: parent
anchors.margins: 24
spacing: 8
uniformCellSizes: true
WButton {
Layout.fillWidth: true
implicitHeight: 32
colBackground: Looks.colors.bg1
horizontalAlignment: Text.AlignHCenter
text: Translation.tr("Yes")
onClicked: PolkitService.submit(inputField.text)
}
WButton {
Layout.fillWidth: true
implicitHeight: 32
horizontalAlignment: Text.AlignHCenter
checked: true
text: Translation.tr("No")
onClicked: PolkitService.cancel()
}
}
}
}
}
component PolkitDialogHeader: BodyRectangle {
implicitHeight: headerContent.implicitHeight
color: Looks.colors.bg2Base
CloseButton {
anchors {
top: parent.top
right: parent.right
}
radius: 0
implicitWidth: 32
implicitHeight: 32
onClicked: {
PolkitService.cancel();
}
}
ColumnLayout {
id: headerContent
anchors.fill: parent
anchors.leftMargin: 24
anchors.rightMargin: 24
spacing: 18
WText {
Layout.topMargin: 20
Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft
text: Translation.tr("Polkit")
}
WText {
Layout.fillWidth: true
Layout.bottomMargin: 12
horizontalAlignment: Text.AlignLeft
wrapMode: Text.Wrap
text: Translation.tr("Do you want to allow this app to make changes to your device?")
font.pixelSize: Looks.font.pixelSize.xlarger
font.weight: Looks.font.weight.strongest
}
}
}
}
@@ -0,0 +1,15 @@
import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
import QtQuick
import Quickshell
import Quickshell.Wayland
FullscreenPolkitWindow {
id: root
contentComponent: Component {
WPolkitContent {}
}
}
@@ -11,6 +11,18 @@ Singleton {
property alias active: polkitAgent.isActive
property alias flow: polkitAgent.flow
property bool interactionAvailable: false
property string cleanMessage: {
if (!root.flow) return "";
return root.flow.message.endsWith(".")
? root.flow.message.slice(0, -1)
: root.flow.message
}
property string cleanPrompt: {
const inputPrompt = PolkitService.flow?.inputPrompt.trim() ?? "";
const cleanedInputPrompt = inputPrompt.endsWith(":") ? inputPrompt.slice(0, -1) : inputPrompt;
const usePasswordChars = !PolkitService.flow?.responseVisible ?? true
return cleanedInputPrompt || (usePasswordChars ? Translation.tr("Password") : Translation.tr("Input"))
}
function cancel() {
root.flow.cancelAuthenticationRequest()
+3 -1
View File
@@ -32,6 +32,7 @@ import qs.modules.waffle.background
import qs.modules.waffle.bar
import qs.modules.waffle.notificationCenter
import qs.modules.waffle.onScreenDisplay
import qs.modules.waffle.polkit
import qs.modules.waffle.startMenu
import qs.modules.waffle.sessionScreen
@@ -84,6 +85,7 @@ ShellRoot {
PanelLoader { identifier: "wBackground"; component: WaffleBackground {} }
PanelLoader { identifier: "wNotificationCenter"; component: WaffleNotificationCenter {} }
PanelLoader { identifier: "wOnScreenDisplay"; component: WaffleOSD {} }
PanelLoader { identifier: "wPolkit"; component: WafflePolkit {} }
PanelLoader { identifier: "wStartMenu"; component: WaffleStartMenu {} }
PanelLoader { identifier: "wSessionScreen"; component: WaffleSessionScreen {} }
ReloadPopup {}
@@ -98,7 +100,7 @@ ShellRoot {
property list<string> families: ["ii", "waffle"]
property var panelFamilies: ({
"ii": ["iiBar", "iiBackground", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiScreenCorners", "iiSessionScreen", "iiSidebarLeft", "iiSidebarRight", "iiVerticalBar", "iiWallpaperSelector"],
"waffle": ["wActionCenter", "wBar", "wBackground", "wNotificationCenter", "wOnScreenDisplay", "wStartMenu", "iiCheatsheet", "iiLock", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiPolkit", "iiRegionSelector", "wSessionScreen", "iiWallpaperSelector"],
"waffle": ["wActionCenter", "wBar", "wBackground", "wNotificationCenter", "wOnScreenDisplay", "wPolkit", "wStartMenu", "iiCheatsheet", "iiLock", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiRegionSelector", "wSessionScreen", "iiWallpaperSelector"],
})
function cyclePanelFamily() {
const currentIndex = families.indexOf(Config.options.panelFamily)