⌨ Terminal
✏ Editor
πŸ‘ Preview
πŸ“¦ Export
Live preview of exported portfolio
`; } function exportHTML(){ const html = generateStandaloneHTML(); download('portfolio.html', html, 'text/html'); } function exportJSON(){ download('portfolio-profile.json', JSON.stringify(profile, null, 2), 'application/json'); } function copyJSON(){ navigator.clipboard.writeText(JSON.stringify(profile, null, 2)); alert('Profile JSON copied to clipboard!'); } function generateMarkdown(){ let md = `# ${profile.name}\n\n`; md += `**${profile.title}** Β· ${profile.location}\n\n`; md += `${profile.email} Β· ${profile.website}\n\n`; md += `---\n\n## About\n\n${profile.bio}\n\n`; md += `## Experience\n\n`; profile.experience.forEach(e => { md += `### ${e.role} @ ${e.company}\n*${e.period}*\n\n`; e.highlights.split(' β€’ ').forEach(h => md += `- ${h}\n`); md += '\n'; }); md += `## Skills\n\n`; profile.skills.forEach(s => md += `- **${s.category}:** ${s.items}\n`); md += `\n## Projects\n\n`; profile.projects.forEach(p => md += `- **${p.name}** β€” ${p.desc}${p.url ? ` ([${p.url}](https://${p.url}))` : ''}\n`); md += `\n## Values\n\n`; profile.values.forEach(v => md += `- ${v}\n`); md += `\n## Social\n\n`; Object.entries(profile.social).forEach(([k,v]) => { if(v) md += `- **${k}:** ${v}\n`; }); return md; } function exportMarkdown(){ download('resume.md', generateMarkdown(), 'text/markdown'); } function copyMarkdown(){ navigator.clipboard.writeText(generateMarkdown()); alert('Markdown copied!'); } function importJSON(e){ const file = e.target.files[0]; if(!file) return; const reader = new FileReader(); reader.onload = function(ev){ try { const data = JSON.parse(ev.target.result); Object.assign(profile, data); save(); renderEditor(); alert('Profile imported! Switch to Terminal to see changes.'); } catch(err){ alert('Invalid JSON file'); } }; reader.readAsText(file); } function download(name, content, type){ const blob = new Blob([content], {type}); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = name; a.click(); URL.revokeObjectURL(a.href); } // === PREVIEW === function refreshPreview(){ const iframe = document.getElementById('previewFrame'); const html = generateStandaloneHTML(); iframe.srcdoc = html; } function openPreviewFullscreen(){ const html = generateStandaloneHTML(); const w = window.open('', '_blank'); w.document.write(html); w.document.close(); } // === TABS === document.querySelectorAll('.tab[data-panel]').forEach(tab => { tab.addEventListener('click', () => { document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); document.querySelectorAll('.panel').forEach(p => p.classList.remove('active')); tab.classList.add('active'); const panel = document.getElementById('panel-' + tab.dataset.panel); panel.classList.add('active'); if(tab.dataset.panel === 'editor') renderEditor(); if(tab.dataset.panel === 'export') renderExport(); if(tab.dataset.panel === 'preview') refreshPreview(); if(tab.dataset.panel === 'terminal') setTimeout(()=> inputEl.focus(), 100); }); }); // Theme button document.getElementById('themeBtn').addEventListener('click', () => { const current = profile.theme || 'default'; const idx = THEMES.indexOf(current); const next = THEMES[(idx + 1) % THEMES.length]; document.body.setAttribute('data-theme', next); profile.theme = next; save(); document.getElementById('themeBtn').textContent = '🎨 ' + THEME_NAMES[next]; }); // Apply saved theme if(profile.theme && profile.theme !== 'default'){ document.body.setAttribute('data-theme', profile.theme); } // Init initTerminal();