extension.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. // The module 'vscode' contains the VS Code extensibility API
  2. // Import the module and reference it with the alias vscode in your code below
  3. import delay from "delay"
  4. import * as vscode from "vscode"
  5. import { ClineProvider } from "./core/webview/ClineProvider"
  6. import { createClineAPI } from "./exports"
  7. import "./utils/path" // necessary to have access to String.prototype.toPosix
  8. import { DIFF_VIEW_URI_SCHEME } from "./integrations/editor/DiffViewProvider"
  9. /*
  10. Built using https://github.com/microsoft/vscode-webview-ui-toolkit
  11. Inspired by
  12. https://github.com/microsoft/vscode-webview-ui-toolkit-samples/tree/main/default/weather-webview
  13. https://github.com/microsoft/vscode-webview-ui-toolkit-samples/tree/main/frameworks/hello-world-react-cra
  14. */
  15. let outputChannel: vscode.OutputChannel
  16. // This method is called when your extension is activated
  17. // Your extension is activated the very first time the command is executed
  18. export function activate(context: vscode.ExtensionContext) {
  19. outputChannel = vscode.window.createOutputChannel("Cline")
  20. context.subscriptions.push(outputChannel)
  21. outputChannel.appendLine("Cline extension activated")
  22. // Get default commands from configuration
  23. const defaultCommands = vscode.workspace
  24. .getConfiguration('roo-cline')
  25. .get<string[]>('allowedCommands') || [];
  26. // Initialize global state if not already set
  27. if (!context.globalState.get('allowedCommands')) {
  28. context.globalState.update('allowedCommands', defaultCommands);
  29. }
  30. const sidebarProvider = new ClineProvider(context, outputChannel)
  31. context.subscriptions.push(
  32. vscode.window.registerWebviewViewProvider(ClineProvider.sideBarId, sidebarProvider, {
  33. webviewOptions: { retainContextWhenHidden: true },
  34. }),
  35. )
  36. context.subscriptions.push(
  37. vscode.commands.registerCommand("roo-cline.plusButtonClicked", async () => {
  38. outputChannel.appendLine("Plus button Clicked")
  39. await sidebarProvider.clearTask()
  40. await sidebarProvider.postStateToWebview()
  41. await sidebarProvider.postMessageToWebview({ type: "action", action: "chatButtonClicked" })
  42. }),
  43. )
  44. context.subscriptions.push(
  45. vscode.commands.registerCommand("roo-cline.mcpButtonClicked", () => {
  46. sidebarProvider.postMessageToWebview({ type: "action", action: "mcpButtonClicked" })
  47. }),
  48. )
  49. const openClineInNewTab = async () => {
  50. outputChannel.appendLine("Opening Cline in new tab")
  51. // (this example uses webviewProvider activation event which is necessary to deserialize cached webview, but since we use retainContextWhenHidden, we don't need to use that event)
  52. // https://github.com/microsoft/vscode-extension-samples/blob/main/webview-sample/src/extension.ts
  53. const tabProvider = new ClineProvider(context, outputChannel)
  54. //const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined
  55. const lastCol = Math.max(...vscode.window.visibleTextEditors.map((editor) => editor.viewColumn || 0))
  56. // Check if there are any visible text editors, otherwise open a new group to the right
  57. const hasVisibleEditors = vscode.window.visibleTextEditors.length > 0
  58. if (!hasVisibleEditors) {
  59. await vscode.commands.executeCommand("workbench.action.newGroupRight")
  60. }
  61. const targetCol = hasVisibleEditors ? Math.max(lastCol + 1, 1) : vscode.ViewColumn.Two
  62. const panel = vscode.window.createWebviewPanel(ClineProvider.tabPanelId, "Cline", targetCol, {
  63. enableScripts: true,
  64. retainContextWhenHidden: true,
  65. localResourceRoots: [context.extensionUri],
  66. })
  67. // TODO: use better svg icon with light and dark variants (see https://stackoverflow.com/questions/58365687/vscode-extension-iconpath)
  68. panel.iconPath = {
  69. light: vscode.Uri.joinPath(context.extensionUri, "assets", "icons", "rocket.png"),
  70. dark: vscode.Uri.joinPath(context.extensionUri, "assets", "icons", "rocket.png"),
  71. }
  72. tabProvider.resolveWebviewView(panel)
  73. // Lock the editor group so clicking on files doesn't open them over the panel
  74. await delay(100)
  75. await vscode.commands.executeCommand("workbench.action.lockEditorGroup")
  76. }
  77. context.subscriptions.push(vscode.commands.registerCommand("roo-cline.popoutButtonClicked", openClineInNewTab))
  78. context.subscriptions.push(vscode.commands.registerCommand("roo-cline.openInNewTab", openClineInNewTab))
  79. context.subscriptions.push(
  80. vscode.commands.registerCommand("roo-cline.settingsButtonClicked", () => {
  81. //vscode.window.showInformationMessage(message)
  82. sidebarProvider.postMessageToWebview({ type: "action", action: "settingsButtonClicked" })
  83. }),
  84. )
  85. context.subscriptions.push(
  86. vscode.commands.registerCommand("roo-cline.historyButtonClicked", () => {
  87. sidebarProvider.postMessageToWebview({ type: "action", action: "historyButtonClicked" })
  88. }),
  89. )
  90. /*
  91. We use the text document content provider API to show the left side for diff view by creating a virtual document for the original content. This makes it readonly so users know to edit the right side if they want to keep their changes.
  92. - This API allows you to create readonly documents in VSCode from arbitrary sources, and works by claiming an uri-scheme for which your provider then returns text contents. The scheme must be provided when registering a provider and cannot change afterwards.
  93. - Note how the provider doesn't create uris for virtual documents - its role is to provide contents given such an uri. In return, content providers are wired into the open document logic so that providers are always considered.
  94. https://code.visualstudio.com/api/extension-guides/virtual-documents
  95. */
  96. const diffContentProvider = new (class implements vscode.TextDocumentContentProvider {
  97. provideTextDocumentContent(uri: vscode.Uri): string {
  98. return Buffer.from(uri.query, "base64").toString("utf-8")
  99. }
  100. })()
  101. context.subscriptions.push(
  102. vscode.workspace.registerTextDocumentContentProvider(DIFF_VIEW_URI_SCHEME, diffContentProvider),
  103. )
  104. // URI Handler
  105. const handleUri = async (uri: vscode.Uri) => {
  106. const path = uri.path
  107. const query = new URLSearchParams(uri.query.replace(/\+/g, "%2B"))
  108. const visibleProvider = ClineProvider.getVisibleInstance()
  109. if (!visibleProvider) {
  110. return
  111. }
  112. switch (path) {
  113. case "/openrouter": {
  114. const code = query.get("code")
  115. if (code) {
  116. await visibleProvider.handleOpenRouterCallback(code)
  117. }
  118. break
  119. }
  120. default:
  121. break
  122. }
  123. }
  124. context.subscriptions.push(vscode.window.registerUriHandler({ handleUri }))
  125. return createClineAPI(outputChannel, sidebarProvider)
  126. }
  127. // This method is called when your extension is deactivated
  128. export function deactivate() {
  129. outputChannel.appendLine("Cline extension deactivated")
  130. }