ui-creater / index.html
basheer1414's picture
Okay do one thing let's remove the sidebar we don't want I think so because you can create like the toolbar on the left side also see you know the photoshop left side told bar excited like that I need and that water input card IV and section everything over there as a small small buttons again I don't small buttons I don't want waste any Canvas face okay and make the Canvas fill every balance and you didn't remove that position and size settings from the toolbar that of top under rotation I don't want that on the toolbox action I want its on any and I went what I am going to add any element on the Canvas I am and able to 100% customisation for that element you understand from the Canvas I am I need to enable the moving ragging I want I need enable is icing I need I want you to made make enable like the corner curving and that's adding distance of the shadow whatever I want and customisation of the element like 100% customisation on the canvas - Initial Deployment
472d17b verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Enhanced 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">
<link rel="stylesheet" href="https://uiverse.io/galaxy/style.min.css">
<style>
/* Custom CSS for enhanced UI */
.sidebar {
transition: all 0.3s ease;
}
.sidebar.collapsed {
transform: translateX(-90%);
}
.sidebar.collapsed:hover {
transform: translateX(0);
}
#toggleSidebar {
transition: transform 0.3s ease;
position: relative;
z-index: 10;
}
.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;
background-image:
linear-gradient(rgba(0, 0, 0, 0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(0, 0, 0, 0.05) 1px, transparent 1px);
background-size: 20px 20px;
}
.resize-handle {
position: absolute;
width: 12px;
height: 12px;
background-color: #3b82f6;
border-radius: 50%;
z-index: 10;
border: 2px solid white;
box-shadow: 0 0 2px rgba(0,0,0,0.5);
}
.resize-handle.nw { top: -6px; left: -6px; cursor: nw-resize; }
.resize-handle.ne { top: -6px; right: -6px; cursor: ne-resize; }
.resize-handle.sw { bottom: -6px; left: -6px; cursor: sw-resize; }
.resize-handle.se { bottom: -6px; right: -6px; cursor: se-resize; }
.toolbar {
transition: all 0.3s ease;
height: 32px;
}
.toolbar input[type="number"],
.toolbar input[type="text"],
.toolbar input[type="range"] {
height: 20px;
}
.toolbar button {
min-width: 24px;
}
.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;
}
/* Galaxy component preview sizing */
.uiverse-element .button-galaxy {
transform: scale(0.8);
}
.uiverse-element .input-galaxy {
width: 200px;
}
.uiverse-element .card-galaxy {
transform: scale(0.8);
}
.uiverse-element .checkbox-galaxy {
transform: scale(0.8);
}
/* Make sure Galaxy components are visible on canvas */
.canvas-element .button-galaxy,
.canvas-element .input-galaxy,
.canvas-element .card-galaxy,
.canvas-element .checkbox-galaxy {
transform: scale(1);
}
/* Selection styles */
.canvas-element.selected {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
/* Rotation handle */
.rotation-handle {
position: absolute;
width: 20px;
height: 20px;
top: -30px;
left: 50%;
transform: translateX(-50%);
cursor: grab;
background-color: #3b82f6;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 10px;
z-index: 10;
}
/* Element ghost */
.element-ghost {
position: absolute;
opacity: 0.5;
pointer-events: none;
z-index: 100;
}
/* Grid toggle */
.grid-toggle {
position: absolute;
bottom: 10px;
right: 10px;
background: white;
padding: 5px 10px;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
cursor: pointer;
z-index: 10;
}
</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">Enhanced UI Customization Tool</h1>
<div class="flex space-x-2">
<button id="htmlBtn" class="bg-blue-500 hover:bg-blue-600 text-white px-2 py-1 rounded text-xs">
<i class="fas fa-code mr-1"></i>HTML
</button>
<button id="cssBtn" class="bg-purple-500 hover:bg-purple-600 text-white px-2 py-1 rounded text-xs">
<i class="fab fa-css3-alt mr-1"></i>CSS
</button>
<button id="saveBtn" class="bg-green-500 hover:bg-green-600 text-white px-2 py-1 rounded text-xs">
<i class="fas fa-save mr-1"></i>Save
</button>
<button id="resetBtn" class="bg-red-500 hover:bg-red-600 text-white px-2 py-1 rounded text-xs">
<i class="fas fa-trash-alt mr-1"></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-1 flex items-center space-x-2 overflow-x-auto text-xs">
<div class="flex items-center space-x-1">
<span>Pos:</span>
<input type="number" id="posX" placeholder="X" class="w-10 px-1 py-0.5 border rounded text-xs">
<input type="number" id="posY" placeholder="Y" class="w-10 px-1 py-0.5 border rounded text-xs">
</div>
<div class="flex items-center space-x-1">
<span>Size:</span>
<input type="number" id="width" placeholder="W" class="w-10 px-1 py-0.5 border rounded text-xs">
<input type="number" id="height" placeholder="H" class="w-10 px-1 py-0.5 border rounded text-xs">
</div>
<div class="flex items-center space-x-1">
<span>Rot:</span>
<input type="number" id="rotation" placeholder="deg" class="w-12 px-1 py-0.5 border rounded text-xs">
</div>
<div class="flex items-center space-x-1">
<span>Color:</span>
<input type="color" id="colorPicker" class="w-6 h-6 cursor-pointer">
</div>
<div class="flex items-center space-x-1">
<span>BG:</span>
<input type="color" id="bgColorPicker" class="w-6 h-6 cursor-pointer">
</div>
<div class="flex items-center space-x-1">
<button id="gradientBtn" class="px-1 py-0.5 bg-gray-200 rounded text-xs">Grad</button>
</div>
<div class="flex items-center space-x-1">
<span>Opacity:</span>
<input type="range" id="opacity" min="0" max="100" value="100" class="w-16">
</div>
<div class="flex items-center space-x-1">
<span>Z:</span>
<input type="number" id="zIndex" value="1" class="w-10 px-1 py-0.5 border rounded text-xs">
</div>
<div class="flex items-center space-x-1">
<button id="deleteBtn" class="px-1 py-0.5 bg-red-500 text-white rounded text-xs">
<i class="fas fa-trash-alt"></i>
</button>
</div>
<div class="flex items-center space-x-1">
<button id="duplicateBtn" class="px-1 py-0.5 bg-blue-500 text-white rounded text-xs">
<i class="fas fa-copy"></i>
</button>
</div>
<div class="flex items-center space-x-1">
<button id="newWindowBtn" class="px-1 py-0.5 bg-green-500 text-white rounded text-xs">
<i class="fas fa-plus"></i>
</button>
</div>
<div class="flex items-center space-x-1">
<button id="snapToggle" class="px-1 py-0.5 bg-yellow-500 text-white rounded text-xs">
<i class="fas fa-magnet"></i>
</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 id="gridToggle" class="grid-toggle">
<i class="fas fa-th"></i> Grid
</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 p-4">
<!-- Galaxy Buttons -->
<div class="uiverse-element border rounded p-4">
<h3 class="font-medium mb-2">Galaxy Button 1</h3>
<div class="mb-3 flex justify-center">
<button class="button-galaxy">
<span class="button-galaxy__content">Hover me</span>
<span class="button-galaxy__glow"></span>
<span class="button-galaxy__stars"></span>
</button>
</div>
<div class="flex justify-between">
<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 to Canvas
</button>
</div>
</div>
<!-- Galaxy Input -->
<div class="uiverse-element border rounded p-4">
<h3 class="font-medium mb-2">Galaxy Input</h3>
<div class="mb-3 flex justify-center">
<div class="input-galaxy">
<input class="input-galaxy__field" type="text" placeholder=" " />
<label class="input-galaxy__label">Username</label>
<div class="input-galaxy__underline"></div>
</div>
</div>
<div class="flex justify-between">
<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 to Canvas
</button>
</div>
</div>
<!-- Galaxy Card -->
<div class="uiverse-element border rounded p-4">
<h3 class="font-medium mb-2">Galaxy Card</h3>
<div class="mb-3 flex justify-center">
<div class="card-galaxy">
<div class="card-galaxy__content">
<h3 class="card-galaxy__title">Galaxy Card</h3>
<p class="card-galaxy__description">Beautiful galaxy-themed card</p>
</div>
</div>
</div>
<div class="flex justify-between">
<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 to Canvas
</button>
</div>
</div>
<!-- Galaxy Checkbox -->
<div class="uiverse-element border rounded p-4">
<h3 class="font-medium mb-2">Galaxy Checkbox</h3>
<div class="mb-3 flex justify-center">
<label class="checkbox-galaxy">
<input type="checkbox" class="checkbox-galaxy__input" />
<div class="checkbox-galaxy__box"></div>
<span class="checkbox-galaxy__text">Check me</span>
</label>
</div>
<div class="flex justify-between">
<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 to Canvas
</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 snapToggle = document.getElementById('snapToggle');
const gridToggle = document.getElementById('gridToggle');
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 isRotating = false;
let resizeHandle = null;
let startX, startY, startWidth, startHeight, startLeft, startTop;
let ghostElement = null;
let snapToGrid = true;
let gridSize = 20;
// 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', (e) => {
const type = btn.dataset.type;
if (type === 'custom') {
customElementModal.style.display = 'block';
customElementEditor.value = '';
} else {
// Get mouse position relative to canvas
const canvasRect = canvas.getBoundingClientRect();
const x = e.clientX - canvasRect.left;
const y = e.clientY - canvasRect.top;
addElementToCanvas(type, null, x, y);
}
});
});
// Uiverse button
uiverseBtn.addEventListener('click', () => uiverseModal.style.display = 'block');
// Uiverse modal interactions
document.querySelectorAll('.add-to-canvas').forEach(btn => {
btn.addEventListener('click', (e) => {
const element = btn.closest('.uiverse-element');
const preview = element.querySelector('.button-galaxy, .input-galaxy, .card-galaxy, .checkbox-galaxy');
if (preview) {
const clone = preview.cloneNode(true);
// Make sure Galaxy components are properly initialized
if (clone.classList.contains('input-galaxy')) {
initGalaxyInput(clone);
} else if (clone.classList.contains('checkbox-galaxy')) {
initGalaxyCheckbox(clone);
}
// Get mouse position relative to canvas
const canvasRect = canvas.getBoundingClientRect();
const x = e.clientX - canvasRect.left;
const y = e.clientY - canvasRect.top;
addElementToCanvas(clone, null, x, y);
uiverseModal.style.display = 'none';
}
});
});
// Initialize Galaxy input functionality
function initGalaxyInput(input) {
const field = input.querySelector('.input-galaxy__field');
const label = input.querySelector('.input-galaxy__label');
field.addEventListener('focus', () => {
label.classList.add('input-galaxy__label--active');
});
field.addEventListener('blur', () => {
if (!field.value) {
label.classList.remove('input-galaxy__label--active');
}
});
}
// Initialize Galaxy checkbox functionality
function initGalaxyCheckbox(checkbox) {
const input = checkbox.querySelector('.checkbox-galaxy__input');
const box = checkbox.querySelector('.checkbox-galaxy__box');
input.addEventListener('change', () => {
if (input.checked) {
box.classList.add('checkbox-galaxy__box--checked');
} else {
box.classList.remove('checkbox-galaxy__box--checked');
}
});
}
// 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);
// Snap to grid toggle
snapToggle.addEventListener('click', () => {
snapToGrid = !snapToGrid;
snapToggle.classList.toggle('bg-yellow-600', snapToGrid);
});
// Grid toggle
gridToggle.addEventListener('click', () => {
canvasContainer.classList.toggle('bg-none');
gridToggle.classList.toggle('bg-blue-200');
});
}
// 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');
toggleSidebar.style.transform = 'translateX(64px)';
} else {
icon.classList.remove('fa-chevron-right');
icon.classList.add('fa-chevron-left');
toggleSidebar.style.transform = 'translateX(0)';
}
}
// Add element to canvas
function addElementToCanvas(type, html = null, x = 50, y = 50) {
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 = `${x}px`;
element.style.top = `${y}px`;
element.style.width = 'auto';
element.style.minWidth = '100px';
element.style.minHeight = '40px';
// Add resize handles
addResizeHandles(element);
// Add rotation handle
addRotationHandle(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);
});
});
}
// Add rotation handle to an element
function addRotationHandle(element) {
const handle = document.createElement('div');
handle.className = 'rotation-handle';
handle.innerHTML = '<i class="fas fa-sync-alt"></i>';
element.appendChild(handle);
// Add event listeners for rotation
handle.addEventListener('mousedown', (e) => {
e.stopPropagation();
isRotating = true;
selectedElement = element;
// Store initial position
const rect = element.getBoundingClientRect();
startX = e.clientX;
startY = e.clientY;
// Get current rotation
const transform = getComputedStyle(element).transform;
let currentRotation = 0;
if (transform && transform !== 'none') {
const values = transform.match(/matrix\((.+)\)/)[1].split(', ');
const a = parseFloat(values[0]);
const b = parseFloat(values[1]);
currentRotation = Math.round(Math.atan2(b, a) * (180 / Math.PI));
}
// Update toolbar inputs
rotation.value = currentRotation;
});
}
// Start dragging an element
function startDrag(e) {
if (e.target.classList.contains('resize-handle') ||
e.target.classList.contains('rotation-handle')) {
return; // Let the resize/rotation 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);
// Create ghost element
createGhostElement(element);
// Update toolbar inputs
updateToolbarWithElementData(element);
// Select the element
selectElement(element);
} else {
// Clicked on canvas background: deselect
deselectElement();
}
}
// Create ghost element for dragging
function createGhostElement(element) {
ghostElement = element.cloneNode(true);
ghostElement.classList.add('element-ghost');
ghostElement.style.opacity = '0.7';
ghostElement.style.pointerEvents = 'none';
// Position ghost element
ghostElement.style.left = element.style.left;
ghostElement.style.top = element.style.top;
ghostElement.style.width = element.style.width;
ghostElement.style.height = element.style.height;
ghostElement.style.transform = element.style.transform;
canvas.appendChild(ghostElement);
}
// Handle dragging
function handleDrag(e) {
if (isDragging && selectedElement && ghostElement) {
const dx = e.clientX - startX;
const dy = e.clientY - startY;
let newLeft = startLeft + dx;
let newTop = startTop + dy;
// Snap to grid if enabled
if (snapToGrid) {
newLeft = Math.round(newLeft / gridSize) * gridSize;
newTop = Math.round(newTop / gridSize) * gridSize;
}
ghostElement.style.left = `${newLeft}px`;
ghostElement.style.top = `${newTop}px`;
} 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;
} else if (isRotating && selectedElement) {
const rect = selectedElement.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
const angle = Math.atan2(e.clientY - centerY, e.clientX - centerX) * 180 / Math.PI;
// Apply rotation
selectedElement.style.transform = `rotate(${angle}deg)`;
// Update toolbar
rotation.value = Math.round(angle);
}
}
// Stop dragging
function stopDrag() {
if (isDragging && selectedElement && ghostElement) {
// Apply ghost position to actual element
selectedElement.style.left = ghostElement.style.left;
selectedElement.style.top = ghostElement.style.top;
// Update position inputs
posX.value = parseInt(ghostElement.style.left);
posY.value = parseInt(ghostElement.style.top);
// Remove ghost element
ghostElement.remove();
ghostElement = null;
}
isDragging = false;
isResizing = false;
isRotating = false;
resizeHandle = null;
}
// Select an element
function selectElement(element) {
// Remove selection from all elements
document.querySelectorAll('.canvas-element').forEach(el => {
el.classList.remove('selected', 'ring-2', 'ring-blue-500');
});
// Select the new element
element.classList.add('selected', 'ring-2', 'ring-blue-500');
selectedElement = element;
// Update toolbar with element data
updateToolbarWithElementData(element);
}
// Deselect element
function deselectElement() {
if (selectedElement) {
selectedElement.classList.remove('selected', 'ring-2', 'ring-blue-500');
selectedElement = null;
clearToolbar();
}
}
// Clear toolbar inputs
function clearToolbar() {
posX.value = '';
posY.value = '';
width.value = '';
height.value = '';
rotation.value = '';
colorPicker.value = '#000000';
bgColorPicker.value = '#ffffff';
opacity.value = '100';
zIndex.value = '1';
}
// 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;
clearToolbar();
}
}
// 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);
// Add rotation handle to the clone
addRotationHandle(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);
// Add rotation handle
addRotationHandle(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);
addRotationHandle(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);
addRotationHandle(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-creater" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>