Skip to content

Commit

Permalink
feat: improve chat , ui and prompt
Browse files Browse the repository at this point in the history
  • Loading branch information
liou666 committed Apr 1, 2023
1 parent b815b8e commit 8ef806d
Show file tree
Hide file tree
Showing 17 changed files with 141 additions and 71 deletions.
Binary file added public/avatars/de.webp
Binary file not shown.
Binary file added public/avatars/en.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/avatars/en1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/avatars/fr.webp
Binary file not shown.
Binary file added public/avatars/fr1.webp
Binary file not shown.
Binary file added public/avatars/jp.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/avatars/jp1.webp
Binary file not shown.
Binary file added public/avatars/ko1.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/avatars/self.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed public/electron-vite-vue.gif
Binary file not shown.
99 changes: 55 additions & 44 deletions src/components/Content.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,23 @@ import { generateText } from '@/server/api'
import { getOpenAzureKey, getOpenAzureRegion, getOpenKey, getOpenProxy, verifyOpenKey } from '@/utils'
import { useConversationStore } from '@/stores'
// const systemMessage: SystemMessage = {
// role: 'system',
// content: 'I want you to act as a spoken English teacher and improver. I will speak to you in English and you will reply to me in English to practice my spoken English. I want you to keep your reply neat, limiting the reply to 100 words. I want you to strictly correct my grammar mistakes, typos, and factual errors. I want you to ask me a question in your reply. Now let\'s start practicing, you could ask me a question first. Remember, I want you to strictly correct my grammar mistakes, typos, and factual errors.',
// }
// hooks
const store = useConversationStore()
const { el, scrollToBottom } = useScroll()
const {
language,
voiceName,
isRecognizing,
startRecognizeSpeech,
stopRecognizeSpeech,
recognizeSpeech,
textToSpeak,
} = useSpeechService(getOpenAzureKey(), getOpenAzureRegion())
} = useSpeechService(getOpenAzureKey(), getOpenAzureRegion(), store.allLanguage as any)
// states
const message = ref('') // input message
const text = ref('') // current select message
const loading = ref(false)
const messageLength = computed(() => store.currentChatMessages.length)
const chatMessages = computed(() => store.currentChatMessages.slice(1))
const currentKey = computed(() => store.currentKey)
// effects
Expand All @@ -35,16 +31,6 @@ watch(currentKey, () => {
})
// methods
const roleClass = (role: string) => {
switch (role) {
case 'user':
return 'bg-gradient-to-br from-green-400 to-blue-300 rounded-full p-4'
case 'assistant':
return 'bg-gradient-to-br from-blue-300 to-red-600 rounded-full p-4'
case 'system':
return 'bg-gray-500'
}
}
const onSubmit = async () => {
const key = getOpenKey()
if (!verifyOpenKey(key)) return alert('请输入正确的API-KEY')
Expand All @@ -55,23 +41,26 @@ const onSubmit = async () => {
{ content: message.value, role: 'user' },
])
message.value = ''
loading.value = true
store.changeLoading(true)
try {
const res = await generateText(store.currentChatMessages, key!, getOpenProxy())
if (res.error) {
alert(res.error?.message)
return loading.value = false
return store.changeLoading(false)
}
const content = res.choices[0].message.content
store.changeConversations([
...store.currentChatMessages,
{
content: res.choices[0].message.content, role: 'assistant',
content, role: 'assistant',
},
])
loading.value = false
speak(content)
store.changeLoading(false)
}
catch (error) {
loading.value = false
store.changeLoading(false)
}
}
Expand All @@ -81,49 +70,70 @@ function speak(content: string) {
}
const recognize = async () => {
if (isRecognizing.value) {
const result = await stopRecognizeSpeech()
try {
isRecognizing.value = true
store.changeLoading(true)
const result = await recognizeSpeech()
isRecognizing.value = false
store.changeLoading(false)
message.value = result
onSubmit()
}
else {
startRecognizeSpeech()
catch (error) {
isRecognizing.value = false
store.changeLoading(true)
alert(error)
}
}
const translate = (text: string) => {
// todo
console.log(text)
}
</script>

<template>
<div flex flex-col p-2 rounded-md bg-white dark="bg-#1e1e1e">
<div ref="el" class="hide-scrollbar flex-1 overflow-auto">
<div
v-for="item, i in store.currentChatMessages"
:key="i"
center-y odd:flex-row-reverse
>
<div :class="roleClass(item.role)" />
<div relative>
<div mx-2>
<p px-2 py-1 chat-box>
<template v-if="chatMessages.length">
<div
v-for="item, i in chatMessages"
:key="i"
center-y odd:flex-row-reverse
>
<div class="w-10">
<img w-full rounded-full :src="item.role === 'user' ? '/avatars/self.png' : store.currentAvatar" alt="">
</div>

<div style="flex-basis:fit-content" mx-2>
<p mb-1 p-2 chat-box>
{{ item.content }}
</p>
<p v-if="item.role === 'assistant'" flex>
<span class="bg-gray-100/20 rounded-lg w-4 py-1 px-3 center" @click="speak(item.content)">
<p v-if="item.role === 'assistant'" mt-2 flex>
<span class="chat-btn" @click="speak(item.content)">
<i icon-btn rotate-90 i-ic:sharp-wifi />
</span>
<span
class="bg-gray-100/20 ml-1 cursor-pointer rounded-lg w-4 py-1 px-3 center"
class="chat-btn ml-1"
@click="translate(item.content)"
>
<i icon-btn i-carbon:ibm-watson-language-translator />
</span>
</p>
</div>
</div>
</div>
</template>
<template v-else>
<div font-italic text-gray-500 center h-full>
Haven't started the conversation yet, let's start now
</div>
</template>
</div>

<div class="flex h-10 w-[-webkit-fill-available] mt-1">
<Button
mr-1
:disabled="isRecognizing || store.loading"
@click="recognize()"
>
<i i-carbon:microphone />
Expand All @@ -133,25 +143,26 @@ const recognize = async () => {
isRecognizing
</div>
<input
v-else-if="!loading "
v-else-if="!store.loading"
v-model="message"
type="text"
text-p
placeholder="Type your message here..."
input-box p-3 flex-1
>
<div v-else class="loading-btn">
AI Is Thinking...
</div>
<Button
:disabled="loading"
:disabled="store.loading"
mx-1
@click="onSubmit"
>
<i i-carbon:send-alt />
</Button>
<Button
:disabled="loading"
@click="store.changeConversations([])"
:disabled="store.loading"
@click="store.cleanConversations()"
>
<i i-carbon:trash-can />
</Button>
Expand Down
15 changes: 10 additions & 5 deletions src/components/Header.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
<script setup lang="ts">
import { shell } from 'electron'
</script>

<template>
<header class="bg-#ecf7fd dark:bg-#1f262a">
<h3 text-2xl>
🤖️ <span class="text-gradient ">Polyglot</span>
<header class="bg-#fff dark:bg-#1f262a">
<h3 center-y text-2xl>
<!-- <img src="" alt=""> -->
<div m-2>
🤖️
</div>
<span class="text-gradient ">Polyglot</span>
</h3>
<a class="center-y">
<div class="center-y" @click="shell.openExternal('https://github.com/liou666')">
<i w-6 h-6 icon-btn i-carbon:logo-github />
</a>
</div>
</header>
</template>

Expand Down
17 changes: 11 additions & 6 deletions src/components/Nav.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script setup lang="ts">
import Card from './widgets/Card.vue'
import type { Key } from '@/stores'
import { useConversationStore } from '@/stores'
import InputKit from '@/components/widgets/InputKit.vue'
import { OPEN_KEY, OPEN_PROXY } from '@/constant'
Expand All @@ -8,24 +9,28 @@ const openKey = useLocalStorage(OPEN_KEY, '')
const proxy = useLocalStorage(OPEN_PROXY, '')
const isDark = useDark()
const toggleDark = useToggle(isDark)
const store = useConversationStore()
const handleCardClick = (key: Key) => {
if (store.loading)
return alert('Please wait for the current operation to complete')
store.changeCurrentKey(key)
}
</script>

<template>
<nav class="rounded-md bg-#eaf7fe dark:bg-#1d262a h-[calc(100vh-78px)]">
<nav class="rounded-md bg-#fff dark:bg-#1d262a h-[calc(100vh-78px)]">
<div h-full flex flex-col p-1>
<div class="hide-scrollbar overflow-y-auto flex-1 ">
<Card
v-for="item, i in store.allPeople"
:key="i"
:avater-url="item.avatar"
:desc="item.desc"
:name="item.key"
:active="store.currentKey === item.key"
@click="store.changeCurrentKey(item.key)"
>
<div rounded-full w-8 h-8 bg-red />
</Card>
@click="handleCardClick(item.key)"
/>
</div>
<div w-full h-0.2 bg-gray-200 dark:bg-gray-700 />
<div py-1>
Expand Down
13 changes: 8 additions & 5 deletions src/components/widgets/Card.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
<script setup lang="ts">
const { name, desc, active } = defineProps<{
const { name, desc, active, avaterUrl } = defineProps<{
name: string
desc: string
active: boolean
avaterUrl: string
}>()
</script>

<template>
<div
p-2 cursor-pointer center-y
m-2 rounded duration-300
m-2 rounded duration-300 shadow-sm
dark="bg-gray-700/80 hover:bg-gray-500/80"
bg="gray-500/10 hover:gray-500/20"
:class="{ 'bg-gray-500/80!': active }"
bg="gray-400/10 hover:gray-500/20"
:class="{ 'bg-gray-500/20! dark:bg-gray-500/80! shadow-lg': active }"
>
<div mr-2>
<slot />
<div w-10 h-10>
<img w-full rounded-full :src="avaterUrl" alt="avater">
</div>
</div>
<div>
<div text-lg font-500>
Expand Down
Loading

0 comments on commit 8ef806d

Please sign in to comment.