import { useState, useCallback, useEffect } from 'react' import { X, AlertCircle } from 'lucide-react' import { DocTree } from './DocTree' import { DocContent } from './DocContent' import { buildFileTree } from '@/lib/parser' import type { DocFile } from '@/lib/types' interface ExternalDoc { name: string path: string relativePath: string content: string } interface ApiDocViewerProps { onDocsPathChange?: (path: string) => void; showAddModal?: boolean; onCloseAddModal?: () => void; } export const ApiDocViewer = ({ onDocsPathChange, showAddModal, onCloseAddModal }: ApiDocViewerProps) => { const [fileTree, setFileTree] = useState([]) const [selectedPath, setSelectedPath] = useState() const [currentContent, setCurrentContent] = useState('') const [showModal, setShowModal] = useState(false) const [docsPath, setDocsPath] = useState('') const [externalDocs, setExternalDocs] = useState([]) const [isLoading, setIsLoading] = useState(false) const [errorMsg, setErrorMsg] = useState(null) useEffect(() => { if (showAddModal) { setShowModal(true) setErrorMsg(null) } }, [showAddModal]) const loadDocsFromPath = async (basePath: string): Promise => { if (!basePath) { setErrorMsg('请输入文档路径') return false } if (!window.electronAPI) { setErrorMsg('Electron API 不可用,请使用打包后的应用') return false } setIsLoading(true) setErrorMsg(null) try { const files = await window.electronAPI.listDocsFiles(basePath) if (files.length === 0) { setErrorMsg(`路径 "${basePath}/api" 下没有找到 .md 文件`) setExternalDocs([]) setFileTree([]) setSelectedPath(undefined) setCurrentContent('') return false } const docs: ExternalDoc[] = [] for (const file of files) { const content = await window.electronAPI.readDocFile(file.path) if (content) { docs.push({ name: file.name, path: file.path, relativePath: file.relativePath.replace(/^api\//, ''), content }) } } setExternalDocs(docs) const fileList = docs .filter(doc => { const parts = doc.relativePath.split('/') if (parts.length < 2) return true const filename = parts[parts.length - 1].replace(/\.md$/, '') const parentFolder = parts[parts.length - 2] return filename !== parentFolder }) .map(d => d.relativePath) const tree = buildFileTree(fileList, '/') setFileTree(tree) if (fileList.length > 0) { setSelectedPath(fileList[0]) setCurrentContent(docs[0].content) } return true } catch (err) { setErrorMsg(`加载失败: ${err}`) return false } finally { setIsLoading(false) } } const handleSelect = useCallback((file: DocFile) => { setSelectedPath(file.relativePath) const doc = externalDocs.find(d => d.relativePath === file.relativePath) if (doc) { setCurrentContent(doc.content) } }, [externalDocs]) const handleFolderClick = useCallback((folderPath: string) => { const parts = folderPath.split('/') const basename = parts[parts.length - 1] const sameNameDoc = externalDocs.find(d => d.relativePath.endsWith(`/${basename}.md`) ) if (sameNameDoc) { setSelectedPath(sameNameDoc.relativePath) setCurrentContent(sameNameDoc.content) } }, [externalDocs]) const handleAddDocs = async () => { if (isLoading) return; const success = await loadDocsFromPath(docsPath.trim()) if (success) { setShowModal(false) onCloseAddModal?.() onDocsPathChange?.(docsPath.trim()) } } const handleReferenceClick = useCallback((href: string) => { const ref = href.replace(/\.md$/, '') const match = externalDocs.find(d => { const docPath = d.relativePath.replace(/\.md$/, '') return docPath === ref || docPath.endsWith('/' + ref.split('/').pop()) }) if (match) { setSelectedPath(match.relativePath) setCurrentContent(match.content) } }, [externalDocs]) return (
{showModal && (

添加文档路径

{errorMsg && (
{errorMsg}
)} { setDocsPath(e.target.value) setErrorMsg(null) }} placeholder="输入 docs 文件夹路径" className="w-full px-3 py-2 bg-[#1e1e1e] border border-[#3c3c3c] rounded text-sm text-gray-200 placeholder-gray-500" onKeyDown={(e) => e.key === 'Enter' && handleAddDocs()} autoFocus />
示例: C:\project\docs (会自动读取 api/*.md)
)}
) }