AI_Tools_Platform / main_ui.py
ik's picture
Upload folder using huggingface_hub
12ddc9e verified
import os
import json
import importlib
import requests
import gradio as gr
import subprocess
class Tool:
def __init__(self, name, description, ui_class, icon="🔧", version="0.1", ui_module=None):
self.name = name
self.description = description
self.ui_class = ui_class
self.icon = icon
self.version = version
self.ui_module = ui_module
class MainUI:
def __init__(self):
# Load installed tools from JSON
self.tools = {}
self.load_installed_tools()
# Marketplace server URL
self.marketplace_url = "https://marketplace.vidplus.app/api/marketplace"
self.version = "0.1"
# Create tools directory if it doesn't exist
os.makedirs('tools', exist_ok=True)
def load_installed_tools(self):
"""Load installed tools from JSON file"""
try:
if os.path.exists('installed_tools.json'):
with open('installed_tools.json', 'r') as f:
data = json.load(f)
for tool_data in data.get('tools', []):
# If ui_module is specified, dynamically import it
if tool_data.get('ui_module'):
try:
module = importlib.import_module(tool_data['ui_module'])
ui_class = getattr(module, tool_data['ui_class'])
self.tools[tool_data['id']] = Tool(
name=tool_data['name'],
description=tool_data['description'],
ui_class=ui_class,
icon=tool_data['icon'],
version=tool_data['version'],
ui_module=tool_data['ui_module']
)
except (ImportError, AttributeError) as e:
print(f"Error loading tool {tool_data['name']}: {e}")
except Exception as e:
print(f"Error loading installed tools: {e}")
# Fallback to default BeatSyncer tool
# self.tools = {
# "beatsyncer": Tool(
# name="BeatSyncer",
# description="Create amazing videos synchronized with music beats",
# ui_class=BeatSyncerUI,
# icon="🎵"
# )
# }
def save_installed_tools(self):
"""Save installed tools to JSON file"""
data = {
"tools": [
{
"id": tool_id,
"name": tool.name,
"description": tool.description,
"icon": tool.icon,
"version": tool.version,
"ui_class": tool.ui_class.__name__,
"ui_module": tool.ui_module
}
for tool_id, tool in self.tools.items()
]
}
with open('installed_tools.json', 'w') as f:
json.dump(data, f, indent=4)
def fetch_marketplace_tools(self):
"""Fetch available tools from marketplace server"""
try:
response = requests.get(f"{self.marketplace_url}/tools")
if response.status_code == 200:
# Get the list of tools from the marketplace
marketplace_tools = response.json()
# Ensure we're working with the latest state of installed tools
# This is important after a tool has been removed
if os.path.exists('installed_tools.json'):
with open('installed_tools.json', 'r') as f:
data = json.load(f)
installed_tool_ids = [tool['id'] for tool in data.get('tools', [])]
else:
installed_tool_ids = []
# Update the is_installed flag for each tool
for tool in marketplace_tools:
tool['is_installed'] = tool['id'] in installed_tool_ids
return marketplace_tools
else:
return []
except Exception as e:
print(f"Error fetching marketplace tools: {e}")
return []
def fetch_tool_details(self, tool_id):
"""Fetch details for a specific tool from marketplace server"""
try:
response = requests.get(f"{self.marketplace_url}/tools/{tool_id}")
if response.status_code == 200:
return response.json()
else:
return None
except Exception as e:
print(f"Error fetching tool details: {e}")
return None
def install_tool(self, tool_id):
"""Install a tool from the marketplace"""
try:
# Import required modules
import importlib
import importlib.util
import sys
import subprocess
import gradio as gr
# Fetch tool data
response = requests.get(f"{self.marketplace_url}/tools/{tool_id}/download")
if response.status_code != 200:
return f"Error: Failed to download tool (Status code: {response.status_code})"
tool_data = response.json()
tool_info = tool_data['tool']
tool_files = tool_data['files']
# Check and install dependencies if they exist
dependencies = tool_info.get('dependencies', [])
if dependencies:
try:
# Read current requirements.txt
current_requirements = []
if os.path.exists('requirements.txt'):
with open('requirements.txt', 'r') as f:
current_requirements = [line.strip() for line in f.readlines() if line.strip()]
# Check which dependencies need to be installed
to_install = []
to_add_to_requirements = []
for dep in dependencies:
# Extract just the package name (ignore version requirements)
pkg_name = dep.split('>=')[0].split('==')[0].split('>')[0].split('<')[0].strip()
# Check if dependency is already in requirements.txt
is_in_requirements = False
for req in current_requirements:
req_name = req.split('>=')[0].split('==')[0].split('>')[0].split('<')[0].strip()
if req_name == pkg_name:
is_in_requirements = True
break
if not is_in_requirements:
to_add_to_requirements.append(dep)
try:
# Try to import the package to check if it's installed
importlib.import_module(pkg_name)
except ImportError:
# Package not installed, add to installation list
to_install.append(dep)
# Install missing dependencies
if to_install:
for dep in to_install:
# Use subprocess to run pip install
subprocess.check_call([sys.executable, "-m", "pip", "install", dep])
# Append new dependencies to requirements.txt
if to_add_to_requirements:
with open('requirements.txt', 'a') as f:
for dep in to_add_to_requirements:
f.write(f"\n{dep}")
print(f"Added {len(to_add_to_requirements)} dependencies to requirements.txt")
except Exception as e:
return f"Error installing dependencies: {e}"
# Create tool directory
tool_dir = os.path.join('tools', tool_id)
os.makedirs(tool_dir, exist_ok=True)
# Write tool files
for file_name, file_content in tool_files.items():
# Rename files to match our convention
target_file_name = file_name
if file_name.endswith('_tool_logic.py'):
target_file_name = f"{tool_id}_tool.py"
elif file_name.endswith('_tool_ui.py'):
target_file_name = f"{tool_id}_ui.py"
with open(os.path.join(tool_dir, target_file_name), 'w') as f:
f.write(file_content)
# Create __init__.py to make the directory a package
with open(os.path.join(tool_dir, '__init__.py'), 'w') as f:
f.write(f"# {tool_info['name']} package\n")
# Add tool to installed tools
ui_module = f"tools.{tool_id}.{tool_id}_ui"
ui_class = f"{tool_id.title().replace('_', '')}UI"
# Update installed_tools.json
with open('installed_tools.json', 'r') as f:
data = json.load(f)
data['tools'].append({
"id": tool_id,
"name": tool_info['name'],
"description": tool_info['description'],
"icon": tool_info['icon'],
"version": tool_info['version'],
"ui_class": ui_class,
"ui_module": ui_module
})
with open('installed_tools.json', 'w') as f:
json.dump(data, f, indent=4)
# Dynamically load the newly installed tool
try:
# Reload the module if it was already imported
if ui_module in sys.modules:
importlib.reload(sys.modules[ui_module])
# Import the module
module = importlib.import_module(ui_module)
ui_class_obj = getattr(module, ui_class)
# Add the tool to self.tools
self.tools[tool_id] = Tool(
name=tool_info['name'],
description=tool_info['description'],
ui_class=ui_class_obj,
icon=tool_info['icon'],
version=tool_info['version'],
ui_module=ui_module
)
# Flag to indicate this is a newly installed tool that needs special handling
self.tools[tool_id].newly_installed = True
# Return success message
return f"Successfully installed {tool_info['name']}. The tool is now available in the Tools page."
except Exception as e:
print(f"Error dynamically loading tool: {e}")
import traceback
traceback.print_exc()
# Still return success but with a restart message
return f"Successfully installed {tool_info['name']}. Please restart the application to use the new tool."
except Exception as e:
return f"Error installing tool: {e}"
def remove_tool(self, tool_id):
"""Remove an installed tool"""
try:
if tool_id not in self.tools:
return "Error: Tool is not installed"
# Get tool info before removal
tool_name = self.tools[tool_id].name
# Remove tool directory
tool_dir = os.path.join('tools', tool_id)
if os.path.exists(tool_dir):
import shutil
shutil.rmtree(tool_dir)
print(f"Removed tool directory: {tool_dir}")
# Remove tool from installed_tools.json
if os.path.exists('installed_tools.json'):
with open('installed_tools.json', 'r') as f:
data = json.load(f)
# Filter out the tool to be removed
data['tools'] = [tool for tool in data['tools'] if tool['id'] != tool_id]
# Save updated tools list
with open('installed_tools.json', 'w') as f:
json.dump(data, f, indent=4)
print(f"Removed tool from installed_tools.json: {tool_id}")
# Remove tool from self.tools
del self.tools[tool_id]
return f"✅ Successfully removed {tool_name}"
except Exception as e:
print(f"Error removing tool: {e}")
import traceback
traceback.print_exc()
return f"Error removing tool: {str(e)}"
def create_tool_card(self, name):
return f"""
<div style="
background: #E6E6E6;
border-radius: 16px;
width: 200px;
height: 200px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: transform 0.2s;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
&:hover {{
transform: scale(1.02);
}}">
<span style="color: #1a1a1a; font-size: 16px; font-weight: 500;">{name}</span>
</div>
"""
def create_ui(self):
css = """
:root {
--sidebar-width: 200px;
}
body {
margin: 0;
padding: 0;
overflow: hidden;
}
#sidebar {
background-color: #0A192F;
color: white;
padding: 32px 24px;
height: 100vh;
width: var(--sidebar-width);
position: fixed;
left: 0;
top: 0;
display: flex;
flex-direction: column;
box-sizing: border-box;
z-index: 1000;
}
#main-content {
background-color: #0F172A;
min-height: 100vh;
margin-left: var(--sidebar-width);
padding: 50px;
box-sizing: border-box;
overflow-y: auto;
position: relative;
max-height: 100vh; /* Ensure content is scrollable */
}
.nav-button {
background: none !important;
border: none !important;
box-shadow: none !important;
color: #94A3B8 !important;
padding: 8px 0 !important;
margin: 4px 0 !important;
min-width: unset !important;
width: 100% !important;
text-align: left !important;
font-size: 16px !important;
font-weight: 500 !important;
height: auto !important;
line-height: 1.2 !important;
}
.nav-button:hover {
color: white !important;
}
.nav-button[variant="primary"] {
color: white !important;
}
.tool-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
padding: 24px 0;
width: 100%;
}
.version-text {
margin-top: auto;
color: #64748B;
font-size: 12px;
padding-top: 16px;
}
.title {
font-size: 32px;
font-weight: 800;
margin-bottom: 32px;
color: white;
letter-spacing: -0.5px;
}
.page-title {
font-size: 28px;
font-weight: 700;
color: white;
margin: 0;
padding: 0;
letter-spacing: -0.5px;
}
#content-wrapper {
width: 100%;
max-width: 100%;
padding: 24px;
}
.tool-card {
background: #1E293B !important;
border: none !important;
border-radius: 16px !important;
overflow: hidden !important;
transition: transform 0.2s !important;
box-shadow: 0 4px 6px rgba(0,0,0,0.1) !important;
height: 100% !important;
display: flex !important;
flex-direction: column !important;
width: auto !important;
min-height: 200px !important;
justify-content: flex-start !important;
align-items: stretch !important;
padding: 0 !important;
color: white !important;
text-align: left !important;
cursor: pointer !important;
}
.tool-card:hover {
transform: scale(1.02) !important;
}
.tool-card-img {
width: 100%;
height: 150px;
background: #2D3748;
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
color: #38BDF8;
}
.tool-card-content {
padding: 16px !important;
flex-grow: 1 !important;
display: flex !important;
flex-direction: column !important;
}
.tool-card-title {
font-size: 18px !important;
font-weight: 600 !important;
color: white !important;
margin: 0 0 8px 0 !important;
}
.tool-card-description {
font-size: 14px !important;
color: #94A3B8 !important;
margin: 0 !important;
flex-grow: 1 !important;
}
.installed-indicator {
background-color: #10B981 !important;
color: white !important;
font-size: 12px !important;
font-weight: 600 !important;
padding: 4px 8px !important;
border-radius: 4px !important;
margin-top: 8px !important;
display: inline-block !important;
}
.tool-card.installed {
border: 2px solid #10B981 !important;
}
.add-tool-card {
background: rgba(255, 255, 255, 0.05) !important;
border: 2px dashed rgba(255, 255, 255, 0.2) !important;
color: rgba(255, 255, 255, 0.8) !important;
display: flex !important;
flex-direction: column !important;
justify-content: center !important;
align-items: center !important;
text-align: center !important;
padding: 16px !important;
}
.add-tool-icon {
font-size: 32px;
margin-bottom: 16px;
color: rgba(255, 255, 255, 0.5);
}
.tool-card-button {
background: transparent !important;
border: none !important;
padding: 0 !important;
margin: 0 !important;
width: 100% !important;
height: auto !important;
min-height: 0 !important;
box-shadow: none !important;
}
.tool-details {
width: 100%;
max-width: 100%;
padding: 0;
}
.tool-details-container {
background: #0F172A;
border-radius: 0;
padding: 32px;
margin-bottom: 0;
max-height: 100vh;
overflow-y: auto;
width: 100%;
}
.tool-details-header {
display: flex;
align-items: center;
margin-bottom: 24px;
justify-content: space-between;
}
.tool-details-title-section {
display: flex;
align-items: center;
}
.tool-details-icon {
font-size: 28px;
margin-right: 16px;
}
.tool-details-title {
font-size: 28px;
font-weight: 700;
color: white;
margin: 0;
}
.tool-details-version {
font-size: 14px;
color: #94A3B8;
margin-left: 8px;
}
.tool-details-description {
font-size: 16px;
color: #E2E8F0;
margin-bottom: 32px;
max-width: 800px;
}
.tool-details-section-title {
font-size: 18px;
font-weight: 600;
color: white;
margin: 32px 0 16px 0;
}
.tool-details-features {
display: flex;
flex-direction: column;
gap: 12px;
margin-bottom: 32px;
}
.tool-details-feature {
display: flex;
align-items: center;
margin-bottom: 0;
}
.tool-details-feature-icon {
color: #38BDF8;
margin-right: 12px;
font-size: 16px;
}
.tool-details-feature-text {
color: #E2E8F0;
font-size: 16px;
}
.tool-details-screenshots {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 16px;
margin-top: 16px;
}
.tool-details-screenshot {
width: 100%;
border-radius: 8px;
overflow: hidden;
}
.tool-details-video {
width: 100%;
aspect-ratio: 16/9;
border-radius: 8px;
overflow: hidden;
margin-top: 16px;
background: #1E293B;
border: none;
max-width: 800px;
height: 450px;
}
.back-button {
background: transparent !important;
border: none !important;
color: #94A3B8 !important;
font-size: 14px !important;
padding: 8px 16px !important;
border-radius: 8px !important;
cursor: pointer !important;
transition: background-color 0.2s !important;
text-align: left !important;
width: auto !important;
margin-bottom: 24px !important;
}
.back-button:hover {
background: rgba(255, 255, 255, 0.1) !important;
}
.install-button {
background: #38BDF8 !important;
color: #0F172A !important;
font-weight: 600 !important;
padding: 12px 24px !important;
border-radius: 8px !important;
margin-top: 24px !important;
width: 100% !important;
}
.install-button:hover {
background: #0EA5E9 !important;
}
.remove-button {
background: #EF4444 !important;
color: white !important;
font-weight: 600 !important;
padding: 12px 24px !important;
border-radius: 8px !important;
margin-top: 24px !important;
width: 100% !important;
border: none !important;
cursor: pointer !important;
transition: background-color 0.2s !important;
}
.remove-button:hover {
background: #DC2626 !important;
}
.tool-details-action .remove-button {
margin-top: 0 !important;
padding: 8px 16px !important;
font-size: 14px !important;
width: auto !important;
}
/* Confirmation dialog styling */
.confirmation-dialog {
background-color: #1E293B;
border-radius: 8px;
padding: 24px;
margin-bottom: 24px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
border: 1px solid #2D3748;
}
.confirmation-title {
font-size: 18px;
font-weight: 600;
color: white;
margin-bottom: 16px;
}
.confirmation-message {
font-size: 14px;
color: #E2E8F0;
margin-bottom: 24px;
}
.confirmation-buttons {
display: flex;
justify-content: flex-end;
gap: 12px;
}
.cancel-button {
background: #4B5563 !important;
color: white !important;
font-weight: 600 !important;
padding: 8px 16px !important;
border-radius: 8px !important;
border: none !important;
cursor: pointer !important;
transition: background-color 0.2s !important;
}
.cancel-button:hover {
background: #374151 !important;
}
.confirm-button {
background: #EF4444 !important;
color: white !important;
font-weight: 600 !important;
padding: 8px 16px !important;
border-radius: 8px !important;
border: none !important;
cursor: pointer !important;
transition: background-color 0.2s !important;
}
.confirm-button:hover {
background: #DC2626 !important;
}
/* Modal styling */
#installation-modal {
max-width: 500px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
#installation-status {
font-size: 18px;
font-weight: 600;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #e5e7eb;
}
#installation-result {
font-size: 14px;
margin-bottom: 20px;
white-space: pre-wrap;
max-height: 300px;
overflow-y: auto;
}
#close-modal-btn {
background-color: #6B7280;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
transition: background-color 0.2s;
margin-top: 12px;
}
#close-modal-btn:hover {
background-color: #4B5563;
}
/* Installation result styling */
#installation-result-container {
margin-bottom: 20px;
border: 1px solid #2D3748;
border-radius: 8px;
padding: 16px;
background-color: #1E293B;
max-width: 100%;
position: relative;
z-index: 100;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
margin-top: 0;
}
#installation-status {
font-size: 18px;
font-weight: 600;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #2D3748;
}
#installation-result {
font-size: 14px;
margin-bottom: 20px;
white-space: pre-wrap;
max-height: 300px;
overflow-y: auto;
}
#close-result-btn {
background-color: #6B7280;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
transition: background-color 0.2s;
margin-top: 12px;
}
#close-result-btn:hover {
background-color: #4B5563;
}
.tool-details-action {
display: flex;
align-items: center;
}
.tool-details-action .install-button {
margin-top: 0 !important;
padding: 8px 16px !important;
font-size: 14px !important;
width: auto !important;
}
"""
with gr.Blocks(theme=gr.themes.Default(), css=css) as app:
# Define state variables
marketplace_tools_state = gr.State([])
current_tool_details_state = gr.State(None)
# Define event handlers first, before any references to them
def show_tools():
# Create updates to hide all tool containers
tool_container_updates = [gr.update(visible=False) for _ in tool_containers]
# Generate fresh HTML for tools grid based on current tools
tools_html = ''
for tool_id, tool in self.tools.items():
# Create HTML for each tool card
tools_html += f'''
<div class="tool-card" id="tool-card-{tool_id}" onclick="document.getElementById('tool-btn-{tool_id}').click()">
<div class="tool-card-img">
{tool.icon}
</div>
<div class="tool-card-content">
<h3 class="tool-card-title">{tool.name}</h3>
<p class="tool-card-description">{tool.description if hasattr(tool, 'description') else ""}</p>
</div>
</div>
'''
# Add "Add New Tool" button
tools_html += '''
<div class="tool-card add-tool-card" onclick="document.getElementById('add-tool-btn').click()">
<div class="add-tool-icon">+</div>
<div>Add New Tool from Marketplace</div>
</div>
'''
# Create HTML update for tools grid
tools_html_component = gr.HTML(f'<div class="tool-grid">{tools_html}</div>')
return [
gr.update(visible=True), # tools_content
gr.update(visible=False), # marketplace_content
gr.update(visible=False), # settings_content
gr.update(visible=False), # tool_interfaces
gr.update(visible=False), # tool_details_content
gr.update(variant="primary"), # tools_btn
gr.update(variant="secondary"), # marketplace_btn
gr.update(variant="secondary"), # settings_btn,
tools_html_component, # tools_html_component
gr.update(value="") # tool_interfaces_html
] + tool_container_updates
def show_marketplace():
# Fetch marketplace tools
tools = self.fetch_marketplace_tools()
# Create HTML for marketplace grid
html = '<div class="tool-grid">'
for i, tool in enumerate(tools):
# Use a unique ID for each card based on the tool ID
# Add a class to indicate if the tool is installed
installed_class = "installed" if tool.get('is_installed', False) else ""
# Add an indicator for installed tools
installed_indicator = '<div class="installed-indicator">✓ Installed</div>' if tool.get('is_installed', False) else ''
html += f'''
<div class="tool-card {installed_class}" id="card-{tool['id']}" onclick="document.getElementById('marketplace-tool-btn-{i}').click()">
<div class="tool-card-img">
{tool['icon']}
</div>
<div class="tool-card-content">
<h3 class="tool-card-title">{tool['name']}</h3>
<p class="tool-card-description">{tool['description']}</p>
{installed_indicator}
</div>
</div>
'''
html += '</div>'
# Hide installation result container and confirmation dialog
installation_result_container_update = gr.update(visible=False)
confirmation_dialog_update = gr.update(visible=False)
# Create updates for all tool containers
tool_container_updates = [gr.update(visible=False) for _ in tool_containers]
# Update marketplace_tools_state with the latest tools
# This ensures the marketplace shows the current state of available tools
return [
gr.update(visible=False), # tools_content
gr.update(visible=True), # marketplace_content
gr.update(visible=False), # settings_content
gr.update(visible=False), # tool_interfaces
gr.update(visible=False), # tool_details_content
gr.update(variant="secondary"), # tools_btn
gr.update(variant="primary"), # marketplace_btn
gr.update(variant="secondary"), # settings_btn
tools, # marketplace_tools_state - updated with latest tools
html, # marketplace_html
installation_result_container_update, # installation_result_container
confirmation_dialog_update, # confirmation_dialog
gr.update(value="") # tool_interfaces_html
] + tool_container_updates
def show_settings():
# Create updates to hide all tool containers
tool_container_updates = [gr.update(visible=False) for _ in tool_containers]
return [
gr.update(visible=False), # tools_content
gr.update(visible=False), # marketplace_content
gr.update(visible=True), # settings_content
gr.update(visible=False), # tool_interfaces
gr.update(visible=False), # tool_details_content
gr.update(variant="secondary"), # tools_btn
gr.update(variant="secondary"), # marketplace_btn
gr.update(variant="primary"), # settings_btn
gr.update(value="") # tool_interfaces_html
] + tool_container_updates
def show_tool(tool_id=None):
# Import gradio at the beginning of the function
import gradio as gr
# Check if the tool exists
if tool_id not in self.tools:
print(f"Tool {tool_id} not found")
return show_tools()
# Get the tool
tool = self.tools[tool_id]
# Check if this is a newly installed tool that needs special handling
if hasattr(tool, 'newly_installed') and tool.newly_installed:
print(f"Dynamically creating UI for newly installed tool: {tool_id}")
try:
# Create a message to inform the user that we're loading the tool
loading_message = f"""
<div style="text-align: center; padding: 20px;">
<h2>Loading {tool.name}...</h2>
<p>Please wait while we set up the tool interface.</p>
</div>
"""
# First, return a loading message
loading_updates = [
gr.update(visible=False), # tools_content
gr.update(visible=False), # marketplace_content
gr.update(visible=False), # settings_content
gr.update(visible=True), # tool_interfaces
gr.update(visible=False), # tool_details_content
gr.update(variant="secondary"), # tools_btn
gr.update(variant="secondary"), # marketplace_btn
gr.update(variant="secondary"), # settings_btn
gr.update(value=loading_message) # tool_interfaces_html
] + [gr.update(visible=False) for _ in tool_containers]
# Instead of trying to create the UI dynamically, which can cause issues with Gradio components,
# we'll create a simple HTML interface that provides basic functionality and instructions
# Get the tool class name and module for display
tool_class_name = tool.ui_class.__name__
tool_module_name = tool.ui_module
# Create a friendly HTML interface
tool_html = f"""
<div style="text-align: center; padding: 20px;">
<h2>{tool.name} is Ready!</h2>
<p>This tool has been successfully installed.</p>
<p>For the best experience with this tool, please restart the application.</p>
<p>After restarting, you'll be able to use all the features of this tool.</p>
<!--<div style="margin-top: 20px;">
<button onclick="window.location.reload()" style="background-color: #4CAF50; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px;">Refresh Page</button>
</div>-->
</div>
"""
# Remove the newly_installed flag so we don't try to create the UI again
delattr(tool, 'newly_installed')
# Return the HTML interface
return [
gr.update(visible=False), # tools_content
gr.update(visible=False), # marketplace_content
gr.update(visible=False), # settings_content
gr.update(visible=True), # tool_interfaces
gr.update(visible=False), # tool_details_content
gr.update(variant="secondary"), # tools_btn
gr.update(variant="secondary"), # marketplace_btn
gr.update(variant="secondary"), # settings_btn
gr.update(value=tool_html) # tool_interfaces_html
] + [gr.update(visible=False) for _ in tool_containers]
except Exception as e:
print(f"Error creating UI for newly installed tool: {e}")
import traceback
traceback.print_exc()
# If there's an error, show a message asking the user to restart
restart_message = f"""
<div style="text-align: center; padding: 20px;">
<h2>Error Loading {tool.name}</h2>
<p>There was an error setting up the tool interface:</p>
<pre style="text-align: left; background: #f0f0f0; padding: 10px; border-radius: 5px; overflow: auto;">{str(e)}</pre>
<p>Please restart the application to use this tool.</p>
<div style="margin-top: 20px;">
<button onclick="window.location.reload()" style="background-color: #4CAF50; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px;">Refresh Page</button>
</div>
</div>
"""
return [
gr.update(visible=False), # tools_content
gr.update(visible=False), # marketplace_content
gr.update(visible=False), # settings_content
gr.update(visible=True), # tool_interfaces
gr.update(visible=False), # tool_details_content
gr.update(variant="secondary"), # tools_btn
gr.update(variant="secondary"), # marketplace_btn
gr.update(variant="secondary"), # settings_btn
gr.update(value=restart_message) # tool_interfaces_html
] + [gr.update(visible=False) for _ in tool_containers]
# For existing tools with proper UI containers
# Create a list of visibility updates for all tool containers
updates = []
for i, container in enumerate(tool_containers):
# Get the tool_id for this container
container_tool_id = None
for tid, t in self.tools.items():
if hasattr(t, 'ui_container') and t.ui_container == container:
container_tool_id = tid
break
# Set visibility based on whether this is the selected tool
if container_tool_id == tool_id:
updates.append(gr.update(visible=True))
else:
updates.append(gr.update(visible=False))
return [
gr.update(visible=False), # tools_content
gr.update(visible=False), # marketplace_content
gr.update(visible=False), # settings_content
gr.update(visible=True), # tool_interfaces
gr.update(visible=False), # tool_details_content
gr.update(variant="secondary"), # tools_btn
gr.update(variant="secondary"), # marketplace_btn
gr.update(variant="secondary"), # settings_btn
gr.update(value="") # tool_interfaces_html
] + updates
def show_tool_details(tool_id):
# Fetch tool details
tool = self.fetch_tool_details(tool_id)
# Create updates to hide all tool containers
tool_container_updates = [gr.update(visible=False) for _ in tool_containers]
if not tool:
return [
gr.update(visible=False), # tools_content
gr.update(visible=True), # marketplace_content
gr.update(visible=False), # settings_content
gr.update(visible=False), # tool_interfaces
gr.update(visible=False), # tool_details_content
gr.update(variant="secondary"), # tools_btn
gr.update(variant="primary"), # marketplace_btn
gr.update(variant="secondary"), # settings_btn
None, # current_tool_details_state
"", # tool_details_html
gr.update(visible=False), # installation_result_container
gr.update(visible=False), # confirmation_dialog
gr.update(value="") # tool_interfaces_html
] + tool_container_updates
# Create features HTML
features_html = "".join([
f'<div class="tool-details-feature"><span class="tool-details-feature-icon">✓</span><span class="tool-details-feature-text">{feature}</span></div>'
for feature in tool.get('features', [])
])
# Create demo video HTML
demo_video_html = f'<iframe class="tool-details-video" src="{tool.get("demo_video", "https://www.youtube.com/embed/dQw4w9WgXcQ")}" frameborder="0" allowfullscreen></iframe>' if True else ''
# Check if the tool is already installed
# Use the is_installed flag if available, otherwise check self.tools
is_installed = tool.get('is_installed', False) or tool_id in self.tools
# Determine which button to show based on installation status
if is_installed:
action_button = f'<button id="remove-tool-btn-inline" class="remove-button" onclick="document.getElementById(\'remove-btn\').click()">Remove Tool</button>'
else:
action_button = f'<button id="add-tool-btn-inline" class="install-button" onclick="document.getElementById(\'install-btn\').click()">Add Tool</button>'
# Create HTML for tool details
html = f'''
<div class="tool-details-container">
<div class="tool-details">
<div class="tool-details-header">
<div class="tool-details-title-section">
<div class="tool-details-icon">{tool['icon']}</div>
<h2 class="tool-details-title">{tool['name']} <span class="tool-details-version">v{tool['version']}</span></h2>
</div>
<div class="tool-details-action">
{action_button}
</div>
</div>
<p class="tool-details-description">{tool['description']}</p>
<h3 class="tool-details-section-title">Features</h3>
<div class="tool-details-features">
{features_html}
</div>
<h3 class="tool-details-section-title">Video</h3>
{demo_video_html}
</div>
</div>
'''
print(f"Setting current_tool_details_state to: {tool}")
return [
gr.update(visible=False), # tools_content
gr.update(visible=False), # marketplace_content
gr.update(visible=False), # settings_content
gr.update(visible=False), # tool_interfaces
gr.update(visible=True), # tool_details_content
gr.update(variant="secondary"), # tools_btn
gr.update(variant="secondary"), # marketplace_btn
gr.update(variant="secondary"), # settings_btn
tool, # current_tool_details_state
html, # tool_details_html
gr.update(visible=False), # installation_result_container
gr.update(visible=False), # confirmation_dialog
gr.update(value="") # tool_interfaces_html
] + tool_container_updates
def install_selected_tool(tool_details):
print(f"Installing tool: {tool_details}")
if not tool_details:
print("No tool details provided")
return [
gr.update(visible=True), # installation_result_container
"⚠️ Installation Failed", # installation_status
"No tool selected to install." # installation_result
]
try:
# Install the tool
result = self.install_tool(tool_details['id'])
if "Error" in result:
return [
gr.update(visible=True), # installation_result_container
"⚠️ Installation Failed", # installation_status
result # installation_result
]
else:
# If installation was successful, we'll show a success message
# The user can click "Close" to see the installation result
# and then navigate to the tools page to see the new tool
return [
gr.update(visible=True), # installation_result_container
"✅ Installation Successful", # installation_status
result # installation_result
]
except Exception as e:
print(f"Error installing tool: {e}")
return [
gr.update(visible=True), # installation_result_container
"⚠️ Installation Failed", # installation_status
f"Error installing tool: {e}" # installation_result
]
# Create components after defining the functions
with gr.Row():
# Sidebar
with gr.Column(elem_id="sidebar", scale=1):
gr.Markdown("AI TOOLS", elem_classes="title")
tools_btn = gr.Button("Tools", elem_classes="nav-button", variant="primary")
marketplace_btn = gr.Button("Marketplace", elem_classes="nav-button", variant="secondary")
settings_btn = gr.Button("Settings", elem_classes="nav-button", variant="secondary")
gr.Markdown(f"version: {self.version}", elem_classes="version-text")
# Main content
with gr.Column(elem_id="main-content", scale=4):
# Define all content containers
tools_content = gr.Column(visible=True)
marketplace_content = gr.Column(visible=False)
settings_content = gr.Column(visible=False)
tool_interfaces = gr.Column(visible=False)
tool_details_content = gr.Column(visible=False)
# Fill tools content
with tools_content:
gr.Markdown("Tools", elem_classes="page-title")
tool_buttons = []
tool_ids = [] # Store tool_ids in the same order as tool_buttons
# Create HTML for all tools
tools_html = ''
for tool_id, tool in self.tools.items():
# Create HTML for each tool card
tools_html += f'''
<div class="tool-card" id="tool-card-{tool_id}" onclick="document.getElementById('tool-btn-{tool_id}').click()">
<div class="tool-card-img">
{tool.icon}
</div>
<div class="tool-card-content">
<h3 class="tool-card-title">{tool.name}</h3>
<p class="tool-card-description">{tool.description if hasattr(tool, 'description') else ""}</p>
</div>
</div>
'''
# Create hidden buttons for tool selection
btn = gr.Button(f"Select {tool.name}", visible=False, elem_id=f"tool-btn-{tool_id}")
tool_buttons.append(btn)
tool_ids.append(tool_id)
# Add "Add New Tool" button
tools_html += '''
<div class="tool-card add-tool-card" onclick="document.getElementById('add-tool-btn').click()">
<div class="add-tool-icon">+</div>
<div>Add New Tool from Marketplace</div>
</div>
'''
# Render the HTML inside a div with the tool-grid class
tools_html_component = gr.HTML(f'<div class="tool-grid">{tools_html}</div>')
# Create hidden add tool button
add_tool_btn = gr.Button("Add New Tool", visible=False, elem_id="add-tool-btn")
# Fill marketplace content
with marketplace_content:
gr.Markdown("Marketplace", elem_classes="page-title")
marketplace_html = gr.HTML("Loading marketplace tools...")
# Create hidden buttons for marketplace tool details with unique IDs
tool_buttons_container = gr.Column(visible=False)
# Create hidden buttons for marketplace tools
marketplace_tool_buttons = []
for i, tool in enumerate(self.fetch_marketplace_tools()):
tool_btn = gr.Button(f"View {tool['name']}", visible=False, elem_id=f"marketplace-tool-btn-{i}")
marketplace_tool_buttons.append((tool['id'], tool_btn))
# The click handlers for marketplace tool buttons will be set up after all components are defined
# Fill tool details content
with tool_details_content:
# Installation result section at the top
with gr.Column(elem_id="installation-result-container", elem_classes="installation-result-container", visible=False) as installation_result_container:
installation_status = gr.Markdown("Ready to install", elem_id="installation-status")
installation_result = gr.Markdown("Click the Install Tool button to install the selected tool.", elem_id="installation-result")
close_result_btn = gr.Button("Close", elem_id="close-result-btn")
# Confirmation dialog for tool removal
with gr.Column(elem_id="confirmation-dialog", elem_classes="confirmation-dialog", visible=False) as confirmation_dialog:
gr.Markdown("⚠️ Warning: Remove Tool", elem_id="confirmation-title", elem_classes="confirmation-title")
gr.Markdown(
"Are you sure you want to remove this tool? This action cannot be undone. "
"All data created by this tool will be permanently deleted.",
elem_id="confirmation-message",
elem_classes="confirmation-message"
)
with gr.Row(elem_classes="confirmation-buttons"):
cancel_btn = gr.Button("Cancel", elem_classes="cancel-button")
confirm_btn = gr.Button("Yes, Remove Tool", elem_classes="confirm-button")
with gr.Row():
with gr.Column(scale=1):
back_btn = gr.Button("← Back to Marketplace", elem_classes="back-button")
with gr.Column(scale=3):
gr.Markdown("", elem_classes="page-title")
# Tool details content
tool_details_html = gr.HTML("")
# Hidden install button (will be triggered by the inline button)
install_btn = gr.Button("Add Tool", elem_classes="install-button", elem_id="install-btn", variant="primary", visible=False)
# Hidden remove button (will be triggered by the inline button)
remove_btn = gr.Button("Remove Tool", elem_classes="remove-button", elem_id="remove-btn", variant="primary", visible=False)
# Add a direct event handler to the install button
def debug_install_click():
print("Install button clicked directly!")
return [gr.update(visible=True), gr.update(value="Install button clicked!")]
# Add a direct event handler to the remove button to show confirmation dialog
def show_confirmation_dialog():
print("Remove button clicked - showing confirmation dialog")
return gr.update(visible=True)
# Function to handle tool removal confirmation
def remove_selected_tool(tool_details):
print(f"Removing tool: {tool_details}")
if not tool_details:
print("No tool details provided")
return [
gr.update(visible=False), # confirmation_dialog
gr.update(visible=True), # installation_result_container
"⚠️ Removal Failed", # installation_status
"No tool selected to remove." # installation_result
]
try:
# Remove the tool
result = self.remove_tool(tool_details['id'])
if "Error" in result:
return [
gr.update(visible=False), # confirmation_dialog
gr.update(visible=True), # installation_result_container
"⚠️ Removal Failed", # installation_status
result # installation_result
]
else:
# After successful removal, show success message
# The user will need to click "Back to Marketplace" manually
# This avoids issues with dynamic UI updates
return [
gr.update(visible=False), # confirmation_dialog
gr.update(visible=True), # installation_result_container
"✅ Removal Successful", # installation_status
f"{result} - Click 'Back to Marketplace' to return. You may need to restart the application to see all changes." # installation_result
]
except Exception as e:
print(f"Error removing tool: {e}")
return [
gr.update(visible=False), # confirmation_dialog
gr.update(visible=True), # installation_result_container
"⚠️ Removal Failed", # installation_status
f"Error removing tool: {e}" # installation_result
]
# Function to cancel removal
def cancel_removal():
return gr.update(visible=False)
debug_output = gr.Textbox(label="Debug Output", visible=False)
# Set up event handlers for buttons
install_btn.click(
fn=install_selected_tool,
inputs=[current_tool_details_state],
outputs=[installation_result_container, installation_status, installation_result]
)
remove_btn.click(fn=show_confirmation_dialog, outputs=confirmation_dialog)
# Set up confirmation dialog buttons
cancel_btn.click(fn=cancel_removal, outputs=confirmation_dialog)
confirm_btn.click(
fn=remove_selected_tool,
inputs=[current_tool_details_state],
outputs=[confirmation_dialog, installation_result_container, installation_status, installation_result]
)
# Fill settings content
with settings_content:
gr.Markdown("Settings", elem_classes="page-title")
gr.Markdown("Settings options will appear here...")
# Fill tool interfaces
with tool_interfaces:
# Add an HTML component to display messages for newly installed tools
tool_interfaces_html = gr.HTML("", elem_id="tool-interfaces-html")
for tool_id, tool in self.tools.items():
with gr.Column(visible=False) as tool_container:
tool_ui = tool.ui_class()
tool_ui.create_ui()
# Store the UI container in the tool object for later reference
tool.ui_container = tool_container
# Set up event handlers after all components are defined
# Set up tool button events
# Collect all tool containers for outputs
tool_containers = []
for tool_id, tool in self.tools.items():
if hasattr(tool, 'ui_container'):
tool_containers.append(tool.ui_container)
# Set up click handlers for marketplace tool buttons
for i, (tool_id, tool_btn) in enumerate(marketplace_tool_buttons):
# We need to create a separate function for each button to avoid lambda closure issues
def make_handler(tid):
return lambda: show_tool_details(tid)
handler = make_handler(tool_id)
tool_btn.click(
fn=handler,
outputs=[
tools_content, marketplace_content, settings_content, tool_interfaces,
tool_details_content, tools_btn, marketplace_btn, settings_btn,
current_tool_details_state, tool_details_html, installation_result_container,
confirmation_dialog, tool_interfaces_html
] + tool_containers
)
for i, btn in enumerate(tool_buttons):
if isinstance(btn, gr.Button): # Check if it's a button from the tools grid
tool_id = tool_ids[i] # Get the corresponding tool_id
# We need to create a separate function for each button to avoid lambda closure issues
def make_handler(tid):
return lambda: show_tool(tid)
handler = make_handler(tool_id)
btn.click(
fn=handler,
outputs=[tools_content, marketplace_content, settings_content, tool_interfaces,
tool_details_content, tools_btn, marketplace_btn, settings_btn,
tool_interfaces_html] + tool_containers
)
# Set up navigation events
tools_btn.click(
fn=show_tools,
outputs=[tools_content, marketplace_content, settings_content, tool_interfaces,
tool_details_content, tools_btn, marketplace_btn, settings_btn,
tools_html_component, tool_interfaces_html] + tool_containers
)
marketplace_btn.click(
fn=show_marketplace,
outputs=[tools_content, marketplace_content, settings_content, tool_interfaces,
tool_details_content, tools_btn, marketplace_btn, settings_btn,
marketplace_tools_state, marketplace_html, installation_result_container,
confirmation_dialog, tool_interfaces_html] + tool_containers
)
settings_btn.click(
fn=show_settings,
outputs=[tools_content, marketplace_content, settings_content, tool_interfaces,
tool_details_content, tools_btn, marketplace_btn, settings_btn,
tool_interfaces_html] + tool_containers
)
# Set up add tool button to navigate to marketplace
add_tool_btn.click(
fn=show_marketplace,
outputs=[tools_content, marketplace_content, settings_content, tool_interfaces,
tool_details_content, tools_btn, marketplace_btn, settings_btn,
marketplace_tools_state, marketplace_html, installation_result_container,
confirmation_dialog, tool_interfaces_html] + tool_containers
)
# Set up event handlers for tool details
back_btn.click(
fn=show_marketplace,
outputs=[
tools_content, marketplace_content, settings_content, tool_interfaces,
tool_details_content, tools_btn, marketplace_btn, settings_btn,
marketplace_tools_state, marketplace_html, installation_result_container,
confirmation_dialog
] + tool_containers
)
# Define a function to handle closing the installation result and navigating to tools
def close_result_and_show_tools():
# First hide the installation result container
installation_result_update = gr.update(visible=False)
# Then get the updates from show_tools
tools_updates = show_tools()
# Combine the updates
return [installation_result_update] + tools_updates
# Set up close button for installation result
close_result_btn.click(
fn=close_result_and_show_tools,
outputs=[installation_result_container,
tools_content, marketplace_content, settings_content, tool_interfaces,
tool_details_content, tools_btn, marketplace_btn, settings_btn,
tools_html_component, tool_interfaces_html] + tool_containers
)
return app
if __name__ == "__main__":
ui = MainUI()
demo = ui.create_ui()
demo.queue() # Add queue for better handling of multiple requests
demo.title = "AI Tools Platform" # Add a title
demo.launch(
debug=True,
share=True,
server_name="0.0.0.0",
server_port=7860
)