matt HOFFNER commited on
Commit
2c3d303
β€’
1 Parent(s): f3b983d

redesign, working on mobile still

Browse files
Files changed (6) hide show
  1. app/hooks/useFunctions.ts +40 -0
  2. app/icons.tsx +0 -21
  3. app/input.tsx +3 -2
  4. app/layout.tsx +1 -0
  5. app/page.module.css +34 -386
  6. app/page.tsx +18 -58
app/hooks/useFunctions.ts ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { FunctionCallHandler, Message, nanoid } from 'ai';
2
+ import { toast } from 'sonner';
3
+
4
+ export const functionCallHandler: FunctionCallHandler = async (
5
+ chatMessages,
6
+ functionCall,
7
+ ) => {
8
+ let result;
9
+ const { name, arguments: args } = functionCall;
10
+ const response = await fetch("/api/functions", {
11
+ method: "POST",
12
+ headers: {
13
+ "Content-Type": "application/json",
14
+ },
15
+ body: JSON.stringify({
16
+ args: args,
17
+ name: name
18
+ })
19
+ } as any);
20
+
21
+ if (!response.ok) {
22
+ const errorText = await response.text();
23
+ toast.error(`Something went wrong: ${errorText}`);
24
+ return;
25
+ }
26
+
27
+ result = await response.text();
28
+
29
+ return {
30
+ messages: [
31
+ ...chatMessages,
32
+ {
33
+ id: nanoid(),
34
+ name: functionCall.name,
35
+ role: "function" as const,
36
+ content: result,
37
+ },
38
+ ],
39
+ };
40
+ };
app/icons.tsx DELETED
@@ -1,21 +0,0 @@
1
-
2
- export const FunctionIcon = ({ className }: { className?: string }) => {
3
- return (
4
- <svg
5
- xmlns="http://www.w3.org/2000/svg"
6
- fill="none"
7
- viewBox="0 0 24 24"
8
- width={25}
9
- height={25}
10
- strokeWidth={1.8}
11
- stroke="currentColor"
12
- className={className}
13
- >
14
- <path
15
- strokeLinecap="round"
16
- strokeLinejoin="round"
17
- d="M4.745 3A23.933 23.933 0 003 12c0 3.183.62 6.22 1.745 9M19.5 3c.967 2.78 1.5 5.817 1.5 9s-.533 6.22-1.5 9M8.25 8.885l1.444-.89a.75.75 0 011.105.402l2.402 7.206a.75.75 0 001.104.401l1.445-.889m-8.25.75l.213.09a1.687 1.687 0 002.062-.617l4.45-6.676a1.688 1.688 0 012.062-.618l.213.09"
18
- />
19
- </svg>
20
- );
21
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/input.tsx CHANGED
@@ -12,6 +12,7 @@ import constants from './constants';
12
  ort.env.wasm.wasmPaths = "/_next/static/chunks/";
13
 
14
  interface VoiceInputFormProps {
 
15
  handleSubmit: any;
16
  input: string;
17
  setInput: React.Dispatch<React.SetStateAction<string>>;
@@ -41,8 +42,7 @@ const convertBlobToAudioBuffer = async (blob: Blob): Promise<AudioBuffer> => {
41
  return await audioContext.decodeAudioData(arrayBuffer);
42
  };
43
 
44
-
45
- const VoiceInputForm: React.FC<VoiceInputFormProps> = ({ handleSubmit, input, setInput }) => {
46
  const [recording, setRecording] = useState(false);
47
  const [duration, setDuration] = useState(0);
48
  const [recordedBlob, setRecordedBlob] = useState<Blob | null>(null);
@@ -222,6 +222,7 @@ const VoiceInputForm: React.FC<VoiceInputFormProps> = ({ handleSubmit, input, se
222
  )}
223
  <form onSubmit={handleSubmit} className={styles.form}>
224
  <input
 
225
  type="text"
226
  value={input}
227
  className={styles.input}
 
12
  ort.env.wasm.wasmPaths = "/_next/static/chunks/";
13
 
14
  interface VoiceInputFormProps {
15
+ inputRef: any;
16
  handleSubmit: any;
17
  input: string;
18
  setInput: React.Dispatch<React.SetStateAction<string>>;
 
42
  return await audioContext.decodeAudioData(arrayBuffer);
43
  };
44
 
45
+ const VoiceInputForm: React.FC<VoiceInputFormProps> = ({ inputRef, handleSubmit, input, setInput }) => {
 
46
  const [recording, setRecording] = useState(false);
47
  const [duration, setDuration] = useState(0);
48
  const [recordedBlob, setRecordedBlob] = useState<Blob | null>(null);
 
222
  )}
223
  <form onSubmit={handleSubmit} className={styles.form}>
224
  <input
225
+ ref={inputRef}
226
  type="text"
227
  value={input}
228
  className={styles.input}
app/layout.tsx CHANGED
@@ -16,6 +16,7 @@ export default function RootLayout({
16
  }) {
17
  return (
18
  <html lang="en">
 
19
  <head>
20
  <meta name="viewport" content="width=device-width, initial-scale=1" />
21
  </head>
 
16
  }) {
17
  return (
18
  <html lang="en">
19
+
20
  <head>
21
  <meta name="viewport" content="width=device-width, initial-scale=1" />
22
  </head>
app/page.module.css CHANGED
@@ -1,406 +1,54 @@
 
1
  .main {
2
- padding: 2rem;
3
  display: flex;
4
  flex-direction: column;
5
- justify-content: space-between;
6
- min-height: 100vh; /* Full viewport height */
7
- position: relative; /* Needed for absolute positioning of pseudo-elements */
8
- overflow: hidden; /* Ensures pseudo-element doesn't overflow */
9
- }
10
-
11
- .background {
12
- position: absolute;
13
- top: 0; right: 0; bottom: 0; left: 0;
14
- background-blend-mode: screen;
15
- background-size: cover;
16
- background-position: center;
17
- background-repeat: no-repeat;
18
- z-index: -1; /* Place it behind the content */
19
- }
20
-
21
- .background::before {
22
- content: '';
23
- position: absolute;
24
- top: -50%; right: -50%; bottom: -50%; left: -50%;
25
- background-repeat: no-repeat;
26
- background-position: 50%;
27
- transform: rotate(45deg) scale(1.5);
28
- }
29
-
30
- .background::after {
31
- content: '';
32
- position: absolute;
33
- top: -50%; right: -50%; bottom: -50%; left: -50%;
34
- background-repeat: no-repeat;
35
- background-position: 50%;
36
- transform: rotate(-45deg) scale(1.5);
37
- backdrop-filter: blur(30px);
38
- pointer-events: none; /* Allows clicks to pass through to elements below */
39
- }
40
-
41
- .card span {
42
- display: inline-block;
43
- transition: transform 200ms;
44
- }
45
-
46
- .card h2 {
47
- font-weight: 600;
48
- margin-bottom: 0.7rem;
49
- }
50
-
51
- .card p {
52
- margin: 0;
53
- opacity: 0.6;
54
- font-size: 0.9rem;
55
- line-height: 1.5;
56
- max-width: 30ch;
57
- }
58
-
59
- .center {
60
- display: flex;
61
- justify-content: center;
62
- align-items: center;
63
- position: relative;
64
- padding: 4rem 0;
65
- }
66
-
67
- .center::before {
68
- background: var(--secondary-glow);
69
- border-radius: 50%;
70
- width: 480px;
71
- height: 360px;
72
- margin-left: -400px;
73
- }
74
-
75
- .center::after {
76
- background: var(--primary-glow);
77
- width: 240px;
78
- height: 180px;
79
- z-index: -1;
80
- }
81
-
82
- .center::before,
83
- .center::after {
84
- content: '';
85
- left: 50%;
86
- position: absolute;
87
- filter: blur(45px);
88
- transform: translateZ(0);
89
- }
90
-
91
- .logo {
92
- position: relative;
93
- }
94
- /* Enable hover only on non-touch devices */
95
- @media (hover: hover) and (pointer: fine) {
96
- .card:hover {
97
- background: rgba(var(--card-rgb), 0.1);
98
- border: 1px solid rgba(var(--card-border-rgb), 0.15);
99
- }
100
-
101
- .card:hover span {
102
- transform: translateX(4px);
103
- }
104
- }
105
-
106
- @media (prefers-reduced-motion) {
107
- .card:hover span {
108
- transform: none;
109
- }
110
- }
111
-
112
- /* Mobile */
113
- @media (max-width: 700px) {
114
- .content {
115
- padding: 4rem;
116
- }
117
-
118
- .grid {
119
- grid-template-columns: 1fr;
120
- margin-bottom: 120px;
121
- max-width: 320px;
122
- text-align: center;
123
- }
124
-
125
- .card {
126
- padding: 1rem 2.5rem;
127
- }
128
-
129
- .card h2 {
130
- margin-bottom: 0.5rem;
131
- }
132
-
133
- .center {
134
- padding: 8rem 0 6rem;
135
- }
136
-
137
- .center::before {
138
- transform: none;
139
- height: 300px;
140
- }
141
-
142
- .description {
143
- font-size: 0.8rem;
144
- }
145
-
146
- .description a {
147
- padding: 1rem;
148
- }
149
-
150
- .description p,
151
- .description div {
152
- display: flex;
153
- justify-content: center;
154
- position: fixed;
155
- width: 100%;
156
- }
157
-
158
- .description p {
159
- align-items: center;
160
- inset: 0 0 auto;
161
- padding: 2rem 1rem 1.4rem;
162
- border-radius: 0;
163
- border: none;
164
- border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25);
165
- background: linear-gradient(
166
- to bottom,
167
- rgba(var(--background-start-rgb), 1),
168
- rgba(var(--callout-rgb), 0.5)
169
- );
170
- background-clip: padding-box;
171
- backdrop-filter: blur(24px);
172
- }
173
-
174
- .description div {
175
- align-items: flex-end;
176
- pointer-events: none;
177
- inset: auto 0 0;
178
- padding: 2rem;
179
- height: 200px;
180
- background: linear-gradient(
181
- to bottom,
182
- transparent 0%,
183
- rgb(var(--background-end-rgb)) 40%
184
- );
185
- z-index: 1;
186
- }
187
- }
188
-
189
- /* Tablet and Smaller Desktop */
190
- @media (min-width: 701px) and (max-width: 1120px) {
191
- .grid {
192
- grid-template-columns: repeat(2, 50%);
193
- }
194
- }
195
-
196
- @media (prefers-color-scheme: dark) {
197
- .vercelLogo {
198
- filter: invert(1);
199
- }
200
-
201
- .logo {
202
- filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70);
203
- }
204
- }
205
-
206
- @keyframes rotate {
207
- from {
208
- transform: rotate(360deg);
209
- }
210
- to {
211
- transform: rotate(0deg);
212
- }
213
- }
214
-
215
- .response pre {
216
- white-space: pre-wrap;
217
- word-break: break-word;
218
- overflow-wrap: break-word;
219
- overflow-y: auto;
220
- max-height: 500px; /* Maximum height before scrolling */
221
- }
222
-
223
- .input {
224
- padding: 0.5rem 1rem; /* Adjust the padding inside the input */
225
- margin: 0.5rem 0; /* Adds margin around the input for spacing */
226
- border: 1px solid #ccc; /* A light border for the input */
227
- border-radius: 4px; /* Rounded corners */
228
- font-size: 1rem; /* Base font size */
229
- line-height: 1.5; /* Height of the input line */
230
- color: #333; /* Text color */
231
- background-color: #fff; /* Background color of the input */
232
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* Optional: Adds a subtle shadow */
233
- width: 100%; /* Make input full width of the parent container */
234
- box-sizing: border-box; /* Include padding and border in the element's total width and height */
235
- transition: border-color 0.3s, box-shadow 0.3s; /* Smooth transition for focus effect */
236
- }
237
-
238
- .input:focus {
239
- border-color: #007bff; /* Highlight color when input is focused */
240
- outline: none; /* Removes the default focus outline */
241
- box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); /* Adds a glow effect on focus */
242
- }
243
-
244
- .button {
245
- padding: 0.75rem 1.5rem; /* Adjust padding to increase the button's size */
246
- margin-top: 1rem; /* Space above the button */
247
- border: 1px solid #707070; /* Darker border for depth */
248
- border-radius: 5px; /* Rounded corners */
249
- font-size: 1rem; /* Font size */
250
- font-weight: bold; /* Bold text */
251
- color: #000; /* Black text for contrast */
252
- background: linear-gradient(180deg, #a8a8a8 0%, #8b8b8b 50%, #a8a8a8 100%); /* Gradient to simulate brushed metal */
253
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25); /* Shadow for a 3D effect */
254
- text-align: center; /* Center the text */
255
- cursor: pointer; /* Cursor change on hover */
256
- transition: background-color 0.3s, box-shadow 0.2s; /* Smooth transitions for interactions */
257
- }
258
-
259
- .button:hover {
260
- background: linear-gradient(180deg, #b0b0b0 0%, #9d9d9d 50%, #b0b0b0 100%); /* Lighter gradient on hover */
261
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); /* Less pronounced shadow on hover */
262
- }
263
-
264
- .button:active {
265
- background: linear-gradient(180deg, #8b8b8b 0%, #a8a8a8 50%, #8b8b8b 100%); /* Inverted gradient for pressed effect */
266
- box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); /* Inner shadow for a pressed effect */
267
- }
268
-
269
- .icon {
270
- height: 75px;
271
- width: 75px;
272
- }
273
-
274
- .button:active {
275
- transform: translateY(0); /* Button goes back down when clicked */
276
- box-shadow: 0 3px 6px rgba(50, 50, 93, 0.16), 0 2px 4px rgba(0, 0, 0, 0.1); /* Smaller shadow when button is pressed */
277
- }
278
-
279
- .button:disabled {
280
- background-image: linear-gradient(to right, #cbd5e1, #94a3b8); /* Less vibrant gradient for disabled state */
281
- cursor: default; /* No pointer cursor since it's not clickable */
282
- box-shadow: none; /* No shadow for a flat appearance */
283
- }
284
- .main {
285
- display: flex;
286
- flex-direction: column;
287
- justify-content: space-between;
288
- padding: 2rem;
289
- }
290
-
291
- .title {
292
- color: #333;
293
- text-align: center;
294
- margin-bottom: 2rem;
295
  }
296
 
 
297
  .messages {
298
- flex: 1;
299
- border: 8px solid transparent;
300
- border-radius: 10px;
301
- padding: 25px;
302
  }
303
 
 
304
  .message {
 
 
 
 
 
 
 
 
 
 
 
 
 
305
  display: flex;
306
- align-items: center;
307
- padding: 10px;
308
- margin-bottom: 10px;
309
- border-radius: 5px;
310
- background: linear-gradient(180deg, #a8a8a8 0%, #8b8b8b 50%, #a8a8a8 100%);
311
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
312
- color: #333;
313
- overflow: hidden; /* Prevents text from overflowing */
314
- }
315
-
316
- .message-content {
317
- flex: 1;
318
- margin-right: 10px; /* Adjust as needed */
319
- overflow: hidden; /* Prevents text from overflowing */
320
- text-overflow: ellipsis; /* Adds an ellipsis to text that overflows */
321
- white-space: nowrap; /* Keeps the text on a single line */
322
- }
323
-
324
- .avatar {
325
- margin-right: 10px;
326
- /* Ensure you have width, height, and other necessary styles here */
327
- }
328
-
329
- /* Style for function call messages */
330
- .function-call {
331
- color: #555;
332
- font-style: italic;
333
- }
334
-
335
- /* Input field styles */
336
- .input {
337
  padding: 10px;
338
- margin: 10px 0;
339
- width: 100%;
340
- box-sizing: border-box;
341
- border: 1px solid #ddd;
342
- border-radius: 4px;
343
- background-color: transparent;
344
- }
345
- .input:focus {
346
- border-color: #111827
347
- }
348
-
349
- /* Button styles */
350
- .button {
351
- padding: 10px 20px;
352
- background-color: #5cb85c;
353
- color: white;
354
- border: none;
355
- border-radius: 4px;
356
- cursor: pointer;
357
- width: 100%;
358
- }
359
-
360
- .button:disabled {
361
- background-color: #ccc;
362
  }
363
 
364
- /* Form styles */
365
  .form {
366
- width: 100%;
367
- }
368
-
369
- .flex {
370
  display: flex;
371
- align-items: flex-start;
372
- }
373
-
374
- .text-gray-500 {
375
- color: #737373;
376
- }
377
-
378
- .font-bold {
379
- font-weight: bold;
380
- }
381
-
382
- /* Additional media query for responsive design */
383
- @media (max-width: 640px) {
384
- .message {
385
- max-width: 100%;
386
- }
387
  }
388
 
389
- /* In your CSS file */
390
- .avatar-user {
391
- width: 20px; /* Based on Tailwind's width setting */
 
 
392
  }
393
 
394
- .avatar {
395
- display: inline-block;
396
- vertical-align: middle;
397
- width: 40px;
398
-
399
-
400
- margin-right: 10px;
401
  }
402
-
403
- @keyframes moveClouds {
404
- 0% { background-position: 0 0; }
405
- 100% { background-position: 1000px 500px; }
406
- }
 
1
+ /* Main container */
2
  .main {
 
3
  display: flex;
4
  flex-direction: column;
5
+ height: 100vh; /* Full viewport height */
6
+ overflow: hidden;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  }
8
 
9
+ /* Messages container */
10
  .messages {
11
+ flex-grow: 1;
12
+ overflow-y: scroll; /* Allows scrolling */
13
+ padding: 10px;
14
+ margin-bottom: 60px; /* Space for the input area */
15
  }
16
 
17
+ /* Individual message styling */
18
  .message {
19
+ /* Add specific styles for message appearance here */
20
+ }
21
+
22
+ /* Input container styling */
23
+ .inputContainer {
24
+ background-color: #aaa; /* Base color */
25
+ background-image: linear-gradient(90deg, rgba(255, 255, 255, 0.1) 50%, transparent 50%),
26
+ linear-gradient(rgba(255, 255, 255, 0.1) 1px, transparent 1px);
27
+ background-size: 2px 2px, 4px 4px;
28
+ position: fixed; /* Fixed at the bottom */
29
+ bottom: 0; /* Align to bottom */
30
+ left: 0; /* Align to left */
31
+ right: 0; /* Align to right */
32
  display: flex;
33
+ justify-content: space-between;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  padding: 10px;
35
+ z-index: 1000; /* Ensure it's above other content */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  }
37
 
38
+ /* Form styling */
39
  .form {
 
 
 
 
40
  display: flex;
41
+ width: 100%;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  }
43
 
44
+ /* Input field styling */
45
+ .input {
46
+ flex-grow: 1; /* Input takes up available space */
47
+ margin-right: 10px; /* Spacing between input and button */
48
+ font-size: 16px; /* Prevent zoom on mobile browsers */
49
  }
50
 
51
+ /* Button styling */
52
+ .button {
53
+ /* Add specific styles for the button appearance here */
 
 
 
 
54
  }
 
 
 
 
 
app/page.tsx CHANGED
@@ -1,66 +1,34 @@
1
  "use client";
2
 
3
  import styles from './page.module.css';
4
- import { useEffect, useState } from 'react';
5
  import { useChat } from 'ai/react';
6
- import { FunctionCallHandler, Message, nanoid } from 'ai';
7
  import ReactMarkdown from "react-markdown";
8
- import { Bot, User } from "lucide-react";
9
- import { toast } from 'sonner';
10
- import { FunctionIcon } from './icons';
11
- import { updateBackground } from './util';
12
  import Input from './input';
13
 
14
  const Page: React.FC = () => {
15
- useEffect(() => {
16
- updateBackground();
17
- const interval = setInterval(updateBackground, 600);
18
-
19
- return () => clearInterval(interval);
20
- }, []);
21
- const functionCallHandler: FunctionCallHandler = async (
22
- chatMessages,
23
- functionCall,
24
- ) => {
25
- let result;
26
- const { name, arguments: args } = functionCall;
27
- const response = await fetch("/api/functions", {
28
- method: "POST",
29
- headers: {
30
- "Content-Type": "application/json",
31
- },
32
- body: JSON.stringify({
33
- args: args,
34
- name: name
35
- })
36
- } as any);
37
-
38
- if (!response.ok) {
39
- const errorText = await response.text();
40
- toast.error(`Something went wrong: ${errorText}`);
41
- return;
42
- }
43
-
44
- result = await response.text();
45
 
46
- return {
47
- messages: [
48
- ...chatMessages,
49
- {
50
- id: nanoid(),
51
- name: functionCall.name,
52
- role: "function" as const,
53
- content: result,
54
- },
55
- ],
56
- };
57
- };
58
  const { messages, input, setInput, handleSubmit, isLoading } = useChat({
59
  experimental_onFunctionCall: functionCallHandler,
60
  onError: (error: any) => {
61
  console.log(error);
62
  },
63
  });
 
 
 
 
 
 
 
 
 
64
  const [isExpanded, setIsExpanded] = useState(false);
65
  const toggleExpand = () => {
66
  setIsExpanded(!isExpanded);
@@ -68,7 +36,6 @@ const Page: React.FC = () => {
68
 
69
  const roleUIConfig: {
70
  [key: string]: {
71
- avatar: JSX.Element;
72
  bgColor: string;
73
  avatarColor: string;
74
  // eslint-disable-next-line no-unused-vars
@@ -76,7 +43,6 @@ const Page: React.FC = () => {
76
  };
77
  } = {
78
  user: {
79
- avatar: <User width={20} />,
80
  bgColor: "bg-white",
81
  avatarColor: "bg-black",
82
  dialogComponent: (message: Message) => (
@@ -95,7 +61,6 @@ const Page: React.FC = () => {
95
  ),
96
  },
97
  assistant: {
98
- avatar: <Bot width={20} />,
99
  bgColor: "bg-gray-100",
100
  avatarColor: "bg-green-500",
101
  dialogComponent: (message: Message) => (
@@ -114,7 +79,6 @@ const Page: React.FC = () => {
114
  ),
115
  },
116
  function: {
117
- avatar: <div className="cursor-pointer" onClick={toggleExpand}><FunctionIcon /></div>,
118
  bgColor: "bg-gray-200",
119
  avatarColor: "bg-blue-500",
120
  dialogComponent: (message: Message) => {
@@ -132,16 +96,12 @@ const Page: React.FC = () => {
132
 
133
  return (
134
  <main className={styles.main}>
135
- <div id="bg" className={styles.background}></div>
136
- <div className={styles.messages}>
137
  {messages.length > 0 ? (
138
  messages.map((message, i) => {
139
  const messageClass = `${styles.message} ${message.role === 'user' ? styles['message-user'] : ''}`;
140
  return (
141
  <div key={i} className={messageClass} style={{ display: 'flex', alignItems: 'center' }}>
142
- <div className={styles.avatar}>
143
- {roleUIConfig[message.role].avatar}
144
- </div>
145
  {message.content === "" && message.function_call != undefined ? (
146
  typeof message.function_call === "object" ? (
147
  <div style={{ display: 'flex', flexDirection: 'column' }}>
@@ -169,7 +129,7 @@ const Page: React.FC = () => {
169
  })
170
  ) : null}
171
  </div>
172
- <Input handleSubmit={handleSubmit as any} setInput={setInput} input={input} />
173
  </main>
174
  );
175
  }
 
1
  "use client";
2
 
3
  import styles from './page.module.css';
4
+ import { useEffect, useRef, useState } from 'react';
5
  import { useChat } from 'ai/react';
6
+ import { Message } from 'ai';
7
  import ReactMarkdown from "react-markdown";
8
+ import { functionCallHandler } from './hooks/useFunctions';
 
 
 
9
  import Input from './input';
10
 
11
  const Page: React.FC = () => {
12
+ // Ref for the messages container
13
+ const messagesRef = useRef<HTMLDivElement>(null);
14
+ const [keyboardHeight, setKeyboardHeight] = useState(0);
15
+ const [isKeyboardVisible, setIsKeyboardVisible] = useState(false);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  const { messages, input, setInput, handleSubmit, isLoading } = useChat({
18
  experimental_onFunctionCall: functionCallHandler,
19
  onError: (error: any) => {
20
  console.log(error);
21
  },
22
  });
23
+
24
+ const inputRef = useRef(null);
25
+
26
+ useEffect(() => {
27
+ if (messagesRef.current) {
28
+ messagesRef.current.scrollTop = messagesRef.current.scrollHeight;
29
+ }
30
+ }, [messages]);
31
+
32
  const [isExpanded, setIsExpanded] = useState(false);
33
  const toggleExpand = () => {
34
  setIsExpanded(!isExpanded);
 
36
 
37
  const roleUIConfig: {
38
  [key: string]: {
 
39
  bgColor: string;
40
  avatarColor: string;
41
  // eslint-disable-next-line no-unused-vars
 
43
  };
44
  } = {
45
  user: {
 
46
  bgColor: "bg-white",
47
  avatarColor: "bg-black",
48
  dialogComponent: (message: Message) => (
 
61
  ),
62
  },
63
  assistant: {
 
64
  bgColor: "bg-gray-100",
65
  avatarColor: "bg-green-500",
66
  dialogComponent: (message: Message) => (
 
79
  ),
80
  },
81
  function: {
 
82
  bgColor: "bg-gray-200",
83
  avatarColor: "bg-blue-500",
84
  dialogComponent: (message: Message) => {
 
96
 
97
  return (
98
  <main className={styles.main}>
99
+ <div className={styles.messages} ref={messagesRef}>
 
100
  {messages.length > 0 ? (
101
  messages.map((message, i) => {
102
  const messageClass = `${styles.message} ${message.role === 'user' ? styles['message-user'] : ''}`;
103
  return (
104
  <div key={i} className={messageClass} style={{ display: 'flex', alignItems: 'center' }}>
 
 
 
105
  {message.content === "" && message.function_call != undefined ? (
106
  typeof message.function_call === "object" ? (
107
  <div style={{ display: 'flex', flexDirection: 'column' }}>
 
129
  })
130
  ) : null}
131
  </div>
132
+ <Input inputRef={inputRef} handleSubmit={handleSubmit as any} setInput={setInput} input={input} />
133
  </main>
134
  );
135
  }