|
194 | 194 | }),
|
195 | 195 | });
|
196 | 196 |
|
| 197 | + if (response.status !== 200) { |
| 198 | + throw new Error(await response.text()); |
| 199 | + } |
| 200 | + |
197 | 201 | const reader = response.body.getReader();
|
198 | 202 | let responseObj = {};
|
199 | 203 | for (;;) {
|
|
260 | 264 | }
|
261 | 265 |
|
262 | 266 | export function Sidebar({
|
263 |
| - numConversations, |
264 |
| - onConversationClick, |
| 267 | + messages, |
| 268 | + onMessagesChange, |
265 | 269 | onModalClick,
|
266 | 270 | onClearClick,
|
267 | 271 | onSettingsClick,
|
268 | 272 | }) {
|
| 273 | + const initConversations = JSON.parse( |
| 274 | + localStorage.getItem("conversations") || "[]" |
| 275 | + ); |
| 276 | + const [conversations, setConversations] = |
| 277 | + React.useState(initConversations); |
| 278 | + const [currentConversation, setCurrentConversation] = React.useState( |
| 279 | + conversations[0] |
| 280 | + ); |
| 281 | + const currentConversationRef = React.useRef(currentConversation); |
| 282 | + |
| 283 | + React.useEffect(() => { |
| 284 | + if (!conversations.length) localStorage.removeItem("conversations"); |
| 285 | + else |
| 286 | + localStorage.setItem( |
| 287 | + "conversations", |
| 288 | + JSON.stringify(conversations) |
| 289 | + ); |
| 290 | + }, [conversations]); |
| 291 | + |
| 292 | + React.useEffect(() => { |
| 293 | + currentConversationRef.current = currentConversation; |
| 294 | + onMessagesChange(currentConversation?.messages || []); |
| 295 | + }, [currentConversation, onMessagesChange]); |
| 296 | + |
| 297 | + React.useEffect(() => { |
| 298 | + if (!messages.length && !currentConversationRef.current) return; |
| 299 | + |
| 300 | + if (!currentConversationRef.current) { |
| 301 | + const newCoversation = { |
| 302 | + id: crypto.getRandomValues(new Uint32Array(1))[0], |
| 303 | + title: messages[0].content.split("\n")[0].slice(0, 20), |
| 304 | + messages, |
| 305 | + }; |
| 306 | + setConversations([newCoversation, ...conversations]); |
| 307 | + setCurrentConversation(newCoversation); |
| 308 | + } else if (currentConversationRef.current.messages !== messages) { |
| 309 | + currentConversationRef.current.messages = messages; |
| 310 | + localStorage.setItem( |
| 311 | + "conversations", |
| 312 | + JSON.stringify(conversations) |
| 313 | + ); |
| 314 | + } |
| 315 | + }, [messages]); |
| 316 | + |
269 | 317 | return [
|
270 | 318 | React.createElement(
|
271 | 319 | "div",
|
|
277 | 325 | "button",
|
278 | 326 | {
|
279 | 327 | key: "new",
|
280 |
| - onClick: () => { |
281 |
| - onConversationClick && onConversationClick(-1); |
282 |
| - }, |
| 328 | + onClick: () => setCurrentConversation(null), |
283 | 329 | },
|
284 | 330 | "New Chat"
|
285 | 331 | ),
|
286 | 332 | ]),
|
287 |
| - ...Array(numConversations) |
288 |
| - .fill(null) |
289 |
| - .map((_, index) => { |
290 |
| - return React.createElement("li", { key: index }, [ |
291 |
| - React.createElement( |
292 |
| - "button", |
293 |
| - { |
294 |
| - key: `conversation-${index}`, |
295 |
| - onClick: () => { |
296 |
| - onConversationClick && onConversationClick(index); |
297 |
| - }, |
| 333 | + conversations.map((conversation, index) => { |
| 334 | + return React.createElement("li", { key: index }, [ |
| 335 | + React.createElement( |
| 336 | + "button", |
| 337 | + { |
| 338 | + key: conversation.id, |
| 339 | + onClick: () => { |
| 340 | + setCurrentConversation(conversation); |
| 341 | + }, |
| 342 | + onContextMenu: (ev) => { |
| 343 | + ev.preventDefault(); |
| 344 | + setConversations( |
| 345 | + conversations.filter((c) => c !== conversation) |
| 346 | + ); |
| 347 | + if (currentConversation === conversation) |
| 348 | + setCurrentConversation(null); |
298 | 349 | },
|
299 |
| - `Conversation ${index + 1}` |
300 |
| - ), |
301 |
| - ]); |
302 |
| - }), |
| 350 | + }, |
| 351 | + conversation.title |
| 352 | + ), |
| 353 | + ]); |
| 354 | + }), |
303 | 355 | React.createElement("li", { key: "clear" }, [
|
304 | 356 | React.createElement(
|
305 | 357 | "button",
|
306 | 358 | {
|
307 | 359 | key: "clear",
|
308 | 360 | onClick: () => {
|
309 |
| - onClearClick && onClearClick(); |
| 361 | + setConversations([]); |
| 362 | + setCurrentConversation(null); |
| 363 | + onMessagesChange([]); |
| 364 | + onModalClick(); |
310 | 365 | },
|
311 | 366 | },
|
312 | 367 | "Clear Conversations"
|
|
317 | 372 | "button",
|
318 | 373 | {
|
319 | 374 | key: "settings",
|
320 |
| - onClick: () => { |
321 |
| - onSettingsClick && onSettingsClick(); |
322 |
| - }, |
| 375 | + onClick: onSettingsClick, |
323 | 376 | },
|
324 | 377 | "Settings"
|
325 | 378 | ),
|
|
330 | 383 | React.createElement("div", {
|
331 | 384 | key: "sidebar-modal",
|
332 | 385 | className: "sidebar-modal",
|
333 |
| - onClick: () => { |
334 |
| - onModalClick && onModalClick(); |
335 |
| - }, |
| 386 | + onClick: onModalClick, |
336 | 387 | }),
|
337 | 388 | ];
|
338 | 389 | }
|
|
342 | 393 | getApiUrl().then(() => {
|
343 | 394 | if (getApiUrl.tokenRequired) token = getToken();
|
344 | 395 | });
|
345 |
| - const conversations = JSON.parse( |
346 |
| - localStorage.getItem("conversations") || "[]" |
347 |
| - ); |
348 |
| - conversations[0] = conversations[0] || []; |
349 | 396 |
|
350 |
| - const [conversationIndex, setConversationIndex] = React.useState(0); |
351 |
| - const [numConversations, setNumConversations] = React.useState( |
352 |
| - conversations.length |
353 |
| - ); |
354 |
| - const [messages, setMessages] = React.useState( |
355 |
| - conversations[conversationIndex] |
356 |
| - ); |
357 |
| - const messagesRef = React.useRef(messages); |
358 |
| - const saveMessages = (value, index) => { |
359 |
| - setMessages(value); |
360 |
| - conversations[index] = value; |
361 |
| - localStorage.setItem("conversations", JSON.stringify(conversations)); |
362 |
| - }; |
| 397 | + const [messages, setMessages] = React.useState([]); |
| 398 | + const [editingMessage, setEditingMessage] = React.useState(null); |
363 | 399 |
|
364 | 400 | const [showSidebar, setShowSidebar] = React.useState(false);
|
365 | 401 |
|
|
387 | 423 | {
|
388 | 424 | key: "clear",
|
389 | 425 | onClick: () => {
|
390 |
| - saveMessages([], conversationIndex); |
391 |
| - messagesRef.current = []; |
| 426 | + setMessages([]); |
392 | 427 | },
|
393 | 428 | className: "clear",
|
394 | 429 | },
|
|
409 | 444 | },
|
410 | 445 |
|
411 | 446 | React.createElement(Sidebar, {
|
412 |
| - messages, |
413 | 447 | key: "sidebar",
|
414 |
| - numConversations, |
415 |
| - |
416 |
| - onConversationClick: (index) => { |
417 |
| - if (index === -1) { |
418 |
| - conversations.unshift([]); |
419 |
| - setNumConversations(conversations.length); |
420 |
| - index = 0; |
421 |
| - } |
422 |
| - setConversationIndex(index); |
423 |
| - saveMessages(conversations[index], index); |
424 |
| - messagesRef.current = conversations[index]; |
425 |
| - setShowSidebar(false); |
426 |
| - }, |
| 448 | + messages, |
| 449 | + onMessagesChange: setMessages, |
427 | 450 |
|
428 | 451 | onModalClick: () => {
|
429 | 452 | setShowSidebar(false);
|
430 | 453 | },
|
431 |
| - |
432 |
| - onClearClick: () => { |
433 |
| - localStorage.removeItem("conversations"); |
434 |
| - setNumConversations(1); |
435 |
| - setConversationIndex(0); |
436 |
| - messagesRef.current = []; |
437 |
| - setMessages([]); |
438 |
| - setShowSidebar(false); |
439 |
| - }, |
440 | 454 | })
|
441 | 455 | ),
|
442 | 456 |
|
443 | 457 | React.createElement(
|
444 | 458 | "main",
|
445 | 459 | { key: "main" },
|
446 | 460 | React.createElement(ChatContainer, {
|
447 |
| - messages, |
| 461 | + messages: [...messages, editingMessage].filter((m) => m), |
448 | 462 | key: "chat-container",
|
449 | 463 | })
|
450 | 464 | ),
|
|
466 | 480 | const message = ev.target.value;
|
467 | 481 | ev.target.value = "";
|
468 | 482 | ev.target.blur();
|
469 |
| - messagesRef.current = [ |
470 |
| - ...messagesRef.current, |
| 483 | + const userMessages = [ |
| 484 | + ...messages, |
471 | 485 | { role: "user", content: message },
|
472 | 486 | ];
|
473 |
| - saveMessages(messagesRef.current, conversationIndex); |
| 487 | + setMessages(userMessages); |
474 | 488 | try {
|
475 | 489 | const completion = await complete(
|
476 |
| - messagesRef.current, |
| 490 | + userMessages, |
477 | 491 | token,
|
478 |
| - (message) => |
479 |
| - saveMessages( |
480 |
| - [...messagesRef.current, message], |
481 |
| - conversationIndex |
482 |
| - ) |
| 492 | + (message) => setEditingMessage({ ...message }) |
483 | 493 | );
|
484 |
| - messagesRef.current = [...messagesRef.current, completion]; |
| 494 | + setMessages((messages) => [...messages, completion]); |
485 | 495 | } catch (error) {
|
486 |
| - console.log("Error: " + error.message); |
| 496 | + console.log(error.message); |
487 | 497 | return;
|
| 498 | + } finally { |
| 499 | + setEditingMessage(null); |
488 | 500 | }
|
489 | 501 | }
|
490 | 502 | },
|
|
0 commit comments