feat(stage-tamagotchi): stage-web like ControlsIsland
This commit is contained in:
@@ -8,7 +8,7 @@ import { useProvidersStore } from '@proj-airi/stage-ui/stores/providers'
|
||||
import { useSettingsAudioDevice } from '@proj-airi/stage-ui/stores/settings'
|
||||
import { BasicTextarea } from '@proj-airi/ui'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import TamagotchiChatHistory from './ChatHistory.vue'
|
||||
@@ -17,7 +17,7 @@ const messageInput = ref('')
|
||||
const listening = ref(false)
|
||||
const attachments = ref<{ type: 'image', data: string, mimeType: string, url: string }[]>([])
|
||||
|
||||
// const { askPermission } = useSettingsAudioDevice()
|
||||
const { askPermission } = useSettingsAudioDevice()
|
||||
const { enabled, selectedAudioInput } = storeToRefs(useSettingsAudioDevice())
|
||||
const { send, onAfterMessageComposed, discoverToolsCompatibility } = useChatStore()
|
||||
const { messages } = storeToRefs(useChatStore())
|
||||
@@ -116,21 +116,16 @@ function handleTranscription(_buffer: Float32Array) {
|
||||
alert('Transcription is not implemented yet')
|
||||
}
|
||||
|
||||
// async function handleAudioInputChange(event: Event) {
|
||||
// const target = event.target as HTMLSelectElement
|
||||
// const found = audioInputs.value.find(d => d.deviceId === target.value)
|
||||
// if (!found) {
|
||||
// selectedAudioDevice.value = undefined
|
||||
// return
|
||||
// }
|
||||
|
||||
// selectedAudioDevice.value = found
|
||||
// }
|
||||
|
||||
watch(enabled, async (value) => {
|
||||
if (value === false) {
|
||||
destroy()
|
||||
}
|
||||
else {
|
||||
await askPermission()
|
||||
start()
|
||||
}
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
|
||||
watch([activeProvider, activeModel], async () => {
|
||||
@@ -144,10 +139,6 @@ onAfterMessageComposed(async () => {
|
||||
attachments.value.forEach(att => URL.revokeObjectURL(att.url))
|
||||
attachments.value = []
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
start()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<button
|
||||
:class="[
|
||||
'border-2 border-solid border-neutral-200/60 dark:border-neutral-800/10',
|
||||
'bg-neutral-50/80 dark:bg-neutral-800/70',
|
||||
'w-fit flex items-center self-end justify-center p-2',
|
||||
'rounded-xl backdrop-blur-md',
|
||||
'transition-all hover:transition-none transition-duration-300 transition-ease-out',
|
||||
]"
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
@@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import { HearingConfigDialog } from '@proj-airi/stage-ui/components'
|
||||
import { useSettingsAudioDevice } from '@proj-airi/stage-ui/stores/settings'
|
||||
import { useDark, useToggle } from '@vueuse/core'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
import ControlButton from './ControlButton.vue'
|
||||
|
||||
const isDark = useDark({ disableTransition: false })
|
||||
const toggleDark = useToggle(isDark)
|
||||
|
||||
const settingsAudioDeviceStore = useSettingsAudioDevice()
|
||||
const { enabled } = storeToRefs(settingsAudioDeviceStore)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div fixed bottom-2 right-2>
|
||||
<div flex flex-col gap-1>
|
||||
<HearingConfigDialog>
|
||||
<ControlButton>
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div v-if="enabled" i-ph:microphone size-5 text="neutral-800 dark:neutral-300" />
|
||||
<div v-else i-ph:microphone-slash size-5 text="neutral-800 dark:neutral-300" />
|
||||
</Transition>
|
||||
</ControlButton>
|
||||
</HearingConfigDialog>
|
||||
<ControlButton :class="['cursor-move', 'drag-region']">
|
||||
<div i-ph:arrows-out-cardinal size-5 text="neutral-800 dark:neutral-300" />
|
||||
</ControlButton>
|
||||
<ControlButton @click="() => toggleDark()">
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div v-if="isDark" i-solar:moon-outline size-5 text="neutral-800 dark:neutral-300" />
|
||||
<div v-else i-solar:sun-2-outline size-5 text="neutral-800 dark:neutral-300" />
|
||||
</Transition>
|
||||
</ControlButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -4,6 +4,7 @@ import { useLive2d } from '@proj-airi/stage-ui/stores/live2d'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
import ControlsIsland from '../components/Widgets/ControlsIsland/index.vue'
|
||||
import ResourceStatusIsland from '../components/Widgets/ResourceStatusIsland/index.vue'
|
||||
|
||||
import { useWindowStore } from '../stores/window'
|
||||
@@ -55,9 +56,9 @@ const modeIndicatorClass = computed(() => {
|
||||
flex="~ col"
|
||||
relative z-2 h-full overflow-hidden rounded-xl
|
||||
transition="opacity duration-500 ease-in-out"
|
||||
class="drag-region"
|
||||
>
|
||||
<div v-show="!isLoading" relative h-full w-full items-end gap-2 class="view">
|
||||
<ResourceStatusIsland ref="resourceStatusIslandRef" />
|
||||
<WidgetStage
|
||||
ref="widgetStageRef"
|
||||
v-model:state="componentStateStage"
|
||||
@@ -69,7 +70,7 @@ const modeIndicatorClass = computed(() => {
|
||||
:y-offset="positionInPercentageString.y"
|
||||
mb="<md:18"
|
||||
/>
|
||||
<ResourceStatusIsland ref="resourceStatusIslandRef" />
|
||||
<ControlsIsland />
|
||||
</div>
|
||||
<div v-show="isLoading" h-full w-full>
|
||||
<div class="absolute left-0 top-0 z-99 h-full w-full flex cursor-grab items-center justify-center overflow-hidden">
|
||||
|
||||
Reference in New Issue
Block a user