File size: 23,777 Bytes
d58d97b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
/*

   ELYSIA CODE COMPANION v1.2.2 - Chat Interface

   Real-time chat with Elysia AI

*/

import API from "./api.js";
import Utils from "./utils.js";
import DB from "./db.js";
import FileSystem from "./filesystem.js";
import Analyzer from "./analyzer.js";

const Chat = {
    messageContainer: null,
    inputField: null,
    sendButton: null,
    cancelButton: null,
    contextInfo: null,
    isProcessing: false, // Prevent concurrent API calls
    lastRequestTime: 0,
    minRequestInterval: 1000, // 1 second between requests
    currentAbortController: null, // For request cancellation
    markdownUpdateThrottle: 100, // ms between markdown re-renders
    conversationHistory: [], // Store conversation history for context
    maxHistoryMessages: 20, // Max messages to keep in context (loaded from settings)

    init() {
        this.messageContainer = document.getElementById("chat-messages");
        this.inputField = document.getElementById("chat-input");
        this.sendButton = document.getElementById("btn-send");
        this.cancelButton = document.getElementById("btn-cancel");
        this.contextInfo = document.getElementById("context-info");

        // Load maxHistoryMessages from settings
        this.maxHistoryMessages = Utils.storage.get("maxHistoryMessages", 20);

        // Make Chat globally accessible for settings updates
        window.Chat = this;

        // Event Listeners
        this.sendButton.addEventListener("click", () => this.sendMessage());

        // Cancel button
        if (this.cancelButton) {
            this.cancelButton.addEventListener("click", () => this.cancelRequest());
        }

        this.inputField.addEventListener("keydown", e => {
            if (e.key === "Enter" && !e.shiftKey) {
                e.preventDefault();
                this.sendMessage();
            }
        });

        this.inputField.addEventListener("input", () => {
            this.updateSendButtonState();
        });
    },

    // Clear conversation history (for new chat)
    clearHistory() {
        this.conversationHistory = [];
        console.log("πŸ’¬ Conversation history cleared");
    },

    updateSendButtonState() {
        const charCount = document.getElementById("char-count");
        const len = this.inputField.value.length;
        charCount.textContent = `${len} / 10000`;

        // Enable/disable send button - also check if processing
        this.sendButton.disabled = len === 0 || len > 10000 || this.isProcessing;
    },

    updateContextInfo() {
        if (FileSystem.folderName) {
            this.contextInfo.textContent = `πŸ“‚ ${FileSystem.folderName} (${FileSystem.files.length} files)`;
        } else {
            this.contextInfo.textContent = "No folder opened";
        }
    },

    async sendMessage() {
        const input = this.inputField.value.trim();
        if (!input) return;

        // Rate limiting
        const now = Date.now();
        if (this.isProcessing) {
            Utils.toast.warning("Please wait for the current request to complete");
            return;
        }
        if (now - this.lastRequestTime < this.minRequestInterval) {
            Utils.toast.warning("Please wait a moment between requests");
            return;
        }

        this.isProcessing = true;
        this.lastRequestTime = now;

        // Clear input
        this.inputField.value = "";
        this.sendButton.disabled = true;

        // Parse input
        const parsed = Analyzer.parseCommand(input);

        // Add user message to UI
        this.addMessage(input, "user");

        // Handle commands
        if (parsed.type === "command") {
            try {
                await this.handleCommand(parsed.command, parsed.args);
            } catch (err) {
                console.error("Command failed:", err);
                this.addMessage(`Command error: ${err.message}`, "error");
            } finally {
                this.isProcessing = false;
                this.sendButton.disabled = false;
            }
            return;
        }

        // Handle regular message - call Elysia
        try {
            await this.callElysia(input);
        } catch (err) {
            console.error("Failed to send message:", err);
            this.addMessage(`Error: ${err.message}`, "error");
        } finally {
            this.isProcessing = false;
            this.sendButton.disabled = false;
        }
    },

    async handleCommand(command, args) {
        try {
            switch (command) {
                case "scan":
                    await this.commandScan();
                    break;

                case "analyze":
                    await this.commandAnalyze(args);
                    break;

                case "tree":
                    await this.commandTree();
                    break;

                case "stats":
                    await this.commandStats();
                    break;

                case "help":
                    this.commandHelp();
                    break;

                case "export":
                    await this.commandExport(args);
                    break;

                default:
                    this.addMessage(`Unknown command: /${command}. Type /help for available commands.`, "error");
            }
        } catch (err) {
            console.error("Command execution error:", err);
            this.addMessage(`Command error: ${err.message}`, "error");
            throw err; // Re-throw to ensure isProcessing reset in sendMessage
        }
    },

    async commandScan() {
        if (FileSystem.files.length === 0) {
            this.addMessage("No folder opened. Open a folder first!", "error");
            return;
        }

        this.addMessage("πŸ” Scanning project... (this may take a moment)", "system");

        try {
            const analysis = Analyzer.analyzeProject();

            let response = `## πŸ“Š Project Analysis: ${analysis.summary.name}\n\n`;
            response += `**Total Files:** ${analysis.summary.totalFiles}\n`;
            response += `**Total Size:** ${analysis.summary.totalSize}\n\n`;

            response += `**Languages:**\n`;
            Object.entries(analysis.summary.languages).forEach(([lang, count]) => {
                response += `- ${lang}: ${count} file(s)\n`;
            });

            if (analysis.insights.length > 0) {
                response += `\n**Insights:**\n`;
                analysis.insights.forEach(insight => {
                    const icon = insight.type === "success" ? "βœ…" : insight.type === "warning" ? "⚠️" : "ℹ️";
                    response += `${icon} ${insight.message}\n`;
                });
            }

            // Ask Elysia to analyze further
            response += `\n\nAsking Elysia to provide deeper insights...\n`;
            this.addMessage(response, "system");

            await this.callElysia(`Analyze this project. Here's the summary:\n${JSON.stringify(analysis, null, 2)}`);
        } catch (err) {
            console.error("Scan failed:", err);
            this.addMessage(`Scan failed: ${err.message}`, "error");
        }
    },

    async commandAnalyze(filename) {
        if (!filename) {
            this.addMessage("Usage: /analyze <filename>", "error");
            return;
        }

        if (FileSystem.files.length === 0) {
            this.addMessage("No folder opened. Open a folder first!", "error");
            return;
        }

        const file = FileSystem.files.find(
            f =>
                f.name.toLowerCase() === filename.toLowerCase() || f.path.toLowerCase().includes(filename.toLowerCase())
        );

        if (!file) {
            this.addMessage(`File not found: ${filename}`, "error");
            return;
        }

        try {
            this.addMessage(`πŸ” Analyzing ${file.path}...`, "system");
            const analysis = await Analyzer.analyzeFile(file.path);

            let response = `## πŸ“„ File Analysis: ${analysis.name}\n\n`;
            response += `**Path:** ${analysis.path}\n`;
            response += `**Size:** ${analysis.size}\n`;
            response += `**Language:** ${analysis.language}\n`;
            response += `**Lines:** ${analysis.lines}\n`;
            if (analysis.linesOfCode) {
                response += `**Code Lines:** ${analysis.linesOfCode}\n`;
            }

            if (analysis.insights.length > 0) {
                response += `\n**Quick Insights:**\n`;
                analysis.insights.forEach(insight => {
                    const icon = insight.type === "warning" ? "⚠️" : "ℹ️";
                    response += `${icon} ${insight.message}\n`;
                });
            }

            response += `\n\nAsking Elysia for detailed analysis...\n`;
            this.addMessage(response, "system");

            // Ask Elysia to analyze the code
            const contextFiles = [
                {
                    name: analysis.name,
                    path: analysis.path,
                    language: analysis.language,
                    content: analysis.content
                }
            ];

            await this.callElysia(
                `Please analyze this file in detail. Look for bugs, code smells, performance issues, and suggest improvements.`,
                contextFiles
            );
        } catch (err) {
            console.error("Analysis failed:", err);
            this.addMessage(`Analysis failed: ${err.message}`, "error");
        }
    },

    async commandTree() {
        if (FileSystem.files.length === 0) {
            this.addMessage("No folder opened. Open a folder first!", "error");
            return;
        }

        const tree = FileSystem.buildTree();
        const treeText = Analyzer.generateTreeText(tree);

        const response = `## 🌳 Project Structure\n\n\`\`\`\n${treeText}\`\`\``;
        this.addMessage(response, "system");
    },

    async commandStats() {
        if (FileSystem.files.length === 0) {
            this.addMessage("No folder opened. Open a folder first!", "error");
            return;
        }

        const stats = FileSystem.getStats();

        let response = `## πŸ“Š Project Statistics\n\n`;
        response += `**Total Files:** ${stats.totalFiles}\n`;
        response += `**Total Size:** ${Utils.formatFileSize(stats.totalSize)}\n\n`;

        response += `**Languages:**\n`;
        Object.entries(stats.languages)
            .sort((a, b) => b[1] - a[1])
            .forEach(([lang, count]) => {
                response += `- ${lang}: ${count} file(s)\n`;
            });

        response += `\n**File Types:**\n`;
        Object.entries(stats.fileTypes)
            .sort((a, b) => b[1] - a[1])
            .slice(0, 10)
            .forEach(([ext, count]) => {
                response += `- .${ext}: ${count} file(s)\n`;
            });

        this.addMessage(response, "system");
    },

    commandHelp() {
        const helpText = `## 🎯 Available Commands



**/scan** - Analyze entire project structure

**/analyze <filename>** - Deep analysis of specific file

**/tree** - Show project file tree

**/stats** - Project statistics (files, languages, etc.)

**/export [format]** - Export conversation (markdown/json/txt)

**/help** - Show this help message



**πŸ’‘ Tips:**

- Just chat naturally! Ask me about your code, and I'll help.

- Mention specific files in your questions, and I'll include them in context.

- I can explain complex code, suggest improvements, find bugs, and more!



**Examples:**

- "What does app.js do?"

- "Find bugs in utils.ts"

- "How can I improve the performance of this component?"

- "Explain the architecture of this project"`;

        this.addMessage(helpText, "system");
    },

    async commandExport(format = "markdown") {
        const validFormats = ["markdown", "md", "json", "txt"];
        // CRITICAL: Lowercase BEFORE validation to prevent crash
        const exportFormat = (format || "markdown").toLowerCase();

        if (!validFormats.includes(exportFormat)) {
            this.addMessage(`Invalid format. Use: /export [markdown|json|txt]`, "error");
            return;
        }

        try {
            Utils.loading.show("Exporting conversation...");

            // Get all messages from current session
            const messages = Array.from(this.messageContainer.querySelectorAll(".message"));

            let exportContent = "";
            const exportData = [];

            messages.forEach(msg => {
                const type = msg.classList.contains("user")
                    ? "user"
                    : msg.classList.contains("assistant")
                      ? "assistant"
                      : "system";
                const author = msg.querySelector(".message-author")?.textContent || type;
                const time = msg.querySelector(".message-time")?.textContent || "";
                const content = msg.querySelector(".message-content")?.textContent || "";

                exportData.push({ type, author, time, content });
            });

            // Generate export based on format
            if (exportFormat === "json") {
                exportContent = JSON.stringify(
                    {
                        exported: new Date().toISOString(),
                        project: FileSystem.folderName || "No project",
                        messages: exportData
                    },
                    null,
                    2
                );
            } else if (exportFormat === "txt") {
                exportContent = `Elysia Code Companion - Conversation Export\n`;
                exportContent += `Exported: ${new Date().toLocaleString()}\n`;
                exportContent += `Project: ${FileSystem.folderName || "No project"}\n`;
                exportContent += `${"=".repeat(60)}\n\n`;

                exportData.forEach(msg => {
                    exportContent += `[${msg.time}] ${msg.author}:\n${msg.content}\n\n`;
                });
            } else {
                // Markdown (default)
                exportContent = `# πŸ’Ž Elysia Code Companion - Conversation\n\n`;
                exportContent += `**Exported:** ${new Date().toLocaleString()}\n`;
                exportContent += `**Project:** ${FileSystem.folderName || "No project"}\n\n`;
                exportContent += `---\n\n`;

                exportData.forEach(msg => {
                    exportContent += `## ${msg.author} (${msg.time})\n\n`;
                    exportContent += `${msg.content}\n\n`;
                    exportContent += `---\n\n`;
                });
            }

            // Download file
            const blob = new Blob([exportContent], { type: "text/plain" });
            const url = URL.createObjectURL(blob);
            const a = document.createElement("a");
            const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, -5);
            const extension = exportFormat === "json" ? "json" : exportFormat === "txt" ? "txt" : "md";
            a.href = url;
            a.download = `elysia-conversation-${timestamp}.${extension}`;
            a.click();
            URL.revokeObjectURL(url);

            Utils.loading.hide();
            Utils.toast.success(`Conversation exported as ${extension.toUpperCase()}`);
            this.addMessage(
                `βœ… Conversation exported successfully as **${extension.toUpperCase()}** format.`,
                "system"
            );
        } catch (err) {
            Utils.loading.hide();
            console.error("Export failed:", err);
            this.addMessage(`Export failed: ${err.message}`, "error");
        }
    },

    async callElysia(userMessage, contextFiles = null) {
        // Get context files if not provided
        if (!contextFiles && FileSystem.files.length > 0) {
            contextFiles = await Analyzer.getContextFiles(userMessage, 3);
        }

        // Build system prompt
        const systemPrompt = API.getSystemPrompt({
            folderName: FileSystem.folderName,
            fileCount: FileSystem.files.length,
            files: contextFiles
        });

        // Add user message to conversation history
        this.conversationHistory.push({ role: "user", content: userMessage });

        // Trim history if too long (keep last N messages)
        while (this.conversationHistory.length > this.maxHistoryMessages) {
            this.conversationHistory.shift();
        }

        // Build messages array with full history
        const messages = [{ role: "system", content: systemPrompt }, ...this.conversationHistory];

        // Create message element for streaming
        const messageEl = this.addMessage("", "assistant", true);
        const contentEl = messageEl.querySelector(".message-content");

        // Show cancel button, hide send
        this.showCancelButton(true);

        try {
            // Create abort controller for cancellation
            this.currentAbortController = new AbortController();

            // Throttle markdown rendering for performance
            let lastRenderTime = 0;
            let pendingContent = "";
            let renderTimeoutId = null;

            const renderMarkdown = content => {
                contentEl.innerHTML = marked.parse(content);
                // Syntax highlighting
                contentEl.querySelectorAll("pre code").forEach(block => {
                    if (window.Prism) {
                        Prism.highlightElement(block);
                    }
                });
                // Scroll to bottom
                this.messageContainer.scrollTop = this.messageContainer.scrollHeight;
            };

            // Stream response
            const fullContent = await API.stream(
                messages,
                (chunk, full) => {
                    const now = Date.now();
                    pendingContent = full;

                    // Throttle rendering to every 100ms for performance
                    if (now - lastRenderTime >= this.markdownUpdateThrottle) {
                        renderMarkdown(full);
                        lastRenderTime = now;
                    } else if (!renderTimeoutId) {
                        // Schedule final render
                        renderTimeoutId = setTimeout(() => {
                            renderMarkdown(pendingContent);
                            renderTimeoutId = null;
                        }, this.markdownUpdateThrottle);
                    }
                },
                { signal: this.currentAbortController.signal }
            );

            // Final render to ensure complete content
            if (renderTimeoutId) clearTimeout(renderTimeoutId);
            renderMarkdown(fullContent);

            // Add assistant response to conversation history
            this.conversationHistory.push({ role: "assistant", content: fullContent });

            // Save to database
            await DB.saveChat(userMessage, fullContent, {
                model: Utils.storage.get("model"),
                folderName: FileSystem.folderName,
                fileCount: FileSystem.files.length
            });
        } catch (err) {
            const escapedError = Utils.escapeHtml(err.message);

            // Check if request was cancelled (not an actual error)
            if (err.name === "AbortError" || err.message.includes("cancelled")) {
                contentEl.innerHTML = `<p style="color: var(--text-secondary);">⏹️ Request cancelled</p>`;
                Utils.toast.info("Request cancelled");
            } else {
                contentEl.innerHTML = `<p class="error">❌ Error: ${escapedError}</p>`;
                console.error("Elysia call failed:", err);
                Utils.toast.error("Failed to get response from Elysia");
            }

            // Remove the failed user message from history (but not if cancelled)
            if (err.name !== "AbortError" && !err.message.includes("cancelled")) {
                this.conversationHistory.pop();
            }
        } finally {
            // CRITICAL: Reset state even if aborted to prevent race condition
            this.currentAbortController = null;
            this.isProcessing = false;
            this.showCancelButton(false);
            this.updateSendButtonState();
        }
    },

    // Cancel ongoing request
    cancelRequest() {
        if (this.currentAbortController) {
            this.currentAbortController.abort();
            Utils.toast.info("Request cancelled");
        }
    },

    // Toggle between send and cancel buttons
    showCancelButton(show) {
        if (this.cancelButton && this.sendButton) {
            this.cancelButton.style.display = show ? "flex" : "none";
            this.sendButton.style.display = show ? "none" : "flex";
        }
    },

    addMessage(content, type = "assistant", isStreaming = false) {
        const messageEl = document.createElement("div");
        messageEl.className = `message ${type}`;

        const timestamp = Utils.formatDateTime(new Date());
        const author = type === "user" ? "You" : type === "assistant" ? "πŸ’Ž Elysia" : "System";

        messageEl.innerHTML = `

            <div class="message-header">

                <span class="message-author">${author}</span>

                <span class="message-time">${timestamp}</span>

            </div>

            <div class="message-content"></div>

        `;

        const contentEl = messageEl.querySelector(".message-content");

        if (!isStreaming) {
            // Render markdown
            contentEl.innerHTML = marked.parse(content);

            // Syntax highlighting
            contentEl.querySelectorAll("pre code").forEach(block => {
                if (window.Prism) {
                    Prism.highlightElement(block);
                }
            });

            // Add copy buttons to code blocks
            contentEl.querySelectorAll("pre").forEach(pre => {
                const copyBtn = document.createElement("button");
                copyBtn.className = "code-copy-btn";
                copyBtn.textContent = "πŸ“‹ Copy";
                copyBtn.style.cssText =
                    "position: absolute; top: 0.5rem; right: 0.5rem; padding: 0.25rem 0.5rem; background: var(--bg-tertiary); border: 1px solid var(--border-color); border-radius: 4px; cursor: pointer; font-size: 0.8rem;";
                copyBtn.onclick = async () => {
                    const code = pre.querySelector("code");
                    if (code) {
                        await Utils.copyToClipboard(code.textContent);
                        copyBtn.textContent = "βœ… Copied!";
                        setTimeout(() => (copyBtn.textContent = "πŸ“‹ Copy"), 2000);
                    }
                };
                pre.style.position = "relative";
                pre.appendChild(copyBtn);
            });
        }

        this.messageContainer.appendChild(messageEl);
        this.messageContainer.scrollTop = this.messageContainer.scrollHeight;

        return messageEl;
    }
};

export default Chat;