[Feature]: Let users set a custom icon for "Open in" apps they add
- Dominant language
- TypeScript
- Stars
- 71.3k
- Forks
- 4.7k
- Avg merge
- 17h 8m
- Merged PRs (30d)
- 475
Description
### Problem or use case
When you add your own entry under **Settings → General → "Open in" menu**, it always renders with the generic `AppWindow` placeholder icon. Only three commands get a real icon.
The catalog in `src/renderer/src/lib/open-in-app-catalog.tsx` hardcodes three presets — VS Code (`code`), Cursor (`cursor`), Zed (`zed`) — each with a `faviconDomain`. `OpenInApplicationIcon` matches the entry's `command` string against that list; anything that doesn't match falls through to ``.
So a user who adds IntelliJ IDEA, Sublime Text, Xcode, Fleet, Neovim, Windsurf, a JetBrains IDE, or a shell wrapper script gets a row of identical grey window glyphs in the "Open in" menu (sidebar worktree card, source-control context menu, file explorer). With the 8-entry cap (`OPEN_IN_APPLICATIONS_MAX`), a full list is effectively unscannable — you have to read every label because the icons carry no information.
Matching on `command` also means a preset icon silently disappears the moment someone points the entry at an absolute path or a wrapper (`/usr/local/bin/code-insiders`, `my-code`), even though it *is* VS Code.
### Proposed solution
Let the user attach an icon when creating or editing an "Open in" entry.
1. Add an optional `icon` field to `OpenInApplication` (`src/shared/types.ts:2706`), currently `{ id, label, command }`.
2. Preserve it in `normalizeOpenInApplications` (`src/shared/open-in-applications.ts`), which today reconstructs each row as `{ id, label, command }` and would drop any extra field. Validate type/size and ignore anything malformed, consistent with how `label`/`command` are normalized.
3. Add an icon control to the row editor in `OpenInMenuSetting.tsx` — clicking the current icon opens a picker.
4. Resolution order in `OpenInApplicationIcon`: user-set icon → command-matched preset → `AppWindow` fallback. Existing entries keep today's behavior with no migration.
Open question on what the picker should accept — worth deciding before implementation:
- **Image file import** (PNG/SVG), copied into user data like the approach proposed in #13316 for the app icon. Most flexible, needs storage + size validation, and has to survive sync to remote/mobile clients.
- **A bundled icon set** (lucide names, or an expanded preset list keyed by name rather than command). No storage concerns, no arbitrary user assets on the wire, but limited choice.
- **A favicon domain**, reusing the existing preset mechanism (user types `jetbrains.com`). Cheapest to build since it's exactly what presets already do, but requires network and leaks the domain to Google's favicon service.
Two smaller fixes that would help even without a picker:
- Decouple preset matching from the exact command string, so an absolute path or a wrapper still resolves to the right preset (basename match, or an explicit preset reference stored on the entry).
- Grow the built-in preset list beyond three — JetBrains IDEs, Sublime Text, Windsurf, and Xcode would cover most of the common additions.
Note for whoever picks this up: the "Open in" list is global settings that reach paired clients, so per [`docs/reference/remote-wire-compatibility.md`](../blob/main/docs/reference/remote-wire-compatibility.md) a new optional field is safe, but an older client will just ignore the icon and show the placeholder. If the icon is an imported asset rather than a name/domain, how it reaches a remote client needs a deliberate answer.
### Alternatives or additional context
Current workaround: none — icons aren't configurable, so the only lever is naming entries so labels disambiguate them.
Related but distinct:
- #13316 — importing a custom **Orca application** icon (Dock/taskbar). Same "let users bring their own icon" shape, different surface; if it lands first, its user-data storage and validation path is directly reusable here.
- #11719 — custom icon **sets** (FontAwesome, SimpleIcons) for project icons, proposed as a plugin extension point. If that ships, "Open in" entries could draw from the same registry instead of getting a bespoke picker.
- #5983 (closed) — "Open in" with absolute paths containing spaces. Related in that absolute-path commands are a normal configuration, and they're exactly the ones that lose their preset icon today.
Contributor guide
Research direction
Start with src/shared/types.ts, src/shared/open-in-applications.ts, src/renderer/src/lib/open-in-app-catalog.tsx, and OpenInMenuSetting.tsx to trace the entry lifecycle and current icon resolution. Decide the supported icon representation and remote-client behavior before implementation, then verify malformed values are ignored, existing presets still work, and older clients retain the fallback behavior described in docs/reference/remote-wire-compatibility.md.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- desktop, developer-experience
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100