Skip to content
This repository was archived by the owner on Apr 12, 2026. It is now read-only.

Commit c3a7129

Browse files
piexianNekyuuYa
authored andcommitted
fix(dashboard): fix WebUI dark mode rendering and multiple UI interaction bugs (AstrBotDevs#7173)
* fix(dashboard): comprehensive dark mode improvements for WebUI - Tune DarkTheme.ts color palette: soften primary, fix on-surface-variant from black to light grey, adjust surface/background/border colors, add codeBg/preBg/code variables for dark code blocks - Add _HljsDark.scss for highlight.js dark token overrides and markstream-vue CSS variable overrides under .v-theme--PurpleThemeDark - Add global dark mode overrides in _override.scss: soften flat primary buttons, timeline dots, dialog card backgrounds, markdown link color - Fix hardcoded light backgrounds in IPythonToolBlock, ExtensionCard, T2ITemplateEditor, ExtensionPage, KnowledgeBase, LongTermMemory - Remove hardcoded color='black' from icons in ProviderPage, PlatformPage, PersonaPage - Desaturate console log ANSI colors for dark mode readability - Add iframe dark mode inversion filter in VerticalSidebar - Fix language switcher mobile adaptation and click behavior - Fix login page theme toggle tooltip showing wrong label * fix(dashboard): apply dark mode review feedback
1 parent 20e5ba8 commit c3a7129

File tree

18 files changed

+267
-76
lines changed

18 files changed

+267
-76
lines changed

dashboard/src/components/chat/message_list_comps/IPythonToolBlock.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ onMounted(async () => {
150150
}
151151
152152
.code-fallback.dark-theme {
153-
background-color: transparent;
153+
background-color: rgb(var(--v-theme-codeBg));
154154
}
155155
156156
.result-section {
@@ -178,7 +178,7 @@ onMounted(async () => {
178178
}
179179
180180
.result-content.dark-theme {
181-
background-color: transparent;
181+
background-color: rgb(var(--v-theme-codeBg));
182182
}
183183
184184
.animate-fade-in {

dashboard/src/components/shared/ConsoleDisplayer.vue

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@ export default {
3636
autoScroll: true,
3737
isFullscreen: false,
3838
logColorAnsiMap: {
39-
'\u001b[1;34m': 'color: #39C5BB; font-weight: bold;',
40-
'\u001b[1;36m': 'color: #00FFFF; font-weight: bold;',
41-
'\u001b[1;33m': 'color: #FFFF00; font-weight: bold;',
42-
'\u001b[31m': 'color: #FF0000;',
43-
'\u001b[1;31m': 'color: #FF0000; font-weight: bold;',
39+
'\u001b[1;34m': 'color: #6cb6d9; font-weight: bold;',
40+
'\u001b[1;36m': 'color: #72c4cc; font-weight: bold;',
41+
'\u001b[1;33m': 'color: #d4b95e; font-weight: bold;',
42+
'\u001b[31m': 'color: #d46a6a;',
43+
'\u001b[1;31m': 'color: #e06060; font-weight: bold;',
4444
'\u001b[0m': 'color: inherit; font-weight: normal;',
45-
'\u001b[32m': 'color: #00FF00;',
46-
'default': 'color: #FFFFFF;'
45+
'\u001b[32m': 'color: #6cc070;',
46+
'default': 'color: #c8c8c8;'
4747
},
4848
logLevels: ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
4949
selectedLevels: [0, 1, 2, 3, 4],

dashboard/src/components/shared/ExtensionCard.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,13 @@ const viewChangelog = () => {
164164
? marketMode
165165
? '#f8f0dd'
166166
: '#ffffff'
167-
: '#282833',
167+
: marketMode
168+
? '#3a3425'
169+
: '#282833',
168170
color:
169171
useCustomizerStore().uiTheme === 'PurpleTheme'
170172
? '#000000dd'
171-
: '#ffffff',
173+
: '#ffffffdd',
172174
}"
173175
>
174176
<v-card-text

dashboard/src/components/shared/ReadmeDialog.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import MarkdownIt from "markdown-it";
44
import hljs from "highlight.js";
55
import axios from "axios";
66
import DOMPurify from "dompurify";
7-
import "highlight.js/styles/github-dark.css";
7+
import "highlight.js/styles/github.css";
88
import { useI18n } from "@/i18n/composables";
99
1010
// 1. 在 setup 作用域创建 MarkdownIt 实例

dashboard/src/components/shared/T2ITemplateEditor.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
</v-btn>
1313
</template>
1414

15-
<v-card>
15+
<v-card class="t2i-template-editor">
1616
<v-card-title class="d-flex align-center justify-space-between">
1717
<span>{{ tm('t2iTemplateEditor.dialogTitle') }}</span>
1818
<v-spacer></v-spacer>
@@ -560,3 +560,9 @@ code {
560560
font-size: 0.875em;
561561
}
562562
</style>
563+
564+
<style>
565+
.v-theme--PurpleThemeDark .t2i-template-editor .preview-container {
566+
background-color: rgb(var(--v-theme-surface));
567+
}
568+
</style>

dashboard/src/layouts/full/vertical-header/VerticalHeader.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,7 @@ const isChristmas = computed(() => {
488488
});
489489
490490
// 语言切换相关
491+
const mainMenuOpen = ref(false);
491492
const { languageOptions, currentLanguage, switchLanguage, locale } = useLanguageSwitcher();
492493
const languages = computed(() =>
493494
languageOptions.value.map(lang => ({
@@ -499,6 +500,7 @@ const languages = computed(() =>
499500
const currentLocale = computed(() => locale.value);
500501
const changeLanguage = async (langCode: string) => {
501502
await switchLanguage(langCode as Locale);
503+
mainMenuOpen.value = false;
502504
};
503505
504506
onMounted(async () => {
@@ -591,7 +593,7 @@ onMounted(async () => {
591593

592594

593595
<!-- 功能菜单 -->
594-
<StyledMenu offset="12" location="bottom end">
596+
<StyledMenu v-model="mainMenuOpen" offset="12" location="bottom end">
595597
<template v-slot:activator="{ props: activatorProps }">
596598
<v-btn
597599
v-bind="activatorProps"
@@ -632,8 +634,8 @@ onMounted(async () => {
632634

633635
<!-- 语言切换分组 -->
634636
<v-menu
637+
open-on-click
635638
:open-on-hover="!$vuetify.display.xs"
636-
:open-on-click="$vuetify.display.xs"
637639
:open-delay="!$vuetify.display.xs ? 60 : 0"
638640
:close-delay="!$vuetify.display.xs ? 120 : 0"
639641
:location="$vuetify.display.xs ? 'bottom' : 'start center'"
@@ -642,6 +644,7 @@ onMounted(async () => {
642644
<template v-slot:activator="{ props: languageMenuProps }">
643645
<v-list-item
644646
v-bind="languageMenuProps"
647+
@click.stop
645648
class="styled-menu-item language-group-trigger"
646649
rounded="md"
647650
>

dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script setup>
2-
import { ref, shallowRef, onMounted, onUnmounted, watch } from 'vue';
2+
import { ref, shallowRef, computed, onMounted, onUnmounted, watch } from 'vue';
3+
import { useTheme } from 'vuetify';
34
import { useCustomizerStore } from '../../../stores/customizer';
45
import { useI18n } from '@/i18n/composables';
56
import sidebarItems from './sidebarItem';
@@ -10,6 +11,7 @@ import ChangelogDialog from '@/components/shared/ChangelogDialog.vue';
1011
const { t, locale } = useI18n();
1112
1213
const customizer = useCustomizerStore();
14+
const theme = useTheme();
1315
1416
function collectGroupValues(items, values = new Set()) {
1517
items.forEach((item) => {
@@ -84,53 +86,58 @@ const minSidebarWidth = 200;
8486
const maxSidebarWidth = 300;
8587
const isResizing = ref(false);
8688
87-
const iframeStyle = ref({
88-
position: 'fixed',
89-
bottom: '16px',
90-
right: '16px',
91-
width: '490px',
92-
height: '640px',
93-
minWidth: '300px',
94-
minHeight: '200px',
95-
background: 'white',
96-
resize: 'both',
97-
overflow: 'auto',
98-
zIndex: '10000000',
99-
borderRadius: '12px',
100-
boxShadow: '0px 4px 12px rgba(0, 0, 0, 0.1)',
101-
});
89+
const isDark = computed(() => customizer.uiTheme === 'PurpleThemeDark');
90+
const themeColors = computed(() => theme.current.value.colors);
91+
const iframeBackground = computed(() => isDark.value ? themeColors.value.surface || 'white' : 'white');
92+
const dragHeaderBackground = computed(() => isDark.value ? themeColors.value.mcpCardBg || themeColors.value.surface || 'white' : '#f0f0f0');
93+
const frameBorder = computed(() => `1px solid ${isDark.value ? (themeColors.value.borderLight || '#ccc') : '#ccc'}`);
94+
95+
const isMobile = window.innerWidth < 768;
96+
if (isMobile) {
97+
customizer.Sidebar_drawer = false;
98+
}
99+
100+
const dragPos = ref({ left: '', top: '' });
102101
103-
if (window.innerWidth < 768) {
104-
iframeStyle.value = {
105-
position: 'fixed',
106-
top: '10%',
107-
left: '0%',
108-
width: '100%',
109-
height: '80%',
102+
const iframeStyle = computed(() => {
103+
const base = isMobile
104+
? { position: 'fixed', top: '10%', left: '0%', width: '100%', height: '80%', zIndex: '1002' }
105+
: { position: 'fixed', bottom: '16px', right: '16px', width: '490px', height: '640px', zIndex: '10000000' };
106+
const pos = dragPos.value.left ? { left: dragPos.value.left, top: dragPos.value.top, bottom: 'auto', right: 'auto' } : {};
107+
return {
108+
...base,
109+
...pos,
110110
minWidth: '300px',
111111
minHeight: '200px',
112-
background: 'white',
112+
background: iframeBackground.value,
113113
resize: 'both',
114114
overflow: 'auto',
115-
zIndex: '1002',
116115
borderRadius: '12px',
117-
boxShadow: '0px 4px 12px rgba(0, 0, 0, 0.1)',
116+
boxShadow: isDark.value ? '0px 4px 16px rgba(0, 0, 0, 0.5)' : '0px 4px 12px rgba(0, 0, 0, 0.1)',
118117
};
119-
customizer.Sidebar_drawer = false;
120-
}
118+
});
121119
122-
const dragHeaderStyle = {
120+
const iframeInnerStyle = computed(() => ({
121+
width: '100%',
122+
height: 'calc(100% - 66px)',
123+
border: 'none',
124+
borderBottomLeftRadius: '12px',
125+
borderBottomRightRadius: '12px',
126+
filter: isDark.value ? 'invert(0.88) hue-rotate(180deg)' : 'none',
127+
}));
128+
129+
const dragHeaderStyle = computed(() => ({
123130
width: '100%',
124131
padding: '8px',
125-
background: '#f0f0f0',
126-
borderBottom: '1px solid #ccc',
132+
background: dragHeaderBackground.value,
133+
borderBottom: frameBorder.value,
127134
borderTopLeftRadius: '8px',
128135
borderTopRightRadius: '8px',
129136
display: 'flex',
130137
justifyContent: 'space-between',
131138
alignItems: 'center',
132139
cursor: 'move'
133-
};
140+
}));
134141
135142
function toggleIframe() {
136143
showIframe.value = !showIframe.value;
@@ -208,9 +215,8 @@ function moveAt(clientX, clientY) {
208215
const dm = document.getElementById('draggable-iframe');
209216
const newLeft = clamp(clientX - offsetX, 0, window.innerWidth - dm.offsetWidth);
210217
const newTop = clamp(clientY - offsetY, 0, window.innerHeight - dm.offsetHeight);
211-
// 将拖拽后的位置同步到响应式样式变量中
212-
iframeStyle.value.left = newLeft + 'px';
213-
iframeStyle.value.top = newTop + 'px';
218+
// Sync dragged position to reactive variable
219+
dragPos.value = { left: newLeft + 'px', top: newTop + 'px' };
214220
}
215221
216222
function endDrag() {
@@ -348,23 +354,23 @@ function openChangelogDialog() {
348354
icon
349355
@click.stop="openIframeLink('https://astrbot.app')"
350356
@mousedown.stop
351-
style="border-radius: 8px; border: 1px solid #ccc;"
357+
:style="{ borderRadius: '8px', border: frameBorder }"
352358
>
353359
<v-icon icon="mdi-open-in-new" />
354360
</v-btn>
355361
<v-btn
356362
icon
357363
@click.stop="toggleIframe"
358364
@mousedown.stop
359-
style="border-radius: 8px; border: 1px solid #ccc;"
365+
:style="{ borderRadius: '8px', border: frameBorder }"
360366
>
361367
<v-icon icon="mdi-close" />
362368
</v-btn>
363369
</div>
364370
</div>
365371
<iframe
366372
src="https://astrbot.app"
367-
style="width: 100%; height: calc(100% - 66px); border: none; border-bottom-left-radius: 12px; border-bottom-right-radius: 12px;"
373+
:style="iframeInnerStyle"
368374
></iframe>
369375
</div>
370376

dashboard/src/scss/_override.scss

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,35 @@ pre, code, .markdown pre, .markdown code, .release-notes pre, .release-notes cod
4545
color: var(--astrbot-code-color);
4646
}
4747

48+
// Dark mode global overrides
49+
.v-theme--PurpleThemeDark {
50+
// Override code text color for dark mode
51+
--astrbot-code-color: rgb(var(--v-theme-on-surface-variant));
4852

53+
// Soften flat primary buttons for dark mode
54+
.v-btn--variant-flat.bg-primary {
55+
background-color: rgba(var(--v-theme-primary), 0.85) !important;
56+
}
57+
58+
// Timeline dots: use a muted version
59+
.v-timeline-item .v-timeline-divider__dot .v-timeline-divider__inner-dot {
60+
opacity: 0.85;
61+
}
62+
63+
// Use a darker scrim for overlays and dialogs in dark mode.
64+
.v-overlay__scrim {
65+
background-color: rgba(var(--v-theme-background), 0.72) !important;
66+
opacity: 1 !important;
67+
}
68+
69+
// Ensure dialog/card surfaces use the dark surface color
70+
.v-overlay__content > .v-card {
71+
background-color: rgb(var(--v-theme-surface)) !important;
72+
}
73+
74+
// Links inside markdown - slightly muted
75+
.markdown-content a,
76+
.markstream-vue a {
77+
color: rgb(var(--v-theme-primary));
78+
}
79+
}

0 commit comments

Comments
 (0)