registerCommands.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import * as vscode from "vscode"
  2. import delay from "delay"
  3. import { ClineProvider } from "../core/webview/ClineProvider"
  4. import { registerHumanRelayCallback, unregisterHumanRelayCallback, handleHumanRelayResponse } from "./humanRelay"
  5. import { handleNewTask } from "./handleTask"
  6. // Store panel references in both modes
  7. let sidebarPanel: vscode.WebviewView | undefined = undefined
  8. let tabPanel: vscode.WebviewPanel | undefined = undefined
  9. /**
  10. * Get the currently active panel
  11. * @returns WebviewPanel或WebviewView
  12. */
  13. export function getPanel(): vscode.WebviewPanel | vscode.WebviewView | undefined {
  14. return tabPanel || sidebarPanel
  15. }
  16. /**
  17. * Set panel references
  18. */
  19. export function setPanel(
  20. newPanel: vscode.WebviewPanel | vscode.WebviewView | undefined,
  21. type: "sidebar" | "tab",
  22. ): void {
  23. if (type === "sidebar") {
  24. sidebarPanel = newPanel as vscode.WebviewView
  25. tabPanel = undefined
  26. } else {
  27. tabPanel = newPanel as vscode.WebviewPanel
  28. sidebarPanel = undefined
  29. }
  30. }
  31. export type RegisterCommandOptions = {
  32. context: vscode.ExtensionContext
  33. outputChannel: vscode.OutputChannel
  34. provider: ClineProvider
  35. }
  36. export const registerCommands = (options: RegisterCommandOptions) => {
  37. const { context, outputChannel } = options
  38. for (const [command, callback] of Object.entries(getCommandsMap(options))) {
  39. context.subscriptions.push(vscode.commands.registerCommand(command, callback))
  40. }
  41. }
  42. const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOptions) => {
  43. return {
  44. "roo-cline.plusButtonClicked": async () => {
  45. await provider.removeClineFromStack()
  46. await provider.postStateToWebview()
  47. await provider.postMessageToWebview({ type: "action", action: "chatButtonClicked" })
  48. },
  49. "roo-cline.mcpButtonClicked": () => {
  50. provider.postMessageToWebview({ type: "action", action: "mcpButtonClicked" })
  51. },
  52. "roo-cline.promptsButtonClicked": () => {
  53. provider.postMessageToWebview({ type: "action", action: "promptsButtonClicked" })
  54. },
  55. "roo-cline.popoutButtonClicked": () => openClineInNewTab({ context, outputChannel }),
  56. "roo-cline.openInNewTab": () => openClineInNewTab({ context, outputChannel }),
  57. "roo-cline.settingsButtonClicked": () => {
  58. provider.postMessageToWebview({ type: "action", action: "settingsButtonClicked" })
  59. },
  60. "roo-cline.historyButtonClicked": () => {
  61. provider.postMessageToWebview({ type: "action", action: "historyButtonClicked" })
  62. },
  63. "roo-cline.helpButtonClicked": () => {
  64. vscode.env.openExternal(vscode.Uri.parse("https://docs.roocode.com"))
  65. },
  66. "roo-cline.showHumanRelayDialog": (params: { requestId: string; promptText: string }) => {
  67. const panel = getPanel()
  68. if (panel) {
  69. panel?.webview.postMessage({
  70. type: "showHumanRelayDialog",
  71. requestId: params.requestId,
  72. promptText: params.promptText,
  73. })
  74. }
  75. },
  76. "roo-cline.registerHumanRelayCallback": registerHumanRelayCallback,
  77. "roo-cline.unregisterHumanRelayCallback": unregisterHumanRelayCallback,
  78. "roo-cline.handleHumanRelayResponse": handleHumanRelayResponse,
  79. "roo-cline.newTask": handleNewTask,
  80. "roo-cline.setCustomStoragePath": async () => {
  81. const { promptForCustomStoragePath } = await import("../shared/storagePathManager")
  82. await promptForCustomStoragePath()
  83. },
  84. }
  85. }
  86. const openClineInNewTab = async ({ context, outputChannel }: Omit<RegisterCommandOptions, "provider">) => {
  87. // (This example uses webviewProvider activation event which is necessary to
  88. // deserialize cached webview, but since we use retainContextWhenHidden, we
  89. // don't need to use that event).
  90. // https://github.com/microsoft/vscode-extension-samples/blob/main/webview-sample/src/extension.ts
  91. const tabProvider = new ClineProvider(context, outputChannel, "editor")
  92. const lastCol = Math.max(...vscode.window.visibleTextEditors.map((editor) => editor.viewColumn || 0))
  93. // Check if there are any visible text editors, otherwise open a new group
  94. // to the right.
  95. const hasVisibleEditors = vscode.window.visibleTextEditors.length > 0
  96. if (!hasVisibleEditors) {
  97. await vscode.commands.executeCommand("workbench.action.newGroupRight")
  98. }
  99. const targetCol = hasVisibleEditors ? Math.max(lastCol + 1, 1) : vscode.ViewColumn.Two
  100. const newPanel = vscode.window.createWebviewPanel(ClineProvider.tabPanelId, "Roo Code", targetCol, {
  101. enableScripts: true,
  102. retainContextWhenHidden: true,
  103. localResourceRoots: [context.extensionUri],
  104. })
  105. // Save as tab type panel.
  106. setPanel(newPanel, "tab")
  107. // TODO: Use better svg icon with light and dark variants (see
  108. // https://stackoverflow.com/questions/58365687/vscode-extension-iconpath).
  109. newPanel.iconPath = {
  110. light: vscode.Uri.joinPath(context.extensionUri, "assets", "icons", "panel_light.png"),
  111. dark: vscode.Uri.joinPath(context.extensionUri, "assets", "icons", "panel_dark.png"),
  112. }
  113. await tabProvider.resolveWebviewView(newPanel)
  114. // Handle panel closing events.
  115. newPanel.onDidDispose(() => {
  116. setPanel(undefined, "tab")
  117. })
  118. // Lock the editor group so clicking on files doesn't open them over the panel.
  119. await delay(100)
  120. await vscode.commands.executeCommand("workbench.action.lockEditorGroup")
  121. }