Skip to content

feat: support multi sources#311

Merged
PaiJi merged 2 commits into
mainfrom
feat/support-multi-sources
Jun 14, 2026
Merged

feat: support multi sources#311
PaiJi merged 2 commits into
mainfrom
feat/support-multi-sources

Conversation

@PaiJi

@PaiJi PaiJi commented Jun 14, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

发布说明

  • 新特性

    • 增强事件来源功能,现支持多个来源的下拉菜单展示
    • 新增复制事件来源的功能与成功提示
  • Chores

    • 更新核心依赖,包括Next.js、React等库版本升级
    • 更新Yarn包管理器版本至最新
    • 调整项目配置设置

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

EventSourceButton 从单链接重构为支持多来源的 Headless UI 下拉菜单组件,新增 URL 校验、剪贴板复制及 sendTrack 追踪,同步扩展四种语言的 i18n 文案,调整事件页面 z-index 样式,并升级 Yarn 至 4.16.0 及多项依赖。

Changes

EventSourceButton 多来源重构

Layer / File(s) Summary
i18n 文案新增与更新
public/locales/en/common.json, public/locales/ru/common.json, public/locales/zh-Hans/common.json, public/locales/zh-Hant/common.json
四种语言均将 event.goToSource 改为带 {{name}} 占位符形式,并新增 event.sourceevent.copySourceevent.sourceListevent.sourceCopySuccess 键。
EventSourceButton 核心重构
src/components/event/EventSourceButton/index.tsx
新增 getUrl URL 校验助手;引入剪贴板复制流程与 toast 提示;sendTrack 统一追踪链接点击与复制操作;单来源渲染共享 action 渲染器(<a><button>),多来源渲染 Headless UI Menu 下拉列表(含可选描述文本)。
事件页面 z-index 与样式调整
src/components/event/EventMapCard.tsx, src/pages/[organization]/[slug].tsx
EventMapCard 外层容器新增 z-0;事件详情页主容器新增 relative z-20 并移除 overflow-hidden,左侧列新增 overflow-hidden 及断点圆角样式,以配合下拉菜单层叠展示。

依赖与 Yarn 配置升级

Layer / File(s) Summary
Yarn 配置新增项
.yarnrc.yml
新增 approvedGitRepositories(通配允许)、enableScripts: truenpmMinimalAgeGate: 1440 三项配置,其余配置项顺序重排。
依赖版本升级
package.json
dependencies 升级 Next/React 生态、i18n、Sentry、axios 等;devDependencies 升级 bundle-analyzer、Node/React 类型及 prettier;packageManager 更新为 yarn@4.16.0

Sequence Diagram(s)

sequenceDiagram
  participant 用户
  participant EventSourceButton
  participant getUrl
  participant navigator.clipboard
  participant sendTrack
  participant toast提示

  用户->>EventSourceButton: 点击来源入口
  alt 多来源
    EventSourceButton->>用户: 展开 Menu 下拉列表
    用户->>EventSourceButton: 选择某一来源
  end
  EventSourceButton->>getUrl: 校验 URL 协议(http/https)
  alt 有效链接
    getUrl-->>EventSourceButton: 返回 URL 对象
    EventSourceButton->>sendTrack: 记录链接点击事件
    EventSourceButton->>用户: 新标签页打开链接
  else 非 http/https 链接
    getUrl-->>EventSourceButton: 返回 null
    EventSourceButton->>navigator.clipboard: writeText(url)
    navigator.clipboard-->>EventSourceButton: 复制成功
    EventSourceButton->>sendTrack: 记录复制事件
    EventSourceButton->>toast提示: 显示本地化复制成功消息
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • FurryConsChina/website#307:同样对 package.json 进行了多项依赖版本升级及 packageManager 更新,与本 PR 的依赖升级部分直接重叠。
  • FurryConsChina/website#302:同样升级了 package.json 中的 Next/React 相关依赖版本范围,代码层面高度相关。
  • FurryConsChina/website#284:同样修改了 public/locales/*/common.json 中的 event.goToSource 等事件来源相关 i18n 键,与本 PR 的多语言文案扩展直接相关。

Poem

🐰 小兔子跳了一跳,
来源变成了下拉菜单!
一个链接还是好多个,
复制粘贴也不怕了~
{{name}} 占位符闪闪发光,
依赖升级,世界更美好! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title "feat: support multi sources" directly and clearly describes the main objective of the changeset, which is to add support for multiple event sources.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/support-multi-sources

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request updates various dependencies, upgrades Yarn, and refactors the EventSourceButton component to support multiple event sources via a dropdown menu using Headless UI. It also adds translation keys for source copying and navigation, and adjusts styling for EventMapCard and the event detail page. Feedback on the changes suggests importing and using ReactNode directly from 'react' to avoid relying on the global React namespace, and adding safety checks and error handling when using the navigator.clipboard API.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines 1 to +5
import { sendTrack } from "@/utils/track";
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
import { useTranslation } from "next-i18next/pages";
import toast from "react-hot-toast";
import { IoChevronDown } from "react-icons/io5";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Import ReactNode from 'react' to avoid relying on global React namespace types, which can cause compilation issues depending on the TypeScript configuration.

Suggested change
import { sendTrack } from "@/utils/track";
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
import { useTranslation } from "next-i18next/pages";
import toast from "react-hot-toast";
import { IoChevronDown } from "react-icons/io5";
import { sendTrack } from "@/utils/track";
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
import { useTranslation } from "next-i18next/pages";
import { ReactNode } from "react";
import toast from "react-hot-toast";
import { IoChevronDown } from "react-icons/io5";

track(source);
};

const renderSourceAction = (source: EventSource, className: string, children: React.ReactNode) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Use the imported ReactNode type instead of React.ReactNode since React is not imported in this file.

Suggested change
const renderSourceAction = (source: EventSource, className: string, children: React.ReactNode) => {
const renderSourceAction = (source: EventSource, className: string, children: ReactNode) => {

Comment on lines +42 to +47
const copySource = (source: EventSource) => {
navigator.clipboard
.writeText(source.url)
.then(() => toast.success(t("event.sourceCopySuccess", { name: source.name || source.url })));
track(source);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The navigator.clipboard API might not be available in all environments (e.g., non-secure HTTP contexts or older browsers) and can reject if permission is denied. To prevent runtime errors, we should check if navigator.clipboard is available and handle potential promise rejections.

Suggested change
const copySource = (source: EventSource) => {
navigator.clipboard
.writeText(source.url)
.then(() => toast.success(t("event.sourceCopySuccess", { name: source.name || source.url })));
track(source);
};
const copySource = (source: EventSource) => {
if (!source?.url) {
toast.error("No URL available to copy");
return;
}
if (typeof navigator !== "undefined" && navigator.clipboard) {
navigator.clipboard
.writeText(source.url)
.then(() => toast.success(t("event.sourceCopySuccess", { name: source.name || source.url })))
.catch((err) => {
console.error("Failed to copy text: ", err);
toast.error("Failed to copy to clipboard");
});
} else {
console.warn("Clipboard API not available");
toast.error("Clipboard not supported in this browser");
}
track(source);
};

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/components/event/EventSourceButton/index.tsx (1)

94-95: 💤 Low value

可以改进 key 的生成方式。

当前使用 ${source.url}-${index} 作为 key,如果同一 URL 出现多次,index 作为区分符是可以接受的,但更推荐使用稳定的唯一标识符(如果可用)。不过在当前场景下,由于列表是静态的且不会重新排序,这个实现是可接受的。

♻️ 可选优化建议

如果数据源包含唯一 ID,可以使用:

-<MenuItem key={`${source.url}-${index}`}>
+<MenuItem key={source.id || `${source.url}-${index}`}>

或者,如果能确保 URL 唯一,直接使用 URL:

-<MenuItem key={`${source.url}-${index}`}>
+<MenuItem key={source.url}>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/event/EventSourceButton/index.tsx` around lines 94 - 95, In
the MenuItem component rendered within sources.map(), consider improving the key
generation from the current `${source.url}-${index}` approach. If the source
object contains a stable unique identifier property (such as an id field), use
that as the key instead. Alternatively, if URLs are guaranteed to be unique
across all sources, use the URL directly as the key. The current implementation
combining URL and index is acceptable for static, non-reordering lists, but
these alternatives would be more robust if applicable to your data structure.
src/pages/[organization]/[slug].tsx (1)

36-36: 💤 Low value

flex 类名冗余。

由于 flex-col 已经包含了 display: flex,显式添加 flex 是多余的。不过这不影响功能,仅是代码简洁性问题。

♻️ 可选的简化建议
-      <div className={clsx("relative z-20 flex border bg-white rounded-xl min-h-[500px]", "lg:flex-row flex-col")}>
+      <div className={clsx("relative z-20 border bg-white rounded-xl min-h-[500px]", "lg:flex-row flex-col")}>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/`[organization]/[slug].tsx at line 36, The `flex` class name is
redundant in the clsx call for this div element. Since `flex-col` already
includes `display: flex`, remove the explicit `flex` class from the first
argument to clsx (in the string "relative z-20 flex border bg-white rounded-xl
min-h-[500px]"). Keep all other classes including the second argument with
`lg:flex-row flex-col` which will properly set the flex display and direction.
.yarnrc.yml (1)

1-2: 考虑更明确地配置 Git 仓库白名单。

配置 approvedGitRepositories: ["**"] 允许从任意 Git 仓库安装依赖。虽然当前项目未使用任何 Git 依赖,但若未来需要添加 Git 源依赖,建议提前更新此配置以显式列出受信任的仓库模式(如 github.com/*),而非使用通配符。这有助于防止意外引入来自不受信任来源的代码。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.yarnrc.yml around lines 1 - 2, The approvedGitRepositories configuration in
.yarnrc.yml is currently set to a wildcard pattern ["**"] which permits
installing dependencies from any Git repository, creating a security risk.
Replace the wildcard pattern with explicit, trusted repository patterns that
align with your organization's security policy, such as ["github.com/*"] or
specific repository URLs, to restrict Git dependency sources to only approved
repositories. This ensures that future Git-based dependencies cannot be
accidentally pulled from untrusted sources.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.yarnrc.yml:
- Line 8: The `enableScripts: true` configuration in the Yarn configuration file
is unnecessary because the project does not declare any installation scripts
(install, postinstall, or preinstall hooks) in its dependencies. Following the
principle of least privilege, either remove the `enableScripts: true` line
entirely from the configuration or change it to `enableScripts: false` to
disable script execution by default. This reduces the security surface and makes
the configuration explicit about the project's actual needs.

In `@package.json`:
- Line 17: The project is using Next.js 16.2.9 which requires completing the
migration from Pages Router to App Router structure. Run the official Next.js
codemod with the command `npx `@next/codemod`@latest upgrade` to automate the
migration process. Additionally, ensure all async request APIs such as
cookies(), headers(), draftMode(), params, and searchParams are properly awaited
throughout the codebase, replace all getServerSideProps and getStaticProps
implementations with async Server Components using the 'use cache' directive,
and update any generateImageMetadata or sitemap functions to properly handle
Promise-based params and id parameters. These changes must be completed across
all affected routes and components to ensure compatibility with Next.js 16.x
requirements.

In `@src/components/event/EventSourceButton/index.tsx`:
- Around line 42-47: The copySource function in EventSourceButton/index.tsx is
missing error handling for the navigator.clipboard.writeText promise chain. Add
a .catch() handler to the clipboard operation to gracefully handle failures when
the clipboard API is unavailable or permission is denied. In the catch handler,
show a failure toast message using t("event.sourceCopyFailed") to inform the
user that the copy operation failed. Additionally, add the corresponding i18n
translation key event.sourceCopyFailed to your i18n translation files to support
the error message.

---

Nitpick comments:
In @.yarnrc.yml:
- Around line 1-2: The approvedGitRepositories configuration in .yarnrc.yml is
currently set to a wildcard pattern ["**"] which permits installing dependencies
from any Git repository, creating a security risk. Replace the wildcard pattern
with explicit, trusted repository patterns that align with your organization's
security policy, such as ["github.com/*"] or specific repository URLs, to
restrict Git dependency sources to only approved repositories. This ensures that
future Git-based dependencies cannot be accidentally pulled from untrusted
sources.

In `@src/components/event/EventSourceButton/index.tsx`:
- Around line 94-95: In the MenuItem component rendered within sources.map(),
consider improving the key generation from the current `${source.url}-${index}`
approach. If the source object contains a stable unique identifier property
(such as an id field), use that as the key instead. Alternatively, if URLs are
guaranteed to be unique across all sources, use the URL directly as the key. The
current implementation combining URL and index is acceptable for static,
non-reordering lists, but these alternatives would be more robust if applicable
to your data structure.

In `@src/pages/`[organization]/[slug].tsx:
- Line 36: The `flex` class name is redundant in the clsx call for this div
element. Since `flex-col` already includes `display: flex`, remove the explicit
`flex` class from the first argument to clsx (in the string "relative z-20 flex
border bg-white rounded-xl min-h-[500px]"). Keep all other classes including the
second argument with `lg:flex-row flex-col` which will properly set the flex
display and direction.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c3bcf99a-5e2b-41f4-8aa0-2fe413d38d39

📥 Commits

Reviewing files that changed from the base of the PR and between 5605031 and b9541ae.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (9)
  • .yarnrc.yml
  • package.json
  • public/locales/en/common.json
  • public/locales/ru/common.json
  • public/locales/zh-Hans/common.json
  • public/locales/zh-Hant/common.json
  • src/components/event/EventMapCard.tsx
  • src/components/event/EventSourceButton/index.tsx
  • src/pages/[organization]/[slug].tsx

Comment thread .yarnrc.yml

enableGlobalCache: false

enableScripts: 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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 检查 node_modules 中声明了 install/postinstall 脚本的包

echo "=== 检查声明了安装脚本的依赖 ==="
fd -t f package.json node_modules --max-depth 2 --exec jq -r 'select(.scripts.install or .scripts.postinstall or .scripts.preinstall) | .name + " (" + (.scripts.install // .scripts.postinstall // .scripts.preinstall) + ")"' 2>/dev/null | head -20 || echo "需要先安装依赖才能检查"

Repository: FurryConsChina/website

Length of output: 87


🏁 Script executed:

# Check if .yarnrc.yml exists and its content
cat -n .yarnrc.yml 2>/dev/null || echo "File not found"

Repository: FurryConsChina/website

Length of output: 351


🏁 Script executed:

# Check if node_modules exists
ls -la | grep node_modules || echo "node_modules directory not found"

Repository: FurryConsChina/website

Length of output: 99


🏁 Script executed:

# Check yarn.lock for packages with install/postinstall/preinstall scripts
if [ -f yarn.lock ]; then
  echo "=== Checking yarn.lock for packages with scripts ==="
  grep -E '^\s*(install|postinstall|preinstall):' yarn.lock | head -20 || echo "No script declarations found in yarn.lock"
fi

Repository: FurryConsChina/website

Length of output: 119


🏁 Script executed:

# Check package.json to see what dependencies are declared
if [ -f package.json ]; then
  echo "=== Project dependencies ==="
  jq '.dependencies, .devDependencies' package.json 2>/dev/null | head -30
fi

Repository: FurryConsChina/website

Length of output: 919


🏁 Script executed:

# Check the format of yarn.lock to understand script declarations
if [ -f yarn.lock ]; then
  echo "=== First 50 lines of yarn.lock to understand format ==="
  head -50 yarn.lock
  echo ""
  echo "=== Sample entry checking for scripts ==="
  grep -A 10 '"next@' yarn.lock | head -20
fi

Repository: FurryConsChina/website

Length of output: 2482


🏁 Script executed:

# More comprehensive check - look for any script keys in yarn.lock
if [ -f yarn.lock ]; then
  echo "=== Checking for all script declarations in yarn.lock ==="
  rg '"(install|postinstall|preinstall)"' yarn.lock -A 1 -B 1 | head -30 || echo "No script declarations found with this pattern"
fi

Repository: FurryConsChina/website

Length of output: 124


🏁 Script executed:

# Do a comprehensive check of yarn.lock for any lifecycle scripts at all
if [ -f yarn.lock ]; then
  echo "=== Total lines in yarn.lock ==="
  wc -l yarn.lock
  echo ""
  echo "=== Checking for lifecycle script patterns (install, postinstall, preinstall, prepublishOnly, etc) ==="
  rg '^\s+(install|postinstall|preinstall|prepublishOnly|prepare|build):' yarn.lock | wc -l
  echo ""
  echo "=== Show any matches if they exist ==="
  rg '^\s+(install|postinstall|preinstall|prepublishOnly|prepare|build):' yarn.lock | head -20
fi

Repository: FurryConsChina/website

Length of output: 260


🏁 Script executed:

# Check if there's a git-revision-webpack-plugin which might have scripts
if [ -f yarn.lock ]; then
  echo "=== Check git-revision-webpack-plugin entry in yarn.lock ==="
  rg '"git-revision-webpack-plugin' yarn.lock -A 15 | head -30
fi

Repository: FurryConsChina/website

Length of output: 793


移除不必要的 enableScripts: true 配置。

当前项目依赖中没有声明任何安装脚本(install/postinstall/preinstall),因此 enableScripts: true 是不必要的。为遵循最小权限原则,建议将其改为 enableScripts: false 或移除此配置项。若未来确实需要运行安装脚本,应建立严格的依赖审查流程。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.yarnrc.yml at line 8, The `enableScripts: true` configuration in the Yarn
configuration file is unnecessary because the project does not declare any
installation scripts (install, postinstall, or preinstall hooks) in its
dependencies. Following the principle of least privilege, either remove the
`enableScripts: true` line entirely from the configuration or change it to
`enableScripts: false` to disable script execution by default. This reduces the
security surface and makes the configuration explicit about the project's actual
needs.

Comment thread package.json
"@headlessui/react": "^2.2.10",
"@next/third-parties": "^16.2.6",
"@sentry/nextjs": "^10.53.1",
"@next/third-parties": "^16.2.9",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 检查 Next.js 16.2.9 是否存在及其发布信息

echo "=== 检查 Next.js 16.2.9 版本信息 ==="
npm view next@16.2.9 version time 2>/dev/null || echo "版本不存在或无法访问"

echo -e "\n=== 检查 Next.js 最新版本 ==="
npm view next dist-tags.latest dist-tags.canary

echo -e "\n=== 检查代码中是否存在 Next.js 15→16 迁移标记 ==="
rg -n '`@next/codemod`|npx.*codemod.*next' --type md --type json --type ts --type tsx 2>/dev/null || echo "未发现迁移脚本记录"

Repository: FurryConsChina/website

Length of output: 152


🌐 Web query:

Next.js 16.2.9 release notes breaking changes migration guide

💡 Result:

Next.js version 16.2.9 was released on June 9, 2026 [1][2]. There are no specific release notes, breaking changes, or migration guides associated with version 16.2.9 [3]. This release was an empty, maintenance-only release created to correct the npm dist-tag for next@latest, which had incorrectly pointed to a pre-release version [3][4]. It does not introduce new features, bug fixes, or breaking changes [3][4]. If you are looking for information regarding major changes in the Next.js 16.x line, you should refer to the documentation for upgrading to version 16, which covers significant breaking changes such as the removal of synchronous access to Async Request APIs and updates to image and sitemap generation parameters [5]. For recent minor or patch-level features and bug fixes within the 16.2.x series, refer to the release notes for version 16.2 [6] and the detailed changelogs for preceding patches like 16.2.7 [7].

Citations:


🏁 Script executed:

#!/bin/bash
# 检查代码库中是否存在 Next.js 15 的过时模式

echo "=== 检查是否存在 getServerSideProps/getStaticProps (Next.js 15 模式) ==="
rg -n 'getServerSideProps|getStaticProps' --type ts --type tsx --type js --type jsx 2>/dev/null | head -20 || echo "未发现"

echo -e "\n=== 检查 App Router 使用情况 ==="
rg -n 'app/' --type-list | head -5

echo -e "\n=== 查找 package.json 所在目录结构 ==="
fd -H 'next.config\.(js|ts|mjs)' | head -5

echo -e "\n=== 检查 .next 或 build 输出 ==="
fd -H 'tsconfig.json' -x head -20 {} \;

Repository: FurryConsChina/website

Length of output: 1112


🌐 Web query:

Next.js 16 breaking changes async request API image generation sitemap 迁移指南

💡 Result:

Next.js 16 introduces significant breaking changes centered on enforcing asynchronous access for Request-time APIs and metadata generation functions [1][2]. Async Request APIs In Next.js 16, synchronous access to Request-time APIs is fully removed [3][1]. You must now use await to access these values [4]. Affected APIs include: - cookies, headers, and draftMode [1][2]. - params in layout.js, page.js, route.js, default.js, and metadata image routes (opengraph-image, twitter-image, icon, apple-icon) [3][1]. - searchParams in page.js [1]. Migration Example: // Next.js 16 - asynchronous access export default async function Page({ params, searchParams }) { const resolvedParams = await params; const resolvedSearchParams = await searchParams; const cookieStore = await cookies; //... } Image Generation Image generation functions (e.g., opengraph-image) now receive both params and id as Promises [3][5]. - The props (params, id) passed to image generation functions must be awaited [3][6]. - generateImageMetadata continues to receive synchronous params [3][5]. Sitemap Generation In Next.js 16, the id parameter passed to the sitemap generation function is now a Promise [3][7]. - You must await the id inside your sitemap function [3][8]: export default async function sitemap({ id }) { const resolvedId = await id; //... } Migration Guidance - Use the official Next.js codemods to automate the migration of synchronous Request-time APIs to their async counterparts [3][4]. - Run npx next typegen to generate updated, type-safe helpers for async params and searchParams [3][1]. - Audit all Server Actions, Route Handlers, and Metadata files for synchronous usage of the affected APIs [4]. Additional Next.js 16 Image Changes - The images.minimumCacheTTL default increased from 1 minute to 4 hours [6]. - The 16px width is removed from default images.imageSizes [9][10]. - Local image sources with query strings now require images.localPatterns configuration for security [2][6]. - The priority prop is deprecated in next/image, replaced by the preload property [11]. Top Results: [3], [1], [2], [4], [8]

Citations:


🏁 Script executed:

#!/bin/bash
# 检查代码库中的异步 API 使用情况

echo "=== 检查是否存在 async 页面/布局函数 ==="
rg -n 'async.*function.*Page|async.*function.*Layout' --type ts --type tsx | head -10

echo -e "\n=== 检查 params/searchParams 使用情况 ==="
rg -n 'await (params|searchParams|cookies|headers|draftMode)' --type ts --type tsx | head -10

echo -e "\n=== 检查图片生成函数 ==="
rg -n 'generateImageMetadata|opengraph-image|twitter-image' --type ts --type tsx --type js | head -10

echo -e "\n=== 查看 pages 或 app 目录结构 ==="
fd -H '(pages|app)' -t d | head -10

echo -e "\n=== 检查是否使用了 Server Components ==="
rg -n "'use server'|'use client'" --type ts --type tsx | head -10

Repository: FurryConsChina/website

Length of output: 355


🏁 Script executed:

#!/bin/bash
# 使用正确的方式检查 Pages Router 代码库

echo "=== 检查 Pages Router 文件结构 ==="
fd -H '\.(js|jsx)$' src/pages/ | head -20

echo -e "\n=== 检查 _app/_document 文件 ==="
fd -H '(_app|_document)\.' src/pages/

echo -e "\n=== 搜索 getServerSideProps 和 getStaticProps ==="
rg -n 'export.*get(ServerSideProps|StaticProps)' --type js 2>/dev/null | head -10

echo -e "\n=== 检查 src 目录结构 ==="
tree -L 2 src/ 2>/dev/null || fd -H '.' src/ -t d | head -20

Repository: FurryConsChina/website

Length of output: 1484


🌐 Web query:

Next.js 16 Pages Router breaking changes migration getServerSideProps getStaticProps

💡 Result:

In Next.js 16, the Pages Router has reached a critical stage where it is strongly recommended to migrate to the App Router, as the framework has fully transitioned to a new architecture [1][2]. While you can perform an incremental migration, the core data-fetching APIs used in the Pages Router (getServerSideProps and getStaticProps) have direct, simplified equivalents in the App Router [3][4]. Key Migration Patterns getServerSideProps to Async Server Components In the Pages Router, getServerSideProps was used to fetch data on every request [4]. In the App Router, you simply use an async Server Component [3][2]. Because fetch is uncached by default in Next.js 16, this behavior is equivalent to getServerSideProps [3]. For non-fetch data sources, you can opt out of caching by using cache: 'no-store' or unstable_cache with a revalidation time of 0 [4]. getStaticProps to 'use cache' Components getStaticProps pages were statically generated by default [3]. To achieve this in the App Router, you use an async Server Component combined with the 'use cache' directive [1][3]. This allows for fine-grained control over caching and revalidation, replacing the old page-level static generation model [1][4]. Breaking Changes in Next.js 16 If you are still maintaining Pages Router code within a project upgrading to Next.js 16, be aware of these global breaking changes: 1. Async Request APIs: Synchronous access to cookies(), headers(), draftMode(), params, and searchParams is fully removed [5][1]. Any code still using these synchronously will throw a runtime error; they must now be awaited as promises [5][6]. 2. Middleware renamed to Proxy: The middleware.ts convention has been updated to proxy.ts [1]. 3. Turbopack Default: Turbopack is now the default bundler for both development and production, replacing webpack [1]. 4. Async Parameters: Image metadata generation and sitemap generation functions now receive params and id as promises [5]. Recommendation for Migration Do not attempt to keep getServerSideProps or getStaticProps while upgrading to Next.js 16, as they are not supported in the App Router and your application should be moved entirely to the app/ directory [2]. Follow these steps for an efficient migration: 1. Run the official migration codemods (npx @next/codemod@latest upgrade) to help handle baseline breaking changes [3]. 2. Identify routes using getServerSideProps and convert them to async Server Components first, as this is the simplest path [4][2]. 3. Use the 'use cache' directive to replicate static caching behavior formerly managed by getStaticProps [3]. 4. Replace next/router with next/navigation hooks [3]. top_results: [5][6][1][3][4][2]

Citations:


Next.js 16.2.9 版本确认无虞,但需完成 Pages Router 至 App Router 的迁移。

Next.js 16.2.9 已于 2026 年 6 月 9 日正式发布,是一个维护版本,自身无破坏性变更。然而,Next.js 16.x 包含多项重大破坏性变更,需要立即处理:

  1. 异步请求 APIcookies()headers()draftMode()paramssearchParams 必须 await,同步访问将抛出运行时错误。
  2. Pages Router 至 App Router 迁移:Next.js 16 强烈建议将 Pages Router 完全迁移至 App Router。getServerSidePropsgetStaticProps 在 App Router 中不支持,应改用异步 Server Component 结合 'use cache' 指令。
  3. 图片和站点地图生成generateImageMetadata 和 sitemap 函数的 params/id 参数现为 Promise,需要 await。
  4. 中间件重命名middleware.tsproxy.ts(已完成)。
  5. 默认打包工具:Turbopack 现为默认打包工具。

代码库当前仍使用 Pages Router 结构,需执行完整迁移。建议运行官方迁移 codemod:npx @next/codemod@latest upgrade

Also applies to: 27-27, 30-30, 31-31, 47-47

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 17, The project is using Next.js 16.2.9 which requires
completing the migration from Pages Router to App Router structure. Run the
official Next.js codemod with the command `npx `@next/codemod`@latest upgrade` to
automate the migration process. Additionally, ensure all async request APIs such
as cookies(), headers(), draftMode(), params, and searchParams are properly
awaited throughout the codebase, replace all getServerSideProps and
getStaticProps implementations with async Server Components using the 'use
cache' directive, and update any generateImageMetadata or sitemap functions to
properly handle Promise-based params and id parameters. These changes must be
completed across all affected routes and components to ensure compatibility with
Next.js 16.x requirements.

Comment on lines +42 to +47
const copySource = (source: EventSource) => {
navigator.clipboard
.writeText(source.url)
.then(() => toast.success(t("event.sourceCopySuccess", { name: source.name || source.url })));
track(source);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

剪贴板操作缺少错误处理。

navigator.clipboard.writeText 返回的 Promise 没有 .catch() 处理,可能导致以下问题:

  • 在非 HTTPS 环境下(除 localhost 外),剪贴板 API 可能不可用
  • 部分浏览器或权限设置可能阻止剪贴板访问
  • 当前实现即使复制失败也会显示成功提示,误导用户
🛡️ 建议添加错误处理
 const copySource = (source: EventSource) => {
   navigator.clipboard
     .writeText(source.url)
-    .then(() => toast.success(t("event.sourceCopySuccess", { name: source.name || source.url })));
+    .then(() => toast.success(t("event.sourceCopySuccess", { name: source.name || source.url })))
+    .catch(() => {
+      toast.error(t("event.sourceCopyFailed", { name: source.name || source.url }));
+    });
   track(source);
 };

注:需在 i18n 文件中添加 event.sourceCopyFailed 键。

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const copySource = (source: EventSource) => {
navigator.clipboard
.writeText(source.url)
.then(() => toast.success(t("event.sourceCopySuccess", { name: source.name || source.url })));
track(source);
};
const copySource = (source: EventSource) => {
navigator.clipboard
.writeText(source.url)
.then(() => toast.success(t("event.sourceCopySuccess", { name: source.name || source.url })))
.catch(() => {
toast.error(t("event.sourceCopyFailed", { name: source.name || source.url }));
});
track(source);
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/event/EventSourceButton/index.tsx` around lines 42 - 47, The
copySource function in EventSourceButton/index.tsx is missing error handling for
the navigator.clipboard.writeText promise chain. Add a .catch() handler to the
clipboard operation to gracefully handle failures when the clipboard API is
unavailable or permission is denied. In the catch handler, show a failure toast
message using t("event.sourceCopyFailed") to inform the user that the copy
operation failed. Additionally, add the corresponding i18n translation key
event.sourceCopyFailed to your i18n translation files to support the error
message.

@github-actions

Copy link
Copy Markdown

📦 Next.js Bundle Analysis for furrycons-site

This analysis was generated by the Next.js Bundle Analysis action. 🤖

🎉 Global Bundle Size Decreased

Page Size (compressed)
global 340.89 KB (🟢 -18.77 KB)
Details

The global bundle is the javascript bundle that loads alongside every page. It is in its own category because its impact is much higher - an increase to its size means that every page on your website loads slower, and a decrease means every page loads faster.

Any third party scripts you have added directly to your app using the <script> tag are not accounted for in this analysis

If you want further insight into what is behind the changes, give @next/bundle-analyzer a try!

Ten Pages Changed Size

The following pages changed size from the code in this PR compared to its base branch:

Page Size (compressed) First Load % of Budget (350 KB)
/ 20.82 KB 361.71 KB 103.35% (🟡 +0.21%)
/404 874 B 341.74 KB 97.64% (+/- <0.01%)
/500 872 B 341.74 KB 97.64% (+/- <0.01%)
/[organization] 8.92 KB 349.81 KB 99.95% (🟢 -0.01%)
/[organization]/[slug] 45.01 KB 385.9 KB 110.26% (🟡 +9.19%)
/_error 2.48 KB 343.37 KB 98.10% (🟡 +0.01%)
/city 991 B 341.86 KB 97.67% (+/- <0.01%)
/city/[code] 1.68 KB 342.57 KB 97.88% (+/- <0.01%)
/organization 3.83 KB 344.71 KB 98.49% (🟢 -0.01%)
/years 4.93 KB 345.82 KB 98.80% (+/- <0.01%)
Details

Only the gzipped size is provided here based on an expert tip.

First Load is the size of the global bundle plus the bundle for the individual page. If a user were to show up to your website and land on a given page, the first load size represents the amount of javascript that user would need to download. If next/link is used, subsequent page loads would only need to download that page's bundle (the number in the "Size" column), since the global bundle has already been downloaded.

Any third party scripts you have added directly to your app using the <script> tag are not accounted for in this analysis

The "Budget %" column shows what percentage of your performance budget the First Load total takes up. For example, if your budget was 100kb, and a given page's first load size was 10kb, it would be 10% of your budget. You can also see how much this has increased or decreased compared to the base branch of your PR. If this percentage has increased by 20% or more, there will be a red status indicator applied, indicating that special attention should be given to this. If you see "+/- <0.01%" it means that there was a change in bundle size, but it is a trivial enough amount that it can be ignored.

@PaiJi PaiJi merged commit 480f049 into main Jun 14, 2026
11 of 12 checks passed
@PaiJi PaiJi deleted the feat/support-multi-sources branch June 14, 2026 15:05
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