diff --git a/src/components/chat-view/MarkdownActionBlock.tsx b/src/components/chat-view/MarkdownActionBlock.tsx index 57eedfb..02010c6 100644 --- a/src/components/chat-view/MarkdownActionBlock.tsx +++ b/src/components/chat-view/MarkdownActionBlock.tsx @@ -2,12 +2,14 @@ import { Check, CopyIcon, Loader2 } from 'lucide-react' import { PropsWithChildren, useMemo, useState } from 'react' import { useDarkModeContext } from '../../contexts/DarkModeContext' +import { ToolArgs } from "../../types/apply" import { InfioBlockAction } from '../../utils/parse-infio-block' import { MemoizedSyntaxHighlighterWrapper } from './SyntaxHighlighterWrapper' export default function MarkdownActionBlock({ - onApply, + msgId, + onApply, isApplying, language, filename, @@ -16,12 +18,8 @@ export default function MarkdownActionBlock({ action, children, }: PropsWithChildren<{ - onApply: (blockInfo: { - content: string - filename?: string - startLine?: number - endLine?: number - }) => void + msgId: string, + onApply: (args: ToolArgs) => void isApplying: boolean language?: string filename?: string @@ -71,9 +69,11 @@ export default function MarkdownActionBlock({ {action === InfioBlockAction.Edit && ( + + + + + {String(children)} + + + ) +} diff --git a/src/components/chat-view/MarkdownListFilesBlock.tsx b/src/components/chat-view/MarkdownListFilesBlock.tsx new file mode 100644 index 0000000..7db761a --- /dev/null +++ b/src/components/chat-view/MarkdownListFilesBlock.tsx @@ -0,0 +1,52 @@ +import { FolderOpen } from 'lucide-react' +import React from 'react' + +import { useApp } from '../../contexts/AppContext' +import { ApplyStatus, ListFilesToolArgs } from '../../types/apply' +import { openMarkdownFile } from '../../utils/obsidian' + +export default function MarkdownListFilesBlock({ + applyStatus, + onApply, + path, + recursive, + finish +}: { + applyStatus: ApplyStatus + onApply: (args: ListFilesToolArgs) => void + path: string, + recursive: boolean, + finish: boolean +}) { + const app = useApp() + + const handleClick = () => { + openMarkdownFile(app, path) + } + + React.useEffect(() => { + console.log('finish', finish, applyStatus) + if (finish && applyStatus === ApplyStatus.Idle) { + console.log('finish auto list files', path) + onApply({ + type: 'list_files', + filepath: path, + recursive + }) + } + }, [finish]) + + return ( +
+
+
+ + List files: {path} +
+
+
+ ) +} diff --git a/src/components/chat-view/MarkdownReadFileBlock.tsx b/src/components/chat-view/MarkdownReadFileBlock.tsx new file mode 100644 index 0000000..a29f05d --- /dev/null +++ b/src/components/chat-view/MarkdownReadFileBlock.tsx @@ -0,0 +1,49 @@ +import { ExternalLink } from 'lucide-react' +import React from 'react' + +import { useApp } from '../../contexts/AppContext' +import { ApplyStatus, ReadFileToolArgs } from '../../types/apply' +import { openMarkdownFile } from '../../utils/obsidian' + +export default function MarkdownReadFileBlock({ + applyStatus, + onApply, + path, + finish +}: { + applyStatus: ApplyStatus + onApply: (args: ReadFileToolArgs) => void + path: string, + finish: boolean +}) { + const app = useApp() + + const handleClick = () => { + openMarkdownFile(app, path) + } + + React.useEffect(() => { + console.log('finish', finish, applyStatus) + if (finish && applyStatus === ApplyStatus.Idle) { + console.log('finish auto read file', path) + onApply({ + type: 'read_file', + filepath: path + }) + } + }, [finish]) + + return ( +
+
+
+ + Read file: {path} +
+
+
+ ) +} diff --git a/src/components/chat-view/MarkdownReasoningBlock.tsx b/src/components/chat-view/MarkdownReasoningBlock.tsx index f4c6e0b..e1864d9 100644 --- a/src/components/chat-view/MarkdownReasoningBlock.tsx +++ b/src/components/chat-view/MarkdownReasoningBlock.tsx @@ -1,4 +1,4 @@ -import { ChevronDown, ChevronRight } from 'lucide-react' +import { ChevronDown, ChevronRight, Brain } from 'lucide-react' import { PropsWithChildren, useEffect, useRef, useState } from 'react' import { useDarkModeContext } from '../../contexts/DarkModeContext' @@ -27,6 +27,7 @@ export default function MarkdownReasoningBlock({ >
+ Reasoning
+
+ + + ) +} diff --git a/src/components/chat-view/MarkdownSemanticSearchFilesBlock.tsx b/src/components/chat-view/MarkdownSemanticSearchFilesBlock.tsx new file mode 100644 index 0000000..b0d861c --- /dev/null +++ b/src/components/chat-view/MarkdownSemanticSearchFilesBlock.tsx @@ -0,0 +1,52 @@ +import { FolderOpen } from 'lucide-react' +import React from 'react' + +import { useApp } from '../../contexts/AppContext' +import { ApplyStatus, SemanticSearchFilesToolArgs } from '../../types/apply' +import { openMarkdownFile } from '../../utils/obsidian' + +export default function MarkdownSemanticSearchFilesBlock({ + applyStatus, + onApply, + path, + query, + finish +}: { + applyStatus: ApplyStatus + onApply: (args: SemanticSearchFilesToolArgs) => void + path: string, + query: string, + finish: boolean +}) { + const app = useApp() + + const handleClick = () => { + openMarkdownFile(app, path) + } + + React.useEffect(() => { + console.log('finish', finish, applyStatus) + if (finish && applyStatus === ApplyStatus.Idle) { + console.log('finish auto semantic search files', path) + onApply({ + type: 'semantic_search_files', + filepath: path, + query: query, + }) + } + }, [finish]) + + return ( +
+
+
+ + semantic search files "{query}" in {path} +
+
+
+ ) +} diff --git a/src/components/chat-view/MarkdownWithIcon.tsx b/src/components/chat-view/MarkdownWithIcon.tsx new file mode 100644 index 0000000..efe8445 --- /dev/null +++ b/src/components/chat-view/MarkdownWithIcon.tsx @@ -0,0 +1,48 @@ +import { CircleCheckBig, CircleHelp } from 'lucide-react'; +import { ComponentPropsWithoutRef } from 'react'; +import ReactMarkdown from 'react-markdown'; +import rehypeRaw from 'rehype-raw'; + +const MarkdownWithIcons = ({ markdownContent, className }: { markdownContent: string, className?: string }) => { + // 预处理markdown内容,将标签转换为ReactMarkdown可以处理的格式 + const processedContent = markdownContent.replace( + /]*\/>/g, + (_, name, size, __, className) => + `` + ); + + const components = { + span: (props: ComponentPropsWithoutRef<'span'> & { + 'data-icon'?: string; + 'data-size'?: string; + }) => { + if (props['data-icon']) { + const name = props['data-icon']; + const size = props['data-size'] ? Number(props['data-size']) : 16; + const className = props.className || ''; + + switch (name) { + case 'ask_followup_question': + return ; + case 'attempt_completion': + return ; + default: + return null; + } + } + return ; + }, + }; + + return ( + + {processedContent} + + ); +}; + +export default MarkdownWithIcons;