Commit
•
fc15a4c
1
Parent(s):
5f9acb9
Share convos (#35)
Browse files- package-lock.json +29 -5
- package.json +1 -0
- src/lib/components/chat/ChatMessage.svelte +2 -2
- src/lib/server/database.ts +1 -0
- src/lib/types/Conversation.ts +2 -0
- src/routes/+layout.svelte +39 -1
- src/routes/conversation/[id]/+server.ts +0 -2
- src/routes/conversation/[id]/share/+server.ts +52 -0
- src/routes/r/[id]/+page.server.ts +16 -0
- src/routes/r/[id]/+page.svelte +8 -0
package-lock.json
CHANGED
@@ -14,6 +14,7 @@
|
|
14 |
"highlight.js": "^11.7.0",
|
15 |
"marked": "^4.3.0",
|
16 |
"mongodb": "^5.3.0",
|
|
|
17 |
"postcss": "^8.4.21",
|
18 |
"tailwind-scrollbar": "^3.0.0",
|
19 |
"tailwindcss": "^3.3.1"
|
@@ -2580,14 +2581,20 @@
|
|
2580 |
}
|
2581 |
},
|
2582 |
"node_modules/nanoid": {
|
2583 |
-
"version": "
|
2584 |
-
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-
|
2585 |
-
"integrity": "sha512-
|
|
|
|
|
|
|
|
|
|
|
|
|
2586 |
"bin": {
|
2587 |
-
"nanoid": "bin/nanoid.
|
2588 |
},
|
2589 |
"engines": {
|
2590 |
-
"node": "^
|
2591 |
}
|
2592 |
},
|
2593 |
"node_modules/natural-compare": {
|
@@ -2898,6 +2905,23 @@
|
|
2898 |
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
2899 |
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
2900 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2901 |
"node_modules/prelude-ls": {
|
2902 |
"version": "1.2.1",
|
2903 |
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
|
|
14 |
"highlight.js": "^11.7.0",
|
15 |
"marked": "^4.3.0",
|
16 |
"mongodb": "^5.3.0",
|
17 |
+
"nanoid": "^4.0.2",
|
18 |
"postcss": "^8.4.21",
|
19 |
"tailwind-scrollbar": "^3.0.0",
|
20 |
"tailwindcss": "^3.3.1"
|
|
|
2581 |
}
|
2582 |
},
|
2583 |
"node_modules/nanoid": {
|
2584 |
+
"version": "4.0.2",
|
2585 |
+
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz",
|
2586 |
+
"integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==",
|
2587 |
+
"funding": [
|
2588 |
+
{
|
2589 |
+
"type": "github",
|
2590 |
+
"url": "https://github.com/sponsors/ai"
|
2591 |
+
}
|
2592 |
+
],
|
2593 |
"bin": {
|
2594 |
+
"nanoid": "bin/nanoid.js"
|
2595 |
},
|
2596 |
"engines": {
|
2597 |
+
"node": "^14 || ^16 || >=18"
|
2598 |
}
|
2599 |
},
|
2600 |
"node_modules/natural-compare": {
|
|
|
2905 |
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
2906 |
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
2907 |
},
|
2908 |
+
"node_modules/postcss/node_modules/nanoid": {
|
2909 |
+
"version": "3.3.6",
|
2910 |
+
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
|
2911 |
+
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
|
2912 |
+
"funding": [
|
2913 |
+
{
|
2914 |
+
"type": "github",
|
2915 |
+
"url": "https://github.com/sponsors/ai"
|
2916 |
+
}
|
2917 |
+
],
|
2918 |
+
"bin": {
|
2919 |
+
"nanoid": "bin/nanoid.cjs"
|
2920 |
+
},
|
2921 |
+
"engines": {
|
2922 |
+
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
2923 |
+
}
|
2924 |
+
},
|
2925 |
"node_modules/prelude-ls": {
|
2926 |
"version": "1.2.1",
|
2927 |
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
package.json
CHANGED
@@ -37,6 +37,7 @@
|
|
37 |
"highlight.js": "^11.7.0",
|
38 |
"marked": "^4.3.0",
|
39 |
"mongodb": "^5.3.0",
|
|
|
40 |
"postcss": "^8.4.21",
|
41 |
"tailwind-scrollbar": "^3.0.0",
|
42 |
"tailwindcss": "^3.3.1"
|
|
|
37 |
"highlight.js": "^11.7.0",
|
38 |
"marked": "^4.3.0",
|
39 |
"mongodb": "^5.3.0",
|
40 |
+
"nanoid": "^4.0.2",
|
41 |
"postcss": "^8.4.21",
|
42 |
"tailwind-scrollbar": "^3.0.0",
|
43 |
"tailwindcss": "^3.3.1"
|
src/lib/components/chat/ChatMessage.svelte
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
<script lang="ts">
|
2 |
import { marked } from 'marked';
|
3 |
-
import type { Message } from '$lib/
|
4 |
import { afterUpdate } from 'svelte';
|
5 |
|
6 |
import CopyToClipBoardBtn from '../CopyToClipBoardBtn.svelte';
|
@@ -80,7 +80,7 @@
|
|
80 |
/>
|
81 |
<div
|
82 |
class="group relative rounded-2xl px-5 py-3.5 border border-gray-100 bg-gradient-to-br from-gray-50 dark:from-gray-800/40 dark:border-gray-800 prose text-gray-600 dark:text-gray-300"
|
83 |
-
|
84 |
>
|
85 |
{@html html}
|
86 |
</div>
|
|
|
1 |
<script lang="ts">
|
2 |
import { marked } from 'marked';
|
3 |
+
import type { Message } from '$lib/types/Message';
|
4 |
import { afterUpdate } from 'svelte';
|
5 |
|
6 |
import CopyToClipBoardBtn from '../CopyToClipBoardBtn.svelte';
|
|
|
80 |
/>
|
81 |
<div
|
82 |
class="group relative rounded-2xl px-5 py-3.5 border border-gray-100 bg-gradient-to-br from-gray-50 dark:from-gray-800/40 dark:border-gray-800 prose text-gray-600 dark:text-gray-300"
|
83 |
+
bind:this={el}
|
84 |
>
|
85 |
{@html html}
|
86 |
</div>
|
src/lib/server/database.ts
CHANGED
@@ -17,4 +17,5 @@ export const collections = { conversations };
|
|
17 |
|
18 |
client.on('open', () => {
|
19 |
conversations.createIndex({ sessionId: 1, updatedAt: -1 });
|
|
|
20 |
});
|
|
|
17 |
|
18 |
client.on('open', () => {
|
19 |
conversations.createIndex({ sessionId: 1, updatedAt: -1 });
|
20 |
+
conversations.createIndex({ 'shares.id': 1 }, { unique: true, sparse: true });
|
21 |
});
|
src/lib/types/Conversation.ts
CHANGED
@@ -8,6 +8,8 @@ export interface Conversation {
|
|
8 |
title: string;
|
9 |
messages: Message[];
|
10 |
|
|
|
|
|
11 |
createdAt: Date;
|
12 |
updatedAt: Date;
|
13 |
}
|
|
|
8 |
title: string;
|
9 |
messages: Message[];
|
10 |
|
11 |
+
shares?: Array<{ id: string; msgCount: number }>;
|
12 |
+
|
13 |
createdAt: Date;
|
14 |
updatedAt: Date;
|
15 |
}
|
src/routes/+layout.svelte
CHANGED
@@ -14,6 +14,37 @@
|
|
14 |
localStorage.theme = 'dark';
|
15 |
}
|
16 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
</script>
|
18 |
|
19 |
<div
|
@@ -34,9 +65,16 @@
|
|
34 |
{#each data.conversations as conv}
|
35 |
<a
|
36 |
href="/conversation/{conv.id}"
|
37 |
-
class="truncate py-3 px-3 rounded-lg flex-none text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700"
|
38 |
>
|
39 |
{conv.title}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
</a>
|
41 |
{/each}
|
42 |
</div>
|
|
|
14 |
localStorage.theme = 'dark';
|
15 |
}
|
16 |
}
|
17 |
+
|
18 |
+
async function shareConversation(id: string, title: string) {
|
19 |
+
try {
|
20 |
+
const res = await fetch(`/conversation/${id}/share`, {
|
21 |
+
method: 'POST',
|
22 |
+
headers: {
|
23 |
+
'Content-Type': 'application/json'
|
24 |
+
}
|
25 |
+
});
|
26 |
+
|
27 |
+
if (!res.ok) {
|
28 |
+
alert('Error while sharing conversation: ' + (await res.text()));
|
29 |
+
return;
|
30 |
+
}
|
31 |
+
|
32 |
+
const { url } = await res.json();
|
33 |
+
|
34 |
+
if (navigator.share) {
|
35 |
+
navigator.share({
|
36 |
+
title,
|
37 |
+
text: 'Share this chat with others',
|
38 |
+
url
|
39 |
+
});
|
40 |
+
} else {
|
41 |
+
prompt('Share this link with your friends:', url);
|
42 |
+
}
|
43 |
+
} catch (err) {
|
44 |
+
console.error(err);
|
45 |
+
alert('Error while sharing conversation: ' + err);
|
46 |
+
}
|
47 |
+
}
|
48 |
</script>
|
49 |
|
50 |
<div
|
|
|
65 |
{#each data.conversations as conv}
|
66 |
<a
|
67 |
href="/conversation/{conv.id}"
|
68 |
+
class="truncate py-3 px-3 rounded-lg flex-none text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 flex justify-between items-center"
|
69 |
>
|
70 |
{conv.title}
|
71 |
+
|
72 |
+
<button
|
73 |
+
class="bg-white rounded border-black px-2 py-1 border-[1px] border-solid"
|
74 |
+
on:click|preventDefault={() => shareConversation(conv.id, conv.title)}
|
75 |
+
>
|
76 |
+
Share
|
77 |
+
</button>
|
78 |
</a>
|
79 |
{/each}
|
80 |
</div>
|
src/routes/conversation/[id]/+server.ts
CHANGED
@@ -44,8 +44,6 @@ export async function POST({ request, fetch, locals, params }) {
|
|
44 |
|
45 |
messages.push({ from: 'assistant', content: generated_text });
|
46 |
|
47 |
-
console.log('updating conversation', convId, messages);
|
48 |
-
|
49 |
await collections.conversations.updateOne(
|
50 |
{
|
51 |
_id: convId
|
|
|
44 |
|
45 |
messages.push({ from: 'assistant', content: generated_text });
|
46 |
|
|
|
|
|
47 |
await collections.conversations.updateOne(
|
48 |
{
|
49 |
_id: convId
|
src/routes/conversation/[id]/share/+server.ts
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { collections } from '$lib/server/database.js';
|
2 |
+
import { error } from '@sveltejs/kit';
|
3 |
+
import { ObjectId } from 'mongodb';
|
4 |
+
import { nanoid } from 'nanoid';
|
5 |
+
|
6 |
+
export async function POST({ params, url, locals }) {
|
7 |
+
const conversation = await collections.conversations.findOne({
|
8 |
+
_id: new ObjectId(params.id),
|
9 |
+
sessionId: locals.sessionId
|
10 |
+
});
|
11 |
+
|
12 |
+
if (!conversation) {
|
13 |
+
throw error(404, 'Conversation not found');
|
14 |
+
}
|
15 |
+
|
16 |
+
const shares = conversation.shares || [];
|
17 |
+
|
18 |
+
const existingShare = shares.find((share) => share.msgCount === conversation.messages.length);
|
19 |
+
|
20 |
+
if (existingShare) {
|
21 |
+
return new Response(
|
22 |
+
JSON.stringify({
|
23 |
+
url: url.origin + `/r/${existingShare.id}`
|
24 |
+
}),
|
25 |
+
{ headers: { 'Content-Type': 'application/json' } }
|
26 |
+
);
|
27 |
+
}
|
28 |
+
|
29 |
+
const share = {
|
30 |
+
id: nanoid(7),
|
31 |
+
msgCount: conversation.messages.length
|
32 |
+
};
|
33 |
+
|
34 |
+
await collections.conversations.updateOne(
|
35 |
+
{
|
36 |
+
_id: conversation._id
|
37 |
+
},
|
38 |
+
{
|
39 |
+
$set: {
|
40 |
+
shares: [...shares, share],
|
41 |
+
updatedAt: new Date()
|
42 |
+
}
|
43 |
+
}
|
44 |
+
);
|
45 |
+
|
46 |
+
return new Response(
|
47 |
+
JSON.stringify({
|
48 |
+
url: url.origin.replace('huggingface.co', 'hf.co') + `/r/${share.id}`
|
49 |
+
}),
|
50 |
+
{ headers: { 'Content-Type': 'application/json' } }
|
51 |
+
);
|
52 |
+
}
|
src/routes/r/[id]/+page.server.ts
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { collections } from '$lib/server/database.js';
|
2 |
+
import { error } from '@sveltejs/kit';
|
3 |
+
|
4 |
+
export async function load({ params }) {
|
5 |
+
const conversation = await collections.conversations.findOne({
|
6 |
+
'shares.id': params.id
|
7 |
+
});
|
8 |
+
|
9 |
+
if (!conversation) {
|
10 |
+
throw error(404, 'Conversation not found');
|
11 |
+
}
|
12 |
+
|
13 |
+
return {
|
14 |
+
messages: conversation.messages
|
15 |
+
};
|
16 |
+
}
|
src/routes/r/[id]/+page.svelte
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import ChatWindow from '$lib/components/chat/ChatWindow.svelte';
|
3 |
+
import type { PageData } from './$types';
|
4 |
+
|
5 |
+
export let data: PageData;
|
6 |
+
</script>
|
7 |
+
|
8 |
+
<ChatWindow disabled={true} messages={data.messages} />
|