Merge pull request #53 from davidmonterocrespo24/website

feat: add GitHub star banner to encourage users to star the project a…
pull/74/head
David Montero Crespo 2026-03-23 14:04:34 -03:00 committed by GitHub
commit 400437278f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 174 additions and 4 deletions

View File

@ -0,0 +1,87 @@
.gh-star-banner {
position: fixed;
bottom: 28px;
right: 28px;
z-index: 9999;
display: flex;
align-items: flex-start;
gap: 14px;
background: #1e2228;
border: 1px solid #3c4049;
border-radius: 12px;
padding: 16px 18px;
width: 320px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.55);
animation: gh-banner-in 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes gh-banner-in {
from {
opacity: 0;
transform: translateY(20px) scale(0.97);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.gh-star-banner__icon {
color: #e3b341;
flex-shrink: 0;
margin-top: 1px;
}
.gh-star-banner__body {
flex: 1;
min-width: 0;
}
.gh-star-banner__body strong {
display: block;
color: #e1e4e8;
font-size: 14px;
font-weight: 600;
margin-bottom: 5px;
}
.gh-star-banner__body p {
color: #8b949e;
font-size: 12.5px;
line-height: 1.45;
margin: 0 0 12px;
}
.gh-star-banner__btn {
display: inline-block;
background: #e3b341;
color: #1b1f23;
font-size: 13px;
font-weight: 700;
padding: 6px 14px;
border-radius: 6px;
text-decoration: none;
transition: background 0.15s ease;
letter-spacing: 0.01em;
}
.gh-star-banner__btn:hover {
background: #f0c860;
}
.gh-star-banner__close {
background: none;
border: none;
color: #6e7681;
cursor: pointer;
font-size: 14px;
line-height: 1;
padding: 0;
flex-shrink: 0;
margin-top: 1px;
transition: color 0.15s ease;
}
.gh-star-banner__close:hover {
color: #e1e4e8;
}

View File

@ -0,0 +1,39 @@
import React from 'react';
import './GitHubStarBanner.css';
const GITHUB_URL = 'https://github.com/davidmonterocrespo24/velxio';
interface Props {
onClose: () => void;
}
export const GitHubStarBanner: React.FC<Props> = ({ onClose }) => (
<div className="gh-star-banner" role="dialog" aria-label="Star Velxio on GitHub">
<div className="gh-star-banner__icon">
{/* GitHub logo */}
<svg width="30" height="30" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z" />
</svg>
</div>
<div className="gh-star-banner__body">
<strong>Enjoying Velxio?</strong>
<p>
A on GitHub helps more developers discover the project it only takes 2 seconds!
</p>
<a
href={GITHUB_URL}
target="_blank"
rel="noopener noreferrer"
className="gh-star-banner__btn"
onClick={onClose}
>
Star on GitHub
</a>
</div>
<button className="gh-star-banner__close" onClick={onClose} aria-label="Dismiss">
</button>
</div>
);

View File

@ -20,6 +20,7 @@ import { Oscilloscope } from '../components/simulator/Oscilloscope';
import { AppHeader } from '../components/layout/AppHeader';
import { SaveProjectModal } from '../components/layout/SaveProjectModal';
import { LoginPromptModal } from '../components/layout/LoginPromptModal';
import { GitHubStarBanner } from '../components/layout/GitHubStarBanner';
import { useSimulatorStore } from '../store/useSimulatorStore';
import { useOscilloscopeStore } from '../store/useOscilloscopeStore';
import { useAuthStore } from '../store/useAuthStore';
@ -68,6 +69,46 @@ export const EditorPage: React.FC = () => {
const [bottomPanelHeight, setBottomPanelHeight] = useState(BOTTOM_PANEL_DEFAULT);
const [saveModalOpen, setSaveModalOpen] = useState(false);
const [loginPromptOpen, setLoginPromptOpen] = useState(false);
const [showStarBanner, setShowStarBanner] = useState(false);
// ── GitHub star prompt (show once: 2nd visit OR after 3 min) ──────────────
useEffect(() => {
const STAR_KEY = 'velxio_star_prompted';
const VISITS_KEY = 'velxio_editor_visits';
const FIRST_VISIT_KEY = 'velxio_editor_first_visit';
const THREE_MIN = 3 * 60 * 1000;
if (localStorage.getItem(STAR_KEY)) return;
// Increment visit counter
const visits = parseInt(localStorage.getItem(VISITS_KEY) ?? '0', 10) + 1;
localStorage.setItem(VISITS_KEY, String(visits));
// Record timestamp of first visit
if (!localStorage.getItem(FIRST_VISIT_KEY)) {
localStorage.setItem(FIRST_VISIT_KEY, String(Date.now()));
}
const firstVisit = parseInt(localStorage.getItem(FIRST_VISIT_KEY)!, 10);
// Show immediately on second+ visit
if (visits >= 2) {
setShowStarBanner(true);
return;
}
// Otherwise schedule after the 3-minute mark
const elapsed = Date.now() - firstVisit;
const delay = Math.max(0, THREE_MIN - elapsed);
const timer = setTimeout(() => {
if (!localStorage.getItem(STAR_KEY)) setShowStarBanner(true);
}, delay);
return () => clearTimeout(timer);
}, []);
const handleDismissStarBanner = () => {
localStorage.setItem('velxio_star_prompted', '1');
setShowStarBanner(false);
};
const [explorerOpen, setExplorerOpen] = useState(true);
const [explorerWidth, setExplorerWidth] = useState(EXPLORER_DEFAULT);
const [isMobile, setIsMobile] = useState(() => window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT}px)`).matches);
@ -348,6 +389,7 @@ export const EditorPage: React.FC = () => {
{saveModalOpen && <SaveProjectModal onClose={() => setSaveModalOpen(false)} />}
{loginPromptOpen && <LoginPromptModal onClose={() => setLoginPromptOpen(false)} />}
{showStarBanner && <GitHubStarBanner onClose={handleDismissStarBanner} />}
</div>
);
};

View File

@ -450,11 +450,13 @@ export const LandingPage: React.FC = () => {
<section className="landing-hero">
<div className="hero-left">
<h1 className="hero-title">
Emulate Hardware.<br />
<span className="hero-accent">In your browser.</span>
Simulate Arduino,<br />
ESP32 &amp; Raspberry Pi.<br />
<span className="hero-accent">And 16 more boards in your browser..</span>
</h1>
<p className="hero-subtitle">
Write, compile, and simulate 17+ boards across 5 CPU architectures no hardware, no cloud, no limits. Real emulation running entirely on your machine.
Write code, compile, and run on 19 real boards Arduino Uno, ESP32, ESP32-C3,
Raspberry Pi Pico, Raspberry Pi 3, and more. No hardware, no cloud, no limits.
</p>
<div className="hero-ctas">
<Link to="/editor" className="cta-primary">
@ -479,7 +481,7 @@ export const LandingPage: React.FC = () => {
<div className="section-header">
<span className="section-label">Supported Hardware</span>
<h2 className="section-title">Every architecture.<br />One tool.</h2>
<p className="section-sub">17 boards across 5 CPU architectures AVR, ARM, RISC-V, Xtensa, and Linux. All running locally, no cloud needed.</p>
<p className="section-sub">19 boards across 5 CPU architectures AVR8, ARM Cortex-M0+, RISC-V, Xtensa, and Linux. All running locally, no cloud needed.</p>
</div>
{/* ── AVR8 · avr8js ────────────────────────────────────────── */}