registerCommands.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import * as vscode from "vscode"
  2. import delay from "delay"
  3. import { ClineProvider } from "../core/webview/ClineProvider"
  4. import { ContextProxy } from "../core/config/ContextProxy"
  5. import { registerHumanRelayCallback, unregisterHumanRelayCallback, handleHumanRelayResponse } from "./humanRelay"
  6. import { handleNewTask } from "./handleTask"
  7. /**
  8. * Helper to get the visible ClineProvider instance or log if not found.
  9. */
  10. export function getVisibleProviderOrLog(outputChannel: vscode.OutputChannel): ClineProvider | undefined {
  11. const visibleProvider = ClineProvider.getVisibleInstance()
  12. if (!visibleProvider) {
  13. outputChannel.appendLine("Cannot find any visible Roo Code instances.")
  14. return undefined
  15. }
  16. return visibleProvider
  17. }
  18. // Store panel references in both modes
  19. let sidebarPanel: vscode.WebviewView | undefined = undefined
  20. let tabPanel: vscode.WebviewPanel | undefined = undefined
  21. /**
  22. * Get the currently active panel
  23. * @returns WebviewPanel或WebviewView
  24. */
  25. export function getPanel(): vscode.WebviewPanel | vscode.WebviewView | undefined {
  26. return tabPanel || sidebarPanel
  27. }
  28. /**
  29. * Set panel references
  30. */
  31. export function setPanel(
  32. newPanel: vscode.WebviewPanel | vscode.WebviewView | undefined,
  33. type: "sidebar" | "tab",
  34. ): void {
  35. if (type === "sidebar") {
  36. sidebarPanel = newPanel as vscode.WebviewView
  37. tabPanel = undefined
  38. } else {
  39. tabPanel = newPanel as vscode.WebviewPanel
  40. sidebarPanel = undefined
  41. }
  42. }
  43. export type RegisterCommandOptions = {
  44. context: vscode.ExtensionContext
  45. outputChannel: vscode.OutputChannel
  46. provider: ClineProvider
  47. }
  48. export const registerCommands = (options: RegisterCommandOptions) => {
  49. const { context } = options
  50. for (const [command, callback] of Object.entries(getCommandsMap(options))) {
  51. context.subscriptions.push(vscode.commands.registerCommand(command, callback))
  52. }
  53. }
  54. const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOptions) => {
  55. return {
  56. "roo-cline.activationCompleted": () => {},
  57. "roo-cline.plusButtonClicked": async () => {
  58. const visibleProvider = getVisibleProviderOrLog(outputChannel)
  59. if (!visibleProvider) {
  60. return
  61. }
  62. await visibleProvider.removeClineFromStack()
  63. await visibleProvider.postStateToWebview()
  64. await visibleProvider.postMessageToWebview({ type: "action", action: "chatButtonClicked" })
  65. },
  66. "roo-cline.mcpButtonClicked": () => {
  67. const visibleProvider = getVisibleProviderOrLog(outputChannel)
  68. if (!visibleProvider) {
  69. return
  70. }
  71. visibleProvider.postMessageToWebview({ type: "action", action: "mcpButtonClicked" })
  72. },
  73. "roo-cline.promptsButtonClicked": () => {
  74. const visibleProvider = getVisibleProviderOrLog(outputChannel)
  75. if (!visibleProvider) {
  76. return
  77. }
  78. visibleProvider.postMessageToWebview({ type: "action", action: "promptsButtonClicked" })
  79. },
  80. "roo-cline.popoutButtonClicked": () => openClineInNewTab({ context, outputChannel }),
  81. "roo-cline.openInNewTab": () => openClineInNewTab({ context, outputChannel }),
  82. "roo-cline.settingsButtonClicked": () => {
  83. const visibleProvider = getVisibleProviderOrLog(outputChannel)
  84. if (!visibleProvider) {
  85. return
  86. }
  87. visibleProvider.postMessageToWebview({ type: "action", action: "settingsButtonClicked" })
  88. },
  89. "roo-cline.historyButtonClicked": () => {
  90. const visibleProvider = getVisibleProviderOrLog(outputChannel)
  91. if (!visibleProvider) {
  92. return
  93. }
  94. visibleProvider.postMessageToWebview({ type: "action", action: "historyButtonClicked" })
  95. },
  96. "roo-cline.helpButtonClicked": () => {
  97. vscode.env.openExternal(vscode.Uri.parse("https://docs.roocode.com"))
  98. },
  99. "roo-cline.showHumanRelayDialog": (params: { requestId: string; promptText: string }) => {
  100. const panel = getPanel()
  101. if (panel) {
  102. panel?.webview.postMessage({
  103. type: "showHumanRelayDialog",
  104. requestId: params.requestId,
  105. promptText: params.promptText,
  106. })
  107. }
  108. },
  109. "roo-cline.registerHumanRelayCallback": registerHumanRelayCallback,
  110. "roo-cline.unregisterHumanRelayCallback": unregisterHumanRelayCallback,
  111. "roo-cline.handleHumanRelayResponse": handleHumanRelayResponse,
  112. "roo-cline.newTask": handleNewTask,
  113. "roo-cline.setCustomStoragePath": async () => {
  114. const { promptForCustomStoragePath } = await import("../shared/storagePathManager")
  115. await promptForCustomStoragePath()
  116. },
  117. "roo-cline.focusInput": async () => {
  118. try {
  119. const panel = getPanel()
  120. if (!panel) {
  121. await vscode.commands.executeCommand("workbench.view.extension.roo-cline-ActivityBar")
  122. } else if (panel === tabPanel) {
  123. panel.reveal(vscode.ViewColumn.Active, false)
  124. } else if (panel === sidebarPanel) {
  125. await vscode.commands.executeCommand(`${ClineProvider.sideBarId}.focus`)
  126. provider.postMessageToWebview({ type: "action", action: "focusInput" })
  127. }
  128. } catch (error) {
  129. outputChannel.appendLine(`Error focusing input: ${error}`)
  130. }
  131. },
  132. "roo.acceptInput": () => {
  133. const visibleProvider = getVisibleProviderOrLog(outputChannel)
  134. if (!visibleProvider) {
  135. return
  136. }
  137. visibleProvider.postMessageToWebview({ type: "acceptInput" })
  138. },
  139. }
  140. }
  141. export const openClineInNewTab = async ({ context, outputChannel }: Omit<RegisterCommandOptions, "provider">) => {
  142. // (This example uses webviewProvider activation event which is necessary to
  143. // deserialize cached webview, but since we use retainContextWhenHidden, we
  144. // don't need to use that event).
  145. // https://github.com/microsoft/vscode-extension-samples/blob/main/webview-sample/src/extension.ts
  146. const contextProxy = await ContextProxy.getInstance(context)
  147. const tabProvider = new ClineProvider(context, outputChannel, "editor", contextProxy)
  148. const lastCol = Math.max(...vscode.window.visibleTextEditors.map((editor) => editor.viewColumn || 0))
  149. // Check if there are any visible text editors, otherwise open a new group
  150. // to the right.
  151. const hasVisibleEditors = vscode.window.visibleTextEditors.length > 0
  152. if (!hasVisibleEditors) {
  153. await vscode.commands.executeCommand("workbench.action.newGroupRight")
  154. }
  155. const targetCol = hasVisibleEditors ? Math.max(lastCol + 1, 1) : vscode.ViewColumn.Two
  156. const newPanel = vscode.window.createWebviewPanel(ClineProvider.tabPanelId, "Roo Code", targetCol, {
  157. enableScripts: true,
  158. retainContextWhenHidden: true,
  159. localResourceRoots: [context.extensionUri],
  160. })
  161. // Save as tab type panel.
  162. setPanel(newPanel, "tab")
  163. // TODO: Use better svg icon with light and dark variants (see
  164. // https://stackoverflow.com/questions/58365687/vscode-extension-iconpath).
  165. newPanel.iconPath = {
  166. light: vscode.Uri.joinPath(context.extensionUri, "assets", "icons", "panel_light.png"),
  167. dark: vscode.Uri.joinPath(context.extensionUri, "assets", "icons", "panel_dark.png"),
  168. }
  169. await tabProvider.resolveWebviewView(newPanel)
  170. // Handle panel closing events.
  171. newPanel.onDidDispose(() => {
  172. setPanel(undefined, "tab")
  173. })
  174. // Lock the editor group so clicking on files doesn't open them over the panel.
  175. await delay(100)
  176. await vscode.commands.executeCommand("workbench.action.lockEditorGroup")
  177. return tabProvider
  178. }