From abe8a92561b008e611bcfa4c603adab4901fac3e Mon Sep 17 00:00:00 2001 From: unclecode Date: Sat, 11 Oct 2025 11:51:22 +0800 Subject: [PATCH] fix(marketplace): resolve app detail page routing and styling issues - Fixed JavaScript errors from missing HTML elements (install-code, usage-code, integration-code) - Added missing CSS classes for tabs, overview layout, sidebar, and integration content - Fixed tab navigation to display horizontally in single line - Added proper padding to tab content sections (removed from container, added to content) - Fixed tab selector from .nav-tab to .tab-btn to match HTML structure - Added sidebar styling with stats grid and metadata display - Improved responsive design with mobile-friendly tab scrolling - Fixed code block positioning for copy buttons - Removed margin from first headings to prevent extra spacing - Added null checks for DOM elements in JavaScript to prevent errors These changes resolve the routing issue where clicking on apps caused page redirects, and fix the broken layout where CSS was not properly applied to the app detail page. --- docs/md_v2/marketplace/app-detail.css | 214 +++++++++++++++++- docs/md_v2/marketplace/app-detail.html | 209 +++++++++++++++++ docs/md_v2/marketplace/app-detail.js | 44 +++- docs/md_v2/marketplace/frontend/app-detail.js | 12 +- 4 files changed, 459 insertions(+), 20 deletions(-) create mode 100644 docs/md_v2/marketplace/app-detail.html diff --git a/docs/md_v2/marketplace/app-detail.css b/docs/md_v2/marketplace/app-detail.css index 9f04c13a..590bea03 100644 --- a/docs/md_v2/marketplace/app-detail.css +++ b/docs/md_v2/marketplace/app-detail.css @@ -197,6 +197,41 @@ } /* Navigation Tabs */ +.tabs { + display: flex; + flex-direction: row; + gap: 0; + border-bottom: 2px solid var(--border-color); + margin-bottom: 0; + background: var(--bg-tertiary); +} + +.tab-btn { + padding: 1rem 2rem; + background: transparent; + border: none; + border-bottom: 3px solid transparent; + color: var(--text-secondary); + cursor: pointer; + transition: all 0.2s; + font-family: inherit; + font-size: 0.95rem; + margin-bottom: -2px; + white-space: nowrap; + font-weight: 500; +} + +.tab-btn:hover { + color: var(--primary-cyan); + background: rgba(80, 255, 255, 0.05); +} + +.tab-btn.active { + color: var(--primary-cyan); + border-bottom-color: var(--primary-cyan); + background: var(--bg-secondary); +} + .app-nav { max-width: 1800px; margin: 2rem auto 0; @@ -228,34 +263,167 @@ border-bottom-color: var(--primary-cyan); } -/* Content Sections */ -.app-content { +/* Main Content Wrapper */ +.app-main { max-width: 1800px; margin: 2rem auto; padding: 0 2rem; } +/* Content Sections */ +.app-content { + background: var(--bg-secondary); + border: 1px solid var(--border-color); + padding: 0; +} + .tab-content { display: none; + padding: 2rem; } .tab-content.active { display: block; } -.docs-content { - max-width: 1200px; - padding: 2rem; +/* Overview Layout */ +.overview-columns { + display: grid; + grid-template-columns: 2fr 1fr; + gap: 2rem; +} + +.overview-main h2, .overview-main h3 { + color: var(--primary-cyan); + margin-top: 2rem; + margin-bottom: 1rem; +} + +.overview-main h2:first-child { + margin-top: 0; +} + +.overview-main h2 { + font-size: 1.8rem; + border-bottom: 2px solid var(--border-color); + padding-bottom: 0.5rem; +} + +.overview-main h3 { + font-size: 1.3rem; +} + +.features-list { + list-style: none; + padding: 0; +} + +.features-list li { + padding: 0.5rem 0; + padding-left: 1.5rem; + position: relative; + color: var(--text-secondary); +} + +.features-list li:before { + content: "▸"; + position: absolute; + left: 0; + color: var(--primary-cyan); +} + +.use-cases p { + color: var(--text-secondary); + line-height: 1.6; +} + +/* Sidebar */ +.sidebar { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.sidebar-card { background: var(--bg-secondary); border: 1px solid var(--border-color); + padding: 1.5rem; +} + +.sidebar-card h3 { + font-size: 1.1rem; + color: var(--primary-cyan); + margin: 0 0 1rem 0; + border-bottom: 1px solid var(--border-color); + padding-bottom: 0.5rem; +} + +.stats-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; +} + +.stats-grid > div { + text-align: center; +} + +.metadata { + margin: 0; +} + +.metadata div { + display: flex; + justify-content: space-between; + padding: 0.75rem 0; + border-bottom: 1px solid var(--border-color); +} + +.metadata dt { + color: var(--text-tertiary); + font-weight: normal; +} + +.metadata dd { + color: var(--text-primary); + margin: 0; + font-weight: 600; +} + +.sidebar-card p { + color: var(--text-secondary); + margin: 0; +} + +/* Integration Content */ +.integration-content { + max-width: 100%; +} + +.integration-content h2 { + font-size: 1.8rem; + color: var(--primary-cyan); + margin: 0 0 2rem 0; + padding-bottom: 0.5rem; + border-bottom: 2px solid var(--border-color); +} + +.integration-content h3 { + font-size: 1.3rem; + color: var(--text-primary); + margin: 2rem 0 1rem; +} + +.docs-content { + max-width: 100%; } .docs-content h2 { font-size: 1.8rem; color: var(--primary-cyan); - margin-bottom: 1rem; + margin: 0 0 1.5rem 0; padding-bottom: 0.5rem; - border-bottom: 1px solid var(--border-color); + border-bottom: 2px solid var(--border-color); } .docs-content h3 { @@ -290,6 +458,7 @@ border: 1px solid var(--border-color); margin: 1rem 0; overflow: hidden; + position: relative; } .code-header { @@ -308,18 +477,23 @@ } .copy-btn { - padding: 0.25rem 0.5rem; - background: transparent; + position: absolute; + top: 0.5rem; + right: 0.5rem; + padding: 0.4rem 0.8rem; + background: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-secondary); cursor: pointer; font-size: 0.75rem; transition: all 0.2s; + z-index: 10; } .copy-btn:hover { border-color: var(--primary-cyan); color: var(--primary-cyan); + background: var(--bg-secondary); } .code-block pre { @@ -435,6 +609,10 @@ .app-stats { justify-content: space-around; } + + .overview-columns { + grid-template-columns: 1fr; + } } @media (max-width: 768px) { @@ -446,6 +624,16 @@ flex-direction: column; } + .tabs { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } + + .tab-btn { + padding: 0.75rem 1.5rem; + font-size: 0.875rem; + } + .app-nav { overflow-x: auto; gap: 0; @@ -459,4 +647,12 @@ .support-grid { grid-template-columns: 1fr; } + + .tab-content { + padding: 1rem; + } + + .app-main { + padding: 0 1rem; + } } \ No newline at end of file diff --git a/docs/md_v2/marketplace/app-detail.html b/docs/md_v2/marketplace/app-detail.html new file mode 100644 index 00000000..ef1138a8 --- /dev/null +++ b/docs/md_v2/marketplace/app-detail.html @@ -0,0 +1,209 @@ + + + + + + App Details - Crawl4AI Marketplace + + + + +
+ +
+
+
+
+ +

+ [ + Marketplace + ] +

+
+
+ +
+
+ + +
+
+
+ +
+
+
+ Open Source + + +
+

App Name

+

App description goes here

+ +
+
+ ★★★★★ + Rating +
+
+ 0 + Downloads +
+
+ Category + Category +
+
+ + +
+
+
+ + +
+
+
+ + + + +
+ +
+
+
+

Overview

+
Overview content goes here.
+ +

Key Features

+
    +
  • Feature 1
  • +
  • Feature 2
  • +
  • Feature 3
  • +
+ +

Use Cases

+
+

Describe how this app can help your workflow.

+
+
+ + +
+
+ +
+
+

Integration Guide

+ +

Installation

+
+
# Installation instructions will appear here
+
+ +

Basic Usage

+
+
# Usage example will appear here
+
+ +

Complete Integration Example

+
+ +
# Complete integration guide will appear here
+
+
+
+ +
+
+

Documentation

+
+

Documentation coming soon.

+
+
+
+ +
+
+

Support

+
+
+

📧 Contact

+

contact@example.com

+
+
+

🐛 Report Issues

+

Found a bug? Report it on GitHub Issues.

+
+
+

💬 Community

+

Join our Discord for help and discussions.

+
+
+
+
+
+ +
+ + + +
+ + + + diff --git a/docs/md_v2/marketplace/app-detail.js b/docs/md_v2/marketplace/app-detail.js index 622f9c88..f470bf51 100644 --- a/docs/md_v2/marketplace/app-detail.js +++ b/docs/md_v2/marketplace/app-detail.js @@ -1,5 +1,15 @@ // App Detail Page JavaScript -const API_BASE = '/marketplace/api'; +const { API_BASE, API_ORIGIN } = (() => { + const { hostname, port, protocol } = window.location; + const isLocalHost = ['localhost', '127.0.0.1', '0.0.0.0'].includes(hostname); + + if (isLocalHost && port && port !== '8100') { + const origin = `${protocol}//127.0.0.1:8100`; + return { API_BASE: `${origin}/marketplace/api`, API_ORIGIN: origin }; + } + + return { API_BASE: '/marketplace/api', API_ORIGIN: '' }; +})(); class AppDetailPage { constructor() { @@ -70,7 +80,6 @@ class AppDetailPage { document.getElementById('app-description').textContent = this.appData.description; document.getElementById('app-type').textContent = this.appData.type || 'Open Source'; document.getElementById('app-category').textContent = this.appData.category; - document.getElementById('app-pricing').textContent = this.appData.pricing || 'Free'; // Badges if (this.appData.featured) { @@ -105,6 +114,15 @@ class AppDetailPage { // Contact document.getElementById('app-contact').textContent = this.appData.contact_email || 'Not available'; + // Sidebar info + document.getElementById('sidebar-downloads').textContent = this.formatNumber(this.appData.downloads || 0); + document.getElementById('sidebar-rating').textContent = (this.appData.rating || 0).toFixed(1); + document.getElementById('sidebar-category').textContent = this.appData.category || '-'; + document.getElementById('sidebar-type').textContent = this.appData.type || '-'; + document.getElementById('sidebar-status').textContent = this.appData.status || 'Active'; + document.getElementById('sidebar-pricing').textContent = this.appData.pricing || 'Free'; + document.getElementById('sidebar-contact').textContent = this.appData.contact_email || 'contact@example.com'; + // Integration guide this.renderIntegrationGuide(); } @@ -112,24 +130,27 @@ class AppDetailPage { renderIntegrationGuide() { // Installation code const installCode = document.getElementById('install-code'); - if (this.appData.type === 'Open Source' && this.appData.github_url) { - installCode.textContent = `# Clone from GitHub + if (installCode) { + if (this.appData.type === 'Open Source' && this.appData.github_url) { + installCode.textContent = `# Clone from GitHub git clone ${this.appData.github_url} # Install dependencies pip install -r requirements.txt`; - } else if (this.appData.name.toLowerCase().includes('api')) { - installCode.textContent = `# Install via pip + } else if (this.appData.name.toLowerCase().includes('api')) { + installCode.textContent = `# Install via pip pip install ${this.appData.slug} # Or install from source pip install git+${this.appData.github_url || 'https://github.com/example/repo'}`; + } } // Usage code - customize based on category const usageCode = document.getElementById('usage-code'); - if (this.appData.category === 'Browser Automation') { - usageCode.textContent = `from crawl4ai import AsyncWebCrawler + if (usageCode) { + if (this.appData.category === 'Browser Automation') { + usageCode.textContent = `from crawl4ai import AsyncWebCrawler from ${this.appData.slug.replace(/-/g, '_')} import ${this.appData.name.replace(/\s+/g, '')} async def main(): @@ -178,11 +199,13 @@ async with AsyncWebCrawler() as crawler: extraction_strategy=strategy ) print(result.extracted_content)`; + } } // Integration example const integrationCode = document.getElementById('integration-code'); - integrationCode.textContent = this.appData.integration_guide || + if (integrationCode) { + integrationCode.textContent = this.appData.integration_guide || `# Complete ${this.appData.name} Integration Example from crawl4ai import AsyncWebCrawler @@ -237,6 +260,7 @@ async def crawl_with_${this.appData.slug.replace(/-/g, '_')}(): if __name__ == "__main__": import asyncio asyncio.run(crawl_with_${this.appData.slug.replace(/-/g, '_')}())`; + } } formatNumber(num) { @@ -250,7 +274,7 @@ if __name__ == "__main__": setupEventListeners() { // Tab switching - const tabs = document.querySelectorAll('.nav-tab'); + const tabs = document.querySelectorAll('.tab-btn'); tabs.forEach(tab => { tab.addEventListener('click', () => { // Update active tab diff --git a/docs/md_v2/marketplace/frontend/app-detail.js b/docs/md_v2/marketplace/frontend/app-detail.js index 622f9c88..5bc86d2b 100644 --- a/docs/md_v2/marketplace/frontend/app-detail.js +++ b/docs/md_v2/marketplace/frontend/app-detail.js @@ -1,5 +1,15 @@ // App Detail Page JavaScript -const API_BASE = '/marketplace/api'; +const { API_BASE, API_ORIGIN } = (() => { + const { hostname, port, protocol } = window.location; + const isLocalHost = ['localhost', '127.0.0.1', '0.0.0.0'].includes(hostname); + + if (isLocalHost && port && port !== '8100') { + const origin = `${protocol}//127.0.0.1:8100`; + return { API_BASE: `${origin}/marketplace/api`, API_ORIGIN: origin }; + } + + return { API_BASE: '/marketplace/api', API_ORIGIN: '' }; +})(); class AppDetailPage { constructor() {