Pamudu13 commited on
Commit
5622e70
·
verified ·
1 Parent(s): f2a2276

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +654 -55
index.html CHANGED
@@ -3,99 +3,698 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>PDF-Based Chatbot</title>
 
 
7
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
 
 
8
  <style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  body {
10
- font-family: Arial, sans-serif;
 
 
 
 
 
 
 
 
 
11
  }
12
- .container {
13
- max-width: 600px;
14
- margin: 0 auto;
15
- padding: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  #chat {
18
- max-height: 400px;
19
- overflow-y: auto;
20
- border: 1px solid #ddd;
21
- padding: 10px;
22
- margin-bottom: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  }
24
- input[type="file"] {
25
- margin-bottom: 20px;
 
 
26
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  </style>
28
  </head>
29
  <body>
30
- <div class="container">
31
- <h1>Chat with Your PDF</h1>
 
 
 
 
32
 
33
- <input type="file" id="pdfUpload" accept=".pdf" />
34
- <button id="uploadButton">Upload PDF</button>
 
 
 
 
 
 
 
 
35
 
36
- <div id="chat"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
- <textarea id="userInput" placeholder="Ask a question" rows="3"></textarea>
39
- <button id="sendButton">Send</button>
 
 
 
 
 
 
40
 
41
- <div id="response"></div>
 
 
 
 
 
 
42
  </div>
43
 
 
44
  <script>
45
  $(document).ready(function() {
46
- // Handle PDF upload
47
- $('#uploadButton').click(function() {
48
- var formData = new FormData();
49
- formData.append('pdf', $('#pdfUpload')[0].files[0]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  $.ajax({
52
- url: '/upload_pdf',
53
  type: 'POST',
54
- data: formData,
55
- contentType: false,
56
- processData: false,
 
 
57
  success: function(response) {
58
- alert(response.message);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  },
60
- error: function(err) {
61
- alert('Error uploading PDF');
 
 
 
 
62
  }
63
  });
 
 
 
 
 
 
 
 
 
 
 
64
  });
65
 
66
- // Handle sending user message
67
- $('#sendButton').click(function() {
68
- var message = $('#userInput').val();
69
- if (message.trim() === "") return;
 
 
 
70
 
71
- var history = $('#chat').data('history') || [];
72
- history.push([message, '']);
 
 
73
 
74
- // Display user message in chat
75
- $('#chat').append(`<div><b>User:</b> ${message}</div>`);
 
 
 
76
 
77
  $.ajax({
78
- url: '/ask_question',
79
  type: 'POST',
80
- contentType: 'application/json',
81
- data: JSON.stringify({ message: message, history: history }),
 
82
  success: function(response) {
83
- // Display assistant's response in chat
84
- $('#chat').append(`<div><b>Assistant:</b> ${response.response}</div>`);
85
- $('#chat').scrollTop($('#chat')[0].scrollHeight);
86
-
87
- // Update history
88
- history.push([message, response.response]);
89
- $('#chat').data('history', history);
 
 
90
  },
91
- error: function(err) {
92
- alert('Error getting response');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  }
94
  });
 
 
 
 
 
95
 
96
- $('#userInput').val('');
97
- });
98
  });
99
  </script>
100
  </body>
101
- </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <!-- Updated Title -->
7
+ <title>Sentry - Document Assistant</title>
8
+ <!-- jQuery -->
9
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
10
+ <!-- Font Awesome for Icons -->
11
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" integrity="sha512-9usAa10IRO0HhonpyAIVpjrylPvoDwiPUiKdWk5t3PyolY1cOd4DSE0Ga+ri4AuTroPR5aQvXU9xC6qOPnzFeg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
12
  <style>
13
+ /* --- CSS Variables for Theme --- */
14
+ :root {
15
+ --primary-color: #2f3136; /* Darker background */
16
+ --secondary-color: #36393f; /* Main chat container background */
17
+ --tertiary-color: #40444b; /* Input fields, message bubbles */
18
+ --text-color: #dcddde; /* Main text color (light gray) */
19
+ --text-muted-color: #b9bbbe; /* Slightly dimmer text */
20
+ --accent-color: #5865f2; /* Sentry/Discord Blurple (Updated) */
21
+ --accent-hover-color: #4e5ae0; /* Darker accent for hover */
22
+ --user-message-bg: var(--accent-color); /* User message bubble */
23
+ --assistant-message-bg: var(--tertiary-color); /* Assistant message bubble */
24
+ --input-bg: var(--tertiary-color);
25
+ --border-color: #202225; /* Darkest borders */
26
+ --success-color: #43b581; /* Green */
27
+ --error-color: #f04747; /* Red */
28
+ --font-family: 'Inter', 'Helvetica Neue', Helvetica, Arial, sans-serif;
29
+ --border-radius: 8px;
30
+ --scrollbar-thumb-color: #202225;
31
+ --scrollbar-track-color: var(--secondary-color);
32
+ }
33
+
34
+ /* --- General Body Styling --- */
35
  body {
36
+ font-family: var(--font-family);
37
+ background-color: var(--primary-color);
38
+ color: var(--text-color);
39
+ margin: 0;
40
+ display: flex;
41
+ justify-content: center;
42
+ align-items: center;
43
+ min-height: 100vh;
44
+ padding: 15px; /* Reduced padding for smaller screens */
45
+ box-sizing: border-box;
46
  }
47
+
48
+ /* --- Main Chat Container --- */
49
+ .chat-container {
50
+ background-color: var(--secondary-color);
51
+ border-radius: var(--border-radius);
52
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
53
+ width: 100%;
54
+ max-width: 750px; /* Slightly wider */
55
+ height: 90vh; /* Adjust height as needed */
56
+ max-height: 800px; /* Max height constraint */
57
+ display: flex;
58
+ flex-direction: column;
59
+ overflow: hidden; /* Contain children */
60
+ }
61
+
62
+ /* --- Header --- */
63
+ header {
64
+ background-color: var(--primary-color);
65
+ padding: 15px 20px;
66
+ border-bottom: 1px solid var(--border-color);
67
+ text-align: center;
68
+ flex-shrink: 0; /* Prevent header from shrinking */
69
+ }
70
+
71
+ header h1 {
72
+ margin: 0;
73
+ font-size: 1.3em;
74
+ color: #fff;
75
+ font-weight: 600;
76
+ }
77
+
78
+ /* --- Upload Section --- */
79
+ .upload-section {
80
+ padding: 12px 20px; /* Reduced padding */
81
+ border-bottom: 1px solid var(--border-color);
82
+ display: flex;
83
+ align-items: center;
84
+ gap: 12px;
85
+ background-color: var(--secondary-color);
86
+ flex-shrink: 0; /* Prevent shrinking */
87
+ }
88
+
89
+ /* Hide default file input */
90
+ #pdfUpload {
91
+ display: none;
92
+ }
93
+
94
+ /* Style the label like a button */
95
+ .upload-label {
96
+ background-color: var(--accent-color);
97
+ color: white;
98
+ padding: 8px 15px;
99
+ border-radius: 5px;
100
+ cursor: pointer;
101
+ transition: background-color 0.2s ease, opacity 0.2s ease;
102
+ font-size: 0.9em;
103
+ white-space: nowrap;
104
+ display: inline-flex;
105
+ align-items: center;
106
+ gap: 8px; /* Space between icon and text */
107
  }
108
+
109
+ .upload-label:hover {
110
+ background-color: var(--accent-hover-color);
111
+ }
112
+ .upload-label[disabled] {
113
+ opacity: 0.6;
114
+ cursor: not-allowed;
115
+ }
116
+
117
+ #uploadStatus {
118
+ font-size: 0.85em;
119
+ color: var(--text-muted-color);
120
+ flex-grow: 1; /* Take remaining space */
121
+ overflow: hidden;
122
+ text-overflow: ellipsis;
123
+ white-space: nowrap;
124
+ line-height: 1.3;
125
+ }
126
+ #uploadStatus i { /* Style icons in status */
127
+ margin-right: 5px;
128
+ }
129
+
130
+ /* --- Chat Area --- */
131
  #chat {
132
+ flex-grow: 1; /* Take available space */
133
+ overflow-y: auto; /* Enable vertical scrolling */
134
+ padding: 20px;
135
+ display: flex;
136
+ flex-direction: column;
137
+ gap: 18px; /* Increased space between messages */
138
+ }
139
+
140
+ /* Scrollbar styling (Webkit) */
141
+ #chat::-webkit-scrollbar {
142
+ width: 8px;
143
+ }
144
+ #chat::-webkit-scrollbar-track {
145
+ background: var(--scrollbar-track-color);
146
+ border-radius: 4px;
147
+ }
148
+ #chat::-webkit-scrollbar-thumb {
149
+ background-color: var(--scrollbar-thumb-color);
150
+ border-radius: 4px;
151
+ }
152
+ #chat::-webkit-scrollbar-thumb:hover {
153
+ background-color: var(--tertiary-color); /* Slightly lighter on hover */
154
+ }
155
+ /* Scrollbar styling (Firefox) */
156
+ #chat {
157
+ scrollbar-width: thin;
158
+ scrollbar-color: var(--scrollbar-thumb-color) var(--scrollbar-track-color);
159
+ }
160
+
161
+ /* --- Message Styling --- */
162
+ .message {
163
+ display: flex;
164
+ max-width: 80%; /* Max width of message bubble */
165
+ opacity: 0; /* Start hidden for animation */
166
+ animation: fadeIn 0.4s ease forwards;
167
+ position: relative; /* For potential absolute elements later */
168
+ line-height: 1.45; /* Improved readability */
169
+ }
170
+
171
+ @keyframes fadeIn {
172
+ to { opacity: 1; }
173
+ }
174
+
175
+ /* Common structure for icon + text block */
176
+ .message-inner-wrapper {
177
+ display: flex;
178
+ gap: 10px; /* Space between icon and text block */
179
+ width: 100%; /* Ensure wrapper takes full width */
180
+ }
181
+
182
+ .sender-icon {
183
+ font-size: 1.1em; /* Adjust icon size */
184
+ color: var(--text-muted-color); /* Default icon color */
185
+ margin-top: 2px; /* Fine-tune vertical alignment */
186
+ flex-shrink: 0; /* Prevent icon from shrinking */
187
+ }
188
+
189
+ .message-text-block {
190
+ display: flex;
191
+ flex-direction: column; /* Stack name and content */
192
+ flex-grow: 1; /* Allow text block to grow */
193
+ }
194
+
195
+ .sender-name {
196
+ font-weight: 600; /* Bolder name */
197
+ margin-bottom: 5px; /* Space below name */
198
+ font-size: 0.88em;
199
+ color: var(--text-muted-color);
200
+ }
201
+
202
+ .message-content {
203
+ padding: 10px 15px;
204
+ border-radius: var(--border-radius);
205
+ word-wrap: break-word;
206
+ font-size: 0.95em;
207
+ background-color: var(--assistant-message-bg); /* Default background */
208
+ color: var(--text-color);
209
+ }
210
+
211
+ /* User Message Specific Styles */
212
+ .message.user {
213
+ margin-left: auto; /* Align bubble to the right */
214
+ flex-direction: row-reverse; /* Put icon on the right */
215
+ }
216
+ .message.user .message-inner-wrapper {
217
+ flex-direction: row-reverse; /* Reverse icon and text block order */
218
+ }
219
+ .message.user .sender-icon {
220
+ color: var(--text-muted-color); /* Optional: Different user icon color */
221
+ }
222
+ .message.user .sender-name {
223
+ text-align: right; /* Align name to the right */
224
+ color: inherit; /* Use bubble text color */
225
+ }
226
+ .message.user .message-content {
227
+ background-color: var(--user-message-bg);
228
+ color: white; /* Text color for user bubble */
229
+ border-bottom-right-radius: 4px; /* Subtle shape difference */
230
+ }
231
+
232
+ /* Assistant Message Specific Styles */
233
+ .message.assistant {
234
+ margin-right: auto; /* Align bubble to the left */
235
+ }
236
+ .message.assistant .sender-icon {
237
+ color: var(--accent-color); /* Sentry icon color */
238
+ }
239
+ .message.assistant .sender-name {
240
+ color: var(--accent-color); /* Sentry name color */
241
+ }
242
+ .message.assistant .message-content {
243
+ background-color: var(--assistant-message-bg);
244
+ border-bottom-left-radius: 4px; /* Subtle shape difference */
245
+ }
246
+
247
+ /* System Message Styling (for errors, info) */
248
+ .message.system {
249
+ font-style: italic;
250
+ color: var(--text-muted-color); /* Default system text color */
251
+ text-align: center;
252
+ width: 100%;
253
+ max-width: 100%;
254
+ font-size: 0.9em;
255
+ margin: 8px 0; /* Adjust spacing */
256
+ gap: 0; /* No gap needed */
257
+ }
258
+ .message.system .message-inner-wrapper { /* System messages don't need the icon/name wrapper */
259
+ justify-content: center;
260
+ }
261
+ .message.system .message-content {
262
+ background: none;
263
+ padding: 0;
264
+ display: inline-block; /* Center the text block */
265
+ }
266
+ .message.system .message-content.error {
267
+ color: var(--error-color);
268
+ }
269
+ .message.system .message-content.info {
270
+ color: var(--success-color);
271
+ }
272
+
273
+
274
+ /* --- Typing Indicator --- */
275
+ .typing-indicator {
276
+ display: flex;
277
+ align-items: center;
278
+ padding: 0px 20px 5px 20px; /* Add padding */
279
+ opacity: 0;
280
+ transition: opacity 0.3s ease, height 0.3s ease;
281
+ height: 0; /* Start hidden */
282
+ overflow: hidden;
283
+ flex-shrink: 0; /* Prevent shrinking */
284
+ gap: 8px; /* Space between icon and text/dots */
285
+ }
286
+ .typing-indicator.visible {
287
+ opacity: 1;
288
+ height: 25px; /* Make visible */
289
+ }
290
+ .typing-indicator .sender-icon { /* Reuse sender icon style */
291
+ font-size: 0.95em;
292
+ color: var(--accent-color);
293
  }
294
+ .typing-indicator span {
295
+ font-size: 0.88em;
296
+ color: var(--text-muted-color);
297
+ margin-right: 5px;
298
  }
299
+ .typing-indicator .dot {
300
+ display: inline-block;
301
+ width: 6px;
302
+ height: 6px;
303
+ background-color: var(--text-muted-color);
304
+ border-radius: 50%;
305
+ margin: 0 2px;
306
+ animation: typing 1.2s infinite ease-in-out;
307
+ }
308
+ .typing-indicator .dot:nth-child(1) { animation-delay: 0.0s; }
309
+ .typing-indicator .dot:nth-child(2) { animation-delay: 0.2s; }
310
+ .typing-indicator .dot:nth-child(3) { animation-delay: 0.4s; }
311
+
312
+ @keyframes typing {
313
+ 0%, 100% { transform: translateY(0); opacity: 0.5; }
314
+ 50% { transform: translateY(-5px); opacity: 1; }
315
+ }
316
+
317
+ /* --- Input Area --- */
318
+ .input-area {
319
+ display: flex;
320
+ padding: 15px 20px;
321
+ border-top: 1px solid var(--border-color);
322
+ background-color: var(--secondary-color); /* Match chat bg */
323
+ flex-shrink: 0; /* Prevent shrinking */
324
+ gap: 10px; /* Space between textarea and button */
325
+ }
326
+
327
+ #userInput {
328
+ flex-grow: 1;
329
+ background-color: var(--input-bg);
330
+ color: var(--text-color);
331
+ border: 1px solid var(--border-color); /* Subtle border */
332
+ border-radius: var(--border-radius);
333
+ padding: 10px 15px;
334
+ resize: none; /* Prevent manual resizing */
335
+ font-family: var(--font-family);
336
+ font-size: 0.95em;
337
+ max-height: 120px; /* Limit growth */
338
+ box-sizing: border-box;
339
+ overflow-y: auto; /* Allow scrolling if text exceeds max height */
340
+ line-height: 1.4; /* Match message line height */
341
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
342
+ }
343
+
344
+ #userInput:focus {
345
+ outline: none;
346
+ border-color: var(--accent-color);
347
+ box-shadow: 0 0 0 1px var(--accent-color);
348
+ }
349
+ #userInput::placeholder {
350
+ color: var(--text-muted-color);
351
+ opacity: 0.8;
352
+ }
353
+
354
+
355
+ #sendButton {
356
+ background-color: var(--accent-color);
357
+ color: white;
358
+ border: none;
359
+ border-radius: var(--border-radius);
360
+ padding: 0 15px;
361
+ cursor: pointer;
362
+ font-size: 1.1em; /* Icon size */
363
+ transition: background-color 0.2s ease, opacity 0.2s ease;
364
+ display: flex;
365
+ align-items: center;
366
+ justify-content: center;
367
+ height: 42px; /* Match input height approx */
368
+ width: 45px; /* Fixed width for the button */
369
+ flex-shrink: 0; /* Prevent button from shrinking */
370
+ }
371
+
372
+ #sendButton:hover {
373
+ background-color: var(--accent-hover-color);
374
+ }
375
+
376
+ #sendButton:disabled {
377
+ opacity: 0.5;
378
+ cursor: not-allowed;
379
+ background-color: var(--tertiary-color); /* Dim background when disabled */
380
+ }
381
+
382
  </style>
383
  </head>
384
  <body>
385
+ <!-- Main Chat Container -->
386
+ <div class="chat-container">
387
+ <!-- Header -->
388
+ <header>
389
+ <h1>SentryLabs Document Assistant</h1>
390
+ </header>
391
 
392
+ <!-- PDF Upload Section -->
393
+ <div class="upload-section">
394
+ <label for="pdfUpload" class="upload-label" id="uploadLabel">
395
+ <i class="fas fa-file-pdf"></i> Choose PDF
396
+ </label>
397
+ <input type="file" id="pdfUpload" accept=".pdf" />
398
+ <span id="uploadStatus">Upload a document for analysis.</span>
399
+ <!-- Hidden button, not really used now -->
400
+ <button id="uploadButton" style="display: none;">Upload</button>
401
+ </div>
402
 
403
+ <!-- Chat Messages Area -->
404
+ <div id="chat">
405
+ <!-- Initial Greeting from Sentry -->
406
+ <div class="message assistant">
407
+ <div class="message-inner-wrapper">
408
+ <i class="fas fa-shield-alt sender-icon"></i> <!-- Sentry Icon -->
409
+ <div class="message-text-block">
410
+ <div class="sender-name">Sentry</div>
411
+ <div class="message-content">
412
+ I am Sentry, your SentryLabs assistant. Please upload a PDF document using the button above to begin our analysis.
413
+ </div>
414
+ </div>
415
+ </div>
416
+ </div>
417
+ </div>
418
 
419
+ <!-- Typing Indicator -->
420
+ <div class="typing-indicator" id="typingIndicator">
421
+ <i class="fas fa-shield-alt sender-icon"></i>
422
+ <span>Sentry is analyzing...</span>
423
+ <div class="dot"></div>
424
+ <div class="dot"></div>
425
+ <div class="dot"></div>
426
+ </div>
427
 
428
+ <!-- User Input Area -->
429
+ <div class="input-area">
430
+ <textarea id="userInput" placeholder="Ask Sentry about the document..." rows="1" disabled></textarea> <!-- Start disabled -->
431
+ <button id="sendButton" title="Send Message" disabled> <!-- Start disabled -->
432
+ <i class="fas fa-paper-plane"></i>
433
+ </button>
434
+ </div>
435
  </div>
436
 
437
+ <!-- JavaScript -->
438
  <script>
439
  $(document).ready(function() {
440
+ // --- State Variables ---
441
+ let chatHistory = []; // Stores pairs: [[userMsg, assistantMsg], ...]
442
+ let pdfUploaded = false;
443
+
444
+ // --- DOM Elements ---
445
+ const chatBox = $('#chat');
446
+ const userInput = $('#userInput');
447
+ const sendButton = $('#sendButton');
448
+ const uploadLabel = $('#uploadLabel');
449
+ const pdfUploadInput = $('#pdfUpload');
450
+ const uploadStatus = $('#uploadStatus');
451
+ const typingIndicator = $('#typingIndicator');
452
+
453
+ // --- Helper Functions ---
454
+
455
+ // Function to add a message to the chat interface
456
+ function addMessage(sender, text, type = 'normal') {
457
+ // Sanitize text to prevent basic HTML injection, preserve newlines for <br> conversion
458
+ const sanitizedHtml = $('<div>').text(text).html().replace(/\n/g, '<br>');
459
+ let messageClass = sender; // 'user', 'assistant', or 'system'
460
+ let senderName = '';
461
+ let senderIconHtml = ''; // Icon HTML string
462
+
463
+ // Define names and icons based on sender
464
+ if (sender === 'user') {
465
+ senderName = 'You';
466
+ // Optional: User icon
467
+ // senderIconHtml = '<i class="fas fa-user sender-icon"></i>';
468
+ } else if (sender === 'assistant') {
469
+ senderName = 'Sentry';
470
+ senderIconHtml = '<i class="fas fa-shield-alt sender-icon"></i>';
471
+ }
472
+
473
+ let messageHtml;
474
+
475
+ // Handle different message types (normal, error, info)
476
+ if (type === 'error' || type === 'info') {
477
+ messageClass = 'system';
478
+ // Simple centered text for system messages
479
+ messageHtml = `<div class="message-inner-wrapper">
480
+ <div class="message-content ${type}">${sanitizedHtml}</div>
481
+ </div>`;
482
+ } else {
483
+ // Standard message structure with icon, name, content
484
+ const nameHtml = senderName ? `<div class="sender-name">${senderName}</div>` : '';
485
+ messageHtml = `
486
+ <div class="message-inner-wrapper">
487
+ ${senderIconHtml}
488
+ <div class="message-text-block">
489
+ ${nameHtml}
490
+ <div class="message-content">${sanitizedHtml}</div>
491
+ </div>
492
+ </div>
493
+ `;
494
+ }
495
+
496
+ // Create message element and append to chat
497
+ const messageElement = $(`<div class="message ${messageClass}">${messageHtml}</div>`);
498
+ chatBox.append(messageElement);
499
+
500
+ // Scroll to the bottom of the chat
501
+ scrollToBottom();
502
+ }
503
+
504
+ // Function to scroll chat box to the bottom
505
+ function scrollToBottom() {
506
+ chatBox.animate({ scrollTop: chatBox[0].scrollHeight }, 300);
507
+ }
508
+
509
+ // Function to show/hide typing indicator
510
+ function showTypingIndicator(show) {
511
+ if (show) {
512
+ typingIndicator.addClass('visible');
513
+ } else {
514
+ typingIndicator.removeClass('visible');
515
+ }
516
+ // Adjust scroll after potential layout shift from indicator
517
+ setTimeout(scrollToBottom, 50);
518
+ }
519
+
520
+ // Adjust textarea height dynamically based on content
521
+ function adjustTextareaHeight() {
522
+ const textarea = userInput[0];
523
+ textarea.style.height = 'auto'; // Reset height to calculate scroll height accurately
524
+ // Calculate the height needed for the content, plus a small buffer if desired
525
+ const scrollHeight = textarea.scrollHeight;
526
+ textarea.style.height = scrollHeight + 'px';
527
 
528
+ // Apply max-height constraint from CSS
529
+ const maxHeight = parseInt(userInput.css('max-height'));
530
+ if (scrollHeight > maxHeight) {
531
+ textarea.style.overflowY = 'auto'; // Show scrollbar if content exceeds max height
532
+ } else {
533
+ textarea.style.overflowY = 'hidden'; // Hide scrollbar if content fits
534
+ }
535
+ }
536
+
537
+ // --- Event Handlers ---
538
+
539
+ // Trigger hidden file input when the styled label is clicked
540
+ uploadLabel.on('click', function() {
541
+ if (!$(this).prop('disabled')) { // Only trigger if not disabled
542
+ pdfUploadInput.click();
543
+ }
544
+ });
545
+
546
+ // Handle file selection via the hidden input
547
+ pdfUploadInput.on('change', function() {
548
+ const file = this.files[0];
549
+ if (file) {
550
+ if (file.type === "application/pdf") {
551
+ uploadStatus.text(`Selected: ${file.name}`).css('color', 'var(--text-muted-color)');
552
+ uploadFile(file); // Automatically start the upload
553
+ } else {
554
+ uploadStatus.html('<i class="fas fa-exclamation-triangle"></i> Error: Please select a PDF file.').css('color', 'var(--error-color)');
555
+ this.value = ''; // Reset file input to allow re-selection of the same file if needed
556
+ }
557
+ }
558
+ });
559
+
560
+ // Handle sending a message
561
+ function sendMessage() {
562
+ const message = userInput.val().trim();
563
+ // Prevent sending empty messages or if PDF isn't uploaded
564
+ if (message === "" || !pdfUploaded) return;
565
+
566
+ // Display user's message
567
+ addMessage('user', message);
568
+ const currentUserMessage = message; // Store for history pairing
569
+
570
+ // Clear input, disable controls, show typing indicator
571
+ userInput.val('').prop('disabled', true);
572
+ sendButton.prop('disabled', true);
573
+ showTypingIndicator(true);
574
+ adjustTextareaHeight(); // Reset height after clearing
575
+
576
+ // Prepare history for the API (only completed pairs)
577
+ const historyForApi = chatHistory.slice(); // Send a copy
578
+
579
+ // Make the AJAX call to the backend
580
  $.ajax({
581
+ url: '/ask_question',
582
  type: 'POST',
583
+ contentType: 'application/json',
584
+ data: JSON.stringify({
585
+ message: currentUserMessage,
586
+ history: historyForApi
587
+ }),
588
  success: function(response) {
589
+ if (response && response.response) {
590
+ // Display Sentry's response
591
+ addMessage('assistant', response.response);
592
+ // Add the completed user/assistant pair to history
593
+ chatHistory.push([currentUserMessage, response.response]);
594
+ } else {
595
+ // Handle cases where backend might return empty response field
596
+ const errorMsg = "Received an unexpected empty response from Sentry.";
597
+ addMessage('system', errorMsg, 'error');
598
+ // Record the turn with an empty assistant message for history integrity
599
+ chatHistory.push([currentUserMessage, ""]);
600
+ }
601
+ },
602
+ error: function(jqXHR, textStatus, errorThrown) {
603
+ // Display error message in chat
604
+ let errorMsg = 'An error occurred while communicating with Sentry.';
605
+ if (jqXHR.responseJSON && jqXHR.responseJSON.response) { // Check for error in 'response' key first
606
+ errorMsg = jqXHR.responseJSON.response;
607
+ } else if (jqXHR.responseJSON && jqXHR.responseJSON.error) { // Check 'error' key
608
+ errorMsg = jqXHR.responseJSON.error;
609
+ }
610
+ addMessage('system', errorMsg, 'error');
611
+ // Record the turn with an empty assistant message for history integrity
612
+ chatHistory.push([currentUserMessage, ""]);
613
  },
614
+ complete: function() {
615
+ // Re-enable input, hide typing indicator
616
+ userInput.prop('disabled', false).focus(); // Re-enable and focus
617
+ showTypingIndicator(false);
618
+ // Re-evaluate send button state (might have typed while waiting)
619
+ sendButton.prop('disabled', userInput.val().trim() === '');
620
  }
621
  });
622
+ }
623
+
624
+ // Attach send handler to button click
625
+ sendButton.click(sendMessage);
626
+
627
+ // Attach send handler to Enter key press in textarea (allow Shift+Enter for newline)
628
+ userInput.keypress(function(e) {
629
+ if (e.which === 13 && !e.shiftKey) { // Enter key pressed without Shift
630
+ e.preventDefault(); // Prevent default newline behavior
631
+ sendMessage();
632
+ }
633
  });
634
 
635
+ // Enable/disable send button based on input content and PDF status
636
+ // Also adjust textarea height on input
637
+ userInput.on('input keyup', function() {
638
+ adjustTextareaHeight();
639
+ const isEmpty = $(this).val().trim() === '';
640
+ sendButton.prop('disabled', isEmpty || !pdfUploaded);
641
+ });
642
 
643
+ // --- File Upload Function ---
644
+ function uploadFile(file) {
645
+ const formData = new FormData();
646
+ formData.append('pdf', file);
647
 
648
+ // Update UI to show uploading state
649
+ uploadStatus.html(`<i class="fas fa-spinner fa-spin"></i> Processing: ${file.name}...`).css('color', 'var(--text-muted-color)');
650
+ uploadLabel.prop('disabled', true).css('opacity', 0.6); // Disable upload button visually
651
+ userInput.prop('disabled', true); // Disable input during upload
652
+ sendButton.prop('disabled', true); // Disable send during upload
653
 
654
  $.ajax({
655
+ url: '/upload_pdf',
656
  type: 'POST',
657
+ data: formData,
658
+ contentType: false, // Important for FormData
659
+ processData: false, // Important for FormData
660
  success: function(response) {
661
+ // Success: Update status, enable input/send
662
+ uploadStatus.html(`<i class="fas fa-check-circle"></i> Document ready for analysis.`).css('color', 'var(--success-color)');
663
+ pdfUploaded = true;
664
+ userInput.prop('disabled', false).attr('placeholder', 'Ask Sentry about the document...').focus();
665
+ // Enable send button only if there's text already (unlikely but possible)
666
+ sendButton.prop('disabled', userInput.val().trim() === '');
667
+ // Optional: Clear chat history if you want each PDF to start fresh
668
+ // chatHistory = [];
669
+ // chatBox.find('.message:not(:first-child)').remove(); // Remove all but initial greeting
670
  },
671
+ error: function(jqXHR, textStatus, errorThrown) {
672
+ // Error: Show error message, keep controls disabled
673
+ let errorMsg = 'Failed to process the PDF.';
674
+ if (jqXHR.responseJSON && jqXHR.responseJSON.error) {
675
+ errorMsg = jqXHR.responseJSON.error;
676
+ } // Add more specific parsing if needed
677
+ uploadStatus.html(`<i class="fas fa-exclamation-triangle"></i> Error: ${errorMsg}`).css('color', 'var(--error-color)');
678
+ pdfUploaded = false;
679
+ userInput.prop('disabled', true).attr('placeholder', 'Upload failed. Please try again.');
680
+ sendButton.prop('disabled', true);
681
+ // Add error to chat as well
682
+ addMessage('system', `PDF processing failed: ${errorMsg}`, 'error');
683
+ },
684
+ complete: function() {
685
+ // Always re-enable the upload button regardless of outcome
686
+ uploadLabel.prop('disabled', false).css('opacity', 1);
687
+ // Clear the file input value so the user can re-upload the same file if needed after an error
688
+ pdfUploadInput.val('');
689
  }
690
  });
691
+ }
692
+
693
+ // --- Initial Page Load Setup ---
694
+ adjustTextareaHeight(); // Initial adjustment for placeholder
695
+ // Initial state is set directly in the HTML (disabled input/button)
696
 
 
 
697
  });
698
  </script>
699
  </body>
700
+ </html>