Add frequently used prompt function.
This commit is contained in:
@@ -65,7 +65,6 @@ const highlightCode = () => {
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
console.log('content changed', props.content)
|
||||
contentHtml.value = props.content ? marked(props.content) : ''
|
||||
if (props.content && props.content.endsWith('```')) {
|
||||
nextTick(() => {
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
<v-textarea
|
||||
v-model="message"
|
||||
:label="$t('writeAMessage')"
|
||||
:placeholder="$t('writeAMessage') + '...'"
|
||||
:placeholder="hint"
|
||||
rows="1"
|
||||
:auto-grow="autoGrow"
|
||||
:disabled="disabled"
|
||||
:loading="loading"
|
||||
:hint="hint"
|
||||
:hide-details="loading"
|
||||
:hide-details="true"
|
||||
append-inner-icon="send"
|
||||
@keyup.enter.exact="enterOnly"
|
||||
@click:appendInner="clickSendBtn"
|
||||
@@ -60,6 +59,9 @@ export default {
|
||||
}
|
||||
this.message = ""
|
||||
},
|
||||
usePrompt(prompt) {
|
||||
this.message = prompt
|
||||
},
|
||||
clickSendBtn () {
|
||||
this.send()
|
||||
},
|
||||
|
||||
224
components/Prompt.vue
Normal file
224
components/Prompt.vue
Normal file
@@ -0,0 +1,224 @@
|
||||
<script setup>
|
||||
const menu = ref(false)
|
||||
const prompts = ref([])
|
||||
const editingPrompt = ref(null)
|
||||
const newPrompt = ref('')
|
||||
const submittingNewPrompt = ref(false)
|
||||
const promptInputErrorMessage = ref('')
|
||||
const loadingPrompts = ref(false)
|
||||
const deletingPromptIndex = ref(null)
|
||||
|
||||
const props = defineProps({
|
||||
usePrompt: {
|
||||
type: Function,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const addPrompt = async () => {
|
||||
if (!newPrompt.value) {
|
||||
promptInputErrorMessage.value = 'Please enter a prompt'
|
||||
return
|
||||
}
|
||||
submittingNewPrompt.value = true
|
||||
const { data, error } = await useAuthFetch('/api/chat/prompts/', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
prompt: newPrompt.value
|
||||
})
|
||||
})
|
||||
if (!error.value) {
|
||||
prompts.value.push(data.value)
|
||||
newPrompt.value = ''
|
||||
}
|
||||
submittingNewPrompt.value = false
|
||||
}
|
||||
|
||||
const editPrompt = (index) => {
|
||||
editingPrompt.value = Object.assign({}, prompts.value[index])
|
||||
}
|
||||
|
||||
const updatePrompt = async (index) => {
|
||||
editingPrompt.value.updating = true
|
||||
const { data, error } = await useAuthFetch(`/api/chat/prompts/${editingPrompt.value.id}/`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({
|
||||
prompt: editingPrompt.value.prompt
|
||||
})
|
||||
})
|
||||
if (!error.value) {
|
||||
prompts.value[index] = editingPrompt.value
|
||||
}
|
||||
editingPrompt.value.updating = false
|
||||
editingPrompt.value = null
|
||||
}
|
||||
|
||||
const cancelEditPrompt = () => {
|
||||
editingPrompt.value = null
|
||||
}
|
||||
|
||||
const deletePrompt = async (index) => {
|
||||
deletingPromptIndex.value = index
|
||||
const { data, error } = await useAuthFetch(`/api/chat/prompts/${prompts.value[index].id}/`, {
|
||||
method: 'DELETE'
|
||||
})
|
||||
deletingPromptIndex.value = null
|
||||
if (!error.value) {
|
||||
prompts.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
const loadPrompts = async () => {
|
||||
loadingPrompts.value = true
|
||||
const { data, error } = await useAuthFetch('/api/chat/prompts/')
|
||||
if (!error.value) {
|
||||
prompts.value = data.value
|
||||
}
|
||||
loadingPrompts.value = false
|
||||
}
|
||||
|
||||
const selectPrompt = (prompt) => {
|
||||
props.usePrompt(prompt.prompt)
|
||||
menu.value = false
|
||||
}
|
||||
|
||||
onMounted( () => {
|
||||
loadPrompts()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<v-menu
|
||||
v-model="menu"
|
||||
:close-on-content-click="false"
|
||||
>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn
|
||||
v-bind="props"
|
||||
icon="speaker_notes"
|
||||
title="Common prompts"
|
||||
class="mr-3"
|
||||
></v-btn>
|
||||
</template>
|
||||
|
||||
<v-container>
|
||||
<v-card
|
||||
min-width="300"
|
||||
max-width="500"
|
||||
>
|
||||
<v-card-title>
|
||||
<span class="headline">Frequently prompts</span>
|
||||
</v-card-title>
|
||||
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-list>
|
||||
<v-list-item v-show="loadingPrompts">
|
||||
<v-list-item-title class="d-flex justify-center">
|
||||
<v-progress-circular indeterminate></v-progress-circular>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<template
|
||||
v-for="(prompt, idx) in prompts"
|
||||
:key="prompt.id"
|
||||
>
|
||||
<v-list-item
|
||||
active-color="primary"
|
||||
rounded="xl"
|
||||
v-if="editingPrompt && editingPrompt.id === prompt.id"
|
||||
>
|
||||
<v-textarea
|
||||
rows="2"
|
||||
v-model="editingPrompt.prompt"
|
||||
:loading="editingPrompt.updating"
|
||||
variant="underlined"
|
||||
hide-details
|
||||
density="compact"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<div class="d-flex flex-column">
|
||||
<v-btn
|
||||
icon="done"
|
||||
variant="text"
|
||||
:loading="editingPrompt.updating"
|
||||
@click="updatePrompt(idx)"
|
||||
>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
icon="close"
|
||||
variant="text"
|
||||
@click="cancelEditPrompt()"
|
||||
>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
</v-textarea>
|
||||
</v-list-item>
|
||||
<v-list-item
|
||||
v-if="!editingPrompt || editingPrompt.id !== prompt.id"
|
||||
rounded="xl"
|
||||
active-color="primary"
|
||||
@click="selectPrompt(prompt)"
|
||||
>
|
||||
<v-list-item-title>{{ prompt.prompt }}</v-list-item-title>
|
||||
<template v-slot:append>
|
||||
<v-btn
|
||||
icon="edit"
|
||||
size="small"
|
||||
variant="text"
|
||||
@click="editPrompt(idx)"
|
||||
>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
icon="delete"
|
||||
size="small"
|
||||
variant="text"
|
||||
:loading="deletingPromptIndex === idx"
|
||||
@click="deletePrompt(idx)"
|
||||
>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</template>
|
||||
|
||||
<v-list-item
|
||||
active-color="primary"
|
||||
>
|
||||
<div
|
||||
class="pt-3"
|
||||
>
|
||||
<v-textarea
|
||||
rows="2"
|
||||
v-model="newPrompt"
|
||||
label="Add a new prompt"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
:error-messages="promptInputErrorMessage"
|
||||
@update:modelValue="promptInputErrorMessage = ''"
|
||||
clearable
|
||||
>
|
||||
</v-textarea>
|
||||
</div>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-btn
|
||||
variant="text"
|
||||
block
|
||||
:loading="submittingNewPrompt"
|
||||
@click="addPrompt()"
|
||||
>
|
||||
<v-icon icon="add"></v-icon>
|
||||
Add prompt
|
||||
</v-btn>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</v-menu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup>
|
||||
import Prompt from "~/components/Prompt.vue";
|
||||
|
||||
definePageMeta({
|
||||
middleware: ["auth"]
|
||||
})
|
||||
@@ -140,6 +142,10 @@ const showSnackbar = (text) => {
|
||||
snackbar.value = true
|
||||
}
|
||||
|
||||
const editor = ref(null)
|
||||
const usePrompt = (prompt) => {
|
||||
editor.value.usePrompt(prompt)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -188,6 +194,7 @@ const showSnackbar = (text) => {
|
||||
<Welcome v-else />
|
||||
<v-footer app class="d-flex flex-column">
|
||||
<div class="px-md-16 w-100 d-flex align-center">
|
||||
<Prompt v-show="!fetchingResponse" :use-prompt="usePrompt" />
|
||||
<v-btn
|
||||
v-show="fetchingResponse"
|
||||
icon="close"
|
||||
@@ -195,7 +202,7 @@ const showSnackbar = (text) => {
|
||||
class="mr-3"
|
||||
@click="stop"
|
||||
></v-btn>
|
||||
<MsgEditor :send-message="send" :disabled="fetchingResponse" :loading="fetchingResponse" />
|
||||
<MsgEditor ref="editor" :send-message="send" :disabled="fetchingResponse" :loading="fetchingResponse" />
|
||||
</div>
|
||||
|
||||
<div class="px-4 py-2 text-disabled text-caption font-weight-light text-center w-100">
|
||||
|
||||
Reference in New Issue
Block a user