perf: webui theme color improvement (#6263)
* fix: update scrollbar styles to follow theme variables * fix: update theme colors to use CSS variables for consistency * fix: change login button color to primary for better visibility * fix: update theme colors for Dark and Light themes; change login button color to secondary * fix: update border and theme colors for consistency in DarkTheme * fix: update sidebar list class to conditionally hide scrollbar in mini sidebar mode * fix: simplify button visibility logic and remove unnecessary leftPadding style * fix: refactor language switcher to use grouped menu for better UX * fix: update theme colors to use primary color for consistency across components * fix: add preview text for template output in multiple languages
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
<transition name="fade">
|
||||
<div v-if="isDragging" class="drop-overlay">
|
||||
<div class="drop-overlay-content">
|
||||
<v-icon size="48" color="deep-purple">mdi-cloud-upload</v-icon>
|
||||
<v-icon size="48" color="primary">mdi-cloud-upload</v-icon>
|
||||
<span class="drop-text">{{ tm('input.dropToUpload') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -41,7 +41,7 @@
|
||||
<!-- Settings Menu -->
|
||||
<StyledMenu offset="8" location="top start" :close-on-content-click="false">
|
||||
<template v-slot:activator="{ props: activatorProps }">
|
||||
<v-btn v-bind="activatorProps" icon="mdi-plus" variant="text" color="deep-purple" />
|
||||
<v-btn v-bind="activatorProps" icon="mdi-plus" variant="text" color="primary" />
|
||||
</template>
|
||||
|
||||
<!-- Upload Files -->
|
||||
@@ -87,7 +87,7 @@
|
||||
{{ tm('voice.liveMode') }}
|
||||
</v-tooltip>
|
||||
</v-btn> -->
|
||||
<v-btn @click="handleRecordClick" icon variant="text" :color="isRecording ? 'error' : 'deep-purple'"
|
||||
<v-btn @click="handleRecordClick" icon variant="text" :color="isRecording ? 'error' : 'primary'"
|
||||
class="record-btn">
|
||||
<v-icon :icon="isRecording ? 'mdi-stop-circle' : 'mdi-microphone'" variant="text"
|
||||
plain></v-icon>
|
||||
@@ -95,13 +95,13 @@
|
||||
{{ isRecording ? tm('voice.speaking') : tm('voice.startRecording') }}
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
<v-btn icon v-if="isRunning" @click="$emit('stop')" variant="tonal" color="deep-purple" class="send-btn">
|
||||
<v-btn icon v-if="isRunning" @click="$emit('stop')" variant="tonal" color="primary" class="send-btn">
|
||||
<v-icon icon="mdi-stop" variant="text" plain></v-icon>
|
||||
<v-tooltip activator="parent" location="top">
|
||||
{{ tm('input.stopGenerating') }}
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
<v-btn v-else @click="$emit('send')" icon="mdi-send" variant="tonal" color="deep-purple"
|
||||
<v-btn v-else @click="$emit('send')" icon="mdi-send" variant="tonal" color="primary"
|
||||
:disabled="!canSend" class="send-btn" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -117,7 +117,7 @@
|
||||
</div>
|
||||
|
||||
<div v-if="stagedAudioUrl" class="audio-preview">
|
||||
<v-chip color="deep-purple-lighten-4" class="audio-chip">
|
||||
<v-chip color="primary" variant="tonal" class="audio-chip">
|
||||
<v-icon start icon="mdi-microphone" size="small"></v-icon>
|
||||
{{ tm('voice.recording') }}
|
||||
</v-chip>
|
||||
@@ -126,7 +126,7 @@
|
||||
</div>
|
||||
|
||||
<div v-for="(file, index) in stagedFiles" :key="'file-' + index" class="file-preview">
|
||||
<v-chip color="blue-grey-lighten-4" class="file-chip">
|
||||
<v-chip color="primary" variant="tonal" class="file-chip">
|
||||
<v-icon start icon="mdi-file-document-outline" size="small"></v-icon>
|
||||
<span class="file-name-preview">{{ file.original_name }}</span>
|
||||
</v-chip>
|
||||
@@ -399,8 +399,8 @@ defineExpose({
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(103, 58, 183, 0.15);
|
||||
border: 2px dashed rgba(103, 58, 183, 0.5);
|
||||
background-color: rgba(var(--v-theme-primary), 0.12);
|
||||
border: 2px dashed rgba(var(--v-theme-primary), 0.45);
|
||||
border-radius: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -419,7 +419,7 @@ defineExpose({
|
||||
.drop-text {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #673ab7;
|
||||
color: rgb(var(--v-theme-primary));
|
||||
}
|
||||
|
||||
/* Fade transition for drop overlay */
|
||||
@@ -439,7 +439,7 @@ defineExpose({
|
||||
justify-content: space-between;
|
||||
padding: 8px 16px;
|
||||
margin: 8px 8px 0 8px;
|
||||
background-color: rgba(103, 58, 183, 0.06);
|
||||
background-color: rgba(var(--v-theme-primary), 0.06);
|
||||
border-radius: 12px;
|
||||
gap: 8px;
|
||||
max-height: 500px;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
'mobile-sidebar-open': isMobile && mobileMenuOpen,
|
||||
'mobile-sidebar': isMobile
|
||||
}"
|
||||
:style="{ 'background-color': isDark ? sidebarCollapsed ? '#1e1e1e' : '#2d2d2d' : sidebarCollapsed ? '#ffffff' : '#f1f4f9' }">
|
||||
:style="{ backgroundColor: sidebarCollapsed && !isMobile ? 'rgb(var(--v-theme-surface))' : 'rgb(var(--v-theme-mcpCardBg))' }">
|
||||
|
||||
<div class="sidebar-collapse-btn-container" v-if="!isMobile">
|
||||
<v-btn icon class="sidebar-collapse-btn" @click="toggleSidebar" variant="text" color="deep-purple">
|
||||
@@ -46,7 +46,7 @@
|
||||
<v-list-item v-for="item in sessions" :key="item.session_id" :value="item.session_id"
|
||||
rounded="lg" class="conversation-item" active-color="secondary">
|
||||
<v-list-item-title v-if="!sidebarCollapsed || isMobile" class="conversation-title"
|
||||
:style="{ color: isDark ? '#ffffff' : '#000000' }">
|
||||
:style="{ color: 'rgb(var(--v-theme-primaryText))' }">
|
||||
{{ item.display_name || tm('conversation.newConversation') }}
|
||||
</v-list-item-title>
|
||||
<!-- <v-list-item-subtitle v-if="!sidebarCollapsed || isMobile" class="timestamp">
|
||||
@@ -98,16 +98,52 @@
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
<!-- 语言切换 -->
|
||||
<v-list-item class="styled-menu-item">
|
||||
<template v-slot:prepend>
|
||||
<v-icon>mdi-translate</v-icon>
|
||||
<!-- 语言切换(分组) -->
|
||||
<v-menu
|
||||
:open-on-hover="!isMobile"
|
||||
:open-on-click="isMobile"
|
||||
:open-delay="!isMobile ? 60 : 0"
|
||||
:close-delay="!isMobile ? 120 : 0"
|
||||
:location="isMobile ? 'bottom' : 'end center'"
|
||||
offset="8"
|
||||
close-on-content-click
|
||||
>
|
||||
<template v-slot:activator="{ props: languageMenuProps }">
|
||||
<v-list-item
|
||||
v-bind="languageMenuProps"
|
||||
class="styled-menu-item chat-settings-group-trigger"
|
||||
rounded="md"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-icon>mdi-translate</v-icon>
|
||||
</template>
|
||||
<v-list-item-title>{{ t('core.common.language') }}</v-list-item-title>
|
||||
<template v-slot:append>
|
||||
<span class="chat-settings-group-current">{{ currentLanguage?.flag }}</span>
|
||||
<v-icon size="18" class="chat-settings-group-arrow">mdi-chevron-right</v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<v-list-item-title>{{ t('core.common.language') }}</v-list-item-title>
|
||||
<template v-slot:append>
|
||||
<LanguageSwitcher variant="chatbox" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-card class="styled-menu-card" style="min-width: 180px;" elevation="8" rounded="lg">
|
||||
<v-list density="compact" class="styled-menu-list pa-1">
|
||||
<v-list-item
|
||||
v-for="lang in languages"
|
||||
:key="lang.code"
|
||||
:value="lang.code"
|
||||
@click="changeLanguage(lang.code)"
|
||||
:class="{ 'styled-menu-item-active': currentLocale === lang.code }"
|
||||
class="styled-menu-item"
|
||||
rounded="md"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<span class="language-flag">{{ lang.flag }}</span>
|
||||
</template>
|
||||
<v-list-item-title>{{ lang.name }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</v-menu>
|
||||
|
||||
<!-- 主题切换 -->
|
||||
<v-list-item class="styled-menu-item" @click="$emit('toggleTheme')">
|
||||
@@ -117,26 +153,49 @@
|
||||
<v-list-item-title>{{ isDark ? tm('modes.lightMode') : tm('modes.darkMode') }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<!-- 通信传输模式 -->
|
||||
<v-list-item class="styled-menu-item">
|
||||
<template v-slot:prepend>
|
||||
<v-icon>mdi-lan-connect</v-icon>
|
||||
<!-- 通信传输模式(分组) -->
|
||||
<v-menu
|
||||
:open-on-hover="!isMobile"
|
||||
:open-on-click="isMobile"
|
||||
:open-delay="!isMobile ? 60 : 0"
|
||||
:close-delay="!isMobile ? 120 : 0"
|
||||
:location="isMobile ? 'bottom' : 'end center'"
|
||||
offset="8"
|
||||
close-on-content-click
|
||||
>
|
||||
<template v-slot:activator="{ props: transportMenuProps }">
|
||||
<v-list-item
|
||||
v-bind="transportMenuProps"
|
||||
class="styled-menu-item chat-settings-group-trigger"
|
||||
rounded="md"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-icon>mdi-lan-connect</v-icon>
|
||||
</template>
|
||||
<v-list-item-title>{{ tm('transport.title') }}</v-list-item-title>
|
||||
<template v-slot:append>
|
||||
<span class="chat-settings-group-current chat-settings-transport-current">{{ currentTransportLabel }}</span>
|
||||
<v-icon size="18" class="chat-settings-group-arrow">mdi-chevron-right</v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<v-list-item-title>{{ tm('transport.title') }}</v-list-item-title>
|
||||
<template v-slot:append>
|
||||
<v-select
|
||||
:model-value="transportMode"
|
||||
:items="transportOptions"
|
||||
item-title="label"
|
||||
item-value="value"
|
||||
density="compact"
|
||||
variant="underlined"
|
||||
hide-details
|
||||
class="transport-mode-select"
|
||||
@update:model-value="handleTransportModeChange"
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-card class="styled-menu-card" style="min-width: 220px;" elevation="8" rounded="lg">
|
||||
<v-list density="compact" class="styled-menu-list pa-1">
|
||||
<v-list-item
|
||||
v-for="opt in transportOptions"
|
||||
:key="opt.value"
|
||||
:value="opt.value"
|
||||
@click="handleTransportModeChange(opt.value)"
|
||||
:class="{ 'styled-menu-item-active': transportMode === opt.value }"
|
||||
class="styled-menu-item"
|
||||
rounded="md"
|
||||
>
|
||||
<v-list-item-title>{{ opt.label }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</v-menu>
|
||||
|
||||
<!-- 全屏/退出全屏 -->
|
||||
<v-list-item class="styled-menu-item" @click="$emit('toggleFullscreen')">
|
||||
@@ -162,15 +221,16 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import { useI18n, useModuleI18n } from '@/i18n/composables';
|
||||
import type { Session } from '@/composables/useSessions';
|
||||
import { askForConfirmation, useConfirmDialog } from '@/utils/confirmDialog';
|
||||
import LanguageSwitcher from '@/components/shared/LanguageSwitcher.vue';
|
||||
import StyledMenu from '@/components/shared/StyledMenu.vue';
|
||||
import ProviderConfigDialog from '@/components/chat/ProviderConfigDialog.vue';
|
||||
import ProjectList from '@/components/chat/ProjectList.vue';
|
||||
import type { Project } from '@/components/chat/ProjectList.vue';
|
||||
import { useLanguageSwitcher } from '@/i18n/composables';
|
||||
import type { Locale } from '@/i18n/types';
|
||||
|
||||
interface Props {
|
||||
sessions: Session[];
|
||||
@@ -216,6 +276,25 @@ const transportOptions = [
|
||||
{ label: tm('transport.websocket'), value: 'websocket' as const }
|
||||
];
|
||||
|
||||
// Language switcher
|
||||
const { languageOptions, currentLanguage, switchLanguage, locale } = useLanguageSwitcher();
|
||||
const languages = computed(() =>
|
||||
languageOptions.value.map(lang => ({
|
||||
code: lang.value,
|
||||
name: lang.label,
|
||||
flag: lang.flag
|
||||
}))
|
||||
);
|
||||
const currentLocale = computed(() => locale.value);
|
||||
const changeLanguage = async (langCode: string) => {
|
||||
await switchLanguage(langCode as Locale);
|
||||
};
|
||||
|
||||
const currentTransportLabel = computed(() => {
|
||||
const found = transportOptions.find(opt => opt.value === props.transportMode);
|
||||
return found?.label ?? '';
|
||||
});
|
||||
|
||||
// 从 localStorage 读取侧边栏折叠状态
|
||||
const savedCollapsedState = localStorage.getItem('sidebarCollapsed');
|
||||
if (savedCollapsedState !== null) {
|
||||
@@ -310,7 +389,7 @@ function handleTransportModeChange(mode: string | null) {
|
||||
}
|
||||
|
||||
.conversation-item:hover {
|
||||
background-color: rgba(103, 58, 183, 0.05);
|
||||
background-color: rgba(var(--v-theme-primary), 0.05);
|
||||
}
|
||||
|
||||
.conversation-item:hover .conversation-actions {
|
||||
@@ -402,7 +481,28 @@ function handleTransportModeChange(mode: string | null) {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.transport-mode-select {
|
||||
min-width: 120px;
|
||||
.chat-settings-group-trigger :deep(.v-list-item__append) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.chat-settings-group-current {
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.chat-settings-transport-current {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.chat-settings-group-arrow {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.language-flag {
|
||||
font-size: 16px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<v-chip v-if="domain" class="ref-chip" size="x-small" variant="flat"
|
||||
:style="{ backgroundColor: isDark ? '#303030' : '#f4f4f4', color: isDark ? '#999' : '#666' }" :href="url"
|
||||
:style="chipStyle" :href="url"
|
||||
target="_blank" clickable>
|
||||
<v-icon start size="x-small" color>mdi-link-variant</v-icon>
|
||||
<span>{{ domain }}</span>
|
||||
|
||||
</v-chip>
|
||||
<span v-else class="ref-fallback" :style="{ color: isDark ? '#999' : '#666' }">{{ 'site' }}</span>
|
||||
<span v-else class="ref-fallback" :style="fallbackStyle">{{ 'site' }}</span>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -46,6 +46,15 @@ const domain = computed(() => {
|
||||
return ''
|
||||
}
|
||||
})
|
||||
|
||||
const chipStyle = computed(() => ({
|
||||
backgroundColor: isDark ? 'rgba(var(--v-theme-on-surface), 0.08)' : 'rgba(var(--v-theme-on-surface), 0.04)',
|
||||
color: isDark ? 'rgba(var(--v-theme-on-surface), 0.62)' : 'rgba(var(--v-theme-on-surface), 0.72)'
|
||||
}))
|
||||
|
||||
const fallbackStyle = computed(() => ({
|
||||
color: isDark ? 'rgba(var(--v-theme-on-surface), 0.62)' : 'rgba(var(--v-theme-on-surface), 0.72)'
|
||||
}))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
>
|
||||
<v-icon
|
||||
size="18"
|
||||
:color="props.variant === 'default' ? (useCustomizerStore().uiTheme === 'PurpleTheme' ? '#5e35b1' : '#d7c5fa') : undefined"
|
||||
:color="props.variant === 'default' ? 'rgb(var(--v-theme-primary))' : undefined"
|
||||
>
|
||||
mdi-translate
|
||||
</v-icon>
|
||||
@@ -42,7 +42,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useI18n, useLanguageSwitcher } from '@/i18n/composables'
|
||||
import { useCustomizerStore } from '@/stores/customizer'
|
||||
import type { Locale } from '@/i18n/types'
|
||||
import StyledMenu from '@/components/shared/StyledMenu.vue'
|
||||
|
||||
@@ -90,7 +89,7 @@ const changeLanguage = async (langCode: string) => {
|
||||
|
||||
.language-switcher--default:hover {
|
||||
transform: scale(1.05);
|
||||
background: rgba(94, 53, 177, 0.08) !important;
|
||||
background: rgba(var(--v-theme-primary), 0.08) !important;
|
||||
}
|
||||
|
||||
/* Header变体样式 - 完全继承Vuetify和action-btn的默认样式 */
|
||||
@@ -103,8 +102,4 @@ const changeLanguage = async (langCode: string) => {
|
||||
/* 继承action-btn样式,与工具栏主题按钮保持一致 */
|
||||
}
|
||||
|
||||
/* 深色模式下的悬停效果(仅对default变体) */
|
||||
:deep(.v-theme--PurpleThemeDark) .language-switcher--default:hover {
|
||||
background: rgba(114, 46, 209, 0.12) !important;
|
||||
}
|
||||
</style>
|
||||
@@ -6,11 +6,11 @@
|
||||
</div>
|
||||
<div class="logo-text">
|
||||
<h2
|
||||
:style="{color: useCustomizerStore().uiTheme === 'PurpleTheme' ? '#5e35b1' : '#d7c5fa'}"
|
||||
:style="{ color: 'rgb(var(--v-theme-primary))' }"
|
||||
v-html="formatTitle(title || t('core.header.logoTitle'))"
|
||||
></h2>
|
||||
<!-- 父子组件传递css变量可能会出错,暂时使用十六进制颜色值 -->
|
||||
<h4 :style="{color: useCustomizerStore().uiTheme === 'PurpleTheme' ? '#000000aa' : '#ffffffcc'}"
|
||||
<h4 :style="{ color: 'rgba(var(--v-theme-on-surface), 0.72)' }"
|
||||
class="hint-text">{{ subtitle || t('core.header.accountDialog.title') }}</h4>
|
||||
</div>
|
||||
</div>
|
||||
@@ -18,7 +18,6 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useCustomizerStore } from "@/stores/customizer";
|
||||
import { useI18n } from '@/i18n/composables';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -24,12 +24,12 @@ withDefaults(defineProps<{
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style>
|
||||
.styled-menu-card {
|
||||
min-width: 100px;
|
||||
width: fit-content;
|
||||
border: 1px solid rgba(94, 53, 177, 0.15) !important;
|
||||
background: #f8f6fc !important;
|
||||
border: 1px solid rgba(var(--v-theme-primary), 0.15) !important;
|
||||
background: rgba(var(--v-theme-surface), 0.98) !important;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
@@ -37,43 +37,41 @@ withDefaults(defineProps<{
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
:deep(.styled-menu-item) {
|
||||
.styled-menu-item {
|
||||
margin: 2px 0;
|
||||
transition: all 0.2s ease;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
:deep(.styled-menu-item:hover) {
|
||||
background: rgba(94, 53, 177, 0.08) !important;
|
||||
.styled-menu-item:hover {
|
||||
background: rgba(var(--v-theme-primary), 0.08) !important;
|
||||
}
|
||||
|
||||
:deep(.styled-menu-item-active) {
|
||||
background: rgba(94, 53, 177, 0.15) !important;
|
||||
.styled-menu-item-active {
|
||||
background: rgba(var(--v-theme-primary), 0.15) !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
:deep(.styled-menu-item-active:hover) {
|
||||
background: rgba(94, 53, 177, 0.2) !important;
|
||||
.styled-menu-item-active:hover {
|
||||
background: rgba(var(--v-theme-primary), 0.2) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* 深色模式下的下拉框样式 - 需要全局样式才能检测主题 */
|
||||
.v-theme--PurpleThemeDark .styled-menu-card {
|
||||
background: #2a2733 !important;
|
||||
border: 1px solid rgba(110, 60, 180, 0.692) !important;
|
||||
background: rgba(var(--v-theme-surface), 0.98) !important;
|
||||
border: 1px solid rgba(var(--v-theme-primary), 0.2) !important;
|
||||
}
|
||||
|
||||
/* 深色模式下的列表项悬停效果 */
|
||||
.v-theme--PurpleThemeDark .styled-menu-item:hover {
|
||||
background: rgba(114, 46, 209, 0.12) !important;
|
||||
background: rgba(var(--v-theme-primary), 0.12) !important;
|
||||
}
|
||||
|
||||
.v-theme--PurpleThemeDark .styled-menu-item-active {
|
||||
background: rgba(114, 46, 209, 0.2) !important;
|
||||
background: rgba(var(--v-theme-primary), 0.2) !important;
|
||||
}
|
||||
|
||||
.v-theme--PurpleThemeDark .styled-menu-item-active:hover {
|
||||
background: rgba(114, 46, 209, 0.25) !important;
|
||||
background: rgba(var(--v-theme-primary), 0.25) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
"save": "Save",
|
||||
"livePreview": "Live Preview (may differ)",
|
||||
"refreshPreview": "Refresh Preview",
|
||||
"previewText": "This is a sample text used to preview the template output.\n\nIt can contain multiple lines and various formatting.",
|
||||
"syntaxHint": "Supports jinja2 syntax. Available variables: text | safe (text to render), version (AstrBot version)",
|
||||
"saveAndApply": "Save and Apply Current Template",
|
||||
"confirmReset": "Confirm Reset",
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
"save": "Сохранить",
|
||||
"livePreview": "Предпросмотр (может отличаться)",
|
||||
"refreshPreview": "Обновить",
|
||||
"previewText": "Это пример текста для предпросмотра результата шаблона.\n\nОн может содержать несколько строк и различные форматы.",
|
||||
"syntaxHint": "Поддерживается синтаксис jinja2. Переменные: text | safe (текст для рендеринга), version (версия AstrBot)",
|
||||
"saveAndApply": "Сохранить и применить текущий шаблон",
|
||||
"confirmReset": "Подтверждение сброса",
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
"save": "保存",
|
||||
"livePreview": "实时预览(可能有差异)",
|
||||
"refreshPreview": "刷新预览",
|
||||
"previewText": "这是一个示例文本,用于预览模板效果。\n\n这里可以包含多行文本,支持换行和各种格式。",
|
||||
"syntaxHint": "支持 jinja2 语法。可用变量:text | safe(要渲染的文本), version(AstrBot 版本)",
|
||||
"saveAndApply": "保存应用当前编辑模板",
|
||||
"confirmReset": "确认重置",
|
||||
|
||||
@@ -465,23 +465,14 @@ onMounted(async () => {
|
||||
<v-app-bar elevation="0" height="50" class="top-header">
|
||||
|
||||
<!-- 桌面端 menu 按钮 - 仅在 bot 模式下显示 -->
|
||||
<v-btn v-if="customizer.viewMode === 'bot' && useCustomizerStore().uiTheme === 'PurpleTheme'" style="margin-left: 16px;"
|
||||
class="hidden-md-and-down" icon rounded="sm" variant="flat"
|
||||
@click.stop="customizer.SET_MINI_SIDEBAR(!customizer.mini_sidebar)">
|
||||
<v-icon>mdi-menu</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="customizer.viewMode === 'bot'"
|
||||
style="margin-left: 22px;"
|
||||
<v-btn v-if="customizer.viewMode === 'bot'"
|
||||
style="margin-left: 16px;"
|
||||
class="hidden-md-and-down" icon rounded="sm" variant="flat"
|
||||
@click.stop="customizer.SET_MINI_SIDEBAR(!customizer.mini_sidebar)">
|
||||
<v-icon>mdi-menu</v-icon>
|
||||
</v-btn>
|
||||
<!-- 移动端 menu 按钮 - 仅在 bot 模式下显示 -->
|
||||
<v-btn v-if="customizer.viewMode === 'bot' && useCustomizerStore().uiTheme === 'PurpleTheme'" class="hidden-lg-and-up ms-3"
|
||||
icon rounded="sm" variant="flat" @click.stop="customizer.SET_SIDEBAR_DRAWER">
|
||||
<v-icon>mdi-menu</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="customizer.viewMode === 'bot'" class="hidden-lg-and-up ms-3" icon rounded="sm" variant="flat"
|
||||
<v-btn v-if="customizer.viewMode === 'bot'" class="hidden-lg-and-up ms-3" icon rounded="sm" variant="flat"
|
||||
@click.stop="customizer.SET_SIDEBAR_DRAWER">
|
||||
<v-icon>mdi-menu</v-icon>
|
||||
</v-btn>
|
||||
@@ -572,21 +563,51 @@ onMounted(async () => {
|
||||
<v-divider class="my-1" />
|
||||
</template>
|
||||
|
||||
<!-- 语言切换 -->
|
||||
<v-list-item
|
||||
v-for="lang in languages"
|
||||
:key="lang.code"
|
||||
:value="lang.code"
|
||||
@click="changeLanguage(lang.code)"
|
||||
:class="{ 'styled-menu-item-active': currentLocale === lang.code }"
|
||||
class="styled-menu-item"
|
||||
rounded="md"
|
||||
<!-- 语言切换分组 -->
|
||||
<v-menu
|
||||
:open-on-hover="!$vuetify.display.xs"
|
||||
:open-on-click="$vuetify.display.xs"
|
||||
:open-delay="!$vuetify.display.xs ? 60 : 0"
|
||||
:close-delay="!$vuetify.display.xs ? 120 : 0"
|
||||
:location="$vuetify.display.xs ? 'bottom' : 'start center'"
|
||||
offset="8"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<span class="language-flag">{{ lang.flag }}</span>
|
||||
<template v-slot:activator="{ props: languageMenuProps }">
|
||||
<v-list-item
|
||||
v-bind="languageMenuProps"
|
||||
class="styled-menu-item language-group-trigger"
|
||||
rounded="md"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-icon>mdi-translate</v-icon>
|
||||
</template>
|
||||
<v-list-item-title>{{ t('core.common.language') }}</v-list-item-title>
|
||||
<template v-slot:append>
|
||||
<span class="language-group-current">{{ currentLanguage?.flag }}</span>
|
||||
<v-icon size="18" class="language-group-arrow">mdi-chevron-right</v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<v-list-item-title>{{ lang.name }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<v-card class="styled-menu-card" style="min-width: 180px;" elevation="8" rounded="lg">
|
||||
<v-list density="compact" class="styled-menu-list pa-1">
|
||||
<v-list-item
|
||||
v-for="lang in languages"
|
||||
:key="lang.code"
|
||||
:value="lang.code"
|
||||
@click="changeLanguage(lang.code)"
|
||||
:class="{ 'styled-menu-item-active': currentLocale === lang.code }"
|
||||
class="styled-menu-item"
|
||||
rounded="md"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<span class="language-flag">{{ lang.flag }}</span>
|
||||
</template>
|
||||
<v-list-item-title>{{ lang.name }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</v-menu>
|
||||
|
||||
<!-- 主题切换 -->
|
||||
<v-list-item
|
||||
@@ -978,6 +999,25 @@ onMounted(async () => {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.language-group-trigger :deep(.v-list-item__append) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.language-group-current {
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.language-group-arrow {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.language-submenu-card {
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.mobile-mode-toggle-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -288,7 +288,7 @@ function openChangelogDialog() {
|
||||
:rail="customizer.mini_sidebar"
|
||||
>
|
||||
<div class="sidebar-container">
|
||||
<v-list class="pa-4 listitem flex-grow-1" v-model:opened="openedItems" :open-strategy="'multiple'">
|
||||
<v-list :class="['pa-4', 'listitem', 'flex-grow-1', { 'hidden-scrollbar': customizer.mini_sidebar }]" v-model:opened="openedItems" :open-strategy="'multiple'">
|
||||
<template v-for="(item, i) in sidebarMenu" :key="item.title || item.to || `sidebar-item-${i}`">
|
||||
<NavItem :item="item" class="leftPadding" />
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
/* 自定义滚动条样式 - 紫色主题 */
|
||||
/* 自定义滚动条样式 - 跟随主题 */
|
||||
|
||||
:root {
|
||||
--astrbot-scrollbar-track: rgba(var(--v-theme-primary), 0.08);
|
||||
--astrbot-scrollbar-thumb: rgba(var(--v-theme-primary), 0.72);
|
||||
--astrbot-scrollbar-thumb-hover: rgba(var(--v-theme-primary), 0.84);
|
||||
--astrbot-scrollbar-thumb-active: rgba(var(--v-theme-primary), 0.94);
|
||||
--astrbot-scrollbar-thumb-border: rgba(var(--v-theme-surface), 0.5);
|
||||
--astrbot-scrollbar-thumb-shadow: rgba(var(--v-theme-primary), 0.32);
|
||||
}
|
||||
|
||||
/* 全局滚动条样式 */
|
||||
::-webkit-scrollbar {
|
||||
@@ -7,52 +16,31 @@
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
background: var(--astrbot-scrollbar-track);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(160, 60, 254, 0.75);
|
||||
background: var(--astrbot-scrollbar-thumb);
|
||||
border-radius: 5px;
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border: 1px solid var(--astrbot-scrollbar-thumb-border);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(147, 51, 234, 0.85);
|
||||
background: var(--astrbot-scrollbar-thumb-hover);
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 2px 8px rgba(147, 51, 234, 0.3);
|
||||
box-shadow: 0 2px 8px var(--astrbot-scrollbar-thumb-shadow);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:active {
|
||||
background: rgba(147, 51, 234, 0.95);
|
||||
background: var(--astrbot-scrollbar-thumb-active);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* 深色主题滚动条样式 */
|
||||
.v-theme--PurpleThemeDark {
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(192, 132, 252, 0.75);
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(192, 132, 252, 0.85);
|
||||
box-shadow: 0 2px 8px rgba(192, 132, 252, 0.4);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:active {
|
||||
background: rgba(192, 132, 252, 0.95);
|
||||
}
|
||||
}
|
||||
|
||||
/* 细滚动条变体 */
|
||||
.thin-scrollbar {
|
||||
::-webkit-scrollbar {
|
||||
@@ -61,17 +49,11 @@
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(147, 51, 234, 0.75);
|
||||
background: var(--astrbot-scrollbar-thumb);
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.v-theme--PurpleThemeDark .thin-scrollbar {
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(192, 132, 252, 0.75);
|
||||
}
|
||||
}
|
||||
|
||||
/* 聊天区域滚动条 */
|
||||
.chat-scrollbar {
|
||||
::-webkit-scrollbar {
|
||||
@@ -79,33 +61,18 @@
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(147, 51, 234, 0.08);
|
||||
background: var(--astrbot-scrollbar-track);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(147, 51, 234, 0.75);
|
||||
background: var(--astrbot-scrollbar-thumb);
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid var(--astrbot-scrollbar-thumb-border);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(147, 51, 234, 0.85);
|
||||
}
|
||||
}
|
||||
|
||||
.v-theme--PurpleThemeDark .chat-scrollbar {
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(192, 132, 252, 0.08);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(192, 132, 252, 0.75);
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(192, 132, 252, 0.85);
|
||||
background: var(--astrbot-scrollbar-thumb-hover);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,11 +90,7 @@
|
||||
/* Firefox 兼容性 */
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(147, 51, 234, 0.75) rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.v-theme--PurpleThemeDark * {
|
||||
scrollbar-color: rgba(192, 132, 252, 0.75) rgba(255, 255, 255, 0.05);
|
||||
scrollbar-color: var(--astrbot-scrollbar-thumb) var(--astrbot-scrollbar-track);
|
||||
}
|
||||
|
||||
/* 平滑滚动 */
|
||||
|
||||
@@ -28,27 +28,27 @@
|
||||
.v-list-group__items .v-list-item,
|
||||
.v-list-item {
|
||||
&:hover {
|
||||
color: #b794f6 !important;
|
||||
color: rgb(var(--v-theme-primary)) !important;
|
||||
|
||||
.v-list-item-title {
|
||||
color: #b794f6 !important;
|
||||
color: rgb(var(--v-theme-primary)) !important;
|
||||
}
|
||||
|
||||
.v-icon {
|
||||
color: #b794f6 !important;
|
||||
color: rgb(var(--v-theme-primary)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 选中状态的样式
|
||||
&.v-list-item--active {
|
||||
color: #b794f6 !important;
|
||||
color: rgb(var(--v-theme-primary)) !important;
|
||||
|
||||
.v-list-item-title {
|
||||
color: #b794f6 !important;
|
||||
color: rgb(var(--v-theme-primary)) !important;
|
||||
}
|
||||
|
||||
.v-icon {
|
||||
color: #b794f6 !important;
|
||||
color: rgb(var(--v-theme-primary)) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,9 +56,6 @@
|
||||
.v-list-item--density-default.v-list-item--one-line {
|
||||
min-height: 40px;
|
||||
}
|
||||
.leftPadding {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
.v-navigation-drawer--rail {
|
||||
.scrollnavbar .v-list .v-list-group__items,
|
||||
|
||||
@@ -4,26 +4,26 @@ const PurpleThemeDark: ThemeTypes = {
|
||||
name: 'PurpleThemeDark',
|
||||
dark: true,
|
||||
variables: {
|
||||
'border-color': '#1677ff',
|
||||
'border-color': '#3c96ca',
|
||||
'carousel-control-size': 10
|
||||
},
|
||||
colors: {
|
||||
primary: '#1677ff',
|
||||
secondary: '#722ed1',
|
||||
primary: '#3c96ca',
|
||||
secondary: '#4ea4d8',
|
||||
info: '#03c9d7',
|
||||
success: '#52c41a',
|
||||
accent: '#FFAB91',
|
||||
warning: '#faad14',
|
||||
error: '#ff4d4f',
|
||||
lightprimary: '#eef2f6',
|
||||
lightsecondary: '#ede7f6',
|
||||
lightprimary: '#e8f3fa',
|
||||
lightsecondary: '#e8f3fa',
|
||||
lightsuccess: '#b9f6ca',
|
||||
lighterror: '#f9d8d8',
|
||||
lightwarning: '#fff8e1',
|
||||
primaryText: '#ffffff',
|
||||
secondaryText: '#ffffffcc',
|
||||
darkprimary: '#1565c0',
|
||||
darksecondary: '#4527a0',
|
||||
darkprimary: '#2f86bd',
|
||||
darksecondary: '#2f86bd',
|
||||
borderLight: '#d0d0d0',
|
||||
border: '#333333ee',
|
||||
inputBorder: '#787878',
|
||||
@@ -34,8 +34,8 @@ const PurpleThemeDark: ThemeTypes = {
|
||||
twitter: '#1da1f2',
|
||||
linkedin: '#0e76a8',
|
||||
gray100: '#cccccccc',
|
||||
primary200: '#90caf9',
|
||||
secondary200: '#b39ddb',
|
||||
primary200: '#84c9ea',
|
||||
secondary200: '#8cc4e1',
|
||||
background: '#1d1d1d',
|
||||
overlay: '#111111aa',
|
||||
codeBg: '#282833',
|
||||
|
||||
@@ -9,21 +9,21 @@ const PurpleTheme: ThemeTypes = {
|
||||
},
|
||||
colors: {
|
||||
primary: '#3c96ca',
|
||||
secondary: '#2288b7',
|
||||
secondary: '#2f86bd',
|
||||
info: '#03c9d7',
|
||||
success: '#00c853',
|
||||
accent: '#FFAB91',
|
||||
warning: '#ffc107',
|
||||
error: '#f44336',
|
||||
lightprimary: '#eef2f6',
|
||||
lightsecondary: '#ede7f6',
|
||||
lightsecondary: '#e8f3fa',
|
||||
lightsuccess: '#b9f6ca',
|
||||
lighterror: '#f9d8d8',
|
||||
lightwarning: '#fff8e1',
|
||||
primaryText: '#1b1c1d',
|
||||
secondaryText: '#000000aa',
|
||||
darkprimary: '#1565c0',
|
||||
darksecondary: '#4527a0',
|
||||
darksecondary: '#236b99',
|
||||
borderLight: '#d0d0d0',
|
||||
border: '#d0d0d0',
|
||||
inputBorder: '#787878',
|
||||
@@ -35,7 +35,7 @@ const PurpleTheme: ThemeTypes = {
|
||||
linkedin: '#0e76a8',
|
||||
gray100: '#fafafacc',
|
||||
primary200: '#90caf9',
|
||||
secondary200: '#b39ddb',
|
||||
secondary200: '#8cc4e1',
|
||||
background: '#ffffff',
|
||||
overlay: '#ffffffaa',
|
||||
codeBg: '#ececec',
|
||||
|
||||
@@ -45,9 +45,9 @@ onMounted(() => {
|
||||
<div class="d-flex align-center gap-1">
|
||||
<LanguageSwitcher />
|
||||
<v-divider vertical class="mx-1"
|
||||
style="height: 24px !important; opacity: 0.9 !important; align-self: center !important; border-color: rgba(180, 148, 246, 0.8) !important;"></v-divider>
|
||||
style="height: 24px !important; opacity: 0.9 !important; align-self: center !important; border-color: rgba(var(--v-theme-primary), 0.45) !important;"></v-divider>
|
||||
<v-btn @click="toggleTheme" class="theme-toggle-btn" icon variant="text" size="small">
|
||||
<v-icon size="18" :color="useCustomizerStore().uiTheme === 'PurpleTheme' ? '#5e35b1' : '#d7c5fa'">
|
||||
<v-icon size="18" :color="'rgb(var(--v-theme-primary))'">
|
||||
mdi-white-balance-sunny
|
||||
</v-icon>
|
||||
<v-tooltip activator="parent" location="top">
|
||||
|
||||
Reference in New Issue
Block a user