Skip to content

Commit 424336e

Browse files
authored
Merge pull request #34 from devlive-community/dev
发布 2025.1.3
2 parents 9db1318 + 4d8db77 commit 424336e

File tree

10 files changed

+252
-58
lines changed

10 files changed

+252
-58
lines changed

docs/content/release/2025.1.3.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
---
2+
title: 2025.1.3
3+
status:
4+
type: new
5+
---
6+
7+
我们很高兴地宣布 PageForge 2025.1.3 正式发布。PageForge 是一款现代化的静态页面生成与部署平台,致力于为用户提供从创建到部署的一站式解决方案。
8+
9+
## 新增功能
10+
11+
- 支持页面 SEO 优化 (#7)
12+
- 添加评论功能 (#9)
13+
- 支持字体大小调节 (#13)
14+
- 新增菜单顶部 Banner (#21)
15+
- 支持页面分享功能 (#17)
16+
- 支持站点地图生成 (#5)
17+
- 支持 HTML 压缩 (#19)
18+
- 支持 Dark 模式主题切换
19+
- 支持图标功能与自定义按钮
20+
- 新增多个主页模板
21+
22+
## 界面优化
23+
24+
- 支持包含子项的导航项展开/收缩
25+
- 优化顶部菜单布局
26+
- 添加代码复制 hover 效果
27+
- 支持 diff 代码复制
28+
- 添加图标加载视觉效果
29+
- 添加 git 贡献者头像加载状态和占位符
30+
- 支持自定义页面状态显示
31+
- 修复页面状态提示被容器遮挡问题
32+
33+
## 架构改进
34+
35+
- 拆分主模版部分功能为独立组件
36+
- 拆分查看源码和修改源码为独立组件
37+
- 拆分移动端语言选择器
38+
- 优化导航获取方式
39+
- 支持国际化不存在的文件使用默认语言
40+
41+
## 问题修复
42+
43+
- 修复缩进情况下代码片段无法解析
44+
- 修复站点地图无法新页面打开问题
45+
- 修复未启用国际化时构建站点地图错误
46+
- 修复标题过长导致内容自动换行问题
47+
- 修复嵌套路径导致跳转异常
48+
49+
## 其他更新
50+
51+
- 添加 Google 广告代码支持
52+
- 新增站点地图文档
53+
- 添加压缩配置文档
54+
55+
## 链接
56+
57+
- GitHub: https://github.com/devlive-community/pageforge
58+
- 官网: https://pageforge.devlive.org
59+
60+
## 后续计划
61+
62+
我们将持续优化用户体验,提升文档展示效果,欢迎社区贡献者参与项目开发。
63+
64+
## 反馈与支持
65+
66+
如果您在使用过程中遇到任何问题,请通过 GitHub Issues 向我们反馈。您的建议对我们至关重要!
67+
68+
---
69+
70+
此版本带来了多项重要功能更新,包括 Dark 模式、SEO 优化、评论功能等重要特性,同时优化了用户界面和架构设计,建议所有用户升级到此版本。

docs/content/setup/comment.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
---
22
title: 评论设置
33
icon: message-circle
4+
status:
5+
type: new
46
---
57

68
PageForge 支持将您选择的第三方评论系统添加到任何页面的页脚。例如,我们将集成 [Giscus](https://giscus.app "Giscus" "_blank"),它是开源的、免费的,并使用 GitHub 讨论作为后端。

docs/content/setup/seo.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
title: SEO 设置
3+
icon: search
4+
status:
5+
type: new
6+
---
7+
8+
PageForge 支持 SEO 设置,可以帮助你优化网站的搜索引擎优化(SEO)。
9+
10+
## 基本用法
11+
12+
---
13+
14+
PageForge 支持 SEO 设置,可以帮助你优化网站的搜索引擎优化(SEO),包括标题、关键字和描述等。
15+
16+
```yaml
17+
site:
18+
description: PageForge 是一个简单的静态网站生成器,可以将 Markdown 文档转换为静态 HTML 页面。
19+
keywords: PageForge, Markdown, Static Site Generator
20+
author: devlive-community
21+
```
22+
23+
- `description`: 网站描述
24+
- `keywords`: 网站关键字
25+
- `author`: 网站作者,默认为 PageForge
26+
27+
> PageForge 支持的 SEO 设置包括标题、关键字和描述等,可以帮助你优化网站的搜索引擎优化(SEO),提高网站的排名。
28+
29+
## 页面配置
30+
31+
---
32+
33+
PageForge 支持设置页面的 SEO 设置,可方便为不同的页面设置不同的 SEO 设置。只需要设置 markdown 文件的元数据即可。
34+
35+
```yaml
36+
---
37+
description: PageForge 是一个简单的静态网站生成器,可以将 Markdown 文档转换为静态 HTML 页面。
38+
keywords: PageForge, Markdown, Static Site Generator
39+
---
40+
```
41+
42+
!!! danger "注意"
43+
44+
页面配置文件的元数据会覆盖全局的 SEO 设置,即使你设置了全局的 SEO 设置,也会被覆盖,页面 SEO 无法设置 `author`。
45+
46+
!!!

docs/content/setup/sitemap.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: 站点地图
2+
title: 站点地图设置
33
icon: map
44
---
55

docs/pageforge.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ site:
22
title: PageForge
33
hiddenTitle: true
44
description: PageForge 是一款现代化的静态页面生成与部署平台,旨在帮助用户快速创建精美的静态网站,并一键部署到 GitHub Pages。 无论是个人博客、项目文档还是企业官网,PageForge 都能让你轻松实现高效构建、智能部署和即时上线。
5+
keywords: PageForge, Markdown, Static Site Generator, GitHub Pages, GitLab Pages, Vercel, Netlify, GitHub Actions, Cloudflare Pages, Vercel Pages, Netlify Pages, 静态网站生成器, 静态网站部署器
56
logo: /assets/logo.svg
67
favicon: assets/logo.svg
78
baseUrl: https://pageforge.devlive.org
@@ -167,6 +168,7 @@ nav:
167168
- /setup/banner
168169
- /setup/font-size
169170
- /setup/comment
171+
- /setup/seo
170172
- InlineTemplate:
171173
- /setup/template/home
172174
- /setup/template/enterprise
@@ -187,6 +189,7 @@ nav:
187189
- /usage/button
188190
- /usage/icon
189191
- ReleaseNotes:
192+
- /release/2025.1.3
190193
- /release/2025.1.2
191194
- /release/2025.1.1
192195
- /release/2025.1.0
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<% if (locals.siteData.feature?.backTop?.enable) { %>
2+
<button id="back-to-top"
3+
class="fixed right-4 bottom-24 lg:bottom-4 z-20 bg-white dark:bg-gray-800 p-2 rounded-full shadow-lg opacity-0 transition-opacity duration-200 hover:bg-gray-100 dark:hover:bg-gray-700"
4+
onclick="window.scrollTo({top: 0, behavior: 'smooth'})">
5+
<svg class="w-6 h-6 text-gray-600 dark:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
6+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 10l7-7m0 0l7 7m-7-7v18"></path>
7+
</svg>
8+
</button>
9+
10+
<script>
11+
// 控制返回顶部按钮的显示和隐藏
12+
window.addEventListener('scroll', function () {
13+
const backToTop = document.getElementById('back-to-top');
14+
if (window.scrollY > 300) {
15+
backToTop.style.opacity = '1';
16+
}
17+
else {
18+
backToTop.style.opacity = '0';
19+
}
20+
});
21+
</script>
22+
<% } %>

templates/includes/feature-lucide.ejs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<% if (locals.siteData.feature?.lucide?.enable) { %>
2+
<script src="<%= locals.siteData.feature.lucide.cdn || 'https://unpkg.com/lucide@latest/dist/umd/lucide.js' %>"></script>
3+
<script>
4+
// 在页面加载完成后执行
5+
window.addEventListener('load', () => {
6+
// 初始化图标
7+
lucide.createIcons();
8+
9+
// 延迟检查图标是否加载完成
10+
setTimeout(() => {
11+
document.querySelectorAll('[data-lucide]').forEach(icon => {
12+
if (icon.children.length > 0) {
13+
icon.style.display = 'block';
14+
const skeleton = icon.previousElementSibling;
15+
if (skeleton) {
16+
skeleton.style.display = 'none';
17+
}
18+
}
19+
});
20+
}, 100);
21+
});
22+
</script>
23+
<% } %>

templates/includes/page-status.ejs

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,19 @@ const getDefaultText = (type) => {
2020
const renderDefaultIcon = (type) => {
2121
switch (type) {
2222
case 'draft':
23-
return `<svg class="w-4 h-4 text-gray-500 hover:opacity-80 cursor-pointer peer" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
23+
return `<svg class="w-4 h-4 text-gray-500 hover:opacity-80 cursor-pointer status-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
2424
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
2525
</svg>`;
2626
case 'new':
27-
return `<svg class="w-4 h-4 text-green-500 hover:opacity-80 cursor-pointer peer" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
27+
return `<svg class="w-4 h-4 text-green-500 hover:opacity-80 cursor-pointer status-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
2828
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" />
2929
</svg>`;
3030
case 'deprecated':
31-
return `<svg class="w-4 h-4 text-red-500 hover:opacity-80 cursor-pointer peer" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
31+
return `<svg class="w-4 h-4 text-red-500 hover:opacity-80 cursor-pointer status-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
3232
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
3333
</svg>`;
3434
case 'updated':
35-
return `<svg class="w-4 h-4 text-blue-500 hover:opacity-80 cursor-pointer peer" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
35+
return `<svg class="w-4 h-4 text-blue-500 hover:opacity-80 cursor-pointer status-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
3636
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
3737
</svg>`;
3838
default:
@@ -43,30 +43,94 @@ const renderDefaultIcon = (type) => {
4343

4444
<% if (item.status?.type) { %>
4545
<div class="inline-flex items-center">
46-
<div class="relative">
46+
<div data-tooltip="<%= item.status.text || getDefaultText(item.status.type) %>" class="relative status-container">
4747
<% if (item.status.type === 'custom') { %>
4848
<% if (locals.siteData.feature?.lucide?.enable && item.status.icon) { %>
4949
<!-- Lucide 图标 -->
50-
<i data-lucide="<%= item.status.icon %>" class="w-4 h-4 text-gray-600 hover:opacity-80 cursor-pointer peer"></i>
50+
<i data-lucide="<%= item.status.icon %>" class="w-4 h-4 text-gray-600 hover:opacity-80 cursor-pointer status-icon"></i>
5151
<% } else if (item.status.icon && item.status.icon.includes('<svg')) { %>
5252
<!-- 自定义 SVG 图标 -->
53-
<span class="inline-block w-4 h-4 hover:opacity-80 cursor-pointer peer">
53+
<span class="inline-block w-4 h-4 hover:opacity-80 cursor-pointer status-icon">
5454
<%- item.status.icon %>
5555
</span>
5656
<% } else { %>
5757
<!-- 默认图标,当没有提供有效的图标时 -->
58-
<svg class="w-4 h-4 text-gray-500 hover:opacity-80 cursor-pointer peer" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
58+
<svg class="w-4 h-4 text-gray-500 hover:opacity-80 cursor-pointer status-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
5959
stroke="currentColor">
6060
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
6161
</svg>
6262
<% } %>
6363
<% } else { %>
6464
<%- renderDefaultIcon(item.status.type) %>
6565
<% } %>
66-
67-
<span class="absolute left-1/2 -translate-x-1/2 -top-8 px-2 py-1 bg-gray-900 text-white text-xs rounded scale-0 peer-hover:scale-100 transition-transform duration-100 origin-bottom pointer-events-none">
68-
<%= item.status.text || getDefaultText(item.status.type) %>
69-
</span>
7066
</div>
7167
</div>
68+
69+
<script>
70+
document.addEventListener('DOMContentLoaded', () => {
71+
// 创建或获取 tooltips 容器
72+
let tooltipContainer = document.getElementById('tooltip-container');
73+
if (!tooltipContainer) {
74+
tooltipContainer = document.createElement('div');
75+
tooltipContainer.id = 'tooltip-container';
76+
document.body.appendChild(tooltipContainer);
77+
}
78+
79+
// 创建单个 tooltip 元素
80+
const tooltip = document.createElement('div');
81+
tooltip.className = 'fixed hidden px-2 py-1 bg-gray-900 text-white text-xs rounded whitespace-nowrap z-[9999] pointer-events-none transition-opacity duration-200';
82+
tooltipContainer.appendChild(tooltip);
83+
84+
// 监听所有状态图标
85+
document.querySelectorAll('.status-container').forEach(container => {
86+
const icon = container.querySelector('.status-icon');
87+
const text = container.dataset.tooltip;
88+
89+
container.addEventListener('mouseenter', () => {
90+
const rect = container.getBoundingClientRect();
91+
tooltip.textContent = text;
92+
tooltip.style.display = 'block';
93+
94+
// 获取 tooltip 的尺寸
95+
const tooltipRect = tooltip.getBoundingClientRect();
96+
97+
// 计算位置
98+
let top = rect.top - tooltipRect.height - 8;
99+
const left = rect.left + (rect.width - tooltipRect.width) / 2;
100+
101+
// 如果顶部空间不足,显示在底部
102+
if (top < 8) {
103+
top = rect.bottom + 8;
104+
}
105+
106+
tooltip.style.top = `${top}px`;
107+
tooltip.style.left = `${left}px`;
108+
});
109+
110+
container.addEventListener('mouseleave', () => {
111+
tooltip.style.display = 'none';
112+
});
113+
});
114+
115+
// 处理页面滚动时更新位置
116+
window.addEventListener('scroll', () => {
117+
const visibleTooltip = document.querySelector('#tooltip-container div[style*="display: block"]');
118+
if (visibleTooltip) {
119+
const container = document.querySelector('.status-container:hover');
120+
if (container) {
121+
const rect = container.getBoundingClientRect();
122+
const tooltipRect = visibleTooltip.getBoundingClientRect();
123+
124+
let top = rect.top - tooltipRect.height - 8;
125+
if (top < 8) {
126+
top = rect.bottom + 8;
127+
}
128+
129+
visibleTooltip.style.top = `${top}px`;
130+
visibleTooltip.style.left = `${rect.left + (rect.width - tooltipRect.width) / 2}px`;
131+
}
132+
}
133+
}, {passive: true});
134+
});
135+
</script>
72136
<% } %>

templates/includes/seo.ejs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<!-- 基础 SEO meta 标签 -->
2+
<meta name="description" content="<%= pageData.description || siteData.site.description %>">
3+
<meta name="keywords" content="<%= pageData.keywords || siteData.site.keywords %>">
4+
<meta name="author" content="<%= siteData.site.author || 'PageForge' %>">

0 commit comments

Comments
 (0)