Add blog posts, cleanup unused files, update components
- Add 100 blog posts covering AI, development, and tech topics - Add .env.example for environment configuration - Add accessibility and lighthouse audit scripts - Remove obsolete SEO reports and temporary files - Remove dev-dist build artifacts and backup files - Remove unused portrait images (moved/consolidated elsewhere) - Update contact form and component improvements Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,507 @@
|
||||
# Browser Extensions für Automation & Productivity
|
||||
|
||||
**Meta-Description:** Browser Extensions entwickeln für Automation. Manifest V3, Content Scripts, Background Workers und Cross-Browser Kompatibilität.
|
||||
|
||||
**Keywords:** Browser Extension, Chrome Extension, Manifest V3, Content Script, Automation, Web Extension API
|
||||
|
||||
---
|
||||
|
||||
## Einführung
|
||||
|
||||
Browser Extensions erweitern die Browser-Funktionalität direkt. Mit **Manifest V3** und der WebExtensions API können Sie Workflows automatisieren, Daten extrahieren und Produktivität steigern.
|
||||
|
||||
---
|
||||
|
||||
## Extension Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ BROWSER EXTENSION ARCHITECTURE │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Components: │
|
||||
│ ├── Manifest (manifest.json) │
|
||||
│ │ └── Extension Metadata & Permissions │
|
||||
│ │ │
|
||||
│ ├── Service Worker (Background) │
|
||||
│ │ └── Event Handling, API Calls, State │
|
||||
│ │ │
|
||||
│ ├── Content Scripts │
|
||||
│ │ └── DOM Manipulation auf Web Pages │
|
||||
│ │ │
|
||||
│ ├── Popup (UI) │
|
||||
│ │ └── Toolbar Button Interface │
|
||||
│ │ │
|
||||
│ └── Options Page │
|
||||
│ └── Extension Settings │
|
||||
│ │
|
||||
│ Communication: │
|
||||
│ Content Script ←→ Service Worker ←→ Popup │
|
||||
│ (chrome.runtime.sendMessage / onMessage) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Manifest V3 Setup
|
||||
|
||||
```json
|
||||
// manifest.json
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Automation Helper",
|
||||
"version": "1.0.0",
|
||||
"description": "Automate repetitive browser tasks",
|
||||
|
||||
"permissions": [
|
||||
"activeTab",
|
||||
"storage",
|
||||
"alarms",
|
||||
"contextMenus"
|
||||
],
|
||||
|
||||
"host_permissions": [
|
||||
"https://*.example.com/*"
|
||||
],
|
||||
|
||||
"background": {
|
||||
"service_worker": "background.js",
|
||||
"type": "module"
|
||||
},
|
||||
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["https://*.example.com/*"],
|
||||
"js": ["content.js"],
|
||||
"css": ["content.css"],
|
||||
"run_at": "document_idle"
|
||||
}
|
||||
],
|
||||
|
||||
"action": {
|
||||
"default_popup": "popup.html",
|
||||
"default_icon": {
|
||||
"16": "icons/icon16.png",
|
||||
"48": "icons/icon48.png",
|
||||
"128": "icons/icon128.png"
|
||||
}
|
||||
},
|
||||
|
||||
"options_page": "options.html",
|
||||
|
||||
"icons": {
|
||||
"16": "icons/icon16.png",
|
||||
"48": "icons/icon48.png",
|
||||
"128": "icons/icon128.png"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Service Worker (Background)
|
||||
|
||||
```typescript
|
||||
// background.ts
|
||||
chrome.runtime.onInstalled.addListener(() => {
|
||||
console.log('Extension installed');
|
||||
|
||||
// Context Menu erstellen
|
||||
chrome.contextMenus.create({
|
||||
id: 'extract-data',
|
||||
title: 'Daten extrahieren',
|
||||
contexts: ['selection']
|
||||
});
|
||||
|
||||
// Default Settings
|
||||
chrome.storage.sync.set({
|
||||
autoExtract: true,
|
||||
extractInterval: 5
|
||||
});
|
||||
});
|
||||
|
||||
// Context Menu Handler
|
||||
chrome.contextMenus.onClicked.addListener((info, tab) => {
|
||||
if (info.menuItemId === 'extract-data' && info.selectionText) {
|
||||
processSelectedText(info.selectionText);
|
||||
}
|
||||
});
|
||||
|
||||
// Message Handler
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.type === 'EXTRACT_PAGE') {
|
||||
handleExtraction(message.data)
|
||||
.then(result => sendResponse({ success: true, data: result }))
|
||||
.catch(error => sendResponse({ success: false, error: error.message }));
|
||||
|
||||
return true; // Async Response
|
||||
}
|
||||
|
||||
if (message.type === 'GET_SETTINGS') {
|
||||
chrome.storage.sync.get(['autoExtract', 'extractInterval'], (settings) => {
|
||||
sendResponse(settings);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Alarm für wiederkehrende Tasks
|
||||
chrome.alarms.create('sync-data', { periodInMinutes: 5 });
|
||||
|
||||
chrome.alarms.onAlarm.addListener((alarm) => {
|
||||
if (alarm.name === 'sync-data') {
|
||||
syncDataToServer();
|
||||
}
|
||||
});
|
||||
|
||||
async function handleExtraction(data: any) {
|
||||
// API Call oder Datenverarbeitung
|
||||
const response = await fetch('https://api.example.com/extract', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async function syncDataToServer() {
|
||||
const { extractedData } = await chrome.storage.local.get('extractedData');
|
||||
|
||||
if (extractedData && extractedData.length > 0) {
|
||||
await fetch('https://api.example.com/sync', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(extractedData)
|
||||
});
|
||||
|
||||
// Clear nach Sync
|
||||
await chrome.storage.local.set({ extractedData: [] });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Content Script
|
||||
|
||||
```typescript
|
||||
// content.ts
|
||||
|
||||
// DOM Ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
|
||||
function init() {
|
||||
console.log('Content script loaded on', window.location.href);
|
||||
|
||||
// UI Element einfügen
|
||||
injectUI();
|
||||
|
||||
// Page Observer für dynamische Inhalte
|
||||
observePageChanges();
|
||||
|
||||
// Auto-Extract wenn aktiviert
|
||||
chrome.storage.sync.get('autoExtract', ({ autoExtract }) => {
|
||||
if (autoExtract) {
|
||||
extractPageData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function injectUI() {
|
||||
const container = document.createElement('div');
|
||||
container.id = 'extension-helper';
|
||||
container.innerHTML = `
|
||||
<button id="ext-extract-btn" class="ext-btn">
|
||||
Daten extrahieren
|
||||
</button>
|
||||
`;
|
||||
|
||||
document.body.appendChild(container);
|
||||
|
||||
document.getElementById('ext-extract-btn')?.addEventListener('click', () => {
|
||||
extractPageData();
|
||||
});
|
||||
}
|
||||
|
||||
async function extractPageData() {
|
||||
const data = {
|
||||
url: window.location.href,
|
||||
title: document.title,
|
||||
timestamp: new Date().toISOString(),
|
||||
content: extractContent()
|
||||
};
|
||||
|
||||
// An Background Script senden
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
type: 'EXTRACT_PAGE',
|
||||
data
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
showNotification('Daten erfolgreich extrahiert!');
|
||||
} else {
|
||||
showNotification('Fehler: ' + response.error, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function extractContent() {
|
||||
// Beispiel: Produkt-Daten extrahieren
|
||||
const products: any[] = [];
|
||||
|
||||
document.querySelectorAll('.product-item').forEach((el) => {
|
||||
products.push({
|
||||
name: el.querySelector('.product-name')?.textContent?.trim(),
|
||||
price: el.querySelector('.price')?.textContent?.trim(),
|
||||
image: el.querySelector('img')?.getAttribute('src')
|
||||
});
|
||||
});
|
||||
|
||||
return products;
|
||||
}
|
||||
|
||||
function observePageChanges() {
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.addedNodes.length > 0) {
|
||||
// Neue Elemente verarbeiten
|
||||
handleNewElements(mutation.addedNodes);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
|
||||
function handleNewElements(nodes: NodeList) {
|
||||
nodes.forEach((node) => {
|
||||
if (node instanceof HTMLElement) {
|
||||
// Highlight neue Produkte
|
||||
const products = node.querySelectorAll('.product-item');
|
||||
products.forEach((product) => {
|
||||
product.classList.add('ext-highlighted');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showNotification(message: string, type: 'success' | 'error' = 'success') {
|
||||
const notification = document.createElement('div');
|
||||
notification.className = `ext-notification ext-${type}`;
|
||||
notification.textContent = message;
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
setTimeout(() => notification.remove(), 3000);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Popup UI
|
||||
|
||||
```html
|
||||
<!-- popup.html -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="popup.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="popup-container">
|
||||
<h1>Automation Helper</h1>
|
||||
|
||||
<div class="status">
|
||||
<span id="status-indicator" class="indicator"></span>
|
||||
<span id="status-text">Bereit</span>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button id="extract-btn" class="btn primary">
|
||||
Aktuelle Seite extrahieren
|
||||
</button>
|
||||
|
||||
<button id="batch-extract-btn" class="btn secondary">
|
||||
Batch-Extraktion
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat">
|
||||
<span class="stat-value" id="extracted-count">0</span>
|
||||
<span class="stat-label">Extrahiert</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-value" id="synced-count">0</span>
|
||||
<span class="stat-label">Synchronisiert</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="options.html" target="_blank" class="settings-link">
|
||||
Einstellungen
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<script src="popup.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```typescript
|
||||
// popup.ts
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
// Stats laden
|
||||
await updateStats();
|
||||
|
||||
// Event Listeners
|
||||
document.getElementById('extract-btn')?.addEventListener('click', extractCurrent);
|
||||
document.getElementById('batch-extract-btn')?.addEventListener('click', batchExtract);
|
||||
});
|
||||
|
||||
async function updateStats() {
|
||||
const { extractedCount = 0, syncedCount = 0 } = await chrome.storage.local.get([
|
||||
'extractedCount',
|
||||
'syncedCount'
|
||||
]);
|
||||
|
||||
document.getElementById('extracted-count')!.textContent = extractedCount.toString();
|
||||
document.getElementById('synced-count')!.textContent = syncedCount.toString();
|
||||
}
|
||||
|
||||
async function extractCurrent() {
|
||||
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
|
||||
if (!tab.id) return;
|
||||
|
||||
// Message an Content Script
|
||||
const response = await chrome.tabs.sendMessage(tab.id, { type: 'EXTRACT' });
|
||||
|
||||
if (response.success) {
|
||||
setStatus('success', 'Extrahiert!');
|
||||
await updateStats();
|
||||
} else {
|
||||
setStatus('error', 'Fehler');
|
||||
}
|
||||
}
|
||||
|
||||
async function batchExtract() {
|
||||
const tabs = await chrome.tabs.query({ url: 'https://*.example.com/*' });
|
||||
|
||||
setStatus('loading', `Extrahiere ${tabs.length} Tabs...`);
|
||||
|
||||
for (const tab of tabs) {
|
||||
if (tab.id) {
|
||||
await chrome.tabs.sendMessage(tab.id, { type: 'EXTRACT' });
|
||||
}
|
||||
}
|
||||
|
||||
setStatus('success', `${tabs.length} Seiten extrahiert`);
|
||||
await updateStats();
|
||||
}
|
||||
|
||||
function setStatus(type: 'success' | 'error' | 'loading', text: string) {
|
||||
const indicator = document.getElementById('status-indicator')!;
|
||||
const statusText = document.getElementById('status-text')!;
|
||||
|
||||
indicator.className = `indicator ${type}`;
|
||||
statusText.textContent = text;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Storage API
|
||||
|
||||
```typescript
|
||||
// Sync Storage (zwischen Geräten synchronisiert, 100KB Limit)
|
||||
await chrome.storage.sync.set({
|
||||
settings: {
|
||||
autoExtract: true,
|
||||
theme: 'dark'
|
||||
}
|
||||
});
|
||||
|
||||
const { settings } = await chrome.storage.sync.get('settings');
|
||||
|
||||
// Local Storage (nur lokal, 5MB Limit)
|
||||
await chrome.storage.local.set({
|
||||
extractedData: largeDataArray
|
||||
});
|
||||
|
||||
// Storage Change Listener
|
||||
chrome.storage.onChanged.addListener((changes, areaName) => {
|
||||
for (const [key, { oldValue, newValue }] of Object.entries(changes)) {
|
||||
console.log(`${areaName}.${key} changed from`, oldValue, 'to', newValue);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cross-Browser Kompatibilität
|
||||
|
||||
```typescript
|
||||
// Browser Detection
|
||||
const isChrome = typeof chrome !== 'undefined' && chrome.runtime;
|
||||
const isFirefox = typeof browser !== 'undefined';
|
||||
|
||||
// Unified API
|
||||
const browserAPI = isFirefox ? browser : chrome;
|
||||
|
||||
// Polyfill für Firefox Promise-based API
|
||||
function promisify<T>(chromeMethod: (...args: any[]) => void): (...args: any[]) => Promise<T> {
|
||||
return (...args) => new Promise((resolve, reject) => {
|
||||
chromeMethod(...args, (result: T) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
reject(chrome.runtime.lastError);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// webextension-polyfill verwenden
|
||||
import browser from 'webextension-polyfill';
|
||||
|
||||
// Unified API für alle Browser
|
||||
const tabs = await browser.tabs.query({ active: true });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fazit
|
||||
|
||||
Browser Extensions bieten:
|
||||
|
||||
1. **Tiefe Integration**: Direkter DOM-Zugriff
|
||||
2. **Automation**: Wiederkehrende Tasks automatisieren
|
||||
3. **Produktivität**: Workflows optimieren
|
||||
4. **Cross-Platform**: Chrome, Firefox, Edge
|
||||
|
||||
Mächtige Tools für Power-User und Entwickler.
|
||||
|
||||
---
|
||||
|
||||
## Bildprompts
|
||||
|
||||
1. "Browser with extension icon showing automation, productivity concept"
|
||||
2. "Extension popup interacting with webpage, data extraction"
|
||||
3. "Multiple browser windows synchronized, cross-browser extension"
|
||||
|
||||
---
|
||||
|
||||
## Quellen
|
||||
|
||||
- [Chrome Extension Documentation](https://developer.chrome.com/docs/extensions/)
|
||||
- [Manifest V3 Migration](https://developer.chrome.com/docs/extensions/mv3/intro/)
|
||||
- [Firefox WebExtensions](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions)
|
||||
- [webextension-polyfill](https://github.com/nicknisi/webextension-polyfill)
|
||||
Reference in New Issue
Block a user