Compare commits

...

53 Commits

Author SHA1 Message Date
Donkey_kevin c36ae092a0 fix: 修复Docusaurus构建失败问题
- 移除未定义的ProgressPanel组件引用
- 移除错误的TypeScript导入语句
- 修复搜索插件配置问题

修复了npm run build构建失败的问题,现在可以成功生成静态文件。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 15:36:03 +08:00
Donkey_kevin 907b40b3d7 Merge remote-tracking branch 'origin/local-changes-merge'
保留本地更完善的阅读进度追踪实现:
- 支持调试接口
- 更完善的选择器和错误处理
- 阅读进度条功能
- URL 编码/解码支持

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 15:07:59 +08:00
Donkey_kevin c0231e0562 1 2026-05-13 14:56:29 +08:00
Donkey_kevin 6a196f1925 feat: 添加文档阅读进度追踪功能
- 新增阅读进度条,显示页面滚动进度
- 实现侧边栏绿色小勾标记,标识已阅读文档
- 阅读记录保存在浏览器本地存储
- 支持中文路径的 URL 编码/解码
- 添加调试功能方便问题排查

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 14:54:55 +08:00
zzx-coder d03d01d20f Update Dockerfile 2026-05-13 22:14:05 +08:00
zzx-coder 2471344b5f Merge pull request '增加了部分功能' (#14) from zhouzexin_branch into master 2026-05-13 21:59:46 +08:00
mengcheng 877416efcc Merge pull request '新增用户点赞/点踩功能' (#13) from mc_branch into zhouzexin_branch 2026-05-13 20:47:29 +08:00
camelliamc 6e6a753b27 新增用户点赞/点踩功能 2026-05-13 20:44:23 +08:00
camelliamc a81a27b5b8 修复发现的所有缺陷 2026-05-13 15:12:51 +08:00
zzx-coder 31d0fed7b1 Merge pull request '修改背景颜色' (#11) from local-changes-merge into zhouzexin_branch 2026-05-13 14:53:16 +08:00
Donkey_kevin b572d5cc02 feat: 添加文档阅读进度追踪功能
- 添加滚动进度自动检测,滚动到90%自动标记为已读
- 侧边栏显示已读文档绿色对勾标记
- 支持 localStorage 本地存储,刷新页面标记保留
- 自动处理 URL 编码路径问题
- 支持所有文档分类(快速开始、代码库、合并请求等)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-05-13 08:58:22 +08:00
Donkey_kevin fb590caf65 小对勾 2026-05-13 08:55:16 +08:00
zzx-coder 8636b658bd Merge pull request 'docker配置修改' (#10) from zhouzexin_branch into master 2026-05-12 23:31:28 +08:00
zzx-coder 392dcd1194 fix: temporarily disable incompatible search plugin to resolve build errors 2026-05-12 23:30:03 +08:00
zzx-coder 858cc50a24 Merge pull request 'docker配置修改' (#9) from zhouzexin_branch into master 2026-05-12 23:19:17 +08:00
zzx-coder 9f84857c78 fix: upgrade to Node.js 20 to meet Docusaurus 3.x requirements 2026-05-12 23:18:20 +08:00
zzx-coder 43bd26d83a fix: increase Node.js memory limit for build process 2026-05-12 23:09:28 +08:00
zzx-coder 293675001a Merge pull request 'docker配置修改' (#8) from zhouzexin_branch into master 2026-05-12 22:54:54 +08:00
zzx-coder 0d20ee0ed2 fix: use npm install with legacy-peer-deps to resolve dependency conflicts 2026-05-12 22:45:18 +08:00
zzx-coder a1b1f5a98b Merge pull request 'docker配置修改' (#7) from zhouzexin_branch into master 2026-05-12 22:39:12 +08:00
zzx-coder 5313d0a92d fix: set USER root before system package installation 2026-05-12 22:38:20 +08:00
zzx-coder b938837b2c Merge pull request 'docker配置修改' (#6) from zhouzexin_branch into master 2026-05-12 22:35:26 +08:00
zzx-coder 9042c56474 fix: adapt Dockerfile for Red Hat ubi8 base image using yum 2026-05-12 22:34:53 +08:00
zzx-coder 581b4874a1 Merge pull request 'docker配置修改' (#5) from zhouzexin_branch into master 2026-05-12 22:30:43 +08:00
zzx-coder ca4191549f fix: use npm instead of yarn for dependency management 2026-05-12 22:28:36 +08:00
zzx-coder 49cd589dd0 Merge pull request 'docker配置修改' (#4) from zhouzexin_branch into master 2026-05-12 22:23:48 +08:00
zzx-coder 218036d855 fix: use private aliyun registry base image for node:18-alpine 2026-05-12 22:22:05 +08:00
zzx-coder a8693d16c5 revert: use official node:18-alpine base image 2026-05-12 21:45:45 +08:00
zzx-coder c947a39409 Merge pull request 'docker配置修改' (#3) from zhouzexin_branch into master 2026-05-12 21:06:28 +08:00
zzx-coder a71e88b167 fix: use aliyun library mirror for node:18-alpine base image 2026-05-12 21:04:42 +08:00
zzx-coder f7760e738b Merge pull request 'docker配置修改' (#2) from zhouzexin_branch into master 2026-05-12 20:59:42 +08:00
zzx-coder 2bfbd34bfc optimize: improve Dockerfile with aliyun mirror and best practices
- Use aliyun mirror for node:18-alpine base image
- Add system dependencies for npm package compilation
- Optimize build caching with layered COPY
- Fix build command to remove incorrect locale parameter
- Add EXPOSE 3000 for container networking
- Add environment variables NODE_ENV and PORT
- Add healthcheck for container monitoring
- Improve workdir naming and structure
2026-05-12 20:59:07 +08:00
zzx-coder 1da78bc2de refactor: .devops/gitlink-help-autodeploy.yml 2026-05-12 20:45:33 +08:00
zzx-coder eb7eec5c56 refactor: .devops/gitlink-help-autodeploy.yml 2026-05-12 20:45:16 +08:00
zzx-coder 0020bf468d refactor: .devops/gitlink-help-autodeploy.yml 2026-05-12 20:07:15 +08:00
zzx-coder 79db184e3e refactor: .devops/gitlink-help-autodeploy.yml 2026-05-12 20:07:13 +08:00
zzx-coder 2108ec3e3b refactor: .devops/gitlink-help-autodeploy.yml 2026-05-12 20:03:52 +08:00
zzx-coder a7cda42e59 refactor: .devops/gitlink-help-autodeploy.yml 2026-05-12 20:03:44 +08:00
zzx-coder 5603bde83c refactor: .devops/gitlink-help-autodeploy.yml 2026-05-12 19:44:35 +08:00
zzx-coder 4404ead1c2 refactor: .devops/gitlink-help-autodeploy.yml 2026-05-12 19:29:55 +08:00
zzx-coder 5e697cbb51 feat: .devops/gitlink-help-autodeploy.yml 2026-05-12 19:29:12 +08:00
zzx-coder 640dac5ecd refactor: delete .devops/帮助中心.yml 2026-05-12 19:29:11 +08:00
mengcheng acbca0caee feat: .devops/帮助中心.yml 2026-05-12 18:41:26 +08:00
mengcheng 3d3d6167a0 refactor: delete .devops/gitlink-help-autodeploy.yml 2026-05-12 18:41:24 +08:00
zzx-coder cd145221a2 refactor: .devops/gitlink-help-autodeploy.yml 2026-05-12 17:19:01 +08:00
zzx-coder 631a55a43a refactor: .devops/gitlink-help-autodeploy.yml 2026-05-12 17:09:47 +08:00
zzx-coder bde3dc87ac feat: .devops/gitlink-help-autodeploy.yml 2026-05-12 17:07:57 +08:00
zzx-coder a6490b7bb7 refactor: delete .devops/帮助中心.yml 2026-05-12 17:07:56 +08:00
mengcheng bc2b1d99c1 refactor: .devops/帮助中心.yml 2026-05-12 15:51:15 +08:00
Donkey_kevin ac21ca3e67 修改界面 2026-05-12 15:49:11 +08:00
Donkey_kevin 30aa2573e8 合并本地修改到远程最新代码 2026-05-12 15:37:17 +08:00
nudt_zk 9d1fc94dd6 refactor: .devops/帮助中心.yml 2026-05-12 12:53:47 +08:00
mengcheng a28bcc64b9 feat: .devops/帮助中心.yml 2026-05-11 20:01:46 +08:00
25 changed files with 895 additions and 85 deletions

View File

@ -0,0 +1,25 @@
{
"permissions": {
"allow": [
"Bash(npm run:*)",
"Bash(npm install:*)",
"Bash(netstat -ano)",
"Bash(powershell -Command \"Get-NetTCPConnection -LocalPort 3000 -ErrorAction SilentlyContinue | Select-Object LocalPort, State\")",
"Bash(findstr \":3000\")",
"Bash(findstr \"LISTENING\")",
"Bash(git config:*)",
"Bash(git fetch:*)",
"Bash(git add:*)",
"Bash(git commit:*)",
"Bash(git push:*)",
"Bash(start:*)",
"Bash(git remote:*)",
"Bash(ssh-keygen:*)",
"Bash(ssh -T git@gitlink.org.cn)",
"Bash(git branch:*)",
"Bash(timeout 5 curl -s http://localhost:3000)",
"Bash(timeout 8 curl -s http://localhost:3000)",
"Bash(curl -s http://localhost:3000)"
]
}
}

View File

@ -0,0 +1,57 @@
version: 2
name: gitlink-help-autodeploy
description: ""
global:
concurrent: 1
trigger:
webhook: gitlink@1.0.0
event:
- ref: push
ruleset-operator: AND
workflow:
- ref: start
name: 开始
task: start
- ref: git_clone_0
name: git clone
task: git_clone@1.2.9
input:
remote_url: '"https://gitlink.org.cn/zzx-coder/gitlink_help_center.git"'
ref: '"refs/heads/master"'
commit_id: '""'
depth: 1
needs:
- start
- ref: docker_image_build_0
name: docker镜像构建
task: docker_image_build@1.6.0
input:
docker_username: ((aliyun.aliyun_username))
docker_password: ((aliyun.aliyun_password))
image_name: '"crpi-1x701qgqjia2gdeu.cn-chengdu.personal.cr.aliyuncs.com/gitlink-help-center/gitlink-help-center"'
image_tag: '"latest"'
registry_address: '"crpi-1x701qgqjia2gdeu.cn-chengdu.personal.cr.aliyuncs.com"'
docker_file: '"Dockerfile"'
docker_build_path: '"."'
workspace: ((git_clone_0.git_path))
image_push: true
build_args: '""'
needs:
- git_clone_0
- ref: ssh_cmd_0
name: ssh执行命令
task: ssh_cmd@1.1.1
input:
ssh_private_key: ((gitlink_help_center_ssh_key.gitlink_help_center_ssh_key))
ssh_ip: '"121.41.222.0"'
ssh_port: '"22"'
ssh_user: '"root"'
ssh_cmd: '"/opt/gitlink-deploy/deploy.sh"'
needs:
- docker_image_build_0
- ref: end
name: 结束
task: end
needs:
- ssh_cmd_0

View File

@ -1,10 +1,34 @@
FROM node:18-alpine
FROM crpi-1x701qgqjia2gdeu.cn-chengdu.personal.cr.aliyuncs.com/gitlink-help-center/gitlink-help-center:node-20-alpine
LABEL maintainer="yuankaifneg <2894340009@qq.com>"
WORKDIR /gitlink_help_center
# 设置为root用户以安装系统依赖
USER root
COPY ./ /gitlink_help_center/
RUN yarn install
RUN npm run build -- --locale zh-cn
# 安装必要的系统依赖
RUN yum install -y \
git \
python3 \
make \
gcc-c++ \
bash && \
yum clean all
# 设置工作目录
WORKDIR /app
# 优先复制package.json和package-lock.json以利用Docker缓存
COPY package.json package-lock.json ./
# 安装依赖忽略peer依赖冲突
RUN npm install --legacy-peer-deps
# 复制项目文件
COPY . .
# 构建应用
RUN npm run build
# 暴露端口
EXPOSE 3000
# 设置环境变量
ENV NODE_ENV=production
ENV PORT=3000
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
# 启动应用
CMD ["npm", "run", "serve"]

View File

@ -8,39 +8,39 @@ sidebar_position: 1
![img](../../static/img/建站工具/1.png)
点击左面板中 个人建站 - 我的站点
点击左面板中 个人建站 - 我的站点
![img](../../static/img/建站工具/3.png)
### 创建站点
我的站点界面点击 新建站点 按钮,进入新建站点界面
**我的站点**界面点击**新建站点**按钮,进入新建站点界面
在新建站点界面输入站点名称,此名称将在我的站点列表中展示
在新建站点界面输入站点名称,此名称将在**我的站点**的**站点列表**中展示
并且选择对应的建站工具与主题我们为您提供了3种不同的工具每个工具10种主题一共30种供您挑选
![img](../../static/img/建站工具/4.png)
填写完毕后,点击页面下方蓝色按钮 创建站点
填写完毕后,点击页面下方蓝色按钮**创建站点**
![img](../../static/img/建站工具/5.png)
这样您就拥有了一个网站,并且有了一个代码仓库。
在仓库的 服务 一栏中 个人建站服务的操作界面,这里您可以查看一些您的站点状态、站点名称、网站地址、建站工具、建站时间
在仓库的**服务**一栏中**个人建站**服务的操作界面,这里您可以查看一些您的站点状态、站点名称、网站地址、建站工具、建站时间
### 部署站点
![img](../../static/img/建站工具/6.png)
点击去部署按钮,选择想要部署的分支后点击确定
点击**去部署**按钮,选择想要部署的分支后点击**确定**
![img](../../static/img/建站工具/7.png)
等待程序运行一会儿后,会返回一些服务器部署信息给您
等待程序运行一会儿后,会返回一些服务器部署信息给您,部署成功后您就可以访问站点了。
部署成功后您就可以访问站点了。点击网站地址就可以跳转
点击网站地址就可以跳转
![img](../../static/img/建站工具/8.png)

View File

@ -3,15 +3,15 @@ sidebar_label: '成员管理'
sidebar_position: 7
---
### **1. 成员管理入口**
在仓库主页,点击“仓库设置”按键,再点击”成员管理“按键,即可进入成员管理模块,如下图所示。
在仓库主页,点击**仓库设置**按键,再点击**成员管理**按键,即可进入成员管理模块,如下图所示。
![](../../static/img/代码库管理/成员管理/成员管理入口.png)<br/>
### **2. 筛选、搜索项目成员**
进入成员管理模块后,可以通过”角色筛选“按键来筛选项目成员类型,通过”搜索“按键可以检索具体的项目成员,如下图所示。
进入成员管理模块后,可以通过**角色筛选**按键来筛选项目成员类型,通过**搜索**按键可以检索具体的项目成员,如下图所示。
![](../../static/img/代码库管理/成员管理/筛选、搜索项目成员.png)<br/>
### **3. 筛选、搜索项目成员**
进入成员管理模块后,可以通过”添加成员“按键来添加项目成员,左侧搜索框检索到具体用户并选中后,单击”添加成员“按键即可成功添加项目成员。
### **3. 添加成员**
进入成员管理模块后,可以通过**添加成员**按键来添加项目成员,左侧搜索框检索到具体用户并选中后,单击**添加成员**按键即可成功添加项目成员。
![](../../static/img/代码库管理/成员管理/添加成员.png)<br/>
### **4. 项目成员权限管理**
@ -19,5 +19,5 @@ sidebar_position: 7
![](../../static/img/代码库管理/成员管理/项目成员权限管理.png)<br/>
### **5. 删除项目成员**
进入成员管理模块后,单击项目成员右侧的”删除“按键,可以删除改名项目成员,如下图所示。
进入成员管理模块后,单击项目成员右侧的**删除**按键,可以删除改名项目成员,如下图所示。
![](../../static/img/代码库管理/成员管理/删除项目成员.png)<br/>

View File

@ -3,4 +3,6 @@ sidebar_label: '合并请求关联疑修'
sidebar_position: 5
---
# 合并请求关联疑修
# 合并请求关联疑修
该功能还在开发完善中,敬请期待......

View File

@ -1,4 +1,12 @@
---
sidebar_label: '平台公告'
sidebar_position: 1
---
---
# 平台公告
GitLink帮助中心致力于帮助开发者快速熟悉平台功能掌握开源协作的正确方法。在这里您可以找到从账号注册到项目管理的完整指南也可以通过实际使用中发现平台存在的问题并提出改进建议。我们希望通过文档与社区的共同努力让GitLink成为更好的开源创新服务平台。如果您在使用过程中遇到任何问题欢迎在平台中提交疑修Issue获得帮助。
帮助中心项目地址是https://gitlink.org.cn/Gitlink/gitlink_help_center/
开发者可以先复刻帮助中心项目,然后在本地进行开发。

View File

@ -33,7 +33,7 @@ Gitee配置方式为个人头像→设置→私人令牌→生成新令牌→
![5.配置webhook](../../static/img/reposyncer/5.配置webhook.jpg)
②访问点击前往按钮前往目标仓库webhook页新建一个webhook以github为例
②访问点击前往按钮前往目标仓库webhook页新建一个webhook以github为例
![6.webhook示例](../../static/img/reposyncer/6.webhook示例.png)

View File

@ -2,13 +2,13 @@
sidebar_label: '组织创建及设置'
sidebar_position: 2
---
# 组织创建Organization Creation
# 组织创建
*https://www.gitlink.org.cn* 页面点击顶部导航栏的“+”符号可以进行组织新建操作。
在 https://www.gitlink.org.cn 页面点击顶部导航栏的“+”符号可以进行组织新建操作。
![](/img/Org/imageOrg1.png)
在新建页面中输入**组织账号****组织名称****组织描述****所在地区****可见性**以及**组织头像**等信息后,点击**创建组织**按钮完成组织的创建。
在新建页面中输入组织账号、组织名称、组织描述、所在地区、可见性以及组织头像等信息后,点击**创建组织**按钮完成组织的创建。
![](/img/Org/imageOrg2.png)

View File

@ -7,7 +7,7 @@ sidebar_position: 3
## 创建组织团队
点击组织信息页面中的“新建团队”按钮可以创建属于该组织的团队平台默认创建“Owners”团队成员为组织的创建者
在团队新建页面,输入团队标识、团队名称、团队描述、项目权限以及版本库权限等信息后,点击“新建团队”完成团队的创建。
在团队新建页面,输入团队标识、团队名称、团队描述、项目权限以及版本库权限等信息后,点击 **新建团队** 按钮完成团队的创建。
![](/img/Org/imageOrg9.png)
@ -19,7 +19,7 @@ sidebar_position: 3
## 管理组织团队
点击团队信息页面中的“团队设置”按钮可以对团队进行管理
点击团队信息页面中的 **团队设置** 按钮可以对团队进行管理。
- 基本设置:修改项目的基本信息,如名称和描述等。
![](/img/Org/imageOrg10.png)

View File

@ -2,9 +2,9 @@
sidebar_label: '组织成员管理'
sidebar_position: 4
---
# 成员管理Members Management
# 成员管理
个人所管理的项目当中的**仓库设置**当中的**成员管理**可以进入成员管理界面
在个人所管理的项目当中的**仓库设置**当中的**成员管理**可以进入成员管理界面
![](/img/Org/imageOrg14.png)

View File

@ -3,9 +3,7 @@ sidebar_label: '模板导入及导出'
sidebar_position: 2
---
# 确实开源帮助中心
## 模板的导入及导出管理
# 模板导入及导出
#### 1.模板导入
通过点击wiki界面的**导入模板**按钮即可导入本地的txtmarkdown等格式文件进行Wiki文档的创建当前平台此项功能尚在开发测试环节
[](/img/wiki/img4.png)

View File

@ -4,20 +4,21 @@ sidebar_position: 1
---
# 确实开源帮助中心
# 维基页面管理
## 维基界面管理
#### 1.维基界面
GitLink项目安排了专门的**维基(Wiki)界面**来展示项目的一些情况
[](/img/wiki/img1.png)
GitLink项目安排了专门的**维基(Wiki)界面**来展示项目的一些情况
![](/img/wiki/img1.png)
#### 2.创建维基内容
在初始化的维基界面当中点击**创建Wiki文档**或者**导入模板**即可进行Wiki文档的编辑
在初始化的维基界面当中点击**创建Wiki文档**或者**导入模板**即可进行Wiki文档的编辑
<br/>
![](/img/wiki/img4.png)
#### 3.进行Wiki文档的编辑
点击**创建Wiki文档**后即跳转到编辑界面,初始内容为空白的文档
[](/img/wiki/img3.png)
也可以通过勾选*添加模板*来进行模板的导入,平台提供的模板包括*周报*和*月报*的格式
[](/img/wiki/img2.png)
完成编辑后点击左下角的的保存即可
![](/img/wiki/img3.png)
也可以通过勾选**添加模板**来进行模板的导入,平台提供的模板包括*周报*和*月报*的格式
![](/img/wiki/img2.png)
完成编辑后点击左下角的的**保存**即可

View File

@ -3,9 +3,8 @@ sidebar_label: '通知简介'
sidebar_position: 1
---
# 确实开源帮助中心
# 通知简介
## 通知简介
#### 1.通知类型
GitLink将通知分为“系统通知”和“@我”的两种类型:
* 系统通知包括“我的状态”、“我创建的或负责的”、“我管理的仓库”三种类型的通知:
@ -25,5 +24,5 @@ GitLink将通知分为“系统通知”和“@我”的两种类型:
<br/>
点击通知按钮可以进入消息通知界面,“我的通知”界面可以进行“进查看未读消息”和“所有消息一键已读”的选择。
点击通知按钮可以进入消息通知界面,“我的通知”界面可以进行“仅看未读消息”和“所有系统通知一键已读”的选择。
![](/img/notice/my_notice2.png)

View File

@ -3,19 +3,16 @@ sidebar_label: '通知设置'
sidebar_position: 2
---
# 确实开源帮助中心
## 通知设置
# 通知设置
#### 1.进入通知设置界面
通过点击首页的通知按钮进入通知设置界面
通过点击首页的通知按钮进入通知设置界面
![](/img/notice/home_notice.png)
<br/>
或在头像下拉列表中选择设置可以进入消息通知设置界面
或在头像下拉列表中选择设置可以进入消息通知设置界面
![](/img/notice/into_notice.png)
#### 2.进行通知设置
通过“通知管理”可以对接受通知的方式进行设置,默认所有通知都是通过站内信的方式接受,可以通过勾选为重要的通知类型增加邮件接受方式。
通过**通知管理**可以对接受通知的方式进行设置,默认所有通知都是通过站内信的方式接受,可以通过勾选为重要的通知类型增加邮件接受方式。
![](/img/notice/notice_set.png)

View File

@ -3,7 +3,6 @@ const darkCodeTheme = require('prism-react-renderer/themes/dracula');
//Docusaurus v3写法
// const { themes } = require('prism-react-renderer');
// import { themes } from 'prism-react-renderer';
import("@easyops-cn/docusaurus-search-local").PluginOptions;
/** @type {import('@docusaurus/types').DocusaurusConfig} */
module.exports = {
@ -16,7 +15,13 @@ module.exports = {
favicon: 'img/icon.ico',
organizationName: 'luffyZh', // Usually your GitHub org/user name.
projectName: 'docusaurus-luffyzh-website', // Usually your repo name.
scripts: [],
scripts: [
{
src: '/js/reading-progress.js',
async: true,
defer: true,
},
],
// stylesheets: ['styles/dark-mode.css'],
themeConfig: {
docs:{
@ -176,6 +181,18 @@ module.exports = {
// sidebarPath: require.resolve('./sidebars.js'),
editUrl:'https://www.gitlink.org.cn/Gitlink/gitlink_help_center/tree/master/',
routeBasePath: "/",
remarkPlugins: [
async () => {
const { visit } = await import('unist-util-visit');
return (tree) => {
visit(tree, 'mdxJsxFlowElement', (node) => {
if (node.name === 'ProgressPanel') {
node.attributes = node.attributes || [];
}
});
};
},
],
},
theme: {
customCss: require.resolve('./src/css/custom.css'),
@ -193,7 +210,6 @@ module.exports = {
hashed: true,
language: ["en", "zh"],
highlightSearchTermsOnTargetPage: true,
blogRouteBasePath: "/",
explicitSearchResultPath: true,
// For Docs using Chinese, The `language` is recommended to set to:
// ```

1
gitlink_help_center Submodule

@ -0,0 +1 @@
Subproject commit 293675001acfe7c74caa0bcf78765ec29034c715

27
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "docusaurus",
"version": "0.0.0",
"version": "3.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "docusaurus",
"version": "0.0.0",
"version": "3.0.0",
"dependencies": {
"@cmfcmf/docusaurus-search-local": "1.1.0",
"@docusaurus/core": "3.9.2",
@ -26,7 +26,7 @@
"react-modal": "3.16.1"
},
"engines": {
"node": ">=14.18.1 <=18.x"
"node": ">=18.x"
}
},
"node_modules/@algolia/abtesting": {
@ -19901,13 +19901,6 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/search-insights": {
"version": "2.17.3",
"resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz",
"integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==",
"license": "MIT",
"peer": true
},
"node_modules/section-matter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
@ -20994,20 +20987,6 @@
"is-typedarray": "^1.0.0"
}
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici": {
"version": "7.22.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz",

View File

@ -1,9 +1,8 @@
module.exports = {
DocsSidebar: [
docs: [
{
type: 'doc',
id: 'index',
label: 'Docs',
}
type: 'autogenerated',
dirName: '.', // 自动生成 docs/ 下所有文档的侧边栏
},
],
};
};

View File

@ -0,0 +1,151 @@
import { useState, useEffect } from 'react';
const GITEE_API = 'https://gitee.com/api/v5/repos/camelliamc/gitlink-feedback';
const TOKEN = 'f8433b20f2a07a28cf95aa0e17edfac4';
const getDocTitle = (path) => {
const cleanPath = path.replace(/^\//, '');
return `Vote_${cleanPath.replace(/\//g, '_')}`;
};
export default function VoteButtons() {
const [upvotes, setUpvotes] = useState(0);
const [downvotes, setDownvotes] = useState(0);
const [userVote, setUserVote] = useState(null); // null | 'up' | 'down'
const [docPath, setDocPath] = useState('');
const [loading, setLoading] = useState(false);
useEffect(() => {
const path = window.location.pathname;
setDocPath(path);
const stored = localStorage.getItem(`vote_${path}`);
if (stored === 'up' || stored === 'down') {
setUserVote(stored);
}
fetchVotes(path);
}, []);
const fetchVotes = async (path) => {
try {
const res = await fetch(`${GITEE_API}/issues?access_token=${TOKEN}&state=open&labels=vote`);
const data = await res.json();
const title = getDocTitle(path);
const issue = data.find(i => i.title === title);
if (issue) {
const labels = issue.labels || [];
const upLabel = labels.find(l => l.name.startsWith('up:'));
const downLabel = labels.find(l => l.name.startsWith('down:'));
if (upLabel) setUpvotes(parseInt(upLabel.name.split(':')[1]) || 0);
if (downLabel) setDownvotes(parseInt(downLabel.name.split(':')[1]) || 0);
}
} catch (e) {
console.error('获取投票失败:', e);
}
};
const updateGiteeIssue = async (newUp, newDown) => {
const title = getDocTitle(docPath);
const res = await fetch(`${GITEE_API}/issues?access_token=${TOKEN}&state=open&labels=vote`);
const data = await res.json();
const existingIssue = data.find(i => i.title === title);
const labels = [`vote`, `up:${newUp}`, `down:${newDown}`];
if (existingIssue) {
await fetch(`${GITEE_API}/issues/${existingIssue.number}?access_token=${TOKEN}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ labels })
});
} else {
await fetch(`${GITEE_API}/issues?access_token=${TOKEN}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title,
body: `文档投票 - ${docPath}`,
labels
})
});
}
};
const handleVote = async (type) => {
if (loading || !docPath) return;
setLoading(true);
try {
let newUp = upvotes;
let newDown = downvotes;
let newVote = null;
if (userVote === null) {
// 未投票 → 投票
if (type === 'up') {
newUp = upvotes + 1;
} else {
newDown = downvotes + 1;
}
newVote = type;
} else if (userVote === type) {
// 点击已选中的 → 取消投票
if (type === 'up') {
newUp = Math.max(0, upvotes - 1);
} else {
newDown = Math.max(0, downvotes - 1);
}
newVote = null;
} else {
// 切换投票
if (type === 'up') {
newUp = upvotes + 1;
newDown = Math.max(0, downvotes - 1);
} else {
newDown = downvotes + 1;
newUp = Math.max(0, upvotes - 1);
}
newVote = type;
}
await updateGiteeIssue(newUp, newDown);
setUpvotes(newUp);
setDownvotes(newDown);
setUserVote(newVote);
if (newVote) {
localStorage.setItem(`vote_${docPath}`, newVote);
} else {
localStorage.removeItem(`vote_${docPath}`);
}
} catch (e) {
console.error('投票失败:', e);
} finally {
setLoading(false);
}
};
return (
<div className="vote-container">
<span className="vote-label">这篇文档有帮助吗</span>
<div className="vote-buttons">
<button
onClick={() => handleVote('up')}
className={`vote-btn vote-btn-up ${userVote === 'up' ? 'vote-btn-active-up' : ''}`}
title={userVote === 'up' ? '取消投票' : '有帮助'}
>
<span className="vote-emoji">👍</span>
<span className="vote-count">{upvotes}</span>
</button>
<button
onClick={() => handleVote('down')}
className={`vote-btn vote-btn-down ${userVote === 'down' ? 'vote-btn-active-down' : ''}`}
title={userVote === 'down' ? '取消投票' : '没帮助'}
>
<span className="vote-emoji">👎</span>
<span className="vote-count">{downvotes}</span>
</button>
</div>
</div>
);
}

View File

@ -148,4 +148,208 @@ html[data-theme='dark'] .docusaurus-highlight-code-line {
.widget{
display: none!important;
opacity: 0;
}
}
/* 帮助文档卡片背景 */
.cardContainer {
background-color: #e6f0ff !important;
border-radius: 12px;
transition: all 0.3s ease;
}
.cardContainer:hover {
background-color: #cce0ff !important;
box-shadow: 0 4px 12px rgba(70, 106, 255, 0.15);
}
/* 阅读进度条 */
.reading-progress {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 4px;
background-color: rgba(70, 106, 255, 0.1);
z-index: 9999;
}
.reading-progress__bar {
height: 100%;
background: linear-gradient(90deg, #466aff, #6b8cff);
transition: width 0.15s ease-out;
}
/* 侧边栏完成标记 */
.sidebar-completed-badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
background-color: #22c55e;
color: white;
border-radius: 50%;
font-size: 10px;
font-weight: bold;
flex-shrink: 0;
}
/* 进度面板 */
.progress-panel {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 16px;
border-radius: 12px;
color: white;
margin-bottom: 16px;
}
.progress-panel__title {
font-size: 14px;
font-weight: 600;
margin-bottom: 8px;
}
.progress-panel__bar {
height: 8px;
background-color: rgba(255, 255, 255, 0.3);
border-radius: 4px;
overflow: hidden;
margin-bottom: 8px;
}
.progress-panel__fill {
height: 100%;
background-color: #22c55e;
border-radius: 4px;
transition: width 0.3s ease;
}
.progress-panel__text {
font-size: 12px;
opacity: 0.9;
}
/* ===== 文档投票组件样式 ===== */
.vote-container {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 1.2rem;
padding: 1.5rem 0 0.5rem 0;
margin-top: 1.5rem;
border-top: 1px solid var(--ifm-toc-border-color);
}
.vote-label {
font-weight: 500;
color: var(--ifm-color-emphasis-600);
font-size: 0.95rem;
white-space: nowrap;
}
.vote-buttons {
display: flex;
align-items: center;
gap: 0.6rem;
}
.vote-btn {
display: flex;
align-items: center;
gap: 0.35rem;
padding: 0.4rem 0.85rem;
border: none;
border-radius: 20px;
background: var(--ifm-color-emphasis-100);
cursor: pointer;
font-size: 1rem;
transition: all 0.2s ease;
user-select: none;
outline: none;
}
.vote-btn:hover {
background: var(--ifm-color-emphasis-200);
transform: scale(1.05);
}
.vote-btn:active {
transform: scale(0.97);
}
.vote-emoji {
font-size: 1.25rem;
line-height: 1;
}
.vote-count {
color: var(--ifm-color-emphasis-700);
font-weight: 600;
font-size: 0.95rem;
min-width: 1ch;
text-align: center;
}
/* 👍 高亮状态 */
.vote-btn-active-up {
background: #e6f0ff;
box-shadow: 0 0 0 2px rgba(70, 106, 255, 0.25);
}
.vote-btn-active-up:hover {
background: #d6e4ff;
}
.vote-btn-active-up .vote-count {
color: #466aff;
}
/* 👎 高亮状态 */
.vote-btn-active-down {
background: #fff0e6;
box-shadow: 0 0 0 2px rgba(255, 102, 0, 0.25);
}
.vote-btn-active-down:hover {
background: #ffe4d0;
}
.vote-btn-active-down .vote-count {
color: #ff6600;
}
/* 暗色模式适配 */
html[data-theme='dark'] .vote-btn {
background: var(--ifm-color-emphasis-200);
}
html[data-theme='dark'] .vote-btn:hover {
background: var(--ifm-color-emphasis-300);
}
html[data-theme='dark'] .vote-count {
color: var(--ifm-color-emphasis-600);
}
html[data-theme='dark'] .vote-btn-active-up {
background: rgba(70, 106, 255, 0.2);
box-shadow: 0 0 0 2px rgba(70, 106, 255, 0.35);
}
html[data-theme='dark'] .vote-btn-active-up:hover {
background: rgba(70, 106, 255, 0.3);
}
html[data-theme='dark'] .vote-btn-active-up .vote-count {
color: #8aa3ff;
}
html[data-theme='dark'] .vote-btn-active-down {
background: rgba(255, 102, 0, 0.15);
box-shadow: 0 0 0 2px rgba(255, 102, 0, 0.3);
}
html[data-theme='dark'] .vote-btn-active-down:hover {
background: rgba(255, 102, 0, 0.25);
}
html[data-theme='dark'] .vote-btn-active-down .vote-count {
color: #ff9944;
}

View File

@ -0,0 +1,57 @@
import React from 'react';
import clsx from 'clsx';
import {useWindowSize} from '@docusaurus/theme-common';
import {useDoc} from '@docusaurus/plugin-content-docs/client';
import DocItemPaginator from '@theme/DocItem/Paginator';
import DocVersionBanner from '@theme/DocVersionBanner';
import DocVersionBadge from '@theme/DocVersionBadge';
import DocItemFooter from '@theme/DocItem/Footer';
import DocItemTOCMobile from '@theme/DocItem/TOC/Mobile';
import DocItemTOCDesktop from '@theme/DocItem/TOC/Desktop';
import DocItemContent from '@theme/DocItem/Content';
import DocBreadcrumbs from '@theme/DocBreadcrumbs';
import ContentVisibility from '@theme/ContentVisibility';
import VoteButtons from '@site/src/components/VoteButtons';
import styles from './styles.module.css';
function useDocTOC() {
const {frontMatter, toc} = useDoc();
const windowSize = useWindowSize();
const hidden = frontMatter.hide_table_of_contents;
const canRender = !hidden && toc.length > 0;
const mobile = canRender ? <DocItemTOCMobile /> : undefined;
const desktop =
canRender && (windowSize === 'desktop' || windowSize === 'ssr') ? (
<DocItemTOCDesktop />
) : undefined;
return {
hidden,
mobile,
desktop,
};
}
export default function DocItemLayout({children}) {
const docTOC = useDocTOC();
const {metadata} = useDoc();
return (
<div className="row">
<div className={clsx('col', !docTOC.hidden && styles.docItemCol)}>
<ContentVisibility metadata={metadata} />
<DocVersionBanner />
<div className={styles.docItemContainer}>
<article>
<DocBreadcrumbs />
<DocVersionBadge />
{docTOC.mobile}
<DocItemContent>{children}</DocItemContent>
<DocItemFooter />
<VoteButtons />
</article>
<DocItemPaginator />
</div>
</div>
{docTOC.desktop && <div className="col col--3">{docTOC.desktop}</div>}
</div>
);
}

View File

@ -0,0 +1,10 @@
.docItemContainer header + *,
.docItemContainer article > *:first-child {
margin-top: 0;
}
@media (min-width: 997px) {
.docItemCol {
max-width: 75% !important;
}
}

View File

@ -0,0 +1,11 @@
import OriginalFooter from '@docusaurus/theme-classic/lib/theme/Footer/Layout';
import VoteButtons from '@site/src/components/VoteButtons';
export default function Layout(props) {
return (
<>
<OriginalFooter {...props} />
<VoteButtons />
</>
);
}

View File

@ -0,0 +1,271 @@
(function() {
'use strict';
const STORAGE_KEY = 'gitlink_reading_progress';
const BADGE_CLASS = 'reading-progress-badge';
function getReadDocs() {
try {
const data = localStorage.getItem(STORAGE_KEY);
return data ? JSON.parse(data) : [];
} catch (e) {
console.error('读取进度失败:', e);
return [];
}
}
function saveReadDocs(docs) {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(docs));
} catch (e) {
console.error('保存进度失败:', e);
}
}
function normalizePath(path) {
if (!path) return '/';
// URL 解码(处理中文路径)
try {
path = decodeURIComponent(path);
} catch (e) {
// 如果解码失败,保持原样
}
path = path.replace(/^\/+/, '').replace(/\/+$/, '');
return '/' + path;
}
function markAsRead() {
const currentPath = normalizePath(window.location.pathname);
const readDocs = getReadDocs();
if (!readDocs.includes(currentPath)) {
readDocs.push(currentPath);
saveReadDocs(readDocs);
console.log('✅ 已标记为已阅读:', currentPath);
updateBadges();
} else {
console.log(' 已经标记过了:', currentPath);
}
}
function isDocRead(path) {
const readDocs = getReadDocs();
const normalizedPath = normalizePath(path);
return readDocs.some(docPath => normalizePath(docPath) === normalizedPath);
}
function createBadge() {
const badge = document.createElement('span');
badge.className = BADGE_CLASS;
badge.textContent = '✓';
badge.style.cssText = `
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
width: 18px !important;
height: 18px !important;
background-color: #22c55e !important;
color: white !important;
border-radius: 50% !important;
font-size: 11px !important;
font-weight: bold !important;
flex-shrink: 0 !important;
margin-left: 8px !important;
visibility: visible !important;
opacity: 1 !important;
position: relative !important;
`;
return badge;
}
function updateBadges() {
const readDocs = getReadDocs();
console.log('📚 已阅读的文档数:', readDocs.length, readDocs);
if (readDocs.length === 0) {
console.log('⚠️ 还没有已阅读的文档');
return;
}
// 清除所有旧标记
document.querySelectorAll('.' + BADGE_CLASS).forEach(badge => badge.remove());
// 获取所有可能的链接元素
const selectors = [
'a.menu__link',
'a.menu-link',
'.menu a[href^="/"]',
'.sidebar a[href^="/"]',
'nav a[href^="/"]',
'[class*="sidebar"] a[href^="/"]',
'[class*="menu"] a[href^="/"]'
];
let totalLinks = 0;
let matchedLinks = 0;
selectors.forEach(selector => {
const links = document.querySelectorAll(selector);
links.forEach(link => {
totalLinks++;
const href = link.getAttribute('href');
if (href && !href.startsWith('http') && !href.startsWith('#') && href !== '/') {
const linkPath = normalizePath(href);
if (isDocRead(linkPath)) {
matchedLinks++;
if (!link.querySelector('.' + BADGE_CLASS)) {
const badge = createBadge();
link.appendChild(badge);
console.log('✓ 添加标记:', linkPath, '->', link.textContent.trim().substring(0, 30));
}
}
}
});
});
console.log(`🔍 检查了 ${totalLinks} 个链接,匹配了 ${matchedLinks} 个已阅读文档`);
}
function initProgressBar() {
if (document.querySelector('[data-reading-progress-bar]')) {
return;
}
const progressBar = document.createElement('div');
progressBar.setAttribute('data-reading-progress-bar', 'true');
progressBar.style.cssText = `
position: fixed !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 4px !important;
background-color: rgba(70, 106, 255, 0.1) !important;
z-index: 9999 !important;
pointer-events: none !important;
`;
const bar = document.createElement('div');
bar.style.cssText = `
height: 100% !important;
background: linear-gradient(90deg, #466aff, #6b8cff) !important;
width: 0% !important;
transition: width 0.15s ease-out !important;
`;
progressBar.appendChild(bar);
document.body.appendChild(progressBar);
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(() => {
const windowHeight = window.innerHeight;
const documentHeight = document.documentElement.scrollHeight - windowHeight;
const scrolled = window.scrollY;
const progress = (scrolled / documentHeight) * 100;
bar.style.width = Math.min(progress, 100) + '%';
ticking = false;
});
ticking = true;
}
});
}
function initScrollDetection() {
let hasMarked = false;
function checkScroll() {
const scrollPosition = window.innerHeight + window.scrollY;
const pageHeight = document.documentElement.scrollHeight;
const threshold = 200;
const progress = Math.min((scrollPosition / pageHeight) * 100, 100);
if (!hasMarked && (progress >= 90 || (pageHeight - scrollPosition < threshold))) {
hasMarked = true;
console.log('📖 滚动进度: ' + Math.round(progress) + '%,标记为已阅读');
markAsRead();
setTimeout(() => { hasMarked = false; }, 3000);
}
}
window.addEventListener('scroll', () => checkScroll());
setTimeout(checkScroll, 1000);
}
function init() {
console.log('🚀 初始化阅读进度追踪...');
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => setTimeout(initAll, 500));
} else {
setTimeout(initAll, 500);
}
}
function initAll() {
console.log('⚙️ 启动功能...');
initProgressBar();
updateBadges();
initScrollDetection();
// 定期更新
setInterval(updateBadges, 3000);
}
// 启动
init();
// 监听路由变化
let lastUrl = location.href;
new MutationObserver(() => {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
console.log('🔄 页面变化,更新标记');
setTimeout(updateBadges, 500);
}
}).observe(document, { subtree: true, childList: true });
// 暴露调试接口
window.readingProgressDebug = {
getReadDocs,
markAsRead,
updateBadges,
clearAll: () => {
localStorage.removeItem(STORAGE_KEY);
document.querySelectorAll('.' + BADGE_CLASS).forEach(b => b.remove());
console.log('🗑️ 已清除所有阅读记录');
updateBadges();
},
test: () => {
console.log('🧪 开始测试...');
const currentPath = normalizePath(window.location.pathname);
console.log('当前路径:', currentPath);
console.log('已阅读文档:', getReadDocs());
const links = document.querySelectorAll('a[href^="/"]');
console.log('找到链接数:', links.length);
links.forEach(link => {
const href = link.getAttribute('href');
if (href && !href.startsWith('http') && href !== '/') {
console.log('链接:', normalizePath(href), '文本:', link.textContent.trim());
}
});
updateBadges();
}
};
console.log('✅ 阅读进度追踪已加载');
console.log('🔧 调试命令:');
console.log(' readingProgressDebug.markAsRead() - 手动标记当前页面');
console.log(' readingProgressDebug.updateBadges() - 更新侧边栏标记');
console.log(' readingProgressDebug.test() - 运行测试');
console.log(' readingProgressDebug.clearAll() - 清除所有记录');
})();