feat(footer): simplify the footer

ewancroft.uk 6ece81dc 260aa375

verified
Changed files
+43 -142
src
lib
components
+43 -46
src/lib/components/layout/footer/Main.svelte
··· 1 1 <script lang="ts"> 2 2 import { onMount } from "svelte"; 3 - import { env } from "$env/dynamic/public"; 4 - import TidClock from "./TidClock.svelte"; 5 3 6 4 export let profile: any; 7 - export const posts: any = undefined; 5 + let showDetails = false; 8 6 9 7 onMount(() => { 10 8 const copyrightYearElement = document.getElementById("copyright-year"); ··· 12 10 copyrightYearElement.textContent = new Date().getFullYear().toString(); 13 11 } 14 12 }); 13 + 14 + function toggleDetails() { 15 + showDetails = !showDetails; 16 + } 15 17 </script> 16 18 17 - <footer class="text-center py-8 text-primary text-sm opacity-60"> 19 + <footer class="text-center py-6 text-primary text-sm opacity-60"> 18 20 <div class="max-w-2xl mx-auto px-4"> 19 - <!-- Main attribution line --> 20 - <div class="mb-4"> 21 + <!-- Main footer line --> 22 + <div class="mb-3"> 21 23 <span>&copy; <span id="copyright-year"></span></span> 22 - <span class="mx-2">•</span> 23 24 {#if profile?.handle} 25 + <span class="mx-2">•</span> 24 26 <a 25 27 href="https://bsky.app/profile/{profile.did}" 26 28 class="text-[var(--link-color)] hover:text-[var(--link-hover-color)] transition-colors" 27 29 > 28 30 @{profile.handle} 29 31 </a> 30 - {:else} 31 - <span>{profile?.displayName || profile?.did}</span> 32 32 {/if} 33 33 <span class="mx-2">•</span> 34 - <span>powered by 35 - <a 36 - class="text-[var(--link-color)] hover:text-[var(--link-hover-color)] transition-colors" 37 - href="https://atproto.com/guides/glossary#at-protocol" 38 - > 39 - atproto 40 - </a> 41 - </span> 34 + <button 35 + class="text-[var(--link-color)] hover:text-[var(--link-hover-color)] transition-colors underline bg-none border-none cursor-pointer text-sm" 36 + on:click={toggleDetails} 37 + > 38 + {showDetails ? 'Hide details' : 'About'} 39 + </button> 42 40 </div> 43 41 44 - <!-- Project info --> 45 - <div class="mb-4 text-xs opacity-75 leading-relaxed"> 46 - <div class="mb-2"> 47 - Linkat Directory made by 48 - <a 49 - class="text-[var(--link-color)] hover:text-[var(--link-hover-color)] transition-colors" 50 - href="https://bsky.app/profile/did:plc:ofrbh253gwicbkc5nktqepol" 51 - > 52 - ewan 53 - </a> 54 - </div> 55 - <div> 56 - <a 57 - class="text-[var(--link-color)] hover:text-[var(--link-hover-color)] transition-colors" 58 - href="https://github.com/ewanc26/linkat-directory" 59 - > 60 - Open source 61 - </a> 62 - and free to use under AGPL-3.0. Not affiliated with 63 - <a 64 - class="text-[var(--link-color)] hover:text-[var(--link-hover-color)] transition-colors" 65 - href="https://linkat.blue" 66 - > 67 - Linkat 68 - </a> 42 + <!-- Collapsible details --> 43 + {#if showDetails} 44 + <div class="text-xs opacity-75 leading-relaxed space-y-2 transition-all duration-200"> 45 + <div> 46 + Linkat Directory made by 47 + <a 48 + class="text-[var(--link-color)] hover:text-[var(--link-hover-color)] transition-colors" 49 + href="https://bsky.app/profile/did:plc:ofrbh253gwicbkc5nktqepol" 50 + > 51 + ewan 52 + </a> 53 + </div> 54 + <div> 55 + <a 56 + class="text-[var(--link-color)] hover:text-[var(--link-hover-color)] transition-colors" 57 + href="https://github.com/ewanc26/linkat-directory" 58 + > 59 + Open source 60 + </a> 61 + and free to use under AGPL-3.0. Not affiliated with 62 + <a 63 + class="text-[var(--link-color)] hover:text-[var(--link-hover-color)] transition-colors" 64 + href="https://linkat.blue" 65 + > 66 + Linkat 67 + </a> 68 + </div> 69 69 </div> 70 - </div> 71 - 72 - <!-- Clock --> 73 - <TidClock /> 70 + {/if} 74 71 </div> 75 72 </footer>
-96
src/lib/components/layout/footer/TidClock.svelte
··· 1 - <script lang="ts"> 2 - import { onMount, onDestroy } from "svelte"; 3 - 4 - let currentTID = ''; 5 - let interval: NodeJS.Timeout; 6 - let isRunning = true; 7 - 8 - // Base32-sortable character set for TID encoding 9 - const BASE32_SORTABLE = '234567abcdefghijklmnopqrstuvwxyz'; 10 - 11 - /** 12 - * Generate a random 10-bit clock identifier 13 - */ 14 - function generateClockId(): number { 15 - return Math.floor(Math.random() * 1024); // 2^10 = 1024 16 - } 17 - 18 - /** 19 - * Convert a number to base32-sortable encoding 20 - */ 21 - function toBase32Sortable(num: bigint): string { 22 - if (num === 0n) { 23 - return '2222222222222'; 24 - } 25 - 26 - let result = ''; 27 - while (num > 0n) { 28 - result = BASE32_SORTABLE[Number(num % 32n)] + result; 29 - num = num / 32n; 30 - } 31 - 32 - // Pad to 13 characters for consistent TID length 33 - return result.padStart(13, '2'); 34 - } 35 - 36 - /** 37 - * Generate a TID for the current timestamp 38 - */ 39 - function generateTID(): string { 40 - // Get current timestamp in microseconds since UNIX epoch 41 - const nowMs = Date.now(); 42 - const nowMicroseconds = BigInt(nowMs * 1000); // Convert to microseconds 43 - 44 - // Generate random clock identifier (10 bits) 45 - const clockId = generateClockId(); 46 - 47 - // Combine timestamp (53 bits) and clock identifier (10 bits) 48 - // The top bit is always 0, so we have 63 bits in total 49 - const tidBigInt = (nowMicroseconds << 10n) | BigInt(clockId); 50 - 51 - return toBase32Sortable(tidBigInt); 52 - } 53 - 54 - /** 55 - * Update the TID display value 56 - */ 57 - function updateTID() { 58 - if (isRunning) { 59 - currentTID = generateTID(); 60 - } 61 - } 62 - 63 - /** 64 - * Copy the TID to the clipboard 65 - */ 66 - async function copyTID() { 67 - try { 68 - await navigator.clipboard.writeText(currentTID); 69 - console.log('TID copied to clipboard:', currentTID); 70 - } catch (err) { 71 - console.error('Failed to copy TID:', err); 72 - } 73 - } 74 - 75 - onMount(() => { 76 - // Generate initial TID 77 - updateTID(); 78 - 79 - // Update every 100ms for a smooth display 80 - interval = setInterval(updateTID, 100); 81 - }); 82 - 83 - onDestroy(() => { 84 - if (interval) { 85 - clearInterval(interval); 86 - } 87 - }); 88 - </script> 89 - 90 - <button 91 - class="inline-block bg-none border-none text-[var(--link-color)] font-mono text-xs cursor-pointer px-0.5 py-0 rounded-md transition-all duration-200 ease-in-out hover:text-[var(--link-hover-color)] hover:bg-[var(--button-bg)]" 92 - on:click={copyTID} 93 - title="Click to copy TID" 94 - > 95 - {currentTID} 96 - </button>