From c3ecebea896b424dd24e12e300aa77b17e40f7b6 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Wed, 18 Mar 2026 19:26:46 +0800 Subject: [PATCH] style: add VS Code tree view for doc sidebar and improve typography --- src/components/DocContent.tsx | 16 ++-- src/components/DocTree.tsx | 163 ++++++++++++++++++++++------------ 2 files changed, 116 insertions(+), 63 deletions(-) diff --git a/src/components/DocContent.tsx b/src/components/DocContent.tsx index 149abd3..ebb985d 100644 --- a/src/components/DocContent.tsx +++ b/src/components/DocContent.tsx @@ -11,7 +11,7 @@ export const DocContent = ({ content, onReferenceClick }: DocContentProps) => { if (!content) { return (
选择左侧文档
+选择左侧文档
+
{children}
)
},
pre: ({ children }) => (
-
+
{children}
),
table: ({ children }) => (
-
+
{children}
@@ -87,22 +87,22 @@ export const DocContent = ({ content, onReferenceClick }: DocContentProps) => {
),
h2: ({ children }) => (
-
+
{children}
),
h3: ({ children }) => (
-
+
{children}
),
h4: ({ children }) => (
-
+
{children}
),
p: ({ children }) => (
-
+
{children}
),
diff --git a/src/components/DocTree.tsx b/src/components/DocTree.tsx
index 9507936..034c12c 100644
--- a/src/components/DocTree.tsx
+++ b/src/components/DocTree.tsx
@@ -1,5 +1,4 @@
import React, { useState } from 'react'
-import { ChevronRight, ChevronDown, Folder, FileText } from 'lucide-react'
import { clsx } from 'clsx'
import type { DocFile } from '@/lib/types'
import { getDisplayName } from '@/lib/parser'
@@ -10,88 +9,142 @@ interface DocTreeProps {
onSelect: (file: DocFile) => void
}
-interface DocTreeNodeProps {
+interface TreeNodeProps {
file: DocFile
level: number
+ isLast: boolean
+ ancestorsLast: boolean[]
selectedPath?: string
onSelect: (file: DocFile) => void
+ expandedSet: Set
+ onToggle: (path: string) => void
}
-const DocTreeNode = React.memo(({ file, level, selectedPath, onSelect }: DocTreeNodeProps) => {
- const [expanded, setExpanded] = useState(level === 0)
-
- const isSelected = selectedPath === file.relativePath
+const TreeNode = React.memo(({
+ file,
+ level,
+ isLast,
+ ancestorsLast,
+ selectedPath,
+ onSelect,
+ expandedSet,
+ onToggle,
+}: TreeNodeProps) => {
const isDir = file.isDir
+ const isExpanded = expandedSet.has(file.relativePath)
+ const isSelected = selectedPath === file.relativePath
- const handleClick = (e: React.MouseEvent) => {
- e.stopPropagation()
+ const handleClick = () => {
if (isDir) {
- setExpanded(!expanded)
+ onToggle(file.relativePath)
} else {
onSelect(file)
}
}
+ const renderIndent = () => {
+ const parts: React.ReactNode[] = []
+
+ for (let i = 0; i < level; i++) {
+ const showLine = !ancestorsLast[i]
+ parts.push(
+
+ {showLine ? '│' : ' '}
+
+ )
+ }
+
+ const connector = isLast ? '└──' : '├──'
+ parts.push(
+
+ {connector}
+
+ )
+
+ return parts
+ }
+
+ const showChildren = isDir && isExpanded && file.children
+
return (
-
+ <>
-
- {isDir ? (
- expanded ? :
- ) : (
-
- )}
+
+ {renderIndent()}
-
-
- {isDir ? : }
+
+ {getDisplayName(file.name)}
-
- {getDisplayName(file.name)}
- {isDir && expanded && file.children && (
-
- {file.children.map((child) => (
-
- ))}
-
- )}
-
+ {showChildren && file.children!.map((child, idx) => (
+
+ ))}
+ >
)
})
-DocTreeNode.displayName = 'DocTreeNode'
+TreeNode.displayName = 'TreeNode'
export const DocTree = ({ files, selectedPath, onSelect }: DocTreeProps) => {
+ const [expandedSet, setExpandedSet] = useState>(() => {
+ const set = new Set()
+ files.forEach(f => {
+ if (f.isDir) set.add(f.relativePath)
+ })
+ return set
+ })
+
+ const handleToggle = (path: string) => {
+ setExpandedSet(prev => {
+ const next = new Set(prev)
+ if (next.has(path)) {
+ next.delete(path)
+ } else {
+ next.add(path)
+ }
+ return next
+ })
+ }
+
return (
-
-
- {files.map((file) => (
-
- ))}
-
+
+ {files.map((file, idx) => (
+
+ ))}
)
}