diff --git a/packages/web/src/content/docs/docs/ide.mdx b/packages/web/src/content/docs/docs/ide.mdx index fd8e0bfe..b2f7d1e1 100644 --- a/packages/web/src/content/docs/docs/ide.mdx +++ b/packages/web/src/content/docs/docs/ide.mdx @@ -9,7 +9,8 @@ opencode integrates with VS Code, Cursor, or any IDE that supports a terminal. J ## Usage -- **Quick Launch**: Open opencode with `Cmd+Esc` (Mac) or `Ctrl+Esc` (Windows/Linux), or click the opencode button in the UI. +- **Quick Launch**: Use `Cmd+Esc` (Mac) or `Ctrl+Esc` (Windows/Linux) to open opencode in a split terminal view, or focus an existing terminal session if one is already running. +- **New Session**: Use `Cmd+Shift+Esc` (Mac) or `Ctrl+Shift+Esc` (Windows/Linux) to start a new opencode terminal session, even if one is already open. You can also click the opencode button in the UI. - **Context Awareness**: Automatically share your current selection or tab with opencode. - **File Reference Shortcuts**: Use `Cmd+Option+K` (Mac) or `Alt+Ctrl+K` (Linux/Windows) to insert file references. For example, `@File#L37-42`. diff --git a/sdks/vscode/README.md b/sdks/vscode/README.md index 78685b2c..1ca5078c 100644 --- a/sdks/vscode/README.md +++ b/sdks/vscode/README.md @@ -1,16 +1,17 @@ # opencode VS Code Extension -A VS Code extension that integrates [opencode](https://opencode.ai) directly into your development environment. +A Visual Studio Code extension that integrates [opencode](https://opencode.ai) directly into your development workflow. ## Prerequisites -This extension requires [opencode](https://opencode.ai) to be installed on your system. Visit [opencode.ai](https://opencode.ai) for installation instructions. +This extension requires the [opencode CLI](https://opencode.ai) to be installed on your system. Visit [opencode.ai](https://opencode.ai) for installation instructions. ## Features -- **Cmd+Escape**: Launch opencode in a split terminal view -- **Alt+Cmd+K**: Send selected code to opencode's prompt -- **Tab awareness**: opencode automatically detects which files you have open +- **Quick Launch**: Use `Cmd+Esc` (Mac) or `Ctrl+Esc` (Windows/Linux) to open opencode in a split terminal view, or focus an existing terminal session if one is already running. +- **New Session**: Use `Cmd+Shift+Esc` (Mac) or `Ctrl+Shift+Esc` (Windows/Linux) to start a new opencode terminal session, even if one is already open. You can also click the opencode button in the UI. +- **Context Awareness**: Automatically share your current selection or tab with opencode. +- **File Reference Shortcuts**: Use `Cmd+Option+K` (Mac) or `Alt+Ctrl+K` (Linux/Windows) to insert file references. For example, `@File#L37-42`. ## Support diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 4f2aa1ed..b4272372 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -26,7 +26,15 @@ "commands": [ { "command": "opencode.openTerminal", - "title": "Open Terminal with Opencode", + "title": "Open opencode", + "icon": { + "light": "images/button-dark.svg", + "dark": "images/button-light.svg" + } + }, + { + "command": "opencode.openNewTerminal", + "title": "Open opencode in new tab", "icon": { "light": "images/button-dark.svg", "dark": "images/button-light.svg" @@ -40,7 +48,7 @@ "menus": { "editor/title": [ { - "command": "opencode.openTerminal", + "command": "opencode.openNewTerminal", "group": "navigation" } ] @@ -54,6 +62,14 @@ "win": "ctrl+escape", "linux": "ctrl+escape" }, + { + "command": "opencode.openNewTerminal", + "title": "Run opencode", + "key": "cmd+shift+escape", + "mac": "cmd+shift+escape", + "win": "ctrl+shift+escape", + "linux": "ctrl+shift+escape" + }, { "command": "opencode.addFilepathToTerminal", "title": "opencode: Insert At-Mentioned", diff --git a/sdks/vscode/src/extension.ts b/sdks/vscode/src/extension.ts index fa6e5c55..34ec8c64 100644 --- a/sdks/vscode/src/extension.ts +++ b/sdks/vscode/src/extension.ts @@ -3,10 +3,13 @@ export function deactivate() {} import * as vscode from "vscode" -export function activate(context: vscode.ExtensionContext) { - const TERMINAL_NAME = "opencode" +const TERMINAL_NAME = "opencode" + +export function activate(context: vscode.ExtensionContext) { + let openNewTerminalDisposable = vscode.commands.registerCommand("opencode.openNewTerminal", async () => { + await openTerminal() + }) - // Register command to open terminal in split screen and run opencode let openTerminalDisposable = vscode.commands.registerCommand("opencode.openTerminal", async () => { // An opencode terminal already exists => focus it const existingTerminal = vscode.window.terminals.find((t) => t.name === TERMINAL_NAME) @@ -15,6 +18,27 @@ export function activate(context: vscode.ExtensionContext) { return } + await openTerminal() + }) + + let addFilepathDisposable = vscode.commands.registerCommand("opencode.addFilepathToTerminal", async () => { + const fileRef = getActiveFile() + if (!fileRef) return + + const terminal = vscode.window.activeTerminal + if (!terminal) return + + if (terminal.name === TERMINAL_NAME) { + // @ts-ignore + const port = terminal.creationOptions.env?.["_EXTENSION_OPENCODE_PORT"] + port ? await appendPrompt(parseInt(port), fileRef) : terminal.sendText(fileRef) + terminal.show() + } + }) + + context.subscriptions.push(openTerminalDisposable, addFilepathDisposable) + + async function openTerminal() { // Create a new terminal in split screen const port = Math.floor(Math.random() * (65535 - 16384 + 1)) + 16384 const terminal = vscode.window.createTerminal({ @@ -57,64 +81,46 @@ export function activate(context: vscode.ExtensionContext) { await appendPrompt(port, `In ${fileRef}`) terminal.show() } - }) - - // Register command to add filepath to terminal - let addFilepathDisposable = vscode.commands.registerCommand("opencode.addFilepathToTerminal", async () => { - const fileRef = getActiveFile() - if (!fileRef) return - - const terminal = vscode.window.activeTerminal - if (!terminal) return - - if (terminal.name === TERMINAL_NAME) { - // @ts-ignore - const port = terminal.creationOptions.env?.["_EXTENSION_OPENCODE_PORT"] - port ? await appendPrompt(parseInt(port), fileRef) : terminal.sendText(fileRef) - terminal.show() - } - }) - - context.subscriptions.push(openTerminalDisposable, addFilepathDisposable) -} - -async function appendPrompt(port: number, text: string) { - await fetch(`http://localhost:${port}/tui/append-prompt`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ text }), - }) -} - -function getActiveFile() { - const activeEditor = vscode.window.activeTextEditor - if (!activeEditor) return - - const document = activeEditor.document - const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri) - if (!workspaceFolder) return - - // Get the relative path from workspace root - const relativePath = vscode.workspace.asRelativePath(document.uri) - let filepathWithAt = `@${relativePath}` - - // Check if there's a selection and add line numbers - const selection = activeEditor.selection - if (!selection.isEmpty) { - // Convert to 1-based line numbers - const startLine = selection.start.line + 1 - const endLine = selection.end.line + 1 - - if (startLine === endLine) { - // Single line selection - filepathWithAt += `#L${startLine}` - } else { - // Multi-line selection - filepathWithAt += `#L${startLine}-${endLine}` - } } - return filepathWithAt + async function appendPrompt(port: number, text: string) { + await fetch(`http://localhost:${port}/tui/append-prompt`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ text }), + }) + } + + function getActiveFile() { + const activeEditor = vscode.window.activeTextEditor + if (!activeEditor) return + + const document = activeEditor.document + const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri) + if (!workspaceFolder) return + + // Get the relative path from workspace root + const relativePath = vscode.workspace.asRelativePath(document.uri) + let filepathWithAt = `@${relativePath}` + + // Check if there's a selection and add line numbers + const selection = activeEditor.selection + if (!selection.isEmpty) { + // Convert to 1-based line numbers + const startLine = selection.start.line + 1 + const endLine = selection.end.line + 1 + + if (startLine === endLine) { + // Single line selection + filepathWithAt += `#L${startLine}` + } else { + // Multi-line selection + filepathWithAt += `#L${startLine}-${endLine}` + } + } + + return filepathWithAt + } }