fix(stage): cover manual TTS stop across layouts
Add the stop-speaking control to the mobile shared input layout and the Electron Tamagotchi chat input so Web/shared, mobile, and Electron all expose the same manual TTS interruption path. Signed-off-by: RainbowBird <git@luoling.moe> Commit-Message-Assisted-by: Codex
This commit is contained in:
@@ -3,6 +3,7 @@ import type { ChatToolCallRendererRegistry } from '@proj-airi/stage-ui/component
|
|||||||
import type { ChatHistoryItem } from '@proj-airi/stage-ui/types/chat'
|
import type { ChatHistoryItem } from '@proj-airi/stage-ui/types/chat'
|
||||||
|
|
||||||
import { errorMessageFrom } from '@moeru/std'
|
import { errorMessageFrom } from '@moeru/std'
|
||||||
|
import { useStopSpeakingButton } from '@proj-airi/stage-layouts/composables/useStopSpeakingButton'
|
||||||
import { ChatHistory, JournalPreviewModal } from '@proj-airi/stage-ui/components'
|
import { ChatHistory, JournalPreviewModal } from '@proj-airi/stage-ui/components'
|
||||||
import { useBackgroundStore } from '@proj-airi/stage-ui/stores/background'
|
import { useBackgroundStore } from '@proj-airi/stage-ui/stores/background'
|
||||||
import { useChatOrchestratorStore } from '@proj-airi/stage-ui/stores/chat'
|
import { useChatOrchestratorStore } from '@proj-airi/stage-ui/stores/chat'
|
||||||
@@ -56,6 +57,7 @@ const sendModeLabels = computed<Record<SendMode, string>>(() => ({
|
|||||||
'ctrl-enter': t('stage.send-mode.ctrl-enter'),
|
'ctrl-enter': t('stage.send-mode.ctrl-enter'),
|
||||||
'double-enter': t('stage.send-mode.double-enter'),
|
'double-enter': t('stage.send-mode.double-enter'),
|
||||||
}))
|
}))
|
||||||
|
const { showStopSpeakingButton, stopSpeakingFromChat } = useStopSpeakingButton()
|
||||||
|
|
||||||
const latestImageEntries = computed(() => {
|
const latestImageEntries = computed(() => {
|
||||||
if (!activeCardId.value)
|
if (!activeCardId.value)
|
||||||
@@ -322,6 +324,24 @@ async function handleRetryMessage(index: number) {
|
|||||||
</DropdownMenuPortal>
|
</DropdownMenuPortal>
|
||||||
</DropdownMenuRoot>
|
</DropdownMenuRoot>
|
||||||
|
|
||||||
|
<button
|
||||||
|
v-if="showStopSpeakingButton"
|
||||||
|
data-testid="stop-speaking-button"
|
||||||
|
:class="[
|
||||||
|
'max-h-[10lh] min-h-[1lh]',
|
||||||
|
]"
|
||||||
|
bg="neutral-100 dark:neutral-800"
|
||||||
|
text="lg neutral-500 dark:neutral-400"
|
||||||
|
hover:text="primary-500 dark:primary-400"
|
||||||
|
flex items-center justify-center rounded-md p-2 outline-none
|
||||||
|
transition-colors transition-transform active:scale-95
|
||||||
|
title="Stop speaking"
|
||||||
|
aria-label="Stop speaking"
|
||||||
|
@click="stopSpeakingFromChat"
|
||||||
|
>
|
||||||
|
<div class="i-solar:stop-circle-bold-duotone" />
|
||||||
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
:class="[
|
:class="[
|
||||||
'max-h-[10lh] min-h-[1lh]',
|
'max-h-[10lh] min-h-[1lh]',
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import IndicatorMicVolume from '../Widgets/IndicatorMicVolume.vue'
|
|||||||
import ActionAbout from './InteractiveArea/Actions/About.vue'
|
import ActionAbout from './InteractiveArea/Actions/About.vue'
|
||||||
|
|
||||||
import { useTranscriptions } from '../../composables/use-transcriptions'
|
import { useTranscriptions } from '../../composables/use-transcriptions'
|
||||||
|
import { useStopSpeakingButton } from '../../composables/useStopSpeakingButton'
|
||||||
import { BackgroundDialogPicker } from '../Backgrounds'
|
import { BackgroundDialogPicker } from '../Backgrounds'
|
||||||
|
|
||||||
const { isDark, toggleDark } = useTheme()
|
const { isDark, toggleDark } = useTheme()
|
||||||
@@ -76,6 +77,7 @@ const { isListening, startStreamingTranscription, stopStreamingTranscription } =
|
|||||||
isStageTamagotchi,
|
isStageTamagotchi,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
const { showStopSpeakingButton, stopSpeakingFromChat } = useStopSpeakingButton()
|
||||||
const toggleTranscription = () => isListening.value ? stopStreamingTranscription() : startStreamingTranscription()
|
const toggleTranscription = () => isListening.value ? stopStreamingTranscription() : startStreamingTranscription()
|
||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
@@ -253,6 +255,19 @@ onMounted(() => {
|
|||||||
@compositionstart="isComposing = true"
|
@compositionstart="isComposing = true"
|
||||||
@compositionend="isComposing = false"
|
@compositionend="isComposing = false"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
v-if="showStopSpeakingButton"
|
||||||
|
data-testid="stop-speaking-button"
|
||||||
|
w="[calc(1lh+4px+4px)]" h="[calc(1lh+4px+4px)]" aspect-square flex items-center self-end justify-center rounded-full outline-none backdrop-blur-md
|
||||||
|
text="neutral-500 hover:primary-600 dark:neutral-900 dark:hover:primary-700"
|
||||||
|
bg="primary-50/80 dark:neutral-100/80 hover:neutral-50"
|
||||||
|
transition="all duration-250 ease-in-out"
|
||||||
|
title="Stop speaking"
|
||||||
|
aria-label="Stop speaking"
|
||||||
|
@click="stopSpeakingFromChat"
|
||||||
|
>
|
||||||
|
<div i-solar:stop-circle-bold-duotone />
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="messageInput.trim() || isComposing"
|
v-if="messageInput.trim() || isComposing"
|
||||||
w="[calc(1lh+4px+4px)]" h="[calc(1lh+4px+4px)]" aspect-square flex items-center self-end justify-center rounded-full outline-none backdrop-blur-md
|
w="[calc(1lh+4px+4px)]" h="[calc(1lh+4px+4px)]" aspect-square flex items-center self-end justify-center rounded-full outline-none backdrop-blur-md
|
||||||
|
|||||||
Reference in New Issue
Block a user