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
| <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> |