Spaces:
Running
Running
| <html lang="ko"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Liger - Green-Drive Optimizer</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link | |
| href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;700&family=Noto+Sans+KR:wght@300;400;500;700&display=swap" | |
| rel="stylesheet"> | |
| <link rel="stylesheet" href="style.css"> | |
| <meta name="description" content="Liger: 친환경 주행 경로 최적화 및 충전소 추천 솔루션"> | |
| </head> | |
| <body> | |
| <div class="background-glob"></div> | |
| <nav class="navbar"> | |
| <div class="logo">LIGER</div> | |
| <div class="nav-links"> | |
| <a href="#demo">Demo</a> | |
| <a href="#presentation">Presentation</a> | |
| <a href="#workflow">Workflow</a> | |
| </div> | |
| </nav> | |
| <header class="hero"> | |
| <div class="hero-content"> | |
| <span class="badge">Green-Drive Optimizer</span> | |
| <h1>스마트한 전기차 라이프의 시작,<br> <span class="gradient-text">LIGER</span></h1> | |
| <p>최적의 경로와 충전소를 찾아주는 당신만의 AI 드라이빙 파트너</p> | |
| <div class="cta-group"> | |
| <a href="#demo" class="btn primary">데모 영상 보기</a> | |
| <a href="#workflow" class="btn secondary">워크플로우 확인하기</a> | |
| </div> | |
| </div> | |
| <div class="hero-image"> | |
| <img src="hero_image.png" alt="Liger Dashboard Preview" class="glass-card"> | |
| </div> | |
| </header> | |
| <main> | |
| <section id="demo" class="section"> | |
| <div class="section-header"> | |
| <h2>Demo Video</h2> | |
| <p>Liger가 어떻게 작동하는지 확인해보세요</p> | |
| </div> | |
| <div class="video-container glass-card"> | |
| <video controls poster="hero_image.png"> | |
| <source src="liger_demo.mp4" type="video/mp4"> | |
| 브라우저가 비디오 태그를 지원하지 않습니다. | |
| </video> | |
| </div> | |
| </section> | |
| <section id="presentation" class="section"> | |
| <div class="section-header"> | |
| <h2>Presentation</h2> | |
| <p>프로젝트 상세 소개 및 기술 스택</p> | |
| </div> | |
| <div class="ppt-container glass-card"> | |
| <div class="ppt-wrapper glass-card" style="padding: 0; overflow: hidden; height: auto;"> | |
| <div class="pdf-canvas-container"> | |
| <canvas id="pdf-render"></canvas> | |
| </div> | |
| <div class="slide-controls"> | |
| <button id="prev-page" class="btn secondary small">Prev</button> | |
| <span id="page-count-container"> | |
| Page <span id="page-num" style="color: white; font-weight: bold;">--</span> of <span | |
| id="page-count">--</span> | |
| </span> | |
| <button id="next-page" class="btn secondary small">Next</button> | |
| <a href="liger_presentation.pdf" download class="btn small" | |
| style="margin-left: 1rem; padding: 0.4rem 0.8rem; font-size: 0.8rem; background: rgba(255,255,255,0.05);"> | |
| </a> | |
| </div> | |
| </div> | |
| <!-- PDF.js Library --> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script> | |
| <script> | |
| const url = 'liger_presentation.pdf'; | |
| let pdfDoc = null, | |
| pageNum = 1, | |
| pageRendering = false, | |
| pageNumPending = null, | |
| scale = 1.5, | |
| canvas = document.getElementById('pdf-render'), | |
| ctx = canvas.getContext('2d'); | |
| // Set worker source | |
| pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'; | |
| /** | |
| * Get page info from document, resize canvas accordingly, and render page. | |
| * @param num Page number. | |
| */ | |
| function renderPage(num) { | |
| pageRendering = true; | |
| // Fetch page | |
| pdfDoc.getPage(num).then(function (page) { | |
| // Adjust scale to fit the container width if needed | |
| const containerWidth = document.querySelector('.pdf-canvas-container').clientWidth; | |
| const viewport = page.getViewport({ scale: 1 }); | |
| // Calculate scale to fit width (minus some padding) | |
| let desiredScale = (containerWidth - 40) / viewport.width; | |
| // Limit max scale to keep quality reasonable | |
| if (desiredScale > 1.5) desiredScale = 1.5; | |
| const scaledViewport = page.getViewport({ scale: desiredScale }); | |
| canvas.height = scaledViewport.height; | |
| canvas.width = scaledViewport.width; | |
| // Render PDF page into canvas context | |
| const renderContext = { | |
| canvasContext: ctx, | |
| viewport: scaledViewport | |
| }; | |
| const renderTask = page.render(renderContext); | |
| // Wait for render to finish | |
| renderTask.promise.then(function () { | |
| pageRendering = false; | |
| if (pageNumPending !== null) { | |
| renderPage(pageNumPending); | |
| pageNumPending = null; | |
| } | |
| }); | |
| }); | |
| // Update page counters | |
| document.getElementById('page-num').textContent = num; | |
| // Update buttons state | |
| document.getElementById('prev-page').disabled = num <= 1; | |
| document.getElementById('next-page').disabled = num >= pdfDoc.numPages; | |
| } | |
| /** | |
| * If another page rendering in progress, waits until the rendering is | |
| * finised. Otherwise, executes rendering immediately. | |
| */ | |
| function queueRenderPage(num) { | |
| if (pageRendering) { | |
| pageNumPending = num; | |
| } else { | |
| renderPage(num); | |
| } | |
| } | |
| /** | |
| * Displays previous page. | |
| */ | |
| function onPrevPage() { | |
| if (pageNum <= 1) { | |
| return; | |
| } | |
| pageNum--; | |
| queueRenderPage(pageNum); | |
| } | |
| document.getElementById('prev-page').addEventListener('click', onPrevPage); | |
| /** | |
| * Displays next page. | |
| */ | |
| function onNextPage() { | |
| if (pageNum >= pdfDoc.numPages) { | |
| return; | |
| } | |
| pageNum++; | |
| queueRenderPage(pageNum); | |
| } | |
| document.getElementById('next-page').addEventListener('click', onNextPage); | |
| /** | |
| * Asynchronously downloads PDF. | |
| */ | |
| pdfjsLib.getDocument(url).promise.then(function (pdfDoc_) { | |
| pdfDoc = pdfDoc_; | |
| document.getElementById('page-count').textContent = pdfDoc.numPages; | |
| // Initial/first page rendering | |
| renderPage(pageNum); | |
| }).catch(function (error) { | |
| console.error('Error loading PDF:', error); | |
| // Fallback UI or message could go here | |
| ctx.font = '20px Arial'; | |
| ctx.fillStyle = 'white'; | |
| ctx.fillText('Error loading PDF.', 50, 50); | |
| }); | |
| // Handle window resize | |
| window.addEventListener('resize', () => { | |
| // Re-render current page to adjust scale | |
| if (pdfDoc) { | |
| queueRenderPage(pageNum); | |
| } | |
| }); | |
| </script> | |
| </div> | |
| </section> | |
| <section id="workflow" class="section"> | |
| <div class="section-header"> | |
| <h2>Core Logic & Workflow</h2> | |
| <p>Google Calendar 연동부터 AI 최적화 제안까지, One-Stop 자동화 프로세스</p> | |
| </div> | |
| <div class="workflow-container glass-card"> | |
| <div class="workflow-img-wrapper"> | |
| <img src="workflow_diagram.png" alt="n8n Workflow Diagram" class="full-width-img"> | |
| </div> | |
| <div class="workflow-details"> | |
| <div class="logic-step"> | |
| <h3>1. Trigger & analysis</h3> | |
| <p>구글 캘린더에 새로운 일정이 등록되면 자동으로 감지하여 출발지와 목적지, 그리고 이동 시간을 계산합니다.</p> | |
| </div> | |
| <div class="logic-step"> | |
| <h3>2. Dual-Optimization Engine</h3> | |
| <p>이동 경로를 바탕으로 두 가지 시나리오를 병렬로 생성합니다.</p> | |
| <ul> | |
| <li><strong>탄소 배출 최적화:</strong> 친환경 경로 및 운전 습관 제안</li> | |
| <li><strong>경제적 충전 시나리오:</strong> 최저가 충전소 및 배터리 효율 분석</li> | |
| </ul> | |
| </div> | |
| <div class="logic-step"> | |
| <h3>3. AI Evaluation (Upstage Solar)</h3> | |
| <p>Upstage Solar LLM이 환경성과 경제성을 종합적으로 평가하여, 사용자에게 가장 밸런스 잡힌 최적의 스케줄을 확정합니다.</p> | |
| </div> | |
| <div class="logic-step"> | |
| <h3>4. Action & Report</h3> | |
| <p>최종 확정된 일정은 캘린더에 다시 등록되며, 상세 분석 리포트가 이메일로 즉시 전송됩니다.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <section id="team" class="section"> | |
| <div class="section-header"> | |
| <h2>Our Team</h2> | |
| <p>Liger 프로젝트를 만들어가는 사람들</p> | |
| </div> | |
| <div class="team-container glass-card"> | |
| <!-- Image removed as requested --> | |
| <div class="team-grid"> | |
| <div class="team-member"> | |
| <h4>강윤재</h4> | |
| <span class="role">Data Research & Scenario</span> | |
| <p>전기차 전비/배터리 자료 조사<br>경제적 충전 시나리오 로직 설계</p> | |
| </div> | |
| <div class="team-member"> | |
| <h4>엄서훈</h4> | |
| <span class="role">Workflow Architect</span> | |
| <p>n8n 메인 워크플로우 개발<br>최종 리포트 템플릿 구현</p> | |
| </div> | |
| <div class="team-member"> | |
| <h4>이승현</h4> | |
| <span class="role">Project Lead</span> | |
| <p>프로젝트 총괄 및 일정 조율<br>서비스 기획 및 소개서 작성</p> | |
| </div> | |
| <div class="team-member"> | |
| <h4>이하윤</h4> | |
| <span class="role">Presentation & Data</span> | |
| <p>발표 자료 제작 및 최종 발표<br>탄소 배출 데이터 리서치</p> | |
| </div> | |
| <div class="team-member"> | |
| <h4>차성경</h4> | |
| <span class="role">Media & Validation</span> | |
| <p>아이디어 산출 및 검증<br>홍보 영상 제작 및 자료 조사</p> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| </main> | |
| <footer> | |
| <p>© 2024 Liger Team. All rights reserved.</p> | |
| <div class="socials"> | |
| <a href="https://huggingface.co/dltmdgus" target="_blank">Hugging Face</a> | |
| </div> | |
| </footer> | |
| <script> | |
| // Smooth scroll for anchor links | |
| document.querySelectorAll('a[href^="#"]').forEach(anchor => { | |
| anchor.addEventListener('click', function (e) { | |
| e.preventDefault(); | |
| document.querySelector(this.getAttribute('href')).scrollIntoView({ | |
| behavior: 'smooth' | |
| }); | |
| }); | |
| }); | |
| // Simple intersection observer for fade-in animations | |
| const observerOptions = { | |
| threshold: 0.1 | |
| }; | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| entry.target.classList.add('visible'); | |
| } | |
| }); | |
| }, observerOptions); | |
| document.querySelectorAll('.section, .hero').forEach(el => { | |
| el.classList.add('fade-in-section'); | |
| observer.observe(el); | |
| }); | |
| </script> | |
| </body> | |
| </html> |