{"repo":"NousResearch/hermes-agent","public_id":"53514","display_label":"Linux/WSL desktop window controls missing or broken","summary":"Multiple bug reports and fix attempts addressing missing or broken native window controls (minimize, maximize, close) on Linux desktop environments including Wayland, WSLg, X11/XWayland, and with titleBarStyle hidden or overlay configurations. The work spans Electron 40+ compatibility, renderer-drawn fallbacks, and related titlebar layout concerns.","subsystem":"apps/desktop","counts":{"members":11,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":4,"merged_prs":2,"attempt_prs":5},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":53989},"resolution":{"state":"partially_resolved","by":"pr-113247"},"freshness":{"state":"verified","last_verified_at":"2026-09-17T18:51:20+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.17818181818181816},"fix_edges":[{"issue_number":53514,"pr_number":53989,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":57614,"pr_number":113247,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true}],"members":[{"kind":"issue","number":53514,"title":"[Bug]: Linux desktop: no close button when titleBarStyle is hidden","url":null,"state":"closed"},{"kind":"issue","number":53690,"title":"Linux (X11/XWayland): Electron 40+ window frame missing with titleBarStyle: \"hidden\"","url":null,"state":"closed"},{"kind":"issue","number":54294,"title":"[Bug]: Desktop (Linux/Wayland): Native window controls (minimize/maximize/close) are missing","url":null,"state":"closed"},{"kind":"issue","number":57614,"title":"[Bug] Desktop (WSL/WSLg): no minimize/maximize/close buttons (companion to #53514)","url":null,"state":"closed"},{"kind":"pr","number":53326,"title":"fix(desktop): enable titleBarOverlay on Linux for Electron 40+","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#53989","number":53989}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":53528,"title":"fix(desktop): add Linux close fallback when no titlebar overlay","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"97640fd9adf532d6ce4ebc63ec817137ba685157","number":0},{"kind":"pr","value":"#53514","number":53514},{"kind":"pr","value":"#53326","number":53326}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":53989,"title":"fix(desktop): restore window controls on plain Linux (min/max/close)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["apps/desktop/electron/main.cjs","apps/desktop/electron/titlebar-overlay-width.cjs","apps/desktop/electron/titlebar-overlay-width.test.cjs","scripts/release.py"],"fix_hunks":[{"file":"apps/desktop/electron/main.cjs","hunks":["@@ -534,9 +534,10 @@ function getTitleBarOverlayOptions() {\n     return { height: TITLEBAR_HEIGHT }\n   }\n \n-  // Windows + WSLg paint WCO natively; plain Linux disables it (frameless hidden\n-  // titlebar still applies).\n-  if (!IS_WINDOWS && !IS_WSL) {\n+  // WSLg paints WCO via the RDP host's own min/max/close, so requesting\n+  // an Electron overlay there just leaves a dead gap. Plain Linux (KDE,\n+  // GNOME) can use the native overlay — let it through.\n+  if (!IS_WINDOWS && IS_WSL) {\n     return false\n   }\n ","@@ -3788,7 +3789,7 @@ function getWindowButtonPosition() {\n }\n \n function getNativeOverlayWidth() {\n-  return computeNativeOverlayWidth({ isWindows: IS_WINDOWS, isWsl: IS_WSL })\n+  return computeNativeOverlayWidth({ isWindows: IS_WINDOWS, isWsl: IS_WSL, isMac: IS_MAC })\n }\n \n function getWindowState() {"]},{"file":"apps/desktop/electron/titlebar-overlay-width.cjs","hunks":["@@ -1,11 +1,24 @@\n-// Pre-layout fallback for WCO right-edge reservation (--titlebar-tools-right).\n-// Live width comes from navigator.windowControlsOverlay in the renderer.\n+'use strict'\n \n const OVERLAY_FALLBACK_WIDTH = 144\n \n-/** @param {{ isWindows?: boolean, isWsl?: boolean }} opts */\n-function nativeOverlayWidth({ isWindows = false, isWsl = false } = {}) {\n-  return isWindows || isWsl ? OVERLAY_FALLBACK_WIDTH : 0\n+/**\n+ * Static pre-layout reservation (px) for the right-side native window-controls\n+ * overlay (min/max/close). Only a FALLBACK — once laid out the renderer reads\n+ * the exact width from navigator.windowControlsOverlay\n+ * (use-window-controls-overlay-width.ts) and uses this value only when the WCO\n+ * API is unavailable.\n+ *\n+ * macOS uses traffic lights positioned via trafficLightPosition, not a WCO\n+ * overlay, so it reserves nothing here. Every other desktop platform now paints\n+ * the Electron overlay (Windows, WSLg, and plain Linux KDE/GNOME), so they all\n+ * reserve the fallback width.\n+ *\n+ * @param {{ isWindows?: boolean, isWsl?: boolean, isMac?: boolean }} opts\n+ */\n+function nativeOverlayWidth({ isWindows = false, isWsl = false, isMac = false } = {}) {\n+  if (isMac) return 0\n+  return OVERLAY_FALLBACK_WIDTH\n }\n \n module.exports = { OVERLAY_FALLBACK_WIDTH, nativeOverlayWidth }"]},{"file":"apps/desktop/electron/titlebar-overlay-width.test.cjs","hunks":["@@ -18,10 +18,17 @@ test('WSLg paints the same WCO, so it reserves the same fallback width', () => {\n   assert.equal(nativeOverlayWidth({ isWsl: true }), OVERLAY_FALLBACK_WIDTH)\n })\n \n-test('plain Linux and macOS reserve nothing', () => {\n-  assert.equal(nativeOverlayWidth({ isWindows: false, isWsl: false }), 0)\n-  assert.equal(nativeOverlayWidth(), 0)\n-  assert.equal(nativeOverlayWidth({}), 0)\n+test('plain Linux paints the WCO too, so it reserves the fallback width', () => {\n+  // Regression #53185: re-enabling the overlay on plain Linux (KDE/GNOME)\n+  // without reserving its width left the native min/max/close buttons painting\n+  // on top of the app's right-edge titlebar tools.\n+  assert.equal(nativeOverlayWidth({ isWindows: false, isWsl: false }), OVERLAY_FALLBACK_WIDTH)\n+  assert.equal(nativeOverlayWidth(), OVERLAY_FALLBACK_WIDTH)\n+  assert.equal(nativeOverlayWidth({}), OVERLAY_FALLBACK_WIDTH)\n+})\n+\n+test('macOS uses traffic lights, not a WCO overlay, so it reserves nothing', () => {\n+  assert.equal(nativeOverlayWidth({ isMac: true }), 0)\n })\n \n test('the fallback width is a sane positive pixel value', () => {"]},{"file":"scripts/release.py","hunks":["@@ -70,6 +70,7 @@\n     \"[redacted:email]\": \"uperLu\",  # PR #50958 salvage (rename plugins/cron → plugins/cron_providers; #50872)\n     \"[redacted:email]\": \"yusekiotacode\",  # PR #48706 salvage (anthropic OAuth login token endpoint → platform.claude.com; #45250/#49821)\n     \"[redacted:email]\": \"s010mn\",  # PR #29221 salvage (ollama-cloud reasoning_effort xhigh→max)\n+    \"[redacted:email]\": \"chriswesley4\",  # PR #53185 salvage (re-enable titleBarOverlay on plain Linux; missing min/max/close regression)\n     \"[redacted:email]\": \"jeeves-assistant\",  # PR #50771 (computer-use CuaDriver vision capture routing)\n     \"[redacted:email]\": \"ScotterMonk\",  # PR #50145 salvage (cron output truncation: adapter-aware chunking, #50126)\n     \"[redacted:email]\": \"TutkuEroglu\",  # PR #50481 salvage (AGENTS.md stale token-lock adapter path)\n"]}]},{"kind":"pr","number":64505,"title":"fix(desktop): renderer-drawn WSLg window controls, zoom-restore coordinator, work-area snap","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#70803","number":70803},{"kind":"pr","value":"#70803","number":70803}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":70803,"title":"fix(desktop): WSLg window controls + GPU crash-loop fallback","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#113247","number":113247}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":71754,"title":"fix(desktop): request window controls overlay on WSLg (#57614)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#113247","number":113247}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":113247,"title":"fix(desktop): working WSLg window controls and native Wayland launch","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["apps/desktop/electron/entry.ts","apps/desktop/electron/main.ts","apps/desktop/electron/preload.ts","apps/desktop/electron/titlebar-overlay-width.test.ts","apps/desktop/electron/titlebar-overlay-width.ts","apps/desktop/electron/window-chrome-events.test.ts","apps/desktop/electron/window-chrome-events.ts","apps/desktop/electron/window-controls.test.ts","apps/desktop/electron/window-controls.ts","apps/desktop/electron/wslg-launch-process.test.ts","apps/desktop/electron/wslg-launch-process.ts","apps/desktop/electron/wslg-launch.test.ts","apps/desktop/electron/wslg-launch.ts","apps/desktop/scripts/bundle-electron-main.mjs","apps/desktop/src/app/contrib/wiring.tsx","apps/desktop/src/app/shell/wslg-window-controls.test.tsx","apps/desktop/src/app/shell/wslg-window-controls.tsx","apps/desktop/src/global.d.ts","website/docs/user-guide/desktop.md"]}],"trust":{"default_tier":"verified","includes_suggested":false}}