File size: 5,552 Bytes
f5ed9bf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
'use client';

/* eslint-disable @next/next/no-img-element */
import React, { MutableRefObject, useEffect, useState } from 'react';
import { Message } from './ChatWindow';
import { cn } from '@/lib/utils';
import { BookCopy, Disc3, Share, Volume2, StopCircle } from 'lucide-react';
import Markdown from 'markdown-to-jsx';
import Copy from './MessageActions/Copy';
import Rewrite from './MessageActions/Rewrite';
import MessageSources from './MessageSources';
import SearchImages from './SearchImages';
import SearchVideos from './SearchVideos';
import { useSpeech } from 'react-text-to-speech';

const MessageBox = ({

  message,

  messageIndex,

  history,

  loading,

  dividerRef,

  isLast,

  rewrite,

}: {

  message: Message;

  messageIndex: number;

  history: Message[];

  loading: boolean;

  dividerRef?: MutableRefObject<HTMLDivElement | null>;

  isLast: boolean;

  rewrite: (messageId: string) => void;

}) => {
  const [parsedMessage, setParsedMessage] = useState(message.content);
  const [speechMessage, setSpeechMessage] = useState(message.content);

  useEffect(() => {
    const regex = /\[(\d+)\]/g;

    if (
      message.role === 'assistant' &&
      message?.sources &&
      message.sources.length > 0
    ) {
      return setParsedMessage(
        message.content.replace(
          regex,
          (_, number) =>
            `<a href="${message.sources?.[number - 1]?.metadata?.url}" target="_blank" className="bg-[#1C1C1C] px-1 rounded ml-1 no-underline text-xs text-white/70 relative">${number}</a>`,
        ),
      );
    }

    setSpeechMessage(message.content.replace(regex, ''));
    setParsedMessage(message.content);
  }, [message.content, message.sources, message.role]);

  const { speechStatus, start, stop } = useSpeech({ text: speechMessage });

  return (
    <div>

      {message.role === 'user' && (

        <div className={cn('w-full', messageIndex === 0 ? 'pt-16' : 'pt-8')}>

          <h2 className="text-white font-medium text-3xl lg:w-9/12">

            {message.content}

          </h2>

        </div>

      )}



      {message.role === 'assistant' && (

        <div className="flex flex-col space-y-9 lg:space-y-0 lg:flex-row lg:justify-between lg:space-x-9">

          <div

            ref={dividerRef}

            className="flex flex-col space-y-6 w-full lg:w-9/12"

          >

            {message.sources && message.sources.length > 0 && (

              <div className="flex flex-col space-y-2">

                <div className="flex flex-row items-center space-x-2">

                  <BookCopy className="text-white" size={20} />

                  <h3 className="text-white font-medium text-xl">Sources</h3>

                </div>

                <MessageSources sources={message.sources} />

              </div>

            )}

            <div className="flex flex-col space-y-2">

              <div className="flex flex-row items-center space-x-2">

                <Disc3

                  className={cn(

                    'text-white',

                    isLast && loading ? 'animate-spin' : 'animate-none',

                  )}

                  size={20}

                />

                <h3 className="text-white font-medium text-xl">Answer</h3>

              </div>

              <Markdown className="prose max-w-none break-words prose-invert prose-p:leading-relaxed prose-pre:p-0 text-white text-sm md:text-base font-medium">

                {parsedMessage}

              </Markdown>

              {loading && isLast ? null : (

                <div className="flex flex-row items-center justify-between w-full text-white py-4 -mx-2">

                  <div className="flex flex-row items-center space-x-1">

                    <button className="p-2 text-white/70 rounded-xl hover:bg-[#1c1c1c] transition duration-200 hover:text-white">

                      <Share size={18} />

                    </button>

                    <Rewrite rewrite={rewrite} messageId={message.id} />

                  </div>

                  <div className="flex flex-row items-center space-x-1">

                    <Copy initialMessage={message.content} message={message} />

                    <button

                      onClick={() => {

                        if (speechStatus === 'started') {

                          stop();

                        } else {

                          start();

                        }

                      }}

                      className="p-2 text-white/70 rounded-xl hover:bg-[#1c1c1c] transition duration-200 hover:text-white"

                    >

                      {speechStatus === 'started' ? (

                        <StopCircle size={18} />

                      ) : (

                        <Volume2 size={18} />

                      )}

                    </button>

                  </div>

                </div>

              )}

            </div>

          </div>

          <div className="lg:sticky lg:top-20 flex flex-col items-center space-y-3 w-full lg:w-3/12 z-30 h-full pb-4">

            <SearchImages

              query={history[messageIndex - 1].content}

              chat_history={history.slice(0, messageIndex - 1)}

            />

            <SearchVideos

              chat_history={history.slice(0, messageIndex - 1)}

              query={history[messageIndex - 1].content}

            />

          </div>

        </div>

      )}

    </div>
  );
};

export default MessageBox;