Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
a8415f4
Save local modifications before update
Ziad-Khattab Jun 12, 2026
e4844da
Restrict Wi-Fi tooltip to Wi-Fi icon hover
Ziad-Khattab Jun 12, 2026
fe08323
Support 6 GHz frequency band calculations in QML components
Ziad-Khattab Jun 12, 2026
d219e03
Add connected Bluetooth device hover details tooltip
Ziad-Khattab Jun 12, 2026
c7ffd2d
Auto-refresh active network details on Wi-Fi icon hover
Ziad-Khattab Jun 12, 2026
3a94c24
chore: Wi-Fi stability options, roaming thresholds, and password prom…
Ziad-Khattab Jun 13, 2026
452d5ab
feat(audio): support up to 200% volume limit and simplify steps to 2%
Ziad-Khattab Jul 9, 2026
0b55b69
feat(tray): ignore blueman-applet to avoid duplicate Bluetooth icon
Ziad-Khattab Jul 10, 2026
39ab33a
ux(wifi/bt): add connection pending visual feedback and rich bluetoot…
Ziad-Khattab Jul 10, 2026
51d4517
feat(niri): configure Picture-in-Picture windows to float in bottom-r…
Ziad-Khattab Jul 11, 2026
42a60ea
feat(systemd): wrap terminal and browser launching in transient syste…
Ziad-Khattab Jul 11, 2026
10a31f1
feat(autopip): implement native QML layout-aware Auto-PiP service for…
Ziad-Khattab Jul 11, 2026
f843f0e
fix(autopip): implement dynamic computed isPipActive property and pla…
Ziad-Khattab Jul 11, 2026
461a647
safety(autopip): disable wtype execution to prevent sticky Wayland mo…
Ziad-Khattab Jul 11, 2026
e191bf3
Fix context menu backdrop screen binding on wakeup
Ziad-Khattab Jul 12, 2026
254ce93
Disable top-left hot corner hover actions
Ziad-Khattab Jul 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions defaults/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,7 @@
"invertPinnedItems": true,
"monochromeIcons": true,
"pinnedItems": [],
"ignoredItems": ["nm-applet", "network-manager-applet", "networkmanager-applet", "blueman-applet", "blueman"],
"showItemId": false
},
"utilButtons": {
Expand Down Expand Up @@ -1099,14 +1100,6 @@
"android": {
"columns": 4,
"toggles": [
{
"size": 1,
"type": "nightLight"
},
{
"size": 1,
"type": "gameMode"
},
{
"size": 1,
"type": "screenSnip"
Expand All @@ -1120,6 +1113,12 @@
"style": "android"
},
"right": {
"controlsSectionOrder": [
"toggles",
"sliders",
"media",
"quickActions"
],
"enabledWidgets": [
"dashboard",
"calendar",
Expand Down Expand Up @@ -1442,6 +1441,7 @@
"showItemId": false,
"invertPinnedItems": true,
"pinnedItems": [],
"ignoredItems": ["nm-applet", "network-manager-applet", "networkmanager-applet", "blueman-applet", "blueman"],
"filterPassive": true
},
"shellUpdates": {
Expand Down
6 changes: 3 additions & 3 deletions defaults/niri/config.d/30-window-rules.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ window-rule {
// Firefox / Zen: picture-in-picture window opens floating.
window-rule {
match app-id=r#"firefox$"# title="^Picture-in-Picture$"
open-floating true
}
window-rule {
match app-id=r#"zen$"# title="^Picture-in-Picture$"
open-floating true
default-floating-position x=24 y=24 relative-to="bottom-right"
default-column-width { fixed 640; }
default-window-height { fixed 360; }
}

window-rule {
Expand Down
4 changes: 2 additions & 2 deletions defaults/niri/config.d/40-environment.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ environment {
// If plasma-integration is not installed, iNiR's setup patches this to "qt6ct".
QT_QPA_PLATFORMTHEME "kde"

// Darkly is a modern Qt style that follows kdeglobals color schemes.
QT_STYLE_OVERRIDE "Darkly"
// Use the native Darkly style selected in qt6ct.conf
// QT_STYLE_OVERRIDE "kvantum"

// Python venv used by iNiR scripts (color generation, AI features, translations).
INIR_VENV "$HOME/.local/state/quickshell/.venv"
Expand Down
3 changes: 2 additions & 1 deletion defaults/niri/config.d/50-startup.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ spawn-at-startup "bash" "-c" "wl-paste --type image --watch cliphist store &"
// Polkit authentication agent — needed for GUI sudo prompts (software install,
// disk mount, etc.). iNiR's setup auto-detects which agent is installed and
// patches this line accordingly (KDE, GNOME, MATE, LXQt, or lxpolkit).
spawn-at-startup "/usr/lib/mate-polkit/polkit-mate-authentication-agent-1"
// spawn-at-startup "/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1"
spawn-at-startup "nm-applet"

// ── iNiR shell ──────────────────────────────────────────────────────────────
// iNiR is managed by the user systemd service (inir.service).
Expand Down
2 changes: 1 addition & 1 deletion defaults/niri/config.d/70-binds.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ binds {

Mod+T { spawn "inir" "terminal"; }
Mod+Return { spawn "inir" "terminal"; }
Super+E { spawn "nautilus"; }
Super+E { spawn "dolphin"; }
Super+W { spawn "inir" "browser"; }

// ═══════════════════════════════════════════════════════════════════════
Expand Down
9 changes: 6 additions & 3 deletions dots/.config/niri/config.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,9 @@ environment {
// Qt theming: kde platform reads colors from kdeglobals (requires plasma-integration pkg)
// The installer falls back to qt6ct only if plasma-integration is not installed
QT_QPA_PLATFORMTHEME "kde"
QT_STYLE_OVERRIDE "Darkly"

// Use the native Darkly style selected in qt6ct.conf
// QT_STYLE_OVERRIDE "kvantum"

// Quickshell Python virtual environment
INIR_VENV "$HOME/.local/state/quickshell/.venv"
Expand All @@ -213,7 +215,8 @@ environment {
spawn-at-startup "bash" "-c" "systemctl --user import-environment XDG_MENU_PREFIX && kbuildsycoca6"

spawn-at-startup "bash" "-c" "wl-paste --watch cliphist store &"
spawn-at-startup "/usr/lib/mate-polkit/polkit-mate-authentication-agent-1"
// spawn-at-startup "/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1"
spawn-at-startup "nm-applet"
// iNiR is started by the user systemd service (inir.service), not spawn-at-startup.

// ============================================================================
Expand Down Expand Up @@ -262,7 +265,7 @@ binds {
// Applications (uses configured terminal from Settings)
Mod+T { spawn "bash" "-c" "$HOME/.config/quickshell/inir/scripts/launch-terminal.sh"; }
Mod+Return { spawn "bash" "-c" "$HOME/.config/quickshell/inir/scripts/launch-terminal.sh"; }
Super+E { spawn "nautilus"; }
Super+E { spawn "dolphin"; }
Super+W { spawn "bash" "-c" "xdg-open https://"; }

// Window management
Expand Down
2 changes: 1 addition & 1 deletion modules/background/Background.qml
Original file line number Diff line number Diff line change
Expand Up @@ -1129,7 +1129,7 @@ Scope {
anchorItem: desktopMenuAnchor
popupAbove: false
closeOnFocusLost: false
closeOnHoverLost: true
closeOnHoverLost: false
model: [
{ text: Translation.tr("Settings"), iconName: "settings", monochromeIcon: true,
action: () => { Quickshell.execDetached([Quickshell.shellPath("scripts/inir"), "settings"]) } },
Expand Down
88 changes: 75 additions & 13 deletions modules/bar/BarContent.qml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Item { // Bar content region
anchorItem: barContextMenuAnchor
popupAbove: Config.options?.bar?.bottom ?? false
closeOnFocusLost: true
closeOnHoverLost: true
closeOnHoverLost: false

model: [
{
Expand Down Expand Up @@ -269,8 +269,8 @@ Item { // Bar content region
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onPressed: event => {
if (event.button === Qt.RightButton)
onReleased: mouse => {
if (mouse.button === Qt.RightButton)
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
}
}
Expand Down Expand Up @@ -538,11 +538,11 @@ Item { // Bar content region
onScrollDown: root.performScrollAction(root.leftAction, false)
onScrollUp: root.performScrollAction(root.leftAction, true)
onMovedAway: root.closeOSD(root.leftAction)
onPressed: event => {
if (event.button === Qt.LeftButton)
onReleased: mouse => {
if (mouse.button === Qt.LeftButton)
GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
else if (event.button === Qt.RightButton)
root.openBarContextMenu(event.x, event.y, barLeftSideMouseArea)
else if (mouse.button === Qt.RightButton)
root.openBarContextMenu(mouse.x, mouse.y, barLeftSideMouseArea)
}

// ScrollHint as overlay - at the inner edge of the margin space
Expand Down Expand Up @@ -704,8 +704,8 @@ Item { // Bar content region
anchors.fill: rightCenterGroupPill
acceptedButtons: Qt.LeftButton | Qt.RightButton
z: -1
onPressed: event => {
if (event.button === Qt.RightButton) {
onReleased: mouse => {
if (mouse.button === Qt.RightButton) {
GlobalStates.controlPanelOpen = !GlobalStates.controlPanelOpen;
} else {
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen;
Expand Down Expand Up @@ -764,11 +764,11 @@ Item { // Bar content region
onScrollDown: root.performScrollAction(root.rightAction, false)
onScrollUp: root.performScrollAction(root.rightAction, true)
onMovedAway: root.closeOSD(root.rightAction)
onPressed: event => {
if (event.button === Qt.LeftButton) {
onReleased: mouse => {
if (mouse.button === Qt.LeftButton) {
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen;
} else if (event.button === Qt.RightButton) {
root.openBarContextMenu(event.x, event.y, barRightSideMouseArea)
} else if (mouse.button === Qt.RightButton) {
root.openBarContextMenu(mouse.x, mouse.y, barRightSideMouseArea)
}
}

Expand Down Expand Up @@ -939,6 +939,49 @@ Item { // Bar content region
iconSize: Appearance.font.pixelSize.larger
color: rightSidebarButton.colText
Layout.rightMargin: BluetoothStatus.available ? indicatorsRowLayout.realSpacing : 0

HoverHandler {
id: wifiHover
onHoveredChanged: {
if (hovered) {
Network.refreshActiveNetworkDetails();
}
}
}

StyledToolTip {
extraVisibleCondition: wifiHover.hovered
text: {
if (!Network.wifiEnabled) return Translation.tr("Wi-Fi is disabled");
if (Network.ethernet) return Translation.tr("Ethernet connected");
// Show disconnected/connecting/limited states clearly
if (Network.wifiStatus === "disconnected" || (!Network.wifi && !Network.ethernet))
return Translation.tr("Not connected");
if (Network.wifiStatus === "connecting")
return Translation.tr("Connecting…");
if (Network.wifiStatus === "disabled")
return Translation.tr("Wi-Fi is disabled");
if (!Network.networkName) return Translation.tr("Not connected");
let lines = [Translation.tr("Connected to %1").arg(Network.networkName)];
// Show limited connectivity warning inline
if (Network.wifiStatus === "limited")
lines.push(Translation.tr("⚠ Limited connectivity (no internet)"));
if (Network.active) {
lines.push(Network.networkStrength + "%");
if (Network.active.frequency) {
let ghz = Network.active.frequency > 5900 ? "6 GHz" : (Network.active.frequency > 4000 ? "5 GHz" : "2.4 GHz");
lines.push(ghz + " (" + Network.active.frequency + " MHz)");
}
if (Network.active.rate) {
lines.push(Network.active.rate);
}
if (Network.active.bssid) {
lines.push(Network.active.bssid);
}
}
return lines.join(" | ");
}
}
}
Revealer {
reveal: BluetoothStatus.available
Expand All @@ -947,6 +990,25 @@ Item { // Bar content region
text: BluetoothStatus.activeIcon
iconSize: Appearance.font.pixelSize.larger
color: rightSidebarButton.colText

HoverHandler {
id: btHover
}

StyledToolTip {
extraVisibleCondition: btHover.hovered
text: {
if (!BluetoothStatus.enabled) return Translation.tr("Bluetooth is disabled");
if (!BluetoothStatus.connected) return Translation.tr("Bluetooth disconnected");
let device = BluetoothStatus.firstActiveDevice;
if (!device) return Translation.tr("Bluetooth connected");
let devInfo = Translation.tr("Bluetooth Active") + " • " + (device.name || Translation.tr("Unknown device"));
if (device.batteryAvailable) {
devInfo += " 🔋 " + Math.round(device.battery * 100) + "%";
}
return devInfo;
}
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion modules/bar/BarTaskbarButton.qml
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ RippleButton {

// Monochrome overlay
Loader {
active: Config.options?.dock?.monochromeIcons ?? false
active: (Config.options?.dock?.monochromeIcons ?? false) && taskbarIcon.source.toString().includes(Config.options?.iconTheme ?? "yet-another-monochrome-icon-set")
anchors.fill: iconLoader
sourceComponent: Item {
Desaturate {
Expand Down
1 change: 1 addition & 0 deletions modules/bar/BatteryIndicator.qml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import QtQuick.Layouts

MouseArea {
id: root
acceptedButtons: Qt.NoButton
property bool borderless: Config.options.bar.borderless
readonly property var chargeState: Battery.chargeState
readonly property bool isCharging: Battery.isCharging
Expand Down
1 change: 1 addition & 0 deletions modules/bar/Resources.qml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import QtQuick.Layouts

MouseArea {
id: root
acceptedButtons: Qt.NoButton
property bool borderless: Config.options?.bar?.borderless ?? false
property bool alwaysShowAllResources: false
implicitWidth: rowLayout.implicitWidth + rowLayout.anchors.leftMargin + rowLayout.anchors.rightMargin
Expand Down
20 changes: 18 additions & 2 deletions modules/bar/SysTray.qml
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,32 @@ Item {
return item && item.id;
}

function isIgnored(item) {
if (!isValidItem(item)) return true;
const id = (item.id || "").toLowerCase();
const title = (item.title || "").toLowerCase();
const tooltip = (item.tooltipTitle || "").toLowerCase();
const ignored = Config.options?.bar?.tray?.ignoredItems
?? ["nm-applet", "network-manager-applet", "networkmanager-applet"];
for (let i = 0; i < ignored.length; i++) {
const pattern = ignored[i].toLowerCase();
if (id.indexOf(pattern) !== -1 || title.indexOf(pattern) !== -1 || tooltip.indexOf(pattern) !== -1) {
return true;
}
}
return false;
}

property list<var> itemsInUserList: SystemTray.items.values.filter(i => {
if (!isValidItem(i)) return false;
if (!isValidItem(i) || isIgnored(i)) return false;
const id = (i.id || "").toLowerCase();
const title = (i.title || "").toLowerCase();
const isSpotify = id.indexOf("spotify") !== -1 || title.indexOf("spotify") !== -1;
return (Config.options?.bar?.tray?.pinnedItems ?? []).includes(i.id)
&& (!smartTray || i.status !== Status.Passive || isSpotify);
})
property list<var> itemsNotInUserList: SystemTray.items.values.filter(i => {
if (!isValidItem(i)) return false;
if (!isValidItem(i) || isIgnored(i)) return false;
const id = (i.id || "").toLowerCase();
const title = (i.title || "").toLowerCase();
const isSpotify = id.indexOf("spotify") !== -1 || title.indexOf("spotify") !== -1;
Expand Down
14 changes: 7 additions & 7 deletions modules/bar/SysTrayItem.qml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ MouseArea {

hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
implicitWidth: 18
implicitHeight: 18
onPressed: (event) => {
implicitWidth: 24
implicitHeight: 24
onReleased: (event) => {
switch (event.button) {
case Qt.LeftButton: {
// Smart toggle: click to show, click again to minimize
Expand Down Expand Up @@ -102,15 +102,15 @@ MouseArea {
visible: !(Config.options?.bar?.tray?.monochromeIcons ?? false)
source: root.item?.icon ?? ""
anchors.centerIn: parent
width: parent.width
height: parent.height
width: 18
height: 18
}

Loader {
active: Config.options?.bar?.tray?.monochromeIcons ?? false
anchors.centerIn: parent
width: root.width
height: root.height
width: 18
height: 18
sourceComponent: Item {
IconImage {
id: tintedIcon
Expand Down
1 change: 1 addition & 0 deletions modules/bar/SysTrayMenu.qml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ PopupWindow {
// Fullscreen transparent backdrop for Niri to detect clicks outside
PanelWindow {
id: clickOutsideBackdrop
screen: root.screen
visible: root.visible && CompositorService.isNiri
color: "transparent"
exclusiveZone: 0
Expand Down
4 changes: 2 additions & 2 deletions modules/bar/Workspaces.qml
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ Item {
}

Loader {
active: wsConfig.monochromeIcons
active: wsConfig.monochromeIcons && mainAppIcon.source.toString().includes(Config.options?.iconTheme ?? "yet-another-monochrome-icon-set")
anchors.fill: mainAppIcon
sourceComponent: Item {
Desaturate {
Expand Down Expand Up @@ -660,7 +660,7 @@ Item {
}

Loader {
active: wsConfig.monochromeIcons
active: wsConfig.monochromeIcons && columnAppIcon.source.toString().includes(Config.options?.iconTheme ?? "yet-another-monochrome-icon-set")
anchors.fill: columnAppIcon
sourceComponent: Item {
Desaturate {
Expand Down
Loading