Skip to content

feat(macos): launch as an agent app so no dock tile is ever created - #3117

Merged
afonsojramos merged 3 commits into
mainfrom
pr-3100-comment-review
Jul 29, 2026
Merged

feat(macos): launch as an agent app so no dock tile is ever created#3117
afonsojramos merged 3 commits into
mainfrom
pr-3100-comment-review

Conversation

@afonsojramos

@afonsojramos afonsojramos commented Jul 29, 2026

Copy link
Copy Markdown
Member

Summary

Follow-up to #3100 after reports that the dock icon was still showing on v7.1.1, so the startup re-hide added in gitify-app/electron-menubar#123 treated a symptom rather than the cause. This declares LSUIElement in the macOS Info.plist so the process is born as an accessory app and no dock tile ever exists.

Root cause

Gitify shipped without LSUIElement, so macOS launched it as a regular dock app. Hiding the icon depended entirely on electron-menubar calling app.dock.hide() at app.ready, which runs TransformProcessType(kProcessTransformToUIElementApplication) after the tile is already on screen. That transform is asynchronous and can be silently dropped, and when it is, the icon stays for the whole session, which is why a restart was the only fix.

Measured on the shipped v7.1.1 build by sampling the activation policy from outside the process (lsappinfo info -only ApplicationType -app Gitify) from launch:

build activation policy over time
v7.1.1 (/Applications, as shipped) Foreground @ +128ms → UIElement @ +529ms
same bundle, ad-hoc re-signed (control) Foreground @ +137ms → UIElement @ +3150ms
control, repeat Foreground @ +119ms → UIElement @ +392ms
control, repeat Foreground @ +119ms → UIElement @ +272ms
+ LSUIElement: true UIElement @ +110ms, never Foreground
+ LSUIElement: true, repeat ×3 UIElement from first sample, never Foreground

Foreground is NSApplicationActivationPolicyRegular, i.e. the dock tile is real and visible. Every unpatched launch passes through it for 150ms-3s; that is the window in which the transform can be lost. With LSUIElement the window does not exist.

Fix

mac.extendInfo.LSUIElement: true in electron-builder.js. One key, macOS only; Windows and Linux packaging is untouched.

The electron-menubar startup re-hide stays as-is and is still worth having: pnpm dev runs against node_modules/electron/dist/Electron.app, whose Info.plist we don't control, and other consumers of showDockIcon: false may not set LSUIElement. It just stops being the only line of defence for packaged builds.

The dialog-close re-hides that #3100 added in updater.ts / first-run.ts are removed here: with LSUIElement they are dead code on packaged builds (both call sites are packaged-only, the updater skips unpacked apps and the first-run prompt skips dev mode), and the live test below confirms parentless dialogs no longer pull an agent app into the Dock. Their tests go with them.

Verification

Packaged this branch (electron-builder --mac dir --arm64), ad-hoc signed it, and ran it:

  • Contents/Info.plist contains LSUIElement = true; CFBundleIdentifier, LSApplicationCategoryType and the gitify / gitify-dev CFBundleURLTypes are intact.
  • Launch never reaches Foreground (UIElement @ +83ms, first sample).
  • Tray icon renders with a live notification count, so the renderer, stored auth and API path all work.
  • Protocol dispatch still reaches the running instance (main:second-instance → forward, main:gotTheLock → quit in main.log).
  • The parentless first-run "Move to Applications Folder?" prompt still appears, and the app stays UIElement for the whole time it is up (sampled every 500ms for 6s), as does an external activate.
  • pnpm check clean, tsc --noEmit clean, full suite green (1202 tests).

No test is added for the fix itself: it is a single build-config literal, and asserting config.mac.extendInfo.LSUIElement === true in a unit test would only restate it. The behaviour it fixes is only observable in a packaged macOS bundle, which is what the measurements above cover.

Closes #3069

Gitify shipped without LSUIElement, so the app started as a regular dock
app and relied on electron-menubar's runtime `app.dock.hide()` to transform
it into an accessory app. Measured on macOS 26, that leaves a 150-3000ms
window where the dock tile genuinely exists, and macOS can silently drop the
transform, stranding the icon until the next launch (#3069).

Declaring LSUIElement makes the process start as an accessory app, so there
is no tile and no transform to lose.
@afonsojramos
afonsojramos requested a review from setchy as a code owner July 29, 2026 20:51
@github-actions github-actions Bot added the bug Something isn't working label Jul 29, 2026
@sonarqubecloud

Copy link
Copy Markdown

@afonsojramos afonsojramos changed the title fix(macos): launch as an agent app so no dock tile is ever created feat(macos): launch as an agent app so no dock tile is ever created Jul 29, 2026
@github-actions github-actions Bot added enhancement New feature or enhancement to existing functionality and removed bug Something isn't working labels Jul 29, 2026
@afonsojramos
afonsojramos merged commit b60df93 into main Jul 29, 2026
20 checks passed
@afonsojramos
afonsojramos deleted the pr-3100-comment-review branch July 29, 2026 21:24
@github-actions github-actions Bot added this to the Release 7.x.x milestone Jul 29, 2026
This was referenced Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or enhancement to existing functionality

Development

Successfully merging this pull request may close these issues.

macOS: dock icon occasionally showing

1 participant