Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

silo/dashboard: auto light/dark + auth recovery button

Theme now honors prefers-color-scheme when the user hasn't explicitly
chosen one, and follows system changes live via MediaQueryList.

On Auth0 init failures (notably stale refresh tokens showing up as
"Unknown or invalid refresh token"), surface a "clear session & retry"
button that nukes the Auth0 localStorage cache and reloads, letting
the user recover without devtools.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+35 -2
+35 -2
silo/dashboard.html
··· 283 283 <h1>silo</h1> 284 284 <p>aesthetic.computer data dashboard</p> 285 285 <button id="loginBtn">sign in</button> 286 + <button id="loginResetBtn" class="btn" style="display:none;margin-top:6px">clear session &amp; retry</button> 286 287 <p id="authStatus">loading...</p> 287 288 </div> 288 289 ··· 716 717 717 718 <script> 718 719 let auth0Client = null, accessToken = null, logCount = 0; 719 - let darkMode = localStorage.getItem('silo-theme') !== 'light'; 720 + // Theme: respect system preference when user hasn't picked one explicitly. 721 + const prefersDark = window.matchMedia('(prefers-color-scheme: dark)'); 722 + const savedTheme = localStorage.getItem('silo-theme'); 723 + let darkMode = savedTheme ? savedTheme === 'dark' : prefersDark.matches; 720 724 applyTheme(); 725 + 726 + // Track system changes only while the user has no explicit preference saved. 727 + prefersDark.addEventListener('change', (e) => { 728 + if (!localStorage.getItem('silo-theme')) { 729 + darkMode = e.matches; 730 + applyTheme(); 731 + } 732 + }); 721 733 722 734 function applyTheme() { 723 735 document.documentElement.setAttribute('data-theme', darkMode ? 'dark' : 'light'); ··· 783 795 } 784 796 } catch (err) { 785 797 console.error('Auth init failed:', err); 786 - document.getElementById('authStatus').textContent = 'error: ' + err.message; 798 + const msg = err?.message || String(err); 799 + const isTokenErr = /refresh token|invalid_grant|login_required|unknown.*token|expired/i.test(msg); 800 + document.getElementById('authStatus').textContent = isTokenErr 801 + ? 'session expired — clear session & retry' 802 + : 'error: ' + msg; 803 + // Surface a recovery button the user can click. 804 + const resetBtn = document.getElementById('loginResetBtn'); 805 + resetBtn.style.display = 'inline-block'; 806 + resetBtn.onclick = async () => { 807 + // Nuke Auth0's local cache, then let them sign in fresh. Works even 808 + // when auth0Client itself failed to initialize. 809 + try { await auth0Client?.logout({ openUrl: false }); } catch {} 810 + Object.keys(localStorage) 811 + .filter((k) => k.startsWith('@@auth0spajs@@') || k.includes('auth0')) 812 + .forEach((k) => localStorage.removeItem(k)); 813 + location.reload(); 814 + }; 815 + // Sign-in button becomes a full redirect (bypasses any cached state). 816 + document.getElementById('loginBtn').onclick = () => { 817 + if (auth0Client) auth0Client.loginWithRedirect(); 818 + else location.reload(); 819 + }; 787 820 } 788 821 document.getElementById('logoutBtn').onclick = () => { 789 822 auth0Client?.logout({ logoutParams: { returnTo: location.origin + location.pathname } });