···11+<script lang="ts">
22+ import { happyMacStore } from '$lib/stores';
33+44+ let isVisible = $state(false);
55+ let position = $state(-100);
66+77+ // Watch the store for when it's triggered (24 clicks)
88+ $effect(() => {
99+ const state = $happyMacStore;
1010+ if (state.isTriggered && !isVisible) {
1111+ startAnimation();
1212+ }
1313+ });
1414+1515+ function startAnimation() {
1616+ isVisible = true;
1717+ position = -100;
1818+1919+ // Animate across screen (takes about 15 seconds)
2020+ const duration = 15000;
2121+ const startTime = Date.now();
2222+2323+ function animate() {
2424+ const elapsed = Date.now() - startTime;
2525+ const progress = Math.min(elapsed / duration, 1);
2626+2727+ // Move from -100 to window width + 100
2828+ position = -100 + (window.innerWidth + 200) * progress;
2929+3030+ if (progress < 1) {
3131+ requestAnimationFrame(animate);
3232+ } else {
3333+ isVisible = false;
3434+ // Reset the store so it can be triggered again
3535+ happyMacStore.reset();
3636+ }
3737+ }
3838+3939+ requestAnimationFrame(animate);
4040+ }
4141+</script>
4242+4343+{#if isVisible}
4444+ <div
4545+ class="happy-mac"
4646+ style="left: {position}px"
4747+ >
4848+ <!--
4949+ Happy Mac SVG
5050+ Original by NiloGlock at Italian Wikipedia
5151+ License: CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0/)
5252+ Source: https://commons.wikimedia.org/wiki/File:Happy_Mac.svg
5353+ -->
5454+ <svg
5555+ width="60"
5656+ height="78"
5757+ viewBox="0 0 8.4710464 10.9614"
5858+ xmlns="http://www.w3.org/2000/svg"
5959+ class="mac-icon"
6060+ >
6161+ <g transform="translate(-5.3090212,-4.3002038)">
6262+ <g transform="matrix(0.06455006,0,0,0.06455006,7.6050574,7.0900779)">
6363+ <path d="m -30.937651,99.78759 h 122 v 26.80449 h -122 z" style="fill:#000000;fill-opacity:1;stroke-width:2.38412714"/>
6464+ <g transform="translate(-56.456402,-31.41017)">
6565+ <path style="fill:#555555;fill-opacity:1;stroke:none;stroke-width:0.17674622" d="m 33.668747,136.75006 v 4.69998 h 31.950504 v -4.69998 z m 41.740088,4.69998 V 146.15 h 11.145573 v -4.69996 z M 91.152059,146.15 v 6.29987 H 102.47075 V 146.15 Z"/>
6666+ <path style="fill:#444444;fill-opacity:1;stroke:none;stroke-width:0.15800072" d="m 65.619251,136.75006 v 4.69998 H 86.554408 V 146.15 h 15.916342 v 6.29987 h 20.86023 V 146.15 h -15.87449 v -4.69996 H 91.152059 v -4.69998 z"/>
6767+ <path style="fill:#222222;fill-opacity:1;stroke:none;stroke-width:0.21712606" d="m 91.152059,136.75006 v 4.69998 H 107.45649 V 146.15 h 15.87449 v 6.29987 h 16.03777 v -6.29987 -4.69996 -4.69998 z"/>
6868+ <path style="fill:#777777;fill-opacity:1;stroke:none;stroke-width:0.20201708" d="M 33.668747,141.45004 V 146.15 h 41.740088 v -4.69996 z M 75.408835,146.15 v 6.29987 H 91.152059 V 146.15 Z"/>
6969+ <path d="m 33.668823,146.14999 h 41.74001 v 6.3 h -41.74001 z" style="fill:#888888;fill-opacity:1;stroke:none;stroke-width:0.23388879"/>
7070+ </g>
7171+ <path d="M -30.969854,-37.120319 H 91.062349 V 99.787579 H -30.969854 Z" style="fill:#cccccc;fill-opacity:1;stroke-width:0.26458332"/>
7272+ <path d="M -15.075892,-21.040775 H 74.98512 v 67.75 h -90.061012 z" style="fill:#ccccff;fill-opacity:1;stroke-width:0.26458332"/>
7373+ <path transform="scale(0.26458333)" d="M 102.17383,-23.402344 V 59.882812 H 83.148438 V 78.779297 H 102.17383 120 120.0508 V -23.402344 Z" style="fill:#000000;fill-opacity:1;stroke-width:0.93718952"/>
7474+ <path d="M -30.969856,-43.220318 H 91.062347 v 6.1 H -30.969856 Z" style="fill:#000000;fill-opacity:1;stroke-width:1.13749063"/>
7575+ <path d="M -15.075892,-27.140776 H 74.98512 v 6.1 h -90.061012 z" style="fill:#444444;fill-opacity:1;stroke-width:0.97719014"/>
7676+ <path d="m -21.040775,15.075892 h 67.75 v 6.1 h -67.75 z" style="fill:#444444;fill-opacity:1;stroke-width:0.84755003" transform="rotate(90)"/>
7777+ <path d="m -21.040775,-81.085121 h 67.75 v 6.1 h -67.75 z" style="fill:#ffffff;fill-opacity:1;stroke-width:0.84755009" transform="rotate(90)"/>
7878+ <path d="m -15.07589,46.709225 h 90.061013 v 6.1 H -15.07589 Z" style="fill:#ffffff;fill-opacity:1;stroke-width:0.9771902"/>
7979+ <path d="m 31.655506,73.81324 h 43.400002 v 5 H 31.655506 Z" style="fill:#000000;fill-opacity:1;stroke-width:0.26445001"/>
8080+ <path d="m 31.655506,78.81324 h 43.400005 v 6 H 31.655506 Z" style="fill:#ffffff;fill-opacity:1;stroke-width:0.28969046"/>
8181+ <path d="m -21.133041,73.785721 h 11.060395 v 5 h -11.060395 z" style="fill:#00bb00;fill-opacity:1;stroke-width:0.13350084"/>
8282+ <path d="m -21.133041,78.785721 h 11.060396 v 6 h -11.060396 z" style="fill:#dd0000;fill-opacity:1;stroke-width:0.14624284"/>
8383+ <path d="M 5.8799295,-6.1919641 H 10.87993 V 5.0080357 H 5.8799295 Z" style="fill:#000000;fill-opacity:1;stroke-width:0.26576424"/>
8484+ <path d="m 47.880306,-6.1919641 h 6.1 V 5.0080357 h -6.1 z" style="fill:#000000;fill-opacity:1;stroke-width:0.29354623"/>
8585+ <path d="m 10.8871,25.947487 h 5 v 6 h -5 z" style="fill:#000000;fill-opacity:1;stroke-width:0.19451953"/>
8686+ <path d="m 38.149635,25.944651 h 4.75 v 6.002836 h -4.75 z" style="fill:#000000;fill-opacity:1;stroke-width:0.18963902"/>
8787+ <path d="m 15.8871,31.947487 h 22.262533 v 5.011021 H 15.8871 Z" style="fill:#000000;fill-opacity:1;stroke-width:11.12128639"/>
8888+ <path d="M -37.120319,30.969854 H 99.787579 v 4.6 H -37.120319 Z" style="fill:#000000;fill-opacity:1;stroke-width:1.04625833" transform="rotate(90)"/>
8989+ <path d="M -37.120331,-95.662346 H 99.787582 v 4.6 H -37.120331 Z" style="fill:#000000;fill-opacity:1;stroke-width:1.04625833" transform="rotate(90)"/>
9090+ </g>
9191+ </g>
9292+ </svg>
9393+ </div>
9494+{/if}
9595+9696+<style>
9797+ .happy-mac {
9898+ position: fixed;
9999+ bottom: 0;
100100+ z-index: 9999;
101101+ pointer-events: none;
102102+ animation: hop 0.6s ease-in-out infinite;
103103+ }
104104+105105+ .mac-icon {
106106+ filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.3));
107107+ }
108108+109109+ @keyframes hop {
110110+ 0%,
111111+ 100% {
112112+ transform: translateY(0) rotate(0deg);
113113+ }
114114+ 50% {
115115+ transform: translateY(-20px) rotate(5deg);
116116+ }
117117+ }
118118+119119+ /* Add a little tilt alternation */
120120+ .happy-mac:hover {
121121+ animation: hop 0.3s ease-in-out infinite;
122122+ }
123123+</style>
+30-13
src/lib/components/layout/Footer.svelte
···11<script lang="ts">
22 import type { ProfileData, SiteInfoData } from '$lib/services/atproto';
33 import DecimalClock from './DecimalClock.svelte';
44+ import { happyMacStore } from '$lib/stores';
55+66+ interface Props {
77+ profile?: ProfileData | null;
88+ siteInfo?: SiteInfoData | null;
99+ }
41055- export let profile: ProfileData | null = null;
66- export let siteInfo: SiteInfoData | null = null;
1111+ let { profile = null, siteInfo = null }: Props = $props();
1212+713 let loading = false;
814 let error: string | null = null;
99- let copyrightText: string;
1010-1515+1116 const currentYear = new Date().getFullYear();
1212-1313- $: {
1717+1818+ // Show click count hint after 3 clicks
1919+ let showHint = $derived($happyMacStore.clickCount >= 3 && $happyMacStore.clickCount < 24);
2020+2121+ // Compute copyright text reactively
2222+ let copyrightText = $derived.by(() => {
1423 console.log('[Footer] Reactive: siteInfo updated:', siteInfo);
1524 const birthYear = siteInfo?.additionalInfo?.websiteBirthYear;
1625 console.log('[Footer] Current year:', currentYear);
···19282029 if (!birthYear || typeof birthYear !== 'number') {
2130 console.log('[Footer] Using current year (invalid/missing birth year)');
2222- copyrightText = `${currentYear}`;
3131+ return `${currentYear}`;
2332 } else if (birthYear > currentYear) {
2433 console.log('[Footer] Using current year (birth year in future)');
2525- copyrightText = `${currentYear}`;
3434+ return `${currentYear}`;
2635 } else if (birthYear === currentYear) {
2736 console.log('[Footer] Using current year (birth year equals current)');
2828- copyrightText = `${currentYear}`;
3737+ return `${currentYear}`;
2938 } else {
3039 console.log('[Footer] Using year range');
3131- copyrightText = `${birthYear} - ${currentYear}`;
4040+ return `${birthYear} - ${currentYear}`;
3241 }
3333- }
4242+ });
34433544 // Data is provided by layout load; no client-side fetch here to avoid using window.fetch during navigation.
3645</script>
···7988 class="underline hover:text-primary-500 focus-visible:text-primary-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 dark:hover:text-primary-400 dark:focus-visible:text-primary-400"
8089 aria-label="View source code on GitHub">code</a
8190 >
8282- <!-- Line 3: Version number -->
8383- <span aria-label="Version 10.3.2">v10.3.2</span>
9191+ <!-- Line 3: Version number (click 24 times for easter egg!) -->
9292+ <button
9393+ type="button"
9494+ onclick={() => happyMacStore.incrementClick()}
9595+ class="cursor-default select-none transition-colors hover:text-ink-600 dark:hover:text-ink-300"
9696+ aria-label="Version 10.3.2{showHint ? ` - ${$happyMacStore.clickCount} of 24 clicks` : ''}"
9797+ title={showHint ? `${$happyMacStore.clickCount}/24` : ''}
9898+ >
9999+ v10.3.2{#if showHint}<span class="ml-1 text-xs opacity-60">({$happyMacStore.clickCount}/24)</span>{/if}
100100+ </button>
84101 </div>
85102 </div>
86103