fix(stage-*): follow-up fix for #1696 (#1809)

This commit is contained in:
DrHuangMHT
2026-05-19 13:54:41 +08:00
committed by GitHub
parent 82b0d6a80c
commit c2bf73c95d
27 changed files with 459 additions and 228 deletions
@@ -1,19 +1,19 @@
<script lang="ts" setup>
import { supportedControl as threeSupportedControl, useThreeViewControl } from '@proj-airi/stage-ui-three'
import { supportedControl as l2dSupportedCtrl, useL2dViewControl } from '@proj-airi/stage-ui/stores/live2d'
import { defaultControlConfig as threeCtrlConf, supportedControl as threeSupportedControl, useThreeViewControl } from '@proj-airi/stage-ui-three'
import { defaultControlConfig as l2dCtrlConf, supportedControl as l2dSupportedCtrl, useL2dViewControl } from '@proj-airi/stage-ui/stores/live2d'
import { useSettingsStageModel } from '@proj-airi/stage-ui/stores/settings/stage-model'
import { Button } from '@proj-airi/ui'
import { storeToRefs } from 'pinia'
import { computed } from 'vue'
const { stageModelRenderer } = storeToRefs(useSettingsStageModel())
const { viewControlsEnabled: l2dViewCtrlEnabled, viewControlMode: l2dCtrlMode, reset: l2dReset } = useL2dViewControl()
const { viewControlsEnabled: threePlainCtrlEnabled, viewControlMode: threeCtrlMode, reset: threeReset } = useThreeViewControl()
const { viewControlsEnabled: l2dViewCtrlEnabled, viewControlMode: l2dCtrlMode, set: l2dSet } = useL2dViewControl()
const { viewControlsEnabled: threeSliderCtrlEnabled, viewControlMode: threeCtrlMode, set: threeSet } = useThreeViewControl()
const controlEnabled = computed(() => {
if (stageModelRenderer.value === 'live2d')
return { enabled: l2dViewCtrlEnabled, mode: l2dCtrlMode, supported: l2dSupportedCtrl, reset: l2dReset }
return { enabled: l2dViewCtrlEnabled, mode: l2dCtrlMode, supported: l2dSupportedCtrl, conf: l2dCtrlConf, reset: l2dSet }
if (stageModelRenderer.value === 'vrm')
return { enabled: threePlainCtrlEnabled, mode: threeCtrlMode, supported: threeSupportedControl, reset: threeReset }
return { enabled: threeSliderCtrlEnabled, mode: threeCtrlMode, supported: threeSupportedControl, conf: threeCtrlConf, reset: threeSet }
return null
})
@@ -26,39 +26,23 @@ function handleViewControlsToggle(targetMode: string) {
}
controlEnabled.value.mode.value = targetMode as any
}
// watch()
</script>
<template>
<div w-full flex flex-1 items-center self-end justify-end gap-2>
<Transition name="fade">
<div v-if="controlEnabled?.enabled.value" w-full flex justify-between gap-2>
<Button variant="secondary-muted" :toggled="controlEnabled.mode.value === 'x'" w-full @click="handleViewControlsToggle('x')">
X
</Button>
<Button variant="secondary-muted" :toggled="controlEnabled.mode.value === 'y'" w-full @click="handleViewControlsToggle('y')">
Y
</Button>
<Button v-if="controlEnabled.supported.includes('z' as any)" variant="secondary-muted" :toggled="controlEnabled.mode.value === 'z'" w-full @click="handleViewControlsToggle('z')">
Z
</Button>
<Button v-if="controlEnabled.supported.includes('cameraFOV' as any)" variant="secondary-muted" :toggled="controlEnabled.mode.value === 'cameraFOV'" w-full @click="handleViewControlsToggle('cameraFOV')">
FOV
</Button>
<Button v-if="controlEnabled.supported.includes('scale' as any)" variant="secondary-muted" :toggled="controlEnabled.mode.value === 'scale'" w-full @click="handleViewControlsToggle('scale')">
Scale
</Button>
<Button v-if="controlEnabled.supported.includes('cameraDistance' as any)" variant="secondary-muted" :toggled="controlEnabled.mode.value === 'cameraDistance'" w-full @click="handleViewControlsToggle('cameraDistance')">
Dis
<Button
v-for="control in controlEnabled.supported" :key="control" variant="secondary-muted"
:toggled="controlEnabled.mode.value === control" w-full @click="handleViewControlsToggle(control)"
>
{{ (controlEnabled.conf as any)[control].buttonText }}
</Button>
</div>
</Transition>
<button
w-fit flex items-center self-end justify-center justify-self-end rounded-xl p-2 backdrop-blur-md
border="2 solid neutral-100/60 dark:neutral-800/30"
bg="neutral-50/70 dark:neutral-800/70"
title="View"
border="2 solid neutral-100/60 dark:neutral-800/30" bg="neutral-50/70 dark:neutral-800/70" title="View"
text="neutral-500 dark:neutral-400"
@click="controlEnabled && (controlEnabled.enabled.value = !controlEnabled.enabled.value)"
>
@@ -31,7 +31,6 @@ import { useTranscriptions } from '../../composables/use-transcriptions'
import { BackgroundDialogPicker } from '../Backgrounds'
const { isDark, toggleDark } = useTheme()
const hearingDialogOpen = ref(false)
const chatOrchestrator = useChatOrchestratorStore()
const chatSession = useChatSessionStore()
const chatStream = useChatStreamStore()
@@ -59,24 +58,25 @@ const { themeColorsHueDynamic } = storeToRefs(useSettings())
const { viewControlsEnabled: l2dViewCtrlEnabled } = useL2dViewControl()
const { viewControlsEnabled: threeViewCtrlEnabled } = useThreeViewControl()
const settingsAudioDevice = useSettingsAudioDevice()
const { enabled, selectedAudioInput, stream, audioInputs } = storeToRefs(settingsAudioDevice)
const { enabled, stream } = storeToRefs(settingsAudioDevice)
const { ingest, onAfterMessageComposed } = chatOrchestrator
const { t } = useI18n()
const { audioContext } = useAudioContext()
const { startAnalyzer, stopAnalyzer, volumeLevel } = useAudioAnalyzer()
const { startAnalyzer, stopAnalyzer } = useAudioAnalyzer()
let analyzerSource: MediaStreamAudioSourceNode | undefined
function isMobileDevice() {
return /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
}
const { isListening } = useTranscriptions(
const { isListening, startStreamingTranscription, stopStreamingTranscription } = useTranscriptions(
{
messageInputRef: messageInput,
sendMessage: handleSend,
isStageTamagotchi,
},
)
const toggleTranscription = () => isListening.value ? stopStreamingTranscription() : startStreamingTranscription()
async function handleSubmit() {
if (!isMobileDevice()) {
@@ -115,14 +115,14 @@ function teardownAnalyzer() {
try {
analyzerSource?.disconnect()
}
catch {}
catch { }
analyzerSource = undefined
stopAnalyzer()
}
async function setupAnalyzer() {
teardownAnalyzer()
if (!hearingDialogOpen.value || !enabled.value || !stream.value)
if (!enabled.value || !stream.value)
return
if (audioContext.state === 'suspended')
await audioContext.resume()
@@ -137,12 +137,6 @@ watch([enabled, stream], () => {
setupAnalyzer()
}, { immediate: true })
watch(hearingDialogOpen, (value) => {
if (value) {
settingsAudioDevice.askPermission()
}
})
onAfterMessageComposed(async () => {
})
@@ -196,11 +190,9 @@ onMounted(() => {
</button>
<ChatSessionsDrawer v-model="sessionsDrawerOpen" />
<HearingConfigDialog
v-model:show="hearingDialogOpen"
v-model:enabled="enabled"
v-model:selected-audio-input="selectedAudioInput"
:audio-inputs="audioInputs"
:volume-level="volumeLevel"
:transcription="isListening"
:toggle-transcription="toggleTranscription"
:granted="true"
>
<button
@@ -139,8 +139,7 @@ watch(hearingPopoverOpen, async (value) => {
onAfterMessageComposed(async () => {
})
const { startAnalyzer, stopAnalyzer, volumeLevel } = useAudioAnalyzer()
const normalizedVolume = computed(() => Math.min(1, Math.max(0, (volumeLevel.value ?? 0) / 100)))
const { startAnalyzer, stopAnalyzer } = useAudioAnalyzer()
let analyzerSource: MediaStreamAudioSourceNode | undefined
function teardownAnalyzer() {
@@ -294,7 +293,6 @@ watch(sendMode, () => {
v-model:auto-send="autoSendEnabled"
:transcription="isListening"
:granted="true"
:volume-level="normalizedVolume"
@toggle-transcription="() => isListening ? stopStreamingTranscription() : startStreamingTranscription()"
/>
</PopoverContent>
@@ -17,13 +17,14 @@ export function useTranscriptions(options: TranscriptionOptions) {
const { messageInputRef: messageInput, sendMessage, isStageTamagotchi } = options
const hearingStore = useHearingStore()
const audioDeviceSettingsStore = useSettingsAudioDevice()
const hearingPipeline = useHearingSpeechInputPipeline()
const { transcribeForMediaStream, stopStreamingTranscription } = hearingPipeline
const { supportsStreamInput } = storeToRefs(hearingPipeline)
const { configured: hearingConfigured, autoSendEnabled, autoSendDelay } = storeToRefs(hearingStore)
const { enabled: hearingEnabled, stream } = storeToRefs(useSettingsAudioDevice())
const { enabled: hearingEnabled, stream } = storeToRefs(audioDeviceSettingsStore)
const providersStore = useProvidersStore()
const { askPermission, startStream } = useSettingsAudioDevice()
const { askPermission, startStream } = audioDeviceSettingsStore
const isListening = ref(false)