cd /news/ai-tools/hiding-an-electron-window-from-scree… · home › topics › ai-tools › article
[ARTICLE · art-168670] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=· neutral

Hiding an Electron window from screen share: 4 things setContentProtection doesn't tell you

The team behind Natively, a desktop AI meeting assistant, documented four gaps in Electron's setContentProtection API that must be handled to keep an overlay window out of screen shares and recordings. Their approach applies protection across every live window, dedupes repeated calls to avoid Windows DWM churn, reasserts the macOS sharingType after app.dock.hide() resets it, and syncs skipTaskbar on Windows so the taskbar button doesn't leak into full-screen shares. The code ships in Natively's open-source repository.

by read3 min views3 publishedNov 12, 2026

Natively is a desktop AI assistant for meetings and interviews. It listens to the call, transcribes it, and shows answers and notes in a small overlay. People use it while they share their screen, so the overlay has an obvious requirement: when the user turns on Undetectable mode, the overlay must not appear in the share or the recording.

Electron gives you one line for this:

win.setContentProtection(true);

On macOS it sets the window's sharingType to NSWindowSharingNone. On Windows it calls SetWindowDisplayAffinity with WDA_EXCLUDEFROMCAPTURE (Windows 10 2004 and later). That line gets you most of the way. Here is what it took to get the rest, from code that ships in Natively today.

An "overlay" in a real app is several windows: the answer panel, a pill, a toggle, a popover catcher, sometimes a launcher. If one of them is missed, that one shows up in the share.

We keep a single method that walks every live window and pushes the same value:

private applyContentProtection(enable: boolean): void {
  const windows = [
    this.launcherWindow,
    this.overlayWindow,
    this.pillWindow,
    this.toggleWindow,
    this.popoverCatcher,
  ];
  windows.forEach((win) => {
    if (win && !win.isDestroyed()) win.setContentProtection(enable);
  });
}

Any window created later (settings, model picker, screenshot cropper) reads the current state when it is created, so a window opened mid-meeting doesn't slip through.

Our setter was called from several places: the settings toggle, every overlay show, and a workaround around Win+Tab. On Windows, setting the same display affinity again and again churns DWM, and the window can go black or blank for a few hundred milliseconds. So the public setter does nothing when the value hasn't changed:

public setContentProtection(enable: boolean): void {
  if (this.contentProtection === enable) return;
  this.contentProtection = enable;
  this.applyContentProtection(enable);
}

This one cost us the most time. Undetectable mode also hides the app from the Dock with app.dock.hide(). That flips the app's activation policy, and WindowServer re-evaluates every window. It can quietly reset sharingType to the default, which means the overlay is visible in the share again. Your in-memory flag still says true, so the deduped setter above would skip the call.

The fix is a second method that skips the dedupe and pushes the current value to the OS again:

public reassertContentProtection(): void {
  this.applyContentProtection(this.contentProtection);
}

Call it after every app.dock.hide() / app.dock.show().

On macOS, hiding from the Dock removes the app's visible presence. On Windows, a window that is excluded from capture can still have a taskbar button with the app's name on it, and the taskbar is part of a full-screen share. Most of our windows are created with skipTaskbar: true. The launcher is the exception because in normal mode it is the main window, so we sync it with the mode:

public syncLauncherTaskbarForStealth(): void {
  if (process.platform !== 'win32') return;
  this.launcherWindow?.setSkipTaskbar(!!this.appState.getUndetectable());
}

The macOS path doesn't use this at all. Forcing skipTaskbar there would change behaviour on a platform that handles it through the Dock.

Content protection is a request to the OS compositor, and different capture APIs respect it differently. Modern APIs (ScreenCaptureKit on macOS, Windows Graphics Capture) honour it. Some older capture paths may not. So we test with the apps people really share from (Zoom, Google Meet in Chrome, Teams, OBS, the system screenshot tool) on both platforms, with the mode on and off, and we treat "it was hidden on my machine" as one data point, not proof.

All of this is in Natively's source on GitHub: Natively-AI-assistant/natively-cluely-ai-assistant, in electron/WindowHelper.ts. Natively runs on macOS and Windows and is free for personal use. You can bring your own AI keys or run models locally with Ollama. If you're building something with an always-on-top overlay, I hope this saves you an afternoon.

Download and docs: heynatively.com

── more in #ai-tools 4 stories · sorted by recency
── more on @natively 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/hiding-an-electron-w…] indexed:0 read:3min 2026-11-12 · —