55 < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
66 < title > PDF Chat with Gemini</ title >
77 < link href ="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css " rel ="stylesheet ">
8+ < link rel ="stylesheet " href ="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css ">
89 < style >
9- body { background-color : # f8f9fa ; }
10- .sidebar { height : 100vh ; background-color : # ffffff ; border-right : 1px solid # dee2e6 ; padding : 20px ; overflow-y : auto; }
11- .chat-container { height : 100vh ; display : flex; flex-direction : column; }
12- .chat-messages { flex-grow : 1 ; padding : 20px ; overflow-y : auto; background-color : # ffffff ; margin : 20px ; border-radius : 10px ; border : 1px solid # dee2e6 ; }
13- .chat-input { padding : 20px ; background-color : # ffffff ; border-top : 1px solid # dee2e6 ; }
14- .message { margin-bottom : 15px ; padding : 10px 15px ; border-radius : 15px ; max-width : 80% ; }
15- .user-message { background-color : # 007bff ; color : white; align-self : flex-end; margin-left : auto; }
16- .bot-message { background-color : # e9ecef ; color : # 333 ; align-self : flex-start; }
17- .loading { font-style : italic; color : # 888 ; }
18- .pdf-item { cursor : pointer; padding : 10px ; border-radius : 5px ; margin-bottom : 5px ; transition : background 0.3s ; }
19- .pdf-item : hover { background-color : # f1f3f5 ; }
20- .pdf-item .active { background-color : # e7f1ff ; border : 1px solid # 0d6efd ; }
10+ body {
11+ background-color : # f0f2f5 ;
12+ font-family : 'Segoe UI' , Tahoma, Geneva, Verdana, sans-serif;
13+ }
14+ .sidebar {
15+ height : 100vh ;
16+ background : linear-gradient (135deg , # 667eea 0% , # 764ba2 100% );
17+ color : white;
18+ border-right : none;
19+ padding : 25px ;
20+ overflow-y : auto;
21+ box-shadow : 2px 0 10px rgba (0 , 0 , 0 , 0.1 );
22+ }
23+ .sidebar hr { border-color : rgba (255 , 255 , 255 , 0.2 ); }
24+ .sidebar h3 , .sidebar h5 { font-weight : 600 ; text-shadow : 1px 1px 2px rgba (0 , 0 , 0 , 0.1 ); }
25+ .form-label { color : # f8f9fa ; }
26+ .form-control { border-radius : 8px ; border : none; padding : 10px ; }
27+ .btn-primary {
28+ background-color : # ff7e67 ;
29+ border : none;
30+ border-radius : 8px ;
31+ padding : 10px ;
32+ font-weight : 600 ;
33+ transition : all 0.3s ease;
34+ box-shadow : 0 4px 6px rgba (255 , 126 , 103 , 0.3 );
35+ }
36+ .btn-primary : hover {
37+ background-color : # ff6b50 ;
38+ transform : translateY (-2px );
39+ box-shadow : 0 6px 8px rgba (255 , 126 , 103 , 0.4 );
40+ }
41+ .chat-container { height : 100vh ; display : flex; flex-direction : column; background-color : # f8f9fa ;}
42+ .chat-header {
43+ padding : 20px ;
44+ background-color : white;
45+ border-bottom : 1px solid # e9ecef ;
46+ box-shadow : 0 2px 5px rgba (0 , 0 , 0 , 0.02 );
47+ z-index : 10 ;
48+ }
49+ .chat-messages {
50+ flex-grow : 1 ;
51+ padding : 25px ;
52+ overflow-y : auto;
53+ background-color : transparent;
54+ }
55+ .chat-input {
56+ padding : 20px 30px ;
57+ background-color : white;
58+ border-top : 1px solid # e9ecef ;
59+ box-shadow : 0 -2px 10px rgba (0 , 0 , 0 , 0.02 );
60+ }
61+ .input-group .form-control {
62+ border : 1px solid # ced4da ;
63+ padding : 15px 20px ;
64+ border-radius : 25px 0 0 25px !important ;
65+ box-shadow : inset 0 1px 2px rgba (0 , 0 , 0 , 0.05 );
66+ }
67+ .input-group .btn {
68+ border-radius : 0 25px 25px 0 !important ;
69+ padding : 0 25px ;
70+ background-color : # 667eea ;
71+ box-shadow : none;
72+ }
73+ .input-group .btn : hover { background-color : # 764ba2 ; }
74+
75+ .message { margin-bottom : 20px ; padding : 15px 20px ; border-radius : 20px ; max-width : 75% ; position : relative; font-size : 0.95rem ; line-height : 1.5 ; box-shadow : 0 2px 5px rgba (0 , 0 , 0 , 0.05 ); }
76+ .user-message {
77+ background : linear-gradient (135deg , # 667eea 0% , # 764ba2 100% );
78+ color : white;
79+ align-self : flex-end;
80+ margin-left : auto;
81+ border-bottom-right-radius : 5px ;
82+ }
83+ .bot-message {
84+ background-color : white;
85+ color : # 333 ;
86+ align-self : flex-start;
87+ border-bottom-left-radius : 5px ;
88+ border : 1px solid # e9ecef ;
89+ }
90+ .bot-message strong { color : # 667eea ; }
91+
92+ .loading { font-style : italic; color : # 888 ; background : transparent; border : none; box-shadow : none; }
93+ .loading : after {
94+ content : '...' ;
95+ animation : dots 1.5s steps (5 , end) infinite;
96+ }
97+ @keyframes dots { 0% , 20% { color : rgba (0 , 0 , 0 , 0 ); text-shadow : .25em 0 0 rgba (0 , 0 , 0 , 0 ), .5em 0 0 rgba (0 , 0 , 0 , 0 );} 40% { color : # 888 ; text-shadow : .25em 0 0 rgba (0 , 0 , 0 , 0 ), .5em 0 0 rgba (0 , 0 , 0 , 0 );} 60% { text-shadow : .25em 0 0 # 888, .5em 0 0 rgba (0 , 0 , 0 , 0 );} 80% , 100% { text-shadow : .25em 0 0 # 888, .5em 0 0 # 888 ;}}
98+
99+ .pdf-item {
100+ cursor : pointer;
101+ padding : 12px 15px ;
102+ border-radius : 10px ;
103+ margin-bottom : 10px ;
104+ transition : all 0.2s ;
105+ background-color : rgba (255 , 255 , 255 , 0.1 );
106+ border-left : 4px solid transparent;
107+ }
108+ .pdf-item : hover {
109+ background-color : rgba (255 , 255 , 255 , 0.2 );
110+ transform : translateX (5px );
111+ }
112+ .pdf-item .active {
113+ background-color : white;
114+ color : # 333 ;
115+ border-left : 4px solid # ff7e67 ;
116+ box-shadow : 0 4px 6px rgba (0 , 0 , 0 , 0.1 );
117+ }
118+ .pdf-item .active .text-muted , .pdf-item .active small { color : # 666 !important ; }
119+
120+ /* Custom scrollbar for webkit */
121+ ::-webkit-scrollbar { width : 8px ; }
122+ ::-webkit-scrollbar-track { background : transparent; }
123+ ::-webkit-scrollbar-thumb { background : rgba (0 , 0 , 0 , 0.2 ); border-radius : 4px ; }
124+ ::-webkit-scrollbar-thumb : hover { background : rgba (0 , 0 , 0 , 0.3 ); }
125+ .sidebar ::-webkit-scrollbar-thumb { background : rgba (255 , 255 , 255 , 0.3 ); }
126+ .sidebar ::-webkit-scrollbar-thumb : hover { background : rgba (255 , 255 , 255 , 0.5 ); }
21127 </ style >
22128</ head >
23129< body >
24- < div class ="container-fluid ">
25- < div class ="row ">
130+ < div class ="container-fluid p-0 ">
131+ < div class ="row g-0 ">
26132 <!-- Sidebar: PDF List and Upload -->
27- < div class ="col-md-3 sidebar ">
28- < h3 > PDF Chat</ h3 >
29- < hr >
30- < h5 > Upload PDF</ h5 >
31- < form action ="{% url 'upload_pdf' %} " method ="post " enctype ="multipart/form-data " class ="mb-4 ">
32- {% csrf_token %}
33- < div class ="mb-3 ">
34- < label for ="id_title " class ="form-label "> Title</ label >
35- {{ form.title }}
36- </ div >
37- < div class ="mb-3 ">
38- < label for ="id_file " class ="form-label "> PDF File</ label >
39- {{ form.file }}
40- </ div >
41- < button type ="submit " class ="btn btn-primary w-100 "> Upload and Process</ button >
42- </ form >
43- < hr >
44- < h5 > Documents</ h5 >
45- < div id ="pdf-list ">
133+ < div class ="col-md-3 col-lg-3 sidebar d-flex flex-column ">
134+ < div class ="mb-4 text-center ">
135+ < h3 class ="mt-2 "> < i class ="fas fa-file-pdf me-2 "> </ i > DocuChat AI</ h3 >
136+ < p class ="small text-light opacity-75 "> Powered by Gemini</ p >
137+ </ div >
138+
139+ < div class ="upload-section bg-white bg-opacity-10 p-3 rounded-3 mb-4 ">
140+ < h5 class ="mb-3 "> < i class ="fas fa-cloud-upload-alt me-2 "> </ i > Upload PDF</ h5 >
141+ < form action ="{% url 'upload_pdf' %} " method ="post " enctype ="multipart/form-data ">
142+ {% csrf_token %}
143+ < div class ="mb-2 ">
144+ < input type ="text " name ="title " class ="form-control form-control-sm " placeholder ="Document Title " required id ="id_title ">
145+ </ div >
146+ < div class ="mb-3 ">
147+ < input type ="file " name ="file " class ="form-control form-control-sm " required id ="id_file " accept =".pdf ">
148+ </ div >
149+ < button type ="submit " class ="btn btn-primary w-100 btn-sm "> < i class ="fas fa-cogs me-1 "> </ i > Process Document</ button >
150+ </ form >
151+ </ div >
152+
153+ < h5 class ="mb-3 mt-2 "> < i class ="fas fa-book me-2 "> </ i > My Library</ h5 >
154+ < div id ="pdf-list " class ="flex-grow-1 overflow-auto pe-2 ">
46155 {% for doc in documents %}
47156 < div class ="pdf-item {% if doc.status == 'FAILED' %}border-danger{% endif %} " data-id ="{{ doc.id }} " data-status ="{{ doc.status }} ">
48- < strong > {{ doc.title }}</ strong > < br >
49- < small class ="text-muted "> {{ doc.uploaded_at|date:"M d, Y H:i" }}</ small > < br >
50- < small class ="{% if doc.status == 'COMPLETED' %}text-success{% elif doc.status == 'FAILED' %}text-danger{% else %}text-warning{% endif %} ">
51- Status: {{ doc.get_status_display }}
52- </ small >
157+ < div class ="d-flex justify-content-between align-items-center ">
158+ < strong class ="text-truncate "> {{ doc.title }}</ strong >
159+ </ div >
160+ < small class ="text-light opacity-75 d-block mt-1 "> < i class ="far fa-clock me-1 "> </ i > {{ doc.uploaded_at|date:"M d, Y H:i" }}</ small >
161+ < div class ="mt-1 ">
162+ {% if doc.status == 'COMPLETED' %}
163+ < span class ="badge bg-success bg-opacity-75 text-white border-0 "> < i class ="fas fa-check-circle me-1 "> </ i > Ready</ span >
164+ {% elif doc.status == 'FAILED' %}
165+ < span class ="badge bg-danger bg-opacity-75 text-white border-0 "> < i class ="fas fa-times-circle me-1 "> </ i > Failed</ span >
166+ {% else %}
167+ < span class ="badge bg-warning text-dark border-0 "> < i class ="fas fa-spinner fa-spin me-1 "> </ i > Processing</ span >
168+ {% endif %}
169+ </ div >
53170 </ div >
54171 {% empty %}
55- < p class ="text-muted "> No documents uploaded yet.</ p >
172+ < div class ="text-center mt-5 text-light opacity-50 ">
173+ < i class ="fas fa-folder-open fa-3x mb-3 "> </ i >
174+ < p > Your library is empty.< br > Upload a PDF to get started.</ p >
175+ </ div >
56176 {% endfor %}
57177 </ div >
58178 </ div >
59179
60180 <!-- Main Chat Area -->
61- < div class ="col-md-9 chat-container ">
181+ < div class ="col-md-9 col-lg-9 chat-container ">
182+ < div class ="chat-header d-flex justify-content-between align-items-center ">
183+ < h4 class ="m-0 text-secondary " id ="current-doc-title "> Select a document to begin</ h4 >
184+ < span class ="badge bg-primary rounded-pill "> < i class ="fas fa-robot me-1 "> </ i > Gemini 1.5 Flash</ span >
185+ </ div >
186+
62187 < div id ="chat-messages " class ="chat-messages d-flex flex-column ">
63- < div class ="message bot-message "> Select a document from the left to start chatting!</ div >
188+ < div class ="text-center mt-5 text-muted ">
189+ < i class ="fas fa-comments fa-4x mb-3 opacity-25 "> </ i >
190+ < h5 > Welcome to DocuChat!</ h5 >
191+ < p > Select a processed document from the sidebar to ask questions about its content.</ p >
192+ </ div >
64193 </ div >
194+
65195 < div class ="chat-input ">
66- < div class ="input-group ">
67- < input type ="text " id ="query-input " class ="form-control " placeholder ="Ask a question about the document... " disabled >
68- < button id ="send-btn " class ="btn btn-primary " type ="button " disabled > Send</ button >
196+ < div class ="input-group input-group-lg shadow-sm rounded-pill ">
197+ < input type ="text " id ="query-input " class ="form-control border-0 " placeholder ="Ask a question about the document... " disabled >
198+ < button id ="send-btn " class ="btn btn-primary " type ="button " disabled >
199+ < i class ="fas fa-paper-plane me-1 "> </ i > Send
200+ </ button >
69201 </ div >
70202 </ div >
71203 </ div >
@@ -79,19 +211,27 @@ <h5>Documents</h5>
79211 item . addEventListener ( 'click' , function ( ) {
80212 const status = this . getAttribute ( 'data-status' ) ;
81213 if ( status !== 'COMPLETED' ) {
82- alert ( 'This document is not ready for chat. Status: ' + status ) ;
214+ alert ( 'This document is not ready for chat yet . Status: ' + status ) ;
83215 return ;
84216 }
85217
86218 document . querySelectorAll ( '.pdf-item' ) . forEach ( i => i . classList . remove ( 'active' ) ) ;
87219 this . classList . add ( 'active' ) ;
88220 selectedDocId = this . getAttribute ( 'data-id' ) ;
221+ const title = this . querySelector ( 'strong' ) . innerText ;
89222
90223 document . getElementById ( 'query-input' ) . disabled = false ;
91224 document . getElementById ( 'send-btn' ) . disabled = false ;
225+ document . getElementById ( 'query-input' ) . focus ( ) ;
226+
227+ document . getElementById ( 'current-doc-title' ) . innerHTML = `<i class="fas fa-file-alt me-2 text-primary"></i>${ title } ` ;
92228
93229 const chatMessages = document . getElementById ( 'chat-messages' ) ;
94- chatMessages . innerHTML = `<div class="message bot-message">Now chatting with: <strong>${ this . querySelector ( 'strong' ) . innerText } </strong></div>` ;
230+ chatMessages . innerHTML = `
231+ <div class="message bot-message shadow-sm">
232+ <i class="fas fa-robot me-2 text-primary"></i>
233+ Hi! I've read <strong>${ title } </strong>. What would you like to know about it?
234+ </div>` ;
95235 } ) ;
96236 } ) ;
97237
@@ -104,16 +244,16 @@ <h5>Documents</h5>
104244
105245 // Add user message
106246 const userMsgDiv = document . createElement ( 'div' ) ;
107- userMsgDiv . className = 'message user-message' ;
247+ userMsgDiv . className = 'message user-message shadow-sm ' ;
108248 userMsgDiv . innerText = query ;
109249 chatMessages . appendChild ( userMsgDiv ) ;
110250
111251 input . value = '' ;
112252
113253 // Add loading message
114254 const loadingMsgDiv = document . createElement ( 'div' ) ;
115- loadingMsgDiv . className = 'message bot-message loading' ;
116- loadingMsgDiv . innerText = 'Gemini is thinking... ' ;
255+ loadingMsgDiv . className = 'message bot-message loading shadow-sm ' ;
256+ loadingMsgDiv . innerHTML = '<i class="fas fa-robot me-2 text-primary opacity-50"></i>Analyzing document ' ;
117257 chatMessages . appendChild ( loadingMsgDiv ) ;
118258 chatMessages . scrollTop = chatMessages . scrollHeight ;
119259
@@ -131,19 +271,20 @@ <h5>Documents</h5>
131271 chatMessages . removeChild ( loadingMsgDiv ) ;
132272
133273 const botMsgDiv = document . createElement ( 'div' ) ;
134- botMsgDiv . className = 'message bot-message' ;
274+ botMsgDiv . className = 'message bot-message shadow-sm' ;
275+
135276 if ( data . answer ) {
136- botMsgDiv . innerText = data . answer ;
277+ botMsgDiv . innerHTML = `<i class="fas fa-robot me-2 text-primary"></i> ${ data . answer . replace ( / \n / g , '<br>' ) } ` ;
137278 } else {
138- botMsgDiv . innerText = 'Error: ' + ( data . error || 'Unknown error' ) ;
279+ botMsgDiv . innerHTML = `<i class="fas fa-exclamation-triangle me-2 text-danger"></i>Error: ${ data . error || 'Unknown error' } ` ;
139280 }
140281 chatMessages . appendChild ( botMsgDiv ) ;
141282
142283 } catch ( error ) {
143284 chatMessages . removeChild ( loadingMsgDiv ) ;
144285 const botMsgDiv = document . createElement ( 'div' ) ;
145- botMsgDiv . className = 'message bot-message text-danger' ;
146- botMsgDiv . innerText = 'Error: Failed to connect to server.' ;
286+ botMsgDiv . className = 'message bot-message border-danger text-danger shadow-sm ' ;
287+ botMsgDiv . innerHTML = `<i class="fas fa-exclamation-triangle me-2"></i>Connection error. Please try again.` ;
147288 chatMessages . appendChild ( botMsgDiv ) ;
148289 }
149290
0 commit comments