Skip to content

Feat(webui): dashboard and console qol improvements#7215

Merged
LIghtJUNction merged 4 commits intoAstrBotDevs:masterfrom
M1LKT:qolDev
Apr 2, 2026
Merged

Feat(webui): dashboard and console qol improvements#7215
LIghtJUNction merged 4 commits intoAstrBotDevs:masterfrom
M1LKT:qolDev

Conversation

@M1LKT
Copy link
Copy Markdown
Contributor

@M1LKT M1LKT commented Mar 31, 2026

Modifications / 改动点

本PR提供了三项提升用户体验的改动:

  • 为日志控制台增加了展开为全屏按钮,方便用户使用副屏进行监控

  • 现在已经在欢迎界面完成两项配置的用户,第二次登录的时候将会直接跳转到数据大屏界面;而不是欢迎界面

  • 为置顶的插件增加下置的插件名,增加可读性

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

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

  • 改动1预览:
image
  • 改动3预览:
image

Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”

  • 🤓 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.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txtpyproject.toml 文件相应位置。

  • 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。

Summary by Sourcery

Improve web UI dashboard and console usability with fullscreen log viewing, smarter post-login routing, and clearer pinned plugin labels.

New Features:

  • Add a fullscreen toggle for the log console to enable focused monitoring.
  • Display plugin names under pinned plugin avatars to improve readability of pinned items.
  • Redirect authenticated users who have completed initial configuration directly to the main dashboard instead of the welcome page.

@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Mar 31, 2026
@auto-assign auto-assign bot requested review from Fridemn and LIghtJUNction March 31, 2026 02:40
@dosubot dosubot bot added the area:webui The bug / feature is about webui(dashboard) of astrbot. label Mar 31, 2026
Copy link
Copy Markdown
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 - I've found 2 issues, and left some high level feedback:

  • The checkOnboardingCompleted method performs two network requests both on login and on every LoginPage mount when a token exists; consider caching the onboarding status in the store or reusing existing config state to avoid repeated calls and reduce login/redirect latency.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `checkOnboardingCompleted` method performs two network requests both on login and on every `LoginPage` mount when a token exists; consider caching the onboarding status in the store or reusing existing config state to avoid repeated calls and reduce login/redirect latency.

## Individual Comments

### Comment 1
<location path="dashboard/src/components/shared/ConsoleDisplayer.vue" line_range="267-269" />
<code_context>
       this.autoScroll = !this.autoScroll;
     },

+    toggleFullscreen() {
+      const container = document.getElementById('console-wrapper');
+      if (!document.fullscreenElement) {
+        container.requestFullscreen().catch(err => {
+          console.error(`Error attempting to enable full-screen mode: ${err.message}`);
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard against a missing `console-wrapper` element before calling `requestFullscreen`.

If `document.getElementById('console-wrapper')` returns `null` (e.g., after refactors or conditional rendering), `container.requestFullscreen()` will throw. Add a null check before calling `requestFullscreen`, for example:

```js
const container = document.getElementById('console-wrapper');
if (!container) {
  console.warn('Console wrapper element not found for fullscreen');
  return;
}
```
</issue_to_address>

### Comment 2
<location path="dashboard/src/stores/auth.ts" line_range="40" />
<code_context>
         return Promise.reject(error);
       }
     },
+    async checkOnboardingCompleted(): Promise<boolean> {
+      try {
+        // 1. 检查平台配置
</code_context>
<issue_to_address>
**issue (complexity):** Consider extracting the onboarding check logic into a dedicated service that also centralizes provider type resolution to keep the auth store focused and easier to maintain.

You can keep the new behavior but reduce complexity and coupling by:

1. Extracting onboarding checks out of the store into a small service/composable.
2. Simplifying provider detection with a helper that normalizes provider type resolution.

For example, create a dedicated onboarding service:

```ts
// services/onboardingService.ts
import axios from 'axios';

async function fetchPlatformHasConfig(): Promise<boolean> {
  const res = await axios.get('/api/config/get');
  return (res.data.data.config.platform || []).length > 0;
}

function resolveProviderType(
  provider: any,
  sourceTypeById: Record<string, string>,
): string | null {
  if (provider.provider_type) return provider.provider_type;
  if (provider.provider_source_id) {
    return sourceTypeById[provider.provider_source_id] || null;
  }
  const legacyType = String(provider.type || '');
  return legacyType || null;
}

async function fetchHasChatCompletionProvider(): Promise<boolean> {
  const res = await axios.get('/api/config/provider/template');
  const providers = res.data.data?.providers || [];
  const sources = res.data.data?.provider_sources || [];

  const sourceTypeById: Record<string, string> = {};
  sources.forEach((s: any) => {
    sourceTypeById[s.id] = s.provider_type;
  });

  return providers.some((p: any) => {
    const type = resolveProviderType(p, sourceTypeById);
    return type === 'chat_completion' || String(type).includes('chat_completion');
  });
}

export async function isOnboardingCompleted(): Promise<boolean> {
  try {
    const hasPlatform = await fetchPlatformHasConfig();
    if (!hasPlatform) return false;

    return await fetchHasChatCompletionProvider();
  } catch (e) {
    console.error('Failed to check onboarding status:', e);
    return false; // preserve existing behavior
  }
}
```

Then the store stays focused and orchestration is clearer:

```ts
// in auth store
import { isOnboardingCompleted } from '@/services/onboardingService';

async login(username: string, password: string) {
  try {
    const res = await axios.post('/api/login', { username, password });
    // ... existing login logic ...

    const onboardingCompleted = await isOnboardingCompleted();
    this.returnUrl = null;

    router.push(onboardingCompleted ? '/dashboard/default' : '/welcome');
  } catch (error) {
    return Promise.reject(error);
  }
}
```

This keeps functionality identical while:
- Removing cross-cutting onboarding logic from the auth store.
- Encapsulating network + domain logic into a service.
- Making provider type resolution self-contained and easier to reason about.
</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 +267 to +269
toggleFullscreen() {
const container = document.getElementById('console-wrapper');
if (!document.fullscreenElement) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Guard against a missing console-wrapper element before calling requestFullscreen.

If document.getElementById('console-wrapper') returns null (e.g., after refactors or conditional rendering), container.requestFullscreen() will throw. Add a null check before calling requestFullscreen, for example:

const container = document.getElementById('console-wrapper');
if (!container) {
  console.warn('Console wrapper element not found for fullscreen');
  return;
}

return Promise.reject(error);
}
},
async checkOnboardingCompleted(): Promise<boolean> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue (complexity): Consider extracting the onboarding check logic into a dedicated service that also centralizes provider type resolution to keep the auth store focused and easier to maintain.

You can keep the new behavior but reduce complexity and coupling by:

  1. Extracting onboarding checks out of the store into a small service/composable.
  2. Simplifying provider detection with a helper that normalizes provider type resolution.

For example, create a dedicated onboarding service:

// services/onboardingService.ts
import axios from 'axios';

async function fetchPlatformHasConfig(): Promise<boolean> {
  const res = await axios.get('/api/config/get');
  return (res.data.data.config.platform || []).length > 0;
}

function resolveProviderType(
  provider: any,
  sourceTypeById: Record<string, string>,
): string | null {
  if (provider.provider_type) return provider.provider_type;
  if (provider.provider_source_id) {
    return sourceTypeById[provider.provider_source_id] || null;
  }
  const legacyType = String(provider.type || '');
  return legacyType || null;
}

async function fetchHasChatCompletionProvider(): Promise<boolean> {
  const res = await axios.get('/api/config/provider/template');
  const providers = res.data.data?.providers || [];
  const sources = res.data.data?.provider_sources || [];

  const sourceTypeById: Record<string, string> = {};
  sources.forEach((s: any) => {
    sourceTypeById[s.id] = s.provider_type;
  });

  return providers.some((p: any) => {
    const type = resolveProviderType(p, sourceTypeById);
    return type === 'chat_completion' || String(type).includes('chat_completion');
  });
}

export async function isOnboardingCompleted(): Promise<boolean> {
  try {
    const hasPlatform = await fetchPlatformHasConfig();
    if (!hasPlatform) return false;

    return await fetchHasChatCompletionProvider();
  } catch (e) {
    console.error('Failed to check onboarding status:', e);
    return false; // preserve existing behavior
  }
}

Then the store stays focused and orchestration is clearer:

// in auth store
import { isOnboardingCompleted } from '@/services/onboardingService';

async login(username: string, password: string) {
  try {
    const res = await axios.post('/api/login', { username, password });
    // ... existing login logic ...

    const onboardingCompleted = await isOnboardingCompleted();
    this.returnUrl = null;

    router.push(onboardingCompleted ? '/dashboard/default' : '/welcome');
  } catch (error) {
    return Promise.reject(error);
  }
}

This keeps functionality identical while:

  • Removing cross-cutting onboarding logic from the auth store.
  • Encapsulating network + domain logic into a service.
  • Making provider type resolution self-contained and easier to reason about.

Copy link
Copy Markdown
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 several UI and logic enhancements, including displaying plugin names in the pinned items list, adding a fullscreen toggle for the console displayer, and implementing an onboarding check to redirect users after login. Feedback focuses on improving code robustness with optional chaining, adhering to Vue best practices by using refs instead of direct DOM manipulation, addressing accessibility concerns regarding color contrast in the console, and resolving potential layout overflows in the pinned plugin component.

try {
// 1. 检查平台配置
const platformRes = await axios.get('/api/config/get');
const hasPlatform = (platformRes.data.data.config.platform || []).length > 0;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

建议使用可选链(Optional Chaining)来安全地访问属性。如果 API 返回的数据结构中 dataconfig 缺失,直接访问 config.platform 会导致运行时错误。

Suggested change
const hasPlatform = (platformRes.data.data.config.platform || []).length > 0;
const hasPlatform = (platformRes.data.data?.config?.platform || []).length > 0;


<template>
<div>
<div class="console-displayer-wrapper" id="console-wrapper">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

建议同时添加 ref 属性,以便在 Vue 组件内部更安全地引用该 DOM 元素,减少对全局 document.getElementById 的依赖。

  <div class="console-displayer-wrapper" id="console-wrapper" ref="consoleWrapper">

},

toggleFullscreen() {
const container = document.getElementById('console-wrapper');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

建议使用 this.$refs 获取元素引用,这比 document.getElementById 更符合 Vue 的开发范式,且能避免潜在的 ID 冲突。

      const container = this.$refs.consoleWrapper;

@dosubot dosubot bot added the lgtm This PR has been approved by a maintainer label Apr 2, 2026
@M1LKT M1LKT requested a review from RC-CHN April 2, 2026 02:31
@LIghtJUNction LIghtJUNction merged commit e8d6938 into AstrBotDevs:master Apr 2, 2026
7 checks passed
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. lgtm This PR has been approved by a maintainer size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants