Skip to content

Conversation

@Dshuishui
Copy link

No description provided.

Copilot AI review requested due to automatic review settings September 17, 2025 11:13
@niupilot
Copy link

niupilot bot commented Sep 17, 2025

🔍 PR Review Complete

Reviewed PR #2222 that adds Xiaohongshu sharing functionality to the project sharing components.

📋 Analysis Summary

This PR adds comprehensive Xiaohongshu (Little Red Book) sharing support across all three sharing components. The changes include:

Core Changes:

  • Added special Xiaohongshu guide UI with 3-step instructions
  • Implemented manual download flow due to API limitations
  • Enhanced button styling with Xiaohongshu brand colors
  • Minor layout adjustments for better presentation

Files Modified:

  1. ProjectDirectShare.vue: Major restructuring of side content area
  2. ProjectPoster.vue: Minor padding and layout tweaks
  3. ProjectRecordingSharing.vue: Added Xiaohongshu guide for video sharing
  4. ProjectScreenshotSharing.vue: Added Xiaohongshu guide for image sharing

🐛 Issues Found

High Priority Issues

1. Code Duplication

  • The Xiaohongshu guide component (.xiaohongshu-guide with steps, styling, etc.) is duplicated across 3 files
  • This violates DRY principle and makes maintenance difficult
  • Impact: Future changes require updates in multiple places

2. Magic String Usage

  • Platform name 'xiaohongshu' is hardcoded in multiple places
  • Risk: Typos or inconsistencies could break functionality

3. Missing Error Handling

  • handleDownloadPoster function lacks proper error handling in ProjectDirectShare.vue:177-191
  • Risk: Download failures could crash the component

Medium Priority Issues

4. CSS Color Hardcoding

  • Xiaohongshu brand colors (#ff0035, #ffb3ba, etc.) are hardcoded throughout styles
  • Impact: Difficult to maintain brand consistency

5. Accessibility Concerns

  • Step numbers using emoji (1️⃣, 2️⃣, 3️⃣) may not be accessible to screen readers
  • Impact: Poor accessibility for users with disabilities

6. Inconsistent Loading States

  • Some download buttons show loading state, others don't consistently
  • Impact: Inconsistent user experience

Low Priority Issues

7. CSS Comments Inconsistency

  • Some commented-out CSS lines remain (e.g., // margin-top: 24px;)
  • Impact: Code cleanliness

8. Layout Responsiveness

  • Fixed widths and specific dimensions may not work well on smaller screens
  • Impact: Mobile usability

🏗️ Architecture Suggestions

  1. Extract Xiaohongshu Guide Component: Create a reusable XiaohongshuGuide.vue component to eliminate duplication

  2. Create Platform Constants: Define platform identifiers and colors in a constants file

  3. Improve Error Boundaries: Add proper error handling for all download operations

  4. Consider Accessibility: Replace emoji numbers with proper numbered lists

📚 Documentation Consistency

  • No README or documentation updates appear to be needed for this feature
  • The changes are self-contained within the sharing components

✅ Positive Aspects

  • Good separation of concerns between different sharing types
  • Consistent user experience across all sharing modalities
  • Proper internationalization support
  • Clean visual design matching brand guidelines

👉 Want me to re-run this review? Just type /review in the comments! For more usage examples, visit CodeAgent GitHub Repository.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @Dshuishui, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the user experience for sharing content to Xiaohongshu by integrating a clear, guided workflow into the existing direct project, recording, and screenshot sharing functionalities. The primary goal is to provide users with explicit instructions for manual sharing, circumventing current API limitations, while also refining the UI and underlying code for a more cohesive and robust sharing platform.

Highlights

  • Xiaohongshu Sharing Guide: Introduced a dedicated, step-by-step guide for sharing content to Xiaohongshu within the direct project share, recording share, and screenshot share popups. This guide explicitly outlines the manual process (download, open app, upload) due to API limitations.
  • UI Enhancements: Added new UI components and extensive styling to support the Xiaohongshu sharing guide, including a prominent 'Download Poster' or 'Download Video' button, and a notice about API limitations. This ensures a consistent and branded experience for Xiaohongshu users.
  • Layout and Styling Adjustments: Refactored various CSS properties across sharing components to accommodate the new guide and improve overall layout and spacing, such as adjusting gaps, padding, and flexbox properties.
  • Code Cleanup and Refinement: Removed outdated comments and streamlined error handling logic in the video sharing component, specifically by removing a DefaultException for unsupported video/URL sharing and updating the associated error message.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds Xiaohongshu sharing guide functionality to three share-option popups. It replaces the standard QR code interface with a step-by-step guide specifically for Xiaohongshu sharing, providing users with manual upload instructions due to API limitations.

  • Adds conditional rendering to show Xiaohongshu-specific sharing guides when the platform is selected
  • Updates layout and styling to accommodate the new guide component
  • Removes some error handling logic for unsupported video sharing platforms

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
ProjectScreenshotSharing.vue Adds Xiaohongshu guide for screenshot sharing with step-by-step instructions and styled component
ProjectRecordingSharing.vue Adds Xiaohongshu guide for video sharing and removes platform compatibility error handling
ProjectPoster.vue Minor layout adjustments to poster component spacing and alignment
ProjectDirectShare.vue Adds Xiaohongshu guide for direct sharing with restructured layout and new styling

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines 640 to 749
.xiaohongshu-guide {
width: 100%;
max-width: 350px;
padding: 16px;
background: linear-gradient(135deg, #fff5f5 0%, #ffeef0 100%);
border-radius: 10px;
border: 1px solid #ffb3ba;
box-shadow: 0 3px 12px rgba(255, 0, 53, 0.12);
h3 {
margin: 0 0 14px 0;
font-size: 15px;
font-weight: 600;
color: #ff0035;
text-align: center;
line-height: 1.2;
}
}
.guide-steps {
margin-bottom: 14px;
.step {
display: flex;
align-items: flex-start;
margin-bottom: 12px;
&:last-child {
margin-bottom: 0;
}
}
.step-number {
font-size: 14px;
margin-right: 8px;
flex-shrink: 0;
line-height: 1.2;
width: 16px;
}
.step-content {
flex: 1;
min-width: 0;
strong {
display: block;
font-size: 13px;
font-weight: 600;
color: #333;
margin-bottom: 3px;
line-height: 1.3;
}
p {
font-size: 12px;
color: #666;
margin: 0;
line-height: 1.4;
word-wrap: break-word;
}
}
}
.api-notice {
display: flex;
align-items: flex-start;
margin-bottom: 14px;
padding: 8px 10px;
background: rgba(255, 255, 255, 0.8);
border-radius: 6px;
.notice-icon {
font-size: 12px;
margin-right: 6px;
flex-shrink: 0;
}
p {
margin: 0;
font-size: 11px;
color: #888;
line-height: 1.4;
word-wrap: break-word;
}
}
.download-btn.primary {
width: 100%;
background: linear-gradient(135deg, #ff0035 0%, #ff4d6d 100%);
color: white;
border: none;
padding: 10px 16px;
border-radius: 8px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 3px 8px rgba(255, 0, 53, 0.25);
&:hover:not(:disabled) {
background: linear-gradient(135deg, #e6002f 0%, #ff3366 100%);
transform: translateY(-1px);
box-shadow: 0 4px 14px rgba(255, 0, 53, 0.35);
}
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
}
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

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

The Xiaohongshu guide styles are duplicated across multiple components (ProjectScreenshotSharing.vue, ProjectRecordingSharing.vue, and ProjectDirectShare.vue). This code duplication makes maintenance difficult. Consider extracting these styles into a shared CSS file or creating a reusable component.

Copilot uses AI. Check for mistakes.
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request adds a sharing guide for Xiaohongshu to the project, recording, and screenshot sharing popups. The implementation correctly displays the guide when Xiaohongshu is selected. However, there is significant code duplication for both the HTML structure and the CSS of the guide across the three components. My main feedback is to refactor this duplicated code into a single reusable component to improve maintainability. I've left specific comments on the relevant files.

Comment on lines 38 to 79
<div v-if="selectedPlatform?.basicInfo.name === 'xiaohongshu'" class="xiaohongshu-guide">
<h3>📱 {{ $t({ en: 'How to share to Xiaohongshu?', zh: '如何分享到小红书?' }) }}</h3>

<div class="guide-steps">
<div class="step">
<span class="step-number">1️⃣</span>
<div class="step-content">
<strong>{{ $t({ en: 'Download Poster', zh: '下载海报' }) }}</strong>
<p>{{ $t({ en: 'Click the button below to save poster', zh: '点击下方按钮保存海报到设备' }) }}</p>
</div>
</div>

<div class="step">
<span class="step-number">2️⃣</span>
<div class="step-content">
<strong>{{ $t({ en: 'Open Xiaohongshu App', zh: '打开小红书APP' }) }}</strong>
<p>{{ $t({ en: 'Tap "+" to create new post', zh: '点击"+"号发布新笔记' }) }}</p>
</div>
</div>

<div class="step">
<span class="step-number">3️⃣</span>
<div class="step-content">
<strong>{{ $t({ en: 'Upload & Share', zh: '上传分享' }) }}</strong>
<p>{{ $t({ en: 'Select the downloaded poster to share', zh: '选择刚下载的海报进行分享' }) }}</p>
</div>
</div>
</div>

<div class="api-notice">
<span class="notice-icon">💡</span>
<p>
{{
$t({ en: 'Manual upload required due to API limitations', zh: '由于API限制,需要手动上传,感谢理解' })
}}
</p>
</div>

<button class="download-btn primary" :disabled="!posterCompRef" @click="handleDownloadPoster">
{{ $t({ en: 'Download Poster', zh: '下载海报' }) }}
</button>
</div>

Choose a reason for hiding this comment

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

high

This block for the Xiaohongshu sharing guide is duplicated in ProjectRecordingSharing.vue (lines 270-322) and ProjectScreenshotSharing.vue (lines 192-244). To improve maintainability and reduce code redundancy, consider extracting this into a reusable component (e.g., XiaohongshuShareGuide.vue). This new component could accept a prop to differentiate between 'poster' and 'video' to display the correct text, and emit an event for the download action.

Comment on lines 289 to 414
.xiaohongshu-guide {
width: 100%;
max-width: 320px;
padding: 10px;
background: linear-gradient(135deg, #fff5f5 0%, #ffeef0 100%);
border-radius: 12px;
border: 2px solid #ffb3ba;
box-shadow: 0 4px 12px rgba(255, 0, 53, 0.1);
h3 {
margin: 0 0 16px 0;
font-size: 16px;
font-weight: 600;
color: #ff0035;
text-align: center;
}
}
.guide-steps {
margin-bottom: 16px;
.step {
display: flex;
align-items: flex-start;
margin-bottom: 12px;
&:last-child {
margin-bottom: 0;
}
}
.step-number {
font-size: 16px;
margin-right: 10px;
flex-shrink: 0;
line-height: 1.2;
}
.step-content {
flex: 1;
strong {
display: block;
font-size: 13px;
font-weight: 600;
color: #333;
margin-bottom: 2px;
}
p {
font-size: 12px;
color: #666;
margin: 0;
line-height: 1.3;
}
}
}
.api-notice {
display: flex;
align-items: flex-start;
margin-bottom: 16px;
padding: 10px 12px;
background: rgba(255, 255, 255, 0.8);
border-radius: 8px;
.notice-icon {
font-size: 14px;
margin-right: 6px;
flex-shrink: 0;
}
p {
margin: 0;
font-size: 11px;
color: #888;
line-height: 1.4;
}
}
.download-btn {
width: 100%;
margin-top: 8px;
border-radius: 6px;
padding: 8px 16px;
border: 1px solid var(--ui-color-primary-main);
background: var(--ui-color-primary-main);
color: white;
cursor: pointer;
transition: all 0.2s ease;
&:hover:not(:disabled) {
background: var(--ui-color-primary-shade);
color: var(--ui-color-primary-main);
}
&:disabled {
background: var(--ui-color-hint-2);
border-color: var(--ui-color-hint-2);
cursor: not-allowed;
}
}
.download-btn.primary {
width: 100%;
background: linear-gradient(135deg, #ff0035 0%, #ff4d6d 100%);
color: white;
border: none;
padding: 12px 20px;
border-radius: 8px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
&:hover:not(:disabled) {
background: linear-gradient(135deg, #e6002f 0%, #ff3366 100%);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(255, 0, 53, 0.3);
}
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
}

Choose a reason for hiding this comment

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

high

The styles for the Xiaohongshu sharing guide are duplicated across ProjectDirectShare.vue, ProjectRecordingSharing.vue (lines 640-749), and ProjectScreenshotSharing.vue (lines 471-580). When refactoring the guide into a reusable component, these styles should be moved into that component's <style> block. This will centralize the styling and make future changes much easier.

<span class="loading-text">{{ $t({ en: 'Generating QR code...', zh: '正在生成二维码...' }) }}</span>

<div class="side-content">
<div v-if="selectedPlatform?.basicInfo.name === 'xiaohongshu'" class="xiaohongshu-guide">

Choose a reason for hiding this comment

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

medium

The platform name 'xiaohongshu' is used as a magic string. This is also repeated in other components. To improve maintainability and prevent potential typos, it's better to define platform names as constants in a shared file (e.g., in platform-share.ts) and import them where needed.

align-items: center;
gap: 8px;
margin-bottom: 16px;
// margin-bottom: 16px;

Choose a reason for hiding this comment

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

medium

This CSS rule is commented out. If it's no longer needed, please remove it to keep the code clean. This also applies to other commented-out styles in this file (lines 539, 551, 754).

Copy link

@niupilot niupilot bot left a comment

Choose a reason for hiding this comment

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

Code Review: Xiaohongshu Sharing Implementation

Found several issues requiring attention:

High Priority

Code Duplication: Xiaohongshu guide component duplicated across 3 files
Missing Error Handling: Download operations lack proper error handling
Magic Strings: Platform identifiers hardcoded in multiple places

Medium Priority

Accessibility: Emoji step numbers not screen reader friendly
CSS Maintenance: Hardcoded brand colors throughout styles
Loading States: Inconsistent loading state implementations

Architectural Recommendations

• Extract XiaohongshuGuide into reusable component
• Create platform constants file for maintainability
• Implement proper error boundaries for download operations

// 清理URL对象
URL.revokeObjectURL(url)
} catch (error) {
console.error('Failed to download poster:', error)
Copy link

Choose a reason for hiding this comment

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

Add proper error handling with user-friendly error messages. Original code only logs error without notifying user or handling the exception properly.

Suggested change
console.error('Failed to download poster:', error)
} catch (error) {
console.error('Failed to download poster:', error)
// Show user-friendly error message
throw new Error('Download failed. Please try again.')
}

<span class="loading-text">{{ $t({ en: 'Generating QR code...', zh: '正在生成二维码...' }) }}</span>

<div class="side-content">
<div v-if="selectedPlatform?.basicInfo.name === 'xiaohongshu'" class="xiaohongshu-guide">
Copy link

Choose a reason for hiding this comment

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

Replace magic string with constant to prevent typos and improve maintainability. This requires defining PLATFORM_CONSTANTS at the top of the file.

Suggested change
<div v-if="selectedPlatform?.basicInfo.name === 'xiaohongshu'" class="xiaohongshu-guide">
<div v-if="selectedPlatform?.basicInfo.name === PLATFORM_CONSTANTS.XIAOHONGSHU" class="xiaohongshu-guide">

<div class="qr-section">
<div class="qr-section-inner">
<div class="qr-content">
<div v-if="selectedPlatform?.basicInfo.name === 'xiaohongshu'" class="xiaohongshu-guide">
Copy link

Choose a reason for hiding this comment

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

Replace magic string with constant for consistency and maintainability. Same magic string appears in multiple components.

Suggested change
<div v-if="selectedPlatform?.basicInfo.name === 'xiaohongshu'" class="xiaohongshu-guide">
<div v-if="selectedPlatform?.basicInfo.name === PLATFORM_CONSTANTS.XIAOHONGSHU" class="xiaohongshu-guide">

<div class="qr-section">
<div class="qr-section-inner">
<div class="qr-content">
<div v-if="selectedPlatform?.basicInfo.name === 'xiaohongshu'" class="xiaohongshu-guide">
Copy link

Choose a reason for hiding this comment

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

Replace magic string with constant for consistency. This pattern should be consistent across all components.

Suggested change
<div v-if="selectedPlatform?.basicInfo.name === 'xiaohongshu'" class="xiaohongshu-guide">
<div v-if="selectedPlatform?.basicInfo.name === PLATFORM_CONSTANTS.XIAOHONGSHU" class="xiaohongshu-guide">


<div class="guide-steps">
<div class="step">
<span class="step-number">1️⃣</span>
Copy link

Choose a reason for hiding this comment

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

Replace emoji with number and add aria-label for better accessibility. Emoji step numbers are not accessible to screen readers.

Suggested change
<span class="step-number">1️⃣</span>
<span class="step-number" aria-label="Step 1">1</span>

</div>

<div class="step">
<span class="step-number">2️⃣</span>
Copy link

Choose a reason for hiding this comment

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

Replace emoji with number and add aria-label for better accessibility.

Suggested change
<span class="step-number">2️⃣</span>
<span class="step-number" aria-label="Step 2">2</span>

</div>

<div class="step">
<span class="step-number">3️⃣</span>
Copy link

Choose a reason for hiding this comment

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

Replace emoji with number and add aria-label for better accessibility.

Suggested change
<span class="step-number">3️⃣</span>
<span class="step-number" aria-label="Step 3">3</span>

@Dshuishui
Copy link
Author

/gemini review Performs a code review for the current pull request in its current state.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a sharing guide for Xiaohongshu across three different sharing popups. A new XiaohongshuShareGuide component has been created for this purpose. The changes are well-structured, but there are a few areas for improvement. I've pointed out an inconsistency in ProjectDirectShare.vue regarding user feedback during downloads, which should be addressed. I've also made some suggestions for improving maintainability and consistency in the new XiaohongshuShareGuide component and cleaning up some commented-out code in ProjectRecordingSharing.vue.

align-items: center;
gap: 8px;
margin-bottom: 16px;
// margin-bottom: 16px;

Choose a reason for hiding this comment

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

medium

This file contains several commented-out CSS rules (e.g., here, and on lines 493, 505, 597). If these styles are no longer needed, they should be removed to keep the codebase clean and maintainable. Leaving commented-out code can cause confusion for future developers.

Comment on lines 9 to 61
<strong>{{
type === 'video'
? $t({ en: 'Download Video', zh: '下载视频' })
: $t({ en: 'Download Poster', zh: '下载海报' })
}}</strong>
<p>
{{
type === 'video'
? $t({ en: 'Click the button below to save video', zh: '点击下方按钮保存视频到设备' })
: $t({ en: 'Click the button below to save poster', zh: '点击下方按钮保存海报到设备' })
}}
</p>
</div>
</div>

<div class="step">
<span class="step-number">2️⃣</span>
<div class="step-content">
<strong>{{ $t({ en: 'Open Xiaohongshu App', zh: '打开小红书APP' }) }}</strong>
<p>{{ $t({ en: 'Tap "+" to create new post', zh: '点击"+"号发布新笔记' }) }}</p>
</div>
</div>

<div class="step">
<span class="step-number">3️⃣</span>
<div class="step-content">
<strong>{{ $t({ en: 'Upload & Share', zh: '上传分享' }) }}</strong>
<p>
{{
type === 'video'
? $t({ en: 'Select the downloaded video to share', zh: '选择刚下载的视频进行分享' })
: $t({ en: 'Select the downloaded poster to share', zh: '选择刚下载的海报进行分享' })
}}
</p>
</div>
</div>
</div>

<div class="api-notice">
<span class="notice-icon">💡</span>
<p>
{{ $t({ en: 'Manual upload required due to API limitations', zh: '由于API限制,需要手动上传,感谢理解' }) }}
</p>
</div>

<button class="download-btn primary" :disabled="isLoading" @click="$emit('download')">
{{
isLoading
? $t({ en: 'Downloading...', zh: '下载中...' })
: type === 'video'
? $t({ en: 'Download Video', zh: '下载视频' })
: $t({ en: 'Download Poster', zh: '下载海报' })
}}

Choose a reason for hiding this comment

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

medium

The template contains several instances of duplicated logic for selecting translated text based on the type and isLoading props (lines 9-20, 35-42, 55-61). This can be simplified by using computed properties in the <script setup> block. This will make the template cleaner and the logic more maintainable.

For example, you could define:

import { computed } from 'vue'
import { useI18n } from 'vue-i18n' // or your i18n path

const { t } = useI18n()

const step1Title = computed(() =>
  props.type === 'video'
    ? t({ en: 'Download Video', zh: '下载视频' })
    : t({ en: 'Download Poster', zh: '下载海报' })
)

// ... and so on for other dynamic texts

Then use {{ step1Title }} in your template.

Comment on lines 78 to 199
.xiaohongshu-guide {
width: 100%;
max-width: 350px;
padding: 16px;
background: linear-gradient(135deg, #fff5f5 0%, #ffeef0 100%);
border-radius: 10px;
border: 1px solid #ffb3ba;
box-shadow: 0 3px 12px rgba(255, 0, 53, 0.12);
h3 {
margin: 0 0 14px 0;
font-size: 15px;
font-weight: 600;
color: #ff0035;
text-align: center;
line-height: 1.2;
}
}
.guide-steps {
margin-bottom: 14px;
.step {
display: flex;
align-items: flex-start;
margin-bottom: 12px;
&:last-child {
margin-bottom: 0;
}
}
.step-number {
font-size: 14px;
margin-right: 8px;
flex-shrink: 0;
line-height: 1.2;
width: 16px;
}
.step-content {
flex: 1;
min-width: 0;
strong {
display: block;
font-size: 13px;
font-weight: 600;
color: #333;
margin-bottom: 3px;
line-height: 1.3;
}
p {
font-size: 12px;
color: #666;
margin: 0;
line-height: 1.4;
word-wrap: break-word;
}
}
}
.api-notice {
display: flex;
align-items: flex-start;
margin-bottom: 14px;
padding: 8px 10px;
background: rgba(255, 255, 255, 0.8);
border-radius: 6px;
.notice-icon {
font-size: 12px;
margin-right: 6px;
flex-shrink: 0;
}
p {
margin: 0;
font-size: 11px;
color: #888;
line-height: 1.4;
word-wrap: break-word;
}
}
.download-btn.primary {
width: 100%;
background: linear-gradient(135deg, #ff0035 0%, #ff4d6d 100%);
color: white;
border: none;
padding: 10px 16px;
border-radius: 8px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 3px 8px rgba(255, 0, 53, 0.25);
&:hover:not(:disabled) {
background: linear-gradient(135deg, #e6002f 0%, #ff3366 100%);
transform: translateY(-1px);
box-shadow: 0 4px 14px rgba(255, 0, 53, 0.35);
}
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
}
</style>

Choose a reason for hiding this comment

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

medium

This component uses hardcoded color values (e.g., #fff5f5, #ffb3ba, #ff0035). Other components in the project consistently use CSS variables from a design system (e.g., var(--ui-color-primary-main)). To maintain consistency and make theming easier, consider replacing these hardcoded colors with existing or new CSS variables. For example, #ff0035 seems to be the brand color for Xiaohongshu, which could be defined as a CSS variable like --ui-color-xiaohongshu-main.

@Dshuishui
Copy link
Author

/gemini review Performs a code review for the current pull request in its current state.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a sharing guide for Xiaohongshu across three different sharing popups. A new XiaohongshuShareGuide component has been created for this purpose. The changes are well-structured, but there are a few areas for improvement. I've suggested refactoring some duplicated code in the new component, improving the download handling in ProjectDirectShare.vue to be consistent with other components, and pointed out a potential regression in ProjectRecordingSharing.vue where an important platform capability check was removed. I've also noted some commented-out code that could be cleaned up.

Comment on lines 38 to 43
<XiaohongshuShareGuide
v-if="selectedPlatform?.basicInfo.name === 'xiaohongshu'"
type="poster"
:is-loading="false"
@download="handleDownloadPoster"
/>

Choose a reason for hiding this comment

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

critical

The new XiaohongshuShareGuide component is used here, but its props are not correctly wired up, leading to a poor user experience and inconsistency with other parts of the application.

  1. :is-loading is hardcoded to false. This will prevent the user from seeing a loading indicator during download.
  2. @download is bound to handleDownloadPoster, but this function doesn't provide a loading state or user feedback on completion/error.

To fix this, handleDownloadPoster should be wrapped with useMessageHandle, similar to how it's done in ProjectScreenshotSharing.vue and ProjectRecordingSharing.vue.

First, update handleDownloadPoster (around line 142) to use useMessageHandle:

const handleDownloadPoster = useMessageHandle(
  async () => {
    if (!posterCompRef.value) {
      throw new Error('Poster component is not available.')
    }
    const posterFile = await posterCompRef.value.createPoster()
    const url = URL.createObjectURL(posterFile)
    const link = document.createElement('a')
    link.href = url
    link.download = `${props.projectData.name}-poster.png`
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
    URL.revokeObjectURL(url)
  },
  { en: 'Failed to download poster', zh: '下载海报失败' },
  { en: 'Poster downloaded successfully', zh: '海报下载成功' }
)

Then, update the template with the suggestion below to correctly wire up the props.

        <XiaohongshuShareGuide
          v-if="selectedPlatform?.basicInfo.name === 'xiaohongshu'"
          type="poster"
          :is-loading="handleDownloadPoster.isLoading.value"
          @download="handleDownloadPoster.fn"
        />

Comment on lines 72 to 75
function handlePlatformChange(platform: PlatformConfig) {
selectedPlatform.value = platform
// 检查是否需要显示下载提示
if (!platform.shareType.supportVideo && !platform.shareType.supportURL) {
throw new DefaultException({
en: `Please download the video to local and share it on ${platform.basicInfo.label.en}`,
zh: `请下载视频到本地,然后去${platform.basicInfo.label.zh}分享`
})
}
// QR code generation is handled automatically by watch, no need to call manually
}

Choose a reason for hiding this comment

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

high

The check for unsupported sharing types has been removed from handlePlatformChange. Previously, if a platform supported neither video nor URL sharing (e.g., Bilibili), an informative message was shown to the user. The current implementation falls back to generating a QR code for the project URL, which can be confusing since the selected platform may not support this sharing method. This check should be restored or replaced with a more user-friendly guide for such platforms, similar to the one implemented for Xiaohongshu.

Copy link
Author

Choose a reason for hiding this comment

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

high

The check for unsupported sharing types has been removed from handlePlatformChange. Previously, if a platform supported neither video nor URL sharing (e.g., Bilibili), an informative message was shown to the user. The current implementation falls back to generating a QR code for the project URL, which can be confusing since the selected platform may not support this sharing method. This check should be restored or replaced with a more user-friendly guide for such platforms, similar to the one implemented for Xiaohongshu.

这个暂时不用,因为抖音后面可以支持了,小红书直接显示不支持,B站后面如果确定了行或者不行,就也给出比较明确的提示

align-items: center;
gap: 8px;
margin-bottom: 16px;
// margin-bottom: 16px;

Choose a reason for hiding this comment

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

medium

This CSS property is commented out. If it's no longer needed, it should be removed to keep the codebase clean. This also applies to other commented-out styles in this file (e.g., lines 493, 505, 597).

Comment on lines +58 to +91
const step1Title = computed(() =>
props.type === 'video' ? t({ en: 'Download Video', zh: '下载视频' }) : t({ en: 'Download Poster', zh: '下载海报' })
)
const step1Description = computed(() =>
props.type === 'video'
? t({ en: 'Click the button below to save video', zh: '点击下方按钮保存视频到设备' })
: t({ en: 'Click the button below to save poster', zh: '点击下方按钮保存海报到设备' })
)
const step2Title = computed(() => t({ en: 'Open Xiaohongshu App', zh: '打开小红书APP' }))
const step2Description = computed(() => t({ en: 'Tap "+" to create new post', zh: '点击"+"号发布新笔记' }))
const step3Title = computed(() => t({ en: 'Upload & Share', zh: '上传分享' }))
const step3Description = computed(() =>
props.type === 'video'
? t({ en: 'Select the downloaded video to share', zh: '选择刚下载的视频进行分享' })
: t({ en: 'Select the downloaded poster to share', zh: '选择刚下载的海报进行分享' })
)
const apiNoticeText = computed(() =>
t({ en: 'Manual upload required due to API limitations', zh: '由于API限制,需要手动上传,感谢理解' })
)
const downloadButtonText = computed(() => {
if (props.isLoading) {
return t({ en: 'Downloading...', zh: '下载中...' })
}
return props.type === 'video'
? t({ en: 'Download Video', zh: '下载视频' })
: t({ en: 'Download Poster', zh: '下载海报' })
})

Choose a reason for hiding this comment

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

medium

The computed properties for step1Title and downloadButtonText contain duplicate logic for generating the button/title text. This can be refactored by creating a shared computed property to avoid repetition and improve maintainability.

const downloadActionText = computed(() =>
  props.type === 'video'
    ? t({ en: 'Download Video', zh: '下载视频' })
    : t({ en: 'Download Poster', zh: '下载海报' })
)

const step1Title = downloadActionText

const step1Description = computed(() =>
  props.type === 'video'
    ? t({ en: 'Click the button below to save video', zh: '点击下方按钮保存视频到设备' })
    : t({ en: 'Click the button below to save poster', zh: '点击下方按钮保存海报到设备' })
)

const step2Title = computed(() => t({ en: 'Open Xiaohongshu App', zh: '打开小红书APP' }))

const step2Description = computed(() => t({ en: 'Tap "+" to create new post', zh: '点击"+"号发布新笔记' }))

const step3Title = computed(() => t({ en: 'Upload & Share', zh: '上传分享' }))

const step3Description = computed(() =>
  props.type === 'video'
    ? t({ en: 'Select the downloaded video to share', zh: '选择刚下载的视频进行分享' })
    : t({ en: 'Select the downloaded poster to share', zh: '选择刚下载的海报进行分享' })
)

const apiNoticeText = computed(() =>
  t({ en: 'Manual upload required due to API limitations', zh: '由于API限制,需要手动上传,感谢理解' })
)

const downloadButtonText = computed(() => {
  if (props.isLoading) {
    return t({ en: 'Downloading...', zh: '下载中...' })
  }
  return downloadActionText.value
})

@Dshuishui
Copy link
Author

/gemini review Performs a code review for the current pull request in its current state.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a sharing guide for Xiaohongshu across three different sharing popups: direct project sharing, recording sharing, and screenshot sharing. A new XiaohongshuShareGuide component has been created for this purpose. The changes also include refactoring the download logic to use useMessageHandle for better user feedback and some style adjustments for layout consistency.

The implementation is generally good, but I've identified a potential regression in the recording sharing flow for platforms other than Xiaohongshu that require manual downloads. I've also suggested some improvements for accessibility and code maintainability in the new XiaohongshuShareGuide component.

@qiniu-ci
Copy link

This PR has been deployed to the preview environment. You can explore it using the preview URL.

Warning

Please note that deployments in the preview environment are temporary and will be automatically cleaned up after a certain period. Make sure to explore it before it is removed. For any questions, contact the XBuilder team.

<span class="unsupported-text">{{ $t({ en: 'Unsupported', zh: '暂不支持' }) }}</span>

<div class="side-content">
<XiaohongshuShareGuide
Copy link
Collaborator

Choose a reason for hiding this comment

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

这个跟 #2189 (comment) 这里提到的有关,我们定义 platform-share 模块的目标是,把每个平台自身的逻辑统一维护(在 platform-share),而不需要扩散到每一个具体的业务模块(如 ProjectDirectSharing, ProjectRecordingSharing, ProjectScreenshotSharing)中;因此在这里出现针对 xiaohongshu 的逻辑不是我们预期的

这里要想一下怎么把 <XiaohongshuShareGuide> 的逻辑也做进现有的 PlatformConfig 中(我们大概率需要扩展 PlatformConfig);它可能体现为一个 component(component 可以看成是返回一份 UI 描述的函数),也可能体现为返回结构化的 steps 信息或者 markdown 字符串的函数,也可能是别的

Copy link
Author

Choose a reason for hiding this comment

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

这个跟 #2189 (comment) 这里提到的有关,我们定义 platform-share 模块的目标是,把每个平台自身的逻辑统一维护(在 platform-share),而不需要扩散到每一个具体的业务模块(如 ProjectDirectSharing, ProjectRecordingSharing, ProjectScreenshotSharing)中;因此在这里出现针对 xiaohongshu 的逻辑不是我们预期的

这里要想一下怎么把 <XiaohongshuShareGuide> 的逻辑也做进现有的 PlatformConfig 中(我们大概率需要扩展 PlatformConfig);它可能体现为一个 component(component 可以看成是返回一份 UI 描述的函数),也可能体现为返回结构化的 steps 信息或者 markdown 字符串的函数,也可能是别的

明白,我去和海龙沟通改进一下

@Dshuishui Dshuishui closed this Sep 28, 2025
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