Add Crawl4AI Assistant Chrome Extension

- Created manifest.json for the Crawl4AI Assistant extension.
- Added popup HTML, CSS, and JS files for the extension interface.
- Included icons and favicon for the extension.
- Implemented functionality for schema capture and code generation.
- Updated index.md to reflect the availability of the new extension.
- Enhanced LLM Context Builder layout and styles for consistency.
- Adjusted global styles for better branding and responsiveness.
This commit is contained in:
UncleCode
2025-06-08 18:34:05 +08:00
parent 6f3a0ea38e
commit 926592649e
32 changed files with 3056 additions and 36 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -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;
}

View File

@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="popup.css">
</head>
<body>
<div class="popup-container">
<header>
<img src="icons/icon-48.png" class="logo" alt="Crawl4AI">
<div class="header-content">
<h1>Crawl4AI Assistant</h1>
<div class="header-stats">
<a href="https://github.com/unclecode/crawl4ai" target="_blank" class="github-stars">
<svg class="github-icon" viewBox="0 0 16 16" width="16" height="16">
<path fill="currentColor" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
</svg>
<span id="stars-count">Loading...</span>
</a>
</div>
</div>
</header>
<div class="mode-selector">
<button id="schema-mode" class="mode-button schema">
<div class="icon">📊</div>
<div class="mode-info">
<h3>Schema Builder</h3>
<p>Click elements to build extraction schemas</p>
</div>
</button>
<button id="script-mode" class="mode-button script" disabled>
<div class="icon">🎯</div>
<div class="mode-info">
<h3>Script Builder</h3>
<p>Coming soon - Build automation scripts</p>
</div>
</button>
</div>
<div id="active-session" class="active-session hidden">
<div class="session-header">
<span class="status-dot"></span>
<span class="session-title">Schema Capture Active</span>
</div>
<div class="session-stats">
<div class="stat">
<span class="stat-label">Container:</span>
<span id="container-status" class="stat-value">Not selected</span>
</div>
<div class="stat">
<span class="stat-label">Fields:</span>
<span id="fields-count" class="stat-value">0</span>
</div>
</div>
<div class="session-actions">
<button id="generate-code" class="action-button primary" disabled>
<span>Generate Code</span>
</button>
<button id="stop-capture" class="action-button secondary">
<span>Stop Capture</span>
</button>
</div>
</div>
<div class="instructions">
<h4>How to use:</h4>
<ol>
<li>Click "Schema Builder" to start</li>
<li>Click on a container element (e.g., product card)</li>
<li>Click individual fields inside and name them</li>
<li>Generate Python code when done</li>
</ol>
</div>
<footer>
<div class="social-links">
<a href="https://docs.crawl4ai.com" target="_blank" class="social-link">
<svg viewBox="0 0 24 24" width="16" height="16">
<path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
</svg>
<span>Docs</span>
</a>
<a href="https://twitter.com/unclecode" target="_blank" class="social-link">
<svg viewBox="0 0 24 24" width="16" height="16">
<path fill="currentColor" d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
</svg>
<span>@unclecode</span>
</a>
<a href="https://discord.gg/jP8KfhDhyN" target="_blank" class="social-link">
<svg viewBox="0 0 24 24" width="16" height="16">
<path fill="currentColor" d="M19.27 5.33C17.94 4.71 16.5 4.26 15 4a.09.09 0 00-.07.03c-.18.33-.39.76-.53 1.09a16.09 16.09 0 00-4.8 0c-.14-.34-.35-.76-.54-1.09-.01-.02-.04-.03-.07-.03-1.5.26-2.93.71-4.27 1.33-.01 0-.02.01-.03.02-2.72 4.07-3.47 8.03-3.1 11.95 0 .02.01.04.03.05 1.8 1.32 3.53 2.12 5.24 2.65.03.01.06 0 .07-.02.4-.55.76-1.13 1.07-1.74.02-.04 0-.08-.04-.09-.57-.22-1.11-.48-1.64-.78-.04-.02-.04-.08-.01-.11.11-.08.22-.17.33-.25.02-.02.05-.02.07-.01 3.44 1.57 7.15 1.57 10.55 0 .02-.01.05-.01.07.01.11.09.22.17.33.26.04.03.04.09-.01.11-.52.31-1.07.56-1.64.78-.04.01-.05.06-.04.09.32.61.68 1.19 1.07 1.74.03.01.06.02.09.01 1.72-.53 3.45-1.33 5.25-2.65.02-.01.03-.03.03-.05.44-4.53-.73-8.46-3.1-11.95-.01-.01-.02-.02-.04-.02zM8.52 14.91c-1.03 0-1.89-.95-1.89-2.12s.84-2.12 1.89-2.12c1.06 0 1.9.96 1.89 2.12 0 1.17-.84 2.12-1.89 2.12zm6.97 0c-1.03 0-1.89-.95-1.89-2.12s.84-2.12 1.89-2.12c1.06 0 1.9.96 1.89 2.12 0 1.17-.83 2.12-1.89 2.12z"/>
</svg>
<span>Discord</span>
</a>
</div>
</footer>
</div>
<script src="popup.js"></script>
</body>
</html>

View File

@@ -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 });
}
});