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 (
-

选择左侧文档

+

选择左侧文档

) } @@ -44,19 +44,19 @@ export const DocContent = ({ content, onReferenceClick }: DocContentProps) => { ) } 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) => ( + + ))}
) }