317317 animation: counterPulse 0.4s ease;
318318 }
319319
320+ .traffic-counter {
321+ display: flex;
322+ align-items: center;
323+ justify-content: center;
324+ gap: 0.75rem;
325+ margin-top: 0.75rem;
326+ font-size: 0.9rem;
327+ color: var(--text-secondary);
328+ }
329+
330+ .traffic-metric {
331+ display: inline-flex;
332+ align-items: center;
333+ gap: 0.35rem;
334+ }
335+
336+ .traffic-value {
337+ font-weight: 600;
338+ color: var(--accent);
339+ }
340+
341+ .traffic-value.updated {
342+ animation: counterPulse 0.4s ease;
343+ }
344+
345+ .traffic-separator {
346+ color: var(--border);
347+ }
348+
320349 @keyframes counterPulse {
321350 0% { transform: scale(1); }
322351 50% { transform: scale(1.15); color: var(--accent-bright); }
796825 <a href="#examples">Examples</a>
797826 <a href="#install">Install</a>
798827 <a href="#configure">Configure</a>
828+ <a href="podcast.html">Podcast</a>
799829 <a href="#changelog">Changelog</a>
800830 <a href="#faq">FAQ</a>
801831 <a href="https://github.com/Persistence-AI" class="github-btn" target="_blank" rel="noopener" style="text-decoration: none;">
@@ -870,6 +900,17 @@ <h3 style="font-size: 1.2rem; margin-bottom: 1rem; color: var(--text-primary);">
870900 </div>
871901 </div>
872902 </div>
903+ <div class="traffic-counter" id="traffic-counter">
904+ <div class="traffic-metric">
905+ <span class="traffic-value" id="visitor-count">0</span>
906+ <span class="traffic-label">visitors</span>
907+ </div>
908+ <span class="traffic-separator">|</span>
909+ <div class="traffic-metric">
910+ <span class="traffic-value" id="pageview-count">0</span>
911+ <span class="traffic-label">page views</span>
912+ </div>
913+ </div>
873914 </div>
874915 <div class="more-options">
875916 <a href="#install">More options →</a>
@@ -2373,6 +2414,7 @@ <h2 style="margin-bottom: 2rem;">
23732414 <a href="#changelog">Changelog</a>
23742415 <a href="https://github.com/Persistence-AI" target="_blank" rel="noopener">Persistence Zero</a>
23752416 <a href="https://persistence-ai.github.io/Landing/docs/" target="_blank" rel="noopener">Documentation</a>
2417+ <a href="https://persistence-ai.github.io/Landing/podcast.html" target="_blank" rel="noopener">Podcast</a>
23762418 <a href="https://discord.gg/persistenceai" target="_blank" rel="noopener">Discord</a>
23772419 <a href="https://x.com/AiPersiste65218" target="_blank" rel="noopener">X.com</a>
23782420 </div>
@@ -2397,6 +2439,16 @@ <h2 style="margin-bottom: 2rem;">
23972439 total: 0
23982440 };
23992441
2442+ const TRAFFIC_GIST_ID = '';
2443+ const TRAFFIC_READ_ENABLED = Boolean(TRAFFIC_GIST_ID);
2444+ const TRAFFIC_WRITE_ENDPOINT = '';
2445+ const TRAFFIC_WRITE_ENABLED = Boolean(TRAFFIC_WRITE_ENDPOINT);
2446+
2447+ let trafficStats = {
2448+ visitors: 92,
2449+ pageViews: 92
2450+ };
2451+
24002452 // Session management
24012453 function getOrCreateSessionId() {
24022454 let sessionId = sessionStorage.getItem('persistenceai_session_id');
@@ -2522,6 +2574,166 @@ <h2 style="margin-bottom: 2rem;">
25222574 return num.toLocaleString('en-US');
25232575 }
25242576
2577+ function updateTrafficDisplay(animate = false) {
2578+ const visitorEl = document.getElementById('visitor-count');
2579+ const pageViewEl = document.getElementById('pageview-count');
2580+
2581+ if (visitorEl) {
2582+ visitorEl.textContent = formatNumber(trafficStats.visitors);
2583+ if (animate) {
2584+ visitorEl.classList.add('updated');
2585+ setTimeout(() => visitorEl.classList.remove('updated'), 400);
2586+ }
2587+ }
2588+
2589+ if (pageViewEl) {
2590+ pageViewEl.textContent = formatNumber(trafficStats.pageViews);
2591+ if (animate) {
2592+ pageViewEl.classList.add('updated');
2593+ setTimeout(() => pageViewEl.classList.remove('updated'), 400);
2594+ }
2595+ }
2596+ }
2597+
2598+ function loadTrafficFromStorage() {
2599+ const stored = localStorage.getItem('persistenceai_traffic_stats');
2600+ if (!stored) {
2601+ return;
2602+ }
2603+ try {
2604+ const parsed = JSON.parse(stored);
2605+ trafficStats = {
2606+ visitors: parseInt(parsed.visitors || 0, 10),
2607+ pageViews: parseInt(parsed.pageViews || 0, 10)
2608+ };
2609+ } catch (e) {
2610+ console.error('Error parsing stored traffic stats:', e);
2611+ }
2612+ }
2613+
2614+ function saveTrafficToStorage() {
2615+ localStorage.setItem('persistenceai_traffic_stats', JSON.stringify(trafficStats));
2616+ }
2617+
2618+ function getDateKey() {
2619+ return new Date().toISOString().split('T')[0];
2620+ }
2621+
2622+ async function getVisitorFingerprint() {
2623+ const input = [
2624+ navigator.userAgent,
2625+ navigator.language,
2626+ screen.width,
2627+ screen.height,
2628+ screen.colorDepth,
2629+ Intl.DateTimeFormat().resolvedOptions().timeZone || 'unknown'
2630+ ].join('|');
2631+ const encoded = new TextEncoder().encode(input);
2632+ const hashBuffer = await crypto.subtle.digest('SHA-256', encoded);
2633+ return Array.from(new Uint8Array(hashBuffer))
2634+ .map((b) => b.toString(16).padStart(2, '0'))
2635+ .join('');
2636+ }
2637+
2638+ function getFallbackFingerprint() {
2639+ const key = 'persistenceai_fallback_fingerprint';
2640+ let fingerprint = localStorage.getItem(key);
2641+ if (!fingerprint) {
2642+ fingerprint = `fp_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
2643+ localStorage.setItem(key, fingerprint);
2644+ }
2645+ return fingerprint;
2646+ }
2647+
2648+ async function refreshTrafficFromGist() {
2649+ if (!TRAFFIC_READ_ENABLED) {
2650+ return;
2651+ }
2652+ try {
2653+ const response = await fetch(`https://api.github.com/gists/${TRAFFIC_GIST_ID}`);
2654+ if (!response.ok) {
2655+ return;
2656+ }
2657+ const gist = await response.json();
2658+ const filename = Object.keys(gist.files || {})[0];
2659+ if (!filename) {
2660+ return;
2661+ }
2662+ const content = gist.files[filename].content;
2663+ const parsed = JSON.parse(content);
2664+ if (parsed && parsed.visitors && parsed.pageViews) {
2665+ trafficStats = {
2666+ visitors: parseInt(parsed.visitors.total || 0, 10),
2667+ pageViews: parseInt(parsed.pageViews.total || 0, 10)
2668+ };
2669+ updateTrafficDisplay();
2670+ saveTrafficToStorage();
2671+ }
2672+ } catch (e) {
2673+ console.error('Error fetching traffic stats:', e);
2674+ }
2675+ }
2676+
2677+ async function trackPageView() {
2678+ let fingerprint = '';
2679+ try {
2680+ fingerprint = await getVisitorFingerprint();
2681+ } catch (e) {
2682+ fingerprint = getFallbackFingerprint();
2683+ }
2684+
2685+ const today = getDateKey();
2686+
2687+ if (!TRAFFIC_WRITE_ENABLED) {
2688+ const seenKey = 'persistenceai_visitor_seen';
2689+ if (!localStorage.getItem(seenKey)) {
2690+ trafficStats.visitors += 1;
2691+ localStorage.setItem(seenKey, 'true');
2692+ }
2693+ trafficStats.pageViews += 1;
2694+ updateTrafficDisplay(true);
2695+ saveTrafficToStorage();
2696+ return;
2697+ }
2698+
2699+ try {
2700+ const response = await fetch(TRAFFIC_WRITE_ENDPOINT, {
2701+ method: 'POST',
2702+ headers: {
2703+ 'Content-Type': 'application/json'
2704+ },
2705+ body: JSON.stringify({
2706+ type: 'pageView',
2707+ fingerprint,
2708+ date: today
2709+ })
2710+ });
2711+
2712+ if (!response.ok) {
2713+ return;
2714+ }
2715+
2716+ const result = await response.json();
2717+ if (result && result.stats && result.stats.visitors && result.stats.pageViews) {
2718+ trafficStats = {
2719+ visitors: parseInt(result.stats.visitors.total || 0, 10),
2720+ pageViews: parseInt(result.stats.pageViews.total || 0, 10)
2721+ };
2722+ updateTrafficDisplay(true);
2723+ saveTrafficToStorage();
2724+ }
2725+ } catch (e) {
2726+ console.error('Error tracking page view:', e);
2727+ }
2728+ }
2729+
2730+ async function initializeTrafficStats() {
2731+ loadTrafficFromStorage();
2732+ updateTrafficDisplay();
2733+ await refreshTrafficFromGist();
2734+ await trackPageView();
2735+ }
2736+
25252737 // Track copy event by platform with rate limiting
25262738 async function trackCopy(platform) {
25272739 // Determine platform
@@ -2678,6 +2890,7 @@ <h2 style="margin-bottom: 2rem;">
26782890 // Initialize counter on page load
26792891 document.addEventListener('DOMContentLoaded', function() {
26802892 initializeCounter();
2893+ initializeTrafficStats();
26812894
26822895 // Auto-detect platform and show appropriate command
26832896 const platform = detectPlatform();
0 commit comments