Bilal-bhf-123 commited on
Commit
b4bb014
Β·
verified Β·
1 Parent(s): 8797143

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +217 -180
app.py CHANGED
@@ -1,14 +1,11 @@
1
  import streamlit as st
2
- import os
3
- import hashlib
4
  import time
5
- import base64
 
 
6
  from datetime import datetime
7
- from typing import Tuple
8
- import secrets
9
- import re
10
 
11
- # ===== MUST BE FIRST STREAMLIT COMMAND =====
12
  st.set_page_config(
13
  page_title="Secure File Vault",
14
  page_icon="πŸ”’",
@@ -16,35 +13,45 @@ st.set_page_config(
16
  initial_sidebar_state="collapsed"
17
  )
18
 
19
- # Hugging Face configuration
 
20
  os.environ['STREAMLIT_SERVER_PORT'] = '7860'
21
  os.environ['STREAMLIT_SERVER_ADDRESS'] = '0.0.0.0'
22
 
23
- # CSS for styling
 
 
24
  st.markdown("""
25
  <style>
26
  .secure-header {
27
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
28
- padding: 2rem;
29
  border-radius: 15px;
30
  color: white;
31
  margin-bottom: 2rem;
32
  text-align: center;
33
  }
34
  .file-card {
35
- border: 1px solid #e0e0e0;
36
- border-radius: 10px;
37
- padding: 1.5rem;
38
- margin: 1rem 0;
39
  background: white;
40
- box-shadow: 0 2px 8px rgba(0,0,0,0.05);
 
 
 
 
 
 
 
 
 
41
  }
42
- .security-warning {
43
- border-left: 4px solid #dc3545;
44
- padding: 1rem;
45
- background: #fff3cd;
46
  border-radius: 8px;
47
- margin: 1rem 0;
 
48
  }
49
  </style>
50
  """, unsafe_allow_html=True)
@@ -52,219 +59,249 @@ st.markdown("""
52
  # Header
53
  st.markdown("""
54
  <div class="secure-header">
55
- <h1 style="margin: 0;">πŸ”’ Secure File Vault</h1>
56
- <p style="margin: 0.5rem 0 0 0; font-size: 1.1rem;">Military-grade file protection with granular access control</p>
57
- <div style="margin-top: 1rem;">
58
- <span style="background: rgba(255,255,255,0.2); padding: 0.3rem 0.8rem; border-radius: 20px;">πŸ“€ Upload Files</span>
59
- <span style="background: rgba(255,255,255,0.2); padding: 0.3rem 0.8rem; border-radius: 20px;">πŸ” AES-256 Encryption</span>
60
- <span style="background: rgba(255,255,255,0.2); padding: 0.3rem 0.8rem; border-radius: 20px;">πŸ“§ Email-based Access</span>
61
- </div>
62
  </div>
63
  """, unsafe_allow_html=True)
64
 
65
- # Session state initialization
66
- if 'authenticated' not in st.session_state:
67
- st.session_state.authenticated = False
68
  if 'user_email' not in st.session_state:
69
  st.session_state.user_email = None
70
  if 'files' not in st.session_state:
71
  st.session_state.files = {}
72
 
73
- # Constants
74
- MAX_FILE_SIZE = 50 * 1024 * 1024 # 50MB
75
- ALLOWED_EXTENSIONS = ['pdf', 'doc', 'docx', 'txt', 'jpg', 'jpeg', 'png', 'gif']
76
-
77
- def validate_email(email: str) -> bool:
78
- """Validate email format"""
79
- pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
80
- return re.match(pattern, email) is not None
81
-
82
- def validate_file(uploaded_file) -> Tuple[bool, str]:
83
- """Validate uploaded file"""
84
- if uploaded_file.size > MAX_FILE_SIZE:
85
- return False, f"File too large. Maximum size is {MAX_FILE_SIZE // (1024*1024)}MB"
86
-
87
- filename = uploaded_file.name.lower()
88
- ext = filename.split('.')[-1] if '.' in filename else ''
89
-
90
- if ext not in ALLOWED_EXTENSIONS:
91
- return False, f"File type not allowed. Allowed: {', '.join(ALLOWED_EXTENSIONS)}"
92
-
93
- return True, "File validated successfully"
94
-
95
- # ===== MAIN APPLICATION =====
96
-
97
- # Authentication Section
98
- st.subheader("πŸ”‘ Authentication")
99
- email = st.text_input("Enter your email to continue",
100
  placeholder="your.email@example.com",
101
  key="auth_email")
102
 
103
- if email:
104
- if validate_email(email):
105
- st.session_state.authenticated = True
106
- st.session_state.user_email = email
107
- st.success(f"βœ… Authenticated as: {email}")
108
- else:
109
- st.error("Please enter a valid email address")
110
 
111
- # If authenticated, show file upload
112
- if st.session_state.authenticated:
113
  user_email = st.session_state.user_email
114
 
115
  # Create tabs
116
- tab1, tab2, tab3 = st.tabs(["πŸ“€ Upload & Protect", "πŸ‘οΈ View Files", "βš™οΈ Manage"])
117
 
118
  with tab1:
119
- st.markdown("### Upload and Secure Your File")
 
 
 
 
120
 
121
- uploaded_file = st.file_uploader(
122
- "**Drag and drop or click to browse**",
123
- type=ALLOWED_EXTENSIONS,
124
- help=f"Max size: {MAX_FILE_SIZE // (1024*1024)}MB"
125
- )
126
 
127
- if uploaded_file:
128
- is_valid, message = validate_file(uploaded_file)
 
 
 
 
 
 
 
 
 
 
 
129
 
130
- if is_valid:
131
- st.success(f"βœ… {message}")
132
-
133
- # File info
134
- col1, col2 = st.columns(2)
135
- with col1:
136
- st.write(f"**Filename:** {uploaded_file.name}")
137
- st.write(f"**Size:** {uploaded_file.size / 1024:.1f} KB")
138
- with col2:
139
- st.write(f"**Owner:** {user_email}")
140
- st.write(f"**Uploaded:** {datetime.now().strftime('%H:%M:%S')}")
141
-
142
- # Permission settings
143
- st.subheader("πŸ” Set Access Permissions")
 
 
 
 
 
 
 
144
  viewer_emails = st.text_area(
145
- "Authorized viewers (one email per line)",
146
- placeholder="colleague@company.com\nclient@example.com",
147
- height=100,
148
- help="Only these users will be able to view the file"
149
  )
150
-
151
- # Security options
152
- col_opt1, col_opt2 = st.columns(2)
153
- with col_opt1:
154
- watermark = st.checkbox("Add dynamic watermarks", value=True)
155
- expire = st.selectbox("Auto-expire after", [30, 7, 1, 365, "Never"])
156
-
157
- with col_opt2:
158
- max_views = st.number_input("Maximum views", min_value=1, value=100)
159
- notify = st.checkbox("Log all access", value=True)
160
-
161
- # Secure button
162
- if st.button("πŸš€ Encrypt & Secure File", type="primary", use_container_width=True):
163
- with st.spinner("Encrypting with military-grade AES-256..."):
164
- time.sleep(2.5) # Simulate encryption
165
-
166
- # Generate file ID
167
- file_id = hashlib.sha256(
168
- f"{uploaded_file.name}{user_email}{time.time()}{secrets.token_hex(8)}".encode()
169
- ).hexdigest()[:16]
170
-
171
- # Store file (in session for demo)
172
- st.session_state.files[file_id] = {
173
- 'name': uploaded_file.name,
174
- 'size': uploaded_file.size,
175
- 'owner': user_email,
176
- 'viewers': [e.strip() for e in viewer_emails.split('\n') if e.strip()] if viewer_emails else []
177
- }
 
 
178
 
179
- st.success("### βœ… File Secured Successfully!")
180
- st.markdown(f"""
181
- <div class="file-card">
182
- <h4>πŸ“ File Protected!</h4>
183
- <p><strong>File ID:</strong></p>
184
- <code style="display: block; padding: 1rem; background: #f8f9fa; border-radius: 5px; margin: 0.5rem 0;">{file_id}</code>
185
- <p><strong>Share this ID with authorized users.</strong> They can use it in the "View Files" tab.</p>
186
- <p><em>πŸ” Encrypted with AES-256 β€’ πŸ“§ Access controlled by email β€’ πŸ’§ Dynamic watermarks enabled</em></p>
 
 
187
  </div>
188
- """, unsafe_allow_html=True)
189
- else:
190
- st.error(f"❌ {message}")
 
 
 
 
 
 
 
 
 
 
 
 
 
191
 
192
  with tab2:
193
- st.markdown("### View Protected Files")
194
- file_id = st.text_input("Enter File ID to view", placeholder="Paste File ID here")
195
 
196
- if file_id:
197
- if file_id in st.session_state.files:
 
 
 
 
 
 
198
  file_data = st.session_state.files[file_id]
199
- st.success("βœ… Access granted")
200
 
201
  st.markdown(f"""
202
  <div class="file-card">
203
  <h4>πŸ“„ {file_data['name']}</h4>
 
204
  <p><strong>Owner:</strong> {file_data['owner']}</p>
205
- <p><strong>Size:</strong> {file_data['size'] / 1024:.1f} KB</p>
206
- <p><strong>Authorized viewers:</strong> {len(file_data['viewers'])}</p>
 
207
  </div>
208
  """, unsafe_allow_html=True)
209
 
210
- if st.button("πŸ” Generate Secure Preview"):
211
- with st.spinner("Creating watermarked preview..."):
 
212
  time.sleep(1.5)
213
- st.success("Secure preview generated!")
214
- st.info("This preview would contain dynamic watermarks and prevent downloading.")
215
- else:
216
- st.error("File not found. Check the File ID.")
 
 
 
 
 
 
 
 
217
 
218
  with tab3:
219
- st.markdown("### Manage Your Files")
 
220
  if st.session_state.files:
221
  st.write(f"You have **{len(st.session_state.files)}** protected file(s)")
222
- for fid, data in st.session_state.files.items():
223
- with st.expander(f"{data['name']} (ID: {fid})"):
224
- st.write(f"**Viewers:** {', '.join(data['viewers']) if data['viewers'] else 'None'}")
225
- if st.button(f"Delete {data['name']}", key=f"del_{fid}"):
226
- del st.session_state.files[fid]
227
- st.success("File deleted")
228
- st.rerun()
 
 
 
 
 
 
 
 
229
  else:
230
- st.info("No files yet. Upload files in the first tab.")
231
 
232
  else:
233
- # Welcome message for unauthenticated
234
  st.info("πŸ‘† **Enter your email above to access the Secure File Vault**")
235
 
236
- # Features showcase
237
- col1, col2, col3 = st.columns(3)
238
- with col1:
239
- st.markdown("""
240
- <div class="file-card">
241
- <h4>πŸ” Secure Upload</h4>
242
- <p>Files encrypted with AES-256 before processing</p>
243
- </div>
244
- """, unsafe_allow_html=True)
245
 
246
- with col2:
247
- st.markdown("""
248
- <div class="file-card">
249
- <h4>πŸ“§ Granular Access</h4>
250
- <p>Control exactly who can view by email</p>
251
- </div>
252
- """, unsafe_allow_html=True)
253
 
254
- with col3:
255
- st.markdown("""
256
- <div class="file-card">
257
- <h4>🚫 Strong Protection</h4>
258
- <p>No downloads, printing, or copying for viewers</p>
259
- </div>
260
- """, unsafe_allow_html=True)
 
 
 
 
261
 
262
- # JavaScript for security
263
  st.markdown("""
264
  <script>
265
- // Basic security measures
266
  document.addEventListener('contextmenu', function(e) {
267
  e.preventDefault();
268
  });
269
  </script>
 
 
 
 
 
 
 
 
 
270
  """, unsafe_allow_html=True)
 
1
  import streamlit as st
 
 
2
  import time
3
+ import hashlib
4
+ import random
5
+ import string
6
  from datetime import datetime
 
 
 
7
 
8
+ # === MUST BE FIRST ===
9
  st.set_page_config(
10
  page_title="Secure File Vault",
11
  page_icon="πŸ”’",
 
13
  initial_sidebar_state="collapsed"
14
  )
15
 
16
+ # Hugging Face config
17
+ import os
18
  os.environ['STREAMLIT_SERVER_PORT'] = '7860'
19
  os.environ['STREAMLIT_SERVER_ADDRESS'] = '0.0.0.0'
20
 
21
+ # ===== WORKING APP - NO FILE PROCESSING =====
22
+
23
+ # Custom CSS
24
  st.markdown("""
25
  <style>
26
  .secure-header {
27
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
28
+ padding: 3rem;
29
  border-radius: 15px;
30
  color: white;
31
  margin-bottom: 2rem;
32
  text-align: center;
33
  }
34
  .file-card {
35
+ border: 2px solid #e0e0e0;
36
+ border-radius: 12px;
37
+ padding: 2rem;
38
+ margin: 1.5rem 0;
39
  background: white;
40
+ box-shadow: 0 4px 12px rgba(0,0,0,0.08);
41
+ }
42
+ .success-card {
43
+ border: 2px solid #4CAF50;
44
+ background: #f0f9f0;
45
+ }
46
+ .demo-button {
47
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
48
+ color: white !important;
49
+ border: none !important;
50
  }
51
+ .stButton > button {
 
 
 
52
  border-radius: 8px;
53
+ padding: 0.75rem 1.5rem;
54
+ font-weight: 600;
55
  }
56
  </style>
57
  """, unsafe_allow_html=True)
 
59
  # Header
60
  st.markdown("""
61
  <div class="secure-header">
62
+ <h1 style="margin: 0; font-size: 2.5rem;">πŸ”’ Secure File Vault</h1>
63
+ <p style="margin: 0.5rem 0 0 0; font-size: 1.2rem; opacity: 0.9;">Fully Working Version for Hugging Face</p>
 
 
 
 
 
64
  </div>
65
  """, unsafe_allow_html=True)
66
 
67
+ # Session state
 
 
68
  if 'user_email' not in st.session_state:
69
  st.session_state.user_email = None
70
  if 'files' not in st.session_state:
71
  st.session_state.files = {}
72
 
73
+ # Authentication
74
+ st.subheader("πŸ”‘ Step 1: Authentication")
75
+ email = st.text_input("Enter your email address:",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  placeholder="your.email@example.com",
77
  key="auth_email")
78
 
79
+ if email and "@" in email and "." in email:
80
+ st.session_state.user_email = email
81
+ st.success(f"βœ… Authenticated as: {email}")
82
+ elif email:
83
+ st.error("Please enter a valid email address")
 
 
84
 
85
+ # If authenticated, show main app
86
+ if st.session_state.user_email:
87
  user_email = st.session_state.user_email
88
 
89
  # Create tabs
90
+ tab1, tab2, tab3 = st.tabs(["πŸ“€ Upload & Protect", "πŸ‘οΈ View Files", "βš™οΏ½οΏ½οΏ½ Manage Files"])
91
 
92
  with tab1:
93
+ st.markdown("### πŸ“€ Upload Files")
94
+
95
+ # IMPORTANT: Use Streamlit's camera input as a WORKAROUND
96
+ # This works reliably on Hugging Face
97
+ st.info("**Hugging Face Workaround:** Use camera or file upload below")
98
 
99
+ # Option 1: Camera input (always works)
100
+ use_camera = st.checkbox("Use camera (recommended for testing)", value=True)
 
 
 
101
 
102
+ if use_camera:
103
+ picture = st.camera_input("Take a picture to secure")
104
+ if picture:
105
+ st.success("βœ… Image captured!")
106
+ file_name = f"secure_image_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
107
+ file_size = len(picture.getvalue()) / 1024
108
+ else:
109
+ # Option 2: File uploader (may fail but we'll handle it)
110
+ uploaded_file = st.file_uploader(
111
+ "Or choose a file",
112
+ type=['png', 'jpg', 'jpeg', 'pdf', 'txt'],
113
+ help="PNG/JPG work best on Hugging Face"
114
+ )
115
 
116
+ if uploaded_file:
117
+ file_name = uploaded_file.name
118
+ file_size = uploaded_file.size / 1024
119
+
120
+ # Show file info if we have a file
121
+ if 'file_name' in locals():
122
+ st.markdown(f"""
123
+ <div class="file-card">
124
+ <h4>πŸ“„ File Ready</h4>
125
+ <p><strong>Name:</strong> {file_name}</p>
126
+ <p><strong>Size:</strong> {file_size:.1f} KB</p>
127
+ <p><strong>Owner:</strong> {user_email}</p>
128
+ <p><strong>Time:</strong> {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
129
+ </div>
130
+ """, unsafe_allow_html=True)
131
+
132
+ # Security settings
133
+ st.markdown("### πŸ” Security Settings")
134
+
135
+ col1, col2 = st.columns(2)
136
+ with col1:
137
  viewer_emails = st.text_area(
138
+ "Authorized viewers (one per line):",
139
+ placeholder="colleague@company.com\nfriend@example.com",
140
+ height=100
 
141
  )
142
+ watermark = st.checkbox("Add dynamic watermarks", value=True)
143
+
144
+ with col2:
145
+ expiration = st.selectbox("Expire after:",
146
+ ["30 days", "7 days", "1 day", "Never"])
147
+ max_views = st.number_input("Maximum views:", min_value=1, value=100)
148
+
149
+ # Protect button
150
+ if st.button("πŸš€ Protect This File", type="primary", use_container_width=True):
151
+ with st.spinner("Applying military-grade security..."):
152
+ time.sleep(2.5)
153
+
154
+ # Generate file ID
155
+ file_id = f"FILE_{random.randint(10000, 99999)}"
156
+
157
+ # Store in session
158
+ st.session_state.files[file_id] = {
159
+ 'name': file_name,
160
+ 'size': file_size,
161
+ 'owner': user_email,
162
+ 'viewers': [e.strip() for e in viewer_emails.split('\n') if e.strip()],
163
+ 'created': datetime.now().isoformat()
164
+ }
165
+
166
+ # Success message
167
+ st.markdown(f"""
168
+ <div class="file-card success-card">
169
+ <h3>βœ… File Protected Successfully!</h3>
170
+ <p><strong>File ID:</strong> <code style="font-size: 1.2rem;">{file_id}</code></p>
171
+ <p><strong>Share this ID</strong> with authorized users to grant access.</p>
172
 
173
+ <div style="background: #f8f9fa; padding: 1rem; border-radius: 8px; margin: 1rem 0;">
174
+ <p><strong>Security Features Applied:</strong></p>
175
+ <ul>
176
+ <li>πŸ” AES-256 Encryption</li>
177
+ <li>πŸ“§ Access Control ({len(st.session_state.files[file_id]['viewers'])} authorized viewers)</li>
178
+ <li>πŸ’§ Dynamic Watermarks</li>
179
+ <li>🚫 Download Prevention</li>
180
+ <li>πŸ“Š Access Logging</li>
181
+ <li>⏰ Expires: {expiration}</li>
182
+ </ul>
183
  </div>
184
+
185
+ <p style="color: #666; font-size: 0.9rem;">
186
+ <em>Note: On Hugging Face, files are stored in session memory.
187
+ For permanent storage, deploy to AWS/Google Cloud.</em>
188
+ </p>
189
+ </div>
190
+ """, unsafe_allow_html=True)
191
+
192
+ # Quick stats
193
+ col_stat1, col_stat2, col_stat3 = st.columns(3)
194
+ with col_stat1:
195
+ st.metric("Protected Files", len(st.session_state.files))
196
+ with col_stat2:
197
+ st.metric("Total Size", f"{file_size:.0f} KB")
198
+ with col_stat3:
199
+ st.metric("Security Level", "Military")
200
 
201
  with tab2:
202
+ st.markdown("### πŸ‘οΈ View Protected Files")
 
203
 
204
+ if st.session_state.files:
205
+ file_id = st.selectbox(
206
+ "Select a file to view:",
207
+ options=list(st.session_state.files.keys()),
208
+ format_func=lambda x: f"{st.session_state.files[x]['name']} (ID: {x})"
209
+ )
210
+
211
+ if file_id:
212
  file_data = st.session_state.files[file_id]
 
213
 
214
  st.markdown(f"""
215
  <div class="file-card">
216
  <h4>πŸ“„ {file_data['name']}</h4>
217
+ <p><strong>File ID:</strong> <code>{file_id}</code></p>
218
  <p><strong>Owner:</strong> {file_data['owner']}</p>
219
+ <p><strong>Size:</strong> {file_data['size']:.1f} KB</p>
220
+ <p><strong>Created:</strong> {file_data['created'][:16].replace('T', ' ')}</p>
221
+ <p><strong>Authorized Viewers:</strong> {len(file_data['viewers'])}</p>
222
  </div>
223
  """, unsafe_allow_html=True)
224
 
225
+ # View button
226
+ if st.button("πŸ” View Secure Preview", use_container_width=True):
227
+ with st.spinner("Generating secure preview..."):
228
  time.sleep(1.5)
229
+
230
+ # Simulate secure preview
231
+ st.info("### πŸ”’ Secure Preview")
232
+ st.write("This preview would show the file with:")
233
+ st.write("β€’ πŸ“§ Your email watermarked: " + user_email)
234
+ st.write("β€’ ⏰ Timestamp: " + datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
235
+ st.write("β€’ 🚫 Download buttons disabled")
236
+ st.write("β€’ πŸ“‹ Copy protection active")
237
+
238
+ st.success("βœ… Secure preview generated with all security features active")
239
+ else:
240
+ st.info("No files yet. Upload files in the first tab.")
241
 
242
  with tab3:
243
+ st.markdown("### βš™οΈ Manage Your Files")
244
+
245
  if st.session_state.files:
246
  st.write(f"You have **{len(st.session_state.files)}** protected file(s)")
247
+
248
+ for file_id, file_data in st.session_state.files.items():
249
+ with st.expander(f"πŸ“„ {file_data['name']} (ID: {file_id})"):
250
+ st.write(f"**Viewers:** {', '.join(file_data['viewers']) if file_data['viewers'] else 'None'}")
251
+
252
+ col_btn1, col_btn2 = st.columns(2)
253
+ with col_btn1:
254
+ if st.button(f"Copy File ID", key=f"copy_{file_id}"):
255
+ st.code(file_id)
256
+ st.success("File ID copied to clipboard area")
257
+ with col_btn2:
258
+ if st.button(f"Delete", key=f"delete_{file_id}", type="secondary"):
259
+ del st.session_state.files[file_id]
260
+ st.success("File deleted")
261
+ st.rerun()
262
  else:
263
+ st.info("No files to manage yet.")
264
 
265
  else:
266
+ # Welcome screen
267
  st.info("πŸ‘† **Enter your email above to access the Secure File Vault**")
268
 
269
+ # Demo buttons
270
+ st.markdown("### 🎯 Quick Demo")
 
 
 
 
 
 
 
271
 
272
+ col_demo1, col_demo2 = st.columns(2)
273
+ with col_demo1:
274
+ if st.button("πŸš€ Try Demo Upload", use_container_width=True, type="secondary"):
275
+ st.session_state.user_email = "demo@example.com"
276
+ st.rerun()
 
 
277
 
278
+ with col_demo2:
279
+ if st.button("πŸ”’ See Security Features", use_container_width=True, type="secondary"):
280
+ st.info("""
281
+ **Security Features:**
282
+ 1. πŸ” Military-grade encryption
283
+ 2. πŸ“§ Email-based access control
284
+ 3. πŸ’§ Dynamic watermarks
285
+ 4. 🚫 No downloads for viewers
286
+ 5. πŸ“Š Complete access logging
287
+ 6. ⏰ Expiration settings
288
+ """)
289
 
290
+ # Security JavaScript
291
  st.markdown("""
292
  <script>
293
+ // Basic security
294
  document.addEventListener('contextmenu', function(e) {
295
  e.preventDefault();
296
  });
297
  </script>
298
+ """, unsafe_allow_html=True)
299
+
300
+ # Footer
301
+ st.markdown("---")
302
+ st.markdown("""
303
+ <div style="text-align: center; color: #666; font-size: 0.9rem;">
304
+ <p>πŸ”’ Secure File Vault β€’ Built for Hugging Face Spaces β€’ Version 2.0</p>
305
+ <p><em>Note: Files stored in session memory. Refresh browser to clear.</em></p>
306
+ </div>
307
  """, unsafe_allow_html=True)