✨ Title edit (#135)
Browse filesCo-authored-by: Victor Mustar <[email protected]>
src/lib/components/NavMenu.svelte
CHANGED
|
@@ -5,7 +5,7 @@
|
|
| 5 |
|
| 6 |
import Logo from "$lib/components/icons/Logo.svelte";
|
| 7 |
import CarbonTrashCan from "~icons/carbon/trash-can";
|
| 8 |
-
import
|
| 9 |
|
| 10 |
import { switchTheme } from "$lib/switchTheme";
|
| 11 |
import { PUBLIC_ORIGIN } from "$env/static/public";
|
|
@@ -13,6 +13,7 @@
|
|
| 13 |
const dispatch = createEventDispatcher<{
|
| 14 |
shareConversation: { id: string; title: string };
|
| 15 |
deleteConversation: string;
|
|
|
|
| 16 |
}>();
|
| 17 |
|
| 18 |
export let conversations: Array<{
|
|
@@ -36,7 +37,7 @@
|
|
| 36 |
<div
|
| 37 |
class="scrollbar-custom flex flex-col gap-1 overflow-y-auto rounded-r-xl bg-gradient-to-l from-gray-50 px-3 pb-3 pt-2 dark:from-gray-800/30"
|
| 38 |
>
|
| 39 |
-
{#each conversations as conv}
|
| 40 |
<a
|
| 41 |
data-sveltekit-noscroll
|
| 42 |
href="{base}/conversation/{conv.id}"
|
|
@@ -50,11 +51,14 @@
|
|
| 50 |
<button
|
| 51 |
type="button"
|
| 52 |
class="flex h-5 w-5 items-center justify-center rounded md:hidden md:group-hover:flex"
|
| 53 |
-
title="
|
| 54 |
-
on:click|preventDefault={() =>
|
| 55 |
-
|
|
|
|
|
|
|
|
|
|
| 56 |
>
|
| 57 |
-
<
|
| 58 |
</button>
|
| 59 |
|
| 60 |
<button
|
|
|
|
| 5 |
|
| 6 |
import Logo from "$lib/components/icons/Logo.svelte";
|
| 7 |
import CarbonTrashCan from "~icons/carbon/trash-can";
|
| 8 |
+
import CarbonEdit from "~icons/carbon/edit";
|
| 9 |
|
| 10 |
import { switchTheme } from "$lib/switchTheme";
|
| 11 |
import { PUBLIC_ORIGIN } from "$env/static/public";
|
|
|
|
| 13 |
const dispatch = createEventDispatcher<{
|
| 14 |
shareConversation: { id: string; title: string };
|
| 15 |
deleteConversation: string;
|
| 16 |
+
editConversationTitle: { id: string; title: string };
|
| 17 |
}>();
|
| 18 |
|
| 19 |
export let conversations: Array<{
|
|
|
|
| 37 |
<div
|
| 38 |
class="scrollbar-custom flex flex-col gap-1 overflow-y-auto rounded-r-xl bg-gradient-to-l from-gray-50 px-3 pb-3 pt-2 dark:from-gray-800/30"
|
| 39 |
>
|
| 40 |
+
{#each conversations as conv (conv.id)}
|
| 41 |
<a
|
| 42 |
data-sveltekit-noscroll
|
| 43 |
href="{base}/conversation/{conv.id}"
|
|
|
|
| 51 |
<button
|
| 52 |
type="button"
|
| 53 |
class="flex h-5 w-5 items-center justify-center rounded md:hidden md:group-hover:flex"
|
| 54 |
+
title="Edit conversation title"
|
| 55 |
+
on:click|preventDefault={() => {
|
| 56 |
+
const newTitle = prompt("Edit this conversation title:", conv.title);
|
| 57 |
+
if (!newTitle) return;
|
| 58 |
+
dispatch("editConversationTitle", { id: conv.id, title: newTitle });
|
| 59 |
+
}}
|
| 60 |
>
|
| 61 |
+
<CarbonEdit class="text-xs text-gray-400 hover:text-gray-500 dark:hover:text-gray-300" />
|
| 62 |
</button>
|
| 63 |
|
| 64 |
<button
|
src/routes/+layout.svelte
CHANGED
|
@@ -62,6 +62,28 @@
|
|
| 62 |
}
|
| 63 |
}
|
| 64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
onDestroy(() => {
|
| 66 |
clearTimeout(errorToastTimeout);
|
| 67 |
});
|
|
@@ -91,6 +113,7 @@
|
|
| 91 |
conversations={data.conversations}
|
| 92 |
on:shareConversation={(ev) => shareConversation(ev.detail.id, ev.detail.title)}
|
| 93 |
on:deleteConversation={(ev) => deleteConversation(ev.detail)}
|
|
|
|
| 94 |
/>
|
| 95 |
</MobileNav>
|
| 96 |
<nav class="grid max-h-screen grid-cols-1 grid-rows-[auto,1fr,auto] max-md:hidden">
|
|
@@ -98,6 +121,7 @@
|
|
| 98 |
conversations={data.conversations}
|
| 99 |
on:shareConversation={(ev) => shareConversation(ev.detail.id, ev.detail.title)}
|
| 100 |
on:deleteConversation={(ev) => deleteConversation(ev.detail)}
|
|
|
|
| 101 |
/>
|
| 102 |
</nav>
|
| 103 |
{#if currentError}
|
|
|
|
| 62 |
}
|
| 63 |
}
|
| 64 |
|
| 65 |
+
async function editConversationTitle(id: string, title: string) {
|
| 66 |
+
try {
|
| 67 |
+
const res = await fetch(`${base}/conversation/${id}`, {
|
| 68 |
+
method: "PATCH",
|
| 69 |
+
headers: {
|
| 70 |
+
"Content-Type": "application/json",
|
| 71 |
+
},
|
| 72 |
+
body: JSON.stringify({ title }),
|
| 73 |
+
});
|
| 74 |
+
|
| 75 |
+
if (!res.ok) {
|
| 76 |
+
$error = "Error while editing title, try again.";
|
| 77 |
+
return;
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
await invalidate(UrlDependency.ConversationList);
|
| 81 |
+
} catch (err) {
|
| 82 |
+
console.error(err);
|
| 83 |
+
$error = String(err);
|
| 84 |
+
}
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
onDestroy(() => {
|
| 88 |
clearTimeout(errorToastTimeout);
|
| 89 |
});
|
|
|
|
| 113 |
conversations={data.conversations}
|
| 114 |
on:shareConversation={(ev) => shareConversation(ev.detail.id, ev.detail.title)}
|
| 115 |
on:deleteConversation={(ev) => deleteConversation(ev.detail)}
|
| 116 |
+
on:editConversationTitle={(ev) => editConversationTitle(ev.detail.id, ev.detail.title)}
|
| 117 |
/>
|
| 118 |
</MobileNav>
|
| 119 |
<nav class="grid max-h-screen grid-cols-1 grid-rows-[auto,1fr,auto] max-md:hidden">
|
|
|
|
| 121 |
conversations={data.conversations}
|
| 122 |
on:shareConversation={(ev) => shareConversation(ev.detail.id, ev.detail.title)}
|
| 123 |
on:deleteConversation={(ev) => deleteConversation(ev.detail)}
|
| 124 |
+
on:editConversationTitle={(ev) => editConversationTitle(ev.detail.id, ev.detail.title)}
|
| 125 |
/>
|
| 126 |
</nav>
|
| 127 |
{#if currentError}
|
src/routes/conversation/[id]/+server.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { trimSuffix } from "$lib/utils/trimSuffix.js";
|
|
| 11 |
import type { TextGenerationStreamOutput } from "@huggingface/inference";
|
| 12 |
import { error } from "@sveltejs/kit";
|
| 13 |
import { ObjectId } from "mongodb";
|
|
|
|
| 14 |
|
| 15 |
export async function POST({ request, fetch, locals, params }) {
|
| 16 |
// todo: add validation on params.id
|
|
@@ -164,3 +165,28 @@ async function parseGeneratedText(
|
|
| 164 |
|
| 165 |
return res;
|
| 166 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
import type { TextGenerationStreamOutput } from "@huggingface/inference";
|
| 12 |
import { error } from "@sveltejs/kit";
|
| 13 |
import { ObjectId } from "mongodb";
|
| 14 |
+
import { z } from "zod";
|
| 15 |
|
| 16 |
export async function POST({ request, fetch, locals, params }) {
|
| 17 |
// todo: add validation on params.id
|
|
|
|
| 165 |
|
| 166 |
return res;
|
| 167 |
}
|
| 168 |
+
|
| 169 |
+
export async function PATCH({request, locals, params}) {
|
| 170 |
+
const {title} = z.object({title: z.string().trim().min(1).max(100)}).parse(await request.json())
|
| 171 |
+
|
| 172 |
+
const convId = new ObjectId(params.id);
|
| 173 |
+
|
| 174 |
+
const conv = await collections.conversations.findOne({
|
| 175 |
+
_id: convId,
|
| 176 |
+
sessionId: locals.sessionId,
|
| 177 |
+
});
|
| 178 |
+
|
| 179 |
+
if (!conv) {
|
| 180 |
+
throw error(404, "Conversation not found");
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
await collections.conversations.updateOne({
|
| 184 |
+
_id: convId,
|
| 185 |
+
}, {
|
| 186 |
+
$set: {
|
| 187 |
+
title,
|
| 188 |
+
}
|
| 189 |
+
});
|
| 190 |
+
|
| 191 |
+
return new Response();
|
| 192 |
+
}
|