No shapes in library.
';
}
ssy_data.shapeLibrary.forEach(shape => {
// Add to Config List
const item = document.createElement('div');
item.className = 'ssy-list-item';
let deleteBtn = '';
if (!shape.default) {
deleteBtn = `
`;
}
item.innerHTML = `
${ssy_escapeHTML(shape.name)}${deleteBtn}`;
ssy_shapeLibraryListEl.appendChild(item);
// Add to Dashboard Toolbar
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'ssy-btn ssy-btn-primary';
btn.textContent = `Add ${shape.name}`;
btn.setAttribute('onclick', `ssy_addShape('${ssy_escapeHTML(shape.name)}')`);
ssy_shapeButtonsEl.appendChild(btn);
});
}
/**
* Adds a new shape type to the library
*/
window.ssy_addShapeType = function(event) {
event.preventDefault();
const name = ssy_newShapeNameInput.value.trim();
const path = ssy_newShapePathInput.value.trim();
if (name && path) {
if (ssy_data.shapeLibrary.some(s => s.name.toLowerCase() === name.toLowerCase())) {
alert('A shape with this name already exists.');
return;
}
ssy_data.shapeLibrary.push({ name, type: 'path', path, default: false });
ssy_renderShapeLibrary();
ssy_newShapeNameInput.value = '';
ssy_newShapePathInput.value = '';
}
}
/**
* Deletes a custom shape type
*/
window.ssy_deleteShapeType = function(name) {
if (confirm(`Are you sure you want to delete the "${name}" shape?`)) {
ssy_data.shapeLibrary = ssy_data.shapeLibrary.filter(s => s.name !== name);
ssy_renderShapeLibrary();
}
}
// --- TAB & NAVIGATION FUNCTIONS ---
window.ssy_switchTab = function(tabIndex) {
ssy_currentTabIndex = tabIndex;
ssy_tabContents.forEach((content, index) => {
content.classList.toggle('ssy-active', index === tabIndex);
});
ssy_tabs.forEach((tab, index) => {
tab.classList.toggle('ssy-active', index === tabIndex);
});
ssy_updateNavButtons();
}
window.ssy_navigateTabs = function(direction) {
if (direction === 'next' && ssy_currentTabIndex < ssy_numTabs - 1) {
ssy_switchTab(ssy_currentTabIndex + 1);
} else if (direction === 'prev' && ssy_currentTabIndex > 0) {
ssy_switchTab(ssy_currentTabIndex - 1);
}
}
function ssy_updateNavButtons() {
ssy_prevBtn.style.visibility = (ssy_currentTabIndex === 0) ? 'hidden' : 'visible';
ssy_nextBtn.style.visibility = (ssy_currentTabIndex === ssy_numTabs - 1) ? 'hidden' : 'visible';
}
// --- PDF GENERATION ---
window.ssy_generatePdf = function() {
if (typeof jspdf === 'undefined' || typeof jspdf.jsPDF === 'undefined') {
alert('Error: PDF generation library (jsPDF) not loaded.');
return;
}
try {
// Deselect shape first so the outline doesn't appear in the PDF
ssy_deselectAll();
const { jsPDF } = jspdf;
const doc = new jsPDF();
const svgEl = document.getElementById('ssy-canvas');
const canvasEl = document.getElementById('ssy-pdf-canvas');
if (!svgEl || !canvasEl) throw new Error('SVG or Canvas element not found.');
if (svgEl.querySelectorAll('.ssy-shape').length === 0) {
alert('Canvas is empty. Add some shapes first!');
return;
}
// 1. Serialize and draw SVG to Canvas
const svgData = new XMLSerializer().serializeToString(svgEl);
const img = new Image();
const svgBase64 = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgData)));
img.onload = function() {
const svgRect = svgEl.getBoundingClientRect();
canvasEl.width = svgRect.width * 2; // Increase resolution
canvasEl.height = svgRect.height * 2;
const ctx = canvasEl.getContext('2d');
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
// Draw a white background for the canvas
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(0, 0, canvasEl.width, canvasEl.height);
ctx.drawImage(img, 0, 0, canvasEl.width, canvasEl.height);
const canvasImgData = canvasEl.toDataURL('image/png');
// 2. Build the PDF
const margin = 15;
const docWidth = doc.internal.pageSize.getWidth();
const imgWidth = docWidth - (margin * 2);
const imgHeight = (canvasEl.height * imgWidth) / canvasEl.width;
doc.setFont('helvetica', 'bold');
doc.setFontSize(20);
doc.setTextColor(varGet('--ssy-primary-color', '#0073e6'));
doc.text("My Shape Symphony", 105, 20, { align: 'center' });
doc.addImage(canvasImgData, 'PNG', margin, 30, imgWidth, imgHeight);
// 3. Save PDF
doc.save('shape_symphony.pdf');
};
img.src = svgBase64;
} catch (e) {
console.error("PDF Generation Error: ", e);
alert("An error occurred while generating the PDF.");
}
}
// --- UTILITY FUNCTIONS ---
function varGet(varName, fallback = '#000') {
try {
return getComputedStyle(document.documentElement).getPropertyValue(varName).trim() || fallback;
} catch (e) {
return fallback;
}
}
function ssy_escapeHTML(str) {
if (!str) return '';
return str.replace(/[&<>"']/g, function(m) {
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[m];
});
}
function ssy_getRandomColor() {
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
// --- INITIALIZATION ---
function ssy_initTool() {
ssy_switchTab(0);
ssy_renderShapeLibrary();
// Add canvas background listener
ssy_canvas.addEventListener('click', ssy_deselectAll);
// Add inspector listeners
ssy_shapeFillInput.addEventListener('input', ssy_applyInspectorChanges);
ssy_shapeWidthInput.addEventListener('input', ssy_applyInspectorChanges);
ssy_shapeHeightInput.addEventListener('input', ssy_applyInspectorChanges);
}
ssy_initTool(); // Run the tool
}); // End of DOMContentLoaded