Skip to content

fix(desktop): use bundle IDs for multi-edition JetBrains IDEs#1317

Merged
saddlepaddle merged 1 commit into
mainfrom
fix-user-issue
Feb 8, 2026
Merged

fix(desktop): use bundle IDs for multi-edition JetBrains IDEs#1317
saddlepaddle merged 1 commit into
mainfrom
fix-user-issue

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented Feb 8, 2026

Summary

  • Uses open -b <bundleId> instead of open -a <appName> for IntelliJ and PyCharm, which have multiple editions (Ultimate/Professional vs Community)
  • Bundle IDs are stable regardless of .app display name, so "IntelliJ IDEA Ultimate.app", "IntelliJ IDEA CE.app", etc. all resolve correctly
  • Tries candidates in order (e.g. Ultimate first, then Community), falling back on failure
  • Single-edition JetBrains IDEs (WebStorm, GoLand, etc.) are unchanged

Closes #1314

Test plan

  • All 1205 tests pass (bun test)
  • Typecheck passes (17/17 packages)
  • Lint passes (bun run lint:fix)
  • Sherif passes
  • Manual: verify open -b com.jetbrains.intellij opens IntelliJ on a machine with Ultimate installed
  • Manual: verify fallback to com.jetbrains.intellij.ce works when only Community is installed
  • Manual: verify PyCharm works similarly with Professional/Community editions

Summary by CodeRabbit

Release Notes

  • New Features

    • Added enhanced support for multiple editions of JetBrains IDEs (IntelliJ IDEA, PyCharm) with improved launch compatibility.
  • Improvements

    • Refined file opening logic with multi-candidate resolution, allowing graceful fallback attempts when primary methods fail, increasing success rates when opening files in external applications.

`open -a "IntelliJ IDEA"` fails when only an edition-specific variant
is installed (e.g. "IntelliJ IDEA Ultimate.app"). Use `open -b` with
bundle IDs instead, which resolves correctly regardless of app name.
Tries each candidate in order, falling back to the next on failure.

Closes #1314
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 8, 2026

📝 Walkthrough

Walkthrough

The getAppCommand() function now returns an array of command objects instead of a single command, enabling support for JetBrains IDEs with multiple editions (IntelliJ IDEA, PyCharm). A new BUNDLE_ID_CANDIDATES mapping provides alternative bundle IDs for these multi-edition apps. The external router was updated to iterate through multiple candidates, attempting each command sequentially with fallback error handling.

Changes

Cohort / File(s) Summary
JetBrains Multi-Edition Support
apps/desktop/src/lib/trpc/routers/external/helpers.ts, apps/desktop/src/lib/trpc/routers/external/helpers.test.ts
Added BUNDLE_ID_CANDIDATES mapping for IntelliJ and PyCharm to support multiple edition-specific bundle IDs (Ultimate, Community). Updated getAppCommand() to return an array of command objects with bundle ID-based -b flags for multi-edition apps, or single-element array with -a for others. Tests updated to assert arrays of commands.
Multi-Candidate Execution Logic
apps/desktop/src/lib/trpc/routers/external/index.ts
Updated command execution to iterate through multiple candidates returned by getAppCommand(), spawning each in sequence until one succeeds. Added warning logs when a candidate fails but alternatives remain. Falls back to shell.openPath() if no candidates are provided, and throws the last error if all candidates fail.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant getAppCommand as getAppCommand<br/>(helpers)
    participant OSExec as OS Execution<br/>(spawn)
    participant Fallback as Fallback<br/>(shell.openPath)

    Caller->>getAppCommand: getAppCommand(app, path)
    alt Multi-edition app (IntelliJ, PyCharm)
        getAppCommand-->>Caller: [cmd1(bundle1), cmd2(bundle2), ...]
    else Single-edition or standard app
        getAppCommand-->>Caller: [cmd(app-name)]
    else Unsupported app
        getAppCommand-->>Caller: null
    end

    Caller->>OSExec: Iterate candidates: spawn(cmd[0])
    alt Success
        OSExec-->>Caller: ✓ Process spawned
    else Failure with more candidates
        OSExec-->>Caller: ✗ Error, try next
        Caller->>OSExec: spawn(cmd[1])
        OSExec-->>Caller: ✓ Process spawned
    else All candidates failed
        OSExec-->>Caller: ✗ Last error
        Caller->>Fallback: shell.openPath(path)
        Fallback-->>Caller: ✓ Fallback opened
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 Multiple bundles, multiple editions bright,
IntelliJ and PyCharm now launch just right!
One app, many faces—we'll try them all,
Until the IDE answers the call,
No more "app not found," just pure delight! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: using bundle IDs for multi-edition JetBrains IDEs (IntelliJ and PyCharm).
Description check ✅ Passed The description includes a clear summary of changes, related issue reference (Closes #1314), test results, and remaining manual test items, though it lacks explicit Type of Change classification.
Linked Issues check ✅ Passed The PR fully addresses issue #1314 by implementing bundle ID-based opening for multi-edition IntelliJ and PyCharm, with fallback logic and unchanged single-edition IDE behavior [#1314].
Out of Scope Changes check ✅ Passed All changes are directly scoped to resolving the multi-edition JetBrains IDE issue; no out-of-scope modifications detected in the helpers logic or test coverage.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-user-issue

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
apps/desktop/src/lib/trpc/routers/external/helpers.ts (1)

48-62: Clean design for multi-edition fallback.

The approach of returning an array of candidates is well-structured. The caller iterates and falls back gracefully. One minor robustness note: if a BUNDLE_ID_CANDIDATES entry were ever an empty array [], it's truthy so the function would return [], and the caller would throw undefined (since the loop body never executes and lastError remains undefined). Not a real risk with the current hardcoded data, but a guard would be cheap:

Optional defensive check
 	const bundleIds = BUNDLE_ID_CANDIDATES[app];
-	if (bundleIds) {
+	if (bundleIds?.length) {
 		return bundleIds.map((id) => ({
apps/desktop/src/lib/trpc/routers/external/index.ts (1)

17-20: openPathInApp uses positional parameters.

The coding guidelines say to use object parameters for functions with 2+ parameters. This function predates the PR, but since lines within it are being modified, it's worth noting for a future cleanup.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@saddlepaddle saddlepaddle merged commit c7254cc into main Feb 8, 2026
5 of 6 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 8, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch
  • ⚠️ Electric Fly.io app

Thank you for your contribution! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] "Open in" fails for JetBrains IDEs with edition-specific names (IntelliJ, PyCharm)

1 participant