update buildin mcp tools

This commit is contained in:
duanfuxiang
2025-06-08 22:33:01 +08:00
parent 2b571f67a7
commit 4053214078
10 changed files with 996 additions and 91 deletions

View File

@@ -64,6 +64,8 @@ import McpHubView from './McpHubView' // Moved after MarkdownReasoningBlock
import QueryProgress, { QueryProgressState } from './QueryProgress'
import ReactMarkdown from './ReactMarkdown'
import SimilaritySearchResults from './SimilaritySearchResults'
import FileReadResults from './FileReadResults'
import WebsiteReadResults from './WebsiteReadResults'
import MarkdownReasoningBlock from './Markdown/MarkdownReasoningBlock'
// Add an empty line here
@@ -1079,6 +1081,18 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
)
}}
/>
{message.fileReadResults && (
<FileReadResults
key={"file-read-" + message.id}
fileContents={message.fileReadResults}
/>
)}
{message.websiteReadResults && (
<WebsiteReadResults
key={"website-read-" + message.id}
websiteContents={message.websiteReadResults}
/>
)}
{message.similaritySearchResults && (
<SimilaritySearchResults
key={"similarity-search-" + message.id}

View File

@@ -0,0 +1,82 @@
import path from 'path'
import { ChevronDown, ChevronRight, FileText } from 'lucide-react'
import { useState } from 'react'
import { useApp } from '../../contexts/AppContext'
import { t } from '../../lang/helpers'
import { openMarkdownFile } from '../../utils/obsidian'
function FileReadItem({
fileResult,
}: {
fileResult: { path: string, content: string }
}) {
const app = useApp()
const handleClick = () => {
openMarkdownFile(app, fileResult.path)
}
const getFileSize = (content: string) => {
const bytes = new Blob([content]).size
if (bytes < 1024) return `${bytes} B`
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
}
return (
<div onClick={handleClick} className="infio-file-read-item">
<div className="infio-file-read-item__icon">
<FileText size={16} />
</div>
<div className="infio-file-read-item__info">
<div className="infio-file-read-item__name">
{path.basename(fileResult.path)}
</div>
<div className="infio-file-read-item__path">
{fileResult.path}
</div>
</div>
<div className="infio-file-read-item__size">
{getFileSize(fileResult.content)}
</div>
</div>
)
}
export default function FileReadResults({
fileContents,
}: {
fileContents: Array<{ path: string, content: string }>
}) {
const [isOpen, setIsOpen] = useState(false)
return (
<div className="infio-file-read-results">
<div
onClick={() => {
setIsOpen(!isOpen)
}}
className="infio-file-read-results__trigger"
>
{isOpen ? <ChevronDown size={16} /> : <ChevronRight size={16} />}
<div>
{t('chat.fileResults.showReadFiles')} ({fileContents.length})
</div>
</div>
{isOpen && (
<div
style={{
display: 'flex',
flexDirection: 'column',
}}
>
{fileContents.map((fileResult, index) => (
<FileReadItem key={`${fileResult.path}-${index}`} fileResult={fileResult} />
))}
</div>
)}
</div>
)
}

View File

@@ -4,6 +4,26 @@ export type QueryProgressState =
| {
type: 'reading-mentionables'
}
| {
type: 'reading-files'
currentFile?: string
totalFiles?: number
completedFiles?: number
}
| {
type: 'reading-files-done'
fileContents: Array<{ path: string, content: string }>
}
| {
type: 'reading-websites'
currentUrl?: string
totalUrls?: number
completedUrls?: number
}
| {
type: 'reading-websites-done'
websiteContents: Array<{ url: string, content: string }>
}
| {
type: 'indexing'
indexProgress: IndexProgress
@@ -43,6 +63,62 @@ export default function QueryProgress({
</p>
</div>
)
case 'reading-files':
return (
<div className="infio-query-progress">
<p>
{t('chat.queryProgress.readingFiles')}
<DotLoader />
</p>
{state.currentFile && (
<p className="infio-query-progress-detail">
{state.currentFile}
{state.totalFiles && state.completedFiles !== undefined && (
<span> ({state.completedFiles}/{state.totalFiles})</span>
)}
</p>
)}
</div>
)
case 'reading-files-done':
return (
<div className="infio-query-progress">
<p>
{t('chat.queryProgress.readingFilesDone')}
</p>
<p className="infio-query-progress-detail">
{t('chat.queryProgress.filesLoaded', { count: state.fileContents.length })}
</p>
</div>
)
case 'reading-websites':
return (
<div className="infio-query-progress">
<p>
{t('chat.queryProgress.readingWebsites')}
<DotLoader />
</p>
{state.currentUrl && (
<p className="infio-query-progress-detail">
{state.currentUrl}
{state.totalUrls && state.completedUrls !== undefined && (
<span> ({state.completedUrls}/{state.totalUrls})</span>
)}
</p>
)}
</div>
)
case 'reading-websites-done':
return (
<div className="infio-query-progress">
<p>
{t('chat.queryProgress.readingWebsitesDone')}
</p>
<p className="infio-query-progress-detail">
{t('chat.queryProgress.websitesLoaded', { count: state.websiteContents.length })}
</p>
</div>
)
case 'indexing':
return (
<div className="infio-query-progress">

View File

@@ -0,0 +1,95 @@
import { ChevronDown, ChevronRight, FileText, Globe } from 'lucide-react'
import { TFile } from 'obsidian'
import { useState } from 'react'
import { useApp } from '../../contexts/AppContext'
import { t } from '../../lang/helpers'
function WebsiteReadItem({
websiteResult,
}: {
websiteResult: { url: string, content: string }
}) {
const app = useApp()
const handleClick = () => {
// 现在url字段实际上是markdown文件路径直接在Obsidian中打开
const file = app.vault.getAbstractFileByPath(websiteResult.url)
if (file instanceof TFile) {
app.workspace.getLeaf('tab').openFile(file)
}
}
const getContentSize = (content: string) => {
const bytes = new Blob([content]).size
if (bytes < 1024) return `${bytes} B`
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
}
const getFileBaseName = (filePath: string) => {
return filePath.split('/').pop()?.replace('.md', '') || 'website'
}
const truncatePath = (filePath: string, maxLength: number = 60) => {
if (filePath.length <= maxLength) return filePath
return '...' + filePath.substring(filePath.length - maxLength)
}
return (
<div onClick={handleClick} className="infio-website-read-item">
<div className="infio-website-read-item__icon">
<FileText size={16} />
</div>
<div className="infio-website-read-item__info">
<div className="infio-website-read-item__domain">
{getFileBaseName(websiteResult.url)}
</div>
<div className="infio-website-read-item__url">
{truncatePath(websiteResult.url)}
</div>
</div>
<div className="infio-website-read-item__actions">
<div className="infio-website-read-item__size">
{getContentSize(websiteResult.content)}
</div>
</div>
</div>
)
}
export default function WebsiteReadResults({
websiteContents,
}: {
websiteContents: Array<{ url: string, content: string }>
}) {
const [isOpen, setIsOpen] = useState(false)
return (
<div className="infio-website-read-results">
<div
onClick={() => {
setIsOpen(!isOpen)
}}
className="infio-website-read-results__trigger"
>
{isOpen ? <ChevronDown size={16} /> : <ChevronRight size={16} />}
<div>
{t('chat.websiteResults.showReadWebsites')} ({websiteContents.length})
</div>
</div>
{isOpen && (
<div
style={{
display: 'flex',
flexDirection: 'column',
}}
>
{websiteContents.map((websiteResult, index) => (
<WebsiteReadItem key={`${websiteResult.url}-${index}`} websiteResult={websiteResult} />
))}
</div>
)}
</div>
)
}