Repository: TabbyML/tabby
File: clients/vscode/src/chat/ChatViewProvider.ts (TypeScript)

Code before:
    return;
  }

  private async refreshChatPage() {
    const agentStatus = this.agent.status;
    const serverInfo = await this.agent.fetchServerInfo();

    if (agentStatus === "unauthorized") {
      return this.client?.showError({
        content:
          "Before you can start chatting, please take a moment to set up your credentials to connect to the Tabby server.",
      });
    }

    const error = this.checkChatPanel(serverInfo);
    if (error) {
      this.client?.showError({ content: error });
      return;
    }

    this.pendingRelevantContexts.forEach((ctx) => this.addRelevantContext(ctx));
    this.pendingMessages.forEach((message) => this.sendMessageToChatPanel(message));
    if (serverInfo.config.token) {
      this.client?.cleanError();
      // Duplicate init won't break or reload the current chat page
      const focusKey = contributes.keybindings.find((cmd) => cmd.command === "tabby.chatView.focus");
      let focusKeybinding;
      if (focusKey) {
        focusKeybinding = parseKeybinding(this.isMac && focusKey.mac ? focusKey.mac : focusKey.key);
      }
      getLogger().info("focus key binding: ", focusKeybinding);
      this.client?.init({
        fetcherOptions: {
          authorization: serverInfo.config.token,
        },
        focusKey: focusKeybinding
          ? focusKeybinding
          : {
              key: "l",
              altKey: false,
              metaKey: this.isMac,
              ctrlKey: !this.isMac,
              shiftKey: false,
            },
      });
    }
  }

  private async displayChatPage(endpoint: string, opts?: { force: boolean }) {
    if (!endpoint) return;
    if (this.isChatPageDisplayed && !opts?.force) return;

Review comment (suggestion):
The `contributes` is import from `package.json` at build time, so it is always `ctrl/cmd+l`. So if we are aiming to support the user-customized keybinding, it is required to read the vscode user keybinding config file.

Code after:

    return;
  }

  private async refreshChatPage() {
    const agentStatus = this.agent.status;
    const serverInfo = await this.agent.fetchServerInfo();

    if (agentStatus === "unauthorized") {
      return this.client?.showError({
        content:
          "Before you can start chatting, please take a moment to set up your credentials to connect to the Tabby server.",
      });
    }

    const error = this.checkChatPanel(serverInfo);
    if (error) {
      this.client?.showError({ content: error });
      return;
    }

    this.pendingRelevantContexts.forEach((ctx) => this.addRelevantContext(ctx));
    this.pendingMessages.forEach((message) => this.sendMessageToChatPanel(message));

    if (serverInfo.config.token) {
      this.client?.cleanError();

      const focusKeybinding = await this.getFocusKeybinding();
      getLogger().info("focus key binding: ", focusKeybinding);

      this.client?.init({
        fetcherOptions: {
          authorization: serverInfo.config.token,
        },
        focusKey: focusKeybinding,
      });
    }
  }

  private async getFocusKeybinding(): Promise<FocusKeybinding | undefined> {
    const focusCommand = "tabby.chatView.focus";
    const defaultFocusKey = contributes.keybindings.find((cmd) => cmd.command === focusCommand);
    const defaultKeybinding = defaultFocusKey
      ? parseKeybinding(this.isMac && defaultFocusKey.mac ? defaultFocusKey.mac : defaultFocusKey.key)
      : undefined;

    const allKeybindings = await readUserKeybindingsConfig();
    const userShortcut = allKeybindings?.find((keybinding) => keybinding.command === focusCommand);

    return userShortcut ? parseKeybinding(userShortcut.key) : defaultKeybinding;
  }