Docs

App Icon & Dev App-Name — Session Notes

Session record. Shipped: the dev app icon is now the Tranquil node-graph mark. Not shipped: the dev menu-bar/dock name — it stays “Electron” and cannot be reliably changed in dev. Read the distributable checklist before packaging. See ADR-0020 for the decision.

Status

  • Icon — implemented (dev). scripts/dev.js patches the Electron bundle on every launch; confirmed the green Tranquil icon shows in the dock.
  • Name — not fixed (dev), by decision. Everything on disk says “Tranquil”; macOS still shows “Electron”. Deferred to packaged builds.
  • Nothing committed in tranquil-client yet at time of writing.

The icon (works)

Artwork. The Tranquil node-graph mark from www-tranquil/static/favicon.svg — a square glyph with a rounded dark backing plate, genuine Tranquil brand art (the leftover resources/app-icons/beta.* set is Pulsar’s purple logo, not ours). Its colors are authored in oklch(), which SVG rasterizers don’t reliably support, so they were converted to sRGB hex:

RoleoklchsRGB hex
accent glyphoklch(0.8 0.17 145)#6fda75
backing plateoklch(0.17 0.012 240)#0b1014

Assets built (in tranquil-client):

  • resources/app-icons/tranquil.svg — the favicon mark with hex colors.
  • resources/app-icons/tranquil.icns — built via rsvg-convert -w 1024 → a .iconset of the standard sizes downscaled with sipsiconutil -c icns.

Launcher patchscripts/dev.js patchMacAppIcon(electronBin), modeled on the existing patchMacAppName(): macOS-only, best-effort, idempotent. It copies tranquil.icns into the bundle’s Contents/Resources, sets CFBundleIconFile to tranquil via PlistBuddy (Set, falling back to Add), and bumps the bundle’s mtime so macOS’s icon services cache re-reads it. main() runs it after patchMacAppName(); a shared reregisterMacBundle() runs lsregister -f only when a patch changed the bundle. If the Dock still shows the old icon, killall Dock forces a refresh.

The name (does NOT work in dev)

Symptom. In yarn start, the bold application menu and the dock/⌘-Tab label read “Electron”, not “Tranquil”.

This corrects the earlier macOS App Name in Dev — Build Notes, which claimed the Info.plist CFBundleName patch fixes the visible name “on the very next yarn start.” It does not. That patch fixes Finder / LaunchServices metadata only; the menu-bar/dock name is unaffected.

What is provably correct on disk yet still displays “Electron”:

  • CFBundleName = CFBundleDisplayName = Tranquil
  • app.getName() = "Tranquil" (from package.json productName; logged from the main process)
  • LaunchServices record name / LSDisplayName = Tranquil (lsregister -dump, lsappinfo)

Everything tried that did not change the visible name:

  1. patchMacAppName (CFBundleName + CFBundleDisplayName → Tranquil).
  2. lsregister -f <bundle> re-registration.
  3. killall Dock.
  4. argv0: 'Tranquil' on the spawn.
  5. Renamed the launcher executable to Contents/MacOS/Tranquil and spawned that.
  6. Ad-hoc re-sign (codesign --force --deep --sign -). The bundle’s signature was in fact broken by our plist edits (“code has no resources but signature indicates they must be present”); re-signing made it valid again but the name still showed “Electron”.
  7. A unique CFBundleIdentifier (dev.tranquil.app) + re-sign + re-register.

Root cause (as far as it could be established). The machine had 6 registered Electron.app bundles (from other projects under Archive/, experiments/, apm-help/, plus ours). Five are named “Electron” and all share CFBundleIdentifier = com.github.Electron — every npm i electron ships the identical stock bundle id. macOS keys app identity (and its display name) by bundle id, so the colliding “Electron” registrations win. But giving our bundle a unique id still didn’t visibly fix it, so there is an additional OS-level name cache/fallback that piecemeal steps couldn’t clear remotely.

Trap — programmatic readouts lie here. lsappinfo info -only name and System Events name of process both reported “Tranquil” for the running instance while the visible menu bar still said “Electron”. Do not trust them as a proxy; only a human looking at the menu bar/dock/⌘-Tab is authoritative (osascript can’t read the menu bar without Accessibility grant).

Bundle left clean. The experimental unique id was reverted to stock com.github.Electron, the bundle re-signed valid, the stray renamed binary removed, and the temporary start.js diagnostic removed. Kept: CFBundleName/DisplayName = Tranquil and the tranquil.icns + CFBundleIconFile.

Distributable-binary checklist

Packaged builds get the correct name and icon because electron-builder does the full rebrand as one operation (rename main executable and helper apps, set the plist, assign a unique bundle id, embed the icon, re-sign). That’s why the packaged path is the source of truth and the dev hacks aren’t. Before shipping Tranquil binaries, in script/electron-builder.js (config is in that JS file, not a package.json build block):

  • Icon set — replace the Pulsar “beta” artwork with the Tranquil node-graph icon. Point the mac icon at a Tranquil .icns, the Windows icon at a .ico, and regenerate the Linux resources/icons/ PNG set (16→512). Reuse resources/app-icons/tranquil.svg as the master.
  • App id — change appId from dev.pulsar-edit.pulsar to a Tranquil identifier (e.g. dev.tranquil.app). A unique, stable id also avoids the com.github.Electron collision class seen in dev.
  • Product nameelectron-builder.js overrides extraMetadata.productName to “Pulsar”; set it to “Tranquil” so the packaged CFBundleName/menu name is correct.
  • Crash reportersrc/main-process/start.js starts crashReporter with productName: 'Pulsar'; rename to “Tranquil”.
  • Signing/notarization (macOS) — a real distributable needs a Developer ID signature + notarization, not the ad-hoc signing used while debugging.
  • Verify, don’t trust readouts: install the packaged .app and look at the dock/menu with your eyes.

Verification (dev)

yarn start → the dock and ⌘-Tab icon show the green Tranquil mark (name will read “Electron”, as documented). If the icon is stale after an Electron reinstall, it self-heals on the next launch (the mtime bump + lsregister -f); a stubborn Dock clears with killall Dock.