coyotte508 HF staff commited on
Commit
fc15a4c
1 Parent(s): 5f9acb9

Share convos (#35)

Browse files
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": "3.3.4",
2584
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
2585
- "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
 
 
 
 
 
 
2586
  "bin": {
2587
- "nanoid": "bin/nanoid.cjs"
2588
  },
2589
  "engines": {
2590
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
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/Types';
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
- bind:this={el}
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} />