From 97c92c4f62dc3ece3912776229e6909e8a1f4af7 Mon Sep 17 00:00:00 2001
From: ntohidi
Date: Tue, 21 Oct 2025 15:39:04 +0200
Subject: [PATCH 1/2] fix(marketplace): replace hardcoded app detail content
with database-driven fields.
The app detail page was displaying hardcoded/templated content instead of
using actual data from the database. This prevented admins from controlling
the content shown in Overview, Integration, and Documentation tabs.
---
docs/md_v2/marketplace/admin/admin.js | 30 +++-
docs/md_v2/marketplace/app-detail.js | 199 ++++++++++----------------
2 files changed, 105 insertions(+), 124 deletions(-)
diff --git a/docs/md_v2/marketplace/admin/admin.js b/docs/md_v2/marketplace/admin/admin.js
index d43dd822..ccc6c467 100644
--- a/docs/md_v2/marketplace/admin/admin.js
+++ b/docs/md_v2/marketplace/admin/admin.js
@@ -529,8 +529,29 @@ class AdminDashboard {
-
-
+
+
+ Supports markdown: **bold**, *italic*, [links](url), # headers, etc.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
`;
@@ -712,7 +733,12 @@ class AdminDashboard {
data.contact_email = document.getElementById('form-email').value;
data.featured = document.getElementById('form-featured').checked ? 1 : 0;
data.sponsored = document.getElementById('form-sponsored').checked ? 1 : 0;
+ data.long_description = document.getElementById('form-long-description').value;
+ data.installation_command = document.getElementById('form-installation').value;
+ data.examples = document.getElementById('form-examples').value;
data.integration_guide = document.getElementById('form-integration').value;
+ data.documentation = document.getElementById('form-documentation').value;
+ data.requirements = document.getElementById('form-requirements').value;
} else if (type === 'articles') {
data.title = document.getElementById('form-title').value;
data.slug = this.generateSlug(data.title);
diff --git a/docs/md_v2/marketplace/app-detail.js b/docs/md_v2/marketplace/app-detail.js
index f470bf51..404f484e 100644
--- a/docs/md_v2/marketplace/app-detail.js
+++ b/docs/md_v2/marketplace/app-detail.js
@@ -123,144 +123,99 @@ class AppDetailPage {
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();
+ // Render tab contents from database fields
+ this.renderTabContents();
}
- renderIntegrationGuide() {
- // Installation code
+ renderTabContents() {
+ // Overview tab - use long_description from database
+ const overviewDiv = document.getElementById('app-overview');
+ if (overviewDiv) {
+ if (this.appData.long_description) {
+ overviewDiv.innerHTML = this.renderMarkdown(this.appData.long_description);
+ } else {
+ overviewDiv.innerHTML = `${this.appData.description || 'No overview available.'}
`;
+ }
+ }
+
+ // Documentation tab - use documentation field from database
+ const docsDiv = document.getElementById('app-docs');
+ if (docsDiv) {
+ if (this.appData.documentation) {
+ docsDiv.innerHTML = this.renderMarkdown(this.appData.documentation);
+ } else {
+ docsDiv.innerHTML = 'Documentation coming soon.
';
+ }
+ }
+
+ // Integration tab - use integration_guide, installation_command, examples from database
+ this.renderIntegrationTab();
+ }
+
+ renderIntegrationTab() {
+ // Installation code - use installation_command from database
const installCode = document.getElementById('install-code');
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
-pip install ${this.appData.slug}
-
-# Or install from source
-pip install git+${this.appData.github_url || 'https://github.com/example/repo'}`;
+ if (this.appData.installation_command) {
+ installCode.textContent = this.appData.installation_command;
+ } else {
+ // Fallback to generic installation
+ installCode.textContent = `# Installation instructions not yet available\n# Please check ${this.appData.website_url || 'the official website'} for details`;
}
}
- // Usage code - customize based on category
+ // Usage code - use examples field from database
const usageCode = document.getElementById('usage-code');
- 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():
- # Initialize ${this.appData.name}
- automation = ${this.appData.name.replace(/\s+/g, '')}()
-
- async with AsyncWebCrawler() as crawler:
- result = await crawler.arun(
- url="https://example.com",
- browser_config=automation.config,
- wait_for="css:body"
- )
- print(result.markdown)`;
- } else if (this.appData.category === 'Proxy Services') {
- usageCode.textContent = `from crawl4ai import AsyncWebCrawler
-import ${this.appData.slug.replace(/-/g, '_')}
-
-# Configure proxy
-proxy_config = {
- "server": "${this.appData.website_url || 'https://proxy.example.com'}",
- "username": "your_username",
- "password": "your_password"
-}
-
-async with AsyncWebCrawler(proxy=proxy_config) as crawler:
- result = await crawler.arun(
- url="https://example.com",
- bypass_cache=True
- )
- print(result.status_code)`;
- } else if (this.appData.category === 'LLM Integration') {
- usageCode.textContent = `from crawl4ai import AsyncWebCrawler
-from crawl4ai.extraction_strategy import LLMExtractionStrategy
-
-# Configure LLM extraction
-strategy = LLMExtractionStrategy(
- provider="${this.appData.name.toLowerCase().includes('gpt') ? 'openai' : 'anthropic'}",
- api_key="your-api-key",
- model="${this.appData.name.toLowerCase().includes('gpt') ? 'gpt-4' : 'claude-3'}",
- instruction="Extract structured data"
-)
-
-async with AsyncWebCrawler() as crawler:
- result = await crawler.arun(
- url="https://example.com",
- extraction_strategy=strategy
- )
- print(result.extracted_content)`;
+ if (usageCode && this.appData.examples) {
+ // Extract first code block from examples if it contains multiple
+ const codeMatch = this.appData.examples.match(/```[\s\S]*?```/);
+ if (codeMatch) {
+ usageCode.textContent = codeMatch[0].replace(/```(\w+)?\n?/g, '').trim();
+ } else {
+ usageCode.textContent = this.appData.examples;
}
}
- // Integration example
+ // Complete integration - use integration_guide field from database
const integrationCode = document.getElementById('integration-code');
if (integrationCode) {
- integrationCode.textContent = this.appData.integration_guide ||
-`# Complete ${this.appData.name} Integration Example
-
-from crawl4ai import AsyncWebCrawler
-from crawl4ai.extraction_strategy import JsonCssExtractionStrategy
-import json
-
-async def crawl_with_${this.appData.slug.replace(/-/g, '_')}():
- """
- Complete example showing how to use ${this.appData.name}
- with Crawl4AI for production web scraping
- """
-
- # Define extraction schema
- schema = {
- "name": "ProductList",
- "baseSelector": "div.product",
- "fields": [
- {"name": "title", "selector": "h2", "type": "text"},
- {"name": "price", "selector": ".price", "type": "text"},
- {"name": "image", "selector": "img", "type": "attribute", "attribute": "src"},
- {"name": "link", "selector": "a", "type": "attribute", "attribute": "href"}
- ]
+ if (this.appData.integration_guide) {
+ integrationCode.textContent = this.appData.integration_guide;
+ } else {
+ // Fallback message
+ integrationCode.textContent = `# Integration guide not yet available for ${this.appData.name}\n\n# Please visit the admin panel to add integration instructions\n# Or check ${this.appData.website_url || 'the official website'} for integration details`;
+ }
+ }
}
- # Initialize crawler with ${this.appData.name}
- async with AsyncWebCrawler(
- browser_type="chromium",
- headless=True,
- verbose=True
- ) as crawler:
+ renderMarkdown(text) {
+ if (!text) return '';
- # Crawl with extraction
- result = await crawler.arun(
- url="https://example.com/products",
- extraction_strategy=JsonCssExtractionStrategy(schema),
- cache_mode="bypass",
- wait_for="css:.product",
- screenshot=True
- )
-
- # Process results
- if result.success:
- products = json.loads(result.extracted_content)
- print(f"Found {len(products)} products")
-
- for product in products[:5]:
- print(f"- {product['title']}: {product['price']}")
-
- return products
-
-# Run the crawler
-if __name__ == "__main__":
- import asyncio
- asyncio.run(crawl_with_${this.appData.slug.replace(/-/g, '_')}())`;
- }
+ // Simple markdown rendering (convert to HTML)
+ return text
+ // Headers
+ .replace(/^### (.*$)/gim, '$1
')
+ .replace(/^## (.*$)/gim, '$1
')
+ .replace(/^# (.*$)/gim, '$1
')
+ // Bold
+ .replace(/\*\*(.*?)\*\*/g, '$1')
+ // Italic
+ .replace(/\*(.*?)\*/g, '$1')
+ // Links
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1')
+ // Code blocks
+ .replace(/```(\w+)?\n([\s\S]*?)```/g, '$2
')
+ // Inline code
+ .replace(/`([^`]+)`/g, '$1')
+ // Line breaks
+ .replace(/\n\n/g, '
')
+ .replace(/\n/g, '
')
+ // Lists
+ .replace(/^\* (.*)$/gim, '
$1')
+ .replace(/^- (.*)$/gim, '$1')
+ // Wrap in paragraphs
+ .replace(/^(?!<[h|p|pre|ul|ol|li])/gim, '')
+ .replace(/(?])$/gim, '
');
}
formatNumber(num) {
From 13e116610daa493fb32d5dffa5f8c5c4a87a7c7d Mon Sep 17 00:00:00 2001
From: ntohidi
Date: Thu, 23 Oct 2025 16:12:30 +0200
Subject: [PATCH 2/2] fix(marketplace): improve app detail page content
rendering and UX
Fixed multiple issues with app detail page content display and formatting
---
docs/md_v2/marketplace/admin/admin.js | 31 ++----
docs/md_v2/marketplace/app-detail.css | 25 +++++
docs/md_v2/marketplace/app-detail.html | 40 +------
docs/md_v2/marketplace/app-detail.js | 142 +++++++++++++------------
4 files changed, 112 insertions(+), 126 deletions(-)
diff --git a/docs/md_v2/marketplace/admin/admin.js b/docs/md_v2/marketplace/admin/admin.js
index ccc6c467..7bdc3fc5 100644
--- a/docs/md_v2/marketplace/admin/admin.js
+++ b/docs/md_v2/marketplace/admin/admin.js
@@ -529,29 +529,19 @@ class AdminDashboard {
-
-
- Supports markdown: **bold**, *italic*, [links](url), # headers, etc.
+
+
+ Markdown support: **bold**, *italic*, [links](url), # headers, code blocks, lists
-
-
+
+
+ Single markdown field with installation, examples, and complete guide. Code blocks get auto copy buttons.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ Full documentation with API reference, examples, best practices, etc.
`;
@@ -734,11 +724,8 @@ class AdminDashboard {
data.featured = document.getElementById('form-featured').checked ? 1 : 0;
data.sponsored = document.getElementById('form-sponsored').checked ? 1 : 0;
data.long_description = document.getElementById('form-long-description').value;
- data.installation_command = document.getElementById('form-installation').value;
- data.examples = document.getElementById('form-examples').value;
data.integration_guide = document.getElementById('form-integration').value;
data.documentation = document.getElementById('form-documentation').value;
- data.requirements = document.getElementById('form-requirements').value;
} else if (type === 'articles') {
data.title = document.getElementById('form-title').value;
data.slug = this.generateSlug(data.title);
diff --git a/docs/md_v2/marketplace/app-detail.css b/docs/md_v2/marketplace/app-detail.css
index 590bea03..0e5d0002 100644
--- a/docs/md_v2/marketplace/app-detail.css
+++ b/docs/md_v2/marketplace/app-detail.css
@@ -510,6 +510,31 @@
line-height: 1.5;
}
+/* Markdown rendered code blocks */
+.integration-content pre,
+.docs-content pre {
+ background: var(--bg-dark);
+ border: 1px solid var(--border-color);
+ margin: 1rem 0;
+ padding: 1rem;
+ padding-top: 2.5rem; /* Space for copy button */
+ overflow-x: auto;
+ position: relative;
+ max-height: none; /* Remove any height restrictions */
+ height: auto; /* Allow content to expand */
+}
+
+.integration-content pre code,
+.docs-content pre code {
+ background: transparent;
+ padding: 0;
+ color: var(--text-secondary);
+ font-size: 0.875rem;
+ line-height: 1.5;
+ white-space: pre; /* Preserve whitespace and line breaks */
+ display: block;
+}
+
/* Feature Grid */
.feature-grid {
display: grid;
diff --git a/docs/md_v2/marketplace/app-detail.html b/docs/md_v2/marketplace/app-detail.html
index ef1138a8..fbc8c13d 100644
--- a/docs/md_v2/marketplace/app-detail.html
+++ b/docs/md_v2/marketplace/app-detail.html
@@ -80,20 +80,7 @@
-
Overview
Overview content goes here.
-
-
Key Features
-
- - Feature 1
- - Feature 2
- - Feature 3
-
-
-
Use Cases
-
-
Describe how this app can help your workflow.
-