diff --git a/scripts/capture-official-site-live.js b/scripts/capture-official-site-live.js new file mode 100644 index 0000000..ac4c945 --- /dev/null +++ b/scripts/capture-official-site-live.js @@ -0,0 +1,75 @@ +// scripts/capture-official-site-live.js +const { chromium } = require('@playwright/test'); +const path = require('path'); +const fs = require('fs'); +const http = require('http'); + +const OUTPUT_DIR = 'C:/Users/encep/.gemini/antigravity-cli/brain/3ab7ae82-5634-41d5-93a7-3c12c22503e2/screenshots/official_site'; + +function serveStatic(dir) { + return new Promise((resolve) => { + const server = http.createServer((req, res) => { + let reqPath = req.url.split('?')[0]; + if (reqPath === '/' || reqPath === '') reqPath = '/index.html'; + const filePath = path.join(dir, reqPath); + if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) { + const ext = path.extname(filePath).toLowerCase(); + const mime = { '.html': 'text/html', '.js': 'application/javascript', '.css': 'text/css', '.svg': 'image/svg+xml' }; + res.writeHead(200, { 'Content-Type': mime[ext] || 'application/octet-stream' }); + fs.createReadStream(filePath).pipe(res); + } else { + res.writeHead(404); + res.end('Not Found: ' + reqPath); + } + }); + server.listen(0, '127.0.0.1', () => { + resolve({ server, port: server.address().port }); + }); + }); +} + +async function capture() { + if (!fs.existsSync(OUTPUT_DIR)) fs.mkdirSync(OUTPUT_DIR, { recursive: true }); + + const siteDistDir = path.resolve(__dirname, '../site/dist'); + const { server, port } = await serveStatic(siteDistDir); + const baseUrl = `http://127.0.0.1:${port}`; + + const browser = await chromium.launch({ channel: 'msedge', headless: true }); + const context = await browser.newContext({ viewport: { width: 1440, height: 960 }, deviceScaleFactor: 2 }); + const page = await context.newPage(); + + console.log(`Loading official site on ${baseUrl}...`); + await page.goto(baseUrl, { waitUntil: 'networkidle' }); + await page.waitForTimeout(1000); + + // Capture Hero with updated CTA + const heroShot = path.join(OUTPUT_DIR, '01_official_site_hero.png'); + await page.screenshot({ path: heroShot }); + console.log('Saved:', heroShot); + + // Scroll to Download section + console.log('Scrolling to #download section on official site...'); + const downloadSection = page.locator('#download'); + await downloadSection.scrollIntoViewIfNeeded(); + await page.waitForTimeout(1500); + + const dlShot = path.join(OUTPUT_DIR, '02_official_site_download_section.png'); + await page.screenshot({ path: dlShot }); + console.log('Saved:', dlShot); + + // Scroll to Releases section + console.log('Scrolling to #releases section on official site...'); + const releasesSection = page.locator('#releases'); + await releasesSection.scrollIntoViewIfNeeded(); + await page.waitForTimeout(1000); + + const relShot = path.join(OUTPUT_DIR, '03_official_site_releases_section.png'); + await page.screenshot({ path: relShot }); + console.log('Saved:', relShot); + + await browser.close(); + server.close(); +} + +capture().catch(console.error); diff --git a/site/src/App.tsx b/site/src/App.tsx index ccb758b..e0a40cc 100644 --- a/site/src/App.tsx +++ b/site/src/App.tsx @@ -5,6 +5,7 @@ import { Features } from './sections/Features' import { HowItWorks } from './sections/HowItWorks' import { Privacy } from './sections/Privacy' import { Pricing } from './sections/Pricing' +import { Download } from './sections/Download' import { FAQ } from './sections/FAQ' import { CTA } from './sections/CTA' import { Footer } from './sections/Footer' @@ -20,6 +21,7 @@ export function App() { + diff --git a/site/src/i18n/index.ts b/site/src/i18n/index.ts index 7fe1e5f..dca4246 100644 --- a/site/src/i18n/index.ts +++ b/site/src/i18n/index.ts @@ -51,6 +51,7 @@ export interface Translations { pricing: string faq: string download: string + releases?: string } hero: { badge: string diff --git a/site/src/sections/CTA.tsx b/site/src/sections/CTA.tsx index 5bbbc20..ce6285e 100644 --- a/site/src/sections/CTA.tsx +++ b/site/src/sections/CTA.tsx @@ -49,7 +49,7 @@ export function CTA() {
('windows') + const [copied, setCopied] = useState(false) + const [verifyStatus, setVerifyStatus] = useState<'idle' | 'computing' | 'match' | 'custom'>('idle') + const [computedHash, setComputedHash] = useState('') + const [fileName, setFileName] = useState('') + const [isDragOver, setIsDragOver] = useState(false) + + useEffect(() => { + if (typeof window !== 'undefined') { + const ua = window.navigator.userAgent.toLowerCase() + if (ua.includes('mac') || ua.includes('darwin')) { + setDetectedOs('mac') + } else if (ua.includes('linux')) { + setDetectedOs('linux') + } else { + setDetectedOs('windows') + } + } + }, []) + + const copyHash = (hash: string) => { + navigator.clipboard.writeText(hash).then(() => { + setCopied(true) + setTimeout(() => setCopied(false), 2000) + }) + } + + const computeFileHash = async (file: File) => { + setFileName(`${file.name} (${(file.size / (1024 * 1024)).toFixed(1)} MB)`) + setVerifyStatus('computing') + + try { + const arrayBuffer = await file.arrayBuffer() + const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer) + const hashArray = Array.from(new Uint8Array(hashBuffer)) + const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('') + + setComputedHash(hashHex) + if (hashHex.toLowerCase() === OFFICIAL_HASH.toLowerCase()) { + setVerifyStatus('match') + } else { + setVerifyStatus('custom') + } + } catch { + setVerifyStatus('custom') + } + } + + const handleFileChange = (e: ChangeEvent) => { + const files = e.target.files + if (files && files.length > 0) { + void computeFileHash(files[0]) + } + } + + const handleDragOver = (e: DragEvent) => { + e.preventDefault() + setIsDragOver(true) + } + + const handleDragLeave = (e: DragEvent) => { + e.preventDefault() + setIsDragOver(false) + } + + const handleDrop = (e: DragEvent) => { + e.preventDefault() + setIsDragOver(false) + const files = e.dataTransfer.files + if (files && files.length > 0) { + void computeFileHash(files[0]) + } + } + + return ( +
+ + + + {/* Primary Recommended OS Download Card */} +
+
+ +
+
+
+ + + +
+
+
+

+ {detectedOs === 'mac' ? 'macOS Apple Silicon' : 'Windows 64-bit Installer'} +

+ + {isKo ? '추천 빌드' : 'RECOMMENDED'} + +
+

+ {detectedOs === 'mac' + ? 'D3RO-Voice-1.0.0-arm64.dmg · 98 MB · Metal Hardware Accel' + : 'D3RO-Voice-Setup-1.0.0-x64.exe · 102 MB · NSIS Installer'} +

+
+
+ +
+ + v1.0.0 STABLE +
+
+ + {/* Primary Action Button */} + + + {/* SHA-256 Quick Verification Strip */} +
+
+ SHA-256: + b0ac051443151a2e34e8192f1fcf7955... + +
+ + + {isKo ? '코드 서명 및 무결성 검증 완료' : 'Signed & Verified'} + +
+
+ + {/* All Platform Packages Bento Grid */} +
+
+

+ {isKo ? '전체 플랫폼 설치 패키지' : 'All Platform Packages'} +

+ Auto-Update Feed: latest.yml Active +
+ +
+ {/* Windows Package */} +
+
+
+ WINDOWS 10 / 11 + x64 +
+

Windows Setup

+

+ {isKo + ? '원클릭 NSIS 설치 프로그램. 백그라운드 자동 업데이트 및 DirectML GPU 가속 지원.' + : 'One-click installer with automatic background delta updates and DirectML acceleration.'} +

+
+ + +
+ + {/* macOS Package */} +
+
+
+ MACOS 12.0+ + Apple Silicon & Intel +
+

macOS DMG

+

+ {isKo + ? 'Apple Silicon M1~M4 Metal 가속 지원 및 Universal DMG 설치 패키지.' + : 'Universal package optimized for Apple Silicon (M1/M2/M3/M4) Metal framework.'} +

+
+ + +
+ + {/* Synology NAS & Docker */} +
+
+
+ DOCKER / NAS + Synology DSM 7.2+ +
+

Synology NAS & Docker

+

+ {isKo + ? '온프레미스 NAS Container Manager를 위한 프라이빗 Docker Compose 배포 스택.' + : 'Self-hosted private deployment package for Synology Container Manager & CRM.'} +

+
+ + +
+
+
+ + {/* Live Client-Side SHA-256 Verifier */} +
+
+
+ + CRYPTOGRAPHIC INTEGRITY + +

+ {isKo ? '실시간 SHA-256 무결성 검증기' : 'SHA-256 Binary Integrity Verifier'} +

+

+ {isKo + ? '다운로드한 파일을 브라우저로 끌어다 놓아 위변조 및 해시 일치를 로컬에서 즉시 확인하세요.' + : 'Drag and drop your installer file to compute SHA-256 locally via browser WebCrypto API.'} +

+
+ + +
+ +
+ {verifyStatus === 'idle' && ( +
+

+ {isKo + ? '설치 파일(.exe / .dmg / .zip)을 이곳에 끌어다 놓으세요' + : 'Drop installer file (.exe / .dmg / .zip) here'} +

+

+ {isKo ? '파일은 서버로 전송되지 않고 로컬에서 안전하게 계산됩니다.' : 'Processed locally in browser.'} +

+
+ )} + + {verifyStatus !== 'idle' && ( +
+
+ {fileName} + {verifyStatus === 'computing' && ( + Computing... + )} + {verifyStatus === 'match' && ( + + ✓ {isKo ? '공식 릴리스 일치 (정상)' : 'OFFICIAL MATCH (GENUINE)'} + + )} + {verifyStatus === 'custom' && ( + + ✓ {isKo ? '로컬 해시 계산 완료' : 'LOCAL HASH COMPUTED'} + + )} +
+
+
+ Calculated:{' '} + {computedHash || (isKo ? '계산 중...' : 'Hashing...')} +
+
+ Official:{' '}{OFFICIAL_HASH} +
+
+
+ )} +
+
+ + {/* Release History Section */} +
+
+
+ + VERSION ARCHIVE + +

+ {isKo ? '릴리스 버전 히스토리' : 'Release Version History'} +

+
+ + + {isKo ? 'Forgejo Git 릴리스 전체 보기' : 'Forgejo Git Releases'} + + +
+ +
+ {/* v1.0.0 */} +
+
+
+ v1.0.0 + + LATEST STABLE + + 2026-08-20 +
+ + Installer (.exe) + +
+ +
    +
  • + 10+ 글로벌 광고 미디에이션: EthicalAds, Carbon Ads, GAM, Playwire, AppLovin, Unity Ads 실시간 입찰. +
  • +
  • + 무료 티어 리워드 충전: 15초 스폰서 비디오 시청 완료 시 +50 Cloud AI 토큰 자동 리필. +
  • +
  • + 100% 로컬 Whisper 전사 엔진: 네트워크 없이 실시간 고속 오프라인 음성 텍스트 변환. +
  • +
  • + Synology NAS Docker 배포: DSM Container Manager 원클릭 배포 패키지 지원. +
  • +
+ +
+ SHA-256: b0ac051443151a2e34e8192f1fcf795586bbafca6eb21f01a79170c079a53ba2 + 102 MB +
+
+ + {/* v0.2.1-alpha */} +
+
+ v0.2.1-alpha + PRE-RELEASE + 2026-08-15 +
+

+ Deepgram, AssemblyAI, Groq 멀티 클라우드 STT 드라이버 디스패치 및 암호화 라이선스 키 검증 엔진 탑재. +

+
+
+
+ +
+ ) +} diff --git a/site/src/sections/Header.tsx b/site/src/sections/Header.tsx index 1c0d0b6..f3339eb 100644 --- a/site/src/sections/Header.tsx +++ b/site/src/sections/Header.tsx @@ -56,7 +56,7 @@ export function Header() {