svg_viewer / index.html
srivatsavdamaraju's picture
Update index.html
4099235 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Enhanced Magic Text HTML Editor</title>
<style>
* {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 30px;
backdrop-filter: blur(10px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.container.ai-active {
margin-left: 320px;
}
h1 {
text-align: center;
margin-bottom: 30px;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-size: 2.5em;
font-weight: 700;
}
.editor-layout {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-top: 20px;
}
.editor-layout.collapsed {
grid-template-columns: 60px 1fr;
}
.input-section, .output-section {
background: #fff;
border-radius: 15px;
padding: 20px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
position: relative;
}
.input-section.collapsed {
width: 60px;
padding: 20px 10px;
overflow: hidden;
}
.input-section.collapsed .section-title,
.input-section.collapsed textarea,
.input-section.collapsed .status-bar {
display: none;
}
.section-title {
font-size: 1.2em;
font-weight: 600;
margin-bottom: 15px;
color: #4a5568;
display: flex;
align-items: center;
gap: 8px;
}
.section-title::before {
content: '';
width: 4px;
height: 20px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 2px;
}
textarea {
width: 100%;
height: 300px;
font-family: 'Fira Code', 'Consolas', monospace;
font-size: 14px;
border: 2px solid #e2e8f0;
border-radius: 10px;
padding: 15px;
resize: vertical;
transition: all 0.3s ease;
line-height: 1.5;
}
textarea:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.toolbar {
display: flex;
gap: 10px;
margin-bottom: 15px;
flex-wrap: wrap;
}
.btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 10px 20px;
border-radius: 25px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 5px;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.btn:active {
transform: translateY(0);
}
.btn.secondary {
background: #e2e8f0;
color: #4a5568;
}
.btn.secondary:hover {
background: #cbd5e0;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.btn.danger {
background: linear-gradient(135deg, #fc8181, #e53e3e);
}
.btn.danger:hover {
box-shadow: 0 5px 15px rgba(229, 62, 62, 0.4);
}
#renderedArea {
border: 2px dashed #e2e8f0;
border-radius: 10px;
padding: 20px;
min-height: 300px;
max-height: 500px;
overflow-y: auto;
transition: all 0.3s ease;
}
#renderedArea:empty::before {
content: 'Your HTML will render here...';
color: #a0aec0;
font-style: italic;
text-align: center;
display: block;
padding: 50px;
}
.editable {
transition: all 0.3s ease;
border-radius: 4px;
position: relative;
padding: 5px;
margin: 2px;
}
.editable:hover {
outline: 2px dashed #667eea;
background-color: rgba(102, 126, 234, 0.05);
cursor: text;
}
.editable:hover::after {
content: '✏️ Click to edit';
position: absolute;
top: -25px;
left: 0;
background: #667eea;
color: white;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
white-space: nowrap;
z-index: 10;
animation: fadeIn 0.3s ease;
}
.editing {
outline: 2px solid #48bb78 !important;
background-color: rgba(72, 187, 120, 0.1) !important;
box-shadow: 0 0 10px rgba(72, 187, 120, 0.2);
}
.editing:hover::after {
content: '✅ Press Enter to save';
background: #48bb78;
}
.status-bar {
background: #f7fafc;
padding: 10px 15px;
border-radius: 8px;
margin-top: 15px;
font-size: 14px;
color: #4a5568;
display: flex;
justify-content: space-between;
align-items: center;
}
.live-preview {
background: linear-gradient(45deg, #48bb78, #38a169);
position: relative;
overflow: hidden;
}
.live-preview::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
animation: shimmer 2s infinite;
}
@keyframes shimmer {
0% { left: -100%; }
100% { left: 100%; }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-5px); }
to { opacity: 1; transform: translateY(0); }
}
.templates-dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
background-color: white;
min-width: 200px;
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
border-radius: 8px;
z-index: 1000;
top: 100%;
left: 0;
margin-top: 5px;
}
.dropdown-content a {
color: #4a5568;
padding: 12px 16px;
text-decoration: none;
display: block;
border-radius: 8px;
margin: 4px;
transition: all 0.2s ease;
}
.dropdown-content a:hover {
background-color: #f7fafc;
color: #667eea;
}
.templates-dropdown:hover .dropdown-content {
display: block;
animation: fadeIn 0.3s ease;
}
@media (max-width: 768px) {
.editor-layout {
grid-template-columns: 1fr;
}
.toolbar {
justify-content: center;
}
.btn {
font-size: 12px;
padding: 8px 16px;
}
h1 {
font-size: 2em;
}
}
.word-count {
font-size: 12px;
color: #718096;
}
.element-count {
font-size: 12px;
color: #718096;
}
.save-indicator {
opacity: 0;
transition: opacity 0.3s ease;
color: #48bb78;
font-weight: 500;
}
.save-indicator.show {
opacity: 1;
}
/* AI Assistant Styles */
.ai-assistant {
position: fixed;
left: 20px;
top: 50%;
transform: translateY(-50%);
width: 300px;
background: white;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
z-index: 1000;
display: none;
flex-direction: column;
max-height: 80vh;
}
.ai-assistant.active {
display: flex;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { transform: translateY(-50%) translateX(-20px); opacity: 0; }
to { transform: translateY(-50%) translateX(0); opacity: 1; }
}
.ai-header {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
padding: 15px;
border-radius: 15px 15px 0 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.ai-header h3 {
margin: 0;
font-size: 1.2em;
}
.ai-close {
background: none;
border: none;
color: white;
font-size: 1.2em;
cursor: pointer;
}
.ai-chat {
flex: 1;
padding: 15px;
overflow-y: auto;
max-height: 400px;
}
.ai-message {
margin-bottom: 15px;
padding: 10px 15px;
border-radius: 10px;
max-width: 85%;
}
.ai-message.user {
background: #e3f2fd;
margin-left: auto;
border-bottom-right-radius: 2px;
}
.ai-message.assistant {
background: #f5f5f5;
margin-right: auto;
border-bottom-left-radius: 2px;
}
.ai-input {
display: flex;
padding: 15px;
border-top: 1px solid #e2e8f0;
}
.ai-input input {
flex: 1;
padding: 10px 15px;
border: 1px solid #e2e8f0;
border-radius: 25px;
outline: none;
}
.ai-input button {
background: #667eea;
color: white;
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
margin-left: 10px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.ai-icon {
position: absolute;
top: 5px;
right: 5px;
background: rgba(102, 126, 234, 0.1);
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
cursor: pointer;
opacity: 0;
transition: all 0.3s ease;
z-index: 5;
}
.editable:hover .ai-icon {
opacity: 1;
}
.ai-icon:hover {
background: rgba(102, 126, 234, 0.2);
transform: scale(1.1);
}
.selected {
background-color: rgba(102, 126, 234, 0.1) !important;
outline: 2px solid #667eea !important;
}
.multi-select-toolbar {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: white;
padding: 10px 20px;
border-radius: 25px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);
display: flex;
gap: 10px;
z-index: 999;
display: none;
}
.multi-select-toolbar.active {
display: flex;
animation: fadeIn 0.3s ease;
}
.multi-select-toolbar .btn {
font-size: 12px;
padding: 8px 15px;
}
/* Collapse Toggle Styles */
.collapse-toggle {
position: absolute;
top: 10px;
right: 10px;
background: #667eea;
color: white;
border: none;
border-radius: 50%;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
z-index: 10;
}
.collapse-toggle:hover {
background: #5a6fd8;
transform: scale(1.1);
}
.input-section.collapsed .collapse-toggle {
position: static;
margin: 0 auto;
}
.input-section.collapsed .collapse-toggle .icon {
transform: rotate(180deg);
}
/* Undo Button Styles */
.undo-btn {
background: linear-gradient(135deg, #48bb78, #38a169);
position: relative;
overflow: hidden;
}
.undo-btn:disabled {
background: #cbd5e0;
cursor: not-allowed;
transform: none !important;
box-shadow: none !important;
}
.undo-btn:disabled:hover {
transform: none !important;
box-shadow: none !important;
}
.undo-btn .icon {
transition: transform 0.3s ease;
}
.undo-btn:active .icon {
transform: rotate(-360deg);
}
/* History Panel */
.history-panel {
position: absolute;
top: 100%;
right: 0;
background: white;
border-radius: 10px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);
padding: 10px;
z-index: 100;
min-width: 200px;
max-height: 300px;
overflow-y: auto;
display: none;
}
.history-panel.active {
display: block;
animation: fadeIn 0.3s ease;
}
.history-item {
padding: 8px 12px;
border-radius: 5px;
cursor: pointer;
transition: all 0.2s ease;
font-size: 14px;
margin-bottom: 5px;
}
.history-item:hover {
background: #f7fafc;
}
.history-item.active {
background: #e3f2fd;
color: #667eea;
}
.history-dropdown {
position: relative;
display: inline-block;
}
/* PDF Export Styles */
.pdf-export-btn {
background: linear-gradient(135deg, #e53e3e, #c53030);
}
.pdf-export-btn:hover {
box-shadow: 0 5px 15px rgba(229, 62, 62, 0.4);
}
.pdf-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 2000;
justify-content: center;
align-items: center;
}
.pdf-modal.active {
display: flex;
animation: fadeIn 0.3s ease;
}
.pdf-modal-content {
background: white;
padding: 30px;
border-radius: 15px;
max-width: 500px;
width: 90%;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
}
.pdf-options {
display: flex;
flex-direction: column;
gap: 15px;
margin: 20px 0;
}
.pdf-option {
display: flex;
align-items: center;
gap: 10px;
padding: 10px;
border: 2px solid #e2e8f0;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
}
.pdf-option:hover {
border-color: #667eea;
background: #f7fafc;
}
.pdf-option.selected {
border-color: #667eea;
background: rgba(102, 126, 234, 0.05);
}
.pdf-option input {
display: none;
}
.pdf-preview {
max-height: 200px;
overflow-y: auto;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 15px;
margin: 15px 0;
background: #f8f9fa;
}
</style>
</head>
<body>
<div class="container" id="mainContainer">
<h1>🎨 Enhanced Magic HTML Editor</h1>
<div class="toolbar">
<button class="btn live-preview" onclick="toggleLivePreview()">
<span id="livePreviewText">🔴 Enable Live Preview</span>
</button>
<button class="btn" onclick="loadHTML()">🚀 Render HTML</button>
<button class="btn secondary" onclick="clearEditor()">🗑️ Clear</button>
<button class="btn secondary" onclick="copyRenderedHTML()">📋 Copy HTML</button>
<button class="btn secondary" onclick="exportHTML()">💾 Export</button>
<button class="btn pdf-export-btn" onclick="showPDFOptions()">📄 Save as PDF</button>
<div class="history-dropdown">
<button class="btn undo-btn" id="undoBtn" onclick="undoChange()" disabled>
<span class="icon"></span> Undo
</button>
<div class="history-panel" id="historyPanel">
<!-- History items will be added here -->
</div>
</div>
<div class="templates-dropdown">
<button class="btn secondary">📋 Templates ▼</button>
<div class="dropdown-content">
<a href="#" onclick="loadTemplate('basic')">Basic Page</a>
<a href="#" onclick="loadTemplate('card')">Card Layout</a>
<a href="#" onclick="loadTemplate('blog')">Blog Post</a>
<a href="#" onclick="loadTemplate('landing')">Landing Page</a>
<a href="#" onclick="loadTemplate('table')">Data Table</a>
</div>
</div>
</div>
<div class="editor-layout" id="editorLayout">
<div class="input-section" id="inputSection">
<button class="collapse-toggle" onclick="toggleEditorCollapse()">
<span class="icon"></span>
</button>
<div class="section-title">HTML Editor</div>
<textarea id="htmlInput" placeholder="Write your HTML here...">
<h1 class="title">Welcome to My Enhanced Page</h1>
<div class="content-card">
<h2>Editable Content</h2>
<p>Click any element to edit it directly! This text is fully editable.</p>
<div class="highlight-box">
<strong>Pro tip:</strong> Hover over elements to see edit hints!
</div>
</div>
<style>
.title { color: #667eea; text-align: center; }
.content-card { background: #f8f9fa; padding: 20px; border-radius: 10px; margin: 20px 0; }
.highlight-box { background: #e3f2fd; padding: 15px; border-left: 4px solid #2196f3; margin-top: 15px; }
</style>
</textarea>
<div class="status-bar">
<span class="word-count" id="wordCount">Words: 0</span>
<span class="save-indicator" id="saveIndicator">✅ Saved</span>
</div>
</div>
<div class="output-section">
<div class="section-title">Live Preview</div>
<div id="renderedArea"></div>
<div class="status-bar">
<span class="element-count" id="elementCount">Elements: 0</span>
<span>🎯 Click elements to edit | Ctrl+Click to select multiple</span>
</div>
</div>
</div>
</div>
<!-- AI Assistant Panel -->
<div class="ai-assistant" id="aiAssistant">
<div class="ai-header">
<h3>🤖 AI Assistant</h3>
<button class="ai-close" onclick="closeAIAssistant()"></button>
</div>
<div class="ai-chat" id="aiChat">
<div class="ai-message assistant">
Hello! I'm your AI assistant. How can I help you with your HTML today?
</div>
</div>
<div class="ai-input">
<input type="text" id="aiInput" placeholder="Ask me anything about HTML...">
<button onclick="sendAIMessage()"></button>
</div>
</div>
<!-- Multi-select toolbar -->
<div class="multi-select-toolbar" id="multiSelectToolbar">
<button class="btn" onclick="applyToSelected('style', 'color: #667eea;')">Style Selected</button>
<button class="btn secondary" onclick="clearSelection()">Clear Selection</button>
<button class="btn danger" onclick="deleteSelected()">Delete Selected</button>
</div>
<!-- PDF Export Modal -->
<div class="pdf-modal" id="pdfModal">
<div class="pdf-modal-content">
<h2 style="margin-top: 0; color: #4a5568;">Export as PDF</h2>
<p>Choose your preferred export format:</p>
<div class="pdf-options">
<div class="pdf-option" onclick="selectPDFOption('text')">
<input type="radio" name="pdf-option" id="text-option" value="text" checked>
<div style="flex: 1;">
<strong>Text Format</strong>
<p style="margin: 5px 0 0; font-size: 14px; color: #718096;">
Best for text content, smaller file size
</p>
</div>
</div>
<div class="pdf-option" onclick="selectPDFOption('visual')">
<input type="radio" name="pdf-option" id="visual-option" value="visual">
<div style="flex: 1;">
<strong>Visual Format</strong>
<p style="margin: 5px 0 0; font-size: 14px; color: #718096;">
Exact visual representation, larger file size
</p>
</div>
</div>
</div>
<div class="pdf-preview" id="pdfPreview">
<strong>Preview:</strong>
<div id="pdfPreviewContent" style="margin-top: 10px; font-size: 14px;">
<!-- Preview content will be added here -->
</div>
</div>
<div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px;">
<button class="btn secondary" onclick="closePDFModal()">Cancel</button>
<button class="btn pdf-export-btn" onclick="generatePDF()">Generate PDF</button>
</div>
</div>
</div>
<!-- External Libraries -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script>
let livePreviewEnabled = false;
let editingElement = null;
let selectedElements = new Set();
let isMultiSelectMode = false;
let isEditorCollapsed = false;
let history = [];
let historyIndex = -1;
let selectedPDFOption = 'text';
// Initialize with default content
window.onload = function() {
loadHTML();
updateWordCount();
saveToHistory();
updatePDFPreview();
};
function makeElementsEditable(container) {
const elements = container.querySelectorAll("*");
elements.forEach(el => {
// Skip script and style elements
if (el.tagName === 'SCRIPT' || el.tagName === 'STYLE') return;
el.classList.add('editable');
// Add AI icon to each element
const aiIcon = document.createElement('div');
aiIcon.className = 'ai-icon';
aiIcon.innerHTML = '🤖';
aiIcon.title = 'Get AI help for this element';
aiIcon.addEventListener('click', (e) => {
e.stopPropagation();
openAIAssistant(el);
});
el.appendChild(aiIcon);
el.addEventListener('click', function handleClick(e) {
e.stopPropagation();
// Check if Ctrl key is pressed for multi-select
if (e.ctrlKey || e.metaKey) {
toggleElementSelection(el);
return;
}
if (editingElement && editingElement !== el) {
finishEditing(editingElement);
}
if (!isMultiSelectMode) {
startEditing(el);
}
});
el.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
finishEditing(el);
} else if (e.key === 'Escape') {
finishEditing(el);
}
});
el.addEventListener('blur', () => {
setTimeout(() => finishEditing(el), 100);
});
el.addEventListener('input', () => {
showSaveIndicator();
});
});
updateElementCount(elements.length);
}
function toggleElementSelection(element) {
if (selectedElements.has(element)) {
selectedElements.delete(element);
element.classList.remove('selected');
} else {
selectedElements.add(element);
element.classList.add('selected');
}
// Show/hide multi-select toolbar
const toolbar = document.getElementById('multiSelectToolbar');
if (selectedElements.size > 0) {
toolbar.classList.add('active');
isMultiSelectMode = true;
} else {
toolbar.classList.remove('active');
isMultiSelectMode = false;
}
}
function clearSelection() {
selectedElements.forEach(el => {
el.classList.remove('selected');
});
selectedElements.clear();
document.getElementById('multiSelectToolbar').classList.remove('active');
isMultiSelectMode = false;
}
function deleteSelected() {
if (confirm(`Are you sure you want to delete ${selectedElements.size} selected elements?`)) {
selectedElements.forEach(el => {
el.remove();
});
selectedElements.clear();
document.getElementById('multiSelectToolbar').classList.remove('active');
isMultiSelectMode = false;
showSaveIndicator();
}
}
function applyToSelected(property, value) {
selectedElements.forEach(el => {
if (property === 'style') {
el.style.cssText += value;
}
});
showSaveIndicator();
}
function startEditing(element) {
if (element.getAttribute('contenteditable') !== 'true') {
element.setAttribute('contenteditable', 'true');
element.classList.add('editing');
element.focus();
editingElement = element;
// Select all text for easy editing
const range = document.createRange();
range.selectNodeContents(element);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
}
function finishEditing(element) {
if (element && element.getAttribute('contenteditable') === 'true') {
element.removeAttribute('contenteditable');
element.classList.remove('editing');
editingElement = null;
saveToHistory();
showSaveIndicator();
}
}
function loadHTML() {
const htmlContent = document.getElementById('htmlInput').value;
const renderedArea = document.getElementById('renderedArea');
// Clear and insert new content
renderedArea.innerHTML = htmlContent;
// Make all elements editable
makeElementsEditable(renderedArea);
showSaveIndicator();
updatePDFPreview();
}
function toggleLivePreview() {
livePreviewEnabled = !livePreviewEnabled;
const button = document.getElementById('livePreviewText');
if (livePreviewEnabled) {
button.textContent = '🟢 Live Preview ON';
startLivePreview();
} else {
button.textContent = '🔴 Enable Live Preview';
stopLivePreview();
}
}
function startLivePreview() {
const textarea = document.getElementById('htmlInput');
textarea.addEventListener('input', loadHTML);
loadHTML();
}
function stopLivePreview() {
const textarea = document.getElementById('htmlInput');
textarea.removeEventListener('input', loadHTML);
}
function clearEditor() {
if (confirm('Are you sure you want to clear the editor?')) {
document.getElementById('htmlInput').value = '';
document.getElementById('renderedArea').innerHTML = '';
updateWordCount();
updateElementCount(0);
clearSelection();
saveToHistory();
updatePDFPreview();
}
}
function copyRenderedHTML() {
const renderedArea = document.getElementById('renderedArea');
const htmlContent = renderedArea.innerHTML;
navigator.clipboard.writeText(htmlContent).then(() => {
showTemporaryMessage('HTML copied to clipboard!');
}).catch(err => {
console.error('Failed to copy: ', err);
});
}
function exportHTML() {
const htmlContent = document.getElementById('htmlInput').value;
const blob = new Blob([`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Exported HTML</title>
</head>
<body>
${htmlContent}
</body>
</html>`], { type: 'text/html' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'magic-editor-export.html';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
function loadTemplate(templateName) {
const templates = {
basic: `<h1>My Basic Page</h1>
<p>This is a simple paragraph.</p>
<ul>
<li>List item 1</li>
<li>List item 2</li>
</ul>`,
card: `<div style="max-width: 400px; margin: 20px auto; padding: 20px; border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); background: white;">
<h2 style="color: #333; margin-top: 0;">Card Title</h2>
<p style="color: #666; line-height: 1.6;">This is a beautiful card component with shadow and rounded corners.</p>
<button style="background: #667eea; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer;">Action Button</button>
</div>`,
blog: `<article style="max-width: 700px; margin: 0 auto; font-family: Georgia, serif;">
<header>
<h1 style="font-size: 2.5em; margin-bottom: 10px;">Blog Post Title</h1>
<p style="color: #666; font-style: italic;">Published on March 15, 2024</p>
</header>
<p style="font-size: 1.1em; line-height: 1.8;">This is the opening paragraph of your blog post. Make it engaging and compelling to draw readers in.</p>
<h2>Section Heading</h2>
<p style="line-height: 1.8;">Content for this section goes here. You can add multiple paragraphs, images, and other elements.</p>
</article>`,
landing: `<div style="text-align: center; padding: 50px 20px; background: linear-gradient(135deg, #667eea, #764ba2); color: white; border-radius: 15px;">
<h1 style="font-size: 3em; margin-bottom: 20px;">Welcome to Our Product</h1>
<p style="font-size: 1.2em; margin-bottom: 30px; opacity: 0.9;">The best solution for your needs. Join thousands of satisfied customers.</p>
<button style="background: white; color: #667eea; border: none; padding: 15px 30px; font-size: 1.1em; border-radius: 25px; cursor: pointer; box-shadow: 0 4px 15px rgba(0,0,0,0.1);">Get Started Now</button>
</div>`,
table: `<table style="width: 100%; border-collapse: collapse; margin: 20px 0; box-shadow: 0 4px 15px rgba(0,0,0,0.1);">
<thead>
<tr style="background: #667eea; color: white;">
<th style="padding: 15px; text-align: left;">Name</th>
<th style="padding: 15px; text-align: left;">Email</th>
<th style="padding: 15px; text-align: left;">Role</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom: 1px solid #eee;">
<td style="padding: 15px;">John Doe</td>
<td style="padding: 15px;">[email protected]</td>
<td style="padding: 15px;">Developer</td>
</tr>
<tr style="background: #f8f9fa; border-bottom: 1px solid #eee;">
<td style="padding: 15px;">Jane Smith</td>
<td style="padding: 15px;">[email protected]</td>
<td style="padding: 15px;">Designer</td>
</tr>
</tbody>
</table>`
};
document.getElementById('htmlInput').value = templates[templateName] || '';
loadHTML();
updateWordCount();
saveToHistory();
updatePDFPreview();
}
function updateWordCount() {
const text = document.getElementById('htmlInput').value;
const words = text.trim().split(/\s+/).filter(word => word.length > 0).length;
document.getElementById('wordCount').textContent = `Words: ${words}`;
}
function updateElementCount(count) {
document.getElementById('elementCount').textContent = `Elements: ${count}`;
}
function showSaveIndicator() {
const indicator = document.getElementById('saveIndicator');
indicator.classList.add('show');
setTimeout(() => {
indicator.classList.remove('show');
}, 2000);
}
function showTemporaryMessage(message) {
const indicator = document.getElementById('saveIndicator');
const originalText = indicator.textContent;
indicator.textContent = message;
indicator.classList.add('show');
setTimeout(() => {
indicator.textContent = originalText;
indicator.classList.remove('show');
}, 2000);
}
// AI Assistant Functions
function openAIAssistant(element) {
const assistant = document.getElementById('aiAssistant');
const container = document.getElementById('mainContainer');
assistant.classList.add('active');
container.classList.add('ai-active');
// Store the current element for context
assistant.currentElement = element;
// Add initial context message
addAIMessage('assistant', `I see you're working on a <${element.tagName.toLowerCase()}> element. How can I help you with it?`);
}
function closeAIAssistant() {
const assistant = document.getElementById('aiAssistant');
const container = document.getElementById('mainContainer');
assistant.classList.remove('active');
container.classList.remove('ai-active');
// Clear chat
document.getElementById('aiChat').innerHTML = `
<div class="ai-message assistant">
Hello! I'm your AI assistant. How can I help you with your HTML today?
</div>
`;
}
function sendAIMessage() {
const input = document.getElementById('aiInput');
const message = input.value.trim();
if (!message) return;
// Add user message to chat
addAIMessage('user', message);
// Clear input
input.value = '';
// Simulate AI response (in a real implementation, this would call an API)
setTimeout(() => {
const assistant = document.getElementById('aiAssistant');
const element = assistant.currentElement;
let response = '';
if (message.toLowerCase().includes('style') || message.toLowerCase().includes('css')) {
response = `For styling your <${element.tagName.toLowerCase()}> element, you can use CSS properties like:
- color: #333; (for text color)
- background-color: #f5f5f5; (for background)
- font-size: 16px; (for text size)
- padding: 10px; (for internal spacing)
- margin: 10px; (for external spacing)
Would you like me to apply any specific styles?`;
} else if (message.toLowerCase().includes('content') || message.toLowerCase().includes('text')) {
response = `For content in your <${element.tagName.toLowerCase()}> element, consider:
- Keeping it concise and relevant
- Using proper heading hierarchy
- Adding semantic meaning with appropriate tags
- Ensuring accessibility with alt text for images
What type of content are you planning to add?`;
} else if (message.toLowerCase().includes('api') || message.toLowerCase().includes('openai')) {
response = `Here's an example of how to make an API request to OpenAI:
\`\`\`javascript
import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-4",
input: "Write a one-sentence bedtime story about a unicorn."
});
console.log(response.output_text);
\`\`\`
Note: You'll need to install the OpenAI package and set up your API key.`;
} else {
response = `I can help you with various aspects of your HTML element:
- Styling and CSS properties
- Content suggestions
- Accessibility improvements
- Best practices for ${element.tagName.toLowerCase()} elements
- Integration with JavaScript or APIs
What specific aspect would you like to focus on?`;
}
addAIMessage('assistant', response);
}, 1000);
}
function addAIMessage(sender, text) {
const chat = document.getElementById('aiChat');
const messageDiv = document.createElement('div');
messageDiv.className = `ai-message ${sender}`;
messageDiv.textContent = text;
chat.appendChild(messageDiv);
chat.scrollTop = chat.scrollHeight;
}
// Editor Collapse Functionality
function toggleEditorCollapse() {
const inputSection = document.getElementById('inputSection');
const editorLayout = document.getElementById('editorLayout');
isEditorCollapsed = !isEditorCollapsed;
if (isEditorCollapsed) {
inputSection.classList.add('collapsed');
editorLayout.classList.add('collapsed');
} else {
inputSection.classList.remove('collapsed');
editorLayout.classList.remove('collapsed');
}
}
// Undo/Redo Functionality
function saveToHistory() {
const content = document.getElementById('htmlInput').value;
// If we're not at the end of history, remove future states
if (historyIndex < history.length - 1) {
history = history.slice(0, historyIndex + 1);
}
history.push(content);
historyIndex = history.length - 1;
updateUndoButton();
updateHistoryPanel();
}
function undoChange() {
if (historyIndex > 0) {
historyIndex--;
document.getElementById('htmlInput').value = history[historyIndex];
loadHTML();
updateWordCount();
updateUndoButton();
updateHistoryPanel();
}
}
function redoChange() {
if (historyIndex < history.length - 1) {
historyIndex++;
document.getElementById('htmlInput').value = history[historyIndex];
loadHTML();
updateWordCount();
updateUndoButton();
updateHistoryPanel();
}
}
function updateUndoButton() {
const undoBtn = document.getElementById('undoBtn');
undoBtn.disabled = historyIndex <= 0;
}
function updateHistoryPanel() {
const panel = document.getElementById('historyPanel');
panel.innerHTML = '';
// Show last 10 history items
const startIndex = Math.max(0, history.length - 10);
for (let i = startIndex; i < history.length; i++) {
const item = document.createElement('div');
item.className = 'history-item';
if (i === historyIndex) {
item.classList.add('active');
}
const preview = history[i].substring(0, 30) + (history[i].length > 30 ? '...' : '');
item.textContent = `Version ${i + 1}: ${preview}`;
item.onclick = () => {
historyIndex = i;
document.getElementById('htmlInput').value = history[i];
loadHTML();
updateWordCount();
updateUndoButton();
updateHistoryPanel();
};
panel.appendChild(item);
}
}
// PDF Export Functions
function showPDFOptions() {
document.getElementById('pdfModal').classList.add('active');
updatePDFPreview();
}
function closePDFModal() {
document.getElementById('pdfModal').classList.remove('active');
}
function selectPDFOption(option) {
selectedPDFOption = option;
// Update UI
document.querySelectorAll('.pdf-option').forEach(el => {
el.classList.remove('selected');
});
if (option === 'text') {
document.getElementById('text-option').parentElement.classList.add('selected');
} else {
document.getElementById('visual-option').parentElement.classList.add('selected');
}
updatePDFPreview();
}
function updatePDFPreview() {
const previewContent = document.getElementById('pdfPreviewContent');
const renderedArea = document.getElementById('renderedArea');
if (selectedPDFOption === 'text') {
const textContent = renderedArea.textContent || renderedArea.innerText || '';
const preview = textContent.substring(0, 150) + (textContent.length > 150 ? '...' : '');
previewContent.innerHTML = `<div style="white-space: pre-wrap; font-family: monospace;">${preview}</div>`;
} else {
previewContent.innerHTML = '<div style="color: #718096; font-style: italic;">Visual preview not available. PDF will contain exact visual representation of your content.</div>';
}
}
function generatePDF() {
closePDFModal();
if (selectedPDFOption === 'text') {
saveAsPDF();
} else {
saveAsPDFWithScreenshot();
}
}
function saveAsPDF() {
const { jsPDF } = window.jspdf;
const renderedArea = document.getElementById('renderedArea');
// Create a new PDF instance
const doc = new jsPDF();
// Get the HTML content
const content = renderedArea.innerHTML;
// Add title
doc.setFontSize(20);
doc.text('HTML Export', 105, 15, { align: 'center' });
// Add current date
const now = new Date();
doc.setFontSize(10);
doc.text(`Generated on: ${now.toLocaleDateString()} ${now.toLocaleTimeString()}`, 105, 22, { align: 'center' });
// Add separator line
doc.setDrawColor(200, 200, 200);
doc.line(10, 25, 200, 25);
// Convert HTML to PDF
doc.setFontSize(12);
// Create a temporary div to parse HTML content
const tempDiv = document.createElement('div');
tempDiv.innerHTML = content;
// Extract text content (simplified version)
let textContent = tempDiv.textContent || tempDiv.innerText || '';
// Clean and format text
textContent = textContent.replace(/\s+/g, ' ').trim();
// Split text into lines that fit PDF width
const lines = doc.splitTextToSize(textContent, 180);
// Add content to PDF
let yPosition = 35;
const lineHeight = 7;
const pageHeight = doc.internal.pageSize.height;
lines.forEach(line => {
// Check if we need a new page
if (yPosition > pageHeight - 20) {
doc.addPage();
yPosition = 20;
}
doc.text(line, 15, yPosition);
yPosition += lineHeight;
});
// Add page numbers
const pageCount = doc.internal.getNumberOfPages();
for (let i = 1; i <= pageCount; i++) {
doc.setPage(i);
doc.setFontSize(8);
doc.text(`Page ${i} of ${pageCount}`, 105, pageHeight - 10, { align: 'center' });
}
// Save the PDF
doc.save('html-export.pdf');
showTemporaryMessage('PDF saved successfully!');
}
function saveAsPDFWithScreenshot() {
const { jsPDF } = window.jspdf;
const renderedArea = document.getElementById('renderedArea');
// Use html2canvas to capture the visual representation
html2canvas(renderedArea, {
scale: 2,
useCORS: true,
logging: false,
width: renderedArea.scrollWidth,
height: renderedArea.scrollHeight
}).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF('p', 'mm', 'a4');
const imgWidth = 190;
const pageHeight = 280;
const imgHeight = (canvas.height * imgWidth) / canvas.width;
let heightLeft = imgHeight;
let position = 10;
pdf.addImage(imgData, 'PNG', 10, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
// Add multiple pages if content is too long
while (heightLeft >= 0) {
position = heightLeft - imgHeight + 10;
pdf.addPage();
pdf.addImage(imgData, 'PNG', 10, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
pdf.save('html-screenshot.pdf');
showTemporaryMessage('PDF saved successfully!');
}).catch(error => {
console.error('Error generating PDF:', error);
showTemporaryMessage('Error generating PDF. Using text fallback.');
saveAsPDF(); // Fallback to text version
});
}
// Update word count as user types
document.getElementById('htmlInput').addEventListener('input', updateWordCount);
document.getElementById('htmlInput').addEventListener('input', () => {
saveToHistory();
updatePDFPreview();
});
// Handle clicks outside of editable elements
document.addEventListener('click', (e) => {
if (!e.target.classList.contains('editable') &&
!e.target.classList.contains('ai-icon') &&
editingElement) {
finishEditing(editingElement);
}
// Close history panel when clicking elsewhere
if (!e.target.closest('.history-dropdown')) {
document.getElementById('historyPanel').classList.remove('active');
}
});
// Handle Enter key in AI input
document.getElementById('aiInput').addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendAIMessage();
}
});
// Handle Escape key to close AI assistant
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeAIAssistant();
}
// Undo/Redo with Ctrl+Z and Ctrl+Y
if (e.ctrlKey || e.metaKey) {
if (e.key === 'z') {
e.preventDefault();
undoChange();
} else if (e.key === 'y') {
e.preventDefault();
redoChange();
}
}
});
// Toggle history panel
document.getElementById('undoBtn').addEventListener('click', (e) => {
if (history.length > 0) {
const panel = document.getElementById('historyPanel');
panel.classList.toggle('active');
e.stopPropagation();
}
});
// Initialize PDF options
selectPDFOption('text');
</script>
</body>
</html>