NanobotzAI commited on
Commit
a8f24ef
·
verified ·
1 Parent(s): 15f4257

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +330 -629
index.html CHANGED
@@ -3,698 +3,399 @@
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>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>SentryLabs AI Assistant</title>
 
 
 
 
 
7
  <style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  body {
9
+ font-family: 'Segoe UI', Arial, sans-serif;
10
+ line-height: 1.6;
11
+ color: #333;
12
+ max-width: 1200px;
13
+ margin: 0 auto;
14
+ padding: 20px;
15
+ background-color: #f5f7fa;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  }
17
+
18
+ h1 {
19
+ color: #2c3e50;
 
 
 
20
  text-align: center;
21
+ margin-bottom: 30px;
 
 
 
 
 
 
 
22
  }
23
+
24
+ .container {
 
 
 
25
  display: flex;
26
+ gap: 20px;
27
+ height: calc(100vh - 160px);
28
+ min-height: 500px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  }
30
+
31
+ .upload-section {
32
+ flex: 1;
33
+ background: white;
34
+ border-radius: 8px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  padding: 20px;
36
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
37
  display: flex;
38
  flex-direction: column;
 
 
 
 
 
 
39
  }
40
+
41
+ .chat-section {
42
+ flex: 2;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  display: flex;
44
+ flex-direction: column;
45
+ background: white;
46
+ border-radius: 8px;
47
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
+
50
+ #upload-form {
51
  display: flex;
52
+ flex-direction: column;
53
+ gap: 15px;
54
+ margin-bottom: 20px;
55
+ }
56
+
57
+ .dropzone {
58
+ border: 2px dashed #2c3e50;
59
+ border-radius: 8px;
60
+ padding: 40px 20px;
61
+ text-align: center;
62
+ cursor: pointer;
63
+ margin-bottom: 20px;
64
+ background: #f9fafc;
65
+ flex-grow: 1;
66
+ display: flex;
67
+ flex-direction: column;
68
+ justify-content: center;
69
+ align-items: center;
 
 
 
 
 
 
70
  }
71
+
72
+ .dropzone:hover {
73
+ background: #eef2f7;
74
  }
75
+
76
+ .dropzone p {
77
+ margin: 10px 0 0;
78
+ color: #7f8c8d;
79
  }
80
+
81
+ input[type="file"] {
82
+ display: none;
 
 
 
 
 
83
  }
84
+
85
+ button {
86
+ background-color: #3498db;
87
+ color: white;
88
+ border: none;
89
+ padding: 10px 15px;
90
+ border-radius: 5px;
91
+ cursor: pointer;
92
+ font-weight: bold;
93
+ transition: background-color 0.3s;
94
  }
95
+
96
+ button:hover {
97
+ background-color: #2980b9;
98
  }
99
+
100
+ button:disabled {
101
+ background-color: #95a5a6;
102
+ cursor: not-allowed;
 
 
103
  }
104
+
105
+ #status-message {
106
+ margin-top: 15px;
107
+ padding: 10px;
108
+ border-radius: 5px;
109
  text-align: center;
 
 
 
 
 
110
  }
111
+
112
+ .success {
113
+ background-color: #d5f5e3;
114
+ color: #27ae60;
 
 
 
115
  }
116
+
117
+ .error {
118
+ background-color: #fadbd8;
119
+ color: #c0392b;
120
+ }
121
+
122
+ .chat-container {
123
+ flex-grow: 1;
 
 
124
  display: flex;
125
+ flex-direction: column;
126
+ padding: 20px;
 
 
 
127
  overflow: hidden;
 
 
128
  }
129
+
130
+ .messages {
131
+ flex-grow: 1;
132
+ overflow-y: auto;
133
+ padding-right: 10px;
134
+ margin-bottom: 20px;
 
135
  }
136
+
137
+ .message {
138
+ margin-bottom: 15px;
139
+ padding: 12px 16px;
140
+ border-radius: 8px;
141
+ max-width: 80%;
142
+ word-wrap: break-word;
143
  }
144
+
145
+ .user {
146
+ background-color: #e3f2fd;
147
+ margin-left: auto;
148
+ border-bottom-right-radius: 2px;
 
 
 
149
  }
150
+
151
+ .bot {
152
+ background-color: #f1f1f1;
153
+ margin-right: auto;
154
+ border-bottom-left-radius: 2px;
 
 
155
  }
156
+
157
+ .input-container {
 
158
  display: flex;
159
+ gap: 10px;
 
 
 
 
160
  }
161
+
162
+ #user-input {
163
  flex-grow: 1;
164
+ padding: 12px;
165
+ border: 1px solid #ddd;
166
+ border-radius: 5px;
167
+ resize: none;
168
+ font-family: inherit;
 
 
 
 
 
 
 
 
169
  }
170
+
171
+ #user-input:focus {
172
  outline: none;
173
+ border-color: #3498db;
 
174
  }
175
+
176
+ #send-button {
177
+ align-self: flex-end;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  }
179
+
180
+ .initial-message {
181
+ text-align: center;
182
+ color: #7f8c8d;
183
+ margin-top: 40px;
184
  }
185
 
186
+ @media (max-width: 768px) {
187
+ .container {
188
+ flex-direction: column;
189
+ height: auto;
190
+ }
191
+
192
+ .upload-section, .chat-section {
193
+ width: 100%;
194
+ }
195
  }
 
196
  </style>
197
  </head>
198
  <body>
199
+ <h1>SentryLabs AI Assistant</h1>
200
+
201
+ <div class="container">
 
 
 
 
 
202
  <div class="upload-section">
203
+ <h2>Upload Document</h2>
204
+ <form id="upload-form">
205
+ <div class="dropzone" id="dropzone">
206
+ <svg width="50" height="50" viewBox="0 0 24 24" fill="none" stroke="#2c3e50" stroke-width="2">
207
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
208
+ <polyline points="17 8 12 3 7 8"></polyline>
209
+ <line x1="12" y1="3" x2="12" y2="15"></line>
210
+ </svg>
211
+ <p>Drag & drop your PDF or click to browse</p>
212
+ </div>
213
+ <input type="file" id="file-input" accept=".pdf">
214
+ <button type="submit" id="upload-button" disabled>Upload PDF</button>
215
+ </form>
216
+ <div id="status-message"></div>
217
  </div>
218
+
219
+ <div class="chat-section">
220
+ <div class="chat-container">
221
+ <div class="messages" id="chat-messages">
222
+ <div class="initial-message">
223
+ <p>Upload a PDF document to start chatting with the AI assistant.</p>
224
+ </div>
225
+ </div>
226
+ <div class="input-container">
227
+ <textarea id="user-input" placeholder="Ask a question about the document..." rows="2" disabled></textarea>
228
+ <button id="send-button" disabled>Send</button>
229
+ </div>
 
 
230
  </div>
231
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  </div>
233
 
 
234
  <script>
235
+ document.addEventListener('DOMContentLoaded', function() {
236
+ const dropzone = document.getElementById('dropzone');
237
+ const fileInput = document.getElementById('file-input');
238
+ const uploadForm = document.getElementById('upload-form');
239
+ const uploadButton = document.getElementById('upload-button');
240
+ const statusMessage = document.getElementById('status-message');
241
+ const chatMessages = document.getElementById('chat-messages');
242
+ const userInput = document.getElementById('user-input');
243
+ const sendButton = document.getElementById('send-button');
244
+
245
+ let selectedFile = null;
246
+
247
+ // Dropzone functionality
248
+ dropzone.addEventListener('click', () => fileInput.click());
249
+
250
+ dropzone.addEventListener('dragover', (e) => {
251
+ e.preventDefault();
252
+ dropzone.style.borderColor = '#3498db';
253
+ });
254
+
255
+ dropzone.addEventListener('dragleave', () => {
256
+ dropzone.style.borderColor = '#2c3e50';
257
+ });
258
+
259
+ dropzone.addEventListener('drop', (e) => {
260
+ e.preventDefault();
261
+ dropzone.style.borderColor = '#2c3e50';
262
+
263
+ if (e.dataTransfer.files.length) {
264
+ handleFileSelection(e.dataTransfer.files[0]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
  }
266
+ });
267
+
268
+ fileInput.addEventListener('change', () => {
269
+ if (fileInput.files.length) {
270
+ handleFileSelection(fileInput.files[0]);
 
 
 
 
 
 
 
 
 
 
 
 
 
271
  }
 
 
 
 
 
 
 
 
 
272
  });
273
+
274
+ function handleFileSelection(file) {
275
+ if (file.type !== 'application/pdf') {
276
+ showStatus('Please select a PDF file.', 'error');
277
+ return;
278
+ }
279
+
280
+ selectedFile = file;
281
+ dropzone.innerHTML = `<p>${file.name}</p>`;
282
+ uploadButton.disabled = false;
283
+ showStatus('', '');
284
+ }
285
+
286
+ // PDF Upload functionality
287
+ uploadForm.addEventListener('submit', async (e) => {
288
+ e.preventDefault();
289
+
290
+ if (!selectedFile) return;
291
+
292
+ const formData = new FormData();
293
+ formData.append('pdf', selectedFile);
294
+
295
+ uploadButton.disabled = true;
296
+ showStatus('Uploading and processing...', '');
297
+
298
+ try {
299
+ const response = await fetch('/upload_pdf', {
300
+ method: 'POST',
301
+ body: formData
302
+ });
303
+
304
+ const result = await response.json();
305
+
306
+ if (response.ok) {
307
+ showStatus(result.message, 'success');
308
+ enableChat();
309
+
310
+ // Clear initial message and add bot welcome
311
+ chatMessages.innerHTML = '';
312
+ addBotMessage("Hello! I've analyzed your document. What would you like to know about it?");
313
  } else {
314
+ showStatus(result.error, 'error');
315
+ uploadButton.disabled = false;
316
  }
317
+ } catch (error) {
318
+ showStatus('Network error. Please try again.', 'error');
319
+ uploadButton.disabled = false;
320
  }
321
  });
322
+
323
+ function showStatus(message, type) {
324
+ statusMessage.textContent = message;
325
+ statusMessage.className = type;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  }
327
+
328
+ function enableChat() {
329
+ userInput.disabled = false;
330
+ sendButton.disabled = false;
331
+ userInput.focus();
332
+ }
333
+
334
+ // Chat functionality
335
+ sendButton.addEventListener('click', sendMessage);
336
+
337
+ userInput.addEventListener('keypress', (e) => {
338
+ if (e.key === 'Enter' && !e.shiftKey) {
339
+ e.preventDefault();
340
  sendMessage();
341
  }
342
  });
343
+
344
+ async function sendMessage() {
345
+ const message = userInput.value.trim();
346
+ if (!message) return;
347
+
348
+ // Add user message to chat
349
+ addUserMessage(message);
350
+ userInput.value = '';
351
+
352
+ // Disable input while processing
353
+ userInput.disabled = true;
354
+ sendButton.disabled = true;
355
+
356
+ try {
357
+ const response = await fetch('/ask_question', {
358
+ method: 'POST',
359
+ headers: {
360
+ 'Content-Type': 'application/json'
361
+ },
362
+ body: JSON.stringify({ message })
363
+ });
364
+
365
+ const result = await response.json();
366
+
367
+ // Add bot response to chat
368
+ addBotMessage(result.response);
369
+ } catch (error) {
370
+ addBotMessage("I'm sorry, I couldn't process your request. Please try again.");
371
+ } finally {
372
+ // Re-enable input
373
+ userInput.disabled = false;
374
+ sendButton.disabled = false;
375
+ userInput.focus();
376
+ }
377
+ }
378
+
379
+ function addUserMessage(text) {
380
+ const messageDiv = document.createElement('div');
381
+ messageDiv.className = 'message user';
382
+ messageDiv.textContent = text;
383
+ chatMessages.appendChild(messageDiv);
384
+ scrollToBottom();
385
+ }
386
+
387
+ function addBotMessage(text) {
388
+ const messageDiv = document.createElement('div');
389
+ messageDiv.className = 'message bot';
390
+ messageDiv.textContent = text;
391
+ chatMessages.appendChild(messageDiv);
392
+ scrollToBottom();
393
+ }
394
+
395
+ function scrollToBottom() {
396
+ chatMessages.scrollTop = chatMessages.scrollHeight;
 
 
 
397
  }
 
 
 
 
 
398
  });
399
  </script>
400
  </body>
401
+ </html>