Repository navigation
Add Open Folder in VS Code (Inline) menu item and command palette entry - #2409
Conversation
Adds a File menu item and command palette entry to pick a local folder and open it in an inline VS Code browser panel. Extracts the inline VS Code open logic from ContentView into a reusable AppDelegate method so both the right-click context action and the new open-panel flow share the same code path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds an "Open Folder in VS Code (Inline)" flow: localization entries, AppDelegate methods to run the inline VS Code serve-web and open a folder, a directory picker panel, a command-palette command, and a File menu item wired to that flow. Changes
Sequence DiagramsequenceDiagram
actor User
participant UI as ContentView / cmuxApp
participant Panel as NSOpenPanel
participant AD as AppDelegate
participant Controller as VSCodeServeWebController
participant Browser as Browser
User->>UI: Select "Open Folder in VS Code (Inline)"
UI->>Panel: Show directory selection
Panel->>UI: Return directory URL (OK)
UI->>AD: openDirectoryInInlineVSCode(directoryURL)
AD->>Controller: ensureServeWebURL()
Controller-->>AD: serve-web URL
AD->>AD: build "open folder" URL
AD->>Browser: open URL (prefer split-right)
Browser->>Browser: display VS Code inline with folder
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~28 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Greptile SummaryThis PR adds a File > Open Folder in VS Code (Inline)… menu item and a matching command-palette entry, wiring them through a new Key changes:
Two points worth verifying:
Confidence Score: 5/5Safe to merge; all findings are P2 design/edge-case observations with no runtime-breaking defects. The implementation is consistent with existing patterns (NSOpenPanel modal deferral, Sources/AppDelegate.swift — the Important Files Changed
Sequence DiagramsequenceDiagram
actor User
participant Menu as File Menu / CmdPalette
participant AppDelegate
participant NSOpenPanel
participant VSCodeController as VSCodeServeWebController
participant TabManager
User->>Menu: Click "Open Folder in VS Code (Inline)…"
Menu->>AppDelegate: showOpenFolderInInlineVSCodePanel(tabManager?)
AppDelegate->>AppDelegate: guard vscodeInline.isAvailable()
AppDelegate->>AppDelegate: resolve targetTabManager
AppDelegate->>NSOpenPanel: runModal() — seeded with workspace cwd
NSOpenPanel-->>AppDelegate: .OK + url
AppDelegate->>AppDelegate: openDirectoryInInlineVSCode(url, tabManager)
AppDelegate->>AppDelegate: guard vscodeInline.applicationURL()
AppDelegate->>AppDelegate: resolve targetWorkspaceId
AppDelegate->>VSCodeController: ensureServeWebURL(vscodeApplicationURL)
VSCodeController-->>AppDelegate: serveWebURL (async callback)
AppDelegate->>AppDelegate: build openFolderURL
AppDelegate->>TabManager: openBrowser(inWorkspace: targetWorkspaceId, url: openFolderURL, preferSplitRight: true)
TabManager-->>AppDelegate: panelId (or nil → NSSound.beep)
Reviews (1): Last reviewed commit: "Add "Open Folder in VS Code (Inline)" vi..." | Re-trigger Greptile |
| let targetWorkspaceId = targetTabManager.selectedWorkspace?.id | ||
| ?? targetTabManager.tabs.first?.id | ||
| ?? targetTabManager.addWorkspace(select: true).id |
There was a problem hiding this comment.
Side-effectful fallback creates a workspace silently
The third branch of the ?? chain eagerly calls addWorkspace(select: true) as a side-effect of what appears to be a simple ID lookup. If selectedWorkspace is nil AND tabs.first is nil (e.g., the user has closed all tabs in the window between the panel's appear and the OK click), a brand-new, empty workspace is created just to serve as the target for the VS Code browser — which can be confusing and unexpected.
Consider guarding against this case instead:
guard let targetWorkspaceId = targetTabManager.selectedWorkspace?.id
?? targetTabManager.tabs.first?.id else {
return false
}If there are genuinely no workspaces, returning false (and letting showOpenFolderInInlineVSCodePanel emit a beep) is safer than silently creating one.
| @discardableResult | ||
| func openDirectoryInInlineVSCode( | ||
| _ directoryURL: URL, | ||
| tabManager preferredTabManager: TabManager? = nil | ||
| ) -> Bool { | ||
| guard let vscodeApplicationURL = TerminalDirectoryOpenTarget.vscodeInline.applicationURL() else { | ||
| return false | ||
| } | ||
|
|
||
| let targetTabManager = preferredTabManager | ||
| ?? preferredMainWindowContextForWorkspaceCreation(debugSource: "inlineVSCode.open.target")?.tabManager | ||
| guard let targetTabManager else { | ||
| return false | ||
| } | ||
|
|
||
| let targetWorkspaceId = targetTabManager.selectedWorkspace?.id | ||
| ?? targetTabManager.tabs.first?.id | ||
| ?? targetTabManager.addWorkspace(select: true).id | ||
| let normalizedDirectoryURL = directoryURL.standardizedFileURL | ||
|
|
||
| VSCodeServeWebController.shared.ensureServeWebURL(vscodeApplicationURL: vscodeApplicationURL) { serveWebURL in | ||
| guard let serveWebURL, | ||
| let openFolderURL = VSCodeServeWebURLBuilder.openFolderURL( | ||
| baseWebUIURL: serveWebURL, | ||
| directoryPath: normalizedDirectoryURL.path | ||
| ) else { | ||
| NSSound.beep() | ||
| return | ||
| } | ||
|
|
||
| guard targetTabManager.openBrowser( | ||
| inWorkspace: targetWorkspaceId, | ||
| url: openFolderURL, | ||
| preferSplitRight: true | ||
| ) != nil else { | ||
| NSSound.beep() | ||
| return | ||
| } | ||
| } | ||
|
|
||
| return true |
There was a problem hiding this comment.
Behavioral change for existing context-menu action
The refactored openDirectoryInInlineVSCode delegates to openBrowser(inWorkspace:url:preferSplitRight:true), which uses topRightBrowserReusePane() to reuse an existing right-pane when the workspace is already split. The previous implementation called newBrowserSplit(from: focusedPanelId, …) unconditionally, always creating a new split directly from the right-clicked terminal.
As a result, when a workspace already has multiple panes, the right-click context-menu "Open in VS Code (Inline)" action will now reuse the top-right pane (loading a new surface there) rather than always appending a new split adjacent to the focused terminal. The test plan checks whether the context action still works, but doesn't explicitly verify the split position. It's worth confirming the new reuse-pane behaviour is intentional for the context-menu case (it may well be — fewer unwanted splits is arguably better UX).
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
Sources/ContentView.swift (1)
5553-5571: Fold VS Code availability into the palette fingerprint.This
whenclause bypassesCommandPaletteContextSnapshot, so the cached command corpus will not rebuild if VS Code is installed or removed while the palette is already open. Consider threading this through the snapshot/fingerprint path, like the terminal open-target availability flags, so the visible command list stays in sync.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/ContentView.swift` around lines 5553 - 5571, The command's when closure calls TerminalDirectoryOpenTarget.vscodeInline.isAvailable() directly, bypassing CommandPaletteContextSnapshot and so changes in VS Code availability won't update the cached palette fingerprint; update the palette fingerprint pathway to include a boolean availability flag for VS Code (similar to the existing terminal open-target availability flags), add that flag to CommandPaletteContextSnapshot (or the fingerprint provider), and change the CommandPaletteCommandContribution's when closure to consult the snapshot/fingerprint flag instead of calling TerminalDirectoryOpenTarget.vscodeInline.isAvailable() directly so the corpus rebuilds when VS Code is installed or removed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Sources/AppDelegate.swift`:
- Around line 5679-5698: targetWorkspaceId is captured before the async
ensureServeWebURL completes, causing stale workspace selection; move the
workspace-id computation into the ensureServeWebURL completion closure
(re-evaluate targetTabManager.selectedWorkspace?.id ??
targetTabManager.tabs.first?.id ?? targetTabManager.addWorkspace(select:
true).id inside the closure) and then call
targetTabManager.openBrowser(inWorkspace:url:preferSplitRight:) with that
freshly computed id so tab/workspace changes during serve startup don't cause
unnecessary failures.
---
Nitpick comments:
In `@Sources/ContentView.swift`:
- Around line 5553-5571: The command's when closure calls
TerminalDirectoryOpenTarget.vscodeInline.isAvailable() directly, bypassing
CommandPaletteContextSnapshot and so changes in VS Code availability won't
update the cached palette fingerprint; update the palette fingerprint pathway to
include a boolean availability flag for VS Code (similar to the existing
terminal open-target availability flags), add that flag to
CommandPaletteContextSnapshot (or the fingerprint provider), and change the
CommandPaletteCommandContribution's when closure to consult the
snapshot/fingerprint flag instead of calling
TerminalDirectoryOpenTarget.vscodeInline.isAvailable() directly so the corpus
rebuilds when VS Code is installed or removed.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1b423c9c-976b-4803-b544-ab99cb1eda56
📒 Files selected for processing (4)
Resources/Localizable.xcstringsSources/AppDelegate.swiftSources/ContentView.swiftSources/cmuxApp.swift
| let targetWorkspaceId = targetTabManager.selectedWorkspace?.id | ||
| ?? targetTabManager.tabs.first?.id | ||
| ?? targetTabManager.addWorkspace(select: true).id | ||
| let normalizedDirectoryURL = directoryURL.standardizedFileURL | ||
|
|
||
| VSCodeServeWebController.shared.ensureServeWebURL(vscodeApplicationURL: vscodeApplicationURL) { serveWebURL in | ||
| guard let serveWebURL, | ||
| let openFolderURL = VSCodeServeWebURLBuilder.openFolderURL( | ||
| baseWebUIURL: serveWebURL, | ||
| directoryPath: normalizedDirectoryURL.path | ||
| ) else { | ||
| NSSound.beep() | ||
| return | ||
| } | ||
|
|
||
| guard targetTabManager.openBrowser( | ||
| inWorkspace: targetWorkspaceId, | ||
| url: openFolderURL, | ||
| preferSplitRight: true | ||
| ) != nil else { |
There was a problem hiding this comment.
Recompute workspace ID inside the async callback to avoid stale-target failures.
targetWorkspaceId is captured before ensureServeWebURL completes (Line 5679). If tabs/workspaces change while serve-web starts, openBrowser(inWorkspace:...) can fail unnecessarily.
Suggested fix
- let targetWorkspaceId = targetTabManager.selectedWorkspace?.id
- ?? targetTabManager.tabs.first?.id
- ?? targetTabManager.addWorkspace(select: true).id
let normalizedDirectoryURL = directoryURL.standardizedFileURL
VSCodeServeWebController.shared.ensureServeWebURL(vscodeApplicationURL: vscodeApplicationURL) { serveWebURL in
guard let serveWebURL,
let openFolderURL = VSCodeServeWebURLBuilder.openFolderURL(
baseWebUIURL: serveWebURL,
directoryPath: normalizedDirectoryURL.path
) else {
NSSound.beep()
return
}
+ let targetWorkspaceId = targetTabManager.selectedWorkspace?.id
+ ?? targetTabManager.tabs.first?.id
+ ?? targetTabManager.addWorkspace(select: true).id
guard targetTabManager.openBrowser(
inWorkspace: targetWorkspaceId,
url: openFolderURL,
preferSplitRight: true
) != nil else {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Sources/AppDelegate.swift` around lines 5679 - 5698, targetWorkspaceId is
captured before the async ensureServeWebURL completes, causing stale workspace
selection; move the workspace-id computation into the ensureServeWebURL
completion closure (re-evaluate targetTabManager.selectedWorkspace?.id ??
targetTabManager.tabs.first?.id ?? targetTabManager.addWorkspace(select:
true).id inside the closure) and then call
targetTabManager.openBrowser(inWorkspace:url:preferSplitRight:) with that
freshly computed id so tab/workspace changes during serve startup don't cause
unnecessary failures.
…-2401-vscode-web-local-folders
There was a problem hiding this comment.
🧹 Nitpick comments (1)
Sources/ContentView.swift (1)
5386-5404: Fingerprint inline VS Code availability instead of reading it ad hoc.This
whenclosure depends onTerminalDirectoryOpenTarget.vscodeInline.isAvailable(), but the command-palette corpus is invalidated fromCommandPaletteContextSnapshot. If VS Code availability changes while the palette is open, this entry can stay stale until a forced refresh. Moving that availability bit into the snapshot would keep visibility aligned with the existing cache key.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/ContentView.swift` around lines 5386 - 5404, The command palette entry currently calls TerminalDirectoryOpenTarget.vscodeInline.isAvailable() directly in the when closure which can become stale; instead add a boolean flag to CommandPaletteContextSnapshot (e.g. vscodeInlineAvailable or an accessor like isVscodeInlineAvailable()), populate that flag when building the snapshot where terminal/availability is resolved, and change the CommandPaletteCommandContribution's when closure to read from the snapshot (via the provided context) rather than calling TerminalDirectoryOpenTarget.vscodeInline.isAvailable() directly so visibility is driven by the cached snapshot key.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@Sources/ContentView.swift`:
- Around line 5386-5404: The command palette entry currently calls
TerminalDirectoryOpenTarget.vscodeInline.isAvailable() directly in the when
closure which can become stale; instead add a boolean flag to
CommandPaletteContextSnapshot (e.g. vscodeInlineAvailable or an accessor like
isVscodeInlineAvailable()), populate that flag when building the snapshot where
terminal/availability is resolved, and change the
CommandPaletteCommandContribution's when closure to read from the snapshot (via
the provided context) rather than calling
TerminalDirectoryOpenTarget.vscodeInline.isAvailable() directly so visibility is
driven by the cached snapshot key.
Summary
NSOpenPanelfolder picker that opens the selected directory in an inline VS Code browser panelOpen Folder in VS Code (Inline)…) so the action is discoverable via Cmd-PContentViewinto a reusableAppDelegate.openDirectoryInInlineVSCode(_:tabManager:)method shared by the context-menu action, new menu item, and command paletteCloses #2401
Test plan
🤖 Generated with Claude Code
Summary by cubic
Adds a File menu item and command palette command to open a folder in inline VS Code, making the action easier to find. Centralizes the open logic and disables the UI when VS Code isn’t installed.
New Features
Refactors
Written for commit a1352e0. Summary will update on new commits.
Summary by CodeRabbit