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

fix: update terminology and improve UX

- replace "third-party" with "atproto" terminology throughout
- improve app circle sizing and layout
- add window resize handler for responsive positioning
- remove redundant identity hint text

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

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

Changed files
+41 -13
src
static
+3 -3
README.md
··· 1 1 # @me 2 2 3 - an accessible visualization of how your atproto identity connects to third-party apps. 3 + an accessible visualization of how your atproto identity connects to atproto apps. 4 4 5 5 [at-me.fly.dev](https://at-me.fly.dev/) 6 6 7 7 ## what is this 8 8 9 - in decentralized social networks, you own your identity and your data lives in your personal data server. third-party applications create records in your repository using different lexicons (data schemas). 9 + in decentralized social networks, you own your identity and your data lives in your personal data server. atproto applications create records in your repository using different lexicons (data schemas). 10 10 11 - @me shows this visually: your identity at the center, surrounded by the third-party apps that have created data for you. click an app to see what record types it stores, then click a record type to view the actual data. 11 + @me shows this visually: your identity at the center, surrounded by the atproto apps that have created data for you. click an app to see what record types it stores, then click a record type to view the actual data. 12 12 13 13 inspired by [pdsls.dev](https://pdsls.dev). 14 14
+2 -9
src/templates.rs
··· 735 735 line-height: 1.2; 736 736 }} 737 737 738 - .identity-hint {{ 739 - font-size: clamp(0.35rem, 0.8vmin, 0.45rem); 740 - color: var(--text-lighter); 741 - margin-top: clamp(0.4rem, 1.5vmin, 0.6rem); 742 - letter-spacing: 0.05em; 743 - }} 744 738 745 739 .identity-pds-label {{ 746 740 position: absolute; ··· 781 775 background: var(--surface-hover); 782 776 border: 1px solid var(--border); 783 777 border-radius: 50%; 784 - width: clamp(45px, 8vmin, 60px); 785 - height: clamp(45px, 8vmin, 60px); 778 + width: clamp(55px, 10vmin, 70px); 779 + height: clamp(55px, 10vmin, 70px); 786 780 display: flex; 787 781 align-items: center; 788 782 justify-content: center; ··· 1636 1630 <div class="identity"> 1637 1631 <div class="identity-label">@</div> 1638 1632 <div class="identity-value" id="handle">loading...</div> 1639 - <div class="identity-hint">tap for details</div> 1640 1633 <div class="identity-pds-label">Your PDS</div> 1641 1634 </div> 1642 1635 <div id="field" class="loading">loading...</div>
+35
static/app.js
··· 4 4 5 5 let globalPds = null; 6 6 let globalHandle = null; 7 + let globalApps = null; // Store apps for repositioning on resize 7 8 8 9 // Fetch app avatar from server 9 10 async function fetchAppAvatar(namespace) { ··· 62 63 apps[app.namespace] = app.collections; 63 64 allCollections.push(...app.collections); 64 65 }); 66 + 67 + // Store apps globally for repositioning 68 + globalApps = apps; 65 69 66 70 // Add identity click handler now that we have the data 67 71 const pdsHost = globalPds.replace('https://', '').replace('http://', ''); ··· 466 470 document.getElementById('detail').classList.remove('visible'); 467 471 } 468 472 }); 473 + 474 + // Add window resize handler to reposition app circles 475 + let resizeTimeout; 476 + window.addEventListener('resize', () => { 477 + clearTimeout(resizeTimeout); 478 + resizeTimeout = setTimeout(() => { 479 + repositionAppCircles(); 480 + }, 100); // Debounce resize events 481 + }); 469 482 }) 470 483 .catch(e => { 471 484 document.getElementById('field').innerHTML = 'error loading records'; 472 485 console.error(e); 473 486 }); 487 + 488 + // Function to reposition app circles on window resize 489 + function repositionAppCircles() { 490 + if (!globalApps) return; 491 + 492 + const appViews = document.querySelectorAll('.app-view'); 493 + const appNames = Object.keys(globalApps).sort(); 494 + 495 + const vmin = Math.min(window.innerWidth, window.innerHeight); 496 + const radius = Math.max(vmin * 0.35, 150); 497 + const centerX = window.innerWidth / 2; 498 + const centerY = window.innerHeight / 2; 499 + 500 + appViews.forEach((div, i) => { 501 + const angle = (i / appNames.length) * 2 * Math.PI - Math.PI / 2; 502 + const x = centerX + radius * Math.cos(angle) - 30; 503 + const y = centerY + radius * Math.sin(angle) - 30; 504 + 505 + div.style.left = `${x}px`; 506 + div.style.top = `${y}px`; 507 + }); 508 + } 474 509 475 510 // MST Visualization Functions 476 511 async function loadMSTStructure(lexicon, containerView) {
+1 -1
static/onboarding.js
··· 10 10 }, 11 11 { 12 12 target: '.canvas', 13 - title: 'third-party applications', 13 + title: 'atproto applications', 14 14 description: 'these apps use your global identity to write public records to your PDS. they can also read records you\'ve created.', 15 15 position: 'center' 16 16 },