fix(stage-ui): stabilize connection status animation (#2365)

This commit is contained in:
Neko
2026-08-26 04:14:53 +08:00
committed by GitHub
parent 95cc685b1b
commit 6299dd0919
6 changed files with 4 additions and 85 deletions
@@ -1,5 +1,4 @@
<script setup lang="ts">
import { useLampFlickerAnimation } from '@proj-airi/stage-ui/composables/use-lamp-flicker-animation'
import { useModsServerChannelStore } from '@proj-airi/stage-ui/stores/mods/api/channel-server'
import { lampFlickerAnimationClass } from '@proj-airi/ui'
import { storeToRefs } from 'pinia'
@@ -11,8 +10,6 @@ const { t } = useI18n()
const router = useRouter()
const { connected } = storeToRefs(useModsServerChannelStore())
const { flickerStyle, onAnimationIteration } = useLampFlickerAnimation(() => !connected.value)
const statusSize = {
border: 'border-2',
icon: 'size-6',
@@ -66,11 +63,7 @@ function openConnectionSettings() {
:title="tooltipLabel"
@click="openConnectionSettings"
>
<div
:class="iconClasses"
:style="flickerStyle"
@animationiteration="onAnimationIteration"
/>
<div :class="iconClasses" />
</button>
</template>
@@ -1,6 +1,5 @@
<script setup lang="ts">
import { useElectronEventaInvoke } from '@proj-airi/electron-vueuse'
import { useLampFlickerAnimation } from '@proj-airi/stage-ui/composables/use-lamp-flicker-animation'
import { useModsServerChannelStore } from '@proj-airi/stage-ui/stores/mods/api/channel-server'
import { lampFlickerAnimationClass } from '@proj-airi/ui'
import { storeToRefs } from 'pinia'
@@ -21,8 +20,6 @@ const { t } = useI18n()
const { connected } = storeToRefs(useModsServerChannelStore())
const openSettings = useElectronEventaInvoke(electronOpenSettings)
const { flickerStyle, onAnimationIteration } = useLampFlickerAnimation(() => !connected.value)
const iconClasses = computed(() => {
return [
'i-mingcute:link-3-line',
@@ -51,7 +48,7 @@ const tooltipLabel = computed(() => {
:title="tooltipLabel"
@click="openSettings({ route: '/settings/connection' })"
>
<div :class="iconClasses" :style="flickerStyle" @animationiteration="onAnimationIteration" />
<div :class="iconClasses" />
</ControlButton>
<template #tooltip>
{{ tooltipLabel }}
@@ -12,7 +12,6 @@ export * from './use-chat-session/summary'
export * from './use-hearing-playground-segments'
export * from './use-inference-preload'
export * from './use-inference-status'
export * from './use-lamp-flicker-animation'
export * from './use-linked-accounts'
export * from './use-model-preload'
export * from './use-optimistic'
@@ -1,64 +0,0 @@
import type { MaybeRefOrGetter } from 'vue'
import { lampFlickerAnimationClass } from '@proj-airi/ui'
import { computed, ref, toValue, watch } from 'vue'
export { lampFlickerAnimationClass }
const LAMP_FLICKER_DELAY_VAR = '--lamp-flicker-delay'
const LAMP_FLICKER_DURATION_VAR = '--lamp-flicker-duration'
/**
* Drives randomized keyframe timing for `.lamp-flicker-animation` from `@proj-airi/ui/main.css` while `flickerActive` is true.
* Import `@proj-airi/ui/main.css` from the app global stylesheet (e.g. `styles/main.css`). When inactive, delay resets to 0s.
*/
export function useLampFlickerAnimation(flickerActive: MaybeRefOrGetter<boolean>) {
const flickerDuration = ref('6.4s')
const flickerDelay = ref('0s')
function randomizeFlicker(resetPhase = false) {
flickerDuration.value = `${(5.8 + Math.random() * 1.8).toFixed(2)}s`
if (resetPhase) {
flickerDelay.value = `${(-Math.random() * 5.4).toFixed(2)}s`
return
}
flickerDelay.value = '0s'
}
function onAnimationIteration() {
if (toValue(flickerActive)) {
randomizeFlicker()
}
}
watch(
() => toValue(flickerActive),
(active) => {
if (!active) {
flickerDelay.value = '0s'
return
}
randomizeFlicker(true)
},
{ immediate: true },
)
const flickerStyle = computed(() => {
if (!toValue(flickerActive)) {
return undefined
}
return {
[LAMP_FLICKER_DELAY_VAR]: flickerDelay.value,
[LAMP_FLICKER_DURATION_VAR]: flickerDuration.value,
} as Record<string, string>
})
return {
flickerStyle,
onAnimationIteration,
}
}
@@ -1,2 +1,2 @@
/** Global class from `@proj-airi/ui/main.css`; pairs with useLampFlickerAnimation in stage-ui. */
/** Global offline-status animation class from `@proj-airi/ui/main.css`. */
export const lampFlickerAnimationClass = 'lamp-flicker-animation' as const
+1 -7
View File
@@ -1,7 +1,5 @@
/**
* Opacity “lamp flicker” for offline / warning icons.
* Timing is driven by useLampFlickerAnimation (@proj-airi/stage-ui) via
* --lamp-flicker-delay and --lamp-flicker-duration.
* Loaded via @proj-airi/ui/main.css (import from app `styles/main.css`).
*/
@keyframes lamp-flicker-keyframes {
@@ -78,11 +76,7 @@
}
.lamp-flicker-animation {
animation-delay: var(--lamp-flicker-delay, 0s);
animation-duration: var(--lamp-flicker-duration, 6.4s);
animation-iteration-count: infinite;
animation-name: lamp-flicker-keyframes;
animation-timing-function: ease-in-out;
animation: lamp-flicker-keyframes 6.4s ease-in-out infinite;
filter: drop-shadow(0 0 0.14rem rgb(251 191 36 / 0.18));
will-change: opacity;
}