diff --git a/.github/workflow-proposals/pages.yml b/.github/workflow-proposals/pages.yml new file mode 100644 index 0000000..9655fda --- /dev/null +++ b/.github/workflow-proposals/pages.yml @@ -0,0 +1,64 @@ +# INACTIVE PROPOSAL. Requires approval before replacing workflows/hugo.yaml. +# Required external settings: main PR protection + required checks + github-pages reviewers. +name: Reviewed Pages release + +on: + push: + branches: [main] + +permissions: + contents: read + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + review-gate: + runs-on: ubuntu-24.04 + permissions: + contents: read + pull-requests: read + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: '3.11' + - name: Require reviewed PR for this exact commit + env: + GITHUB_TOKEN: ${{ github.token }} + run: python scripts/review_gate.py + validate: + needs: review-gate + uses: ./.github/workflows/quality.yml + permissions: + contents: read + package: + needs: validate + runs-on: ubuntu-24.04 + permissions: + contents: read + actions: read + steps: + - name: Download tested artifact + env: + GH_TOKEN: ${{ github.token }} + run: gh run download "$GITHUB_RUN_ID" --repo "$GITHUB_REPOSITORY" --name verified-site --dir site + - uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4 + with: + path: site + deploy: + needs: package + runs-on: ubuntu-24.04 + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Publish tested artifact after environment approval + id: deployment + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 diff --git a/.github/workflows/hugo.yaml b/.github/workflows/hugo.yaml index e12d325..41a31d6 100644 --- a/.github/workflows/hugo.yaml +++ b/.github/workflows/hugo.yaml @@ -13,8 +13,6 @@ on: # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read - pages: write - id-token: write # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. concurrency: @@ -29,44 +27,67 @@ defaults: jobs: # Build job build: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 + permissions: + contents: read + pages: read + timeout-minutes: 20 env: - HUGO_VERSION: 0.141.0 # 这里可以指定版本0.141.0,或者删掉这行用默认latest HUGO_ENV: production steps: - - name: Install Hugo CLI - run: | - wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \ - && sudo dpkg -i ${{ runner.temp }}/hugo.deb - - name: Install Dart Sass - run: sudo snap install dart-sass - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: submodules: recursive fetch-depth: 0 + persist-credentials: false + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: '24' + cache: npm + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: '3.11' + - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: .cache/hugo + key: pages-hugo-${{ runner.os }}-${{ hashFiles('toolchain.json') }}-${{ github.sha }} + restore-keys: pages-hugo-${{ runner.os }}-${{ hashFiles('toolchain.json') }}- - name: Setup Pages id: pages - uses: actions/configure-pages@v5 + uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5 - name: Install Node.js dependencies - run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true" - - name: Build with Hugo + run: npm ci --ignore-scripts + - name: Install verified Hugo Extended + run: python scripts/install_hugo.py + - name: Install browser + run: npm run browsers -- --with-deps + - name: Validate, build and test env: - # For maximum backward compatibility with Hugo modules HUGO_ENVIRONMENT: production - HUGO_ENV: production - run: | - hugo \ - --gc \ - --minify \ - --baseURL "${{ steps.pages.outputs.base_url }}/" + HUGO_BASEURL: "${{ steps.pages.outputs.base_url }}/" + run: npm run verify + - name: Preserve validation reports + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: pages-validation-reports + path: | + reports/ + playwright-report/ + test-results/ + retention-days: 7 + if-no-files-found: ignore - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4 with: - path: ./public + path: ./.build/site # Deployment job deploy: + permissions: + pages: write + id-token: write environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} @@ -75,4 +96,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml new file mode 100644 index 0000000..c80f30d --- /dev/null +++ b/.github/workflows/quality.yml @@ -0,0 +1,84 @@ +name: Blog quality + +on: + pull_request: + push: + branches-ignore: [main] + workflow_dispatch: + workflow_call: + +permissions: + contents: read + +concurrency: + group: quality-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + verify: + name: Verify content and site + runs-on: ubuntu-24.04 + timeout-minutes: 20 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + submodules: recursive + persist-credentials: false + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: '24' + cache: npm + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: '3.11' + - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: .cache/hugo + key: hugo-${{ runner.os }}-${{ hashFiles('toolchain.json') }}-${{ github.sha }} + restore-keys: hugo-${{ runner.os }}-${{ hashFiles('toolchain.json') }}- + - name: Install locked dependencies + run: npm ci --ignore-scripts + - name: Install verified Hugo and audited math assets + run: | + python scripts/install_hugo.py + python scripts/sync_vendor.py + - name: Content and Markdown checks + run: | + python scripts/site.py check + npm run lint + - name: Unit tests + run: | + python -m unittest discover -s tests -v + npm run test:workflows + - name: Dependency and source security + run: | + npm audit --audit-level=low + python scripts/security_check.py + - name: Build and check every local reference + run: | + python scripts/site.py build + python scripts/check_site.py + - name: Install browser + run: npm run browsers -- --with-deps + - name: Browser and accessibility tests + run: npm run test:e2e + - name: Preserve diagnostic reports + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: quality-reports + path: | + reports/ + playwright-report/ + test-results/ + retention-days: 7 + if-no-files-found: ignore + - name: Preserve tested site + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: verified-site + path: .build/site/ + retention-days: 3 + - name: Summarize failures + if: failure() + run: echo 'Validation failed. Open the first failing step and download quality-reports. Production deployment was not started.' >> "$GITHUB_STEP_SUMMARY" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fea3197 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +/.tools/ +/.build/ +/.cache/ +/assets/lib/katex/ +/assets/lib/mermaid/ +/resources/_gen/ +/node_modules/ +/test-results/ +/playwright-report/ +/reports/*.json +/reports/*.png +/reports/*.log +/.hugo_build.lock +__pycache__/ +*.pyc +.env +.env.* +!.env.example diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc new file mode 100644 index 0000000..53a9e21 --- /dev/null +++ b/.markdownlint-cli2.jsonc @@ -0,0 +1,14 @@ +{ + "globs": ["content/**/*.md", "*.md"], + "config": { + "default": true, + "MD013": false, + "MD024": { "siblings_only": true }, + "MD025": false, + "MD029": false, + "MD033": false, + "MD041": false, + "MD046": { "style": "fenced" }, + "MD060": false + } +} diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..b583fad --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,50 @@ +# 架构与现状审计 + +当前界面(2026-10-30):恢复 Blowfish 原生 background 首页,主要发布文章并使用 Tags。分类、项目、友链、写作约定及工作台模板已归档到 archive/,不生成公开页面。下文保留首次重构的审计与决策记录;开发与测试工具继续使用。 + +审计日期:2026-10-29。工作区为 `D:\hugo`,实际 Git/Hugo 项目根为 `myblog/`;所有开发命令从该目录执行。初始工作区干净,基线提交 `efbb03e`,远程为 `git@github.com:Nizeross/nizeross.github.io.git`,默认分支 `main`。本次仅在本地 `refactor/blog-system` 开发。 + +## 决策:保留 Hugo + Blowfish + +内容使用 Markdown + TOML Frontmatter;Hugo 在构建时生成 HTML、RSS、搜索 JSON。浏览器只加载主题现有库与少量原生 JavaScript,不引入 React、数据库或在线写作 API。Blowfish 子模块固定在 `3a9016a6d93f6d498ab5fc3b0aa7d51869962f25`,所有覆盖位于站点自己的 `layouts/` 和 `assets/`。 + +换成其他框架对三篇既有文章没有实质收益,却要重做 shortcode、图片处理、搜索和链接兼容。本方案保留 `/posts/<原目录>/`,用首页覆盖和独立 Projects 模板提供辨识度;构建和审核工具独立于主题,未来换主题仍能复用。 + +## 审计事实 + +| 项目 | 发现与处理策略 | +| --- | --- | +| 内容 | 三个 leaf bundle,含本地封面。保留文章正文与路径;首篇示例有未闭合代码围栏,修复语法并记录。 | +| 配置 | `config/_default/` 分文件;语言文件标为 English/en 但内容为中文,需修正;全站基础色遵循原生色板,白天纯白、夜间 #1E293B。 | +| 主题 | submodule,无本地改动;要求 Hugo Extended 0.141.0–0.154.5。工作区二进制为非 Extended 0.155.0,线上 workflow 用 Extended 0.141.0,需统一到受支持的 Extended 0.154.5。 | +| 输出 | 仓库根与 `public/` 都有历史生成文件。本次不删除;新本地构建输出到忽略的 `.build/site/`,避免修改这些历史文件。未来在确认后才从 Git 移除旧输出。 | +| 部署 | `.github/workflows/hugo.yaml` push main 后直接部署;整个 workflow 有 Pages 写权限、未做内容检查、Sass 未锁版本、npm 失败被忽略。保留现有 workflow 原样,提供待批准的替换方案。 | +| 域名 | 根 `CNAME` 为 `kirisame.me`;baseURL 仍为用户名地址。不能自行决定哪一个是权威域名,保持配置不变,批准迁移时再核对 Pages base URL。 | +| 远程 | 连接可见的 Nizeross 仓库只有 `nizeross.github.io`,未发现另一个可见用户名站点;这不是对所有私有仓库的证明。GitHub 最近一次部署 efbb03e 成功;读取 rulesets 返回空列表,不能证明已有审核保护。 | +| 安全 | 原 footer 请求第三方访问统计;有注释掉的 Firebase 配置。移除页面统计依赖,清理无效配置;自托管资源,不将在线 AI 设为必需。个人邮箱本来公开,保留用户身份资料。 | + +## 数据与控制流 + +```text +Markdown + 本地图片 → 内容/格式检查 → Hugo → HTML/RSS/JSON + ↓ + 链接、集成与浏览器测试 + ↓ + PR 审核 → main → Pages artifact +``` + +站点代码:`layouts/`(首页、项目列表、小型扩展)、`assets/css/`(主题色板与排版)、`assets/js/`(阅读进度)。`content/` 是唯一文章来源;archetypes 定义五种文章与项目模板。`scripts/` 提供写作、验证、建议工具。Node 依赖只用于开发检查和浏览器测试;Python 3.11+ 使用标准库解析 TOML、HTML 与本地链接。 + +## 发布边界 + +新增 CI 只有 contents:read,不部署。生产 workflow 的建议版本保存在 `.github/workflow-proposals/`,不会被 GitHub 执行。最终需要人工批准替换旧工作流,并配置 main 必须 PR、必需检查、禁止绕过,以及 github-pages 环境审核。代码无法代替服务器端分支保护;未配置前不能声称审核已强制生效。本次不修改默认分支、远程、域名或 Pages 设置,也不推送。 + +参考:[Blowfish 自定义机制](https://blowfish.page/docs/advanced-customisation/)、[GitHub Pages workflows](https://docs.github.com/en/pages/getting-started-with-github-pages/using-custom-workflows-with-github-pages)、[Hugo 安全配置](https://gohugo.io/configuration/security/)。 + +## 本地重构落地情况 + +`home/custom.html` 提供工作台首页,Projects 使用独立列表模板,其余文章保留主题的主体模板。目录、图标、搜索与移动菜单通过小范围站点覆盖修复可访问性问题。`contentType` 用作文章类型字段,避免占用 Hugo 保留的 kind。五种 archetypes 同时支持原生 Hugo 与本地助手。 + +构建同时隔离 publishDir 与 resourceDir,分别为 `.build/site` 和 `.build/resources`;历史 public/ 与 resources/ 文件不作为新源码。构建前同步锁定的数学资源,并从经 audit 的依赖图重建 Mermaid,随后 Hugo 负责指纹与按需发布。esbuild 只在这一步打包已有图表库,不形成应用框架依赖。 + +只读 HTTP 核对确认用户名地址返回 200 并跳转 `https://kirisame.me/`,未发现独立占用的第二站点。仍需站点所有者确认后才统一 canonical/baseURL 与正式发布策略。当前方案、实现和验证均保存在本地分支,线上仍为原有版本。 diff --git a/DELIVERY_REPORT.md b/DELIVERY_REPORT.md new file mode 100644 index 0000000..4b655ce --- /dev/null +++ b/DELIVERY_REPORT.md @@ -0,0 +1,56 @@ +# 本地重构交付报告 + +2026-10-30 更新:作者选择回归文章博客。已恢复原背景图首页,移除分类、项目展示、友情链接与写作约定,保留 Tags、文章阅读与自动检查。下面的项目展示交付清单为上一轮历史,已撤下内容可从 archive/removed-site-sections/2026-10-30/ 找回。 + +2026-10-29。本地分支:`refactor/blog-system`;基线:`efbb03e`。本地开发六阶段已完成,生产发布审批尚未执行。 + +## 可运行的成果 + +网站保留 Hugo 与固定版本 Blowfish,新增工作台风首页,沿用原头像、站名和明暗色板。首页包含个人介绍、文章/项目入口、五种内容类型和最近更新。Projects 页面展示真实项目、技术栈、仓库、运行截图、过程、收获及后续计划;关于、友情链接和写作约定页面均可访问。 + +文章具备代码高亮/复制、KaTeX 公式、可折叠目录、标题锚点、图片放大、阅读进度、标签/分类、相关文章、RSS 与基础 SEO。搜索使用本地 JSON 与 Fuse,无需网络 API;输入会等待索引准备完成,结果使用安全文本节点构造。 + +白天基础背景为纯白,夜间为 #1E293B;顶部栏与文章背景仍使用 Blowfish 自带的透明度和模糊。中文使用系统字体回退,没有引入在线字体或大量动画。移动端菜单支持键盘开启、Escape 和焦点循环。 + +## 写作与工程工具 + +- 五类 TOML Frontmatter 模板:技术学习、项目开发、读书笔记、思想随笔、生活记录。 +- 写作助手:生成 slug、创建草稿、拒绝覆盖、检查内容/图片、预览、生产构建、展示提交草稿。 +- 离线建议:摘要摘取、标签与相关文章候选,输出报告供人工审阅;不调用生成式 API、不修改正文、不执行代码示例。 +- 一键 `npm run verify`:执行内容、格式、37 项 Python 测试、3 项 workflow 测试、安全、依赖、构建、内部链接和浏览器验证。失败停在对应阶段,日志保存到 reports/verify.log。 + +## 实际验证结果 + +| 项目 | 结果 | +| --- | --- | +| Python 单元测试 | 37 项通过 | +| Workflow 配置测试 | 3 项通过 | +| 浏览器端到端测试 | 桌面/移动端共 8 组通过 | +| 内容、格式、安全、构建 | 通过 | +| 生成页与链接 | 40 个 HTML、1213 个内部引用通过 | +| 旧文章链接 | 3 条全部保留 | +| 基础无障碍 | 受测首页、文章、移动菜单未发现 axe A/AA 违规 | +| 本站依赖审计 | 0 个已知漏洞 | +| 上游主题审计 | 29 个告警包节点,单独记录并处理当前使用的公式/图表运行库 | +| 外链 | 7/8 可访问;知乎 403 保留待人工验证 | + +完整证据和复现范围见 [验证记录](reports/validation.md) 与 [安全说明](SECURITY.md)。截图位于 reports/home-desktop-light.png、home-desktop-dark.png、home-mobile-light.png、home-mobile-dark.png 等文件;HTML 报告为 playwright-report/index.html。本地首页测得约 286 KB 的资源(不含 HTML、未压缩传输),未加载公式/图表库。 + +## 兼容与保留 + +三个旧文章目录、旧 URL、既有正文、原始图片、主题子模块及历史生成目录均保留。正文只修复未闭合围栏与格式,补充分类/摘要等元数据,没有替作者重写观点。新构建写入 .build,不更新历史根目录 HTML 或 public。 + +远程 origin、默认分支、CNAME、baseURL、原生产 workflow 和 Pages 设置均未修改,也没有推送、创建远程 PR、合并或部署。本地变更按阶段提交,记录见 MIGRATION_PLAN。 + +## 尚需批准的上线事项 + +1. 审阅本地界面与内容。真实项目示例的截图来自本次运行。 +2. 确认 `kirisame.me` 为正式权威域名;只读检查已确认用户名地址跳转到该域名,但本次不自行更改 baseURL。 +3. 批准替换生产 workflow,并配置 main 分支保护、可信审核者和 github-pages 环境批准。新发布提案位于 `.github/workflow-proposals/pages.yml`,当前不会执行。 +4. 明确授权后才能推送重构分支并创建 PR。审核后的部署还需在 GitHub 上验证;不能把本地测试成功当成线上已经发布。 + +现有生产 workflow 仍会在 main 推送时直接发布,因此正式替换前不要绕过 PR 把本次变更直接推到 main。新方案要求另一名真人审核者批准最终提交;单人维护如何安排审核,需要在启用前明确。 + +## 下一次打开项目 + +从 myblog/ 运行 `python scripts/site.py preview` 即可查看本地草稿预览;首次换电脑按 README 安装锁定依赖。先阅读 ARCHITECTURE 了解选择,再按 DEVELOPMENT 写文章,正式上线按 DEPLOYMENT 执行。LEARNING_NOTES 解释其中的 Git、HTTP、静态站点与 CI/CD 原理。 diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..1a39693 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,40 @@ +# 自动化发布与审核边界 + +2026-10-30 已获得将重构推送并合并 main 的授权。生产 hugo.yaml 已补齐锁定 Hugo/Node/Python、资源生成和完整验证,发布经过测试的 .build/site;保留原 main 触发、既有域名与 Pages 设置。另一人审核的 proposal 仍不启用。下文“保留旧 workflow 原样”的描述属于本次授权上线之前的历史记录。 + +## 当前与拟议流程 + +线上现有 `.github/workflows/hugo.yaml` 未改动,仍是 push main 后构建发布。新增 `quality.yml` 只验证 PR、非 main 分支和手动运行;没有 Pages 写权限。它也可被审核后的发布流程复用。生产替换配置保存在 `.github/workflow-proposals/pages.yml`,GitHub 不会执行这个目录的文件。 + +因此,现阶段不能承诺远程 main 已被保护;连接读取的 rulesets 列表为空,经典分支保护与 Pages 环境设置未获得完整验证。本地代码已经提供审核门槛,但启用需要你批准并配置仓库。 + +## 为什么发布产物而不是再构建 + +拟议流程:PR 检查 → 人工批准最终文章 → 合并 main → 校验该提交关联的已合并 PR 与有效审核 → 完整质量检查 → 上传已经测试的 site artifact → github-pages 环境批准 → 发布。构建与测试失败不会触发部署,上一份线上成功版本继续存在。 + +审核门槛要求其他真人审核者批准最终 head commit,拒绝直接 push、过期审核、撤销审核和仍存在的请求修改。独立 gate 不替代分支保护,二者共同防止遗漏。单人维护时需要先选择一名可信审核者;作者不能在 GitHub 上批准自己的 PR。若要改为环境审批模式,需要另外讨论审核策略,不会自动降低门槛。 + +部署只获得 `pages:write` 与 `id-token:write`;验证仅有 `contents:read`。artifact 下载另需 `actions:read`。OIDC 让 GitHub 给部署 job 发短时凭据,不需要把长期个人 token 写进仓库。PR 流程不使用 `pull_request_target`,也不读取生产密钥。 + +## 缓存、版本和日志 + +Hugo Extended 版本、下载校验和、主题提交、npm 包和 Actions 提交 SHA 均有记录。Node 限定 24 大版本并使用 lockfile;Python 3.11 使用标准库。Hugo 缓存 key 包含工具链与提交,恢复只影响构建加速,不替代最终构建与测试。npm 缓存由 setup-node 按 lockfile 管理。 + +失败日志直接保留在对应 job step;Playwright 截图、trace、HTML/JSON 报告在失败时仍上传为 quality-reports,保留 7 天。网络外链检查不设为主检查,因为目标站反爬和临时故障会产生误报。密钥不出现在命令参数或脚本输出。 + +## 获得批准后的上线清单 + +1. 审阅 DELIVERY_REPORT 与截图,确认新首页、文章和项目内容。 +2. 在 Pages 页面核对当前域名:仓库 CNAME 为 `kirisame.me`,原 baseURL 为 `https://nizeross.github.io`。确认唯一权威 URL 后,才修改 baseURL 或静态 CNAME 策略;目前两个都保留。 +3. 将 proposal 作为 PR 中的新 `hugo.yaml`,避免两个发布 workflow 并行运行。首次切换 PR 可以先由旧流程发布,或者在维护窗口人工启用门槛;提前确认,不直接操作生产。 +4. main 配置必须 PR、至少一名批准、撤销过期批准、必须通过 `Verify content and site`,禁止 force push 和审核绕过。为 `.github/` 变化指定可信维护者审核。 +5. github-pages 环境设置 reviewer 和 main 分支限制。不要自动批准环境。确认 GitHub 账户与套餐支持所需保护。 +6. 明确授权后推送本地分支、打开 PR;通过审阅后合并。检查 Actions、HTTPS、canonical、RSS、站内搜索和三个旧 URL。 + +本次不更改默认分支、Pages 发布源、域名或远程设置,不推送,也不部署。 + +## 回滚 + +通过回退变更的 PR 恢复已知可用版本并重新验证;不要 force push 回写 main。如果新发布失败,先保留当前线上版本,读第一个失败步骤,再修复并重跑。构建日志中的失败和浏览器报告中的断言失败需要分别排查。 + +参考:[GitHub Pages 自定义工作流](https://docs.github.com/en/pages/getting-started-with-github-pages/using-custom-workflows-with-github-pages)、[保护分支](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches)。 diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 0000000..58a8963 --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,50 @@ +# 本地开发与写作 + +## 先明确内容来源 + +`content/` 中的 Markdown 才是文章源文件;`.build/site/` 是检查用产物。历史 `public/` 和根目录 HTML 暂时保留,避免迁移时误删,但后续开发不再手动更新这些文件。模板在 `layouts/`,主题样式扩展在 `assets/css/`,Hugo 会优先读取站点覆盖,再读取主题。 + +## 开发循环 + +先按 README 安装锁定工具链和同步数学资源,再运行 `python scripts/site.py preview`。保存 Markdown 会触发 Hugo 重建;Ctrl+C 停止预览。预览包含草稿,生产构建不包含。正文使用 UTF-8,换行和编辑器显示异常时先检查编码,不要盲目转换文件。 + +每次改动围绕一个问题:创建分支、修改、执行检查、查看差异、提交。建议 `content/<主题>` 作为内容分支名;本次重构已经在独立本地分支,不需要改变 main。 + +## Frontmatter 约定 + +使用 `+++` 包围 TOML,日期必须含时区,draft 为真正布尔值。`contentType` 说明写作模板类型;不要用 `kind`,它是 Hugo 的保留字段。summary 用一句话交代问题与结论,tags 用于文章归档与关联,网站不再使用 Categories。 + +```toml +title = "理解虚拟内存" +date = 2026-10-29T12:00:00+08:00 +draft = true +contentType = "technical" +tags = ["操作系统", "内存"] +summary = "从地址映射与缺页异常理解虚拟内存。" +``` + +五种模板的内容骨架各不相同:技术学习强调环境与验证;项目开发强调取舍与测试;读书笔记区分引用、转述和理解;思想随笔保留开放问题;生活记录保留场景和观察。标签不要为了凑数量添加,关联推荐依赖实际语义。 + +也可以直接使用 Hugo 原生命令,例如 `.tools/hugo new content --kind technical posts/example/index.md`(Windows 使用 `.tools/hugo.exe`)。原生模板会以目录名作为初始标题,助手则接收完整中文标题。未指定类型时的默认 archetype 也包含必要字段。 + +## 图片与数学 + +图片和 `index.md` 放在同一个 leaf bundle,使用 `![描述](image.png)`。描述应说明图片传达的信息。不要把本地磁盘路径写进正文,也不要引用工作区外文件。既有文章仍保留原目录,从而保留外链。 + +数学公式在文章开头加入 `{{}}`,然后使用 `\( ... \)` 或 `$$ ... $$`。资源来自本地锁定的 KaTeX,不依赖 CDN。代码围栏必须标注语言;Python 示例会做语法解析,不执行文章代码,因此不会误运行读者提供的程序。 + +不再维护项目展示页。项目实践可以直接写成普通文章,与其他笔记一样通过 Tags 组织。 + +## 检查分层 + +内容检查定位到源文件,识别 Frontmatter 类型、slug、图片与围栏错误。Markdown lint 统一排版。Hugo 验证实际模板和短代码;生成页检查再验证 HTML 中的每个内部链接、资源、锚点、重复 ID、搜索索引及旧 URL。两层检查互补:源文件看起来正确,不代表模板生成的链接正确。 + +Playwright 在桌面和移动视口验证搜索、主题、目录、代码、公式、放大图片、阅读进度和主要页面,并用 axe 检查 WCAG A/AA 规则。自动检查不等于完整无障碍认证;写作时仍需审阅图片描述与内容层次。 + +公式等测试样例位于 `tests/fixtures/`。浏览器检查会单独生成 `.build/browser-site/`,这些测试页面不进入正式发布目录 `.build/site/`。 + +## 依赖更新 + +依赖仅安装锁文件:`npm ci --ignore-scripts`。更新时明确修改版本并重建锁文件,运行 audit、内容检查、构建与浏览器测试。`overrides` 用于修复 lint 和图表工具的间接依赖漏洞,不能在未来更新时不加检查地删除。KaTeX 在构建时复制,Mermaid 根据锁定的依赖重新打包,更新后运行 sync_vendor 或 build;主题子模块仍保持原样。图表只在对应文章加载,不进入首页。 + +版本较新的 Hugo 不一定兼容当前主题。升级需同时检查主题的 hugoVersion 范围与实际构建结果。不要通过禁用所有检查来消除兼容警告。 diff --git a/LEARNING_NOTES.md b/LEARNING_NOTES.md new file mode 100644 index 0000000..8f18dd5 --- /dev/null +++ b/LEARNING_NOTES.md @@ -0,0 +1,41 @@ +# 从这个博客理解软件工程 + +## 静态站点把复杂性放在哪里 + +Markdown 是源文件,Hugo 是编译器,HTML、CSS、JavaScript 和图片是产物。访问者不触发服务器端模板渲染,所以 Pages 只需分发文件。搜索索引也在构建时生成,浏览器用 Fuse 查询本地 JSON。代价是更新内容后必须重新构建;收益是运行成本低、攻击面小、容易缓存。 + +## Git 管的是历史与协作 + +提交是某个时刻的快照,分支是指向提交的名字。本地 commit 不代表 push,push 也不等于上线;是否上线取决于远程 workflow 和 Pages 设置。PR 把变更组织成可审核的差异。主题 submodule 记录的是另一个仓库的精确提交,不是“随时跟随 main”;更新它是一次需要验证的依赖升级。 + +本次把工作放在 refactor 分支,避免把未完成迁移混入 main。保留原文目录是兼容性设计:一个 URL 被分享后就成为外部接口,改目录相当于改变 API,需要 aliases 或重定向。 + +## HTTP 与链接 + +浏览器先请求 HTML,再按里面的 URL 请求样式、脚本和图片。200 说明请求得到响应,不代表页面交互正确;404 常见于路径、大小写或漏上传资源。Linux 文件名区分大小写,Windows 常不区分,因此 CI 是必要的跨平台验证。 + +绝对 URL 用于 canonical 和 RSS;根相对 URL 让站内导航在本地与线上使用同一站点路径。锚点是 URL 的 fragment,不向服务器发送,浏览器用它定位 DOM id;重复 id 会使目录和辅助技术的行为不确定。HTML 检查因此同时查文件与 fragment。 + +## CI/CD 的职责分工 + +CI 让每次改动都经过同一套检查,减少“我电脑上能跑”的偶然性。CD 交付那一份已经通过检查的产物,避免测试一次、上线又重建出另一份内容。artifact 是两个过程之间的交付物,缓存只是提速,两者不能混淆。 + +失败时不发布是门槛设计:检查 job 是发布 job 的 needs。人工批准和自动检查回答不同的问题:检查发现格式、链接和交互错误;人判断内容是否正确、适当、值得发布。草稿字段只是内容开关,不能承担权限控制。 + +## CSS 色板与主题 + +Blowfish 用 RGB 变量定义 neutral、primary、secondary。具体组件再决定透明度、模糊和颜色组合;自定义色板改变基础配色,而全局强制覆盖会破坏组件的组合能力。站点保留白天纯白、夜间 #1E293B 的基础色,并保留框架的透明效果。正文采用系统中文字体回退,减少字体下载和布局跳动。 + +首页布局用 CSS Grid,移动端重新排列列数;代码块横向滚动,避免长代码撑破整页。prefers-reduced-motion 尊重减少动态效果的系统偏好。语义化标题、可见焦点、搜索对话框与图片描述让键盘和读屏用户也能使用网站。 + +## 测试如何提供不同证据 + +单元测试检查小函数,例如 slug 转换和审核状态判断;集成检查把 Markdown、主题和图片一起构建后检查链接;浏览器测试让实际 Chromium 执行搜索、公式和主题切换。仅断言某个字符串出现在源码里无法证明浏览器会正确使用它,所以关键交互必须真实运行。 + +自动无障碍工具可以发现无名称控件与对比度问题,但不能判断图片描述是否准确。网络外链返回 403 也不能直接断定链接已失效,可能是目标站限制机器人。测试报告要说明证据范围,而不是把“命令执行过”当成“功能正确”。 + +## 安全与可选辅助 + +锁定版本提高可复现性,校验和验证下载内容,audit 根据已知公告检查包;这些都不能证明没有未知漏洞。前端主题里捆绑的第三方脚本也需要单独盘点,不能只看本站 package-lock。最小权限意味着 PR 检查没有发布权限,失败或恶意内容无法借检查 job 获得生产 token。 + +离线辅助脚本采用摘要摘取、词语匹配和相似度排序,没有调用生成式模型。它把候选输出为报告,让作者自己决定,不替代阅读原文或核实论据。Python 示例只用 ast.parse 检查语法,不执行文章中的任意代码。 diff --git a/MIGRATION_PLAN.md b/MIGRATION_PLAN.md new file mode 100644 index 0000000..868c609 --- /dev/null +++ b/MIGRATION_PLAN.md @@ -0,0 +1,40 @@ +# 分阶段迁移计划 + +2026-10-30 后续调整:按作者要求恢复原背景图首页,移除 Categories、Projects、友链与写作约定,聚焦文章发布;旧页面归档而不参与构建。以下阶段记录为上一轮重构历史。 + +所有阶段均在本地分支完成;构建写入 `.build/site/`,保留原始内容与历史输出。每阶段以本地提交、执行记录和明确验收项交付。 + +| 阶段 | 本地交付与验收 | 状态 | +| --- | --- | --- | +| 1 审计与方案 | ARCHITECTURE.md、路径与域名清单、技术决策 | 已完成 | +| 2 可运行基线 | 固定兼容 Hugo Extended、独立输出、现有链接基线、写作命令 | 本地已完成 | +| 3 界面与内容 | 桌面工作台风首页、分类/项目/关于/友链、中文排版、数学/搜索/阅读进度 | 本地已完成 | +| 4 自动化 | 内容检查、Markdown lint、构建、测试、只检查 CI、待批准发布方案 | 本地已完成;生产替换待批准 | +| 5 质量 | 单元/集成/链接/浏览器/无障碍/依赖检查,截图与问题记录 | 本地已完成;上游告警已记录 | +| 6 文档 | README、开发/部署/学习笔记、最终报告、操作边界 | 本地已完成 | + +## 兼容与回滚 + +不移动已有三篇文章目录;保存基线 URL 并自动验证。文章仅修复已知不闭合围栏及补充组织元数据,不重写正文。旧模板和生成文件不删除。所有自定义不进入主题 submodule。可通过切回原 main 在本地恢复原站;正式上线后通过回退变更 PR 并重新部署恢复,避免重写历史。 + +## 发布前需确认 + +1. 审阅本地界面、文章变更和测试报告。 +2. 确认正式权威域名;当前 CNAME=kirisame.me 与 baseURL 不一致。 +3. 同意用建议 workflow 替换旧部署流程,并配置分支/环境审核。 +4. 明确授权后才推送此分支与创建 PR;审核合并后由受保护 workflow 发布。 + +## 不纳入首版 + +在线编辑后台、账户登录、数据库、评论系统和在线 AI 自动改文。可选辅助仅产生建议报告,正文必须作者编辑与审核。外链在线检查独立运行,避免网络波动阻塞日常离线写作。 + +## 阶段交付记录 + +- 阶段 1:`8fdf911`,完成只读审计与两份计划文档。 +- 阶段 2:`3eb2da2`,兼容 Hugo Extended 校验安装与独立构建目录。 +- 阶段 3:`34643e7`,首页、项目、文章排版、五类模板与搜索实现。 +- 阶段 4:`99c8752`,验证 CI、审核发布提案与内容/浏览器检查工具。 +- 阶段 5:`d50c51a`,实测问题修复、供应商库审计和截图;后续补充的模板兼容与统一验证入口随文档收尾提交。 +- 阶段 6:文档与最终验证报告见当前本地分支最后一条提交。所有工作均未推送。 + +本地验证证据见 [reports/validation.md](reports/validation.md),上线边界见 [DEPLOYMENT.md](DEPLOYMENT.md)。 diff --git a/README.md b/README.md new file mode 100644 index 0000000..19f2b39 --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ +# NIZEROSS 的个人网站 + +2026-10-30 调整:网站回归文章发布,首页恢复原来的 Blowfish 背景图、头像、个人简介和最近文章布局。使用 Tags 组织内容;分类、Projects、友情链接及写作约定不再发布。相关页面与旧首页模板保存在 `archive/removed-site-sections/2026-10-30/`。 + +一个基于 Hugo + Blowfish 的个人数字花园,记录计算机学习、项目实践、阅读与生活。站点无需数据库或在线写作 API;Node 只用于开发检查,浏览器不运行应用框架。 + +## 从哪里开始 + +实际 Git 根目录是 `myblog/`;外层 `D:\hugo` 还存放历史 Hugo 二进制。Windows、Linux 均使用 Python 3.11+ 与 Node 24。首次克隆需要初始化主题子模块。 + +```powershell +git clone --recurse-submodules git@github.com:Nizeross/nizeross.github.io.git +cd nizeross.github.io +npm ci --ignore-scripts +python scripts/install_hugo.py +python scripts/sync_vendor.py +python scripts/site.py preview +``` + +预览地址为 `http://127.0.0.1:1313`,包含草稿。安装器固定 Hugo Extended 0.154.5 并校验 SHA-256;不要使用外层旧二进制。Linux 安装流程相同;当前安装器支持 Windows/Linux AMD64,其他平台需手动安装相同版本 Extended 到 `.tools/`。 + +## 日常写作 + +```powershell +python scripts/site.py new technical "理解虚拟内存" --slug virtual-memory +python scripts/site.py check +npm run lint +python scripts/site.py build +python scripts/check_site.py +``` + +五种类型为 `technical`、`project`、`reading`、`essay`、`life`。新文章在 `content/posts//index.md`,默认草稿;图片和文章放在同一目录。中文标题可自动生成中文 slug;想要便于输入的 URL,可以显式传英文 `--slug`。不要随意改已有文章目录名。 + +`python scripts/site.py commit-draft` 只展示差异摘要和提交信息建议,不自动暂存、提交或推送。`python scripts/suggest.py <文章路径>` 离线提供摘要片段、标签与相关文章候选;它不是生成式 AI,也不修改正文。外链报告用 `python scripts/check_external.py` 单独生成。 + +## 验证与发布 + +```powershell +python -m unittest discover -s tests -v +npm run test:workflows +python scripts/security_check.py +npm run browsers +npm run test:e2e +npm run audit:dependencies +``` + +本地发布目录是 `.build/site/`;Playwright 会自动启动并关闭测试服务器。截图和浏览器结果保存在 `reports/`,HTML 报告在 `playwright-report/`。开发前先构建再执行浏览器测试。 + +浏览器安装完成后,可以用 `npm run verify` 一次执行全部本地检查。失败会停在对应阶段,并将完整输出保存为 `reports/verify.log`。 + +正式部署使用 `hugo.yaml`:合并到 main 后安装锁定工具链、执行完整验证,再将 `.build/site` 发布到既有 Pages 站点;检查失败不会进入部署。严格的另一人审核发布方案仍位于 `.github/workflow-proposals/pages.yml`,未启用。日常文章使用独立分支与 PR,检查通过并人工审阅后再合并。 + +## 文档导航 + +- [架构与审计](ARCHITECTURE.md):为什么保留 Hugo、目录与数据流。 +- [迁移计划](MIGRATION_PLAN.md):阶段交付、兼容性和回滚。 +- [开发流程](DEVELOPMENT.md):写作、项目页和检查规则。 +- [发布原理](DEPLOYMENT.md):审核门槛、权限和上线步骤。 +- [学习笔记](LEARNING_NOTES.md):Git、HTTP、静态站点与测试背后的概念。 +- [交付报告](DELIVERY_REPORT.md):实际运行结果与剩余事项。 +- [安全说明](SECURITY.md):本站依赖与上游主题审计的不同范围。 + +原有文章、域名文件与历史生成目录均保留。不要编辑 `themes/blowfish/`:站点覆盖文件才是定制入口。 diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..1aeb1f6 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,31 @@ +# 安全审查与维护范围 + +## 本次执行的检查 + +2026-10-29 对本站锁定的 npm 依赖执行了完整 audit,并扫描作者维护的配置、模板和内容中常见凭据模式。检查 CI 最小权限、Actions 提交 SHA 与主题提交固定情况。扫描结果不等于完整渗透测试,尤其不能发现所有未知漏洞或历史 Git 对象中的敏感信息。 + +原来的第三方访问统计脚本已从 footer 与阅读量覆盖模板移除。注释中的旧 Firebase 集成配置已清理;Firebase 公共配置本身通常不是服务器密钥,但未启用的配置不应给维护者造成错觉。网站仍保留作者主动公开的联系信息。 + +## 主题上游审计需要单独解读 + +对固定 Blowfish 子模块的 package-lock 执行 `npm audit --prefix themes/blowfish --json`,发现 29 个有告警的包节点:1 low、10 moderate、17 high、1 critical。包含 Puppeteer、FTP、翻译工具、CSS 工具及图表相关依赖。本项目不会安装或运行主题的 Node 工具链;构建使用主题已编译 CSS。不能因此把 29 个告警全部归为网站运行时漏洞,也不能声称上游依赖没有问题。 + +涉及当前/潜在阅读运行时的 KaTeX 与 Mermaid 已通过站点资源覆盖处理:KaTeX 固定 0.19.1;Mermaid 固定 11.17.2,并从锁文件依赖重新打包,使用 DOMPurify 3.4.17 与 lodash-es 4.18.1。直接复制 npm 包里的预构建 bundle 可能仍携带旧版内嵌依赖,因此 Mermaid 使用 esbuild 根据审核后的依赖图打包。主题文件与子模块提交不变。 + +首页不会加载公式或图表脚本;只有相应文章按需加载。本项目不启用主题的图表、视频、图库等其他可选 shortcodes。后续启用新 shortcode 之前,应先审核它带入的前端脚本;本次没有对每个未使用的供应商 bundle 作“安全”背书。 + +报告可重生成: + +```powershell +node scripts/audit_dependencies.cjs +``` + +本站与主题分别输出到 `reports/dependency-audit.json` 和 `reports/theme-audit.json`。脚本对本站漏洞或审计服务失败返回非零;上游主题结果保留为待跟踪信息。CI 的强制 audit 针对实际采用的站点工具链。 + +## 发布保护 + +PR 检查不读取生产密钥,不使用 `pull_request_target`。待批准的发布方案检查最终提交的人工审核,部署 job 才有 Pages 写权限与 OIDC 权限。分支保护与环境 reviewer 必须在 GitHub 设置中启用;当前现有生产 workflow 未改动,所以这项保护尚未在线生效。 + +## 后续维护 + +定期在独立分支更新依赖与主题,复核兼容范围和告警,再执行完整验证。公开问题报告不要包含 token、完整凭据或可利用的个人数据;先通过作者公开邮箱联系。不要用 `npm audit fix --force` 在未经检查的情况下升级整个主题。 diff --git a/archetypes/default.md b/archetypes/default.md index 25b6752..8a6b7da 100644 --- a/archetypes/default.md +++ b/archetypes/default.md @@ -2,4 +2,7 @@ date = '{{ .Date }}' draft = true title = '{{ replace .File.ContentBaseName "-" " " | title }}' +contentType = "technical" +tags = [] +summary = "请用一句话说明这篇文章的核心问题。" +++ diff --git a/archetypes/essay.md b/archetypes/essay.md new file mode 100644 index 0000000..e960a8e --- /dev/null +++ b/archetypes/essay.md @@ -0,0 +1,15 @@ ++++ +title = {{ printf "%q" .Name }} +date = '{{ .Date }}' +draft = true +contentType = "essay" +tags = [] +summary = "请用一句话说明这篇文章的核心问题。" ++++ + +## 问题从哪里来 + +## 观察与思考 + +## 尚未想明白的事 + diff --git a/archetypes/life.md b/archetypes/life.md new file mode 100644 index 0000000..ed9b336 --- /dev/null +++ b/archetypes/life.md @@ -0,0 +1,15 @@ ++++ +title = {{ printf "%q" .Name }} +date = '{{ .Date }}' +draft = true +contentType = "life" +tags = [] +summary = "请用一句话说明这篇文章的核心问题。" ++++ + +## 此时此地 + +## 想留下的细节 + +## 回看 + diff --git a/archetypes/project.md b/archetypes/project.md new file mode 100644 index 0000000..1876672 --- /dev/null +++ b/archetypes/project.md @@ -0,0 +1,19 @@ ++++ +title = {{ printf "%q" .Name }} +date = '{{ .Date }}' +draft = true +contentType = "project" +tags = [] +summary = "请用一句话说明这篇文章的核心问题。" ++++ + +## 背景与目标 + +## 设计与取舍 + +## 实现与测试 + +## 收获与改进 + +## 参考资料 + diff --git a/archetypes/reading.md b/archetypes/reading.md new file mode 100644 index 0000000..fc98d2e --- /dev/null +++ b/archetypes/reading.md @@ -0,0 +1,21 @@ ++++ +title = {{ printf "%q" .Name }} +date = '{{ .Date }}' +draft = true +contentType = "reading" +tags = [] +summary = "请用一句话说明这篇文章的核心问题。" ++++ + +## 书目信息 + +书名、作者、版本、章节。 + +## 核心论证 + +转述与引文需要注明出处和页码。 + +## 我的理解与疑问 + +## 延伸阅读 + diff --git a/archetypes/technical.md b/archetypes/technical.md new file mode 100644 index 0000000..9f89a7c --- /dev/null +++ b/archetypes/technical.md @@ -0,0 +1,23 @@ ++++ +title = {{ printf "%q" .Name }} +date = '{{ .Date }}' +draft = true +contentType = "technical" +tags = [] +summary = "请用一句话说明这篇文章的核心问题。" ++++ + +## 问题与环境 + +记录系统、工具版本,以及要解决的问题。 + +## 原理与验证 + +```text +在这里写最小可复现示例 +``` + +## 结论与参考 + +区分验证结果和推断。 + diff --git a/archive/removed-site-sections/2026-10-30/README.md b/archive/removed-site-sections/2026-10-30/README.md new file mode 100644 index 0000000..7e728de --- /dev/null +++ b/archive/removed-site-sections/2026-10-30/README.md @@ -0,0 +1,3 @@ +# 已撤下的展示内容 + +按 2026-10-30 的调整要求,项目、分类、友情链接、写作约定页面与工作台首页模板移至本目录。这里不属于 Hugo 的 content 或 layouts,内容不会发布,也不会进入搜索或 RSS。原文件和项目截图保留,方便日后恢复。 diff --git "a/archive/removed-site-sections/2026-10-30/content/categories/\346\200\235\346\203\263\351\232\217\347\254\224/_index.md" "b/archive/removed-site-sections/2026-10-30/content/categories/\346\200\235\346\203\263\351\232\217\347\254\224/_index.md" new file mode 100644 index 0000000..35c0d40 --- /dev/null +++ "b/archive/removed-site-sections/2026-10-30/content/categories/\346\200\235\346\203\263\351\232\217\347\254\224/_index.md" @@ -0,0 +1,4 @@ ++++ +title = "思想随笔" +description = "一些尚在生长的想法。" ++++ diff --git "a/archive/removed-site-sections/2026-10-30/content/categories/\346\212\200\346\234\257\345\255\246\344\271\240/_index.md" "b/archive/removed-site-sections/2026-10-30/content/categories/\346\212\200\346\234\257\345\255\246\344\271\240/_index.md" new file mode 100644 index 0000000..cc64258 --- /dev/null +++ "b/archive/removed-site-sections/2026-10-30/content/categories/\346\212\200\346\234\257\345\255\246\344\271\240/_index.md" @@ -0,0 +1,4 @@ ++++ +title = "技术学习" +description = "理解计算机原理,记录可复现的学习过程。" ++++ diff --git "a/archive/removed-site-sections/2026-10-30/content/categories/\347\224\237\346\264\273\350\256\260\345\275\225/_index.md" "b/archive/removed-site-sections/2026-10-30/content/categories/\347\224\237\346\264\273\350\256\260\345\275\225/_index.md" new file mode 100644 index 0000000..790db41 --- /dev/null +++ "b/archive/removed-site-sections/2026-10-30/content/categories/\347\224\237\346\264\273\350\256\260\345\275\225/_index.md" @@ -0,0 +1,4 @@ ++++ +title = "生活记录" +description = "收集日常的细节。" ++++ diff --git "a/archive/removed-site-sections/2026-10-30/content/categories/\350\257\273\344\271\246\347\254\224\350\256\260/_index.md" "b/archive/removed-site-sections/2026-10-30/content/categories/\350\257\273\344\271\246\347\254\224\350\256\260/_index.md" new file mode 100644 index 0000000..9e88522 --- /dev/null +++ "b/archive/removed-site-sections/2026-10-30/content/categories/\350\257\273\344\271\246\347\254\224\350\256\260/_index.md" @@ -0,0 +1,4 @@ ++++ +title = "读书笔记" +description = "区分原文、理解与疑问,让阅读留下痕迹。" ++++ diff --git "a/archive/removed-site-sections/2026-10-30/content/categories/\351\241\271\347\233\256\345\274\200\345\217\221/_index.md" "b/archive/removed-site-sections/2026-10-30/content/categories/\351\241\271\347\233\256\345\274\200\345\217\221/_index.md" new file mode 100644 index 0000000..8fa58a2 --- /dev/null +++ "b/archive/removed-site-sections/2026-10-30/content/categories/\351\241\271\347\233\256\345\274\200\345\217\221/_index.md" @@ -0,0 +1,4 @@ ++++ +title = "项目开发" +description = "从问题到实现,也记录过程中的取舍。" ++++ diff --git a/archive/removed-site-sections/2026-10-30/content/links/index.md b/archive/removed-site-sections/2026-10-30/content/links/index.md new file mode 100644 index 0000000..126b407 --- /dev/null +++ b/archive/removed-site-sections/2026-10-30/content/links/index.md @@ -0,0 +1,17 @@ ++++ +title = "友情链接与常去的地方" +description = "值得持续阅读的工具文档与交流入口。" +showHero = false +showAuthor = false +showDate = false ++++ + +## 常去的地方 + +- [Hugo 文档](https://gohugo.io/documentation/):静态站点与内容组织的参考手册。 +- [Blowfish 文档](https://blowfish.page/zh-cn/docs/):本站主题的配置、模板与写作组件。 +- [GitHub Skills](https://skills.github.com/):通过练习理解 GitHub 协作。 + +## 朋友的站点 + +这个列表等待慢慢生长。欢迎通过[邮件](mailto:1578686349@qq.com)介绍你的个人网站;我会在阅读后手动添加。 diff --git a/archive/removed-site-sections/2026-10-30/content/projects/_index.md b/archive/removed-site-sections/2026-10-30/content/projects/_index.md new file mode 100644 index 0000000..53ff57c --- /dev/null +++ b/archive/removed-site-sections/2026-10-30/content/projects/_index.md @@ -0,0 +1,7 @@ ++++ +title = "项目实验室" +description = "把问题变成项目,记录每一次构建与迭代。" +showHero = false ++++ + +这里存放自己动手做过的项目。不只展示结果,也记录取舍、踩坑和下一步。 diff --git a/archive/removed-site-sections/2026-10-30/content/projects/personal-blog/index.md b/archive/removed-site-sections/2026-10-30/content/projects/personal-blog/index.md new file mode 100644 index 0000000..79d1efb --- /dev/null +++ b/archive/removed-site-sections/2026-10-30/content/projects/personal-blog/index.md @@ -0,0 +1,52 @@ ++++ +title = "个人博客与自动化发布系统" +date = 2026-10-29T12:00:00+08:00 +draft = false +description = "用 Hugo 与 Blowfish 搭建一个适合长期写作的数字花园,并实践内容检查、测试与审核发布。" +summary = "一个持续维护的工程练习:把写作、静态站点构建和自动化检查连接起来。" +stack = ["Hugo", "Blowfish", "Python", "GitHub Actions"] +github = "https://github.com/Nizeross/nizeross.github.io" +status = "持续维护" +weight = 1 +tags = ["Hugo", "Blowfish", "工程实践"] +categories = ["项目开发"] +showHero = false ++++ + +## 为什么做这个项目 + +把日常技术笔记、读书记录与个人随笔放在自己能长期维护的地方,同时通过真实项目学习 Git、HTTP、测试和自动化发布。 + +## 项目一览 + +![博客首页的桌面浅色模式运行截图](screenshot.png) + +上图来自本地重构版本的真实浏览器测试。下面是这套系统的工作流示意: + +![博客的构建与审核发布流程示意](screenshot.svg) + +## 技术栈与取舍 + +Hugo 将 Markdown 在构建时转成静态 HTML,Blowfish 提供搜索、目录、图片处理和明暗主题。少量原生 CSS 与 JavaScript 负责首页和阅读进度,避免把日常写作绑定到复杂应用框架。 + +## 开发过程 + +1. 审计现有文章路径、主题子模块、域名与部署流程。 +2. 在独立本地分支开发界面与内容工具,保留已有文章 URL。 +3. 加入 Frontmatter、Markdown、本地链接与浏览器检查。 +4. 准备需要人工审核的发布流程,确认后才接入线上站点。 + +## 学到的知识 + +- 静态资源可以在构建时完成处理,无需运行数据库服务。 +- CI 负责验证变更,CD 负责交付已经通过验证的产物。 +- 草稿开关不等于审核;审核需要分支保护与部署权限共同约束。 +- 设计系统应通过主题变量扩展,减少对上游模板的依赖。 + +## 后续改进 + +- 在审核后启用新的生产发布流程。 +- 随文章积累改善分类与关联推荐。 +- 定期复核工具链和主题内置脚本的安全更新。 + +源码与问题记录:[GitHub 仓库](https://github.com/Nizeross/nizeross.github.io)。 diff --git a/archive/removed-site-sections/2026-10-30/content/projects/personal-blog/screenshot.png b/archive/removed-site-sections/2026-10-30/content/projects/personal-blog/screenshot.png new file mode 100644 index 0000000..89a8ca7 Binary files /dev/null and b/archive/removed-site-sections/2026-10-30/content/projects/personal-blog/screenshot.png differ diff --git a/archive/removed-site-sections/2026-10-30/content/projects/personal-blog/screenshot.svg b/archive/removed-site-sections/2026-10-30/content/projects/personal-blog/screenshot.svg new file mode 100644 index 0000000..b7ae679 --- /dev/null +++ b/archive/removed-site-sections/2026-10-30/content/projects/personal-blog/screenshot.svg @@ -0,0 +1,9 @@ + +博客自动化工作流示意Markdown 内容经过检查和构建,再经人工审核发布到 GitHub Pages。 + +~/nizeross/blog-system +WRITE. BUILD. LEARN.A small system for long-term thinking. + +MarkdownCheck + BuildReviewPages + +Hugo / Blowfish / Python / GitHub Actions diff --git a/archive/removed-site-sections/2026-10-30/content/writing/index.md b/archive/removed-site-sections/2026-10-30/content/writing/index.md new file mode 100644 index 0000000..203a6ee --- /dev/null +++ b/archive/removed-site-sections/2026-10-30/content/writing/index.md @@ -0,0 +1,44 @@ ++++ +title = "写作约定与排版示例" +description = "这个数字花园的内容约定、Markdown 与数学公式示例。" +showHero = false +showAuthor = false +showDate = false ++++ + +{{< katex >}} + +## 先写清楚问题 + +技术笔记交代环境、步骤和验证方法;读书笔记区分原文、转述和自己的理解;推断与结论尽量附上依据。 + +## 代码 + +为围栏标记语言,方便高亮与复制: + +```python +def learn_by_building(question: str) -> str: + return f"提出问题 → 动手验证 → 记录:{question}" +``` + +## 数学公式 + +在文章中加入 `{{}}`,随后可以使用行内公式 \(O(n \log n)\) 和独立公式: + +$$ +\sum_{i=1}^{n} i = \frac{n(n+1)}{2} +$$ + +## 图片 + +图片放在文章自己的目录,与 `index.md` 同级,再以相对路径引用。点击下面的图片可以放大。 + +![站点标志](logo.png) + +## 链接与锚点 + +站内路径使用根相对链接,例如[关于页面](/about/);同页链接可以跳转到[代码](#代码)。发布前检查会验证本地文件和生成页面的锚点。 + +## 内容维护 + +小修改保留原 slug,实质更新补充 `lastmod`。文章默认创建为草稿,检查通过后再提交审核。 diff --git a/archive/removed-site-sections/2026-10-30/content/writing/logo.png b/archive/removed-site-sections/2026-10-30/content/writing/logo.png new file mode 100644 index 0000000..d0c878b Binary files /dev/null and b/archive/removed-site-sections/2026-10-30/content/writing/logo.png differ diff --git a/archive/removed-site-sections/2026-10-30/layouts/partials/home/custom.html b/archive/removed-site-sections/2026-10-30/layouts/partials/home/custom.html new file mode 100644 index 0000000..54cf775 --- /dev/null +++ b/archive/removed-site-sections/2026-10-30/layouts/partials/home/custom.html @@ -0,0 +1,47 @@ +
+
+
+

NIZEROSS / PERSONAL WORKSPACE

+

保持好奇,
把想法写成现实。

+

我是 NIZEROSS,一名计算机专业学生。在这里记录学习、构建与思考,也给尚未想明白的问题留一点空间。

+ +

计算机 · 开源实践 · 阅读与生活

+
+ +
+ +
+

EXPLORE THE GARDEN

沿着兴趣,慢慢探索

全部分类
+
+ {{ range slice (dict "name" "技术学习" "icon" "code" "note" "理解原理,也动手验证" "index" "01") (dict "name" "项目开发" "icon" "codeberg" "note" "让知识成为可用的东西" "index" "02") (dict "name" "读书笔记" "icon" "file-lines" "note" "与书本展开一场对话" "index" "03") (dict "name" "思想随笔" "icon" "lightbulb" "note" "留住尚在生长的想法" "index" "04") (dict "name" "生活记录" "icon" "mug-hot" "note" "收集日常的细节" "index" "05") }} + {{ $term := site.GetPage (printf "/categories/%s" .name) }} + {{ partial "icon.html" .icon }}{{ .index }}

{{ .name }}

{{ .note }}

+ {{ end }} +
+
+ +
+

RECENT CHANGES

最近的记录

所有文章
+
+ {{ range first 5 (where site.RegularPages "Section" "posts").ByLastmod.Reverse }} +

{{ .Title }}

{{ .Summary | plainify | truncate 105 }}

+ {{ end }} +
+
+ +

BUILD IN PUBLIC

从使用工具,到理解工具。

这个网站也是我的工程练习场:用简单的技术,认真解决写作、阅读和发布的问题。

打开项目日志
+
diff --git a/archive/removed-site-sections/2026-10-30/layouts/projects/list.html b/archive/removed-site-sections/2026-10-30/layouts/projects/list.html new file mode 100644 index 0000000..76cfef1 --- /dev/null +++ b/archive/removed-site-sections/2026-10-30/layouts/projects/list.html @@ -0,0 +1,16 @@ +{{ define "main" }} +
+

SMALL TOOLS, REAL QUESTIONS

+

{{ .Title }}

+
{{ .Content }}
+
+ {{ range .Pages.ByWeight }} +
+ {{ $page := . }} + {{ with .Resources.GetMatch "screenshot*" }}{{ end }} +
{{ .Params.status | default "持续维护" }}

{{ .Title }}

{{ .Description }}

    {{ range .Params.stack }}
  • {{ . }}
  • {{ end }}
+
+ {{ end }} +
+
+{{ end }} diff --git a/assets/css/custom.css b/assets/css/custom.css index 4221e5a..9ff1f62 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -1,13 +1,25 @@ -/* 光标闪烁动画 */ -.blink-cursor { - display: inline-block; - font-weight: bold; - color: orange; /* 光标颜色 */ - animation: blink 1s step-end infinite; - margin-left: 2px; -} - -@keyframes blink { - 0%, 100% { opacity: 1; } - 50% { opacity: 0; } -} +/* Original homepage cursor; reading and search enhancements remain scoped. */ +.blink-cursor { display: inline-block; font-weight: bold; color: orange; animation: blink 1s step-end infinite; margin-left: 2px; } +@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } +:root { --desk-border: rgba(var(--color-neutral-300), .8); --desk-muted: rgb(var(--color-neutral-600)); --desk-accent: #16645a; --desk-panel: rgba(var(--color-neutral-50), .7); } +.dark { --desk-border: rgba(var(--color-neutral-600), .7); --desk-muted: rgb(var(--color-neutral-300)); --desk-accent: #7dd3b8; --desk-panel: rgba(var(--color-neutral-700), .23); } +body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; } +:focus-visible { outline: 2px solid var(--desk-accent); outline-offset: 5px; } +.article-content { line-height: 1.95; overflow-wrap: anywhere; } +.article-content p { text-wrap: pretty; } +.article-content pre { max-width: 100%; overflow-x: auto; line-height: 1.7; } +.article-content pre code { overflow-wrap: normal; word-break: normal; font: .875rem/1.7 ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace; } +.article-content img { border-radius: .5rem; } +.article-content h2, .article-content h3, .article-content h4 { scroll-margin-top: 8rem; } +.article-content table { font-size: .9em; } +.search-result { display: block; padding: .85rem; margin: .4rem 0; border-radius: .3rem; background: var(--desk-panel); } +.search-result strong, .search-result > span { display: block; } +.search-result > span { color: var(--desk-muted); font-size: .8rem; line-height: 1.7; margin-top: .3rem; } +.search-result:hover, .search-result:focus-visible { background: rgba(var(--color-primary-300), .15); } +.reading-progress { position: fixed; top: 0; left: 0; width: 100%; height: 3px; border: 0; appearance: none; z-index: 110; background: transparent; color: var(--desk-accent); pointer-events: none; } +.reading-progress::-webkit-progress-bar { background: transparent; } +.reading-progress::-webkit-progress-value { background: var(--desk-accent); } +.reading-progress::-moz-progress-bar { background: var(--desk-accent); } +#site-footer { border-top: 1px solid var(--desk-border); margin-top: 2rem; } +@media (prefers-reduced-motion: reduce) { *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; transition-duration: .01ms !important; } } +@media print { .reading-progress { display: none; } } diff --git a/assets/js/navigation-accessibility.js b/assets/js/navigation-accessibility.js new file mode 100644 index 0000000..50d16aa --- /dev/null +++ b/assets/js/navigation-accessibility.js @@ -0,0 +1,37 @@ +(() => { + const toggle = document.getElementById('mobile-menu-toggle'); + const dialog = document.getElementById('mobile-navigation'); + const labels = [...document.querySelectorAll('label[for="mobile-menu-toggle"]')]; + if (!toggle || !dialog) return; + const opener = labels.find(label => !dialog.contains(label)); + const close = () => { + toggle.checked = false; + opener?.setAttribute('aria-expanded', 'false'); + opener?.focus(); + }; + opener?.setAttribute('aria-expanded', 'false'); + labels.forEach(label => label.addEventListener('keydown', event => { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + label.click(); + } + })); + toggle.addEventListener('change', () => { + opener?.setAttribute('aria-expanded', String(toggle.checked)); + requestAnimationFrame(() => { + if (toggle.checked) dialog.querySelector('[role="button"]')?.focus(); + else opener?.focus(); + }); + }); + document.addEventListener('keydown', event => { + if (!toggle.checked) return; + if (event.key === 'Escape') { event.preventDefault(); close(); } + if (event.key !== 'Tab') return; + const focusable = [...dialog.querySelectorAll('a[href],button,[tabindex="0"]')].filter(el => el.getClientRects().length); + if (event.shiftKey && document.activeElement === focusable[0]) { + event.preventDefault(); focusable.at(-1)?.focus(); + } else if (!event.shiftKey && document.activeElement === focusable.at(-1)) { + event.preventDefault(); focusable[0]?.focus(); + } + }); +})(); diff --git a/assets/js/reading-progress.js b/assets/js/reading-progress.js new file mode 100644 index 0000000..16e6b2e --- /dev/null +++ b/assets/js/reading-progress.js @@ -0,0 +1,24 @@ +(() => { + const article = document.querySelector('.article-content'); + if (!article) return; + const progress = document.createElement('progress'); + progress.className = 'reading-progress'; + progress.max = 100; + progress.value = 0; + progress.setAttribute('aria-label', '文章阅读进度'); + document.body.append(progress); + let scheduled = false; + const update = () => { + const top = article.getBoundingClientRect().top + window.scrollY; + const distance = Math.max(1, article.offsetHeight - window.innerHeight + 160); + progress.value = Math.min(100, Math.max(0, (window.scrollY - top + 160) / distance * 100)); + scheduled = false; + }; + const schedule = () => { + if (!scheduled) { scheduled = true; requestAnimationFrame(update); } + }; + window.addEventListener('scroll', schedule, { passive: true }); + window.addEventListener('resize', schedule); + window.addEventListener('load', schedule); + update(); +})(); diff --git a/assets/js/search.js b/assets/js/search.js new file mode 100644 index 0000000..26ffbbf --- /dev/null +++ b/assets/js/search.js @@ -0,0 +1,90 @@ +/* Uses Blowfish's local Fuse index; creates text nodes instead of injecting HTML. */ +(() => { + const wrapper = document.getElementById('search-wrapper'); + const input = document.getElementById('search-query'); + const output = document.getElementById('search-results'); + const modal = document.getElementById('search-modal'); + if (!wrapper || !input || !output || !modal) return; + let indexPromise; + let previousFocus; + let visible = false; + let queryRevision = 0; + + const index = () => indexPromise ||= fetch(`${wrapper.dataset.url.replace(/\/?$/, '/')}index.json`) + .then(response => { if (!response.ok) throw new Error('Search index unavailable'); return response.json(); }) + .then(data => new Fuse(data, { ignoreLocation: true, threshold: .2, keys: ['title', 'summary', 'content', 'section'] })); + + const close = () => { + visible = false; + queryRevision++; + wrapper.style.visibility = 'hidden'; + document.body.style.overflow = ''; + input.value = ''; + output.replaceChildren(); + previousFocus?.focus(); + }; + const open = () => { + if (visible) return; + visible = true; + previousFocus = document.activeElement; + wrapper.style.visibility = 'visible'; + document.body.style.overflow = 'hidden'; + input.focus(); + index().catch(() => { indexPromise = null; }); + }; + ['search-button', 'search-button-mobile'].forEach(id => document.getElementById(id)?.addEventListener('click', open)); + document.getElementById('close-search-button').addEventListener('click', close); + wrapper.addEventListener('click', event => { if (event.target === wrapper) close(); }); + input.closest('form').addEventListener('submit', event => { + event.preventDefault(); + output.querySelector('a')?.focus(); + }); + input.addEventListener('input', async () => { + const revision = ++queryRevision; + const term = input.value.trim(); + output.replaceChildren(); + if (!term) return; + try { + const fuse = await index(); + if (revision !== queryRevision || !visible) return; + const results = fuse.search(term).slice(0, 12); + if (!results.length) { + const item = document.createElement('li'); + item.textContent = '没有找到匹配的文章。'; + output.append(item); + } + for (const { item } of results) { + const li = document.createElement('li'); + const link = document.createElement('a'); + const title = document.createElement('strong'); + const summary = document.createElement('span'); + const url = new URL(item.permalink, location.origin); + if (!['http:', 'https:'].includes(url.protocol)) continue; + link.href = url.pathname + url.search + url.hash; + link.className = 'search-result'; + title.textContent = item.title; + const plain = new DOMParser().parseFromString(item.summary || '', 'text/html').body.textContent; + summary.textContent = plain.slice(0, 130); + link.append(title, summary); + li.append(link); + output.append(li); + } + } catch { + output.textContent = '搜索暂时无法加载,请稍后重试。'; + indexPromise = null; + } + }); + document.addEventListener('keydown', event => { + if (event.key === '/' && !visible && !/input|textarea|select/i.test(document.activeElement.tagName) && !document.activeElement.isContentEditable) { + event.preventDefault(); open(); + } + if (!visible) return; + if (event.key === 'Escape') { event.preventDefault(); close(); } + const elements = [...modal.querySelectorAll('input, button, a[href]')]; + const current = elements.indexOf(document.activeElement); + if (event.key === 'Tab' && event.shiftKey && current === 0) { event.preventDefault(); elements.at(-1).focus(); } + else if (event.key === 'Tab' && !event.shiftKey && current === elements.length - 1) { event.preventDefault(); elements[0].focus(); } + if (event.key === 'ArrowDown') { event.preventDefault(); elements[Math.min(current + 1, elements.length - 1)]?.focus(); } + if (event.key === 'ArrowUp') { event.preventDefault(); elements[Math.max(0, current - 1)]?.focus(); } + }); +})(); diff --git a/assets/js/typewriter.js b/assets/js/typewriter.js new file mode 100644 index 0000000..1df74c5 --- /dev/null +++ b/assets/js/typewriter.js @@ -0,0 +1,12 @@ +(() => { + const element = document.getElementById('typewriter'); + if (!element || matchMedia('(prefers-reduced-motion: reduce)').matches) return; + const text = element.dataset.text || element.textContent; + element.textContent = ''; + let index = 0; + const type = () => { + element.textContent = text.slice(0, ++index); + if (index < text.length) setTimeout(type, 350); + }; + setTimeout(type, 350); +})(); diff --git a/config/_default/hugo.toml b/config/_default/hugo.toml index aeb6985..c84c44a 100644 --- a/config/_default/hugo.toml +++ b/config/_default/hugo.toml @@ -28,7 +28,6 @@ enableEmoji = true [taxonomies] tag = "tags" - category = "categories" author = "authors" series = "series" @@ -48,10 +47,6 @@ enableEmoji = true name = "tags" weight = 100 - [[related.indices]] - name = "categories" - weight = 100 - [[related.indices]] name = "series" weight = 50 diff --git a/config/_default/languages.zh-cn.toml b/config/_default/languages.zh-cn.toml index 2eb7d2a..e25014c 100644 --- a/config/_default/languages.zh-cn.toml +++ b/config/_default/languages.zh-cn.toml @@ -1,27 +1,27 @@ disabled = false -languageCode = "en" -languageName = "English" +languageCode = "zh-CN" +languageName = "简体中文" weight = 1 title = "NizerossのBlog" [params] displayName = "简体中文" - isoCode = "cn" + isoCode = "zh-CN" rtl = false dateFormat = "2006 年 1 月 2 日" logo = "img/site_logo.png" # secondaryLogo = "img/secondary-logo.png" - description = "NIZEROSS's website" + description = "NIZEROSS 的数字花园:计算机学习、项目实践、阅读与思考。" copyright = '© 2025 - { year } Nizeross All Rights Reserved.' [params.author] name = "NIZEROSS" image = "img/avatar.png" - headline = "|" + headline = "千锤百炼" bio = "千锤百炼" links = [ { bilibili = "https://space.bilibili.com/176505165" }, { email = "mailto:1578686349@qq.com" }, { zhihu = "https://www.zhihu.com/people/da-kong-mo-shu" }, { steam = "https://steamcommunity.com/id/nizeross/" }, - ] \ No newline at end of file + ] diff --git a/config/_default/menus.zh-cn.toml b/config/_default/menus.zh-cn.toml index 22f1319..2f6f129 100644 --- a/config/_default/menus.zh-cn.toml +++ b/config/_default/menus.zh-cn.toml @@ -28,6 +28,17 @@ pageRef = "/tags" weight = 30 +[[main]] + name = "About" + pre = "circle-info" + pageRef = "about" + weight = 40 + +[[footer]] + name = "RSS" + url = "/index.xml" + weight = 40 + #[[main]] # name = "Parent" diff --git a/config/_default/params.toml b/config/_default/params.toml index 688e627..9b04f22 100644 --- a/config/_default/params.toml +++ b/config/_default/params.toml @@ -12,6 +12,7 @@ autoSwitchAppearance = true enableA11y = false enableSearch = true enableCodeCopy = true +disableImageZoom = false enableStructuredBreadcrumbs = false # enableStyledScrollbar = true # disable to use native scrollbar style (defaults to true) @@ -52,7 +53,7 @@ forgejoDefaultServer = "https://v11.next.forgejo.org" showScrollToTop = true [homepage] - layout = "background" # valid options: page, profile, hero, card, background, custom + layout = "background" # Original Blowfish profile and background layout homepageImage = "image.jpg" # used in: hero, and card showRecent = true showRecentItems = 3 @@ -65,7 +66,7 @@ forgejoDefaultServer = "https://v11.next.forgejo.org" [article] showDate = true - showViews = true + showViews = false showLikes = false showDasteOnlyInArticle = false showDateUpdated = false @@ -86,9 +87,9 @@ forgejoDefaultServer = "https://v11.next.forgejo.org" invertPagination = false showReadingTime = true showTableOfContents = true - # showRelatedContent = false - # relatedContentLimit = 3 - showTaxonomies = false # use showTaxonomies OR showCategoryOnly, not both + showRelatedContent = true + relatedContentLimit = 3 + showTaxonomies = true # use showTaxonomies OR showCategoryOnly, not both showCategoryOnly = false # use showTaxonomies OR showCategoryOnly, not both showAuthorsBadges = false showWordCount = true @@ -138,13 +139,7 @@ forgejoDefaultServer = "https://v11.next.forgejo.org" cardViewScreenWidth = false [firebase] - # apiKey = "AIzaSyDlMaHFta2wQE_de28aIka_LMI28PJUBV4" - # authDomain = "blog-blowfish-cfaee.firebaseapp.com" - # projectId = "blog-blowfish-cfaee" - # storageBucket = "blog-blowfish-cfaee.firebasestorage.app" - # messagingSenderId = "587286702711" - # appId = "1:587286702711:web:327f6d58bd48f0dcc4b472" - # measurementId = "G-V7RW6NWLKM" + # Optional integration deliberately left unconfigured. [fathomAnalytics] # site = "ABC12345" diff --git a/content/about/index.md b/content/about/index.md new file mode 100644 index 0000000..7f447f8 --- /dev/null +++ b/content/about/index.md @@ -0,0 +1,33 @@ ++++ +title = "关于这个角落" +description = "一名计算机专业学生的学习、构建与记录。" +showHero = false +showAuthor = true +showDate = false +showReadingTime = false +showWordCount = false ++++ + +Hi,there +我是 **NIZEROSS**,一名计算机专业的大二学生。 + +这个网站是我的数字花园:有已经想明白的技术笔记,也有仍在生长的问题。希望记录的不只是“怎么做”,还有“为什么这样做”。 + +## 我在关注 + +- **技术学习**:理解计算机与软件背后的原理。 +- **项目开发**:从真实问题出发,做能持续使用的小工具。 +- **读书笔记**:记录哲学与其他阅读中的问题、论证和联想。 +- **思想随笔与生活**:保留学习之外的观察。 + +## 关于本站 + +本站使用 Hugo 和 Blowfish 构建,采用静态发布。文章中的推断和未验证结论应当明确标注,技术内容也可能随着学习而修订。 + +你可以通过 [RSS](/index.xml) 订阅更新,或在 [GitHub](https://github.com/Nizeross/nizeross.github.io) 查看这个网站的实现。 + +## Find Me + +- [Bilibili](https://space.bilibili.com/176505165) +- [知乎](https://www.zhihu.com/people/da-kong-mo-shu) +- [邮件联系](mailto:1578686349@qq.com) diff --git a/content/posts/how-build-blog/index.md b/content/posts/how-build-blog/index.md index ba6b4e6..a2a9c79 100644 --- a/content/posts/how-build-blog/index.md +++ b/content/posts/how-build-blog/index.md @@ -2,19 +2,27 @@ date = '2026-03-31T17:01:46+08:00' draft = false tags = ["Hugo", "Blowfish"] +contentType = "technical" +summary = "记录使用 Hugo 与 Blowfish 搭建博客时的目录组织、主题覆盖与 GitHub Pages 部署实践。" title = '记录一下折腾blog的经历' +++ ## 前言 + 搭建本博客参考了Gemini 3.0 Pro,Blowfish官方文档以及其他人的博客,还是踩了不少坑的,有的文档已经过时或者操作过于繁琐,官方文档又很庞杂,AI也有一定幻觉. - - [hugo官方文档](https://gohugo.io/documentation/) - - [Blowfish文档](https://blowfish.page/zh-cn/docs/) - - [褐瞳さん - 如何使用 Github Page 搭建自己的博客](https://www.hetong-re4per.com/posts/how-bulid-blog-on-github-page/) + +- [hugo官方文档](https://gohugo.io/documentation/) +- [Blowfish文档](https://blowfish.page/zh-cn/docs/) +- [褐瞳さん - 如何使用 Github Page 搭建自己的博客](https://www.hetong-re4per.com/posts/how-bulid-blog-on-github-page/) + ## Step + ### 创建项目 + 首先创建一个 Hugo 项目,site-name 记得替换成你的网站的名称(其实就是待会创建的文件夹名称)`hugo new site site-name` 可以看一眼它的项目结构,项目结构大概像这样: -``` + +```text site-name ├── archetypes # 模板文件 ├── assets # 资源文件,如 Logo @@ -26,15 +34,19 @@ site-name ├── themes # 主题文件夹 └── hugo.toml # 项目配置文件 ``` + 然后可以添加第一篇文章 -``` + +```text hugo new content posts/first-blog.md ``` + 这条命令会向 content 文件夹内新建一个 posts 文件夹,然后再根据默认模板创建一个 first-blog.md 文件 然后使用`hugo server` 即可在本地运行 ### 覆盖主题 + 在 Hugo 中,你自己所写的主题文件优先级都是高于 `themes/主题名称/` 文件夹内主题的,所以可以很轻松的覆写你想 DIY 的部分 对于 themes 文件夹内的文件,你只需要保证在根目录有着同样文件结构就能够覆盖 @@ -42,12 +54,13 @@ hugo new content posts/first-blog.md 例如目录部分,对于 blowfish 它的位置是 `themes/blowfish/layouts/partials/toc.html`,而我们只需要复制这个文件,并把它放在 layouts/partials/toc.html 即可 ### 部署到Github Pages -* **本地预览**:`hugo server` -* **部署发布**:`git add .` -> `git commit -m "..."` -> `git push` -* **在线编辑**:在 GitHub 仓库按 `.` 进入 Web 编辑器。 + +- **本地预览**:`hugo server` +- **部署发布**:`git add .` -> `git commit -m "..."` -> `git push` +- **在线编辑**:在 GitHub 仓库按 `.` 进入 Web 编辑器。 ## Tips -### 自定义图标添加 -- [simpleicon](https://simpleicons.org/) +### 自定义图标添加 +- [simpleicon](https://simpleicons.org/) diff --git a/content/posts/my-first-post/index.md b/content/posts/my-first-post/index.md index cdbde30..157d85f 100644 --- a/content/posts/my-first-post/index.md +++ b/content/posts/my-first-post/index.md @@ -3,26 +3,32 @@ date = '2026-02-01T18:46:57+08:00' draft = false title = 'My First Post' tags = ["生活", "编程"] -categories = ["随笔"] +contentType = "life" +summary = "博客的第一篇记录,以及 Markdown、代码块与提示组件的尝试。" +++ 这里是摘要部分。写在这里的文字会显示在博客首页的卡片上。 ## 一、这是二级标题 + 这是正文段落。你可以在这里写下你的想法。**这是加粗的文字**,*这是斜体*。 ### 1.1 这是三级标题 + 如果你想列出清单: + * 第一点 * 第二点 * 子要点 ## 二、插入代码 + 作为计算机学生,你肯定要贴代码。Hugo 的代码高亮非常漂亮: ```python def hello_world(): print("Hello Blowfish!") +``` {{< alert icon="lightbulb" >}} 这是一个提示! 可以在这里写一些 Tips。 {{< /alert >}} -{{< alert icon="triangle-exclamation" cardColor="#e63946" iconColor="#1d3557" >}} 这是一个警告! 这里的颜色是可以自定义的。 {{< /alert >}} \ No newline at end of file +{{< alert icon="triangle-exclamation" cardColor="#e63946" iconColor="#1d3557" >}} 这是一个警告! 这里的颜色是可以自定义的。 {{< /alert >}} diff --git a/content/posts/pcl2-memory-optimization/index.md b/content/posts/pcl2-memory-optimization/index.md index 61bc4e3..1cc78a7 100644 --- a/content/posts/pcl2-memory-optimization/index.md +++ b/content/posts/pcl2-memory-optimization/index.md @@ -3,9 +3,11 @@ date = '2026-09-07T12:00:00+08:00' draft = false title = 'PCL2百宝箱内存优化功能分析' tags = ["内存优化"] -categories = ["随笔"] +contentType = "technical" +summary = "基于公开信息与技术推断分析 PCL2 内存优化:工作集缩减机制、局限与其他方式的比较。" +++ # PCL2 百宝箱 — 内存优化功能实现分析 + 本文借助了Claude Opus4.6 ## 概述 @@ -15,7 +17,7 @@ PCL2(Plain Craft Launcher 2)百宝箱中的**内存优化**功能本质上 > [!NOTE] > PCL2 官方开源仓库中,百宝箱相关代码被故意隐去,源码中仅有占位提示: > `Hint("为便于维护,开源内容中不包含百宝箱功能……")` -> +> > 以下分析基于社区版 (PCL2-CE) 的公开讨论、GitHub Issues,以及对技术原理的逆向推断。 --- @@ -55,6 +57,7 @@ End Sub ``` **关键步骤**: + 1. **枚举进程** — 通过 `Process.GetProcesses()` 获取所有运行中的进程 2. **逐一清理** — 对每个进程调用 `EmptyWorkingSet(proc.Handle)` 3. **异常处理** — 对于系统关键进程(权限不足无法操作的),捕获异常并跳过 @@ -64,7 +67,7 @@ End Sub PCL2 还支持通过启动参数 `--memory` 以静默方式执行内存优化: -``` +```text PCL.exe --memory ``` @@ -92,6 +95,7 @@ flowchart TD ## 效果与局限性 ### ✅ 能做到什么 + | 效果 | 说明 | |------|------| | 降低物理内存占用 | 任务管理器中显示的内存使用量会立即下降(通常可降低约 1/3) | diff --git a/layouts/_default/_markup/render-codeblock-mermaid.html b/layouts/_default/_markup/render-codeblock-mermaid.html new file mode 100644 index 0000000..a204e91 --- /dev/null +++ b/layouts/_default/_markup/render-codeblock-mermaid.html @@ -0,0 +1 @@ +
{{ .Inner }}
diff --git a/layouts/partials/extend-footer.html b/layouts/partials/extend-footer.html new file mode 100644 index 0000000..5240536 --- /dev/null +++ b/layouts/partials/extend-footer.html @@ -0,0 +1,2 @@ +{{ $scripts := slice (resources.Get "js/reading-progress.js") (resources.Get "js/navigation-accessibility.js") (resources.Get "js/typewriter.js") | resources.Concat "js/site.js" | resources.Minify | resources.Fingerprint "sha512" }} + diff --git a/layouts/partials/extend-head-uncached.html b/layouts/partials/extend-head-uncached.html new file mode 100644 index 0000000..25fd50e --- /dev/null +++ b/layouts/partials/extend-head-uncached.html @@ -0,0 +1,7 @@ +{{/* Existing Markdown mermaid fences use the audited local asset bundle. */}} +{{ if and (not (.HasShortcode "mermaid")) (findRE "(?m)^```mermaid" .RawContent) }} + {{ $mermaid := resources.Get "lib/mermaid/mermaid.min.js" }} + {{ $config := resources.Get "js/mermaid.js" | resources.Minify }} + {{ $bundle := slice $mermaid $config | resources.Concat "js/mermaid.bundle.js" | resources.Fingerprint "sha512" }} + +{{ end }} diff --git a/layouts/partials/footer.html b/layouts/partials/footer.html index 859cbf3..752e86b 100644 --- a/layouts/partials/footer.html +++ b/layouts/partials/footer.html @@ -79,31 +79,4 @@ {{ if templates.Exists "partials/extend-footer.html" }} {{ partialCached "extend-footer.html" . }} {{ end }} -
- - - -
- diff --git a/layouts/partials/header/components/mobile-menu.html b/layouts/partials/header/components/mobile-menu.html new file mode 100644 index 0000000..1f538a9 --- /dev/null +++ b/layouts/partials/header/components/mobile-menu.html @@ -0,0 +1,155 @@ +
+ {{ if .Site.Params.enableSearch | default false }} + + {{ end }} + + {{ if .Site.Params.footer.showAppearanceSwitcher | default false }} + + {{ end }} + + {{ if or + .Site.Menus.main + .Site.Menus.subnavigation + .Site.Params.enableA11y + }} + + + + + {{ end }} +
+ +{{ define "mobile-main-menu" }} + {{ range .Site.Menus.main }} + {{ $submenuId := printf "fullscreen-submenu-%s" (.Identifier | default .Name | anchorize) }} +
+ + {{ if .Pre }} + + {{ partial "icon.html" .Pre }} + + {{ end }} + + {{ .Name | markdownify }} + + {{ if .HasChildren }} + + {{ end }} + + + {{ if .HasChildren }} + + + + {{ end }} +
+ {{ end }} +{{ end }} + +{{ define "mobile-subnavigation" }} + {{ if .Site.Menus.subnavigation }} +
+ {{ range .Site.Menus.subnavigation }} + + {{ if .Pre }} + + {{ partial "icon.html" .Pre }} + + {{ end }} + {{ .Name | markdownify }} + + {{ end }} +
+ {{ end }} +{{ end }} + +{{ define "mobile-footer-components" }} + {{ if or + hugo.IsMultilingual + .Site.Params.enableA11y + }} +
+ {{ partial "header/components/translations.html" . }} + + {{ if .Site.Params.enableA11y | default false }} + {{ partial "header/components/a11y.html" (dict "prefix" "mobile-menu-") }} + {{ end }} +
+ {{ end }} +{{ end }} diff --git a/layouts/partials/icon.html b/layouts/partials/icon.html new file mode 100644 index 0000000..63e9d34 --- /dev/null +++ b/layouts/partials/icon.html @@ -0,0 +1,4 @@ +{{- $icon := resources.Get (print "icons/" . ".svg") -}} +{{- with $icon -}} +