mirror of
https://github.com/ParisNeo/lollms-webui.git
synced 2024-12-18 20:17:50 +00:00
edit title, new discussion, remove discussion
This commit is contained in:
parent
3179e69a4c
commit
aea170cdce
11
app.py
11
app.py
@ -108,6 +108,7 @@ class Gpt4AllWebUI(GPT4AllAPI):
|
||||
self.add_endpoint("/stop_gen", "stop_gen", self.stop_gen, methods=["GET"])
|
||||
|
||||
self.add_endpoint("/rename", "rename", self.rename, methods=["POST"])
|
||||
self.add_endpoint("/edit_title", "edit_title", self.edit_title, methods=["POST"])
|
||||
self.add_endpoint(
|
||||
"/load_discussion", "load_discussion", self.load_discussion, methods=["POST"]
|
||||
)
|
||||
@ -370,7 +371,15 @@ class Gpt4AllWebUI(GPT4AllAPI):
|
||||
title = data["title"]
|
||||
self.current_discussion.rename(title)
|
||||
return "renamed successfully"
|
||||
|
||||
|
||||
def edit_title(self):
|
||||
data = request.get_json()
|
||||
title = data["title"]
|
||||
discussion_id = data["id"]
|
||||
self.current_discussion = Discussion(discussion_id, self.db)
|
||||
self.current_discussion.rename(title)
|
||||
return "title renamed successfully"
|
||||
|
||||
def load_discussion(self):
|
||||
data = request.get_json()
|
||||
if "id" in data:
|
||||
|
3
web/.gitignore
vendored
3
web/.gitignore
vendored
@ -25,3 +25,6 @@ coverage
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# REST Client files (VSCODE extension for making GET POST requests easy and fst from text files)
|
||||
*.http
|
||||
|
@ -1,22 +1,54 @@
|
||||
<template>
|
||||
<div :class="selected ? 'bg-bg-light-discussion dark:bg-bg-dark-discussion shadow-md' : ''"
|
||||
class="container flex flex-col sm:flex-row item-center shadow-sm gap-2 py-2 my-2 hover:shadow-md hover:bg-primary-light dark:hover:bg-primary rounded-md p-2 duration-75 group cursor-pointer">
|
||||
class="container flex flex-col sm:flex-row item-center shadow-sm gap-2 py-2 my-2 hover:shadow-md hover:bg-primary-light dark:hover:bg-primary rounded-md p-2 duration-75 group cursor-pointer"
|
||||
:id="'dis-' + id" @click.stop="selectEvent()">
|
||||
<!-- INDICATOR FOR SELECTED ITEM -->
|
||||
<div v-if="selected" class="items-center inline-block min-h-full w-2 rounded-xl self-stretch "
|
||||
:class="loading ? 'animate-bounce bg-accent ' : ' bg-secondary '"></div>
|
||||
<div v-else class="items-center inline-block min-h-full w-2 rounded-xl self-stretch"></div>
|
||||
<div v-if="!selected" class="items-center inline-block min-h-full w-2 rounded-xl self-stretch"></div>
|
||||
|
||||
<!-- TITLE -->
|
||||
<p class="truncate w-auto">{{ title ? title === "untitled" ? "New discussion" : title : "New discussion" }}</p>
|
||||
<p v-if="!editTitle" :title="title" class="truncate w-full">{{ title ? title === "untitled" ? "New discussion" : title : "New discussion" }}</p>
|
||||
|
||||
<input v-if="editTitle" type="text" id="title-box" class="bg-bg-light dark:bg-bg-dark rounded-md border-0 w-full -m-1 p-1"
|
||||
:value="title" required @input="chnageTitle($event.target.value)" @click.stop>
|
||||
|
||||
<!-- CONTROL BUTTONS -->
|
||||
<div class="flex items-center gap-3 flex-1 max-h-6">
|
||||
<div class="flex gap-3 flex-1 items-center justify-end invisible group-hover:visible duration-75">
|
||||
<div class="text-2xl hover:text-secondary duration-75 active:scale-90" title="Edit title">
|
||||
<div class="flex items-center flex-1 max-h-6">
|
||||
<!-- DELETE CONFIRM -->
|
||||
<div v-if="showConfirmation && !editTitleMode" class="flex gap-3 flex-1 items-center justify-end duration-75">
|
||||
<button class="text-2xl hover:text-secondary duration-75 active:scale-90" title="Confirm removal"
|
||||
type="button" @click.stop="deleteEvent()">
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
<button class="text-2xl hover:text-red-600 duration-75 active:scale-90 " title="Cancel removal"
|
||||
type="button" @click.stop="showConfirmation = false">
|
||||
<i data-feather="x"></i>
|
||||
</button>
|
||||
</div>
|
||||
<!-- EDIT TITLE CONFIRM -->
|
||||
<div v-if="showConfirmation && editTitleMode" class="flex gap-3 flex-1 items-center justify-end duration-75">
|
||||
<button class="text-2xl hover:text-red-600 duration-75 active:scale-90 " title="Discard title changes"
|
||||
type="button" @click.stop="editTitleMode = false ">
|
||||
<i data-feather="x"></i>
|
||||
</button>
|
||||
<button class="text-2xl hover:text-secondary duration-75 active:scale-90" title="Confirm title changes"
|
||||
type="button" @click.stop="editTitleEvent()">
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
<!-- EDIT AND REMOVE -->
|
||||
<div v-if="!showConfirmation"
|
||||
class="flex gap-3 flex-1 items-center justify-end invisible group-hover:visible duration-75">
|
||||
<button class="text-2xl hover:text-secondary duration-75 active:scale-90" title="Edit title" type="button"
|
||||
@click.stop="editTitleMode = true">
|
||||
<i data-feather="edit-2"></i>
|
||||
</div>
|
||||
<div class="text-2xl hover:text-red-600 duration-75 active:scale-90" title="Remove discussion">
|
||||
</button>
|
||||
<button class="text-2xl hover:text-red-600 duration-75 active:scale-90 " title="Remove discussion"
|
||||
type="button" @click.stop="showConfirmation = true">
|
||||
<i data-feather="trash"></i>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -28,6 +60,7 @@ import feather from 'feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'Discussion',
|
||||
emits: ['delete', 'select', 'editTitle'],
|
||||
props: {
|
||||
id: Number,
|
||||
title: String,
|
||||
@ -39,18 +72,53 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
showConfirmation: false,
|
||||
editTitleMode: false,
|
||||
editTitle: false,
|
||||
newTitle: String,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
},
|
||||
deleteEvent() {
|
||||
this.showConfirmation = false
|
||||
this.$emit("delete")
|
||||
},
|
||||
selectEvent() {
|
||||
this.$emit("select")
|
||||
},
|
||||
editTitleEvent() {
|
||||
this.editTitle= false
|
||||
this.editTitleMode= false
|
||||
this.showConfirmation = false
|
||||
this.$emit("editTitle",
|
||||
{
|
||||
title: this.newTitle,
|
||||
id: this.id
|
||||
})
|
||||
},
|
||||
chnageTitle(text){
|
||||
this.newTitle=text
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.newTitle= this.title
|
||||
nextTick(() => {
|
||||
feather.replace()
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
}, watch: {
|
||||
showConfirmation() {
|
||||
nextTick(() => {
|
||||
feather.replace()
|
||||
|
||||
})
|
||||
},
|
||||
editTitleMode(newval) {
|
||||
|
||||
this.showConfirmation=newval
|
||||
this.editTitle = newval
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,45 +0,0 @@
|
||||
<script setup>
|
||||
import { Modal } from 'flowbite-vue'
|
||||
import { ref } from 'vue'
|
||||
|
||||
// const isShowModal = ref(false)
|
||||
// function closeModal() {
|
||||
// isShowModal.value = false
|
||||
|
||||
// }
|
||||
// function showModal() {
|
||||
// isShowModal.value = true
|
||||
// }
|
||||
</script>
|
||||
<template>
|
||||
<!-- <button @click="showModal" type="button"
|
||||
class="mt-5 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
|
||||
Show modal
|
||||
</button> -->
|
||||
<Modal :size="size" v-if="ShowModal" @close="closeModal()">
|
||||
<template #header>
|
||||
<div class="flex items-center text-lg">
|
||||
<slot name="header"></slot>
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<slot name="body"></slot>
|
||||
</template>
|
||||
<template #footer>
|
||||
<slot name="footer"></slot>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'ModalSimple',
|
||||
props: {
|
||||
ShowModal: Boolean
|
||||
},methods:{
|
||||
closeModal(){
|
||||
this.ShowModal = false
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
@ -6,7 +6,7 @@
|
||||
class="z-10 sticky top-0 flex-row p-2 flex items-center gap-3 flex-0 bg-bg-light-tone dark:bg-bg-dark-tone mt-0 px-4 shadow-md">
|
||||
<!-- CONTROL PANEL -->
|
||||
<button class=" text-2xl hover:text-secondary duration-75 active:scale-90 " title="Create new discussion"
|
||||
type="button" @click="createNewDiscussionShow">
|
||||
type="button" @click="createNewDiscussion()">
|
||||
<i data-feather="plus"></i>
|
||||
</button>
|
||||
<button class=" text-2xl hover:text-secondary duration-75 active:scale-90 "
|
||||
@ -43,33 +43,15 @@
|
||||
@input="filterDiscussions()">
|
||||
</div>
|
||||
</form>
|
||||
<!-- Create discussion -->
|
||||
<ModalSimple :ShowModal="showCreateDiscussionModal">
|
||||
<template v-slot:header>
|
||||
<p>Create new discussion</p>
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<div class="mb-6">
|
||||
<label for="default-input" class="block mb-2 text-sm font-medium ">Enter discussion title:</label>
|
||||
<input type="text" id="default-input"
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<div class="flex justify-between">
|
||||
<button type="button"
|
||||
class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Create</button>
|
||||
</div>
|
||||
</template>
|
||||
</ModalSimple>
|
||||
|
||||
</div>
|
||||
<div class="relative overflow-y-scroll no-scrollbar">
|
||||
<!-- DISCUSSION LIST -->
|
||||
<div class="mx-4 flex-grow" :class="filterInProgress ? 'opacity-20 pointer-events-none' : ''">
|
||||
|
||||
<Discussion v-for="(item, index) in list" :key="index" :id="item.id" :title="item.title"
|
||||
<Discussion v-for="(item, index) in list" :key="index" :id="item.id" :title="item.title" ref="discussionList"
|
||||
:selected="currentDiscussion.id == item.id" :loading="currentDiscussion.id == item.id && loading"
|
||||
@click="selectDiscussion(item)" />
|
||||
@select="selectDiscussion(item)" @delete="deleteDiscussion(item.id)" @editTitle="editTitle" />
|
||||
|
||||
<div v-if="list.length < 1"
|
||||
class=" gap-2 py-2 my-2 hover:shadow-md hover:bg-primary-light dark:hover:bg-primary rounded-md p-2 duration-75 group cursor-pointer">
|
||||
@ -93,7 +75,7 @@
|
||||
|
||||
<WelcomeComponent v-if="discussionArr.length < 1" />
|
||||
|
||||
<ChatBox v-if="discussionArr.length > 1" @messageSentEvent="sendMsg" />
|
||||
<ChatBox v-if="discussionArr.length > 0" @messageSentEvent="sendMsg" />
|
||||
|
||||
</div>
|
||||
|
||||
@ -108,12 +90,6 @@
|
||||
|
||||
<script>
|
||||
|
||||
import axios from "axios";
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
axios.defaults.baseURL = import.meta.env.VITE_GPT4ALL_API_BASEURL // Use this for external development not for production
|
||||
import websocket from '@/services/websocket.js';
|
||||
|
||||
export default {
|
||||
|
||||
setup() {
|
||||
@ -139,7 +115,8 @@ export default {
|
||||
const res = await axios.get("/list_discussions");
|
||||
|
||||
if (res) {
|
||||
|
||||
this.list = res.data
|
||||
this.tempList = this.list
|
||||
return res.data
|
||||
|
||||
}
|
||||
@ -152,17 +129,68 @@ export default {
|
||||
},
|
||||
async load_discussion(id) {
|
||||
try {
|
||||
|
||||
|
||||
if (id) {
|
||||
this.loading = true
|
||||
const res = await axios.post("/load_discussion", {
|
||||
id: id
|
||||
});
|
||||
|
||||
this.loading = false
|
||||
if (res) {
|
||||
this.discussionArr = res.data
|
||||
this.loading = false
|
||||
|
||||
this.discussionArr = res.data.filter((item) => item.sender != "conditionner") // Filter out the conditionner entries
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
},
|
||||
async new_discussion(title) {
|
||||
try {
|
||||
const res = await axios.get("/new_discussion", { params: { title: title } });
|
||||
|
||||
if (res) {
|
||||
return res.data
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return {}
|
||||
}
|
||||
},
|
||||
async delete_discussion(id) {
|
||||
try {
|
||||
if (id) {
|
||||
this.loading = true
|
||||
const res = await axios.post("/delete_discussion", {
|
||||
id: id
|
||||
});
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
},
|
||||
async edit_title(discussion_id, new_title) {
|
||||
try {
|
||||
if (discussion_id) {
|
||||
this.loading = true
|
||||
const res = await axios.post("/edit_title", {
|
||||
id: discussion_id,
|
||||
title: new_title
|
||||
});
|
||||
this.loading = false
|
||||
if (res.status == 200) {
|
||||
const index = this.list.findIndex(x => x.id == discussion_id);
|
||||
const discussionItem = this.list[index]
|
||||
discussionItem.title = new_title
|
||||
this.tempList = this.list
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,12 +211,20 @@ export default {
|
||||
}, 100)
|
||||
}
|
||||
},
|
||||
selectDiscussion(item) {
|
||||
async selectDiscussion(item) {
|
||||
this.currentDiscussion = item
|
||||
this.load_discussion(item.id)
|
||||
await this.load_discussion(item.id)
|
||||
|
||||
if (this.discussionArr.length > 1) {
|
||||
|
||||
if (this.currentDiscussion.title === "" || this.currentDiscussion.title === null ) {
|
||||
this.changeTitleUsingUserMSG(this.currentDiscussion.id, this.discussionArr[1].content)
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
scrollToElement(el) {
|
||||
|
||||
if (el) {
|
||||
|
||||
el.scrollIntoView({ behavior: 'smooth', block: "center", inline: "nearest" });
|
||||
@ -228,28 +264,72 @@ export default {
|
||||
this.scrollToElement(responseMessageElement)
|
||||
})
|
||||
|
||||
if (this.currentDiscussion.title === "" || this.currentDiscussion.title === null) {
|
||||
this.changeTitleUsingUserMSG(this.currentDiscussion.id, usrMessage.content)
|
||||
}
|
||||
|
||||
},
|
||||
sendMsg(msg) {
|
||||
//console.log("Message sent:", msg)
|
||||
|
||||
websocket.emit('generate_msg', { prompt: msg });
|
||||
|
||||
},
|
||||
steamMessageContent(content) {
|
||||
//console.log("Message obj recieved:", content)
|
||||
|
||||
const lastMsg = this.discussionArr[this.discussionArr.length - 1]
|
||||
lastMsg.content = content.data
|
||||
},
|
||||
createNewDiscussionShow(){
|
||||
console.log("aa",this.showCreateDiscussionModal)
|
||||
this.showCreateDiscussionModal=true
|
||||
}
|
||||
async changeTitleUsingUserMSG(id, msg) {
|
||||
const index = this.list.findIndex(x => x.id == id);
|
||||
const discussionItem = this.list[index]
|
||||
if (msg) {
|
||||
discussionItem.title = msg
|
||||
this.tempList = this.list
|
||||
}
|
||||
await this.edit_title(id,msg)
|
||||
|
||||
},
|
||||
async createNewDiscussion() {
|
||||
|
||||
const res = await this.new_discussion()
|
||||
await this.list_discussions()
|
||||
const index = this.list.findIndex(x => x.id == res.id);
|
||||
const discussionItem = this.list[index]
|
||||
this.selectDiscussion(discussionItem)
|
||||
nextTick(() => {
|
||||
const selectedDisElement = document.getElementById('dis-' + res.id)
|
||||
this.scrollToElement(selectedDisElement)
|
||||
|
||||
})
|
||||
|
||||
},
|
||||
async deleteDiscussion(id) {
|
||||
const index = this.list.findIndex(x => x.id == id);
|
||||
const discussionItem = this.list[index]
|
||||
discussionItem.loading=true
|
||||
this.delete_discussion(id)
|
||||
if (this.currentDiscussion.id == id) {
|
||||
this.currentDiscussion = {}
|
||||
}
|
||||
await this.list_discussions()
|
||||
},
|
||||
async editTitle(newTitleObj) {
|
||||
//const index = this.$refs.discussionList.findIndex(x => x.id == newTitleObj.id);
|
||||
//const discussionItem = this.$refs.discussionList[index]
|
||||
//console.log(JSON.stringify(discussionItem))
|
||||
//discussionItem.loading.value=true
|
||||
//console.log(discussionItem.title)
|
||||
await this.edit_title(newTitleObj.id, newTitleObj.title)
|
||||
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
async created() {
|
||||
|
||||
// Constructor
|
||||
this.list = await this.list_discussions()
|
||||
this.tempList = this.list
|
||||
await this.list_discussions()
|
||||
|
||||
nextTick(() => {
|
||||
feather.replace()
|
||||
})
|
||||
@ -263,7 +343,7 @@ export default {
|
||||
Message,
|
||||
ChatBox,
|
||||
WelcomeComponent,
|
||||
ModalSimple
|
||||
|
||||
}, watch: {
|
||||
filterTitle(newVal, oldVal) {
|
||||
if (newVal == "") {
|
||||
@ -282,9 +362,14 @@ import Discussion from '../components/Discussion.vue';
|
||||
import Message from '../components/Message.vue';
|
||||
import ChatBox from '../components/ChatBox.vue'
|
||||
import WelcomeComponent from '../components/WelcomeComponent.vue'
|
||||
import ModalSimple from '../components/ModalSimple.vue'
|
||||
|
||||
import feather from 'feather-icons'
|
||||
|
||||
import axios from "axios";
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
import websocket from '@/services/websocket.js';
|
||||
|
||||
import { onMounted } from 'vue'
|
||||
import { initFlowbite } from 'flowbite'
|
||||
|
||||
@ -292,4 +377,7 @@ import { initFlowbite } from 'flowbite'
|
||||
onMounted(() => {
|
||||
initFlowbite();
|
||||
})
|
||||
|
||||
axios.defaults.baseURL = import.meta.env.VITE_GPT4ALL_API_BASEURL;
|
||||
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user