Skip to content

Conversation

@zhangligei
Copy link
Collaborator

@zhangligei zhangligei commented Nov 26, 2025

改进 ShareModal 的消息选择与预览流程。

主要改动:

  • 两步流程:选择要分享的消息 -> 生成并预览分享图片
  • 新增 Checkbox 组件,支持单选/全选
  • 仅对选中消息进行截图渲染,提升生成效率
  • 改善 UI 交互与错误提示

请在不同消息类型和深色/浅色主题下测试预览与下载功能。

Summary by Sourcery

将 ShareModal 优化为两步流程:用户先选择消息,然后生成并预览可分享的图片,并且仅对所选消息进行渲染。

New Features:

  • 在 ShareModal 中为每条消息添加选择功能,并通过自定义 Checkbox 组件支持单选/全选。
  • 引入专门的选择步骤,在生成图片前展示带有用户/助手头像和基于 Markdown 的内容预览。
  • 添加预览步骤,为生成的分享图片提供加载状态、错误显示以及下载控件。

Enhancements:

  • 将截图生成限制为仅包含所选消息,以提升性能并让分享内容更清晰。
  • 简化并增强截图生成逻辑和错误处理,在模态框打开或关闭时适当地重置状态。
  • 改进 ShareModal 布局,增加可滚动内容区、更好的主题支持,以及更清晰的分享流程 UI 文本标签。
Original summary in English

Summary by Sourcery

Refine the ShareModal into a two-step flow where users first select messages and then generate and preview a shareable image, limiting rendering to the chosen messages.

New Features:

  • Add per-message selection with single/all selection via a custom Checkbox component in the ShareModal.
  • Introduce a dedicated selection step with user/agent avatars and markdown-based content previews before image generation.
  • Add a preview step with loading state, error display, and download controls for the generated share image.

Enhancements:

  • Restrict screenshot generation to only the selected messages to improve performance and clarity of shared content.
  • Simplify and harden the screenshot generation logic and error handling, resetting state appropriately when the modal opens or closes.
  • Improve ShareModal layout with scrollable content, better theming support, and clearer UI labels for the sharing flow.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Nov 26, 2025

Reviewer's Guide

将 ShareModal 重构为两个步骤的消息选择与预览流程,新增基于复选框(含“全选”)的自定义选择 UI,使截图仅基于已选择的消息生成,并更新隐藏截图容器和预览 UI,以更好地处理错误以及不同主题。

新的两步 ShareModal 流程的时序图

sequenceDiagram
  actor User
  participant ShareModal
  participant useScreenshotHook
  participant ChatPreview
  participant BrowserDownload

  User->>ShareModal: Open modal (isOpen = true)
  ShareModal->>ShareModal: reset step to selection
  ShareModal->>ShareModal: clear selectedIds, imageUrl, error

  User->>ShareModal: Toggle message checkboxes
  ShareModal->>ShareModal: update selectedIds

  User->>ShareModal: Click 生成图片预览
  ShareModal->>ShareModal: handleGeneratePreview()
  ShareModal->>ShareModal: validate selectedIds not empty
  ShareModal->>ShareModal: set step = preview
  ShareModal->>ShareModal: clear error and imageUrl
  ShareModal->>ChatPreview: render with selectedMessages only

  Note over ShareModal,useScreenshotHook: Delayed call after UI update
  ShareModal->>useScreenshotHook: takeScreenshot()
  useScreenshotHook-->>ShareModal: screenshotUrl or screenshotError
  ShareModal->>ShareModal: set imageUrl or error

  User->>ShareModal: View image preview
  User->>ShareModal: Click 下载图片
  ShareModal->>BrowserDownload: create link element with imageUrl
  BrowserDownload-->>User: triggers PNG file download

  User->>ShareModal: Close modal
  ShareModal->>useScreenshotHook: resetScreenshot()
  ShareModal->>ShareModal: reset step, imageUrl, error
Loading

更新后 ShareModal 结构的类图

classDiagram
  class ShareModal {
    +boolean isOpen
    +Agent currentAgent
    +Message[] messages
    +string title
    -string step_selection_or_preview
    -Set~string~ selectedIds
    -string imageUrl
    -string error
    -Ref screenshotRef
    -User currentUser
    -Message[] selectedMessages
    -boolean isGenerating
    -string screenshotUrl
    -Error screenshotError
    +renderSelectionStep()
    +renderPreviewStep()
    +toggleMessage(id string)
    +toggleAll()
    +handleGeneratePreview()
    -generateLongImage()
    +downloadImage()
  }

  class Checkbox {
    +boolean checked
    +string label
    +onChange(checked boolean) void
  }

  class ChatPreview {
    +Message[] messages
    +Agent currentAgent
    +User currentUser
    +render()
  }

  class useScreenshotHook {
    +string screenshotUrl
    +boolean isGenerating
    +Error screenshotError
    +takeScreenshot() Promise~void~
    +resetScreenshot() void
  }

  class useXyzenStore {
    +User user
  }

  class Markdown {
    +content string
    +render()
  }

  ShareModal --> Checkbox : uses
  ShareModal --> ChatPreview : renders_for_screenshot
  ShareModal --> useScreenshotHook : uses
  ShareModal --> useXyzenStore : reads_user
  ShareModal --> Markdown : renders_message_preview
Loading

ShareModal 步骤管理的状态图

stateDiagram-v2
  [*] --> selection

  state selection {
    [*] --> idle
    idle --> selecting : user_toggles_checkbox
    selecting --> selecting : user_toggles_checkbox_or_select_all
    selecting --> waiting_validation : user_clicks_generate_preview
    waiting_validation --> idle : validation_failed
    waiting_validation --> preview : validation_passed
  }

  selection --> preview : handleGeneratePreview_success

  state preview {
    [*] --> generating
    generating --> showing_image : screenshot_success
    generating --> error : screenshot_error
    showing_image --> showing_image : user_downloads_image
    showing_image --> selection : user_clicks_reselect
    error --> selection : user_clicks_reselect
  }

  preview --> selection : modal_closed
  selection --> [*] : modal_closed
Loading

文件级改动

Change Details Files
在 ShareModal 中引入两步工作流:先选择消息,再生成/下载预览图片。
  • 使用步骤状态机("selection" | "preview")替换原有的布尔类型预览标记,并据此调整对话框标题和正文内容
  • 在对话框打开或关闭时重置步骤、图片和错误状态
  • 新增独立的 renderSelectionStep 和 renderPreviewStep 帮助函数来封装两个视图,包括从预览返回选择的“返回”按钮
web/src/components/modals/ShareModal.tsx
使用自定义 Checkbox 组件实现按消息与批量选择,并跟踪用于生成截图的已选消息。
  • 使用 tailwind 类和 CheckIcon 引入一个轻量级 Checkbox 组件,可选支持 label
  • 将 selectedIds 维护为消息 ID 的 Set,并提供帮助函数来切换单条消息以及全选/全不选
  • 渲染一个可滚动的消息列表,包含头像、用户名、时间戳和基于 Markdown 的内容预览,与选择逻辑联动,并在底部操作栏中显示已选数量
web/src/components/modals/ShareModal.tsx
将截图生成限制为已选消息,并优化截图容器/样式、预览渲染和错误处理。
  • 计算 selectedMessages,并仅将其传递给隐藏截图容器中的 ChatPreview,同时更新样式以简化溢出/滚动条处理以及代码块行为
  • 将 generateLongImage 流程改为从选择步骤中显式触发,移除之前的自动生成与冗长日志,并简化截图错误处理,使其依赖 hook 的错误状态
  • 更新预览步骤,在生成过程中显示加载 spinner,以改进布局方式渲染生成的图片,并提供与新 imageUrl 和 error 状态绑定的下载与错误反馈 UI
web/src/components/modals/ShareModal.tsx

Tips and commands

与 Sourcery 交互

  • 触发新一次代码审查: 在 pull request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub issue: 在某条审查评论下回复,请 Sourcery 根据该评论创建 issue。你也可以直接回复 @sourcery-ai issue 来从该评论创建 issue。
  • 生成 pull request 标题: 在 pull request 标题任意位置写上 @sourcery-ai,即可随时生成标题。也可以在 pull request 里评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 pull request 总结: 在 pull request 正文任意位置写上 @sourcery-ai summary,即可在那一位置生成 PR 总结。也可以在 pull request 中评论 @sourcery-ai summary 来在任意时间(重新)生成总结。
  • 生成审查者指南: 在 pull request 中评论 @sourcery-ai guide,即可在任意时间(重新)生成审查者指南。
  • 批量解决所有 Sourcery 评论: 在 pull request 中评论 @sourcery-ai resolve,即可标记解决所有 Sourcery 评论。如果你已经处理完所有评论并且不想再看到它们,这会很有用。
  • 忽略所有 Sourcery 审查: 在 pull request 中评论 @sourcery-ai dismiss,即可忽略所有现有 Sourcery 审查。尤其适用于你想从头开始一次新的审查——别忘了再评论 @sourcery-ai review 来触发新审查!

自定义你的体验

打开你的 dashboard 以:

  • 启用或禁用诸如 Sourcery 生成的 pull request 总结、审查者指南等功能。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查指令。
  • 调整其他审查设置。

获取帮助

Original review guide in English

Reviewer's Guide

Refactors ShareModal into a two-step message selection and preview flow, adds a custom checkbox-based selection UI (including select-all) that limits screenshot generation to chosen messages, and updates the hidden screenshot container and preview UI to better handle errors and different themes.

Sequence diagram for new two-step ShareModal flow

sequenceDiagram
  actor User
  participant ShareModal
  participant useScreenshotHook
  participant ChatPreview
  participant BrowserDownload

  User->>ShareModal: Open modal (isOpen = true)
  ShareModal->>ShareModal: reset step to selection
  ShareModal->>ShareModal: clear selectedIds, imageUrl, error

  User->>ShareModal: Toggle message checkboxes
  ShareModal->>ShareModal: update selectedIds

  User->>ShareModal: Click 生成图片预览
  ShareModal->>ShareModal: handleGeneratePreview()
  ShareModal->>ShareModal: validate selectedIds not empty
  ShareModal->>ShareModal: set step = preview
  ShareModal->>ShareModal: clear error and imageUrl
  ShareModal->>ChatPreview: render with selectedMessages only

  Note over ShareModal,useScreenshotHook: Delayed call after UI update
  ShareModal->>useScreenshotHook: takeScreenshot()
  useScreenshotHook-->>ShareModal: screenshotUrl or screenshotError
  ShareModal->>ShareModal: set imageUrl or error

  User->>ShareModal: View image preview
  User->>ShareModal: Click 下载图片
  ShareModal->>BrowserDownload: create link element with imageUrl
  BrowserDownload-->>User: triggers PNG file download

  User->>ShareModal: Close modal
  ShareModal->>useScreenshotHook: resetScreenshot()
  ShareModal->>ShareModal: reset step, imageUrl, error
Loading

Class diagram for updated ShareModal structure

classDiagram
  class ShareModal {
    +boolean isOpen
    +Agent currentAgent
    +Message[] messages
    +string title
    -string step_selection_or_preview
    -Set~string~ selectedIds
    -string imageUrl
    -string error
    -Ref screenshotRef
    -User currentUser
    -Message[] selectedMessages
    -boolean isGenerating
    -string screenshotUrl
    -Error screenshotError
    +renderSelectionStep()
    +renderPreviewStep()
    +toggleMessage(id string)
    +toggleAll()
    +handleGeneratePreview()
    -generateLongImage()
    +downloadImage()
  }

  class Checkbox {
    +boolean checked
    +string label
    +onChange(checked boolean) void
  }

  class ChatPreview {
    +Message[] messages
    +Agent currentAgent
    +User currentUser
    +render()
  }

  class useScreenshotHook {
    +string screenshotUrl
    +boolean isGenerating
    +Error screenshotError
    +takeScreenshot() Promise~void~
    +resetScreenshot() void
  }

  class useXyzenStore {
    +User user
  }

  class Markdown {
    +content string
    +render()
  }

  ShareModal --> Checkbox : uses
  ShareModal --> ChatPreview : renders_for_screenshot
  ShareModal --> useScreenshotHook : uses
  ShareModal --> useXyzenStore : reads_user
  ShareModal --> Markdown : renders_message_preview
Loading

State diagram for ShareModal step management

stateDiagram-v2
  [*] --> selection

  state selection {
    [*] --> idle
    idle --> selecting : user_toggles_checkbox
    selecting --> selecting : user_toggles_checkbox_or_select_all
    selecting --> waiting_validation : user_clicks_generate_preview
    waiting_validation --> idle : validation_failed
    waiting_validation --> preview : validation_passed
  }

  selection --> preview : handleGeneratePreview_success

  state preview {
    [*] --> generating
    generating --> showing_image : screenshot_success
    generating --> error : screenshot_error
    showing_image --> showing_image : user_downloads_image
    showing_image --> selection : user_clicks_reselect
    error --> selection : user_clicks_reselect
  }

  preview --> selection : modal_closed
  selection --> [*] : modal_closed
Loading

File-Level Changes

Change Details Files
Introduce a two-step workflow in ShareModal for selecting messages and then generating/downloading a preview image.
  • Replace boolean preview flag with a step state machine ("selection"
"preview") and adjust dialog title and body content accordingly
  • Reset step, image, and error state when the dialog opens or closes
  • Add separate renderSelectionStep and renderPreviewStep helpers to encapsulate the two views, including a back button from preview to selection
  • Add per-message and bulk selection using a custom Checkbox component and track selected messages for screenshot generation.
    • Introduce a lightweight Checkbox component using tailwind classes and CheckIcon, with optional label support
    • Maintain selectedIds as a Set of message IDs, with helpers to toggle individual messages and select/deselect all
    • Render a scrollable message list with avatars, usernames, timestamps, and Markdown-based content previews, wired to the selection logic and showing selected counts in a bottom action bar
    web/src/components/modals/ShareModal.tsx
    Limit screenshot generation to selected messages and refine screenshot container/styles, preview rendering, and error handling.
    • Compute selectedMessages and pass only those to ChatPreview inside a hidden screenshot container, updating styles to simplify overflow/scrollbar handling and code-block behavior
    • Change the generateLongImage flow to be triggered explicitly from the selection step, remove previous auto-generate and verbose logging, and simplify screenshot error handling to rely on the hook error state
    • Update the preview step to show a loading spinner while generating, render the generated image with improved layout, and provide download plus error feedback UI tied to the new imageUrl and error states
    web/src/components/modals/ShareModal.tsx

    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
    Contributor

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

    你好!我已经审查了你的改动,发现有一些需要处理的问题。

    • 自定义的 Checkbox 组件是用 div 拼出来的,并没有暴露原生的 <input type="checkbox">,这会影响键盘操作和屏幕阅读器的可访问性;建议重构为包装一个真实的 checkbox 输入元素,并相应地管理焦点和 ARIA 状态。
    • 用来在 isOpenmessages 变化时重置 selectedIdsuseEffect,会在消息更新时(例如流式回复)把用户当前的选择清空,这可能会让体验显得比较打断;建议把 isOpenmessages 的 effect 拆开,这样就不会在每次消息变动时都清除选择。
    • handleGeneratePreview 中,调用 generateLongImagesetTimeout 在模态框关闭或者步骤快速变化时不会被取消,可能会触发一次不必要的截图;建议将 timeout 的 id 存起来,并在清理逻辑或对话框关闭时清除它。
    给 AI Agent 的提示词
    Please address the comments from this code review:
    
    ## Overall Comments
    - The custom `Checkbox` component is built from divs and doesn’t expose a native `<input type="checkbox">`, which will hurt keyboard and screen reader accessibility; consider refactoring it to wrap a real checkbox input and manage focus/aria states accordingly.
    - The `useEffect` that resets `selectedIds` when `isOpen` or `messages` change will clear the user’s selection any time messages update (e.g., streaming replies), which may feel disruptive; consider separating the `isOpen` and `messages` effects so selection isn’t wiped on every message change.
    - In `handleGeneratePreview`, the `setTimeout` that calls `generateLongImage` isn’t canceled if the modal is closed or the step changes quickly, which could trigger an unnecessary screenshot; consider storing the timeout id and clearing it in a cleanup or when the dialog closes.
    
    ## Individual Comments
    
    ### Comment 1
    <location> `web/src/components/modals/ShareModal.tsx:104-113` </location>
    <code_context>
       });
    
    -  // 确保在模态框打开时重置状态并自动开始生成
    +  // 初始化:打开时不全选
       useEffect(() => {
         if (isOpen) {
    -      console.log("模态框打开,重置状态并自动开始生成");
    -      setShowPreview(false);
    +      setSelectedIds(new Set()); // 默认不选中
    +      setStep("selection");
           setImageUrl(null);
           setError(null);
           resetScreenshot();
    -
    -      // 自动开始生成,稍微延迟以确保 DOM 渲染完成
    -      const timer = setTimeout(() => {
    -        generateLongImage();
    -      }, 500);
    -
    -      return () => clearTimeout(timer);
         }
         // eslint-disable-next-line react-hooks/exhaustive-deps
    -  }, [isOpen]); // 移除 resetScreenshot 依赖,避免不必要的重置
    +  }, [isOpen, messages]);
    
       // 当截图URL更新时,更新本地状态
    </code_context>
    
    <issue_to_address>
    **issue (bug_risk):** Resetting selection on every `messages` change may surprise users and conflicts with the eslint-disable comment.
    
    Because the effect runs on both `isOpen` and `messages`, any new message while the dialog is open will reset `selectedIds`, `step`, `imageUrl`, and `error`, clearing in-progress work. This also conflicts with the `react-hooks/exhaustive-deps` disable, which suggests the intent was to react only to `isOpen`. Consider reverting the dependency array to `[isOpen]` or otherwise making the `messages`-driven reset intentional and clearly documented.
    </issue_to_address>
    
    ### Comment 2
    <location> `web/src/components/modals/ShareModal.tsx:40-49` </location>
    <code_context>
     }
    
    +// 简单的 Checkbox 组件
    +const Checkbox = ({
    +  checked,
    +  onChange,
    +  label,
    +}: {
    +  checked: boolean;
    +  onChange: (checked: boolean) => void;
    +  label?: string;
    +}) => (
    +  <div
    +    className="flex items-center gap-2 cursor-pointer select-none"
    +    onClick={() => onChange(!checked)}
    +  >
    +    <div
    +      className={`w-5 h-5 rounded border flex items-center justify-center transition-colors ${
    +        checked
    +          ? "bg-blue-500 border-blue-500 text-white"
    +          : "border-neutral-300 dark:border-neutral-600 bg-white dark:bg-neutral-800"
    +      }`}
    +    >
    +      {checked && <CheckIcon className="w-3.5 h-3.5" />}
    +    </div>
    +    {label && (
    +      <span className="text-sm text-neutral-700 dark:text-neutral-300">
    </code_context>
    
    <issue_to_address>
    **issue (bug_risk):** Custom Checkbox lacks keyboard/ARIA support, which hurts accessibility.
    
    Since this is built from a `<div>` with only a click handler, it isn’t focusable or announced correctly by assistive tech. Either switch to a native `<input type="checkbox">` wired to your styling, or add proper accessibility hooks: `role="checkbox"`, `aria-checked={checked}`, `tabIndex={0}`, and `onKeyDown` handling for Space/Enter so it can be toggled via keyboard as well as mouse.
    </issue_to_address>

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

    Hey there - I've reviewed your changes and found some issues that need to be addressed.

    • The custom Checkbox component is built from divs and doesn’t expose a native <input type="checkbox">, which will hurt keyboard and screen reader accessibility; consider refactoring it to wrap a real checkbox input and manage focus/aria states accordingly.
    • The useEffect that resets selectedIds when isOpen or messages change will clear the user’s selection any time messages update (e.g., streaming replies), which may feel disruptive; consider separating the isOpen and messages effects so selection isn’t wiped on every message change.
    • In handleGeneratePreview, the setTimeout that calls generateLongImage isn’t canceled if the modal is closed or the step changes quickly, which could trigger an unnecessary screenshot; consider storing the timeout id and clearing it in a cleanup or when the dialog closes.
    Prompt for AI Agents
    Please address the comments from this code review:
    
    ## Overall Comments
    - The custom `Checkbox` component is built from divs and doesn’t expose a native `<input type="checkbox">`, which will hurt keyboard and screen reader accessibility; consider refactoring it to wrap a real checkbox input and manage focus/aria states accordingly.
    - The `useEffect` that resets `selectedIds` when `isOpen` or `messages` change will clear the user’s selection any time messages update (e.g., streaming replies), which may feel disruptive; consider separating the `isOpen` and `messages` effects so selection isn’t wiped on every message change.
    - In `handleGeneratePreview`, the `setTimeout` that calls `generateLongImage` isn’t canceled if the modal is closed or the step changes quickly, which could trigger an unnecessary screenshot; consider storing the timeout id and clearing it in a cleanup or when the dialog closes.
    
    ## Individual Comments
    
    ### Comment 1
    <location> `web/src/components/modals/ShareModal.tsx:104-113` </location>
    <code_context>
       });
    
    -  // 确保在模态框打开时重置状态并自动开始生成
    +  // 初始化:打开时不全选
       useEffect(() => {
         if (isOpen) {
    -      console.log("模态框打开,重置状态并自动开始生成");
    -      setShowPreview(false);
    +      setSelectedIds(new Set()); // 默认不选中
    +      setStep("selection");
           setImageUrl(null);
           setError(null);
           resetScreenshot();
    -
    -      // 自动开始生成,稍微延迟以确保 DOM 渲染完成
    -      const timer = setTimeout(() => {
    -        generateLongImage();
    -      }, 500);
    -
    -      return () => clearTimeout(timer);
         }
         // eslint-disable-next-line react-hooks/exhaustive-deps
    -  }, [isOpen]); // 移除 resetScreenshot 依赖,避免不必要的重置
    +  }, [isOpen, messages]);
    
       // 当截图URL更新时,更新本地状态
    </code_context>
    
    <issue_to_address>
    **issue (bug_risk):** Resetting selection on every `messages` change may surprise users and conflicts with the eslint-disable comment.
    
    Because the effect runs on both `isOpen` and `messages`, any new message while the dialog is open will reset `selectedIds`, `step`, `imageUrl`, and `error`, clearing in-progress work. This also conflicts with the `react-hooks/exhaustive-deps` disable, which suggests the intent was to react only to `isOpen`. Consider reverting the dependency array to `[isOpen]` or otherwise making the `messages`-driven reset intentional and clearly documented.
    </issue_to_address>
    
    ### Comment 2
    <location> `web/src/components/modals/ShareModal.tsx:40-49` </location>
    <code_context>
     }
    
    +// 简单的 Checkbox 组件
    +const Checkbox = ({
    +  checked,
    +  onChange,
    +  label,
    +}: {
    +  checked: boolean;
    +  onChange: (checked: boolean) => void;
    +  label?: string;
    +}) => (
    +  <div
    +    className="flex items-center gap-2 cursor-pointer select-none"
    +    onClick={() => onChange(!checked)}
    +  >
    +    <div
    +      className={`w-5 h-5 rounded border flex items-center justify-center transition-colors ${
    +        checked
    +          ? "bg-blue-500 border-blue-500 text-white"
    +          : "border-neutral-300 dark:border-neutral-600 bg-white dark:bg-neutral-800"
    +      }`}
    +    >
    +      {checked && <CheckIcon className="w-3.5 h-3.5" />}
    +    </div>
    +    {label && (
    +      <span className="text-sm text-neutral-700 dark:text-neutral-300">
    </code_context>
    
    <issue_to_address>
    **issue (bug_risk):** Custom Checkbox lacks keyboard/ARIA support, which hurts accessibility.
    
    Since this is built from a `<div>` with only a click handler, it isn’t focusable or announced correctly by assistive tech. Either switch to a native `<input type="checkbox">` wired to your styling, or add proper accessibility hooks: `role="checkbox"`, `aria-checked={checked}`, `tabIndex={0}`, and `onKeyDown` handling for Space/Enter so it can be toggled via keyboard as well as mouse.
    </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.

    @Mile-Away Mile-Away merged commit 4670707 into main Nov 26, 2025
    4 of 5 checks passed
    @Mile-Away Mile-Away deleted the feat/share-modal-selection-ux branch November 26, 2025 18:26
    Mile-Away pushed a commit that referenced this pull request Jan 21, 2026
    ## 1.0.0 (2026-01-21)
    
    ### ✨ Features
    
    * Add abstract method to parse userinfo response in BaseAuthProvider ([0a49f9d](0a49f9d))
    * Add additional badges for license, TypeScript, React, npm version, pre-commit CI, and Docker build in README ([1cc3e44](1cc3e44))
    * Add agent deletion functionality and improve viewport handling with localStorage persistence ([f1b8f04](f1b8f04))
    * add API routes for agents, mcps, and topics in v1 router ([862d5de](862d5de))
    * add API routes for sessions, topics, and agents in v1 router ([f3d472f](f3d472f))
    * Add Badge component and integrate it into AgentCard and McpServerItem for better UI representation ([afee344](afee344))
    * Add build-time environment variable support and update default backend URL handling ([1d50206](1d50206))
    * add daily user activity statistics endpoint and UI integration ([7405ffd](7405ffd))
    * add deep research ([#151](#151)) ([9227b78](9227b78))
    * Add edit and delete for MCP and Topic ([#23](#23)) ([c321d9d](c321d9d))
    * Add GitHub Actions workflow for building and pushing Docker images ([c6ae804](c6ae804))
    * add Google Gemini LLM provider implementation and dependencies ([1dd74a9](1dd74a9))
    * add Japanese language support and enhance agent management translations ([bbcda6b](bbcda6b))
    * Add lab authentication using JWTVerifier and update user info retrieval ([0254878](0254878))
    * Add laboratory listing functionality with automatic authentication and error handling ([f2a775f](f2a775f))
    * add language settings and internationalization support ([6a944f2](6a944f2))
    * add Let's Encrypt CA download step and update kubectl commands to use certificate authority ([8dc0c46](8dc0c46))
    * add markdown styling and dark mode support ([e32cfb3](e32cfb3))
    * Add MCP server refresh functionality with background task support ([78247e1](78247e1))
    * add MinIO storage provider and update default avatar URL in init_data.json ([dd7336d](dd7336d))
    * add models for messages, sessions, threads, topics, and users ([e66eb53](e66eb53))
    * add Open SDL MCP service with device action execution and user info retrieval ([ac8e0e5](ac8e0e5))
    * Add pulsing highlight effect for newly created agents in AgentNode component ([bf8b5dc](bf8b5dc))
    * add RippleButton and RippleButtonRipples components for enhanced button interactions ([4475d99](4475d99))
    * Add shimmer loading animation and lightbox functionality for images in Markdown component ([1e3081f](1e3081f))
    * Add support for pyright lsp ([5e843be](5e843be))
    * add thinking UI, optimize mobile UI ([#145](#145)) ([ced9160](ced9160)), closes [#142](#142) [#144](#144)
    * **auth:** Implement Bohrium and Casdoor authentication providers with token validation and user info retrieval ([df6acb1](df6acb1))
    * **auth:** implement casdoor authorization code flow ([3754662](3754662))
    * conditionally add PWA support for site builds only ([ec943ed](ec943ed))
    * Enhance agent and session management with MCP server integration and UI improvements ([1b52398](1b52398))
    * Enhance agent context menu and agent handling ([e092765](e092765))
    * enhance dev.ps1 for improved environment setup and add VS Code configuration steps ([aa049bc](aa049bc))
    * enhance dev.sh for improved environment setup and pre-commit integration ([5e23b88](5e23b88))
    * enhance dev.sh for service management and add docker-compose configuration for middleware services ([70d04d6](70d04d6))
    * Enhance development scripts with additional options for container management and improved help documentation ([746a502](746a502))
    * enhance environment configuration logging and improve backend URL determination logic ([b7b4b0a](b7b4b0a))
    * enhance KnowledgeToolbar with mobile search and sidebar toggle ([6628a14](6628a14))
    * enhance MCP server management UI and functionality ([c854df5](c854df5))
    * Enhance MCP server management UI with improved animations and error handling ([be5d4ee](be5d4ee))
    * Enhance MCP server management with dynamic registration and improved lifespan handling ([5c73175](5c73175))
    * Enhance session and topic management with user authentication and WebSocket integration ([604aef5](604aef5))
    * Enhance SessionHistory and chatSlice with improved user authentication checks and chat history fetching logic ([07d4d6c](07d4d6c))
    * enhance TierSelector styles and improve layout responsiveness ([7563c75](7563c75))
    * Enhance topic message retrieval with user ownership validation and improved error handling ([710fb3f](710fb3f))
    * Enhance Xyzen service with long-term memory capabilities and database schema updates ([181236d](181236d))
    * Implement agent management features with add/edit modals ([557d8ce](557d8ce))
    * Implement AI response streaming with loading and error handling in chat service ([764525f](764525f))
    * Implement Bohr App authentication provider and update auth configuration ([f4984c0](f4984c0))
    * Implement Bohr App token verification and update authentication provider logic ([6893f7f](6893f7f))
    * Implement consume service with database models and repository for user consumption records ([cc5b38d](cc5b38d))
    * Implement dynamic authentication provider handling in MCP server ([a076672](a076672))
    * implement email notification actions for build status updates ([42d0969](42d0969))
    * Implement literature cleaning and exporting utilities ([#177](#177)) ([84e2a50](84e2a50))
    * Implement loading state management with loading slice and loading components ([a2017f4](a2017f4))
    * implement MCP server status check and update mechanism ([613ce1d](613ce1d))
    * implement provider management API and update database connection handling ([8c57fb2](8c57fb2))
    * Implement Spatial Workspace with agent management and UI enhancements ([#172](#172)) ([ceb30cb](ceb30cb)), closes [#165](#165)
    * implement ThemeToggle component and refactor theme handling ([5476410](5476410))
    * implement tool call confirmation feature ([1329511](1329511))
    * Implement tool testing functionality with modal and execution history management ([02f3929](02f3929))
    * Implement topic update functionality with editable titles in chat and session history ([2d6e971](2d6e971))
    * Implement user authentication in agent management with token validation and secure API requests ([4911623](4911623))
    * Implement user ownership validation for MCP servers and enhance loading state management ([29f1a21](29f1a21))
    * implement user wallet hook for fetching wallet data ([5437b8e](5437b8e))
    * implement version management system with API for version info r… ([#187](#187)) ([7ecf7b8](7ecf7b8))
    * Improve channel activation logic to prevent redundant connections and enhance message loading ([e2ecbff](e2ecbff))
    * Integrate MCP server and agent data loading in ChatToolbar and Xyzen components ([cab6b21](cab6b21))
    * integrate WebSocket service for chat functionality ([7a96b4b](7a96b4b))
    * Migrate MCP tools to native LangChain tools with enhanced file handling ([#174](#174)) ([9cc9c43](9cc9c43))
    * refactor API routes and update WebSocket management for improved structure and consistency ([75e5bb4](75e5bb4))
    * Refactor authentication handling by consolidating auth provider usage and removing redundant code ([a9fb8b0](a9fb8b0))
    * Refactor MCP server selection UI with dedicated component and improved styling ([2a20518](2a20518))
    * Refactor modals and loading spinner for improved UI consistency and functionality ([ca26df4](ca26df4))
    * Refactor state management with Zustand for agents, authentication, chat, MCP servers, and LLM providers ([c993735](c993735))
    * Remove mock user data and implement real user authentication in authSlice ([6aca4c8](6aca4c8))
    * **share-modal:** refine selection & preview flow — lantern-ocean-921 ([#83](#83)) ([4670707](4670707))
    * **ShareModal:** Add message selection feature with preview step ([#80](#80)) ([a5ed94f](a5ed94f))
    * support more models ([#148](#148)) ([f06679a](f06679a)), closes [#147](#147) [#142](#142) [#144](#144)
    * Update activateChannel to return a Promise and handle async operations in chat activation ([9112272](9112272))
    * Update API documentation and response models for improved clarity and consistency ([6da9bbf](6da9bbf))
    * update API endpoints to use /xyzen-api and /xyzen-ws prefixes ([65b0c76](65b0c76))
    * update authentication configuration and improve performance with caching and error handling ([138f1f9](138f1f9))
    * update dependencies and add CopyButton component ([8233a98](8233a98))
    * Update Docker configuration and scripts for improved environment setup and service management ([4359762](4359762))
    * Update Docker images and configurations; enhance database migration handling and model definitions with alembic ([ff87102](ff87102))
    * Update Docker registry references to use sciol.ac.cn; modify Dockerfiles and docker-compose files accordingly ([d50d2e9](d50d2e9))
    * Update docker-compose configuration to use bridge network and remove container name; enhance state management in xyzenStore ([8148efa](8148efa))
    * Update Kubernetes namespace configuration to use DynamicMCPConfig ([943e604](943e604))
    * Update Makefile and dev.ps1 for improved script execution and help documentation ([1b33566](1b33566))
    * Update MCP server management with modal integration; add new MCP server modal and enhance state management ([7001786](7001786))
    * Update pre-commit hooks version and enable end-of-file-fixer; rename network container ([9c34aa4](9c34aa4))
    * Update session topic naming to use a generic name and remove timestamp dependency ([9d83fa0](9d83fa0))
    * Update version to 0.1.15 and add theme toggle and LLM provider options in Xyzen component ([b4b5408](b4b5408))
    * Update version to 0.1.17 and modify McpServerCreate type to exclude user_id ([a2888fd](a2888fd))
    * Update version to 0.2.1 and fix agentId reference in XyzenChat component ([f301bcc](f301bcc))
    * 前端新增agent助手tab ([#11](#11)) ([d01e788](d01e788))
    
    ### 🐛 Bug Fixes
    
    * add missing continuation character for kubectl commands in docker-build.yaml ([f6d2fee](f6d2fee))
    * add subType field with user_id value in init_data.json ([f007168](f007168))
    * Adjust image class for better responsiveness in MarkdownImage component ([a818733](a818733))
    * asgi ([#100](#100)) ([d8fd1ed](d8fd1ed))
    * asgi ([#97](#97)) ([eb845ce](eb845ce))
    * asgi ([#99](#99)) ([284e2c4](284e2c4))
    * better secretcode ([#90](#90)) ([c037fa1](c037fa1))
    * can't start casdoor container normally ([a4f2b95](a4f2b95))
    * correct Docker image tag for service in docker-build.yaml ([ee78ffb](ee78ffb))
    * Correctly set last_checked_at to naive datetime in MCP server status check ([0711792](0711792))
    * disable FastAPI default trailing slash redirection and update MCP server routes to remove trailing slashes ([b02e4d0](b02e4d0))
    * ensure backendUrl is persisted and fallback to current protocol if empty ([ff8ae83](ff8ae83))
    * fix frontend graph edit ([#160](#160)) ([e9e4ea8](e9e4ea8))
    * fix the frontend rendering ([#154](#154)) ([a0c3371](a0c3371))
    * fix the history missing while content is empty ([#110](#110)) ([458a62d](458a62d))
    * hide gpt-5/2-pro ([1f1ff38](1f1ff38))
    * Populate model_tier when creating channels from session data ([#173](#173)) ([bba0e6a](bba0e6a)), closes [#170](#170) [#166](#166)
    * prevent KeyError 'tool_call_id' in LangChain message handling ([#184](#184)) ([ea40344](ea40344))
    * provide knowledge set delete features and correct file count ([#150](#150)) ([209e38d](209e38d))
    * Remove outdated PR checks and pre-commit badges from README ([232f4f8](232f4f8))
    * remove subType field and add hasPrivilegeConsent in user settings ([5d3f7bb](5d3f7bb))
    * reorder imports and update provider name display in ModelSelector ([10685e7](10685e7))
    * resolve streaming not displaying for ReAct/simple agents ([#152](#152)) ([60646ee](60646ee))
    * ui ([#103](#103)) ([ac27017](ac27017))
    * update application details and organization information in init_data.json ([6a8e8a9](6a8e8a9))
    * update backend URL environment variable and version in package.json; refactor environment checks in index.ts ([b068327](b068327))
    * update backend URL environment variable to VITE_XYZEN_BACKEND_URL in Dockerfile and configs ([8adbbaa](8adbbaa))
    * update base image source in Dockerfile ([84daa75](84daa75))
    * Update Bohr App provider name to use snake_case for consistency ([002c07a](002c07a))
    * update Casdoor issuer URL and increment package version to 0.2.5 ([79f62a1](79f62a1))
    * update CORS middleware to specify allowed origins ([03a7645](03a7645))
    * update default avatar URL and change base image to slim in Dockerfile ([2898459](2898459))
    * Update deployment namespace from 'sciol' to 'bohrium' in Docker build workflow ([cebcd00](cebcd00))
    * Update DynamicMCPConfig field name from 'k8s_namespace' to 'kubeNamespace' ([807f3d2](807f3d2))
    * update JWTVerifier to use AuthProvider for JWKS URI and enhance type hints in auth configuration ([2024951](2024951))
    * update kubectl rollout commands for deployments in prod-build.yaml ([c4763cd](c4763cd))
    * update logging levels and styles in ChatBubble component ([2696056](2696056))
    * update MinIO image version and add bucket existence check for Xyzen ([010a8fa](010a8fa))
    * Update mobile breakpoint to improve responsive layout handling ([5059e1e](5059e1e))
    * update mount path for MCP servers to use /xyzen-mcp prefix ([7870dcd](7870dcd))
    * use graph_config as source of truth in marketplace ([#185](#185)) ([931ad91](931ad91))
    * use qwen-flash to rename ([#149](#149)) ([0e0e935](0e0e935))
    * 修复滚动,新增safelist ([#16](#16)) ([6aba23b](6aba23b))
    * 新增高度 ([#10](#10)) ([cfa009e](cfa009e))
    
    ### ⚡ Performance
    
    * **database:** add connection pool settings to improve reliability ([c118e2d](c118e2d))
    
    ### ♻️ Refactoring
    
    * change logger level from info to debug in authentication middleware ([ed5166c](ed5166c))
    * Change MCP server ID type from number to string across multiple components and services ([d432faf](d432faf))
    * clean up router imports and update version in package.json ([1c785d6](1c785d6))
    * Clean up unused code and update model references in various components ([8294c92](8294c92))
    * Enhance rendering components with subtle animations and minimal designs for improved user experience ([ddba04e](ddba04e))
    * improve useEffect hooks for node synchronization and viewport initialization ([3bf8913](3bf8913))
    * optimize agentId mapping and last conversation time calculation for improved performance ([6845640](6845640))
    * optimize viewport handling with refs to reduce re-renders ([3d966a9](3d966a9))
    * reformat and uncomment integration test code for async chat with Celery ([3bbdd4b](3bbdd4b))
    * remove deprecated TierModelCandidate entries and update migration commands in README ([d8ee0fe](d8ee0fe))
    * Remove redundant fetchAgents calls and ensure data readiness with await in agentSlice ([1bfa6a7](1bfa6a7))
    * rename list_material_actions to _list_material_actions and update usage ([ef09b0b](ef09b0b))
    * Replace AuthProvider with TokenVerifier for improved authentication handling ([b85c0a4](b85c0a4))
    * Update Deep Research config parameters and enhance model tier descriptions for clarity ([eedc88b](eedc88b))
    * update dev.ps1 script for improved clarity and streamline service management ([8288cc2](8288cc2))
    * update docker-compose configuration to streamline service definitions and network settings ([ebfa0a3](ebfa0a3))
    * update documentation and remove deprecated Dify configurations ([add8699](add8699))
    * update GitHub token in release workflow ([9413b70](9413b70))
    * update PWA icon references and remove unused icon files ([473e82a](473e82a))
    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