refactor(stage-web): better structure of backgrounds, fix color issue

This commit is contained in:
Neko Ayaka
2025-12-24 21:56:07 +08:00
parent 74136b9ccf
commit b834e2348c
13 changed files with 60 additions and 44 deletions
@@ -1,15 +0,0 @@
<script setup lang="ts">
import AnimatedWave from '../Widgets/AnimatedWave.vue'
import Cross from './Cross.vue'
</script>
<template>
<Cross>
<AnimatedWave
fill-color="linear-gradient(120deg, hsl(var(--chromatic-hue) 72% 75%), hsl(var(--chromatic-hue) 70% 62%))"
class="h-full w-full"
>
<div class="relative h-full w-full from-black/10 via-black/0 to-black/0 bg-gradient-to-b" />
</AnimatedWave>
</Cross>
</template>
@@ -3,29 +3,18 @@ import type { BackgroundItem } from '../../stores/background'
import ThemeOverlay from '@proj-airi/stage-ui/components/ThemeOverlay.vue'
import { useTheme } from '@proj-airi/ui'
import { computed, ref } from 'vue'
import AnimatedWave from '../Widgets/AnimatedWave.vue'
import Cross from './Cross.vue'
import { ref } from 'vue'
import { BackgroundKind } from '../../stores/background'
import { DefaultBackground } from '../Backgrounds/default'
defineProps<{
background: BackgroundItem
topColor?: string
}>()
const { isDark: dark } = useTheme()
const containerRef = ref<HTMLElement | null>(null)
const waveFillColor = computed(() => {
const hue = 'var(--chromatic-hue)'
return dark.value
? `hsl(${hue} 60% 32%)`
: `hsl(${hue} 75% 78%)`
})
defineExpose({
surfaceEl: containerRef,
})
@@ -39,12 +28,7 @@ defineExpose({
:class="[(background.blur && background.kind !== BackgroundKind.Wave) ? 'blur-md scale-110' : '']"
>
<template v-if="background.kind === BackgroundKind.Wave">
<Cross class="h-full w-full">
<AnimatedWave
class="h-full w-full"
:fill-color="waveFillColor"
/>
</Cross>
<DefaultBackground class="h-full w-full" />
</template>
<template v-else-if="background.kind === BackgroundKind.Image">
<img
@@ -0,0 +1,4 @@
export { default as DefaultBackground } from './index.vue'
export { default as PartAnimatedWave } from './part-animated-wave.vue'
export { default as PatternCross } from './pattern-cross.vue'
export { default as DefaultBackgroundPreview } from './preview.vue'
@@ -0,0 +1,20 @@
<script setup lang="ts">
import { useTheme } from '@proj-airi/ui'
import { PartAnimatedWave, PatternCross } from '.'
const { isDark } = useTheme()
</script>
<template>
<PatternCross>
<PartAnimatedWave
:fill-color="isDark
? 'oklch(35% calc(var(--chromatic-chroma) * 0.6) var(--chromatic-hue))'
: 'color-mix(in srgb, oklch(95% calc(var(--chromatic-chroma-50) * 0.5) var(--chromatic-hue)) 80%, oklch(100% 0 360))'"
class="h-full w-full"
>
<slot />
</PartAnimatedWave>
</PatternCross>
</template>
@@ -0,0 +1,20 @@
<script setup lang="ts">
import { useTheme } from '@proj-airi/ui'
import { PartAnimatedWave, PatternCross } from '.'
const { isDark } = useTheme()
</script>
<template>
<PatternCross>
<PartAnimatedWave
:fill-color="isDark
? 'oklch(35% calc(var(--chromatic-chroma) * 0.6) var(--chromatic-hue))'
: 'color-mix(in srgb, oklch(95% calc(var(--chromatic-chroma-50) * 0.5) var(--chromatic-hue)) 80%, oklch(100% 0 360))'"
class="h-full w-full"
>
<div class="relative h-full w-full from-black/10 via-black/0 to-black/0 bg-gradient-to-b" />
</PartAnimatedWave>
</PatternCross>
</template>
@@ -0,0 +1,2 @@
export { default as BackgroundDialogPicker } from './DialogPicker.vue'
export { default as BackgroundProvider } from './Provider.vue'
@@ -15,12 +15,13 @@ import { onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
import AppBackgroundPickerDialog from '../Backgrounds/AppBackgroundPickerDialog.vue'
import IndicatorMicVolume from '../Widgets/IndicatorMicVolume.vue'
import ActionAbout from './InteractiveArea/Actions/About.vue'
import ActionViewControls from './InteractiveArea/Actions/ViewControls.vue'
import ViewControlInputs from './ViewControls/Inputs.vue'
import { BackgroundDialogPicker } from '../Backgrounds'
const { isDark, toggleDark } = useTheme()
const hearingDialogOpen = ref(false)
const { messages, sending, streamingMessage } = storeToRefs(useChatStore())
@@ -132,7 +133,7 @@ onMounted(() => {
<template>
<div fixed bottom-0 w-full flex flex-col>
<AppBackgroundPickerDialog v-model="backgroundDialogOpen" />
<BackgroundDialogPicker v-model="backgroundDialogOpen" />
<KeepAlive>
<Transition name="fade">
<ChatHistory
@@ -3,7 +3,7 @@ import { useChatStore } from '@proj-airi/stage-ui/stores/chat'
import { useTheme } from '@proj-airi/ui'
import { ref } from 'vue'
import AppBackgroundPickerDialog from '../Backgrounds/AppBackgroundPickerDialog.vue'
import { BackgroundDialogPicker } from '../Backgrounds'
const { cleanupMessages } = useChatStore()
const { isDark, toggleDark } = useTheme()
@@ -12,7 +12,7 @@ const backgroundDialogOpen = ref(false)
</script>
<template>
<AppBackgroundPickerDialog v-model="backgroundDialogOpen" />
<BackgroundDialogPicker v-model="backgroundDialogOpen" />
<div absolute bottom--8 right-0 flex gap-2>
<button
class="max-h-[10lh] min-h-[1lh]"
+4 -4
View File
@@ -16,12 +16,12 @@ import { breakpointsTailwind, useBreakpoints, useMouse } from '@vueuse/core'
import { storeToRefs } from 'pinia'
import { onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue'
import CustomizedBackground from '../components/Backgrounds/CustomizedBackground.vue'
import Header from '../components/Layouts/Header.vue'
import InteractiveArea from '../components/Layouts/InteractiveArea.vue'
import MobileHeader from '../components/Layouts/MobileHeader.vue'
import MobileInteractiveArea from '../components/Layouts/MobileInteractiveArea.vue'
import { BackgroundProvider } from '../components/Backgrounds'
import { useBackgroundThemeColor } from '../composables/theme-color'
import { useBackgroundStore } from '../stores/background'
@@ -38,7 +38,7 @@ const isMobile = breakpoints.smaller('md')
const backgroundStore = useBackgroundStore()
const { selectedOption, sampledColor } = storeToRefs(backgroundStore)
const backgroundSurface = useTemplateRef<InstanceType<typeof CustomizedBackground>>('backgroundSurface')
const backgroundSurface = useTemplateRef<InstanceType<typeof BackgroundProvider>>('backgroundSurface')
const { syncBackgroundTheme } = useBackgroundThemeColor({ backgroundSurface, selectedOption, sampledColor })
onMounted(() => syncBackgroundTheme())
@@ -130,7 +130,7 @@ watch([stream, () => vadLoaded.value], async ([s, loaded]) => {
</script>
<template>
<CustomizedBackground
<BackgroundProvider
ref="backgroundSurface"
class="widgets top-widgets"
:background="selectedOption"
@@ -159,7 +159,7 @@ watch([stream, () => vadLoaded.value], async ([s, loaded]) => {
<MobileInteractiveArea v-if="isMobile" @settings-open="handleSettingsOpen" />
</div>
</div>
</CustomizedBackground>
</BackgroundProvider>
</template>
<route lang="yaml">
+2 -2
View File
@@ -7,7 +7,7 @@ import { useLocalStorage, useObjectUrl } from '@vueuse/core'
import { defineStore } from 'pinia'
import { computed, markRaw, onScopeDispose, ref, shallowRef } from 'vue'
import ChromaticWavePreview from '../components/Backgrounds/ChromaticWavePreview.vue'
import { DefaultBackgroundPreview } from '../components/Backgrounds/default'
export enum BackgroundKind {
Wave = 'wave',
@@ -32,7 +32,7 @@ export const useBackgroundStore = defineStore('background', () => {
label: 'Colorful Wave',
description: 'Animated wave on cross grid',
kind: BackgroundKind.Wave,
component: markRaw(ChromaticWavePreview),
component: markRaw(DefaultBackgroundPreview),
},
]