ui-customization-tool / index.html
basheer1414's picture
Please deduct the code again and there is some error I can see some more text it looks like a code off of the window of the preview - Initial Deployment
f49c1ee verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>UI Customization Tool</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* Custom CSS for elements that can't be done with Tailwind */
.sidebar {
transition: all 0.3s ease;
}
.sidebar.collapsed {
transform: translateX(-90%);
}
.sidebar.collapsed:hover {
transform: translateX(0);
}
.color-picker {
width: 30px;
height: 30px;
border-radius: 50%;
cursor: pointer;
transition: transform 0.2s;
}
.color-picker:hover {
transform: scale(1.2);
}
.gradient-preview {
width: 100%;
height: 60px;
border-radius: 8px;
margin-top: 10px;
}
.modal {
display: none;
position: fixed;
z-index: 100;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
}
.modal-content {
background-color: #f8fafc;
margin: 5% auto;
padding: 20px;
border-radius: 10px;
width: 80%;
max-width: 700px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.code-editor {
width: 100%;
height: 300px;
font-family: 'Courier New', monospace;
padding: 10px;
border-radius: 5px;
border: 1px solid #cbd5e1;
resize: none;
}
.canvas-container {
position: relative;
border: 2px dashed #94a3b8;
background-color: #f1f5f9;
overflow: auto;
}
.resize-handle {
position: absolute;
width: 10px;
height: 10px;
background-color: #3b82f6;
border-radius: 50%;
z-index: 10;
}
.resize-handle.nw { top: -5px; left: -5px; cursor: nw-resize; }
.resize-handle.ne { top: -5px; right: -5px; cursor: ne-resize; }
.resize-handle.sw { bottom: -5px; left: -5px; cursor: sw-resize; }
.resize-handle.se { bottom: -5px; right: -5px; cursor: se-resize; }
.toolbar {
transition: all 0.3s ease;
}
.element-controls {
position: absolute;
top: -40px;
left: 0;
background-color: white;
padding: 5px;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
display: none;
}
.canvas-element:hover .element-controls {
display: block;
}
.tab-button {
transition: all 0.2s ease;
}
.tab-button.active {
background-color: #3b82f6;
color: white;
}
.uiverse-modal {
max-height: 80vh;
overflow-y: auto;
}
</style>
</head>
<body class="bg-gray-100 h-screen flex flex-col">
<!-- Top Navigation -->
<header class="bg-white shadow-sm py-2 px-4 flex justify-between items-center">
<h1 class="text-xl font-bold text-gray-800">UI Customization Tool</h1>
<div class="flex space-x-2">
<button id="htmlBtn" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg transition">
<i class="fas fa-code mr-2"></i>HTML
</button>
<button id="cssBtn" class="bg-purple-500 hover:bg-purple-600 text-white px-4 py-2 rounded-lg transition">
<i class="fab fa-css3-alt mr-2"></i>CSS
</button>
<button id="saveBtn" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-lg transition">
<i class="fas fa-save mr-2"></i>Save
</button>
<button id="resetBtn" class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg transition">
<i class="fas fa-trash-alt mr-2"></i>Reset
</button>
</div>
</header>
<div class="flex flex-1 overflow-hidden">
<!-- Sidebar -->
<div id="sidebar" class="sidebar w-64 bg-gray-800 text-white h-full flex flex-col transition-all duration-300">
<div class="p-4 flex justify-between items-center bg-gray-900">
<h2 class="text-lg font-semibold">Elements</h2>
<button id="toggleSidebar" class="text-gray-400 hover:text-white">
<i class="fas fa-chevron-left"></i>
</button>
</div>
<div class="flex-1 overflow-y-auto p-4">
<div class="mb-6">
<h3 class="text-sm uppercase font-medium text-gray-400 mb-2">Basic Elements</h3>
<div class="space-y-2">
<button class="element-btn w-full text-left px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded flex items-center" data-type="button">
<i class="fas fa-square mr-2"></i> Button
</button>
<button class="element-btn w-full text-left px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded flex items-center" data-type="input">
<i class="fas fa-font mr-2"></i> Input
</button>
<button class="element-btn w-full text-left px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded flex items-center" data-type="card">
<i class="fas fa-id-card mr-2"></i> Card
</button>
</div>
</div>
<div class="mb-6">
<h3 class="text-sm uppercase font-medium text-gray-400 mb-2">Containers</h3>
<div class="space-y-2">
<button class="element-btn w-full text-left px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded flex items-center" data-type="div">
<i class="fas fa-square-full mr-2"></i> Div
</button>
<button class="element-btn w-full text-left px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded flex items-center" data-type="section">
<i class="fas fa-border-all mr-2"></i> Section
</button>
</div>
</div>
<div class="mb-6">
<h3 class="text-sm uppercase font-medium text-gray-400 mb-2">Advanced</h3>
<div class="space-y-2">
<button id="uiverseBtn" class="w-full text-left px-3 py-2 bg-indigo-700 hover:bg-indigo-600 rounded flex items-center">
<i class="fas fa-magic mr-2"></i> Uiverse.io
</button>
<button class="element-btn w-full text-left px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded flex items-center" data-type="custom">
<i class="fas fa-code mr-2"></i> Custom HTML
</button>
</div>
</div>
</div>
</div>
<!-- Main Content -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Toolbar -->
<div class="toolbar bg-white border-b p-2 flex items-center space-x-4 overflow-x-auto">
<div class="flex items-center space-x-2">
<span class="text-sm font-medium">Position:</span>
<input type="number" id="posX" placeholder="X" class="w-12 px-2 py-1 border rounded">
<input type="number" id="posY" placeholder="Y" class="w-12 px-2 py-1 border rounded">
</div>
<div class="flex items-center space-x-2">
<span class="text-sm font-medium">Size:</span>
<input type="number" id="width" placeholder="W" class="w-12 px-2 py-1 border rounded">
<input type="number" id="height" placeholder="H" class="w-12 px-2 py-1 border rounded">
</div>
<div class="flex items-center space-x-2">
<span class="text-sm font-medium">Rotation:</span>
<input type="number" id="rotation" placeholder="deg" class="w-16 px-2 py-1 border rounded">
</div>
<div class="flex items-center space-x-2">
<span class="text-sm font-medium">Color:</span>
<input type="color" id="colorPicker" class="w-8 h-8 cursor-pointer">
</div>
<div class="flex items-center space-x-2">
<span class="text-sm font-medium">BG:</span>
<input type="color" id="bgColorPicker" class="w-8 h-8 cursor-pointer">
</div>
<div class="flex items-center space-x-2">
<button id="gradientBtn" class="px-2 py-1 bg-gray-200 rounded text-sm">Gradient</button>
</div>
<div class="flex items-center space-x-2">
<span class="text-sm font-medium">Opacity:</span>
<input type="range" id="opacity" min="0" max="100" value="100" class="w-20">
</div>
<div class="flex items-center space-x-2">
<span class="text-sm font-medium">Z-index:</span>
<input type="number" id="zIndex" value="1" class="w-12 px-2 py-1 border rounded">
</div>
<div class="flex items-center space-x-2">
<button id="deleteBtn" class="px-2 py-1 bg-red-500 text-white rounded text-sm">
<i class="fas fa-trash-alt"></i>
</button>
</div>
<div class="flex items-center space-x-2">
<button id="duplicateBtn" class="px-2 py-1 bg-blue-500 text-white rounded text-sm">
<i class="fas fa-copy"></i>
</button>
</div>
<div class="flex items-center space-x-2">
<button id="newWindowBtn" class="px-2 py-1 bg-green-500 text-white rounded text-sm">
<i class="fas fa-plus"></i> New Window
</button>
</div>
</div>
<!-- Canvas -->
<div id="canvasContainer" class="canvas-container flex-1 m-4 bg-white relative overflow-auto">
<div id="canvas" class="relative w-full h-full min-h-[500px]">
<!-- Elements will be added here -->
</div>
</div>
</div>
</div>
<!-- HTML Modal -->
<div id="htmlModal" class="modal">
<div class="modal-content">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold">HTML Editor</h2>
<button id="closeHtmlModal" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<textarea id="htmlEditor" class="code-editor" placeholder="Paste your HTML code here..."></textarea>
<div class="flex justify-end mt-4 space-x-2">
<button id="cancelHtmlBtn" class="px-4 py-2 bg-gray-300 hover:bg-gray-400 rounded">Cancel</button>
<button id="applyHtmlBtn" class="px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded">Apply</button>
</div>
</div>
</div>
<!-- CSS Modal -->
<div id="cssModal" class="modal">
<div class="modal-content">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold">CSS Editor</h2>
<button id="closeCssModal" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<textarea id="cssEditor" class="code-editor" placeholder="Paste your CSS code here..."></textarea>
<div class="flex justify-end mt-4 space-x-2">
<button id="cancelCssBtn" class="px-4 py-2 bg-gray-300 hover:bg-gray-400 rounded">Cancel</button>
<button id="applyCssBtn" class="px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded">Apply</button>
</div>
</div>
</div>
<!-- Gradient Modal -->
<div id="gradientModal" class="modal">
<div class="modal-content">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold">Gradient Editor</h2>
<button id="closeGradientModal" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium mb-1">Gradient Type</label>
<select id="gradientType" class="w-full p-2 border rounded">
<option value="linear">Linear</option>
<option value="radial">Radial</option>
</select>
</div>
<div id="linearOptions">
<label class="block text-sm font-medium mb-1">Direction</label>
<select id="gradientDirection" class="w-full p-2 border rounded">
<option value="to right">Left to Right</option>
<option value="to bottom">Top to Bottom</option>
<option value="to bottom right">Diagonal</option>
<option value="135deg">135°</option>
</select>
</div>
</div>
<div class="mt-4">
<label class="block text-sm font-medium mb-2">Color Stops</label>
<div id="colorStops" class="space-y-2">
<div class="flex items-center space-x-2">
<input type="color" value="#3b82f6" class="color-picker">
<input type="range" min="0" max="100" value="0" class="flex-1">
<span>0%</span>
<button class="remove-stop px-2 py-1 bg-red-500 text-white rounded text-sm">
<i class="fas fa-times"></i>
</button>
</div>
<div class="flex items-center space-x-2">
<input type="color" value="#8b5cf6" class="color-picker">
<input type="range" min="0" max="100" value="100" class="flex-1">
<span>100%</span>
<button class="remove-stop px-2 py-1 bg-red-500 text-white rounded text-sm">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<button id="addStopBtn" class="mt-2 px-3 py-1 bg-gray-200 hover:bg-gray-300 rounded text-sm">
<i class="fas fa-plus mr-1"></i> Add Color Stop
</button>
</div>
<div class="mt-4">
<label class="block text-sm font-medium mb-1">Preview</label>
<div id="gradientPreview" class="gradient-preview" style="background: linear-gradient(to right, #3b82f6, #8b5cf6);"></div>
</div>
<div class="flex justify-end mt-4 space-x-2">
<button id="cancelGradientBtn" class="px-4 py-2 bg-gray-300 hover:bg-gray-400 rounded">Cancel</button>
<button id="applyGradientBtn" class="px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded">Apply</button>
</div>
</div>
</div>
<!-- Uiverse Modal -->
<div id="uiverseModal" class="modal">
<div class="modal-content uiverse-modal">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold">Uiverse Elements</h2>
<button id="closeUiverseModal" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<!-- Sample Uiverse elements - in a real app these would be fetched from the API -->
<div class="uiverse-element border rounded p-4">
<h3 class="font-medium mb-2">Bitter Parrot Button</h3>
<div class="mb-3">
<button class="px-4 py-2 bg-gradient-to-r from-purple-500 to-pink-500 text-white rounded-full shadow-lg hover:shadow-xl transition duration-300">
Hover me
</button>
</div>
<div class="flex justify-between">
<button class="copy-code px-2 py-1 bg-blue-500 text-white rounded text-sm">
<i class="fas fa-copy mr-1"></i> Copy
</button>
<button class="add-to-canvas px-2 py-1 bg-green-500 text-white rounded text-sm">
<i class="fas fa-plus mr-1"></i> Add
</button>
</div>
</div>
<div class="uiverse-element border rounded p-4">
<h3 class="font-medium mb-2">Cool Input</h3>
<div class="mb-3">
<div class="relative">
<input type="text" class="w-full px-4 py-2 border-b-2 border-gray-300 focus:border-blue-500 outline-none transition" placeholder="Type something...">
</div>
</div>
<div class="flex justify-between">
<button class="copy-code px-2 py-1 bg-blue-500 text-white rounded text-sm">
<i class="fas fa-copy mr-1"></i> Copy
</button>
<button class="add-to-canvas px-2 py-1 bg-green-500 text-white rounded text-sm">
<i class="fas fa-plus mr-1"></i> Add
</button>
</div>
</div>
<div class="uiverse-element border rounded p-4">
<h3 class="font-medium mb-2">Animated Card</h3>
<div class="mb-3">
<div class="w-full max-w-sm bg-white border border-gray-200 rounded-lg shadow hover:scale-105 transition duration-300 p-4">
<h4 class="text-lg font-semibold">Card Title</h4>
<p class="text-gray-600">Hover to see animation</p>
</div>
</div>
<div class="flex justify-between">
<button class="copy-code px-2 py-1 bg-blue-500 text-white rounded text-sm">
<i class="fas fa-copy mr-1"></i> Copy
</button>
<button class="add-to-canvas px-2 py-1 bg-green-500 text-white rounded text-sm">
<i class="fas fa-plus mr-1"></i> Add
</button>
</div>
</div>
</div>
<div class="flex justify-center mt-6">
<button class="px-4 py-2 bg-gray-200 hover:bg-gray-300 rounded">
Load More
</button>
</div>
</div>
</div>
<!-- Custom Element Modal -->
<div id="customElementModal" class="modal">
<div class="modal-content">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold">Custom Element</h2>
<button id="closeCustomModal" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<textarea id="customElementEditor" class="code-editor" placeholder="Enter your custom HTML here..."></textarea>
<div class="flex justify-end mt-4 space-x-2">
<button id="cancelCustomBtn" class="px-4 py-2 bg-gray-300 hover:bg-gray-400 rounded">Cancel</button>
<button id="applyCustomBtn" class="px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded">Add to Canvas</button>
</div>
</div>
</div>
<script>
// DOM Elements
const htmlBtn = document.getElementById('htmlBtn');
const cssBtn = document.getElementById('cssBtn');
const saveBtn = document.getElementById('saveBtn');
const resetBtn = document.getElementById('resetBtn');
const htmlModal = document.getElementById('htmlModal');
const cssModal = document.getElementById('cssModal');
const gradientModal = document.getElementById('gradientModal');
const uiverseModal = document.getElementById('uiverseModal');
const customElementModal = document.getElementById('customElementModal');
const closeHtmlModal = document.getElementById('closeHtmlModal');
const closeCssModal = document.getElementById('closeCssModal');
const closeGradientModal = document.getElementById('closeGradientModal');
const closeUiverseModal = document.getElementById('closeUiverseModal');
const closeCustomModal = document.getElementById('closeCustomModal');
const cancelHtmlBtn = document.getElementById('cancelHtmlBtn');
const cancelCssBtn = document.getElementById('cancelCssBtn');
const cancelGradientBtn = document.getElementById('cancelGradientBtn');
const cancelCustomBtn = document.getElementById('cancelCustomBtn');
const applyHtmlBtn = document.getElementById('applyHtmlBtn');
const applyCssBtn = document.getElementById('applyCssBtn');
const applyGradientBtn = document.getElementById('applyGradientBtn');
const applyCustomBtn = document.getElementById('applyCustomBtn');
const htmlEditor = document.getElementById('htmlEditor');
const cssEditor = document.getElementById('cssEditor');
const customElementEditor = document.getElementById('customElementEditor');
const canvas = document.getElementById('canvas');
const gradientBtn = document.getElementById('gradientBtn');
const toggleSidebar = document.getElementById('toggleSidebar');
const sidebar = document.getElementById('sidebar');
const elementBtns = document.querySelectorAll('.element-btn');
const uiverseBtn = document.getElementById('uiverseBtn');
const deleteBtn = document.getElementById('deleteBtn');
const duplicateBtn = document.getElementById('duplicateBtn');
const newWindowBtn = document.getElementById('newWindowBtn');
const posX = document.getElementById('posX');
const posY = document.getElementById('posY');
const width = document.getElementById('width');
const height = document.getElementById('height');
const rotation = document.getElementById('rotation');
const colorPicker = document.getElementById('colorPicker');
const bgColorPicker = document.getElementById('bgColorPicker');
const opacity = document.getElementById('opacity');
const zIndex = document.getElementById('zIndex');
const gradientType = document.getElementById('gradientType');
const gradientDirection = document.getElementById('gradientDirection');
const colorStops = document.getElementById('colorStops');
const addStopBtn = document.getElementById('addStopBtn');
const gradientPreview = document.getElementById('gradientPreview');
// State
let selectedElement = null;
let isDragging = false;
let isResizing = false;
let resizeHandle = null;
let startX, startY, startWidth, startHeight, startLeft, startTop;
// Initialize
function init() {
// Set default color picker values
colorPicker.value = '#000000';
bgColorPicker.value = '#ffffff';
// Add event listeners
setupEventListeners();
// Load saved data if available
loadSavedData();
}
// Set up all event listeners
function setupEventListeners() {
// Modal buttons
htmlBtn.addEventListener('click', () => htmlModal.style.display = 'block');
cssBtn.addEventListener('click', () => cssModal.style.display = 'block');
saveBtn.addEventListener('click', saveDesign);
resetBtn.addEventListener('click', resetCanvas);
// Close modals
closeHtmlModal.addEventListener('click', () => htmlModal.style.display = 'none');
closeCssModal.addEventListener('click', () => cssModal.style.display = 'none');
closeGradientModal.addEventListener('click', () => gradientModal.style.display = 'none');
closeUiverseModal.addEventListener('click', () => uiverseModal.style.display = 'none');
closeCustomModal.addEventListener('click', () => customElementModal.style.display = 'none');
// Cancel buttons
cancelHtmlBtn.addEventListener('click', () => htmlModal.style.display = 'none');
cancelCssBtn.addEventListener('click', () => cssModal.style.display = 'none');
cancelGradientBtn.addEventListener('click', () => gradientModal.style.display = 'none');
cancelCustomBtn.addEventListener('click', () => customElementModal.style.display = 'none');
// Apply buttons
applyHtmlBtn.addEventListener('click', applyHtml);
applyCssBtn.addEventListener('click', applyCss);
applyGradientBtn.addEventListener('click', applyGradient);
applyCustomBtn.addEventListener('click', addCustomElement);
// Gradient editor
gradientBtn.addEventListener('click', () => gradientModal.style.display = 'block');
gradientType.addEventListener('change', updateGradientUI);
gradientDirection.addEventListener('change', updateGradientPreview);
// Color stops
addStopBtn.addEventListener('click', addColorStop);
colorStops.addEventListener('click', (e) => {
if (e.target.classList.contains('remove-stop')) {
if (colorStops.children.length > 2) {
e.target.parentElement.remove();
updateGradientPreview();
}
} else if (e.target.classList.contains('color-picker')) {
e.target.addEventListener('change', updateGradientPreview);
} else if (e.target.type === 'range') {
e.target.addEventListener('input', () => {
e.target.nextElementSibling.textContent = e.target.value + '%';
updateGradientPreview();
});
}
});
// Sidebar
toggleSidebar.addEventListener('click', toggleSidebarCollapse);
// Element buttons
elementBtns.forEach(btn => {
btn.addEventListener('click', () => {
const type = btn.dataset.type;
if (type === 'custom') {
customElementModal.style.display = 'block';
customElementEditor.value = '';
} else {
addElementToCanvas(type);
}
});
});
// Uiverse button
uiverseBtn.addEventListener('click', () => uiverseModal.style.display = 'block');
// Uiverse modal interactions
document.querySelectorAll('.add-to-canvas').forEach(btn => {
btn.addEventListener('click', () => {
const element = btn.closest('.uiverse-element');
const preview = element.querySelector('button, input, div');
if (preview) {
const clone = preview.cloneNode(true);
addElementToCanvas(clone);
uiverseModal.style.display = 'none';
}
});
});
// Canvas interactions
canvas.addEventListener('mousedown', startDrag);
document.addEventListener('mousemove', handleDrag);
document.addEventListener('mouseup', stopDrag);
// Toolbar controls
posX.addEventListener('change', updateElementPosition);
posY.addEventListener('change', updateElementPosition);
width.addEventListener('change', updateElementSize);
height.addEventListener('change', updateElementSize);
rotation.addEventListener('change', updateElementRotation);
colorPicker.addEventListener('change', updateElementColor);
bgColorPicker.addEventListener('change', updateElementBackground);
opacity.addEventListener('input', updateElementOpacity);
zIndex.addEventListener('change', updateElementZIndex);
deleteBtn.addEventListener('click', deleteSelectedElement);
duplicateBtn.addEventListener('click', duplicateSelectedElement);
newWindowBtn.addEventListener('click', addNewWindow);
}
// Toggle sidebar collapse
function toggleSidebarCollapse() {
sidebar.classList.toggle('collapsed');
const icon = toggleSidebar.querySelector('i');
if (sidebar.classList.contains('collapsed')) {
icon.classList.remove('fa-chevron-left');
icon.classList.add('fa-chevron-right');
} else {
icon.classList.remove('fa-chevron-right');
icon.classList.add('fa-chevron-left');
}
}
// Add element to canvas
function addElementToCanvas(type, html = null) {
let element;
if (html) {
// Create a temporary div to parse the HTML
const tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
element = tempDiv.firstChild;
} else {
// Create standard elements based on type
switch (type) {
case 'button':
element = document.createElement('button');
element.textContent = 'Button';
element.className = 'px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition';
break;
case 'input':
element = document.createElement('input');
element.type = 'text';
element.placeholder = 'Type something...';
element.className = 'px-3 py-2 border rounded';
break;
case 'card':
element = document.createElement('div');
element.className = 'bg-white rounded-lg shadow-md p-4 w-64';
element.innerHTML = `
<h3 class="text-lg font-semibold mb-2">Card Title</h3>
<p class="text-gray-600">This is a sample card element.</p>
`;
break;
case 'div':
element = document.createElement('div');
element.className = 'bg-gray-200 border border-gray-300 p-4';
element.textContent = 'Div Container';
break;
case 'section':
element = document.createElement('section');
element.className = 'bg-white border border-gray-300 p-4';
element.textContent = 'Section Container';
break;
default:
element = document.createElement('div');
element.textContent = 'New Element';
}
}
// Set default styles for positioning
element.className += ' absolute cursor-move canvas-element';
element.style.left = '50px';
element.style.top = '50px';
element.style.width = 'auto';
element.style.minWidth = '100px';
element.style.minHeight = '40px';
// Add resize handles
addResizeHandles(element);
// Add element to canvas
canvas.appendChild(element);
// Select the new element
selectElement(element);
}
// Add custom element from modal
function addCustomElement() {
const html = customElementEditor.value.trim();
if (html) {
addElementToCanvas('custom', html);
customElementModal.style.display = 'none';
}
}
// Add resize handles to an element
function addResizeHandles(element) {
const positions = ['nw', 'ne', 'sw', 'se'];
positions.forEach(pos => {
const handle = document.createElement('div');
handle.className = `resize-handle ${pos}`;
handle.dataset.position = pos;
element.appendChild(handle);
// Add event listeners for resizing
handle.addEventListener('mousedown', (e) => {
e.stopPropagation();
isResizing = true;
resizeHandle = pos;
selectedElement = element;
// Store initial dimensions and position
startWidth = parseInt(getComputedStyle(element).width);
startHeight = parseInt(getComputedStyle(element).height);
startLeft = parseInt(getComputedStyle(element).left);
startTop = parseInt(getComputedStyle(element).top);
// Update toolbar inputs
updateToolbarWithElementData(element);
});
});
}
// Start dragging an element
function startDrag(e) {
if (e.target.classList.contains('resize-handle')) {
return; // Let the resize handle handle this
}
const element = e.target.closest('.canvas-element');
if (element) {
e.preventDefault();
isDragging = true;
selectedElement = element;
// Store initial position
startX = e.clientX;
startY = e.clientY;
startLeft = parseInt(getComputedStyle(element).left);
startTop = parseInt(getComputedStyle(element).top);
// Update toolbar inputs
updateToolbarWithElementData(element);
}
}
// Handle dragging
function handleDrag(e) {
if (isDragging && selectedElement) {
const dx = e.clientX - startX;
const dy = e.clientY - startY;
selectedElement.style.left = `${startLeft + dx}px`;
selectedElement.style.top = `${startTop + dy}px`;
// Update position inputs
posX.value = startLeft + dx;
posY.value = startTop + dy;
} else if (isResizing && selectedElement) {
const dx = e.clientX - startX;
const dy = e.clientY - startY;
let newWidth = startWidth;
let newHeight = startHeight;
let newLeft = startLeft;
let newTop = startTop;
switch (resizeHandle) {
case 'nw':
newWidth = startWidth - dx;
newHeight = startHeight - dy;
newLeft = startLeft + dx;
newTop = startTop + dy;
break;
case 'ne':
newWidth = startWidth + dx;
newHeight = startHeight - dy;
newTop = startTop + dy;
break;
case 'sw':
newWidth = startWidth - dx;
newHeight = startHeight + dy;
newLeft = startLeft + dx;
break;
case 'se':
newWidth = startWidth + dx;
newHeight = startHeight + dy;
break;
}
// Apply minimum dimensions
newWidth = Math.max(newWidth, 30);
newHeight = Math.max(newHeight, 30);
selectedElement.style.width = `${newWidth}px`;
selectedElement.style.height = `${newHeight}px`;
if (newLeft !== startLeft) selectedElement.style.left = `${newLeft}px`;
if (newTop !== startTop) selectedElement.style.top = `${newTop}px`;
// Update size inputs
width.value = newWidth;
height.value = newHeight;
if (newLeft !== startLeft) posX.value = newLeft;
if (newTop !== startTop) posY.value = newTop;
}
}
// Stop dragging
function stopDrag() {
isDragging = false;
isResizing = false;
resizeHandle = null;
}
// Select an element
function selectElement(element) {
// Remove selection from all elements
document.querySelectorAll('.canvas-element').forEach(el => {
el.classList.remove('ring-2', 'ring-blue-500');
});
// Select the new element
element.classList.add('ring-2', 'ring-blue-500');
selectedElement = element;
// Update toolbar with element data
updateToolbarWithElementData(element);
}
// Update toolbar inputs with element data
function updateToolbarWithElementData(element) {
const style = getComputedStyle(element);
// Position
posX.value = parseInt(style.left) || 0;
posY.value = parseInt(style.top) || 0;
// Size
width.value = parseInt(style.width) || 0;
height.value = parseInt(style.height) || 0;
// Rotation
const transform = style.transform;
if (transform && transform !== 'none') {
const values = transform.match(/matrix\((.+)\)/)[1].split(', ');
const a = parseFloat(values[0]);
const b = parseFloat(values[1]);
const angle = Math.round(Math.atan2(b, a) * (180 / Math.PI));
rotation.value = angle;
} else {
rotation.value = 0;
}
// Color
colorPicker.value = rgbToHex(style.color);
// Background color
if (style.background && style.background !== 'none' && !style.background.includes('gradient')) {
bgColorPicker.value = rgbToHex(style.backgroundColor);
}
// Opacity
opacity.value = Math.round(parseFloat(style.opacity) * 100);
// Z-index
zIndex.value = style.zIndex;
}
// Convert RGB to HEX
function rgbToHex(rgb) {
if (!rgb) return '#000000';
// Check if it's already hex
if (rgb.startsWith('#')) return rgb;
// Extract RGB values
const match = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
if (!match) return '#000000';
const r = parseInt(match[1]);
const g = parseInt(match[2]);
const b = parseInt(match[3]);
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
// Update element position from toolbar
function updateElementPosition() {
if (selectedElement) {
selectedElement.style.left = `${posX.value}px`;
selectedElement.style.top = `${posY.value}px`;
}
}
// Update element size from toolbar
function updateElementSize() {
if (selectedElement) {
selectedElement.style.width = `${width.value}px`;
selectedElement.style.height = `${height.value}px`;
}
}
// Update element rotation from toolbar
function updateElementRotation() {
if (selectedElement) {
selectedElement.style.transform = `rotate(${rotation.value}deg)`;
}
}
// Update element color from toolbar
function updateElementColor() {
if (selectedElement) {
selectedElement.style.color = colorPicker.value;
}
}
// Update element background from toolbar
function updateElementBackground() {
if (selectedElement) {
selectedElement.style.background = bgColorPicker.value;
}
}
// Update element opacity from toolbar
function updateElementOpacity() {
if (selectedElement) {
selectedElement.style.opacity = opacity.value / 100;
}
}
// Update element z-index from toolbar
function updateElementZIndex() {
if (selectedElement) {
selectedElement.style.zIndex = zIndex.value;
}
}
// Delete selected element
function deleteSelectedElement() {
if (selectedElement) {
selectedElement.remove();
selectedElement = null;
}
}
// Duplicate selected element
function duplicateSelectedElement() {
if (selectedElement) {
const clone = selectedElement.cloneNode(true);
canvas.appendChild(clone);
// Offset the clone slightly
const left = parseInt(getComputedStyle(selectedElement).left) + 20;
const top = parseInt(getComputedStyle(selectedElement).top) + 20;
clone.style.left = `${left}px`;
clone.style.top = `${top}px`;
// Add resize handles to the clone
addResizeHandles(clone);
// Select the new clone
selectElement(clone);
}
}
// Add new window
function addNewWindow() {
const window = document.createElement('div');
window.className = 'absolute bg-white border border-gray-300 shadow-lg rounded-lg overflow-hidden canvas-element';
window.style.width = '400px';
window.style.height = '300px';
window.style.left = '100px';
window.style.top = '100px';
// Window header
const header = document.createElement('div');
header.className = 'bg-gray-800 text-white p-2 flex justify-between items-center';
header.innerHTML = `
<span>New Window</span>
<button class="close-window text-white hover:text-gray-300">
<i class="fas fa-times"></i>
</button>
`;
// Window content
const content = document.createElement('div');
content.className = 'p-4 flex-1 overflow-auto';
content.textContent = 'Window content goes here...';
// Add elements to window
window.appendChild(header);
window.appendChild(content);
// Add to canvas
canvas.appendChild(window);
// Add resize handles
addResizeHandles(window);
// Select the new window
selectElement(window);
// Add close event
header.querySelector('.close-window').addEventListener('click', () => {
window.remove();
});
}
// Apply HTML from modal
function applyHtml() {
const html = htmlEditor.value.trim();
if (html) {
// Clear canvas
canvas.innerHTML = '';
// Add the HTML to canvas
canvas.innerHTML = html;
// Add canvas-element class and resize handles to all top-level children
Array.from(canvas.children).forEach(child => {
child.classList.add('canvas-element');
addResizeHandles(child);
});
// Close modal
htmlModal.style.display = 'none';
}
}
// Apply CSS from modal
function applyCss() {
const css = cssEditor.value.trim();
if (css) {
// Create a style element or use existing one
let styleElement = document.getElementById('custom-css');
if (!styleElement) {
styleElement = document.createElement('style');
styleElement.id = 'custom-css';
document.head.appendChild(styleElement);
}
// Set CSS
styleElement.textContent = css;
// Close modal
cssModal.style.display = 'none';
}
}
// Add color stop to gradient editor
function addColorStop() {
const stopDiv = document.createElement('div');
stopDiv.className = 'flex items-center space-x-2';
// Random color for new stop
const randomColor = '#' + Math.floor(Math.random()*16777215).toString(16);
const position = Math.floor(Math.random() * 50) + 25; // Between 25-75%
stopDiv.innerHTML = `
<input type="color" value="${randomColor}" class="color-picker">
<input type="range" min="0" max="100" value="${position}" class="flex-1">
<span>${position}%</span>
<button class="remove-stop px-2 py-1 bg-red-500 text-white rounded text-sm">
<i class="fas fa-times"></i>
</button>
`;
colorStops.appendChild(stopDiv);
// Add event listeners
stopDiv.querySelector('.color-picker').addEventListener('change', updateGradientPreview);
stopDiv.querySelector('input[type="range"]').addEventListener('input', function() {
this.nextElementSibling.textContent = this.value + '%';
updateGradientPreview();
});
updateGradientPreview();
}
// Update gradient UI based on type
function updateGradientUI() {
const type = gradientType.value;
if (type === 'linear') {
document.getElementById('linearOptions').style.display = 'block';
} else {
document.getElementById('linearOptions').style.display = 'none';
}
updateGradientPreview();
}
// Update gradient preview
function updateGradientPreview() {
const type = gradientType.value;
const stops = Array.from(colorStops.querySelectorAll('.flex.items-center')).map(div => {
const color = div.querySelector('.color-picker').value;
const position = div.querySelector('input[type="range"]').value;
return `${color} ${position}%`;
}).join(', ');
if (type === 'linear') {
const direction = gradientDirection.value;
gradientPreview.style.background = `linear-gradient(${direction}, ${stops})`;
} else {
gradientPreview.style.background = `radial-gradient(circle, ${stops})`;
}
}
// Apply gradient to selected element
function applyGradient() {
if (selectedElement) {
const type = gradientType.value;
const stops = Array.from(colorStops.querySelectorAll('.flex.items-center')).map(div => {
const color = div.querySelector('.color-picker').value;
const position = div.querySelector('input[type="range"]').value;
return `${color} ${position}%`;
}).join(', ');
if (type === 'linear') {
const direction = gradientDirection.value;
selectedElement.style.background = `linear-gradient(${direction}, ${stops})`;
} else {
selectedElement.style.background = `radial-gradient(circle, ${stops})`;
}
// Close modal
gradientModal.style.display = 'none';
}
}
// Save design to localStorage
function saveDesign() {
if (canvas) {
const html = canvas.innerHTML;
const css = document.getElementById('custom-css')?.textContent || '';
localStorage.setItem('uiDesigner_html', html);
localStorage.setItem('uiDesigner_css', css);
alert('Design saved successfully!');
}
}
// Load saved data from localStorage
function loadSavedData() {
const savedHtml = localStorage.getItem('uiDesigner_html');
const savedCss = localStorage.getItem('uiDesigner_css');
if (savedHtml) {
canvas.innerHTML = savedHtml;
// Add canvas-element class and resize handles to all top-level children
Array.from(canvas.children).forEach(child => {
child.classList.add('canvas-element');
addResizeHandles(child);
});
}
if (savedCss) {
let styleElement = document.getElementById('custom-css');
if (!styleElement) {
styleElement = document.createElement('style');
styleElement.id = 'custom-css';
document.head.appendChild(styleElement);
}
styleElement.textContent = savedCss;
// Update editors
cssEditor.value = savedCss;
}
}
// Reset canvas
function resetCanvas() {
if (confirm('Are you sure you want to reset the canvas? This cannot be undone.')) {
canvas.innerHTML = '';
// Remove custom CSS
const styleElement = document.getElementById('custom-css');
if (styleElement) {
styleElement.remove();
}
// Clear localStorage
localStorage.removeItem('uiDesigner_html');
localStorage.removeItem('uiDesigner_css');
// Clear editors
htmlEditor.value = '';
cssEditor.value = '';
selectedElement = null;
}
}
// Initialize the app
init();
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=basheer1414/ui-customization-tool" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>