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

feat: add domain redirect mapping for moved ATProto apps

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

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

+23 -6
+23 -6
static/app.js
··· 2 2 const did = window.DID; 3 3 localStorage.setItem('atme_did', did); 4 4 5 + // Domain redirects - map old domains to their new canonical domains 6 + // Some ATProto apps have moved domains but their lexicons still use the old namespace 7 + const DOMAIN_REDIRECTS = { 8 + 'tangled.sh': 'tangled.org', 9 + }; 10 + 11 + // Apply domain redirect if one exists 12 + function applyDomainRedirect(domain) { 13 + return DOMAIN_REDIRECTS[domain] || domain; 14 + } 15 + 5 16 let globalPds = null; 6 17 let globalHandle = null; 7 18 let globalApps = null; // Store apps for repositioning on resize ··· 147 158 const appNames = Object.keys(globalApps).filter(k => k !== '_circleSize').sort(); 148 159 149 160 filterList.innerHTML = appNames.map(namespace => { 150 - const displayName = namespace.split('.').reverse().join('.'); 161 + const rawDisplayName = namespace.split('.').reverse().join('.'); 162 + const displayName = applyDomainRedirect(rawDisplayName); 151 163 const isChecked = !hiddenApps.has(namespace); 152 164 return ` 153 165 <div class="filter-item ${isChecked ? 'checked' : ''}" data-namespace="${namespace}"> ··· 525 537 const firstLetter = namespace.split('.')[1]?.[0]?.toUpperCase() || namespace[0].toUpperCase(); 526 538 527 539 // Reverse namespace for display (app.bsky -> bsky.app) 528 - const displayName = namespace.split('.').reverse().join('.'); 540 + const rawDisplayName = namespace.split('.').reverse().join('.'); 541 + const displayName = applyDomainRedirect(rawDisplayName); 529 542 const url = `https://${displayName}`; 530 543 531 544 div.innerHTML = ` ··· 552 565 553 566 appDivs.forEach(({ div, namespace }, i) => { 554 567 // Reverse namespace for display and create URL 555 - const displayName = namespace.split('.').reverse().join('.'); 568 + const rawDisplayName = namespace.split('.').reverse().join('.'); 569 + const displayName = applyDomainRedirect(rawDisplayName); 556 570 const url = `https://${displayName}`; 557 571 558 572 // Validate URL ··· 578 592 const collections = apps[namespace]; 579 593 580 594 // Reverse namespace for display and create URL 581 - const displayName = namespace.split('.').reverse().join('.'); 595 + const rawDisplayName = namespace.split('.').reverse().join('.'); 596 + const displayName = applyDomainRedirect(rawDisplayName); 582 597 const appUrl = `https://${displayName}`; 583 598 584 599 // Check if the link was already validated as invalid ··· 1524 1539 // If app circle doesn't exist and this is a create event, add it dynamically 1525 1540 if (!appCircle && event.action === 'create') { 1526 1541 // Reverse namespace for URL (app.at-me -> at-me.app) 1527 - const displayName = event.namespace.split('.').reverse().join('.'); 1542 + const rawDisplayName = event.namespace.split('.').reverse().join('.'); 1543 + const displayName = applyDomainRedirect(rawDisplayName); 1528 1544 const url = `https://${displayName}`; 1529 1545 1530 1546 // Add the app circle with the collection from the event ··· 1781 1797 const firstLetter = namespace.split('.')[1]?.[0]?.toUpperCase() || namespace[0].toUpperCase(); 1782 1798 1783 1799 // Reverse namespace for display (app.at-me -> at-me.app) 1784 - const displayName = namespace.split('.').reverse().join('.'); 1800 + const rawDisplayName = namespace.split('.').reverse().join('.'); 1801 + const displayName = applyDomainRedirect(rawDisplayName); 1785 1802 1786 1803 div.innerHTML = ` 1787 1804 <div class="app-circle" data-namespace="${namespace}" style="width: ${circleSize}px; height: ${circleSize}px; font-size: ${circleSize * 0.4}px;">${firstLetter}</div>