mirror of
https://github.com/soconnor0919/robot-plugins.git
synced 2025-12-12 23:24:43 -05:00
Enhance repository page with dynamic favicon, detailed plugin view, and improved UI components
This commit is contained in:
897
assets/style.css
897
assets/style.css
File diff suppressed because it is too large
Load Diff
513
index.html
513
index.html
@@ -5,6 +5,22 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>HRIStudio Robot Plugins</title>
|
||||
<link rel="stylesheet" href="assets/style.css">
|
||||
<link rel="icon" type="image/png" id="favicon">
|
||||
<script>
|
||||
// Update favicon when logo is loaded
|
||||
async function updateFavicon() {
|
||||
try {
|
||||
const response = await fetch('repository.json');
|
||||
const data = await response.json();
|
||||
if (data.assets?.logo) {
|
||||
document.getElementById('favicon').href = data.assets.logo;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load favicon:', error);
|
||||
}
|
||||
}
|
||||
updateFavicon();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
@@ -20,8 +36,11 @@
|
||||
<div class="card-content">
|
||||
<div class="header-content">
|
||||
<img id="repoIcon" alt="Repository Icon" class="header-icon hidden">
|
||||
<div>
|
||||
<h1 id="repoName" class="title"></h1>
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 id="repoName" class="title"></h1>
|
||||
<div id="repoLinks" class="button-group"></div>
|
||||
</div>
|
||||
<p id="repoDescription" class="description"></p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -42,16 +61,63 @@
|
||||
<h2>Author</h2>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div>
|
||||
<span>Name:</span>
|
||||
<div class="card-row">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
<span class="label">Name:</span>
|
||||
<span id="authorName"></span>
|
||||
</div>
|
||||
<div id="authorOrgContainer" class="hidden">
|
||||
<span>Organization:</span>
|
||||
<div id="authorOrgContainer" class="card-row hidden">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="2" y="7" width="20" height="14" rx="2" ry="2"></rect>
|
||||
<path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"></path>
|
||||
</svg>
|
||||
<span class="label">Organization:</span>
|
||||
<span id="authorOrg"></span>
|
||||
</div>
|
||||
<div id="authorUrlContainer" class="hidden">
|
||||
<a id="authorUrl" target="_blank">View Profile</a>
|
||||
<div id="authorEmailContainer" class="card-row hidden">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
|
||||
<polyline points="22,6 12,13 2,6"></polyline>
|
||||
</svg>
|
||||
<span class="label">Email:</span>
|
||||
<a id="authorEmail" target="_blank" class="text-primary hover:underline"></a>
|
||||
</div>
|
||||
<div id="authorUrlContainer" class="card-row hidden">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
|
||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
|
||||
</svg>
|
||||
<span class="label">Website:</span>
|
||||
<a id="authorUrl" target="_blank" class="text-primary hover:underline">View</a>
|
||||
</div>
|
||||
<div id="maintainersContainer" class="card-row hidden">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="9" cy="7" r="4"></circle>
|
||||
<path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
|
||||
<path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
|
||||
</svg>
|
||||
<span class="label">Maintainers:</span>
|
||||
<div id="maintainersList"></div>
|
||||
</div>
|
||||
<div class="card-row">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path>
|
||||
<rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect>
|
||||
</svg>
|
||||
<span class="label">License:</span>
|
||||
<span id="license"></span>
|
||||
</div>
|
||||
<div class="card-row">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<polyline points="12 6 12 12 16 14"></polyline>
|
||||
</svg>
|
||||
<span class="label">Last Updated:</span>
|
||||
<span id="lastUpdated"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -62,22 +128,32 @@
|
||||
<h2>Compatibility</h2>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div>
|
||||
<h3>HRIStudio</h3>
|
||||
<div>
|
||||
<span>Min Version:</span>
|
||||
<div class="card-section">
|
||||
<h3>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bot"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/></svg>
|
||||
HRIStudio
|
||||
</h3>
|
||||
<div class="card-row">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-minus"><circle cx="12" cy="12" r="10"/><path d="M8 12h8"/></svg>
|
||||
<span class="label">Minimum Version:</span>
|
||||
<span id="hriMin"></span>
|
||||
</div>
|
||||
<div id="hriRecommendedContainer" class="hidden">
|
||||
<span>Recommended:</span>
|
||||
<div id="hriRecommendedContainer" class="card-row hidden">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check-big"><path d="M21.801 10A10 10 0 1 1 17 3.335"/><path d="m9 11 3 3L22 4"/></svg>
|
||||
<span class="label">Recommended:</span>
|
||||
<span id="hriRecommended"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ros2Container" class="hidden">
|
||||
<h3>ROS 2</h3>
|
||||
<div id="ros2Distributions" style="margin: 0.5rem 0;"></div>
|
||||
<div id="ros2RecommendedContainer" class="hidden">
|
||||
<span>Recommended:</span>
|
||||
|
||||
<div id="ros2Container" class="card-section hidden">
|
||||
<h3>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-grip"><circle cx="12" cy="5" r="1"/><circle cx="19" cy="5" r="1"/><circle cx="5" cy="5" r="1"/><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/><circle cx="12" cy="19" r="1"/><circle cx="19" cy="19" r="1"/><circle cx="5" cy="19" r="1"/></svg>
|
||||
ROS 2
|
||||
</h3>
|
||||
<div id="ros2Distributions" class="flex flex-wrap gap-1 mb-2"></div>
|
||||
<div id="ros2RecommendedContainer" class="card-row hidden">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check-big"><path d="M21.801 10A10 10 0 1 1 17 3.335"/><path d="m9 11 3 3L22 4"/></svg>
|
||||
<span class="label">Recommended:</span>
|
||||
<span id="ros2Recommended"></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -100,14 +176,157 @@
|
||||
<div class="tab-content" role="tabpanel" data-tab="plugins" aria-hidden="true">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<h2 style="margin: 0;">Available Plugins</h2>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h2>Available Plugins</h2>
|
||||
<p class="text-sm text-muted-foreground mt-1">
|
||||
Browse robot plugins from this repository
|
||||
</p>
|
||||
</div>
|
||||
<span class="badge badge-secondary" id="pluginCount"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div id="pluginGrid" class="plugin-grid">
|
||||
<!-- Plugins will be loaded here -->
|
||||
<div class="plugin-layout">
|
||||
<!-- Plugin List -->
|
||||
<div class="plugin-list">
|
||||
<div id="pluginList"></div>
|
||||
</div>
|
||||
|
||||
<!-- Plugin Details -->
|
||||
<div class="plugin-details">
|
||||
<div id="pluginDetails" class="hidden">
|
||||
<div class="plugin-details-header">
|
||||
<div class="mb-4 flex items-start justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="plugin-details-icon">
|
||||
<img id="detailsIcon" alt="" class="plugin-icon">
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 id="detailsTitle" class="text-xl font-semibold"></h3>
|
||||
<p id="detailsDescription" class="mt-1 text-muted-foreground"></p>
|
||||
<div class="mt-4 flex items-center gap-2">
|
||||
<span id="detailsSpeed" class="badge badge-secondary"></span>
|
||||
<span id="detailsBattery" class="badge badge-secondary"></span>
|
||||
<span id="detailsWeight" class="badge badge-secondary"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<a id="detailsDocsButton" href="#" target="_blank" rel="noopener noreferrer" class="button button-outline">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
|
||||
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
|
||||
</svg>
|
||||
Documentation
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6">
|
||||
<div class="plugin-details-tabs">
|
||||
<div class="plugin-details-tabs-list" role="tablist">
|
||||
<button class="plugin-details-tab" role="tab" aria-selected="true" data-state="active" data-plugin-tab="plugin-overview">Overview</button>
|
||||
<button class="plugin-details-tab" role="tab" aria-selected="false" data-plugin-tab="plugin-specs">Specifications</button>
|
||||
<button class="plugin-details-tab" role="tab" aria-selected="false" data-plugin-tab="plugin-actions">Actions</button>
|
||||
</div>
|
||||
|
||||
<div class="plugin-details-tab-content" data-state="active" role="tabpanel" data-plugin-tab="plugin-overview">
|
||||
<div id="detailsImages" class="relative mb-6">
|
||||
<!-- Images will be loaded here -->
|
||||
</div>
|
||||
<div class="card-secondary">
|
||||
<h4>Documentation</h4>
|
||||
<div class="grid gap-2 text-sm">
|
||||
<a id="detailsMainDocs" href="#" target="_blank" rel="noopener noreferrer" class="text-primary hover:underline">
|
||||
User Manual
|
||||
</a>
|
||||
<a id="detailsApiDocs" href="#" target="_blank" rel="noopener noreferrer" class="text-primary hover:underline hidden">
|
||||
API Reference
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="plugin-details-tab-content" role="tabpanel" data-plugin-tab="plugin-specs">
|
||||
<div class="space-y-6">
|
||||
<div class="card-secondary">
|
||||
<h4>Physical Specifications</h4>
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M19 21l2-2v-6"/><path d="M12 3v18"/><path d="m5 21-2-2v-6"/>
|
||||
<path d="M3 7V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v2"/>
|
||||
<path d="M3 17v2a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-2"/>
|
||||
</svg>
|
||||
<span id="detailsDimensions" class="text-sm"></span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m5 8 6 6"/><path d="m4 14 6 6 10-10-6-6-10 10z"/>
|
||||
</svg>
|
||||
<span id="detailsSpeedFull" class="text-sm"></span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M3 3v18h18"/><path d="m19 9-5 5-4-4-3 3"/>
|
||||
</svg>
|
||||
<span id="detailsBatteryFull" class="text-sm"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-secondary">
|
||||
<h4>Capabilities</h4>
|
||||
<div id="detailsCapabilities" class="flex flex-wrap gap-2">
|
||||
<!-- Capabilities will be loaded here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-secondary">
|
||||
<h4>ROS 2 Configuration</h4>
|
||||
<div class="grid gap-3 text-sm">
|
||||
<div>
|
||||
<span class="text-muted-foreground">Namespace: </span>
|
||||
<code id="detailsNamespace" class="rounded bg-muted px-1.5 py-0.5"></code>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-muted-foreground">Node Prefix: </span>
|
||||
<code id="detailsNodePrefix" class="rounded bg-muted px-1.5 py-0.5"></code>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<span class="text-muted-foreground">Default Topics:</span>
|
||||
<div id="detailsTopics" class="pl-4">
|
||||
<!-- Topics will be loaded here -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="plugin-details-tab-content" role="tabpanel" data-plugin-tab="plugin-actions">
|
||||
<div id="detailsActions" class="space-y-4">
|
||||
<!-- Actions will be loaded here -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="emptyState" class="absolute inset-0 flex flex-col items-center justify-center p-6 text-center">
|
||||
<div class="flex h-16 w-16 items-center justify-center rounded-lg bg-muted mb-4">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground">
|
||||
<path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/>
|
||||
<path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-medium">No Plugin Selected</h3>
|
||||
<p class="text-sm text-muted-foreground mt-1">
|
||||
Select a plugin from the list to view its details
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -119,10 +338,10 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Tab management
|
||||
// Separate tab management for root and plugin details
|
||||
document.querySelectorAll('.tab').forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
// Update tab states
|
||||
// Update root tab states
|
||||
document.querySelectorAll('.tab').forEach(t => {
|
||||
t.setAttribute('data-state', '');
|
||||
t.setAttribute('aria-selected', 'false');
|
||||
@@ -130,7 +349,7 @@
|
||||
tab.setAttribute('data-state', 'active');
|
||||
tab.setAttribute('aria-selected', 'true');
|
||||
|
||||
// Update content states
|
||||
// Update root content states
|
||||
const tabId = tab.getAttribute('data-tab');
|
||||
document.querySelectorAll('.tab-content').forEach(content => {
|
||||
const isActive = content.getAttribute('data-tab') === tabId;
|
||||
@@ -140,6 +359,27 @@
|
||||
});
|
||||
});
|
||||
|
||||
// Add separate tab management for plugin details
|
||||
document.querySelectorAll('.plugin-details-tab').forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
// Update plugin details tab states
|
||||
document.querySelectorAll('.plugin-details-tab').forEach(t => {
|
||||
t.setAttribute('data-state', '');
|
||||
t.setAttribute('aria-selected', 'false');
|
||||
});
|
||||
tab.setAttribute('data-state', 'active');
|
||||
tab.setAttribute('aria-selected', 'true');
|
||||
|
||||
// Update plugin details content states
|
||||
const tabId = tab.getAttribute('data-plugin-tab');
|
||||
document.querySelectorAll('.plugin-details-tab-content').forEach(content => {
|
||||
const isActive = content.getAttribute('data-plugin-tab') === tabId;
|
||||
content.setAttribute('data-state', isActive ? 'active' : '');
|
||||
content.setAttribute('aria-hidden', isActive ? 'false' : 'true');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Repository data loading
|
||||
async function loadRepositoryData() {
|
||||
try {
|
||||
@@ -153,8 +393,175 @@
|
||||
document.getElementById('repoName').textContent = data.name;
|
||||
document.getElementById('repoDescription').textContent = data.description || '';
|
||||
|
||||
// Update stats
|
||||
document.getElementById('pluginCount').textContent = `${data.stats?.plugins || 0} plugins`;
|
||||
// Add repository links
|
||||
const repoLinks = document.getElementById('repoLinks');
|
||||
if (data.urls?.git) {
|
||||
const gitButton = document.createElement('a');
|
||||
gitButton.href = data.urls.git;
|
||||
gitButton.target = '_blank';
|
||||
gitButton.rel = 'noopener noreferrer';
|
||||
gitButton.className = 'button button-outline';
|
||||
gitButton.innerHTML = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-folder-git-2"><path d="M9 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v5"/><circle cx="13" cy="12" r="2"/><path d="M18 19c-2.8 0-5-2.2-5-5v8"/><circle cx="20" cy="19" r="2"/></svg>
|
||||
View Git Repository
|
||||
`;
|
||||
repoLinks.appendChild(gitButton);
|
||||
}
|
||||
|
||||
// Load plugins
|
||||
const pluginsResponse = await fetch('plugins/index.json');
|
||||
const pluginFiles = await pluginsResponse.json();
|
||||
|
||||
// Update plugin count
|
||||
document.getElementById('pluginCount').textContent = `${pluginFiles.length} ${pluginFiles.length === 1 ? 'plugin' : 'plugins'}`;
|
||||
|
||||
// Clear existing plugins
|
||||
const pluginList = document.getElementById('pluginList');
|
||||
pluginList.innerHTML = '';
|
||||
|
||||
// Load each plugin
|
||||
for (const pluginFile of pluginFiles) {
|
||||
const pluginResponse = await fetch(`plugins/${pluginFile}`);
|
||||
const plugin = await pluginResponse.json();
|
||||
|
||||
const card = document.createElement('div');
|
||||
card.className = 'plugin-list-item';
|
||||
card.innerHTML = `
|
||||
<div class="plugin-list-icon">
|
||||
<img src="${plugin.assets.thumbnailUrl}" alt="${plugin.name}" class="plugin-icon">
|
||||
</div>
|
||||
<div class="plugin-list-info">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="plugin-list-title">${plugin.name}</div>
|
||||
<span class="badge badge-secondary">${plugin.platform}</span>
|
||||
</div>
|
||||
<div class="plugin-list-description">${plugin.description || ''}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add click handler
|
||||
card.addEventListener('click', () => {
|
||||
// Remove selected class from all items
|
||||
document.querySelectorAll('.plugin-list-item').forEach(item => {
|
||||
item.classList.remove('selected');
|
||||
});
|
||||
// Add selected class to clicked item
|
||||
card.classList.add('selected');
|
||||
|
||||
// Show plugin details
|
||||
document.getElementById('pluginDetails').classList.remove('hidden');
|
||||
document.getElementById('emptyState').classList.add('hidden');
|
||||
|
||||
// Update details
|
||||
document.getElementById('detailsIcon').src = plugin.assets.thumbnailUrl;
|
||||
document.getElementById('detailsTitle').textContent = plugin.name;
|
||||
document.getElementById('detailsDescription').textContent = plugin.description || '';
|
||||
document.getElementById('detailsDocsButton').href = plugin.documentation.mainUrl;
|
||||
document.getElementById('detailsSpeed').textContent = `${plugin.specs.maxSpeed}m/s`;
|
||||
document.getElementById('detailsBattery').textContent = `${plugin.specs.batteryLife}h`;
|
||||
document.getElementById('detailsWeight').textContent = `${plugin.specs.dimensions.weight}kg`;
|
||||
|
||||
// Load images in the overview tab
|
||||
const imagesContainer = document.getElementById('detailsImages');
|
||||
imagesContainer.innerHTML = `
|
||||
<div class="image-grid">
|
||||
<div class="image-item main">
|
||||
<img src="${plugin.assets.images.main}" alt="${plugin.name} main view">
|
||||
</div>
|
||||
${plugin.assets.images.angles ? Object.entries(plugin.assets.images.angles)
|
||||
.filter(([_, url]) => url)
|
||||
.map(([angle, url]) => `
|
||||
<div class="image-item angle">
|
||||
<img src="${url}" alt="${plugin.name} ${angle} view">
|
||||
<div class="image-label">${angle} View</div>
|
||||
</div>
|
||||
`).join('') : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Update specifications
|
||||
document.getElementById('detailsDimensions').textContent =
|
||||
`${plugin.specs.dimensions.length}m × ${plugin.specs.dimensions.width}m × ${plugin.specs.dimensions.height}m`;
|
||||
document.getElementById('detailsSpeedFull').textContent = `${plugin.specs.maxSpeed}m/s`;
|
||||
document.getElementById('detailsBatteryFull').textContent = `${plugin.specs.batteryLife}h`;
|
||||
|
||||
// Update capabilities
|
||||
const capsContainer = document.getElementById('detailsCapabilities');
|
||||
capsContainer.innerHTML = '';
|
||||
plugin.specs.capabilities.forEach(cap => {
|
||||
const badge = document.createElement('span');
|
||||
badge.className = 'badge badge-secondary';
|
||||
badge.textContent = cap;
|
||||
capsContainer.appendChild(badge);
|
||||
});
|
||||
|
||||
// Update ROS2 config
|
||||
document.getElementById('detailsNamespace').textContent = plugin.ros2Config.namespace;
|
||||
document.getElementById('detailsNodePrefix').textContent = plugin.ros2Config.nodePrefix;
|
||||
|
||||
const topicsContainer = document.getElementById('detailsTopics');
|
||||
topicsContainer.innerHTML = '';
|
||||
Object.entries(plugin.ros2Config.defaultTopics).forEach(([name, topic]) => {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = `
|
||||
<span class="text-muted-foreground">${name}: </span>
|
||||
<code class="rounded bg-muted px-1.5 py-0.5">${topic}</code>
|
||||
`;
|
||||
topicsContainer.appendChild(div);
|
||||
});
|
||||
|
||||
// Update documentation links
|
||||
document.getElementById('detailsMainDocs').href = plugin.documentation.mainUrl;
|
||||
const apiDocs = document.getElementById('detailsApiDocs');
|
||||
if (plugin.documentation.apiReference) {
|
||||
apiDocs.href = plugin.documentation.apiReference;
|
||||
apiDocs.classList.remove('hidden');
|
||||
} else {
|
||||
apiDocs.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Update actions
|
||||
const actionsContainer = document.getElementById('detailsActions');
|
||||
actionsContainer.innerHTML = '';
|
||||
plugin.actions.forEach(action => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'card';
|
||||
div.innerHTML = `
|
||||
<div class="card-content">
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<h4 class="font-medium">${action.title}</h4>
|
||||
<span class="badge badge-secondary">${action.type}</span>
|
||||
</div>
|
||||
<p class="mb-4 text-sm text-muted-foreground">
|
||||
${action.description}
|
||||
</p>
|
||||
<div class="grid gap-2">
|
||||
<h5 class="text-sm font-medium text-muted-foreground">Parameters:</h5>
|
||||
<div class="grid gap-2 pl-4">
|
||||
${Object.entries(action.parameters.properties).map(([name, prop]) => `
|
||||
<div class="text-sm">
|
||||
<span class="font-medium">${prop.title}</span>
|
||||
${prop.unit ? `<span class="text-muted-foreground"> (${prop.unit})</span>` : ''}
|
||||
${prop.description ? `<p class="mt-0.5 text-muted-foreground">${prop.description}</p>` : ''}
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
actionsContainer.appendChild(div);
|
||||
});
|
||||
|
||||
// Trigger click on the Overview tab to show it by default
|
||||
document.querySelector('.plugin-details-tabs-list .plugin-details-tab[data-plugin-tab="plugin-overview"]').click();
|
||||
});
|
||||
|
||||
pluginList.appendChild(card);
|
||||
}
|
||||
|
||||
// Show empty state initially
|
||||
document.getElementById('pluginDetails').classList.add('hidden');
|
||||
document.getElementById('emptyState').classList.remove('hidden');
|
||||
|
||||
// Update author info
|
||||
document.getElementById('authorName').textContent = data.author.name;
|
||||
@@ -162,11 +569,28 @@
|
||||
document.getElementById('authorOrgContainer').classList.remove('hidden');
|
||||
document.getElementById('authorOrg').textContent = data.author.organization;
|
||||
}
|
||||
if (data.author.email) {
|
||||
document.getElementById('authorEmailContainer').classList.remove('hidden');
|
||||
const emailLink = document.getElementById('authorEmail');
|
||||
emailLink.href = `mailto:${data.author.email}`;
|
||||
emailLink.textContent = data.author.email;
|
||||
}
|
||||
if (data.author.url) {
|
||||
document.getElementById('authorUrlContainer').classList.remove('hidden');
|
||||
document.getElementById('authorUrl').href = data.author.url;
|
||||
}
|
||||
|
||||
// Update maintainers
|
||||
if (data.maintainers && data.maintainers.length > 0) {
|
||||
document.getElementById('maintainersContainer').classList.remove('hidden');
|
||||
const maintainersList = document.getElementById('maintainersList');
|
||||
maintainersList.innerHTML = data.maintainers.map(maintainer =>
|
||||
`<span>${maintainer.name}${maintainer.url ?
|
||||
` (<a href="${maintainer.url}" target="_blank" rel="noopener noreferrer">Git</a>)` :
|
||||
''}</span>`
|
||||
).join('');
|
||||
}
|
||||
|
||||
// Update compatibility
|
||||
document.getElementById('hriMin').textContent = data.compatibility.hristudio.min;
|
||||
if (data.compatibility.hristudio.recommended) {
|
||||
@@ -212,31 +636,14 @@
|
||||
banner.querySelector('img').src = data.assets.banner;
|
||||
}
|
||||
|
||||
// Load plugins
|
||||
const pluginGrid = document.getElementById('pluginGrid');
|
||||
const pluginsResponse = await fetch('plugins/index.json');
|
||||
const pluginFiles = await pluginsResponse.json();
|
||||
|
||||
for (const pluginFile of pluginFiles) {
|
||||
const pluginResponse = await fetch(`plugins/${pluginFile}`);
|
||||
const plugin = await pluginResponse.json();
|
||||
|
||||
const card = document.createElement('div');
|
||||
card.className = 'plugin-card';
|
||||
card.innerHTML = `
|
||||
<div class="plugin-header">
|
||||
<img src="${plugin.assets.thumbnailUrl}" alt="${plugin.name}" class="plugin-icon">
|
||||
<div class="plugin-info">
|
||||
<div class="plugin-title">${plugin.name}</div>
|
||||
<div class="plugin-description">${plugin.description || ''}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="badge badge-secondary">${plugin.platform}</span>
|
||||
</div>
|
||||
`;
|
||||
pluginGrid.appendChild(card);
|
||||
}
|
||||
// Update license and last updated
|
||||
document.getElementById('license').textContent = data.license;
|
||||
const lastUpdated = new Date(data.lastUpdated);
|
||||
document.getElementById('lastUpdated').textContent = lastUpdated.toLocaleDateString(undefined, {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
|
||||
// Show content
|
||||
document.getElementById('loading').classList.add('hidden');
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
"name": "HRIStudio Official Robot Plugins",
|
||||
"description": "Official collection of robot plugins maintained by the HRIStudio team",
|
||||
"urls": {
|
||||
"repository": "https://soconnor0919.github.io/robot-plugins"
|
||||
"git": "https://github.com/soconnor0919/robot-plugins",
|
||||
"repository": "https://repo.hristudio.com"
|
||||
},
|
||||
"official": true,
|
||||
"trust": "official",
|
||||
"author": {
|
||||
"name": "HRIStudio Team",
|
||||
"email": "team@hristudio.org",
|
||||
"url": "https://hristudio.org",
|
||||
"email": "support@hristudio.com",
|
||||
"url": "https://hristudio.com",
|
||||
"organization": "HRIStudio"
|
||||
},
|
||||
"maintainers": [
|
||||
|
||||
Reference in New Issue
Block a user