feat(stage-tamagotchi): move connection status into controls island (#2317)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { defineInvoke } from '@moeru/eventa'
|
||||
import { useElectronEventaContext, useElectronEventaInvoke, useElectronMouseInElement } from '@proj-airi/electron-vueuse'
|
||||
import { IS_DEV } from '@proj-airi/stage-shared'
|
||||
import { useSettings, useSettingsAudioDevice } from '@proj-airi/stage-ui/stores/settings'
|
||||
import { useTheme } from '@proj-airi/ui'
|
||||
import { refDebounced, useIntervalFn } from '@vueuse/core'
|
||||
@@ -8,6 +9,7 @@ import { storeToRefs } from 'pinia'
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import StatusIsland from '../status-island/index.vue'
|
||||
import ControlButtonTooltip from './control-button-tooltip.vue'
|
||||
import ControlButton from './control-button.vue'
|
||||
import ControlsIslandAuthButton from './controls-island-auth-button.vue'
|
||||
@@ -303,6 +305,12 @@ function resetMainWindowPosition() {
|
||||
</template>
|
||||
</ControlButtonTooltip>
|
||||
|
||||
<StatusIsland
|
||||
v-if="IS_DEV"
|
||||
:button-style="adjustStyleClasses.button"
|
||||
:icon-class="adjustStyleClasses.icon"
|
||||
/>
|
||||
|
||||
<ControlButtonTooltip side="left">
|
||||
<ControlButton
|
||||
v-track-button="{ name: 'controls_island_action', action: 'toggle_chat' }"
|
||||
|
||||
+22
-38
@@ -12,37 +12,23 @@ import ControlButton from '../controls-island/control-button.vue'
|
||||
|
||||
import { electronOpenSettings } from '../../../../shared/eventa'
|
||||
|
||||
const props = defineProps<{
|
||||
buttonStyle: string
|
||||
iconClass: string
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
const { connected } = storeToRefs(useModsServerChannelStore())
|
||||
const openSettings = useElectronEventaInvoke(electronOpenSettings)
|
||||
|
||||
const { flickerStyle, onAnimationIteration } = useLampFlickerAnimation(() => !connected.value)
|
||||
|
||||
const statusIslandSize = {
|
||||
border: 'border-2',
|
||||
icon: 'size-6',
|
||||
padding: 'p-2.5',
|
||||
} as const
|
||||
|
||||
const buttonStyle = computed(() => {
|
||||
return [
|
||||
statusIslandSize.border,
|
||||
statusIslandSize.padding,
|
||||
'transition-all duration-300 ease-in-out',
|
||||
connected.value
|
||||
? 'border-emerald-200/60 bg-white/85 hover:bg-emerald-50/90 dark:border-emerald-400/15 dark:bg-neutral-900/75 dark:hover:bg-neutral-900/88'
|
||||
: 'border-amber-300/80 bg-amber-50/90 hover:bg-amber-100/90 dark:border-amber-400/30 dark:bg-amber-950/25 dark:hover:bg-amber-950/38',
|
||||
]
|
||||
})
|
||||
|
||||
const iconClasses = computed(() => {
|
||||
return [
|
||||
connected.value ? 'i-ph:wifi-high' : `i-ph:wifi-slash ${lampFlickerAnimationClass}`,
|
||||
statusIslandSize.icon,
|
||||
'shrink-0 transition-colors duration-300 ease-in-out',
|
||||
connected.value
|
||||
? 'text-emerald-600 dark:text-emerald-300'
|
||||
: 'text-amber-600 dark:text-amber-300',
|
||||
'i-mingcute:link-3-line',
|
||||
!connected.value && lampFlickerAnimationClass,
|
||||
props.iconClass,
|
||||
'shrink-0 text-neutral-800 transition-colors duration-300 ease-in-out dark:text-neutral-300',
|
||||
]
|
||||
})
|
||||
|
||||
@@ -58,19 +44,17 @@ const tooltipLabel = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div fixed right-3 top-3 z-20>
|
||||
<ControlButtonTooltip side="left">
|
||||
<ControlButton
|
||||
:button-style="buttonStyle.join(' ')"
|
||||
:aria-label="tooltipLabel"
|
||||
:title="tooltipLabel"
|
||||
@click="openSettings({ route: '/settings/connection' })"
|
||||
>
|
||||
<div :class="iconClasses" :style="flickerStyle" @animationiteration="onAnimationIteration" />
|
||||
</ControlButton>
|
||||
<template #tooltip>
|
||||
{{ tooltipLabel }}
|
||||
</template>
|
||||
</ControlButtonTooltip>
|
||||
</div>
|
||||
<ControlButtonTooltip side="left">
|
||||
<ControlButton
|
||||
:button-style="props.buttonStyle"
|
||||
:aria-label="tooltipLabel"
|
||||
:title="tooltipLabel"
|
||||
@click="openSettings({ route: '/settings/connection' })"
|
||||
>
|
||||
<div :class="iconClasses" :style="flickerStyle" @animationiteration="onAnimationIteration" />
|
||||
</ControlButton>
|
||||
<template #tooltip>
|
||||
{{ tooltipLabel }}
|
||||
</template>
|
||||
</ControlButtonTooltip>
|
||||
</template>
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
useElectronRelativeMouse,
|
||||
} from '@proj-airi/electron-vueuse'
|
||||
import { createTranscriptBuffer } from '@proj-airi/pipelines-audio'
|
||||
import { hearingInputChannelName, IS_DEV } from '@proj-airi/stage-shared'
|
||||
import { hearingInputChannelName } from '@proj-airi/stage-shared'
|
||||
import { useModelStore, useThreeSceneIsTransparentAtPoint } from '@proj-airi/stage-ui-three'
|
||||
import { HoloCoupon } from '@proj-airi/stage-ui/components'
|
||||
import {
|
||||
@@ -37,7 +37,6 @@ import { toast } from 'vue-sonner'
|
||||
|
||||
import ControlsIsland from '../components/stage-islands/controls-island/index.vue'
|
||||
import ResourceStatusIsland from '../components/stage-islands/resource-status-island/index.vue'
|
||||
import StatusIsland from '../components/stage-islands/status-island/index.vue'
|
||||
|
||||
import { electronOpenOnboarding } from '../../shared/eventa'
|
||||
import { modelSettingsRuntimeSnapshotChannelName } from '../../shared/model-settings-runtime'
|
||||
@@ -53,7 +52,6 @@ import {
|
||||
} from '../utils/voice-input-suppression'
|
||||
|
||||
const controlsIslandRef = ref<InstanceType<typeof ControlsIsland>>()
|
||||
const statusIslandRef = ref<InstanceType<typeof StatusIsland>>()
|
||||
const widgetStageRef = ref<InstanceType<typeof WidgetStage>>()
|
||||
const stageCanvas = toRef(() => widgetStageRef.value?.canvasElement())
|
||||
const componentStateStage = ref<'pending' | 'loading' | 'mounted'>('pending')
|
||||
@@ -68,9 +66,7 @@ const openOnboarding = useElectronEventaInvoke(electronOpenOnboarding)
|
||||
|
||||
const { isOutside: isOutsideWindow } = useElectronMouseInWindow()
|
||||
const { isOutside } = useElectronMouseInElement(controlsIslandRef)
|
||||
const { isOutside: isOutsideStatusIsland } = useElectronMouseInElement(statusIslandRef)
|
||||
const isOutsideFor250Ms = refDebounced(isOutside, 250)
|
||||
const isOutsideStatusIslandFor250Ms = refDebounced(isOutsideStatusIsland, 250)
|
||||
const { x: relativeMouseX, y: relativeMouseY } = useElectronRelativeMouse()
|
||||
// NOTICE: In real-world use cases of Fade on Hover feature, the cursor may move around the edge of the
|
||||
// model rapidly, causing flickering effects when checking pixel transparency strictly.
|
||||
@@ -245,7 +241,7 @@ const modelSettingsRuntimeSnapshot = computed<ModelSettingsRuntimeSnapshot>(() =
|
||||
* -> {@link handleFadeOnHoverInteractionChange}
|
||||
*
|
||||
* Upstream:
|
||||
* - {@link isOutsideFor250Ms}, {@link isOutsideStatusIslandFor250Ms}, and {@link isAroundWindowBorderFor250Ms}
|
||||
* - {@link isOutsideFor250Ms} and {@link isAroundWindowBorderFor250Ms}
|
||||
* - {@link isOutsideWindow}, {@link isTransparent}, and {@link isTransparentForMouseEvents}
|
||||
* - {@link hearingDialogOpen}, {@link fadeOnHoverEnabled}, and {@link stagePaused}
|
||||
*
|
||||
@@ -269,7 +265,7 @@ function handleFadeOnHoverInteractionChange() {
|
||||
return
|
||||
}
|
||||
|
||||
const insideControls = !isOutsideFor250Ms.value || !isOutsideStatusIslandFor250Ms.value
|
||||
const insideControls = !isOutsideFor250Ms.value
|
||||
const nearBorder = isAroundWindowBorderFor250Ms.value
|
||||
|
||||
if (insideControls || nearBorder) {
|
||||
@@ -293,7 +289,7 @@ function handleFadeOnHoverInteractionChange() {
|
||||
}
|
||||
|
||||
watch(
|
||||
[isOutsideFor250Ms, isOutsideStatusIslandFor250Ms, isAroundWindowBorderFor250Ms, isOutsideWindow, isTransparent, isTransparentForMouseEvents, hearingDialogOpen, fadeOnHoverEnabled, stagePaused],
|
||||
[isOutsideFor250Ms, isAroundWindowBorderFor250Ms, isOutsideWindow, isTransparent, isTransparentForMouseEvents, hearingDialogOpen, fadeOnHoverEnabled, stagePaused],
|
||||
handleFadeOnHoverInteractionChange,
|
||||
{ immediate: true },
|
||||
)
|
||||
@@ -816,7 +812,6 @@ const cursorPosition = computed(() => ({
|
||||
'transition-opacity duration-250 ease-in-out',
|
||||
]"
|
||||
>
|
||||
<StatusIsland v-if="IS_DEV" ref="statusIslandRef" />
|
||||
<ResourceStatusIsland />
|
||||
<WidgetStage
|
||||
ref="widgetStageRef"
|
||||
|
||||
Reference in New Issue
Block a user