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

fix: add view/index.html to serve /view/ path directly

Wisp's cleanUrls and _redirects have issues with query params.
Using a directory with index.html instead.

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

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

+170 -1
+168
view/index.html
···
··· 1 + <!DOCTYPE html> 2 + <html> 3 + 4 + <head> 5 + <meta charset="UTF-8"> 6 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 + <title>@me - explore your atproto identity</title> 8 + <link rel="icon" type="image/svg+xml" href="/favicon.svg"> 9 + 10 + <!-- Open Graph / Facebook --> 11 + <meta property="og:type" content="website"> 12 + <meta property="og:url" content="https://at-me.wisp.place/"> 13 + <meta property="og:title" content="@me - explore your atproto identity"> 14 + <meta property="og:description" 15 + content="visualize your decentralized identity and see what apps have stored data in your Personal Data Server"> 16 + <meta property="og:image" content="https://at-me.wisp.place/og-image.png"> 17 + 18 + <!-- Twitter --> 19 + <meta property="twitter:card" content="summary_large_image"> 20 + <meta property="twitter:url" content="https://at-me.wisp.place/"> 21 + <meta property="twitter:title" content="@me - explore your atproto identity"> 22 + <meta property="twitter:description" 23 + content="visualize your decentralized identity and see what apps have stored data in your Personal Data Server"> 24 + <meta property="twitter:image" content="https://at-me.wisp.place/og-image.png"> 25 + </head> 26 + 27 + <body> 28 + <a href="/" class="home-btn" title="back to landing"> 29 + <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" 30 + stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 31 + <path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /> 32 + <polyline points="9 22 9 12 15 12 15 22" /> 33 + </svg> 34 + </a> 35 + <div class="info" id="infoBtn" title="learn about your data"> 36 + <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" 37 + stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 38 + <circle cx="12" cy="12" r="10" /> 39 + <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" /> 40 + <path d="M12 17h.01" /> 41 + </svg> 42 + </div> 43 + <div class="top-right-buttons"> 44 + <button class="filter-btn" id="filterBtn"> 45 + <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" 46 + stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 47 + <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" /> 48 + </svg> 49 + <span class="filter-label-text">filter</span> 50 + <span class="filter-count" id="filterCount" style="display: none;"></span> 51 + </button> 52 + <button class="watch-live-btn" id="watchLiveBtn"> 53 + <span class="watch-indicator"></span> 54 + <span class="watch-label">watch live</span> 55 + </button> 56 + </div> 57 + <div class="filter-panel" id="filterPanel"> 58 + <div class="filter-panel-header"> 59 + <span class="filter-panel-title">show apps</span> 60 + <div class="filter-panel-actions"> 61 + <button type="button" class="filter-action-btn" id="filterShowAll">all</button> 62 + <button type="button" class="filter-action-btn" id="filterHideUnresolved">valid</button> 63 + <button type="button" class="filter-action-btn" id="filterHideAll">none</button> 64 + </div> 65 + </div> 66 + <div class="filter-list" id="filterList"></div> 67 + </div> 68 + <div class="pov-indicator">point of view of <a class="pov-handle" id="povHandle" href="#" target="_blank" 69 + rel="noopener noreferrer"></a></div> 70 + <div class="guestbook-sign">sign the guest list</div> 71 + <div class="guestbook-buttons-container"> 72 + <button class="view-guestbook-btn" id="viewGuestbookBtn" title="view all signatures"> 73 + <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" 74 + stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 75 + <line x1="8" x2="21" y1="6" y2="6" /> 76 + <line x1="8" x2="21" y1="12" y2="12" /> 77 + <line x1="8" x2="21" y1="18" y2="18" /> 78 + <line x1="3" x2="3.01" y1="6" y2="6" /> 79 + <line x1="3" x2="3.01" y1="12" y2="12" /> 80 + <line x1="3" x2="3.01" y1="18" y2="18" /> 81 + </svg> 82 + </button> 83 + <button class="sign-guestbook-btn" id="signGuestbookBtn" title="sign the guestbook"> 84 + <span class="guestbook-icon"> 85 + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" 86 + stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 87 + <path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1 0-5H20" /> 88 + </svg> 89 + </span> 90 + <span class="guestbook-text">sign guestbook</span> 91 + <img class="guestbook-avatar" id="guestbookAvatar" style="display: none;" /> 92 + </button> 93 + </div> 94 + 95 + <div class="firehose-toast" id="firehoseToast"> 96 + <div class="firehose-toast-action"></div> 97 + <div class="firehose-toast-collection"></div> 98 + <a class='firehose-toast-link' id='firehoseToastLink' href='#' target='_blank' rel='noopener noreferrer'>view 99 + record</a> 100 + </div> 101 + 102 + <div class="overlay" id="overlay"></div> 103 + <div class="info-modal" id="infoModal"> 104 + <h2>this is your data</h2> 105 + <p>this visualization shows your <a href="https://atproto.com/guides/data-repos" target="_blank" 106 + rel="noopener noreferrer" style="color: var(--text); text-decoration: underline;">Personal Data 107 + Server</a> - where your social data actually lives. unlike traditional platforms that lock everything in 108 + their database, your posts, likes, and follows are stored here, on infrastructure you control.</p> 109 + <p>each circle represents an app that writes to your space. <a href="https://bsky.app" target="_blank" 110 + rel="noopener noreferrer" style="color: var(--text); text-decoration: underline;">bluesky</a> for 111 + microblogging. <a href="https://whtwnd.com" target="_blank" rel="noopener noreferrer" 112 + style="color: var(--text); text-decoration: underline;">whitewind</a> for long-form posts. <a 113 + href="https://tangled.org" target="_blank" rel="noopener noreferrer" 114 + style="color: var(--text); text-decoration: underline;">tangled.org</a> for code hosting. they're all 115 + just different views of the same underlying data - <strong>your</strong> data.</p> 116 + <p>this is what "<a href="https://overreacted.io/open-social/" target="_blank" rel="noopener noreferrer" 117 + style="color: var(--text); text-decoration: underline;">open social</a>" means: your followers, your 118 + content, your connections - they all belong to you, not the app. switch apps anytime and take everything 119 + with you. no platform can hold your social graph hostage.</p> 120 + <p style="margin-bottom: 1rem;"><strong>how to explore:</strong> click your avatar in the center to see the 121 + details of your identity. click any app to browse the records it's created in your repository.</p> 122 + <button id="closeInfo">got it</button> 123 + <p 124 + style="margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid var(--border); font-size: 0.7rem; color: var(--text-light); display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap;"> 125 + <span>view <a href="https://tangled.org/@zzstoatzz.io/at-me" target="_blank" rel="noopener noreferrer" 126 + style="color: var(--text); text-decoration: underline;">the source code</a> on</span> 127 + <a href="https://tangled.org" target="_blank" rel="noopener noreferrer" 128 + style="color: var(--text); text-decoration: underline;">tangled.org</a> 129 + </p> 130 + </div> 131 + 132 + <div class="guestbook-modal" id="guestbookModal"> 133 + <button class="guestbook-close" id="guestbookClose">x</button> 134 + <div id="guestbookContent"></div> 135 + </div> 136 + 137 + <div class="canvas"> 138 + <div class="identity"> 139 + <img class="identity-avatar" id="identityAvatar" /> 140 + <div class="identity-handle" id="handleDisplay"></div> 141 + </div> 142 + <div id="field" class="loading"> 143 + <div class="loading-spinner"></div> 144 + <div class="loading-text">loading your data</div> 145 + <div class="loading-progress" id="status">resolving identity...</div> 146 + </div> 147 + </div> 148 + <div id="detail" class="detail-panel"></div> 149 + 150 + <script type="module" src="/src/view/main.js"></script> 151 + <script> 152 + // Info modal handlers (kept inline as they're simple UI toggles) 153 + document.getElementById('infoBtn').addEventListener('click', () => { 154 + document.getElementById('infoModal').classList.add('visible'); 155 + document.getElementById('overlay').classList.add('visible'); 156 + }); 157 + document.getElementById('closeInfo').addEventListener('click', () => { 158 + document.getElementById('infoModal').classList.remove('visible'); 159 + document.getElementById('overlay').classList.remove('visible'); 160 + }); 161 + document.getElementById('overlay').addEventListener('click', () => { 162 + document.getElementById('infoModal').classList.remove('visible'); 163 + document.getElementById('overlay').classList.remove('visible'); 164 + }); 165 + </script> 166 + </body> 167 + 168 + </html>
+2 -1
vite.config.js
··· 9 rollupOptions: { 10 input: { 11 main: 'index.html', 12 - view: 'view.html' 13 } 14 } 15 },
··· 9 rollupOptions: { 10 input: { 11 main: 'index.html', 12 + view: 'view.html', 13 + 'view-dir': 'view/index.html' 14 } 15 } 16 },