interactive intro to open social at-me.zzstoatzz.io

fix: improve ux with logout, load more pagination, and interactive identity

- fix logout button to clear localStorage before redirecting
- fix load more button using event delegation for dynamic buttons
- add interactive identity circle with pds info on click
- improve record display styling with better spacing and hover effects
- improve overlay behavior to close all panels

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

+68 -10
+68 -10
src/templates.rs
··· 248 248 justify-content: center; 249 249 gap: 0.3rem; 250 250 z-index: 10; 251 + cursor: pointer; 252 + transition: all 0.2s ease; 253 + }} 254 + 255 + .identity:hover {{ 256 + transform: scale(1.05); 257 + border-color: var(--text); 258 + box-shadow: 0 0 20px rgba(255, 255, 255, 0.1); 251 259 }} 252 260 253 261 .identity-label {{ ··· 379 387 }} 380 388 381 389 .record {{ 382 - padding: 0.5rem; 390 + padding: 0.6rem; 383 391 margin-bottom: 0.5rem; 384 - background: var(--surface); 385 - border-radius: 2px; 392 + background: var(--bg); 393 + border: 1px solid var(--border); 394 + border-radius: 4px; 386 395 font-size: 0.65rem; 387 396 color: var(--text-light); 397 + transition: all 0.15s ease; 398 + }} 399 + 400 + .record:hover {{ 401 + border-color: var(--text-light); 402 + background: var(--surface); 388 403 }} 389 404 390 405 .record:last-child {{ ··· 395 410 margin: 0; 396 411 white-space: pre-wrap; 397 412 word-break: break-word; 413 + line-height: 1.5; 398 414 }} 399 415 400 416 .load-more {{ ··· 440 456 </head> 441 457 <body> 442 458 <div class="info" id="infoBtn">i</div> 443 - <a href="/logout" class="logout">logout</a> 459 + <a href="javascript:void(0)" id="logoutBtn" class="logout">logout</a> 444 460 445 461 <div class="overlay" id="overlay"></div> 446 462 <div class="info-modal" id="infoModal"> ··· 468 484 localStorage.setItem('atme_did', did); 469 485 470 486 let globalPds = null; 487 + let globalHandle = null; 488 + 489 + // Logout handler 490 + document.getElementById('logoutBtn').addEventListener('click', (e) => {{ 491 + e.preventDefault(); 492 + localStorage.removeItem('atme_did'); 493 + window.location.href = '/logout'; 494 + }}); 471 495 472 496 // Info modal handlers 473 497 document.getElementById('infoBtn').addEventListener('click', () => {{ ··· 483 507 document.getElementById('overlay').addEventListener('click', () => {{ 484 508 document.getElementById('infoModal').classList.remove('visible'); 485 509 document.getElementById('overlay').classList.remove('visible'); 510 + const detail = document.getElementById('detail'); 511 + detail.classList.remove('visible'); 486 512 }}); 487 513 488 514 // First resolve DID to get PDS endpoint and handle ··· 493 519 const handle = didDoc.alsoKnownAs?.[0]?.replace('at://', '') || did; 494 520 495 521 globalPds = pds; 522 + globalHandle = handle; 496 523 497 524 // Update identity display with handle 498 525 document.getElementById('handle').textContent = '@' + handle; 499 526 527 + // Add identity click handler to show PDS info 528 + document.querySelector('.identity').addEventListener('click', () => {{ 529 + const detail = document.getElementById('detail'); 530 + detail.innerHTML = ` 531 + <h3>your identity</h3> 532 + <div class="subtitle">decentralized identifier & storage</div> 533 + <div class="tree-item"> 534 + <div class="tree-item-header"> 535 + <span style="color: var(--text-light);">did</span> 536 + <span style="font-size: 0.6rem; color: var(--text);">${{did}}</span> 537 + </div> 538 + </div> 539 + <div class="tree-item"> 540 + <div class="tree-item-header"> 541 + <span style="color: var(--text-light);">handle</span> 542 + <span style="font-size: 0.6rem; color: var(--text);">@${{handle}}</span> 543 + </div> 544 + </div> 545 + <div class="tree-item"> 546 + <div class="tree-item-header"> 547 + <span style="color: var(--text-light);">personal data server</span> 548 + <span style="font-size: 0.6rem; color: var(--text);">${{pds}}</span> 549 + </div> 550 + </div> 551 + <div style="margin-top: 1rem; padding: 0.6rem; background: var(--bg); border-radius: 4px; font-size: 0.65rem; line-height: 1.5; color: var(--text-lighter);"> 552 + your data lives in your personal data server. third-party apps write to and read from your pds using the atproto protocol. you control your identity and can move to a different pds anytime. 553 + </div> 554 + `; 555 + detail.classList.add('visible'); 556 + }}); 557 + 500 558 // Get all collections from PDS 501 559 return fetch(`${{pds}}/xrpc/com.atproto.repo.describeRepo?repo=${{did}}`); 502 560 }}) ··· 626 684 627 685 recordListDiv.innerHTML = recordsHtml; 628 686 629 - // Add handler for load more button 630 - const loadMoreBtn = recordListDiv.querySelector('.load-more'); 631 - if (loadMoreBtn) {{ 632 - loadMoreBtn.addEventListener('click', (e) => {{ 687 + // Use event delegation for load more buttons 688 + recordListDiv.addEventListener('click', (e) => {{ 689 + if (e.target.classList.contains('load-more')) {{ 633 690 e.stopPropagation(); 691 + const loadMoreBtn = e.target; 634 692 const cursor = loadMoreBtn.dataset.cursor; 635 693 const lexicon = loadMoreBtn.dataset.lexicon; 636 694 ··· 654 712 ); 655 713 }} 656 714 }}); 657 - }}); 658 - }} 715 + }} 716 + }}); 659 717 }} else {{ 660 718 recordListDiv.innerHTML = '<div class="record">no records found</div>'; 661 719 }}