Watch
1
0
Fork
You've already forked souveraine
0

publish: the public projection begins here

This is a projection, not a development branch. The tree above was constructed
from the internal source named below under a manifest that decides which paths
may leave, then scanned as a whole tree rather than as a series of patches, and
only then published.

Public history starts here because the history before it was not admissible,
and neither was the tree. What used to stand in this repository included a
rescue copy of another machine, a directory of phone handoffs, deployment
wired to one house, and a submodule pointing at a forge no stranger can reach.
None of that was ever the product. It stays in the private forge, which is
allowed to hold the whole working organism, and this is what was deliberately
sent out instead.

Three mechanisms produced this tree, in decreasing order of trust. A top-level
path the manifest does not name never arrives at all, which is the one that
catches directories nobody has thought of yet. Named internal files inside
admitted roots are dropped. A short, reviewed table replaces deployment
defaults that a public build must not carry -- an endpoint aimed at one LAN, a
VPN profile belonging to one phone, packaging built from one checkout path.

Everything after this commit is an ordinary publication with the same three
trailers, so a force push stops being routine and starts meaning that
something deliberate happened. The trailers bind the projection to its source
without pretending the public SHA is the private one: same lineage, different
tree, and the record says so.

Source-Sha: 8f27b1e76a8fef560a336aba18e6990713ff1047
Policy-Sha: 6b261d2f3e6e1fb19874846ba4bb1dfe15565d25b8618c1c1afba0419c101d27
Tree-Digest: 18ec3563c5e5ef9a414993a9f6734b251ff9ed3cd56eebdd6cac01e45c6e3067
This commit is contained in:
Fimeg 2026-09-04 15:55:48 -04:00
commit 8f42fc953d
1476 changed files with 238455 additions and 0 deletions

View file

@ -0,0 +1,33 @@
// Pixel3Arch addition: quick toggle for blueline-autorotate (systemd user
// service). Deploy to modules/common/models/quickToggles/.
import QtQuick
import Quickshell
import Quickshell.Io
import qs
import qs.services
import qs.modules.common
QuickToggleModel {
id: root
property bool running: false
name: Translation.tr("Auto-rotate")
toggled: root.running
icon: "screen_rotation"
mainAction: () => {
root.running = !root.running;
// iio-sensor-proxy runs at boot (blueline-sensors-enable.service)
// for proximity gating + ambient light; this toggle only controls
// the rotation daemon.
Quickshell.execDetached(["sh", "-c", root.running
? "systemctl --user start blueline-autorotate"
: "systemctl --user stop blueline-autorotate"]);
}
tooltipText: Translation.tr("Lock screen rotation to accelerometer")
Process {
running: true
command: ["systemctl", "--user", "is-active", "--quiet", "blueline-autorotate"]
onExited: exitCode => root.running = (exitCode === 0)
}
}

View file

@ -0,0 +1,16 @@
// Pixel3Arch addition: one-tap wallpaper shuffle (the settings page barely
// scales on the phone). Deploy to modules/common/models/quickToggles/.
import QtQuick
import qs
import qs.services
import qs.modules.common
QuickToggleModel {
name: Translation.tr("Shuffle wallpaper")
toggled: false
icon: "wallpaper"
mainAction: () => {
Wallpapers.randomFromCurrentFolder();
}
tooltipText: Translation.tr("Random wallpaper from the current folder")
}

View file

@ -0,0 +1,59 @@
// Pixel3Arch addition: stop the Waydroid session. Phone-only — waydroid is
// installed on the phone and not on the laptop, so this lives in the overlay.
// Deploy to modules/common/models/quickToggles/.
import QtQuick
import Quickshell
import Quickshell.Io
import qs.services
import qs.modules.common
/**
* Terminate the Android container's session.
*
* `waydroidTerminate` sat in the phone's config with no component behind it in
* any tree or commit — a configured verb that did nothing, silently.
*/
QuickToggleModel {
id: root
/// True while a stop is in flight, so a double tap cannot race itself.
property bool busy: false
name: Translation.tr("Stop Android")
hasStatusText: false
toggled: false
icon: "android"
// Deliberate, and always pressable. There is deliberately NO status poll:
// `waydroid status` talks to the container and can block for seconds, and
// a button that greys itself out on a stale read is a verb the owner
// cannot reach. Press it; it reports what actually happened.
mainAction: () => {
if (root.busy)
return;
root.busy = true;
stopProc.running = true;
}
Process {
id: stopProc
command: ["bash", "-c",
"waydroid session stop >/dev/null 2>&1; "
+ "waydroid status 2>/dev/null | grep -q 'Session:.*RUNNING' && echo running || echo stopped"]
stdout: StdioCollector {
id: stopResult
onStreamFinished: {
root.busy = false;
const stillRunning = stopResult.text.trim() === "running";
Quickshell.execDetached(["notify-send", Translation.tr("Android"),
stillRunning
? Translation.tr("Session did not stop.")
: Translation.tr("Session stopped."),
"-a", "Shell"]);
}
}
onExited: root.busy = false
}
tooltipText: Translation.tr("Stop the Waydroid session")
}

View file

@ -0,0 +1,43 @@
import qs.modules.common
import qs.services
import QtQuick
import QtQuick.Layouts
MouseArea {
id: root
property bool borderless: Config.options.bar.borderless
property bool alwaysShowAllResources: false
implicitWidth: rowLayout.implicitWidth + rowLayout.anchors.leftMargin + rowLayout.anchors.rightMargin
implicitHeight: Appearance.sizes.barHeight
hoverEnabled: !Config.options.bar.tooltips.clickToShow
// Pixel 3: one stat at a time, rotating memory -> cpu -> swap every few
// seconds — three circles don't fit a 540px bar. Tap still opens the
// full ResourcesPopup.
property int shownResource: 0
Timer {
interval: 4000
running: true
repeat: true
onTriggered: root.shownResource = (root.shownResource + 1) % 3
}
RowLayout {
id: rowLayout
spacing: 0
anchors.fill: parent
anchors.leftMargin: 4
anchors.rightMargin: 4
Resource {
iconName: root.shownResource === 0 ? "memory" : root.shownResource === 1 ? "planner_review" : "swap_horiz"
percentage: root.shownResource === 0 ? ResourceUsage.memoryUsedPercentage : root.shownResource === 1 ? ResourceUsage.cpuUsage : ResourceUsage.swapUsedPercentage
warningThreshold: root.shownResource === 0 ? Config.options.bar.resources.memoryWarningThreshold : root.shownResource === 1 ? Config.options.bar.resources.cpuWarningThreshold : Config.options.bar.resources.swapWarningThreshold
}
}
ResourcesPopup {
hoverTarget: root
}
}

View file

@ -0,0 +1,373 @@
// We're going to use ydotool
// See /usr/include/linux/input-event-codes.h for keycodes
const defaultLayout = "English (US)";
const byName = {
"Phone (compact)": {
name_short: "PH",
description: "Compact phone layout",
comment: "5 rows, no F-keys, fits 540px",
keys: [
[
{ keytype: "normal", label: "1", labelShift: "!", shape: "normal", keycode: 2 },
{ keytype: "normal", label: "2", labelShift: "@", shape: "normal", keycode: 3 },
{ keytype: "normal", label: "3", labelShift: "#", shape: "normal", keycode: 4 },
{ keytype: "normal", label: "4", labelShift: "$", shape: "normal", keycode: 5 },
{ keytype: "normal", label: "5", labelShift: "%", shape: "normal", keycode: 6 },
{ keytype: "normal", label: "6", labelShift: "^", shape: "normal", keycode: 7 },
{ keytype: "normal", label: "7", labelShift: "&", shape: "normal", keycode: 8 },
{ keytype: "normal", label: "8", labelShift: "*", shape: "normal", keycode: 9 },
{ keytype: "normal", label: "9", labelShift: "(", shape: "normal", keycode: 10 },
{ keytype: "normal", label: "0", labelShift: ")", shape: "normal", keycode: 11 }
],
[
{ keytype: "normal", label: "q", labelShift: "Q", shape: "normal", keycode: 16 },
{ keytype: "normal", label: "w", labelShift: "W", shape: "normal", keycode: 17 },
{ keytype: "normal", label: "e", labelShift: "E", shape: "normal", keycode: 18 },
{ keytype: "normal", label: "r", labelShift: "R", shape: "normal", keycode: 19 },
{ keytype: "normal", label: "t", labelShift: "T", shape: "normal", keycode: 20 },
{ keytype: "normal", label: "y", labelShift: "Y", shape: "normal", keycode: 21 },
{ keytype: "normal", label: "u", labelShift: "U", shape: "normal", keycode: 22 },
{ keytype: "normal", label: "i", labelShift: "I", shape: "normal", keycode: 23 },
{ keytype: "normal", label: "o", labelShift: "O", shape: "normal", keycode: 24 },
{ keytype: "normal", label: "p", labelShift: "P", shape: "normal", keycode: 25 }
],
[
{ keytype: "normal", label: "a", labelShift: "A", shape: "normal", keycode: 30 },
{ keytype: "normal", label: "s", labelShift: "S", shape: "normal", keycode: 31 },
{ keytype: "normal", label: "d", labelShift: "D", shape: "normal", keycode: 32 },
{ keytype: "normal", label: "f", labelShift: "F", shape: "normal", keycode: 33 },
{ keytype: "normal", label: "g", labelShift: "G", shape: "normal", keycode: 34 },
{ keytype: "normal", label: "h", labelShift: "H", shape: "normal", keycode: 35 },
{ keytype: "normal", label: "j", labelShift: "J", shape: "normal", keycode: 36 },
{ keytype: "normal", label: "k", labelShift: "K", shape: "normal", keycode: 37 },
{ keytype: "normal", label: "l", labelShift: "L", shape: "normal", keycode: 38 }
],
[
{ keytype: "modkey", label: "Shift", shape: "shift", keycode: 42 },
{ keytype: "normal", label: "z", labelShift: "Z", shape: "normal", keycode: 44 },
{ keytype: "normal", label: "x", labelShift: "X", shape: "normal", keycode: 45 },
{ keytype: "normal", label: "c", labelShift: "C", shape: "normal", keycode: 46 },
{ keytype: "normal", label: "v", labelShift: "V", shape: "normal", keycode: 47 },
{ keytype: "normal", label: "b", labelShift: "B", shape: "normal", keycode: 48 },
{ keytype: "normal", label: "n", labelShift: "N", shape: "normal", keycode: 49 },
{ keytype: "normal", label: "m", labelShift: "M", shape: "normal", keycode: 50 },
{ keytype: "normal", label: "Bksp", shape: "expand", keycode: 14 }
],
[
{ keytype: "modkey", label: "Ctrl", shape: "control", keycode: 29 },
{ keytype: "normal", label: ",", labelShift: "<", shape: "normal", keycode: 51 },
{ keytype: "normal", label: "Space", shape: "space", keycode: 57 },
{ keytype: "normal", label: ".", labelShift: ">", shape: "normal", keycode: 52 },
{ keytype: "normal", label: "/", labelShift: "?", shape: "normal", keycode: 53 },
{ keytype: "normal", label: "Enter", shape: "expand", keycode: 28 }
]
]
},
"English (US)": {
name_short: "US",
description: "QWERTY - Full",
comment: "Like physical keyboard",
// A key looks like this: { k: "a", ks: "A", t: "normal" } (key, key-shift, type)
// key types are: normal, tab, caps, shift, control, fn (normal w/ half height), space, expand
// keys: [
// [{ k: "Esc", t: "fn" }, { k: "F1", t: "fn" }, { k: "F2", t: "fn" }, { k: "F3", t: "fn" }, { k: "F4", t: "fn" }, { k: "F5", t: "fn" }, { k: "F6", t: "fn" }, { k: "F7", t: "fn" }, { k: "F8", t: "fn" }, { k: "F9", t: "fn" }, { k: "F10", t: "fn" }, { k: "F11", t: "fn" }, { k: "F12", t: "fn" }, { k: "PrtSc", t: "fn" }, { k: "Del", t: "fn" }],
// [{ k: "`", ks: "~", t: "normal" }, { k: "1", ks: "!", t: "normal" }, { k: "2", ks: "@", t: "normal" }, { k: "3", ks: "#", t: "normal" }, { k: "4", ks: "$", t: "normal" }, { k: "5", ks: "%", t: "normal" }, { k: "6", ks: "^", t: "normal" }, { k: "7", ks: "&", t: "normal" }, { k: "8", ks: "*", t: "normal" }, { k: "9", ks: "(", t: "normal" }, { k: "0", ks: ")", t: "normal" }, { k: "-", ks: "_", t: "normal" }, { k: "=", ks: "+", t: "normal" }, { k: "Backspace", t: "shift" }],
// [{ k: "Tab", t: "tab" }, { k: "q", ks: "Q", t: "normal" }, { k: "w", ks: "W", t: "normal" }, { k: "e", ks: "E", t: "normal" }, { k: "r", ks: "R", t: "normal" }, { k: "t", ks: "T", t: "normal" }, { k: "y", ks: "Y", t: "normal" }, { k: "u", ks: "U", t: "normal" }, { k: "i", ks: "I", t: "normal" }, { k: "o", ks: "O", t: "normal" }, { k: "p", ks: "P", t: "normal" }, { k: "[", ks: "{", t: "normal" }, { k: "]", ks: "}", t: "normal" }, { k: "\\", ks: "|", t: "expand" }],
// [{ k: "Caps", t: "caps" }, { k: "a", ks: "A", t: "normal" }, { k: "s", ks: "S", t: "normal" }, { k: "d", ks: "D", t: "normal" }, { k: "f", ks: "F", t: "normal" }, { k: "g", ks: "G", t: "normal" }, { k: "h", ks: "H", t: "normal" }, { k: "j", ks: "J", t: "normal" }, { k: "k", ks: "K", t: "normal" }, { k: "l", ks: "L", t: "normal" }, { k: ";", ks: ":", t: "normal" }, { k: "'", ks: '"', t: "normal" }, { k: "Enter", t: "expand" }],
// [{ k: "Shift", t: "shift" }, { k: "z", ks: "Z", t: "normal" }, { k: "x", ks: "X", t: "normal" }, { k: "c", ks: "C", t: "normal" }, { k: "v", ks: "V", t: "normal" }, { k: "b", ks: "B", t: "normal" }, { k: "n", ks: "N", t: "normal" }, { k: "m", ks: "M", t: "normal" }, { k: ",", ks: "<", t: "normal" }, { k: ".", ks: ">", t: "normal" }, { k: "/", ks: "?", t: "normal" }, { k: "Shift", t: "expand" }],
// [{ k: "Ctrl", t: "control" }, { k: "Fn", t: "normal" }, { k: "Win", t: "normal" }, { k: "Alt", t: "normal" }, { k: "Space", t: "space" }, { k: "Alt", t: "normal" }, { k: "Menu", t: "normal" }, { k: "Ctrl", t: "control" }]
// ]
// A normal key looks like this: {label: "a", labelShift: "A", shape: "normal", keycode: 30, type: "normal"}
// A modkey looks like this: {label: "Ctrl", shape: "control", keycode: 29, type: "modkey"}
// key types are: normal, tab, caps, shift, control, fn (normal w/ half height), space, expand
keys: [
[
{ keytype: "normal", label: "Esc", shape: "fn", keycode: 1 },
{ keytype: "normal", label: "F1", shape: "fn", keycode: 59 },
{ keytype: "normal", label: "F2", shape: "fn", keycode: 60 },
{ keytype: "normal", label: "F3", shape: "fn", keycode: 61 },
{ keytype: "normal", label: "F4", shape: "fn", keycode: 62 },
{ keytype: "normal", label: "F5", shape: "fn", keycode: 63 },
{ keytype: "normal", label: "F6", shape: "fn", keycode: 64 },
{ keytype: "normal", label: "F7", shape: "fn", keycode: 65 },
{ keytype: "normal", label: "F8", shape: "fn", keycode: 66 },
{ keytype: "normal", label: "F9", shape: "fn", keycode: 67 },
{ keytype: "normal", label: "F10", shape: "fn", keycode: 68 },
{ keytype: "normal", label: "F11", shape: "fn", keycode: 87 },
{ keytype: "normal", label: "F12", shape: "fn", keycode: 88 },
{ keytype: "normal", label: "PrtSc", shape: "fn", keycode: 99 },
{ keytype: "normal", label: "Del", shape: "fn", keycode: 111 }
],
[
{ keytype: "normal", label: "`", labelShift: "~", shape: "normal", keycode: 41 },
{ keytype: "normal", label: "1", labelShift: "!", shape: "normal", keycode: 2 },
{ keytype: "normal", label: "2", labelShift: "@", shape: "normal", keycode: 3 },
{ keytype: "normal", label: "3", labelShift: "#", shape: "normal", keycode: 4 },
{ keytype: "normal", label: "4", labelShift: "$", shape: "normal", keycode: 5 },
{ keytype: "normal", label: "5", labelShift: "%", shape: "normal", keycode: 6 },
{ keytype: "normal", label: "6", labelShift: "^", shape: "normal", keycode: 7 },
{ keytype: "normal", label: "7", labelShift: "&", shape: "normal", keycode: 8 },
{ keytype: "normal", label: "8", labelShift: "*", shape: "normal", keycode: 9 },
{ keytype: "normal", label: "9", labelShift: "(", shape: "normal", keycode: 10 },
{ keytype: "normal", label: "0", labelShift: ")", shape: "normal", keycode: 11 },
{ keytype: "normal", label: "-", labelShift: "_", shape: "normal", keycode: 12 },
{ keytype: "normal", label: "=", labelShift: "+", shape: "normal", keycode: 13 },
{ keytype: "normal", label: "Backspace", shape: "expand", keycode: 14 }
],
[
{ keytype: "normal", label: "Tab", shape: "tab", keycode: 15 },
{ keytype: "normal", label: "q", labelShift: "Q", shape: "normal", keycode: 16 },
{ keytype: "normal", label: "w", labelShift: "W", shape: "normal", keycode: 17 },
{ keytype: "normal", label: "e", labelShift: "E", shape: "normal", keycode: 18 },
{ keytype: "normal", label: "r", labelShift: "R", shape: "normal", keycode: 19 },
{ keytype: "normal", label: "t", labelShift: "T", shape: "normal", keycode: 20 },
{ keytype: "normal", label: "y", labelShift: "Y", shape: "normal", keycode: 21 },
{ keytype: "normal", label: "u", labelShift: "U", shape: "normal", keycode: 22 },
{ keytype: "normal", label: "i", labelShift: "I", shape: "normal", keycode: 23 },
{ keytype: "normal", label: "o", labelShift: "O", shape: "normal", keycode: 24 },
{ keytype: "normal", label: "p", labelShift: "P", shape: "normal", keycode: 25 },
{ keytype: "normal", label: "[", labelShift: "{", shape: "normal", keycode: 26 },
{ keytype: "normal", label: "]", labelShift: "}", shape: "normal", keycode: 27 },
{ keytype: "normal", label: "\\", labelShift: "|", shape: "expand", keycode: 43 }
],
[
//{ keytype: "normal", label: "Caps", shape: "caps", keycode: 58 }, // not needed as double-pressing shift does that
{ keytype: "spacer", label: "", shape: "empty" },
{ keytype: "spacer", label: "", shape: "empty" },
{ keytype: "normal", label: "a", labelShift: "A", shape: "normal", keycode: 30 },
{ keytype: "normal", label: "s", labelShift: "S", shape: "normal", keycode: 31 },
{ keytype: "normal", label: "d", labelShift: "D", shape: "normal", keycode: 32 },
{ keytype: "normal", label: "f", labelShift: "F", shape: "normal", keycode: 33 },
{ keytype: "normal", label: "g", labelShift: "G", shape: "normal", keycode: 34 },
{ keytype: "normal", label: "h", labelShift: "H", shape: "normal", keycode: 35 },
{ keytype: "normal", label: "j", labelShift: "J", shape: "normal", keycode: 36 },
{ keytype: "normal", label: "k", labelShift: "K", shape: "normal", keycode: 37 },
{ keytype: "normal", label: "l", labelShift: "L", shape: "normal", keycode: 38 },
{ keytype: "normal", label: ";", labelShift: ":", shape: "normal", keycode: 39 },
{ keytype: "normal", label: "'", labelShift: '"', shape: "normal", keycode: 40 },
{ keytype: "normal", label: "Enter", shape: "expand", keycode: 28 }
],
[
{ keytype: "modkey", label: "Shift", labelShift: "Shift", labelCaps: "Caps", shape: "shift", keycode: 42 },
{ keytype: "normal", label: "z", labelShift: "Z", shape: "normal", keycode: 44 },
{ keytype: "normal", label: "x", labelShift: "X", shape: "normal", keycode: 45 },
{ keytype: "normal", label: "c", labelShift: "C", shape: "normal", keycode: 46 },
{ keytype: "normal", label: "v", labelShift: "V", shape: "normal", keycode: 47 },
{ keytype: "normal", label: "b", labelShift: "B", shape: "normal", keycode: 48 },
{ keytype: "normal", label: "n", labelShift: "N", shape: "normal", keycode: 49 },
{ keytype: "normal", label: "m", labelShift: "M", shape: "normal", keycode: 50 },
{ keytype: "normal", label: ",", labelShift: "<", shape: "normal", keycode: 51 },
{ keytype: "normal", label: ".", labelShift: ">", shape: "normal", keycode: 52 },
{ keytype: "normal", label: "/", labelShift: "?", shape: "normal", keycode: 53 },
{ keytype: "modkey", label: "Shift", labelShift: "Shift", labelCaps: "Caps", shape: "expand", keycode: 54 } // optional
],
[
{ keytype: "modkey", label: "Ctrl", shape: "control", keycode: 29 },
// { label: "Super", shape: "normal", keycode: 125 }, // dangerous
{ keytype: "modkey", label: "Alt", shape: "normal", keycode: 56 },
{ keytype: "normal", label: "Space", shape: "space", keycode: 57 },
{ keytype: "modkey", label: "Alt", shape: "normal", keycode: 100 },
// { label: "Super", shape: "normal", keycode: 126 }, // dangerous
{ keytype: "normal", label: "Menu", shape: "normal", keycode: 139 },
{ keytype: "modkey", label: "Ctrl", shape: "control", keycode: 97 }
]
]
},
"German": {
name_short: "DE",
description: "QWERTZ - Full",
comment: "Keyboard layout commonly used in German-speaking countries",
keys: [
[
{ keytype: "normal", label: "Esc", shape: "fn", keycode: 1 },
{ keytype: "normal", label: "F1", shape: "fn", keycode: 59 },
{ keytype: "normal", label: "F2", shape: "fn", keycode: 60 },
{ keytype: "normal", label: "F3", shape: "fn", keycode: 61 },
{ keytype: "normal", label: "F4", shape: "fn", keycode: 62 },
{ keytype: "normal", label: "F5", shape: "fn", keycode: 63 },
{ keytype: "normal", label: "F6", shape: "fn", keycode: 64 },
{ keytype: "normal", label: "F7", shape: "fn", keycode: 65 },
{ keytype: "normal", label: "F8", shape: "fn", keycode: 66 },
{ keytype: "normal", label: "F9", shape: "fn", keycode: 67 },
{ keytype: "normal", label: "F10", shape: "fn", keycode: 68 },
{ keytype: "normal", label: "F11", shape: "fn", keycode: 87 },
{ keytype: "normal", label: "F12", shape: "fn", keycode: 88 },
{ keytype: "normal", label: "Druck", shape: "fn", keycode: 99 },
{ keytype: "normal", label: "Entf", shape: "fn", keycode: 111 }
],
[
{ keytype: "normal", label: "^", labelShift: "°", labelAlt: "′", shape: "normal", keycode: 41 },
{ keytype: "normal", label: "1", labelShift: "!", labelAlt: "¹", shape: "normal", keycode: 2 },
{ keytype: "normal", label: "2", labelShift: "\"", labelAlt: "²", shape: "normal", keycode: 3 },
{ keytype: "normal", label: "3", labelShift: "§", labelAlt: "³", shape: "normal", keycode: 4 },
{ keytype: "normal", label: "4", labelShift: "$", labelAlt: "¼", shape: "normal", keycode: 5 },
{ keytype: "normal", label: "5", labelShift: "%", labelAlt: "½", shape: "normal", keycode: 6 },
{ keytype: "normal", label: "6", labelShift: "&", labelAlt: "¬", shape: "normal", keycode: 7 },
{ keytype: "normal", label: "7", labelShift: "/", labelAlt: "{", shape: "normal", keycode: 8 },
{ keytype: "normal", label: "8", labelShift: "(", labelAlt: "[", shape: "normal", keycode: 9 },
{ keytype: "normal", label: "9", labelShift: ")", labelAlt: "]", shape: "normal", keycode: 10 },
{ keytype: "normal", label: "0", labelShift: "=", labelAlt: "}", shape: "normal", keycode: 11 },
{ keytype: "normal", label: "ß", labelShift: "?", labelAlt: "\\", shape: "normal", keycode: 12 },
{ keytype: "normal", label: "´", labelShift: "`", labelAlt: "¸", shape: "normal", keycode: 13 },
{ keytype: "normal", label: "⟵", shape: "expand", keycode: 14 }
],
[
{ keytype: "normal", label: "Tab ⇆", shape: "tab", keycode: 15 },
{ keytype: "normal", label: "q", labelShift: "Q", labelAlt: "@", shape: "normal", keycode: 16 },
{ keytype: "normal", label: "w", labelShift: "W", labelAlt: "ſ", shape: "normal", keycode: 17 },
{ keytype: "normal", label: "e", labelShift: "E", labelAlt: "€", shape: "normal", keycode: 18 },
{ keytype: "normal", label: "r", labelShift: "R", labelAlt: "¶", shape: "normal", keycode: 19 },
{ keytype: "normal", label: "t", labelShift: "T", labelAlt: "ŧ", shape: "normal", keycode: 20 },
{ keytype: "normal", label: "z", labelShift: "Z", labelAlt: "←", shape: "normal", keycode: 21 },
{ keytype: "normal", label: "u", labelShift: "U", labelAlt: "↓", shape: "normal", keycode: 22 },
{ keytype: "normal", label: "i", labelShift: "I", labelAlt: "→", shape: "normal", keycode: 23 },
{ keytype: "normal", label: "o", labelShift: "O", labelAlt: "ø", shape: "normal", keycode: 24 },
{ keytype: "normal", label: "p", labelShift: "P", labelAlt: "þ", shape: "normal", keycode: 25 },
{ keytype: "normal", label: "ü", labelShift: "Ü", labelAlt: "¨", shape: "normal", keycode: 26 },
{ keytype: "normal", label: "+", labelShift: "*", labelAlt: "~", shape: "normal", keycode: 27 },
{ keytype: "normal", label: "↵", shape: "expand", keycode: 28 }
],
[
//{ keytype: "normal", label: "Umschalt ⇩", shape: "caps", keycode: 58 },
{ keytype: "spacer", label: "", shape: "empty" },
{ keytype: "spacer", label: "", shape: "empty" },
{ keytype: "normal", label: "a", labelShift: "A", labelAlt: "æ", shape: "normal", keycode: 30 },
{ keytype: "normal", label: "s", labelShift: "S", labelAlt: "ſ", shape: "normal", keycode: 31 },
{ keytype: "normal", label: "d", labelShift: "D", labelAlt: "ð", shape: "normal", keycode: 32 },
{ keytype: "normal", label: "f", labelShift: "F", labelAlt: "đ", shape: "normal", keycode: 33 },
{ keytype: "normal", label: "g", labelShift: "G", labelAlt: "ŋ", shape: "normal", keycode: 34 },
{ keytype: "normal", label: "h", labelShift: "H", labelAlt: "ħ", shape: "normal", keycode: 35 },
{ keytype: "normal", label: "j", labelShift: "J", labelAlt: "", shape: "normal", keycode: 36 },
{ keytype: "normal", label: "k", labelShift: "K", labelAlt: "ĸ", shape: "normal", keycode: 37 },
{ keytype: "normal", label: "l", labelShift: "L", labelAlt: "ł", shape: "normal", keycode: 38 },
{ keytype: "normal", label: "ö", labelShift: "Ö", labelAlt: "˝", shape: "normal", keycode: 39 },
{ keytype: "normal", label: "ä", labelShift: 'Ä', labelAlt: "^", shape: "normal", keycode: 40 },
{ keytype: "normal", label: "#", labelShift: '\'', labelAlt: "’", shape: "normal", keycode: 43 },
{ keytype: "spacer", label: "", shape: "empty" },
//{ keytype: "normal", label: "↵", shape: "expand", keycode: 28 }
],
[
{ keytype: "modkey", label: "Shift", labelShift: "Shift ⇧", labelCaps: "Locked ⇩", shape: "shift", keycode: 42 },
{ keytype: "normal", label: "<", labelShift: ">", labelAlt: "|", shape: "normal", keycode: 86 },
{ keytype: "normal", label: "y", labelShift: "Y", labelAlt: "»", shape: "normal", keycode: 44 },
{ keytype: "normal", label: "x", labelShift: "X", labelAlt: "«", shape: "normal", keycode: 45 },
{ keytype: "normal", label: "c", labelShift: "C", labelAlt: "¢", shape: "normal", keycode: 46 },
{ keytype: "normal", label: "v", labelShift: "V", labelAlt: "„", shape: "normal", keycode: 47 },
{ keytype: "normal", label: "b", labelShift: "B", labelAlt: "“", shape: "normal", keycode: 48 },
{ keytype: "normal", label: "n", labelShift: "N", labelAlt: "”", shape: "normal", keycode: 49 },
{ keytype: "normal", label: "m", labelShift: "M", labelAlt: "µ", shape: "normal", keycode: 50 },
{ keytype: "normal", label: ",", labelShift: ";", labelAlt: "·", shape: "normal", keycode: 51 },
{ keytype: "normal", label: ".", labelShift: ":", labelAlt: "…", shape: "normal", keycode: 52 },
{ keytype: "normal", label: "-", labelShift: "_", labelAlt: "–", shape: "normal", keycode: 53 },
{ keytype: "modkey", label: "Shift", labelShift: "Shift ⇧", labelCaps: "Locked ⇩", shape: "expand", keycode: 54 }, // optional
],
[
{ keytype: "modkey", label: "Strg", shape: "control", keycode: 29 },
//{ keytype: "normal", label: "", shape: "normal", keycode: 125 }, // dangerous
{ keytype: "modkey", label: "Alt", shape: "normal", keycode: 56 },
{ keytype: "normal", label: "Leertaste", shape: "space", keycode: 57 },
{ keytype: "modkey", label: "Alt Gr", shape: "normal", keycode: 100 },
// { label: "Super", shape: "normal", keycode: 126 }, // dangerous
//{ keytype: "normal", label: "Menu", shape: "normal", keycode: 139 }, // doesn't work?
{ keytype: "modkey", label: "Strg", shape: "control", keycode: 97 },
{ keytype: "normal", label: "⇦", shape: "normal", keycode: 105 },
{ keytype: "normal", label: "⇨", shape: "normal", keycode: 106 },
]
]
},
"Russian": {
name_short: "RU",
description: "ЙЦУКЕН - Full",
comment: "Standard Russian keyboard layout",
keys: [
[
{ keytype: "normal", label: "Esc", shape: "fn", keycode: 1 },
{ keytype: "normal", label: "F1", shape: "fn", keycode: 59 },
{ keytype: "normal", label: "F2", shape: "fn", keycode: 60 },
{ keytype: "normal", label: "F3", shape: "fn", keycode: 61 },
{ keytype: "normal", label: "F4", shape: "fn", keycode: 62 },
{ keytype: "normal", label: "F5", shape: "fn", keycode: 63 },
{ keytype: "normal", label: "F6", shape: "fn", keycode: 64 },
{ keytype: "normal", label: "F7", shape: "fn", keycode: 65 },
{ keytype: "normal", label: "F8", shape: "fn", keycode: 66 },
{ keytype: "normal", label: "F9", shape: "fn", keycode: 67 },
{ keytype: "normal", label: "F10", shape: "fn", keycode: 68 },
{ keytype: "normal", label: "F11", shape: "fn", keycode: 87 },
{ keytype: "normal", label: "F12", shape: "fn", keycode: 88 },
{ keytype: "normal", label: "PrtSc", shape: "fn", keycode: 99 },
{ keytype: "normal", label: "Del", shape: "fn", keycode: 111 }
],
[
{ keytype: "normal", label: "ё", labelShift: "Ё", shape: "normal", keycode: 41 },
{ keytype: "normal", label: "1", labelShift: "!", shape: "normal", keycode: 2 },
{ keytype: "normal", label: "2", labelShift: "\"", shape: "normal", keycode: 3 },
{ keytype: "normal", label: "3", labelShift: "№", shape: "normal", keycode: 4 },
{ keytype: "normal", label: "4", labelShift: ";", shape: "normal", keycode: 5 },
{ keytype: "normal", label: "5", labelShift: "%", shape: "normal", keycode: 6 },
{ keytype: "normal", label: "6", labelShift: ":", shape: "normal", keycode: 7 },
{ keytype: "normal", label: "7", labelShift: "?", shape: "normal", keycode: 8 },
{ keytype: "normal", label: "8", labelShift: "*", shape: "normal", keycode: 9 },
{ keytype: "normal", label: "9", labelShift: "(", shape: "normal", keycode: 10 },
{ keytype: "normal", label: "0", labelShift: ")", shape: "normal", keycode: 11 },
{ keytype: "normal", label: "-", labelShift: "_", shape: "normal", keycode: 12 },
{ keytype: "normal", label: "=", labelShift: "+", shape: "normal", keycode: 13 },
{ keytype: "normal", label: "Backspace", shape: "expand", keycode: 14 }
],
[
{ keytype: "normal", label: "Tab", shape: "tab", keycode: 15 },
{ keytype: "normal", label: "й", labelShift: "Й", shape: "normal", keycode: 16 },
{ keytype: "normal", label: "ц", labelShift: "Ц", shape: "normal", keycode: 17 },
{ keytype: "normal", label: "у", labelShift: "У", shape: "normal", keycode: 18 },
{ keytype: "normal", label: "к", labelShift: "К", shape: "normal", keycode: 19 },
{ keytype: "normal", label: "е", labelShift: "Е", shape: "normal", keycode: 20 },
{ keytype: "normal", label: "н", labelShift: "Н", shape: "normal", keycode: 21 },
{ keytype: "normal", label: "г", labelShift: "Г", shape: "normal", keycode: 22 },
{ keytype: "normal", label: "ш", labelShift: "Ш", shape: "normal", keycode: 23 },
{ keytype: "normal", label: "щ", labelShift: "Щ", shape: "normal", keycode: 24 },
{ keytype: "normal", label: "з", labelShift: "З", shape: "normal", keycode: 25 },
{ keytype: "normal", label: "х", labelShift: "Х", shape: "normal", keycode: 26 },
{ keytype: "normal", label: "ъ", labelShift: "Ъ", shape: "normal", keycode: 27 },
{ keytype: "normal", label: "\\", labelShift: "/", shape: "expand", keycode: 43 }
],
[
{ keytype: "spacer", label: "", shape: "empty" },
{ keytype: "spacer", label: "", shape: "empty" },
{ keytype: "normal", label: "ф", labelShift: "Ф", shape: "normal", keycode: 30 },
{ keytype: "normal", label: "ы", labelShift: "Ы", shape: "normal", keycode: 31 },
{ keytype: "normal", label: "в", labelShift: "В", shape: "normal", keycode: 32 },
{ keytype: "normal", label: "а", labelShift: "А", shape: "normal", keycode: 33 },
{ keytype: "normal", label: "п", labelShift: "П", shape: "normal", keycode: 34 },
{ keytype: "normal", label: "р", labelShift: "Р", shape: "normal", keycode: 35 },
{ keytype: "normal", label: "о", labelShift: "О", shape: "normal", keycode: 36 },
{ keytype: "normal", label: "л", labelShift: "Л", shape: "normal", keycode: 37 },
{ keytype: "normal", label: "д", labelShift: "Д", shape: "normal", keycode: 38 },
{ keytype: "normal", label: "ж", labelShift: "Ж", shape: "normal", keycode: 39 },
{ keytype: "normal", label: "э", labelShift: "Э", shape: "normal", keycode: 40 },
{ keytype: "normal", label: "Enter", shape: "expand", keycode: 28 }
],
[
{ keytype: "modkey", label: "Shift", shape: "shift", keycode: 42 },
{ keytype: "normal", label: "я", labelShift: "Я", shape: "normal", keycode: 44 },
{ keytype: "normal", label: "ч", labelShift: "Ч", shape: "normal", keycode: 45 },
{ keytype: "normal", label: "с", labelShift: "С", shape: "normal", keycode: 46 },
{ keytype: "normal", label: "м", labelShift: "М", shape: "normal", keycode: 47 },
{ keytype: "normal", label: "и", labelShift: "И", shape: "normal", keycode: 48 },
{ keytype: "normal", label: "т", labelShift: "Т", shape: "normal", keycode: 49 },
{ keytype: "normal", label: "ь", labelShift: "Ь", shape: "normal", keycode: 50 },
{ keytype: "normal", label: "б", labelShift: "Б", shape: "normal", keycode: 51 },
{ keytype: "normal", label: "ю", labelShift: "Ю", shape: "normal", keycode: 52 },
{ keytype: "normal", label: ".", labelShift: ",", shape: "normal", keycode: 53 },
{ keytype: "modkey", label: "Shift", shape: "expand", keycode: 54 }
],
[
{ keytype: "modkey", label: "Ctrl", shape: "control", keycode: 29 },
{ keytype: "modkey", label: "Alt", shape: "normal", keycode: 56 },
{ keytype: "normal", label: "Space", shape: "space", keycode: 57 },
{ keytype: "modkey", label: "Alt", shape: "normal", keycode: 100 },
{ keytype: "normal", label: "Menu", shape: "normal", keycode: 139 },
{ keytype: "modkey", label: "Ctrl", shape: "control", keycode: 97 }
]
]
}
}

View file

@ -0,0 +1,10 @@
import qs.services
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.functions
import qs.modules.common.widgets
import QtQuick
AndroidQuickToggleButton {
toggleModel: AutoRotateToggle {}
}

View file

@ -0,0 +1,353 @@
pragma ComponentBehavior: Bound
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Bluetooth
DelegateChooser {
id: root
property bool editMode: false
required property real baseCellWidth
required property real baseCellHeight
required property real spacing
required property int startingIndex
property int dragIndex: -1
signal openAudioOutputDialog()
signal openAudioInputDialog()
signal openBluetoothDialog()
signal openNightLightDialog()
signal openWifiDialog()
role: "type"
DelegateChoice { roleValue: "antiFlashbang"; AndroidAntiFlashbangToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
onOpenMenu: {
root.openNightLightDialog()
}
} }
DelegateChoice { roleValue: "audio"; AndroidAudioToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
onOpenMenu: {
root.openAudioOutputDialog()
}
} }
// Phone-only roles: inert unless the toggle arrangement includes them
// (laptop configs never do), so they live in the shared base.
DelegateChoice { roleValue: "autoRotate"; AndroidAutoRotateToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "wallpaperShuffle"; AndroidWallpaperShuffleToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "bluetooth"; AndroidBluetoothToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
onOpenMenu: {
root.openBluetoothDialog()
}
} }
DelegateChoice { roleValue: "cloudflareWarp"; AndroidCloudflareWarpToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "colorPicker"; AndroidColorPickerToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "darkMode"; AndroidDarkModeToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "easyEffects"; AndroidEasyEffectsToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "gameMode"; AndroidGameModeToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "idleInhibitor"; AndroidIdleInhibitorToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "mic"; AndroidMicToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
onOpenMenu: {
root.openAudioInputDialog()
}
} }
DelegateChoice { roleValue: "musicRecognition"; AndroidMusicRecognition {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "network"; AndroidNetworkToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
onOpenMenu: {
root.openWifiDialog()
}
} }
DelegateChoice { roleValue: "nightLight"; AndroidNightLightToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
onOpenMenu: {
root.openNightLightDialog()
}
} }
DelegateChoice { roleValue: "notifications"; AndroidNotificationToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "onScreenKeyboard"; AndroidOnScreenKeyboardToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "powerProfile"; AndroidPowerProfileToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "vpn"; AndroidVpnToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "screenSnip"; AndroidScreenSnipToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "fullScreenshot"; AndroidFullScreenshotToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "waydroidTerminate"; AndroidWaydroidTerminateToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
dragIndex: root.dragIndex
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
}

View file

@ -0,0 +1,10 @@
import qs.services
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.functions
import qs.modules.common.widgets
import QtQuick
AndroidQuickToggleButton {
toggleModel: WallpaperShuffleToggle {}
}

View file

@ -0,0 +1,11 @@
// Pixel3Arch addition: phone-only, waydroid is not installed on the laptop.
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
AndroidQuickToggleButton {
toggleModel: WaydroidTerminateToggle {}
}

View file

@ -0,0 +1,65 @@
#!/usr/bin/env bash
if [[ -z "$1" ]]; then
echo "Usage: $0 <image_path> [model] [prompt]"
echo "Tip: set GEMINI_WALLPAPER_MODEL and/or GEMINI_WALLPAPER_PROMPT to provide defaults."
exit 1
fi
# Variables
SOURCE_IMG_PATH="$1"
MODEL="${2:-${GEMINI_WALLPAPER_MODEL:-gemini-3.6-flash}}" # Souveraine overlay: gemini-2.5-flash 404s for new free-tier keys (2026-08)
WALLPAPER_NAME="$(basename "$SOURCE_IMG_PATH")"
PROMPT="${3:-${GEMINI_WALLPAPER_PROMPT:-Categorize the wallpaper. Its file name is $WALLPAPER_NAME}}"
RESIZED_IMG_PATH="/tmp/quickshell/ai/wallpaper.jpg"
# Resize image for speed
mkdir -p "$(dirname "$RESIZED_IMG_PATH")"
magick "$SOURCE_IMG_PATH" -resize 200x -quality 50 "$RESIZED_IMG_PATH"
# Get API key
API_KEY=$(secret-tool lookup 'application' 'illogical-impulse' | jq -r '.apiKeys.gemini')
# Encode image to base64
if [[ "$(base64 --version 2>&1)" = *"FreeBSD"* ]]; then
B64FLAGS="--input"
else
B64FLAGS="-w0"
fi
B64DATA="$(base64 $B64FLAGS $RESIZED_IMG_PATH)"
# echo $B64DATA
# Prepare request data
payload='{
"contents": [{
"parts":[
{
"inline_data": {
"mime_type":"image/jpeg",
"data": "'"$B64DATA"'"
}
},
{"text": "'"$PROMPT"'"}
]
}],
"generationConfig": {
"responseMimeType": "text/x.enum",
"responseSchema": {
"type": "string",
"enum": [ "abstract", "anime", "city", "minimalist", "landscape", "plants", "person", "space" ]
},
"temperature": 0
}
}'
# echo "$payload" | jq
# Make the request
response=$(curl "https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent" \
-H "x-goog-api-key: $API_KEY" \
-H 'Content-Type: application/json' \
-X POST \
-d "$payload" 2> /dev/null)
# echo "$response" | jq
# Write the result
echo "$response" | jq -r '.candidates[0].content.parts[0].text'

View file

@ -0,0 +1,477 @@
#!/usr/bin/env bash
QUICKSHELL_CONFIG_NAME="ii"
XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}"
CONFIG_DIR="$XDG_CONFIG_HOME/quickshell/$QUICKSHELL_CONFIG_NAME"
CACHE_DIR="$XDG_CACHE_HOME/quickshell"
STATE_DIR="$XDG_STATE_HOME/quickshell"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SHELL_CONFIG_FILE="$XDG_CONFIG_HOME/illogical-impulse/config.json"
MATUGEN_DIR="$XDG_CONFIG_HOME/matugen"
terminalscheme="$SCRIPT_DIR/terminal/scheme-base.json"
handle_kde_material_you_colors() {
# Check if Qt app theming is enabled in config
if [ -f "$SHELL_CONFIG_FILE" ]; then
enable_qt_apps=$(jq -r '.appearance.wallpaperTheming.enableQtApps' "$SHELL_CONFIG_FILE")
if [ "$enable_qt_apps" == "false" ]; then
return
fi
fi
# Map $type_flag to allowed scheme variants for kde-material-you-colors-wrapper.sh
local kde_scheme_variant=""
case "$type_flag" in
scheme-content|scheme-expressive|scheme-fidelity|scheme-fruit-salad|scheme-monochrome|scheme-neutral|scheme-rainbow|scheme-tonal-spot)
kde_scheme_variant="$type_flag"
;;
*)
kde_scheme_variant="scheme-tonal-spot" # default
;;
esac
"$XDG_CONFIG_HOME"/matugen/templates/kde/kde-material-you-colors-wrapper.sh --scheme-variant "$kde_scheme_variant"
}
pre_process() {
local mode_flag="$1"
# Set GNOME color-scheme if mode_flag is dark or light
if [[ "$mode_flag" == "dark" ]]; then
gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark'
gsettings set org.gnome.desktop.interface gtk-theme 'adw-gtk3-dark'
elif [[ "$mode_flag" == "light" ]]; then
gsettings set org.gnome.desktop.interface color-scheme 'prefer-light'
gsettings set org.gnome.desktop.interface gtk-theme 'adw-gtk3'
fi
if [ ! -d "$CACHE_DIR"/user/generated ]; then
mkdir -p "$CACHE_DIR"/user/generated
fi
}
post_process() {
local screen_width="$1"
local screen_height="$2"
local wallpaper_path="$3"
handle_kde_material_you_colors &
"$SCRIPT_DIR/code/material-code-set-color.sh" &
}
check_and_prompt_upscale() {
local img="$1"
min_width_desired="$(hyprctl monitors -j | jq '([.[].width] | max)' | xargs)" # max monitor width
min_height_desired="$(hyprctl monitors -j | jq '([.[].height] | max)' | xargs)" # max monitor height
# blueline: upscayl not installed and the blocking notify-send -A stalls
# the whole wallpaper switch on phone — skip the prompt entirely.
return 0
if command -v identify &>/dev/null && [ -f "$img" ]; then
local img_width img_height
if is_video "$img"; then # Not check resolution for videos, just let em pass
img_width=$min_width_desired
img_height=$min_height_desired
else
img_width=$(identify -format "%w" "$img" 2>/dev/null)
img_height=$(identify -format "%h" "$img" 2>/dev/null)
fi
if [[ "$img_width" -lt "$min_width_desired" || "$img_height" -lt "$min_height_desired" ]]; then
action=$(notify-send "Upscale?" \
"Image resolution (${img_width}x${img_height}) is lower than screen resolution (${min_width_desired}x${min_height_desired})" \
-A "open_upscayl=Open Upscayl"\
-a "Wallpaper switcher")
if [[ "$action" == "open_upscayl" ]]; then
if command -v upscayl &>/dev/null; then
nohup upscayl > /dev/null 2>&1 &
else
action2=$(notify-send \
-a "Wallpaper switcher" \
-c "im.error" \
-A "install_upscayl=Install Upscayl (Arch)" \
"Install Upscayl?" \
"yay -S upscayl-bin")
if [[ "$action2" == "install_upscayl" ]]; then
kitty -1 yay -S upscayl-bin
if command -v upscayl &>/dev/null; then
nohup upscayl > /dev/null 2>&1 &
fi
fi
fi
fi
fi
fi
}
CUSTOM_DIR="$XDG_CONFIG_HOME/hypr/custom"
RESTORE_SCRIPT_DIR="$CUSTOM_DIR/scripts"
RESTORE_SCRIPT="$RESTORE_SCRIPT_DIR/__restore_video_wallpaper.sh"
THUMBNAIL_DIR="$RESTORE_SCRIPT_DIR/mpvpaper_thumbnails"
VIDEO_OPTS="no-audio loop hwdec=auto scale=bilinear interpolation=no video-sync=display-resample panscan=1.0 video-scale-x=1.0 video-scale-y=1.0 video-align-x=0.5 video-align-y=0.5 load-scripts=no"
is_video() {
local extension="${1##*.}"
[[ "$extension" == "mp4" || "$extension" == "webm" || "$extension" == "mkv" || "$extension" == "avi" || "$extension" == "mov" ]] && return 0 || return 1
}
kill_existing_mpvpaper() {
pkill -f -9 mpvpaper || true
}
create_restore_script() {
local video_path=$1
cat > "$RESTORE_SCRIPT.tmp" << EOF
#!/bin/bash
# Generated by switchwall.sh - Don't modify it by yourself.
# Time: $(date)
pkill -f -9 mpvpaper
for monitor in \$(hyprctl monitors -j | jq -r '.[] | .name'); do
mpvpaper -o "$VIDEO_OPTS" "\$monitor" "$video_path" &
sleep 0.1
done
EOF
mv "$RESTORE_SCRIPT.tmp" "$RESTORE_SCRIPT"
chmod +x "$RESTORE_SCRIPT"
}
remove_restore() {
cat > "$RESTORE_SCRIPT.tmp" << EOF
#!/bin/bash
# The content of this script will be generated by switchwall.sh - Don't modify it by yourself.
EOF
mv "$RESTORE_SCRIPT.tmp" "$RESTORE_SCRIPT"
}
set_wallpaper_path() {
local path="$1"
if [ -f "$SHELL_CONFIG_FILE" ]; then
jq --arg path "$path" '.background.wallpaperPath = $path' "$SHELL_CONFIG_FILE" > "$SHELL_CONFIG_FILE.tmp" && mv "$SHELL_CONFIG_FILE.tmp" "$SHELL_CONFIG_FILE"
fi
}
set_thumbnail_path() {
local path="$1"
if [ -f "$SHELL_CONFIG_FILE" ]; then
jq --arg path "$path" '.background.thumbnailPath = $path' "$SHELL_CONFIG_FILE" > "$SHELL_CONFIG_FILE.tmp" && mv "$SHELL_CONFIG_FILE.tmp" "$SHELL_CONFIG_FILE"
fi
}
categorize_wallpaper() {
img_cat=$("$SCRIPT_DIR/../ai/gemini-categorize-wallpaper.sh" "$1")
# notify-send "Wallpaper category" "$img_cat"
echo "$img_cat" > "$STATE_DIR/user/generated/wallpaper/category.txt"
}
switch() {
imgpath="$1"
mode_flag="$2"
type_flag="$3"
color_flag="$4"
color="$5"
# Start Gemini auto-categorization if enabled
aiStylingEnabled=$(jq -r '.background.widgets.clock.cookie.aiStyling' "$SHELL_CONFIG_FILE")
if [[ "$aiStylingEnabled" == "true" ]]; then
categorize_wallpaper "$imgpath" &
fi
read scale screenx screeny screensizey < <(hyprctl monitors -j | jq '.[] | select(.focused) | .scale, .x, .y, .height' | xargs)
cursorposx=$(hyprctl cursorpos -j | jq '.x' 2>/dev/null) || cursorposx=960
cursorposx=$(bc <<< "scale=0; ($cursorposx - $screenx) * $scale / 1")
cursorposy=$(hyprctl cursorpos -j | jq '.y' 2>/dev/null) || cursorposy=540
cursorposy=$(bc <<< "scale=0; ($cursorposy - $screeny) * $scale / 1")
cursorposy_inverted=$((screensizey - cursorposy))
matugen_args=(--source-color-index 0)
if [[ "$color_flag" == "1" ]]; then
matugen_args+=(color hex "$color")
generate_colors_material_args=(--color "$color")
else
if [[ -z "$imgpath" ]]; then
echo 'Aborted'
exit 0
fi
check_and_prompt_upscale "$imgpath" &
kill_existing_mpvpaper
if is_video "$imgpath"; then
mkdir -p "$THUMBNAIL_DIR"
missing_deps=()
if ! command -v mpvpaper &> /dev/null; then
missing_deps+=("mpvpaper")
fi
if ! command -v ffmpeg &> /dev/null; then
missing_deps+=("ffmpeg")
fi
if [ ${#missing_deps[@]} -gt 0 ]; then
echo "Missing deps: ${missing_deps[*]}"
echo "Arch: sudo pacman -S ${missing_deps[*]}"
action=$(notify-send \
-a "Wallpaper switcher" \
-c "im.error" \
-A "install_arch=Install (Arch)" \
"Can't switch to video wallpaper" \
"Missing dependencies: ${missing_deps[*]}")
if [[ "$action" == "install_arch" ]]; then
kitty -1 sudo pacman -S "${missing_deps[*]}"
if command -v mpvpaper &>/dev/null && command -v ffmpeg &>/dev/null; then
notify-send 'Wallpaper switcher' 'Alright, try again!' -a "Wallpaper switcher"
fi
fi
exit 0
fi
# Set wallpaper path
set_wallpaper_path "$imgpath"
# Set video wallpaper
local video_path="$imgpath"
monitors=$(hyprctl monitors -j | jq -r '.[] | .name')
for monitor in $monitors; do
mpvpaper -o "$VIDEO_OPTS" "$monitor" "$video_path" &
sleep 0.1
done
# Extract first frame for color generation
thumbnail="$THUMBNAIL_DIR/$(basename "$imgpath").jpg"
ffmpeg -y -i "$imgpath" -vframes 1 "$thumbnail" 2>/dev/null
# Set thumbnail path
set_thumbnail_path "$thumbnail"
if [ -f "$thumbnail" ]; then
matugen_args+=(image "$thumbnail")
generate_colors_material_args=(--path "$thumbnail")
create_restore_script "$video_path"
else
echo "Cannot create image to colorgen"
remove_restore
exit 1
fi
else
matugen_args+=(image "$imgpath")
generate_colors_material_args=(--path "$imgpath")
# Update wallpaper path in config
set_wallpaper_path "$imgpath"
remove_restore
fi
fi
# Determine mode if not set
if [[ -z "$mode_flag" ]]; then
current_mode=$(gsettings get org.gnome.desktop.interface color-scheme 2>/dev/null | tr -d "'")
if [[ "$current_mode" == "prefer-dark" ]]; then
mode_flag="dark"
else
mode_flag="light"
fi
fi
# enforce dark mode for terminal
if [[ -n "$mode_flag" ]]; then
matugen_args+=(--mode "$mode_flag")
if [[ $(jq -r '.appearance.wallpaperTheming.terminalGenerationProps.forceDarkMode' "$SHELL_CONFIG_FILE") == "true" ]]; then
generate_colors_material_args+=(--mode "dark")
else
generate_colors_material_args+=(--mode "$mode_flag")
fi
fi
[[ -n "$type_flag" ]] && matugen_args+=(--type "$type_flag") && generate_colors_material_args+=(--scheme "$type_flag")
generate_colors_material_args+=(--termscheme "$terminalscheme" --blend_bg_fg)
generate_colors_material_args+=(--cache "$STATE_DIR/user/generated/color.txt")
pre_process "$mode_flag"
# Check if app and shell theming is enabled in config
if [ -f "$SHELL_CONFIG_FILE" ]; then
enable_apps_shell=$(jq -r '.appearance.wallpaperTheming.enableAppsAndShell' "$SHELL_CONFIG_FILE")
if [ "$enable_apps_shell" == "false" ]; then
echo "App and shell theming disabled, skipping matugen and color generation"
return
fi
fi
# Set harmony and related properties
if [ -f "$SHELL_CONFIG_FILE" ]; then
harmony=$(jq -r '.appearance.wallpaperTheming.terminalGenerationProps.harmony' "$SHELL_CONFIG_FILE")
harmonize_threshold=$(jq -r '.appearance.wallpaperTheming.terminalGenerationProps.harmonizeThreshold' "$SHELL_CONFIG_FILE")
term_fg_boost=$(jq -r '.appearance.wallpaperTheming.terminalGenerationProps.termFgBoost' "$SHELL_CONFIG_FILE")
[[ "$harmony" != "null" && -n "$harmony" ]] && generate_colors_material_args+=(--harmony "$harmony")
[[ "$harmonize_threshold" != "null" && -n "$harmonize_threshold" ]] && generate_colors_material_args+=(--harmonize_threshold "$harmonize_threshold")
[[ "$term_fg_boost" != "null" && -n "$term_fg_boost" ]] && generate_colors_material_args+=(--term_fg_boost "$term_fg_boost")
fi
matugen "${matugen_args[@]}"
source "$HOME/.local/state/quickshell/.venv/bin/activate"
python3 "$SCRIPT_DIR/generate_colors_material.py" "${generate_colors_material_args[@]}" \
> "$STATE_DIR"/user/generated/material_colors.scss
deactivate
"$SCRIPT_DIR"/applycolor.sh
# Pass screen width, height, and wallpaper path to post_process
max_width_desired="$(hyprctl monitors -j | jq '([.[].width] | min)' | xargs)"
max_height_desired="$(hyprctl monitors -j | jq '([.[].height] | min)' | xargs)"
post_process "$max_width_desired" "$max_height_desired" "$imgpath"
}
main() {
imgpath=""
mode_flag=""
type_flag=""
color_flag=""
color=""
noswitch_flag=""
get_type_from_config() {
jq -r '.appearance.palette.type' "$SHELL_CONFIG_FILE" 2>/dev/null || echo "auto"
}
get_accent_color_from_config() {
jq -r '.appearance.palette.accentColor' "$SHELL_CONFIG_FILE" 2>/dev/null || echo ""
}
set_accent_color() {
local color="$1"
jq --arg color "$color" '.appearance.palette.accentColor = $color' "$SHELL_CONFIG_FILE" > "$SHELL_CONFIG_FILE.tmp" && mv "$SHELL_CONFIG_FILE.tmp" "$SHELL_CONFIG_FILE"
}
detect_scheme_type_from_image() {
local img="$1"
source "$HOME/.local/state/quickshell/.venv/bin/activate"
"$SCRIPT_DIR"/scheme_for_image.py "$img" 2>/dev/null | tr -d '\n'
deactivate
}
while [[ $# -gt 0 ]]; do
case "$1" in
--mode)
mode_flag="$2"
shift 2
;;
--type)
type_flag="$2"
shift 2
;;
--color)
if [[ "$2" =~ ^#?[A-Fa-f0-9]{6}$ ]]; then
set_accent_color "$2"
shift 2
elif [[ "$2" == "clear" ]]; then
set_accent_color ""
shift 2
else
set_accent_color $(hyprpicker --no-fancy)
shift
fi
;;
--image)
imgpath="$2"
shift 2
;;
--noswitch)
noswitch_flag="1"
imgpath=$(jq -r '.background.wallpaperPath' "$SHELL_CONFIG_FILE" 2>/dev/null || echo "")
shift
;;
*)
if [[ -z "$imgpath" ]]; then
imgpath="$1"
fi
shift
;;
esac
done
# If accentColor is set in config, use it
config_color="$(get_accent_color_from_config)"
if [[ "$config_color" =~ ^#?[A-Fa-f0-9]{6}$ ]]; then
color_flag="1"
color="$config_color"
fi
# If type_flag is not set, get it from config
if [[ -z "$type_flag" ]]; then
type_flag="$(get_type_from_config)"
fi
# Validate type_flag (allow 'auto' as well)
allowed_types=(scheme-content scheme-expressive scheme-fidelity scheme-fruit-salad scheme-monochrome scheme-neutral scheme-rainbow scheme-tonal-spot auto)
valid_type=0
for t in "${allowed_types[@]}"; do
if [[ "$type_flag" == "$t" ]]; then
valid_type=1
break
fi
done
if [[ $valid_type -eq 0 ]]; then
echo "[switchwall.sh] Warning: Invalid type '$type_flag', defaulting to 'auto'" >&2
type_flag="auto"
fi
# Only prompt for wallpaper if not using --color and not using --noswitch and no imgpath set
if [[ -z "$imgpath" && -z "$color_flag" && -z "$noswitch_flag" ]]; then
cd "$(xdg-user-dir PICTURES)/Wallpapers/showcase" 2>/dev/null || cd "$(xdg-user-dir PICTURES)/Wallpapers" 2>/dev/null || cd "$(xdg-user-dir PICTURES)" || return 1
imgpath="$(kdialog --getopenfilename . --title 'Choose wallpaper')"
fi
if [[ -n "$imgpath" && -z "$noswitch_flag" ]]; then
set_accent_color ""
color_flag=""
color=""
fi
# If type_flag is 'auto', detect scheme type from image (after imgpath is set)
if [[ "$type_flag" == "auto" ]]; then
if [[ -n "$imgpath" && -f "$imgpath" ]]; then
detected_type="$(detect_scheme_type_from_image "$imgpath")"
# Only use detected_type if it's valid
valid_detected=0
for t in "${allowed_types[@]}"; do
if [[ "$detected_type" == "$t" && "$detected_type" != "auto" ]]; then
valid_detected=1
break
fi
done
if [[ $valid_detected -eq 1 ]]; then
type_flag="$detected_type"
else
echo "[switchwall] Warning: Could not auto-detect a valid scheme, defaulting to 'scheme-tonal-spot'" >&2
type_flag="scheme-tonal-spot"
fi
else
echo "[switchwall] Warning: No image to auto-detect scheme from, defaulting to 'scheme-tonal-spot'" >&2
type_flag="scheme-tonal-spot"
fi
fi
# If mode_flag is dark or light, try to find a variant with that mode suffix
if [[ "$mode_flag" == "dark" || "$mode_flag" == "light" ]]; then
# Get directory, filename without extension, and extension
local imgdir="$(dirname "$imgpath")"
local imgbase="$(basename "$imgpath")"
local imgname="${imgbase%.*}"
local imgext="${imgbase##*.}"
# Strip existing -dark or -light suffix
local stripped_name="${imgname%-dark}"
stripped_name="${stripped_name%-light}"
# Construct the new path with the requested mode suffix
local new_imgpath="${imgdir}/${stripped_name}-${mode_flag}.${imgext}"
local new_stripped_imgpath="${imgdir}/${stripped_name}.${imgext}"
# If the variant exists, use it
if [[ -f "$new_imgpath" ]]; then
imgpath="$new_imgpath"
elif [[ -f "$new_stripped_imgpath" ]]; then
imgpath="$new_stripped_imgpath"
fi
fi
switch "$imgpath" "$mode_flag" "$type_flag" "$color_flag" "$color"
}
main "$@"

View file

@ -0,0 +1,139 @@
pragma Singleton
pragma ComponentBehavior: Bound
import qs.modules.common
import QtQuick
import Quickshell
import Quickshell.Services.Pipewire
/**
* A nice wrapper for default Pipewire audio sink and source.
*/
Singleton {
id: root
// Misc props
property bool ready: Pipewire.defaultAudioSink?.ready ?? false
property PwNode sink: Pipewire.defaultAudioSink
property PwNode source: Pipewire.defaultAudioSource
readonly property real hardMaxValue: 2.00 // People keep joking about setting volume to 5172% so...
property string audioTheme: Config.options.sounds.theme
property real value: sink?.audio.volume ?? 0
function friendlyDeviceName(node) {
return (node.nickname || node.description || Translation.tr("Unknown"));
}
function appNodeDisplayName(node) {
return (node.properties["application.name"] || node.description || node.name)
}
// Lists
function correctType(node, isSink) {
return (node.isSink === isSink) && node.audio
}
function appNodes(isSink) {
return Pipewire.nodes.values.filter((node) => { // Should be list<PwNode> but it breaks ScriptModel
return root.correctType(node, isSink) && node.isStream
})
}
function devices(isSink) {
return Pipewire.nodes.values.filter(node => {
return root.correctType(node, isSink) && !node.isStream
})
}
readonly property list<var> outputAppNodes: root.appNodes(true)
readonly property list<var> inputAppNodes: root.appNodes(false)
readonly property list<var> outputDevices: root.devices(true)
readonly property list<var> inputDevices: root.devices(false)
// Signals
signal sinkProtectionTriggered(string reason);
// Controls
function toggleMute() {
Audio.sink.audio.muted = !Audio.sink.audio.muted
}
function toggleMicMute() {
Audio.source.audio.muted = !Audio.source.audio.muted
}
function incrementVolume() {
const currentVolume = Audio.value;
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
Audio.sink.audio.volume = Math.min(1, Audio.sink.audio.volume + step);
}
function decrementVolume() {
const currentVolume = Audio.value;
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
Audio.sink.audio.volume -= step;
}
function setDefaultSink(node) {
Pipewire.preferredDefaultAudioSink = node;
}
function setDefaultSource(node) {
Pipewire.preferredDefaultAudioSource = node;
}
// Internals
PwObjectTracker {
objects: [sink, source]
}
Connections { // Protection against sudden volume changes
target: sink?.audio ?? null
property bool lastReady: false
property real lastVolume: 0
function onVolumeChanged() {
if (!Config.options.audio.protection.enable) return;
const newVolume = sink.audio.volume;
// when resuming from suspend, we should not write volume to avoid pipewire volume reset issues
if (isNaN(newVolume) || newVolume === undefined || newVolume === null) {
lastReady = false;
lastVolume = 0;
return;
}
if (!lastReady) {
lastVolume = newVolume;
lastReady = true;
return;
}
const maxAllowedIncrease = Config.options.audio.protection.maxAllowedIncrease / 100;
const maxAllowed = Config.options.audio.protection.maxAllowed / 100;
if (newVolume - lastVolume > maxAllowedIncrease) {
sink.audio.volume = lastVolume;
root.sinkProtectionTriggered(Translation.tr("Illegal increment"));
} else if (newVolume > maxAllowed || newVolume > root.hardMaxValue) {
root.sinkProtectionTriggered(Translation.tr("Exceeded max allowed"));
sink.audio.volume = Math.min(lastVolume, maxAllowed);
}
lastVolume = sink.audio.volume;
}
}
function playSystemSound(soundName) {
const ogaPath = `/usr/share/sounds/${root.audioTheme}/stereo/${soundName}.oga`;
const oggPath = `/usr/share/sounds/${root.audioTheme}/stereo/${soundName}.ogg`;
// Try playing .oga first
let command = [
"ffplay",
"-nodisp",
"-autoexit",
ogaPath
];
Quickshell.execDetached(command);
// Also try playing .ogg (ffplay will just fail silently if file doesn't exist)
command = [
"ffplay",
"-nodisp",
"-autoexit",
oggPath
];
Quickshell.execDetached(command);
}
}