* feat(linux): hybrid titlebar mode for clickable in-app topbar 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> * docs(learnings): add hybrid-mode screenshot 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> * docs(learnings): fix codespell hit (Pre-emptive → Preemptive) Codespell flags hyphenated "Pre-emptive" as a misspelling of "Preemptive". Drops the hyphen to clear the spellcheck CI gate on PR #538. Co-Authored-By: Claude <claude@anthropic.com> --------- Co-authored-by: Claude <claude@anthropic.com>
8.3 KiB
Configuration
MCP Configuration
Model Context Protocol settings are stored in:
~/.config/Claude/claude_desktop_config.json
Environment Variables
| Variable | Default | Description |
|---|---|---|
CLAUDE_USE_WAYLAND |
unset | Set to 1 to use native Wayland instead of XWayland. Note: Global hotkeys won't work in native Wayland mode. |
CLAUDE_MENU_BAR |
unset (auto) |
Controls menu bar behavior: auto (hidden, Alt toggles), visible / 1 (always shown), hidden / 0 (always hidden, Alt disabled). See Menu Bar below. |
CLAUDE_TITLEBAR_STYLE |
unset (hybrid) |
Controls window decoration style: hybrid (system frame + in-app topbar), native (system frame, no in-app topbar), hidden (frameless WCO — broken on X11, kept for diagnostics). See Titlebar Style below. |
COWORK_VM_BACKEND |
unset (auto-detect) | Force a specific Cowork isolation backend: kvm (full VM), bwrap (bubblewrap namespace sandbox), or host (no isolation). See Cowork Backend below. |
Wayland Support
By default, Claude Desktop uses X11 mode (via XWayland) on Wayland sessions to ensure global hotkeys work. If you prefer native Wayland and don't need global hotkeys:
# One-time launch
CLAUDE_USE_WAYLAND=1 claude-desktop
# Or add to your environment permanently
export CLAUDE_USE_WAYLAND=1
Important: Native Wayland mode doesn't support global hotkeys due to Electron/Chromium limitations with XDG GlobalShortcuts Portal. If global hotkeys (Ctrl+Alt+Space) are important to your workflow, keep the default X11 mode.
Menu Bar
By default, the menu bar is hidden but can be toggled with the Alt key (auto mode). On KDE Plasma and other DEs where Alt is heavily used, this can cause layout shifts. Use CLAUDE_MENU_BAR to control the behavior:
| Value | Menu visible | Alt toggles | Use case |
|---|---|---|---|
unset / auto |
No | Yes | Default — hidden, Alt toggles |
visible / 1 / true / yes / on |
Yes | No | Stable layout, no shift on Alt |
hidden / 0 / false / no / off |
No | No | Menu fully disabled, Alt free |
# Always show the menu bar (no layout shift on Alt)
CLAUDE_MENU_BAR=visible claude-desktop
# Or add to your environment permanently
export CLAUDE_MENU_BAR=visible
Titlebar Style
Claude Desktop's web UI includes a custom topbar (hamburger menu, sidebar toggle, search, back/forward, Cowork ghost). On Windows / macOS the bundle gates rendering on display-mode: window-controls-overlay; on Linux a shim convinces the bundle to render anyway. Use CLAUDE_TITLEBAR_STYLE to choose the layout:
| Value | Frame | In-app topbar | Window controls drawn by | Notes |
|---|---|---|---|---|
unset / hybrid |
system | Yes | Desktop environment | Default. Stacked layout — DE-drawn titlebar on top, in-app topbar below. Topbar buttons clickable. |
native |
system | No | Desktop environment | When the stacked layout looks wrong on your DE, or you don't need the in-app topbar. |
hidden |
frameless | Yes | Chromium (WCO region) | Matches Windows / macOS upstream config. Broken on Linux X11 — topbar buttons unresponsive due to a Chromium-level implicit drag region for frame:false windows. Kept for diagnostic / Wayland investigation; see docs/learnings/linux-topbar-shim.md. |
# Switch to the bare native experience (no in-app topbar)
CLAUDE_TITLEBAR_STYLE=native claude-desktop
# Or add to your environment permanently
export CLAUDE_TITLEBAR_STYLE=native
This setting applies to the main window only. The Quick Entry and About windows are always frameless.
Run claude-desktop --doctor to confirm the resolved titlebar style. The doctor output also flags hidden mode as broken on Linux and unrecognized values as fallbacks to hybrid.
Cowork Backend
Cowork mode auto-detects the best available isolation backend:
| Priority | Backend | Isolation | Detection |
|---|---|---|---|
| 1 | bubblewrap | Namespace sandbox | bwrap installed and functional |
| 2 | KVM | Full QEMU/KVM VM | /dev/kvm (r/w) + qemu-system-x86_64 + /dev/vhost-vsock |
| 3 | host | None (direct execution) | Always available |
To override auto-detection:
# Force bubblewrap (recommended if KVM times out)
COWORK_VM_BACKEND=bwrap claude-desktop
# Force host mode (no isolation)
COWORK_VM_BACKEND=host claude-desktop
# Make permanent via desktop entry override
mkdir -p ~/.local/share/applications/
cat > ~/.local/share/applications/claude-desktop.desktop << 'EOF'
[Desktop Entry]
Name=Claude
Exec=env COWORK_VM_BACKEND=bwrap /usr/bin/claude-desktop %u
Icon=claude-desktop
Type=Application
Terminal=false
Categories=Office;Utility;
MimeType=x-scheme-handler/claude;
StartupWMClass=Claude
EOF
Run claude-desktop --doctor to see which backend is selected and which dependencies are available.
Cowork Sandbox Mounts
When using Cowork mode with the BubbleWrap (bwrap) backend, you can customize
the sandbox mount points via ~/.config/Claude/claude_desktop_linux_config.json
(a dedicated config for the Linux port, separate from the official
claude_desktop_config.json):
{
"preferences": {
"coworkBwrapMounts": {
"additionalROBinds": ["/opt/my-tools", "/nix/store"],
"additionalBinds": ["/home/user/shared-data"],
"disabledDefaultBinds": ["/etc"]
}
}
}
| Key | Type | Description |
|---|---|---|
additionalROBinds |
(string | {src, dst})[] |
Extra paths mounted read-only inside the sandbox. Accepts any absolute path except /, /proc, /dev, /sys. |
additionalBinds |
(string | {src, dst})[] |
Extra paths mounted read-write inside the sandbox. src is restricted to paths under $HOME for security; dst is unconstrained. |
disabledDefaultBinds |
string[] |
Default mounts to skip. Cannot disable critical mounts (/, /dev, /proc). Use with caution: disabling /usr or /etc may break tools inside the sandbox. |
Distinct host/sandbox paths ({src, dst} form)
By default a string entry like "/opt/tools" mounts the host path at the
same path inside the sandbox. To map a host directory to a different path
inside the sandbox, use the object form { "src": "...", "dst": "..." }.
The most common use case is making /tmp persistent across Bash tool calls.
Each Bash invocation spawns a fresh bwrap with --tmpfs /tmp and
--die-with-parent, so the default /tmp is wiped between calls. Mapping a
host cache directory onto /tmp keeps state across calls without exposing the
host's real /tmp:
{
"preferences": {
"coworkBwrapMounts": {
"additionalBinds": [
{ "src": "/home/user/.cache/claude-tmp", "dst": "/tmp" }
],
"disabledDefaultBinds": ["/tmp"]
}
}
}
disabledDefaultBinds: ["/tmp"] is required to remove the default
--tmpfs /tmp so the bind takes effect.
The string and object forms can be mixed freely in the same array.
Caution: Mapping
dstonto a default RO mount (/usr,/etc,/bin,/sbin,/lib,/lib64) silently replaces it inside the sandbox; you almost never want this, and--doctorwill warn if you do.
Security notes
- Paths
/,/proc,/dev,/sys(and their subpaths) are always rejected for bothsrcanddst - For read-write mounts (
additionalBinds),srcmust be under your home directory.dsthas no$HOMEconstraint — that is the entire purpose of the object form (e.g. mapping onto/tmp) - The core sandbox structure (
--tmpfs /,--unshare-pid,--die-with-parent,--new-session) cannot be modified - Mount order is enforced: user mounts cannot override security-critical read-only mounts
Applying changes
The daemon reads the configuration at startup. After editing the config file, restart the daemon:
pkill -f cowork-vm-service
The daemon will be automatically relaunched on the next Cowork session.
Diagnostics
Run claude-desktop --doctor to see your custom mount configuration and any
warnings about potentially dangerous settings.
Application Logs
Runtime logs are available at:
~/.cache/claude-desktop-debian/launcher.log