morphoweb-3d-architect / beginner.html
sam-bottes's picture
Concept Général de MorphoWeb 3D
6947c8c verified
Raw
History Blame Contribute Delete
21.5 kB
<!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">
<!-- Sidebar -->
<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>
<!-- Main Content -->
<div class="flex-1 flex flex-col">
<!-- Toolbar -->
<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>
<!-- Canvas Area -->
<div class="flex-1 p-6 overflow-auto">
<div id="preview-container" class="w-full relative">
<!-- Grid will be generated here -->
</div>
</div>
<!-- AI Assistant Chat -->
<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();
// Create grid for the canvas
const container = document.getElementById('preview-container');
const containerWidth = container.clientWidth;
const containerHeight = container.clientHeight;
const gridSize = 20; // pixels
// Generate grid cells
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);
}
}
// Handle drag and drop for elements
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;
// Snap to grid
const snappedX = Math.floor(x / gridSize) * gridSize;
const snappedY = Math.floor(y / gridSize) * gridSize;
createElement(type, snappedX, snappedY);
});
// Create elements on the canvas
function createElement(type, x, y) {
const element = document.createElement('div');
element.className = `element element-${type}`;
element.dataset.type = type;
// Position and size
element.style.left = `${x}px`;
element.style.top = `${y}px`;
// Set default dimensions based on type
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;
// Make element draggable
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';
});
// Handle double-click to edit
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;
}
}
});
// Add resize handles
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();
// Notify AI
addAIMessage(`I've added a ${type} element to your page. You can move it by dragging.`);
}
// Handle moving elements
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;
// Snap to grid
const snappedX = Math.floor(x / gridSize) * gridSize;
const snappedY = Math.floor(y / gridSize) * gridSize;
element.style.left = `${snappedX}px`;
element.style.top = `${snappedY}px`;
}
}
});
// Resize functionality
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);
// Snap to grid
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);
}
// Chat functionality
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;
}
// Handle chat input
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) {
// Add user 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);
// Clear input
chatInput.value = '';
// Simulate AI response (in a real app, this would call an API)
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);
}
});
// Allow pressing Enter to send message
chatInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
chatButton.click();
}
});
});
</script>
</body>
</html>