1// DOM Elements Selection 2const templateSelect = document.getElementById('templateSelect'); 3const templateBadge = document.getElementById('templateBadge'); 4 5const fullNameInput = document.getElementById('fullName'); 6const locationInput = document.getElementById('location'); 7const emailInput = document.getElementById('email'); 8const phoneInput = document.getElementById('phone'); 9const githubInput = document.getElementById('github'); 10const titleInput = document.getElementById('title'); 11 12const eduUniInput = document.getElementById('eduUni'); 13const eduDegreeInput = document.getElementById('eduDegree'); 14const eduALInput = document.getElementById('eduAL'); 15const eduOLInput = document.getElementById('eduOL'); 16 17const projectsTextInput = document.getElementById('projectsText'); 18const skillsLangInput = document.getElementById('skillsLang'); 19const skillsCloudInput = document.getElementById('skillsCloud'); 20const accomplishmentsTextInput = document.getElementById('accomplishmentsText'); 21const referencesTextInput = document.getElementById('referencesText'); 22 23const generateBtn = document.getElementById('generateBtn'); 24const loadSampleBtn = document.getElementById('loadSampleBtn'); 25const resultContainer = document.getElementById('resultContainer'); 26const statusText = document.getElementById('statusText'); 27const progressBar = document.getElementById('progressBar'); 28const downloadLink = document.getElementById('downloadLink'); 29 30const tabHtmlBtn = document.getElementById('tabHtmlBtn'); 31const tabMdBtn = document.getElementById('tabMdBtn'); 32const htmlPreviewContainer = document.getElementById('htmlPreviewContainer'); 33const mdPreviewContainer = document.getElementById('mdPreviewContainer'); 34const htmlPreviewArea = document.getElementById('htmlPreviewArea'); 35const markdownEditor = document.getElementById('markdownEditor'); 36 37const BACKEND_URL = '/api/v1/vault/generate-pdf'; 38let isManualMarkdownEdit = false; 39 40// Sample Moratuwa CSE Resume Dataset 41const sampleCseData = { 42 fullName: "Keshana Dewranga", 43 location: "Moratuwa, Sri Lanka", 44 email: "[email protected]", 45 phone: "+94 76 903 3466", 46 github: "KeshRD", 47 title: "Computer Science and Engineering Undergraduate", 48 eduUni: "University of Moratuwa (2024 â 2028 Expected)", 49 eduDegree: "BSc Engineering(Hons), Computer Science and Engineering", 50 eduAL: "Physical Science Stream â Combined Maths, Physics, Chemistry | Results: 3A's, Island Rank: 335 (2023)", 51 eduOL: "Results: 9A's", 52 projectsText: `### Resource Coordination Platform (RCP) (Disaster Relief System) *(2026 â Present)* 53- **Backend API & Database Modeling:** Designed relational database schemas using SQLAlchemy ORM (PostgreSQL) and developed efficient RESTful API routes using FastAPI for core microservices. 54- **Mobile App Development:** Built the Volunteer Mobile Application using React Native (Expo), integrating live map navigation, secure JWT authentication, and REST API endpoints. 55- **Real-Time Data Integration:** Upgraded the notification system to a real-time push architecture by establishing live WebSocket (Go) connections to frontend clients. 56- **Event-Driven Architecture:** Gained hands-on experience in consuming asynchronous data streams routed through RabbitMQ message brokers for instant disaster alerts. 57- **Tech Stack:** React Native, Next.js, FastAPI, SQLAlchemy, PostgreSQL, WebSockets, RabbitMQ, Docker 58 59### Lume (Mindfulness & Habit Tracker Mobile App) *(2026)* 60- Developed a modern Sinhala-language mindfulness application featuring a meditation timer, reflective journaling, and 7-day mental well-being challenges. 61- Integrated Google Gemini API to build a context-aware AI chatbot delivering guidance rooted in Buddhist philosophy. 62- Implemented secure user authentication and cloud storage architecture using Supabase for tracking daily progress, photo uploads, and habit formations. 63- **Tech Stack:** React Native, Expo, Supabase, Google Gemini API 64 65### PregmaAI (AI Pregnancy Companion) *(2025, 2026)* 66#### Web App Version *(2026)* 67- Developed an AI-powered healthcare platform for expectant mothers to track daily symptoms, fetal movements, and manage pregnancy milestones efficie
67ntly. 68- Integrated an intelligent NLP-based AI assistant analyzing user-logged health vitals to generate personalized wellness plans and risk factor alerts. 69- Implemented a real-time vital sign tracking dashboard with dynamic data visualization and a localized RAG pipeline providing medically relevant healthcare information. 70- **Tech Stack:** React, Tailwind CSS, FastAPI, Gemini API, Recharts, LangChain, PostgreSQL, Git 71 72#### Android Mobile App *(2025)* 73- Architected an AI Speech-to-Summary pipeline integrating Android SpeechRecognizer with Hugging Face API (bart-large-cnn-samsum) for real-time doctor consultation summarization, cached locally via Room DB. 74- Implemented Firebase Auth and Cloud Firestore real-time listeners for live tracking of pregnancy progress, kick counter, contraction timer, and appointment calendar. 75 76### Bright-Buy (Team Project) *(2025)* 77- Developed a full-stack e-commerce application featuring a modern user interface, secure JWT-based authentication, and product image upload capabilities. 78- Integrated an AI-powered chatbot to assist customers with real-time product inquiries, enhancing customer support. 79- Implemented robust database architecture utilizing PostgreSQL with stored procedures, views, and triggers. 80- **Tech Stack:** React, PostgreSQL, Node.js, Express, Chart.js, Axios`, 81 skillsLang: "JavaScript, TypeScript, Python, SQL, Go, Java, React.js, FastAPI, Next.js, React Native", 82 skillsCloud: "PostgreSQL, MySQL, AWS, Docker, Kubernetes, Supabase, Firebase, Git, GitHub, Postman, Figma", 83 accomplishmentsText: `- **Finalist**, **SLIOT 2025** national IoT competition (contributed to "Ceylon Riya" Kotlin Mobile app development) *(2025)* 84- **Semi-Finalist**, **IDEALIZE 2025** Web and App Development Competition, organized by AIESEC *(2025)* 85- **Certifications:** SLIOT Finalist â SLT MOBITEL | Semi-Finalist - IDEALIZE 2025 86- **Volunteer Experience:** Compering Team Lead (Gammaddata IEEE API), MC - Octwave 2.0 Finals, OC Member (SLT Robogames, SLIOT, Hit the Ground)`, 87 referencesText: `**Prof. Dulani Meedeniya** 88Department of Computer Science and Engineering, Faculty of Engineering, University of Moratuwa, Sri Lanka. 89*Email:* [email protected] 90 91**Dr. Adeesha Wijayasiri** 92Department of Computer Science and Engineering, Faculty of Engineering, University of Moratuwa, Sri Lanka. 93*Email:* [email protected]` 94}; 95 96// Fill inputs with data object 97function loadDataIntoInputs(data) { 98 fullNameInput.value = data.fullName || ''; 99 locationInput.value = data.location || ''; 100 emailInput.value = data.email || ''; 101 phoneInput.value = data.phone || ''; 102 githubInput.value = data.github || ''; 103 titleInput.value = data.title || ''; 104 105 eduUniInput.value = data.eduUni || ''; 106 eduDegreeInput.value = data.eduDegree || ''; 107 eduALInput.value = data.eduAL || ''; 108 eduOLInput.value = data.eduOL || ''; 109 110 projectsTextInput.value = data.projectsText || ''; 111 skillsLangInput.value = data.skillsLang || ''; 112 skillsCloudInput.value = data.skillsCloud || ''; 113 accomplishmentsTextInput.value = data.accomplishmentsText || ''; 114 referencesTextInput.value = data.referencesText || ''; 115 116 isManualMarkdownEdit = false; 117 updateMarkdownFromForm(); 118} 119 120// Synthesize Markdown text from structured form fields 121function generateMarkdownFromForm() { 122 const name = fullNameInput.value || 'Keshana Dewranga'; 123 const title = titleInput.value || 'Computer Science & Engineering Undergraduate'; 124 const uni = eduUniInput.value || 'University of Moratuwa (2024 â 2028 Expected)'; 125 const degree = eduDegreeInput.value || 'BSc Engineering(Hons), Computer Science and Engineering'; 126 const al = eduALInput.value || "Physical Science Stream â Combined Maths, Physics, Chemistry | Results: 3A's, Island Rank: 335"; 127 const ol = eduOLInput.value || "Results: 9A's"; 128 129 let md = `## Education\n\n`; 130 md += `### ${uni}\n`; 131 md += `**${degree}**\n\n`; 132 if (al) md += `- **G.C.E. Advanced Level (2023):** ${al}\n`; 133 if (ol) md += `- **G.C.E. Ordinary Level:** ${ol}\n`; 134 135 md += `\n---\n\n## Projects\n\n`; 136 md += `${projectsTextInput.value || 'Add project descriptions...'}\n`; 137 138 md += `\n---\n\n## Skills and Technologies\n\n`; 139 if (skillsLangInput.value) md += `- **Languages & Frameworks:** ${skillsLangInput.value}\n`; 140 if (skillsCloudInput.value) md += `- **Databases, DevOps & Tools:** ${skillsCloudInput.value}\n`; 141 md += `- **Core CS:** Data Structures & Algorithms, OOP, Database Systems, Softw
141are Engineering\n`; 142 md += `- **Soft Skills:** Team Collaboration, Problem Solving, Time Management, Technical Communication, Adaptability\n`; 143 144 if (accomplishmentsTextInput.value.trim()) { 145 md += `\n---\n\n## Accomplishments & Leadership\n\n`; 146 md += `${accomplishmentsTextInput.value}\n`; 147 } 148 149 if (referencesTextInput.value.trim()) { 150 md += `\n---\n\n## References\n\n`; 151 md += `${referencesTextInput.value}\n`; 152 } 153 154 return md; 155} 156 157// Update Markdown & Live HTML Preview 158function updateMarkdownFromForm() { 159 if (isManualMarkdownEdit) return; 160 161 const mdContent = generateMarkdownFromForm(); 162 markdownEditor.value = mdContent; 163 renderHtmlPreview(mdContent); 164} 165 166// Render HTML Preview in right-hand column 167function renderHtmlPreview(markdownText) { 168 if (window.marked) { 169 htmlPreviewArea.innerHTML = marked.parse(markdownText || ''); 170 } else { 171 htmlPreviewArea.textContent = markdownText; 172 } 173} 174 175// Event listeners for structured inputs 176[fullNameInput, locationInput, emailInput, phoneInput, githubInput, titleInput, 177 eduUniInput, eduDegreeInput, eduALInput, eduOLInput, 178 projectsTextInput, skillsLangInput, skillsCloudInput, accomplishmentsTextInput, referencesTextInput].forEach(input => { 179 input.addEventListener('input', () => { 180 isManualMarkdownEdit = false; 181 updateMarkdownFromForm(); 182 }); 183}); 184 185// Markdown direct editor handler 186markdownEditor.addEventListener('input', () => { 187 isManualMarkdownEdit = true; 188 renderHtmlPreview(markdownEditor.value); 189}); 190 191// Template selector badge update 192templateSelect.addEventListener('change', () => { 193 const selectedText = templateSelect.options[templateSelect.selectedIndex].text; 194 templateBadge.textContent = `Theme: ${selectedText.split('(')[0].replace(/[^a-zA-Z0-9 ]/g, '').trim()}`; 195}); 196 197// Tab switching (Rendered HTML vs Raw Markdown) 198tabHtmlBtn.addEventListener('click', () => { 199 tabHtmlBtn.className = "text-xs font-semibold px-3 py-1.5 rounded-lg bg-blue-600 text-white transition"; 200 tabMdBtn.className = "text-xs font-semibold px-3 py-1.5 rounded-lg bg-gray-800 text-gray-400 hover:text-gray-200 transition"; 201 htmlPreviewContainer.classList.remove('hidden'); 202 mdPreviewContainer.classList.add('hidden'); 203}); 204 205tabMdBtn.addEventListener('click', () => { 206 tabMdBtn.className = "text-xs font-semibold px-3 py-1.5 rounded-lg bg-blue-600 text-white transition"; 207 tabHtmlBtn.className = "text-xs font-semibold px-3 py-1.5 rounded-lg bg-gray-800 text-gray-400 hover:text-gray-200 transition"; 208 mdPreviewContainer.classList.remove('hidden'); 209 htmlPreviewContainer.classList.add('hidden'); 210}); 211 212// "Load Moratuwa CSE Sample CV" Button 213loadSampleBtn.addEventListener('click', () => {
214 loadDataIntoInputs(sampleCseData); 215 statusText.textContent = "⨠Moratuwa CSE Sample CV loaded successfully! Click generate to compile PDF."; 216 resultContainer.classList.remove('hidden'); 217 downloadLink.classList.add('hidden'); 218 progressBar.style.width = "100%"; 219}); 220 221// Initial sample data load 222loadDataIntoInputs(sampleCseData); 223 224// Asynchronous Job Polling Handler 225async function pollJobStatus(jobId) { 226 const maxAttempts = 35; 227 let attempts = 0; 228 229 const pollInterval = setInterval(async () => { 230 attempts++; 231 try { 232 const response = await fetch(`/api/v1/vault/jobs/${jobId}`); 233 const data = await response.json(); 234 235 if (data.status === 'processing') { 236 statusText.textContent = "âï¸ Worker compiling Academic Puppeteer HTML to A4 PDF Buffer..."; 237 progressBar.style.width = "65%"; 238 } else if (data.status === 'completed' && data.result) { 239 clearInterval(pollInterval); 240 statusText.textContent = "â Academic CV Snapshot Sync Complete! Link expires in 15 mins."; 241 progressBar.style.width = "100%"; 242 downloadLink.href = data.result.downloadUrl; 243 downloadLink.classList.remove('hidden'); 244 generateBtn.disabled = false; 245 generateBtn.innerHTML = "<span>ð Dispatch Async Job & Vault PDF</span>"; 246 } else if (data.status === 'failed') { 247 clearInterval(pollInterval); 248 statusText.textContent = `â Compilation Failed: ${data.error || 'Worker execution error'}`; 249 progressBar.style.width = "0%"; 250 generateBtn.disabled = false; 251 generateBtn.innerHTML = "<span>ð Dispatch Async Job & Vault PDF</span>"; 252 } 253 254 if (attempts >= maxAttempts) { 255 clearInterval(pollInterval); 256 statusText.textContent = "â ï¸ Timeout: Job compilation taking longer than expected."; 257 generateBtn.disabled = false; 258 generateBtn.innerHTML = "<span>ð Dispatch Async Job & Vault PDF</span>"; 259 } 260 261 } catch (err) { 262 console.error("Polling Error:", err); 263 } 264 }, 1000); 265} 266 267// Generate PDF Button Click Handler 268generateBtn.addEventListener('click', async () => { 269 const finalMarkdown = markdownEditor.value || generateMarkdownFromForm(); 270 271 resultContainer.classList.remove('hidden'); 272 downloadLink.classList.add('hidden'); 273 statusText.textContent = "â³ Job Dispatched to Background Worker Queue..."; 274 progressBar.style.width = "20%"; 275 generateBtn.disabled = true; 276 generateBtn.innerHTML = "<span>Processing Queue...</span>"; 277 278 try { 279 const response = await fetch(BACKEND_URL, { 280 method: 'POST', 281 headers: { 282 'Content-Type': 'application/json' 283 }, 284 body: JSON.stringify({ 285 markdownContent: finalMarkdown, 286 template: templateSelect.value, 287 metadata: { 288 fullName: fullNameInput.value, 289 location: locationInput.value, 290 email: emailInput.value, 291 phone: phoneInput.value, 292 github: githubInput.value, 293 title: titleInput.value 294 } 295 }) 296 }); 297 298 const data = await response.json(); 299 300 if (data.success && data.jobId) { 301 statusText.textContent = `ð¥ Job Queued (ID: ${data.jobId.slice(0, 10)}...). Worker rendering PDF...`; 302 progressBar.style.width = "40%"; 303 pollJobStatus(data.jobId); 304 } else { 305 statusText.textContent = `â Dispatch Failed: ${data.error || 'Unknown error'}`; 306 generateBtn.disabled = false; 307 generateBtn.innerHTML = "<span>ð Dispatch Async Job & Vault PDF</span>"; 308 } 309 310 } catch (error) { 311 console.error("Frontend Dispatch Error:", error); 312 statusText.textContent = "â Network Error: Could not connect to the Vault API Gateway."; 313 generateBtn.disabled = false;
314 generateBtn.innerHTML = "<span>ð Dispatch Async Job & Vault PDF</span>"; 315 } 316});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.