motioncraft-studio / index.html
abdelrahman98's picture
app still not showing anything
7da8881 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MotionCraft Studio - SVG Animation Tool</title>
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
<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://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/svg.js@3.0/dist/svg.min.js"></script>
</head>
<body class="bg-gray-50 text-gray-900">
<div id="root" class="h-screen flex flex-col">
<div class="flex items-center justify-center h-full">
<div class="text-center">
<div class="animate-pulse">
<i data-feather="loader" class="w-12 h-12 text-indigo-500"></i>
</div>
<p class="mt-4 text-gray-600">Loading MotionCraft Studio...</p>
</div>
</div>
</div>
<script type="text/babel">
const { useState, useRef, useEffect } = React;
const { createRoot } = ReactDOM;
// Main App Component
const SVG = window.SVG; // Make SVG.js available globally
function App() {
const [activeTool, setActiveTool] = useState('select');
const [layers, setLayers] = React.useState([]);
const [selectedLayer, setSelectedLayer] = React.useState(null);
const [timeline, setTimeline] = React.useState([]);
const [currentFrame, setCurrentFrame] = React.useState(0);
const [isPlaying, setIsPlaying] = React.useState(false);
const svgContainerRef = React.useRef(null);
const [svgInstance, setSvgInstance] = React.useState(null);
// Initialize SVG.js and check for errors
React.useEffect(() => {
try {
if (svgContainerRef.current && !svgInstance && window.SVG) {
const svg = SVG(svgContainerRef.current).size('100%', '100%');
if (!svg) throw new Error('SVG initialization failed');
setSvgInstance(svg);
}
} catch (error) {
console.error('SVG.js Error:', error);
alert('Failed to initialize SVG editor. Please refresh the page.');
}
}, [svgInstance]);
// Tools panel
const tools = React.useMemo(() => [
{ id: 'select', icon: 'mouse-pointer', name: 'Select' },
{ id: 'rectangle', icon: 'square', name: 'Rectangle' },
{ id: 'circle', icon: 'circle', name: 'Circle' },
{ id: 'path', icon: 'pen-tool', name: 'Path' },
{ id: 'text', icon: 'type', name: 'Text' },
], []);
// Add a new shape
const addShape = (type) => {
if (!svgInstance) return;
const id = Date.now().toString();
let shape;
switch(type) {
case 'rectangle':
shape = svgInstance.rect(100, 100).fill('#4f46e5').move(50, 50);
break;
case 'circle':
shape = svgInstance.circle(100).fill('#ec4899').move(50, 50);
break;
case 'text':
shape = svgInstance.text('Hello').font({ size: 24 }).fill('#000').move(50, 50);
break;
default:
shape = svgInstance.rect(100, 100).fill('#4f46e5').move(50, 50);
}
const newLayer = {
id,
name: `${type}-${id.slice(-4)}`,
type,
element: shape,
properties: {}
};
setLayers([...layers, newLayer]);
setSelectedLayer(newLayer);
};
// Timeline playback controls
const togglePlayback = React.useCallback(() => {
setIsPlaying(prev => !prev);
// Animation logic would go here
}, []);
return (
<div className="flex flex-col h-full" id="app-container">
{/* Top Navigation */}
<header className="bg-white border-b border-gray-200 px-4 py-2 flex items-center justify-between">
<div className="flex items-center space-x-4">
<h1 className="text-xl font-bold text-indigo-600">MotionCraft Studio</h1>
<nav className="flex space-x-2">
<button className="px-3 py-1 text-sm rounded hover:bg-gray-100">File</button>
<button className="px-3 py-1 text-sm rounded hover:bg-gray-100">Edit</button>
<button className="px-3 py-1 text-sm rounded hover:bg-gray-100">View</button>
<button className="px-3 py-1 text-sm rounded hover:bg-gray-100">Help</button>
</nav>
</div>
<div className="flex items-center space-x-2">
<button className="px-4 py-1 bg-indigo-600 text-white rounded text-sm font-medium hover:bg-indigo-700">
Export
</button>
</div>
</header>
{/* Main Workspace */}
<div className="flex flex-1 overflow-hidden">
{/* Tools Panel */}
<div className="w-16 bg-white border-r border-gray-200 flex flex-col items-center py-4 space-y-4">
{tools.map(tool => (
<button
key={tool.id}
onClick={() => tool.id !== 'select' ? addShape(tool.id) : setActiveTool(tool.id)}
className={`p-2 rounded-lg ${activeTool === tool.id ? 'bg-indigo-100 text-indigo-600' : 'hover:bg-gray-100'}`}
title={tool.name}
>
<i data-feather={tool.icon} className="w-5 h-5"></i>
</button>
))}
</div>
{/* Layers Panel */}
<div className="w-64 bg-white border-r border-gray-200 flex flex-col">
<div className="px-4 py-2 border-b border-gray-200 flex justify-between items-center">
<h2 className="font-medium">Layers</h2>
<button className="text-gray-500 hover:text-gray-700">
<i data-feather="plus" className="w-4 h-4"></i>
</button>
</div>
<div className="flex-1 overflow-y-auto">
{layers.map(layer => (
<div
key={layer.id}
onClick={() => setSelectedLayer(layer)}
className={`px-4 py-2 flex items-center space-x-2 cursor-pointer ${selectedLayer?.id === layer.id ? 'bg-indigo-50 text-indigo-600' : 'hover:bg-gray-50'}`}
>
<i data-feather={layer.type === 'rectangle' ? 'square' : layer.type === 'circle' ? 'circle' : 'type'} className="w-4 h-4"></i>
<span className="text-sm truncate">{layer.name}</span>
</div>
))}
</div>
</div>
{/* Canvas Area */}
<div className="flex-1 bg-gray-100 relative overflow-hidden">
<div
ref={svgContainerRef}
className="absolute inset-0 bg-white shadow-inner"
style={{ backgroundImage: 'linear-gradient(45deg, #f5f5f5 25%, transparent 25%, transparent 75%, #f5f5f5 75%, #f5f5f5), linear-gradient(45deg, #f5f5f5 25%, transparent 25%, transparent 75%, #f5f5f5 75%, #f5f5f5)', backgroundSize: '20px 20px', backgroundPosition: '0 0, 10px 10px' }}
></div>
</div>
{/* Properties Panel */}
<div className="w-64 bg-white border-l border-gray-200 flex flex-col">
<div className="px-4 py-2 border-b border-gray-200">
<h2 className="font-medium">Properties</h2>
</div>
<div className="flex-1 overflow-y-auto p-4">
{selectedLayer && (
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Name</label>
<input
type="text"
value={selectedLayer.name}
onChange={(e) => {
const updated = layers.map(l =>
l.id === selectedLayer.id ? {...l, name: e.target.value} : l
);
setLayers(updated);
setSelectedLayer({...selectedLayer, name: e.target.value});
}}
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Position</label>
<div className="grid grid-cols-2 gap-2">
<div>
<label className="text-xs text-gray-500">X</label>
<input
type="number"
value={0} // Would be dynamic
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500"
/>
</div>
<div>
<label className="text-xs text-gray-500">Y</label>
<input
type="number"
value={0} // Would be dynamic
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500"
/>
</div>
</div>
</div>
</div>
)}
</div>
</div>
</div>
{/* Timeline */}
<div className="h-24 bg-white border-t border-gray-200 flex flex-col">
<div className="px-4 py-2 border-b border-gray-200 flex items-center justify-between">
<div className="flex items-center space-x-4">
<button onClick={togglePlayback} className="text-gray-700 hover:text-indigo-600">
<i data-feather={isPlaying ? 'pause' : 'play'} className="w-4 h-4"></i>
</button>
<span className="text-sm font-mono">{currentFrame} / 60</span>
</div>
<div className="flex items-center space-x-2">
<button className="text-gray-700 hover:text-indigo-600">
<i data-feather="plus" className="w-4 h-4"></i>
</button>
<button className="text-gray-700 hover:text-indigo-600">
<i data-feather="minus" className="w-4 h-4"></i>
</button>
</div>
</div>
<div className="flex-1 overflow-x-auto px-4 py-2">
<div className="h-full w-full min-w-max bg-gray-50 rounded flex items-center">
{/* Keyframes would render here */}
<div className="h-4 w-4 bg-indigo-600 rounded-full"></div>
</div>
</div>
</div>
</div>
);
}
// Render the app with error boundary
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('App Error:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<div className="p-8 text-center">
<h2 className="text-xl font-bold text-red-600 mb-2">Application Error</h2>
<p className="text-gray-700 mb-4">Something went wrong. Please refresh the page.</p>
<button
onClick={() => window.location.reload()}
className="px-4 py-2 bg-indigo-600 text-white rounded hover:bg-indigo-700"
>
Refresh Page
</button>
</div>
);
}
return this.props.children;
}
}
// Check if required libraries are loaded
if (!window.React || !window.ReactDOM || !window.SVG) {
document.getElementById('root').innerHTML = `
<div class="p-8 text-center">
<h2 class="text-xl font-bold text-red-600 mb-2">Missing Dependencies</h2>
<p class="text-gray-700">Required libraries failed to load. Please check your internet connection.</p>
</div>
`;
} else {
const root = createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<ErrorBoundary>
<App />
</ErrorBoundary>
</React.StrictMode>
);
}
// Initialize feather icons after render with retry logic
function initializeFeather() {
if (window.feather) {
try {
feather.replace();
} catch (error) {
console.error('Feather Icons Error:', error);
setTimeout(initializeFeather, 500);
}
} else {
setTimeout(initializeFeather, 500);
}
}
document.addEventListener('DOMContentLoaded', initializeFeather);
</script>
</body>
</html>