diff --git a/website/src/hooks/useLatestVersion.ts b/website/src/hooks/useLatestVersion.ts new file mode 100644 index 00000000..87965ffc --- /dev/null +++ b/website/src/hooks/useLatestVersion.ts @@ -0,0 +1,57 @@ +import { useState, useEffect } from "react"; + +const GITHUB_REPO = "tiann/hapi"; +const CACHE_KEY = "hapi-latest-version"; +const CACHE_TTL = 1000 * 60 * 60; // 1 hour + +interface CachedVersion { + version: string; + timestamp: number; +} + +export function useLatestVersion(fallback: string = "latest") { + const [version, setVersion] = useState(() => { + // Try to get from cache first + try { + const cached = localStorage.getItem(CACHE_KEY); + if (cached) { + const { version, timestamp }: CachedVersion = JSON.parse(cached); + if (Date.now() - timestamp < CACHE_TTL) { + return version; + } + } + } catch { + // Ignore cache errors + } + return fallback; + }); + + useEffect(() => { + const fetchVersion = async () => { + try { + const res = await fetch( + `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`, + { headers: { Accept: "application/vnd.github.v3+json" } } + ); + if (!res.ok) return; + + const data = await res.json(); + const tag = data.tag_name as string; + if (tag) { + setVersion(tag); + // Cache the result + localStorage.setItem( + CACHE_KEY, + JSON.stringify({ version: tag, timestamp: Date.now() }) + ); + } + } catch { + // Keep fallback on error + } + }; + + fetchVersion(); + }, []); + + return version; +} diff --git a/website/src/locales/en.json b/website/src/locales/en.json index 31f694a4..a86879d0 100644 --- a/website/src/locales/en.json +++ b/website/src/locales/en.json @@ -7,7 +7,7 @@ "viewOnGithub": "View on GitHub" }, "hero": { - "version": "v0.3.1 is now available", + "version": "{{version}} is now available", "title": "Vibe Coding
Anytime, Anywhere.", "subtitle": "Go for a hike, grab a coffee, or just relax. Your AI agents work in the background. We'll notify you via your favorite messaging app only when they need a nod.", "startBtn": "Start Your Freedom", diff --git a/website/src/locales/zh.json b/website/src/locales/zh.json index 7cdfc822..2fac3013 100644 --- a/website/src/locales/zh.json +++ b/website/src/locales/zh.json @@ -7,7 +7,7 @@ "viewOnGithub": "GitHub" }, "hero": { - "version": "v0.3.1 现已发布", + "version": "{{version}} 现已发布", "title": "Vibe Coding
随时随地,自由编程", "subtitle": "去徒步,去喝咖啡,或者只是放松一下。你的 AI 代理在后台为你工作。只有在需要你确认时,我们才会通过你喜欢的即时通讯应用通知你。", "startBtn": "开启自由之旅", diff --git a/website/src/pages/Home.tsx b/website/src/pages/Home.tsx index 6f0bc25b..e2fd3c1a 100644 --- a/website/src/pages/Home.tsx +++ b/website/src/pages/Home.tsx @@ -9,11 +9,13 @@ import { Link } from "wouter"; import { motion, useScroll, useTransform } from "framer-motion"; import { useTranslation } from "react-i18next"; import { SEO } from "@/components/SEO"; +import { useLatestVersion } from "@/hooks/useLatestVersion"; export default function Home() { const [copied, setCopied] = useState(""); const { scrollY } = useScroll(); const { t } = useTranslation(); + const version = useLatestVersion(); const copyToClipboard = (text: string, key: string) => { navigator.clipboard.writeText(text); @@ -34,9 +36,9 @@ export default function Home() { - {t('hero.version')} + {t('hero.version', { version })} - +