style(stage-ui): adjust how the banner works

This commit is contained in:
Neko Ayaka
2025-04-15 10:16:39 +08:00
parent 8e4d46e598
commit 1b1685205c
32 changed files with 168 additions and 469 deletions
@@ -1,18 +1,16 @@
<script setup lang="ts">
import type { ccv3 } from '@proj-airi/ccc'
import { InputFile } from '@proj-airi/stage-ui/components'
import { InputFile, PageHeader } from '@proj-airi/stage-ui/components'
import { useAiriCardStore } from '@proj-airi/stage-ui/stores'
import { storeToRefs } from 'pinia'
import { computed, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import CardDetailDialog from './components/CardDetailDialog.vue'
import CardListItem from './components/CardListItem.vue'
import DeleteCardDialog from './components/DeleteCardDialog.vue'
const router = useRouter()
const { t } = useI18n()
const cardStore = useAiriCardStore()
const { addCard, removeCard } = cardStore
@@ -148,26 +146,10 @@ function getModuleShortName(id: string, module: 'consciousness' | 'voice') {
</script>
<template>
<div
v-motion
flex="~ row" items-center gap-2
:initial="{ opacity: 0, x: 10 }"
:enter="{ opacity: 1, x: 0 }"
:leave="{ opacity: 0, x: -10 }"
:duration="250"
>
<button @click="router.back()">
<div i-solar:alt-arrow-left-line-duotone text-2xl />
</button>
<h1 relative>
<div absolute left-0 top-0 translate-y="[-80%]">
<span text="neutral-300 dark:neutral-500" text-nowrap>{{ t('settings.title') }}</span>
</div>
<div text-nowrap text-3xl font-semibold>
{{ t('settings.pages.card.title') }}
</div>
</h1>
</div>
<PageHeader
:title="$t('settings.pages.card.title')"
:subtitle="$t('settings.title')"
/>
<div rounded-xl p-4 flex="~ col gap-4">
<!-- Toolbar with search and filters -->
@@ -1,16 +1,14 @@
<script setup lang="ts">
import { Section } from '@proj-airi/stage-ui/components'
import { PageHeader, Section } from '@proj-airi/stage-ui/components'
import { useSettings } from '@proj-airi/stage-ui/stores'
import { useDark } from '@vueuse/core'
import { ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import CheckBar from '../../../components/Settings/CheckBar.vue'
import ColorPalette from '../../../components/Settings/ColorPalette.vue'
import COLOR_PRESETS from './color-presets.json'
const router = useRouter()
const settings = useSettings()
const dark = useDark()
const { t } = useI18n()
@@ -24,26 +22,10 @@ watch(() => [settings.usePageSpecificTransitions, settings.disableTransitions],
</script>
<template>
<div
v-motion
flex="~ row" items-center gap-2
:initial="{ opacity: 0, x: 10 }"
:enter="{ opacity: 1, x: 0 }"
:leave="{ opacity: 0, x: -10 }"
:duration="250"
>
<button @click="router.back()">
<div i-solar:alt-arrow-left-line-duotone text-2xl />
</button>
<h1 relative>
<div absolute left-0 top-0 translate-y="[-80%]">
<span text="neutral-300 dark:neutral-500" text-nowrap>{{ $t('settings.title') }}</span>
</div>
<div text-nowrap text-3xl font-semibold>
{{ $t('settings.pages.themes.title') }}
</div>
</h1>
</div>
<PageHeader
:title="$t('settings.pages.themes.title')"
:subtitle="$t('settings.title')"
/>
<Section
v-motion
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { IconItem } from '@proj-airi/stage-ui/components'
import { IconItem, PageHeader } from '@proj-airi/stage-ui/components'
import { useSettings } from '@proj-airi/stage-ui/stores'
import { computed, nextTick, ref } from 'vue'
import { useI18n } from 'vue-i18n'
@@ -106,21 +106,9 @@ const settings = computed(() => [
</script>
<template>
<div
v-motion
flex items-center gap-2
:initial="{ opacity: 0, x: 10 }"
:enter="{ opacity: 1, x: 0 }"
:duration="100"
>
<button @click="router.back()">
<div i-solar:alt-arrow-left-line-duotone text-2xl />
</button>
<h1 text-3xl>
{{ $t('settings.title') }}
</h1>
</div>
<PageHeader :title="$t('settings.title')" />
<div flex="~ col gap-4">
<div />
<div flex="~ col gap-4">
<IconItem
v-for="(setting, index) in settings"
@@ -2,11 +2,8 @@
// import { useServerStore } from '@proj-airi/stage-ui/stores'
// import { storeToRefs } from 'pinia'
// import { onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
const router = useRouter()
const { t } = useI18n()
import { PageHeader } from '@proj-airi/stage-ui/components'
// const { server } = storeToRefs(useServerStore())
@@ -16,36 +13,20 @@ const { t } = useI18n()
</script>
<template>
<PageHeader
:title="$t('settings.pages.memory.title')"
:subtitle="$t('settings.title')"
/>
<div
v-motion
flex="~ row" items-center gap-2
:initial="{ opacity: 0, x: 10 }"
:enter="{ opacity: 1, x: 0 }"
:leave="{ opacity: 0, x: -10 }"
:duration="250"
text="neutral-200/50 dark:neutral-600/20" pointer-events-none
fixed top="[calc(100dvh-20rem)]" bottom-0 right--15 z--1
:initial="{ scale: 0.9, opacity: 0, y: 40 }"
:enter="{ scale: 1, opacity: 1, y: 0 }"
:duration="500"
size-60
flex items-center justify-center
>
<button @click="router.back()">
<div i-solar:alt-arrow-left-line-duotone text-2xl />
</button>
<h1 relative>
<div absolute left-0 top-0 translate-y="[-80%]">
<span text="neutral-300 dark:neutral-500" text-nowrap>{{ t('settings.title') }}</span>
</div>
<div text-nowrap text-3xl font-semibold>
{{ t('settings.pages.memory.title') }}
</div>
</h1>
<div
v-motion
text="neutral-200/50 dark:neutral-600/20" pointer-events-none
fixed top="[calc(100dvh-20rem)]" bottom-0 right--15 z--1
:initial="{ scale: 0.9, opacity: 0, y: 40 }"
:enter="{ scale: 1, opacity: 1, y: 0 }"
:duration="500"
size-60
flex items-center justify-center
>
<div text="60" i-solar:leaf-bold-duotone />
</div>
<div text="60" i-solar:leaf-bold-duotone />
</div>
</template>
@@ -1,17 +1,12 @@
<script setup lang="ts">
import Live2DCanvas from '@proj-airi/stage-ui/components/Live2D/Canvas.vue'
import Live2DModel from '@proj-airi/stage-ui/components/Live2D/Model.vue'
import { Live2DCanvas, Live2DModel, PageHeader } from '@proj-airi/stage-ui/components'
import { useElementBounding } from '@vueuse/core'
import { Vibrant } from 'node-vibrant/browser'
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import Live2DSettings from '../../../components/Widgets/Live2DSettings.vue'
import { useIconAnimation } from '../../../composables/useIconAnimation'
const { t } = useI18n()
const router = useRouter()
const live2dContainerRef = ref<HTMLDivElement>()
const live2dCanvasRef = ref<InstanceType<typeof Live2DCanvas>>()
const { width, height } = useElementBounding(live2dContainerRef)
@@ -48,24 +43,10 @@ const {
</script>
<template>
<div
v-motion
flex="~ row" items-center gap-2
:initial="{ opacity: 0, x: 10 }"
:enter="{ opacity: 1, x: 0 }"
:leave="{ opacity: 0, x: -10 }"
:duration="250"
>
<button i-solar:alt-arrow-left-line-duotone text-2xl @click="router.back()" />
<h1 relative text-nowrap>
<div absolute left-0 top-0 translate-y="[-80%]" text="neutral-300 dark:neutral-500">
{{ t('settings.title') }}
</div>
<div text-3xl font-semibold>
{{ t('settings.pages.models.title') }}
</div>
</h1>
</div>
<PageHeader
:title="$t('settings.pages.models.title')"
:subtitle="$t('settings.title')"
/>
<div flex>
<div ref="live2dContainerRef" w="50%" h="80vh">
<Live2DCanvas v-slot="{ app }" ref="live2dCanvasRef" :width="width" :height="height">
@@ -1,10 +1,10 @@
<script setup lang="ts">
import { RadioCardDetailManySelect, RadioCardSimple } from '@proj-airi/stage-ui/components'
import { PageHeader, RadioCardDetailManySelect, RadioCardSimple } from '@proj-airi/stage-ui/components'
import { useConsciousnessStore, useProvidersStore } from '@proj-airi/stage-ui/stores'
import { storeToRefs } from 'pinia'
import { onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { RouterLink, useRouter } from 'vue-router'
import { RouterLink } from 'vue-router'
const providersStore = useProvidersStore()
const consciousnessStore = useConsciousnessStore()
@@ -21,7 +21,6 @@ const {
} = storeToRefs(consciousnessStore)
const { t } = useI18n()
const router = useRouter()
onMounted(async () => {
await consciousnessStore.loadModelsForProvider(activeProvider.value)
@@ -33,26 +32,10 @@ function updateCustomModelName(value: string) {
</script>
<template>
<div
v-motion
flex="~ row" items-center gap-2
:initial="{ opacity: 0, x: 10 }"
:enter="{ opacity: 1, x: 0 }"
:leave="{ opacity: 0, x: -10 }"
:duration="250"
>
<button @click="router.back()">
<div i-solar:alt-arrow-left-line-duotone text-xl />
</button>
<h1 relative>
<div absolute left-0 top-0 translate-y="[-80%]">
<span text="neutral-300 dark:neutral-500" text-nowrap>{{ t('settings.pages.modules.title') }}</span>
</div>
<div text-nowrap text-3xl font-semibold>
{{ t('settings.pages.modules.consciousness.title') }}
</div>
</h1>
</div>
<PageHeader
:title="$t('settings.pages.modules.consciousness.title')"
:subtitle="$t('settings.pages.modules.title')"
/>
<div bg="neutral-50 dark:[rgba(0,0,0,0.3)]" rounded-xl p-4 flex="~ col gap-4">
<div>
<div flex="~ col gap-4">
@@ -1,13 +1,11 @@
<script setup lang="ts">
import { IconStatusItem } from '@proj-airi/stage-ui/components'
import { IconStatusItem, PageHeader } from '@proj-airi/stage-ui/components'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import IconAnimation from '../../../components/IconAnimation.vue'
import { useIconAnimation } from '../../../composables/useIconAnimation'
const router = useRouter()
const { t } = useI18n()
interface Module {
@@ -113,26 +111,10 @@ const {
</script>
<template>
<div
v-motion
flex="~ row" items-center gap-2
:initial="{ opacity: 0, x: 10 }"
:enter="{ opacity: 1, x: 0 }"
:leave="{ opacity: 0, x: -10 }"
:duration="250"
>
<button @click="router.back()">
<div i-solar:alt-arrow-left-line-duotone text-2xl />
</button>
<h1 relative>
<div absolute left-0 top-0 translate-y="[-80%]">
<span text="neutral-300 dark:neutral-500" text-nowrap>{{ t('settings.title') }}</span>
</div>
<div text-nowrap text-3xl font-semibold>
{{ t('settings.pages.modules.title') }}
</div>
</h1>
</div>
<PageHeader
:title="$t('settings.pages.modules.title')"
:subtitle="$t('settings.title')"
/>
<div grid="~ cols-1 sm:cols-2 gap-4">
<IconStatusItem
v-for="(module, index) of modulesList"
@@ -1,33 +1,14 @@
<script setup lang="ts">
// import { useServerStore } from '@proj-airi/stage-ui/stores'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
const router = useRouter()
const { t } = useI18n()
import { PageHeader } from '@proj-airi/stage-ui/components'
// const serverStore = useServerStore()
</script>
<template>
<div
v-motion
flex="~ row" items-center gap-2
:initial="{ opacity: 0, x: 10 }"
:enter="{ opacity: 1, x: 0 }"
:leave="{ opacity: 0, x: -10 }"
:duration="250"
>
<button @click="router.back()">
<div i-solar:alt-arrow-left-line-duotone text-2xl />
</button>
<h1 relative>
<div absolute left-0 top-0 translate-y="[-80%]">
<span text="neutral-300 dark:neutral-500" text-nowrap>{{ t('settings.title') }}</span>
</div>
<div text-nowrap text-3xl font-semibold>
{{ t('settings.pages.modules.memory-long-term.title') }}
</div>
</h1>
</div>
<PageHeader
:title="$t('settings.pages.modules.memory-long-term.title')"
:subtitle="$t('settings.title')"
/>
</template>
@@ -1,33 +1,14 @@
<script setup lang="ts">
// import { useServerStore } from '@proj-airi/stage-ui/stores'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
const router = useRouter()
const { t } = useI18n()
import { PageHeader } from '@proj-airi/stage-ui/components'
// const serverStore = useServerStore()
</script>
<template>
<div
v-motion
flex="~ row" items-center gap-2
:initial="{ opacity: 0, x: 10 }"
:enter="{ opacity: 1, x: 0 }"
:leave="{ opacity: 0, x: -10 }"
:duration="250"
>
<button @click="router.back()">
<div i-solar:alt-arrow-left-line-duotone text-2xl />
</button>
<h1 relative>
<div absolute left-0 top-0 translate-y="[-80%]">
<span text="neutral-300 dark:neutral-500" text-nowrap>{{ t('settings.title') }}</span>
</div>
<div text-nowrap text-3xl font-semibold>
{{ t('settings.pages.modules.memory-short-term.title') }}
</div>
</h1>
</div>
<PageHeader
:title="$t('settings.pages.modules.memory-short-term.title')"
:subtitle="$t('settings.title')"
/>
</template>
@@ -5,6 +5,7 @@ import {
FieldCheckbox,
FieldInput,
FieldRange,
PageHeader,
RadioCardDetailManySelect,
RadioCardSimple,
Skeleton,
@@ -16,7 +17,7 @@ import { generateSpeech } from '@xsai/generate-speech'
import { storeToRefs } from 'pinia'
import { onMounted, onUnmounted, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { RouterLink, useRouter } from 'vue-router'
import { RouterLink } from 'vue-router'
const { t } = useI18n()
const providersStore = useProvidersStore()
@@ -42,8 +43,6 @@ const {
availableVoices,
} = storeToRefs(speechStore)
const router = useRouter()
const voiceSearchQuery = ref('')
const useSSML = ref(false)
const testText = ref('Hello, my name is AI Assistant')
@@ -166,23 +165,10 @@ function updateCustomModelName(value: string) {
</script>
<template>
<div
v-motion flex="~ row" items-center gap-2 :initial="{ opacity: 0, x: 10 }" :enter="{ opacity: 1, x: 0 }"
:leave="{ opacity: 0, x: -10 }" :duration="250"
>
<button @click="router.back()">
<div i-solar:alt-arrow-left-line-duotone text-xl />
</button>
<h1 relative>
<div absolute left-0 top-0 translate-y="[-80%]">
<span text="neutral-300 dark:neutral-500" text-nowrap>{{ t('settings.pages.modules.title') }}</span>
</div>
<div text-nowrap text-3xl font-semibold>
{{ t('settings.pages.modules.speech.title') }}
</div>
</h1>
</div>
<PageHeader
:title="t('settings.pages.modules.speech.title')"
:subtitle="t('settings.pages.modules.title')"
/>
<div flex="~ col md:row gap-6">
<div bg="neutral-100 dark:[rgba(0,0,0,0.3)]" rounded-xl p-4 flex="~ col gap-4" class="w-full md:w-[40%]">
<div>
@@ -1,14 +1,10 @@
<script setup lang="ts">
import { IconStatusItem } from '@proj-airi/stage-ui/components'
import { IconStatusItem, PageHeader } from '@proj-airi/stage-ui/components'
import { useProvidersStore } from '@proj-airi/stage-ui/stores'
import { storeToRefs } from 'pinia'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { useIconAnimation } from '../../../composables/useIconAnimation'
const { t } = useI18n()
const router = useRouter()
const providersStore = useProvidersStore()
const { allProvidersMetadata } = storeToRefs(providersStore)
@@ -20,26 +16,10 @@ const {
</script>
<template>
<div
v-motion
flex="~ row" items-center gap-2
:initial="{ opacity: 0, x: 10 }"
:enter="{ opacity: 1, x: 0 }"
:leave="{ opacity: 0, x: -10 }"
:duration="250"
>
<button @click="router.back()">
<div i-solar:alt-arrow-left-line-duotone text-2xl />
</button>
<h1 relative>
<div absolute left-0 top-0 translate-y="[-80%]">
<span text="neutral-300 dark:neutral-500" text-nowrap>{{ t('settings.title') }}</span>
</div>
<div text-nowrap text-3xl font-semibold>
{{ t('settings.pages.providers.title') }}
</div>
</h1>
</div>
<PageHeader
:title="$t('settings.pages.providers.title')"
:subtitle="$t('settings.title')"
/>
<div grid="~ cols-2 gap-4">
<IconStatusItem
v-for="(provider, index) of allProvidersMetadata"
@@ -1,12 +1,8 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { PageHeader } from '@proj-airi/stage-ui/components'
import { useIconAnimation } from '../../../composables/useIconAnimation'
const { t } = useI18n()
const router = useRouter()
const {
iconAnimationStarted,
showIconAnimation,
@@ -15,26 +11,10 @@ const {
</script>
<template>
<div
v-motion
flex="~ row" items-center gap-2
:initial="{ opacity: 0, x: 10 }"
:enter="{ opacity: 1, x: 0 }"
:leave="{ opacity: 0, x: -10 }"
:duration="250"
>
<button @click="router.back()">
<div i-solar:alt-arrow-left-line-duotone text-2xl />
</button>
<h1 relative>
<div absolute left-0 top-0 translate-y="[-80%]">
<span text="neutral-300 dark:neutral-500" text-nowrap>{{ t('settings.title') }}</span>
</div>
<div text-nowrap text-3xl font-semibold>
{{ t('settings.pages.scene.title') }}
</div>
</h1>
</div>
<PageHeader
:title="$t('settings.pages.scene.title')"
:subtitle="$t('settings.title')"
/>
<div>
Scene
</div>
+10 -5
View File
@@ -1,6 +1,13 @@
@import './themes.css';
@import './transitions.css';
:root {
--bg-color-light: rgb(255 255 255);
--bg-color-dark: rgb(18 18 18);
--progress-bar-color: rgb(244 114 182);
--bg-color: var(--bg-color-light);
}
html,
body,
#app {
@@ -11,23 +18,21 @@ body,
}
html {
background: var(--bg-color);
transition: all 0.3s ease-in-out;
}
html.dark {
--bg-color: var(--bg-color-dark);
color-scheme: dark;
}
.dark #app {
background: transparent !important;
}
#nprogress {
pointer-events: none;
}
#nprogress .bar {
background: rgb(13, 148, 136);
background: var(--progress-bar-color);
opacity: 0.75;
position: fixed;
z-index: 1031;
+2 -2
View File
@@ -16,13 +16,13 @@ import HeaderLink from '../components/Layouts/HeaderLink.vue'
<!-- Header -->
<div
class="px-0 py-1 md:px-3 md:py-3"
mb-4 w-full gap-2
w-full gap-2
bg="$bg-color"
>
<HeaderLink />
</div>
<!-- Content -->
<div class="px-3 py-2 md:px-5 md:py-5" flex="~ col gap-4" mx-auto max-w-screen-xl>
<div class="px-3 py-2 md:px-5 md:py-5" flex="~ col" mx-auto max-w-screen-xl>
<RouterView />
</div>
</div>
@@ -1,13 +1,12 @@
<script setup lang="ts">
import type { ccv3 } from '@proj-airi/ccc'
import { InputFile } from '@proj-airi/stage-ui/components'
import { InputFile, PageHeader } from '@proj-airi/stage-ui/components'
import { useAiriCardStore } from '@proj-airi/stage-ui/stores'
import { storeToRefs } from 'pinia'
import { computed, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import PageHeader from '../../../components/PageHeader.vue'
import CardDetailDialog from './components/CardDetailDialog.vue'
import CardListItem from './components/CardListItem.vue'
import DeleteCardDialog from './components/DeleteCardDialog.vue'
@@ -1,11 +1,10 @@
<script setup lang="ts">
import { Section } from '@proj-airi/stage-ui/components'
import { PageHeader, Section } from '@proj-airi/stage-ui/components'
import { useSettings } from '@proj-airi/stage-ui/stores'
import { useDark } from '@vueuse/core'
import { ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import PageHeader from '../../../components/PageHeader.vue'
import CheckBar from '../../../components/Settings/CheckBar.vue'
import ColorPalette from '../../../components/Settings/ColorPalette.vue'
import COLOR_PRESETS from './color-presets.json'
+2 -2
View File
@@ -1,12 +1,11 @@
<script setup lang="ts">
import { IconItem } from '@proj-airi/stage-ui/components'
import { IconItem, PageHeader } from '@proj-airi/stage-ui/components'
import { useSettings } from '@proj-airi/stage-ui/stores'
import { computed, nextTick, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import IconAnimation from '../../components/IconAnimation.vue'
import PageHeader from '../../components/PageHeader.vue'
const router = useRouter()
const iconAnimationStarted = ref(false)
@@ -109,6 +108,7 @@ const settings = computed(() => [
<template>
<PageHeader :title="$t('settings.title')" />
<div flex="~ col gap-4">
<div />
<div flex="~ col gap-4">
<IconItem
v-for="(setting, index) in settings"
@@ -3,7 +3,7 @@
// import { storeToRefs } from 'pinia'
// import { onMounted } from 'vue'
import PageHeader from '../../../components/PageHeader.vue'
import { PageHeader } from '@proj-airi/stage-ui/components'
// const { server } = storeToRefs(useServerStore())
@@ -1,6 +1,5 @@
<script setup lang="ts">
import Live2DCanvas from '@proj-airi/stage-ui/components/Live2D/Canvas.vue'
import Live2DModel from '@proj-airi/stage-ui/components/Live2D/Model.vue'
import { Live2DCanvas, Live2DModel, PageHeader } from '@proj-airi/stage-ui/components'
import { useElementBounding } from '@vueuse/core'
import { Vibrant } from 'node-vibrant/browser'
import { ref } from 'vue'
@@ -1,13 +1,11 @@
<script setup lang="ts">
import { RadioCardDetailManySelect, RadioCardSimple } from '@proj-airi/stage-ui/components'
import { PageHeader, RadioCardDetailManySelect, RadioCardSimple } from '@proj-airi/stage-ui/components'
import { useConsciousnessStore, useProvidersStore } from '@proj-airi/stage-ui/stores'
import { storeToRefs } from 'pinia'
import { onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
import PageHeader from '../../../components/PageHeader.vue'
const providersStore = useProvidersStore()
const consciousnessStore = useConsciousnessStore()
const { availableProviders, availableTextGenerationsProvidersMetadata } = storeToRefs(providersStore)
@@ -1,10 +1,9 @@
<script setup lang="ts">
import { IconStatusItem } from '@proj-airi/stage-ui/components'
import { IconStatusItem, PageHeader } from '@proj-airi/stage-ui/components'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import IconAnimation from '../../../components/IconAnimation.vue'
import PageHeader from '../../../components/PageHeader.vue'
import { useIconAnimation } from '../../../composables/useIconAnimation'
const { t } = useI18n()
@@ -1,7 +1,7 @@
<script setup lang="ts">
// import { useServerStore } from '@proj-airi/stage-ui/stores'
import PageHeader from '../../../components/PageHeader.vue'
import { PageHeader } from '@proj-airi/stage-ui/components'
// const serverStore = useServerStore()
</script>
@@ -1,7 +1,7 @@
<script setup lang="ts">
// import { useServerStore } from '@proj-airi/stage-ui/stores'
import PageHeader from '../../../components/PageHeader.vue'
import { PageHeader } from '@proj-airi/stage-ui/components'
// const serverStore = useServerStore()
</script>
@@ -5,6 +5,7 @@ import {
FieldCheckbox,
FieldInput,
FieldRange,
PageHeader,
RadioCardDetailManySelect,
RadioCardSimple,
Skeleton,
@@ -16,7 +17,7 @@ import { generateSpeech } from '@xsai/generate-speech'
import { storeToRefs } from 'pinia'
import { onMounted, onUnmounted, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { RouterLink, useRouter } from 'vue-router'
import { RouterLink } from 'vue-router'
const { t } = useI18n()
const providersStore = useProvidersStore()
@@ -42,8 +43,6 @@ const {
availableVoices,
} = storeToRefs(speechStore)
const router = useRouter()
const voiceSearchQuery = ref('')
const useSSML = ref(false)
const testText = ref('Hello, my name is AI Assistant')
@@ -166,23 +165,10 @@ function updateCustomModelName(value: string) {
</script>
<template>
<div
v-motion flex="~ row" items-center gap-2 :initial="{ opacity: 0, x: 10 }" :enter="{ opacity: 1, x: 0 }"
:leave="{ opacity: 0, x: -10 }" :duration="250"
>
<button @click="router.back()">
<div i-solar:alt-arrow-left-line-duotone text-xl />
</button>
<h1 relative>
<div absolute left-0 top-0 translate-y="[-80%]">
<span text="neutral-300 dark:neutral-500" text-nowrap>{{ t('settings.pages.modules.title') }}</span>
</div>
<div text-nowrap text-3xl font-semibold>
{{ t('settings.pages.modules.speech.title') }}
</div>
</h1>
</div>
<PageHeader
:title="t('settings.pages.modules.speech.title')"
:subtitle="t('settings.pages.modules.title')"
/>
<div flex="~ col md:row gap-6">
<div bg="neutral-100 dark:[rgba(0,0,0,0.3)]" rounded-xl p-4 flex="~ col gap-4" class="w-full md:w-[40%]">
<div>
@@ -1,9 +1,8 @@
<script setup lang="ts">
import { IconStatusItem } from '@proj-airi/stage-ui/components'
import { IconStatusItem, PageHeader } from '@proj-airi/stage-ui/components'
import { useProvidersStore } from '@proj-airi/stage-ui/stores'
import { storeToRefs } from 'pinia'
import PageHeader from '../../../components/PageHeader.vue'
import { useIconAnimation } from '../../../composables/useIconAnimation'
const providersStore = useProvidersStore()
@@ -1,5 +1,6 @@
<script setup lang="ts">
import PageHeader from '../../../components/PageHeader.vue'
import { PageHeader } from '@proj-airi/stage-ui/components'
import { useIconAnimation } from '../../../composables/useIconAnimation'
const {
@@ -0,0 +1,39 @@
<script setup lang="ts">
import { useRouter } from 'vue-router'
defineProps<{
title: string
subtitle?: string
}>()
const router = useRouter()
</script>
<template>
<div
v-motion
:initial="{ opacity: 0, x: 10 }"
:enter="{ opacity: 1, x: 0 }"
:duration="250"
:style="{
top: 'env(safe-area-inset-top, 0px)',
right: 'env(safe-area-inset-right, 0px)',
left: 'env(safe-area-inset-left, 0px)',
}"
sticky inset-x-0 top-0 z-99 w-full pb-6 pt-10
flex="~ row items-center gap-2"
bg="$bg-color"
>
<button @click="router.back()">
<div i-solar:alt-arrow-left-line-duotone text-2xl />
</button>
<h1 relative>
<div v-if="subtitle" absolute left-0 top-0 translate-y="[-80%]">
<span text="neutral-300 dark:neutral-500" text-nowrap>{{ subtitle }}</span>
</div>
<div text-nowrap text-3xl font-semibold>
{{ title }}
</div>
</h1>
</div>
</template>
@@ -0,0 +1 @@
export { default as PageHeader } from './PageHeader.vue'
@@ -63,9 +63,9 @@ defineProps<{
z-index: -1;
width: 25%;
height: 100%;
transition: all 0.4s ease-in-out;
mask-image: linear-gradient(120deg, white 30%, transparent 50%);
opacity: 0;
transition: all 0.4s ease-in-out;
}
.menu-icon-item:hover::before,
@@ -1,33 +1,22 @@
<script setup lang="ts">
import { PageHeader } from '@proj-airi/stage-ui/components'
import { useI18n } from 'vue-i18n'
defineProps<{
providerName: string
providerIcon?: string
providerIconColor?: string
onBack?: () => void
}>()
const { t } = useI18n()
</script>
<template>
<div
v-motion
flex="~ row" items-center gap-2
:initial="{ opacity: 0, x: 10 }"
:enter="{ opacity: 1, x: 0 }"
:leave="{ opacity: 0, x: -10 }"
:duration="250"
>
<button @click="onBack">
<div i-solar:alt-arrow-left-line-duotone text-2xl />
</button>
<h1 relative>
<div absolute left-0 top-0 translate-y="[-80%]">
<span text="neutral-300 dark:neutral-500" text-nowrap>Provider</span>
</div>
<div text-nowrap text-3xl font-semibold>
{{ providerName }}
</div>
</h1>
</div>
<PageHeader
:title="providerName"
:subtitle="t('settings.pages.providers.title')"
/>
<slot />
<div
v-motion
@@ -1,135 +1,32 @@
<script setup lang="ts">
import { computed } from 'vue'
import { PageHeader } from '@proj-airi/stage-ui/components'
import { useI18n } from 'vue-i18n'
const props = defineProps<{
/**
* Provider name to display in the header
*/
providerName?: string
/**
* Provider icon CSS class
*/
defineProps<{
providerName: string
providerIcon?: string
/**
* Provider icon color CSS class
*/
providerIconColor?: string
/**
* Optional handler for back button
*/
onBack?: () => void
/**
* Optional title to display (overrides providerName)
*/
title?: string
/**
* Optional subtitle to display
*/
subtitle?: string
/**
* Optional category label to display above the title
*/
categoryLabel?: string
}>()
// Expose event handlers
const emit = defineEmits<{
back: []
}>()
// Define slots
defineSlots<{
default: () => any
title: () => any
subtitle: () => any
backButton: () => any
headerExtra: () => any
header: () => any
}>()
const { t } = useI18n()
// Compute effective title
const effectiveTitle = computed(() => props.title || props.providerName || '')
// Compute effective category label
const effectiveCategoryLabel = computed(() => props.categoryLabel || t('settings.pages.providers.title'))
// Handle back button click
function handleBackClick() {
if (props.onBack) {
props.onBack()
}
emit('back')
}
</script>
<template>
<div>
<!-- Header section -->
<slot name="header">
<div
v-motion
flex="~ row"
:initial="{ opacity: 0, x: 10 }"
:enter="{ opacity: 1, x: 0 }"
:leave="{ opacity: 0, x: -10 }"
:duration="250"
mb-6 items-center gap-3
>
<!-- Back button -->
<slot name="backButton">
<button @click="handleBackClick">
<div i-solar:alt-arrow-left-line-duotone text-2xl />
</button>
</slot>
<!-- Title area -->
<div>
<slot name="title">
<h1 relative>
<div v-if="effectiveCategoryLabel" absolute left-0 top-0 translate-y="[-80%]">
<span text="neutral-300 dark:neutral-500" text-nowrap>{{ effectiveCategoryLabel }}</span>
</div>
<div text-nowrap text-3xl font-semibold>
{{ effectiveTitle }}
</div>
</h1>
</slot>
<slot name="subtitle">
<div v-if="subtitle" text-sm text="neutral-500 dark:neutral-400">
{{ subtitle }}
</div>
</slot>
</div>
<!-- Extra header content (right side) -->
<slot name="headerExtra" />
</div>
</slot>
<!-- Main content -->
<slot />
<!-- Background icon -->
<div
v-motion
text="neutral-200/50 dark:neutral-600/20" pointer-events-none
fixed top="[calc(100dvh-15rem)]" bottom-0 right--5 z--1
:initial="{ scale: 0.9, opacity: 0, x: 20 }"
:enter="{ scale: 1, opacity: 1, x: 0 }"
:duration="500"
size-60
>
<div text="60" :class="providerIcon || providerIconColor" />
</div>
<PageHeader
:title="providerName"
:subtitle="t('settings.pages.providers.title')"
/>
<slot />
<div
v-motion
text="neutral-200/50 dark:neutral-600/20" pointer-events-none
fixed top="[calc(100dvh-15rem)]" bottom-0 right--5 z--1
:initial="{ scale: 0.9, opacity: 0, x: 20 }"
:enter="{ scale: 1, opacity: 1, x: 0 }"
:duration="500"
size-60
>
<div text="60" :class="providerIcon || providerIconColor" />
</div>
</template>
@@ -4,6 +4,7 @@ export { default as Collapsable } from './Collapsable.vue'
export * from './Form'
export * from './Gadgets'
export * from './Graphics'
export * from './Layouts'
export { default as Live2DCanvas } from './Live2D/Canvas.vue'
export { default as Live2DModel } from './Live2D/Model.vue'
export * from './Menu'