Skip to content

feat: add ConfigRouteManagerDialog component for managing routing configurations#5597

Open
Soulter wants to merge 1 commit intomasterfrom
feat/configpageroute
Open

feat: add ConfigRouteManagerDialog component for managing routing configurations#5597
Soulter wants to merge 1 commit intomasterfrom
feat/configpageroute

Conversation

@Soulter
Copy link
Member

@Soulter Soulter commented Mar 1, 2026

  • Implemented a new dialog component for managing routes associated with configurations, allowing users to view, delete, and save routes.
  • Enhanced the ProviderSelector component with improved styling for better readability.
  • Updated English and Chinese localization files to include new strings for the route manager and profile sidebar.
  • Refactored ConfigPage.vue to integrate the new route management dialog and improve layout responsiveness.
  • Added methods for handling route management, including fetching, saving, and removing routes.

Modifications / 改动点

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

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


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

为配置配置文件引入路由管理界面,并改进配置页面布局和用户体验(UX)。

新功能:

  • 添加 ConfigRouteManagerDialog 组件,用于查看和管理与配置配置文件关联的路由条目。
  • 添加 ConfigProfileSidebar 组件,用于列出配置配置文件及其平台绑定,并提供快速访问路由管理的入口。

增强:

  • ConfigPage 布局重构为双栏工作台,包含固定的配置文件侧边栏、自适应工具栏,并改进未保存更改和加载状态的处理。
  • 通过调整字体大小,提升 ProviderSelector 中已选提供方文本的可读性。
  • 改进 AstrBotMessage 的平台消息模型文档,使用更清晰的英文字段描述。

日常维护(Chores):

  • 通过消费路由和平台 API 填充并维护配置与平台绑定的映射,用于支持侧边栏和对话框视图。
  • 为路由管理器对话框和配置文件侧边栏添加中英文 i18n 文案。
Original summary in English

Summary by Sourcery

Introduce a routing management UI for configuration profiles and improve the config page layout and UX.

New Features:

  • Add ConfigRouteManagerDialog component to view and manage routing entries associated with a configuration profile.
  • Add ConfigProfileSidebar component to list configuration profiles with their platform bindings and provide quick access to route management.

Enhancements:

  • Refactor ConfigPage layout into a two-pane workbench with a sticky profile sidebar, responsive toolbar, and improved handling of unsaved changes and loading state.
  • Improve ProviderSelector selected provider text readability with adjusted font sizing.
  • Enhance platform message model documentation for AstrBotMessage with clearer, English-language field descriptions.

Chores:

  • Populate and maintain configuration-to-platform binding mappings by consuming routing and platform APIs, enabling sidebar and dialog views.
  • Add i18n strings for the route manager dialog and profile sidebar in English and Chinese locales.

…figurations

- Implemented a new dialog component for managing routes associated with configurations, allowing users to view, delete, and save routes.
- Enhanced the ProviderSelector component with improved styling for better readability.
- Updated English and Chinese localization files to include new strings for the route manager and profile sidebar.
- Refactored ConfigPage.vue to integrate the new route management dialog and improve layout responsiveness.
- Added methods for handling route management, including fetching, saving, and removing routes.
@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Mar 1, 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!

This pull request significantly enhances the configuration management capabilities by introducing dedicated components for managing routing configurations and improving the overall user interface for configuration profiles. It streamlines the process of associating platform sessions with specific configurations, making the system more intuitive and robust for users to define how different platforms interact with their bot's settings.

Highlights

  • New Route Management Dialog: Introduced a ConfigRouteManagerDialog component, enabling users to view, delete, and save routing configurations associated with specific config profiles. This dialog provides a clear interface for managing which platform sessions are handled by which configuration.
  • Enhanced Configuration Profile Sidebar: Added a ConfigProfileSidebar component to the ConfigPage, which displays a list of configuration profiles along with their associated platform bindings (routes). This sidebar improves navigation and provides quick access to route management for each profile.
  • Improved ConfigPage Layout and Functionality: The main ConfigPage has been refactored to integrate the new sidebar and route manager dialog. This includes updates to data properties, methods for fetching, saving, and removing routes, and a more responsive layout. The logic for handling unsaved changes during config switching has also been refined.
  • Localization Updates: New localization strings have been added for both English and Chinese to support the new profile sidebar and route manager features, ensuring a consistent user experience across languages.
  • Message Object Documentation: Updated docstrings and removed redundant comments in the AstrBotMessage class to provide clearer and more concise explanations of its attributes and properties.
Changelog
  • astrbot/core/platform/astrbot_message.py
    • Updated docstrings for AstrBotMessage class and its attributes to provide more detailed explanations.
    • Removed redundant comments from group_id property and its setter.
  • dashboard/src/components/config/ConfigProfileSidebar.vue
    • Added new Vue component ConfigProfileSidebar to display configuration profiles and their platform bindings.
    • Implemented logic to show visible bindings with tooltips and a count for additional bindings.
    • Included event emissions for selecting a config, managing configs, and managing routes for a specific config.
  • dashboard/src/components/config/ConfigRouteManagerDialog.vue
    • Added new Vue dialog component ConfigRouteManagerDialog for managing routes.
    • Implemented functionality to group routes by platform ID and display UMOPs.
    • Provided options to delete individual routes and save changes, with loading and saving states.
  • dashboard/src/components/shared/ProviderSelector.vue
    • Adjusted the font size of the selected-label class to 13px for improved readability.
  • dashboard/src/i18n/locales/en-US/features/config.json
    • Added new localization keys and values for profileSidebar and routeManager features.
  • dashboard/src/i18n/locales/zh-CN/features/config.json
    • Added new localization keys and values for profileSidebar and routeManager features in Chinese.
  • dashboard/src/views/ConfigPage.vue
    • Integrated ConfigProfileSidebar and ConfigRouteManagerDialog components into the page.
    • Refactored the layout with new CSS classes (config-page-wrap, config-workbench, config-sidebar, config-main) for improved structure and responsiveness.
    • Added new data properties for managing route dialog state, loading, saving, and route items.
    • Implemented methods for opening and managing the route dialog (openRouteManageDialog, removeRouteItem, saveRouteManageDialog).
    • Introduced helper methods for parsing UMOPs, creating route items, and checking route entry relevance (parseUmop, createRouteItem, isRouteEntryForConfig).
    • Added buildConfigBindingMap and refreshConfigBindings methods to process and display routing information.
    • Modified onConfigSelect to handle unsaved changes more robustly and removed the 'manage' option from the dropdown.
    • Updated getConfigInfoList to sort configuration items and trigger binding refreshes.
    • Removed lastSavedConfigSnapshot and getConfigSnapshot properties/methods, updating the hasUnsavedChanges logic.
    • Added isLoadingConfig state to manage UI during config loading.
    • Adjusted media queries for responsive display of the configuration page elements.
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 dosubot bot added the area:webui The bug / feature is about webui(dashboard) of astrbot. label Mar 1, 2026
@dosubot
Copy link

dosubot bot commented Mar 1, 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 个问题,并留下了一些整体上的反馈:

  • 加载和映射路线/平台数据的逻辑在 openRouteManageDialog(构建 typeMap)以及 refreshConfigBindings/buildConfigBindingMap 中被重复实现;建议提取一个共享的辅助函数,或者直接复用 buildConfigBindingMap,这样既能保持行为一致,也有机会集中 API 调用。
  • onConfigSelect 方法里有嵌套分支,重复执行 this.selectedConfigID = value; this.getConfig(value);;可以重构为:先统一处理一次“未保存更改”的流程,然后在唯一的一个位置执行切换逻辑,这会让控制流更易理解,也更不容易出错。
面向 AI Agent 的提示
Please address the comments from this code review:

## Overall Comments
- 加载和映射路线/平台数据的逻辑在 `openRouteManageDialog`(构建 `typeMap`)以及 `refreshConfigBindings`/`buildConfigBindingMap` 中被重复实现;建议提取一个共享的辅助函数,或者直接复用 `buildConfigBindingMap`,这样既能保持行为一致,也有机会集中 API 调用。
- `onConfigSelect` 方法里有嵌套分支,重复执行 `this.selectedConfigID = value; this.getConfig(value);`;可以重构为:先统一处理一次“未保存更改”的流程,然后在唯一的一个位置执行切换逻辑,这会让控制流更易理解,也更不容易出错。

## Individual Comments

### Comment 1
<location path="dashboard/src/views/ConfigPage.vue" line_range="604-613" />
<code_context>
+          nonTargetUmopSet.add(umop);
+        });
+
+        const targetEntries = [];
+        for (const item of this.routeManageItems) {
+          const umop = String(item.umop || '').trim();
+          if (!umop) {
+            continue;
+          }
+          if (nonTargetUmopSet.has(umop)) {
+            this.save_message = this.tm('routeManager.routeOccupied', { umop });
+            this.save_message_snack = true;
+            this.save_message_success = "error";
+            this.routeManageSaving = false;
+            return;
+          }
+          targetEntries.push([umop, this.routeManageConfigId]);
+        }
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):** 校验同一条路线列表中的重复 UMoP,避免静默覆盖。

`nonTargetUmopSet` 可以防止与 `nonTargetEntries` 的冲突,但仍然允许 `this.routeManageItems` 内部存在重复项。如果同一个 `umop` 被添加了两次,`Object.fromEntries(mergedEntries)` 只会保留最后一个。请对 `targetEntries` 做去重(例如通过 Set),或者在 `routeManageItems` 包含重复 UMoP 时抛出错误。

建议实现:

```
          nonTargetEntries.push([umop, confId]);
          nonTargetUmopSet.add(umop);
        });

        const targetEntries = [];
        const targetUmopSet = new Set();
        for (const item of this.routeManageItems) {
          const umop = String(item.umop || '').trim();
          if (!umop) {
            continue;
          }
          if (nonTargetUmopSet.has(umop)) {
            this.save_message = this.tm('routeManager.routeOccupied', { umop });
            this.save_message_snack = true;
            this.save_message_success = "error";
            this.routeManageSaving = false;
            return;
          }
          if (targetUmopSet.has(umop)) {
            this.save_message = this.tm('routeManager.duplicateUmop', { umop });
            this.save_message_snack = true;
            this.save_message_success = "error";
            this.routeManageSaving = false;
            return;
          }
          targetUmopSet.add(umop);
          targetEntries.push([umop, this.routeManageConfigId]);
        }

```

1. 在 i18n 文案文件中为新的错误 key `routeManager.duplicateUmop` 添加本地化字符串(例如:`"routeManager.duplicateUmop": "UMoP {umop} is listed more than once in this route."`),并与现有翻译风格保持一致。
2. 如果你的项目对 i18n key 的命名规范或文案措辞有不同偏好,请在组件和语言文件中相应调整该 key 和消息内容。
</issue_to_address>

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

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

  • The logic for loading and mapping route/platform data is duplicated between openRouteManageDialog (building typeMap) and refreshConfigBindings/buildConfigBindingMap; consider extracting a shared helper or reusing buildConfigBindingMap so the behavior stays consistent and the API calls can potentially be centralized.
  • The onConfigSelect method has nested branches that repeat this.selectedConfigID = value; this.getConfig(value);; refactoring to handle the "unsaved changes" flow once and then perform the switch in a single place would make the control flow easier to follow and less error-prone.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The logic for loading and mapping route/platform data is duplicated between `openRouteManageDialog` (building `typeMap`) and `refreshConfigBindings`/`buildConfigBindingMap`; consider extracting a shared helper or reusing `buildConfigBindingMap` so the behavior stays consistent and the API calls can potentially be centralized.
- The `onConfigSelect` method has nested branches that repeat `this.selectedConfigID = value; this.getConfig(value);`; refactoring to handle the "unsaved changes" flow once and then perform the switch in a single place would make the control flow easier to follow and less error-prone.

## Individual Comments

### Comment 1
<location path="dashboard/src/views/ConfigPage.vue" line_range="604-613" />
<code_context>
+          nonTargetUmopSet.add(umop);
+        });
+
+        const targetEntries = [];
+        for (const item of this.routeManageItems) {
+          const umop = String(item.umop || '').trim();
+          if (!umop) {
+            continue;
+          }
+          if (nonTargetUmopSet.has(umop)) {
+            this.save_message = this.tm('routeManager.routeOccupied', { umop });
+            this.save_message_snack = true;
+            this.save_message_success = "error";
+            this.routeManageSaving = false;
+            return;
+          }
+          targetEntries.push([umop, this.routeManageConfigId]);
+        }
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Validate duplicate UMoPs within the same route list to avoid silent overwrites.

`nonTargetUmopSet` prevents conflicts with `nonTargetEntries`, but duplicates within `this.routeManageItems` are still allowed. If the same `umop` is added twice, `Object.fromEntries(mergedEntries)` will drop all but the last. Please either deduplicate `targetEntries` (e.g., via a Set) or surface an error when `routeManageItems` contains duplicate UMoPs.

Suggested implementation:

```
          nonTargetEntries.push([umop, confId]);
          nonTargetUmopSet.add(umop);
        });

        const targetEntries = [];
        const targetUmopSet = new Set();
        for (const item of this.routeManageItems) {
          const umop = String(item.umop || '').trim();
          if (!umop) {
            continue;
          }
          if (nonTargetUmopSet.has(umop)) {
            this.save_message = this.tm('routeManager.routeOccupied', { umop });
            this.save_message_snack = true;
            this.save_message_success = "error";
            this.routeManageSaving = false;
            return;
          }
          if (targetUmopSet.has(umop)) {
            this.save_message = this.tm('routeManager.duplicateUmop', { umop });
            this.save_message_snack = true;
            this.save_message_success = "error";
            this.routeManageSaving = false;
            return;
          }
          targetUmopSet.add(umop);
          targetEntries.push([umop, this.routeManageConfigId]);
        }

```

1. Add a localized string for the new error key `routeManager.duplicateUmop` in your i18n message files (e.g., something like `"routeManager.duplicateUmop": "UMoP {umop} is listed more than once in this route."`), matching your existing translation patterns.
2. If your project prefers a different naming convention for i18n keys or message wording, adjust the key and message accordingly in both this component and the locale files.
</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 +604 to +613
const targetEntries = [];
for (const item of this.routeManageItems) {
const umop = String(item.umop || '').trim();
if (!umop) {
continue;
}
if (nonTargetUmopSet.has(umop)) {
this.save_message = this.tm('routeManager.routeOccupied', { umop });
this.save_message_snack = true;
this.save_message_success = "error";
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (bug_risk): 校验同一条路线列表中的重复 UMoP,避免静默覆盖。

nonTargetUmopSet 可以防止与 nonTargetEntries 的冲突,但仍然允许 this.routeManageItems 内部存在重复项。如果同一个 umop 被添加了两次,Object.fromEntries(mergedEntries) 只会保留最后一个。请对 targetEntries 做去重(例如通过 Set),或者在 routeManageItems 包含重复 UMoP 时抛出错误。

建议实现:

          nonTargetEntries.push([umop, confId]);
          nonTargetUmopSet.add(umop);
        });

        const targetEntries = [];
        const targetUmopSet = new Set();
        for (const item of this.routeManageItems) {
          const umop = String(item.umop || '').trim();
          if (!umop) {
            continue;
          }
          if (nonTargetUmopSet.has(umop)) {
            this.save_message = this.tm('routeManager.routeOccupied', { umop });
            this.save_message_snack = true;
            this.save_message_success = "error";
            this.routeManageSaving = false;
            return;
          }
          if (targetUmopSet.has(umop)) {
            this.save_message = this.tm('routeManager.duplicateUmop', { umop });
            this.save_message_snack = true;
            this.save_message_success = "error";
            this.routeManageSaving = false;
            return;
          }
          targetUmopSet.add(umop);
          targetEntries.push([umop, this.routeManageConfigId]);
        }

  1. 在 i18n 文案文件中为新的错误 key routeManager.duplicateUmop 添加本地化字符串(例如:"routeManager.duplicateUmop": "UMoP {umop} is listed more than once in this route."),并与现有翻译风格保持一致。
  2. 如果你的项目对 i18n key 的命名规范或文案措辞有不同偏好,请在组件和语言文件中相应调整该 key 和消息内容。
Original comment in English

suggestion (bug_risk): Validate duplicate UMoPs within the same route list to avoid silent overwrites.

nonTargetUmopSet prevents conflicts with nonTargetEntries, but duplicates within this.routeManageItems are still allowed. If the same umop is added twice, Object.fromEntries(mergedEntries) will drop all but the last. Please either deduplicate targetEntries (e.g., via a Set) or surface an error when routeManageItems contains duplicate UMoPs.

Suggested implementation:

          nonTargetEntries.push([umop, confId]);
          nonTargetUmopSet.add(umop);
        });

        const targetEntries = [];
        const targetUmopSet = new Set();
        for (const item of this.routeManageItems) {
          const umop = String(item.umop || '').trim();
          if (!umop) {
            continue;
          }
          if (nonTargetUmopSet.has(umop)) {
            this.save_message = this.tm('routeManager.routeOccupied', { umop });
            this.save_message_snack = true;
            this.save_message_success = "error";
            this.routeManageSaving = false;
            return;
          }
          if (targetUmopSet.has(umop)) {
            this.save_message = this.tm('routeManager.duplicateUmop', { umop });
            this.save_message_snack = true;
            this.save_message_success = "error";
            this.routeManageSaving = false;
            return;
          }
          targetUmopSet.add(umop);
          targetEntries.push([umop, this.routeManageConfigId]);
        }

  1. Add a localized string for the new error key routeManager.duplicateUmop in your i18n message files (e.g., something like "routeManager.duplicateUmop": "UMoP {umop} is listed more than once in this route."), matching your existing translation patterns.
  2. If your project prefers a different naming convention for i18n keys or message wording, adjust the key and message accordingly in both this component and the locale files.

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

This pull request introduces a significant and well-executed feature for managing routing configurations through a new UI. The refactoring of ConfigPage.vue into a more organized workbench layout with a responsive design is a great improvement to the user experience. The new components, ConfigProfileSidebar and ConfigRouteManagerDialog, are well-implemented. My feedback includes a few suggestions to improve code style, user experience in the route manager, and code conciseness.

<v-icon size="24" class="mr-2">mdi-file-outline</v-icon>
{{ config.name }}
</div>
<div class="mt-3 d-flex" style="align-items: start; justify-content: center;">
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

For better code organization and adherence to best practices, it's recommended to avoid inline styles. Please move the styling for this div into a dedicated CSS class in the <style> block. This improves maintainability and separates concerns. You can then add the corresponding styles to a new .profile-card__routes class in the <style> block.

        <div class="mt-3 d-flex profile-card__routes">

Comment on lines +143 to +150
routes: (() => {
const sortedRoutes = routes.sort((a, b) => a.umop.localeCompare(b.umop));
const allSessionsRoute = sortedRoutes.find((route) => isAllSessionsRoute(route.umop));
if (allSessionsRoute) {
return [allSessionsRoute];
}
return sortedRoutes;
})()
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The current logic in groupedRoutes hides specific routes if a wildcard "all sessions" route (:*:*) exists for a platform. This might be confusing for users, as they won't see the complete picture of all configured routes for that platform. I suggest displaying all routes, with the "all sessions" route sorted to the top for clarity. This will provide a more transparent view of the configuration.

      routes: routes.sort((a, b) => {
        const aIsAll = isAllSessionsRoute(a.umop);
        const bIsAll = isAllSessionsRoute(b.umop);
        if (aIsAll !== bIsAll) {
          return aIsAll ? -1 : 1;
        }
        return a.umop.localeCompare(b.umop);
      })

Comment on lines +703 to +711
this.configInfoList = [...infoList].sort((a, b) => {
if (a.id === 'default' && b.id !== 'default') {
return -1;
}
if (a.id !== 'default' && b.id === 'default') {
return 1;
}
return 0;
});
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The sorting logic to place the 'default' configuration first is a bit verbose. It can be simplified for better readability and conciseness by using boolean-to-number conversion. This achieves the same result in a more compact way.

        this.configInfoList = [...infoList].sort((a, b) => {
          // Puts 'default' config at the top of the list.
          return (b.id === 'default') - (a.id === 'default');
        });

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

Labels

area:webui The bug / feature is about webui(dashboard) of astrbot. size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant