feat(insight): add customizable report card themes

Give color-only theme controls localized positional labels and expose their selected state without adding visible scheme names.
This commit is contained in:
digua committed 2026-08-06 22:19:52 +08:00
1 parent 25071890b2
commit 11eef47a7e
8 files changed
+136 -11

No files matched your search

+38 -7
View File
@@ -1,10 +1,41 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useSettingsStore } from '@/stores/settings'
import { getInsightCardTheme } from '@/utils/insight-card-theme'
const settingsStore = useSettingsStore()
const decorationStyle = computed(() => {
const theme = getInsightCardTheme(settingsStore.insightCardTheme)
return {
'--insight-card-glow-start': theme.startColor,
'--insight-card-glow-end': theme.endColor,
}
})
</script>
<template>
<div class="pointer-events-none absolute inset-0 overflow-hidden">
<div
class="absolute -left-[20%] -top-[20%] h-[70%] w-[70%] rounded-full bg-blue-400/10 blur-[80px] dark:bg-blue-500/20"
/>
<div
class="absolute -right-[20%] top-[10%] h-[70%] w-[70%] rounded-full bg-pink-400/10 blur-[80px] dark:bg-pink-500/20"
/>
<div class="pointer-events-none absolute inset-0 overflow-hidden" :style="decorationStyle">
<div class="insight-card-glow-start absolute -left-[20%] -top-[20%] h-[70%] w-[70%] rounded-full blur-[80px]" />
<div class="insight-card-glow-end absolute -right-[20%] top-[10%] h-[70%] w-[70%] rounded-full blur-[80px]" />
</div>
</template>
<style scoped>
.insight-card-glow-start {
background-color: color-mix(in srgb, var(--insight-card-glow-start) 10%, transparent);
}
.insight-card-glow-end {
background-color: color-mix(in srgb, var(--insight-card-glow-end) 10%, transparent);
}
:global(.dark) .insight-card-glow-start {
background-color: color-mix(in srgb, var(--insight-card-glow-start) 20%, transparent);
}
:global(.dark) .insight-card-glow-end {
background-color: color-mix(in srgb, var(--insight-card-glow-end) 20%, transparent);
}
</style>
@@ -10,6 +10,7 @@ import NetworkSettingsSection from './NetworkSettingsSection.vue'
import UITabs from '@/components/UI/Tabs.vue'
import { usePlatformService } from '@/services'
import { IS_ELECTRON } from '@/utils/platform'
import { INSIGHT_CARD_THEMES, type InsightCardThemeId } from '@/utils/insight-card-theme'
const { t } = useI18n()
@@ -17,7 +18,7 @@ const { t } = useI18n()
const layoutStore = useLayoutStore()
const settingsStore = useSettingsStore()
const { toolsPanelPosition } = storeToRefs(layoutStore)
const { locale, defaultSessionTab } = storeToRefs(settingsStore)
const { locale, defaultSessionTab, insightCardTheme } = storeToRefs(settingsStore)
// Auto Launch
const openAtLogin = ref(false)
@@ -58,6 +59,16 @@ const colorModeOptions = computed(() => [
{ label: t('settings.basic.appearance.dark'), value: 'dark' },
])
function getInsightCardThemePreview(startColor: string, endColor: string) {
return {
background: `linear-gradient(135deg, color-mix(in srgb, ${startColor} 35%, white), color-mix(in srgb, ${endColor} 42%, white))`,
}
}
function selectInsightCardTheme(theme: InsightCardThemeId) {
insightCardTheme.value = theme
}
// Language options
const languageOptions = computed(() =>
availableLocales.map((l) => ({
@@ -131,8 +142,8 @@ const toolsPanelPositionOptions = computed(() => [
<UIcon name="i-heroicons-paint-brush" class="h-4 w-4 text-pink-500" />
{{ t('settings.basic.appearance.title') }}
</h3>
<div class="rounded-lg border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-800/50">
<div class="flex items-center justify-between">
<div class="rounded-lg border border-gray-200 bg-gray-50 dark:border-gray-700 dark:bg-gray-800/50">
<div class="flex items-center justify-between p-4">
<div class="flex-1 pr-4">
<p class="text-sm font-medium text-gray-900 dark:text-white">
{{ t('settings.basic.appearance.themeMode') }}
@@ -142,6 +153,42 @@ const toolsPanelPositionOptions = computed(() => [
<UTabs v-model="colorMode" size="sm" class="gap-0" :items="colorModeOptions"></UTabs>
</div>
</div>
<div class="border-t border-gray-200 dark:border-gray-700"></div>
<div class="flex items-center justify-between p-4">
<div class="flex-1 pr-4">
<p class="text-sm font-medium text-gray-900 dark:text-white">
{{ t('settings.basic.appearance.insightCardTheme') }}
</p>
</div>
<div class="flex shrink-0 items-center gap-1.5">
<button
v-for="(theme, index) in INSIGHT_CARD_THEMES"
:key="theme.id"
type="button"
class="group flex h-9 w-9 items-center justify-center rounded-full outline-none transition-colors hover:bg-gray-200/60 focus-visible:ring-2 focus-visible:ring-primary-500/40 dark:hover:bg-gray-700/70"
:aria-label="t('settings.basic.appearance.insightCardThemeOption', { index: index + 1 })"
:aria-pressed="insightCardTheme === theme.id"
@click="selectInsightCardTheme(theme.id)"
>
<span
class="relative block h-6 w-6 overflow-hidden rounded-full shadow-sm ring-1 transition-all"
:class="
insightCardTheme === theme.id
? 'ring-2 ring-primary-500 ring-offset-2 ring-offset-gray-50 dark:ring-primary-400 dark:ring-offset-gray-800'
: 'ring-black/5 group-hover:ring-black/10 dark:ring-white/10 dark:group-hover:ring-white/20'
"
:style="getInsightCardThemePreview(theme.startColor, theme.endColor)"
>
<span
v-if="insightCardTheme === theme.id"
class="absolute inset-0 flex items-center justify-center text-gray-800/70"
>
<UIcon name="i-heroicons-check-20-solid" class="h-3.5 w-3.5" />
</span>
</span>
</button>
</div>
</div>
</div>
</div>
+2
View File
@@ -34,6 +34,8 @@
"appearance": {
"title": "Appearance",
"themeMode": "Theme Mode",
"insightCardTheme": "Insight card colors",
"insightCardThemeOption": "Color scheme {index}",
"auto": "System",
"light": "Light",
"dark": "Dark"
+2
View File
@@ -34,6 +34,8 @@
"appearance": {
"title": "外観設定",
"themeMode": "テーマモード",
"insightCardTheme": "インサイトカードの配色",
"insightCardThemeOption": "配色 {index}",
"auto": "システムに従う",
"light": "ライトモード",
"dark": "ダークモード"
+2
View File
@@ -34,6 +34,8 @@
"appearance": {
"title": "外观设置",
"themeMode": "主题模式",
"insightCardTheme": "洞察卡片配色",
"insightCardThemeOption": "配色 {index}",
"auto": "跟随系统",
"light": "浅色模式",
"dark": "深色模式"
+2
View File
@@ -34,6 +34,8 @@
"appearance": {
"title": "外觀設定",
"themeMode": "主題模式",
"insightCardTheme": "洞察卡片配色",
"insightCardThemeOption": "配色 {index}",
"auto": "依系統設定",
"light": "淺色模式",
"dark": "深色模式"
+5 -1
View File
@@ -10,6 +10,7 @@ import type { PreprocessConfig } from '@electron/preload/index'
import type { AIPreprocessConfig } from '@openchatlab/shared-types'
import { useAIService, usePlatformService } from '@/services'
import { PLATFORM_CAPABILITIES } from '@/utils/platform-capabilities'
import { DEFAULT_INSIGHT_CARD_THEME, type InsightCardThemeId } from '@/utils/insight-card-theme'
const DESENSITIZE_RULES_SCHEMA_VERSION = 2
@@ -35,6 +36,8 @@ export const useSettingsStore = defineStore(
const defaultSessionTab = ref<'insights' | 'ai-chat'>('insights')
const insightCardTheme = ref<InsightCardThemeId>(DEFAULT_INSIGHT_CARD_THEME)
const debugMode = ref(false)
function setDebugMode(enabled: boolean) {
@@ -112,6 +115,7 @@ export const useSettingsStore = defineStore(
setLocale,
initLocale,
defaultSessionTab,
insightCardTheme,
debugMode,
setDebugMode,
aiPreprocessConfig,
@@ -120,7 +124,7 @@ export const useSettingsStore = defineStore(
},
{
persist: {
pick: ['debugMode'],
pick: ['debugMode', 'insightCardTheme'],
storage: localStorage,
},
backendPersist: {
+35
View File
@@ -0,0 +1,35 @@
export const INSIGHT_CARD_THEME_IDS = [
'default',
'sunset',
'galaxy',
'mist',
'moonlight',
'berry',
'sunshine',
'aurora',
] as const
export type InsightCardThemeId = (typeof INSIGHT_CARD_THEME_IDS)[number]
export interface InsightCardTheme {
id: InsightCardThemeId
startColor: string
endColor: string
}
export const DEFAULT_INSIGHT_CARD_THEME: InsightCardThemeId = 'default'
export const INSIGHT_CARD_THEMES: readonly InsightCardTheme[] = [
{ id: 'default', startColor: '#60a5fa', endColor: '#f472b6' },
{ id: 'sunset', startColor: '#fb923c', endColor: '#fb7185' },
{ id: 'galaxy', startColor: '#818cf8', endColor: '#c084fc' },
{ id: 'mist', startColor: '#22d3ee', endColor: '#60a5fa' },
{ id: 'moonlight', startColor: '#22c55e', endColor: '#14b8a6' },
{ id: 'berry', startColor: '#fb7185', endColor: '#a78bfa' },
{ id: 'sunshine', startColor: '#f59e0b', endColor: '#facc15' },
{ id: 'aurora', startColor: '#34d399', endColor: '#818cf8' },
]
export function getInsightCardTheme(id: InsightCardThemeId): InsightCardTheme {
return INSIGHT_CARD_THEMES.find((theme) => theme.id === id) ?? INSIGHT_CARD_THEMES[0]
}