Skip to content

Commit

Permalink
feat(editor): use speech recognition tool
Browse files Browse the repository at this point in the history
  • Loading branch information
Novout committed Mar 24, 2022
1 parent 9acefb8 commit 05874eb
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
@keypress.enter.prevent="onEnter"
@keydown="onKeyboard"
@click="onClick"
@paste="entity.base().onPaste(props.entity, data, $event, input)"
@paste="entity.base().onPaste(props.entity, data, $event, input as HTMLInputElement)"
@copy="raw.v2().copy()"
v-html="raw.v2().purge().editor(props.entity)"
/>
Expand Down Expand Up @@ -72,6 +72,7 @@
import { useUtils } from '@/use/utils'
import { useFocus } from '@vueuse/core'
import { useFormat } from '@/use/format'
import { useProject } from '@/use/project'
const props = defineProps({
entity: {
Expand All @@ -95,6 +96,7 @@
const raw = useRaw()
const plugin = usePlugin()
const format = useFormat()
const project = useProject()
const hover = ref<boolean>(false)
const focus = ref<boolean>(false)
Expand All @@ -104,7 +106,7 @@
const press = ref<boolean>(false)
const data = ref<string>('')
const input = ref<any>(null)
const input = ref<HTMLInputElement | null>(null)
const keyboard = ref<boolean>(false)
const style = computed(() => EDITOR.styles.show)
Expand All @@ -115,13 +117,25 @@
const target = computed(
() => EDITOR.actives.entity.index === _index.value && ABSOLUTE.entity.menu
)
const { focused } = useFocus({ target: input } as any)
const { focused } = useFocus(input)
watch(props.entity, () => {
// new entity properties
onReset()
})
watch(focused, (_focused) => {
if (_focused && !entity.utils().isFixed(_index.value)) {
EDITOR.actives.entity.index = _index.value
if (ABSOLUTE.tools.speechRecognition) {
project.utils().resetAllVisual()
CONTEXT.entities[_index.value].visual.warning = true
}
}
})
watch(hover, async (_hover) => {
emitter.emit('entity-hover', _hover)
Expand Down Expand Up @@ -165,6 +179,8 @@
const setData = (val: string) => {
const _input = input.value as HTMLDivElement
if (val === null) return
data.value = val
_input.innerHTML = val
}
Expand Down Expand Up @@ -478,6 +494,18 @@
}
}
})
emitter.on('entity-speech-recognition', async ({ id, result }) => {
if (CONTEXT.entities[id] === props.entity && input.value) {
const _data = data.value
setData(result)
await nextTick
if (result !== _data) onUpdateContent()
}
})
})
const onUpdateContent = async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
<template>
<div
id="tools-speech-recognition"
ref="spc"
v-motion
:initial="{ opacity: 0 }"
:enter="{ opacity: 1 }"
:delay="0"
class="flex bg-theme-background-3 items-center justify-center absolute transform -translate-y-1/2 top-1/2 z-20 right-0 transform m-5 p-2 shadow-lg"
class="flex bg-theme-background-3 items-center justify-center absolute z-50 right-0 transform m-5 p-2 shadow-lg w-60 absolute transform -translate-y-1/2 top-1/2"
>
<div
class="bg-theme-text-1 transition-colors hover:bg-theme-background-opacity-1 rounded-full shadow-lg"
>
<HeroIcon class="p-2 md:p-3">
<HeroIcon
class="p-2 md:p-3 text-theme-background-1"
@click.prevent.stop="onMicrophone"
>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
Expand All @@ -34,15 +38,12 @@
<div class="flex items-end justify-between flex-col ml-5">
<div class="flex items-center justify-between w-full mb-1">
<div
class="w-1 h-1 p-3 rounded-full"
class="w-1 h-1 p-3 rounded-full shadow-lg"
:style="{
backgroundColor: speech.isListening.value ? '#E62B1C' : '#FFD935',
backgroundColor: isListening ? '#E62B1C' : '#FFD935',
}"
/>
<HeroIcon
class="wb-icon"
@click.prevent.stop="ABSOLUTE.tools.speechRecognition = false"
>
<HeroIcon class="wb-icon" @click.prevent.stop="onClose">
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
Expand All @@ -59,30 +60,91 @@
</svg>
</HeroIcon>
</div>
<InputSelect v-model="iso" class="wb-text" :arr="VueI18nAllISO" />
<InputSelect v-model="lang" class="wb-text" :arr="VueI18nAllISO" />
</div>
</div>
</template>

<script lang="ts" setup>
import { useAbsoluteStore } from '@/store/absolute'
import { useSpeechRecognition } from '@vueuse/core'
import { useContextStore } from '@/store/context'
import { useEditorStore } from '@/store/editor'
import useEmitter from '@/use/emitter'
import { useProject } from '@/use/project'
import {
tryOnMounted,
tryOnUnmounted,
useSpeechRecognition,
} from '@vueuse/core'
import { VueI18nAllISO } from 'better-write-localisation'
import { ref, watch } from 'vue'
import { computed, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useToast } from 'vue-toastification'
const ABSOLUTE = useAbsoluteStore()
const CONTEXT = useContextStore()
const EDITOR = useEditorStore()
const iso = ref<string>(VueI18nAllISO[0])
const spc = ref<HTMLElement | null>(null)
const lang = ref<string>(VueI18nAllISO[0])
let speech = useSpeechRecognition({
lang: iso.value,
const emitter = useEmitter()
const toast = useToast()
const { t } = useI18n()
const project = useProject()
const speech = useSpeechRecognition({
lang,
continuous: true,
})
const id = computed(() => EDITOR.actives.entity.index)
const { isListening, isSupported, stop, result, start } = speech
const onClose = () => {
onStop()
ABSOLUTE.tools.speechRecognition = false
}
const onStop = () => {
stop()
project.utils().resetAllVisual()
}
const onMicrophone = () => {
if (!isSupported) {
toast(t('toast.generics.supported'))
return
}
if (!isListening.value) {
result.value = ''
start()
project.utils().resetAllVisual()
CONTEXT.entities[id.value].visual.warning = true
return
}
onStop()
}
tryOnMounted(() => {
CONTEXT.entities[id.value].visual.warning = true
})
tryOnUnmounted(() => {
project.utils().resetAllVisual()
})
watch(lang, () => {
if (isListening.value) stop()
})
watch(iso, (_iso) => {
speech = useSpeechRecognition({
lang: _iso,
continuous: true,
})
watch(result, (_result) => {
emitter.emit('entity-speech-recognition', { id: id.value, result: _result })
})
</script>
9 changes: 9 additions & 0 deletions packages/better-write-app/src/use/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,14 @@ export const useProject = () => {
}

const utils = () => {
const resetAllVisual = () => {
CONTEXT.entities.forEach((entity) => {
entity.visual.info = false
entity.visual.warning = false
entity.visual.error = false
})
}

const isValidType = (val: Entity) => {
return !entity.utils().isFixedRaw(val.raw) && val.type !== 'image'
}
Expand Down Expand Up @@ -392,6 +400,7 @@ export const useProject = () => {
}

return {
resetAllVisual,
isValidType,
getWords,
getChapterLetters,
Expand Down
1 change: 1 addition & 0 deletions packages/better-write-localisation/src/en-US/toast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export default {
generics: {
load: 'Wait a moment...',
error: 'An unexpected error occurred :(',
supported: 'Your browser does not support this feature :(',
},
pdf: {
error:
Expand Down
1 change: 1 addition & 0 deletions packages/better-write-localisation/src/pt-BR/toast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export default {
generics: {
load: 'Aguarde um momento...',
error: 'Um erro inesperado ocorreu :(',
supported: 'O seu navegador não suporta este recurso :(',
},
pdf: {
error:
Expand Down
1 change: 1 addition & 0 deletions packages/better-write-types/src/types/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type VueEmitterName =
| 'entity-edit-save'
| 'entity-hover'
| 'entity-external-comment-save'
| 'entity-speech-recognition'
| 'project-creative-drafts-set-info'
| 'project-save'
| 'pdf-preview-exists'
Expand Down

0 comments on commit 05874eb

Please sign in to comment.