diff --git a/Resources/Localizable.xcstrings b/Resources/Localizable.xcstrings index 923b3d4a..67c72595 100644 --- a/Resources/Localizable.xcstrings +++ b/Resources/Localizable.xcstrings @@ -19547,6 +19547,40 @@ } } }, + "command.openFolderInVSCodeInline.subtitle": { + "extractionState": "manual", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "VS Code Inline" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "VS Code インライン" + } + } + } + }, + "command.openFolderInVSCodeInline.title": { + "extractionState": "manual", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Open Folder in VS Code (Inline)…" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "フォルダを VS Code で開く(インライン)…" + } + } + } + }, "command.openSettings.subtitle": { "extractionState": "manual", "localizations": { @@ -38571,6 +38605,57 @@ } } }, + "menu.file.openFolderInVSCodeInline": { + "extractionState": "manual", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Open Folder in VS Code (Inline)…" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "フォルダを VS Code で開く(インライン)…" + } + } + } + }, + "menu.file.openFolderInVSCodeInline.panelPrompt": { + "extractionState": "manual", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Open in VS Code" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "VS Code で開く" + } + } + } + }, + "menu.file.openFolderInVSCodeInline.panelTitle": { + "extractionState": "manual", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Open Folder in VS Code (Inline)" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "フォルダを VS Code で開く(インライン)" + } + } + } + }, "menu.file.reopenClosedBrowserPanel": { "extractionState": "manual", "localizations": { diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index 3a13393f..6dc11743 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -5661,6 +5661,86 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent } } + @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 + } + + func showOpenFolderInInlineVSCodePanel(tabManager preferredTabManager: TabManager? = nil) { + guard TerminalDirectoryOpenTarget.vscodeInline.isAvailable() else { + NSSound.beep() + return + } + + let targetTabManager = preferredTabManager + ?? preferredMainWindowContextForWorkspaceCreation(debugSource: "inlineVSCode.panel.target")?.tabManager + guard let targetTabManager else { + NSSound.beep() + return + } + + let panel = NSOpenPanel() + panel.canChooseFiles = false + panel.canChooseDirectories = true + panel.allowsMultipleSelection = false + panel.title = String( + localized: "menu.file.openFolderInVSCodeInline.panelTitle", + defaultValue: "Open Folder in VS Code (Inline)" + ) + panel.prompt = String( + localized: "menu.file.openFolderInVSCodeInline.panelPrompt", + defaultValue: "Open in VS Code" + ) + if let cwd = targetTabManager.selectedWorkspace?.currentDirectory, + !cwd.isEmpty { + panel.directoryURL = URL(fileURLWithPath: cwd) + } + + if panel.runModal() == .OK, + let url = panel.url, + !openDirectoryInInlineVSCode(url, tabManager: targetTabManager) { + NSSound.beep() + } + } + @objc func openWindow( _ pasteboard: NSPasteboard, userData: String?, diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index c88b83f1..97476ba2 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -5550,6 +5550,25 @@ struct ContentView: View { keywords: ["open", "folder", "repository", "project", "directory"] ) ) + contributions.append( + CommandPaletteCommandContribution( + commandId: "palette.openFolderInVSCodeInline", + title: constant( + String( + localized: "command.openFolderInVSCodeInline.title", + defaultValue: "Open Folder in VS Code (Inline)…" + ) + ), + subtitle: constant( + String( + localized: "command.openFolderInVSCodeInline.subtitle", + defaultValue: "VS Code Inline" + ) + ), + keywords: ["open", "folder", "directory", "project", "vs", "code", "inline", "editor", "browser"], + when: { _ in TerminalDirectoryOpenTarget.vscodeInline.isAvailable() } + ) + ) contributions.append( CommandPaletteCommandContribution( commandId: "palette.newTerminalTab", @@ -6244,6 +6263,11 @@ struct ContentView: View { } } } + registry.register(commandId: "palette.openFolderInVSCodeInline") { + DispatchQueue.main.async { + AppDelegate.shared?.showOpenFolderInInlineVSCodePanel(tabManager: tabManager) + } + } registry.register(commandId: "palette.newWindow") { AppDelegate.shared?.openNewMainWindow(nil) } @@ -7642,35 +7666,7 @@ struct ContentView: View { } private func openFocusedDirectoryInInlineVSCode(_ directoryURL: URL) -> Bool { - guard let vscodeApplicationURL = TerminalDirectoryOpenTarget.vscodeInline.applicationURL(), - let workspace = tabManager.selectedWorkspace, - let sourcePanelId = workspace.focusedPanelId else { - return false - } - let sourceTabId = workspace.id - let tabManager = tabManager - VSCodeServeWebController.shared.ensureServeWebURL(vscodeApplicationURL: vscodeApplicationURL) { serveWebURL in - guard let serveWebURL, - let openFolderURL = VSCodeServeWebURLBuilder.openFolderURL( - baseWebUIURL: serveWebURL, - directoryPath: directoryURL.path - ) else { - NSSound.beep() - return - } - guard tabManager.newBrowserSplit( - tabId: sourceTabId, - fromPanelId: sourcePanelId, - orientation: SplitDirection.right.orientation, - insertFirst: SplitDirection.right.insertFirst, - url: openFolderURL, - focus: true - ) != nil else { - NSSound.beep() - return - } - } - return true + AppDelegate.shared?.openDirectoryInInlineVSCode(directoryURL, tabManager: tabManager) ?? false } private func stopInlineVSCodeServeWeb() { diff --git a/Sources/cmuxApp.swift b/Sources/cmuxApp.swift index 701f8970..d7473ba6 100644 --- a/Sources/cmuxApp.swift +++ b/Sources/cmuxApp.swift @@ -588,6 +588,16 @@ struct cmuxApp: App { splitCommandButton(title: String(localized: "menu.file.openFolder", defaultValue: "Open Folder…"), shortcut: openFolderMenuShortcut) { AppDelegate.shared?.showOpenFolderPanel() } + + Button( + String( + localized: "menu.file.openFolderInVSCodeInline", + defaultValue: "Open Folder in VS Code (Inline)…" + ) + ) { + AppDelegate.shared?.showOpenFolderInInlineVSCodePanel() + } + .disabled(!TerminalDirectoryOpenTarget.vscodeInline.isAvailable()) } // Close tab/workspace