- {
- hide_pending ?
-
+ { conversation.map(({role, content}, idx) => {
+ return (
+
+ )
+ }) }
+
+
Please select a conversation or start a new one.
}
diff --git a/src/components/chat/ConversationBubble.jsx b/src/components/chat/ConversationBubble.jsx
index ebffa5a..b6fc753 100644
--- a/src/components/chat/ConversationBubble.jsx
+++ b/src/components/chat/ConversationBubble.jsx
@@ -1,12 +1,12 @@
import { CircleFill } from "react-bootstrap-icons"
import Markdown from "react-markdown"
-export default function ConversationBubble({role, content, hidden}) {
+export default function ConversationBubble({role, content, hidden, special}) {
return (
{
- content ?
-
{ content } :
+ content || !special?
+
{ content || "**[ EMPTY MESSAGE ]**" } :
<>
diff --git a/src/components/chat/Ticket.jsx b/src/components/chat/Ticket.jsx
index dd8afe8..9db8a9f 100644
--- a/src/components/chat/Ticket.jsx
+++ b/src/components/chat/Ticket.jsx
@@ -1,10 +1,20 @@
-export default function Ticket({ title, info, selectChat, is_selected }) {
+import { XLg } from "react-bootstrap-icons";
+
+export default function Ticket({ title, info, selectChat, is_selected, deleteHistory }) {
+
return (
selectChat(info)}
>
{ title }
+ {
+ evt.stopPropagation();
+ deleteHistory({ uid: info.uid, title })
+ }}
+ />
)
}
\ No newline at end of file
diff --git a/src/components/chat/Tickets.jsx b/src/components/chat/Tickets.jsx
index d91491b..2e019de 100644
--- a/src/components/chat/Tickets.jsx
+++ b/src/components/chat/Tickets.jsx
@@ -3,14 +3,14 @@ import Ticket from "./Ticket";
import useIDB from "../../utils/idb";
import { genRandomID } from "../../utils/tools";
-export default function Tickets({selectChat, current_chat, history, setHistory}) {
+export default function Tickets({selectChat, current_chat, history, setHistory, deleteHistory}) {
const idb = useIDB();
async function syncHistory() {
const history = await idb.getAll('chat-history')
history.sort((a, b)=>b.updatedAt - a.updatedAt)
- setHistory(history)
+ setHistory(history.map(e=>{return {...e, client: null}}))
}
async function startNewConversation() {
@@ -27,7 +27,7 @@ export default function Tickets({selectChat, current_chat, history, setHistory})
const new_conv_info = await idb.getByID('chat-history', conv_id);
new_conv_info &&
setHistory([
- new_conv_info,
+ {...new_conv_info, client: null},
...history
])
selectChat(new_conv_info)
@@ -50,9 +50,10 @@ export default function Tickets({selectChat, current_chat, history, setHistory})
const { title, uid } = elem;
return (
)
diff --git a/src/components/chat/index.jsx b/src/components/chat/index.jsx
index 51ce95f..288007e 100644
--- a/src/components/chat/index.jsx
+++ b/src/components/chat/index.jsx
@@ -1,4 +1,4 @@
-import { useState } from "react";
+import { useEffect, useRef, useState } from "react";
import Tickets from "./Tickets";
import Conversation from "./Conversation";
import useIDB from "../../utils/idb";
@@ -8,6 +8,9 @@ export default function Chat() {
const [chat, selectChat] = useState({});
const [history, setHistory] = useState([]);
const idb = useIDB();
+ const dialogRef = useRef(null);
+ const [showConfirm, toggleConfirm] = useState(false);
+ const [conv_to_delete, requestDelete] = useState(null);
function updateChatClient(client) {
selectChat({
@@ -19,14 +22,74 @@ export default function Chat() {
history_cp.findIndex(e=>e.uid === chat.uid)
].client = client;
setHistory(history_cp);
+ }
+
+ function resetRequestDelete() {
+ requestDelete(null);
+ toggleConfirm(false);
+ }
+
+ async function deleteHistory() {
+ if(!conv_to_delete) return;
+
+ const {uid} = conv_to_delete;
+ await idb.deleteOne("chat-history", [{uid}]);
+ await idb.deleteAll("messages", [{'history-uid': uid}]);
+ setHistory(history.filter(e=>e.uid !== uid));
+ uid === chat.uid && selectChat({});
+ resetRequestDelete();
+ }
+
+ async function updateTitle(title) {
+ await idb.updateOne("chat-history", {title}, [{uid: chat.uid}])
+
+ selectChat({
+ ...chat, title: title
+ })
- idb.updateOne('chat-history', {client}, [{uid:chat.uid}])
+ let history_cp = [...history];
+ history_cp[
+ history_cp.findIndex(e=>e.uid === chat.uid)
+ ].title = title;
+ setHistory(history_cp);
}
+ useEffect(()=>{
+ if(dialogRef.current) {
+ if(showConfirm) dialogRef.current.showModal();
+ else dialogRef.current.close();
+ }
+ }, [showConfirm])
+
+ useEffect(()=>{
+ conv_to_delete && toggleConfirm(true);
+ }, [conv_to_delete])
+
return (
-
-
+
+
+
)
}
\ No newline at end of file
diff --git a/src/styles/chat.css b/src/styles/chat.css
index dd9b4dc..7a056eb 100644
--- a/src/styles/chat.css
+++ b/src/styles/chat.css
@@ -7,6 +7,7 @@
display: block;
--tickets-width: 250px;
+ --ticket-height: 50px;
}
.chat > .tickets {
@@ -35,7 +36,7 @@
background-color: var(--ticket-bg-color);
width: 100%;
padding: 0px 10px;
- height: 50px;
+ height: var(--ticket-height);
align-content: center;
font-size: 16px;
position: relative;
@@ -59,6 +60,46 @@
background-image: linear-gradient(to right, transparent, var(--ticket-bg-color) 70%);
}
+.chat > .tickets > .ticket > .delete-icon {
+ position: absolute;
+ right: 7px;
+ --size: calc(var(--ticket-height) * 0.3);
+ top: calc(var(--ticket-height) * 0.35);
+ width: var(--size);
+ height: var(--size);
+ z-index: 3;
+ color: gray;
+ display: none;
+}
+.chat > .tickets > .ticket:hover > .delete-icon {
+ display: block;
+}
+.chat > .tickets > .ticket:hover > .delete-icon:hover {
+ color: black;
+}
+
+.chat > dialog {
+ border: 2px solid black;
+ padding: 30px;
+ border-radius: 10px;
+ text-align: center;
+}
+.chat > dialog:focus {
+ outline: none;
+}
+
+.chat > dialog > .button {
+ border-bottom: 1px solid;
+ width: fit-content;
+ margin: auto;
+ margin-top: 7px;
+ padding: 0px 7px;
+ color: rgb(70, 70, 70);
+}
+.chat > dialog > .button:hover {
+ color: black;
+}
+
.chat > .conversation-main {
width: calc(100% - var(--tickets-width));
position: absolute;
@@ -70,9 +111,10 @@
background-repeat: no-repeat;
background-size: cover;
+ --title-bar-height: 40px;
--send-input-height: 60px;
--elem-size: 40px;
- --bubbles-height: calc(100% - var(--send-input-height) - 10px);
+ --bubbles-height: calc(100% - var(--send-input-height) - var(--title-bar-height) - 10px);
}
.chat > .conversation-main::before {
@@ -101,6 +143,39 @@
background-image: none;
}
+.chat > .conversation-main > .title-bar {
+ background-color: var(--normal-bg-color);
+ width: 100%;
+ height: var(--title-bar-height);
+ padding: 0px 15px;
+ align-content: center;
+ font-size: 15px;
+ font-weight: bold;
+ color: rgb(50, 50, 50);
+
+ --elem-height: calc(var(--title-bar-height) - 14px);
+}
+
+.chat > .conversation-main > .title-bar > form {
+ display: flex;
+}
+
+.chat > .conversation-main > .title-bar .edit-title {
+ border: none;
+ background-color: transparent;
+ border-bottom: 1px solid gray;
+ padding: 0px 5px;
+ width: 100%;
+ font-size: 15px;
+ height: var(--elem-height);
+}
+
+.chat > .conversation-main > .title-bar .btn {
+ height: var(--elem-height);
+ width: var(--elem-height);
+ margin-left: 15px;
+}
+
.chat > .conversation-main > .bubbles {
height: var(--bubbles-height);
width: 100%;
@@ -108,7 +183,7 @@
margin-bottom: 10px;
position: absolute;
left: 0;
- top: 0;
+ top: var(--title-bar-height);
padding: 20px 10px;
}
diff --git a/src/utils/idb/settings.js b/src/utils/idb/settings.js
index 17bf3de..47c3a2b 100644
--- a/src/utils/idb/settings.js
+++ b/src/utils/idb/settings.js
@@ -31,12 +31,5 @@ export const versions = [
{ name: 'json' }
]
}
- },
- {
- 'chat-history': {
- columns: [
- { 'name': "client" }
- ]
- }
}
]
\ No newline at end of file
diff --git a/src/utils/workers/openai-worker.js b/src/utils/workers/openai-worker.js
index 0eb0c26..99ff0e1 100644
--- a/src/utils/workers/openai-worker.js
+++ b/src/utils/workers/openai-worker.js
@@ -96,6 +96,7 @@ export async function chatCompletions(messages, cb = null) {
if(chunk.choices[0].finish_reason) break;
if(abort_signal) break;
}
+ cb && cb(response_text, true);
} catch(error) {
console.error(error);
cb && cb(`**${error.name}**:\n\`\`\`\n${error.message}\n\`\`\``, true);