Repository: TabbyML/tabby
File: clients/tabby-chat-panel/src/index.ts (TypeScript)

Code before:
   * Open the target file location in the editor.
   * @param target The target file location to open.
   * @returns Whether the file location is opened successfully.
   */
  openInEditor: (target: FileLocation) => Promise<boolean>

  /**
   * Open the target URL in the external browser.
   * @param url The target URL to open.
   */
  openExternal: (url: string) => Promise<void>

  // Provide all repos found in workspace folders.
  readWorkspaceGitRepositories?: () => Promise<GitRepository[]>

  /**
   * @returns The active selection of active editor.
   */
  getActiveEditorSelection: () => Promise<EditorFileContext | null>

  /**
   * Returns a list of file information matching the specified query.
   * @param opts An optional {@link AtInputOpts} object that includes a search query and a limit for the results.
   * @returns An array of {@link FileRange} objects, or `null` if no matching files are found.
   */
  listFileInWorkspace?: (opts?: AtInputOpts) => Promise<FileRange[] | null>

  /**
   * Returns the content of a file within the specified range.
   * If `range` is not provided, the entire file content is returned.
   * @param info A {@link FileRange} object that includes the file path and optionally a 1-based line range.
   * @returns The content of the file as a string, or `null` if the file or range cannot be accessed.
   */
  readFileContent?: (info: FileRange) => Promise<string | null>
}

export interface ClientApi extends ClientApiMethods {
  /**
   * Checks if the client supports this capability.
   * This method is designed to check capability across different clients (IDEs).
   * Note: This method should not be used to ensure compatibility across different chat panel SDK versions.
   */
  hasCapability: (method: keyof ClientApiMethods) => Promise<boolean>
}

export function createClient(target: HTMLIFrameElement, api: ClientApiMethods): ServerApi {
  return createThreadFromIframe(target, {
    expose: {
      refresh: api.refresh,
      onApplyInEditor: api.onApplyInEditor,
      onApplyInEditorV2: api.onApplyInEditorV2,

Review comment (suggestion):
```suggestion
  listFileInWorkspace?: (option?: ListFileOption) => Promise<FileRange[] | null>
```

Code after:
   * Open the target file location in the editor.
   * @param target The target file location to open.
   * @returns Whether the file location is opened successfully.
   */
  openInEditor: (target: FileLocation) => Promise<boolean>

  /**
   * Open the target URL in the external browser.
   * @param url The target URL to open.
   */
  openExternal: (url: string) => Promise<void>

  // Provide all repos found in workspace folders.
  readWorkspaceGitRepositories?: () => Promise<GitRepository[]>

  /**
   * @returns The active selection of active editor.
   */
  getActiveEditorSelection: () => Promise<EditorFileContext | null>

  /**
   * Fetch the saved session state from the client.
   * When initialized, the chat panel attempts to fetch the saved session state to restore the session.
   * @param keys The keys to be fetched. If not provided, all keys will be returned.
   * @return The saved persisted state, or null if no state is found.
   */
  fetchSessionState?: (keys?: string[] | undefined) => Promise<Record<string, unknown> | null>

  /**
   * Save the session state of the chat panel.
   * The client is responsible for maintaining the state in case of a webview reload.
   * The saved state should be merged and updated by the record key.
   * @param state The state to save.
   */
  storeSessionState?: (state: Record<string, unknown>) => Promise<void>

  /**
   * Returns a list of file information matching the specified query.
   * @param params An {@link ListFilesInWorkspaceParams} object that includes a search query and a limit for the results.
   * @returns An array of {@link ListFileItem} objects that could be empty.
   */
  listFileInWorkspace?: (params: ListFilesInWorkspaceParams) => Promise<ListFileItem[]>

  /**
   * Returns the content of a file within the specified range.
   * If `range` is not provided, the entire file content is returned.
   * @param info A {@link FileRange} object that includes the file path and optionally a 1-based line range.
   * @returns The content of the file as a string, or `null` if the file or range cannot be accessed.
   */
  readFileContent?: (info: FileRange) => Promise<string | null>
}