From 9a8448facf9855250f4fede7ec76db5d34770947 Mon Sep 17 00:00:00 2001 From: liangfung Date: Thu, 12 Dec 2024 16:40:26 +0700 Subject: [PATCH 01/39] feat(ui): resolve indexed repository of IDE workspace --- ee/tabby-ui/app/chat/page.tsx | 15 +++++ ee/tabby-ui/components/chat/chat-panel.tsx | 67 +++++++++++++++++++++- ee/tabby-ui/components/chat/chat.tsx | 8 ++- ee/tabby-ui/lib/stores/chat-actions.ts | 4 ++ ee/tabby-ui/lib/stores/chat-store.ts | 4 +- 5 files changed, 93 insertions(+), 5 deletions(-) diff --git a/ee/tabby-ui/app/chat/page.tsx b/ee/tabby-ui/app/chat/page.tsx index 48287de58c56..62cb048d5f22 100644 --- a/ee/tabby-ui/app/chat/page.tsx +++ b/ee/tabby-ui/app/chat/page.tsx @@ -28,6 +28,9 @@ import { MemoizedReactMarkdown } from '@/components/markdown' import './page.css' +import { useQuery } from 'urql' + +import { resolveGitUrlQuery } from '@/lib/tabby/query' import { saveFetcherOptions } from '@/lib/tabby/token-management' const convertToHSLColor = (style: string) => { @@ -154,6 +157,17 @@ export default function ChatPage() { updateActiveSelection }) + // FIXME get url from workspace + const workspaceGitURL = 'https://github.com/tabbyML/tabby' + + const [{ data, fetching }] = useQuery({ + query: resolveGitUrlQuery, + variables: { + gitUrl: workspaceGitURL + }, + pause: !workspaceGitURL + }) + useEffect(() => { const onFocus = () => { // When we receive top level focus, just focus chatInputRef @@ -395,6 +409,7 @@ export default function ChatPage() { (supportsOnLookupSymbol ? server?.lookupSymbol : undefined) } openInEditor={isInEditor && server?.openInEditor} + indexedRepository={data?.resolveGitUrl} /> ) diff --git a/ee/tabby-ui/components/chat/chat-panel.tsx b/ee/tabby-ui/components/chat/chat-panel.tsx index b9f41fa47a0d..4a182523ffb1 100644 --- a/ee/tabby-ui/components/chat/chat-panel.tsx +++ b/ee/tabby-ui/components/chat/chat-panel.tsx @@ -9,7 +9,10 @@ import type { Context } from 'tabby-chat-panel' import { SLUG_TITLE_MAX_LENGTH } from '@/lib/constants' import { useCopyToClipboard } from '@/lib/hooks/use-copy-to-clipboard' -import { updateEnableActiveSelection } from '@/lib/stores/chat-actions' +import { + updateEnableActiveSelection, + updateEnableIndexedRepository +} from '@/lib/stores/chat-actions' import { useChatStore } from '@/lib/stores/chat-store' import { useMutation } from '@/lib/tabby/gql' import { setThreadPersistedMutation } from '@/lib/tabby/query' @@ -20,6 +23,7 @@ import { IconCheck, IconEye, IconEyeOff, + IconFolderGit, IconRefresh, IconRemove, IconShare, @@ -70,11 +74,16 @@ function ChatPanelRenderer( relevantContext, removeRelevantContext, activeSelection, - onCopyContent + onCopyContent, + indexedRepository } = React.useContext(ChatContext) const enableActiveSelection = useChatStore( state => state.enableActiveSelection ) + const enableIndexedRepository = useChatStore( + state => state.enableIndexedRepository + ) + const [persisting, setPerisiting] = useState(false) const { width } = useWindowSize() const isExtraSmallScreen = typeof width === 'number' && width < 376 @@ -220,6 +229,60 @@ function ChatPanelRenderer(
+ {indexedRepository ? ( + + + + + + {indexedRepository.name} + + Current repo + + + + + + + + Indexed repository of current workspace: + +

{indexedRepository.gitUrl}

+
+
+ ) : null} {activeSelection ? ( void chatInputRef: RefObject supportsOnApplyInEditorV2: boolean + indexedRepository: ResolveGitUrlQuery['resolveGitUrl'] } export const ChatContext = React.createContext( @@ -125,7 +127,8 @@ function ChatRenderer( onLookupSymbol, openInEditor, chatInputRef, - supportsOnApplyInEditorV2 + supportsOnApplyInEditorV2, + indexedRepository }: ChatProps, ref: React.ForwardedRef ) { @@ -556,7 +559,8 @@ function ChatRenderer( removeRelevantContext, chatInputRef, activeSelection, - supportsOnApplyInEditorV2 + supportsOnApplyInEditorV2, + indexedRepository }} >
diff --git a/ee/tabby-ui/lib/stores/chat-actions.ts b/ee/tabby-ui/lib/stores/chat-actions.ts index eb88174aad17..52349cbb624c 100644 --- a/ee/tabby-ui/lib/stores/chat-actions.ts +++ b/ee/tabby-ui/lib/stores/chat-actions.ts @@ -13,3 +13,7 @@ export const updateSelectedModel = (model: string | undefined) => { export const updateEnableActiveSelection = (enable: boolean) => { set(() => ({ enableActiveSelection: enable })) } + +export const updateEnableIndexedRepository = (enable: boolean) => { + set(() => ({ enableIndexedRepository: enable })) +} diff --git a/ee/tabby-ui/lib/stores/chat-store.ts b/ee/tabby-ui/lib/stores/chat-store.ts index 1c09b032c6b7..6d0bd7674bac 100644 --- a/ee/tabby-ui/lib/stores/chat-store.ts +++ b/ee/tabby-ui/lib/stores/chat-store.ts @@ -9,12 +9,14 @@ export interface ChatState { activeChatId: string | undefined selectedModel: string | undefined enableActiveSelection: boolean + enableIndexedRepository: boolean } const initialState: ChatState = { activeChatId: nanoid(), selectedModel: undefined, - enableActiveSelection: true + enableActiveSelection: true, + enableIndexedRepository: true } export const useChatStore = create()( From eb6a1ac8f070d71eeed3a33e6618819a79ea6cf6 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 09:46:24 +0000 Subject: [PATCH 02/39] [autofix.ci] apply automated fixes --- ee/tabby-ui/components/chat/chat-panel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/tabby-ui/components/chat/chat-panel.tsx b/ee/tabby-ui/components/chat/chat-panel.tsx index 4a182523ffb1..07f71b117ef1 100644 --- a/ee/tabby-ui/components/chat/chat-panel.tsx +++ b/ee/tabby-ui/components/chat/chat-panel.tsx @@ -276,7 +276,7 @@ function ChatPanelRenderer( - + Indexed repository of current workspace:

{indexedRepository.gitUrl}

From 55d58ab902c77a86fac32e55d596db9976ff7517 Mon Sep 17 00:00:00 2001 From: liangfung Date: Thu, 12 Dec 2024 18:21:09 +0700 Subject: [PATCH 03/39] update: sync git url --- clients/tabby-chat-panel/src/index.ts | 2 ++ clients/vscode/src/chat/WebviewHelper.ts | 27 +++++++++++++++++ ee/tabby-ui/app/chat/page.tsx | 28 +++++++++--------- ee/tabby-ui/components/chat/chat-panel.tsx | 5 +++- ee/tabby-ui/components/chat/chat.tsx | 34 +++++++++++++++++++--- 5 files changed, 76 insertions(+), 20 deletions(-) diff --git a/clients/tabby-chat-panel/src/index.ts b/clients/tabby-chat-panel/src/index.ts index da6a46aa0512..98234d9dd3b5 100644 --- a/clients/tabby-chat-panel/src/index.ts +++ b/clients/tabby-chat-panel/src/index.ts @@ -185,6 +185,7 @@ export interface ServerApi { addRelevantContext: (context: Context) => void updateTheme: (style: string, themeClass: string) => void updateActiveSelection: (context: Context | null) => void + updateGitUrl: (gitUrl: string | undefined) => void } export interface ClientApiMethods { @@ -272,6 +273,7 @@ export function createServer(api: ServerApi): ClientApi { addRelevantContext: api.addRelevantContext, updateTheme: api.updateTheme, updateActiveSelection: api.updateActiveSelection, + updateGitUrl: api.updateGitUrl, }, }) } diff --git a/clients/vscode/src/chat/WebviewHelper.ts b/clients/vscode/src/chat/WebviewHelper.ts index bb514c66a4ce..01d3745ba28a 100644 --- a/clients/vscode/src/chat/WebviewHelper.ts +++ b/clients/vscode/src/chat/WebviewHelper.ts @@ -287,6 +287,21 @@ export class WebviewHelper { } } + public async syncIndexedGitUrlToChatPanel(url: string | undefined) { + try { + this.logger.log('sync indexed git url', url) + await this.client?.updateGitUrl(url); + } catch { + this.logger.log( + { + every: 100, + level: "warn", + }, + "Git URL sync failed. Please update your Tabby server to the latest version.", + ); + } + } + public addRelevantContext(context: Context) { if (!this.client) { this.pendingRelevantContexts.push(context); @@ -318,6 +333,7 @@ export class WebviewHelper { this.pendingMessages = []; this.syncActiveSelection(window.activeTextEditor); + this.syncIndexedGitURL(window.activeTextEditor); const agentConfig = this.lspClient.agentConfig.current; if (agentConfig?.server.token) { @@ -371,6 +387,16 @@ export class WebviewHelper { this.syncActiveSelectionToChatPanel(fileContext); } + public async syncIndexedGitURL(editor: TextEditor | undefined) { + if (!editor) { + this.syncIndexedGitUrlToChatPanel(undefined); + return; + } + + const filePathParams = await buildFilePathParams(editor.document.uri, this.gitProvider); + this.syncIndexedGitUrlToChatPanel(filePathParams?.gitRemoteUrl) + } + public addAgentEventListeners() { this.lspClient.status.on("didChange", async (status: StatusInfo) => { const agentConfig = this.lspClient.agentConfig.current; @@ -386,6 +412,7 @@ export class WebviewHelper { public addTextEditorEventListeners() { window.onDidChangeActiveTextEditor((e) => { this.syncActiveSelection(e); + this.syncIndexedGitURL(e); }); window.onDidChangeTextEditorSelection((e) => { diff --git a/ee/tabby-ui/app/chat/page.tsx b/ee/tabby-ui/app/chat/page.tsx index 62cb048d5f22..468d98daedef 100644 --- a/ee/tabby-ui/app/chat/page.tsx +++ b/ee/tabby-ui/app/chat/page.tsx @@ -28,9 +28,6 @@ import { MemoizedReactMarkdown } from '@/components/markdown' import './page.css' -import { useQuery } from 'urql' - -import { resolveGitUrlQuery } from '@/lib/tabby/query' import { saveFetcherOptions } from '@/lib/tabby/token-management' const convertToHSLColor = (style: string) => { @@ -60,6 +57,7 @@ export default function ChatPage() { >([]) const [pendingActiveSelection, setPendingActiveSelection] = useState(null) + const [pendingGitUrl, setPendingGitUrl] = useState() const [errorMessage, setErrorMessage] = useState(null) const [isRefreshLoading, setIsRefreshLoading] = useState(false) @@ -107,6 +105,14 @@ export default function ChatPage() { } } + const updateGitUrl = (gitUrl: string | undefined) => { + if (chatRef.current) { + chatRef.current.updateGitUrl(gitUrl) + } else if (gitUrl) { + setPendingGitUrl(gitUrl) + } + } + const server = useServer({ init: (request: InitRequest) => { if (chatRef.current) return @@ -154,18 +160,8 @@ export default function ChatPage() { document.documentElement.className = themeClass + ` client client-${client}` }, - updateActiveSelection - }) - - // FIXME get url from workspace - const workspaceGitURL = 'https://github.com/tabbyML/tabby' - - const [{ data, fetching }] = useQuery({ - query: resolveGitUrlQuery, - variables: { - gitUrl: workspaceGitURL - }, - pause: !workspaceGitURL + updateActiveSelection, + updateGitUrl }) useEffect(() => { @@ -276,12 +272,14 @@ export default function ChatPage() { setPendingRelevantContexts([]) setPendingMessages([]) setPendingActiveSelection(null) + setPendingGitUrl(undefined) } const onChatLoaded = () => { pendingRelevantContexts.forEach(addRelevantContext) pendingMessages.forEach(sendMessage) chatRef.current?.updateActiveSelection(pendingActiveSelection) + chatRef.current?.updateGitUrl(pendingGitUrl) clearPendingState() setChatLoaded(true) diff --git a/ee/tabby-ui/components/chat/chat-panel.tsx b/ee/tabby-ui/components/chat/chat-panel.tsx index 07f71b117ef1..6f20e34413bc 100644 --- a/ee/tabby-ui/components/chat/chat-panel.tsx +++ b/ee/tabby-ui/components/chat/chat-panel.tsx @@ -23,6 +23,8 @@ import { IconCheck, IconEye, IconEyeOff, + IconFileSearch2, + IconFileText, IconFolderGit, IconRefresh, IconRemove, @@ -275,7 +277,7 @@ function ChatPanelRenderer( - + Indexed repository of current workspace: @@ -303,6 +305,7 @@ function ChatPanelRenderer( } )} > + void focus: () => void updateActiveSelection: (context: Context | null) => void + updateGitUrl: (gitUrl: string | undefined) => void } interface ChatProps extends React.ComponentProps<'div'> { @@ -127,8 +130,7 @@ function ChatRenderer( onLookupSymbol, openInEditor, chatInputRef, - supportsOnApplyInEditorV2, - indexedRepository + supportsOnApplyInEditorV2 }: ChatProps, ref: React.ForwardedRef ) { @@ -141,11 +143,24 @@ function ChatRenderer( const [activeSelection, setActiveSelection] = React.useState( null ) + const [gitUrl, setGitUrl] = React.useState() const enableActiveSelection = useChatStore( state => state.enableActiveSelection ) + const enableIndexedRepository = useChatStore( + state => state.enableIndexedRepository + ) + const chatPanelRef = React.useRef(null) + const [{ data }] = useQuery({ + query: resolveGitUrlQuery, + variables: { + gitUrl: gitUrl as string + }, + pause: !gitUrl + }) + const { sendUserMessage, isLoading, @@ -510,9 +525,19 @@ function ChatRenderer( 300 ) + const debouncedUpdateGitUrl = useDebounceCallback( + (url: string | undefined) => { + setGitUrl(url) + }, + 300 + ) + const updateActiveSelection = (ctx: Context | null) => { debouncedUpdateActiveSelection.run(ctx) } + const updateGitUrl = (gitUrl: string | undefined) => { + debouncedUpdateGitUrl.run(gitUrl) + } React.useImperativeHandle( ref, @@ -523,7 +548,8 @@ function ChatRenderer( isLoading, addRelevantContext, focus: () => chatPanelRef.current?.focus(), - updateActiveSelection + updateActiveSelection, + updateGitUrl } }, [] @@ -560,7 +586,7 @@ function ChatRenderer( chatInputRef, activeSelection, supportsOnApplyInEditorV2, - indexedRepository + indexedRepository: gitUrl ? data?.resolveGitUrl : undefined }} >
From 1deab14b48c5b5d7a41dd77c812e6b055cecdd6d Mon Sep 17 00:00:00 2001 From: liangfung Date: Thu, 12 Dec 2024 18:29:59 +0700 Subject: [PATCH 04/39] update: lint --- ee/tabby-ui/components/chat/chat-panel.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/ee/tabby-ui/components/chat/chat-panel.tsx b/ee/tabby-ui/components/chat/chat-panel.tsx index 6f20e34413bc..aa6a80b00002 100644 --- a/ee/tabby-ui/components/chat/chat-panel.tsx +++ b/ee/tabby-ui/components/chat/chat-panel.tsx @@ -23,7 +23,6 @@ import { IconCheck, IconEye, IconEyeOff, - IconFileSearch2, IconFileText, IconFolderGit, IconRefresh, From 9671198087109319ae105d46fcf3ec089181ed37 Mon Sep 17 00:00:00 2001 From: liangfung Date: Thu, 12 Dec 2024 18:38:30 +0700 Subject: [PATCH 05/39] update: lint --- clients/vscode/src/chat/WebviewHelper.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clients/vscode/src/chat/WebviewHelper.ts b/clients/vscode/src/chat/WebviewHelper.ts index 01d3745ba28a..d0801722cb44 100644 --- a/clients/vscode/src/chat/WebviewHelper.ts +++ b/clients/vscode/src/chat/WebviewHelper.ts @@ -289,7 +289,7 @@ export class WebviewHelper { public async syncIndexedGitUrlToChatPanel(url: string | undefined) { try { - this.logger.log('sync indexed git url', url) + this.logger.log("sync indexed git url", url); await this.client?.updateGitUrl(url); } catch { this.logger.log( @@ -394,7 +394,7 @@ export class WebviewHelper { } const filePathParams = await buildFilePathParams(editor.document.uri, this.gitProvider); - this.syncIndexedGitUrlToChatPanel(filePathParams?.gitRemoteUrl) + this.syncIndexedGitUrlToChatPanel(filePathParams?.gitRemoteUrl); } public addAgentEventListeners() { From 81cdbf43b9736e3b84ce75c5bbe71b181709257a Mon Sep 17 00:00:00 2001 From: liangfung Date: Thu, 12 Dec 2024 18:44:04 +0700 Subject: [PATCH 06/39] update: animation --- ee/tabby-ui/components/chat/chat-panel.tsx | 42 +++++++++++----------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/ee/tabby-ui/components/chat/chat-panel.tsx b/ee/tabby-ui/components/chat/chat-panel.tsx index aa6a80b00002..5ba006fadec5 100644 --- a/ee/tabby-ui/components/chat/chat-panel.tsx +++ b/ee/tabby-ui/components/chat/chat-panel.tsx @@ -231,17 +231,18 @@ function ChatPanelRenderer(
{indexedRepository ? ( - - - + + + - - - - - Indexed repository of current workspace: - -

{indexedRepository.gitUrl}

-
-
+ + + + Indexed repository of current workspace: + +

{indexedRepository.gitUrl}

+
+ + ) : null} {activeSelection ? ( Date: Sun, 15 Dec 2024 19:52:08 +0700 Subject: [PATCH 07/39] update --- clients/vscode/src/chat/WebviewHelper.ts | 2 +- ee/tabby-ui/app/chat/page.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/clients/vscode/src/chat/WebviewHelper.ts b/clients/vscode/src/chat/WebviewHelper.ts index d0801722cb44..0c624fbf98a5 100644 --- a/clients/vscode/src/chat/WebviewHelper.ts +++ b/clients/vscode/src/chat/WebviewHelper.ts @@ -33,7 +33,7 @@ import { GitProvider } from "../git/GitProvider"; import { createClient } from "./chatPanel"; import { Client as LspClient } from "../lsp/Client"; import { isBrowser } from "../env"; -import { getFileContextFromSelection, showFileContext, openTextDocument } from "./fileContext"; +import { getFileContextFromSelection, showFileContext, openTextDocument, buildFilePathParams } from "./fileContext"; import { localUriToChatPanelFilepath, chatPanelFilepathToLocalUri, diff --git a/ee/tabby-ui/app/chat/page.tsx b/ee/tabby-ui/app/chat/page.tsx index 468d98daedef..0c537de99b07 100644 --- a/ee/tabby-ui/app/chat/page.tsx +++ b/ee/tabby-ui/app/chat/page.tsx @@ -407,7 +407,6 @@ export default function ChatPage() { (supportsOnLookupSymbol ? server?.lookupSymbol : undefined) } openInEditor={isInEditor && server?.openInEditor} - indexedRepository={data?.resolveGitUrl} /> ) From 12c9a894460fdc3dd5fd6af9405ac403c60735fc Mon Sep 17 00:00:00 2001 From: liangfung Date: Sat, 14 Dec 2024 23:40:49 +0700 Subject: [PATCH 08/39] feat(ui): add repo select in chat sidebar --- ee/tabby-ui/components/chat/prompt-form.tsx | 14 ++- ee/tabby-ui/components/chat/repo-select.tsx | 97 +++++++++++++++++++++ 2 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 ee/tabby-ui/components/chat/repo-select.tsx diff --git a/ee/tabby-ui/components/chat/prompt-form.tsx b/ee/tabby-ui/components/chat/prompt-form.tsx index abe51405a278..184dffe279a4 100644 --- a/ee/tabby-ui/components/chat/prompt-form.tsx +++ b/ee/tabby-ui/components/chat/prompt-form.tsx @@ -26,6 +26,7 @@ import { SearchableSelectOption, SearchableSelectTextarea } from '@/components/searchable-select' +import { RepoSelect } from './repo-select' export interface PromptProps extends Pick { @@ -198,21 +199,21 @@ function PromptFormRenderer( return ( <> -
- + {/* - + */} { @@ -304,6 +305,11 @@ function PromptFormRenderer( ) }} + {}} + models={['1', '2']} + /> ) } diff --git a/ee/tabby-ui/components/chat/repo-select.tsx b/ee/tabby-ui/components/chat/repo-select.tsx new file mode 100644 index 000000000000..1ea63b5ddadb --- /dev/null +++ b/ee/tabby-ui/components/chat/repo-select.tsx @@ -0,0 +1,97 @@ +import { Maybe } from '@/lib/gql/generates/graphql' +import { cn } from '@/lib/utils' +import { Badge } from '@/components/ui/badge' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuTrigger +} from '@/components/ui/dropdown-menu' +import { IconCheck, IconFolderGit } from '@/components/ui/icons' +import { Skeleton } from '@/components/ui/skeleton' +import LoadingWrapper from '@/components/loading-wrapper' + +interface RepoSelectProps { + // todo rename + models: Maybe> | undefined + value: string | undefined + onChange: (v: string) => void + isInitializing?: boolean + // sourceId + workspaceRepoId?: string +} + +export function RepoSelect({ + models, + value, + onChange, + isInitializing +}: RepoSelectProps) { + const onModelSelect = (v: string) => { + onChange(v) + } + + return ( + + +
+ } + > + {!!models?.length && ( + + + + + {/* FIXME */} + {/* {value} */} + TabbyML/tabby + + + + + {models.map(model => { + const isSelected = model === value + return ( + { + onModelSelect(model) + e.stopPropagation() + }} + value={model} + key={model} + className="cursor-pointer py-2 pl-3" + > + + + {model} + + + ) + })} + + + + )} + + ) +} From 33da2ba4da9ea4c180c8b28a7066192d04d85ce1 Mon Sep 17 00:00:00 2001 From: liangfung Date: Sun, 15 Dec 2024 23:55:34 +0700 Subject: [PATCH 09/39] update --- ee/tabby-schema/src/schema/mod.rs | 2 +- ee/tabby-ui/components/chat/chat-panel.tsx | 24 ++-- ee/tabby-ui/components/chat/chat.tsx | 36 ++++- ee/tabby-ui/components/chat/prompt-form.tsx | 12 +- ee/tabby-ui/components/chat/repo-select.tsx | 145 +++++++++++++------- 5 files changed, 148 insertions(+), 71 deletions(-) diff --git a/ee/tabby-schema/src/schema/mod.rs b/ee/tabby-schema/src/schema/mod.rs index ffef5bdc5ee9..d0f81ad41c76 100644 --- a/ee/tabby-schema/src/schema/mod.rs +++ b/ee/tabby-schema/src/schema/mod.rs @@ -550,7 +550,7 @@ impl Query { } async fn context_info(ctx: &Context) -> Result { - let user = check_user(ctx).await?; + let user = check_user_allow_auth_token(ctx).await?; ctx.locator.context().read(Some(&user.policy)).await } diff --git a/ee/tabby-ui/components/chat/chat-panel.tsx b/ee/tabby-ui/components/chat/chat-panel.tsx index 5ba006fadec5..be87f39a909b 100644 --- a/ee/tabby-ui/components/chat/chat-panel.tsx +++ b/ee/tabby-ui/components/chat/chat-panel.tsx @@ -9,10 +9,7 @@ import type { Context } from 'tabby-chat-panel' import { SLUG_TITLE_MAX_LENGTH } from '@/lib/constants' import { useCopyToClipboard } from '@/lib/hooks/use-copy-to-clipboard' -import { - updateEnableActiveSelection, - updateEnableIndexedRepository -} from '@/lib/stores/chat-actions' +import { updateEnableActiveSelection } from '@/lib/stores/chat-actions' import { useChatStore } from '@/lib/stores/chat-store' import { useMutation } from '@/lib/tabby/gql' import { setThreadPersistedMutation } from '@/lib/tabby/query' @@ -24,7 +21,6 @@ import { IconEye, IconEyeOff, IconFileText, - IconFolderGit, IconRefresh, IconRemove, IconShare, @@ -37,6 +33,7 @@ import { FooterText } from '@/components/footer' import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip' import { ChatContext } from './chat' +import { RepoSelect } from './repo-select' export interface ChatPanelProps extends Pick { @@ -76,7 +73,11 @@ function ChatPanelRenderer( removeRelevantContext, activeSelection, onCopyContent, - indexedRepository + indexedRepository, + selectedRepoId, + setSelectedRepoId, + repos, + fetchingRepos } = React.useContext(ChatContext) const enableActiveSelection = useChatStore( state => state.enableActiveSelection @@ -230,7 +231,14 @@ function ChatPanelRenderer(
- {indexedRepository ? ( + + {/* {indexedRepository ? ( - ) : null} + ) : null} */} {activeSelection ? ( supportsOnApplyInEditorV2: boolean indexedRepository: ResolveGitUrlQuery['resolveGitUrl'] + selectedRepoId: string | null + setSelectedRepoId: React.Dispatch> + repos: ContextInfo['sources'] + fetchingRepos: boolean } export const ChatContext = React.createContext( @@ -143,7 +149,13 @@ function ChatRenderer( const [activeSelection, setActiveSelection] = React.useState( null ) + // gitUrl from workspace const [gitUrl, setGitUrl] = React.useState() + // sourceId + const [selectedRepoId, setSelectedRepoId] = React.useState( + null + ) + const enableActiveSelection = useChatStore( state => state.enableActiveSelection ) @@ -152,8 +164,22 @@ function ChatRenderer( ) const chatPanelRef = React.useRef(null) + const [{ data: contextInfoData, fetching: fetchingSources }] = useQuery({ + query: contextInfoQuery + }) + const repos = React.useMemo(() => { + return ( + contextInfoData?.contextInfo?.sources.filter(source => { + return [ + ContextSourceKind.Git, + ContextSourceKind.Github, + ContextSourceKind.Gitlab + ].includes(source.sourceKind) + }) ?? [] + ) + }, [contextInfoData]) - const [{ data }] = useQuery({ + const [{ data: resolvedGitUrl }] = useQuery({ query: resolveGitUrlQuery, variables: { gitUrl: gitUrl as string @@ -586,7 +612,11 @@ function ChatRenderer( chatInputRef, activeSelection, supportsOnApplyInEditorV2, - indexedRepository: gitUrl ? data?.resolveGitUrl : undefined + indexedRepository: gitUrl ? resolvedGitUrl?.resolveGitUrl : undefined, + selectedRepoId, + setSelectedRepoId, + repos, + fetchingRepos: fetchingSources }} >
diff --git a/ee/tabby-ui/components/chat/prompt-form.tsx b/ee/tabby-ui/components/chat/prompt-form.tsx index 184dffe279a4..c8916baf9f23 100644 --- a/ee/tabby-ui/components/chat/prompt-form.tsx +++ b/ee/tabby-ui/components/chat/prompt-form.tsx @@ -26,7 +26,6 @@ import { SearchableSelectOption, SearchableSelectTextarea } from '@/components/searchable-select' -import { RepoSelect } from './repo-select' export interface PromptProps extends Pick { @@ -199,15 +198,15 @@ function PromptFormRenderer( return ( <> -
- {/* + - */} + - {}} - models={['1', '2']} - /> ) } diff --git a/ee/tabby-ui/components/chat/repo-select.tsx b/ee/tabby-ui/components/chat/repo-select.tsx index 1ea63b5ddadb..8d805af47fc3 100644 --- a/ee/tabby-ui/components/chat/repo-select.tsx +++ b/ee/tabby-ui/components/chat/repo-select.tsx @@ -1,6 +1,5 @@ -import { Maybe } from '@/lib/gql/generates/graphql' +import { ContextInfo } from '@/lib/gql/generates/graphql' import { cn } from '@/lib/utils' -import { Badge } from '@/components/ui/badge' import { DropdownMenu, DropdownMenuContent, @@ -8,30 +7,44 @@ import { DropdownMenuRadioItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu' -import { IconCheck, IconFolderGit } from '@/components/ui/icons' +import { + IconCheck, + IconChevronUpDown, + IconFolderGit, + IconRemove +} from '@/components/ui/icons' import { Skeleton } from '@/components/ui/skeleton' import LoadingWrapper from '@/components/loading-wrapper' +import { Badge } from '../ui/badge' +import { Button } from '../ui/button' + interface RepoSelectProps { - // todo rename - models: Maybe> | undefined - value: string | undefined - onChange: (v: string) => void + repos: ContextInfo['sources'] + value: string | null | undefined + onChange: (v: string | null) => void isInitializing?: boolean // sourceId workspaceRepoId?: string } export function RepoSelect({ - models, + repos, value, onChange, - isInitializing + isInitializing, + workspaceRepoId }: RepoSelectProps) { - const onModelSelect = (v: string) => { + const onSelectRepo = (v: string) => { onChange(v) + // todo input focus } + const isWorkspaceRepo = !!workspaceRepoId && workspaceRepoId === value + const selectedRepoName = repos?.find( + repo => repo.sourceId === value + )?.sourceName + return ( } > - {!!models?.length && ( - - - - - {/* FIXME */} - {/* {value} */} - TabbyML/tabby - + + + +
+ +
+ + {selectedRepoName || 'Select repo...'} + + {isWorkspaceRepo && ( + + Repo in workspace + + )} +
+ {!value && } +
- - - {models.map(model => { - const isSelected = model === value - return ( - { - onModelSelect(model) - e.stopPropagation() - }} - value={model} - key={model} - className="cursor-pointer py-2 pl-3" - > + {!!value && ( + + )} +
+ + + {repos.map(repo => { + const isSelected = repo.sourceId === value + return ( + onSelectRepo(repo.sourceId)} + > +
- {model} + {repo.sourceName} + +
+ {repo.sourceId === workspaceRepoId && ( + + Repo in workspace -
- ) - })} -
-
-
- )} + )} + + ) + })} + + +
) } From 1fce9824dc68df14843fdd801de640a3176590c9 Mon Sep 17 00:00:00 2001 From: liangfung Date: Tue, 17 Dec 2024 01:17:21 +0700 Subject: [PATCH 10/39] update: set default repo --- clients/vscode/src/chat/WebviewHelper.ts | 19 +++---- clients/vscode/src/chat/fileContext.ts | 12 +++++ ee/tabby-ui/components/chat/chat-panel.tsx | 58 --------------------- ee/tabby-ui/components/chat/chat.tsx | 20 +++++-- ee/tabby-ui/components/chat/repo-select.tsx | 18 ++++--- ee/tabby-ui/lib/stores/chat-actions.ts | 4 -- ee/tabby-ui/lib/stores/chat-store.ts | 4 +- 7 files changed, 48 insertions(+), 87 deletions(-) diff --git a/clients/vscode/src/chat/WebviewHelper.ts b/clients/vscode/src/chat/WebviewHelper.ts index 0c624fbf98a5..a04bc227a183 100644 --- a/clients/vscode/src/chat/WebviewHelper.ts +++ b/clients/vscode/src/chat/WebviewHelper.ts @@ -33,7 +33,7 @@ import { GitProvider } from "../git/GitProvider"; import { createClient } from "./chatPanel"; import { Client as LspClient } from "../lsp/Client"; import { isBrowser } from "../env"; -import { getFileContextFromSelection, showFileContext, openTextDocument, buildFilePathParams } from "./fileContext"; +import { getFileContextFromSelection, showFileContext, openTextDocument, getGitRemoteUrl } from "./fileContext"; import { localUriToChatPanelFilepath, chatPanelFilepathToLocalUri, @@ -287,7 +287,7 @@ export class WebviewHelper { } } - public async syncIndexedGitUrlToChatPanel(url: string | undefined) { + public async syncWorkspaceGitUrlToChatPanel(url: string | undefined) { try { this.logger.log("sync indexed git url", url); await this.client?.updateGitUrl(url); @@ -333,7 +333,7 @@ export class WebviewHelper { this.pendingMessages = []; this.syncActiveSelection(window.activeTextEditor); - this.syncIndexedGitURL(window.activeTextEditor); + this.syncWorkspaceGitURL(window.activeTextEditor); const agentConfig = this.lspClient.agentConfig.current; if (agentConfig?.server.token) { @@ -387,14 +387,9 @@ export class WebviewHelper { this.syncActiveSelectionToChatPanel(fileContext); } - public async syncIndexedGitURL(editor: TextEditor | undefined) { - if (!editor) { - this.syncIndexedGitUrlToChatPanel(undefined); - return; - } - - const filePathParams = await buildFilePathParams(editor.document.uri, this.gitProvider); - this.syncIndexedGitUrlToChatPanel(filePathParams?.gitRemoteUrl); + public async syncWorkspaceGitURL(editor: TextEditor | undefined) { + const gitRemoteUrl = await getGitRemoteUrl(editor?.document.uri, this.gitProvider); + this.syncWorkspaceGitUrlToChatPanel(gitRemoteUrl); } public addAgentEventListeners() { @@ -412,7 +407,7 @@ export class WebviewHelper { public addTextEditorEventListeners() { window.onDidChangeActiveTextEditor((e) => { this.syncActiveSelection(e); - this.syncIndexedGitURL(e); + this.syncWorkspaceGitURL(e); }); window.onDidChangeTextEditorSelection((e) => { diff --git a/clients/vscode/src/chat/fileContext.ts b/clients/vscode/src/chat/fileContext.ts index 8cf6dbb50fce..cc9e3e350691 100644 --- a/clients/vscode/src/chat/fileContext.ts +++ b/clients/vscode/src/chat/fileContext.ts @@ -81,6 +81,18 @@ export async function showFileContext(fileContext: FileContext, gitProvider: Git editor.revealRange(new Range(start, end), TextEditorRevealType.InCenter); } +export async function getGitRemoteUrl(fileUri: Uri | undefined, gitProvider: GitProvider): Promise { + if (fileUri) { + const pathParams = await buildFilePathParams(fileUri, gitProvider) + return pathParams.gitRemoteUrl + } + + const workspaceFolder = workspace.workspaceFolders?.[0]; + if (!workspaceFolder) return undefined; + const repo = gitProvider.getRepository(workspaceFolder.uri); + return repo ? gitProvider.getDefaultRemoteUrl(repo) : undefined; +} + export async function buildFilePathParams(uri: Uri, gitProvider: GitProvider): Promise { const workspaceFolder = workspace.getWorkspaceFolder(uri) ?? (uri.scheme === "untitled" ? workspace.workspaceFolders?.[0] : undefined); diff --git a/ee/tabby-ui/components/chat/chat-panel.tsx b/ee/tabby-ui/components/chat/chat-panel.tsx index be87f39a909b..e644eaf80d4e 100644 --- a/ee/tabby-ui/components/chat/chat-panel.tsx +++ b/ee/tabby-ui/components/chat/chat-panel.tsx @@ -82,9 +82,6 @@ function ChatPanelRenderer( const enableActiveSelection = useChatStore( state => state.enableActiveSelection ) - const enableIndexedRepository = useChatStore( - state => state.enableIndexedRepository - ) const [persisting, setPerisiting] = useState(false) const { width } = useWindowSize() @@ -238,61 +235,6 @@ function ChatPanelRenderer( workspaceRepoId={indexedRepository?.sourceId} isInitializing={fetchingRepos} /> - {/* {indexedRepository ? ( - - - - - - {indexedRepository.name} - - Current repo - - - - - - - Indexed repository of current workspace: - -

{indexedRepository.gitUrl}

-
-
-
- ) : null} */} {activeSelection ? ( state.enableActiveSelection ) - const enableIndexedRepository = useChatStore( - state => state.enableIndexedRepository - ) const chatPanelRef = React.useRef(null) const [{ data: contextInfoData, fetching: fetchingSources }] = useQuery({ @@ -179,6 +176,7 @@ function ChatRenderer( ) }, [contextInfoData]) + // todo remove this const [{ data: resolvedGitUrl }] = useQuery({ query: resolveGitUrlQuery, variables: { @@ -199,6 +197,22 @@ function ChatRenderer( threadId }) + React.useEffect(() => { + const setDefaultRepoId = () => { + if (!resolvedGitUrl || !repos?.length) return + if (selectedRepoId) return + + const defaultRepo = repos.find( + o => o.sourceId === resolvedGitUrl.resolveGitUrl?.sourceId + ) + if (defaultRepo) { + setSelectedRepoId(defaultRepo.sourceId) + } + } + + setDefaultRepoId() + }, [resolvedGitUrl, repos]) + const onDeleteMessage = async (userMessageId: string) => { if (!threadId) return diff --git a/ee/tabby-ui/components/chat/repo-select.tsx b/ee/tabby-ui/components/chat/repo-select.tsx index 8d805af47fc3..bbd39b4c6099 100644 --- a/ee/tabby-ui/components/chat/repo-select.tsx +++ b/ee/tabby-ui/components/chat/repo-select.tsx @@ -1,3 +1,5 @@ +import { useWindowSize } from '@uidotdev/usehooks' + import { ContextInfo } from '@/lib/gql/generates/graphql' import { cn } from '@/lib/utils' import { @@ -35,6 +37,9 @@ export function RepoSelect({ isInitializing, workspaceRepoId }: RepoSelectProps) { + const { width } = useWindowSize() + const isExtraSmallScreen = typeof width === 'number' && width < 240 + const onSelectRepo = (v: string) => { onChange(v) // todo input focus @@ -57,14 +62,14 @@ export function RepoSelect({ -
+
-
+
@@ -72,7 +77,7 @@ export function RepoSelect({ {isWorkspaceRepo && ( - Repo in workspace + {isExtraSmallScreen ? 'Workspace' : 'Repo in workspace'} )}
@@ -86,7 +91,6 @@ export function RepoSelect({ variant="ghost" className="h-7 w-7 shrink-0 rounded-l-none" onClick={e => { - console.log('eeeeeeee') e.preventDefault() e.stopPropagation() onChange(null) @@ -128,7 +132,7 @@ export function RepoSelect({
{repo.sourceId === workspaceRepoId && ( - Repo in workspace + {isExtraSmallScreen ? 'Workspace' : 'Repo in workspace'} )} diff --git a/ee/tabby-ui/lib/stores/chat-actions.ts b/ee/tabby-ui/lib/stores/chat-actions.ts index 52349cbb624c..eb88174aad17 100644 --- a/ee/tabby-ui/lib/stores/chat-actions.ts +++ b/ee/tabby-ui/lib/stores/chat-actions.ts @@ -13,7 +13,3 @@ export const updateSelectedModel = (model: string | undefined) => { export const updateEnableActiveSelection = (enable: boolean) => { set(() => ({ enableActiveSelection: enable })) } - -export const updateEnableIndexedRepository = (enable: boolean) => { - set(() => ({ enableIndexedRepository: enable })) -} diff --git a/ee/tabby-ui/lib/stores/chat-store.ts b/ee/tabby-ui/lib/stores/chat-store.ts index 6d0bd7674bac..1c09b032c6b7 100644 --- a/ee/tabby-ui/lib/stores/chat-store.ts +++ b/ee/tabby-ui/lib/stores/chat-store.ts @@ -9,14 +9,12 @@ export interface ChatState { activeChatId: string | undefined selectedModel: string | undefined enableActiveSelection: boolean - enableIndexedRepository: boolean } const initialState: ChatState = { activeChatId: nanoid(), selectedModel: undefined, - enableActiveSelection: true, - enableIndexedRepository: true + enableActiveSelection: true } export const useChatStore = create()( From 05214936529c16d90a047d2cdf036409ee510a5f Mon Sep 17 00:00:00 2001 From: liangfung Date: Tue, 17 Dec 2024 10:18:24 +0700 Subject: [PATCH 11/39] update: code_query --- ee/tabby-ui/components/chat/chat.tsx | 37 ++++++---------------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/ee/tabby-ui/components/chat/chat.tsx b/ee/tabby-ui/components/chat/chat.tsx index 943f347a25f1..4780232c3605 100644 --- a/ee/tabby-ui/components/chat/chat.tsx +++ b/ee/tabby-ui/components/chat/chat.tsx @@ -254,8 +254,7 @@ function ChatRenderer( ] setQaPairs(nextQaPairs) const [userMessage, threadRunOptions] = generateRequestPayload( - qaPair.user, - enableActiveSelection + qaPair.user ) return regenerate({ @@ -412,30 +411,13 @@ function ChatRenderer( }, [error]) const generateRequestPayload = ( - userMessage: UserMessage, - enableActiveSelection?: boolean + userMessage: UserMessage ): [CreateMessageInput, ThreadRunOptionsInput] => { - // use selectContext for code query by default - let contextForCodeQuery: FileContext | undefined = userMessage.selectContext - - // if enableActiveSelection, use selectContext or activeContext for code query - if (enableActiveSelection) { - contextForCodeQuery = contextForCodeQuery || userMessage.activeContext - } - - // check context for codeQuery - if (!isValidContextForCodeQuery(contextForCodeQuery)) { - contextForCodeQuery = undefined - } - - const codeQuery: InputMaybe = contextForCodeQuery + const content = userMessage.message + const codeQuery: InputMaybe = selectedRepoId ? { - content: contextForCodeQuery.content ?? '', - filepath: contextForCodeQuery.filepath, - language: contextForCodeQuery.filepath - ? filename2prism(contextForCodeQuery.filepath)[0] || 'plaintext' - : 'plaintext', - gitUrl: contextForCodeQuery?.git_url ?? '' + content, + sourceId: selectedRepoId } : null @@ -443,6 +425,7 @@ function ChatRenderer( enableActiveSelection && !!userMessage.activeContext const clientSideFileContexts: FileContext[] = uniqWith( compact([ + userMessage.selectContext, hasUsableActiveContext && userMessage.activeContext, ...(userMessage?.relevantContext || []) ]), @@ -456,8 +439,6 @@ function ChatRenderer( startLine: o.range.start })) - const content = userMessage.message - return [ { content, @@ -517,9 +498,7 @@ function ChatRenderer( setQaPairs(nextQaPairs) - sendUserMessage( - ...generateRequestPayload(newUserMessage, enableActiveSelection) - ) + sendUserMessage(...generateRequestPayload(newUserMessage)) } ) From 16dc9ff1021160d1e544f36d52270f34961b60fa Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 03:19:40 +0000 Subject: [PATCH 12/39] [autofix.ci] apply automated fixes --- clients/vscode/src/chat/fileContext.ts | 4 ++-- ee/tabby-ui/components/chat/prompt-form.tsx | 2 +- ee/tabby-ui/components/chat/repo-select.tsx | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clients/vscode/src/chat/fileContext.ts b/clients/vscode/src/chat/fileContext.ts index cc9e3e350691..4fdf203808b7 100644 --- a/clients/vscode/src/chat/fileContext.ts +++ b/clients/vscode/src/chat/fileContext.ts @@ -83,8 +83,8 @@ export async function showFileContext(fileContext: FileContext, gitProvider: Git export async function getGitRemoteUrl(fileUri: Uri | undefined, gitProvider: GitProvider): Promise { if (fileUri) { - const pathParams = await buildFilePathParams(fileUri, gitProvider) - return pathParams.gitRemoteUrl + const pathParams = await buildFilePathParams(fileUri, gitProvider); + return pathParams.gitRemoteUrl; } const workspaceFolder = workspace.workspaceFolders?.[0]; diff --git a/ee/tabby-ui/components/chat/prompt-form.tsx b/ee/tabby-ui/components/chat/prompt-form.tsx index c8916baf9f23..eb93fd553437 100644 --- a/ee/tabby-ui/components/chat/prompt-form.tsx +++ b/ee/tabby-ui/components/chat/prompt-form.tsx @@ -212,7 +212,7 @@ function PromptFormRenderer( rows={1} placeholder="Ask a question." spellCheck={false} - className="min-h-[60px] w-full resize-none bg-transparent pr-4 sm:pl-4 py-[1.3rem] focus-within:outline-none" + className="min-h-[60px] w-full resize-none bg-transparent py-[1.3rem] pr-4 focus-within:outline-none sm:pl-4" value={input} ref={chatInputRef} onChange={e => { diff --git a/ee/tabby-ui/components/chat/repo-select.tsx b/ee/tabby-ui/components/chat/repo-select.tsx index bbd39b4c6099..41d661bd9c23 100644 --- a/ee/tabby-ui/components/chat/repo-select.tsx +++ b/ee/tabby-ui/components/chat/repo-select.tsx @@ -62,12 +62,12 @@ export function RepoSelect({ -
+
-
+
onSelectRepo(repo.sourceId)} > -
+
{repo.sourceId === workspaceRepoId && ( - + {isExtraSmallScreen ? 'Workspace' : 'Repo in workspace'} )} From 4f7751b259ffc7e87f454256c38fdcc8c2601355 Mon Sep 17 00:00:00 2001 From: liangfung Date: Tue, 17 Dec 2024 11:31:25 +0700 Subject: [PATCH 13/39] update: focus --- ee/tabby-ui/components/chat/chat-panel.tsx | 10 +++++++++- ee/tabby-ui/components/chat/repo-select.tsx | 1 - 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ee/tabby-ui/components/chat/chat-panel.tsx b/ee/tabby-ui/components/chat/chat-panel.tsx index e644eaf80d4e..8e373ec26ce1 100644 --- a/ee/tabby-ui/components/chat/chat-panel.tsx +++ b/ee/tabby-ui/components/chat/chat-panel.tsx @@ -132,6 +132,14 @@ function ChatPanelRenderer( } } + const onSelectRepo = (sourceId: string | null) => { + setSelectedRepoId(sourceId) + + setTimeout(() => { + chatInputRef.current?.focus() + }) + } + React.useImperativeHandle( ref, () => { @@ -230,7 +238,7 @@ function ChatPanelRenderer( { onChange(v) - // todo input focus } const isWorkspaceRepo = !!workspaceRepoId && workspaceRepoId === value From 58b82edb196768da88513598836639a317eba56f Mon Sep 17 00:00:00 2001 From: liangfung Date: Tue, 17 Dec 2024 11:36:48 +0700 Subject: [PATCH 14/39] update: type --- ee/tabby-ui/components/chat/chat-panel.tsx | 2 +- ee/tabby-ui/components/chat/chat.tsx | 10 +++++----- ee/tabby-ui/components/chat/repo-select.tsx | 7 +++---- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/ee/tabby-ui/components/chat/chat-panel.tsx b/ee/tabby-ui/components/chat/chat-panel.tsx index 8e373ec26ce1..9afed6890119 100644 --- a/ee/tabby-ui/components/chat/chat-panel.tsx +++ b/ee/tabby-ui/components/chat/chat-panel.tsx @@ -132,7 +132,7 @@ function ChatPanelRenderer( } } - const onSelectRepo = (sourceId: string | null) => { + const onSelectRepo = (sourceId: string | undefined) => { setSelectedRepoId(sourceId) setTimeout(() => { diff --git a/ee/tabby-ui/components/chat/chat.tsx b/ee/tabby-ui/components/chat/chat.tsx index 4780232c3605..da15841f11be 100644 --- a/ee/tabby-ui/components/chat/chat.tsx +++ b/ee/tabby-ui/components/chat/chat.tsx @@ -69,8 +69,8 @@ type ChatContextValue = { chatInputRef: RefObject supportsOnApplyInEditorV2: boolean indexedRepository: ResolveGitUrlQuery['resolveGitUrl'] - selectedRepoId: string | null - setSelectedRepoId: React.Dispatch> + selectedRepoId: string | undefined + setSelectedRepoId: React.Dispatch> repos: ContextInfo['sources'] fetchingRepos: boolean } @@ -152,9 +152,9 @@ function ChatRenderer( // gitUrl from workspace const [gitUrl, setGitUrl] = React.useState() // sourceId - const [selectedRepoId, setSelectedRepoId] = React.useState( - null - ) + const [selectedRepoId, setSelectedRepoId] = React.useState< + string | undefined + >() const enableActiveSelection = useChatStore( state => state.enableActiveSelection diff --git a/ee/tabby-ui/components/chat/repo-select.tsx b/ee/tabby-ui/components/chat/repo-select.tsx index d748119dc419..c98c2b758655 100644 --- a/ee/tabby-ui/components/chat/repo-select.tsx +++ b/ee/tabby-ui/components/chat/repo-select.tsx @@ -23,8 +23,8 @@ import { Button } from '../ui/button' interface RepoSelectProps { repos: ContextInfo['sources'] - value: string | null | undefined - onChange: (v: string | null) => void + value: string | undefined + onChange: (v: string | undefined) => void isInitializing?: boolean // sourceId workspaceRepoId?: string @@ -90,9 +90,8 @@ export function RepoSelect({ variant="ghost" className="h-7 w-7 shrink-0 rounded-l-none" onClick={e => { - e.preventDefault() e.stopPropagation() - onChange(null) + onChange(undefined) }} > From bd34f11ae5fef9a52685aa347b08ceb5cbdd4270 Mon Sep 17 00:00:00 2001 From: Meng Zhang Date: Tue, 17 Dec 2024 13:39:28 +0800 Subject: [PATCH 15/39] Update ee/tabby-ui/components/chat/repo-select.tsx --- ee/tabby-ui/components/chat/repo-select.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/tabby-ui/components/chat/repo-select.tsx b/ee/tabby-ui/components/chat/repo-select.tsx index c98c2b758655..299b7ded42d5 100644 --- a/ee/tabby-ui/components/chat/repo-select.tsx +++ b/ee/tabby-ui/components/chat/repo-select.tsx @@ -76,7 +76,7 @@ export function RepoSelect({ {isWorkspaceRepo && ( - {isExtraSmallScreen ? 'Workspace' : 'Repo in workspace'} + Context )}
From 86d3290ebbb1fa331f2aff72dd4f4bd33264fcc2 Mon Sep 17 00:00:00 2001 From: Meng Zhang Date: Tue, 17 Dec 2024 13:44:33 +0800 Subject: [PATCH 16/39] Apply suggestions from code review --- ee/tabby-ui/components/chat/repo-select.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ee/tabby-ui/components/chat/repo-select.tsx b/ee/tabby-ui/components/chat/repo-select.tsx index 299b7ded42d5..4ebd57ace05e 100644 --- a/ee/tabby-ui/components/chat/repo-select.tsx +++ b/ee/tabby-ui/components/chat/repo-select.tsx @@ -72,13 +72,8 @@ export function RepoSelect({ 'text-muted-foreground': !selectedRepoName })} > - {selectedRepoName || 'Select repo...'} + {selectedRepoName || 'Workspace'}
- {isWorkspaceRepo && ( - - Context - - )}
{!value && }
From 43754198b9fc0e9bc8779dd9b25286067107f1a5 Mon Sep 17 00:00:00 2001 From: liangfung Date: Tue, 17 Dec 2024 11:39:43 +0700 Subject: [PATCH 17/39] update --- ee/tabby-ui/components/chat/repo-select.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ee/tabby-ui/components/chat/repo-select.tsx b/ee/tabby-ui/components/chat/repo-select.tsx index 4ebd57ace05e..689764433384 100644 --- a/ee/tabby-ui/components/chat/repo-select.tsx +++ b/ee/tabby-ui/components/chat/repo-select.tsx @@ -49,6 +49,9 @@ export function RepoSelect({ repo => repo.sourceId === value )?.sourceName + // if there's no repo, hide the repo select + if (!isInitializing || !repos.length) return null + return ( Date: Tue, 17 Dec 2024 13:45:18 +0700 Subject: [PATCH 18/39] update: styling --- ee/tabby-ui/components/chat/repo-select.tsx | 179 +++++++++++++------- ee/tabby-ui/components/source-icon.tsx | 34 ++++ 2 files changed, 153 insertions(+), 60 deletions(-) create mode 100644 ee/tabby-ui/components/source-icon.tsx diff --git a/ee/tabby-ui/components/chat/repo-select.tsx b/ee/tabby-ui/components/chat/repo-select.tsx index 689764433384..bdc7b130ee9d 100644 --- a/ee/tabby-ui/components/chat/repo-select.tsx +++ b/ee/tabby-ui/components/chat/repo-select.tsx @@ -1,25 +1,33 @@ +import { useRef, useState } from 'react' import { useWindowSize } from '@uidotdev/usehooks' import { ContextInfo } from '@/lib/gql/generates/graphql' import { cn } from '@/lib/utils' +import { Badge } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuRadioGroup, - DropdownMenuRadioItem, - DropdownMenuTrigger -} from '@/components/ui/dropdown-menu' + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList +} from '@/components/ui/command' import { IconCheck, IconChevronUpDown, IconFolderGit, IconRemove } from '@/components/ui/icons' +import { + Popover, + PopoverContent, + PopoverTrigger +} from '@/components/ui/popover' import { Skeleton } from '@/components/ui/skeleton' import LoadingWrapper from '@/components/loading-wrapper' -import { Badge } from '../ui/badge' -import { Button } from '../ui/button' +import { SourceIcon } from '../source-icon' interface RepoSelectProps { repos: ContextInfo['sources'] @@ -37,38 +45,67 @@ export function RepoSelect({ isInitializing, workspaceRepoId }: RepoSelectProps) { + const [open, setOpen] = useState(false) + const commandListRef = useRef(null) + const { width } = useWindowSize() - const isExtraSmallScreen = typeof width === 'number' && width < 240 + const isExtraSmallScreen = typeof width === 'number' && width < 360 const onSelectRepo = (v: string) => { onChange(v) } - const isWorkspaceRepo = !!workspaceRepoId && workspaceRepoId === value - const selectedRepoName = repos?.find( - repo => repo.sourceId === value - )?.sourceName + const scrollCommandListToTop = () => { + requestAnimationFrame(() => { + if (commandListRef.current) { + commandListRef.current.scrollTop = 0 + } + }) + } + + const onSearchChange = () => { + scrollCommandListToTop() + } + + const selectedRepo = value + ? repos?.find(repo => repo.sourceId === value) + : undefined + const selectedRepoName = selectedRepo?.sourceName // if there's no repo, hide the repo select - if (!isInitializing || !repos.length) return null + if (!isInitializing && !repos.length) return null return ( - +
} > - + - -
- + +
+ {selectedRepo ? ( + + ) : ( + + )}
- {!value && } + {!value && ( +
+ +
+ )}
- +
{!!value && ( )} - - - {repos.map(repo => { - const isSelected = repo.sourceId === value - return ( - onSelectRepo(repo.sourceId)} - > -
- - + + + No context found + + {repos.map(repo => { + const isSelected = repo.sourceId === value + + return ( + { + onSelectRepo(repo.sourceId) + setOpen(false) + }} + title={repo.sourceName} > - {repo.sourceName} - -
- {repo.sourceId === workspaceRepoId && ( - - {isExtraSmallScreen ? 'Workspace' : 'Repo in workspace'} - - )} -
- ) - })} -
-
- + +
+ +
+ {repo.sourceName} +
+
+ {repo.sourceId === workspaceRepoId && ( + + {isExtraSmallScreen + ? 'Workspace' + : 'Repo in workspace'} + + )} + + ) + })} + + + + + ) } diff --git a/ee/tabby-ui/components/source-icon.tsx b/ee/tabby-ui/components/source-icon.tsx new file mode 100644 index 000000000000..01d78eaa0206 --- /dev/null +++ b/ee/tabby-ui/components/source-icon.tsx @@ -0,0 +1,34 @@ +import { ReactNode } from 'react' + +import { ContextSourceKind } from '@/lib/gql/generates/graphql' +import { + IconCode, + IconEmojiBook, + IconEmojiGlobe, + IconGitHub, + IconGitLab +} from '@/components/ui/icons' + +interface SourceIconProps { + kind: ContextSourceKind + gitIcon?: ReactNode + className?: string +} + +export function SourceIcon({ kind, gitIcon, ...props }: SourceIconProps) { + switch (kind) { + case ContextSourceKind.Doc: + return + case ContextSourceKind.Web: + return + case ContextSourceKind.Github: + return + case ContextSourceKind.Gitlab: + return + case ContextSourceKind.Git: + // for custom git icon + return gitIcon || + default: + return null + } +} From 0dc66295667f9444bc4be27529414c5f79bcb4b3 Mon Sep 17 00:00:00 2001 From: Meng Zhang Date: Tue, 17 Dec 2024 15:03:39 +0800 Subject: [PATCH 19/39] Update ee/tabby-ui/components/chat/repo-select.tsx --- ee/tabby-ui/components/chat/repo-select.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ee/tabby-ui/components/chat/repo-select.tsx b/ee/tabby-ui/components/chat/repo-select.tsx index bdc7b130ee9d..9b8c4faf5d39 100644 --- a/ee/tabby-ui/components/chat/repo-select.tsx +++ b/ee/tabby-ui/components/chat/repo-select.tsx @@ -181,13 +181,6 @@ export function RepoSelect({ {repo.sourceName}
- {repo.sourceId === workspaceRepoId && ( - - {isExtraSmallScreen - ? 'Workspace' - : 'Repo in workspace'} - - )} ) })} From 1111c8d37c1b5bf0d24ee79fe346eb2f0986ac68 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 07:04:44 +0000 Subject: [PATCH 20/39] [autofix.ci] apply automated fixes --- ee/tabby-ui/components/chat/repo-select.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ee/tabby-ui/components/chat/repo-select.tsx b/ee/tabby-ui/components/chat/repo-select.tsx index 9b8c4faf5d39..f5ae86742b0f 100644 --- a/ee/tabby-ui/components/chat/repo-select.tsx +++ b/ee/tabby-ui/components/chat/repo-select.tsx @@ -101,7 +101,7 @@ export function RepoSelect({ {selectedRepo ? ( ) : ( @@ -116,7 +116,7 @@ export function RepoSelect({
{!value && ( -
+
)} @@ -164,17 +164,17 @@ export function RepoSelect({ > -
+
From 0bb466fe52c1dfc3c2f873f45e5f1c9b2c9df772 Mon Sep 17 00:00:00 2001 From: liangfung Date: Tue, 17 Dec 2024 16:25:42 +0700 Subject: [PATCH 21/39] update: optional --- clients/tabby-chat-panel/src/index.ts | 2 +- clients/vscode/src/chat/WebviewHelper.ts | 10 +++++----- ee/tabby-ui/components/message-markdown/index.tsx | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/clients/tabby-chat-panel/src/index.ts b/clients/tabby-chat-panel/src/index.ts index 98234d9dd3b5..4edf9923cfad 100644 --- a/clients/tabby-chat-panel/src/index.ts +++ b/clients/tabby-chat-panel/src/index.ts @@ -185,7 +185,7 @@ export interface ServerApi { addRelevantContext: (context: Context) => void updateTheme: (style: string, themeClass: string) => void updateActiveSelection: (context: Context | null) => void - updateGitUrl: (gitUrl: string | undefined) => void + updateGitUrl?: (gitUrl: string | undefined) => void } export interface ClientApiMethods { diff --git a/clients/vscode/src/chat/WebviewHelper.ts b/clients/vscode/src/chat/WebviewHelper.ts index a04bc227a183..f942e7eb094b 100644 --- a/clients/vscode/src/chat/WebviewHelper.ts +++ b/clients/vscode/src/chat/WebviewHelper.ts @@ -289,8 +289,8 @@ export class WebviewHelper { public async syncWorkspaceGitUrlToChatPanel(url: string | undefined) { try { - this.logger.log("sync indexed git url", url); - await this.client?.updateGitUrl(url); + this.logger.log("sync workspace git url", url); + await this.client?.updateGitUrl?.(url); } catch { this.logger.log( { @@ -333,7 +333,7 @@ export class WebviewHelper { this.pendingMessages = []; this.syncActiveSelection(window.activeTextEditor); - this.syncWorkspaceGitURL(window.activeTextEditor); + this.syncWorkspaceGitUrl(window.activeTextEditor); const agentConfig = this.lspClient.agentConfig.current; if (agentConfig?.server.token) { @@ -387,7 +387,7 @@ export class WebviewHelper { this.syncActiveSelectionToChatPanel(fileContext); } - public async syncWorkspaceGitURL(editor: TextEditor | undefined) { + public async syncWorkspaceGitUrl(editor: TextEditor | undefined) { const gitRemoteUrl = await getGitRemoteUrl(editor?.document.uri, this.gitProvider); this.syncWorkspaceGitUrlToChatPanel(gitRemoteUrl); } @@ -407,7 +407,7 @@ export class WebviewHelper { public addTextEditorEventListeners() { window.onDidChangeActiveTextEditor((e) => { this.syncActiveSelection(e); - this.syncWorkspaceGitURL(e); + this.syncWorkspaceGitUrl(e); }); window.onDidChangeTextEditorSelection((e) => { diff --git a/ee/tabby-ui/components/message-markdown/index.tsx b/ee/tabby-ui/components/message-markdown/index.tsx index 935ed8bed849..a47c7c5e8fc6 100644 --- a/ee/tabby-ui/components/message-markdown/index.tsx +++ b/ee/tabby-ui/components/message-markdown/index.tsx @@ -203,6 +203,7 @@ export function MessageMarkdown({ }) } const symbolInfo = await onLookupSymbol(keyword, hints) + console.log('symbolInfo====', keyword, hints?.length || 0, symbolInfo) setSymbolLocationMap(map => new Map(map.set(keyword, symbolInfo))) } From a9b587193a80aaa7904a9ae6d4bcfbc64eb85c36 Mon Sep 17 00:00:00 2001 From: liangfung Date: Tue, 17 Dec 2024 16:27:24 +0700 Subject: [PATCH 22/39] update --- ee/tabby-ui/components/message-markdown/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/ee/tabby-ui/components/message-markdown/index.tsx b/ee/tabby-ui/components/message-markdown/index.tsx index a47c7c5e8fc6..935ed8bed849 100644 --- a/ee/tabby-ui/components/message-markdown/index.tsx +++ b/ee/tabby-ui/components/message-markdown/index.tsx @@ -203,7 +203,6 @@ export function MessageMarkdown({ }) } const symbolInfo = await onLookupSymbol(keyword, hints) - console.log('symbolInfo====', keyword, hints?.length || 0, symbolInfo) setSymbolLocationMap(map => new Map(map.set(keyword, symbolInfo))) } From a2b3b93be782d385020fba1d02b30491accc7b3b Mon Sep 17 00:00:00 2001 From: liangfung Date: Tue, 17 Dec 2024 20:35:21 +0700 Subject: [PATCH 23/39] update: chat panel api --- clients/tabby-chat-panel/src/index.ts | 13 ++++- clients/vscode/src/chat/WebviewHelper.ts | 54 ++++++++++++--------- clients/vscode/src/chat/chatPanel.ts | 1 + clients/vscode/src/chat/fileContext.ts | 12 ----- ee/tabby-ui/app/chat/page.tsx | 26 +++++----- ee/tabby-ui/components/chat/chat.tsx | 31 ++++++------ ee/tabby-ui/components/chat/repo-select.tsx | 10 +++- 7 files changed, 80 insertions(+), 67 deletions(-) diff --git a/clients/tabby-chat-panel/src/index.ts b/clients/tabby-chat-panel/src/index.ts index 4edf9923cfad..aeaf328a4712 100644 --- a/clients/tabby-chat-panel/src/index.ts +++ b/clients/tabby-chat-panel/src/index.ts @@ -177,6 +177,13 @@ export interface SymbolInfo { target: FileLocation } +/** + * Includes information about a git repository in workspace folder + */ +export interface GitRepoInfo { + gitUrl: string +} + export interface ServerApi { init: (request: InitRequest) => void sendMessage: (message: ChatMessage) => void @@ -185,7 +192,6 @@ export interface ServerApi { addRelevantContext: (context: Context) => void updateTheme: (style: string, themeClass: string) => void updateActiveSelection: (context: Context | null) => void - updateGitUrl?: (gitUrl: string | undefined) => void } export interface ClientApiMethods { @@ -225,6 +231,9 @@ export interface ClientApiMethods { * @returns Whether the file location is opened successfully. */ openInEditor: (target: FileLocation) => Promise + + // Provide all repos found in workspace folders. + provideWorkspaceGitRepoInfo?: () => Promise } export interface ClientApi extends ClientApiMethods { @@ -259,6 +268,7 @@ export function createClient(target: HTMLIFrameElement, api: ClientApiMethods): onKeyboardEvent: api.onKeyboardEvent, lookupSymbol: api.lookupSymbol, openInEditor: api.openInEditor, + provideWorkspaceGitRepoInfo: api.provideWorkspaceGitRepoInfo, }, }) } @@ -273,7 +283,6 @@ export function createServer(api: ServerApi): ClientApi { addRelevantContext: api.addRelevantContext, updateTheme: api.updateTheme, updateActiveSelection: api.updateActiveSelection, - updateGitUrl: api.updateGitUrl, }, }) } diff --git a/clients/vscode/src/chat/WebviewHelper.ts b/clients/vscode/src/chat/WebviewHelper.ts index f942e7eb094b..c059f8708c9d 100644 --- a/clients/vscode/src/chat/WebviewHelper.ts +++ b/clients/vscode/src/chat/WebviewHelper.ts @@ -13,6 +13,7 @@ import { commands, Location, LocationLink, + workspace } from "vscode"; import type { ServerApi, @@ -23,6 +24,7 @@ import type { LookupSymbolHint, SymbolInfo, FileLocation, + GitRepoInfo, } from "tabby-chat-panel"; import { TABBY_CHAT_PANEL_API_VERSION } from "tabby-chat-panel"; import hashObject from "object-hash"; @@ -33,7 +35,7 @@ import { GitProvider } from "../git/GitProvider"; import { createClient } from "./chatPanel"; import { Client as LspClient } from "../lsp/Client"; import { isBrowser } from "../env"; -import { getFileContextFromSelection, showFileContext, openTextDocument, getGitRemoteUrl } from "./fileContext"; +import { getFileContextFromSelection, showFileContext, openTextDocument, buildFilePathParams } from "./fileContext"; import { localUriToChatPanelFilepath, chatPanelFilepathToLocalUri, @@ -287,21 +289,6 @@ export class WebviewHelper { } } - public async syncWorkspaceGitUrlToChatPanel(url: string | undefined) { - try { - this.logger.log("sync workspace git url", url); - await this.client?.updateGitUrl?.(url); - } catch { - this.logger.log( - { - every: 100, - level: "warn", - }, - "Git URL sync failed. Please update your Tabby server to the latest version.", - ); - } - } - public addRelevantContext(context: Context) { if (!this.client) { this.pendingRelevantContexts.push(context); @@ -333,7 +320,6 @@ export class WebviewHelper { this.pendingMessages = []; this.syncActiveSelection(window.activeTextEditor); - this.syncWorkspaceGitUrl(window.activeTextEditor); const agentConfig = this.lspClient.agentConfig.current; if (agentConfig?.server.token) { @@ -387,11 +373,6 @@ export class WebviewHelper { this.syncActiveSelectionToChatPanel(fileContext); } - public async syncWorkspaceGitUrl(editor: TextEditor | undefined) { - const gitRemoteUrl = await getGitRemoteUrl(editor?.document.uri, this.gitProvider); - this.syncWorkspaceGitUrlToChatPanel(gitRemoteUrl); - } - public addAgentEventListeners() { this.lspClient.status.on("didChange", async (status: StatusInfo) => { const agentConfig = this.lspClient.agentConfig.current; @@ -407,7 +388,6 @@ export class WebviewHelper { public addTextEditorEventListeners() { window.onDidChangeActiveTextEditor((e) => { this.syncActiveSelection(e); - this.syncWorkspaceGitUrl(e); }); window.onDidChangeTextEditorSelection((e) => { @@ -720,6 +700,34 @@ export class WebviewHelper { return false; } }, + provideWorkspaceGitRepoInfo: async (): Promise => { + const activeTextEditor = window.activeTextEditor; + const infoList: GitRepoInfo[] = []; + let activeGitUrl: string | undefined; + if (activeTextEditor) { + const pathParams = await buildFilePathParams(activeTextEditor.document.uri, this.gitProvider); + if (pathParams.gitRemoteUrl) { + activeGitUrl = pathParams.gitRemoteUrl; + infoList.push({ + gitUrl: activeGitUrl, + }) + } + }; + + const workspaceFolder = workspace.workspaceFolders || [] + for (const folder of workspaceFolder) { + const repo = this.gitProvider.getRepository(folder.uri); + if (repo) { + const gitRemoteUrl = this.gitProvider.getDefaultRemoteUrl(repo); + if (gitRemoteUrl && gitRemoteUrl !== activeGitUrl) { + infoList.push({ + gitUrl: gitRemoteUrl, + }); + } + } + } + return infoList; + }, }); } } diff --git a/clients/vscode/src/chat/chatPanel.ts b/clients/vscode/src/chat/chatPanel.ts index 0d3c50de1582..36098eb61916 100644 --- a/clients/vscode/src/chat/chatPanel.ts +++ b/clients/vscode/src/chat/chatPanel.ts @@ -35,6 +35,7 @@ export function createClient(webview: Webview, api: ClientApiMethods): ServerApi onKeyboardEvent: api.onKeyboardEvent, lookupSymbol: api.lookupSymbol, openInEditor: api.openInEditor, + provideWorkspaceGitRepoInfo: api.provideWorkspaceGitRepoInfo }, }); } diff --git a/clients/vscode/src/chat/fileContext.ts b/clients/vscode/src/chat/fileContext.ts index 4fdf203808b7..8cf6dbb50fce 100644 --- a/clients/vscode/src/chat/fileContext.ts +++ b/clients/vscode/src/chat/fileContext.ts @@ -81,18 +81,6 @@ export async function showFileContext(fileContext: FileContext, gitProvider: Git editor.revealRange(new Range(start, end), TextEditorRevealType.InCenter); } -export async function getGitRemoteUrl(fileUri: Uri | undefined, gitProvider: GitProvider): Promise { - if (fileUri) { - const pathParams = await buildFilePathParams(fileUri, gitProvider); - return pathParams.gitRemoteUrl; - } - - const workspaceFolder = workspace.workspaceFolders?.[0]; - if (!workspaceFolder) return undefined; - const repo = gitProvider.getRepository(workspaceFolder.uri); - return repo ? gitProvider.getDefaultRemoteUrl(repo) : undefined; -} - export async function buildFilePathParams(uri: Uri, gitProvider: GitProvider): Promise { const workspaceFolder = workspace.getWorkspaceFolder(uri) ?? (uri.scheme === "untitled" ? workspace.workspaceFolders?.[0] : undefined); diff --git a/ee/tabby-ui/app/chat/page.tsx b/ee/tabby-ui/app/chat/page.tsx index 0c537de99b07..880ea1edc09c 100644 --- a/ee/tabby-ui/app/chat/page.tsx +++ b/ee/tabby-ui/app/chat/page.tsx @@ -57,7 +57,6 @@ export default function ChatPage() { >([]) const [pendingActiveSelection, setPendingActiveSelection] = useState(null) - const [pendingGitUrl, setPendingGitUrl] = useState() const [errorMessage, setErrorMessage] = useState(null) const [isRefreshLoading, setIsRefreshLoading] = useState(false) @@ -76,6 +75,10 @@ export default function ChatPage() { const [supportsOnApplyInEditorV2, setSupportsOnApplyInEditorV2] = useState(false) const [supportsOnLookupSymbol, setSupportsOnLookupSymbol] = useState(false) + const [ + supportsProvideWorkspaceGitRepoInfo, + setSupportsProvideWorkspaceGitRepoInfo + ] = useState(false) const sendMessage = (message: ChatMessage) => { if (chatRef.current) { @@ -105,14 +108,6 @@ export default function ChatPage() { } } - const updateGitUrl = (gitUrl: string | undefined) => { - if (chatRef.current) { - chatRef.current.updateGitUrl(gitUrl) - } else if (gitUrl) { - setPendingGitUrl(gitUrl) - } - } - const server = useServer({ init: (request: InitRequest) => { if (chatRef.current) return @@ -160,8 +155,7 @@ export default function ChatPage() { document.documentElement.className = themeClass + ` client client-${client}` }, - updateActiveSelection, - updateGitUrl + updateActiveSelection }) useEffect(() => { @@ -248,6 +242,9 @@ export default function ChatPage() { ?.hasCapability('onApplyInEditorV2') .then(setSupportsOnApplyInEditorV2) server?.hasCapability('lookupSymbol').then(setSupportsOnLookupSymbol) + server + ?.hasCapability('provideWorkspaceGitRepoInfo') + .then(setSupportsProvideWorkspaceGitRepoInfo) } checkCapabilities() @@ -272,14 +269,12 @@ export default function ChatPage() { setPendingRelevantContexts([]) setPendingMessages([]) setPendingActiveSelection(null) - setPendingGitUrl(undefined) } const onChatLoaded = () => { pendingRelevantContexts.forEach(addRelevantContext) pendingMessages.forEach(sendMessage) chatRef.current?.updateActiveSelection(pendingActiveSelection) - chatRef.current?.updateGitUrl(pendingGitUrl) clearPendingState() setChatLoaded(true) @@ -407,6 +402,11 @@ export default function ChatPage() { (supportsOnLookupSymbol ? server?.lookupSymbol : undefined) } openInEditor={isInEditor && server?.openInEditor} + provideWorkspaceGitRepoInfo={ + isInEditor && supportsProvideWorkspaceGitRepoInfo + ? server?.provideWorkspaceGitRepoInfo + : undefined + } /> ) diff --git a/ee/tabby-ui/components/chat/chat.tsx b/ee/tabby-ui/components/chat/chat.tsx index da15841f11be..0b20362fe61d 100644 --- a/ee/tabby-ui/components/chat/chat.tsx +++ b/ee/tabby-ui/components/chat/chat.tsx @@ -4,6 +4,7 @@ import type { Context, FileContext, FileLocation, + GitRepoInfo, LookupSymbolHint, NavigateOpts, SymbolInfo @@ -86,7 +87,6 @@ export interface ChatRef { addRelevantContext: (context: Context) => void focus: () => void updateActiveSelection: (context: Context | null) => void - updateGitUrl: (gitUrl: string | undefined) => void } interface ChatProps extends React.ComponentProps<'div'> { @@ -114,6 +114,7 @@ interface ChatProps extends React.ComponentProps<'div'> { openInEditor?: (target: FileLocation) => void chatInputRef: RefObject supportsOnApplyInEditorV2: boolean + provideWorkspaceGitRepoInfo?: () => Promise } function ChatRenderer( @@ -136,7 +137,8 @@ function ChatRenderer( onLookupSymbol, openInEditor, chatInputRef, - supportsOnApplyInEditorV2 + supportsOnApplyInEditorV2, + provideWorkspaceGitRepoInfo }: ChatProps, ref: React.ForwardedRef ) { @@ -176,7 +178,6 @@ function ChatRenderer( ) }, [contextInfoData]) - // todo remove this const [{ data: resolvedGitUrl }] = useQuery({ query: resolveGitUrlQuery, variables: { @@ -544,19 +545,9 @@ function ChatRenderer( 300 ) - const debouncedUpdateGitUrl = useDebounceCallback( - (url: string | undefined) => { - setGitUrl(url) - }, - 300 - ) - const updateActiveSelection = (ctx: Context | null) => { debouncedUpdateActiveSelection.run(ctx) } - const updateGitUrl = (gitUrl: string | undefined) => { - debouncedUpdateGitUrl.run(gitUrl) - } React.useImperativeHandle( ref, @@ -567,14 +558,24 @@ function ChatRenderer( isLoading, addRelevantContext, focus: () => chatPanelRef.current?.focus(), - updateActiveSelection, - updateGitUrl + updateActiveSelection } }, [] ) React.useEffect(() => { + const fetchWorkspaceGitRepo = () => { + if (provideWorkspaceGitRepoInfo) { + provideWorkspaceGitRepoInfo().then(repos => { + if (repos.length) { + setGitUrl(repos[0].gitUrl) + } + }) + } + } + + fetchWorkspaceGitRepo() setInitialzed(true) onLoaded?.() }, []) diff --git a/ee/tabby-ui/components/chat/repo-select.tsx b/ee/tabby-ui/components/chat/repo-select.tsx index f5ae86742b0f..e26a09db8a85 100644 --- a/ee/tabby-ui/components/chat/repo-select.tsx +++ b/ee/tabby-ui/components/chat/repo-select.tsx @@ -87,7 +87,13 @@ export function RepoSelect({
{!value && ( -
+
)} From fce7a5a18aaa0dd2c59d81ea8ada30b5696749ac Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 13:36:32 +0000 Subject: [PATCH 24/39] [autofix.ci] apply automated fixes --- clients/vscode/src/chat/WebviewHelper.ts | 8 ++++---- clients/vscode/src/chat/chatPanel.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clients/vscode/src/chat/WebviewHelper.ts b/clients/vscode/src/chat/WebviewHelper.ts index c059f8708c9d..f5a161b6504e 100644 --- a/clients/vscode/src/chat/WebviewHelper.ts +++ b/clients/vscode/src/chat/WebviewHelper.ts @@ -13,7 +13,7 @@ import { commands, Location, LocationLink, - workspace + workspace, } from "vscode"; import type { ServerApi, @@ -710,11 +710,11 @@ export class WebviewHelper { activeGitUrl = pathParams.gitRemoteUrl; infoList.push({ gitUrl: activeGitUrl, - }) + }); } - }; + } - const workspaceFolder = workspace.workspaceFolders || [] + const workspaceFolder = workspace.workspaceFolders || []; for (const folder of workspaceFolder) { const repo = this.gitProvider.getRepository(folder.uri); if (repo) { diff --git a/clients/vscode/src/chat/chatPanel.ts b/clients/vscode/src/chat/chatPanel.ts index 36098eb61916..f991b2144fa1 100644 --- a/clients/vscode/src/chat/chatPanel.ts +++ b/clients/vscode/src/chat/chatPanel.ts @@ -35,7 +35,7 @@ export function createClient(webview: Webview, api: ClientApiMethods): ServerApi onKeyboardEvent: api.onKeyboardEvent, lookupSymbol: api.lookupSymbol, openInEditor: api.openInEditor, - provideWorkspaceGitRepoInfo: api.provideWorkspaceGitRepoInfo + provideWorkspaceGitRepoInfo: api.provideWorkspaceGitRepoInfo, }, }); } From cd90e84c1350f113de175bd892c8ad01c1f989b3 Mon Sep 17 00:00:00 2001 From: liangfung Date: Wed, 18 Dec 2024 02:05:29 +0700 Subject: [PATCH 25/39] update: init --- ee/tabby-ui/components/chat/chat-panel.tsx | 6 +- ee/tabby-ui/components/chat/chat.tsx | 91 ++++++++++----------- ee/tabby-ui/components/chat/repo-select.tsx | 15 +--- 3 files changed, 50 insertions(+), 62 deletions(-) diff --git a/ee/tabby-ui/components/chat/chat-panel.tsx b/ee/tabby-ui/components/chat/chat-panel.tsx index 9afed6890119..c488e3727cd5 100644 --- a/ee/tabby-ui/components/chat/chat-panel.tsx +++ b/ee/tabby-ui/components/chat/chat-panel.tsx @@ -73,11 +73,10 @@ function ChatPanelRenderer( removeRelevantContext, activeSelection, onCopyContent, - indexedRepository, selectedRepoId, setSelectedRepoId, repos, - fetchingRepos + initialized } = React.useContext(ChatContext) const enableActiveSelection = useChatStore( state => state.enableActiveSelection @@ -240,8 +239,7 @@ function ChatPanelRenderer( value={selectedRepoId} onChange={onSelectRepo} repos={repos} - workspaceRepoId={indexedRepository?.sourceId} - isInitializing={fetchingRepos} + isInitializing={!initialized} /> {activeSelection ? ( void chatInputRef: RefObject supportsOnApplyInEditorV2: boolean - indexedRepository: ResolveGitUrlQuery['resolveGitUrl'] selectedRepoId: string | undefined setSelectedRepoId: React.Dispatch> repos: ContextInfo['sources'] - fetchingRepos: boolean + fetchingSources: boolean } export const ChatContext = React.createContext( @@ -151,8 +151,6 @@ function ChatRenderer( const [activeSelection, setActiveSelection] = React.useState( null ) - // gitUrl from workspace - const [gitUrl, setGitUrl] = React.useState() // sourceId const [selectedRepoId, setSelectedRepoId] = React.useState< string | undefined @@ -178,14 +176,6 @@ function ChatRenderer( ) }, [contextInfoData]) - const [{ data: resolvedGitUrl }] = useQuery({ - query: resolveGitUrlQuery, - variables: { - gitUrl: gitUrl as string - }, - pause: !gitUrl - }) - const { sendUserMessage, isLoading, @@ -198,22 +188,6 @@ function ChatRenderer( threadId }) - React.useEffect(() => { - const setDefaultRepoId = () => { - if (!resolvedGitUrl || !repos?.length) return - if (selectedRepoId) return - - const defaultRepo = repos.find( - o => o.sourceId === resolvedGitUrl.resolveGitUrl?.sourceId - ) - if (defaultRepo) { - setSelectedRepoId(defaultRepo.sourceId) - } - } - - setDefaultRepoId() - }, [resolvedGitUrl, repos]) - const onDeleteMessage = async (userMessageId: string) => { if (!threadId) return @@ -549,6 +523,45 @@ function ChatRenderer( debouncedUpdateActiveSelection.run(ctx) } + const fetchWorkspaceGitRepo = () => { + if (provideWorkspaceGitRepoInfo) { + return provideWorkspaceGitRepoInfo() + } else { + return [] + } + } + + const resolveGitUrl = async (gitUrl: string | undefined) => { + if (!gitUrl) return undefined + return client.query(resolveGitUrlQuery, { gitUrl }).toPromise() + } + + React.useEffect(() => { + const init = async () => { + const gitRepoInfo = await fetchWorkspaceGitRepo() + // get default repo + if (gitRepoInfo?.length) { + const defaultGitUrl = gitRepoInfo[0].gitUrl + const repo = await resolveGitUrl(defaultGitUrl) + if (repo?.data?.resolveGitUrl) { + setSelectedRepoId(repo.data.resolveGitUrl.sourceId) + } + } + + setInitialzed(true) + } + + if (!fetchingSources && !initialized) { + init() + } + }, [fetchingSources]) + + React.useEffect(() => { + if (initialized) { + onLoaded?.() + } + }, [initialized]) + React.useImperativeHandle( ref, () => { @@ -564,22 +577,6 @@ function ChatRenderer( [] ) - React.useEffect(() => { - const fetchWorkspaceGitRepo = () => { - if (provideWorkspaceGitRepoInfo) { - provideWorkspaceGitRepoInfo().then(repos => { - if (repos.length) { - setGitUrl(repos[0].gitUrl) - } - }) - } - } - - fetchWorkspaceGitRepo() - setInitialzed(true) - onLoaded?.() - }, []) - const chatMaxWidthClass = maxWidth ? `max-w-${maxWidth}` : 'max-w-2xl' if (!initialized) { return ( @@ -606,11 +603,11 @@ function ChatRenderer( chatInputRef, activeSelection, supportsOnApplyInEditorV2, - indexedRepository: gitUrl ? resolvedGitUrl?.resolveGitUrl : undefined, selectedRepoId, setSelectedRepoId, repos, - fetchingRepos: fetchingSources + fetchingSources, + initialized }} >
diff --git a/ee/tabby-ui/components/chat/repo-select.tsx b/ee/tabby-ui/components/chat/repo-select.tsx index e26a09db8a85..d2ee9229c8d2 100644 --- a/ee/tabby-ui/components/chat/repo-select.tsx +++ b/ee/tabby-ui/components/chat/repo-select.tsx @@ -1,5 +1,4 @@ import { useRef, useState } from 'react' -import { useWindowSize } from '@uidotdev/usehooks' import { ContextInfo } from '@/lib/gql/generates/graphql' import { cn } from '@/lib/utils' @@ -30,27 +29,21 @@ import LoadingWrapper from '@/components/loading-wrapper' import { SourceIcon } from '../source-icon' interface RepoSelectProps { - repos: ContextInfo['sources'] + repos: ContextInfo['sources'] | undefined value: string | undefined onChange: (v: string | undefined) => void isInitializing?: boolean - // sourceId - workspaceRepoId?: string } export function RepoSelect({ repos, value, onChange, - isInitializing, - workspaceRepoId + isInitializing }: RepoSelectProps) { const [open, setOpen] = useState(false) const commandListRef = useRef(null) - const { width } = useWindowSize() - const isExtraSmallScreen = typeof width === 'number' && width < 360 - const onSelectRepo = (v: string) => { onChange(v) } @@ -73,7 +66,7 @@ export function RepoSelect({ const selectedRepoName = selectedRepo?.sourceName // if there's no repo, hide the repo select - if (!isInitializing && !repos.length) return null + if (!isInitializing && !repos?.length) return null return ( No context found - {repos.map(repo => { + {repos?.map(repo => { const isSelected = repo.sourceId === value return ( From 69c9d2e901f378d74a2844b8cb9421c66567ab65 Mon Sep 17 00:00:00 2001 From: liangfung Date: Wed, 18 Dec 2024 10:03:57 +0700 Subject: [PATCH 26/39] update: init --- ee/tabby-ui/components/chat/chat-panel.tsx | 1 + ee/tabby-ui/components/chat/chat.tsx | 10 ++-------- ee/tabby-ui/components/chat/prompt-form.tsx | 14 +++++++++++--- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/ee/tabby-ui/components/chat/chat-panel.tsx b/ee/tabby-ui/components/chat/chat-panel.tsx index c488e3727cd5..92d5a7746e7e 100644 --- a/ee/tabby-ui/components/chat/chat-panel.tsx +++ b/ee/tabby-ui/components/chat/chat-panel.tsx @@ -323,6 +323,7 @@ function ChatPanelRenderer( setInput={setInput} isLoading={isLoading} chatInputRef={chatInputRef} + isInitializing={!initialized} />
diff --git a/ee/tabby-ui/components/chat/chat.tsx b/ee/tabby-ui/components/chat/chat.tsx index 4da7c9999252..e0939fdfcf42 100644 --- a/ee/tabby-ui/components/chat/chat.tsx +++ b/ee/tabby-ui/components/chat/chat.tsx @@ -38,7 +38,6 @@ import { } from '@/lib/types/chat' import { cn, nanoid } from '@/lib/utils' -import { ListSkeleton } from '../skeleton' import { ChatPanel, ChatPanelRef } from './chat-panel' import { ChatScrollAnchor } from './chat-scroll-anchor' import { EmptyScreen } from './empty-screen' @@ -142,7 +141,7 @@ function ChatRenderer( }: ChatProps, ref: React.ForwardedRef ) { - const [initialized, setInitialzed] = React.useState(false) + const [initialized, setInitialized] = React.useState(false) const [threadId, setThreadId] = React.useState() const isOnLoadExecuted = React.useRef(false) const [qaPairs, setQaPairs] = React.useState(initialMessages ?? []) @@ -548,7 +547,7 @@ function ChatRenderer( } } - setInitialzed(true) + setInitialized(true) } if (!fetchingSources && !initialized) { @@ -578,11 +577,6 @@ function ChatRenderer( ) const chatMaxWidthClass = maxWidth ? `max-w-${maxWidth}` : 'max-w-2xl' - if (!initialized) { - return ( - - ) - } return ( Promise isLoading: boolean chatInputRef: React.RefObject + isInitializing?: boolean } export interface PromptFormRef { @@ -39,7 +40,14 @@ export interface PromptFormRef { } function PromptFormRenderer( - { onSubmit, input, setInput, isLoading, chatInputRef }: PromptProps, + { + onSubmit, + input, + setInput, + isLoading, + chatInputRef, + isInitializing + }: PromptProps, ref: React.ForwardedRef ) { const { formRef, onKeyDown } = useEnterSubmit() @@ -137,7 +145,7 @@ function PromptFormRenderer( HTMLFormElement > = async e => { e.preventDefault() - if (!input?.trim() || isLoading) { + if (!input?.trim() || isLoading || isInitializing) { return } @@ -233,7 +241,7 @@ function PromptFormRenderer(