diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 35d0d343..250b8d7d 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -4,7 +4,11 @@ "Bash(cd:*)", "Bash(python3:*)", "Bash(python:*)", - "Bash(grep:*)" + "Bash(grep:*)", + "Bash(mkdir:*)", + "Bash(cp:*)", + "Bash(rm:*)", + "Bash(true)" ] }, "enableAllProjectMcpServers": false diff --git a/docs/md_v2/apps/assets/DankMono-Bold.woff2 b/docs/md_v2/apps/assets/DankMono-Bold.woff2 new file mode 100644 index 00000000..3072fd85 Binary files /dev/null and b/docs/md_v2/apps/assets/DankMono-Bold.woff2 differ diff --git a/docs/md_v2/apps/assets/DankMono-Italic.woff2 b/docs/md_v2/apps/assets/DankMono-Italic.woff2 new file mode 100644 index 00000000..1d01ea6d Binary files /dev/null and b/docs/md_v2/apps/assets/DankMono-Italic.woff2 differ diff --git a/docs/md_v2/apps/assets/DankMono-Regular.woff2 b/docs/md_v2/apps/assets/DankMono-Regular.woff2 new file mode 100644 index 00000000..99c1425c Binary files /dev/null and b/docs/md_v2/apps/assets/DankMono-Regular.woff2 differ diff --git a/docs/md_v2/apps/crawl4ai-assistant/README.md b/docs/md_v2/apps/crawl4ai-assistant/README.md new file mode 100644 index 00000000..9d3841f1 --- /dev/null +++ b/docs/md_v2/apps/crawl4ai-assistant/README.md @@ -0,0 +1,123 @@ +# Crawl4AI Chrome Extension + +Visual schema and script builder for Crawl4AI - Build extraction schemas by clicking on webpage elements! + +## ๐Ÿš€ Features + +- **Visual Schema Builder**: Click on elements to build extraction schemas +- **Smart Element Selection**: Container and field selection with visual feedback +- **Code Generation**: Generates complete Python code with LLM integration +- **Beautiful Dark UI**: Consistent with Crawl4AI's design language +- **One-Click Download**: Get your generated code instantly + +## ๐Ÿ“ฆ Installation + +### Method 1: Load Unpacked Extension (Recommended for Development) + +1. Open Chrome and navigate to `chrome://extensions/` +2. Enable "Developer mode" in the top right corner +3. Click "Load unpacked" +4. Select the `crawl4ai-assistant` folder +5. The extension icon (๐Ÿš€๐Ÿค–) will appear in your toolbar + +### Method 2: Generate Icons First + +If you want proper icons: + +1. Open `icons/generate_icons.html` in your browser +2. Right-click each canvas and save as: + - `icon-16.png` + - `icon-48.png` + - `icon-128.png` +3. Then follow Method 1 above + +## ๐ŸŽฏ How to Use + +### Building a Schema + +1. **Navigate to any website** you want to extract data from +2. **Click the Crawl4AI extension icon** in your toolbar +3. **Click "Schema Builder"** to start the capture mode +4. **Select a container element**: + - Hover over elements (they'll highlight in blue) + - Click on a repeating container (e.g., product card, article block) +5. **Select fields within the container**: + - Elements will now highlight in green + - Click on each piece of data you want to extract + - Name each field (e.g., "title", "price", "description") +6. **Generate the code**: + - Click "Generate Code" in the extension popup + - A Python file will automatically download + +### Running the Generated Code + +The downloaded Python file contains: + +```python +# 1. The HTML snippet of your selected container +HTML_SNIPPET = """...""" + +# 2. The extraction query based on your selections +EXTRACTION_QUERY = """...""" + +# 3. Functions to generate and test the schema +async def generate_schema(): + # Generates the extraction schema using LLM + +async def test_extraction(): + # Tests the schema on the actual website +``` + +To use it: + +1. Install Crawl4AI: `pip install crawl4ai` +2. Run the script: `python crawl4ai_schema_*.py` +3. The script will generate a `generated_schema.json` file +4. Use this schema in your Crawl4AI projects! + +## ๐ŸŽจ Visual Feedback + +- **Blue dashed outline**: Container selection mode +- **Green dashed outline**: Field selection mode +- **Solid blue outline**: Selected container +- **Solid green outline**: Selected fields +- **Floating toolbar**: Shows current mode and selection status + +## โŒจ๏ธ Keyboard Shortcuts + +- **ESC**: Cancel current capture session + +## ๐Ÿ”ง Technical Details + +- Built with Manifest V3 for security and performance +- Pure client-side - no data sent to external servers +- Generates code that uses Crawl4AI's LLM integration +- Smart selector generation prioritizes stable attributes + +## ๐Ÿ› Troubleshooting + +### Extension doesn't load +- Make sure you're in Developer Mode +- Check the console for any errors +- Ensure all files are in the correct directories + +### Can't select elements +- Some websites may block extensions +- Try refreshing the page +- Make sure you clicked "Schema Builder" first + +### Generated code doesn't work +- Ensure you have Crawl4AI installed +- Check that you have an LLM API key configured +- Make sure the website structure hasn't changed + +## ๐Ÿค Contributing + +This extension is part of the Crawl4AI project. Contributions are welcome! + +- Report issues: [GitHub Issues](https://github.com/unclecode/crawl4ai/issues) +- Join discussion: [Discord](https://discord.gg/crawl4ai) + +## ๐Ÿ“„ License + +Same as Crawl4AI - see main project for details. \ No newline at end of file diff --git a/docs/md_v2/apps/crawl4ai-assistant/assets/DankMono-Bold.woff2 b/docs/md_v2/apps/crawl4ai-assistant/assets/DankMono-Bold.woff2 new file mode 100644 index 00000000..3072fd85 Binary files /dev/null and b/docs/md_v2/apps/crawl4ai-assistant/assets/DankMono-Bold.woff2 differ diff --git a/docs/md_v2/apps/crawl4ai-assistant/assets/DankMono-Italic.woff2 b/docs/md_v2/apps/crawl4ai-assistant/assets/DankMono-Italic.woff2 new file mode 100644 index 00000000..1d01ea6d Binary files /dev/null and b/docs/md_v2/apps/crawl4ai-assistant/assets/DankMono-Italic.woff2 differ diff --git a/docs/md_v2/apps/crawl4ai-assistant/assets/DankMono-Regular.woff2 b/docs/md_v2/apps/crawl4ai-assistant/assets/DankMono-Regular.woff2 new file mode 100644 index 00000000..99c1425c Binary files /dev/null and b/docs/md_v2/apps/crawl4ai-assistant/assets/DankMono-Regular.woff2 differ diff --git a/docs/md_v2/apps/crawl4ai-assistant/assistant.css b/docs/md_v2/apps/crawl4ai-assistant/assistant.css new file mode 100644 index 00000000..4b0aac90 --- /dev/null +++ b/docs/md_v2/apps/crawl4ai-assistant/assistant.css @@ -0,0 +1,539 @@ +/* Crawl4AI Assistant Landing Page Styles */ + +/* Font Face Definitions */ +@font-face { + font-family: 'Dank Mono'; + src: url('../assets/DankMono-Regular.woff2') format('woff2'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Dank Mono'; + src: url('../assets/DankMono-Bold.woff2') format('woff2'); + font-weight: 700; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Dank Mono'; + src: url('../assets/DankMono-Italic.woff2') format('woff2'); + font-weight: 400; + font-style: italic; + font-display: swap; +} + +:root { + --primary-green: #0fbbaa; + --primary-pink: #f380f5; + --bg-dark: #070708; + --bg-secondary: #1a1a1a; + --bg-tertiary: #3f3f44; + --text-primary: #e8e9ed; + --text-secondary: #a3abba; + --text-accent: #d5cec0; + --border-color: #3f3f44; + --code-bg: #070708; + --font-primary: 'Dank Mono', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, monospace; + --font-code: 'Dank Mono', 'Monaco', 'Menlo', 'Consolas', monospace; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + background-color: var(--bg-dark); + color: var(--text-primary); + font-family: var(--font-primary); + line-height: 1.6; + overflow-x: hidden; +} + +/* Terminal Container */ +.terminal-container { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +/* Header */ +.header { + background: var(--bg-secondary); + border-bottom: 1px solid var(--border-color); + padding: 1.5rem 0; + position: sticky; + top: 0; + z-index: 100; + backdrop-filter: blur(10px); + background: rgba(26, 26, 26, 0.95); +} + +.header-content { + max-width: 1200px; + margin: 0 auto; + padding: 0 2rem; + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo-section { + display: flex; + align-items: center; + gap: 1rem; +} + +.logo { + width: 48px; + height: 48px; +} + +.logo-section h1 { + font-size: 1.75rem; + font-weight: 700; + color: var(--text-primary); +} + +.tagline { + font-size: 0.875rem; + color: var(--text-secondary); + margin-top: 0.25rem; +} + +.nav-links { + display: flex; + gap: 2rem; +} + +.nav-link { + color: var(--text-secondary); + text-decoration: none; + font-size: 0.875rem; + transition: color 0.2s ease; +} + +.nav-link:hover { + color: var(--primary-green); +} + +/* Content */ +.content { + flex: 1; + max-width: 1200px; + margin: 0 auto; + padding: 0 2rem; + width: 100%; +} + +/* Video Section */ +.video-section { + margin: 3rem 0; +} + +.video-wrapper { + position: relative; + border-radius: 12px; + overflow: hidden; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6); + border: 1px solid var(--border-color); +} + +.demo-video { + width: 100%; + height: auto; + display: block; +} + +/* Terminal Windows */ +.terminal-window { + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 8px; + overflow: hidden; + margin-bottom: 2rem; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); +} + +.terminal-header { + background: #2a2a2a; + padding: 0.75rem 1rem; + border-bottom: 1px solid var(--border-color); + display: flex; + align-items: center; + gap: 0.5rem; +} + +.terminal-header::before { + content: ''; + display: flex; + gap: 8px; + align-items: center; +} + +.terminal-header::before { + content: 'โ— โ— โ—'; + color: #ff5f57; + font-size: 12px; + letter-spacing: 8px; +} + +.terminal-title { + font-size: 0.875rem; + color: var(--text-secondary); + margin-left: 1rem; +} + +.terminal-content { + padding: 2rem; +} + +/* Features Grid */ +.features-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 1.5rem; + margin-top: 2rem; +} + +.feature-card { + background: var(--bg-tertiary); + padding: 1.5rem; + border-radius: 8px; + border: 1px solid var(--border-color); + transition: all 0.2s ease; +} + +.feature-card:hover { + border-color: var(--primary-green); + transform: translateY(-2px); + box-shadow: 0 4px 16px rgba(15, 187, 170, 0.2); +} + +.feature-icon { + font-size: 2rem; + margin-bottom: 1rem; + display: block; +} + +.feature-card h3 { + font-size: 1.125rem; + margin-bottom: 0.5rem; + color: var(--text-primary); +} + +.feature-card p { + font-size: 0.875rem; + color: var(--text-secondary); +} + +/* Installation Steps */ +.installation-steps { + margin-top: 1.5rem; +} + +.step { + display: flex; + gap: 1.5rem; + margin-bottom: 2rem; + align-items: flex-start; +} + +.step-number { + background: var(--primary-green); + color: var(--bg-dark); + width: 40px; + height: 40px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; + flex-shrink: 0; +} + +.step-content h4 { + margin-bottom: 0.5rem; + color: var(--text-primary); +} + +.step-content p { + color: var(--text-secondary); + margin-bottom: 1rem; +} + +.download-button { + display: inline-flex; + align-items: center; + gap: 0.75rem; + background: var(--primary-green); + color: var(--bg-dark); + padding: 0.75rem 1.5rem; + border-radius: 6px; + text-decoration: none; + font-weight: 600; + transition: all 0.2s ease; + margin-top: 1rem; +} + +.download-button:hover { + background: #1fcbba; + transform: translateY(-2px); + box-shadow: 0 4px 16px rgba(15, 187, 170, 0.3); +} + +.button-icon { + font-size: 1.25rem; +} + +/* Usage Flow */ +.usage-flow { + margin-top: 1.5rem; +} + +.usage-step { + background: var(--bg-tertiary); + padding: 1.5rem; + border-radius: 8px; + margin-bottom: 1rem; + border: 1px solid var(--border-color); +} + +.usage-header { + display: flex; + align-items: center; + gap: 1rem; + margin-bottom: 0.75rem; +} + +.usage-icon { + font-size: 1.5rem; +} + +.usage-step h4 { + margin: 0; + color: var(--text-primary); +} + +.usage-step p { + color: var(--text-secondary); + margin-bottom: 0.5rem; +} + +/* Code Snippets */ +.code-snippet { + background: var(--code-bg); + padding: 0.75rem; + border-radius: 4px; + margin-top: 0.5rem; + font-family: var(--font-code); + font-size: 0.875rem; + border: 1px solid var(--border-color); +} + +.comment { + color: var(--text-secondary); + font-style: italic; +} + +/* Code Section */ +pre { + margin: 0; + overflow-x: auto; +} + +code { + font-family: var(--font-code); + font-size: 0.875rem; + line-height: 1.6; +} + +.keyword { + color: var(--primary-green); + font-weight: bold; +} + +.string { + color: var(--primary-pink); +} + +.function { + color: #ff3c74; +} + +/* Coming Soon Section */ +.coming-soon-section { + margin: 4rem 0; +} + +.coming-soon-section h2 { + font-size: 2rem; + margin-bottom: 2rem; + color: var(--text-primary); +} + +.intro-text { + font-size: 1.125rem; + color: var(--text-secondary); + margin-bottom: 2rem; +} + +.coming-features { + display: grid; + gap: 1.5rem; + margin-top: 2rem; +} + +.coming-feature { + background: var(--bg-tertiary); + padding: 2rem; + border-radius: 12px; + border: 1px solid var(--border-color); + transition: all 0.3s ease; +} + +.coming-feature:hover { + border-color: var(--primary-green); + transform: translateY(-2px); + box-shadow: 0 8px 24px rgba(15, 187, 170, 0.2); +} + +.feature-header { + display: flex; + align-items: center; + gap: 1rem; + margin-bottom: 1rem; +} + +.feature-badge { + background: var(--primary-green); + color: var(--bg-dark); + padding: 0.25rem 0.75rem; + border-radius: 20px; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; +} + +.coming-feature h3 { + font-size: 1.25rem; + color: var(--text-primary); + margin: 0; +} + +.coming-feature p { + color: var(--text-secondary); + margin-bottom: 1rem; + line-height: 1.6; +} + +.feature-preview { + background: var(--bg-secondary); + padding: 1rem; + border-radius: 6px; + font-family: var(--font-code); + font-size: 0.875rem; + color: var(--text-accent); + border: 1px solid var(--border-color); +} + +.stay-tuned { + text-align: center; + margin-top: 3rem; + padding: 2rem; + background: var(--bg-tertiary); + border-radius: 12px; + border: 1px solid var(--border-color); +} + +.stay-tuned p { + font-size: 1.125rem; + color: var(--text-primary); +} + +.stay-tuned a { + color: var(--primary-green); + text-decoration: none; + font-weight: 600; +} + +.stay-tuned a:hover { + text-decoration: underline; +} + +/* Footer */ +.footer { + background: var(--bg-secondary); + border-top: 1px solid var(--border-color); + margin-top: 4rem; + padding: 3rem 0 2rem; +} + +.footer-content { + max-width: 1200px; + margin: 0 auto; + padding: 0 2rem; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 3rem; +} + +.footer-section h4 { + margin-bottom: 1rem; + color: var(--text-primary); +} + +.footer-section ul { + list-style: none; +} + +.footer-section li { + margin-bottom: 0.5rem; +} + +.footer-section a { + color: var(--text-secondary); + text-decoration: none; + transition: color 0.2s ease; +} + +.footer-section a:hover { + color: var(--primary-green); +} + +.footer-bottom { + text-align: center; + margin-top: 2rem; + padding-top: 2rem; + border-top: 1px solid var(--border-color); + color: var(--text-secondary); +} + +/* Responsive */ +@media (max-width: 768px) { + .header-content { + flex-direction: column; + gap: 1.5rem; + } + + .nav-links { + gap: 1rem; + } + + .features-grid { + grid-template-columns: 1fr; + } + + .step { + flex-direction: column; + gap: 1rem; + } + + .coming-soon-section h2 { + font-size: 1.5rem; + } +} \ No newline at end of file diff --git a/docs/md_v2/apps/crawl4ai-assistant/background/service-worker.js b/docs/md_v2/apps/crawl4ai-assistant/background/service-worker.js new file mode 100644 index 00000000..30b8b37b --- /dev/null +++ b/docs/md_v2/apps/crawl4ai-assistant/background/service-worker.js @@ -0,0 +1,39 @@ +// Service worker for Crawl4AI Assistant + +// Handle messages from content script +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + if (message.action === 'downloadCode') { + try { + // Create a data URL for the Python code + const dataUrl = 'data:text/plain;charset=utf-8,' + encodeURIComponent(message.code); + + // Download the file + chrome.downloads.download({ + url: dataUrl, + filename: message.filename || 'crawl4ai_schema.py', + saveAs: true + }, (downloadId) => { + if (chrome.runtime.lastError) { + console.error('Download failed:', chrome.runtime.lastError); + sendResponse({ success: false, error: chrome.runtime.lastError.message }); + } else { + console.log('Download started with ID:', downloadId); + sendResponse({ success: true, downloadId: downloadId }); + } + }); + } catch (error) { + console.error('Error creating download:', error); + sendResponse({ success: false, error: error.message }); + } + + return true; // Keep the message channel open for async response + } + + return false; +}); + +// Clean up on extension install/update +chrome.runtime.onInstalled.addListener(() => { + // Clear any stored state + chrome.storage.local.clear(); +}); \ No newline at end of file diff --git a/docs/md_v2/apps/crawl4ai-assistant/content/content.js b/docs/md_v2/apps/crawl4ai-assistant/content/content.js new file mode 100644 index 00000000..9c9519c0 --- /dev/null +++ b/docs/md_v2/apps/crawl4ai-assistant/content/content.js @@ -0,0 +1,738 @@ +// Content script for Crawl4AI Assistant +class SchemaBuilder { + constructor() { + this.mode = null; + this.container = null; + this.fields = []; + this.overlay = null; + this.toolbar = null; + this.highlightBox = null; + this.selectedElements = new Set(); + this.isPaused = false; + this.codeModal = null; + + this.handleMouseMove = this.handleMouseMove.bind(this); + this.handleClick = this.handleClick.bind(this); + this.handleKeyPress = this.handleKeyPress.bind(this); + } + + start() { + this.mode = 'container'; + this.createOverlay(); + this.createToolbar(); + this.attachEventListeners(); + this.updateToolbar(); + } + + stop() { + this.detachEventListeners(); + this.overlay?.remove(); + this.toolbar?.remove(); + this.highlightBox?.remove(); + this.removeAllHighlights(); + this.mode = null; + this.container = null; + this.fields = []; + this.selectedElements.clear(); + } + + createOverlay() { + // Create highlight box + this.highlightBox = document.createElement('div'); + this.highlightBox.className = 'c4ai-highlight-box'; + document.body.appendChild(this.highlightBox); + } + + createToolbar() { + this.toolbar = document.createElement('div'); + this.toolbar.className = 'c4ai-toolbar'; + this.toolbar.innerHTML = ` +
+
+ + + +
+ Crawl4AI +
Crawl4AI Schema Builder
+
+
+
+
+ Mode: + Select Container +
+
+ Container: + Not selected +
+
+ +
+ Click on a container element (e.g., product card, article, etc.) +
+
+ + +
+
+ `; + document.body.appendChild(this.toolbar); + + // Add event listeners for toolbar buttons + document.getElementById('c4ai-pause').addEventListener('click', () => this.togglePause()); + document.getElementById('c4ai-generate').addEventListener('click', () => this.stopAndGenerate()); + document.getElementById('c4ai-close').addEventListener('click', () => this.stop()); + + // Make toolbar draggable + this.makeDraggable(this.toolbar); + } + + attachEventListeners() { + document.addEventListener('mousemove', this.handleMouseMove, true); + document.addEventListener('click', this.handleClick, true); + document.addEventListener('keydown', this.handleKeyPress, true); + } + + detachEventListeners() { + document.removeEventListener('mousemove', this.handleMouseMove, true); + document.removeEventListener('click', this.handleClick, true); + document.removeEventListener('keydown', this.handleKeyPress, true); + } + + handleMouseMove(e) { + if (this.isPaused) return; + + const element = document.elementFromPoint(e.clientX, e.clientY); + if (element && !this.isOurElement(element)) { + this.highlightElement(element); + } + } + + handleClick(e) { + if (this.isPaused) return; + + const element = e.target; + + if (this.isOurElement(element)) { + return; + } + + e.preventDefault(); + e.stopPropagation(); + + if (this.mode === 'container') { + this.selectContainer(element); + } else if (this.mode === 'field') { + this.selectField(element); + } + } + + handleKeyPress(e) { + if (e.key === 'Escape') { + this.stop(); + } + } + + isOurElement(element) { + return element.classList.contains('c4ai-highlight-box') || + element.classList.contains('c4ai-toolbar') || + element.closest('.c4ai-toolbar') || + element.closest('.c4ai-field-dialog') || + element.closest('.c4ai-code-modal'); + } + + makeDraggable(element) { + let isDragging = false; + let startX, startY, initialX, initialY; + + const titlebar = element.querySelector('.c4ai-toolbar-titlebar'); + + titlebar.addEventListener('mousedown', (e) => { + // Don't drag if clicking on buttons + if (e.target.classList.contains('c4ai-dot')) return; + + isDragging = true; + startX = e.clientX; + startY = e.clientY; + + const rect = element.getBoundingClientRect(); + initialX = rect.left; + initialY = rect.top; + + element.style.transition = 'none'; + titlebar.style.cursor = 'grabbing'; + }); + + document.addEventListener('mousemove', (e) => { + if (!isDragging) return; + + const deltaX = e.clientX - startX; + const deltaY = e.clientY - startY; + + element.style.left = `${initialX + deltaX}px`; + element.style.top = `${initialY + deltaY}px`; + element.style.right = 'auto'; + }); + + document.addEventListener('mouseup', () => { + if (isDragging) { + isDragging = false; + element.style.transition = ''; + titlebar.style.cursor = 'grab'; + } + }); + } + + togglePause() { + this.isPaused = !this.isPaused; + const pauseBtn = document.getElementById('c4ai-pause'); + if (this.isPaused) { + pauseBtn.innerHTML = 'โ–ถ Resume'; + pauseBtn.classList.add('c4ai-paused'); + this.highlightBox.style.display = 'none'; + } else { + pauseBtn.innerHTML = 'โธ Pause'; + pauseBtn.classList.remove('c4ai-paused'); + } + } + + stopAndGenerate() { + if (!this.container || this.fields.length === 0) { + alert('Please select a container and at least one field before generating code.'); + return; + } + + const code = this.generateCode(); + this.showCodeModal(code); + } + + highlightElement(element) { + const rect = element.getBoundingClientRect(); + this.highlightBox.style.cssText = ` + left: ${rect.left + window.scrollX}px; + top: ${rect.top + window.scrollY}px; + width: ${rect.width}px; + height: ${rect.height}px; + display: block; + `; + + if (this.mode === 'container') { + this.highlightBox.className = 'c4ai-highlight-box c4ai-container-mode'; + } else { + this.highlightBox.className = 'c4ai-highlight-box c4ai-field-mode'; + } + } + + selectContainer(element) { + // Remove previous container highlight + if (this.container) { + this.container.element.classList.remove('c4ai-selected-container'); + } + + this.container = { + element: element, + html: element.outerHTML, + selector: this.generateSelector(element), + tagName: element.tagName.toLowerCase() + }; + + element.classList.add('c4ai-selected-container'); + this.mode = 'field'; + this.updateToolbar(); + this.updateStats(); + } + + selectField(element) { + // Don't select the container itself + if (element === this.container.element) { + return; + } + + // Check if already selected - if so, deselect it + if (this.selectedElements.has(element)) { + this.deselectField(element); + return; + } + + // Must be inside the container + if (!this.container.element.contains(element)) { + return; + } + + this.showFieldDialog(element); + } + + deselectField(element) { + // Remove from fields array + this.fields = this.fields.filter(f => f.element !== element); + + // Remove from selected elements set + this.selectedElements.delete(element); + + // Remove visual selection + element.classList.remove('c4ai-selected-field'); + + // Update UI + this.updateToolbar(); + this.updateStats(); + } + + showFieldDialog(element) { + const dialog = document.createElement('div'); + dialog.className = 'c4ai-field-dialog'; + + const rect = element.getBoundingClientRect(); + dialog.style.cssText = ` + left: ${rect.left + window.scrollX}px; + top: ${rect.bottom + window.scrollY + 10}px; + `; + + dialog.innerHTML = ` +
+

Name this field:

+ +
+ Content: ${element.textContent.trim().substring(0, 50)}... +
+
+ + +
+
+ `; + + document.body.appendChild(dialog); + + const input = dialog.querySelector('#c4ai-field-name'); + const saveBtn = dialog.querySelector('#c4ai-field-save'); + const cancelBtn = dialog.querySelector('#c4ai-field-cancel'); + + const save = () => { + const fieldName = input.value.trim(); + if (fieldName) { + this.fields.push({ + name: fieldName, + value: element.textContent.trim(), + element: element, + selector: this.generateSelector(element, this.container.element) + }); + + element.classList.add('c4ai-selected-field'); + this.selectedElements.add(element); + this.updateToolbar(); + this.updateStats(); + } + dialog.remove(); + }; + + const cancel = () => { + dialog.remove(); + }; + + saveBtn.addEventListener('click', save); + cancelBtn.addEventListener('click', cancel); + input.addEventListener('keypress', (e) => { + if (e.key === 'Enter') save(); + if (e.key === 'Escape') cancel(); + }); + + input.focus(); + } + + generateSelector(element, context = document) { + // Try to generate a robust selector + if (element.id) { + return `#${CSS.escape(element.id)}`; + } + + // Check for data attributes (most stable) + const dataAttrs = ['data-testid', 'data-id', 'data-test', 'data-cy']; + for (const attr of dataAttrs) { + const value = element.getAttribute(attr); + if (value) { + return `[${attr}="${value}"]`; + } + } + + // Check for aria-label + if (element.getAttribute('aria-label')) { + return `[aria-label="${element.getAttribute('aria-label')}"]`; + } + + // Try semantic HTML elements with text + const tagName = element.tagName.toLowerCase(); + if (['button', 'a', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tagName)) { + const text = element.textContent.trim(); + if (text && text.length < 50) { + // Use tag name with partial text match + return `${tagName}`; + } + } + + // Check for simple, non-utility classes + const classes = Array.from(element.classList) + .filter(c => !c.startsWith('c4ai-')) // Exclude our classes + .filter(c => !c.includes('[') && !c.includes('(') && !c.includes(':')) // Exclude utility classes + .filter(c => c.length < 30); // Exclude very long classes + + if (classes.length > 0 && classes.length <= 3) { + const selector = classes.map(c => `.${CSS.escape(c)}`).join(''); + try { + if (context.querySelectorAll(selector).length === 1) { + return selector; + } + } catch (e) { + // Invalid selector, continue + } + } + + // Use nth-child with simple parent tag + const parent = element.parentElement; + if (parent && parent !== context) { + const siblings = Array.from(parent.children); + const index = siblings.indexOf(element) + 1; + // Just use parent tag name to avoid recursion + const parentTag = parent.tagName.toLowerCase(); + return `${parentTag} > ${tagName}:nth-child(${index})`; + } + + // Final fallback + return tagName; + } + + updateToolbar() { + document.getElementById('c4ai-mode').textContent = + this.mode === 'container' ? 'Select Container' : 'Select Fields'; + + document.getElementById('c4ai-container').textContent = + this.container ? `${this.container.tagName} โœ“` : 'Not selected'; + + // Update fields list + const fieldsList = document.getElementById('c4ai-fields-list'); + const fieldsItems = document.getElementById('c4ai-fields-items'); + + if (this.fields.length > 0) { + fieldsList.style.display = 'block'; + fieldsItems.innerHTML = this.fields.map(field => ` +
  • + ${field.name} + ${field.value.substring(0, 30)}${field.value.length > 30 ? '...' : ''} +
  • + `).join(''); + } else { + fieldsList.style.display = 'none'; + } + + const hint = document.getElementById('c4ai-hint'); + if (this.mode === 'container') { + hint.textContent = 'Click on a container element (e.g., product card, article, etc.)'; + } else if (this.fields.length === 0) { + hint.textContent = 'Click on fields inside the container to extract (title, price, etc.)'; + } else { + hint.innerHTML = `Continue selecting fields or click Stop & Generate to finish.`; + } + } + + updateStats() { + chrome.runtime.sendMessage({ + action: 'updateStats', + stats: { + container: !!this.container, + fields: this.fields.length + } + }); + } + + removeAllHighlights() { + document.querySelectorAll('.c4ai-selected-container').forEach(el => { + el.classList.remove('c4ai-selected-container'); + }); + document.querySelectorAll('.c4ai-selected-field').forEach(el => { + el.classList.remove('c4ai-selected-field'); + }); + } + + generateCode() { + const fieldDescriptions = this.fields.map(f => + `- ${f.name} (example: "${f.value.substring(0, 50)}...")` + ).join('\n'); + + return `#!/usr/bin/env python3 +""" +Generated by Crawl4AI Chrome Extension +URL: ${window.location.href} +Generated: ${new Date().toISOString()} +""" + +import asyncio +import json +from pathlib import Path +from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig +from crawl4ai.extraction_strategy import JsonCssExtractionStrategy + +# HTML snippet of the selected container element +HTML_SNIPPET = """ +${this.container.html} +""" + +# Extraction query based on your field selections +EXTRACTION_QUERY = """ +Create a JSON CSS extraction schema to extract the following fields: +${fieldDescriptions} + +The schema should handle multiple ${this.container.tagName} elements on the page. +Each item should be extracted as a separate object in the results array. +""" + +async def generate_schema(): + """Generate extraction schema using LLM""" + print("๐Ÿ”ง Generating extraction schema...") + + try: + # Generate the schema using Crawl4AI's built-in LLM integration + schema = JsonCssExtractionStrategy.generate_schema( + html=HTML_SNIPPET, + query=EXTRACTION_QUERY, + ) + + # Save the schema for reuse + schema_path = Path('generated_schema.json') + with open(schema_path, 'w') as f: + json.dump(schema, f, indent=2) + + print("โœ… Schema generated successfully!") + print(f"๐Ÿ“„ Schema saved to: {schema_path}") + print("\\nGenerated schema:") + print(json.dumps(schema, indent=2)) + + return schema + + except Exception as e: + print(f"โŒ Error generating schema: {e}") + return None + +async def test_extraction(url: str = "${window.location.href}"): + """Test the generated schema on the actual webpage""" + print("\\n๐Ÿงช Testing extraction on live webpage...") + + # Load the generated schema + try: + with open('generated_schema.json', 'r') as f: + schema = json.load(f) + except FileNotFoundError: + print("โŒ Schema file not found. Run generate_schema() first.") + return + + # Configure browser + browser_config = BrowserConfig( + headless=True, + verbose=False + ) + + # Configure extraction + crawler_config = CrawlerRunConfig( + extraction_strategy=JsonCssExtractionStrategy(schema=schema) + ) + + async with AsyncWebCrawler(config=browser_config) as crawler: + result = await crawler.arun( + url=url, + config=crawler_config + ) + + if result.success and result.extracted_content: + data = json.loads(result.extracted_content) + print(f"\\nโœ… Successfully extracted {len(data)} items!") + + # Save results + with open('extracted_data.json', 'w') as f: + json.dump(data, f, indent=2) + + # Show sample results + print("\\n๐Ÿ“Š Sample results (first 2 items):") + for i, item in enumerate(data[:2], 1): + print(f"\\nItem {i}:") + for key, value in item.items(): + print(f" {key}: {value}") + else: + print("โŒ Extraction failed:", result.error_message) + +if __name__ == "__main__": + # Step 1: Generate the schema from HTML snippet + asyncio.run(generate_schema()) + + # Step 2: Test extraction on the live webpage + # Uncomment the line below to test extraction: + # asyncio.run(test_extraction()) + + print("\\n๐ŸŽฏ Next steps:") + print("1. Review the generated schema in 'generated_schema.json'") + print("2. Uncomment the test_extraction() line to test on the live site") + print("3. Use the schema in your Crawl4AI projects!") +`; + + return code; + } + + showCodeModal(code) { + // Create modal + this.codeModal = document.createElement('div'); + this.codeModal.className = 'c4ai-code-modal'; + this.codeModal.innerHTML = ` +
    +
    +

    Generated Python Code

    + +
    +
    +
    ${this.escapeHtml(code)}
    +
    + +
    + `; + + document.body.appendChild(this.codeModal); + + // Add event listeners + document.getElementById('c4ai-close-modal').addEventListener('click', () => { + this.codeModal.remove(); + this.codeModal = null; + // Don't stop the capture session + }); + + document.getElementById('c4ai-download-code').addEventListener('click', () => { + chrome.runtime.sendMessage({ + action: 'downloadCode', + code: code, + filename: `crawl4ai_schema_${Date.now()}.py` + }, (response) => { + if (response && response.success) { + const btn = document.getElementById('c4ai-download-code'); + const originalHTML = btn.innerHTML; + btn.innerHTML = 'โœ“ Downloaded!'; + setTimeout(() => { + btn.innerHTML = originalHTML; + }, 2000); + } else { + console.error('Download failed:', response?.error); + alert('Download failed. Please check your browser settings.'); + } + }); + }); + + document.getElementById('c4ai-copy-code').addEventListener('click', () => { + navigator.clipboard.writeText(code).then(() => { + const btn = document.getElementById('c4ai-copy-code'); + btn.innerHTML = 'โœ“ Copied!'; + setTimeout(() => { + btn.innerHTML = '๐Ÿ“‹ Copy to Clipboard'; + }, 2000); + }); + }); + + // Apply syntax highlighting if possible + this.applySyntaxHighlighting(); + } + + escapeHtml(text) { + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; + } + + applySyntaxHighlighting() { + // Simple Python syntax highlighting - using a different approach + const codeElement = this.codeModal.querySelector('.language-python'); + const code = codeElement.textContent; + + // Split by lines to handle line-by-line + const lines = code.split('\n'); + const highlightedLines = lines.map(line => { + let highlightedLine = this.escapeHtml(line); + + // Skip if line is empty + if (!highlightedLine.trim()) return highlightedLine; + + // Comments (lines starting with #) + if (highlightedLine.trim().startsWith('#')) { + return `${highlightedLine}`; + } + + // Triple quoted strings + if (highlightedLine.includes('"""')) { + highlightedLine = highlightedLine.replace(/(""".*?""")/g, '$1'); + } + + // Regular strings - single and double quotes + highlightedLine = highlightedLine.replace(/(["'])([^"']*)\1/g, '$1$2$1'); + + // Keywords - only highlight if not inside a string + const keywords = ['import', 'from', 'async', 'def', 'await', 'try', 'except', 'with', 'as', 'for', 'if', 'else', 'elif', 'return', 'print', 'open', 'and', 'or', 'not', 'in', 'is', 'class', 'self', 'None', 'True', 'False', '__name__', '__main__']; + + keywords.forEach(keyword => { + // Use word boundaries and lookahead/lookbehind to ensure we're not in a string + const regex = new RegExp(`\\b(${keyword})\\b(?![^<]*)`, 'g'); + highlightedLine = highlightedLine.replace(regex, '$1'); + }); + + // Functions (word followed by parenthesis) + highlightedLine = highlightedLine.replace(/\b([a-zA-Z_]\w*)\s*\(/g, '$1('); + + return highlightedLine; + }); + + codeElement.innerHTML = highlightedLines.join('\n'); + } +} + +// Initialize +let schemaBuilder = null; + +// Listen for messages from popup +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + switch (message.action) { + case 'startSchemaCapture': + if (!schemaBuilder) { + schemaBuilder = new SchemaBuilder(); + } + schemaBuilder.start(); + sendResponse({ success: true }); + break; + + case 'stopCapture': + if (schemaBuilder) { + schemaBuilder.stop(); + schemaBuilder = null; + } + sendResponse({ success: true }); + break; + + case 'generateCode': + if (schemaBuilder) { + const code = schemaBuilder.generateCode(); + schemaBuilder.showCodeModal(code); + } + sendResponse({ success: true }); + break; + } + + return true; +}); \ No newline at end of file diff --git a/docs/md_v2/apps/crawl4ai-assistant/content/overlay.css b/docs/md_v2/apps/crawl4ai-assistant/content/overlay.css new file mode 100644 index 00000000..56dd5d6e --- /dev/null +++ b/docs/md_v2/apps/crawl4ai-assistant/content/overlay.css @@ -0,0 +1,561 @@ +/* Crawl4AI Assistant Overlay Styles */ + +/* Font Face Definitions */ +@font-face { + font-family: 'Dank Mono'; + src: url('../assets/DankMono-Regular.woff2') format('woff2'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Dank Mono'; + src: url('../assets/DankMono-Bold.woff2') format('woff2'); + font-weight: 700; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Dank Mono'; + src: url('../assets/DankMono-Italic.woff2') format('woff2'); + font-weight: 400; + font-style: italic; + font-display: swap; +} + +:root { + --font-primary: 'Dank Mono', -apple-system, BlinkMacSystemFont, sans-serif; + --font-code: 'Dank Mono', 'Monaco', 'Menlo', 'Consolas', monospace; +} + +/* Highlight box for hovering */ +.c4ai-highlight-box { + position: absolute; + pointer-events: none; + z-index: 999999; + transition: all 0.1s ease; + display: none; +} + +.c4ai-highlight-box.c4ai-container-mode { + border: 3px dashed #0fbbaa; + background: rgba(15, 187, 170, 0.1); + box-shadow: 0 0 0 2px rgba(15, 187, 170, 0.3); +} + +.c4ai-highlight-box.c4ai-field-mode { + border: 2px dashed #f380f5; + background: rgba(243, 128, 245, 0.1); + box-shadow: 0 0 0 2px rgba(243, 128, 245, 0.3); +} + +/* Selected elements */ +.c4ai-selected-container { + outline: 3px solid #0fbbaa !important; + outline-offset: 2px; + background: rgba(15, 187, 170, 0.05) !important; +} + +.c4ai-selected-field { + outline: 2px solid #f380f5 !important; + outline-offset: 1px; + background: rgba(243, 128, 245, 0.1) !important; + position: relative; +} + +.c4ai-selected-field::after { + content: attr(data-c4ai-field); + position: absolute; + top: -24px; + left: 0; + background: #f380f5; + color: #000; + padding: 2px 8px; + border-radius: 4px; + font-size: 12px; + font-weight: bold; + font-family: var(--font-primary); + z-index: 999999; + white-space: nowrap; +} + +/* Toolbar */ +.c4ai-toolbar { + position: fixed; + top: 20px; + right: 20px; + background: #070708; + border: 1px solid #3f3f44; + border-radius: 8px; + z-index: 999999; + font-family: var(--font-primary); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.8); + width: 320px; + color: #e8e9ed; + overflow: hidden; +} + +.c4ai-toolbar * { + font-family: inherit; + box-sizing: border-box; +} + +/* macOS-style titlebar */ +.c4ai-toolbar-titlebar { + display: flex; + align-items: center; + padding: 12px; + background: #1a1a1a; + border-bottom: 1px solid #3f3f44; + cursor: grab; + user-select: none; +} + +.c4ai-titlebar-dots { + display: flex; + gap: 8px; + flex-shrink: 0; +} + +.c4ai-dot { + width: 12px; + height: 12px; + border-radius: 50%; + border: none; + cursor: pointer; + transition: opacity 0.2s ease; + padding: 0; +} + +.c4ai-dot-close { + background: #ff5f57; +} + +.c4ai-dot-close:hover { + background: #ff3030; +} + +.c4ai-dot-minimize { + background: #ffbd2e; +} + +.c4ai-dot-minimize:hover { + background: #ffaa00; +} + +.c4ai-dot-maximize { + background: #28ca42; +} + +.c4ai-dot-maximize:hover { + background: #1eb533; +} + +.c4ai-titlebar-icon { + width: 16px; + height: 16px; + margin-left: 8px; + margin-right: 8px; +} + +.c4ai-titlebar-title { + flex: 1; + text-align: left; + font-size: 13px; + font-weight: 600; + color: #e8e9ed; +} + +.c4ai-toolbar-content { + padding: 16px; +} + +.c4ai-toolbar-status { + display: flex; + flex-direction: column; + gap: 8px; + margin-bottom: 16px; + padding: 12px; + background: #3f3f44; + border-radius: 8px; +} + +.c4ai-status-item { + display: flex; + justify-content: space-between; + align-items: center; +} + +.c4ai-status-label { + font-size: 12px; + color: #a3abba; + text-transform: uppercase; +} + +.c4ai-status-value { + font-size: 14px; + font-weight: 600; + color: #e8e9ed; +} + +.c4ai-toolbar-hint { + margin-top: 16px; + font-size: 13px; + line-height: 1.4; + color: #d5cec0; + padding: 12px; + background: #3f3f44; + border-radius: 8px; + border-left: 3px solid #0fbbaa; +} + +.c4ai-toolbar-hint strong { + color: #0fbbaa; + font-weight: 600; +} + +/* Fields list */ +.c4ai-fields-list { + margin-top: 16px; + padding: 12px; + background: #3f3f44; + border-radius: 8px; + border: 1px solid #3f3f44; +} + +.c4ai-fields-header { + font-size: 12px; + text-transform: uppercase; + color: #a3abba; + margin-bottom: 8px; + font-weight: 600; +} + +.c4ai-fields-items { + list-style: none; + margin: 0; + padding: 0; +} + +.c4ai-field-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 6px 8px; + margin-bottom: 4px; + background: #070708; + border-radius: 4px; + font-size: 12px; +} + +.c4ai-field-name { + font-weight: 600; + color: #f380f5; + margin-right: 8px; +} + +.c4ai-field-value { + color: #a3abba; + font-size: 11px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1; + text-align: right; +} + +/* Field naming dialog */ +.c4ai-field-dialog { + position: absolute; + background: #070708; + border: 2px solid #f380f5; + border-radius: 12px; + padding: 16px; + z-index: 999999; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.8); + font-family: -apple-system, BlinkMacSystemFont, sans-serif; + color: #e8e9ed; + min-width: 300px; +} + +.c4ai-field-dialog h4 { + margin: 0 0 12px 0; + font-size: 14px; + color: #e8e9ed; +} + +.c4ai-field-dialog input { + width: 100%; + padding: 8px 12px; + background: #3f3f44; + border: 1px solid #3f3f44; + border-radius: 6px; + color: #e8e9ed; + font-size: 14px; + margin-bottom: 12px; + outline: none; +} + +.c4ai-field-dialog input:focus { + border-color: #f380f5; + box-shadow: 0 0 0 2px rgba(243, 128, 245, 0.2); +} + +.c4ai-field-preview { + font-size: 12px; + color: #999; + margin-bottom: 12px; + padding: 8px; + background: #1a1a1a; + border-radius: 6px; + word-break: break-word; +} + +.c4ai-field-preview strong { + color: #ccc; +} + +.c4ai-field-actions { + display: flex; + gap: 8px; + justify-content: flex-end; +} + +.c4ai-field-actions button { + padding: 6px 16px; + border: none; + border-radius: 6px; + font-size: 13px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; +} + +#c4ai-field-save { + background: #f380f5; + color: #070708; +} + +#c4ai-field-save:hover { + background: #e370e5; + transform: translateY(-1px); +} + +#c4ai-field-cancel { + background: #3f3f44; + color: #e8e9ed; +} + +#c4ai-field-cancel:hover { + background: #4f4f54; +} + +/* Toolbar action buttons */ +.c4ai-toolbar-actions { + display: flex; + gap: 8px; + margin-top: 16px; + padding-top: 16px; + border-top: 1px solid #2a2a2a; +} + +.c4ai-action-btn { + flex: 1; + padding: 8px 16px; + background: #3f3f44; + border: 1px solid #3f3f44; + border-radius: 6px; + color: #e8e9ed; + font-size: 13px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: center; + gap: 6px; +} + +.c4ai-action-btn:hover { + background: #4f4f54; + border-color: #5f5f64; + transform: translateY(-1px); +} + +.c4ai-pause-btn { + background: #09b5a5; + border-color: #09b5a5; + color: #070708; +} + +.c4ai-pause-btn:hover { + background: #0ac5b5; +} + +.c4ai-pause-btn.c4ai-paused { + background: #ff3c74; + border-color: #ff3c74; +} + +.c4ai-generate-btn { + background: #0fbbaa; + border-color: #0fbbaa; + color: #070708; +} + +.c4ai-generate-btn:hover { + background: #1fcbba; +} + +/* Code modal */ +.c4ai-code-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.8); + z-index: 999999; + display: flex; + align-items: center; + justify-content: center; + padding: 20px; +} + +.c4ai-code-modal-content { + background: #070708; + border: 2px solid #3f3f44; + border-radius: 16px; + width: 90%; + max-width: 900px; + max-height: 90vh; + display: flex; + flex-direction: column; + box-shadow: 0 16px 64px rgba(0, 0, 0, 0.9); +} + +.c4ai-code-modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 24px; + border-bottom: 1px solid #2a2a2a; +} + +.c4ai-code-modal-header h2 { + margin: 0; + font-size: 20px; + color: #fff; + font-family: -apple-system, BlinkMacSystemFont, sans-serif; +} + +.c4ai-close-modal { + background: none; + border: none; + color: #999; + font-size: 24px; + cursor: pointer; + padding: 0; + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + transition: all 0.2s ease; +} + +.c4ai-close-modal:hover { + background: #2a2a2a; + color: #fff; +} + +.c4ai-code-modal-body { + flex: 1; + overflow: auto; + padding: 24px; +} + +.c4ai-code-block { + background: #3f3f44; + border: 1px solid #3f3f44; + border-radius: 8px; + padding: 20px; + overflow-x: auto; + margin: 0; +} + +.c4ai-code-block code { + font-family: var(--font-code); + font-size: 13px; + line-height: 1.6; + color: #e8e9ed; + white-space: pre; + display: block; +} + +/* Syntax highlighting */ +.c4ai-keyword { + color: #0fbbaa; + font-weight: bold; +} + +.c4ai-string { + color: #f380f5; +} + +.c4ai-comment { + color: #a3abba; + font-style: italic; +} + +.c4ai-function { + color: #ff3c74; +} + +.c4ai-code-modal-footer { + display: flex; + gap: 12px; + padding: 20px 24px; + border-top: 1px solid #2a2a2a; + justify-content: flex-end; +} + +.c4ai-download-btn { + background: #0fbbaa; + color: #070708; + border: none; +} + +.c4ai-download-btn:hover { + background: #1fcbba; +} + +.c4ai-copy-btn { + background: #3f3f44; + border-color: #3f3f44; +} + +.c4ai-copy-btn:hover { + background: #4f4f54; + border-color: #5f5f64; +} + +.c4ai-cloud-btn { + background: #3f3f44; + border-color: #3f3f44; + opacity: 0.6; + cursor: not-allowed; +} + +.c4ai-cloud-btn:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.c4ai-cloud-btn:hover:disabled { + background: #3f3f44; + transform: none; +} \ No newline at end of file diff --git a/docs/md_v2/apps/crawl4ai-assistant/crawl4ai-assistant-v1.0.0.zip b/docs/md_v2/apps/crawl4ai-assistant/crawl4ai-assistant-v1.0.0.zip new file mode 100644 index 00000000..d8fec586 Binary files /dev/null and b/docs/md_v2/apps/crawl4ai-assistant/crawl4ai-assistant-v1.0.0.zip differ diff --git a/docs/md_v2/apps/crawl4ai-assistant/crawl4ai-assistant-v1.0.1.zip b/docs/md_v2/apps/crawl4ai-assistant/crawl4ai-assistant-v1.0.1.zip new file mode 100644 index 00000000..bc782873 Binary files /dev/null and b/docs/md_v2/apps/crawl4ai-assistant/crawl4ai-assistant-v1.0.1.zip differ diff --git a/docs/md_v2/apps/crawl4ai-assistant/icons/favicon.ico b/docs/md_v2/apps/crawl4ai-assistant/icons/favicon.ico new file mode 100644 index 00000000..42e1dd25 Binary files /dev/null and b/docs/md_v2/apps/crawl4ai-assistant/icons/favicon.ico differ diff --git a/docs/md_v2/apps/crawl4ai-assistant/icons/icon-128.png b/docs/md_v2/apps/crawl4ai-assistant/icons/icon-128.png new file mode 100644 index 00000000..ed82a3cc Binary files /dev/null and b/docs/md_v2/apps/crawl4ai-assistant/icons/icon-128.png differ diff --git a/docs/md_v2/apps/crawl4ai-assistant/icons/icon-16.png b/docs/md_v2/apps/crawl4ai-assistant/icons/icon-16.png new file mode 100644 index 00000000..ed82a3cc Binary files /dev/null and b/docs/md_v2/apps/crawl4ai-assistant/icons/icon-16.png differ diff --git a/docs/md_v2/apps/crawl4ai-assistant/icons/icon-48.png b/docs/md_v2/apps/crawl4ai-assistant/icons/icon-48.png new file mode 100644 index 00000000..ed82a3cc Binary files /dev/null and b/docs/md_v2/apps/crawl4ai-assistant/icons/icon-48.png differ diff --git a/docs/md_v2/apps/crawl4ai-assistant/index.html b/docs/md_v2/apps/crawl4ai-assistant/index.html new file mode 100644 index 00000000..d0721a6a --- /dev/null +++ b/docs/md_v2/apps/crawl4ai-assistant/index.html @@ -0,0 +1,328 @@ + + + + + + Crawl4AI Assistant - Chrome Extension for Visual Web Scraping + + + +
    +
    +
    +
    + +
    +

    Crawl4AI Assistant

    +

    Chrome Extension for Visual Web Scraping

    +
    +
    + +
    +
    + +
    + +
    +
    + +
    +
    + + +
    +
    +
    + About Crawl4AI Assistant +
    +
    +

    Transform any website into structured data with just a few clicks! The Crawl4AI Assistant Chrome Extension lets you visually select elements on any webpage and automatically generates Python code for web scraping.

    + +
    +
    + ๐ŸŽฏ +

    Visual Selection

    +

    Click on any element to select it - no CSS selectors needed

    +
    +
    + ๐Ÿ“Š +

    Schema Builder

    +

    Build extraction schemas by clicking on container and field elements

    +
    +
    + ๐Ÿ +

    Python Code

    +

    Get production-ready Crawl4AI code with LLM extraction

    +
    +
    + ๐ŸŽจ +

    Beautiful UI

    +

    Draggable toolbar with macOS-style interface

    +
    +
    +
    +
    +
    + + +
    +

    Quick Start

    +
    +
    + Installation +
    +
    +
    +
    + 1 +
    +

    Download the Extension

    +

    Get the latest release from GitHub or use the button below

    + + โ†“ + Download Extension (v1.0.1) + +
    +
    +
    + 2 +
    +

    Load in Chrome

    +

    Navigate to chrome://extensions/ and enable Developer Mode

    +
    +
    +
    + 3 +
    +

    Load Unpacked

    +

    Click "Load unpacked" and select the extracted extension folder

    +
    +
    +
    +
    +
    +
    + + +
    +

    How to Use

    +
    +
    + Step-by-Step Guide +
    +
    +
    +
    +
    + 1๏ธโƒฃ +

    Start Schema Builder

    +
    +

    Click the extension icon and select "Schema Builder" to begin

    +
    + +
    +
    + 2๏ธโƒฃ +

    Select Container

    +
    +

    Click on a container element (e.g., product card, article, listing)

    +
    + # Container will be highlighted in green +
    +
    + +
    +
    + 3๏ธโƒฃ +

    Select Fields

    +
    +

    Click on individual fields inside the container and name them

    +
    + # Fields will be highlighted in pink + # Examples: title, price, description, image +
    +
    + +
    +
    + 4๏ธโƒฃ +

    Generate Code

    +
    +

    Click "Stop & Generate" to create your Python extraction code

    +
    +
    +
    +
    +
    + + +
    +

    Generated Code Example

    +
    +
    + example_extraction.py +
    +
    +
    import asyncio
    +import json
    +from crawl4ai import AsyncWebCrawler, CrawlerRunConfig
    +from crawl4ai.extraction_strategy import JsonCssExtractionStrategy
    +
    +async def extract_products():
    +    # Schema generated from your visual selection
    +    schema = {
    +        "name": "Product Catalog",
    +        "baseSelector": "div.product-card",  # Container you clicked
    +        "fields": [
    +            {
    +                "name": "title",
    +                "selector": "h3.product-title",
    +                "type": "text"
    +            },
    +            {
    +                "name": "price",
    +                "selector": "span.price",
    +                "type": "text"
    +            },
    +            {
    +                "name": "description",
    +                "selector": "p.description",
    +                "type": "text"
    +            },
    +            {
    +                "name": "image",
    +                "selector": "img.product-image",
    +                "type": "attribute",
    +                "attribute": "src"
    +            }
    +        ]
    +    }
    +
    +    # Create extraction strategy
    +    extraction_strategy = JsonCssExtractionStrategy(schema, verbose=True)
    +    
    +    # Configure the crawler
    +    config = CrawlerRunConfig(
    +        extraction_strategy=extraction_strategy
    +    )
    +
    +    async with AsyncWebCrawler() as crawler:
    +        result = await crawler.arun(
    +            url="https://example.com/products",
    +            config=config
    +        )
    +        
    +        # Parse the extracted data
    +        products = json.loads(result.extracted_content)
    +        print(f"Extracted {len(products)} products")
    +        
    +        # Display first product
    +        if products:
    +            print(json.dumps(products[0], indent=2))
    +        
    +        return products
    +
    +# Run the extraction
    +if __name__ == "__main__":
    +    asyncio.run(extract_products())
    +
    +
    +
    + + +
    +

    Coming Soon: Even More Power

    +
    +
    + Future Features +
    +
    +

    We're continuously expanding C4AI Assistant with powerful new features to make web scraping even easier:

    + +
    +
    +
    + Cloud +

    Run on C4AI Cloud

    +
    +

    Execute your extraction directly in the cloud without setting up any local environment. Just click "Run on Cloud" and get your data instantly.

    +
    + โ˜๏ธ Instant results โ€ข Auto-scaling +
    +
    + +
    +
    + Direct +

    Get CrawlResult Without Code

    +
    +

    Skip the code generation entirely! Get extracted data directly in the extension as a CrawlResult object, ready to download as JSON.

    +
    + ๐Ÿ“Š One-click extraction โ€ข No Python needed โ€ข Export to JSON/CSV +
    +
    + +
    +
    + AI +

    Smart Schema Suggestions

    +
    +

    AI-powered field detection that automatically suggests the most likely data fields on any page, making schema building even faster.

    +
    + ๐Ÿค– Auto-detect fields โ€ข Smart naming โ€ข Pattern recognition +
    +
    + +
    +
    + Script +

    C4A Script Builder

    +
    +

    Visual automation script builder for complex interactions - fill forms, click buttons, handle pagination, all without writing code.

    +
    + ๐ŸŽฏ Visual automation โ€ข Record & replay โ€ข Export as C4A script +
    +
    +
    + +
    +

    ๐Ÿš€ Stay tuned for updates! Follow our GitHub for the latest releases.

    +
    +
    +
    +
    + + + +
    +
    + + \ No newline at end of file diff --git a/docs/md_v2/apps/crawl4ai-assistant/manifest.json b/docs/md_v2/apps/crawl4ai-assistant/manifest.json new file mode 100644 index 00000000..7dbc972f --- /dev/null +++ b/docs/md_v2/apps/crawl4ai-assistant/manifest.json @@ -0,0 +1,44 @@ +{ + "manifest_version": 3, + "name": "Crawl4AI Assistant", + "version": "1.0.1", + "description": "Visual schema and script builder for Crawl4AI - Build extraction schemas by clicking on elements", + "permissions": [ + "activeTab", + "storage", + "downloads" + ], + "host_permissions": [ + "" + ], + "action": { + "default_popup": "popup/popup.html", + "default_icon": { + "16": "icons/icon-16.png", + "48": "icons/icon-48.png", + "128": "icons/icon-128.png" + } + }, + "content_scripts": [ + { + "matches": [""], + "js": ["content/content.js"], + "css": ["content/overlay.css"], + "run_at": "document_idle" + } + ], + "background": { + "service_worker": "background/service-worker.js" + }, + "icons": { + "16": "icons/icon-16.png", + "48": "icons/icon-48.png", + "128": "icons/icon-128.png" + }, + "web_accessible_resources": [ + { + "resources": ["icons/*", "assets/*"], + "matches": [""] + } + ] +} \ No newline at end of file diff --git a/docs/md_v2/apps/crawl4ai-assistant/popup/icons/favicon.ico b/docs/md_v2/apps/crawl4ai-assistant/popup/icons/favicon.ico new file mode 100644 index 00000000..42e1dd25 Binary files /dev/null and b/docs/md_v2/apps/crawl4ai-assistant/popup/icons/favicon.ico differ diff --git a/docs/md_v2/apps/crawl4ai-assistant/popup/icons/icon-128.png b/docs/md_v2/apps/crawl4ai-assistant/popup/icons/icon-128.png new file mode 100644 index 00000000..ed82a3cc Binary files /dev/null and b/docs/md_v2/apps/crawl4ai-assistant/popup/icons/icon-128.png differ diff --git a/docs/md_v2/apps/crawl4ai-assistant/popup/icons/icon-16.png b/docs/md_v2/apps/crawl4ai-assistant/popup/icons/icon-16.png new file mode 100644 index 00000000..ed82a3cc Binary files /dev/null and b/docs/md_v2/apps/crawl4ai-assistant/popup/icons/icon-16.png differ diff --git a/docs/md_v2/apps/crawl4ai-assistant/popup/icons/icon-48.png b/docs/md_v2/apps/crawl4ai-assistant/popup/icons/icon-48.png new file mode 100644 index 00000000..ed82a3cc Binary files /dev/null and b/docs/md_v2/apps/crawl4ai-assistant/popup/icons/icon-48.png differ diff --git a/docs/md_v2/apps/crawl4ai-assistant/popup/popup.css b/docs/md_v2/apps/crawl4ai-assistant/popup/popup.css new file mode 100644 index 00000000..b1a46650 --- /dev/null +++ b/docs/md_v2/apps/crawl4ai-assistant/popup/popup.css @@ -0,0 +1,324 @@ +/* Font Face Definitions */ +@font-face { + font-family: 'Dank Mono'; + src: url('../assets/DankMono-Regular.woff2') format('woff2'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Dank Mono'; + src: url('../assets/DankMono-Bold.woff2') format('woff2'); + font-weight: 700; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Dank Mono'; + src: url('../assets/DankMono-Italic.woff2') format('woff2'); + font-weight: 400; + font-style: italic; + font-display: swap; +} + +:root { + --font-primary: 'Dank Mono', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, monospace; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + width: 380px; + font-family: var(--font-primary); + background: #0a0a0a; + color: #e0e0e0; +} + +.popup-container { + padding: 20px; +} + +header { + display: flex; + align-items: center; + gap: 16px; + margin-bottom: 20px; + padding-bottom: 16px; + border-bottom: 1px solid #2a2a2a; +} + +.logo { + width: 48px; + height: 48px; + flex-shrink: 0; +} + +.header-content { + flex: 1; +} + +header h1 { + font-size: 20px; + font-weight: 600; + color: #fff; + margin: 0 0 4px 0; +} + +.header-stats { + display: flex; + align-items: center; + gap: 12px; +} + +.github-stars { + display: flex; + align-items: center; + gap: 6px; + color: #999; + text-decoration: none; + font-size: 13px; + transition: color 0.2s ease; +} + +.github-stars:hover { + color: #4a9eff; +} + +.github-icon { + flex-shrink: 0; +} + +.mode-selector { + display: flex; + flex-direction: column; + gap: 12px; + margin-bottom: 20px; +} + +.mode-button { + display: flex; + align-items: center; + gap: 16px; + padding: 16px; + background: #1a1a1a; + border: 2px solid #2a2a2a; + border-radius: 12px; + cursor: pointer; + transition: all 0.2s ease; + width: 100%; + text-align: left; +} + +.mode-button:hover:not(:disabled) { + background: #252525; + border-color: #4a9eff; + transform: translateY(-2px); +} + +.mode-button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.mode-button .icon { + font-size: 32px; + width: 48px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + background: #252525; + border-radius: 8px; +} + +.mode-button.schema .icon { + background: #1e3a5f; +} + +.mode-button.script .icon { + background: #3a1e5f; +} + +.mode-info h3 { + font-size: 16px; + color: #fff; + margin-bottom: 4px; +} + +.mode-info p { + font-size: 13px; + color: #999; + line-height: 1.4; +} + +.active-session { + background: #1a1a1a; + border: 2px solid #4a9eff; + border-radius: 12px; + padding: 16px; + margin-bottom: 20px; +} + +.active-session.hidden { + display: none; +} + +.session-header { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 12px; +} + +.status-dot { + width: 8px; + height: 8px; + background: #4a9eff; + border-radius: 50%; + animation: pulse 2s infinite; +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } +} + +.session-title { + font-size: 14px; + font-weight: 600; + color: #4a9eff; +} + +.session-stats { + display: flex; + gap: 20px; + margin-bottom: 16px; + padding: 12px; + background: #0a0a0a; + border-radius: 8px; +} + +.stat { + flex: 1; +} + +.stat-label { + display: block; + font-size: 11px; + color: #666; + text-transform: uppercase; + margin-bottom: 4px; +} + +.stat-value { + font-size: 14px; + color: #fff; + font-weight: 600; +} + +.session-actions { + display: flex; + gap: 8px; +} + +.action-button { + flex: 1; + padding: 10px 16px; + border: none; + border-radius: 8px; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; +} + +.action-button.primary { + background: #4a9eff; + color: #000; +} + +.action-button.primary:hover:not(:disabled) { + background: #3a8eef; + transform: translateY(-1px); +} + +.action-button.primary:disabled { + background: #2a4a7f; + color: #666; + cursor: not-allowed; +} + +.action-button.secondary { + background: #2a2a2a; + color: #fff; +} + +.action-button.secondary:hover { + background: #3a3a3a; +} + +.instructions { + background: #1a1a1a; + border-radius: 12px; + padding: 16px; + margin-bottom: 16px; +} + +.instructions h4 { + font-size: 14px; + margin-bottom: 12px; + color: #fff; +} + +.instructions ol { + padding-left: 20px; +} + +.instructions li { + font-size: 13px; + line-height: 1.6; + color: #ccc; + margin-bottom: 6px; +} + +footer { + padding-top: 16px; + border-top: 1px solid #2a2a2a; +} + +.social-links { + display: flex; + justify-content: center; + gap: 16px; +} + +.social-link { + display: flex; + align-items: center; + gap: 6px; + color: #999; + text-decoration: none; + font-size: 12px; + transition: all 0.2s ease; + padding: 6px 12px; + border-radius: 6px; + background: #1a1a1a; +} + +.social-link:hover { + color: #0fbbaa; + background: #2a2a2a; + transform: translateY(-1px); +} + +.social-link svg { + width: 16px; + height: 16px; + flex-shrink: 0; +} \ No newline at end of file diff --git a/docs/md_v2/apps/crawl4ai-assistant/popup/popup.html b/docs/md_v2/apps/crawl4ai-assistant/popup/popup.html new file mode 100644 index 00000000..3ce1c729 --- /dev/null +++ b/docs/md_v2/apps/crawl4ai-assistant/popup/popup.html @@ -0,0 +1,103 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/md_v2/apps/crawl4ai-assistant/popup/popup.js b/docs/md_v2/apps/crawl4ai-assistant/popup/popup.js new file mode 100644 index 00000000..59ab82a0 --- /dev/null +++ b/docs/md_v2/apps/crawl4ai-assistant/popup/popup.js @@ -0,0 +1,112 @@ +// Popup script for Crawl4AI Assistant +let activeMode = null; + +document.addEventListener('DOMContentLoaded', () => { + // Fetch GitHub stars + fetchGitHubStars(); + + // Check current state + chrome.storage.local.get(['captureMode', 'captureStats'], (data) => { + if (data.captureMode) { + activeMode = data.captureMode; + showActiveSession(data.captureStats || {}); + } + }); + + // Mode buttons + document.getElementById('schema-mode').addEventListener('click', () => { + startSchemaCapture(); + }); + + // Session actions + document.getElementById('generate-code').addEventListener('click', () => { + generateCode(); + }); + + document.getElementById('stop-capture').addEventListener('click', () => { + stopCapture(); + }); +}); + +async function fetchGitHubStars() { + try { + const response = await fetch('https://api.github.com/repos/unclecode/crawl4ai'); + const data = await response.json(); + const stars = data.stargazers_count; + + // Format the number (e.g., 1.2k) + let formattedStars; + if (stars >= 1000) { + formattedStars = (stars / 1000).toFixed(1) + 'k'; + } else { + formattedStars = stars.toString(); + } + + document.getElementById('stars-count').textContent = `โญ ${formattedStars}`; + } catch (error) { + console.error('Failed to fetch GitHub stars:', error); + document.getElementById('stars-count').textContent = 'โญ 2k+'; + } +} + +function startSchemaCapture() { + chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { + chrome.tabs.sendMessage(tabs[0].id, { + action: 'startSchemaCapture' + }, (response) => { + if (response && response.success) { + // Close the popup to let user interact with the page + window.close(); + } + }); + }); +} + +function showActiveSession(stats) { + document.querySelector('.mode-selector').style.display = 'none'; + document.getElementById('active-session').classList.remove('hidden'); + + updateSessionStats(stats); +} + +function updateSessionStats(stats) { + document.getElementById('container-status').textContent = + stats.container ? 'Selected โœ“' : 'Not selected'; + document.getElementById('fields-count').textContent = stats.fields || 0; + + // Enable generate button if we have container and fields + document.getElementById('generate-code').disabled = + !stats.container || stats.fields === 0; +} + +function generateCode() { + chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { + chrome.tabs.sendMessage(tabs[0].id, { + action: 'generateCode' + }); + }); +} + +function stopCapture() { + chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { + chrome.tabs.sendMessage(tabs[0].id, { + action: 'stopCapture' + }, () => { + // Reset UI + document.querySelector('.mode-selector').style.display = 'flex'; + document.getElementById('active-session').classList.add('hidden'); + activeMode = null; + + // Clear storage + chrome.storage.local.remove(['captureMode', 'captureStats']); + }); + }); +} + +// Listen for stats updates from content script +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + if (message.action === 'updateStats') { + updateSessionStats(message.stats); + chrome.storage.local.set({ captureStats: message.stats }); + } +}); \ No newline at end of file diff --git a/docs/md_v2/apps/index.md b/docs/md_v2/apps/index.md index 57e86e4c..78d6558c 100644 --- a/docs/md_v2/apps/index.md +++ b/docs/md_v2/apps/index.md @@ -186,19 +186,20 @@ Our apps are designed to make Crawl4AI more accessible and powerful. Whether you
    - Coming Soon -

    ๐Ÿ—๏ธ Schema Builder

    + Available +

    ๐Ÿ” Crawl4AI Assistant (Chrome Extension)

    - Visually design JSON extraction schemas by pointing and clicking on webpage elements. No more guessing CSS selectors! + Visual schema builder Chrome extension - click on webpage elements to generate extraction schemas and Python code!

      -
    • Point-and-click selector generation
    • -
    • Automatic schema inference
    • -
    • Live extraction preview
    • -
    • Export to multiple formats
    • +
    • Visual element selection
    • +
    • Container & field selection modes
    • +
    • Smart selector generation
    • +
    • Complete Python code generation
    • +
    • One-click installation
    diff --git a/docs/md_v2/apps/llmtxt/index.html b/docs/md_v2/apps/llmtxt/index.html index 1e4bd965..e5fee16e 100644 --- a/docs/md_v2/apps/llmtxt/index.html +++ b/docs/md_v2/apps/llmtxt/index.html @@ -7,10 +7,25 @@ -
    -
    -

    Crawl4AI LLM Context Builder

    -
    +
    +
    +
    +
    + +
    +

    Crawl4AI LLM Context Builder

    +

    Multi-Dimensional Context for AI Assistants

    +
    +
    + +
    +
    + +
    @@ -135,6 +150,7 @@
    +
    diff --git a/docs/md_v2/apps/llmtxt/llmtxt.css b/docs/md_v2/apps/llmtxt/llmtxt.css index 7026b98d..fe0eb028 100644 --- a/docs/md_v2/apps/llmtxt/llmtxt.css +++ b/docs/md_v2/apps/llmtxt/llmtxt.css @@ -1,5 +1,30 @@ /* Terminal Theme CSS for LLM Context Builder */ +/* Font Face Definitions */ +@font-face { + font-family: 'Dank Mono'; + src: url('../assets/DankMono-Regular.woff2') format('woff2'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Dank Mono'; + src: url('../assets/DankMono-Bold.woff2') format('woff2'); + font-weight: 700; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Dank Mono'; + src: url('../assets/DankMono-Italic.woff2') format('woff2'); + font-weight: 400; + font-style: italic; + font-display: swap; +} + :root { --background-color: #070708; --font-color: #e8e9ed; @@ -13,6 +38,9 @@ --border-color: #3f3f44; --hover-bg: #1a1a1c; --success-color: #50ff50; + --bg-secondary: #1a1a1a; + --primary-green: #0fbbaa; + --font-primary: 'Dank Mono', dm, Monaco, Courier New, monospace; } * { @@ -22,38 +50,88 @@ body { margin: 0; padding: 0; - font-family: dm, Monaco, Courier New, monospace; + font-family: var(--font-primary); font-size: 14px; line-height: 1.5; background-color: var(--background-color); color: var(--font-color); } -.container { +/* Terminal Container */ +.terminal-container { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +/* Header - matching assistant layout */ +.header { + background: var(--bg-secondary); + border-bottom: 1px solid var(--border-color); + padding: 1.5rem 0; + position: sticky; + top: 0; + z-index: 100; + backdrop-filter: blur(10px); + background: rgba(26, 26, 26, 0.95); +} + +.header-content { max-width: 1200px; margin: 0 auto; - padding: 20px; + padding: 0 2rem; + display: flex; + justify-content: space-between; + align-items: center; } -/* Header */ -.header { - text-align: center; - margin-bottom: 40px; - border-bottom: 1px dashed var(--tertiary-color); - padding-bottom: 20px; -} - -.header h1 { - font-size: 24px; - color: var(--primary-color); - margin: 0; - text-transform: uppercase; - letter-spacing: 2px; +.logo-section { + display: flex; + align-items: center; + gap: 1rem; } .logo { - font-size: 28px; - vertical-align: middle; + width: 48px; + height: 48px; +} + +.logo-section h1 { + font-size: 1.75rem; + font-weight: 700; + color: var(--font-color); + margin: 0; +} + +.tagline { + font-size: 0.875rem; + color: var(--tertiary-color); + margin-top: 0.25rem; +} + +.nav-links { + display: flex; + gap: 2rem; +} + +.nav-link { + color: var(--tertiary-color); + text-decoration: none; + font-size: 0.875rem; + transition: color 0.2s ease; +} + +.nav-link:hover { + color: var(--primary-green); +} + +/* Content */ +.content { + flex: 1; + max-width: 1200px; + margin: 0 auto; + padding: 2rem; + width: 100%; } /* Intro Section */ @@ -446,6 +524,15 @@ body { /* Responsive Design */ @media (max-width: 768px) { + .header-content { + flex-direction: column; + gap: 1.5rem; + } + + .nav-links { + gap: 1rem; + } + .preset-options { flex-direction: column; } @@ -454,7 +541,7 @@ body { grid-template-columns: 1fr; } - .container { - padding: 10px; + .content { + padding: 1rem; } } \ No newline at end of file diff --git a/docs/md_v2/assets/styles.css b/docs/md_v2/assets/styles.css index fcd56b7a..5d693197 100644 --- a/docs/md_v2/assets/styles.css +++ b/docs/md_v2/assets/styles.css @@ -28,7 +28,8 @@ --secondary-color: #d5cec0; --tertiary-color: #a3abba; --primary-dimmed-color: #09b5a5; /* Updated to the brand color */ - --primary-color: #0fbbaa; /* Updated to the brand color */ + /* --primary-color: #0fbbaa; */ + --primary-color: #50ffff; /* Updated to the brand color */ --accent-color: rgb(243, 128, 245); --error-color: #ff3c74; --progress-bar-background: #3f3f44; diff --git a/docs/md_v2/core/llmtxt.md b/docs/md_v2/core/llmtxt.md index f5e02d2b..b2c67f3a 100644 --- a/docs/md_v2/core/llmtxt.md +++ b/docs/md_v2/core/llmtxt.md @@ -1,4 +1,4 @@ -
    +I