diff --git a/frontend/src/components/layout/GitHubStarBanner.css b/frontend/src/components/layout/GitHubStarBanner.css new file mode 100644 index 0000000..79d0c0e --- /dev/null +++ b/frontend/src/components/layout/GitHubStarBanner.css @@ -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; +} diff --git a/frontend/src/components/layout/GitHubStarBanner.tsx b/frontend/src/components/layout/GitHubStarBanner.tsx new file mode 100644 index 0000000..de81943 --- /dev/null +++ b/frontend/src/components/layout/GitHubStarBanner.tsx @@ -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 = ({ onClose }) => ( +
+
+ {/* GitHub logo */} + +
+ +
+ Enjoying Velxio? +

+ A ⭐ on GitHub helps more developers discover the project — it only takes 2 seconds! +

+ + ⭐ Star on GitHub + +
+ + +
+); diff --git a/frontend/src/pages/EditorPage.tsx b/frontend/src/pages/EditorPage.tsx index f8614a4..99d68b1 100644 --- a/frontend/src/pages/EditorPage.tsx +++ b/frontend/src/pages/EditorPage.tsx @@ -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 && setSaveModalOpen(false)} />} {loginPromptOpen && setLoginPromptOpen(false)} />} + {showStarBanner && } ); }; diff --git a/frontend/src/pages/LandingPage.tsx b/frontend/src/pages/LandingPage.tsx index fd4ae17..98606f7 100644 --- a/frontend/src/pages/LandingPage.tsx +++ b/frontend/src/pages/LandingPage.tsx @@ -450,11 +450,13 @@ export const LandingPage: React.FC = () => {

- Emulate Hardware.
- In your browser. + Simulate Arduino,
+ ESP32 & Raspberry Pi.
+ And 16 more boards in your browser..

- 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.

@@ -479,7 +481,7 @@ export const LandingPage: React.FC = () => {
Supported Hardware

Every architecture.
One tool.

-

17 boards across 5 CPU architectures — AVR, ARM, RISC-V, Xtensa, and Linux. All running locally, no cloud needed.

+

19 boards across 5 CPU architectures — AVR8, ARM Cortex-M0+, RISC-V, Xtensa, and Linux. All running locally, no cloud needed.

{/* ── AVR8 · avr8js ────────────────────────────────────────── */}