this repo has no description
at main 12 kB view raw
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>JMAP Email Client</title> 7 <style> 8 :root { 9 --bg-color: #1a1a2e; 10 --card-bg: #16213e; 11 --accent: #0f3460; 12 --highlight: #e94560; 13 --text: #eee; 14 --text-muted: #888; 15 --success: #4ade80; 16 --error: #f87171; 17 --warning: #fbbf24; 18 } 19 20 * { 21 box-sizing: border-box; 22 margin: 0; 23 padding: 0; 24 } 25 26 body { 27 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; 28 background: var(--bg-color); 29 color: var(--text); 30 min-height: 100vh; 31 padding: 20px; 32 } 33 34 .container { 35 max-width: 1200px; 36 margin: 0 auto; 37 } 38 39 header { 40 text-align: center; 41 margin-bottom: 30px; 42 } 43 44 h1 { 45 font-size: 2rem; 46 margin-bottom: 10px; 47 } 48 49 h1 span { 50 color: var(--highlight); 51 } 52 53 .subtitle { 54 color: var(--text-muted); 55 font-size: 0.9rem; 56 } 57 58 /* Top Section - Two Column Layout */ 59 .top-section { 60 display: grid; 61 grid-template-columns: 1fr 1fr; 62 gap: 20px; 63 margin-bottom: 20px; 64 } 65 66 @media (max-width: 800px) { 67 .top-section { 68 grid-template-columns: 1fr; 69 } 70 } 71 72 /* Login Form */ 73 .login-card { 74 background: var(--card-bg); 75 border-radius: 12px; 76 padding: 16px; 77 box-shadow: 0 4px 20px rgba(0,0,0,0.3); 78 } 79 80 .login-card h3 { 81 margin-bottom: 12px; 82 font-size: 0.9rem; 83 color: var(--text-muted); 84 text-transform: uppercase; 85 letter-spacing: 1px; 86 } 87 88 .form-row { 89 display: flex; 90 gap: 12px; 91 margin-bottom: 12px; 92 } 93 94 .form-group { 95 flex: 1; 96 margin-bottom: 12px; 97 } 98 99 .form-group:last-child { 100 margin-bottom: 0; 101 } 102 103 .form-group.small { 104 flex: 0.4; 105 } 106 107 label { 108 display: block; 109 margin-bottom: 4px; 110 font-weight: 500; 111 font-size: 0.75rem; 112 color: var(--text-muted); 113 } 114 115 input[type="text"], 116 input[type="password"] { 117 width: 100%; 118 padding: 8px 12px; 119 border: 2px solid var(--accent); 120 border-radius: 6px; 121 background: var(--bg-color); 122 color: var(--text); 123 font-size: 0.85rem; 124 transition: border-color 0.2s; 125 } 126 127 input:focus { 128 outline: none; 129 border-color: var(--highlight); 130 } 131 132 .btn-row { 133 display: flex; 134 gap: 8px; 135 } 136 137 .btn { 138 flex: 1; 139 padding: 10px; 140 border: none; 141 border-radius: 6px; 142 font-size: 0.85rem; 143 font-weight: 600; 144 cursor: pointer; 145 transition: transform 0.1s, opacity 0.2s; 146 } 147 148 .btn:hover { 149 transform: translateY(-1px); 150 } 151 152 .btn:active { 153 transform: translateY(0); 154 } 155 156 .btn:disabled { 157 opacity: 0.5; 158 cursor: not-allowed; 159 transform: none; 160 } 161 162 .btn-primary { 163 background: var(--highlight); 164 color: white; 165 } 166 167 .btn-secondary { 168 background: var(--accent); 169 color: var(--text); 170 } 171 172 /* Status/Log Panel */ 173 .log-panel { 174 background: var(--card-bg); 175 border-radius: 12px; 176 padding: 20px; 177 margin-bottom: 30px; 178 max-height: 500px; 179 overflow-y: auto; 180 } 181 182 .log-panel h3 { 183 margin-bottom: 15px; 184 font-size: 0.9rem; 185 color: var(--text-muted); 186 text-transform: uppercase; 187 letter-spacing: 1px; 188 } 189 190 .log-entry { 191 font-family: 'SF Mono', Monaco, 'Courier New', monospace; 192 font-size: 0.85rem; 193 padding: 8px 0; 194 border-bottom: 1px solid var(--accent); 195 } 196 197 .log-entry:last-child { 198 border-bottom: none; 199 } 200 201 .log-entry-header { 202 display: flex; 203 align-items: center; 204 gap: 8px; 205 } 206 207 .log-info .log-entry-header { color: var(--text); } 208 .log-success .log-entry-header { color: var(--success); } 209 .log-error .log-entry-header { color: var(--error); } 210 .log-warning .log-entry-header { color: var(--warning); } 211 212 .log-time { 213 color: var(--text-muted); 214 font-size: 0.8rem; 215 flex-shrink: 0; 216 } 217 218 .log-message { 219 flex: 1; 220 } 221 222 .log-expand-btn { 223 background: var(--accent); 224 border: none; 225 color: var(--text-muted); 226 padding: 2px 8px; 227 border-radius: 4px; 228 font-size: 0.7rem; 229 cursor: pointer; 230 font-family: inherit; 231 transition: background 0.2s, color 0.2s; 232 flex-shrink: 0; 233 } 234 235 .log-expand-btn:hover { 236 background: var(--highlight); 237 color: white; 238 } 239 240 .log-expand-btn.expanded { 241 background: var(--highlight); 242 color: white; 243 } 244 245 /* JSON content within log entry */ 246 .log-json { 247 display: none; 248 margin-top: 8px; 249 border-radius: 8px; 250 overflow: hidden; 251 } 252 253 .log-json.visible { 254 display: block; 255 } 256 257 .log-json-header { 258 padding: 6px 12px; 259 font-size: 0.75rem; 260 font-weight: 600; 261 display: flex; 262 justify-content: space-between; 263 align-items: center; 264 } 265 266 .log-json.request .log-json-header { 267 background: var(--accent); 268 color: var(--highlight); 269 } 270 271 .log-json.response .log-json-header { 272 background: #1a3a2e; 273 color: var(--success); 274 } 275 276 .log-json-body { 277 background: var(--bg-color); 278 padding: 12px; 279 font-size: 0.75rem; 280 line-height: 1.4; 281 white-space: pre-wrap; 282 word-break: break-all; 283 max-height: 300px; 284 overflow-y: auto; 285 color: var(--text-muted); 286 } 287 288 .log-json-body.collapsed { 289 max-height: 100px; 290 } 291 292 .json-toggle-size { 293 background: none; 294 border: none; 295 color: inherit; 296 cursor: pointer; 297 font-size: 0.7rem; 298 opacity: 0.7; 299 } 300 301 .json-toggle-size:hover { 302 opacity: 1; 303 } 304 305 /* Session Info */ 306 .session-info { 307 background: var(--card-bg); 308 border-radius: 12px; 309 padding: 16px; 310 display: none; 311 box-shadow: 0 4px 20px rgba(0,0,0,0.3); 312 } 313 314 .session-info.visible { 315 display: block; 316 } 317 318 .session-info h3 { 319 margin-bottom: 12px; 320 font-size: 0.9rem; 321 color: var(--success); 322 text-transform: uppercase; 323 letter-spacing: 1px; 324 } 325 326 .session-detail { 327 display: flex; 328 margin-bottom: 6px; 329 font-size: 0.85rem; 330 } 331 332 .session-detail .label { 333 width: 100px; 334 color: var(--text-muted); 335 flex-shrink: 0; 336 } 337 338 .session-detail .value { 339 color: var(--text); 340 word-break: break-all; 341 font-family: 'SF Mono', Monaco, 'Courier New', monospace; 342 font-size: 0.8rem; 343 } 344 345 .search-box { 346 margin-top: 12px; 347 padding-top: 12px; 348 border-top: 1px solid var(--accent); 349 display: flex; 350 gap: 8px; 351 } 352 353 .search-box input { 354 flex: 1; 355 padding: 8px 12px; 356 border: 2px solid var(--accent); 357 border-radius: 6px; 358 background: var(--bg-color); 359 color: var(--text); 360 font-size: 0.85rem; 361 } 362 363 .search-box input:focus { 364 outline: none; 365 border-color: var(--highlight); 366 } 367 368 .btn-small { 369 flex: 0; 370 padding: 8px 16px; 371 white-space: nowrap; 372 } 373 374 /* Email List */ 375 .email-list { 376 display: none; 377 } 378 379 .email-list.visible { 380 display: block; 381 } 382 383 .email-list h2 { 384 margin-bottom: 20px; 385 } 386 387 .email-item { 388 background: var(--card-bg); 389 border-radius: 8px; 390 padding: 16px 20px; 391 margin-bottom: 12px; 392 cursor: pointer; 393 transition: background 0.2s, transform 0.1s; 394 border-left: 4px solid transparent; 395 } 396 397 .email-item:hover { 398 background: var(--accent); 399 transform: translateX(4px); 400 } 401 402 .email-item.unread { 403 border-left-color: var(--highlight); 404 } 405 406 .email-header { 407 display: flex; 408 justify-content: space-between; 409 align-items: flex-start; 410 margin-bottom: 8px; 411 } 412 413 .email-from { 414 font-weight: 600; 415 font-size: 1rem; 416 } 417 418 .email-date { 419 color: var(--text-muted); 420 font-size: 0.85rem; 421 } 422 423 .email-subject { 424 font-size: 0.95rem; 425 color: var(--text); 426 margin-bottom: 6px; 427 } 428 429 .email-preview { 430 color: var(--text-muted); 431 font-size: 0.85rem; 432 white-space: nowrap; 433 overflow: hidden; 434 text-overflow: ellipsis; 435 } 436 437 .email-keywords { 438 margin-top: 8px; 439 } 440 441 .keyword-tag { 442 display: inline-block; 443 background: var(--accent); 444 color: var(--text-muted); 445 padding: 2px 8px; 446 border-radius: 4px; 447 font-size: 0.75rem; 448 margin-right: 6px; 449 } 450 451 .keyword-tag.flagged { 452 background: var(--warning); 453 color: var(--bg-color); 454 } 455 456 /* Loading spinner */ 457 .spinner { 458 display: inline-block; 459 width: 20px; 460 height: 20px; 461 border: 2px solid var(--text-muted); 462 border-top-color: var(--highlight); 463 border-radius: 50%; 464 animation: spin 0.8s linear infinite; 465 margin-right: 10px; 466 vertical-align: middle; 467 } 468 469 @keyframes spin { 470 to { transform: rotate(360deg); } 471 } 472 473 /* Responsive */ 474 @media (max-width: 600px) { 475 body { 476 padding: 10px; 477 } 478 479 .login-card { 480 padding: 20px; 481 } 482 483 h1 { 484 font-size: 1.5rem; 485 } 486 } 487 </style> 488</head> 489<body> 490 <div class="container"> 491 <header> 492 <h1>JMAP <span>Email Client</span></h1> 493 <p class="subtitle">Built with OCaml and Brr</p> 494 </header> 495 496 <!-- Top Section: Login + Session Info --> 497 <div class="top-section"> 498 <!-- Login Form --> 499 <div class="login-card" id="login-card"> 500 <h3>Connection</h3> 501 <div class="form-group"> 502 <label for="session-url">Session URL</label> 503 <input type="text" id="session-url" 504 value="https://api.fastmail.com/jmap/session" 505 placeholder="https://api.fastmail.com/jmap/session"> 506 </div> 507 <div class="form-row"> 508 <div class="form-group"> 509 <label for="api-token">API Token</label> 510 <input type="password" id="api-token" 511 placeholder="Enter your JMAP API token"> 512 </div> 513 </div> 514 <div class="btn-row"> 515 <button class="btn btn-primary" id="connect-btn">Connect</button> 516 <button class="btn btn-secondary" id="disconnect-btn" style="display: none;">Disconnect</button> 517 </div> 518 </div> 519 520 <!-- Session Info --> 521 <div class="session-info" id="session-info"> 522 <h3>Connected</h3> 523 <div class="session-detail"> 524 <span class="label">Username:</span> 525 <span class="value" id="session-username">-</span> 526 </div> 527 <div class="session-detail"> 528 <span class="label">API URL:</span> 529 <span class="value" id="session-api-url">-</span> 530 </div> 531 <div class="session-detail"> 532 <span class="label">Account ID:</span> 533 <span class="value" id="session-account-id">-</span> 534 </div> 535 <div class="search-box"> 536 <input type="text" id="email-search" placeholder="Search emails..."> 537 <button class="btn btn-primary btn-small" id="search-btn">Search</button> 538 </div> 539 </div> 540 </div> 541 542 <!-- Log Panel with expandable JSON --> 543 <div class="log-panel" id="log-panel"> 544 <h3>Activity Log</h3> 545 <div id="log-entries"></div> 546 </div> 547 548 <!-- Email List --> 549 <div class="email-list" id="email-list"> 550 <h2>Recent Emails</h2> 551 <div id="emails"></div> 552 </div> 553 </div> 554 555 <script type="text/javascript" defer src="brr.js"></script> 556 <noscript> 557 <p style="text-align: center; padding: 50px; color: #888;"> 558 Please enable JavaScript to use this application. 559 </p> 560 </noscript> 561</body> 562</html>