Skip to content

Conversation

@zhangligei
Copy link
Collaborator

@zhangligei zhangligei commented Nov 13, 2025

  • ♻️ refactor:Create /chat route with HeroParallax component for product showcase
  • Add products.ts with Xyzen product data
  • Implement chat page layout with animated parallax effect
  • Add CTA button linking to Bohrium Xyzen application
  • Update Navbar to include Projects menu with 5 ScienceOL products
  • Add dark mode styling for better visual appearance
  • Integrate React Router with new /chat route in router.tsx

变更内容

  • 新功能
  • 修复 Bug
  • 增强重构
  • 其他(请描述)

简要描述本次 PR 的主要变更内容。

相关 Issue

请关联相关 Issue(如有):#编号

检查清单

默认已勾选,如不满足,请检查。

  • 已在本地测试通过
  • 已补充/更新相关文档
  • 代码风格已经过 pre-commit 钩子检查
  • 非企业版分支
  • 遵循 贡献者文档 中的最佳实践

其他说明

如有特殊说明或注意事项,请补充。

Sourcery 总结

添加一个新的 Xyzen 聊天着陆页,带有动画视差产品展示,并将其集成到网站导航和样式中。

新功能:

  • 引入 /chat 路由,使用 ChatPage 组件,该组件包含 HeroParallax 展示和一个启动 Xyzen 的 CTA 按钮
  • 提供 products.ts 数据文件,列出 Xyzen 缩略图和链接
  • 集成 HeroParallax UI 组件以实现滚动视差效果

改进:

  • 重构 Projects 导航栏以使用 NavbarFullWidth,并更新资源图标,包括 Xyzen 入口
  • Tutorials 导航栏下拉菜单替换为一个直接的外部文档链接
  • 使用 tw-animate-css、主题 CSS 变量和深色模式样式扩展全局样式

构建:

  • index.css 中导入 tw-animate-css

杂项:

  • 添加用于 className 合并的 cn 工具函数
Original summary in English

Summary by Sourcery

Add a new Xyzen chat landing page with animated parallax product showcase and integrate it into the site navigation and styling

New Features:

  • Introduce /chat route with ChatPage component featuring HeroParallax showcase and a CTA button to launch Xyzen
  • Provide products.ts data file listing Xyzen thumbnails and links
  • Integrate HeroParallax UI component for scrolling parallax effect

Enhancements:

  • Refactor Projects navbar to use NavbarFullWidth with updated resource icons including Xyzen entry
  • Replace Tutorials navbar dropdown with a direct external docs link
  • Extend global styles with tw-animate-css, theme CSS variables and dark mode styling

Build:

  • Import tw-animate-css in index.css

Chores:

  • Add cn utility function for className merging

- ♻️ refactor:Create /chat route with HeroParallax component for product showcase
- Add products.ts with Xyzen product data
- Implement chat page layout with animated parallax effect
- Add CTA button linking to Bohrium Xyzen application
- Update Navbar to include Projects menu with 5 ScienceOL products
- Add dark mode styling for better visual appearance
- Integrate React Router with new /chat route in router.tsx
@sourcery-ai
Copy link

sourcery-ai bot commented Nov 13, 2025

评审指南

此 PR 通过为动画产品展示创建 HeroParallax 组件和一个带有 CTA 按钮的 ChatPage,实现了新的 Xyzen 聊天登录页;将 /chat 路由集成到 React Router 中;更新了导航栏的“项目”和“教程”菜单以展示新页面;并通过暗模式主题和实用函数增强了样式。

HeroParallax 和 ChatPage 组件的类图

classDiagram
  class ChatPage {
    +HeroParallax products
    +CTA button ("开始使用 Xyzen")
  }
  class HeroParallax {
    +products: Product[]
    +Header()
    +ProductCard(product, translate)
  }
  class Product {
    +title: string
    +link: string
    +thumbnail: string
  }
  ChatPage --> HeroParallax : uses
  HeroParallax --> Product : displays
Loading

文件级变更

变更 详情 文件
添加 Xyzen 聊天登录页,包含动画 HeroParallax 和 CTA
  • 使用 motion 创建 HeroParallax 组件,实现滚动驱动的视差效果
  • 在 products.ts 中定义产品数据,用于视差展示
  • 实现 ChatPage 布局,渲染 HeroParallax 和一个打开 Xyzen 应用的 CTA 按钮
  • 在 router.tsx 中注册指向 ChatPage 的新 /chat 路由
web/src/components/ui/hero-parallax.tsx
web/src/app/chat/products.ts
web/src/app/chat/page.tsx
web/src/router.tsx
重构导航栏项目和教程,以包含新的聊天页面
  • 在“项目”菜单中用 NavbarFullWidth 替换 NavbarFullWidthPreview,并更新 resources/callsToAction 数组
  • 将“教程”从完整下拉菜单简化为直接外部链接
  • 调整 NavbarMenu,通过类型化包装器渲染 Tutorial 组件
web/src/app/navbar/Projects.tsx
web/src/app/navbar/Tutorials.tsx
web/src/app/navbar/Navbar.tsx
通过暗模式和 CSS 主题增强全局样式
  • 导入 tw-animate-css 并为亮/暗主题设置 CSS 自定义属性
  • 定义内联主题变体并应用基础层实用样式
web/index.css
引入用于合并 Tailwind 类名的实用函数
  • 添加 cn() 辅助函数,封装 clsx 和 tailwind-merge 以进行 className 组合
web/src/lib/utils.ts

提示和命令

与 Sourcery 交互

  • 触发新评审: 在拉取请求上评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的评审评论。
  • 从评审评论生成 GitHub issue: 回复 Sourcery 的评审评论并请求它创建 issue。你也可以回复评审评论并输入 @sourcery-ai issue 来创建 issue。
  • 生成拉取请求标题: 在拉取请求标题的任意位置写入 @sourcery-ai 以随时生成标题。你也可以在拉取请求上评论 @sourcery-ai title 以随时(重新)生成标题。
  • 生成拉取请求摘要: 在拉取请求正文的任意位置写入 @sourcery-ai summary 以随时在你想要的位置生成 PR 摘要。你也可以在拉取请求上评论 @sourcery-ai summary 以随时(重新)生成摘要。
  • 生成评审指南: 在拉取请求上评论 @sourcery-ai guide 以随时(重新)生成评审指南。
  • 解决所有 Sourcery 评论: 在拉取请求上评论 @sourcery-ai resolve 以解决所有 Sourcery 评论。如果你已处理完所有评论并且不想再看到它们,这会很有用。
  • 驳回所有 Sourcery 评审: 在拉取请求上评论 @sourcery-ai dismiss 以驳回所有现有的 Sourcery 评审。如果你想重新开始一个新的评审,这特别有用——别忘了评论 @sourcery-ai review 来触发新的评审!

自定义你的体验

访问你的仪表盘以:

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

获取帮助

Original review guide in English

Reviewer's Guide

This PR implements a new Xyzen chat landing page by creating a HeroParallax component for animated product showcases and a ChatPage with a CTA button, integrates the /chat route into React Router, updates the Navbar Projects and Tutorials menus to surface the new page, and enhances styling with dark mode theming and utility functions.

Class diagram for HeroParallax and ChatPage components

classDiagram
  class ChatPage {
    +HeroParallax products
    +CTA button ("开始使用 Xyzen")
  }
  class HeroParallax {
    +products: Product[]
    +Header()
    +ProductCard(product, translate)
  }
  class Product {
    +title: string
    +link: string
    +thumbnail: string
  }
  ChatPage --> HeroParallax : uses
  HeroParallax --> Product : displays
Loading

File-Level Changes

Change Details Files
Add Xyzen chat landing page with animated HeroParallax and CTA
  • Create HeroParallax component using motion for scroll-driven parallax effects
  • Define products data in products.ts for the parallax showcase
  • Implement ChatPage layout that renders HeroParallax and a CTA button opening the Xyzen app
  • Register new /chat route pointing to ChatPage in router.tsx
web/src/components/ui/hero-parallax.tsx
web/src/app/chat/products.ts
web/src/app/chat/page.tsx
web/src/router.tsx
Refactor Navbar Projects and Tutorials to include new chat page
  • Swap out NavbarFullWidthPreview for NavbarFullWidth in Projects menu and update the resources/callsToAction arrays
  • Simplify Tutorials from a full dropdown to a direct external link
  • Adjust NavbarMenu to render Tutorial component via a typed wrapper
web/src/app/navbar/Projects.tsx
web/src/app/navbar/Tutorials.tsx
web/src/app/navbar/Navbar.tsx
Enhance global styling with dark mode and CSS theming
  • Import tw-animate-css and set up CSS custom properties for light/dark themes
  • Define inline theme variants and apply base layer utility styles
web/index.css
Introduce utility for merging Tailwind class names
  • Add cn() helper wrapping clsx and tailwind-merge for className composition
web/src/lib/utils.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

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

你好 - 我已经审查了你的更改 - 这里有一些反馈:

  • 删除 Projects.tsx 和 Tutorials.tsx 中大块注释掉的代码,以提高可读性并保持代码库整洁。
  • 将 index.css 中大量的 CSS 自定义属性提取到专门的主题文件或 Tailwind 配置中,以使你的基础样式表保持专注和可维护。
  • 重构 HeroParallax 以动态生成行(或接受一个 columns prop),而不是硬编码五个项目的切片,以便在产品数量不同时具有更好的灵活性。
供 AI 代理使用的提示
请处理此代码审查中的评论:

## 总体评论
- 删除 Projects.tsx 和 Tutorials.tsx 中大块注释掉的代码,以提高可读性并保持代码库整洁。
- 将 index.css 中大量的 CSS 自定义属性提取到专门的主题文件或 Tailwind 配置中,以使你的基础样式表保持专注和可维护。
- 重构 HeroParallax 以动态生成行(或接受一个 columns prop),而不是硬编码五个项目的切片,以便在产品数量不同时具有更好的灵活性。

## 单独评论

### 评论 1
<location> `web/src/app/navbar/Tutorials.tsx:64-73` </location>
<code_context>
+
+// import Link from 'next/link';
+
+export default function Tutorial() {
   return (
-    <NavbarFullWidth
-      buttonName="tutorial"
-      solutions={resources}
-      callsToAction={callsToAction}
-      numberOfCallsToAction={2}
-      numberOfSolutions={3}
-      {...props}
-    />
+    <a
+      href="https://docs.sciol.ac.cn"
+      target="_blank"
+      rel="noopener noreferrer"
+      className="text-sm font-medium text-gray-700 hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100"
+    >
+      教程
+    </a>
   );
 }
</code_context>

<issue_to_address>
**issue (bug_risk):** Tutorial 组件签名已更改,但 Navbar 需要 props。

Navbar 将多个 props 传递给 Tutorial,但 Tutorial 不再接受任何 props。这种不匹配可能会导致运行时问题。请更新 Tutorial 以处理这些 props,或调整 Navbar 以避免传递它们。
</issue_to_address>

### 评论 2
<location> `web/src/components/ui/hero-parallax.tsx:20-22` </location>
<code_context>
+    thumbnail: string;
+  }[];
+}) => {
+  const firstRow = products.slice(0, 5);
+  const secondRow = products.slice(5, 10);
+  const thirdRow = products.slice(10, 15);
+  const ref = React.useRef(null);
+  const { scrollYProgress } = useScroll({
</code_context>

<issue_to_address>
**suggestion:** 将产品切片为固定大小的行可能会导致 `products.length < 15` 时出现问题。

如果产品少于 15 个,行可能会为空或不完整。考虑处理更短的数组或动态计算行大小。

建议的实现:

```typescript
  // Dynamically split products into rows of up to 5 items each
  const rowSize = 5;
  const rows = [];
  for (let i = 0; i < products.length; i += rowSize) {
    rows.push(products.slice(i, i + rowSize));
  }

```

如果你在文件中的其他地方引用了 `firstRow``secondRow``thirdRow`,请更新这些用法以使用 `rows[0]``rows[1]``rows[2]` 等。如果要在循环中渲染它们,你可能还需要迭代 `rows`。
</issue_to_address>

### 评论 3
<location> `web/src/components/ui/hero-parallax.tsx:141-146` </location>
<code_context>
+      className="group/product h-96 w-[30rem] relative shrink-0"
+    >
+      <a href={product.link} className="block group-hover/product:shadow-2xl ">
+        <img
+          src={product.thumbnail}
+          height="600"
+          width="600"
+          className="object-cover object-left-top absolute h-full w-full inset-0"
+          alt={product.title}
+        />
+      </a>
</code_context>

<issue_to_address>
**suggestion:** ProductCard 使用带有外部 URL 的 `<img>`;考虑处理加载失败的错误。

实施备用图像或错误处理程序,以防止 URL 加载失败时显示损坏的图像。
</issue_to_address>

### 评论 4
<location> `web/src/app/navbar/Projects.tsx:1` </location>
<code_context>
-import { GitHubIcon } from '@/assets/SocialIcons';
-import { EnvelopeIcon, RocketLaunchIcon } from '@heroicons/react/24/outline';
+// import { GitHubIcon } from '@/assets/SocialIcons';
+// import { EnvelopeIcon, RocketLaunchIcon } from '@heroicons/react/24/outline';
+
</code_context>

<issue_to_address>
**issue (complexity):** 考虑删除注释掉的代码,并动态推导数组长度,而不是硬编码计数。

```suggestion
删除所有过时的、注释掉的导入和选项定义(你总是可以从 Git 中恢复它们),并避免硬编码计数——从你的数组中推导它们。例如:

1) 清理文件顶部:
```ts
// before: dozens of // import ... and // const options = { ... }
// after:
import { EnvelopeIcon } from '@heroicons/react/24/outline';
import { SiUnrealengine, SiUnity, SiProton, SiX, SiStmicroelectronics } from 'react-icons/si';
import { GitHubIcon } from '@/assets/SocialIcons';
import NavbarFullWidth from './NavbarFullWidth';
import type { NavbarFullWidthProps } from './types';
```

2) 内联你的数据并推导长度,而不是传递字面量:
```ts
const resources = [
  { name: 'Studio', icon: SiStmicroelectronics, /* ... */ },
  /* other items */
];
const callsToAction = [
  { name: 'Follow on GitHub', href: 'https://github.com/Protium', icon: GitHubIcon },
  { name: 'Contact us', href: '#', icon: EnvelopeIcon },
];

export default function Projects(
  props: Omit<NavbarFullWidthProps, 'solutions' | 'callsToAction' | 'buttonName'>
) {
  return (
    <NavbarFullWidth
      buttonName="project"
      solutions={resources}
      callsToAction={callsToAction}
      numberOfSolutions={resources.length}
      numberOfCallsToAction={callsToAction.length}
      {...props}
    />
  );
}
```

3) (可选)如果 `NavbarFullWidth` 可以自行推断这些计数,则完全删除 `numberOf*` props,并在该组件内部计算 `solutions.length` / `callsToAction.length`。这可以使 API 表面最小化并消除魔术数字。
```ts
// inside NavbarFullWidth.tsx
const numSolutions = solutions.length;
const numActions   = callsToAction.length;
```
</issue_to_address>

Sourcery 对开源免费 - 如果你喜欢我们的评论,请考虑分享它们 ✨
帮助我更有用!请点击每个评论上的 👍 或 👎,我将利用这些反馈来改进你的评论。
Original comment in English

Hey there - I've reviewed your changes - here's some feedback:

  • Remove large blocks of commented-out code in Projects.tsx and Tutorials.tsx to improve readability and keep the codebase clean.
  • Extract the extensive CSS custom properties in index.css into a dedicated theme file or Tailwind config so your base stylesheet stays focused and maintainable.
  • Refactor HeroParallax to generate rows dynamically (or accept a columns prop) rather than hard-coding slices of five items for better flexibility with varying product counts.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Remove large blocks of commented-out code in Projects.tsx and Tutorials.tsx to improve readability and keep the codebase clean.
- Extract the extensive CSS custom properties in index.css into a dedicated theme file or Tailwind config so your base stylesheet stays focused and maintainable.
- Refactor HeroParallax to generate rows dynamically (or accept a columns prop) rather than hard-coding slices of five items for better flexibility with varying product counts.

## Individual Comments

### Comment 1
<location> `web/src/app/navbar/Tutorials.tsx:64-73` </location>
<code_context>
+
+// import Link from 'next/link';
+
+export default function Tutorial() {
   return (
-    <NavbarFullWidth
-      buttonName="tutorial"
-      solutions={resources}
-      callsToAction={callsToAction}
-      numberOfCallsToAction={2}
-      numberOfSolutions={3}
-      {...props}
-    />
+    <a
+      href="https://docs.sciol.ac.cn"
+      target="_blank"
+      rel="noopener noreferrer"
+      className="text-sm font-medium text-gray-700 hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100"
+    >
+      教程
+    </a>
   );
 }
</code_context>

<issue_to_address>
**issue (bug_risk):** Tutorial component signature changed, but Navbar expects props.

Navbar passes several props to Tutorial, but Tutorial no longer accepts any. This mismatch may cause runtime issues. Please update Tutorial to handle these props or adjust Navbar to avoid passing them.
</issue_to_address>

### Comment 2
<location> `web/src/components/ui/hero-parallax.tsx:20-22` </location>
<code_context>
+    thumbnail: string;
+  }[];
+}) => {
+  const firstRow = products.slice(0, 5);
+  const secondRow = products.slice(5, 10);
+  const thirdRow = products.slice(10, 15);
+  const ref = React.useRef(null);
+  const { scrollYProgress } = useScroll({
</code_context>

<issue_to_address>
**suggestion:** Slicing products into fixed-size rows may cause issues if products.length < 15.

Rows may be empty or incomplete if there are fewer than 15 products. Consider handling shorter arrays or calculating row sizes dynamically.

Suggested implementation:

```typescript
  // Dynamically split products into rows of up to 5 items each
  const rowSize = 5;
  const rows = [];
  for (let i = 0; i < products.length; i += rowSize) {
    rows.push(products.slice(i, i + rowSize));
  }

```

If you reference `firstRow`, `secondRow`, or `thirdRow` elsewhere in the file, update those usages to use `rows[0]`, `rows[1]`, `rows[2]`, etc. You may also want to iterate over `rows` if rendering them in a loop.
</issue_to_address>

### Comment 3
<location> `web/src/components/ui/hero-parallax.tsx:141-146` </location>
<code_context>
+      className="group/product h-96 w-[30rem] relative shrink-0"
+    >
+      <a href={product.link} className="block group-hover/product:shadow-2xl ">
+        <img
+          src={product.thumbnail}
+          height="600"
+          width="600"
+          className="object-cover object-left-top absolute h-full w-full inset-0"
+          alt={product.title}
+        />
+      </a>
</code_context>

<issue_to_address>
**suggestion:** ProductCard uses <img> with external URLs; consider error handling for failed loads.

Implement a fallback image or error handler to prevent broken image displays when URLs fail to load.
</issue_to_address>

### Comment 4
<location> `web/src/app/navbar/Projects.tsx:1` </location>
<code_context>
-import { GitHubIcon } from '@/assets/SocialIcons';
-import { EnvelopeIcon, RocketLaunchIcon } from '@heroicons/react/24/outline';
+// import { GitHubIcon } from '@/assets/SocialIcons';
+// import { EnvelopeIcon, RocketLaunchIcon } from '@heroicons/react/24/outline';
+
</code_context>

<issue_to_address>
**issue (complexity):** Consider removing commented-out code and deriving array lengths dynamically instead of hard-coding counts.

```suggestion
Remove all of the stale, commented-out imports and option definitions (you can always recover them from Git), and avoid hard-coding counts—derive them from your arrays. For example:

1) Clean up the top of the file:
```ts
// before: dozens of // import ... and // const options = { ... }
// after:
import { EnvelopeIcon } from '@heroicons/react/24/outline';
import { SiUnrealengine, SiUnity, SiProton, SiX, SiStmicroelectronics } from 'react-icons/si';
import { GitHubIcon } from '@/assets/SocialIcons';
import NavbarFullWidth from './NavbarFullWidth';
import type { NavbarFullWidthProps } from './types';
```

2) Inline your data and derive lengths instead of passing literals:
```ts
const resources = [
  { name: 'Studio', icon: SiStmicroelectronics, /* ... */ },
  /* other items */
];
const callsToAction = [
  { name: 'Follow on GitHub', href: 'https://github.com/Protium', icon: GitHubIcon },
  { name: 'Contact us', href: '#', icon: EnvelopeIcon },
];

export default function Projects(
  props: Omit<NavbarFullWidthProps, 'solutions' | 'callsToAction' | 'buttonName'>
) {
  return (
    <NavbarFullWidth
      buttonName="project"
      solutions={resources}
      callsToAction={callsToAction}
      numberOfSolutions={resources.length}
      numberOfCallsToAction={callsToAction.length}
      {...props}
    />
  );
}
```

3) (Optional) If `NavbarFullWidth` can infer those counts itself, remove the `numberOf*` props entirely and compute `solutions.length` / `callsToAction.length` inside that component. This keeps the API surface minimal and removes magic numbers.
```ts
// inside NavbarFullWidth.tsx
const numSolutions = solutions.length;
const numActions   = callsToAction.length;
```
</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 on lines +64 to +73
export default function Tutorial() {
return (
<NavbarFullWidth
buttonName="tutorial"
solutions={resources}
callsToAction={callsToAction}
numberOfCallsToAction={2}
numberOfSolutions={3}
{...props}
/>
<a
href="https://docs.sciol.ac.cn"
target="_blank"
rel="noopener noreferrer"
className="text-sm font-medium text-gray-700 hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100"
>
教程
</a>
Copy link

Choose a reason for hiding this comment

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

issue (bug_risk): Tutorial 组件签名已更改,但 Navbar 需要 props。

Navbar 将多个 props 传递给 Tutorial,但 Tutorial 不再接受任何 props。这种不匹配可能会导致运行时问题。请更新 Tutorial 以处理这些 props,或调整 Navbar 以避免传递它们。

Original comment in English

issue (bug_risk): Tutorial component signature changed, but Navbar expects props.

Navbar passes several props to Tutorial, but Tutorial no longer accepts any. This mismatch may cause runtime issues. Please update Tutorial to handle these props or adjust Navbar to avoid passing them.

Comment on lines +20 to +22
const firstRow = products.slice(0, 5);
const secondRow = products.slice(5, 10);
const thirdRow = products.slice(10, 15);
Copy link

Choose a reason for hiding this comment

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

suggestion: 将产品切片为固定大小的行可能会导致 products.length < 15 时出现问题。

如果产品少于 15 个,行可能会为空或不完整。考虑处理更短的数组或动态计算行大小。

建议的实现:

  // Dynamically split products into rows of up to 5 items each
  const rowSize = 5;
  const rows = [];
  for (let i = 0; i < products.length; i += rowSize) {
    rows.push(products.slice(i, i + rowSize));
  }

如果你在文件中的其他地方引用了 firstRowsecondRowthirdRow,请更新这些用法以使用 rows[0]rows[1]rows[2] 等。如果要在循环中渲染它们,你可能还需要迭代 rows

Original comment in English

suggestion: Slicing products into fixed-size rows may cause issues if products.length < 15.

Rows may be empty or incomplete if there are fewer than 15 products. Consider handling shorter arrays or calculating row sizes dynamically.

Suggested implementation:

  // Dynamically split products into rows of up to 5 items each
  const rowSize = 5;
  const rows = [];
  for (let i = 0; i < products.length; i += rowSize) {
    rows.push(products.slice(i, i + rowSize));
  }

If you reference firstRow, secondRow, or thirdRow elsewhere in the file, update those usages to use rows[0], rows[1], rows[2], etc. You may also want to iterate over rows if rendering them in a loop.

Comment on lines +141 to +146
<img
src={product.thumbnail}
height="600"
width="600"
className="object-cover object-left-top absolute h-full w-full inset-0"
alt={product.title}
Copy link

Choose a reason for hiding this comment

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

suggestion: ProductCard 使用带有外部 URL 的 <img>;考虑处理加载失败的错误。

实施备用图像或错误处理程序,以防止 URL 加载失败时显示损坏的图像。

Original comment in English

suggestion: ProductCard uses with external URLs; consider error handling for failed loads.

Implement a fallback image or error handler to prevent broken image displays when URLs fail to load.

@Mile-Away Mile-Away merged commit bf8adce into main Nov 13, 2025
1 check passed
@Mile-Away Mile-Away deleted the feat/xyzen-chat-landing-page branch November 13, 2025 16:14
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.

3 participants