| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>MorphoWeb 3D - Architecte Visuel</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://unpkg.com/feather-icons"></script> |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> |
| <style> |
| #preview-container { |
| background-color: #f3f4f6; |
| border-radius: 0.5rem; |
| height: 600px; |
| position: relative; |
| overflow: hidden; |
| } |
| .grid-cell { |
| border: 1px dashed #d1d5db; |
| position: absolute; |
| background-color: rgba(255, 255, 255, 0.3); |
| } |
| .element { |
| position: absolute; |
| cursor: move; |
| z-index: 10; |
| transition: all 0.2s; |
| } |
| .element:hover { |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); |
| transform: scale(1.02); |
| } |
| .element-header { |
| background: linear-gradient(135deg, #6366f1, #8b5cf6); |
| color: white; |
| } |
| .element-text { |
| background: linear-gradient(135deg, #f59e0b, #f97316); |
| color: white; |
| } |
| .element-image { |
| background: linear-gradient(135deg, #10b981, #059669); |
| color: white; |
| } |
| .element-button { |
| background: linear-gradient(135deg, #ef4444, #dc2626); |
| color: white; |
| } |
| .chat-message { |
| max-width: 80%; |
| border-radius: 1rem; |
| padding: 0.75rem 1rem; |
| margin-bottom: 0.5rem; |
| } |
| .user-message { |
| background-color: #6366f1; |
| color: white; |
| margin-left: auto; |
| border-bottom-right-radius: 0.25rem; |
| } |
| .ai-message { |
| background-color: #e5e7eb; |
| color: #111827; |
| margin-right: auto; |
| border-bottom-left-radius: 0.25rem; |
| } |
| </style> |
| </head> |
| <body class="bg-gray-100"> |
| <div class="flex h-screen"> |
| |
| <div class="w-64 bg-white shadow-md p-4 overflow-y-auto"> |
| <div class="flex items-center mb-6"> |
| <i data-feather="box" class="text-purple-600 mr-2"></i> |
| <h2 class="text-xl font-bold">Elements</h2> |
| </div> |
| |
| <div class="space-y-3"> |
| <div class="element-draggable p-3 bg-gray-50 rounded-lg border border-gray-200 cursor-pointer" draggable="true" data-type="header"> |
| <div class="flex items-center"> |
| <div class="w-8 h-8 rounded-full bg-indigo-100 flex items-center justify-center mr-2"> |
| <i data-feather="type" class="text-indigo-600 w-4 h-4"></i> |
| </div> |
| <span>Header</span> |
| </div> |
| </div> |
| |
| <div class="element-draggable p-3 bg-gray-50 rounded-lg border border-gray-200 cursor-pointer" draggable="true" data-type="text"> |
| <div class="flex items-center"> |
| <div class="w-8 h-8 rounded-full bg-amber-100 flex items-center justify-center mr-2"> |
| <i data-feather="align-left" class="text-amber-600 w-4 h-4"></i> |
| </div> |
| <span>Text Block</span> |
| </div> |
| </div> |
| |
| <div class="element-draggable p-3 bg-gray-50 rounded-lg border border-gray-200 cursor-pointer" draggable="true" data-type="image"> |
| <div class="flex items-center"> |
| <div class="w-8 h-8 rounded-full bg-emerald-100 flex items-center justify-center mr-2"> |
| <i data-feather="image" class="text-emerald-600 w-4 h-4"></i> |
| </div> |
| <span>Image</span> |
| </div> |
| </div> |
| |
| <div class="element-draggable p-3 bg-gray-50 rounded-lg border border-gray-200 cursor-pointer" draggable="true" data-type="button"> |
| <div class="flex items-center"> |
| <div class="w-8 h-8 rounded-full bg-red-100 flex items-center justify-center mr-2"> |
| <i data-feather="mouse-pointer" class="text-red-600 w-4 h-4"></i> |
| </div> |
| <span>Button</span> |
| </div> |
| </div> |
| |
| <div class="element-draggable p-3 bg-gray-50 rounded-lg border border-gray-200 cursor-pointer" draggable="true" data-type="video"> |
| <div class="flex items-center"> |
| <div class="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center mr-2"> |
| <i data-feather="video" class="text-blue-600 w-4 h-4"></i> |
| </div> |
| <span>Video</span> |
| </div> |
| </div> |
| |
| <div class="element-draggable p-3 bg-gray-50 rounded-lg border border-gray-200 cursor-pointer" draggable="true" data-type="form"> |
| <div class="flex items-center"> |
| <div class="w-8 h-8 rounded-full bg-green-100 flex items-center justify-center mr-2"> |
| <i data-feather="edit-3" class="text-green-600 w-4 h-4"></i> |
| </div> |
| <span>Contact Form</span> |
| </div> |
| </div> |
| </div> |
| |
| <div class="mt-8"> |
| <h3 class="font-medium mb-2 flex items-center"> |
| <i data-feather="help-circle" class="w-4 h-4 mr-2"></i> |
| Quick Tips |
| </h3> |
| <div class="text-sm text-gray-600 space-y-2"> |
| <p>• Drag elements to the canvas</p> |
| <p>• Double-click to edit content</p> |
| <p>• Resize by dragging corners</p> |
| <p>• Ask Codex AI for help anytime</p> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="flex-1 flex flex-col"> |
| |
| <div class="bg-white shadow-sm p-3 flex items-center justify-between"> |
| <div class="flex items-center space-x-4"> |
| <h1 class="font-bold text-lg">Architecte Visuel</h1> |
| <div class="flex items-center text-sm bg-indigo-50 text-indigo-700 px-3 py-1 rounded-full"> |
| <i data-feather="compass" class="w-4 h-4 mr-1"></i> |
| <span>Construction Guidée</span> |
| </div> |
| </div> |
| |
| <div class="flex space-x-3"> |
| <button class="flex items-center px-3 py-1 bg-gray-100 rounded-md text-sm hover:bg-gray-200"> |
| <i data-feather="eye" class="w-4 h-4 mr-1"></i> |
| Prévisualiser |
| </button> |
| <button class="flex items-center px-3 py-1 bg-indigo-600 text-white rounded-md text-sm hover:bg-indigo-700"> |
| <i data-feather="download" class="w-4 h-4 mr-1"></i> |
| Exporter |
| </button> |
| </div> |
| </div> |
| |
| |
| <div class="flex-1 p-6 overflow-auto"> |
| <div id="preview-container" class="w-full relative"> |
| |
| </div> |
| </div> |
| |
| |
| <div class="bg-white border-t border-gray-200 h-64 flex flex-col"> |
| <div class="p-3 border-b border-gray-200 flex items-center bg-gray-50"> |
| <div class="w-8 h-8 rounded-full bg-purple-100 flex items-center justify-center mr-2"> |
| <i data-feather="cpu" class="text-purple-600 w-4 h-4"></i> |
| </div> |
| <h3 class="font-medium">Assistant IA Codex</h3> |
| <button class="ml-auto text-gray-500 hover:text-gray-700"> |
| <i data-feather="minimize-2" class="w-4 h-4"></i> |
| </button> |
| </div> |
| |
| <div id="chat-messages" class="flex-1 p-4 overflow-y-auto"> |
| <div class="chat-message ai-message"> |
| <p>Bonjour ! Je suis Codex, votre assistant IA. Je peux vous aider à construire votre site web. Essayez de glisser un élément depuis la barre latérale pour commencer !</p> |
| </div> |
| </div> |
| |
| <div class="p-3 border-t border-gray-200 flex"> |
| <input type="text" placeholder="Demandez de l'aide à Codex..." class="flex-1 border border-gray-300 rounded-l-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500"> |
| <button class="bg-indigo-600 text-white px-4 py-2 rounded-r-lg hover:bg-indigo-700"> |
| <i data-feather="send" class="w-4 h-4"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', () => { |
| feather.replace(); |
| |
| |
| const container = document.getElementById('preview-container'); |
| const containerWidth = container.clientWidth; |
| const containerHeight = container.clientHeight; |
| const gridSize = 20; |
| |
| |
| for (let x = 0; x < containerWidth; x += gridSize) { |
| for (let y = 0; y < containerHeight; y += gridSize) { |
| const cell = document.createElement('div'); |
| cell.className = 'grid-cell'; |
| cell.style.width = `${gridSize}px`; |
| cell.style.height = `${gridSize}px`; |
| cell.style.left = `${x}px`; |
| cell.style.top = `${y}px`; |
| container.appendChild(cell); |
| } |
| } |
| |
| |
| const draggables = document.querySelectorAll('.element-draggable'); |
| |
| draggables.forEach(draggable => { |
| draggable.addEventListener('dragstart', (e) => { |
| e.dataTransfer.setData('text/plain', draggable.dataset.type); |
| e.dataTransfer.effectAllowed = 'copy'; |
| }); |
| }); |
| |
| container.addEventListener('dragover', (e) => { |
| e.preventDefault(); |
| e.dataTransfer.dropEffect = 'copy'; |
| }); |
| |
| container.addEventListener('drop', (e) => { |
| e.preventDefault(); |
| const type = e.dataTransfer.getData('text/plain'); |
| const x = e.clientX - container.getBoundingClientRect().left; |
| const y = e.clientY - container.getBoundingClientRect().top; |
| |
| |
| const snappedX = Math.floor(x / gridSize) * gridSize; |
| const snappedY = Math.floor(y / gridSize) * gridSize; |
| |
| createElement(type, snappedX, snappedY); |
| }); |
| |
| |
| function createElement(type, x, y) { |
| const element = document.createElement('div'); |
| element.className = `element element-${type}`; |
| element.dataset.type = type; |
| |
| |
| element.style.left = `${x}px`; |
| element.style.top = `${y}px`; |
| |
| |
| let width, height, content; |
| |
| switch(type) { |
| case 'header': |
| width = 300; |
| height = 80; |
| content = '<h1>Your Header Text</h1>'; |
| element.classList.add('element-header'); |
| break; |
| case 'text': |
| width = 200; |
| height = 120; |
| content = '<p>Double-click to edit this text block.</p>'; |
| element.classList.add('element-text'); |
| break; |
| case 'image': |
| width = 250; |
| height = 150; |
| content = '<div class="flex items-center justify-center h-full"><i data-feather="image" class="w-12 h-12"></i></div>'; |
| element.classList.add('element-image'); |
| break; |
| case 'button': |
| width = 120; |
| height = 40; |
| content = '<button class="w-full h-full">Click Me</button>'; |
| element.classList.add('element-button'); |
| break; |
| default: |
| width = 150; |
| height = 100; |
| content = '<p>New Element</p>'; |
| } |
| |
| element.style.width = `${width}px`; |
| element.style.height = `${height}px`; |
| element.innerHTML = content; |
| |
| |
| element.draggable = true; |
| |
| element.addEventListener('dragstart', (e) => { |
| e.dataTransfer.setData('text/plain', 'move'); |
| e.dataTransfer.setData('element-id', e.target.id || (e.target.id = 'el-' + Date.now())); |
| e.dataTransfer.effectAllowed = 'move'; |
| }); |
| |
| |
| element.addEventListener('dblclick', () => { |
| if (type === 'text') { |
| const currentText = element.querySelector('p').textContent; |
| const newText = prompt('Edit text:', currentText); |
| if (newText !== null) { |
| element.querySelector('p').textContent = newText; |
| } |
| } else if (type === 'header') { |
| const currentText = element.querySelector('h1').textContent; |
| const newText = prompt('Edit header:', currentText); |
| if (newText !== null) { |
| element.querySelector('h1').textContent = newText; |
| } |
| } else if (type === 'button') { |
| const currentText = element.querySelector('button').textContent; |
| const newText = prompt('Edit button text:', currentText); |
| if (newText !== null) { |
| element.querySelector('button').textContent = newText; |
| } |
| } |
| }); |
| |
| |
| if (type !== 'button') { |
| const resizeHandle = document.createElement('div'); |
| resizeHandle.className = 'absolute bottom-0 right-0 w-3 h-3 bg-white border border-gray-400 cursor-nwse-resize'; |
| resizeHandle.style.transform = 'translate(50%, 50%)'; |
| |
| resizeHandle.addEventListener('mousedown', initResize); |
| element.appendChild(resizeHandle); |
| } |
| |
| container.appendChild(element); |
| feather.replace(); |
| |
| |
| addAIMessage(`I've added a ${type} element to your page. You can move it by dragging.`); |
| } |
| |
| |
| container.addEventListener('dragover', (e) => { |
| e.preventDefault(); |
| e.dataTransfer.dropEffect = 'move'; |
| }); |
| |
| container.addEventListener('drop', (e) => { |
| e.preventDefault(); |
| if (e.dataTransfer.getData('text/plain') === 'move') { |
| const id = e.dataTransfer.getData('element-id'); |
| const element = document.getElementById(id); |
| |
| if (element) { |
| const x = e.clientX - container.getBoundingClientRect().left - parseInt(element.style.width) / 2; |
| const y = e.clientY - container.getBoundingClientRect().top - parseInt(element.style.height) / 2; |
| |
| |
| const snappedX = Math.floor(x / gridSize) * gridSize; |
| const snappedY = Math.floor(y / gridSize) * gridSize; |
| |
| element.style.left = `${snappedX}px`; |
| element.style.top = `${snappedY}px`; |
| } |
| } |
| }); |
| |
| |
| function initResize(e) { |
| e.stopPropagation(); |
| |
| const element = e.target.parentElement; |
| const startX = e.clientX; |
| const startY = e.clientY; |
| const startWidth = parseInt(element.style.width); |
| const startHeight = parseInt(element.style.height); |
| |
| function doResize(e) { |
| const newWidth = startWidth + (e.clientX - startX); |
| const newHeight = startHeight + (e.clientY - startY); |
| |
| |
| const snappedWidth = Math.max(40, Math.floor(newWidth / gridSize) * gridSize); |
| const snappedHeight = Math.max(40, Math.floor(newHeight / gridSize) * gridSize); |
| |
| element.style.width = `${snappedWidth}px`; |
| element.style.height = `${snappedHeight}px`; |
| } |
| |
| function stopResize() { |
| window.removeEventListener('mousemove', doResize); |
| window.removeEventListener('mouseup', stopResize); |
| } |
| |
| window.addEventListener('mousemove', doResize); |
| window.addEventListener('mouseup', stopResize); |
| } |
| |
| |
| function addAIMessage(text) { |
| const chat = document.getElementById('chat-messages'); |
| const message = document.createElement('div'); |
| message.className = 'chat-message ai-message'; |
| message.innerHTML = `<p>${text}</p>`; |
| chat.appendChild(message); |
| chat.scrollTop = chat.scrollHeight; |
| } |
| |
| |
| const chatInput = document.querySelector('#chat-messages + div input'); |
| const chatButton = document.querySelector('#chat-messages + div button'); |
| |
| chatButton.addEventListener('click', () => { |
| const message = chatInput.value.trim(); |
| if (message) { |
| |
| const chat = document.getElementById('chat-messages'); |
| const userMessage = document.createElement('div'); |
| userMessage.className = 'chat-message user-message'; |
| userMessage.innerHTML = `<p>${message}</p>`; |
| chat.appendChild(userMessage); |
| |
| |
| chatInput.value = ''; |
| |
| |
| setTimeout(() => { |
| const responses = [ |
| "Je peux vous aider avec ça ! Que souhaitez-vous savoir précisément ?", |
| "Excellente question ! Pour cet élément, essayez de double-cliquer pour modifier son contenu.", |
| "Vous pouvez redimensionner les éléments en tirant la poignée dans le coin inférieur droit.", |
| "Essayez de glisser un autre élément depuis la barre latérale pour ajouter du contenu.", |
| "Voulez-vous que je génère du contenu exemple pour vous ?" |
| ]; |
| addAIMessage(responses[Math.floor(Math.random() * responses.length)]); |
| }, 1000); |
| } |
| }); |
| |
| |
| chatInput.addEventListener('keypress', (e) => { |
| if (e.key === 'Enter') { |
| chatButton.click(); |
| } |
| }); |
| }); |
| </script> |
| </body> |
| </html> |
|
|