radames commited on
Commit
effb90e
·
1 Parent(s): 5bb150a
frontend/.env.example ADDED
@@ -0,0 +1 @@
 
 
1
+ PUBLIC_BACKEND_WS_URL="wss://ysharma-instructpix2pix-chatbot.hf.space/queue/join"
frontend/package-lock.json CHANGED
@@ -7,6 +7,10 @@
7
  "": {
8
  "name": "frontend",
9
  "version": "0.0.1",
 
 
 
 
10
  "devDependencies": {
11
  "@sveltejs/adapter-static": "^1.0.5",
12
  "@sveltejs/kit": "^1.0.0",
@@ -1868,6 +1872,11 @@
1868
  "node": ">= 4"
1869
  }
1870
  },
 
 
 
 
 
1871
  "node_modules/import-fresh": {
1872
  "version": "3.3.0",
1873
  "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -2034,6 +2043,14 @@
2034
  "node": ">= 0.8.0"
2035
  }
2036
  },
 
 
 
 
 
 
 
 
2037
  "node_modules/lilconfig": {
2038
  "version": "2.0.6",
2039
  "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz",
@@ -2043,6 +2060,14 @@
2043
  "node": ">=10"
2044
  }
2045
  },
 
 
 
 
 
 
 
 
2046
  "node_modules/locate-path": {
2047
  "version": "6.0.0",
2048
  "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -2189,15 +2214,14 @@
2189
  "dev": true
2190
  },
2191
  "node_modules/nanoid": {
2192
- "version": "3.3.4",
2193
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
2194
- "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
2195
- "dev": true,
2196
  "bin": {
2197
- "nanoid": "bin/nanoid.cjs"
2198
  },
2199
  "engines": {
2200
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
2201
  }
2202
  },
2203
  "node_modules/natural-compare": {
@@ -2509,6 +2533,18 @@
2509
  "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
2510
  "dev": true
2511
  },
 
 
 
 
 
 
 
 
 
 
 
 
2512
  "node_modules/prelude-ls": {
2513
  "version": "1.2.1",
2514
  "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
 
7
  "": {
8
  "name": "frontend",
9
  "version": "0.0.1",
10
+ "dependencies": {
11
+ "localforage": "^1.10.0",
12
+ "nanoid": "^4.0.0"
13
+ },
14
  "devDependencies": {
15
  "@sveltejs/adapter-static": "^1.0.5",
16
  "@sveltejs/kit": "^1.0.0",
 
1872
  "node": ">= 4"
1873
  }
1874
  },
1875
+ "node_modules/immediate": {
1876
+ "version": "3.0.6",
1877
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
1878
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
1879
+ },
1880
  "node_modules/import-fresh": {
1881
  "version": "3.3.0",
1882
  "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
 
2043
  "node": ">= 0.8.0"
2044
  }
2045
  },
2046
+ "node_modules/lie": {
2047
+ "version": "3.1.1",
2048
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
2049
+ "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
2050
+ "dependencies": {
2051
+ "immediate": "~3.0.5"
2052
+ }
2053
+ },
2054
  "node_modules/lilconfig": {
2055
  "version": "2.0.6",
2056
  "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz",
 
2060
  "node": ">=10"
2061
  }
2062
  },
2063
+ "node_modules/localforage": {
2064
+ "version": "1.10.0",
2065
+ "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
2066
+ "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
2067
+ "dependencies": {
2068
+ "lie": "3.1.1"
2069
+ }
2070
+ },
2071
  "node_modules/locate-path": {
2072
  "version": "6.0.0",
2073
  "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
 
2214
  "dev": true
2215
  },
2216
  "node_modules/nanoid": {
2217
+ "version": "4.0.0",
2218
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.0.tgz",
2219
+ "integrity": "sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==",
 
2220
  "bin": {
2221
+ "nanoid": "bin/nanoid.js"
2222
  },
2223
  "engines": {
2224
+ "node": "^14 || ^16 || >=18"
2225
  }
2226
  },
2227
  "node_modules/natural-compare": {
 
2533
  "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
2534
  "dev": true
2535
  },
2536
+ "node_modules/postcss/node_modules/nanoid": {
2537
+ "version": "3.3.4",
2538
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
2539
+ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
2540
+ "dev": true,
2541
+ "bin": {
2542
+ "nanoid": "bin/nanoid.cjs"
2543
+ },
2544
+ "engines": {
2545
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
2546
+ }
2547
+ },
2548
  "node_modules/prelude-ls": {
2549
  "version": "1.2.1",
2550
  "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
frontend/package.json CHANGED
@@ -30,5 +30,9 @@
30
  "typescript": "^4.9.3",
31
  "vite": "^4.0.0"
32
  },
33
- "type": "module"
 
 
 
 
34
  }
 
30
  "typescript": "^4.9.3",
31
  "vite": "^4.0.0"
32
  },
33
+ "type": "module",
34
+ "dependencies": {
35
+ "localforage": "^1.10.0",
36
+ "nanoid": "^4.0.0"
37
+ }
38
  }
frontend/postcss.config.cjs CHANGED
@@ -1,6 +1,6 @@
1
  module.exports = {
2
- plugins: {
3
- tailwindcss: {},
4
- autoprefixer: {},
5
- },
6
- }
 
1
  module.exports = {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {}
5
+ }
6
+ };
frontend/src/app.css CHANGED
@@ -1,3 +1,3 @@
1
  @tailwind base;
2
  @tailwind components;
3
- @tailwind utilities;
 
1
  @tailwind base;
2
  @tailwind components;
3
+ @tailwind utilities;
frontend/src/app.html CHANGED
@@ -1,17 +1,14 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
 
 
 
 
 
 
 
3
 
4
- <head>
5
- <meta charset="utf-8" />
6
- <link rel="icon" href="%sveltekit.assets%/favicon.png" />
7
- <meta name="viewport" content="width=device-width" />
8
- <script
9
- src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.1/iframeResizer.contentWindow.min.js"></script>
10
- %sveltekit.head%
11
- </head>
12
-
13
- <body class="dark:bg-[rgb(11,15,25)] bg-white dark:text-white text-black">
14
- <div>%sveltekit.body%</div>
15
- </body>
16
-
17
- </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <link rel="icon" href="%sveltekit.assets%/favicon.png" />
6
+ <meta name="viewport" content="width=device-width" />
7
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.1/iframeResizer.contentWindow.min.js"></script>
8
+ %sveltekit.head%
9
+ </head>
10
 
11
+ <body class="dark:bg-[rgb(11,15,25)] bg-white dark:text-white text-black">
12
+ <div>%sveltekit.body%</div>
13
+ </body>
14
+ </html>
 
 
 
 
 
 
 
 
 
 
frontend/src/lib/App.svelte ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import { PUBLIC_BACKEND_WS_URL } from '$env/static/public';
3
+ import { onMount } from 'svelte';
4
+ import { nanoid } from 'nanoid';
5
+ import { chatsStore, selectedChatId, loadingState } from '$lib/store';
6
+ import type { Message, Chat } from '$lib/types';
7
+ import { MessageType, Sender } from '$lib/types';
8
+ import ChatInput from '$lib/ChatInput.svelte';
9
+ import ChatMessage from '$lib/ChatMessage.svelte';
10
+ import ChatNewBtn from '$lib/ChatNewBtn.svelte';
11
+
12
+ $: isLoading = false;
13
+
14
+ function clearStateMsg(t = 5000) {
15
+ setTimeout(() => {
16
+ $loadingState = '';
17
+ }, t);
18
+ }
19
+ onMount(() => {
20
+ // generateImage();
21
+ });
22
+
23
+ $: chatData = $chatsStore.find((chat) => chat.id === $selectedChatId);
24
+ $: messages = chatData?.messages || [];
25
+
26
+ $: console.log($chatsStore);
27
+
28
+ function newChat() {
29
+ const chatId = nanoid();
30
+ const chat: Chat = {
31
+ id: chatId,
32
+ blurb: `New Chat - ${chatId}`,
33
+ messages: [],
34
+ timestamp: new Date().getTime()
35
+ };
36
+ $chatsStore = [chat].concat($chatsStore);
37
+ $selectedChatId = chat.id;
38
+ }
39
+
40
+ function submitMessage(event: CustomEvent) {
41
+ const value = event.detail;
42
+ const message: Message = {
43
+ sender: Sender.USER,
44
+ id: nanoid(),
45
+ type: MessageType.TEXT,
46
+ content: value,
47
+ timestamp: new Date().getTime()
48
+ };
49
+ $chatsStore = $chatsStore.map((chat) => {
50
+ if (chat.id === $selectedChatId) {
51
+ chat.messages.push(message);
52
+ }
53
+ return chat;
54
+ });
55
+ }
56
+ const timeFormater = new Intl.DateTimeFormat('en-US', {
57
+ day: 'numeric',
58
+ month: 'short',
59
+ hour: 'numeric',
60
+ minute: 'numeric'
61
+ }).format;
62
+ async function generateImage() {
63
+ if (isLoading) return;
64
+
65
+ $loadingState = 'Pending';
66
+
67
+ const sessionHash = crypto.randomUUID();
68
+ const hashpayload = {
69
+ fn_index: 1,
70
+ session_hash: sessionHash
71
+ };
72
+ const image = '';
73
+ const datapayload = {
74
+ data: [
75
+ 'make him wear shirts', // prompt
76
+ 10.5, // text guidance
77
+ 1.5, // image guidance
78
+ image,
79
+ 15, // steps
80
+ '', // negative promtp,
81
+ 512, // width
82
+ 512, // height
83
+ 0 // seed
84
+ ]
85
+ };
86
+
87
+ const websocket = new WebSocket(PUBLIC_BACKEND_WS_URL);
88
+ // websocket.onopen = async function (event) {
89
+ // websocket.send(JSON.stringify({ hash: sessionHash }));
90
+ // };
91
+ websocket.onclose = (evt) => {
92
+ if (!evt.wasClean) {
93
+ $loadingState = 'Error';
94
+ }
95
+ };
96
+ websocket.onmessage = async function (event) {
97
+ try {
98
+ const data = JSON.parse(event.data);
99
+ $loadingState = '';
100
+ switch (data.msg) {
101
+ case 'send_hash':
102
+ websocket.send(JSON.stringify(hashpayload));
103
+ break;
104
+ case 'send_data':
105
+ $loadingState = 'Sending Data';
106
+ websocket.send(JSON.stringify({ ...hashpayload, ...datapayload }));
107
+ break;
108
+ case 'queue_full':
109
+ $loadingState = 'Queue full';
110
+ websocket.close();
111
+
112
+ return;
113
+ case 'estimation':
114
+ const { rank, queue_size } = data;
115
+ $loadingState = `On queue ${rank}/${queue_size}`;
116
+ break;
117
+ case 'process_generating':
118
+ $loadingState = data.success ? 'Generating' : 'Error';
119
+ break;
120
+ case 'process_completed':
121
+ try {
122
+ console.log(data);
123
+ // const params = data.output.data[0] as {
124
+ // is_nsfw: boolean;
125
+ // image: {
126
+ // url: string;
127
+ // filename: string;
128
+ // };
129
+ // };
130
+ // const isNSWF = params.is_nsfw;
131
+ // if (isNSWF) {
132
+ // throw new Error('NFSW');
133
+ // }
134
+ // // const imgBlob = await base64ToBlob(imgBase64);
135
+ // const promptImgParams = {
136
+ // imgURL: params.image.filename
137
+ // };
138
+ // // const imgURL = await uploadImage(imgBlob, promptImgParams);
139
+ // // $promptImgStorage.set(imageKey, promptImgParams);
140
+ // console.log(params.image.url);
141
+ $loadingState = data.success ? 'Complete' : 'Error';
142
+ clearStateMsg();
143
+ } catch (err) {
144
+ const tError = err as Error;
145
+ $loadingState = tError?.message;
146
+
147
+ clearStateMsg(10000);
148
+ }
149
+ websocket.close();
150
+ return;
151
+ case 'process_starts':
152
+ $loadingState = 'Processing';
153
+ break;
154
+ }
155
+ } catch (e) {
156
+ console.error(e);
157
+ $loadingState = 'Error';
158
+ }
159
+ };
160
+ }
161
+ </script>
162
+
163
+ <div>
164
+ <h1 class="text-2xl">CHATS</h1>
165
+ <div class="grid min-h-[40rem] grid-cols-4">
166
+ <div class="col-span-1 flex flex-col border-r p-4">
167
+ <ChatNewBtn on:click={newChat} />
168
+ <div class="max-h-[40rem] flex flex-col gap-2 overflow-y-scroll">
169
+ {#if $chatsStore.length}
170
+ {#each $chatsStore as chat}
171
+ <button on:click={() => ($selectedChatId = chat.id)}>
172
+ <div
173
+ class="flex h-16 flex-col items-start justify-center rounded-xl bg-gray-100 px-4 text-gray-900
174
+ {chat.id === $selectedChatId ? 'bg-gray-400' : ''}"
175
+ >
176
+ <h3 class="w-full truncate font-semibold">{chat.blurb}</h3>
177
+ <p class="w-full truncate text-sm text-gray-500">
178
+ {timeFormater(new Date(chat.timestamp))}
179
+ </p>
180
+ </div>
181
+ </button>
182
+ {/each}
183
+ {:else}
184
+ <div
185
+ class="flex h-16 flex-col items-start justify-center rounded-xl bg-gray-100 px-4 text-gray-900"
186
+ >
187
+ <h3 class="w-full truncate font-semibold">No chats</h3>
188
+ <p class="w-full truncate text-sm text-gray-500">Start a new Chat!</p>
189
+ </div>
190
+ {/if}
191
+ </div>
192
+ </div>
193
+ <div class="col-span-3 flex flex-col">
194
+ {#each messages as message}
195
+ <ChatMessage {message} />
196
+ {/each}
197
+ <ChatInput on:submitMessage={submitMessage} />
198
+ </div>
199
+ </div>
200
+ </div>
201
+
202
+ <style lang="postcss" scoped>
203
+ </style>
frontend/src/lib/ChatInput.svelte ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import { createEventDispatcher, onMount } from 'svelte';
3
+
4
+ const dispatch = createEventDispatcher();
5
+
6
+ let textInput: string = '';
7
+ let inputEl: HTMLInputElement;
8
+
9
+ function onSubmit(event: Event) {
10
+ event.stopPropagation();
11
+ event.preventDefault();
12
+ event.stopImmediatePropagation();
13
+
14
+ if (textInput.trim() !== '') {
15
+ dispatch('submitMessage', textInput);
16
+ textInput = '';
17
+ }
18
+ }
19
+ onMount(() => {
20
+ inputEl.focus();
21
+ });
22
+ </script>
23
+
24
+ <form class="mt-auto flex w-full gap-2 border-t px-3 pt-4 pb-6" on:submit={onSubmit}>
25
+ <input
26
+ bind:value={textInput}
27
+ bind:this={inputEl}
28
+ on:click|stopPropagation
29
+ type="text"
30
+ class="flex h-11 flex-1 items-center rounded-full border bg-gray-100 px-4 text-black"
31
+ placeholder="Type here"
32
+ title="Type here to send a message"
33
+ />
34
+ <button
35
+ title="Send your message"
36
+ type="submit"
37
+ class="rounded-full bg-black dark:bg-white dark:text-black px-4 font-semibold text-gray-200">Submit</button
38
+ >
39
+ </form>
frontend/src/lib/ChatMessage.svelte ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import type { Message, Chat } from '$lib/types';
3
+ import { MessageType, Sender } from '$lib/types';
4
+
5
+ export let message: Message;
6
+ </script>
7
+
8
+ <div class="flex flex-col gap-4 p-4">
9
+ <img
10
+ class="self-end max-h-[328px] rounded-xl bg-gray-200"
11
+ src="https://huggingface.co/datasets/victor/assets/resolve/main/Frame%201.png"
12
+ alt=""
13
+ />
14
+ <p
15
+ class="self-end rounded-3 -mt-2 rounded-t rounded-b-2xl bg-black dark:bg-white py-2 px-4 text-white dark:text-black"
16
+ >
17
+ {message.content}
18
+ </p>
19
+ <img
20
+ class="max-h-[328px] self-start rounded-xl bg-gray-200"
21
+ src="https://huggingface.co/datasets/victor/assets/resolve/main/Frame%202.png"
22
+ alt=""
23
+ />
24
+ </div>
frontend/src/lib/ChatNewBtn.svelte ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <button
2
+ on:click|preventDefault|stopPropagation
3
+ class="self-end mb-8 flex h-6 w-6 items-center justify-center rounded bg-black dark:bg-white"
4
+ >
5
+ <div class="h-2 w-2 rounded-full bg-white dark:bg-black" />
6
+ </button>
frontend/src/lib/Input ADDED
File without changes
frontend/src/lib/constants.ts ADDED
@@ -0,0 +1 @@
 
 
1
+ export const FRAME_SIZE = 512;
frontend/src/lib/store.ts ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import localforage from 'localforage';
2
+ import { writable } from 'svelte/store';
3
+ import type { ChatData } from './types';
4
+ import { nanoid } from 'nanoid';
5
+
6
+ const intitalChatId = nanoid();
7
+ const initialData: ChatData = [{
8
+ id: intitalChatId,
9
+ messages: [],
10
+ blurb: `New Chat - ${intitalChatId}`,
11
+ timestamp: new Date().getTime()
12
+ }];
13
+
14
+
15
+ const loadingState = writable<string>('');
16
+ const chatsStore = writable<ChatData>(initialData);
17
+ const selectedChatId = writable<string>(intitalChatId);
18
+
19
+ localforage.config({
20
+ name: 'Pix2PixChat',
21
+ storeName: 'chatsStore'
22
+ });
23
+
24
+ localforage.getItem<ChatData>('chatsStore').then((value) => {
25
+ if (value) {
26
+ chatsStore.set(value);
27
+ } else {
28
+ chatsStore.set(initialData);
29
+ }
30
+ });
31
+
32
+ chatsStore.subscribe((value) => localforage.setItem<ChatData>('chatsStore', value));
33
+
34
+ localforage.getItem<string>('selectedChatId').then((value) => {
35
+ if (value) {
36
+ selectedChatId.set(value);
37
+ } else {
38
+ selectedChatId.set(intitalChatId);
39
+ }
40
+ });
41
+
42
+ selectedChatId.subscribe((value) => localforage.setItem<string>('selectedChatId', value));
43
+
44
+ export { loadingState, chatsStore, selectedChatId };
45
+
46
+ // import { writable } from 'svelte/store';
47
+ // import { browser } from '$app/environment';
48
+ // import type { ChatData } from './types';
49
+ // import { nanoid } from 'nanoid';
50
+
51
+
52
+ // const intitalChatId = nanoid();
53
+ // const initialData: ChatData = [{
54
+ // id: intitalChatId,
55
+ // messages: [],
56
+ // blurb: `New Chat - ${new Date().getTime()}`
57
+ // }
58
+ // ]
59
+
60
+ // export const loadingState = writable<string>('');
61
+ // export const chatsStore = writable<ChatData>(
62
+ // browser ? JSON.parse(localStorage['chatsStore'] || JSON.stringify(initialData)) : initialData
63
+ // );
64
+ // chatsStore.subscribe((value) => {
65
+ // if (browser) {
66
+ // return (localStorage['chatsStore'] = JSON.stringify(value));
67
+ // }
68
+ // });
69
+
70
+ // export const selectedChatId = writable<string>(
71
+ // browser ? JSON.parse(localStorage['selectedChatId'] || JSON.stringify(intitalChatId)) : intitalChatId
72
+ // );
73
+ // selectedChatId.subscribe((value) => {
74
+ // if (browser) {
75
+ // return (localStorage['selectedChatId'] = JSON.stringify(value));
76
+ // }
77
+ // });
frontend/src/lib/types.ts ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const enum MessageType {
2
+ TEXT = 'text',
3
+ IMAGE = 'image',
4
+ VIDEO = 'video',
5
+ AUDIO = 'audio',
6
+ FILE = 'file'
7
+ }
8
+
9
+ export const enum Sender {
10
+ USER = 'user',
11
+ BOT = 'bot'
12
+ }
13
+
14
+ export interface Message {
15
+ id: string;
16
+ content: string;
17
+ sender: Sender;
18
+ type: MessageType;
19
+ timestamp: number;
20
+ }
21
+
22
+ export interface Chat {
23
+ id: string;
24
+ messages: Message[];
25
+ blurb: string;
26
+ timestamp: number;
27
+ }
28
+
29
+ export type ChatData = Chat[];
frontend/src/routes/+page.svelte CHANGED
@@ -1,7 +1,7 @@
1
- <h1 class="text-3xl font-bold underline">Hello world!</h1>
 
 
2
 
3
- <style lang="postcss">
4
- :global(html) {
5
- background-color: theme(colors.gray.100);
6
- }
7
- </style>
 
1
+ <script lang="ts">
2
+ import App from '$lib/App.svelte';
3
+ </script>
4
 
5
+ <div class="max-w-7xl mx-auto px-4">
6
+ <App />
7
+ </div>
 
 
frontend/src/routes/+page.ts CHANGED
@@ -1 +1,2 @@
1
- export const prerender = true;
 
 
1
+ export const prerender = true;
2
+ export const ssr = false;
frontend/svelte.config.js CHANGED
@@ -14,8 +14,8 @@ const config = {
14
  assets: 'build',
15
  fallback: null,
16
  precompress: false
17
- })
18
  }
19
  };
20
 
21
- export default config;
 
14
  assets: 'build',
15
  fallback: null,
16
  precompress: false
17
+ }),
18
  }
19
  };
20
 
21
+ export default config;
frontend/tailwind.config.cjs CHANGED
@@ -1,8 +1,8 @@
1
  /** @type {import('tailwindcss').Config} */
2
  module.exports = {
3
- content: ['./src/**/*.{html,js,svelte,ts}'],
4
- theme: {
5
- extend: {}
6
- },
7
- plugins: []
8
- };
 
1
  /** @type {import('tailwindcss').Config} */
2
  module.exports = {
3
+ content: ['./src/**/*.{html,js,svelte,ts}'],
4
+ theme: {
5
+ extend: {}
6
+ },
7
+ plugins: []
8
+ };