//@ pragma UseQApplication // Copyright (C) 2026 Danilo M. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // UseQApplication (line 1) is for the island's tray menus: SystemTrayItem // display() fails without it. Pragmas are read only at launch, so changing // it needs a restart, not a hot reload. import Quickshell import Quickshell.Io import Quickshell.Wayland import QtQuick import "modules/ai" import "modules/appearance" import "modules/assistant" import "modules/autostart" import "modules/bluetooth" import "modules/clipboard" import "modules/git" import "modules/kdeconnect" import "modules/mail" import "modules/network" import "modules/notes" import "modules/perf" import "modules/sound" import "modules/status" import "modules/updates" import "modules/user" import "modules/vm" import "island" import "assistant" ShellRoot { // Quickshell exits once no window is visible, and the drawer is closed // most of the time. See AGENTS.md. // // It is also the window the idle inhibitor attaches to: IdleInhibitor // needs a non-null window, and this is the one window that exists for // the whole life of the shell. PanelWindow { id: keepalive visible: true implicitWidth: 1 implicitHeight: 1 color: "transparent" exclusionMode: ExclusionMode.Ignore mask: Region {} WlrLayershell.keyboardFocus: WlrKeyboardFocus.None // The singleton has no window of its own and IdleInhibitor needs one. Component.onCompleted: Status.inhibitWindow = keepalive } Drawer { id: drawer settingsWindow: settings modules: [ SoundModule { id: soundModule }, NetworkModule {}, BluetoothModule {}, KdeConnectModule {}, MailModule {}, StatusModule {}, PerfModule {}, ClipboardModule {}, UpdatesModule {}, VmModule {}, AiModule {}, UserModule {}, NotesModule {}, WallpaperModule {}, ThemeModule {}, IconsModule {}, SunsetModule {}, IdleModule {}, AutostartModule {}, AssistantModule { id: asstSettings }, PresetsModule {}, GitModule {}, ] } // Pages set once and left alone, the modules marked `settings`, in a // window of their own. See Settings.qml. Settings { id: settings modules: drawer.modules } // unified-desktop-theme's manual steps, its own window on DP-3. See // modules/appearance/README.md. StepsWindow { id: stepsWindow } // The island on DP-1. It reads the sound module's service rather than // binding PipeWire a second time. Island { id: island sound: soundModule.service drawer: drawer asst: assistant } // The assistant canvas on SUPER+Home, a view over desktop-assistant's // socket. See assistant/README.md. Assistant { id: assistant; swapping: asstSettings.loading } // Opening the assistant loads its preferred model if another one is in // VRAM. See modules/assistant/README.md. Connections { target: assistant function onOpenChanged() { if (assistant.open) asstSettings.ensurePreferred(); } } // The waybar launcher and the deep-link keybinds all reach this: // qs -p ipc call drawer toggle -> the grid // qs -p ipc call drawer open mail -> the mail page // Quickshell 0.3.1 IPC requires every declared argument to be present, // and a parameter with a JS default registers as QVariant, which IPC // rejects. So the page name cannot be optional: `open` always takes a // page, and the page-less grid is `toggle`, the zero-argument shape the // waybar launcher called. IpcHandler { target: "drawer" function open(page: string) { drawer.show(page); } function toggle() { drawer.toggle(""); } function close() { drawer.close(); } } // qs -p ipc call settings open autostart // qs -p ipc call settings toggle IpcHandler { target: "settings" function open(page: string) { settings.show(page); } function toggle() { if (settings.open) settings.close(); else settings.show(""); } } // The verbs of the appearance shell this replaced, so its callers only // change the path: udt-steps calls `steps`. // qs -p ipc call appearance wallpaper IpcHandler { target: "appearance" function toggle() { settings.show("wallpaper"); } function wallpaper() { settings.show("wallpaper"); } function theme() { settings.show("theme"); } function sunset() { settings.show("sunset"); } function idle() { settings.show("idle"); } function icons() { settings.show("icons"); } function steps() { stepsWindow.poke(); } function selftest(): string { return Hyprsunset.selftest() + "\n" + Hypridle.selftest() + "\n" + Icons.selftest() + "\n" + Steps.selftest(); } } // qs -p ipc call assistant toggle -> SUPER+Home IpcHandler { target: "assistant" function toggle() { assistant.toggle(); } } }