diff --git a/ee/tabby-ui/app/chat/page.tsx b/ee/tabby-ui/app/chat/page.tsx index f4a309a46ec9..fe5bada07987 100644 --- a/ee/tabby-ui/app/chat/page.tsx +++ b/ee/tabby-ui/app/chat/page.tsx @@ -389,7 +389,7 @@ export default function ChatPage() { } supportsOnApplyInEditorV2={supportsOnApplyInEditorV2} // TODO: adding capability check for onRenderLsp - onRenderLsp={isInEditor && server?.onRenderLsp} + onNavigateSymbol={isInEditor && server?.onNavigateSymbol} /> ) 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 de87e95fc496..9540ad23e369 100644 --- a/ee/tabby-ui/app/files/components/chat-side-bar.tsx +++ b/ee/tabby-ui/app/files/components/chat-side-bar.tsx @@ -79,7 +79,8 @@ export const ChatSideBar: React.FC = ({ onApplyInEditor(_content) {}, onLoaded() {}, onCopy(_content) {}, - onKeyboardEvent() {} + onKeyboardEvent() {}, + onNavigateSymbol(_filepath, _keywords) {} }) const getPrompt = ({ action }: QuickActionEventPayload) => { diff --git a/ee/tabby-ui/components/chat/chat.tsx b/ee/tabby-ui/components/chat/chat.tsx index 5a47d7ae1ae8..bac7904b4ac6 100644 --- a/ee/tabby-ui/components/chat/chat.tsx +++ b/ee/tabby-ui/components/chat/chat.tsx @@ -1,11 +1,6 @@ import React, { RefObject } from 'react' import { compact, findIndex, isEqual, some, uniqWith } from 'lodash-es' -import type { - Context, - FileContext, - KeywordInfo, - NavigateOpts -} from 'tabby-chat-panel' +import type { Context, FileContext, NavigateOpts } from 'tabby-chat-panel' import { ERROR_CODE_NOT_FOUND } from '@/lib/constants' import { @@ -50,10 +45,7 @@ type ChatContextValue = { onApplyInEditor?: | ((content: string) => void) | ((content: string, opts?: { languageId: string; smart: boolean }) => void) - onRenderLsp?: ( - filepaths: string[], - keywords: string[] - ) => Promise> + onNavigateSymbol?: (filepaths: string[], keywords: string) => void relevantContext: Context[] activeSelection: Context | null removeRelevantContext: (index: number) => void @@ -92,10 +84,7 @@ interface ChatProps extends React.ComponentProps<'div'> { onApplyInEditor?: | ((content: string) => void) | ((content: string, opts?: { languageId: string; smart: boolean }) => void) - onRenderLsp?: ( - filepaths: string[], - keywords: string[] - ) => Promise> + onNavigateSymbol?: (filepaths: string[], keywords: string) => void chatInputRef: RefObject supportsOnApplyInEditorV2: boolean } @@ -117,7 +106,7 @@ function ChatRenderer( onCopyContent, onSubmitMessage, onApplyInEditor, - onRenderLsp, + onNavigateSymbol, chatInputRef, supportsOnApplyInEditorV2 }: ChatProps, @@ -543,7 +532,7 @@ function ChatRenderer( container, onCopyContent, onApplyInEditor, - onRenderLsp, + onNavigateSymbol, relevantContext, removeRelevantContext, chatInputRef, diff --git a/ee/tabby-ui/components/chat/question-answer.tsx b/ee/tabby-ui/components/chat/question-answer.tsx index 74ba939db990..0f373e46aa24 100644 --- a/ee/tabby-ui/components/chat/question-answer.tsx +++ b/ee/tabby-ui/components/chat/question-answer.tsx @@ -261,7 +261,7 @@ function AssistantMessageCard(props: AssistantMessageCardProps) { onNavigateToContext, onApplyInEditor, onCopyContent, - onRenderLsp, + onNavigateSymbol, supportsOnApplyInEditorV2 } = React.useContext(ChatContext) const [relevantCodeHighlightIndex, setRelevantCodeHighlightIndex] = @@ -399,7 +399,7 @@ function AssistantMessageCard(props: AssistantMessageCardProps) { onCodeCitationMouseEnter={onCodeCitationMouseEnter} onCodeCitationMouseLeave={onCodeCitationMouseLeave} canWrapLongLines={!isLoading} - onRenderLsp={onRenderLsp} + onNavigateSymbol={onNavigateSymbol} onNavigateToContext={onNavigateToContext} supportsOnApplyInEditorV2={supportsOnApplyInEditorV2} /> diff --git a/ee/tabby-ui/components/message-markdown/index.tsx b/ee/tabby-ui/components/message-markdown/index.tsx index 92cc07ffc2be..f109b98ff5b4 100644 --- a/ee/tabby-ui/components/message-markdown/index.tsx +++ b/ee/tabby-ui/components/message-markdown/index.tsx @@ -1,11 +1,4 @@ -import { - createContext, - ReactNode, - useContext, - useEffect, - useMemo, - useState -} from 'react' +import { createContext, ReactNode, useContext, useMemo, useState } from 'react' import Image from 'next/image' import defaultFavicon from '@/assets/default-favicon.png' import DOMPurify from 'dompurify' @@ -28,7 +21,7 @@ import { MemoizedReactMarkdown } from '@/components/markdown' import './style.css' -import { Context, KeywordInfo, NavigateOpts } from 'tabby-chat-panel/index' +import { Context, NavigateOpts, SymbolInfo } from 'tabby-chat-panel/index' import { MARKDOWN_CITATION_REGEX, @@ -79,10 +72,7 @@ export interface MessageMarkdownProps { content: string, opts?: { languageId: string; smart: boolean } ) => void - onRenderLsp?: ( - filepaths: string[], - keywords: string[] - ) => Promise> + onNavigateSymbol?: (filepaths: string[], keywords: string) => void onNavigateToContext?: | ((context: Context, opts?: NavigateOpts) => void) | undefined @@ -109,8 +99,8 @@ type MessageMarkdownContextValue = { contextInfo: ContextInfo | undefined fetchingContextInfo: boolean canWrapLongLines: boolean - keywordMap: KeywordMapType onNavigateToContext?: (context: Context, opts?: NavigateOpts) => void + onNavigateSymbol?: (filepaths: string[], keywords: string) => void supportsOnApplyInEditorV2: boolean } @@ -129,7 +119,7 @@ export function MessageMarkdown({ fetchingContextInfo, className, canWrapLongLines, - onRenderLsp, + onNavigateSymbol, onNavigateToContext, supportsOnApplyInEditorV2, ...rest @@ -196,27 +186,6 @@ export function MessageMarkdown({ return elements } - const [keywordMap, setKeywordMap] = useState({}) - // rendering keywords - useEffect(() => { - if (message && canWrapLongLines && onRenderLsp) { - setKeywordMap({}) // TODO: remove htis - const inlineCodeRegex = /`([^`]+)`/g - const matches = message.match(inlineCodeRegex) - if (matches) { - const inlineCodes = Array.from( - new Set(matches.map(match => match.replace(/`/g, '').trim())) - ) - onRenderLsp( - attachmentCode ? attachmentCode?.map(code => code.filepath) : [], - inlineCodes - ).then(res => { - setKeywordMap(res) - }) - } - } - }, [message, canWrapLongLines, onRenderLsp, attachmentCode]) - return ( {children} }, code({ node, inline, className, children, ...props }) { - const { keywordMap, onNavigateToContext, canWrapLongLines } = - // eslint-disable-next-line react-hooks/rules-of-hooks - useContext(MessageMarkdownContext) + // eslint-disable-next-line react-hooks/rules-of-hooks + const { onNavigateSymbol, canWrapLongLines } = useContext( + MessageMarkdownContext + ) if (children.length) { - if (children[0] == '▍') { + if (children[0] === '▍') { return ( ) @@ -294,39 +264,31 @@ export function MessageMarkdown({ ) } - const info = keywordMap[keyword] + const isClickable = Boolean(onNavigateSymbol && canWrapLongLines) - const isClickable = Boolean( - info && onNavigateToContext && canWrapLongLines - ) + const handleClick = () => { + if (!isClickable) return + if (onNavigateSymbol) { + onNavigateSymbol( + attachmentCode + ? attachmentCode.map(code => code.filepath) + : [], + keyword + ) + } + } return ( { - if (!isClickable) return - if (onNavigateToContext) { - onNavigateToContext( - { - filepath: info.targetFile, - content: '', - git_url: '', - kind: 'file', - range: { - start: info.targetLine, - end: info.targetLine + 1 - } - }, - { openInEditor: true } - ) - } - }} - title={info ? `${info.targetFile}:${info.targetLine}` : ''} + onClick={handleClick} + // TODO(Sma1lboy): use onHover to lazy load this part + title={isClickable ? '' : ''} {...props} > {children} @@ -588,8 +550,8 @@ export function SiteFavicon({ ) } -interface KeywordMapType { - [key: string]: KeywordInfo +interface SymbolMapType { + [key: string]: SymbolInfo } function IssueStateBadge({ closed }: { closed: boolean }) {