Height / Build
${cc_escapeHTML(cc_data.height) || 'N/A'}
Distinguishing Features
${cc_escapeHTML(cc_data.features) || 'N/A'}
Personality
Strengths
-
${cc_data.strengths.length > 0 ? cc_data.strengths.map(s => `
- ${cc_escapeHTML(s)} `).join('') : `
- No strengths listed. `}
Weaknesses / Flaws
-
${cc_data.weaknesses.length > 0 ? cc_data.weaknesses.map(w => `
- ${cc_escapeHTML(w)} `).join('') : `
- No weaknesses listed. `}
Narrative
Backstory
${cc_escapeHTML(cc_data.backstory) || 'No backstory provided.'}
Goal / Motivation
${cc_escapeHTML(cc_data.motivation) || 'No motivation provided.'}
`; } /** * Reads all values from the config tab and updates the cc_data object */ function cc_updateDataFromConfig() { if (!cc_inputName) return; cc_data.name = cc_inputName.value; cc_data.age = cc_inputAge.value; cc_data.gender = cc_inputGender.value; cc_data.role = cc_inputRole.value; cc_data.hair = cc_inputHair.value; cc_data.eyes = cc_inputEyes.value; cc_data.height = cc_inputHeight.value; cc_data.features = cc_inputFeatures.value; cc_data.backstory = cc_inputBackstory.value; cc_data.motivation = cc_inputMotivation.value; cc_data.strengths = []; cc_strengthsContainer.querySelectorAll('.cc-input-strength').forEach(input => { if (input.value) cc_data.strengths.push(input.value); }); cc_data.weaknesses = []; cc_weaknessesContainer.querySelectorAll('.cc-input-weakness').forEach(input => { if (input.value) cc_data.weaknesses.push(input.value); }); } /** * Generates and downloads a multi-page PDF of the dashboard */ async function cc_downloadPDF() { // Check for required libraries if (typeof jspdf === 'undefined' || typeof html2canvas === 'undefined') { console.error("CC Tool Error: jsPDF or html2canvas library not loaded."); alert("Error: PDF libraries failed to load. Please check console."); return; } // 1. Render the full-size report into the clone cc_renderDashboard(cc_pdfRenderClone); const { jsPDF } = window.jspdf; try { // 2. Render canvas from the clone const canvas = await html2canvas(cc_pdfRenderClone, { scale: 2, // High resolution useCORS: true, windowWidth: cc_pdfRenderClone.scrollWidth, windowHeight: cc_pdfRenderClone.scrollHeight }); const imgData = canvas.toDataURL('image/png'); const imgWidth = canvas.width; const imgHeight = canvas.height; // Use 'pt' for units. A4 is 595.28 x 841.89 const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); // Scale image height to fit pdf width const ratio = imgHeight / imgWidth; const scaledImgHeight = (pdfWidth / imgWidth) * imgHeight; let heightLeft = scaledImgHeight; let position = 0; // y-position of the image on the page const margin = 40; // 40pt margin const contentWidth = pdfWidth - (margin * 2); const contentHeight = (contentWidth * imgHeight) / imgWidth; // 3. Add the first page pdf.addImage(imgData, 'PNG', margin, position + margin, contentWidth, contentHeight); heightLeft -= (pdfHeight - margin * 2); // 4. Add subsequent pages if needed while (heightLeft > 0) { position -= (pdfHeight - margin * 2); // Move the image's y-position up pdf.addPage(); pdf.addImage(imgData, 'PNG', margin, position + margin, contentWidth, contentHeight); heightLeft -= (pdfHeight - margin * 2); } const safeName = (cc_data.name || 'character_profile').replace(/[^a-z0-9]/gi, '_').toLowerCase(); pdf.save(`${safeName}.pdf`); } catch (error) { console.error("CC Tool Error: PDF generation failed.", error); alert("An error occurred while generating the PDF. Please try again."); } } // --- EVENT LISTENERS --- // Tab link clicks cc_tabLinks.forEach((link, index) => { link.addEventListener('click', () => cc_switchTab(index)); }); // Next/Prev button clicks if (cc_prevButton) { cc_prevButton.addEventListener('click', () => { if (cc_currentTab > 0) cc_switchTab(cc_currentTab - 1); }); } if (cc_nextButton) { cc_nextButton.addEventListener('click', () => { if (cc_currentTab < cc_tabLinks.length - 1) cc_switchTab(cc_currentTab + 1); }); } // PDF download if (cc_downloadPdfButton) { cc_downloadPdfButton.addEventListener('click', cc_downloadPDF); } // --- Config Tab "Add" Buttons --- if (cc_addStrengthButton) { cc_addStrengthButton.addEventListener('click', () => cc_addStrengthInput('', cc_strengthCounter++)); } if (cc_addWeaknessButton) { cc_addWeaknessButton.addEventListener('click', () => cc_addWeaknessInput('', cc_weaknessCounter++)); } // --- Config Tab "Remove" Buttons (Event Delegation) --- if (cc_configTab) { cc_configTab.addEventListener('click', (e) => { if (e.target.classList.contains('cc-remove-item')) { e.target.closest('.flex').remove(); } }); // Auto-update dashboard on config changes (change event) cc_configTab.addEventListener('change', () => { cc_updateDataFromConfig(); if (cc_currentTab === 0) { cc_renderDashboard(cc_dashboardOutput); } }); // Also listen for keyup for textareas/inputs for faster preview cc_configTab.addEventListener('keyup', (e) => { if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') { cc_updateDataFromConfig(); if (cc_currentTab === 0) { cc_renderDashboard(cc_dashboardOutput); } } }); } // --- INITIALIZATION --- cc_initSampleData(); cc_renderConfig(); cc_renderDashboard(cc_dashboardOutput); // Set initial tab state cc_tabPanes.forEach((pane, index) => { pane.classList.toggle('hidden', index !== 0); pane.classList.toggle('cc-active', index === 0); }); });