@huyooo/file-explorer-frontend-vue 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Breadcrumb.vue.d.ts +11 -0
- package/dist/components/Breadcrumb.vue.d.ts.map +1 -0
- package/dist/components/CompressDialog.vue.d.ts +16 -0
- package/dist/components/CompressDialog.vue.d.ts.map +1 -0
- package/dist/components/ContextMenu.vue.d.ts +18 -0
- package/dist/components/ContextMenu.vue.d.ts.map +1 -0
- package/dist/components/FileGrid.vue.d.ts +40 -0
- package/dist/components/FileGrid.vue.d.ts.map +1 -0
- package/dist/components/FileIcon.vue.d.ts +13 -0
- package/dist/components/FileIcon.vue.d.ts.map +1 -0
- package/dist/components/FileInfoDialog.vue.d.ts +14 -0
- package/dist/components/FileInfoDialog.vue.d.ts.map +1 -0
- package/dist/components/FileList.vue.d.ts +37 -0
- package/dist/components/FileList.vue.d.ts.map +1 -0
- package/dist/components/FileListView.vue.d.ts +43 -0
- package/dist/components/FileListView.vue.d.ts.map +1 -0
- package/dist/components/FileSidebar.vue.d.ts +17 -0
- package/dist/components/FileSidebar.vue.d.ts.map +1 -0
- package/dist/components/ProgressDialog.vue.d.ts +28 -0
- package/dist/components/ProgressDialog.vue.d.ts.map +1 -0
- package/dist/components/SortIndicator.vue.d.ts +6 -0
- package/dist/components/SortIndicator.vue.d.ts.map +1 -0
- package/dist/components/StatusBar.vue.d.ts +27 -0
- package/dist/components/StatusBar.vue.d.ts.map +1 -0
- package/dist/components/Toolbar.vue.d.ts +60 -0
- package/dist/components/Toolbar.vue.d.ts.map +1 -0
- package/dist/components/Window.vue.d.ts +65 -0
- package/dist/components/Window.vue.d.ts.map +1 -0
- package/dist/composables/useApplicationIcon.d.ts +16 -0
- package/dist/composables/useApplicationIcon.d.ts.map +1 -0
- package/dist/composables/useDragAndDrop.d.ts +14 -0
- package/dist/composables/useDragAndDrop.d.ts.map +1 -0
- package/dist/composables/useMediaPlayer.d.ts +24 -0
- package/dist/composables/useMediaPlayer.d.ts.map +1 -0
- package/dist/composables/useSelection.d.ts +15 -0
- package/dist/composables/useSelection.d.ts.map +1 -0
- package/dist/composables/useWindowDrag.d.ts +18 -0
- package/dist/composables/useWindowDrag.d.ts.map +1 -0
- package/dist/composables/useWindowResize.d.ts +12 -0
- package/dist/composables/useWindowResize.d.ts.map +1 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4051 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +268 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/utils/fileTypeIcon.d.ts +6 -0
- package/dist/utils/fileTypeIcon.d.ts.map +1 -0
- package/dist/utils/folderTypeIcon.d.ts +14 -0
- package/dist/utils/folderTypeIcon.d.ts.map +1 -0
- package/package.json +55 -0
- package/src/components/Breadcrumb.vue +111 -0
- package/src/components/CompressDialog.vue +478 -0
- package/src/components/ContextMenu.vue +550 -0
- package/src/components/FileGrid.vue +504 -0
- package/src/components/FileIcon.vue +132 -0
- package/src/components/FileInfoDialog.vue +465 -0
- package/src/components/FileList.vue +421 -0
- package/src/components/FileListView.vue +321 -0
- package/src/components/FileSidebar.vue +158 -0
- package/src/components/ProgressDialog.vue +368 -0
- package/src/components/SortIndicator.vue +22 -0
- package/src/components/StatusBar.vue +43 -0
- package/src/components/Toolbar.vue +271 -0
- package/src/components/Window.vue +561 -0
- package/src/composables/useApplicationIcon.ts +79 -0
- package/src/composables/useDragAndDrop.ts +103 -0
- package/src/composables/useMediaPlayer.ts +174 -0
- package/src/composables/useSelection.ts +107 -0
- package/src/composables/useWindowDrag.ts +66 -0
- package/src/composables/useWindowResize.ts +134 -0
- package/src/index.ts +32 -0
- package/src/types/index.ts +273 -0
- package/src/utils/fileTypeIcon.ts +309 -0
- package/src/utils/folderTypeIcon.ts +132 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 文件类型(使用 const object 而非 enum,更好的 tree-shaking)
|
|
3
|
+
*/
|
|
4
|
+
export const FileType = {
|
|
5
|
+
FOLDER: 'folder',
|
|
6
|
+
FILE: 'file',
|
|
7
|
+
IMAGE: 'image',
|
|
8
|
+
VIDEO: 'video',
|
|
9
|
+
MUSIC: 'music',
|
|
10
|
+
DOCUMENT: 'document',
|
|
11
|
+
CODE: 'code',
|
|
12
|
+
TEXT: 'text',
|
|
13
|
+
PDF: 'pdf',
|
|
14
|
+
ARCHIVE: 'archive',
|
|
15
|
+
APPLICATION: 'application',
|
|
16
|
+
UNKNOWN: 'unknown'
|
|
17
|
+
} as const;
|
|
18
|
+
|
|
19
|
+
export type FileType = typeof FileType[keyof typeof FileType];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 文件系统项
|
|
23
|
+
*/
|
|
24
|
+
export interface FileItem {
|
|
25
|
+
/** 唯一标识(通常是完整路径) */
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
type: FileType;
|
|
29
|
+
size?: string;
|
|
30
|
+
dateModified?: string;
|
|
31
|
+
url?: string;
|
|
32
|
+
thumbnailUrl?: string;
|
|
33
|
+
children?: FileItem[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 视图模式
|
|
38
|
+
*/
|
|
39
|
+
export type ViewMode = 'grid' | 'list' | 'columns';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 排序配置
|
|
43
|
+
*/
|
|
44
|
+
export interface SortConfig {
|
|
45
|
+
field: 'name' | 'dateModified' | 'size' | 'type';
|
|
46
|
+
direction: 'asc' | 'desc';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 侧边栏项目
|
|
51
|
+
*/
|
|
52
|
+
export interface SidebarItem {
|
|
53
|
+
id: string;
|
|
54
|
+
label: string;
|
|
55
|
+
icon?: string;
|
|
56
|
+
path?: string;
|
|
57
|
+
children?: SidebarItem[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 面包屑项
|
|
62
|
+
*/
|
|
63
|
+
export interface BreadcrumbItem {
|
|
64
|
+
id: string;
|
|
65
|
+
name: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 操作结果
|
|
70
|
+
*/
|
|
71
|
+
export interface OperationResult<T = void> {
|
|
72
|
+
success: boolean;
|
|
73
|
+
data?: T;
|
|
74
|
+
error?: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** 压缩格式 */
|
|
78
|
+
export type CompressFormat = 'zip' | 'tar' | 'tgz' | 'tarbz2';
|
|
79
|
+
|
|
80
|
+
/** 压缩级别 */
|
|
81
|
+
export type CompressLevel = 'fast' | 'normal' | 'best';
|
|
82
|
+
|
|
83
|
+
/** 压缩选项 */
|
|
84
|
+
export interface CompressOptions {
|
|
85
|
+
format: CompressFormat;
|
|
86
|
+
level?: CompressLevel;
|
|
87
|
+
outputName: string;
|
|
88
|
+
outputDir: string;
|
|
89
|
+
deleteSource?: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** 解压选项 */
|
|
93
|
+
export interface ExtractOptions {
|
|
94
|
+
targetDir: string;
|
|
95
|
+
deleteArchive?: boolean;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** 压缩进度 */
|
|
99
|
+
export interface CompressProgress {
|
|
100
|
+
currentFile: string;
|
|
101
|
+
processedCount: number;
|
|
102
|
+
totalCount: number;
|
|
103
|
+
percent: number;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** 压缩结果 */
|
|
107
|
+
export interface CompressResult {
|
|
108
|
+
success: boolean;
|
|
109
|
+
outputPath?: string;
|
|
110
|
+
error?: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** 进度状态 */
|
|
114
|
+
export type ProgressStatus = 'pending' | 'processing' | 'success' | 'error';
|
|
115
|
+
|
|
116
|
+
/** 进度信息(UI 使用) */
|
|
117
|
+
export interface ProgressInfo {
|
|
118
|
+
type: 'compress' | 'extract';
|
|
119
|
+
status: ProgressStatus;
|
|
120
|
+
percent: number;
|
|
121
|
+
currentFile?: string;
|
|
122
|
+
processedCount?: number;
|
|
123
|
+
totalCount?: number;
|
|
124
|
+
error?: string;
|
|
125
|
+
outputPath?: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 文件操作适配器接口
|
|
130
|
+
*/
|
|
131
|
+
export interface FileExplorerAdapter {
|
|
132
|
+
/** 读取目录 */
|
|
133
|
+
readDirectory(dirPath: string): Promise<FileItem[]>;
|
|
134
|
+
/** 读取系统路径目录 */
|
|
135
|
+
readSystemPath(pathId: string): Promise<FileItem[]>;
|
|
136
|
+
/** 获取系统路径 */
|
|
137
|
+
getSystemPath(pathId: string): Promise<string | null>;
|
|
138
|
+
/** 读取文件内容 */
|
|
139
|
+
readFileContent(filePath: string): Promise<string>;
|
|
140
|
+
/** 写入文件内容 */
|
|
141
|
+
writeFileContent(filePath: string, content: string): Promise<OperationResult>;
|
|
142
|
+
/** 创建文件夹 */
|
|
143
|
+
createFolder(parentDir: string, folderName: string): Promise<OperationResult<{ finalPath: string }>>;
|
|
144
|
+
/** 创建文件 */
|
|
145
|
+
createFile(parentDir: string, fileName: string, content?: string): Promise<OperationResult<{ finalPath: string }>>;
|
|
146
|
+
/** 删除文件 */
|
|
147
|
+
deleteFiles(paths: string[]): Promise<OperationResult>;
|
|
148
|
+
/** 重命名文件 */
|
|
149
|
+
renameFile(oldPath: string, newPath: string): Promise<OperationResult>;
|
|
150
|
+
/** 复制文件 */
|
|
151
|
+
copyFiles(sourcePaths: string[], targetDir: string): Promise<OperationResult<{ copiedPaths: string[] }>>;
|
|
152
|
+
/** 移动文件 */
|
|
153
|
+
moveFiles(sourcePaths: string[], targetDir: string): Promise<OperationResult<{ movedPaths: string[] }>>;
|
|
154
|
+
/** 使用系统默认应用打开 */
|
|
155
|
+
openPath(filePath: string): Promise<OperationResult>;
|
|
156
|
+
/** 复制文件到剪贴板 */
|
|
157
|
+
copyFilesToClipboard(filePaths: string[]): Promise<OperationResult>;
|
|
158
|
+
/** 获取剪贴板文件 */
|
|
159
|
+
getClipboardFiles(): Promise<OperationResult<{ files: string[] }>>;
|
|
160
|
+
/** 粘贴文件 */
|
|
161
|
+
pasteFiles(targetDir: string, sourcePaths: string[]): Promise<OperationResult<{ pastedPaths: string[] }>>;
|
|
162
|
+
/** 流式搜索文件 */
|
|
163
|
+
searchFilesStream(searchPath: string, pattern: string, searchId: string): Promise<OperationResult>;
|
|
164
|
+
/** 监听搜索结果 */
|
|
165
|
+
onSearchResults(callback: (data: { searchId: string; items: FileItem[]; done: boolean }) => void): () => void;
|
|
166
|
+
/** 压缩文件 */
|
|
167
|
+
compressFiles(sources: string[], options: CompressOptions): Promise<CompressResult>;
|
|
168
|
+
/** 解压文件 */
|
|
169
|
+
extractArchive(archivePath: string, options: ExtractOptions): Promise<CompressResult>;
|
|
170
|
+
/** 判断是否为压缩文件 */
|
|
171
|
+
isArchiveFile(filePath: string): Promise<boolean>;
|
|
172
|
+
/** 监听压缩进度 */
|
|
173
|
+
onCompressProgress(callback: (data: CompressProgress) => void): () => void;
|
|
174
|
+
/** 监听解压进度 */
|
|
175
|
+
onExtractProgress(callback: (data: CompressProgress) => void): () => void;
|
|
176
|
+
/** 监听目录变化 */
|
|
177
|
+
watchDirectory(dirPath: string, watchId: string): Promise<OperationResult>;
|
|
178
|
+
/** 停止监听目录 */
|
|
179
|
+
unwatchDirectory(watchId: string): Promise<OperationResult>;
|
|
180
|
+
/** 监听文件变化事件 */
|
|
181
|
+
onWatchEvent(callback: (data: { watchId: string; event: WatchEvent }) => void): () => void;
|
|
182
|
+
/** 显示文件/文件夹的系统属性窗口 */
|
|
183
|
+
showFileInfo(filePath: string): Promise<OperationResult>;
|
|
184
|
+
/** 在终端中打开目录 */
|
|
185
|
+
openInTerminal(dirPath: string): Promise<OperationResult>;
|
|
186
|
+
/** 通过 Cursor 打开 */
|
|
187
|
+
openInEditor(targetPath: string): Promise<OperationResult>;
|
|
188
|
+
/** 在新窗口中打开文件夹(使用系统文件管理器) */
|
|
189
|
+
openInNewWindow(folderPath: string): Promise<OperationResult>;
|
|
190
|
+
/** 请求窗口聚焦(用于右键打开菜单前激活窗口) */
|
|
191
|
+
requestWindowFocus(): void;
|
|
192
|
+
|
|
193
|
+
// ===== 媒体服务 =====
|
|
194
|
+
|
|
195
|
+
/** 检测媒体文件是否需要转码 */
|
|
196
|
+
mediaNeedsTranscode?(filePath: string): Promise<OperationResult<TranscodeInfo>>;
|
|
197
|
+
/** 获取可播放的媒体 URL(自动转码) */
|
|
198
|
+
mediaGetPlayableUrl?(filePath: string): Promise<{ success: boolean; url?: string; error?: string }>;
|
|
199
|
+
/** 获取媒体元数据 */
|
|
200
|
+
mediaGetMetadata?(filePath: string): Promise<OperationResult<MediaMetadata>>;
|
|
201
|
+
/** 监听转码进度 */
|
|
202
|
+
onMediaTranscodeProgress?(callback: (data: { filePath: string; progress: MediaTranscodeProgress }) => void): () => void;
|
|
203
|
+
/** 清理指定文件的转码缓存 */
|
|
204
|
+
mediaCleanupFile?(filePath: string): Promise<OperationResult>;
|
|
205
|
+
|
|
206
|
+
// ===== 媒体预览窗口 =====
|
|
207
|
+
|
|
208
|
+
/** 打开媒体预览窗口(原生窗口) */
|
|
209
|
+
openMediaPreviewWindow?(filePath: string, mediaType: 'image' | 'video' | 'audio'): Promise<OperationResult>;
|
|
210
|
+
/** 关闭媒体预览窗口 */
|
|
211
|
+
closeMediaPreviewWindow?(filePath: string): Promise<OperationResult>;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** 文件变化事件类型 */
|
|
215
|
+
export type WatchEventType = 'add' | 'change' | 'remove' | 'rename';
|
|
216
|
+
|
|
217
|
+
/** 文件变化事件 */
|
|
218
|
+
export interface WatchEvent {
|
|
219
|
+
type: WatchEventType;
|
|
220
|
+
path: string;
|
|
221
|
+
filename: string;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ===== 媒体服务类型 =====
|
|
225
|
+
|
|
226
|
+
/** 媒体转码方式 */
|
|
227
|
+
export type TranscodeMethod = 'direct' | 'remux' | 'transcode';
|
|
228
|
+
|
|
229
|
+
/** 媒体转码状态 */
|
|
230
|
+
export type TranscodeStatus = 'idle' | 'checking' | 'transcoding' | 'ready' | 'error';
|
|
231
|
+
|
|
232
|
+
/** 媒体转码信息 */
|
|
233
|
+
export interface TranscodeInfo {
|
|
234
|
+
type: 'video' | 'audio';
|
|
235
|
+
needsTranscode: boolean;
|
|
236
|
+
method: TranscodeMethod;
|
|
237
|
+
estimatedTime?: number;
|
|
238
|
+
targetFormat?: string;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** 媒体转码进度 */
|
|
242
|
+
export interface MediaTranscodeProgress {
|
|
243
|
+
percent: number;
|
|
244
|
+
time?: number;
|
|
245
|
+
duration?: number;
|
|
246
|
+
speed?: string;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** 媒体元数据 */
|
|
250
|
+
export interface MediaMetadata {
|
|
251
|
+
filePath: string;
|
|
252
|
+
type: 'video' | 'audio';
|
|
253
|
+
duration: number;
|
|
254
|
+
title?: string;
|
|
255
|
+
artist?: string;
|
|
256
|
+
album?: string;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* 右键菜单项
|
|
261
|
+
*/
|
|
262
|
+
export interface ContextMenuItem {
|
|
263
|
+
id: string;
|
|
264
|
+
label: string;
|
|
265
|
+
icon?: string;
|
|
266
|
+
shortcut?: string;
|
|
267
|
+
disabled?: boolean;
|
|
268
|
+
separator?: boolean;
|
|
269
|
+
danger?: boolean; // 危险操作标记(如删除)
|
|
270
|
+
checked?: boolean; // 勾选状态(显示在右侧)
|
|
271
|
+
action?: () => void;
|
|
272
|
+
children?: ContextMenuItem[];
|
|
273
|
+
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { FileType } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 文件图标映射(基于 material-icon-theme.json)
|
|
5
|
+
*
|
|
6
|
+
* 原理:
|
|
7
|
+
* 1. KNOWN_TYPES:从 material-icon-theme.json 提取的所有图标名
|
|
8
|
+
* 2. EXT_MAP:扩展名 → 图标名
|
|
9
|
+
* 3. SPECIAL_FILES:特殊文件名 → 图标名
|
|
10
|
+
* 4. 逻辑:特殊文件名匹配 → 扩展名匹配 → 兜底
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// 从 material-icon-theme.json 提取的所有图标名(~200 个常用)
|
|
14
|
+
const KNOWN_TYPES = new Set([
|
|
15
|
+
'3d', 'actionscript', 'ada', 'adobe-illustrator', 'adobe-photoshop', 'android',
|
|
16
|
+
'angular', 'applescript', 'arduino', 'asciidoc', 'assembly', 'astro', 'audio',
|
|
17
|
+
'aurelia', 'babel', 'ballerina', 'bazel', 'biome', 'blender', 'bower', 'bun',
|
|
18
|
+
'c', 'cabal', 'caddy', 'cake', 'certificate', 'changelog', 'circleci', 'claude',
|
|
19
|
+
'clojure', 'cmake', 'cobol', 'coffee', 'command', 'conduct', 'contributing',
|
|
20
|
+
'controller', 'copilot', 'cpp', 'crystal', 'csharp', 'css', 'css-map', 'cucumber',
|
|
21
|
+
'cuda', 'cursor', 'cypress', 'd', 'dart', 'database', 'deno', 'dependabot', 'diff',
|
|
22
|
+
'django', 'docker', 'document', 'drawio', 'drizzle', 'editorconfig', 'ejs',
|
|
23
|
+
'elixir', 'elm', 'email', 'ember', 'epub', 'erlang', 'esbuild', 'eslint',
|
|
24
|
+
'excalidraw', 'exe', 'favicon', 'figma', 'firebase', 'flash', 'flow', 'font',
|
|
25
|
+
'forth', 'fortran', 'freemarker', 'fsharp', 'gamemaker', 'gatsby', 'gcp', 'gemfile',
|
|
26
|
+
'gemini', 'git', 'gitlab', 'gleam', 'go', 'go-mod', 'godot', 'gradle', 'graphql',
|
|
27
|
+
'groovy', 'grunt', 'gulp', 'h', 'haml', 'handlebars', 'hardhat', 'haskell', 'haxe',
|
|
28
|
+
'hcl', 'helm', 'hjson', 'hosts', 'hpp', 'html', 'http', 'husky', 'i18n', 'image',
|
|
29
|
+
'imba', 'ionic', 'jar', 'java', 'javaclass', 'javascript', 'javascript-map',
|
|
30
|
+
'jenkins', 'jest', 'jinja', 'jsconfig', 'json', 'julia', 'jupyter', 'just',
|
|
31
|
+
'karma', 'key', 'kotlin', 'kubernetes', 'laravel', 'lean', 'lefthook', 'lerna',
|
|
32
|
+
'less', 'lib', 'license', 'lighthouse', 'liquid', 'lisp', 'livescript', 'lock',
|
|
33
|
+
'log', 'lua', 'luau', 'makefile', 'markdown', 'markdownlint', 'matlab', 'maven',
|
|
34
|
+
'mdx', 'mercurial', 'mermaid', 'meson', 'minecraft', 'mjml', 'mocha', 'mojo',
|
|
35
|
+
'nest', 'netlify', 'next', 'nginx', 'nim', 'nix', 'nodejs', 'nodemon', 'npm',
|
|
36
|
+
'nuget', 'nunjucks', 'nuxt', 'nx', 'objective-c', 'objective-cpp', 'ocaml', 'odin',
|
|
37
|
+
'openapi', 'opentofu', 'pascal', 'pdf', 'perl', 'php', 'phpstan', 'phpunit',
|
|
38
|
+
'pipeline', 'playwright', 'pnpm', 'postcss', 'powerpoint', 'powershell', 'prettier',
|
|
39
|
+
'prisma', 'processing', 'prolog', 'proto', 'pug', 'puppet', 'purescript', 'python',
|
|
40
|
+
'pytorch', 'quasar', 'r', 'racket', 'razor', 'react', 'react-ts', 'readme', 'reason',
|
|
41
|
+
'remark', 'remix', 'renovate', 'replit', 'rescript', 'riot', 'roadmap', 'robot',
|
|
42
|
+
'robots', 'rollup', 'routing', 'rspec', 'rubocop', 'ruby', 'ruff', 'rust', 'salt',
|
|
43
|
+
'san', 'sas', 'sass', 'sbt', 'scala', 'scheme', 'search', 'sentry', 'sequelize',
|
|
44
|
+
'serverless', 'settings', 'shader', 'shellcheck', 'sketch', 'slim', 'slint',
|
|
45
|
+
'smarty', 'snakemake', 'snapcraft', 'snyk', 'solidity', 'spwn', 'stackblitz',
|
|
46
|
+
'stan', 'stencil', 'storybook', 'stryker', 'stylelint', 'stylus', 'sublime',
|
|
47
|
+
'subtitles', 'supabase', 'svelte', 'svg', 'svgo', 'swagger', 'swc', 'swift',
|
|
48
|
+
'tailwindcss', 'taskfile', 'tauri', 'tcl', 'teal', 'templ', 'template', 'terraform',
|
|
49
|
+
'test-js', 'test-jsx', 'test-ts', 'tex', 'todo', 'toml', 'travis', 'tree', 'tsconfig',
|
|
50
|
+
'tsdoc', 'turborepo', 'twig', 'typescript', 'typescript-def', 'unity', 'unocss',
|
|
51
|
+
'vagrant', 'vanilla-extract', 'vercel', 'verilog', 'video', 'vim', 'vite', 'vitest',
|
|
52
|
+
'vlang', 'vscode', 'vue', 'vue-config', 'wakatime', 'wallaby', 'webassembly',
|
|
53
|
+
'webhint', 'webpack', 'windicss', 'word', 'wrangler', 'wxt', 'xaml', 'xmake',
|
|
54
|
+
'xml', 'yaml', 'yarn', 'zig', 'zip',
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
// 扩展名 → 图标名
|
|
58
|
+
const EXT_MAP: Record<string, string> = {
|
|
59
|
+
// JavaScript/TypeScript
|
|
60
|
+
'js': 'javascript', 'mjs': 'javascript', 'cjs': 'javascript',
|
|
61
|
+
'jsx': 'react',
|
|
62
|
+
'ts': 'typescript', 'mts': 'typescript', 'cts': 'typescript',
|
|
63
|
+
'tsx': 'react-ts',
|
|
64
|
+
// 前端框架
|
|
65
|
+
'vue': 'vue',
|
|
66
|
+
'svelte': 'svelte',
|
|
67
|
+
'astro': 'astro',
|
|
68
|
+
// 后端语言
|
|
69
|
+
'py': 'python', 'pyw': 'python', 'pyi': 'python', 'pyc': 'python',
|
|
70
|
+
'java': 'java', 'class': 'javaclass', 'jar': 'jar',
|
|
71
|
+
'c': 'c', 'h': 'h',
|
|
72
|
+
'cpp': 'cpp', 'cc': 'cpp', 'cxx': 'cpp', 'hpp': 'hpp', 'hh': 'hpp', 'hxx': 'hpp',
|
|
73
|
+
'cs': 'csharp', 'csx': 'csharp',
|
|
74
|
+
'go': 'go',
|
|
75
|
+
'rs': 'rust',
|
|
76
|
+
'php': 'php', 'phtml': 'php',
|
|
77
|
+
'rb': 'ruby', 'rake': 'ruby',
|
|
78
|
+
'swift': 'swift',
|
|
79
|
+
'kt': 'kotlin', 'kts': 'kotlin',
|
|
80
|
+
'scala': 'scala',
|
|
81
|
+
'dart': 'dart',
|
|
82
|
+
'lua': 'lua', 'luau': 'luau',
|
|
83
|
+
'r': 'r', 'rdata': 'r', 'rds': 'r',
|
|
84
|
+
'pl': 'perl', 'pm': 'perl',
|
|
85
|
+
'sh': 'command', 'bash': 'command', 'zsh': 'command', 'fish': 'command',
|
|
86
|
+
'ps1': 'powershell', 'psm1': 'powershell', 'psd1': 'powershell',
|
|
87
|
+
'bat': 'command', 'cmd': 'command',
|
|
88
|
+
// 样式
|
|
89
|
+
'css': 'css',
|
|
90
|
+
'scss': 'sass', 'sass': 'sass',
|
|
91
|
+
'less': 'less',
|
|
92
|
+
'styl': 'stylus',
|
|
93
|
+
// 标记/配置
|
|
94
|
+
'html': 'html', 'htm': 'html', 'xhtml': 'html',
|
|
95
|
+
'xml': 'xml', 'xsl': 'xml', 'xslt': 'xml',
|
|
96
|
+
'json': 'json', 'jsonc': 'json', 'json5': 'json',
|
|
97
|
+
'yaml': 'yaml', 'yml': 'yaml',
|
|
98
|
+
'toml': 'toml',
|
|
99
|
+
'ini': 'settings',
|
|
100
|
+
'conf': 'settings', 'config': 'settings',
|
|
101
|
+
// 文档
|
|
102
|
+
'md': 'markdown', 'markdown': 'markdown', 'mdx': 'mdx',
|
|
103
|
+
'txt': 'document',
|
|
104
|
+
'pdf': 'pdf',
|
|
105
|
+
'doc': 'word', 'docx': 'word', 'dot': 'word', 'dotx': 'word', 'odt': 'word',
|
|
106
|
+
'xls': 'table', 'xlsx': 'table', 'xlsm': 'table', 'ods': 'table', 'csv': 'table',
|
|
107
|
+
'ppt': 'powerpoint', 'pptx': 'powerpoint', 'odp': 'powerpoint',
|
|
108
|
+
// 图片
|
|
109
|
+
'jpg': 'image', 'jpeg': 'image', 'png': 'image', 'gif': 'image', 'webp': 'image',
|
|
110
|
+
'ico': 'image', 'bmp': 'image', 'tiff': 'image', 'tif': 'image', 'heic': 'image', 'heif': 'image',
|
|
111
|
+
'svg': 'svg',
|
|
112
|
+
'psd': 'adobe-photoshop',
|
|
113
|
+
'ai': 'adobe-illustrator',
|
|
114
|
+
'sketch': 'sketch',
|
|
115
|
+
'fig': 'figma', 'figma': 'figma',
|
|
116
|
+
// 视频/音频
|
|
117
|
+
'mp4': 'video', 'mov': 'video', 'avi': 'video', 'mkv': 'video', 'webm': 'video',
|
|
118
|
+
'flv': 'video', 'wmv': 'video', 'm4v': 'video', '3gp': 'video', 'mpeg': 'video', 'mpg': 'video',
|
|
119
|
+
'mp3': 'audio', 'wav': 'audio', 'flac': 'audio', 'aac': 'audio', 'ogg': 'audio',
|
|
120
|
+
'wma': 'audio', 'm4a': 'audio', 'aiff': 'audio',
|
|
121
|
+
// 压缩
|
|
122
|
+
'zip': 'zip', 'rar': 'zip', '7z': 'zip', 'tar': 'zip', 'gz': 'zip', 'bz2': 'zip',
|
|
123
|
+
'xz': 'zip', 'tgz': 'zip', 'tbz2': 'zip',
|
|
124
|
+
// 数据库
|
|
125
|
+
'sql': 'database',
|
|
126
|
+
'db': 'database', 'sqlite': 'database', 'sqlite3': 'database',
|
|
127
|
+
'prisma': 'prisma',
|
|
128
|
+
// 其他
|
|
129
|
+
'log': 'log',
|
|
130
|
+
'lock': 'lock',
|
|
131
|
+
'env': 'settings',
|
|
132
|
+
'graphql': 'graphql', 'gql': 'graphql',
|
|
133
|
+
'proto': 'proto',
|
|
134
|
+
'wasm': 'webassembly',
|
|
135
|
+
'zig': 'zig',
|
|
136
|
+
'nim': 'nim',
|
|
137
|
+
'nix': 'nix',
|
|
138
|
+
'hcl': 'hcl', 'tf': 'terraform',
|
|
139
|
+
'sol': 'solidity',
|
|
140
|
+
'ex': 'elixir', 'exs': 'elixir',
|
|
141
|
+
'erl': 'erlang', 'hrl': 'erlang',
|
|
142
|
+
'hs': 'haskell', 'lhs': 'haskell',
|
|
143
|
+
'ml': 'ocaml', 'mli': 'ocaml',
|
|
144
|
+
'clj': 'clojure', 'cljs': 'clojure', 'cljc': 'clojure',
|
|
145
|
+
'lisp': 'lisp', 'lsp': 'lisp', 'el': 'lisp',
|
|
146
|
+
'vim': 'vim',
|
|
147
|
+
'dockerfile': 'docker',
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// 特殊文件名 → 图标名
|
|
151
|
+
const SPECIAL_FILES: Record<string, string> = {
|
|
152
|
+
// Git
|
|
153
|
+
'.gitignore': 'git', '.gitattributes': 'git', '.gitmodules': 'git', '.gitkeep': 'git',
|
|
154
|
+
// 环境
|
|
155
|
+
'.env': 'settings', '.env.local': 'settings', '.env.development': 'settings',
|
|
156
|
+
'.env.production': 'settings', '.env.test': 'settings', '.env.example': 'settings',
|
|
157
|
+
// Node/包管理
|
|
158
|
+
'package.json': 'nodejs', 'package-lock.json': 'npm',
|
|
159
|
+
'yarn.lock': 'yarn', '.yarnrc': 'yarn', '.yarnrc.yml': 'yarn',
|
|
160
|
+
'pnpm-lock.yaml': 'pnpm', '.pnpmfile.cjs': 'pnpm',
|
|
161
|
+
'bun.lockb': 'bun', 'bunfig.toml': 'bun',
|
|
162
|
+
// Python
|
|
163
|
+
'requirements.txt': 'python', 'pipfile': 'python', 'pipfile.lock': 'python',
|
|
164
|
+
'pyproject.toml': 'python', 'poetry.lock': 'python', 'setup.py': 'python',
|
|
165
|
+
// Rust
|
|
166
|
+
'cargo.toml': 'rust', 'cargo.lock': 'rust',
|
|
167
|
+
// Go
|
|
168
|
+
'go.mod': 'go-mod', 'go.sum': 'go-mod', 'go.work': 'go-mod',
|
|
169
|
+
// PHP
|
|
170
|
+
'composer.json': 'php', 'composer.lock': 'php',
|
|
171
|
+
// Ruby
|
|
172
|
+
'gemfile': 'gemfile', 'gemfile.lock': 'gemfile', 'rakefile': 'ruby',
|
|
173
|
+
// Docker
|
|
174
|
+
'dockerfile': 'docker', 'docker-compose.yml': 'docker', 'docker-compose.yaml': 'docker',
|
|
175
|
+
'.dockerignore': 'docker',
|
|
176
|
+
// 构建工具
|
|
177
|
+
'makefile': 'makefile', 'gnumakefile': 'makefile',
|
|
178
|
+
'cmakelists.txt': 'cmake',
|
|
179
|
+
'build.gradle': 'gradle', 'build.gradle.kts': 'gradle', 'settings.gradle': 'gradle',
|
|
180
|
+
'pom.xml': 'maven',
|
|
181
|
+
// 配置
|
|
182
|
+
'tsconfig.json': 'tsconfig', 'jsconfig.json': 'jsconfig',
|
|
183
|
+
'.prettierrc': 'prettier', '.prettierrc.json': 'prettier', '.prettierrc.js': 'prettier',
|
|
184
|
+
'.prettierignore': 'prettier', 'prettier.config.js': 'prettier',
|
|
185
|
+
'.eslintrc': 'eslint', '.eslintrc.json': 'eslint', '.eslintrc.js': 'eslint',
|
|
186
|
+
'.eslintignore': 'eslint', 'eslint.config.js': 'eslint', 'eslint.config.mjs': 'eslint',
|
|
187
|
+
'.editorconfig': 'editorconfig',
|
|
188
|
+
'vite.config.ts': 'vite', 'vite.config.js': 'vite',
|
|
189
|
+
'webpack.config.js': 'webpack', 'webpack.config.ts': 'webpack',
|
|
190
|
+
'rollup.config.js': 'rollup', 'rollup.config.ts': 'rollup',
|
|
191
|
+
'esbuild.config.js': 'esbuild',
|
|
192
|
+
'tailwind.config.js': 'tailwindcss', 'tailwind.config.ts': 'tailwindcss',
|
|
193
|
+
'postcss.config.js': 'postcss', 'postcss.config.cjs': 'postcss',
|
|
194
|
+
'babel.config.js': 'babel', '.babelrc': 'babel',
|
|
195
|
+
'jest.config.js': 'jest', 'jest.config.ts': 'jest',
|
|
196
|
+
'vitest.config.ts': 'vitest', 'vitest.config.js': 'vitest',
|
|
197
|
+
'playwright.config.ts': 'playwright', 'playwright.config.js': 'playwright',
|
|
198
|
+
'cypress.config.ts': 'cypress', 'cypress.config.js': 'cypress',
|
|
199
|
+
'.swcrc': 'swc', 'swc.config.js': 'swc',
|
|
200
|
+
'turbo.json': 'turborepo',
|
|
201
|
+
'nx.json': 'nx',
|
|
202
|
+
'biome.json': 'biome',
|
|
203
|
+
'.nvmrc': 'nodejs', '.node-version': 'nodejs',
|
|
204
|
+
// 框架配置
|
|
205
|
+
'nuxt.config.ts': 'nuxt', 'nuxt.config.js': 'nuxt',
|
|
206
|
+
'next.config.js': 'next', 'next.config.mjs': 'next', 'next.config.ts': 'next',
|
|
207
|
+
'svelte.config.js': 'svelte',
|
|
208
|
+
'astro.config.mjs': 'astro', 'astro.config.ts': 'astro',
|
|
209
|
+
'vue.config.js': 'vue-config',
|
|
210
|
+
'angular.json': 'angular',
|
|
211
|
+
'nest-cli.json': 'nest',
|
|
212
|
+
'tauri.conf.json': 'tauri',
|
|
213
|
+
// CI/CD
|
|
214
|
+
'.travis.yml': 'travis',
|
|
215
|
+
'.gitlab-ci.yml': 'gitlab',
|
|
216
|
+
'vercel.json': 'vercel',
|
|
217
|
+
'netlify.toml': 'netlify',
|
|
218
|
+
// 其他
|
|
219
|
+
'license': 'license', 'license.md': 'license', 'license.txt': 'license',
|
|
220
|
+
'readme': 'readme', 'readme.md': 'readme', 'readme.txt': 'readme',
|
|
221
|
+
'changelog': 'changelog', 'changelog.md': 'changelog',
|
|
222
|
+
'.npmrc': 'npm', '.npmignore': 'npm',
|
|
223
|
+
'robots.txt': 'robots',
|
|
224
|
+
'.htaccess': 'nginx',
|
|
225
|
+
'vagrantfile': 'vagrant',
|
|
226
|
+
'.stylelintrc': 'stylelint', '.stylelintrc.json': 'stylelint',
|
|
227
|
+
'nodemon.json': 'nodemon',
|
|
228
|
+
'.huskyrc': 'husky',
|
|
229
|
+
'renovate.json': 'renovate',
|
|
230
|
+
'.snyk': 'snyk',
|
|
231
|
+
'deno.json': 'deno', 'deno.jsonc': 'deno',
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* 根据文件名获取 material-icon-theme 图标名称
|
|
236
|
+
*/
|
|
237
|
+
export function getFileTypeIcon(fileName: string, fallbackType?: FileType): string {
|
|
238
|
+
const lowerName = fileName.toLowerCase();
|
|
239
|
+
|
|
240
|
+
// 1. 特殊文件名匹配
|
|
241
|
+
if (SPECIAL_FILES[lowerName]) {
|
|
242
|
+
const type = SPECIAL_FILES[lowerName];
|
|
243
|
+
if (KNOWN_TYPES.has(type)) {
|
|
244
|
+
return `material-icon-theme:${type}`;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// 2. Dockerfile 特殊处理
|
|
249
|
+
if (lowerName === 'dockerfile' || lowerName.startsWith('dockerfile.')) {
|
|
250
|
+
return 'material-icon-theme:docker';
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// 3. .env 变体处理
|
|
254
|
+
if (lowerName === '.env' || lowerName.startsWith('.env.')) {
|
|
255
|
+
return 'material-icon-theme:settings';
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// 4. 提取扩展名并匹配
|
|
259
|
+
const lastDotIndex = fileName.lastIndexOf('.');
|
|
260
|
+
const ext = lastDotIndex > 0 ? fileName.substring(lastDotIndex + 1).toLowerCase() : '';
|
|
261
|
+
|
|
262
|
+
// 处理复合扩展名
|
|
263
|
+
if (ext === 'ts' || ext === 'js') {
|
|
264
|
+
const baseName = fileName.substring(0, lastDotIndex).toLowerCase();
|
|
265
|
+
if (baseName.endsWith('.d')) {
|
|
266
|
+
return 'material-icon-theme:typescript-def';
|
|
267
|
+
}
|
|
268
|
+
if (baseName.endsWith('.test') || baseName.endsWith('.spec')) {
|
|
269
|
+
return ext === 'ts' ? 'material-icon-theme:test-ts' : 'material-icon-theme:test-js';
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (ext === 'jsx' || ext === 'tsx') {
|
|
273
|
+
const baseName = fileName.substring(0, lastDotIndex).toLowerCase();
|
|
274
|
+
if (baseName.endsWith('.test') || baseName.endsWith('.spec')) {
|
|
275
|
+
return ext === 'tsx' ? 'material-icon-theme:test-ts' : 'material-icon-theme:test-jsx';
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (ext && EXT_MAP[ext]) {
|
|
280
|
+
const type = EXT_MAP[ext];
|
|
281
|
+
if (KNOWN_TYPES.has(type)) {
|
|
282
|
+
return `material-icon-theme:${type}`;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// 5. 根据 fallbackType 返回兜底图标
|
|
287
|
+
if (fallbackType) {
|
|
288
|
+
return getFallbackIcon(fallbackType);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// 6. 最终兜底
|
|
292
|
+
return 'material-icon-theme:document';
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function getFallbackIcon(type: FileType): string {
|
|
296
|
+
switch (type) {
|
|
297
|
+
case FileType.IMAGE: return 'material-icon-theme:image';
|
|
298
|
+
case FileType.VIDEO: return 'material-icon-theme:video';
|
|
299
|
+
case FileType.MUSIC: return 'material-icon-theme:audio';
|
|
300
|
+
case FileType.CODE: return 'material-icon-theme:javascript';
|
|
301
|
+
case FileType.TEXT: return 'material-icon-theme:document';
|
|
302
|
+
case FileType.DOCUMENT: return 'material-icon-theme:word';
|
|
303
|
+
case FileType.PDF: return 'material-icon-theme:pdf';
|
|
304
|
+
case FileType.ARCHIVE: return 'material-icon-theme:zip';
|
|
305
|
+
case FileType.APPLICATION: return 'material-icon-theme:exe';
|
|
306
|
+
default: return 'material-icon-theme:document';
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|