feat(linux): hybrid titlebar mode for clickable in-app topbar#538
feat(linux): hybrid titlebar mode for clickable in-app topbar#538
Conversation
Default `CLAUDE_TITLEBAR_STYLE` is now `hybrid`: native OS frame plus a BrowserView preload shim that convinces claude.ai's bundle to render its in-app topbar (hamburger / sidebar / search / nav / Cowork ghost). Stacked layout instead of Windows's combined bar, but every button is clickable. Why not the upstream `frame:false` + WCO config: investigation (see docs/learnings/linux-topbar-shim.md) ruled out `titleBarOverlay`, `titleBarStyle:'hidden'`, and the `.draggable` CSS class as the source of the topbar click-eating drag region. The remaining cause is a Chromium-level implicit drag region for `frame:false` windows that exists on both X11 and Wayland and has no Electron-API knob. With `frame:true` the OS handles dragging and Chromium pushes no drag-region map, so the buttons receive mouse events normally. Modes: - `hybrid` (default) — system frame + shim, topbar visible and clickable - `native` — system frame, no shim, no in-app topbar - `hidden` — frameless + WCO config, matches Windows/macOS upstream; topbar visible but not clickable on Linux. Kept for Wayland comparison and future investigation Tests: tests/launcher-common.bats grew 16 cases covering `_resolve_titlebar_style`, `build_electron_args` flag selection per mode, and `setup_electron_env` env-var wiring per mode. `claude-desktop --doctor` now reports the resolved mode and warns when `hidden` is set. Co-Authored-By: Claude <claude@anthropic.com>
Visual reference of the stacked layout: DE-drawn titlebar on top with native window controls, claude.ai's in-app topbar (hamburger / search / back-forward) immediately below it. Co-Authored-By: Claude <claude@anthropic.com>
|
@typedrat - I don't think this will impact Nix at all, but do you mind verifying? |
Not at all, and I can also knock out testing on Hyprland while I'm at it. |
|
Working great on NixOS with Hyprland. |
2026-04-29.18-18-31.mp4Looks like its not working on OmarchyOS Hyprland |
|
@lukedev45 thanks for the video, that's really helpful. I reproduced what you're seeing locally by building an AppImage with the WCO shim patch disabled ( I went looking at Omarchy to see what might be different. DHH's I tested four scenarios on my KDE Plasma + Wayland box: the three most relevant Omarchy vars inline, all six together, Could you grab three things from a broken run? Written by Claude Opus 4.7 via Claude Code |
|
No problem, will take a look tomorrow |
|
Issue and PR open with electron to fix the underlying issue that makes the shim a requirement. |

Summary
Adds a new
hybridtitlebar mode (now the default) that makes claude.ai's in-app topbar work on Linux. Before this branch, the hamburger / sidebar / search / nav / Cowork ghost simply didn't render — the bundle gates the topbar behind anisWindows()UA check, and PR #127 forcedframe: trueto fix missing window controls, which definitively hid the topbar.The
hybridmode keeps the native OS frame (close/min/max draw correctly) and uses a BrowserView preload shim to satisfy the bundle's UA gate. Shim spoofsnavigator.userAgent, plus belt-and-suspendersmatchMediaandwindowControlsOverlayoverrides, plus aclassNameintercept that strips'draggable'from any DOM assignment so.draggable { -webkit-app-region: drag }can't carve drag regions out of the framed content area.Why hybrid instead of the upstream
frame:false+ WCO configThe upstream config matches Windows/macOS but has been broken on Linux: topbar renders, but mouse clicks don't fire. I tested four hypotheses (drag class on parent,
titleBarOverlayheight,titleBarStyle: 'hidden', X11-vs-Wayland) and disproved all four. The remaining cause is a Chromium-level implicit drag region forframe:falsewindows on both X11 and Wayland with no Electron-API override.Full investigation chain, gates, and the three outstanding upstream Electron bugs are in
docs/learnings/linux-topbar-shim.md. Thehiddenmode is kept on the branch as documented prior art and for future Wayland work;--doctorwarns when it's active.Modes
hybrid(default)nativehiddenKnown follow-up
The Cowork ghost icon (right side of the topbar on Windows) doesn't render in
hybridmode on Linux. The other four items (hamburger / sidebar / search / nav) all render and click correctly. Will track separately — the ghost likely has its own boot-features gate (coworkKappa/coworkArtifactsreturning"unavailable"on Linux) that the topbar's UA check doesn't cover.What changed
New:
scripts/wco-shim.js— BrowserView preload shim (UA + matchMedia + WCO + className intercept + diagnostic probes)scripts/patches/wco-shim.sh— inlines the shim at top ofmainView.jsat builddocs/learnings/linux-topbar-shim.md— investigation history, gates, upstream Electron bugs A/B/C, diagnostic recipesRemoved:
scripts/patches/titlebar.sh(replaced by wco-shim approach)Modified:
scripts/frame-fix-wrapper.js—hybridvalue, default switch, three-way BrowserWindow branch, WCO probe, shim console mirror to launcher.log, Ctrl+Q handler now attaches to all webContents (old per-main-window handler missed BrowserView keypresses)scripts/launcher-common.sh—_resolve_titlebar_style(), mode-aware feature flags, mode-awareELECTRON_USE_SYSTEM_TITLE_BARscripts/doctor.sh— reports resolved titlebar style: PASS forhybrid/native, WARN forhidden, WARN + valid-value hint for unrecognized valuestests/launcher-common.bats— 16 new cases for resolver + flag selection + env-var wiring per modescripts/patches/app-asar.sh,build.sh— wire upwco-shim.shin place oftitlebar.shdocs/CONFIGURATION.md— three-mode table,--doctormentionCLAUDE.md,.claude/agents/electron-linux-specialist.md,.claude/agents/issue-triage.md— reference updates@sabiut
Two files in your domain per CODEOWNERS — take a look when you get a chance:
tests/launcher-common.bats(+108, 16 new cases for_resolve_titlebar_style+build_electron_args+setup_electron_env)scripts/doctor.sh(+23, titlebar style validation block)Rest is mine.
Test plan
Automated:
bats tests/launcher-common.batspasses (64/64)claude-desktop --doctorreports PASS forhybrid(unset + explicit) andnative, WARN forhidden, WARN+fallback for unrecognized valuesBehavioral verification by DE+backend (distro doesn't matter for the click logic — Chromium is the same; package format covered separately by
test-artifact-*.sh):CLAUDE_USE_WAYLAND=1nix build, any DE) — needs verification (separate launcher path, not covered bytest-artifact-*.sh)Mode coverage (KDE Plasma X11):
hiddenon X11 — confirms upstream config is still broken;--doctorwarns correctlyhiddenon Wayland — confirms Bug C is not X11-specificCtrl+Qquits from any focused webContents (main window or BrowserView) — after the all-webContents handler fixGenerated with Claude Code
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com
75% AI / 25% Human
Claude: code, doc rewrites, test additions, investigation narrative
Human: empirical click testing across X11/Wayland/all-modes, strategic direction (hybrid pivot), build+install loop