commit 370d1a5c28d44b0997b8e5f73208e89efbaedefd
parent ebd15a137f3f9fdcaac6e93f45b0275540d39e76
Author: Amit Dutta <amitdutta4255@gmail.com>
Date: Mon, 22 Jun 2026 19:22:23 +0530
added sponser button
Diffstat:
5 files changed, 227 insertions(+), 152 deletions(-)
diff --git a/docs/.vitepress/theme/components/ArticleAd.vue b/docs/.vitepress/theme/components/ArticleAd.vue
@@ -1,39 +0,0 @@
-<template>
- <div class="article-ad-container">
- <span class="ad-label">ADVERTISEMENT</span>
-
- <!-- EXACT A-ADS ADAPTIVE UNIT -->
- <div id="frame" style="width: 100%; margin: auto; position: relative; z-index: 99998;">
- <iframe
- data-aa='2445210'
- src='//acceptable.a-ads.com/2445210/?size=Adaptive'
- style='border:0; padding:0; width:70%; height:90px; overflow:hidden; display: block; margin: auto'
- allowtransparency='true'>
- </iframe>
- </div>
-
- </div>
-</template>
-
-<style scoped>
-.article-ad-container {
- margin-top: 2rem;
- padding: 1rem;
- background: var(--vp-c-bg-elv);
- border: 1px solid var(--vp-c-divider);
- border-radius: 12px;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- width: 100%;
-}
-
-.ad-label {
- font-size: 10px;
- font-weight: 700;
- color: var(--vp-c-text-3);
- letter-spacing: 0.5px;
- margin-bottom: 12px;
-}
-</style>-
\ No newline at end of file
diff --git a/docs/.vitepress/theme/components/FloatingAd.vue b/docs/.vitepress/theme/components/FloatingAd.vue
@@ -1,107 +0,0 @@
-<script setup lang="ts">
-import { ref } from 'vue'
-
-const isVisible = ref(true)
-
-const closeAd = () => {
- isVisible.value = false
-}
-</script>
-
-<template>
- <Transition name="slide-up" appear>
- <div v-if="isVisible" class="floating-ad-container">
- <!-- Header zone for the button and label so they never touch the iframe -->
- <div class="ad-header">
- <span class="ad-label">ADVERTISEMENT</span>
- <button class="close-btn" @click="closeAd" aria-label="Close Ad">×</button>
- </div>
-
- <!-- Isolated content zone purely for the 125x125 ad -->
- <div class="ad-content">
- <iframe
- data-aa='2445210'
- src='//ad.a-ads.com/2445210/?size=125x125'
- style='border:0; padding:0; width:125px; height:125px; overflow:hidden; display:block; margin:0;'
- allowtransparency='true'>
- </iframe>
- </div>
- </div>
- </Transition>
-</template>
-
-<style scoped>
-.floating-ad-container {
- position: fixed;
- bottom: 24px;
- right: 24px;
- /* Expanded size to ensure zero overlap with the 125x125 ad */
- width: 145px;
- height: 165px;
-
- background: var(--vp-c-bg-elv);
- border: 1px solid var(--vp-c-divider);
- border-radius: 12px;
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
- /* Maximum z-index to ensure it sits on top of all VitePress UI elements */
- z-index: 99999;
-
- display: flex;
- flex-direction: column;
- align-items: center;
- overflow: hidden;
- padding: 6px;
- box-sizing: border-box;
-}
-
-.ad-header {
- width: 100%;
- display: flex;
- justify-content: space-between;
- align-items: center;
- height: 20px;
- margin-bottom: 4px;
- padding: 0 4px;
- box-sizing: border-box;
-}
-
-.ad-label {
- font-size: 9px;
- font-weight: 700;
- color: var(--vp-c-text-3);
- letter-spacing: 0.5px;
-}
-
-.close-btn {
- font-size: 20px;
- color: var(--vp-c-text-2);
- background: none;
- border: none;
- cursor: pointer;
- line-height: 1;
- padding: 0;
-}
-
-.close-btn:hover {
- color: var(--vp-c-text-1);
-}
-
-.ad-content {
- width: 125px;
- height: 125px;
- display: block;
- clear: both;
-}
-
-/* Slide-In Animation */
-.slide-up-enter-active,
-.slide-up-leave-active {
- transition: opacity 0.4s ease, transform 0.4s cubic-bezier(0.25, 1, 0.5, 1);
-}
-
-.slide-up-enter-from,
-.slide-up-leave-to {
- opacity: 0;
- transform: translateY(40px) scale(0.95);
-}
-</style>-
\ No newline at end of file
diff --git a/docs/.vitepress/theme/components/Footer.vue b/docs/.vitepress/theme/components/Footer.vue
@@ -0,0 +1,33 @@
+<template>
+ <footer class="simple-footer">
+ <p>
+ Released under the
+ <a href="https://github.com" target="_blank" rel="noopener noreferrer" class="footer-link">MIT License</a>.
+ </p>
+ <p>Copyright (c) 2025-present <a href="https://amit.is-a.dev" target="_blank" rel="noopener noreferrer" class="footer-link">Amit Dutta</a></p>
+ </footer>
+</template>
+
+<style scoped>
+.simple-footer {
+ margin-top: 3rem;
+ padding-top: 1.5rem;
+ border-top: 1px solid var(--vp-c-divider);
+ text-align: center;
+ font-size: 13px;
+ color: var(--vp-c-text-2);
+ line-height: 1.5;
+}
+
+.footer-link {
+ color: var(--vp-c-brand-1);
+ text-decoration: none;
+ font-weight: 500;
+ transition: color 0.2s ease;
+}
+
+.footer-link:hover {
+ color: var(--vp-c-brand-2);
+ text-decoration: underline;
+}
+</style>
diff --git a/docs/.vitepress/theme/components/SponsorButton.vue b/docs/.vitepress/theme/components/SponsorButton.vue
@@ -0,0 +1,189 @@
+<script setup lang="ts">
+import { ref, onMounted } from 'vue'
+
+const particles = ref<SVGElement[]>([])
+const isBeating = ref(false)
+const isTouchDevice = ref(false)
+
+onMounted(() => {
+ isTouchDevice.value = window.matchMedia('(hover: none) and (pointer: coarse)').matches
+})
+
+// Varied colors like anime.js — different pinks, reds, corals
+const particleColors = [
+ '#ff4772', '#ff6b9d', '#ff3355', '#ff8fab',
+ '#e91e63', '#f06292', '#ff1744', '#ff80ab'
+]
+
+const triggerBurst = () => {
+ if (isTouchDevice.value || !particles.value?.length) return
+
+ // Cancel any running animations first so re-hover always works
+ particles.value.forEach((particle, i) => {
+ particle.getAnimations().forEach(a => a.cancel())
+
+ const angle = (i / particles.value.length) * Math.PI * 2 + (Math.random() - 0.5) * 0.6
+ const distance = 28 + Math.random() * 32
+ const tx = Math.cos(angle) * distance
+ const ty = Math.sin(angle) * distance
+ const size = 0.6 + Math.random() * 0.8 // vary particle size
+
+ // Random color from palette
+ const color = particleColors[Math.floor(Math.random() * particleColors.length)];
+ (particle as SVGElement & { style: CSSStyleDeclaration }).style.color = color
+
+ particle.animate([
+ {
+ transform: `translate(0px, 0px) scale(${size})`,
+ opacity: 1,
+ visibility: 'visible'
+ },
+ {
+ transform: `translate(${tx * 0.4}px, ${ty * 0.4}px) scale(${size * 1.1})`,
+ opacity: 1,
+ offset: 0.2
+ },
+ {
+ transform: `translate(${tx}px, ${ty}px) scale(0)`,
+ opacity: 0,
+ visibility: 'hidden'
+ }
+ ], {
+ duration: 500 + Math.random() * 250,
+ easing: 'cubic-bezier(0.25, 1, 0.5, 1)',
+ fill: 'forwards'
+ })
+ })
+
+ // Heart beat
+ isBeating.value = true
+ setTimeout(() => { isBeating.value = false }, 400)
+}
+</script>
+
+<template>
+ <div class="sponsor-wrapper">
+ <a
+ href="https://github.com/sponsors/notamitgamer"
+ class="sponsor-button"
+ target="_blank"
+ title="Sponsor Amit Dutta on GitHub"
+ @mouseenter="triggerBurst"
+ @click="triggerBurst"
+ :class="{ 'mobile-pill': isTouchDevice }"
+ >
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="4 7 16 12"
+ class="icon heart-icon"
+ :class="{ beating: isBeating }"
+ >
+ <path
+ fill="currentColor"
+ d="M12 18.445a.778.778 0 0 1-.34-.078C11.39 18.235 5 15.077 5 9.889a3.889 3.889 0 0 1 6.638-2.75L12 7.5l.362-.361A3.889 3.889 0 0 1 19 9.889c0 5.17-6.387 8.344-6.66 8.478a.778.778 0 0 1-.34.078z"
+ />
+ </svg>
+ <span>Sponsor</span>
+ </a>
+
+ <!-- Burst particles — desktop only -->
+ <svg
+ v-if="!isTouchDevice"
+ v-for="i in 12"
+ :key="i"
+ ref="particles"
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="4 7 16 12"
+ class="particle"
+ >
+ <path
+ fill="currentColor"
+ d="M12 18.445a.778.778 0 0 1-.34-.078C11.39 18.235 5 15.077 5 9.889a3.889 3.889 0 0 1 6.638-2.75L12 7.5l.362-.361A3.889 3.889 0 0 1 19 9.889c0 5.17-6.387 8.344-6.66 8.478a.778.778 0 0 1-.34.078z"
+ />
+ </svg>
+ </div>
+</template>
+
+<style scoped>
+.sponsor-wrapper {
+ position: relative;
+ display: inline-flex;
+ align-items: center;
+ margin-left: 8px;
+}
+
+.sponsor-button {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ padding: 5px 12px 5px 10px;
+ background-color: transparent;
+ color: var(--vp-c-text-1, #3c3c43);
+ font-size: 13px;
+ font-weight: 500;
+ border-radius: 20px;
+ transition:
+ background-color 0.2s ease,
+ color 0.2s ease;
+ text-decoration: none;
+ position: relative;
+ z-index: 2;
+ white-space: nowrap;
+ cursor: pointer;
+}
+
+.sponsor-button:hover {
+ background-color: rgba(255, 71, 114, 0.1);
+ color: #ff4772;
+}
+
+/* Heart icon */
+.heart-icon {
+ width: 15px;
+ height: 15px;
+ color: #ff4772;
+ flex-shrink: 0;
+ transition: transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+}
+
+.sponsor-button:hover .heart-icon {
+ transform: scale(1.2);
+}
+
+/* Beat animation triggered on click */
+.heart-icon.beating {
+ animation: heartbeat 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+}
+
+@keyframes heartbeat {
+ 0% { transform: scale(1); }
+ 40% { transform: scale(1.4); }
+ 70% { transform: scale(0.9); }
+ 100% { transform: scale(1.2); }
+}
+
+/* Mobile: static pink pill, no hover/animation */
+.mobile-pill {
+ background-color: rgba(255, 71, 114, 0.12);
+ color: #ff4772;
+}
+
+.mobile-pill .heart-icon {
+ color: #ff4772;
+}
+
+.particle {
+ position: absolute;
+ top: 50%;
+ left: 18px; /* roughly where the heart icon center sits */
+ width: 10px;
+ height: 10px;
+ margin-top: -5px;
+ margin-left: -5px;
+ color: #ff4772;
+ visibility: hidden;
+ opacity: 0;
+ pointer-events: none;
+ z-index: 1;
+}
+</style>+
\ No newline at end of file
diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts
@@ -1,9 +1,9 @@
import { h } from 'vue'
import DefaultTheme from 'vitepress/theme'
import type { Theme } from 'vitepress'
-import CodePage from './CodePage.vue'
import TermsBanner from './components/TermsBanner.vue'
-import FloatingAd from './components/ArticleAd.vue'
+import Footer from './components/Footer.vue'
+import SponsorButton from './components/SponsorButton.vue'
import './style.css'
export default {
@@ -12,8 +12,8 @@ export default {
Layout() {
return h(DefaultTheme.Layout, null, {
'layout-bottom': () => h(TermsBanner),
-
- 'doc-after': () => h(FloatingAd)
+ 'doc-after': () => h(Footer),
+ 'nav-bar-content-after': () => h(SponsorButton)
})
}
} satisfies Theme
\ No newline at end of file