Add frequently used prompt function.
This commit is contained in:
@@ -65,7 +65,6 @@ const highlightCode = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
console.log('content changed', props.content)
|
|
||||||
contentHtml.value = props.content ? marked(props.content) : ''
|
contentHtml.value = props.content ? marked(props.content) : ''
|
||||||
if (props.content && props.content.endsWith('```')) {
|
if (props.content && props.content.endsWith('```')) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
|
|||||||
@@ -2,13 +2,12 @@
|
|||||||
<v-textarea
|
<v-textarea
|
||||||
v-model="message"
|
v-model="message"
|
||||||
:label="$t('writeAMessage')"
|
:label="$t('writeAMessage')"
|
||||||
:placeholder="$t('writeAMessage') + '...'"
|
:placeholder="hint"
|
||||||
rows="1"
|
rows="1"
|
||||||
:auto-grow="autoGrow"
|
:auto-grow="autoGrow"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:hint="hint"
|
:hide-details="true"
|
||||||
:hide-details="loading"
|
|
||||||
append-inner-icon="send"
|
append-inner-icon="send"
|
||||||
@keyup.enter.exact="enterOnly"
|
@keyup.enter.exact="enterOnly"
|
||||||
@click:appendInner="clickSendBtn"
|
@click:appendInner="clickSendBtn"
|
||||||
@@ -60,6 +59,9 @@ export default {
|
|||||||
}
|
}
|
||||||
this.message = ""
|
this.message = ""
|
||||||
},
|
},
|
||||||
|
usePrompt(prompt) {
|
||||||
|
this.message = prompt
|
||||||
|
},
|
||||||
clickSendBtn () {
|
clickSendBtn () {
|
||||||
this.send()
|
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>
|
<script setup>
|
||||||
|
import Prompt from "~/components/Prompt.vue";
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: ["auth"]
|
middleware: ["auth"]
|
||||||
})
|
})
|
||||||
@@ -140,6 +142,10 @@ const showSnackbar = (text) => {
|
|||||||
snackbar.value = true
|
snackbar.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const editor = ref(null)
|
||||||
|
const usePrompt = (prompt) => {
|
||||||
|
editor.value.usePrompt(prompt)
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -188,6 +194,7 @@ const showSnackbar = (text) => {
|
|||||||
<Welcome v-else />
|
<Welcome v-else />
|
||||||
<v-footer app class="d-flex flex-column">
|
<v-footer app class="d-flex flex-column">
|
||||||
<div class="px-md-16 w-100 d-flex align-center">
|
<div class="px-md-16 w-100 d-flex align-center">
|
||||||
|
<Prompt v-show="!fetchingResponse" :use-prompt="usePrompt" />
|
||||||
<v-btn
|
<v-btn
|
||||||
v-show="fetchingResponse"
|
v-show="fetchingResponse"
|
||||||
icon="close"
|
icon="close"
|
||||||
@@ -195,7 +202,7 @@ const showSnackbar = (text) => {
|
|||||||
class="mr-3"
|
class="mr-3"
|
||||||
@click="stop"
|
@click="stop"
|
||||||
></v-btn>
|
></v-btn>
|
||||||
<MsgEditor :send-message="send" :disabled="fetchingResponse" :loading="fetchingResponse" />
|
<MsgEditor ref="editor" :send-message="send" :disabled="fetchingResponse" :loading="fetchingResponse" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="px-4 py-2 text-disabled text-caption font-weight-light text-center w-100">
|
<div class="px-4 py-2 text-disabled text-caption font-weight-light text-center w-100">
|
||||||
|
|||||||
Reference in New Issue
Block a user