Phase 17: Plugin marketplace and enhanced documentation

This commit is contained in:
truetype committed 2026-03-20 20:55:34 +01:00
1 parent cf8e4c677f
commit 1dae4c5656
4 files changed
+1270

No files matched your search

+906
View File
@@ -0,0 +1,906 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MaxClaw Agent - Marketplace</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
}
header {
text-align: center;
color: white;
margin-bottom: 30px;
}
header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
}
header p {
font-size: 1.1rem;
opacity: 0.9;
}
.language-selector {
position: absolute;
top: 20px;
right: 20px;
}
.language-selector select {
padding: 8px 12px;
border-radius: 6px;
border: 1px solid #ddd;
background: white;
font-size: 14px;
cursor: pointer;
}
.card {
background: white;
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
padding: 30px;
margin-bottom: 30px;
}
.card h2 {
color: #333;
margin-bottom: 20px;
border-bottom: 2px solid #667eea;
padding-bottom: 10px;
}
.search-container {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.search-input {
flex: 1;
min-width: 200px;
padding: 12px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 16px;
}
.search-btn {
background: #667eea;
color: white;
border: none;
padding: 12px 24px;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
font-weight: 600;
transition: background 0.3s;
}
.search-btn:hover {
background: #5a6fd8;
}
.stats-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 6px;
padding: 20px;
text-align: center;
}
.stat-value {
font-size: 2rem;
font-weight: 700;
color: #667eea;
margin-bottom: 5px;
}
.stat-label {
font-size: 0.9rem;
color: #666;
}
.plugins-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
}
.plugin-card {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 6px;
padding: 20px;
transition: transform 0.2s, box-shadow 0.2s;
}
.plugin-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.plugin-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 10px;
}
.plugin-name {
font-size: 1.2rem;
font-weight: 600;
color: #333;
}
.plugin-status {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
}
.status-installed {
background: #d4edda;
color: #155724;
}
.status-available {
background: #fff3cd;
color: #856404;
}
.plugin-description {
color: #666;
font-size: 14px;
margin-bottom: 15px;
line-height: 1.4;
}
.plugin-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.btn {
padding: 8px 16px;
border-radius: 6px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: background 0.3s;
border: none;
}
.btn-primary {
background: #667eea;
color: white;
}
.btn-primary:hover {
background: #5a6fd8;
}
.btn-secondary {
background: #6c757d;
color: white;
}
.btn-secondary:hover {
background: #5a6268;
}
.btn-danger {
background: #dc3545;
color: white;
}
.btn-danger:hover {
background: #c82333;
}
.btn-success {
background: #28a745;
color: white;
}
.btn-success:hover {
background: #218838;
}
.alert {
padding: 15px;
border-radius: 6px;
margin-bottom: 20px;
}
.alert-success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.alert-warning {
background: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.tabs {
display: flex;
border-bottom: 2px solid #dee2e6;
margin-bottom: 20px;
flex-wrap: wrap;
}
.tab {
padding: 10px 20px;
cursor: pointer;
border-bottom: 3px solid transparent;
margin-bottom: -2px;
flex: 1;
text-align: center;
min-width: 120px;
}
.tab.active {
border-bottom-color: #667eea;
font-weight: 600;
color: #667eea;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.hidden {
display: none;
}
/* Responsive Design */
@media (max-width: 768px) {
body {
padding: 10px;
}
.container {
padding: 0 10px;
}
header h1 {
font-size: 2rem;
}
header p {
font-size: 1rem;
}
.card {
padding: 20px;
margin-bottom: 20px;
}
.card h2 {
font-size: 1.3rem;
}
.search-container {
flex-direction: column;
}
.search-input {
min-width: 100%;
}
.search-btn {
width: 100%;
}
.stats-container {
grid-template-columns: 1fr;
}
.plugins-grid {
grid-template-columns: 1fr;
}
.plugin-card {
padding: 15px;
}
.plugin-actions {
flex-direction: column;
}
.btn {
width: 100%;
margin: 5px 0;
}
.tabs {
flex-direction: column;
}
.tab {
padding: 8px 15px;
border-bottom: none;
border-left: 3px solid transparent;
margin-bottom: 0;
text-align: left;
}
.tab.active {
border-bottom: none;
border-left-color: #667eea;
}
.language-selector {
position: static;
text-align: center;
margin-bottom: 20px;
}
}
@media (max-width: 480px) {
header h1 {
font-size: 1.8rem;
}
.card {
padding: 15px;
}
.stat-value {
font-size: 1.5rem;
}
}
/* Touch-friendly improvements */
@media (hover: none) and (pointer: coarse) {
.btn:hover {
background: inherit;
}
.btn:active {
transform: scale(0.98);
}
.plugin-card:hover {
transform: none;
}
.plugin-card:active {
transform: scale(0.98);
}
}
</style>
</head>
<body>
<div class="container">
<div class="language-selector">
<select id="language-select" onchange="changeLanguage(this.value)">
<option value="it">Italiano</option>
<option value="en">English</option>
</select>
</div>
<header>
<h1 id="title">MaxClaw Agent - Marketplace</h1>
<p id="subtitle">Scopri e installa plugin per estendere le funzionalità</p>
</header>
<div class="card">
<div class="tabs">
<div class="tab active" onclick="switchTab('marketplace')" id="tab-marketplace">Marketplace</div>
<div class="tab" onclick="switchTab('installed')" id="tab-installed">Plugin Installati</div>
<div class="tab" onclick="switchTab('search')" id="tab-search">Cerca</div>
</div>
<div id="marketplace-tab" class="tab-content active">
<h2 id="marketplace-title">🏪 Plugin Disponibili</h2>
<div id="alert-container"></div>
<div class="stats-container">
<div class="stat-card">
<div class="stat-value" id="total-plugins">0</div>
<div class="stat-label" id="total-plugins-label">Plugin Totali</div>
</div>
<div class="stat-card">
<div class="stat-value" id="installed-plugins">0</div>
<div class="stat-label" id="installed-plugins-label">Plugin Installati</div>
</div>
<div class="stat-card">
<div class="stat-value" id="available-plugins">0</div>
<div class="stat-label" id="available-plugins-label">Plugin Disponibili</div>
</div>
</div>
<div class="plugins-grid" id="plugins-grid">
<!-- Plugins will be loaded here -->
</div>
</div>
<div id="installed-tab" class="tab-content">
<h2 id="installed-title">📦 Plugin Installati</h2>
<div class="plugins-grid" id="installed-plugins-grid">
<!-- Installed plugins will be loaded here -->
</div>
</div>
<div id="search-tab" class="tab-content">
<h2 id="search-title">🔍 Cerca Plugin</h2>
<div class="search-container">
<input type="text" class="search-input" id="search-input" placeholder="Cerca plugin...">
<button class="search-btn" onclick="searchPlugins()" id="search-btn">Cerca</button>
</div>
<div class="plugins-grid" id="search-results-grid">
<!-- Search results will be loaded here -->
</div>
</div>
</div>
</div>
<script>
// API base URL
const API_BASE = 'http://localhost:8000';
// Current language
let currentLanguage = 'it';
// Translations
const translations = {
it: {
title: "MaxClaw Agent - Marketplace",
subtitle: "Scopri e installa plugin per estendere le funzionalità",
tabMarketplace: "Marketplace",
tabInstalled: "Plugin Installati",
tabSearch: "Cerca",
marketplaceTitle: "🏪 Plugin Disponibili",
totalPluginsLabel: "Plugin Totali",
installedPluginsLabel: "Plugin Installati",
availablePluginsLabel: "Plugin Disponibili",
installedTitle: "📦 Plugin Installati",
searchTitle: "🔍 Cerca Plugin",
searchPlaceholder: "Cerca plugin...",
searchBtn: "Cerca",
installBtn: "Installa",
uninstallBtn: "Disinstalla",
infoBtn: "Info",
alertSuccess: "Operazione completata con successo!",
alertError: "Errore",
alertWarning: "Avviso",
alertSearching: "Ricerca in corso...",
alertInstalling: "Installazione in corso...",
alertUninstalling: "Disinstallazione in corso...",
noPlugins: "Nessun plugin trovato",
installed: "Installato",
available: "Disponibile",
refreshBtn: "Aggiorna"
},
en: {
title: "MaxClaw Agent - Marketplace",
subtitle: "Discover and install plugins to extend functionality",
tabMarketplace: "Marketplace",
tabInstalled: "Installed Plugins",
tabSearch: "Search",
marketplaceTitle: "🏪 Available Plugins",
totalPluginsLabel: "Total Plugins",
installedPluginsLabel: "Installed Plugins",
availablePluginsLabel: "Available Plugins",
installedTitle: "📦 Installed Plugins",
searchTitle: "🔍 Search Plugins",
searchPlaceholder: "Search plugins...",
searchBtn: "Search",
installBtn: "Install",
uninstallBtn: "Uninstall",
infoBtn: "Info",
alertSuccess: "Operation completed successfully!",
alertError: "Error",
alertWarning: "Warning",
alertSearching: "Searching...",
alertInstalling: "Installing...",
alertUninstalling: "Uninstalling...",
noPlugins: "No plugins found",
installed: "Installed",
available: "Available",
refreshBtn: "Refresh"
}
};
// Initialize
document.addEventListener('DOMContentLoaded', function() {
// Load saved language preference
const savedLanguage = localStorage.getItem('maxclaw-language') || 'it';
document.getElementById('language-select').value = savedLanguage;
changeLanguage(savedLanguage);
loadMarketplaceStats();
loadPlugins();
loadInstalledPlugins();
// Handle search input
document.getElementById('search-input').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
searchPlugins();
}
});
});
// Change language
function changeLanguage(language) {
currentLanguage = language;
localStorage.setItem('maxclaw-language', language);
const t = translations[language];
// Update all translatable elements
document.getElementById('title').textContent = t.title;
document.getElementById('subtitle').textContent = t.subtitle;
document.getElementById('tab-marketplace').textContent = t.tabMarketplace;
document.getElementById('tab-installed').textContent = t.tabInstalled;
document.getElementById('tab-search').textContent = t.tabSearch;
document.getElementById('marketplace-title').textContent = t.marketplaceTitle;
document.getElementById('total-plugins-label').textContent = t.totalPluginsLabel;
document.getElementById('installed-plugins-label').textContent = t.installedPluginsLabel;
document.getElementById('available-plugins-label').textContent = t.availablePluginsLabel;
document.getElementById('installed-title').textContent = t.installedTitle;
document.getElementById('search-title').textContent = t.searchTitle;
document.getElementById('search-input').placeholder = t.searchPlaceholder;
document.getElementById('search-btn').textContent = t.searchBtn;
// Update HTML lang attribute
document.documentElement.lang = language;
}
// Switch tabs
function switchTab(tabName) {
// Update tab buttons
document.querySelectorAll('.tab').forEach(tab => {
tab.classList.remove('active');
});
event.target.classList.add('active');
// Update tab content
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
document.getElementById(tabName + '-tab').classList.add('active');
}
// Load marketplace stats
async function loadMarketplaceStats() {
try {
// Simulate stats (in real implementation, this would come from API)
const stats = {
total_plugins: 15,
installed_plugins: 3,
available_plugins: 12
};
document.getElementById('total-plugins').textContent = stats.total_plugins;
document.getElementById('installed-plugins').textContent = stats.installed_plugins;
document.getElementById('available-plugins').textContent = stats.available_plugins;
} catch (error) {
console.error('Error loading marketplace stats:', error);
}
}
// Load plugins
async function loadPlugins() {
try {
// Simulate plugins (in real implementation, this would come from API)
const plugins = [
{
name: "weather",
description: "Get weather information for any location",
installed: false,
downloads: 1250,
rating: 4.5
},
{
name: "calendar",
description: "Manage calendar events and reminders",
installed: true,
downloads: 980,
rating: 4.2
},
{
name: "translation",
description: "Translate text between languages",
installed: false,
downloads: 2100,
rating: 4.7
},
{
name: "image_recognition",
description: "Analyze and describe images",
installed: false,
downloads: 750,
rating: 4.0
},
{
name: "code_analysis",
description: "Analyze code quality and suggest improvements",
installed: true,
downloads: 1500,
rating: 4.8
},
{
name: "database_tools",
description: "Connect to and query databases",
installed: false,
downloads: 620,
rating: 4.3
}
];
const container = document.getElementById('plugins-grid');
container.innerHTML = '';
plugins.forEach(plugin => {
const card = document.createElement('div');
card.className = 'plugin-card';
const statusClass = plugin.installed ? 'status-installed' : 'status-available';
const statusText = plugin.installed ? translations[currentLanguage].installed : translations[currentLanguage].available;
card.innerHTML = `
<div class="plugin-header">
<div class="plugin-name">${plugin.name}</div>
<div class="plugin-status ${statusClass}">${statusText}</div>
</div>
<div class="plugin-description">${plugin.description}</div>
<div class="plugin-actions">
${plugin.installed ?
`<button class="btn btn-danger" onclick="uninstallPlugin('${plugin.name}')">${translations[currentLanguage].uninstallBtn}</button>` :
`<button class="btn btn-primary" onclick="installPlugin('${plugin.name}')">${translations[currentLanguage].installBtn}</button>`
}
<button class="btn btn-secondary" onclick="showPluginInfo('${plugin.name}')">${translations[currentLanguage].infoBtn}</button>
</div>
`;
container.appendChild(card);
});
} catch (error) {
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'error');
}
}
// Load installed plugins
async function loadInstalledPlugins() {
try {
// Simulate installed plugins (in real implementation, this would come from API)
const installed = [
{
name: "calendar",
description: "Manage calendar events and reminders",
version: "1.0.0",
author: "MaxClaw Team"
},
{
name: "code_analysis",
description: "Analyze code quality and suggest improvements",
version: "1.2.0",
author: "Community"
},
{
name: "file_tools",
description: "Additional file management tools",
version: "1.0.0",
author: "MaxClaw Team"
}
];
const container = document.getElementById('installed-plugins-grid');
container.innerHTML = '';
if (installed.length === 0) {
container.innerHTML = `<p>${translations[currentLanguage].noPlugins}</p>`;
return;
}
installed.forEach(plugin => {
const card = document.createElement('div');
card.className = 'plugin-card';
card.innerHTML = `
<div class="plugin-header">
<div class="plugin-name">${plugin.name}</div>
<div class="plugin-status status-installed">${translations[currentLanguage].installed}</div>
</div>
<div class="plugin-description">${plugin.description}</div>
<div class="plugin-actions">
<button class="btn btn-danger" onclick="uninstallPlugin('${plugin.name}')">${translations[currentLanguage].uninstallBtn}</button>
<button class="btn btn-secondary" onclick="showPluginInfo('${plugin.name}')">${translations[currentLanguage].infoBtn}</button>
</div>
`;
container.appendChild(card);
});
} catch (error) {
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'error');
}
}
// Search plugins
async function searchPlugins() {
const query = document.getElementById('search-input').value.trim();
if (!query) {
showAlert(translations[currentLanguage].alertWarning + ': Inserisci un termine di ricerca', 'warning');
return;
}
showAlert(translations[currentLanguage].alertSearching, 'info');
try {
// Simulate search results (in real implementation, this would come from API)
const results = [
{
name: "weather",
description: "Get weather information for any location",
installed: false
},
{
name: "translation",
description: "Translate text between languages",
installed: false
}
];
const container = document.getElementById('search-results-grid');
container.innerHTML = '';
if (results.length === 0) {
container.innerHTML = `<p>${translations[currentLanguage].noPlugins}</p>`;
return;
}
results.forEach(plugin => {
const card = document.createElement('div');
card.className = 'plugin-card';
const statusClass = plugin.installed ? 'status-installed' : 'status-available';
const statusText = plugin.installed ? translations[currentLanguage].installed : translations[currentLanguage].available;
card.innerHTML = `
<div class="plugin-header">
<div class="plugin-name">${plugin.name}</div>
<div class="plugin-status ${statusClass}">${statusText}</div>
</div>
<div class="plugin-description">${plugin.description}</div>
<div class="plugin-actions">
${plugin.installed ?
`<button class="btn btn-danger" onclick="uninstallPlugin('${plugin.name}')">${translations[currentLanguage].uninstallBtn}</button>` :
`<button class="btn btn-primary" onclick="installPlugin('${plugin.name}')">${translations[currentLanguage].installBtn}</button>`
}
<button class="btn btn-secondary" onclick="showPluginInfo('${plugin.name}')">${translations[currentLanguage].infoBtn}</button>
</div>
`;
container.appendChild(card);
});
showAlert(`${translations[currentLanguage].alertSuccess} Trovati ${results.length} plugin`, 'success');
} catch (error) {
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'error');
}
}
// Install plugin
async function installPlugin(pluginName) {
showAlert(translations[currentLanguage].alertInstalling + ' ' + pluginName, 'info');
try {
// Simulate installation (in real implementation, this would call API)
await new Promise(resolve => setTimeout(resolve, 1500));
showAlert(`${translations[currentLanguage].alertSuccess} Plugin '${pluginName}' installato!`, 'success');
// Refresh the lists
loadPlugins();
loadInstalledPlugins();
loadMarketplaceStats();
} catch (error) {
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'error');
}
}
// Uninstall plugin
async function uninstallPlugin(pluginName) {
if (!confirm(`Sei sicuro di voler disinstallare il plugin '${pluginName}'?`)) {
return;
}
showAlert(translations[currentLanguage].alertUninstalling + ' ' + pluginName, 'info');
try {
// Simulate uninstallation (in real implementation, this would call API)
await new Promise(resolve => setTimeout(resolve, 1000));
showAlert(`${translations[currentLanguage].alertSuccess} Plugin '${pluginName}' disinstallato!`, 'success');
// Refresh the lists
loadPlugins();
loadInstalledPlugins();
loadMarketplaceStats();
} catch (error) {
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'error');
}
}
// Show plugin info
function showPluginInfo(pluginName) {
alert(`Informazioni sul plugin: ${pluginName}\n\nQuesta funzionalità mostrerà dettagli completi sul plugin, inclusi:\n- Versione\n- Autore\n- Descrizione dettagliata\n- Dipendenze\n- Documentazione`);
}
// Show alert
function showAlert(message, type) {
const container = document.getElementById('alert-container');
const alert = document.createElement('div');
alert.className = `alert alert-${type}`;
alert.textContent = message;
// Remove existing alerts
container.innerHTML = '';
container.appendChild(alert);
// Auto-remove after 5 seconds
setTimeout(() => {
if (alert.parentNode) {
alert.parentNode.removeChild(alert);
}
}, 5000);
}
</script>
</body>
</html>