chore(stage-ui|stage-tamagotchi): icon

This commit is contained in:
Neko Ayaka
2025-04-14 00:11:05 +08:00
parent 5f3fc99db4
commit 939a5ed208
24 changed files with 374 additions and 83 deletions
+3
View File
@@ -330,6 +330,9 @@ settings:
description: x.ai
title: xAI
title: Providers
scene:
description: Configure the environment where the character lives
title: Scene
themes:
description: Customize your stage!
sections:
+3
View File
@@ -317,6 +317,9 @@ settings:
description: X.AI
title: xAI
title: 提供商
scene:
description: 配置角色所在环境
title: 场景
themes:
description: 自定义你的舞台外观!
sections:
@@ -31,6 +31,15 @@ const sortOption = ref('nameAsc')
const inputFiles = ref<File[]>([])
// Card list data structure
interface CardItem {
id: string
name: string
description?: string
deprecated?: boolean
customizable?: boolean
}
watch(inputFiles, async (newFiles) => {
const file = newFiles[0]
if (!file)
@@ -49,15 +58,6 @@ watch(inputFiles, async (newFiles) => {
}
})
// Card list data structure
interface CardItem {
id: string
name: string
description?: string
deprecated?: boolean
customizable?: boolean
}
// Transform cards Map to array for display
const cardsArray = computed<CardItem[]>(() =>
Array.from(cards.value.entries()).map(([id, card]) => ({
@@ -221,16 +221,18 @@ function getModuleShortName(id: string, module: 'consciousness' | 'voice') {
<InputFile v-model="inputFiles" accept="*.json">
<template #default="{ isDragging }">
<template v-if="!isDragging">
<div i-solar:upload-square-line-duotone mb-4 text-5xl text="neutral-400 dark:neutral-500" />
<p font-medium text="center neutral-600 dark:neutral-300">
{{ t('settings.pages.card.upload') }}
</p>
<p text="center neutral-500 dark:neutral-400" mt-2 text-sm>
{{ t('settings.pages.card.upload_desc') }}) }}
</p>
<div flex flex-col items-center>
<div i-solar:upload-square-line-duotone mb-4 text-5xl text="neutral-400 dark:neutral-500" />
<p font-medium text="neutral-600 dark:neutral-300">
{{ t('settings.pages.card.upload') }}
</p>
<p text="neutral-500 dark:neutral-400" mt-2 text-sm>
{{ t('settings.pages.card.upload_desc') }}
</p>
</div>
</template>
<template v-else>
<div class="text-center">
<div flex flex-col items-center>
<div i-solar:upload-minimalistic-bold class="text-primary-500 dark:text-primary-400 mb-2 text-5xl" />
<p font-medium text="primary-600 dark:primary-300">
{{ t('settings.pages.card.drop_here') }}
@@ -304,12 +306,14 @@ function getModuleShortName(id: string, module: 'consciousness' | 'voice') {
<div
v-motion
text="neutral-200/50 dark:neutral-600/20" pointer-events-none
fixed top="[72dvh]" right--15 z--1
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="250"
:duration="500"
size-60
flex items-center justify-center
>
<div text="60" i-lucide:id-card />
<div text="60" i-solar:emoji-funny-square-bold-duotone />
</div>
</template>
@@ -269,8 +269,9 @@ watch(() => [settings.usePageSpecificTransitions, settings.disableTransitions],
:initial="{ scale: 0.9, opacity: 0, rotate: 30 }"
:enter="{ scale: 1, opacity: 1, rotate: 0 }"
:duration="250"
flex items-center justify-center
>
<div text="60" i-lucide:paintbrush />
<div text="60" i-solar:filters-bold-duotone />
</div>
</template>
@@ -5,8 +5,11 @@ import { computed, nextTick, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import IconAnimation from '../../components/IconAnimation.vue'
const router = useRouter()
const iconAnimationStarted = ref(false)
const iconAnimation = ref<InstanceType<typeof IconAnimation>>()
const resolveAnimation = ref<() => void>()
const { t } = useI18n()
@@ -15,6 +18,10 @@ const animationPosition = ref('')
const showAnimationComponent = ref(false)
const settingsStore = useSettings()
function handleAnimationEnded() {
resolveAnimation.value?.()
}
async function handleIconItemClick(event: MouseEvent, setting: typeof settings.value[0]) {
const target = event.currentTarget as HTMLElement
const iconElement = target.querySelector('.menu-icon-item-icon') as HTMLElement
@@ -56,37 +63,43 @@ const settings = computed(() => [
{
title: t('settings.pages.card.title'),
description: t('settings.pages.card.description'),
icon: 'i-lucide:id-card',
icon: 'i-solar:emoji-funny-square-bold-duotone',
to: '/settings/airi-card',
},
{
title: t('settings.pages.modules.title'),
description: t('settings.pages.modules.description'),
icon: 'i-lucide:blocks',
icon: 'i-solar:layers-bold-duotone',
to: '/settings/modules',
},
{
title: t('settings.pages.scene.title'),
description: t('settings.pages.scene.description'),
icon: 'i-solar:armchair-2-bold-duotone',
to: '/settings/scene',
},
{
title: t('settings.pages.models.title'),
description: t('settings.pages.models.description'),
icon: 'i-lucide:person-standing',
icon: 'i-solar:people-nearby-bold-duotone',
to: '/settings/models',
},
{
title: t('settings.pages.memory.title'),
description: t('settings.pages.memory.description'),
icon: 'i-lucide:sprout',
icon: 'i-solar:leaf-bold-duotone',
to: '/settings/memory',
},
{
title: t('settings.pages.providers.title'),
description: t('settings.pages.providers.description'),
icon: 'i-lucide:brain',
icon: 'i-solar:box-minimalistic-bold-duotone',
to: '/settings/providers',
},
{
title: t('settings.pages.themes.title'),
description: t('settings.pages.themes.description'),
icon: 'i-lucide:paintbrush',
icon: 'i-solar:filters-bold-duotone',
to: '/settings/appearance',
},
])
@@ -126,15 +139,29 @@ const settings = computed(() => [
@click="(e: MouseEvent) => handleIconItemClick(e, setting)"
/>
</div>
<IconAnimation
v-if="showAnimationComponent && !settingsStore.disableTransitions && settingsStore.usePageSpecificTransitions"
ref="iconAnimation"
:icon="animationIcon"
:icon-size="6 * 1.2"
:position="animationPosition"
:duration="1000"
text-color="text-neutral-400/50 dark:text-neutral-600/20"
:started="iconAnimationStarted"
@animation-ended.once="handleAnimationEnded"
/>
<div
v-else
v-motion
text="neutral-200/50 dark:neutral-600/20" pointer-events-none
fixed top="[75dvh]" right--15 z--1
:initial="{ scale: 0.9, opacity: 0, rotate: 45 }"
fixed top="[calc(100dvh-12rem)]" bottom-0 right--10 z--1
:initial="{ scale: 0.9, opacity: 0, rotate: 180 }"
:enter="{ scale: 1, opacity: 1, rotate: 0 }"
:duration="500"
size-60
flex items-center justify-center
>
<div v-motion text="60" i-lucide:cog />
<div v-motion text="60" i-solar:settings-bold-duotone />
</div>
</div>
</template>
@@ -38,12 +38,14 @@ const { t } = useI18n()
<div
v-motion
text="neutral-200/50 dark:neutral-600/20" pointer-events-none
fixed top="[65dvh]" right--15 z--1
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="250"
:duration="500"
size-60
flex items-center justify-center
>
<div text="60" i-lucide:sprout />
<div text="60" i-solar:leaf-bold-duotone />
</div>
</div>
</template>
@@ -1,7 +1,6 @@
<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 { useSettings } from '@proj-airi/stage-ui/stores'
import { useElementBounding } from '@vueuse/core'
import { Vibrant } from 'node-vibrant/browser'
import { ref } from 'vue'
@@ -9,13 +8,13 @@ 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)
const settingsStore = useSettings()
const palette = ref<string[]>([])
@@ -40,6 +39,12 @@ async function extractColorsFromModel() {
URL.revokeObjectURL(frameUrl)
}
}
const {
iconAnimationStarted,
showIconAnimation,
animationIcon,
} = useIconAnimation('i-solar:people-nearby-bold-duotone')
</script>
<template>
@@ -70,8 +75,29 @@ async function extractColorsFromModel() {
<Live2DSettings w="50%" h="80vh" :palette="palette" @extract-colors-from-model="extractColorsFromModel" />
</div>
<div v-if="!settingsStore.usePageSpecificTransitions" text="neutral-200/50 dark:neutral-500/20" pointer-events-none fixed bottom-0 right-0 z--1 translate-x-10 translate-y-10>
<div text="40" i-lucide:person-standing />
<IconAnimation
v-if="showIconAnimation"
:z-index="-1"
:icon="animationIcon"
:icon-size="12"
:duration="1000"
:started="iconAnimationStarted"
:is-reverse="true"
position="calc(100dvw - 9.5rem), calc(100dvh - 9.5rem)"
text-color="text-neutral-200/50 dark:text-neutral-600/20"
/>
<div
v-else
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, y: 15 }"
:enter="{ scale: 1, opacity: 1, y: 0 }"
:duration="500"
size-60
flex items-center justify-center
>
<div text="60" i-solar:people-nearby-bold-duotone />
</div>
</template>
@@ -214,8 +214,17 @@ function updateCustomModelName(value: string) {
</div>
</div>
<div text="neutral-200/50 dark:neutral-600/20" pointer-events-none fixed bottom-0 right-0 z--1 translate-x-10 translate-y-10>
<div text="40" i-lucide:ghost />
<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
flex items-center justify-center
>
<div text="60" i-solar:ghost-bold-duotone />
</div>
</template>
@@ -4,6 +4,9 @@ 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()
@@ -24,7 +27,7 @@ const modulesList = computed<Module[]>(() => [
id: 'consciousness',
name: t('settings.pages.modules.consciousness.title'),
description: t('settings.pages.modules.consciousness.description'),
icon: 'i-lucide:ghost',
icon: 'i-solar:ghost-bold-duotone',
to: '/settings/modules/consciousness',
configured: false,
},
@@ -32,7 +35,7 @@ const modulesList = computed<Module[]>(() => [
id: 'speech',
name: t('settings.pages.modules.speech.title'),
description: t('settings.pages.modules.speech.description'),
icon: 'i-lucide:mic',
icon: 'i-solar:user-speak-rounded-bold-duotone',
to: '/settings/modules/speech',
configured: false,
},
@@ -40,7 +43,7 @@ const modulesList = computed<Module[]>(() => [
id: 'hearing',
name: t('settings.pages.modules.hearing.title'),
description: t('settings.pages.modules.hearing.description'),
icon: 'i-lucide:ear',
icon: 'i-solar:microphone-3-bold-duotone',
to: '',
configured: false,
},
@@ -48,7 +51,7 @@ const modulesList = computed<Module[]>(() => [
id: 'vision',
name: t('settings.pages.modules.vision.title'),
description: t('settings.pages.modules.vision.description'),
icon: 'i-lucide:eye',
icon: 'i-solar:eye-closed-bold-duotone',
to: '',
configured: false,
},
@@ -56,7 +59,7 @@ const modulesList = computed<Module[]>(() => [
id: 'memory-short-term',
name: t('settings.pages.modules.memory-short-term.title'),
description: t('settings.pages.modules.memory-short-term.description'),
icon: 'i-lucide:book',
icon: 'i-solar:bookmark-bold-duotone',
to: '/settings/modules/memory-short-term',
configured: false,
},
@@ -64,7 +67,7 @@ const modulesList = computed<Module[]>(() => [
id: 'memory-long-term',
name: t('settings.pages.modules.memory-long-term.title'),
description: t('settings.pages.modules.memory-long-term.description'),
icon: 'i-lucide:book-copy',
icon: 'i-solar:book-bookmark-bold-duotone',
to: '/settings/modules/memory-long-term',
configured: false,
},
@@ -101,6 +104,12 @@ const modulesList = computed<Module[]>(() => [
configured: false,
},
])
const {
iconAnimationStarted,
showIconAnimation,
animationIcon,
} = useIconAnimation('i-solar:layers-bold-duotone')
</script>
<template>
@@ -142,15 +151,29 @@ const modulesList = computed<Module[]>(() => [
:configured="module.configured"
/>
</div>
<IconAnimation
v-if="showIconAnimation"
:icon="animationIcon"
:icon-size="12"
:duration="1000"
:started="iconAnimationStarted"
:is-reverse="true"
:z-index="-1"
text-color="text-neutral-200/50 dark:text-neutral-600/20"
position="calc(100dvw - 9.5rem), calc(100dvh - 9.5rem)"
/>
<div
v-else
v-motion
text="neutral-200/50 dark:neutral-600/20" pointer-events-none
fixed top="[72dvh]" right--15 z--1
:initial="{ scale: 0.9, opacity: 0, y: 0 }"
:enter="{ scale: 1, opacity: 1, y: 10 }"
:duration="250"
fixed top="[calc(100dvh-15rem)]" bottom-0 right--5 z--1
:initial="{ scale: 0.9, opacity: 0, y: 20 }"
:enter="{ scale: 1, opacity: 1, y: 0 }"
:duration="500"
size-60
flex items-center justify-center
>
<div text="60" i-lucide:blocks />
<div text="60" i-solar:layers-bold-duotone />
</div>
</template>
@@ -518,10 +518,16 @@ function updateCustomModelName(value: string) {
</div>
<div
text="neutral-200/50 dark:neutral-500/20" pointer-events-none fixed bottom-0 right-0 z--1 translate-x-10
translate-y-10
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
flex items-center justify-center
>
<div text="40" i-lucide:volume-2 />
<div text="60" i-solar:user-speak-rounded-bold-duotone />
</div>
</template>
@@ -5,10 +5,18 @@ 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)
const {
iconAnimationStarted,
showIconAnimation,
animationIcon,
} = useIconAnimation('i-solar:box-minimalistic-bold-duotone')
</script>
<template>
@@ -50,15 +58,29 @@ const { allProvidersMetadata } = storeToRefs(providersStore)
:configured="provider.configured"
/>
</div>
<IconAnimation
v-if="showIconAnimation"
:z-index="-1"
:icon="animationIcon"
:icon-size="12"
:duration="1000"
:started="iconAnimationStarted"
:is-reverse="true"
position="calc(100dvw - 9.5rem), calc(100dvh - 9.5rem)"
text-color="text-neutral-200/50 dark:text-neutral-600/20"
/>
<div
v-else
v-motion
text="neutral-200/50 dark:neutral-600/20" pointer-events-none
fixed top="[70dvh]" right--15 z--1
:initial="{ scale: 0.9, opacity: 0 }"
:enter="{ scale: 1, opacity: 1 }"
:duration="250"
fixed top="[calc(100dvh-15rem)]" bottom-0 right--5 z--1
:initial="{ scale: 0.9, opacity: 0, y: 20 }"
:enter="{ scale: 1, opacity: 1, y: 0 }"
:duration="500"
size-60
flex items-center justify-center
>
<div text="60" i-lucide:brain />
<div text="60" i-solar:box-minimalistic-bold-duotone />
</div>
</template>
@@ -0,0 +1,72 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { useIconAnimation } from '../../../composables/useIconAnimation'
const { t } = useI18n()
const router = useRouter()
const {
iconAnimationStarted,
showIconAnimation,
animationIcon,
} = useIconAnimation('i-solar:armchair-2-bold-duotone')
</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>
<div>
Scene
</div>
<IconAnimation
v-if="showIconAnimation"
:z-index="-1"
:icon="animationIcon"
:icon-size="12"
:duration="1000"
:started="iconAnimationStarted"
:is-reverse="true"
position="calc(100dvw - 9.5rem), calc(100dvh - 9.5rem)"
text-color="text-neutral-200/50 dark:text-neutral-600/20"
/>
<div
v-else
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, y: 20 }"
:enter="{ scale: 1, opacity: 1, y: 0 }"
:duration="500"
size-60
flex items-center justify-center
>
<div text="60" i-solar:armchair-2-bold-duotone />
</div>
</template>
<route lang="yaml">
meta:
stageTransition:
name: slide
pageSpecificAvailable: true
</route>
+3
View File
@@ -339,6 +339,9 @@ settings:
description: x.ai
title: xAI
title: Providers
scene:
description: Configure the environment where the character lives
title: Scene
themes:
description: Customize your stage!
sections:
+3
View File
@@ -323,6 +323,9 @@ settings:
description: X.AI
title: xAI
title: 提供商
scene:
description: 配置角色所在环境
title: 场景
themes:
description: 自定义你的舞台外观!
sections:
@@ -311,8 +311,9 @@ function getModuleShortName(id: string, module: 'consciousness' | 'voice') {
:enter="{ scale: 1, opacity: 1, x: 0 }"
:duration="500"
size-60
flex items-center justify-center
>
<div text="60" i-lucide:id-card />
<div text="60" i-solar:emoji-funny-square-bold-duotone />
</div>
</template>
@@ -269,8 +269,9 @@ watch(() => [settings.usePageSpecificTransitions, settings.disableTransitions],
:initial="{ scale: 0.9, opacity: 0, rotate: 30 }"
:enter="{ scale: 1, opacity: 1, rotate: 0 }"
:duration="250"
flex items-center justify-center
>
<div text="60" i-lucide:paintbrush />
<div text="60" i-solar:filters-bold-duotone />
</div>
</template>
+15 -8
View File
@@ -63,37 +63,43 @@ const settings = computed(() => [
{
title: t('settings.pages.card.title'),
description: t('settings.pages.card.description'),
icon: 'i-lucide:id-card',
icon: 'i-solar:emoji-funny-square-bold-duotone',
to: '/settings/airi-card',
},
{
title: t('settings.pages.modules.title'),
description: t('settings.pages.modules.description'),
icon: 'i-lucide:blocks',
icon: 'i-solar:layers-bold-duotone',
to: '/settings/modules',
},
{
title: t('settings.pages.scene.title'),
description: t('settings.pages.scene.description'),
icon: 'i-solar:armchair-2-bold-duotone',
to: '/settings/scene',
},
{
title: t('settings.pages.models.title'),
description: t('settings.pages.models.description'),
icon: 'i-lucide:person-standing',
icon: 'i-solar:people-nearby-bold-duotone',
to: '/settings/models',
},
{
title: t('settings.pages.memory.title'),
description: t('settings.pages.memory.description'),
icon: 'i-lucide:sprout',
icon: 'i-solar:leaf-bold-duotone',
to: '/settings/memory',
},
{
title: t('settings.pages.providers.title'),
description: t('settings.pages.providers.description'),
icon: 'i-lucide:brain',
icon: 'i-solar:box-minimalistic-bold-duotone',
to: '/settings/providers',
},
{
title: t('settings.pages.themes.title'),
description: t('settings.pages.themes.description'),
icon: 'i-lucide:paintbrush',
icon: 'i-solar:filters-bold-duotone',
to: '/settings/appearance',
},
])
@@ -149,12 +155,13 @@ const settings = computed(() => [
v-motion
text="neutral-200/50 dark:neutral-600/20" pointer-events-none
fixed top="[calc(100dvh-12rem)]" bottom-0 right--10 z--1
:initial="{ scale: 0.9, opacity: 0, rotate: 45 }"
:initial="{ scale: 0.9, opacity: 0, rotate: 180 }"
:enter="{ scale: 1, opacity: 1, rotate: 0 }"
:duration="500"
size-60
flex items-center justify-center
>
<div v-motion text="60" i-lucide:cog />
<div v-motion text="60" i-solar:settings-bold-duotone />
</div>
</div>
</template>
@@ -43,8 +43,9 @@ const { t } = useI18n()
:enter="{ scale: 1, opacity: 1, y: 0 }"
:duration="500"
size-60
flex items-center justify-center
>
<div text="60" i-lucide:sprout />
<div text="60" i-solar:leaf-bold-duotone />
</div>
</div>
</template>
@@ -44,7 +44,7 @@ const {
iconAnimationStarted,
showIconAnimation,
animationIcon,
} = useIconAnimation('i-lucide:person-standing')
} = useIconAnimation('i-solar:people-nearby-bold-duotone')
</script>
<template>
@@ -95,8 +95,9 @@ const {
:enter="{ scale: 1, opacity: 1, y: 0 }"
:duration="500"
size-60
flex items-center justify-center
>
<div text="60" i-lucide:person-standing />
<div text="60" i-solar:people-nearby-bold-duotone />
</div>
</template>
@@ -222,8 +222,9 @@ function updateCustomModelName(value: string) {
:enter="{ scale: 1, opacity: 1, x: 0 }"
:duration="500"
size-60
flex items-center justify-center
>
<div text="60" i-lucide:ghost />
<div text="60" i-solar:ghost-bold-duotone />
</div>
</template>
@@ -27,7 +27,7 @@ const modulesList = computed<Module[]>(() => [
id: 'consciousness',
name: t('settings.pages.modules.consciousness.title'),
description: t('settings.pages.modules.consciousness.description'),
icon: 'i-lucide:ghost',
icon: 'i-solar:ghost-bold-duotone',
to: '/settings/modules/consciousness',
configured: false,
},
@@ -35,7 +35,7 @@ const modulesList = computed<Module[]>(() => [
id: 'speech',
name: t('settings.pages.modules.speech.title'),
description: t('settings.pages.modules.speech.description'),
icon: 'i-lucide:mic',
icon: 'i-solar:user-speak-rounded-bold-duotone',
to: '/settings/modules/speech',
configured: false,
},
@@ -43,7 +43,7 @@ const modulesList = computed<Module[]>(() => [
id: 'hearing',
name: t('settings.pages.modules.hearing.title'),
description: t('settings.pages.modules.hearing.description'),
icon: 'i-lucide:ear',
icon: 'i-solar:microphone-3-bold-duotone',
to: '',
configured: false,
},
@@ -51,7 +51,7 @@ const modulesList = computed<Module[]>(() => [
id: 'vision',
name: t('settings.pages.modules.vision.title'),
description: t('settings.pages.modules.vision.description'),
icon: 'i-lucide:eye',
icon: 'i-solar:eye-closed-bold-duotone',
to: '',
configured: false,
},
@@ -59,7 +59,7 @@ const modulesList = computed<Module[]>(() => [
id: 'memory-short-term',
name: t('settings.pages.modules.memory-short-term.title'),
description: t('settings.pages.modules.memory-short-term.description'),
icon: 'i-lucide:book',
icon: 'i-solar:bookmark-bold-duotone',
to: '/settings/modules/memory-short-term',
configured: false,
},
@@ -67,7 +67,7 @@ const modulesList = computed<Module[]>(() => [
id: 'memory-long-term',
name: t('settings.pages.modules.memory-long-term.title'),
description: t('settings.pages.modules.memory-long-term.description'),
icon: 'i-lucide:book-copy',
icon: 'i-solar:book-bookmark-bold-duotone',
to: '/settings/modules/memory-long-term',
configured: false,
},
@@ -109,7 +109,7 @@ const {
iconAnimationStarted,
showIconAnimation,
animationIcon,
} = useIconAnimation('i-lucide:blocks')
} = useIconAnimation('i-solar:layers-bold-duotone')
</script>
<template>
@@ -171,8 +171,9 @@ const {
:enter="{ scale: 1, opacity: 1, y: 0 }"
:duration="500"
size-60
flex items-center justify-center
>
<div text="60" i-lucide:blocks />
<div text="60" i-solar:layers-bold-duotone />
</div>
</template>
@@ -525,8 +525,9 @@ function updateCustomModelName(value: string) {
:enter="{ scale: 1, opacity: 1, x: 0 }"
:duration="500"
size-60
flex items-center justify-center
>
<div text="60" i-lucide:mic />
<div text="60" i-solar:user-speak-rounded-bold-duotone />
</div>
</template>
@@ -16,7 +16,7 @@ const {
iconAnimationStarted,
showIconAnimation,
animationIcon,
} = useIconAnimation('i-lucide:brain')
} = useIconAnimation('i-solar:box-minimalistic-bold-duotone')
</script>
<template>
@@ -78,8 +78,9 @@ const {
:enter="{ scale: 1, opacity: 1, y: 0 }"
:duration="500"
size-60
flex items-center justify-center
>
<div text="60" i-lucide:brain />
<div text="60" i-solar:box-minimalistic-bold-duotone />
</div>
</template>
@@ -0,0 +1,72 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { useIconAnimation } from '../../../composables/useIconAnimation'
const { t } = useI18n()
const router = useRouter()
const {
iconAnimationStarted,
showIconAnimation,
animationIcon,
} = useIconAnimation('i-solar:armchair-2-bold-duotone')
</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>
<div>
Scene
</div>
<IconAnimation
v-if="showIconAnimation"
:z-index="-1"
:icon="animationIcon"
:icon-size="12"
:duration="1000"
:started="iconAnimationStarted"
:is-reverse="true"
position="calc(100dvw - 9.5rem), calc(100dvh - 9.5rem)"
text-color="text-neutral-200/50 dark:text-neutral-600/20"
/>
<div
v-else
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, y: 20 }"
:enter="{ scale: 1, opacity: 1, y: 0 }"
:duration="500"
size-60
flex items-center justify-center
>
<div text="60" i-solar:armchair-2-bold-duotone />
</div>
</template>
<route lang="yaml">
meta:
stageTransition:
name: slide
pageSpecificAvailable: true
</route>