Skip to content

fix: add plugin sorting by name and improve search filtering logic#5559

Merged
Soulter merged 1 commit intoAstrBotDevs:masterfrom
Flartiny:fix/plugin-stable-sort
Feb 28, 2026
Merged

fix: add plugin sorting by name and improve search filtering logic#5559
Soulter merged 1 commit intoAstrBotDevs:masterfrom
Flartiny:fix/plugin-stable-sort

Conversation

@Flartiny
Copy link
Contributor

@Flartiny Flartiny commented Feb 28, 2026

fixes: #2762

解决:在已安装插件的界面,对插件进行保存配置、重载等操作后被置于页面末尾的问题,在issue #2762有提及

Modifications / 改动点

仅修改dashboard/src/views/extension/useExtensionPage.js,未影响后端

  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

image

Checklist / 检查清单

  • 😊 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。/ If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
  • 👀 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。/ My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
  • 🤓 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到了 requirements.txtpyproject.toml 文件相应位置。/ I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
  • 😮 我的更改没有引入恶意代码。/ My changes do not introduce malicious code.

Summary by Sourcery

在扩展控制台页面中,对已安装插件列表按名称排序,并增强插件搜索过滤功能。

New Features:

  • 在已安装插件视图中,按插件名称进行字母顺序排序;当名称相同时,保留原有顺序。

Enhancements:

  • 扩展插件搜索范围,安全处理缺失字段,并对文本进行标准化,支持在名称、描述、作者、支持的平台以及 AstrBot 版本字段中进行不区分大小写的匹配。
Original summary in English

Summary by Sourcery

Sort the installed plugin list by name and enhance the plugin search filtering on the extensions dashboard page.

New Features:

  • Add alphabetical sorting of plugins by name in the installed plugins view, preserving original order when names match.

Enhancements:

  • Broaden plugin search to safely handle missing fields and normalize text for case-insensitive matching across name, description, author, supported platforms, and AstrBot version.

@dosubot dosubot bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Feb 28, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, 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!

此拉取请求旨在通过引入插件名称排序功能来解决已安装插件界面中插件顺序不一致的问题。同时,它还改进了插件搜索过滤逻辑,确保无论是否进行搜索,插件列表都能保持按名称排序,从而提升用户体验和界面稳定性。

Highlights

  • 插件排序功能: 引入了按插件名称排序的功能,解决了插件在保存配置或重载后位置不一致的问题。
  • 搜索过滤逻辑改进: 优化了插件搜索过滤逻辑,使其在过滤后也能保持插件列表的有序性。
Changelog
  • dashboard/src/views/extension/useExtensionPage.js
    • 新增了 sortPluginsByName 函数,用于按插件名称进行排序。
    • 修改了 filteredPlugins 计算属性,使其在过滤后调用 sortPluginsByName 函数,确保插件列表始终按名称排序。
    • 重构了 filteredPlugins 中的搜索逻辑,使其更清晰。
Activity
  • 目前没有人工活动。
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.

@dosubot
Copy link

dosubot bot commented Feb 28, 2026

Related Documentation

Checked 1 published document(s) in 1 knowledge base(s). No updates required.

How did I do? Any feedback?  Join Discord

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.

Hey - 我发现了 1 个问题,并给出了一些整体反馈:

  • filteredPlugins 中,你已经在 sortPluginsByName 里创建了一个不变更原数组的排序副本,因此这里的展开操作 sortPluginsByName([...filtered]) 是多余的;你可以直接传入 filtered,以避免额外的数组分配。
  • 由于 sortPluginsByName 是一个不依赖组件状态的纯工具函数,建议将它移动到 useExtensionPage 之外(例如移动到一个共享的工具文件,或者模块作用域),这样可以避免在每次 hook 调用时重新定义该函数,并且在其他插件列表也需要同样的排序逻辑时能复用它。
给 AI Agent 的提示
Please address the comments from this code review:

## Overall Comments
- In `filteredPlugins`, you’re already creating a non-mutating sorted copy in `sortPluginsByName`, so the spread `sortPluginsByName([...filtered])` is redundant; you can pass `filtered` directly to avoid the extra array allocation.
- Since `sortPluginsByName` is a pure utility that doesn’t depend on component state, consider moving it outside `useExtensionPage` (e.g., to a shared utility file or at module scope) to avoid redefining the function on each hook invocation and to make it reusable if other plugin lists need the same ordering.

## Individual Comments

### Comment 1
<location path="dashboard/src/views/extension/useExtensionPage.js" line_range="330-339" />
<code_context>
     return data;
   });

+  const sortPluginsByName = (plugins) => {
+    return plugins
+      .map((plugin, index) => ({ plugin, index }))
+      .sort((a, b) => {
+        const nameA = String(a.plugin?.name ?? "");
+        const nameB = String(b.plugin?.name ?? "");
+        const nameCompare = nameA.localeCompare(nameB, undefined, {
+          sensitivity: "base",
+        });
+        if (nameCompare !== 0) {
+          return nameCompare;
+        }
+        return a.index - b.index;
+      })
+      .map((item) => item.plugin);
+  };
+
</code_context>
<issue_to_address>
**suggestion (performance):** Consider simplifying sorting logic by relying on stable `Array.prototype.sort` instead of mapping to index-tagged objects.

Modern JS engines (evergreen browsers and Node 12+) guarantee a stable `Array.prototype.sort`, so you can rely on that stability instead of carrying the original index through the sort. That lets you drop the pre/post `map` calls and index tracking, keeping just the `localeCompare`-based comparator and operating directly on the `plugins` array (or a shallow copy if you want to avoid mutation).

Suggested implementation:

```javascript
  const sortPluginsByName = (plugins) => {
    return [...plugins].sort((a, b) => {
      const nameA = String(a?.name ?? "");
      const nameB = String(b?.name ?? "");
      return nameA.localeCompare(nameB, undefined, {
        sensitivity: "base",
      });
    });
  };

```

If callers of `sortPluginsByName` previously depended on it preserving the original array reference (i.e., mutating `plugins` in place), you can drop the spread and simply call `plugins.sort(...)` instead. In that case, update the body to `plugins.sort(...)` and optionally return `plugins` for chaining.
</issue_to_address>

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

Hey - I've found 1 issue, and left some high level feedback:

  • In filteredPlugins, you’re already creating a non-mutating sorted copy in sortPluginsByName, so the spread sortPluginsByName([...filtered]) is redundant; you can pass filtered directly to avoid the extra array allocation.
  • Since sortPluginsByName is a pure utility that doesn’t depend on component state, consider moving it outside useExtensionPage (e.g., to a shared utility file or at module scope) to avoid redefining the function on each hook invocation and to make it reusable if other plugin lists need the same ordering.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `filteredPlugins`, you’re already creating a non-mutating sorted copy in `sortPluginsByName`, so the spread `sortPluginsByName([...filtered])` is redundant; you can pass `filtered` directly to avoid the extra array allocation.
- Since `sortPluginsByName` is a pure utility that doesn’t depend on component state, consider moving it outside `useExtensionPage` (e.g., to a shared utility file or at module scope) to avoid redefining the function on each hook invocation and to make it reusable if other plugin lists need the same ordering.

## Individual Comments

### Comment 1
<location path="dashboard/src/views/extension/useExtensionPage.js" line_range="330-339" />
<code_context>
     return data;
   });

+  const sortPluginsByName = (plugins) => {
+    return plugins
+      .map((plugin, index) => ({ plugin, index }))
+      .sort((a, b) => {
+        const nameA = String(a.plugin?.name ?? "");
+        const nameB = String(b.plugin?.name ?? "");
+        const nameCompare = nameA.localeCompare(nameB, undefined, {
+          sensitivity: "base",
+        });
+        if (nameCompare !== 0) {
+          return nameCompare;
+        }
+        return a.index - b.index;
+      })
+      .map((item) => item.plugin);
+  };
+
</code_context>
<issue_to_address>
**suggestion (performance):** Consider simplifying sorting logic by relying on stable `Array.prototype.sort` instead of mapping to index-tagged objects.

Modern JS engines (evergreen browsers and Node 12+) guarantee a stable `Array.prototype.sort`, so you can rely on that stability instead of carrying the original index through the sort. That lets you drop the pre/post `map` calls and index tracking, keeping just the `localeCompare`-based comparator and operating directly on the `plugins` array (or a shallow copy if you want to avoid mutation).

Suggested implementation:

```javascript
  const sortPluginsByName = (plugins) => {
    return [...plugins].sort((a, b) => {
      const nameA = String(a?.name ?? "");
      const nameB = String(b?.name ?? "");
      return nameA.localeCompare(nameB, undefined, {
        sensitivity: "base",
      });
    });
  };

```

If callers of `sortPluginsByName` previously depended on it preserving the original array reference (i.e., mutating `plugins` in place), you can drop the spread and simply call `plugins.sort(...)` instead. In that case, update the body to `plugins.sort(...)` and optionally return `plugins` for chaining.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +330 to +339
const sortPluginsByName = (plugins) => {
return plugins
.map((plugin, index) => ({ plugin, index }))
.sort((a, b) => {
const nameA = String(a.plugin?.name ?? "");
const nameB = String(b.plugin?.name ?? "");
const nameCompare = nameA.localeCompare(nameB, undefined, {
sensitivity: "base",
});
if (nameCompare !== 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (performance): 考虑利用稳定的 Array.prototype.sort 来简化排序逻辑,而不是先映射成带索引的对象再排序。

现代 JS 引擎(主流浏览器和 Node 12+)都保证了 Array.prototype.sort 的稳定性,因此你可以依赖这种稳定性,而无需在排序过程中额外携带原始索引。这样就可以去掉排序前后的 map 调用和索引追踪,只保留基于 localeCompare 的比较器,并直接在 plugins 数组上操作(如果需要避免原地修改,可以先做一个浅拷贝)。

推荐实现:

  const sortPluginsByName = (plugins) => {
    return [...plugins].sort((a, b) => {
      const nameA = String(a?.name ?? "");
      const nameB = String(b?.name ?? "");
      return nameA.localeCompare(nameB, undefined, {
        sensitivity: "base",
      });
    });
  };

如果 sortPluginsByName 的调用方之前依赖它保留原数组引用(即就地修改 plugins),那你可以去掉展开操作,直接调用 plugins.sort(...)。这种情况下,将函数体更新为 plugins.sort(...),并可选择返回 plugins 以便链式调用。

Original comment in English

suggestion (performance): Consider simplifying sorting logic by relying on stable Array.prototype.sort instead of mapping to index-tagged objects.

Modern JS engines (evergreen browsers and Node 12+) guarantee a stable Array.prototype.sort, so you can rely on that stability instead of carrying the original index through the sort. That lets you drop the pre/post map calls and index tracking, keeping just the localeCompare-based comparator and operating directly on the plugins array (or a shallow copy if you want to avoid mutation).

Suggested implementation:

  const sortPluginsByName = (plugins) => {
    return [...plugins].sort((a, b) => {
      const nameA = String(a?.name ?? "");
      const nameB = String(b?.name ?? "");
      return nameA.localeCompare(nameB, undefined, {
        sensitivity: "base",
      });
    });
  };

If callers of sortPluginsByName previously depended on it preserving the original array reference (i.e., mutating plugins in place), you can drop the spread and simply call plugins.sort(...) instead. In that case, update the body to plugins.sort(...) and optionally return plugins for chaining.

@dosubot dosubot bot added the feature:plugin The bug / feature is about AstrBot plugin system. label Feb 28, 2026
Copy link
Contributor

@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

你好,感谢你的贡献。这次的改动通过增加按名称排序和改进搜索过滤逻辑,解决了插件列表在操作后顺序混乱的问题,提升了用户体验。代码实现方面,新的排序函数 sortPluginsByName 逻辑清晰,实现了稳定的不区分大小写排序。搜索过滤的空值处理也更加健壮。我有一个关于优化过滤逻辑可读性和可维护性的建议,请查看具体评论。

Comment on lines +354 to +370
filtered = plugins.filter((plugin) => {
const pluginName = (plugin.name ?? "").toLowerCase();
const pluginDesc = (plugin.desc ?? "").toLowerCase();
const pluginAuthor = (plugin.author ?? "").toLowerCase();
const supportPlatforms = Array.isArray(plugin.support_platforms)
? plugin.support_platforms.join(" ").toLowerCase()
: "";
const astrbotVersion = (plugin.astrbot_version ?? "").toLowerCase();

return (
pluginName.includes(search) ||
pluginDesc.includes(search) ||
pluginAuthor.includes(search) ||
supportPlatforms.includes(search) ||
astrbotVersion.includes(search)
);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

为了提高代码的可读性和可维护性,可以考虑将过滤逻辑重构一下。通过将所有需要搜索的字段放入一个数组中,然后使用 some 方法来检查是否至少有一个字段匹配搜索条件,可以避免重复的 toLowerCase().includes() 调用,也让未来增删搜索字段变得更加容易。

      filtered = plugins.filter((plugin) => {
        const fieldsToSearch = [
          plugin.name,
          plugin.desc,
          plugin.author,
          Array.isArray(plugin.support_platforms)
            ? plugin.support_platforms.join(" ")
            : "",
          plugin.astrbot_version,
        ];

        return fieldsToSearch.some((field) =>
          (field ?? "").toLowerCase().includes(search)
        );
      });

@dosubot dosubot bot added the lgtm This PR has been approved by a maintainer label Feb 28, 2026
@Soulter Soulter merged commit 0e2ca03 into AstrBotDevs:master Feb 28, 2026
6 checks passed
astrbot-doc-agent bot pushed a commit to AstrBotDevs/AstrBot-docs that referenced this pull request Feb 28, 2026
@astrbot-doc-agent
Copy link

Generated docs update PR (pending manual review):
AstrBotDevs/AstrBot-docs#153
Trigger: PR merged


AI change summary:

  • 更新 zh/use/webui.mden/use/webui.md,同步上游 PR fix: add plugin sorting by name and improve search filtering logic #5559 的功能变更。
  • 在“已安装插件列表”部分,补充说明插件按名称字母顺序排序显示。
  • 在“插件搜索”部分,更新搜索逻辑说明,支持在名称、描述、作者、支持平台、AstrBot 版本等字段中进行不区分大小写的匹配。
  • i18n 状态:中英文文档均已更新。

Experimental bot notice:

  • This output is generated by AstrBot-Doc-Agent for review only.
  • It does not represent the final documentation form.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature:plugin The bug / feature is about AstrBot plugin system. lgtm This PR has been approved by a maintainer size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 插件页面的插件顺序问题

2 participants