From d4f51ee57fc34bf0cd2ce3d8903f560b525f2535 Mon Sep 17 00:00:00 2001 From: graphieros Date: Tue, 3 Mar 2026 07:55:47 +0100 Subject: [PATCH 1/3] feat: add easter egg CPR for sparkline pulse --- .../Package/WeeklyDownloadStats.vue | 152 +++++++++++++++++- 1 file changed, 149 insertions(+), 3 deletions(-) diff --git a/app/components/Package/WeeklyDownloadStats.vue b/app/components/Package/WeeklyDownloadStats.vue index fa192bb187..aa7b8f1e5b 100644 --- a/app/components/Package/WeeklyDownloadStats.vue +++ b/app/components/Package/WeeklyDownloadStats.vue @@ -7,6 +7,7 @@ import { OKLCH_NEUTRAL_FALLBACK, lightenOklch } from '~/utils/colors' import { applyBlocklistCorrection } from '~/utils/download-anomalies' import type { RepoRef } from '#shared/utils/git-providers' import type { VueUiSparklineConfig, VueUiSparklineDatasetItem } from 'vue-data-ui' +import { onKeyDown } from '@vueuse/core' const props = defineProps<{ packageName: string @@ -205,6 +206,104 @@ const dataset = computed(() => const lastDatapoint = computed(() => dataset.value.at(-1)?.period ?? '') +const isLoop = ref(false) +const showPulse = ref(true) +const keyboardShortcuts = useKeyboardShortcuts() + +const cheatCode = [ + 'arrowup', + 'arrowright', + 'arrowleft', + 'arrowup', + 'arrowleft', + 'arrowright', +] as const + +type CheatKey = (typeof cheatCode)[number] + +const easterEgg = ref([]) +let resetTimeout: ReturnType | undefined +const easterEggResetDelay = 1500 + +function resetEasterEgg() { + easterEgg.value = [] + clearTimeout(resetTimeout) + resetTimeout = undefined +} + +function pushEasterEggKey(key: CheatKey) { + clearTimeout(resetTimeout) + resetTimeout = setTimeout(resetEasterEgg, easterEggResetDelay) + + const nextIndex = easterEgg.value.length + + // Reset if the position is wrong + if (cheatCode[nextIndex] !== key) { + resetEasterEgg() + return + } + + easterEgg.value.push(key) + + // Match! reset & trigger + if (easterEgg.value.length === cheatCode.length) { + resetEasterEgg() + layEgg() + } +} + +onKeyDown( + 'ArrowUp', + e => { + if (!keyboardShortcuts.value) return + pushEasterEggKey('arrowup') + }, + { dedupe: true }, +) + +onKeyDown( + 'ArrowRight', + e => { + if (!keyboardShortcuts.value) return + pushEasterEggKey('arrowright') + }, + { dedupe: true }, +) + +onKeyDown( + 'ArrowLeft', + e => { + if (!keyboardShortcuts.value) return + pushEasterEggKey('arrowleft') + }, + { dedupe: true }, +) + +onBeforeUnmount(() => { + resetEasterEgg() +}) + +const eggPulse = ref(false) + +function playEggPulse() { + eggPulse.value = false + void document.documentElement.offsetHeight + eggPulse.value = true + + window.setTimeout(() => { + eggPulse.value = false + }, 900) +} + +function layEgg() { + showPulse.value = false + nextTick(() => { + showPulse.value = true + isLoop.value = !isLoop.value + playEggPulse() + }) +} + const config = computed(() => { return { theme: 'dark', @@ -248,8 +347,8 @@ const config = computed(() => { line: { color: colors.value.borderHover, pulse: { - show: true, // the pulse will not show if prefers-reduced-motion (enforced by vue-data-ui) - loop: false, + show: showPulse.value, // the pulse will not show if prefers-reduced-motion (enforced by vue-data-ui) + loop: isLoop.value, radius: 1.5, color: pulseColor.value!, easing: 'ease-in-out', @@ -306,7 +405,10 @@ const config = computed(() => { -
+