Skip to content

Commit 5339d28

Browse files
authored
1.4.0
【修复】 - TS 静态错误 - 过时的构建命令 - 潜在的代码问题 【优化】 - 搜索高亮性能 - 虚拟列表性能 - 移除部分死代码 - 文档/注释语法错误 - 文本类型文件预览性能 - 文本类型文件预览响应式设计 - README 中弃用的 HTML 特性 - Markdown 相对链接跳转稳定性
1 parent 1bfe764 commit 5339d28

105 files changed

Lines changed: 958 additions & 3865 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CONTRIBUTING.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ npm run lint
9191

9292
### 代码格式化
9393

94-
我们使用 `EditorConfig` 进行一致的格式化,请确保你的编辑器安装了相应插件以自动应用格式规则。
94+
我们使用 `EditorConfig` 进行统一格式化,请确保你的编辑器安装了相应插件以自动应用格式规则。
9595

9696
## 提交 issue
9797

@@ -104,7 +104,7 @@ npm run lint
104104

105105
### [enhancement]
106106

107-
- 清晰的增强描述,杜绝抽象概念
107+
- 清晰地描述增强点,杜绝抽象概念
108108
- 建议的实现逻辑
109109

110110
### [feature]
@@ -115,8 +115,6 @@ npm run lint
115115

116116
> 推荐保持与历史 issue 一致的标题格式
117117

118-
---
119-
120118
### 许可证
121119

122120
通过贡献,您同意您的贡献将按照与项目相同的许可证 [AGPL-3.0](LICENSE) 进行许可。

README.md

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
<h1 align="center">
2-
<em>Repo-Viewer</em>
3-
</h1>
4-
5-
<p align="center">
6-
<strong>基于 Material Design 3设计风格的 GitHub仓库浏览应用</strong>
7-
&nbsp;&nbsp;
8-
<a href="https://deepwiki.com/UE-DND/Repo-Viewer">
9-
<img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki">
10-
</a>
11-
</p>
1+
<div style="text-align: center;">
2+
<h1><em>Repo-Viewer</em></h1>
3+
</div>
4+
5+
<div style="text-align: center;">
6+
<p>
7+
<strong>基于 Material Design 3设计风格的 GitHub仓库浏览应用</strong>
8+
&nbsp;&nbsp;
9+
<a href="https://deepwiki.com/UE-DND/Repo-Viewer">
10+
<img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki">
11+
</a>
12+
</p>
13+
</div>
1214

1315
![Preview Dark](docs/image/dark.png)
1416

@@ -20,9 +22,17 @@
2022
<tr>
2123
</table>
2224

25+
## 为 Repo Viewer 贡献代码
26+
27+
此项目已进入稳定阶段,本人将不再花过多精力维护。若发现已知的问题,欢迎任何形式的贡献!无论是修复错误、改进功能,还是提升代码质量,我们都非常欢迎您的参与。
28+
29+
> 此组织的所有成员均有管理员权限,若不想提交 Pull Request,直接推送代码是被允许的。
30+
>
31+
> 但在提交贡献前,推荐阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 以了解建议的代码规范和提交流程。
32+
2333
## 主要功能
2434

25-
- 📁 **仓库浏览**:直观的文件结构导航,同时提供首页文件和文件夹排除选项.
35+
- 📁 **仓库浏览**:直观的文件结构导航,同时提供首页文件与文件夹排除选项.
2636
- 🔎 **文件搜索**:支持基于自建索引和 Github API 的快速文件搜索,可按分支、路径前缀和扩展名过滤.
2737
- 📄 **文件预览**:多种文件格式预览,目前支持 `Markdown``PDF``图片`.
2838
- ⬇️ **文件下载**:可下载单个文件或整个文件夹.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "repo-viewer",
33
"private": true,
4-
"version": "1.3.9",
4+
"version": "1.4.0",
55
"type": "module",
66
"engines": {
77
"node": "24.x"
@@ -10,7 +10,7 @@
1010
"dev": "vite",
1111
"build": "tsc -p scripts/tsconfig.json && node scripts/dist/generateInitialContent.js && node scripts/dist/generateDocfindIndex.js && tsc && vite build",
1212
"generate:index": "tsc -p scripts/tsconfig.json && node scripts/dist/generateDocfindIndex.js",
13-
"install-deps": "npm install --legacy-peer-deps",
13+
"install-deps": "npm install",
1414
"lint": "eslint . --ext ts,tsx",
1515
"preview": "vite preview"
1616
},

scripts/generateDocfindIndex.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ const resolveBranchRef = async (repoPath: string, branch: string): Promise<strin
402402
await runCommandText("git", ["rev-parse", "--verify", candidate], repoPath);
403403
return candidate;
404404
} catch {
405-
continue;
405+
406406
}
407407
}
408408
return null;

scripts/generateInitialContent.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ const fetchJson = async <T>(url: string): Promise<T> => {
7878
if (!response.ok) {
7979
throw new Error(`Request failed: ${response.status} ${response.statusText}`);
8080
}
81-
return response.json() as Promise<T>;
81+
return (await response.json()) as T;
8282
};
8383

8484
const fetchText = async (url: string): Promise<string> => {
@@ -139,7 +139,9 @@ const run = async (): Promise<void> => {
139139
const contents = await fetchJson<unknown>(contentsUrl);
140140

141141
if (!Array.isArray(contents)) {
142-
throw new Error("Unexpected contents response");
142+
console.warn("[hydration] Unexpected contents response, writing null payload.");
143+
await writeOutput(null);
144+
return;
143145
}
144146

145147
const contentItems = contents.filter(isRecord);
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
import type { InitialContentHydrationPayload } from "@/types";
2-
3-
export const initialContentPayload: InitialContentHydrationPayload | null = null;
1+
export {};

src/components/file/FileList.tsx

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { AutoSizer } from "react-virtualized-auto-sizer";
55
import AlphabetIndex from "./AlphabetIndex";
66
import { RowComponent } from "./FileListRow";
77
import { FILE_ITEM_CONFIG, LIST_HEIGHT_CONFIG } from "./utils/fileListConfig";
8-
import { calculateLayoutMetrics } from "./utils/fileListLayout";
8+
import { calculateLayoutMetrics, getListPadding, getRowMetrics } from "./utils/fileListLayout";
99
import type { VirtualListItemData, FileListLayoutMetrics } from "./utils/types";
1010
import type { GitHubContent } from "@/types";
1111
import { theme } from "@/utils";
@@ -70,17 +70,8 @@ const FileList = React.memo<FileListProps>(
7070

7171
// 计算每个文件项的高度(包括间距)
7272
// 这个计算需要与 FileListItem 的实际高度保持一致
73-
const rowHeight = useMemo(() => {
74-
// 基础高度
75-
const baseHeight = isSmallScreen
76-
? FILE_ITEM_CONFIG.baseHeight.xs
77-
: FILE_ITEM_CONFIG.baseHeight.sm;
78-
79-
// 行间距(上下各分一半)
80-
const rowGap = FILE_ITEM_CONFIG.spacing.marginBottom;
81-
82-
// 计算总高度:基础高度 + 行间距
83-
return baseHeight + rowGap;
73+
const { rowHeight, rowPaddingBottom } = useMemo(() => {
74+
return getRowMetrics(isSmallScreen);
8475
}, [isSmallScreen]);
8576

8677
/**
@@ -200,6 +191,7 @@ const FileList = React.memo<FileListProps>(
200191
isScrolling,
201192
scrollSpeed,
202193
highlightedIndex,
194+
rowPaddingBottom,
203195
}),
204196
[
205197
contents,
@@ -214,26 +206,17 @@ const FileList = React.memo<FileListProps>(
214206
isScrolling,
215207
scrollSpeed,
216208
highlightedIndex,
209+
rowPaddingBottom,
217210
],
218211
);
219212

220213
// 简化的列表内边距计算
221-
const listPadding = useMemo((): { paddingTop: number; paddingBottom: number } => {
222-
// 非滚动模式:使用固定的对称内边距,稍微增加一点
223-
if (!needsScrolling) {
224-
const padding = isSmallScreen ? 16 : 20;
225-
return {
226-
paddingTop: padding - 4,
227-
paddingBottom: padding,
228-
};
229-
}
230-
231-
// 滚动模式:使用较小的内边距
232-
return {
233-
paddingTop: 0,
234-
paddingBottom: 8,
235-
};
236-
}, [needsScrolling, isSmallScreen]);
214+
// 列表内边距规则集中管理,区分滚动与非滚动模式。
215+
const listPadding = useMemo(
216+
(): { paddingTop: number; paddingBottom: number } =>
217+
getListPadding(needsScrolling, isSmallScreen),
218+
[needsScrolling, isSmallScreen],
219+
);
237220

238221
// 处理滚动事件(监听列表容器)
239222
React.useEffect(() => {

src/components/file/FileListItem.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import {
1717
Download as DownloadIcon,
1818
Cancel as CancelIcon,
1919
} from "@mui/icons-material";
20-
import { file, logger, theme as themeUtils } from "@/utils";
20+
import { logger, theme as themeUtils } from "@/utils";
21+
import { fileExtensionIcons } from "@/utils/files/fileHelpers";
2122
import type { GitHubContent } from "@/types";
2223
import { getFeaturesConfig } from "@/config";
2324
import { useI18n } from "@/contexts/I18nContext";
@@ -143,7 +144,7 @@ const FileListItem = memo<FileListItemProps>(
143144

144145
const extension = item.name.split('.').pop()?.toLowerCase();
145146
if (typeof extension === "string" && extension.length > 0) {
146-
const icon = file.fileExtensionIcons[extension];
147+
const icon = fileExtensionIcons[extension];
147148
if (icon !== undefined) {
148149
return icon;
149150
}
Lines changed: 4 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import React, { useEffect, useRef, useState } from "react";
2-
import type { ReactElement } from "react";
1+
import type { CSSProperties, ReactElement } from "react";
32
import { motion } from "framer-motion";
43
import type { MotionStyle } from "framer-motion";
54
import type { RowComponentProps } from "react-window";
65

76
import FileListItem from "./FileListItem";
8-
import { FILE_ITEM_CONFIG } from "./utils/fileListConfig";
97
import { getDynamicItemVariants, optimizedAnimationStyle } from "./utils/fileListAnimations";
108
import type { VirtualListItemData } from "./utils/types";
119

@@ -15,8 +13,6 @@ const RowComponent = ({
1513
style,
1614
...rowData
1715
}: RowComponentProps<VirtualListItemData>): ReactElement => {
18-
const rowRef = useRef<HTMLDivElement>(null);
19-
const [isVisible, setIsVisible] = useState(true);
2016
const {
2117
contents,
2218
downloadingPath,
@@ -30,42 +26,15 @@ const RowComponent = ({
3026
isScrolling,
3127
scrollSpeed,
3228
highlightedIndex,
29+
rowPaddingBottom,
3330
} = rowData;
3431

3532
const item = contents[index];
3633

37-
useEffect(() => {
38-
const element = rowRef.current;
39-
if (element === null) {
40-
return;
41-
}
42-
43-
const scrollContainer = element.closest('.virtual-file-list');
44-
45-
const observer = new IntersectionObserver(
46-
(entries) => {
47-
entries.forEach((entry) => {
48-
setIsVisible(entry.isIntersecting);
49-
});
50-
},
51-
{
52-
root: scrollContainer ?? null,
53-
rootMargin: "100px",
54-
threshold: 0.01,
55-
}
56-
);
57-
58-
observer.observe(element);
59-
60-
return () => {
61-
observer.disconnect();
62-
};
63-
}, []);
6434

6535
if (item === undefined) {
6636
return (
6737
<div
68-
ref={rowRef}
6938
style={style}
7039
{...ariaAttributes}
7140
aria-hidden="true"
@@ -75,7 +44,7 @@ const RowComponent = ({
7544

7645
const isHighlighted = highlightedIndex === index;
7746

78-
const adjustedStyle: React.CSSProperties = {
47+
const adjustedStyle: CSSProperties = {
7948
...style,
8049
boxSizing: "border-box",
8150
alignItems: "flex-start",
@@ -85,7 +54,7 @@ const RowComponent = ({
8554
height: "100%",
8655
width: "100%",
8756
paddingTop: 0,
88-
paddingBottom: FILE_ITEM_CONFIG.spacing.marginBottom,
57+
paddingBottom: rowPaddingBottom,
8958
paddingRight: "12px",
9059
boxSizing: "border-box",
9160
...optimizedAnimationStyle,
@@ -95,11 +64,9 @@ const RowComponent = ({
9564

9665
return (
9766
<div
98-
ref={rowRef}
9967
style={adjustedStyle}
10068
className="file-list-item-container"
10169
{...ariaAttributes}
102-
aria-hidden={!isVisible ? "true" : undefined}
10370
data-oid="_c:db-1"
10471
>
10572
<motion.div
@@ -122,18 +89,11 @@ const RowComponent = ({
12289
currentPath={currentPath}
12390
contents={contents}
12491
isHighlighted={isHighlighted}
125-
isVisible={isVisible}
12692
data-oid="k4zj3qr"
12793
/>
12894
</motion.div>
12995
</div>
13096
);
13197
};
13298

133-
const Row = React.memo(RowComponent);
134-
135-
Row.displayName = "FileListRow";
136-
13799
export { RowComponent };
138-
139-
export default Row;

0 commit comments

Comments
 (0)