Skip to content

feat: add shuoshuo micro-posts - #451

Draft
Ryderwe wants to merge 2 commits into
CuteLeaf:masterfrom
Ryderwe:feat/shuoshuo
Draft

feat: add shuoshuo micro-posts#451
Ryderwe wants to merge 2 commits into
CuteLeaf:masterfrom
Ryderwe:feat/shuoshuo

Conversation

@Ryderwe

@Ryderwe Ryderwe commented Jun 27, 2026

Copy link
Copy Markdown

Summary

  • add a shuoshuo content collection and /shuoshuo/ timeline page
  • add latest shuoshuo sidebar widget, navbar entry, and site stats count
  • add generic demo shuoshuo content and shared text/shuoshuo utilities

Tests

  • pnpm exec biome check src/components/pages/shuoshuo/ShuoshuoCard.astro src/components/widget/LatestShuoshuo.astro src/pages/shuoshuo.astro src/utils/shuoshuo-utils.ts src/utils/text-utils.ts src/content.config.ts src/components/widget/SiteStats.astro src/components/layout/SideBar.astro src/config/navBarConfig.ts src/config/sidebarConfig.ts src/types/sidebarConfig.ts src/i18n/i18nKey.ts src/i18n/languages/en.ts src/i18n/languages/ja.ts src/i18n/languages/ru.ts src/i18n/languages/zh_CN.ts src/i18n/languages/zh_TW.ts
  • pnpm check
  • pnpm build

Summary by Sourcery

添加了新的说说(shuoshuo)微帖内容类型及其专用时间线页面,并将其整合进站点导航、侧边栏、统计和工具模块中。

新功能:

  • 引入用于短微帖条目的说说内容集合(shuoshuo content collection)。
  • 新增 /shuoshuo/ 时间线页面,使用卡片视图查看和展开单条说说内容。
  • 新增「最新说说」侧边栏挂件以及对应的导航栏链接,用于展示最新微帖。

功能增强:

  • 扩展站点统计功能,统计说说条目数量,并将其活动纳入最近活动日期计算中。
  • 新增通用文本工具,用于字数统计、Markdown 转纯文本、DOM id 生成以及相对时间格式化。
  • 更新侧边栏配置和类型定义,以支持新的 latestShuoshuo 挂件类型。
  • 添加示例说说内容条目,用于初始化和演示新的微帖功能。
Original summary in English

Summary by Sourcery

Add a new shuoshuo micro-post content type with a dedicated timeline page and integrate it into site navigation, sidebar, stats, and utilities.

New Features:

  • Introduce a shuoshuo content collection for short micro-post entries.
  • Add a /shuoshuo/ timeline page with cards for viewing and expanding individual shuoshuo entries.
  • Add a Latest Shuoshuo sidebar widget and corresponding navbar link to surface recent micro-posts.

Enhancements:

  • Extend site statistics to count shuoshuo entries and include their activity in the latest activity date.
  • Add shared text utilities for word counting, markdown-to-text conversion, DOM id generation, and relative time formatting.
  • Update sidebar configuration and typings to support the new latestShuoshuo widget type.
  • Add demo shuoshuo content entries to seed the new micro-post feature.

@netlify

netlify Bot commented Jun 27, 2026

Copy link
Copy Markdown

Deploy Preview for demo-firefly ready!

Name Link
🔨 Latest commit 439be4e
🔍 Latest deploy log https://app.netlify.com/projects/demo-firefly/deploys/6a3fcb14e459750008299c74
😎 Deploy Preview https://deploy-preview-451--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 Jun 27, 2026

Copy link
Copy Markdown

Reviewer's Guide

通过添加专门的内容集合、时间线页面、侧边栏挂件、共享文本/格式化工具、导航入口以及站点统计集成(同时提供示例内容和 i18n 文案),实现新的 “shuoshuo” 微博客功能。

File-Level Changes

Change Details Files
Introduce shuoshuo content collection and retrieval utilities.
  • 定义新的 shuoshuo 内容集合,使用基础的 frontmatter 模式,包括发布日期、可选的更新日期、草稿标记、作者和头像。
  • 实现 getSortedShuoshuo 辅助函数,用于加载 shuoshuo 条目,在生产环境中过滤掉草稿,并按发布日期倒序排序。
  • 添加用于开发/测试的示例 shuoshuo markdown 内容条目。
src/content.config.ts
src/utils/shuoshuo-utils.ts
src/content/shuoshuo/hello.md
src/content/shuoshuo/long-note.md
Add shuoshuo timeline page and card component with collapsible long content behavior.
  • 创建 /shuoshuo/ 页面,获取已排序的 shuoshuo 条目,使用 ShuoshuoCard 进行渲染,并为每条内容计算作者、头像、锚点 ID、相对时间和字数等元数据。
  • 实现 ShuoshuoCard 组件,用于展示作者信息、相对时间、字数以及 markdown 内容,并提供基于最大高度的可折叠区域、渐隐遮罩,以及展开/收起按钮。
  • 添加页面级脚本,为所有 shuoshuo 卡片初始化折叠行为,包括基于 ResizeObserver 的高度重计算,以及与 Astro/swup 导航事件的集成。
src/pages/shuoshuo.astro
src/components/pages/shuoshuo/ShuoshuoCard.astro
Add latest shuoshuo sidebar widget and wire it into sidebar configuration.
  • 创建 LatestShuoshuo 挂件组件,列出最新的 shuoshuo 条目,展示作者、相对时间、纯文本摘要,并链接到时间线页面上的对应锚点;挂件标题提供跳转到 /shuoshuo/ 的入口。
  • 在侧边栏配置和类型定义中注册新的 latestShuoshuo 挂件类型,并在侧边栏布局中将其映射到 LatestShuoshuo 组件。
  • 在侧边栏布局配置中默认启用最新 shuoshuo 挂件,并设置为默认吸附(sticky)。
src/components/widget/LatestShuoshuo.astro
src/config/sidebarConfig.ts
src/components/layout/SideBar.astro
src/types/sidebarConfig.ts
Expose shuoshuo in navigation and site statistics.
  • 在导航栏配置中添加 Shuoshuo 预设,将其包含到顶级导航链接中,指向 /shuoshuo/ 并使用聊天图标。
  • 扩展站点统计,加载 shuoshuo 条目,计算最近的 shuoshuo 日期,并将其纳入整体最近活动日期的计算。
  • 在站点统计中展示新的 shuoshuo 计数项,并使用合适的图标和标签。
src/config/navBarConfig.ts
src/components/widget/SiteStats.astro
Introduce shared text utilities for word-counting, plain-text extraction, DOM IDs, and relative time formatting, and use them in shuoshuo features.
  • 实现 countCjkAndEnglishWords,在 markdown 中统计 CJK 字符和英文单词数量,用于字数显示。
  • 实现 markdownToPlainText,去除 markdown 语法并返回适合挂件展示的紧凑纯文本摘要。
  • 实现 toDomId 辅助函数,为 shuoshuo 锚点链接创建安全、带前缀的片段 ID。
  • 基于 Intl.RelativeTimeFormat 实现 formatRelativeTime,使用由 siteConfig.lang 推导出的 locale,并在页面和挂件中使用它来展示更易读的时间戳。
src/utils/text-utils.ts
src/pages/shuoshuo.astro
src/components/widget/LatestShuoshuo.astro
Localize shuoshuo count label in all supported languages.
  • 新增 i18n key siteStatsShuoshuoCount
  • 为新的 shuoshuo 统计标签在英文、日文、俄文、简体中文和繁体中文语言环境中提供翻译。
src/i18n/i18nKey.ts
src/i18n/languages/en.ts
src/i18n/languages/ja.ts
src/i18n/languages/ru.ts
src/i18n/languages/zh_CN.ts
src/i18n/languages/zh_TW.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: 在 pull request 中评论 @sourcery-ai review
  • Continue discussions: 直接回复 Sourcery 的评审评论以继续讨论。
  • Generate a GitHub issue from a review comment: 在某条评审评论下回复,让 Sourcery 从该评论创建一个 issue。你也可以直接回复 @sourcery-ai issue,从该评论生成一个 issue。
  • Generate a pull request title: 在 pull request 标题任意位置写上 @sourcery-ai,即可在任意时间生成标题。你也可以在 pull request 中评论 @sourcery-ai title 以(重新)生成标题。
  • Generate a pull request summary: 在 pull request 正文任意位置写上 @sourcery-ai summary,即可在你想要的位置生成 PR 摘要。你也可以在 pull request 中评论 @sourcery-ai summary 以在任意时间(重新)生成摘要。
  • Generate reviewer's guide: 在 pull request 中评论 @sourcery-ai guide,即可在任意时间(重新)生成评审指南。
  • Resolve all Sourcery comments: 在 pull request 中评论 @sourcery-ai resolve,将所有 Sourcery 评论标记为已解决。如果你已经处理完所有评论且不想再看到它们,这会很有用。
  • Dismiss all Sourcery reviews: 在 pull request 中评论 @sourcery-ai dismiss,以撤销所有现有的 Sourcery 评审。特别适用于你想从头开始新的评审——别忘了再评论 @sourcery-ai review 以触发新一轮评审!

Customizing Your Experience

访问你的 dashboard 以:

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

Getting Help

Original review guide in English

Reviewer's Guide

Implements a new "shuoshuo" micro-post feature by adding a dedicated content collection, timeline page, sidebar widget, shared text/formatting utilities, navigation entry, and site statistics integration, along with demo content and i18n keys.

File-Level Changes

Change Details Files
Introduce shuoshuo content collection and retrieval utilities.
  • Define a new shuoshuo content collection with basic frontmatter schema including published date, optional updated, draft flag, author, and avatar.
  • Implement getSortedShuoshuo helper that loads shuoshuo entries, filters out drafts in production, and sorts them by published date descending.
  • Add demo shuoshuo markdown content entries used for development/testing.
src/content.config.ts
src/utils/shuoshuo-utils.ts
src/content/shuoshuo/hello.md
src/content/shuoshuo/long-note.md
Add shuoshuo timeline page and card component with collapsible long content behavior.
  • Create /shuoshuo/ page that fetches sorted shuoshuo entries, renders them with ShuoshuoCard, and computes per-entry metadata like author, avatar, anchor IDs, relative time, and word count.
  • Implement ShuoshuoCard component that displays author info, relative time, word count, and markdown content with a max-height based collapsible area, fade overlay, and expand/collapse button.
  • Add page-level script to initialize collapsible behavior on all shuoshuo cards, including ResizeObserver-based height recalculation and integration with Astro/swup navigation events.
src/pages/shuoshuo.astro
src/components/pages/shuoshuo/ShuoshuoCard.astro
Add latest shuoshuo sidebar widget and wire it into sidebar configuration.
  • Create LatestShuoshuo widget component that lists the most recent shuoshuo entries with author, relative time, a plain-text excerpt, and links to anchors on the timeline page, including a title action linking to /shuoshuo/.
  • Register a new latestShuoshuo widget type in sidebar config and type definitions and map it to the LatestShuoshuo component in the sidebar layout.
  • Make the latest shuoshuo widget enabled and sticky by default in the sidebar layout configuration.
src/components/widget/LatestShuoshuo.astro
src/config/sidebarConfig.ts
src/components/layout/SideBar.astro
src/types/sidebarConfig.ts
Expose shuoshuo in navigation and site statistics.
  • Add a Shuoshuo preset to the navbar config and include it in the top-level navigation links, pointing to /shuoshuo/ with a chat icon.
  • Extend site statistics to load shuoshuo entries, compute the most recent shuoshuo date, and use it when determining overall latest activity date.
  • Show a new shuoshuo count entry in site statistics with appropriate icon and label.
src/config/navBarConfig.ts
src/components/widget/SiteStats.astro
Introduce shared text utilities for word-counting, plain-text extraction, DOM IDs, and relative time formatting, and use them in shuoshuo features.
  • Implement countCjkAndEnglishWords to count CJK characters and English words in markdown for word-count display.
  • Implement markdownToPlainText to strip markdown syntax and return a compact plain-text excerpt for widget display.
  • Implement toDomId helper to create safe, prefixed fragment IDs for shuoshuo anchor links.
  • Implement formatRelativeTime based on Intl.RelativeTimeFormat, using a locale derived from siteConfig.lang, and use it in the page and widget for human-friendly timestamps.
src/utils/text-utils.ts
src/pages/shuoshuo.astro
src/components/widget/LatestShuoshuo.astro
Localize shuoshuo count label in all supported languages.
  • Add new i18n key siteStatsShuoshuoCount.
  • Provide translations for the new shuoshuo statistics label across English, Japanese, Russian, Simplified Chinese, and Traditional Chinese locales.
src/i18n/i18nKey.ts
src/i18n/languages/en.ts
src/i18n/languages/ja.ts
src/i18n/languages/ru.ts
src/i18n/languages/zh_CN.ts
src/i18n/languages/zh_TW.ts

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,我这边发现了 3 个问题,并给了一些整体层面的反馈:

  • shuoshuo.astro 里的 initShuoshuoCards 函数绑定到了多个生命周期事件,但从不清理事件监听器或 ResizeObserver,所以在多次导航后可能会不断累积处理函数;建议让它对每张卡片是幂等的,或者添加相应的销毁/清理逻辑。
  • countCjkAndEnglishWords 目前只统计 \u4e00-\u9fa5 范围内的字符,这会遗漏很多 CJK 字符(例如扩展区以及日文汉字);如果你希望支持更广泛的 CJK 内容,建议扩展正则表达式以覆盖完整的 CJK Unicode 区块。
  • getSortedShuoshuo 中的排序比较函数始终返回 -11,从不返回 0,这会导致在 published 日期相同的情况下排序不稳定;在相等的情况下返回 0 会更正确、更可预测。
给 AI Agent 的提示(Prompt)
请根据这次代码审查中的评论进行修改:

## 整体反馈
- `shuoshuo.astro` 里的 `initShuoshuoCards` 函数绑定到了多个生命周期事件,但从不清理事件监听器或 `ResizeObserver`,所以在多次导航后可能会不断累积处理函数;建议让它对每张卡片是幂等的,或者添加相应的销毁/清理逻辑。
- `countCjkAndEnglishWords` 目前只统计 `\u4e00-\u9fa5` 范围内的字符,这会遗漏很多 CJK 字符(例如扩展区以及日文汉字);如果你希望支持更广泛的 CJK 内容,建议扩展正则表达式以覆盖完整的 CJK Unicode 区块。
- `getSortedShuoshuo` 中的排序比较函数始终返回 `-1``1`,从不返回 `0`,这会导致在 `published` 日期相同的情况下排序不稳定;在相等的情况下返回 `0` 会更正确、更可预测。

## 逐条评论

### 评论 1
<location path="src/pages/shuoshuo.astro" line_range="87-93" />
<code_context>
+
+  <script>
+    function initShuoshuoCards() {
+      const cards = document.querySelectorAll<HTMLElement>("[data-shuoshuo-card]");
+      for (const card of cards) {
+        const maxHeight = Number(card.getAttribute("data-max-height") || "260");
+        const collapsible = card.querySelector<HTMLElement>("[data-collapsible]");
+        const content = card.querySelector<HTMLElement>("[data-collapsible-content]");
+        const fade = card.querySelector<HTMLElement>("[data-fade]");
+        const toggle = card.querySelector<HTMLButtonElement>("[data-toggle]");
+
+        if (!collapsible || !content || !toggle) continue;
</code_context>
<issue_to_address>
**issue (bug_risk):** 请从内联的浏览器脚本中移除 TypeScript 特有的泛型和类型标注。

由于这个内联的 `<script>` 会在浏览器中作为普通 JavaScript 运行,像 `querySelectorAll<HTMLElement>(...)``querySelector<HTMLButtonElement>(...)` 这样的 TypeScript 泛型语法是无效的,会导致运行时语法错误。去掉这些泛型即可保持现有行为不变:

```js
const cards = document.querySelectorAll("[data-shuoshuo-card]");
...
const toggle = card.querySelector("[data-toggle]");
```

如果需要类型检查,请把这段逻辑移到由打包工具处理的 `.ts` 文件中,而不要在内联脚本中使用 TS 标注。
</issue_to_address>

### 评论 2
<location path="src/pages/shuoshuo.astro" line_range="86-95" />
<code_context>
+    function initShuoshuoCards() {
</code_context>
<issue_to_address>
**suggestion (bug_risk):** 避免在每次页面加载/内容替换时重复初始化同一批卡片,并重复绑定事件监听器。

`initShuoshuoCards` 会在函数定义后立即运行一次,同时在 `astro:page-load``swup:contentReplaced` 事件触发时再次运行,每次都会为所有 `[data-shuoshuo-card]` 元素重新绑定点击事件处理器和 `ResizeObserver`。当前的 `data-initialized` 标志只控制初始展开/折叠状态,而并没有表示监听器是否已经绑定。

建议跳过已经初始化过的卡片,例如:

```js
function initShuoshuoCards() {
  const cards = document.querySelectorAll('[data-shuoshuo-card]');
  for (const card of cards) {
    if (card.hasAttribute('data-initialized')) continue;
    // existing initialization code...
    card.setAttribute('data-initialized', 'true');
  }
}
```

这样可以在保持现有行为的前提下,避免在多次导航后累积重复的监听器和观察器。

推荐实现方式:

```
    function initShuoshuoCards() {
      const cards = document.querySelectorAll<HTMLElement>("[data-shuoshuo-card]");
      for (const card of cards) {
        // 跳过已经初始化的卡片,避免重复绑定监听器/观察器
        if (card.hasAttribute("data-initialized")) continue;

        const maxHeight = Number(card.getAttribute("data-max-height") || "260");
        const collapsible = card.querySelector<HTMLElement>("[data-collapsible]");
        const content = card.querySelector<HTMLElement>("[data-collapsible-content]");
        const fade = card.querySelector<HTMLElement>("[data-fade]");
        const toggle = card.querySelector<HTMLButtonElement>("[data-toggle]");

        if (!collapsible || !content || !toggle) continue;

        // 注意:在每张卡片初始化逻辑的末尾,记得标记:
        // card.setAttribute("data-initialized", "true");

```

我目前只能看到循环体的开头。要完整实现这个行为:
1. 在完成每张卡片的所有事件监听器绑定、`ResizeObserver` 设置以及初始展开/折叠状态逻辑之后,添加 `card.setAttribute("data-initialized", "true");`2. 确保这行代码只在初始化成功时执行(也就是不要放在任何提前 `return` 的分支中),避免在卡片只初始化了一半时就被标记为已初始化。
</issue_to_address>

### 评论 3
<location path="src/components/widget/LatestShuoshuo.astro" line_range="41-43" />
<code_context>
+    slot="title-icon"
+    href={url("/shuoshuo/")}
+    class="btn-plain rounded-lg h-7 px-2 text-xs flex items-center gap-1 text-[var(--primary)] hover:opacity-80 active:opacity-60"
+    aria-label="Go to shuoshuo page"
+  >
+    <span>{isZh ? "更多" : "More"}</span>
</code_context>
<issue_to_address>
**suggestion:** 请将 `aria-label` 本地化,使其像可见文本一样随当前语言切换。

可见文本已经基于 `isZh` 在中英文之间切换,但 `aria-label` 始终是英文,这会导致使用中文区域设置的屏幕阅读器用户体验不一致。建议同样根据 `isZh` 派生 `aria-label`,例如:

```astro
aria-label={isZh ? "前往说说页面" : "Go to shuoshuo page"}
```

或者复用可见文本,并加上一句简短的说明。

```suggestion
    class="btn-plain rounded-lg h-7 px-2 text-xs flex items-center gap-1 text-[var(--primary)] hover:opacity-80 active:opacity-60"
    aria-label={isZh ? "前往说说页面" : "Go to shuoshuo page"}
  >
```
</issue_to_address>

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

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

  • The initShuoshuoCards function in shuoshuo.astro is bound to multiple lifecycle events but never cleans up listeners or ResizeObservers, so repeated navigations could accumulate handlers; consider making it idempotent per card or adding teardown logic.
  • countCjkAndEnglishWords only counts characters in the \u4e00-\u9fa5 range, which omits many CJK characters (e.g., extended ranges and Japanese Kanji); if you intend to support broader CJK content, consider expanding the regex to cover the full CJK blocks.
  • The sort comparator in getSortedShuoshuo always returns -1 or 1 and never 0, which can make sorting unstable when published dates are equal; returning 0 in the equal case would be more correct and predictable.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `initShuoshuoCards` function in `shuoshuo.astro` is bound to multiple lifecycle events but never cleans up listeners or `ResizeObserver`s, so repeated navigations could accumulate handlers; consider making it idempotent per card or adding teardown logic.
- `countCjkAndEnglishWords` only counts characters in the `\u4e00-\u9fa5` range, which omits many CJK characters (e.g., extended ranges and Japanese Kanji); if you intend to support broader CJK content, consider expanding the regex to cover the full CJK blocks.
- The sort comparator in `getSortedShuoshuo` always returns `-1` or `1` and never `0`, which can make sorting unstable when `published` dates are equal; returning `0` in the equal case would be more correct and predictable.

## Individual Comments

### Comment 1
<location path="src/pages/shuoshuo.astro" line_range="87-93" />
<code_context>
+
+  <script>
+    function initShuoshuoCards() {
+      const cards = document.querySelectorAll<HTMLElement>("[data-shuoshuo-card]");
+      for (const card of cards) {
+        const maxHeight = Number(card.getAttribute("data-max-height") || "260");
+        const collapsible = card.querySelector<HTMLElement>("[data-collapsible]");
+        const content = card.querySelector<HTMLElement>("[data-collapsible-content]");
+        const fade = card.querySelector<HTMLElement>("[data-fade]");
+        const toggle = card.querySelector<HTMLButtonElement>("[data-toggle]");
+
+        if (!collapsible || !content || !toggle) continue;
</code_context>
<issue_to_address>
**issue (bug_risk):** Remove TypeScript-specific generics and type annotations from the inline browser script.

Because this inline `<script>` runs as plain JavaScript in the browser, TypeScript-style generics like `querySelectorAll<HTMLElement>(...)` and `querySelector<HTMLButtonElement>(...)` are invalid and will cause runtime syntax errors. Drop the generics to keep the behavior the same:

```js
const cards = document.querySelectorAll("[data-shuoshuo-card]");
...
const toggle = card.querySelector("[data-toggle]");
```

If you need type checking, move this logic into a `.ts` file handled by your bundler instead of using TS annotations inline.
</issue_to_address>

### Comment 2
<location path="src/pages/shuoshuo.astro" line_range="86-95" />
<code_context>
+    function initShuoshuoCards() {
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Avoid re-initializing the same cards and attaching duplicate event listeners on each page-load/content-replacement.

`initShuoshuoCards` runs immediately, on `astro:page-load`, and on `swup:contentReplaced`, and each time it re-wires all `[data-shuoshuo-card]` elements with new click handlers / `ResizeObserver`s. The `data-initialized` flag currently only controls initial open/collapsed state, not whether listeners are already attached.

Consider skipping already-initialized cards instead:

```js
function initShuoshuoCards() {
  const cards = document.querySelectorAll('[data-shuoshuo-card]');
  for (const card of cards) {
    if (card.hasAttribute('data-initialized')) continue;
    // existing initialization code...
    card.setAttribute('data-initialized', 'true');
  }
}
```

This avoids accumulating duplicate listeners and observers across navigations while preserving existing behavior.

Suggested implementation:

```
    function initShuoshuoCards() {
      const cards = document.querySelectorAll<HTMLElement>("[data-shuoshuo-card]");
      for (const card of cards) {
        // Skip cards that have already been initialized to avoid duplicate listeners/observers
        if (card.hasAttribute("data-initialized")) continue;

        const maxHeight = Number(card.getAttribute("data-max-height") || "260");
        const collapsible = card.querySelector<HTMLElement>("[data-collapsible]");
        const content = card.querySelector<HTMLElement>("[data-collapsible-content]");
        const fade = card.querySelector<HTMLElement>("[data-fade]");
        const toggle = card.querySelector<HTMLButtonElement>("[data-toggle]");

        if (!collapsible || !content || !toggle) continue;

        // NOTE: at the end of the per-card initialization logic, remember to mark:
        // card.setAttribute("data-initialized", "true");

```

I can only see the beginning of the loop body. To fully implement the behavior:
1. After all event listeners, `ResizeObserver` setup, and initial open/collapsed state logic for each card, add `card.setAttribute("data-initialized", "true");`.
2. Ensure this line runs only when initialization succeeds (i.e., not inside any early-return branches), so that partially-initialized cards don't get marked as initialized prematurely.
</issue_to_address>

### Comment 3
<location path="src/components/widget/LatestShuoshuo.astro" line_range="41-43" />
<code_context>
+    slot="title-icon"
+    href={url("/shuoshuo/")}
+    class="btn-plain rounded-lg h-7 px-2 text-xs flex items-center gap-1 text-[var(--primary)] hover:opacity-80 active:opacity-60"
+    aria-label="Go to shuoshuo page"
+  >
+    <span>{isZh ? "更多" : "More"}</span>
</code_context>
<issue_to_address>
**suggestion:** Localize the `aria-label` to match the current language like the visible text does.

The visible text already switches on `isZh`, but the `aria-label` is always English, which is inconsistent for screen-reader users in the Chinese locale. Please derive the `aria-label` from `isZh` as well, e.g.:

```astro
aria-label={isZh ? "前往说说页面" : "Go to shuoshuo page"}
```

or reuse the visible text plus a short descriptive phrase.

```suggestion
    class="btn-plain rounded-lg h-7 px-2 text-xs flex items-center gap-1 text-[var(--primary)] hover:opacity-80 active:opacity-60"
    aria-label={isZh ? "前往说说页面" : "Go to shuoshuo page"}
  >
```
</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/pages/shuoshuo.astro
Comment on lines +87 to +93
const cards = document.querySelectorAll<HTMLElement>("[data-shuoshuo-card]");
for (const card of cards) {
const maxHeight = Number(card.getAttribute("data-max-height") || "260");
const collapsible = card.querySelector<HTMLElement>("[data-collapsible]");
const content = card.querySelector<HTMLElement>("[data-collapsible-content]");
const fade = card.querySelector<HTMLElement>("[data-fade]");
const toggle = card.querySelector<HTMLButtonElement>("[data-toggle]");

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): 请从内联的浏览器脚本中移除 TypeScript 特有的泛型和类型标注。

由于这个内联的 <script> 会在浏览器中作为普通 JavaScript 运行,像 querySelectorAll<HTMLElement>(...)querySelector<HTMLButtonElement>(...) 这样的 TypeScript 泛型语法是无效的,会导致运行时语法错误。去掉这些泛型即可保持现有行为不变:

const cards = document.querySelectorAll("[data-shuoshuo-card]");
...
const toggle = card.querySelector("[data-toggle]");

如果需要类型检查,请把这段逻辑移到由打包工具处理的 .ts 文件中,而不要在内联脚本中使用 TS 标注。

Original comment in English

issue (bug_risk): Remove TypeScript-specific generics and type annotations from the inline browser script.

Because this inline <script> runs as plain JavaScript in the browser, TypeScript-style generics like querySelectorAll<HTMLElement>(...) and querySelector<HTMLButtonElement>(...) are invalid and will cause runtime syntax errors. Drop the generics to keep the behavior the same:

const cards = document.querySelectorAll("[data-shuoshuo-card]");
...
const toggle = card.querySelector("[data-toggle]");

If you need type checking, move this logic into a .ts file handled by your bundler instead of using TS annotations inline.

Comment thread src/pages/shuoshuo.astro
Comment thread src/components/widget/LatestShuoshuo.astro
@Ryderwe

Ryderwe commented Jun 27, 2026

Copy link
Copy Markdown
Author

已根据 review 处理合理项:

  • Made initShuoshuoCards idempotent so cards that already have data-initialized are skipped, avoiding duplicate click listeners / ResizeObservers across page-load events.
  • Localized the LatestShuoshuo title action aria-label for Chinese locale.
  • Expanded CJK word counting from the basic \u4e00-\u9fa5 range to Unicode script properties for Han / Hiragana / Katakana / Hangul.
  • Changed shuoshuo sorting to use timestamp subtraction, so equal published dates return 0.

One note on the inline script TypeScript comment: I do not think that issue applies here. This is a normal Astro <script>, not is:inline, so Astro/Vite processes and transpiles it. The verified build output in dist/shuoshuo/index.html contains plain JavaScript (querySelectorAll(...), querySelector(...)) with no TypeScript generics, so it will not produce a browser syntax error. I kept the generics in source for type checking.

Validation after changes:

  • pnpm exec biome check src/pages/shuoshuo.astro src/components/widget/LatestShuoshuo.astro src/utils/shuoshuo-utils.ts src/utils/text-utils.ts
  • pnpm check
  • pnpm build

@CuteLeaf
CuteLeaf marked this pull request as draft June 28, 2026 04:39
@CuteLeaf CuteLeaf mentioned this pull request Jul 15, 2026
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.

1 participant