mirror of
https://github.com/ParisNeo/lollms-webui.git
synced 2024-12-19 04:17:52 +00:00
moved around controls and lots of housekeeping
This commit is contained in:
parent
f7530e7bc4
commit
6baf5f9823
@ -1,73 +1,77 @@
|
||||
<template>
|
||||
<div
|
||||
class="overflow-y-scroll flex flex-col no-scrollbar shadow-lg min-w-[24rem] max-w-[24rem] bg-bg-light-tone dark:bg-bg-dark-tone ">
|
||||
class="overflow-y-scroll flex flex-col no-scrollbar shadow-lg min-w-[24rem] max-w-[24rem] bg-bg-light-tone dark:bg-bg-dark-tone">
|
||||
<!-- LEFT SIDE PANEL -->
|
||||
<div
|
||||
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="createNewDiscussion()">
|
||||
<i data-feather="plus"></i>
|
||||
</button>
|
||||
<button class=" text-2xl hover:text-secondary duration-75 active:scale-90 "
|
||||
title="Reset database, remove all discussions">
|
||||
<i data-feather="refresh-ccw"></i>
|
||||
</button>
|
||||
<button class=" text-2xl hover:text-secondary duration-75 active:scale-90 " title="Export database"
|
||||
type="button">
|
||||
<i data-feather="database"></i>
|
||||
</button>
|
||||
<button class=" text-2xl hover:text-secondary duration-75 active:scale-90 rotate-90"
|
||||
title="Export discussion to a file" type="button">
|
||||
<i data-feather="log-out"></i>
|
||||
</button>
|
||||
|
||||
class="z-10 sticky top-0 flex-col bg-bg-light-tone dark:bg-bg-dark-tone shadow-md">
|
||||
<!-- SEARCH BAR -->
|
||||
<form>
|
||||
<form class="flex-row p-4 pb-0 items-center gap-3 flex-0 w-full">
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
|
||||
<div class="scale-75 ">
|
||||
<div class="scale-75">
|
||||
<i data-feather="search"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="absolute inset-y-0 right-0 flex items-center pr-3 ">
|
||||
<div class=" hover:text-secondary duration-75 active:scale-90 "
|
||||
<div class="absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<div class="hover:text-secondary duration-75 active:scale-90"
|
||||
:class="filterTitle ? 'visible' : 'invisible'" title="Clear" @click="filterTitle = ''">
|
||||
<i data-feather="x"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="search" id="default-search"
|
||||
class="block w-full p-2 pl-10 pr-10 text-sm border border-gray-300 rounded-lg bg-bg-light focus:ring-secondary focus:border-secondary dark:bg-bg-dark dark:border-gray-600 dark:placeholder-gray-400 dark:focus:ring-secondary dark:focus:border-secondary "
|
||||
class="block w-full p-2 pl-10 pr-10 text-sm border border-gray-300 rounded-lg bg-bg-light focus:ring-secondary focus:border-secondary dark:bg-bg-dark dark:border-gray-600 dark:placeholder-gray-400 dark:focus:ring-secondary dark:focus:border-secondary"
|
||||
placeholder="Search..." title="Filter discussions by title" v-model="filterTitle"
|
||||
@input="filterDiscussions()">
|
||||
@input="filterDiscussions()" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- CONTROL PANEL -->
|
||||
<div class="flex-row p-4 flex items-center gap-3 flex-0">
|
||||
|
||||
|
||||
<button class="text-2xl hover:text-secondary duration-75 active:scale-90" title="Create new discussion"
|
||||
type="button" @click="createNewDiscussion()">
|
||||
<i data-feather="plus"></i>
|
||||
</button>
|
||||
<button class="text-2xl hover:text-secondary duration-75 active:scale-90" title="Edit discussion list"
|
||||
type="button" @click="">
|
||||
<i data-feather="check-square"></i>
|
||||
</button>
|
||||
<button class="text-2xl hover:text-secondary duration-75 active:scale-90"
|
||||
title="Reset database, remove all discussions">
|
||||
<i data-feather="refresh-ccw"></i>
|
||||
</button>
|
||||
<button class="text-2xl hover:text-secondary duration-75 active:scale-90" title="Export database" type="button">
|
||||
<i data-feather="database"></i>
|
||||
</button>
|
||||
<button class="text-2xl hover:text-secondary duration-75 active:scale-90 rotate-90"
|
||||
title="Export discussion to a file" type="button">
|
||||
<i data-feather="log-out"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="relative overflow-y-scroll no-scrollbar">
|
||||
|
||||
</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"
|
||||
ref="discussionList" :selected="currentDiscussion.id == item.id"
|
||||
:loading="currentDiscussion.id == item.id && loading" @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">
|
||||
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">
|
||||
<p class="px-3">No discussions are found</p>
|
||||
</div>
|
||||
<div
|
||||
class="sticky bottom-0 bg-gradient-to-t pointer-events-none from-bg-light-tone dark:from-bg-dark-tone flex height-64 ">
|
||||
class="sticky bottom-0 bg-gradient-to-t pointer-events-none from-bg-light-tone dark:from-bg-dark-tone flex height-64">
|
||||
<!-- FADING DISCUSSION LIST END ELEMENT -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="overflow-y-scroll flex flex-col no-scrollbar flex-grow "
|
||||
<div class="overflow-y-scroll flex flex-col no-scrollbar flex-grow"
|
||||
:class="loading ? 'opacity-20 pointer-events-none' : ''">
|
||||
<!-- CHAT AREA -->
|
||||
<div>
|
||||
@ -76,11 +80,8 @@
|
||||
|
||||
<WelcomeComponent v-if="discussionArr.length < 1" />
|
||||
|
||||
<ChatBox v-if="discussionArr.length > 0" @messageSentEvent="sendMsg" />
|
||||
|
||||
<ChatBox v-if="discussionArr.length > 0" @messageSentEvent="sendMsg" :loading="isGenerating"/>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
@ -90,53 +91,45 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
|
||||
setup() {
|
||||
|
||||
|
||||
},
|
||||
setup() { },
|
||||
data() {
|
||||
|
||||
return {
|
||||
list: [], // Discussion list
|
||||
tempList: [], // Copy of Discussion list (used for keeping the original list during filtering discussions/searching action)
|
||||
currentDiscussion: {}, // Current/selected discussion id
|
||||
discussionArr: [],
|
||||
loading: false,
|
||||
filterTitle: "",
|
||||
filterTitle: '',
|
||||
filterInProgress: false,
|
||||
isCreated:false
|
||||
isCreated: false,
|
||||
isGenerating:false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async list_discussions() {
|
||||
try {
|
||||
const res = await axios.get("/list_discussions");
|
||||
const res = await axios.get('/list_discussions')
|
||||
|
||||
if (res) {
|
||||
this.list = res.data
|
||||
this.tempList = this.list
|
||||
return res.data
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return []
|
||||
}
|
||||
|
||||
},
|
||||
async load_discussion(id) {
|
||||
try {
|
||||
if (id) {
|
||||
this.loading = true
|
||||
const res = await axios.post("/load_discussion", {
|
||||
const res = await axios.post('/load_discussion', {
|
||||
id: id
|
||||
});
|
||||
})
|
||||
this.loading = false
|
||||
if (res) {
|
||||
|
||||
// Filter out the user and bot entries
|
||||
this.discussionArr = res.data.filter((item) => item.type == 0)
|
||||
const lastMessage = this.discussionArr[this.discussionArr.length - 1]
|
||||
@ -144,26 +137,21 @@ export default {
|
||||
nextTick(() => {
|
||||
const selectedElement = document.getElementById('msg-' + lastMessage.id)
|
||||
this.scrollToElement(selectedElement)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
},
|
||||
async new_discussion(title) {
|
||||
try {
|
||||
const res = await axios.get("/new_discussion", { params: { title: title } });
|
||||
const res = await axios.get('/new_discussion', { params: { title: title } })
|
||||
|
||||
if (res) {
|
||||
return res.data
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
@ -174,83 +162,71 @@ export default {
|
||||
try {
|
||||
if (id) {
|
||||
this.loading = true
|
||||
const res = await axios.post("/delete_discussion", {
|
||||
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", {
|
||||
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 index = this.list.findIndex((x) => x.id == discussion_id)
|
||||
const discussionItem = this.list[index]
|
||||
discussionItem.title = new_title
|
||||
this.tempList = this.list
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
},
|
||||
filterDiscussions() {
|
||||
|
||||
// Search bar in for filtering discussions by title (serch)
|
||||
|
||||
if (!this.filterInProgress) {
|
||||
this.filterInProgress = true
|
||||
setTimeout(() => {
|
||||
|
||||
this.list = this.tempList.filter((item) => item.title.includes(this.filterTitle))
|
||||
this.filterInProgress = false
|
||||
|
||||
}, 100)
|
||||
}
|
||||
},
|
||||
async selectDiscussion(item) {
|
||||
|
||||
// When discussion is selected it loads the discussion array
|
||||
|
||||
this.currentDiscussion = item
|
||||
|
||||
localStorage.setItem("selected_discussion", this.currentDiscussion.id)
|
||||
localStorage.setItem('selected_discussion', this.currentDiscussion.id)
|
||||
|
||||
await this.load_discussion(item.id)
|
||||
|
||||
if (this.discussionArr.length > 1) {
|
||||
|
||||
if (this.currentDiscussion.title === "" || this.currentDiscussion.title === null) {
|
||||
if (this.currentDiscussion.title === '' || this.currentDiscussion.title === null) {
|
||||
this.changeTitleUsingUserMSG(this.currentDiscussion.id, this.discussionArr[1].content)
|
||||
}
|
||||
}
|
||||
nextTick(() => {
|
||||
const selectedDisElement = document.getElementById('dis-' + item.id)
|
||||
this.scrollToElement(selectedDisElement)
|
||||
|
||||
})
|
||||
|
||||
},
|
||||
scrollToElement(el) {
|
||||
|
||||
//console.log("scroll", el.id)
|
||||
if (el) {
|
||||
|
||||
el.scrollIntoView({ behavior: 'smooth', block: "center", inline: "nearest" });
|
||||
el.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' })
|
||||
}
|
||||
},
|
||||
createMsg(msgObj) {
|
||||
@ -262,23 +238,22 @@ export default {
|
||||
id: msgObj.message,
|
||||
//parent: 10,
|
||||
rank: 0,
|
||||
sender: msgObj.user,
|
||||
sender: msgObj.user
|
||||
//type: 0
|
||||
}
|
||||
this.discussionArr.push(usrMessage)
|
||||
nextTick(() => {
|
||||
const userMsgElement = document.getElementById('msg-' + msgObj.message)
|
||||
this.scrollToElement(userMsgElement)
|
||||
|
||||
})
|
||||
|
||||
// Create response message
|
||||
let responseMessage = {
|
||||
content: "..typing",
|
||||
content: '..typing',
|
||||
id: msgObj.response_id,
|
||||
//parent: 10,
|
||||
rank: 0,
|
||||
sender: msgObj.bot,
|
||||
sender: msgObj.bot
|
||||
//type: 0
|
||||
}
|
||||
this.discussionArr.push(responseMessage)
|
||||
@ -287,73 +262,63 @@ export default {
|
||||
this.scrollToElement(responseMessageElement)
|
||||
})
|
||||
|
||||
if (this.currentDiscussion.title === "" || this.currentDiscussion.title === null) {
|
||||
if (this.currentDiscussion.title === '' || this.currentDiscussion.title === null) {
|
||||
this.changeTitleUsingUserMSG(this.currentDiscussion.id, usrMessage.content)
|
||||
}
|
||||
|
||||
this.isGenerating=false
|
||||
},
|
||||
sendMsg(msg) {
|
||||
|
||||
// Sends message to backend
|
||||
|
||||
websocket.emit('generate_msg', { prompt: msg });
|
||||
|
||||
this.isGenerating=true
|
||||
websocket.emit('generate_msg', { prompt: msg })
|
||||
},
|
||||
steamMessageContent(content) {
|
||||
|
||||
// Streams response message content from backend
|
||||
|
||||
const lastMsg = this.discussionArr[this.discussionArr.length - 1]
|
||||
lastMsg.content = content.data
|
||||
},
|
||||
async changeTitleUsingUserMSG(id, msg) {
|
||||
|
||||
// If discussion is untitled or title is null then it sets the title to first user message.
|
||||
|
||||
const index = this.list.findIndex(x => x.id == id);
|
||||
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() {
|
||||
|
||||
// Creates new discussion on backend,
|
||||
// gets new discussion list, selects
|
||||
// newly created discussion,
|
||||
// Creates new discussion on backend,
|
||||
// gets new discussion list, selects
|
||||
// newly created discussion,
|
||||
// scrolls to the discussion
|
||||
|
||||
const res = await this.new_discussion()
|
||||
await this.list_discussions()
|
||||
const index = this.list.findIndex(x => x.id == res.id);
|
||||
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)
|
||||
|
||||
})
|
||||
|
||||
},
|
||||
loadLastUsedDiscussion() {
|
||||
// Checks local storage for last selected discussion
|
||||
const id = localStorage.getItem("selected_discussion")
|
||||
const id = localStorage.getItem('selected_discussion')
|
||||
if (id) {
|
||||
const index = this.list.findIndex(x => x.id == id);
|
||||
const index = this.list.findIndex((x) => x.id == id)
|
||||
const discussionItem = this.list[index]
|
||||
this.selectDiscussion(discussionItem)
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
async deleteDiscussion(id) {
|
||||
|
||||
// Deletes discussion from backend and frontend
|
||||
|
||||
const index = this.list.findIndex(x => x.id == id);
|
||||
const index = this.list.findIndex((x) => x.id == id)
|
||||
const discussionItem = this.list[index]
|
||||
discussionItem.loading = true
|
||||
this.delete_discussion(id)
|
||||
@ -363,85 +328,81 @@ export default {
|
||||
await this.list_discussions()
|
||||
},
|
||||
async editTitle(newTitleObj) {
|
||||
//const index = this.$refs.discussionList.findIndex(x => x.id == newTitleObj.id);
|
||||
//const discussionItem = this.$refs.discussionList[index]
|
||||
// const index = this.$refs.discussionList.findIndex(x => x.id == newTitleObj.id);
|
||||
// let discussionItem = this.$refs.discussionList[index]
|
||||
// discussionItem.title = newTitleObj.title
|
||||
//console.log(JSON.stringify(discussionItem))
|
||||
//discussionItem.loading.value=true
|
||||
//console.log(discussionItem.title)
|
||||
const index = this.list.findIndex((x) => x.id == newTitleObj.id)
|
||||
const discussionItem = this.list[index]
|
||||
discussionItem.title = newTitleObj.title
|
||||
await this.edit_title(newTitleObj.id, newTitleObj.title)
|
||||
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
|
||||
// Constructor
|
||||
|
||||
await this.list_discussions()
|
||||
|
||||
this.loadLastUsedDiscussion()
|
||||
this.isCreated=true
|
||||
this.isCreated = true
|
||||
|
||||
nextTick(() => {
|
||||
feather.replace()
|
||||
})
|
||||
|
||||
// WebSocket responses
|
||||
websocket.on("infos", this.createMsg)
|
||||
websocket.on("message", this.steamMessageContent)
|
||||
|
||||
},activated(){
|
||||
|
||||
websocket.on('infos', this.createMsg)
|
||||
websocket.on('message', this.steamMessageContent)
|
||||
},
|
||||
activated() {
|
||||
// This lifecycle hook runs every time you switch from other page back to this page (vue-router)
|
||||
// To fix scrolling back to last message, this hook is needed.
|
||||
// If anyone knows hor to fix scroll issue when changing pages, please do fix it :D
|
||||
|
||||
if(this.isCreated){
|
||||
if (this.isCreated) {
|
||||
this.loadLastUsedDiscussion()
|
||||
}
|
||||
|
||||
},
|
||||
components: {
|
||||
components: {
|
||||
Discussion,
|
||||
Message,
|
||||
ChatBox,
|
||||
WelcomeComponent,
|
||||
|
||||
}, watch: {
|
||||
WelcomeComponent
|
||||
},
|
||||
watch: {
|
||||
filterTitle(newVal, oldVal) {
|
||||
if (newVal == "") {
|
||||
if (newVal == '') {
|
||||
this.filterInProgress = true
|
||||
this.list = this.tempList
|
||||
this.filterInProgress = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<script setup >
|
||||
import Discussion from '../components/Discussion.vue';
|
||||
import Message from '../components/Message.vue';
|
||||
<script setup>
|
||||
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 feather from 'feather-icons'
|
||||
|
||||
import axios from "axios";
|
||||
import { nextTick, onActivated } from 'vue';
|
||||
import axios from 'axios'
|
||||
import { nextTick, onActivated } from 'vue'
|
||||
|
||||
import websocket from '@/services/websocket.js';
|
||||
import websocket from '@/services/websocket.js'
|
||||
|
||||
import { onMounted } from 'vue'
|
||||
import { initFlowbite } from 'flowbite'
|
||||
|
||||
// initialize components based on data attribute selectors
|
||||
onMounted(() => {
|
||||
initFlowbite();
|
||||
initFlowbite()
|
||||
})
|
||||
|
||||
axios.defaults.baseURL = import.meta.env.VITE_GPT4ALL_API_BASEURL;
|
||||
|
||||
</script>
|
||||
axios.defaults.baseURL = import.meta.env.VITE_GPT4ALL_API_BASEURL
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user