feat(stage-web|stage-tamagotchi): enter animation

This commit is contained in:
Neko Ayaka
2025-03-16 11:43:40 +08:00
parent 64649f12a2
commit 5259158827
20 changed files with 441 additions and 152 deletions
@@ -18,7 +18,14 @@ function handleLanguageChange(event: Event) {
</script>
<template>
<div flex="~ row" items-center gap-2>
<div
v-motion
flex="~ row" 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>
@@ -28,12 +35,57 @@ function handleLanguageChange(event: Event) {
</div>
<div flex="~ col gap-4">
<div flex="~ col gap-4">
<IconItem title="Modules" description="Thinking, vision, speech synthesis, gaming, etc." icon="i-lucide:blocks" to="/settings/modules" />
<IconItem title="Models" description="Live2D, VRM, etc." icon="i-lucide:person-standing" to="/settings/models" />
<IconItem title="Providers" description="LLMs, speech providers, etc." icon="i-lucide:brain" to="/settings/providers" />
<IconItem title="Themes" description="Customize your stage!" icon="i-lucide:paintbrush" to="/settings/themes" />
<IconItem
v-motion
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250"
title="Modules"
description="Thinking, vision, speech synthesis, gaming, etc."
icon="i-lucide:blocks"
to="/settings/modules"
/>
<IconItem
v-motion
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250"
:delay="50"
title="Models"
description="Live2D, VRM, etc."
icon="i-lucide:person-standing"
to="/settings/models"
/>
<IconItem
v-motion
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250"
:delay="100"
title="Providers"
description="LLMs, speech providers, etc."
icon="i-lucide:brain"
to="/settings/providers"
/>
<IconItem
v-motion
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250"
:delay="150"
title="Themes"
description="Customize your stage!"
icon="i-lucide:paintbrush"
to="/settings/themes"
/>
</div>
<div>
<div
v-motion
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250"
:delay="150"
>
<h2 text-2xl>
General
</h2>
@@ -41,11 +93,16 @@ function handleLanguageChange(event: Event) {
<div flex="~ col gap-4">
<!-- Language Setting -->
<div
v-motion
grid="~ cols-[150px_1fr]"
bg="neutral-100 dark:neutral-800"
hover="bg-neutral-200 dark:bg-neutral-700"
transition="all ease-in-out duration-250"
items-center gap-1.5 rounded-lg px-4 py-3
transition="all ease-in-out duration-250" items-center gap-1.5 rounded-lg px-4
py-3
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250"
:delay="200"
>
<div text="sm">
<span>{{ t('settings.language.title') }}</span>
@@ -71,10 +128,15 @@ function handleLanguageChange(event: Event) {
</div>
<!-- Theme Setting -->
<label
v-motion
bg="neutral-100 dark:neutral-800"
hover="bg-neutral-200 dark:bg-neutral-700"
transition="all ease-in-out duration-250"
w-full flex cursor-pointer rounded-lg px-4 py-3
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250"
:delay="250"
>
<input
v-model="dark"
@@ -106,10 +168,15 @@ function handleLanguageChange(event: Event) {
</label>
<!-- Developer Settings -->
<label
v-motion
bg="neutral-100 dark:neutral-800"
hover="bg-neutral-200 dark:bg-neutral-700"
transition="all ease-in-out duration-250"
w-full flex cursor-pointer rounded-lg px-4 py-3
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250"
:delay="300"
>
<input
v-model="disableTransitions"
@@ -1,14 +1,26 @@
<script setup lang="ts">
import { WidgetStage } from '@proj-airi/stage-ui/components'
import Live2DCanvas from '@proj-airi/stage-ui/components/Live2D/Canvas.vue'
import Live2DModel from '@proj-airi/stage-ui/components/Live2D/Model.vue'
import { useElementBounding } from '@vueuse/core'
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import Live2DSettings from '../../../components/Widgets/Live2DSettings.vue'
const router = useRouter()
const live2dContainerRef = ref<HTMLDivElement>()
const { width, height } = useElementBounding(live2dContainerRef)
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
@@ -22,7 +34,11 @@ const router = useRouter()
</h1>
</div>
<div flex>
<WidgetStage w="50%" h="80vh" />
<div ref="live2dContainerRef" w="50%" h="80vh">
<Live2DCanvas v-slot="{ app }" :width="width" :height="height">
<Live2DModel :app="app" :mouth-open-size="0" :width="width" :height="height" :paused="false" />
</Live2DCanvas>
</div>
<Live2DSettings w="50%" h="80vh" />
</div>
@@ -31,7 +31,14 @@ function updateCustomModelName(value: string) {
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
@@ -26,22 +26,22 @@ const modulesList = computed<Module[]>(() => [
to: '/settings/modules/consciousness',
configured: false,
},
// {
// id: 'hearing',
// name: 'Hearing',
// description: 'Hearing, speech recognition, etc.',
// icon: 'i-lucide:ear',
// to: '',
// configured: false,
// },
// {
// id: 'messaging-discord',
// name: 'Discord',
// description: 'Messaging, notifications, etc.',
// icon: 'i-simple-icons:discord',
// to: '',
// configured: false,
// },
{
id: 'hearing',
name: 'Hearing',
description: 'Hearing, speech recognition, etc.',
icon: 'i-lucide:ear',
to: '',
configured: false,
},
{
id: 'messaging-discord',
name: 'Discord',
description: 'Messaging, notifications, etc.',
icon: 'i-simple-icons:discord',
to: '',
configured: false,
},
{
id: 'speech',
name: 'Speech',
@@ -50,51 +50,58 @@ const modulesList = computed<Module[]>(() => [
to: '/settings/modules/speech',
configured: false,
},
// {
// id: 'memory-short-term',
// name: 'Short-Term Memory',
// description: 'Short-term memory, etc.',
// icon: 'i-lucide:book',
// to: '',
// configured: false,
// },
// {
// id: 'memory-long-term',
// name: 'Long-Term Memory',
// description: 'Long-term memory, etc.',
// icon: 'i-lucide:book-copy',
// to: '',
// configured: false,
// },
// {
// id: 'vision',
// name: 'Vision',
// description: 'Vision, etc.',
// icon: 'i-lucide:eye',
// to: '',
// configured: false,
// },
// {
// id: 'game-minecraft',
// name: 'Minecraft',
// description: 'Playing Minecraft with you, etc.',
// iconColor: 'i-vscode-icons:file-type-minecraft',
// to: '',
// configured: false,
// },
// {
// id: 'game-factorio',
// name: 'Factorio',
// description: 'Playing Factorio with you, etc.',
// iconImage: FactorioIcon,
// to: '',
// configured: false,
// },
{
id: 'memory-short-term',
name: 'Short-Term Memory',
description: 'Short-term memory, etc.',
icon: 'i-lucide:book',
to: '',
configured: false,
},
{
id: 'memory-long-term',
name: 'Long-Term Memory',
description: 'Long-term memory, etc.',
icon: 'i-lucide:book-copy',
to: '',
configured: false,
},
{
id: 'vision',
name: 'Vision',
description: 'Vision, etc.',
icon: 'i-lucide:eye',
to: '',
configured: false,
},
{
id: 'game-minecraft',
name: 'Minecraft',
description: 'Playing Minecraft with you, etc.',
iconColor: 'i-vscode-icons:file-type-minecraft',
to: '',
configured: false,
},
{
id: 'game-factorio',
name: 'Factorio',
description: 'Playing Factorio with you, etc.',
iconImage: '',
to: '',
configured: false,
},
])
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
@@ -109,8 +116,13 @@ const modulesList = computed<Module[]>(() => [
</div>
<div grid="~ cols-1 sm:cols-2 gap-4">
<IconStatusItem
v-for="module in modulesList"
v-for="(module, index) of modulesList"
:key="module.id"
v-motion
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250 + index * 10"
:delay="index * 50"
:title="module.name"
:description="module.description"
:icon="module.icon"
@@ -61,7 +61,14 @@ function updateSSMLExample() {
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
@@ -239,7 +239,14 @@ function handleResetVoiceSettings() {
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
@@ -10,7 +10,14 @@ const { allProvidersMetadata } = storeToRefs(providersStore)
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
@@ -25,8 +32,13 @@ const { allProvidersMetadata } = storeToRefs(providersStore)
</div>
<div grid="~ cols-2 gap-4">
<IconStatusItem
v-for="provider in allProvidersMetadata"
v-for="(provider, index) of allProvidersMetadata"
:key="provider.id"
v-motion
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250 + index * 10"
:delay="index * 50"
:title="provider.localizedName"
:description="provider.localizedDescription"
:icon="provider.icon"
@@ -33,7 +33,14 @@ watch([apiKey, baseUrl], () => {
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
@@ -37,7 +37,14 @@ watch([apiKey, baseUrl], () => {
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
@@ -135,7 +135,14 @@ function isColorSelected(hexColor: string): boolean {
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
+75 -8
View File
@@ -18,7 +18,14 @@ function handleLanguageChange(event: Event) {
</script>
<template>
<div flex="~ row" items-center gap-2>
<div
v-motion
flex="~ row" 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>
@@ -28,12 +35,57 @@ function handleLanguageChange(event: Event) {
</div>
<div flex="~ col gap-4">
<div flex="~ col gap-4">
<IconItem title="Modules" description="Thinking, vision, speech synthesis, gaming, etc." icon="i-lucide:blocks" to="/settings/modules" />
<IconItem title="Models" description="Live2D, VRM, etc." icon="i-lucide:person-standing" to="/settings/models" />
<IconItem title="Providers" description="LLMs, speech providers, etc." icon="i-lucide:brain" to="/settings/providers" />
<IconItem title="Themes" description="Customize your stage!" icon="i-lucide:paintbrush" to="/settings/themes" />
<IconItem
v-motion
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250"
title="Modules"
description="Thinking, vision, speech synthesis, gaming, etc."
icon="i-lucide:blocks"
to="/settings/modules"
/>
<IconItem
v-motion
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250"
:delay="50"
title="Models"
description="Live2D, VRM, etc."
icon="i-lucide:person-standing"
to="/settings/models"
/>
<IconItem
v-motion
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250"
:delay="100"
title="Providers"
description="LLMs, speech providers, etc."
icon="i-lucide:brain"
to="/settings/providers"
/>
<IconItem
v-motion
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250"
:delay="150"
title="Themes"
description="Customize your stage!"
icon="i-lucide:paintbrush"
to="/settings/themes"
/>
</div>
<div>
<div
v-motion
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250"
:delay="150"
>
<h2 text-2xl>
General
</h2>
@@ -41,11 +93,16 @@ function handleLanguageChange(event: Event) {
<div flex="~ col gap-4">
<!-- Language Setting -->
<div
v-motion
grid="~ cols-[150px_1fr]"
bg="neutral-100 dark:neutral-800"
hover="bg-neutral-200 dark:bg-neutral-700"
transition="all ease-in-out duration-250"
items-center gap-1.5 rounded-lg px-4 py-3
transition="all ease-in-out duration-250" items-center gap-1.5 rounded-lg px-4
py-3
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250"
:delay="200"
>
<div text="sm">
<span>{{ t('settings.language.title') }}</span>
@@ -71,10 +128,15 @@ function handleLanguageChange(event: Event) {
</div>
<!-- Theme Setting -->
<label
v-motion
bg="neutral-100 dark:neutral-800"
hover="bg-neutral-200 dark:bg-neutral-700"
transition="all ease-in-out duration-250"
w-full flex cursor-pointer rounded-lg px-4 py-3
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250"
:delay="250"
>
<input
v-model="dark"
@@ -106,10 +168,15 @@ function handleLanguageChange(event: Event) {
</label>
<!-- Developer Settings -->
<label
v-motion
bg="neutral-100 dark:neutral-800"
hover="bg-neutral-200 dark:bg-neutral-700"
transition="all ease-in-out duration-250"
w-full flex cursor-pointer rounded-lg px-4 py-3
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250"
:delay="300"
>
<input
v-model="disableTransitions"
@@ -13,7 +13,14 @@ const { width, height } = useElementBounding(live2dContainerRef)
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
@@ -31,7 +31,14 @@ function updateCustomModelName(value: string) {
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
@@ -26,22 +26,22 @@ const modulesList = computed<Module[]>(() => [
to: '/settings/modules/consciousness',
configured: false,
},
// {
// id: 'hearing',
// name: 'Hearing',
// description: 'Hearing, speech recognition, etc.',
// icon: 'i-lucide:ear',
// to: '',
// configured: false,
// },
// {
// id: 'messaging-discord',
// name: 'Discord',
// description: 'Messaging, notifications, etc.',
// icon: 'i-simple-icons:discord',
// to: '',
// configured: false,
// },
{
id: 'hearing',
name: 'Hearing',
description: 'Hearing, speech recognition, etc.',
icon: 'i-lucide:ear',
to: '',
configured: false,
},
{
id: 'messaging-discord',
name: 'Discord',
description: 'Messaging, notifications, etc.',
icon: 'i-simple-icons:discord',
to: '',
configured: false,
},
{
id: 'speech',
name: 'Speech',
@@ -50,51 +50,58 @@ const modulesList = computed<Module[]>(() => [
to: '/settings/modules/speech',
configured: false,
},
// {
// id: 'memory-short-term',
// name: 'Short-Term Memory',
// description: 'Short-term memory, etc.',
// icon: 'i-lucide:book',
// to: '',
// configured: false,
// },
// {
// id: 'memory-long-term',
// name: 'Long-Term Memory',
// description: 'Long-term memory, etc.',
// icon: 'i-lucide:book-copy',
// to: '',
// configured: false,
// },
// {
// id: 'vision',
// name: 'Vision',
// description: 'Vision, etc.',
// icon: 'i-lucide:eye',
// to: '',
// configured: false,
// },
// {
// id: 'game-minecraft',
// name: 'Minecraft',
// description: 'Playing Minecraft with you, etc.',
// iconColor: 'i-vscode-icons:file-type-minecraft',
// to: '',
// configured: false,
// },
// {
// id: 'game-factorio',
// name: 'Factorio',
// description: 'Playing Factorio with you, etc.',
// iconImage: FactorioIcon,
// to: '',
// configured: false,
// },
{
id: 'memory-short-term',
name: 'Short-Term Memory',
description: 'Short-term memory, etc.',
icon: 'i-lucide:book',
to: '',
configured: false,
},
{
id: 'memory-long-term',
name: 'Long-Term Memory',
description: 'Long-term memory, etc.',
icon: 'i-lucide:book-copy',
to: '',
configured: false,
},
{
id: 'vision',
name: 'Vision',
description: 'Vision, etc.',
icon: 'i-lucide:eye',
to: '',
configured: false,
},
{
id: 'game-minecraft',
name: 'Minecraft',
description: 'Playing Minecraft with you, etc.',
iconColor: 'i-vscode-icons:file-type-minecraft',
to: '',
configured: false,
},
{
id: 'game-factorio',
name: 'Factorio',
description: 'Playing Factorio with you, etc.',
iconImage: '',
to: '',
configured: false,
},
])
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
@@ -109,8 +116,13 @@ const modulesList = computed<Module[]>(() => [
</div>
<div grid="~ cols-1 sm:cols-2 gap-4">
<IconStatusItem
v-for="module in modulesList"
v-for="(module, index) of modulesList"
:key="module.id"
v-motion
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250 + index * 10"
:delay="index * 50"
:title="module.name"
:description="module.description"
:icon="module.icon"
@@ -61,7 +61,14 @@ function updateSSMLExample() {
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
@@ -239,7 +239,14 @@ function handleResetVoiceSettings() {
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
@@ -10,7 +10,14 @@ const { allProvidersMetadata } = storeToRefs(providersStore)
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
@@ -25,8 +32,13 @@ const { allProvidersMetadata } = storeToRefs(providersStore)
</div>
<div grid="~ cols-2 gap-4">
<IconStatusItem
v-for="provider in allProvidersMetadata"
v-for="(provider, index) of allProvidersMetadata"
:key="provider.id"
v-motion
:initial="{ opacity: 0, y: 10 }"
:enter="{ opacity: 1, y: 0 }"
:duration="250 + index * 10"
:delay="index * 50"
:title="provider.localizedName"
:description="provider.localizedDescription"
:icon="provider.icon"
@@ -33,7 +33,14 @@ watch([apiKey, baseUrl], () => {
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
@@ -37,7 +37,14 @@ watch([apiKey, baseUrl], () => {
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>
@@ -135,7 +135,14 @@ function isColorSelected(hexColor: string): boolean {
</script>
<template>
<div flex="~ row" items-center gap-2>
<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>