Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: sync 3.20.0 to dev #2639

Merged
merged 17 commits into from
Dec 11, 2024
Merged

feat: sync 3.20.0 to dev #2639

merged 17 commits into from
Dec 11, 2024

Conversation

zzcr
Copy link
Member

@zzcr zzcr commented Dec 11, 2024

PR

PR Checklist

Please check if your PR fulfills the following requirements:

  • The commit message follows our Commit Message Guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

What is the current behavior?

Issue Number: N/A

What is the new behavior?

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Summary by CodeRabbit

Release Notes

  • New Features

    • Integrated new search functionality across various components.
    • Added properties showExpand and widthAdapt to tree menu configuration.
  • Bug Fixes

    • Improved clipboard operations in demo component.
  • Updates

    • Version updates for multiple packages.
    • Enhanced styling and responsiveness in demo component and CSS files.
  • Documentation

    • Added detailed comments to the filterAttrs function for clarity.
  • Chores

    • Streamlined element selection logic in the renderless package.

zzcr and others added 17 commits December 4, 2024 17:34
* feat: change default router link

* feat: change default router link
#2619)

* fix(anchor): [anchor] fixed the bug that querySelect cannot select special id

* fix(anchor): [anchor] fixed the bug that querySelect cannot select special id
…2623)

* fix: fixed the problem of global search js resource access failure

* feat: update docs version
…nu (#2627)

* feat(tree-menu): [tree-menu] Resolve the warning issue on the tree-menu page

* feat(tree-menu): [tree-menu] Resolve the warning issue on the tree-menu page
… errors (#2629)

* feat: support global search in multiple environments and fix pipeline errors

* feat: support global search in multiple environments and fix pipeline errors
Copy link

coderabbitai bot commented Dec 11, 2024

Walkthrough

The changes in this pull request primarily involve the removal of search functionality across various components of the application. This includes the deletion of DocSearch resources from the HTML and adjustments to visibility settings for the search box. Additionally, several files have been updated to reflect new dependencies, version changes, and modifications to routing and component logic. The overall structure of the application remains intact, but the search-related features have been significantly altered or removed.

Changes

File Change Summary
examples/sites/index.html Removed DocSearch CSS/JS links and initialization script; modified search box visibility.
examples/sites/package.json Updated version to 3.20.7; added dependencies for @docsearch/js, @docsearch/css, and @docsearch/react.
examples/sites/playground/App.vue Updated versions array to include '3.20'; refined getVersion logic and improved code readability.
examples/sites/src/App.vue Removed envTarget constant and related conditional logic for search box visibility.
examples/sites/src/main.js Added imports for @docsearch/js and @docsearch/css; configured search functionality with docsearch.
examples/sites/src/menus.jsx Conditional generation of docOptions based on development environment.
examples/sites/src/router.js Simplified routing logic; removed dynamic theme/language retrieval from local storage.
examples/sites/src/style.css Adjusted CSS for various elements, including z-index and media query updates.
examples/sites/src/tools/docsearch.js Introduced OPEN_TINY_URL constant and doSearchEverySite function for global search handling.
examples/sites/src/views/components/demo.vue Updated template structure and styling for responsiveness; refined demo code handling.
examples/sites/vite-dosearch-plugin.js Introduced a new Vite plugin for handling DocSearch integration.
examples/sites/vite.config.ts Conditionally included viteDosearchPlugin based on build target.
packages/renderless/package.json Updated version to 3.20.3.
packages/renderless/src/anchor/index.ts Added getEleMentBySelect function for streamlined DOM selection.
packages/vue-common/package.json Updated version to 3.20.1.
packages/vue-common/src/index.ts Enhanced global design configuration handling and added utility functions.
packages/vue/src/fluent-editor/package.json Updated version to 3.20.1 and dependency to @opentiny/fluent-editor to ~3.23.0.
packages/vue/src/tree-menu/package.json Updated version to 3.20.1.
packages/vue/src/tree-menu/src/props.ts Added new properties showExpand and widthAdapt to treeMenuProps.

Possibly related PRs

Suggested labels

chore

Poem

🐇 In the garden of code, changes bloom bright,
The search box now dances in the light.
With dependencies fresh and versions anew,
The rabbit hops on, with a joyful view!
No more DocSearch, but fear not, my friend,
For clarity and structure, we surely commend! 🌼


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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. (Experiment)
  • @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.

@zzcr zzcr changed the title Sync 3.20 to dev feat: sync 3.20.0 to dev Dec 11, 2024
@github-actions github-actions bot added the enhancement New feature or request (功能增强) label Dec 11, 2024
Copy link

[e2e-test-warn]
The component to be tested is missing.

The title of the Pull request should look like "fix(vue-renderless): [action-menu, alert] fix xxx bug".

Please make sure you've read our contributing guide

Copy link

@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: 3

🧹 Outside diff range and nitpick comments (4)
packages/vue/src/tree-menu/src/props.ts (1)

Line range hint 93-97: Document the metaErp-specific widthAdapt property.

While the property implementation is correct, the comment "metaErp 新增属性" suggests this is a specific feature. Consider adding JSDoc documentation to clarify:

  • The purpose of this property
  • When it should be used
  • How it affects the component's behavior
-  // metaErp 新增属性
+  /**
+   * Enables width adaptation for metaErp integration.
+   * @description Controls whether the tree menu width should adapt dynamically
+   */
   widthAdapt: {
     type: Boolean,
     default: false
   }
examples/sites/vite-dosearch-plugin.js (1)

7-10: Consider using more robust code transformation

The current string replacement approach is fragile and could break if the docsearch library updates its implementation.

Consider using AST transformation instead of string replacement for more reliable code modification. This would make the plugin more maintainable and less likely to break with dependency updates.

examples/sites/vite.config.ts (1)

46-46: Document the purpose of the VITE_BUILD_TARGET environment variable

The isInner flag's purpose and its impact on the build process should be documented.

Consider adding a comment explaining when and why the 'inner' build target should be used:

+ // VITE_BUILD_TARGET='inner' enables additional search functionality through the docsearch plugin
 const isInner = env.VITE_BUILD_TARGET === 'inner'
packages/renderless/src/anchor/index.ts (1)

101-102: Consider caching DOM query results

Multiple DOM queries for the same elements could impact performance. Consider caching the results where appropriate.

For example, in the linkClick function:

 export const linkClick = (...) => (e: Event, item: IAnchorLinkItem) => {
+  let cachedLinkEl
   // ...
-  const linkEl = getEleMentBySelect(scrollContainer, item.link) as HTMLElement
+  if (!cachedLinkEl) {
+    cachedLinkEl = getElementBySelector(scrollContainer, item.link) as HTMLElement
+  }
   const top = cachedLinkEl?.getBoundingClientRect().top - ...

Also applies to: 240-248

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7e1016e and 36b3838.

📒 Files selected for processing (20)
  • examples/sites/index.html (1 hunks)
  • examples/sites/package.json (2 hunks)
  • examples/sites/playground/App.vue (2 hunks)
  • examples/sites/src/App.vue (0 hunks)
  • examples/sites/src/main.js (1 hunks)
  • examples/sites/src/menus.jsx (2 hunks)
  • examples/sites/src/router.js (2 hunks)
  • examples/sites/src/style.css (4 hunks)
  • examples/sites/src/tools/docsearch.js (1 hunks)
  • examples/sites/src/views/components/demo.vue (1 hunks)
  • examples/sites/vite-dosearch-plugin.js (1 hunks)
  • examples/sites/vite.config.ts (3 hunks)
  • packages/renderless/package.json (1 hunks)
  • packages/renderless/src/anchor/index.ts (5 hunks)
  • packages/renderless/src/color-select-panel/vue.ts (1 hunks)
  • packages/vue-common/package.json (1 hunks)
  • packages/vue-common/src/index.ts (1 hunks)
  • packages/vue/src/fluent-editor/package.json (2 hunks)
  • packages/vue/src/tree-menu/package.json (1 hunks)
  • packages/vue/src/tree-menu/src/props.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • examples/sites/src/App.vue
✅ Files skipped from review due to trivial changes (6)
  • packages/renderless/package.json
  • examples/sites/index.html
  • packages/vue-common/package.json
  • packages/vue/src/tree-menu/package.json
  • packages/vue/src/fluent-editor/package.json
  • examples/sites/package.json
🧰 Additional context used
🪛 Gitleaks (8.21.2)
examples/sites/src/main.js

46-46: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (19)
packages/vue/src/tree-menu/src/props.ts (1)

66-69: LGTM! The showExpand prop follows component conventions.

The new prop follows the established pattern for boolean flags in the component and is appropriately positioned near other visibility-related properties.

examples/sites/src/router.js (2)

3-3: Consider UX implications of removing theme persistence

The removal of storage-related imports ($local, CURRENT_THEME_KEY) indicates that user theme preferences will no longer persist between sessions. This might impact user experience, especially for returning visitors who previously had their preferences saved.

Let's verify if there are any other components still relying on the removed storage functionality:

Consider implementing a different persistence mechanism or documenting the rationale behind removing this feature in the PR description.


37-38: ⚠️ Potential issue

Reconsider hardcoding Chinese language as default

The redirection logic now defaults to Chinese (ZH_CN_LANG) for all unmatched routes, which might not be ideal for international users. Consider:

  1. Using browser's language preference (navigator.language)
  2. Implementing a language detection middleware
  3. Adding a language selection landing page

Here's a suggested improvement:

- const langPath = LANG_PATH_MAP[ZH_CN_LANG]
- const theme = THEME_ROUTE_MAP[DEFAULT_THEME]
+ const browserLang = navigator.language.toLowerCase()
+ const supportedLang = LANG_PATH_MAP[browserLang] ? browserLang : ZH_CN_LANG
+ const langPath = LANG_PATH_MAP[supportedLang]
+ const theme = THEME_ROUTE_MAP[DEFAULT_THEME]

Let's check if there are any language-related configurations or middleware in the codebase:

examples/sites/vite-dosearch-plugin.js (1)

1-18: ⚠️ Potential issue

Consider adding safety checks and error handling

The plugin modifies third-party code by injecting a global handler, which could lead to runtime errors. Several improvements are recommended:

  1. Add safety checks for the global handler:
 export default function viteDosearchPlugin() {
   return {
     name: 'vite-dosearch-plugin',
     enforce: 'pre',
     transform(code, id) {
       if (id.includes('@docsearch')) {
         const newCode = code.replace(
           'Promise((function(t){var n=new XMLHttpRequest;',
-          'Promise((function(t){t=window.handleGlobalSearchData(t);var n=new XMLHttpRequest;'
+          'Promise((function(t){t=typeof window.handleGlobalSearchData === "function" ? window.handleGlobalSearchData(t) : t;var n=new XMLHttpRequest;'
         )
         return {
           code: newCode,
           map: null
         }
       }
     }
   }
 }
  1. Consider adding a warning if the handler is missing:
 export default function viteDosearchPlugin() {
+  let warned = false;
   return {
     name: 'vite-dosearch-plugin',
     enforce: 'pre',
     transform(code, id) {
       if (id.includes('@docsearch')) {
+        if (!warned) {
+          console.warn('[vite-dosearch-plugin] Ensure window.handleGlobalSearchData is defined before docsearch initialization');
+          warned = true;
+        }
         const newCode = code.replace(

Let's verify the docsearch dependency and usage:

examples/sites/vite.config.ts (2)

3-3: LGTM: Import statement is correctly placed

The import statement for the local plugin is properly positioned among other plugin imports.


120-121: Verify the plugin order and dependencies

The placement of viteDosearchPlugin at the end of the plugins array needs verification to ensure it doesn't conflict with other plugins.

Let's check for potential plugin conflicts:

✅ Verification successful

Plugin order is safe and well-configured

The viteDosearchPlugin placement is correct and safe because:

  1. The plugin has enforce: 'pre' configuration, ensuring it runs before other plugins
  2. It only transforms code that includes '@docsearch' and makes a specific, isolated modification
  3. It's conditionally loaded only when isInner is true, which is appropriate for its purpose
  4. The plugin's transformation doesn't interfere with other plugins in the chain as it only modifies docsearch-specific code
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for other plugins that might interact with docsearch
rg -l "docsearch" --type ts --type js

# Check for environment variable usage
rg "VITE_BUILD_TARGET" --type ts --type js

Length of output: 620


Script:

#!/bin/bash
# Let's check the content of the docsearch plugin to understand its dependencies
cat examples/sites/vite-dosearch-plugin.js

# Also check the vite config to see the full plugin configuration
cat examples/sites/vite.config.ts

Length of output: 5811

packages/vue-common/src/index.ts (1)

179-179: Ensure consistent handling of reactive globalDesignConfig

The reassignment correctly handles the possibility that globalDesignConfig is a reactive object with a .value property, ensuring it is always an object for subsequent use.

packages/renderless/src/color-select-panel/vue.ts (1)

Line range hint 61-65: Switch to using hooks.onMounted for lifecycle management

Using hooks.onMounted ensures that the lifecycle hook is properly managed within the renderless function, enhancing consistency and flexibility.

examples/sites/src/style.css (5)

7-8: Restore bold text styling after reset.css

Adding font-weight: 700; to b and strong tags ensures that bold text is rendered correctly after including reset.css.


26-27: Set vertical alignment for table cells

Applying vertical-align: middle; to .tiny-grid td and .tiny-grid th aligns cell content centrally, improving table presentation.


40-40: Increase specificity of CSS selector for search container

Adjusting the selector to .DocSearch.DocSearch-Container increases specificity, ensuring the styles are applied correctly without conflicts.


79-81: Adjust padding for search button on mobile devices

Adding padding to .search-box .DocSearch-Button within the media query enhances its appearance on screens up to 768px wide.


47-47: ⚠️ Potential issue

Fix missing semicolon in CSS declaration

Adding the missing semicolon to the transition property corrects a syntax error, ensuring the CSS is parsed correctly.

Apply this diff to fix the syntax error:

-   transition: 0.4s
+   transition: 0.4s;

Likely invalid or redundant comment.

examples/sites/src/menus.jsx (1)

29-41: Verify if restricting documentation menus to DEV mode is intentional

The change makes documentation menus only available in development mode. This could impact users trying to access documentation in production environments.

Consider:

  1. Documenting this behavior change in the README or relevant documentation
  2. Adding a configuration flag to optionally enable documentation in production if needed
examples/sites/src/main.js (1)

52-55: Verify search functionality in non-open environments

The conditional implementation of doSearchEverySite for non-open environments should be tested to ensure consistent search behavior.

✅ Verification successful

Let me gather more context about the search functionality implementation.


Search functionality implementation is simple and properly scoped

The implementation of doSearchEverySite is straightforward - it sets up a global handler that replaces the OpenTiny URL with the current origin in search results. This is a reasonable approach for non-open environments and doesn't require additional verification as it:

  • Only affects URL replacement in search results
  • Is properly scoped to non-open environments
  • Has a simple, focused implementation without complex dependencies
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if doSearchEverySite is properly implemented
rg -A 5 "doSearchEverySite" ./examples/sites/src/tools/

Length of output: 548


Script:

#!/bin/bash
# Check the complete implementation of doSearchEverySite and related constants
rg -A 15 "OPEN_TINY_URL|doSearchEverySite" ./examples/sites/src/

# Check where and how the search functionality is used
ast-grep --pattern 'doSearchEverySite()'

Length of output: 2859

examples/sites/playground/App.vue (3)

26-26: LGTM: Version array updated to include 3.20.

The version array maintains the three latest versions, which is consistent with the PR objective of syncing version 3.20.


87-88: LGTM: Improved readability of design theme imports.

The multi-line formatting enhances code readability while maintaining the same functionality.


Line range hint 313-317: LGTM: Added mobile responsiveness for demo container.

The media query ensures proper content handling on small screens (≤640px) by enabling horizontal scrolling.

examples/sites/src/views/components/demo.vue (1)

322-324: LGTM: Enhanced visual styling of demo container.

The updated styles improve the visual presentation with a white background, border, and rounded corners.

Comment on lines +6 to +8
if (typeof data.content === 'string') {
data.content = data.content.replaceAll(OPEN_TINY_URL, window.location.origin)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure compatibility of String.prototype.replaceAll

The replaceAll method may not be supported in all browsers, especially older ones. To improve compatibility, consider using replace with a global regular expression.

Apply this diff to enhance compatibility:

- data.content = data.content.replaceAll(OPEN_TINY_URL, window.location.origin)
+ data.content = data.content.replace(new RegExp(OPEN_TINY_URL, 'g'), window.location.origin)
📝 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
if (typeof data.content === 'string') {
data.content = data.content.replaceAll(OPEN_TINY_URL, window.location.origin)
}
if (typeof data.content === 'string') {
data.content = data.content.replace(new RegExp(OPEN_TINY_URL, 'g'), window.location.origin)
}

Comment on lines +44 to +50
docsearch({
appId: 'AGPA5UXHMH',
apiKey: '5fa09fc20270efa61d68e2c2eb0f56df',
indexName: 'opentiny',
container: '.search-box',
debug: false
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical: Move DocSearch API credentials to environment variables

The DocSearch API credentials are hardcoded in the source code, which poses a security risk. These should be moved to environment variables.

Apply this change:

 docsearch({
-  appId: 'AGPA5UXHMH',
-  apiKey: '5fa09fc20270efa61d68e2c2eb0f56df',
+  appId: import.meta.env.VITE_DOCSEARCH_APP_ID,
+  apiKey: import.meta.env.VITE_DOCSEARCH_API_KEY,
   indexName: 'opentiny',
   container: '.search-box',
   debug: false
 })
📝 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
docsearch({
appId: 'AGPA5UXHMH',
apiKey: '5fa09fc20270efa61d68e2c2eb0f56df',
indexName: 'opentiny',
container: '.search-box',
debug: false
})
docsearch({
appId: import.meta.env.VITE_DOCSEARCH_APP_ID,
apiKey: import.meta.env.VITE_DOCSEARCH_API_KEY,
indexName: 'opentiny',
container: '.search-box',
debug: false
})
🧰 Tools
🪛 Gitleaks (8.21.2)

46-46: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Comment on lines +15 to +16
const getEleMentBySelect = (parent, selector) =>
selector?.startsWith('#') ? document.getElementById(selector.slice(1)) : parent.querySelector(selector)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve naming convention and add input validation

The function name getEleMentBySelect has inconsistent capitalization. Also, consider adding input validation for security.

-const getEleMentBySelect = (parent, selector) =>
+const getElementById = (id) => document.getElementById(id)
+const querySelector = (parent, selector) => parent?.querySelector(selector)
+
+const getElementBySelector = (parent, selector) => {
+  if (!selector || typeof selector !== 'string') {
+    return null
+  }
+  return selector.startsWith('#') 
+    ? getElementById(selector.slice(1))
+    : querySelector(parent, selector)
+}
📝 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 getEleMentBySelect = (parent, selector) =>
selector?.startsWith('#') ? document.getElementById(selector.slice(1)) : parent.querySelector(selector)
const getElementById = (id) => document.getElementById(id)
const querySelector = (parent, selector) => parent?.querySelector(selector)
const getElementBySelector = (parent, selector) => {
if (!selector || typeof selector !== 'string') {
return null
}
return selector.startsWith('#')
? getElementById(selector.slice(1))
: querySelector(parent, selector)
}

@kagol kagol merged commit 231a1ea into dev Dec 11, 2024
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request (功能增强)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants