Skip to content

UI/0626#220

Merged
cubxxw merged 25 commits intomainfrom
ui/0624
Jun 26, 2025
Merged

UI/0626#220
cubxxw merged 25 commits intomainfrom
ui/0624

Conversation

@Neo-Neooo
Copy link
Contributor

@Neo-Neooo Neo-Neooo commented Jun 26, 2025

User description

🫰 Thanks for your Pull Request! Here are some helpful tips:

👀 Purpose and Importance of this PR:

  • My code conforms to the coding style of this project.
  • My code requires changes to the documentation.
  • I have updated the documentation accordingly.
  • All tests have passed.

🅰 Fixes for Issues:

Fixes #211 #214 #186 #

📝 Notes for the Reviewer:

🎯 How to Verify this PR:

📑 Additional Documentation, e.g., KEPs (Telepace Enhancement Proposals), Usage Docs, etc.:


PR Type

Enhancement


Description

  • Refactor AI analysis components for better reusability and consistency

  • Add responsive library width for large screens (2xl breakpoint)

  • Improve ContentPreview and ContentAnalysisSidebar UI layouts

  • Update tooltip configuration and markdown rendering enhancements


Changes walkthrough 📝

Relevant files
Enhancement
13 files
globals.css
Add responsive library width for large screens                     
+20/-0   
AnalysisCards.tsx
Major refactor of analysis cards with unified data adapter
+353/-127
ContentPreview.tsx
Simplify ContentPreview using refactored analysis components
+27/-362
ContentAnalysisSidebar.tsx
Refactor sidebar to use unified analysis components           
+32/-281
ClientContent.tsx
Update content loading and context integration                     
+34/-95 
ReaderLayout.tsx
Improve layout with better data fetching and loading states
+115/-45
VirtualScrollRenderer.tsx
Update styling to use design system colors                             
+13/-13 
ContentCard.tsx
Add tooltip for star rating component                                       
+30/-39 
loading.tsx
Update loading skeleton to match new layout                           
+15/-16 
MarkdownRenderer.tsx
Add math support and improve plugin configuration               
+11/-15 
enhanced-llm-analysis-sidebar.tsx
Remove redundant header from analysis sidebar                       
+4/-14   
tooltip.tsx
Improve tooltip timing and animation configuration             
+4/-2     
page.tsx
Update library layout to use responsive width classes       
+2/-2     
Tests
1 files
page.tsx
Update test page to use new component variants                     
+7/-4     
Configuration changes
1 files
next.config.mjs
Add KaTeX webpack configuration for math rendering             
+18/-17 
Additional files
2 files
CloudIcon.tsx +0/-20   
llm-analysis-sidebar.tsx +0/-340 

Need help?
  • Type /help how to ... in the comments thread for any questions about Qodo Merge usage.
  • Check out the documentation for more information.
  • Summary by CodeRabbit

    • New Features

      • Added math syntax and automatic table of contents support in markdown rendering, including image zoom functionality.
      • Introduced unified and enhanced AI analysis cards with improved summary, key points, and metadata displays, including star ratings with tooltips.
    • Improvements

      • Streamlined and simplified content preview and content card displays, removing difficulty badges and outdated UI elements.
      • Enhanced AI analysis sidebar with unified data handling and loading skeletons.
      • Improved theming and consistency of UI elements, including semantic color usage and responsive sidebar widths for large screens.
      • Updated tooltip animations and delay for a smoother user experience.
      • Simplified and consolidated AI analysis sidebar and reader layout to use unified content item data and loading states.
      • Refined skeleton UIs for loading states in content reader and right panel.
      • Reduced main reading panel width and increased AI analysis panel width for better layout balance.
    • Bug Fixes

      • Fixed padding and centering issues in markdown and skeleton components.
    • Refactor

      • Replaced fragmented content and analysis state management with unified content item models and context.
      • Removed obsolete and redundant components, such as difficulty badges, legacy AI analysis sidebar, and cloud icon.
      • Simplified internal logic for content loading and analysis display.
      • Updated styling classes in virtual scroll and other components for consistent theming.
    • Chores

      • Updated webpack configuration for better KaTeX support.
      • Cleaned up and reorganized imports and styling classes.

    Neo-Neooo added 24 commits June 25, 2025 17:09
    - 移除了不再需要的 Brain 图标,简化了组件的视觉元素。
    - 调整了标题和布局,使其更符合用户体验,提升了可读性和美观性。
    
    这些更改增强了组件的整体用户体验。
    - 将背景颜色类从 `bg-neutral-100` 更改为 `linear-bg-2`,增强了组件的视觉层次感。
    - 调整标签和描述的样式,移除了多余的背景颜色,提升了整体美观性。
    
    这些更改优化了用户界面的视觉体验。
    - 在 ContentCard 组件中移除了难度等级的显示逻辑,简化了组件内容。
    - 在 ContentPreview 组件中调整了难度等级的样式,移除了颜色类,提升了视觉一致性。
    - 重新组织了内容展示顺序,确保质量评分在查看全文按钮之后,增强了用户体验。
    
    这些更改旨在提升组件的可读性和用户交互体验。
    - 将输入框的占位符文本从“chat with content”更改为“ask something...”以更好地反映功能。
    - 调整了输入框和发送按钮的样式,增加了圆角和阴影效果,提升了视觉效果和用户体验。
    - 在多个测试用例中同步更新了占位符文本,确保测试的一致性。
    
    这些更改增强了组件的可用性和视觉吸引力。
    - 移除了不再使用的 CloudIcon.tsx 文件,减少了代码冗余。
    - 此更改有助于提升项目的可维护性和清晰度。
    - 调整了 SummaryCard、KeyPointsCard 和 MetadataCard 组件的标题样式,移除了背景渐变,统一了文本颜色为前景色,增强了视觉统一性。
    - 更新了内容摘要和关键要点的布局,使其更符合用户体验,提升了可读性和美观性。
    
    这些更改旨在优化用户界面的视觉效果和交互体验。
    - 调整了内容预览组件的布局,移除了多余的顶部外边距,使整体视觉效果更加协调。
    - 这些更改旨在优化用户界面的可读性和美观性。
    - 将错误提示和按钮的颜色类更新为更具一致性的样式,增强了组件的视觉效果。
    - 调整了加载更多内容和结束指示器的样式,使其更符合整体设计风格。
    - 这些更改旨在优化用户界面的可读性和美观性。
    - 在 ContentPreview 组件中引入了新的分析卡片组件,统一了摘要和关键要点的展示方式,增强了信息的可读性。
    - 更新了 SummaryCard 和 KeyPointsCard 组件,添加了变体支持,优化了样式和布局,使其在不同场景下表现更佳。
    - 在 ReaderLayout 和 ContentAnalysisSidebar 中整合了新的数据适配逻辑,确保分析结果的正确展示。
    
    这些更改旨在提升用户界面的整体一致性和交互体验。
    - 添加了 webpack 配置以处理 KaTeX 模块,确保在服务器端正确加载。
    - 配置了 externals 和 fallback,以优化客户端的模块解析。
    
    这些更改旨在提升项目对 KaTeX 的支持和整体构建配置的灵活性。
    - 在 ContentPreview 组件中引入了新的分析卡片组件,统一了摘要和关键要点的展示方式,增强了信息的可读性。
    - 更新了 SummaryCard 和 KeyPointsCard 组件,添加了变体支持,优化了样式和布局,使其在不同场景下表现更佳。
    - 在 ReaderLayout 和 ContentAnalysisSidebar 中整合了新的数据适配逻辑,确保分析结果的正确展示。
    
    这些更改旨在提升用户界面的整体一致性和交互体验。
    - 移除了不再使用的 LLMAnalysisSidebar.tsx 文件,减少了代码冗余。
    - 此更改有助于提升项目的可维护性和清晰度。
    - 在 ContentPreview 组件中,调整了 SummaryCard 和 KeyPointsCard 的代码格式,提升了可读性。
    - 在 AnalysisCards 组件中,优化了条件判断和内容提取逻辑,增强了代码的清晰度。
    - 更新了 ContentAnalysisSidebar 和 ReaderLayout 组件中的代码结构,确保一致性和可维护性。
    
    这些更改旨在提升代码的可读性和维护性,进一步优化用户体验。
    - 在 ClientContent、AnalysisCards 和 VirtualScrollRenderer 组件中,调整了样式类,增加了 mx-auto 以改善布局。
    - 在 ContentAnalysisSidebar 组件中,增加了底部内边距,确保内容显示更为美观。
    
    这些更改旨在提升用户界面的整体可读性和视觉一致性。
    - 修复评分转换逻辑:将0.8转换为4.0/5.0格式而非0.8/5.0
    - 统一ContentCard、ContentPreview、AnalysisCards中的评分显示
    - 为所有五星评分组件添加tooltip提示'内容质量评分'
    - 确保80%质量评分正确显示为4颗星+4.0/5.0分数
    
    涉及文件:
    - frontend/app/content-library/components/ContentCard.tsx
    - frontend/app/content-library/components/ContentPreview.tsx
    - frontend/components/ai/AnalysisCards.tsx
    - 优化tooltip hover区域:从整行改为fit内容宽度
    - 移除tooltip的cursor-help样式,避免问号光标
    - 改进tooltip动画:增加丝滑的fade+zoom过渡效果
    - 统一ContentPreview评分组件尺寸与ContentCard保持一致
    - 将ContentPreview中内容质量评分移至最底部
    - 优化tooltip延迟和跳过延迟时间,提升交互体验
    
    技术改进:
    - 容器从flex改为inline-flex,精确控制hover区域
    - tooltip使用transition-all duration-200 ease-out实现平滑动画
    - 统一星级图标尺寸为h-3 w-3,文字为text-xs
    - 将ContentPreview标题从'内容预览'改为'Preview'
    - 隐藏preview中的'简短描述'和'内容质量'组件
    - 为sidebar variant的AnalysisCards添加hover:shadow-none
    - 调整VirtualScrollRenderer中chunk信息文字颜色为neutral-200
    
    UI改进:
    - Preview界面更简洁,专注核心AI分析内容
    - 统一sidebar和preview的hover行为
    - 提升chunk信息的可读性
    - 调整了 ReaderSkeleton 和 RightPanelSkeleton 的样式,使其更加简洁和一致。
    - 更新了 ReaderLayout 组件的主阅读区域和 AI 分析区域的默认占比,提升了布局的灵活性。
    - 改进了 EnhancedLLMAnalysisSidebar 组件的结构,简化了头部和内容区域的样式。
    
    这些更改旨在提升用户界面的可读性和交互体验。
    - Add --size-library-lg CSS variable (38rem/608px) for 2xl+ screens
    - Add w-library-lg and 2xl:w-library-lg Tailwind utility classes
    - Update content library layout to use responsive width system
    - Fix overflow handling in ContentPreview components
    - Minor code formatting improvements
    - Remove unused imports and variables in multiple components
    - Fix TypeScript type issues and any types
    - Clean up ContentPreview, ContentCard, AnalysisCards components
    - Remove unused components (DifficultyBadge, StarRating)
    - Update ReaderLayout interface to simplify unused parameter
    - Ensure strict ESLint compliance for CI pipeline
    - 在 ContentCard、ContentPreview、ClientContent、AnalysisCards 和 ContentAnalysisSidebar 组件中,清理了多余的空行。
    - 这些更改旨在使代码更加简洁,提升整体可维护性。
    @coderabbitai
    Copy link
    Contributor

    coderabbitai bot commented Jun 26, 2025

    Caution

    Review failed

    The pull request is closed.

    Walkthrough

    This update refactors several UI components, focusing on unifying and simplifying the AI analysis and content preview interfaces. It removes the difficulty badge, streamlines the star rating display, consolidates analysis card handling, updates sidebar and preview logic, and introduces improved styling and layout utilities. Some obsolete files and components are deleted.

    Changes

    File(s) Change Summary
    frontend/app/content-library/components/ContentCard.tsx Removed DifficultyBadge, updated StarRating with tooltip, adjusted ContentCard UI.
    frontend/app/content-library/components/ContentPreview.tsx Refactored to use unified analysis adapter and cards; removed detailed preview UI elements and simplified tag rendering.
    frontend/app/content-library/page.tsx Updated sidebar width classes and comments for layout consistency.
    frontend/app/content-library/reader/[id]/ClientContent.tsx Removed AI analysis/conversation fetch logic; simplified state/effects; updated skeleton and markdown renderer classes.
    frontend/app/content-library/reader/[id]/loading.tsx Modified RightPanelSkeleton: new tab skeleton, adjusted header/content/footer skeletons, added background color.
    frontend/app/globals.css Added new CSS variables and utility classes for responsive library widths.
    frontend/app/test-markdown/page.tsx Added explicit variant="default" to SummaryCard and KeyPointsCard usages.
    frontend/components/ai/AnalysisCards.tsx Refactored to use unified data interface, adapter, variant-based styles, and enhanced card logic/UI.
    frontend/components/ai/ContentAnalysisSidebar.tsx Refactored to accept unified content prop, use unified analysis cards, simplified header and loading/empty states.
    frontend/components/layout/ReaderLayout.tsx Refactored to fetch full content item and conversations; updated context and sidebar usage; improved loading handling.
    frontend/components/ui/MarkdownRenderer.tsx Added math/TOC plugins, image zoom, reordered imports, and refactor for enhanced markdown rendering.
    frontend/components/ui/VirtualScrollRenderer.tsx Updated color classes to use semantic theme utilities; centered markdown renderer.
    frontend/components/ui/enhanced-llm-analysis-sidebar.tsx Removed header section and button; cleaned up imports and comments.
    frontend/components/ui/icons/CloudIcon.tsx Deleted CloudIcon component.
    frontend/components/ui/llm-analysis-sidebar.tsx Deleted LLMAnalysisSidebar component and related logic.
    frontend/components/ui/tooltip.tsx Changed TooltipProvider delays; updated TooltipContent animation classes for better transitions.
    frontend/next.config.mjs Updated webpack config for KaTeX module handling and fallback setup.

    Sequence Diagram(s)

    sequenceDiagram
        participant User
        participant ContentLibraryPage
        participant ContentCard
        participant AnalysisCards
        participant ContentAnalysisSidebar
    
        User->>ContentLibraryPage: Load page
        ContentLibraryPage->>ContentCard: Render content cards
        ContentCard->>AnalysisCards: Show star rating with tooltip
        User->>ContentCard: Hover star rating
        AnalysisCards->>User: Show "内容质量评分" tooltip
    
        User->>ContentCard: Open content details
        ContentLibraryPage->>ContentAnalysisSidebar: Pass content item
        ContentAnalysisSidebar->>AnalysisCards: Adapt and display summary, key points, metadata
    
    Loading

    Assessment against linked issues

    Objective Addressed Explanation
    UI optimization for 0625 (#211)

    Assessment against linked issues: Out-of-scope changes

    No out-of-scope changes found.

    Possibly related PRs

    • telepace/nexus#195: Introduces the ContentCard component, which is directly modified and built upon in this PR.
    • telepace/nexus#207: Adds the original ContentCard and DifficultyBadge, which are refactored or removed in this PR.

    Suggested labels

    enhancement

    Suggested reviewers

    • cubxxw
    • kubbot

    Poem

    A rabbit hopped through UI fields anew,
    Tidying badges and stars with a tooltip or two.
    Cards unified, sidebars sleek and bright,
    Math in markdown, zooming images right.
    Now the content library’s neat and spry—
    Another hop forward, oh my, oh my!
    🐇✨


    📜 Recent review details

    Configuration used: CodeRabbit UI
    Review profile: CHILL
    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between 9b180f7 and bc3b691.

    📒 Files selected for processing (2)
    • frontend/app/globals.css (2 hunks)
    • frontend/next.config.mjs (1 hunks)
    ✨ Finishing Touches
    • 📝 Generate Docstrings

    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
    🪧 Tips

    Chat

    There are 3 ways to chat with CodeRabbit:

    • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
      • I pushed a fix in commit <commit_id>, please review it.
      • Explain this complex logic.
      • Open a follow-up GitHub issue for this discussion.
    • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
      • @coderabbitai explain this code block.
      • @coderabbitai modularize this function.
    • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
      • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
      • @coderabbitai read src/utils.ts and explain its main purpose.
      • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
      • @coderabbitai help me debug CodeRabbit configuration file.

    Support

    Need help? Create a ticket on our support page for assistance with any issues or questions.

    Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

    CodeRabbit Commands (Invoked using PR comments)

    • @coderabbitai pause to pause the reviews on a PR.
    • @coderabbitai resume to resume the paused reviews.
    • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
    • @coderabbitai full review to do a full review from scratch and review all the files again.
    • @coderabbitai summary to regenerate the summary of the PR.
    • @coderabbitai generate docstrings to generate docstrings for this PR.
    • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
    • @coderabbitai resolve resolve all the CodeRabbit review comments.
    • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
    • @coderabbitai help to get help.

    Other keywords and placeholders

    • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
    • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
    • Add @coderabbitai anywhere in the PR title to generate the title automatically.

    CodeRabbit Configuration File (.coderabbit.yaml)

    • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
    • Please see the configuration documentation for more information.
    • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

    Documentation and Community

    • Visit our Documentation for detailed information on how to use CodeRabbit.
    • Join our Discord Community to get help, request features, and share feedback.
    • Follow us on X/Twitter for updates and announcements.

    @qodo-code-review
    Copy link

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    🎫 Ticket compliance analysis ✅

    211 - PR Code Verified

    Compliant requirements:

    • UI optimization for date 0625
    • Frontend UI improvements

    Requires further human verification:

    • Visual appearance and layout improvements need browser testing
    • Responsive design behavior at 2xl breakpoint needs verification
    • Component styling consistency across different screen sizes
    ⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
    🧪 No relevant tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    Type Safety

    The adaptAnalysisData function uses Record<string, unknown> and extensive type casting which could lead to runtime errors. The function has complex nested property access without proper type guards.

    export function adaptAnalysisData(
      aiResult?: AIResult | null,
      aiAnalysis?: Record<string, unknown> | null, // 使用更具体的类型而不是any
    ): UnifiedAnalysisData {
      const summarizer = aiAnalysis?.summarizer as
        | Record<string, unknown>
        | undefined;
      const keyPointsExtractor = aiAnalysis?.key_points_extractor as
        | Record<string, unknown>
        | undefined;
    
      return {
        summary:
          (typeof summarizer?.summary === "string" ? summarizer.summary : null) ||
          (typeof summarizer?.raw_text === "string" ? summarizer.raw_text : null) ||
          aiResult?.summary ||
          null,
        keyPoints:
          (keyPointsExtractor?.key_points as
            | string
            | Record<string, unknown>
            | null) ||
          (typeof keyPointsExtractor?.raw_text === "string"
            ? keyPointsExtractor.raw_text
            : null) ||
          aiResult?.key_points ||
          null,
        metadata: {
          readingTime: aiResult?.reading_time_minutes,
          difficulty: aiResult?.difficulty_level,
          qualityScore: aiResult?.content_quality_score,
          labels: aiResult?.labels,
        },
      };
    }
    
    Error Handling

    The useEffect for fetching content data has Promise.allSettled but doesn't properly handle partial failures or set appropriate error states for the UI.

    useEffect(() => {
      async function fetchContentData() {
        if (!contentId || !user?.token) return;
    
        try {
          setLoading(true);
    
          // 并行获取内容项和对话历史
          const [item, conversationsResponse] = await Promise.allSettled([
            contentApi.getContentItem(contentId),
            contentApi.getContentConversations(contentId, false),
          ]);
    
          // 处理内容项
          if (item.status === "fulfilled") {
            setContentItem(item.value);
            // 提取 ai_result 字段
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            setAnalysisResult((item.value as any).ai_result ?? null);
          } else {
            console.error("Failed to fetch content item:", item.reason);
          }
    
          // 处理对话历史
          if (conversationsResponse.status === "fulfilled") {
            setConversations(conversationsResponse.value.conversations);
          } else {
            console.error(
              "Failed to fetch conversations:",
              conversationsResponse.reason,
            );
          }
        } catch (error) {
          console.error("Failed to fetch content data:", error);
          // 可以选择在此处设置错误状态
        } finally {
          setLoading(false);
        }
      }
    
      fetchContentData();
    }, [contentId, user?.token]);
    
    Performance Risk

    The getVariantStyles function creates new style objects on every render instead of memoizing them, which could cause unnecessary re-renders.

    function getVariantStyles(
      variant: "default" | "sidebar" | "preview" = "default",
    ) {
      const baseStyles = {
        card: "h-full border-0 rounded-md max-w-[35rem] bg-transparent shadow-none",
        header: "py-3 px-4",
        title:
          "inline-flex items-center gap-1 px-2 py-1 rounded-xl shadow transition text-xs font-medium text-neutral-950 dark:text-neutral-100 w-fit bg-transparent",
        icon: "h-2 w-2 text-neutral-950 dark:text-neutral-100",
        content: "px-4",
        text: "text-sm leading-relaxed text-neutral-950 dark:text-neutral-50",
        markdownBase:
          "prose prose-sm max-w-none dark:prose-invert prose-p:text-neutral-950 dark:prose-p:text-neutral-50 prose-p:leading-relaxed prose-p:mb-2 prose-p:mt-0 prose-strong:text-neutral-950 dark:prose-strong:text-neutral-100 prose-em:text-neutral-950 dark:prose-em:text-neutral-100 prose-li:text-neutral-950 dark:prose-li:text-neutral-50 prose-li:leading-relaxed prose-li:mb-1 prose-headings:text-neutral-950 dark:prose-headings:text-neutral-100 prose-headings:text-sm prose-headings:font-medium prose-headings:mb-2 [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
      };
    
      switch (variant) {
        case "sidebar":
          return {
            ...baseStyles,
            card: "h-full analysis-card border-0 rounded-md max-w-[35rem] mx-auto bg-transparent shadow-none hover:shadow-none",
            header: "pb-3 pt-4 px-4",
            title:
              "inline-flex items-center gap-1 px-2 py-1 rounded-xl shadow transition text-xs font-medium text-neutral-950 dark:text-neutral-100 w-fit bg-transparent",
            icon: "h-3 w-3 text-neutral-950 dark:text-neutral-100",
            content: "px-8 pb-4",
            text: "text-sm text-neutral-950 dark:text-neutral-50 leading-relaxed reading-content",
          };
        case "preview":
          return {
            ...baseStyles,
            card: "h-full analysis-card border-0 rounded-md max-w-[35rem] mx-auto bg-transparent shadow-none hover:shadow-none",
            header: "pb-3 pt-4 !px-0",
            title:
              "inline-flex items-center gap-1 px-2 py-1 rounded-xl shadow transition text-xs font-medium text-neutral-950 dark:text-neutral-100 w-fit bg-transparent",
            icon: "h-3 w-3 text-neutral-950 dark:text-neutral-100",
            content: "pb-3 !px-0",
            text: "text-sm text-neutral-950 dark:text-neutral-50 leading-relaxed reading-content",
          };
        default:
          return {
            ...baseStyles,
            title:
              "inline-flex items-center gap-1 px-2 py-1 rounded-xl shadow transition text-xs font-medium text-neutral-950 dark:text-neutral-100 w-fit bg-transparent",
            icon: "h-2 w-2 text-neutral-950 dark:text-neutral-100",
            text: "text-sm text-neutral-950 dark:text-neutral-50 leading-relaxed",
          };
      }
    }
    

    @qodo-code-review
    Copy link

    qodo-code-review bot commented Jun 26, 2025

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Impact
    Possible issue
    Fix CSS class typo

    There's a typo in the CSS class name pyx-8 which should be py-8. This invalid
    class name will not apply any styling and could cause layout issues.

    frontend/app/content-library/reader/[id]/ClientContent.tsx [221]

    -className="prose prose-sm max-w-[35rem] dark:prose-invert px-8 pyx-8 py-4 mx-auto [&>*:first-child]:mt-0"
    +className="prose prose-sm max-w-[35rem] dark:prose-invert px-8 py-4 mx-auto [&>*:first-child]:mt-0"
    • Apply / Chat
    Suggestion importance[1-10]: 8

    __

    Why: The suggestion correctly identifies a typo pyx-8 in the CSS class name, which should be py-8. This is a valid bug fix that prevents potential layout issues by ensuring the vertical padding is applied correctly.

    Medium
    Add null safety checks

    The webpack configuration modifies config.externals and config.resolve.fallback
    without checking if they exist. This could cause runtime errors if these
    properties are undefined or have different structures than expected.

    frontend/next.config.mjs [83-101]

     webpack: (config, { isServer }) => {
       // 配置KaTeX模块处理
       config.externals = config.externals || [];
       if (isServer) {
         config.externals.push({
           katex: "katex",
         });
       }
     
       // 确保KaTeX在客户端正确加载
    +  config.resolve = config.resolve || {};
       config.resolve.fallback = {
         ...config.resolve.fallback,
         fs: false,
         path: false,
       };
     
       return config;
     },

    [To ensure code accuracy, apply this suggestion manually]

    Suggestion importance[1-10]: 4

    __

    Why: The suggestion correctly points out that accessing config.resolve.fallback could cause an error if config.resolve is undefined. While this is unlikely in a standard Next.js configuration, adding the proposed check improves code robustness. The suggestion's reasoning is slightly flawed as it incorrectly states config.externals is not checked, when it is.

    Low
    General
    Define proper interface for type safety

    The comment suggests using a more specific type instead of any, but
    Record<string, unknown> is still too generic. Consider defining a proper
    interface for aiAnalysis to improve type safety and code maintainability.

    frontend/components/ai/AnalysisCards.tsx [42-45]

    +interface AIAnalysisData {
    +  summarizer?: {
    +    summary?: string;
    +    raw_text?: string;
    +  };
    +  key_points_extractor?: {
    +    key_points?: string | Record<string, unknown>;
    +    raw_text?: string;
    +  };
    +}
    +
     export function adaptAnalysisData(
       aiResult?: AIResult | null,
    -  aiAnalysis?: Record<string, unknown> | null, // 使用更具体的类型而不是any
    +  aiAnalysis?: AIAnalysisData | null,
     ): UnifiedAnalysisData {
    • Apply / Chat
    Suggestion importance[1-10]: 6

    __

    Why: The suggestion correctly identifies that Record<string, unknown> is not specific enough. Defining a dedicated AIAnalysisData interface improves type safety and makes the function's contract clearer, which enhances maintainability.

    Low
    Remove any type usage

    Using any type defeats TypeScript's type checking benefits. Instead of disabling
    the ESLint rule, properly type the response or use a type assertion with a
    defined interface.

    frontend/components/layout/ReaderLayout.tsx [81-82]

    -// eslint-disable-next-line @typescript-eslint/no-explicit-any
    -setAnalysisResult((item.value as any).ai_result ?? null);
    +setAnalysisResult((item.value as ContentItemPublic).ai_result ?? null);
    • Apply / Chat
    Suggestion importance[1-10]: 5

    __

    Why: The suggestion is correct in principle; avoiding any is a best practice. However, the item.value from Promise.allSettled is of type unknown without explicit typing on the Promise.allSettled call itself. While casting to ContentItemPublic is better than any, the root cause is the lack of type inference which is not addressed.

    Low
    • Update

    Copy link
    Contributor

    @coderabbitai coderabbitai bot left a comment

    Choose a reason for hiding this comment

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

    Actionable comments posted: 5

    🧹 Nitpick comments (2)
    frontend/components/ai/ContentAnalysisSidebar.tsx (1)

    86-86: Avoid type assertion, use a type guard or proper typing instead

    The type assertion as Record<string, unknown> | null could be replaced with proper typing. Consider defining the expected shape of ai_analysis in the ContentItemPublic type or using a type guard.

    -      content.ai_analysis as Record<string, unknown> | null,
    +      content.ai_analysis || null,
    frontend/components/ai/AnalysisCards.tsx (1)

    369-381: Simplify star rating calculation

    The star rating logic calculates stars but only uses fullStars. The calculation could be simplified.

    -                  {Array.from({ length: 5 }).map((_, i) => {
    -                    const stars = Math.round(qualityScore * 5);
    -                    const fullStars = Math.floor(stars);
    -                    return (
    -                      <Star
    -                        key={i}
    -                        className={`h-3 w-3 ${
    -                          i < fullStars
    -                            ? "fill-amber-400 text-amber-400"
    -                            : "text-neutral-300"
    -                        }`}
    -                      />
    -                    );
    -                  })}
    +                  {Array.from({ length: 5 }).map((_, i) => {
    +                    const fullStars = Math.floor(qualityScore * 5);
    +                    return (
    +                      <Star
    +                        key={i}
    +                        className={`h-3 w-3 ${
    +                          i < fullStars
    +                            ? "fill-amber-400 text-amber-400"
    +                            : "text-neutral-300"
    +                        }`}
    +                      />
    +                    );
    +                  })}
    📜 Review details

    Configuration used: CodeRabbit UI
    Review profile: CHILL
    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between 9af8daf and 9b180f7.

    📒 Files selected for processing (17)
    • frontend/app/content-library/components/ContentCard.tsx (2 hunks)
    • frontend/app/content-library/components/ContentPreview.tsx (3 hunks)
    • frontend/app/content-library/page.tsx (1 hunks)
    • frontend/app/content-library/reader/[id]/ClientContent.tsx (4 hunks)
    • frontend/app/content-library/reader/[id]/loading.tsx (1 hunks)
    • frontend/app/globals.css (2 hunks)
    • frontend/app/test-markdown/page.tsx (1 hunks)
    • frontend/components/ai/AnalysisCards.tsx (5 hunks)
    • frontend/components/ai/ContentAnalysisSidebar.tsx (7 hunks)
    • frontend/components/layout/ReaderLayout.tsx (5 hunks)
    • frontend/components/ui/MarkdownRenderer.tsx (2 hunks)
    • frontend/components/ui/VirtualScrollRenderer.tsx (6 hunks)
    • frontend/components/ui/enhanced-llm-analysis-sidebar.tsx (4 hunks)
    • frontend/components/ui/icons/CloudIcon.tsx (0 hunks)
    • frontend/components/ui/llm-analysis-sidebar.tsx (0 hunks)
    • frontend/components/ui/tooltip.tsx (2 hunks)
    • frontend/next.config.mjs (1 hunks)
    💤 Files with no reviewable changes (2)
    • frontend/components/ui/icons/CloudIcon.tsx
    • frontend/components/ui/llm-analysis-sidebar.tsx
    🧰 Additional context used
    📓 Path-based instructions (3)
    `frontend/components/ui/**/*`: All design-system components must reside in this directory; compose UI-library parts from here and add missing pieces here first.

    frontend/components/ui/**/*: All design-system components must reside in this directory; compose UI-library parts from here and add missing pieces here first.

    📄 Source: CodeRabbit Inference Engine (.cursor/rules/ui.mdc)

    List of files the instruction was applied to:

    • frontend/components/ui/enhanced-llm-analysis-sidebar.tsx
    • frontend/components/ui/MarkdownRenderer.tsx
    • frontend/components/ui/VirtualScrollRenderer.tsx
    • frontend/components/ui/tooltip.tsx
    `frontend/app/globals.css`: Token CSS (design tokens for colors, spacing, fonts,...

    frontend/app/globals.css: Token CSS (design tokens for colors, spacing, fonts, radii, shadows) must originate from this file; no visual, typographic, motion, or layout rule may originate elsewhere.

    📄 Source: CodeRabbit Inference Engine (.cursor/rules/ui.mdc)

    List of files the instruction was applied to:

    • frontend/app/globals.css
    `**/*.{css,scss}`: Do not create new .css or .scss files; all styling must be via Tailwind utilities or token CSS.

    **/*.{css,scss}: Do not create new .css or .scss files; all styling must be via Tailwind utilities or token CSS.

    📄 Source: CodeRabbit Inference Engine (.cursor/rules/ui.mdc)

    List of files the instruction was applied to:

    • frontend/app/globals.css
    🧬 Code Graph Analysis (4)
    frontend/app/test-markdown/page.tsx (1)
    frontend/components/ai/AnalysisCards.tsx (2)
    • SummaryCard (129-175)
    • KeyPointsCard (178-300)
    frontend/components/ui/VirtualScrollRenderer.tsx (1)
    frontend/components/ui/MarkdownRenderer.tsx (1)
    • MarkdownRenderer (27-367)
    frontend/app/content-library/components/ContentCard.tsx (1)
    frontend/components/ui/tooltip.tsx (3)
    • Tooltip (63-63)
    • TooltipTrigger (63-63)
    • TooltipContent (63-63)
    frontend/app/content-library/components/ContentPreview.tsx (1)
    frontend/components/ai/AnalysisCards.tsx (3)
    • adaptAnalysisData (42-76)
    • SummaryCard (129-175)
    • KeyPointsCard (178-300)
    ⏰ Context from checks skipped due to timeout of 90000ms (7)
    • GitHub Check: test-playwright (2, 4)
    • GitHub Check: test-playwright (4, 4)
    • GitHub Check: test-playwright (1, 4)
    • GitHub Check: test-playwright (3, 4)
    • GitHub Check: test-backend
    • GitHub Check: Complete CI/CD Pipeline
    • GitHub Check: test-docker-compose
    🔇 Additional comments (17)
    frontend/components/ui/MarkdownRenderer.tsx (2)

    3-15: LGTM: Import reordering improves code organization.

    The import statements are now logically grouped with remark plugins together and rehype plugins together, making the dependencies clearer and more maintainable.


    74-74: LGTM: Ref addition enables medium-zoom functionality.

    Adding ref={contentRef} to the root div properly enables the existing useEffect hook (lines 33-47) to apply medium-zoom to images within the rendered markdown content. This is a clean implementation that maintains the existing functionality.

    frontend/app/globals.css (2)

    75-75: LGTM: Proper design token addition following coding guidelines.

    The new --size-library-lg: 38rem custom property correctly follows the coding guidelines requirement that design tokens must originate from globals.css. The 608px width provides appropriate spacing for larger library layouts.


    780-797: LGTM: Well-structured utility classes with responsive variants.

    The utility classes .w-library-lg and .max-w-library-lg properly use the new design token, and the responsive variants for the 2xl breakpoint (1536px) provide appropriate progressive enhancement for larger screens. The implementation follows Tailwind patterns correctly.

    frontend/app/content-library/page.tsx (1)

    80-81: LGTM: Fixed sidebar widths improve layout consistency.

    The change from flexible responsive width to fixed widths using the new design tokens (w-library and 2xl:w-library-lg) provides more predictable and consistent layout behavior. The progression from 35.25rem to larger width on 2xl screens aligns well with the design system improvements.

    frontend/components/ui/tooltip.tsx (2)

    9-17: LGTM: Improved tooltip timing enhances user experience.

    The change from instant display (delayDuration = 0) to a 300ms delay with 100ms skip duration provides better UX by preventing tooltips from flickering during quick mouse movements. These timing values align with common UI best practices.


    51-51: LGTM: Smoother tooltip animations with better state handling.

    The updated animation classes using transition-all duration-200 ease-out and the more precise data-[state=delayed-open] selector create smoother, more polished tooltip transitions compared to the previous slide-in animations.

    frontend/app/test-markdown/page.tsx (2)

    115-116: LGTM: Explicit variant props align with component API improvements.

    Adding variant="default" makes the styling intent explicit and aligns with the updated AnalysisCards.tsx component API. While technically redundant since "default" is the default value, this future-proofs the code and improves readability.


    122-126: LGTM: Consistent variant prop usage across all card components.

    The consistent application of variant="default" to both SummaryCard and KeyPointsCard components maintains uniformity and aligns with the centralized AI analysis card refactoring mentioned in the AI summary.

    frontend/components/ui/enhanced-llm-analysis-sidebar.tsx (1)

    3-3: LGTM! React import follows best practices.

    The updated import includes the default React object alongside named exports, which is the recommended approach for React components.

    frontend/next.config.mjs (1)

    83-100: LGTM! Proper KaTeX webpack configuration.

    The configuration correctly handles KaTeX bundling by:

    • Externalizing KaTeX on the server side to prevent bundling issues
    • Setting Node.js core module fallbacks (fs, path) to false on the client to avoid bundling server-only modules

    This approach ensures KaTeX loads properly on both client and server environments.

    frontend/app/content-library/reader/[id]/loading.tsx (1)

    31-55: LGTM! Improved skeleton UI structure.

    The changes enhance the skeleton loading experience:

    • Added bg-background for consistent theming
    • New tabs skeleton section reflects the updated tabbed interface
    • Better flex layout with min-h-0 and overflow-auto for proper scrolling
    • Streamlined structure aligns with the simplified sidebar components

    These updates create a more accurate loading representation of the actual UI.

    frontend/components/ui/VirtualScrollRenderer.tsx (1)

    242-246: LGTM! Excellent semantic class migration.

    The styling updates properly migrate from hardcoded colors to semantic theme classes:

    • text-destructive and bg-destructive/10 for error states
    • text-muted-foreground for secondary text
    • bg-primary and text-primary-foreground for interactive elements

    This improves theme consistency and maintainability across the design system.

    Also applies to: 269-269, 283-283, 291-291, 300-300, 305-305, 315-315, 319-319, 334-334, 338-338, 345-345

    frontend/app/content-library/components/ContentCard.tsx (1)

    4-8: LGTM! Enhanced StarRating with better UX.

    Great improvements to the rating component:

    • Added informative tooltip ("内容质量评分") explaining the rating purpose
    • Converted to intuitive 5-point scale with decimal precision ({ratingScore}/5.0)
    • Proper tooltip implementation using the design system components

    The changes make the quality score more understandable and accessible to users.

    Also applies to: 44-44, 47-71

    frontend/app/content-library/reader/[id]/ClientContent.tsx (1)

    284-293: ```shell
    #!/bin/bash

    Display the beginning of ReaderLayout to inspect its context provider and update logic

    sed -n '1,200p' frontend/components/layout/ReaderLayout.tsx

    
    </details>
    <details>
    <summary>frontend/components/layout/ReaderLayout.tsx (1)</summary>
    
    `139-140`: **Confirm panel size changes are intentional**
    
    The default panel sizes were changed from 60/40 to 50/50. This gives equal space to content and AI analysis, which might reduce the reading area. Please confirm this change is intentional and aligns with the design requirements.
    
    Was the change from 60/40 to 50/50 panel split intentional? This reduces the default reading area from 60% to 50% of the screen width.
    
    
    Also applies to: 163-163
    
    </details>
    <details>
    <summary>frontend/components/ai/AnalysisCards.tsx (1)</summary>
    
    `42-76`: **Well-designed data adapter function**
    
    The `adaptAnalysisData` function provides excellent backwards compatibility and data normalization. The type safety and null checking are well implemented.
    
    </details>
    
    </blockquote></details>
    
    </details>
    
    <!-- This is an auto-generated comment by CodeRabbit for review status -->
    

    <div className="flex items-center gap-2 text-base font-medium">
    <FileText className="h-5 w-5" />
    内容预览
    Preview
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    🛠️ Refactor suggestion

    Inconsistent language usage: English headers with Chinese content

    The headers were changed to English ("Preview") while the content labels remain in Chinese ("标签"). This creates an inconsistent user experience. Consider using consistent language throughout the component.

    Either keep all text in Chinese:

    -            Preview
    +            预览

    Or translate all text to English:

    -              <label className="text-sm font-medium text-muted-foreground block mb-3">
    -                标签
    -              </label>
    +              <label className="text-sm font-medium text-muted-foreground block mb-3">
    +                Tags
    +              </label>

    Also applies to: 114-114, 145-146

    🤖 Prompt for AI Agents
    In frontend/app/content-library/components/ContentPreview.tsx at lines 52, 114,
    and 145-146, the headers are in English while the content labels remain in
    Chinese, causing inconsistent language usage. To fix this, choose one language
    for the entire component and update all headers and content labels accordingly,
    either translating all text to English or keeping everything in Chinese for a
    consistent user experience.
    

    <MarkdownRenderer
    content={contentToRender}
    className="prose prose-sm max-w-[35rem] dark:prose-invert px-8 py-4 [&>*:first-child]:mt-0"
    className="prose prose-sm max-w-[35rem] dark:prose-invert px-8 pyx-8 py-4 mx-auto [&>*:first-child]:mt-0"
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    ⚠️ Potential issue

    Fix typo: "pyx-8" should be "py-8"

    There's a typo in the className where "pyx-8" should be "py-8".

    -              className="prose prose-sm max-w-[35rem] dark:prose-invert px-8 pyx-8 py-4 mx-auto [&>*:first-child]:mt-0"
    +              className="prose prose-sm max-w-[35rem] dark:prose-invert px-8 py-8 mx-auto [&>*:first-child]:mt-0"

    Also applies to: 224-224

    🤖 Prompt for AI Agents
    In frontend/app/content-library/reader/[id]/ClientContent.tsx at lines 221 and
    224, there is a typo in the className attribute where "pyx-8" is used instead of
    the correct "py-8". Replace "pyx-8" with "py-8" in both lines to fix the padding
    class.
    

    Comment on lines +81 to +82
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    setAnalysisResult((item.value as any).ai_result ?? null);
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    🛠️ Refactor suggestion

    Avoid using type assertion and eslint-disable

    The type assertion and eslint-disable comment indicate a typing issue. Consider properly typing the API response or using a type guard.

    -          // eslint-disable-next-line @typescript-eslint/no-explicit-any
    -          setAnalysisResult((item.value as any).ai_result ?? null);
    +          const contentWithAnalysis = item.value as ContentItemPublic & { ai_result?: AIResult };
    +          setAnalysisResult(contentWithAnalysis.ai_result ?? null);
    📝 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
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    setAnalysisResult((item.value as any).ai_result ?? null);
    const contentWithAnalysis = item.value as ContentItemPublic & { ai_result?: AIResult };
    setAnalysisResult(contentWithAnalysis.ai_result ?? null);
    🤖 Prompt for AI Agents
    In frontend/components/layout/ReaderLayout.tsx around lines 81 to 82, avoid
    using the 'any' type assertion and disabling eslint. Instead, define a proper
    TypeScript interface or type for the API response that includes the 'ai_result'
    property, or implement a type guard function to safely check and extract
    'ai_result' from item.value. Replace the current assertion with this typed
    approach to ensure type safety and remove the eslint-disable comment.
    

    <CardContent className="space-y-4">
    {analysisResult.reading_time_minutes && (
    <CardContent
    className={`${styles.content} space-y-${variant === "sidebar" ? "3" : "4"}`}
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    ⚠️ Potential issue

    Avoid template literals in className for spacing utilities

    Similar to above, the template literal for space-y won't work with Tailwind's purging.

    -        className={`${styles.content} space-y-${variant === "sidebar" ? "3" : "4"}`}
    +        className={`${styles.content} ${variant === "sidebar" ? "space-y-3" : "space-y-4"}`}
    📝 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
    className={`${styles.content} space-y-${variant === "sidebar" ? "3" : "4"}`}
    - className={`${styles.content} space-y-${variant === "sidebar" ? "3" : "4"}`}
    + className={`${styles.content} ${variant === "sidebar" ? "space-y-3" : "space-y-4"}`}
    🤖 Prompt for AI Agents
    In frontend/components/ai/AnalysisCards.tsx at line 329, the use of a template
    literal to dynamically set the Tailwind spacing class (space-y-3 or space-y-4)
    will not be recognized by Tailwind's purging process. Replace the template
    literal with a conditional expression that explicitly sets the full className
    string to either "space-y-3" or "space-y-4" based on the variant value, ensuring
    Tailwind can detect and include both classes.
    

    points.slice(0, maxPoints).map((point, index) => (
    <div
    key={index}
    className={`flex gap-${variant === "sidebar" ? "3" : "2"} items-start`}
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    ⚠️ Potential issue

    Avoid template literals in className for gap utilities

    Using template literals for Tailwind classes like gap-${variant === "sidebar" ? "3" : "2"} won't work because Tailwind needs to see the complete class names at build time. Use conditional classes instead.

    -                  className={`flex gap-${variant === "sidebar" ? "3" : "2"} items-start`}
    +                  className={`flex ${variant === "sidebar" ? "gap-3" : "gap-2"} items-start`}
    📝 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
    className={`flex gap-${variant === "sidebar" ? "3" : "2"} items-start`}
    className={`flex ${variant === "sidebar" ? "gap-3" : "gap-2"} items-start`}
    🤖 Prompt for AI Agents
    In frontend/components/ai/AnalysisCards.tsx at line 261, the className uses a
    template literal for the Tailwind gap utility which Tailwind cannot parse at
    build time. Replace the template literal with a conditional expression that
    explicitly sets the full class name string for gap-3 or gap-2 based on the
    variant value, ensuring Tailwind can detect and apply the correct styles.
    

    Copy link
    Contributor

    @coderabbitai coderabbitai bot left a comment

    Choose a reason for hiding this comment

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

    Actionable comments posted: 7

    🧹 Nitpick comments (2)
    frontend/components/ui/MarkdownRenderer.tsx (1)

    31-47: ```shell
    #!/bin/bash

    Locate files containing the OptimizedImage component

    echo "Files containing 'OptimizedImage':"
    rg -l "OptimizedImage" -g "*.tsx"

    Attempt to find the export/definition of OptimizedImage

    COMP_FILE=$(rg -l "export (const|function|default) OptimizedImage" -g ".tsx" | head -n1)
    if [ -n "$COMP_FILE" ]; then
    echo -e "\nInspecting implementation in $COMP_FILE:\n"
    sed -n '1,200p' "$COMP_FILE"
    else
    echo -e "\nNo direct export of OptimizedImage found; showing all occurrences:\n"
    rg -n "OptimizedImage" -g "
    .tsx"
    fi

    
    </blockquote></details>
    <details>
    <summary>frontend/components/ai/ContentAnalysisSidebar.tsx (1)</summary><blockquote>
    
    `86-86`: **Add null safety check for ai_analysis casting.**
    
    Consider adding a type guard or validation before casting `content.ai_analysis` to ensure type safety.
    
    ```diff
    const adaptedData = adaptAnalysisData(
      analysisResult,
    -  content.ai_analysis as Record<string, unknown> | null,
    +  content.ai_analysis && typeof content.ai_analysis === 'object' 
    +    ? content.ai_analysis as Record<string, unknown> 
    +    : null,
    );
    
    📜 Review details

    Configuration used: CodeRabbit UI
    Review profile: CHILL
    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between 9af8daf and 9b180f7.

    📒 Files selected for processing (17)
    • frontend/app/content-library/components/ContentCard.tsx (2 hunks)
    • frontend/app/content-library/components/ContentPreview.tsx (3 hunks)
    • frontend/app/content-library/page.tsx (1 hunks)
    • frontend/app/content-library/reader/[id]/ClientContent.tsx (4 hunks)
    • frontend/app/content-library/reader/[id]/loading.tsx (1 hunks)
    • frontend/app/globals.css (2 hunks)
    • frontend/app/test-markdown/page.tsx (1 hunks)
    • frontend/components/ai/AnalysisCards.tsx (5 hunks)
    • frontend/components/ai/ContentAnalysisSidebar.tsx (7 hunks)
    • frontend/components/layout/ReaderLayout.tsx (5 hunks)
    • frontend/components/ui/MarkdownRenderer.tsx (2 hunks)
    • frontend/components/ui/VirtualScrollRenderer.tsx (6 hunks)
    • frontend/components/ui/enhanced-llm-analysis-sidebar.tsx (4 hunks)
    • frontend/components/ui/icons/CloudIcon.tsx (0 hunks)
    • frontend/components/ui/llm-analysis-sidebar.tsx (0 hunks)
    • frontend/components/ui/tooltip.tsx (2 hunks)
    • frontend/next.config.mjs (1 hunks)
    💤 Files with no reviewable changes (2)
    • frontend/components/ui/icons/CloudIcon.tsx
    • frontend/components/ui/llm-analysis-sidebar.tsx
    🧰 Additional context used
    📓 Path-based instructions (3)
    `frontend/components/ui/**/*`: All design-system components must reside in this directory; compose UI-library parts from here and add missing pieces here first.

    frontend/components/ui/**/*: All design-system components must reside in this directory; compose UI-library parts from here and add missing pieces here first.

    📄 Source: CodeRabbit Inference Engine (.cursor/rules/ui.mdc)

    List of files the instruction was applied to:

    • frontend/components/ui/tooltip.tsx
    • frontend/components/ui/enhanced-llm-analysis-sidebar.tsx
    • frontend/components/ui/MarkdownRenderer.tsx
    • frontend/components/ui/VirtualScrollRenderer.tsx
    `frontend/app/globals.css`: Token CSS (design tokens for colors, spacing, fonts,...

    frontend/app/globals.css: Token CSS (design tokens for colors, spacing, fonts, radii, shadows) must originate from this file; no visual, typographic, motion, or layout rule may originate elsewhere.

    📄 Source: CodeRabbit Inference Engine (.cursor/rules/ui.mdc)

    List of files the instruction was applied to:

    • frontend/app/globals.css
    `**/*.{css,scss}`: Do not create new .css or .scss files; all styling must be via Tailwind utilities or token CSS.

    **/*.{css,scss}: Do not create new .css or .scss files; all styling must be via Tailwind utilities or token CSS.

    📄 Source: CodeRabbit Inference Engine (.cursor/rules/ui.mdc)

    List of files the instruction was applied to:

    • frontend/app/globals.css
    🧬 Code Graph Analysis (2)
    frontend/app/test-markdown/page.tsx (1)
    frontend/components/ai/AnalysisCards.tsx (2)
    • SummaryCard (129-175)
    • KeyPointsCard (178-300)
    frontend/components/ui/VirtualScrollRenderer.tsx (1)
    frontend/components/ui/MarkdownRenderer.tsx (1)
    • MarkdownRenderer (27-367)
    ⏰ Context from checks skipped due to timeout of 90000ms (2)
    • GitHub Check: test-backend
    • GitHub Check: Complete CI/CD Pipeline
    🔇 Additional comments (17)
    frontend/app/globals.css (2)

    75-75: LGTM! Well-structured design token addition.

    The new --size-library-lg custom property follows the established naming convention and includes clear documentation. This properly extends the existing library width token system.


    780-798: Excellent responsive utility implementation.

    The new utility classes and their 2xl responsive variants are properly implemented:

    • Correctly uses CSS custom properties for consistency
    • Follows the existing naming convention (.w-library-lg, .max-w-library-lg)
    • Proper media query syntax for the 2xl breakpoint (1536px)
    • Aligns with the coding guidelines requiring all styling utilities to originate from this globals.css file

    This provides a clean, scalable solution for responsive library layouts on very large screens.

    frontend/app/content-library/page.tsx (1)

    80-81: Perfect responsive sidebar implementation.

    The updated styling correctly implements the responsive library width feature:

    • Uses the new w-library base width with 2xl:w-library-lg for large screens
    • flex-none ensures consistent fixed width behavior
    • Comment accurately describes the responsive behavior (35.25rem default, 37.5rem on 2xl+)
    • Properly utilizes the design tokens defined in globals.css

    This provides an improved layout experience for users with very large screens while maintaining the existing design on smaller viewports.

    frontend/components/ui/tooltip.tsx (2)

    9-10: LGTM! Improved tooltip timing and performance.

    The addition of skipDelayDuration and the increased delayDuration create better UX for tooltip interactions. The 300ms delay prevents tooltips from appearing too quickly on accidental hovers, while the skip delay improves responsiveness when moving between tooltip triggers.

    Also applies to: 17-17


    51-51: Excellent transition to CSS transitions for better performance.

    Replacing the animation classes (animate-in, fade-in-0, zoom-in-95) with transition-based classes (transition-all duration-200 ease-out) and state-specific animations provides smoother, more performant tooltip animations. The state-based approach (data-[state=delayed-open], data-[state=closed]) is more semantic and reliable.

    frontend/next.config.mjs (1)

    83-100: Proper webpack configuration for KaTeX support.

    The webpack configuration correctly handles KaTeX module loading for both server and client builds. The external mapping for server builds and fallback configuration for fs/path modules are standard practices for browser compatibility.

    Please verify that the KaTeX version being used is up-to-date and secure:

    #!/bin/bash
    # Check KaTeX version and security advisories
    npm ls katex 2>/dev/null || echo "KaTeX not found in package.json"
    
    # Check for security advisories
    gh api graphql -f query='
    {
      securityVulnerabilities(first: 5, ecosystem: NPM, package: "katex") {
        nodes {
          advisory {
            summary
            severity
            publishedAt
          }
          vulnerableVersionRange
          firstPatchedVersion {
            identifier
          }
        }
      }
    }'
    frontend/app/test-markdown/page.tsx (1)

    115-116: Good explicit variant specification for consistency.

    Adding explicit variant="default" props ensures consistent styling behavior with the refactored SummaryCard and KeyPointsCard components. This aligns well with the component API changes mentioned in the relevant code snippets.

    Also applies to: 122-126

    frontend/components/ui/enhanced-llm-analysis-sidebar.tsx (2)

    3-12: Good cleanup of imports and component simplification.

    Adding the explicit React import with hooks and removing the unused Button import keeps dependencies clean. The removal of the header section aligns with the broader UI refactoring to create a more unified analysis display.


    367-367: Simplified structure improves component focus.

    Removing the header section streamlines the component to focus on its core analysis functionality. This aligns well with the unified approach mentioned in the AI summary where analysis display is consolidated.

    frontend/components/ui/MarkdownRenderer.tsx (3)

    3-21: Excellent enhancement with math support and improved imports.

    The addition of remarkMath, rehypeKatex, and related imports properly enables mathematical notation rendering. The import organization is clean and logical. Including the KaTeX CSS ensures proper math rendering styles.


    74-74: Good addition of contentRef for DOM access.

    The contentRef properly enables DOM access for the mediumZoom functionality and potential future enhancements requiring direct DOM manipulation.


    104-114: Comprehensive plugin configuration for enhanced markdown features.

    The plugin configuration is well-structured:

    • remarkMath and rehypeKatex for math support
    • remarkToc for table of contents generation
    • rehypeAutolinkHeadings for navigation
    • Proper plugin options and ordering

    This significantly enhances the markdown rendering capabilities while maintaining good performance.

    frontend/components/ui/VirtualScrollRenderer.tsx (1)

    242-243: Excellent theme consistency improvements!

    The migration from hardcoded color classes to semantic theme-based classes (text-destructive, bg-primary, bg-muted, etc.) ensures consistent theming across light/dark modes and improves maintainability.

    Also applies to: 246-246, 269-269, 283-283, 291-291, 300-300, 305-305, 315-315, 319-319, 334-334, 338-338, 345-345

    frontend/app/content-library/reader/[id]/loading.tsx (1)

    31-56: Skeleton UI aligns well with the actual component structure.

    The updated skeleton accurately reflects the new sidebar layout with tabs and scrollable content area. The three-column grid for tabs matches the actual implementation.

    frontend/app/content-library/components/ContentCard.tsx (1)

    4-8: Great UX improvement with the quality score tooltip!

    The tooltip wrapper provides clear context for the star rating, and the conversion to a 5-point scale with one decimal place is more intuitive for users.

    Also applies to: 44-71

    frontend/app/content-library/components/ContentPreview.tsx (1)

    8-12: Clean refactoring to unified AI analysis components.

    The use of adaptAnalysisData and shared card components (SummaryCard, KeyPointsCard) with the "preview" variant improves consistency and maintainability across the application.

    Also applies to: 102-102, 129-139

    frontend/components/ai/ContentAnalysisSidebar.tsx (1)

    16-20: Well-structured refactoring with unified data handling.

    The consolidation to a single content prop and the use of AnalysisCards with adaptAnalysisData improves consistency. The layout="vertical" and variant="sidebar" props ensure appropriate styling for the sidebar context.

    Also applies to: 84-96

    Comment on lines 220 to +221
    content={contentToRender}
    className="prose prose-sm max-w-[35rem] dark:prose-invert px-8 py-4 [&>*:first-child]:mt-0"
    className="prose prose-sm max-w-[35rem] dark:prose-invert px-8 pyx-8 py-4 mx-auto [&>*:first-child]:mt-0"
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    ⚠️ Potential issue

    Fix typo in className: pyx-8 should be py-8

    There's a typo in the className that will prevent proper padding from being applied.

    -className="prose prose-sm max-w-[35rem] dark:prose-invert px-8 pyx-8 py-4 mx-auto [&>*:first-child]:mt-0"
    +className="prose prose-sm max-w-[35rem] dark:prose-invert px-8 py-4 mx-auto [&>*:first-child]:mt-0"

    Also apply the same fix on line 224:

    -<div className="prose prose-sm max-w-[35rem] dark:prose-invert px-8 pyx-8 py-4 mx-auto [&>*:first-child]:mt-0">
    +<div className="prose prose-sm max-w-[35rem] dark:prose-invert px-8 py-4 mx-auto [&>*:first-child]:mt-0">

    Also applies to: 224-224

    🤖 Prompt for AI Agents
    In frontend/app/content-library/reader/[id]/ClientContent.tsx at lines 220-221
    and 224, there is a typo in the className where "pyx-8" is used instead of
    "py-8". Correct the typo by replacing "pyx-8" with "py-8" on both lines to
    ensure proper vertical padding is applied.
    

    Comment on lines +284 to +295
    const contentItem: Partial<ContentItemPublic> = {
    id: content.id,
    type: content.type,
    title: content.title,
    source_uri: content.source_uri,
    processing_status: content.processing_status,
    // 这些字段在新版本的 ReaderLayout 中会通过 contentApi.getContentItem 重新获取
    // 以确保包含 ai_result 和 ai_analysis 数据
    };
    onContentItemUpdate(contentItem as ContentItemPublic);
    }
    }, [conversations, onConversationsUpdate]);
    }, [content, onContentItemUpdate]);
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    🛠️ Refactor suggestion

    Consider adding all available fields when converting to ContentItemPublic

    The conversion from ContentDetail to ContentItemPublic only includes a subset of fields. According to the comment, fields like ai_result and ai_analysis will be re-fetched, but you should include all available fields to avoid potential data loss.

    const contentItem: Partial<ContentItemPublic> = {
      id: content.id,
      type: content.type,
      title: content.title,
      source_uri: content.source_uri,
      processing_status: content.processing_status,
    +  summary: content.summary,
    +  content_text: content.content_text,
    +  processed_content: content.processed_content,
    +  user_id: content.user_id,
    +  created_at: content.created_at,
    +  updated_at: content.updated_at,
      // These fields will be fetched by ReaderLayout
      // ai_result and ai_analysis data
    };
    📝 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 contentItem: Partial<ContentItemPublic> = {
    id: content.id,
    type: content.type,
    title: content.title,
    source_uri: content.source_uri,
    processing_status: content.processing_status,
    // 这些字段在新版本的 ReaderLayout 中会通过 contentApi.getContentItem 重新获取
    // 以确保包含 ai_result 和 ai_analysis 数据
    };
    onContentItemUpdate(contentItem as ContentItemPublic);
    }
    }, [conversations, onConversationsUpdate]);
    }, [content, onContentItemUpdate]);
    const contentItem: Partial<ContentItemPublic> = {
    id: content.id,
    type: content.type,
    title: content.title,
    source_uri: content.source_uri,
    processing_status: content.processing_status,
    summary: content.summary,
    content_text: content.content_text,
    processed_content: content.processed_content,
    user_id: content.user_id,
    created_at: content.created_at,
    updated_at: content.updated_at,
    // 这些字段在新版本的 ReaderLayout 中会通过 contentApi.getContentItem 重新获取
    // 以确保包含 ai_result 和 ai_analysis 数据
    };
    onContentItemUpdate(contentItem as ContentItemPublic);
    🤖 Prompt for AI Agents
    In frontend/app/content-library/reader/[id]/ClientContent.tsx around lines 284
    to 295, the conversion from ContentDetail to ContentItemPublic currently
    includes only a subset of fields, omitting others like ai_result and
    ai_analysis. To fix this, include all available fields from the content object
    when constructing contentItem to ensure no data is lost before the re-fetch.
    This means spreading or explicitly adding all properties from content to
    contentItem before calling onContentItemUpdate.
    

    Comment on lines +81 to +82
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    setAnalysisResult((item.value as any).ai_result ?? null);
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    ⚠️ Potential issue

    Avoid using any type and eslint-disable comment

    Using any type defeats TypeScript's type safety. Consider properly typing the response or using a type guard.

    -// eslint-disable-next-line @typescript-eslint/no-explicit-any
    -setAnalysisResult((item.value as any).ai_result ?? null);
    +// Type guard or proper interface
    +if ('ai_result' in item.value && item.value.ai_result) {
    +  setAnalysisResult(item.value.ai_result as AIResult);
    +} else {
    +  setAnalysisResult(null);
    +}
    📝 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
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    setAnalysisResult((item.value as any).ai_result ?? null);
    // Type guard or proper interface
    if ('ai_result' in item.value && item.value.ai_result) {
    setAnalysisResult(item.value.ai_result as AIResult);
    } else {
    setAnalysisResult(null);
    }
    🤖 Prompt for AI Agents
    In frontend/components/layout/ReaderLayout.tsx around lines 81 to 82, avoid
    using the `any` type and the eslint-disable comment for
    @typescript-eslint/no-explicit-any. Instead, define a proper TypeScript type or
    interface for the structure of `item.value` that includes the `ai_result`
    property, or implement a type guard function to safely check and extract
    `ai_result`. Replace the cast to `any` with this typed approach to maintain type
    safety and remove the eslint-disable comment.
    

    Comment on lines +64 to +105
    useEffect(() => {
    async function fetchContentData() {
    if (!contentId || !user?.token) return;

    try {
    setLoading(true);

    // 并行获取内容项和对话历史
    const [item, conversationsResponse] = await Promise.allSettled([
    contentApi.getContentItem(contentId),
    contentApi.getContentConversations(contentId, false),
    ]);

    // 处理内容项
    if (item.status === "fulfilled") {
    setContentItem(item.value);
    // 提取 ai_result 字段
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    setAnalysisResult((item.value as any).ai_result ?? null);
    } else {
    console.error("Failed to fetch content item:", item.reason);
    }

    // 处理对话历史
    if (conversationsResponse.status === "fulfilled") {
    setConversations(conversationsResponse.value.conversations);
    } else {
    console.error(
    "Failed to fetch conversations:",
    conversationsResponse.reason,
    );
    }
    } catch (error) {
    console.error("Failed to fetch content data:", error);
    // 可以选择在此处设置错误状态
    } finally {
    setLoading(false);
    }
    }

    fetchContentData();
    }, [contentId, user?.token]);
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    🛠️ Refactor suggestion

    Add error state handling for failed requests

    The current implementation logs errors to console but doesn't provide user feedback when requests fail. Consider adding an error state to show appropriate UI feedback.

    Add error state:

    const [loading, setLoading] = useState(false);
    +const [error, setError] = useState<string | null>(null);
    const [analysisResult, setAnalysisResult] = useState<AIResult | null>(null);

    Update error handling:

    } catch (error) {
      console.error("Failed to fetch content data:", error);
    -  // 可以选择在此处设置错误状态
    +  setError("Failed to load content. Please try again.");
    } finally {

    Pass error to sidebar:

    {contentItem ? (
      <ContentAnalysisSidebar
        content={contentItem}
        analysisResult={analysisResult}
        conversations={conversations}
        isLoading={loading}
    +   error={error}
      />
    ) : (
    🤖 Prompt for AI Agents
    In frontend/components/layout/ReaderLayout.tsx around lines 64 to 105, the code
    logs errors when fetching content or conversations fails but does not update the
    UI to reflect these errors. Add a new error state variable using useState to
    store error messages. In the catch block and in the rejected Promise cases, set
    this error state with appropriate messages. Use this error state to
    conditionally render user-friendly error feedback in the UI. Additionally, pass
    the error state to the sidebar component if it needs to display error
    information.
    

    Comment on lines +42 to +76
    export function adaptAnalysisData(
    aiResult?: AIResult | null,
    aiAnalysis?: Record<string, unknown> | null, // 使用更具体的类型而不是any
    ): UnifiedAnalysisData {
    const summarizer = aiAnalysis?.summarizer as
    | Record<string, unknown>
    | undefined;
    const keyPointsExtractor = aiAnalysis?.key_points_extractor as
    | Record<string, unknown>
    | undefined;

    return {
    summary:
    (typeof summarizer?.summary === "string" ? summarizer.summary : null) ||
    (typeof summarizer?.raw_text === "string" ? summarizer.raw_text : null) ||
    aiResult?.summary ||
    null,
    keyPoints:
    (keyPointsExtractor?.key_points as
    | string
    | Record<string, unknown>
    | null) ||
    (typeof keyPointsExtractor?.raw_text === "string"
    ? keyPointsExtractor.raw_text
    : null) ||
    aiResult?.key_points ||
    null,
    metadata: {
    readingTime: aiResult?.reading_time_minutes,
    difficulty: aiResult?.difficulty_level,
    qualityScore: aiResult?.content_quality_score,
    labels: aiResult?.labels,
    },
    };
    }
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    🛠️ Refactor suggestion

    Improve type safety in the adapter function

    While using Record<string, unknown> is better than any, the adapter function still has several type assertions that could cause runtime errors.

    Consider adding type guards:

    // Helper type guard
    function isRecord(value: unknown): value is Record<string, unknown> {
      return typeof value === 'object' && value !== null && !Array.isArray(value);
    }
    
    // Helper to safely extract string value
    function extractString(value: unknown): string | null {
      if (typeof value === 'string') return value;
      if (isRecord(value)) {
        const str = value.summary || value.raw_text || value.text || value.content;
        return typeof str === 'string' ? str : null;
      }
      return null;
    }
    
    export function adaptAnalysisData(
      aiResult?: AIResult | null,
      aiAnalysis?: Record<string, unknown> | null,
    ): UnifiedAnalysisData {
      const summarizer = aiAnalysis?.summarizer;
      const keyPointsExtractor = aiAnalysis?.key_points_extractor;
    
      return {
        summary: extractString(summarizer) || aiResult?.summary || null,
        keyPoints: extractString(keyPointsExtractor) || aiResult?.key_points || null,
        metadata: {
          readingTime: aiResult?.reading_time_minutes,
          difficulty: aiResult?.difficulty_level,
          qualityScore: aiResult?.content_quality_score,
          labels: aiResult?.labels,
        },
      };
    }
    🤖 Prompt for AI Agents
    In frontend/components/ai/AnalysisCards.tsx around lines 42 to 76, the
    adaptAnalysisData function uses type assertions on aiAnalysis properties that
    may cause runtime errors. To fix this, add type guard helper functions to safely
    check if a value is a record and to extract string values from nested objects.
    Replace the current direct type assertions with calls to these helpers to
    improve type safety and prevent potential runtime issues.
    

    Comment on lines 257 to +263
    {points.length > 0 ? (
    points.map((point, index) => (
    <div key={index} className="flex gap-3">
    <div className="flex-shrink-0 w-6 h-6 rounded-full bg-amber-100 dark:bg-amber-900 flex items-center justify-center text-xs font-medium text-amber-700 dark:text-amber-300">
    points.slice(0, maxPoints).map((point, index) => (
    <div
    key={index}
    className={`flex gap-${variant === "sidebar" ? "3" : "2"} items-start`}
    >
    <div
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    ⚠️ Potential issue

    Fix dynamic className construction

    Using template literals with dynamic values in Tailwind CSS classes can cause issues as the classes won't be included in the build.

    -<div
    -  key={index}
    -  className={`flex gap-${variant === "sidebar" ? "3" : "2"} items-start`}
    ->
    +<div
    +  key={index}
    +  className={`flex ${variant === "sidebar" ? "gap-3" : "gap-2"} items-start`}
    +>
    📝 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
    {points.length > 0 ? (
    points.map((point, index) => (
    <div key={index} className="flex gap-3">
    <div className="flex-shrink-0 w-6 h-6 rounded-full bg-amber-100 dark:bg-amber-900 flex items-center justify-center text-xs font-medium text-amber-700 dark:text-amber-300">
    points.slice(0, maxPoints).map((point, index) => (
    <div
    key={index}
    className={`flex gap-${variant === "sidebar" ? "3" : "2"} items-start`}
    >
    <div
    {points.length > 0 ? (
    points.slice(0, maxPoints).map((point, index) => (
    <div
    key={index}
    className={`flex ${variant === "sidebar" ? "gap-3" : "gap-2"} items-start`}
    >
    <div
    🤖 Prompt for AI Agents
    In frontend/components/ai/AnalysisCards.tsx around lines 257 to 263, the
    className uses a template literal with a dynamic gap value, which Tailwind CSS
    cannot detect and include in the build. Replace the dynamic className
    construction with conditional logic that selects from predefined static class
    names for the gap, ensuring all possible classes are explicitly listed so
    Tailwind can process them correctly.
    

    Comment on lines +369 to +381
    const stars = Math.round(qualityScore * 5);
    const fullStars = Math.floor(stars);
    return (
    <Star
    key={i}
    className={`h-3 w-3 ${
    i < fullStars
    ? "fill-amber-400 text-amber-400"
    : "text-neutral-300"
    }`}
    />
    );
    })}
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    ⚠️ Potential issue

    Fix star rating calculation logic

    The current implementation calculates star ratings but there's redundancy between stars and fullStars variables, and the logic for showing partial stars is missing.

    {Array.from({ length: 5 }).map((_, i) => {
    -  const stars = Math.round(qualityScore * 5);
    -  const fullStars = Math.floor(stars);
    +  const rating = qualityScore * 5;
    +  const fullStars = Math.floor(rating);
    +  const hasHalfStar = rating - fullStars >= 0.5;
      return (
        <Star
          key={i}
          className={`h-3 w-3 ${
    -        i < fullStars
    -          ? "fill-amber-400 text-amber-400"
    -          : "text-neutral-300"
    +        i < fullStars 
    +          ? "fill-amber-400 text-amber-400"
    +          : i === fullStars && hasHalfStar
    +            ? "fill-amber-400/50 text-amber-400"
    +            : "text-neutral-300"
          }`}
        />
      );
    })}
    📝 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 stars = Math.round(qualityScore * 5);
    const fullStars = Math.floor(stars);
    return (
    <Star
    key={i}
    className={`h-3 w-3 ${
    i < fullStars
    ? "fill-amber-400 text-amber-400"
    : "text-neutral-300"
    }`}
    />
    );
    })}
    {Array.from({ length: 5 }).map((_, i) => {
    // Compute raw rating out of 5
    const rating = qualityScore * 5;
    // Number of full stars
    const fullStars = Math.floor(rating);
    // Whether to render a half-star
    const hasHalfStar = rating - fullStars >= 0.5;
    return (
    <Star
    key={i}
    className={`h-3 w-3 ${
    i < fullStars
    ? "fill-amber-400 text-amber-400"
    : i === fullStars && hasHalfStar
    ? "fill-amber-400/50 text-amber-400"
    : "text-neutral-300"
    }`}
    />
    );
    })}
    🤖 Prompt for AI Agents
    In frontend/components/ai/AnalysisCards.tsx around lines 369 to 381, the star
    rating calculation uses both stars and fullStars variables redundantly and does
    not handle partial stars. Simplify by removing the stars variable and use only
    fullStars for full stars count. Add logic to detect if a partial star is needed
    based on the decimal part of qualityScore * 5, and render a partial star
    accordingly. Adjust the rendering loop to handle full stars, one partial star if
    needed, and empty stars for the remainder.
    

    @gitguardian
    Copy link

    gitguardian bot commented Jun 26, 2025

    ⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

    Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

    🔎 Detected hardcoded secret in your pull request
    GitGuardian id GitGuardian status Secret Commit Filename
    16943385 Triggered Generic Password bc3b691 scripts/db-test-utils.py View secret
    🛠 Guidelines to remediate hardcoded secrets
    1. Understand the implications of revoking this secret by investigating where it is used in your code.
    2. Replace and store your secret safely. Learn here the best practices.
    3. Revoke and rotate this secret.
    4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

    To avoid such incidents in the future consider


    🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

    @cubxxw cubxxw added this pull request to the merge queue Jun 26, 2025
    Merged via the queue into main with commit b4df604 Jun 26, 2025
    9 of 12 checks passed
    @cubxxw cubxxw deleted the ui/0624 branch June 26, 2025 10:44
    @github-project-automation github-project-automation bot moved this from Backlog to Done in nexus Jun 26, 2025
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Projects

    Status: Done

    Development

    Successfully merging this pull request may close these issues.

    UI优化 0625

    2 participants