From 31e821250f9084192e4218e9ac7f71477664fe91 Mon Sep 17 00:00:00 2001 From: altrup <51763643+altrup@users.noreply.github.com> Date: Fri, 2 Jan 2026 16:04:01 -0500 Subject: [PATCH 01/10] Force focus workaround --- .../.config/quickshell/ii/services/GlobalFocusGrab.qml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml b/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml index 547d79e17..5fa3d2606 100644 --- a/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml +++ b/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml @@ -52,9 +52,17 @@ Singleton { } } + function hasActive(element) { + return element.activeFocus || Array.from( + element.children + ).some( + (child) => hasActive(child) + ); + } + HyprlandFocusGrab { id: grab - windows: [...root.persistent, ...root.dismissable] + windows: root.dismissable.some(w => hasActive(w.contentItem)) ? [...root.dismissable, ...root.persistent] : [...root.dismissable] active: root.dismissable.length > 0 onCleared: () => { root.dismiss(); From 5b27dfa7470dc50dba038a92bf642644e1309e40 Mon Sep 17 00:00:00 2001 From: altrup <51763643+altrup@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:54:44 -0500 Subject: [PATCH 02/10] Uncomment WlrLayerShell.keyboardFocus in Overview --- dots/.config/quickshell/ii/modules/ii/overview/Overview.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml b/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml index d94404721..fe3df64a3 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml @@ -24,7 +24,7 @@ Scope { WlrLayershell.namespace: "quickshell:overview" WlrLayershell.layer: WlrLayer.Top - // WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None + WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None color: "transparent" mask: Region { From cf2e944742a1a2f8109105af82d91f5246671284 Mon Sep 17 00:00:00 2001 From: altrup <51763643+altrup@users.noreply.github.com> Date: Sat, 10 Jan 2026 14:09:22 -0500 Subject: [PATCH 03/10] Add WlrKeyboardFocus.OnDemand to sidebar right when open --- .../quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml index 62946cb61..e7da61e44 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml @@ -22,8 +22,7 @@ Scope { exclusiveZone: 0 implicitWidth: sidebarWidth WlrLayershell.namespace: "quickshell:sidebarRight" - // Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab - // WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive + WlrLayershell.keyboardFocus: GlobalStates.sidebarRightOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None color: "transparent" anchors { From 94490f371b662a073977528b5720b50af06b891f Mon Sep 17 00:00:00 2001 From: altrup <51763643+altrup@users.noreply.github.com> Date: Mon, 12 Jan 2026 14:00:41 -0500 Subject: [PATCH 04/10] Fix case with unfocusable dismissables --- dots/.config/quickshell/ii/services/GlobalFocusGrab.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml b/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml index 5fa3d2606..6385d3df7 100644 --- a/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml +++ b/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml @@ -53,8 +53,8 @@ Singleton { } function hasActive(element) { - return element.activeFocus || Array.from( - element.children + return element?.activeFocus || Array.from( + element?.children ).some( (child) => hasActive(child) ); @@ -62,7 +62,7 @@ Singleton { HyprlandFocusGrab { id: grab - windows: root.dismissable.some(w => hasActive(w.contentItem)) ? [...root.dismissable, ...root.persistent] : [...root.dismissable] + windows: root.dismissable.every(w => !w?.focusable) || root.dismissable.some(w => hasActive(w?.contentItem)) ? [...root.dismissable, ...root.persistent] : [...root.dismissable] active: root.dismissable.length > 0 onCleared: () => { root.dismiss(); From 1932dcbab8eb73bb27126d3ebc02d7b41b535ced Mon Sep 17 00:00:00 2001 From: altrup <51763643+altrup@users.noreply.github.com> Date: Mon, 12 Jan 2026 14:58:04 -0500 Subject: [PATCH 05/10] Add keyboardFocus.OnDemand to WallpaperSelector --- .../ii/modules/ii/wallpaperSelector/WallpaperSelector.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperSelector.qml b/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperSelector.qml index 5bd79b56b..9d9ba9008 100644 --- a/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperSelector.qml +++ b/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperSelector.qml @@ -25,6 +25,7 @@ Scope { exclusionMode: ExclusionMode.Ignore WlrLayershell.namespace: "quickshell:wallpaperSelector" WlrLayershell.layer: WlrLayer.Overlay + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand color: "transparent" anchors.top: true From 15ceda494eea7a565f46f71ad0a451548f1f4969 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Fri, 30 Jan 2026 08:51:19 -0500 Subject: [PATCH 06/10] fix(quickshell): hide lock-screen temp workspace from UI, batch lock/unlock --- .../ii/modules/ii/bar/Workspaces.qml | 16 ++--- .../quickshell/ii/modules/ii/lock/Lock.qml | 60 +++++++++++++++---- .../ii/modules/ii/overview/OverviewWidget.qml | 8 ++- .../quickshell/ii/services/HyprlandData.qml | 4 +- 4 files changed, 64 insertions(+), 24 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml b/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml index f0a61ef23..0728a3e10 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml @@ -18,9 +18,11 @@ Item { property bool borderless: Config.options.bar.borderless readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen) readonly property Toplevel activeWindow: ToplevelManager.activeToplevel + // Clamp to avoid lock-screen temp workspace (2147483647 - N) leaking into UI + readonly property int effectiveActiveWorkspaceId: Math.max(1, Math.min(100, monitor?.activeWorkspace?.id ?? 1)) readonly property int workspacesShown: Config.options.bar.workspaces.shown - readonly property int workspaceGroup: Math.floor((monitor?.activeWorkspace?.id - 1) / root.workspacesShown) + readonly property int workspaceGroup: Math.floor((effectiveActiveWorkspaceId - 1) / root.workspacesShown) property list workspaceOccupied: [] property int widgetPadding: 4 property int workspaceButtonWidth: 26 @@ -29,7 +31,7 @@ Item { property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55 property real workspaceIconOpacityShrinked: 1 property real workspaceIconMarginShrinked: -4 - property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % root.workspacesShown + property int workspaceIndexInGroup: (effectiveActiveWorkspaceId - 1) % root.workspacesShown property bool showNumbers: false Timer { @@ -122,8 +124,8 @@ Item { implicitWidth: workspaceButtonWidth implicitHeight: workspaceButtonWidth radius: (width / 2) - property var previousOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index)) - property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index+2)) + property var previousOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && root.effectiveActiveWorkspaceId === index)) + property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && root.effectiveActiveWorkspaceId === index+2)) property var radiusPrev: previousOccupied ? 0 : (width / 2) property var radiusNext: rightOccupied ? 0 : (width / 2) @@ -133,7 +135,7 @@ Item { bottomRightRadius: radiusNext color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4) - opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index+1)) ? 1 : 0 + opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && root.effectiveActiveWorkspaceId === index+1)) ? 1 : 0 Behavior on opacity { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) @@ -225,7 +227,7 @@ Item { } text: Config.options?.bar.workspaces.numberMap[button.workspaceValue - 1] || button.workspaceValue elide: Text.ElideRight - color: (monitor?.activeWorkspace?.id == button.workspaceValue) ? + color: (root.effectiveActiveWorkspaceId == button.workspaceValue) ? Appearance.m3colors.m3onPrimary : (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1Inactive) @@ -245,7 +247,7 @@ Item { width: workspaceButtonWidth * 0.18 height: width radius: width / 2 - color: (monitor?.activeWorkspace?.id == button.workspaceValue) ? + color: (root.effectiveActiveWorkspaceId == button.workspaceValue) ? Appearance.m3colors.m3onPrimary : (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1Inactive) diff --git a/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml b/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml index 7cbd68030..33927f8b5 100644 --- a/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml +++ b/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml @@ -11,11 +11,57 @@ import Quickshell.Hyprland LockScreen { id: root + // Monitor name -> workspace id to restore on unlock (set when locking) + property var savedWorkspaces: ({}) + + Timer { + id: restoreTimer + interval: 150 + repeat: false + onTriggered: { + var batch = "" + for (var j = 0; j < Quickshell.screens.length; ++j) { + var monName = Quickshell.screens[j].name + var wsId = root.savedWorkspaces[monName] + if (wsId !== undefined && wsId >= 1 && wsId <= 100) { + batch += "dispatch focusmonitor " + monName + "; dispatch workspace " + wsId + "; " + } + } + if (batch.length > 0) { + Quickshell.execDetached(["hyprctl", "--batch", batch + "reload"]) + } + } + } + lockSurface: LockSurface { context: root.context } - // Push everything down + // Single batch for lock and unlock so we don't race multiple hyprctl calls + Connections { + target: GlobalStates + function onScreenLockedChanged() { + if (GlobalStates.screenLocked) { + // Lock: save workspace per monitor and move all to temp workspace in one batch + var next = {} + var batch = "keyword animation workspaces,1,7,menu_decel,slidevert; " + for (var i = 0; i < Quickshell.screens.length; ++i) { + var mon = Quickshell.screens[i].name + var mData = HyprlandData.monitors.find(m => m.name === mon) + var ws = (mData?.activeWorkspace?.id ?? 1) + if (ws < 1 || ws > 100) ws = 1 + next[mon] = ws + batch += "dispatch focusmonitor " + mon + "; dispatch workspace " + (2147483647 - ws) + "; " + } + root.savedWorkspaces = next + Quickshell.execDetached(["hyprctl", "--batch", batch + "reload"]) + } else { + restoreTimer.start() + } + } + } + + // Push everything down (visual only; workspace switch is in Connections above) Variants { model: Quickshell.screens delegate: Scope { @@ -24,18 +70,6 @@ LockScreen { property string targetMonitorName: modelData.name property int verticalMovementDistance: modelData.height property int horizontalSqueeze: modelData.width * 0.2 - property int lastWorkspaceId - onShouldPushChanged: { - if (shouldPush) { - // Save workspace - print(targetMonitorName) - lastWorkspaceId = HyprlandData.monitors.find(m => m.name == targetMonitorName).activeWorkspace.id - // Set anim to vertical and move to very very big workspace for a sliding up effect - Quickshell.execDetached(["hyprctl", "--batch", `keyword animation workspaces,1,7,menu_decel,slidevert; dispatch workspace ${2147483647 - lastWorkspaceId}`]); - } else { - Quickshell.execDetached(["hyprctl", "--batch", `dispatch workspace ${lastWorkspaceId}; reload`]); - } - } } } } diff --git a/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml b/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml index f2a600c6d..96d72d9d1 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml @@ -16,8 +16,10 @@ Item { required property var screen readonly property HyprlandMonitor monitor: Hyprland.monitorFor(screen) readonly property var toplevels: ToplevelManager.toplevels + // Clamp to avoid lock-screen temp workspace (2147483647 - N) leaking into UI + readonly property int effectiveActiveWorkspaceId: Math.max(1, Math.min(100, monitor?.activeWorkspace?.id ?? 1)) readonly property int workspacesShown: Config.options.overview.rows * Config.options.overview.columns - readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / workspacesShown) + readonly property int workspaceGroup: Math.floor((effectiveActiveWorkspaceId - 1) / workspacesShown) property bool monitorIsFocused: (Hyprland.focusedMonitor?.name == monitor.name) property var windows: HyprlandData.windowList property var windowByAddress: HyprlandData.windowByAddress @@ -301,8 +303,8 @@ Item { Rectangle { // Focused workspace indicator id: focusedWorkspaceIndicator - property int rowIndex: getWsRow(monitor.activeWorkspace?.id) - property int colIndex: getWsColumn(monitor.activeWorkspace?.id) + property int rowIndex: getWsRow(root.effectiveActiveWorkspaceId) + property int colIndex: getWsColumn(root.effectiveActiveWorkspaceId) x: (root.workspaceImplicitWidth + workspaceSpacing) * colIndex y: (root.workspaceImplicitHeight + workspaceSpacing) * rowIndex z: root.windowZ diff --git a/dots/.config/quickshell/ii/services/HyprlandData.qml b/dots/.config/quickshell/ii/services/HyprlandData.qml index 5ec7bd68d..5dfedab6a 100644 --- a/dots/.config/quickshell/ii/services/HyprlandData.qml +++ b/dots/.config/quickshell/ii/services/HyprlandData.qml @@ -139,7 +139,9 @@ Singleton { stdout: StdioCollector { id: workspacesCollector onStreamFinished: { - root.workspaces = JSON.parse(workspacesCollector.text); + var rawWorkspaces = JSON.parse(workspacesCollector.text); + // Filter out invalid workspace ids (e.g. lock-screen temp workspace 2147483647 - N) + root.workspaces = rawWorkspaces.filter(ws => ws.id >= 1 && ws.id <= 100); let tempWorkspaceById = {}; for (var i = 0; i < root.workspaces.length; ++i) { var ws = root.workspaces[i]; From 68f0355940f374ec4dcc168be3f0e123e7635bfb Mon Sep 17 00:00:00 2001 From: ATDMA Date: Sun, 1 Feb 2026 17:06:53 -0500 Subject: [PATCH 07/10] refactor(quickshell): simplify workspace ID handling in Workspaces.qml and Lock.qml --- dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml | 3 +-- dots/.config/quickshell/ii/modules/ii/lock/Lock.qml | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml b/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml index 0728a3e10..36c488730 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml @@ -18,8 +18,7 @@ Item { property bool borderless: Config.options.bar.borderless readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen) readonly property Toplevel activeWindow: ToplevelManager.activeToplevel - // Clamp to avoid lock-screen temp workspace (2147483647 - N) leaking into UI - readonly property int effectiveActiveWorkspaceId: Math.max(1, Math.min(100, monitor?.activeWorkspace?.id ?? 1)) + readonly property int effectiveActiveWorkspaceId: monitor?.activeWorkspace?.id ?? 1 readonly property int workspacesShown: Config.options.bar.workspaces.shown readonly property int workspaceGroup: Math.floor((effectiveActiveWorkspaceId - 1) / root.workspacesShown) diff --git a/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml b/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml index 33927f8b5..1837480ad 100644 --- a/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml +++ b/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml @@ -23,7 +23,7 @@ LockScreen { for (var j = 0; j < Quickshell.screens.length; ++j) { var monName = Quickshell.screens[j].name var wsId = root.savedWorkspaces[monName] - if (wsId !== undefined && wsId >= 1 && wsId <= 100) { + if (wsId !== undefined) { batch += "dispatch focusmonitor " + monName + "; dispatch workspace " + wsId + "; " } } @@ -49,7 +49,6 @@ LockScreen { var mon = Quickshell.screens[i].name var mData = HyprlandData.monitors.find(m => m.name === mon) var ws = (mData?.activeWorkspace?.id ?? 1) - if (ws < 1 || ws > 100) ws = 1 next[mon] = ws batch += "dispatch focusmonitor " + mon + "; dispatch workspace " + (2147483647 - ws) + "; " } From 3a14cc644b00582e8213a8412bddf344e8a095c2 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:08:22 +0100 Subject: [PATCH 08/10] ai: remove openrouter deepseek --- dots/.config/quickshell/ii/services/Ai.qml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/dots/.config/quickshell/ii/services/Ai.qml b/dots/.config/quickshell/ii/services/Ai.qml index 580b3cfdb..a273415f5 100644 --- a/dots/.config/quickshell/ii/services/Ai.qml +++ b/dots/.config/quickshell/ii/services/Ai.qml @@ -294,18 +294,6 @@ Singleton { "key_get_description": Translation.tr("**Instructions**: Log into Mistral account, go to Keys on the sidebar, click Create new key"), "api_format": "mistral", }), - "openrouter-deepseek-r1": aiModelComponent.createObject(this, { - "name": "DeepSeek R1", - "icon": "deepseek-symbolic", - "description": Translation.tr("Online via %1 | %2's model").arg("OpenRouter").arg("DeepSeek"), - "homepage": "https://openrouter.ai/deepseek/deepseek-r1:free", - "endpoint": "https://openrouter.ai/api/v1/chat/completions", - "model": "deepseek/deepseek-r1:free", - "requires_key": true, - "key_id": "openrouter", - "key_get_link": "https://openrouter.ai/settings/keys", - "key_get_description": Translation.tr("**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key"), - }), } property var modelList: Object.keys(root.models) property var currentModelId: Persistent.states?.ai?.model || modelList[0] From 9e9c6b70b68c3f6734e138de302c0ab38a3ef32c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:50:34 +0100 Subject: [PATCH 09/10] Update shapes --- dots/.config/quickshell/ii/modules/common/widgets/shapes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/shapes b/dots/.config/quickshell/ii/modules/common/widgets/shapes index 7f0f0709e..b62e4482a 160000 --- a/dots/.config/quickshell/ii/modules/common/widgets/shapes +++ b/dots/.config/quickshell/ii/modules/common/widgets/shapes @@ -1 +1 @@ -Subproject commit 7f0f0709ec5bbe4c3158c7f5fc68fd890af46618 +Subproject commit b62e4482af56722840dd4b86c4dab41d5c9f2dda From d23d39df0453c92b34878e2f026218d2de083a90 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:50:54 +0100 Subject: [PATCH 10/10] fish: more typo aliases --- dots/.config/fish/config.fish | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dots/.config/fish/config.fish b/dots/.config/fish/config.fish index 72476660f..0d26fe1dc 100755 --- a/dots/.config/fish/config.fish +++ b/dots/.config/fish/config.fish @@ -18,9 +18,11 @@ if status is-interactive # Commands to run in interactive sessions can go here end # Aliases - alias pamcan pacman + alias clear "printf '\033[2J\033[3J\033[1;1H'" # fix: kitty doesn't clear properly + alias celar "printf '\033[2J\033[3J\033[1;1H'" + alias claer "printf '\033[2J\033[3J\033[1;1H'" alias ls 'eza --icons' - alias clear "printf '\033[2J\033[3J\033[1;1H'" + alias pamcan pacman alias q 'qs -c ii' end