From 04111b318d4768af0b17d2e15bf1ff359f5ccf44 Mon Sep 17 00:00:00 2001 From: liangfung Date: Thu, 2 Jan 2025 15:44:01 +0800 Subject: [PATCH 1/9] refactor(chat): change the implementation of synchronizing the active editor selection --- clients/tabby-chat-panel/src/index.ts | 6 ++ clients/vscode/src/chat/createClient.ts | 1 + clients/vscode/src/chat/webview.ts | 26 +++++-- ee/tabby-ui/app/chat/page.tsx | 49 ++++++++----- .../app/files/components/chat-side-bar.tsx | 53 ++++++++------ .../files/components/source-code-browser.tsx | 11 ++- ee/tabby-ui/components/chat/chat.tsx | 73 ++++++++++++------- 7 files changed, 144 insertions(+), 75 deletions(-) diff --git a/clients/tabby-chat-panel/src/index.ts b/clients/tabby-chat-panel/src/index.ts index 43e44e350522..a9346b4293a1 100644 --- a/clients/tabby-chat-panel/src/index.ts +++ b/clients/tabby-chat-panel/src/index.ts @@ -273,6 +273,11 @@ export interface ClientApiMethods { // Provide all repos found in workspace folders. readWorkspaceGitRepositories?: () => Promise + + /** + * @returns The active selection of active editor. + */ + getActiveEditorSelection?: () => Promise } export interface ClientApi extends ClientApiMethods { @@ -297,6 +302,7 @@ export function createClient(target: HTMLIFrameElement, api: ClientApiMethods): openInEditor: api.openInEditor, openExternal: api.openExternal, readWorkspaceGitRepositories: api.readWorkspaceGitRepositories, + getActiveEditorSelection: api.getActiveEditorSelection, }, }) } diff --git a/clients/vscode/src/chat/createClient.ts b/clients/vscode/src/chat/createClient.ts index a0c65b93b98d..b10fcc4bd55d 100644 --- a/clients/vscode/src/chat/createClient.ts +++ b/clients/vscode/src/chat/createClient.ts @@ -35,6 +35,7 @@ export function createClient(webview: Webview, api: ClientApiMethods): ServerApi openInEditor: api.openInEditor, openExternal: api.openExternal, readWorkspaceGitRepositories: api.readWorkspaceGitRepositories, + getActiveEditorSelection: api.getActiveEditorSelection }, }); } diff --git a/clients/vscode/src/chat/webview.ts b/clients/vscode/src/chat/webview.ts index e51747620210..5f647fd52dc6 100644 --- a/clients/vscode/src/chat/webview.ts +++ b/clients/vscode/src/chat/webview.ts @@ -25,6 +25,7 @@ import type { SymbolInfo, FileLocation, GitRepository, + EditorFileContext, } from "tabby-chat-panel"; import * as semver from "semver"; import type { StatusInfo, Config } from "tabby-agent"; @@ -101,14 +102,14 @@ export class ChatWebview { this.disposables.push( window.onDidChangeActiveTextEditor((editor) => { if (this.chatPanelLoaded) { - this.syncActiveEditorSelection(editor); + this.notifyActiveEditorSelectionChange(editor); } }), ); this.disposables.push( window.onDidChangeTextEditorSelection((event) => { if (event.textEditor === window.activeTextEditor && this.chatPanelLoaded) { - this.syncActiveEditorSelection(event.textEditor); + this.notifyActiveEditorSelectionChange(event.textEditor); } }), ); @@ -237,11 +238,10 @@ export class ChatWebview { this.chatPanelLoaded = true; - // 1. Sync the active editor selection - // 2. Send pending actions - // 3. Call the client's init method - // 4. Show the chat panel (call syncStyle underlay) - await this.syncActiveEditorSelection(window.activeTextEditor); + // 1. Send pending actions + // 2. Call the client's init method + // 3. Show the chat panel (call syncStyle underlay) + // await this.notifyActiveEditorSelectionChange(window.activeTextEditor); this.pendingActions.forEach(async (fn) => { await fn(); @@ -448,6 +448,16 @@ export class ChatWebview { } return infoList; }, + + getActiveEditorSelection: async(): Promise => { + const editor = window.activeTextEditor; + if (!editor) { + return null + } + + const fileContext = await getFileContextFromSelection(editor, this.gitProvider); + return fileContext + } }); } @@ -635,7 +645,7 @@ export class ChatWebview { ); } - private async syncActiveEditorSelection(editor: TextEditor | undefined) { + private async notifyActiveEditorSelectionChange(editor: TextEditor | undefined) { if (!editor || !isValidForSyncActiveEditorSelection(editor)) { await this.client?.updateActiveSelection(null); return; diff --git a/ee/tabby-ui/app/chat/page.tsx b/ee/tabby-ui/app/chat/page.tsx index d7864b99000a..a05744e66913 100644 --- a/ee/tabby-ui/app/chat/page.tsx +++ b/ee/tabby-ui/app/chat/page.tsx @@ -46,7 +46,9 @@ const convertToHSLColor = (style: string) => { } export default function ChatPage() { - const [isInit, setIsInit] = useState(false) + // if chat component loaded + const [isChatComponentLoaded, setIsChatComponentLoaded] = useState(false) + const [isServerInitialized, setIsServerInitialized] = useState(false) const [fetcherOptions, setFetcherOptions] = useState( null ) @@ -61,7 +63,6 @@ export default function ChatPage() { const [isRefreshLoading, setIsRefreshLoading] = useState(false) const chatRef = useRef(null) - const [chatLoaded, setChatLoaded] = useState(false) const { width } = useWindowSize() const prevWidthRef = useRef(width) const chatInputRef = useRef(null) @@ -76,8 +77,12 @@ export default function ChatPage() { useState(false) const [supportsOnLookupSymbol, setSupportsOnLookupSymbol] = useState(false) const [ - supportsProvideWorkspaceGitRepoInfo, - setSupportsProvideWorkspaceGitRepoInfo + supportsReadWorkspaceGitRepoInfo, + setSupportsReadWorkspaceGitRepoInfo + ] = useState(false) + const [ + supportsGetActiveEditorSelection, + setSupportsGetActiveEditorSelection ] = useState(false) const executeCommand = (command: ChatCommand) => { @@ -116,7 +121,6 @@ export default function ChatPage() { } setActiveChatId(nanoid()) - setIsInit(true) setFetcherOptions(request.fetcherOptions) useMacOSKeyboardEventHandler.current = request.useMacOSKeyboardEventHandler @@ -244,18 +248,25 @@ export default function ChatPage() { server?.hasCapability('lookupSymbol').then(setSupportsOnLookupSymbol) server ?.hasCapability('readWorkspaceGitRepositories') - .then(setSupportsProvideWorkspaceGitRepoInfo) + .then(setSupportsReadWorkspaceGitRepoInfo) + server + ?.hasCapability('getActiveEditorSelection') + .then(setSupportsGetActiveEditorSelection) } - checkCapabilities() + checkCapabilities().then(() => { + setTimeout(() => { + setIsServerInitialized(true) + }) + }) } }, [server]) useLayoutEffect(() => { - if (!chatLoaded) return + if (!isChatComponentLoaded) return if ( width && - isInit && + isServerInitialized && fetcherOptions && !errorMessage && !prevWidthRef.current @@ -263,7 +274,7 @@ export default function ChatPage() { chatRef.current?.focus() } prevWidthRef.current = width - }, [width, chatLoaded]) + }, [width, isChatComponentLoaded]) const clearPendingState = () => { setPendingRelevantContexts([]) @@ -271,7 +282,7 @@ export default function ChatPage() { setPendingActiveSelection(null) } - const onChatLoaded = () => { + const onChatLoaded = async () => { const currentChatRef = chatRef.current if (!currentChatRef) return @@ -284,14 +295,11 @@ export default function ChatPage() { } if (pendingCommand) { - // FIXME: this delay is a workaround for waiting for the active selection to be updated - setTimeout(() => { - currentChatRef.executeCommand(pendingCommand) - }, 500) + await currentChatRef.executeCommand(pendingCommand) } clearPendingState() - setChatLoaded(true) + await setIsChatComponentLoaded(true) } const openInEditor = async (fileLocation: FileLocation) => { @@ -376,7 +384,7 @@ export default function ChatPage() { ) } - if (!isInit || !fetcherOptions) { + if (!isServerInitialized || !fetcherOptions) { return ( <> @@ -420,10 +428,15 @@ export default function ChatPage() { openInEditor={openInEditor} openExternal={openExternal} readWorkspaceGitRepositories={ - supportsProvideWorkspaceGitRepoInfo + supportsReadWorkspaceGitRepoInfo ? server?.readWorkspaceGitRepositories : undefined } + getActiveEditorSelection={ + supportsGetActiveEditorSelection + ? server?.getActiveEditorSelection + : undefined + } /> ) diff --git a/ee/tabby-ui/app/files/components/chat-side-bar.tsx b/ee/tabby-ui/app/files/components/chat-side-bar.tsx index 52d1a1156586..668c04cae262 100644 --- a/ee/tabby-ui/app/files/components/chat-side-bar.tsx +++ b/ee/tabby-ui/app/files/components/chat-side-bar.tsx @@ -1,6 +1,10 @@ -import React, { useRef, useState } from 'react' +import React, { useState } from 'react' import { find } from 'lodash-es' -import type { FileLocation, GitRepository } from 'tabby-chat-panel' +import type { + EditorContext, + FileLocation, + GitRepository +} from 'tabby-chat-panel' import { useClient } from 'tabby-chat-panel/react' import { RepositoryListQuery } from '@/lib/gql/generates/graphql' @@ -32,7 +36,6 @@ export const ChatSideBar: React.FC = ({ React.useContext(SourceCodeBrowserContext) const activeChatId = useChatStore(state => state.activeChatId) const iframeRef = React.useRef(null) - const executedCommand = useRef(false) const repoMapRef = useLatest(repoMap) const openInCodeBrowser = async (fileLocation: FileLocation) => { const { filepath, location } = fileLocation @@ -72,6 +75,25 @@ export const ChatSideBar: React.FC = ({ return list }) + const getActiveEditorSelection = useLatest(() => { + if (!pendingEvent) return null + const { lineFrom, lineTo, code, path, gitUrl } = pendingEvent + const activeSelection: EditorContext = { + kind: 'file', + content: code, + range: { + start: lineFrom, + end: lineTo ?? lineFrom + }, + filepath: { + kind: 'git', + filepath: path, + gitUrl + } + } + return activeSelection + }) + const client = useClient(iframeRef, { refresh: async () => { window.location.reload() @@ -94,7 +116,10 @@ export const ChatSideBar: React.FC = ({ window.open(url, '_blank') }, readWorkspaceGitRepositories: async () => { - return readWorkspaceGitRepositories.current?.() + return readWorkspaceGitRepositories.current() + }, + getActiveEditorSelection: async () => { + return getActiveEditorSelection.current() } }) @@ -122,25 +147,9 @@ export const ChatSideBar: React.FC = ({ React.useEffect(() => { if (pendingEvent && client && initialized) { const execute = async () => { - const { lineFrom, lineTo, code, path, gitUrl } = pendingEvent - client.updateActiveSelection({ - kind: 'file', - content: code, - range: { - start: lineFrom, - end: lineTo ?? lineFrom - }, - filepath: { - kind: 'git', - filepath: path, - gitUrl - } - }) + // todo notify activeselection change const command = getCommand(pendingEvent) - // FIXME: this delay is a workaround for waiting for the active selection to be updated - setTimeout(() => { - client.executeCommand(command) - }, 500) + client.executeCommand(command) setPendingEvent(undefined) } diff --git a/ee/tabby-ui/app/files/components/source-code-browser.tsx b/ee/tabby-ui/app/files/components/source-code-browser.tsx index ef501e77a9b3..a7c1e96c0b49 100644 --- a/ee/tabby-ui/app/files/components/source-code-browser.tsx +++ b/ee/tabby-ui/app/files/components/source-code-browser.tsx @@ -361,6 +361,7 @@ const SourceCodeBrowserRenderer: React.FC = ({ setInitialized, chatSideBarVisible, setChatSideBarVisible, + pendingEvent, setPendingEvent, repoMap, setRepoMap, @@ -655,8 +656,10 @@ const SourceCodeBrowserRenderer: React.FC = ({ React.useEffect(() => { const onCallCompletion = (data: QuickActionEventPayload) => { - setChatSideBarVisible(true) setPendingEvent(data) + // setTimeout(() => { + // setChatSideBarVisible(true) + // }) } emitter.on('code_browser_quick_action', onCallCompletion) @@ -665,6 +668,12 @@ const SourceCodeBrowserRenderer: React.FC = ({ } }, []) + React.useEffect(() => { + if (pendingEvent && !chatSideBarVisible) { + setChatSideBarVisible(true) + } + }, [pendingEvent]) + return ( { chatId: string api?: string initialMessages?: QuestionAnswerPair[] - onLoaded?: () => void + onLoaded?: () => Promise onThreadUpdates?: (messages: QuestionAnswerPair[]) => void container?: HTMLDivElement docQuery?: boolean @@ -119,6 +120,7 @@ interface ChatProps extends React.ComponentProps<'div'> { chatInputRef: RefObject supportsOnApplyInEditorV2: boolean readWorkspaceGitRepositories?: () => Promise + getActiveEditorSelection?: () => Promise } function ChatRenderer( @@ -141,10 +143,12 @@ function ChatRenderer( openExternal, chatInputRef, supportsOnApplyInEditorV2, - readWorkspaceGitRepositories + readWorkspaceGitRepositories, + getActiveEditorSelection }: ChatProps, ref: React.ForwardedRef ) { + const [isDataSetup, setIsDataSetup] = React.useState(false) const [initialized, setInitialized] = React.useState(false) const [threadId, setThreadId] = React.useState() const isOnLoadExecuted = React.useRef(false) @@ -516,7 +520,7 @@ function ChatRenderer( (ctx: Context | null) => { setActiveSelection(ctx) }, - 300 + 200 ) const updateActiveSelection = (editorContext: EditorContext | null) => { @@ -532,10 +536,18 @@ function ChatRenderer( } } + const initActiveEditorSelection = async () => { + return getActiveEditorSelection?.() + } + React.useEffect(() => { const init = async () => { - const workspaceGitRepositories = await fetchWorkspaceGitRepo() - // get default repo + const [workspaceGitRepositories, activeEditorSelecition] = + await Promise.all([ + fetchWorkspaceGitRepo(), + initActiveEditorSelection() + ]) + // get default repository if (workspaceGitRepositories?.length && repos?.length) { const defaultGitUrl = workspaceGitRepositories[0].url const repo = findClosestGitRepository( @@ -547,19 +559,26 @@ function ChatRenderer( } } - setInitialized(true) + // update active selection + if (activeEditorSelecition) { + const context = convertEditorContext(activeEditorSelecition) + setActiveSelection(context) + } } - if (!fetchingRepos && !initialized) { - init() + if (!fetchingRepos && !isDataSetup) { + init().finally(() => { + setIsDataSetup(true) + }) } - }, [fetchingRepos]) + }, [fetchingRepos, isDataSetup]) React.useEffect(() => { - if (initialized) { + if (isDataSetup) { onLoaded?.() + setInitialized(true) } - }, [initialized]) + }, [isDataSetup]) React.useImperativeHandle( ref, @@ -609,21 +628,23 @@ function ChatRenderer( className={`w-full px-4 md:pl-10 md:pr-[3.75rem] ${chatMaxWidthClass}`} > {/* FIXME: pb-[200px] might not enough when adding a large number of relevantContext */} -
- {qaPairs?.length ? ( - - ) : ( - - )} - -
+ {initialized && ( +
+ {qaPairs?.length ? ( + + ) : ( + + )} + +
+ )} Date: Thu, 2 Jan 2025 15:55:50 +0800 Subject: [PATCH 2/9] update --- ee/tabby-ui/app/chat/page.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ee/tabby-ui/app/chat/page.tsx b/ee/tabby-ui/app/chat/page.tsx index a05744e66913..54473d0e321e 100644 --- a/ee/tabby-ui/app/chat/page.tsx +++ b/ee/tabby-ui/app/chat/page.tsx @@ -46,9 +46,8 @@ const convertToHSLColor = (style: string) => { } export default function ChatPage() { - // if chat component loaded const [isChatComponentLoaded, setIsChatComponentLoaded] = useState(false) - const [isServerInitialized, setIsServerInitialized] = useState(false) + const [isServerLoaded, setIsServerLoaded] = useState(false) const [fetcherOptions, setFetcherOptions] = useState( null ) @@ -256,7 +255,7 @@ export default function ChatPage() { checkCapabilities().then(() => { setTimeout(() => { - setIsServerInitialized(true) + setIsServerLoaded(true) }) }) } @@ -266,7 +265,7 @@ export default function ChatPage() { if (!isChatComponentLoaded) return if ( width && - isServerInitialized && + isServerLoaded && fetcherOptions && !errorMessage && !prevWidthRef.current @@ -384,7 +383,7 @@ export default function ChatPage() { ) } - if (!isServerInitialized || !fetcherOptions) { + if (!isServerLoaded || !fetcherOptions) { return ( <> From debbeb2ad0ac4bb80934380dee25bace441db83a Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 07:57:18 +0000 Subject: [PATCH 3/9] [autofix.ci] apply automated fixes --- clients/vscode/src/chat/createClient.ts | 2 +- clients/vscode/src/chat/webview.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clients/vscode/src/chat/createClient.ts b/clients/vscode/src/chat/createClient.ts index b10fcc4bd55d..924ee0f546d0 100644 --- a/clients/vscode/src/chat/createClient.ts +++ b/clients/vscode/src/chat/createClient.ts @@ -35,7 +35,7 @@ export function createClient(webview: Webview, api: ClientApiMethods): ServerApi openInEditor: api.openInEditor, openExternal: api.openExternal, readWorkspaceGitRepositories: api.readWorkspaceGitRepositories, - getActiveEditorSelection: api.getActiveEditorSelection + getActiveEditorSelection: api.getActiveEditorSelection, }, }); } diff --git a/clients/vscode/src/chat/webview.ts b/clients/vscode/src/chat/webview.ts index 5f647fd52dc6..278c852f901b 100644 --- a/clients/vscode/src/chat/webview.ts +++ b/clients/vscode/src/chat/webview.ts @@ -449,15 +449,15 @@ export class ChatWebview { return infoList; }, - getActiveEditorSelection: async(): Promise => { + getActiveEditorSelection: async (): Promise => { const editor = window.activeTextEditor; - if (!editor) { - return null + if (!editor) { + return null; } const fileContext = await getFileContextFromSelection(editor, this.gitProvider); - return fileContext - } + return fileContext; + }, }); } From ebe3b67239de222d1c8b728f94a35ea3ba4a35c3 Mon Sep 17 00:00:00 2001 From: aliang Date: Thu, 2 Jan 2025 16:41:35 +0800 Subject: [PATCH 4/9] Update clients/vscode/src/chat/webview.ts Co-authored-by: Zhiming Ma --- clients/vscode/src/chat/webview.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/vscode/src/chat/webview.ts b/clients/vscode/src/chat/webview.ts index 278c852f901b..e15c917bf9cc 100644 --- a/clients/vscode/src/chat/webview.ts +++ b/clients/vscode/src/chat/webview.ts @@ -451,7 +451,7 @@ export class ChatWebview { getActiveEditorSelection: async (): Promise => { const editor = window.activeTextEditor; - if (!editor) { + if (!editor || !isValidForSyncActiveEditorSelection(editor)) { return null; } From 2dd4c97259f649b8cd48e008246a1265cbe8cc1d Mon Sep 17 00:00:00 2001 From: liangfung Date: Thu, 2 Jan 2025 17:01:52 +0800 Subject: [PATCH 5/9] update: required --- clients/tabby-chat-panel/src/index.ts | 2 +- ee/tabby-ui/app/chat/page.tsx | 17 +++++------------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/clients/tabby-chat-panel/src/index.ts b/clients/tabby-chat-panel/src/index.ts index a9346b4293a1..857d83a42362 100644 --- a/clients/tabby-chat-panel/src/index.ts +++ b/clients/tabby-chat-panel/src/index.ts @@ -277,7 +277,7 @@ export interface ClientApiMethods { /** * @returns The active selection of active editor. */ - getActiveEditorSelection?: () => Promise + getActiveEditorSelection: () => Promise } export interface ClientApi extends ClientApiMethods { diff --git a/ee/tabby-ui/app/chat/page.tsx b/ee/tabby-ui/app/chat/page.tsx index 54473d0e321e..2a33e1cc6437 100644 --- a/ee/tabby-ui/app/chat/page.tsx +++ b/ee/tabby-ui/app/chat/page.tsx @@ -79,10 +79,6 @@ export default function ChatPage() { supportsReadWorkspaceGitRepoInfo, setSupportsReadWorkspaceGitRepoInfo ] = useState(false) - const [ - supportsGetActiveEditorSelection, - setSupportsGetActiveEditorSelection - ] = useState(false) const executeCommand = (command: ChatCommand) => { if (chatRef.current) { @@ -248,9 +244,6 @@ export default function ChatPage() { server ?.hasCapability('readWorkspaceGitRepositories') .then(setSupportsReadWorkspaceGitRepoInfo) - server - ?.hasCapability('getActiveEditorSelection') - .then(setSupportsGetActiveEditorSelection) } checkCapabilities().then(() => { @@ -309,6 +302,10 @@ export default function ChatPage() { return server?.openExternal(url) } + const getActiveEditorSelection = async () => { + return server?.getActiveEditorSelection() ?? null + } + const refresh = async () => { setIsRefreshLoading(true) await server?.refresh() @@ -431,11 +428,7 @@ export default function ChatPage() { ? server?.readWorkspaceGitRepositories : undefined } - getActiveEditorSelection={ - supportsGetActiveEditorSelection - ? server?.getActiveEditorSelection - : undefined - } + getActiveEditorSelection={getActiveEditorSelection} /> ) From aed733b042d27aa177b86d06aa798277c3a61346 Mon Sep 17 00:00:00 2001 From: liangfung Date: Thu, 2 Jan 2025 17:11:28 +0800 Subject: [PATCH 6/9] revert --- ee/tabby-ui/app/chat/page.tsx | 41 +++++------ .../app/files/components/chat-side-bar.tsx | 53 ++++++-------- .../files/components/source-code-browser.tsx | 11 +-- ee/tabby-ui/components/chat/chat.tsx | 73 +++++++------------ 4 files changed, 67 insertions(+), 111 deletions(-) diff --git a/ee/tabby-ui/app/chat/page.tsx b/ee/tabby-ui/app/chat/page.tsx index 2a33e1cc6437..d7864b99000a 100644 --- a/ee/tabby-ui/app/chat/page.tsx +++ b/ee/tabby-ui/app/chat/page.tsx @@ -46,8 +46,7 @@ const convertToHSLColor = (style: string) => { } export default function ChatPage() { - const [isChatComponentLoaded, setIsChatComponentLoaded] = useState(false) - const [isServerLoaded, setIsServerLoaded] = useState(false) + const [isInit, setIsInit] = useState(false) const [fetcherOptions, setFetcherOptions] = useState( null ) @@ -62,6 +61,7 @@ export default function ChatPage() { const [isRefreshLoading, setIsRefreshLoading] = useState(false) const chatRef = useRef(null) + const [chatLoaded, setChatLoaded] = useState(false) const { width } = useWindowSize() const prevWidthRef = useRef(width) const chatInputRef = useRef(null) @@ -76,8 +76,8 @@ export default function ChatPage() { useState(false) const [supportsOnLookupSymbol, setSupportsOnLookupSymbol] = useState(false) const [ - supportsReadWorkspaceGitRepoInfo, - setSupportsReadWorkspaceGitRepoInfo + supportsProvideWorkspaceGitRepoInfo, + setSupportsProvideWorkspaceGitRepoInfo ] = useState(false) const executeCommand = (command: ChatCommand) => { @@ -116,6 +116,7 @@ export default function ChatPage() { } setActiveChatId(nanoid()) + setIsInit(true) setFetcherOptions(request.fetcherOptions) useMacOSKeyboardEventHandler.current = request.useMacOSKeyboardEventHandler @@ -243,22 +244,18 @@ export default function ChatPage() { server?.hasCapability('lookupSymbol').then(setSupportsOnLookupSymbol) server ?.hasCapability('readWorkspaceGitRepositories') - .then(setSupportsReadWorkspaceGitRepoInfo) + .then(setSupportsProvideWorkspaceGitRepoInfo) } - checkCapabilities().then(() => { - setTimeout(() => { - setIsServerLoaded(true) - }) - }) + checkCapabilities() } }, [server]) useLayoutEffect(() => { - if (!isChatComponentLoaded) return + if (!chatLoaded) return if ( width && - isServerLoaded && + isInit && fetcherOptions && !errorMessage && !prevWidthRef.current @@ -266,7 +263,7 @@ export default function ChatPage() { chatRef.current?.focus() } prevWidthRef.current = width - }, [width, isChatComponentLoaded]) + }, [width, chatLoaded]) const clearPendingState = () => { setPendingRelevantContexts([]) @@ -274,7 +271,7 @@ export default function ChatPage() { setPendingActiveSelection(null) } - const onChatLoaded = async () => { + const onChatLoaded = () => { const currentChatRef = chatRef.current if (!currentChatRef) return @@ -287,11 +284,14 @@ export default function ChatPage() { } if (pendingCommand) { - await currentChatRef.executeCommand(pendingCommand) + // FIXME: this delay is a workaround for waiting for the active selection to be updated + setTimeout(() => { + currentChatRef.executeCommand(pendingCommand) + }, 500) } clearPendingState() - await setIsChatComponentLoaded(true) + setChatLoaded(true) } const openInEditor = async (fileLocation: FileLocation) => { @@ -302,10 +302,6 @@ export default function ChatPage() { return server?.openExternal(url) } - const getActiveEditorSelection = async () => { - return server?.getActiveEditorSelection() ?? null - } - const refresh = async () => { setIsRefreshLoading(true) await server?.refresh() @@ -380,7 +376,7 @@ export default function ChatPage() { ) } - if (!isServerLoaded || !fetcherOptions) { + if (!isInit || !fetcherOptions) { return ( <> @@ -424,11 +420,10 @@ export default function ChatPage() { openInEditor={openInEditor} openExternal={openExternal} readWorkspaceGitRepositories={ - supportsReadWorkspaceGitRepoInfo + supportsProvideWorkspaceGitRepoInfo ? server?.readWorkspaceGitRepositories : undefined } - getActiveEditorSelection={getActiveEditorSelection} /> ) diff --git a/ee/tabby-ui/app/files/components/chat-side-bar.tsx b/ee/tabby-ui/app/files/components/chat-side-bar.tsx index 668c04cae262..52d1a1156586 100644 --- a/ee/tabby-ui/app/files/components/chat-side-bar.tsx +++ b/ee/tabby-ui/app/files/components/chat-side-bar.tsx @@ -1,10 +1,6 @@ -import React, { useState } from 'react' +import React, { useRef, useState } from 'react' import { find } from 'lodash-es' -import type { - EditorContext, - FileLocation, - GitRepository -} from 'tabby-chat-panel' +import type { FileLocation, GitRepository } from 'tabby-chat-panel' import { useClient } from 'tabby-chat-panel/react' import { RepositoryListQuery } from '@/lib/gql/generates/graphql' @@ -36,6 +32,7 @@ export const ChatSideBar: React.FC = ({ React.useContext(SourceCodeBrowserContext) const activeChatId = useChatStore(state => state.activeChatId) const iframeRef = React.useRef(null) + const executedCommand = useRef(false) const repoMapRef = useLatest(repoMap) const openInCodeBrowser = async (fileLocation: FileLocation) => { const { filepath, location } = fileLocation @@ -75,25 +72,6 @@ export const ChatSideBar: React.FC = ({ return list }) - const getActiveEditorSelection = useLatest(() => { - if (!pendingEvent) return null - const { lineFrom, lineTo, code, path, gitUrl } = pendingEvent - const activeSelection: EditorContext = { - kind: 'file', - content: code, - range: { - start: lineFrom, - end: lineTo ?? lineFrom - }, - filepath: { - kind: 'git', - filepath: path, - gitUrl - } - } - return activeSelection - }) - const client = useClient(iframeRef, { refresh: async () => { window.location.reload() @@ -116,10 +94,7 @@ export const ChatSideBar: React.FC = ({ window.open(url, '_blank') }, readWorkspaceGitRepositories: async () => { - return readWorkspaceGitRepositories.current() - }, - getActiveEditorSelection: async () => { - return getActiveEditorSelection.current() + return readWorkspaceGitRepositories.current?.() } }) @@ -147,9 +122,25 @@ export const ChatSideBar: React.FC = ({ React.useEffect(() => { if (pendingEvent && client && initialized) { const execute = async () => { - // todo notify activeselection change + const { lineFrom, lineTo, code, path, gitUrl } = pendingEvent + client.updateActiveSelection({ + kind: 'file', + content: code, + range: { + start: lineFrom, + end: lineTo ?? lineFrom + }, + filepath: { + kind: 'git', + filepath: path, + gitUrl + } + }) const command = getCommand(pendingEvent) - client.executeCommand(command) + // FIXME: this delay is a workaround for waiting for the active selection to be updated + setTimeout(() => { + client.executeCommand(command) + }, 500) setPendingEvent(undefined) } diff --git a/ee/tabby-ui/app/files/components/source-code-browser.tsx b/ee/tabby-ui/app/files/components/source-code-browser.tsx index a7c1e96c0b49..ef501e77a9b3 100644 --- a/ee/tabby-ui/app/files/components/source-code-browser.tsx +++ b/ee/tabby-ui/app/files/components/source-code-browser.tsx @@ -361,7 +361,6 @@ const SourceCodeBrowserRenderer: React.FC = ({ setInitialized, chatSideBarVisible, setChatSideBarVisible, - pendingEvent, setPendingEvent, repoMap, setRepoMap, @@ -656,10 +655,8 @@ const SourceCodeBrowserRenderer: React.FC = ({ React.useEffect(() => { const onCallCompletion = (data: QuickActionEventPayload) => { + setChatSideBarVisible(true) setPendingEvent(data) - // setTimeout(() => { - // setChatSideBarVisible(true) - // }) } emitter.on('code_browser_quick_action', onCallCompletion) @@ -668,12 +665,6 @@ const SourceCodeBrowserRenderer: React.FC = ({ } }, []) - React.useEffect(() => { - if (pendingEvent && !chatSideBarVisible) { - setChatSideBarVisible(true) - } - }, [pendingEvent]) - return ( { chatId: string api?: string initialMessages?: QuestionAnswerPair[] - onLoaded?: () => Promise + onLoaded?: () => void onThreadUpdates?: (messages: QuestionAnswerPair[]) => void container?: HTMLDivElement docQuery?: boolean @@ -120,7 +119,6 @@ interface ChatProps extends React.ComponentProps<'div'> { chatInputRef: RefObject supportsOnApplyInEditorV2: boolean readWorkspaceGitRepositories?: () => Promise - getActiveEditorSelection?: () => Promise } function ChatRenderer( @@ -143,12 +141,10 @@ function ChatRenderer( openExternal, chatInputRef, supportsOnApplyInEditorV2, - readWorkspaceGitRepositories, - getActiveEditorSelection + readWorkspaceGitRepositories }: ChatProps, ref: React.ForwardedRef ) { - const [isDataSetup, setIsDataSetup] = React.useState(false) const [initialized, setInitialized] = React.useState(false) const [threadId, setThreadId] = React.useState() const isOnLoadExecuted = React.useRef(false) @@ -520,7 +516,7 @@ function ChatRenderer( (ctx: Context | null) => { setActiveSelection(ctx) }, - 200 + 300 ) const updateActiveSelection = (editorContext: EditorContext | null) => { @@ -536,18 +532,10 @@ function ChatRenderer( } } - const initActiveEditorSelection = async () => { - return getActiveEditorSelection?.() - } - React.useEffect(() => { const init = async () => { - const [workspaceGitRepositories, activeEditorSelecition] = - await Promise.all([ - fetchWorkspaceGitRepo(), - initActiveEditorSelection() - ]) - // get default repository + const workspaceGitRepositories = await fetchWorkspaceGitRepo() + // get default repo if (workspaceGitRepositories?.length && repos?.length) { const defaultGitUrl = workspaceGitRepositories[0].url const repo = findClosestGitRepository( @@ -559,26 +547,19 @@ function ChatRenderer( } } - // update active selection - if (activeEditorSelecition) { - const context = convertEditorContext(activeEditorSelecition) - setActiveSelection(context) - } + setInitialized(true) } - if (!fetchingRepos && !isDataSetup) { - init().finally(() => { - setIsDataSetup(true) - }) + if (!fetchingRepos && !initialized) { + init() } - }, [fetchingRepos, isDataSetup]) + }, [fetchingRepos]) React.useEffect(() => { - if (isDataSetup) { + if (initialized) { onLoaded?.() - setInitialized(true) } - }, [isDataSetup]) + }, [initialized]) React.useImperativeHandle( ref, @@ -628,23 +609,21 @@ function ChatRenderer( className={`w-full px-4 md:pl-10 md:pr-[3.75rem] ${chatMaxWidthClass}`} > {/* FIXME: pb-[200px] might not enough when adding a large number of relevantContext */} - {initialized && ( -
- {qaPairs?.length ? ( - - ) : ( - - )} - -
- )} +
+ {qaPairs?.length ? ( + + ) : ( + + )} + +
Date: Thu, 2 Jan 2025 17:12:14 +0800 Subject: [PATCH 7/9] update --- clients/vscode/src/chat/webview.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/clients/vscode/src/chat/webview.ts b/clients/vscode/src/chat/webview.ts index e15c917bf9cc..9cef59828d50 100644 --- a/clients/vscode/src/chat/webview.ts +++ b/clients/vscode/src/chat/webview.ts @@ -241,8 +241,6 @@ export class ChatWebview { // 1. Send pending actions // 2. Call the client's init method // 3. Show the chat panel (call syncStyle underlay) - // await this.notifyActiveEditorSelectionChange(window.activeTextEditor); - this.pendingActions.forEach(async (fn) => { await fn(); }); From 92d2376d836037e69601d7f09e35afef5fc35a1c Mon Sep 17 00:00:00 2001 From: liangfung Date: Thu, 2 Jan 2025 17:20:59 +0800 Subject: [PATCH 8/9] update --- ee/tabby-ui/app/files/components/chat-side-bar.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ee/tabby-ui/app/files/components/chat-side-bar.tsx b/ee/tabby-ui/app/files/components/chat-side-bar.tsx index 52d1a1156586..a5977316fb30 100644 --- a/ee/tabby-ui/app/files/components/chat-side-bar.tsx +++ b/ee/tabby-ui/app/files/components/chat-side-bar.tsx @@ -95,7 +95,11 @@ export const ChatSideBar: React.FC = ({ }, readWorkspaceGitRepositories: async () => { return readWorkspaceGitRepositories.current?.() - } + }, + getActiveEditorSelection: async() => { + // FIXME implement + return null + }, }) const getCommand = ({ action }: QuickActionEventPayload) => { From b346ce7068235c775d4bbd93632cde28e34b8916 Mon Sep 17 00:00:00 2001 From: liangfung Date: Thu, 2 Jan 2025 17:21:53 +0800 Subject: [PATCH 9/9] update --- ee/tabby-ui/app/files/components/chat-side-bar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/tabby-ui/app/files/components/chat-side-bar.tsx b/ee/tabby-ui/app/files/components/chat-side-bar.tsx index a5977316fb30..134de1ce0486 100644 --- a/ee/tabby-ui/app/files/components/chat-side-bar.tsx +++ b/ee/tabby-ui/app/files/components/chat-side-bar.tsx @@ -97,7 +97,7 @@ export const ChatSideBar: React.FC = ({ return readWorkspaceGitRepositories.current?.() }, getActiveEditorSelection: async() => { - // FIXME implement + // FIXME(@jueliang) implement return null }, })