Skip to content

Commit

Permalink
Merge pull request #647 from nature-heart-software/dev
Browse files Browse the repository at this point in the history
release
  • Loading branch information
Wurielle authored May 21, 2023
2 parents 6b73952 + b0d1ffa commit c6dcb08
Show file tree
Hide file tree
Showing 33 changed files with 723 additions and 316 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ instead of
fullscreen.

You can press `Ctrl` + `Enter` (or `Esc` if the text input is focused) to close the window.
Pressing `Enter` while the text input is focused and no text is present will also close the window.

## Support

Expand All @@ -73,14 +74,14 @@ the [Available engines](#available-engines) section.
> **Note:** The Izabela engine is only available to "Supporter" members or above
> on [Ko-fi](https://ko-fi.com/woowee/tiers).
If you prefer configuring your credentials yourself, you can do so by following the guides in the [Guides](#guides)
section (coming soon).
If you prefer configuring your credentials yourself, you can do so by following the guides on the [Wiki](https://github.com/nature-heart-software/izabela/wiki)
page.

## Available engines

Here's a list of all the text-to-speech engines that are supported in Izabela:

| Engine | Included with Universal Credentials | Sample | Credits |
| Engine | Included with Universal credentials | Sample | Credits |
| -------------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| Izabela (multiple engines) | Yes | [Download](https://github.com/nature-heart-software/izabela/blob/dev/assets/izabela-sample.mp3?raw=true) | https://github.com/Weilbyte/tiktok-tts |
| Amazon Polly | Yes | [Download](https://github.com/nature-heart-software/izabela/blob/dev/assets/amazon-polly-sample.mp3?raw=true) | https://aws.amazon.com/polly/ |
Expand All @@ -94,8 +95,7 @@ Here's a list of all the text-to-speech engines that are supported in Izabela:

## Guides

- [How to use Izabela as audio input](https://github.com/nature-heart-software/izabela/blob/dev/guides/onboarding/how-to-use-as-audio-input.md)
- [How to intercept websocket events](https://github.com/nature-heart-software/izabela/blob/dev/guides/onboarding/how-to-intercept-websocket-events.md)
Find guides and API documentation on the [Wiki](https://github.com/nature-heart-software/izabela/wiki) page.

## Resources

Expand Down
8 changes: 8 additions & 0 deletions apps/app/src/electron/events/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,11 @@ export const onIPCProcessError = (callback: (error: Error, process: string) => a
)
})
}

export const emitIPCCancelCurrentMessage = () => {
ipcMain.sendTo('speech-worker', 'cancel-current-message')
}

export const emitIPCCancelAllMessages = () => {
ipcMain.sendTo('speech-worker', 'cancel-all-messages')
}
12 changes: 12 additions & 0 deletions apps/app/src/electron/events/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,15 @@ export const onIPCSay = (callback: (payload: IPCSayPayload) => any) => {
export const emitIPCSay = (payload: IPCSayPayload) => {
ipc.sendTo('speech-worker', 'say', payload)
}

export const onIPCCancelCurrentMessage = (callback: () => any) => {
processes.forEach((process) => {
ipc.on(process, 'cancel-current-message', callback)
})
}

export const onIPCCancelAllMessages = (callback: () => void) => {
processes.forEach((process) => {
ipc.on(process, 'cancel-all-messages', callback)
})
}
7 changes: 7 additions & 0 deletions apps/app/src/electron/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import path from 'path'
import { screen } from 'electron'
import { minBy } from 'lodash'

export const env = process.env.NODE_ENV
export const EXTERNALS_DIR =
env === 'development' ? path.join(ROOT_DIR, '/resources') : process.resourcesPath

export const getTopLeftWindow = () => {
const displays = screen.getAllDisplays()
return minBy(displays, (display) => display.bounds.y)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<NvGoogleCloudCredentialsFormPart>
Izabela uses Google Cloud Speech for speech recognition which requires a
<a
href="https://developers.google.com/workspace/guides/create-credentials#service-account"
href="https://github.com/nature-heart-software/izabela/wiki/How-to-get-Google-Cloud-service-account-credentials"
target="_blank"
>Google Cloud service account credentials</a
>
Expand All @@ -15,7 +15,9 @@
<NvGroup align="start" justify="apart" no-wrap spacing="5">
<NvStack>
<NvText type="label">Enable speech-to-text-to-speech</NvText>
<NvText type="caption">This might prevent your device from going to sleep</NvText>
<NvText type="caption"
><strong>NOTE:</strong> This might prevent your device from going to sleep
</NvText>
</NvStack>
<NvSwitch
:modelValue="settingsStore.enableSTTTS"
Expand Down
41 changes: 35 additions & 6 deletions apps/app/src/features/dictionary/store/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { computed, ref } from 'vue'

export const useDictionaryStore = defineStore(
'dictionary',
() => {
const enableDictionary = ref(true)
const matchExactWord = ref(true)
const caseSensitive = ref(false)
const definitions = ref<[string, string][]>([
['wyd', 'what are you doing'],
['hbu', 'how about you'],
Expand Down Expand Up @@ -59,16 +62,42 @@ export const useDictionaryStore = defineStore(
['ysk', 'you should know'],
['yt', 'YouTube'],
])

const filteredDefinitions = computed(() =>
definitions.value.filter((def) => Array.isArray(def)),
)
const translateText = (text: string) => {
let newText = text
definitions.value.forEach(([word, definition]) => {
newText = newText.replace(new RegExp(`(\\b${word}\\b)`, 'gi'), definition)
if (!enableDictionary.value) return text
const words = text.split(' ')
const flags = caseSensitive.value ? 'g' : 'gi'
filteredDefinitions.value.forEach(([word, definition]) => {
const escapedWord = word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
const exactMatchRegexExpression = `(?<![\\w:])${escapedWord}(?![\\w:])`
const boundaryMatchRegexExpression = `^${escapedWord}|${escapedWord}$`
const exactMatchRegex = new RegExp(exactMatchRegexExpression, flags)
const boundaryMatchRegex = new RegExp(boundaryMatchRegexExpression, flags)
words.forEach((currentWord, i) => {
const evaluations = [
exactMatchRegex.test(currentWord),
boundaryMatchRegex.test(currentWord),
]
if (word && (evaluations[0] || (!matchExactWord.value && evaluations[1]))) {
words[i] = currentWord.replace(
new RegExp(
evaluations[0] ? exactMatchRegexExpression : boundaryMatchRegexExpression,
flags,
),
definition,
)
}
})
})
return newText
return words.join(' ')
}

return {
enableDictionary,
matchExactWord,
caseSensitive,
definitions,
translateText,
updateDefinition: (index: number, definition: [string, string]) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<template>
<NvSelect
ref="select"
:autocompleteWidth="width"
:modelValue="settingsStore.messageMode"
:options="options"
@update:modelValue="(value) => settingsStore.$patch({ messageMode: value })"
/>
</template>
<script lang="ts" setup>
import { NvSelect } from '@packages/ui'
import { useElementSize } from '@vueuse/core'
import { computed, ref } from 'vue'
import { useSettingsStore } from '@/features/settings/store'
const settingsStore = useSettingsStore()
const options = computed(() => [
{
label: 'Sentence',
value: 'sentence',
},
{
label: 'Word',
value: 'word',
},
])
const select = ref()
const { width } = useElementSize(select)
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<NvGroup align="start" justify="apart" no-wrap>
<NvStack>
<NvGroup>
<NvText type="label">Google Cloud Credentials</NvText>
<NvText> (required) </NvText>
<NvText type="label">Google Cloud credentials</NvText>
<NvText> (required)</NvText>
</NvGroup>
<NvText>
<slot />
Expand Down
66 changes: 61 additions & 5 deletions apps/app/src/features/settings/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export const useSettingsStore = defineStore(
const customTextTranslationApiKey = ref('')
const customTextTranslationFrom = ref('')
const customTextTranslationTo = ref('')
// const enableBackgroundDim = ref(true)
const backgroundDimOpacity = ref(50)
const keybindings = ref<Record<string, Key[]>>({
recordAudio: [
{
Expand Down Expand Up @@ -105,12 +107,64 @@ export const useSettingsStore = defineStore(
metaKey: false,
},
{
key: 'Space',
code: 'Space',
keyCode: 32,
rawCode: 32,
key: 'ControlRight',
code: 'ControlRight',
keyCode: 17,
rawCode: 163,
charCode: 0,
which: 17,
shiftKey: false,
altKey: false,
ctrlKey: true,
metaKey: false,
},
],
cancelCurrentMessage: [
{
key: 'Control',
code: 'ControlLeft',
keyCode: 17,
rawCode: 162,
charCode: 0,
which: 17,
shiftKey: false,
altKey: false,
ctrlKey: true,
metaKey: false,
},
{
key: 'Delete',
code: 'Delete',
keyCode: 46,
rawCode: 46,
charCode: 0,
which: 46,
shiftKey: false,
altKey: false,
ctrlKey: true,
metaKey: false,
},
],
cancelAllMessages: [
{
key: 'Control',
code: 'ControlLeft',
keyCode: 17,
rawCode: 162,
charCode: 0,
which: 17,
shiftKey: false,
altKey: false,
ctrlKey: true,
metaKey: false,
},
{
key: 'Backspace',
code: 'Backspace',
keyCode: 8,
rawCode: 8,
charCode: 0,
which: 32,
which: 8,
shiftKey: false,
altKey: false,
ctrlKey: true,
Expand All @@ -119,6 +173,8 @@ export const useSettingsStore = defineStore(
],
})
return {
// enableBackgroundDim,
backgroundDimOpacity,
preferredSavDir,
playSpeechOnDefaultPlaybackDevice,
audioOutputs,
Expand Down
6 changes: 3 additions & 3 deletions apps/app/src/modules/electron-display/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,22 @@ export default () =>
const settingsStore = useSettingsStore()
electronMessengerWindow.isReady().then(() => {
electronMessengerWindow.setDisplay(settingsStore.display)
electronSpeechWorkerWindow.setDisplay(settingsStore.display)
electronSpeechWorkerWindow.setDisplay()
})

watch(
() => settingsStore.display,
() => {
electronMessengerWindow.setDisplay(settingsStore.display)
electronSpeechWorkerWindow.setDisplay(settingsStore.display)
electronSpeechWorkerWindow.setDisplay()
},
)

const screenEvents = ['display-added', 'display-removed', 'display-metrics-changed'] as const
screenEvents.forEach((event) => {
screen.on(event as any, () => {
electronMessengerWindow.setDisplay(settingsStore.display)
electronSpeechWorkerWindow.setDisplay(settingsStore.display)
electronSpeechWorkerWindow.setDisplay()
})
})
})
24 changes: 24 additions & 0 deletions apps/app/src/modules/electron-keybinding/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { purify } from '@packages/toolbox'
import { debounce } from 'lodash'
import { IGlobalKeyDownMap, IGlobalKeyEvent, IGlobalKeyListener } from 'node-global-key-listener'
import { gkl, handleShortcut, keybindingTriggered } from '@/modules/electron-keybinding/utils'
import { emitIPCCancelAllMessages, emitIPCCancelCurrentMessage } from '@/electron/events/main'

export default () =>
app.whenReady().then(async () => {
Expand All @@ -18,6 +19,8 @@ export default () =>
const multiKeysKeybindings = {
toggleMessengerWindow: handleShortcut(() => electronMessengerWindow.toggleWindow('keyboard')),
toggleMessengerWindowAlt: handleShortcut(() => electronMessengerWindow.toggleWindow('mouse')),
cancelCurrentMessage: handleShortcut(() => emitIPCCancelCurrentMessage()),
cancelAllMessages: handleShortcut(() => emitIPCCancelAllMessages()),
}
const registeredShortcuts: Record<string, string> = {}
const registeredCallbacks: Record<
Expand All @@ -32,6 +35,23 @@ export default () =>
}
}
}

const cancelCurrentMessageListener: IGlobalKeyListener = (e, down) => {
if (e.state === 'DOWN') {
if (keybindingTriggered(settingsStore.keybindings.cancelCurrentMessage)) {
multiKeysKeybindings.cancelCurrentMessage()
}
}
}

const cancelAllMessagesListener: IGlobalKeyListener = (e, down) => {
if (e.state === 'DOWN') {
if (keybindingTriggered(settingsStore.keybindings.cancelAllMessages)) {
multiKeysKeybindings.cancelAllMessages()
}
}
}

const unregisterAllShortcuts = () => {
gkl?.removeListener(toggleMessengerWindowListener)
Object.keys(registeredShortcuts).forEach((key) => {
Expand All @@ -40,6 +60,8 @@ export default () =>
})
Object.keys(registeredCallbacks).forEach((key) => {
gkl?.removeListener(registeredCallbacks[key])
gkl?.removeListener(cancelCurrentMessageListener)
gkl?.removeListener(cancelAllMessagesListener)
delete registeredShortcuts[key]
})
}
Expand All @@ -50,6 +72,8 @@ export default () =>
.join('+')

gkl?.addListener(toggleMessengerWindowListener)
gkl?.addListener(cancelCurrentMessageListener)
gkl?.addListener(cancelAllMessagesListener)
globalShortcut.register(keybinding, multiKeysKeybindings.toggleMessengerWindow)
registeredShortcuts.toggleMessengerWindow = keybinding
}
Expand Down
Loading

0 comments on commit c6dcb08

Please sign in to comment.