atproto pds in zig pds.zat.dev
pds atproto
24

Configure Feed

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

Add landing page preferences

zzstoatzz (May 26, 2026, 1:10 AM -0500) 1080ae01 384f1c78

+48 -7
+38 -5
src/http/landing/assets.zig
··· 14 14 \\ --green: #39c178; --pink: #f05f8e; --focus: #c9d7ff; --glass: rgba(255,255,255,.085); 15 15 \\ color-scheme: dark; 16 16 \\} 17 + \\html[data-theme="dark"] { 18 + \\ --bg: #070807; --panel: #101210; --panel-2: #151915; --line: #273026; 19 + \\ --text: #f0f1ec; --muted: #aaa69b; --dim: #69645c; --accent: #91adff; 20 + \\ --green: #39c178; --pink: #f05f8e; --focus: #c9d7ff; --glass: rgba(255,255,255,.085); 21 + \\ color-scheme: dark; 22 + \\} 17 23 \\@media (prefers-color-scheme: light) { 18 24 \\ :root { 19 25 \\ --bg: #f6f4ed; --panel: #fffdf7; --panel-2: #ece8dd; --line: #d8d0c0; ··· 21 27 \\ --green: #1b7340; --pink: #b72e5c; --focus: #163f9f; --glass: rgba(255,255,255,.72); color-scheme: light; 22 28 \\ } 23 29 \\} 30 + \\html[data-theme="light"] { 31 + \\ --bg: #f6f4ed; --panel: #fffdf7; --panel-2: #ece8dd; --line: #d8d0c0; 32 + \\ --text: #161412; --muted: #625c53; --dim: #8f8679; --accent: #315dcb; 33 + \\ --green: #1b7340; --pink: #b72e5c; --focus: #163f9f; --glass: rgba(255,255,255,.72); 34 + \\ color-scheme: light; 35 + \\} 24 36 \\* { box-sizing: border-box; } 25 37 \\html { min-height: 100%; background: var(--bg); } 26 38 \\body { ··· 35 47 \\.shell { width: min(100%, 780px); margin: 0 auto; padding: 18px 16px 34px; } 36 48 \\header { 37 49 \\ position: sticky; top: 0; z-index: 10; display: flex; justify-content: space-between; align-items: center; gap: 12px; 38 - \\ min-height: 48px; margin: -18px -16px 26px; padding: 8px 16px; 39 - \\ background: color-mix(in srgb, var(--bg) 90%, transparent); backdrop-filter: blur(14px); 50 + \\ min-height: 50px; margin: -18px 0 26px; padding: 8px 0; 51 + \\ background: linear-gradient(180deg, color-mix(in srgb, var(--bg) 82%, transparent), color-mix(in srgb, var(--bg) 58%, transparent) 72%, transparent); 52 + \\ backdrop-filter: blur(14px); 40 53 \\ -webkit-backdrop-filter: blur(14px); 41 - \\ border-bottom: 1px solid color-mix(in srgb, var(--line) 72%, transparent); 42 54 \\ color: var(--muted); font-size: 12px; 43 55 \\} 44 56 \\.brand { flex: 0 0 auto; display: inline-flex; align-items: center; color: var(--text); text-decoration: none; font-weight: 700; font-size: 14px; letter-spacing: 0; } 45 57 \\.brand:hover { opacity: 0.82; } 46 - \\nav { min-width: 0; display: flex; align-items: center; justify-content: flex-end; gap: 7px; padding: 4px; border: 1px solid color-mix(in srgb, var(--line) 72%, transparent); border-radius: 18px; background: color-mix(in srgb, var(--glass) 46%, transparent); box-shadow: inset 0 1px 0 rgba(255,255,255,.10); } 58 + \\nav { min-width: 0; display: flex; align-items: center; justify-content: flex-end; gap: 7px; padding: 4px; border: 1px solid color-mix(in srgb, var(--line) 66%, transparent); border-radius: 18px; background: 59 + \\ radial-gradient(circle at 20% 0%, color-mix(in srgb, var(--signal-a, var(--green)) 12%, transparent), transparent 46%), 60 + \\ color-mix(in srgb, var(--glass) 38%, transparent); 61 + \\ box-shadow: inset 0 1px 0 rgba(255,255,255,.10), 0 8px 28px rgba(0,0,0,.12); } 47 62 \\nav a, .settings { color: var(--muted); text-decoration: none; background: none; border: 0; padding: 0; font: inherit; cursor: pointer; } 48 63 \\nav a:hover, .settings:hover { color: var(--text); } 49 64 \\.icon-link, .settings { ··· 144 159 \\.system-links span { color: var(--dim); font-size: 11px; } 145 160 \\.client-body { padding: 15px 16px 17px; } 146 161 \\.hint { margin: 0 0 12px; color: var(--dim); font-size: 11px; } 162 + \\.pref-section { display: grid; gap: 10px; margin-bottom: 16px; } 163 + \\.pref-label { color: var(--dim); font-size: 11px; text-transform: lowercase; } 164 + \\.theme-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; } 165 + \\.theme-option { min-height: 42px; border: 1px solid color-mix(in srgb, var(--line) 82%, white 6%); border-radius: 12px; background: color-mix(in srgb, var(--panel) 72%, transparent); color: var(--muted); font: inherit; font-size: 11px; cursor: pointer; box-shadow: inset 0 1px 0 rgba(255,255,255,.10); } 166 + \\.theme-option:hover, .theme-option.active { color: var(--text); border-color: color-mix(in srgb, var(--signal-a, var(--green)) 62%, var(--line)); } 167 + \\.theme-option.active { background: linear-gradient(145deg, color-mix(in srgb, var(--signal-a, var(--green)) 18%, transparent), color-mix(in srgb, var(--signal-b, var(--accent)) 8%, transparent)), color-mix(in srgb, var(--panel) 82%, transparent); } 147 168 \\.client-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(92px, 1fr)); gap: 9px; } 148 169 \\.client { display: grid; justify-items: center; gap: 8px; min-height: 82px; padding: 11px 8px 10px; border: 1px solid color-mix(in srgb, var(--line) 82%, white 6%); border-radius: 13px; background: 149 170 \\ radial-gradient(circle at 30% 18%, rgba(255,255,255,.14), transparent 34%), ··· 168 189 \\} 169 190 \\@media (max-width: 430px) { 170 191 \\ .shell { padding-left: 14px; padding-right: 14px; } 171 - \\ header { margin-left: -14px; margin-right: -14px; padding-left: 14px; padding-right: 14px; gap: 8px; } 192 + \\ header { gap: 8px; } 172 193 \\ nav { gap: 4px; padding: 3px; border-radius: 16px; } 173 194 \\ .icon-link, .settings { width: 32px; height: 32px; border-radius: 10px; } 174 195 \\ .logo-link::after { inset: 5px; border-radius: 8px; } ··· 180 201 pub const script = 181 202 \\(function() { 182 203 \\ var key = 'zds.preferredClient'; 204 + \\ var themeKey = 'zds.theme'; 183 205 \\ var clients = [ 184 206 \\ { value: 'bsky', label: 'Bluesky', icon: 'https://web-cdn.bsky.app/static/apple-touch-icon.png', url: function(handle, did) { return 'https://bsky.app/profile/' + handle; } }, 185 207 \\ { value: 'blacksky', label: 'Blacksky', icon: 'https://blacksky.community/static/apple-touch-icon.png', url: function(handle, did) { return 'https://blacksky.community/profile/' + handle; } }, ··· 190 212 \\ ]; 191 213 \\ function read() { try { return localStorage.getItem(key); } catch (_) { return null; } } 192 214 \\ function write(value) { try { localStorage.setItem(key, value); } catch (_) {} } 215 + \\ function readTheme() { try { return localStorage.getItem(themeKey) || 'system'; } catch (_) { return 'system'; } } 216 + \\ function writeTheme(value) { try { value === 'system' ? localStorage.removeItem(themeKey) : localStorage.setItem(themeKey, value); } catch (_) {} } 217 + \\ function applyTheme(value) { 218 + \\ if (value === 'light' || value === 'dark') document.documentElement.dataset.theme = value; 219 + \\ else delete document.documentElement.dataset.theme; 220 + \\ document.querySelectorAll('.theme-option').forEach(function(btn) { btn.classList.toggle('active', btn.dataset.theme === value); }); 221 + \\ } 193 222 \\ function preferred() { var value = read(); return clients.find(function(c) { return c.value === value; }) || clients[0]; } 194 223 \\ function syncLinks() { 195 224 \\ var client = preferred(); ··· 209 238 \\ btn.addEventListener('click', function() { write(client.value); syncLinks(); }); 210 239 \\ grid.appendChild(btn); 211 240 \\ }); 241 + \\ document.querySelectorAll('.theme-option').forEach(function(btn) { 242 + \\ btn.addEventListener('click', function() { writeTheme(btn.dataset.theme); applyTheme(btn.dataset.theme); }); 243 + \\ }); 212 244 \\ document.getElementById('settings').addEventListener('click', function() { sheet.classList.add('open'); }); 213 245 \\ document.getElementById('system').addEventListener('click', function() { systemSheet.classList.add('open'); }); 214 246 \\ document.getElementById('close-client').addEventListener('click', function() { sheet.classList.remove('open'); }); ··· 216 248 \\ sheet.addEventListener('click', function(event) { if (event.target === sheet) sheet.classList.remove('open'); }); 217 249 \\ systemSheet.addEventListener('click', function(event) { if (event.target === systemSheet) systemSheet.classList.remove('open'); }); 218 250 \\ document.addEventListener('keydown', function(event) { if (event.key === 'Escape') { sheet.classList.remove('open'); systemSheet.classList.remove('open'); } }); 251 + \\ applyTheme(readTheme()); 219 252 \\ syncLinks(); 220 253 \\})(); 221 254 ;
+10 -2
src/http/landing/mod.zig
··· 129 129 \\ </main> 130 130 \\ <div class="sheet" id="client-sheet" role="dialog" aria-modal="true" aria-labelledby="client-title"> 131 131 \\ <div class="sheet-card"> 132 - \\ <div class="sheet-head"><h2 id="client-title">open profiles with</h2><button class="close" type="button" aria-label="close" id="close-client"><svg aria-hidden="true" viewBox="0 0 24 24"><path d="M7 7l10 10M17 7L7 17"/></svg></button></div> 132 + \\ <div class="sheet-head"><h2 id="client-title">settings</h2><button class="close" type="button" aria-label="close" id="close-client"><svg aria-hidden="true" viewBox="0 0 24 24"><path d="M7 7l10 10M17 7L7 17"/></svg></button></div> 133 133 \\ <div class="client-body"> 134 - \\ <p class="hint">choose where resident links open.</p> 134 + \\ <div class="pref-section"> 135 + \\ <div class="pref-label">appearance</div> 136 + \\ <div class="theme-grid" role="group" aria-label="theme"> 137 + \\ <button class="theme-option" type="button" data-theme="system">system</button> 138 + \\ <button class="theme-option" type="button" data-theme="dark">dark</button> 139 + \\ <button class="theme-option" type="button" data-theme="light">light</button> 140 + \\ </div> 141 + \\ </div> 142 + \\ <div class="pref-label">open profiles with</div> 135 143 \\ <div class="client-grid" id="client-grid"></div> 136 144 \\ </div> 137 145 \\ </div>