Spaces:
Running
Running
Upload 14 files
Browse files- admin.html +613 -0
- contact.html +271 -0
- features.html +247 -0
- feedback.html +303 -0
- gender_predictor.html +448 -0
- home.html +247 -0
- images/example-1.jpg +0 -0
- images//340/244/205/340/244/202/340/244/244/340/244/260/340/245/215/340/244/260/340/244/276/340/244/267/340/245/215/340/244/237/340/245/215/340/244/260/340/245/200/340/244/257.png +0 -0
- images//340/244/205/340/244/227/340/245/215/340/244/260/340/244/270/340/244/260.png +0 -0
- images//340/244/205/340/244/247/340/245/215/340/244/257/340/244/257/340/244/250.png +0 -0
- images//340/244/205/340/244/250/340/245/201/340/244/270/340/244/202/340/244/247/340/244/276/340/244/250.png +0 -0
- index.html +1114 -19
- style.css +501 -28
- translation.html +443 -0
admin.html
ADDED
|
@@ -0,0 +1,613 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Hindi OCR App - Admin Panel</title>
|
| 7 |
+
<style>
|
| 8 |
+
/* --- REUSABLE STYLES (from translation.html) --- */
|
| 9 |
+
:root {
|
| 10 |
+
--primary-color: #4a90e2; /* Consistent primary color */
|
| 11 |
+
--secondary-color: #f8f9fa;
|
| 12 |
+
--text-color: #2c3e50;
|
| 13 |
+
--border-radius: 12px;
|
| 14 |
+
--transition: all 0.3s ease;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
* {
|
| 18 |
+
margin: 0;
|
| 19 |
+
padding: 0;
|
| 20 |
+
box-sizing: border-box;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
body {
|
| 24 |
+
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; /* Consistent font */
|
| 25 |
+
line-height: 1.6;
|
| 26 |
+
color: var(--text-color);
|
| 27 |
+
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); /* Consistent background */
|
| 28 |
+
min-height: 100vh;
|
| 29 |
+
padding: 2rem;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
.container {
|
| 33 |
+
max-width: 1000px;
|
| 34 |
+
margin: 0 auto;
|
| 35 |
+
background: white;
|
| 36 |
+
padding: 2rem;
|
| 37 |
+
border-radius: var(--border-radius);
|
| 38 |
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
h1 {
|
| 42 |
+
color: var(--primary-color);
|
| 43 |
+
text-align: center;
|
| 44 |
+
margin-bottom: 2rem;
|
| 45 |
+
font-size: 2.5rem;
|
| 46 |
+
font-weight: 700;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
h2 {
|
| 50 |
+
color: var(--text-color);
|
| 51 |
+
margin-bottom: 1.5rem;
|
| 52 |
+
font-size: 1.8rem;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
label {
|
| 56 |
+
display: block;
|
| 57 |
+
margin-bottom: 0.5rem;
|
| 58 |
+
font-weight: 600;
|
| 59 |
+
color: var(--text-color);
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
input[type="text"], input[type="password"], input[type="email"] {
|
| 63 |
+
width: 100%;
|
| 64 |
+
padding: 1rem;
|
| 65 |
+
margin-bottom: 1.5rem;
|
| 66 |
+
border: 2px solid #e1e8ed;
|
| 67 |
+
border-radius: var(--border-radius);
|
| 68 |
+
font-size: 1rem;
|
| 69 |
+
transition: var(--transition);
|
| 70 |
+
background: white;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
input[type="text"]:focus, input[type="password"]:focus, input[type="email"]:focus {
|
| 75 |
+
outline: none;
|
| 76 |
+
border-color: var(--primary-color);
|
| 77 |
+
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
button {
|
| 81 |
+
background: var(--primary-color);
|
| 82 |
+
color: white;
|
| 83 |
+
padding: 1rem 2rem;
|
| 84 |
+
border: none;
|
| 85 |
+
border-radius: var(--border-radius);
|
| 86 |
+
font-size: 1.1rem;
|
| 87 |
+
cursor: pointer;
|
| 88 |
+
transition: var(--transition);
|
| 89 |
+
width: 100%;
|
| 90 |
+
margin-top: 1rem;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
button:hover {
|
| 94 |
+
transform: translateY(-2px);
|
| 95 |
+
box-shadow: 0 5px 15px rgba(74, 144, 226, 0.3);
|
| 96 |
+
}
|
| 97 |
+
/* --- ERROR MESSAGE --- */
|
| 98 |
+
.error-message {
|
| 99 |
+
background: #fee2e2;
|
| 100 |
+
color: #dc2626;
|
| 101 |
+
padding: 1rem;
|
| 102 |
+
border-radius: var(--border-radius);
|
| 103 |
+
margin-top: 1rem;
|
| 104 |
+
display: none; /* Hidden by default */
|
| 105 |
+
}
|
| 106 |
+
/* --- BUTTONS --- */
|
| 107 |
+
.button-secondary {
|
| 108 |
+
background-color: var(--secondary-color);
|
| 109 |
+
color: var(--primary-color);
|
| 110 |
+
border: 2px solid var(--primary-color);
|
| 111 |
+
width: auto;
|
| 112 |
+
}
|
| 113 |
+
.button-secondary:hover {
|
| 114 |
+
background-color: rgba(74, 144, 226, 0.1);
|
| 115 |
+
box-shadow: 0 5px 15px rgba(74, 144, 226, 0.3);
|
| 116 |
+
}
|
| 117 |
+
/* --- NAVIGATION BAR --- */
|
| 118 |
+
nav {
|
| 119 |
+
display: flex;
|
| 120 |
+
flex-wrap: wrap; /* Allow wrapping on smaller screens */
|
| 121 |
+
gap: 1rem;
|
| 122 |
+
justify-content: center; /* Center items */
|
| 123 |
+
margin-bottom: 2rem;
|
| 124 |
+
border-bottom: 2px solid var(--secondary-color);
|
| 125 |
+
padding-bottom: 1rem;
|
| 126 |
+
}
|
| 127 |
+
nav a {
|
| 128 |
+
color: var(--primary-color);
|
| 129 |
+
text-decoration: none;
|
| 130 |
+
padding: 0.5rem 1rem;
|
| 131 |
+
border-radius: var(--border-radius);
|
| 132 |
+
transition: var(--transition);
|
| 133 |
+
}
|
| 134 |
+
nav a:hover {
|
| 135 |
+
background-color: rgba(74, 144, 226, 0.1);
|
| 136 |
+
}
|
| 137 |
+
nav a.active {
|
| 138 |
+
background-color: var(--primary-color);
|
| 139 |
+
color: white;
|
| 140 |
+
}
|
| 141 |
+
.subtitle {
|
| 142 |
+
text-align: center;
|
| 143 |
+
color: #64748b;
|
| 144 |
+
margin-bottom: 1rem;
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
.attribution {
|
| 148 |
+
text-align: center;
|
| 149 |
+
color: #64748b;
|
| 150 |
+
font-size: 0.875rem;
|
| 151 |
+
margin-bottom: 2rem;
|
| 152 |
+
}
|
| 153 |
+
/* --- LOGOUT BUTTON --- */
|
| 154 |
+
.logout-button {
|
| 155 |
+
position: absolute;
|
| 156 |
+
top: 1rem;
|
| 157 |
+
right: 1rem;
|
| 158 |
+
width: auto; /* Let the button size itself based on content */
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
.logout-button svg {
|
| 162 |
+
margin-right: 0.5rem;
|
| 163 |
+
}
|
| 164 |
+
/* --- ADMIN TABLE STYLES --- */
|
| 165 |
+
.admin-table {
|
| 166 |
+
width: 100%;
|
| 167 |
+
border-collapse: collapse;
|
| 168 |
+
margin-top: 1rem; /* Reduced margin */
|
| 169 |
+
}
|
| 170 |
+
.admin-table th, .admin-table td {
|
| 171 |
+
border: 1px solid #e1e8ed; /* Lighter border */
|
| 172 |
+
padding: 0.75rem; /* Reduced padding */
|
| 173 |
+
text-align: left;
|
| 174 |
+
font-size: 0.9rem; /* Smaller font size */
|
| 175 |
+
}
|
| 176 |
+
.admin-table th {
|
| 177 |
+
background-color: var(--secondary-color);
|
| 178 |
+
font-weight: 600; /* Use font-weight from variables */
|
| 179 |
+
color: var(--text-color);
|
| 180 |
+
}
|
| 181 |
+
/* --- ADMIN BUTTONS (Pagination) --- */
|
| 182 |
+
.admin-buttons {
|
| 183 |
+
display: flex;
|
| 184 |
+
gap: 0.5rem; /* Reduced gap */
|
| 185 |
+
justify-content: center;
|
| 186 |
+
margin-top: 1rem; /* Reduced margin */
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
.admin-buttons button {
|
| 190 |
+
width: auto; /* Let buttons size to content */
|
| 191 |
+
padding: 0.5rem 1rem; /* Smaller padding */
|
| 192 |
+
font-size: 0.9rem;
|
| 193 |
+
margin-top: 0; /* Remove top margin from generic button style */
|
| 194 |
+
}
|
| 195 |
+
/* --- RESPONSIVE --- */
|
| 196 |
+
@media (max-width: 768px) {
|
| 197 |
+
body {
|
| 198 |
+
padding: 1rem;
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
.container {
|
| 202 |
+
padding: 1rem;
|
| 203 |
+
}
|
| 204 |
+
nav {
|
| 205 |
+
justify-content: space-between; /* Adjust as needed */
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
nav a {
|
| 209 |
+
padding: 0.5rem; /* Smaller padding on mobile */
|
| 210 |
+
}
|
| 211 |
+
.admin-table th,
|
| 212 |
+
.admin-table td {
|
| 213 |
+
padding: 0.5rem; /*Even smaller padding on mobile*/
|
| 214 |
+
font-size: 0.8rem;
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
.admin-buttons button {
|
| 218 |
+
padding: 0.4rem 0.8rem; /*Smaller padding on mobile*/
|
| 219 |
+
font-size: 0.8rem;
|
| 220 |
+
}
|
| 221 |
+
}
|
| 222 |
+
.credits {
|
| 223 |
+
text-align: center;
|
| 224 |
+
margin-top: 2rem;
|
| 225 |
+
color: var(--text-color);
|
| 226 |
+
font-size: 0.875rem;
|
| 227 |
+
}
|
| 228 |
+
.feedback-form {
|
| 229 |
+
padding: 2rem;
|
| 230 |
+
background: var(--secondary-color);
|
| 231 |
+
border-radius: var(--border-radius);
|
| 232 |
+
margin-bottom: 2rem;
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
</style>
|
| 236 |
+
</head>
|
| 237 |
+
<body>
|
| 238 |
+
<div class="container" id="admin-app-container" style="display: none;">
|
| 239 |
+
<header>
|
| 240 |
+
<nav>
|
| 241 |
+
<a href="home.html">Home</a>
|
| 242 |
+
<a href="translation.html" target="_blank">translation</a> <!-- Open in new tab -->
|
| 243 |
+
<a href="gender_predictor.html" target="_blank">Gender predictor</a> <!-- Open in new tab -->
|
| 244 |
+
<a href="index.html">OCR App</a>
|
| 245 |
+
<a href="features.html" target="_blank">Key Features</a>
|
| 246 |
+
<a href="feedback.html" target="_blank">Feedback</a>
|
| 247 |
+
<a href="contact.html" target="_blank">Contact Us</a>
|
| 248 |
+
<a href="admin.html" class="active">Admin Panel</a>
|
| 249 |
+
</nav>
|
| 250 |
+
<button id="adminLogoutButton" class="logout-button button-secondary">
|
| 251 |
+
Logout
|
| 252 |
+
</button>
|
| 253 |
+
<h1>Hindi OCR Admin Panel</h1>
|
| 254 |
+
<p class="subtitle">Manage users and feedback for the Hindi OCR Application</p>
|
| 255 |
+
<p class="attribution">Powered by Sakshi's Hindi OCR Engine</p>
|
| 256 |
+
|
| 257 |
+
</header>
|
| 258 |
+
|
| 259 |
+
<div class="main-content">
|
| 260 |
+
<div class="container">
|
| 261 |
+
<h2> User Management</h2>
|
| 262 |
+
<div class="error-message" id="userErrorMessage"></div>
|
| 263 |
+
<div class = "feedback-form">
|
| 264 |
+
<table class="admin-table" id="userTable">
|
| 265 |
+
<thead>
|
| 266 |
+
<tr>
|
| 267 |
+
<th>ID</th>
|
| 268 |
+
<th>Username</th>
|
| 269 |
+
<th>Email</th>
|
| 270 |
+
<th>Is Active</th>
|
| 271 |
+
<th>Is Admin</th>
|
| 272 |
+
<th>Actions</th>
|
| 273 |
+
</tr>
|
| 274 |
+
</thead>
|
| 275 |
+
<tbody id="userTableBody">
|
| 276 |
+
<tr><td colspan="6">Loading users...</td></tr>
|
| 277 |
+
</tbody>
|
| 278 |
+
</table>
|
| 279 |
+
<div class="admin-buttons" id="userPagination">
|
| 280 |
+
<button id="prevUsers" disabled>< Previous</button>
|
| 281 |
+
<button id="nextUsers">Next ></button>
|
| 282 |
+
</div>
|
| 283 |
+
</div>
|
| 284 |
+
</div>
|
| 285 |
+
|
| 286 |
+
<div class="container">
|
| 287 |
+
<h2>Feedback Management</h2>
|
| 288 |
+
<div class="error-message" id="feedbackErrorMessage"></div>
|
| 289 |
+
<div class = "feedback-form">
|
| 290 |
+
<table class="admin-table" id="feedbackTable">
|
| 291 |
+
<thead>
|
| 292 |
+
<tr>
|
| 293 |
+
<th>ID</th>
|
| 294 |
+
<th>Username</th>
|
| 295 |
+
<th>Comment</th>
|
| 296 |
+
<th>Created At</th>
|
| 297 |
+
</tr>
|
| 298 |
+
</thead>
|
| 299 |
+
<tbody id="feedbackTableBody">
|
| 300 |
+
<tr><td colspan="4">Loading feedback...</td></tr>
|
| 301 |
+
</tbody>
|
| 302 |
+
</table>
|
| 303 |
+
<div class="admin-buttons" id="feedbackPagination">
|
| 304 |
+
<button id="prevFeedback" disabled>< Previous</button>
|
| 305 |
+
<button id="nextFeedback">Next ></button>
|
| 306 |
+
</div>
|
| 307 |
+
</div>
|
| 308 |
+
</div>
|
| 309 |
+
</div>
|
| 310 |
+
|
| 311 |
+
<div class="credits">
|
| 312 |
+
<p>Powered by <strong>D SAKSHI</strong> (MCA Final Year BIT Durg, Chhattisgarh) | © SlimShadow Org. All Rights Reserved.</p>
|
| 313 |
+
</div>
|
| 314 |
+
</div>
|
| 315 |
+
|
| 316 |
+
<div id="login-container" style="display:flex; justify-content: center; align-items: center;">
|
| 317 |
+
<div class="container" id="admin-login-card">
|
| 318 |
+
<h2>Admin Login</h2>
|
| 319 |
+
<p>Login to access the Admin Panel.</p>
|
| 320 |
+
<div class="error-message" id="adminLoginErrorMessage"></div>
|
| 321 |
+
<label for="adminUsername">Username</label>
|
| 322 |
+
<input type="text" id="adminUsername" placeholder="Username" value="admin">
|
| 323 |
+
<label for="adminPassword">Password</label>
|
| 324 |
+
<input type="password" id="adminPassword" placeholder="Password" value="adminpassword">
|
| 325 |
+
<button id="adminLoginButton">Login</button>
|
| 326 |
+
</div>
|
| 327 |
+
</div>
|
| 328 |
+
|
| 329 |
+
<script>
|
| 330 |
+
const API_BASE_URL = 'https://sameernotes-ocr.hf.space';
|
| 331 |
+
let adminAccessToken = null;
|
| 332 |
+
const adminAppContainer = document.getElementById('admin-app-container');
|
| 333 |
+
const loginContainer = document.getElementById('login-container');
|
| 334 |
+
const adminLoginCard = document.getElementById('admin-login-card');
|
| 335 |
+
|
| 336 |
+
// Admin Login Elements
|
| 337 |
+
const adminUsernameInput = document.getElementById('adminUsername');
|
| 338 |
+
const adminPasswordInput = document.getElementById('adminPassword');
|
| 339 |
+
const adminLoginButton = document.getElementById('adminLoginButton');
|
| 340 |
+
const adminLoginErrorMessage = document.getElementById('adminLoginErrorMessage');
|
| 341 |
+
const adminLogoutButton = document.getElementById('adminLogoutButton');
|
| 342 |
+
|
| 343 |
+
// User Table Elements
|
| 344 |
+
const userTableBody = document.getElementById('userTableBody');
|
| 345 |
+
const userErrorMessage = document.getElementById('userErrorMessage');
|
| 346 |
+
const prevUsersButton = document.getElementById('prevUsers');
|
| 347 |
+
const nextUsersButton = document.getElementById('nextUsers');
|
| 348 |
+
let currentUserPage = 0;
|
| 349 |
+
const usersPerPage = 5; // Adjust as needed
|
| 350 |
+
|
| 351 |
+
// Feedback Table Elements
|
| 352 |
+
const feedbackTableBody = document.getElementById('feedbackTableBody');
|
| 353 |
+
const feedbackErrorMessage = document.getElementById('feedbackErrorMessage');
|
| 354 |
+
const prevFeedbackButton = document.getElementById('prevFeedback');
|
| 355 |
+
const nextFeedbackButton = document.getElementById('nextFeedback');
|
| 356 |
+
let currentFeedbackPage = 0;
|
| 357 |
+
const feedbackPerPage = 5; // Adjust as needed
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
adminLoginButton.addEventListener('click', async () => {
|
| 361 |
+
const username = adminUsernameInput.value;
|
| 362 |
+
const password = adminPasswordInput.value;
|
| 363 |
+
|
| 364 |
+
if (!username || !password) {
|
| 365 |
+
showAdminLoginError("Please fill in all fields.");
|
| 366 |
+
return;
|
| 367 |
+
}
|
| 368 |
+
|
| 369 |
+
const formData = new URLSearchParams();
|
| 370 |
+
formData.append('username', username);
|
| 371 |
+
formData.append('password', password);
|
| 372 |
+
|
| 373 |
+
try {
|
| 374 |
+
const response = await fetch(`${API_BASE_URL}/token`, {
|
| 375 |
+
method: 'POST',
|
| 376 |
+
headers: {
|
| 377 |
+
'Content-Type': 'application/x-www-form-urlencoded'
|
| 378 |
+
},
|
| 379 |
+
body: formData.toString()
|
| 380 |
+
});
|
| 381 |
+
|
| 382 |
+
if (!response.ok) {
|
| 383 |
+
const errorData = await response.json();
|
| 384 |
+
showAdminLoginError(errorData.detail || "Login failed");
|
| 385 |
+
return;
|
| 386 |
+
}
|
| 387 |
+
|
| 388 |
+
const data = await response.json();
|
| 389 |
+
adminAccessToken = data.access_token;
|
| 390 |
+
loginContainer.style.display = 'none';
|
| 391 |
+
adminAppContainer.style.display = 'block';
|
| 392 |
+
adminLoginErrorMessage.style.display = 'none';
|
| 393 |
+
loadUsers(); // Load users table after login
|
| 394 |
+
loadFeedback(); // Load feedback table after login
|
| 395 |
+
|
| 396 |
+
|
| 397 |
+
} catch (error) {
|
| 398 |
+
showAdminLoginError( "Error during login. Please try again.");
|
| 399 |
+
console.error("Admin Login error:", error);
|
| 400 |
+
}
|
| 401 |
+
});
|
| 402 |
+
|
| 403 |
+
adminLogoutButton.addEventListener('click', () => {
|
| 404 |
+
adminAccessToken = null;
|
| 405 |
+
adminAppContainer.style.display = 'none';
|
| 406 |
+
loginContainer.style.display = 'flex';
|
| 407 |
+
// Redirect to index.html
|
| 408 |
+
window.location.href = "index.html";
|
| 409 |
+
});
|
| 410 |
+
|
| 411 |
+
|
| 412 |
+
async function loadUsers() {
|
| 413 |
+
userTableBody.innerHTML = '<tr><td colspan="6">Loading users...</td></tr>';
|
| 414 |
+
userErrorMessage.style.display = 'none';
|
| 415 |
+
|
| 416 |
+
try {
|
| 417 |
+
const response = await fetch(`${API_BASE_URL}/admin/users/?skip=${currentUserPage * usersPerPage}&limit=${usersPerPage}`, {
|
| 418 |
+
headers: {
|
| 419 |
+
'Authorization': `Bearer ${adminAccessToken}`
|
| 420 |
+
}
|
| 421 |
+
});
|
| 422 |
+
|
| 423 |
+
if (!response.ok) {
|
| 424 |
+
if (response.status === 403) {
|
| 425 |
+
showUserError( "Unauthorized: Admin access required.");
|
| 426 |
+
} else {
|
| 427 |
+
showUserError( `Failed to load users. Status: ${response.status}`);
|
| 428 |
+
}
|
| 429 |
+
userTableBody.innerHTML = '<tr><td colspan="6">Error loading users.</td></tr>';
|
| 430 |
+
return;
|
| 431 |
+
}
|
| 432 |
+
|
| 433 |
+
const users = await response.json();
|
| 434 |
+
if (users.length === 0 && currentUserPage > 0) {
|
| 435 |
+
currentUserPage--; // Adjust page if no users on current page and not on first page
|
| 436 |
+
await loadUsers(); // Reload users with adjusted page
|
| 437 |
+
return;
|
| 438 |
+
}
|
| 439 |
+
populateUserTable(users);
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
} catch (error) {
|
| 443 |
+
showUserError("Error loading users. Please check console.");
|
| 444 |
+
userTableBody.innerHTML = '<tr><td colspan="6">Error loading users.</td></tr>';
|
| 445 |
+
console.error("Error fetching users:", error);
|
| 446 |
+
}
|
| 447 |
+
}
|
| 448 |
+
|
| 449 |
+
function populateUserTable(users) {
|
| 450 |
+
userTableBody.innerHTML = '';
|
| 451 |
+
if (users.length === 0) {
|
| 452 |
+
userTableBody.innerHTML = '<tr><td colspan="6">No users found.</td></tr>';
|
| 453 |
+
return;
|
| 454 |
+
}
|
| 455 |
+
|
| 456 |
+
users.forEach(user => {
|
| 457 |
+
const row = userTableBody.insertRow();
|
| 458 |
+
row.insertCell(0).textContent = user.id;
|
| 459 |
+
row.insertCell(1).textContent = user.username;
|
| 460 |
+
row.insertCell(2).textContent = user.email;
|
| 461 |
+
row.insertCell(3).textContent = user.is_active ? 'Yes' : 'No';
|
| 462 |
+
row.insertCell(4).textContent = user.is_admin ? 'Yes' : 'No';
|
| 463 |
+
const actionsCell = row.insertCell(5);
|
| 464 |
+
actionsCell.innerHTML = `<button class="button-secondary" onclick="deleteUser(${user.id})">Delete</button>`;
|
| 465 |
+
});
|
| 466 |
+
|
| 467 |
+
// Update pagination button states
|
| 468 |
+
prevUsersButton.disabled = currentUserPage === 0;
|
| 469 |
+
nextUsersButton.disabled = users.length < usersPerPage; // Disable if fewer users than per page, assuming last page
|
| 470 |
+
}
|
| 471 |
+
|
| 472 |
+
prevUsersButton.addEventListener('click', async () => {
|
| 473 |
+
if (currentUserPage > 0) {
|
| 474 |
+
currentUserPage--;
|
| 475 |
+
await loadUsers();
|
| 476 |
+
}
|
| 477 |
+
});
|
| 478 |
+
|
| 479 |
+
nextUsersButton.addEventListener('click', async () => {
|
| 480 |
+
currentUserPage++;
|
| 481 |
+
await loadUsers();
|
| 482 |
+
});
|
| 483 |
+
|
| 484 |
+
|
| 485 |
+
async function deleteUser(userId) {
|
| 486 |
+
if (confirm(`Are you sure you want to delete user ID ${userId}?`)) {
|
| 487 |
+
try {
|
| 488 |
+
const response = await fetch(`${API_BASE_URL}/admin/users/${userId}`, {
|
| 489 |
+
method: 'DELETE',
|
| 490 |
+
headers: {
|
| 491 |
+
'Authorization': `Bearer ${adminAccessToken}`
|
| 492 |
+
}
|
| 493 |
+
});
|
| 494 |
+
|
| 495 |
+
if (!response.ok) {
|
| 496 |
+
const errorData = await response.json();
|
| 497 |
+
showUserError( errorData.detail || `Failed to delete user. Status: ${response.status}`);
|
| 498 |
+
return;
|
| 499 |
+
}
|
| 500 |
+
|
| 501 |
+
userErrorMessage.style.display = 'none';
|
| 502 |
+
alert(`User ID ${userId} deleted successfully.`);
|
| 503 |
+
loadUsers(); // Reload user list
|
| 504 |
+
} catch (error) {
|
| 505 |
+
showUserError("Error deleting user. Please check console.");
|
| 506 |
+
console.error("Error deleting user:", error);
|
| 507 |
+
}
|
| 508 |
+
}
|
| 509 |
+
}
|
| 510 |
+
|
| 511 |
+
|
| 512 |
+
async function loadFeedback() {
|
| 513 |
+
feedbackTableBody.innerHTML = '<tr><td colspan="4">Loading feedback...</td></tr>';
|
| 514 |
+
feedbackErrorMessage.style.display = 'none';
|
| 515 |
+
|
| 516 |
+
try {
|
| 517 |
+
const response = await fetch(`${API_BASE_URL}/admin/feedback/?skip=${currentFeedbackPage * feedbackPerPage}&limit=${feedbackPerPage}`, {
|
| 518 |
+
headers: {
|
| 519 |
+
'Authorization': `Bearer ${adminAccessToken}`
|
| 520 |
+
}
|
| 521 |
+
});
|
| 522 |
+
|
| 523 |
+
if (!response.ok) {
|
| 524 |
+
if (response.status === 403) {
|
| 525 |
+
showFeedbackError( "Unauthorized access.");
|
| 526 |
+
} else {
|
| 527 |
+
showFeedbackError( `Failed to load feedback. Status: ${response.status}`);
|
| 528 |
+
}
|
| 529 |
+
feedbackTableBody.innerHTML = '<tr><td colspan="4">Error loading feedback.</td></tr>';
|
| 530 |
+
return;
|
| 531 |
+
}
|
| 532 |
+
|
| 533 |
+
const feedbackData = await response.json();
|
| 534 |
+
if (feedbackData.length === 0 && currentFeedbackPage > 0) {
|
| 535 |
+
currentFeedbackPage--;
|
| 536 |
+
await loadFeedback();
|
| 537 |
+
return;
|
| 538 |
+
}
|
| 539 |
+
populateFeedbackTable(feedbackData);
|
| 540 |
+
|
| 541 |
+
|
| 542 |
+
} catch (error) {
|
| 543 |
+
showFeedbackError("Error loading feedback. Please check console.");
|
| 544 |
+
feedbackTableBody.innerHTML = '<tr><td colspan="4">Error loading feedback.</td></tr>';
|
| 545 |
+
console.error("Error fetching feedback:", error);
|
| 546 |
+
}
|
| 547 |
+
}
|
| 548 |
+
|
| 549 |
+
function populateFeedbackTable(feedbackData) {
|
| 550 |
+
feedbackTableBody.innerHTML = '';
|
| 551 |
+
if (feedbackData.length === 0) {
|
| 552 |
+
feedbackTableBody.innerHTML = '<tr><td colspan="4">No feedback found.</td></tr>';
|
| 553 |
+
return;
|
| 554 |
+
}
|
| 555 |
+
|
| 556 |
+
feedbackData.forEach(feedback => {
|
| 557 |
+
const row = feedbackTableBody.insertRow();
|
| 558 |
+
row.insertCell(0).textContent = feedback.id;
|
| 559 |
+
row.insertCell(1).textContent = feedback.username;
|
| 560 |
+
row.insertCell(2).textContent = feedback.comment;
|
| 561 |
+
row.insertCell(3).textContent = new Date(feedback.created_at).toLocaleString();
|
| 562 |
+
});
|
| 563 |
+
|
| 564 |
+
prevFeedbackButton.disabled = currentFeedbackPage === 0;
|
| 565 |
+
nextFeedbackButton.disabled = feedbackData.length < feedbackPerPage;
|
| 566 |
+
}
|
| 567 |
+
|
| 568 |
+
|
| 569 |
+
prevFeedbackButton.addEventListener('click', async () => {
|
| 570 |
+
if (currentFeedbackPage > 0) {
|
| 571 |
+
currentFeedbackPage--;
|
| 572 |
+
await loadFeedback();
|
| 573 |
+
}
|
| 574 |
+
});
|
| 575 |
+
|
| 576 |
+
nextFeedbackButton.addEventListener('click', async () => {
|
| 577 |
+
currentFeedbackPage++;
|
| 578 |
+
await loadFeedback();
|
| 579 |
+
});
|
| 580 |
+
|
| 581 |
+
function showAdminLoginError(message) {
|
| 582 |
+
adminLoginErrorMessage.textContent = message;
|
| 583 |
+
adminLoginErrorMessage.style.display = 'block';
|
| 584 |
+
setTimeout(() => {
|
| 585 |
+
adminLoginErrorMessage.style.display = 'none';
|
| 586 |
+
}, 5000); // Hide after 5 seconds
|
| 587 |
+
|
| 588 |
+
}
|
| 589 |
+
function showUserError(message) {
|
| 590 |
+
userErrorMessage.textContent = message;
|
| 591 |
+
userErrorMessage.style.display = 'block';
|
| 592 |
+
setTimeout(() => {
|
| 593 |
+
userErrorMessage.style.display = 'none';
|
| 594 |
+
}, 5000);
|
| 595 |
+
}
|
| 596 |
+
|
| 597 |
+
function showFeedbackError(message) {
|
| 598 |
+
feedbackErrorMessage.textContent = message;
|
| 599 |
+
feedbackErrorMessage.style.display = 'block';
|
| 600 |
+
setTimeout(() => {
|
| 601 |
+
feedbackErrorMessage.style.display = 'none';
|
| 602 |
+
}, 5000);
|
| 603 |
+
}
|
| 604 |
+
|
| 605 |
+
// --- Initial Check for Admin Token ---
|
| 606 |
+
// For simplicity, always start at admin login for this example
|
| 607 |
+
loginContainer.style.display = 'flex';
|
| 608 |
+
adminAppContainer.style.display = 'none';
|
| 609 |
+
|
| 610 |
+
|
| 611 |
+
</script>
|
| 612 |
+
</body>
|
| 613 |
+
</html>
|
contact.html
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<!-- Start with lang="en" and no 'dark' class initially -->
|
| 3 |
+
<html lang="en" class="">
|
| 4 |
+
|
| 5 |
+
<head>
|
| 6 |
+
<meta charset="UTF-8">
|
| 7 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 8 |
+
<title>Vision and Discern - Feedback</title> <!-- Updated Title -->
|
| 9 |
+
|
| 10 |
+
<!-- Tailwind CSS via CDN -->
|
| 11 |
+
<script src="https://cdn.tailwindcss.com/3.4.1"></script>
|
| 12 |
+
<script>
|
| 13 |
+
tailwind.config = {
|
| 14 |
+
darkMode: 'class', // Enable class-based dark mode
|
| 15 |
+
theme: {
|
| 16 |
+
extend: {
|
| 17 |
+
colors: {
|
| 18 |
+
primary: '#4a90e2', // Match main app's primary color
|
| 19 |
+
secondary: '#f0f8ff',
|
| 20 |
+
},
|
| 21 |
+
borderRadius: {
|
| 22 |
+
'none': '0px', 'sm': '4px', DEFAULT: '8px', 'md': '12px',
|
| 23 |
+
'lg': '16px', 'xl': '20px', '2xl': '24px', '3xl': '32px',
|
| 24 |
+
'full': '9999px',
|
| 25 |
+
'button': '4px' // Match main app's button radius
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
}
|
| 29 |
+
}
|
| 30 |
+
</script>
|
| 31 |
+
<!-- Fonts -->
|
| 32 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 33 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 34 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
| 35 |
+
<!-- Icons (Remixicon) -->
|
| 36 |
+
<link href="https://cdn.jsdelivr.net/npm/remixicon@4.2.0/fonts/remixicon.css" rel="stylesheet">
|
| 37 |
+
|
| 38 |
+
<!-- Custom Styles (Mainly for the switch) -->
|
| 39 |
+
<style>
|
| 40 |
+
body { font-family: 'Inter', sans-serif; }
|
| 41 |
+
/* Custom Switch Styles - Copied from main app */
|
| 42 |
+
.custom-switch { position: relative; display: inline-block; width: 50px; height: 24px; }
|
| 43 |
+
.custom-switch-input { opacity: 0; width: 0; height: 0; }
|
| 44 |
+
.custom-switch-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 34px; }
|
| 45 |
+
.custom-switch-slider:before { position: absolute; content: ""; height: 18px; width: 18px; left: 3px; bottom: 3px; background-color: white; transition: .4s; border-radius: 50%; }
|
| 46 |
+
.custom-switch-input:checked + .custom-switch-slider { background-color: #4a90e2; } /* Your primary color */
|
| 47 |
+
.custom-switch-input:checked + .custom-switch-slider:before { transform: translateX(26px); }
|
| 48 |
+
|
| 49 |
+
/* Base dark mode styles - Copied from main app */
|
| 50 |
+
html.dark body { background-color: #111827; color: #d1d5db; }
|
| 51 |
+
html.dark header, html.dark footer { background-color: #1f2937; }
|
| 52 |
+
html.dark .card { background-color: #1f2937; border-color: #374151; }
|
| 53 |
+
html.dark h1, html.dark h2, html.dark h3, html.dark p, html.dark span, html.dark li, html.dark label, html.dark small, html.dark .subtitle, html.dark .info-text, html.dark .credits p, html.dark .attribution { color: #d1d5db; }
|
| 54 |
+
html.dark .text-gray-600 { color: #9ca3af; }
|
| 55 |
+
html.dark .text-gray-700 { color: #9ca3af; }
|
| 56 |
+
html.dark .text-gray-500 { color: #6b7280; }
|
| 57 |
+
/* Input/Textarea dark styles */
|
| 58 |
+
html.dark input[type="text"],
|
| 59 |
+
html.dark input[type="email"],
|
| 60 |
+
html.dark textarea {
|
| 61 |
+
background-color: #374151; /* gray-700 */
|
| 62 |
+
border-color: #4b5563; /* gray-600 */
|
| 63 |
+
color: #d1d5db; /* gray-300 */
|
| 64 |
+
}
|
| 65 |
+
html.dark input::placeholder,
|
| 66 |
+
html.dark textarea::placeholder {
|
| 67 |
+
color: #9ca3af; /* gray-400 */
|
| 68 |
+
}
|
| 69 |
+
html.dark .button-secondary { background-color: #4b5563; color: #d1d5db; }
|
| 70 |
+
html.dark .button-secondary:hover { background-color: #374151; }
|
| 71 |
+
/* Success message dark */
|
| 72 |
+
html.dark #feedbackSuccessMessage {
|
| 73 |
+
background-color: #065f46; /* emerald-800 */
|
| 74 |
+
color: #a7f3d0; /* emerald-200 */
|
| 75 |
+
border-color: #047857; /* emerald-700 */
|
| 76 |
+
}
|
| 77 |
+
</style>
|
| 78 |
+
</head>
|
| 79 |
+
|
| 80 |
+
<body class="bg-gray-100 dark:bg-gray-900 min-h-screen flex flex-col text-gray-900 dark:text-gray-200">
|
| 81 |
+
|
| 82 |
+
<!-- Main Application Container -->
|
| 83 |
+
<div id="app-container" class="flex-grow flex flex-col">
|
| 84 |
+
|
| 85 |
+
<!-- Header -->
|
| 86 |
+
<header class="bg-white dark:bg-gray-800 shadow-sm sticky top-0 z-50">
|
| 87 |
+
<div class="container mx-auto px-4 py-3 flex items-center justify-between">
|
| 88 |
+
<!-- Left Side: Logo & Nav -->
|
| 89 |
+
<div class="flex items-center">
|
| 90 |
+
<a href="home.html" class="text-xl font-bold text-primary dark:text-blue-400 mr-6">Vision & Discern</a>
|
| 91 |
+
<nav class="hidden md:flex space-x-6">
|
| 92 |
+
<a href="home.html" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Home</a>
|
| 93 |
+
<a href="index.html" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">OCR</a>
|
| 94 |
+
<a href="features.html" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Features</a>
|
| 95 |
+
<a href="feedback.html" class="text-primary dark:text-blue-400 font-medium">Feedback</a>
|
| 96 |
+
<a href="contact.html" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Contact Us</a>
|
| 97 |
+
</nav>
|
| 98 |
+
</div>
|
| 99 |
+
|
| 100 |
+
<!-- Right Side: Switches & Logout -->
|
| 101 |
+
<div class="flex items-center space-x-4">
|
| 102 |
+
<!-- Theme Switch -->
|
| 103 |
+
<div class="items-center space-x-2 hidden md:flex">
|
| 104 |
+
<span class="text-sm text-gray-600 dark:text-gray-400"><i class="ri-sun-line"></i></span>
|
| 105 |
+
<label class="custom-switch">
|
| 106 |
+
<input type="checkbox" id="themeToggle" class="custom-switch-input">
|
| 107 |
+
<span class="custom-switch-slider"></span>
|
| 108 |
+
</label>
|
| 109 |
+
<span class="text-sm text-gray-600 dark:text-gray-400"><i class="ri-moon-line"></i></span>
|
| 110 |
+
</div>
|
| 111 |
+
<!-- Logout Button -->
|
| 112 |
+
<button id="logoutButton" class="button-secondary bg-red-600 hover:bg-red-700 dark:bg-red-700 dark:hover:bg-red-800 text-white px-3 py-1.5 rounded-button text-sm inline-flex items-center gap-1">
|
| 113 |
+
<i class="ri-logout-box-r-line"></i>
|
| 114 |
+
Logout
|
| 115 |
+
</button>
|
| 116 |
+
<!-- Mobile Menu Button (Placeholder) -->
|
| 117 |
+
<button class="md:hidden w-10 h-10 flex items-center justify-center" aria-label="Toggle Menu">
|
| 118 |
+
<i class="ri-menu-line text-gray-600 dark:text-gray-300 text-xl"></i>
|
| 119 |
+
</button>
|
| 120 |
+
</div>
|
| 121 |
+
</div>
|
| 122 |
+
</header>
|
| 123 |
+
|
| 124 |
+
<!-- Main Content Area -->
|
| 125 |
+
<main class="flex-grow container mx-auto px-4 py-8">
|
| 126 |
+
|
| 127 |
+
<!-- Page Header Section -->
|
| 128 |
+
<section class="mb-10 text-center border-b border-gray-200 dark:border-gray-700 pb-6">
|
| 129 |
+
<h1 class="text-3xl md:text-4xl font-bold text-gray-900 dark:text-white mb-2">Feedback</h1>
|
| 130 |
+
<p class="subtitle text-lg text-gray-700 dark:text-gray-300 mb-1">Help us improve by sharing your thoughts</p>
|
| 131 |
+
</section>
|
| 132 |
+
|
| 133 |
+
<!-- Feedback Form Section -->
|
| 134 |
+
<div class="card bg-white dark:bg-gray-800 p-6 md:p-8 rounded-lg shadow-lg border border-gray-100 dark:border-gray-700 max-w-2xl mx-auto">
|
| 135 |
+
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-6 flex items-center gap-3 border-b border-gray-200 dark:border-gray-600 pb-3">
|
| 136 |
+
<i class="ri-mail-send-line text-primary dark:text-blue-400 text-3xl"></i>
|
| 137 |
+
<span>Submit Your Feedback</span>
|
| 138 |
+
</h2>
|
| 139 |
+
|
| 140 |
+
<!-- Success Message Placeholder -->
|
| 141 |
+
<div id="feedbackSuccessMessage" class="hidden bg-emerald-100 border border-emerald-300 text-emerald-800 dark:bg-emerald-900 dark:bg-opacity-60 dark:border-emerald-700 dark:text-emerald-200 px-4 py-3 rounded-md text-sm mb-6" role="alert">
|
| 142 |
+
<p><strong class="font-medium">Thank you!</strong> Your feedback has been submitted successfully.</p>
|
| 143 |
+
</div>
|
| 144 |
+
|
| 145 |
+
<form id="feedbackForm" class="space-y-5">
|
| 146 |
+
<div>
|
| 147 |
+
<label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Name</label>
|
| 148 |
+
<input type="text" id="name" name="name" required
|
| 149 |
+
class="w-full px-4 py-2 border border-gray-300 rounded-button focus:outline-none focus:ring-2 focus:ring-primary/50 dark:bg-gray-700 dark:border-gray-600 dark:text-gray-200"
|
| 150 |
+
placeholder="Your Name">
|
| 151 |
+
</div>
|
| 152 |
+
|
| 153 |
+
<div>
|
| 154 |
+
<label for="email" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Email Address</label>
|
| 155 |
+
<input type="email" id="email" name="email" required
|
| 156 |
+
class="w-full px-4 py-2 border border-gray-300 rounded-button focus:outline-none focus:ring-2 focus:ring-primary/50 dark:bg-gray-700 dark:border-gray-600 dark:text-gray-200"
|
| 157 |
+
placeholder="you@example.com">
|
| 158 |
+
</div>
|
| 159 |
+
|
| 160 |
+
<div>
|
| 161 |
+
<label for="feedback" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Feedback / Comments</label>
|
| 162 |
+
<textarea id="feedback" name="feedback" rows="5" required
|
| 163 |
+
class="w-full px-4 py-2 border border-gray-300 rounded-button focus:outline-none focus:ring-2 focus:ring-primary/50 dark:bg-gray-700 dark:border-gray-600 dark:text-gray-200"
|
| 164 |
+
placeholder="Please share your experience, suggestions, or any issues you encountered..."></textarea>
|
| 165 |
+
</div>
|
| 166 |
+
|
| 167 |
+
<div class="pt-2">
|
| 168 |
+
<button type="submit"
|
| 169 |
+
class="w-full sm:w-auto bg-primary text-white px-6 py-2.5 rounded-button hover:bg-blue-700 transition-colors font-medium inline-flex items-center justify-center gap-2 disabled:opacity-50">
|
| 170 |
+
<i class="ri-send-plane-2-line"></i> Send Feedback
|
| 171 |
+
</button>
|
| 172 |
+
</div>
|
| 173 |
+
</form>
|
| 174 |
+
</div> <!-- End Feedback Card -->
|
| 175 |
+
|
| 176 |
+
</main>
|
| 177 |
+
|
| 178 |
+
<!-- Footer -->
|
| 179 |
+
<footer class="bg-gray-800 text-gray-400 py-8 mt-12">
|
| 180 |
+
<div class="container mx-auto px-4 text-center">
|
| 181 |
+
<div class="credits text-sm mb-4">
|
| 182 |
+
<p>Powered by <strong>D SAKSHI</strong> (MCA Final Year BIT Durg, Chhattisgarh) | © SlimShadow Org. All Rights Reserved.</p>
|
| 183 |
+
</div>
|
| 184 |
+
<div class="flex justify-center space-x-4">
|
| 185 |
+
<a href="#" class="hover:text-white" title="GitHub (Placeholder)"><i class="ri-github-fill"></i></a>
|
| 186 |
+
<a href="#" class="hover:text-white" title="LinkedIn (Placeholder)"><i class="ri-linkedin-box-fill"></i></a>
|
| 187 |
+
</div>
|
| 188 |
+
</div>
|
| 189 |
+
</footer>
|
| 190 |
+
|
| 191 |
+
</div> <!-- End #app-container -->
|
| 192 |
+
|
| 193 |
+
<script>
|
| 194 |
+
// --- THEME TOGGLE LOGIC ---
|
| 195 |
+
const themeToggle = document.getElementById('themeToggle');
|
| 196 |
+
const htmlElement = document.documentElement;
|
| 197 |
+
|
| 198 |
+
function applyTheme(isDark) {
|
| 199 |
+
if (isDark) {
|
| 200 |
+
htmlElement.classList.add('dark');
|
| 201 |
+
if (themeToggle) themeToggle.checked = true;
|
| 202 |
+
} else {
|
| 203 |
+
htmlElement.classList.remove('dark');
|
| 204 |
+
if (themeToggle) themeToggle.checked = false;
|
| 205 |
+
}
|
| 206 |
+
}
|
| 207 |
+
// Check localStorage on load
|
| 208 |
+
const prefersDark = localStorage.getItem('theme') === 'dark' ||
|
| 209 |
+
(localStorage.getItem('theme') === null && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
| 210 |
+
applyTheme(prefersDark);
|
| 211 |
+
|
| 212 |
+
// Add listener to toggle button
|
| 213 |
+
if (themeToggle) {
|
| 214 |
+
themeToggle.addEventListener('change', (event) => {
|
| 215 |
+
const isDark = event.target.checked;
|
| 216 |
+
applyTheme(isDark);
|
| 217 |
+
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
| 218 |
+
});
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
// --- Logout Button Logic ---
|
| 222 |
+
const logoutButton = document.getElementById("logoutButton");
|
| 223 |
+
if (logoutButton) {
|
| 224 |
+
logoutButton.addEventListener("click", function() {
|
| 225 |
+
console.log("Logout clicked, redirecting...");
|
| 226 |
+
window.location.href = "index.html"; // Redirect to main app/login
|
| 227 |
+
});
|
| 228 |
+
} else {
|
| 229 |
+
console.warn("Logout button not found.");
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
// --- Feedback Form Logic ---
|
| 233 |
+
const feedbackForm = document.getElementById('feedbackForm');
|
| 234 |
+
const successMessage = document.getElementById('feedbackSuccessMessage');
|
| 235 |
+
|
| 236 |
+
if (feedbackForm) {
|
| 237 |
+
feedbackForm.addEventListener('submit', function(event) {
|
| 238 |
+
event.preventDefault(); // Prevent default form submission
|
| 239 |
+
|
| 240 |
+
// Basic validation (Tailwind uses :required for browser validation)
|
| 241 |
+
// You could add more JS validation here if needed
|
| 242 |
+
|
| 243 |
+
// Simulate submission (Replace with actual fetch/API call)
|
| 244 |
+
console.log('Feedback form submitted (simulation)');
|
| 245 |
+
const formData = new FormData(feedbackForm);
|
| 246 |
+
for (let [key, value] of formData.entries()) {
|
| 247 |
+
console.log(`${key}: ${value}`);
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
// Clear the form
|
| 251 |
+
feedbackForm.reset();
|
| 252 |
+
|
| 253 |
+
// Show success message
|
| 254 |
+
if (successMessage) {
|
| 255 |
+
successMessage.classList.remove('hidden');
|
| 256 |
+
// Optional: Hide the message after a few seconds
|
| 257 |
+
setTimeout(() => {
|
| 258 |
+
successMessage.classList.add('hidden');
|
| 259 |
+
}, 5000); // Hide after 5 seconds
|
| 260 |
+
}
|
| 261 |
+
|
| 262 |
+
alert('Thank you for your feedback! (Simulation)'); // Simple alert for now
|
| 263 |
+
});
|
| 264 |
+
} else {
|
| 265 |
+
console.warn("Feedback form not found.");
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
</script>
|
| 269 |
+
|
| 270 |
+
</body>
|
| 271 |
+
</html>
|
features.html
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<!-- Start with lang="en" and no 'dark' class initially -->
|
| 3 |
+
<html lang="en" class="">
|
| 4 |
+
|
| 5 |
+
<head>
|
| 6 |
+
<meta charset="UTF-8">
|
| 7 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 8 |
+
<title>Vision and Discern - Key Features</title> <!-- Updated Title -->
|
| 9 |
+
|
| 10 |
+
<!-- Tailwind CSS via CDN -->
|
| 11 |
+
<script src="https://cdn.tailwindcss.com/3.4.1"></script>
|
| 12 |
+
<script>
|
| 13 |
+
tailwind.config = {
|
| 14 |
+
darkMode: 'class', // Enable class-based dark mode
|
| 15 |
+
theme: {
|
| 16 |
+
extend: {
|
| 17 |
+
colors: {
|
| 18 |
+
primary: '#4a90e2', // Match main app's primary color
|
| 19 |
+
secondary: '#f0f8ff', // Light blueish background for reference
|
| 20 |
+
},
|
| 21 |
+
borderRadius: {
|
| 22 |
+
'none': '0px', 'sm': '4px', DEFAULT: '8px', 'md': '12px',
|
| 23 |
+
'lg': '16px', 'xl': '20px', '2xl': '24px', '3xl': '32px',
|
| 24 |
+
'full': '9999px',
|
| 25 |
+
'button': '4px' // Match main app's button radius
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
}
|
| 29 |
+
}
|
| 30 |
+
</script>
|
| 31 |
+
<!-- Fonts -->
|
| 32 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 33 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 34 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
| 35 |
+
<!-- Icons (Remixicon) -->
|
| 36 |
+
<link href="https://cdn.jsdelivr.net/npm/remixicon@4.2.0/fonts/remixicon.css" rel="stylesheet">
|
| 37 |
+
|
| 38 |
+
<!-- Custom Styles (Mainly for the switch) -->
|
| 39 |
+
<style>
|
| 40 |
+
body { font-family: 'Inter', sans-serif; }
|
| 41 |
+
/* Custom Switch Styles - Copied from main app */
|
| 42 |
+
.custom-switch { position: relative; display: inline-block; width: 50px; height: 24px; }
|
| 43 |
+
.custom-switch-input { opacity: 0; width: 0; height: 0; }
|
| 44 |
+
.custom-switch-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 34px; }
|
| 45 |
+
.custom-switch-slider:before { position: absolute; content: ""; height: 18px; width: 18px; left: 3px; bottom: 3px; background-color: white; transition: .4s; border-radius: 50%; }
|
| 46 |
+
.custom-switch-input:checked + .custom-switch-slider { background-color: #4a90e2; } /* Your primary color */
|
| 47 |
+
.custom-switch-input:checked + .custom-switch-slider:before { transform: translateX(26px); }
|
| 48 |
+
|
| 49 |
+
/* Base dark mode styles - Copied from main app */
|
| 50 |
+
html.dark body { background-color: #111827; color: #d1d5db; }
|
| 51 |
+
html.dark header, html.dark footer { background-color: #1f2937; }
|
| 52 |
+
html.dark .card { background-color: #1f2937; border-color: #374151; }
|
| 53 |
+
html.dark h1, html.dark h2, html.dark h3, html.dark p, html.dark span, html.dark li, html.dark label, html.dark small, html.dark .subtitle, html.dark .info-text, html.dark .credits p, html.dark .attribution { color: #d1d5db; }
|
| 54 |
+
html.dark .text-gray-600 { color: #9ca3af; }
|
| 55 |
+
html.dark .text-gray-700 { color: #9ca3af; }
|
| 56 |
+
html.dark .text-gray-500 { color: #6b7280; }
|
| 57 |
+
html.dark .button-secondary { background-color: #4b5563; color: #d1d5db; }
|
| 58 |
+
html.dark .button-secondary:hover { background-color: #374151; }
|
| 59 |
+
html.dark .features-list li strong { color: #e5e7eb; } /* Slightly lighter strong tag in dark mode */
|
| 60 |
+
</style>
|
| 61 |
+
</head>
|
| 62 |
+
|
| 63 |
+
<body class="bg-gray-100 dark:bg-gray-900 min-h-screen flex flex-col text-gray-900 dark:text-gray-200">
|
| 64 |
+
|
| 65 |
+
<!-- Main Application Container -->
|
| 66 |
+
<div id="app-container" class="flex-grow flex flex-col">
|
| 67 |
+
|
| 68 |
+
<!-- Header -->
|
| 69 |
+
<header class="bg-white dark:bg-gray-800 shadow-sm sticky top-0 z-50">
|
| 70 |
+
<div class="container mx-auto px-4 py-3 flex items-center justify-between">
|
| 71 |
+
<!-- Left Side: Logo & Nav -->
|
| 72 |
+
<div class="flex items-center">
|
| 73 |
+
<a href="home.html" class="text-xl font-bold text-primary dark:text-blue-400 mr-6">Vision & Discern</a>
|
| 74 |
+
<nav class="hidden md:flex space-x-6">
|
| 75 |
+
<a href="home.html" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Home</a>
|
| 76 |
+
<a href="index.html" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">OCR</a>
|
| 77 |
+
<!-- Links to other tools (optional, consider if they are separate apps or tabs) -->
|
| 78 |
+
<!-- <a href="translation.html" target="_blank" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Translation</a> -->
|
| 79 |
+
<!-- <a href="gender_predictor.html" target="_blank" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Gender</a> -->
|
| 80 |
+
<a href="features.html" class="text-primary dark:text-blue-400 font-medium">Features</a>
|
| 81 |
+
<a href="feedback.html" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Feedback</a>
|
| 82 |
+
<a href="contact.html" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Contact Us</a>
|
| 83 |
+
<!-- <a href="admin.html" target="_blank" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Admin</a> -->
|
| 84 |
+
</nav>
|
| 85 |
+
</div>
|
| 86 |
+
|
| 87 |
+
<!-- Right Side: Switches & Logout -->
|
| 88 |
+
<div class="flex items-center space-x-4">
|
| 89 |
+
<!-- Theme Switch -->
|
| 90 |
+
<div class="items-center space-x-2 hidden md:flex">
|
| 91 |
+
<span class="text-sm text-gray-600 dark:text-gray-400"><i class="ri-sun-line"></i></span>
|
| 92 |
+
<label class="custom-switch">
|
| 93 |
+
<input type="checkbox" id="themeToggle" class="custom-switch-input">
|
| 94 |
+
<span class="custom-switch-slider"></span>
|
| 95 |
+
</label>
|
| 96 |
+
<span class="text-sm text-gray-600 dark:text-gray-400"><i class="ri-moon-line"></i></span>
|
| 97 |
+
</div>
|
| 98 |
+
<!-- Logout Button -->
|
| 99 |
+
<button id="logoutButton" class="button-secondary bg-red-600 hover:bg-red-700 dark:bg-red-700 dark:hover:bg-red-800 text-white px-3 py-1.5 rounded-button text-sm inline-flex items-center gap-1">
|
| 100 |
+
<i class="ri-logout-box-r-line"></i>
|
| 101 |
+
Logout
|
| 102 |
+
</button>
|
| 103 |
+
<!-- Mobile Menu Button (Placeholder) -->
|
| 104 |
+
<button class="md:hidden w-10 h-10 flex items-center justify-center" aria-label="Toggle Menu">
|
| 105 |
+
<i class="ri-menu-line text-gray-600 dark:text-gray-300 text-xl"></i>
|
| 106 |
+
</button>
|
| 107 |
+
</div>
|
| 108 |
+
</div>
|
| 109 |
+
</header>
|
| 110 |
+
|
| 111 |
+
<!-- Main Content Area -->
|
| 112 |
+
<main class="flex-grow container mx-auto px-4 py-8">
|
| 113 |
+
|
| 114 |
+
<!-- Page Header Section -->
|
| 115 |
+
<section class="mb-10 text-center border-b border-gray-200 dark:border-gray-700 pb-6">
|
| 116 |
+
<h1 class="text-3xl md:text-4xl font-bold text-gray-900 dark:text-white mb-2">Application Features</h1>
|
| 117 |
+
<p class="subtitle text-lg text-gray-700 dark:text-gray-300 mb-1">Explore the capabilities of our AI-powered tools</p>
|
| 118 |
+
<p class="attribution text-sm text-gray-500 dark:text-gray-400">Powered by Sakshi's Engines</p>
|
| 119 |
+
</section>
|
| 120 |
+
|
| 121 |
+
<!-- Features Section -->
|
| 122 |
+
<div class="card bg-white dark:bg-gray-800 p-6 md:p-8 rounded-lg shadow-lg border border-gray-100 dark:border-gray-700 max-w-3xl mx-auto">
|
| 123 |
+
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-6 flex items-center gap-3 border-b border-gray-200 dark:border-gray-600 pb-3">
|
| 124 |
+
<i class="ri-star-line text-primary dark:text-blue-400 text-3xl"></i>
|
| 125 |
+
<span>Key Features</span>
|
| 126 |
+
</h2>
|
| 127 |
+
|
| 128 |
+
<!-- Using Tailwind for the list -->
|
| 129 |
+
<ul class="features-list list-none space-y-4 text-gray-700 dark:text-gray-300">
|
| 130 |
+
<li class="flex items-start gap-3">
|
| 131 |
+
<i class="ri-text-scan-line text-lg mt-1 text-primary dark:text-blue-400"></i>
|
| 132 |
+
<div>
|
| 133 |
+
<strong class="font-semibold text-gray-800 dark:text-gray-100">Accurate Hindi OCR:</strong>
|
| 134 |
+
<span>Extracts Hindi text from images with high precision using advanced deep learning models.</span>
|
| 135 |
+
</div>
|
| 136 |
+
</li>
|
| 137 |
+
<li class="flex items-start gap-3">
|
| 138 |
+
<i class="ri-focus-3-line text-lg mt-1 text-primary dark:text-blue-400"></i>
|
| 139 |
+
<div>
|
| 140 |
+
<strong class="font-semibold text-gray-800 dark:text-gray-100">Word Detection:</strong>
|
| 141 |
+
<span>Identifies and visually highlights individual word boundaries within the uploaded image.</span>
|
| 142 |
+
</div>
|
| 143 |
+
</li>
|
| 144 |
+
<li class="flex items-start gap-3">
|
| 145 |
+
<i class="ri-markup-line text-lg mt-1 text-primary dark:text-blue-400"></i>
|
| 146 |
+
<div>
|
| 147 |
+
<strong class="font-semibold text-gray-800 dark:text-gray-100">Text Prediction:</strong>
|
| 148 |
+
<span>Provides the most likely textual representation of the detected handwritten words.</span>
|
| 149 |
+
</div>
|
| 150 |
+
</li>
|
| 151 |
+
<li class="flex items-start gap-3">
|
| 152 |
+
<i class="ri-translate-2 text-lg mt-1 text-primary dark:text-blue-400"></i>
|
| 153 |
+
<div>
|
| 154 |
+
<strong class="font-semibold text-gray-800 dark:text-gray-100">Multi-Language Translation:</strong>
|
| 155 |
+
<span>Translates the extracted text (or any input text) between numerous languages.</span>
|
| 156 |
+
</div>
|
| 157 |
+
</li>
|
| 158 |
+
<li class="flex items-start gap-3">
|
| 159 |
+
<i class="ri-men-line text-lg mt-1 text-primary dark:text-blue-400"></i> <i class="ri-women-line text-lg mt-1 text-primary dark:text-blue-400 -ml-2"></i>
|
| 160 |
+
<div class="ml-1">
|
| 161 |
+
<strong class="font-semibold text-gray-800 dark:text-gray-100">Gender Prediction:</strong>
|
| 162 |
+
<span>Predicts the likely gender associated with given names based on statistical models.</span>
|
| 163 |
+
</div>
|
| 164 |
+
</li>
|
| 165 |
+
<li class="flex items-start gap-3">
|
| 166 |
+
<i class="ri-drag-move-2-line text-lg mt-1 text-primary dark:text-blue-400"></i>
|
| 167 |
+
<div>
|
| 168 |
+
<strong class="font-semibold text-gray-800 dark:text-gray-100">User-Friendly Interface:</strong>
|
| 169 |
+
<span>Simple, intuitive, and responsive design built with Tailwind CSS for ease of use across devices. Drag & drop supported for uploads.</span>
|
| 170 |
+
</div>
|
| 171 |
+
</li>
|
| 172 |
+
<li class="flex items-start gap-3">
|
| 173 |
+
<i class="ri-lock-password-line text-lg mt-1 text-primary dark:text-blue-400"></i>
|
| 174 |
+
<div>
|
| 175 |
+
<strong class="font-semibold text-gray-800 dark:text-gray-100">Secure Authentication:</strong>
|
| 176 |
+
<span>Protects access with JWT-based login and signup functionality, ensuring only registered users can use the core features.</span>
|
| 177 |
+
</div>
|
| 178 |
+
</li>
|
| 179 |
+
<li class="flex items-start gap-3">
|
| 180 |
+
<i class="ri-moon-foggy-line text-lg mt-1 text-primary dark:text-blue-400"></i>
|
| 181 |
+
<div>
|
| 182 |
+
<strong class="font-semibold text-gray-800 dark:text-gray-100">Dark Mode Support:</strong>
|
| 183 |
+
<span>Includes a theme toggle for comfortable viewing in different lighting conditions.</span>
|
| 184 |
+
</div>
|
| 185 |
+
</li>
|
| 186 |
+
</ul>
|
| 187 |
+
</div> <!-- End Features Card -->
|
| 188 |
+
|
| 189 |
+
</main>
|
| 190 |
+
|
| 191 |
+
<!-- Footer -->
|
| 192 |
+
<footer class="bg-gray-800 text-gray-400 py-8 mt-12">
|
| 193 |
+
<div class="container mx-auto px-4 text-center">
|
| 194 |
+
<div class="credits text-sm mb-4">
|
| 195 |
+
<p>Powered by <strong>D SAKSHI</strong> (MCA Final Year BIT Durg, Chhattisgarh) | © SlimShadow Org. All Rights Reserved.</p>
|
| 196 |
+
</div>
|
| 197 |
+
<div class="flex justify-center space-x-4">
|
| 198 |
+
<a href="#" class="hover:text-white" title="GitHub (Placeholder)"><i class="ri-github-fill"></i></a>
|
| 199 |
+
<a href="#" class="hover:text-white" title="LinkedIn (Placeholder)"><i class="ri-linkedin-box-fill"></i></a>
|
| 200 |
+
</div>
|
| 201 |
+
</div>
|
| 202 |
+
</footer>
|
| 203 |
+
|
| 204 |
+
</div> <!-- End #app-container -->
|
| 205 |
+
|
| 206 |
+
<script>
|
| 207 |
+
// --- THEME TOGGLE LOGIC ---
|
| 208 |
+
const themeToggle = document.getElementById('themeToggle');
|
| 209 |
+
const htmlElement = document.documentElement;
|
| 210 |
+
|
| 211 |
+
function applyTheme(isDark) {
|
| 212 |
+
if (isDark) {
|
| 213 |
+
htmlElement.classList.add('dark');
|
| 214 |
+
if (themeToggle) themeToggle.checked = true;
|
| 215 |
+
} else {
|
| 216 |
+
htmlElement.classList.remove('dark');
|
| 217 |
+
if (themeToggle) themeToggle.checked = false;
|
| 218 |
+
}
|
| 219 |
+
}
|
| 220 |
+
// Check localStorage on load
|
| 221 |
+
const prefersDark = localStorage.getItem('theme') === 'dark' ||
|
| 222 |
+
(localStorage.getItem('theme') === null && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
| 223 |
+
applyTheme(prefersDark);
|
| 224 |
+
|
| 225 |
+
// Add listener to toggle button
|
| 226 |
+
if (themeToggle) {
|
| 227 |
+
themeToggle.addEventListener('change', (event) => {
|
| 228 |
+
const isDark = event.target.checked;
|
| 229 |
+
applyTheme(isDark);
|
| 230 |
+
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
| 231 |
+
});
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
// --- Logout Button Logic ---
|
| 235 |
+
const logoutButton = document.getElementById("logoutButton");
|
| 236 |
+
if (logoutButton) {
|
| 237 |
+
logoutButton.addEventListener("click", function() {
|
| 238 |
+
console.log("Logout clicked, redirecting...");
|
| 239 |
+
window.location.href = "index.html"; // Redirect to main app/login
|
| 240 |
+
});
|
| 241 |
+
} else {
|
| 242 |
+
console.warn("Logout button not found.");
|
| 243 |
+
}
|
| 244 |
+
</script>
|
| 245 |
+
|
| 246 |
+
</body>
|
| 247 |
+
</html>
|
feedback.html
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<!-- Start with lang="en" and no 'dark' class initially -->
|
| 3 |
+
<html lang="en" class="">
|
| 4 |
+
|
| 5 |
+
<head>
|
| 6 |
+
<meta charset="UTF-8">
|
| 7 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 8 |
+
<title>Vision and Discern - Feedback</title> <!-- Updated Title -->
|
| 9 |
+
|
| 10 |
+
<!-- Tailwind CSS via CDN -->
|
| 11 |
+
<script src="https://cdn.tailwindcss.com/3.4.1"></script>
|
| 12 |
+
<script>
|
| 13 |
+
tailwind.config = {
|
| 14 |
+
darkMode: 'class', // Enable class-based dark mode
|
| 15 |
+
theme: {
|
| 16 |
+
extend: {
|
| 17 |
+
colors: {
|
| 18 |
+
primary: '#4a90e2', // Match main app's primary color
|
| 19 |
+
secondary: '#f8f9fa', // Using light gray for backgrounds now
|
| 20 |
+
success: '#10b981', // emerald-500
|
| 21 |
+
danger: '#ef4444', // red-500
|
| 22 |
+
},
|
| 23 |
+
borderRadius: {
|
| 24 |
+
'none': '0px', 'sm': '4px', DEFAULT: '8px', 'md': '12px',
|
| 25 |
+
'lg': '16px', 'xl': '20px', '2xl': '24px', '3xl': '32px',
|
| 26 |
+
'full': '9999px',
|
| 27 |
+
'button': '8px' // Using slightly rounder buttons like NIC project
|
| 28 |
+
// 'button': '4px' // Or keep the previous style
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
}
|
| 33 |
+
</script>
|
| 34 |
+
<!-- Fonts -->
|
| 35 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 36 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 37 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
| 38 |
+
<!-- Icons (Remixicon) -->
|
| 39 |
+
<link href="https://cdn.jsdelivr.net/npm/remixicon@4.2.0/fonts/remixicon.css" rel="stylesheet">
|
| 40 |
+
|
| 41 |
+
<!-- Custom Styles (Mainly for the switch) -->
|
| 42 |
+
<style>
|
| 43 |
+
body { font-family: 'Inter', sans-serif; }
|
| 44 |
+
/* Custom Switch Styles - Copied from main app */
|
| 45 |
+
.custom-switch { position: relative; display: inline-block; width: 50px; height: 24px; }
|
| 46 |
+
.custom-switch-input { opacity: 0; width: 0; height: 0; }
|
| 47 |
+
.custom-switch-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 34px; }
|
| 48 |
+
.custom-switch-slider:before { position: absolute; content: ""; height: 18px; width: 18px; left: 3px; bottom: 3px; background-color: white; transition: .4s; border-radius: 50%; }
|
| 49 |
+
.custom-switch-input:checked + .custom-switch-slider { background-color: #4a90e2; } /* Your primary color */
|
| 50 |
+
.custom-switch-input:checked + .custom-switch-slider:before { transform: translateX(26px); }
|
| 51 |
+
|
| 52 |
+
/* Base dark mode styles - Copied from main app */
|
| 53 |
+
html.dark body { background-color: #111827; color: #d1d5db; }
|
| 54 |
+
html.dark header, html.dark footer { background-color: #1f2937; }
|
| 55 |
+
html.dark .card { background-color: #1f2937; border-color: #374151; }
|
| 56 |
+
html.dark h1, html.dark h2, html.dark h3, html.dark p, html.dark span, html.dark li, html.dark label, html.dark small, html.dark .subtitle, html.dark .info-text, html.dark .credits p, html.dark .attribution { color: #d1d5db; }
|
| 57 |
+
html.dark .text-gray-600 { color: #9ca3af; }
|
| 58 |
+
html.dark .text-gray-700 { color: #9ca3af; }
|
| 59 |
+
html.dark .text-gray-500 { color: #6b7280; }
|
| 60 |
+
/* Input/Textarea dark styles */
|
| 61 |
+
html.dark input[type="text"],
|
| 62 |
+
html.dark input[type="email"],
|
| 63 |
+
html.dark textarea {
|
| 64 |
+
background-color: #374151; /* gray-700 */
|
| 65 |
+
border-color: #4b5563; /* gray-600 */
|
| 66 |
+
color: #d1d5db; /* gray-300 */
|
| 67 |
+
}
|
| 68 |
+
html.dark input::placeholder,
|
| 69 |
+
html.dark textarea::placeholder {
|
| 70 |
+
color: #9ca3af; /* gray-400 */
|
| 71 |
+
}
|
| 72 |
+
html.dark .button-secondary { background-color: #4b5563; color: #d1d5db; }
|
| 73 |
+
html.dark .button-secondary:hover { background-color: #374151; }
|
| 74 |
+
/* Feedback Message Dark Mode */
|
| 75 |
+
html.dark .feedback-message-success { background-color: #065f46; color: #a7f3d0; } /* emerald */
|
| 76 |
+
html.dark .feedback-message-error { background-color: #991b1b; color: #fecaca; } /* red */
|
| 77 |
+
|
| 78 |
+
</style>
|
| 79 |
+
</head>
|
| 80 |
+
|
| 81 |
+
<body class="bg-gray-50 dark:bg-gray-900 min-h-screen flex flex-col text-gray-900 dark:text-gray-200">
|
| 82 |
+
|
| 83 |
+
<!-- Main Application Container -->
|
| 84 |
+
<div id="app-container" class="flex-grow flex flex-col">
|
| 85 |
+
|
| 86 |
+
<!-- Header -->
|
| 87 |
+
<header class="bg-white dark:bg-gray-800 shadow-sm sticky top-0 z-50">
|
| 88 |
+
<div class="container mx-auto px-4 py-3 flex items-center justify-between">
|
| 89 |
+
<!-- Left Side: Logo & Nav -->
|
| 90 |
+
<div class="flex items-center">
|
| 91 |
+
<a href="home.html" class="text-xl font-bold text-primary dark:text-blue-400 mr-6">Vision & Discern</a>
|
| 92 |
+
<nav class="hidden md:flex space-x-6">
|
| 93 |
+
<a href="home.html" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Home</a>
|
| 94 |
+
<a href="index.html" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">OCR</a>
|
| 95 |
+
<!-- Optional Links, remove if not separate pages -->
|
| 96 |
+
<!-- <a href="translation.html" target="_blank" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Translation</a> -->
|
| 97 |
+
<!-- <a href="gender_predictor.html" target="_blank" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Gender</a> -->
|
| 98 |
+
<a href="features.html" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Features</a>
|
| 99 |
+
<a href="feedback.html" class="text-primary dark:text-blue-400 font-medium">Feedback</a>
|
| 100 |
+
<a href="contact.html" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Contact Us</a>
|
| 101 |
+
<!-- <a href="admin.html" target="_blank" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Admin</a> -->
|
| 102 |
+
</nav>
|
| 103 |
+
</div>
|
| 104 |
+
|
| 105 |
+
<!-- Right Side: Switches & Logout -->
|
| 106 |
+
<div class="flex items-center space-x-4">
|
| 107 |
+
<!-- Theme Switch -->
|
| 108 |
+
<div class="items-center space-x-2 hidden md:flex">
|
| 109 |
+
<span class="text-sm text-gray-600 dark:text-gray-400"><i class="ri-sun-line"></i></span>
|
| 110 |
+
<label class="custom-switch">
|
| 111 |
+
<input type="checkbox" id="themeToggle" class="custom-switch-input">
|
| 112 |
+
<span class="custom-switch-slider"></span>
|
| 113 |
+
</label>
|
| 114 |
+
<span class="text-sm text-gray-600 dark:text-gray-400"><i class="ri-moon-line"></i></span>
|
| 115 |
+
</div>
|
| 116 |
+
<!-- Logout Button -->
|
| 117 |
+
<button id="logoutButton" class="button-secondary bg-red-600 hover:bg-red-700 dark:bg-red-700 dark:hover:bg-red-800 text-white px-3 py-1.5 rounded-button text-sm inline-flex items-center gap-1">
|
| 118 |
+
<i class="ri-logout-box-r-line"></i>
|
| 119 |
+
Logout
|
| 120 |
+
</button>
|
| 121 |
+
<!-- Mobile Menu Button (Placeholder) -->
|
| 122 |
+
<button class="md:hidden w-10 h-10 flex items-center justify-center" aria-label="Toggle Menu">
|
| 123 |
+
<i class="ri-menu-line text-gray-600 dark:text-gray-300 text-xl"></i>
|
| 124 |
+
</button>
|
| 125 |
+
</div>
|
| 126 |
+
</div>
|
| 127 |
+
</header>
|
| 128 |
+
|
| 129 |
+
<!-- Main Content Area -->
|
| 130 |
+
<main class="flex-grow container mx-auto px-4 py-8">
|
| 131 |
+
|
| 132 |
+
<!-- Page Header Section -->
|
| 133 |
+
<section class="mb-10 text-center border-b border-gray-200 dark:border-gray-700 pb-6">
|
| 134 |
+
<h1 class="text-3xl md:text-4xl font-bold text-gray-900 dark:text-white mb-2">Feedback</h1>
|
| 135 |
+
<p class="subtitle text-lg text-gray-700 dark:text-gray-300 mb-1">Share your feedback to help us improve</p>
|
| 136 |
+
<p class="attribution text-sm text-gray-500 dark:text-gray-400">Powered by Sakshi's Hindi OCR Engine</p>
|
| 137 |
+
</section>
|
| 138 |
+
|
| 139 |
+
<!-- Feedback Form Section -->
|
| 140 |
+
<div class="card bg-white dark:bg-gray-800 p-6 md:p-8 rounded-lg shadow-lg border border-gray-100 dark:border-gray-700 max-w-xl mx-auto" id="feedback">
|
| 141 |
+
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-4 flex items-center gap-3">
|
| 142 |
+
<i class="ri-chat-voice-line text-primary dark:text-blue-400"></i>
|
| 143 |
+
<span>Your Feedback</span>
|
| 144 |
+
</h2>
|
| 145 |
+
<!-- Removed feedback-form wrapper div, styling applied to card -->
|
| 146 |
+
<p class="text-gray-600 dark:text-gray-400 mb-6">We appreciate your input! Please let us know your thoughts, suggestions, or any issues you encountered with the application.</p>
|
| 147 |
+
|
| 148 |
+
<!-- Using a standard form element -->
|
| 149 |
+
<form id="feedbackFormActual" class="space-y-5">
|
| 150 |
+
<div>
|
| 151 |
+
<label for="feedbackText" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1 sr-only">Feedback</label> <!-- Screen reader only label -->
|
| 152 |
+
<textarea id="feedbackText" name="comment" required rows="6"
|
| 153 |
+
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/50 dark:bg-gray-700 dark:border-gray-600 dark:text-gray-200"
|
| 154 |
+
placeholder="Enter your feedback here..."></textarea>
|
| 155 |
+
</div>
|
| 156 |
+
<!-- Feedback Message Placeholder - Styled with Tailwind -->
|
| 157 |
+
<p id="feedbackMessage" class="info-text text-sm font-medium p-3 rounded-md hidden"></p>
|
| 158 |
+
|
| 159 |
+
<div class="pt-1">
|
| 160 |
+
<button id="submitFeedback" type="submit"
|
| 161 |
+
class="w-full bg-primary text-white px-6 py-2.5 rounded-button hover:bg-blue-700 transition-colors font-medium inline-flex items-center justify-center gap-2 disabled:opacity-50">
|
| 162 |
+
<i class="ri-send-plane-2-line"></i> Submit Feedback
|
| 163 |
+
</button>
|
| 164 |
+
</div>
|
| 165 |
+
</form>
|
| 166 |
+
</div> <!-- End Feedback Card -->
|
| 167 |
+
|
| 168 |
+
</main>
|
| 169 |
+
|
| 170 |
+
<!-- Footer -->
|
| 171 |
+
<footer class="bg-gray-800 text-gray-400 py-8 mt-12">
|
| 172 |
+
<div class="container mx-auto px-4 text-center">
|
| 173 |
+
<div class="credits text-sm mb-4">
|
| 174 |
+
<p>Powered by <strong>D SAKSHI</strong> (MCA Final Year BIT Durg, Chhattisgarh) | © SlimShadow Org. All Rights Reserved.</p>
|
| 175 |
+
</div>
|
| 176 |
+
<div class="flex justify-center space-x-4">
|
| 177 |
+
<a href="#" class="hover:text-white" title="GitHub (Placeholder)"><i class="ri-github-fill"></i></a>
|
| 178 |
+
<a href="#" class="hover:text-white" title="LinkedIn (Placeholder)"><i class="ri-linkedin-box-fill"></i></a>
|
| 179 |
+
</div>
|
| 180 |
+
</div>
|
| 181 |
+
</footer>
|
| 182 |
+
|
| 183 |
+
</div> <!-- End #app-container -->
|
| 184 |
+
|
| 185 |
+
<script>
|
| 186 |
+
// --- API Base URL ---
|
| 187 |
+
const API_BASE_URL = 'https://sameernotes-ocr.hf.space';
|
| 188 |
+
|
| 189 |
+
// --- DOM Elements ---
|
| 190 |
+
const themeToggle = document.getElementById('themeToggle');
|
| 191 |
+
const htmlElement = document.documentElement;
|
| 192 |
+
const logoutButton = document.getElementById('logoutButton');
|
| 193 |
+
const feedbackForm = document.getElementById('feedbackFormActual'); // Get the form element
|
| 194 |
+
const submitFeedbackButton = document.getElementById('submitFeedback');
|
| 195 |
+
const feedbackText = document.getElementById('feedbackText');
|
| 196 |
+
const feedbackMessage = document.getElementById('feedbackMessage');
|
| 197 |
+
|
| 198 |
+
// --- THEME TOGGLE LOGIC ---
|
| 199 |
+
function applyTheme(isDark) {
|
| 200 |
+
if (isDark) {
|
| 201 |
+
htmlElement.classList.add('dark');
|
| 202 |
+
if (themeToggle) themeToggle.checked = true;
|
| 203 |
+
} else {
|
| 204 |
+
htmlElement.classList.remove('dark');
|
| 205 |
+
if (themeToggle) themeToggle.checked = false;
|
| 206 |
+
}
|
| 207 |
+
}
|
| 208 |
+
const prefersDark = localStorage.getItem('theme') === 'dark' || (localStorage.getItem('theme') === null && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
| 209 |
+
applyTheme(prefersDark);
|
| 210 |
+
if (themeToggle) {
|
| 211 |
+
themeToggle.addEventListener('change', (event) => {
|
| 212 |
+
const isDark = event.target.checked;
|
| 213 |
+
applyTheme(isDark);
|
| 214 |
+
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
| 215 |
+
});
|
| 216 |
+
}
|
| 217 |
+
|
| 218 |
+
// --- Logout Button Logic ---
|
| 219 |
+
if (logoutButton) {
|
| 220 |
+
logoutButton.addEventListener("click", function() {
|
| 221 |
+
console.log("Logout clicked, redirecting...");
|
| 222 |
+
window.location.href = "index.html"; // Redirect to main app/login
|
| 223 |
+
});
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
// --- Feedback Submission Logic ---
|
| 227 |
+
function showFeedbackMessage(message, type = 'info') { // type can be 'info', 'success', 'error'
|
| 228 |
+
if (!feedbackMessage) return;
|
| 229 |
+
|
| 230 |
+
feedbackMessage.textContent = message;
|
| 231 |
+
// Remove previous color classes
|
| 232 |
+
feedbackMessage.classList.remove('hidden', 'feedback-message-success', 'bg-emerald-100', 'text-emerald-800', 'feedback-message-error', 'bg-red-100', 'text-red-800', 'dark:bg-emerald-900', 'dark:text-emerald-200', 'dark:bg-red-900', 'dark:text-red-200');
|
| 233 |
+
|
| 234 |
+
if (type === 'success') {
|
| 235 |
+
feedbackMessage.classList.add('feedback-message-success', 'bg-emerald-100', 'text-emerald-800', 'dark:bg-emerald-900', 'dark:text-emerald-200');
|
| 236 |
+
} else if (type === 'error') {
|
| 237 |
+
feedbackMessage.classList.add('feedback-message-error', 'bg-red-100', 'text-red-800', 'dark:bg-red-900', 'dark:text-red-200');
|
| 238 |
+
} else {
|
| 239 |
+
// Default info style (optional)
|
| 240 |
+
feedbackMessage.classList.add('text-gray-600', 'dark:text-gray-400');
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
feedbackMessage.classList.remove('hidden'); // Make it visible
|
| 244 |
+
|
| 245 |
+
// Auto-hide after 5 seconds
|
| 246 |
+
setTimeout(() => {
|
| 247 |
+
feedbackMessage.classList.add('hidden');
|
| 248 |
+
feedbackMessage.textContent = '';
|
| 249 |
+
// Clean up color classes after hiding
|
| 250 |
+
feedbackMessage.classList.remove('feedback-message-success', 'bg-emerald-100', 'text-emerald-800', 'feedback-message-error', 'bg-red-100', 'text-red-800', 'dark:bg-emerald-900', 'dark:text-emerald-200', 'dark:bg-red-900', 'dark:text-red-200', 'text-gray-600', 'dark:text-gray-400');
|
| 251 |
+
}, 5000);
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
if (feedbackForm) {
|
| 255 |
+
feedbackForm.addEventListener('submit', async (event) => { // Use the form's submit event
|
| 256 |
+
event.preventDefault(); // Prevent default form submission
|
| 257 |
+
submitFeedbackButton.disabled = true; // Disable button during submission
|
| 258 |
+
|
| 259 |
+
const comment = feedbackText.value.trim();
|
| 260 |
+
|
| 261 |
+
if (!comment) {
|
| 262 |
+
showFeedbackMessage('Please enter your feedback.', 'error');
|
| 263 |
+
submitFeedbackButton.disabled = false; // Re-enable button
|
| 264 |
+
return;
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
try {
|
| 268 |
+
// Assuming the API accepts anonymous feedback or you handle authentication differently
|
| 269 |
+
const response = await fetch(`${API_BASE_URL}/feedback/`, {
|
| 270 |
+
method: 'POST',
|
| 271 |
+
headers: {
|
| 272 |
+
'Content-Type': 'application/json',
|
| 273 |
+
// If authentication is sometimes required, add Authorization header conditionally
|
| 274 |
+
// 'Authorization': `Bearer ${accessToken}` // Example if token needed
|
| 275 |
+
},
|
| 276 |
+
body: JSON.stringify({
|
| 277 |
+
username: "Anonymous", // Or get username if available
|
| 278 |
+
comment: comment
|
| 279 |
+
})
|
| 280 |
+
});
|
| 281 |
+
|
| 282 |
+
if (response.ok) {
|
| 283 |
+
showFeedbackMessage('Feedback submitted successfully! Thank you.', 'success');
|
| 284 |
+
feedbackText.value = ''; // Clear input
|
| 285 |
+
} else {
|
| 286 |
+
const errorData = await response.json().catch(() => ({ detail: 'Failed to submit feedback.' }));
|
| 287 |
+
showFeedbackMessage(`Error: ${errorData.detail || 'Failed to submit feedback.'}`, 'error');
|
| 288 |
+
}
|
| 289 |
+
} catch (error) {
|
| 290 |
+
showFeedbackMessage("Network error submitting feedback. Please try again.", 'error');
|
| 291 |
+
console.error("Feedback submission error:", error);
|
| 292 |
+
} finally {
|
| 293 |
+
submitFeedbackButton.disabled = false; // Re-enable button
|
| 294 |
+
}
|
| 295 |
+
});
|
| 296 |
+
} else {
|
| 297 |
+
console.warn("Feedback form element not found");
|
| 298 |
+
}
|
| 299 |
+
|
| 300 |
+
</script>
|
| 301 |
+
|
| 302 |
+
</body>
|
| 303 |
+
</html>
|
gender_predictor.html
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Name Gender Predictor</title>
|
| 7 |
+
<style>
|
| 8 |
+
/* --- REUSABLE STYLES (from translation.html) --- */
|
| 9 |
+
:root {
|
| 10 |
+
--primary-color: #4a90e2; /* Consistent primary color */
|
| 11 |
+
--secondary-color: #f8f9fa;
|
| 12 |
+
--text-color: #2c3e50;
|
| 13 |
+
--border-radius: 12px;
|
| 14 |
+
--transition: all 0.3s ease;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
* {
|
| 18 |
+
margin: 0;
|
| 19 |
+
padding: 0;
|
| 20 |
+
box-sizing: border-box;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
body {
|
| 24 |
+
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; /* Consistent font */
|
| 25 |
+
line-height: 1.6;
|
| 26 |
+
color: var(--text-color);
|
| 27 |
+
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); /* Consistent background */
|
| 28 |
+
min-height: 100vh;
|
| 29 |
+
padding: 2rem;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
.container {
|
| 33 |
+
max-width: 1000px;
|
| 34 |
+
margin: 0 auto;
|
| 35 |
+
background: white;
|
| 36 |
+
padding: 2rem;
|
| 37 |
+
border-radius: var(--border-radius);
|
| 38 |
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
h1 {
|
| 42 |
+
color: var(--primary-color);
|
| 43 |
+
text-align: center;
|
| 44 |
+
margin-bottom: 2rem;
|
| 45 |
+
font-size: 2.5rem;
|
| 46 |
+
font-weight: 700;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
h2 {
|
| 50 |
+
color: var(--text-color);
|
| 51 |
+
margin-bottom: 1.5rem;
|
| 52 |
+
font-size: 1.8rem;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
/* --- TAB STYLES --- */
|
| 56 |
+
.tab {
|
| 57 |
+
display: flex;
|
| 58 |
+
gap: 1rem;
|
| 59 |
+
margin-bottom: 2rem;
|
| 60 |
+
border: none;
|
| 61 |
+
background: none;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
.tab button {
|
| 65 |
+
flex: 1;
|
| 66 |
+
padding: 1rem;
|
| 67 |
+
font-size: 1.1rem;
|
| 68 |
+
border: 2px solid var(--primary-color);
|
| 69 |
+
background: transparent;
|
| 70 |
+
color: var(--primary-color);
|
| 71 |
+
border-radius: var(--border-radius);
|
| 72 |
+
cursor: pointer;
|
| 73 |
+
transition: var(--transition);
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
.tab button:hover {
|
| 77 |
+
background: rgba(74, 144, 226, 0.1);
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
.tab button.active {
|
| 81 |
+
background: var(--primary-color);
|
| 82 |
+
color: white;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
.tabcontent {
|
| 86 |
+
display: none; /* Initially hidden */
|
| 87 |
+
padding: 2rem;
|
| 88 |
+
background: var(--secondary-color);
|
| 89 |
+
border-radius: var(--border-radius);
|
| 90 |
+
margin-bottom: 2rem;
|
| 91 |
+
}
|
| 92 |
+
/* --- END TAB STYLES --- */
|
| 93 |
+
|
| 94 |
+
label {
|
| 95 |
+
display: block;
|
| 96 |
+
margin-bottom: 0.5rem;
|
| 97 |
+
font-weight: 600;
|
| 98 |
+
color: var(--text-color);
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
textarea, input[type="text"] {
|
| 102 |
+
width: 100%;
|
| 103 |
+
padding: 1rem;
|
| 104 |
+
margin-bottom: 1.5rem;
|
| 105 |
+
border: 2px solid #e1e8ed;
|
| 106 |
+
border-radius: var(--border-radius);
|
| 107 |
+
font-size: 1rem;
|
| 108 |
+
transition: var(--transition);
|
| 109 |
+
background: white;
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
textarea {
|
| 113 |
+
min-height: 150px;
|
| 114 |
+
resize: vertical;
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
textarea:focus, input[type="text"]:focus {
|
| 118 |
+
outline: none;
|
| 119 |
+
border-color: var(--primary-color);
|
| 120 |
+
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
button {
|
| 124 |
+
background: var(--primary-color);
|
| 125 |
+
color: white;
|
| 126 |
+
padding: 1rem 2rem;
|
| 127 |
+
border: none;
|
| 128 |
+
border-radius: var(--border-radius);
|
| 129 |
+
font-size: 1.1rem;
|
| 130 |
+
cursor: pointer;
|
| 131 |
+
transition: var(--transition);
|
| 132 |
+
width: 100%;
|
| 133 |
+
margin-top: 1rem;
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
button:hover {
|
| 137 |
+
transform: translateY(-2px);
|
| 138 |
+
box-shadow: 0 5px 15px rgba(74, 144, 226, 0.3);
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
.loading-indicator {
|
| 142 |
+
display: none;
|
| 143 |
+
text-align: center;
|
| 144 |
+
color: var(--primary-color);
|
| 145 |
+
margin: 1rem 0;
|
| 146 |
+
font-weight: 600;
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
/* --- RESULT STYLES (from translation.html)--- */
|
| 150 |
+
#results {
|
| 151 |
+
background: white;
|
| 152 |
+
padding: 1.5rem;
|
| 153 |
+
border-radius: var(--border-radius);
|
| 154 |
+
margin-top: 2rem;
|
| 155 |
+
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
.result-header {
|
| 159 |
+
color: var(--primary-color);
|
| 160 |
+
margin-bottom: 1rem;
|
| 161 |
+
font-size: 1.3rem;
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
.result-item {
|
| 165 |
+
margin-bottom: 1rem;
|
| 166 |
+
padding-bottom: 1rem;
|
| 167 |
+
border-bottom: 1px solid #e1e8ed;
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
.result-item:last-child {
|
| 171 |
+
border-bottom: none;
|
| 172 |
+
margin-bottom: 0;
|
| 173 |
+
padding-bottom: 0;
|
| 174 |
+
}
|
| 175 |
+
.result-label {
|
| 176 |
+
font-weight: 600;
|
| 177 |
+
margin-bottom: 0.5rem;
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
/* --- ERROR MESSAGE --- */
|
| 181 |
+
#error-message {
|
| 182 |
+
background: #fee2e2;
|
| 183 |
+
color: #dc2626;
|
| 184 |
+
padding: 1rem;
|
| 185 |
+
border-radius: var(--border-radius);
|
| 186 |
+
margin-top: 1rem;
|
| 187 |
+
display: none;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
/* --- RESPONSIVE --- */
|
| 191 |
+
@media (max-width: 768px) {
|
| 192 |
+
body {
|
| 193 |
+
padding: 1rem;
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
.container {
|
| 197 |
+
padding: 1rem;
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
.tab button {
|
| 201 |
+
padding: 0.8rem;
|
| 202 |
+
font-size: 1rem;
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
.tabcontent {
|
| 206 |
+
padding: 1rem;
|
| 207 |
+
}
|
| 208 |
+
}
|
| 209 |
+
.credits {
|
| 210 |
+
text-align: center;
|
| 211 |
+
margin-top: 2rem;
|
| 212 |
+
color: var(--text-color);
|
| 213 |
+
font-size: 0.875rem;
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
</style>
|
| 217 |
+
</head>
|
| 218 |
+
<body>
|
| 219 |
+
<div class="container">
|
| 220 |
+
<h1>Name Gender Predictor</h1>
|
| 221 |
+
|
| 222 |
+
<div class="tab">
|
| 223 |
+
<button class="tablinks active" onclick="openTab(event, 'single')">Single Name</button>
|
| 224 |
+
<button class="tablinks" onclick="openTab(event, 'batch')">Batch Names</button>
|
| 225 |
+
</div>
|
| 226 |
+
|
| 227 |
+
<!-- Single Name Prediction Tab -->
|
| 228 |
+
<div id="single" class="tabcontent" style="display: block;">
|
| 229 |
+
<h2>Single Name Prediction</h2>
|
| 230 |
+
<label for="singleName">Enter a single name:</label>
|
| 231 |
+
<input type="text" id="singleName" name="singleName" placeholder="e.g., Sakshi">
|
| 232 |
+
<button id="predictSingleButton">Predict Gender</button>
|
| 233 |
+
|
| 234 |
+
<div class="loading-indicator" id="singleLoading">
|
| 235 |
+
Analyzing name...
|
| 236 |
+
</div>
|
| 237 |
+
|
| 238 |
+
<div id="singleResult" class = "results">
|
| 239 |
+
<h3 class="result-header">Prediction Results</h3>
|
| 240 |
+
</div>
|
| 241 |
+
</div>
|
| 242 |
+
|
| 243 |
+
<!-- Batch Name Prediction Tab -->
|
| 244 |
+
<div id="batch" class="tabcontent">
|
| 245 |
+
<h2>Batch Name Prediction</h2>
|
| 246 |
+
<label for="batchNames">Enter names (comma-separated):</label>
|
| 247 |
+
<textarea id="batchNames" name="batchNames" rows="4" placeholder="e.g., Sakshi, Sameer, Shweta"></textarea>
|
| 248 |
+
<button id="predictBatchButton">Predict Multiple Names</button>
|
| 249 |
+
|
| 250 |
+
<div class="loading-indicator" id="batchLoading">
|
| 251 |
+
Analyzing names...
|
| 252 |
+
</div>
|
| 253 |
+
|
| 254 |
+
<div id="batchResults" class = "results">
|
| 255 |
+
<h3 class="result-header">Prediction Results</h3>
|
| 256 |
+
</div>
|
| 257 |
+
</div>
|
| 258 |
+
<div id="error-message"></div>
|
| 259 |
+
<div class="credits">
|
| 260 |
+
<p>Powered by <strong>D SAKSHI</strong> (MCA Final Year BIT Durg, Chhattisgarh)</p>
|
| 261 |
+
<p>© SlimShadow Org. All Rights Reserved.</p>
|
| 262 |
+
</div>
|
| 263 |
+
</div>
|
| 264 |
+
|
| 265 |
+
<script>
|
| 266 |
+
const API_BASE_URL = "https://sameernotes-gender-prediction-space.hf.space";
|
| 267 |
+
|
| 268 |
+
// --- ELEMENTS ---
|
| 269 |
+
const singleNameInput = document.getElementById('singleName');
|
| 270 |
+
const predictSingleButton = document.getElementById('predictSingleButton');
|
| 271 |
+
const singleResultDiv = document.getElementById('singleResult');
|
| 272 |
+
const batchNamesInput = document.getElementById('batchNames');
|
| 273 |
+
const predictBatchButton = document.getElementById('predictBatchButton');
|
| 274 |
+
const batchResultsDiv = document.getElementById('batchResults');
|
| 275 |
+
const errorMessageDiv = document.getElementById('error-message');
|
| 276 |
+
const singleLoadingDiv = document.getElementById('singleLoading'); // Separate loading indicators
|
| 277 |
+
const batchLoadingDiv = document.getElementById('batchLoading');
|
| 278 |
+
|
| 279 |
+
// --- TAB FUNCTIONALITY ---
|
| 280 |
+
function openTab(evt, tabName) {
|
| 281 |
+
const tabcontent = document.getElementsByClassName("tabcontent");
|
| 282 |
+
for (let i = 0; i < tabcontent.length; i++) {
|
| 283 |
+
tabcontent[i].style.display = "none";
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
const tablinks = document.getElementsByClassName("tablinks");
|
| 287 |
+
for (let i = 0; i < tablinks.length; i++) {
|
| 288 |
+
tablinks[i].className = tablinks[i].className.replace(" active", "");
|
| 289 |
+
}
|
| 290 |
+
|
| 291 |
+
document.getElementById(tabName).style.display = "block";
|
| 292 |
+
evt.currentTarget.className += " active";
|
| 293 |
+
|
| 294 |
+
// Hide results on tab switch
|
| 295 |
+
singleResultDiv.innerHTML = '';
|
| 296 |
+
batchResultsDiv.innerHTML = '';
|
| 297 |
+
|
| 298 |
+
}
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
// --- SINGLE PREDICTION ---
|
| 302 |
+
predictSingleButton.addEventListener('click', async () => {
|
| 303 |
+
const name = singleNameInput.value.trim();
|
| 304 |
+
|
| 305 |
+
if (!name) {
|
| 306 |
+
showError("Please enter a name.");
|
| 307 |
+
return;
|
| 308 |
+
}
|
| 309 |
+
|
| 310 |
+
hideError();
|
| 311 |
+
showLoading(singleLoadingDiv); // Show single loading
|
| 312 |
+
singleResultDiv.innerHTML = ''; // Clear previous results
|
| 313 |
+
|
| 314 |
+
try {
|
| 315 |
+
const response = await fetch(`${API_BASE_URL}/predict_single?name=${encodeURIComponent(name)}&threshold=0.5`);
|
| 316 |
+
|
| 317 |
+
if (!response.ok) {
|
| 318 |
+
const errorData = await response.json();
|
| 319 |
+
showError(`Error: ${errorData.detail || 'An unexpected error occurred.'}`);
|
| 320 |
+
return;
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
const data = await response.json();
|
| 324 |
+
displaySingleResult(data);
|
| 325 |
+
} catch (error) {
|
| 326 |
+
showError("An error occurred during prediction. Please try again.");
|
| 327 |
+
console.error("Prediction error:", error);
|
| 328 |
+
} finally {
|
| 329 |
+
hideLoading(singleLoadingDiv); // Hide single loading
|
| 330 |
+
}
|
| 331 |
+
});
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
|
| 335 |
+
// --- BATCH PREDICTION ---
|
| 336 |
+
predictBatchButton.addEventListener('click', async () => {
|
| 337 |
+
const namesString = batchNamesInput.value.trim();
|
| 338 |
+
const names = namesString.split(',').map(name => name.trim()).filter(name => name !== "");
|
| 339 |
+
|
| 340 |
+
if (names.length === 0) {
|
| 341 |
+
showError("Please enter at least one valid name.");
|
| 342 |
+
return;
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
hideError();
|
| 346 |
+
showLoading(batchLoadingDiv); // Show batch loading
|
| 347 |
+
batchResultsDiv.innerHTML = ''; // Clear previous results.
|
| 348 |
+
|
| 349 |
+
try {
|
| 350 |
+
const response = await fetch(`${API_BASE_URL}/predict`, {
|
| 351 |
+
method: 'POST',
|
| 352 |
+
headers: {
|
| 353 |
+
'Content-Type': 'application/json'
|
| 354 |
+
},
|
| 355 |
+
body: JSON.stringify({ names: names, threshold: 0.5 })
|
| 356 |
+
});
|
| 357 |
+
|
| 358 |
+
if (!response.ok) {
|
| 359 |
+
const errorData = await response.json();
|
| 360 |
+
showError(`Error: ${errorData.detail || 'An unexpected error occurred.'}`);
|
| 361 |
+
return;
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
const data = await response.json();
|
| 365 |
+
displayBatchResults(data);
|
| 366 |
+
} catch (error) {
|
| 367 |
+
showError("An error occurred during prediction. Please try again.");
|
| 368 |
+
console.error("Prediction error:", error);
|
| 369 |
+
} finally {
|
| 370 |
+
hideLoading(batchLoadingDiv); // Hide batch loading
|
| 371 |
+
}
|
| 372 |
+
});
|
| 373 |
+
|
| 374 |
+
// --- DISPLAY RESULTS (REFACTORED) ---
|
| 375 |
+
function displaySingleResult(data) {
|
| 376 |
+
const resultHTML = `
|
| 377 |
+
<div class="result-item">
|
| 378 |
+
<div class="result-label">Name:</div>
|
| 379 |
+
<div>${data.name}</div>
|
| 380 |
+
</div>
|
| 381 |
+
<div class="result-item">
|
| 382 |
+
<div class="result-label">Predicted Gender:</div>
|
| 383 |
+
<div>${data.predicted_gender}</div>
|
| 384 |
+
</div>
|
| 385 |
+
<div class="result-item">
|
| 386 |
+
<div class="result-label">Male Probability:</div>
|
| 387 |
+
<div> ${data.male_probability.toFixed(2)}</div>
|
| 388 |
+
</div>
|
| 389 |
+
<div class="result-item">
|
| 390 |
+
<div class="result-label">Confidence:</div>
|
| 391 |
+
<div>${data.confidence.toFixed(2)}</div>
|
| 392 |
+
</div>
|
| 393 |
+
`;
|
| 394 |
+
singleResultDiv.innerHTML = resultHTML;
|
| 395 |
+
}
|
| 396 |
+
|
| 397 |
+
|
| 398 |
+
function displayBatchResults(data) {
|
| 399 |
+
let resultsHTML = '';
|
| 400 |
+
for (const prediction of data.predictions) {
|
| 401 |
+
resultsHTML += `
|
| 402 |
+
<div class="result-item">
|
| 403 |
+
<div class="result-label">Name:</div>
|
| 404 |
+
<div>${prediction.name}</div>
|
| 405 |
+
</div>
|
| 406 |
+
<div class="result-item">
|
| 407 |
+
<div class="result-label">Predicted Gender:</div>
|
| 408 |
+
<div>${prediction.predicted_gender}</div>
|
| 409 |
+
</div>
|
| 410 |
+
<div class="result-item">
|
| 411 |
+
<div class="result-label">Male Probability:</div>
|
| 412 |
+
<div> ${prediction.male_probability.toFixed(2)}</div>
|
| 413 |
+
</div>
|
| 414 |
+
<div class="result-item">
|
| 415 |
+
<div class="result-label">Confidence:</div>
|
| 416 |
+
<div>${prediction.confidence.toFixed(2)}</div>
|
| 417 |
+
</div>
|
| 418 |
+
`;
|
| 419 |
+
}
|
| 420 |
+
batchResultsDiv.innerHTML = resultsHTML;
|
| 421 |
+
}
|
| 422 |
+
|
| 423 |
+
|
| 424 |
+
|
| 425 |
+
// --- UTILITY FUNCTIONS ---
|
| 426 |
+
|
| 427 |
+
function showError(message) {
|
| 428 |
+
errorMessageDiv.textContent = message;
|
| 429 |
+
errorMessageDiv.style.display = 'block';
|
| 430 |
+
setTimeout(() => {
|
| 431 |
+
errorMessageDiv.style.display = 'none';
|
| 432 |
+
}, 5000);
|
| 433 |
+
}
|
| 434 |
+
|
| 435 |
+
function hideError() {
|
| 436 |
+
errorMessageDiv.style.display = 'none';
|
| 437 |
+
}
|
| 438 |
+
|
| 439 |
+
function showLoading(loadingIndicator) {
|
| 440 |
+
loadingIndicator.style.display = 'block';
|
| 441 |
+
}
|
| 442 |
+
|
| 443 |
+
function hideLoading(loadingIndicator) {
|
| 444 |
+
loadingIndicator.style.display = 'none';
|
| 445 |
+
}
|
| 446 |
+
</script>
|
| 447 |
+
</body>
|
| 448 |
+
</html>
|
home.html
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<!-- Start with lang="en" and no 'dark' class initially -->
|
| 3 |
+
<html lang="en" class="">
|
| 4 |
+
|
| 5 |
+
<head>
|
| 6 |
+
<meta charset="UTF-8">
|
| 7 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 8 |
+
<title>Vision and Discern - Home</title> <!-- Updated Title -->
|
| 9 |
+
|
| 10 |
+
<!-- Tailwind CSS via CDN -->
|
| 11 |
+
<script src="https://cdn.tailwindcss.com/3.4.1"></script>
|
| 12 |
+
<script>
|
| 13 |
+
tailwind.config = {
|
| 14 |
+
darkMode: 'class', // Enable class-based dark mode
|
| 15 |
+
theme: {
|
| 16 |
+
extend: {
|
| 17 |
+
colors: {
|
| 18 |
+
primary: '#4a90e2', // Match main app's primary color
|
| 19 |
+
secondary: '#f0f8ff',
|
| 20 |
+
},
|
| 21 |
+
borderRadius: {
|
| 22 |
+
'none': '0px', 'sm': '4px', DEFAULT: '8px', 'md': '12px',
|
| 23 |
+
'lg': '16px', 'xl': '20px', '2xl': '24px', '3xl': '32px',
|
| 24 |
+
'full': '9999px',
|
| 25 |
+
'button': '4px' // Match main app's button radius
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
}
|
| 29 |
+
}
|
| 30 |
+
</script>
|
| 31 |
+
<!-- Fonts -->
|
| 32 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 33 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 34 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
| 35 |
+
<!-- Icons (Remixicon) -->
|
| 36 |
+
<link href="https://cdn.jsdelivr.net/npm/remixicon@4.2.0/fonts/remixicon.css" rel="stylesheet">
|
| 37 |
+
|
| 38 |
+
<!-- Custom Styles (Mainly for the switch) -->
|
| 39 |
+
<style>
|
| 40 |
+
body { font-family: 'Inter', sans-serif; }
|
| 41 |
+
/* Custom Switch Styles - Copied from main app */
|
| 42 |
+
.custom-switch { position: relative; display: inline-block; width: 50px; height: 24px; }
|
| 43 |
+
.custom-switch-input { opacity: 0; width: 0; height: 0; }
|
| 44 |
+
.custom-switch-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 34px; }
|
| 45 |
+
.custom-switch-slider:before { position: absolute; content: ""; height: 18px; width: 18px; left: 3px; bottom: 3px; background-color: white; transition: .4s; border-radius: 50%; }
|
| 46 |
+
.custom-switch-input:checked + .custom-switch-slider { background-color: #4a90e2; } /* Your primary color */
|
| 47 |
+
.custom-switch-input:checked + .custom-switch-slider:before { transform: translateX(26px); }
|
| 48 |
+
|
| 49 |
+
/* Base dark mode styles - Copied from main app */
|
| 50 |
+
html.dark body { background-color: #111827; color: #d1d5db; }
|
| 51 |
+
html.dark header, html.dark footer { background-color: #1f2937; }
|
| 52 |
+
html.dark .card { background-color: #1f2937; border-color: #374151; }
|
| 53 |
+
html.dark h1, html.dark h2, html.dark h3, html.dark p, html.dark span, html.dark li, html.dark label, html.dark small, html.dark .subtitle, html.dark .info-text, html.dark .credits p, html.dark .attribution { color: #d1d5db; }
|
| 54 |
+
html.dark .text-gray-600 { color: #9ca3af; }
|
| 55 |
+
html.dark .text-gray-700 { color: #9ca3af; }
|
| 56 |
+
html.dark .text-gray-500 { color: #6b7280; }
|
| 57 |
+
html.dark .button-secondary { background-color: #4b5563; color: #d1d5db; }
|
| 58 |
+
html.dark .button-secondary:hover { background-color: #374151; }
|
| 59 |
+
</style>
|
| 60 |
+
</head>
|
| 61 |
+
|
| 62 |
+
<body class="bg-gray-100 dark:bg-gray-900 min-h-screen flex flex-col text-gray-900 dark:text-gray-200">
|
| 63 |
+
|
| 64 |
+
<!-- Main Application Container -->
|
| 65 |
+
<div id="app-container" class="flex-grow flex flex-col">
|
| 66 |
+
|
| 67 |
+
<!-- Header -->
|
| 68 |
+
<header class="bg-white dark:bg-gray-800 shadow-sm sticky top-0 z-50">
|
| 69 |
+
<div class="container mx-auto px-4 py-3 flex items-center justify-between">
|
| 70 |
+
<!-- Left Side: Logo & Nav -->
|
| 71 |
+
<div class="flex items-center">
|
| 72 |
+
<a href="home.html" class="text-xl font-bold text-primary dark:text-blue-400 mr-6">Vision & Discern</a>
|
| 73 |
+
<nav class="hidden md:flex space-x-6">
|
| 74 |
+
<!-- Set 'active' class for the current page -->
|
| 75 |
+
<a href="home.html" class="text-primary dark:text-blue-400 font-medium">Home</a>
|
| 76 |
+
<a href="index.html" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">OCR</a>
|
| 77 |
+
<a href="features.html" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Features</a>
|
| 78 |
+
<a href="feedback.html" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Feedback</a>
|
| 79 |
+
<a href="contact.html" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Contact Us</a>
|
| 80 |
+
</nav>
|
| 81 |
+
</div>
|
| 82 |
+
|
| 83 |
+
<!-- Right Side: Switches & Logout -->
|
| 84 |
+
<div class="flex items-center space-x-4">
|
| 85 |
+
<!-- Theme Switch -->
|
| 86 |
+
<div class="items-center space-x-2 hidden md:flex">
|
| 87 |
+
<span class="text-sm text-gray-600 dark:text-gray-400"><i class="ri-sun-line"></i></span>
|
| 88 |
+
<label class="custom-switch">
|
| 89 |
+
<input type="checkbox" id="themeToggle" class="custom-switch-input">
|
| 90 |
+
<span class="custom-switch-slider"></span>
|
| 91 |
+
</label>
|
| 92 |
+
<span class="text-sm text-gray-600 dark:text-gray-400"><i class="ri-moon-line"></i></span>
|
| 93 |
+
</div>
|
| 94 |
+
<!-- Logout Button -->
|
| 95 |
+
<button id="logoutButton" class="button-secondary bg-red-600 hover:bg-red-700 dark:bg-red-700 dark:hover:bg-red-800 text-white px-3 py-1.5 rounded-button text-sm inline-flex items-center gap-1">
|
| 96 |
+
<i class="ri-logout-box-r-line"></i>
|
| 97 |
+
Logout
|
| 98 |
+
</button>
|
| 99 |
+
<!-- Mobile Menu Button (Placeholder) -->
|
| 100 |
+
<button class="md:hidden w-10 h-10 flex items-center justify-center" aria-label="Toggle Menu">
|
| 101 |
+
<i class="ri-menu-line text-gray-600 dark:text-gray-300 text-xl"></i>
|
| 102 |
+
</button>
|
| 103 |
+
</div>
|
| 104 |
+
</div>
|
| 105 |
+
</header>
|
| 106 |
+
|
| 107 |
+
<!-- Main Content Area -->
|
| 108 |
+
<main class="flex-grow container mx-auto px-4 py-8">
|
| 109 |
+
|
| 110 |
+
<!-- Page Header Section -->
|
| 111 |
+
<section class="mb-10 text-center border-b border-gray-200 dark:border-gray-700 pb-6">
|
| 112 |
+
<h1 class="text-3xl md:text-4xl font-bold text-gray-900 dark:text-white mb-2">Hindi OCR Application</h1>
|
| 113 |
+
<p class="subtitle text-lg text-gray-700 dark:text-gray-300 mb-1">Convert Images to Hindi Text with Ease</p>
|
| 114 |
+
<p class="attribution text-sm text-gray-500 dark:text-gray-400">Powered by Sakshi's Hindi OCR Engine</p>
|
| 115 |
+
</section>
|
| 116 |
+
|
| 117 |
+
<!-- Main Content Grid -->
|
| 118 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
| 119 |
+
|
| 120 |
+
<!-- Welcome Section -->
|
| 121 |
+
<div class="card bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md border border-gray-100 dark:border-gray-700">
|
| 122 |
+
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-4 flex items-center gap-2">
|
| 123 |
+
<i class="ri-sparkling-2-line text-primary dark:text-blue-400"></i>
|
| 124 |
+
Welcome!
|
| 125 |
+
</h2>
|
| 126 |
+
<p class="text-gray-700 dark:text-gray-300 mb-4">
|
| 127 |
+
Welcome to the Hindi OCR application. This tool allows you to easily extract text from images containing Hindi script.
|
| 128 |
+
</p>
|
| 129 |
+
<p class="text-gray-700 dark:text-gray-300">
|
| 130 |
+
Navigate to the <a href="index.html" class="text-primary dark:text-blue-400 hover:underline font-medium">OCR</a> page to start converting your images.
|
| 131 |
+
</p>
|
| 132 |
+
<div class="mt-6 flex justify-center">
|
| 133 |
+
<img src="placeholder.svg" alt="Illustration showing OCR process" class="h-40 w-auto text-gray-400 dark:text-gray-600 border border-dashed rounded p-4" />
|
| 134 |
+
<!-- Replace placeholder.svg with a relevant image or SVG if available -->
|
| 135 |
+
</div>
|
| 136 |
+
</div>
|
| 137 |
+
|
| 138 |
+
<!-- How to Use Section -->
|
| 139 |
+
<div class="card bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md border border-gray-100 dark:border-gray-700">
|
| 140 |
+
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-4 flex items-center gap-2">
|
| 141 |
+
<i class="ri-question-line text-primary dark:text-blue-400"></i>
|
| 142 |
+
How to Use
|
| 143 |
+
</h2>
|
| 144 |
+
<ol class="list-decimal list-inside space-y-3 text-gray-700 dark:text-gray-300">
|
| 145 |
+
<li class="flex items-start gap-2">
|
| 146 |
+
<i class="ri-user-add-line mt-1 text-primary dark:text-blue-400"></i>
|
| 147 |
+
<span>Register or Login with your credentials.</span>
|
| 148 |
+
</li>
|
| 149 |
+
<li class="flex items-start gap-2">
|
| 150 |
+
<i class="ri-computer-line mt-1 text-primary dark:text-blue-400"></i>
|
| 151 |
+
<span>Navigate to the <a href="index.html" class="text-primary dark:text-blue-400 hover:underline font-medium">OCR tab</a>.</span>
|
| 152 |
+
</li>
|
| 153 |
+
<li class="flex items-start gap-2">
|
| 154 |
+
<i class="ri-upload-2-line mt-1 text-primary dark:text-blue-400"></i>
|
| 155 |
+
<span>Click the upload area or drag & drop an image containing Hindi text. You can also select a sample image.</span>
|
| 156 |
+
</li>
|
| 157 |
+
<li class="flex items-start gap-2">
|
| 158 |
+
<i class="ri-camera-lens-line mt-1 text-primary dark:text-blue-400"></i>
|
| 159 |
+
<span>Press the <strong>Process Image</strong> button.</span>
|
| 160 |
+
</li>
|
| 161 |
+
<li class="flex items-start gap-2">
|
| 162 |
+
<i class="ri-timer-line mt-1 text-primary dark:text-blue-400"></i>
|
| 163 |
+
<span>Wait briefly for the AI to analyze and extract the text.</span>
|
| 164 |
+
</li>
|
| 165 |
+
<li class="flex items-start gap-2">
|
| 166 |
+
<i class="ri-file-copy-line mt-1 text-primary dark:text-blue-400"></i>
|
| 167 |
+
<span>View and copy the extracted text from the results area.</span>
|
| 168 |
+
</li>
|
| 169 |
+
<li class="flex items-start gap-2">
|
| 170 |
+
<i class="ri-translate-2 mt-1 text-primary dark:text-blue-400"></i>
|
| 171 |
+
<span>Optionally, use the <strong>Translation</strong> tab to translate the extracted text.</span>
|
| 172 |
+
</li>
|
| 173 |
+
<li class="flex items-start gap-2">
|
| 174 |
+
<i class="ri-user-search-line mt-1 text-primary dark:text-blue-400"></i>
|
| 175 |
+
<span>Explore the <strong>Gender Prediction</strong> tab for name analysis.</span>
|
| 176 |
+
</li>
|
| 177 |
+
<li class="flex items-start gap-2">
|
| 178 |
+
<i class="ri-feedback-line mt-1 text-primary dark:text-blue-400"></i>
|
| 179 |
+
<span>Use the <a href="feedback.html" class="text-primary dark:text-blue-400 hover:underline font-medium">Feedback</a> link to share your thoughts.</span>
|
| 180 |
+
</li>
|
| 181 |
+
</ol>
|
| 182 |
+
</div>
|
| 183 |
+
|
| 184 |
+
</div> <!-- End Grid -->
|
| 185 |
+
|
| 186 |
+
</main>
|
| 187 |
+
|
| 188 |
+
<!-- Footer -->
|
| 189 |
+
<footer class="bg-gray-800 text-gray-400 py-8 mt-12">
|
| 190 |
+
<div class="container mx-auto px-4 text-center">
|
| 191 |
+
<div class="credits text-sm mb-4">
|
| 192 |
+
<p>Powered by <strong>D SAKSHI</strong> (MCA Final Year BIT Durg, Chhattisgarh) | © SlimShadow Org. All Rights Reserved.</p>
|
| 193 |
+
</div>
|
| 194 |
+
<div class="flex justify-center space-x-4">
|
| 195 |
+
<a href="#" class="hover:text-white" title="GitHub (Placeholder)"><i class="ri-github-fill"></i></a>
|
| 196 |
+
<a href="#" class="hover:text-white" title="LinkedIn (Placeholder)"><i class="ri-linkedin-box-fill"></i></a>
|
| 197 |
+
</div>
|
| 198 |
+
</div>
|
| 199 |
+
</footer>
|
| 200 |
+
|
| 201 |
+
</div> <!-- End #app-container -->
|
| 202 |
+
|
| 203 |
+
<script>
|
| 204 |
+
// --- THEME TOGGLE LOGIC ---
|
| 205 |
+
const themeToggle = document.getElementById('themeToggle');
|
| 206 |
+
const htmlElement = document.documentElement;
|
| 207 |
+
|
| 208 |
+
function applyTheme(isDark) {
|
| 209 |
+
if (isDark) {
|
| 210 |
+
htmlElement.classList.add('dark');
|
| 211 |
+
if (themeToggle) themeToggle.checked = true;
|
| 212 |
+
} else {
|
| 213 |
+
htmlElement.classList.remove('dark');
|
| 214 |
+
if (themeToggle) themeToggle.checked = false;
|
| 215 |
+
}
|
| 216 |
+
}
|
| 217 |
+
// Check localStorage on load
|
| 218 |
+
const prefersDark = localStorage.getItem('theme') === 'dark' ||
|
| 219 |
+
(localStorage.getItem('theme') === null && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
| 220 |
+
applyTheme(prefersDark);
|
| 221 |
+
|
| 222 |
+
// Add listener to toggle button
|
| 223 |
+
if (themeToggle) {
|
| 224 |
+
themeToggle.addEventListener('change', (event) => {
|
| 225 |
+
const isDark = event.target.checked;
|
| 226 |
+
applyTheme(isDark);
|
| 227 |
+
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
| 228 |
+
});
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
// --- Logout Button Logic ---
|
| 232 |
+
const logoutButton = document.getElementById("logoutButton");
|
| 233 |
+
if (logoutButton) {
|
| 234 |
+
logoutButton.addEventListener("click", function() {
|
| 235 |
+
// TODO: Add actual logout logic here if needed (e.g., clear tokens)
|
| 236 |
+
console.log("Logout clicked, redirecting...");
|
| 237 |
+
// Redirect to index.html (which should show the login screen if not authenticated)
|
| 238 |
+
window.location.href = "index.html";
|
| 239 |
+
});
|
| 240 |
+
} else {
|
| 241 |
+
console.warn("Logout button not found.");
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
</script>
|
| 245 |
+
|
| 246 |
+
</body>
|
| 247 |
+
</html>
|
images/example-1.jpg
ADDED
|
images//340/244/205/340/244/202/340/244/244/340/244/260/340/245/215/340/244/260/340/244/276/340/244/267/340/245/215/340/244/237/340/245/215/340/244/260/340/245/200/340/244/257.png
ADDED
|
images//340/244/205/340/244/227/340/245/215/340/244/260/340/244/270/340/244/260.png
ADDED
|
images//340/244/205/340/244/247/340/245/215/340/244/257/340/244/257/340/244/250.png
ADDED
|
images//340/244/205/340/244/250/340/245/201/340/244/270/340/244/202/340/244/247/340/244/276/340/244/250.png
ADDED
|
index.html
CHANGED
|
@@ -1,19 +1,1114 @@
|
|
| 1 |
-
<!
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<!-- Start with lang="en" and no 'dark' class initially -->
|
| 3 |
+
<html lang="en" class="">
|
| 4 |
+
|
| 5 |
+
<head>
|
| 6 |
+
<meta charset="UTF-8">
|
| 7 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 8 |
+
<title>Vision and Discern - AI Text Analysis</title>
|
| 9 |
+
|
| 10 |
+
<!-- Tailwind CSS via CDN -->
|
| 11 |
+
<script src="https://cdn.tailwindcss.com/3.4.1"></script>
|
| 12 |
+
<script>
|
| 13 |
+
tailwind.config = {
|
| 14 |
+
darkMode: 'class', // Enable class-based dark mode
|
| 15 |
+
theme: {
|
| 16 |
+
extend: {
|
| 17 |
+
colors: {
|
| 18 |
+
primary: '#4a90e2', // Adjusted primary to match old button color
|
| 19 |
+
// primary: '#1a73e8', // Or use the NIC project's primary
|
| 20 |
+
secondary: '#f0f8ff', // Light blueish background
|
| 21 |
+
// Define dark mode colors or rely on Tailwind's defaults
|
| 22 |
+
},
|
| 23 |
+
borderRadius: {
|
| 24 |
+
'none': '0px', 'sm': '4px', DEFAULT: '8px', 'md': '12px',
|
| 25 |
+
'lg': '16px', 'xl': '20px', '2xl': '24px', '3xl': '32px',
|
| 26 |
+
'full': '9999px',
|
| 27 |
+
'button': '4px' // Match old button radius
|
| 28 |
+
// 'button': '8px' // Or use the NIC project's button radius
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
}
|
| 33 |
+
</script>
|
| 34 |
+
<!-- Fonts -->
|
| 35 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 36 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 37 |
+
<!-- Using Inter like the NIC project, add Noto Sans Devanagari if needed for specific Hindi text -->
|
| 38 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Noto+Sans+Devanagari:wght@400;700&display=swap" rel="stylesheet">
|
| 39 |
+
<!-- Icons (Remixicon like the NIC project) -->
|
| 40 |
+
<link href="https://cdn.jsdelivr.net/npm/remixicon@4.2.0/fonts/remixicon.css" rel="stylesheet">
|
| 41 |
+
|
| 42 |
+
<!-- Custom Styles (Mainly for the switch, keep this) -->
|
| 43 |
+
<style>
|
| 44 |
+
body { font-family: 'Inter', sans-serif; }
|
| 45 |
+
.hindi-font { font-family: 'Noto Sans Devanagari', sans-serif; } /* Add if displaying Hindi */
|
| 46 |
+
|
| 47 |
+
/* Custom Switch Styles - Keep As Is from NIC Project */
|
| 48 |
+
.custom-switch { position: relative; display: inline-block; width: 50px; height: 24px; }
|
| 49 |
+
.custom-switch-input { opacity: 0; width: 0; height: 0; }
|
| 50 |
+
.custom-switch-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 34px; }
|
| 51 |
+
.custom-switch-slider:before { position: absolute; content: ""; height: 18px; width: 18px; left: 3px; bottom: 3px; background-color: white; transition: .4s; border-radius: 50%; }
|
| 52 |
+
/* Use the primary color from Tailwind config if possible, or hardcode */
|
| 53 |
+
.custom-switch-input:checked + .custom-switch-slider { background-color: #4a90e2; } /* Your primary */
|
| 54 |
+
/* .custom-switch-input:checked + .custom-switch-slider { background-color: #1a73e8; } /* NIC primary */
|
| 55 |
+
.custom-switch-input:checked + .custom-switch-slider:before { transform: translateX(26px); }
|
| 56 |
+
|
| 57 |
+
/* Base dark mode styles adapted from NIC project */
|
| 58 |
+
html.dark body {
|
| 59 |
+
background-color: #111827; /* Tailwind gray-900 */
|
| 60 |
+
color: #d1d5db; /* Tailwind gray-300 */
|
| 61 |
+
}
|
| 62 |
+
html.dark header, html.dark footer {
|
| 63 |
+
background-color: #1f2937; /* Tailwind gray-800 */
|
| 64 |
+
}
|
| 65 |
+
html.dark .tab-container {
|
| 66 |
+
border-bottom-color: #4b5563; /* gray-600 */
|
| 67 |
+
}
|
| 68 |
+
html.dark .tab-button {
|
| 69 |
+
color: #9ca3af; /* gray-400 */
|
| 70 |
+
}
|
| 71 |
+
html.dark .tab-button.active {
|
| 72 |
+
color: #60a5fa; /* blue-400 */
|
| 73 |
+
border-bottom-color: #60a5fa; /* blue-400 */
|
| 74 |
+
}
|
| 75 |
+
html.dark .card,
|
| 76 |
+
html.dark .auth-card,
|
| 77 |
+
html.dark .results {
|
| 78 |
+
background-color: #1f2937; /* gray-800 */
|
| 79 |
+
border-color: #374151; /* gray-700 */
|
| 80 |
+
}
|
| 81 |
+
/* Adjust general text elements */
|
| 82 |
+
html.dark h1, html.dark h2, html.dark h3, html.dark p, html.dark span, html.dark li, html.dark label, html.dark small, html.dark .subtitle, html.dark .info-text, html.dark .credits p {
|
| 83 |
+
color: #d1d5db; /* gray-300 */
|
| 84 |
+
}
|
| 85 |
+
/* Adjust specific text colors */
|
| 86 |
+
html.dark .text-gray-600 { color: #9ca3af; /* gray-400 */ }
|
| 87 |
+
html.dark .text-gray-500 { color: #6b7280; /* gray-500 */ }
|
| 88 |
+
html.dark .text-gray-700 { color: #9ca3af; /* gray-400 */ }
|
| 89 |
+
|
| 90 |
+
/* Input/Textarea dark styles */
|
| 91 |
+
html.dark input[type="text"],
|
| 92 |
+
html.dark input[type="password"],
|
| 93 |
+
html.dark input[type="email"],
|
| 94 |
+
html.dark textarea {
|
| 95 |
+
background-color: #374151; /* gray-700 */
|
| 96 |
+
border-color: #4b5563; /* gray-600 */
|
| 97 |
+
color: #d1d5db; /* gray-300 */
|
| 98 |
+
}
|
| 99 |
+
html.dark input::placeholder,
|
| 100 |
+
html.dark textarea::placeholder {
|
| 101 |
+
color: #9ca3af; /* gray-400 */
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
/* Button dark styles */
|
| 105 |
+
html.dark .button-secondary {
|
| 106 |
+
background-color: #4b5563; /* gray-600 */
|
| 107 |
+
color: #d1d5db; /* gray-300 */
|
| 108 |
+
}
|
| 109 |
+
html.dark .button-secondary:hover {
|
| 110 |
+
background-color: #374151; /* gray-700 */
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
/* Error message dark styles */
|
| 114 |
+
html.dark .error-message {
|
| 115 |
+
background-color: #450a0a; /* red-950 */
|
| 116 |
+
color: #fecaca; /* red-200 */
|
| 117 |
+
border-color: #7f1d1d; /* red-800 */
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
/* Upload area dark */
|
| 121 |
+
html.dark .upload-area {
|
| 122 |
+
border-color: #4b5563; /* gray-600 */
|
| 123 |
+
color: #9ca3af; /* gray-400 */
|
| 124 |
+
}
|
| 125 |
+
html.dark .upload-area.dragover {
|
| 126 |
+
border-color: #60a5fa; /* blue-400 */
|
| 127 |
+
background-color: #1e3a8a; /* blue-900 */
|
| 128 |
+
}
|
| 129 |
+
/* Image borders dark */
|
| 130 |
+
html.dark #imagePreview,
|
| 131 |
+
html.dark .sample-image,
|
| 132 |
+
html.dark .result-image {
|
| 133 |
+
border-color: #4b5563; /* gray-600 */
|
| 134 |
+
}
|
| 135 |
+
/* Results dark */
|
| 136 |
+
html.dark .ocr-result {
|
| 137 |
+
background-color: #374151; /* gray-700 */
|
| 138 |
+
border-color: #4b5563; /* gray-600 */
|
| 139 |
+
}
|
| 140 |
+
html.dark .result-content {
|
| 141 |
+
background-color: #1f2937; /* gray-800 */
|
| 142 |
+
border-color: #4b5563; /* gray-600 */
|
| 143 |
+
}
|
| 144 |
+
html.dark .result-item {
|
| 145 |
+
border-bottom-color: #4b5563; /* gray-600 */
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
</style>
|
| 150 |
+
</head>
|
| 151 |
+
|
| 152 |
+
<!-- Add dark mode background/text for body base -->
|
| 153 |
+
<body class="bg-gray-100 dark:bg-gray-900 min-h-screen flex flex-col text-gray-900 dark:text-gray-200">
|
| 154 |
+
|
| 155 |
+
<!-- Login Section - Styled with Tailwind -->
|
| 156 |
+
<div id="login-container" class="min-h-screen flex items-center justify-center p-4">
|
| 157 |
+
<!-- Login Card -->
|
| 158 |
+
<div class="auth-card bg-white dark:bg-gray-800 p-6 md:p-8 rounded-lg shadow-lg text-center w-full max-w-sm border dark:border-gray-700">
|
| 159 |
+
<h1 class="text-2xl font-bold mb-2 text-primary dark:text-blue-400">Vision and Discern</h1>
|
| 160 |
+
<h2 class="text-xl font-semibold mb-3">Welcome!</h2>
|
| 161 |
+
<p class="text-gray-600 dark:text-gray-400 mb-6 text-sm">Login to access the applications.</p>
|
| 162 |
+
<div class="error-message bg-red-100 border border-red-300 text-red-800 dark:bg-red-900 dark:bg-opacity-50 dark:border-red-600 dark:text-red-200 px-4 py-2 rounded-md text-sm mb-4 hidden" id="loginErrorMessage"></div>
|
| 163 |
+
<input type="text" id="username" placeholder="Username" class="w-full px-4 py-2 mb-4 border border-gray-300 rounded-button focus:outline-none focus:ring-2 focus:ring-primary/50 dark:bg-gray-700 dark:border-gray-600 dark:text-gray-200">
|
| 164 |
+
<input type="password" id="password" placeholder="Password" class="w-full px-4 py-2 mb-4 border border-gray-300 rounded-button focus:outline-none focus:ring-2 focus:ring-primary/50 dark:bg-gray-700 dark:border-gray-600 dark:text-gray-200">
|
| 165 |
+
<button id="loginButton" class="w-full bg-primary text-white px-4 py-2 rounded-button hover:bg-blue-700 transition-colors font-medium disabled:opacity-50">Login</button>
|
| 166 |
+
<div class="mt-4 text-sm text-gray-600 dark:text-gray-400">
|
| 167 |
+
Don't have an account? <a href="#" id="signup-link" class="text-primary dark:text-blue-400 hover:underline">Sign up</a>
|
| 168 |
+
</div>
|
| 169 |
+
</div>
|
| 170 |
+
|
| 171 |
+
<!-- Signup Card -->
|
| 172 |
+
<div class="auth-card bg-white dark:bg-gray-800 p-6 md:p-8 rounded-lg shadow-lg text-center w-full max-w-sm border dark:border-gray-700 hidden" id="signup-card">
|
| 173 |
+
<h1 class="text-2xl font-bold mb-2 text-primary dark:text-blue-400">Vision and Discern</h1>
|
| 174 |
+
<h2 class="text-xl font-semibold mb-3">Create Account</h2>
|
| 175 |
+
<p class="text-gray-600 dark:text-gray-400 mb-6 text-sm">Sign up to start using applications.</p>
|
| 176 |
+
<div class="error-message bg-red-100 border border-red-300 text-red-800 dark:bg-red-900 dark:bg-opacity-50 dark:border-red-600 dark:text-red-200 px-4 py-2 rounded-md text-sm mb-4 hidden" id="signupErrorMessage"></div>
|
| 177 |
+
<input type="text" id="signupUsername" placeholder="Username" class="w-full px-4 py-2 mb-4 border border-gray-300 rounded-button focus:outline-none focus:ring-2 focus:ring-primary/50 dark:bg-gray-700 dark:border-gray-600 dark:text-gray-200">
|
| 178 |
+
<input type="email" id="signupEmail" placeholder="Email" class="w-full px-4 py-2 mb-4 border border-gray-300 rounded-button focus:outline-none focus:ring-2 focus:ring-primary/50 dark:bg-gray-700 dark:border-gray-600 dark:text-gray-200">
|
| 179 |
+
<input type="password" id="signupPassword" placeholder="Password" class="w-full px-4 py-2 mb-4 border border-gray-300 rounded-button focus:outline-none focus:ring-2 focus:ring-primary/50 dark:bg-gray-700 dark:border-gray-600 dark:text-gray-200">
|
| 180 |
+
<button id="signupButton" class="w-full bg-primary text-white px-4 py-2 rounded-button hover:bg-blue-700 transition-colors font-medium disabled:opacity-50">Sign Up</button>
|
| 181 |
+
<div class="mt-4 text-sm text-gray-600 dark:text-gray-400">
|
| 182 |
+
Already have an account? <a href="#" id="login-link" class="text-primary dark:text-blue-400 hover:underline">Login</a>
|
| 183 |
+
</div>
|
| 184 |
+
</div>
|
| 185 |
+
</div>
|
| 186 |
+
|
| 187 |
+
<!-- Main Application Container (Initially Hidden) -->
|
| 188 |
+
<div id="app-container" class="hidden flex-grow flex flex-col">
|
| 189 |
+
|
| 190 |
+
<!-- Header -->
|
| 191 |
+
<header class="bg-white dark:bg-gray-800 shadow-sm sticky top-0 z-50">
|
| 192 |
+
<div class="container mx-auto px-4 py-3 flex items-center justify-between">
|
| 193 |
+
<!-- Left Side: Logo & Nav -->
|
| 194 |
+
<div class="flex items-center">
|
| 195 |
+
<a href="#" class="text-xl font-bold text-primary dark:text-blue-400 mr-6">Vision & Discern</a>
|
| 196 |
+
<nav class="hidden md:flex space-x-6">
|
| 197 |
+
<a href="home.html" target="_blank" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Home</a>
|
| 198 |
+
<a href="features.html" target="_blank" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Key Features</a>
|
| 199 |
+
<a href="feedback.html" target="_blank" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Feedback</a>
|
| 200 |
+
<a href="contact.html" target="_blank" class="text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-blue-400">Contact Us</a>
|
| 201 |
+
</nav>
|
| 202 |
+
</div>
|
| 203 |
+
|
| 204 |
+
<!-- Right Side: Switches & Logout -->
|
| 205 |
+
<div class="flex items-center space-x-4">
|
| 206 |
+
<!-- Language Switch (Placeholder Structure) -->
|
| 207 |
+
<div class="items-center space-x-2 hidden md:flex">
|
| 208 |
+
<span class="text-sm text-gray-600 dark:text-gray-400">EN</span>
|
| 209 |
+
<label class="custom-switch">
|
| 210 |
+
<input type="checkbox" id="languageToggle" class="custom-switch-input" disabled> <!-- Disabled for now -->
|
| 211 |
+
<span class="custom-switch-slider"></span>
|
| 212 |
+
</label>
|
| 213 |
+
<span class="text-sm text-gray-600 dark:text-gray-400 hindi-font">हिंदी</span>
|
| 214 |
+
</div>
|
| 215 |
+
<!-- Theme Switch -->
|
| 216 |
+
<div class="items-center space-x-2 hidden md:flex">
|
| 217 |
+
<span class="text-sm text-gray-600 dark:text-gray-400"><i class="ri-sun-line"></i></span>
|
| 218 |
+
<label class="custom-switch">
|
| 219 |
+
<input type="checkbox" id="themeToggle" class="custom-switch-input">
|
| 220 |
+
<span class="custom-switch-slider"></span>
|
| 221 |
+
</label>
|
| 222 |
+
<span class="text-sm text-gray-600 dark:text-gray-400"><i class="ri-moon-line"></i></span>
|
| 223 |
+
</div>
|
| 224 |
+
<!-- Logout Button -->
|
| 225 |
+
<button id="logoutButton" class="button-secondary bg-red-600 hover:bg-red-700 dark:bg-red-700 dark:hover:bg-red-800 text-white px-3 py-1.5 rounded-button text-sm inline-flex items-center">
|
| 226 |
+
<i class="ri-logout-box-r-line mr-1"></i>
|
| 227 |
+
Logout
|
| 228 |
+
</button>
|
| 229 |
+
<!-- Mobile Menu Button (Placeholder) -->
|
| 230 |
+
<button class="md:hidden w-10 h-10 flex items-center justify-center" aria-label="Toggle Menu">
|
| 231 |
+
<i class="ri-menu-line text-gray-600 dark:text-gray-300 text-xl"></i>
|
| 232 |
+
</button>
|
| 233 |
+
</div>
|
| 234 |
+
</div>
|
| 235 |
+
</header>
|
| 236 |
+
|
| 237 |
+
<!-- Main Content Area -->
|
| 238 |
+
<main class="flex-grow container mx-auto px-4 py-8">
|
| 239 |
+
|
| 240 |
+
<!-- App Header Section -->
|
| 241 |
+
<section class="mb-8 text-center">
|
| 242 |
+
<h1 class="text-3xl md:text-4xl font-bold text-gray-900 dark:text-white mb-3">Comprehensive AI-Powered Text Analysis</h1>
|
| 243 |
+
<p class="subtitle text-lg text-gray-700 dark:text-gray-300 max-w-3xl mx-auto">
|
| 244 |
+
Leverage advanced AI to extract Hindi text from images, translate it, and predict gender from names.
|
| 245 |
+
</p>
|
| 246 |
+
</section>
|
| 247 |
+
|
| 248 |
+
<!-- Tab Navigation - Styled with Tailwind -->
|
| 249 |
+
<div class="tab-container flex justify-center border-b border-gray-200 dark:border-gray-700 mb-6">
|
| 250 |
+
<!-- Added Icons to Tabs -->
|
| 251 |
+
<button class="tab-button flex items-center gap-2 px-6 py-3 text-gray-600 dark:text-gray-300 border-b-2 border-transparent hover:text-primary dark:hover:text-blue-400 transition-colors active" data-tab="ocr">
|
| 252 |
+
<i class="ri-image-line"></i> OCR
|
| 253 |
+
</button>
|
| 254 |
+
<button class="tab-button flex items-center gap-2 px-6 py-3 text-gray-600 dark:text-gray-300 border-b-2 border-transparent hover:text-primary dark:hover:text-blue-400 transition-colors" data-tab="translation">
|
| 255 |
+
<i class="ri-translate-2"></i> Translation
|
| 256 |
+
</button>
|
| 257 |
+
<button class="tab-button flex items-center gap-2 px-6 py-3 text-gray-600 dark:text-gray-300 border-b-2 border-transparent hover:text-primary dark:hover:text-blue-400 transition-colors" data-tab="gender">
|
| 258 |
+
<i class="ri-men-line"></i><i class="ri-women-line"></i> Gender
|
| 259 |
+
</button>
|
| 260 |
+
</div>
|
| 261 |
+
|
| 262 |
+
<!-- Tab Content Sections -->
|
| 263 |
+
<div>
|
| 264 |
+
<!-- OCR Tab Content -->
|
| 265 |
+
<div class="tab-content active" id="ocr">
|
| 266 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
| 267 |
+
|
| 268 |
+
<!-- Input Card -->
|
| 269 |
+
<div class="card bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md border border-gray-100 dark:border-gray-700">
|
| 270 |
+
<h2 class="card-title text-xl font-semibold text-gray-800 dark:text-white mb-4 border-b border-gray-200 dark:border-gray-600 pb-2 flex items-center">
|
| 271 |
+
<i class="ri-upload-cloud-2-line mr-2 text-primary dark:text-blue-400"></i>
|
| 272 |
+
Upload Image
|
| 273 |
+
</h2>
|
| 274 |
+
<div id="uploadArea" class="upload-area border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg p-6 text-center cursor-pointer hover:border-primary dark:hover:border-blue-400 mb-4 transition-colors">
|
| 275 |
+
<div class="upload-icon text-4xl text-gray-400 dark:text-gray-500 mb-2"><i class="ri-image-add-line"></i></div>
|
| 276 |
+
<p class="text-gray-700 dark:text-gray-300">Click to select or drag & drop</p>
|
| 277 |
+
<p class="info-text text-sm text-gray-500 dark:text-gray-400">PNG, JPG, JPEG</p>
|
| 278 |
+
</div>
|
| 279 |
+
<input type="file" id="fileInput" accept="image/png, image/jpeg, image/jpg" class="hidden"/>
|
| 280 |
+
|
| 281 |
+
<!-- Sample Images Section -->
|
| 282 |
+
<div class="sample-images-container mt-4 pt-4 border-t border-gray-200 dark:border-gray-600">
|
| 283 |
+
<h3 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Or try a sample image:</h3>
|
| 284 |
+
<div class="flex flex-wrap gap-2">
|
| 285 |
+
<img src="https://raw.githubusercontent.com/sameer-banchhor-git/data-sakshi/refs/heads/main/%E0%A4%B9%E0%A5%89%E0%A4%B8%E0%A5%8D%E0%A4%9F%E0%A4%B2.png"
|
| 286 |
+
data-src="https://raw.githubusercontent.com/sameer-banchhor-git/data-sakshi/refs/heads/main/%E0%A4%B9%E0%A5%89%E0%A4%B8%E0%A5%8D%E0%A4%9F%E0%A4%B2.png"
|
| 287 |
+
alt="Sample Hostel Image" class="sample-image h-16 w-auto border border-gray-300 dark:border-gray-600 rounded-sm cursor-pointer hover:scale-105 hover:shadow-md transition-transform" title="Load Hostel Sample">
|
| 288 |
+
<img src="https://raw.githubusercontent.com/sameer-banchhor-git/data-sakshi/refs/heads/main/%E0%A4%B8%E0%A5%8D%E0%A4%B5%E0%A4%BE%E0%A4%B8%E0%A5%8D%E0%A4%A5%E0%A5%8D%E0%A4%AF.png"
|
| 289 |
+
data-src="https://raw.githubusercontent.com/sameer-banchhor-git/data-sakshi/refs/heads/main/%E0%A4%B8%E0%A5%8D%E0%A4%B5%E0%A4%BE%E0%A4%B8%E0%A5%8D%E0%A4%A5%E0%A5%8D%E0%A4%AF.png"
|
| 290 |
+
alt="Sample Swasthya Image" class="sample-image h-16 w-auto border border-gray-300 dark:border-gray-600 rounded-sm cursor-pointer hover:scale-105 hover:shadow-md transition-transform" title="Load Swasthya Sample">
|
| 291 |
+
<img src="https://raw.githubusercontent.com/sameer-banchhor-git/data-sakshi/refs/heads/main/%E0%A4%B9%E0%A5%81%E0%A4%88.png"
|
| 292 |
+
data-src="https://raw.githubusercontent.com/sameer-banchhor-git/data-sakshi/refs/heads/main/%E0%A4%B9%E0%A5%81%E0%A4%88.png"
|
| 293 |
+
alt="Sample Hui Image" class="sample-image h-16 w-auto border border-gray-300 dark:border-gray-600 rounded-sm cursor-pointer hover:scale-105 hover:shadow-md transition-transform" title="Load Hui Sample">
|
| 294 |
+
</div>
|
| 295 |
+
</div>
|
| 296 |
+
|
| 297 |
+
<!-- Preview Area -->
|
| 298 |
+
<div class="mt-4 pt-4 border-t border-gray-200 dark:border-gray-600">
|
| 299 |
+
<h3 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 text-center">Preview</h3>
|
| 300 |
+
<div class="flex justify-center">
|
| 301 |
+
<img id="imagePreview" src="#" alt="Image Preview" class="hidden max-h-48 w-auto rounded border bg-gray-50 dark:bg-gray-700 dark:border-gray-600 p-1"/>
|
| 302 |
+
</div>
|
| 303 |
+
</div>
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
<!-- Action Buttons -->
|
| 307 |
+
<div class="mt-6 flex gap-3">
|
| 308 |
+
<button id="processButton" disabled class="flex-1 bg-green-600 text-white px-4 py-2 rounded-button font-medium hover:bg-green-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center justify-center gap-2">
|
| 309 |
+
<i class="ri-camera-lens-line"></i> Process
|
| 310 |
+
</button>
|
| 311 |
+
<button id="clearButton" class="button-secondary flex-1 bg-gray-300 text-gray-800 px-4 py-2 rounded-button hover:bg-gray-400 dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500 transition-colors inline-flex items-center justify-center gap-2 hidden">
|
| 312 |
+
<i class="ri-delete-bin-line"></i> Clear
|
| 313 |
+
</button>
|
| 314 |
+
</div>
|
| 315 |
+
<div class="error-message bg-red-100 border border-red-300 text-red-800 dark:bg-red-900 dark:bg-opacity-50 dark:border-red-600 dark:text-red-200 px-4 py-2 rounded-md text-sm mt-4 hidden" id="ocrErrorMessage"></div>
|
| 316 |
+
</div>
|
| 317 |
+
|
| 318 |
+
<!-- Results Card -->
|
| 319 |
+
<div class="card bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md border border-gray-100 dark:border-gray-700 flex flex-col items-center justify-center">
|
| 320 |
+
<h2 class="card-title text-xl font-semibold text-gray-800 dark:text-white mb-4 border-b border-gray-200 dark:border-gray-600 pb-2 w-full text-center flex items-center justify-center">
|
| 321 |
+
<i class="ri-file-text-line mr-2 text-primary dark:text-blue-400"></i>
|
| 322 |
+
Results
|
| 323 |
+
</h2>
|
| 324 |
+
|
| 325 |
+
<!-- Loading Indicator -->
|
| 326 |
+
<div id="loadingSpinner" class="flex flex-col items-center justify-center my-4 text-primary dark:text-blue-400 hidden">
|
| 327 |
+
<svg class="animate-spin h-8 w-8 text-primary dark:text-blue-400 mb-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
| 328 |
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
| 329 |
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
| 330 |
+
</svg>
|
| 331 |
+
<p class="text-sm">Processing your image...</p>
|
| 332 |
+
</div>
|
| 333 |
+
|
| 334 |
+
<!-- Results Display Area -->
|
| 335 |
+
<div id="resultsSection" class="w-full space-y-6 hidden">
|
| 336 |
+
<!-- Combined OCR Output Card -->
|
| 337 |
+
<div class="ocr-result bg-gray-50 dark:bg-gray-700 p-4 rounded-md border border-gray-200 dark:border-gray-600">
|
| 338 |
+
<div class="result-title text-md font-semibold text-gray-800 dark:text-white mb-2">OCR Text Output</div>
|
| 339 |
+
<div class="result-content bg-white dark:bg-gray-800 p-3 rounded text-lg font-medium min-h-[50px] border border-gray-200 dark:border-gray-600" id="ocrOutput">
|
| 340 |
+
<!-- Text will appear here -->
|
| 341 |
+
</div>
|
| 342 |
+
</div>
|
| 343 |
+
|
| 344 |
+
<!-- Word Detection Card -->
|
| 345 |
+
<div class="result-card border-t border-gray-200 dark:border-gray-600 pt-4">
|
| 346 |
+
<div class="result-title text-md font-semibold text-gray-800 dark:text-white mb-2">Word Detection</div>
|
| 347 |
+
<p class="text-sm text-gray-600 dark:text-gray-400 mb-2">Words Detected: <strong class="text-gray-800 dark:text-white"><span id="wordCount">0</span></strong></p>
|
| 348 |
+
<img id="wordDetectionImg" class="result-image w-full h-auto max-h-48 object-contain border border-gray-200 dark:border-gray-600 rounded bg-gray-100 dark:bg-gray-700 hidden" src="" alt="Word Detection">
|
| 349 |
+
</div>
|
| 350 |
+
|
| 351 |
+
<!-- Prediction Card -->
|
| 352 |
+
<div class="result-card border-t border-gray-200 dark:border-gray-600 pt-4">
|
| 353 |
+
<div class="result-title text-md font-semibold text-gray-800 dark:text-white mb-2">Prediction</div>
|
| 354 |
+
<p class="text-sm text-gray-600 dark:text-gray-400 mb-2">Predicted Text: <strong class="text-gray-800 dark:text-white"><span id="predictionLabel">N/A</span></strong></p>
|
| 355 |
+
<img id="predictionImg" class="result-image w-full h-auto max-h-48 object-contain border border-gray-200 dark:border-gray-600 rounded bg-gray-100 dark:bg-gray-700 hidden" src="" alt="Prediction">
|
| 356 |
+
</div>
|
| 357 |
+
</div>
|
| 358 |
+
<p id="initialMessage" class="text-center text-gray-500 dark:text-gray-400 text-sm mt-4">Upload or select a sample image and click 'Process' to see the results.</p>
|
| 359 |
+
</div>
|
| 360 |
+
</div>
|
| 361 |
+
</div>
|
| 362 |
+
|
| 363 |
+
<!-- Translation Tab Content -->
|
| 364 |
+
<div class="tab-content hidden" id="translation">
|
| 365 |
+
<div class="card bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md border border-gray-100 dark:border-gray-700 max-w-2xl mx-auto">
|
| 366 |
+
<h2 class="card-title text-xl font-semibold text-gray-800 dark:text-white mb-4 border-b border-gray-200 dark:border-gray-600 pb-2 flex items-center">
|
| 367 |
+
<i class="ri-translate-2 mr-2 text-primary dark:text-blue-400"></i>
|
| 368 |
+
Text Translation
|
| 369 |
+
</h2>
|
| 370 |
+
<div class="space-y-4">
|
| 371 |
+
<div>
|
| 372 |
+
<label for="text-to-translate" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Text to Translate:</label>
|
| 373 |
+
<textarea id="text-to-translate" placeholder="Enter text here or it will be populated from OCR results..." rows="4" class="w-full px-3 py-2 border border-gray-300 rounded-button focus:outline-none focus:ring-2 focus:ring-primary/50 dark:bg-gray-700 dark:border-gray-600 dark:text-gray-200"></textarea>
|
| 374 |
+
</div>
|
| 375 |
+
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
| 376 |
+
<div>
|
| 377 |
+
<label for="source-language" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Source Language (Optional):</label>
|
| 378 |
+
<input type="text" id="source-language" placeholder="Auto-detect if empty" class="w-full px-3 py-2 border border-gray-300 rounded-button focus:outline-none focus:ring-2 focus:ring-primary/50 dark:bg-gray-700 dark:border-gray-600 dark:text-gray-200">
|
| 379 |
+
</div>
|
| 380 |
+
<div>
|
| 381 |
+
<label for="target-language" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Target Language:</label>
|
| 382 |
+
<input type="text" id="target-language" placeholder="e.g., English, Hindi" required class="w-full px-3 py-2 border border-gray-300 rounded-button focus:outline-none focus:ring-2 focus:ring-primary/50 dark:bg-gray-700 dark:border-gray-600 dark:text-gray-200">
|
| 383 |
+
</div>
|
| 384 |
+
</div>
|
| 385 |
+
<div>
|
| 386 |
+
<button id="translate-button" class="w-full sm:w-auto bg-primary text-white px-5 py-2 rounded-button hover:bg-blue-700 transition-colors font-medium inline-flex items-center justify-center gap-2 disabled:opacity-50">
|
| 387 |
+
<i class="ri-translate"></i> Translate Text
|
| 388 |
+
</button>
|
| 389 |
+
</div>
|
| 390 |
+
</div>
|
| 391 |
+
<!-- Loading Indicator -->
|
| 392 |
+
<div id="translation-loading" class="flex items-center justify-center my-4 text-primary dark:text-blue-400 hidden">
|
| 393 |
+
<svg class="animate-spin -ml-1 mr-3 h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
| 394 |
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
| 395 |
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
| 396 |
+
</svg>
|
| 397 |
+
<span>Translating...</span>
|
| 398 |
+
</div>
|
| 399 |
+
<div class="error-message bg-red-100 border border-red-300 text-red-800 dark:bg-red-900 dark:bg-opacity-50 dark:border-red-600 dark:text-red-200 px-4 py-2 rounded-md text-sm mt-4 hidden" id="translationErrorMessage"></div>
|
| 400 |
+
</div>
|
| 401 |
+
|
| 402 |
+
<!-- Translation Results -->
|
| 403 |
+
<div id="translation-result" class="results card bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md border border-gray-100 dark:border-gray-700 max-w-2xl mx-auto mt-6 hidden">
|
| 404 |
+
<h3 class="result-header text-lg font-semibold text-gray-800 dark:text-white mb-4 border-b border-gray-200 dark:border-gray-600 pb-2">Translation Result</h3>
|
| 405 |
+
<div class="space-y-3 text-sm">
|
| 406 |
+
<div class="result-item flex justify-between">
|
| 407 |
+
<span class="result-label font-medium text-gray-600 dark:text-gray-400">Source Language:</span>
|
| 408 |
+
<span id="detected-source-language" class="text-gray-800 dark:text-white font-medium"></span>
|
| 409 |
+
</div>
|
| 410 |
+
<div class="result-item flex justify-between">
|
| 411 |
+
<span class="result-label font-medium text-gray-600 dark:text-gray-400">Target Language:</span>
|
| 412 |
+
<span id="translation-target-language" class="text-gray-800 dark:text-white font-medium"></span>
|
| 413 |
+
</div>
|
| 414 |
+
<div class="result-item pt-3 mt-3 border-t border-gray-200 dark:border-gray-600">
|
| 415 |
+
<div class="result-label font-medium text-gray-600 dark:text-gray-400 mb-1">Translated Text:</div>
|
| 416 |
+
<div id="translated-text" class="result-content bg-gray-50 dark:bg-gray-700 p-3 rounded border border-gray-200 dark:border-gray-600 text-gray-800 dark:text-white"></div>
|
| 417 |
+
</div>
|
| 418 |
+
</div>
|
| 419 |
+
</div>
|
| 420 |
+
</div>
|
| 421 |
+
|
| 422 |
+
<!-- Gender Prediction Tab Content -->
|
| 423 |
+
<div class="tab-content hidden" id="gender">
|
| 424 |
+
<div class="card bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md border border-gray-100 dark:border-gray-700 max-w-2xl mx-auto">
|
| 425 |
+
<h2 class="card-title text-xl font-semibold text-gray-800 dark:text-white mb-4 border-b border-gray-200 dark:border-gray-600 pb-2 flex items-center">
|
| 426 |
+
<i class="ri-user-search-line mr-2 text-primary dark:text-blue-400"></i>
|
| 427 |
+
Gender Prediction
|
| 428 |
+
</h2>
|
| 429 |
+
<div class="space-y-4">
|
| 430 |
+
<div>
|
| 431 |
+
<label for="names-input" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Enter names (comma-separated):</label>
|
| 432 |
+
<textarea id="names-input" placeholder="e.g., Sakshi, Sameer, Priya" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-button focus:outline-none focus:ring-2 focus:ring-primary/50 dark:bg-gray-700 dark:border-gray-600 dark:text-gray-200"></textarea>
|
| 433 |
+
</div>
|
| 434 |
+
<div>
|
| 435 |
+
<button id="predict-gender-button" class="w-full sm:w-auto bg-primary text-white px-5 py-2 rounded-button hover:bg-blue-700 transition-colors font-medium inline-flex items-center justify-center gap-2 disabled:opacity-50">
|
| 436 |
+
<i class="ri-user-shared-line"></i> Predict Gender
|
| 437 |
+
</button>
|
| 438 |
+
</div>
|
| 439 |
+
</div>
|
| 440 |
+
<!-- Loading Indicator -->
|
| 441 |
+
<div id="gender-loading" class="flex items-center justify-center my-4 text-primary dark:text-blue-400 hidden">
|
| 442 |
+
<svg class="animate-spin -ml-1 mr-3 h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
| 443 |
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
| 444 |
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
| 445 |
+
</svg>
|
| 446 |
+
<span>Analyzing names...</span>
|
| 447 |
+
</div>
|
| 448 |
+
<div class="error-message bg-red-100 border border-red-300 text-red-800 dark:bg-red-900 dark:bg-opacity-50 dark:border-red-600 dark:text-red-200 px-4 py-2 rounded-md text-sm mt-4 hidden" id="genderErrorMessage"></div>
|
| 449 |
+
</div>
|
| 450 |
+
|
| 451 |
+
<!-- Gender Prediction Results -->
|
| 452 |
+
<div id="gender-results" class="results card bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md border border-gray-100 dark:border-gray-700 max-w-2xl mx-auto mt-6 hidden">
|
| 453 |
+
<h3 class="result-header text-lg font-semibold text-gray-800 dark:text-white mb-4 border-b border-gray-200 dark:border-gray-600 pb-2">Prediction Results</h3>
|
| 454 |
+
<!-- Results populated by JS -->
|
| 455 |
+
<div class="space-y-4 text-sm">
|
| 456 |
+
<!-- JS will insert result items here -->
|
| 457 |
+
</div>
|
| 458 |
+
</div>
|
| 459 |
+
</div>
|
| 460 |
+
</div>
|
| 461 |
+
|
| 462 |
+
</main>
|
| 463 |
+
|
| 464 |
+
<!-- Footer -->
|
| 465 |
+
<footer class="bg-gray-800 text-gray-400 py-8 mt-12"> <!-- Similar style to NIC project footer -->
|
| 466 |
+
<div class="container mx-auto px-4 text-center">
|
| 467 |
+
<div class="mb-4">
|
| 468 |
+
<a href="#" class="text-xl font-bold text-white hover:text-blue-300">Vision & Discern</a>
|
| 469 |
+
</div>
|
| 470 |
+
<div class="credits text-sm mb-4">
|
| 471 |
+
<p>Powered by <strong>D SAKSHI</strong> (MCA Final Year BIT Durg, Chhattisgarh) | © SlimShadow Org. All Rights Reserved.</p>
|
| 472 |
+
</div>
|
| 473 |
+
<div class="flex justify-center space-x-4">
|
| 474 |
+
<a href="#" class="hover:text-white" title="GitHub (Placeholder)"><i class="ri-github-fill"></i></a>
|
| 475 |
+
<a href="#" class="hover:text-white" title="LinkedIn (Placeholder)"><i class="ri-linkedin-box-fill"></i></a>
|
| 476 |
+
<a href="#" class="hover:text-white" title="Twitter (Placeholder)"><i class="ri-twitter-fill"></i></a>
|
| 477 |
+
</div>
|
| 478 |
+
</div>
|
| 479 |
+
</footer>
|
| 480 |
+
|
| 481 |
+
</div> <!-- End #app-container -->
|
| 482 |
+
|
| 483 |
+
|
| 484 |
+
<script>
|
| 485 |
+
// --- API Base URLs ---
|
| 486 |
+
const OCR_API_BASE_URL = 'https://sameernotes-ocr.hf.space';
|
| 487 |
+
const TRANSLATION_API_BASE_URL = 'https://sameernotes-translation-prediction-space.hf.space';
|
| 488 |
+
const GENDER_API_BASE_URL = "https://sidvilas-gender-prediction-space.hf.space";
|
| 489 |
+
const supportedLanguages = ["Afrikaans", "Arabic", "Armenian", "Azerbaijani", "Belarusian", "Bosnian", "Bulgarian", "Catalan", "Chinese", "Croatian", "Czech", "Danish", "Dutch", "English", "Estonian", "Finnish", "French", "Galician", "German", "Greek", "Hebrew", "Hindi", "Hungarian", "Icelandic", "Indonesian", "Italian", "Japanese", "Kannada", "Kazakh", "Korean", "Latvian", "Lithuanian", "Macedonian", "Malay", "Marathi", "Maori", "Nepali", "Norwegian", "Persian", "Polish", "Portuguese", "Romanian", "Russian", "Serbian", "Slovak", "Slovenian", "Spanish", "Swahili", "Swedish", "Tagalog", "Tamil", "Thai", "Turkish", "Ukrainian", "Urdu", "Vietnamese", "Welsh"];
|
| 490 |
+
|
| 491 |
+
let accessToken = null;
|
| 492 |
+
let selectedImageSource = null; // Holds the File object or Blob for processing
|
| 493 |
+
|
| 494 |
+
// --- DOM Element References (Consolidated) ---
|
| 495 |
+
const htmlElement = document.documentElement; // Target <html> for dark mode/lang
|
| 496 |
+
|
| 497 |
+
// Login/Signup
|
| 498 |
+
const loginContainer = document.getElementById('login-container');
|
| 499 |
+
const appContainer = document.getElementById('app-container');
|
| 500 |
+
const loginCard = document.getElementById('login-card');
|
| 501 |
+
const signupCard = document.getElementById('signup-card');
|
| 502 |
+
const loginLink = document.getElementById('login-link');
|
| 503 |
+
const signupLink = document.getElementById('signup-link');
|
| 504 |
+
const usernameInput = document.getElementById('username');
|
| 505 |
+
const passwordInput = document.getElementById('password');
|
| 506 |
+
const loginButton = document.getElementById('loginButton');
|
| 507 |
+
const loginErrorMessage = document.getElementById('loginErrorMessage');
|
| 508 |
+
const signupUsernameInput = document.getElementById('signupUsername');
|
| 509 |
+
const signupEmailInput = document.getElementById('signupEmail');
|
| 510 |
+
const signupPasswordInput = document.getElementById('signupPassword');
|
| 511 |
+
const signupButton = document.getElementById('signupButton');
|
| 512 |
+
const signupErrorMessage = document.getElementById('signupErrorMessage');
|
| 513 |
+
const logoutButton = document.getElementById('logoutButton');
|
| 514 |
+
|
| 515 |
+
// --- Theme/Lang Toggles ---
|
| 516 |
+
const themeToggle = document.getElementById('themeToggle');
|
| 517 |
+
const languageToggle = document.getElementById('languageToggle'); // Added for structure
|
| 518 |
+
|
| 519 |
+
// --- OCR Elements ---
|
| 520 |
+
const fileInput = document.getElementById('fileInput');
|
| 521 |
+
const uploadArea = document.getElementById('uploadArea');
|
| 522 |
+
const imagePreview = document.getElementById('imagePreview');
|
| 523 |
+
const processButton = document.getElementById('processButton');
|
| 524 |
+
const clearButton = document.getElementById('clearButton');
|
| 525 |
+
const sampleImages = document.querySelectorAll('.sample-image');
|
| 526 |
+
const loadingSpinner = document.getElementById('loadingSpinner');
|
| 527 |
+
const resultsSection = document.getElementById('resultsSection');
|
| 528 |
+
const initialMessage = document.getElementById('initialMessage');
|
| 529 |
+
const ocrErrorMessage = document.getElementById('ocrErrorMessage');
|
| 530 |
+
const ocrOutput = document.getElementById('ocrOutput');
|
| 531 |
+
const wordCount = document.getElementById('wordCount');
|
| 532 |
+
const wordDetectionImg = document.getElementById('wordDetectionImg');
|
| 533 |
+
const predictionLabel = document.getElementById('predictionLabel');
|
| 534 |
+
const predictionImg = document.getElementById('predictionImg');
|
| 535 |
+
|
| 536 |
+
// --- Translation Elements ---
|
| 537 |
+
const textToTranslateInput = document.getElementById('text-to-translate');
|
| 538 |
+
const sourceLanguageInput = document.getElementById('source-language');
|
| 539 |
+
const targetLanguageInput = document.getElementById('target-language');
|
| 540 |
+
const translateButton = document.getElementById('translate-button');
|
| 541 |
+
const translationLoading = document.getElementById('translation-loading');
|
| 542 |
+
const translationResultDiv = document.getElementById('translation-result');
|
| 543 |
+
const detectedSourceLanguage = document.getElementById('detected-source-language');
|
| 544 |
+
const translationTargetLanguage = document.getElementById('translation-target-language');
|
| 545 |
+
const translatedText = document.getElementById('translated-text');
|
| 546 |
+
const translationErrorMessage = document.getElementById('translationErrorMessage');
|
| 547 |
+
|
| 548 |
+
// --- Gender Prediction Elements ---
|
| 549 |
+
const namesInput = document.getElementById('names-input');
|
| 550 |
+
const predictGenderButton = document.getElementById('predict-gender-button');
|
| 551 |
+
const genderLoadingDiv = document.getElementById('gender-loading');
|
| 552 |
+
const genderResultsDiv = document.getElementById('gender-results');
|
| 553 |
+
const genderErrorMessageDiv = document.getElementById('genderErrorMessage');
|
| 554 |
+
|
| 555 |
+
// --- Tab Management ---
|
| 556 |
+
const tabs = document.querySelectorAll('.tab-button');
|
| 557 |
+
const tabContents = document.querySelectorAll('.tab-content');
|
| 558 |
+
|
| 559 |
+
|
| 560 |
+
// --- THEME TOGGLE LOGIC (from NIC Project) ---
|
| 561 |
+
function applyTheme(isDark) {
|
| 562 |
+
if (isDark) {
|
| 563 |
+
htmlElement.classList.add('dark');
|
| 564 |
+
if (themeToggle) themeToggle.checked = true;
|
| 565 |
+
console.log("Theme applied: dark");
|
| 566 |
+
} else {
|
| 567 |
+
htmlElement.classList.remove('dark');
|
| 568 |
+
if (themeToggle) themeToggle.checked = false;
|
| 569 |
+
console.log("Theme applied: light");
|
| 570 |
+
}
|
| 571 |
+
}
|
| 572 |
+
// Check localStorage on load
|
| 573 |
+
const prefersDark = localStorage.getItem('theme') === 'dark' ||
|
| 574 |
+
(localStorage.getItem('theme') === null && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
| 575 |
+
applyTheme(prefersDark);
|
| 576 |
+
|
| 577 |
+
// Add listener to toggle button
|
| 578 |
+
if (themeToggle) {
|
| 579 |
+
themeToggle.addEventListener('change', (event) => {
|
| 580 |
+
const isDark = event.target.checked;
|
| 581 |
+
applyTheme(isDark);
|
| 582 |
+
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
| 583 |
+
});
|
| 584 |
+
} else {
|
| 585 |
+
console.warn("Theme toggle button not found.");
|
| 586 |
+
}
|
| 587 |
+
|
| 588 |
+
// --- LANGUAGE TOGGLE LOGIC (Placeholder from NIC Project) ---
|
| 589 |
+
// Note: This app doesn't use data-lang attributes yet, so this just sets the toggle state and html lang.
|
| 590 |
+
function applyLanguage(lang) { // lang should be 'en' or 'hi'
|
| 591 |
+
console.log("Applying language:", lang);
|
| 592 |
+
htmlElement.setAttribute('lang', lang); // Set overall page lang
|
| 593 |
+
|
| 594 |
+
// TODO: If you add data-lang attributes, iterate and update text here
|
| 595 |
+
|
| 596 |
+
// Update toggle state
|
| 597 |
+
if (languageToggle) languageToggle.checked = (lang === 'hi');
|
| 598 |
+
|
| 599 |
+
// Save preference
|
| 600 |
+
localStorage.setItem('language', lang);
|
| 601 |
+
}
|
| 602 |
+
// Check localStorage on load for language
|
| 603 |
+
const savedLang = localStorage.getItem('language') || 'en'; // Default to English
|
| 604 |
+
applyLanguage(savedLang);
|
| 605 |
+
|
| 606 |
+
// Add listener to language toggle button
|
| 607 |
+
if (languageToggle) {
|
| 608 |
+
languageToggle.disabled = false; // Enable if found
|
| 609 |
+
languageToggle.addEventListener('change', (event) => {
|
| 610 |
+
const newLang = event.target.checked ? 'hi' : 'en';
|
| 611 |
+
applyLanguage(newLang);
|
| 612 |
+
});
|
| 613 |
+
} else {
|
| 614 |
+
console.warn("Language toggle button not found.");
|
| 615 |
+
}
|
| 616 |
+
|
| 617 |
+
|
| 618 |
+
// --- TAB MANAGEMENT ---
|
| 619 |
+
tabs.forEach(tab => {
|
| 620 |
+
tab.addEventListener('click', () => {
|
| 621 |
+
// Deactivate all tabs and content
|
| 622 |
+
tabs.forEach(t => {
|
| 623 |
+
t.classList.remove('active', 'text-primary', 'dark:text-blue-400', 'border-primary', 'dark:border-blue-400', 'font-medium');
|
| 624 |
+
t.classList.add('text-gray-600', 'dark:text-gray-300', 'border-transparent');
|
| 625 |
+
});
|
| 626 |
+
tabContents.forEach(c => c.classList.add('hidden')); // Use hidden class
|
| 627 |
+
|
| 628 |
+
// Activate clicked tab and corresponding content
|
| 629 |
+
tab.classList.add('active', 'text-primary', 'dark:text-blue-400', 'border-primary', 'dark:border-blue-400', 'font-medium');
|
| 630 |
+
tab.classList.remove('text-gray-600', 'dark:text-gray-300', 'border-transparent');
|
| 631 |
+
const activeTabContent = document.getElementById(tab.dataset.tab);
|
| 632 |
+
if (activeTabContent) {
|
| 633 |
+
activeTabContent.classList.remove('hidden'); // Show active content
|
| 634 |
+
}
|
| 635 |
+
});
|
| 636 |
+
});
|
| 637 |
+
|
| 638 |
+
// --- LOGIN/SIGNUP LOGIC (Adapted for hidden class) ---
|
| 639 |
+
loginLink.addEventListener('click', (e) => {
|
| 640 |
+
e.preventDefault();
|
| 641 |
+
signupCard.classList.add('hidden');
|
| 642 |
+
loginCard.classList.remove('hidden');
|
| 643 |
+
});
|
| 644 |
+
|
| 645 |
+
signupLink.addEventListener('click', (e) => {
|
| 646 |
+
e.preventDefault();
|
| 647 |
+
loginCard.classList.add('hidden');
|
| 648 |
+
signupCard.classList.remove('hidden');
|
| 649 |
+
});
|
| 650 |
+
|
| 651 |
+
loginButton.addEventListener('click', async () => {
|
| 652 |
+
loginButton.disabled = true;
|
| 653 |
+
hideElement(loginErrorMessage); // Use helper
|
| 654 |
+
const username = usernameInput.value;
|
| 655 |
+
const password = passwordInput.value;
|
| 656 |
+
|
| 657 |
+
if (!username || !password) {
|
| 658 |
+
showLoginError("Please enter both username and password.");
|
| 659 |
+
loginButton.disabled = false;
|
| 660 |
+
return;
|
| 661 |
+
}
|
| 662 |
+
|
| 663 |
+
const formData = new URLSearchParams();
|
| 664 |
+
formData.append('username', username);
|
| 665 |
+
formData.append('password', password);
|
| 666 |
+
|
| 667 |
+
try {
|
| 668 |
+
const response = await fetch(`${OCR_API_BASE_URL}/token`, {
|
| 669 |
+
method: 'POST',
|
| 670 |
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
| 671 |
+
body: formData.toString()
|
| 672 |
+
});
|
| 673 |
+
|
| 674 |
+
if (!response.ok) {
|
| 675 |
+
const errorData = await response.json().catch(() => ({ detail: "Login failed. Check credentials." }));
|
| 676 |
+
showLoginError(errorData.detail || "Login failed. Check credentials.");
|
| 677 |
+
return;
|
| 678 |
+
}
|
| 679 |
+
|
| 680 |
+
const data = await response.json();
|
| 681 |
+
accessToken = data.access_token;
|
| 682 |
+
hideElement(loginContainer); // Hide login section
|
| 683 |
+
showElement(appContainer, 'flex flex-col'); // Show app container using flex for layout
|
| 684 |
+
|
| 685 |
+
} catch (error) {
|
| 686 |
+
showLoginError("Network error during login. Please try again.");
|
| 687 |
+
console.error("Login error:", error);
|
| 688 |
+
} finally {
|
| 689 |
+
loginButton.disabled = false;
|
| 690 |
+
}
|
| 691 |
+
});
|
| 692 |
+
|
| 693 |
+
signupButton.addEventListener('click', async () => {
|
| 694 |
+
signupButton.disabled = true;
|
| 695 |
+
hideElement(signupErrorMessage); // Use helper
|
| 696 |
+
const username = signupUsernameInput.value;
|
| 697 |
+
const email = signupEmailInput.value;
|
| 698 |
+
const password = signupPasswordInput.value;
|
| 699 |
+
|
| 700 |
+
if (!username || !email || !password) {
|
| 701 |
+
showSignupError("Please fill in all fields.");
|
| 702 |
+
signupButton.disabled = false;
|
| 703 |
+
return;
|
| 704 |
+
}
|
| 705 |
+
if (!/\S+@\S+\.\S+/.test(email)) {
|
| 706 |
+
showSignupError("Please enter a valid email address.");
|
| 707 |
+
signupButton.disabled = false;
|
| 708 |
+
return;
|
| 709 |
+
}
|
| 710 |
+
|
| 711 |
+
try {
|
| 712 |
+
const response = await fetch(`${OCR_API_BASE_URL}/signup`, {
|
| 713 |
+
method: 'POST',
|
| 714 |
+
headers: { 'Content-Type': 'application/json' },
|
| 715 |
+
body: JSON.stringify({ username, email, password })
|
| 716 |
+
});
|
| 717 |
+
|
| 718 |
+
if (!response.ok) {
|
| 719 |
+
let errorMsg = "Signup failed.";
|
| 720 |
+
try {
|
| 721 |
+
const errorData = await response.json();
|
| 722 |
+
errorMsg = `Signup failed: ${errorData.detail || 'Unknown error'}`;
|
| 723 |
+
} catch (e) { errorMsg = `Signup failed with status ${response.status}.`; }
|
| 724 |
+
showSignupError(errorMsg);
|
| 725 |
+
return;
|
| 726 |
+
}
|
| 727 |
+
|
| 728 |
+
alert('Signup successful! Please login.');
|
| 729 |
+
hideElement(signupCard); // Use helper
|
| 730 |
+
showElement(loginCard); // Use helper
|
| 731 |
+
signupUsernameInput.value = '';
|
| 732 |
+
signupEmailInput.value = '';
|
| 733 |
+
signupPasswordInput.value = '';
|
| 734 |
+
|
| 735 |
+
} catch (error) {
|
| 736 |
+
showSignupError("Network error during signup. Please try again.");
|
| 737 |
+
console.error("Signup error:", error);
|
| 738 |
+
} finally {
|
| 739 |
+
signupButton.disabled = false;
|
| 740 |
+
}
|
| 741 |
+
});
|
| 742 |
+
|
| 743 |
+
logoutButton.addEventListener('click', () => {
|
| 744 |
+
accessToken = null;
|
| 745 |
+
selectedImageSource = null;
|
| 746 |
+
hideElement(appContainer); // Use helper
|
| 747 |
+
showElement(loginContainer, 'flex'); // Use helper for flex centering
|
| 748 |
+
usernameInput.value = '';
|
| 749 |
+
passwordInput.value = '';
|
| 750 |
+
hideElement(loginErrorMessage);
|
| 751 |
+
clearOCRResults();
|
| 752 |
+
clearTranslationResults();
|
| 753 |
+
clearGenderResults();
|
| 754 |
+
});
|
| 755 |
+
|
| 756 |
+
// --- OCR FUNCTIONALITY (Adapted for hidden class) ---
|
| 757 |
+
uploadArea.addEventListener('click', () => fileInput.click());
|
| 758 |
+
uploadArea.addEventListener('dragover', (e) => { e.preventDefault(); uploadArea.classList.add('dragover', 'border-primary', 'dark:border-blue-400', 'bg-secondary'); });
|
| 759 |
+
uploadArea.addEventListener('dragleave', () => uploadArea.classList.remove('dragover', 'border-primary', 'dark:border-blue-400', 'bg-secondary'));
|
| 760 |
+
uploadArea.addEventListener('drop', (e) => {
|
| 761 |
+
e.preventDefault();
|
| 762 |
+
uploadArea.classList.remove('dragover', 'border-primary', 'dark:border-blue-400', 'bg-secondary');
|
| 763 |
+
if (e.dataTransfer.files.length) {
|
| 764 |
+
fileInput.files = e.dataTransfer.files;
|
| 765 |
+
handleFileSelect();
|
| 766 |
+
}
|
| 767 |
+
});
|
| 768 |
+
fileInput.addEventListener('change', handleFileSelect);
|
| 769 |
+
|
| 770 |
+
sampleImages.forEach(img => {
|
| 771 |
+
img.addEventListener('click', () => {
|
| 772 |
+
const imageUrl = img.dataset.src;
|
| 773 |
+
if (imageUrl) { loadSampleImage(imageUrl); }
|
| 774 |
+
else { showOCRError("Sample image URL is missing."); }
|
| 775 |
+
});
|
| 776 |
+
});
|
| 777 |
+
|
| 778 |
+
async function loadSampleImage(imageUrl) {
|
| 779 |
+
clearOCRResults();
|
| 780 |
+
showLoading(loadingSpinner);
|
| 781 |
+
imagePreview.src = imageUrl;
|
| 782 |
+
showElement(imagePreview); // Show preview
|
| 783 |
+
processButton.disabled = true;
|
| 784 |
+
showElement(clearButton, 'inline-flex');
|
| 785 |
+
hideOCRError();
|
| 786 |
+
|
| 787 |
+
try {
|
| 788 |
+
const response = await fetch(imageUrl);
|
| 789 |
+
if (!response.ok) throw new Error(`Failed to fetch sample image: ${response.statusText}`);
|
| 790 |
+
const blob = await response.blob();
|
| 791 |
+
const filename = decodeURIComponent(imageUrl.substring(imageUrl.lastIndexOf('/') + 1) || 'sample.png');
|
| 792 |
+
selectedImageSource = new File([blob], filename, { type: blob.type });
|
| 793 |
+
processButton.disabled = false;
|
| 794 |
+
hideElement(initialMessage);
|
| 795 |
+
|
| 796 |
+
} catch (error) {
|
| 797 |
+
console.error('Error loading sample image:', error);
|
| 798 |
+
showOCRError(`Could not load sample image: ${error.message}`);
|
| 799 |
+
clearOCRResults();
|
| 800 |
+
} finally {
|
| 801 |
+
hideLoading(loadingSpinner);
|
| 802 |
+
}
|
| 803 |
+
}
|
| 804 |
+
|
| 805 |
+
function handleFileSelect() {
|
| 806 |
+
const file = fileInput.files[0];
|
| 807 |
+
if (file) {
|
| 808 |
+
if (!file.type.startsWith('image/')) {
|
| 809 |
+
showOCRError('Please select a valid image file (PNG, JPG, JPEG).');
|
| 810 |
+
fileInput.value = ''; return;
|
| 811 |
+
}
|
| 812 |
+
hideOCRError();
|
| 813 |
+
clearOCRResults();
|
| 814 |
+
selectedImageSource = file;
|
| 815 |
+
|
| 816 |
+
const reader = new FileReader();
|
| 817 |
+
reader.onload = (e) => {
|
| 818 |
+
imagePreview.src = e.target.result;
|
| 819 |
+
showElement(imagePreview);
|
| 820 |
+
processButton.disabled = false;
|
| 821 |
+
showElement(clearButton, 'inline-flex');
|
| 822 |
+
hideElement(initialMessage);
|
| 823 |
+
};
|
| 824 |
+
reader.onerror = () => { showOCRError('Error reading the selected file.'); };
|
| 825 |
+
reader.readAsDataURL(file);
|
| 826 |
+
}
|
| 827 |
+
}
|
| 828 |
+
|
| 829 |
+
processButton.addEventListener('click', processImage);
|
| 830 |
+
clearButton.addEventListener('click', clearOCRResults);
|
| 831 |
+
|
| 832 |
+
async function processImage() {
|
| 833 |
+
const imageSource = selectedImageSource;
|
| 834 |
+
if (!imageSource) { showOCRError('Please select or click a sample image first.'); return; }
|
| 835 |
+
|
| 836 |
+
showLoading(loadingSpinner);
|
| 837 |
+
hideElement(resultsSection);
|
| 838 |
+
hideElement(initialMessage);
|
| 839 |
+
processButton.disabled = true;
|
| 840 |
+
clearButton.disabled = true;
|
| 841 |
+
hideOCRError();
|
| 842 |
+
|
| 843 |
+
try {
|
| 844 |
+
const formData = new FormData();
|
| 845 |
+
formData.append('file', imageSource, imageSource.name || 'image.png');
|
| 846 |
+
|
| 847 |
+
const response = await fetch(`${OCR_API_BASE_URL}/process/`, {
|
| 848 |
+
method: 'POST',
|
| 849 |
+
headers: { 'Authorization': `Bearer ${accessToken}`, 'accept': 'application/json' },
|
| 850 |
+
body: formData
|
| 851 |
+
});
|
| 852 |
+
|
| 853 |
+
if (response.status === 401) {
|
| 854 |
+
showOCRError('Authentication failed. Please log out and log back in.');
|
| 855 |
+
logoutButton.click(); return;
|
| 856 |
+
}
|
| 857 |
+
if (!response.ok) {
|
| 858 |
+
let errorMsg = `Image processing failed (Status: ${response.status})`;
|
| 859 |
+
try { const errorData = await response.json(); errorMsg = `Image processing failed: ${errorData.detail || response.statusText}`; } catch (e) { }
|
| 860 |
+
throw new Error(errorMsg);
|
| 861 |
+
}
|
| 862 |
+
|
| 863 |
+
const data = await response.json();
|
| 864 |
+
ocrOutput.textContent = data.sakshi_output || 'No text detected.';
|
| 865 |
+
textToTranslateInput.value = data.sakshi_output || '';
|
| 866 |
+
wordCount.textContent = data.word_count ?? '0';
|
| 867 |
+
predictionLabel.textContent = data.prediction_label || 'N/A';
|
| 868 |
+
|
| 869 |
+
const fetchImage = async (url, imgElement) => {
|
| 870 |
+
if (!url) { imgElement.src = ''; hideElement(imgElement); return; }
|
| 871 |
+
try {
|
| 872 |
+
const imageResponse = await fetch(url, { headers: { 'Authorization': `Bearer ${accessToken}` } });
|
| 873 |
+
if (imageResponse.ok) {
|
| 874 |
+
const blob = await imageResponse.blob();
|
| 875 |
+
imgElement.src = URL.createObjectURL(blob);
|
| 876 |
+
showElement(imgElement); // Show the image
|
| 877 |
+
// Ensure parent card is visible if it contains the image
|
| 878 |
+
const parentCard = imgElement.closest('.result-card');
|
| 879 |
+
if(parentCard) showElement(parentCard);
|
| 880 |
+
} else { console.error('Failed to fetch result image:', url, imageResponse.status); imgElement.src = ''; hideElement(imgElement); }
|
| 881 |
+
} catch (fetchError) { console.error('Error fetching result image:', url, fetchError); imgElement.src = ''; hideElement(imgElement); }
|
| 882 |
+
};
|
| 883 |
+
|
| 884 |
+
// Use absolute URLs if provided by API, otherwise construct them
|
| 885 |
+
const wordDetectionUrl = data.word_detection_url ? (data.word_detection_url.startsWith('http') ? data.word_detection_url : `${OCR_API_BASE_URL}${data.word_detection_url}`) : null;
|
| 886 |
+
const predictionUrl = data.prediction_image_url ? (data.prediction_image_url.startsWith('http') ? data.prediction_image_url : `${OCR_API_BASE_URL}${data.prediction_image_url}`) : null;
|
| 887 |
+
|
| 888 |
+
await Promise.all([
|
| 889 |
+
fetchImage(wordDetectionUrl, wordDetectionImg),
|
| 890 |
+
fetchImage(predictionUrl, predictionImg)
|
| 891 |
+
]);
|
| 892 |
+
|
| 893 |
+
showElement(resultsSection); // Show results section
|
| 894 |
+
hideElement(initialMessage);
|
| 895 |
+
|
| 896 |
+
} catch (error) {
|
| 897 |
+
console.error('Error processing image:', error);
|
| 898 |
+
showOCRError(`Error: ${error.message}`);
|
| 899 |
+
hideElement(resultsSection);
|
| 900 |
+
showElement(initialMessage);
|
| 901 |
+
initialMessage.textContent = 'An error occurred during processing. Please try again.';
|
| 902 |
+
} finally {
|
| 903 |
+
hideLoading(loadingSpinner);
|
| 904 |
+
processButton.disabled = false;
|
| 905 |
+
clearButton.disabled = false;
|
| 906 |
+
}
|
| 907 |
+
}
|
| 908 |
+
|
| 909 |
+
// --- TRANSLATION FUNCTIONALITY (Adapted for hidden class) ---
|
| 910 |
+
translateButton.addEventListener('click', async () => {
|
| 911 |
+
const text = textToTranslateInput.value.trim();
|
| 912 |
+
const sourceLanguage = sourceLanguageInput.value.trim();
|
| 913 |
+
const targetLanguage = targetLanguageInput.value.trim();
|
| 914 |
+
|
| 915 |
+
clearTranslationError();
|
| 916 |
+
hideElement(translationResultDiv);
|
| 917 |
+
|
| 918 |
+
if (!text) { showTranslationError('Please enter text to translate.'); return; }
|
| 919 |
+
if (!targetLanguage) { showTranslationError('Please enter the target language.'); return; }
|
| 920 |
+
|
| 921 |
+
showLoading(translationLoading);
|
| 922 |
+
translateButton.disabled = true;
|
| 923 |
+
|
| 924 |
+
try {
|
| 925 |
+
const requestBody = { text: text, target_language: targetLanguage };
|
| 926 |
+
if (sourceLanguage) { requestBody.source_language = sourceLanguage; }
|
| 927 |
+
|
| 928 |
+
const response = await fetch(`${TRANSLATION_API_BASE_URL}/translate`, {
|
| 929 |
+
method: 'POST',
|
| 930 |
+
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${accessToken}`, 'accept': 'application/json' },
|
| 931 |
+
body: JSON.stringify(requestBody)
|
| 932 |
+
});
|
| 933 |
+
|
| 934 |
+
if (response.status === 401) { showTranslationError('Authentication failed...'); logoutButton.click(); return; }
|
| 935 |
+
if (!response.ok) {
|
| 936 |
+
let errorMsg = `Translation failed (Status: ${response.status})`;
|
| 937 |
+
try { const errorData = await response.json(); errorMsg = `Translation failed: ${errorData.detail || response.statusText}`; } catch (e) { }
|
| 938 |
+
throw new Error(errorMsg);
|
| 939 |
+
}
|
| 940 |
+
|
| 941 |
+
const data = await response.json();
|
| 942 |
+
detectedSourceLanguage.textContent = data.source_language || (sourceLanguage || "Auto-detected");
|
| 943 |
+
translationTargetLanguage.textContent = data.target_language || targetLanguage;
|
| 944 |
+
translatedText.textContent = data.translated_text || "No translation returned.";
|
| 945 |
+
showElement(translationResultDiv); // Show results
|
| 946 |
+
|
| 947 |
+
} catch (error) {
|
| 948 |
+
showTranslationError(`Error: ${error.message}`);
|
| 949 |
+
console.error('Error during translation:', error);
|
| 950 |
+
} finally {
|
| 951 |
+
hideLoading(translationLoading);
|
| 952 |
+
translateButton.disabled = false;
|
| 953 |
+
}
|
| 954 |
+
});
|
| 955 |
+
|
| 956 |
+
// --- GENDER PREDICTION FUNCTIONALITY (Adapted for hidden class) ---
|
| 957 |
+
predictGenderButton.addEventListener('click', async () => {
|
| 958 |
+
const namesString = namesInput.value.trim();
|
| 959 |
+
const names = namesString.split(',').map(name => name.trim()).filter(name => name !== "");
|
| 960 |
+
|
| 961 |
+
if (names.length === 0) { showGenderError("Please enter at least one name."); return; }
|
| 962 |
+
|
| 963 |
+
clearGenderError();
|
| 964 |
+
hideElement(genderResultsDiv);
|
| 965 |
+
showLoading(genderLoadingDiv);
|
| 966 |
+
predictGenderButton.disabled = true;
|
| 967 |
+
|
| 968 |
+
try {
|
| 969 |
+
const response = await fetch(`${GENDER_API_BASE_URL}/predict`, {
|
| 970 |
+
method: 'POST',
|
| 971 |
+
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${accessToken}`, 'accept': 'application/json' },
|
| 972 |
+
body: JSON.stringify({ names: names, threshold: 0.5 }) // API expects { names: [...] }
|
| 973 |
+
});
|
| 974 |
+
|
| 975 |
+
if (response.status === 401) { showGenderError('Authentication failed...'); logoutButton.click(); return; }
|
| 976 |
+
if (!response.ok) {
|
| 977 |
+
let errorMsg = `Gender prediction failed (Status: ${response.status})`;
|
| 978 |
+
try { const errorData = await response.json(); errorMsg = `Prediction failed: ${errorData.detail || response.statusText}`; } catch (e) { }
|
| 979 |
+
throw new Error(errorMsg);
|
| 980 |
+
}
|
| 981 |
+
|
| 982 |
+
const data = await response.json();
|
| 983 |
+
if (data && Array.isArray(data.predictions)) {
|
| 984 |
+
displayGenderResults(data.predictions);
|
| 985 |
+
showElement(genderResultsDiv); // Show results container
|
| 986 |
+
} else {
|
| 987 |
+
console.error("Unexpected response format:", data);
|
| 988 |
+
showGenderError("Received an unexpected response format from the server.");
|
| 989 |
+
}
|
| 990 |
+
|
| 991 |
+
} catch (error) {
|
| 992 |
+
showGenderError(`Error: ${error.message}`);
|
| 993 |
+
console.error("Prediction error:", error);
|
| 994 |
+
} finally {
|
| 995 |
+
hideLoading(genderLoadingDiv);
|
| 996 |
+
predictGenderButton.disabled = false;
|
| 997 |
+
}
|
| 998 |
+
});
|
| 999 |
+
|
| 1000 |
+
// --- DISPLAY RESULTS (GENDER) ---
|
| 1001 |
+
function displayGenderResults(predictions) {
|
| 1002 |
+
const resultsContainer = genderResultsDiv.querySelector('.space-y-4'); // Target the inner container
|
| 1003 |
+
resultsContainer.innerHTML = ''; // Clear previous results first
|
| 1004 |
+
|
| 1005 |
+
if (predictions.length === 0) {
|
| 1006 |
+
resultsContainer.innerHTML = '<p class="text-gray-500 dark:text-gray-400">No predictions were returned.</p>';
|
| 1007 |
+
} else {
|
| 1008 |
+
predictions.forEach(prediction => {
|
| 1009 |
+
const name = prediction.name || 'N/A';
|
| 1010 |
+
const gender = prediction.predicted_gender || 'Unknown';
|
| 1011 |
+
const maleProb = typeof prediction.male_probability === 'number' ? (prediction.male_probability * 100).toFixed(1) + '%' : 'N/A';
|
| 1012 |
+
const confidence = typeof prediction.confidence === 'number' ? (prediction.confidence * 100).toFixed(1) + '%' : 'N/A';
|
| 1013 |
+
|
| 1014 |
+
// Create structure with Tailwind classes
|
| 1015 |
+
const resultItemHTML = `
|
| 1016 |
+
<div class="result-item border-b border-gray-200 dark:border-gray-600 pb-3 last:border-b-0 last:pb-0">
|
| 1017 |
+
<div class="flex justify-between mb-1">
|
| 1018 |
+
<span class="result-label font-medium text-gray-600 dark:text-gray-400">Name:</span>
|
| 1019 |
+
<span class="text-gray-800 dark:text-white font-semibold">${name}</span>
|
| 1020 |
+
</div>
|
| 1021 |
+
<div class="flex justify-between mb-1">
|
| 1022 |
+
<span class="result-label font-medium text-gray-600 dark:text-gray-400">Predicted Gender:</span>
|
| 1023 |
+
<span class="text-gray-800 dark:text-white">${gender}</span>
|
| 1024 |
+
</div>
|
| 1025 |
+
<div class="flex justify-between mb-1">
|
| 1026 |
+
<span class="result-label font-medium text-gray-600 dark:text-gray-400">Confidence:</span>
|
| 1027 |
+
<span class="text-gray-800 dark:text-white">${confidence}</span>
|
| 1028 |
+
</div>
|
| 1029 |
+
<div class="flex justify-between">
|
| 1030 |
+
<span class="result-label font-medium text-gray-600 dark:text-gray-400">Male Probability:</span>
|
| 1031 |
+
<span class="text-gray-800 dark:text-white">${maleProb}</span>
|
| 1032 |
+
</div>
|
| 1033 |
+
</div>
|
| 1034 |
+
`;
|
| 1035 |
+
resultsContainer.innerHTML += resultItemHTML;
|
| 1036 |
+
});
|
| 1037 |
+
}
|
| 1038 |
+
showElement(genderResultsDiv); // Ensure results container is visible
|
| 1039 |
+
}
|
| 1040 |
+
|
| 1041 |
+
// --- UTILITY FUNCTIONS (Error Handling, Loading, Clearing using hidden class) ---
|
| 1042 |
+
|
| 1043 |
+
function showElement(element, displayType = 'block') {
|
| 1044 |
+
if (element) {
|
| 1045 |
+
element.classList.remove('hidden');
|
| 1046 |
+
// If a specific display type like 'flex' is needed when visible
|
| 1047 |
+
if (displayType !== 'block') {
|
| 1048 |
+
// Ensure Tailwind classes for the display type are present
|
| 1049 |
+
// This might require removing 'block' if it was added by default
|
| 1050 |
+
element.classList.remove('block', 'inline-block', 'flex', 'grid'); // Remove common display types
|
| 1051 |
+
element.classList.add(...displayType.split(' ')); // Add the required classes
|
| 1052 |
+
}
|
| 1053 |
+
}
|
| 1054 |
+
}
|
| 1055 |
+
|
| 1056 |
+
function hideElement(element) {
|
| 1057 |
+
if (element) element.classList.add('hidden');
|
| 1058 |
+
}
|
| 1059 |
+
|
| 1060 |
+
function showLoginError(message) { loginErrorMessage.textContent = message; showElement(loginErrorMessage); }
|
| 1061 |
+
function showSignupError(message) { signupErrorMessage.textContent = message; showElement(signupErrorMessage); }
|
| 1062 |
+
function showOCRError(message) { ocrErrorMessage.textContent = message; showElement(ocrErrorMessage); }
|
| 1063 |
+
function hideOCRError() { hideElement(ocrErrorMessage); }
|
| 1064 |
+
function showTranslationError(message) { translationErrorMessage.textContent = message; showElement(translationErrorMessage); }
|
| 1065 |
+
function clearTranslationError() { translationErrorMessage.textContent = ''; hideElement(translationErrorMessage); }
|
| 1066 |
+
function showGenderError(message) { genderErrorMessageDiv.textContent = message; showElement(genderErrorMessageDiv); }
|
| 1067 |
+
function clearGenderError() { genderErrorMessageDiv.textContent = ''; hideElement(genderErrorMessageDiv); }
|
| 1068 |
+
function showLoading(loadingIndicator) { showElement(loadingIndicator, 'flex flex-col items-center justify-center'); } // Assuming flex layout for spinner
|
| 1069 |
+
function hideLoading(loadingIndicator) { hideElement(loadingIndicator); }
|
| 1070 |
+
|
| 1071 |
+
function clearOCRResults() {
|
| 1072 |
+
fileInput.value = '';
|
| 1073 |
+
selectedImageSource = null;
|
| 1074 |
+
hideElement(imagePreview); imagePreview.src = '';
|
| 1075 |
+
hideElement(wordDetectionImg); wordDetectionImg.src = '';
|
| 1076 |
+
hideElement(predictionImg); predictionImg.src = '';
|
| 1077 |
+
ocrOutput.textContent = '';
|
| 1078 |
+
wordCount.textContent = '0';
|
| 1079 |
+
predictionLabel.textContent = 'N/A';
|
| 1080 |
+
hideElement(resultsSection);
|
| 1081 |
+
showElement(initialMessage);
|
| 1082 |
+
initialMessage.textContent = "Upload or select a sample image and click 'Process' to see the results.";
|
| 1083 |
+
processButton.disabled = true;
|
| 1084 |
+
hideElement(clearButton);
|
| 1085 |
+
hideOCRError();
|
| 1086 |
+
hideLoading(loadingSpinner);
|
| 1087 |
+
}
|
| 1088 |
+
|
| 1089 |
+
function clearTranslationResults() {
|
| 1090 |
+
textToTranslateInput.value = ''; sourceLanguageInput.value = ''; targetLanguageInput.value = '';
|
| 1091 |
+
detectedSourceLanguage.textContent = ''; translationTargetLanguage.textContent = ''; translatedText.textContent = '';
|
| 1092 |
+
hideElement(translationResultDiv);
|
| 1093 |
+
clearTranslationError();
|
| 1094 |
+
hideLoading(translationLoading);
|
| 1095 |
+
}
|
| 1096 |
+
|
| 1097 |
+
function clearGenderResults() {
|
| 1098 |
+
namesInput.value = '';
|
| 1099 |
+
// Clear only the content inside the results div, keep the header
|
| 1100 |
+
const resultsContainer = genderResultsDiv.querySelector('.space-y-4');
|
| 1101 |
+
if (resultsContainer) resultsContainer.innerHTML = '';
|
| 1102 |
+
hideElement(genderResultsDiv);
|
| 1103 |
+
clearGenderError();
|
| 1104 |
+
hideLoading(genderLoadingDiv);
|
| 1105 |
+
}
|
| 1106 |
+
|
| 1107 |
+
// --- Initial state: show login screen ---
|
| 1108 |
+
showElement(loginContainer, 'flex'); // Use flex for centering
|
| 1109 |
+
hideElement(appContainer); // Hide app initially
|
| 1110 |
+
|
| 1111 |
+
</script>
|
| 1112 |
+
|
| 1113 |
+
</body>
|
| 1114 |
+
</html>
|
style.css
CHANGED
|
@@ -1,28 +1,501 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
:root {
|
| 2 |
+
--primary: #4f46e5;
|
| 3 |
+
--primary-light: #818cf8;
|
| 4 |
+
--primary-dark: #3730a3;
|
| 5 |
+
--secondary: #f97316;
|
| 6 |
+
--background: #f8fafc;
|
| 7 |
+
--text: #1e293b;
|
| 8 |
+
--gray-50: #f8fafc;
|
| 9 |
+
--gray-100: #f1f5f9;
|
| 10 |
+
--gray-200: #e2e8f0;
|
| 11 |
+
--gray-300: #cbd5e1;
|
| 12 |
+
--gray-400: #94a3b8;
|
| 13 |
+
--gray-800: #1e293b;
|
| 14 |
+
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
|
| 15 |
+
--shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
| 16 |
+
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
| 17 |
+
--radius-sm: 4px;
|
| 18 |
+
--radius: 8px;
|
| 19 |
+
--radius-lg: 12px;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
* {
|
| 23 |
+
margin: 0;
|
| 24 |
+
padding: 0;
|
| 25 |
+
box-sizing: border-box;
|
| 26 |
+
font-family: 'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
body {
|
| 30 |
+
background: linear-gradient(135deg, var(--gray-50), var(--gray-100));
|
| 31 |
+
color: var(--text);
|
| 32 |
+
line-height: 1.6;
|
| 33 |
+
min-height: 100vh;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
.container {
|
| 37 |
+
max-width: 1200px;
|
| 38 |
+
margin: 0 auto;
|
| 39 |
+
padding: 2rem;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
header {
|
| 43 |
+
padding: 2.5rem 2rem;
|
| 44 |
+
text-align: center;
|
| 45 |
+
background: linear-gradient(135deg, var(--primary-dark), var(--primary));
|
| 46 |
+
color: white;
|
| 47 |
+
border-radius: var(--radius-lg);
|
| 48 |
+
margin-bottom: 3rem;
|
| 49 |
+
box-shadow: var(--shadow-lg);
|
| 50 |
+
position: relative;
|
| 51 |
+
overflow: hidden;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
header::before {
|
| 55 |
+
content: '';
|
| 56 |
+
position: absolute;
|
| 57 |
+
top: 0;
|
| 58 |
+
left: 0;
|
| 59 |
+
right: 0;
|
| 60 |
+
bottom: 0;
|
| 61 |
+
background: linear-gradient(45deg, rgba(255,255,255,0.1) 25%, transparent 25%),
|
| 62 |
+
linear-gradient(-45deg, rgba(255,255,255,0.1) 25%, transparent 25%),
|
| 63 |
+
linear-gradient(45deg, transparent 75%, rgba(255,255,255,0.1) 75%),
|
| 64 |
+
linear-gradient(-45deg, transparent 75%, rgba(255,255,255,0.1) 75%);
|
| 65 |
+
background-size: 20px 20px;
|
| 66 |
+
opacity: 0.1;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
h1 {
|
| 70 |
+
font-size: 3rem;
|
| 71 |
+
font-weight: 800;
|
| 72 |
+
margin-bottom: 1rem;
|
| 73 |
+
letter-spacing: -0.025em;
|
| 74 |
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
.subtitle {
|
| 78 |
+
font-size: 1.25rem;
|
| 79 |
+
opacity: 0.9;
|
| 80 |
+
margin-bottom: 0.5rem;
|
| 81 |
+
font-weight: 500;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
.attribution {
|
| 85 |
+
margin-top: 1rem;
|
| 86 |
+
font-size: 0.875rem;
|
| 87 |
+
opacity: 0.8;
|
| 88 |
+
font-weight: 500;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
nav {
|
| 92 |
+
display: flex;
|
| 93 |
+
justify-content: center;
|
| 94 |
+
gap: 2rem;
|
| 95 |
+
padding: 1rem 0;
|
| 96 |
+
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
| 97 |
+
margin-bottom: 2rem;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
nav a {
|
| 101 |
+
color: white;
|
| 102 |
+
text-decoration: none;
|
| 103 |
+
font-weight: 600;
|
| 104 |
+
opacity: 0.9;
|
| 105 |
+
transition: all 0.2s ease;
|
| 106 |
+
padding: 0.5rem 1rem;
|
| 107 |
+
border-radius: var(--radius);
|
| 108 |
+
position: relative;
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
nav a::after {
|
| 112 |
+
content: '';
|
| 113 |
+
position: absolute;
|
| 114 |
+
bottom: -2px;
|
| 115 |
+
left: 50%;
|
| 116 |
+
transform: translateX(-50%);
|
| 117 |
+
width: 0;
|
| 118 |
+
height: 2px;
|
| 119 |
+
background-color: white;
|
| 120 |
+
transition: width 0.2s ease;
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
nav a:hover::after {
|
| 124 |
+
width: 100%;
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
nav a:hover {
|
| 128 |
+
opacity: 1;
|
| 129 |
+
background-color: rgba(255, 255, 255, 0.1);
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
nav a.active {
|
| 133 |
+
background-color: rgba(255, 255, 255, 0.2);
|
| 134 |
+
opacity: 1;
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
.main-content {
|
| 138 |
+
display: grid;
|
| 139 |
+
grid-template-columns: 1fr;
|
| 140 |
+
gap: 2rem;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
@media (min-width: 768px) {
|
| 144 |
+
.main-content {
|
| 145 |
+
grid-template-columns: repeat(2, 1fr);
|
| 146 |
+
}
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
.card {
|
| 150 |
+
background: white;
|
| 151 |
+
border-radius: var(--radius-lg);
|
| 152 |
+
padding: 2rem;
|
| 153 |
+
box-shadow: var(--shadow);
|
| 154 |
+
transition: all 0.3s ease;
|
| 155 |
+
border: 1px solid var(--gray-200);
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
.card:hover {
|
| 159 |
+
transform: translateY(-4px);
|
| 160 |
+
box-shadow: var(--shadow-lg);
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
.card-title {
|
| 164 |
+
font-size: 1.5rem;
|
| 165 |
+
color: var(--primary);
|
| 166 |
+
margin-bottom: 1.5rem;
|
| 167 |
+
display: flex;
|
| 168 |
+
align-items: center;
|
| 169 |
+
gap: 0.75rem;
|
| 170 |
+
font-weight: 700;
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
.card-title svg {
|
| 174 |
+
width: 1.5rem;
|
| 175 |
+
height: 1.5rem;
|
| 176 |
+
stroke-width: 2;
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
.upload-area {
|
| 180 |
+
border: 2px dashed var(--gray-300);
|
| 181 |
+
border-radius: var(--radius);
|
| 182 |
+
padding: 3rem 2rem;
|
| 183 |
+
text-align: center;
|
| 184 |
+
cursor: pointer;
|
| 185 |
+
transition: all 0.2s ease;
|
| 186 |
+
margin-bottom: 1.5rem;
|
| 187 |
+
background-color: var(--gray-50);
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
.upload-area:hover, .upload-area.dragover {
|
| 191 |
+
border-color: var(--primary);
|
| 192 |
+
background-color: rgba(79, 70, 229, 0.05);
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
.upload-icon {
|
| 196 |
+
font-size: 3rem;
|
| 197 |
+
color: var(--gray-400);
|
| 198 |
+
margin-bottom: 1rem;
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
#imagePreview {
|
| 202 |
+
max-width: 100%;
|
| 203 |
+
max-height: 300px;
|
| 204 |
+
margin: 1.5rem auto;
|
| 205 |
+
display: none;
|
| 206 |
+
border-radius: var(--radius);
|
| 207 |
+
box-shadow: var(--shadow);
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
button {
|
| 211 |
+
background-color: var(--primary);
|
| 212 |
+
color: white;
|
| 213 |
+
border: none;
|
| 214 |
+
padding: 0.75rem 1.5rem;
|
| 215 |
+
border-radius: var(--radius);
|
| 216 |
+
cursor: pointer;
|
| 217 |
+
font-size: 1rem;
|
| 218 |
+
font-weight: 600;
|
| 219 |
+
transition: all 0.2s ease;
|
| 220 |
+
display: inline-flex;
|
| 221 |
+
align-items: center;
|
| 222 |
+
gap: 0.5rem;
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
button:hover {
|
| 226 |
+
background-color: var(--primary-dark);
|
| 227 |
+
transform: translateY(-1px);
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
button:active {
|
| 231 |
+
transform: translateY(0);
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
button:disabled {
|
| 235 |
+
background-color: var(--gray-300);
|
| 236 |
+
cursor: not-allowed;
|
| 237 |
+
transform: none;
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
.button-secondary {
|
| 241 |
+
background-color: var(--secondary);
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
.button-secondary:hover {
|
| 245 |
+
background-color: #ea580c;
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
.results-section {
|
| 249 |
+
margin-top: 2rem;
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
.result-card {
|
| 253 |
+
background-color: var(--gray-50);
|
| 254 |
+
border-radius: var(--radius);
|
| 255 |
+
padding: 1.5rem;
|
| 256 |
+
margin-bottom: 1.5rem;
|
| 257 |
+
border: 1px solid var(--gray-200);
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
.result-title {
|
| 261 |
+
font-size: 1.25rem;
|
| 262 |
+
font-weight: 700;
|
| 263 |
+
margin-bottom: 1rem;
|
| 264 |
+
color: var(--primary-dark);
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
.result-content {
|
| 268 |
+
background-color: white;
|
| 269 |
+
padding: 1.25rem;
|
| 270 |
+
border-radius: var(--radius);
|
| 271 |
+
border: 1px solid var(--gray-200);
|
| 272 |
+
overflow-wrap: break-word;
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
.result-image {
|
| 276 |
+
max-width: 100%;
|
| 277 |
+
border-radius: var(--radius);
|
| 278 |
+
margin-top: 1rem;
|
| 279 |
+
box-shadow: var(--shadow);
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
.loading-spinner {
|
| 283 |
+
display: none;
|
| 284 |
+
text-align: center;
|
| 285 |
+
margin: 2rem 0;
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
.spinner {
|
| 289 |
+
width: 2.5rem;
|
| 290 |
+
height: 2.5rem;
|
| 291 |
+
border: 3px solid rgba(79, 70, 229, 0.1);
|
| 292 |
+
border-left-color: var(--primary);
|
| 293 |
+
border-radius: 50%;
|
| 294 |
+
animation: spin 0.8s linear infinite;
|
| 295 |
+
margin: 0 auto;
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
@keyframes spin {
|
| 299 |
+
to {
|
| 300 |
+
transform: rotate(360deg);
|
| 301 |
+
}
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
.error-message {
|
| 305 |
+
color: #dc2626;
|
| 306 |
+
background-color: #fee2e2;
|
| 307 |
+
padding: 1rem;
|
| 308 |
+
border-radius: var(--radius);
|
| 309 |
+
display: none;
|
| 310 |
+
margin: 1rem 0;
|
| 311 |
+
font-weight: 500;
|
| 312 |
+
border: 1px solid #fecaca;
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
.info-text {
|
| 316 |
+
color: var(--gray-400);
|
| 317 |
+
font-size: 0.875rem;
|
| 318 |
+
margin-top: 0.75rem;
|
| 319 |
+
}
|
| 320 |
+
|
| 321 |
+
.credits {
|
| 322 |
+
text-align: center;
|
| 323 |
+
margin-top: 3rem;
|
| 324 |
+
padding-top: 1.5rem;
|
| 325 |
+
border-top: 1px solid var(--gray-200);
|
| 326 |
+
color: var(--gray-400);
|
| 327 |
+
font-size: 0.875rem;
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
#login-container {
|
| 331 |
+
display: flex;
|
| 332 |
+
justify-content: center;
|
| 333 |
+
align-items: center;
|
| 334 |
+
min-height: 100vh;
|
| 335 |
+
background: linear-gradient(135deg, var(--gray-50), var(--gray-100));
|
| 336 |
+
padding: 2rem;
|
| 337 |
+
}
|
| 338 |
+
|
| 339 |
+
.auth-card {
|
| 340 |
+
background: white;
|
| 341 |
+
border-radius: var(--radius-lg);
|
| 342 |
+
padding: 2.5rem;
|
| 343 |
+
box-shadow: var(--shadow-lg);
|
| 344 |
+
width: 100%;
|
| 345 |
+
max-width: 400px;
|
| 346 |
+
text-align: center;
|
| 347 |
+
border: 1px solid var(--gray-200);
|
| 348 |
+
}
|
| 349 |
+
|
| 350 |
+
.auth-card h2 {
|
| 351 |
+
font-size: 2rem;
|
| 352 |
+
color: var(--primary);
|
| 353 |
+
margin-bottom: 1.5rem;
|
| 354 |
+
font-weight: 700;
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
.auth-card input[type="text"],
|
| 358 |
+
.auth-card input[type="password"],
|
| 359 |
+
.auth-card input[type="email"] {
|
| 360 |
+
width: 100%;
|
| 361 |
+
padding: 0.75rem 1rem;
|
| 362 |
+
margin-bottom: 1rem;
|
| 363 |
+
border: 1px solid var(--gray-300);
|
| 364 |
+
border-radius: var(--radius);
|
| 365 |
+
font-size: 1rem;
|
| 366 |
+
transition: all 0.2s ease;
|
| 367 |
+
}
|
| 368 |
+
|
| 369 |
+
.auth-card input:focus {
|
| 370 |
+
outline: none;
|
| 371 |
+
border-color: var(--primary);
|
| 372 |
+
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
.auth-card button {
|
| 376 |
+
width: 100%;
|
| 377 |
+
padding: 0.875rem;
|
| 378 |
+
font-size: 1rem;
|
| 379 |
+
font-weight: 600;
|
| 380 |
+
margin-top: 0.5rem;
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
.auth-card .form-switch {
|
| 384 |
+
margin-top: 1.25rem;
|
| 385 |
+
font-size: 0.875rem;
|
| 386 |
+
color: var(--gray-400);
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
.auth-card .form-switch a {
|
| 390 |
+
color: var(--primary);
|
| 391 |
+
text-decoration: none;
|
| 392 |
+
font-weight: 600;
|
| 393 |
+
transition: color 0.2s ease;
|
| 394 |
+
}
|
| 395 |
+
|
| 396 |
+
.auth-card .form-switch a:hover {
|
| 397 |
+
color: var(--primary-dark);
|
| 398 |
+
text-decoration: underline;
|
| 399 |
+
}
|
| 400 |
+
|
| 401 |
+
.logout-button {
|
| 402 |
+
position: absolute;
|
| 403 |
+
top: 1.5rem;
|
| 404 |
+
right: 1.5rem;
|
| 405 |
+
background-color: var(--secondary);
|
| 406 |
+
padding: 0.625rem 1.25rem;
|
| 407 |
+
font-size: 0.875rem;
|
| 408 |
+
}
|
| 409 |
+
|
| 410 |
+
.features-list {
|
| 411 |
+
list-style-type: none;
|
| 412 |
+
padding: 0;
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
.features-list li {
|
| 416 |
+
margin-bottom: 1rem;
|
| 417 |
+
padding-left: 1.5rem;
|
| 418 |
+
position: relative;
|
| 419 |
+
}
|
| 420 |
+
|
| 421 |
+
.features-list li::before {
|
| 422 |
+
content: '•';
|
| 423 |
+
position: absolute;
|
| 424 |
+
left: 0;
|
| 425 |
+
color: var(--primary);
|
| 426 |
+
font-weight: bold;
|
| 427 |
+
}
|
| 428 |
+
|
| 429 |
+
.contact-info p,
|
| 430 |
+
.feedback-form p {
|
| 431 |
+
margin-bottom: 1rem;
|
| 432 |
+
}
|
| 433 |
+
|
| 434 |
+
.feedback-form textarea {
|
| 435 |
+
width: 100%;
|
| 436 |
+
padding: 1rem;
|
| 437 |
+
margin-bottom: 1rem;
|
| 438 |
+
border: 1px solid var(--gray-300);
|
| 439 |
+
border-radius: var(--radius);
|
| 440 |
+
font-size: 1rem;
|
| 441 |
+
min-height: 120px;
|
| 442 |
+
resize: vertical;
|
| 443 |
+
transition: all 0.2s ease;
|
| 444 |
+
}
|
| 445 |
+
|
| 446 |
+
.feedback-form textarea:focus {
|
| 447 |
+
outline: none;
|
| 448 |
+
border-color: var(--primary);
|
| 449 |
+
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
|
| 450 |
+
}
|
| 451 |
+
|
| 452 |
+
.video-bg {
|
| 453 |
+
position: fixed;
|
| 454 |
+
top: 0;
|
| 455 |
+
left: 0;
|
| 456 |
+
width: 100%;
|
| 457 |
+
height: 100%;
|
| 458 |
+
overflow: hidden;
|
| 459 |
+
z-index: -1;
|
| 460 |
+
}
|
| 461 |
+
|
| 462 |
+
.video-bg video {
|
| 463 |
+
position: absolute;
|
| 464 |
+
top: 50%;
|
| 465 |
+
left: 50%;
|
| 466 |
+
min-width: 100%;
|
| 467 |
+
min-height: 100%;
|
| 468 |
+
width: auto;
|
| 469 |
+
height: auto;
|
| 470 |
+
transform: translate(-50%, -50%);
|
| 471 |
+
object-fit: cover;
|
| 472 |
+
filter: brightness(40%) saturate(120%);
|
| 473 |
+
}
|
| 474 |
+
|
| 475 |
+
@media (max-width: 640px) {
|
| 476 |
+
.container {
|
| 477 |
+
padding: 1rem;
|
| 478 |
+
}
|
| 479 |
+
|
| 480 |
+
header {
|
| 481 |
+
padding: 1.5rem 1rem;
|
| 482 |
+
}
|
| 483 |
+
|
| 484 |
+
h1 {
|
| 485 |
+
font-size: 2rem;
|
| 486 |
+
}
|
| 487 |
+
|
| 488 |
+
.card {
|
| 489 |
+
padding: 1.5rem;
|
| 490 |
+
}
|
| 491 |
+
|
| 492 |
+
nav {
|
| 493 |
+
gap: 1rem;
|
| 494 |
+
flex-wrap: wrap;
|
| 495 |
+
}
|
| 496 |
+
|
| 497 |
+
nav a {
|
| 498 |
+
padding: 0.375rem 0.75rem;
|
| 499 |
+
font-size: 0.875rem;
|
| 500 |
+
}
|
| 501 |
+
}
|
translation.html
ADDED
|
@@ -0,0 +1,443 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Translation API</title>
|
| 7 |
+
<style>
|
| 8 |
+
:root {
|
| 9 |
+
--primary-color: #4a90e2;
|
| 10 |
+
--secondary-color: #f8f9fa;
|
| 11 |
+
--text-color: #2c3e50;
|
| 12 |
+
--border-radius: 12px;
|
| 13 |
+
--transition: all 0.3s ease;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
* {
|
| 17 |
+
margin: 0;
|
| 18 |
+
padding: 0;
|
| 19 |
+
box-sizing: border-box;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
body {
|
| 23 |
+
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
| 24 |
+
line-height: 1.6;
|
| 25 |
+
color: var(--text-color);
|
| 26 |
+
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
| 27 |
+
min-height: 100vh;
|
| 28 |
+
padding: 2rem;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
.container {
|
| 32 |
+
max-width: 1000px;
|
| 33 |
+
margin: 0 auto;
|
| 34 |
+
background: white;
|
| 35 |
+
padding: 2rem;
|
| 36 |
+
border-radius: var(--border-radius);
|
| 37 |
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
h1 {
|
| 41 |
+
color: var(--primary-color);
|
| 42 |
+
text-align: center;
|
| 43 |
+
margin-bottom: 2rem;
|
| 44 |
+
font-size: 2.5rem;
|
| 45 |
+
font-weight: 700;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
h2 {
|
| 49 |
+
color: var(--text-color);
|
| 50 |
+
margin-bottom: 1.5rem;
|
| 51 |
+
font-size: 1.8rem;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
.tab {
|
| 55 |
+
display: flex;
|
| 56 |
+
gap: 1rem;
|
| 57 |
+
margin-bottom: 2rem;
|
| 58 |
+
border: none;
|
| 59 |
+
background: none;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
.tab button {
|
| 63 |
+
flex: 1;
|
| 64 |
+
padding: 1rem;
|
| 65 |
+
font-size: 1.1rem;
|
| 66 |
+
border: 2px solid var(--primary-color);
|
| 67 |
+
background: transparent;
|
| 68 |
+
color: var(--primary-color);
|
| 69 |
+
border-radius: var(--border-radius);
|
| 70 |
+
cursor: pointer;
|
| 71 |
+
transition: var(--transition);
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
.tab button:hover {
|
| 75 |
+
background: rgba(74, 144, 226, 0.1);
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
.tab button.active {
|
| 79 |
+
background: var(--primary-color);
|
| 80 |
+
color: white;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
.tabcontent {
|
| 84 |
+
display: none;
|
| 85 |
+
padding: 2rem;
|
| 86 |
+
background: var(--secondary-color);
|
| 87 |
+
border-radius: var(--border-radius);
|
| 88 |
+
margin-bottom: 2rem;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
label {
|
| 92 |
+
display: block;
|
| 93 |
+
margin-bottom: 0.5rem;
|
| 94 |
+
font-weight: 600;
|
| 95 |
+
color: var(--text-color);
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
textarea, input[type="text"] {
|
| 99 |
+
width: 100%;
|
| 100 |
+
padding: 1rem;
|
| 101 |
+
margin-bottom: 1.5rem;
|
| 102 |
+
border: 2px solid #e1e8ed;
|
| 103 |
+
border-radius: var(--border-radius);
|
| 104 |
+
font-size: 1rem;
|
| 105 |
+
transition: var(--transition);
|
| 106 |
+
background: white;
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
textarea {
|
| 110 |
+
min-height: 150px;
|
| 111 |
+
resize: vertical;
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
textarea:focus, input[type="text"]:focus {
|
| 115 |
+
outline: none;
|
| 116 |
+
border-color: var(--primary-color);
|
| 117 |
+
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
button {
|
| 121 |
+
background: var(--primary-color);
|
| 122 |
+
color: white;
|
| 123 |
+
padding: 1rem 2rem;
|
| 124 |
+
border: none;
|
| 125 |
+
border-radius: var(--border-radius);
|
| 126 |
+
font-size: 1.1rem;
|
| 127 |
+
cursor: pointer;
|
| 128 |
+
transition: var(--transition);
|
| 129 |
+
width: 100%;
|
| 130 |
+
margin-top: 1rem;
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
button:hover {
|
| 134 |
+
transform: translateY(-2px);
|
| 135 |
+
box-shadow: 0 5px 15px rgba(74, 144, 226, 0.3);
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
.loading-indicator {
|
| 139 |
+
display: none;
|
| 140 |
+
text-align: center;
|
| 141 |
+
color: var(--primary-color);
|
| 142 |
+
margin: 1rem 0;
|
| 143 |
+
font-weight: 600;
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
#translation-result, #detection-result {
|
| 147 |
+
background: white;
|
| 148 |
+
padding: 1.5rem;
|
| 149 |
+
border-radius: var(--border-radius);
|
| 150 |
+
margin-top: 2rem;
|
| 151 |
+
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
#error-message {
|
| 155 |
+
background: #fee2e2;
|
| 156 |
+
color: #dc2626;
|
| 157 |
+
padding: 1rem;
|
| 158 |
+
border-radius: var(--border-radius);
|
| 159 |
+
margin-top: 1rem;
|
| 160 |
+
display: none;
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
.result-header {
|
| 164 |
+
color: var(--primary-color);
|
| 165 |
+
margin-bottom: 1rem;
|
| 166 |
+
font-size: 1.3rem;
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
.result-item {
|
| 170 |
+
margin-bottom: 1rem;
|
| 171 |
+
padding-bottom: 1rem;
|
| 172 |
+
border-bottom: 1px solid #e1e8ed;
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
.result-item:last-child {
|
| 176 |
+
border-bottom: none;
|
| 177 |
+
margin-bottom: 0;
|
| 178 |
+
padding-bottom: 0;
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
.result-label {
|
| 182 |
+
font-weight: 600;
|
| 183 |
+
margin-bottom: 0.5rem;
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
#translation-options-list {
|
| 187 |
+
list-style: none;
|
| 188 |
+
margin-top: 1rem;
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
#translation-options-list li {
|
| 192 |
+
padding: 0.5rem 0;
|
| 193 |
+
border-bottom: 1px solid #e1e8ed;
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
#translation-options-list li:last-child {
|
| 197 |
+
border-bottom: none;
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
@media (max-width: 768px) {
|
| 201 |
+
body {
|
| 202 |
+
padding: 1rem;
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
.container {
|
| 206 |
+
padding: 1rem;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
.tab button {
|
| 210 |
+
padding: 0.8rem;
|
| 211 |
+
font-size: 1rem;
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
.tabcontent {
|
| 215 |
+
padding: 1rem;
|
| 216 |
+
}
|
| 217 |
+
}
|
| 218 |
+
</style>
|
| 219 |
+
</head>
|
| 220 |
+
<body>
|
| 221 |
+
<div class="container">
|
| 222 |
+
<h1>Translation & Language Detection</h1>
|
| 223 |
+
|
| 224 |
+
<div class="tab">
|
| 225 |
+
<button class="tablinks active" onclick="openTab(event, 'translate')">Translate</button>
|
| 226 |
+
<button class="tablinks" onclick="openTab(event, 'detect')">Detect Language</button>
|
| 227 |
+
</div>
|
| 228 |
+
|
| 229 |
+
<div id="translate" class="tabcontent" style="display: block;">
|
| 230 |
+
<h2>Text Translation</h2>
|
| 231 |
+
<label for="text-to-translate">Text to Translate</label>
|
| 232 |
+
<textarea id="text-to-translate" placeholder="Enter your text here..."></textarea>
|
| 233 |
+
|
| 234 |
+
<label for="source-language">Source Language (optional)</label>
|
| 235 |
+
<input type="text" id="source-language" placeholder="Leave empty for auto-detection">
|
| 236 |
+
|
| 237 |
+
<label for="target-language">Target Language</label>
|
| 238 |
+
<input type="text" id="target-language" placeholder="e.g., Spanish, French, German">
|
| 239 |
+
|
| 240 |
+
<button id="translate-button">Translate Text</button>
|
| 241 |
+
|
| 242 |
+
<div class="loading-indicator" id="translation-loading">
|
| 243 |
+
Translating your text...
|
| 244 |
+
</div>
|
| 245 |
+
|
| 246 |
+
<div id="translation-result">
|
| 247 |
+
<h3 class="result-header">Translation Results</h3>
|
| 248 |
+
<div class="result-item">
|
| 249 |
+
<div class="result-label">Source Language:</div>
|
| 250 |
+
<div id="detected-source-language"></div>
|
| 251 |
+
</div>
|
| 252 |
+
<div class="result-item">
|
| 253 |
+
<div class="result-label">Target Language:</div>
|
| 254 |
+
<div id="translation-target-language"></div>
|
| 255 |
+
</div>
|
| 256 |
+
<div class="result-item">
|
| 257 |
+
<div class="result-label">Translated Text:</div>
|
| 258 |
+
<div id="translated-text"></div>
|
| 259 |
+
</div>
|
| 260 |
+
</div>
|
| 261 |
+
</div>
|
| 262 |
+
|
| 263 |
+
<div id="detect" class="tabcontent">
|
| 264 |
+
<h2>Language Detection</h2>
|
| 265 |
+
<label for="text-to-detect">Text to Analyze</label>
|
| 266 |
+
<textarea id="text-to-detect" placeholder="Enter text to detect its language..."></textarea>
|
| 267 |
+
|
| 268 |
+
<button id="detect-button">Detect Language</button>
|
| 269 |
+
|
| 270 |
+
<div class="loading-indicator" id="detection-loading">
|
| 271 |
+
Detecting language...
|
| 272 |
+
</div>
|
| 273 |
+
|
| 274 |
+
<div id="detection-result">
|
| 275 |
+
<h3 class="result-header">Detection Results</h3>
|
| 276 |
+
<div class="result-item">
|
| 277 |
+
<div class="result-label">Detected Language:</div>
|
| 278 |
+
<div id="detected-language"></div>
|
| 279 |
+
</div>
|
| 280 |
+
<div id="translation-options-container">
|
| 281 |
+
<h4 class="result-header">Translation Options</h4>
|
| 282 |
+
<ul id="translation-options-list"></ul>
|
| 283 |
+
</div>
|
| 284 |
+
</div>
|
| 285 |
+
</div>
|
| 286 |
+
|
| 287 |
+
<div id="error-message"></div>
|
| 288 |
+
</div>
|
| 289 |
+
|
| 290 |
+
<script>
|
| 291 |
+
const API_BASE_URL = 'https://sameernotes-translation-prediction-space.hf.space';
|
| 292 |
+
const supportedLanguages = ["Afrikaans","Arabic","Armenian","Azerbaijani","Belarusian","Bosnian","Bulgarian","Catalan","Chinese","Croatian","Czech","Danish","Dutch","English","Estonian","Finnish","French","Galician","German","Greek","Hebrew","Hindi","Hungarian","Icelandic","Indonesian","Italian","Japanese","Kannada","Kazakh","Korean","Latvian","Lithuanian","Macedonian","Malay","Marathi","Maori","Nepali","Norwegian","Persian","Polish","Portuguese","Romanian","Russian","Serbian","Slovak","Slovenian","Spanish","Swahili","Swedish","Tagalog","Tamil","Thai","Turkish","Ukrainian","Urdu","Vietnamese","Welsh"];
|
| 293 |
+
|
| 294 |
+
function showError(message) {
|
| 295 |
+
const errorElement = document.getElementById('error-message');
|
| 296 |
+
errorElement.textContent = message;
|
| 297 |
+
errorElement.style.display = 'block';
|
| 298 |
+
setTimeout(() => {
|
| 299 |
+
errorElement.style.display = 'none';
|
| 300 |
+
}, 5000);
|
| 301 |
+
}
|
| 302 |
+
|
| 303 |
+
function clearError() {
|
| 304 |
+
document.getElementById('error-message').style.display = 'none';
|
| 305 |
+
}
|
| 306 |
+
|
| 307 |
+
function openTab(evt, tabName) {
|
| 308 |
+
const tabcontent = document.getElementsByClassName("tabcontent");
|
| 309 |
+
for (let i = 0; i < tabcontent.length; i++) {
|
| 310 |
+
tabcontent[i].style.display = "none";
|
| 311 |
+
}
|
| 312 |
+
|
| 313 |
+
const tablinks = document.getElementsByClassName("tablinks");
|
| 314 |
+
for (let i = 0; i < tablinks.length; i++) {
|
| 315 |
+
tablinks[i].className = tablinks[i].className.replace(" active", "");
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
document.getElementById(tabName).style.display = "block";
|
| 319 |
+
evt.currentTarget.className += " active";
|
| 320 |
+
|
| 321 |
+
document.getElementById('translation-result').style.display = 'none';
|
| 322 |
+
document.getElementById('detection-result').style.display = 'none';
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
document.getElementById('translate-button').addEventListener('click', async () => {
|
| 326 |
+
const text = document.getElementById('text-to-translate').value;
|
| 327 |
+
const sourceLanguage = document.getElementById('source-language').value;
|
| 328 |
+
const targetLanguage = document.getElementById('target-language').value;
|
| 329 |
+
|
| 330 |
+
clearError();
|
| 331 |
+
document.getElementById('translation-loading').style.display = 'block';
|
| 332 |
+
document.getElementById('translation-result').style.display = 'none';
|
| 333 |
+
|
| 334 |
+
if (!text) {
|
| 335 |
+
showError('Please enter text to translate.');
|
| 336 |
+
document.getElementById('translation-loading').style.display = 'none';
|
| 337 |
+
return;
|
| 338 |
+
}
|
| 339 |
+
if (!targetLanguage) {
|
| 340 |
+
showError('Please enter the target language.');
|
| 341 |
+
document.getElementById('translation-loading').style.display = 'none';
|
| 342 |
+
return;
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
if (!supportedLanguages.includes(targetLanguage)) {
|
| 346 |
+
showError(`Target language '${targetLanguage}' is not supported. Supported languages are: ${supportedLanguages.join(', ')}`);
|
| 347 |
+
document.getElementById('translation-loading').style.display = 'none';
|
| 348 |
+
return;
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
try {
|
| 352 |
+
const response = await fetch(`${API_BASE_URL}/translate`, {
|
| 353 |
+
method: 'POST',
|
| 354 |
+
headers: {
|
| 355 |
+
'Content-Type': 'application/json',
|
| 356 |
+
'accept': 'application/json'
|
| 357 |
+
},
|
| 358 |
+
body: JSON.stringify({
|
| 359 |
+
text: text,
|
| 360 |
+
source_language: sourceLanguage || undefined,
|
| 361 |
+
target_language: targetLanguage
|
| 362 |
+
})
|
| 363 |
+
});
|
| 364 |
+
|
| 365 |
+
if (!response.ok) {
|
| 366 |
+
const errorData = await response.json();
|
| 367 |
+
showError(`Translation failed: ${errorData.detail}`);
|
| 368 |
+
return;
|
| 369 |
+
}
|
| 370 |
+
|
| 371 |
+
const htmlResponse = await response.text();
|
| 372 |
+
|
| 373 |
+
document.getElementById('detected-source-language').textContent = sourceLanguage || "Auto-detected";
|
| 374 |
+
document.getElementById('translation-target-language').textContent = targetLanguage;
|
| 375 |
+
document.getElementById('translated-text').innerHTML = htmlResponse;
|
| 376 |
+
document.getElementById('translation-result').style.display = 'block';
|
| 377 |
+
|
| 378 |
+
} catch (error) {
|
| 379 |
+
showError(`Error: ${error.message}`);
|
| 380 |
+
console.error('Error during translation:', error);
|
| 381 |
+
} finally {
|
| 382 |
+
document.getElementById('translation-loading').style.display = 'none';
|
| 383 |
+
}
|
| 384 |
+
});
|
| 385 |
+
|
| 386 |
+
document.getElementById('detect-button').addEventListener('click', async () => {
|
| 387 |
+
const text = document.getElementById('text-to-detect').value;
|
| 388 |
+
|
| 389 |
+
clearError();
|
| 390 |
+
document.getElementById('detection-loading').style.display = 'block';
|
| 391 |
+
document.getElementById('detection-result').style.display = 'none';
|
| 392 |
+
|
| 393 |
+
if (!text) {
|
| 394 |
+
showError('Please enter text to detect its language.');
|
| 395 |
+
document.getElementById('detection-loading').style.display = 'none';
|
| 396 |
+
return;
|
| 397 |
+
}
|
| 398 |
+
|
| 399 |
+
try {
|
| 400 |
+
const response = await fetch(`${API_BASE_URL}/detect_language`, {
|
| 401 |
+
method: 'POST',
|
| 402 |
+
headers: {
|
| 403 |
+
'Content-Type': 'application/json',
|
| 404 |
+
'accept': 'application/json',
|
| 405 |
+
},
|
| 406 |
+
body: JSON.stringify({ text: text }),
|
| 407 |
+
});
|
| 408 |
+
|
| 409 |
+
if (!response.ok) {
|
| 410 |
+
const errorData = await response.json();
|
| 411 |
+
showError(`Detection failed: ${errorData.detail}`);
|
| 412 |
+
return;
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
const data = await response.json();
|
| 416 |
+
document.getElementById('detected-language').textContent = data.source_language;
|
| 417 |
+
|
| 418 |
+
const optionsList = document.getElementById('translation-options-list');
|
| 419 |
+
optionsList.innerHTML = '';
|
| 420 |
+
|
| 421 |
+
if (data.translation_options && typeof data.translation_options === 'object') {
|
| 422 |
+
for (const [key, value] of Object.entries(data.translation_options)) {
|
| 423 |
+
const listItem = document.createElement('li');
|
| 424 |
+
listItem.textContent = `${key}: ${value}`;
|
| 425 |
+
optionsList.appendChild(listItem);
|
| 426 |
+
}
|
| 427 |
+
document.getElementById('translation-options-container').style.display = 'block';
|
| 428 |
+
} else {
|
| 429 |
+
document.getElementById('translation-options-container').style.display = 'none';
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
document.getElementById('detection-result').style.display = 'block';
|
| 433 |
+
|
| 434 |
+
} catch (error) {
|
| 435 |
+
showError(`Error: ${error.message}`);
|
| 436 |
+
console.error('Error during language detection:', error);
|
| 437 |
+
} finally {
|
| 438 |
+
document.getElementById('detection-loading').style.display = 'none';
|
| 439 |
+
}
|
| 440 |
+
});
|
| 441 |
+
</script>
|
| 442 |
+
</body>
|
| 443 |
+
</html>
|