diff --git a/src/ui/Desktop/Apps/File Manager/components/FileViewer.tsx b/src/ui/Desktop/Apps/File Manager/components/FileViewer.tsx index df74e5cd..c155b1ba 100644 --- a/src/ui/Desktop/Apps/File Manager/components/FileViewer.tsx +++ b/src/ui/Desktop/Apps/File Manager/components/FileViewer.tsx @@ -19,6 +19,9 @@ import { } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; +import CodeMirror from '@uiw/react-codemirror'; +import { oneDark } from '@uiw/codemirror-themes'; +import { languages, loadLanguage } from '@uiw/codemirror-extensions-langs'; interface FileItem { name: string; @@ -50,7 +53,7 @@ function getFileType(filename: string): { type: string; icon: React.ReactNode; c const videoExts = ['mp4', 'avi', 'mkv', 'mov', 'wmv', 'flv', 'webm']; const audioExts = ['mp3', 'wav', 'flac', 'ogg', 'aac', 'm4a']; const textExts = ['txt', 'md', 'readme']; - const codeExts = ['js', 'ts', 'jsx', 'tsx', 'py', 'java', 'cpp', 'c', 'cs', 'php', 'rb', 'go', 'rs', 'html', 'css', 'scss', 'less', 'json', 'xml', 'yaml', 'yml', 'toml', 'ini', 'conf']; + const codeExts = ['js', 'ts', 'jsx', 'tsx', 'py', 'java', 'cpp', 'c', 'cs', 'php', 'rb', 'go', 'rs', 'html', 'css', 'scss', 'less', 'json', 'xml', 'yaml', 'yml', 'toml', 'ini', 'conf', 'sh', 'bash', 'zsh', 'sql', 'vue', 'svelte']; if (imageExts.includes(ext)) { return { type: 'image', icon: , color: 'text-green-500' }; @@ -67,6 +70,55 @@ function getFileType(filename: string): { type: string; icon: React.ReactNode; c } } +// 获取CodeMirror语言扩展 +function getLanguageExtension(filename: string) { + const ext = filename.split('.').pop()?.toLowerCase() || ''; + const baseName = filename.toLowerCase(); + + // 特殊文件名处理 + if (['dockerfile', 'makefile', 'rakefile', 'gemfile'].includes(baseName)) { + return loadLanguage(baseName); + } + + // 根据扩展名映射 + const langMap: Record = { + 'js': 'javascript', + 'jsx': 'jsx', + 'ts': 'typescript', + 'tsx': 'tsx', + 'py': 'python', + 'java': 'java', + 'cpp': 'cpp', + 'c': 'c', + 'cs': 'csharp', + 'php': 'php', + 'rb': 'ruby', + 'go': 'go', + 'rs': 'rust', + 'html': 'html', + 'css': 'css', + 'scss': 'sass', + 'less': 'less', + 'json': 'json', + 'xml': 'xml', + 'yaml': 'yaml', + 'yml': 'yaml', + 'toml': 'toml', + 'sql': 'sql', + 'sh': 'shell', + 'bash': 'shell', + 'zsh': 'shell', + 'vue': 'vue', + 'svelte': 'svelte', + 'md': 'markdown', + 'conf': 'shell', + 'ini': 'properties' + }; + + const language = langMap[ext]; + return language ? loadLanguage(language) : null; +} + // 格式化文件大小 function formatFileSize(bytes?: number): string { if (!bytes) return 'Unknown size'; @@ -521,37 +573,74 @@ export function FileViewer({ {/* 文本和代码文件预览 */} {shouldShowAsText && !showLargeFileWarning && (
- {isEditable ? ( + {fileTypeInfo.type === 'code' ? ( + // 代码文件使用CodeMirror
{searchText && searchMatches.length > 0 ? ( - // 当有搜索结果时,显示只读的高亮文本 -
- {renderHighlightedText(editedContent)} + // 当有搜索结果时,显示只读的高亮文本(带行号) +
+ {/* 行号列 */} +
+ {editedContent.split('\n').map((_, index) => ( +
+ {index + 1} +
+ ))} +
+ {/* 代码内容 */} +
+ {renderHighlightedText(editedContent)} +
) : ( - // 没有搜索时显示可编辑的textarea -