KDE: window enumeration + raise, and reverse-DNS layout identity fixes (#133) - #158

Merged
jonocodes merged 2 commits into
mainfrom
issue-133
Aug 15, 2026
Merged

KDE: window enumeration + raise, and reverse-DNS layout identity fixes (#133)#158
jonocodes merged 2 commits into
mainfrom
issue-133

Conversation

@jonocodes

Copy link
Copy Markdown
Owner

Closes#133 (field-verification follow-ups).

Changes

Two commits:

  1. feat(kde): window enumeration + raise, reaching GNOME parity (3a345a7)
    KWin script pushes the active window + full window list into a daemon-owned org.deckd.Focus; daemon serves GetActiveWindow / ListWindows, and raise is enqueued-and-polled by the KWin script. Verified live on Plasma 6.5.6 Wayland.

  2. fix(layouts): reverse-DNS identity matching + packaging-suffix labels (c2d5bd2)
    Two live field bugs found while verifying KDE backend advertises watch_windows/raise_window it can't fulfill #133:

    • Layout.matches_identity matched identities exactly, so a bare konsole token never matched KDE/GNOME's reverse-DNS wm_class (org.kde.konsole) — Konsole silently fell to the default layout. Now also matches the identity's last dotted segment; full tokens still match only their exact identity.
    • _humanize_identity took the last dotted segment, so org.telegram.desktop labeled "Desktop" instead of "Telegram". Now strips trailing .desktop/.app packaging segments first.

Verification

  • Full daemon suite: 713 passed (3 new tests for the fixes)
  • pyright clean on changed lines
  • Live on KDE Plasma 6.5.6 Wayland: konsole resolves to the Terminal layout, Telegram labels correctly, both layout buttons and raise work end-to-end

Environment note

On the verification machine, layout buttons (uinput injection) required granting /dev/uinput access (udev rule + input group) — fixed in the separate host config repo, not this PR.

jonocodesand others added 2 commits August 15, 2026 07:24
The #133 short-term fix dropped KdeFocusBackend to focus-only because the
KWin script could only push the active window in. This follow-up teaches the
KWin script + daemon cache to answer enumeration and raise, so KDE honestly
re-advertises watch_windows / raise_window / raise_app.
Grounded in a primary-source pass over KWin master (windowList(), writable
workspace.activeWindow, and — the crux — a QTimer global in the script
sandbox), the push architecture is extended both directions the outbound-only
callDBus constraint allows:
- Enumeration inverts to push: the KWin script pushes the full window list
(UpdateWindowList) into the same cache the daemon serves back on
ListWindows(); the inherited GnomeShellFocusBackend.watch_windows gdbus-poll
is answered unchanged.
- Raise inverts to enqueue-and-poll: since the daemon can't call into KWin,
raise_window / raise_app resolve the id against the daemon's own cached
window list and enqueue it; the script drains the queue on a QTimer tick
(DrainPendingRaises) and sets workspace.activeWindow.
Wire shape stays byte-identical to GNOME: resourceClass -> wm_class,
desktopFileName -> sandboxed_app_id, focused-first ordering, 0-based
workspace index, retired-id -> RaiseWindowFailed. skipTaskbar is the sole
enumeration filter (KDE's native switcher signal), matching GNOME's
inclusiveness.
Tests: cache/service/backend covered test-first in tests/test_platform_kde.py;
the real main.js is driven headless via a node:vm sandbox in the new
scripts/test_kwin_focus_bridge.mjs (wired into `just test` / `test-focus-wire`).
docs/PLATFORM-PARITY.md flips the KDE column to ✓ (parity test #136 stays
green) and documents the inverted plumbing; docs/TESTING.md notes the rows are
code-and-CI truth, unverified on hardware.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…#133)
Konsole (org.kde.konsole) fell to the default layout because the layout
matcher compared identities exactly, and a bare `konsole` token never
matched KDE/GNOME's reverse-DNS wm_class. Running-windows labels took the
last dotted segment, so org.telegram.desktop labeled 'Desktop' instead of
'Telegram'.
- matches_identity also matches the identity's last dotted segment against
layout tokens (org.kde.konsole -> konsole); full tokens still match only
their own exact identity.
- _humanize_identity strips trailing .desktop/.app packaging segments
before the last-dotted-segment split.
- Tests for both; PLATFORM-PARITY.md KDE bullet updated.
@jonocodes
jonocodes merged commit b16a533 into mainAug 15, 2026
1 of 2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KDE backend advertises watch_windows/raise_window it can't fulfill

1 participant

@jonocodes
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

KDE: window enumeration + raise, and reverse-DNS layout identity fixes (#133) - #158

Merged
jonocodes merged 2 commits into
mainfrom
issue-133
Aug 15, 2026
Merged

KDE: window enumeration + raise, and reverse-DNS layout identity fixes (#133)#158
jonocodes merged 2 commits into
mainfrom
issue-133

Conversation

@jonocodes

Copy link
Copy Markdown
Owner

Closes#133 (field-verification follow-ups).

Changes

Two commits:

  1. feat(kde): window enumeration + raise, reaching GNOME parity (3a345a7)
    KWin script pushes the active window + full window list into a daemon-owned org.deckd.Focus; daemon serves GetActiveWindow / ListWindows, and raise is enqueued-and-polled by the KWin script. Verified live on Plasma 6.5.6 Wayland.

  2. fix(layouts): reverse-DNS identity matching + packaging-suffix labels (c2d5bd2)
    Two live field bugs found while verifying KDE backend advertises watch_windows/raise_window it can't fulfill #133:

    • Layout.matches_identity matched identities exactly, so a bare konsole token never matched KDE/GNOME's reverse-DNS wm_class (org.kde.konsole) — Konsole silently fell to the default layout. Now also matches the identity's last dotted segment; full tokens still match only their exact identity.
    • _humanize_identity took the last dotted segment, so org.telegram.desktop labeled "Desktop" instead of "Telegram". Now strips trailing .desktop/.app packaging segments first.

Verification

  • Full daemon suite: 713 passed (3 new tests for the fixes)
  • pyright clean on changed lines
  • Live on KDE Plasma 6.5.6 Wayland: konsole resolves to the Terminal layout, Telegram labels correctly, both layout buttons and raise work end-to-end

Environment note

On the verification machine, layout buttons (uinput injection) required granting /dev/uinput access (udev rule + input group) — fixed in the separate host config repo, not this PR.

jonocodesand others added 2 commits August 15, 2026 07:24
The #133 short-term fix dropped KdeFocusBackend to focus-only because the
KWin script could only push the active window in. This follow-up teaches the
KWin script + daemon cache to answer enumeration and raise, so KDE honestly
re-advertises watch_windows / raise_window / raise_app.
Grounded in a primary-source pass over KWin master (windowList(), writable
workspace.activeWindow, and — the crux — a QTimer global in the script
sandbox), the push architecture is extended both directions the outbound-only
callDBus constraint allows:
- Enumeration inverts to push: the KWin script pushes the full window list
(UpdateWindowList) into the same cache the daemon serves back on
ListWindows(); the inherited GnomeShellFocusBackend.watch_windows gdbus-poll
is answered unchanged.
- Raise inverts to enqueue-and-poll: since the daemon can't call into KWin,
raise_window / raise_app resolve the id against the daemon's own cached
window list and enqueue it; the script drains the queue on a QTimer tick
(DrainPendingRaises) and sets workspace.activeWindow.
Wire shape stays byte-identical to GNOME: resourceClass -> wm_class,
desktopFileName -> sandboxed_app_id, focused-first ordering, 0-based
workspace index, retired-id -> RaiseWindowFailed. skipTaskbar is the sole
enumeration filter (KDE's native switcher signal), matching GNOME's
inclusiveness.
Tests: cache/service/backend covered test-first in tests/test_platform_kde.py;
the real main.js is driven headless via a node:vm sandbox in the new
scripts/test_kwin_focus_bridge.mjs (wired into `just test` / `test-focus-wire`).
docs/PLATFORM-PARITY.md flips the KDE column to ✓ (parity test #136 stays
green) and documents the inverted plumbing; docs/TESTING.md notes the rows are
code-and-CI truth, unverified on hardware.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…#133)
Konsole (org.kde.konsole) fell to the default layout because the layout
matcher compared identities exactly, and a bare `konsole` token never
matched KDE/GNOME's reverse-DNS wm_class. Running-windows labels took the
last dotted segment, so org.telegram.desktop labeled 'Desktop' instead of
'Telegram'.
- matches_identity also matches the identity's last dotted segment against
layout tokens (org.kde.konsole -> konsole); full tokens still match only
their own exact identity.
- _humanize_identity strips trailing .desktop/.app packaging segments
before the last-dotted-segment split.
- Tests for both; PLATFORM-PARITY.md KDE bullet updated.
@jonocodes
jonocodes merged commit b16a533 into mainAug 15, 2026
1 of 2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KDE backend advertises watch_windows/raise_window it can't fulfill

1 participant

@jonocodes
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

KDE: window enumeration + raise, and reverse-DNS layout identity fixes (#133) - #158

Merged
jonocodes merged 2 commits into
mainfrom
issue-133
Aug 15, 2026
Merged

KDE: window enumeration + raise, and reverse-DNS layout identity fixes (#133)#158
jonocodes merged 2 commits into
mainfrom
issue-133

Conversation

@jonocodes

Copy link
Copy Markdown
Owner

Closes#133 (field-verification follow-ups).

Changes

Two commits:

  1. feat(kde): window enumeration + raise, reaching GNOME parity (3a345a7)
    KWin script pushes the active window + full window list into a daemon-owned org.deckd.Focus; daemon serves GetActiveWindow / ListWindows, and raise is enqueued-and-polled by the KWin script. Verified live on Plasma 6.5.6 Wayland.

  2. fix(layouts): reverse-DNS identity matching + packaging-suffix labels (c2d5bd2)
    Two live field bugs found while verifying KDE backend advertises watch_windows/raise_window it can't fulfill #133:

    • Layout.matches_identity matched identities exactly, so a bare konsole token never matched KDE/GNOME's reverse-DNS wm_class (org.kde.konsole) — Konsole silently fell to the default layout. Now also matches the identity's last dotted segment; full tokens still match only their exact identity.
    • _humanize_identity took the last dotted segment, so org.telegram.desktop labeled "Desktop" instead of "Telegram". Now strips trailing .desktop/.app packaging segments first.

Verification

  • Full daemon suite: 713 passed (3 new tests for the fixes)
  • pyright clean on changed lines
  • Live on KDE Plasma 6.5.6 Wayland: konsole resolves to the Terminal layout, Telegram labels correctly, both layout buttons and raise work end-to-end

Environment note

On the verification machine, layout buttons (uinput injection) required granting /dev/uinput access (udev rule + input group) — fixed in the separate host config repo, not this PR.

jonocodesand others added 2 commits August 15, 2026 07:24
The #133 short-term fix dropped KdeFocusBackend to focus-only because the
KWin script could only push the active window in. This follow-up teaches the
KWin script + daemon cache to answer enumeration and raise, so KDE honestly
re-advertises watch_windows / raise_window / raise_app.
Grounded in a primary-source pass over KWin master (windowList(), writable
workspace.activeWindow, and — the crux — a QTimer global in the script
sandbox), the push architecture is extended both directions the outbound-only
callDBus constraint allows:
- Enumeration inverts to push: the KWin script pushes the full window list
(UpdateWindowList) into the same cache the daemon serves back on
ListWindows(); the inherited GnomeShellFocusBackend.watch_windows gdbus-poll
is answered unchanged.
- Raise inverts to enqueue-and-poll: since the daemon can't call into KWin,
raise_window / raise_app resolve the id against the daemon's own cached
window list and enqueue it; the script drains the queue on a QTimer tick
(DrainPendingRaises) and sets workspace.activeWindow.
Wire shape stays byte-identical to GNOME: resourceClass -> wm_class,
desktopFileName -> sandboxed_app_id, focused-first ordering, 0-based
workspace index, retired-id -> RaiseWindowFailed. skipTaskbar is the sole
enumeration filter (KDE's native switcher signal), matching GNOME's
inclusiveness.
Tests: cache/service/backend covered test-first in tests/test_platform_kde.py;
the real main.js is driven headless via a node:vm sandbox in the new
scripts/test_kwin_focus_bridge.mjs (wired into `just test` / `test-focus-wire`).
docs/PLATFORM-PARITY.md flips the KDE column to ✓ (parity test #136 stays
green) and documents the inverted plumbing; docs/TESTING.md notes the rows are
code-and-CI truth, unverified on hardware.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…#133)
Konsole (org.kde.konsole) fell to the default layout because the layout
matcher compared identities exactly, and a bare `konsole` token never
matched KDE/GNOME's reverse-DNS wm_class. Running-windows labels took the
last dotted segment, so org.telegram.desktop labeled 'Desktop' instead of
'Telegram'.
- matches_identity also matches the identity's last dotted segment against
layout tokens (org.kde.konsole -> konsole); full tokens still match only
their own exact identity.
- _humanize_identity strips trailing .desktop/.app packaging segments
before the last-dotted-segment split.
- Tests for both; PLATFORM-PARITY.md KDE bullet updated.
@jonocodes
jonocodes merged commit b16a533 into mainAug 15, 2026
1 of 2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KDE backend advertises watch_windows/raise_window it can't fulfill

1 participant

@jonocodes
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

KDE: window enumeration + raise, and reverse-DNS layout identity fixes (#133) - #158

Merged
jonocodes merged 2 commits into
mainfrom
issue-133
Aug 15, 2026
Merged

KDE: window enumeration + raise, and reverse-DNS layout identity fixes (#133)#158
jonocodes merged 2 commits into
mainfrom
issue-133

Conversation

@jonocodes

Copy link
Copy Markdown
Owner

Closes#133 (field-verification follow-ups).

Changes

Two commits:

  1. feat(kde): window enumeration + raise, reaching GNOME parity (3a345a7)
    KWin script pushes the active window + full window list into a daemon-owned org.deckd.Focus; daemon serves GetActiveWindow / ListWindows, and raise is enqueued-and-polled by the KWin script. Verified live on Plasma 6.5.6 Wayland.

  2. fix(layouts): reverse-DNS identity matching + packaging-suffix labels (c2d5bd2)
    Two live field bugs found while verifying KDE backend advertises watch_windows/raise_window it can't fulfill #133:

    • Layout.matches_identity matched identities exactly, so a bare konsole token never matched KDE/GNOME's reverse-DNS wm_class (org.kde.konsole) — Konsole silently fell to the default layout. Now also matches the identity's last dotted segment; full tokens still match only their exact identity.
    • _humanize_identity took the last dotted segment, so org.telegram.desktop labeled "Desktop" instead of "Telegram". Now strips trailing .desktop/.app packaging segments first.

Verification

  • Full daemon suite: 713 passed (3 new tests for the fixes)
  • pyright clean on changed lines
  • Live on KDE Plasma 6.5.6 Wayland: konsole resolves to the Terminal layout, Telegram labels correctly, both layout buttons and raise work end-to-end

Environment note

On the verification machine, layout buttons (uinput injection) required granting /dev/uinput access (udev rule + input group) — fixed in the separate host config repo, not this PR.

jonocodesand others added 2 commits August 15, 2026 07:24
The #133 short-term fix dropped KdeFocusBackend to focus-only because the
KWin script could only push the active window in. This follow-up teaches the
KWin script + daemon cache to answer enumeration and raise, so KDE honestly
re-advertises watch_windows / raise_window / raise_app.
Grounded in a primary-source pass over KWin master (windowList(), writable
workspace.activeWindow, and — the crux — a QTimer global in the script
sandbox), the push architecture is extended both directions the outbound-only
callDBus constraint allows:
- Enumeration inverts to push: the KWin script pushes the full window list
(UpdateWindowList) into the same cache the daemon serves back on
ListWindows(); the inherited GnomeShellFocusBackend.watch_windows gdbus-poll
is answered unchanged.
- Raise inverts to enqueue-and-poll: since the daemon can't call into KWin,
raise_window / raise_app resolve the id against the daemon's own cached
window list and enqueue it; the script drains the queue on a QTimer tick
(DrainPendingRaises) and sets workspace.activeWindow.
Wire shape stays byte-identical to GNOME: resourceClass -> wm_class,
desktopFileName -> sandboxed_app_id, focused-first ordering, 0-based
workspace index, retired-id -> RaiseWindowFailed. skipTaskbar is the sole
enumeration filter (KDE's native switcher signal), matching GNOME's
inclusiveness.
Tests: cache/service/backend covered test-first in tests/test_platform_kde.py;
the real main.js is driven headless via a node:vm sandbox in the new
scripts/test_kwin_focus_bridge.mjs (wired into `just test` / `test-focus-wire`).
docs/PLATFORM-PARITY.md flips the KDE column to ✓ (parity test #136 stays
green) and documents the inverted plumbing; docs/TESTING.md notes the rows are
code-and-CI truth, unverified on hardware.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…#133)
Konsole (org.kde.konsole) fell to the default layout because the layout
matcher compared identities exactly, and a bare `konsole` token never
matched KDE/GNOME's reverse-DNS wm_class. Running-windows labels took the
last dotted segment, so org.telegram.desktop labeled 'Desktop' instead of
'Telegram'.
- matches_identity also matches the identity's last dotted segment against
layout tokens (org.kde.konsole -> konsole); full tokens still match only
their own exact identity.
- _humanize_identity strips trailing .desktop/.app packaging segments
before the last-dotted-segment split.
- Tests for both; PLATFORM-PARITY.md KDE bullet updated.
@jonocodes
jonocodes merged commit b16a533 into mainAug 15, 2026
1 of 2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KDE backend advertises watch_windows/raise_window it can't fulfill

1 participant

@jonocodes
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

KDE: window enumeration + raise, and reverse-DNS layout identity fixes (#133) - #158

Merged
jonocodes merged 2 commits into
mainfrom
issue-133
Aug 15, 2026
Merged

KDE: window enumeration + raise, and reverse-DNS layout identity fixes (#133)#158
jonocodes merged 2 commits into
mainfrom
issue-133

Conversation

@jonocodes

Copy link
Copy Markdown
Owner

Closes#133 (field-verification follow-ups).

Changes

Two commits:

  1. feat(kde): window enumeration + raise, reaching GNOME parity (3a345a7)
    KWin script pushes the active window + full window list into a daemon-owned org.deckd.Focus; daemon serves GetActiveWindow / ListWindows, and raise is enqueued-and-polled by the KWin script. Verified live on Plasma 6.5.6 Wayland.

  2. fix(layouts): reverse-DNS identity matching + packaging-suffix labels (c2d5bd2)
    Two live field bugs found while verifying KDE backend advertises watch_windows/raise_window it can't fulfill #133:

    • Layout.matches_identity matched identities exactly, so a bare konsole token never matched KDE/GNOME's reverse-DNS wm_class (org.kde.konsole) — Konsole silently fell to the default layout. Now also matches the identity's last dotted segment; full tokens still match only their exact identity.
    • _humanize_identity took the last dotted segment, so org.telegram.desktop labeled "Desktop" instead of "Telegram". Now strips trailing .desktop/.app packaging segments first.

Verification

  • Full daemon suite: 713 passed (3 new tests for the fixes)
  • pyright clean on changed lines
  • Live on KDE Plasma 6.5.6 Wayland: konsole resolves to the Terminal layout, Telegram labels correctly, both layout buttons and raise work end-to-end

Environment note

On the verification machine, layout buttons (uinput injection) required granting /dev/uinput access (udev rule + input group) — fixed in the separate host config repo, not this PR.

jonocodesand others added 2 commits August 15, 2026 07:24
The #133 short-term fix dropped KdeFocusBackend to focus-only because the
KWin script could only push the active window in. This follow-up teaches the
KWin script + daemon cache to answer enumeration and raise, so KDE honestly
re-advertises watch_windows / raise_window / raise_app.
Grounded in a primary-source pass over KWin master (windowList(), writable
workspace.activeWindow, and — the crux — a QTimer global in the script
sandbox), the push architecture is extended both directions the outbound-only
callDBus constraint allows:
- Enumeration inverts to push: the KWin script pushes the full window list
(UpdateWindowList) into the same cache the daemon serves back on
ListWindows(); the inherited GnomeShellFocusBackend.watch_windows gdbus-poll
is answered unchanged.
- Raise inverts to enqueue-and-poll: since the daemon can't call into KWin,
raise_window / raise_app resolve the id against the daemon's own cached
window list and enqueue it; the script drains the queue on a QTimer tick
(DrainPendingRaises) and sets workspace.activeWindow.
Wire shape stays byte-identical to GNOME: resourceClass -> wm_class,
desktopFileName -> sandboxed_app_id, focused-first ordering, 0-based
workspace index, retired-id -> RaiseWindowFailed. skipTaskbar is the sole
enumeration filter (KDE's native switcher signal), matching GNOME's
inclusiveness.
Tests: cache/service/backend covered test-first in tests/test_platform_kde.py;
the real main.js is driven headless via a node:vm sandbox in the new
scripts/test_kwin_focus_bridge.mjs (wired into `just test` / `test-focus-wire`).
docs/PLATFORM-PARITY.md flips the KDE column to ✓ (parity test #136 stays
green) and documents the inverted plumbing; docs/TESTING.md notes the rows are
code-and-CI truth, unverified on hardware.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…#133)
Konsole (org.kde.konsole) fell to the default layout because the layout
matcher compared identities exactly, and a bare `konsole` token never
matched KDE/GNOME's reverse-DNS wm_class. Running-windows labels took the
last dotted segment, so org.telegram.desktop labeled 'Desktop' instead of
'Telegram'.
- matches_identity also matches the identity's last dotted segment against
layout tokens (org.kde.konsole -> konsole); full tokens still match only
their own exact identity.
- _humanize_identity strips trailing .desktop/.app packaging segments
before the last-dotted-segment split.
- Tests for both; PLATFORM-PARITY.md KDE bullet updated.
@jonocodes
jonocodes merged commit b16a533 into mainAug 15, 2026
1 of 2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KDE backend advertises watch_windows/raise_window it can't fulfill

1 participant

@jonocodes
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

KDE: window enumeration + raise, and reverse-DNS layout identity fixes (#133) - #158

Merged
jonocodes merged 2 commits into
mainfrom
issue-133
Aug 15, 2026
Merged

KDE: window enumeration + raise, and reverse-DNS layout identity fixes (#133)#158
jonocodes merged 2 commits into
mainfrom
issue-133

Conversation

@jonocodes

Copy link
Copy Markdown
Owner

Closes#133 (field-verification follow-ups).

Changes

Two commits:

  1. feat(kde): window enumeration + raise, reaching GNOME parity (3a345a7)
    KWin script pushes the active window + full window list into a daemon-owned org.deckd.Focus; daemon serves GetActiveWindow / ListWindows, and raise is enqueued-and-polled by the KWin script. Verified live on Plasma 6.5.6 Wayland.

  2. fix(layouts): reverse-DNS identity matching + packaging-suffix labels (c2d5bd2)
    Two live field bugs found while verifying KDE backend advertises watch_windows/raise_window it can't fulfill #133:

    • Layout.matches_identity matched identities exactly, so a bare konsole token never matched KDE/GNOME's reverse-DNS wm_class (org.kde.konsole) — Konsole silently fell to the default layout. Now also matches the identity's last dotted segment; full tokens still match only their exact identity.
    • _humanize_identity took the last dotted segment, so org.telegram.desktop labeled "Desktop" instead of "Telegram". Now strips trailing .desktop/.app packaging segments first.

Verification

  • Full daemon suite: 713 passed (3 new tests for the fixes)
  • pyright clean on changed lines
  • Live on KDE Plasma 6.5.6 Wayland: konsole resolves to the Terminal layout, Telegram labels correctly, both layout buttons and raise work end-to-end

Environment note

On the verification machine, layout buttons (uinput injection) required granting /dev/uinput access (udev rule + input group) — fixed in the separate host config repo, not this PR.

jonocodesand others added 2 commits August 15, 2026 07:24
The #133 short-term fix dropped KdeFocusBackend to focus-only because the
KWin script could only push the active window in. This follow-up teaches the
KWin script + daemon cache to answer enumeration and raise, so KDE honestly
re-advertises watch_windows / raise_window / raise_app.
Grounded in a primary-source pass over KWin master (windowList(), writable
workspace.activeWindow, and — the crux — a QTimer global in the script
sandbox), the push architecture is extended both directions the outbound-only
callDBus constraint allows:
- Enumeration inverts to push: the KWin script pushes the full window list
(UpdateWindowList) into the same cache the daemon serves back on
ListWindows(); the inherited GnomeShellFocusBackend.watch_windows gdbus-poll
is answered unchanged.
- Raise inverts to enqueue-and-poll: since the daemon can't call into KWin,
raise_window / raise_app resolve the id against the daemon's own cached
window list and enqueue it; the script drains the queue on a QTimer tick
(DrainPendingRaises) and sets workspace.activeWindow.
Wire shape stays byte-identical to GNOME: resourceClass -> wm_class,
desktopFileName -> sandboxed_app_id, focused-first ordering, 0-based
workspace index, retired-id -> RaiseWindowFailed. skipTaskbar is the sole
enumeration filter (KDE's native switcher signal), matching GNOME's
inclusiveness.
Tests: cache/service/backend covered test-first in tests/test_platform_kde.py;
the real main.js is driven headless via a node:vm sandbox in the new
scripts/test_kwin_focus_bridge.mjs (wired into `just test` / `test-focus-wire`).
docs/PLATFORM-PARITY.md flips the KDE column to ✓ (parity test #136 stays
green) and documents the inverted plumbing; docs/TESTING.md notes the rows are
code-and-CI truth, unverified on hardware.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…#133)
Konsole (org.kde.konsole) fell to the default layout because the layout
matcher compared identities exactly, and a bare `konsole` token never
matched KDE/GNOME's reverse-DNS wm_class. Running-windows labels took the
last dotted segment, so org.telegram.desktop labeled 'Desktop' instead of
'Telegram'.
- matches_identity also matches the identity's last dotted segment against
layout tokens (org.kde.konsole -> konsole); full tokens still match only
their own exact identity.
- _humanize_identity strips trailing .desktop/.app packaging segments
before the last-dotted-segment split.
- Tests for both; PLATFORM-PARITY.md KDE bullet updated.
@jonocodes
jonocodes merged commit b16a533 into mainAug 15, 2026
1 of 2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KDE backend advertises watch_windows/raise_window it can't fulfill

1 participant

@jonocodes
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

KDE: window enumeration + raise, and reverse-DNS layout identity fixes (#133) - #158

Merged
jonocodes merged 2 commits into
mainfrom
issue-133
Aug 15, 2026
Merged

KDE: window enumeration + raise, and reverse-DNS layout identity fixes (#133)#158
jonocodes merged 2 commits into
mainfrom
issue-133

Conversation

@jonocodes

Copy link
Copy Markdown
Owner

Closes#133 (field-verification follow-ups).

Changes

Two commits:

  1. feat(kde): window enumeration + raise, reaching GNOME parity (3a345a7)
    KWin script pushes the active window + full window list into a daemon-owned org.deckd.Focus; daemon serves GetActiveWindow / ListWindows, and raise is enqueued-and-polled by the KWin script. Verified live on Plasma 6.5.6 Wayland.

  2. fix(layouts): reverse-DNS identity matching + packaging-suffix labels (c2d5bd2)
    Two live field bugs found while verifying KDE backend advertises watch_windows/raise_window it can't fulfill #133:

    • Layout.matches_identity matched identities exactly, so a bare konsole token never matched KDE/GNOME's reverse-DNS wm_class (org.kde.konsole) — Konsole silently fell to the default layout. Now also matches the identity's last dotted segment; full tokens still match only their exact identity.
    • _humanize_identity took the last dotted segment, so org.telegram.desktop labeled "Desktop" instead of "Telegram". Now strips trailing .desktop/.app packaging segments first.

Verification

  • Full daemon suite: 713 passed (3 new tests for the fixes)
  • pyright clean on changed lines
  • Live on KDE Plasma 6.5.6 Wayland: konsole resolves to the Terminal layout, Telegram labels correctly, both layout buttons and raise work end-to-end

Environment note

On the verification machine, layout buttons (uinput injection) required granting /dev/uinput access (udev rule + input group) — fixed in the separate host config repo, not this PR.

jonocodesand others added 2 commits August 15, 2026 07:24
The #133 short-term fix dropped KdeFocusBackend to focus-only because the
KWin script could only push the active window in. This follow-up teaches the
KWin script + daemon cache to answer enumeration and raise, so KDE honestly
re-advertises watch_windows / raise_window / raise_app.
Grounded in a primary-source pass over KWin master (windowList(), writable
workspace.activeWindow, and — the crux — a QTimer global in the script
sandbox), the push architecture is extended both directions the outbound-only
callDBus constraint allows:
- Enumeration inverts to push: the KWin script pushes the full window list
(UpdateWindowList) into the same cache the daemon serves back on
ListWindows(); the inherited GnomeShellFocusBackend.watch_windows gdbus-poll
is answered unchanged.
- Raise inverts to enqueue-and-poll: since the daemon can't call into KWin,
raise_window / raise_app resolve the id against the daemon's own cached
window list and enqueue it; the script drains the queue on a QTimer tick
(DrainPendingRaises) and sets workspace.activeWindow.
Wire shape stays byte-identical to GNOME: resourceClass -> wm_class,
desktopFileName -> sandboxed_app_id, focused-first ordering, 0-based
workspace index, retired-id -> RaiseWindowFailed. skipTaskbar is the sole
enumeration filter (KDE's native switcher signal), matching GNOME's
inclusiveness.
Tests: cache/service/backend covered test-first in tests/test_platform_kde.py;
the real main.js is driven headless via a node:vm sandbox in the new
scripts/test_kwin_focus_bridge.mjs (wired into `just test` / `test-focus-wire`).
docs/PLATFORM-PARITY.md flips the KDE column to ✓ (parity test #136 stays
green) and documents the inverted plumbing; docs/TESTING.md notes the rows are
code-and-CI truth, unverified on hardware.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…#133)
Konsole (org.kde.konsole) fell to the default layout because the layout
matcher compared identities exactly, and a bare `konsole` token never
matched KDE/GNOME's reverse-DNS wm_class. Running-windows labels took the
last dotted segment, so org.telegram.desktop labeled 'Desktop' instead of
'Telegram'.
- matches_identity also matches the identity's last dotted segment against
layout tokens (org.kde.konsole -> konsole); full tokens still match only
their own exact identity.
- _humanize_identity strips trailing .desktop/.app packaging segments
before the last-dotted-segment split.
- Tests for both; PLATFORM-PARITY.md KDE bullet updated.
@jonocodes
jonocodes merged commit b16a533 into mainAug 15, 2026
1 of 2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KDE backend advertises watch_windows/raise_window it can't fulfill

1 participant

@jonocodes
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

KDE: window enumeration + raise, and reverse-DNS layout identity fixes (#133) - #158

Merged
jonocodes merged 2 commits into
mainfrom
issue-133
Aug 15, 2026
Merged

KDE: window enumeration + raise, and reverse-DNS layout identity fixes (#133)#158
jonocodes merged 2 commits into
mainfrom
issue-133

Conversation

@jonocodes

Copy link
Copy Markdown
Owner

Closes#133 (field-verification follow-ups).

Changes

Two commits:

  1. feat(kde): window enumeration + raise, reaching GNOME parity (3a345a7)
    KWin script pushes the active window + full window list into a daemon-owned org.deckd.Focus; daemon serves GetActiveWindow / ListWindows, and raise is enqueued-and-polled by the KWin script. Verified live on Plasma 6.5.6 Wayland.

  2. fix(layouts): reverse-DNS identity matching + packaging-suffix labels (c2d5bd2)
    Two live field bugs found while verifying KDE backend advertises watch_windows/raise_window it can't fulfill #133:

    • Layout.matches_identity matched identities exactly, so a bare konsole token never matched KDE/GNOME's reverse-DNS wm_class (org.kde.konsole) — Konsole silently fell to the default layout. Now also matches the identity's last dotted segment; full tokens still match only their exact identity.
    • _humanize_identity took the last dotted segment, so org.telegram.desktop labeled "Desktop" instead of "Telegram". Now strips trailing .desktop/.app packaging segments first.

Verification

  • Full daemon suite: 713 passed (3 new tests for the fixes)
  • pyright clean on changed lines
  • Live on KDE Plasma 6.5.6 Wayland: konsole resolves to the Terminal layout, Telegram labels correctly, both layout buttons and raise work end-to-end

Environment note

On the verification machine, layout buttons (uinput injection) required granting /dev/uinput access (udev rule + input group) — fixed in the separate host config repo, not this PR.

jonocodesand others added 2 commits August 15, 2026 07:24
The #133 short-term fix dropped KdeFocusBackend to focus-only because the
KWin script could only push the active window in. This follow-up teaches the
KWin script + daemon cache to answer enumeration and raise, so KDE honestly
re-advertises watch_windows / raise_window / raise_app.
Grounded in a primary-source pass over KWin master (windowList(), writable
workspace.activeWindow, and — the crux — a QTimer global in the script
sandbox), the push architecture is extended both directions the outbound-only
callDBus constraint allows:
- Enumeration inverts to push: the KWin script pushes the full window list
(UpdateWindowList) into the same cache the daemon serves back on
ListWindows(); the inherited GnomeShellFocusBackend.watch_windows gdbus-poll
is answered unchanged.
- Raise inverts to enqueue-and-poll: since the daemon can't call into KWin,
raise_window / raise_app resolve the id against the daemon's own cached
window list and enqueue it; the script drains the queue on a QTimer tick
(DrainPendingRaises) and sets workspace.activeWindow.
Wire shape stays byte-identical to GNOME: resourceClass -> wm_class,
desktopFileName -> sandboxed_app_id, focused-first ordering, 0-based
workspace index, retired-id -> RaiseWindowFailed. skipTaskbar is the sole
enumeration filter (KDE's native switcher signal), matching GNOME's
inclusiveness.
Tests: cache/service/backend covered test-first in tests/test_platform_kde.py;
the real main.js is driven headless via a node:vm sandbox in the new
scripts/test_kwin_focus_bridge.mjs (wired into `just test` / `test-focus-wire`).
docs/PLATFORM-PARITY.md flips the KDE column to ✓ (parity test #136 stays
green) and documents the inverted plumbing; docs/TESTING.md notes the rows are
code-and-CI truth, unverified on hardware.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…#133)
Konsole (org.kde.konsole) fell to the default layout because the layout
matcher compared identities exactly, and a bare `konsole` token never
matched KDE/GNOME's reverse-DNS wm_class. Running-windows labels took the
last dotted segment, so org.telegram.desktop labeled 'Desktop' instead of
'Telegram'.
- matches_identity also matches the identity's last dotted segment against
layout tokens (org.kde.konsole -> konsole); full tokens still match only
their own exact identity.
- _humanize_identity strips trailing .desktop/.app packaging segments
before the last-dotted-segment split.
- Tests for both; PLATFORM-PARITY.md KDE bullet updated.
@jonocodes
jonocodes merged commit b16a533 into mainAug 15, 2026
1 of 2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KDE backend advertises watch_windows/raise_window it can't fulfill

1 participant

@jonocodes