GradioMake / index.html
ThorAILabs's picture
Update index.html
7d8f306 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gradio App Builder</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">
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.14.0/Sortable.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.15.3/ace.js"></script>
<style>
#editor {
height: 300px;
width: 100%;
}
.component-item {
transition: all 0.2s ease;
}
.component-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
.preview-container {
min-height: 300px;
background-color: #f8fafc;
border-radius: 0.5rem;
}
.gradio-component {
margin-bottom: 1rem;
padding: 0.5rem;
border-radius: 0.375rem;
background-color: white;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
.gradio-component:hover {
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
.gradio-component-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 0.5rem;
border-bottom: 1px solid #e2e8f0;
margin-bottom: 0.5rem;
}
</style>
</head>
<body class="bg-gray-50">
<div class="container mx-auto px-4 py-8">
<div class="flex justify-between items-center mb-8">
<h1 class="text-3xl font-bold text-gray-800">
<i class="fas fa-cubes mr-2 text-indigo-600"></i> Gradio App Builder
</h1>
<div class="flex space-x-2">
<button id="exportBtn" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 transition">
<i class="fas fa-file-export mr-2"></i> Export Python Code
</button>
<button id="clearBtn" class="px-4 py-2 bg-red-500 text-white rounded-md hover:bg-red-600 transition">
<i class="fas fa-trash-alt mr-2"></i> Clear All
</button>
</div>
</div>
<div class="bg-yellow-50 border-l-4 border-yellow-400 p-4 mb-6 rounded">
<div class="flex">
<div class="flex-shrink-0">
<i class="fas fa-exclamation-circle text-yellow-500"></i>
</div>
<div class="ml-3">
<p class="text-sm text-yellow-700">
<strong>Note:</strong> The generated code is a template that will need further enhancement to become a full app. You'll need to add your specific logic and functionality.
</p>
</div>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Components Panel -->
<div class="bg-white p-4 rounded-lg shadow">
<h2 class="text-xl font-semibold mb-4 text-gray-700">
<i class="fas fa-puzzle-piece mr-2 text-indigo-500"></i> Components
</h2>
<div class="grid grid-cols-2 gap-3" id="componentsList">
<div class="component-item p-3 bg-gray-50 rounded cursor-move border border-gray-200" data-type="textbox" data-label="Textbox">
<i class="fas fa-font text-indigo-500 mr-2"></i> Textbox
</div>
<div class="component-item p-3 bg-gray-50 rounded cursor-move border border-gray-200" data-type="number" data-label="Number Input">
<i class="fas fa-hashtag text-indigo-500 mr-2"></i> Number
</div>
<div class="component-item p-3 bg-gray-50 rounded cursor-move border border-gray-200" data-type="slider" data-label="Slider">
<i class="fas fa-sliders-h text-indigo-500 mr-2"></i> Slider
</div>
<div class="component-item p-3 bg-gray-50 rounded cursor-move border border-gray-200" data-type="checkbox" data-label="Checkbox">
<i class="far fa-check-square text-indigo-500 mr-2"></i> Checkbox
</div>
<div class="component-item p-3 bg-gray-50 rounded cursor-move border border-gray-200" data-type="dropdown" data-label="Dropdown">
<i class="fas fa-caret-down text-indigo-500 mr-2"></i> Dropdown
</div>
<div class="component-item p-3 bg-gray-50 rounded cursor-move border border-gray-200" data-type="radio" data-label="Radio Buttons">
<i class="far fa-dot-circle text-indigo-500 mr-2"></i> Radio
</div>
<div class="component-item p-3 bg-gray-50 rounded cursor-move border border-gray-200" data-type="button" data-label="Button">
<i class="fas fa-square text-indigo-500 mr-2"></i> Button
</div>
<div class="component-item p-3 bg-gray-50 rounded cursor-move border border-gray-200" data-type="image" data-label="Image">
<i class="far fa-image text-indigo-500 mr-2"></i> Image
</div>
<div class="component-item p-3 bg-gray-50 rounded cursor-move border border-gray-200" data-type="audio" data-label="Audio">
<i class="fas fa-volume-up text-indigo-500 mr-2"></i> Audio
</div>
<div class="component-item p-3 bg-gray-50 rounded cursor-move border border-gray-200" data-type="video" data-label="Video">
<i class="fas fa-video text-indigo-500 mr-2"></i> Video
</div>
<div class="component-item p-3 bg-gray-50 rounded cursor-move border border-gray-200" data-type="file" data-label="File">
<i class="fas fa-file-upload text-indigo-500 mr-2"></i> File
</div>
<div class="component-item p-3 bg-gray-50 rounded cursor-move border border-gray-200" data-type="dataframe" data-label="Dataframe">
<i class="fas fa-table text-indigo-500 mr-2"></i> Dataframe
</div>
</div>
</div>
<!-- Preview Panel -->
<div class="bg-white p-4 rounded-lg shadow">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold text-gray-700">
<i class="fas fa-eye mr-2 text-indigo-500"></i> Preview
</h2>
<button id="refreshPreview" class="px-3 py-1 bg-gray-100 text-gray-700 rounded hover:bg-gray-200 transition">
<i class="fas fa-sync-alt mr-1"></i> Refresh
</button>
</div>
<div class="preview-container p-4 border border-gray-200" id="previewArea">
<p class="text-gray-400 italic">Drag components here to build your Gradio app</p>
</div>
</div>
<!-- Properties Panel -->
<div class="bg-white p-4 rounded-lg shadow">
<h2 class="text-xl font-semibold mb-4 text-gray-700">
<i class="fas fa-cog mr-2 text-indigo-500"></i> Properties
</h2>
<div id="propertiesPanel" class="space-y-4">
<div class="text-center py-8 text-gray-400">
<i class="fas fa-mouse-pointer text-2xl mb-2"></i>
<p>Select a component to edit its properties</p>
</div>
</div>
</div>
</div>
<!-- Code Editor -->
<div class="mt-8 bg-white p-4 rounded-lg shadow">
<h2 class="text-xl font-semibold mb-4 text-gray-700">
<i class="fas fa-code mr-2 text-indigo-500"></i> Generated Python Code
</h2>
<div id="editor">import gradio as gr
def greet(name):
return "Hello " + name + "!"
# Define your function here that will process the inputs
def process_inputs(*args):
# Add your logic here
return "Processed result"
with gr.Blocks() as demo:
# Components will appear here
gr.Markdown("# My Gradio App")
# Add your components here
# Remember to connect your function to the interface
# demo.launch()
# Note: You'll need to add your specific processing logic
# and connect the components to your function</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Initialize code editor
const editor = ace.edit("editor");
editor.setTheme("ace/theme/chrome");
editor.session.setMode("ace/mode/python");
editor.setOptions({
fontSize: "14px",
highlightActiveLine: true,
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
});
// Make components draggable
new Sortable(document.getElementById('componentsList'), {
group: {
name: 'shared',
pull: 'clone',
put: false
},
sort: false,
animation: 150
});
// Make preview area droppable
new Sortable(document.getElementById('previewArea'), {
group: 'shared',
animation: 150,
onAdd: function(evt) {
const componentType = evt.item.dataset.type;
const componentLabel = evt.item.dataset.label;
// Remove the dragged element (it's a clone)
evt.item.remove();
// Create a new component in the preview area
addComponentToPreview(componentType, componentLabel);
// Update the code
updateGeneratedCode();
}
});
// Component counter
let componentCounter = 0;
let selectedComponent = null;
// Function to add a component to the preview area
function addComponentToPreview(type, label) {
componentCounter++;
const componentId = `component-${componentCounter}`;
const componentDiv = document.createElement('div');
componentDiv.className = 'gradio-component';
componentDiv.id = componentId;
componentDiv.dataset.type = type;
// Component header
const headerDiv = document.createElement('div');
headerDiv.className = 'gradio-component-header';
const titleSpan = document.createElement('span');
titleSpan.className = 'font-medium text-gray-700';
titleSpan.innerHTML = `<i class="fas fa-${getIconForType(type)} mr-2 text-indigo-500"></i> ${label}`;
const deleteBtn = document.createElement('button');
deleteBtn.className = 'text-red-500 hover:text-red-700';
deleteBtn.innerHTML = '<i class="fas fa-times"></i>';
deleteBtn.onclick = function() {
componentDiv.remove();
updateGeneratedCode();
};
headerDiv.appendChild(titleSpan);
headerDiv.appendChild(deleteBtn);
componentDiv.appendChild(headerDiv);
// Component content
const contentDiv = document.createElement('div');
contentDiv.className = 'component-content';
// Add different inputs based on component type
switch(type) {
case 'textbox':
contentDiv.innerHTML = `
<input type="text" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" placeholder="Enter text">
`;
break;
case 'number':
contentDiv.innerHTML = `
<input type="number" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" placeholder="Enter number">
`;
break;
case 'slider':
contentDiv.innerHTML = `
<input type="range" class="w-full" min="0" max="100" value="50">
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>0</span>
<span>100</span>
</div>
`;
break;
case 'checkbox':
contentDiv.innerHTML = `
<div class="flex items-center">
<input type="checkbox" class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded">
<label class="ml-2 block text-sm text-gray-700">Checkbox</label>
</div>
`;
break;
case 'dropdown':
contentDiv.innerHTML = `
<select class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md">
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
</select>
`;
break;
case 'radio':
contentDiv.innerHTML = `
<div class="space-y-2">
<div class="flex items-center">
<input type="radio" name="radio-group" class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300">
<label class="ml-2 block text-sm text-gray-700">Option 1</label>
</div>
<div class="flex items-center">
<input type="radio" name="radio-group" class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300">
<label class="ml-2 block text-sm text-gray-700">Option 2</label>
</div>
</div>
`;
break;
case 'button':
contentDiv.innerHTML = `
<button class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 transition">Button</button>
`;
break;
case 'image':
contentDiv.innerHTML = `
<div class="border-2 border-dashed border-gray-300 rounded-md p-4 text-center">
<i class="fas fa-image text-4xl text-gray-400 mb-2"></i>
<p class="text-sm text-gray-500">Image input will appear here</p>
</div>
`;
break;
case 'audio':
contentDiv.innerHTML = `
<div class="border-2 border-dashed border-gray-300 rounded-md p-4 text-center">
<i class="fas fa-volume-up text-4xl text-gray-400 mb-2"></i>
<p class="text-sm text-gray-500">Audio input will appear here</p>
</div>
`;
break;
case 'video':
contentDiv.innerHTML = `
<div class="border-2 border-dashed border-gray-300 rounded-md p-4 text-center">
<i class="fas fa-video text-4xl text-gray-400 mb-2"></i>
<p class="text-sm text-gray-500">Video input will appear here</p>
</div>
`;
break;
case 'file':
contentDiv.innerHTML = `
<div class="border-2 border-dashed border-gray-300 rounded-md p-4 text-center">
<i class="fas fa-file-upload text-4xl text-gray-400 mb-2"></i>
<p class="text-sm text-gray-500">File upload will appear here</p>
</div>
`;
break;
case 'dataframe':
contentDiv.innerHTML = `
<div class="border-2 border-dashed border-gray-300 rounded-md p-4 text-center">
<i class="fas fa-table text-4xl text-gray-400 mb-2"></i>
<p class="text-sm text-gray-500">Dataframe will appear here</p>
</div>
`;
break;
}
componentDiv.appendChild(contentDiv);
// Add click event to select component
componentDiv.onclick = function(e) {
if (e.target.tagName !== 'BUTTON' && e.target.tagName !== 'I') {
selectComponent(componentDiv);
}
};
// Add to preview area
const previewArea = document.getElementById('previewArea');
if (previewArea.firstChild && previewArea.firstChild.className === 'text-gray-400 italic') {
previewArea.innerHTML = '';
}
previewArea.appendChild(componentDiv);
// Select the new component
selectComponent(componentDiv);
}
// Function to get icon for component type
function getIconForType(type) {
const icons = {
'textbox': 'font',
'number': 'hashtag',
'slider': 'sliders-h',
'checkbox': 'check-square',
'dropdown': 'caret-down',
'radio': 'dot-circle',
'button': 'square',
'image': 'image',
'audio': 'volume-up',
'video': 'video',
'file': 'file-upload',
'dataframe': 'table'
};
return icons[type] || 'cube';
}
// Function to select a component
function selectComponent(component) {
// Deselect previous component
if (selectedComponent) {
selectedComponent.classList.remove('ring-2', 'ring-indigo-500');
}
// Select new component
selectedComponent = component;
component.classList.add('ring-2', 'ring-indigo-500');
// Update properties panel
updatePropertiesPanel(component.dataset.type);
}
// Function to update properties panel
function updatePropertiesPanel(type) {
const propertiesPanel = document.getElementById('propertiesPanel');
let propertiesHTML = `
<div>
<label class="block text-sm font-medium text-gray-700">Label</label>
<input type="text" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" value="${selectedComponent.querySelector('.gradio-component-header span').textContent.replace(/^\s+|\s+$/g, '')}">
</div>
`;
// Add type-specific properties
switch(type) {
case 'textbox':
propertiesHTML += `
<div>
<label class="block text-sm font-medium text-gray-700">Placeholder</label>
<input type="text" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" value="Enter text">
</div>
<div class="flex items-center">
<input type="checkbox" class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded">
<label class="ml-2 block text-sm text-gray-700">Multiline</label>
</div>
`;
break;
case 'number':
propertiesHTML += `
<div>
<label class="block text-sm font-medium text-gray-700">Minimum Value</label>
<input type="number" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" value="0">
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Maximum Value</label>
<input type="number" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" value="100">
</div>
`;
break;
case 'slider':
propertiesHTML += `
<div>
<label class="block text-sm font-medium text-gray-700">Minimum Value</label>
<input type="number" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" value="0">
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Maximum Value</label>
<input type="number" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" value="100">
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Step</label>
<input type="number" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" value="1">
</div>
`;
break;
case 'dropdown':
propertiesHTML += `
<div>
<label class="block text-sm font-medium text-gray-700">Options (comma separated)</label>
<input type="text" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" value="Option 1, Option 2, Option 3">
</div>
<div class="flex items-center">
<input type="checkbox" class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded">
<label class="ml-2 block text-sm text-gray-700">Multiple selection</label>
</div>
`;
break;
case 'radio':
propertiesHTML += `
<div>
<label class="block text-sm font-medium text-gray-700">Options (comma separated)</label>
<input type="text" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" value="Option 1, Option 2">
</div>
`;
break;
case 'button':
propertiesHTML += `
<div>
<label class="block text-sm font-medium text-gray-700">Variant</label>
<select class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
<option>Primary</option>
<option>Secondary</option>
<option>Success</option>
<option>Danger</option>
</select>
</div>
`;
break;
}
propertiesPanel.innerHTML = propertiesHTML;
// Add event listeners to property inputs
const labelInput = propertiesPanel.querySelector('input[type="text"]');
if (labelInput) {
labelInput.addEventListener('change', function() {
selectedComponent.querySelector('.gradio-component-header span').textContent = this.value;
updateGeneratedCode();
});
}
}
// Function to update the generated code
function updateGeneratedCode() {
const previewArea = document.getElementById('previewArea');
const components = previewArea.querySelectorAll('.gradio-component');
let code = `import gradio as gr
def process_inputs(*args):
# Add your processing logic here
return "Processed result"
with gr.Blocks() as demo:
gr.Markdown("# My Gradio App")\n`;
components.forEach(component => {
const type = component.dataset.type;
const label = component.querySelector('.gradio-component-header span').textContent.replace(/^\s+|\s+$/g, '');
const varName = label.toLowerCase().replace(/\s+/g, '_');
switch(type) {
case 'textbox':
code += ` ${varName} = gr.Textbox(label="${label}")\n`;
break;
case 'number':
code += ` ${varName} = gr.Number(label="${label}")\n`;
break;
case 'slider':
code += ` ${varName} = gr.Slider(minimum=0, maximum=100, label="${label}")\n`;
break;
case 'checkbox':
code += ` ${varName} = gr.Checkbox(label="${label}")\n`;
break;
case 'dropdown':
code += ` ${varName} = gr.Dropdown(choices=["Option 1", "Option 2", "Option 3"], label="${label}")\n`;
break;
case 'radio':
code += ` ${varName} = gr.Radio(choices=["Option 1", "Option 2"], label="${label}")\n`;
break;
case 'button':
code += ` ${varName} = gr.Button("${label}")\n`;
break;
case 'image':
code += ` ${varName} = gr.Image(label="${label}")\n`;
break;
case 'audio':
code += ` ${varName} = gr.Audio(label="${label}")\n`;
break;
case 'video':
code += ` ${varName} = gr.Video(label="${label}")\n`;
break;
case 'file':
code += ` ${varName} = gr.File(label="${label}")\n`;
break;
case 'dataframe':
code += ` ${varName} = gr.Dataframe(label="${label}")\n`;
break;
}
});
code += `\n # Connect your components to the processing function\n`;
code += ` # demo.launch()`;
editor.setValue(code);
}
// Export button
document.getElementById('exportBtn').addEventListener('click', function() {
const code = editor.getValue();
const blob = new Blob([code], {type: 'text/plain'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'gradio_app.py';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
// Clear button
document.getElementById('clearBtn').addEventListener('click', function() {
const previewArea = document.getElementById('previewArea');
previewArea.innerHTML = '<p class="text-gray-400 italic">Drag components here to build your Gradio app</p>';
updateGeneratedCode();
});
// Refresh preview button
document.getElementById('refreshPreview').addEventListener('click', function() {
updateGeneratedCode();
});
// Initial code
updateGeneratedCode();
});
</script>
</body>
</html>