Skip to content

feat: 新增动态页 - #494

Merged
CuteLeaf merged 10 commits into
masterfrom
feat/dynamic-feed
Jul 15, 2026
Merged

feat: 新增动态页#494
CuteLeaf merged 10 commits into
masterfrom
feat/dynamic-feed

Conversation

@CuteLeaf

@CuteLeaf CuteLeaf commented Jul 15, 2026

Copy link
Copy Markdown
Owner

关联 #483, #451

Summary by Sourcery

添加一个可配置的「动态 / 瞬间」页面,包含列表、画廊、行内评论以及基于 API 的内容流;并将其集成到导航栏、侧边栏、统计信息和评论系统中,同时调整分页和音乐小组件的行为。

New Features:

  • 引入一个基于 markdown 的「动态 / 瞬间」内容集合,提供 API 接口和带时间戳的文件生成 CLI 命令。
  • 增加一个专门的「动态 / 瞬间」页面,支持搜索、按年份过滤、分页、图片画廊,以及每条内容可选的行内评论。
  • 在侧边栏中展示最新的「动态 / 瞬间」作为小组件,支持配置展示条目数量,并提供跳转至完整页面的导航。

Enhancements:

  • 将「动态 / 瞬间」集成到导航栏、站点页面切换以及站点统计中,并增加一项「动态数量」指标。
  • 扩展图片封装组件,支持可选的淡入效果,并优化分页按钮样式,使圆角半径和禁用状态保持一致。
  • 通过显式配置控制侧边栏音乐小组件的启用,并扩展音乐配置以控制其在侧边栏中的可见性。
  • 允许评论系统(Twikoo、Waline、Artalk、Giscus、Disqus)使用自定义 path 参数,以便复用到动态详情的评论线程。

Build:

  • 添加通过 pnpm 命令创建新的「动态 / 瞬间」条目的 package 脚本。

Documentation:

  • 在主 README 和各本地化 README 中补充「动态 / 瞬间」的配置、内容存放位置以及 CLI 创建命令的说明。
  • 更新配置文档,涵盖 dynamicConfig、增强后的 musicConfig,以及新的「动态」相关组件。
Original summary in English

Summary by Sourcery

Add a configurable dynamic/moments page with list, gallery, inline comments, and API-backed feed, integrate it into navigation, sidebar, stats, and comments, and adjust pagination and music widget behavior.

New Features:

  • Introduce a dynamic/moments content collection with markdown-based entries, API endpoint, and timestamped file generator CLI commands.
  • Add a dedicated dynamic/moments page with search, year filtering, pagination, image gallery, and optional inline comments per entry.
  • Expose latest dynamic/moments as a sidebar widget with configurable item count and navigation to the full page.

Enhancements:

  • Integrate dynamic/moments into the navbar, site page toggles, and site statistics including a dynamic count metric.
  • Extend image wrapper to support optional fade-in behavior and refine pagination button styling with consistent rounded radius and disabled states.
  • Gate the sidebar music widget with explicit config, and expand music config to control sidebar visibility.
  • Allow comments systems (Twikoo, Waline, Artalk, Giscus, Disqus) to use a custom path parameter, enabling reuse for dynamic detail threads.

Build:

  • Add package scripts for creating new dynamic/moment entries via pnpm commands.

Documentation:

  • Document dynamic/moments configuration, content location, and CLI creation commands across main and localized READMEs.
  • Update configuration docs to cover dynamicConfig, enhanced musicConfig, and new dynamic components.

@netlify

netlify Bot commented Jul 15, 2026

Copy link
Copy Markdown

Deploy Preview for demo-firefly ready!

Name Link
🔨 Latest commit f877963
🔍 Latest deploy log https://app.netlify.com/projects/demo-firefly/deploys/6a576e2267466f0008fbce52
😎 Deploy Preview https://deploy-preview-494--demo-firefly.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@sourcery-ai

sourcery-ai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Reviewer's Guide

添加了一个全新的动态/瞬间功能:专用页面、侧边栏小组件、内容集合、创建脚本、i18n 文案和 API;同时优化分页的样式/行为,并集成动态专属统计和评论路由。

动态内嵌评论的加载与主题切换序列图

sequenceDiagram
  participant User
  participant DynamicInlineComments
  participant ParentWindow as Parent_window
  participant Iframe as Comments_iframe

  User->>DynamicInlineComments: click data-comment-toggle
  DynamicInlineComments->>DynamicInlineComments: toggle()
  alt first_open
    DynamicInlineComments->>DynamicInlineComments: load(panel)
    DynamicInlineComments->>Iframe: create_iframe_src_/dynamic/comments
    DynamicInlineComments->>Iframe: attach_load_listener
  end
  Iframe-->>DynamicInlineComments: load_event
  DynamicInlineComments->>Iframe: postMessage type_dynamic-comment-theme

  Iframe->>ParentWindow: postMessage type_dynamic-comment-height
  ParentWindow->>DynamicInlineComments: message_event
  DynamicInlineComments->>DynamicInlineComments: handleMessage()
  DynamicInlineComments->>Iframe: set_height_via_style

  ParentWindow->>Iframe: message_event_type_dynamic-comment-theme
  Iframe->>Iframe: toggle_dark_class_and_dataset_theme
Loading

File-Level Changes

Change Details Files
引入完整的动态/瞬间系统:内容模型、API、页面布局、SSG/CSR 渲染流程以及创建脚本。
  • 定义新的 dynamic 内容集合,使用 published: z.date() 作为 schema。
  • 添加 dynamic-utils 辅助方法,用于排序、slug、锚点和搜索文本。
  • 实现 /api/dynamic.json,将动态 markdown 渲染为 HTML,同时提取图片元数据并规范化搜索文本。
  • 创建动态页面布局(index.astro),包含头部、搜索框、年份筛选、客户端动态 feed,以及可选的内嵌评论。
  • 构建 DynamicFeed.svelte,用于 JSON 加载、搜索/年份筛选、URL 驱动的分页、基于 hash 的深度链接,以及从模板动态构造 DOM 项。
  • 添加 DynamicItem.astro / DynamicItemTemplate.astro,用于 SSR/CSR 项渲染,组合头像、时间、内容、图集和内嵌评论。
  • 实现 DynamicGallery.astro + dynamic-gallery.ts 自定义元素,用于图片网格、查看器、缩略图和 lightbox 集成,数据源来自渲染后的内容图片。
  • 实现 DynamicInlineComments.astro + dynamic-inline-comments.ts 自定义元素以及 /dynamic/comments.astro iframe 页面,用于每个动态的嵌入式评论,并同步高度与主题。
  • 添加 dynamic.css,覆盖整页动态、卡片、图集、筛选和评论 UI。
src/content.config.ts
src/utils/dynamic-utils.ts
src/pages/api/dynamic.json.ts
src/pages/dynamic/index.astro
src/components/pages/dynamic/DynamicFeed.svelte
src/components/pages/dynamic/DynamicItem.astro
src/components/pages/dynamic/DynamicItemTemplate.astro
src/components/pages/dynamic/DynamicGallery.astro
src/components/pages/dynamic/DynamicInlineComments.astro
src/components/pages/dynamic/dynamic-gallery.ts
src/components/pages/dynamic/dynamic-inline-comments.ts
src/pages/dynamic/comments.astro
src/styles/dynamic.css
scripts/new-dynamic.js
src/content/dynamic/2026-07-15-010756.md
src/content/dynamic/2026-07-15-020613.md
src/content/dynamic/2026-07-15-021127.md
src/content/dynamic/2026-07-15-161529.md
在导航、侧边栏布局、站点配置和文档中暴露动态内容,并接好侧边栏小组件类型。
  • 添加新的 Dynamic 预设链接指向 /dynamic/,并调整顶层导航链接以包含 Dynamic,将 friends/guestbook 嵌套到新的“社交”菜单下。
  • 通过 siteConfig.pages.dynamic 启用动态页面,并在配置 README 及语言 README(CN/EN/JA/RU/zh-TW)中记录 dynamicConfig.ts
  • SideBar.astro 中注册侧边栏 dynamic 小组件类型和组件映射项,并在 sidebarConfig.ts 中为右/上和右/下位置配置动态小组件,包括每个小组件的 limit
  • 移除侧边栏音乐小组件项,改为通过 musicPlayerConfig.showInSidebar 控制侧边栏音乐显示。
  • 添加 dynamicConfig.ts 及类型导出;更新 index.tstypes/config.tstypes/dynamicConfig.ts 以支持动态页面配置。
  • 更新 components/README.md,描述与动态相关的组件。
  • 调整 musicConfig.tstypes/musicConfig.ts,添加 showInSidebar 设置,并在 config/README.md 中扩展说明。
  • 添加 CLI 脚本 pnpm new-d / pnpm new-dynamic,使用站点时区创建带时间戳的动态 markdown 文件,并在所有 README 变体中记录这些脚本。
src/config/navBarConfig.ts
src/config/siteConfig.ts
src/config/sidebarConfig.ts
src/types/sidebarConfig.ts
src/components/layout/SideBar.astro
src/components/widget/Dynamic.astro
src/config/dynamicConfig.ts
src/config/index.ts
src/types/config.ts
src/types/dynamicConfig.ts
src/config/musicConfig.ts
src/types/musicConfig.ts
src/components/widget/Music.astro
src/components/README.md
src/config/README.md
README.md
README.en.md
docs/README.ja.md
docs/README.ru.md
docs/README.zh-TW.md
package.json
scripts/new-dynamic.js
为动态/瞬间 UI 和站点统计添加 i18n key 与翻译,并在统计小组件中集成动态数量。
  • 扩展 I18nKey,增加与动态相关的 key(页面标题、描述、搜索、加载中、空状态、筛选、图集操作、返回列表、个人资料/永久链接文本)以及 siteStatsDynamicCount
  • enjakoruzh_CNzh_TW 中为所有新的动态 key 和动态数量标签提供翻译。
  • 更新 SiteStats.astro,在启用时获取 dynamic 集合,并有条件地包含一个动态数量的统计项。
  • SiteStats.astro 中进行少量标记微调(span 间距类)。
src/i18n/i18nKey.ts
src/i18n/languages/en.ts
src/i18n/languages/ja.ts
src/i18n/languages/ko.ts
src/i18n/languages/ru.ts
src/i18n/languages/zh_CN.ts
src/i18n/languages/zh_TW.ts
src/components/widget/SiteStats.astro
优化分页和图片包装的交互体验,使用统一的圆角 tokens,并改进禁用/过渡行为。
  • 更新服务端 Pagination.astro 和客户端 ClientPagination.svelte,将硬编码的 rounded-lg 替换为 rounded-(--radius-large),并调整激活/当前页样式(字重、颜色)。
  • 将上一页/下一页链接的禁用判断从严格的 === undefined 改为基于真值的检查(!page.url.prev/next),并为不存在链接添加禁用样式(opacity-50cursor-not-allowed)。
  • ImageWrapper.astro 支持可选的 fadeIn 属性(默认为 true),用于控制 LQIP 占位符和 CSS 类,并在 DynamicItemTemplate.astro 中对头像使用 fadeIn={false}
  • 确保仅在启用 fade-in 时渲染占位符 div。
src/components/common/Pagination.astro
src/components/common/ClientPagination.svelte
src/components/common/ImageWrapper.astro
src/components/pages/dynamic/DynamicItemTemplate.astro
增强评论系统集成(Twikoo、Waline、Artalk、Disqus、Giscus),支持通过 path 查询路由的动态内嵌评论,并更新端点。
  • commentConfig.ts 中将第三方 Twikoo 和 Waline 的端点更新为新 URL。
  • 修改 Twikoo、Waline、Artalk 和 Disqus 的 Astro 组件,从查询字符串中读取 path,并据此覆盖 identifier/path/pageKey/url,支持通过 /dynamic/comments/?path=... 路由到每条动态的独立评论线程。
  • 重构 Giscus.astro,在存在 path 参数时设置 mapping/term,并在内联脚本中切换为动态 import("https://esm.sh/giscus"),同时保留主题同步逻辑。
  • 实现 /pages/dynamic/comments.astro 作为可嵌入的纯评论页面,代理已配置的评论系统,并通过 postMessage 通信高度和主题。
  • 将动态内嵌评论自定义元素接好评论 iframe,并响应主题变化和高度消息。
src/config/commentConfig.ts
src/components/comment/Twikoo.astro
src/components/comment/Waline.astro
src/components/comment/Artalk.astro
src/components/comment/Disqus.astro
src/components/comment/Giscus.astro
src/components/pages/dynamic/dynamic-inline-comments.ts
src/pages/dynamic/comments.astro

Tips and commands

Interacting with Sourcery

  • 触发新评审: 在 Pull Request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的评审评论。
  • 从评审评论生成 GitHub issue: 通过回复评审评论让 Sourcery 从该评论创建 issue。你也可以在评审评论里回复 @sourcery-ai issue 来从它创建一个 issue。
  • 生成 Pull Request 标题: 在 Pull Request 标题中任意位置写入 @sourcery-ai,即可随时生成标题。你也可以在 Pull Request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 Pull Request 摘要: 在 Pull Request 正文任意位置写入 @sourcery-ai summary,即可在指定位置生成 PR 摘要。你也可以在 Pull Request 中评论 @sourcery-ai summary 来随时(重新)生成摘要。
  • 生成 Reviewer's Guide: 在 Pull Request 中评论 @sourcery-ai guide,即可随时(重新)生成评审指南。
  • 解决所有 Sourcery 评论: 在 Pull Request 中评论 @sourcery-ai resolve,将所有 Sourcery 评论标记为已解决。如果你已经处理完所有评论且不再需要看到它们,这会很有用。
  • 取消所有 Sourcery 评审: 在 Pull Request 中评论 @sourcery-ai dismiss,取消所有现有的 Sourcery 评审。尤其适用于你想从一个全新的评审开始 —— 别忘了评论 @sourcery-ai review 来触发新的评审!

Customizing Your Experience

访问你的 dashboard 以:

  • 启用或禁用评审功能,例如 Sourcery 自动生成的 Pull Request 摘要、评审指南等。
  • 更改评审语言。
  • 添加、移除或编辑自定义评审说明。
  • 调整其他评审设置。

Getting Help

Original review guide in English

Reviewer's Guide

Adds a new dynamic/moments feature with a dedicated page, sidebar widget, content collection, creation script, i18n strings and API, while refining pagination styling/behavior and integrating dynamic-specific stats and comment routing.

Sequence diagram for dynamic inline comments loading and theming

sequenceDiagram
  participant User
  participant DynamicInlineComments
  participant ParentWindow as Parent_window
  participant Iframe as Comments_iframe

  User->>DynamicInlineComments: click data-comment-toggle
  DynamicInlineComments->>DynamicInlineComments: toggle()
  alt first_open
    DynamicInlineComments->>DynamicInlineComments: load(panel)
    DynamicInlineComments->>Iframe: create_iframe_src_/dynamic/comments
    DynamicInlineComments->>Iframe: attach_load_listener
  end
  Iframe-->>DynamicInlineComments: load_event
  DynamicInlineComments->>Iframe: postMessage type_dynamic-comment-theme

  Iframe->>ParentWindow: postMessage type_dynamic-comment-height
  ParentWindow->>DynamicInlineComments: message_event
  DynamicInlineComments->>DynamicInlineComments: handleMessage()
  DynamicInlineComments->>Iframe: set_height_via_style

  ParentWindow->>Iframe: message_event_type_dynamic-comment-theme
  Iframe->>Iframe: toggle_dark_class_and_dataset_theme
Loading

File-Level Changes

Change Details Files
Introduce a full dynamic/moments system: content model, API, page layout, SSG/CSR rendering pipeline, and creation script.
  • Define a new dynamic content collection with published: z.date() schema.
  • Add dynamic-utils helpers for sorting, slugs, anchors and search text.
  • Implement /api/dynamic.json to render dynamic markdown into HTML plus extracted image metadata and normalized search text.
  • Create dynamic page layout (index.astro) with header, search box, year filter, client-side feed, and optional inline comments.
  • Build DynamicFeed.svelte for JSON loading, search/year filtering, URL-driven pagination, hash-based deep-linking, and dynamic DOM item construction from a template.
  • Add DynamicItem.astro / DynamicItemTemplate.astro for SSR/CSR item rendering that composes avatar, time, content, gallery and inline comments.
  • Implement DynamicGallery.astro + dynamic-gallery.ts custom element for image grid, viewer, thumbnails and lightbox integration sourced from rendered content images.
  • Implement DynamicInlineComments.astro + dynamic-inline-comments.ts custom element and /dynamic/comments.astro iframe page for per-item embedded comments with height and theme synchronization.
  • Add dynamic.css for the full dynamic-page, card, gallery, filter and comments UI.
src/content.config.ts
src/utils/dynamic-utils.ts
src/pages/api/dynamic.json.ts
src/pages/dynamic/index.astro
src/components/pages/dynamic/DynamicFeed.svelte
src/components/pages/dynamic/DynamicItem.astro
src/components/pages/dynamic/DynamicItemTemplate.astro
src/components/pages/dynamic/DynamicGallery.astro
src/components/pages/dynamic/DynamicInlineComments.astro
src/components/pages/dynamic/dynamic-gallery.ts
src/components/pages/dynamic/dynamic-inline-comments.ts
src/pages/dynamic/comments.astro
src/styles/dynamic.css
scripts/new-dynamic.js
src/content/dynamic/2026-07-15-010756.md
src/content/dynamic/2026-07-15-020613.md
src/content/dynamic/2026-07-15-021127.md
src/content/dynamic/2026-07-15-161529.md
Expose dynamic content in navigation, sidebar layout, site config and documentation, and wire sidebar widget type.
  • Add a new Dynamic preset link pointing to /dynamic/ and swap top-level nav links to include Dynamic and nest friends/guestbook under a new "社交" menu.
  • Enable the dynamic page via siteConfig.pages.dynamic, and document dynamicConfig.ts in config README and language READMEs (CN/EN/JA/RU/zh-TW).
  • Register sidebar dynamic widget type and component map entry in SideBar.astro, and configure dynamic widgets in sidebarConfig.ts for both right/top and right/bottom positions, including per-widget limit.
  • Remove sidebar music widget entry and instead control sidebar music via musicPlayerConfig.showInSidebar.
  • Add dynamicConfig.ts and type export; update index.ts, types/config.ts, types/dynamicConfig.ts to support dynamic page configuration.
  • Update components/README.md to describe dynamic-related components.
  • Adjust musicConfig.ts and types/musicConfig.ts to add showInSidebar setting and extend description in config/README.md.
  • Add CLI scripts pnpm new-d / pnpm new-dynamic that create timestamped dynamic markdown files using the site timezone, and document them in all README variants.
src/config/navBarConfig.ts
src/config/siteConfig.ts
src/config/sidebarConfig.ts
src/types/sidebarConfig.ts
src/components/layout/SideBar.astro
src/components/widget/Dynamic.astro
src/config/dynamicConfig.ts
src/config/index.ts
src/types/config.ts
src/types/dynamicConfig.ts
src/config/musicConfig.ts
src/types/musicConfig.ts
src/components/widget/Music.astro
src/components/README.md
src/config/README.md
README.md
README.en.md
docs/README.ja.md
docs/README.ru.md
docs/README.zh-TW.md
package.json
scripts/new-dynamic.js
Add i18n keys and translations for dynamic/moments UI and site stats, and integrate dynamic count into stats widget.
  • Extend I18nKey with dynamic-related keys (page title, description, search, loading, empty, filters, gallery actions, back-to-list, profile/permalink strings) and siteStatsDynamicCount.
  • Provide translations for all new dynamic keys and dynamic-count label across en, ja, ko, ru, zh_CN, zh_TW.
  • Update SiteStats.astro to fetch dynamic collection when enabled and include a dynamic count stat item conditionally.
  • Minor markup tweaks in SiteStats.astro (span spacing classes).
src/i18n/i18nKey.ts
src/i18n/languages/en.ts
src/i18n/languages/ja.ts
src/i18n/languages/ko.ts
src/i18n/languages/ru.ts
src/i18n/languages/zh_CN.ts
src/i18n/languages/zh_TW.ts
src/components/widget/SiteStats.astro
Refine pagination and image wrapper UX to use unified radius tokens and better disabled/transition behavior.
  • Update server-side Pagination.astro and client ClientPagination.svelte to replace hard-coded rounded-lg with rounded-(--radius-large) and adjust active/current styles (font-weight, colors).
  • Change prev/next link disabled checks from strict === undefined to truthiness (!page.url.prev/next) and add disabled styling (opacity-50, cursor-not-allowed) to class list for non-existent links.
  • Make ImageWrapper.astro support an optional fadeIn prop (default true) controlling LQIP placeholder and CSS classes, and use fadeIn={false} in DynamicItemTemplate.astro for avatar.
  • Ensure placeholder div only renders when fade-in is enabled.
src/components/common/Pagination.astro
src/components/common/ClientPagination.svelte
src/components/common/ImageWrapper.astro
src/components/pages/dynamic/DynamicItemTemplate.astro
Enhance comment system integrations (Twikoo, Waline, Artalk, Disqus, Giscus) to support dynamic inline comments via path query routing and update endpoints.
  • Update commentConfig.ts third-party endpoints for Twikoo and Waline to new URLs.
  • Modify Twikoo, Waline, Artalk and Disqus Astro components to read path from the query string and override the identifier/path/pageKey/url accordingly, enabling per-dynamic comment threads routed via /dynamic/comments/?path=....
  • Refactor Giscus.astro to set mapping/term when a path param is present, and switch to dynamic import("https://esm.sh/giscus") inside the inline script while keeping theme sync logic.
  • Implement /pages/dynamic/comments.astro as an embeddable comments-only page that proxies the configured comment system and communicates height and theme via postMessage.
  • Wire dynamic inline comments custom element to use the comments iframe and respond to theme-change and height messages.
src/config/commentConfig.ts
src/components/comment/Twikoo.astro
src/components/comment/Waline.astro
src/components/comment/Artalk.astro
src/components/comment/Disqus.astro
src/components/comment/Giscus.astro
src/components/pages/dynamic/dynamic-inline-comments.ts
src/pages/dynamic/comments.astro

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 4 个问题,并且给出了一些总体反馈:

  • DynamicFeed.svelte 中的时区偏移格式化逻辑与 date-utils 中新的 formatTimezoneOffset 辅助函数重复;建议复用这个共享工具函数,以保持格式一致并避免重复代码。
  • scripts/new-dynamic.js 通过对 siteConfig.ts 源文件使用正则来解析时区,这种做法在配置重构时非常脆弱;与其抓取 TypeScript 源码,不如从一个小的 JSON/配置模块或环境变量中读取配置,会更加稳健。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- `DynamicFeed.svelte` 中的时区偏移格式化逻辑与 `date-utils` 中新的 `formatTimezoneOffset` 辅助函数重复;建议复用这个共享工具函数,以保持格式一致并避免重复代码。
- `scripts/new-dynamic.js` 通过对 `siteConfig.ts` 源文件使用正则来解析时区,这种做法在配置重构时非常脆弱;与其抓取 TypeScript 源码,不如从一个小的 JSON/配置模块或环境变量中读取配置,会更加稳健。

## Individual Comments

### Comment 1
<location path="src/components/comment/Disqus.astro" line_range="21-24" />
<code_context>
 <div id="disqus_thread"></div>

 <script is:inline define:vars={{ shortname, identifier, url, title }}>
+  const requestedPath = new URLSearchParams(window.location.search).get("path")
+  if (requestedPath) {
+    identifier = requestedPath.replace(/^\/+|\/+$/g, "")
+    url = new URL(requestedPath, window.location.origin).href
+  }
   // @ts-ignore
</code_context>
<issue_to_address>
**issue (bug_risk):** 由于 `identifier``url` 是通过 `define:vars` 注入的常量,重新赋值会失败。

因为 `define:vars` 在浏览器中会把这些绑定生成为 `const`,所以在存在 `requestedPath` 时重新给 `identifier``url` 赋值会抛出 `Assignment to constant variable`,从而阻止 Disqus 初始化。可以改为引入单独的可变本地变量(例如 `let effectiveIdentifier = identifier; let effectiveUrl = url;`),根据 `requestedPath` 更新这些变量,并在 `disqus_config` 中使用它们,而不是修改注入的常量。
</issue_to_address>

### Comment 2
<location path="src/components/pages/dynamic/DynamicFeed.svelte" line_range="143-152" />
<code_context>
+				history.replaceState(history.state, "", permalink);
+			});
+		});
+	const time = root.querySelector<HTMLTimeElement>("[data-dynamic-time]");
+	if (time) {
+		const date = new Date(entry.published);
+		time.dateTime = date.toISOString();
+		time.textContent = new Intl.DateTimeFormat(
+			document.documentElement.lang || undefined,
+			{
+				year: "numeric",
+				month: "2-digit",
+				day: "2-digit",
+				hour: "2-digit",
+				minute: "2-digit",
+				second: "2-digit",
+			},
+		).format(date);
+		const timezoneName = new Intl.DateTimeFormat("en-US", {
+			timeZone: timezone,
+			timeZoneName: "longOffset",
+		})
+			.formatToParts(date)
+			.find((part) => part.type === "timeZoneName")?.value;
+		const timezoneLabel =
+			!timezoneName || timezoneName === "GMT"
+				? "UTC"
</code_context>
<issue_to_address>
**suggestion:** `DynamicFeed` 中的时区格式化逻辑与 `formatTimezoneOffset` 重复,且未来可能出现差异。

目前的 `timezoneLabel` 逻辑(使用 `Intl.DateTimeFormat` 和字符串处理)与 `date-utils.ts` 中的 `formatTimezoneOffset` 功能重复,这会带来风险:如果将来只更新其中一个实现(例如处理非整点偏移或本地化变更),输出就会不一致。请通过复用 `formatTimezoneOffset` 或抽取一个供二者共享的辅助函数来集中管理这部分行为。

建议实现:

```
					event.ctrlKey ||
					event.shiftKey ||
					event.altKey
				)
					return;
				event.preventDefault();
				event.stopPropagation();
				history.replaceState(history.state, "", permalink);
			});
		});
	const time = root.querySelector<HTMLTimeElement>("[data-dynamic-time]");
	if (time) {
		const date = new Date(entry.published);
		time.dateTime = date.toISOString();
		time.textContent = new Intl.DateTimeFormat(
			document.documentElement.lang || undefined,
			{
				year: "numeric",
				month: "2-digit",
				day: "2-digit",
					hour: "2-digit",
				minute: "2-digit",
				second: "2-digit",
			},
		).format(date);
		time.textContent += ` ${formatTimezoneOffset(timezone, date)}`;
	}

	const content = root.querySelector<HTMLElement>("[data-dynamic-content]");
	if (content) {
		content.id = `${anchorId}-content`;

```

1.`DynamicFeed.svelte` 顶部添加对 `formatTimezoneOffset` 的导入,并使用指向 `date-utils.ts` 的正确相对路径,例如:
   ```ts
   import { formatTimezoneOffset } from "../../../utils/date-utils";
   ```
   根据你实际的项目结构调整路径。
2. 确认 `formatTimezoneOffset` 的函数签名与这里的用法一致。如果它目前只接受 `Date` 或只接受时区字符串(比如仅接受 `Date` 或仅接受时区 ID),请相应地调整调用 `formatTimezoneOffset(timezone, date)`,或者扩展 `date-utils.ts`,提供一个既能使用时区标识又能使用 `Date` 实例进行格式化的辅助函数。
3. 如果 `formatTimezoneOffset` 目前在其它地方的使用不带 `Date` 参数,可以考虑统一它的 API(例如 `formatTimezoneOffset(date: Date, timeZone?: string)`),并更新现有调用点,以保证整个应用中的行为一致。
</issue_to_address>

### Comment 3
<location path="src/config/sidebarConfig.ts" line_range="105-114" />
<code_context>

 	// 右侧边栏组件配置列表
 	rightComponents: [
+		{
+			// 组件类型:最新动态组件
+			type: "dynamic",
+			// 是否启用该组件
+			enable: true,
+			// 组件位置
+			position: "top",
+			// 是否在文章详情页显示
+			showOnPostPage: true,
+			// 组件专属配置
+			specificConfig: {
+				dynamic: {
+					// 显示的最新动态数量
+					limit: 2,
+				},
+			},
+		},
 		{
 			// 组件类型:站点统计组件
</code_context>
<issue_to_address>
**question (bug_risk):** `rightComponents` 中的 `dynamic` 小部件被配置了两次,可能导致重复的界面显示。

`rightComponents` 中存在两个 `dynamic` 条目,而且配置几乎完全相同(`enable: true``showOnPostPage: true``limit: 2`)。除非布局引擎对它们有特殊处理,否则这很可能会让该小部件被渲染两次。如果你并不希望重复显示,请移除其中一个条目,并根据需要调整剩余小部件的位置。
</issue_to_address>

### Comment 4
<location path="src/content/dynamic/2026-07-15-010756.md" line_range="7-13" />
<code_context>
+
+今天天气真不错,流萤真可爱。
+
+![图片一](https://s41.ax1x.com/2026/05/13/peXsfit.webp)
+![图片一](https://s41.ax1x.com/2026/05/13/peXs2dA.webp)
+![图片一](https://s41.ax1x.com/2026/05/13/peXshJP.webp)
+![图片一](https://s41.ax1x.com/2026/05/13/peXsRII.webp)
+![图片一](https://s41.ax1x.com/2026/05/13/peXyWm4.jpg)
+![图片一](https://s41.ax1x.com/2026/05/13/peXyf0J.jpg)
+![图片一](https://s41.ax1x.com/2026/05/13/peXyh79.jpg)
</code_context>
<issue_to_address>
**nitpick:** 建议为每张图片使用更具描述性且彼此不同的替代文本,而不是重复使用 `图片一`。

目前所有图片都使用相同的替代文本。为了提升无障碍体验并支持屏幕阅读器,请为每张图片提供独立且具有描述性的替代文本,以体现它们各自的具体内容。
</issue_to_address>

Sourcery 对开源项目免费 —— 如果你觉得我们的代码审查有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的代码审查。
Original comment in English

Hey - I've found 4 issues, and left some high level feedback:

  • The timezone offset formatting logic in DynamicFeed.svelte duplicates the new formatTimezoneOffset helper in date-utils; consider reusing the shared utility to keep formatting consistent and avoid duplicated code.
  • scripts/new-dynamic.js parses timezone from siteConfig.ts using a regex on the source file, which is brittle to config refactors; it would be more robust to read from a small JSON/config module or environment variable instead of scraping TypeScript.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The timezone offset formatting logic in DynamicFeed.svelte duplicates the new formatTimezoneOffset helper in date-utils; consider reusing the shared utility to keep formatting consistent and avoid duplicated code.
- scripts/new-dynamic.js parses timezone from siteConfig.ts using a regex on the source file, which is brittle to config refactors; it would be more robust to read from a small JSON/config module or environment variable instead of scraping TypeScript.

## Individual Comments

### Comment 1
<location path="src/components/comment/Disqus.astro" line_range="21-24" />
<code_context>
 <div id="disqus_thread"></div>

 <script is:inline define:vars={{ shortname, identifier, url, title }}>
+  const requestedPath = new URLSearchParams(window.location.search).get("path")
+  if (requestedPath) {
+    identifier = requestedPath.replace(/^\/+|\/+$/g, "")
+    url = new URL(requestedPath, window.location.origin).href
+  }
   // @ts-ignore
</code_context>
<issue_to_address>
**issue (bug_risk):** Reassigning `identifier` and `url` will fail because they are constants injected via `define:vars`.

Because `define:vars` emits these bindings as `const` in the browser, reassigning `identifier` and `url` when `requestedPath` is present will throw `Assignment to constant variable` and prevent Disqus from initializing. Instead, introduce separate mutable locals (e.g. `let effectiveIdentifier = identifier; let effectiveUrl = url;`), update those based on `requestedPath`, and use them inside `disqus_config` rather than modifying the injected constants.
</issue_to_address>

### Comment 2
<location path="src/components/pages/dynamic/DynamicFeed.svelte" line_range="143-152" />
<code_context>
+				history.replaceState(history.state, "", permalink);
+			});
+		});
+	const time = root.querySelector<HTMLTimeElement>("[data-dynamic-time]");
+	if (time) {
+		const date = new Date(entry.published);
+		time.dateTime = date.toISOString();
+		time.textContent = new Intl.DateTimeFormat(
+			document.documentElement.lang || undefined,
+			{
+				year: "numeric",
+				month: "2-digit",
+				day: "2-digit",
+				hour: "2-digit",
+				minute: "2-digit",
+				second: "2-digit",
+			},
+		).format(date);
+		const timezoneName = new Intl.DateTimeFormat("en-US", {
+			timeZone: timezone,
+			timeZoneName: "longOffset",
+		})
+			.formatToParts(date)
+			.find((part) => part.type === "timeZoneName")?.value;
+		const timezoneLabel =
+			!timezoneName || timezoneName === "GMT"
+				? "UTC"
</code_context>
<issue_to_address>
**suggestion:** Timezone formatting logic in `DynamicFeed` duplicates `formatTimezoneOffset` and could diverge over time.

The `timezoneLabel` logic (using `Intl.DateTimeFormat` and string manipulation) duplicates `formatTimezoneOffset` in `date-utils.ts`, which risks inconsistent output if only one is updated (e.g., for non-`:00` offsets or locale changes). Please centralize this behavior by reusing `formatTimezoneOffset` or extracting a shared helper used by both.

Suggested implementation:

```
					event.ctrlKey ||
					event.shiftKey ||
					event.altKey
				)
					return;
				event.preventDefault();
				event.stopPropagation();
				history.replaceState(history.state, "", permalink);
			});
		});
	const time = root.querySelector<HTMLTimeElement>("[data-dynamic-time]");
	if (time) {
		const date = new Date(entry.published);
		time.dateTime = date.toISOString();
		time.textContent = new Intl.DateTimeFormat(
			document.documentElement.lang || undefined,
			{
				year: "numeric",
				month: "2-digit",
				day: "2-digit",
				hour: "2-digit",
				minute: "2-digit",
				second: "2-digit",
			},
		).format(date);
		time.textContent += ` ${formatTimezoneOffset(timezone, date)}`;
	}

	const content = root.querySelector<HTMLElement>("[data-dynamic-content]");
	if (content) {
		content.id = `${anchorId}-content`;

```

1. Add an import for `formatTimezoneOffset` at the top of `DynamicFeed.svelte`, using the correct relative path to `date-utils.ts`, for example:
   ```ts
   import { formatTimezoneOffset } from "../../../utils/date-utils";
   ```
   Adjust the path to match your actual project structure.
2. Ensure the `formatTimezoneOffset` signature matches the usage here. If it currently accepts a `Date` or timezone string differently (e.g., only a `Date` or only a timezone ID), update the call `formatTimezoneOffset(timezone, date)` accordingly, or extend `date-utils.ts` to expose a helper that can format using both the timezone identifier and the `Date` instance.
3. If `formatTimezoneOffset` is currently used elsewhere without a `Date` argument, consider standardizing its API (e.g., `formatTimezoneOffset(date: Date, timeZone?: string)`) and updating existing call sites to keep behavior consistent across the app.
</issue_to_address>

### Comment 3
<location path="src/config/sidebarConfig.ts" line_range="105-114" />
<code_context>

 	// 右侧边栏组件配置列表
 	rightComponents: [
+		{
+			// 组件类型:最新动态组件
+			type: "dynamic",
+			// 是否启用该组件
+			enable: true,
+			// 组件位置
+			position: "top",
+			// 是否在文章详情页显示
+			showOnPostPage: true,
+			// 组件专属配置
+			specificConfig: {
+				dynamic: {
+					// 显示的最新动态数量
+					limit: 2,
+				},
+			},
+		},
 		{
 			// 组件类型:站点统计组件
</code_context>
<issue_to_address>
**question (bug_risk):** The `dynamic` widget is configured twice in `rightComponents`, which may lead to duplicate UI.

There are two `dynamic` entries in `rightComponents` with effectively the same configuration (`enable: true`, `showOnPostPage: true`, `limit: 2`). Unless the layout engine treats them differently, this will likely render the widget twice. If you don’t intend duplication, remove one entry and adjust the remaining widget’s position as needed.
</issue_to_address>

### Comment 4
<location path="src/content/dynamic/2026-07-15-010756.md" line_range="7-13" />
<code_context>
+
+今天天气真不错,流萤真可爱。
+
+![图片一](https://s41.ax1x.com/2026/05/13/peXsfit.webp)
+![图片一](https://s41.ax1x.com/2026/05/13/peXs2dA.webp)
+![图片一](https://s41.ax1x.com/2026/05/13/peXshJP.webp)
+![图片一](https://s41.ax1x.com/2026/05/13/peXsRII.webp)
+![图片一](https://s41.ax1x.com/2026/05/13/peXyWm4.jpg)
+![图片一](https://s41.ax1x.com/2026/05/13/peXyf0J.jpg)
+![图片一](https://s41.ax1x.com/2026/05/13/peXyh79.jpg)
</code_context>
<issue_to_address>
**nitpick:** Consider using more descriptive and/or distinct alt text for each image instead of repeating `图片一`.

All images currently use the same alt text. To improve accessibility and support screen readers, please give each image a distinct, descriptive alt text that reflects its specific content.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/components/comment/Disqus.astro Outdated
Comment on lines +21 to +24
const requestedPath = new URLSearchParams(window.location.search).get("path")
if (requestedPath) {
identifier = requestedPath.replace(/^\/+|\/+$/g, "")
url = new URL(requestedPath, window.location.origin).href

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): 由于 identifierurl 是通过 define:vars 注入的常量,重新赋值会失败。

因为 define:vars 在浏览器中会把这些绑定生成为 const,所以在存在 requestedPath 时重新给 identifierurl 赋值会抛出 Assignment to constant variable,从而阻止 Disqus 初始化。可以改为引入单独的可变本地变量(例如 let effectiveIdentifier = identifier; let effectiveUrl = url;),根据 requestedPath 更新这些变量,并在 disqus_config 中使用它们,而不是修改注入的常量。

Original comment in English

issue (bug_risk): Reassigning identifier and url will fail because they are constants injected via define:vars.

Because define:vars emits these bindings as const in the browser, reassigning identifier and url when requestedPath is present will throw Assignment to constant variable and prevent Disqus from initializing. Instead, introduce separate mutable locals (e.g. let effectiveIdentifier = identifier; let effectiveUrl = url;), update those based on requestedPath, and use them inside disqus_config rather than modifying the injected constants.

Comment on lines +143 to +152
const time = root.querySelector<HTMLTimeElement>("[data-dynamic-time]");
if (time) {
const date = new Date(entry.published);
time.dateTime = date.toISOString();
time.textContent = new Intl.DateTimeFormat(
document.documentElement.lang || undefined,
{
year: "numeric",
month: "2-digit",
day: "2-digit",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: DynamicFeed 中的时区格式化逻辑与 formatTimezoneOffset 重复,且未来可能出现差异。

目前的 timezoneLabel 逻辑(使用 Intl.DateTimeFormat 和字符串处理)与 date-utils.ts 中的 formatTimezoneOffset 功能重复,这会带来风险:如果将来只更新其中一个实现(例如处理非整点偏移或本地化变更),输出就会不一致。请通过复用 formatTimezoneOffset 或抽取一个供二者共享的辅助函数来集中管理这部分行为。

建议实现:

					event.ctrlKey ||
					event.shiftKey ||
					event.altKey
				)
					return;
				event.preventDefault();
				event.stopPropagation();
				history.replaceState(history.state, "", permalink);
			});
		});
	const time = root.querySelector<HTMLTimeElement>("[data-dynamic-time]");
	if (time) {
		const date = new Date(entry.published);
		time.dateTime = date.toISOString();
		time.textContent = new Intl.DateTimeFormat(
			document.documentElement.lang || undefined,
			{
				year: "numeric",
				month: "2-digit",
				day: "2-digit",
				hour: "2-digit",
				minute: "2-digit",
				second: "2-digit",
			},
		).format(date);
		time.textContent += ` ${formatTimezoneOffset(timezone, date)}`;
	}

	const content = root.querySelector<HTMLElement>("[data-dynamic-content]");
	if (content) {
		content.id = `${anchorId}-content`;

  1. DynamicFeed.svelte 顶部添加对 formatTimezoneOffset 的导入,并使用指向 date-utils.ts 的正确相对路径,例如:
    import { formatTimezoneOffset } from "../../../utils/date-utils";
    根据你实际的项目结构调整路径。
  2. 确认 formatTimezoneOffset 的函数签名与这里的用法一致。如果它目前只接受 Date 或只接受时区字符串(比如仅接受 Date 或仅接受时区 ID),请相应地调整调用 formatTimezoneOffset(timezone, date),或者扩展 date-utils.ts,提供一个既能使用时区标识又能使用 Date 实例进行格式化的辅助函数。
  3. 如果 formatTimezoneOffset 目前在其它地方的使用不带 Date 参数,可以考虑统一它的 API(例如 formatTimezoneOffset(date: Date, timeZone?: string)),并更新现有调用点,以保证整个应用中的行为一致。
Original comment in English

suggestion: Timezone formatting logic in DynamicFeed duplicates formatTimezoneOffset and could diverge over time.

The timezoneLabel logic (using Intl.DateTimeFormat and string manipulation) duplicates formatTimezoneOffset in date-utils.ts, which risks inconsistent output if only one is updated (e.g., for non-:00 offsets or locale changes). Please centralize this behavior by reusing formatTimezoneOffset or extracting a shared helper used by both.

Suggested implementation:

					event.ctrlKey ||
					event.shiftKey ||
					event.altKey
				)
					return;
				event.preventDefault();
				event.stopPropagation();
				history.replaceState(history.state, "", permalink);
			});
		});
	const time = root.querySelector<HTMLTimeElement>("[data-dynamic-time]");
	if (time) {
		const date = new Date(entry.published);
		time.dateTime = date.toISOString();
		time.textContent = new Intl.DateTimeFormat(
			document.documentElement.lang || undefined,
			{
				year: "numeric",
				month: "2-digit",
				day: "2-digit",
				hour: "2-digit",
				minute: "2-digit",
				second: "2-digit",
			},
		).format(date);
		time.textContent += ` ${formatTimezoneOffset(timezone, date)}`;
	}

	const content = root.querySelector<HTMLElement>("[data-dynamic-content]");
	if (content) {
		content.id = `${anchorId}-content`;

  1. Add an import for formatTimezoneOffset at the top of DynamicFeed.svelte, using the correct relative path to date-utils.ts, for example:
    import { formatTimezoneOffset } from "../../../utils/date-utils";
    Adjust the path to match your actual project structure.
  2. Ensure the formatTimezoneOffset signature matches the usage here. If it currently accepts a Date or timezone string differently (e.g., only a Date or only a timezone ID), update the call formatTimezoneOffset(timezone, date) accordingly, or extend date-utils.ts to expose a helper that can format using both the timezone identifier and the Date instance.
  3. If formatTimezoneOffset is currently used elsewhere without a Date argument, consider standardizing its API (e.g., formatTimezoneOffset(date: Date, timeZone?: string)) and updating existing call sites to keep behavior consistent across the app.

Comment on lines +105 to +114
{
// 组件类型:最新动态组件
type: "dynamic",
// 是否启用该组件
enable: true,
// 组件位置
position: "top",
// 是否在文章详情页显示
showOnPostPage: true,
// 组件专属配置

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question (bug_risk): rightComponents 中的 dynamic 小部件被配置了两次,可能导致重复的界面显示。

rightComponents 中存在两个 dynamic 条目,而且配置几乎完全相同(enable: trueshowOnPostPage: truelimit: 2)。除非布局引擎对它们有特殊处理,否则这很可能会让该小部件被渲染两次。如果你并不希望重复显示,请移除其中一个条目,并根据需要调整剩余小部件的位置。

Original comment in English

question (bug_risk): The dynamic widget is configured twice in rightComponents, which may lead to duplicate UI.

There are two dynamic entries in rightComponents with effectively the same configuration (enable: true, showOnPostPage: true, limit: 2). Unless the layout engine treats them differently, this will likely render the widget twice. If you don’t intend duplication, remove one entry and adjust the remaining widget’s position as needed.

Comment on lines +7 to +13
![图片一](https://s41.ax1x.com/2026/05/13/peXsfit.webp)
![图片一](https://s41.ax1x.com/2026/05/13/peXs2dA.webp)
![图片一](https://s41.ax1x.com/2026/05/13/peXshJP.webp)
![图片一](https://s41.ax1x.com/2026/05/13/peXsRII.webp)
![图片一](https://s41.ax1x.com/2026/05/13/peXyWm4.jpg)
![图片一](https://s41.ax1x.com/2026/05/13/peXyf0J.jpg)
![图片一](https://s41.ax1x.com/2026/05/13/peXyh79.jpg)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: 建议为每张图片使用更具描述性且彼此不同的替代文本,而不是重复使用 图片一

目前所有图片都使用相同的替代文本。为了提升无障碍体验并支持屏幕阅读器,请为每张图片提供独立且具有描述性的替代文本,以体现它们各自的具体内容。

Original comment in English

nitpick: Consider using more descriptive and/or distinct alt text for each image instead of repeating 图片一.

All images currently use the same alt text. To improve accessibility and support screen readers, please give each image a distinct, descriptive alt text that reflects its specific content.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 15, 2026

Copy link
Copy Markdown

Deploying firefly with  Cloudflare Pages  Cloudflare Pages

Latest commit: f877963
Status:⚡️  Build in progress...

View logs

@CuteLeaf

Copy link
Copy Markdown
Owner Author

@sourcery-ai review

@sourcery-ai

sourcery-ai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Sorry @CuteLeaf, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@CuteLeaf
CuteLeaf merged commit c4dbeef into master Jul 15, 2026
9 of 11 checks passed
@CuteLeaf CuteLeaf linked an issue Jul 15, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: 希望增加类似于qq空间的板块

1 participant