edgepress 2026.9.5 → 2026.9.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +5 -3
  2. package/config.yml +121 -69
  3. package/content/pages/home/en.md +15 -0
  4. package/content/pages/home/zh-cn.md +16 -1
  5. package/content/pages/plugin-development/en.md +5 -4
  6. package/content/pages/plugin-development/zh-cn.md +5 -4
  7. package/content/pages/privacy-policy/en.md +8 -5
  8. package/content/pages/privacy-policy/zh-cn.md +8 -5
  9. package/content/pages/theme-development/en.md +24 -0
  10. package/content/pages/theme-development/zh-cn.md +24 -0
  11. package/content/posts/2026-09-23-edgepress-on-the-edge/en.md +12 -2
  12. package/content/posts/2026-09-23-edgepress-on-the-edge/zh-cn.md +12 -2
  13. package/content/posts/2026-09-24-edgepress-release-2026-9-1/en.md +2 -0
  14. package/content/posts/2026-09-24-edgepress-release-2026-9-1/zh-cn.md +2 -0
  15. package/content/posts/2026-09-25-edgepress-release-2026-9-2/en.md +2 -0
  16. package/content/posts/2026-09-25-edgepress-release-2026-9-2/zh-cn.md +2 -0
  17. package/content/posts/2026-09-25-edgepress-release-2026-9-3/en.md +2 -0
  18. package/content/posts/2026-09-25-edgepress-release-2026-9-3/zh-cn.md +2 -0
  19. package/content/posts/2026-09-25-edgepress-release-2026-9-4/en.md +2 -0
  20. package/content/posts/2026-09-25-edgepress-release-2026-9-4/zh-cn.md +2 -0
  21. package/content/posts/2026-09-25-edgepress-release-2026-9-5/en.md +2 -0
  22. package/content/posts/2026-09-25-edgepress-release-2026-9-5/zh-cn.md +2 -0
  23. package/content/posts/2026-09-25-edgepress-release-2026-9-6/en.md +31 -0
  24. package/content/posts/2026-09-25-edgepress-release-2026-9-6/zh-cn.md +31 -0
  25. package/content/posts/2026-09-26-edgepress-release-2026-9-7/en.md +26 -0
  26. package/content/posts/2026-09-26-edgepress-release-2026-9-7/zh-cn.md +26 -0
  27. package/content/posts/2026-09-26-edgepress-release-2026-9-8/en.md +26 -0
  28. package/content/posts/2026-09-26-edgepress-release-2026-9-8/zh-cn.md +26 -0
  29. package/languages/base/en.json +9 -2
  30. package/languages/packs/zh-CN.json +9 -2
  31. package/package.json +2 -1
  32. package/plugins/consent/index.js +4 -2
  33. package/project-compatibility.json +26 -2
  34. package/src/build.js +5 -4
  35. package/src/cli.js +8 -13
  36. package/src/config.js +72 -14
  37. package/src/content.js +55 -1
  38. package/src/generators.js +125 -23
  39. package/src/i18n.js +6 -0
  40. package/src/icons.js +16 -0
  41. package/src/maintenance.js +0 -27
  42. package/src/markdown.js +88 -9
  43. package/src/page-blocks.js +84 -18
  44. package/src/page-check.js +79 -25
  45. package/src/report-visuals.js +111 -16
  46. package/src/theme.js +13 -2
  47. package/static/edgepress/brand/edgepress-mark.svg +7 -0
  48. package/static/edgepress/brand/mascot-209.webp +0 -0
  49. package/static/edgepress/brand/mascot-418.png +0 -0
  50. package/static/edgepress/brand/mascot-418.webp +0 -0
  51. package/static/edgepress/brand/mascot.png +0 -0
  52. package/static/edgepress/brand/og-image.png +0 -0
  53. package/static/edgepress/brand/og-image.svg +46 -0
  54. package/static/edgepress/code-copy.js +55 -0
  55. package/static/edgepress/icons.svg +24 -0
  56. package/static/edgepress/plugins/consent/manager.js +204 -71
  57. package/static/edgepress/plugins/statistics/cloudflare-web-analytics.js +1 -0
  58. package/themes/atelier/assets/style.css +98 -20
  59. package/themes/atelier/layouts/partials/head.html +5 -0
  60. package/themes/atelier/layouts/partials/header.html +1 -1
  61. package/themes/default/assets/style.css +99 -20
  62. package/themes/default/layouts/partials/head.html +3 -0
  63. package/themes/default/layouts/partials/header.html +1 -1
  64. package/themes/signal/assets/style.css +98 -20
  65. package/themes/signal/layouts/partials/head.html +5 -0
  66. package/themes/signal/layouts/partials/header.html +1 -1
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  title: "EdgePress 与 Markdown:完整写作指南"
3
+ author: toewpq
3
4
  date: 2026-09-23
4
5
  lang: zh-CN
5
6
  slug: markdown-syntax-guide
@@ -8,6 +9,7 @@ tags:
8
9
  - Markdown
9
10
  - 写作指南
10
11
  description: "通过可直接查看的示例,了解 EdgePress 支持的 Markdown、GitHub Flavored Markdown、安全 HTML、嵌套语法和功能边界。"
12
+
11
13
  ---
12
14
 
13
15
  EdgePress 对 **pages(页面)** 和 **posts(文章)** 采用不同的编辑方式。页面在 Markdown 前置资料中配置有序、可编辑的元素块;文章则使用 Markdown 渲染器。本文本身就是一篇实际渲染的文章,可作为语法参考。
@@ -102,6 +104,14 @@ CommonMark 也支持每行缩进四个空格的代码块:
102
104
 
103
105
  ![Markdown 源文件依次经过 EdgePress 渲染与 HTML 清理,生成发布文章的流程图](/edgepress-markdown-guide.svg "EdgePress 文章生成流程")
104
106
 
107
+ 文章也支持使用图片形状的 Markdown 语法插入直接视频文件。替代文字用于向辅助技术描述视频,可选标题会显示为视频说明:
108
+
109
+ ```markdown
110
+ ![一只风筝掠过田野](https://media.example.org/field-recording.mp4 "清晨的风况测试")
111
+ ```
112
+
113
+ MP4、WebM、OGV 和 OGG 文件会渲染为带控制按钮的原生视频播放器,并设置 `preload="none"`,在访客播放前不预加载视频。文章不支持嵌入式播放器或任意 iframe 标记。
114
+
105
115
  ## 列表与嵌套组合
106
116
 
107
117
  无序列表项以 `-`、`+` 或 `*` 开头;有序列表项以数字和句点开头。子项目缩进至少两个空格。两种列表都可以包含强调、代码和链接:
@@ -158,12 +168,12 @@ GFM 也会把 `www.example.com` 与 `https://example.com` 转换成链接,并
158
168
 
159
169
  <script>alert('unsafe');</script>
160
170
 
161
- 视频请使用页面的「媒体与文字」元素;文章不允许嵌入框架或任意脚本。
171
+ 视频请使用上面的图片形状 Markdown 语法;文章不允许嵌入播放器框架或任意脚本。
162
172
 
163
173
  本项目启用的文章语法范围是:CommonMark 的块级和行内语法,加上 GFM 表格、自动链接、任务列表与删除线。脚注、数学公式、Mermaid 图表、下划线语法和定义列表等扩展没有启用;除非添加受信任的渲染扩展,否则它们会作为普通文字显示。
164
174
 
165
175
  ## 在 EdgePress 中写文章
166
176
 
167
- 在 `content/posts/` 下为每篇文章建立目录,并放入 `en.md` 或 `zh-cn.md` 这样的语言文件。前置资料中的 `lang` 值必须与文件名匹配。添加标题、日期、网址别名、标签和可选摘要,再在前置资料之后编写 Markdown 正文。运行 `edgepress build` 生成文章;运行 `edgepress check` 则会构建网站,并输出无障碍、Agent 友好度、移动端截图与 Markdown 渲染报告。
177
+ 在 `content/posts/` 下为每篇文章建立目录,并放入 `en.md` 或 `zh-cn.md` 这样的语言文件。前置资料中的 `lang` 值必须与文件名匹配。添加标题、作者、日期、网址别名、标签和可选摘要,再在前置资料之后编写 Markdown 正文。作者会显示在文章卡片和文章页中,也会写入订阅源、搜索索引及 BlogPosting 结构化数据。运行 `edgepress build` 生成文章;运行 `edgepress check` 则会构建网站,并输出无障碍、Agent 友好度、移动端截图与 Markdown 渲染报告。
168
178
 
169
179
  本地文章搜索在浏览器中读取网站生成的 `search.json`,搜索文章标题和正文,不会把搜索词发送给第三方服务。
@@ -1,11 +1,13 @@
1
1
  ---
2
2
  title: EdgePress September 2026 update
3
+ author: toewpq
3
4
  date: 2026-09-24
4
5
  lang: en
5
6
  tags:
6
7
  - release
7
8
  - page builder
8
9
  - accessibility
10
+
9
11
  ---
10
12
  This is the first requested release update for September 2026.
11
13
 
@@ -1,11 +1,13 @@
1
1
  ---
2
2
  title: EdgePress 2026 年 9 月更新
3
+ author: toewpq
3
4
  date: 2026-09-24
4
5
  lang: zh-CN
5
6
  tags:
6
7
  - 发布
7
8
  - 页面构建器
8
9
  - 无障碍
10
+
9
11
  ---
10
12
  这是 2026 年 9 月针对本次需求的第一次更新。
11
13
 
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  title: "EdgePress September 2026 update: request 2"
3
+ author: toewpq
3
4
  date: 2026-09-25
4
5
  lang: en
5
6
  slug: edgepress-release-2026-9-2
@@ -7,6 +8,7 @@ tags:
7
8
  - release
8
9
  - responsive design
9
10
  - Markdown
11
+
10
12
  ---
11
13
 
12
14
  This is the second requested EdgePress update for September 2026.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  title: EdgePress 2026 年 9 月更新:第 2 次需求
3
+ author: toewpq
3
4
  date: 2026-09-25
4
5
  lang: zh-CN
5
6
  slug: edgepress-release-2026-9-2
@@ -7,6 +8,7 @@ tags:
7
8
  - 发布
8
9
  - 移动端适配
9
10
  - Markdown
11
+
10
12
  ---
11
13
 
12
14
  这是 EdgePress 在 2026 年 9 月针对第 2 次需求的更新。
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  title: "EdgePress September 2026 update: request 3"
3
+ author: toewpq
3
4
  date: 2026-09-25
4
5
  lang: en
5
6
  slug: edgepress-release-2026-9-3
@@ -7,6 +8,7 @@ tags:
7
8
  - release
8
9
  - npm
9
10
  - themes
11
+
10
12
  ---
11
13
 
12
14
  This is the third requested EdgePress update for September 2026.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  title: EdgePress 2026 年 9 月更新:第 3 次需求
3
+ author: toewpq
3
4
  date: 2026-09-25
4
5
  lang: zh-CN
5
6
  slug: edgepress-release-2026-9-3
@@ -7,6 +8,7 @@ tags:
7
8
  - 发布
8
9
  - npm
9
10
  - 主题
11
+
10
12
  ---
11
13
 
12
14
  这是 EdgePress 在 2026 年 9 月针对第 3 次需求的更新。
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  title: "EdgePress September 2026 update: request 4"
3
+ author: toewpq
3
4
  date: 2026-09-25
4
5
  lang: en
5
6
  slug: edgepress-release-2026-9-4
@@ -7,6 +8,7 @@ tags:
7
8
  - release
8
9
  - reports
9
10
  - cloudflare
11
+
10
12
  ---
11
13
 
12
14
  This is the fourth requested EdgePress update for September 2026.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  title: EdgePress 2026 年 9 月更新:第 4 次需求
3
+ author: toewpq
3
4
  date: 2026-09-25
4
5
  lang: zh-CN
5
6
  slug: edgepress-release-2026-9-4
@@ -7,6 +8,7 @@ tags:
7
8
  - 发布
8
9
  - 报告
9
10
  - Cloudflare
11
+
10
12
  ---
11
13
 
12
14
  这是 EdgePress 在 2026 年 9 月针对第 4 次需求的更新。
@@ -1,11 +1,13 @@
1
1
  ---
2
2
  title: "EdgePress September 2026 update: request 5"
3
+ author: toewpq
3
4
  date: 2026-09-25
4
5
  lang: en
5
6
  slug: edgepress-release-2026-9-5
6
7
  tags:
7
8
  - release
8
9
  - cloudflare
10
+
9
11
  ---
10
12
 
11
13
  This is the fifth requested EdgePress update for September 2026.
@@ -1,11 +1,13 @@
1
1
  ---
2
2
  title: EdgePress 2026 年 9 月更新:第 5 次需求
3
+ author: toewpq
3
4
  date: 2026-09-25
4
5
  lang: zh-CN
5
6
  slug: edgepress-release-2026-9-5
6
7
  tags:
7
8
  - 发布
8
9
  - Cloudflare
10
+
9
11
  ---
10
12
 
11
13
  这是 EdgePress 在 2026 年 9 月针对第 5 次需求的更新。
@@ -0,0 +1,31 @@
1
+ ---
2
+ title: "EdgePress September 2026 update: request 6"
3
+ author: toewpq
4
+ date: 2026-09-25
5
+ lang: en
6
+ slug: edgepress-release-2026-9-6
7
+ tags:
8
+ - release
9
+ - themes
10
+
11
+ ---
12
+
13
+ This is the sixth requested EdgePress update for September 2026.
14
+
15
+ ## Navigation and article reading
16
+
17
+ The shared navigation now includes a localized Posts link to the article archive. Markdown post pages generate a linked contents list from their headings.
18
+
19
+ ## Code blocks and small screens
20
+
21
+ Generated code blocks now have an accessible copy button. Theme styles wrap long command lines so the full command remains readable on narrow screens without horizontal dragging. The theme development guide describes the shared behavior.
22
+
23
+ The project compatibility history is updated to version `2026.9.6`.
24
+
25
+ ## Theme palettes
26
+
27
+ The bundled themes have refreshed, higher-contrast palettes.
28
+
29
+ ## A clearer visual identity and reading path
30
+
31
+ The site now shares a local set of 22 SVG icons across theme navigation and feature cards. A page-shaped project mark, folded-paper sparrow mascot, and Open Graph image give the project a consistent preview on the homepage and when shared. Latest-post lists link into older archive pages, and localized post metadata now names each article's author.
@@ -0,0 +1,31 @@
1
+ ---
2
+ title: EdgePress 2026 年 9 月更新:第 6 次需求
3
+ author: toewpq
4
+ date: 2026-09-25
5
+ lang: zh-CN
6
+ slug: edgepress-release-2026-9-6
7
+ tags:
8
+ - 发布
9
+ - 主题
10
+
11
+ ---
12
+
13
+ 这是 EdgePress 在 2026 年 9 月针对第 6 次需求的更新。
14
+
15
+ ## 导航和文章阅读
16
+
17
+ 全站导航新增本地化的文章入口,指向文章归档页。Markdown 文章页会根据标题生成可跳转的目录。
18
+
19
+ ## 代码块和小屏显示
20
+
21
+ 生成的代码块新增无障碍复制按钮。主题样式会自动折行显示较长命令,窄屏下无需左右拖动即可阅读完整内容。主题开发指南也已补充对应说明。
22
+
23
+ 项目兼容性历史已更新至 `2026.9.6`。
24
+
25
+ ## 主题配色
26
+
27
+ 内置主题已换成对比度更清晰、视觉更鲜明的新配色。
28
+
29
+ ## 更清晰的视觉识别与阅读路径
30
+
31
+ 网站现在使用一组本地 22 枚 SVG 图标,为主题导航和功能卡片提供一致的图形语言。页面式项目图标、折页纸鸟吉祥物和 OG 分享图让首页与链接预览拥有统一的识别形象。首页文章列表可继续翻看旧文章,本地化文章元数据也会标明作者。
@@ -0,0 +1,26 @@
1
+ ---
2
+ title: "EdgePress September 2026 update: request 7"
3
+ author: toewpq
4
+ date: 2026-09-26
5
+ lang: en
6
+ slug: edgepress-release-2026-9-7
7
+ tags:
8
+ - release
9
+ - performance
10
+ - privacy
11
+
12
+ ---
13
+
14
+ This is the seventh requested EdgePress update for September 2026.
15
+
16
+ ## Consent and privacy disclosures
17
+
18
+ Cloudflare Web Analytics is configured as a statistics service and remains behind the visitor's explicit consent. Browser service groups now live under `plugins.consent` in `config.yml`. The privacy policy lists each enabled service's provider, category, purpose, and retention period when the site is built.
19
+
20
+ ## Images and article lists
21
+
22
+ The homepage uses smaller responsive mascot images. Article archives are sorted newest-first across all available languages, while homepage latest-post lists stay in the current language. Post excerpts now remove Markdown syntax before display.
23
+
24
+ ## Mobile controls and maintenance
25
+
26
+ The consent panel has a responsive mobile layout, clearer service details, and larger touch targets. The unused integrations directory and obsolete cleanup paths for retired audit files have been removed.
@@ -0,0 +1,26 @@
1
+ ---
2
+ title: EdgePress 2026 年 9 月更新:第 7 次需求
3
+ author: toewpq
4
+ date: 2026-09-26
5
+ lang: zh-CN
6
+ slug: edgepress-release-2026-9-7
7
+ tags:
8
+ - 发布
9
+ - 性能
10
+ - 隐私
11
+
12
+ ---
13
+
14
+ 这是 EdgePress 在 2026 年 9 月针对第 7 次需求的更新。
15
+
16
+ ## 同意管理和隐私披露
17
+
18
+ Cloudflare Web Analytics 已作为统计服务接入,并继续等待访客明确同意后才会加载。`config.yml` 中的浏览器服务组现在统一放在 `plugins.consent` 下。每次构建时,隐私政策都会列出已启用服务的供应商、类别、用途和保留期限。
19
+
20
+ ## 图片和文章列表
21
+
22
+ 首页现在使用较小的自适应吉祥物图片。文章归档会跨语言按发布时间从新到旧排列;首页最新文章列表仍按当前语言显示。文章摘要在显示前会去除 Markdown 标记。
23
+
24
+ ## 移动端控件和维护
25
+
26
+ 同意面板改进了移动端布局和服务说明,并扩大了按钮触控区域。未使用的 integrations 空目录和已废弃审计文件清理路径也已移除。
@@ -0,0 +1,26 @@
1
+ ---
2
+ title: "EdgePress September 2026 update: request 8"
3
+ author: toewpq
4
+ date: 2026-09-26
5
+ lang: en
6
+ slug: edgepress-release-2026-9-8
7
+ tags:
8
+ - release
9
+ - privacy
10
+ - Markdown
11
+
12
+ ---
13
+
14
+ This is the eighth requested EdgePress update for September 2026.
15
+
16
+ ## Date-based consent and localized disclosures
17
+
18
+ Consent settings use a required `proposedDate` and an optional `effectiveDate` instead of a version counter. The generated privacy policy displays these dates and lists configured services with localized names, purposes, data categories, recipients, and retention details. Accept and reject controls have equal prominence, optional services start unchecked, and visitors can expand the panel to make a granular choice. The consent component has scoped theme styles and its own viewport checks and screenshots, separate from page-content layout checks.
19
+
20
+ ## Video in Markdown posts
21
+
22
+ Posts can insert direct MP4, WebM, OGV, and OGG files using image-shaped Markdown syntax. The renderer creates an accessible native player with controls and `preload="none"`.
23
+
24
+ ## Post lists and summaries
25
+
26
+ Archives now choose one language version per post and sort equal-date release notes by their numeric sequence. The homepage continues to show the latest posts in the current language. Archive, tag, and homepage cards render Markdown formatting in summaries.
@@ -0,0 +1,26 @@
1
+ ---
2
+ title: EdgePress 2026 年 9 月更新:第 8 次需求
3
+ author: toewpq
4
+ date: 2026-09-26
5
+ lang: zh-CN
6
+ slug: edgepress-release-2026-9-8
7
+ tags:
8
+ - 发布
9
+ - 隐私
10
+ - Markdown
11
+
12
+ ---
13
+
14
+ 这是 EdgePress 在 2026 年 9 月针对第 8 次需求的更新。
15
+
16
+ ## 日期化同意配置与多语言披露
17
+
18
+ 同意设置使用必填的 `proposedDate` 拟定日期和可选的 `effectiveDate` 生效日期,取代版本计数。生成的隐私政策会显示这两个日期,并列出已配置服务的多语言名称、用途、数据类别、接收方和保留期限。接受与拒绝按钮具有同等醒目程度,可选服务默认不勾选;访客可以展开面板逐项设置。组件样式限定在同意管理器内,其视口检查和截图也与页面内容布局检测分开。
19
+
20
+ ## Markdown 文章中的视频
21
+
22
+ 文章可使用图片形状的 Markdown 语法插入 MP4、WebM、OGV 和 OGG 直接视频文件。渲染器会生成带控制按钮的原生播放器,并设置 `preload="none"`。
23
+
24
+ ## 文章列表和摘要
25
+
26
+ 归档现在会为每篇文章选择一个语言版本,并在日期相同时按数字序号从新到旧排列发布记录。首页仍按当前语言显示最新文章。归档、标签和首页卡片的摘要都会渲染 Markdown 格式。
@@ -24,6 +24,7 @@
24
24
  "searchResultCount": "Matching articles: {count}.",
25
25
  "markdownTaskComplete": "Task complete",
26
26
  "markdownTaskIncomplete": "Task incomplete",
27
+ "postContents": "On this page",
27
28
  "feed": "Atom feed",
28
29
  "languageName": "English",
29
30
  "mainNavigationLabel": "Main navigation",
@@ -33,9 +34,10 @@
33
34
  "pagination": "Pagination",
34
35
  "pages": "Project pages",
35
36
  "latestPosts": "Latest posts",
37
+ "postAuthor": "By {author}",
36
38
  "privacySettings": "Privacy settings",
37
- "privacyNotice": "Optional services",
38
- "privacyIntro": "Choose which optional services may load. Nothing is enabled until you save or accept your choices.",
39
+ "privacyNotice": "Privacy choices",
40
+ "privacyIntro": "Optional services stay off until you choose. Accept or reject them now, or review each service before saving a custom choice.",
39
41
  "acceptOptional": "Accept all",
40
42
  "rejectOptional": "Reject optional",
41
43
  "savePreferences": "Save choices",
@@ -44,7 +46,12 @@
44
46
  "privacyPolicy": "Privacy policy",
45
47
  "privacyController": "Site operator",
46
48
  "servicePurpose": "Purpose",
49
+ "serviceDataCategories": "Data used",
50
+ "serviceRecipient": "Recipient",
47
51
  "serviceRetention": "Retention",
52
+ "reviewDetails": "Review data and service settings",
53
+ "managePrivacy": "Manage choices",
54
+ "essentialStorage": "Your choice is saved in this browser for up to {days} days. This preference storage is required to remember your choice.",
48
55
  "pluginTracking": "Tracking",
49
56
  "pluginStatistics": "Statistics",
50
57
  "pluginAdvertising": "Advertising",
@@ -24,6 +24,7 @@
24
24
  "searchResultCount": "匹配文章数:{count}。",
25
25
  "markdownTaskComplete": "任务已完成",
26
26
  "markdownTaskIncomplete": "任务未完成",
27
+ "postContents": "文章目录",
27
28
  "feed": "Atom 订阅",
28
29
  "languageName": "简体中文",
29
30
  "mainNavigationLabel": "主导航",
@@ -33,9 +34,10 @@
33
34
  "pagination": "分页导航",
34
35
  "pages": "项目文档",
35
36
  "latestPosts": "最新文章",
37
+ "postAuthor": "作者:{author}",
36
38
  "privacySettings": "隐私设置",
37
- "privacyNotice": "可选服务",
38
- "privacyIntro": "请选择允许加载的可选服务。保存或接受之前,不会启用任何服务。",
39
+ "privacyNotice": "隐私选择",
40
+ "privacyIntro": "可选服务默认关闭。你可以立即全部接受或拒绝,也可以先查看各项服务并保存自定义选择。",
39
41
  "acceptOptional": "全部接受",
40
42
  "rejectOptional": "拒绝可选服务",
41
43
  "savePreferences": "保存选择",
@@ -44,7 +46,12 @@
44
46
  "privacyPolicy": "隐私政策",
45
47
  "privacyController": "网站运营者",
46
48
  "servicePurpose": "用途",
49
+ "serviceDataCategories": "涉及的数据",
50
+ "serviceRecipient": "接收方",
47
51
  "serviceRetention": "保留期限",
52
+ "reviewDetails": "查看数据详情并管理服务",
53
+ "managePrivacy": "管理选择",
54
+ "essentialStorage": "此浏览器会保存你的选择,最长 {days} 天。这项偏好存储用于记住你的选择。",
48
55
  "pluginTracking": "跟踪",
49
56
  "pluginStatistics": "统计",
50
57
  "pluginAdvertising": "广告",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edgepress",
3
- "version": "2026.9.5",
3
+ "version": "2026.9.8",
4
4
  "description": "A static website builder for Cloudflare Workers with editable pages, Markdown posts, local search, themes, and consent-gated integrations.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -53,6 +53,7 @@
53
53
  "yaml": "^2.9.1"
54
54
  },
55
55
  "devDependencies": {
56
+ "playwright": "^1.63.0",
56
57
  "wrangler": "^4.129.1"
57
58
  }
58
59
  }
@@ -21,7 +21,9 @@ export default function consentManager(api) {
21
21
  const ui = Object.fromEntries([
22
22
  'privacySettings', 'privacyNotice', 'privacyIntro', 'acceptOptional', 'rejectOptional',
23
23
  'savePreferences', 'optionalServices', 'noIntegrations', 'privacyPolicy', 'privacyController',
24
- 'servicePurpose', 'serviceRetention', 'consentReload', 'closePrivacy'
24
+ 'servicePurpose', 'serviceDataCategories', 'serviceRecipient', 'serviceRetention', 'consentReload',
25
+ 'closePrivacy', 'reviewDetails', 'managePrivacy', 'essentialStorage', 'pluginTracking',
26
+ 'pluginStatistics', 'pluginAdvertising', 'pluginCaptcha'
25
27
  ].map((key) => [key, translate(config, locale, key)]));
26
28
  const services = ['tracking', 'statistics', 'advertising', 'captcha'].flatMap((group) =>
27
29
  config.browserPlugins[group].enabled ? config.browserPlugins[group].services.map((service) => ({ ...service, category: group })) : []
@@ -32,7 +34,7 @@ export default function consentManager(api) {
32
34
  integrations: services
33
35
  };
34
36
  if (privacy.policyUrl.startsWith('/')) privacy.policyUrl = localizedUrl(config, locale, privacy.policyUrl);
35
- const payload = safeJson({ privacy, ui, siteLanguage: locale });
37
+ const payload = safeJson({ privacy, ui, siteLanguage: locale, defaultLocale: config.i18n.defaultLocale });
36
38
  const integration = '<script type="application/json" id="edgepress-privacy-config">' + payload + '</script>' +
37
39
  '<script src="/edgepress/plugins/consent/manager.js" defer></script>';
38
40
  return html.replace(/<\/body\s*>/i, integration + '</body>');
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "frameworkVersion": "2026.9.5",
3
+ "frameworkVersion": "2026.9.8",
4
4
  "requirements": {
5
5
  "nodeMajor": 22,
6
6
  "nodeMinimumVersion": "22.12.0",
@@ -25,7 +25,13 @@
25
25
  { "name": "Compact localized language selector", "stage": "experimental", "since": "2026.9.2" },
26
26
  { "name": "npm theme installation and blank theme scaffolds", "stage": "experimental", "since": "2026.9.3" },
27
27
  { "name": "PDF-only audit output and temporary screenshot capture", "stage": "experimental", "since": "2026.9.4" },
28
- { "name": "Cloudflare one-click deployment guidance for existing and new projects", "stage": "stable", "since": "2026.9.5" }
28
+ { "name": "Cloudflare one-click deployment guidance for existing and new projects", "stage": "stable", "since": "2026.9.5" },
29
+ { "name": "Localized Posts navigation and responsive code copy controls", "stage": "experimental", "since": "2026.9.6" },
30
+ { "name": "Linked tables of contents on Markdown post pages", "stage": "experimental", "since": "2026.9.6" },
31
+ { "name": "Consent-nested browser services and generated privacy disclosures", "stage": "experimental", "since": "2026.9.7" },
32
+ { "name": "Responsive homepage imagery and chronological all-post archives", "stage": "experimental", "since": "2026.9.7" },
33
+ { "name": "Date-versioned localized consent disclosures with isolated responsive checks", "stage": "experimental", "since": "2026.9.8" },
34
+ { "name": "Markdown video syntax and locale-deduplicated chronological post lists", "stage": "experimental", "since": "2026.9.8" }
29
35
  ],
30
36
  "releaseHistory": [
31
37
  {
@@ -57,6 +63,24 @@
57
63
  "date": "2026-09-25",
58
64
  "request": 5,
59
65
  "notes": "Clarify that the Cloudflare one-click deployment button creates a new repository copy and Worker, explain unique naming and the path for updating existing Workers, and publish the guidance in README and bilingual quick-start pages."
66
+ },
67
+ {
68
+ "version": "2026.9.6",
69
+ "date": "2026-09-25",
70
+ "request": 6,
71
+ "notes": "Add a localized Posts link, accessible code-copy controls, responsive command wrapping, linked post contents, and refreshed bundled theme palettes."
72
+ },
73
+ {
74
+ "version": "2026.9.7",
75
+ "date": "2026-09-26",
76
+ "request": 7,
77
+ "notes": "Add consent-gated Cloudflare Web Analytics and configuration-generated privacy disclosures; optimize responsive homepage imagery and all-post archive ordering; improve Markdown excerpts and mobile consent controls; remove obsolete report cleanup code."
78
+ },
79
+ {
80
+ "version": "2026.9.8",
81
+ "date": "2026-09-26",
82
+ "request": 8,
83
+ "notes": "Replace consent version counters with proposed and effective dates; localize service data disclosures; audit the consent component independently from page layout; add Markdown video rendering; fix unique localized archive ordering and Markdown excerpts."
60
84
  }
61
85
  ]
62
86
  }
package/src/build.js CHANGED
@@ -4,14 +4,15 @@ import { dirname, relative, resolve, sep } from 'node:path';
4
4
  import { hostname } from 'node:os';
5
5
  import { mapLimit } from './concurrency.js';
6
6
  import { loadConfig } from './config.js';
7
- import { readDocuments } from './content.js';
7
+ import { readDocuments, sortPostsNewest } from './content.js';
8
+ import { plainText } from './markdown.js';
8
9
  import { createExtensions } from './plugin-api.js';
9
10
  import { collectAssets, rewriteAssetLinks, writeAssets } from './assets.js';
10
11
  import { loadLanguagePacks } from './i18n.js';
11
12
  import { generateBuiltinRoutes } from './generators.js';
12
13
 
13
14
  const RENDER_CACHE_VERSION = 3;
14
- const MARKDOWN_SECURITY_POLICY_VERSION = 3;
15
+ const MARKDOWN_SECURITY_POLICY_VERSION = 4;
15
16
 
16
17
  async function readCache(file) {
17
18
  try {
@@ -222,7 +223,7 @@ export async function buildSite(root = process.cwd(), options = {}) {
222
223
  if (typeof html !== 'string') throw new Error('Markdown renderers must return HTML strings for ' + document.file);
223
224
  if (renderer.cacheable) nextCache[signature] = html;
224
225
  const rendered = { ...document, html };
225
- rendered.description = rendered.description || rendered.markdown.replace(/\s+/g, ' ').slice(0, 180);
226
+ rendered.description = rendered.description || plainText(rendered.markdown).slice(0, 180);
226
227
  return rendered;
227
228
  });
228
229
 
@@ -230,7 +231,7 @@ export async function buildSite(root = process.cwd(), options = {}) {
230
231
  const visible = documents.filter((document) => !document.draft && publishedByLocalDate(document, now));
231
232
  let site = {
232
233
  config,
233
- posts: visible.filter((document) => document.kind === 'posts').sort((a, b) => b.date - a.date),
234
+ posts: sortPostsNewest(visible.filter((document) => document.kind === 'posts')),
234
235
  pages: visible.filter((document) => document.kind === 'pages')
235
236
  };
236
237
  site = await extensions.filter('content:rendered', site, { config });
package/src/cli.js CHANGED
@@ -292,17 +292,7 @@ async function clean(config) {
292
292
  try {
293
293
  const info = await lstat(toolsDirectory);
294
294
  if (info.isSymbolicLink() || !info.isDirectory()) throw new Error('Refusing to clean reports through an unsafe tools path: ' + toolsDirectory);
295
- for (const name of ['page-check.json', 'page-check.md', 'page-check.pdf', 'page-check-visual.html']) {
296
- await rm(resolve(toolsDirectory, name), { force: true });
297
- }
298
- const screenshots = resolve(toolsDirectory, 'page-check-screenshots');
299
- try {
300
- const screenshotsInfo = await lstat(screenshots);
301
- if (screenshotsInfo.isSymbolicLink() || !screenshotsInfo.isDirectory()) throw new Error('Refusing to clean an unsafe screenshot path: ' + screenshots);
302
- await rm(screenshots, { recursive: true, force: true });
303
- } catch (error) {
304
- if (error.code !== 'ENOENT') throw error;
305
- }
295
+ await rm(resolve(toolsDirectory, 'page-check.pdf'), { force: true });
306
296
  } catch (error) {
307
297
  if (error.code !== 'ENOENT') throw error;
308
298
  }
@@ -322,6 +312,11 @@ async function showReport(title, report, reportPath) {
322
312
  console.log('Report: ' + reportPath);
323
313
  }
324
314
 
315
+ function showStatus(title, report) {
316
+ console.log(title + ': ' + report.status);
317
+ for (const issue of report.issues) console.log('- ' + issue.severity + ': ' + issue.message);
318
+ }
319
+
325
320
  const [command, ...args] = process.argv.slice(2);
326
321
  try {
327
322
  const config = command === 'new' || command === 'clean' || command === 'check' || command === 'doctor' || command === 'iterate'
@@ -350,11 +345,11 @@ try {
350
345
  const pageReport = await checkPages(config);
351
346
  const compatibilityReport = await checkCompatibility(config);
352
347
  await showReport('Page check', pageReport, resolve(config.root, 'tools/page-check.pdf'));
353
- await showReport('Compatibility check', compatibilityReport, resolve(config.resolvedPaths.cache, 'reports/compatibility.md'));
348
+ showStatus('Compatibility check', compatibilityReport);
354
349
  if (pageReport.errors || compatibilityReport.issues.some((issue) => issue.severity === 'error')) process.exitCode = 1;
355
350
  } else if (command === 'doctor') {
356
351
  const report = await checkCompatibility(config);
357
- await showReport('Compatibility check', report, resolve(config.resolvedPaths.cache, 'reports/compatibility.md'));
352
+ showStatus('Compatibility check', report);
358
353
  if (report.issues.some((issue) => issue.severity === 'error')) process.exitCode = 1;
359
354
  } else if (command === 'iterate') {
360
355
  await buildSite(config.root);