更新模块化配置支持
This commit is contained in:
370
README.md
370
README.md
@@ -12,15 +12,11 @@
|
|||||||
- [快速开始](#快速开始)
|
- [快速开始](#快速开始)
|
||||||
- [部署方式](#部署方式)
|
- [部署方式](#部署方式)
|
||||||
- [快速部署到GitHub Pages](#快速部署到github-pages)
|
- [快速部署到GitHub Pages](#快速部署到github-pages)
|
||||||
|
- [部署到服务器](#部署到服务器)
|
||||||
|
- [设置配置文件](#设置配置文件)
|
||||||
|
- [使用单文件配置](#使用单文件配置)
|
||||||
|
- [使用模块化配置](#使用模块化配置)
|
||||||
- [书签导入功能](#书签导入功能)
|
- [书签导入功能](#书签导入功能)
|
||||||
- [使用方法](#使用方法)
|
|
||||||
- [自动化工作流程详解](#自动化工作流程详解)
|
|
||||||
- [书签配置自定义](#书签配置自定义)
|
|
||||||
- [模板说明](#模板说明)
|
|
||||||
- [配置文件结构](#配置文件结构)
|
|
||||||
- [设置网站字体](#设置网站字体)
|
|
||||||
- [设置网站图标](#设置网站图标)
|
|
||||||
- [添加新的网站链接](#添加新的网站链接)
|
|
||||||
- [贡献](#贡献)
|
- [贡献](#贡献)
|
||||||
- [许可证](#许可证)
|
- [许可证](#许可证)
|
||||||
|
|
||||||
@@ -40,8 +36,18 @@
|
|||||||
|
|
||||||
## 近期更新
|
## 近期更新
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>点击查看/隐藏更新日志</summary>
|
||||||
|
|
||||||
### 2025/05/02
|
### 2025/05/02
|
||||||
|
|
||||||
|
**1. 模块化配置**
|
||||||
|
- ✅ 支持将配置拆分为多个文件,便于管理和维护
|
||||||
|
- ✅ 引入配置目录结构,分离页面配置
|
||||||
|
- ✅ 保持向后兼容性,同时支持传统配置文件
|
||||||
|
|
||||||
|
### 2025/05/01
|
||||||
|
|
||||||
**1. 页面布局优化**
|
**1. 页面布局优化**
|
||||||
- ✅ 优化了内容区域和侧边栏的间距,确保各种分辨率下内容不会贴近边缘
|
- ✅ 优化了内容区域和侧边栏的间距,确保各种分辨率下内容不会贴近边缘
|
||||||
- ✅ 卡片与边框始终保持合理间距,避免在窄屏设备上与滚动条贴边
|
- ✅ 卡片与边框始终保持合理间距,避免在窄屏设备上与滚动条贴边
|
||||||
@@ -65,6 +71,8 @@
|
|||||||
- ✅ 生成配置文件,无需手动录入即可批量导入网站链接
|
- ✅ 生成配置文件,无需手动录入即可批量导入网站链接
|
||||||
- ✅ 与GitHub Actions集成,全自动化的导入和部署流程
|
- ✅ 与GitHub Actions集成,全自动化的导入和部署流程
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
## 技术栈
|
## 技术栈
|
||||||
|
|
||||||
- HTML5 + CSS3
|
- HTML5 + CSS3
|
||||||
@@ -86,8 +94,22 @@ menav/
|
|||||||
│ └── index.html # HTML骨架模板文件
|
│ └── index.html # HTML骨架模板文件
|
||||||
├── dist/ # 生成的静态网站(由generator.js生成)
|
├── dist/ # 生成的静态网站(由generator.js生成)
|
||||||
├── bookmarks/ # 书签导入目录
|
├── bookmarks/ # 书签导入目录
|
||||||
├── config.yml # 默认配置文件
|
├── config/ # 模块化配置目录
|
||||||
└── config.user.yml # 用户自定义配置文件
|
│ ├── _default/ # 默认配置
|
||||||
|
│ │ ├── site.yml # 网站基本配置
|
||||||
|
│ │ ├── navigation.yml # 导航配置
|
||||||
|
│ │ └── pages/ # 页面配置
|
||||||
|
│ │ ├── home.yml
|
||||||
|
│ │ ├── projects.yml
|
||||||
|
│ │ ├── articles.yml
|
||||||
|
│ │ ├── friends.yml
|
||||||
|
│ │ └── bookmarks.yml
|
||||||
|
│ └── user/ # 用户自定义配置
|
||||||
|
│ ├── site.yml # 用户网站配置
|
||||||
|
│ ├── navigation.yml # 用户导航配置
|
||||||
|
│ └── pages/ # 用户页面配置
|
||||||
|
├── config.yml # 默认配置文件(传统格式)
|
||||||
|
└── config.user.yml # 用户自定义配置文件(传统格式)
|
||||||
```
|
```
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
@@ -109,14 +131,8 @@ npm install
|
|||||||
```
|
```
|
||||||
|
|
||||||
3. 修改配置
|
3. 修改配置
|
||||||
- 复制 `config.yml` 为 `config.user.yml`
|
- 可以选择使用单文件配置或模块化配置(见[设置配置文件](#设置配置文件))
|
||||||
- 在 `config.user.yml` 中根据你的需求修改网站内容:
|
- 自定义网站内容、导航链接、社交媒体链接等
|
||||||
- 修改网站基本信息
|
|
||||||
- 添加/修改导航链接
|
|
||||||
- 自定义社交媒体链接
|
|
||||||
- 更新个人项目展示
|
|
||||||
- 添加友情链接等
|
|
||||||
|
|
||||||
|
|
||||||
4. 本地预览
|
4. 本地预览
|
||||||
```bash
|
```bash
|
||||||
@@ -138,9 +154,9 @@ npm run dev
|
|||||||
- 勾选 "Issues"
|
- 勾选 "Issues"
|
||||||
|
|
||||||
3. 启用Actions:
|
3. 启用Actions:
|
||||||
- 进入fork后的仓库
|
- 进入fork后的仓库
|
||||||
- 点击顶部的 "Actions" 标签页
|
- 点击顶部的 "Actions" 标签页
|
||||||
- 点击绿色按钮 "I understand my workflows, go ahead and enable them"
|
- 点击绿色按钮 "I understand my workflows, go ahead and enable them"
|
||||||
|
|
||||||
4. 配置Pages:
|
4. 配置Pages:
|
||||||
- 进入仓库的 Settings -> Pages
|
- 进入仓库的 Settings -> Pages
|
||||||
@@ -150,20 +166,16 @@ npm run dev
|
|||||||
#### 第二步:自定义配置
|
#### 第二步:自定义配置
|
||||||
|
|
||||||
1. 创建个人配置文件:
|
1. 创建个人配置文件:
|
||||||
- 复制 `config.yml` 为 `config.user.yml`
|
- 可以使用单文件配置或模块化配置
|
||||||
- 在 `config.user.yml` 中修改配置
|
- 推荐使用模块化配置(见[使用模块化配置](#使用模块化配置))
|
||||||
- 提交 `config.user.yml` 到您的仓库,这样GitHub Actions才能使用您的自定义配置进行构建
|
- 提交您的配置文件到仓库
|
||||||
|
|
||||||
2. 修改配置信息:
|
2. 等待自动部署:
|
||||||
- 修改网站基本信息
|
- GitHub Actions会自动检测您的更改
|
||||||
- 添加/修改导航链接
|
- 构建并部署您的网站
|
||||||
- 自定义社交媒体链接
|
- 部署完成后,您可以在 Settings -> Pages 中找到您的网站地址
|
||||||
- 更新个人项目展示
|
|
||||||
- 添加友情链接等
|
|
||||||
|
|
||||||
完成以上步骤后,系统会自动部署您的网站。部署完成后,您可以在 Settings -> Pages 中找到您的网站地址。
|
> 重要提示: 请注意不要在配置文件中包含敏感信息,因为它将被提交到公开仓库。
|
||||||
|
|
||||||
> 重要提示: 请务必使用 `config.user.yml` 进行配置,这样在同步上游更新时不会丢失您的个人设置。同时注意不要在配置文件中包含敏感信息,因为它将被提交到公开仓库。
|
|
||||||
|
|
||||||
#### 故障排除
|
#### 故障排除
|
||||||
|
|
||||||
@@ -175,114 +187,61 @@ npm run dev
|
|||||||
- 找到失败的工作流
|
- 找到失败的工作流
|
||||||
- 点击 "Re-run all jobs" 重新运行
|
- 点击 "Re-run all jobs" 重新运行
|
||||||
|
|
||||||
|
### 部署到服务器
|
||||||
|
|
||||||
## 书签导入功能
|
如果您想部署到自己的Web服务器,只需要以下几个步骤:
|
||||||
|
|
||||||
### 使用方法
|
1. 构建静态网站:
|
||||||
|
```bash
|
||||||
1. **从浏览器导出书签**
|
npm run build
|
||||||
- 在Chrome中: 打开书签管理器 -> 点击"更多"(三个点) -> 导出书签
|
|
||||||
- 在Firefox中: 打开书签库 -> 显示所有书签 -> 导入和备份 -> 导出书签为HTML
|
|
||||||
- 在Edge中: 设置 -> 收藏夹 -> 管理收藏夹 -> 导出为HTML文件
|
|
||||||
|
|
||||||
2. **导入书签到MeNav**
|
|
||||||
- 在项目根目录下创建 `bookmarks` 文件夹(如果不存在)
|
|
||||||
- 将导出的HTML书签文件放入 `bookmarks` 文件夹
|
|
||||||
- 提交并推送变更到仓库
|
|
||||||
- GitHub Actions将自动处理书签文件并生成书签配置
|
|
||||||
|
|
||||||
3. **书签处理流程**
|
|
||||||
- 系统会扫描 `bookmarks` 目录,查找最新的HTML书签文件
|
|
||||||
- 解析书签文件中的链接和文件夹结构
|
|
||||||
- 自动为书签分配适当的图标
|
|
||||||
- 生成 `bookmarks.user.yml` 配置文件(而不是直接修改 `bookmarks.yml`)
|
|
||||||
- 处理完成后会自动清空 `bookmarks` 目录(防止重复处理)
|
|
||||||
- 重新构建并部署网站
|
|
||||||
|
|
||||||
4. **注意事项**
|
|
||||||
- 每次只处理一个书签文件,如有多个文件,系统会选择最新的那个
|
|
||||||
- 书签导入是构建时的一次性操作,不会在浏览器中保存或同步您的书签
|
|
||||||
- 如果想要更新书签,可以直接编辑 `bookmarks.user.yml`,或重新导出书签文件并重新导入
|
|
||||||
- 系统会优先使用 `bookmarks.user.yml`,如果同时存在 `bookmarks.yml`,它们的内容会被合并(用户配置优先)
|
|
||||||
|
|
||||||
### 自动化工作流程详解
|
|
||||||
|
|
||||||
MeNav使用单一的GitHub Actions工作流处理书签导入与网站部署,实现了无缝集成:
|
|
||||||
|
|
||||||
1. **触发条件**:
|
|
||||||
- 当您推送任何更改到主分支(特别是向 `bookmarks` 目录添加HTML文件)时
|
|
||||||
- 手动触发工作流时
|
|
||||||
|
|
||||||
2. **书签处理步骤**:
|
|
||||||
- 自动检测 `bookmarks` 目录中的HTML文件
|
|
||||||
- 使用 `bookmark-processor.js` 脚本处理书签文件
|
|
||||||
- 生成/更新 `bookmarks.user.yml` 配置文件
|
|
||||||
- 提交更改(如有)并保存至仓库
|
|
||||||
- 自动清理已处理的HTML书签文件
|
|
||||||
|
|
||||||
3. **网站构建与部署**:
|
|
||||||
- 基于最新的配置(包括新生成的书签配置)构建网站
|
|
||||||
- 自动将构建结果部署到GitHub Pages
|
|
||||||
|
|
||||||
4. **故障排除**:
|
|
||||||
- 如果书签未正确显示,请检查导出的HTML文件格式是否标准
|
|
||||||
- 可以通过GitHub Actions日志查看处理过程中的详细信息
|
|
||||||
- 确保书签文件是有效的HTML格式,且包含正确的书签数据结构
|
|
||||||
|
|
||||||
### 书签配置自定义
|
|
||||||
|
|
||||||
处理后生成的 `bookmarks.user.yml` 文件可以手动编辑以进一步自定义:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# 自动生成的书签配置示例(用户自定义版本)
|
|
||||||
title: 我的书签
|
|
||||||
subtitle: 从浏览器导入的书签收藏
|
|
||||||
categories:
|
|
||||||
- name: 开发工具
|
|
||||||
icon: fas fa-folder
|
|
||||||
sites:
|
|
||||||
- name: GitHub
|
|
||||||
url: https://github.com
|
|
||||||
icon: fab fa-github
|
|
||||||
description: "从书签导入: GitHub"
|
|
||||||
# 更多网站...
|
|
||||||
# 更多分类...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
您可以:
|
2. 复制构建结果:
|
||||||
- 修改分类和网站的名称和描述
|
- 所有生成的静态文件都位于 `dist` 目录中
|
||||||
- 调整图标(使用Font Awesome图标类)
|
- 将 `dist` 目录中的所有文件复制到您的Web服务器根目录
|
||||||
- 重新组织书签分类结构
|
|
||||||
- 添加或删除特定书签
|
|
||||||
|
|
||||||
**提示**:
|
3. 配置Web服务器:
|
||||||
- 尽管自动处理会清理原始HTML文件,但修改后的 `bookmarks.user.yml` 会被保留,您可以随时手动编辑它来更新书签内容
|
- 确保服务器配置为提供静态文件
|
||||||
- 如果项目中同时存在 `bookmarks.yml` 和 `bookmarks.user.yml`,系统会合并两者的内容,以 `bookmarks.user.yml` 中的配置为优先
|
- 对于Apache: 在网站根目录中已有正确的 .htaccess 文件
|
||||||
- 这种设计允许您在更新源仓库时保留自己的书签配置
|
- 对于Nginx: 添加以下配置到您的server块:
|
||||||
|
|
||||||
### 模板说明
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name your-domain.com;
|
||||||
|
root /path/to/dist;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
本项目使用模板与配置文件分离的方式生成网站:
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
1. `templates/index.html` 是不包含具体内容的HTML骨架模板:
|
4. 更新配置:
|
||||||
- 使用占位符 (如 `{{SITE_TITLE}}`, `{{NAVIGATION}}`, `{{HOME_CONTENT}}`) 标记动态内容的位置
|
- 如果您想在服务器上更新网站,只需重复上述步骤1-2
|
||||||
- 只包含页面结构、CSS引用和基本Javascript引用
|
- 或者设置自动部署流程,例如使用GitLab CI/CD或Jenkins
|
||||||
|
|
||||||
2. 工作原理:
|
## 设置配置文件
|
||||||
- `generator.js` 读取配置文件 (优先使用 `config.user.yml`)
|
|
||||||
- 将配置内容注入到模板中的占位符位置
|
|
||||||
- 生成最终的静态HTML网站
|
|
||||||
|
|
||||||
3. 优点:
|
MeNav支持两种配置方式:单文件配置和模块化配置(推荐)。
|
||||||
- 清晰分离结构与内容
|
|
||||||
- 用户只需修改配置文件,不需要编辑HTML
|
|
||||||
- 便于更新与维护
|
|
||||||
|
|
||||||
|
在加载配置时遵循以下优先级顺序:
|
||||||
|
|
||||||
### 配置文件结构
|
1. `config/user/` (模块化用户配置)
|
||||||
|
2. `config.user.yml`(单文件用户配置)
|
||||||
|
3. `config/_default/` (模块化默认配置)
|
||||||
|
4. `config.yml`(单文件默认配置)
|
||||||
|
|
||||||
`config.user.yml` 应包含以下主要部分:
|
### 使用单文件配置
|
||||||
|
|
||||||
|
单文件配置是最简单的配置方式,适合小型网站和快速开始。
|
||||||
|
|
||||||
|
1. **创建配置文件**:
|
||||||
|
- 复制 `config.yml` 为 `config.user.yml`
|
||||||
|
- 编辑 `config.user.yml` 文件
|
||||||
|
|
||||||
|
2. **配置文件结构**:
|
||||||
```yaml
|
```yaml
|
||||||
# 网站基本信息
|
# 网站基本信息
|
||||||
site:
|
site:
|
||||||
@@ -294,9 +253,9 @@ site:
|
|||||||
# 字体设置
|
# 字体设置
|
||||||
fonts:
|
fonts:
|
||||||
title: # 标题字体
|
title: # 标题字体
|
||||||
family: 字体名称 # 可以是Web安全字体或Google Fonts
|
family: 字体名称
|
||||||
weight: 字重值 # 如400、500、600等
|
weight: 字重值
|
||||||
source: 字体来源 # "google"或"system"
|
source: 字体来源
|
||||||
subtitle: # 副标题字体
|
subtitle: # 副标题字体
|
||||||
family: 字体名称
|
family: 字体名称
|
||||||
weight: 字重值
|
weight: 字重值
|
||||||
@@ -319,67 +278,21 @@ navigation:
|
|||||||
id: 页面ID
|
id: 页面ID
|
||||||
active: 是否激活
|
active: 是否激活
|
||||||
|
|
||||||
|
# 类别
|
||||||
|
categories:
|
||||||
|
- name: 分类名称
|
||||||
|
icon: 分类图标
|
||||||
|
sites:
|
||||||
|
- name: 网站名称
|
||||||
|
url: 网站地址
|
||||||
|
icon: 网站图标
|
||||||
|
description: 网站描述
|
||||||
|
|
||||||
# 更多配置...
|
# 更多配置...
|
||||||
```
|
```
|
||||||
|
|
||||||
### 设置网站字体
|
3. **添加网站链接**:
|
||||||
|
在 `config.user.yml` 中的 categories 部分添加新站点:
|
||||||
1. 字体配置选项:
|
|
||||||
- `family`: 字体名称,支持Web安全字体或Google Fonts
|
|
||||||
- `weight`: 字体粗细,常用值如400(常规)、500(中等)、600(粗体)等
|
|
||||||
- `source`: 字体来源,可选"google"或"system"
|
|
||||||
|
|
||||||
2. 字体分类:
|
|
||||||
- `title`: 标题字体,用于大标题
|
|
||||||
- `subtitle`: 副标题字体,用于副标题
|
|
||||||
- `body`: 正文字体,用于普通文本
|
|
||||||
|
|
||||||
3. 使用Google字体示例:
|
|
||||||
```yaml
|
|
||||||
fonts:
|
|
||||||
body:
|
|
||||||
family: "Noto Sans SC" # Google提供的中文字体
|
|
||||||
weight: 400
|
|
||||||
source: "google"
|
|
||||||
```
|
|
||||||
|
|
||||||
4. 使用系统字体示例:
|
|
||||||
```yaml
|
|
||||||
fonts:
|
|
||||||
body:
|
|
||||||
family: "Segoe UI, system-ui, -apple-system, sans-serif"
|
|
||||||
weight: 400
|
|
||||||
source: "system"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 设置网站图标
|
|
||||||
|
|
||||||
1. 准备图标文件:
|
|
||||||
- 支持.ico、.png等格式
|
|
||||||
- 建议尺寸为32x32或16x16像素
|
|
||||||
- 将图标文件放在assets目录下
|
|
||||||
- 例如: `assets/favicon.ico` 或 `assets/favicon.png`
|
|
||||||
|
|
||||||
2. 配置图标:
|
|
||||||
- 在`config.yml`或`config.user.yml`的site部分设置favicon
|
|
||||||
- 使用相对于仓库根目录的路径
|
|
||||||
- 例如: `favicon: favicon.ico`(generator会自动从assets目录查找)
|
|
||||||
- 也可以使用在线图标URL
|
|
||||||
|
|
||||||
3. 生成和部署:
|
|
||||||
- 运行 `npm run generate` 时会自动复制图标文件到dist目录
|
|
||||||
- 确保图标文件存在于assets目录中
|
|
||||||
- 部署后图标会自动显示在浏览器标签页
|
|
||||||
|
|
||||||
> 提示: 如果图标没有显示,请检查:
|
|
||||||
> 1. 图标文件是否存在于assets目录
|
|
||||||
> 2. 配置文件中的路径是否正确
|
|
||||||
> 3. 是否重新运行了生成命令
|
|
||||||
|
|
||||||
### 添加新的网站链接
|
|
||||||
|
|
||||||
在 `config.user.yml` 中相应的分类下添加新站点:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
categories:
|
categories:
|
||||||
- name: 分类名称
|
- name: 分类名称
|
||||||
@@ -391,6 +304,87 @@ categories:
|
|||||||
description: 网站描述
|
description: 网站描述
|
||||||
```
|
```
|
||||||
|
|
||||||
|
4. **设置网站字体**:
|
||||||
|
- `family`: 字体名称,支持Web安全字体或Google Fonts
|
||||||
|
- `weight`: 字体粗细,常用值如400(常规)、500(中等)、600(粗体)等
|
||||||
|
- `source`: 字体来源,可选"google"或"system"
|
||||||
|
|
||||||
|
例如使用Google字体:
|
||||||
|
```yaml
|
||||||
|
fonts:
|
||||||
|
body:
|
||||||
|
family: "Noto Sans SC" # Google提供的中文字体
|
||||||
|
weight: 400
|
||||||
|
source: "google"
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **设置网站图标**:
|
||||||
|
- 支持.ico、.png等格式
|
||||||
|
- 建议尺寸为32x32或16x16像素
|
||||||
|
- 将图标文件放在assets目录下
|
||||||
|
- 在配置文件中设置:`favicon: favicon.ico`
|
||||||
|
|
||||||
|
### 使用模块化配置
|
||||||
|
|
||||||
|
模块化配置将配置分散到多个文件中,更易于管理和维护,推荐用于复杂网站。
|
||||||
|
|
||||||
|
#### 模块化配置目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
config/
|
||||||
|
├── _default/ # 默认配置
|
||||||
|
│ ├── site.yml # 网站基本信息、字体等
|
||||||
|
│ ├── navigation.yml # 导航菜单配置
|
||||||
|
│ └── pages/ # 页面配置
|
||||||
|
│ ├── home.yml # 首页配置
|
||||||
|
│ ├── projects.yml # 项目页配置
|
||||||
|
│ ├── articles.yml # 文章页配置
|
||||||
|
│ ├── friends.yml # 朋友页配置
|
||||||
|
│ └── bookmarks.yml # 书签页配置
|
||||||
|
└── user/ # 用户自定义配置(可选覆盖)
|
||||||
|
├── site.yml # 用户网站配置
|
||||||
|
├── navigation.yml # 用户导航配置
|
||||||
|
└── pages/ # 用户页面配置
|
||||||
|
├── home.yml # 用户首页配置
|
||||||
|
# 其他用户自定义页面...
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 使用模块化配置的优势
|
||||||
|
|
||||||
|
1. **分离关注点**:每个页面和功能区域有专属配置文件
|
||||||
|
2. **简化编辑**:修改特定页面时只需编辑对应文件
|
||||||
|
3. **更好的版本控制**:减少合并冲突
|
||||||
|
4. **向后兼容**:仍然支持传统的 `config.yml` 和 `config.user.yml`
|
||||||
|
|
||||||
|
|
||||||
|
## 书签导入功能
|
||||||
|
|
||||||
|
MeNav支持从浏览器导入书签,快速批量添加网站链接。
|
||||||
|
|
||||||
|
### 导入步骤
|
||||||
|
|
||||||
|
1. **从浏览器导出书签**
|
||||||
|
- 在Chrome中: 打开书签管理器 -> 点击"更多"(三个点) -> 导出书签
|
||||||
|
- 在Firefox中: 打开书签库 -> 显示所有书签 -> 导入和备份 -> 导出书签为HTML
|
||||||
|
- 在Edge中: 设置 -> 收藏夹 -> 管理收藏夹 -> 导出为HTML文件
|
||||||
|
|
||||||
|
2. **导入书签到MeNav**
|
||||||
|
- 在项目根目录下创建 `bookmarks` 文件夹(如果不存在)
|
||||||
|
- 将导出的HTML书签文件放入 `bookmarks` 文件夹
|
||||||
|
- 提交并推送变更到仓库
|
||||||
|
- 系统会自动处理书签文件并生成配置文件
|
||||||
|
|
||||||
|
3. **书签处理结果**
|
||||||
|
- 生成的书签配置会保存到 `bookmarks.user.yml`
|
||||||
|
- 如果使用模块化配置,也会同时保存到 `config/user/pages/bookmarks.yml`
|
||||||
|
- 书签会自动添加到导航菜单中
|
||||||
|
|
||||||
|
> 有关书签导入功能的更多信息,请参阅源代码中的相关注释。
|
||||||
|
|
||||||
|
## 贡献
|
||||||
|
|
||||||
|
欢迎通过 Issues 或 Pull Requests 的形式做出贡献。如果您有好的想法或发现了问题,请随时提出。
|
||||||
|
|
||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
AGPL-3.0 License
|
AGPL-3.0 License
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ navigation:
|
|||||||
- name: 书签
|
- name: 书签
|
||||||
icon: fas fa-bookmark
|
icon: fas fa-bookmark
|
||||||
id: bookmarks
|
id: bookmarks
|
||||||
active: false
|
|
||||||
social:
|
social:
|
||||||
- name: GitHub
|
- name: GitHub
|
||||||
url: https://github.com
|
url: https://github.com
|
||||||
|
|||||||
16
config/_default/navigation.yml
Normal file
16
config/_default/navigation.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
- name: 首页
|
||||||
|
icon: fas fa-home
|
||||||
|
id: home
|
||||||
|
active: true
|
||||||
|
- name: 项目
|
||||||
|
icon: fas fa-project-diagram
|
||||||
|
id: projects
|
||||||
|
- name: 文章
|
||||||
|
icon: fas fa-book
|
||||||
|
id: articles
|
||||||
|
- name: 朋友
|
||||||
|
icon: fas fa-users
|
||||||
|
id: friends
|
||||||
|
- name: 书签
|
||||||
|
icon: fas fa-bookmark
|
||||||
|
id: bookmarks
|
||||||
33
config/_default/pages/articles.yml
Normal file
33
config/_default/pages/articles.yml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
title: 技术文章
|
||||||
|
subtitle: 分享我的技术文章和学习笔记
|
||||||
|
categories:
|
||||||
|
- name: 最新文章
|
||||||
|
icon: fas fa-pen
|
||||||
|
sites:
|
||||||
|
- name: Vue3最佳实践
|
||||||
|
icon: fab fa-vuejs
|
||||||
|
description: Vue3组合式API的使用技巧
|
||||||
|
url: "#"
|
||||||
|
- name: JavaScript进阶
|
||||||
|
icon: fab fa-js
|
||||||
|
description: JavaScript高级特性解析
|
||||||
|
url: "#"
|
||||||
|
- name: Git使用技巧
|
||||||
|
icon: fab fa-git-alt
|
||||||
|
description: Git常用命令和工作流
|
||||||
|
url: "#"
|
||||||
|
- name: Docker入门
|
||||||
|
icon: fab fa-docker
|
||||||
|
description: Docker基础知识和实践
|
||||||
|
url: "#"
|
||||||
|
- name: 学习笔记
|
||||||
|
icon: fas fa-book
|
||||||
|
sites:
|
||||||
|
- name: React Hooks
|
||||||
|
icon: fab fa-react
|
||||||
|
description: React Hooks最佳实践
|
||||||
|
url: "#"
|
||||||
|
- name: Node.js实战
|
||||||
|
icon: fab fa-node-js
|
||||||
|
description: Node.js服务端开发笔记
|
||||||
|
url: "#"
|
||||||
29
config/_default/pages/bookmarks.yml
Normal file
29
config/_default/pages/bookmarks.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
title: 我的书签
|
||||||
|
subtitle: 从浏览器导入的书签收藏
|
||||||
|
categories:
|
||||||
|
- name: 技术资源
|
||||||
|
icon: fas fa-folder
|
||||||
|
sites:
|
||||||
|
- name: GitHub
|
||||||
|
url: https://github.com/
|
||||||
|
icon: fab fa-github
|
||||||
|
description: "从书签导入: GitHub"
|
||||||
|
- name: Stack Overflow
|
||||||
|
url: https://stackoverflow.com/
|
||||||
|
icon: fab fa-stack-overflow
|
||||||
|
description: "从书签导入: Stack Overflow"
|
||||||
|
- name: MDN Web Docs
|
||||||
|
url: https://developer.mozilla.org/
|
||||||
|
icon: fas fa-link
|
||||||
|
description: "从书签导入: MDN Web Docs"
|
||||||
|
- name: 社交媒体
|
||||||
|
icon: fas fa-folder
|
||||||
|
sites:
|
||||||
|
- name: Twitter
|
||||||
|
url: https://twitter.com/
|
||||||
|
icon: fab fa-twitter
|
||||||
|
description: "从书签导入: Twitter"
|
||||||
|
- name: LinkedIn
|
||||||
|
url: https://www.linkedin.com/
|
||||||
|
icon: fab fa-linkedin
|
||||||
|
description: "从书签导入: LinkedIn"
|
||||||
29
config/_default/pages/friends.yml
Normal file
29
config/_default/pages/friends.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
title: 友情链接
|
||||||
|
subtitle: 优秀的博主和朋友们
|
||||||
|
categories:
|
||||||
|
- name: 技术博主
|
||||||
|
icon: fas fa-user-friends
|
||||||
|
sites:
|
||||||
|
- name: 小明的博客
|
||||||
|
icon: fas fa-code
|
||||||
|
description: 全栈开发工程师,分享技术心得
|
||||||
|
url: "#"
|
||||||
|
- name: 小红的前端
|
||||||
|
icon: fas fa-paint-brush
|
||||||
|
description: 专注前端开发与设计
|
||||||
|
url: "#"
|
||||||
|
- name: 小张的后端
|
||||||
|
icon: fas fa-server
|
||||||
|
description: 分享后端开发经验
|
||||||
|
url: "#"
|
||||||
|
- name: 技术社区
|
||||||
|
icon: fas fa-laptop-code
|
||||||
|
sites:
|
||||||
|
- name: GitHub
|
||||||
|
icon: fab fa-github
|
||||||
|
description: 开源代码托管平台
|
||||||
|
url: https://github.com
|
||||||
|
- name: Stack Overflow
|
||||||
|
icon: fab fa-stack-overflow
|
||||||
|
description: 程序员问答社区
|
||||||
|
url: https://stackoverflow.com
|
||||||
43
config/_default/pages/home.yml
Normal file
43
config/_default/pages/home.yml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
categories:
|
||||||
|
- name: 常用网站
|
||||||
|
icon: fas fa-star
|
||||||
|
sites:
|
||||||
|
- name: Linux.do
|
||||||
|
url: https://linux.do/
|
||||||
|
icon: fab fa-linux
|
||||||
|
description: 新的理想型社区
|
||||||
|
- name: Google
|
||||||
|
url: https://www.google.com
|
||||||
|
icon: fab fa-google
|
||||||
|
description: 全球最大的搜索引擎
|
||||||
|
- name: GitHub
|
||||||
|
url: https://www.github.com
|
||||||
|
icon: fab fa-github
|
||||||
|
description: 代码托管平台
|
||||||
|
- name: Stack Overflow
|
||||||
|
url: https://stackoverflow.com
|
||||||
|
icon: fab fa-stack-overflow
|
||||||
|
description: 程序员问答社区
|
||||||
|
- name: ChatGPT
|
||||||
|
url: https://chat.openai.com
|
||||||
|
icon: fas fa-robot
|
||||||
|
description: AI智能助手
|
||||||
|
- name: 学习资源
|
||||||
|
icon: fas fa-graduation-cap
|
||||||
|
sites:
|
||||||
|
- name: 哔哩哔哩
|
||||||
|
url: https://www.bilibili.com
|
||||||
|
icon: fas fa-play-circle
|
||||||
|
description: 视频学习平台
|
||||||
|
- name: 知乎
|
||||||
|
url: https://www.zhihu.com
|
||||||
|
icon: fas fa-question-circle
|
||||||
|
description: 问答社区
|
||||||
|
- name: 掘金
|
||||||
|
url: https://juejin.cn
|
||||||
|
icon: fas fa-book
|
||||||
|
description: 高质量技术社区
|
||||||
|
- name: LeetCode
|
||||||
|
url: https://leetcode.cn
|
||||||
|
icon: fas fa-code
|
||||||
|
description: 算法刷题平台
|
||||||
29
config/_default/pages/projects.yml
Normal file
29
config/_default/pages/projects.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
title: 我的项目
|
||||||
|
subtitle: 这里展示了我的一些个人项目和开源贡献
|
||||||
|
categories:
|
||||||
|
- name: 个人项目
|
||||||
|
icon: fas fa-code
|
||||||
|
sites:
|
||||||
|
- name: 个人导航站
|
||||||
|
icon: fas fa-compass
|
||||||
|
description: 一个简洁美观的个人导航页面
|
||||||
|
url: "#"
|
||||||
|
- name: Todo List
|
||||||
|
icon: fas fa-tasks
|
||||||
|
description: 基于Vue3的待办事项管理器
|
||||||
|
url: "#"
|
||||||
|
- name: 个人博客
|
||||||
|
icon: fas fa-blog
|
||||||
|
description: 使用Hexo搭建的技术博客
|
||||||
|
url: "#"
|
||||||
|
- name: 开源贡献
|
||||||
|
icon: fas fa-code-branch
|
||||||
|
sites:
|
||||||
|
- name: Project A
|
||||||
|
icon: fab fa-github
|
||||||
|
description: 开源项目贡献
|
||||||
|
url: "#"
|
||||||
|
- name: Project B
|
||||||
|
icon: fab fa-github
|
||||||
|
description: 开源项目贡献
|
||||||
|
url: "#"
|
||||||
38
config/_default/site.yml
Normal file
38
config/_default/site.yml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
title: 我的导航
|
||||||
|
description: 个人网络导航站
|
||||||
|
author: Your Name
|
||||||
|
favicon: favicon.ico
|
||||||
|
logo_text: 导航站
|
||||||
|
|
||||||
|
fonts:
|
||||||
|
title:
|
||||||
|
family: Poppins
|
||||||
|
weight: 600
|
||||||
|
source: google
|
||||||
|
subtitle:
|
||||||
|
family: Quicksand
|
||||||
|
weight: 500
|
||||||
|
source: google
|
||||||
|
body:
|
||||||
|
family: Noto Sans SC
|
||||||
|
weight: 400
|
||||||
|
source: google
|
||||||
|
|
||||||
|
profile:
|
||||||
|
title: Hello,
|
||||||
|
subtitle: Welcome to My Navigation
|
||||||
|
description: 导航菜单
|
||||||
|
|
||||||
|
social:
|
||||||
|
- name: GitHub
|
||||||
|
url: https://github.com
|
||||||
|
icon: fab fa-github
|
||||||
|
- name: Telegram
|
||||||
|
url: https://t.me
|
||||||
|
icon: fab fa-telegram
|
||||||
|
- name: Twitter
|
||||||
|
url: https://twitter.com
|
||||||
|
icon: fab fa-twitter
|
||||||
|
- name: Steam
|
||||||
|
url: https://steam.com
|
||||||
|
icon: fab fa-steam
|
||||||
@@ -6,8 +6,12 @@ const yaml = require('js-yaml');
|
|||||||
const BOOKMARKS_DIR = 'bookmarks';
|
const BOOKMARKS_DIR = 'bookmarks';
|
||||||
// 输出配置文件路径 - 使用相对路径
|
// 输出配置文件路径 - 使用相对路径
|
||||||
const OUTPUT_FILE = 'bookmarks.user.yml';
|
const OUTPUT_FILE = 'bookmarks.user.yml';
|
||||||
|
// 模块化输出配置文件路径
|
||||||
|
const MODULAR_OUTPUT_FILE = 'config/user/pages/bookmarks.yml';
|
||||||
// 默认书签配置文件路径 - 使用相对路径
|
// 默认书签配置文件路径 - 使用相对路径
|
||||||
const DEFAULT_BOOKMARKS_FILE = 'bookmarks.yml';
|
const DEFAULT_BOOKMARKS_FILE = 'bookmarks.yml';
|
||||||
|
// 模块化默认书签配置文件路径
|
||||||
|
const MODULAR_DEFAULT_BOOKMARKS_FILE = 'config/_default/pages/bookmarks.yml';
|
||||||
|
|
||||||
// 图标映射,根据URL关键字匹配合适的图标
|
// 图标映射,根据URL关键字匹配合适的图标
|
||||||
const ICON_MAPPING = {
|
const ICON_MAPPING = {
|
||||||
@@ -209,20 +213,114 @@ ${yamlString}`;
|
|||||||
|
|
||||||
// 更新现有config.yml中的导航,添加书签页面
|
// 更新现有config.yml中的导航,添加书签页面
|
||||||
function updateConfigWithBookmarks() {
|
function updateConfigWithBookmarks() {
|
||||||
|
// 传统配置文件
|
||||||
const configFile = 'config.yml';
|
const configFile = 'config.yml';
|
||||||
const userConfigFile = 'config.user.yml';
|
const userConfigFile = 'config.user.yml';
|
||||||
|
|
||||||
// 优先使用用户配置文件,如果存在
|
// 模块化配置文件
|
||||||
const targetConfigFile = fs.existsSync(userConfigFile) ? userConfigFile : configFile;
|
const modularNavFile = 'config/_default/navigation.yml';
|
||||||
|
const modularUserNavFile = 'config/user/navigation.yml';
|
||||||
|
|
||||||
|
let navigationUpdated = false;
|
||||||
|
|
||||||
|
// 按优先级顺序尝试更新导航配置
|
||||||
|
|
||||||
|
// 1. 最高优先级: 模块化用户导航配置
|
||||||
|
if (fs.existsSync(modularUserNavFile)) {
|
||||||
|
navigationUpdated = updateNavigationFile(modularUserNavFile);
|
||||||
|
if (navigationUpdated) {
|
||||||
|
console.log(`Updated modular user navigation file: ${modularUserNavFile} (highest priority)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 次高优先级: 传统用户配置
|
||||||
|
if (!navigationUpdated && fs.existsSync(userConfigFile)) {
|
||||||
|
navigationUpdated = updateTraditionalConfig(userConfigFile);
|
||||||
|
if (navigationUpdated) {
|
||||||
|
console.log(`Updated legacy user config file: ${userConfigFile}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 次低优先级: 模块化默认导航
|
||||||
|
if (!navigationUpdated && fs.existsSync(modularNavFile)) {
|
||||||
|
navigationUpdated = updateNavigationFile(modularNavFile);
|
||||||
|
if (navigationUpdated) {
|
||||||
|
console.log(`Updated modular default navigation file: ${modularNavFile}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 最低优先级: 传统默认配置
|
||||||
|
if (!navigationUpdated && fs.existsSync(configFile)) {
|
||||||
|
navigationUpdated = updateTraditionalConfig(configFile);
|
||||||
|
if (navigationUpdated) {
|
||||||
|
console.log(`Updated legacy default config file: ${configFile} (lowest priority)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!navigationUpdated) {
|
||||||
|
console.log('Did not find any configuration file to update with bookmarks navigation');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新单个导航配置文件(模块化版本)
|
||||||
|
function updateNavigationFile(filePath) {
|
||||||
try {
|
try {
|
||||||
const configContent = fs.readFileSync(targetConfigFile, 'utf8');
|
const content = fs.readFileSync(filePath, 'utf8');
|
||||||
|
const navConfig = yaml.load(content);
|
||||||
|
|
||||||
|
// 检查是否已有书签页面
|
||||||
|
const hasBookmarksNav = Array.isArray(navConfig) &&
|
||||||
|
navConfig.some(nav => nav.id === 'bookmarks');
|
||||||
|
|
||||||
|
if (!hasBookmarksNav) {
|
||||||
|
// 添加书签导航项
|
||||||
|
if (!Array.isArray(navConfig)) {
|
||||||
|
console.log(`Warning: Navigation config in ${filePath} is not an array, cannot update`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
navConfig.push({
|
||||||
|
name: '书签',
|
||||||
|
icon: 'fas fa-bookmark',
|
||||||
|
id: 'bookmarks',
|
||||||
|
active: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新文件
|
||||||
|
const updatedYaml = yaml.dump(navConfig, {
|
||||||
|
indent: 2,
|
||||||
|
lineWidth: -1,
|
||||||
|
quotingType: '"'
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.writeFileSync(filePath, updatedYaml, 'utf8');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // 无需更新
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error updating navigation file ${filePath}:`, error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新传统配置文件(整体配置)
|
||||||
|
function updateTraditionalConfig(filePath) {
|
||||||
|
try {
|
||||||
|
const configContent = fs.readFileSync(filePath, 'utf8');
|
||||||
const config = yaml.load(configContent);
|
const config = yaml.load(configContent);
|
||||||
|
|
||||||
// 检查导航中是否已有书签页面
|
// 检查导航中是否已有书签页面
|
||||||
const hasBookmarksNav = config.navigation.some(nav => nav.id === 'bookmarks');
|
const hasBookmarksNav = config.navigation &&
|
||||||
|
Array.isArray(config.navigation) &&
|
||||||
|
config.navigation.some(nav => nav.id === 'bookmarks');
|
||||||
|
|
||||||
if (!hasBookmarksNav) {
|
if (!hasBookmarksNav) {
|
||||||
|
// 确保navigation数组存在
|
||||||
|
if (!config.navigation) {
|
||||||
|
config.navigation = [];
|
||||||
|
}
|
||||||
|
|
||||||
// 添加书签页面到导航
|
// 添加书签页面到导航
|
||||||
config.navigation.push({
|
config.navigation.push({
|
||||||
name: '书签',
|
name: '书签',
|
||||||
@@ -238,11 +336,14 @@ function updateConfigWithBookmarks() {
|
|||||||
quotingType: '"'
|
quotingType: '"'
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.writeFileSync(targetConfigFile, updatedYaml, 'utf8');
|
fs.writeFileSync(filePath, updatedYaml, 'utf8');
|
||||||
console.log(`Updated ${targetConfigFile} with bookmarks navigation`);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false; // 无需更新
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating config with bookmarks navigation:', error);
|
console.error(`Error updating config file ${filePath}:`, error);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,7 +351,8 @@ function updateConfigWithBookmarks() {
|
|||||||
async function main() {
|
async function main() {
|
||||||
console.log('Starting bookmark processing...');
|
console.log('Starting bookmark processing...');
|
||||||
console.log(`Current working directory: ${process.cwd()}`);
|
console.log(`Current working directory: ${process.cwd()}`);
|
||||||
console.log(`Output file will be: ${OUTPUT_FILE} (absolute: ${path.resolve(OUTPUT_FILE)})`);
|
console.log(`Legacy output file will be: ${OUTPUT_FILE} (absolute: ${path.resolve(OUTPUT_FILE)})`);
|
||||||
|
console.log(`Modular output file will be: ${MODULAR_OUTPUT_FILE} (absolute: ${path.resolve(MODULAR_OUTPUT_FILE)})`);
|
||||||
|
|
||||||
// 获取最新的书签文件
|
// 获取最新的书签文件
|
||||||
const bookmarkFile = getLatestBookmarkFile();
|
const bookmarkFile = getLatestBookmarkFile();
|
||||||
@@ -288,39 +390,58 @@ async function main() {
|
|||||||
console.log(yaml.split('\n').slice(0, 5).join('\n') + '\n...');
|
console.log(yaml.split('\n').slice(0, 5).join('\n') + '\n...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 确保目标目录存在
|
// 确保传统目标目录存在
|
||||||
const outputDir = path.dirname(OUTPUT_FILE);
|
const outputDir = path.dirname(OUTPUT_FILE);
|
||||||
if (!fs.existsSync(outputDir) && outputDir !== '.') {
|
if (!fs.existsSync(outputDir) && outputDir !== '.') {
|
||||||
console.log(`Creating output directory: ${outputDir}`);
|
console.log(`Creating output directory: ${outputDir}`);
|
||||||
fs.mkdirSync(outputDir, { recursive: true });
|
fs.mkdirSync(outputDir, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存YAML文件
|
// 确保模块化目标目录存在
|
||||||
console.log(`Writing to: ${OUTPUT_FILE}`);
|
const modularOutputDir = path.dirname(MODULAR_OUTPUT_FILE);
|
||||||
|
if (!fs.existsSync(modularOutputDir)) {
|
||||||
|
console.log(`Creating modular output directory structure: ${modularOutputDir}`);
|
||||||
|
fs.mkdirSync(modularOutputDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存YAML到传统位置
|
||||||
|
console.log(`Writing to legacy location: ${OUTPUT_FILE}`);
|
||||||
fs.writeFileSync(OUTPUT_FILE, yaml, 'utf8');
|
fs.writeFileSync(OUTPUT_FILE, yaml, 'utf8');
|
||||||
|
|
||||||
|
// 保存YAML到模块化位置
|
||||||
|
console.log(`Writing to modular location: ${MODULAR_OUTPUT_FILE}`);
|
||||||
|
fs.writeFileSync(MODULAR_OUTPUT_FILE, yaml, 'utf8');
|
||||||
|
|
||||||
// 验证文件是否确实被创建
|
// 验证文件是否确实被创建
|
||||||
|
let success = false;
|
||||||
|
|
||||||
if (fs.existsSync(OUTPUT_FILE)) {
|
if (fs.existsSync(OUTPUT_FILE)) {
|
||||||
const stats = fs.statSync(OUTPUT_FILE);
|
const stats = fs.statSync(OUTPUT_FILE);
|
||||||
console.log(`Successfully saved bookmarks configuration to ${OUTPUT_FILE}`);
|
console.log(`Successfully saved bookmarks configuration to ${OUTPUT_FILE}`);
|
||||||
console.log(`Verified file exists: ${OUTPUT_FILE}`);
|
|
||||||
console.log(`File size: ${stats.size} bytes`);
|
console.log(`File size: ${stats.size} bytes`);
|
||||||
console.log(`File permissions: ${stats.mode.toString(8)}`);
|
success = true;
|
||||||
|
|
||||||
// 列出当前目录内容以确认
|
|
||||||
console.log('Current directory contains:');
|
|
||||||
fs.readdirSync('.').forEach(file => {
|
|
||||||
console.log(`- ${file}`);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
console.error(`ERROR: File was not created: ${OUTPUT_FILE}`);
|
console.error(`ERROR: Legacy file was not created: ${OUTPUT_FILE}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs.existsSync(MODULAR_OUTPUT_FILE)) {
|
||||||
|
const stats = fs.statSync(MODULAR_OUTPUT_FILE);
|
||||||
|
console.log(`Successfully saved bookmarks configuration to ${MODULAR_OUTPUT_FILE}`);
|
||||||
|
console.log(`File size: ${stats.size} bytes`);
|
||||||
|
success = true;
|
||||||
|
} else {
|
||||||
|
console.error(`ERROR: Modular file was not created: ${MODULAR_OUTPUT_FILE}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
console.error('ERROR: No output files were created successfully');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新导航
|
// 更新导航
|
||||||
updateConfigWithBookmarks();
|
updateConfigWithBookmarks();
|
||||||
} catch (writeError) {
|
} catch (writeError) {
|
||||||
console.error(`ERROR writing file ${OUTPUT_FILE}:`, writeError);
|
console.error(`ERROR writing files:`, writeError);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
201
src/generator.js
201
src/generator.js
@@ -15,48 +15,199 @@ function escapeHtml(unsafe) {
|
|||||||
.replace(/'/g, "'");
|
.replace(/'/g, "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取配置文件
|
/**
|
||||||
function loadConfig() {
|
* 从文件合并配置到主配置对象
|
||||||
let config = null;
|
* @param {Object} config 主配置对象
|
||||||
|
* @param {string} filePath 配置文件路径
|
||||||
try {
|
*/
|
||||||
// 优先尝试读取用户配置
|
function mergeConfigFromFile(config, filePath) {
|
||||||
if (fs.existsSync('config.user.yml')) {
|
if (fs.existsSync(filePath)) {
|
||||||
const userConfigFile = fs.readFileSync('config.user.yml', 'utf8');
|
try {
|
||||||
config = yaml.load(userConfigFile);
|
const fileContent = fs.readFileSync(filePath, 'utf8');
|
||||||
console.log('Using user configuration from config.user.yml');
|
const fileConfig = yaml.load(fileContent);
|
||||||
}
|
|
||||||
// 如果没有用户配置,则使用默认配置
|
// 提取文件名(不含扩展名)作为配置键
|
||||||
else {
|
const configKey = path.basename(filePath, path.extname(filePath));
|
||||||
const defaultConfigFile = fs.readFileSync('config.yml', 'utf8');
|
|
||||||
config = yaml.load(defaultConfigFile);
|
// 如果是site或navigation文件,直接合并到主配置
|
||||||
console.log('No user configuration found, using default config.yml');
|
if (configKey === 'site' || configKey === 'navigation') {
|
||||||
|
if (!config[configKey]) config[configKey] = {};
|
||||||
|
deepMerge(config[configKey], fileConfig);
|
||||||
|
} else {
|
||||||
|
// 其他配置直接合并到根级别
|
||||||
|
deepMerge(config, fileConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Loaded and merged configuration from ${filePath}`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Error loading configuration from ${filePath}:`, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载页面配置目录中的所有配置文件
|
||||||
|
* @param {Object} config 主配置对象
|
||||||
|
* @param {string} dirPath 页面配置目录路径
|
||||||
|
*/
|
||||||
|
function loadPageConfigs(config, dirPath) {
|
||||||
|
if (fs.existsSync(dirPath)) {
|
||||||
|
const files = fs.readdirSync(dirPath).filter(file =>
|
||||||
|
file.endsWith('.yml') || file.endsWith('.yaml'));
|
||||||
|
|
||||||
|
files.forEach(file => {
|
||||||
|
try {
|
||||||
|
const filePath = path.join(dirPath, file);
|
||||||
|
const fileContent = fs.readFileSync(filePath, 'utf8');
|
||||||
|
const fileConfig = yaml.load(fileContent);
|
||||||
|
|
||||||
|
// 提取文件名(不含扩展名)作为配置键
|
||||||
|
const configKey = path.basename(file, path.extname(file));
|
||||||
|
|
||||||
|
// 将页面配置添加到主配置对象
|
||||||
|
config[configKey] = fileConfig;
|
||||||
|
|
||||||
|
console.log(`Loaded page configuration from ${filePath}`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Error loading page configuration from ${path.join(dirPath, file)}:`, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 深度合并两个对象
|
||||||
|
* @param {Object} target 目标对象
|
||||||
|
* @param {Object} source 源对象
|
||||||
|
* @returns {Object} 合并后的对象
|
||||||
|
*/
|
||||||
|
function deepMerge(target, source) {
|
||||||
|
if (!source) return target;
|
||||||
|
|
||||||
|
for (const key in source) {
|
||||||
|
if (source.hasOwnProperty(key)) {
|
||||||
|
if (typeof source[key] === 'object' && source[key] !== null) {
|
||||||
|
// 确保目标对象有这个属性
|
||||||
|
if (!target[key]) {
|
||||||
|
if (Array.isArray(source[key])) {
|
||||||
|
target[key] = [];
|
||||||
|
} else {
|
||||||
|
target[key] = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归合并
|
||||||
|
if (Array.isArray(source[key])) {
|
||||||
|
// 对于数组,直接替换或添加
|
||||||
|
target[key] = source[key];
|
||||||
|
} else {
|
||||||
|
// 对于对象,递归合并
|
||||||
|
deepMerge(target[key], source[key]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 对于基本类型,直接替换
|
||||||
|
target[key] = source[key];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
console.error('Error loading configuration file:', e);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试读取书签配置
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取配置文件
|
||||||
|
function loadConfig() {
|
||||||
|
// 初始化空配置对象
|
||||||
|
let config = {
|
||||||
|
site: {},
|
||||||
|
navigation: [],
|
||||||
|
fonts: {},
|
||||||
|
profile: {},
|
||||||
|
social: [],
|
||||||
|
categories: []
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理配置目录结构,按照优先级从低到高加载
|
||||||
|
// 4. 最低优先级: config.yml (传统默认配置)
|
||||||
|
if (fs.existsSync('config.yml')) {
|
||||||
|
const defaultConfigFile = fs.readFileSync('config.yml', 'utf8');
|
||||||
|
const defaultConfig = yaml.load(defaultConfigFile);
|
||||||
|
deepMerge(config, defaultConfig);
|
||||||
|
console.log('Loaded legacy default config.yml');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 其次优先级: config/_default/ 目录
|
||||||
|
if (fs.existsSync('config/_default')) {
|
||||||
|
console.log('Loading modular default configuration from config/_default/');
|
||||||
|
|
||||||
|
// 加载基础配置
|
||||||
|
mergeConfigFromFile(config, 'config/_default/site.yml');
|
||||||
|
mergeConfigFromFile(config, 'config/_default/navigation.yml');
|
||||||
|
|
||||||
|
// 加载页面配置
|
||||||
|
if (fs.existsSync('config/_default/pages')) {
|
||||||
|
loadPageConfigs(config, 'config/_default/pages');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 次高优先级: config.user.yml (传统用户配置)
|
||||||
|
if (fs.existsSync('config.user.yml')) {
|
||||||
|
const userConfigFile = fs.readFileSync('config.user.yml', 'utf8');
|
||||||
|
const userConfig = yaml.load(userConfigFile);
|
||||||
|
|
||||||
|
// 深度合并配置
|
||||||
|
deepMerge(config, userConfig);
|
||||||
|
console.log('Merged legacy user configuration from config.user.yml');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 最高优先级: config/user/ 目录
|
||||||
|
if (fs.existsSync('config/user')) {
|
||||||
|
console.log('Loading modular user configuration from config/user/ (highest priority)');
|
||||||
|
|
||||||
|
// 覆盖基础配置
|
||||||
|
mergeConfigFromFile(config, 'config/user/site.yml');
|
||||||
|
mergeConfigFromFile(config, 'config/user/navigation.yml');
|
||||||
|
|
||||||
|
// 覆盖页面配置
|
||||||
|
if (fs.existsSync('config/user/pages')) {
|
||||||
|
loadPageConfigs(config, 'config/user/pages');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理书签文件(保持现有功能)
|
||||||
try {
|
try {
|
||||||
let bookmarksConfig = null;
|
let bookmarksConfig = null;
|
||||||
|
let bookmarksSource = null;
|
||||||
|
|
||||||
// 优先尝试读取用户书签配置
|
// 按照相同的优先级顺序处理书签配置
|
||||||
if (fs.existsSync('bookmarks.user.yml')) {
|
// 1. 模块化用户书签配置 (最高优先级)
|
||||||
|
if (fs.existsSync('config/user/pages/bookmarks.yml')) {
|
||||||
|
const userBookmarksFile = fs.readFileSync('config/user/pages/bookmarks.yml', 'utf8');
|
||||||
|
bookmarksConfig = yaml.load(userBookmarksFile);
|
||||||
|
bookmarksSource = 'config/user/pages/bookmarks.yml';
|
||||||
|
}
|
||||||
|
// 2. 传统用户书签配置
|
||||||
|
else if (fs.existsSync('bookmarks.user.yml')) {
|
||||||
const userBookmarksFile = fs.readFileSync('bookmarks.user.yml', 'utf8');
|
const userBookmarksFile = fs.readFileSync('bookmarks.user.yml', 'utf8');
|
||||||
bookmarksConfig = yaml.load(userBookmarksFile);
|
bookmarksConfig = yaml.load(userBookmarksFile);
|
||||||
console.log('Using user bookmarks configuration from bookmarks.user.yml');
|
bookmarksSource = 'bookmarks.user.yml';
|
||||||
}
|
}
|
||||||
// 如果没有用户书签配置,则尝试读取默认书签配置
|
// 3. 模块化默认书签配置
|
||||||
|
else if (fs.existsSync('config/_default/pages/bookmarks.yml')) {
|
||||||
|
const defaultBookmarksFile = fs.readFileSync('config/_default/pages/bookmarks.yml', 'utf8');
|
||||||
|
bookmarksConfig = yaml.load(defaultBookmarksFile);
|
||||||
|
bookmarksSource = 'config/_default/pages/bookmarks.yml';
|
||||||
|
}
|
||||||
|
// 4. 传统默认书签配置 (最低优先级)
|
||||||
else if (fs.existsSync('bookmarks.yml')) {
|
else if (fs.existsSync('bookmarks.yml')) {
|
||||||
const bookmarksFile = fs.readFileSync('bookmarks.yml', 'utf8');
|
const bookmarksFile = fs.readFileSync('bookmarks.yml', 'utf8');
|
||||||
bookmarksConfig = yaml.load(bookmarksFile);
|
bookmarksConfig = yaml.load(bookmarksFile);
|
||||||
console.log('Using default bookmarks configuration from bookmarks.yml');
|
bookmarksSource = 'bookmarks.yml';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加书签页面配置
|
// 添加书签页面配置
|
||||||
if (bookmarksConfig) {
|
if (bookmarksConfig) {
|
||||||
config.bookmarks = bookmarksConfig;
|
config.bookmarks = bookmarksConfig;
|
||||||
|
console.log(`Using bookmarks configuration from ${bookmarksSource}`);
|
||||||
|
|
||||||
// 确保导航中有书签页面
|
// 确保导航中有书签页面
|
||||||
const hasBookmarksNav = config.navigation.some(nav => nav.id === 'bookmarks');
|
const hasBookmarksNav = config.navigation.some(nav => nav.id === 'bookmarks');
|
||||||
|
|||||||
Reference in New Issue
Block a user